summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:54:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:54:43 +0000
commite4283f6d48b98e764b988b43bbc86b9d52e6ec94 (patch)
treec8f7f7a6c2f5faa2942d27cefc6fd46cca492656
parentInitial commit. (diff)
downloadgnome-shell-e4283f6d48b98e764b988b43bbc86b9d52e6ec94.tar.xz
gnome-shell-e4283f6d48b98e764b988b43bbc86b9d52e6ec94.zip
Adding upstream version 43.9.upstream/43.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--.eslintrc.yml8
-rw-r--r--.gitignore84
-rw-r--r--.gitlab-ci.yml289
-rw-r--r--.gitlab-ci/check-potfiles.js207
-rwxr-xr-x.gitlab-ci/check-potfiles.sh38
-rwxr-xr-x.gitlab-ci/checkout-mutter.sh70
-rw-r--r--.gitlab-ci/commit-rules.yml16
-rwxr-xr-x.gitlab-ci/download-coverity-tarball.sh38
-rwxr-xr-x.gitlab-ci/install-meson-project.sh82
-rwxr-xr-x.gitlab-ci/run-eslint128
-rw-r--r--.gitlab/issue_templates/Bug.md55
-rw-r--r--.gitlab/issue_templates/Feature.md30
-rw-r--r--.gitmodules3
-rw-r--r--.jscheckignore1
-rw-r--r--.settings/.jsdtscope11
-rw-r--r--COPYING339
-rw-r--r--HACKING.md341
-rw-r--r--NEWS5659
-rw-r--r--README.md43
-rw-r--r--config.h.meson38
-rw-r--r--data/00_org.gnome.shell.gschema.override6
-rw-r--r--data/50-gnome-shell-launchers.xml45
-rw-r--r--data/50-gnome-shell-screenshots.xml20
-rw-r--r--data/50-gnome-shell-system.xml24
-rw-r--r--data/README.osk-layouts75
-rw-r--r--data/cldr2json/README.md40
-rwxr-xr-xdata/cldr2json/cldr2json.py212
-rw-r--r--data/cldr2json/test/__init__.py0
-rw-r--r--data/cldr2json/test/data/fr-t-k0-android.xml138
-rw-r--r--data/cldr2json/test/data/fr.json614
-rwxr-xr-xdata/cldr2json/test/test_cldr2json.py212
-rw-r--r--data/dbus-interfaces/meson.build10
-rw-r--r--data/dbus-interfaces/net.hadess.PowerProfiles.xml76
-rw-r--r--data/dbus-interfaces/net.hadess.SensorProxy.xml5
-rw-r--r--data/dbus-interfaces/net.hadess.SwitcherooControl.xml46
-rw-r--r--data/dbus-interfaces/net.reactivated.Fprint.Device.xml78
-rw-r--r--data/dbus-interfaces/net.reactivated.Fprint.Manager.xml18
-rw-r--r--data/dbus-interfaces/org.Gtk.MountOperationHandler.xml32
-rw-r--r--data/dbus-interfaces/org.freedesktop.Application.xml12
-rw-r--r--data/dbus-interfaces/org.freedesktop.DBus.xml16
-rw-r--r--data/dbus-interfaces/org.freedesktop.GeoClue2.Agent.xml11
-rw-r--r--data/dbus-interfaces/org.freedesktop.GeoClue2.Manager.xml9
-rw-r--r--data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Cdma.xml13
-rw-r--r--data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Gsm.Network.xml19
-rw-r--r--data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml6
-rw-r--r--data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.ModemCdma.xml5
-rw-r--r--data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.xml5
-rw-r--r--data/dbus-interfaces/org.freedesktop.Notifications.xml35
-rw-r--r--data/dbus-interfaces/org.freedesktop.PackageKit.Offline.xml13
-rw-r--r--data/dbus-interfaces/org.freedesktop.UPower.Device.xml11
-rw-r--r--data/dbus-interfaces/org.freedesktop.UPower.xml5
-rw-r--r--data/dbus-interfaces/org.freedesktop.bolt1.Device.xml18
-rw-r--r--data/dbus-interfaces/org.freedesktop.bolt1.Manager.xml15
-rw-r--r--data/dbus-interfaces/org.freedesktop.impl.portal.Access.xml15
-rw-r--r--data/dbus-interfaces/org.freedesktop.impl.portal.PermissionStore.xml31
-rw-r--r--data/dbus-interfaces/org.freedesktop.impl.portal.Request.xml5
-rw-r--r--data/dbus-interfaces/org.freedesktop.login1.Manager.xml33
-rw-r--r--data/dbus-interfaces/org.freedesktop.login1.Session.xml15
-rw-r--r--data/dbus-interfaces/org.freedesktop.login1.User.xml6
-rw-r--r--data/dbus-interfaces/org.freedesktop.realmd.Provider.xml13
-rw-r--r--data/dbus-interfaces/org.freedesktop.realmd.Realm.xml20
-rw-r--r--data/dbus-interfaces/org.freedesktop.realmd.Service.xml15
-rw-r--r--data/dbus-interfaces/org.gnome.Mutter.ScreenCast.xml191
-rw-r--r--data/dbus-interfaces/org.gnome.ScreenSaver.xml18
-rw-r--r--data/dbus-interfaces/org.gnome.SessionManager.EndSessionDialog.xml16
-rw-r--r--data/dbus-interfaces/org.gnome.SessionManager.Inhibitor.xml13
-rw-r--r--data/dbus-interfaces/org.gnome.SessionManager.Presence.xml11
-rw-r--r--data/dbus-interfaces/org.gnome.SessionManager.xml23
-rw-r--r--data/dbus-interfaces/org.gnome.SettingsDaemon.Color.xml6
-rw-r--r--data/dbus-interfaces/org.gnome.SettingsDaemon.Power.Screen.xml5
-rw-r--r--data/dbus-interfaces/org.gnome.SettingsDaemon.Rfkill.xml11
-rw-r--r--data/dbus-interfaces/org.gnome.SettingsDaemon.Wacom.xml13
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.AudioDeviceSelection.xml12
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.CalendarServer.xml19
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.ClocksIntegration.xml15
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.Extensions.xml267
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.HotplugSniffer.xml8
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.Introspect.xml89
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.PadOsd.xml28
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.PerfHelper.xml13
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.PortalHelper.xml19
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.Screencast.xml96
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.Screenshot.xml145
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.Wacom.PadOsd.xml8
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.WeatherIntegration.xml16
-rw-r--r--data/dbus-interfaces/org.gnome.Shell.xml47
-rw-r--r--data/dbus-interfaces/org.gnome.ShellSearchProvider.xml73
-rw-r--r--data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml87
-rw-r--r--data/dbus-interfaces/org.gtk.Notifications.xml13
-rw-r--r--data/dbus-interfaces/org.mpris.MediaPlayer2.Player.xml12
-rw-r--r--data/dbus-interfaces/org.mpris.MediaPlayer2.xml7
-rw-r--r--data/emoji.json15740
-rw-r--r--data/gnome-shell-dbus-interfaces.gresource.xml60
-rw-r--r--data/gnome-shell-icons.gresource.xml33
-rw-r--r--data/gnome-shell-osk-layouts.gresource.xml65
-rw-r--r--data/gnome-shell-overrides-migration.desktop.in5
-rw-r--r--data/gnome-shell-theme.gresource.xml25
-rw-r--r--data/gnome-shell.portal4
-rw-r--r--data/icons/hicolor/scalable/apps/org.gnome.Shell.Extensions.svg1
-rw-r--r--data/icons/hicolor/symbolic/apps/org.gnome.Shell.Extensions-symbolic.svg1
-rw-r--r--data/icons/meson.build1
-rw-r--r--data/icons/scalable/actions/carousel-arrow-next-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/carousel-arrow-previous-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/color-pick.svg94
-rw-r--r--data/icons/scalable/actions/dark-mode-symbolic.svg2
-rw-r--r--data/icons/scalable/actions/pointer-double-click-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/pointer-drag-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/pointer-primary-click-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/pointer-secondary-click-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/preview-close-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/record-screen-symbolic.svg9
-rw-r--r--data/icons/scalable/actions/screencast-recorded-symbolic.svg8
-rw-r--r--data/icons/scalable/actions/screenshot-recorded-symbolic.svg8
-rw-r--r--data/icons/scalable/actions/screenshot-ui-area-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/screenshot-ui-display-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/screenshot-ui-show-pointer-symbolic.svg4
-rw-r--r--data/icons/scalable/actions/screenshot-ui-window-symbolic.svg4
-rw-r--r--data/icons/scalable/status/keyboard-caps-lock-symbolic.svg4
-rw-r--r--data/icons/scalable/status/keyboard-enter-symbolic.svg4
-rw-r--r--data/icons/scalable/status/keyboard-hide-symbolic.svg5
-rw-r--r--data/icons/scalable/status/keyboard-layout-symbolic.svg4
-rw-r--r--data/icons/scalable/status/keyboard-shift-symbolic.svg4
-rw-r--r--data/icons/scalable/status/message-indicator-symbolic.svg4
-rw-r--r--data/icons/scalable/status/no-events-symbolic.svg7
-rw-r--r--data/icons/scalable/status/no-notifications-symbolic.svg4
-rw-r--r--data/icons/scalable/status/screen-privacy-disabled-symbolic.svg4
-rw-r--r--data/icons/scalable/status/screen-privacy-symbolic.svg4
-rw-r--r--data/icons/scalable/status/stop-symbolic.svg2
-rw-r--r--data/meson.build138
-rw-r--r--data/org.gnome.Shell-disable-extensions.service15
-rw-r--r--data/org.gnome.Shell-symbolic.Source.svg511
-rw-r--r--data/org.gnome.Shell.Extensions.desktop.in.in10
-rw-r--r--data/org.gnome.Shell.PortalHelper.desktop.in.in10
-rw-r--r--data/org.gnome.Shell.PortalHelper.service.in3
-rw-r--r--data/org.gnome.Shell.desktop.in.in13
-rw-r--r--data/org.gnome.Shell.target10
-rw-r--r--data/org.gnome.Shell@wayland.service.in37
-rw-r--r--data/org.gnome.Shell@x11.service.in42
-rw-r--r--data/org.gnome.shell.gschema.xml.in364
-rw-r--r--data/osk-layouts/am.json1051
-rw-r--r--data/osk-layouts/ara.json793
-rw-r--r--data/osk-layouts/at.json978
-rw-r--r--data/osk-layouts/be.json1021
-rw-r--r--data/osk-layouts/bg.json955
-rw-r--r--data/osk-layouts/by.json965
-rw-r--r--data/osk-layouts/ca.json1007
-rw-r--r--data/osk-layouts/ch+fr.json978
-rw-r--r--data/osk-layouts/ch.json978
-rw-r--r--data/osk-layouts/cz.json1021
-rw-r--r--data/osk-layouts/de.json978
-rw-r--r--data/osk-layouts/dk.json1012
-rw-r--r--data/osk-layouts/ee.json1088
-rw-r--r--data/osk-layouts/epo.json1123
-rw-r--r--data/osk-layouts/es+cat.json1014
-rw-r--r--data/osk-layouts/es.json1016
-rw-r--r--data/osk-layouts/fi.json992
-rw-r--r--data/osk-layouts/fr.json1026
-rw-r--r--data/osk-layouts/ge.json705
-rw-r--r--data/osk-layouts/gr.json940
-rw-r--r--data/osk-layouts/hr.json939
-rw-r--r--data/osk-layouts/hu.json987
-rw-r--r--data/osk-layouts/id.json915
-rw-r--r--data/osk-layouts/il.json718
-rw-r--r--data/osk-layouts/in+bolnagri.json745
-rw-r--r--data/osk-layouts/in+mal.json986
-rw-r--r--data/osk-layouts/ir.json797
-rw-r--r--data/osk-layouts/is.json991
-rw-r--r--data/osk-layouts/it.json988
-rw-r--r--data/osk-layouts/ke.json985
-rw-r--r--data/osk-layouts/kg.json971
-rw-r--r--data/osk-layouts/kh.json817
-rw-r--r--data/osk-layouts/kr.json720
-rw-r--r--data/osk-layouts/la.json808
-rw-r--r--data/osk-layouts/latam.json1015
-rw-r--r--data/osk-layouts/lt.json1055
-rw-r--r--data/osk-layouts/lv.json1053
-rw-r--r--data/osk-layouts/mk.json965
-rw-r--r--data/osk-layouts/mn.json971
-rw-r--r--data/osk-layouts/my.json915
-rw-r--r--data/osk-layouts/nl.json992
-rw-r--r--data/osk-layouts/no.json1012
-rw-r--r--data/osk-layouts/ph.json915
-rw-r--r--data/osk-layouts/pl.json987
-rw-r--r--data/osk-layouts/pt.json992
-rw-r--r--data/osk-layouts/ro.json955
-rw-r--r--data/osk-layouts/rs.json965
-rw-r--r--data/osk-layouts/ru.json965
-rw-r--r--data/osk-layouts/se.json1046
-rw-r--r--data/osk-layouts/si.json926
-rw-r--r--data/osk-layouts/sk.json1056
-rw-r--r--data/osk-layouts/th.json1135
-rw-r--r--data/osk-layouts/tr.json974
-rw-r--r--data/osk-layouts/ua.json969
-rw-r--r--data/osk-layouts/uk.json984
-rw-r--r--data/osk-layouts/us-extended.json1043
-rw-r--r--data/osk-layouts/us.json983
-rw-r--r--data/osk-layouts/vn.json1051
-rw-r--r--data/osk-layouts/za.json997
-rw-r--r--data/perf-background.xml.in31
-rw-r--r--data/theme/README.md32
-rw-r--r--data/theme/calendar-today-light.svg1
-rw-r--r--data/theme/calendar-today.svg1
-rw-r--r--data/theme/checkbox-focused.svg1
-rw-r--r--data/theme/checkbox-off-focused-light.svg220
-rw-r--r--data/theme/checkbox-off-focused.svg1
-rw-r--r--data/theme/checkbox-off-light.svg211
-rw-r--r--data/theme/checkbox-off.svg1
-rw-r--r--data/theme/checkbox.svg1
-rw-r--r--data/theme/dash-placeholder.svg84
-rw-r--r--data/theme/gnome-shell-high-contrast.css2883
-rw-r--r--data/theme/gnome-shell-high-contrast.scss38
-rw-r--r--data/theme/gnome-shell-sass/COPYING339
-rw-r--r--data/theme/gnome-shell-sass/NEWS0
-rw-r--r--data/theme/gnome-shell-sass/README.md16
-rw-r--r--data/theme/gnome-shell-sass/_colors.scss75
-rw-r--r--data/theme/gnome-shell-sass/_common.scss222
-rw-r--r--data/theme/gnome-shell-sass/_drawing.scss336
-rw-r--r--data/theme/gnome-shell-sass/_high-contrast-colors.scss72
-rw-r--r--data/theme/gnome-shell-sass/_palette.scss46
-rw-r--r--data/theme/gnome-shell-sass/_widgets.scss51
-rw-r--r--data/theme/gnome-shell-sass/gnome-shell-sass.doap37
-rw-r--r--data/theme/gnome-shell-sass/widgets/_a11y.scss24
-rw-r--r--data/theme/gnome-shell-sass/widgets/_app-grid.scss161
-rw-r--r--data/theme/gnome-shell-sass/widgets/_base.scss18
-rw-r--r--data/theme/gnome-shell-sass/widgets/_buttons.scss19
-rw-r--r--data/theme/gnome-shell-sass/widgets/_calendar.scss257
-rw-r--r--data/theme/gnome-shell-sass/widgets/_check-box.scss18
-rw-r--r--data/theme/gnome-shell-sass/widgets/_corner-ripple.scss15
-rw-r--r--data/theme/gnome-shell-sass/widgets/_dash.scss75
-rw-r--r--data/theme/gnome-shell-sass/widgets/_dialogs.scss172
-rw-r--r--data/theme/gnome-shell-sass/widgets/_entries.scss31
-rw-r--r--data/theme/gnome-shell-sass/widgets/_hotplug.scss10
-rw-r--r--data/theme/gnome-shell-sass/widgets/_ibus-popup.scss39
-rw-r--r--data/theme/gnome-shell-sass/widgets/_keyboard.scss159
-rw-r--r--data/theme/gnome-shell-sass/widgets/_login-dialog.scss201
-rw-r--r--data/theme/gnome-shell-sass/widgets/_looking-glass.scss144
-rw-r--r--data/theme/gnome-shell-sass/widgets/_message-list.scss180
-rw-r--r--data/theme/gnome-shell-sass/widgets/_misc.scss65
-rw-r--r--data/theme/gnome-shell-sass/widgets/_notifications.scss61
-rw-r--r--data/theme/gnome-shell-sass/widgets/_osd.scss64
-rw-r--r--data/theme/gnome-shell-sass/widgets/_overview.scss13
-rw-r--r--data/theme/gnome-shell-sass/widgets/_panel.scss233
-rw-r--r--data/theme/gnome-shell-sass/widgets/_popovers.scss182
-rw-r--r--data/theme/gnome-shell-sass/widgets/_quick-settings.scss118
-rw-r--r--data/theme/gnome-shell-sass/widgets/_screen-shield.scss78
-rw-r--r--data/theme/gnome-shell-sass/widgets/_screenshot.scss204
-rw-r--r--data/theme/gnome-shell-sass/widgets/_scrollbars.scss29
-rw-r--r--data/theme/gnome-shell-sass/widgets/_search-entry.scss26
-rw-r--r--data/theme/gnome-shell-sass/widgets/_search-results.scss96
-rw-r--r--data/theme/gnome-shell-sass/widgets/_slider.scss22
-rw-r--r--data/theme/gnome-shell-sass/widgets/_switcher-popup.scss72
-rw-r--r--data/theme/gnome-shell-sass/widgets/_switches.scss16
-rw-r--r--data/theme/gnome-shell-sass/widgets/_window-picker.scss53
-rw-r--r--data/theme/gnome-shell-sass/widgets/_workspace-switcher.scss25
-rw-r--r--data/theme/gnome-shell-sass/widgets/_workspace-thumbnails.scss28
-rw-r--r--data/theme/gnome-shell-start.svg343
-rw-r--r--data/theme/gnome-shell.css2784
-rw-r--r--data/theme/gnome-shell.scss6
-rw-r--r--data/theme/meson.build62
-rw-r--r--data/theme/pad-osd.css29
-rw-r--r--data/theme/process-working.svg3084
-rw-r--r--data/theme/running-indicator.svg130
-rw-r--r--data/theme/toggle-off-hc.svg1
-rw-r--r--data/theme/toggle-off-light.svg1
-rw-r--r--data/theme/toggle-off.svg1
-rw-r--r--data/theme/toggle-on-hc.svg1
-rw-r--r--data/theme/toggle-on-light.svg1
-rw-r--r--data/theme/toggle-on.svg1
-rw-r--r--data/theme/workspace-placeholder.svg119
-rwxr-xr-xdata/update-osk-layouts.sh47
-rw-r--r--docs/reference/meson.build5
-rw-r--r--docs/reference/shell/meson.build51
-rw-r--r--docs/reference/shell/shell-docs.sgml69
-rw-r--r--docs/reference/shell/version.xml.in1
-rw-r--r--docs/reference/st/meson.build23
-rw-r--r--docs/reference/st/st-docs.sgml69
-rw-r--r--docs/reference/st/version.xml.in1
-rw-r--r--gnome-shell.doap82
-rw-r--r--js/dbusServices/dbus-service.in5
-rw-r--r--js/dbusServices/dbus-service.service.in3
-rw-r--r--js/dbusServices/dbusService.js188
-rw-r--r--js/dbusServices/extensions/css/application.css9
-rw-r--r--js/dbusServices/extensions/extensionPrefsDialog.js178
-rw-r--r--js/dbusServices/extensions/extensionsService.js161
-rw-r--r--js/dbusServices/extensions/main.js23
-rw-r--r--js/dbusServices/extensions/ui/extension-error-page.ui112
-rw-r--r--js/dbusServices/meson.build44
-rw-r--r--js/dbusServices/notifications/main.js11
-rw-r--r--js/dbusServices/notifications/notificationDaemon.js160
-rw-r--r--js/dbusServices/org.gnome.ScreenSaver.src.gresource.xml11
-rw-r--r--js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml19
-rw-r--r--js/dbusServices/org.gnome.Shell.Notifications.src.gresource.xml11
-rw-r--r--js/dbusServices/org.gnome.Shell.Screencast.src.gresource.xml11
-rw-r--r--js/dbusServices/screencast/main.js14
-rw-r--r--js/dbusServices/screencast/screencastService.js498
-rw-r--r--js/dbusServices/screensaver/main.js11
-rw-r--r--js/dbusServices/screensaver/screenSaverService.js70
-rw-r--r--js/gdm/authList.js176
-rw-r--r--js/gdm/authPrompt.js707
-rw-r--r--js/gdm/batch.js206
-rw-r--r--js/gdm/credentialManager.js27
-rw-r--r--js/gdm/loginDialog.js1293
-rw-r--r--js/gdm/oVirt.js51
-rw-r--r--js/gdm/realmd.js112
-rw-r--r--js/gdm/util.js785
-rw-r--r--js/gdm/vmware.js54
-rw-r--r--js/js-resources.gresource.xml152
-rw-r--r--js/meson.build16
-rw-r--r--js/misc/config.js.in23
-rw-r--r--js/misc/dbusUtils.js68
-rw-r--r--js/misc/extensionUtils.js318
-rw-r--r--js/misc/fileUtils.js68
-rw-r--r--js/misc/gnomeSession.js45
-rw-r--r--js/misc/history.js114
-rw-r--r--js/misc/ibusManager.js398
-rw-r--r--js/misc/inputMethod.js386
-rw-r--r--js/misc/introspect.js217
-rw-r--r--js/misc/jsParse.js236
-rw-r--r--js/misc/keyboardManager.js163
-rw-r--r--js/misc/loginManager.js247
-rw-r--r--js/misc/meson.build15
-rw-r--r--js/misc/modemManager.js298
-rw-r--r--js/misc/objectManager.js261
-rw-r--r--js/misc/params.js28
-rw-r--r--js/misc/parentalControlsManager.js153
-rw-r--r--js/misc/permissionStore.js16
-rw-r--r--js/misc/signalTracker.js269
-rw-r--r--js/misc/signals.js22
-rw-r--r--js/misc/smartcardManager.js119
-rw-r--r--js/misc/systemActions.js474
-rw-r--r--js/misc/util.js617
-rw-r--r--js/misc/weather.js326
-rw-r--r--js/perf/basic.js146
-rw-r--r--js/perf/core.js271
-rw-r--r--js/perf/hwtest.js319
-rw-r--r--js/portal-resources.gresource.xml10
-rw-r--r--js/portalHelper/main.js381
-rw-r--r--js/ui/accessDialog.js160
-rw-r--r--js/ui/altTab.js1134
-rw-r--r--js/ui/animation.js196
-rw-r--r--js/ui/appDisplay.js3273
-rw-r--r--js/ui/appFavorites.js212
-rw-r--r--js/ui/appMenu.js287
-rw-r--r--js/ui/audioDeviceSelection.js207
-rw-r--r--js/ui/background.js842
-rw-r--r--js/ui/backgroundMenu.js67
-rw-r--r--js/ui/barLevel.js262
-rw-r--r--js/ui/boxpointer.js654
-rw-r--r--js/ui/calendar.js1031
-rw-r--r--js/ui/checkBox.js40
-rw-r--r--js/ui/closeDialog.js207
-rw-r--r--js/ui/components/__init__.js58
-rw-r--r--js/ui/components/automountManager.js256
-rw-r--r--js/ui/components/autorunManager.js345
-rw-r--r--js/ui/components/keyring.js229
-rw-r--r--js/ui/components/networkAgent.js877
-rw-r--r--js/ui/components/polkitAgent.js471
-rw-r--r--js/ui/components/telepathyClient.js1019
-rw-r--r--js/ui/ctrlAltTab.js203
-rw-r--r--js/ui/dash.js992
-rw-r--r--js/ui/dateMenu.js980
-rw-r--r--js/ui/dialog.js359
-rw-r--r--js/ui/dnd.js841
-rw-r--r--js/ui/edgeDragAction.js89
-rw-r--r--js/ui/endSessionDialog.js798
-rw-r--r--js/ui/environment.js470
-rw-r--r--js/ui/extensionDownloader.js282
-rw-r--r--js/ui/extensionSystem.js687
-rw-r--r--js/ui/focusCaretTracker.js91
-rw-r--r--js/ui/grabHelper.js291
-rw-r--r--js/ui/ibusCandidatePopup.js359
-rw-r--r--js/ui/iconGrid.js1415
-rw-r--r--js/ui/inhibitShortcutsDialog.js160
-rw-r--r--js/ui/init.js6
-rw-r--r--js/ui/kbdA11yDialog.js76
-rw-r--r--js/ui/keyboard.js2275
-rw-r--r--js/ui/layout.js1451
-rw-r--r--js/ui/lightbox.js289
-rw-r--r--js/ui/locatePointer.js39
-rw-r--r--js/ui/lookingGlass.js1670
-rw-r--r--js/ui/magnifier.js2093
-rw-r--r--js/ui/main.js958
-rw-r--r--js/ui/messageList.js760
-rw-r--r--js/ui/messageTray.js1423
-rw-r--r--js/ui/modalDialog.js288
-rw-r--r--js/ui/mpris.js297
-rw-r--r--js/ui/notificationDaemon.js771
-rw-r--r--js/ui/osdMonitorLabeler.js117
-rw-r--r--js/ui/osdWindow.js192
-rw-r--r--js/ui/overview.js715
-rw-r--r--js/ui/overviewControls.js867
-rw-r--r--js/ui/padOsd.js991
-rw-r--r--js/ui/pageIndicators.js116
-rw-r--r--js/ui/panel.js774
-rw-r--r--js/ui/panelMenu.js233
-rw-r--r--js/ui/pointerA11yTimeout.js134
-rw-r--r--js/ui/pointerWatcher.js125
-rw-r--r--js/ui/popupMenu.js1415
-rw-r--r--js/ui/quickSettings.js717
-rw-r--r--js/ui/remoteSearch.js332
-rw-r--r--js/ui/ripples.js110
-rw-r--r--js/ui/runDialog.js256
-rw-r--r--js/ui/screenShield.js686
-rw-r--r--js/ui/screenshot.js2897
-rw-r--r--js/ui/scripting.js340
-rw-r--r--js/ui/search.js945
-rw-r--r--js/ui/searchController.js325
-rw-r--r--js/ui/sessionMode.js206
-rw-r--r--js/ui/shellDBus.js540
-rw-r--r--js/ui/shellEntry.js206
-rw-r--r--js/ui/shellMountOperation.js752
-rw-r--r--js/ui/slider.js218
-rw-r--r--js/ui/status/accessibility.js153
-rw-r--r--js/ui/status/autoRotate.js45
-rw-r--r--js/ui/status/bluetooth.js211
-rw-r--r--js/ui/status/brightness.js64
-rw-r--r--js/ui/status/darkMode.js49
-rw-r--r--js/ui/status/dwellClick.js83
-rw-r--r--js/ui/status/keyboard.js1095
-rw-r--r--js/ui/status/location.js371
-rw-r--r--js/ui/status/network.js2095
-rw-r--r--js/ui/status/nightLight.js70
-rw-r--r--js/ui/status/powerProfiles.js126
-rw-r--r--js/ui/status/remoteAccess.js230
-rw-r--r--js/ui/status/rfkill.js136
-rw-r--r--js/ui/status/system.js348
-rw-r--r--js/ui/status/thunderbolt.js332
-rw-r--r--js/ui/status/volume.js458
-rw-r--r--js/ui/swipeTracker.js787
-rw-r--r--js/ui/switchMonitor.js122
-rw-r--r--js/ui/switcherPopup.js688
-rw-r--r--js/ui/unlockDialog.js899
-rw-r--r--js/ui/userWidget.js212
-rw-r--r--js/ui/welcomeDialog.js64
-rw-r--r--js/ui/windowAttentionHandler.js100
-rw-r--r--js/ui/windowManager.js1927
-rw-r--r--js/ui/windowMenu.js252
-rw-r--r--js/ui/windowPreview.js681
-rw-r--r--js/ui/workspace.js1457
-rw-r--r--js/ui/workspaceAnimation.js496
-rw-r--r--js/ui/workspaceSwitcherPopup.js101
-rw-r--r--js/ui/workspaceThumbnail.js1436
-rw-r--r--js/ui/workspacesView.js1156
-rw-r--r--js/ui/xdndHandler.js116
-rw-r--r--lint/eslintrc-gjs.yml264
-rw-r--r--lint/eslintrc-legacy.yml24
-rw-r--r--lint/eslintrc-shell.yml32
-rw-r--r--man/gnome-shell.1131
-rw-r--r--man/gnome-shell.txt93
-rw-r--r--man/meson.build7
-rw-r--r--man/stylesheet.xsl27
-rw-r--r--meson.build332
-rwxr-xr-xmeson/check-version.py32
-rw-r--r--meson/generate-manpages.py23
-rw-r--r--meson/generate-stylesheets.py19
-rw-r--r--meson_options.txt47
-rw-r--r--po/LINGUAS88
-rw-r--r--po/POTFILES.in108
-rw-r--r--po/POTFILES.skip5
-rw-r--r--po/ab.po6952
-rw-r--r--po/af.po2912
-rw-r--r--po/an.po2315
-rw-r--r--po/ar.po3168
-rw-r--r--po/as.po2396
-rw-r--r--po/ast.po1630
-rw-r--r--po/be.po2917
-rw-r--r--po/bg.po3171
-rw-r--r--po/bn.po1203
-rw-r--r--po/bn_IN.po3225
-rw-r--r--po/bs.po1735
-rw-r--r--po/ca.po3019
-rw-r--r--po/ca@valencia.po2395
-rw-r--r--po/ckb.po2619
-rw-r--r--po/cs.po3008
-rw-r--r--po/da.po4189
-rw-r--r--po/de.po3395
-rw-r--r--po/el.po4266
-rw-r--r--po/en_GB.po4231
-rw-r--r--po/eo.po3467
-rw-r--r--po/es.po4478
-rw-r--r--po/et.po2885
-rw-r--r--po/eu.po3197
-rw-r--r--po/fa.po4027
-rw-r--r--po/fi.po4027
-rw-r--r--po/fr.po3366
-rw-r--r--po/fur.po3491
-rw-r--r--po/fy.po2981
-rw-r--r--po/ga.po1668
-rw-r--r--po/gd.po3623
-rw-r--r--po/gl.po3306
-rw-r--r--po/gu.po2375
-rw-r--r--po/he.po4432
-rw-r--r--po/hi.po2381
-rw-r--r--po/hr.po3368
-rw-r--r--po/hu.po2978
-rw-r--r--po/ia.po1950
-rw-r--r--po/id.po3137
-rw-r--r--po/ie.po3045
-rw-r--r--po/is.po3439
-rw-r--r--po/it.po3215
-rw-r--r--po/ja.po3236
-rw-r--r--po/ka.po3076
-rw-r--r--po/kab.po2948
-rw-r--r--po/kk.po3529
-rw-r--r--po/km.po1740
-rw-r--r--po/kn.po2220
-rw-r--r--po/ko.po3115
-rw-r--r--po/ku.po1575
-rw-r--r--po/ky.po1753
-rw-r--r--po/lt.po3612
-rw-r--r--po/lv.po3324
-rw-r--r--po/meson.build1
-rw-r--r--po/mjw.po2758
-rw-r--r--po/mk.po1640
-rw-r--r--po/ml.po2852
-rw-r--r--po/mr.po2455
-rw-r--r--po/ms.po3068
-rw-r--r--po/nb.po2977
-rw-r--r--po/ne.po3407
-rw-r--r--po/nl.po3369
-rw-r--r--po/nn.po1713
-rw-r--r--po/oc.po4141
-rw-r--r--po/or.po2368
-rw-r--r--po/os.po2803
-rw-r--r--po/pa.po4381
-rw-r--r--po/pl.po2993
-rw-r--r--po/pt.po3776
-rw-r--r--po/pt_BR.po3643
-rw-r--r--po/ro.po3867
-rw-r--r--po/ru.po3272
-rw-r--r--po/si.po1667
-rw-r--r--po/sk.po3717
-rw-r--r--po/sl.po2993
-rw-r--r--po/sr.po3730
-rw-r--r--po/sr@latin.po2641
-rw-r--r--po/sv.po3397
-rw-r--r--po/ta.po2087
-rw-r--r--po/te.po1965
-rw-r--r--po/tg.po2803
-rw-r--r--po/th.po1941
-rw-r--r--po/tr.po2972
-rw-r--r--po/ug.po2208
-rw-r--r--po/uk.po3277
-rw-r--r--po/uz@cyrillic.po2064
-rw-r--r--po/vi.po4033
-rw-r--r--po/zh_CN.po3346
-rw-r--r--po/zh_HK.po2070
-rw-r--r--po/zh_TW.po3623
-rw-r--r--src/calendar-server/README1
-rw-r--r--src/calendar-server/calendar-debug.h50
-rw-r--r--src/calendar-server/calendar-sources.c506
-rw-r--r--src/calendar-server/calendar-sources.h64
-rw-r--r--src/calendar-server/evolution-calendar.desktop.in8
-rw-r--r--src/calendar-server/gnome-shell-calendar-server.c1131
-rw-r--r--src/calendar-server/meson.build37
-rw-r--r--src/calendar-server/org.gnome.Shell.CalendarServer.service.in3
-rwxr-xr-xsrc/data-to-c.pl37
-rwxr-xr-xsrc/gnome-shell-extension-prefs31
-rwxr-xr-xsrc/gnome-shell-extension-tool.in59
-rwxr-xr-xsrc/gnome-shell-perf-tool.in326
-rw-r--r--src/gnome-shell-plugin.c394
-rw-r--r--src/gnome-shell-portal-helper.c52
-rw-r--r--src/gtkactionmuxer.c945
-rw-r--r--src/gtkactionmuxer.h64
-rw-r--r--src/gtkactionobservable.c78
-rw-r--r--src/gtkactionobservable.h60
-rw-r--r--src/gtkactionobserver.c189
-rw-r--r--src/gtkactionobserver.h91
-rw-r--r--src/hotplug-sniffer/hotplug-mimetypes.h141
-rw-r--r--src/hotplug-sniffer/hotplug-sniffer.c298
-rw-r--r--src/hotplug-sniffer/meson.build22
-rw-r--r--src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in3
-rw-r--r--src/hotplug-sniffer/shell-mime-sniffer.c590
-rw-r--r--src/hotplug-sniffer/shell-mime-sniffer.h46
-rw-r--r--src/main.c599
-rw-r--r--src/meson.build276
-rw-r--r--src/org.gtk.Application.xml19
-rw-r--r--src/run-js-test.c118
-rw-r--r--src/shell-action-modes.h35
-rw-r--r--src/shell-app-cache-private.h19
-rw-r--r--src/shell-app-cache.c404
-rw-r--r--src/shell-app-private.h24
-rw-r--r--src/shell-app-system-private.h9
-rw-r--r--src/shell-app-system.c586
-rw-r--r--src/shell-app-system.h32
-rw-r--r--src/shell-app-usage.c774
-rw-r--r--src/shell-app-usage.h23
-rw-r--r--src/shell-app.c1756
-rw-r--r--src/shell-app.h83
-rw-r--r--src/shell-blur-effect.c907
-rw-r--r--src/shell-blur-effect.h57
-rw-r--r--src/shell-embedded-window-private.h20
-rw-r--r--src/shell-embedded-window.c247
-rw-r--r--src/shell-embedded-window.h19
-rw-r--r--src/shell-global-private.h23
-rw-r--r--src/shell-global.c1860
-rw-r--r--src/shell-global.h94
-rw-r--r--src/shell-glsl-effect.c205
-rw-r--r--src/shell-glsl-effect.h62
-rw-r--r--src/shell-gtk-embed.c364
-rw-r--r--src/shell-gtk-embed.h20
-rw-r--r--src/shell-invert-lightness-effect.c152
-rw-r--r--src/shell-invert-lightness-effect.h41
-rw-r--r--src/shell-keyring-prompt.c832
-rw-r--r--src/shell-keyring-prompt.h57
-rw-r--r--src/shell-mount-operation.c189
-rw-r--r--src/shell-mount-operation.h41
-rw-r--r--src/shell-network-agent.c899
-rw-r--r--src/shell-network-agent.h73
-rw-r--r--src/shell-perf-helper.c376
-rw-r--r--src/shell-perf-log.c959
-rw-r--r--src/shell-perf-log.h75
-rw-r--r--src/shell-polkit-authentication-agent.c429
-rw-r--r--src/shell-polkit-authentication-agent.h35
-rw-r--r--src/shell-screenshot.c1217
-rw-r--r--src/shell-screenshot.h90
-rw-r--r--src/shell-secure-text-buffer.c191
-rw-r--r--src/shell-secure-text-buffer.h39
-rw-r--r--src/shell-square-bin.c43
-rw-r--r--src/shell-square-bin.h13
-rw-r--r--src/shell-stack.c196
-rw-r--r--src/shell-stack.h11
-rw-r--r--src/shell-tray-icon.c294
-rw-r--r--src/shell-tray-icon.h16
-rw-r--r--src/shell-tray-manager.c374
-rw-r--r--src/shell-tray-manager.h22
-rw-r--r--src/shell-util.c854
-rw-r--r--src/shell-util.h93
-rw-r--r--src/shell-window-preview-layout.c495
-rw-r--r--src/shell-window-preview-layout.h33
-rw-r--r--src/shell-window-preview.c177
-rw-r--r--src/shell-window-preview.h14
-rw-r--r--src/shell-window-tracker-private.h11
-rw-r--r--src/shell-window-tracker.c811
-rw-r--r--src/shell-window-tracker.h29
-rw-r--r--src/shell-wm-private.h63
-rw-r--r--src/shell-wm.c462
-rw-r--r--src/shell-wm.h32
-rw-r--r--src/shell-workspace-background.c226
-rw-r--r--src/shell-workspace-background.h14
-rw-r--r--src/st/croco/cr-additional-sel.c498
-rw-r--r--src/st/croco/cr-additional-sel.h98
-rw-r--r--src/st/croco/cr-attr-sel.c234
-rw-r--r--src/st/croco/cr-attr-sel.h74
-rw-r--r--src/st/croco/cr-cascade.c215
-rw-r--r--src/st/croco/cr-cascade.h74
-rw-r--r--src/st/croco/cr-declaration.c798
-rw-r--r--src/st/croco/cr-declaration.h136
-rw-r--r--src/st/croco/cr-doc-handler.c276
-rw-r--r--src/st/croco/cr-doc-handler.h298
-rw-r--r--src/st/croco/cr-enc-handler.c184
-rw-r--r--src/st/croco/cr-enc-handler.h94
-rw-r--r--src/st/croco/cr-fonts.c948
-rw-r--r--src/st/croco/cr-fonts.h315
-rw-r--r--src/st/croco/cr-input.c1191
-rw-r--r--src/st/croco/cr-input.h174
-rw-r--r--src/st/croco/cr-num.c313
-rw-r--r--src/st/croco/cr-num.h127
-rw-r--r--src/st/croco/cr-om-parser.c1142
-rw-r--r--src/st/croco/cr-om-parser.h98
-rw-r--r--src/st/croco/cr-parser.c4539
-rw-r--r--src/st/croco/cr-parser.h128
-rw-r--r--src/st/croco/cr-parsing-location.c171
-rw-r--r--src/st/croco/cr-parsing-location.h70
-rw-r--r--src/st/croco/cr-prop-list.c404
-rw-r--r--src/st/croco/cr-prop-list.h80
-rw-r--r--src/st/croco/cr-pseudo.c166
-rw-r--r--src/st/croco/cr-pseudo.h64
-rw-r--r--src/st/croco/cr-rgb.c604
-rw-r--r--src/st/croco/cr-rgb.h84
-rw-r--r--src/st/croco/cr-selector.c305
-rw-r--r--src/st/croco/cr-selector.h95
-rw-r--r--src/st/croco/cr-simple-sel.c323
-rw-r--r--src/st/croco/cr-simple-sel.h130
-rw-r--r--src/st/croco/cr-statement.c2784
-rw-r--r--src/st/croco/cr-statement.h440
-rw-r--r--src/st/croco/cr-string.c168
-rw-r--r--src/st/croco/cr-string.h76
-rw-r--r--src/st/croco/cr-stylesheet.c177
-rw-r--r--src/st/croco/cr-stylesheet.h102
-rw-r--r--src/st/croco/cr-term.c786
-rw-r--r--src/st/croco/cr-term.h190
-rw-r--r--src/st/croco/cr-tknzr.c2762
-rw-r--r--src/st/croco/cr-tknzr.h115
-rw-r--r--src/st/croco/cr-token.c636
-rw-r--r--src/st/croco/cr-token.h212
-rw-r--r--src/st/croco/cr-utils.c1330
-rw-r--r--src/st/croco/cr-utils.h246
-rw-r--r--src/st/croco/libcroco-config.h13
-rw-r--r--src/st/croco/libcroco.h42
-rw-r--r--src/st/meson.build220
-rw-r--r--src/st/st-adjustment.c1013
-rw-r--r--src/st/st-adjustment.h92
-rw-r--r--src/st/st-bin.c404
-rw-r--r--src/st/st-bin.h53
-rw-r--r--src/st/st-border-image.c171
-rw-r--r--src/st/st-border-image.h54
-rw-r--r--src/st/st-box-layout.c307
-rw-r--r--src/st/st-box-layout.h65
-rw-r--r--src/st/st-button.c1043
-rw-r--r--src/st/st-button.h85
-rw-r--r--src/st/st-clipboard.c348
-rw-r--r--src/st/st-clipboard.h105
-rw-r--r--src/st/st-drawing-area.c242
-rw-r--r--src/st/st-drawing-area.h44
-rw-r--r--src/st/st-entry.c1626
-rw-r--r--src/st/st-entry.h79
-rw-r--r--src/st/st-focus-manager.c256
-rw-r--r--src/st/st-focus-manager.h65
-rw-r--r--src/st/st-generic-accessible.c246
-rw-r--r--src/st/st-generic-accessible.h62
-rw-r--r--src/st/st-icon-colors.c133
-rw-r--r--src/st/st-icon-colors.h43
-rw-r--r--src/st/st-icon.c833
-rw-r--r--src/st/st-icon.h82
-rw-r--r--src/st/st-image-content.c346
-rw-r--r--src/st/st-image-content.h33
-rw-r--r--src/st/st-label.c549
-rw-r--r--src/st/st-label.h58
-rw-r--r--src/st/st-password-entry.c363
-rw-r--r--src/st/st-password-entry.h46
-rw-r--r--src/st/st-private.c804
-rw-r--r--src/st/st-private.h75
-rw-r--r--src/st/st-scroll-bar.c1014
-rw-r--r--src/st/st-scroll-bar.h53
-rw-r--r--src/st/st-scroll-view-fade.c461
-rw-r--r--src/st/st-scroll-view-fade.glsl77
-rw-r--r--src/st/st-scroll-view-fade.h36
-rw-r--r--src/st/st-scroll-view.c1327
-rw-r--r--src/st/st-scroll-view.h90
-rw-r--r--src/st/st-scrollable.c196
-rw-r--r--src/st/st-scrollable.h59
-rw-r--r--src/st/st-settings.c451
-rw-r--r--src/st/st-settings.h42
-rw-r--r--src/st/st-shadow.c307
-rw-r--r--src/st/st-shadow.h95
-rw-r--r--src/st/st-texture-cache.c1688
-rw-r--r--src/st/st-texture-cache.h120
-rw-r--r--src/st/st-theme-context.c492
-rw-r--r--src/st/st-theme-context.h65
-rw-r--r--src/st/st-theme-node-drawing.c2864
-rw-r--r--src/st/st-theme-node-private.h131
-rw-r--r--src/st/st-theme-node-transition.c470
-rw-r--r--src/st/st-theme-node-transition.h58
-rw-r--r--src/st/st-theme-node.c4325
-rw-r--r--src/st/st-theme-node.h368
-rw-r--r--src/st/st-theme-private.h41
-rw-r--r--src/st/st-theme.c1085
-rw-r--r--src/st/st-theme.h51
-rw-r--r--src/st/st-types.h52
-rw-r--r--src/st/st-viewport.c600
-rw-r--r--src/st/st-viewport.h40
-rw-r--r--src/st/st-widget-accessible.h76
-rw-r--r--src/st/st-widget.c3049
-rw-r--r--src/st/st-widget.h167
-rw-r--r--src/st/st.h.in3
-rw-r--r--src/st/test-theme.c637
-rw-r--r--src/st/test-theme.css107
-rw-r--r--src/tray/meson.build12
-rw-r--r--src/tray/na-tray-child.c504
-rw-r--r--src/tray/na-tray-child.h66
-rw-r--r--src/tray/na-tray-manager.c888
-rw-r--r--src/tray/na-tray-manager.h106
-rw-r--r--subprojects/extensions-app/COPYING339
-rw-r--r--subprojects/extensions-app/README.md32
-rw-r--r--subprojects/extensions-app/build-aux/flatpak/org.gnome.Extensions.json39
l---------subprojects/extensions-app/build-aux/meson/check-version.py1
-rw-r--r--subprojects/extensions-app/data/css/style.css21
l---------subprojects/extensions-app/data/dbus-interfaces/org.gnome.Shell.Extensions.xml1
-rw-r--r--subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.Devel.svg1
-rw-r--r--subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.svg1
-rw-r--r--subprojects/extensions-app/data/icons/hicolor/symbolic/apps/org.gnome.Extensions-symbolic.svg1
-rw-r--r--subprojects/extensions-app/data/icons/meson.build1
-rw-r--r--subprojects/extensions-app/data/meson.build50
-rw-r--r--subprojects/extensions-app/data/metainfo/extensions-main.pngbin0 -> 44240 bytes
-rw-r--r--subprojects/extensions-app/data/metainfo/extensions-remove.pngbin0 -> 42278 bytes
-rw-r--r--subprojects/extensions-app/data/metainfo/extensions-update.pngbin0 -> 53240 bytes
-rw-r--r--subprojects/extensions-app/data/metainfo/meson.build16
-rw-r--r--subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in67
-rw-r--r--subprojects/extensions-app/data/org.gnome.Extensions.data.gresource.xml.in13
-rw-r--r--subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in10
-rw-r--r--subprojects/extensions-app/data/org.gnome.Extensions.service.in3
-rw-r--r--subprojects/extensions-app/data/ui/extension-row.ui143
-rw-r--r--subprojects/extensions-app/data/ui/extensions-window.ui204
-rwxr-xr-xsubprojects/extensions-app/generate-translations.sh19
-rw-r--r--subprojects/extensions-app/js/gnome-extensions-app.in2
-rw-r--r--subprojects/extensions-app/js/main.js548
-rw-r--r--subprojects/extensions-app/js/meson.build40
-rw-r--r--subprojects/extensions-app/js/misc/config.js1
-rw-r--r--subprojects/extensions-app/js/org.gnome.Extensions.in6
-rw-r--r--subprojects/extensions-app/js/org.gnome.Extensions.src.gresource.xml.in9
-rw-r--r--subprojects/extensions-app/logo.pngbin0 -> 2365 bytes
-rw-r--r--subprojects/extensions-app/meson.build90
-rw-r--r--subprojects/extensions-app/meson_options.txt12
-rw-r--r--subprojects/extensions-app/po/.gitignore3
l---------subprojects/extensions-app/po/LINGUAS1
-rw-r--r--subprojects/extensions-app/po/meson.build1
l---------subprojects/extensions-app/subprojects/shew1
-rw-r--r--subprojects/extensions-tool/COPYING675
-rw-r--r--subprojects/extensions-tool/README.md23
-rw-r--r--subprojects/extensions-tool/completion/bash/gnome-extensions91
-rwxr-xr-xsubprojects/extensions-tool/generate-translations.sh19
-rw-r--r--subprojects/extensions-tool/man/gnome-extensions.1297
-rw-r--r--subprojects/extensions-tool/man/gnome-extensions.txt211
-rw-r--r--subprojects/extensions-tool/man/meson.build7
-rw-r--r--subprojects/extensions-tool/man/stylesheet.xsl27
-rw-r--r--subprojects/extensions-tool/meson.build84
-rw-r--r--subprojects/extensions-tool/meson_options.txt17
-rw-r--r--subprojects/extensions-tool/po/.gitignore3
l---------subprojects/extensions-tool/po/LINGUAS1
-rw-r--r--subprojects/extensions-tool/po/meson.build1
-rw-r--r--subprojects/extensions-tool/src/command-create.c506
-rw-r--r--subprojects/extensions-tool/src/command-disable.c126
-rw-r--r--subprojects/extensions-tool/src/command-enable.c126
-rw-r--r--subprojects/extensions-tool/src/command-info.c113
-rw-r--r--subprojects/extensions-tool/src/command-install.c213
-rw-r--r--subprojects/extensions-tool/src/command-list.c196
-rw-r--r--subprojects/extensions-tool/src/command-pack.c516
-rw-r--r--subprojects/extensions-tool/src/command-prefs.c115
-rw-r--r--subprojects/extensions-tool/src/command-reset.c86
-rw-r--r--subprojects/extensions-tool/src/command-uninstall.c114
-rw-r--r--subprojects/extensions-tool/src/commands.h38
-rw-r--r--subprojects/extensions-tool/src/common.h73
-rw-r--r--subprojects/extensions-tool/src/gnome-extensions-tool.gresource.xml11
-rw-r--r--subprojects/extensions-tool/src/main.c412
-rw-r--r--subprojects/extensions-tool/src/meson.build37
-rw-r--r--subprojects/extensions-tool/src/templates/00-plain.desktop.in5
-rw-r--r--subprojects/extensions-tool/src/templates/indicator.desktop.in5
-rw-r--r--subprojects/extensions-tool/src/templates/indicator/extension.js70
-rw-r--r--subprojects/extensions-tool/src/templates/indicator/stylesheet.css1
-rw-r--r--subprojects/extensions-tool/src/templates/meson.build13
-rw-r--r--subprojects/extensions-tool/src/templates/plain/extension.js34
-rw-r--r--subprojects/extensions-tool/src/templates/plain/stylesheet.css1
-rw-r--r--subprojects/gvc/.gitignore11
-rw-r--r--subprojects/gvc/.gitlab-ci.yml16
-rw-r--r--subprojects/gvc/.gitlab-ci/meson.build23
l---------subprojects/gvc/.gitlab-ci/subprojects/gvc1
-rw-r--r--subprojects/gvc/README.md12
-rw-r--r--subprojects/gvc/gvc-channel-map-private.h39
-rw-r--r--subprojects/gvc/gvc-channel-map.c246
-rw-r--r--subprojects/gvc/gvc-channel-map.h73
-rw-r--r--subprojects/gvc/gvc-mixer-card-private.h35
-rw-r--r--subprojects/gvc/gvc-mixer-card.c574
-rw-r--r--subprojects/gvc/gvc-mixer-card.h102
-rw-r--r--subprojects/gvc/gvc-mixer-control-private.h35
-rw-r--r--subprojects/gvc/gvc-mixer-control.c3881
-rw-r--r--subprojects/gvc/gvc-mixer-control.h155
-rw-r--r--subprojects/gvc/gvc-mixer-event-role.c229
-rw-r--r--subprojects/gvc/gvc-mixer-event-role.h57
-rw-r--r--subprojects/gvc/gvc-mixer-sink-input.c159
-rw-r--r--subprojects/gvc/gvc-mixer-sink-input.h57
-rw-r--r--subprojects/gvc/gvc-mixer-sink.c189
-rw-r--r--subprojects/gvc/gvc-mixer-sink.h57
-rw-r--r--subprojects/gvc/gvc-mixer-source-output.c160
-rw-r--r--subprojects/gvc/gvc-mixer-source-output.h57
-rw-r--r--subprojects/gvc/gvc-mixer-source.c189
-rw-r--r--subprojects/gvc/gvc-mixer-source.h57
-rw-r--r--subprojects/gvc/gvc-mixer-stream-private.h34
-rw-r--r--subprojects/gvc/gvc-mixer-stream.c1055
-rw-r--r--subprojects/gvc/gvc-mixer-stream.h146
-rw-r--r--subprojects/gvc/gvc-mixer-ui-device.c744
-rw-r--r--subprojects/gvc/gvc-mixer-ui-device.h85
-rw-r--r--subprojects/gvc/gvc-pulseaudio-fake.h30
-rw-r--r--subprojects/gvc/libgnome-volume-control.doap32
-rw-r--r--subprojects/gvc/meson.build137
-rw-r--r--subprojects/gvc/meson_options.txt41
-rw-r--r--subprojects/gvc/test-audio-device-selection.c84
-rw-r--r--subprojects/shew/COPYING502
-rw-r--r--subprojects/shew/README.md24
-rw-r--r--subprojects/shew/meson.build28
-rw-r--r--subprojects/shew/meson_options.txt4
-rw-r--r--subprojects/shew/src/meson.build29
-rw-r--r--subprojects/shew/src/shew-external-window-wayland.c117
-rw-r--r--subprojects/shew/src/shew-external-window-wayland.h30
-rw-r--r--subprojects/shew/src/shew-external-window-x11.c136
-rw-r--r--subprojects/shew/src/shew-external-window-x11.h30
-rw-r--r--subprojects/shew/src/shew-external-window.c161
-rw-r--r--subprojects/shew/src/shew-external-window.h43
-rw-r--r--subprojects/shew/src/shew-window-exporter.c217
-rw-r--r--subprojects/shew/src/shew-window-exporter.h38
-rw-r--r--tests/interactive/background-repeat.js28
-rw-r--r--tests/interactive/background-size.js82
-rw-r--r--tests/interactive/border-radius.js61
-rw-r--r--tests/interactive/border-width.js58
-rw-r--r--tests/interactive/borders.js133
-rw-r--r--tests/interactive/box-layout.js85
-rw-r--r--tests/interactive/box-shadow-animated.js80
-rw-r--r--tests/interactive/box-shadows.js56
-rw-r--r--tests/interactive/calendar.js28
-rw-r--r--tests/interactive/css-fonts.js40
-rw-r--r--tests/interactive/entry.js57
-rwxr-xr-xtests/interactive/gapplication.js104
-rw-r--r--tests/interactive/icons.js79
-rw-r--r--tests/interactive/inline-style.js46
-rw-r--r--tests/interactive/scroll-view-sizing.js395
-rw-r--r--tests/interactive/scrolling.js51
-rwxr-xr-xtests/interactive/test-title.js37
-rw-r--r--tests/interactive/transitions.js35
-rw-r--r--tests/meson.build29
-rwxr-xr-xtests/run-test.sh.in45
-rw-r--r--tests/testcommon/100-200.svg21
-rw-r--r--tests/testcommon/200-100.svg21
-rw-r--r--tests/testcommon/200-200.svg21
-rw-r--r--tests/testcommon/border-image.pngbin0 -> 981 bytes
-rw-r--r--tests/testcommon/face-plain.pngbin0 -> 4298 bytes
-rw-r--r--tests/testcommon/test.css112
-rw-r--r--tests/testcommon/ui.js28
-rw-r--r--tests/unit/highlighter.js106
-rw-r--r--tests/unit/insertSorted.js76
-rw-r--r--tests/unit/jsParse.js194
-rw-r--r--tests/unit/markup.js143
-rw-r--r--tests/unit/params.js32
-rw-r--r--tests/unit/signalTracker.js115
-rw-r--r--tests/unit/url.js77
-rw-r--r--tests/unit/versionCompare.js52
-rwxr-xr-xtools/build/gnome-shell-build-setup.sh348
-rw-r--r--tools/build/jhbuildrc-custom-example18
-rw-r--r--tools/build/jhbuildrc-gnome-shell54
-rwxr-xr-xtools/gnome-shell-overrides-migration.sh38
-rw-r--r--tools/meson.build4
920 files changed, 540078 insertions, 0 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
new file mode 100644
index 0000000..018e857
--- /dev/null
+++ b/.eslintrc.yml
@@ -0,0 +1,8 @@
+extends:
+ - ./lint/eslintrc-gjs.yml
+ - ./lint/eslintrc-shell.yml
+overrides:
+ - files:
+ - js/ui/init.js
+ parserOptions:
+ sourceType: module \ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..396a628
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,84 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+ABOUT-NLS
+ChangeLog
+INSTALL
+aclocal.m4
+autom4te.cache
+data/.osk-layout-workbench
+data/org.gnome.Shell.desktop
+data/org.gnome.Shell.desktop.in
+data/gnome-shell-extension-prefs.desktop
+data/gnome-shell-extension-prefs.desktop.in
+data/gnome-shell-theme.gresource
+data/gschemas.compiled
+data/perf-background.xml
+data/org.gnome.shell.gschema.xml
+data/org.gnome.shell.gschema.valid
+data/org.gnome.Shell.PortalHelper.desktop
+data/org.gnome.Shell.PortalHelper.service
+data/theme/.sass-cache
+data/theme/gnome-shell*.css.map
+data/theme/gnome-shell*.css
+docs/reference/*/*.args
+docs/reference/*/*.bak
+docs/reference/*/*.hierarchy
+docs/reference/*/*.interfaces
+docs/reference/*/*.prerequisites
+docs/reference/*/*.sgml
+docs/reference/*/*.signals
+docs/reference/*/*.stamp
+docs/reference/*/*.txt
+docs/reference/*/*.types
+docs/reference/*/html/
+docs/reference/*/xml/
+docs/reference/shell/doc-gen-*
+js/misc/config.js
+js/js-resources.c
+js/js-resources.h
+man/gnome-shell.1
+omf.make
+po/*.gmo
+po/gnome-shell.pot
+po/*.header
+po/*.sed
+po/*.sin
+po/.intltool-merge-cache
+po/Makevars.template
+po/POTFILES
+po/Rules-quot
+po/stamp-it
+scripts/launcher.pyc
+src/*.gir
+src/*.typelib
+src/*-enum-types.[ch]
+src/*-marshal.[ch]
+src/calendar-server/evolution-calendar.desktop
+src/calendar-server/org.gnome.Shell.CalendarServer.service
+src/gnome-shell
+src/gnome-shell-calendar-server
+src/gnome-shell-extension-tool
+src/gnome-shell-hotplug-sniffer
+src/gnome-shell-perf-helper
+src/gnome-shell-perf-tool
+src/gnome-shell-portal-helper
+src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
+src/org-gtk-application.[ch]
+src/run-js-test
+src/test-recorder
+src/test-recorder.ogg
+src/test-theme
+src/st.h
+src/stamp-st.h
+src/stamp-st.h.tmp
+src/st-scroll-view-fade-generated.c
+src/stamp-st-scroll-view-fade-generated.c
+stamp-h1
+tests/run-test.sh
+*~
+*.patch
+*.sw?
+.vscode
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..468549f
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,289 @@
+include:
+ - remote: 'https://gitlab.gnome.org/GNOME/citemplates/raw/HEAD/flatpak/flatpak_ci_initiative.yml'
+ - remote: 'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/34f4ade99434043f88e164933f570301fd18b125/templates/fedora.yml'
+ - remote: 'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/34f4ade99434043f88e164933f570301fd18b125/templates/ci-fairy.yml'
+
+stages:
+ - pre_review
+ - prep
+ - review
+ - build
+ - test
+ - analyze
+ - deploy
+
+default:
+ image: registry.gitlab.gnome.org/gnome/mutter/fedora/36:x86_64-2022-09-01.0
+ # Cancel jobs if newer commits are pushed to the branch
+ interruptible: true
+ # Auto-retry jobs in case of infra failures
+ retry:
+ max: 1
+ when:
+ - 'runner_system_failure'
+ - 'stuck_or_timeout_failure'
+ - 'scheduler_failure'
+ - 'api_failure'
+
+variables:
+ FDO_UPSTREAM_REPO: GNOME/gnome-shell
+ BUNDLE: "extensions-git.flatpak"
+ JS_LOG: "js-report.txt"
+ LINT_LOG: "eslint-report.xml"
+ LINT_MR_LOG: "eslint-mr-report.xml"
+
+workflow:
+ rules:
+ - if: '$CI_MERGE_REQUEST_IID'
+ - if: '$CI_COMMIT_TAG'
+ - if: '$CI_COMMIT_BRANCH'
+
+.pipeline_guard: &pipeline_guard
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+ - if: '$CI_COMMIT_TAG'
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+ - if: '$CI_COMMIT_BRANCH =~ /^gnome-[0-9-]+$/'
+ - when: 'manual'
+
+.gnome-shell.fedora:35:
+ variables:
+ FDO_DISTRIBUTION_VERSION: 35
+ FDO_DISTRIBUTION_TAG: '2022-01-18.0'
+ FDO_DISTRIBUTION_PACKAGES:
+ findutils
+ mozjs91-devel
+ nodejs
+ npm
+ meson
+ pkgconfig(gio-2.0)
+ pkgconfig(gio-unix-2.0)
+ pkgconfig(gnome-autoar-0)
+ pkgconfig(json-glib-1.0)
+ FDO_DISTRIBUTION_EXEC: |
+ # For static analysis with eslint
+ npm install -g eslint eslint-plugin-jsdoc &&
+
+ dnf group install -y 'Development Tools' \
+ 'C Development Tools and Libraries' &&
+
+ ./.gitlab-ci/install-meson-project.sh \
+ --subdir subprojects/extensions-tool/ \
+ --prepare ./generate-translations.sh \
+ -Dman=false \
+ https://gitlab.gnome.org/GNOME/gnome-shell.git \
+ main &&
+
+ dnf clean all
+
+check_commit_log:
+ extends:
+ - .fdo.ci-fairy
+ stage: pre_review
+ variables:
+ GIT_DEPTH: "100"
+ script:
+ - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
+ then
+ ci-fairy check-commits --junit-xml=commit-message-junit-report.xml ;
+ else
+ echo "Not a merge request" ;
+ fi
+ <<: *pipeline_guard
+ artifacts:
+ expire_in: 1 week
+ paths:
+ - commit-message-junit-report.xml
+ reports:
+ junit: commit-message-junit-report.xml
+
+check-merge-request:
+ extends:
+ - .fdo.ci-fairy
+ stage: pre_review
+ script:
+ - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
+ then
+ ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-merge-request-report.xml ;
+ else
+ echo "Not a merge request" ;
+ fi
+ <<: *pipeline_guard
+ artifacts:
+ expire_in: 1 week
+ paths:
+ - check-merge-request-report.xml
+ reports:
+ junit: check-merge-request-report.xml
+
+build-fedora-container:
+ extends:
+ - .fdo.container-build@fedora@x86_64
+ - .gnome-shell.fedora:35
+ stage: prep
+
+js_check:
+ extends:
+ - .fdo.distribution-image@fedora
+ - .gnome-shell.fedora:35
+ stage: review
+ script:
+ - find js -name '*.js' $(printf "! -wholename %s " $(cat .jscheckignore)) -exec js91 -c '{}' ';' 2>&1 | tee $JS_LOG
+ - (! grep -q . $JS_LOG)
+ artifacts:
+ paths:
+ - ${JS_LOG}
+ when: on_failure
+
+eslint:
+ extends:
+ - .fdo.distribution-image@fedora
+ - .gnome-shell.fedora:35
+ stage: review
+ script:
+ - export NODE_PATH=$(npm root -g)
+ - ./.gitlab-ci/run-eslint --output-file ${LINT_LOG} --format junit
+ artifacts:
+ reports:
+ junit: ${LINT_LOG}
+ when: always
+
+eslint_mr:
+ extends:
+ - .fdo.distribution-image@fedora
+ - .gnome-shell.fedora:35
+ stage: review
+ script:
+ - export NODE_PATH=$(npm root -g)
+ - ./.gitlab-ci/run-eslint --output-file ${LINT_MR_LOG} --format junit
+ --remote ${CI_MERGE_REQUEST_PROJECT_URL}.git
+ --branch ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}
+ only:
+ - merge_requests
+ artifacts:
+ reports:
+ junit: ${LINT_MR_LOG}
+ when: always
+
+potfile_c_check:
+ extends:
+ - .fdo.distribution-image@fedora
+ - .gnome-shell.fedora:35
+ stage: review
+ script:
+ - ./.gitlab-ci/check-potfiles.sh
+
+potfile_js_check:
+ extends:
+ - .fdo.distribution-image@fedora
+ - .gnome-shell.fedora:35
+ stage: review
+ script:
+ - js91 -m .gitlab-ci/check-potfiles.js
+
+build:
+ stage: build
+ needs: ["check_commit_log"]
+ variables:
+ GIT_SUBMODULE_STRATEGY: normal
+ before_script:
+ - .gitlab-ci/checkout-mutter.sh
+ - meson mutter mutter/build --prefix=/usr
+ - meson install -C mutter/build
+ script:
+ - meson . build -Dbuildtype=debugoptimized -Dman=false --werror
+ - meson compile -C build
+ - meson install -C build
+ artifacts:
+ expire_in: 1 day
+ paths:
+ - mutter
+ - build
+
+test:
+ stage: test
+ needs: ["build"]
+ variables:
+ GIT_SUBMODULE_STRATEGY: normal
+ XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir"
+ NO_AT_BRIDGE: "1"
+ before_script:
+ - meson install -C mutter/build
+ script:
+ - dbus-run-session -- xvfb-run meson test -C build --no-rebuild
+ artifacts:
+ expire_in: 1 day
+ paths:
+ - build/meson-logs/testlog.txt
+ reports:
+ junit: build/meson-logs/testlog.junit.xml
+ when: on_failure
+
+test-coverity:
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "schedule" && $GNOME_SHELL_SCHEDULED_JOB == "coverity"'
+ when: always
+ - when: manual
+ needs: ["build"]
+ stage: analyze
+ allow_failure: true
+ variables:
+ GIT_SUBMODULE_STRATEGY: normal
+ before_script:
+ - meson install -C mutter/build
+ script:
+ - .gitlab-ci/download-coverity-tarball.sh
+ - CC=clang meson coverity-build -Dman=false
+ - ./coverity/cov-analysis-linux64-*/bin/cov-build --fs-capture-search js --dir cov-int meson compile -C coverity-build
+ - tar czf cov-int.tar.gz cov-int
+ - curl https://scan.coverity.com/builds?project=GNOME+Shell
+ --form token=$COVERITY_TOKEN --form email=carlosg@gnome.org
+ --form file=@cov-int.tar.gz --form version="`git describe --tags`"
+ --form description="GitLab CI build"
+ cache:
+ key: coverity-tarball
+ paths:
+ - coverity
+
+flatpak:
+ stage: build
+ needs: ["check_commit_log"]
+ variables:
+ SUBPROJECT: "subprojects/extensions-app"
+ # Your manifest path
+ MANIFEST_PATH: "$SUBPROJECT/build-aux/flatpak/org.gnome.Extensions.json"
+ RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo"
+ FLATPAK_MODULE: "gnome-extensions-app"
+ APP_ID: "org.gnome.Extensions.Devel"
+ extends: .flatpak
+
+nightly:
+ extends: '.publish_nightly'
+
+dist:
+ variables:
+ XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir"
+ NO_AT_BRIDGE: "1"
+ GIT_SUBMODULE_STRATEGY: normal
+ stage: deploy
+ needs: ["build"]
+ before_script:
+ - meson install -C mutter/build
+ - mkdir -m 700 $XDG_RUNTIME_DIR
+ script:
+ - dbus-run-session xvfb-run meson dist -C build
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+ changes:
+ - "**/meson.build"
+ - meson/*
+
+
+dist-tarball:
+ extends: dist
+ artifacts:
+ expose_as: 'Get tarball here'
+ paths:
+ - build/meson-dist/$CI_PROJECT_NAME-$CI_COMMIT_TAG.tar.xz
+ rules:
+ - if: '$CI_COMMIT_TAG'
diff --git a/.gitlab-ci/check-potfiles.js b/.gitlab-ci/check-potfiles.js
new file mode 100644
index 0000000..0c8885e
--- /dev/null
+++ b/.gitlab-ci/check-potfiles.js
@@ -0,0 +1,207 @@
+const gettextFuncs = new Set([
+ '_',
+ 'N_',
+ 'C_',
+ 'NC_',
+ 'dcgettext',
+ 'dgettext',
+ 'dngettext',
+ 'dpgettext',
+ 'gettext',
+ 'ngettext',
+ 'pgettext',
+]);
+
+function dirname(file) {
+ const split = file.split('/');
+ split.pop();
+ return split.join('/');
+}
+
+const scriptDir = dirname(import.meta.url);
+const root = dirname(scriptDir);
+
+const excludedFiles = new Set();
+const foundFiles = new Set()
+
+function addExcludes(filename) {
+ const contents = os.file.readFile(filename);
+ const lines = contents.split('\n')
+ .filter(l => l && !l.startsWith('#'));
+ lines.forEach(line => excludedFiles.add(line));
+}
+
+addExcludes(`${root}/po/POTFILES.in`);
+addExcludes(`${root}/po/POTFILES.skip`);
+
+function walkAst(node, func) {
+ func(node);
+ nodesToWalk(node).forEach(n => walkAst(n, func));
+}
+
+function findGettextCalls(node) {
+ switch(node.type) {
+ case 'CallExpression':
+ if (node.callee.type === 'Identifier' &&
+ gettextFuncs.has(node.callee.name))
+ throw new Error();
+ if (node.callee.type === 'MemberExpression' &&
+ node.callee.object.type === 'Identifier' &&
+ node.callee.object.name === 'Gettext' &&
+ node.callee.property.type === 'Identifier' &&
+ gettextFuncs.has(node.callee.property.name))
+ throw new Error();
+ break;
+ }
+ return true;
+}
+
+function nodesToWalk(node) {
+ switch(node.type) {
+ case 'ArrayPattern':
+ case 'BreakStatement':
+ case 'CallSiteObject': // i.e. strings passed to template
+ case 'ContinueStatement':
+ case 'DebuggerStatement':
+ case 'EmptyStatement':
+ case 'Identifier':
+ case 'Literal':
+ case 'MetaProperty': // i.e. new.target
+ case 'Super':
+ case 'ThisExpression':
+ return [];
+ case 'ArrowFunctionExpression':
+ case 'FunctionDeclaration':
+ case 'FunctionExpression':
+ return [...node.defaults, node.body].filter(n => !!n);
+ case 'AssignmentExpression':
+ case 'BinaryExpression':
+ case 'ComprehensionBlock':
+ case 'LogicalExpression':
+ return [node.left, node.right];
+ case 'ArrayExpression':
+ case 'TemplateLiteral':
+ return node.elements.filter(n => !!n);
+ case 'BlockStatement':
+ case 'Program':
+ return node.body;
+ case 'StaticClassBlock':
+ return [node.body];
+ case 'ClassField':
+ return [node.name, node.init];
+ case 'CallExpression':
+ case 'NewExpression':
+ case 'OptionalCallExpression':
+ case 'TaggedTemplate':
+ return [node.callee, ...node.arguments];
+ case 'CatchClause':
+ return [node.body, node.guard].filter(n => !!n);
+ case 'ClassExpression':
+ case 'ClassStatement':
+ return [...node.body, node.superClass].filter(n => !!n);
+ case 'ClassMethod':
+ return [node.name, node.body];
+ case 'ComprehensionExpression':
+ case 'GeneratorExpression':
+ return [node.body, ...node.blocks, node.filter].filter(n => !!n);
+ case 'ComprehensionIf':
+ return [node.test];
+ case 'ComputedName':
+ return [node.name];
+ case 'ConditionalExpression':
+ case 'IfStatement':
+ return [node.test, node.consequent, node.alternate].filter(n => !!n);
+ case 'DoWhileStatement':
+ case 'WhileStatement':
+ return [node.body, node.test];
+ case 'ExportDeclaration':
+ return [node.declaration, node.source].filter(n => !!n);
+ case 'ImportDeclaration':
+ return [...node.specifiers, node.source];
+ case 'LetStatement':
+ return [...node.head, node.body];
+ case 'ExpressionStatement':
+ return [node.expression];
+ case 'ForInStatement':
+ case 'ForOfStatement':
+ return [node.body, node.left, node.right];
+ case 'ForStatement':
+ return [node.init, node.test, node.update, node.body].filter(n => !!n);
+ case 'LabeledStatement':
+ return [node.body];
+ case 'MemberExpression':
+ return [node.object, node.property];
+ case 'ObjectExpression':
+ case 'ObjectPattern':
+ return node.properties;
+ case 'OptionalExpression':
+ return [node.expression];
+ case 'OptionalMemberExpression':
+ return [node.object, node.property];
+ case 'Property':
+ case 'PrototypeMutation':
+ return [node.value];
+ case 'ReturnStatement':
+ case 'ThrowStatement':
+ case 'UnaryExpression':
+ case 'UpdateExpression':
+ case 'YieldExpression':
+ return node.argument ? [node.argument] : [];
+ case 'SequenceExpression':
+ return node.expressions;
+ case 'SpreadExpression':
+ return [node.expression];
+ case 'SwitchCase':
+ return [node.test, ...node.consequent].filter(n => !!n);
+ case 'SwitchStatement':
+ return [node.discriminant, ...node.cases];
+ case 'TryStatement':
+ return [node.block, node.handler, node.finalizer].filter(n => !!n);
+ case 'VariableDeclaration':
+ return node.declarations;
+ case 'VariableDeclarator':
+ return node.init ? [node.init] : [];
+ case 'WithStatement':
+ return [node.object, node.body];
+ default:
+ print(`Ignoring ${node.type}, you should probably fix this in the script`);
+ }
+}
+
+function walkDir(dir) {
+ os.file.listDir(dir).forEach(child => {
+ if (child.startsWith('.'))
+ return;
+
+ const path = os.path.join(dir, child);
+ const relativePath = path.replace(`${root}/`, '');
+ if (excludedFiles.has(relativePath))
+ return;
+
+ if (!child.endsWith('.js')) {
+ try {
+ walkDir(path);
+ } catch (e) {
+ // not a directory
+ }
+ return;
+ }
+
+ try {
+ const script = os.file.readFile(path);
+ const ast = Reflect.parse(script);
+ walkAst(ast, findGettextCalls);
+ } catch (e) {
+ foundFiles.add(path);
+ }
+ });
+}
+
+walkDir(root);
+
+if (foundFiles.size === 0)
+ quit(0);
+
+print('The following files are missing from po/POTFILES.in:')
+foundFiles.forEach(f => print(` ${f}`));
+quit(1);
diff --git a/.gitlab-ci/check-potfiles.sh b/.gitlab-ci/check-potfiles.sh
new file mode 100755
index 0000000..0969da1
--- /dev/null
+++ b/.gitlab-ci/check-potfiles.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+srcdirs="src subprojects/extensions-tool"
+uidirs="js subprojects/extensions-app"
+desktopdirs="data subprojects/extensions-app/ subprojects/extensions-tool"
+
+# find source files that contain gettext keywords
+files=$(grep -lR --include='*.c' '\(gettext\|[^I_)]_\)(' $srcdirs)
+
+# find ui files that contain translatable string
+files="$files "$(grep -lRi --include='*.ui' 'translatable="[ty1]' $uidirs)
+
+# find .desktop files
+files="$files "$(find $desktopdirs -name '*.desktop*')
+
+# filter out excluded files
+if [ -f po/POTFILES.skip ]; then
+ files=$(for f in $files; do ! grep -q ^$f po/POTFILES.skip && echo $f; done)
+fi
+
+# find those that aren't listed in POTFILES.in
+missing=$(for f in $files; do ! grep -q ^$f po/POTFILES.in && echo $f; done)
+
+if [ ${#missing} -eq 0 ]; then
+ exit 0
+fi
+
+cat >&2 <<EOT
+
+The following files are missing from po/POTFILES.po:
+
+EOT
+for f in $missing; do
+ echo " $f" >&2
+done
+echo >&2
+
+exit 1
diff --git a/.gitlab-ci/checkout-mutter.sh b/.gitlab-ci/checkout-mutter.sh
new file mode 100755
index 0000000..76375fd
--- /dev/null
+++ b/.gitlab-ci/checkout-mutter.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/bash
+
+fetch() {
+ local remote=$1
+ local ref=$2
+
+ git fetch --quiet --depth=1 $remote $ref 2>/dev/null
+}
+
+mutter_target=
+
+echo -n Cloning into mutter ...
+if git clone --quiet --depth=1 https://gitlab.gnome.org/GNOME/mutter.git; then
+ echo \ done
+else
+ echo \ failed
+ exit 1
+fi
+
+cd mutter
+
+if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then
+ merge_request_remote=${CI_MERGE_REQUEST_SOURCE_PROJECT_URL//gnome-shell/mutter}
+ merge_request_branch=$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
+
+ echo -n Looking for $merge_request_branch on remote ...
+ if fetch $merge_request_remote $merge_request_branch; then
+ echo \ found
+ mutter_target=FETCH_HEAD
+ else
+ echo \ not found
+
+ echo -n Looking for $CI_MERGE_REQUEST_TARGET_BRANCH_NAME instead ...
+ if fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME; then
+ echo \ found
+ mutter_target=FETCH_HEAD
+ else
+ echo \ not found
+ fi
+ fi
+fi
+
+if [ -z "$mutter_target" ]; then
+ ref_remote=${CI_PROJECT_URL//gnome-shell/mutter}
+ echo -n Looking for $CI_COMMIT_REF_NAME on remote ...
+ if fetch $ref_remote $CI_COMMIT_REF_NAME; then
+ echo \ found
+ mutter_target=FETCH_HEAD
+ else
+ echo \ not found
+ fi
+fi
+
+fallback_branch=${CI_COMMIT_TAG:+gnome-}${CI_COMMIT_TAG%%.*}
+if [ -z "$mutter_target" -a "$fallback_branch" ]; then
+ echo -n Looking for $fallback_branch instead ...
+ if fetch origin $fallback_branch; then
+ echo \ found
+ mutter_target=FETCH_HEAD
+ else
+ echo \ not found
+ fi
+fi
+
+if [ -z "$mutter_target" ]; then
+ mutter_target=HEAD
+ echo Using $mutter_target instead
+fi
+
+git checkout -q $mutter_target
diff --git a/.gitlab-ci/commit-rules.yml b/.gitlab-ci/commit-rules.yml
new file mode 100644
index 0000000..5828f8a
--- /dev/null
+++ b/.gitlab-ci/commit-rules.yml
@@ -0,0 +1,16 @@
+patterns:
+ deny:
+ - regex: '^$CI_MERGE_REQUEST_PROJECT_URL/(-/)?merge_requests/$CI_MERGE_REQUEST_IID$'
+ message: Commit message must not contain a link to its own merge request
+ - regex: '^(st-|St)'
+ message: Commit message subject should not be prefixed with 'st-' or 'St', use 'st/' instead
+ where: subject
+ - regex: '^[^:]+: [a-z]'
+ message: "Commit message subject should be properly Capitalized. E.g. 'window: Marginalize extradicity'"
+ where: subject
+ - regex: '^\S*\.(js|c|h):'
+ message: Commit message subject prefix should not include .c, .h etc.
+ where: subject
+ - regex: '([^.]\.|[:,;])\s*$'
+ message: Commit message subject should not end with punctuation
+ where: subject
diff --git a/.gitlab-ci/download-coverity-tarball.sh b/.gitlab-ci/download-coverity-tarball.sh
new file mode 100755
index 0000000..e2afc5d
--- /dev/null
+++ b/.gitlab-ci/download-coverity-tarball.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/bash
+
+# We need a coverity token to fetch the tarball
+if [ -x $COVERITY_TOKEN ]
+then
+ echo "No coverity token. Run this job from a protected branch."
+ exit -1
+fi
+
+mkdir -p coverity
+
+# Download and check MD5 first
+curl https://scan.coverity.com/download/linux64 \
+ --data "token=$COVERITY_TOKEN&project=GNOME+Shell&md5=1" \
+ --output /tmp/coverity_tool.md5
+
+diff /tmp/coverity_tool.md5 coverity/coverity_tool.md5 >/dev/null 2>&1
+
+if [ $? -eq 0 -a -d coverity/cov-analysis* ]
+then
+ echo "Coverity tarball is up-to-date"
+ exit 0
+fi
+
+# Download and extract coverity tarball
+curl https://scan.coverity.com/download/linux64 \
+ --data "token=$COVERITY_TOKEN&project=GNOME+Shell" \
+ --output /tmp/coverity_tool.tgz
+
+rm -rf ./coverity/cov-analysis*
+
+tar zxf /tmp/coverity_tool.tgz -C coverity/
+if [ $? -eq 0 ]
+then
+ mv /tmp/coverity_tool.md5 coverity/
+fi
+
+rm /tmp/coverity_tool.tgz
diff --git a/.gitlab-ci/install-meson-project.sh b/.gitlab-ci/install-meson-project.sh
new file mode 100755
index 0000000..8ecf8a3
--- /dev/null
+++ b/.gitlab-ci/install-meson-project.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+set -e
+
+usage() {
+ cat <<-EOF
+ Usage: $(basename $0) [OPTION…] REPO_URL COMMIT
+
+ Check out and install a meson project
+
+ Options:
+ -Dkey=val Option to pass on to meson
+ --subdir Build subdirectory instead of whole project
+ --prepare Script to run before build
+
+ -h, --help Display this help
+
+ EOF
+}
+
+TEMP=$(getopt \
+ --name=$(basename $0) \
+ --options='D:h' \
+ --longoptions='subdir:' \
+ --longoptions='prepare:' \
+ --longoptions='help' \
+ -- "$@")
+
+eval set -- "$TEMP"
+unset TEMP
+
+MESON_OPTIONS=()
+SUBDIR=.
+PREPARE=:
+
+while true; do
+ case "$1" in
+ -D)
+ MESON_OPTIONS+=( -D$2 )
+ shift 2
+ ;;
+
+ --subdir)
+ SUBDIR=$2
+ shift 2
+ ;;
+
+ --prepare)
+ PREPARE=$2
+ shift 2
+ ;;
+
+ -h|--help)
+ usage
+ exit 0
+ ;;
+
+ --)
+ shift
+ break
+ ;;
+ esac
+done
+
+if [[ $# -lt 2 ]]; then
+ usage
+ exit 1
+fi
+
+REPO_URL="$1"
+COMMIT="$2"
+
+CHECKOUT_DIR=$(mktemp --directory)
+trap "rm -rf $CHECKOUT_DIR" EXIT
+
+git clone --depth 1 "$REPO_URL" -b "$COMMIT" "$CHECKOUT_DIR"
+
+pushd "$CHECKOUT_DIR/$SUBDIR"
+sh -c "$PREPARE"
+meson setup --prefix=/usr _build "${MESON_OPTIONS[@]}"
+meson install -C _build
+popd
diff --git a/.gitlab-ci/run-eslint b/.gitlab-ci/run-eslint
new file mode 100755
index 0000000..2a8f60d
--- /dev/null
+++ b/.gitlab-ci/run-eslint
@@ -0,0 +1,128 @@
+#!/usr/bin/env node
+
+const { ESLint } = require('eslint');
+const fs = require('fs');
+const path = require('path');
+const { spawn } = require('child_process');
+
+function createConfig(config) {
+ const options = {
+ cache: true,
+ cacheLocation: `.eslintcache-${config}`,
+ };
+
+ if (config === 'legacy')
+ options.overrideConfigFile='lint/eslintrc-legacy.yml';
+
+ return new ESLint(options);
+}
+
+function git(...args) {
+ const git = spawn('git', args, { stdio: ['ignore', null, 'ignore'] });
+ git.stdout.setEncoding('utf8');
+
+ return new Promise(resolve => {
+ let out = '';
+ git.stdout.on('data', chunk => out += chunk);
+ git.stdout.on('end', () => resolve(out.trim()));
+ });
+}
+
+function createCommon(report1, report2, ignoreColumn=false) {
+ return report1.map(result => {
+ const { filePath, messages } = result;
+ const match =
+ report2.find(r => r.filePath === filePath) || { messages: [] };
+
+ const filteredMessages = messages.filter(
+ msg => match.messages.some(
+ m => m.line === msg.line && (ignoreColumn || m.column === msg.column)));
+
+ const [errorCount, warningCount] = filteredMessages.reduce(
+ ([e, w], msg) => {
+ return [
+ e + Number(msg.severity === 2),
+ w + Number(msg.severity === 1)];
+ }, [0, 0]);
+
+ return {
+ filePath,
+ messages: filteredMessages,
+ errorCount,
+ warningCount,
+ };
+ });
+}
+
+async function getMergeRequestChanges(remote, branch) {
+ await git('fetch', remote, branch);
+ const branchPoint = await git('merge-base', 'HEAD', 'FETCH_HEAD');
+ const diff = await git('diff', '-U0', `${branchPoint}...HEAD`);
+
+ const report = [];
+ let messages = null;
+ for (const line of diff.split('\n')) {
+ if (line.startsWith('+++ b/')) {
+ const filePath = path.resolve(line.substring(6));
+ messages = filePath.endsWith('.js') ? [] : null;
+ if (messages)
+ report.push({ filePath, messages });
+ } else if (messages && line.startsWith('@@ ')) {
+ [, , changes] = line.split(' ');
+ [start, count] = `${changes},1`.split(',').map(i => parseInt(i));
+ for (let i = start; i < start + count; i++)
+ messages.push({ line: i });
+ }
+ }
+
+ return report;
+}
+
+function getOption(...names) {
+ const optIndex =
+ process.argv.findIndex(arg => names.includes(arg)) + 1;
+
+ if (optIndex === 0)
+ return undefined;
+
+ return process.argv[optIndex];
+}
+
+(async function main() {
+ const outputOption = getOption('--output-file', '-o');
+ const outputPath = outputOption ? path.resolve(outputOption) : null;
+
+ const sourceDir = path.dirname(process.argv[1]);
+ process.chdir(path.resolve(sourceDir, '..'));
+
+ const remote = getOption('--remote') || 'origin';
+ const branch = getOption('--branch', '-b');
+
+ const sources = ['js', 'subprojects/extensions-app/js'];
+ const regular = createConfig('regular');
+
+ const ops = [];
+ ops.push(regular.lintFiles(sources));
+ if (branch)
+ ops.push(getMergeRequestChanges(remote, branch));
+ else
+ ops.push(createConfig('legacy').lintFiles(sources));
+
+ const results = await Promise.all(ops);
+ const commonResults = createCommon(...results, branch !== undefined);
+
+ const formatter = await regular.loadFormatter(getOption('--format', '-f'));
+ const resultText = formatter.format(commonResults);
+
+ if (outputPath) {
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
+ fs.writeFileSync(outputPath, resultText);
+ } else {
+ console.log(resultText);
+ }
+
+ process.exitCode = commonResults.some(r => r.errorCount > 0) ? 1 : 0;
+})().catch((error) => {
+ process.exitCode = 1;
+ console.error(error);
+});
diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md
new file mode 100644
index 0000000..2043fe4
--- /dev/null
+++ b/.gitlab/issue_templates/Bug.md
@@ -0,0 +1,55 @@
+<!--
+Please read https://wiki.gnome.org/Community/GettingInTouch/BugReportingGuidelines
+first to ensure that you create a clear and specific issue.
+-->
+
+### Affected version
+
+<!--
+Provide at least the following information:
+* Your OS and version
+* Affected GNOME Shell version (see https://wiki.gnome.org/Schedule for currently supported versions)
+* Does this issue appear in XOrg and/or Wayland
+-->
+
+### Bug summary
+
+<!--
+Provide a short summary of the bug you encountered.
+-->
+
+### Steps to reproduce
+
+<!--
+1. Step one
+2. Step two
+3. ...
+-->
+
+### What happened
+
+<!--
+What did GNOME Shell do that was unexpected?
+-->
+
+### What did you expect to happen
+
+<!--
+What did you expect GNOME Shell to do?
+-->
+
+### Relevant logs, screenshots, screencasts etc.
+
+<!--
+If you have further information, such as technical documentation, logs,
+screenshots or screencasts related, please provide them here.
+
+If the bug is a crash, please obtain a stack trace with installed debug
+symbols (at least for GNOME Shell and Mutter) and attach it to
+this issue following the instructions on
+https://wiki.gnome.org/Community/GettingInTouch/Bugzilla/GettingTraces.
+-->
+
+
+<!-- Do not remove the following line. -->
+/label ~"1. Bug"
diff --git a/.gitlab/issue_templates/Feature.md b/.gitlab/issue_templates/Feature.md
new file mode 100644
index 0000000..8443b5a
--- /dev/null
+++ b/.gitlab/issue_templates/Feature.md
@@ -0,0 +1,30 @@
+<!--
+Please read https://wiki.gnome.org/Community/GettingInTouch/BugReportingGuidelines
+first to ensure that you create a clear and specific issue.
+-->
+
+### Feature summary
+
+<!--
+Describe what you would like to be able to do with GNOME Shell
+that you currently cannot do.
+-->
+
+### How would you like it to work
+
+<!--
+If you can think of a way GNOME Shell might be able to do this,
+let us know here.
+-->
+
+### Relevant links, screenshots, screencasts etc.
+
+<!--
+If you have further information, such as technical documentation,
+code, mockups or a similar feature in another desktop environments,
+please provide them here.
+-->
+
+
+<!-- Do not remove the following line. -->
+/label ~"1. Feature"
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..bb57dfc
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "subprojects/gvc"]
+ path = subprojects/gvc
+ url = ../../GNOME/libgnome-volume-control.git
diff --git a/.jscheckignore b/.jscheckignore
new file mode 100644
index 0000000..7313eb4
--- /dev/null
+++ b/.jscheckignore
@@ -0,0 +1 @@
+js/ui/init.js \ No newline at end of file
diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope
new file mode 100644
index 0000000..bbb8e68
--- /dev/null
+++ b/.settings/.jsdtscope
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
+ <attributes>
+ <attribute name="hide" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
+ <classpathentry kind="output" path=""/>
+</classpath>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/HACKING.md b/HACKING.md
new file mode 100644
index 0000000..bdcb9ba
--- /dev/null
+++ b/HACKING.md
@@ -0,0 +1,341 @@
+# Coding guide
+
+Our goal is to have all JavaScript code in GNOME follow a consistent style. In
+a dynamic language like JavaScript, it is essential to be rigorous about style
+(and unit tests), or you rapidly end up with a spaghetti-code mess.
+
+## A quick note
+
+Life isn't fun if you can't break the rules. If a rule seems unnecessarily
+restrictive while you're coding, ignore it, and let the patch reviewer decide
+what to do.
+
+## Indentation, braces and whitespace
+
+* Use four-space indents.
+* Braces are on the same line as their associated statements.
+* You should only omit braces if *both* sides of the statement are on one line.
+* One space after the `function` keyword.
+* No space between the function name in a declaration or a call.
+* One space before the parens in the `if` statements, or `while`, or `for` loops.
+
+```javascript
+ function foo(a, b) {
+ let bar;
+
+ if (a > b)
+ bar = do_thing(a);
+ else
+ bar = do_thing(b);
+
+ if (var == 5) {
+ for (let i = 0; i < 10; i++)
+ print(i);
+ } else {
+ print(20);
+ }
+ }
+```
+
+## Semicolons
+
+JavaScript allows omitting semicolons at the end of lines, but don't. Always
+end statements with a semicolon.
+
+## js2-mode
+
+If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
+while. emacs now has a built-in JavaScript mode, js-mode, based on
+espresso-mode. It is the de facto emacs mode for JavaScript.
+
+## File naming and creation
+
+For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
+
+We only use C where gjs/gobject-introspection is not available for the task, or
+where C would be cleaner. To work around limitations in
+gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
+
+Like many other GNOME projects, we prefix our C source filenames with the
+library name followed by a dash, e.g. `shell-app-system.c`. Create a
+`-private.h` header when you want to share code internally in the
+library. These headers are not installed, distributed or introspected.
+
+## Imports
+
+Use UpperCamelCase when importing modules to distinguish them from ordinary
+variables, e.g.
+```javascript
+ const GLib = imports.gi.GLib;
+```
+Imports should be categorized into one of two places. The top-most import block
+should contain only "environment imports". These are either modules from
+gobject-introspection or modules added by gjs itself.
+
+The second block of imports should contain only "application imports". These
+are the JS code that is in the gnome-shell codebase,
+e.g. `imports.ui.popupMenu`.
+
+Each import block should be sorted alphabetically. Don't import modules you
+don't use.
+```javascript
+ const { GLib, Gio, St } = imports.gi;
+
+ const Main = imports.ui.main;
+ const Params = imports.misc.params;
+ const Util = imports.misc.util;
+```
+The alphabetical ordering should be done independently of the location of the
+location. Never reference `imports` in actual code.
+
+## Constants
+
+We use CONSTANTS_CASE to define constants. All constants should be directly
+under the imports:
+```javascript
+ const MY_DBUS_INTERFACE = 'org.my.Interface';
+```
+
+## Variable declaration
+
+Always use either `const` or `let` when defining a variable.
+```javascript
+ // Iterating over an array
+ for (let i = 0; i < arr.length; ++i)
+ let item = arr[i];
+
+ // Iterating over an object's properties
+ for (let prop in someobj) {
+ ...
+ }
+```
+
+If you use "var" then the variable is added to function scope, not block scope.
+See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29)
+
+## Classes
+
+There are many approaches to classes in JavaScript. We use standard ES6 classes
+whenever possible, that is when not inheriting from GObjects.
+```javascript
+ var IconLabelMenuItem = class extends PopupMenu.PopupMenuBaseItem {
+ constructor(icon, label) {
+ super({ reactive: false });
+ this.actor.add_child(icon);
+ this.actor.add_child(label);
+ }
+
+ open() {
+ log("menu opened!");
+ }
+ };
+```
+
+For GObject inheritance, we use the GObject.registerClass() function provided
+by gjs.
+```javascript
+ var MyActor = GObject.registerClass(
+ class MyActor extends Clutter.Actor {
+ _init(params) {
+ super._init(params);
+
+ this.name = 'MyCustomActor';
+ }
+ });
+```
+
+## GObject Introspection
+
+GObject Introspection is a powerful feature that allows us to have native
+bindings for almost any library built around GObject. If a library requires
+you to inherit from a type to use it, you can do so:
+```javascript
+ var MyClutterActor = GObject.registerClass(
+ class MyClutterActor extends Clutter.Actor {
+
+ vfunc_get_preferred_width(forHeight) {
+ return [100, 100];
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ return [100, 100];
+ }
+
+ vfunc_paint(paintContext) {
+ let framebuffer = paintContext.get_framebuffer();
+ let coglContext = framebuffer.get_context();
+ let alloc = this.get_allocation_box();
+
+ let pipeline = new Cogl.Pipeline(coglContext);
+ pipeline.set_color4ub(255, 0, 0, 255);
+
+ framebuffer.draw_rectangle(pipeline,
+ alloc.x1, alloc.y1,
+ alloc.x2, alloc.y2);
+ }
+ });
+```
+
+## Translatable strings, `environment.js`
+
+We use gettext to translate the GNOME Shell into all the languages that GNOME
+supports. The `gettext` function is aliased globally as `_`, you do not need to
+explicitly import it. This is done through some magic in the
+[environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
+file. If you can't find a method that's used, it's probably either in gjs itself
+or installed on the global object from the Environment.
+
+Use 'single quotes' for programming strings that should not be translated
+and "double quotes" for strings that the user may see. This allows us to
+quickly find untranslated or mistranslated strings by grepping through the
+sources for double quotes without a gettext call around them.
+
+## `actor` (deprecated) and `_delegate`
+
+gjs allows us to set so-called "expando properties" on introspected objects,
+allowing us to treat them like any other. Because the Shell was built before
+you could inherit from GTypes natively in JS, in some cases we have a wrapper
+class that has a property called `actor` (now deprecated). We call this
+wrapper class the "delegate".
+
+We sometimes use expando properties to set a property called `_delegate` on
+the actor itself:
+```javascript
+ var MyActor = GObject.registerClass(
+ class MyActor extends Clutter.Actor {
+ _init(params) {
+ super._init(params);
+ this._delegate = this;
+ }
+ });
+```
+
+Or using the deprecated `actor`:
+```javascript
+ var MyClass = class {
+ constructor() {
+ this.actor = new St.Button({ text: "This is a button" });
+ this.actor._delegate = this;
+
+ this.actor.connect('clicked', this._onClicked.bind(this));
+ }
+
+ _onClicked(actor) {
+ actor.set_label("You clicked the button!");
+ }
+ };
+```
+
+The 'delegate' property is important for anything which trying to get the
+delegate object from an associated actor. For instance, the drag and drop
+system calls the `handleDragOver` function on the delegate of a "drop target"
+when the user drags an item over it. If you do not set the `_delegate`
+property, your actor will not be able to be dropped onto.
+In case the class is an actor itself, the `_delegate` can be just set to `this`.
+
+## Functional style
+
+JavaScript Array objects offer a lot of common functional programming
+capabilities such as forEach, map, filter and so on. You can use these when
+they make sense, but please don't have a spaghetti mess of function programming
+messed in a procedural style. Use your best judgment.
+
+## Closures
+
+`this` will not be captured in a closure, it is relative to how the closure is
+invoked, not to the value of this where the closure is created, because "this"
+is a keyword with a value passed in at function invocation time, it is not a
+variable that can be captured in closures.
+
+All closures should be wrapped with Function.prototype.bind or use arrow
+notation.
+```javascript
+ let closure1 = () => this._fnorbate();
+ let closure2 = this._fnorbate.bind(this);
+```
+
+A more realistic example would be connecting to a signal on a method of a
+prototype:
+```javascript
+ const FnorbLib = imports.fborbLib;
+
+ var MyClass = class {
+ _init() {
+ let fnorb = new FnorbLib.Fnorb();
+ fnorb.connect('frobate', this._onFnorbFrobate.bind(this));
+ }
+
+ _onFnorbFrobate(fnorb) {
+ this._updateFnorb();
+ }
+ };
+```
+
+## Object literal syntax
+
+In JavaScript, these are equivalent:
+```javascript
+ foo = { 'bar': 42 };
+ foo = { bar: 42 };
+```
+
+and so are these:
+```javascript
+ var b = foo['bar'];
+ var b = foo.bar;
+```
+
+If your usage of an object is like an object, then you're defining "member
+variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
+42 }` `foo.bar`.
+
+If your usage of an object is like a hash table (and thus conceptually the keys
+can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
+}`, `foo['bar']`.
+
+## Animations
+
+Most objects that are animated are actors, and most properties used in animations
+are animatable, which means they can use implicit animations:
+
+```javascript
+ moveActor(actor, x, y) {
+ actor.ease({
+ x,
+ y,
+ duration: 500, // ms
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD
+ });
+ }
+```
+
+The above is a convenience wrapper around the actual Clutter API, and should generally
+be preferred over the more verbose:
+
+```javascript
+ moveActor(actor, x, y) {
+ actor.save_easing_state();
+
+ actor.set_easing_duration(500);
+ actor.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
+ actor.set({
+ x,
+ y
+ });
+
+ actor.restore_easing_state();
+ }
+```
+
+There is a similar convenience API around Clutter.PropertyTransition to animate
+actor (or actor meta) properties that cannot use implicit animations:
+
+```javascript
+ desaturateActor(actor, desaturate) {
+ let factor = desaturate ? 1.0 : 0.0;
+ actor.ease_property('@effects.desaturate.factor', factor, {
+ duration: 500, // ms
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD
+ });
+ }
+```
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..a38286f
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,5659 @@
+43.9
+====
+* Fix exposing window previews on lock screen via keyboard shortcuts
+ [Florian; !2944]
+* Improve handling of latched vs. locked modes in OSK [Carlos; !2945]
+* Reverse slider direction in RTL locales [Khalid; !2943]
+* Misc. bug fixes and cleanups [xiaofan; !2947]
+
+Contributors:
+ Carlos Garnacho, Florian Müllner, Khalid Abu Shawarib, xiaofan
+
+Translators:
+ Daniel Rusek [cs]
+
+43.8
+====
+* Fix accessibility of quick settings sliders [Lukáš; !2762]
+* Allow notification dismissal with backspace [Chris; !2435]
+* Misc. bug fixes and cleanups [Florian, Takao, Carlos, Brendan, Daniel, Jonas;
+ !2814, !2842, !2849, !2668, !2666, !2876, !2729, !2828, !2904]
+
+Contributors:
+ Takao Fujiwara, Carlos Garnacho, Chris Heywood, Florian Müllner,
+ Lukáš Tyrychtr, Daniel van Vugt, Brendan William, Jonas Ådahl
+
+Translators:
+ Danial Behzadi [fa]
+
+43.7
+====
+* Fix cursor offset when using magnifier [Андрей; !2780]
+* Fix missing workspace borders after wallpaper changes [Florian; !2801]
+* Misc. bug fixes and cleanups [Florian; !2796]
+
+Contributors:
+ Florian Müllner, Андрей Гриценко
+
+43.6
+====
+* Fix stuck authentication dialog in remote sessions [Joan; !2761]
+* Fix IM popup getting stuck on engine changes [Daniel; !2774]
+* Fixed crash [Carlos; !2756]
+* Misc. bug fixes and cleanups [Zacharie; !2770]
+
+Contributors:
+ Daniel Drake, Zacharie DUBRULLE, Carlos Garnacho, Joan Torres
+
+Translators:
+ Leônidas Araújo [pt_BR]
+
+43.5
+====
+* Fix window screenshots with pointer [Ivan; !2710, !2702]
+* Fix notifications getting stuck indefinitely [msizanoen1; !2736]
+* Fix keynav of menu-less buttons [Florian; !2734]
+* Fix corner cases when matching apps on StartupWmClass [Marco; !2721]
+* Fix occasional misalignment of search results [Sebastian; !2744]
+* Fix regression in content-type sniffing on autorun [Balló; !2745]
+* Misc. bug fixes and cleanups [Will, Daniel, Florian, Jonas, Marco, Sebastian;
+ !2689, !2693, !2696, !2699, !2730, !2739, !2738, !2712]
+
+Contributors:
+ Jonas Dreßler, Balló György, Sebastian Keller, Ivan Molodetskikh, msizanoen1,
+ Florian Müllner, Will Thompson, Marco Trevisan (Treviño), Daniel van Vugt
+
+Translators:
+ Nart Tlisha [ab], Boyuan Yang [zh_CN]
+
+43.4
+====
+* Plugged leak [Sebastian; !2652]
+* Misc. bug fixes and cleanups [Jonas, Sebastian; !2318, !2667]
+
+Contributors:
+ Jonas Dreßler, Sebastian Keller
+
+Translators:
+ Anders Jonsson [sv], Sabri Ünal [tr], Jiri Grönroos [fi],
+ Ekaterine Papava [ka], Hugo Carvalho [pt], Yuri Chornoivan [uk],
+ Kukuh Syafaat [id], Martin [sl], Tim Sabsch [de], Piotr Drąg [pl],
+ Aurimas Černius [lt], Мирослав Николић [sr], Balázs Úr [hu], Alan Paris [fr]
+
+43.3
+====
+* Ensure quick settings toggles align with the pixel grid [Sebastian; !2577]
+* Lower OOM score of gnome-shell process [Jian-Hong; !2582]
+* Fix incomplete device list in volume menus [Florian; !2616]
+* Fixed crash [Florian; !2622]
+* Plugged leak [Sebastian; !2637]
+* Misc. bug fixes and cleanups [Jonas Å., Florian, Jonas D., Alessandro, Heiko,
+ Takao, Carlos, Sebastian; !2578, !2571, !2594, !2606, !2604, !2399, !2613,
+ !2496, !2625, !2552, !2634, !2514]
+
+Contributors:
+ Alessandro Bono, Jonas Dreßler, Takao Fujiwara, Carlos Garnacho, Balló György,
+ Heiko Hösch, Sebastian Keller, Florian Müllner, Jian-Hong Pan, Olga Smirnova,
+ Jonas Ådahl
+
+Translators:
+ Olga Smirnova [ie], Ekaterine Papava [ka], Aleksandr Melman [ru],
+ Nart Tlisha [ab], Bruce Cowan [en_GB], Hugo Carvalho [pt]
+
+43.2
+====
+* Fix screencast UI being erroneously disabled [Florian; !2533]
+* Properly apply saved shortcuts inhibition permissions [Florian; !2548]
+* Fix IM candidate popup getting closed when switching pages [Carlos; !2497]
+* Fix unresponsive screen area when quick settings are opened [Carlos; !2535]
+* Ensure completions are disabled when hiding on-screen keyboard [Carlos; !2543]
+* Fix missing name in wired network quick settings button [Florian; !2529]
+* Don't let wayland popovers block overview [Florian; !2549]
+* Fix repeated entries in volume device menu [Florian; !2558]
+* Fix input source switching with focus on password entry [Carlos; !2534]
+* Misc. bug fixes and cleanups [Alessandro, Florian, Jonas; !2536, !2545, !2546,
+ !2547, !2532, !1349]
+
+Contributors:
+ Alessandro Bono, Carlos Garnacho, Florian Müllner, Jonas Ådahl
+
+Translators:
+ Aleksandr Melman [ru], Dušan Kazik [sk], Vasil Pupkin [be],
+ Sveinn í Felli [is], Nart Tlisha [ab]
+
+43.1
+====
+* Fix default keyboard layout during initial setup [Carlos; !2487, !2495]
+* Show Bluetooth switch in more cases [Bastien; !2488]
+* Include origin in sound device switcher [Florian; !2502]
+* Fix remembering inhibit-shortcut permission for multiple apps [Florian; !2504]
+* Forward TERMINAL input purpose to ibus [Jonas; !2511]
+* Fix style glitches [Sam; !2513, !2510]
+* Fix tracking of newly installed apps [Florian; !2480]
+* Fix space key getting stuck in on-screen keyboard [Carlos; !2508]
+* Fix showing passwords as on-screen keyboard suggestions [Carlos; !2512]
+* Fix network portal regression [Marco; !2524]
+* Fix possible freeze during initialization [Florian; !2519]
+* Misc. bug fixes and cleanups [Andy, Jonas, Alessandro, Florian, Sebastian;
+ !2470, !2476, !2471, !2483, !2518, !2503]
+
+Contributors:
+ Marco Trevisan (Treviño), Alessandro Bono, Carlos Garnacho, Sam Hewitt,
+ Andy Holmes, Sebastian Keller, Florian Müllner, Bastien Nocera, Jonas Ådahl
+
+Translators:
+ Baurzhan Muftakhidinov [kk], Марко Костић [sr], Yosef Or Boczko [he],
+ Guillaume Bernard [fr], Hugo Carvalho [pt], Kukuh Syafaat [id],
+ Pawan Chitrakar [ne], Aleksandr Melman [ru], Aurimas Černius [lt],
+ Anders Jonsson [sv], Jordi Mas [ca], Danial Behzadi [fa], Dušan Kazik [sk],
+ Fabio Tomat [fur], Olexander [uk], Nart Tlisha [ab], Cheng-Chia Tseng [zh_TW],
+ Alexander Shopov [bg], Zurab Kargareteli [ka], A S Alam [pa],
+ Kosmas Martakidis [el], Nathan Follens [nl]
+
+43.0
+====
+* Fix rebooting into boot loader menu [Florian; !2472]
+* Improve entry legibility in GNOME Classic [Jakub; !2477]
+* Fixed crash [Jonas; !2478]
+* Misc. bug fixes and cleanups [sabriunal, Florian; !2467, !2484, !2485]
+
+Contributors:
+ Florian Müllner, Jakub Steiner, sabriunal, Jonas Ådahl
+
+Translators:
+ Jürgen Benvenuti [de], Quentin PAGÈS [oc], Aurimas Černius [lt],
+ Anders Jonsson [sv], Pawan Chitrakar [ne], Jordi Mas [ca],
+ Daniel Mustieles [es], Alexander Shopov [bg], Asier Sarasua Garmendia [eu],
+ Aleksandr Melman [ru], Alexandre Franke [fr], Goran Vidović [hr],
+ Emin Tufan Çetin [tr], Balázs Úr [hu], Marek Černocký [cs],
+ Yuri Chornoivan [uk], Jiri Grönroos [fi], Yosef Or Boczko [he],
+ Piotr Drąg [pl], Luming Zh [zh_CN], Fran Dieguez [gl],
+ Enrico Nicoletto [pt_BR], Alan Mortensen [da], Matej Urbančič [sl]
+
+43.rc
+=====
+* Tweak quick settings appearance [Sam; !2427, !2441, !2463]
+* Use NM connectivity check URI for portal helper [Clerie; !2228]
+* Only show "Night Light" toggle when supported [Florian; !2425]
+* Fix accessibility of quick settings sliders [Florian; !2431]
+* Use more unambiguous icons in on-screen keyboard [Carlos; !2433]
+* Add screenshot item to quick settings [Florian; !2439]
+* Fix entering extended characters from on-screen keyboard [Carlos; !2445]
+* Prevent SignalTracker from leaking objects [Sebastian; !2450, !2466]
+* Fix turning off VPN in quick settings [Florian; !2426]
+* Fix initial selection in switch-monitor popup on desktop [Yao; !2451]
+* Improve handling of recurring events [Milan; !2328]
+* Indicate transitional states in bluetooth quick toggle [Florian; !2444]
+* Work around pipewire regression for screencasts [Sebastian; !2436]
+* Plugged memory leak [Florian; !2461]
+* Misc. bug fixes and cleanups [Daniel, Jonas Å., Florian, Georges, Carlos;
+ !2429, !2434, !2430, !2442, !2455, !2462, !2456, !2464, !2465, !2288]
+
+Contributors:
+ Yao Wei (魏銘廷), Milan Crha, Carlos Garnacho, Sam Hewitt, Sebastian Keller,
+ Florian Müllner, Dmitrii Naidolinskii, Georges Basile Stavracas Neto,
+ Clerie Riese, Daniel van Vugt, msizanoen1, Jonas Ådahl
+
+Translators:
+ Danial Behzadi [fa], Kukuh Syafaat [id], Марко Костић [sr],
+ Zurab Kargareteli [ka], Hugo Carvalho [pt], Goran Vidović [hr],
+ Aleksandr Melman [ru], Jordi Mas [ca], Piotr Drąg [pl],
+ Asier Sarasua Garmendia [eu], Luming Zh [zh_CN], Yosef Or Boczko [he],
+ Fran Dieguez [gl], Emin Tufan Çetin [tr], Daniel Mustieles [es],
+ Nart Tlisha [ab], Marek Černocký [cs], Leônidas Araújo [pt_BR],
+ Matej Urbančič [sl], Balázs Úr [hu], Nathan Follens [nl],
+ Baurzhan Muftakhidinov [kk], Rūdolfs Mazurs [lv], Alan Mortensen [da],
+ Changwoo Ryu [ko]
+
+43.beta
+=======
+* Allow extension updates with only Extension Manager installed [Matthew; !2358]
+* Use new About window in Extensions app [Christopher; !2373]
+* Stop auto-dismissing notifications on focus changes [Florian; !2332]
+* Tweak access portal dialog [Georges; !2377]
+* Add device switcher to volume sliders [Florian; !2380]
+* Replace combined system menu with quick settings [Florian; !2385, !2389,
+ !2391, !2392, !2393, !2406, !2407, !2408]
+* Use the same format in screencast and screenshot file names [Emmanuele; !2300]
+* Only load extensions that support the current session mode [Alessandro; !2378]
+* Improve overview animation performance [Ivan; !2395, !2394]
+* Port to gcr4 [Milan; !2386]
+* Fix logging in with realmd [Alessandro; !2404]
+* Optimize workspace background shadow [Daniel; !1915]
+* Improve app grid navigation [Georges; !2335]
+* Improve on-screen keyboard
+ - delete multiple characters and words when holding backspace
+ - provide word suggestions when using plan keyboard layouts
+ - use extended layout when TERMINAL hint is set
+ [Carlos; !2278]
+* Fix remembering set up bluetooth devices [Florian; !2409]
+* Misc. bug fixes and cleanups [Florian, Carlos, Sam, Sebastian, Alessandro,
+ Robert; !2374, !2369, !2350, !2381, !2375, !2384, !2388, !2371, !2344, !2370,
+ !2396, !2397, !2403, !2402, !2326, !2405, !2401, !2398, !2414, !2412, !2411,
+ !2415, !2416, !2419, !2413]
+
+Contributors:
+ Robert Ancell, Emmanuele Bassi, Alessandro Bono, Milan Crha,
+ Christopher Davis, Carlos Garnacho, Sam Hewitt, Matthew Jakeman,
+ Sebastian Keller, Andre Klapper, Ivan Molodetskikh, Florian Müllner,
+ Georges Basile Stavracas Neto, Nart Tlisha, Daniel van Vugt
+
+Translators:
+ Kukuh Syafaat [id], Emin Tufan Çetin [tr], Jordi Mas [ca],
+ Zurab Kargareteli [ka], Nart Tlisha [ab], Asier Sarasua Garmendia [eu],
+ Fran Dieguez [gl], Aleksandr Melman [ru], Hugo Carvalho [pt],
+ Yuri Chornoivan [uk], Alexander Shopov [bg], Yosef Or Boczko [he],
+ Quentin PAGÈS [oc], Danial Behzadi [fa], Daniel Mustieles [es],
+ Luming Zh [zh_CN], Jiri Grönroos [fi]
+
+43.alpha
+========
+* Add convenience property for icon-only buttons [Florian; !2254]
+* Align space-padded times in world clocks [Maksym; !2294, !2340]
+* Fix top bar menus on lock screen [Florian; !2298]
+* Show wireguard connects in VPN list [Christian; !1995]
+* Fix on-screen keyboard gestures [Carlos; !2304]
+* Fix focus tracking in magnifier on wayland [Sebastian; !2301]
+* Fix OSD colors with light stylesheet [Jakub; !2315]
+* Only close messages via delete key if they can be closed [PhilProg; !2323]
+* Fix screenshots when XDG directories are disabled [Sam U.; !2327]
+* Do not create systemd scope for D-Bus activated apps [msizanoen1; !2305]
+* Improve high-contrast stylesheet [Sam H.; !2284]
+* Hide overview after 'Show Details' from app context menu [PhilProg; !2329]
+* Fix feedback when turning on a11y features by keyboard [Olivier; !2334]
+* Fix stylesheet papercuts [Sam H.; !2309]
+* Use libsoup 3.0 by default [Florian; !2338]
+* Indicate selected language in on-screen keyboard menu [Carlos; !2346]
+* Respect IM hint for candidates list in on-screen keyboard
+ [Carlos, Florian; !2347, !2359]
+* Fix edge case where windows stay dimmed after a modal is closed
+ [Jonas D.; !2349]
+* Improve Belgian on-screen keyboard layout [Evert; !2336]
+* Fix fallback ibus-daemon launching [oreo639; !2345]
+* Make sure screenshot UI opens above dialogs [Alynx; !2331]
+* Use EventEmitter class instead of injecting Signal methods [Evan; !2043]
+* Plugged leak [Sebastian; !2367]
+* Misc. bug fixes and cleanups [Florian, Jonas D., Jonas Å., Sebastian, Evan,
+ Marco; !2295, !2296, !2297, !2306, !2317, !2316, !2319, !2325, !2337, !2355,
+ !2351, !2293, !2365, !2366, !697, !2363, !2362, !2372]
+
+Contributors:
+ Marco Trevisan (Treviño), Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
+ Christian Glombek, Maksym Hazevych, Sam Hewitt, Sebastian Keller,
+ Florian Müllner, PhilProg, Evert Provoost, Jakub Steiner, Sam Uong,
+ Evan Welsh, Alynx Zhou, msizanoen1, oreo639, Jonas Ådahl
+
+Translators:
+ Jordi Mas [ca], Cheng-Chia Tseng [zh_TW], Charles Monzat [fr],
+ Changwoo Ryu [ko], Daniel Rusek [cs], Aleksandr Melman [ru],
+ Christian Kirbach [de], Yuri Chornoivan [uk], Emin Tufan Çetin [tr],
+ Danial Behzadi [fa], Hugo Carvalho [pt], Luming Zh [zh_CN],
+ Quentin PAGÈS [oc], Yosef Or Boczko [he], Fabio Tomat [fur],
+ Irénée THIRION [fr], Jürgen Benvenuti [de], Milo Casagrande [it],
+ Aurimas Černius [lt], Daniel Mustieles [es], Vojtěch Perník [cs]
+
+42.1
+====
+* Limit unfullscreen gesture to not interfere with overview [Ivan; !2227]
+* Properly hide the second (real) cursor when magnified [Jonas; !2234]
+* Fix various style glitches [Sam; !2215, !2277]
+* Fix creating default application folders [Adam; !2242]
+* Fix switching monitor configuration [Florian; !2245]
+* Add Home/End keynav in app grid [Anupam; !2241]
+* Handle monitor changes during startup animation [13r0ck; !2144]
+* Fix fractional timezone offsets in world clock [Raghuveer; !2255]
+* Default to right text-align in RTL locales [Florian; !2247]
+* calendar: Fix alignment of world clocks header in RTL [Yosef; !2240]
+* Rely on symbolic icons instead of 'HighContrast' icon theme [Jakub; !2243]
+* Fix moving windows from secondary monitor to non-active workspace
+ [Florian; !2259]
+* Make sure startup animation completes [Florian; !2269]
+* Fix Swiss on-screen keyboard layouts [Florian; !2273]
+* Add Austrian-German on-screen keyboard layout [Florian; !2274]
+* Fix on-screen keyboard in modal dialogs and lock screen [Jonas; !2263]
+* Fix menus in pad OSD [Carlos; !2279]
+* Sync default colors with libadwaita [Jakub; !2280]
+* Fix grab regressions when entering overview [Carlos, Sebastian; !2281, !2287]
+* Scale calendar with text size [Jonas; !2282]
+* Allow more intermediate icon sizes in app grid [Sebastian; !2289]
+* Fixed crash [Jonas D.; !2231]
+* Plugged memory leak [Sebastian; !2256]
+* Misc. bug fixes and cleanups [Jonas, Florian, Sebastian, Daniel, Marco, Simon;
+ !2283, !2285, !2286, !2233, !2236, !2244, !2246, !2258, !2257, !2252, !2261,
+ !2268, !2272, !2271, !2275]
+
+Contributors:
+ Marco Trevisan (Treviño), 13r0ck, Yosef Or Boczko, Jonas Dreßler,
+ Carlos Garnacho, Sam Hewitt, Zurab Kargareteli, Raghuveer Kasaraneni,
+ Sebastian Keller, Anupam Kumar, Simon McVittie, Ivan Molodetskikh,
+ Florian Müllner, Jakub Steiner, Daniel van Vugt, Adam Williamson
+
+Translators:
+ Balázs Úr [hu], Goran Vidović [hr], Charles Monzat [fr], Jiri Grönroos [fi],
+ Milo Casagrande [it], Jordi Mas [ca], Yuri Chornoivan [uk], Fran Dieguez [gl],
+ Marek Černocký [cs], Emin Tufan Çetin [tr], Alexander Shopov [bg],
+ Hugo Carvalho [pt], Piotr Drąg [pl], Anders Jonsson [sv], Matej Urbančič [sl],
+ Aurimas Černius [lt], Dušan Kazik [sk], Matheus Barbosa [pt_BR],
+ Bruce Cowan [en_GB], Rūdolfs Mazurs [lv], Nathan Follens [nl],
+ Марко Костић [sr], Aleksandr Melman [ru], Quentin PAGÈS [oc],
+ Andika Triwidada [id], Danial Behzadi [fa], Ngọc Quân Trần [vi],
+ Yaron Shahrabani [he], Daniel Mustieles [es], Gun Chleoc [gd],
+ Changwoo Ryu [ko], Fabio Tomat [fur], Ask Hjorth Larsen [da],
+ Yosef Or Boczko [he], Sveinn í Felli [is], Zurab Kargareteli [ka],
+ Olexander [uk]
+
+42.0
+====
+* introspect: Add WindowsChanged signal [Cenk; !2229]
+* Fix on-screen keyboard during grabs [Carlos; !2237]
+* Fixed crash [Adam; !2238]
+
+Contributors:
+ Carlos Garnacho, Cenk Uluisik, Adam Williamson
+
+Translators:
+ Fabio Tomat [fur], Tim Sabsch [de], Ivan Molodetskikh [ru],
+ Yosef Or Boczko [he], Марко Костић [sr], Baurzhan Muftakhidinov [kk],
+ 純 小山田 [ja], sicklylife [ja]
+
+42.rc
+=====
+* Fix programatically set scrollview fade [Jonas D.; !2189]
+* Fix overview DND during screencasts [Ivan; !2192]
+* Fix taking screenshots with shift+PrntScrn [Ivan; !2202]
+* Disable workspace switching while in search [Sebastian; !2204]
+* Fix opening device settings for enterprise WPA networks [Xiaoguang; !2194]
+* Switch workspaces with Home and End keys in overview [kyte; !2201]
+* Fix regressions from style refresh [Sam; !2206, !2185, !2212]
+* Show disabled icons in system menu when devices are disabled [Jonas D.; !2198]
+* Fix inserting items from object inspector in looking glass [Florian; !2218]
+* Fix drag placeholder position in dash in RTL locales [Florian; !2219]
+* Simplify signal handling with (dis)connectObject() convenience methods
+ [Florian; !1953, !2221, !2226]
+* Ensure remote access indicator is shown for a minimum time [Jonas Å.; !2132]
+* Improve CSS shadow appearance [Daniel; !1918]
+* Ignore work area in overview layout [Florian; !2223]
+* Fix glitches in overview transition [Jonas D., Sebastian; !2203, !1980]
+* Fixed crash [Florian; !2207]
+* Misc. bug fixes and cleanups [Jonas D., Björn, Florian, Carlos, Jonas Å.;
+ !2191, !2188, !2195, !2196, !2184, !2183, !2200, !2209, !2211, !2214, !2216,
+ !2110, !2222, !2220, !2225]
+
+Contributors:
+ Björn Daase, Jonas Dreßler, Carlos Garnacho, Sam Hewitt, Sebastian Keller,
+ Ivan Molodetskikh, Florian Müllner, Daniel van Vugt, Xiaoguang Wang, kyte,
+ Jonas Ådahl
+
+Translators:
+ Jordi Mas [ca], Quentin PAGÈS [oc], Asier Sarasua Garmendia [eu],
+ Kukuh Syafaat [id], Emin Tufan Çetin [tr], Changwoo Ryu [ko],
+ Danial Behzadi [fa], Aurimas Černius [lt], Daniel Mustieles [es],
+ Dušan Kazik [sk], Alan Mortensen [da], Goran Vidović [hr], Jiri Grönroos [fi],
+ Luna Jernberg [sv], Piotr Drąg [pl]
+
+42.beta
+=======
+* Add Swiss French and Swiss German OSK layouts [Jürg; !2096]
+* Add switches for debug flags to looking glass [Sebastian; !1994]
+* Support auth lists on the login screen [Ray; !1978]
+* Overhaul shell stylesheet [Sam; !2104, !2111, !2150, !2161, !2172]
+* Fix timing issue on login screen [Naïm; !2116]
+* Fix activating apps with tracked OR windows [Florian; !2131]
+* Fix banding issues with the vignette lightbox shader [Jonas; !2133]
+* Implement in-shell screenshot and screencast UI
+ [Ivan; !1954, !2103, !2149, !2147, !2106, !2105, !2107, !2163]
+* Fix keyboard navigation to windows/apps in overview [Florian; !2124]
+* Adjust to Clutter grab API changes [Carlos; !2045, !2046, !2140]
+* Fix stuck transition when using super-scroll gesture [kyte; !2135]
+* Support the new 'high-contrast' setting [Alexander, Florian; !2069]
+* Remove rounded top bar corners [Florian; !2151]
+* Fix calendar events with custom timezone [Milan; !2145]
+* Support dark wallpapers [Alexander; !2137]
+* Improve terminology for pinning icons to dash [Rachit; !2136]
+* Use libadwaita for extension preferences [Florian; !2012]
+* Fix out-of-sync location indicator [Florian; !2170]
+* Fade screen when switching between dark/light mode [Alexander; !2070]
+* Improve presentation of multi-day events in calendar [Björn; !2108]
+* Implement Extensions redesign [Romain; !2114]
+* Implement OSD redesign [Florian; !2127]
+* Misc. bug fixes and cleanups [Florian, Evan, Jonas, Aleksandr, Ivan, Georges,
+ Leleat, Daniel, Carlos, Sam, Olivier, Bastien, Mohammed, Sebastian, Razze,
+ Alexander, Marek; !2089, !2040, !2097, !2047, !2102, !2118, !2123, !2121,
+ !2115, !2113, !2125, !337, !2098, !2126, !2139, !2138, !2143, !2146, !2155,
+ !2141, !2157, !2158, !2153, !2156, !2152, !2117, !2101, !2162, !2164, !2055,
+ !2165, !2166, !2168, !2169, !1972, !2174, !2175, !2171, !2173, !2160, !2167,
+ !2176, !2177, !2178, !1744, !2179, !2099]
+
+Contributors:
+ Milan Crha, Björn Daase, Rachit Keerti Das, Jonas Dreßler, Naïm Favier,
+ Olivier Fourdan, Carlos Garnacho, Sam Hewitt, Sebastian Keller, Leleat,
+ Jürg Lempen, Aleksandr Mezin, Alexander Mikhaylenko, Ivan Molodetskikh,
+ Florian Müllner, Georges Basile Stavracas Neto, Bastien Nocera, Razze,
+ Mohammed Sadiq, Ray Strode, Romain Vigier, Daniel van Vugt, Evan Welsh,
+ Sebastian Wick, kyte, Marek Černocký
+
+Translators:
+ Tao Liu [zh_CN], Yosef Or Boczko [he], Matej Urbančič [sl],
+ Yuri Chornoivan [uk], Daniel Mustieles [es], Aleksandr Melman [ru],
+ Rafael Fontenelle [pt_BR], Sveinn í Felli [is], Matheus Barbosa [pt_BR],
+ Luming Zh [zh_CN], Fran Dieguez [gl], sicklylife [ja], Hugo Carvalho [pt],
+ Quentin PAGÈS [oc], Yaron Shahrabani [he], Asier Sarasua Garmendia [eu],
+ Aurimas Černius [lt], Kukuh Syafaat [id], Marek Černocký [cs],
+ Leônidas Araújo [pt_BR], Alexander Shopov [bg]
+
+42.alpha
+========
+* Fix icon updates while hidden [Marco; !1983]
+* Fix erratic scrolling in GTK apps [Christian, Carlos; !1993, !2011]
+* Fix GTK color picker support [Florian; !1990]
+* Fix sound-file support in notifications [Florian; !1991]
+* Fix high-contrast switches [Florian; !2000]
+* Allow extensions to opt-in to running on lock/login screen [Ray; !1967]
+* magnifier: Avoid offscreen rendering if possible [Sebastian; !2026]
+* Warn when unsafe-mode is toggled [Florian; !2050]
+* Improve handling of all-day/zero-length events in calendar [Sebastian; !2023]
+* Keep keyboard focus in notification list after deleting message [Dylan; !2053]
+* switchMonitor: Don't include builtin/external modes on desktops [Jonas; !2056]
+* Fix wrongly rejected D-Bus calls after gnome-shell restarts [Sebastian; !2048]
+* Improve window tracking [Florian; [Florian; !2029]
+* Simplify scroll fade shader to work with old hardware [Sebastian; !2072]
+* Tweak (un)minimize animations [Daniel; !2066]
+* Properly handle markup when highlighting search terms [Sebastian; !2033]
+* Don't wake up screen in DND mode [kyte; !2051]
+* Port to GWeather 4.0 [Florian; !2083]
+* extensions-app: Use libadwaita [Romain, Florian; !1997]
+* Fix immediately withdrawn notifications getting stuck [Sebastian; !2079]
+* Honor XDG SingleMainWindow key in .desktop files [Nate; !2084]
+* Opt-in to unique GType names in extension prefs [Florian; !2024]
+* Don't terminate remote sessions in headless mode [Joan; !2057]
+* Fixed crashes [Ray, Sebastian, Benjamin, Florian; !2004, !2014, !2025,
+ !2065, !2073]
+* Misc. bug fixes and cleanups [Jonas, Florian, Jakub, Ray, Carlos, Sebastian,
+ Georges, Evan, Sergio, Emily, Robert; !1985, !1998, !1996, !1999, !1979,
+ !2007, !2010, !2013, !2021, !2027, !2015, !2030, !2039, !2036, !2038, !2041,
+ !2034, !1992, !2059, !2054, !2068, !2076, !2077, !2078, !2081, !2082, !2031,
+ !2042, !2086, !2087, !2085, !2088]
+
+Contributors:
+ Marco Trevisan (Treviño), Benjamin Berg, Sergio Costas, Carlos Garnacho,
+ Emily Gonyer, Nate Graham, Christian Hergert, Sebastian Keller, Robert Mader,
+ Dylan McCall, Florian Müllner, Georges Basile Stavracas Neto, Jakub Steiner,
+ Ray Strode, Joan Torres, Romain Vigier, Daniel van Vugt, Evan Welsh, kyte,
+ Jonas Ådahl
+
+Translators:
+ Марко Костић [sr], Ngọc Quân Trần [vi], Emin Tufan Çetin [tr],
+ Yaron Shahrabani [he], Sveinn í Felli [is], Goran Vidović [hr],
+ Marek Černocký [cs], Selyan Slimane AMIRI [kab], Rūdolfs Mazurs [lv],
+ eshagh shahidani [fa], Yuri Chornoivan [uk], Anders Jonsson [sv],
+ Rafael Fontenelle [pt_BR], Kukuh Syafaat [id], Christian Kirbach [de],
+ Piotr Drąg [pl], Dušan Kazik [sk], Charles Monzat [fr], Fabio Tomat [fur],
+ Quentin PAGÈS [oc], Hugo Carvalho [pt], Milo Casagrande [it],
+ Daniel Mustieles [es], MohammadSaleh Kamyab [fa], Aurimas Černius [lt],
+ Aleksandr Melman [ru], Fran Dieguez [gl], Мирослав Николић [sr],
+ Danial Behzadi [fa]
+
+41.0
+====
+
+Translators:
+ Goran Vidović [hr], Jordi Mas [ca], Aman Alam [pa], Balázs Úr [hu],
+ Piotr Drąg [pl], Nathan Follens [nl], Quentin PAGÈS [oc], Jiri Grönroos [fi],
+ Alexander Shopov [bg], Alan Mortensen [da], Efstathios Iosifidis [el]
+
+41.rc.1
+=======
+* Make tests optional [Florian; !1975]
+* Misc. bug fixes [Florian; !1974]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Daniel Șerbănescu [ro]
+
+41.rc
+=====
+* Optimize rendering of fullscreen zoom [Daniel; !1899]
+* Fix glitchy launch animations when leaving overview [Daniel; !1440]
+* Show low priority notifications only in message list [Florian; #4265]
+* Support Soup 3 instead of 2.4 [Florian; !1940, !1966]
+* Set log domain for extensions that use console.log() [Evan; !1958]
+* Fix wrong separator position in dash [Raghuveer; !1804]
+* Fix OSK not registering button presses on X11 [Sebastian; !1955]
+* Fix work area getting messed up by hidden panels [Florian; #4569]
+* Fix IM candidate popover position [Florian; #4593]
+* Restrict callers of private D-Bus APIs [Florian; !1970]
+* Support committing preedit string on focus loss [Carlos; !1929]
+* Misc. bug fixes and cleanups [Florian, Ivan, Sebastian, José, Robert; !1956,
+ !1949, !1937, !1961, !1962, !1871, !1964, !1946, !1957, !1959, !1879, !1968]
+
+Contributors:
+ José Expósito, Carlos Garnacho, Raghuveer Kasaraneni, Sebastian Keller,
+ Robert Mader, Ivan Molodetskikh, Florian Müllner, Daniel van Vugt, Evan Welsh
+
+Translators:
+ Yuri Chornoivan [uk], Quentin PAGÈS [oc], Asier Sarasua Garmendia [eu],
+ Hugo Carvalho [pt], Rafael Fontenelle [pt_BR], Jordi Mas [ca],
+ Alexey Rubtsov [ru], Danial Behzadi [fa], Kukuh Syafaat [id],
+ Marek Černocký [cs], Fran Dieguez [gl], Daniel Mustieles [es],
+ Źmicier Turok [be], Jiri Grönroos [fi], Emin Tufan Çetin [tr],
+ Baurzhan Muftakhidinov [kk], Bruce Cowan [en_GB], Philipp Kiemle [de],
+ Matej Urbančič [sl], Dušan Kazik [sk], Charles Monzat [fr],
+ Luna Jernberg [sv], Aurimas Černius [lt], Boyuan Yang [zh_CN],
+ Changwoo Ryu [ko]
+
+41.beta
+=======
+* Unbreak Xwayland apps when not using systemd in user session [Olivier; !1850]
+* extension-tool: Always print location on successful creation [Leleat; !1856]
+* Fix distributed man pages [Florian; !1859]
+* Immediately start app grid drags for non-touch devices [Georges; !1866]
+* Avoid unnecessary icon shadow changes [Daniel; !1869]
+* Handle screencast failures more gracefully [Florian; !1878]
+* Fix glitch in app grid → session transition [Harshad; !1886]
+* Use symbolic icons in looking glass toolbar [Chris; !1671]
+* Fix IM popup visibility when in fullscreen [Florian; !1900]
+* magnifier: Fix view jumps when using caret tracking [Lee; !1823]
+* Fix shadow rendering glitches [Daniel; !1897]
+* Allow overriding super+number shortcuts in Settings [Florian; #1250]
+* Fix vertical scrollbars in RTL locales [Florian; !1910]
+* Don't include hidden search results in keynav [Florian; #4470]
+* Use anaccelerated deltas in swipe tracker [Alexander; !1763]
+* status/network: Do not disable on login screen [Florian; !1874]
+* Always take workarea into account when allocating workspaces [Marco; !1892]
+* location: Split out geoclue agent from indicator [Ian; !1919]
+* Add focus indication for switches [vanadiae; !1920]
+* Honor switch-user lockdown settings in unlock screen [Ray; !1833]
+* Fix glitch when dragging window preview in overview [Sebastian; #4292]
+* Lock down "Show Text" in password entries [Ray; !687]
+* Add power mode selection to status menu [Florian; !1907]
+* workspace: Handle skip-taskbar changes [Daniel; !1925]
+* status/network: Use WWan settings panel for GSM/LTE modems [Mohammed; !1942]
+* extensionUtils: Add gettext convenience helpers [Florian; !1941]
+* screenShield: Fix suspend inhibitor accounting [Sebastian; !1927, !1952]
+* Unify app context menus [Florian; !1948]
+* Use consistent terminology in window menu [nitinosiris; !1834]
+* Fixed crashes [Gustavo, Florian; !1848, !1885, !1935]
+* Misc. bug fixes and cleanups [Jonas Å., Florian, Georges, Daniel, Olivier,
+ Carlos, Robert, Sebastian, Jakub, Jonas D., Fionn; !1838, !1854, !1860,
+ !1872, !1876, !1880, !1888, !1890, !1891, !1894, !1902, !1901, !1903,
+ !1841, !1904, !1786, !1906, !1909, !1908, !1840, !1913, !1917, !1914,
+ !1857, !1922, !1924, !1931, !1926, !953, !1932, !1939, !1938, !1944,
+ !1936, !1947, !1945, !1950]
+
+Contributors:
+ Marco Trevisan (Treviño), Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
+ Harshad Gavali, Tjipke van der Heide, Fionn Kelleher, Sebastian Keller, Lee,
+ Leleat, Robert Mader, Chris Mayo, Alexander Mikhaylenko, Florian Müllner,
+ Georges Basile Stavracas Neto, nitinosiris, Mohammed Sadiq, Ian Douglas Scott,
+ Gustavo Noronha Silva, Jakub Steiner, Ray Strode, Daniel van Vugt, vanadiae,
+ Jonas Ådahl
+
+Translators:
+ Quentin PAGÈS [oc], Cheng-Chia Tseng [zh_TW], Jordi Mas [ca],
+ Hugo Carvalho [pt], Julia Dronova [ru], Efstathios Iosifidis [el],
+ Alexander Shopov [bg], Florentina Mușat [ro], Yuri Chornoivan [uk],
+ Alexey Rubtsov [ru], Rafael Fontenelle [pt_BR], Yaron Shahrabani [he],
+ Daniel Mustieles [es], Matej Urbančič [sl], Danial Behzadi [fa],
+ Kukuh Syafaat [id], Fran Dieguez [gl], Boyuan Yang [zh_CN], Fabio Tomat [fur],
+ Tjipke van der Heide [fy], Emin Tufan Çetin [tr], Luna Jernberg [sv],
+ Daniel Șerbănescu [ro]
+
+40.1
+====
+* Fix keyboard preview for IM that use 'default' layout [Peng; !1756]
+* extensions: Fix copying technical details on prefs crash [Jan; !1795, !1796]
+* Make window previews as large as possible [Jonas D.; !1670, !1813]
+* Fix PgUp/PgDown handling in overview [Florian; !1798]
+* Fix scroll view following keyboard focus [Andrew; !1802]
+* Fix word suggestions in on-screen keyboard [Jia; !1806]
+* Support animated backgrounds set via the wallpaper portal [Alexander; !1816]
+* Fix freeze after cancelling (some) system-modal dialogs [Florian; !1819]
+* Support double-super when animations are disabled [Florian; !1811]
+* gdm: Allow timed login with disabled user list [Ray; !1809]
+* status: Report real battery percentage when full [Prajna; !1787]
+* Allow tap-drag-release to select variants in OSK [Carlos; !1789]
+* Fix password authentication after fingerprint failure [Ray, Benjamin; !1821]
+* Fix screen blanking on idle [Florian; !1824]
+* Fix oversized system action icons in search [Sebastian; !1777]
+* Only show 'Open Windows' in app menus if there are multiple [Leleat; !1827]
+* Fix duplicated workspaces in minimap [Sebastian; !1828]
+* Fix repeated letters in type-to-find overview search [Sebastian; !1828]
+* Improve workspace placeholder in overview minimap [Joonas; !1801]
+* screencasts: Improve default gstreamer pipeline [Jonas D.; !1633]
+* Leave overview when activating app via shortcut [Nishal; !1839]
+* Fix invisible scroll bars in faded views [Florian; !1831]
+* Correct arrow navigation in alt-tab switcher in RTL locales [Leleat; !1835]
+* Improve touch interaction of app grid actions [Carlos; !1774]
+* Fixed crashes [Jonas Å., Sebastian; !1810, !1817]
+* Misc. bug fixes and cleanups [Carlos, Yuri, Sebastian, Suryashankar, Zander,
+ arushsharma24, Florian, Georges, Jonas D., Frederic, Leleat, Jakub,
+ theRealPadster, Simon, Olivier; !1770, !1781, !1782, !1775, !1769, !1791,
+ !1764, !1793, !1799, !1805, !1484, !1815, !1820, !1807, !1825, !1822, !1800,
+ !1818, !1830, !1790, !1812, !1771, !1844, !1845, !1837, !1842]
+
+Contributors:
+ Jonas Ådahl, arushsharma24, Benjamin Berg, Zander Brown, Jia Chao,
+ Frederic Crozat, Suryashankar Das, Jonas Dreßler, Olivier Fourdan,
+ Carlos Garnacho, Joonas Henriksson, Sebastian Keller, Yuri Konotopov,
+ Nishal Kulkarni, Leleat, Alexander Mikhaylenko, Florian Müllner,
+ Georges Basile Stavracas Neto, Prajna Sariputra, Simon Schneegans,
+ Jakub Steiner, Ray Strode, theRealPadster, Jan Tojnar, Peng Wu, Andrew Zaech
+
+Translators:
+ Timo Jyrinki [fi], Boyuan Yang [zh_CN], Hannie Dumoleyn [nl], Dz Chen [zh_CN],
+ Ngọc Quân Trần [vi], Hugo Carvalho [pt], Yosef Or Boczko [he],
+ Yaron Shahrabani [he], Nathan Follens [nl], Cédric Valmary [oc],
+ Carmen Bianca BAKKER [eo], Emin Tufan Çetin [tr], Dušan Kazik [sk],
+ Stas Solovey [ru], Pawan Chitrakar [ne], Jordi Mas [ca], Quentin PAGÈS [oc]
+
+40.0
+====
+
+Translators:
+ Zander Brown [en_GB], Dušan Kazik [sk]
+
+40.rc
+=====
+* Define default app grid arrangement [Allan; !1700]
+* Fix shading behind system modal dialogs [Florian; !1712]
+* Only show logout-inhibiting apps in end-session dialog [Michael; !1424]
+* Fix minimap previews on multi-monitor systems [Florian; !1721]
+* Drop strict extension version match for unstable releases [Florian; !1719]
+* Fix missing app menu after overview gesture [Florian; !1726]
+* Improve app folder appearance [Jakub; !1714]
+* Fix window previews becoming undraggable after workspace switch
+ [Sebastian; !1717]
+* Fix dragging icons to a different page in RTL locales [Carlos; !1734]
+* Support double super for opening app grid [Florian; !1736]
+* Improve workspace handling on secondary monitors [Florian; !1735]
+* Improve interaction when dragging between app grid pages [Carlos; !1630]
+* Fix launching on requested workspace on wayland [Florian; !1316]
+* Make wifi strength indicator consistent with icons in Settings [Remi; !1432]
+* Start session in overview [Georges; !1678]
+* Tweak window preview layout in overview [Jonas D.; !1702, !1737]
+* Improve overview performance [Jonas D.; !1713, !1733, !1743, !1755]
+* Fix new workspaces being invisible in minimap [Florian; !1716]
+* Fix glitch after dragging window preview from second monitor [Ivan; !1727]
+* Do not switch workspaces when scrolling during super-tab [Florian; !1746]
+* Make OSK shifting up windows more reliable [Jonas D.; !1728, !1760]
+* Fix swipe gesture glitches [Alexander; !1731]
+* Fix mis-scaled preview icons in window picker [Sebastian; !1751]
+* Fix unintended app grid changes during icon DND in dash [Sebastian; !1767]
+* Fix missing X11 fallback icons [Florian; !1761]
+* Fixed crashes [Sebastian; !1718]
+* Misc. bug fixes and cleanups [Jonas Å., Sebastian, Abderrahim, Florian,
+ Alexander, Felix, Ivan, Jonas D., Jakub, Daniel; !1710, !1707, !1720, !1722,
+ !1724, !1732, !1246, !1738, !1740] !1742, !1741, !1745, !1748, !1749, !1765,
+ !1766, !1757, !1758]
+
+Contributors:
+ Jonas Ådahl, arushsharma24, Allan Day, Felix Divo, Jonas Dreßler,
+ Carlos Garnacho, Sebastian Keller, Abderrahim Kitouni, Alexander Mikhaylenko,
+ Ivan Molodetskikh, Florian Müllner, Georges Basile Stavracas Neto,
+ Remi Salmon, Jakub Steiner, Michael Terry, Daniel van Vugt
+
+Translators:
+ Fran Dieguez [gl], Balázs Meskó [hu], Baurzhan Muftakhidinov [kk],
+ Goran Vidović [hr], Christian Kirbach [de], Марко Костић [sr],
+ Asier Sarasua Garmendia [eu], Guillaume Bernard [fr], Tim Sabsch [de],
+ Philipp Kiemle [de], Danial Behzadi [fa], Piotr Drąg [pl],
+ Rūdolfs Mazurs [lv], Daniel Mustieles [es], Marek Černocký [cs],
+ Fabio Tomat [fur], Changwoo Ryu [ko], Kjartan Maraas [nb], A S Alam [pa],
+ Milo Casagrande [it]
+
+40.beta
+=======
+* Overview redesign
+ - Move dash to the bottom [Georges, Abderrahim, Jakub; !1559]
+ - Do not duplicate dash icons in app grid [Florian; !1580]
+ - Separate favorites from running apps in dash [Florian; !1606, !1610, !1659]
+ - Change to a horizontal workspace layout [Georges; !1593, !1603]
+ - Move background to workspaces [Georges, Florian, Jakub; !1599]
+ - Overhaul window preview style [Florian, Jakub; !1605, !1654, !1657]
+ - Rework workspace switching animation [Alexander; !1326]
+ - Show workspace side-by-side [Georges; !1613]
+ - Allow switching workspaces with super+scroll on wayland [Florian; !1612]
+ - Allow swiping through more than one workspace at a time [Alexander; !1647]
+ - Extend active dash area to the screen edge [Florian; !1660]
+ - Change overall overview layout [Georges, Florian; !1624]
+ - Refine workspace activation in overview [Georges; !1645]
+ - Add vertical swipe gesture to shift through overview [Carlos; !1643]
+ - Add shortcuts for shifting through overview [Florian; !1695]
+ - Round background corners in overview [Jonas D.; !1651, !1697]
+ - Hide workspace minimap when using less than two workspaces [Florian; !1696]
+ - Scale down backgrounds in window picker [Georges; !1691]
+ - Minor improvements and bug fixes [Florian, Abderrahim, Georges, Sebastian,
+ Jakub, Pascal, Jonas D.; !1594, !1609, !1608, !1620, !1629, !1625, !1634,
+ !1640, #3669, !1636, !1655, !1656, !1665, !1667, !1638, !1680, !1682, !1684,
+ !1681, !1687, !1693, !1694, !1692]
+* Fix glitch in app folder icons on smaller displays [Sebastian; !1581]
+* Remove arrows from top bar menus [Doan, Florian, Razze; !1583, !1618, !1615]
+* Fix stuck grab after failed area screenshots [Sebastian; !1600]
+* Prefer image-data hint over app-icon in notifications [Guilherme; !1616]
+* Fix drag actor position when animations are turned off [Sebastian; !1627]
+* Improve spacing in MPRIS notifications with small text [Sebastian; !1632]
+* Add Welcome dialog after major updates [Bastien; !1619]
+* Do not save empty commands in run dialog history [Lucas; !1442, !1653]
+* Consider device type for fingerprint hint on login screen [Razze; !1572]
+* Add tooltip to Extensions app to view unellipsized description [Leleat; !1663]
+* Fix stuck polkit dialog when using non-password auth [Florian; !1662]
+* Provide screenshot feedback (flash, shutter) before writing file to disk
+ [Brion, Jonas; !1658]
+* Improve Thai layout in on-screen keyboard [Panawat; !1427]
+* Improve error handling for fingerprint auth [Marco; !1652]
+* Handle a large number of window previews more gracefully [Sebastian; !1685]
+* Follow swipe gesture when bringing up on-screen keyboard [Carlos; !1668]
+* Disable out-of-date extensions by default (again) [Evan; !1706]
+* Fixed crashes [Jonas D., Carlos; !1673, !1672]
+* Misc. bug fixes and cleanups [Florian, Sebastian, Razze, Georges, Daniel,
+ Robert, Jonas D., Jonas Å., Marco, Bastien, TerraAr, Carlos; !1575, !1579,
+ !1577, !1578, !1585, !1587, !1588, !1590, !1591, !1592, !1595, !1596, !1597,
+ !1598, !1582, !1589, !1617, !1611, !1637, !1635, !1641, !1646, !1546, !1648,
+ !1650, !1649, !1644, !1661, !1622, !1075, !1674, !1556, !1686, !1683, !1688,
+ !1699, !1698, !1701, !1709]
+
+Contributors:
+ Jonas Ådahl, Jonas Dreßler, Carlos Garnacho, Sebastian Keller,
+ Abderrahim Kitouni, Leleat, Robert Mader, Alexander Mikhaylenko,
+ Florian Müllner, Georges Basile Stavracas Neto, Bastien Nocera, Pascal Nowack,
+ Razze, Guilherme Silva, Jakub Steiner, TerraAr, Marco Trevisan (Treviño),
+ Brion Vibber, Doan Nam Long Vu, Daniel van Vugt, Evan Welsh,
+ Lucas Werkmeister, Panawat Wong-kleaw
+
+Translators:
+ Balázs Úr [hu], Danial Behzadi [fa], Rafael Fontenelle [pt_BR], A S Alam [pa],
+ Anders Jonsson [sv], Yuri Chornoivan [uk], Marek Černocký [cs],
+ Hugo Carvalho [pt], Fran Dieguez [gl], Daniel Șerbănescu [ro],
+ Florentina Mușat [ro], Daniel Mustieles [es], Jordi Mas [ca],
+ Matej Urbančič [sl], Kukuh Syafaat [id], Bruno Lopes da Silva [pt_BR],
+ Аляксей [be], Balázs Meskó [hu], Марко Костић [sr], Enrico Nicoletto [pt_BR],
+ Emin Tufan Çetin [tr], Jiri Grönroos [fi], Aurimas Černius [lt]
+
+40.alpha.1.1
+============
+* Adapt to settings moving to gsettings-desktop-schemas [Florian; !1573]
+* Misc. bug fixes [Georges, Florian; !1571, !1574]
+
+Contributors:
+ Florian Müllner, Georges Basile Stavracas Neto
+
+Translators:
+ Марко Костић [sr], Marek Černocký [cs], Fabio Tomat [fur]
+
+40.alpha.1
+==========
+* Restore app picker fade during swipes [Georges, Florian; !1519, !1569]
+* Sort extensions in Looking Glass alphabetically [Florijan; !1516]
+* Fix screenshots of unredirected fullscreen windows on X11
+ [Jonas Å., LuK1337; !1534, !1549]
+* Use GPU to render (most) shadows [Georges; !1539]
+* Refine avatar styling [Joonas; !1490]
+* Fix storing VPN secrets [Sebastian; !1535]
+* Don't depend on asciidoc and sassc when building from tarball
+ [Florian; !1553, !1555]
+* Use horizontal pages in app picker [Georges; !1554]
+* Avoid costly icon theme lookups unless when needed [Ivan; !1558]
+* Properly expose locked state to logind [Florian; !1561]
+* Use 3 fingers for system swipe gestures [Georges; !1562]
+* Fix glitches in overview transitions [Jonas Å., Ivan; !1545, !1550]
+* Improve login screen accessibility [Mike; !1567]
+* Overhaul top bar styling [Jonas D.; !1397]
+* Allow changing input volume by scrolling over microphone icon [Razze; !1566]
+* Port Extensions app and portal to GTK4 [Florian; !1495]
+* Fixed crashes [Jonas Å.; !1527]
+* Misc. bug fixes and cleanups [Georges, Florian, Tom, Hans, Jonas D., Ivan,
+ Sebastian, Daniel; !1518, !1521, !1523, !1525, !1520, !1526, !1472, !1339,
+ !1529, !1531, !1532, !1533, !1528, !1350] !1542, !1537, !1548, !1551, !1557,
+ !1543, !1560, !1544, !1565]
+
+Contributors:
+ Jonas Ådahl, Jonas Dreßler, Mike Gerow, Florijan Hamzic, Joonas Henriksson,
+ Sebastian Keller, Tom Levy, LuK1337, Ivan Molodetskikh, Florian Müllner,
+ Georges Basile Stavracas Neto, Hans Ulrich Niedermann, Razze, Daniel van Vugt
+
+Translators:
+ Florentina Mușat [ro], Fabio Tomat [fur], Hugo Carvalho [pt],
+ Jordi Mas i Hernandez [ca], Philipp Kiemle [de], Fran Dieguez [gl],
+ Jordi Mas [ca], Kukuh Syafaat [id], Kjartan Maraas [nb],
+ Daniel Șerbănescu [ro], Matej Urbančič [sl]
+
+40.alpha
+========
+* Handle @content properties in ease() functions [Andre; !1461]
+* Fix screencast indicator on Xorg [Jonas Å.; !1466]
+* Expand ellipsized app picker labels on hover [Georges; !1477, !1496]
+* Fix inline-replies in chat notifications [Florian; #3226]
+* Refine MPRIS notification layout [Harry; !1480]
+* Improve do-not-disturb support [Florian; #2873, !1511]
+* Fix mobile broadband support [Popax21; #3203]
+* Add extension filtering in Extensions app [Florian; #3382]
+* Improve app picker spacing on larger resolutions [Georges; !1510]
+* Plugged leaks [Ray; !1460, !1459]
+* Fixed crashes [Daniel, Florian; !1453, #3258, !1507]
+* Misc. bug fixes and cleanups [Florian, Georges, Carlos, Joonas, Jan,
+ Jonas Å., Robert, Jonas D., Ian, Daniel, Sebastian, Jordan; !1456, !1436,
+ !1405, !1421, #3252, !1464, !1469, !1465, !1473, !1475, #3307, !1479,
+ !1482, !1470, !1486, #3335, #3342, #3388, !1497, !1498, !1491, !1501,
+ !1492, !1489, !1502, !1505, !1504, !1506, !1503, !1483, !1508, !1481,
+ !1509, !1512, !1417, !1517]
+
+Contributors:
+ Jonas Dreßler, Carlos Garnacho, Joonas Henriksson, Sebastian Keller,
+ Robert Mader, Andre Moreira Magalhaes, Daniel García Moreno, Harry Míchal,
+ Florian Müllner, Georges Basile Stavracas Neto, Jordan Petridis, Popax21,
+ Ian Douglas Scott, Ray Strode, Jan Tojnar, Olav Vitters, Daniel van Vugt,
+ Jonas Ådahl
+
+Translators:
+ Juliano de Souza Camargo [pt], Fabio Tomat [fur], Akarshan Biswas [bn_IN],
+ Carmen Bianca BAKKER [eo], Daniel Mustieles [es],
+ Asier Sarasua Garmendia [eu], Ask Hjorth Larsen [da], Yuri Chornoivan [uk],
+ Anders Jonsson [sv], Kjartan Maraas [nb]
+
+3.38.1
+======
+* Add screen recordings to recent items [Florian; #3171]
+* Tweak peek-password feature [Florian; #3138]
+* Fix workspace glitches in overview [Florian; #3208, #3211]
+* Improve DND behavior in app picker [Georges; !1447]
+* Misc. bug fixes and cleanups [Florian, Daniel, Georges, Bastien, Christopher,
+ yun341, Carlos; #3169, !1434, !1441, !1444, !379, !1443, #2230, !1448,
+ !1451, #3209]
+
+Contributors:
+ Yacine Bouklif, Christopher Davis, Carlos Garnacho, Daniel García Moreno,
+ Florian Müllner, Georges Basile Stavracas Neto, Bastien Nocera, yun341
+
+Translators:
+ Juliano de Souza Camargo [pt], Yacine Bouklif [kab], Charles Monzat [fr],
+ Cheng-Chia Tseng [zh_TW], Yosef Or Boczko [he]
+
+3.38.0
+======
+* Misc. bug fixes and cleanups [Andy, Florian; #3147, !1438]
+
+Contributors:
+ Andy Holmes, Florian Müllner
+
+Translators:
+ Anders Jonsson [sv], Balázs Úr [hu], Dušan Kazik [sk], Alan Mortensen [da],
+ Tim Sabsch [de], Claude Paroz [fr], Jiri Grönroos [fi], Juliano Camargo [pt],
+ Milo Casagrande [it], Aron Xu [zh_CN], Rūdolfs Mazurs [lv]
+
+3.37.92
+=======
+* Fix screen recorder shortcut [Florian; #3097]
+* Support cycle-group cycling only over windows in the workspac3 [Mitar; #3116]
+* Misc. bug fixes and cleanups [Georges, Daniel, Carlos; !1428, !1423,
+ !1311, #2968]
+
+
+Contributors:
+ Carlos Garnacho, Mitar, Florian Müllner, Georges Basile Stavracas Neto,
+ Daniel van Vugt
+
+Translators:
+ Yuri Chornoivan [uk], Zander Brown [en_GB], Rafael Fontenelle [pt_BR],
+ Nathan Follens [nl], Danial Behzadi [fa], Kukuh Syafaat [id],
+ Aurimas Černius [lt], Marek Černocký [cs], Fabio Tomat [fur],
+ Changwoo Ryu [ko], Марко Костић [sr], Daniel Mustieles [es], Dušan Kazik [sk],
+ Thibault Martin [fr], Jordi Mas [ca]
+
+3.37.91
+=======
+* Fix potential stack overflow in libcroco [Michael; !1404]
+* Fix page removal in app picker [Georges; !1406]
+* Fix "old style" VPN authentication [Florian; !1407]
+* Fix X11 session login [Benjamin; !1418]
+* Fix week number alignment when using font-scaling [Joonas; !1415]
+* Fix lock screen on systems without GLSL shader support [Zephaniah; #3071]
+* Misc. bug fixes and cleanups [Florian, Jonas, Georges, Andre, Will, Björn;
+ !1396, !1359, !1408, !1409, !1414, !1413, !1416, #3073]
+
+Contributors:
+ Jonas Ådahl, Benjamin Berg, Michael Catanzaro, Björn Daase, Joonas Henriksson,
+ Zephaniah E. Loss-Cutler-Hull, Andre Moreira Magalhaes, Florian Müllner,
+ Georges Basile Stavracas Neto, Will Thompson
+
+Translators:
+ Fran Dieguez [gl], Jordi Mas [ca], Daniel Mustieles [es],
+ Florentina Mușat [ro], Akarshan Biswas [bn_IN], Baurzhan Muftakhidinov [kk],
+ Piotr Drąg [pl], Rafael Fontenelle [pt_BR], Emin Tufan Çetin [tr],
+ sicklylife [ja], Danial Behzadi [fa], Jiri Grönroos [fi], Goran Vidović [hr],
+ Matej Urbančič [sl], Boyuan Yang [zh_CN]
+
+3.37.90
+=======
+* Fix extension updates when many extensions are installed [Jeremias; !1363]
+* Fix missing icons in on-screen keyboard [Emre; #2631, #3007]
+* Fix delay when showing calendar events [Sebastian; #2992]
+* Allow rearranging items in app picker [Georges; !1284]
+* Fix top bar navigation when NumLock is active [Olivier; #550]
+* Delay login animation until wallpaper has loaded [Michael; #734996]
+* Reset auth prompt on login screen on VT switch before fade in [Ray; #2997]
+* Move screencasting into a separate service [Jonas Å.; !1372]
+* Replace loaded terms with more descriptive one [Olivier; !1393]
+* Add "Boot Options" support to restart dialog [Hans; !199]
+* Move "Restart" into a separate menu item/dialog [Florian; #2202]
+* Default to not installing updates on low battery [Michael; #2717]
+* Misc. bug fixes and cleanups [Florian, Daniel V., Georges, Jonas Å.,
+ Daniel G., Carlos, Benjamin, Piotr, Andre, Jonas D., Andy; !1357, !1356,
+ #2969, #2969, !1358, !1371, #3005, !1380, #3022, !1381, !895, !1387, !1386,
+ !1385, #3037, !1389, !1390, !1391, !1383, !1399, #2983, !1403]
+
+Contributors:
+ Jonas Ådahl, Benjamin Berg, Michael Catanzaro, Piotr Drąg, Jonas Dreßler,
+ Olivier Fourdan, Carlos Garnacho, Hans de Goede, Andy Holmes,
+ Sebastian Keller, Andre Moreira Magalhaes, Daniel García Moreno,
+ Florian Müllner, Georges Basile Stavracas Neto, Jeremias Ortega, Ray Strode,
+ Emre Uyguroglu, Daniel van Vugt
+
+Translators:
+ Tim Sabsch [de], Boyuan Yang [zh_CN], Fabio Tomat [fur],
+ Efstathios Iosifidis [el], Rafael Fontenelle [pt_BR], Yuri Chornoivan [uk],
+ Daniel Șerbănescu [ro], Jordi Mas [ca], Daniel Mustieles [es],
+ Emin Tufan Çetin [tr], Asier Sarasua Garmendia [eu]
+
+3.37.3
+======
+* Refactor and clean up window picker
+ [Jonas D., Florian; !1297, !1298, !1305, !1345, !1353]
+* Move calendar events out of notifications list [Florian; !1282]
+* Refine app folder dialogs [Georges; !1301]
+* Hide switch-user button on lock screen if unsupported [Chingkai; #2687]
+* Refactor and clean up app picker pagination [Georges; !1271]
+* Add API to retrieve specified mimetypes from clipboards [Carlos; !1321]
+* Support prepending workspace with horizontal layouts [Florian; #2916]
+* Update microphone icon on input volume changes [fludixx; #2902]
+* Cache labels on GPU [Daniel; !1329]
+* Fix regressions in redesigned modal dialogs [Florian, Jonas D.; #2491, !1336]
+* Use GIcon for all application icons [Florian; !1342]
+* Support pre-authenticated logins in vmware environments [yun341; #1983]
+* Better support sandboxed apps with multiple .desktop files [Florian; #219]
+* Fix on-screen keyboard size in portrait orientation [Florian; #2349]
+* Plugged leaks [Sebastian, Daniel, Florian; !1306, !1319, !1341]
+* Misc. bug fixes and cleanups [Jonas D., Georges, Marco, Florian, Sebastian,
+ MOZGIII, Daniel, Mariana, Jonas Å.; !1296, !1295, #2643, !1300, !1309,
+ !1119, #2901, !1313, !1251, !1285, !1307, !1318, !1310, !1320, !1327, !1315,
+ !1289, !1331, !1332, !1333, !1334, !1340, !1287, !1308, !1346, !1299, !1343,
+ !1351, !1352, !1322]
+
+Contributors:
+ Marco Trevisan (Treviño), Chingkai, Jonas Dreßler, Carlos Garnacho,
+ Sebastian Keller, MOZGIII, Florian Müllner, Georges Basile Stavracas Neto,
+ Mariana Picolo, Daniel van Vugt, fludixx, yun341, Jonas Ådahl
+
+Translators:
+ Daniel Mustieles [es], Boyuan Yang [zh_CN], Yuri Chornoivan [uk],
+ Jordi Mas [ca], sicklylife [ja], Emin Tufan Çetin [tr],
+ Baurzhan Muftakhidinov [kk], Florentina Mușat [ro], Aurimas Černius [lt],
+ Rūdolfs Mazurs [lv]
+
+3.37.2
+======
+* Add support for "PrefersNonDefaultGPU" desktop key [Bastien; !1226]
+* Only start systemd units when running under systemd
+ [Carlos, Florian; #2755, !1242, !1252]
+* Fix "ghost" media controls [Bryan; #2776]
+* Fix zombie sockets from extensions downloader [Michael; #2774]
+* Update world clocks offsets when timezone changes [Bryan; #2209]
+* Support scrolling anywhere in slider menu items [Peter; #2795]
+* Fix "Do Not Disturb" setting getting reset on startup [Florian; #2804]
+* Only allow updates for extensions that aren't cached [Florian; !1248]
+* Fix matching notifications by PID [Florian; #2592]
+* Indicate extension errors in Extensions app [Florian; #2337]
+* Add clipboard API for querying supported mimetypes [Andy; #2819]
+* Add preview to color picker [Florian; #451]
+* Improve world clocks styling [PrOF-kk; #2825]
+* Remove Frequent view from app picker [Georges; !880]
+* Fix pad OSD glitches [Carlos; !1290]
+* Expose actor tree in looking glass [Georges; !1292]
+* Fixed crashes [Jonas D., Florian; #2709, #2757]
+* Misc. bug fixes and cleanups [Florian, AsciiWolf, Michael, Piotr, Ting-Wei,
+ Amr, Alexander, Bryan, Georges, Jonas D., Andy, Björn, Koki, Carlos; !1229,
+ !1231, !1233, !1235, #2578, #2735, #2751, #2602, #2777, !1249, #2796, !1268,
+ !1269, !1265, !1245, !1273, #2816, !1274, !1263, !1188, !1276, #2652, !1277,
+ !1281, #2286, !1267, !1286, !1279, !1288, !1293, !1294, !1291]
+
+Contributors:
+ AsciiWolf, Michael Catanzaro, Björn Daase, Jonas Dreßler, Bryan Dunsmore,
+ Koki Fukuda, Carlos Garnacho, Andy Holmes, Amr Ibrahim, Soslan Khubulov,
+ Ting-Wei Lan, Michael Lass, Alexander Mikhaylenko, Florian Müllner,
+ Georges Basile Stavracas Neto, Bastien Nocera, PrOF-kk, Peter Simonyi
+
+Translators:
+ Fabio Tomat [fur], Cheng-Chia Tseng [zh_TW], Yuri Chornoivan [uk],
+ Dušan Kazik [sk], Piotr Drąg [pl], Soslan Khubulov [os],
+ Daniel Mustieles [es], Nathan Follens [nl], Bruce Cowan [en_GB],
+ Florentina Mușat [ro], Milo Casagrande [it], Anders Jonsson [sv],
+ Charles Monzat [fr], Danial Behzadi [fa], sicklylife [ja], Kukuh Syafaat [id],
+ Jordi Mas [ca], Emin Tufan Çetin [tr], Jiri Grönroos [fi], Марко Костић [sr],
+ Christian Kirbach [de], Changwoo Ryu [ko], Matej Urbančič [sl]
+
+3.37.1
+======
+* Improve bluetooth submenu title [Mariana; #2340]
+* Add openPrefs() convenience method for extensions [Florian; !1163]
+* Bring back support for empty StIcons [Andre, Jonas D.; !1173, !1178]
+* Wake up screen when unlocking programmatically [Florian; !1158]
+* Improve extensions tool error reporting [Florian; #2391]
+* Improve handling of scale-factor changes [Georges; !1176]
+* Tone down weekend days with events in calendar [Jakub; #2588]
+* Fix showing bluetooth submenu when devices were set up [Florian; !1174]
+* Add support for parental controls filtering [Philip W.; !465]
+* Provide alternative extension templates [Florian; !812]
+* Improve weather section's empty state [Mariana; #2179]
+* Fix translations of folder names [Florian; #2623]
+* Drop Tweener [Jonas Å.; !1200]
+* Match ASCII alternatives of system actions [Will; #2688]
+* Fix delay on lock screen after entering wrong password [Jonas D.; #2655]
+* Use globalThis instead of window [Andy; #2322]
+* Inhibit remote access when disabled by session mode [Jonas Å.; !1210]
+* Improve calendar-server performance [Milan; #1875]
+* Add gnome-shell-extension-prefs wrapper for compatibility [Florian; !1220]
+* Fix stuck lock screen after unlock [Jonas D., Florian; #2446]
+* Fixed crashes [Jonas D., Florian, Carlos; #2584, #2625, !1223, !1218]
+* Misc. bug fixes and cleanups [Florian, Jonas Å., Marco, Andre, Georges,
+ Jonas D., Jan, Philip Ch.,, Xiaoguang, Will, Jordan, Matthew, qarmin;
+ !1126, !1155, !1156, !1165, !1168, !1169, #2551, #2563, !1172, !1175, !1179,
+ !1160, #2562, #2578, !1184, #2559, !1186, #2607, !1191, !1194, !1199, !1203,
+ #2649, #2628, !1205, !1206, !1208, !1207, !1211, !1214, !1213, !1192, !1217,
+ !1219, #1615, #2691, !1094, !1177]
+
+Contributors:
+ Marco Trevisan (Treviño), Philip Chimento, Milan Crha, Jonas Dreßler,
+ Carlos Garnacho, Andy Holmes, Matthew Leeds, Andre Moreira Magalhaes,
+ Florian Müllner, Georges Basile Stavracas Neto, Jordan Petridis,
+ Mariana Picolo, Jakub Steiner, Will Thompson, Jan Tojnar, Xiaoguang Wang,
+ Philip Withnall, qarmin, Jonas Ådahl
+
+Translators:
+ Fabio Tomat [fur], Cheng-Chia Tseng [zh_TW], Danial Behzadi [fa],
+ Jiri Grönroos [fi], Ibai Oihanguren Sala [eu], Марко Костић [sr],
+ Rūdolfs Mazurs [lv], Yuri Chornoivan [uk], Carmen Bianca BAKKER [eo],
+ Dingzhong Chen [zh_CN], Rafael Fontenelle [pt_BR], Petr Kovář [cs],
+ Asier Sarasua Garmendia [eu], Daniel Mustieles [es], Emin Tufan Çetin [tr]
+
+3.36.0
+======
+* Fix off-by-1900 error in date conversions [Florian; !1061]
+* Fix crash on startup with topIcons* extension enabled [Florian; #2308]
+* Don't require gsd-xsettings for X11 support on wayland [Olivier; !1065]
+* Fix ibus support in Xorg session [Carlos; #1690]
+* Improve Extensions D-Bus API [Florian; !1074]
+* Allow session modes to specify alternative resource name [Marco; !1063]
+* Fix link to location settings in aggregate menu [Sebastian; #2316]
+* Fix illegible app folder titles with light theme [ub; !1059]
+* Really fix visual glitch in sliders [Jonas; #1569]
+
+Contributors:
+ Marco Trevisan (Treviño), Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
+ Sebastian Keller, Florian Müllner, ub
+
+Translators:
+ Aman Alam [pa], Goran Vidović [hr], Aurimas Černius [lt],
+ Milo Casagrande [it], Daniel Korostil [uk], sicklylife [ja],
+ Marek Černocký [cs], Nathan Follens [nl]
+
+3.35.92
+=======
+* Plug a memory leak [Jonas D.; !1015]
+* Fix missing "back" button on login screen [Florian; #2228]
+* Fix width of window preview titles in overview [Jonas D.; #58]
+* Fix looking glass text with light style variant [Feichtmeier; !1023]
+* Center unlock entry [Florian; !1021]
+* Hide overlay scrollbars in notification popup [Jonas D.; !1013]
+* Work around add_actor() slowness in icon spring animation [Daniel; !1002]
+* Add disable-animations heuristics [Jonas Å.; !757]
+* Fix visual glitches in on-screen keyboard [Carlos; #2214]
+* Fix clearing changed textures from cache [Florian; #2244]
+* Fix visual glitch in sliders [Daniel; #1569]
+* Stop using dedicated lock screen background [Florian; !1001]
+* Fix entries disappearing after authentication errors [Florian; #2236]
+* Fix crash when animations are disabled [Florian; #2255]
+* Fix passing pointer events to clients when magnified [Jonas D.; !993]
+* Fix keynav on new lock screen [Florian; #2210]
+* Avoid short-lived allocations on actor removal [Christian; #2263]
+* Fix super-sized default avatars in user list [Florian, Sam; #2242]
+* Leave overview when locking the screen [Jonas D.; !1043]
+* Hide message list on login screen [Florian; #2241]
+* Avoid IO on the main thread [Christian, Florian; !1050, !1051]
+* Fix window animations getting stuck when client doesn't respond [Jonas; !1055]
+* Only subscribe to touchpad events for touchpad gestures [Daniel; !925]
+* Start X11 session services before Xwayland clients [Carlos; !836, !1056]
+* Only show switch-user button with unlock prompt [Florian; !1029]
+* Misc. bug fixes and cleanups [Jonas D., Florian, Georges, Jonas Å., Daniel,
+ Jakub, Philippe; !1018, !1020, !1024, !1027, !1026, !1022, !1031, !1035,
+ !1032, !1025, !1039, #2157, !1037, !1042, !1047, !1048, #2270, !1046,
+ !167, !1016]
+
+Contributors:
+ Jonas Dreßler, Feichtmeier, Carlos Garnacho, Christian Hergert, Sam Hewitt,
+ Florian Müllner, Georges Basile Stavracas Neto, Jakub Steiner, Philippe Troin,
+ Daniel van Vugt, Jonas Ådahl
+
+Translators:
+ Danial Behzadi [fa], Efstathios Iosifidis [el], Daniel Mustieles [es],
+ Sabri Ünal [tr], sicklylife [ja], Piotr Drąg [pl], Jordi Mas [ca],
+ Anders Jonsson [sv], Chao-Hsiung Liao [zh_TW], Asier Sarasua Garmendia [eu],
+ Rafael Fontenelle [pt_BR], Марко Костић [sr], Changwoo Ryu [ko],
+ Charles Monzat [fr], Jiri Grönroos [fi], Jor Teron [mjw], Bruce Cowan [en_GB],
+ Emin Tufan Çetin [tr], Alan Mortensen [da], Balázs Úr [hu], Fran Dieguez [gl],
+ Kukuh Syafaat [id]
+
+3.35.91
+=======
+* Improve magnifier [Carlos; !984]
+* Only enable OSK automatically if touch-mode is enabled [Carlos; #872]
+* Merge screen shield and unlock dialog to new lock screen [Georges; !872]
+* Improve ShellBlur effect [Jonas; !991]
+* Adapt user avatar for new lock screen [Umang, Georges; !922]
+* Animate prompt transition on lock screen [Florian; !972]
+* Reduce font-size in dialog titles if text doesn't fit [Jonas; !1012]
+* Various lock screen improvements and bug fixes [Jakub, Florian, Georges;
+ !996, !997, !999, #2212, !998, !1006, #2215, #2213]
+* Misc. bug fixes and cleanups [Daniel, Florian, Jakub, nana-4, Jonas; #2170,
+ #2167, !936, !988, #2187, !994, !995, !938, #2194, #2203, !1004, !977, !1014]
+
+Contributors:
+ Jonas Dreßler, Carlos Garnacho, Umang Jain, Daniel Mustieles, Florian Müllner,
+ Georges Basile Stavracas Neto, Jakub Steiner, Daniel van Vugt, nana-4
+
+Translators:
+ Daniel Mustieles [es, pt_BR], Rafael Fontenelle [pt_BR], Danial Behzadi [fa],
+ Anders Jonsson [sv], Asier Sarasua Garmendia [eu], Aurimas Černius [lt],
+ Bruce Cowan [en_GB], sicklylife [ja], Fran Dieguez [gl], Kukuh Syafaat [id],
+ Emin Tufan Çetin [tr], Jiri Grönroos [fi], Jordi Mas [ca], Claude Paroz [fr],
+ Ask Hjorth Larsen [da], Марко Костић [sr], Piotr Drąg [pl],
+ Charles Monzat [fr], Balázs Úr [hu]
+
+3.35.90
+=======
+* Update default favorite apps [Michael; !907]
+* Add Shell.Blur effect [Georges; !864, !924]
+* Overhaul scroll/swipe gestures [Alexander; !821, !825, !826]
+* Fix VPN connections when delaying request [Florian; #2008]
+* Overhaul theme [Sam, Jakub, nana-4; !904, !931, !957]
+* Improve visual appearance of Weather integration [Florian; #1143]
+* Implement new system dialog designs [Jonas; #1343]
+* Animate position changes of app icons [Georges; !882]
+* Add St.Viewport [Georges; !929]
+* Make app folders behave as dialogs [Georges; !896]
+* Add do-not-disturb functionality to calendar popup [Florian; #239]
+* Show hint actor in focused entries [Jonas; !944]
+* Switch screen-recorder back to VP8 [Björn; #256]
+* Allow to run perf-tool as wayland compositor [Olivier; !941]
+* Handle extension updates [Florian; !945]
+* Animate showing and hiding caps-lock warning [Jonas; !952]
+* Support "auto" lengths in CSS [Florian; !971]
+* Turn extension-prefs into the official Extensions app [Florian; #1968]
+* Sandbox the portal helper [Michael; !983]
+* Misc. bug fixes and cleanups [Florian, Björn, Jakub, Alexander, Daniel V.,
+ Jonas, nana-4, Carlos, Sebastian, Daniel G., Georges, Piotr; !918, !917,
+ !919, !920, #763, #791659, !927, #2091, !930, !926, !888, !934, !168, #2133,
+ #682, #2142, #2131, !943, #2132, #1958, #2146, !951, #1779, #2130, !964,
+ !965, !948, #2151, #1746, !967, !760, !968, !970, !973, #2169, #2176, !978,
+ !980, !979, #2177, !981, #2180, !974]
+
+Contributors:
+ Michael Catanzaro, Björn Daase, Jonas Dreßler, Piotr Drąg, Olivier Fourdan,
+ Carlos Garnacho, Sam Hewitt, Sebastian Keller, Andre Klapper,
+ Alexander Mikhaylenko, Daniel García Moreno, Florian Müllner,
+ Georges Basile Stavracas Neto, Jakub Steiner, Daniel van Vugt, nana-4
+
+Translators:
+ Asier Sarasua Garmendia [eu], Daniel Mustieles [es], Andrej Shadura [sk],
+ Carmen Bianca BAKKER [eo], Sucipto [id], Dušan Kazik [sk], Goran Vidović [hr],
+ sicklylife [ja], Kukuh Syafaat [id], Yi-Jyun Pan [zh_TW],
+ Rafael Fontenelle [pt_BR], Jordi Mas [ca], Jiri Grönroos [fi],
+ Fabio Tomat [fur], Umarzuki Bin Mochlis Moktar [ms], Daniel Korostil [uk],
+ Jor Teron [mjw], Anders Jonsson [sv], Aurimas Černius [lt]
+
+3.35.3
+======
+* Add discrete GPU support for NVidia drivers [Bastien; #1810]
+* Fix DND of window previews with tablet devices [Carlos; !897]
+* Update pad OSD actions dynamically on mode changes [Carlos; !898]
+* st: Add dedicated PasswordEntry widget [Umang; !619]
+* Allow stand-alone builds of gnome-extensions tool [Florian; !877]
+* extension-tool: Don't treat missing .js handler as error [Chuck; !905]
+* Disallow top bar menus without top bar [Florian; #2002]
+* Misc. bug fixes and cleanups [Georges, Florian, Robert, Umang; !901,
+ #789937, !909, !910, !911, !913, !916]
+
+Contributors:
+ Michael Catanzaro, Chuck, Carlos Garnacho, Umang Jain, Robert Mader,
+ Florian Müllner, Georges Basile Stavracas Neto, Bastien Nocera
+
+Translators:
+ Fabio Tomat [fur], Fran Dieguez [gl], Jordi Mas [ca], Daniel Mustieles [es]
+
+3.35.2
+======
+* Fix unredirection after cancelled animations [Florian; #1788]
+* Include shadow in window screenshots [Robert; !762]
+* Show indicator when microphone is active [Florian; !729]
+* Use inheritance instead of delegate pattern [Marco; !559]
+* Use cached coordinates for window sorting in overview [Andrew; !763]
+* Wiggle login/unlock password entries on failure [Georges; !769]
+* Update window titles in app menu [Florian; #1830]
+* Fix window animations getting stuck by workspace switches [Jonas D.; !784]
+* Fix not-responding dialog size when using geometry scaling [Jonas D.; !783]
+* Handle buggy MPRIS clients more gracefully [Philip; #1362]
+* Deprecate StBoxLayout's child properties [Florian; !780]
+* Remove StBin's align properties [Florian; !803]
+* Use correct timezones for events [Milan, Florian; !806, #1895]
+* Reduce overhead of tracking stylesheet changes [Carlos; !779]
+* Replace action icons in system menu with regular menu items [Florian; #270]
+* Refine polkit dialogs [Jonas D.; !788]
+* Fix battery icon glitch in "100% but charging" case [Philip; !814]
+* Fix windows getting stuck on screen if closed while animating [Florian; !815]
+* Use font from interface settings [Florian; #688288]
+* Show polkit confirmation dialog for users with no password
+ [Joaquim, Jonas D.; !829]
+* Use better OSK layout fallback for unsupported variants [Florian; #1907]
+* Hide stopped spinner in top bar [Joonas; !832]
+* Reuse existing icons when updating the app picker grid [Georges; !841]
+* Show switcher popups immediately on second key press [Florian; #1928]
+* Add position-based animation to page indicators [Alexander; !843]
+* Improve modifier-less keyboard navigation of switcher popups [Florian; #1883]
+* Improve weather integration [Florian; #1927, #1926]
+* Add back sound feedback when scrolling volume indicator [Florian; #53]
+* Fix creating app folders with no pre-existing folders [Jonas D.; #1652]
+* Improve DND page switching in app picker [Florian, Jonas D.; #1693]
+* Fix disable command of gnome-extensions tool [Florian; #1946]
+* Tweak styling of notifications/media constrols [Joonas; !855, !865]
+* Enable clean session shutdown after gnome-shell failure [Benjamin; !858]
+* Also remove scaled keys when texture cache is cleared [Daniel M.; !567]
+* Don't show overflow indicator in switchers that fit screen [Florian; #1834]
+* Move libcroco dependency in-tree [Federico; !861]
+* Move to app folder location when it is created/renamed [Georges; !883]
+* Dismiss switcher popups when a system modal dialogs opens [Florian; #1536]
+* Fix weather forecasts for automatic location when Weather is not sandboxed
+ [Florian; #1823]
+* Place launched applications into a systemd scope [Benjamin; !863]
+* Fixed crashes [Jonas D., Carlos; !787, !813]
+* Misc. bug fixes and cleanups [Marco, Georges, Daniel V., Florian, Robert,
+ Kalev, Philip, Jonas D., Will, Carlos, Jonas Å., cunidev, Joonas, Federico;
+ !747, !765, !421, !759, !749, !730, !770, #1799, !774, !773, !776, !777,
+ !782, !794, !778, !792, !790, !190, !796, !795, !797, !798, !800, !804, !808,
+ !807, !810, !811, !563, !809, !805, !817, !818, !822, !830, !828, !823, !835,
+ !840, !842, !833, !845, !846, !847, !851, #1916, !862, !866, #1979, !827,
+ #1976, !884, !873, !885, !799, !887, !891, !816]
+
+Contributors:
+ Marco Trevisan (Treviño), Benjamin Berg, Philip Chimento, Milan Crha,
+ Jonas Dreßler, Carlos Garnacho, Joonas Henriksson, Kalev Lember, Robert Mader,
+ Alexander Mikhaylenko, Daniel García Moreno, Florian Müllner,
+ Georges Basile Stavracas Neto, Federico Mena Quintero, Joaquim Rocha,
+ Will Thompson, Daniel van Vugt, Andrew Watson, cunidev, Jonas Ådahl
+
+Translators:
+ Daniel Mustieles [es], Goran Vidović [hr], Fabio Tomat [fur],
+ Danial Behzadi [fa], Andika Triwidada [id], Efstathios Iosifidis [el],
+ Ricardo Silva Veloso [pt_BR]
+
+3.35.1
+======
+* Misc. bug fixes and cleanups [Marco; Matthias; !758, #701212]
+
+Contributors:
+ Marco Trevisan (Treviño)
+
+3.34.1
+======
+* Fix "Frequent" view icons disappearing on hover [Jonas D.; #1502]
+* Allow editing app folder names [Georges, Marco; !675, !720]
+* Skip property transitions while hidden [Florian; !708]
+* Make menu animations more consistent [Florian, GB_2; #1595, !717]
+* Improve performance when enabling/disabling all extensions [Jonas D.; !96]
+* Fix extra icons appearing in "Frequent" view animation [Georges; !696]
+* Fix fading out desktop icons [Harshula; #1616]
+* Fix box-shadow glitch with prerendered resources [Daniel; #1186]
+* Fix accidentally skipped animations [Florian; #1572]
+* Fix screenshots and window animations when scaled [Robert; !728]
+* Don't leak NOTIFY_SOCKET environment variable to applications [Benjamin; !741]
+* Fix lock-up on X11 when ibus is already running on startup [Marco; #1712]
+* Fix screen dimming on idle [Marco; #1683]
+* Do not notify systemd before initialization is complete [Iain; !750]
+* Support SAE secrets in network agent [Lubomir; !751]
+* Fix various regressions with dynamic workspaces [Florian; #1497]
+* Fixed crashes [Florian, Marco; #1678, !746]
+* Misc. bug fixes and cleanups [Marco, Jonas D., Florian, Iain, Georges,
+ Jonas Å., Martin, Takao, Carlos; !700, !705, !709, !711, !707, #1538, !710,
+ !713, !699, !715, !718, !716, !719, !721, #1243, !725, !731, #1614, !683,
+ !732, !121, !735, !736, !740, #573, #1641, #1571]
+
+Contributors:
+ Marco Trevisan (Treviño), Benjamin Berg, Jonas Dreßler, Takao Fujiwara, GB_2,
+ Carlos Garnacho, Harshula Jayasuriya, Iain Lane, Robert Mader,
+ Daniel García Moreno, Florian Müllner, Georges Basile Stavracas Neto,
+ Lubomir Rintel, Martin Zurowietz, Jonas Ådahl
+
+Translators:
+ Rafael Fontenelle [pt_BR], Fran Dieguez [gl], Balázs Úr [hu],
+ Milo Casagrande [it], Daniel Șerbănescu [ro], Kukuh Syafaat [id],
+ Jiri Grönroos [fi], Daniel Mustieles [es], Piotr Drąg [pl],
+ Anders Jonsson [sv], Marek Černocký [cs], Jordi Mas [ca],
+ Aurimas Černius [lt], Christian Kirbach [de], Emin Tufan Çetin [tr],
+ Enrico Nicoletto [pt_BR], Danial Behzadi [fa], Марко Костић [sr],
+ Alexandre Franke [fr], Charles Monzat [fr], Kjartan Maraas [nb],
+ Ryuta Fujii [ja], Nathan Follens [nl], Dušan Kazik [sk], Fabio Tomat [fur],
+ Matej Urbančič [sl], Ask Hjorth Larsen [da], Alan Mortensen [da]
+
+3.34.0
+======
+* Handle startup/shutdown of misc X11 services [Carlos; !680]
+* Fix sound volume mute/unmute [Iain; #1557]
+* Correctly terminate pasted text [Carlos; #1570]
+
+Contributors:
+ Carlos Garnacho, Iain Lane
+
+Translators:
+ Tom Tryfonidis [el], Milo Casagrande [it], Ryuta Fujii [ja],
+ Efstathios Iosifidis [el], Carmen Bianca BAKKER [eo], Sabri Ünal [tr],
+ Dušan Kazik [sk], Balázs Meskó [hu], Claude Paroz [fr]
+
+3.33.92
+=======
+* Animate pointer a11y pie timer [Jonas D.; !688]
+* Fix restarting shell in systemd user session [Benjamin; !690]
+* Misc. bug fixes and cleanups [Florian, Jonas D., Jonas Å., Will;
+ !691, !689, !692, #1552, !698]
+
+Contributors:
+ Jonas Ådahl, Benjamin Berg, Piotr Drąg, Jonas Dreßler, Florian Müllner,
+ Will Thompson
+
+Translators:
+ Daniel Șerbănescu [ro], Danial Behzadi [fa], Daniel Mustieles [es],
+ Jiri Grönroos [fi], Asier Sarasua Garmendia [eu], Piotr Drąg [pl],
+ Rūdolfs Mazurs [lv], Anders Jonsson [sv], Fran Dieguez [gl], Jordi Mas [ca],
+ Matej Urbančič [sl], Zander Brown [en_GB], Ryuta Fujii [ja], Tim Sabsch [de],
+ Fabio Tomat [fur], Pawan Chitrakar [ne], A S Alam [pa], Changwoo Ryu [ko],
+ Aurimas Černius [lt], Daniel Rusek [cs], Marek Černocký [cs],
+ Kukuh Syafaat [id], Goran Vidović [hr], Rafael Fontenelle [pt_BR]
+
+3.33.91
+=======
+* Fix regression when adjusting brightness [Florian; #1500]
+* Fix pointer a11y timeout animation [Jonas D.; #1533]
+* Add new extensions CLI tool [Florian; #1234]
+* Only track top-level windows [Carlos; #556]
+* Misc. bug fixes and cleanups [Jonas D., Jonas Å., Piotr, Florian;
+ !678, !682, !686]
+
+Contributors:
+ Jonas Ådahl, Jonas Dreßler, Carlos Garnacho, Florian Müllner
+
+Translators:
+ Asier Sarasua Garmendia [eu], Sveinn í Felli [is], Anders Jonsson [sv],
+ Jordi Mas [ca], Kukuh Syafaat [id], Florentina Mușat [ro], Jiri Grönroos [fi],
+ Aurimas Černius [lt], Daniel Mustieles [es], Piotr Drąg [pl],
+ Danial Behzadi [fa]
+
+3.33.90
+=======
+* Implement DND app picker folder management [Georges; !643, !645, !664, !671]
+* Make Clocks/Weather integration work with sandboxed apps [Florian; #1158]
+* Support startup via systemd user instance [Benjamin; !507]
+* Replace Tweener with Clutter animations [Florian; !663, !22, !666, !668, !669]
+* Minimize travel distance in overview animation [Sergey; !267]
+* Rescan icon theme when installed apps changed [Georges; !661]
+* Consistently animate new window actions [Jonas; !662, !673]
+* Misc. bug fixes and cleanups [Florian, Daniel, Ray, Bastien, Jonas, Niels,
+ Marco, Georges; !635, !636, !637, #1462, !628, !640, !641, !627, !644, !647,
+ !385, #1474, !651, #1144, !646, !653, !652, !655, #1482, !656, $654, !665,
+ !667, !670, #1357, !672, !657, #1507, !674, !677]
+
+Contributors:
+ Benjamin Berg, Sergey Bugaev, Jonas Dreßler, Niels De Graef, Florian Müllner,
+ Georges Basile Stavracas Neto, Bastien Nocera, Ray Strode,
+ Marco Trevisan (Treviño), verdre, Daniel van Vugt
+
+Translators:
+ Asier Sarasua Garmendia [eu], Rafael Fontenelle [pt_BR],
+ Kristjan SCHMIDT [eo], Jor Teron [mjw], Daniel Mustieles [es],
+ Kukuh Syafaat [id], Jordi Mas [ca], Fabio Tomat [fur], Daniel Șerbănescu [ro],
+ Anders Jonsson [sv]
+
+3.33.4
+======
+* Fix unintentional interference between gestures [Jonas; !598]
+* Fix unintentional loop while polkit dialog is active [Ray; !602]
+* Fix alt-tab icon size on HiDPI [Jonas; !587]
+* Style fixes and improvements [Frederik, Jakub; !610, #1446, #1449]
+* Fix style updates for non-background CSS properties [Florian; #1212]
+* Fix cursor visibility in screen recordings [Illya; #1208]
+* Add option for disabling the hot corner [Florian; #688320]
+* Use more fine-grained levels in battery indicator [Florian; !561, #1442]
+* Fix the calculation of the maximum number of app search results [Jonas; !110]
+* Handle horizontal workspace layout with gestures/animations [Florian; !575]
+* Improve handling of session mode extensions [Florian, Didier; #789852]
+* Misc. bug fixes and cleanups [Jonas, Florian, Sonny, Carlos, Mario, Benjamin,
+ Marco, Ting-Wei; !599, !600, !591, !606, !152, !607, !604, !495, !608, !611,
+ !614, !612, !615, !618, #369, !620, #774, !621, !616, #1065, !609, !626,
+ !491, !631, !632, !633, #1457]
+
+Contributors:
+ Benjamin Berg, Jonas Dreßler, Frederik Feichtmeier, Carlos Garnacho,
+ Illya Klymov, Ting-Wei Lan, Florian Müllner, Sonny Piers, Mario Sanchez Prada,
+ Didier Roche, Jakub Steiner, Ray Strode, Jor Teron, Marco Trevisan (Treviño)
+
+Translators:
+ Jordi Mas [ca], Jor Teron [mjw]
+
+3.33.3
+======
+* Prepare for optional X11 [Carlos; !378]
+* Fix opening window menu [Marco; !557]
+* Reload search providers when installed applications change [Cosimo; !562]
+* Implement locate-pointer accessibility feature [Olivier; #981]
+* Allow to disable window menus via session mode [Cosimo; !569]
+* Implement mouse accessibility [Olivier; !474]
+* Call GDM's RegisterSession() after startup [Iain; !570]
+* Fix extended keys popups being hidden by on-screen keyboard [Marco; !583]
+* Fix top bar being hidden by lock screen [Jonas; !571]
+* Update theme to better match GTK's Adwaita [Frederik; #841]
+* Set up GJS profiler when GJS_TRACE_FD is set [Christian; !573]
+* Misc. bug fixes and cleanups [Jonas, Cosimo, Robert, Florian, Marco, Simon,
+ Laurent, Niels, Will; !551, !555, !464, #1333, !565, !572, !568, !558, #1205,
+ #1336, !579, !576, #1392, !582, !586, #1406, #1351]
+
+Contributors:
+ Laurent Bigonville, Cosimo Cecchi, Piotr Drąg, Jonas Dreßler,
+ Frederik Feichtmeier, Olivier Fourdan, Carlos Garnacho, Niels De Graef,
+ Christian Hergert, Iain Lane, Robert Mader, Florian Müllner, Simon Schampijer,
+ Jakub Steiner, Will Thompson, Marco Trevisan (Treviño)
+
+Translators:
+ Kukuh Syafaat [id], Balázs Meskó [hu], Daniel Mustieles [es],
+ Fabio Tomat [fur], Nathan Follens [nl], Goran Vidović [hr], Jordi Mas [ca]
+
+3.33.2
+======
+* Fix keeping actors visible in scrollviews [Marco; #1061]
+* Move some chrome above popup windows [Jonas D.; !358]
+* Include 'sandboxed-app-id' in window introspection info [Florian; #1289]
+* Port to libecal-2.0 [Milan; !501]
+* Support TCRYPT in mount password dialog [segfault; !126]
+* Misc. bug fixes and cleanups [Florian, Marco, Veerasamy; !517, #745, !499,
+ !510, !515, !546, !549]
+
+Contributors:
+ Cosimo Cecchi, Milan Crha, Jonas Dreßler, Florian Müllner, segfault,
+ Veerasamy Sevagen, Marco Trevisan (Treviño)
+
+Translators:
+ Daniel Mustieles [es]
+
+3.33.1
+======
+* Refine the app menu [Florian; #968]
+* Refine window preview style [Feichtmeier; !461]
+* Only emit ::style-changed on actual changes [Carlos; #1153]
+* Disable emoji on-screen keyboard support on X11 [Florian; #1172]
+* Fix tablet button mapping overlay [Carlos; #1220]
+* Don't crash for world clock locations with no timezone [Florian; #1062]
+* Don't leak old on-screen keyboard layout groups [Carlos; mutter#556]
+* Fix ellipsization in dialog subtitles/bodies [Marco; !531]
+* Fix glitch of fullscreen window in workspace switch animation [Jonas D.; !322]
+* Fix distortion of some image contents [Florian; !525]
+* Allow dragging unfocused tiled/maximized windows from top bar [Dylan; #679290]
+* Handle network interface name changes [Fabrice; !534]
+* Avoid unnecessary style changes when computing :first/:last-child
+ [Florian; !529]
+* Misc. bug fixes and cleanups [Florian, Marco, Robert, Georges, Carlos, Simon,
+ Jonas D.; !487, !441, !502, !503, !504, !506, #822, !551, !512, !509, !511,
+ #1054, !524, #1065, !331, !540]
+
+Contributors:
+ Fabrice Bellet, Jonas Dreßler, Feichtmeier, Carlos Garnacho, Robert Mader,
+ Dylan McCall, Simon McVittie, Florian Müllner, Georges Basile Stavracas Neto,
+ Marco Trevisan (Treviño)
+
+Translators:
+ Daniel Mustieles [es], Kukuh Syafaat [id], Fabio Tomat [fur],
+ Carmen Bianca BAKKER [eo], Dingzhong Chen [zh_CN], Tim Sabsch [de]
+
+3.32.1
+======
+* Fix avatar scaling on login screen [Florian; #1024]
+* Fix distortion of desktop zoom [Florian; #646]
+* Fix mouse cursor visibility when using desktop zoom [Florian, Marco; #1020]
+* Fix screen dimming after wake-up on lock screen [Xiaoguang; #900]
+* Fix Alt+Esc switcher [Florian; #1064]
+* Respect struts for popover placement [Andrea; #1102]
+* Fix app icons updates after theme changes [Florian; #1117]
+* Fix desktop zoom after resolution changes [Marco; #1120]
+* Implement stick-to-finger workspace switch overview gestures [Florian; #516]
+* Make World Clocks offsets relative to local time [Florian; #1157]
+* Fix top app icon disappearing from dash [Florian; #1053]
+* Update switch style to match new Adwaita [Jakub; !496]
+* Ensure CSS units are pixel-aligned when scaling is used [Carlos; #91]
+* Misc. bug fixes and cleanups [Florian, Jakub, Robert, Alex, Carlos, Phil,
+ Marco, Benjamin, AsciiWolf, Ray, verdre; !444, #1016, #1018, !449, #1036,
+ !455, #1094, !440, #1023, #624, #1017, !476, !473, !480, #1130, !485, !481,
+ !490, !489, #1151, !435, #1160, !482, #1150, #1166, !384]
+
+Contributors:
+ AsciiWolf, Andrea Azzarone, Benjamin Berg, Carlos Garnacho, Victor Ibragimov,
+ Robert Mader, Alex Monday, Florian Müllner, Jakub Steiner, Ray Strode,
+ Marco Trevisan (Treviño), verdre, Xiaoguang Wang, Phil Wyett
+
+Translators:
+ Victor Ibragimov [tg, af, et, ja], Bruce Cowan [en_GB], Piotr Drąg [tg],
+ Charles Monzat [fr], Khaled Hosny [ar], Goran Vidović [hr],
+ Cheng-Chia Tseng [zh_TW], Carmen Bianca BAKKER [eo], Daniel Mustieles [es],
+ Dušan Kazik [sk]
+
+3.32.0
+======
+* Fix sizing issues in on-screen-keyboard emoji panel [Carlos; !439]
+* Fix test linker failure on Debian/Ubuntu [Iain; !442]
+* Avoid assertion when sizing fallback app icons from CSS [Florian; #1027]
+* Fix mis-sized menu arrows after texture cache changes [Florian; !452]
+
+Contributors:
+ Carlos Garnacho, Iain Lane, Florian Müllner
+
+Translators:
+ Gábor Kelemen [hu], Victor Ibragimov [tg], Ryuta Fujii [ja], Piotr Drąg [af,
+ tg], Mart Raudsepp [et]
+
+3.31.92
+=======
+* Fix visual glitch in submenus [Alex; #987]
+* Support fractional scaling [Jonas, Marco; #765011, !5]
+* Only consider visible children for :first-child/:last-child [Florian; !312]
+* Hide trailing separator in search results [verdre; !311]
+* Remember choice in inhibit-shortcuts dialogue [Olivier; !382]
+* Don't toggle on-screen keyboard on every focus change [Carlos; !397]
+* Fix legacy tray icons not responding to events on wayland [Florian; #191]
+* Fix generating French OSK layout [Florian; #997]
+* Use borderless round user images [Florian; #811]
+* Misc. bug fixes and cleanups [Andrea, Robert, Florian, Marco, Niels,
+ Benjamin; !414, !417, !420, #996, !408, !422, !425, #1006, !427, !315,
+ #989, !430, !431, !432, #1015, !429, !423, !419, !434]
+
+Contributors:
+ Jonas Ådahl, Alan, Andrea Azzarone, Benjamin Berg, Olivier Fourdan,
+ Carlos Garnacho, Niels De Graef, Robert Mader, Alex Monday, Florian Müllner,
+ Marco Trevisan (Treviño), verdre
+
+Translators:
+ Carmen Bianca BAKKER [eo], Asier Sarasua Garmendia [eu], Stas Solovey [ru],
+ Changwoo Ryu [ko], Julien Humbert [fr], Milo Casagrande [it],
+ Марко Костић [sr], Ask Hjorth Larsen [da], Kukuh Syafaat [id],
+ Daniel Șerbănescu [ro], Bernd Homuth [de], Trần Ngọc Quân [vi],
+ Nathan Follens [nl], Rūdolfs Mazurs [lv], Aurimas Černius [lt]
+
+3.31.91
+=======
+* Don't close on-screen-keyboard's language menu on hover [Florian; #171]
+* Don't let unfullscreen gesture interfere with top bar taps [Jonas D.; #552]
+* Always use symbolic user icon in system menu [Florian; #957]
+* Add flags parameter in GrabAccelerators API [Andrea; #68]
+* Misc. bug fixes and cleanups [Florian, Jonas A.; !399, !398, !400, !402,
+ !407, !410, !411]
+
+Contributors:
+ Jonas Ådahl, Andrea Azzarone, Arnaud Bonatti, Jonas Dreßler, Florian Müllner
+
+Translators:
+ Jordi Mas [ca], Ryuta Fujii [ja], Marek Cernocky [cs], Fran Dieguez [gl],
+ Jiri Grönroos [fi], Serdar Sağlam [tr], Anders Jonsson [sv],
+ Matej Urbančič [sl], Gun Chleoc [gd], Kukuh Syafaat [id],
+ Baurzhan Muftakhidinov [kk], Alan Mortensen [da], Rafael Fontenelle [pt_BR],
+ Sveinn í Felli [is]
+
+3.31.90
+=======
+* Fix input methods after ibus restarts [Takao; #295]
+* Refresh world clocks and weather sections [Florian; #262]
+* Port to ES6 classes (update your extensions!) [Florian; !361]
+* networkAgent: Advise users when WPS support is available [Lubomir; !329]
+* Performance improvements [Carlos; #832, #815]
+* Fix drag-and-drop with wacom pens [Carlos; #540]
+* Fix CAPS LOCK indication on wayland [Carlos; #762881]
+* Show details of non-sensitive notifications on lock screen [Philip; #726]
+* Refine extension-prefs' error UI [Florian; !193]
+* Add switch-to-application-n keybindings [Florian; #648000]
+* Remove top bar translucency [Florian; #408]
+* Support emojis and keypads in on-screen keyboard [Carlos; #675]
+* Don't allow popups to re-enable keyboard shortcuts on lock screen
+ (CVE-2019-3820) [Florian, Ray; #851]
+* Replace app menu [Florian; #624]
+* Include commonly copied+pasted extension helpers [Florian; !150]
+* Misc. bug fixes and cleanups [Florian, Daniel, Philip, Sergio, Pascal,
+ Georges, verdre, Carlos, Christopher; #780, #909, !316, !308, !309, #915,
+ !350, !362, !357, !365, !366, !283, !367, #942, !371, !373, !374, !343,
+ !375, !292, !317, !377, !379, !346, !383, #953, !388]
+
+Contributors:
+ Sergio Costas, Christopher Davis, Bilal Elmoussaoui, Takao Fujiwara,
+ Carlos Garnacho, Niels De Graef, Christian Kellner, Ignat Loskutov,
+ Florian Müllner, Georges Basile Stavracas Neto, Pascal Nowack, Lubomir Rintel,
+ Jakub Steiner, Ray Strode, verdre, Daniel van Vugt, Philip Withnall
+
+Translators:
+ Daniel Mustieles [es], Carmen Bianca BAKKER [eo], Charles Monzat [fr],
+ Pieter Schalk Schoeman [af], Jordi Mas [ca], Matej Urbančič [sl],
+ Fran Dieguez [gl], Balázs Úr [hu], A S Alam [pa], Fabio Tomat [fur],
+ Aurimas Černius [lt], Piotr Drąg [pl], Marek Cernocky [cs], Ryuta Fujii [ja]
+
+3.31.4
+======
+* Improve icon grid performance [Daniel; #174]
+* Remove browser plugin [Michael; #766776]
+* Add DBus API for introspecting the application state [Jonas, Olivier; !326]
+* Always allow leaving the overview via the hot-corner [Pascal; #429]
+* Misc. bug fixes [Florian, Jasper, Andrea, Sam, Dani, Cosimo, Jonas, Carlos;
+ #643595, #673767, !293, #783, #781, !298, !297, #782, !301, !314, !305, #799,
+ #632, !327]
+
+Contributors:
+ Jonas Ådahl, Andrea Azzarone, Michael Catanzaro, Cosimo Cecchi, daniruiz,
+ Olivier Fourdan, Carlos Garnacho, Sam Hewitt, Andre Klapper, Florian Müllner,
+ Pascal Nowack, Jasper St. Pierre, RyuzakiKK, Marco Trevisan (Treviño),
+ João Paulo Rechi Vita, Daniel van Vugt
+
+Translators:
+ Kristjan SCHMIDT [eo], Matej Urbančič [sl], Daniel Mustieles [es],
+ Fabio Tomat [fur], Emin Tufan Çetin [tr], Anders Jonsson [sv],
+ Ryuta Fujii [ja]
+
+3.31.2
+======
+* Port away from and remove ShellGenericContainer [Georges; !153]
+* popupMenu: Fix keyboard activation when numlock is active [Andrea; #550]
+* Do not block all shortcuts while app folders are expanded [Florian; #648]
+* Fix regression in handling new input sources [Carlos; #691]
+* Reask password after udisk errors for no/wrong passwords [Sebastian; #640]
+* Improve performance of app icon animations [Daniel; !253, !261]
+* Avoid focus changes when updating keyboard options [Takao; #391]
+* notifications: Support icon theme names in 'image-path' hint [Marco; !285]
+* Respect natural-scroll setting for workspace swipe gesture [Erik; #516]
+* Confine window preview titles to workspace area [Florian; !214]
+* Misc. bug fixes [Florian, Carmen, Georges, Cosimo, Carlos; #602, #693,
+ #666, #647, !66, #768, #430, !286, !258, !287, gtk#1447]
+
+Contributors:
+ Andrea Azzarone, Carmen Bianca Bakker, Cosimo Cecchi, Sergio Costas,
+ Erik Duxstad, Takao Fujiwara, Carlos Garnacho, Florian Müllner,
+ Georges Basile Stavracas Neto, Sebastian Pinnau, Didier Roche, Jakub Steiner,
+ Marco Trevisan (Treviño), verdre, Daniel van Vugt
+
+Translators:
+ Kristjan SCHMIDT [eo], Dušan Kazik [sk], Matej Urbančič [sl],
+ Anish Sheela [ml], Rafael Fontenelle [pt_BR], Daniel Mustieles [es]
+
+3.30.1
+======
+* Cancel search on overview hiding [Marco; !205]
+* Fix disappearing network icon [Iain; #140]
+* Improve switch-monitor shortcut handling [Daniel; !208]
+* Fix missing key information in keyring dialog [Florian; #574]
+* De-duplicate all entries in run command history [Pascal; #524]
+* Fix frozen disk unlock dialogs [Florian; #565]
+* Fix unresponsive-app dialog blocking input in other windows [Florian; #273]
+* Fix handling of forward-key-event input method signal [Carlos; #531]
+* Misc. bug fixes [Florian, Marco, Carlos, Pascal, Andrea; #520, #791233,
+ !188, #539, !217, #536, #537, #578, !236, #579, !228, #618, #471, !255]
+
+Contributors:
+ Andrea Azzarone, Jürg Billeter, Daniel Drake, Carlos Garnacho, Andre Klapper,
+ Iain Lane, Florian Müllner, Bastien Nocera, Pascal Nowack, Jakub Steiner,
+ Ray Strode, Will Thompson, Marco Trevisan (Treviño), Adam Williamson,
+ Andrew Zaborowski
+
+Translators:
+ Yuras Shumovich [be], Stas Solovey [ru], Justin van Steijn [nl],
+ Dušan Kazik [sk], Khaled Hosny [ar], Madis O [et], Mart Raudsepp [et],
+ Марко Костић [sr], Piotr Drąg [pl], Marek Černocký [cs], Fran Dieguez [gl],
+ Ask Hjorth Larsen [da], Balázs Meskó [hu], Jiri Grönroos [fi],
+ Cheng-Chia Tseng [zh_TW]
+
+3.30.0
+======
+
+Contributors:
+ Harry Mallon, Marco Trevisan (Treviño)
+
+Translators:
+ Fran Dieguez [gl], Trần Ngọc Quân [vi], Balázs Meskó [hu],
+ Rūdolfs Mazurs [lv], Jiri Grönroos [fi], Anders Jonsson [sv], gogo [hr],
+ Ask Hjorth Larsen [da]
+
+3.29.92
+=======
+* Choose some actors to cache on the GPU [Daniel; #792633]
+* inputMethod: Hide preedit text if requested [Takao; #431]
+* Fix forced fallback app-menus on wayland [Jonas; #276]
+
+Contributors:
+ Jonas Ådahl, Takao Fujiwara, Mohammed Sadiq, Marco Trevisan (Treviño),
+ Daniel van Vugt
+
+Translators:
+ Baurzhan Muftakhidinov [kk], Kukuh Syafaat [id], Milo Casagrande [it],
+ Changwoo Ryu [ko], Marek Cernocky [cs]
+
+3.29.91
+=======
+* Fix handling of 0/false options in ShowOSD D-Bus API [Florian; #791669]
+* overview: Fix handling of confirmation dialogs on wayland [verdre; !180]
+* Avoid some full relayout/redraws [Carlos; !197]
+* Keep workspace switcher slid out when workspaces are in use [Florian; !161]
+* Ignore auto-repeat for some keybindings [Andrea; #373]
+* Misc. bug fixes [Carlos, Florian, Pascal; #464, !189, !191, !192, !162]
+
+Contributors:
+ Andrea Azzarone, Olivier Blin, Carlos Garnacho, Florian Müllner,
+ Pascal Nowack, verdre
+
+Translators:
+ Bruno Lopes da Silva [pt_BR], Matej Urbančič [sl], Piotr Drąg [pl],
+ Aurimas Černius [lt], Emin Tufan Çetin [tr], Fabio Tomat [fur],
+ Alexandre Franke [fr], Yi-Jyun Pan [zh_TW], Bernd Homuth [de],
+ Andre Klapper [cs], Jordi Mas [ca], Daniel Șerbănescu [ro],
+ Bruce Cowan [en_GB]
+
+3.29.90
+=======
+* Add remote access indication on wayland [Jonas; !160]
+* Fix wrong window positions in overview on wayland [Marco; #776588]
+* Add gesture to unfullscreen a window [Jan-Michael; !123]
+* Add PickColor method to screenshot D-Bus interface [Florian; #286]
+* Consider "new-window" action when opening new windows [Florian; #756844]
+* Make workspace switching gestures follow motion [Carlos; #788994]
+* Support audio volumes above 100% [Didier; #790280]
+* Misc. bug fixes [Florian, Daniel; #424, !132, !182, #433, !179, #786496]
+
+Contributors:
+ Jonas Ådahl, Jan-Michael Brummer, Piotr Drąg, Daniel Drake, Carlos Garnacho,
+ Florian Müllner, Georges Basile Stavracas Neto, Didier Roche, Jakub Steiner,
+ Marco Trevisan (Treviño)
+
+Translators:
+ Charles Monzat [fr], Daniel Mustieles [es]
+
+3.29.4
+======
+* Fix "Clear All" for calendar events [Florian; #325]
+* Allow cancelling direct switch operations [Xavier; #315]
+* Support being started by systemd --user [Iain; !137, !138]
+* Support key event forwarding required by some input methods [Carlos; #275]
+* Misc. bug fixes and cleanups [Jasper, Andrea, Florian; #663461, #372, !112,
+ #414, !151]
+
+Contributors:
+ Andrea Azzarone, Carlos Garnacho, Xavier Johnson, Iain Lane, Florian Müllner,
+ Jasper St. Pierre
+
+Translators:
+ Stas Solovey [ru]
+
+3.29.3
+======
+* Save creation time in screenshot metadata [Florian; #790481]
+* Improve consistency between ctrl- and middle-click on app icons [Xavier; #316]
+* Add support for font-feature-settings CSS property [Ryan; #34]
+* Adjust to MetaScreen removal [Jonas; #759538]
+* Misc. bug fixes [Florian, Marco, Sam; #298, #788931, #26, #76, !54, #788882,
+ #791233]
+
+Contributors:
+ Jonas Ådahl, Ryan Hendrickson, Xavier Johnson, Florian Müllner, Joe Rabinoff,
+ Sam Spilsbury, Marco Trevisan (Treviño)
+
+Translators:
+ Gun Chleoc [gd], Yi-Jyun Pan [zh_TW], Cédric Valmary [oc], Jordi Mas [ca]
+
+3.29.2
+======
+* Guard against untimely keyboard map changes [Carlos; #240]
+* Fix icons in search provider results [Florian; #249]
+* Fix blurriness of OSD under some resolutions [Silvère; #782011]
+* Fix lagging pointer when zoomed [Daniel; #682013]
+* Misc. bug fixes [Milan, Xiaoguang, Florian, Mario, Ole; #244, #787871,
+ #781471, #136, #214, #294]
+
+Contributors:
+ Ole Jørgen Brønner, Milan Crha, Carlos Garnacho, Yussuf Khalil,
+ Silvère Latchurié, Florian Müllner, Mario Sanchez Prada, Ray Strode,
+ Daniel van Vugt, Xiaoguang Wang
+
+Translators:
+ Rafael Fontenelle [pt_BR], Kukuh Syafaat [id], Marcos Lans [gl],
+ Anders Jonsson [sv], Mingcong Bai [zh_CN]
+
+3.29.1
+======
+* Support icons in app-menu [Florian; #760985]
+* Misc. bug fixes [Marco, Florian, Lubomir; #792687, #221, !63]
+
+Contributors:
+ Piotr Drąg, Takao Fujiwara, Christian Kellner, Florian Müllner,
+ Mario Sanchez Prada, Lubomir Rintel, Didier Roche, Marco Trevisan (Treviño),
+ verdre
+
+Translators:
+ gogo [hr], Stas Solovey [ru], Matej Urbančič [sl], Daniel Șerbănescu [ro],
+ Fabio Tomat [fur], Marek Cernocky [cs], Daniel Mustieles [es]
+
+3.28.1
+======
+* Fix compose characters in shell entries [Carlos; #115]
+* Don't show authentication dialogs on lock screen [Florian; #179, #166]
+* Fix handling of UTC timezone in world clock [Florian; #150]
+* Fix keyboard navigation in overview when hovering windows [Florian; #50]
+* Misc. bug fixes [Florian; #127, #788908, #763886, !39]
+
+Contributors:
+ Jeremy Bicha, Carlos Garnacho, Andy Holmes, Florian Müllner, Bastien Nocera
+
+Translators:
+ Stas Solovey [ru], Daniel Șerbănescu [ro], Dušan Kazik [sk],
+ Rafael Fontenelle [pt_BR], Nathan Follens [nl], Dz Chen [zh_CN],
+ Matej Urbančič [sl], Hannie Dumoleyn [nl], Khaled Hosny [ar],
+ Guillaume Bernard [fr]
+
+3.28.0
+======
+
+Translators:
+ A S Alam [pa], gogo [hr], Chao-Hsiung Liao [zh_TW], Jordi Mas [ca],
+ Anders Jonsson [sv], Balázs Úr [hu]
+
+3.27.92
+=======
+* Misc. bug fixes [Florian; #64, #66, #72]
+
+Contributors:
+ Jonas Ådahl, Christian Kellner, Florian Müllner
+
+Translators:
+ Daniel Mustieles [es], Wolfgang Stöggl [de], Cheng-Chia Tseng [zh_TW],
+ Dušan Kazik [sk], Changwoo Ryu [ko], Furkan Ahmet Kara [tr], Balázs Úr [hu],
+ Trần Ngọc Quân [vi], Milo Casagrande [it], GNOME Translation Robot [gd, nl],
+ Marek Cernocky [cs], Aurimas Černius [lt], Alain Lojewski [fr],
+ Rūdolfs Mazurs [lv], Stas Solovey [ru], Alan Mortensen [da]
+
+3.27.91
+=======
+* Fix wrong bluetooth state when disabled by HW airplane mode [Mario; #789110]
+* Dump javascript stack on aborts, traps and segfaults [Marco; #789237]
+* Allow Escape to "cancel" top bar focus [Stefano; #671121]
+* Fix leaving the overview erroneously on window hover [Carlos; #784545]
+* Add keyboard accessibility dialog [Olivier; #788564]
+* Port to libnm [Lubomir, Florian; #789811]
+* Don't pop up on-screen-keyboard on touch events [Florian, Carlos; #788188]
+* Improve the on-screen-keyboard [Carlos; !9, #46]
+* Add Thunderbolt support [Christian; !14]
+* Don't lock immediately on login after a wayland session crash [Florian; !17]
+* Respect cursor's hot x/y coordinates when recording [Florian Z.; #792860]
+* Allow closing windows and apps during <alt>Tab [Florian, Mario; #620106]
+* Fix small app folder icons when using HiDPI [Nikita; #792259]
+* Make sassc a mandatory build dependency [Mario; #792822]
+* Misc. bug fixes [Florian, Marco, Alessandro, Gautier, Jeremy, Bastien, Ray,
+ Carlos, Didier, Exalm, Rafal; #789231, #789277, #788542, #789103, #779974,
+ #788931, #776940, #786987, #791007, #791233, #791148, #706191, #791655,
+ #791487, #779413, #787845, #10, #788627, #792354, #792616, #781329, #780957,
+ #33, #740142, !38, !23]
+
+Contributors:
+ Jeremy Bicha, Alessandro Bono, Nikita Churaev, Piotr Drąg, Exalm,
+ Stefano Facchini, Olivier Fourdan, Carlos Garnacho, Christian Kellner,
+ Rafal Luzynski, Iñigo Martínez, Florian Müllner, Bastien Nocera,
+ Gautier Pelloux-Prayer, Mario Sanchez Prada, Lubomir Rintel, Didier Roche,
+ Jakub Steiner, Ray Strode, Marco Trevisan (Treviño), Florian Zwoch
+
+Translators:
+ Mingcong Bai [zh_CN], Hannie Dumoleyn [nl], Khaled Hosny [ar],
+ Kjartan Maraas [nb], Petr Kovar [cs], Marek Cernocky [cs],
+ Aurimas Černius [lt], Yosef Or Boczko [he], Kukuh Syafaat [id],
+ Sveinn í Felli [is], Jordi Mas [ca], Daniel Mustieles [es], Fabio Tomat [fur],
+ Rūdolfs Mazurs [lv], Emin Tufan Çetin [tr], Anders Jonsson [sv],
+ Matej Urbančič [sl], Jiri Grönroos [fi], Tim Sabsch [de], Gil Forcada [ca],
+ Dušan Kazik [sk], Balázs Meskó [hu], Piotr Drąg [pl], Tong Hui [zh_CN],
+ Fran Dieguez [gl], Enrico Nicoletto [pt_BR], gogo [hr],
+ Baurzhan Muftakhidinov [kk], Robert Antoni Buj Gelonch [ca],
+ Bruce Cowan [en_GB], Борисав Живановић [sr], Милош Поповић [sr@latin],
+ Марко Костић [sr]
+
+3.27.1
+======
+* Fix using icon-name strings with PopupImageMenuItems [Florian; #789018]
+* Misc. bug fixes [Jonas, Florian; #788607, #788943]
+
+Contributors:
+ Jonas Ådahl, Florian Müllner
+
+Translations:
+ Xavi Ivars [ca@valencia], Matej Urbančič [sl], Fabio Tomat [fur]
+
+3.26.1
+======
+* Don't detach launched apps to not break pkexec and friends [Florian; #763531]
+* Allow search providers to not autostart [Bastien, Florian; #785380, #787986]
+* Fix crash when tray icons are hidden/shown in quick succession [Ray; #787361]
+* Make window group switcher more consistent [Didier; #786009]
+* Improve legibility of the top bar when translucent [Jakub; #787940]
+* Don't crash when running outside a logind session [Florian; #788046]
+* Allow to run headless [Mario, Jonas; #730551]
+* Update calendar on timezone changes [Martin; #678507]
+* Improve keyboard navigation of window previews [Florian; #786546]
+* Run unit tests on `meson test` [Florian; #786497]
+* Misc. bug fixes [Florian, Marc-Antoine, Mario, Jakub, Krzesimir; #787423,
+ #766368, #787580, #787907, #787901, #788039, #788003, #786343, #787902,
+ #788265, #788339, #787905, #788282, #787676]
+
+Contributors:
+ Martin Andersson, Florian Müllner, Bastien Nocera, Krzesimir Nowak,
+ Marc-Antoine Perennou, Didier Roche, Mario Sanchez Prada, Jakub Steiner,
+ Ray Strode
+
+Translations:
+ Efstathios Iosifidis [el], Khaled Hosny [ar], Stas Solovey [ru],
+ Arash Mousavi [fa], Sveinn í Felli [is], Athul R T [ml],
+ Cheng-Chia Tseng [zh_TW], Anders Jonsson [sv]
+
+3.26.0
+======
+* Misc. bug fixes [Ray, Michael, Jonas; #786332] #786783, #786886, #786868]
+
+Contributors:
+ Jonas Ådahl, Michael Catanzaro, Sebastian Keller, Ray Strode
+
+Translations:
+ Daniel Mustieles [es], Fran Dieguez [gl], Baurzhan Muftakhidinov [kk],
+ Marek Cernocky [cs], Andika Triwidada [id], Aurimas Černius [lt],
+ Piotr Drąg [pl], Trần Ngọc Quân [vi], Jordi Mas [ca], Fabio Tomat [fur],
+ gogo [hr], Dušan Kazik [sk], Piotr Drąg [ne], Emin Tufan Çetin [tr],
+ Ask Hjorth Larsen [da], Cheng-Chia Tseng [zh_TW], Rūdolfs Mazurs [lv],
+ Balázs Meskó [hu], Matej Urbančič [sl], Jiri Grönroos [fi],
+ Милош Поповић [sr], Милош Поповић [sr@latin], Rafael Fontenelle [pt_BR],
+ Wolfgang Stöggl [de], Milo Casagrande [it], hanniedu [nl],
+ Yuras Shumovich [be], Changwoo Ryu [ko], Alain Lojewski [fr],
+ Alexander Shopov [bg], Daniel Korostil [uk], Kris Thomsen [da],
+ A S Alam [pa], Sebastian Rasmussen [sv], Inaki Larranaga Murgoitio [eu],
+ Jiro Matsuzawa [ja]
+
+3.25.91
+=======
+* Open context menu of highlighted search result on Shift+F10 [Florian; #675315]
+* Fix mid-sentence capitalization in weather forecasts [Florian; #779873]
+* Add switcher popup to cycle through monitor configurations [Rui; #783550]
+* Offer system actions in search [Rares; #691900]
+* Misc. bug fixes [Mario, Florian, Rui; #777519, #786120, #786145, #786419,
+ #786526, #786520, #786520, #786146]
+
+Contributors:
+ Emmanuele Bassi, Marek Cernocky, Piotr Drąg, Carlos Garnacho, Rui Matos,
+ Florian Müllner, Mario Sanchez Prada, Rares Visalom
+
+Translations:
+ Marek Cernocky [cs], Piotr Drąg [pl], Muhammet Kara [tr],
+ Мирослав Николић [sr, sr@latin], Kukuh Syafaat [id],
+ Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Fran Dieguez [gl],
+ gogo [hr], Jordi Mas [ca]
+
+3.25.90
+=======
+* Add permission dialog for inhibiting shortucts [Florian; #783342]
+* Improve window picker layout [Florian, Jakub; #783953]
+* Remove legacy status icon tray [Florian; #785956]
+* Drop autotools support [Florian; #785153]
+* Misc. bug fixes [Florian, Carlos, Cosimo; #785090, #785309, #767805,
+ #747794, #785358, #785556]
+
+Contributors:
+ Emmanuele Bassi, Cosimo Cecchi, Carlos Garnacho, Florian Müllner,
+ Jakub Steiner
+
+Translations:
+ Fabio Tomat [fur], Pawan Chitrakar [ne], Aurimas Černius [lt], gogo [hr],
+ Daniel Mustieles [es], Baurzhan Muftakhidinov [kk], Matej Urbančič [sl],
+ Marek Cernocky [cs], Dušan Kazik [sk], Jordi Mas [ca], Fran Dieguez [gl],
+ Andika Triwidada [id], Anders Jonsson [sv], Balázs Meskó [hu]
+
+3.25.4
+======
+* gdm: Fix "Not listed" focus indication [Florian; #784040]
+* Fix missing icons in freedesktop notifications [Florian; #784245]
+* gdm: Disable user list when empty [Xiaoguang; #731320]
+* gdm: Allow empty response to PAM messages [Ray; #784360]
+* Fix blocked clicks in shutdown dialog [Florian; #781738]
+* Show OSD popup when changing volume via scroll wheel [Florian; #781028]
+* Refine list search results [Rares; #749957]
+* Replace mutter's unresponsive app dialog [Carlos; #762083]
+* Improve handling of extension errors [Florian; #781728]
+* Implement tablet rings/strips configuration [Carlos; #782033]
+* Adjust to mozjs52 update in gjs [Florian; #785084, #785090]
+* Support the meson build system [Florian; #783229]
+* Misc. bug fixes [Ray, Florian, Jonas, Marco, Shih-Yuan, Joaquim, Carlos S.;
+ #780403, #772589, #784130, #783975, #784353, #784361, #772284, #765011,
+ #765011, #765011, #784985, #781471, #785047, #736148, #736148]
+
+Contributors:
+ Jonas Ådahl, Alessandro Bono, Michael Catanzaro, Carlos Garnacho,
+ Shih-Yuan Lee (FourDollars), Florian Müllner, Joaquim Rocha,
+ Mario Sanchez Prada, Carlos Soriano, Ray Strode, Marco Trevisan (Treviño),
+ Rares Visalom, Xiaoguang Wang
+
+Translations:
+ Jeremy Bicha po/es, he.po, Kukuh Syafaat [id], Fabio Tomat [fur]
+
+3.25.3
+======
+* Bypass proxies for captive portal [Bastien; #769692]
+* Correctly handle "text-shadow: none;" [Matt; #783485]
+* Add StEntry:hint-actor property [Mario; #783484]
+* Support text-shadow CSS property in StEntry [Mario; #783484]
+* Misc. bug fixes [Jonas, Florian, Bastien, Ting-Wei, Cosimo, Mario, Sebastian;
+ #777732, #783202, #783210, #783206, #783286, #783439, #783483, #783823,
+ #781950]
+
+Contributors:
+ Jonas Ådahl, Cosimo Cecchi, Sebastian Keller, Ting-Wei Lan, Florian Müllner,
+ Bastien Nocera, Mario Sanchez Prada, Matt Watson
+
+3.25.2
+======
+* Fix StEntry::primary-icon-clicked emission [Florian; #782190]
+* Add an optional icon parameter to PopupMenu.addAction() [Mario; #782166]
+* Allow search providers to include clipboard text with results [Daiki; #775099]
+* Reduce dependency on Caribou [Carlos; #777342]
+* Add transparency to top bar when free floating [Alessandro; #747163]
+* Animate maximize/unmaximize operations [Alessandro; #766685]
+* Misc. bug fixes [Florian, Matthias, Jeremy, Michael, Carlos, Lan; #782000,
+ #780215, #782802, #782637, #782930, #755164, #780215, #782982]
+
+Contributors:
+ Jeremy Bicha, Michael Biebl, Alessandro Bono, Carlos Garnacho, Ting-Wei Lan,
+ Matthias Liertzer, Florian Müllner, Mario Sanchez Prada, Daiki Ueno
+
+Translations:
+ Jordi Mas [ca], Christian Stadelmann [de], Милош Поповић [sr],
+ Милош Поповић [sr@latin], Furkan Ahmet Kara [tr]
+
+3.25.1
+======
+* Close Wifi selection dialog on lock [Florian; #780054]
+* Fix DND over window previews in overview [Florian; #737166]
+* Do not lock the screen when disabled by lockdown settings [Florian; #780212]
+* Follow GNOME Weather's location permissions [Florian; #780252]
+* Fix portals that require a new window to be loaded [Catalin; #759044]
+* Fix restricting menus to screen height on HiDPI displays [Cosimo; #753305]
+* Misc. bug fixes and cleanups [Florian, Cosimo, Bastien, Catalin, Carlos G.,
+ Jonas, Carlos S., Xiaoguang, Rares, Emilio; #780063, #780321, #780381,
+ #780453, #758873, #780606, #642652, #777732, #780157, #781482, #780404,
+ #781545, #781728]
+
+Contributors:
+ Jonas Ådahl, Cosimo Cecchi, Philip Chimento, Carlos Garnacho, Catalin Iacob,
+ Florian Müllner, Bastien Nocera, Emilio Pozuelo Monfort, Carlos Soriano,
+ Rares Visalom, Xiaoguang Wang
+
+Translations:
+ Marek Cernocky [cs], Piotr Drąg [pl], Anders Jonsson [sv], Stas Solovey [ru],
+ Rafael Fontenelle [pt_BR], Baurzhan Muftakhidinov [kk], Daniel Korostil [uk],
+ Kukuh Syafaat [id], Milo Casagrande [it], Jiri Grönroos [fi],
+ Daniel Mustieles [es], Balázs Úr [hu], Guillaume Bernard [fr],
+ Changwoo Ryu [ko], Mario Blättermann [de], Fran Dieguez [gl],
+ Dušan Kazik [sk], Yuras Shumovich [be], Fabio Tomat [fur],
+ Kjartan Maraas [nb], Aurimas Černius [lt], Trần Ngọc Quân [vi],
+ Rūdolfs Mazurs [lv], Γιάννης Κουτσούκος [el], gogo [hr], Марко Костић [sr],
+ Jordi Mas [ca], Khaled Hosny [ar]
+
+3.24.0
+======
+
+Translations:
+ GNOME Translation Robot [tg], Мирослав Николић [sr, sr@latin],
+ Guillaume Bernard [fr], Rūdolfs Mazurs [lv], Emin Tufan Çetin [tr],
+ sujiniku [ja], Daniel Korostil [uk]
+
+3.23.92
+=======
+* Implement DND to overview on wayland [Hyungwon; #765003]
+* Make telepathy optional at runtime [Florian; #771721, #779878]
+* Don't show forecasts for NYC when geoclue gets stuck [Sebastian; #779898]
+* Add bottom edge drag gesture to bring up the OSK [Jan-Michael; #757712]
+* Allow switching between pads in the same group [Carlos; #779986]
+* Ignore showBanners policy for critical notifications [Florian; #779974]
+* Misc. bug fixes [Florian; #779435, #779819, #779820]
+
+Contributors:
+ Jan-Michael Brummer, Allan Day, Carlos Garnacho, Hyungwon Hwang,
+ Sebastian Keller, Florian Müllner
+
+Translations:
+ Enrico Nicoletto [pt_BR], Jiri Grönroos [fi], Chao-Hsiung Liao [zh_TW],
+ Piotr Drąg [pl], Piotr Drąg [he], Balázs Meskó [hu], Kris Thomsen [da],
+ Yuras Shumovich [be], Sveinn í Felli [is], Inaki Larranaga Murgoitio [eu],
+ Changwoo Ryu [ko], Jordi Mas [ca], Aurimas Černius [lt],
+ Мирослав Николић [sr, sr@latin], Christian Kirbach [de], Anders Jonsson [sv],
+ Fabio Tomat [fur], GNOME Translation Robot [gd], Dušan Kazik [sk],
+ Kukuh Syafaat [id], Marek Černocký [cs], Stas Solovey [ru],
+ Milo Casagrande [it], Fran Dieguez [gl], Daniel Boles [gl], A S Alam [pa],
+ Daniel Mustieles [es]
+
+3.23.91
+=======
+* Use the original timestamps for restored notifications [Florian; #766410]
+* Add weather information to date+time drop-down [Florian; #754031]
+* Refine message list layout in date+time drop-down [Florian; #775763]
+* Make next/prev media controls insensitive when unavailable [Florian; #773884]
+* Misc. bug fixes [Piotr, Bastien, Florian; #772210, #769546, #775799]
+
+Contributors:
+ Piotr Drąg, Carlos Garnacho, Florian Müllner, Bastien Nocera
+
+Translations:
+ Baurzhan Muftakhidinov [kk], Jordi Mas [ca], Ask Hjorth Larsen [da],
+ Inaki Larranaga Murgoitio [eu], Daniel Mustieles [es], Dušan Kazik [sk],
+ Aurimas Černius [lt], Jiri Grönroos [fi], Kjartan Maraas [nb],
+ Piotr Drąg [pl], Daniel Korostil [uk], Kukuh Syafaat [id],
+ Milo Casagrande [it], Fabio Tomat [fur], Rafael Fontenelle [pt_BR],
+ Fran Dieguez [gl], Мирослав Николић [sr, sr@latin], Balázs Meskó [hu],
+ Chao-Hsiung Liao [zh_TW]
+
+3.23.90
+=======
+* Handle Ctrl+Q and Ctrl+W in portal window [Bastien; #764133]
+* Allow to scroll through ibus candidates with mouse [Peng; #776032]
+* Reload apps on .desktop file content changes [Adrian; #773636]
+* Use private data/cache directories in portal helper [Bastien; #775639]
+* Fix subsurfaces not showing up in previews [Rui; #756715]
+* Fix theme node transitions [Florian; #778145]
+* Update pad (o)leds on mode switches [Carlos; #776543]
+* Add security indicators to defend against malicious portals [Bastien; #749197]
+* Don't allow type ahead at the login screen [Ray; #766139]
+* Don't fail to load because of TLS errors [Bastien; #778253]
+* Ensure the network lists remains sorted on rename [Benjamin; #778686]
+* Toggle power-off/suspend button on long-press [Florian; #721173]
+* Add "kill-switch" for user extensions [Florian; #778664]
+* Add night light indicator to status area [Florian; #741224]
+* Misc. bug fixes [Michael, Bastien, Carlos, Rui, Florian, Alan, Philip, Jonas;
+ #759793, #735233, #762444, #777784, #777934, #778158, #776199, #778425,
+ #771098, #778552, #777317, #778660, #778661, #745626, #778672]
+
+Contributors:
+ Jonas Ådahl, Benjamin Berg, Michael Catanzaro, Philip Chimento,
+ Alan Coopersmith, Piotr Drąg, Carlos Garnacho, Yuri Konotopov,
+ Lionel Landwerlin, Rui Matos, Florian Müllner, Bastien Nocera,
+ Adrian Perez de Castro, Robert Roth, Ray Strode, Peng Wu
+
+Translations:
+ Jiri Grönroos [fi], Balázs Meskó [hu], Gábor Kelemen [hu],
+ Daniel Mustieles [es], Dušan Kazik [sk],
+ Piotr Drąg [ar, eu, fa, hr, pa, pt, sr, sr@latin], Rafael Fontenelle [pt_BR],
+ Jordi Mas [ca], Piotr Drąg [pl], Alexandre Franke [fr],
+ Baurzhan Muftakhidinov [kk], Yuras Shumovich [be], Mandy Wang [zh_CN],
+ Marek Černocký [cs], Kukuh Syafaat [id], Kjartan Maraas [nb],
+ Daniel Korostil [uk]
+
+3.23.3
+======
+* Fix replacing of GNotifications [Florian; #775149]
+* Prepare for mozjs31 GJS [Philip; #775374]
+* Misc. bug fixes [Niels, Jonas; #775507, #776130]
+
+Contributors:
+ Jonas Ådahl, Michael Catanzaro, Philip Chimento, Niels De Graef,
+ Carlos Garnacho, Florian Müllner
+
+Translations:
+ Muhammet Kara [tr], Christian Kirbach [de], Baurzhan Muftakhidinov [kk],
+ Cheng-Chia Tseng [zh_TW], A S Alam [pa], Gianvito Cavasoli [it]
+
+3.23.2
+======
+* Implement Pad configuration OSD [Carlos; #771067]
+* Show overview on three-finger touchpad pinch [Carlos; #765937]
+* Summarize network sections with too many devices [Florian; #773892]
+* Always show primary network icon when connected [Florian; #773890]
+* Fix fullscreen transitions on wayland [Rui; #770345]
+* Work around portal failures by using a URL without HTPPS redirect [Debarshi; #769940]
+* Fix app view hiding when no usage data is available [Florian, Xiaoguang; #774381]
+* Misc. bug fixes [Florian, Ray; #773875, #740043, #773893, #774643, #774805]
+
+Contributors:
+ Carlos Garnacho, Rui Matos, Florian Müllner, Debarshi Ray, Ray Strode,
+ Xiaoguang Wang
+
+Translations:
+ Balázs Meskó [hu], Fabio Tomat [fur], Marek Cernocky [cs], Stas Solovey [ru],
+ Daniel Mustieles [es], Marek Černocký [cs], Piotr Drąg [pl],
+ Rafael Fontenelle [pt_BR], Baurzhan Muftakhidinov [kk], Jiri Grönroos [fi],
+ Kjartan Maraas [nb]
+
+3.23.1
+======
+* Request periodic scans while WiFi list is open [Dan; #767918]
+* Include extension UUID in structured log metadata [Jonh; #770717]
+* Line-wrap PAM messages on login screen [Tao; #764445]
+* Add a way to launch an app on the discrete GPU [Bastien; #773117]
+* Only allow graphs to lift screen shield when locked [Florian; #773328]
+* Add reload option to gnome-shell-extension-tool [Jonh; #772593]
+* Update background animations when resuming from suspend [Florian; #773265]
+* Misc. bug fixes [Cosimo, Bastien, Florian, Philip, Carlos; #772723, #772287,
+ #756432, #772386, #772386, #773085, #773634]
+
+Contributors:
+ Cosimo Cecchi, Philip Chimento, Carlos Garcia Campos, Florian Müllner,
+ Bastien Nocera, Jonh Wendell, Dan Williams, Tao Yang
+
+Translations:
+ Fabio Tomat [fur], Philip Chimento [zh_CN], YunQiang Su [zh_CN],
+ Jordi Mas [ca], Piotr Drąg [pl], Muhammet Kara [tr], Marek Černocký [cs],
+ Daniel Korostil [uk], Dušan Kazik [sk]
+
+3.22.1
+======
+* Fix hidden network indicator on startup [Florian; #772249]
+* Fix order of windows with modal dialogs in window switcher [Florian; #747153]
+* Fix feedback loop between StClipboard and X11 bridge [Carlos; #760745]
+* Reliably match windows from Flatpak apps [Florian; #772615]
+* Misc. bug fixes [Philip; #742249]
+
+Contributors:
+ Philip Chimento, Carlos Garnacho, Florian Müllner
+
+Translations:
+ Inaki Larranaga Murgoitio [eu], Khaled Hosny [ar], BM [uz@cyrillic],
+ Milo Casagrande [it], Cheng-Chia Tseng [zh_TW], gogo [hr]
+
+3.22.0
+======
+* Misc. bug fixes [Florian, Rui; #771391, #771536] #771656]
+
+Contributors:
+ Rui Matos, Florian Müllner
+
+Translations:
+ Ask Hjorth Larsen [da], GNOME Translation Robot [gd], Alexandre Franke [fr],
+ Daniel Korostil [uk], Jordi Mas [ca], Khaled Hosny [ar], David King [en_GB]
+
+3.21.92
+=======
+* Adjust screen capture to work with multiple stage views [Jonas; #770128]
+* Improve handling of cycle shortcuts [Florian; #771063]
+* Fix windows not getting undimmed in some cases [Rui; #770163, #752524]
+* Disable extension version check by default [Florian; #770887]
+* Misc. bug fixes [Rui, Florian, Michael; #770382, #770888, #770328]
+
+Contributors:
+ Jonas Ådahl, Michael Catanzaro, Fran Dieguez, Olivier Fourdan, Rui Matos,
+ Florian Müllner
+
+Translations:
+ Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Aurimas Černius [lt],
+ Muhammet Kara [tr], Trần Ngọc Quân [vi], A S Alam [pa], Yosef Or Boczko [he],
+ Anders Jonsson [sv], Tiago Santos [pt], Hannie Dumoleyn [nl],
+ Rūdolfs Mazurs [lv], Claude Paroz [fr], Arash Mousavi [fa],
+ Fran Dieguez [gl], Stas Solovey [ru], Tom Tryfonidis [el]
+
+3.21.91
+=======
+Translations:
+ Mario Blättermann [de], Jiri Grönroos [fi], Dušan Kazik [sk],
+ Andika Triwidada [id], Daniel Mustieles [es], Fabio Tomat [fur],
+ Enrico Nicoletto [pt_BR], Matej Urbančič [sl], Мирослав Николић [sr, sr@latin]
+
+3.21.90.1
+=========
+Contributors:
+ Piotr Drąg
+
+Translations:
+ Marek Černocký [cs], Balázs Úr [hu]
+
+3.21.90
+=======
+* Improve on-screen keyboard on wayland [Carlos; #765009]
+* Misc. bug fixes [Florian; #769156, #769216, #769074]
+
+Contributors:
+ Carlos Garnacho, Florian Müllner
+
+Translations:
+ Fabio Tomat [fur], Tiago Santos [pt], Daniel Mustieles [es],
+ Bernd Homuth [de], Aurimas Černius [lt], Balázs Úr [hu],
+ Yosef Or Boczko [he], Jiri Grönroos [fi], Marek Cernocky [cs],
+ Muhammet Kara [tr], Enrico Nicoletto [pt_BR], Andika Triwidada [id]
+
+3.21.4
+======
+* overview: Fix switching workspaces when scrolling on non-primary monitors
+ [Florian; #766883, #768316]
+* Fix crash when using screen recorder under wayland [Rui; #767001]
+* Update theme on video memory purge errors [Rui; #739178]
+* Free old backgrounds immediately [Hyungwon; #766353]
+* Add support for system upgrades to end session dialog [Kalev; #763611]
+* Fix maximized windows flickering to the wrong size on restart [Owen; #761566]
+* Hide ignored events in calendar as well [Florian; #768538]
+* calendar: Only hide dismissed occurrence of recurring event [Florian; #748226]
+* Provide org.freedesktop.impl.portal.access implementation [Florian; #768669]
+* Misc. bug fixes and cleanups [Rui, Florian, Marinus, Jonas; #767954, #768317,
+ #746867, #762206, #768956, #768979]
+
+Contributors:
+ Jonas Ådahl, Piotr Drąg, Hyungwon Hwang, Kalev Lember, Rui Matos,
+ Florian Müllner, Marinus Schraal, Owen W. Taylor
+
+Translations:
+ Andika Triwidada [id], Daniel Mustieles [es], Bruce Cowan [en_GB],
+ Dušan Kazik [sk], Piotr Drąg [pl], Chao-Hsiung Liao [zh_HK]
+
+3.21.3
+======
+ * Do not disable suspend action when locked [Florian; #725960]
+ * Remember input sources MRU list [Cosimo; #766826]
+ * networkAgent: Handle VPN service aliases [David; #658484]
+ * Plug a memory leak [Hans; #710230]
+
+Contributors:
+ Cosimo Cecchi, Florian Müllner, Hans Petter Jansson, David Woodhouse
+
+Translations:
+ Tiago Santos [pt], Cédric Valmary [oc], Muhammet Kara [tr],
+ Daniel Mustieles [es], Rafael Fontenelle [pt_BR]
+
+3.21.2
+======
+* Fix sorting of hidden apps in app switcher [Florian; #766238]
+* Set logind's LockedHint property when locked [Victor; #764773]
+* Allocate framebuffers early to fix a crash on NVIDIA [Martin; #764898]
+* Fix cycle-windows/cycle-group keybindings [Florian; #730739]
+* Switch to shared desktop schema for calendar settings [Iain; #766318]
+* Misc. bug fixes [Florian, Cosimo, Michele; #766325, #758471, #757556,
+ #757019, #766598]
+
+Contributors:
+ Cosimo Cecchi, Michele Gaio, Iain Lane, Florian Müllner, Martin Szulecki,
+ Victor Toso
+
+Translations:
+ Tiago Santos [pt], Kjartan Maraas [nb], Jiro Matsuzawa [ja],
+ Cédric Valmary [oc], Sveinn í Felli [is]
+
+3.21.1
+======
+* Save screencasts in HOME if XDG_VIDEO_DIR doesn't exist [Florian; #765015]
+* Don't show orientation lock when g-s-d won't rotate [Florian; #765267]
+* Misc. bug fixes [Heiher, Florian, Marek, Rui; #722752, #765061, #763068,
+ #765607, #757676, #760439]
+
+Contributors:
+ Heiher, Marek Chalupa, Rui Matos, Florian Müllner
+
+Translations:
+ Arash Mousavi [fa], Kristjan SCHMIDT [eo], GNOME Translation Robot [gd]
+
+3.20.1
+======
+* Plug a memory leak [Aaron; #735705]
+
+Contributors:
+ Aaron Plattner
+
+Translations:
+ Daniel Korostil [uk], Matej Urbančič [sl], Inaki Larranaga Murgoitio [eu],
+ Cheng-Chia Tseng [zh_TW], Fabio Tomat [fur], Trần Ngọc Quân [vi],
+ YunQiang Su [zh_CN], Marek Černocký [cs], Arash Mousavi [fa],
+ Alexander Shopov [bg], Khaled Hosny [ar]
+
+3.20.0
+======
+
+Translations:
+ Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Milo Casagrande [it],
+ Anders Jonsson [sv], Muhammet Kara [tr], Alexandre Franke [fr],
+ Rūdolfs Mazurs [lv], Ask Hjorth Larsen [da], Jiro Matsuzawa [ja]
+
+3.19.92
+=======
+* Update location dialog according to latest mockups [Zeeshan; #762480]
+* Fix deleting chat notifications in calendar [Florian; #747991]
+
+Contributors:
+ Zeeshan Ali (Khattak), Florian Müllner
+
+Translations:
+ Rūdolfs Mazurs [lv], Changwoo Ryu [ko], Matej Urbančič [sl],
+ Justin van Steijn [nl], Fabio Tomat [fur], Kris Thomsen [da],
+ Marek Černocký [cs], Piotr Drąg [pl], Dušan Kazik [sk],
+ Мирослав Николић [sr, sr@latin], Balázs Úr [hu], Yosef Or Boczko [he],
+ Daniel Mustieles [es], Fran Dieguez [gl], Bernd Homuth [de],
+ Tom Tryfonidis [el], Jiri Grönroos [fi], Gil Forcada [ca],
+ Artur Morais [pt_BR], Aurimas Černius [lt], Stas Solovey [ru]
+
+3.19.91
+=======
+* location: Ask user only once [Zeeshan; #762559]
+* Fix jiggling when auto-hiding legacy tray [Florian; #747957]
+* Misc. bug fixes [Florian, Michael, Ting-Wei; #762475, #762507, #755659]
+
+Contributors:
+ Zeeshan Ali (Khattak), Michael Catanzaro, Ting-Wei Lan, Florian Müllner
+
+Translations:
+ Мирослав Николић [sr, sr@latin], Piotr Drąg [pl], A S Alam [pa],
+ Artur de Aquino Morais [pt_BR], Daniel Mustieles [es],
+ Chao-Hsiung Liao [zh_TW], Daniel Korostil [uk], Fran Dieguez [gl],
+ Tom Tryfonidis [el], Bernd Homuth [de], Sebastian Rasmussen [sv],
+ Jordi Mas [ca], Piotr Drąg [ga], Cédric Valmary [oc], Gábor Kelemen [hu],
+ Baurzhan Muftakhidinov [kk], Friedel Wolff [af], Marek Černocký [cs],
+ Mingye Wang (Arthur2e5) [zh_CN], Aron Xu [zh_CN], Khaled Hosny [ar],
+ Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he]
+
+3.19.90
+=======
+* Correctly identify VPN secret requests [Lubomir; #760999]
+* Improve week number presentation [Jakub; #683245]
+* Add audio device selection dialog [Florian; #760284]
+* Add media controls to the time and date drop down [Florian; #756491]
+* Fix IBus candidate popup position under wayland [Rui; #753476]
+* Ask user to grant applications access to location [Zeeshan; #762119]
+* Misc. bug fixes [Mario, Jakub, Florian; #761208, #761772, #762270]
+
+Contributors:
+ Zeeshan Ali (Khattak), Michael Catanzaro, Rui Matos, Florian Müllner,
+ Lubomir Rintel, Mario Sanchez Prada, Jakub Steiner
+
+Translations:
+ Alexander Shopov [bg], Balázs Meskó [hu], Fabio Tomat [fur],
+ Dušan Kazik [sk], Piotr Drąg [pl], Alexandre Franke [fr],
+ Mario Blättermann [de], Milo Casagrande [it], Jordi Mas [ca]
+
+3.19.4
+======
+* gdm: Do not allow bypassing disabled Sign In button [Michael; #746180]
+* Style week numbers in calendar [Jakub; #683245]
+* Misc. bug fixes [Christophe, Jakub, Rui; #759708, #760577, #760945]
+
+Contributors:
+ Michael Catanzaro, Marek Černocký, Christophe Fergeau, Rui Matos,
+ Jakub Steiner
+
+Translations:
+ Aurimas Černius [lt], Enrico Nicoletto [pt_BR], Andika Triwidada [id],
+ Mario Blättermann [de], Marek Černocký [cs], Kjartan Maraas [nb],
+ Muhammet Kara [tr], Stas Solovey [ru]
+
+3.19.3
+======
+* Fix thumbnail scaling in window switcher on HiDPI [Florian; #758676]
+* Update animated backgrounds on timezone changes [Florian; #758939]
+* loginDialog: Update user list on user changes [Michael; #758568]
+* Fix touch interaction on wayland [Carlos; #756748]
+
+Contributors:
+ Michael Catanzaro, Carlos Garnacho, Kalev Lember, Florian Müllner
+
+Translations:
+ Daniel Korostil [uk], Muhammet Kara [tr], Dušan Kazik [sk],
+ Baurzhan Muftakhidinov [kk], Marek Černocký [cs]
+
+3.19.2
+======
+* Make gnome-shell DBus activatable [Ray; #741666]
+* Fix browser plugin crash in Firefox [Carlos; #737932, #757940]
+* Optionally show battery percentage in system status area [Bastien; #735771]
+* Misc. bug fixes [Kalev, Florian, Bastien; #757418, #757668, #757779, #757816,
+ #745626, #758220]
+
+Contributors:
+ Michael Biebl, Michael Catanzaro, Piotr Drąg, Carlos Garcia Campos,
+ Kalev Lember, Florian Müllner, Bastien Nocera, Ray Strode
+
+Translations:
+ Pedro Albuquerque [pt], liushuyu [zh_CN], Yosef Or Boczko [he],
+ Jiri Grönroos [fi], Kjartan Maraas [nb], GNOME Translation Robot [gd],
+ Daniel Mustieles [es], Marek Černocký [cs], Kristjan SCHMIDT [eo],
+ Stas Solovey [ru]
+
+3.19.1
+======
+* Respect text-scaling factor under wayland [Owen; #756447]
+* Show the Bluetooth submenu when there were setup devices [Bastien; #723848]
+* Misc. bug fixes [Florian, Cosimo, Rui, Ray, Owen, Jakub, Bastien;
+ #756697, #756714, #756605, #754814, #738942, #756983, #756925,
+ #757011, #673235, #757150]
+
+Contributors:
+ Cosimo Cecchi, Rui Matos, Florian Müllner, Bastien Nocera, Jakub Steiner,
+ Ray Strode, Owen W. Taylor
+
+Translations:
+ Kjartan Maraas [nb], Khaled Hosny [ar], Balázs Meskó [hu],
+ Daniel Șerbănescu [ro], Marek Černocký [cs]
+
+3.18.1
+======
+* Fix screen freezes when a notification is pushed [Carlos; #755425]
+* Fix overzealous ellipsization in system status menu [Adel, Florian; #708472]
+* Hide app menu when disabled by setting [Florian; #745919]
+* Fix lightbox effect when animations are disabled [Rui; #755827]
+* Do not mark hotplug notifications as critical [Florian; #657923]
+* Fix icons getting cut off in dash [Florian; #745649]
+* Animate fullscreen/unfullscreen operations [Cosimo; #707248]
+* Misc. bug fixes [Florian, Owen; #748919, #674799, #754581]
+
+Contributors:
+ Emmanuele Bassi, Michael Catanzaro, Cosimo Cecchi, Matthias Clasen,
+ Adel Gadllah, Carlos Garnacho, Ekaterina Gerasimova, Rui Matos,
+ Florian Müllner, Owen W. Taylor
+
+Translations:
+ Марко Костић [sr], Милош Поповић [sr@latin], Khaled Hosny [ar],
+ Trần Ngọc Quân [vi], Petr Kovar [cs], Alexandre Franke [fr],
+ Fran Dieguez [gl], Anders Jonsson [sv], Piotr Drąg [pl], Dušan Kazik [sk],
+ Milo Casagrande [it], Changwoo Ryu [ko], Stas Solovey [ru],
+ Rafael Fontenelle [pt_BR], Tom Tryfonidis [el], Aurimas Černius [lt],
+ Seán de Búrca [ga], Christian Kirbach [de], Jiri Grönroos [fi],
+ Pedro Albuquerque [pt], Baurzhan Muftakhidinov [kk], Daniel Mustieles [es],
+ Marek Černocký [cs], Ask Hjorth Larsen [da], Inaki Larranaga Murgoitio [eu]
+
+3.18.0
+======
+
+Translations:
+ Sendy Aditya Suryana [id], Kris Thomsen [da], Seán de Búrca [ga],
+ Andika Triwidada [id], Enrico Nicoletto [pt_BR], Anders Jonsson [sv],
+ Rūdolfs Mazurs [lv]
+
+3.17.92
+=======
+* Fix race when loading multiple background animations [Josselin; #741453]
+
+Contributors:
+ Michael Biebl, Josselin Mouette, Florian Müllner
+
+Translations:
+ Baurzhan Muftakhidinov [kk], Changwoo Ryu [ko], Christian Kirbach [de],
+ Kjartan Maraas [nb], Jiri Grönroos [fi], Arash Mousavi [fa],
+ Jiro Matsuzawa [ja], Marek Černocký [cs], Milo Casagrande [it]
+
+3.17.91
+=======
+* Fix login screen spinner causing wakeups while VT-switched away
+ [Ray, Rui; #753891]
+* Fix scrolling of user list on login screen [Florian; #754525]
+
+Contributors:
+ Piotr Drąg, Rui Matos, Florian Müllner, Ray Strode
+
+Translations:
+ Dušan Kazik [sk], Jordi Mas [ca], Aurimas Černius [lt], Stas Solovey [ru],
+ Piotr Drąg [pl], Pedro Albuquerque [pt], Daniel Mustieles [es],
+ Chao-Hsiung Liao [zh_TW], Muhammet Kara [tr], Fran Dieguez [gl],
+ Hannie Dumoleyn [nl], Yosef Or Boczko [he], Tom Tryfonidis [el],
+ A S Alam [pa], Balázs Úr [hu], Alexandre Franke [fr], Frédéric Péters [fr]
+
+3.17.90
+=======
+* Avoid caret/focus viewport changes during pointer movement [Rui; #752138]
+* Match GTK+'s modal dialogs for system modal dialogs [Carlos; #746108]
+* Refine message list style [Florian; #749958]
+* Fix type-ahead behavior for backspace and compose key [Rui; #753319, #753320]
+* Refine the system status menu [Florian; #751377]
+* Misc. bug fixes and cleanups [Bastien, Ray, Florian, Jakub; #752779, #752739,
+ #741366, #651503, #753064, #753181, #752881]
+
+Contributors:
+ Rui Matos, Florian Müllner, Bastien Nocera, Carlos Soriano, Jakub Steiner,
+ Ray Strode, Rico Tzschichholz
+
+Translations:
+ Marek Černocký [cs], Kjartan Maraas [nb], Jordi Mas [ca], Muhammet Kara [tr],
+ Enrico Nicoletto [pt_BR]
+
+3.17.4
+======
+* Fix fuzziness of app menu icon [Jakub; #747932]
+* Implement 4 finger swipe gesture for touchpads [Carlos; #752250]
+* Misc. bug fixes [Florian, Alexandre, Piotr, Ray, Mario; #751921, #659969,
+ #752438, #752675]
+
+Contributors:
+ Piotr Drąg, Alexandre Franke, Carlos Garnacho, Florian Müllner,
+ Mario Sanchez Prada, Jakub Steiner, Jasper St. Pierre, Ray Strode
+
+Translations:
+ Benjamin Steinwender [de], Pedro Albuquerque [pt], Fabio Tomat [fur],
+ Matej Urbančič [sl], Daniel Mustieles [es], Yosef Or Boczko [he],
+ Daniel Martinez [an]
+
+3.17.3
+======
+* Handle touch events in OSK on wayland [Rui; #750287]
+* Reinstate left/right movement to window menu [Ron; #751344]
+* Allow extensions to disable "Window is ready" notification [Adel; #748846]
+* Misc. bug fixes [Watson, Michael, Ray, Rui, Florian, Cosimo; #750465,
+ #751016, #751517, #750714, #751541, #751599]
+
+Contributors:
+ Michael Biebl, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
+ Ray Strode, Wim Taymans, Ron Yorston, Watson Yuuma Sato
+
+Translations:
+ Sebastian Rasmussen [sv], Dimitris Spingos [el], Muhammet Kara [tr],
+ Stas Solovey [ru], Benjamin Steinwender [de], Balázs Úr [hu],
+ Victor Ibragimov [tg], Dušan Kazik [sk], Pedro Albuquerque [pt]
+
+3.17.2
+======
+* Remove StTable widget [Florian; #703833]
+* Increase visibility of expanders in alt-tab popup [Jakub; #745058]
+* Ensure suspend inhibitors are released when VT switched away [Rui; #749228]
+* Use iio-sensor-proxy directly for orientation lock [Bastien; #749671]
+* Misc. bug fixes [Florian, Lan, Carlos; #749279, #749383, #749529, #749490,
+ #749742]
+
+Contributors:
+ Carlos Garnacho, Ting-Wei Lan, Rui Matos, Florian Müllner, Bastien Nocera,
+ Jakub Steiner
+
+Translations:
+ Yosef Or Boczko [he], sun [zh_CN], Felipe Braga [pt_BR],
+ Victor Ibragimov [tg], Gábor Kelemen [hu], Cédric Valmary [oc],
+ Dušan Kazik [sk], Kjartan Maraas [nb], Bruno Ramalhete [pt],
+ Matej Urbančič [sl], Daniel Mustieles [es]
+
+3.17.1
+======
+* Add Display Settings entry to background menu [Meet; #697346]
+* Add window menu option to move to different monitor [Isaac; #633994]
+* Improve switch style in default/highContrast themes [Jakub; #746294, #747912]
+* Make event highlight in calendar more prominent [Jakub; #747715]
+* Fix keyboard focus when focusing a notification banner [Florian; #747205]
+* Move notification banners below the dateMenu [Meet, Florian; #745910]
+* Misc. bug fixes [Mario, Rui; #748338, #748541]
+
+Contributors:
+ Isaac Ge, Rui Matos, Florian Müllner, Meet Parikh, Mario Sanchez Prada,
+ Jakub Steiner, Jasper St. Pierre
+
+Translations:
+ Sveinn í Felli [is], Marek Černocký [cs], laurent Soleil [oc]
+
+3.16.1
+======
+* gdm: Move long session chooser menus to the side [Florian; #734352]
+* Work around background corruption with NVIDIA driver [Rui; #739178]
+* Don't allow move-to-workspace for always-sticky windows [Florian; #746782]
+* Allow switching workspaces with PgUp/PgDown in overview [Devyani; #742581]
+* Bump time PAM messages are displayed [Sarvjeet; #720885]
+* Fix "stutter" when moving window past the last workspace [Shivam; #712778]
+* Fix blurred text on login screen [Clément; #746912]
+* keyboard: Restore whole MRU list after password mode [Rui; #746605]
+* Pass event timestamps when activating remote actions [Owen; #747323]
+* Fix hung login screen when password is typed too quickly [Shivam; #737586]
+* Make on-screen keyboard work for shell chrome on wayland [Rui; #747274]
+* Implement reexec_self() for FreeBSD [Ting-Wei; #747788]
+* Allow to dismiss resident notifications [Florian; #746860]
+* Temporarily reveal legacy tray when icons are added [Florian; #746025]
+* Make concealed tray smaller to minimize overlap with apps [Florian; #746787]
+* Misc. bug fixes [Florian, Rui, Giovanni; #746323, #746579, #746902, #746364,
+ #746509, #747636]
+
+Contributors:
+ Sarvjeet, Giovanni Campagna, Adel Gadllah, Clément Guérin, Devyani Kota,
+ Ting-Wei Lan, Rui Matos, Shivam Mishra, Florian Müllner, Owen W. Taylor
+
+Translations:
+ Khaled Hosny [ar], Dušan Kazik [sk], Yuri Myasoedov [ru], Stas Solovey [ru],
+ Hannie Dumoleyn [nl], Rūdolfs Mazurs [lv]
+
+3.16.0
+======
+* Revert erroneous login dialog changes [Ray; #740142]
+* Improve accessibility of legacy tray [Florian; #746487]
+* Fix legacy status icons leaking into other monitors [Florian; #745824]
+
+Contributors:
+ Florian Müllner, Ray Strode
+
+Translations:
+ Daniel Martinez [an], Sebastian Rasmussen [sv], Fran Dieguez [gl],
+ Andika Triwidada [id], Jordi Mas [ca], Kjartan Maraas [nb],
+ Inaki Larranaga Murgoitio [eu], Muhammet Kara [tr], Khaled Hosny [ar],
+ Bernd Homuth [de], Jiro Matsuzawa [ja]
+
+3.15.92
+=======
+* gdm: Fix user list accessibility [Florian; #729603]
+* Handle multiline questions in mount operations [Ross; #745713]
+* Improve classic theme [Jakub; #745686, #745687]
+* Fix ordering of calendar events [Florian; #745988]
+* Pick first input source for new windows when per-window [Rui; #746037]
+* networkAgent: Show a notification for non-user-initiated password requests
+ [Giovanni; #660293]
+* Fix dismissing calendar events [Florian; #744927]
+* Add legacy tray to ctrl-alt-tab popup [Florian; #746022]
+* Manage on-screen-keyboard visibility in gnome-shell [Carlos; #745977]
+* Add pointer barriers to legacy tray [Cosimo; #746026]
+* Use fallback when app icon cannot be resolved [Cosimo; #746219]
+* Fix handling of removed smartcard at startup [Ray; #740143]
+* gdm: Don't pick a random session for the user [Jasper; #740142]
+* Make menu selection behavior consistent with GTK [Florian; #745246]
+* gdm: Fix empty user list on user switching [Ray; #719418]
+* Misc bug fixes [Florian, Giovanni, Clément, Rui; #745666, #746019, #745861,
+ #746027, #746223, #737502, #746343, #746288]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Piotr Drąg, Adel Gadllah, Carlos Garnacho,
+ Clément Guérin, Ross Lagerwall, Rui Matos, Florian Müllner, Jakub Steiner,
+ Jasper St. Pierre, Ray Strode
+
+Translations:
+ Piotr Drąg [pl], Changwoo Ryu [ko], Milo Casagrande [it],
+ Baurzhan Muftakhidinov [kk], Мирослав Николић [sr, sr@latin], Balázs Úr [hu],
+ IWAI, Masaharu [ja], Daniel Korostil [uk], Aurimas Černius [lt],
+ Matej Urbančič [sl], Daniel Mustieles [es], Kjartan Maraas [nb],
+ Victor Ibragimov [tg], Claude Paroz [fr], Jordi Mas [ca], Bernd Homuth [de],
+ Muhammet Kara [tr], Frédéric Péters [fr], Jiri Grönroos [fi],
+ Alexander Shopov [bg], Stas Solovey [ru], Trần Ngọc Quân [vi],
+ Samir Ribic [bs], Dušan Kazik [sk], Enrico Nicoletto [pt_BR],
+ Marek Černocký [cs], A S Alam [pa], Ask Hjorth Larsen [da],
+ Tom Tryfonidis [el], Alexandre Franke [fr], Yosef Or Boczko [he],
+ Chao-Hsiung Liao [zh_TW]
+
+3.15.91
+=======
+* Don't disable all shortcuts while non-panel menus are open [Florian; #745039]
+* Do not wake up the screen for disabled notifications [Florian; #744114]
+* Add unminimize animation [Florian; #702662]
+* Change default shortcut for viewing notifications [Florian; #687986]
+* Add shortcut to dismiss notifications from list [Florian; #745279]
+* Use Polari instead of Empathy for IRC conversations [Giovanni; #745431]
+* Add a hideable bottom tray for legacy status icons [Florian; #745162]
+* Improve accessibility of calendar/message list [Florian; #706903, #745393]
+* Fix window thumbnail scaling in overview [Sebastian; #744883]
+* Stop handling non-chat notifications for Empathy [Giovanni; #745503]
+* Fix mouse interaction with ibus candidate window [Rui; #745167, #745245]
+* Misc bug fixes and cleanups [Giovanni, Florian, Miguel, Cosimo, Jakub,
+ Michele, Raul, Matthias, Rui; #677412, #744880, #744900, #744894, #744907,
+ #745061, #741111, #744912, #745175, #745246, #744959, #744575, #745570,
+ #652742, #659187, #745412, #745521, #745521, #741665, #745574, #745627,
+ #745494, #745111, #745132, #745110]
+
+Contributors:
+ Michele, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
+ Raul Gutierrez Segales, Sebastian Keller, David Liang, Rui Matos,
+ Florian Müllner, Yosef Or Boczko, Jakub Steiner, Jasper St. Pierre,
+ Rico Tzschichholz, Miguel Vaello Martínez
+
+Translations:
+ Dušan Kazik [sk], Chao-Hsiung Liao [zh_TW], Muhammet Kara [tr],
+ Balázs Úr [hu], Daniel Mustieles [es], Frédéric Péters [fr],
+ Rafael Ferreira [pt_BR], Aurimas Černius [lt], Milo Casagrande [it],
+ Yosef Or Boczko [he], Stas Solovey [ru], Baurzhan Muftakhidinov [kk],
+ Daniel Korostil [uk], Fran Dieguez [gl], Kjartan Maraas [nb],
+ Tom Tryfonidis [el], Cheng-Chia Tseng [zh_TW], Friedel Wolff [af],
+ Alexandre Franke [fr], Efstathios Iosifidis [el], Aron Xu [zh_CN],
+ Marek Černocký [cs], Enrico Nicoletto [pt_BR]
+
+3.15.90
+=======
+* gdm: Fix reactivity of first user in user list [Ray; #743370]
+* Prevent DND to dash when favorites are locked down [David; #741325]
+* Prevent DND in app picker when favorites are locked down [Murray; #741325]
+* Implement Display panel's OSD monitor labels in the shell [Rui; #743744]
+* Remove GSystem dependency [Colin; #744457]
+* Do not duplicate "New Window" action in dash [Florian; #744446]
+* Fix 'draw-cursor' option of screen recorder [Alexander; #744599]
+* Fix screencast timestamps [Sebastian; #744642]
+* Allow keybindings for switching to VT8-VT12 [Ray; #744800]
+* Implement notification redesign [Florian; #744498, #744815, #744817, #744850]
+* Fix struts computation of chrome on non-primary monitors [Sylvain; #744183]
+* Visual refresh based on GTK+ theme redesign [Jakub, Carlos; #737785, #744680]
+* Misc. bug fixes and cleanups [Ray, Bastien, Cosimo, Rui, Florian; #743371,
+ #736182, #744013, #743993, #744665]
+
+Contributors:
+ Cosimo Cecchi, Murray Cumming, Sebastian Dröge, David King,
+ Alexander Larsson, Rui Matos, Florian Müllner, Bastien Nocera,
+ Sylvain Pasche, Carlos Soriano, Jakub Steiner, Jasper St. Pierre, Ray Strode,
+ Colin Walters
+
+Translations:
+ Daniel Martinez [an], Sveinn í Felli [is], GNOME Translation Robot [de],
+ Yosef Or Boczko [he], Kristjan SCHMIDT [eo], Dušan Kazik [sk],
+ Andika Triwidada [id], Marek Černocký [cs], Muhammet Kara [tr],
+ Matej Urbančič [sl], Milo Casagrande [it], Anders Jonsson [sv]
+
+3.15.4
+======
+* Add mode parameter to AcceleratorActivated signal [Florian; #711682]
+* Fix PID based window/app association [Sebastian; #736527]
+* Fix current day highlight on day change [Sebastian; #742492]
+* Switch to vp9 for screencast recordings [Adel; #742744]
+* Disable IBus input sources on password entries [Takao; #730628]
+* Make slider scrolling smoother [Adel; #742648]
+* Allow move-up shortcut to move window above top workspace [Florian; #665764]
+* Misc. bug fixes [Adel, Rui; #742748, #742824, #741114]
+
+Contributors:
+ Cosimo Cecchi, Takao Fujiwara, Adel Gadllah, Sebastian Keller, Rui Matos,
+ Florian Müllner, Jasper St. Pierre
+
+Translations:
+ Andika Triwidada [id], Matej Urbančič [sl], Saibal Ray [bn_IN],
+ Inaki Larranaga Murgoitio [eu], Stas Solovey [ru], Kjartan Maraas [nb],
+ Balázs Úr [hu], Marek Černocký [cs], Rafael Ferreira [pt_BR],
+ Bernd Homuth [de], Daniel Mustieles [es], Fabio Tomat [fur]
+
+3.15.3
+======
+* Add support for high-contrast themes [Florian; #740447]
+* Fix banner message on login screen without user list [Ray; #703972]
+* Fix flicker when activating windows on another workspace [Florian; #741680]
+* Misc. bug fixes [Giovanni, Florian; #735308, #740237]
+
+Contributors:
+ Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
+
+Translations:
+ Balázs Úr [hu], Josef Andersson [sv], Muhammet Kara [tr],
+ Baurzhan Muftakhidinov [kk], Inaki Larranaga Murgoitio [eu]
+
+3.15.2
+======
+* Fix visual glitch of window preview outline in overview [Chris; #699044]
+* Change user facing name of "Captive Portal" to "Network Login" [Elad; #737198]
+* Port to Python 3 [Slavek; #732478]
+* Hide Airplane mode indicator when g-s-d says so [Cosimo; #736292]
+* Allow translators to change non-work days [Lavi; #664645]
+* Delay invocation of caribou daemon until really needed [Daiki; #739712]
+* Don't lock screen after crash if locking is disabled [Adel; #704884]
+* Improve layout of extension installation dialog [William; #739888]
+* Fix workspace changes from app picker [Yuki; #737534]
+* Preload all ibus input sources in user configuration [Takao; #695428]
+* Properly remove network connections from list [Ryan; #740227]
+* Support CSS margin property [Carlos; #728437]
+* Improve handling of BUSY application state [Phillip; #736492]
+* Fix erroneous week numbers in calendar [Florian; #736722]
+* Misc. bugfixes and cleanups [Darcy, Yuki, Alexander, Eskild, Bastien, Cosimo, Colin,
+ Ray; #738725, #739497, #739241, #672500, #739822, #740074, #704163, #740141]
+
+Contributors:
+ Yuki, Lavi .A, Elad Alfassa, Cosimo Cecchi, Takao Fujiwara, Adel Gadllah,
+ Eskild Hustvedt, Chris Johns, William Jon McCann, Slavek Kabrda, Ryan Lortie,
+ Florian Müllner, Bastien Nocera, Christian Persch, Carlos Soriano,
+ Jasper St. Pierre, Ray Strode, Alexander Tsoy, Daiki Ueno, Colin Walters,
+ Phillip Wood
+
+Translations:
+ Jorge Perez Perez [an], Daniel Martinez [an], Daniel Mustieles [es],
+ Trần Ngọc Quân [vi], Changwoo Ryu [ko], Kjartan Maraas [nb],
+ Yosef Or Boczko [he], Marek Černocký [cs]
+
+3.15.1
+======
+* Use GResources for theme loading [Cosimo; #736936]
+* Reset the OSK to primary on monitor changes [Rui; #738536]
+* Use LC_TIME locale for format string translations [Florian; #738640]
+* Summarize queued up notifications [Devyani; #702460]
+* Improve handling of multi-day events [Andreas; #727302]
+* Support EXTERNAL scroll policy type [Florian; #739379]
+* Misc. bugfixes [Owen, Rui; #738652, #739252]
+
+Contributors:
+ Andreas Brauchli, Cosimo Cecchi, Devyani Kota, Rui Matos, Florian Müllner,
+ Jasper St. Pierre, Owen W. Taylor
+
+Translations:
+ Bahodir Mansurov [uz@cyrillic]
+
+3.14.1
+======
+* Fix pulse animation for scrolled app folders [Florian; #736885]
+* Fix updating background on file changes [Owen; #710756]
+* Obtain keyboard variant from IBus [Jinkyu; #735066]
+* Implement Ctrl-u / Ctrl-k keybindings in entries [Florian; #737346]
+* Pass VPN hints to auth dialog [Dan; #737592]
+* Only allow one screenshot request at a time [Adel; #737456]
+* Respect disable-save-to-disc lockdown setting [Florian; #737846]
+* Respect scaling-factor for profile pictures [Darcy; #735419]
+* Focus login screen after lifting the lock screen shield [Ray; #708105]
+* Speed up pulse animation for few items [Carlos S.; #737017]
+* Fix gap between workspace switcher and screen edge [Florian; #728899]
+* Disable unredirection during recordings [Adel; #738226]
+* Ensure there's always at least one input source [Rui; #738303]
+* Restrict width of dash icons' context menus [Adel; #738054]
+* Misc. bug fixes [Jasper, Florian, Carlos G., Owen; #736999, #737382, #737001,
+ #738314, #738256, #738147]
+
+Contributors:
+ Darcy Beurle, Cosimo Cecchi, Adel Gadllah, Carlos Garnacho, Rui Matos,
+ Florian Müllner, Carlos Soriano, Jasper St. Pierre, Ray Strode, Patrick Ward,
+ Dan Williams, Owen W. Taylor, Jinkyu Yi
+
+Translations:
+ Мирослав Николић po/sr, sr@latin.po, Fran Diéguez [gl], Marek Černocký [cs],
+ Saibal Ray [bn_IN], Rajesh Ranjan [hi], Friedel Wolff [af],
+ Zhou Fang [zh_CN], Krishnababu Krothapalli [te], Kjartan Maraas [nb],
+ Rūdolfs Mazurs [lv], Sweta Kothari [gu], Christian Kirbach [de],
+ Cheng-Chia Tseng [zh_TW], Pedro Albuquerque [pt], Daniel Mustieles [es],
+ Luca Ferretti [it], Baurzhan Muftakhidinov [kk], Arash Mousavi [fa],
+ Milo Casagrande [it]
+
+3.14.0
+======
+* Fix exposure of the accessible tree [Alejandro; #736821]
+* Hide empty app folders in app picker [Florian; #736910]
+
+Contributors:
+ Florian Müllner, Alejandro Piñeiro
+
+Translations:
+ Yuri Myasoedov [ru], Pawan Chitrakar [ne], Manoj Kumar Giri [or],
+ Daniel Mustieles [es], GNOME Translation Robot [de], Rajesh Ranjan [hi],
+ Shankar Prasad [kn], Kenneth Nielsen [da], Daniel Korostil [uk],
+ Changwoo Ryu [ko], A S Alam [pa], Tom Tryfonidis [el], Petr Kovar [cs]
+
+3.13.92
+=======
+* Fix submenu arrow animations [Hashem; #728927]
+* Always initialize clutter accessibility [Alejandro; #735908]
+* Adapt to mutter background changes [Owen; #735638]
+* Improve handling of outOfDate extensions in prefs [Florian; #736185]
+* Port offline updates to PackageKit's DBus interface [Kalev; #736337]
+* location: Translate accuracy levels for geoclue [Zeeshan; #736479]
+* Implement input source switching [Rui; #736435]
+* Fix crash when dragging window from workspace switcher [Carlos G.; #735972]
+* Clean out list of default favorites [Elad; #735682]
+* Add settings link to location submenu [Florian; #736542]
+* Fix keynav of message tray menu button [Florian; #707799]
+* Misc. bug fixes [Carlos G., Florian, Rui; #736110, #736329, #736343,
+ #735927, #735976]
+
+Contributors:
+ Elad Alfassa, Zeeshan Ali (Khattak), Michael Catanzaro, Adel Gadllah,
+ Carlos Garnacho, Kalev Lember, Rui Matos, Florian Müllner, Hashem Nasarat,
+ Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
+
+Translations:
+ Piotr Drąg [pl], Changwoo Ryu [ko], Yuri Myasoedov [ru], Zhou Fang [ja],
+ Peter Mráz [sk], Ville-Pekka Vainio [fi], Sweta Kothari [gu],
+ Marek Černocký [cs], A S Alam [pa], Christian Kirbach [de],
+ Alexandre Franke [fr], Aurimas Černius [lt], Khaled Hosny [ar],
+ Enrico Nicoletto [pt_BR], Andika Triwidada [id], Shantha kumar [ta],
+ Matej Urbančič [sl], Pawan Chitrakar [ne], Yosef Or Boczko [he],
+ Balázs Úr [hu], Dušan Kazik [sk], Gil Forcada [ca],
+ Carles Ferrando [ca@valencia], Nilamdyuti Goswami [as], Ivaylo Valkov [bg],
+ Sandeep Sheshrao Shedmake [mr], Umarzuki Bin Mochlis Moktar [ms],
+ Muhammet Kara [tr], Jiro Matsuzawa [ja], Kris Thomsen [da],
+ Mattias Eriksson [sv]
+
+3.13.91
+=======
+* Fix keynav into session menu on login screen [Florian; #735614]
+* Add gesture to summon message tray [Carlos G.; #735625]
+* Accept scrolls/swipes either way on the screen shield [Jasper; #734874]
+* Animate opening the app picker and improve app folder animations
+ [Carlos S.; #734726]
+* Animate app icons on launching a new window [Carlos S., Florian; #734726]
+* Show the on-screen keyboard when touch input is being used [David; #702015]
+* Misc. bug fixes [Bastien, Owen, Florian, Carlos G.; #735190, #735385,
+ #735608, #735681]
+
+Contributors:
+ Carlos Garnacho, David King, Kalev Lember, Florian Müllner, Bastien Nocera,
+ Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
+
+Translations:
+ Fran Diéguez [gl], Sweta Kothari [gu], Manoj Kumar Giri [or],
+ Alain Lojewski [fr], Praveen Illa [te], Arash Mousavi [fa],
+ Andika Triwidada [id]
+
+3.13.90
+=======
+* Make use of GLSL optional [Adel; #733623]
+* Update on-screen-keyboard position on monitor changes [Cosimo; #733790]
+* Improve window manager animations [Giovanni; #732857]
+* Handle touch events [Carlos G.; #733633]
+* Try to not show "New Window" action for single-window apps
+ [Giovanni; #722554]
+* Fix overview exceeding screen size with many apps installed
+ [Carlos S.; #723496]
+* Add Software to default favorites [Mathieu; #734406]
+* Improve app picker <-> desktop transition [Carlos S.; #732901]
+* Remove <shift>-magic for switcher popups [Christophe; #732296]
+* Add a special background to use for performance testing [Owen; #734610]
+* Add support for default disabled search providers [Giovanni; #734110]
+* Fix portals that don't redirect properly [Giovanni; #733848]
+* Fix history trimming in chat notifications [Giovanni; #733899]
+* Try to use default calendar application [Florian; #722333]
+* Only show location menu when geolocation is in use [Zeeshan; #731122]
+* Misc. bug fixes and cleanups [Giovanni, Carlos G., Zeeshan, Carlos S.,
+ Cosimo; #711682, #733840, #734483, #734680, #733813, #735062]
+
+Contributors:
+ Zeeshan Ali (Khattak), Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi,
+ Piotr Drąg, Christophe Fergeau, Adel Gadllah, Carlos Garnacho,
+ Florian Müllner, Carlos Soriano, Jasper St. Pierre, Olav Vitters,
+ Owen W. Taylor
+
+Translations:
+ Aurimas Černius [lt], MarMav [el], Inaki Larranaga Murgoitio [eu],
+ Reinout van Schouwen [nl], ngoswami [as], Fabio Tomat [fur],
+ Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.13.4
+======
+* Handle portal login requests [Giovanni; #704416]
+* Scale fonts on wayland on hiDPI devices [Adel; #732537]
+* Fix default ibus candidate index labels [Rui; #702944]
+* Add gestures for various system actions [Carlos G.]
+* Add performance test script for the perf.gnome.org [Owen; #732350]
+* Use new restart framework to improve restart visuals [Owen; #733026]
+* Improve keynav in app folder popups [Carlos S.; #731477]
+* Fix truncation of app search results [Carlos S.; #732416]
+* Automatically update renamed desktop files in favorites [Kalev; #729429]
+* Misc. bug fixes and cleanups [Giovanni, Yosef, Owen, Bastien, Javier;
+ #729823, #726401, #732301, #732348, #732349, #733498, #733540]
+
+Contributors:
+ Giovanni Campagna, Adel Gadllah, Carlos Garnacho, Javier Hernández,
+ Kalev Lember, Rui Matos, Florian Müllner, Bastien Nocera, Yosef Or Boczko,
+ Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
+
+Translations:
+ Yuri Myasoedov [ru], Daniel Mustieles [es], Fran Diéguez [gl],
+ Cheng-Chia Tseng [zh_TW], A S Alam [pa], Benjamin Steinwender [de],
+ Enrico Nicoletto [pt_BR], MarMav [el], Yosef Or Boczko [he],
+ Kjartan Maraas [nb]
+
+3.13.3
+======
+* Don't allow closing windows with attached modals [Florian; #729886]
+* Fix self-restarting on OpenBSD [Antoine; #727763]
+* Improve behavior of window buttons with compositor menus [Florian; #731058]
+* Work around atspi-related performance regression [Alejandro; #730118]
+* Misc bug fixes and cleanups [Florian, Lan, Jasper, Christophe, Debarshi,
+ Zeeshan; #728271, #726460, #703833, #731118, #731220, #695487, #730527,
+ #728170, #731619, #731738, #731882, #731923]
+
+Contributors:
+ Zeeshan Ali (Khattak), Christophe Fergeau, Adel Gadllah, Antoine Jacoutot,
+ Ting-Wei Lan, Florian Müllner, Alejandro Piñeiro, Debarshi Ray,
+ Carlos Soriano, Jasper St. Pierre, Wim Taymans, Rico Tzschichholz
+
+Translations:
+ Philip Withnall [en_GB], Milo Casagrande [it], Aurimas Černius [lt],
+ Enrico Nicoletto [pt_BR], Kjartan Maraas [nb], Balázs Meskó [hu],
+ Muhammet Kara [tr], Daniel Mustieles [es], Yosef Or Boczko [he],
+ Matej Urbančič [sl], Mattias Eriksson [sv]
+
+3.13.2
+======
+* Make airplane mode menu insensitive in lock screen [Giovanni; #729224]
+* Don't always extend struts to the screen edge [Florian; #683819]
+* Fix keynav for alternatives in AltSwitcher [Florian; #727259]
+* Implement window menus in the shell [Jasper; #726352]
+* Support resource:/// URLs in GNOME_SHELL_JS envvar [Owen; #730409]
+* Fix switcher popups with keybindings containing Escape [Rui; #730739]
+* Update extension-prefs UI to follow GNOME 3 patterns [Florian; #730829]
+* Add support for fallback app menu in window decorations [Florian; #730752]
+* Fix keynav escaping open app folders [Florian; #726760]
+* Misc. bug fixes [Kalev, Florian, Owen; #729429, #728449, #730408, #730753,
+ #730653]
+
+Contributors:
+ Giovanni Campagna, Piotr Drąg, Kalev Lember, Rui Matos, Florian Müllner,
+ Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
+
+Translations:
+ Ihar Hrachyshka [be], Giovanni Campagna [it], Carles Ferrando [ca@valencia],
+ Daniel Mustieles [es], Aurimas Černius [lt], Enrico Nicoletto [pt_BR],
+ Yosef Or Boczko [he], Marek Černocký [cs], Muhammet Kara [tr],
+ Georges Neto [pt_BR], Andika Triwidada [id]
+
+3.13.1
+======
+* Ensure the currently focused app icon is viewable [Rui; #726759]
+* Improve language in location menu [Zeeshan; #726498]
+* Improve HiDpi support [Cosimo; #726907]
+* Set accessible role for window previews [Alejandro; #726670]
+* Fix bad antialiasing on panel menu buttons [Carlos; #727336]
+* Don't hide location menu [Zeeshan; #727398]
+* Fix IM candidate window obscuring current text [Rui; #727579]
+* Don't always extend struts to the screen edge [Florian; #663690]
+* Add shortcuts for switching to the last workspace [Elad; #659288]
+* Show OSD window on all monitors [Adel; #722684]
+* Improve consistency of labels in network menu [Paul; #727163]
+* Fix zombie search providers showing up [Jasper; #728597]
+* Remove ConsoleKit support [Florian; #686626]
+* Fix region screenshots with open shell menus [Florian; #709126]
+* Support <shift>insert in text entries [Florian; #648318]
+* Improve app picker scrolling on touch [Jasper; #729064]
+* Don't make date button clickable when on current date [Carlos; #726724]
+* Tweak heuristic for hiding workspace switcher [Florian; #662457]
+* Add option to show in Software to app context menu [Matthias; #643043]
+* Misc. bug fixes and cleanups [Bastien, Florian, Giovanni, Adel, Vadim,
+ Carlos; #727983, #727948, #728512, #728681, #728897, #727384, #728820,
+ #715042, #728449, #728343]
+
+Contributors:
+ Elad Alfassa, Zeeshan Ali (Khattak), Giovanni Campagna, Cosimo Cecchi,
+ Matthias Clasen, Piotr Drąg, Adel Gadllah, Paul Lange, Rui Matos,
+ Simon McVittie, Florian Müllner, Bastien Nocera, Alejandro Piñeiro,
+ Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre
+
+Translations:
+ Khaled Hosny [ar], Piotr Drąg [pl], Yosef Or Boczko [he],
+ Antonio Fernandes C. Neto [pt_BR], Marek Černocký [cs], maria thukididu [el],
+ Andika Triwidada [id], Daniel Mustieles [es], Changwoo Ryu [ko],
+ Benjamin Steinwender [de], Sphinx Jiang [zh_CN],
+ Inaki Larranaga Murgoitio [eu], Marcus Lundblad [sv], Aurimas Černius [lt],
+ Stas Solovey [ru], Alexandre Franke [fr], Matej Urbančič [sl],
+ Fran Diéguez [gl], Pau Iranzo [ca], Luca Ferretti [it], Milo Casagrande [it],
+ Tiago S [pt], Victor Ibragimov [tg], Dirgita [id], Khoem Sokhem [km],
+ Rūdolfs Mazurs [lv], Balázs Úr [hu], Ask H. Larsen [da], Ikuya Awashiro [ja],
+ Wouter Bolsterlee [nl], Daniel Korostil [uk], Daniel Șerbănescu [ro],
+ Enrico Nicoletto [pt_BR]
+
+3.12.0
+======
+* gdm: Reset greeter when coming back to login screen [Jasper; #726989]
+
+Contributors:
+ Jasper St. Pierre
+
+Translations:
+ Daniel Martinez [an], Yuri Myasoedov [ru], Inaki Larranaga Murgoitio [eu],
+ Abderrahim Kitouni [ar], Praveen Illa [te], Matej Urbančič [sl],
+ Chao-Hsiung Liao [zh_HK, zh_TW], Frédéric Péters [fr],
+ Мирослав Николић [sr, sr@latin], Ask H. Larsen [da], Kenneth Nielsen [da],
+ Jiro Matsuzawa [ja], Dušan Kazik [sk]
+
+3.11.92
+=======
+* calendar: Grab key focus after changing day [Volker; #725606]
+* gdm: Don't load user list if disabled [Florian; #725905]
+* Don't show network-offline in the top bar [Jasper; #725340]
+* Improve radial shade effect of modal dialogs [Giovanni; #725830]
+* Fix broken suspend-on-idle functionality [Giovanni; #712706]
+* Close wifi selection dialog when device disappears [Giovanni; #723935]
+* Don't close chats when pressing Escape [Giovanni; #724178]
+* Improve smartcard support in login/lock screen [Ray; #726262, #726263]
+* Wake up screen when resuming from suspend [Giovanni; #726378]
+* Make bluetooth and location items insensitive when locked [Florian; #726319]
+* Don't show bluetooth icon when there is no adapter [Giovanni; #725057]
+* Make sure to keep the OSK on top of modal dialogs [Rui; #719451]
+* Misc. bug fixes and cleanups [Giovanni, Ray, Adel, Daniel, Jasper, Florian;
+ #725832, #725958, #722149, #724977, #724798, #725020, #723976, #726119,
+ #726238, #585500, #704844, #726323, #726322, #726120, #726414]
+
+Contributors:
+ Giovanni Campagna, Daniel Drake, Adel Gadllah, Rui Matos, Florian Müllner,
+ Volker Sobek, Jasper St. Pierre, Ray Strode
+
+Translations:
+ Fabio Tomat [fur], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
+ Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Andika Triwidada [id],
+ A S Alam [pa], Rūdolfs Mazurs [lv], Wylmer Wang [zh_CN],
+ Aurimas Černius [lt], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
+ Tiagosdot [pt], Benjamin Steinwender [de], Frédéric Peters [fr],
+ Daniel Korostil [uk], Yaron Shahrabani [he], Ville-Pekka Vainio [fi],
+ maria thukididu [el], Victor Ibragimov [tg], Kjartan Maraas [nb],
+ Gábor Kelemen [hu], Ask H. Larsen [da]
+
+3.11.91
+=======
+* Don't use network profile name in menu [Giovanni; #725586]
+* calendar: Make date label clickable to return to current date [Vit; #641366]
+* Misc. bug fixes [Florian, Zeeshan, Adel, Jasper, Dan, Volker; #724813,
+ #724686, #725082, #724870, #724779, #725533]
+
+Contributors:
+ Zeeshan Ali (Khattak), Giovanni Campagna, Piotr Drąg, Adel Gadllah,
+ Florian Müllner, Volker Sobek, Vit Stanislav, Jasper St. Pierre, Dan Williams
+
+Translations:
+ Victor Ibragimov [tg], Aurimas Černius [lt], Dimitris Spingos [el],
+ Andika Triwidada [id], Rafael Ferreira [pt_BR], Daniel Mustieles [es],
+ Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Ihar Hrachyshka [be],
+ eternalhui [zh_CN], Yosef Or Boczko [he], Fran Diéguez [gl],
+ Khaled Hosny [ar], Ville-Pekka Vainio [fi], Piotr Drąg [pl],
+ Kjartan Maraas [nb], Changwoo Ryu [ko]
+
+3.11.90
+=======
+* Stop showing two bluetooth entries [Giovanni; #709353]
+* Improve styling of login/lock screen [Reda; #723833]
+* Fix magnifier crosshairs [Magdalen; #723709]
+* Make NetworkManager support optional [Michael; #669495]
+* Make middle-click open a new instance [Florian; #695010]
+* Scale the UI on high resolution displays [Cosimo, Adel; #705410, #724607]
+* Remove notification counter on screen shield [Carlos; #709275]
+* Improve app picker transition [Carlos; #722331]
+* Add geolocation indicator to status menu [Zeeshan; #723684]
+* Improve timestamps in chat notifications [Carlos; #708031, #715158]
+* Improve network menus [Giovanni; #723570]
+* Add "VPN Setting" item to VPN submenu [Giovanni; #709167]
+* Improve appearance of disclosure arrows [Carlos; #720206]
+* Add GSetting key to disable version validation of extensions [Adel; #724683]
+* Delay auto-removing empty workspaces [Florian; #709064]
+* Offer offline updates in the shutdown dialog [Kalev; #722898]
+* Animate tile previews [Florian; #665758]
+* Misc. bug fixes and cleanups [Giovanni, Ryan, Debarshi, Florian; #709128,
+ #722342, #723661, #724184, #724256, #724293, #724305, #722554, #724282,
+ #724690, #722928]
+
+Contributors:
+ Zeeshan Ali (Khattak), Magdalen Berns, Michael Biebl, Giovanni Campagna,
+ Cosimo Cecchi, Adel Gadllah, Reda Lazri, Kalev Lember, Ryan Lortie,
+ Florian Müllner, Debarshi Ray, Carlos Soriano, Jasper St. Pierre,
+ Colin Walters
+
+Translations:
+ Victor Ibragimov [tg], Daniel Mustieles [es], Khaled Hosny [ar],
+ Enrico Nicoletto [pt_BR], Yosef Or Boczko [he], Fran Diéguez [gl],
+ Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Jorge Pérez Pérez [an],
+ Kjartan Maraas [nb], David Lüder [de], Daniel Korostil [uk], ngoswami [as],
+ Rafael Ferreira [pt_BR]
+
+3.11.5
+======
+* Fix extension preference tool [Florian; #722334]
+* Fix keyboard activation of legacy tray icons [Giovanni; #721267]
+* Add radial background shade for modal dialogs [Giovanni; #669798]
+* Show attached modal windows in the overview [Giovanni; #650843]
+* Add support for desktop actions [Giovanni; #669603]
+* Indicate in system status when location service is used [Zeeshan; #709372]
+* Add support for extended app folder schema [Jasper; #723179]
+* Show status icon for wired network connections [Jasper; #708966]
+* Indicate airplane mode in network selection dialog [Giovanni; #709128]
+* Misc bug fixes and cleanups [Florian, Sebastian, Giovanni, Tim, Matt, Jasper;
+ #722417, #722494, #722547, #722593, #722434, #722787, #722690, #722840,
+ #722660, #722812, #723197, #722927, #723306, #723308, #723523, #709685,
+ #723570]
+
+Contributors:
+ Zeeshan Ali (Khattak), Magdalen Berns, Giovanni Campagna, William Jon McCann,
+ Sebastian Keller, Tim Lunn, Florian Müllner, Carlos Soriano,
+ Jasper St. Pierre, Rico Tzschichholz, Matt Watson
+
+Translations:
+ Marek Černocký [cs], Mattias Põldaru [et], Tong Hui [zh_CN],
+ Victor Ibragimov [tg], Enrico Nicoletto [pt_BR], Daniel Mustieles [es],
+ Fran Diéguez [gl], Kjartan Maraas [nb], Nilamdyuti Goswami [as],
+ Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he],
+ Jorge Pérez Pérez [an], Dimitris Spingos [el], Baurzhan Muftakhidinov [kk],
+ Chao-Hsiung Liao [zh_HK, zh_TW], Shankar Prasad [kn], Yaron Shahrabani [he],
+ Andika Triwidada [id]
+
+3.11.4
+======
+* Fix removal of workspacaes that are not at the end [Giovanni; #721417]
+* Allow session mode to be specified in the environment [Ray; #720894]
+* Special-case launching of terminals [Debarshi; #695010]
+* Always show arrow if app switcher is scrollable [Jonh; #711467]
+* Implement new app folders system [Jasper; #722117]
+* Remove arrow from background menu [Tarun; #699608]
+* Misc bug fixes and cleanups [Giovanni, Andika, Florian, Ray; #721039,
+ #721439, #721507, #721629, #721868, #722210]
+
+Contributors:
+ Giovanni Campagna, Piotr Drąg, Tarun Kumar Joshi, Florian Müllner,
+ Debarshi Ray, Jasper St. Pierre, Ray Strode, Andika Triwidada, Jonh Wendell
+
+Translations:
+ Dušan Kazik [sk], Tong Hui [zh_CN], Benjamin Steinwender [de],
+ Matej Urbančič [sl], Jorge Pérez Pérez [an], Kjartan Maraas [nb],
+ Milo Casagrande [it], Rafael Ferreira [pt_BR], Marek Černocký [cs],
+ Daniel Mustieles [es], Adorilson Bezerra [pt_BR], Christian Kirbach [de],
+ Aurimas Černius [lt], Andika Triwidada [id], Baurzhan Muftakhidinov [kk],
+ Victor Ibragimov [tg], Yosef Or Boczko [he], Dimitris Spingos [el],
+ Fran Diéguez [gl]
+
+3.11.3
+======
+* Fix fade effect of desktop icons [Florian; #707671]
+* Fix issues with background management code [Jasper; #709313]
+* Use new Glib facilities for application search [Jasper; #711631]
+* Add focus indication to session menu button [Sebastien; #710539]
+* Fix hover tracking for StEntries [Jasper; #706749]
+* Fix reentrancy issue in message tray [Jasper; #711694]
+* Tone down zoom animation on login/unlock [Jasper; #712362]
+* Allow specifying monitor for OSD [Carlos; #712664]
+* Fix resetting prompt on user switch [Ray; #710456]
+* Stop using gnome-bluetooth-applet [Bastien; #719341]
+* Add support for EAP-FAST password requests [Dan; #719813]
+* Fix entry focus of chat notifications [Jasper; #709853]
+* Make window previews keyboard navigatable [Jasper; #644306]
+* Fix app switcher order with dialog windows [Florian; #719824]
+* Allow remote search providers without icons [Debarshi; #719965]
+* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
+ #712594, #712600, #712579]
+* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
+ #712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
+ #720298]
+
+Contributors:
+ Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
+ Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
+ Jasper St. Pierre, Ray Strode, Dan Williams
+
+Translations:
+ Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
+ Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
+ Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
+ Daniel Mustieles [es]
+
+3.11.2
+======
+* Cache search result display actors [Jasper; #704912]
+* Use username in userWidget if real name doesn't fit [Jasper; #706851]
+* Support shell_global_reexec_self() on OpenBSD [Antoine; #709571]
+* Support disabling browser plugin [Colin; #711218]
+* Restore support for 'disable-restart-buttons' [Florian; #711244]
+* Validate parameters of exposed DBus methods [Florian; #699752]
+* Connect applications to systemd journal if available [Colin; #711626]
+* Misc bug fixes and cleanups [Florian, Jasper; #711205, #698486, #711416,
+ #644306, #711555, #709806, #711631, #711732]
+
+Contributors:
+ Cosimo Cecchi, Antoine Jacoutot, Florian Müllner, Jasper St. Pierre,
+ Rico Tzschichholz, Colin Walters
+
+Translations:
+ Yuri Myasoedov [ru], Kjartan Maraas [nb], Efstathios Iosifidis [el],
+ Benjamin Steinwender [de], eternalhui [zh_CN], Shantha kumar [ta]
+
+3.11.1
+======
+* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
+* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
+ #710137, #710596]
+* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
+* Don't autostart remote search providers at login [Giovanni; #708830]
+* Fix spacing in end-session dialog [Sebastien; #710543]
+* Prepare for js24 [Tim; #711052]
+* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
+ #710144, #710541, #691409, #710745, #688331, #704912]
+
+Contributors:
+ Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
+ Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
+
+Translations:
+ Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
+
+3.10.1
+======
+* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
+* Fix signal strength indicators in network selector [Jasper; #708442]
+* Scroll search results when focusing provider icons [Jasper; #708868]
+* Add separate hover/active states to page indicators [Carlos; #708852]
+* Tweak appearance of user name and avatar [Yash; #702309]
+* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
+* Cancel open keyring prompts when the screen is locked [Florian; #708910]
+* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
+* Make network settings items point to the right device [Giovanni; #709246]
+* Remove animation of window preview titles [Sebastien; #709392]
+* Add 'Notifications' switch to tray menu [Florian; #707073]
+* Make dropdown arrows consistent [Carlos; #709564]
+* power: Use icon from primary device for status [Jasper; #709925]
+* Fix XDND drags to overview [Adel; #708887]
+* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
+* Handle search results with 'special:' prefix specially [Giovanni; #707055]
+* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
+* Use ARROW role for labels representing arrows [Alejandro; #710120]
+* Make selected view in app picker persistent [Florian; #710042]
+* Make network selector navigable by keyboard [Alejandro; #710144]
+* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
+ Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
+ #709638, #709866, #709998, #710019, #710104, #710115]
+
+Contributors:
+ Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
+ Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
+ Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
+ Dieter Verfaillie, Dan Winship
+
+Translations:
+ Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
+ Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
+ Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
+ Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
+ A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
+ Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
+ Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
+ Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
+ Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
+ Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
+ Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
+ Wouter Bolsterlee [nl], António Lima [pt]
+
+3.10.0.1
+=========
+* Fix login screen [Ray; #708691]
+
+Contributors:
+ Ray Strode, Giovanni Campagna, Jasper St. Pierree
+
+Translations:
+ Kjartan Maraas [nb], Marek Černocký [cs], A S Alam [pa], Daniel Mustieles [es],
+ Ihar Hrachyshka [be], Chao-Hsiung Liao [zh_HK], Nilamdyuti Goswami [as],
+ Yuri Myasoedov [ru], Baurzhan Muftakhidinov [kk]
+
+3.10.0
+======
+* Fix fade effect in ScrollViews [Carlos; #708256]
+* network: Resync when activating connection changes [Jasper; #708322]
+* Close run dialog when the screen locks [Florian; #708218]
+* Fix entry growing out of password dialogs [Florian; #708324, #703833]
+* Vertically center labels in submenu items [Jasper; #708330]
+* https://bugzilla.gnome.org/show_bug.cgi?id=708387 [Mike; #708387]
+* Fix bluetooth icon not being added to status menu [Jasper; #708541]
+* Fix GNOME 2 keyring dialogs appearing on lock screen [Florian; #708187]
+* Fix passwords being cleared twice when authentication fails [Florian; #708186]
+* Fix message tray appearing in a11y popup on login screen [Florian; #708380]
+* Increase width of aggregate menu popup [Adel; #708472]
+
+Contributors:
+ Adel Gadllah, Mike Gorse, Ryan Lortie, Florian Müllner, Frédéric Péters,
+ Carlos Soriano, Jasper St. Pierre, Rico Tzschichholz
+
+Translations:
+ Daniel Șerbănescu [ro], Ryan Lortie [eo], Ihar Hrachyshka [be],
+ A S Alam [pa], Jiro Matsuzawa [ja], Chao-Hsiung Liao [zh_HK, zh_TW],
+ Piotr Drąg [pl], Kristjan SCHMIDT [eo], Daniel Korostil [uk],
+ Rūdolfs Mazurs [lv], Reinout van Schouwen [nl], Yosef Or Boczko [he],
+ Fran Diéguez [gl], António Lima [pt], Andika Triwidada [id],
+ Alexandre Franke [fr], Rafael Ferreira [pt_BR], Milo Casagrande [it],
+ Kenneth Nielsen [da], Matej Urbančič [sl]
+
+3.9.92
+======
+* Don't show page indicators if there's only one page [Florian; #707363]
+* Make :active style of app and non-app results consistent [Jakub; #704714]
+* Fade app pages when scrolled [Florian; #707409]
+* Don't block scrolling on page indicators [Carlos; #707609]
+* Tweak visual appearance of folder views [Florian; #707662]
+* Don't put minimized apps at the end of the app switcher [Florian; #707663]
+* Merge the wayland branch [Giovanni, Neil; #707467]
+* Make search entry behave better in RTL locales [Matthias, Florian; #705779]
+* Allow to change app pages with pageUp/pageDown keys [Carlos; #707979]
+* Set appropriate a11y states on expandable menu items [Alejandro; #708038]
+* Improve page indicator animation [Carlos; #707565]
+* Misc bug fixes and cleanups [Florian, Olivier, Jasper, Giovanni, Magdalen,
+ Adel, Carlos, Rico, Joanmarie; #707308, #707430, #707508, #707557, #707600,
+ #707614, #707666, #707814, #707806, #707801, #707889, #707892, #707935,
+ #707842, #707940, #707996, #708007, #708009, #708020, #707580, #708080]
+
+Contributors:
+ Magdalen Berns, Olivier Blin, Giovanni Campagna, Matthias Clasen,
+ Joanmarie Diggs, Adel Gadllah, Florian Müllner, Alejandro Piñeiro,
+ Neil Roberts, Carlos Soriano, Jasper St. Pierre, Jakub Steiner,
+ Rico Tzschichholz
+
+Translations:
+ Rafael Ferreira [pt_BR], Fran Diéguez [gl], Daniel Mustieles [es],
+ Aurimas Černius [lt], Luca Ferretti [it], Piotr Drąg [pl],
+ Chao-Hsiung Liao [zh_HK, zh_TW], Timo Jyrinki [fi], Daniel Korostil [uk],
+ Dušan Kazik [sk], Adam Matoušek [cs], Marek Černocký [cs],
+ Jiro Matsuzawa [ja], Yuri Myasoedov [ru], Tobias Endrigkeit [de],
+ Kjartan Maraas [nb], Victor Ibragimov [tg], Мирослав Николић [sr, sr@latin],
+ A S Alam [pa], Khaled Hosny [ar], Andika Triwidada [id],
+ Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Rūdolfs Mazurs [lv],
+ Mattias Põldaru [et], Gabor Kelemen [hu], Bruce Cowan [en_GB],
+ Matej Urbančič [sl], Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
+ Changwoo Ryu [ko], Kris Thomsen [da], Alexandre Franke [fr],
+ Evgeny Bobkin [ru], Baurzhan Muftakhidinov [kk], Peter Mráz [sk],
+ Inaki Larranaga Murgoitio [eu], Yosef Or Boczko [he]
+
+3.9.91
+======
+* Improve submenu styling [Jakub; #706037]
+* Fix changing slider values via keyboard [Alejandro; #706386]
+* Fix accessibility of sliders [Alejandro; #706391]
+* Tweak system actions style [Jakub; #706638]
+* Add support for auth without username / fix Not Listed? [Ray; #706607]
+* Dash: Don't show tooltips for apps with open popups [Giovanni; #705611]
+* Implement new end-session/power-off dialog design [Jasper, Matthias; #706612]
+* Implement building separate binaries for x11 and wayland [Giovanni; #705497]
+* authPrompt: Fix controls moving when showing messages [Ray; #706670]
+* Tweak padding between system status icons [Allan; #706796]
+* Add a generic accessible usable by JS code [Alejandro; #648623]
+* Improve keynav and accessibility of the calendar [Alejandro; #706903]
+* Update to new NetworkManager APIs [Jasper; #706098]
+* Hide system actions section in the lock screen [Jasper; #706852]
+* Don't show other logged in users at log out [Giovanni; #707124]
+* Remove "Session" subtitle heading in login dialog [Jasper; #707072]
+* dash: Reload favorites when installed apps change [Giovanni; #706878]
+* Don't open overview after closing last window on workspace [Florian; #662581]
+* Add FocusApp DBus method [Giovanni; #654086]
+* Add ShowApplications DBus method [Giovanni; #698743]
+* Implement new app picker design [Carlos, Florian; #706081]
+* Improve frequent apps being empty by default [Carlos, Florian; #694710]
+* Extend clickable area of page indicators [Giovanni; #707314]
+
+* Misc bug fixes [Ray, Giovanni, Jasper, Emmanuele; #706542, #706654, #706005,
+ #706681, #706841, #706843, #707064, #706262, #707197, #707269]
+
+Contributors:
+ Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah,
+ Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
+ Jakub Steiner, Ray Strode, Seán de Búrca
+
+Translations:
+ Piotr Drąg [pl], Kjartan Maraas [nb], Victor Ibragimov [tg],
+ Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
+ Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Seán de Búrca [ga],
+ Fran Diéguez [gl], Daniel Mustieles [es], Dušan Kazik [sk],
+ Matej Urbančič [sl], Andika Triwidada [id], Jordi Mas [ca],
+ Ihar Hrachyshka [be]
+
+3.9.90
+======
+* workspaceThumbnails: Exclude transient windows when shifting workspaces
+ [Bradley; #705174]
+* Never show a horizontal scrollbar on lock screen [Jasper; #704327]
+* authPrompt: Fix disable-user-list / Not Listed? [Ray; #705370]
+* Animate the lock screen notification transitions [Giovanni; #687660]
+* Wake up the screen when new notifications appear [Giovanni; #703084]
+* Use StartupWMClass for application matching [Giovanni; #673657, #705801]
+* dateMenu: Add style class for the clock label [Jonh; #705634]
+* keyboard: Translate IBus IME name if possible [Daiki; #695673]
+* power: Display single digit minutes correctly [Sebastian; #705803]
+* Implement new aggregate status menu [Jasper; #705845]
+* Improve triangle animation when expanding sub-menus [Tarun; #703109]
+* Fix alignment of search provider icons [Tarun; #695760]
+* Slide dash and workspace switcher on overview transitions [Tarun; #694262]
+* Respect always-show-universal-access-status setting [Tanner; #705733]
+* Handle .desktop files with capital letters [Giovanni; #706252]
+* authPrompt: Add smartcard support [Ray; #683437]
+* Fix call notifications in busy mode [Emilio; #666221]
+* Improve triangle animation when expanding sub-menus [Tarun; #703109]
+* Move message tray menu to a tray button [Jasper; #699272]
+* Wi-fi dialog improvements [Jasper, Allan; #705916, #706136]
+* Work towards running as wayland compositor [Giovanni]
+ - Switch to Mutter abstraction layer for cursor tracking [#705911]
+ - Add confirmation dialog for display changes [#706208]
+* Use a different background in screen shield [Giovanni; #688210]
+* Add fade animation before blanking the screen [Giovanni; #699112]
+* Misc. bugfixes and cleanups [Jasper, Giovanni, Adel, Colin, Ray, Florian,
+ Magdalen; #704448, #702536, #686855, #695581, #700901, #701761, #701495,
+ #701848, #697833, #701731, #705664, #705840, #705898, #706089, #706153,
+ #704646, #706262, #706324, #703810, #703811, #704015, #706232, #705917,
+ #706536]
+
+Contributors:
+ Magdalen Berns, Giovanni Campagna, Allan Day, Tanner Doshier, Adel Gadllah,
+ Sebastian Keller, Tarun Kumar Joshi, Florian Müllner, Bradley Pankow,
+ Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
+ Daiki Ueno, Colin Walters, Jonh Wendell
+
+Translations:
+ Kjartan Maraas [nb], Aurimas Černius [lt], Yaron Shahrabani [he],
+ Fran Diéguez [gl], Gabor Kelemen [hu],
+ Juan Diego Martins da Costa Cruz [pt_BR], Inaki Larranaga Murgoitio [eu],
+ Yuri Myasoedov [ru], Daniel Mustieles [es], Seán de Búrca [ga],
+ Khaled Hosny [ar], Victor Ibragimov [tg], Friedel Wolff [af],
+ Marek Černocký [cs], Matej Urbančič [sl], A S Alam [pa],
+ Rafael Ferreira [pt_BR], Andika Triwidada [id], Dušan Kazik [sk]
+
+3.9.5
+=====
+* Fix width changes of the calendar popup [Florian; #704200]
+* Work towards aggregate status menu [Jasper; #702539, #704336, #704368,
+ #704670]
+* Update design of lock screen notifications [Allan; #702305]
+* Don't show empty backgroundMenu [Michael; #703868]
+* Add option to limit app switcher to current workspace [Adel; #703538]
+* Consolidate design of login screen and unlock dialog [Ray; #702308, #704795]
+* Respect hasWorkspace property of session mode [Jasper; #698593]
+* Fix fade of app menu icon in RTL locales [Jasper; #704583]
+* Destroy notifications when the close button is clicked [Adel; #687016]
+* Fix clicks on legacy tray icons in the message tray [Florian; #704095]
+* authPrompt: Fade out message if users start to type [Ray; #704817]
+* Export timestamps of global shortcuts on DBus [Bastien; #704859]
+* Fix duplicate search provider results [Jasper; #700283]
+* Misc bug fixes and cleanups [Lionel, Florian, Emilio, Ray, Jasper; #703859,
+ #703540, #704077, #703997, #704318, #704347, #704265, #704411, #704430,
+ #704347, #704453, #704471, #704542, #704707, #703905, #705037]
+
+Contributors:
+ Allan Day, Adel Gadllah, Lionel Landwerlin, Florian Müllner, Bastien Nocera,
+ Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Colin Walters,
+ Michael Wood
+
+Translations:
+ eternalhui [zh_CN], Victor Ibragimov [tg], Dušan Kazik [sk],
+ Jiro Matsuzawa [ja], Kjartan Maraas [nb], Milo Casagrande [it],
+ Marek Černocký [cs], Daniel Mustieles [es], Benjamin Steinwender [de]
+
+3.9.4
+=====
+* Fix chat entries not being focused when expanded [Jasper; #698778]
+* Fix alignment of "Not Listed?" label [Mathieu; #702307]
+* Fix alignment of time stamps in chat notifications [Carlos; #687809]
+* Round the ends of slider trough [Jasper; #702825]
+* Add support for "box-shadow: none" [Cosimo; #702782]
+* Keep chrome below popup windows [Florian; #702338]
+* Move the session list to a popup menu [Ray; #702818]
+* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
+* dnd: Speed up by not picking on each motion event [Jasper; #703443]
+* Fix management of asynchronous background loading [Lionel; #703001]
+* Rework focus handling [Jasper; #700735]
+* Optimize box-shadow rendering [Lionel; #689858]
+* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
+* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
+ #702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
+ #698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
+
+Contributors:
+ Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
+ Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
+ Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
+
+Translations:
+ Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
+ Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
+ Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
+
+3.9.3
+=====
+* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
+* Tweak timeout for activating windows during XDND [Adel; #700150]
+* Fix ellipsization in control buttons in app picker [Carlos; #696307]
+* Fix DND to empty dash [Florian; #684618]
+* Fix OSD window appearing below system modal dialogs [Rui; #701269]
+* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
+* Allow session mode specific overrides schema [Florian; #701717]
+* window-switcher: Only show windows from current workspace by default
+ [Florian; #701214]
+* logout dialog: Show the correct text right away [Matthias; #702056]
+* bluetooth: Port to bluez 5 [Emilio; #700891]
+* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
+* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
+* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
+ #700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
+ #701849, #702121]
+
+Contributors:
+ Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
+ Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
+ Jonh Wendell
+
+Translations:
+ Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
+ Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
+ eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
+ Daniel Mustieles [es]
+
+3.9.2
+=====
+* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
+* Move paint state cache into StWidget [Jasper; #697274]
+* gdm: Fix regression where domain login hint not shown [Stef; #698200]
+* Make calendar keyboard navigable [Tanner; #667434]
+* Hide "Open Calendar" item when no calendar app is installed [Lionel; #697725]
+* Update how branding appears on login screen [Florian; #694912, #699877]
+* Allow OSD popups to grow if necessary [Marta; #696523]
+* Fix offset of shadow offscreen rendering [Lionel; #698301]
+* Fix insensitive button preventing empty keyring password [Stef; #696304]
+* Allow cancelling keyring dialog between prompts [Stef; #682830]
+* modalDialog: Show spinner while working [Stef; #684438]
+* overview: Only show close buttons for windows that may close [Jasper; #699269]
+* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
+* Set input-purpose property for password entries [Rui; #700043]
+* Provide a DBus API for screencasting [Florian; #696247]
+* overview: Disable hotcorner during DND [Jasper; #698484]
+* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
+* Add a way to get backtraces from criticals and warnings [Giovanni; #700262]
+* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
+* Update man page [Matthias; #700339]
+* Add FocusSearch DBus method [Florian; #700536]
+* Hide frequent view when app monitoring is disabled [Florian; #699714]
+* Show switcher popup for switch-to-workspace-n keybindings [Elad; #659288]
+* gdm: Update the session chooser style [Allan; #695742]
+* Fix some app folders getting truncated at the top [Florian; #694371]
+* Don't block the message tray while a notification is showing [Jasper; #700639]
+* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
+* Re-lock screen when restarted after a crash [Colin; #691987]
+* Synchronize input source switching with key events [Rui; #697007]
+* Switch input source on modifiers-only accelerator [Rui; #697008]
+* Allow input source switching in message tray [Rui; #697009]
+* Misc bug fixes and cleanups [Alban, Jasper, Giovanni, Florian, Rui, Tomeu,
+ Stef, Gustavo; #698863, #699799, #699800, #676285, #699975, #700097, #698812,
+ #698486, #700194, #695314, #700257, #699678, #700356, #700322, #700394,
+ #700409, #700595, #700625, #691746, #700620, #700807, #659288, #700784,
+ #700842, #700847, #700488, #700735, #696159, #700900, #700853, #700923,
+ #700944, #697661, #700854, #700190, #699189, #701097]
+
+Contributors:
+ Elad Alfassa, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
+ Tanner Doshier, Lionel Landwerlin, Rui Matos, Simon McVittie,
+ Marta Milakovic, Florian Müllner, Gustavo Padovan, Jasper St. Pierre,
+ Daiki Ueno, Tomeu Vizoso, Stef Walter, Colin Walters
+
+Translations:
+ Matej Urbančič [sl], Kjartan Maraas [nb], Victor Ibragimov [tg],
+ Dušan Kazik [sk], Gil Forcada [ca], Daniel Mustieles [es]
+
+3.9.1
+=====
+* Add additional toggle-overview keybinding [Matthias; #698251]
+* Disable <super> shortcut when sticky keys are enabled [Matthias; #685974]
+* Disable tray context menu while a notification displays [Jasper; #695800]
+* Watch GApplication busy state [Cosimo; #697207]
+* Disable style transitions if animations are disabled [Jasper; #698391]
+* Filter out hidden applications from "Frequent" view [Giovanni; #696949]
+* Fix window previews swapping place randomly [Jasper; #694469, #698776]
+* Add support for serialized GIcons in remote search providers [Cosimo; #698761]
+* Fix hotcorner regression in RTL locales [Jasper; #698884]
+* Allow some keybindings to work while a top bar menu is open [Florian; #698938]
+* Make open-app-menu keybinding a toggle action [Florian; #686756]
+* Only recognize common URL schemes in notification messages [Monica; #661225]
+* Misc fixes and cleanups [Tim, Jasper, Florian, Giovanni, Owen; #698531,
+ #698622, #698427, #698483, #698513, #697203, #698959, #698918, #699029,
+ #699075, #696720, #649748]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Monica Chelliah, Matthias Clasen, Tim Lunn,
+ Florian Müllner, Jasper St. Pierre, Michael Wood, Owen W. Taylor
+
+Translations:
+ Fran Diéguez [gl], Muhammet Kara [tr], Daniel Mustieles [es],
+ Gil Forcada [ia], Anish A [ml], Dimitris Spingos [el], Marek Černocký [cs],
+ Žygimantas Beručka [lt]
+
+3.8.1
+=====
+* Clip window group during startup animation [Jasper; #696323]
+* Check for logind rather than systemd [Martin; #696252]
+* Don't special-case last remote search provider position [Giovanni; #694974]
+* Fix memory leaks [Ray, Jasper; ##697119, #697295, #697300, #697395]
+* AppSwitcherPopup: Activate only the selected window if any [Rui; #697480]
+* Enable screen recorder keybinding in all modes [Florian; #696200]
+* Remove box-shadow from screen shield for performance reasons [Adel; #697274]
+* Add support for -st-natural-width/height CSS properties [Giovanni; #664411]
+* Remove excessive padding from notification buttons [Allan; #664411]
+* Fix thumbnail dragging in overview [Jasper; #697504]
+* theme-node: Add get_url()/lookup_url() methods [Florian; #693688]
+* Misc bug fixes and cleanups [Jasper, Rui, Colin, David, Ray, Matthias:
+ #695859, #696259, #696585, #696436, #697432, #697435, #697560, #697722,
+ #697709]
+
+Contributors:
+ Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah, David Gumberg,
+ Rui Matos, Florian Müllner, Martin Pitt, Jasper St. Pierre, Ray Strode,
+ Colin Walters
+
+Translations:
+ Daniel Martinez [an], Bruce Cowan [en_GB], Khaled Hosny [ar],
+ Ihar Hrachyshka [be], Aron Xu [zh_CN], Wojciech Szczęsny [pl],
+ Inaki Larranaga Murgoitio [eu], Kjartan Maraas [nb],
+ Милош Поповић [sr, sr@latin], Trần Ngọc Quân [vi]
+
+3.8.0.1
+=======
+* Background bug fixes [Ray; #696712]
+
+3.8.0
+=====
+* Remove blur and desaturation from lock screen [Jasper; #696322]
+* Remove scroll view fade near edges [Adel; #696404]
+* dateMenu: Open calendar component when using Evolution [Florian; #696432]
+* Fix unlocking on fast user switch [Cosimo; #696287]
+* Tweak screen shield animation [Rui; #696380]
+* Fix major memory leak when changing backgrounds [Ray; #696157]
+* Miscellaneous bug fixes [Jasper, Adel, Florian; #696199, #696212, #696422,
+ #696447, #696235]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
+ Jasper St. Pierre, Ray Strode
+
+Translations:
+ Alexandre Franke [fr], Victor Ibragimov [tg], Arash Mousavi [fa],
+ Gabor Kelemen [hu], Sandeep Sheshrao Shedmake [mr], ManojKumar Giri [or],
+ Shantha kumar [ta], Rajesh Ranjan [hi], Stas Solovey [ru],
+ Shankar Prasad [kn], Dušan Kazik [sk], Ihar Hrachyshka [be],
+ Wouter Bolsterlee [nl], Kris Thomsen [da], Jiro Matsuzawa [ja],
+ Daniel Korostil [uk], Ani Peter [ml], Krishnababu Krothapalli [te],
+ Mantas Kriaučiūnas [lt], Praveen Illa [te]
+
+3.7.92
+======
+* Drop fallback lock implementation [Florian; #693403]
+* Don't let the user trigger message-tray when in fullscreen [Jasper; #694997]
+* Scroll search results when using keynav [Jasper; #689681]
+* Allow raising the shield by starting to type the password [Jasper; #686740]
+* Improve tracking of fullscreen windows [Owen; #649748]
+* Improve animation of new windows in overview [Giovanni; #695582]
+* workspace switcher: Animate new workspaces created by DND [Giovanni; #685285]
+* Give user time to read messages on login screen [Ray; #694688]
+* Misc bug fixes and cleanups [Jasper, Ray, Florian, Cosimo, Giovanni, Adel,
+ Stef, Takao, Rui, Neil; #695154, #694993, #695272, #691578, #694321, #695338,
+ #695409, #695458, #695526, #695601, #695471, #695324, #695650, #695656,
+ #695659, #695485, #695395, #694951, #695824, #695841, #695801, #694321,
+ #693708, #695800, #695607, #695882, #691578, #685851, #694371, #690857,
+ #694092, #695747, #696007, #693438, #696064, #696102
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Allan Day, Takao Fujiwara, Adel Gadllah,
+ Tim Lunn, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre,
+ Ray Strode, Stef Walter, Colin Walters, Owen W. Taylor
+
+Translations:
+ Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW],
+ Yuri Myasoedov [ru], Gheyret Kenji [ug], Baurzhan Muftakhidinov [kk],
+ Ville-Pekka Vainio [fi], Matej Urbančič [sl],
+ Мирослав Николић [sr, sr@latin], Rūdolfs Mazurs [lv], Christian Kirbach [de],
+ Andika Triwidada [id], Gil Forcada [ca], Mattias Põldaru [et],
+ Duarte Loreto [pt], Adam Matoušek [cs], Changwoo Ryu [ko],
+ Ihar Hrachyshka [be], Carles Ferrando [ca@valencia], Sweta Kothari [gu]
+
+3.7.91
+======
+* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
+* overview: Keep open when a Control key is held [Florian; #686984]
+* Improve login screen => session transition [Ray; #694321]
+* Center application grid horizontally [Florian; #694261]
+* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
+* Tweak thresholds of pressure barrier [Jasper; #694467]
+* Tweak window picker layout [Jasper; #694902]
+* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
+* Don't always show message tray in overview, add message indicator
+ [Cosimo; #687787]
+* Tweak startup animation [Ray; #694326]
+* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
+* Move loupe icon to the start of the search entry [Jasper; #695069]
+* Don't show the input switcher with less than 2 items [Rui; 695000]
+* Fix auto-completion of system modals immediately upon display [Stef; #692937]
+* Ignore workspaces in alt-tab [Florian; #661156]
+* Disable copying text from password entries [Florian; #695104]
+* Use standard styling for ibus candidate popups [Allan; #694796]
+* Fix calendar changing height on month changes [Giovanni; #641383]
+* Port the hot corner to use pressure barriers [Jasper; #663661]
+* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
+ Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
+ #694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
+ #663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
+ #694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
+ #694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
+ #695126, #687748, #694837, #693907, #679851, #694988]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
+ Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
+ Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
+
+Translations:
+ Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
+ Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
+ Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
+ Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
+ Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
+ Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
+
+3.7.90
+======
+* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
+* Implement middle-click paste [Jasper; #645019]
+* Fix top bar transition between session modes [Rui; #692966]
+* Trigger the message tray with downward pressure [Jasper; #677215]
+* Don't ask for a password on shutdown [Adel; #693385]
+* Add a context menu to the message tray [Adel; #691035, #693887]
+* Use unicode formatting in the date menu [Matthias; #689251]
+* Use proper ellipsis instead of three dots [Jeremy; #689542]
+* Tweak screen shield animation [Giovanni; #691964]
+* Always hide the OSK when showing the message tray [Florian; #662687]
+* Support sound in notifications [Giovanni; #642831]
+* Place application popup menus above chrome [Jasper; #633620]
+* Hide overview elements while searching [Cosimo; #682050]
+* Implement updated IBus candidate popup designs [Rui; #691902]
+* Add support for enable-animations preference [Cosimo; #655746]
+* Don't always show the message tray in the overview [Cosimo; #693987]
+* Improve arrangement of window previews [Adel; #690313]
+* Remove builtin settings provider [Giovanni; #690824]
+* Minimize fullscreen windows when they end up in the background [Adel; #693991]
+* Add context menu to the background actor [Jasper; #681540]
+* Handle backgrounds in the shell, improve startup animation [Ray; #682429]
+* Hide universal access menu when not needed [Giovanni; #681528]
+* Implement updated app picker designs [Florian; #694192]
+* Improve login manager -> session transition [Ray; #694062]
+* Don't use a grid layout in window picker [Adel; #694210]
+* Use scroll wheel for workspace switching rather than zoom [Florian; #686639]
+* Misc bug fixes and cleanups: [Jasper, Florian, Debarshi, Adel, Matthias,
+ Giovanni, Daiki, Rico, Bastien, Cosimo, Ray, Allan, Antonio; #693284,
+ #692680, #691746, #693303, #693162, #693161, #693522, #693385, #691715,
+ #688915, #689106, #682429, #693570, #693737, #693458, #692845, 693836,
+ #681540, #679925, #688227, #692773, #693909, #683288, #693854, #693746,
+ #693931, #693924, #693940, #693970, #693935, #693937, #693974, #693936,
+ #693975, #693822, #694030, #685849, #694052, #694035, #694038, #694079,
+ #694064, #681735, #694100, #694057, #694070, #693572, #693896, #686984,
+ #694123, #694125, #693756, #693757, #687556, #694215, 694062, #694227,
+ #694240, #694234, #694264, 694276, 694282, #694241, #689394, #694202,
+ #694265, #694289, #691806, #694290, #694296]
+
+Contributors:
+ Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day,
+ António Fernandes, Adel Gadllah, Rui Matos, Florian Müllner, Bastien Nocera,
+ Debarshi Ray, Neil Roberts, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
+ Daiki Ueno
+
+Translations:
+ Yasumichi Akahoshi [ja], Yoji TOYODA [ja], Dušan Kazik [sk],
+ Wouter Bolsterlee [nl], Matej Urbančič [sl], Gheyret Kenji [ug],
+ Ivaylo Valkov [bg], Daniel Korostil [uk], Gheyret Kenji [ug],
+ Daniel Mustieles [es], Anish A [ml], Gil Forcada [ca],
+ Carles Ferrando [ca@valencia], Мирослав Николић [sr, sr@latin],
+ Aurimas Černius [lt], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
+ Piotr Drąg [pl], Luca Ferretti [it], A S Alam [pa]
+
+3.7.5
+=====
+* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
+* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
+* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
+* Make list search results span all available horizontal space [Tanner; #691967]
+* Make Show-Applications button depress when held down [Hashem; #692319]
+* Set a max width on search results [Cosimo; #692453]
+* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
+* Improve scaling algorithm for window thumbnails [Jasper; #686944]
+* Fix launching settings panels after g-c-c changes [Jasper; #692483]
+* Stop launching applications from empty searches [Hashem; #692391]
+* Implement per-source notification filtering [Giovanni; #685926]
+* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
+* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
+* Make previews of minimized windows translucent in overview [Florian; #692999]
+* windowManager: Respect icon geometry when minimizing [Florian; #692997]
+* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
+* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
+* network: Add support for new ModemManager1 interface [Aleksander; #687359]
+* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
+* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
+* general: Consistently use Title Case in top bar [Jeremy; #689589]
+* panel: Add :overview pseudo class while in overview [Florian; #693218]
+* sessionMode: Add support for mode-specific styling [Florian; #693219]
+* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
+* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
+* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
+ Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
+ #692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
+ #683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
+ #692586, #693067, #693031, #693049, #643111, #693161, #693220]
+
+Contributors:
+ Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
+ Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
+ Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
+
+Translations:
+ Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
+ Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
+ Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
+ Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
+ Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
+ Milo Casagrande [it], Benjamin Steinwender [de]
+
+3.7.4.1
+=======
+* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
+* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
+* screenshot: Immediately show the flash spot [Jasper; #691875]
+* Misc. bug fixes [Rico, Jeremy]
+
+Contributors:
+ Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
+ Rico Tzschichholz
+
+Translations:
+ Ihar Hrachyshka [be]
+
+3.7.4
+=====
+* Make menu separators crisp [Giovanni, Allan; #641745]
+* power: Update for new D-Bus name [Bastien; #690506]
+* Add smooth scrolling support [Jasper; #687573]
+* Tweak notification layout [Allan; #688506]
+* Ping the active window when using the app menu [Giovanni; #684340]
+* Make password entries insensitive after submission [Jasper; #690594, #690895]
+* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
+* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
+* lookingGlass: Move to an inspect() function [Jasper; #690726]
+* Make OSK animation quicker, snappier [Rui; #688642]
+* Allow to close chat notifications with Escape [Jasper; #690897]
+* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
+* Add a SelectArea() DBus method [Cosimo; #687954]
+* Support non-absolute paths when saving screenshots [Cosimo; #688004]
+* OSK: Fix extended keys popups [Rui; #674955]
+* Don't hide or show the keyboard immediately [Rui; #688646]
+* Improve padding in power menu [Giovanni; #689297]
+* Add per-window input source switching [Rui; #691414]
+* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
+ #690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
+ #690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
+ #685513, #689263, #691553, #691720, #691743, #691750]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
+ Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
+ Dan Winship
+
+Translations:
+ Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
+ Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
+ Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
+ Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
+ Aron Xu [zh_CN], Gheyret Kenji [ug]
+
+3.7.3
+=====
+* Add 'No Messages' label when message tray is empty [Victoria; #686738]
+* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
+* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
+* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
+* Improve login process indication [Stéphane; #687113]
+* Omit empty categories in apps view [Stéphane; #687970]
+* Style panel differently according to mode [Florian; #684573]
+* Make it possible to hide the user name [Matthias; #688577]
+* Consolidate and improve chat connection notifications [Giovanni; #687213]
+* Improve notification scrollbar appearance [Carlos; #688393]
+* Fade scroll view fade near scrolling edges [Jasper; #689249]
+* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
+* Don't close message tray after using context menus [Giovanni; #689296]
+* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
+* Remember state of 'Remember Password' checkbox [Ron; #688039]
+* Improve timestamp format in chat notifications [Carlos; #680989]
+* Improve style of missed-messages counter [Carlos; #686472]
+* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
+* Add window-based Alt-Tab popup [Florian; #688913]
+* Support external session mode definitions [Florian; #689304]
+* Support session-mode-specific extensions [Florian; #689305]
+* Support 'parentMode' property in session modes [Florian; #689308]
+* Support a new org.gnome.ShellSearchProvider2 DBus interface
+ [Cosimo; #689735, #690009]
+* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
+* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
+* Show headphone icon when headphones are plugged in [Giovanni; #675902]
+* Display (non-app) search results as list [Tanner, Cosimo; #681797]
+* Skip diacritical marks in search terms [Aleksander; #648587]
+* Expose all engine options in input sources [Giovanni, Rui; #682318]
+* Add input source switcher popup [Rui; #682315]
+* Add minimal support for InfiniBand in network menu [Dan; #677150]
+* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
+ Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
+ Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
+ #688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
+ #688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
+ #689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
+ #689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
+ #689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
+ #690241, #690312, #690175, #687955, #650843, #688234, #690427
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
+ Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
+ Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
+ Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
+ Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
+
+Translations:
+ Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
+ Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
+ Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
+ Daniel Mustieles [es], Dušan Kazik [sk]
+
+3.7.2
+=====
+* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]
+* gdm: Move logo into the panel [Florian; #685852]
+* Hide notifications when closed button is clicked [Jasper, Florian; #682237]
+* Tweak screenShield animations [Rui; #686745]
+* Restore Fittsability of summary items in message tray [Florian; #686474]
+* Save screencasts as recent item [Ray; #680647]
+* overview: Resize window captions on content change [Giovanni, Alex; #620874]
+* App search: Match GenericName too [Matthias; #687121]
+* runDialog: Better match style of other modal dialogs [Florian, Allan; #687127]
+* Improve the button insensitive style [Stéphane; #687110]
+* network: Don't use a global switch for all VPN connections [Giovanni; #682929]
+* appMenu: Update on icon theme changes [Florian; #687224]
+* Show 'Log out' in more situations [Matthias; #686736]
+* Add a setting to force the 'Log out' menuitem [Matthias; #686057]
+* overview: Improve styling of search box [Stéphane; #686479]
+* Implement 'disable-user-list' in login screen [Ray; #660660]
+* Fix auto-scroll to bottom in chat notifications [Sjoerd; #686571]
+* Show feedback notifications when user is busy [Stéphane; #662900]
+* Disable login button when there is no input [Stéphane; #687112]
+* Use non-linear overview shade for background [Giovanni, Pierre-Eric; #669798]
+* Reduce blocking in compositor thread [Simon, Jasper; #687465]
+* network: new country-specific type to gather providers [Aleksander; #687356]
+* Update man page [Matthias; #680601]
+* st-entry: Change the pointer cursor on enter/leave events [Thomas; #687130]
+* screenShield: Blur and desaturate the background [Giovanni, Cosimo; #682536]
+* Change height of chat notifications to have more context [Carlos; #665255]
+* screenShield: Account for motion velocity when hiding [Giovanni; #682537]
+* screenShield: hide the cursor while the lock screen is on [Giovanni; #682535]
+* Support remote search provider settings [Cosimo; #687491]
+* unlockDialog: Improve label of confirmation button [Stéphane; #687656]
+* userMenu: Rename "System Settings" item to "Settings" [Elad; #687738]
+* messageTray: Add keybinding to focus current notification [Stéphane; #652082]
+* Remove shell-screen-grabber [Neil; #685915]
+* main: Stop using Metacity's keybinding files [Florian; #687672]
+* Bluetooth: Remove ObexFTP functionality [Bastien; #688160]
+* a11y: Also set WM theme when HighContrast is switched on [Cosimo; #688256]
+* network: Rework multiple NIC support [Giovanni; #677142]
+* Rework keybindings to allow selective blocking/processing [Florian; #688202]
+* recorder: Show indicator on primary monitor [Adel; #688470]
+* recorder: Set frame duration to fix broken video headers [Adel; #688487]
+* Misc. bugfixes and cleanups [Florian, Jasper, Giovanni, Matthew, Stéphane,
+ Allan, Daiki, Owen, Alejandro, Jean-François, Cosimo, Sebastian, Adel, Alban;
+ #686484, #686728, #686805, #686574, #686763, #682428, #687132, #685239,
+ #687189, #687226, #658091, #670687, #687457, #687242, #687287, #687020,
+ #686583, #661194, #687491, #657315, #687958, #683986, #688089, #687708,
+ #686530, #684810, #688181, #688475, #688557, #688507, #638351]
+
+Contributors:
+ Elad Alfassa, Matthew Barnes, Alban Browaeys, Giovanni Campagna,
+ Cosimo Cecchi, Matthias Clasen, Allan Day, Stéphane Démurget,
+ Jean-François Fortin Tam, Adel Gadllah, Alex Hultman, Sebastian Keller,
+ Rui Matos, Simon McVittie, Aleksander Morgado, Florian Müllner,
+ Bastien Nocera, Pierre-Eric Pelloux-Prayer, Alejandro Piñeiro, Neil Roberts,
+ Sjoerd Simons, Carlos Soriano Sánchez, Jasper St. Pierre, Ray Strode,
+ Owen Taylor, Daiki Ueno, Thomas Wood
+
+Translations:
+ Dušan Kazik [sk], Pavol Klačanský [sk], Piotr Drąg [pl], Yuri Myasoedov [ru],
+ Marek Černocký [cs], Kjartan Maraas [nb], Wolfgang Stöggl [de],
+ Yaron Shahrabani [he], Fran Diéguez [gl], Mattias Põldaru [et]
+
+3.7.1
+=====
+* Add shortcut to open application view directly [Jeremy; #685738]
+* Expose '<Super>F10' shortcut in System Settings [Florian; #672909]
+* Clean up timestamp format in chat notifications [Carlos; #680989]
+* loginScreen: Add support for 'disable-restart-buttons' [Florian; #686247]
+* Update textures automatically on file changes [Florian; #679268]
+* Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064]
+* Add missing translations for GSetting schema [Giovanni; #686413]
+* Hide workspace switcher completely when it's not necessary [Seif; #686483]
+* Explicitly load gnome-screensaver when not running GDM [Tim; #683060]
+* Port to GnomeIdleMonitor [Jasper; #682224]
+* Set Empathy as preferred handler when delegating channels [Xavier; #686296]
+* Allow testing GDM login dialog from the session [Giovanni; #683725]
+* Use all available space for windows in window picker [Jasper, Pierre-Eric;
+ #582650]
+* Use logind for suspend if available [Florian; #686482]
+* Misc. fixes and cleanups [Jasper, Florian, Adel, Rui; #677426, #680426,
+ #686233, #686241, #686318, #686240, #686484, #686002, #684650, #686487]
+
+Contributors:
+ Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Adel Gadllah, Seif Lotfy,
+ Tim Lunn, Rui Matos, Florian Müllner, Pierre-Eric Pelloux-Prayer,
+ Carlos Soriano, Jasper St. Pierre
+
+Translations:
+ Andika Triwidada [id], Matej Urbančič [sl], Ihar Hrachyshka [be],
+ Daniel Mustieles [es], Fran Diéguez [gl], Takayuki KUSANO [ja],
+ Мирослав Николић [sr, sr@latin], Dušan Kazik [sk], Tom Tryfonidis [el]
+
+3.6.1
+=====
+* dash: Make padding even on the top/bottom of the dash [Jasper; #684619]
+* Fix a crash when dragging search results [Jasper; #684888]
+* workspaceThumbnail: Fix dragging with static workspaces [Jasper; #684641]
+* Really hide 'Show Keyboard Layout' on the lock screen [Matthias]
+* Misc. improvements to jhbuild setup [Owen; #685352, #685353, #685354, #685355]
+* Show message tray in Ctrl+Alt+Tab outside of the overview [Jasper, Florian;
+ #684633, #685914]
+* Disable hotplug sniffer on remote filesystems [Jasper; #684093]
+* userMenu: Remove 'Switch Session' item [Florian; #685062]
+* unlockDialog: Make prompt entry insensitive while logging in [Jasper; #685444]
+* messageTray: Don't animate desktop clone for failed grabs [Jasper; #685342]
+* Fix crash on dragging windows between workspaces [Ryan; #681399]
+* userMenu: Ignore 'lock-enabled' setting for user switching [Florian; #685536]
+* gdm: Fix key-focus on first user [Adel; #684650]
+* Make grid button insensitive when dragging non-favorites [Jasper; #685313]
+* Calendar: hide all actions when on the login screen [Matthias; #685142]
+* Adapt unlock dialog layout for the login screen [Florian; #685201]
+* Make focus-follows-mouse work better with Shell UI [Florian; #678169]
+* Improve look of screen shield [Jasper; #685919]
+* Fix keynav in the login screen [Florian; #684730]
+* dateMenu: Hide "Open Calendar" item if calendar unavailable [Florian; #686050]
+* unlockDialog: Reset UI on verification failure [Giovanni; #685441]
+* Show unlock dialog on primary monitor when using keynav [Giovanni; #685855]
+* Fix height changes of entries when entering text [Florian; #685534]
+* Fix show-apps label after successful drags [Florian; #684627]
+* Misc. bugfixes and cleanups [Jasper, Olivier, Florian, Owen, Adel, Tanner, Tim, Matthias; #685434, #685511, #685466, #685341, #685156, #681159, #673189, #686016, 684869, #686079, #686063
+
+Contributors:
+Jasper St. Pierre
+Matthias Clasen
+Owen Taylor
+Olivier Blin
+Florian Müllner
+Ryan Lortie
+Adel Gadllah
+Tanner Doshier
+Tim Lunn
+Giovanni Campagna
+
+Translations:
+ Tobias Endrigkeit [de], Rudolfs Mazurs [lv], Ask H. Larsen [da],
+ Shankar Prasad [kn], Changwoo Ryu [ko], Chris Leonard [en_GB],
+ Arash Mousavi [fa], Theppitak Karoonboonyanan [th], Seán de Búrca [ga],
+ Yaron Shahrabani [he], Alexander Shopov [bg], Žygimantas Beručka [lt],
+ Milo Casagrande [it], Kjartan Maraas [nb], Kris Thomsen [da],
+ Aurimas Černius [lt], Yuri Myasoedov [ru], Мирослав Николић [sr],
+ Marek Černocký [cs], Gabor Kelemen [hu], Ihar Hrachyshka [be],
+ Chao-Hsiung Liao [zh_HK, zh_TW], Eleanor Chen [zh_CN],
+ Carles Ferrando [ca@valencia], Vicent Cubells [ca], Daniel Korostil [uk],
+ Alexandre Franke [fr], Piotr Drąg [pl]
+
+3.6.0
+=====
+* keyboard: Make input source items accessible [Florian; #684462]
+* Don't show network dialogs in the lock screen [Giovanni; #684384]
+* popupMenu: Fix initial visibility of settings items [Florian; #684473]
+* userMenu: Close menu immediately on user/session switch [Florian; #684459]
+* Fix alignment of search section headers in RTL locales [Florian; #684379]
+* screenShield: Fix unlock animation [Florian; #684591]
+* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
+* userMenu: Fix texture updates on icon changes [Florian; #679268]
+* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
+* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
+ #683546]
+* Fix initial visibility of input volume in lock-screen [Florian; #684611]
+
+Contributors:
+ Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
+
+Translations:
+ Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
+ Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
+ Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
+ Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
+ Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
+ Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
+ Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
+ Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
+ Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
+ YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
+ Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
+ Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
+ Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
+
+3.5.92
+======
+* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
+* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
+* Add zoom out animation on login [Jasper; #683170]
+* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
+* Force chat notification to stay open when focusing entry [Debarshi; #682236]
+* Make sure the screen is fully locked before suspending [Giovanni; #683448]
+* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
+* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
+* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
+* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
+* Change stage background color to grey [Adel; #683514]
+* messageTray: Update style of summary counters [Debarshi; #682891]
+* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
+* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
+* Hide the a11y menu in the lock screen, but show it in the login screen
+ [Giovanni; #682542]
+* Fix show-apps button dropping off the dash [Florian; #683340]
+* Fix committing strings to shell entries from input method [Florian; #658325]
+* Make IBus display strings consistent with control-center [Rui; #683124]
+* Fix missing short codes for some input sources [Rui; #683613]
+* Remove support for long-press from entry context menus [Jasper; #683509]
+* screenShield: Add box-shadow to the shield [Florian]
+* Don't show a right-click menu for the hotplug source [Jasper; #683438]
+* Fix extension styling [Giovanni; #682128]
+* Fix on-screen keyboard not working with system-modal dialogs
+ [Giovanni; #664309]
+* Fix insensitive styling for popup menu items [Giovanni; #683988]
+* Disable the message tray dwell when the user is interacting [Owen; #683811]
+* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
+* Autostart fprintd when necessary [Ray; #683131]
+* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
+* Make Return key dismiss screenshield [Ray; #683889]
+* Fix keyboard navigation in the message tray [Florian; #682243]
+* Remove the places & devices search provider [Giovanni; #683506]
+* Enable hot corner while the message tray is up [Florian; #682255]
+* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
+* Fix fish flickering [Giovanni; #684154]
+* Fix extension ordering with !important [Jasper; #684163]
+* Allow the shell to run without the screenshield [Giovanni; #683060]
+* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
+* Improve transition to the login dialog [Jasper; #682428]
+* Keep unlock dialog around until shield animation ends [Florian; #684342]
+* Expose shell keybindings in System Settings [Florian; #671010]
+* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
+ #672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
+ #683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
+ #683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
+ #684162, #684214, #684343]
+
+Contributors:
+ Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
+ Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
+
+Translations:
+ Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
+ Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
+ Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
+ Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
+ Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
+ Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
+ Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
+ Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
+ Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
+ Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
+ Fran Diéguez [gl], Praveen Illa [te]
+
+3.5.91
+======
+* Improve modal dialog styling of network secret prompts [Jasper; #682412]
+* Fix visibility of non-active workspaces during overview transition
+ [Florian; #682002]
+* Improve scrollbar theming [Cosimo; #682476]
+* Make sure the app menu remains hidden in locked state [Florian; #682475]
+* Add tooltip to show-applications icon [Jasper; #682445]
+* Do not add duplicate remote search providers [Florian; #682470]
+* Handle 'popup-menu' signal on summary items [Florian; #682486]
+* Fix dwelling during mouse-down [Owen; #682385]
+* Set label actor for endSessionDialog.ListItem [Alejandro; #677503]
+* Don't match on comments when searching applications [Florian; #682529]
+* Make workspace selector more similar to the mockup [Stefano; #662087]
+* Fix extension installation and reloading [Jasper; #682578]
+* Hide removable devices in the lock screen [Giovanni; #681143]
+* Reset cancellable after hitting Escape on login screen [Alban; #681537]
+* Fix suspend from the user menu [Giovanni; #682746]
+* Set label actor for summary items in message tray [Alejandro; #677229]
+* Set label for the "Show applications" dash button [Alejandro; #682366]
+* Load extensions as late as possible [Jasper; #682822]
+* Improve mount operation dialogs [Jon; #682645]
+* Remove "Connect to ..." item from places search [Florian; #682817]
+* Don't auto-expand notifications with actions [Giovanni; #682738]
+* Add a new lock screen menu to combine volume network and power
+ [Giovanni; #682540]
+* Add support for pre-edit to StIMText [Daiki; #664041]
+* Remove StIconType [Jasper, Florian, Rui, Giovanni, Debarshi; #682540]
+* Use monitor geometry for dwelling [Florian; #683044]
+* Add support for surrounding-text to StIMText [Daiki; #683015]
+* Improve the placement and style of the "No results" text [Jasper; #683135]
+* Remove broken network device activation policy [Giovanni; #683136]
+* Hide power status icon when no battery is present [Tim; #683080]
+* Ensure summary items are square and have spacing [Debarshi; #682248]
+* Fix close buttons overlapping screen edge [Debarshi; #682343]
+* Escape the tray when a legacy icon is clicked [Giovanni; #682244]
+* Update arrow in the screen shield to match latest mockups [Giovanni; #682285]
+* Allow lifting the screen shield with the mouse wheel [Giovanni; #683164]
+* Make sure to show the app menu after unlocking the screen [Jasper; #683154]
+* Misc bug fixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rui;
+ #582650, #667439, #682238, #682268, #682429, #682455, #682544, #682546,
+ #682683, #682710, #682998, #683073, #683137, #683156]
+
+Contributors:
+ Alban Browaeys, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
+ Adel Gadllah, Tim Lunn, Rui Matos, William Jon McCann, Florian Müllner,
+ Alejandro Piñeiro, Debarshi Ray, Jasper St. Pierre, Owen Taylor, Daiki Ueno
+
+Translations:
+ Piotr Drąg [pl], Takayuki KUSANO [ja], Kjartan Maraas [nb],
+ Aurimas Černius [lt], Daniel Mustieles [es], Yuri Myasoedov [ru],
+ Khaled Hosny [ar], Yaron Shahrabani [he], Tom Tryfonidis [el],
+ Nilamdyuti Goswami [as], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi],
+ A S Alam [pa], Dr.T.Vasudevan [ta], Luca Ferretti [it]
+
+3.5.90
+======
+* Use symbolic icons for workspace switch OSD [Jon; #680738]
+* Lock screen improvements:
+ - Hide user menu and a11y menu in the screen lock [Giovanni; #681143]
+ - Bump the lock screen slightly when pressing a key [Giovanni; #681143]
+ - Constrain vertical movement of the screen shield [Giovanni; #681143]
+ - Return to lock screen on idle [Giovanni; #682041]
+ - Unlock screen automatically after fast-user switching [Giovanni; #682096]
+ - Fix "other user" label [Ray; #681750]
+* Constrain content of system modals to primary monitor [#681743]
+* Respect automatic lock setting on suspend/user-switch [Giovanni; #680231]
+* Improve styling of keyring prompt [Jasper; #681821]
+* Do not hard-code <super> as overlay-key [Florian; #665547]
+* Update style of attached modal dialogs [Florian; #681601]
+* a11y: allow navigation on non reactive items [Alejandro; #667439, #667439]
+* Implement mode-less overview design [Joost, Florian; #682109]
+* Implement message-tray redesign:
+ - Restyle the message tray [Ana, Allan, Florian; #677213, #682342]
+ - Move the desktop upwards when showing the tray [Debarshi; #681392]
+ - Add a close button to notifications [Ana, Jasper; #682253]
+ - Add a keybinding to toggle the tray [Debarshi; #681392]
+ - Make the tray keyboard navigable [Debarshi; #681519]
+ - Add dwelling at the bottom of the screen to open the tray [Owen; #682310]
+ - Don't time out banners when the user is inactive [Marina, Jasper]
+ - Misc fixes and cleanups [Jasper, Marina]
+* Fix showing "Next Week" on Sundays [Sebastian; #682198]
+* Delay restoring IM presence until the network comes up [Florian; #677982]
+* Display enterprise login hint [Ray; #681975]
+* Ignore unrecognized/irrelevant network devices/connections [Dan; #682364]
+* Misc bug fixes and cleanups: [Dan, Florian, Jasper, Jiro, Piotr, Rico;
+ #643687, #682045, #682189]
+
+Contributors:
+ Giovanni Campagna, Allan Day, Piotr Drąg, William Jon McCann,
+ Sebastian Keller, Jiro Matsuzawa, Florian Müllner, Alejandro Piñeiro,
+ Debarshi Ray, Ana Risteska, Jasper St. Pierre, Ray Strode, Owen Taylor,
+ Rico Tzschichholz, Joost Verdoorn, Dan Winship, Marina Zhurakhinskaya
+
+Translations:
+ Nilamdyuti Goswami [as], Daniel Mustieles [es], Yaron Shahrabani [he],
+ Chao-Hsiung Liao [zh_HK, zh_TW], Tobias Endrigkeit [de], A S Alam [pa],
+ Sandeep Sheshrao Shedmake [mr], Fran Diéguez [gl],
+ Мирослав Николић [sr, sr@latin]
+
+3.5.5
+=====
+* Update style to match mockups [Allan]
+ - improve calendar layout and legibility
+ - update notifications and menus
+ - use a common style for entries
+ - update scrollbars to match GTK+
+ - improve clock/unlock button in lock screen
+ - update polkit dialogs [Jasper]
+* Fix login dialog growing when selecting different users [Florian; #675076]
+* Implement screen lock in the shell [Giovanni]
+ - restructure login code to be shared with session unlock [#619955]
+ - add initial screen shield / unlock dialog implementation [#619955]
+ - implement (optional) notification list on lock shield [#619955]
+ - update login dialog style to match lock screen [#619955]
+ - filter notifications to only show new ones on the screen lock [#681143]
+ - make notifications scrollable if necessary [#681143]
+ - use correct application names in notifications [#681143]
+ - allow to return to the shield by pressing Escape [#681143]
+* Minor login dialog improvements [Florian]
+ - update style to match the overall visuals [#660913]
+ - indicate whether users are logged in [#658185]
+* Add support for background-repeat CSS property [Jasper; #680801]
+* Add :active pseudo class on scroll handles [Florian]
+* Remove markup from translated strings [Matthias; #681270]
+* Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy,
+ Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426,
+ #681101, #681382]
+
+Contributors:
+ Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
+ Piotr Drąg , Florian Müllner, Jasper St. Pierre
+
+Translations:
+ Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he],
+ Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te],
+ Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu],
+ Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru],
+ Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin]
+
+3.5.4
+=====
+* Fix wrong result handling of remote calls [Florian; #678852]
+* dateMenu: Fix regression that caused no date to be displayed [Colin]
+* WindowTracker: Fix refcounting bug in get_app_for_window() [Giovanni; #678992]
+* Show the workspace switcher for move-to-workspace keybinding
+ [Giovanni, Jasper; #674104, #660839, #679005]
+* userMenu: Move "Power off" item to the bottom [Florian; #678887]
+* Remove contacts search provider [Florian, Rui; #677442]
+* network: don't ask for always-ask secrets when interaction isn't allowed
+ [Dan; #679091]
+* PolkitAgent: Look for the right password prompt [Matthias; #675300]
+* Implement extension updates [Jasper; #679099]
+* userMenu: Don't disconnect account signals when disabled [Guillaume; #669112]
+* Fix startup notification when opening calendar [Florian; #677907]
+* networkAgent: use absolute path if configured [Clemens; #679212]
+* recorder: Port to GStreamer-1.0 API [Florian; #679445]
+* telepathyClient: don't add log messages on presence changes [Ana; #669508]
+* lookingGlass: Don't use a signal callback on 'paint' to draw the border
+ [Jasper; #679464]
+* Add support for inhibiting automount [Hans; #678597]
+* Implemented banner support for the login screen [Matthias, Marius; #665346]
+* boxpointer: Flip side if we would end outside the monitor [Rui; #678164]
+* boxpointer: Change 'animate' parameter on show/hide to a bitmask
+ [Rui; #678337]
+* Add a grayscale effect [Matthias, Jasper, Florian: #676782, #674499]
+* UserMenu: show "Install Updates & Restart" when appropriate
+ [Giovanni; #677394, #680080]
+* messageTray: don't show the message tray when a new notification is shown
+ [Ana; #677210]
+* panel: don't break when indicator has no menu [Jean-Philippe; #678694]
+* appMenu: Disable app menu during startup animations [Florian; #672322]
+* autorun: Add a notification when unmounting drives [Cosimo; #676125]
+* st-icon: Fix potential crash involving shadows [Jasper; #679776]
+* Remove manual garbage collection on tweeners end [Cosimo; #679832]
+* dash: hide tooltips when overview begins hiding [Stefano; #674241]
+* Update modal dialog animation for new centered position [Florian; #674499]
+* calendar: Fix grid lines in RTL locales [Florian; #679879]
+* Integrate IBus with keyboard indicator [Rui; #641531]
+* Move ibus status icon under keyboard [Matthias]
+* gdm: port from libgdmgreeter to libgdm [Ray; #676401]
+* Misc bug fixes and cleanups [Antoine, Cosimo, Giovanni, Jasper, Rico;
+ #678978, #672790, #679847, #679944]
+
+Contributors:
+ Jean-Philippe Braun, Clemens Buchacher, Giovanni Campagna, Cosimo Cecchi,
+ Matthias Clasen, Hans de Goede, Guillaume Desmottes, Stefano Facchini,
+ Antoine Jacoutot, Rui Matos, Florian Müllner, Marius Rieder, Ana Risteska,
+ Jasper St. Pierre, Rico Tzschichholz, Colin Walters, Dan Williams
+
+Translations:
+ Matej Urbančič [sl], Khaled Hosny [ar], Nguyễn Thái Ngọc Duy [vi],
+ Nilamdyuti Goswami [as], Alexander Shopov [bg], Ivaylo Valkov [bg],
+ Daniel Mustieles [es], Kjartan Maraas [nb,nn], Yaron Shahrabani [he],
+ Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW], Ihar Hrachyshka [be],
+ Praveen Illa [te]
+
+3.5.3
+=====
+* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
+* messageTray: Don't show non urgent notifications while in fullscreen
+ [Adel; #677590]
+* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
+* extensionSystem: Prepare for extension updating system [Jasper; #677586]
+* appDisplay: Don't show apps in NoDisplay categories in the All view
+ [Jasper; #658176]
+* st: Trigger theme updates on resolution changes [Florian; #677975]
+* Always enable a11y [Bastien; #678095]
+* telepathyClient: ignore invalidated channels [Guillaume; #677457]
+* shell-app: Update app menu if necessary [Florian; #676238]
+* Enable the Screen Reader menu item [Matthias; #663256]
+* Disable unredirection when a modal operation is active [Giovanni]
+* Make folks optional [Colin]
+* Improve mount-operation support [Cosimo]
+ - Fix exception when showing password entry [#678428]
+ - Close the password entry on operation abort [#673787]
+ - autorun: Don't allow autorun for things we mount on startup [#660595]
+ - Turn passphrase prompt into a dialog [#674962]
+ - Implement org.Gtk.MountOperationHandler [#678516]
+* Network menu improvements
+ - Sort Wifi networks by strength [Giovanni; #658946]
+ - Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
+* clock: Switch to using GnomeWallClock [Colin; #657074]
+* remoteSearch: Allow to reference .desktop file for Title/Icon
+ [Florian; #678816]
+* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
+* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
+ #658955, #677497, #678396, #678502]
+* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
+
+Contributors:
+ Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
+ Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
+ Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
+
+Translations:
+ Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
+ Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
+ Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
+
+3.5.2
+=====
+* main: Move 'toggle-recording' binding into the shell [Florian; #674377]
+* popupMenu: make sure to break the grab when the slider is not visible
+ [Stefano; #672713]
+* st-theme-node-drawing: Don't use GL types [Neil; #672711]
+* Mirror Evolution calendar settings into our own schema [Owen; #674424]
+* shell-network-agent: don't crash if a request isn't found [Dan; #674961]
+* notificationDaemon: Match app based on WM_CLASS [Jasper; #673761]
+* NetworkMenu: use network-offline while loading [Giovanni; #674426]
+* lookingGlass: Remove the Errors tab [Jasper; #675104]
+* searchDisplay: Reset keyboard focus after displaying async results
+ [Rui; #675078]
+* gdm: don't fail if fprintd is unavailable [Ray; #675006]
+* messageTray: Fix scrolling up [Jasper; #661615]
+* main: Close the recorder instead of pausing it [Rui; #675128]
+* Accessibility [Alejandro]
+ - Use the proper label_actor for date menu on top panel [#675307]
+ - Set the proper role/label_actor for SearchResult.content [#672242]
+ - do not expose a label text if 'hidden' style class is used [#675341]
+* Magnifier: Add brightness and contrast functionality [Joseph; #639851]
+* theme: use a smaller border-radius for top bar [Jakub; #672430]
+* placeDisplay: use new bookmark file location [Matthias; #675443]
+* port all synchronous search providers to the async API [Jasper, Rui; #675328]
+* NetworkAgent: disallow multiple requests for the same connection/setting
+ [Giovanni; #674961]
+* userMenu: Update to latest mockups [Florian; #675802]
+* util: Don't double-fork when spawning from Alt-F2 [Colin; #675789]
+* messageTray: Make Source usable without subclassing [Jasper; #661236]
+* panel: Check for appMenu button's reactivity before opening [Florian; #676316]
+* Fix formatting of bluetooth passkey [Florian; #651251]
+* notificationDaemon: Filter out file-transfer notifications [Jasper; #676175]
+* Don't use global.log() [Jasper; #675790]
+* Fix broken extension loading in some distributions [Owen, Alexandre; #670477]
+* shell-app: Raise windows in reverse order to preserve the stacking
+ [Rui; #676371]
+* Generalize gdm-mode [Florian; #676156]
+* Switch string formatting to the one inside gjs [Jasper; #675479]
+* extensionUtils: Support subdirectories in getCurrentExtension
+ [Jasper; #677001]
+* panel: Refuse to add (legacy) status icons not part of the session mode
+ [Florian; #677058]
+* Add an initial-setup mode [Matthias; #676697]
+* status/keyboard: Port to the new input sources settings [Rui; #641531]
+* NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330]
+* userMenu: Indicate progress on status changes [Florian; #659067]
+* recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630]
+* searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527]
+* endSessionDialog: Factor out _updateDescription from _updateContent
+ [Alejandro; #674210]
+* Fix empathy's appMenu freezing the shell [Alban; #676447]
+* Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850,
+ #672272]
+* Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico,
+ Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301,
+ #675370, #676347, #676806, #677097]
+
+Contributors:
+ Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes,
+ Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner,
+ Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer,
+ Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz,
+ Colin Walters, Dan Winship, Ron Yorston
+
+Translations:
+ OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko],
+ Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR],
+ Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el],
+ Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id],
+ Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja]
+
+3.4.1
+=====
+* Fix crash that occurred when an icon theme change caused unexpected
+ reentrancy in the icon loading code [Jasper; #673512]
+* Don't show system and other disabled users in the GDM user list
+ [Adel; #673784]
+* Make gnome-shell-calendar-server initialize GTK+ so it can display
+ password prompts if needed [#673608; Owen, Rico]
+* Adapt to Mutter API change for keybinding addition [Florian; #673014]
+* Fix crash when an extension was installed as both a user extension
+ and a system extension [#673613; Jasper]
+* Fix bug where chat entry could end up partially offscreen [Joost, 661944]
+* Fix problem where icons weren't updating when theme was changed
+ [#672941; Florian]
+* Look for Evolution calendar settings in GSettings, not GConf [#673610; Owen]
+* Add <super>F10 for the application menu [#672909; Florian]
+* Fix %Id format characters to work in translations [#673106; Cosimo]
+ (were already used in fa translation)
+* Fix error when NetworkManager restarts [#673043; Giovanni]
+* Improve efficiency of overview redraws by working around Clutter issue
+ [Stefano; #670636]
+* Misc bug fixes [Florian, Giovanni, Jasper, Rui, Stefano;
+ #672592, #672641, #672719, #673187, #673233, #673656]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Stefano Facchini, Adel Gadllah, Rui Matos,
+ Florian Müllner, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz,
+ Joost Verdoorn
+
+Translations:
+ Khaled Hosny [ar], Ihar Hrachyshka [be], Alexander Shopov [bg], Gil Forcada,
+ Jordi Serratosa [ca], Petr Kovar [cs], Bruce Cowan [en_GB],
+ Carles Ferrando [ca@valencia], Wolfgang Stöggl [de], Daniel Mustieles [es],
+ Arash Mousavi [fa], Bruno Brouard [fr], Fran Diéguez [gl],
+ Sweta Kothari [gu], Yaron Shahrabani [he], Gabor Kelemen [hu],
+ Shankar Prasad [kn], Žygimantas Beručka [lt], Rudolfs Mazurs [lv],
+ Sandeep Sheshrao Shedmake [mr], Kjartan Maraas [nb], Piotr Drąg [pl],
+ Yuri Myasoedov [ru], Daniel Nylander [se], Matej Urbančič [sl],
+ Miroslav Nikolić [sr], Sasi Bhushan, Praveen Illa [te], Yinghua Wang [zh_CN]
+
+3.4.0
+=====
+* Don't crash when taking screenshots [Jasper; #672775]
+* Fix dialog-resizing problem [Florian; #672543]
+
+Contributors:
+ Florian Müllner, Jasper St. Pierre
+
+Translations:
+ Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be],
+ Alexander Shopov [bg], Marek Černocký [cs], Jiri Grönroos, Timo Jyrinki [fi],
+ Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he],
+ Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk],
+ Mattias Põldaru [et], Changwoo Ryu [ko], Rudolfs Mazurs [lv],
+ Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk],
+ Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.3.92
+======
+* Add shell-dialogs for GNOME Keyring prompts [Stef; #652459, #652460, #671034]
+* When the user returns from idle, bring up the message tray if there were
+ messages while they were away [Marina; #643014]
+* https://live.gnome.org/EveryDetailMatters
+ - Make the workspace thumbnails clickable all the way to the edge of the
+ screen [Stefano; #643319]
+ - Don't slide out the workspace thumbnails if the mouse is over them when
+ entering the overview [Joost, #651092]
+ - Fix placeholder jumps while dragging a dash item [Joost; #651842]
+ - Don't favorite apps if they are dropped back at the same position
+ [Jean-Philippe; #656333]
+ - To avoid confusion, don't allow removing running apps from favorites
+ [Florian; #644853]
+ - Fix creation of new workspaces by dragging application launchers
+ [Stefano; #664202]
+ - Make it easier to drag dash items without triggering the menu
+ [Florian; #637103]
+* Accessibility [Alejandro]
+ - Add StWidget API for easily adding accessible states and setting roles,
+ names [#668366, #667432, #671378]
+ - Set accessibility information on UI elements
+ [#644255, #667432, #668361, #672047, #670308, #670312, #670719, #671404]
+* Improve key-navigation in the overview [Rui, Florian; #663901]
+* Key navigation bug fixes [Rui, Florian; #662493, #663437, #665215, #671998]
+* Honor a 'org.gnome.shell.overrides.dynamic-workspaces' setting that
+ determines whether the workspace count is dynamic and unsaved in GSettings
+ or static and saved. [Florian; #671568]
+* Avoid saving user presence to GSettings when not necessary
+ [Florian; #665701, #668214]
+* Save screencasts in the users Videos/ directory [Adel; #670749]
+ Use a "human readable" filename [Florian, Adel, Ray; #670753]
+* Allow dragging from the empty part of the top panel to unmaximize a window
+ [Florian; #666359]
+* Fix hangs that could occur when switching away to a VT [Ray; #653833]
+* Fix problems with installing from extensions.gnome.org [Giovanni; #671134]
+* Fix locking the screen when suspending via menu [David, Gert; #670820]
+* Fix browser plugin with Konqueror and Opera [Jasper]
+* Fix shell restart not to bring up failure screen [Giovanni; #648384]
+* Reorganize and clean up CSS theming [Allan; #668209]
+* Improve appearance of modal dialogs [Allan, Florian; #670227, #668209]
+* Update the calendar code to use ECalClient [Giovanni; #671177]
+* Update jhbuild script to use the main moduleset [Owen, Will; #668440]
+* StTextureCache: code cleanup, evict unused icons, merge together
+ simultaneous requests for the same icon [Jasper; #670771, #671656, #672273]
+* Clean up St for recent Clutter changes and fix bugs. StContainer and
+ StGroup are removed [Jasper, Florian; #670034, #670640, #670904]
+* Code cleanup [Adel, Jasper, Rui; #613194, #671086, #671103]
+* Misc bug fixes
+ [Adel, Colin G, Cosimo, Florian, Giovanni, Jasper, Marius, Rui, Stefano;
+ #651130, #658946, #667552, #670076, #671001, #670979, #671410, #671411,
+ #671556, #671656, #671657, #672011, #672024, #672240, #672265, #672270,
+ #672321, #672326, #672413, #672471]
+
+Contributors:
+ Jean-Philippe Braun, Giovanni Campagna, Cosimo Cecchi, Allan Day,
+ Stefano Facchini, David Foerster, Adel Gadllah, Marius Gedminas,
+ Colin Guthrie, Gert Michael Kulyk, William Lachance, Rui Matos,
+ Florian Müllner, Alejandro Piñeiro, Jan Alexander Steffens,
+ Jasper St. Pierre, Ray Strode, Owen Taylor, Joost Verdoorn, Stef Walter,
+ Marina Zhurakhinskaya
+
+Translations:
+ Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be],
+ Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cs],
+ Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB],
+ Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et],
+ Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi],
+ Bruno Brouard [fr], Fran Diéguez [gl], Sweta Kothari [gu],
+ Yaron Shahrabani [he], Gabor Kelemen [hu], Jiro Matsuzawa [ja],
+ Baurzhan Muftakhidinov [kk], Seong-ho Cho [ko], Žygimantas Beručka [lt],
+ Anita Reitere [lv], Anish A, Praveen Arimbrathodiyil, Mohammed Sadiq [ml],
+ fKjartan Maraas [nb], Wouter Bolsterlee [nl], A S Alam [pa], Piotr Drąg [pl],
+ Duarte Loreto [pt], Jonh Wendell [pt_BR], Yuri Myasoedov [ru],
+ Matej Urbančič [sl], Miroslav Nikolić [sr], Tirumurti Vasudevan [ta],
+ Sasi Bhushan, Krishnababu Krothapalli [te], Daniel Korostil [uk],
+ Nguyễn Thái Ngọc Duy [vi], YunQiang Su, Yinghua Wang [zh_CN],
+ Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.3.90
+======
+
+* Allow other applications to implement search providers via D-Bus
+ [Florian; #663125, #670148]
+* Remove "Recent Items" search, as replaced by Documents search
+ [Florian; #663125]
+* Allow NetworkManager VPN plugins to use a shell-themed dialog
+ [Giovanni; #658484]
+* Port away from deprecated Clutter API [Jasper, Florian, Adel; #670034]
+ - StTooltip is removed
+ - StWidget is now a concrete class and can be instantiated
+ - st_container_destroy_children(), st_box_layout_insert_actor(),
+ and other functions removed in favor of new replacements in Clutter.
+* Use systemd for console/session handling when available [Lennart]
+* Visual improvements to contact search, padding, top panel, checkboxes
+ [Allan, Florian, Jakub; #669489, #669811, #669993]
+* Add a include_cursor parameter to Screenshot and ScreenshotWindow
+ D-Bus methods [Adel; #670086]
+* Add a "FlashArea" D-Bus method to do the screenshot flash without a
+ screenshot [Adel; #669660]
+* Build fixes [Adel, Giovanni, Jasper; #658484, #669637]
+* Misc bug fixes [Adel, Florian, Giovanni, Guillaume, Jasper, Jeff,
+ Marc-Antoine, Stef, Stefano, Will; #642135, #658484, #658908, #667694,
+ #669098, #669921, #669662, #669694, #669776, #669887, #669921, #670005,
+ #670006, #670319, #670418, #670489]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Allan Day, Guillaume Desmottes, Jeff Epler,
+ Stefano Facchini, Adel Gadllah, Florian Müllner, Marc-Antoine Perennou,
+ Jasper St. Pierre, Lennart Poettering, Jakub Steiner, Jasper St. Pierre,
+ Will Thompson, Stef Walter
+
+Translations:
+ Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cs],
+ Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et],
+ Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
+ Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb],
+ A S Alam [pa], Matej Urbančič [sl], Miroslav Nikolić [sr],
+ Praveen Illa [te], Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.3.5
+=====
+
+* Extension system: [Jasper; #668429]
+ http://blog.mecheye.net/2012/02/more-extension-api-breaks/
+ - Add a 'gnome-shell-extension-prefs' application for displaying extension
+ preferences as provided by the extension in a prefs.js file.
+ - Allow launching gnome-shell-extension-prefs from extensions.gnome.org
+ throuhg the browser plugin.
+ - Add ExtensionUtils.getCurrentExtension() for an extension to get a
+ handle to an extension object, to get local imports or paths.
+ - Add an onshellrestart callback to the browser plugin [Jasper; #668517]
+* Screenshots:
+ - Move the screenshot "flash" to the shell [Cosimo; #668618]
+ - Move saving screenshots to a thread [Adel; #652952]
+ - Correctly screenshot rounded decorations [Jasper; #662486]
+* Screen recorder:
+ - Change the default pipeline to favor speed over quality [Owen; #669066]
+ - Drop frames to keep from running the user out of memory [Owen; #669066]
+* Work around a slow implementation of glReadPixels() in the Intel drivers,
+ improving performance for screenshots and the screen recorder.
+ [Owen; #669065]
+* Use Keywords: field in desktop files when search for applications
+ [Florian; #609702]
+* Strip debian- when matching desktop file names [Jasper; #665647]
+* Fix up various problems from CSS background size-addition
+ [Florian, Jasper; #668430, #633462]
+* UI tweaks and behavior fixes
+ [Florian, Giovanni, Stefano; #643867, #666197, #664622]
+* Some improvements to exported accessibility information [Alejando; #667376]
+* Don't show contacts without IM information as offline [Florian; #662685]
+* Don't change status from hidden to extended_away when going idle
+ [Florian; #642408]
+* Cleanups [Emmanuele, Jasper; #662152, #669239]
+* Misc bug fixes [Cosimo, Dan, Florian, Jasper, Rui, Stefano;
+ #633462, #643867, #662213, #662386, #662747, #665000, #665017, #665322,
+ #667371, #667860, #668020, #668517, #668541, #669236]
+
+Contributors:
+ Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
+ Adel Gadllah, Rui Matos, Florian Müllner, Alejandro Piñeiro,
+ Jasper St. Pierre, Owen Taylor, Dan Winship
+
+Translations:
+ Daniel Mustieles [es], Timo Jyrinki [fi], Seán de Búrca [ga],
+ Fran Diéguez [gl], Kjartan Maraas [nb], Wouter Bolsterlee [nl],
+ Danishka Navin [si], Yaron Shahrabani [he], Matej Urbančič [sl],
+ Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.3.4
+=====
+* https://live.gnome.org/EveryDetailMatters
+ - Add "browse" for labels for dash items - once a tooltip is
+ showing, switch to other items without a delay [Seif; #666170]
+ - Always scale down windows in the overview at least to 70% [Vit; #646704]
+ - Fix the new-workspace drop indicator sometimes getting stuck
+ [Stefano; #664201]
+ - Delay rearranging windows in the overview as long as the pointer
+ is over a window [Vit; #645325]
+* Add a GConf => DConf migration file for overridden Mutter settings
+ [Florian; #667636]
+* When a VPN connection is active, show that as the network icon
+ [Giovanni; #665115]
+* Handle the "ExtendedAway" IM status as away, not offline [Guillaume; #667813]
+* Improve the appearance of the labels in "Applications" [Alex; #642392]
+* Adjust for GTK+ and Mutter API changes for application menu [Ryan; #668118]
+* Add section label support to the application menu [Giovanni; #666681]
+* Fix screenshot methods to work again [Cosimo; #667662]
+* Fix several crashers related to updating workspace thumbnails [Owen; #667652]
+* Fix memory management error causing gnome-shell-hotplug-sniffer to crash
+ [Owen; #667378]
+* Build fixes [Emmanuele, Rico; #667864]
+* Code cleanups [Adel; #668087]
+* Misc bug fixes [Colin, Florian, Giovanni, Owen, Xavier; #633028, #658817,
+ #664138, #667881, #668048, #668050]
+
+Contributors:
+ Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Xavier Claessens,
+ Guillaume Desmottes, Stefano Facchini, Adel Gadllah, Alex Hultman,
+ Ryan Lortie, Seif Lotfy, Florian Müllner, Vit Stanislav, Owen Taylor,
+ Rico Tzschichholz, Colin Walters
+
+Translations:
+ Ihar Hrachyshka [be], Alexander Shopov [bg], Arash Mousavi [fa],
+ Jiri Grönroos, Timo Jyrinki [fi], Fran Diéguez [gl], Kjartan Maraas [nb],
+ Yuri Myasoedov [ru], Matej Urbančič [sl], Nguyễn Thái Ngọc Duy [vi]
+
+3.3.3
+=====
+* https://live.gnome.org/EveryDetailMatters
+ - Stop flashing the window labels on actions in overview [Zan; #644861]
+ - Improve the look of window captions in the overview [Marc; #664487]
+ - Move dash tooltips beside the icon [Seif, Stefano; #666166]
+* Support application menus exported from applications via new GLib API
+ and D-Bus protocol. [Giovanni, Colin, Matthias, Cosimo]
+* For removable device prompts, show "Open with Rhythmbox], rather
+ than "Open with Rhythmbox Music Player' [Florian; #664561]
+* Switch to activating the message tray only with a hot corner rather
+ than a full row of pixels, allowing mouse events to apps [Rui; #663366]
+* Fully handle the case where the workspaces-only-on-primary
+ GSetting is set to false [Florian; #652580]
+* Add support for background-size CSS property to St [Quentin; #633462]
+* Port to new GJS Lang.Class framework [Giovanni; #664436]
+* Finish port to GDBus [Giovanni; #664436]
+* Stop using the deprecated Clutter default stage [Florian, Jasper; #664052]
+* Fix bugs that kept browser plugin from working in WebKit-based browser
+ [Jasper; #666444]
+* Fix typo that made uninstalling extensions not work [Jasper]
+* Fix crash in browser plugin if shell is not run [Jürg]
+* Reintroduce piscine paschal ovum [Giovanni; #666606]
+* Network menu bug fixes
+ Giovanni; #664124, #665194, #665680, #666429, #666614]
+* Misc bug fixes [Florian, Jasper, Jonny, Marina, Ron; #647587, #659272,
+ #664138, #665261, #666020, #666243]
+* Build fixes [Owen]
+
+Contributors:
+ Jürg Billeter, Giovanni Campagna, Stefano Candori, Cosimo Cecchi,
+ Matthias Clasen, Zan Dobersek, Quentin Glidic, Jonny Lamb, Ryan Lortie,
+ Seif Lotfy, Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre,
+ Marc Plano-Lesay, Owen Taylor, Colin Walters, Ron Yorsten,
+ Marina Zhurakhinskaya
+
+Translations:
+ Petr Kovar [cs], Kris Thomsen [dk], Daniel Mustieles [es],
+ Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
+ Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
+ Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
+ Matej Urbančič [sl], Praveen Illa [te], Muhammet Kara [tr],
+ Daniel Korostil [uk], Aron Xu [zh_CN]
+
+3.3.2
+=====
+* Port D-Bus usage in the shell to GDBus [Giovanni, Marc-Antoine, Florian,
+ Jasper, Matthias; #648651, #658078, #663902, #663941]
+* Message tray
+ - Add right-click option to chats to mute the conversation [Ana; #659962]
+ - Don't steal the focus when popping up under the pointer [Rui; #661358]
+* Looking Glass
+ - Add alt-Tab completion [Jason; #661054]
+ - Show errors from extensions in the extensions tab [Jasper; #660546]
+ - Allow switching tabs with <Control>PageUp/PageDown
+ - Theme consistently with the rest of the shell [Jason; 650900]
+* Extension system
+ - Don't try to load disabled extensions at all [Jasper; #661815, #662704]
+ - Enable and disable plugins in a consistent order [Jasper; #661815, #662704]
+ - Add options to enable/disable extensions to gnome-shell-extension-tool
+ [Jasper; #661815]
+* Adapt to Mutter change to GSettings [Florian, Matthias; #663429]
+* Allow creating a new workspace by dragging a window or launcher in the
+ middle of two existing ones [Jasper; #646409]
+* Allow using Alt-Tab while during drag-and-drop and other operations
+ that grab the pointer [Adel; #660457]
+* Do a better job of finding the right user to authenticate
+ as when showing a PolKit dialog [Matthias; #651547]
+* Control the D-Bus Eval() method by the developer-tools GSetting which
+ is used for looking glass and screen recorder. [Jasper; #662891]
+* Fix browser plugin to work under WebKit-based browser [Jasper; #663823]
+* Fix certain stacking issues with alt-Tab [Jasper; #660650]
+* Fixes for GLib deprecations [Jasper; #662011]p
+* Fixes for GTK+ deprecations [Florian, Rico; #662245]p
+* Fixes for Clutter deprecations [Jasper; #662627]
+* Visual improvements and UI tweaks [Florian, Jakub, Jasper;
+ #662800, #658096, #662226]
+* Hard-code "Home" as the name for the home dir, rather than looking
+ it up via GSettings; avoids schema dependency [Cosimo; #559895]
+* Don't show "Switch User" on single user machines [Florian; #657011]
+* Generate documentation for St toolkit [Florian]
+* Improve marking of strings for translation [Matthias, Piotr; #658664]
+* Networking menu bug fixes [Giovanni; #650007, #651378, #659277, #663278]
+* Code cleanups and leak fixes to StTextureCache
+ [Jasper, Florian; #660968, #662998]
+* Code cleanups [Adel, Florian, Jasper; #662238, #663584]
+* Build fixes [Adel, Colin, Florian, Ming Han]
+* Misc bug fixes [Adel, Florian, "Fry", Jasper, Giovanni, Ray, Rui, Stefan;
+ #660520, #661029, #661231, #661623, #661921, #662235, #662236, #662502,
+ #662394, #662799, #662969, #663175, #663277, #663815, #663891, #662967]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Piotr Drąg, Adel Gadllah,
+ Rui Matos, Florian Müllner, Marc-Antoine Perennou, Ana Risteska,
+ Jason Siefken, Jakub Steiner, Ray Strode, Jasper St. Pierre, Ming Han Teh,
+ Rico Tzschichholz, Colin Walters, Stefan Zwanenburg
+
+Translation:
+ Alexander Shopov [bg], Marek Černocký [cs], Mario Blättermann [de],
+ Kostas Papadimas [el], Bruce Cowan [en_GB], Kristjan Schmidt [eo],
+ Jorge González, Daniel Mustieles, Benjamín Valero Espinosa [es],
+ Mattias Põldaru [et], Arash Mousavi [fa], Ville-Pekka Vainio [fi],
+ Fran Diéguez [gl], Yaron Shahrabani [he], Hideki Yamane [ja],
+ Algimantas Margevičius [lt], Kjartan Maraas [nb], Daniel Nylander [se],
+ Matej Urbančič [sl], Praveen Illa [te], Muhammet Kara [tr],
+ Nguyễn Thái Ngọc Duy [vi], Cheng-Chia Tseng [zh_HK, zh_TW]
+
+3.2.1
+=====
+* Restore the IM state on startup - if you were available in when you logged
+ out, then you'll be set available again when you log in.
+ [Florian; #65902, #661485]
+* Improve searching for contacts in the overview: search more fields,
+ show a more meaningful name, require that all search terms match.
+ [Florian, Matthias; #660580]
+* Improve search for applications in the overview: take frequency into
+ account and tweak match algorithm [Florian; #623372]
+* Remove the "Show Password" switch from network password prompts, and
+ move the functionality to a right-click menu [Florian; #658948]
+* Add context menus with Cut/Paste options to most entries [Florian; #659275]
+* On screen keyboard:
+ - Show the keyboard immediately when it's turned enabled [Dan; #659743]
+ - Fix problem where keyboard would hide when starting to type
+ in the search entry [Nohemi; #661340]
+ - Fix problem with keyboard hiding when selected accented characters
+ [Nohemi; 661707]
+* Login mode:
+ - Allow hitting Enter to select the first user [Ray; #657996]
+ - Fix flicker of a fingerprint prompt that could show up [Ray; #660492]
+ - Fix password bullets vanishing during login [Ray; #657894]
+ - Misc bug fixes and visual tweaks [Ray; #659763, #660919, #661479]
+* Display a caps-lock warning in password entries [Florian; #660806]
+* Show the state of installed extensions in Looking Glass [Jasper; #660494]
+* Load user extensions after system ones [Jasper; #661815]
+* Fix problem with many applications showing extra-large icons in
+ notifications [Marina; #659158]
+* Fix a problem where alt-Tab had trouble tracking the current
+ application with certain applications such as Emacs. [Dan; #645026]
+* Fix confusion between different users avatar images [Florian; #660585]
+* Remove behavior where you could switch workspaces by bumping
+ a dragged window in the overview against a screen edge; it was
+ leftover and just confusing. [Florian; #660838]
+* Fix long-standing bug where the Dash in the overview could end up mis-sized
+ and run off the screen [Florian; #649248]
+* Fix automatic launching of applications when media is inserted
+ [Cosimo; #660821]
+* Fix handling of vertically stacked monitors with NVIDIA drivers
+ [Florian; #661387]
+* Translation marking fixes [Jasper, Wouter; #660600]
+* Code cleanups and warning fixes [Adel, Dan, Florian, Jasper;
+ #659822, #659940, #660122, #660358, #660968, #661231]
+* Small memory leak fixes [Florian, Jasper; #661231]
+* Misc bug fixes [Adel, Florian, Jasper; #659274, #659861, #660166, #660310,
+ #660397, #660608, #660606, #660674, #660774. #660848, #661151, #661617]
+
+Contributors:
+ Wouter Bolsterlee, Cosimo Cecchi, Matthias Clasen, Nohemi Fernandez,
+ Adel Gadllah, Florian Müllner, Jasper St. Pierre, Ray Strode, Dan Winship,
+ Marina Zhurakhinskaya
+
+Translations:
+ Tiffany Antopolski [eo], Xandru Armesto [ast], Alexander Shopov,
+ Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
+ Mario Blättermann, Paul Seyfert [de], Bruce Cowan [en_GB],
+ Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Bruno Brouard [fr],
+ Seán de Búrca [ga], Fran Diéguez [gl], Gabor Kelemen [hu], Luca Ferretti [it],
+ Takayuki Kusano [ja], Changwoo Ryu [ko], Erdal Ronahi [ku],
+ Algimantas Margevičius [lt], Rudolfs Mazurs [lv], Wouter Bolsterlee [nl],
+ Piotr Drąg [pl], Adorilson Bezerra [pt_BR], Yuri Myasoedov [ru],
+ Matej Urbančič [sl], Daniel Nylander [sv], Miroslav Nikolić [sr, sr@latin],
+ Tirumurti Vasudevan [ta], Krishnababu Krothapalli [te], Daniel Korostil [uk],
+ Nguyễn Thái Ngọc Duy [vi], YunQiang Su [zh_CN]
+
+3.2.0
+=====
+* Prevent the fallback on-screen keyboard from showing up while
+ GNOME Shell is running [Dan, #659865]
+* Disable code to reposition windows around the on-screen keyboard;
+ it wasn't finished or working properly. [Dan; #659643]
+* Fix interaction between on-screen keyboard and notifications
+ [Dan; #658603]
+* Fix menu-sizing problems in right-to-left locales. [Florian; #659827]
+* Update chat icons in the message tray when an avatar image changes
+ [Marina; #659768]
+* Fix problem with empty notification bubbles being left [Marina; #659862]
+* Fix problem with chat notifications bouncing when new messages come in.
+ [Marina; #659768]
+* Fix bug that was causing SIP calls to automatically be accepted in some
+ circumstances [Guillaume; #660084]
+* Fix string that should have been marked translatable [Frédéric]
+* Fix a crash that could happen during CSS transitions [Florian; #659676]
+* Build fixes [Colin, Florian]
+
+Contributors:
+ Guillaume Desmottes, Florian Müllner, Frédéric Péters, Colin Walters,
+ Dan Winship, Marina Zhurakhinskaya
+
+Translations:
+ Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
+ Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
+ Petr Kovar [cs], Mario Blättermann [de], Kris Thomsen [dk],
+ Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
+ Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
+ Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
+ Andika Triwidada [id], Jiro Matsuzawa [ja], Changwoo Ryu [ko],
+ Rudolfs Mazurs [lv], Aurimas Černius [lt], Kjartan Maraas [nb],
+ A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Djavan Fagundes,
+ Rodolfo Ribeiro Gomes, Gabriel F. Vilar [pt_BR], Yuri Myasoedov [ru],
+ Daniel Nylander [se], Martin Srebotnjak [sl], Michal Štrba [sv],
+ Krishnababu Krothapalli, Praveen Illa [te], Cheng-Chia Tseng [zh_KH, zh_TW]
+
+3.1.92
+======
+
+* Login screen
+ - Add the ability to set a logo at the top of the user list [Ray; #658062]
+ - Add fingerprint reader support [Ray; #657823]
+ - Add a power button offering the choice of Suspend/Restart/Power off
+ [Ray; #657822]
+ - Remove the option to view the current keyboard layout [Matthias; #659164]
+ - Make Control-Alt-Tab work for full keyboard access [Ray; #659177]
+* Frequently initiate a full garbage collection; Spidermonkey isn't very good
+ at tracking the amount of resources we have allocated so this hopefully will
+ improve memory usage without affecting performance too much [Colin; #659254]
+* Stop adding a notification when the network connection is lost
+ [Colin; #658954]
+* When disabling notifications; display a notification
+ "Your chat status will be set to busy" [Florian; #652718]
+* Fix keynav in network dialogs [Florian; #659133]
+* Improve calendar styling [Sean; #641135, #651299]
+* Shrink padding around panel buttons for narrow screens [Dan; #651299]
+* Allow enabling the onscreen keyboard through the accessibility menu
+ [Dan; #612662]
+* Fix problem that was causing VPN secret dialogs to be delayed before showing
+ [Florian; #658484]
+* Make custom-keybindings for the window switcher that don't use alt
+ work correctly [Florian; #645200]
+* Fix duplicate application icons in the Activities Overview [Colin; #659351]
+* Bug fixes for dimming windows with attached modal dialogs
+ [Jasper, Owen; #659302, 659634]
+* Add build-time support for BROWSER_PLUGIN_DIR environment variable
+ [Vincent; #659123]
+* Build fixes [Vincent; #659194]
+* Code cleanups and test cases
+ [Adel, Dan, Florian, Jasper; #651299, #658092, #658939]
+* Misc bug fixes
+ [Adel, Colin, Cosimo, Dan, Florian, Giovanni, Jasper, Ray, Xavier;
+ #651299, #652837, #657249, #658004, #658150, #658239, #658469, #658598,
+ #658605, #659050, #659159, #659210, #659270, #659370, #659633]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Xavier Claessens, Matthias Clasen,
+ Rui Matos, Florian Müllner, Jasper St. Pierre, Owen Taylor,
+ Vincent Untz, Colin Walters, Sean Wilson, Dan Winship
+
+Translations:
+ Ihar Hrachyshka [be], Alexander Shopov, Ivaylo Valkov [bg],
+ Mario Blättermann [de], Jorge González, Daniel Mustieles [es],
+ Arash Mousavi [fa], Ville-Pekka Vainio [fi], Fran Dieguez [gl],
+ Sweta Kothari [gu], Gabor Kelemen [hu], Jiro Matsuzawa [ja],
+ Luca Ferretti [it], Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa],
+ Piotr Drąg [pl], Duarte Loreto [pt], Yuri Myasoedov [ru],
+ Daniel Nylander [se], Matej Urbančič [sl], Miroslav Nikolić [sr, sr@latin],
+ Michal Štrba [sv], Tirumurti Vasudevan [ta], Phương Lê Hoàng [vi],
+ Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.1.91.1
+========
+
+* Add a browser plugin - this plugin, tied to extensions.gnome.org,
+ allows users to download and install shell extensions, and enable,
+ disable, and uninstall extensions they already have installed.
+ [Jasper; #658070, #658612]
+* Improve adding links to URLs in notifications [Dan; #636252]
+* Remove "connection lost" notifications after reconnecting [Giovanni; #658049]
+* Hide the onscreen keyboard when leaving a text entry [Dan; #658591]
+* Fixes for translated strings [Florian; #639987, #644097, #645037]
+* Bug fixes for network menu [Florian; #658492]
+* Code cleanup [Dan; #646934]
+* Build fixes [Javier, Rico]
+* Misc bug fixes [Emmanuele, Florian, Jasper, Marina, Matthias, Ray;
+ #652837, #658423, #658503, #658525, #658562, #658624, #658640, #658983]
+
+Conributors:
+ Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Javier Jardón,
+ Florian Muellner, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
+ Dan Winship, Marina Zhurakhinskaya
+
+Translations:
+ Ihar Hrachyshka [be], Bruce Cowan [en_GB], Jorge González,
+ Daniel Mustieles [es], Timo Jyrinki [fi], Bruno Brouard, Luc Guillemin,
+ Claude Paroz, Luc Pionchon [fr], Fran Dieguez [gl], Rajesh Ranjan [hi],
+ Andika Triwidada [id], Luca Ferretti [it], Changwoo Ryu [ko],
+ Rudolfs Mazurs [lt], Kjartan Maraas [nb], Manoj Kumar Giri [or],
+ A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Henrique P. Machado,
+ Gabriel F. Vilar [pt_BR], Daniel Nylander [se], Matej Urbančič [sl],
+ Tirumurti Vasudevan [ta], Yinghua Wang [zh_CN],
+ Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.1.91
+======
+
+* Fix problem with applications vanishing from alt-Tab when
+ desktop files change. [Colin; #657990]
+* Fix interaction of on-screen keyboard with run-dialog and
+ Looking Glass console [Dan; #657986]
+* Add public API for adding and removing search providers
+ [Philippe; #657548, #658113]
+* Allow changing IM status with scroll wheel [Florian; #657973]
+* Limit volume slider to 100% [Bastien; #657607]
+* Change "Do Not Disturb" to "Notifications" in user menu [Florian; #652718]
+* Switch browser in default favorites to Epiphany [Colin; #650616]
+* Misc bug fixes [Dan, Florian, Jasper, Marc-Antoine, Rui;
+ #649631, #655069, #656142, #657703, #657759, #658007, #658065, #658176]
+
+Contributors:
+ Rui Matos, Florian Müllner, Philippe Normand, Marc-Antoine Perennou,
+ Jasper St. Pierre, Colin Walters, Dan Winship
+
+Translations:
+ Ihar Hrachyshka [be], Mario Blättermann [de], Kris Thomsen [da],
+ Jorge González [es], Arash Mousavi [fa], Fran Dieguez [gl],
+ Takayuki Kusano [ja],Aurimas Černius [lt], Kjartan Maraas [nb], A S Alam [pa],
+ Stas Solovey [ru], Daniel Nylander [se], Tirumurti Vasudevan [ta],
+ Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.1.90.1
+========
+
+* Fix typo that was breaking the "Login Screen" mode [Marc-Antoine]
+* Fix build with new gobject-introspection [Dan]
+* Use a better icon for removable devices [Cosimo; #657757]
+* Add support for asynchronous search provides [Philippe, Jasper, Seif; #655220]
+* Misc bug fixes [Alex, Guillaume, Jasper; #657657, #657696]
+* Misc build fixes [Adel; #657697]
+
+Contributors:
+ Cosimo Cecchi, Guillaume Desmottes, Adel Gadllah, Alexander Larsson, Seif Lotfy,
+ Philippe Normand, Marc-Antoine Perennou, Jasper St. Pierre, Dan Winship
+
+Translations:
+ Jorge González, Daniel Mustieles [es], Stas Solovey [ru]
+
+3.1.90
+======
+* Add an on-screen keyboard that uses Caribou as a backend
+ [Nohemi, Dan; #612662]
+* Allow searching for people in the overview using libfolks
+ as the backend [Morten; #643018]
+* Add a "Login Screen" mode to be used when GDM is running; this
+ mode has a stripped down user interface, and also contains the
+ code to display the user list and authentication. [Ray; #657082]
+* Rework user menu to separate out "Do Not Disturb" from the IM
+ status and to visually match GNOME Contacts. [Florian; #652837]
+* Implement displaying images such as cover-art in notifications
+ [Neha, Marina; #621009]
+* Support default actions for notifications [Florian; #655818]
+* Networking
+ - Stop using nm-applet for dialogs; do them as proper system modal
+ dialogs in the shell code. [Giovanni; #650244]
+ - Fix handling of hidden access points [Giovanni; #646454]
+* Telepathy integration
+ - Support subscription requests [Guillaume, Xavier; #653941]
+ - Notify on account connection errors [Alban, Jasper, Xavier; #654159]
+ - Allow approving file transfers [Guillaume; #653940]
+ - Improve styling of messages [Jasper; #640271]
+* Extension system [Jasper; #654770]
+ - Support live enabling and disabling of extensions
+ - Add the ability to install extensions from HTTP
+ - Enhance D-Bus interface for controlling extensions
+ - Collect errors separately for each extension
+* Add Main.panel.addToStatusArea for extension convenience
+ [Giovanni, Jasper, Marc-Antoine; #653205]
+* Port to the new gnome-menus API. Clean up and speed up
+ application information loading [Colin; #648149, #656546]
+* Use the accountsservice library rather than cut-and-pasted GDM code
+ [Florian; #650893]
+* Add a D-Bus interface to take a screenshot; this will avoid various race
+ conditions with the current gnome-screenshot approach [Adel; #652952]
+* Show numeric indicators to distinguish duplicate keyboard names
+ [Giovanni; #650128]
+* Add GNOME Documents to the favorites list [Adel; #657520]
+* Update the clock immediately on resume from suspend [Colin; #656403]
+* Remove animation support from StAdjustment [Ray; #657082]
+* Support configuration of calendar applications via gsettings
+ [Tassilo; #651190]
+* Don't fade in alt-Tab - wait a bit and show it instantly [Rui; #652346]
+* Darken workspace background on all workspaces [Rui; #656433]
+* Improve detection of the starting day of the week [Florian; #649078]
+* Add StButtonAccessible [Alejandro]
+* Visual tweaks to match mockups
+ [Allan, Dan, Jasper, Marina; #640271, #655627, #655428, #656732]
+* Misc bug fixes [Dan, Florian, Giovanni, Guillaume, Jasper, Jeremy, Rui;
+ #645708, #646761, #653119, #654398, #656125, #654707, #654898, #654638,
+ #656335, #657111]
+* Code cleanups [Colin, Dan, Guillaume, Ray;
+ #652718, #654639, #648651, #655813, #657082]
+* String tweaks [Jasper, Jeremy; #652984, #640271]
+* Build fixes [Jasper, Nohemi; #644275, #655812]
+
+Contributors:
+ Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Alban Crequy,
+ Guillaume Desmottes, Allan Day, Neha Doijode, Nohemi Fernandez,
+ Tassilo Horn, Rui Matos, Morten Mjelva, Florian Müllner, Alejandro Piñeiro,
+ Jasper St. Pierre, Ray Strode, Colin Walters, Dan Winship,
+ Marina Zhurakhinskaya
+
+Translations:
+ Ivaylo Valkov [bg], Mario Blättermann [de], Diego Escalante Urrelo,
+ Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Fran Dieguez [gl],
+ Yaron Shahrabani [he], Andika Triwidada, Wibiharto [id],
+ Aurimas Černius [lt], Umarzuki Bin Mochlis Moktar [ml], Kjartan Maraas [nb],
+ A S Alam [pa], Daniel Nylander [se], Ngô Chin, Nguyễn Thái Ngọc Duy [vi],
+ Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.1.4
+=====
+* Take over inserted media handling and autorun from gnome-session [Cosimo]
+* Message Tray
+ - Display a count of unread notifications on icons
+ [Jasper, Guillaume; #649356, #654139]
+ - Only remove icons when the sender quits from D-Bus, not when it
+ closes its last window [Neha, Marina; #645764]
+ - Solve problems switching chats between shell and Empathy
+ [Guillaume; #654237]
+ - Fix handling of bad GMarkup in messages [Dan; #650298]
+ - Never show notifications when the screensaver is active [Dan; #654550]
+* Telepathy integrationpp
+ - Implement Telepathy Debug interface to enable empathy-debugger
+ [Guillaume; #652816]
+ - Allow approving room invitations, and audio/video calls
+ [Guillaume; #653740 #653939]
+ - Send typing notifications [Jonny; #650196]
+* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
+* Make control-Return in the overview open a new window [Maxim]
+* Delay showing the alt-Tab switcher to reduce visual noise when
+ flipping between windows [Dan; #652346]
+* When we have vertically stacked monitors, put the message tray
+ on the bottom one [Dan; #636963]
+* Fix various problems with keynav and the Activities button
+ [Dan; #641253 #645759]
+* Ensure screensaver is locked when switching users [Colin; #654565]
+* Improve extension creation tool [Jasper; #653206]
+* Fix compatibility with latest GJS [Giovanni; #654349]
+* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
+* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
+ #647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
+ #654269 #654527 #655446]
+* Build fixes [Florian, Siegfried; #654300]
+
+Contributors:
+ Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
+ Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
+ Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
+ Dan Winship, Marina Zhurakhinskaya
+
+Translations:
+ Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
+ Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
+ Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
+ Michal Štrba, Matej Urbančič [sl]
+
+3.1.3
+=====
+* Fix problem with "user theme extension" breaking the CSS for other
+ extensions [Giovanni; #650971]
+* Telepathy IM framework integration
+ - Switch to using telepathy-glib rather than talking to
+ Telepathy via D-Bus [Guillaume, Jasper; #645585, #649633, #651138, #651227]
+ - Acknowledge messages when the user clicks on them [Guillaume, #647893]
+ - Fix problem with telepathy icon blinking for incoming messages
+ even though the user has been notified of them [Guillaume; #643594]
+* Networking
+ - keep wirelesss networks in predictable order [Giovanni; #646580, #652313]
+ - Show unmanaged devices in the menu [Giovanni; #646946]
+ - Fix overflow when too many VPN connections [Giovanni; #651602]
+* Bluetooth
+ - Show "hardware disabled" when disabled by rfkill [Giovanni; #648048]
+ - Fix bug updating status of devices [Giovanni; #647565]
+* LookingGlass console:
+ - Add a "Memory" tab [Colin; #650692]
+ - Make escape work from any page [Dan Winship; #647303]
+ - Provide a way to refer to panel items as, e.g.,
+ Main.panel._activities [Dan Winship; #646915]
+* User menu
+ - Fix problem with suspend menu option locking the screen even when the user
+ disabled that. [Florian; #652327]
+ - Hide "power off..." option if shutdown is disabled via PolicyKit
+ [Florian; #652038]
+* Track changes to WM_CLASS (fixes problems with LibreOffice tracking)
+ [Colin; #649315]
+* Remove app tracking workarounds for Firefox and LibreOffice [Colin; #651015]
+* Use upstream gettext autoconfigury rather than glib version [Javier; #631576]
+* Show messages in the message tray when an application is fullscreen
+ [Dan Winship; #608667]
+* Don't autohide the workspace pager if there is more than one workspace
+ [Florian; #652714, #653078, #653142]
+* Don't always slide out the workspace pager at drag begin [Florian; #652730]
+* Only offer to remove a favorite app when dragging it's icon [Owen; #642895]
+* Allow dropping an icon anywhere on a workspace [Adel; #652079]
+* st-scroll-view: Make the fade effect and offset themable [Jasper; #651813]
+* Obey the user's preference when running an application in a terminal
+ from the run dialog [Florian; #648422]
+* Consistently exit overview when launching external applications
+ [Colin; #653095]
+* Adapt to changes in GJS for how GObject APIs are bound
+ [Alex, Colin, Florian, Jasper, Marc-Antoine; #649981, #652597]
+* Fix problems with scrolling in overflow for alt-Tab switcher
+ [Dan Winship, Adel; #647807]
+* Mark relationships between labels and actors for accessibility [Alejandro]
+* Add org.gnome.shell.enabled-extensions complementing disabled-extensions
+ GSetting [Tassilo; #651088]
+* Visual tweaks [Jakub, Jasper; #646261, #652715]
+* Switch to building against clutter-1.7 with independent Cogl [Adel; #653397]
+* Code cleanups [Colin, Dan Winship, Florian; #633620, #645031, #648755, #648758,
+ #648760, #649203, #649517, #650317, #652730]
+* Memory leak fixes [Colin, Maxim; #649508, #650934]
+* Build Fixes [Colin, Dan Winship, Florian, Ionut, Morten, Owen, Sean; #647395,
+ #648006, #650869, #653199, #653275
+* Miscellaneous bug fixes [Adam, Adel, Dan Williams, Dan Winship, Florian,
+ Ionut, Jasper, Maxim, Ray; #620105, #639459, #641570, #642793, #643513,
+ #645848, #646919, #647186, #648305, #648410, #648562, #648894, #649001,
+ #645990, #647893, #647907, #651012, #651086, #651606, #651569, #651866,
+ #652388, #653511]
+
+Contributors:
+ Ionut Biru, Giovanni Campagna, Guillaume Desmottes, Adam Dingle,
+ Maxim Ermilov, Adel Gadllah, Tassilo Horn, Javier Jardón, Jonny Lamb,
+ Alexander Larsson, Rui Matos, Morten Mjelva, Florian Müllner,
+ Marc-Antoine Perennou, Alejandro Piñeiro, Jasper St. Pierre, Jakub Steiner,
+ Ray Strode, Owen Taylor, Colin Walters, Dan Williams, Sean Wilson, Dan Winship
+
+Translations:
+ Daniel Martinez Cucalon [ar], Ihar Hrachyshka [be], Carles Ferrando,
+ Gil Forcada, Sílvia Miranda [ca], Kristjan Schmidt [eo], Jorge González,
+ Daniel Mustieles [es], Seán de Búrca [ga], Fran Diéguez [gl],
+ Yaron Shahrabani [he], Kjartan Maraas [nb], Misha Shnurapet,
+ Yuri Myasoedov [ru], Daniel Nylander [se], Peter Mráz [sk],
+ Matej Urbančič [sl], Krishnababu Krothapalli [te], Daniel Korostil [uk],
+ Aron Xu [zh_CN]
+
+3.0.2
+=====
+* Network Menu [Dan Williams]
+ - Fix connecting to WPA2 Enterprise access points
+ Fixes https://bugzilla.gnome.org/show_bug.cgi?id=648171
+ - Show the mobile broadband wizard when selecting 3G network
+ Fixes https://bugzilla.gnome.org/show_bug.cgi?id=649318
+ - Miscellaneous bug fixes
+ 648648, 650124
+* Fix duplicate icons in the application browser [Owen]
+ https://bugzilla.gnome.org/show_bug.cgi?id=648739
+* Make clicking anywhere on the volume icon slider work [Giovanni]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646660
+* Fix a case where activating and clicking the hot corner
+ at the same time could result in immediately leaving the
+ overview [Rui]
+ https://bugzilla.gnome.org/show_bug.cgi?id=649427
+* Fix a case where applications became misordered in Alt-Tab [Jasper]
+ https://bugzilla.gnome.org/show_bug.cgi?id=643302
+* Fix a bug where messages you send could show up in
+ notifications as if someone else sent them [Jonny]
+ https://bugzilla.gnome.org/show_bug.cgi?id=650219
+* Memory leak fixes [Colin, Maxim]
+ 642652, 649508, 649497
+* Miscellaneous minor bug fixes [Adel, Christopher, Jasper]
+ 649596, 648765, 648983, 649632
+
+Contributors:
+ Christopher Aillon, Giovanni Campagna, Maxim Ermilov,
+ Adel Gadllah, Jonny Lamb, Rui Matos, Jasper St. Pierre,
+ Owen Taylor, Colin Walters, Dan Williams
+
+Translations:
+ Arash Mousavi [fa], Seán de Búrca [ga], Timo Jyrinki [fi],
+ Sigurd Gartmann [nb], Daniel Nylander [se], Peter Mráz [sl],
+ Abduxukur Abdurixit [ug], Nguyễn Thái Ngọc Duy [vi]
+
+3.0.1
+=====
+
+* Network menu
+ - Fix problems updating the menu for mobile broadband devices [Giovanni]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646395
+ - Fix missing device descriptions with multiple devices of the
+ same type [Giovanni]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646074
+ - Label ad-hoc networks with an appropriate icon [Dan]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646141
+ - Fix displaying some devices states as "invalid" [Dan]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646946
+ - Fix problems with access points that don't report a SSID [Giovanni]
+ https://bugzilla.gnome.org/show_bug.cgi?id=647040
+ - Miscellaneous minor bug fixes [Dan, Giovanni, Owen]
+ 645981, 646558, 646443, 646708, 646968
+* Application menu and icon
+ - Fix bug where application menu icon was missing at GNOME Shell
+ startup. [Florian]
+ https://bugzilla.gnome.org/show_bug.cgi?id=644122
+ - Fix missing application menu for dialog windows [Colin]
+ https://bugzilla.gnome.org/show_bug.cgi?id=647082
+ - When launching an application through an alternate launcher
+ (like for a System Settings pane), association the windows with
+ the application, not the launcher. [Colin]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646689
+* Activities overview
+ - Load the applications view incrementally to avoid potentially freezing
+ for multiple seconds [Colin]
+ https://bugzilla.gnome.org/show_bug.cgi?id=647778
+ - Fix bug where package installation while the overview
+ was up could result in a corrupted application display. [Giovanni]
+ https://bugzilla.gnome.org/show_bug.cgi?id=645801
+ - Fix dragging from the search results to launch apps and docs [Florian]
+ https://bugzilla.gnome.org/show_bug.cgi?id=645990
+ - Fix flickering of selection when searching in the overview [Florian]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646019
+ - Fix bug when typing into the search box when text was already
+ selected [Nohemi]
+ https://bugzilla.gnome.org/show_bug.cgi?id=636341
+* Fix layout of notifications for right-to-left languages [Florian]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646921
+* Remove a confusing special case where Alt-Tab sometimes switched
+ to a different window of the same application rather than to
+ a different application. [Rui]
+ https://bugzilla.gnome.org/show_bug.cgi?id=648132
+* Fix a crash that could happen when a window was opened on a
+ workspace that was immediately removed [Dan]
+ https://bugzilla.gnome.org/show_bug.cgi?id=648132
+* Fix keyboard navigation in logout/reboot dialogs [Dan]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646740
+* Fix missing inspector icon in Looking Glass console [Dan]
+* Miscellaneous minor bug fixes [Adel, Colin, Dan, Florian, Nohemi]
+ 645648, 646205, 646257, 646855, 647098, 646730
+
+Contributors:
+ Giovanni Campagna, Nohemi Fernandez, Adel Gadllah, Rui Matos, Florian Müllner,
+ Owen Taylor, Colin Walters, Dan Winship
+
+Translations:
+ Hendrik Richter [de], Jorge González [es], Arash Mousavi [fa],
+ Fran Diéguez [gl], Jiro Matsuzawa [ja], Piotr Drąg [pl], Daniel Nylander [sv],
+ Sira Nokyoongtong [th], Muhammet Kara [tr], Nguyễn Thái Ngọc Duy [vi],
+ Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
+
+3.0.0.2
+=======
+
+* Fix missing import that was preventing extensions from loading.
+ [Maxim Ermilov]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646333
+
+Translations:
+ Timo Jyrinki [fi]
+
+3.0.0.1
+=======
+
+* Fix problem with stuck event handling if network menu pops down while
+ user is using the scrollbar. [Owen Taylor]
+ https://bugzilla.gnome.org/show_bug.cgi?id=646825
+
+Contributors to GNOME Shell 3.0
+===============================
+
+Code:
+
+ Josh Adams, Kiyoshi Aman, Nuno Araujo, Emmanuele Bassi, Dirk-Jan C. Binnema,
+ Wouter Bolsterlee, Raphael Bosshard, Milan Bouchet-Valat, Christina Boumpouka,
+ Mathieu Bridon, Alban Browaeys, Phil Bull, Micro Cai, Giovanni Campagna,
+ Cosimo Cecchi, Tor-björn Claesson, Matthias Clasen, Jason D. Clinton,
+ Frederic Crozat, Guillaume Desmottes, Sander Dijkhuis, Neha Doijode,
+ Maxim Ermilov, Diego Escalante Urrelo, Luca Ferretti, Steve Frécinaux,
+ Takao Fujiwara, Adel Gadllah, Vadim Girlin, Nick Glynn, Guido Günther,
+ Leon Handreke, Lex Hider, Richard Hughes, Javier Jardón, Abderrahim Kitouni,
+ Andre Klapper, Alexander Larsson, Nickolas Lloyd, Ryan Lortie, Kjartan Maraas,
+ Koop Mast, Rui Matos, Jonathan Matthew, William Jon McCann, Morten Mjelva,
+ Federico Mena Quintero, Florian Müllner, Jon Nettleton, Hellyna Ng,
+ Discardi Nicola, Carlos Martín Nieto, Bastien Nocera, Bill Nottingham,
+ Matt Novenstern, Marc-Antoine Perennou, Neil Perry, Frédéric Péters,
+ Alejandro Piñeiro, Siegfried-Angel Gevatter Pujals, "res", Neil Roberts,
+ "Sardem FF7", Florian Scandella, Joseph Scheuhammer, Christian Schramm,
+ Gustavo Noronha Silva, Jasper St. Pierre, Eric Springer, Jakub Steiner,
+ Jonathan Strander, Ray Strode, Owen Taylor, Rico Tzschichholz,
+ Sergey V. Udaltsov, Daiki Ueno, Vincent Untz, Marcelo Jorge Vieira,
+ Mads Villadsen, Colin Walters, Dan Winship, William Wolf, Thomas Wood,
+ Pierre Yager, David Zeuthen, Marina Zhurakhinskaya
+
+Design:
+
+ Allan Day, William Jon McCann, Jeremy Perry, Jakub Steiner
+ 2008 Boston GNOME design hackfest participants (especially Neil J. Patel
+ for turning the resulting sketches into our first mockups.)
+ Everybody on irc.gnome.org:#gnome-design
+
+Translations:
+
+ Friedel Wolff (af), Khaled Hosny (ar), Ivaylo Valkov (bg), Jamil Ahmed (bn)
+ Runa Bhattacharjee (bn_IN), Gil Forcada, Siegfried-Angel Gevatter Pujals,
+ Jordi Serratosa (ca), Andre Klapper, Petr Kovar (cs), Kenneth Nielsen,
+ Kris Thomsen (da), Mario Blättermann, Hendrik Brandt, Christian Kirbach,
+ Hendrik Richter, Wolfgang Stöggl (de), Michael Kotsarinis, Kostas Papadimas,
+ Jennie Petoumenou, Sterios Prosiniklis, Fotis Tsamis, Simos Xenitellis (el),
+ Bruce Cowan, Philip Withnall (en_GB), Jorge Gonzalez, Daniel Mustieles (es),
+ Mattias Põldaru, Ivar Smolin (et), Inaki Larranaga Murgoitio (eu),
+ Mahyar Moghimi (fa), Timo Jyrinki (fi), Cyril Arnaud, Bruno Brouard,
+ Pablo Martin-Gomez, Claude Paroz, Frédéric Peters (fr), Seán de Búrca (ga)
+ Francisco Diéguez, Antón Méixome (gl), Sweta Kothari (gu), Liel Fridman,
+ Yaron Shahrabani (he), Rajesh Ranjan (hi), Gabor Kelemen (hu), Milo Casagrande,
+ Luca Ferretti (it), Dirgita, Andika Triwidada (id), Takayuki KUSANO,
+ Takayoshi OKANO, Kiyotaka NISHIBORI, Futoshi NISHIO (ja), Shankar Prasad (kn),
+ Young-Ho Cha, Changwoo Ryu (ko), Žygimantas Beručka, Gintautas Miliauskas (lt),
+ Rudolfs Mazurs (lv), Sandeep Shedmake (mr), Kjartan Maraas (nb),
+ Wouter Bolsterlee, Sander Dijkhuis, Reinout van Schouwen (nl),
+ Torstein Winterseth (nn), A S Alam (pa), Tomasz Dominikowski, Piotr Drąg (pl),
+ Duarte Loreto (pt), Felipe Borges, Rodrigo Padula de Oliveira,
+ Rodrigo L. M. Flores, Amanda Magalhães, Og B. Maciel, Gabriel F. Vilar,
+ Jonh Wendell (pt_BR), Lucian Adrian Grijincu, Daniel Șerbănescu (ro),
+ Sergey V. Kovylov, Andrey Korzinev, Yuri Myasoedov, Marina Zhurakhinskaya (ru),
+ Daniel Nylander (se), Matej Urbančič, Andrej Žnidaršič (sl),
+ Miloš Popović (sr, sr@latin), Miroslav Nikolić (sr), Tirumurti Vasudevan (ta),
+ Sira Nokyoongtong (th), Baris Cicek (tr), Abduxukur Abdurixit,
+ Gheyret T. Kenji (ug), Maxim V. Dziumanenko, Daniel Korostil (uk),
+ Nguyễn Thái Ngọc Duy (vi), Jessica Ban, 'jiero', Wei Li, YunQiang Su, Ray Wang,
+ Aron Xu (zh_CN), Chao-Hsiung Liao (zh_HK, zh_TW)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bf0d9aa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,43 @@
+# GNOME Shell
+GNOME Shell provides core user interface functions for the GNOME desktop,
+like switching to windows and launching applications. GNOME Shell takes
+advantage of the capabilities of modern graphics hardware and introduces
+innovative user interface concepts to provide a visually attractive and
+easy to use experience.
+
+For more information about GNOME Shell, including instructions on how
+to build GNOME Shell from source and how to get involved with the project,
+see the [project wiki][project-wiki].
+
+Bugs should be reported to the GNOME [bug tracking system][bug-tracker].
+Please refer to the [*Schedule* wiki page][schedule] to see the supported versions.
+
+## Contributing
+
+To contribute, open merge requests at https://gitlab.gnome.org/GNOME/gnome-shell.
+
+Commit messages should follow the [GNOME commit message
+guidelines](https://wiki.gnome.org/Git/CommitMessages). We require an URL
+to either an issue or a merge request in each commit.
+
+## Default branch
+
+The default development branch is `main`. If you still have a local
+checkout under the old name, use:
+```sh
+git checkout master
+git branch -m master main
+git fetch
+git branch --unset-upstream
+git branch -u origin/main
+git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
+```
+
+## License
+GNOME Shell is distributed under the terms of the GNU General Public License,
+version 2 or later. See the [COPYING][license] file for details.
+
+[project-wiki]: https://wiki.gnome.org/Projects/GnomeShell
+[bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell/issues
+[schedule]: https://wiki.gnome.org/Schedule
+[license]: COPYING
diff --git a/config.h.meson b/config.h.meson
new file mode 100644
index 0000000..ff355d3
--- /dev/null
+++ b/config.h.meson
@@ -0,0 +1,38 @@
+/* The prefix for our gettext translation domains. */
+#mesondefine GETTEXT_PACKAGE
+
+/* Version number of package */
+#mesondefine VERSION
+
+/* Version number of package */
+#mesondefine PACKAGE_VERSION
+
+/* Define to 1 if you have the `fdwalk' function. */
+#mesondefine HAVE_FDWALK
+
+/* Define to 1 if you have the `mallinfo' function. */
+#mesondefine HAVE_MALLINFO
+
+/* Define to 1 if you have the `mallinfo2' function. */
+#mesondefine HAVE_MALLINFO2
+
+/* Define to 1 fi you have the <sys/resource.h> header file. */
+#mesondefine HAVE_SYS_RESOURCE_H
+
+/* Define if we have NetworkManager */
+#mesondefine HAVE_NETWORKMANAGER
+
+/* Define if we have systemd */
+#mesondefine HAVE_SYSTEMD
+
+/* Define if _NL_TIME_FIRST_WEEKDATE is available */
+#mesondefine HAVE__NL_TIME_FIRST_WEEKDAY
+
+/* Define if you have the `g_desktop_app_info_launch_uris_as_manager_with_fds` function */
+#mesondefine HAVE_GIO_DESKTOP_LAUNCH_URIS_WITH_FDS
+
+/* Define if fdwalk is available in libc */
+#mesondefine HAVE_FDWALK
+
+/* Define if polkit defines autocleanup functions */
+#mesondefine HAVE_POLKIT_AUTOCLEANUP
diff --git a/data/00_org.gnome.shell.gschema.override b/data/00_org.gnome.shell.gschema.override
new file mode 100644
index 0000000..ac732e6
--- /dev/null
+++ b/data/00_org.gnome.shell.gschema.override
@@ -0,0 +1,6 @@
+[org.gnome.mutter:GNOME]
+attach-modal-dialogs=true
+edge-tiling=true
+dynamic-workspaces=true
+workspaces-only-on-primary=true
+focus-change-on-pointer-rest=true
diff --git a/data/50-gnome-shell-launchers.xml b/data/50-gnome-shell-launchers.xml
new file mode 100644
index 0000000..ca57ee6
--- /dev/null
+++ b/data/50-gnome-shell-launchers.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<KeyListEntries schema="org.gnome.shell.keybindings"
+ group="system"
+ name="Launchers"
+ wm_name="GNOME Shell"
+ package="gnome-shell">
+
+ <KeyListEntry name="switch-to-application-1"
+ description="Activate favorite application 1"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-2"
+ description="Activate favorite application 2"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-3"
+ description="Activate favorite application 3"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-4"
+ description="Activate favorite application 4"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-5"
+ description="Activate favorite application 5"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-6"
+ description="Activate favorite application 6"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-7"
+ description="Activate favorite application 7"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-8"
+ description="Activate favorite application 8"
+ hidden="true"/>
+
+ <KeyListEntry name="switch-to-application-9"
+ description="Activate favorite application 9"
+ hidden="true"/>
+
+</KeyListEntries>
+
diff --git a/data/50-gnome-shell-screenshots.xml b/data/50-gnome-shell-screenshots.xml
new file mode 100644
index 0000000..c82f1a3
--- /dev/null
+++ b/data/50-gnome-shell-screenshots.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<KeyListEntries schema="org.gnome.shell.keybindings"
+ group="system"
+ name="Screenshots"
+ wm_name="GNOME Shell"
+ package="gnome-shell">
+
+ <KeyListEntry name="show-screenshot-ui"
+ description="Take a screenshot interactively"/>
+
+ <KeyListEntry name="screenshot"
+ description="Take a screenshot"/>
+
+ <KeyListEntry name="screenshot-window"
+ description="Take a screenshot of a window"/>
+
+ <KeyListEntry name="show-screen-recording-ui"
+ description="Record a screencast interactively"/>
+
+</KeyListEntries> \ No newline at end of file
diff --git a/data/50-gnome-shell-system.xml b/data/50-gnome-shell-system.xml
new file mode 100644
index 0000000..33e5ba5
--- /dev/null
+++ b/data/50-gnome-shell-system.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<KeyListEntries schema="org.gnome.shell.keybindings"
+ group="system"
+ name="System"
+ wm_name="GNOME Shell"
+ package="gnome-shell">
+
+ <KeyListEntry name="toggle-message-tray"
+ description="Show the notification list"/>
+
+ <KeyListEntry name="focus-active-notification"
+ description="Focus the active notification"/>
+
+ <KeyListEntry name="toggle-overview"
+ description="Show the overview"/>
+
+ <KeyListEntry name="toggle-application-view"
+ description="Show all applications"/>
+
+ <KeyListEntry name="open-application-menu"
+ description="Open the application menu"/>
+
+</KeyListEntries>
+
diff --git a/data/README.osk-layouts b/data/README.osk-layouts
new file mode 100644
index 0000000..a0a0dbe
--- /dev/null
+++ b/data/README.osk-layouts
@@ -0,0 +1,75 @@
+Gnome-shell OSK layouts are extracted from CLDR layout definitions:
+https://www.unicode.org/cldr/charts/latest/keyboards/layouts/index.html
+
+To add new layouts from CLDR:
+
+1) Run update-osk-layouts.sh
+
+2) Modify JSON files to add extra keys, tweak appearance...
+
+2) Do git add and git commit
+
+JSON file format
+================
+
+Each JSON file describes a keymap for a certain language and layout,
+it has the following structure:
+
+- Root (Object)
+
+ Root object of a layout, has the following properties:
+
+ - levels (Array of Level): Levels in the keymap
+ - locale (String): Locale name for this keymap
+ - name (String): Human readable name for this keymap
+
+- Level (Object)
+
+ A level defines the keys available on a keyboard level,
+ these are the key panels visible at a time. E.g. to type
+ uppercase levels or symbols.
+
+ Levels have the following properties:
+
+ - level (String): Name of the level, common names are
+ "", "shift", "opt" and "opt+shift".
+ - mode (String): Mode for this level, common modes are
+ "default", "latched" and "locked".
+ - rows (Array of Row): Array of rows of keys.
+
+- Row (Array)
+
+ A row is an Array of Key.
+
+- Key (Object)
+
+ A keyboard key. Keys have the following properties:
+
+ - iconName (String): Icon name to show on the key.
+ Keys with an icon name do not have a label.
+ - label (String): Label to show on the key.
+ - strings (Array of String): Strings to commit. If
+ label property does not exist, the first element
+ will be also the label. Extra elements are shown
+ in the extra keys popover.
+ - keyval (String): Hexadecimal keyval to emit as
+ emulated key presses. Committed strings are
+ preferred.
+ - width (Double): Relative width of the key in the
+ row. 1 is for a square key. Multiples of 0.5 are
+ accepted.
+ - level (Integer): Level that the key switches to.
+ See the levelSwitch action.
+ - action (string): Action performed by the key,
+ accepted actions are:
+
+ - hide: Hides the OSK
+ - languageMenu: Pops up the language selection
+ menu
+ - emoji: Switches to the emoji selection panel
+ - modifier: Handles the keyval as a modifier
+ key. This handles e.g. Ctrl+A as a sequence
+ of Ctrl press, A press, A release, Ctrl
+ release.
+ - delete: Deletes text backwards
+ - levelSwitch: Switches OSK to a different level
diff --git a/data/cldr2json/README.md b/data/cldr2json/README.md
new file mode 100644
index 0000000..0eb54bc
--- /dev/null
+++ b/data/cldr2json/README.md
@@ -0,0 +1,40 @@
+cldr2json
+=========
+
+This script converts Unicode CLDR android keyboard layouts to JSON usable by
+GNOME Shell.
+
+CLDR keyboard layouts can be found at
+<http://www.unicode.org/Public/cldr/latest/keyboards.zip>
+
+
+Usage
+=====
+
+ ./cldr2json <input file or directory> <output directory>
+
+example:
+
+ ./cldr2json cldr/keyboards/android/ json_layouts/
+
+
+Keyboard layout mapping
+=======================
+
+Unicode CLDR layout identifiers are language codes, while XKB layout
+identifiers are... something else. The mapping between the two currently uses
+heuristic based on the layout descriptions, in this order:
+
+- if the CLDR layout description matches an XKB layout description, chose its
+ XKB identifier
+- if one word of the CLDR layout description matches an XKB layout
+ description, chose its XKB identifier
+- if the CLDR layout description matches one word of an XKB layout description,
+ chose its XKB identifier
+
+That doesn't always work. For instance it fails for "en" language, that should
+match "us" XKB identifier. For such cases, there is a mapping in
+LOCALE_TO_XKB_OVERRIDES at the top of the script. If you discover a weird
+mapping of if you get a "failed to find XKB mapping for <locale>" warning then
+please consider adding an override there.
+
diff --git a/data/cldr2json/cldr2json.py b/data/cldr2json/cldr2json.py
new file mode 100755
index 0000000..e5eb3cb
--- /dev/null
+++ b/data/cldr2json/cldr2json.py
@@ -0,0 +1,212 @@
+#!/usr/bin/python3
+#
+# Copyright 2015 Daiki Ueno <dueno@src.gnome.org>
+# 2016 Parag Nemade <pnemade@redhat.com>
+# 2017 Alan <alan@boum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import glob
+import json
+import locale
+import logging
+import os
+import re
+import sys
+import xml.etree.ElementTree
+
+import gi
+gi.require_version('GnomeDesktop', '3.0') # NOQA: E402
+from gi.repository import GnomeDesktop
+
+ESCAPE_PATTERN = re.compile(r'\\u\{([0-9A-Fa-f]+?)\}')
+ISO_PATTERN = re.compile(r'[A-E]([0-9]+)')
+
+LOCALE_TO_XKB_OVERRIDES = {
+ 'af': 'za',
+ 'en': 'us',
+ 'en-GB': 'uk',
+ 'es-US': 'latam',
+ 'fr-CA': 'ca',
+ 'hi': 'in+bolnagri',
+ 'ky': 'kg',
+ 'nl-BE': 'be',
+ 'zu': None
+}
+
+
+def parse_single_key(value):
+ def unescape(m):
+ return chr(int(m.group(1), 16))
+ value = ESCAPE_PATTERN.sub(unescape, value)
+ return value
+
+
+def parse_rows(keymap):
+ unsorted_rows = {}
+ for _map in keymap.iter('map'):
+ value = _map.get('to')
+ key = [parse_single_key(value)]
+ iso = _map.get('iso')
+ if not ISO_PATTERN.match(iso):
+ sys.stderr.write('invalid ISO key name: %s\n' % iso)
+ continue
+ if not iso[0] in unsorted_rows:
+ unsorted_rows[iso[0]] = []
+ unsorted_rows[iso[0]].append((int(iso[1:]), key))
+ # add subkeys
+ longPress = _map.get('longPress')
+ if longPress:
+ for value in longPress.split(' '):
+ subkey = parse_single_key(value)
+ key.append(subkey)
+
+ rows = []
+ for k, v in sorted(list(unsorted_rows.items()),
+ key=lambda x: x[0],
+ reverse=True):
+ row = []
+ for key in sorted(v, key=lambda x: x):
+ row.append({ 'strings': key[1] })
+ rows.append(row)
+
+ return rows
+
+
+def convert_xml(tree):
+ root = {}
+ for xml_keyboard in tree.iter("keyboard"):
+ locale_full = xml_keyboard.get("locale")
+ locale, sep, end = locale_full.partition("-t-")
+ root["locale"] = locale
+ for xml_name in tree.iter("name"):
+ name = xml_name.get("value")
+ root["name"] = name
+ root["levels"] = []
+ # parse levels
+ for index, keymap in enumerate(tree.iter('keyMap')):
+ # FIXME: heuristics here
+ modifiers = keymap.get('modifiers')
+ if not modifiers:
+ mode = 'default'
+ modifiers = ''
+ elif 'shift' in modifiers.split(' '):
+ mode = 'latched'
+ modifiers = 'shift'
+ else:
+ mode = 'locked'
+ level = {}
+ level["level"] = modifiers
+ level["mode"] = mode
+ level["rows"] = parse_rows(keymap)
+ root["levels"].append(level)
+ return root
+
+
+def locale_to_xkb(locale, name):
+ if locale in sorted(LOCALE_TO_XKB_OVERRIDES.keys()):
+ xkb = LOCALE_TO_XKB_OVERRIDES[locale]
+ logging.debug("override for %s → %s",
+ locale, xkb)
+ if xkb:
+ return xkb
+ else:
+ raise KeyError("layout %s explicitly disabled in overrides"
+ % locale)
+ xkb_names = sorted(name_to_xkb.keys())
+ if name in xkb_names:
+ return name_to_xkb[name]
+ else:
+ logging.debug("name %s failed" % name)
+ for sub_name in name.split(' '):
+ if sub_name in xkb_names:
+ xkb = name_to_xkb[sub_name]
+ logging.debug("dumb mapping failed but match with locale word: "
+ "%s (%s) → %s (%s)",
+ locale, name, xkb, sub_name)
+ return xkb
+ else:
+ logging.debug("sub_name failed")
+ for xkb_name in xkb_names:
+ for xkb_sub_name in xkb_name.split(' '):
+ if xkb_sub_name.strip('()') == name:
+ xkb = name_to_xkb[xkb_name]
+ logging.debug("dumb mapping failed but match with xkb word: "
+ "%s (%s) → %s (%s)",
+ locale, name, xkb, xkb_name)
+ return xkb
+ raise KeyError("failed to find XKB mapping for %s" % locale)
+
+
+def convert_file(source_file, destination_path):
+ logging.info("Parsing %s", source_file)
+
+ itree = xml.etree.ElementTree.ElementTree()
+ itree.parse(source_file)
+
+ root = convert_xml(itree)
+
+ try:
+ xkb_name = locale_to_xkb(root["locale"], root["name"])
+ except KeyError as e:
+ logging.warning(e)
+ return False
+ destination_file = os.path.join(destination_path, xkb_name + ".json")
+
+ try:
+ with open(destination_file, 'x', encoding="utf-8") as dest_fd:
+ json.dump(root, dest_fd, ensure_ascii=False, indent=2, sort_keys=True)
+ except FileExistsError as e:
+ logging.info("File %s exists, not updating", destination_file)
+ return False
+
+ logging.debug("written %s", destination_file)
+
+
+def load_xkb_mappings():
+ xkb = GnomeDesktop.XkbInfo()
+ layouts = xkb.get_all_layouts()
+ name_to_xkb = {}
+
+ for layout in layouts:
+ name = xkb.get_layout_info(layout).display_name
+ name_to_xkb[name] = layout
+
+ return name_to_xkb
+
+
+locale.setlocale(locale.LC_ALL, "C")
+name_to_xkb = load_xkb_mappings()
+
+
+if __name__ == "__main__":
+ if "DEBUG" in os.environ:
+ logging.basicConfig(level=logging.DEBUG)
+
+ if len(sys.argv) < 2:
+ print("supply a CLDR keyboard file")
+ sys.exit(1)
+
+ if len(sys.argv) < 3:
+ print("supply an output directory")
+ sys.exit(1)
+
+ source = sys.argv[1]
+ destination = sys.argv[2]
+ if os.path.isfile(source):
+ convert_file(source, destination)
+ elif os.path.isdir(source):
+ for path in glob.glob(source + "/*-t-k0-android.xml"):
+ convert_file(path, destination)
diff --git a/data/cldr2json/test/__init__.py b/data/cldr2json/test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/data/cldr2json/test/__init__.py
diff --git a/data/cldr2json/test/data/fr-t-k0-android.xml b/data/cldr2json/test/data/fr-t-k0-android.xml
new file mode 100644
index 0000000..1e76b81
--- /dev/null
+++ b/data/cldr2json/test/data/fr-t-k0-android.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE keyboard SYSTEM "../dtd/ldmlKeyboard.dtd">
+<keyboard locale="fr-t-k0-android">
+ <version platform="4.4" number="$Revision: 11914 $"/>
+ <names>
+ <name value="French"/>
+ </names>
+ <keyMap>
+ <map iso="D01" to="a" longPress="à â % æ á ä ã å ā ª"/> <!-- Q -->
+ <map iso="D02" to="z"/> <!-- W -->
+ <map iso="D03" to="e" longPress="é è ê ë % ę ė ē"/>
+ <map iso="D04" to="r"/>
+ <map iso="D05" to="t"/>
+ <map iso="D06" to="y" longPress="% ÿ"/>
+ <map iso="D07" to="u" longPress="ù û % ü ú ū"/>
+ <map iso="D08" to="i" longPress="î % ï ì í į ī"/>
+ <map iso="D09" to="o" longPress="ô œ % ö ò ó õ ø ō º"/>
+ <map iso="D10" to="p"/>
+ <map iso="C01" to="q"/> <!-- A -->
+ <map iso="C02" to="s"/>
+ <map iso="C03" to="d"/>
+ <map iso="C04" to="f"/>
+ <map iso="C05" to="g"/>
+ <map iso="C06" to="h"/>
+ <map iso="C07" to="j"/>
+ <map iso="C08" to="k"/>
+ <map iso="C09" to="l"/>
+ <map iso="C10" to="m"/> <!-- ; -->
+ <map iso="B01" to="w"/> <!-- Z -->
+ <map iso="B02" to="x"/>
+ <map iso="B03" to="c" longPress="ç ć č"/>
+ <map iso="B04" to="v"/>
+ <map iso="B05" to="b"/>
+ <map iso="B06" to="n"/>
+ <map iso="B07" to="&apos;" longPress="‘ ’ ‹ ›"/> <!-- M -->
+ <map iso="A02" to=","/> <!-- (key to left of space) -->
+ <map iso="A03" to=" "/> <!-- space -->
+ <map iso="A04" to="." longPress="# ! , ? - : ' @"/> <!-- (key to right of space) -->
+ </keyMap>
+ <keyMap modifiers="shift caps">
+ <map iso="D01" to="A" longPress="À Â % Æ Á Ä Ã Å Ā ª"/> <!-- Q -->
+ <map iso="D02" to="Z"/> <!-- W -->
+ <map iso="D03" to="E" longPress="É È Ê Ë % Ę Ė Ē"/>
+ <map iso="D04" to="R"/>
+ <map iso="D05" to="T"/>
+ <map iso="D06" to="Y" longPress="% Ÿ"/>
+ <map iso="D07" to="U" longPress="Ù Û % Ü Ú Ū"/>
+ <map iso="D08" to="I" longPress="Î % Ï Ì Í Į Ī"/>
+ <map iso="D09" to="O" longPress="Ô Œ % Ö Ò Ó Õ Ø Ō º"/>
+ <map iso="D10" to="P"/>
+ <map iso="C01" to="Q"/> <!-- A -->
+ <map iso="C02" to="S"/>
+ <map iso="C03" to="D"/>
+ <map iso="C04" to="F"/>
+ <map iso="C05" to="G"/>
+ <map iso="C06" to="H"/>
+ <map iso="C07" to="J"/>
+ <map iso="C08" to="K"/>
+ <map iso="C09" to="L"/>
+ <map iso="C10" to="M"/> <!-- ; -->
+ <map iso="B01" to="W"/> <!-- Z -->
+ <map iso="B02" to="X"/>
+ <map iso="B03" to="C" longPress="Ç Ć Č"/>
+ <map iso="B04" to="V"/>
+ <map iso="B05" to="B"/>
+ <map iso="B06" to="N"/>
+ <map iso="B07" to="&apos;" longPress="‘ ’ ‹ ›"/> <!-- M -->
+ <map iso="A02" to=","/> <!-- (key to left of space) -->
+ <map iso="A03" to=" "/> <!-- space -->
+ <map iso="A04" to="." longPress="# ! , ? - : ' @"/> <!-- (key to right of space) -->
+ </keyMap>
+ <keyMap modifiers="opt">
+ <map iso="D01" to="1" longPress="¹ ½ ⅓ ¼ ⅛"/> <!-- Q base=a -->
+ <map iso="D02" to="2" longPress="² ⅔"/> <!-- W base=z -->
+ <map iso="D03" to="3" longPress="³ ¾ ⅜"/> <!-- E -->
+ <map iso="D04" to="4" longPress="⁴"/> <!-- R -->
+ <map iso="D05" to="5" longPress="⅝"/> <!-- T -->
+ <map iso="D06" to="6"/> <!-- Y -->
+ <map iso="D07" to="7" longPress="⅞"/> <!-- U -->
+ <map iso="D08" to="8"/> <!-- I -->
+ <map iso="D09" to="9"/> <!-- O -->
+ <map iso="D10" to="0" longPress="ⁿ ∅"/> <!-- P -->
+ <map iso="C01" to="@"/> <!-- A base=q -->
+ <map iso="C02" to="#"/> <!-- S -->
+ <map iso="C03" to="€" longPress="¢ £ $ ¥ ₱"/> <!-- D -->
+ <map iso="C04" to="%" longPress="‰"/> <!-- F -->
+ <map iso="C05" to="&amp;"/> <!-- G -->
+ <map iso="C06" to="-" longPress="_ – — ·"/> <!-- H -->
+ <map iso="C07" to="+" longPress="±"/> <!-- J -->
+ <map iso="C08" to="(" longPress="&lt; { ["/> <!-- K -->
+ <map iso="C09" to=")" longPress="&gt; } ]"/> <!-- L -->
+ <map iso="B01" to="*" longPress="† ‡ ★"/> <!-- Z base=w -->
+ <map iso="B02" to="\u{22}" longPress="“ ” « »"/> <!-- X to= " -->
+ <map iso="B03" to="&apos;" longPress="‘ ’ ‹ ›"/> <!-- C -->
+ <map iso="B04" to=":"/> <!-- V -->
+ <map iso="B05" to=";"/> <!-- B -->
+ <map iso="B06" to="!" longPress="¡"/> <!-- N -->
+ <map iso="B07" to="?" longPress="¿"/> <!-- M base=' -->
+ <map iso="A00" to="_"/> <!-- (3 keys to left of space) -->
+ <map iso="A01" to="/"/> <!-- (2 keys to left of space) -->
+ <map iso="A02" to=" "/> <!-- (key to left of space) base=, -->
+ <map iso="A03" to=","/> <!-- space -->
+ <map iso="A04" to="." longPress="…"/> <!-- (key to right of space) -->
+ </keyMap>
+ <keyMap modifiers="opt+shift">
+ <map iso="D01" to="~"/> <!-- Q base=a -->
+ <map iso="D02" to="`"/> <!-- W base=z -->
+ <map iso="D03" to="|"/> <!-- E -->
+ <map iso="D04" to="•" longPress="♪ ♥ ♠ ♦ ♣"/> <!-- R -->
+ <map iso="D05" to="√"/> <!-- T -->
+ <map iso="D06" to="Π" longPress="π"/> <!-- Y -->
+ <map iso="D07" to="÷"/> <!-- U -->
+ <map iso="D08" to="×"/> <!-- I -->
+ <map iso="D09" to="¶" longPress="§"/> <!-- O -->
+ <map iso="D10" to="∆"/> <!-- P -->
+ <map iso="C01" to="£"/> <!-- A base=q -->
+ <map iso="C02" to="¥"/> <!-- S -->
+ <map iso="C03" to="$" longPress="¢"/> <!-- D -->
+ <map iso="C04" to="¢"/> <!-- F -->
+ <map iso="C05" to="^" longPress="↑ ↓ ← →"/> <!-- G -->
+ <map iso="C06" to="°" longPress="′ ″"/> <!-- H -->
+ <map iso="C07" to="=" longPress="≠ ≈ ∞"/> <!-- J -->
+ <map iso="C08" to="{"/> <!-- K -->
+ <map iso="C09" to="}"/> <!-- L -->
+ <map iso="B01" to="\"/> <!-- Z base=w -->
+ <map iso="B02" to="©"/> <!-- X -->
+ <map iso="B03" to="®"/> <!-- C -->
+ <map iso="B04" to="™"/> <!-- V -->
+ <map iso="B05" to="℅"/> <!-- B -->
+ <map iso="B06" to="["/> <!-- N -->
+ <map iso="B07" to="]"/> <!-- M base=' -->
+ <map iso="A00" to="&lt;" longPress="‹ ≤ «"/> <!-- (3 keys to left of space) -->
+ <map iso="A01" to="&gt;" longPress="› ≥ »"/> <!-- (2 keys to left of space) -->
+ <map iso="A02" to=" "/> <!-- (key to left of space) base=, -->
+ <map iso="A03" to=","/> <!-- space -->
+ <map iso="A04" to="." longPress="…"/> <!-- (key to right of space) -->
+ </keyMap>
+</keyboard>
diff --git a/data/cldr2json/test/data/fr.json b/data/cldr2json/test/data/fr.json
new file mode 100644
index 0000000..3a8949f
--- /dev/null
+++ b/data/cldr2json/test/data/fr.json
@@ -0,0 +1,614 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ [
+ "a",
+ "à",
+ "â",
+ "%",
+ "æ",
+ "á",
+ "ä",
+ "ã",
+ "å",
+ "ā",
+ "ª"
+ ],
+ [
+ "z"
+ ],
+ [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "%",
+ "ę",
+ "ė",
+ "ē"
+ ],
+ [
+ "r"
+ ],
+ [
+ "t"
+ ],
+ [
+ "y",
+ "%",
+ "ÿ"
+ ],
+ [
+ "u",
+ "ù",
+ "û",
+ "%",
+ "ü",
+ "ú",
+ "ū"
+ ],
+ [
+ "i",
+ "î",
+ "%",
+ "ï",
+ "ì",
+ "í",
+ "į",
+ "ī"
+ ],
+ [
+ "o",
+ "ô",
+ "œ",
+ "%",
+ "ö",
+ "ò",
+ "ó",
+ "õ",
+ "ø",
+ "ō",
+ "º"
+ ],
+ [
+ "p"
+ ]
+ ],
+ [
+ [
+ "q"
+ ],
+ [
+ "s"
+ ],
+ [
+ "d"
+ ],
+ [
+ "f"
+ ],
+ [
+ "g"
+ ],
+ [
+ "h"
+ ],
+ [
+ "j"
+ ],
+ [
+ "k"
+ ],
+ [
+ "l"
+ ],
+ [
+ "m"
+ ]
+ ],
+ [
+ [
+ "w"
+ ],
+ [
+ "x"
+ ],
+ [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ],
+ [
+ "v"
+ ],
+ [
+ "b"
+ ],
+ [
+ "n"
+ ],
+ [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ ],
+ [
+ [
+ ","
+ ],
+ [
+ " "
+ ],
+ [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ [
+ "A",
+ "À",
+ "Â",
+ "%",
+ "Æ",
+ "Á",
+ "Ä",
+ "Ã",
+ "Å",
+ "Ā",
+ "ª"
+ ],
+ [
+ "Z"
+ ],
+ [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "%",
+ "Ę",
+ "Ė",
+ "Ē"
+ ],
+ [
+ "R"
+ ],
+ [
+ "T"
+ ],
+ [
+ "Y",
+ "%",
+ "Ÿ"
+ ],
+ [
+ "U",
+ "Ù",
+ "Û",
+ "%",
+ "Ü",
+ "Ú",
+ "Ū"
+ ],
+ [
+ "I",
+ "Î",
+ "%",
+ "Ï",
+ "Ì",
+ "Í",
+ "Į",
+ "Ī"
+ ],
+ [
+ "O",
+ "Ô",
+ "Œ",
+ "%",
+ "Ö",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Ø",
+ "Ō",
+ "º"
+ ],
+ [
+ "P"
+ ]
+ ],
+ [
+ [
+ "Q"
+ ],
+ [
+ "S"
+ ],
+ [
+ "D"
+ ],
+ [
+ "F"
+ ],
+ [
+ "G"
+ ],
+ [
+ "H"
+ ],
+ [
+ "J"
+ ],
+ [
+ "K"
+ ],
+ [
+ "L"
+ ],
+ [
+ "M"
+ ]
+ ],
+ [
+ [
+ "W"
+ ],
+ [
+ "X"
+ ],
+ [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ],
+ [
+ "V"
+ ],
+ [
+ "B"
+ ],
+ [
+ "N"
+ ],
+ [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ ],
+ [
+ [
+ ","
+ ],
+ [
+ " "
+ ],
+ [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ],
+ [
+ "2",
+ "²",
+ "⅔"
+ ],
+ [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ],
+ [
+ "4",
+ "⁴"
+ ],
+ [
+ "5",
+ "⅝"
+ ],
+ [
+ "6"
+ ],
+ [
+ "7",
+ "⅞"
+ ],
+ [
+ "8"
+ ],
+ [
+ "9"
+ ],
+ [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ ],
+ [
+ [
+ "@"
+ ],
+ [
+ "#"
+ ],
+ [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ],
+ [
+ "%",
+ "‰"
+ ],
+ [
+ "&"
+ ],
+ [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ],
+ [
+ "+",
+ "±"
+ ],
+ [
+ "(",
+ "<",
+ "{",
+ "["
+ ],
+ [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ ],
+ [
+ [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ],
+ [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ],
+ [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ],
+ [
+ ":"
+ ],
+ [
+ ";"
+ ],
+ [
+ "!",
+ "¡"
+ ],
+ [
+ "?",
+ "¿"
+ ]
+ ],
+ [
+ [
+ "_"
+ ],
+ [
+ "/"
+ ],
+ [
+ " "
+ ],
+ [
+ ","
+ ],
+ [
+ ".",
+ "…"
+ ]
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ [
+ "~"
+ ],
+ [
+ "`"
+ ],
+ [
+ "|"
+ ],
+ [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ],
+ [
+ "√"
+ ],
+ [
+ "Π",
+ "π"
+ ],
+ [
+ "÷"
+ ],
+ [
+ "×"
+ ],
+ [
+ "¶",
+ "§"
+ ],
+ [
+ "∆"
+ ]
+ ],
+ [
+ [
+ "£"
+ ],
+ [
+ "¥"
+ ],
+ [
+ "$",
+ "¢"
+ ],
+ [
+ "¢"
+ ],
+ [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ],
+ [
+ "°",
+ "′",
+ "″"
+ ],
+ [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ],
+ [
+ "{"
+ ],
+ [
+ "}"
+ ]
+ ],
+ [
+ [
+ "\\"
+ ],
+ [
+ "©"
+ ],
+ [
+ "®"
+ ],
+ [
+ "™"
+ ],
+ [
+ "℅"
+ ],
+ [
+ "["
+ ],
+ [
+ "]"
+ ]
+ ],
+ [
+ [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ],
+ [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ],
+ [
+ " "
+ ],
+ [
+ ","
+ ],
+ [
+ ".",
+ "…"
+ ]
+ ]
+ ]
+ }
+ ],
+ "locale": "fr",
+ "name": "French"
+}
diff --git a/data/cldr2json/test/test_cldr2json.py b/data/cldr2json/test/test_cldr2json.py
new file mode 100755
index 0000000..78a6aa0
--- /dev/null
+++ b/data/cldr2json/test/test_cldr2json.py
@@ -0,0 +1,212 @@
+#!/usr/bin/python3
+
+import json
+import tempfile
+import unittest
+import xml.etree.ElementTree
+
+import cldr2json
+
+
+class TestParseSingleKey(unittest.TestCase):
+ def test_ascii(self):
+ self.assertEqual(cldr2json.parse_single_key("a"), "a")
+
+ def test_nonascii(self):
+ self.assertEqual(cldr2json.parse_single_key("Æ"), "Æ")
+
+ def test_twochars(self):
+ self.assertEqual(cldr2json.parse_single_key("ԵՒ"), "ԵՒ")
+
+ def test_decode(self):
+ self.assertEqual(cldr2json.parse_single_key("\\u{200D}"), "\u200d")
+
+ def test_decode_threechars(self):
+ self.assertEqual(cldr2json.parse_single_key("ज\\u{94D}ञ"), "ज\u094Dञ")
+
+
+class TestParseRow(unittest.TestCase):
+ def test_parse_row(self):
+ xml_string = """
+ <keyMap>
+ <map iso="D01" to="a" longPress="à â % æ á ä ã å ā ª"/> <!-- Q -->
+ <map iso="D02" to="z"/> <!-- W -->
+ <map iso="D03" to="e" longPress="é è ê ë % ę ė ē"/>
+ <map iso="D04" to="r"/>
+ <map iso="D05" to="t"/>
+ <map iso="D06" to="y" longPress="% ÿ"/>
+ <map iso="D07" to="u" longPress="ù û % ü ú ū"/>
+ <map iso="D08" to="i" longPress="î % ï ì í į ī"/>
+ <map iso="D09" to="o" longPress="ô œ % ö ò ó õ ø ō º"/>
+ <map iso="D10" to="p"/>
+ <map iso="C01" to="q"/> <!-- A -->
+ <map iso="C02" to="s"/>
+ <map iso="C03" to="d"/>
+ <map iso="C04" to="f"/>
+ <map iso="C05" to="g"/>
+ <map iso="C06" to="h"/>
+ <map iso="C07" to="j"/>
+ <map iso="C08" to="k"/>
+ <map iso="C09" to="l"/>
+ <map iso="C10" to="m"/> <!-- ; -->
+ <map iso="B01" to="w"/> <!-- Z -->
+ <map iso="B02" to="x"/>
+ <map iso="B03" to="c" longPress="ç ć č"/>
+ <map iso="B04" to="v"/>
+ <map iso="B05" to="b"/>
+ <map iso="B06" to="n"/>
+ <map iso="B07" to="&apos;" longPress="‘ ’ ‹ ›"/> <!-- M -->
+ <map iso="A02" to=","/> <!-- (key to left of space) -->
+ <map iso="A03" to=" "/> <!-- space -->
+ <map iso="A04" to="." longPress="# ! , ? - : ' @"/> <!-- (key to right of space) -->
+ </keyMap>
+ """
+ xml_tree = xml.etree.ElementTree.XML(xml_string)
+ json = [[
+ ['a', 'à', 'â', '%', 'æ', 'á', 'ä', 'ã', 'å', 'ā', 'ª'],
+ ['z'],
+ ['e', 'é', 'è', 'ê', 'ë', '%', 'ę', 'ė', 'ē'],
+ ['r'],
+ ['t'],
+ ['y', '%', 'ÿ'],
+ ['u', 'ù', 'û', '%', 'ü', 'ú', 'ū'],
+ ['i', 'î', '%', 'ï', 'ì', 'í', 'į', 'ī'],
+ ['o', 'ô', 'œ', '%', 'ö', 'ò', 'ó', 'õ', 'ø', 'ō', 'º'],
+ ['p']
+ ], [
+ ['q'],
+ ['s'],
+ ['d'],
+ ['f'],
+ ['g'],
+ ['h'],
+ ['j'],
+ ['k'],
+ ['l'],
+ ['m']
+ ], [
+ ['w'],
+ ['x'],
+ ['c', 'ç', 'ć', 'č'],
+ ['v'],
+ ['b'],
+ ['n'],
+ ["'", '‘', '’', '‹', '›']
+ ], [
+ [','],
+ [' '],
+ ['.', '#', '!', ',', '?', '-', ':', "'", '@']
+ ]]
+ self.assertEqual(cldr2json.parse_rows(xml_tree), json)
+
+
+class TestConvertXml(unittest.TestCase):
+ def test_convert_xml(self):
+ xml_string = """<?xml version="1.0" encoding="UTF-8" ?>
+ <!DOCTYPE keyboard SYSTEM "../dtd/ldmlKeyboard.dtd">
+ <keyboard locale="fr-t-k0-android">
+ <version platform="4.4" number="$Revision: 11914 $"/>
+ <names>
+ <name value="French"/>
+ </names>
+ <keyMap>
+ <map iso="D01" to="a" longPress="à â % æ á ä ã å ā ª"/> <!-- Q -->
+ </keyMap>
+ <keyMap modifiers="shift caps">
+ <map iso="D01" to="A" longPress="À Â % Æ Á Ä Ã Å Ā ª"/> <!-- Q -->
+ </keyMap>
+ <keyMap modifiers="opt">
+ <map iso="D01" to="1" longPress="¹ ½ ⅓ ¼ ⅛"/> <!-- Q base=a -->
+ </keyMap>
+ <keyMap modifiers="opt+shift">
+ <map iso="D01" to="~"/> <!-- Q base=a -->
+ </keyMap>
+ </keyboard>
+ """
+ xml_tree = xml.etree.ElementTree.XML(xml_string)
+ json = {
+ "locale": "fr",
+ "name": "French",
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ ['a', 'à', 'â', '%', 'æ', 'á', 'ä', 'ã', 'å', 'ā', 'ª'],
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ ['A', 'À', 'Â', '%', 'Æ', 'Á', 'Ä', 'Ã', 'Å', 'Ā', 'ª'],
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ ["1", "¹", "½", "⅓", "¼", "⅛"],
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ ["~"],
+ ]
+ ]
+ }
+ ]
+ }
+ self.assertEqual(cldr2json.convert_xml(xml_tree), json)
+
+
+class TestConvertFile(unittest.TestCase):
+ def test_fr(self):
+ outdir = tempfile.mkdtemp()
+ cldr2json.convert_file("test/data/fr-t-k0-android.xml", outdir)
+ with open("test/data/fr.json", encoding="utf-8") as expected_json_fd:
+ expected_json = json.load(expected_json_fd)
+ with open(outdir + "/fr.json", encoding="utf-8") as actual_json_fd:
+ actual_json = json.load(actual_json_fd)
+ self.assertEqual(expected_json, actual_json)
+
+
+class TestLocaleToXKB(unittest.TestCase):
+ def test_simple(self):
+ self.assertEqual(cldr2json.locale_to_xkb("fr", "French"),
+ "fr")
+
+ def test_fallback(self):
+ self.assertEqual(cldr2json.locale_to_xkb("nb", "Norwegian Bokmål"),
+ "no")
+
+ def test_fallback2(self):
+ self.assertEqual(cldr2json.locale_to_xkb("km", "Khmer"),
+ "kh")
+
+ def test_override(self):
+ self.assertEqual(cldr2json.locale_to_xkb("en-GB",
+ "English Great Britain"),
+ "uk")
+
+
+class LoadXKBMapplings(unittest.TestCase):
+ def test_dictionnary(self):
+ self.assertIsInstance(cldr2json.load_xkb_mappings(), dict)
+
+ def test_mapping(self):
+ mapping = cldr2json.load_xkb_mappings()
+ self.assertEqual(mapping["French"], "fr")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/data/dbus-interfaces/meson.build b/data/dbus-interfaces/meson.build
new file mode 100644
index 0000000..c96bbbb
--- /dev/null
+++ b/data/dbus-interfaces/meson.build
@@ -0,0 +1,10 @@
+dbus_interfaces = [
+ 'org.gnome.Shell.Extensions.xml',
+ 'org.gnome.Shell.Introspect.xml',
+ 'org.gnome.Shell.PadOsd.xml',
+ 'org.gnome.Shell.Screencast.xml',
+ 'org.gnome.Shell.Screenshot.xml',
+ 'org.gnome.ShellSearchProvider.xml',
+ 'org.gnome.ShellSearchProvider2.xml'
+]
+install_data(dbus_interfaces, install_dir: ifacedir)
diff --git a/data/dbus-interfaces/net.hadess.PowerProfiles.xml b/data/dbus-interfaces/net.hadess.PowerProfiles.xml
new file mode 100644
index 0000000..fce04a8
--- /dev/null
+++ b/data/dbus-interfaces/net.hadess.PowerProfiles.xml
@@ -0,0 +1,76 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+
+ <!--
+ net.hadess.PowerProfiles:
+ @short_description: Power Profiles daemon
+
+ The power-profiles-daemon API is meant to be used by parts of the OS or
+ desktop environment to switch system power profiles based on user choice,
+ or user intent.
+
+ OS components would typically use the "Profiles" property to construct
+ their UI (2 or 3 profiles available), and monitor the "ActiveProfile"
+ and the "PerformanceInhibited" properties to update that UI. The UI
+ would try to set the "ActiveProfile" property if the user selected
+ a different one.
+
+ Note that the reason why the project exists and how it is different from
+ existing projects is explained <ulink href=" https://gitlab.freedesktop.org/hadess/power-profiles-daemon/-/blob/master/README.md">
+ in the project's README file</ulink>.
+
+ The object path will be "/net/hadess/PowerProfiles".
+ -->
+ <interface name="net.hadess.PowerProfiles">
+ <!--
+ ActiveProfile:
+
+ The type of the currently active profile. It might change automatically
+ if the "performance" profile was selected but it got inhibited, in which
+ case the "PerformanceInhibited" property will reflect the reason.
+ -->
+ <property name="ActiveProfile" type="s" access="readwrite"/>
+
+ <!--
+ PerformanceInhibited:
+
+ This will be set if the performance power profile is unavailable, with
+ the value being used to identify the reason for unavailability. As new
+ reasons can be added, it is recommended that front-ends show a generic
+ reason if they do not recognise the value. Possible values are:
+ - "lap-detected" (the computer is sitting on the user's lap)
+ - "high-operating-temperature" (the computer is close to overheating)
+ - "" (the empty string, if not inhibited)
+ -->
+ <property name="PerformanceInhibited" type="s" access="read"/>
+
+ <!--
+ Profiles:
+
+ An array of key-pair values representing each profile. The key named
+ "Driver" (s) identifies the power-profiles-daemon backend code used to
+ implement the profile.
+
+ The key named "Profile" (s) will be one of:
+ - "power-saver" (battery saving profile)
+ - "balanced" (the default profile)
+ - "performance" (a profile that does not care about noise or battery consumption)
+
+ Only one of each type of profile will be listed, with the daemon choosing the
+ more appropriate "driver" for each profile type.
+ -->
+ <property name="Profiles" type="aa{sv}" access="read"/>
+
+ <!--
+ Actions:
+
+ An array of strings listing each one of the "actions" implemented in
+ the running daemon. This is used by API users to figure out whether
+ particular functionality is available in a version of the daemon.
+ -->
+ <property name="Actions" type="as" access="read"/>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/net.hadess.SensorProxy.xml b/data/dbus-interfaces/net.hadess.SensorProxy.xml
new file mode 100644
index 0000000..2bf8ffa
--- /dev/null
+++ b/data/dbus-interfaces/net.hadess.SensorProxy.xml
@@ -0,0 +1,5 @@
+<node>
+ <interface name="net.hadess.SensorProxy">
+ <property name="HasAccelerometer" type="b" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/net.hadess.SwitcherooControl.xml b/data/dbus-interfaces/net.hadess.SwitcherooControl.xml
new file mode 100644
index 0000000..e52bc1a
--- /dev/null
+++ b/data/dbus-interfaces/net.hadess.SwitcherooControl.xml
@@ -0,0 +1,46 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+
+ <!--
+ net.hadess.SwitcherooControl:
+ @short_description: D-Bus proxy to access dual-GPU controls.
+
+ After checking the availability of two switchable GPUs in the machine,
+ check the value of net.hadess.SwitcherooControl.HasDualGpu to see
+ if running applications on the discrete GPU should be offered.
+
+ The object path will be "/net/hadess/SwitcherooControl".
+ -->
+ <interface name="net.hadess.SwitcherooControl">
+ <!--
+ HasDualGpu:
+
+ Whether two switchable GPUs are present on the system. This property
+ has been obsoleted in favour of the "NumGPUs" property.
+ -->
+ <property name="HasDualGpu" type="b" access="read"/>
+
+ <!--
+ NumGPUs:
+
+ The number of GPUs available on the system. Note that while having no
+ GPUs is unlikely, consumers of this API should probably not throw errors
+ if that were the case.
+ -->
+ <property name="NumGPUs" type="u" access="read"/>
+
+ <!--
+ GPUs:
+
+ An array of key-pair values representing each GPU. The key named "Name" (s)
+ will contain a user-facing name for the GPU, the "Environment" (as) key will
+ contain an array of even number of strings, each being an environment
+ variable to set to use the GPU, followed by its value, the "Default" (b) key
+ will tag the default (usually integrated) GPU.
+ -->
+ <property name="GPUs" type="aa{sv}" access="read"/>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/net.reactivated.Fprint.Device.xml b/data/dbus-interfaces/net.reactivated.Fprint.Device.xml
new file mode 100644
index 0000000..b3d143c
--- /dev/null
+++ b/data/dbus-interfaces/net.reactivated.Fprint.Device.xml
@@ -0,0 +1,78 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="net.reactivated.Fprint.Device">
+ <method name="ListEnrolledFingers">
+ <arg type="s" name="username" direction="in">
+ </arg>
+ <arg type="as" name="enrolled_fingers" direction="out">
+ </arg>
+ </method>
+
+ <method name="DeleteEnrolledFingers">
+ <arg type="s" name="username" direction="in">
+ </arg>
+ </method>
+
+ <method name="DeleteEnrolledFingers2">
+ </method>
+
+ <method name="Claim">
+ <arg type="s" name="username" direction="in">
+ </arg>
+ </method>
+
+ <method name="Release">
+ </method>
+
+ <method name="VerifyStart">
+ <arg type="s" name="finger_name" direction="in">
+ </arg>
+ </method>
+
+ <method name="VerifyStop">
+ </method>
+
+ <signal name="VerifyFingerSelected">
+ <arg type="s" name="finger_name">
+ </arg>
+ </signal>
+
+ <signal name="VerifyStatus">
+ <arg type="s" name="result">
+ </arg>
+
+ <arg type="b" name="done">
+ </arg>
+
+ </signal>
+
+ <method name="EnrollStart">
+ <arg type="s" name="finger_name" direction="in">
+ </arg>
+ </method>
+
+ <method name="EnrollStop">
+ </method>
+
+ <signal name="EnrollStatus">
+ <arg type="s" name="result">
+ </arg>
+
+ <arg type="b" name="done">
+ </arg>
+ </signal>
+
+ <property name="name" type="s" access="read">
+ </property>
+
+ <property name="num-enroll-stages" type="i" access="read">
+ </property>
+
+ <property name="scan-type" type="s" access="read">
+ </property>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/net.reactivated.Fprint.Manager.xml b/data/dbus-interfaces/net.reactivated.Fprint.Manager.xml
new file mode 100644
index 0000000..21e03f2
--- /dev/null
+++ b/data/dbus-interfaces/net.reactivated.Fprint.Manager.xml
@@ -0,0 +1,18 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="net.reactivated.Fprint.Manager">
+
+ <method name="GetDevices">
+ <arg type="ao" name="devices" direction="out">
+ </arg>
+ </method>
+
+ <method name="GetDefaultDevice">
+ <arg type="o" name="device" direction="out">
+ </arg>
+ </method>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.Gtk.MountOperationHandler.xml b/data/dbus-interfaces/org.Gtk.MountOperationHandler.xml
new file mode 100644
index 0000000..8b03a2e
--- /dev/null
+++ b/data/dbus-interfaces/org.Gtk.MountOperationHandler.xml
@@ -0,0 +1,32 @@
+<node>
+ <interface name="org.Gtk.MountOperationHandler">
+ <method name="AskPassword">
+ <arg type="s" direction="in" name="object_id"/>
+ <arg type="s" direction="in" name="message"/>
+ <arg type="s" direction="in" name="icon_name"/>
+ <arg type="s" direction="in" name="default_user"/>
+ <arg type="s" direction="in" name="default_domain"/>
+ <arg type="u" direction="in" name="flags"/>
+ <arg type="u" direction="out" name="response"/>
+ <arg type="a{sv}" direction="out" name="response_details"/>
+ </method>
+ <method name="AskQuestion">
+ <arg type="s" direction="in" name="object_id"/>
+ <arg type="s" direction="in" name="message"/>
+ <arg type="s" direction="in" name="icon_name"/>
+ <arg type="as" direction="in" name="choices"/>
+ <arg type="u" direction="out" name="response"/>
+ <arg type="a{sv}" direction="out" name="response_details"/>
+ </method>
+ <method name="ShowProcesses">
+ <arg type="s" direction="in" name="object_id"/>
+ <arg type="s" direction="in" name="message"/>
+ <arg type="s" direction="in" name="icon_name"/>
+ <arg type="ai" direction="in" name="application_pids"/>
+ <arg type="as" direction="in" name="choices"/>
+ <arg type="u" direction="out" name="response"/>
+ <arg type="a{sv}" direction="out" name="response_details"/>
+ </method>
+ <method name="Close"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.Application.xml b/data/dbus-interfaces/org.freedesktop.Application.xml
new file mode 100644
index 0000000..55be7d7
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.Application.xml
@@ -0,0 +1,12 @@
+<node>
+ <interface name="org.freedesktop.Application">
+ <method name="ActivateAction">
+ <arg type="s" direction="in"/>
+ <arg type="av" direction="in"/>
+ <arg type="a{sv}" direction="in"/>
+ </method>
+ <method name="Activate">
+ <arg type="a{sv}" direction="in"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.DBus.xml b/data/dbus-interfaces/org.freedesktop.DBus.xml
new file mode 100644
index 0000000..18ff2aa
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.DBus.xml
@@ -0,0 +1,16 @@
+<node>
+ <interface name="org.freedesktop.DBus">
+ <method name="ListNames">
+ <arg type="as" direction="out" name="names"/>
+ </method>
+ <method name="GetConnectionUnixProcessID">
+ <arg type="s" direction="in"/>
+ <arg type="u" direction="out"/>
+ </method>
+ <signal name="NameOwnerChanged">
+ <arg type="s" direction="out" name="name"/>
+ <arg type="s" direction="out" name="oldOwner"/>
+ <arg type="s" direction="out" name="newOwner"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.GeoClue2.Agent.xml b/data/dbus-interfaces/org.freedesktop.GeoClue2.Agent.xml
new file mode 100644
index 0000000..2773601
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.GeoClue2.Agent.xml
@@ -0,0 +1,11 @@
+<node>
+ <interface name="org.freedesktop.GeoClue2.Agent">
+ <property name="MaxAccuracyLevel" type="u" access="read"/>
+ <method name="AuthorizeApp">
+ <arg name="desktop_id" type="s" direction="in"/>
+ <arg name="req_accuracy_level" type="u" direction="in"/>
+ <arg name="authorized" type="b" direction="out"/>
+ <arg name="allowed_accuracy_level" type="u" direction="out"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.GeoClue2.Manager.xml b/data/dbus-interfaces/org.freedesktop.GeoClue2.Manager.xml
new file mode 100644
index 0000000..28db3c6
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.GeoClue2.Manager.xml
@@ -0,0 +1,9 @@
+<node>
+ <interface name="org.freedesktop.GeoClue2.Manager">
+ <property name="InUse" type="b" access="read"/>
+ <property name="AvailableAccuracyLevel" type="u" access="read"/>
+ <method name="AddAgent">
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Cdma.xml b/data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Cdma.xml
new file mode 100644
index 0000000..12a85e0
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Cdma.xml
@@ -0,0 +1,13 @@
+<node>
+ <interface name="org.freedesktop.ModemManager.Modem.Cdma">
+ <method name="GetSignalQuality">
+ <arg type="u" direction="out"/>
+ </method>
+ <method name="GetServingSystem">
+ <arg type="(usu)" direction="out"/>
+ </method>
+ <signal name="SignalQuality">
+ <arg type="u" direction="out"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Gsm.Network.xml b/data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Gsm.Network.xml
new file mode 100644
index 0000000..7d8c25a
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.ModemManager.Modem.Gsm.Network.xml
@@ -0,0 +1,19 @@
+<node>
+ <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
+ <method name="GetRegistrationInfo">
+ <arg type="(uss)" direction="out"/>
+ </method>
+ <method name="GetSignalQuality">
+ <arg type="u" direction="out"/>
+ </method>
+ <property name="AccessTechnology" type="u" access="read"/>
+ <signal name="SignalQuality">
+ <arg type="u" direction="out"/>
+ </signal>
+ <signal name="RegistrationInfo">
+ <arg type="u" direction="out"/>
+ <arg type="s" direction="out"/>
+ <arg type="s" direction="out"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml b/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
new file mode 100644
index 0000000..41e6601
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
@@ -0,0 +1,6 @@
+<node>
+ <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
+ <property name="OperatorCode" type="s" access="read"/>
+ <property name="OperatorName" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.ModemCdma.xml b/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.ModemCdma.xml
new file mode 100644
index 0000000..d029252
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.ModemCdma.xml
@@ -0,0 +1,5 @@
+<node>
+ <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
+ <property name="Sid" type="u" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.xml b/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.xml
new file mode 100644
index 0000000..204426b
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.ModemManager1.Modem.xml
@@ -0,0 +1,5 @@
+<node>
+ <interface name="org.freedesktop.ModemManager1.Modem">
+ <property name="SignalQuality" type="(ub)" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.Notifications.xml b/data/dbus-interfaces/org.freedesktop.Notifications.xml
new file mode 100644
index 0000000..0ed8657
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.Notifications.xml
@@ -0,0 +1,35 @@
+<node>
+ <interface name="org.freedesktop.Notifications">
+ <method name="Notify">
+ <arg type="s" direction="in"/>
+ <arg type="u" direction="in"/>
+ <arg type="s" direction="in"/>
+ <arg type="s" direction="in"/>
+ <arg type="s" direction="in"/>
+ <arg type="as" direction="in"/>
+ <arg type="a{sv}" direction="in"/>
+ <arg type="i" direction="in"/>
+ <arg type="u" direction="out"/>
+ </method>
+ <method name="CloseNotification">
+ <arg type="u" direction="in"/>
+ </method>
+ <method name="GetCapabilities">
+ <arg type="as" direction="out"/>
+ </method>
+ <method name="GetServerInformation">
+ <arg type="s" direction="out"/>
+ <arg type="s" direction="out"/>
+ <arg type="s" direction="out"/>
+ <arg type="s" direction="out"/>
+ </method>
+ <signal name="NotificationClosed">
+ <arg type="u"/>
+ <arg type="u"/>
+ </signal>
+ <signal name="ActionInvoked">
+ <arg type="u"/>
+ <arg type="s"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.PackageKit.Offline.xml b/data/dbus-interfaces/org.freedesktop.PackageKit.Offline.xml
new file mode 100644
index 0000000..ff61166
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.PackageKit.Offline.xml
@@ -0,0 +1,13 @@
+<node>
+ <interface name="org.freedesktop.PackageKit.Offline">
+ <property name="UpdatePrepared" type="b" access="read"/>
+ <property name="UpdateTriggered" type="b" access="read"/>
+ <property name="UpgradePrepared" type="b" access="read"/>
+ <property name="UpgradeTriggered" type="b" access="read"/>
+ <property name="PreparedUpgrade" type="a{sv}" access="read"/>
+ <method name="Trigger">
+ <arg type="s" name="action" direction="in"/>
+ </method>
+ <method name="Cancel"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.UPower.Device.xml b/data/dbus-interfaces/org.freedesktop.UPower.Device.xml
new file mode 100644
index 0000000..4882285
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.UPower.Device.xml
@@ -0,0 +1,11 @@
+<node>
+ <interface name="org.freedesktop.UPower.Device">
+ <property name="Type" type="u" access="read"/>
+ <property name="State" type="u" access="read"/>
+ <property name="Percentage" type="d" access="read"/>
+ <property name="TimeToEmpty" type="x" access="read"/>
+ <property name="TimeToFull" type="x" access="read"/>
+ <property name="IsPresent" type="b" access="read"/>
+ <property name="IconName" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.UPower.xml b/data/dbus-interfaces/org.freedesktop.UPower.xml
new file mode 100644
index 0000000..a0fba20
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.UPower.xml
@@ -0,0 +1,5 @@
+<node>
+ <interface name="org.freedesktop.UPower">
+ <property name="OnBattery" type="b" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.bolt1.Device.xml b/data/dbus-interfaces/org.freedesktop.bolt1.Device.xml
new file mode 100644
index 0000000..44b40dc
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.bolt1.Device.xml
@@ -0,0 +1,18 @@
+<node>
+ <interface name="org.freedesktop.bolt1.Device">
+ <property name="Uid" type="s" access="read"></property>
+ <property name="Name" type="s" access="read"></property>
+ <property name="Vendor" type="s" access="read"></property>
+ <property name="Type" type="s" access="read"></property>
+ <property name="Status" type="s" access="read"></property>
+ <property name="Parent" type="s" access="read"></property>
+ <property name="SysfsPath" type="s" access="read"></property>
+ <property name="Stored" type="b" access="read"></property>
+ <property name="Policy" type="s" access="read"></property>
+ <property name="Key" type="s" access="read"></property>
+ <property name="Label" type="s" access="read"></property>
+ <property name="ConnectTime" type="t" access="read"></property>
+ <property name="AuthorizeTime" type="t" access="read"></property>
+ <property name="StoreTime" type="t" access="read"></property>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.bolt1.Manager.xml b/data/dbus-interfaces/org.freedesktop.bolt1.Manager.xml
new file mode 100644
index 0000000..c98a94f
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.bolt1.Manager.xml
@@ -0,0 +1,15 @@
+<node>
+ <interface name="org.freedesktop.bolt1.Manager">
+ <property name="Probing" type="b" access="read"></property>
+ <property name="AuthMode" type="s" access="readwrite"></property>
+ <method name="EnrollDevice">
+ <arg type="s" name="uid" direction="in"/>
+ <arg type="s" name="policy" direction="in"/>
+ <arg type="s" name="flags" direction="in"/>
+ <arg name="device" direction="out" type="o"/>
+ </method>
+ <signal name="DeviceAdded">
+ <arg name="device" type="o"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.impl.portal.Access.xml b/data/dbus-interfaces/org.freedesktop.impl.portal.Access.xml
new file mode 100644
index 0000000..802a2c1
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.impl.portal.Access.xml
@@ -0,0 +1,15 @@
+<node>
+ <interface name="org.freedesktop.impl.portal.Access">
+ <method name="AccessDialog">
+ <arg type="o" name="handle" direction="in"/>
+ <arg type="s" name="app_id" direction="in"/>
+ <arg type="s" name="parent_window" direction="in"/>
+ <arg type="s" name="title" direction="in"/>
+ <arg type="s" name="subtitle" direction="in"/>
+ <arg type="s" name="body" direction="in"/>
+ <arg type="a{sv}" name="options" direction="in"/>
+ <arg type="u" name="response" direction="out"/>
+ <arg type="a{sv}" name="results" direction="out"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.impl.portal.PermissionStore.xml b/data/dbus-interfaces/org.freedesktop.impl.portal.PermissionStore.xml
new file mode 100644
index 0000000..55d3fc3
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.impl.portal.PermissionStore.xml
@@ -0,0 +1,31 @@
+<node>
+ <interface name="org.freedesktop.impl.portal.PermissionStore">
+ <method name="Lookup">
+ <arg name="table" type="s" direction="in"/>
+ <arg name="id" type="s" direction="in"/>
+ <arg name="permissions" type="a{sas}" direction="out"/>
+ <arg name="data" type="v" direction="out"/>
+ </method>
+ <method name="Set">
+ <arg name="table" type="s" direction="in"/>
+ <arg name="create" type="b" direction="in"/>
+ <arg name="id" type="s" direction="in"/>
+ <arg name="app_permissions" type="a{sas}" direction="in"/>
+ <arg name="data" type="v" direction="in"/>
+ </method>
+ <method name="SetPermission">
+ <arg name='table' type='s' direction='in'/>
+ <arg name='create' type='b' direction='in'/>
+ <arg name='id' type='s' direction='in'/>
+ <arg name='app' type='s' direction='in'/>
+ <arg name='permissions' type='as' direction='in'/>
+ </method>
+ <signal name="Changed">
+ <arg name="table" type="s" direction="out"/>
+ <arg name="id" type="s" direction="out"/>
+ <arg name="deleted" type="b" direction="out"/>
+ <arg name="data" type="v" direction="out"/>
+ <arg name="permissions" type="a{sas}" direction="out"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.impl.portal.Request.xml b/data/dbus-interfaces/org.freedesktop.impl.portal.Request.xml
new file mode 100644
index 0000000..a9d03eb
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.impl.portal.Request.xml
@@ -0,0 +1,5 @@
+<node>
+ <interface name="org.freedesktop.impl.portal.Request">
+ <method name="Close"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.login1.Manager.xml b/data/dbus-interfaces/org.freedesktop.login1.Manager.xml
new file mode 100644
index 0000000..f40d498
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.login1.Manager.xml
@@ -0,0 +1,33 @@
+<node>
+ <interface name="org.freedesktop.login1.Manager">
+ <method name="Suspend">
+ <arg type="b" direction="in"/>
+ </method>
+ <method name="CanSuspend">
+ <arg type="s" direction="out"/>
+ </method>
+ <method name="Inhibit">
+ <arg type="s" direction="in"/>
+ <arg type="s" direction="in"/>
+ <arg type="s" direction="in"/>
+ <arg type="s" direction="in"/>
+ <arg type="h" direction="out"/>
+ </method>
+ <method name="GetSession">
+ <arg type="s" direction="in"/>
+ <arg type="o" direction="out"/>
+ </method>
+ <method name="ListSessions">
+ <arg name="sessions" type="a(susso)" direction="out"/>
+ </method>
+ <method name="CanRebootToBootLoaderMenu">
+ <arg type="s" direction="out"/>
+ </method>
+ <method name="SetRebootToBootLoaderMenu">
+ <arg type="t" direction="in"/>
+ </method>
+ <signal name="PrepareForSleep">
+ <arg type="b" direction="out"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.login1.Session.xml b/data/dbus-interfaces/org.freedesktop.login1.Session.xml
new file mode 100644
index 0000000..6fab817
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.login1.Session.xml
@@ -0,0 +1,15 @@
+<node>
+ <interface name="org.freedesktop.login1.Session">
+ <signal name="Lock"/>
+ <signal name="Unlock"/>
+ <property name="Active" type="b" access="read"/>
+ <property name="Class" type="s" access="read"/>
+ <property name="Id" type="s" access="read"/>
+ <property name="Remote" type="b" access="read"/>
+ <property name="Type" type="s" access="read"/>
+ <property name="State" type="s" access="read"/>
+ <method name="SetLockedHint">
+ <arg type="b" direction="in"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.login1.User.xml b/data/dbus-interfaces/org.freedesktop.login1.User.xml
new file mode 100644
index 0000000..d6f10b0
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.login1.User.xml
@@ -0,0 +1,6 @@
+<node>
+ <interface name="org.freedesktop.login1.User">
+ <property name="Display" type="(so)" access="read"/>
+ <property name="Sessions" type="a(so)" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.realmd.Provider.xml b/data/dbus-interfaces/org.freedesktop.realmd.Provider.xml
new file mode 100644
index 0000000..2fdb045
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.realmd.Provider.xml
@@ -0,0 +1,13 @@
+<node>
+ <interface name="org.freedesktop.realmd.Provider">
+ <property name="Name" type="s" access="read"/>
+ <property name="Version" type="s" access="read"/>
+ <property name="Realms" type="ao" access="read"/>
+ <method name="Discover">
+ <arg name="string" type="s" direction="in"/>
+ <arg name="options" type="a{sv}" direction="in"/>
+ <arg name="relevance" type="i" direction="out"/>
+ <arg name="realm" type="ao" direction="out"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.realmd.Realm.xml b/data/dbus-interfaces/org.freedesktop.realmd.Realm.xml
new file mode 100644
index 0000000..351066d
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.realmd.Realm.xml
@@ -0,0 +1,20 @@
+<node>
+ <interface name="org.freedesktop.realmd.Realm">
+ <property name="Name" type="s" access="read"/>
+ <property name="Configured" type="s" access="read"/>
+ <property name="Details" type="a(ss)" access="read"/>
+ <property name="LoginFormats" type="as" access="read"/>
+ <property name="LoginPolicy" type="s" access="read"/>
+ <property name="PermittedLogins" type="as" access="read"/>
+ <property name="SupportedInterfaces" type="as" access="read"/>
+ <method name="ChangeLoginPolicy">
+ <arg name="login_policy" type="s" direction="in"/>
+ <arg name="permitted_add" type="as" direction="in"/>
+ <arg name="permitted_remove" type="as" direction="in"/>
+ <arg name="options" type="a{sv}" direction="in"/>
+ </method>
+ <method name="Deconfigure">
+ <arg name="options" type="a{sv}" direction="in"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.freedesktop.realmd.Service.xml b/data/dbus-interfaces/org.freedesktop.realmd.Service.xml
new file mode 100644
index 0000000..4ffb7e7
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.realmd.Service.xml
@@ -0,0 +1,15 @@
+<node>
+ <interface name="org.freedesktop.realmd.Service">
+ <method name="Cancel">
+ <arg name="operation" type="s" direction="in"/>
+ </method>
+ <method name="Release"/>
+ <method name="SetLocale">
+ <arg name="locale" type="s" direction="in"/>
+ </method>
+ <signal name="Diagnostics">
+ <arg name="data" type="s"/>
+ <arg name="operation" type="s"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Mutter.ScreenCast.xml b/data/dbus-interfaces/org.gnome.Mutter.ScreenCast.xml
new file mode 100644
index 0000000..a8ff3cc
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Mutter.ScreenCast.xml
@@ -0,0 +1,191 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Mutter.ScreenCast:
+ @short_description: Screen cast interface
+
+ This API is private and not intended to be used outside of the integrated
+ system that uses libmutter. No compatibility between versions are
+ promised.
+ -->
+ <interface name="org.gnome.Mutter.ScreenCast">
+
+ <!--
+ CreateSession:
+ @properties: Properties
+ @session_path: Path to the new session object
+
+ * "remote-desktop-session-id" (s): The ID of a remote desktop session.
+ Remote desktop driven screen casts
+ are started and stopped by the remote
+ desktop session.
+ * "disable-animations" (b): Set to "true" if the screen cast application
+ would prefer animations to be globally
+ disabled, while the session is running. Default
+ is "false". Available since version 3.
+ -->
+ <method name="CreateSession">
+ <arg name="properties" type="a{sv}" direction="in" />
+ <arg name="session_path" type="o" direction="out" />
+ </method>
+
+ <!--
+ Version:
+ @short_description: API version
+ -->
+ <property name="Version" type="i" access="read" />
+
+ </interface>
+
+ <!--
+ org.gnome.Mutter.ScreenCast.Session:
+ @short_description: Screen cast session
+ -->
+ <interface name="org.gnome.Mutter.ScreenCast.Session">
+
+ <!--
+ Start:
+
+ Start the screen cast session
+ -->
+ <method name="Start" />
+
+ <!--
+ Stop:
+
+ Stop the screen cast session
+ -->
+ <method name="Stop" />
+
+ <!--
+ Closed:
+
+ The session has closed.
+ -->
+ <signal name="Closed" />
+
+ <!--
+ RecordMonitor:
+ @connector: Connector of the monitor to record
+ @properties: Properties
+ @stream_path: Path to the new stream object
+
+ Record a single monitor.
+
+ Available @properties include:
+
+ * "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
+ Available since API version 2.
+ * "is-recording" (b): Whether this is a screen recording. May be
+ be used for choosing panel icon.
+ Default: false. Available since API version 4.
+
+ Available cursor mode values:
+
+ 0: hidden - cursor is not included in the stream
+ 1: embedded - cursor is included in the framebuffer
+ 2: metadata - cursor is included as metadata in the PipeWire stream
+ -->
+ <method name="RecordMonitor">
+ <arg name="connector" type="s" direction="in" />
+ <arg name="properties" type="a{sv}" direction="in" />
+ <arg name="stream_path" type="o" direction="out" />
+ </method>
+
+ <!--
+ RecordWindow:
+ @properties: Properties used determining what window to select
+ @stream_path: Path to the new stream object
+
+ Supported since API version 2.
+
+ Record a single window. The cursor will not be included.
+
+ Available @properties include:
+
+ * "window-id" (t): Id of the window to record.
+ * "cursor-mode" (u): Cursor mode. Default: 'hidden' (see RecordMonitor).
+ * "is-recording" (b): Whether this is a screen recording. May be
+ be used for choosing panel icon.
+ Default: false. Available since API version 4.
+
+ -->
+ <method name="RecordWindow">
+ <arg name="properties" type="a{sv}" direction="in" />
+ <arg name="stream_path" type="o" direction="out" />
+ </method>
+
+ <!--
+ RecordArea:
+ @x: X position of the recorded area
+ @y: Y position of the recorded area
+ @width: width of the recorded area
+ @height: height of the recorded area
+ @properties: Properties
+ @stream_path: Path to the new stream object
+
+ Record an area of the stage. The coordinates are in stage coordinates.
+ The size of the stream does not necessarily match the size of the
+ recorded area, and will depend on DPI scale of the affected monitors.
+
+ Available @properties include:
+
+ * "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
+ Available since API version 2.
+ * "is-recording" (b): Whether this is a screen recording. May be
+ be used for choosing panel icon.
+ Default: false. Available since API version 4.
+
+ Available cursor mode values:
+
+ 0: hidden - cursor is not included in the stream
+ 1: embedded - cursor is included in the framebuffer
+ 2: metadata - cursor is included as metadata in the PipeWire stream
+ -->
+ <method name="RecordArea">
+ <arg name="x" type="i" direction="in" />
+ <arg name="y" type="i" direction="in" />
+ <arg name="width" type="i" direction="in" />
+ <arg name="height" type="i" direction="in" />
+ <arg name="properties" type="a{sv}" direction="in" />
+ <arg name="stream_path" type="o" direction="out" />
+ </method>
+ </interface>
+
+ <!--
+ org.gnome.Mutter.ScreenCast.Stream:
+ @short_description: Screen cast stream
+ -->
+ <interface name="org.gnome.Mutter.ScreenCast.Stream">
+
+ <!--
+ PipeWireStreamAdded:
+ @short_description: Pipewire stream added
+
+ A signal emitted when PipeWire stream for the screen cast stream has
+ been created. The @node_id corresponds to the PipeWire stream node.
+ -->
+ <signal name="PipeWireStreamAdded">
+ <annotation name="org.gtk.GDBus.C.Name" value="pipewire-stream-added"/>
+ <arg name="node_id" type="u" direction="out" />
+ </signal>
+
+ <!--
+ Parameters:
+ @short_description: Optional stream parameters
+
+ Available parameters include:
+
+ * "position" (ii): Position of the source of the stream in the
+ compositor coordinate space.
+ * "size" (ii): Size of the source of the stream in the compositor
+ coordinate space.
+ -->
+ <property name="Parameters" type="a{sv}" access="read" />
+
+ </interface>
+
+</node>
diff --git a/data/dbus-interfaces/org.gnome.ScreenSaver.xml b/data/dbus-interfaces/org.gnome.ScreenSaver.xml
new file mode 100644
index 0000000..5a2ca98
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.ScreenSaver.xml
@@ -0,0 +1,18 @@
+<node>
+ <interface name="org.gnome.ScreenSaver">
+ <method name="Lock"/>
+ <method name="GetActive">
+ <arg type="b" direction="out" name="active"/>
+ </method>
+ <method name="SetActive">
+ <arg type="b" direction="in" name="value"/>
+ </method>
+ <method name="GetActiveTime">
+ <arg type="u" direction="out" name="value"/>
+ </method>
+ <signal name="ActiveChanged">
+ <arg name="new_value" type="b"/>
+ </signal>
+ <signal name="WakeUpScreen"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SessionManager.EndSessionDialog.xml b/data/dbus-interfaces/org.gnome.SessionManager.EndSessionDialog.xml
new file mode 100644
index 0000000..3a57aa4
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SessionManager.EndSessionDialog.xml
@@ -0,0 +1,16 @@
+<node>
+ <interface name="org.gnome.SessionManager.EndSessionDialog">
+ <method name="Open">
+ <arg type="u" direction="in"/>
+ <arg type="u" direction="in"/>
+ <arg type="u" direction="in"/>
+ <arg type="ao" direction="in"/>
+ </method>
+ <method name="Close"/>
+ <signal name="ConfirmedLogout"/>
+ <signal name="ConfirmedReboot"/>
+ <signal name="ConfirmedShutdown"/>
+ <signal name="Canceled"/>
+ <signal name="Closed"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SessionManager.Inhibitor.xml b/data/dbus-interfaces/org.gnome.SessionManager.Inhibitor.xml
new file mode 100644
index 0000000..aa6a23b
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SessionManager.Inhibitor.xml
@@ -0,0 +1,13 @@
+<node>
+ <interface name="org.gnome.SessionManager.Inhibitor">
+ <method name="GetAppId">
+ <arg type="s" direction="out"/>
+ </method>
+ <method name="GetFlags">
+ <arg type="u" direction="out"/>
+ </method>
+ <method name="GetReason">
+ <arg type="s" direction="out"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SessionManager.Presence.xml b/data/dbus-interfaces/org.gnome.SessionManager.Presence.xml
new file mode 100644
index 0000000..78bc604
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SessionManager.Presence.xml
@@ -0,0 +1,11 @@
+<node>
+ <interface name="org.gnome.SessionManager.Presence">
+ <method name="SetStatus">
+ <arg type="u" direction="in"/>
+ </method>
+ <property name="status" type="u" access="readwrite"/>
+ <signal name="StatusChanged">
+ <arg type="u" direction="out"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SessionManager.xml b/data/dbus-interfaces/org.gnome.SessionManager.xml
new file mode 100644
index 0000000..c81c90f
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SessionManager.xml
@@ -0,0 +1,23 @@
+<node>
+ <interface name="org.gnome.SessionManager">
+ <method name="Logout">
+ <arg type="u" direction="in"/>
+ </method>
+ <method name="Shutdown"/>
+ <method name="Reboot"/>
+ <method name="CanShutdown">
+ <arg type="b" direction="out"/>
+ </method>
+ <method name="IsInhibited">
+ <arg type="u" direction="in"/>
+ <arg type="b" direction="out"/>
+ </method>
+ <property name="SessionIsActive" type="b" access="read"/>
+ <signal name="InhibitorAdded">
+ <arg type="o" direction="out"/>
+ </signal>
+ <signal name="InhibitorRemoved">
+ <arg type="o" direction="out"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SettingsDaemon.Color.xml b/data/dbus-interfaces/org.gnome.SettingsDaemon.Color.xml
new file mode 100644
index 0000000..1528f90
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SettingsDaemon.Color.xml
@@ -0,0 +1,6 @@
+<node>
+ <interface name="org.gnome.SettingsDaemon.Color">
+ <property name="DisabledUntilTomorrow" type="b" access="readwrite"/>
+ <property name="NightLightActive" type="b" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SettingsDaemon.Power.Screen.xml b/data/dbus-interfaces/org.gnome.SettingsDaemon.Power.Screen.xml
new file mode 100644
index 0000000..05e4b8d
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SettingsDaemon.Power.Screen.xml
@@ -0,0 +1,5 @@
+<node>
+ <interface name="org.gnome.SettingsDaemon.Power.Screen">
+ <property name="Brightness" type="i" access="readwrite"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SettingsDaemon.Rfkill.xml b/data/dbus-interfaces/org.gnome.SettingsDaemon.Rfkill.xml
new file mode 100644
index 0000000..bde65f9
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SettingsDaemon.Rfkill.xml
@@ -0,0 +1,11 @@
+<node>
+ <interface name="org.gnome.SettingsDaemon.Rfkill">
+ <property name="AirplaneMode" type="b" access="readwrite"/>
+ <property name="HasAirplaneMode" type="b" access="read"/>
+ <property name="HardwareAirplaneMode" type="b" access="read"/>
+ <property name="BluetoothAirplaneMode" type="b" access="readwrite"/>
+ <property name="BluetoothHasAirplaneMode" type="b" access="read"/>
+ <property name="BluetoothHardwareAirplaneMode" type="b" access="readwrite"/>
+ <property name="ShouldShowAirplaneMode" type="b" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.SettingsDaemon.Wacom.xml b/data/dbus-interfaces/org.gnome.SettingsDaemon.Wacom.xml
new file mode 100644
index 0000000..1370214
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.SettingsDaemon.Wacom.xml
@@ -0,0 +1,13 @@
+<node>
+<interface name="org.gnome.SettingsDaemon.Wacom">
+ <method name="SetGroupModeLED">
+ <arg name="device_path" direction="in" type="s"/>
+ <arg name="group" direction="in" type="u"/>
+ <arg name="mode" direction="in" type="u"/>
+ </method>
+ <method name="SetOLEDLabels">
+ <arg name="device_path" direction="in" type="s"/>
+ <arg name="labels" direction="in" type="as"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.AudioDeviceSelection.xml b/data/dbus-interfaces/org.gnome.Shell.AudioDeviceSelection.xml
new file mode 100644
index 0000000..28cc6da
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.AudioDeviceSelection.xml
@@ -0,0 +1,12 @@
+<node>
+ <interface name="org.gnome.Shell.AudioDeviceSelection">
+ <method name="Open">
+ <arg name="devices" direction="in" type="as"/>
+ </method>
+ <method name="Close">
+ </method>
+ <signal name="DeviceSelected">
+ <arg name="device" type="s"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.CalendarServer.xml b/data/dbus-interfaces/org.gnome.Shell.CalendarServer.xml
new file mode 100644
index 0000000..5814710
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.CalendarServer.xml
@@ -0,0 +1,19 @@
+<node>
+ <interface name="org.gnome.Shell.CalendarServer">
+ <method name="SetTimeRange">
+ <arg type="x" name="since" direction="in"/>
+ <arg type="x" name="until" direction="in"/>
+ <arg type="b" name="force_reload" direction="in"/>
+ </method>
+ <signal name="EventsAddedOrUpdated">
+ <arg type="a(ssxxa{sv})" name="events" direction="out"/>
+ </signal>
+ <signal name="EventsRemoved">
+ <arg type="as" name="ids" direction="out"/>
+ </signal>
+ <signal name="ClientDisappeared">
+ <arg type="s" name="source_uid" direction="out"/>
+ </signal>
+ <property name="HasCalendars" type="b" access="read" />
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.ClocksIntegration.xml b/data/dbus-interfaces/org.gnome.Shell.ClocksIntegration.xml
new file mode 100644
index 0000000..0cbb132
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.ClocksIntegration.xml
@@ -0,0 +1,15 @@
+<node>
+
+ <!--
+ org.gnome.Shell.ClocksIntegration:
+ @short_description: Clocks integration interface
+
+ The interface used for exporting location settings to GNOME Shell's
+ world clocks integration.
+ -->
+ <interface name="org.gnome.Shell.ClocksIntegration">
+
+ <property name="Locations" type="av" access="read"/>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.Extensions.xml b/data/dbus-interfaces/org.gnome.Shell.Extensions.xml
new file mode 100644
index 0000000..51448dc
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.Extensions.xml
@@ -0,0 +1,267 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.Extensions:
+ @short_description: Extensions interface
+
+ The interface used to query and manage extensions.
+ -->
+ <interface name="org.gnome.Shell.Extensions">
+
+ <!--
+ ListExtensions:
+ @extensions: A dictionary of extension infos
+
+ Get a list of installed extensions. The returned @extensions
+ dictionary maps extension UUIDs to info vardicts. See
+ GetExtensionInfo() for documentation on possible keys.
+ -->
+ <method name="ListExtensions">
+ <arg type="a{sa{sv}}" direction="out" name="extensions"/>
+ </method>
+
+ <!--
+ GetExtensionInfo:
+ @uuid: The UUID of the extension
+ @info: The returned extension info
+
+ The information returned in the @info vardict depends on the
+ metadata the extension provides, however it is guaranteed to
+ contain the following keys:
+
+ <variablelist>
+ <varlistentry>
+ <term>uuid s</term>
+ <listitem><para>The UUID of the extension</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>name s</term>
+ <listitem><para>The name of the extension</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>description s</term>
+ <listitem><para>
+ A short summary that describes what the extension does
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>shell-version as</term>
+ <listitem><para>An array of support shell versions</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>type d</term>
+ <listitem><para>
+ The type of extension:
+ <simplelist>
+ <member>1: SYSTEM</member>
+ <member>2: PER_USER</member>
+ </simplelist>
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>state d</term>
+ <listitem><para>
+ The state the extension is in:
+ <simplelist>
+ <member>1: ENABLED</member>
+ <member>2: DISABLED</member>
+ <member>3: ERROR</member>
+ <member>4: OUT_OF_DATE</member>
+ <member>5: DOWNLOADING</member>
+ <member>6: INITIALIZED</member>
+ <member>99: UNINSTALLED</member>
+ </simplelist>
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>path s</term>
+ <listitem><para>The extension directory</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>error s</term>
+ <listitem><para>The most recent error caught in init(), enable() or disable()</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>hasPrefs b</term>
+ <listitem><para>Whether the extension includes preference UI</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ By convention, many extensions will also include the following keys:
+ <variablelist>
+ <varlistentry>
+ <term>version d</term>
+ <listitem><para>The extension version</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>url s</term>
+ <listitem><para>The URL to the extension homepage or repository</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>settings-schema s</term>
+ <listitem><para>The ID of a bundled GSettings schema</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>gettext-domain s</term>
+ <listitem><para>The domain used for translations</para></listitem>
+ </varlistentry>
+ </variablelist>
+ -->
+ <method name="GetExtensionInfo">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="a{sv}" direction="out" name="info"/>
+ </method>
+
+ <!--
+ GetExtensionErrors:
+ @uuid: The UUID of the extension
+ @errors: The returned errors
+
+ Get the list of errors that caused the extension
+ to be in ERROR state.
+ -->
+ <method name="GetExtensionErrors">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="as" direction="out" name="errors"/>
+ </method>
+
+ <!--
+ InstallRemoteExtension:
+ @uuid: The UUID of the extension
+ @result: The result of the operation
+
+ Download and install an extension.
+ -->
+ <method name="InstallRemoteExtension">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="s" direction="out" name="result"/>
+ </method>
+
+ <!--
+ UninstallExtension:
+ @uuid: The UUID of the extension
+ @success: Whether the operation was successful
+
+ Uninstall an extension.
+ -->
+ <method name="UninstallExtension">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="b" direction="out" name="success"/>
+ </method>
+
+ <!--
+ ReloadExtension:
+ @uuid: The UUID of the extension
+
+ Reload an extension.
+ -->
+ <method name="ReloadExtension">
+ <arg type="s" direction="in" name="uuid"/>
+ </method>
+
+ <!--
+ EnableExtension:
+ @uuid: The UUID of the extension
+ @success: Whether the operation was successful
+
+ Enable an extension.
+ -->
+ <method name="EnableExtension">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="b" direction="out" name="success"/>
+ </method>
+
+ <!--
+ DisableExtension:
+ @uuid: The UUID of the extension
+ @success: Whether the operation was successful
+
+ Disable an extension.
+ -->
+ <method name="DisableExtension">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="b" direction="out" name="success"/>
+ </method>
+
+ <!--
+ LaunchExtensionPrefs:
+ Deprecated for OpenExtensionPrefs
+ -->
+ <method name="LaunchExtensionPrefs">
+ <arg type="s" direction="in" name="uuid"/>
+ </method>
+
+ <!--
+ OpenExtensionPrefs:
+ @uuid: The UUID of the extension
+ @parent_window: Identifier for the application window
+ @options: Vardict with further options
+
+ Opens the prefs dialog of extension @uuid.
+
+ The following @options are recognized:
+
+ <variablelist>
+ <varlistentry>
+ <term>modal b</term>
+ <listitem>
+ <para>Whether the prefs window should be modal, default: false</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ -->
+ <method name="OpenExtensionPrefs">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="s" direction="in" name="parent_window"/>
+ <arg type="a{sv}" direction="in" name="options"/>
+ </method>
+
+ <!--
+ CheckForUpdates:
+ Update all extensions for which updates are available
+ -->
+ <method name="CheckForUpdates"/>
+
+ <signal name="ExtensionStateChanged">
+ <arg type="s" name="uuid"/>
+ <arg type="a{sv}" name="state"/>
+ </signal>
+
+ <!--
+ ExtensionStatusChanged:
+ Deprecated for ExtensionStateChanged
+ -->
+ <signal name="ExtensionStatusChanged">
+ <arg type="s" name="uuid"/>
+ <arg type="i" name="state"/>
+ <arg type="s" name="error"/>
+ </signal>
+
+ <!--
+ ShellVersion:
+ The GNOME Shell version
+ -->
+ <property name="ShellVersion" type="s" access="read"/>
+
+ <!--
+ UserExtensionsEnabled:
+ Whether user extensions are enabled
+ -->
+ <property name="UserExtensionsEnabled" type="b" access="readwrite"/>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.HotplugSniffer.xml b/data/dbus-interfaces/org.gnome.Shell.HotplugSniffer.xml
new file mode 100644
index 0000000..b4373cc
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.HotplugSniffer.xml
@@ -0,0 +1,8 @@
+<node>
+ <interface name="org.gnome.Shell.HotplugSniffer">
+ <method name="SniffURI">
+ <arg type="s" direction="in"/>
+ <arg type="as" direction="out"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.Introspect.xml b/data/dbus-interfaces/org.gnome.Shell.Introspect.xml
new file mode 100644
index 0000000..cb19cfe
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.Introspect.xml
@@ -0,0 +1,89 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.Introspect:
+ @short_description: Introspection interface
+
+ The interface used to introspect the state of Shell, such as running
+ applications, currently active application, etc.
+ -->
+ <interface name="org.gnome.Shell.Introspect">
+
+ <!--
+ RunningApplicationsChanged:
+ @short_description: Notifies when the running applications changes
+ -->
+ <signal name="RunningApplicationsChanged" />
+
+ <!--
+ WindowsChanged:
+ @short_description: Notifies when any window opens or closes
+ -->
+ <signal name="WindowsChanged" />
+
+ <!--
+ GetRunningApplications:
+ @short_description: Retrieves the description of all running applications
+
+ Each application is associated by an application ID. The details of
+ each application consists of a varlist of keys and values. Available
+ keys are listed below.
+
+ 'active-on-seats' - (as) list of seats the application is active on
+ (a seat only has at most one active
+ application)
+ -->
+ <method name="GetRunningApplications">
+ <arg name="apps" direction="out" type="a{sa{sv}}" />
+ </method>
+
+ <!--
+ GetWindows:
+ @short_description: Retrieves the current list of windows and their properties
+
+ A window is exposed as:
+ * t ID: unique ID of the window
+ * a{sv} properties: high-level properties
+
+ Known properties:
+
+ - "title" (s): (readonly) title of the window
+ - "app-id" (s): (readonly) application ID of the window
+ - "wm-class" (s): (readonly) class of the window
+ - "client-type" (u): (readonly) 0 for Wayland, 1 for X11
+ - "is-hidden" (b): (readonly) if the window is currently hidden
+ - "has-focus" (b): (readonly) if the window currently have
+ keyboard focus
+ - "width" (u): (readonly) width of the window
+ - "height" (u): (readonly) height of the window
+ -->
+ <method name="GetWindows">
+ <arg name="windows" direction="out" type="a{ta{sv}}" />
+ </method>
+
+ <!--
+ AnimationsEnabled:
+ @short_description: Whether the shell animations are enabled
+
+ By default determined by the org.gnome.desktop.interface enable-animations
+ gsetting, but may be overridden, e.g. if there is an active screen cast or
+ remote desktop session that asked for animations to be disabled.
+
+ Since: 2
+ -->
+ <property name="AnimationsEnabled" type="b" access="read"/>
+
+ <!--
+ ScreenSize:
+ @short_description: The size of the screen
+
+ Since: 3
+ -->
+ <property name="ScreenSize" type="(ii)" access="read"/>
+
+ <property name="version" type="u" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.PadOsd.xml b/data/dbus-interfaces/org.gnome.Shell.PadOsd.xml
new file mode 100644
index 0000000..765973c
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.PadOsd.xml
@@ -0,0 +1,28 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.PadOSD:
+ @short_description: Pad OSD interface
+
+ The interface used to show button map OSD on pad devices.
+ -->
+ <interface name='org.gnome.Shell.Wacom.PadOsd'>
+
+ <!--
+ Show:
+ @device_node: device node file, usually in /dev/input/...
+ @edition_mode: whether toggling edition mode on when showing
+
+ Shows the pad button map OSD for the requested device, the OSD
+ will be shown according the current device settings (output
+ mapping, left handed mode, ...)
+ -->
+ <method name='Show'>
+ <arg name='device_node' direction='in' type='o'/>
+ <arg name='edition_mode' direction='in' type='b'/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.PerfHelper.xml b/data/dbus-interfaces/org.gnome.Shell.PerfHelper.xml
new file mode 100644
index 0000000..98fea27
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.PerfHelper.xml
@@ -0,0 +1,13 @@
+<node>
+ <interface name="org.gnome.Shell.PerfHelper">
+ <method name="CreateWindow">
+ <arg type="i" direction="in"/>
+ <arg type="i" direction="in"/>
+ <arg type="b" direction="in"/>
+ <arg type="b" direction="in"/>
+ <arg type="b" direction="in"/>
+ </method>
+ <method name="WaitWindows"/>
+ <method name="DestroyWindows"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.PortalHelper.xml b/data/dbus-interfaces/org.gnome.Shell.PortalHelper.xml
new file mode 100644
index 0000000..8c6a13e
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.PortalHelper.xml
@@ -0,0 +1,19 @@
+<node>
+ <interface name="org.gnome.Shell.PortalHelper">
+ <method name="Authenticate">
+ <arg name="connection" type="o" direction="in"/>
+ <arg name="url" type="s" direction="in"/>
+ <arg name="timestamp" type="u" direction="in"/>
+ </method>
+ <method name="Close">
+ <arg name="connection" type="o" direction="in"/>
+ </method>
+ <method name="Refresh">
+ <arg name="connection" type="o" direction="in"/>
+ </method>
+ <signal name="Done">
+ <arg type="o" name="connection"/>
+ <arg type="u" name="result"/>
+ </signal>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.Screencast.xml b/data/dbus-interfaces/org.gnome.Shell.Screencast.xml
new file mode 100644
index 0000000..b4cd592
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.Screencast.xml
@@ -0,0 +1,96 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.Screencast:
+ @short_description: Screencast interface
+
+ The interface used to record screen contents.
+ -->
+ <interface name="org.gnome.Shell.Screencast">
+ <property name="ScreencastSupported" type="b" access="read"/>
+
+ <!--
+ Screencast:
+ @file_template: the template for the filename to use
+ @options: a dictionary of optional parameters
+ @success: whether the screencast was started successfully
+ @filename_used: the file where the screencast is being saved
+
+ Records a screencast of the whole screen and saves it
+ (by default) as webm video under a filename derived from
+ @file_template. The template is either a relative or absolute
+ filename which may contain some escape sequences - %d and %t
+ will be replaced by the start date and time of the recording.
+ If a relative name is used, the screencast will be saved in the
+ $XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
+ The actual filename of the saved video is returned in @filename_used.
+ The set of optional parameters in @options currently consists of:
+ 'draw-cursor'(b): whether the cursor should be included in the
+ recording (true)
+ 'framerate'(i): the number of frames per second that should be
+ recorded if possible (30)
+ 'pipeline'(s): the GStreamer pipeline used to encode recordings
+ in gst-launch format; if not specified, the
+ recorder will produce vp8 (webm) video (unset)
+ -->
+ <method name="Screencast">
+ <arg type="s" direction="in" name="file_template"/>
+ <arg type="a{sv}" direction="in" name="options"/>
+ <arg type="b" direction="out" name="success"/>
+ <arg type="s" direction="out" name="filename_used"/>
+ </method>
+
+ <!--
+ ScreencastArea:
+ @x: the X coordinate of the area to capture
+ @y: the Y coordinate of the area to capture
+ @width: the width of the area to capture
+ @height: the height of the area to capture
+ @file_template: the template for the filename to use
+ @options: a dictionary of optional parameters
+ @success: whether the screencast was started successfully
+ @filename_used: the file where the screencast is being saved
+
+ Records a screencast of the passed in area and saves it
+ (by default) as webm video under a filename derived from
+ @file_template. The template is either a relative or absolute
+ filename which may contain some escape sequences - %d and %t
+ will be replaced by the start date and time of the recording.
+ If a relative name is used, the screencast will be saved in the
+ $XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
+ The actual filename of the saved video is returned in @filename_used.
+ The set of optional parameters in @options currently consists of:
+ 'draw-cursor'(b): whether the cursor should be included in the
+ recording (true)
+ 'framerate'(i): the number of frames per second that should be
+ recorded if possible (30)
+ 'pipeline'(s): the GStreamer pipeline used to encode recordings
+ in gst-launch format; if not specified, the
+ recorder will produce vp8 (webm) video (unset)
+ -->
+ <method name="ScreencastArea">
+ <arg type="i" direction="in" name="x"/>
+ <arg type="i" direction="in" name="y"/>
+ <arg type="i" direction="in" name="width"/>
+ <arg type="i" direction="in" name="height"/>
+ <arg type="s" direction="in" name="file_template"/>
+ <arg type="a{sv}" direction="in" name="options"/>
+ <arg type="b" direction="out" name="success"/>
+ <arg type="s" direction="out" name="filename_used"/>
+ </method>
+
+ <!--
+ StopScreencast:
+ @success: whether stopping the recording was successful
+
+ Stop the recording started by either Screencast or ScreencastArea.
+ -->
+ <method name="StopScreencast">
+ <arg type="b" direction="out" name="success"/>
+ </method>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.Screenshot.xml b/data/dbus-interfaces/org.gnome.Shell.Screenshot.xml
new file mode 100644
index 0000000..528b01e
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.Screenshot.xml
@@ -0,0 +1,145 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.Screenshot:
+ @short_description: Screenshot interface
+
+ The interface used to capture pictures of the screen contents.
+ -->
+ <interface name="org.gnome.Shell.Screenshot">
+
+ <!--
+ Screenshot:
+ @filename: The filename for the screenshot
+ @include_cursor: Whether to include the cursor image or not
+ @flash: Whether to flash the screen or not
+ @success: whether the screenshot was captured
+ @filename_used: the file where the screenshot was saved
+
+ Takes a screenshot of the whole screen and saves it
+ in @filename as png image, it returns a boolean
+ indicating whether the operation was successful or not.
+ @filename can either be an absolute path or a basename, in
+ which case the screenshot will be saved in the $XDG_PICTURES_DIR
+ or the home directory if it doesn't exist. The filename used
+ to save the screenshot will be returned in @filename_used.
+ -->
+ <method name="Screenshot">
+ <arg type="b" direction="in" name="include_cursor"/>
+ <arg type="b" direction="in" name="flash"/>
+ <arg type="s" direction="in" name="filename"/>
+ <arg type="b" direction="out" name="success"/>
+ <arg type="s" direction="out" name="filename_used"/>
+ </method>
+
+ <!--
+ ScreenshotWindow:
+ @include_frame: Whether to include the frame or not
+ @include_cursor: Whether to include the cursor image or not
+ @flash: Whether to flash the window area or not
+ @filename: The filename for the screenshot
+ @success: whether the screenshot was captured
+ @filename_used: the file where the screenshot was saved
+
+ Takes a screenshot of the focused window (optionally omitting the frame)
+ and saves it in @filename as png image, it returns a boolean
+ indicating whether the operation was successful or not.
+ @filename can either be an absolute path or a basename, in
+ which case the screenshot will be saved in the $XDG_PICTURES_DIR
+ or the home directory if it doesn't exist. The filename used
+ to save the screenshot will be returned in @filename_used.
+ -->
+ <method name="ScreenshotWindow">
+ <arg type="b" direction="in" name="include_frame"/>
+ <arg type="b" direction="in" name="include_cursor"/>
+ <arg type="b" direction="in" name="flash"/>
+ <arg type="s" direction="in" name="filename"/>
+ <arg type="b" direction="out" name="success"/>
+ <arg type="s" direction="out" name="filename_used"/>
+ </method>
+
+ <!--
+ ScreenshotArea:
+ @x: the X coordinate of the area to capture
+ @y: the Y coordinate of the area to capture
+ @width: the width of the area to capture
+ @height: the height of the area to capture
+ @flash: whether to flash the area or not
+ @filename: the filename for the screenshot
+ @success: whether the screenshot was captured
+ @filename_used: the file where the screenshot was saved
+
+ Takes a screenshot of the passed in area and saves it
+ in @filename as png image, it returns a boolean
+ indicating whether the operation was successful or not.
+ @filename can either be an absolute path or a basename, in
+ which case the screenshot will be saved in the $XDG_PICTURES_DIR
+ or the home directory if it doesn't exist. The filename used
+ to save the screenshot will be returned in @filename_used.
+ -->
+ <method name="ScreenshotArea">
+ <arg type="i" direction="in" name="x"/>
+ <arg type="i" direction="in" name="y"/>
+ <arg type="i" direction="in" name="width"/>
+ <arg type="i" direction="in" name="height"/>
+ <arg type="b" direction="in" name="flash"/>
+ <arg type="s" direction="in" name="filename"/>
+ <arg type="b" direction="out" name="success"/>
+ <arg type="s" direction="out" name="filename_used"/>
+ </method>
+
+ <!--
+ PickColor:
+
+ Picks a color and returns the result.
+
+ The @result vardict contains:
+ <variablelist>
+ <varlistentry>
+ <term>color (ddd)</term>
+ <listitem><para>The color, RGB values in the range [0,1].</para></listitem>
+ </varlistentry>
+ </variablelist>
+ -->
+ <method name="PickColor">
+ <arg type="a{sv}" direction="out" name="result"/>
+ </method>
+
+ <!--
+ FlashArea:
+ @x: the X coordinate of the area to flash
+ @y: the Y coordinate of the area to flash
+ @width: the width of the area to flash
+ @height: the height of the area to flash
+
+ Renders a flash spot effect in the specified rectangle of the screen.
+ -->
+ <method name="FlashArea">
+ <arg type="i" direction="in" name="x"/>
+ <arg type="i" direction="in" name="y"/>
+ <arg type="i" direction="in" name="width"/>
+ <arg type="i" direction="in" name="height"/>
+ </method>
+
+ <!--
+ SelectArea:
+ @x: the X coordinate of the selected area
+ @y: the Y coordinate of the selected area
+ @width: the width of the selected area
+ @height: the height of the selected area
+
+ Interactively allows the user to select a rectangular area of
+ the screen, and returns its coordinates.
+ -->
+ <method name="SelectArea">
+ <arg type="i" direction="out" name="x"/>
+ <arg type="i" direction="out" name="y"/>
+ <arg type="i" direction="out" name="width"/>
+ <arg type="i" direction="out" name="height"/>
+ </method>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.Wacom.PadOsd.xml b/data/dbus-interfaces/org.gnome.Shell.Wacom.PadOsd.xml
new file mode 100644
index 0000000..5e046c7
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.Wacom.PadOsd.xml
@@ -0,0 +1,8 @@
+<node>
+ <interface name="org.gnome.Shell.Wacom.PadOsd">
+ <method name="Show">
+ <arg name="device_node" direction="in" type="o"/>
+ <arg name="edition_mode" direction="in" type="b"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.WeatherIntegration.xml b/data/dbus-interfaces/org.gnome.Shell.WeatherIntegration.xml
new file mode 100644
index 0000000..1e89bbe
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.WeatherIntegration.xml
@@ -0,0 +1,16 @@
+<node>
+
+ <!--
+ org.gnome.Shell.WeatherIntegration:
+ @short_description: Weather integration interface
+
+ The interface used for exporting location settings to GNOME Shell's
+ weather integration.
+ -->
+ <interface name="org.gnome.Shell.WeatherIntegration">
+
+ <property name="AutomaticLocation" type="b" access="read"/>
+ <property name="Locations" type="av" access="read"/>
+
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.Shell.xml b/data/dbus-interfaces/org.gnome.Shell.xml
new file mode 100644
index 0000000..3ffb5c3
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.xml
@@ -0,0 +1,47 @@
+<node>
+ <interface name="org.gnome.Shell">
+ <method name="Eval">
+ <arg type="s" direction="in" name="script"/>
+ <arg type="b" direction="out" name="success"/>
+ <arg type="s" direction="out" name="result"/>
+ </method>
+ <method name="FocusSearch"/>
+ <method name="ShowOSD">
+ <arg type="a{sv}" direction="in" name="params"/>
+ </method>
+ <method name="ShowMonitorLabels">
+ <arg type="a{sv}" direction="in" name="params"/>
+ </method>
+ <method name="HideMonitorLabels"/>
+ <method name="FocusApp">
+ <arg type="s" direction="in" name="id"/>
+ </method>
+ <method name="ShowApplications"/>
+ <method name="GrabAccelerator">
+ <arg type="s" direction="in" name="accelerator"/>
+ <arg type="u" direction="in" name="modeFlags"/>
+ <arg type="u" direction="in" name="grabFlags"/>
+ <arg type="u" direction="out" name="action"/>
+ </method>
+ <method name="GrabAccelerators">
+ <arg type="a(suu)" direction="in" name="accelerators"/>
+ <arg type="au" direction="out" name="actions"/>
+ </method>
+ <method name="UngrabAccelerator">
+ <arg type="u" direction="in" name="action"/>
+ <arg type="b" direction="out" name="success"/>
+ </method>
+ <method name="UngrabAccelerators">
+ <arg type="au" direction="in" name="action"/>
+ <arg type="b" direction="out" name="success"/>
+ </method>
+ <method name="ScreenTransition"/>
+ <signal name="AcceleratorActivated">
+ <arg name="action" type="u"/>
+ <arg name="parameters" type="a{sv}"/>
+ </signal>
+ <property name="Mode" type="s" access="read"/>
+ <property name="OverviewActive" type="b" access="readwrite"/>
+ <property name="ShellVersion" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.ShellSearchProvider.xml b/data/dbus-interfaces/org.gnome.ShellSearchProvider.xml
new file mode 100644
index 0000000..78ad305
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.ShellSearchProvider.xml
@@ -0,0 +1,73 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.SearchProvider:
+ @short_description: Search provider interface
+
+ The interface used for integrating into GNOME Shell's search
+ interface. This interface is deprecated, and org.gnome.Shell.SearchProvider2 should be used instead.
+ -->
+ <interface name="org.gnome.Shell.SearchProvider">
+
+ <!--
+ GetInitialResultSet:
+ @terms: Array of search terms, which the provider should treat as logical AND.
+ @results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
+
+ Called when the user first begins a search.
+ -->
+ <method name="GetInitialResultSet">
+ <arg type="as" name="terms" direction="in" />
+ <arg type="as" name="results" direction="out" />
+ </method>
+
+ <!--
+ GetSubsearchResultSet:
+ @previous_results: Array of results previously returned by GetInitialResultSet().
+ @terms: Array of updated search terms, which the provider should treat as logical AND.
+ @results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
+
+ Called when a search is performed which is a "subsearch" of
+ the previous search, e.g. the method may return less results, but
+ not more or different results.
+
+ This allows search providers to only search through the previous
+ result set, rather than possibly performing a full re-query.
+ -->
+ <method name="GetSubsearchResultSet">
+ <arg type="as" name="previous_results" direction="in" />
+ <arg type="as" name="terms" direction="in" />
+ <arg type="as" name="results" direction="out" />
+ </method>
+
+ <!--
+ GetResultMetas:
+ @identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
+ @metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
+
+ Return an array of meta data used to display each given result
+ -->
+ <method name="GetResultMetas">
+ <arg type="as" name="identifiers" direction="in" />
+ <arg type="aa{sv}" name="metas" direction="out" />
+ </method>
+
+ <!--
+ ActivateResult:
+ @identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
+
+ Called when the users chooses a given result. The result should
+ be displayed in the application associated with the corresponding
+ provider.
+
+ This method is deprecated, and providers should implement ActivateResult2()
+ instead.
+ -->
+ <method name="ActivateResult">
+ <arg type="s" name="identifier" direction="in" />
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml b/data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml
new file mode 100644
index 0000000..9502340
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.ShellSearchProvider2.xml
@@ -0,0 +1,87 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.SearchProvider2:
+ @short_description: Search provider interface
+
+ The interface used for integrating into GNOME Shell's search
+ interface (version 2).
+ -->
+ <interface name="org.gnome.Shell.SearchProvider2">
+
+ <!--
+ GetInitialResultSet:
+ @terms: Array of search terms, which the provider should treat as logical AND.
+ @results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
+
+ Called when the user first begins a search.
+ -->
+ <method name="GetInitialResultSet">
+ <arg type="as" name="terms" direction="in" />
+ <arg type="as" name="results" direction="out" />
+ </method>
+
+ <!--
+ GetSubsearchResultSet:
+ @previous_results: Array of results previously returned by GetInitialResultSet().
+ @terms: Array of updated search terms, which the provider should treat as logical AND.
+ @results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
+
+ Called when a search is performed which is a "subsearch" of
+ the previous search, e.g. the method may return less results, but
+ not more or different results.
+
+ This allows search providers to only search through the previous
+ result set, rather than possibly performing a full re-query.
+ -->
+ <method name="GetSubsearchResultSet">
+ <arg type="as" name="previous_results" direction="in" />
+ <arg type="as" name="terms" direction="in" />
+ <arg type="as" name="results" direction="out" />
+ </method>
+
+ <!--
+ GetResultMetas:
+ @identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
+ @metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also be specified if more context would help the user find the desired result.
+
+ Return an array of meta data used to display each given result
+ -->
+ <method name="GetResultMetas">
+ <arg type="as" name="identifiers" direction="in" />
+ <arg type="aa{sv}" name="metas" direction="out" />
+ </method>
+
+ <!--
+ ActivateResult:
+ @identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
+ @terms: Array of search terms, which the provider should treat as logical AND.
+ @timestamp: A timestamp of the user interaction that triggered this call
+
+ Called when the users chooses a given result. The result should
+ be displayed in the application associated with the corresponding
+ provider. The provided search terms can be used to allow launching a full search in
+ the application.
+ -->
+ <method name="ActivateResult">
+ <arg type="s" name="identifier" direction="in" />
+ <arg type="as" name="terms" direction="in" />
+ <arg type="u" name="timestamp" direction="in" />
+ </method>
+
+ <!--
+ LaunchSearch:
+ @terms: Array of search terms, which the provider should treat as logical AND.
+ @timestamp: A timestamp of the user interaction that triggered this call
+
+ Asks the search provider to launch a full search in the application for the provided terms.
+ -->
+ <method name="LaunchSearch">
+ <arg type="as" name="terms" direction="in" />
+ <arg type="u" name="timestamp" direction="in" />
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.gtk.Notifications.xml b/data/dbus-interfaces/org.gtk.Notifications.xml
new file mode 100644
index 0000000..56a6cec
--- /dev/null
+++ b/data/dbus-interfaces/org.gtk.Notifications.xml
@@ -0,0 +1,13 @@
+<node>
+ <interface name="org.gtk.Notifications">
+ <method name="AddNotification">
+ <arg name="app_id" type="s" direction="in"/>
+ <arg name="id" type="s" direction="in"/>
+ <arg name="notification" type="a{sv}" direction="in"/>
+ </method>
+ <method name="RemoveNotification">
+ <arg name="app_id" type="s" direction="in"/>
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.mpris.MediaPlayer2.Player.xml b/data/dbus-interfaces/org.mpris.MediaPlayer2.Player.xml
new file mode 100644
index 0000000..550cb4c
--- /dev/null
+++ b/data/dbus-interfaces/org.mpris.MediaPlayer2.Player.xml
@@ -0,0 +1,12 @@
+<node>
+ <interface name="org.mpris.MediaPlayer2.Player">
+ <method name="PlayPause"/>
+ <method name="Next"/>
+ <method name="Previous"/>
+ <property name="CanGoNext" type="b" access="read"/>
+ <property name="CanGoPrevious" type="b" access="read"/>
+ <property name="CanPlay" type="b" access="read"/>
+ <property name="Metadata" type="a{sv}" access="read"/>
+ <property name="PlaybackStatus" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/data/dbus-interfaces/org.mpris.MediaPlayer2.xml b/data/dbus-interfaces/org.mpris.MediaPlayer2.xml
new file mode 100644
index 0000000..5febaf9
--- /dev/null
+++ b/data/dbus-interfaces/org.mpris.MediaPlayer2.xml
@@ -0,0 +1,7 @@
+<node>
+ <interface name="org.mpris.MediaPlayer2">
+ <method name="Raise"/>
+ <property name="CanRaise" type="b" access="read"/>
+ <property name="DesktopEntry" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/data/emoji.json b/data/emoji.json
new file mode 100644
index 0000000..72144df
--- /dev/null
+++ b/data/emoji.json
@@ -0,0 +1,15740 @@
+[
+ {
+ "no": 1,
+ "code": "1F600",
+ "char": "😀",
+ "name": "grinning face"
+ },
+ {
+ "no": 2,
+ "code": "1F601",
+ "char": "😁",
+ "name": "beaming face with smiling eyes"
+ },
+ {
+ "no": 3,
+ "code": "1F602",
+ "char": "😂",
+ "name": "face with tears of joy"
+ },
+ {
+ "no": 4,
+ "code": "1F923",
+ "char": "🤣",
+ "name": "rolling on the floor laughing"
+ },
+ {
+ "no": 5,
+ "code": "1F603",
+ "char": "😃",
+ "name": "grinning face with big eyes"
+ },
+ {
+ "no": 6,
+ "code": "1F604",
+ "char": "😄",
+ "name": "grinning face with smiling eyes"
+ },
+ {
+ "no": 7,
+ "code": "1F605",
+ "char": "😅",
+ "name": "grinning face with sweat"
+ },
+ {
+ "no": 8,
+ "code": "1F606",
+ "char": "😆",
+ "name": "grinning squinting face"
+ },
+ {
+ "no": 9,
+ "code": "1F609",
+ "char": "😉",
+ "name": "winking face"
+ },
+ {
+ "no": 10,
+ "code": "1F60A",
+ "char": "😊",
+ "name": "smiling face with smiling eyes"
+ },
+ {
+ "no": 11,
+ "code": "1F60B",
+ "char": "😋",
+ "name": "face savoring food"
+ },
+ {
+ "no": 12,
+ "code": "1F60E",
+ "char": "😎",
+ "name": "smiling face with sunglasses"
+ },
+ {
+ "no": 13,
+ "code": "1F60D",
+ "char": "😍",
+ "name": "smiling face with heart-eyes"
+ },
+ {
+ "no": 14,
+ "code": "1F618",
+ "char": "😘",
+ "name": "face blowing a kiss"
+ },
+ {
+ "no": 15,
+ "code": "1F617",
+ "char": "😗",
+ "name": "kissing face"
+ },
+ {
+ "no": 16,
+ "code": "1F619",
+ "char": "😙",
+ "name": "kissing face with smiling eyes"
+ },
+ {
+ "no": 17,
+ "code": "1F61A",
+ "char": "😚",
+ "name": "kissing face with closed eyes"
+ },
+ {
+ "no": 18,
+ "code": "263A",
+ "char": "☺",
+ "name": "smiling face"
+ },
+ {
+ "no": 19,
+ "code": "1F642",
+ "char": "🙂",
+ "name": "slightly smiling face"
+ },
+ {
+ "no": 20,
+ "code": "1F917",
+ "char": "🤗",
+ "name": "hugging face"
+ },
+ {
+ "no": 21,
+ "code": "1F929",
+ "char": "🤩",
+ "name": "⊛ star-struck"
+ },
+ {
+ "no": 22,
+ "code": "1F914",
+ "char": "🤔",
+ "name": "thinking face"
+ },
+ {
+ "no": 23,
+ "code": "1F928",
+ "char": "🤨",
+ "name": "⊛ face with raised eyebrow"
+ },
+ {
+ "no": 24,
+ "code": "1F610",
+ "char": "😐",
+ "name": "neutral face"
+ },
+ {
+ "no": 25,
+ "code": "1F611",
+ "char": "😑",
+ "name": "expressionless face"
+ },
+ {
+ "no": 26,
+ "code": "1F636",
+ "char": "😶",
+ "name": "face without mouth"
+ },
+ {
+ "no": 27,
+ "code": "1F644",
+ "char": "🙄",
+ "name": "face with rolling eyes"
+ },
+ {
+ "no": 28,
+ "code": "1F60F",
+ "char": "😏",
+ "name": "smirking face"
+ },
+ {
+ "no": 29,
+ "code": "1F623",
+ "char": "😣",
+ "name": "persevering face"
+ },
+ {
+ "no": 30,
+ "code": "1F625",
+ "char": "😥",
+ "name": "sad but relieved face"
+ },
+ {
+ "no": 31,
+ "code": "1F62E",
+ "char": "😮",
+ "name": "face with open mouth"
+ },
+ {
+ "no": 32,
+ "code": "1F910",
+ "char": "🤐",
+ "name": "zipper-mouth face"
+ },
+ {
+ "no": 33,
+ "code": "1F62F",
+ "char": "😯",
+ "name": "hushed face"
+ },
+ {
+ "no": 34,
+ "code": "1F62A",
+ "char": "😪",
+ "name": "sleepy face"
+ },
+ {
+ "no": 35,
+ "code": "1F62B",
+ "char": "😫",
+ "name": "tired face"
+ },
+ {
+ "no": 36,
+ "code": "1F634",
+ "char": "😴",
+ "name": "sleeping face"
+ },
+ {
+ "no": 37,
+ "code": "1F60C",
+ "char": "😌",
+ "name": "relieved face"
+ },
+ {
+ "no": 38,
+ "code": "1F61B",
+ "char": "😛",
+ "name": "face with tongue"
+ },
+ {
+ "no": 39,
+ "code": "1F61C",
+ "char": "😜",
+ "name": "winking face with tongue"
+ },
+ {
+ "no": 40,
+ "code": "1F61D",
+ "char": "😝",
+ "name": "squinting face with tongue"
+ },
+ {
+ "no": 41,
+ "code": "1F924",
+ "char": "🤤",
+ "name": "drooling face"
+ },
+ {
+ "no": 42,
+ "code": "1F612",
+ "char": "😒",
+ "name": "unamused face"
+ },
+ {
+ "no": 43,
+ "code": "1F613",
+ "char": "😓",
+ "name": "downcast face with sweat"
+ },
+ {
+ "no": 44,
+ "code": "1F614",
+ "char": "😔",
+ "name": "pensive face"
+ },
+ {
+ "no": 45,
+ "code": "1F615",
+ "char": "😕",
+ "name": "confused face"
+ },
+ {
+ "no": 46,
+ "code": "1F643",
+ "char": "🙃",
+ "name": "upside-down face"
+ },
+ {
+ "no": 47,
+ "code": "1F911",
+ "char": "🤑",
+ "name": "money-mouth face"
+ },
+ {
+ "no": 48,
+ "code": "1F632",
+ "char": "😲",
+ "name": "astonished face"
+ },
+ {
+ "no": 49,
+ "code": "2639",
+ "char": "☹",
+ "name": "frowning face"
+ },
+ {
+ "no": 50,
+ "code": "1F641",
+ "char": "🙁",
+ "name": "slightly frowning face"
+ },
+ {
+ "no": 51,
+ "code": "1F616",
+ "char": "😖",
+ "name": "confounded face"
+ },
+ {
+ "no": 52,
+ "code": "1F61E",
+ "char": "😞",
+ "name": "disappointed face"
+ },
+ {
+ "no": 53,
+ "code": "1F61F",
+ "char": "😟",
+ "name": "worried face"
+ },
+ {
+ "no": 54,
+ "code": "1F624",
+ "char": "😤",
+ "name": "face with steam from nose"
+ },
+ {
+ "no": 55,
+ "code": "1F622",
+ "char": "😢",
+ "name": "crying face"
+ },
+ {
+ "no": 56,
+ "code": "1F62D",
+ "char": "😭",
+ "name": "loudly crying face"
+ },
+ {
+ "no": 57,
+ "code": "1F626",
+ "char": "😦",
+ "name": "frowning face with open mouth"
+ },
+ {
+ "no": 58,
+ "code": "1F627",
+ "char": "😧",
+ "name": "anguished face"
+ },
+ {
+ "no": 59,
+ "code": "1F628",
+ "char": "😨",
+ "name": "fearful face"
+ },
+ {
+ "no": 60,
+ "code": "1F629",
+ "char": "😩",
+ "name": "weary face"
+ },
+ {
+ "no": 61,
+ "code": "1F92F",
+ "char": "🤯",
+ "name": "⊛ exploding head"
+ },
+ {
+ "no": 62,
+ "code": "1F62C",
+ "char": "😬",
+ "name": "grimacing face"
+ },
+ {
+ "no": 63,
+ "code": "1F630",
+ "char": "😰",
+ "name": "anxious face with sweat"
+ },
+ {
+ "no": 64,
+ "code": "1F631",
+ "char": "😱",
+ "name": "face screaming in fear"
+ },
+ {
+ "no": 65,
+ "code": "1F633",
+ "char": "😳",
+ "name": "flushed face"
+ },
+ {
+ "no": 66,
+ "code": "1F92A",
+ "char": "🤪",
+ "name": "⊛ crazy face"
+ },
+ {
+ "no": 67,
+ "code": "1F635",
+ "char": "😵",
+ "name": "dizzy face"
+ },
+ {
+ "no": 68,
+ "code": "1F621",
+ "char": "😡",
+ "name": "pouting face"
+ },
+ {
+ "no": 69,
+ "code": "1F620",
+ "char": "😠",
+ "name": "angry face"
+ },
+ {
+ "no": 70,
+ "code": "1F92C",
+ "char": "🤬",
+ "name": "⊛ face with symbols on mouth"
+ },
+ {
+ "no": 71,
+ "code": "1F637",
+ "char": "😷",
+ "name": "face with medical mask"
+ },
+ {
+ "no": 72,
+ "code": "1F912",
+ "char": "🤒",
+ "name": "face with thermometer"
+ },
+ {
+ "no": 73,
+ "code": "1F915",
+ "char": "🤕",
+ "name": "face with head-bandage"
+ },
+ {
+ "no": 74,
+ "code": "1F922",
+ "char": "🤢",
+ "name": "nauseated face"
+ },
+ {
+ "no": 75,
+ "code": "1F92E",
+ "char": "🤮",
+ "name": "⊛ face vomiting"
+ },
+ {
+ "no": 76,
+ "code": "1F927",
+ "char": "🤧",
+ "name": "sneezing face"
+ },
+ {
+ "no": 77,
+ "code": "1F607",
+ "char": "😇",
+ "name": "smiling face with halo"
+ },
+ {
+ "no": 78,
+ "code": "1F920",
+ "char": "🤠",
+ "name": "cowboy hat face"
+ },
+ {
+ "no": 79,
+ "code": "1F921",
+ "char": "🤡",
+ "name": "clown face"
+ },
+ {
+ "no": 80,
+ "code": "1F925",
+ "char": "🤥",
+ "name": "lying face"
+ },
+ {
+ "no": 81,
+ "code": "1F92B",
+ "char": "🤫",
+ "name": "⊛ shushing face"
+ },
+ {
+ "no": 82,
+ "code": "1F92D",
+ "char": "🤭",
+ "name": "⊛ face with hand over mouth"
+ },
+ {
+ "no": 83,
+ "code": "1F9D0",
+ "char": "🧐",
+ "name": "⊛ face with monocle"
+ },
+ {
+ "no": 84,
+ "code": "1F913",
+ "char": "🤓",
+ "name": "nerd face"
+ },
+ {
+ "no": 85,
+ "code": "1F608",
+ "char": "😈",
+ "name": "smiling face with horns"
+ },
+ {
+ "no": 86,
+ "code": "1F47F",
+ "char": "👿",
+ "name": "angry face with horns"
+ },
+ {
+ "no": 87,
+ "code": "1F479",
+ "char": "👹",
+ "name": "ogre"
+ },
+ {
+ "no": 88,
+ "code": "1F47A",
+ "char": "👺",
+ "name": "goblin"
+ },
+ {
+ "no": 89,
+ "code": "1F480",
+ "char": "💀",
+ "name": "skull"
+ },
+ {
+ "no": 90,
+ "code": "2620",
+ "char": "☠",
+ "name": "skull and crossbones"
+ },
+ {
+ "no": 91,
+ "code": "1F47B",
+ "char": "👻",
+ "name": "ghost"
+ },
+ {
+ "no": 92,
+ "code": "1F47D",
+ "char": "👽",
+ "name": "alien"
+ },
+ {
+ "no": 93,
+ "code": "1F47E",
+ "char": "👾",
+ "name": "alien monster"
+ },
+ {
+ "no": 94,
+ "code": "1F916",
+ "char": "🤖",
+ "name": "robot face"
+ },
+ {
+ "no": 95,
+ "code": "1F4A9",
+ "char": "💩",
+ "name": "pile of poo"
+ },
+ {
+ "no": 96,
+ "code": "1F63A",
+ "char": "😺",
+ "name": "grinning cat face"
+ },
+ {
+ "no": 97,
+ "code": "1F638",
+ "char": "😸",
+ "name": "grinning cat face with smiling eyes"
+ },
+ {
+ "no": 98,
+ "code": "1F639",
+ "char": "😹",
+ "name": "cat face with tears of joy"
+ },
+ {
+ "no": 99,
+ "code": "1F63B",
+ "char": "😻",
+ "name": "smiling cat face with heart-eyes"
+ },
+ {
+ "no": 100,
+ "code": "1F63C",
+ "char": "😼",
+ "name": "cat face with wry smile"
+ },
+ {
+ "no": 101,
+ "code": "1F63D",
+ "char": "😽",
+ "name": "kissing cat face"
+ },
+ {
+ "no": 102,
+ "code": "1F640",
+ "char": "🙀",
+ "name": "weary cat face"
+ },
+ {
+ "no": 103,
+ "code": "1F63F",
+ "char": "😿",
+ "name": "crying cat face"
+ },
+ {
+ "no": 104,
+ "code": "1F63E",
+ "char": "😾",
+ "name": "pouting cat face"
+ },
+ {
+ "no": 105,
+ "code": "1F648",
+ "char": "🙈",
+ "name": "see-no-evil monkey"
+ },
+ {
+ "no": 106,
+ "code": "1F649",
+ "char": "🙉",
+ "name": "hear-no-evil monkey"
+ },
+ {
+ "no": 107,
+ "code": "1F64A",
+ "char": "🙊",
+ "name": "speak-no-evil monkey"
+ },
+ {
+ "no": 108,
+ "code": "1F476",
+ "char": "👶",
+ "name": "baby"
+ },
+ {
+ "no": 109,
+ "code": "1F476 1F3FB",
+ "char": "👶🏻",
+ "name": "baby: light skin tone"
+ },
+ {
+ "no": 110,
+ "code": "1F476 1F3FC",
+ "char": "👶🏼",
+ "name": "baby: medium-light skin tone"
+ },
+ {
+ "no": 111,
+ "code": "1F476 1F3FD",
+ "char": "👶🏽",
+ "name": "baby: medium skin tone"
+ },
+ {
+ "no": 112,
+ "code": "1F476 1F3FE",
+ "char": "👶🏾",
+ "name": "baby: medium-dark skin tone"
+ },
+ {
+ "no": 113,
+ "code": "1F476 1F3FF",
+ "char": "👶🏿",
+ "name": "baby: dark skin tone"
+ },
+ {
+ "no": 114,
+ "code": "1F9D2",
+ "char": "🧒",
+ "name": "⊛ child"
+ },
+ {
+ "no": 115,
+ "code": "1F9D2 1F3FB",
+ "char": "🧒🏻",
+ "name": "⊛ child: light skin tone"
+ },
+ {
+ "no": 116,
+ "code": "1F9D2 1F3FC",
+ "char": "🧒🏼",
+ "name": "⊛ child: medium-light skin tone"
+ },
+ {
+ "no": 117,
+ "code": "1F9D2 1F3FD",
+ "char": "🧒🏽",
+ "name": "⊛ child: medium skin tone"
+ },
+ {
+ "no": 118,
+ "code": "1F9D2 1F3FE",
+ "char": "🧒🏾",
+ "name": "⊛ child: medium-dark skin tone"
+ },
+ {
+ "no": 119,
+ "code": "1F9D2 1F3FF",
+ "char": "🧒🏿",
+ "name": "⊛ child: dark skin tone"
+ },
+ {
+ "no": 120,
+ "code": "1F466",
+ "char": "👦",
+ "name": "boy"
+ },
+ {
+ "no": 121,
+ "code": "1F466 1F3FB",
+ "char": "👦🏻",
+ "name": "boy: light skin tone"
+ },
+ {
+ "no": 122,
+ "code": "1F466 1F3FC",
+ "char": "👦🏼",
+ "name": "boy: medium-light skin tone"
+ },
+ {
+ "no": 123,
+ "code": "1F466 1F3FD",
+ "char": "👦🏽",
+ "name": "boy: medium skin tone"
+ },
+ {
+ "no": 124,
+ "code": "1F466 1F3FE",
+ "char": "👦🏾",
+ "name": "boy: medium-dark skin tone"
+ },
+ {
+ "no": 125,
+ "code": "1F466 1F3FF",
+ "char": "👦🏿",
+ "name": "boy: dark skin tone"
+ },
+ {
+ "no": 126,
+ "code": "1F467",
+ "char": "👧",
+ "name": "girl"
+ },
+ {
+ "no": 127,
+ "code": "1F467 1F3FB",
+ "char": "👧🏻",
+ "name": "girl: light skin tone"
+ },
+ {
+ "no": 128,
+ "code": "1F467 1F3FC",
+ "char": "👧🏼",
+ "name": "girl: medium-light skin tone"
+ },
+ {
+ "no": 129,
+ "code": "1F467 1F3FD",
+ "char": "👧🏽",
+ "name": "girl: medium skin tone"
+ },
+ {
+ "no": 130,
+ "code": "1F467 1F3FE",
+ "char": "👧🏾",
+ "name": "girl: medium-dark skin tone"
+ },
+ {
+ "no": 131,
+ "code": "1F467 1F3FF",
+ "char": "👧🏿",
+ "name": "girl: dark skin tone"
+ },
+ {
+ "no": 132,
+ "code": "1F9D1",
+ "char": "🧑",
+ "name": "⊛ adult"
+ },
+ {
+ "no": 133,
+ "code": "1F9D1 1F3FB",
+ "char": "🧑🏻",
+ "name": "⊛ adult: light skin tone"
+ },
+ {
+ "no": 134,
+ "code": "1F9D1 1F3FC",
+ "char": "🧑🏼",
+ "name": "⊛ adult: medium-light skin tone"
+ },
+ {
+ "no": 135,
+ "code": "1F9D1 1F3FD",
+ "char": "🧑🏽",
+ "name": "⊛ adult: medium skin tone"
+ },
+ {
+ "no": 136,
+ "code": "1F9D1 1F3FE",
+ "char": "🧑🏾",
+ "name": "⊛ adult: medium-dark skin tone"
+ },
+ {
+ "no": 137,
+ "code": "1F9D1 1F3FF",
+ "char": "🧑🏿",
+ "name": "⊛ adult: dark skin tone"
+ },
+ {
+ "no": 138,
+ "code": "1F468",
+ "char": "👨",
+ "name": "man"
+ },
+ {
+ "no": 139,
+ "code": "1F468 1F3FB",
+ "char": "👨🏻",
+ "name": "man: light skin tone"
+ },
+ {
+ "no": 140,
+ "code": "1F468 1F3FC",
+ "char": "👨🏼",
+ "name": "man: medium-light skin tone"
+ },
+ {
+ "no": 141,
+ "code": "1F468 1F3FD",
+ "char": "👨🏽",
+ "name": "man: medium skin tone"
+ },
+ {
+ "no": 142,
+ "code": "1F468 1F3FE",
+ "char": "👨🏾",
+ "name": "man: medium-dark skin tone"
+ },
+ {
+ "no": 143,
+ "code": "1F468 1F3FF",
+ "char": "👨🏿",
+ "name": "man: dark skin tone"
+ },
+ {
+ "no": 144,
+ "code": "1F469",
+ "char": "👩",
+ "name": "woman"
+ },
+ {
+ "no": 145,
+ "code": "1F469 1F3FB",
+ "char": "👩🏻",
+ "name": "woman: light skin tone"
+ },
+ {
+ "no": 146,
+ "code": "1F469 1F3FC",
+ "char": "👩🏼",
+ "name": "woman: medium-light skin tone"
+ },
+ {
+ "no": 147,
+ "code": "1F469 1F3FD",
+ "char": "👩🏽",
+ "name": "woman: medium skin tone"
+ },
+ {
+ "no": 148,
+ "code": "1F469 1F3FE",
+ "char": "👩🏾",
+ "name": "woman: medium-dark skin tone"
+ },
+ {
+ "no": 149,
+ "code": "1F469 1F3FF",
+ "char": "👩🏿",
+ "name": "woman: dark skin tone"
+ },
+ {
+ "no": 150,
+ "code": "1F9D3",
+ "char": "🧓",
+ "name": "⊛ older adult"
+ },
+ {
+ "no": 151,
+ "code": "1F9D3 1F3FB",
+ "char": "🧓🏻",
+ "name": "⊛ older adult: light skin tone"
+ },
+ {
+ "no": 152,
+ "code": "1F9D3 1F3FC",
+ "char": "🧓🏼",
+ "name": "⊛ older adult: medium-light skin tone"
+ },
+ {
+ "no": 153,
+ "code": "1F9D3 1F3FD",
+ "char": "🧓🏽",
+ "name": "⊛ older adult: medium skin tone"
+ },
+ {
+ "no": 154,
+ "code": "1F9D3 1F3FE",
+ "char": "🧓🏾",
+ "name": "⊛ older adult: medium-dark skin tone"
+ },
+ {
+ "no": 155,
+ "code": "1F9D3 1F3FF",
+ "char": "🧓🏿",
+ "name": "⊛ older adult: dark skin tone"
+ },
+ {
+ "no": 156,
+ "code": "1F474",
+ "char": "👴",
+ "name": "old man"
+ },
+ {
+ "no": 157,
+ "code": "1F474 1F3FB",
+ "char": "👴🏻",
+ "name": "old man: light skin tone"
+ },
+ {
+ "no": 158,
+ "code": "1F474 1F3FC",
+ "char": "👴🏼",
+ "name": "old man: medium-light skin tone"
+ },
+ {
+ "no": 159,
+ "code": "1F474 1F3FD",
+ "char": "👴🏽",
+ "name": "old man: medium skin tone"
+ },
+ {
+ "no": 160,
+ "code": "1F474 1F3FE",
+ "char": "👴🏾",
+ "name": "old man: medium-dark skin tone"
+ },
+ {
+ "no": 161,
+ "code": "1F474 1F3FF",
+ "char": "👴🏿",
+ "name": "old man: dark skin tone"
+ },
+ {
+ "no": 162,
+ "code": "1F475",
+ "char": "👵",
+ "name": "old woman"
+ },
+ {
+ "no": 163,
+ "code": "1F475 1F3FB",
+ "char": "👵🏻",
+ "name": "old woman: light skin tone"
+ },
+ {
+ "no": 164,
+ "code": "1F475 1F3FC",
+ "char": "👵🏼",
+ "name": "old woman: medium-light skin tone"
+ },
+ {
+ "no": 165,
+ "code": "1F475 1F3FD",
+ "char": "👵🏽",
+ "name": "old woman: medium skin tone"
+ },
+ {
+ "no": 166,
+ "code": "1F475 1F3FE",
+ "char": "👵🏾",
+ "name": "old woman: medium-dark skin tone"
+ },
+ {
+ "no": 167,
+ "code": "1F475 1F3FF",
+ "char": "👵🏿",
+ "name": "old woman: dark skin tone"
+ },
+ {
+ "no": 168,
+ "code": "1F468 200D 2695 FE0F",
+ "char": "👨‍⚕️",
+ "name": "man health worker"
+ },
+ {
+ "no": 169,
+ "code": "1F468 1F3FB 200D 2695 FE0F",
+ "char": "👨🏻‍⚕️",
+ "name": "man health worker: light skin tone"
+ },
+ {
+ "no": 170,
+ "code": "1F468 1F3FC 200D 2695 FE0F",
+ "char": "👨🏼‍⚕️",
+ "name": "man health worker: medium-light skin tone"
+ },
+ {
+ "no": 171,
+ "code": "1F468 1F3FD 200D 2695 FE0F",
+ "char": "👨🏽‍⚕️",
+ "name": "man health worker: medium skin tone"
+ },
+ {
+ "no": 172,
+ "code": "1F468 1F3FE 200D 2695 FE0F",
+ "char": "👨🏾‍⚕️",
+ "name": "man health worker: medium-dark skin tone"
+ },
+ {
+ "no": 173,
+ "code": "1F468 1F3FF 200D 2695 FE0F",
+ "char": "👨🏿‍⚕️",
+ "name": "man health worker: dark skin tone"
+ },
+ {
+ "no": 174,
+ "code": "1F469 200D 2695 FE0F",
+ "char": "👩‍⚕️",
+ "name": "woman health worker"
+ },
+ {
+ "no": 175,
+ "code": "1F469 1F3FB 200D 2695 FE0F",
+ "char": "👩🏻‍⚕️",
+ "name": "woman health worker: light skin tone"
+ },
+ {
+ "no": 176,
+ "code": "1F469 1F3FC 200D 2695 FE0F",
+ "char": "👩🏼‍⚕️",
+ "name": "woman health worker: medium-light skin tone"
+ },
+ {
+ "no": 177,
+ "code": "1F469 1F3FD 200D 2695 FE0F",
+ "char": "👩🏽‍⚕️",
+ "name": "woman health worker: medium skin tone"
+ },
+ {
+ "no": 178,
+ "code": "1F469 1F3FE 200D 2695 FE0F",
+ "char": "👩🏾‍⚕️",
+ "name": "woman health worker: medium-dark skin tone"
+ },
+ {
+ "no": 179,
+ "code": "1F469 1F3FF 200D 2695 FE0F",
+ "char": "👩🏿‍⚕️",
+ "name": "woman health worker: dark skin tone"
+ },
+ {
+ "no": 180,
+ "code": "1F468 200D 1F393",
+ "char": "👨‍🎓",
+ "name": "man student"
+ },
+ {
+ "no": 181,
+ "code": "1F468 1F3FB 200D 1F393",
+ "char": "👨🏻‍🎓",
+ "name": "man student: light skin tone"
+ },
+ {
+ "no": 182,
+ "code": "1F468 1F3FC 200D 1F393",
+ "char": "👨🏼‍🎓",
+ "name": "man student: medium-light skin tone"
+ },
+ {
+ "no": 183,
+ "code": "1F468 1F3FD 200D 1F393",
+ "char": "👨🏽‍🎓",
+ "name": "man student: medium skin tone"
+ },
+ {
+ "no": 184,
+ "code": "1F468 1F3FE 200D 1F393",
+ "char": "👨🏾‍🎓",
+ "name": "man student: medium-dark skin tone"
+ },
+ {
+ "no": 185,
+ "code": "1F468 1F3FF 200D 1F393",
+ "char": "👨🏿‍🎓",
+ "name": "man student: dark skin tone"
+ },
+ {
+ "no": 186,
+ "code": "1F469 200D 1F393",
+ "char": "👩‍🎓",
+ "name": "woman student"
+ },
+ {
+ "no": 187,
+ "code": "1F469 1F3FB 200D 1F393",
+ "char": "👩🏻‍🎓",
+ "name": "woman student: light skin tone"
+ },
+ {
+ "no": 188,
+ "code": "1F469 1F3FC 200D 1F393",
+ "char": "👩🏼‍🎓",
+ "name": "woman student: medium-light skin tone"
+ },
+ {
+ "no": 189,
+ "code": "1F469 1F3FD 200D 1F393",
+ "char": "👩🏽‍🎓",
+ "name": "woman student: medium skin tone"
+ },
+ {
+ "no": 190,
+ "code": "1F469 1F3FE 200D 1F393",
+ "char": "👩🏾‍🎓",
+ "name": "woman student: medium-dark skin tone"
+ },
+ {
+ "no": 191,
+ "code": "1F469 1F3FF 200D 1F393",
+ "char": "👩🏿‍🎓",
+ "name": "woman student: dark skin tone"
+ },
+ {
+ "no": 192,
+ "code": "1F468 200D 1F3EB",
+ "char": "👨‍🏫",
+ "name": "man teacher"
+ },
+ {
+ "no": 193,
+ "code": "1F468 1F3FB 200D 1F3EB",
+ "char": "👨🏻‍🏫",
+ "name": "man teacher: light skin tone"
+ },
+ {
+ "no": 194,
+ "code": "1F468 1F3FC 200D 1F3EB",
+ "char": "👨🏼‍🏫",
+ "name": "man teacher: medium-light skin tone"
+ },
+ {
+ "no": 195,
+ "code": "1F468 1F3FD 200D 1F3EB",
+ "char": "👨🏽‍🏫",
+ "name": "man teacher: medium skin tone"
+ },
+ {
+ "no": 196,
+ "code": "1F468 1F3FE 200D 1F3EB",
+ "char": "👨🏾‍🏫",
+ "name": "man teacher: medium-dark skin tone"
+ },
+ {
+ "no": 197,
+ "code": "1F468 1F3FF 200D 1F3EB",
+ "char": "👨🏿‍🏫",
+ "name": "man teacher: dark skin tone"
+ },
+ {
+ "no": 198,
+ "code": "1F469 200D 1F3EB",
+ "char": "👩‍🏫",
+ "name": "woman teacher"
+ },
+ {
+ "no": 199,
+ "code": "1F469 1F3FB 200D 1F3EB",
+ "char": "👩🏻‍🏫",
+ "name": "woman teacher: light skin tone"
+ },
+ {
+ "no": 200,
+ "code": "1F469 1F3FC 200D 1F3EB",
+ "char": "👩🏼‍🏫",
+ "name": "woman teacher: medium-light skin tone"
+ },
+ {
+ "no": 201,
+ "code": "1F469 1F3FD 200D 1F3EB",
+ "char": "👩🏽‍🏫",
+ "name": "woman teacher: medium skin tone"
+ },
+ {
+ "no": 202,
+ "code": "1F469 1F3FE 200D 1F3EB",
+ "char": "👩🏾‍🏫",
+ "name": "woman teacher: medium-dark skin tone"
+ },
+ {
+ "no": 203,
+ "code": "1F469 1F3FF 200D 1F3EB",
+ "char": "👩🏿‍🏫",
+ "name": "woman teacher: dark skin tone"
+ },
+ {
+ "no": 204,
+ "code": "1F468 200D 2696 FE0F",
+ "char": "👨‍⚖️",
+ "name": "man judge"
+ },
+ {
+ "no": 205,
+ "code": "1F468 1F3FB 200D 2696 FE0F",
+ "char": "👨🏻‍⚖️",
+ "name": "man judge: light skin tone"
+ },
+ {
+ "no": 206,
+ "code": "1F468 1F3FC 200D 2696 FE0F",
+ "char": "👨🏼‍⚖️",
+ "name": "man judge: medium-light skin tone"
+ },
+ {
+ "no": 207,
+ "code": "1F468 1F3FD 200D 2696 FE0F",
+ "char": "👨🏽‍⚖️",
+ "name": "man judge: medium skin tone"
+ },
+ {
+ "no": 208,
+ "code": "1F468 1F3FE 200D 2696 FE0F",
+ "char": "👨🏾‍⚖️",
+ "name": "man judge: medium-dark skin tone"
+ },
+ {
+ "no": 209,
+ "code": "1F468 1F3FF 200D 2696 FE0F",
+ "char": "👨🏿‍⚖️",
+ "name": "man judge: dark skin tone"
+ },
+ {
+ "no": 210,
+ "code": "1F469 200D 2696 FE0F",
+ "char": "👩‍⚖️",
+ "name": "woman judge"
+ },
+ {
+ "no": 211,
+ "code": "1F469 1F3FB 200D 2696 FE0F",
+ "char": "👩🏻‍⚖️",
+ "name": "woman judge: light skin tone"
+ },
+ {
+ "no": 212,
+ "code": "1F469 1F3FC 200D 2696 FE0F",
+ "char": "👩🏼‍⚖️",
+ "name": "woman judge: medium-light skin tone"
+ },
+ {
+ "no": 213,
+ "code": "1F469 1F3FD 200D 2696 FE0F",
+ "char": "👩🏽‍⚖️",
+ "name": "woman judge: medium skin tone"
+ },
+ {
+ "no": 214,
+ "code": "1F469 1F3FE 200D 2696 FE0F",
+ "char": "👩🏾‍⚖️",
+ "name": "woman judge: medium-dark skin tone"
+ },
+ {
+ "no": 215,
+ "code": "1F469 1F3FF 200D 2696 FE0F",
+ "char": "👩🏿‍⚖️",
+ "name": "woman judge: dark skin tone"
+ },
+ {
+ "no": 216,
+ "code": "1F468 200D 1F33E",
+ "char": "👨‍🌾",
+ "name": "man farmer"
+ },
+ {
+ "no": 217,
+ "code": "1F468 1F3FB 200D 1F33E",
+ "char": "👨🏻‍🌾",
+ "name": "man farmer: light skin tone"
+ },
+ {
+ "no": 218,
+ "code": "1F468 1F3FC 200D 1F33E",
+ "char": "👨🏼‍🌾",
+ "name": "man farmer: medium-light skin tone"
+ },
+ {
+ "no": 219,
+ "code": "1F468 1F3FD 200D 1F33E",
+ "char": "👨🏽‍🌾",
+ "name": "man farmer: medium skin tone"
+ },
+ {
+ "no": 220,
+ "code": "1F468 1F3FE 200D 1F33E",
+ "char": "👨🏾‍🌾",
+ "name": "man farmer: medium-dark skin tone"
+ },
+ {
+ "no": 221,
+ "code": "1F468 1F3FF 200D 1F33E",
+ "char": "👨🏿‍🌾",
+ "name": "man farmer: dark skin tone"
+ },
+ {
+ "no": 222,
+ "code": "1F469 200D 1F33E",
+ "char": "👩‍🌾",
+ "name": "woman farmer"
+ },
+ {
+ "no": 223,
+ "code": "1F469 1F3FB 200D 1F33E",
+ "char": "👩🏻‍🌾",
+ "name": "woman farmer: light skin tone"
+ },
+ {
+ "no": 224,
+ "code": "1F469 1F3FC 200D 1F33E",
+ "char": "👩🏼‍🌾",
+ "name": "woman farmer: medium-light skin tone"
+ },
+ {
+ "no": 225,
+ "code": "1F469 1F3FD 200D 1F33E",
+ "char": "👩🏽‍🌾",
+ "name": "woman farmer: medium skin tone"
+ },
+ {
+ "no": 226,
+ "code": "1F469 1F3FE 200D 1F33E",
+ "char": "👩🏾‍🌾",
+ "name": "woman farmer: medium-dark skin tone"
+ },
+ {
+ "no": 227,
+ "code": "1F469 1F3FF 200D 1F33E",
+ "char": "👩🏿‍🌾",
+ "name": "woman farmer: dark skin tone"
+ },
+ {
+ "no": 228,
+ "code": "1F468 200D 1F373",
+ "char": "👨‍🍳",
+ "name": "man cook"
+ },
+ {
+ "no": 229,
+ "code": "1F468 1F3FB 200D 1F373",
+ "char": "👨🏻‍🍳",
+ "name": "man cook: light skin tone"
+ },
+ {
+ "no": 230,
+ "code": "1F468 1F3FC 200D 1F373",
+ "char": "👨🏼‍🍳",
+ "name": "man cook: medium-light skin tone"
+ },
+ {
+ "no": 231,
+ "code": "1F468 1F3FD 200D 1F373",
+ "char": "👨🏽‍🍳",
+ "name": "man cook: medium skin tone"
+ },
+ {
+ "no": 232,
+ "code": "1F468 1F3FE 200D 1F373",
+ "char": "👨🏾‍🍳",
+ "name": "man cook: medium-dark skin tone"
+ },
+ {
+ "no": 233,
+ "code": "1F468 1F3FF 200D 1F373",
+ "char": "👨🏿‍🍳",
+ "name": "man cook: dark skin tone"
+ },
+ {
+ "no": 234,
+ "code": "1F469 200D 1F373",
+ "char": "👩‍🍳",
+ "name": "woman cook"
+ },
+ {
+ "no": 235,
+ "code": "1F469 1F3FB 200D 1F373",
+ "char": "👩🏻‍🍳",
+ "name": "woman cook: light skin tone"
+ },
+ {
+ "no": 236,
+ "code": "1F469 1F3FC 200D 1F373",
+ "char": "👩🏼‍🍳",
+ "name": "woman cook: medium-light skin tone"
+ },
+ {
+ "no": 237,
+ "code": "1F469 1F3FD 200D 1F373",
+ "char": "👩🏽‍🍳",
+ "name": "woman cook: medium skin tone"
+ },
+ {
+ "no": 238,
+ "code": "1F469 1F3FE 200D 1F373",
+ "char": "👩🏾‍🍳",
+ "name": "woman cook: medium-dark skin tone"
+ },
+ {
+ "no": 239,
+ "code": "1F469 1F3FF 200D 1F373",
+ "char": "👩🏿‍🍳",
+ "name": "woman cook: dark skin tone"
+ },
+ {
+ "no": 240,
+ "code": "1F468 200D 1F527",
+ "char": "👨‍🔧",
+ "name": "man mechanic"
+ },
+ {
+ "no": 241,
+ "code": "1F468 1F3FB 200D 1F527",
+ "char": "👨🏻‍🔧",
+ "name": "man mechanic: light skin tone"
+ },
+ {
+ "no": 242,
+ "code": "1F468 1F3FC 200D 1F527",
+ "char": "👨🏼‍🔧",
+ "name": "man mechanic: medium-light skin tone"
+ },
+ {
+ "no": 243,
+ "code": "1F468 1F3FD 200D 1F527",
+ "char": "👨🏽‍🔧",
+ "name": "man mechanic: medium skin tone"
+ },
+ {
+ "no": 244,
+ "code": "1F468 1F3FE 200D 1F527",
+ "char": "👨🏾‍🔧",
+ "name": "man mechanic: medium-dark skin tone"
+ },
+ {
+ "no": 245,
+ "code": "1F468 1F3FF 200D 1F527",
+ "char": "👨🏿‍🔧",
+ "name": "man mechanic: dark skin tone"
+ },
+ {
+ "no": 246,
+ "code": "1F469 200D 1F527",
+ "char": "👩‍🔧",
+ "name": "woman mechanic"
+ },
+ {
+ "no": 247,
+ "code": "1F469 1F3FB 200D 1F527",
+ "char": "👩🏻‍🔧",
+ "name": "woman mechanic: light skin tone"
+ },
+ {
+ "no": 248,
+ "code": "1F469 1F3FC 200D 1F527",
+ "char": "👩🏼‍🔧",
+ "name": "woman mechanic: medium-light skin tone"
+ },
+ {
+ "no": 249,
+ "code": "1F469 1F3FD 200D 1F527",
+ "char": "👩🏽‍🔧",
+ "name": "woman mechanic: medium skin tone"
+ },
+ {
+ "no": 250,
+ "code": "1F469 1F3FE 200D 1F527",
+ "char": "👩🏾‍🔧",
+ "name": "woman mechanic: medium-dark skin tone"
+ },
+ {
+ "no": 251,
+ "code": "1F469 1F3FF 200D 1F527",
+ "char": "👩🏿‍🔧",
+ "name": "woman mechanic: dark skin tone"
+ },
+ {
+ "no": 252,
+ "code": "1F468 200D 1F3ED",
+ "char": "👨‍🏭",
+ "name": "man factory worker"
+ },
+ {
+ "no": 253,
+ "code": "1F468 1F3FB 200D 1F3ED",
+ "char": "👨🏻‍🏭",
+ "name": "man factory worker: light skin tone"
+ },
+ {
+ "no": 254,
+ "code": "1F468 1F3FC 200D 1F3ED",
+ "char": "👨🏼‍🏭",
+ "name": "man factory worker: medium-light skin tone"
+ },
+ {
+ "no": 255,
+ "code": "1F468 1F3FD 200D 1F3ED",
+ "char": "👨🏽‍🏭",
+ "name": "man factory worker: medium skin tone"
+ },
+ {
+ "no": 256,
+ "code": "1F468 1F3FE 200D 1F3ED",
+ "char": "👨🏾‍🏭",
+ "name": "man factory worker: medium-dark skin tone"
+ },
+ {
+ "no": 257,
+ "code": "1F468 1F3FF 200D 1F3ED",
+ "char": "👨🏿‍🏭",
+ "name": "man factory worker: dark skin tone"
+ },
+ {
+ "no": 258,
+ "code": "1F469 200D 1F3ED",
+ "char": "👩‍🏭",
+ "name": "woman factory worker"
+ },
+ {
+ "no": 259,
+ "code": "1F469 1F3FB 200D 1F3ED",
+ "char": "👩🏻‍🏭",
+ "name": "woman factory worker: light skin tone"
+ },
+ {
+ "no": 260,
+ "code": "1F469 1F3FC 200D 1F3ED",
+ "char": "👩🏼‍🏭",
+ "name": "woman factory worker: medium-light skin tone"
+ },
+ {
+ "no": 261,
+ "code": "1F469 1F3FD 200D 1F3ED",
+ "char": "👩🏽‍🏭",
+ "name": "woman factory worker: medium skin tone"
+ },
+ {
+ "no": 262,
+ "code": "1F469 1F3FE 200D 1F3ED",
+ "char": "👩🏾‍🏭",
+ "name": "woman factory worker: medium-dark skin tone"
+ },
+ {
+ "no": 263,
+ "code": "1F469 1F3FF 200D 1F3ED",
+ "char": "👩🏿‍🏭",
+ "name": "woman factory worker: dark skin tone"
+ },
+ {
+ "no": 264,
+ "code": "1F468 200D 1F4BC",
+ "char": "👨‍💼",
+ "name": "man office worker"
+ },
+ {
+ "no": 265,
+ "code": "1F468 1F3FB 200D 1F4BC",
+ "char": "👨🏻‍💼",
+ "name": "man office worker: light skin tone"
+ },
+ {
+ "no": 266,
+ "code": "1F468 1F3FC 200D 1F4BC",
+ "char": "👨🏼‍💼",
+ "name": "man office worker: medium-light skin tone"
+ },
+ {
+ "no": 267,
+ "code": "1F468 1F3FD 200D 1F4BC",
+ "char": "👨🏽‍💼",
+ "name": "man office worker: medium skin tone"
+ },
+ {
+ "no": 268,
+ "code": "1F468 1F3FE 200D 1F4BC",
+ "char": "👨🏾‍💼",
+ "name": "man office worker: medium-dark skin tone"
+ },
+ {
+ "no": 269,
+ "code": "1F468 1F3FF 200D 1F4BC",
+ "char": "👨🏿‍💼",
+ "name": "man office worker: dark skin tone"
+ },
+ {
+ "no": 270,
+ "code": "1F469 200D 1F4BC",
+ "char": "👩‍💼",
+ "name": "woman office worker"
+ },
+ {
+ "no": 271,
+ "code": "1F469 1F3FB 200D 1F4BC",
+ "char": "👩🏻‍💼",
+ "name": "woman office worker: light skin tone"
+ },
+ {
+ "no": 272,
+ "code": "1F469 1F3FC 200D 1F4BC",
+ "char": "👩🏼‍💼",
+ "name": "woman office worker: medium-light skin tone"
+ },
+ {
+ "no": 273,
+ "code": "1F469 1F3FD 200D 1F4BC",
+ "char": "👩🏽‍💼",
+ "name": "woman office worker: medium skin tone"
+ },
+ {
+ "no": 274,
+ "code": "1F469 1F3FE 200D 1F4BC",
+ "char": "👩🏾‍💼",
+ "name": "woman office worker: medium-dark skin tone"
+ },
+ {
+ "no": 275,
+ "code": "1F469 1F3FF 200D 1F4BC",
+ "char": "👩🏿‍💼",
+ "name": "woman office worker: dark skin tone"
+ },
+ {
+ "no": 276,
+ "code": "1F468 200D 1F52C",
+ "char": "👨‍🔬",
+ "name": "man scientist"
+ },
+ {
+ "no": 277,
+ "code": "1F468 1F3FB 200D 1F52C",
+ "char": "👨🏻‍🔬",
+ "name": "man scientist: light skin tone"
+ },
+ {
+ "no": 278,
+ "code": "1F468 1F3FC 200D 1F52C",
+ "char": "👨🏼‍🔬",
+ "name": "man scientist: medium-light skin tone"
+ },
+ {
+ "no": 279,
+ "code": "1F468 1F3FD 200D 1F52C",
+ "char": "👨🏽‍🔬",
+ "name": "man scientist: medium skin tone"
+ },
+ {
+ "no": 280,
+ "code": "1F468 1F3FE 200D 1F52C",
+ "char": "👨🏾‍🔬",
+ "name": "man scientist: medium-dark skin tone"
+ },
+ {
+ "no": 281,
+ "code": "1F468 1F3FF 200D 1F52C",
+ "char": "👨🏿‍🔬",
+ "name": "man scientist: dark skin tone"
+ },
+ {
+ "no": 282,
+ "code": "1F469 200D 1F52C",
+ "char": "👩‍🔬",
+ "name": "woman scientist"
+ },
+ {
+ "no": 283,
+ "code": "1F469 1F3FB 200D 1F52C",
+ "char": "👩🏻‍🔬",
+ "name": "woman scientist: light skin tone"
+ },
+ {
+ "no": 284,
+ "code": "1F469 1F3FC 200D 1F52C",
+ "char": "👩🏼‍🔬",
+ "name": "woman scientist: medium-light skin tone"
+ },
+ {
+ "no": 285,
+ "code": "1F469 1F3FD 200D 1F52C",
+ "char": "👩🏽‍🔬",
+ "name": "woman scientist: medium skin tone"
+ },
+ {
+ "no": 286,
+ "code": "1F469 1F3FE 200D 1F52C",
+ "char": "👩🏾‍🔬",
+ "name": "woman scientist: medium-dark skin tone"
+ },
+ {
+ "no": 287,
+ "code": "1F469 1F3FF 200D 1F52C",
+ "char": "👩🏿‍🔬",
+ "name": "woman scientist: dark skin tone"
+ },
+ {
+ "no": 288,
+ "code": "1F468 200D 1F4BB",
+ "char": "👨‍💻",
+ "name": "man technologist"
+ },
+ {
+ "no": 289,
+ "code": "1F468 1F3FB 200D 1F4BB",
+ "char": "👨🏻‍💻",
+ "name": "man technologist: light skin tone"
+ },
+ {
+ "no": 290,
+ "code": "1F468 1F3FC 200D 1F4BB",
+ "char": "👨🏼‍💻",
+ "name": "man technologist: medium-light skin tone"
+ },
+ {
+ "no": 291,
+ "code": "1F468 1F3FD 200D 1F4BB",
+ "char": "👨🏽‍💻",
+ "name": "man technologist: medium skin tone"
+ },
+ {
+ "no": 292,
+ "code": "1F468 1F3FE 200D 1F4BB",
+ "char": "👨🏾‍💻",
+ "name": "man technologist: medium-dark skin tone"
+ },
+ {
+ "no": 293,
+ "code": "1F468 1F3FF 200D 1F4BB",
+ "char": "👨🏿‍💻",
+ "name": "man technologist: dark skin tone"
+ },
+ {
+ "no": 294,
+ "code": "1F469 200D 1F4BB",
+ "char": "👩‍💻",
+ "name": "woman technologist"
+ },
+ {
+ "no": 295,
+ "code": "1F469 1F3FB 200D 1F4BB",
+ "char": "👩🏻‍💻",
+ "name": "woman technologist: light skin tone"
+ },
+ {
+ "no": 296,
+ "code": "1F469 1F3FC 200D 1F4BB",
+ "char": "👩🏼‍💻",
+ "name": "woman technologist: medium-light skin tone"
+ },
+ {
+ "no": 297,
+ "code": "1F469 1F3FD 200D 1F4BB",
+ "char": "👩🏽‍💻",
+ "name": "woman technologist: medium skin tone"
+ },
+ {
+ "no": 298,
+ "code": "1F469 1F3FE 200D 1F4BB",
+ "char": "👩🏾‍💻",
+ "name": "woman technologist: medium-dark skin tone"
+ },
+ {
+ "no": 299,
+ "code": "1F469 1F3FF 200D 1F4BB",
+ "char": "👩🏿‍💻",
+ "name": "woman technologist: dark skin tone"
+ },
+ {
+ "no": 300,
+ "code": "1F468 200D 1F3A4",
+ "char": "👨‍🎤",
+ "name": "man singer"
+ },
+ {
+ "no": 301,
+ "code": "1F468 1F3FB 200D 1F3A4",
+ "char": "👨🏻‍🎤",
+ "name": "man singer: light skin tone"
+ },
+ {
+ "no": 302,
+ "code": "1F468 1F3FC 200D 1F3A4",
+ "char": "👨🏼‍🎤",
+ "name": "man singer: medium-light skin tone"
+ },
+ {
+ "no": 303,
+ "code": "1F468 1F3FD 200D 1F3A4",
+ "char": "👨🏽‍🎤",
+ "name": "man singer: medium skin tone"
+ },
+ {
+ "no": 304,
+ "code": "1F468 1F3FE 200D 1F3A4",
+ "char": "👨🏾‍🎤",
+ "name": "man singer: medium-dark skin tone"
+ },
+ {
+ "no": 305,
+ "code": "1F468 1F3FF 200D 1F3A4",
+ "char": "👨🏿‍🎤",
+ "name": "man singer: dark skin tone"
+ },
+ {
+ "no": 306,
+ "code": "1F469 200D 1F3A4",
+ "char": "👩‍🎤",
+ "name": "woman singer"
+ },
+ {
+ "no": 307,
+ "code": "1F469 1F3FB 200D 1F3A4",
+ "char": "👩🏻‍🎤",
+ "name": "woman singer: light skin tone"
+ },
+ {
+ "no": 308,
+ "code": "1F469 1F3FC 200D 1F3A4",
+ "char": "👩🏼‍🎤",
+ "name": "woman singer: medium-light skin tone"
+ },
+ {
+ "no": 309,
+ "code": "1F469 1F3FD 200D 1F3A4",
+ "char": "👩🏽‍🎤",
+ "name": "woman singer: medium skin tone"
+ },
+ {
+ "no": 310,
+ "code": "1F469 1F3FE 200D 1F3A4",
+ "char": "👩🏾‍🎤",
+ "name": "woman singer: medium-dark skin tone"
+ },
+ {
+ "no": 311,
+ "code": "1F469 1F3FF 200D 1F3A4",
+ "char": "👩🏿‍🎤",
+ "name": "woman singer: dark skin tone"
+ },
+ {
+ "no": 312,
+ "code": "1F468 200D 1F3A8",
+ "char": "👨‍🎨",
+ "name": "man artist"
+ },
+ {
+ "no": 313,
+ "code": "1F468 1F3FB 200D 1F3A8",
+ "char": "👨🏻‍🎨",
+ "name": "man artist: light skin tone"
+ },
+ {
+ "no": 314,
+ "code": "1F468 1F3FC 200D 1F3A8",
+ "char": "👨🏼‍🎨",
+ "name": "man artist: medium-light skin tone"
+ },
+ {
+ "no": 315,
+ "code": "1F468 1F3FD 200D 1F3A8",
+ "char": "👨🏽‍🎨",
+ "name": "man artist: medium skin tone"
+ },
+ {
+ "no": 316,
+ "code": "1F468 1F3FE 200D 1F3A8",
+ "char": "👨🏾‍🎨",
+ "name": "man artist: medium-dark skin tone"
+ },
+ {
+ "no": 317,
+ "code": "1F468 1F3FF 200D 1F3A8",
+ "char": "👨🏿‍🎨",
+ "name": "man artist: dark skin tone"
+ },
+ {
+ "no": 318,
+ "code": "1F469 200D 1F3A8",
+ "char": "👩‍🎨",
+ "name": "woman artist"
+ },
+ {
+ "no": 319,
+ "code": "1F469 1F3FB 200D 1F3A8",
+ "char": "👩🏻‍🎨",
+ "name": "woman artist: light skin tone"
+ },
+ {
+ "no": 320,
+ "code": "1F469 1F3FC 200D 1F3A8",
+ "char": "👩🏼‍🎨",
+ "name": "woman artist: medium-light skin tone"
+ },
+ {
+ "no": 321,
+ "code": "1F469 1F3FD 200D 1F3A8",
+ "char": "👩🏽‍🎨",
+ "name": "woman artist: medium skin tone"
+ },
+ {
+ "no": 322,
+ "code": "1F469 1F3FE 200D 1F3A8",
+ "char": "👩🏾‍🎨",
+ "name": "woman artist: medium-dark skin tone"
+ },
+ {
+ "no": 323,
+ "code": "1F469 1F3FF 200D 1F3A8",
+ "char": "👩🏿‍🎨",
+ "name": "woman artist: dark skin tone"
+ },
+ {
+ "no": 324,
+ "code": "1F468 200D 2708 FE0F",
+ "char": "👨‍✈️",
+ "name": "man pilot"
+ },
+ {
+ "no": 325,
+ "code": "1F468 1F3FB 200D 2708 FE0F",
+ "char": "👨🏻‍✈️",
+ "name": "man pilot: light skin tone"
+ },
+ {
+ "no": 326,
+ "code": "1F468 1F3FC 200D 2708 FE0F",
+ "char": "👨🏼‍✈️",
+ "name": "man pilot: medium-light skin tone"
+ },
+ {
+ "no": 327,
+ "code": "1F468 1F3FD 200D 2708 FE0F",
+ "char": "👨🏽‍✈️",
+ "name": "man pilot: medium skin tone"
+ },
+ {
+ "no": 328,
+ "code": "1F468 1F3FE 200D 2708 FE0F",
+ "char": "👨🏾‍✈️",
+ "name": "man pilot: medium-dark skin tone"
+ },
+ {
+ "no": 329,
+ "code": "1F468 1F3FF 200D 2708 FE0F",
+ "char": "👨🏿‍✈️",
+ "name": "man pilot: dark skin tone"
+ },
+ {
+ "no": 330,
+ "code": "1F469 200D 2708 FE0F",
+ "char": "👩‍✈️",
+ "name": "woman pilot"
+ },
+ {
+ "no": 331,
+ "code": "1F469 1F3FB 200D 2708 FE0F",
+ "char": "👩🏻‍✈️",
+ "name": "woman pilot: light skin tone"
+ },
+ {
+ "no": 332,
+ "code": "1F469 1F3FC 200D 2708 FE0F",
+ "char": "👩🏼‍✈️",
+ "name": "woman pilot: medium-light skin tone"
+ },
+ {
+ "no": 333,
+ "code": "1F469 1F3FD 200D 2708 FE0F",
+ "char": "👩🏽‍✈️",
+ "name": "woman pilot: medium skin tone"
+ },
+ {
+ "no": 334,
+ "code": "1F469 1F3FE 200D 2708 FE0F",
+ "char": "👩🏾‍✈️",
+ "name": "woman pilot: medium-dark skin tone"
+ },
+ {
+ "no": 335,
+ "code": "1F469 1F3FF 200D 2708 FE0F",
+ "char": "👩🏿‍✈️",
+ "name": "woman pilot: dark skin tone"
+ },
+ {
+ "no": 336,
+ "code": "1F468 200D 1F680",
+ "char": "👨‍🚀",
+ "name": "man astronaut"
+ },
+ {
+ "no": 337,
+ "code": "1F468 1F3FB 200D 1F680",
+ "char": "👨🏻‍🚀",
+ "name": "man astronaut: light skin tone"
+ },
+ {
+ "no": 338,
+ "code": "1F468 1F3FC 200D 1F680",
+ "char": "👨🏼‍🚀",
+ "name": "man astronaut: medium-light skin tone"
+ },
+ {
+ "no": 339,
+ "code": "1F468 1F3FD 200D 1F680",
+ "char": "👨🏽‍🚀",
+ "name": "man astronaut: medium skin tone"
+ },
+ {
+ "no": 340,
+ "code": "1F468 1F3FE 200D 1F680",
+ "char": "👨🏾‍🚀",
+ "name": "man astronaut: medium-dark skin tone"
+ },
+ {
+ "no": 341,
+ "code": "1F468 1F3FF 200D 1F680",
+ "char": "👨🏿‍🚀",
+ "name": "man astronaut: dark skin tone"
+ },
+ {
+ "no": 342,
+ "code": "1F469 200D 1F680",
+ "char": "👩‍🚀",
+ "name": "woman astronaut"
+ },
+ {
+ "no": 343,
+ "code": "1F469 1F3FB 200D 1F680",
+ "char": "👩🏻‍🚀",
+ "name": "woman astronaut: light skin tone"
+ },
+ {
+ "no": 344,
+ "code": "1F469 1F3FC 200D 1F680",
+ "char": "👩🏼‍🚀",
+ "name": "woman astronaut: medium-light skin tone"
+ },
+ {
+ "no": 345,
+ "code": "1F469 1F3FD 200D 1F680",
+ "char": "👩🏽‍🚀",
+ "name": "woman astronaut: medium skin tone"
+ },
+ {
+ "no": 346,
+ "code": "1F469 1F3FE 200D 1F680",
+ "char": "👩🏾‍🚀",
+ "name": "woman astronaut: medium-dark skin tone"
+ },
+ {
+ "no": 347,
+ "code": "1F469 1F3FF 200D 1F680",
+ "char": "👩🏿‍🚀",
+ "name": "woman astronaut: dark skin tone"
+ },
+ {
+ "no": 348,
+ "code": "1F468 200D 1F692",
+ "char": "👨‍🚒",
+ "name": "man firefighter"
+ },
+ {
+ "no": 349,
+ "code": "1F468 1F3FB 200D 1F692",
+ "char": "👨🏻‍🚒",
+ "name": "man firefighter: light skin tone"
+ },
+ {
+ "no": 350,
+ "code": "1F468 1F3FC 200D 1F692",
+ "char": "👨🏼‍🚒",
+ "name": "man firefighter: medium-light skin tone"
+ },
+ {
+ "no": 351,
+ "code": "1F468 1F3FD 200D 1F692",
+ "char": "👨🏽‍🚒",
+ "name": "man firefighter: medium skin tone"
+ },
+ {
+ "no": 352,
+ "code": "1F468 1F3FE 200D 1F692",
+ "char": "👨🏾‍🚒",
+ "name": "man firefighter: medium-dark skin tone"
+ },
+ {
+ "no": 353,
+ "code": "1F468 1F3FF 200D 1F692",
+ "char": "👨🏿‍🚒",
+ "name": "man firefighter: dark skin tone"
+ },
+ {
+ "no": 354,
+ "code": "1F469 200D 1F692",
+ "char": "👩‍🚒",
+ "name": "woman firefighter"
+ },
+ {
+ "no": 355,
+ "code": "1F469 1F3FB 200D 1F692",
+ "char": "👩🏻‍🚒",
+ "name": "woman firefighter: light skin tone"
+ },
+ {
+ "no": 356,
+ "code": "1F469 1F3FC 200D 1F692",
+ "char": "👩🏼‍🚒",
+ "name": "woman firefighter: medium-light skin tone"
+ },
+ {
+ "no": 357,
+ "code": "1F469 1F3FD 200D 1F692",
+ "char": "👩🏽‍🚒",
+ "name": "woman firefighter: medium skin tone"
+ },
+ {
+ "no": 358,
+ "code": "1F469 1F3FE 200D 1F692",
+ "char": "👩🏾‍🚒",
+ "name": "woman firefighter: medium-dark skin tone"
+ },
+ {
+ "no": 359,
+ "code": "1F469 1F3FF 200D 1F692",
+ "char": "👩🏿‍🚒",
+ "name": "woman firefighter: dark skin tone"
+ },
+ {
+ "no": 360,
+ "code": "1F46E",
+ "char": "👮",
+ "name": "police officer"
+ },
+ {
+ "no": 361,
+ "code": "1F46E 1F3FB",
+ "char": "👮🏻",
+ "name": "police officer: light skin tone"
+ },
+ {
+ "no": 362,
+ "code": "1F46E 1F3FC",
+ "char": "👮🏼",
+ "name": "police officer: medium-light skin tone"
+ },
+ {
+ "no": 363,
+ "code": "1F46E 1F3FD",
+ "char": "👮🏽",
+ "name": "police officer: medium skin tone"
+ },
+ {
+ "no": 364,
+ "code": "1F46E 1F3FE",
+ "char": "👮🏾",
+ "name": "police officer: medium-dark skin tone"
+ },
+ {
+ "no": 365,
+ "code": "1F46E 1F3FF",
+ "char": "👮🏿",
+ "name": "police officer: dark skin tone"
+ },
+ {
+ "no": 366,
+ "code": "1F46E 200D 2642 FE0F",
+ "char": "👮‍♂️",
+ "name": "man police officer"
+ },
+ {
+ "no": 367,
+ "code": "1F46E 1F3FB 200D 2642 FE0F",
+ "char": "👮🏻‍♂️",
+ "name": "man police officer: light skin tone"
+ },
+ {
+ "no": 368,
+ "code": "1F46E 1F3FC 200D 2642 FE0F",
+ "char": "👮🏼‍♂️",
+ "name": "man police officer: medium-light skin tone"
+ },
+ {
+ "no": 369,
+ "code": "1F46E 1F3FD 200D 2642 FE0F",
+ "char": "👮🏽‍♂️",
+ "name": "man police officer: medium skin tone"
+ },
+ {
+ "no": 370,
+ "code": "1F46E 1F3FE 200D 2642 FE0F",
+ "char": "👮🏾‍♂️",
+ "name": "man police officer: medium-dark skin tone"
+ },
+ {
+ "no": 371,
+ "code": "1F46E 1F3FF 200D 2642 FE0F",
+ "char": "👮🏿‍♂️",
+ "name": "man police officer: dark skin tone"
+ },
+ {
+ "no": 372,
+ "code": "1F46E 200D 2640 FE0F",
+ "char": "👮‍♀️",
+ "name": "woman police officer"
+ },
+ {
+ "no": 373,
+ "code": "1F46E 1F3FB 200D 2640 FE0F",
+ "char": "👮🏻‍♀️",
+ "name": "woman police officer: light skin tone"
+ },
+ {
+ "no": 374,
+ "code": "1F46E 1F3FC 200D 2640 FE0F",
+ "char": "👮🏼‍♀️",
+ "name": "woman police officer: medium-light skin tone"
+ },
+ {
+ "no": 375,
+ "code": "1F46E 1F3FD 200D 2640 FE0F",
+ "char": "👮🏽‍♀️",
+ "name": "woman police officer: medium skin tone"
+ },
+ {
+ "no": 376,
+ "code": "1F46E 1F3FE 200D 2640 FE0F",
+ "char": "👮🏾‍♀️",
+ "name": "woman police officer: medium-dark skin tone"
+ },
+ {
+ "no": 377,
+ "code": "1F46E 1F3FF 200D 2640 FE0F",
+ "char": "👮🏿‍♀️",
+ "name": "woman police officer: dark skin tone"
+ },
+ {
+ "no": 378,
+ "code": "1F575",
+ "char": "🕵",
+ "name": "detective"
+ },
+ {
+ "no": 379,
+ "code": "1F575 1F3FB",
+ "char": "🕵🏻",
+ "name": "detective: light skin tone"
+ },
+ {
+ "no": 380,
+ "code": "1F575 1F3FC",
+ "char": "🕵🏼",
+ "name": "detective: medium-light skin tone"
+ },
+ {
+ "no": 381,
+ "code": "1F575 1F3FD",
+ "char": "🕵🏽",
+ "name": "detective: medium skin tone"
+ },
+ {
+ "no": 382,
+ "code": "1F575 1F3FE",
+ "char": "🕵🏾",
+ "name": "detective: medium-dark skin tone"
+ },
+ {
+ "no": 383,
+ "code": "1F575 1F3FF",
+ "char": "🕵🏿",
+ "name": "detective: dark skin tone"
+ },
+ {
+ "no": 384,
+ "code": "1F575 FE0F 200D 2642 FE0F",
+ "char": "🕵️‍♂️",
+ "name": "man detective"
+ },
+ {
+ "no": 385,
+ "code": "1F575 1F3FB 200D 2642 FE0F",
+ "char": "🕵🏻‍♂️",
+ "name": "man detective: light skin tone"
+ },
+ {
+ "no": 386,
+ "code": "1F575 1F3FC 200D 2642 FE0F",
+ "char": "🕵🏼‍♂️",
+ "name": "man detective: medium-light skin tone"
+ },
+ {
+ "no": 387,
+ "code": "1F575 1F3FD 200D 2642 FE0F",
+ "char": "🕵🏽‍♂️",
+ "name": "man detective: medium skin tone"
+ },
+ {
+ "no": 388,
+ "code": "1F575 1F3FE 200D 2642 FE0F",
+ "char": "🕵🏾‍♂️",
+ "name": "man detective: medium-dark skin tone"
+ },
+ {
+ "no": 389,
+ "code": "1F575 1F3FF 200D 2642 FE0F",
+ "char": "🕵🏿‍♂️",
+ "name": "man detective: dark skin tone"
+ },
+ {
+ "no": 390,
+ "code": "1F575 FE0F 200D 2640 FE0F",
+ "char": "🕵️‍♀️",
+ "name": "woman detective"
+ },
+ {
+ "no": 391,
+ "code": "1F575 1F3FB 200D 2640 FE0F",
+ "char": "🕵🏻‍♀️",
+ "name": "woman detective: light skin tone"
+ },
+ {
+ "no": 392,
+ "code": "1F575 1F3FC 200D 2640 FE0F",
+ "char": "🕵🏼‍♀️",
+ "name": "woman detective: medium-light skin tone"
+ },
+ {
+ "no": 393,
+ "code": "1F575 1F3FD 200D 2640 FE0F",
+ "char": "🕵🏽‍♀️",
+ "name": "woman detective: medium skin tone"
+ },
+ {
+ "no": 394,
+ "code": "1F575 1F3FE 200D 2640 FE0F",
+ "char": "🕵🏾‍♀️",
+ "name": "woman detective: medium-dark skin tone"
+ },
+ {
+ "no": 395,
+ "code": "1F575 1F3FF 200D 2640 FE0F",
+ "char": "🕵🏿‍♀️",
+ "name": "woman detective: dark skin tone"
+ },
+ {
+ "no": 396,
+ "code": "1F482",
+ "char": "💂",
+ "name": "guard"
+ },
+ {
+ "no": 397,
+ "code": "1F482 1F3FB",
+ "char": "💂🏻",
+ "name": "guard: light skin tone"
+ },
+ {
+ "no": 398,
+ "code": "1F482 1F3FC",
+ "char": "💂🏼",
+ "name": "guard: medium-light skin tone"
+ },
+ {
+ "no": 399,
+ "code": "1F482 1F3FD",
+ "char": "💂🏽",
+ "name": "guard: medium skin tone"
+ },
+ {
+ "no": 400,
+ "code": "1F482 1F3FE",
+ "char": "💂🏾",
+ "name": "guard: medium-dark skin tone"
+ },
+ {
+ "no": 401,
+ "code": "1F482 1F3FF",
+ "char": "💂🏿",
+ "name": "guard: dark skin tone"
+ },
+ {
+ "no": 402,
+ "code": "1F482 200D 2642 FE0F",
+ "char": "💂‍♂️",
+ "name": "man guard"
+ },
+ {
+ "no": 403,
+ "code": "1F482 1F3FB 200D 2642 FE0F",
+ "char": "💂🏻‍♂️",
+ "name": "man guard: light skin tone"
+ },
+ {
+ "no": 404,
+ "code": "1F482 1F3FC 200D 2642 FE0F",
+ "char": "💂🏼‍♂️",
+ "name": "man guard: medium-light skin tone"
+ },
+ {
+ "no": 405,
+ "code": "1F482 1F3FD 200D 2642 FE0F",
+ "char": "💂🏽‍♂️",
+ "name": "man guard: medium skin tone"
+ },
+ {
+ "no": 406,
+ "code": "1F482 1F3FE 200D 2642 FE0F",
+ "char": "💂🏾‍♂️",
+ "name": "man guard: medium-dark skin tone"
+ },
+ {
+ "no": 407,
+ "code": "1F482 1F3FF 200D 2642 FE0F",
+ "char": "💂🏿‍♂️",
+ "name": "man guard: dark skin tone"
+ },
+ {
+ "no": 408,
+ "code": "1F482 200D 2640 FE0F",
+ "char": "💂‍♀️",
+ "name": "woman guard"
+ },
+ {
+ "no": 409,
+ "code": "1F482 1F3FB 200D 2640 FE0F",
+ "char": "💂🏻‍♀️",
+ "name": "woman guard: light skin tone"
+ },
+ {
+ "no": 410,
+ "code": "1F482 1F3FC 200D 2640 FE0F",
+ "char": "💂🏼‍♀️",
+ "name": "woman guard: medium-light skin tone"
+ },
+ {
+ "no": 411,
+ "code": "1F482 1F3FD 200D 2640 FE0F",
+ "char": "💂🏽‍♀️",
+ "name": "woman guard: medium skin tone"
+ },
+ {
+ "no": 412,
+ "code": "1F482 1F3FE 200D 2640 FE0F",
+ "char": "💂🏾‍♀️",
+ "name": "woman guard: medium-dark skin tone"
+ },
+ {
+ "no": 413,
+ "code": "1F482 1F3FF 200D 2640 FE0F",
+ "char": "💂🏿‍♀️",
+ "name": "woman guard: dark skin tone"
+ },
+ {
+ "no": 414,
+ "code": "1F477",
+ "char": "👷",
+ "name": "construction worker"
+ },
+ {
+ "no": 415,
+ "code": "1F477 1F3FB",
+ "char": "👷🏻",
+ "name": "construction worker: light skin tone"
+ },
+ {
+ "no": 416,
+ "code": "1F477 1F3FC",
+ "char": "👷🏼",
+ "name": "construction worker: medium-light skin tone"
+ },
+ {
+ "no": 417,
+ "code": "1F477 1F3FD",
+ "char": "👷🏽",
+ "name": "construction worker: medium skin tone"
+ },
+ {
+ "no": 418,
+ "code": "1F477 1F3FE",
+ "char": "👷🏾",
+ "name": "construction worker: medium-dark skin tone"
+ },
+ {
+ "no": 419,
+ "code": "1F477 1F3FF",
+ "char": "👷🏿",
+ "name": "construction worker: dark skin tone"
+ },
+ {
+ "no": 420,
+ "code": "1F477 200D 2642 FE0F",
+ "char": "👷‍♂️",
+ "name": "man construction worker"
+ },
+ {
+ "no": 421,
+ "code": "1F477 1F3FB 200D 2642 FE0F",
+ "char": "👷🏻‍♂️",
+ "name": "man construction worker: light skin tone"
+ },
+ {
+ "no": 422,
+ "code": "1F477 1F3FC 200D 2642 FE0F",
+ "char": "👷🏼‍♂️",
+ "name": "man construction worker: medium-light skin tone"
+ },
+ {
+ "no": 423,
+ "code": "1F477 1F3FD 200D 2642 FE0F",
+ "char": "👷🏽‍♂️",
+ "name": "man construction worker: medium skin tone"
+ },
+ {
+ "no": 424,
+ "code": "1F477 1F3FE 200D 2642 FE0F",
+ "char": "👷🏾‍♂️",
+ "name": "man construction worker: medium-dark skin tone"
+ },
+ {
+ "no": 425,
+ "code": "1F477 1F3FF 200D 2642 FE0F",
+ "char": "👷🏿‍♂️",
+ "name": "man construction worker: dark skin tone"
+ },
+ {
+ "no": 426,
+ "code": "1F477 200D 2640 FE0F",
+ "char": "👷‍♀️",
+ "name": "woman construction worker"
+ },
+ {
+ "no": 427,
+ "code": "1F477 1F3FB 200D 2640 FE0F",
+ "char": "👷🏻‍♀️",
+ "name": "woman construction worker: light skin tone"
+ },
+ {
+ "no": 428,
+ "code": "1F477 1F3FC 200D 2640 FE0F",
+ "char": "👷🏼‍♀️",
+ "name": "woman construction worker: medium-light skin tone"
+ },
+ {
+ "no": 429,
+ "code": "1F477 1F3FD 200D 2640 FE0F",
+ "char": "👷🏽‍♀️",
+ "name": "woman construction worker: medium skin tone"
+ },
+ {
+ "no": 430,
+ "code": "1F477 1F3FE 200D 2640 FE0F",
+ "char": "👷🏾‍♀️",
+ "name": "woman construction worker: medium-dark skin tone"
+ },
+ {
+ "no": 431,
+ "code": "1F477 1F3FF 200D 2640 FE0F",
+ "char": "👷🏿‍♀️",
+ "name": "woman construction worker: dark skin tone"
+ },
+ {
+ "no": 432,
+ "code": "1F934",
+ "char": "🤴",
+ "name": "prince"
+ },
+ {
+ "no": 433,
+ "code": "1F934 1F3FB",
+ "char": "🤴🏻",
+ "name": "prince: light skin tone"
+ },
+ {
+ "no": 434,
+ "code": "1F934 1F3FC",
+ "char": "🤴🏼",
+ "name": "prince: medium-light skin tone"
+ },
+ {
+ "no": 435,
+ "code": "1F934 1F3FD",
+ "char": "🤴🏽",
+ "name": "prince: medium skin tone"
+ },
+ {
+ "no": 436,
+ "code": "1F934 1F3FE",
+ "char": "🤴🏾",
+ "name": "prince: medium-dark skin tone"
+ },
+ {
+ "no": 437,
+ "code": "1F934 1F3FF",
+ "char": "🤴🏿",
+ "name": "prince: dark skin tone"
+ },
+ {
+ "no": 438,
+ "code": "1F478",
+ "char": "👸",
+ "name": "princess"
+ },
+ {
+ "no": 439,
+ "code": "1F478 1F3FB",
+ "char": "👸🏻",
+ "name": "princess: light skin tone"
+ },
+ {
+ "no": 440,
+ "code": "1F478 1F3FC",
+ "char": "👸🏼",
+ "name": "princess: medium-light skin tone"
+ },
+ {
+ "no": 441,
+ "code": "1F478 1F3FD",
+ "char": "👸🏽",
+ "name": "princess: medium skin tone"
+ },
+ {
+ "no": 442,
+ "code": "1F478 1F3FE",
+ "char": "👸🏾",
+ "name": "princess: medium-dark skin tone"
+ },
+ {
+ "no": 443,
+ "code": "1F478 1F3FF",
+ "char": "👸🏿",
+ "name": "princess: dark skin tone"
+ },
+ {
+ "no": 444,
+ "code": "1F473",
+ "char": "👳",
+ "name": "person wearing turban"
+ },
+ {
+ "no": 445,
+ "code": "1F473 1F3FB",
+ "char": "👳🏻",
+ "name": "person wearing turban: light skin tone"
+ },
+ {
+ "no": 446,
+ "code": "1F473 1F3FC",
+ "char": "👳🏼",
+ "name": "person wearing turban: medium-light skin tone"
+ },
+ {
+ "no": 447,
+ "code": "1F473 1F3FD",
+ "char": "👳🏽",
+ "name": "person wearing turban: medium skin tone"
+ },
+ {
+ "no": 448,
+ "code": "1F473 1F3FE",
+ "char": "👳🏾",
+ "name": "person wearing turban: medium-dark skin tone"
+ },
+ {
+ "no": 449,
+ "code": "1F473 1F3FF",
+ "char": "👳🏿",
+ "name": "person wearing turban: dark skin tone"
+ },
+ {
+ "no": 450,
+ "code": "1F473 200D 2642 FE0F",
+ "char": "👳‍♂️",
+ "name": "man wearing turban"
+ },
+ {
+ "no": 451,
+ "code": "1F473 1F3FB 200D 2642 FE0F",
+ "char": "👳🏻‍♂️",
+ "name": "man wearing turban: light skin tone"
+ },
+ {
+ "no": 452,
+ "code": "1F473 1F3FC 200D 2642 FE0F",
+ "char": "👳🏼‍♂️",
+ "name": "man wearing turban: medium-light skin tone"
+ },
+ {
+ "no": 453,
+ "code": "1F473 1F3FD 200D 2642 FE0F",
+ "char": "👳🏽‍♂️",
+ "name": "man wearing turban: medium skin tone"
+ },
+ {
+ "no": 454,
+ "code": "1F473 1F3FE 200D 2642 FE0F",
+ "char": "👳🏾‍♂️",
+ "name": "man wearing turban: medium-dark skin tone"
+ },
+ {
+ "no": 455,
+ "code": "1F473 1F3FF 200D 2642 FE0F",
+ "char": "👳🏿‍♂️",
+ "name": "man wearing turban: dark skin tone"
+ },
+ {
+ "no": 456,
+ "code": "1F473 200D 2640 FE0F",
+ "char": "👳‍♀️",
+ "name": "woman wearing turban"
+ },
+ {
+ "no": 457,
+ "code": "1F473 1F3FB 200D 2640 FE0F",
+ "char": "👳🏻‍♀️",
+ "name": "woman wearing turban: light skin tone"
+ },
+ {
+ "no": 458,
+ "code": "1F473 1F3FC 200D 2640 FE0F",
+ "char": "👳🏼‍♀️",
+ "name": "woman wearing turban: medium-light skin tone"
+ },
+ {
+ "no": 459,
+ "code": "1F473 1F3FD 200D 2640 FE0F",
+ "char": "👳🏽‍♀️",
+ "name": "woman wearing turban: medium skin tone"
+ },
+ {
+ "no": 460,
+ "code": "1F473 1F3FE 200D 2640 FE0F",
+ "char": "👳🏾‍♀️",
+ "name": "woman wearing turban: medium-dark skin tone"
+ },
+ {
+ "no": 461,
+ "code": "1F473 1F3FF 200D 2640 FE0F",
+ "char": "👳🏿‍♀️",
+ "name": "woman wearing turban: dark skin tone"
+ },
+ {
+ "no": 462,
+ "code": "1F472",
+ "char": "👲",
+ "name": "man with Chinese cap"
+ },
+ {
+ "no": 463,
+ "code": "1F472 1F3FB",
+ "char": "👲🏻",
+ "name": "man with Chinese cap: light skin tone"
+ },
+ {
+ "no": 464,
+ "code": "1F472 1F3FC",
+ "char": "👲🏼",
+ "name": "man with Chinese cap: medium-light skin tone"
+ },
+ {
+ "no": 465,
+ "code": "1F472 1F3FD",
+ "char": "👲🏽",
+ "name": "man with Chinese cap: medium skin tone"
+ },
+ {
+ "no": 466,
+ "code": "1F472 1F3FE",
+ "char": "👲🏾",
+ "name": "man with Chinese cap: medium-dark skin tone"
+ },
+ {
+ "no": 467,
+ "code": "1F472 1F3FF",
+ "char": "👲🏿",
+ "name": "man with Chinese cap: dark skin tone"
+ },
+ {
+ "no": 468,
+ "code": "1F9D5",
+ "char": "🧕",
+ "name": "⊛ woman with headscarf"
+ },
+ {
+ "no": 469,
+ "code": "1F9D5 1F3FB",
+ "char": "🧕🏻",
+ "name": "⊛ woman with headscarf: light skin tone"
+ },
+ {
+ "no": 470,
+ "code": "1F9D5 1F3FC",
+ "char": "🧕🏼",
+ "name": "⊛ woman with headscarf: medium-light skin tone"
+ },
+ {
+ "no": 471,
+ "code": "1F9D5 1F3FD",
+ "char": "🧕🏽",
+ "name": "⊛ woman with headscarf: medium skin tone"
+ },
+ {
+ "no": 472,
+ "code": "1F9D5 1F3FE",
+ "char": "🧕🏾",
+ "name": "⊛ woman with headscarf: medium-dark skin tone"
+ },
+ {
+ "no": 473,
+ "code": "1F9D5 1F3FF",
+ "char": "🧕🏿",
+ "name": "⊛ woman with headscarf: dark skin tone"
+ },
+ {
+ "no": 474,
+ "code": "1F9D4",
+ "char": "🧔",
+ "name": "⊛ bearded person"
+ },
+ {
+ "no": 475,
+ "code": "1F9D4 1F3FB",
+ "char": "🧔🏻",
+ "name": "⊛ bearded person: light skin tone"
+ },
+ {
+ "no": 476,
+ "code": "1F9D4 1F3FC",
+ "char": "🧔🏼",
+ "name": "⊛ bearded person: medium-light skin tone"
+ },
+ {
+ "no": 477,
+ "code": "1F9D4 1F3FD",
+ "char": "🧔🏽",
+ "name": "⊛ bearded person: medium skin tone"
+ },
+ {
+ "no": 478,
+ "code": "1F9D4 1F3FE",
+ "char": "🧔🏾",
+ "name": "⊛ bearded person: medium-dark skin tone"
+ },
+ {
+ "no": 479,
+ "code": "1F9D4 1F3FF",
+ "char": "🧔🏿",
+ "name": "⊛ bearded person: dark skin tone"
+ },
+ {
+ "no": 480,
+ "code": "1F471",
+ "char": "👱",
+ "name": "blond-haired person"
+ },
+ {
+ "no": 481,
+ "code": "1F471 1F3FB",
+ "char": "👱🏻",
+ "name": "blond-haired person: light skin tone"
+ },
+ {
+ "no": 482,
+ "code": "1F471 1F3FC",
+ "char": "👱🏼",
+ "name": "blond-haired person: medium-light skin tone"
+ },
+ {
+ "no": 483,
+ "code": "1F471 1F3FD",
+ "char": "👱🏽",
+ "name": "blond-haired person: medium skin tone"
+ },
+ {
+ "no": 484,
+ "code": "1F471 1F3FE",
+ "char": "👱🏾",
+ "name": "blond-haired person: medium-dark skin tone"
+ },
+ {
+ "no": 485,
+ "code": "1F471 1F3FF",
+ "char": "👱🏿",
+ "name": "blond-haired person: dark skin tone"
+ },
+ {
+ "no": 486,
+ "code": "1F471 200D 2642 FE0F",
+ "char": "👱‍♂️",
+ "name": "blond-haired man"
+ },
+ {
+ "no": 487,
+ "code": "1F471 1F3FB 200D 2642 FE0F",
+ "char": "👱🏻‍♂️",
+ "name": "blond-haired man: light skin tone"
+ },
+ {
+ "no": 488,
+ "code": "1F471 1F3FC 200D 2642 FE0F",
+ "char": "👱🏼‍♂️",
+ "name": "blond-haired man: medium-light skin tone"
+ },
+ {
+ "no": 489,
+ "code": "1F471 1F3FD 200D 2642 FE0F",
+ "char": "👱🏽‍♂️",
+ "name": "blond-haired man: medium skin tone"
+ },
+ {
+ "no": 490,
+ "code": "1F471 1F3FE 200D 2642 FE0F",
+ "char": "👱🏾‍♂️",
+ "name": "blond-haired man: medium-dark skin tone"
+ },
+ {
+ "no": 491,
+ "code": "1F471 1F3FF 200D 2642 FE0F",
+ "char": "👱🏿‍♂️",
+ "name": "blond-haired man: dark skin tone"
+ },
+ {
+ "no": 492,
+ "code": "1F471 200D 2640 FE0F",
+ "char": "👱‍♀️",
+ "name": "blond-haired woman"
+ },
+ {
+ "no": 493,
+ "code": "1F471 1F3FB 200D 2640 FE0F",
+ "char": "👱🏻‍♀️",
+ "name": "blond-haired woman: light skin tone"
+ },
+ {
+ "no": 494,
+ "code": "1F471 1F3FC 200D 2640 FE0F",
+ "char": "👱🏼‍♀️",
+ "name": "blond-haired woman: medium-light skin tone"
+ },
+ {
+ "no": 495,
+ "code": "1F471 1F3FD 200D 2640 FE0F",
+ "char": "👱🏽‍♀️",
+ "name": "blond-haired woman: medium skin tone"
+ },
+ {
+ "no": 496,
+ "code": "1F471 1F3FE 200D 2640 FE0F",
+ "char": "👱🏾‍♀️",
+ "name": "blond-haired woman: medium-dark skin tone"
+ },
+ {
+ "no": 497,
+ "code": "1F471 1F3FF 200D 2640 FE0F",
+ "char": "👱🏿‍♀️",
+ "name": "blond-haired woman: dark skin tone"
+ },
+ {
+ "no": 498,
+ "code": "1F935",
+ "char": "🤵",
+ "name": "man in tuxedo"
+ },
+ {
+ "no": 499,
+ "code": "1F935 1F3FB",
+ "char": "🤵🏻",
+ "name": "man in tuxedo: light skin tone"
+ },
+ {
+ "no": 500,
+ "code": "1F935 1F3FC",
+ "char": "🤵🏼",
+ "name": "man in tuxedo: medium-light skin tone"
+ },
+ {
+ "no": 501,
+ "code": "1F935 1F3FD",
+ "char": "🤵🏽",
+ "name": "man in tuxedo: medium skin tone"
+ },
+ {
+ "no": 502,
+ "code": "1F935 1F3FE",
+ "char": "🤵🏾",
+ "name": "man in tuxedo: medium-dark skin tone"
+ },
+ {
+ "no": 503,
+ "code": "1F935 1F3FF",
+ "char": "🤵🏿",
+ "name": "man in tuxedo: dark skin tone"
+ },
+ {
+ "no": 504,
+ "code": "1F470",
+ "char": "👰",
+ "name": "bride with veil"
+ },
+ {
+ "no": 505,
+ "code": "1F470 1F3FB",
+ "char": "👰🏻",
+ "name": "bride with veil: light skin tone"
+ },
+ {
+ "no": 506,
+ "code": "1F470 1F3FC",
+ "char": "👰🏼",
+ "name": "bride with veil: medium-light skin tone"
+ },
+ {
+ "no": 507,
+ "code": "1F470 1F3FD",
+ "char": "👰🏽",
+ "name": "bride with veil: medium skin tone"
+ },
+ {
+ "no": 508,
+ "code": "1F470 1F3FE",
+ "char": "👰🏾",
+ "name": "bride with veil: medium-dark skin tone"
+ },
+ {
+ "no": 509,
+ "code": "1F470 1F3FF",
+ "char": "👰🏿",
+ "name": "bride with veil: dark skin tone"
+ },
+ {
+ "no": 510,
+ "code": "1F930",
+ "char": "🤰",
+ "name": "pregnant woman"
+ },
+ {
+ "no": 511,
+ "code": "1F930 1F3FB",
+ "char": "🤰🏻",
+ "name": "pregnant woman: light skin tone"
+ },
+ {
+ "no": 512,
+ "code": "1F930 1F3FC",
+ "char": "🤰🏼",
+ "name": "pregnant woman: medium-light skin tone"
+ },
+ {
+ "no": 513,
+ "code": "1F930 1F3FD",
+ "char": "🤰🏽",
+ "name": "pregnant woman: medium skin tone"
+ },
+ {
+ "no": 514,
+ "code": "1F930 1F3FE",
+ "char": "🤰🏾",
+ "name": "pregnant woman: medium-dark skin tone"
+ },
+ {
+ "no": 515,
+ "code": "1F930 1F3FF",
+ "char": "🤰🏿",
+ "name": "pregnant woman: dark skin tone"
+ },
+ {
+ "no": 516,
+ "code": "1F931",
+ "char": "🤱",
+ "name": "⊛ breast-feeding"
+ },
+ {
+ "no": 517,
+ "code": "1F931 1F3FB",
+ "char": "🤱🏻",
+ "name": "⊛ breast-feeding: light skin tone"
+ },
+ {
+ "no": 518,
+ "code": "1F931 1F3FC",
+ "char": "🤱🏼",
+ "name": "⊛ breast-feeding: medium-light skin tone"
+ },
+ {
+ "no": 519,
+ "code": "1F931 1F3FD",
+ "char": "🤱🏽",
+ "name": "⊛ breast-feeding: medium skin tone"
+ },
+ {
+ "no": 520,
+ "code": "1F931 1F3FE",
+ "char": "🤱🏾",
+ "name": "⊛ breast-feeding: medium-dark skin tone"
+ },
+ {
+ "no": 521,
+ "code": "1F931 1F3FF",
+ "char": "🤱🏿",
+ "name": "⊛ breast-feeding: dark skin tone"
+ },
+ {
+ "no": 522,
+ "code": "1F47C",
+ "char": "👼",
+ "name": "baby angel"
+ },
+ {
+ "no": 523,
+ "code": "1F47C 1F3FB",
+ "char": "👼🏻",
+ "name": "baby angel: light skin tone"
+ },
+ {
+ "no": 524,
+ "code": "1F47C 1F3FC",
+ "char": "👼🏼",
+ "name": "baby angel: medium-light skin tone"
+ },
+ {
+ "no": 525,
+ "code": "1F47C 1F3FD",
+ "char": "👼🏽",
+ "name": "baby angel: medium skin tone"
+ },
+ {
+ "no": 526,
+ "code": "1F47C 1F3FE",
+ "char": "👼🏾",
+ "name": "baby angel: medium-dark skin tone"
+ },
+ {
+ "no": 527,
+ "code": "1F47C 1F3FF",
+ "char": "👼🏿",
+ "name": "baby angel: dark skin tone"
+ },
+ {
+ "no": 528,
+ "code": "1F385",
+ "char": "🎅",
+ "name": "Santa Claus"
+ },
+ {
+ "no": 529,
+ "code": "1F385 1F3FB",
+ "char": "🎅🏻",
+ "name": "Santa Claus: light skin tone"
+ },
+ {
+ "no": 530,
+ "code": "1F385 1F3FC",
+ "char": "🎅🏼",
+ "name": "Santa Claus: medium-light skin tone"
+ },
+ {
+ "no": 531,
+ "code": "1F385 1F3FD",
+ "char": "🎅🏽",
+ "name": "Santa Claus: medium skin tone"
+ },
+ {
+ "no": 532,
+ "code": "1F385 1F3FE",
+ "char": "🎅🏾",
+ "name": "Santa Claus: medium-dark skin tone"
+ },
+ {
+ "no": 533,
+ "code": "1F385 1F3FF",
+ "char": "🎅🏿",
+ "name": "Santa Claus: dark skin tone"
+ },
+ {
+ "no": 534,
+ "code": "1F936",
+ "char": "🤶",
+ "name": "Mrs. Claus"
+ },
+ {
+ "no": 535,
+ "code": "1F936 1F3FB",
+ "char": "🤶🏻",
+ "name": "Mrs. Claus: light skin tone"
+ },
+ {
+ "no": 536,
+ "code": "1F936 1F3FC",
+ "char": "🤶🏼",
+ "name": "Mrs. Claus: medium-light skin tone"
+ },
+ {
+ "no": 537,
+ "code": "1F936 1F3FD",
+ "char": "🤶🏽",
+ "name": "Mrs. Claus: medium skin tone"
+ },
+ {
+ "no": 538,
+ "code": "1F936 1F3FE",
+ "char": "🤶🏾",
+ "name": "Mrs. Claus: medium-dark skin tone"
+ },
+ {
+ "no": 539,
+ "code": "1F936 1F3FF",
+ "char": "🤶🏿",
+ "name": "Mrs. Claus: dark skin tone"
+ },
+ {
+ "no": 540,
+ "code": "1F9D9",
+ "char": "🧙",
+ "name": "⊛ mage"
+ },
+ {
+ "no": 541,
+ "code": "1F9D9 1F3FB",
+ "char": "🧙🏻",
+ "name": "⊛ mage: light skin tone"
+ },
+ {
+ "no": 542,
+ "code": "1F9D9 1F3FC",
+ "char": "🧙🏼",
+ "name": "⊛ mage: medium-light skin tone"
+ },
+ {
+ "no": 543,
+ "code": "1F9D9 1F3FD",
+ "char": "🧙🏽",
+ "name": "⊛ mage: medium skin tone"
+ },
+ {
+ "no": 544,
+ "code": "1F9D9 1F3FE",
+ "char": "🧙🏾",
+ "name": "⊛ mage: medium-dark skin tone"
+ },
+ {
+ "no": 545,
+ "code": "1F9D9 1F3FF",
+ "char": "🧙🏿",
+ "name": "⊛ mage: dark skin tone"
+ },
+ {
+ "no": 546,
+ "code": "1F9D9 200D 2640 FE0F",
+ "char": "🧙‍♀️",
+ "name": "⊛ woman mage"
+ },
+ {
+ "no": 547,
+ "code": "1F9D9 1F3FB 200D 2640 FE0F",
+ "char": "🧙🏻‍♀️",
+ "name": "⊛ woman mage: light skin tone"
+ },
+ {
+ "no": 548,
+ "code": "1F9D9 1F3FC 200D 2640 FE0F",
+ "char": "🧙🏼‍♀️",
+ "name": "⊛ woman mage: medium-light skin tone"
+ },
+ {
+ "no": 549,
+ "code": "1F9D9 1F3FD 200D 2640 FE0F",
+ "char": "🧙🏽‍♀️",
+ "name": "⊛ woman mage: medium skin tone"
+ },
+ {
+ "no": 550,
+ "code": "1F9D9 1F3FE 200D 2640 FE0F",
+ "char": "🧙🏾‍♀️",
+ "name": "⊛ woman mage: medium-dark skin tone"
+ },
+ {
+ "no": 551,
+ "code": "1F9D9 1F3FF 200D 2640 FE0F",
+ "char": "🧙🏿‍♀️",
+ "name": "⊛ woman mage: dark skin tone"
+ },
+ {
+ "no": 552,
+ "code": "1F9D9 200D 2642 FE0F",
+ "char": "🧙‍♂️",
+ "name": "⊛ man mage"
+ },
+ {
+ "no": 553,
+ "code": "1F9D9 1F3FB 200D 2642 FE0F",
+ "char": "🧙🏻‍♂️",
+ "name": "⊛ man mage: light skin tone"
+ },
+ {
+ "no": 554,
+ "code": "1F9D9 1F3FC 200D 2642 FE0F",
+ "char": "🧙🏼‍♂️",
+ "name": "⊛ man mage: medium-light skin tone"
+ },
+ {
+ "no": 555,
+ "code": "1F9D9 1F3FD 200D 2642 FE0F",
+ "char": "🧙🏽‍♂️",
+ "name": "⊛ man mage: medium skin tone"
+ },
+ {
+ "no": 556,
+ "code": "1F9D9 1F3FE 200D 2642 FE0F",
+ "char": "🧙🏾‍♂️",
+ "name": "⊛ man mage: medium-dark skin tone"
+ },
+ {
+ "no": 557,
+ "code": "1F9D9 1F3FF 200D 2642 FE0F",
+ "char": "🧙🏿‍♂️",
+ "name": "⊛ man mage: dark skin tone"
+ },
+ {
+ "no": 558,
+ "code": "1F9DA",
+ "char": "🧚",
+ "name": "⊛ fairy"
+ },
+ {
+ "no": 559,
+ "code": "1F9DA 1F3FB",
+ "char": "🧚🏻",
+ "name": "⊛ fairy: light skin tone"
+ },
+ {
+ "no": 560,
+ "code": "1F9DA 1F3FC",
+ "char": "🧚🏼",
+ "name": "⊛ fairy: medium-light skin tone"
+ },
+ {
+ "no": 561,
+ "code": "1F9DA 1F3FD",
+ "char": "🧚🏽",
+ "name": "⊛ fairy: medium skin tone"
+ },
+ {
+ "no": 562,
+ "code": "1F9DA 1F3FE",
+ "char": "🧚🏾",
+ "name": "⊛ fairy: medium-dark skin tone"
+ },
+ {
+ "no": 563,
+ "code": "1F9DA 1F3FF",
+ "char": "🧚🏿",
+ "name": "⊛ fairy: dark skin tone"
+ },
+ {
+ "no": 564,
+ "code": "1F9DA 200D 2640 FE0F",
+ "char": "🧚‍♀️",
+ "name": "⊛ woman fairy"
+ },
+ {
+ "no": 565,
+ "code": "1F9DA 1F3FB 200D 2640 FE0F",
+ "char": "🧚🏻‍♀️",
+ "name": "⊛ woman fairy: light skin tone"
+ },
+ {
+ "no": 566,
+ "code": "1F9DA 1F3FC 200D 2640 FE0F",
+ "char": "🧚🏼‍♀️",
+ "name": "⊛ woman fairy: medium-light skin tone"
+ },
+ {
+ "no": 567,
+ "code": "1F9DA 1F3FD 200D 2640 FE0F",
+ "char": "🧚🏽‍♀️",
+ "name": "⊛ woman fairy: medium skin tone"
+ },
+ {
+ "no": 568,
+ "code": "1F9DA 1F3FE 200D 2640 FE0F",
+ "char": "🧚🏾‍♀️",
+ "name": "⊛ woman fairy: medium-dark skin tone"
+ },
+ {
+ "no": 569,
+ "code": "1F9DA 1F3FF 200D 2640 FE0F",
+ "char": "🧚🏿‍♀️",
+ "name": "⊛ woman fairy: dark skin tone"
+ },
+ {
+ "no": 570,
+ "code": "1F9DA 200D 2642 FE0F",
+ "char": "🧚‍♂️",
+ "name": "⊛ man fairy"
+ },
+ {
+ "no": 571,
+ "code": "1F9DA 1F3FB 200D 2642 FE0F",
+ "char": "🧚🏻‍♂️",
+ "name": "⊛ man fairy: light skin tone"
+ },
+ {
+ "no": 572,
+ "code": "1F9DA 1F3FC 200D 2642 FE0F",
+ "char": "🧚🏼‍♂️",
+ "name": "⊛ man fairy: medium-light skin tone"
+ },
+ {
+ "no": 573,
+ "code": "1F9DA 1F3FD 200D 2642 FE0F",
+ "char": "🧚🏽‍♂️",
+ "name": "⊛ man fairy: medium skin tone"
+ },
+ {
+ "no": 574,
+ "code": "1F9DA 1F3FE 200D 2642 FE0F",
+ "char": "🧚🏾‍♂️",
+ "name": "⊛ man fairy: medium-dark skin tone"
+ },
+ {
+ "no": 575,
+ "code": "1F9DA 1F3FF 200D 2642 FE0F",
+ "char": "🧚🏿‍♂️",
+ "name": "⊛ man fairy: dark skin tone"
+ },
+ {
+ "no": 576,
+ "code": "1F9DB",
+ "char": "🧛",
+ "name": "⊛ vampire"
+ },
+ {
+ "no": 577,
+ "code": "1F9DB 1F3FB",
+ "char": "🧛🏻",
+ "name": "⊛ vampire: light skin tone"
+ },
+ {
+ "no": 578,
+ "code": "1F9DB 1F3FC",
+ "char": "🧛🏼",
+ "name": "⊛ vampire: medium-light skin tone"
+ },
+ {
+ "no": 579,
+ "code": "1F9DB 1F3FD",
+ "char": "🧛🏽",
+ "name": "⊛ vampire: medium skin tone"
+ },
+ {
+ "no": 580,
+ "code": "1F9DB 1F3FE",
+ "char": "🧛🏾",
+ "name": "⊛ vampire: medium-dark skin tone"
+ },
+ {
+ "no": 581,
+ "code": "1F9DB 1F3FF",
+ "char": "🧛🏿",
+ "name": "⊛ vampire: dark skin tone"
+ },
+ {
+ "no": 582,
+ "code": "1F9DB 200D 2640 FE0F",
+ "char": "🧛‍♀️",
+ "name": "⊛ woman vampire"
+ },
+ {
+ "no": 583,
+ "code": "1F9DB 1F3FB 200D 2640 FE0F",
+ "char": "🧛🏻‍♀️",
+ "name": "⊛ woman vampire: light skin tone"
+ },
+ {
+ "no": 584,
+ "code": "1F9DB 1F3FC 200D 2640 FE0F",
+ "char": "🧛🏼‍♀️",
+ "name": "⊛ woman vampire: medium-light skin tone"
+ },
+ {
+ "no": 585,
+ "code": "1F9DB 1F3FD 200D 2640 FE0F",
+ "char": "🧛🏽‍♀️",
+ "name": "⊛ woman vampire: medium skin tone"
+ },
+ {
+ "no": 586,
+ "code": "1F9DB 1F3FE 200D 2640 FE0F",
+ "char": "🧛🏾‍♀️",
+ "name": "⊛ woman vampire: medium-dark skin tone"
+ },
+ {
+ "no": 587,
+ "code": "1F9DB 1F3FF 200D 2640 FE0F",
+ "char": "🧛🏿‍♀️",
+ "name": "⊛ woman vampire: dark skin tone"
+ },
+ {
+ "no": 588,
+ "code": "1F9DB 200D 2642 FE0F",
+ "char": "🧛‍♂️",
+ "name": "⊛ man vampire"
+ },
+ {
+ "no": 589,
+ "code": "1F9DB 1F3FB 200D 2642 FE0F",
+ "char": "🧛🏻‍♂️",
+ "name": "⊛ man vampire: light skin tone"
+ },
+ {
+ "no": 590,
+ "code": "1F9DB 1F3FC 200D 2642 FE0F",
+ "char": "🧛🏼‍♂️",
+ "name": "⊛ man vampire: medium-light skin tone"
+ },
+ {
+ "no": 591,
+ "code": "1F9DB 1F3FD 200D 2642 FE0F",
+ "char": "🧛🏽‍♂️",
+ "name": "⊛ man vampire: medium skin tone"
+ },
+ {
+ "no": 592,
+ "code": "1F9DB 1F3FE 200D 2642 FE0F",
+ "char": "🧛🏾‍♂️",
+ "name": "⊛ man vampire: medium-dark skin tone"
+ },
+ {
+ "no": 593,
+ "code": "1F9DB 1F3FF 200D 2642 FE0F",
+ "char": "🧛🏿‍♂️",
+ "name": "⊛ man vampire: dark skin tone"
+ },
+ {
+ "no": 594,
+ "code": "1F9DC",
+ "char": "🧜",
+ "name": "⊛ merperson"
+ },
+ {
+ "no": 595,
+ "code": "1F9DC 1F3FB",
+ "char": "🧜🏻",
+ "name": "⊛ merperson: light skin tone"
+ },
+ {
+ "no": 596,
+ "code": "1F9DC 1F3FC",
+ "char": "🧜🏼",
+ "name": "⊛ merperson: medium-light skin tone"
+ },
+ {
+ "no": 597,
+ "code": "1F9DC 1F3FD",
+ "char": "🧜🏽",
+ "name": "⊛ merperson: medium skin tone"
+ },
+ {
+ "no": 598,
+ "code": "1F9DC 1F3FE",
+ "char": "🧜🏾",
+ "name": "⊛ merperson: medium-dark skin tone"
+ },
+ {
+ "no": 599,
+ "code": "1F9DC 1F3FF",
+ "char": "🧜🏿",
+ "name": "⊛ merperson: dark skin tone"
+ },
+ {
+ "no": 600,
+ "code": "1F9DC 200D 2640 FE0F",
+ "char": "🧜‍♀️",
+ "name": "⊛ mermaid"
+ },
+ {
+ "no": 601,
+ "code": "1F9DC 1F3FB 200D 2640 FE0F",
+ "char": "🧜🏻‍♀️",
+ "name": "⊛ mermaid: light skin tone"
+ },
+ {
+ "no": 602,
+ "code": "1F9DC 1F3FC 200D 2640 FE0F",
+ "char": "🧜🏼‍♀️",
+ "name": "⊛ mermaid: medium-light skin tone"
+ },
+ {
+ "no": 603,
+ "code": "1F9DC 1F3FD 200D 2640 FE0F",
+ "char": "🧜🏽‍♀️",
+ "name": "⊛ mermaid: medium skin tone"
+ },
+ {
+ "no": 604,
+ "code": "1F9DC 1F3FE 200D 2640 FE0F",
+ "char": "🧜🏾‍♀️",
+ "name": "⊛ mermaid: medium-dark skin tone"
+ },
+ {
+ "no": 605,
+ "code": "1F9DC 1F3FF 200D 2640 FE0F",
+ "char": "🧜🏿‍♀️",
+ "name": "⊛ mermaid: dark skin tone"
+ },
+ {
+ "no": 606,
+ "code": "1F9DC 200D 2642 FE0F",
+ "char": "🧜‍♂️",
+ "name": "⊛ merman"
+ },
+ {
+ "no": 607,
+ "code": "1F9DC 1F3FB 200D 2642 FE0F",
+ "char": "🧜🏻‍♂️",
+ "name": "⊛ merman: light skin tone"
+ },
+ {
+ "no": 608,
+ "code": "1F9DC 1F3FC 200D 2642 FE0F",
+ "char": "🧜🏼‍♂️",
+ "name": "⊛ merman: medium-light skin tone"
+ },
+ {
+ "no": 609,
+ "code": "1F9DC 1F3FD 200D 2642 FE0F",
+ "char": "🧜🏽‍♂️",
+ "name": "⊛ merman: medium skin tone"
+ },
+ {
+ "no": 610,
+ "code": "1F9DC 1F3FE 200D 2642 FE0F",
+ "char": "🧜🏾‍♂️",
+ "name": "⊛ merman: medium-dark skin tone"
+ },
+ {
+ "no": 611,
+ "code": "1F9DC 1F3FF 200D 2642 FE0F",
+ "char": "🧜🏿‍♂️",
+ "name": "⊛ merman: dark skin tone"
+ },
+ {
+ "no": 612,
+ "code": "1F9DD",
+ "char": "🧝",
+ "name": "⊛ elf"
+ },
+ {
+ "no": 613,
+ "code": "1F9DD 1F3FB",
+ "char": "🧝🏻",
+ "name": "⊛ elf: light skin tone"
+ },
+ {
+ "no": 614,
+ "code": "1F9DD 1F3FC",
+ "char": "🧝🏼",
+ "name": "⊛ elf: medium-light skin tone"
+ },
+ {
+ "no": 615,
+ "code": "1F9DD 1F3FD",
+ "char": "🧝🏽",
+ "name": "⊛ elf: medium skin tone"
+ },
+ {
+ "no": 616,
+ "code": "1F9DD 1F3FE",
+ "char": "🧝🏾",
+ "name": "⊛ elf: medium-dark skin tone"
+ },
+ {
+ "no": 617,
+ "code": "1F9DD 1F3FF",
+ "char": "🧝🏿",
+ "name": "⊛ elf: dark skin tone"
+ },
+ {
+ "no": 618,
+ "code": "1F9DD 200D 2640 FE0F",
+ "char": "🧝‍♀️",
+ "name": "⊛ woman elf"
+ },
+ {
+ "no": 619,
+ "code": "1F9DD 1F3FB 200D 2640 FE0F",
+ "char": "🧝🏻‍♀️",
+ "name": "⊛ woman elf: light skin tone"
+ },
+ {
+ "no": 620,
+ "code": "1F9DD 1F3FC 200D 2640 FE0F",
+ "char": "🧝🏼‍♀️",
+ "name": "⊛ woman elf: medium-light skin tone"
+ },
+ {
+ "no": 621,
+ "code": "1F9DD 1F3FD 200D 2640 FE0F",
+ "char": "🧝🏽‍♀️",
+ "name": "⊛ woman elf: medium skin tone"
+ },
+ {
+ "no": 622,
+ "code": "1F9DD 1F3FE 200D 2640 FE0F",
+ "char": "🧝🏾‍♀️",
+ "name": "⊛ woman elf: medium-dark skin tone"
+ },
+ {
+ "no": 623,
+ "code": "1F9DD 1F3FF 200D 2640 FE0F",
+ "char": "🧝🏿‍♀️",
+ "name": "⊛ woman elf: dark skin tone"
+ },
+ {
+ "no": 624,
+ "code": "1F9DD 200D 2642 FE0F",
+ "char": "🧝‍♂️",
+ "name": "⊛ man elf"
+ },
+ {
+ "no": 625,
+ "code": "1F9DD 1F3FB 200D 2642 FE0F",
+ "char": "🧝🏻‍♂️",
+ "name": "⊛ man elf: light skin tone"
+ },
+ {
+ "no": 626,
+ "code": "1F9DD 1F3FC 200D 2642 FE0F",
+ "char": "🧝🏼‍♂️",
+ "name": "⊛ man elf: medium-light skin tone"
+ },
+ {
+ "no": 627,
+ "code": "1F9DD 1F3FD 200D 2642 FE0F",
+ "char": "🧝🏽‍♂️",
+ "name": "⊛ man elf: medium skin tone"
+ },
+ {
+ "no": 628,
+ "code": "1F9DD 1F3FE 200D 2642 FE0F",
+ "char": "🧝🏾‍♂️",
+ "name": "⊛ man elf: medium-dark skin tone"
+ },
+ {
+ "no": 629,
+ "code": "1F9DD 1F3FF 200D 2642 FE0F",
+ "char": "🧝🏿‍♂️",
+ "name": "⊛ man elf: dark skin tone"
+ },
+ {
+ "no": 630,
+ "code": "1F9DE",
+ "char": "🧞",
+ "name": "⊛ genie"
+ },
+ {
+ "no": 631,
+ "code": "1F9DE 200D 2640 FE0F",
+ "char": "🧞‍♀️",
+ "name": "⊛ woman genie"
+ },
+ {
+ "no": 632,
+ "code": "1F9DE 200D 2642 FE0F",
+ "char": "🧞‍♂️",
+ "name": "⊛ man genie"
+ },
+ {
+ "no": 633,
+ "code": "1F9DF",
+ "char": "🧟",
+ "name": "⊛ zombie"
+ },
+ {
+ "no": 634,
+ "code": "1F9DF 200D 2640 FE0F",
+ "char": "🧟‍♀️",
+ "name": "⊛ woman zombie"
+ },
+ {
+ "no": 635,
+ "code": "1F9DF 200D 2642 FE0F",
+ "char": "🧟‍♂️",
+ "name": "⊛ man zombie"
+ },
+ {
+ "no": 636,
+ "code": "1F64D",
+ "char": "🙍",
+ "name": "person frowning"
+ },
+ {
+ "no": 637,
+ "code": "1F64D 1F3FB",
+ "char": "🙍🏻",
+ "name": "person frowning: light skin tone"
+ },
+ {
+ "no": 638,
+ "code": "1F64D 1F3FC",
+ "char": "🙍🏼",
+ "name": "person frowning: medium-light skin tone"
+ },
+ {
+ "no": 639,
+ "code": "1F64D 1F3FD",
+ "char": "🙍🏽",
+ "name": "person frowning: medium skin tone"
+ },
+ {
+ "no": 640,
+ "code": "1F64D 1F3FE",
+ "char": "🙍🏾",
+ "name": "person frowning: medium-dark skin tone"
+ },
+ {
+ "no": 641,
+ "code": "1F64D 1F3FF",
+ "char": "🙍🏿",
+ "name": "person frowning: dark skin tone"
+ },
+ {
+ "no": 642,
+ "code": "1F64D 200D 2642 FE0F",
+ "char": "🙍‍♂️",
+ "name": "man frowning"
+ },
+ {
+ "no": 643,
+ "code": "1F64D 1F3FB 200D 2642 FE0F",
+ "char": "🙍🏻‍♂️",
+ "name": "man frowning: light skin tone"
+ },
+ {
+ "no": 644,
+ "code": "1F64D 1F3FC 200D 2642 FE0F",
+ "char": "🙍🏼‍♂️",
+ "name": "man frowning: medium-light skin tone"
+ },
+ {
+ "no": 645,
+ "code": "1F64D 1F3FD 200D 2642 FE0F",
+ "char": "🙍🏽‍♂️",
+ "name": "man frowning: medium skin tone"
+ },
+ {
+ "no": 646,
+ "code": "1F64D 1F3FE 200D 2642 FE0F",
+ "char": "🙍🏾‍♂️",
+ "name": "man frowning: medium-dark skin tone"
+ },
+ {
+ "no": 647,
+ "code": "1F64D 1F3FF 200D 2642 FE0F",
+ "char": "🙍🏿‍♂️",
+ "name": "man frowning: dark skin tone"
+ },
+ {
+ "no": 648,
+ "code": "1F64D 200D 2640 FE0F",
+ "char": "🙍‍♀️",
+ "name": "woman frowning"
+ },
+ {
+ "no": 649,
+ "code": "1F64D 1F3FB 200D 2640 FE0F",
+ "char": "🙍🏻‍♀️",
+ "name": "woman frowning: light skin tone"
+ },
+ {
+ "no": 650,
+ "code": "1F64D 1F3FC 200D 2640 FE0F",
+ "char": "🙍🏼‍♀️",
+ "name": "woman frowning: medium-light skin tone"
+ },
+ {
+ "no": 651,
+ "code": "1F64D 1F3FD 200D 2640 FE0F",
+ "char": "🙍🏽‍♀️",
+ "name": "woman frowning: medium skin tone"
+ },
+ {
+ "no": 652,
+ "code": "1F64D 1F3FE 200D 2640 FE0F",
+ "char": "🙍🏾‍♀️",
+ "name": "woman frowning: medium-dark skin tone"
+ },
+ {
+ "no": 653,
+ "code": "1F64D 1F3FF 200D 2640 FE0F",
+ "char": "🙍🏿‍♀️",
+ "name": "woman frowning: dark skin tone"
+ },
+ {
+ "no": 654,
+ "code": "1F64E",
+ "char": "🙎",
+ "name": "person pouting"
+ },
+ {
+ "no": 655,
+ "code": "1F64E 1F3FB",
+ "char": "🙎🏻",
+ "name": "person pouting: light skin tone"
+ },
+ {
+ "no": 656,
+ "code": "1F64E 1F3FC",
+ "char": "🙎🏼",
+ "name": "person pouting: medium-light skin tone"
+ },
+ {
+ "no": 657,
+ "code": "1F64E 1F3FD",
+ "char": "🙎🏽",
+ "name": "person pouting: medium skin tone"
+ },
+ {
+ "no": 658,
+ "code": "1F64E 1F3FE",
+ "char": "🙎🏾",
+ "name": "person pouting: medium-dark skin tone"
+ },
+ {
+ "no": 659,
+ "code": "1F64E 1F3FF",
+ "char": "🙎🏿",
+ "name": "person pouting: dark skin tone"
+ },
+ {
+ "no": 660,
+ "code": "1F64E 200D 2642 FE0F",
+ "char": "🙎‍♂️",
+ "name": "man pouting"
+ },
+ {
+ "no": 661,
+ "code": "1F64E 1F3FB 200D 2642 FE0F",
+ "char": "🙎🏻‍♂️",
+ "name": "man pouting: light skin tone"
+ },
+ {
+ "no": 662,
+ "code": "1F64E 1F3FC 200D 2642 FE0F",
+ "char": "🙎🏼‍♂️",
+ "name": "man pouting: medium-light skin tone"
+ },
+ {
+ "no": 663,
+ "code": "1F64E 1F3FD 200D 2642 FE0F",
+ "char": "🙎🏽‍♂️",
+ "name": "man pouting: medium skin tone"
+ },
+ {
+ "no": 664,
+ "code": "1F64E 1F3FE 200D 2642 FE0F",
+ "char": "🙎🏾‍♂️",
+ "name": "man pouting: medium-dark skin tone"
+ },
+ {
+ "no": 665,
+ "code": "1F64E 1F3FF 200D 2642 FE0F",
+ "char": "🙎🏿‍♂️",
+ "name": "man pouting: dark skin tone"
+ },
+ {
+ "no": 666,
+ "code": "1F64E 200D 2640 FE0F",
+ "char": "🙎‍♀️",
+ "name": "woman pouting"
+ },
+ {
+ "no": 667,
+ "code": "1F64E 1F3FB 200D 2640 FE0F",
+ "char": "🙎🏻‍♀️",
+ "name": "woman pouting: light skin tone"
+ },
+ {
+ "no": 668,
+ "code": "1F64E 1F3FC 200D 2640 FE0F",
+ "char": "🙎🏼‍♀️",
+ "name": "woman pouting: medium-light skin tone"
+ },
+ {
+ "no": 669,
+ "code": "1F64E 1F3FD 200D 2640 FE0F",
+ "char": "🙎🏽‍♀️",
+ "name": "woman pouting: medium skin tone"
+ },
+ {
+ "no": 670,
+ "code": "1F64E 1F3FE 200D 2640 FE0F",
+ "char": "🙎🏾‍♀️",
+ "name": "woman pouting: medium-dark skin tone"
+ },
+ {
+ "no": 671,
+ "code": "1F64E 1F3FF 200D 2640 FE0F",
+ "char": "🙎🏿‍♀️",
+ "name": "woman pouting: dark skin tone"
+ },
+ {
+ "no": 672,
+ "code": "1F645",
+ "char": "🙅",
+ "name": "person gesturing NO"
+ },
+ {
+ "no": 673,
+ "code": "1F645 1F3FB",
+ "char": "🙅🏻",
+ "name": "person gesturing NO: light skin tone"
+ },
+ {
+ "no": 674,
+ "code": "1F645 1F3FC",
+ "char": "🙅🏼",
+ "name": "person gesturing NO: medium-light skin tone"
+ },
+ {
+ "no": 675,
+ "code": "1F645 1F3FD",
+ "char": "🙅🏽",
+ "name": "person gesturing NO: medium skin tone"
+ },
+ {
+ "no": 676,
+ "code": "1F645 1F3FE",
+ "char": "🙅🏾",
+ "name": "person gesturing NO: medium-dark skin tone"
+ },
+ {
+ "no": 677,
+ "code": "1F645 1F3FF",
+ "char": "🙅🏿",
+ "name": "person gesturing NO: dark skin tone"
+ },
+ {
+ "no": 678,
+ "code": "1F645 200D 2642 FE0F",
+ "char": "🙅‍♂️",
+ "name": "man gesturing NO"
+ },
+ {
+ "no": 679,
+ "code": "1F645 1F3FB 200D 2642 FE0F",
+ "char": "🙅🏻‍♂️",
+ "name": "man gesturing NO: light skin tone"
+ },
+ {
+ "no": 680,
+ "code": "1F645 1F3FC 200D 2642 FE0F",
+ "char": "🙅🏼‍♂️",
+ "name": "man gesturing NO: medium-light skin tone"
+ },
+ {
+ "no": 681,
+ "code": "1F645 1F3FD 200D 2642 FE0F",
+ "char": "🙅🏽‍♂️",
+ "name": "man gesturing NO: medium skin tone"
+ },
+ {
+ "no": 682,
+ "code": "1F645 1F3FE 200D 2642 FE0F",
+ "char": "🙅🏾‍♂️",
+ "name": "man gesturing NO: medium-dark skin tone"
+ },
+ {
+ "no": 683,
+ "code": "1F645 1F3FF 200D 2642 FE0F",
+ "char": "🙅🏿‍♂️",
+ "name": "man gesturing NO: dark skin tone"
+ },
+ {
+ "no": 684,
+ "code": "1F645 200D 2640 FE0F",
+ "char": "🙅‍♀️",
+ "name": "woman gesturing NO"
+ },
+ {
+ "no": 685,
+ "code": "1F645 1F3FB 200D 2640 FE0F",
+ "char": "🙅🏻‍♀️",
+ "name": "woman gesturing NO: light skin tone"
+ },
+ {
+ "no": 686,
+ "code": "1F645 1F3FC 200D 2640 FE0F",
+ "char": "🙅🏼‍♀️",
+ "name": "woman gesturing NO: medium-light skin tone"
+ },
+ {
+ "no": 687,
+ "code": "1F645 1F3FD 200D 2640 FE0F",
+ "char": "🙅🏽‍♀️",
+ "name": "woman gesturing NO: medium skin tone"
+ },
+ {
+ "no": 688,
+ "code": "1F645 1F3FE 200D 2640 FE0F",
+ "char": "🙅🏾‍♀️",
+ "name": "woman gesturing NO: medium-dark skin tone"
+ },
+ {
+ "no": 689,
+ "code": "1F645 1F3FF 200D 2640 FE0F",
+ "char": "🙅🏿‍♀️",
+ "name": "woman gesturing NO: dark skin tone"
+ },
+ {
+ "no": 690,
+ "code": "1F646",
+ "char": "🙆",
+ "name": "person gesturing OK"
+ },
+ {
+ "no": 691,
+ "code": "1F646 1F3FB",
+ "char": "🙆🏻",
+ "name": "person gesturing OK: light skin tone"
+ },
+ {
+ "no": 692,
+ "code": "1F646 1F3FC",
+ "char": "🙆🏼",
+ "name": "person gesturing OK: medium-light skin tone"
+ },
+ {
+ "no": 693,
+ "code": "1F646 1F3FD",
+ "char": "🙆🏽",
+ "name": "person gesturing OK: medium skin tone"
+ },
+ {
+ "no": 694,
+ "code": "1F646 1F3FE",
+ "char": "🙆🏾",
+ "name": "person gesturing OK: medium-dark skin tone"
+ },
+ {
+ "no": 695,
+ "code": "1F646 1F3FF",
+ "char": "🙆🏿",
+ "name": "person gesturing OK: dark skin tone"
+ },
+ {
+ "no": 696,
+ "code": "1F646 200D 2642 FE0F",
+ "char": "🙆‍♂️",
+ "name": "man gesturing OK"
+ },
+ {
+ "no": 697,
+ "code": "1F646 1F3FB 200D 2642 FE0F",
+ "char": "🙆🏻‍♂️",
+ "name": "man gesturing OK: light skin tone"
+ },
+ {
+ "no": 698,
+ "code": "1F646 1F3FC 200D 2642 FE0F",
+ "char": "🙆🏼‍♂️",
+ "name": "man gesturing OK: medium-light skin tone"
+ },
+ {
+ "no": 699,
+ "code": "1F646 1F3FD 200D 2642 FE0F",
+ "char": "🙆🏽‍♂️",
+ "name": "man gesturing OK: medium skin tone"
+ },
+ {
+ "no": 700,
+ "code": "1F646 1F3FE 200D 2642 FE0F",
+ "char": "🙆🏾‍♂️",
+ "name": "man gesturing OK: medium-dark skin tone"
+ },
+ {
+ "no": 701,
+ "code": "1F646 1F3FF 200D 2642 FE0F",
+ "char": "🙆🏿‍♂️",
+ "name": "man gesturing OK: dark skin tone"
+ },
+ {
+ "no": 702,
+ "code": "1F646 200D 2640 FE0F",
+ "char": "🙆‍♀️",
+ "name": "woman gesturing OK"
+ },
+ {
+ "no": 703,
+ "code": "1F646 1F3FB 200D 2640 FE0F",
+ "char": "🙆🏻‍♀️",
+ "name": "woman gesturing OK: light skin tone"
+ },
+ {
+ "no": 704,
+ "code": "1F646 1F3FC 200D 2640 FE0F",
+ "char": "🙆🏼‍♀️",
+ "name": "woman gesturing OK: medium-light skin tone"
+ },
+ {
+ "no": 705,
+ "code": "1F646 1F3FD 200D 2640 FE0F",
+ "char": "🙆🏽‍♀️",
+ "name": "woman gesturing OK: medium skin tone"
+ },
+ {
+ "no": 706,
+ "code": "1F646 1F3FE 200D 2640 FE0F",
+ "char": "🙆🏾‍♀️",
+ "name": "woman gesturing OK: medium-dark skin tone"
+ },
+ {
+ "no": 707,
+ "code": "1F646 1F3FF 200D 2640 FE0F",
+ "char": "🙆🏿‍♀️",
+ "name": "woman gesturing OK: dark skin tone"
+ },
+ {
+ "no": 708,
+ "code": "1F481",
+ "char": "💁",
+ "name": "person tipping hand"
+ },
+ {
+ "no": 709,
+ "code": "1F481 1F3FB",
+ "char": "💁🏻",
+ "name": "person tipping hand: light skin tone"
+ },
+ {
+ "no": 710,
+ "code": "1F481 1F3FC",
+ "char": "💁🏼",
+ "name": "person tipping hand: medium-light skin tone"
+ },
+ {
+ "no": 711,
+ "code": "1F481 1F3FD",
+ "char": "💁🏽",
+ "name": "person tipping hand: medium skin tone"
+ },
+ {
+ "no": 712,
+ "code": "1F481 1F3FE",
+ "char": "💁🏾",
+ "name": "person tipping hand: medium-dark skin tone"
+ },
+ {
+ "no": 713,
+ "code": "1F481 1F3FF",
+ "char": "💁🏿",
+ "name": "person tipping hand: dark skin tone"
+ },
+ {
+ "no": 714,
+ "code": "1F481 200D 2642 FE0F",
+ "char": "💁‍♂️",
+ "name": "man tipping hand"
+ },
+ {
+ "no": 715,
+ "code": "1F481 1F3FB 200D 2642 FE0F",
+ "char": "💁🏻‍♂️",
+ "name": "man tipping hand: light skin tone"
+ },
+ {
+ "no": 716,
+ "code": "1F481 1F3FC 200D 2642 FE0F",
+ "char": "💁🏼‍♂️",
+ "name": "man tipping hand: medium-light skin tone"
+ },
+ {
+ "no": 717,
+ "code": "1F481 1F3FD 200D 2642 FE0F",
+ "char": "💁🏽‍♂️",
+ "name": "man tipping hand: medium skin tone"
+ },
+ {
+ "no": 718,
+ "code": "1F481 1F3FE 200D 2642 FE0F",
+ "char": "💁🏾‍♂️",
+ "name": "man tipping hand: medium-dark skin tone"
+ },
+ {
+ "no": 719,
+ "code": "1F481 1F3FF 200D 2642 FE0F",
+ "char": "💁🏿‍♂️",
+ "name": "man tipping hand: dark skin tone"
+ },
+ {
+ "no": 720,
+ "code": "1F481 200D 2640 FE0F",
+ "char": "💁‍♀️",
+ "name": "woman tipping hand"
+ },
+ {
+ "no": 721,
+ "code": "1F481 1F3FB 200D 2640 FE0F",
+ "char": "💁🏻‍♀️",
+ "name": "woman tipping hand: light skin tone"
+ },
+ {
+ "no": 722,
+ "code": "1F481 1F3FC 200D 2640 FE0F",
+ "char": "💁🏼‍♀️",
+ "name": "woman tipping hand: medium-light skin tone"
+ },
+ {
+ "no": 723,
+ "code": "1F481 1F3FD 200D 2640 FE0F",
+ "char": "💁🏽‍♀️",
+ "name": "woman tipping hand: medium skin tone"
+ },
+ {
+ "no": 724,
+ "code": "1F481 1F3FE 200D 2640 FE0F",
+ "char": "💁🏾‍♀️",
+ "name": "woman tipping hand: medium-dark skin tone"
+ },
+ {
+ "no": 725,
+ "code": "1F481 1F3FF 200D 2640 FE0F",
+ "char": "💁🏿‍♀️",
+ "name": "woman tipping hand: dark skin tone"
+ },
+ {
+ "no": 726,
+ "code": "1F64B",
+ "char": "🙋",
+ "name": "person raising hand"
+ },
+ {
+ "no": 727,
+ "code": "1F64B 1F3FB",
+ "char": "🙋🏻",
+ "name": "person raising hand: light skin tone"
+ },
+ {
+ "no": 728,
+ "code": "1F64B 1F3FC",
+ "char": "🙋🏼",
+ "name": "person raising hand: medium-light skin tone"
+ },
+ {
+ "no": 729,
+ "code": "1F64B 1F3FD",
+ "char": "🙋🏽",
+ "name": "person raising hand: medium skin tone"
+ },
+ {
+ "no": 730,
+ "code": "1F64B 1F3FE",
+ "char": "🙋🏾",
+ "name": "person raising hand: medium-dark skin tone"
+ },
+ {
+ "no": 731,
+ "code": "1F64B 1F3FF",
+ "char": "🙋🏿",
+ "name": "person raising hand: dark skin tone"
+ },
+ {
+ "no": 732,
+ "code": "1F64B 200D 2642 FE0F",
+ "char": "🙋‍♂️",
+ "name": "man raising hand"
+ },
+ {
+ "no": 733,
+ "code": "1F64B 1F3FB 200D 2642 FE0F",
+ "char": "🙋🏻‍♂️",
+ "name": "man raising hand: light skin tone"
+ },
+ {
+ "no": 734,
+ "code": "1F64B 1F3FC 200D 2642 FE0F",
+ "char": "🙋🏼‍♂️",
+ "name": "man raising hand: medium-light skin tone"
+ },
+ {
+ "no": 735,
+ "code": "1F64B 1F3FD 200D 2642 FE0F",
+ "char": "🙋🏽‍♂️",
+ "name": "man raising hand: medium skin tone"
+ },
+ {
+ "no": 736,
+ "code": "1F64B 1F3FE 200D 2642 FE0F",
+ "char": "🙋🏾‍♂️",
+ "name": "man raising hand: medium-dark skin tone"
+ },
+ {
+ "no": 737,
+ "code": "1F64B 1F3FF 200D 2642 FE0F",
+ "char": "🙋🏿‍♂️",
+ "name": "man raising hand: dark skin tone"
+ },
+ {
+ "no": 738,
+ "code": "1F64B 200D 2640 FE0F",
+ "char": "🙋‍♀️",
+ "name": "woman raising hand"
+ },
+ {
+ "no": 739,
+ "code": "1F64B 1F3FB 200D 2640 FE0F",
+ "char": "🙋🏻‍♀️",
+ "name": "woman raising hand: light skin tone"
+ },
+ {
+ "no": 740,
+ "code": "1F64B 1F3FC 200D 2640 FE0F",
+ "char": "🙋🏼‍♀️",
+ "name": "woman raising hand: medium-light skin tone"
+ },
+ {
+ "no": 741,
+ "code": "1F64B 1F3FD 200D 2640 FE0F",
+ "char": "🙋🏽‍♀️",
+ "name": "woman raising hand: medium skin tone"
+ },
+ {
+ "no": 742,
+ "code": "1F64B 1F3FE 200D 2640 FE0F",
+ "char": "🙋🏾‍♀️",
+ "name": "woman raising hand: medium-dark skin tone"
+ },
+ {
+ "no": 743,
+ "code": "1F64B 1F3FF 200D 2640 FE0F",
+ "char": "🙋🏿‍♀️",
+ "name": "woman raising hand: dark skin tone"
+ },
+ {
+ "no": 744,
+ "code": "1F647",
+ "char": "🙇",
+ "name": "person bowing"
+ },
+ {
+ "no": 745,
+ "code": "1F647 1F3FB",
+ "char": "🙇🏻",
+ "name": "person bowing: light skin tone"
+ },
+ {
+ "no": 746,
+ "code": "1F647 1F3FC",
+ "char": "🙇🏼",
+ "name": "person bowing: medium-light skin tone"
+ },
+ {
+ "no": 747,
+ "code": "1F647 1F3FD",
+ "char": "🙇🏽",
+ "name": "person bowing: medium skin tone"
+ },
+ {
+ "no": 748,
+ "code": "1F647 1F3FE",
+ "char": "🙇🏾",
+ "name": "person bowing: medium-dark skin tone"
+ },
+ {
+ "no": 749,
+ "code": "1F647 1F3FF",
+ "char": "🙇🏿",
+ "name": "person bowing: dark skin tone"
+ },
+ {
+ "no": 750,
+ "code": "1F647 200D 2642 FE0F",
+ "char": "🙇‍♂️",
+ "name": "man bowing"
+ },
+ {
+ "no": 751,
+ "code": "1F647 1F3FB 200D 2642 FE0F",
+ "char": "🙇🏻‍♂️",
+ "name": "man bowing: light skin tone"
+ },
+ {
+ "no": 752,
+ "code": "1F647 1F3FC 200D 2642 FE0F",
+ "char": "🙇🏼‍♂️",
+ "name": "man bowing: medium-light skin tone"
+ },
+ {
+ "no": 753,
+ "code": "1F647 1F3FD 200D 2642 FE0F",
+ "char": "🙇🏽‍♂️",
+ "name": "man bowing: medium skin tone"
+ },
+ {
+ "no": 754,
+ "code": "1F647 1F3FE 200D 2642 FE0F",
+ "char": "🙇🏾‍♂️",
+ "name": "man bowing: medium-dark skin tone"
+ },
+ {
+ "no": 755,
+ "code": "1F647 1F3FF 200D 2642 FE0F",
+ "char": "🙇🏿‍♂️",
+ "name": "man bowing: dark skin tone"
+ },
+ {
+ "no": 756,
+ "code": "1F647 200D 2640 FE0F",
+ "char": "🙇‍♀️",
+ "name": "woman bowing"
+ },
+ {
+ "no": 757,
+ "code": "1F647 1F3FB 200D 2640 FE0F",
+ "char": "🙇🏻‍♀️",
+ "name": "woman bowing: light skin tone"
+ },
+ {
+ "no": 758,
+ "code": "1F647 1F3FC 200D 2640 FE0F",
+ "char": "🙇🏼‍♀️",
+ "name": "woman bowing: medium-light skin tone"
+ },
+ {
+ "no": 759,
+ "code": "1F647 1F3FD 200D 2640 FE0F",
+ "char": "🙇🏽‍♀️",
+ "name": "woman bowing: medium skin tone"
+ },
+ {
+ "no": 760,
+ "code": "1F647 1F3FE 200D 2640 FE0F",
+ "char": "🙇🏾‍♀️",
+ "name": "woman bowing: medium-dark skin tone"
+ },
+ {
+ "no": 761,
+ "code": "1F647 1F3FF 200D 2640 FE0F",
+ "char": "🙇🏿‍♀️",
+ "name": "woman bowing: dark skin tone"
+ },
+ {
+ "no": 762,
+ "code": "1F926",
+ "char": "🤦",
+ "name": "person facepalming"
+ },
+ {
+ "no": 763,
+ "code": "1F926 1F3FB",
+ "char": "🤦🏻",
+ "name": "person facepalming: light skin tone"
+ },
+ {
+ "no": 764,
+ "code": "1F926 1F3FC",
+ "char": "🤦🏼",
+ "name": "person facepalming: medium-light skin tone"
+ },
+ {
+ "no": 765,
+ "code": "1F926 1F3FD",
+ "char": "🤦🏽",
+ "name": "person facepalming: medium skin tone"
+ },
+ {
+ "no": 766,
+ "code": "1F926 1F3FE",
+ "char": "🤦🏾",
+ "name": "person facepalming: medium-dark skin tone"
+ },
+ {
+ "no": 767,
+ "code": "1F926 1F3FF",
+ "char": "🤦🏿",
+ "name": "person facepalming: dark skin tone"
+ },
+ {
+ "no": 768,
+ "code": "1F926 200D 2642 FE0F",
+ "char": "🤦‍♂️",
+ "name": "man facepalming"
+ },
+ {
+ "no": 769,
+ "code": "1F926 1F3FB 200D 2642 FE0F",
+ "char": "🤦🏻‍♂️",
+ "name": "man facepalming: light skin tone"
+ },
+ {
+ "no": 770,
+ "code": "1F926 1F3FC 200D 2642 FE0F",
+ "char": "🤦🏼‍♂️",
+ "name": "man facepalming: medium-light skin tone"
+ },
+ {
+ "no": 771,
+ "code": "1F926 1F3FD 200D 2642 FE0F",
+ "char": "🤦🏽‍♂️",
+ "name": "man facepalming: medium skin tone"
+ },
+ {
+ "no": 772,
+ "code": "1F926 1F3FE 200D 2642 FE0F",
+ "char": "🤦🏾‍♂️",
+ "name": "man facepalming: medium-dark skin tone"
+ },
+ {
+ "no": 773,
+ "code": "1F926 1F3FF 200D 2642 FE0F",
+ "char": "🤦🏿‍♂️",
+ "name": "man facepalming: dark skin tone"
+ },
+ {
+ "no": 774,
+ "code": "1F926 200D 2640 FE0F",
+ "char": "🤦‍♀️",
+ "name": "woman facepalming"
+ },
+ {
+ "no": 775,
+ "code": "1F926 1F3FB 200D 2640 FE0F",
+ "char": "🤦🏻‍♀️",
+ "name": "woman facepalming: light skin tone"
+ },
+ {
+ "no": 776,
+ "code": "1F926 1F3FC 200D 2640 FE0F",
+ "char": "🤦🏼‍♀️",
+ "name": "woman facepalming: medium-light skin tone"
+ },
+ {
+ "no": 777,
+ "code": "1F926 1F3FD 200D 2640 FE0F",
+ "char": "🤦🏽‍♀️",
+ "name": "woman facepalming: medium skin tone"
+ },
+ {
+ "no": 778,
+ "code": "1F926 1F3FE 200D 2640 FE0F",
+ "char": "🤦🏾‍♀️",
+ "name": "woman facepalming: medium-dark skin tone"
+ },
+ {
+ "no": 779,
+ "code": "1F926 1F3FF 200D 2640 FE0F",
+ "char": "🤦🏿‍♀️",
+ "name": "woman facepalming: dark skin tone"
+ },
+ {
+ "no": 780,
+ "code": "1F937",
+ "char": "🤷",
+ "name": "person shrugging"
+ },
+ {
+ "no": 781,
+ "code": "1F937 1F3FB",
+ "char": "🤷🏻",
+ "name": "person shrugging: light skin tone"
+ },
+ {
+ "no": 782,
+ "code": "1F937 1F3FC",
+ "char": "🤷🏼",
+ "name": "person shrugging: medium-light skin tone"
+ },
+ {
+ "no": 783,
+ "code": "1F937 1F3FD",
+ "char": "🤷🏽",
+ "name": "person shrugging: medium skin tone"
+ },
+ {
+ "no": 784,
+ "code": "1F937 1F3FE",
+ "char": "🤷🏾",
+ "name": "person shrugging: medium-dark skin tone"
+ },
+ {
+ "no": 785,
+ "code": "1F937 1F3FF",
+ "char": "🤷🏿",
+ "name": "person shrugging: dark skin tone"
+ },
+ {
+ "no": 786,
+ "code": "1F937 200D 2642 FE0F",
+ "char": "🤷‍♂️",
+ "name": "man shrugging"
+ },
+ {
+ "no": 787,
+ "code": "1F937 1F3FB 200D 2642 FE0F",
+ "char": "🤷🏻‍♂️",
+ "name": "man shrugging: light skin tone"
+ },
+ {
+ "no": 788,
+ "code": "1F937 1F3FC 200D 2642 FE0F",
+ "char": "🤷🏼‍♂️",
+ "name": "man shrugging: medium-light skin tone"
+ },
+ {
+ "no": 789,
+ "code": "1F937 1F3FD 200D 2642 FE0F",
+ "char": "🤷🏽‍♂️",
+ "name": "man shrugging: medium skin tone"
+ },
+ {
+ "no": 790,
+ "code": "1F937 1F3FE 200D 2642 FE0F",
+ "char": "🤷🏾‍♂️",
+ "name": "man shrugging: medium-dark skin tone"
+ },
+ {
+ "no": 791,
+ "code": "1F937 1F3FF 200D 2642 FE0F",
+ "char": "🤷🏿‍♂️",
+ "name": "man shrugging: dark skin tone"
+ },
+ {
+ "no": 792,
+ "code": "1F937 200D 2640 FE0F",
+ "char": "🤷‍♀️",
+ "name": "woman shrugging"
+ },
+ {
+ "no": 793,
+ "code": "1F937 1F3FB 200D 2640 FE0F",
+ "char": "🤷🏻‍♀️",
+ "name": "woman shrugging: light skin tone"
+ },
+ {
+ "no": 794,
+ "code": "1F937 1F3FC 200D 2640 FE0F",
+ "char": "🤷🏼‍♀️",
+ "name": "woman shrugging: medium-light skin tone"
+ },
+ {
+ "no": 795,
+ "code": "1F937 1F3FD 200D 2640 FE0F",
+ "char": "🤷🏽‍♀️",
+ "name": "woman shrugging: medium skin tone"
+ },
+ {
+ "no": 796,
+ "code": "1F937 1F3FE 200D 2640 FE0F",
+ "char": "🤷🏾‍♀️",
+ "name": "woman shrugging: medium-dark skin tone"
+ },
+ {
+ "no": 797,
+ "code": "1F937 1F3FF 200D 2640 FE0F",
+ "char": "🤷🏿‍♀️",
+ "name": "woman shrugging: dark skin tone"
+ },
+ {
+ "no": 798,
+ "code": "1F486",
+ "char": "💆",
+ "name": "person getting massage"
+ },
+ {
+ "no": 799,
+ "code": "1F486 1F3FB",
+ "char": "💆🏻",
+ "name": "person getting massage: light skin tone"
+ },
+ {
+ "no": 800,
+ "code": "1F486 1F3FC",
+ "char": "💆🏼",
+ "name": "person getting massage: medium-light skin tone"
+ },
+ {
+ "no": 801,
+ "code": "1F486 1F3FD",
+ "char": "💆🏽",
+ "name": "person getting massage: medium skin tone"
+ },
+ {
+ "no": 802,
+ "code": "1F486 1F3FE",
+ "char": "💆🏾",
+ "name": "person getting massage: medium-dark skin tone"
+ },
+ {
+ "no": 803,
+ "code": "1F486 1F3FF",
+ "char": "💆🏿",
+ "name": "person getting massage: dark skin tone"
+ },
+ {
+ "no": 804,
+ "code": "1F486 200D 2642 FE0F",
+ "char": "💆‍♂️",
+ "name": "man getting massage"
+ },
+ {
+ "no": 805,
+ "code": "1F486 1F3FB 200D 2642 FE0F",
+ "char": "💆🏻‍♂️",
+ "name": "man getting massage: light skin tone"
+ },
+ {
+ "no": 806,
+ "code": "1F486 1F3FC 200D 2642 FE0F",
+ "char": "💆🏼‍♂️",
+ "name": "man getting massage: medium-light skin tone"
+ },
+ {
+ "no": 807,
+ "code": "1F486 1F3FD 200D 2642 FE0F",
+ "char": "💆🏽‍♂️",
+ "name": "man getting massage: medium skin tone"
+ },
+ {
+ "no": 808,
+ "code": "1F486 1F3FE 200D 2642 FE0F",
+ "char": "💆🏾‍♂️",
+ "name": "man getting massage: medium-dark skin tone"
+ },
+ {
+ "no": 809,
+ "code": "1F486 1F3FF 200D 2642 FE0F",
+ "char": "💆🏿‍♂️",
+ "name": "man getting massage: dark skin tone"
+ },
+ {
+ "no": 810,
+ "code": "1F486 200D 2640 FE0F",
+ "char": "💆‍♀️",
+ "name": "woman getting massage"
+ },
+ {
+ "no": 811,
+ "code": "1F486 1F3FB 200D 2640 FE0F",
+ "char": "💆🏻‍♀️",
+ "name": "woman getting massage: light skin tone"
+ },
+ {
+ "no": 812,
+ "code": "1F486 1F3FC 200D 2640 FE0F",
+ "char": "💆🏼‍♀️",
+ "name": "woman getting massage: medium-light skin tone"
+ },
+ {
+ "no": 813,
+ "code": "1F486 1F3FD 200D 2640 FE0F",
+ "char": "💆🏽‍♀️",
+ "name": "woman getting massage: medium skin tone"
+ },
+ {
+ "no": 814,
+ "code": "1F486 1F3FE 200D 2640 FE0F",
+ "char": "💆🏾‍♀️",
+ "name": "woman getting massage: medium-dark skin tone"
+ },
+ {
+ "no": 815,
+ "code": "1F486 1F3FF 200D 2640 FE0F",
+ "char": "💆🏿‍♀️",
+ "name": "woman getting massage: dark skin tone"
+ },
+ {
+ "no": 816,
+ "code": "1F487",
+ "char": "💇",
+ "name": "person getting haircut"
+ },
+ {
+ "no": 817,
+ "code": "1F487 1F3FB",
+ "char": "💇🏻",
+ "name": "person getting haircut: light skin tone"
+ },
+ {
+ "no": 818,
+ "code": "1F487 1F3FC",
+ "char": "💇🏼",
+ "name": "person getting haircut: medium-light skin tone"
+ },
+ {
+ "no": 819,
+ "code": "1F487 1F3FD",
+ "char": "💇🏽",
+ "name": "person getting haircut: medium skin tone"
+ },
+ {
+ "no": 820,
+ "code": "1F487 1F3FE",
+ "char": "💇🏾",
+ "name": "person getting haircut: medium-dark skin tone"
+ },
+ {
+ "no": 821,
+ "code": "1F487 1F3FF",
+ "char": "💇🏿",
+ "name": "person getting haircut: dark skin tone"
+ },
+ {
+ "no": 822,
+ "code": "1F487 200D 2642 FE0F",
+ "char": "💇‍♂️",
+ "name": "man getting haircut"
+ },
+ {
+ "no": 823,
+ "code": "1F487 1F3FB 200D 2642 FE0F",
+ "char": "💇🏻‍♂️",
+ "name": "man getting haircut: light skin tone"
+ },
+ {
+ "no": 824,
+ "code": "1F487 1F3FC 200D 2642 FE0F",
+ "char": "💇🏼‍♂️",
+ "name": "man getting haircut: medium-light skin tone"
+ },
+ {
+ "no": 825,
+ "code": "1F487 1F3FD 200D 2642 FE0F",
+ "char": "💇🏽‍♂️",
+ "name": "man getting haircut: medium skin tone"
+ },
+ {
+ "no": 826,
+ "code": "1F487 1F3FE 200D 2642 FE0F",
+ "char": "💇🏾‍♂️",
+ "name": "man getting haircut: medium-dark skin tone"
+ },
+ {
+ "no": 827,
+ "code": "1F487 1F3FF 200D 2642 FE0F",
+ "char": "💇🏿‍♂️",
+ "name": "man getting haircut: dark skin tone"
+ },
+ {
+ "no": 828,
+ "code": "1F487 200D 2640 FE0F",
+ "char": "💇‍♀️",
+ "name": "woman getting haircut"
+ },
+ {
+ "no": 829,
+ "code": "1F487 1F3FB 200D 2640 FE0F",
+ "char": "💇🏻‍♀️",
+ "name": "woman getting haircut: light skin tone"
+ },
+ {
+ "no": 830,
+ "code": "1F487 1F3FC 200D 2640 FE0F",
+ "char": "💇🏼‍♀️",
+ "name": "woman getting haircut: medium-light skin tone"
+ },
+ {
+ "no": 831,
+ "code": "1F487 1F3FD 200D 2640 FE0F",
+ "char": "💇🏽‍♀️",
+ "name": "woman getting haircut: medium skin tone"
+ },
+ {
+ "no": 832,
+ "code": "1F487 1F3FE 200D 2640 FE0F",
+ "char": "💇🏾‍♀️",
+ "name": "woman getting haircut: medium-dark skin tone"
+ },
+ {
+ "no": 833,
+ "code": "1F487 1F3FF 200D 2640 FE0F",
+ "char": "💇🏿‍♀️",
+ "name": "woman getting haircut: dark skin tone"
+ },
+ {
+ "no": 834,
+ "code": "1F6B6",
+ "char": "🚶",
+ "name": "person walking"
+ },
+ {
+ "no": 835,
+ "code": "1F6B6 1F3FB",
+ "char": "🚶🏻",
+ "name": "person walking: light skin tone"
+ },
+ {
+ "no": 836,
+ "code": "1F6B6 1F3FC",
+ "char": "🚶🏼",
+ "name": "person walking: medium-light skin tone"
+ },
+ {
+ "no": 837,
+ "code": "1F6B6 1F3FD",
+ "char": "🚶🏽",
+ "name": "person walking: medium skin tone"
+ },
+ {
+ "no": 838,
+ "code": "1F6B6 1F3FE",
+ "char": "🚶🏾",
+ "name": "person walking: medium-dark skin tone"
+ },
+ {
+ "no": 839,
+ "code": "1F6B6 1F3FF",
+ "char": "🚶🏿",
+ "name": "person walking: dark skin tone"
+ },
+ {
+ "no": 840,
+ "code": "1F6B6 200D 2642 FE0F",
+ "char": "🚶‍♂️",
+ "name": "man walking"
+ },
+ {
+ "no": 841,
+ "code": "1F6B6 1F3FB 200D 2642 FE0F",
+ "char": "🚶🏻‍♂️",
+ "name": "man walking: light skin tone"
+ },
+ {
+ "no": 842,
+ "code": "1F6B6 1F3FC 200D 2642 FE0F",
+ "char": "🚶🏼‍♂️",
+ "name": "man walking: medium-light skin tone"
+ },
+ {
+ "no": 843,
+ "code": "1F6B6 1F3FD 200D 2642 FE0F",
+ "char": "🚶🏽‍♂️",
+ "name": "man walking: medium skin tone"
+ },
+ {
+ "no": 844,
+ "code": "1F6B6 1F3FE 200D 2642 FE0F",
+ "char": "🚶🏾‍♂️",
+ "name": "man walking: medium-dark skin tone"
+ },
+ {
+ "no": 845,
+ "code": "1F6B6 1F3FF 200D 2642 FE0F",
+ "char": "🚶🏿‍♂️",
+ "name": "man walking: dark skin tone"
+ },
+ {
+ "no": 846,
+ "code": "1F6B6 200D 2640 FE0F",
+ "char": "🚶‍♀️",
+ "name": "woman walking"
+ },
+ {
+ "no": 847,
+ "code": "1F6B6 1F3FB 200D 2640 FE0F",
+ "char": "🚶🏻‍♀️",
+ "name": "woman walking: light skin tone"
+ },
+ {
+ "no": 848,
+ "code": "1F6B6 1F3FC 200D 2640 FE0F",
+ "char": "🚶🏼‍♀️",
+ "name": "woman walking: medium-light skin tone"
+ },
+ {
+ "no": 849,
+ "code": "1F6B6 1F3FD 200D 2640 FE0F",
+ "char": "🚶🏽‍♀️",
+ "name": "woman walking: medium skin tone"
+ },
+ {
+ "no": 850,
+ "code": "1F6B6 1F3FE 200D 2640 FE0F",
+ "char": "🚶🏾‍♀️",
+ "name": "woman walking: medium-dark skin tone"
+ },
+ {
+ "no": 851,
+ "code": "1F6B6 1F3FF 200D 2640 FE0F",
+ "char": "🚶🏿‍♀️",
+ "name": "woman walking: dark skin tone"
+ },
+ {
+ "no": 852,
+ "code": "1F3C3",
+ "char": "🏃",
+ "name": "person running"
+ },
+ {
+ "no": 853,
+ "code": "1F3C3 1F3FB",
+ "char": "🏃🏻",
+ "name": "person running: light skin tone"
+ },
+ {
+ "no": 854,
+ "code": "1F3C3 1F3FC",
+ "char": "🏃🏼",
+ "name": "person running: medium-light skin tone"
+ },
+ {
+ "no": 855,
+ "code": "1F3C3 1F3FD",
+ "char": "🏃🏽",
+ "name": "person running: medium skin tone"
+ },
+ {
+ "no": 856,
+ "code": "1F3C3 1F3FE",
+ "char": "🏃🏾",
+ "name": "person running: medium-dark skin tone"
+ },
+ {
+ "no": 857,
+ "code": "1F3C3 1F3FF",
+ "char": "🏃🏿",
+ "name": "person running: dark skin tone"
+ },
+ {
+ "no": 858,
+ "code": "1F3C3 200D 2642 FE0F",
+ "char": "🏃‍♂️",
+ "name": "man running"
+ },
+ {
+ "no": 859,
+ "code": "1F3C3 1F3FB 200D 2642 FE0F",
+ "char": "🏃🏻‍♂️",
+ "name": "man running: light skin tone"
+ },
+ {
+ "no": 860,
+ "code": "1F3C3 1F3FC 200D 2642 FE0F",
+ "char": "🏃🏼‍♂️",
+ "name": "man running: medium-light skin tone"
+ },
+ {
+ "no": 861,
+ "code": "1F3C3 1F3FD 200D 2642 FE0F",
+ "char": "🏃🏽‍♂️",
+ "name": "man running: medium skin tone"
+ },
+ {
+ "no": 862,
+ "code": "1F3C3 1F3FE 200D 2642 FE0F",
+ "char": "🏃🏾‍♂️",
+ "name": "man running: medium-dark skin tone"
+ },
+ {
+ "no": 863,
+ "code": "1F3C3 1F3FF 200D 2642 FE0F",
+ "char": "🏃🏿‍♂️",
+ "name": "man running: dark skin tone"
+ },
+ {
+ "no": 864,
+ "code": "1F3C3 200D 2640 FE0F",
+ "char": "🏃‍♀️",
+ "name": "woman running"
+ },
+ {
+ "no": 865,
+ "code": "1F3C3 1F3FB 200D 2640 FE0F",
+ "char": "🏃🏻‍♀️",
+ "name": "woman running: light skin tone"
+ },
+ {
+ "no": 866,
+ "code": "1F3C3 1F3FC 200D 2640 FE0F",
+ "char": "🏃🏼‍♀️",
+ "name": "woman running: medium-light skin tone"
+ },
+ {
+ "no": 867,
+ "code": "1F3C3 1F3FD 200D 2640 FE0F",
+ "char": "🏃🏽‍♀️",
+ "name": "woman running: medium skin tone"
+ },
+ {
+ "no": 868,
+ "code": "1F3C3 1F3FE 200D 2640 FE0F",
+ "char": "🏃🏾‍♀️",
+ "name": "woman running: medium-dark skin tone"
+ },
+ {
+ "no": 869,
+ "code": "1F3C3 1F3FF 200D 2640 FE0F",
+ "char": "🏃🏿‍♀️",
+ "name": "woman running: dark skin tone"
+ },
+ {
+ "no": 870,
+ "code": "1F483",
+ "char": "💃",
+ "name": "woman dancing"
+ },
+ {
+ "no": 871,
+ "code": "1F483 1F3FB",
+ "char": "💃🏻",
+ "name": "woman dancing: light skin tone"
+ },
+ {
+ "no": 872,
+ "code": "1F483 1F3FC",
+ "char": "💃🏼",
+ "name": "woman dancing: medium-light skin tone"
+ },
+ {
+ "no": 873,
+ "code": "1F483 1F3FD",
+ "char": "💃🏽",
+ "name": "woman dancing: medium skin tone"
+ },
+ {
+ "no": 874,
+ "code": "1F483 1F3FE",
+ "char": "💃🏾",
+ "name": "woman dancing: medium-dark skin tone"
+ },
+ {
+ "no": 875,
+ "code": "1F483 1F3FF",
+ "char": "💃🏿",
+ "name": "woman dancing: dark skin tone"
+ },
+ {
+ "no": 876,
+ "code": "1F57A",
+ "char": "🕺",
+ "name": "man dancing"
+ },
+ {
+ "no": 877,
+ "code": "1F57A 1F3FB",
+ "char": "🕺🏻",
+ "name": "man dancing: light skin tone"
+ },
+ {
+ "no": 878,
+ "code": "1F57A 1F3FC",
+ "char": "🕺🏼",
+ "name": "man dancing: medium-light skin tone"
+ },
+ {
+ "no": 879,
+ "code": "1F57A 1F3FD",
+ "char": "🕺🏽",
+ "name": "man dancing: medium skin tone"
+ },
+ {
+ "no": 880,
+ "code": "1F57A 1F3FE",
+ "char": "🕺🏾",
+ "name": "man dancing: medium-dark skin tone"
+ },
+ {
+ "no": 881,
+ "code": "1F57A 1F3FF",
+ "char": "🕺🏿",
+ "name": "man dancing: dark skin tone"
+ },
+ {
+ "no": 882,
+ "code": "1F46F",
+ "char": "👯",
+ "name": "people with bunny ears"
+ },
+ {
+ "no": 883,
+ "code": "1F46F 200D 2642 FE0F",
+ "char": "👯‍♂️",
+ "name": "men with bunny ears"
+ },
+ {
+ "no": 884,
+ "code": "1F46F 200D 2640 FE0F",
+ "char": "👯‍♀️",
+ "name": "women with bunny ears"
+ },
+ {
+ "no": 885,
+ "code": "1F9D6",
+ "char": "🧖",
+ "name": "⊛ person in steamy room"
+ },
+ {
+ "no": 886,
+ "code": "1F9D6 1F3FB",
+ "char": "🧖🏻",
+ "name": "⊛ person in steamy room: light skin tone"
+ },
+ {
+ "no": 887,
+ "code": "1F9D6 1F3FC",
+ "char": "🧖🏼",
+ "name": "⊛ person in steamy room: medium-light skin tone"
+ },
+ {
+ "no": 888,
+ "code": "1F9D6 1F3FD",
+ "char": "🧖🏽",
+ "name": "⊛ person in steamy room: medium skin tone"
+ },
+ {
+ "no": 889,
+ "code": "1F9D6 1F3FE",
+ "char": "🧖🏾",
+ "name": "⊛ person in steamy room: medium-dark skin tone"
+ },
+ {
+ "no": 890,
+ "code": "1F9D6 1F3FF",
+ "char": "🧖🏿",
+ "name": "⊛ person in steamy room: dark skin tone"
+ },
+ {
+ "no": 891,
+ "code": "1F9D6 200D 2640 FE0F",
+ "char": "🧖‍♀️",
+ "name": "⊛ woman in steamy room"
+ },
+ {
+ "no": 892,
+ "code": "1F9D6 1F3FB 200D 2640 FE0F",
+ "char": "🧖🏻‍♀️",
+ "name": "⊛ woman in steamy room: light skin tone"
+ },
+ {
+ "no": 893,
+ "code": "1F9D6 1F3FC 200D 2640 FE0F",
+ "char": "🧖🏼‍♀️",
+ "name": "⊛ woman in steamy room: medium-light skin tone"
+ },
+ {
+ "no": 894,
+ "code": "1F9D6 1F3FD 200D 2640 FE0F",
+ "char": "🧖🏽‍♀️",
+ "name": "⊛ woman in steamy room: medium skin tone"
+ },
+ {
+ "no": 895,
+ "code": "1F9D6 1F3FE 200D 2640 FE0F",
+ "char": "🧖🏾‍♀️",
+ "name": "⊛ woman in steamy room: medium-dark skin tone"
+ },
+ {
+ "no": 896,
+ "code": "1F9D6 1F3FF 200D 2640 FE0F",
+ "char": "🧖🏿‍♀️",
+ "name": "⊛ woman in steamy room: dark skin tone"
+ },
+ {
+ "no": 897,
+ "code": "1F9D6 200D 2642 FE0F",
+ "char": "🧖‍♂️",
+ "name": "⊛ man in steamy room"
+ },
+ {
+ "no": 898,
+ "code": "1F9D6 1F3FB 200D 2642 FE0F",
+ "char": "🧖🏻‍♂️",
+ "name": "⊛ man in steamy room: light skin tone"
+ },
+ {
+ "no": 899,
+ "code": "1F9D6 1F3FC 200D 2642 FE0F",
+ "char": "🧖🏼‍♂️",
+ "name": "⊛ man in steamy room: medium-light skin tone"
+ },
+ {
+ "no": 900,
+ "code": "1F9D6 1F3FD 200D 2642 FE0F",
+ "char": "🧖🏽‍♂️",
+ "name": "⊛ man in steamy room: medium skin tone"
+ },
+ {
+ "no": 901,
+ "code": "1F9D6 1F3FE 200D 2642 FE0F",
+ "char": "🧖🏾‍♂️",
+ "name": "⊛ man in steamy room: medium-dark skin tone"
+ },
+ {
+ "no": 902,
+ "code": "1F9D6 1F3FF 200D 2642 FE0F",
+ "char": "🧖🏿‍♂️",
+ "name": "⊛ man in steamy room: dark skin tone"
+ },
+ {
+ "no": 903,
+ "code": "1F9D7",
+ "char": "🧗",
+ "name": "⊛ person climbing"
+ },
+ {
+ "no": 904,
+ "code": "1F9D7 1F3FB",
+ "char": "🧗🏻",
+ "name": "⊛ person climbing: light skin tone"
+ },
+ {
+ "no": 905,
+ "code": "1F9D7 1F3FC",
+ "char": "🧗🏼",
+ "name": "⊛ person climbing: medium-light skin tone"
+ },
+ {
+ "no": 906,
+ "code": "1F9D7 1F3FD",
+ "char": "🧗🏽",
+ "name": "⊛ person climbing: medium skin tone"
+ },
+ {
+ "no": 907,
+ "code": "1F9D7 1F3FE",
+ "char": "🧗🏾",
+ "name": "⊛ person climbing: medium-dark skin tone"
+ },
+ {
+ "no": 908,
+ "code": "1F9D7 1F3FF",
+ "char": "🧗🏿",
+ "name": "⊛ person climbing: dark skin tone"
+ },
+ {
+ "no": 909,
+ "code": "1F9D7 200D 2640 FE0F",
+ "char": "🧗‍♀️",
+ "name": "⊛ woman climbing"
+ },
+ {
+ "no": 910,
+ "code": "1F9D7 1F3FB 200D 2640 FE0F",
+ "char": "🧗🏻‍♀️",
+ "name": "⊛ woman climbing: light skin tone"
+ },
+ {
+ "no": 911,
+ "code": "1F9D7 1F3FC 200D 2640 FE0F",
+ "char": "🧗🏼‍♀️",
+ "name": "⊛ woman climbing: medium-light skin tone"
+ },
+ {
+ "no": 912,
+ "code": "1F9D7 1F3FD 200D 2640 FE0F",
+ "char": "🧗🏽‍♀️",
+ "name": "⊛ woman climbing: medium skin tone"
+ },
+ {
+ "no": 913,
+ "code": "1F9D7 1F3FE 200D 2640 FE0F",
+ "char": "🧗🏾‍♀️",
+ "name": "⊛ woman climbing: medium-dark skin tone"
+ },
+ {
+ "no": 914,
+ "code": "1F9D7 1F3FF 200D 2640 FE0F",
+ "char": "🧗🏿‍♀️",
+ "name": "⊛ woman climbing: dark skin tone"
+ },
+ {
+ "no": 915,
+ "code": "1F9D7 200D 2642 FE0F",
+ "char": "🧗‍♂️",
+ "name": "⊛ man climbing"
+ },
+ {
+ "no": 916,
+ "code": "1F9D7 1F3FB 200D 2642 FE0F",
+ "char": "🧗🏻‍♂️",
+ "name": "⊛ man climbing: light skin tone"
+ },
+ {
+ "no": 917,
+ "code": "1F9D7 1F3FC 200D 2642 FE0F",
+ "char": "🧗🏼‍♂️",
+ "name": "⊛ man climbing: medium-light skin tone"
+ },
+ {
+ "no": 918,
+ "code": "1F9D7 1F3FD 200D 2642 FE0F",
+ "char": "🧗🏽‍♂️",
+ "name": "⊛ man climbing: medium skin tone"
+ },
+ {
+ "no": 919,
+ "code": "1F9D7 1F3FE 200D 2642 FE0F",
+ "char": "🧗🏾‍♂️",
+ "name": "⊛ man climbing: medium-dark skin tone"
+ },
+ {
+ "no": 920,
+ "code": "1F9D7 1F3FF 200D 2642 FE0F",
+ "char": "🧗🏿‍♂️",
+ "name": "⊛ man climbing: dark skin tone"
+ },
+ {
+ "no": 921,
+ "code": "1F9D8",
+ "char": "🧘",
+ "name": "⊛ person in lotus position"
+ },
+ {
+ "no": 922,
+ "code": "1F9D8 1F3FB",
+ "char": "🧘🏻",
+ "name": "⊛ person in lotus position: light skin tone"
+ },
+ {
+ "no": 923,
+ "code": "1F9D8 1F3FC",
+ "char": "🧘🏼",
+ "name": "⊛ person in lotus position: medium-light skin tone"
+ },
+ {
+ "no": 924,
+ "code": "1F9D8 1F3FD",
+ "char": "🧘🏽",
+ "name": "⊛ person in lotus position: medium skin tone"
+ },
+ {
+ "no": 925,
+ "code": "1F9D8 1F3FE",
+ "char": "🧘🏾",
+ "name": "⊛ person in lotus position: medium-dark skin tone"
+ },
+ {
+ "no": 926,
+ "code": "1F9D8 1F3FF",
+ "char": "🧘🏿",
+ "name": "⊛ person in lotus position: dark skin tone"
+ },
+ {
+ "no": 927,
+ "code": "1F9D8 200D 2640 FE0F",
+ "char": "🧘‍♀️",
+ "name": "⊛ woman in lotus position"
+ },
+ {
+ "no": 928,
+ "code": "1F9D8 1F3FB 200D 2640 FE0F",
+ "char": "🧘🏻‍♀️",
+ "name": "⊛ woman in lotus position: light skin tone"
+ },
+ {
+ "no": 929,
+ "code": "1F9D8 1F3FC 200D 2640 FE0F",
+ "char": "🧘🏼‍♀️",
+ "name": "⊛ woman in lotus position: medium-light skin tone"
+ },
+ {
+ "no": 930,
+ "code": "1F9D8 1F3FD 200D 2640 FE0F",
+ "char": "🧘🏽‍♀️",
+ "name": "⊛ woman in lotus position: medium skin tone"
+ },
+ {
+ "no": 931,
+ "code": "1F9D8 1F3FE 200D 2640 FE0F",
+ "char": "🧘🏾‍♀️",
+ "name": "⊛ woman in lotus position: medium-dark skin tone"
+ },
+ {
+ "no": 932,
+ "code": "1F9D8 1F3FF 200D 2640 FE0F",
+ "char": "🧘🏿‍♀️",
+ "name": "⊛ woman in lotus position: dark skin tone"
+ },
+ {
+ "no": 933,
+ "code": "1F9D8 200D 2642 FE0F",
+ "char": "🧘‍♂️",
+ "name": "⊛ man in lotus position"
+ },
+ {
+ "no": 934,
+ "code": "1F9D8 1F3FB 200D 2642 FE0F",
+ "char": "🧘🏻‍♂️",
+ "name": "⊛ man in lotus position: light skin tone"
+ },
+ {
+ "no": 935,
+ "code": "1F9D8 1F3FC 200D 2642 FE0F",
+ "char": "🧘🏼‍♂️",
+ "name": "⊛ man in lotus position: medium-light skin tone"
+ },
+ {
+ "no": 936,
+ "code": "1F9D8 1F3FD 200D 2642 FE0F",
+ "char": "🧘🏽‍♂️",
+ "name": "⊛ man in lotus position: medium skin tone"
+ },
+ {
+ "no": 937,
+ "code": "1F9D8 1F3FE 200D 2642 FE0F",
+ "char": "🧘🏾‍♂️",
+ "name": "⊛ man in lotus position: medium-dark skin tone"
+ },
+ {
+ "no": 938,
+ "code": "1F9D8 1F3FF 200D 2642 FE0F",
+ "char": "🧘🏿‍♂️",
+ "name": "⊛ man in lotus position: dark skin tone"
+ },
+ {
+ "no": 939,
+ "code": "1F6C0",
+ "char": "🛀",
+ "name": "person taking bath"
+ },
+ {
+ "no": 940,
+ "code": "1F6C0 1F3FB",
+ "char": "🛀🏻",
+ "name": "person taking bath: light skin tone"
+ },
+ {
+ "no": 941,
+ "code": "1F6C0 1F3FC",
+ "char": "🛀🏼",
+ "name": "person taking bath: medium-light skin tone"
+ },
+ {
+ "no": 942,
+ "code": "1F6C0 1F3FD",
+ "char": "🛀🏽",
+ "name": "person taking bath: medium skin tone"
+ },
+ {
+ "no": 943,
+ "code": "1F6C0 1F3FE",
+ "char": "🛀🏾",
+ "name": "person taking bath: medium-dark skin tone"
+ },
+ {
+ "no": 944,
+ "code": "1F6C0 1F3FF",
+ "char": "🛀🏿",
+ "name": "person taking bath: dark skin tone"
+ },
+ {
+ "no": 945,
+ "code": "1F6CC",
+ "char": "🛌",
+ "name": "person in bed"
+ },
+ {
+ "no": 946,
+ "code": "1F6CC 1F3FB",
+ "char": "🛌🏻",
+ "name": "person in bed: light skin tone"
+ },
+ {
+ "no": 947,
+ "code": "1F6CC 1F3FC",
+ "char": "🛌🏼",
+ "name": "person in bed: medium-light skin tone"
+ },
+ {
+ "no": 948,
+ "code": "1F6CC 1F3FD",
+ "char": "🛌🏽",
+ "name": "person in bed: medium skin tone"
+ },
+ {
+ "no": 949,
+ "code": "1F6CC 1F3FE",
+ "char": "🛌🏾",
+ "name": "person in bed: medium-dark skin tone"
+ },
+ {
+ "no": 950,
+ "code": "1F6CC 1F3FF",
+ "char": "🛌🏿",
+ "name": "person in bed: dark skin tone"
+ },
+ {
+ "no": 951,
+ "code": "1F574",
+ "char": "🕴",
+ "name": "man in suit levitating"
+ },
+ {
+ "no": 952,
+ "code": "1F574 1F3FB",
+ "char": "🕴🏻",
+ "name": "man in suit levitating: light skin tone"
+ },
+ {
+ "no": 953,
+ "code": "1F574 1F3FC",
+ "char": "🕴🏼",
+ "name": "man in suit levitating: medium-light skin tone"
+ },
+ {
+ "no": 954,
+ "code": "1F574 1F3FD",
+ "char": "🕴🏽",
+ "name": "man in suit levitating: medium skin tone"
+ },
+ {
+ "no": 955,
+ "code": "1F574 1F3FE",
+ "char": "🕴🏾",
+ "name": "man in suit levitating: medium-dark skin tone"
+ },
+ {
+ "no": 956,
+ "code": "1F574 1F3FF",
+ "char": "🕴🏿",
+ "name": "man in suit levitating: dark skin tone"
+ },
+ {
+ "no": 957,
+ "code": "1F5E3",
+ "char": "🗣",
+ "name": "speaking head"
+ },
+ {
+ "no": 958,
+ "code": "1F464",
+ "char": "👤",
+ "name": "bust in silhouette"
+ },
+ {
+ "no": 959,
+ "code": "1F465",
+ "char": "👥",
+ "name": "busts in silhouette"
+ },
+ {
+ "no": 960,
+ "code": "1F93A",
+ "char": "🤺",
+ "name": "person fencing"
+ },
+ {
+ "no": 961,
+ "code": "1F3C7",
+ "char": "🏇",
+ "name": "horse racing"
+ },
+ {
+ "no": 962,
+ "code": "1F3C7 1F3FB",
+ "char": "🏇🏻",
+ "name": "horse racing: light skin tone"
+ },
+ {
+ "no": 963,
+ "code": "1F3C7 1F3FC",
+ "char": "🏇🏼",
+ "name": "horse racing: medium-light skin tone"
+ },
+ {
+ "no": 964,
+ "code": "1F3C7 1F3FD",
+ "char": "🏇🏽",
+ "name": "horse racing: medium skin tone"
+ },
+ {
+ "no": 965,
+ "code": "1F3C7 1F3FE",
+ "char": "🏇🏾",
+ "name": "horse racing: medium-dark skin tone"
+ },
+ {
+ "no": 966,
+ "code": "1F3C7 1F3FF",
+ "char": "🏇🏿",
+ "name": "horse racing: dark skin tone"
+ },
+ {
+ "no": 967,
+ "code": "26F7",
+ "char": "⛷",
+ "name": "skier"
+ },
+ {
+ "no": 968,
+ "code": "1F3C2",
+ "char": "🏂",
+ "name": "snowboarder"
+ },
+ {
+ "no": 969,
+ "code": "1F3C2 1F3FB",
+ "char": "🏂🏻",
+ "name": "snowboarder: light skin tone"
+ },
+ {
+ "no": 970,
+ "code": "1F3C2 1F3FC",
+ "char": "🏂🏼",
+ "name": "snowboarder: medium-light skin tone"
+ },
+ {
+ "no": 971,
+ "code": "1F3C2 1F3FD",
+ "char": "🏂🏽",
+ "name": "snowboarder: medium skin tone"
+ },
+ {
+ "no": 972,
+ "code": "1F3C2 1F3FE",
+ "char": "🏂🏾",
+ "name": "snowboarder: medium-dark skin tone"
+ },
+ {
+ "no": 973,
+ "code": "1F3C2 1F3FF",
+ "char": "🏂🏿",
+ "name": "snowboarder: dark skin tone"
+ },
+ {
+ "no": 974,
+ "code": "1F3CC",
+ "char": "🏌",
+ "name": "person golfing"
+ },
+ {
+ "no": 975,
+ "code": "1F3CC 1F3FB",
+ "char": "🏌🏻",
+ "name": "person golfing: light skin tone"
+ },
+ {
+ "no": 976,
+ "code": "1F3CC 1F3FC",
+ "char": "🏌🏼",
+ "name": "person golfing: medium-light skin tone"
+ },
+ {
+ "no": 977,
+ "code": "1F3CC 1F3FD",
+ "char": "🏌🏽",
+ "name": "person golfing: medium skin tone"
+ },
+ {
+ "no": 978,
+ "code": "1F3CC 1F3FE",
+ "char": "🏌🏾",
+ "name": "person golfing: medium-dark skin tone"
+ },
+ {
+ "no": 979,
+ "code": "1F3CC 1F3FF",
+ "char": "🏌🏿",
+ "name": "person golfing: dark skin tone"
+ },
+ {
+ "no": 980,
+ "code": "1F3CC FE0F 200D 2642 FE0F",
+ "char": "🏌️‍♂️",
+ "name": "man golfing"
+ },
+ {
+ "no": 981,
+ "code": "1F3CC 1F3FB 200D 2642 FE0F",
+ "char": "🏌🏻‍♂️",
+ "name": "man golfing: light skin tone"
+ },
+ {
+ "no": 982,
+ "code": "1F3CC 1F3FC 200D 2642 FE0F",
+ "char": "🏌🏼‍♂️",
+ "name": "man golfing: medium-light skin tone"
+ },
+ {
+ "no": 983,
+ "code": "1F3CC 1F3FD 200D 2642 FE0F",
+ "char": "🏌🏽‍♂️",
+ "name": "man golfing: medium skin tone"
+ },
+ {
+ "no": 984,
+ "code": "1F3CC 1F3FE 200D 2642 FE0F",
+ "char": "🏌🏾‍♂️",
+ "name": "man golfing: medium-dark skin tone"
+ },
+ {
+ "no": 985,
+ "code": "1F3CC 1F3FF 200D 2642 FE0F",
+ "char": "🏌🏿‍♂️",
+ "name": "man golfing: dark skin tone"
+ },
+ {
+ "no": 986,
+ "code": "1F3CC FE0F 200D 2640 FE0F",
+ "char": "🏌️‍♀️",
+ "name": "woman golfing"
+ },
+ {
+ "no": 987,
+ "code": "1F3CC 1F3FB 200D 2640 FE0F",
+ "char": "🏌🏻‍♀️",
+ "name": "woman golfing: light skin tone"
+ },
+ {
+ "no": 988,
+ "code": "1F3CC 1F3FC 200D 2640 FE0F",
+ "char": "🏌🏼‍♀️",
+ "name": "woman golfing: medium-light skin tone"
+ },
+ {
+ "no": 989,
+ "code": "1F3CC 1F3FD 200D 2640 FE0F",
+ "char": "🏌🏽‍♀️",
+ "name": "woman golfing: medium skin tone"
+ },
+ {
+ "no": 990,
+ "code": "1F3CC 1F3FE 200D 2640 FE0F",
+ "char": "🏌🏾‍♀️",
+ "name": "woman golfing: medium-dark skin tone"
+ },
+ {
+ "no": 991,
+ "code": "1F3CC 1F3FF 200D 2640 FE0F",
+ "char": "🏌🏿‍♀️",
+ "name": "woman golfing: dark skin tone"
+ },
+ {
+ "no": 992,
+ "code": "1F3C4",
+ "char": "🏄",
+ "name": "person surfing"
+ },
+ {
+ "no": 993,
+ "code": "1F3C4 1F3FB",
+ "char": "🏄🏻",
+ "name": "person surfing: light skin tone"
+ },
+ {
+ "no": 994,
+ "code": "1F3C4 1F3FC",
+ "char": "🏄🏼",
+ "name": "person surfing: medium-light skin tone"
+ },
+ {
+ "no": 995,
+ "code": "1F3C4 1F3FD",
+ "char": "🏄🏽",
+ "name": "person surfing: medium skin tone"
+ },
+ {
+ "no": 996,
+ "code": "1F3C4 1F3FE",
+ "char": "🏄🏾",
+ "name": "person surfing: medium-dark skin tone"
+ },
+ {
+ "no": 997,
+ "code": "1F3C4 1F3FF",
+ "char": "🏄🏿",
+ "name": "person surfing: dark skin tone"
+ },
+ {
+ "no": 998,
+ "code": "1F3C4 200D 2642 FE0F",
+ "char": "🏄‍♂️",
+ "name": "man surfing"
+ },
+ {
+ "no": 999,
+ "code": "1F3C4 1F3FB 200D 2642 FE0F",
+ "char": "🏄🏻‍♂️",
+ "name": "man surfing: light skin tone"
+ },
+ {
+ "no": 1000,
+ "code": "1F3C4 1F3FC 200D 2642 FE0F",
+ "char": "🏄🏼‍♂️",
+ "name": "man surfing: medium-light skin tone"
+ },
+ {
+ "no": 1001,
+ "code": "1F3C4 1F3FD 200D 2642 FE0F",
+ "char": "🏄🏽‍♂️",
+ "name": "man surfing: medium skin tone"
+ },
+ {
+ "no": 1002,
+ "code": "1F3C4 1F3FE 200D 2642 FE0F",
+ "char": "🏄🏾‍♂️",
+ "name": "man surfing: medium-dark skin tone"
+ },
+ {
+ "no": 1003,
+ "code": "1F3C4 1F3FF 200D 2642 FE0F",
+ "char": "🏄🏿‍♂️",
+ "name": "man surfing: dark skin tone"
+ },
+ {
+ "no": 1004,
+ "code": "1F3C4 200D 2640 FE0F",
+ "char": "🏄‍♀️",
+ "name": "woman surfing"
+ },
+ {
+ "no": 1005,
+ "code": "1F3C4 1F3FB 200D 2640 FE0F",
+ "char": "🏄🏻‍♀️",
+ "name": "woman surfing: light skin tone"
+ },
+ {
+ "no": 1006,
+ "code": "1F3C4 1F3FC 200D 2640 FE0F",
+ "char": "🏄🏼‍♀️",
+ "name": "woman surfing: medium-light skin tone"
+ },
+ {
+ "no": 1007,
+ "code": "1F3C4 1F3FD 200D 2640 FE0F",
+ "char": "🏄🏽‍♀️",
+ "name": "woman surfing: medium skin tone"
+ },
+ {
+ "no": 1008,
+ "code": "1F3C4 1F3FE 200D 2640 FE0F",
+ "char": "🏄🏾‍♀️",
+ "name": "woman surfing: medium-dark skin tone"
+ },
+ {
+ "no": 1009,
+ "code": "1F3C4 1F3FF 200D 2640 FE0F",
+ "char": "🏄🏿‍♀️",
+ "name": "woman surfing: dark skin tone"
+ },
+ {
+ "no": 1010,
+ "code": "1F6A3",
+ "char": "🚣",
+ "name": "person rowing boat"
+ },
+ {
+ "no": 1011,
+ "code": "1F6A3 1F3FB",
+ "char": "🚣🏻",
+ "name": "person rowing boat: light skin tone"
+ },
+ {
+ "no": 1012,
+ "code": "1F6A3 1F3FC",
+ "char": "🚣🏼",
+ "name": "person rowing boat: medium-light skin tone"
+ },
+ {
+ "no": 1013,
+ "code": "1F6A3 1F3FD",
+ "char": "🚣🏽",
+ "name": "person rowing boat: medium skin tone"
+ },
+ {
+ "no": 1014,
+ "code": "1F6A3 1F3FE",
+ "char": "🚣🏾",
+ "name": "person rowing boat: medium-dark skin tone"
+ },
+ {
+ "no": 1015,
+ "code": "1F6A3 1F3FF",
+ "char": "🚣🏿",
+ "name": "person rowing boat: dark skin tone"
+ },
+ {
+ "no": 1016,
+ "code": "1F6A3 200D 2642 FE0F",
+ "char": "🚣‍♂️",
+ "name": "man rowing boat"
+ },
+ {
+ "no": 1017,
+ "code": "1F6A3 1F3FB 200D 2642 FE0F",
+ "char": "🚣🏻‍♂️",
+ "name": "man rowing boat: light skin tone"
+ },
+ {
+ "no": 1018,
+ "code": "1F6A3 1F3FC 200D 2642 FE0F",
+ "char": "🚣🏼‍♂️",
+ "name": "man rowing boat: medium-light skin tone"
+ },
+ {
+ "no": 1019,
+ "code": "1F6A3 1F3FD 200D 2642 FE0F",
+ "char": "🚣🏽‍♂️",
+ "name": "man rowing boat: medium skin tone"
+ },
+ {
+ "no": 1020,
+ "code": "1F6A3 1F3FE 200D 2642 FE0F",
+ "char": "🚣🏾‍♂️",
+ "name": "man rowing boat: medium-dark skin tone"
+ },
+ {
+ "no": 1021,
+ "code": "1F6A3 1F3FF 200D 2642 FE0F",
+ "char": "🚣🏿‍♂️",
+ "name": "man rowing boat: dark skin tone"
+ },
+ {
+ "no": 1022,
+ "code": "1F6A3 200D 2640 FE0F",
+ "char": "🚣‍♀️",
+ "name": "woman rowing boat"
+ },
+ {
+ "no": 1023,
+ "code": "1F6A3 1F3FB 200D 2640 FE0F",
+ "char": "🚣🏻‍♀️",
+ "name": "woman rowing boat: light skin tone"
+ },
+ {
+ "no": 1024,
+ "code": "1F6A3 1F3FC 200D 2640 FE0F",
+ "char": "🚣🏼‍♀️",
+ "name": "woman rowing boat: medium-light skin tone"
+ },
+ {
+ "no": 1025,
+ "code": "1F6A3 1F3FD 200D 2640 FE0F",
+ "char": "🚣🏽‍♀️",
+ "name": "woman rowing boat: medium skin tone"
+ },
+ {
+ "no": 1026,
+ "code": "1F6A3 1F3FE 200D 2640 FE0F",
+ "char": "🚣🏾‍♀️",
+ "name": "woman rowing boat: medium-dark skin tone"
+ },
+ {
+ "no": 1027,
+ "code": "1F6A3 1F3FF 200D 2640 FE0F",
+ "char": "🚣🏿‍♀️",
+ "name": "woman rowing boat: dark skin tone"
+ },
+ {
+ "no": 1028,
+ "code": "1F3CA",
+ "char": "🏊",
+ "name": "person swimming"
+ },
+ {
+ "no": 1029,
+ "code": "1F3CA 1F3FB",
+ "char": "🏊🏻",
+ "name": "person swimming: light skin tone"
+ },
+ {
+ "no": 1030,
+ "code": "1F3CA 1F3FC",
+ "char": "🏊🏼",
+ "name": "person swimming: medium-light skin tone"
+ },
+ {
+ "no": 1031,
+ "code": "1F3CA 1F3FD",
+ "char": "🏊🏽",
+ "name": "person swimming: medium skin tone"
+ },
+ {
+ "no": 1032,
+ "code": "1F3CA 1F3FE",
+ "char": "🏊🏾",
+ "name": "person swimming: medium-dark skin tone"
+ },
+ {
+ "no": 1033,
+ "code": "1F3CA 1F3FF",
+ "char": "🏊🏿",
+ "name": "person swimming: dark skin tone"
+ },
+ {
+ "no": 1034,
+ "code": "1F3CA 200D 2642 FE0F",
+ "char": "🏊‍♂️",
+ "name": "man swimming"
+ },
+ {
+ "no": 1035,
+ "code": "1F3CA 1F3FB 200D 2642 FE0F",
+ "char": "🏊🏻‍♂️",
+ "name": "man swimming: light skin tone"
+ },
+ {
+ "no": 1036,
+ "code": "1F3CA 1F3FC 200D 2642 FE0F",
+ "char": "🏊🏼‍♂️",
+ "name": "man swimming: medium-light skin tone"
+ },
+ {
+ "no": 1037,
+ "code": "1F3CA 1F3FD 200D 2642 FE0F",
+ "char": "🏊🏽‍♂️",
+ "name": "man swimming: medium skin tone"
+ },
+ {
+ "no": 1038,
+ "code": "1F3CA 1F3FE 200D 2642 FE0F",
+ "char": "🏊🏾‍♂️",
+ "name": "man swimming: medium-dark skin tone"
+ },
+ {
+ "no": 1039,
+ "code": "1F3CA 1F3FF 200D 2642 FE0F",
+ "char": "🏊🏿‍♂️",
+ "name": "man swimming: dark skin tone"
+ },
+ {
+ "no": 1040,
+ "code": "1F3CA 200D 2640 FE0F",
+ "char": "🏊‍♀️",
+ "name": "woman swimming"
+ },
+ {
+ "no": 1041,
+ "code": "1F3CA 1F3FB 200D 2640 FE0F",
+ "char": "🏊🏻‍♀️",
+ "name": "woman swimming: light skin tone"
+ },
+ {
+ "no": 1042,
+ "code": "1F3CA 1F3FC 200D 2640 FE0F",
+ "char": "🏊🏼‍♀️",
+ "name": "woman swimming: medium-light skin tone"
+ },
+ {
+ "no": 1043,
+ "code": "1F3CA 1F3FD 200D 2640 FE0F",
+ "char": "🏊🏽‍♀️",
+ "name": "woman swimming: medium skin tone"
+ },
+ {
+ "no": 1044,
+ "code": "1F3CA 1F3FE 200D 2640 FE0F",
+ "char": "🏊🏾‍♀️",
+ "name": "woman swimming: medium-dark skin tone"
+ },
+ {
+ "no": 1045,
+ "code": "1F3CA 1F3FF 200D 2640 FE0F",
+ "char": "🏊🏿‍♀️",
+ "name": "woman swimming: dark skin tone"
+ },
+ {
+ "no": 1046,
+ "code": "26F9",
+ "char": "⛹",
+ "name": "person bouncing ball"
+ },
+ {
+ "no": 1047,
+ "code": "26F9 1F3FB",
+ "char": "⛹🏻",
+ "name": "person bouncing ball: light skin tone"
+ },
+ {
+ "no": 1048,
+ "code": "26F9 1F3FC",
+ "char": "⛹🏼",
+ "name": "person bouncing ball: medium-light skin tone"
+ },
+ {
+ "no": 1049,
+ "code": "26F9 1F3FD",
+ "char": "⛹🏽",
+ "name": "person bouncing ball: medium skin tone"
+ },
+ {
+ "no": 1050,
+ "code": "26F9 1F3FE",
+ "char": "⛹🏾",
+ "name": "person bouncing ball: medium-dark skin tone"
+ },
+ {
+ "no": 1051,
+ "code": "26F9 1F3FF",
+ "char": "⛹🏿",
+ "name": "person bouncing ball: dark skin tone"
+ },
+ {
+ "no": 1052,
+ "code": "26F9 FE0F 200D 2642 FE0F",
+ "char": "⛹️‍♂️",
+ "name": "man bouncing ball"
+ },
+ {
+ "no": 1053,
+ "code": "26F9 1F3FB 200D 2642 FE0F",
+ "char": "⛹🏻‍♂️",
+ "name": "man bouncing ball: light skin tone"
+ },
+ {
+ "no": 1054,
+ "code": "26F9 1F3FC 200D 2642 FE0F",
+ "char": "⛹🏼‍♂️",
+ "name": "man bouncing ball: medium-light skin tone"
+ },
+ {
+ "no": 1055,
+ "code": "26F9 1F3FD 200D 2642 FE0F",
+ "char": "⛹🏽‍♂️",
+ "name": "man bouncing ball: medium skin tone"
+ },
+ {
+ "no": 1056,
+ "code": "26F9 1F3FE 200D 2642 FE0F",
+ "char": "⛹🏾‍♂️",
+ "name": "man bouncing ball: medium-dark skin tone"
+ },
+ {
+ "no": 1057,
+ "code": "26F9 1F3FF 200D 2642 FE0F",
+ "char": "⛹🏿‍♂️",
+ "name": "man bouncing ball: dark skin tone"
+ },
+ {
+ "no": 1058,
+ "code": "26F9 FE0F 200D 2640 FE0F",
+ "char": "⛹️‍♀️",
+ "name": "woman bouncing ball"
+ },
+ {
+ "no": 1059,
+ "code": "26F9 1F3FB 200D 2640 FE0F",
+ "char": "⛹🏻‍♀️",
+ "name": "woman bouncing ball: light skin tone"
+ },
+ {
+ "no": 1060,
+ "code": "26F9 1F3FC 200D 2640 FE0F",
+ "char": "⛹🏼‍♀️",
+ "name": "woman bouncing ball: medium-light skin tone"
+ },
+ {
+ "no": 1061,
+ "code": "26F9 1F3FD 200D 2640 FE0F",
+ "char": "⛹🏽‍♀️",
+ "name": "woman bouncing ball: medium skin tone"
+ },
+ {
+ "no": 1062,
+ "code": "26F9 1F3FE 200D 2640 FE0F",
+ "char": "⛹🏾‍♀️",
+ "name": "woman bouncing ball: medium-dark skin tone"
+ },
+ {
+ "no": 1063,
+ "code": "26F9 1F3FF 200D 2640 FE0F",
+ "char": "⛹🏿‍♀️",
+ "name": "woman bouncing ball: dark skin tone"
+ },
+ {
+ "no": 1064,
+ "code": "1F3CB",
+ "char": "🏋",
+ "name": "person lifting weights"
+ },
+ {
+ "no": 1065,
+ "code": "1F3CB 1F3FB",
+ "char": "🏋🏻",
+ "name": "person lifting weights: light skin tone"
+ },
+ {
+ "no": 1066,
+ "code": "1F3CB 1F3FC",
+ "char": "🏋🏼",
+ "name": "person lifting weights: medium-light skin tone"
+ },
+ {
+ "no": 1067,
+ "code": "1F3CB 1F3FD",
+ "char": "🏋🏽",
+ "name": "person lifting weights: medium skin tone"
+ },
+ {
+ "no": 1068,
+ "code": "1F3CB 1F3FE",
+ "char": "🏋🏾",
+ "name": "person lifting weights: medium-dark skin tone"
+ },
+ {
+ "no": 1069,
+ "code": "1F3CB 1F3FF",
+ "char": "🏋🏿",
+ "name": "person lifting weights: dark skin tone"
+ },
+ {
+ "no": 1070,
+ "code": "1F3CB FE0F 200D 2642 FE0F",
+ "char": "🏋️‍♂️",
+ "name": "man lifting weights"
+ },
+ {
+ "no": 1071,
+ "code": "1F3CB 1F3FB 200D 2642 FE0F",
+ "char": "🏋🏻‍♂️",
+ "name": "man lifting weights: light skin tone"
+ },
+ {
+ "no": 1072,
+ "code": "1F3CB 1F3FC 200D 2642 FE0F",
+ "char": "🏋🏼‍♂️",
+ "name": "man lifting weights: medium-light skin tone"
+ },
+ {
+ "no": 1073,
+ "code": "1F3CB 1F3FD 200D 2642 FE0F",
+ "char": "🏋🏽‍♂️",
+ "name": "man lifting weights: medium skin tone"
+ },
+ {
+ "no": 1074,
+ "code": "1F3CB 1F3FE 200D 2642 FE0F",
+ "char": "🏋🏾‍♂️",
+ "name": "man lifting weights: medium-dark skin tone"
+ },
+ {
+ "no": 1075,
+ "code": "1F3CB 1F3FF 200D 2642 FE0F",
+ "char": "🏋🏿‍♂️",
+ "name": "man lifting weights: dark skin tone"
+ },
+ {
+ "no": 1076,
+ "code": "1F3CB FE0F 200D 2640 FE0F",
+ "char": "🏋️‍♀️",
+ "name": "woman lifting weights"
+ },
+ {
+ "no": 1077,
+ "code": "1F3CB 1F3FB 200D 2640 FE0F",
+ "char": "🏋🏻‍♀️",
+ "name": "woman lifting weights: light skin tone"
+ },
+ {
+ "no": 1078,
+ "code": "1F3CB 1F3FC 200D 2640 FE0F",
+ "char": "🏋🏼‍♀️",
+ "name": "woman lifting weights: medium-light skin tone"
+ },
+ {
+ "no": 1079,
+ "code": "1F3CB 1F3FD 200D 2640 FE0F",
+ "char": "🏋🏽‍♀️",
+ "name": "woman lifting weights: medium skin tone"
+ },
+ {
+ "no": 1080,
+ "code": "1F3CB 1F3FE 200D 2640 FE0F",
+ "char": "🏋🏾‍♀️",
+ "name": "woman lifting weights: medium-dark skin tone"
+ },
+ {
+ "no": 1081,
+ "code": "1F3CB 1F3FF 200D 2640 FE0F",
+ "char": "🏋🏿‍♀️",
+ "name": "woman lifting weights: dark skin tone"
+ },
+ {
+ "no": 1082,
+ "code": "1F6B4",
+ "char": "🚴",
+ "name": "person biking"
+ },
+ {
+ "no": 1083,
+ "code": "1F6B4 1F3FB",
+ "char": "🚴🏻",
+ "name": "person biking: light skin tone"
+ },
+ {
+ "no": 1084,
+ "code": "1F6B4 1F3FC",
+ "char": "🚴🏼",
+ "name": "person biking: medium-light skin tone"
+ },
+ {
+ "no": 1085,
+ "code": "1F6B4 1F3FD",
+ "char": "🚴🏽",
+ "name": "person biking: medium skin tone"
+ },
+ {
+ "no": 1086,
+ "code": "1F6B4 1F3FE",
+ "char": "🚴🏾",
+ "name": "person biking: medium-dark skin tone"
+ },
+ {
+ "no": 1087,
+ "code": "1F6B4 1F3FF",
+ "char": "🚴🏿",
+ "name": "person biking: dark skin tone"
+ },
+ {
+ "no": 1088,
+ "code": "1F6B4 200D 2642 FE0F",
+ "char": "🚴‍♂️",
+ "name": "man biking"
+ },
+ {
+ "no": 1089,
+ "code": "1F6B4 1F3FB 200D 2642 FE0F",
+ "char": "🚴🏻‍♂️",
+ "name": "man biking: light skin tone"
+ },
+ {
+ "no": 1090,
+ "code": "1F6B4 1F3FC 200D 2642 FE0F",
+ "char": "🚴🏼‍♂️",
+ "name": "man biking: medium-light skin tone"
+ },
+ {
+ "no": 1091,
+ "code": "1F6B4 1F3FD 200D 2642 FE0F",
+ "char": "🚴🏽‍♂️",
+ "name": "man biking: medium skin tone"
+ },
+ {
+ "no": 1092,
+ "code": "1F6B4 1F3FE 200D 2642 FE0F",
+ "char": "🚴🏾‍♂️",
+ "name": "man biking: medium-dark skin tone"
+ },
+ {
+ "no": 1093,
+ "code": "1F6B4 1F3FF 200D 2642 FE0F",
+ "char": "🚴🏿‍♂️",
+ "name": "man biking: dark skin tone"
+ },
+ {
+ "no": 1094,
+ "code": "1F6B4 200D 2640 FE0F",
+ "char": "🚴‍♀️",
+ "name": "woman biking"
+ },
+ {
+ "no": 1095,
+ "code": "1F6B4 1F3FB 200D 2640 FE0F",
+ "char": "🚴🏻‍♀️",
+ "name": "woman biking: light skin tone"
+ },
+ {
+ "no": 1096,
+ "code": "1F6B4 1F3FC 200D 2640 FE0F",
+ "char": "🚴🏼‍♀️",
+ "name": "woman biking: medium-light skin tone"
+ },
+ {
+ "no": 1097,
+ "code": "1F6B4 1F3FD 200D 2640 FE0F",
+ "char": "🚴🏽‍♀️",
+ "name": "woman biking: medium skin tone"
+ },
+ {
+ "no": 1098,
+ "code": "1F6B4 1F3FE 200D 2640 FE0F",
+ "char": "🚴🏾‍♀️",
+ "name": "woman biking: medium-dark skin tone"
+ },
+ {
+ "no": 1099,
+ "code": "1F6B4 1F3FF 200D 2640 FE0F",
+ "char": "🚴🏿‍♀️",
+ "name": "woman biking: dark skin tone"
+ },
+ {
+ "no": 1100,
+ "code": "1F6B5",
+ "char": "🚵",
+ "name": "person mountain biking"
+ },
+ {
+ "no": 1101,
+ "code": "1F6B5 1F3FB",
+ "char": "🚵🏻",
+ "name": "person mountain biking: light skin tone"
+ },
+ {
+ "no": 1102,
+ "code": "1F6B5 1F3FC",
+ "char": "🚵🏼",
+ "name": "person mountain biking: medium-light skin tone"
+ },
+ {
+ "no": 1103,
+ "code": "1F6B5 1F3FD",
+ "char": "🚵🏽",
+ "name": "person mountain biking: medium skin tone"
+ },
+ {
+ "no": 1104,
+ "code": "1F6B5 1F3FE",
+ "char": "🚵🏾",
+ "name": "person mountain biking: medium-dark skin tone"
+ },
+ {
+ "no": 1105,
+ "code": "1F6B5 1F3FF",
+ "char": "🚵🏿",
+ "name": "person mountain biking: dark skin tone"
+ },
+ {
+ "no": 1106,
+ "code": "1F6B5 200D 2642 FE0F",
+ "char": "🚵‍♂️",
+ "name": "man mountain biking"
+ },
+ {
+ "no": 1107,
+ "code": "1F6B5 1F3FB 200D 2642 FE0F",
+ "char": "🚵🏻‍♂️",
+ "name": "man mountain biking: light skin tone"
+ },
+ {
+ "no": 1108,
+ "code": "1F6B5 1F3FC 200D 2642 FE0F",
+ "char": "🚵🏼‍♂️",
+ "name": "man mountain biking: medium-light skin tone"
+ },
+ {
+ "no": 1109,
+ "code": "1F6B5 1F3FD 200D 2642 FE0F",
+ "char": "🚵🏽‍♂️",
+ "name": "man mountain biking: medium skin tone"
+ },
+ {
+ "no": 1110,
+ "code": "1F6B5 1F3FE 200D 2642 FE0F",
+ "char": "🚵🏾‍♂️",
+ "name": "man mountain biking: medium-dark skin tone"
+ },
+ {
+ "no": 1111,
+ "code": "1F6B5 1F3FF 200D 2642 FE0F",
+ "char": "🚵🏿‍♂️",
+ "name": "man mountain biking: dark skin tone"
+ },
+ {
+ "no": 1112,
+ "code": "1F6B5 200D 2640 FE0F",
+ "char": "🚵‍♀️",
+ "name": "woman mountain biking"
+ },
+ {
+ "no": 1113,
+ "code": "1F6B5 1F3FB 200D 2640 FE0F",
+ "char": "🚵🏻‍♀️",
+ "name": "woman mountain biking: light skin tone"
+ },
+ {
+ "no": 1114,
+ "code": "1F6B5 1F3FC 200D 2640 FE0F",
+ "char": "🚵🏼‍♀️",
+ "name": "woman mountain biking: medium-light skin tone"
+ },
+ {
+ "no": 1115,
+ "code": "1F6B5 1F3FD 200D 2640 FE0F",
+ "char": "🚵🏽‍♀️",
+ "name": "woman mountain biking: medium skin tone"
+ },
+ {
+ "no": 1116,
+ "code": "1F6B5 1F3FE 200D 2640 FE0F",
+ "char": "🚵🏾‍♀️",
+ "name": "woman mountain biking: medium-dark skin tone"
+ },
+ {
+ "no": 1117,
+ "code": "1F6B5 1F3FF 200D 2640 FE0F",
+ "char": "🚵🏿‍♀️",
+ "name": "woman mountain biking: dark skin tone"
+ },
+ {
+ "no": 1118,
+ "code": "1F3CE",
+ "char": "🏎",
+ "name": "racing car"
+ },
+ {
+ "no": 1119,
+ "code": "1F3CD",
+ "char": "🏍",
+ "name": "motorcycle"
+ },
+ {
+ "no": 1120,
+ "code": "1F938",
+ "char": "🤸",
+ "name": "person cartwheeling"
+ },
+ {
+ "no": 1121,
+ "code": "1F938 1F3FB",
+ "char": "🤸🏻",
+ "name": "person cartwheeling: light skin tone"
+ },
+ {
+ "no": 1122,
+ "code": "1F938 1F3FC",
+ "char": "🤸🏼",
+ "name": "person cartwheeling: medium-light skin tone"
+ },
+ {
+ "no": 1123,
+ "code": "1F938 1F3FD",
+ "char": "🤸🏽",
+ "name": "person cartwheeling: medium skin tone"
+ },
+ {
+ "no": 1124,
+ "code": "1F938 1F3FE",
+ "char": "🤸🏾",
+ "name": "person cartwheeling: medium-dark skin tone"
+ },
+ {
+ "no": 1125,
+ "code": "1F938 1F3FF",
+ "char": "🤸🏿",
+ "name": "person cartwheeling: dark skin tone"
+ },
+ {
+ "no": 1126,
+ "code": "1F938 200D 2642 FE0F",
+ "char": "🤸‍♂️",
+ "name": "man cartwheeling"
+ },
+ {
+ "no": 1127,
+ "code": "1F938 1F3FB 200D 2642 FE0F",
+ "char": "🤸🏻‍♂️",
+ "name": "man cartwheeling: light skin tone"
+ },
+ {
+ "no": 1128,
+ "code": "1F938 1F3FC 200D 2642 FE0F",
+ "char": "🤸🏼‍♂️",
+ "name": "man cartwheeling: medium-light skin tone"
+ },
+ {
+ "no": 1129,
+ "code": "1F938 1F3FD 200D 2642 FE0F",
+ "char": "🤸🏽‍♂️",
+ "name": "man cartwheeling: medium skin tone"
+ },
+ {
+ "no": 1130,
+ "code": "1F938 1F3FE 200D 2642 FE0F",
+ "char": "🤸🏾‍♂️",
+ "name": "man cartwheeling: medium-dark skin tone"
+ },
+ {
+ "no": 1131,
+ "code": "1F938 1F3FF 200D 2642 FE0F",
+ "char": "🤸🏿‍♂️",
+ "name": "man cartwheeling: dark skin tone"
+ },
+ {
+ "no": 1132,
+ "code": "1F938 200D 2640 FE0F",
+ "char": "🤸‍♀️",
+ "name": "woman cartwheeling"
+ },
+ {
+ "no": 1133,
+ "code": "1F938 1F3FB 200D 2640 FE0F",
+ "char": "🤸🏻‍♀️",
+ "name": "woman cartwheeling: light skin tone"
+ },
+ {
+ "no": 1134,
+ "code": "1F938 1F3FC 200D 2640 FE0F",
+ "char": "🤸🏼‍♀️",
+ "name": "woman cartwheeling: medium-light skin tone"
+ },
+ {
+ "no": 1135,
+ "code": "1F938 1F3FD 200D 2640 FE0F",
+ "char": "🤸🏽‍♀️",
+ "name": "woman cartwheeling: medium skin tone"
+ },
+ {
+ "no": 1136,
+ "code": "1F938 1F3FE 200D 2640 FE0F",
+ "char": "🤸🏾‍♀️",
+ "name": "woman cartwheeling: medium-dark skin tone"
+ },
+ {
+ "no": 1137,
+ "code": "1F938 1F3FF 200D 2640 FE0F",
+ "char": "🤸🏿‍♀️",
+ "name": "woman cartwheeling: dark skin tone"
+ },
+ {
+ "no": 1138,
+ "code": "1F93C",
+ "char": "🤼",
+ "name": "people wrestling"
+ },
+ {
+ "no": 1139,
+ "code": "1F93C 200D 2642 FE0F",
+ "char": "🤼‍♂️",
+ "name": "men wrestling"
+ },
+ {
+ "no": 1140,
+ "code": "1F93C 200D 2640 FE0F",
+ "char": "🤼‍♀️",
+ "name": "women wrestling"
+ },
+ {
+ "no": 1141,
+ "code": "1F93D",
+ "char": "🤽",
+ "name": "person playing water polo"
+ },
+ {
+ "no": 1142,
+ "code": "1F93D 1F3FB",
+ "char": "🤽🏻",
+ "name": "person playing water polo: light skin tone"
+ },
+ {
+ "no": 1143,
+ "code": "1F93D 1F3FC",
+ "char": "🤽🏼",
+ "name": "person playing water polo: medium-light skin tone"
+ },
+ {
+ "no": 1144,
+ "code": "1F93D 1F3FD",
+ "char": "🤽🏽",
+ "name": "person playing water polo: medium skin tone"
+ },
+ {
+ "no": 1145,
+ "code": "1F93D 1F3FE",
+ "char": "🤽🏾",
+ "name": "person playing water polo: medium-dark skin tone"
+ },
+ {
+ "no": 1146,
+ "code": "1F93D 1F3FF",
+ "char": "🤽🏿",
+ "name": "person playing water polo: dark skin tone"
+ },
+ {
+ "no": 1147,
+ "code": "1F93D 200D 2642 FE0F",
+ "char": "🤽‍♂️",
+ "name": "man playing water polo"
+ },
+ {
+ "no": 1148,
+ "code": "1F93D 1F3FB 200D 2642 FE0F",
+ "char": "🤽🏻‍♂️",
+ "name": "man playing water polo: light skin tone"
+ },
+ {
+ "no": 1149,
+ "code": "1F93D 1F3FC 200D 2642 FE0F",
+ "char": "🤽🏼‍♂️",
+ "name": "man playing water polo: medium-light skin tone"
+ },
+ {
+ "no": 1150,
+ "code": "1F93D 1F3FD 200D 2642 FE0F",
+ "char": "🤽🏽‍♂️",
+ "name": "man playing water polo: medium skin tone"
+ },
+ {
+ "no": 1151,
+ "code": "1F93D 1F3FE 200D 2642 FE0F",
+ "char": "🤽🏾‍♂️",
+ "name": "man playing water polo: medium-dark skin tone"
+ },
+ {
+ "no": 1152,
+ "code": "1F93D 1F3FF 200D 2642 FE0F",
+ "char": "🤽🏿‍♂️",
+ "name": "man playing water polo: dark skin tone"
+ },
+ {
+ "no": 1153,
+ "code": "1F93D 200D 2640 FE0F",
+ "char": "🤽‍♀️",
+ "name": "woman playing water polo"
+ },
+ {
+ "no": 1154,
+ "code": "1F93D 1F3FB 200D 2640 FE0F",
+ "char": "🤽🏻‍♀️",
+ "name": "woman playing water polo: light skin tone"
+ },
+ {
+ "no": 1155,
+ "code": "1F93D 1F3FC 200D 2640 FE0F",
+ "char": "🤽🏼‍♀️",
+ "name": "woman playing water polo: medium-light skin tone"
+ },
+ {
+ "no": 1156,
+ "code": "1F93D 1F3FD 200D 2640 FE0F",
+ "char": "🤽🏽‍♀️",
+ "name": "woman playing water polo: medium skin tone"
+ },
+ {
+ "no": 1157,
+ "code": "1F93D 1F3FE 200D 2640 FE0F",
+ "char": "🤽🏾‍♀️",
+ "name": "woman playing water polo: medium-dark skin tone"
+ },
+ {
+ "no": 1158,
+ "code": "1F93D 1F3FF 200D 2640 FE0F",
+ "char": "🤽🏿‍♀️",
+ "name": "woman playing water polo: dark skin tone"
+ },
+ {
+ "no": 1159,
+ "code": "1F93E",
+ "char": "🤾",
+ "name": "person playing handball"
+ },
+ {
+ "no": 1160,
+ "code": "1F93E 1F3FB",
+ "char": "🤾🏻",
+ "name": "person playing handball: light skin tone"
+ },
+ {
+ "no": 1161,
+ "code": "1F93E 1F3FC",
+ "char": "🤾🏼",
+ "name": "person playing handball: medium-light skin tone"
+ },
+ {
+ "no": 1162,
+ "code": "1F93E 1F3FD",
+ "char": "🤾🏽",
+ "name": "person playing handball: medium skin tone"
+ },
+ {
+ "no": 1163,
+ "code": "1F93E 1F3FE",
+ "char": "🤾🏾",
+ "name": "person playing handball: medium-dark skin tone"
+ },
+ {
+ "no": 1164,
+ "code": "1F93E 1F3FF",
+ "char": "🤾🏿",
+ "name": "person playing handball: dark skin tone"
+ },
+ {
+ "no": 1165,
+ "code": "1F93E 200D 2642 FE0F",
+ "char": "🤾‍♂️",
+ "name": "man playing handball"
+ },
+ {
+ "no": 1166,
+ "code": "1F93E 1F3FB 200D 2642 FE0F",
+ "char": "🤾🏻‍♂️",
+ "name": "man playing handball: light skin tone"
+ },
+ {
+ "no": 1167,
+ "code": "1F93E 1F3FC 200D 2642 FE0F",
+ "char": "🤾🏼‍♂️",
+ "name": "man playing handball: medium-light skin tone"
+ },
+ {
+ "no": 1168,
+ "code": "1F93E 1F3FD 200D 2642 FE0F",
+ "char": "🤾🏽‍♂️",
+ "name": "man playing handball: medium skin tone"
+ },
+ {
+ "no": 1169,
+ "code": "1F93E 1F3FE 200D 2642 FE0F",
+ "char": "🤾🏾‍♂️",
+ "name": "man playing handball: medium-dark skin tone"
+ },
+ {
+ "no": 1170,
+ "code": "1F93E 1F3FF 200D 2642 FE0F",
+ "char": "🤾🏿‍♂️",
+ "name": "man playing handball: dark skin tone"
+ },
+ {
+ "no": 1171,
+ "code": "1F93E 200D 2640 FE0F",
+ "char": "🤾‍♀️",
+ "name": "woman playing handball"
+ },
+ {
+ "no": 1172,
+ "code": "1F93E 1F3FB 200D 2640 FE0F",
+ "char": "🤾🏻‍♀️",
+ "name": "woman playing handball: light skin tone"
+ },
+ {
+ "no": 1173,
+ "code": "1F93E 1F3FC 200D 2640 FE0F",
+ "char": "🤾🏼‍♀️",
+ "name": "woman playing handball: medium-light skin tone"
+ },
+ {
+ "no": 1174,
+ "code": "1F93E 1F3FD 200D 2640 FE0F",
+ "char": "🤾🏽‍♀️",
+ "name": "woman playing handball: medium skin tone"
+ },
+ {
+ "no": 1175,
+ "code": "1F93E 1F3FE 200D 2640 FE0F",
+ "char": "🤾🏾‍♀️",
+ "name": "woman playing handball: medium-dark skin tone"
+ },
+ {
+ "no": 1176,
+ "code": "1F93E 1F3FF 200D 2640 FE0F",
+ "char": "🤾🏿‍♀️",
+ "name": "woman playing handball: dark skin tone"
+ },
+ {
+ "no": 1177,
+ "code": "1F939",
+ "char": "🤹",
+ "name": "person juggling"
+ },
+ {
+ "no": 1178,
+ "code": "1F939 1F3FB",
+ "char": "🤹🏻",
+ "name": "person juggling: light skin tone"
+ },
+ {
+ "no": 1179,
+ "code": "1F939 1F3FC",
+ "char": "🤹🏼",
+ "name": "person juggling: medium-light skin tone"
+ },
+ {
+ "no": 1180,
+ "code": "1F939 1F3FD",
+ "char": "🤹🏽",
+ "name": "person juggling: medium skin tone"
+ },
+ {
+ "no": 1181,
+ "code": "1F939 1F3FE",
+ "char": "🤹🏾",
+ "name": "person juggling: medium-dark skin tone"
+ },
+ {
+ "no": 1182,
+ "code": "1F939 1F3FF",
+ "char": "🤹🏿",
+ "name": "person juggling: dark skin tone"
+ },
+ {
+ "no": 1183,
+ "code": "1F939 200D 2642 FE0F",
+ "char": "🤹‍♂️",
+ "name": "man juggling"
+ },
+ {
+ "no": 1184,
+ "code": "1F939 1F3FB 200D 2642 FE0F",
+ "char": "🤹🏻‍♂️",
+ "name": "man juggling: light skin tone"
+ },
+ {
+ "no": 1185,
+ "code": "1F939 1F3FC 200D 2642 FE0F",
+ "char": "🤹🏼‍♂️",
+ "name": "man juggling: medium-light skin tone"
+ },
+ {
+ "no": 1186,
+ "code": "1F939 1F3FD 200D 2642 FE0F",
+ "char": "🤹🏽‍♂️",
+ "name": "man juggling: medium skin tone"
+ },
+ {
+ "no": 1187,
+ "code": "1F939 1F3FE 200D 2642 FE0F",
+ "char": "🤹🏾‍♂️",
+ "name": "man juggling: medium-dark skin tone"
+ },
+ {
+ "no": 1188,
+ "code": "1F939 1F3FF 200D 2642 FE0F",
+ "char": "🤹🏿‍♂️",
+ "name": "man juggling: dark skin tone"
+ },
+ {
+ "no": 1189,
+ "code": "1F939 200D 2640 FE0F",
+ "char": "🤹‍♀️",
+ "name": "woman juggling"
+ },
+ {
+ "no": 1190,
+ "code": "1F939 1F3FB 200D 2640 FE0F",
+ "char": "🤹🏻‍♀️",
+ "name": "woman juggling: light skin tone"
+ },
+ {
+ "no": 1191,
+ "code": "1F939 1F3FC 200D 2640 FE0F",
+ "char": "🤹🏼‍♀️",
+ "name": "woman juggling: medium-light skin tone"
+ },
+ {
+ "no": 1192,
+ "code": "1F939 1F3FD 200D 2640 FE0F",
+ "char": "🤹🏽‍♀️",
+ "name": "woman juggling: medium skin tone"
+ },
+ {
+ "no": 1193,
+ "code": "1F939 1F3FE 200D 2640 FE0F",
+ "char": "🤹🏾‍♀️",
+ "name": "woman juggling: medium-dark skin tone"
+ },
+ {
+ "no": 1194,
+ "code": "1F939 1F3FF 200D 2640 FE0F",
+ "char": "🤹🏿‍♀️",
+ "name": "woman juggling: dark skin tone"
+ },
+ {
+ "no": 1195,
+ "code": "1F46B",
+ "char": "👫",
+ "name": "man and woman holding hands"
+ },
+ {
+ "no": 1196,
+ "code": "1F46C",
+ "char": "👬",
+ "name": "two men holding hands"
+ },
+ {
+ "no": 1197,
+ "code": "1F46D",
+ "char": "👭",
+ "name": "two women holding hands"
+ },
+ {
+ "no": 1198,
+ "code": "1F48F",
+ "char": "💏",
+ "name": "kiss"
+ },
+ {
+ "no": 1199,
+ "code": "1F469 200D 2764 FE0F 200D 1F48B 200D 1F468",
+ "char": "👩‍❤️‍💋‍👨",
+ "name": "kiss: woman, man"
+ },
+ {
+ "no": 1200,
+ "code": "1F468 200D 2764 FE0F 200D 1F48B 200D 1F468",
+ "char": "👨‍❤️‍💋‍👨",
+ "name": "kiss: man, man"
+ },
+ {
+ "no": 1201,
+ "code": "1F469 200D 2764 FE0F 200D 1F48B 200D 1F469",
+ "char": "👩‍❤️‍💋‍👩",
+ "name": "kiss: woman, woman"
+ },
+ {
+ "no": 1202,
+ "code": "1F491",
+ "char": "💑",
+ "name": "couple with heart"
+ },
+ {
+ "no": 1203,
+ "code": "1F469 200D 2764 FE0F 200D 1F468",
+ "char": "👩‍❤️‍👨",
+ "name": "couple with heart: woman, man"
+ },
+ {
+ "no": 1204,
+ "code": "1F468 200D 2764 FE0F 200D 1F468",
+ "char": "👨‍❤️‍👨",
+ "name": "couple with heart: man, man"
+ },
+ {
+ "no": 1205,
+ "code": "1F469 200D 2764 FE0F 200D 1F469",
+ "char": "👩‍❤️‍👩",
+ "name": "couple with heart: woman, woman"
+ },
+ {
+ "no": 1206,
+ "code": "1F46A",
+ "char": "👪",
+ "name": "family"
+ },
+ {
+ "no": 1207,
+ "code": "1F468 200D 1F469 200D 1F466",
+ "char": "👨‍👩‍👦",
+ "name": "family: man, woman, boy"
+ },
+ {
+ "no": 1208,
+ "code": "1F468 200D 1F469 200D 1F467",
+ "char": "👨‍👩‍👧",
+ "name": "family: man, woman, girl"
+ },
+ {
+ "no": 1209,
+ "code": "1F468 200D 1F469 200D 1F467 200D 1F466",
+ "char": "👨‍👩‍👧‍👦",
+ "name": "family: man, woman, girl, boy"
+ },
+ {
+ "no": 1210,
+ "code": "1F468 200D 1F469 200D 1F466 200D 1F466",
+ "char": "👨‍👩‍👦‍👦",
+ "name": "family: man, woman, boy, boy"
+ },
+ {
+ "no": 1211,
+ "code": "1F468 200D 1F469 200D 1F467 200D 1F467",
+ "char": "👨‍👩‍👧‍👧",
+ "name": "family: man, woman, girl, girl"
+ },
+ {
+ "no": 1212,
+ "code": "1F468 200D 1F468 200D 1F466",
+ "char": "👨‍👨‍👦",
+ "name": "family: man, man, boy"
+ },
+ {
+ "no": 1213,
+ "code": "1F468 200D 1F468 200D 1F467",
+ "char": "👨‍👨‍👧",
+ "name": "family: man, man, girl"
+ },
+ {
+ "no": 1214,
+ "code": "1F468 200D 1F468 200D 1F467 200D 1F466",
+ "char": "👨‍👨‍👧‍👦",
+ "name": "family: man, man, girl, boy"
+ },
+ {
+ "no": 1215,
+ "code": "1F468 200D 1F468 200D 1F466 200D 1F466",
+ "char": "👨‍👨‍👦‍👦",
+ "name": "family: man, man, boy, boy"
+ },
+ {
+ "no": 1216,
+ "code": "1F468 200D 1F468 200D 1F467 200D 1F467",
+ "char": "👨‍👨‍👧‍👧",
+ "name": "family: man, man, girl, girl"
+ },
+ {
+ "no": 1217,
+ "code": "1F469 200D 1F469 200D 1F466",
+ "char": "👩‍👩‍👦",
+ "name": "family: woman, woman, boy"
+ },
+ {
+ "no": 1218,
+ "code": "1F469 200D 1F469 200D 1F467",
+ "char": "👩‍👩‍👧",
+ "name": "family: woman, woman, girl"
+ },
+ {
+ "no": 1219,
+ "code": "1F469 200D 1F469 200D 1F467 200D 1F466",
+ "char": "👩‍👩‍👧‍👦",
+ "name": "family: woman, woman, girl, boy"
+ },
+ {
+ "no": 1220,
+ "code": "1F469 200D 1F469 200D 1F466 200D 1F466",
+ "char": "👩‍👩‍👦‍👦",
+ "name": "family: woman, woman, boy, boy"
+ },
+ {
+ "no": 1221,
+ "code": "1F469 200D 1F469 200D 1F467 200D 1F467",
+ "char": "👩‍👩‍👧‍👧",
+ "name": "family: woman, woman, girl, girl"
+ },
+ {
+ "no": 1222,
+ "code": "1F468 200D 1F466",
+ "char": "👨‍👦",
+ "name": "family: man, boy"
+ },
+ {
+ "no": 1223,
+ "code": "1F468 200D 1F466 200D 1F466",
+ "char": "👨‍👦‍👦",
+ "name": "family: man, boy, boy"
+ },
+ {
+ "no": 1224,
+ "code": "1F468 200D 1F467",
+ "char": "👨‍👧",
+ "name": "family: man, girl"
+ },
+ {
+ "no": 1225,
+ "code": "1F468 200D 1F467 200D 1F466",
+ "char": "👨‍👧‍👦",
+ "name": "family: man, girl, boy"
+ },
+ {
+ "no": 1226,
+ "code": "1F468 200D 1F467 200D 1F467",
+ "char": "👨‍👧‍👧",
+ "name": "family: man, girl, girl"
+ },
+ {
+ "no": 1227,
+ "code": "1F469 200D 1F466",
+ "char": "👩‍👦",
+ "name": "family: woman, boy"
+ },
+ {
+ "no": 1228,
+ "code": "1F469 200D 1F466 200D 1F466",
+ "char": "👩‍👦‍👦",
+ "name": "family: woman, boy, boy"
+ },
+ {
+ "no": 1229,
+ "code": "1F469 200D 1F467",
+ "char": "👩‍👧",
+ "name": "family: woman, girl"
+ },
+ {
+ "no": 1230,
+ "code": "1F469 200D 1F467 200D 1F466",
+ "char": "👩‍👧‍👦",
+ "name": "family: woman, girl, boy"
+ },
+ {
+ "no": 1231,
+ "code": "1F469 200D 1F467 200D 1F467",
+ "char": "👩‍👧‍👧",
+ "name": "family: woman, girl, girl"
+ },
+ {
+ "no": 1232,
+ "code": "1F933",
+ "char": "🤳",
+ "name": "selfie"
+ },
+ {
+ "no": 1233,
+ "code": "1F933 1F3FB",
+ "char": "🤳🏻",
+ "name": "selfie: light skin tone"
+ },
+ {
+ "no": 1234,
+ "code": "1F933 1F3FC",
+ "char": "🤳🏼",
+ "name": "selfie: medium-light skin tone"
+ },
+ {
+ "no": 1235,
+ "code": "1F933 1F3FD",
+ "char": "🤳🏽",
+ "name": "selfie: medium skin tone"
+ },
+ {
+ "no": 1236,
+ "code": "1F933 1F3FE",
+ "char": "🤳🏾",
+ "name": "selfie: medium-dark skin tone"
+ },
+ {
+ "no": 1237,
+ "code": "1F933 1F3FF",
+ "char": "🤳🏿",
+ "name": "selfie: dark skin tone"
+ },
+ {
+ "no": 1238,
+ "code": "1F4AA",
+ "char": "💪",
+ "name": "flexed biceps"
+ },
+ {
+ "no": 1239,
+ "code": "1F4AA 1F3FB",
+ "char": "💪🏻",
+ "name": "flexed biceps: light skin tone"
+ },
+ {
+ "no": 1240,
+ "code": "1F4AA 1F3FC",
+ "char": "💪🏼",
+ "name": "flexed biceps: medium-light skin tone"
+ },
+ {
+ "no": 1241,
+ "code": "1F4AA 1F3FD",
+ "char": "💪🏽",
+ "name": "flexed biceps: medium skin tone"
+ },
+ {
+ "no": 1242,
+ "code": "1F4AA 1F3FE",
+ "char": "💪🏾",
+ "name": "flexed biceps: medium-dark skin tone"
+ },
+ {
+ "no": 1243,
+ "code": "1F4AA 1F3FF",
+ "char": "💪🏿",
+ "name": "flexed biceps: dark skin tone"
+ },
+ {
+ "no": 1244,
+ "code": "1F448",
+ "char": "👈",
+ "name": "backhand index pointing left"
+ },
+ {
+ "no": 1245,
+ "code": "1F448 1F3FB",
+ "char": "👈🏻",
+ "name": "backhand index pointing left: light skin tone"
+ },
+ {
+ "no": 1246,
+ "code": "1F448 1F3FC",
+ "char": "👈🏼",
+ "name": "backhand index pointing left: medium-light skin tone"
+ },
+ {
+ "no": 1247,
+ "code": "1F448 1F3FD",
+ "char": "👈🏽",
+ "name": "backhand index pointing left: medium skin tone"
+ },
+ {
+ "no": 1248,
+ "code": "1F448 1F3FE",
+ "char": "👈🏾",
+ "name": "backhand index pointing left: medium-dark skin tone"
+ },
+ {
+ "no": 1249,
+ "code": "1F448 1F3FF",
+ "char": "👈🏿",
+ "name": "backhand index pointing left: dark skin tone"
+ },
+ {
+ "no": 1250,
+ "code": "1F449",
+ "char": "👉",
+ "name": "backhand index pointing right"
+ },
+ {
+ "no": 1251,
+ "code": "1F449 1F3FB",
+ "char": "👉🏻",
+ "name": "backhand index pointing right: light skin tone"
+ },
+ {
+ "no": 1252,
+ "code": "1F449 1F3FC",
+ "char": "👉🏼",
+ "name": "backhand index pointing right: medium-light skin tone"
+ },
+ {
+ "no": 1253,
+ "code": "1F449 1F3FD",
+ "char": "👉🏽",
+ "name": "backhand index pointing right: medium skin tone"
+ },
+ {
+ "no": 1254,
+ "code": "1F449 1F3FE",
+ "char": "👉🏾",
+ "name": "backhand index pointing right: medium-dark skin tone"
+ },
+ {
+ "no": 1255,
+ "code": "1F449 1F3FF",
+ "char": "👉🏿",
+ "name": "backhand index pointing right: dark skin tone"
+ },
+ {
+ "no": 1256,
+ "code": "261D",
+ "char": "☝",
+ "name": "index pointing up"
+ },
+ {
+ "no": 1257,
+ "code": "261D 1F3FB",
+ "char": "☝🏻",
+ "name": "index pointing up: light skin tone"
+ },
+ {
+ "no": 1258,
+ "code": "261D 1F3FC",
+ "char": "☝🏼",
+ "name": "index pointing up: medium-light skin tone"
+ },
+ {
+ "no": 1259,
+ "code": "261D 1F3FD",
+ "char": "☝🏽",
+ "name": "index pointing up: medium skin tone"
+ },
+ {
+ "no": 1260,
+ "code": "261D 1F3FE",
+ "char": "☝🏾",
+ "name": "index pointing up: medium-dark skin tone"
+ },
+ {
+ "no": 1261,
+ "code": "261D 1F3FF",
+ "char": "☝🏿",
+ "name": "index pointing up: dark skin tone"
+ },
+ {
+ "no": 1262,
+ "code": "1F446",
+ "char": "👆",
+ "name": "backhand index pointing up"
+ },
+ {
+ "no": 1263,
+ "code": "1F446 1F3FB",
+ "char": "👆🏻",
+ "name": "backhand index pointing up: light skin tone"
+ },
+ {
+ "no": 1264,
+ "code": "1F446 1F3FC",
+ "char": "👆🏼",
+ "name": "backhand index pointing up: medium-light skin tone"
+ },
+ {
+ "no": 1265,
+ "code": "1F446 1F3FD",
+ "char": "👆🏽",
+ "name": "backhand index pointing up: medium skin tone"
+ },
+ {
+ "no": 1266,
+ "code": "1F446 1F3FE",
+ "char": "👆🏾",
+ "name": "backhand index pointing up: medium-dark skin tone"
+ },
+ {
+ "no": 1267,
+ "code": "1F446 1F3FF",
+ "char": "👆🏿",
+ "name": "backhand index pointing up: dark skin tone"
+ },
+ {
+ "no": 1268,
+ "code": "1F595",
+ "char": "🖕",
+ "name": "middle finger"
+ },
+ {
+ "no": 1269,
+ "code": "1F595 1F3FB",
+ "char": "🖕🏻",
+ "name": "middle finger: light skin tone"
+ },
+ {
+ "no": 1270,
+ "code": "1F595 1F3FC",
+ "char": "🖕🏼",
+ "name": "middle finger: medium-light skin tone"
+ },
+ {
+ "no": 1271,
+ "code": "1F595 1F3FD",
+ "char": "🖕🏽",
+ "name": "middle finger: medium skin tone"
+ },
+ {
+ "no": 1272,
+ "code": "1F595 1F3FE",
+ "char": "🖕🏾",
+ "name": "middle finger: medium-dark skin tone"
+ },
+ {
+ "no": 1273,
+ "code": "1F595 1F3FF",
+ "char": "🖕🏿",
+ "name": "middle finger: dark skin tone"
+ },
+ {
+ "no": 1274,
+ "code": "1F447",
+ "char": "👇",
+ "name": "backhand index pointing down"
+ },
+ {
+ "no": 1275,
+ "code": "1F447 1F3FB",
+ "char": "👇🏻",
+ "name": "backhand index pointing down: light skin tone"
+ },
+ {
+ "no": 1276,
+ "code": "1F447 1F3FC",
+ "char": "👇🏼",
+ "name": "backhand index pointing down: medium-light skin tone"
+ },
+ {
+ "no": 1277,
+ "code": "1F447 1F3FD",
+ "char": "👇🏽",
+ "name": "backhand index pointing down: medium skin tone"
+ },
+ {
+ "no": 1278,
+ "code": "1F447 1F3FE",
+ "char": "👇🏾",
+ "name": "backhand index pointing down: medium-dark skin tone"
+ },
+ {
+ "no": 1279,
+ "code": "1F447 1F3FF",
+ "char": "👇🏿",
+ "name": "backhand index pointing down: dark skin tone"
+ },
+ {
+ "no": 1280,
+ "code": "270C",
+ "char": "✌",
+ "name": "victory hand"
+ },
+ {
+ "no": 1281,
+ "code": "270C 1F3FB",
+ "char": "✌🏻",
+ "name": "victory hand: light skin tone"
+ },
+ {
+ "no": 1282,
+ "code": "270C 1F3FC",
+ "char": "✌🏼",
+ "name": "victory hand: medium-light skin tone"
+ },
+ {
+ "no": 1283,
+ "code": "270C 1F3FD",
+ "char": "✌🏽",
+ "name": "victory hand: medium skin tone"
+ },
+ {
+ "no": 1284,
+ "code": "270C 1F3FE",
+ "char": "✌🏾",
+ "name": "victory hand: medium-dark skin tone"
+ },
+ {
+ "no": 1285,
+ "code": "270C 1F3FF",
+ "char": "✌🏿",
+ "name": "victory hand: dark skin tone"
+ },
+ {
+ "no": 1286,
+ "code": "1F91E",
+ "char": "🤞",
+ "name": "crossed fingers"
+ },
+ {
+ "no": 1287,
+ "code": "1F91E 1F3FB",
+ "char": "🤞🏻",
+ "name": "crossed fingers: light skin tone"
+ },
+ {
+ "no": 1288,
+ "code": "1F91E 1F3FC",
+ "char": "🤞🏼",
+ "name": "crossed fingers: medium-light skin tone"
+ },
+ {
+ "no": 1289,
+ "code": "1F91E 1F3FD",
+ "char": "🤞🏽",
+ "name": "crossed fingers: medium skin tone"
+ },
+ {
+ "no": 1290,
+ "code": "1F91E 1F3FE",
+ "char": "🤞🏾",
+ "name": "crossed fingers: medium-dark skin tone"
+ },
+ {
+ "no": 1291,
+ "code": "1F91E 1F3FF",
+ "char": "🤞🏿",
+ "name": "crossed fingers: dark skin tone"
+ },
+ {
+ "no": 1292,
+ "code": "1F596",
+ "char": "🖖",
+ "name": "vulcan salute"
+ },
+ {
+ "no": 1293,
+ "code": "1F596 1F3FB",
+ "char": "🖖🏻",
+ "name": "vulcan salute: light skin tone"
+ },
+ {
+ "no": 1294,
+ "code": "1F596 1F3FC",
+ "char": "🖖🏼",
+ "name": "vulcan salute: medium-light skin tone"
+ },
+ {
+ "no": 1295,
+ "code": "1F596 1F3FD",
+ "char": "🖖🏽",
+ "name": "vulcan salute: medium skin tone"
+ },
+ {
+ "no": 1296,
+ "code": "1F596 1F3FE",
+ "char": "🖖🏾",
+ "name": "vulcan salute: medium-dark skin tone"
+ },
+ {
+ "no": 1297,
+ "code": "1F596 1F3FF",
+ "char": "🖖🏿",
+ "name": "vulcan salute: dark skin tone"
+ },
+ {
+ "no": 1298,
+ "code": "1F918",
+ "char": "🤘",
+ "name": "sign of the horns"
+ },
+ {
+ "no": 1299,
+ "code": "1F918 1F3FB",
+ "char": "🤘🏻",
+ "name": "sign of the horns: light skin tone"
+ },
+ {
+ "no": 1300,
+ "code": "1F918 1F3FC",
+ "char": "🤘🏼",
+ "name": "sign of the horns: medium-light skin tone"
+ },
+ {
+ "no": 1301,
+ "code": "1F918 1F3FD",
+ "char": "🤘🏽",
+ "name": "sign of the horns: medium skin tone"
+ },
+ {
+ "no": 1302,
+ "code": "1F918 1F3FE",
+ "char": "🤘🏾",
+ "name": "sign of the horns: medium-dark skin tone"
+ },
+ {
+ "no": 1303,
+ "code": "1F918 1F3FF",
+ "char": "🤘🏿",
+ "name": "sign of the horns: dark skin tone"
+ },
+ {
+ "no": 1304,
+ "code": "1F919",
+ "char": "🤙",
+ "name": "call me hand"
+ },
+ {
+ "no": 1305,
+ "code": "1F919 1F3FB",
+ "char": "🤙🏻",
+ "name": "call me hand: light skin tone"
+ },
+ {
+ "no": 1306,
+ "code": "1F919 1F3FC",
+ "char": "🤙🏼",
+ "name": "call me hand: medium-light skin tone"
+ },
+ {
+ "no": 1307,
+ "code": "1F919 1F3FD",
+ "char": "🤙🏽",
+ "name": "call me hand: medium skin tone"
+ },
+ {
+ "no": 1308,
+ "code": "1F919 1F3FE",
+ "char": "🤙🏾",
+ "name": "call me hand: medium-dark skin tone"
+ },
+ {
+ "no": 1309,
+ "code": "1F919 1F3FF",
+ "char": "🤙🏿",
+ "name": "call me hand: dark skin tone"
+ },
+ {
+ "no": 1310,
+ "code": "1F590",
+ "char": "🖐",
+ "name": "hand with fingers splayed"
+ },
+ {
+ "no": 1311,
+ "code": "1F590 1F3FB",
+ "char": "🖐🏻",
+ "name": "hand with fingers splayed: light skin tone"
+ },
+ {
+ "no": 1312,
+ "code": "1F590 1F3FC",
+ "char": "🖐🏼",
+ "name": "hand with fingers splayed: medium-light skin tone"
+ },
+ {
+ "no": 1313,
+ "code": "1F590 1F3FD",
+ "char": "🖐🏽",
+ "name": "hand with fingers splayed: medium skin tone"
+ },
+ {
+ "no": 1314,
+ "code": "1F590 1F3FE",
+ "char": "🖐🏾",
+ "name": "hand with fingers splayed: medium-dark skin tone"
+ },
+ {
+ "no": 1315,
+ "code": "1F590 1F3FF",
+ "char": "🖐🏿",
+ "name": "hand with fingers splayed: dark skin tone"
+ },
+ {
+ "no": 1316,
+ "code": "270B",
+ "char": "✋",
+ "name": "raised hand"
+ },
+ {
+ "no": 1317,
+ "code": "270B 1F3FB",
+ "char": "✋🏻",
+ "name": "raised hand: light skin tone"
+ },
+ {
+ "no": 1318,
+ "code": "270B 1F3FC",
+ "char": "✋🏼",
+ "name": "raised hand: medium-light skin tone"
+ },
+ {
+ "no": 1319,
+ "code": "270B 1F3FD",
+ "char": "✋🏽",
+ "name": "raised hand: medium skin tone"
+ },
+ {
+ "no": 1320,
+ "code": "270B 1F3FE",
+ "char": "✋🏾",
+ "name": "raised hand: medium-dark skin tone"
+ },
+ {
+ "no": 1321,
+ "code": "270B 1F3FF",
+ "char": "✋🏿",
+ "name": "raised hand: dark skin tone"
+ },
+ {
+ "no": 1322,
+ "code": "1F44C",
+ "char": "👌",
+ "name": "OK hand"
+ },
+ {
+ "no": 1323,
+ "code": "1F44C 1F3FB",
+ "char": "👌🏻",
+ "name": "OK hand: light skin tone"
+ },
+ {
+ "no": 1324,
+ "code": "1F44C 1F3FC",
+ "char": "👌🏼",
+ "name": "OK hand: medium-light skin tone"
+ },
+ {
+ "no": 1325,
+ "code": "1F44C 1F3FD",
+ "char": "👌🏽",
+ "name": "OK hand: medium skin tone"
+ },
+ {
+ "no": 1326,
+ "code": "1F44C 1F3FE",
+ "char": "👌🏾",
+ "name": "OK hand: medium-dark skin tone"
+ },
+ {
+ "no": 1327,
+ "code": "1F44C 1F3FF",
+ "char": "👌🏿",
+ "name": "OK hand: dark skin tone"
+ },
+ {
+ "no": 1328,
+ "code": "1F44D",
+ "char": "👍",
+ "name": "thumbs up"
+ },
+ {
+ "no": 1329,
+ "code": "1F44D 1F3FB",
+ "char": "👍🏻",
+ "name": "thumbs up: light skin tone"
+ },
+ {
+ "no": 1330,
+ "code": "1F44D 1F3FC",
+ "char": "👍🏼",
+ "name": "thumbs up: medium-light skin tone"
+ },
+ {
+ "no": 1331,
+ "code": "1F44D 1F3FD",
+ "char": "👍🏽",
+ "name": "thumbs up: medium skin tone"
+ },
+ {
+ "no": 1332,
+ "code": "1F44D 1F3FE",
+ "char": "👍🏾",
+ "name": "thumbs up: medium-dark skin tone"
+ },
+ {
+ "no": 1333,
+ "code": "1F44D 1F3FF",
+ "char": "👍🏿",
+ "name": "thumbs up: dark skin tone"
+ },
+ {
+ "no": 1334,
+ "code": "1F44E",
+ "char": "👎",
+ "name": "thumbs down"
+ },
+ {
+ "no": 1335,
+ "code": "1F44E 1F3FB",
+ "char": "👎🏻",
+ "name": "thumbs down: light skin tone"
+ },
+ {
+ "no": 1336,
+ "code": "1F44E 1F3FC",
+ "char": "👎🏼",
+ "name": "thumbs down: medium-light skin tone"
+ },
+ {
+ "no": 1337,
+ "code": "1F44E 1F3FD",
+ "char": "👎🏽",
+ "name": "thumbs down: medium skin tone"
+ },
+ {
+ "no": 1338,
+ "code": "1F44E 1F3FE",
+ "char": "👎🏾",
+ "name": "thumbs down: medium-dark skin tone"
+ },
+ {
+ "no": 1339,
+ "code": "1F44E 1F3FF",
+ "char": "👎🏿",
+ "name": "thumbs down: dark skin tone"
+ },
+ {
+ "no": 1340,
+ "code": "270A",
+ "char": "✊",
+ "name": "raised fist"
+ },
+ {
+ "no": 1341,
+ "code": "270A 1F3FB",
+ "char": "✊🏻",
+ "name": "raised fist: light skin tone"
+ },
+ {
+ "no": 1342,
+ "code": "270A 1F3FC",
+ "char": "✊🏼",
+ "name": "raised fist: medium-light skin tone"
+ },
+ {
+ "no": 1343,
+ "code": "270A 1F3FD",
+ "char": "✊🏽",
+ "name": "raised fist: medium skin tone"
+ },
+ {
+ "no": 1344,
+ "code": "270A 1F3FE",
+ "char": "✊🏾",
+ "name": "raised fist: medium-dark skin tone"
+ },
+ {
+ "no": 1345,
+ "code": "270A 1F3FF",
+ "char": "✊🏿",
+ "name": "raised fist: dark skin tone"
+ },
+ {
+ "no": 1346,
+ "code": "1F44A",
+ "char": "👊",
+ "name": "oncoming fist"
+ },
+ {
+ "no": 1347,
+ "code": "1F44A 1F3FB",
+ "char": "👊🏻",
+ "name": "oncoming fist: light skin tone"
+ },
+ {
+ "no": 1348,
+ "code": "1F44A 1F3FC",
+ "char": "👊🏼",
+ "name": "oncoming fist: medium-light skin tone"
+ },
+ {
+ "no": 1349,
+ "code": "1F44A 1F3FD",
+ "char": "👊🏽",
+ "name": "oncoming fist: medium skin tone"
+ },
+ {
+ "no": 1350,
+ "code": "1F44A 1F3FE",
+ "char": "👊🏾",
+ "name": "oncoming fist: medium-dark skin tone"
+ },
+ {
+ "no": 1351,
+ "code": "1F44A 1F3FF",
+ "char": "👊🏿",
+ "name": "oncoming fist: dark skin tone"
+ },
+ {
+ "no": 1352,
+ "code": "1F91B",
+ "char": "🤛",
+ "name": "left-facing fist"
+ },
+ {
+ "no": 1353,
+ "code": "1F91B 1F3FB",
+ "char": "🤛🏻",
+ "name": "left-facing fist: light skin tone"
+ },
+ {
+ "no": 1354,
+ "code": "1F91B 1F3FC",
+ "char": "🤛🏼",
+ "name": "left-facing fist: medium-light skin tone"
+ },
+ {
+ "no": 1355,
+ "code": "1F91B 1F3FD",
+ "char": "🤛🏽",
+ "name": "left-facing fist: medium skin tone"
+ },
+ {
+ "no": 1356,
+ "code": "1F91B 1F3FE",
+ "char": "🤛🏾",
+ "name": "left-facing fist: medium-dark skin tone"
+ },
+ {
+ "no": 1357,
+ "code": "1F91B 1F3FF",
+ "char": "🤛🏿",
+ "name": "left-facing fist: dark skin tone"
+ },
+ {
+ "no": 1358,
+ "code": "1F91C",
+ "char": "🤜",
+ "name": "right-facing fist"
+ },
+ {
+ "no": 1359,
+ "code": "1F91C 1F3FB",
+ "char": "🤜🏻",
+ "name": "right-facing fist: light skin tone"
+ },
+ {
+ "no": 1360,
+ "code": "1F91C 1F3FC",
+ "char": "🤜🏼",
+ "name": "right-facing fist: medium-light skin tone"
+ },
+ {
+ "no": 1361,
+ "code": "1F91C 1F3FD",
+ "char": "🤜🏽",
+ "name": "right-facing fist: medium skin tone"
+ },
+ {
+ "no": 1362,
+ "code": "1F91C 1F3FE",
+ "char": "🤜🏾",
+ "name": "right-facing fist: medium-dark skin tone"
+ },
+ {
+ "no": 1363,
+ "code": "1F91C 1F3FF",
+ "char": "🤜🏿",
+ "name": "right-facing fist: dark skin tone"
+ },
+ {
+ "no": 1364,
+ "code": "1F91A",
+ "char": "🤚",
+ "name": "raised back of hand"
+ },
+ {
+ "no": 1365,
+ "code": "1F91A 1F3FB",
+ "char": "🤚🏻",
+ "name": "raised back of hand: light skin tone"
+ },
+ {
+ "no": 1366,
+ "code": "1F91A 1F3FC",
+ "char": "🤚🏼",
+ "name": "raised back of hand: medium-light skin tone"
+ },
+ {
+ "no": 1367,
+ "code": "1F91A 1F3FD",
+ "char": "🤚🏽",
+ "name": "raised back of hand: medium skin tone"
+ },
+ {
+ "no": 1368,
+ "code": "1F91A 1F3FE",
+ "char": "🤚🏾",
+ "name": "raised back of hand: medium-dark skin tone"
+ },
+ {
+ "no": 1369,
+ "code": "1F91A 1F3FF",
+ "char": "🤚🏿",
+ "name": "raised back of hand: dark skin tone"
+ },
+ {
+ "no": 1370,
+ "code": "1F44B",
+ "char": "👋",
+ "name": "waving hand"
+ },
+ {
+ "no": 1371,
+ "code": "1F44B 1F3FB",
+ "char": "👋🏻",
+ "name": "waving hand: light skin tone"
+ },
+ {
+ "no": 1372,
+ "code": "1F44B 1F3FC",
+ "char": "👋🏼",
+ "name": "waving hand: medium-light skin tone"
+ },
+ {
+ "no": 1373,
+ "code": "1F44B 1F3FD",
+ "char": "👋🏽",
+ "name": "waving hand: medium skin tone"
+ },
+ {
+ "no": 1374,
+ "code": "1F44B 1F3FE",
+ "char": "👋🏾",
+ "name": "waving hand: medium-dark skin tone"
+ },
+ {
+ "no": 1375,
+ "code": "1F44B 1F3FF",
+ "char": "👋🏿",
+ "name": "waving hand: dark skin tone"
+ },
+ {
+ "no": 1376,
+ "code": "1F91F",
+ "char": "🤟",
+ "name": "⊛ love-you gesture"
+ },
+ {
+ "no": 1377,
+ "code": "1F91F 1F3FB",
+ "char": "🤟🏻",
+ "name": "⊛ love-you gesture: light skin tone"
+ },
+ {
+ "no": 1378,
+ "code": "1F91F 1F3FC",
+ "char": "🤟🏼",
+ "name": "⊛ love-you gesture: medium-light skin tone"
+ },
+ {
+ "no": 1379,
+ "code": "1F91F 1F3FD",
+ "char": "🤟🏽",
+ "name": "⊛ love-you gesture: medium skin tone"
+ },
+ {
+ "no": 1380,
+ "code": "1F91F 1F3FE",
+ "char": "🤟🏾",
+ "name": "⊛ love-you gesture: medium-dark skin tone"
+ },
+ {
+ "no": 1381,
+ "code": "1F91F 1F3FF",
+ "char": "🤟🏿",
+ "name": "⊛ love-you gesture: dark skin tone"
+ },
+ {
+ "no": 1382,
+ "code": "270D",
+ "char": "✍",
+ "name": "writing hand"
+ },
+ {
+ "no": 1383,
+ "code": "270D 1F3FB",
+ "char": "✍🏻",
+ "name": "writing hand: light skin tone"
+ },
+ {
+ "no": 1384,
+ "code": "270D 1F3FC",
+ "char": "✍🏼",
+ "name": "writing hand: medium-light skin tone"
+ },
+ {
+ "no": 1385,
+ "code": "270D 1F3FD",
+ "char": "✍🏽",
+ "name": "writing hand: medium skin tone"
+ },
+ {
+ "no": 1386,
+ "code": "270D 1F3FE",
+ "char": "✍🏾",
+ "name": "writing hand: medium-dark skin tone"
+ },
+ {
+ "no": 1387,
+ "code": "270D 1F3FF",
+ "char": "✍🏿",
+ "name": "writing hand: dark skin tone"
+ },
+ {
+ "no": 1388,
+ "code": "1F44F",
+ "char": "👏",
+ "name": "clapping hands"
+ },
+ {
+ "no": 1389,
+ "code": "1F44F 1F3FB",
+ "char": "👏🏻",
+ "name": "clapping hands: light skin tone"
+ },
+ {
+ "no": 1390,
+ "code": "1F44F 1F3FC",
+ "char": "👏🏼",
+ "name": "clapping hands: medium-light skin tone"
+ },
+ {
+ "no": 1391,
+ "code": "1F44F 1F3FD",
+ "char": "👏🏽",
+ "name": "clapping hands: medium skin tone"
+ },
+ {
+ "no": 1392,
+ "code": "1F44F 1F3FE",
+ "char": "👏🏾",
+ "name": "clapping hands: medium-dark skin tone"
+ },
+ {
+ "no": 1393,
+ "code": "1F44F 1F3FF",
+ "char": "👏🏿",
+ "name": "clapping hands: dark skin tone"
+ },
+ {
+ "no": 1394,
+ "code": "1F450",
+ "char": "👐",
+ "name": "open hands"
+ },
+ {
+ "no": 1395,
+ "code": "1F450 1F3FB",
+ "char": "👐🏻",
+ "name": "open hands: light skin tone"
+ },
+ {
+ "no": 1396,
+ "code": "1F450 1F3FC",
+ "char": "👐🏼",
+ "name": "open hands: medium-light skin tone"
+ },
+ {
+ "no": 1397,
+ "code": "1F450 1F3FD",
+ "char": "👐🏽",
+ "name": "open hands: medium skin tone"
+ },
+ {
+ "no": 1398,
+ "code": "1F450 1F3FE",
+ "char": "👐🏾",
+ "name": "open hands: medium-dark skin tone"
+ },
+ {
+ "no": 1399,
+ "code": "1F450 1F3FF",
+ "char": "👐🏿",
+ "name": "open hands: dark skin tone"
+ },
+ {
+ "no": 1400,
+ "code": "1F64C",
+ "char": "🙌",
+ "name": "raising hands"
+ },
+ {
+ "no": 1401,
+ "code": "1F64C 1F3FB",
+ "char": "🙌🏻",
+ "name": "raising hands: light skin tone"
+ },
+ {
+ "no": 1402,
+ "code": "1F64C 1F3FC",
+ "char": "🙌🏼",
+ "name": "raising hands: medium-light skin tone"
+ },
+ {
+ "no": 1403,
+ "code": "1F64C 1F3FD",
+ "char": "🙌🏽",
+ "name": "raising hands: medium skin tone"
+ },
+ {
+ "no": 1404,
+ "code": "1F64C 1F3FE",
+ "char": "🙌🏾",
+ "name": "raising hands: medium-dark skin tone"
+ },
+ {
+ "no": 1405,
+ "code": "1F64C 1F3FF",
+ "char": "🙌🏿",
+ "name": "raising hands: dark skin tone"
+ },
+ {
+ "no": 1406,
+ "code": "1F932",
+ "char": "🤲",
+ "name": "⊛ palms up together"
+ },
+ {
+ "no": 1407,
+ "code": "1F932 1F3FB",
+ "char": "🤲🏻",
+ "name": "⊛ palms up together: light skin tone"
+ },
+ {
+ "no": 1408,
+ "code": "1F932 1F3FC",
+ "char": "🤲🏼",
+ "name": "⊛ palms up together: medium-light skin tone"
+ },
+ {
+ "no": 1409,
+ "code": "1F932 1F3FD",
+ "char": "🤲🏽",
+ "name": "⊛ palms up together: medium skin tone"
+ },
+ {
+ "no": 1410,
+ "code": "1F932 1F3FE",
+ "char": "🤲🏾",
+ "name": "⊛ palms up together: medium-dark skin tone"
+ },
+ {
+ "no": 1411,
+ "code": "1F932 1F3FF",
+ "char": "🤲🏿",
+ "name": "⊛ palms up together: dark skin tone"
+ },
+ {
+ "no": 1412,
+ "code": "1F64F",
+ "char": "🙏",
+ "name": "folded hands"
+ },
+ {
+ "no": 1413,
+ "code": "1F64F 1F3FB",
+ "char": "🙏🏻",
+ "name": "folded hands: light skin tone"
+ },
+ {
+ "no": 1414,
+ "code": "1F64F 1F3FC",
+ "char": "🙏🏼",
+ "name": "folded hands: medium-light skin tone"
+ },
+ {
+ "no": 1415,
+ "code": "1F64F 1F3FD",
+ "char": "🙏🏽",
+ "name": "folded hands: medium skin tone"
+ },
+ {
+ "no": 1416,
+ "code": "1F64F 1F3FE",
+ "char": "🙏🏾",
+ "name": "folded hands: medium-dark skin tone"
+ },
+ {
+ "no": 1417,
+ "code": "1F64F 1F3FF",
+ "char": "🙏🏿",
+ "name": "folded hands: dark skin tone"
+ },
+ {
+ "no": 1418,
+ "code": "1F91D",
+ "char": "🤝",
+ "name": "handshake"
+ },
+ {
+ "no": 1419,
+ "code": "1F485",
+ "char": "💅",
+ "name": "nail polish"
+ },
+ {
+ "no": 1420,
+ "code": "1F485 1F3FB",
+ "char": "💅🏻",
+ "name": "nail polish: light skin tone"
+ },
+ {
+ "no": 1421,
+ "code": "1F485 1F3FC",
+ "char": "💅🏼",
+ "name": "nail polish: medium-light skin tone"
+ },
+ {
+ "no": 1422,
+ "code": "1F485 1F3FD",
+ "char": "💅🏽",
+ "name": "nail polish: medium skin tone"
+ },
+ {
+ "no": 1423,
+ "code": "1F485 1F3FE",
+ "char": "💅🏾",
+ "name": "nail polish: medium-dark skin tone"
+ },
+ {
+ "no": 1424,
+ "code": "1F485 1F3FF",
+ "char": "💅🏿",
+ "name": "nail polish: dark skin tone"
+ },
+ {
+ "no": 1425,
+ "code": "1F442",
+ "char": "👂",
+ "name": "ear"
+ },
+ {
+ "no": 1426,
+ "code": "1F442 1F3FB",
+ "char": "👂🏻",
+ "name": "ear: light skin tone"
+ },
+ {
+ "no": 1427,
+ "code": "1F442 1F3FC",
+ "char": "👂🏼",
+ "name": "ear: medium-light skin tone"
+ },
+ {
+ "no": 1428,
+ "code": "1F442 1F3FD",
+ "char": "👂🏽",
+ "name": "ear: medium skin tone"
+ },
+ {
+ "no": 1429,
+ "code": "1F442 1F3FE",
+ "char": "👂🏾",
+ "name": "ear: medium-dark skin tone"
+ },
+ {
+ "no": 1430,
+ "code": "1F442 1F3FF",
+ "char": "👂🏿",
+ "name": "ear: dark skin tone"
+ },
+ {
+ "no": 1431,
+ "code": "1F443",
+ "char": "👃",
+ "name": "nose"
+ },
+ {
+ "no": 1432,
+ "code": "1F443 1F3FB",
+ "char": "👃🏻",
+ "name": "nose: light skin tone"
+ },
+ {
+ "no": 1433,
+ "code": "1F443 1F3FC",
+ "char": "👃🏼",
+ "name": "nose: medium-light skin tone"
+ },
+ {
+ "no": 1434,
+ "code": "1F443 1F3FD",
+ "char": "👃🏽",
+ "name": "nose: medium skin tone"
+ },
+ {
+ "no": 1435,
+ "code": "1F443 1F3FE",
+ "char": "👃🏾",
+ "name": "nose: medium-dark skin tone"
+ },
+ {
+ "no": 1436,
+ "code": "1F443 1F3FF",
+ "char": "👃🏿",
+ "name": "nose: dark skin tone"
+ },
+ {
+ "no": 1437,
+ "code": "1F463",
+ "char": "👣",
+ "name": "footprints"
+ },
+ {
+ "no": 1438,
+ "code": "1F440",
+ "char": "👀",
+ "name": "eyes"
+ },
+ {
+ "no": 1439,
+ "code": "1F441",
+ "char": "👁",
+ "name": "eye"
+ },
+ {
+ "no": 1440,
+ "code": "1F441 FE0F 200D 1F5E8 FE0F",
+ "char": "👁️‍🗨️",
+ "name": "eye in speech bubble"
+ },
+ {
+ "no": 1441,
+ "code": "1F9E0",
+ "char": "🧠",
+ "name": "⊛ brain"
+ },
+ {
+ "no": 1442,
+ "code": "1F445",
+ "char": "👅",
+ "name": "tongue"
+ },
+ {
+ "no": 1443,
+ "code": "1F444",
+ "char": "👄",
+ "name": "mouth"
+ },
+ {
+ "no": 1444,
+ "code": "1F48B",
+ "char": "💋",
+ "name": "kiss mark"
+ },
+ {
+ "no": 1445,
+ "code": "1F498",
+ "char": "💘",
+ "name": "heart with arrow"
+ },
+ {
+ "no": 1446,
+ "code": "2764",
+ "char": "❤",
+ "name": "red heart"
+ },
+ {
+ "no": 1447,
+ "code": "1F493",
+ "char": "💓",
+ "name": "beating heart"
+ },
+ {
+ "no": 1448,
+ "code": "1F494",
+ "char": "💔",
+ "name": "broken heart"
+ },
+ {
+ "no": 1449,
+ "code": "1F495",
+ "char": "💕",
+ "name": "two hearts"
+ },
+ {
+ "no": 1450,
+ "code": "1F496",
+ "char": "💖",
+ "name": "sparkling heart"
+ },
+ {
+ "no": 1451,
+ "code": "1F497",
+ "char": "💗",
+ "name": "growing heart"
+ },
+ {
+ "no": 1452,
+ "code": "1F499",
+ "char": "💙",
+ "name": "blue heart"
+ },
+ {
+ "no": 1453,
+ "code": "1F49A",
+ "char": "💚",
+ "name": "green heart"
+ },
+ {
+ "no": 1454,
+ "code": "1F49B",
+ "char": "💛",
+ "name": "yellow heart"
+ },
+ {
+ "no": 1455,
+ "code": "1F9E1",
+ "char": "🧡",
+ "name": "⊛ orange heart"
+ },
+ {
+ "no": 1456,
+ "code": "1F49C",
+ "char": "💜",
+ "name": "purple heart"
+ },
+ {
+ "no": 1457,
+ "code": "1F5A4",
+ "char": "🖤",
+ "name": "black heart"
+ },
+ {
+ "no": 1458,
+ "code": "1F49D",
+ "char": "💝",
+ "name": "heart with ribbon"
+ },
+ {
+ "no": 1459,
+ "code": "1F49E",
+ "char": "💞",
+ "name": "revolving hearts"
+ },
+ {
+ "no": 1460,
+ "code": "1F49F",
+ "char": "💟",
+ "name": "heart decoration"
+ },
+ {
+ "no": 1461,
+ "code": "2763",
+ "char": "❣",
+ "name": "heavy heart exclamation"
+ },
+ {
+ "no": 1462,
+ "code": "1F48C",
+ "char": "💌",
+ "name": "love letter"
+ },
+ {
+ "no": 1463,
+ "code": "1F4A4",
+ "char": "💤",
+ "name": "zzz"
+ },
+ {
+ "no": 1464,
+ "code": "1F4A2",
+ "char": "💢",
+ "name": "anger symbol"
+ },
+ {
+ "no": 1465,
+ "code": "1F4A3",
+ "char": "💣",
+ "name": "bomb"
+ },
+ {
+ "no": 1466,
+ "code": "1F4A5",
+ "char": "💥",
+ "name": "collision"
+ },
+ {
+ "no": 1467,
+ "code": "1F4A6",
+ "char": "💦",
+ "name": "sweat droplets"
+ },
+ {
+ "no": 1468,
+ "code": "1F4A8",
+ "char": "💨",
+ "name": "dashing away"
+ },
+ {
+ "no": 1469,
+ "code": "1F4AB",
+ "char": "💫",
+ "name": "dizzy"
+ },
+ {
+ "no": 1470,
+ "code": "1F4AC",
+ "char": "💬",
+ "name": "speech balloon"
+ },
+ {
+ "no": 1471,
+ "code": "1F5E8",
+ "char": "🗨",
+ "name": "left speech bubble"
+ },
+ {
+ "no": 1472,
+ "code": "1F5EF",
+ "char": "🗯",
+ "name": "right anger bubble"
+ },
+ {
+ "no": 1473,
+ "code": "1F4AD",
+ "char": "💭",
+ "name": "thought balloon"
+ },
+ {
+ "no": 1474,
+ "code": "1F573",
+ "char": "🕳",
+ "name": "hole"
+ },
+ {
+ "no": 1475,
+ "code": "1F453",
+ "char": "👓",
+ "name": "glasses"
+ },
+ {
+ "no": 1476,
+ "code": "1F576",
+ "char": "🕶",
+ "name": "sunglasses"
+ },
+ {
+ "no": 1477,
+ "code": "1F454",
+ "char": "👔",
+ "name": "necktie"
+ },
+ {
+ "no": 1478,
+ "code": "1F455",
+ "char": "👕",
+ "name": "t-shirt"
+ },
+ {
+ "no": 1479,
+ "code": "1F456",
+ "char": "👖",
+ "name": "jeans"
+ },
+ {
+ "no": 1480,
+ "code": "1F9E3",
+ "char": "🧣",
+ "name": "⊛ scarf"
+ },
+ {
+ "no": 1481,
+ "code": "1F9E4",
+ "char": "🧤",
+ "name": "⊛ gloves"
+ },
+ {
+ "no": 1482,
+ "code": "1F9E5",
+ "char": "🧥",
+ "name": "⊛ coat"
+ },
+ {
+ "no": 1483,
+ "code": "1F9E6",
+ "char": "🧦",
+ "name": "⊛ socks"
+ },
+ {
+ "no": 1484,
+ "code": "1F457",
+ "char": "👗",
+ "name": "dress"
+ },
+ {
+ "no": 1485,
+ "code": "1F458",
+ "char": "👘",
+ "name": "kimono"
+ },
+ {
+ "no": 1486,
+ "code": "1F459",
+ "char": "👙",
+ "name": "bikini"
+ },
+ {
+ "no": 1487,
+ "code": "1F45A",
+ "char": "👚",
+ "name": "woman’s clothes"
+ },
+ {
+ "no": 1488,
+ "code": "1F45B",
+ "char": "👛",
+ "name": "purse"
+ },
+ {
+ "no": 1489,
+ "code": "1F45C",
+ "char": "👜",
+ "name": "handbag"
+ },
+ {
+ "no": 1490,
+ "code": "1F45D",
+ "char": "👝",
+ "name": "clutch bag"
+ },
+ {
+ "no": 1491,
+ "code": "1F6CD",
+ "char": "🛍",
+ "name": "shopping bags"
+ },
+ {
+ "no": 1492,
+ "code": "1F392",
+ "char": "🎒",
+ "name": "school backpack"
+ },
+ {
+ "no": 1493,
+ "code": "1F45E",
+ "char": "👞",
+ "name": "man’s shoe"
+ },
+ {
+ "no": 1494,
+ "code": "1F45F",
+ "char": "👟",
+ "name": "running shoe"
+ },
+ {
+ "no": 1495,
+ "code": "1F460",
+ "char": "👠",
+ "name": "high-heeled shoe"
+ },
+ {
+ "no": 1496,
+ "code": "1F461",
+ "char": "👡",
+ "name": "woman’s sandal"
+ },
+ {
+ "no": 1497,
+ "code": "1F462",
+ "char": "👢",
+ "name": "woman’s boot"
+ },
+ {
+ "no": 1498,
+ "code": "1F451",
+ "char": "👑",
+ "name": "crown"
+ },
+ {
+ "no": 1499,
+ "code": "1F452",
+ "char": "👒",
+ "name": "woman’s hat"
+ },
+ {
+ "no": 1500,
+ "code": "1F3A9",
+ "char": "🎩",
+ "name": "top hat"
+ },
+ {
+ "no": 1501,
+ "code": "1F393",
+ "char": "🎓",
+ "name": "graduation cap"
+ },
+ {
+ "no": 1502,
+ "code": "1F9E2",
+ "char": "🧢",
+ "name": "⊛ billed cap"
+ },
+ {
+ "no": 1503,
+ "code": "26D1",
+ "char": "⛑",
+ "name": "rescue worker’s helmet"
+ },
+ {
+ "no": 1504,
+ "code": "1F4FF",
+ "char": "📿",
+ "name": "prayer beads"
+ },
+ {
+ "no": 1505,
+ "code": "1F484",
+ "char": "💄",
+ "name": "lipstick"
+ },
+ {
+ "no": 1506,
+ "code": "1F48D",
+ "char": "💍",
+ "name": "ring"
+ },
+ {
+ "no": 1507,
+ "code": "1F48E",
+ "char": "💎",
+ "name": "gem stone"
+ },
+ {
+ "no": 1508,
+ "code": "1F435",
+ "char": "🐵",
+ "name": "monkey face"
+ },
+ {
+ "no": 1509,
+ "code": "1F412",
+ "char": "🐒",
+ "name": "monkey"
+ },
+ {
+ "no": 1510,
+ "code": "1F98D",
+ "char": "🦍",
+ "name": "gorilla"
+ },
+ {
+ "no": 1511,
+ "code": "1F436",
+ "char": "🐶",
+ "name": "dog face"
+ },
+ {
+ "no": 1512,
+ "code": "1F415",
+ "char": "🐕",
+ "name": "dog"
+ },
+ {
+ "no": 1513,
+ "code": "1F429",
+ "char": "🐩",
+ "name": "poodle"
+ },
+ {
+ "no": 1514,
+ "code": "1F43A",
+ "char": "🐺",
+ "name": "wolf face"
+ },
+ {
+ "no": 1515,
+ "code": "1F98A",
+ "char": "🦊",
+ "name": "fox face"
+ },
+ {
+ "no": 1516,
+ "code": "1F431",
+ "char": "🐱",
+ "name": "cat face"
+ },
+ {
+ "no": 1517,
+ "code": "1F408",
+ "char": "🐈",
+ "name": "cat"
+ },
+ {
+ "no": 1518,
+ "code": "1F981",
+ "char": "🦁",
+ "name": "lion face"
+ },
+ {
+ "no": 1519,
+ "code": "1F42F",
+ "char": "🐯",
+ "name": "tiger face"
+ },
+ {
+ "no": 1520,
+ "code": "1F405",
+ "char": "🐅",
+ "name": "tiger"
+ },
+ {
+ "no": 1521,
+ "code": "1F406",
+ "char": "🐆",
+ "name": "leopard"
+ },
+ {
+ "no": 1522,
+ "code": "1F434",
+ "char": "🐴",
+ "name": "horse face"
+ },
+ {
+ "no": 1523,
+ "code": "1F40E",
+ "char": "🐎",
+ "name": "horse"
+ },
+ {
+ "no": 1524,
+ "code": "1F984",
+ "char": "🦄",
+ "name": "unicorn face"
+ },
+ {
+ "no": 1525,
+ "code": "1F993",
+ "char": "🦓",
+ "name": "⊛ zebra"
+ },
+ {
+ "no": 1526,
+ "code": "1F98C",
+ "char": "🦌",
+ "name": "deer"
+ },
+ {
+ "no": 1527,
+ "code": "1F42E",
+ "char": "🐮",
+ "name": "cow face"
+ },
+ {
+ "no": 1528,
+ "code": "1F402",
+ "char": "🐂",
+ "name": "ox"
+ },
+ {
+ "no": 1529,
+ "code": "1F403",
+ "char": "🐃",
+ "name": "water buffalo"
+ },
+ {
+ "no": 1530,
+ "code": "1F404",
+ "char": "🐄",
+ "name": "cow"
+ },
+ {
+ "no": 1531,
+ "code": "1F437",
+ "char": "🐷",
+ "name": "pig face"
+ },
+ {
+ "no": 1532,
+ "code": "1F416",
+ "char": "🐖",
+ "name": "pig"
+ },
+ {
+ "no": 1533,
+ "code": "1F417",
+ "char": "🐗",
+ "name": "boar"
+ },
+ {
+ "no": 1534,
+ "code": "1F43D",
+ "char": "🐽",
+ "name": "pig nose"
+ },
+ {
+ "no": 1535,
+ "code": "1F40F",
+ "char": "🐏",
+ "name": "ram"
+ },
+ {
+ "no": 1536,
+ "code": "1F411",
+ "char": "🐑",
+ "name": "ewe"
+ },
+ {
+ "no": 1537,
+ "code": "1F410",
+ "char": "🐐",
+ "name": "goat"
+ },
+ {
+ "no": 1538,
+ "code": "1F42A",
+ "char": "🐪",
+ "name": "camel"
+ },
+ {
+ "no": 1539,
+ "code": "1F42B",
+ "char": "🐫",
+ "name": "two-hump camel"
+ },
+ {
+ "no": 1540,
+ "code": "1F992",
+ "char": "🦒",
+ "name": "⊛ giraffe"
+ },
+ {
+ "no": 1541,
+ "code": "1F418",
+ "char": "🐘",
+ "name": "elephant"
+ },
+ {
+ "no": 1542,
+ "code": "1F98F",
+ "char": "🦏",
+ "name": "rhinoceros"
+ },
+ {
+ "no": 1543,
+ "code": "1F42D",
+ "char": "🐭",
+ "name": "mouse face"
+ },
+ {
+ "no": 1544,
+ "code": "1F401",
+ "char": "🐁",
+ "name": "mouse"
+ },
+ {
+ "no": 1545,
+ "code": "1F400",
+ "char": "🐀",
+ "name": "rat"
+ },
+ {
+ "no": 1546,
+ "code": "1F439",
+ "char": "🐹",
+ "name": "hamster face"
+ },
+ {
+ "no": 1547,
+ "code": "1F430",
+ "char": "🐰",
+ "name": "rabbit face"
+ },
+ {
+ "no": 1548,
+ "code": "1F407",
+ "char": "🐇",
+ "name": "rabbit"
+ },
+ {
+ "no": 1549,
+ "code": "1F43F",
+ "char": "🐿",
+ "name": "chipmunk"
+ },
+ {
+ "no": 1550,
+ "code": "1F994",
+ "char": "🦔",
+ "name": "⊛ hedgehog"
+ },
+ {
+ "no": 1551,
+ "code": "1F987",
+ "char": "🦇",
+ "name": "bat"
+ },
+ {
+ "no": 1552,
+ "code": "1F43B",
+ "char": "🐻",
+ "name": "bear face"
+ },
+ {
+ "no": 1553,
+ "code": "1F428",
+ "char": "🐨",
+ "name": "koala"
+ },
+ {
+ "no": 1554,
+ "code": "1F43C",
+ "char": "🐼",
+ "name": "panda face"
+ },
+ {
+ "no": 1555,
+ "code": "1F43E",
+ "char": "🐾",
+ "name": "paw prints"
+ },
+ {
+ "no": 1556,
+ "code": "1F983",
+ "char": "🦃",
+ "name": "turkey"
+ },
+ {
+ "no": 1557,
+ "code": "1F414",
+ "char": "🐔",
+ "name": "chicken"
+ },
+ {
+ "no": 1558,
+ "code": "1F413",
+ "char": "🐓",
+ "name": "rooster"
+ },
+ {
+ "no": 1559,
+ "code": "1F423",
+ "char": "🐣",
+ "name": "hatching chick"
+ },
+ {
+ "no": 1560,
+ "code": "1F424",
+ "char": "🐤",
+ "name": "baby chick"
+ },
+ {
+ "no": 1561,
+ "code": "1F425",
+ "char": "🐥",
+ "name": "front-facing baby chick"
+ },
+ {
+ "no": 1562,
+ "code": "1F426",
+ "char": "🐦",
+ "name": "bird"
+ },
+ {
+ "no": 1563,
+ "code": "1F427",
+ "char": "🐧",
+ "name": "penguin"
+ },
+ {
+ "no": 1564,
+ "code": "1F54A",
+ "char": "🕊",
+ "name": "dove"
+ },
+ {
+ "no": 1565,
+ "code": "1F985",
+ "char": "🦅",
+ "name": "eagle"
+ },
+ {
+ "no": 1566,
+ "code": "1F986",
+ "char": "🦆",
+ "name": "duck"
+ },
+ {
+ "no": 1567,
+ "code": "1F989",
+ "char": "🦉",
+ "name": "owl"
+ },
+ {
+ "no": 1568,
+ "code": "1F438",
+ "char": "🐸",
+ "name": "frog face"
+ },
+ {
+ "no": 1569,
+ "code": "1F40A",
+ "char": "🐊",
+ "name": "crocodile"
+ },
+ {
+ "no": 1570,
+ "code": "1F422",
+ "char": "🐢",
+ "name": "turtle"
+ },
+ {
+ "no": 1571,
+ "code": "1F98E",
+ "char": "🦎",
+ "name": "lizard"
+ },
+ {
+ "no": 1572,
+ "code": "1F40D",
+ "char": "🐍",
+ "name": "snake"
+ },
+ {
+ "no": 1573,
+ "code": "1F432",
+ "char": "🐲",
+ "name": "dragon face"
+ },
+ {
+ "no": 1574,
+ "code": "1F409",
+ "char": "🐉",
+ "name": "dragon"
+ },
+ {
+ "no": 1575,
+ "code": "1F995",
+ "char": "🦕",
+ "name": "⊛ sauropod"
+ },
+ {
+ "no": 1576,
+ "code": "1F996",
+ "char": "🦖",
+ "name": "⊛ T-Rex"
+ },
+ {
+ "no": 1577,
+ "code": "1F433",
+ "char": "🐳",
+ "name": "spouting whale"
+ },
+ {
+ "no": 1578,
+ "code": "1F40B",
+ "char": "🐋",
+ "name": "whale"
+ },
+ {
+ "no": 1579,
+ "code": "1F42C",
+ "char": "🐬",
+ "name": "dolphin"
+ },
+ {
+ "no": 1580,
+ "code": "1F41F",
+ "char": "🐟",
+ "name": "fish"
+ },
+ {
+ "no": 1581,
+ "code": "1F420",
+ "char": "🐠",
+ "name": "tropical fish"
+ },
+ {
+ "no": 1582,
+ "code": "1F421",
+ "char": "🐡",
+ "name": "blowfish"
+ },
+ {
+ "no": 1583,
+ "code": "1F988",
+ "char": "🦈",
+ "name": "shark"
+ },
+ {
+ "no": 1584,
+ "code": "1F419",
+ "char": "🐙",
+ "name": "octopus"
+ },
+ {
+ "no": 1585,
+ "code": "1F41A",
+ "char": "🐚",
+ "name": "spiral shell"
+ },
+ {
+ "no": 1586,
+ "code": "1F980",
+ "char": "🦀",
+ "name": "crab"
+ },
+ {
+ "no": 1587,
+ "code": "1F990",
+ "char": "🦐",
+ "name": "shrimp"
+ },
+ {
+ "no": 1588,
+ "code": "1F991",
+ "char": "🦑",
+ "name": "squid"
+ },
+ {
+ "no": 1589,
+ "code": "1F40C",
+ "char": "🐌",
+ "name": "snail"
+ },
+ {
+ "no": 1590,
+ "code": "1F98B",
+ "char": "🦋",
+ "name": "butterfly"
+ },
+ {
+ "no": 1591,
+ "code": "1F41B",
+ "char": "🐛",
+ "name": "bug"
+ },
+ {
+ "no": 1592,
+ "code": "1F41C",
+ "char": "🐜",
+ "name": "ant"
+ },
+ {
+ "no": 1593,
+ "code": "1F41D",
+ "char": "🐝",
+ "name": "honeybee"
+ },
+ {
+ "no": 1594,
+ "code": "1F41E",
+ "char": "🐞",
+ "name": "lady beetle"
+ },
+ {
+ "no": 1595,
+ "code": "1F997",
+ "char": "🦗",
+ "name": "⊛ cricket"
+ },
+ {
+ "no": 1596,
+ "code": "1F577",
+ "char": "🕷",
+ "name": "spider"
+ },
+ {
+ "no": 1597,
+ "code": "1F578",
+ "char": "🕸",
+ "name": "spider web"
+ },
+ {
+ "no": 1598,
+ "code": "1F982",
+ "char": "🦂",
+ "name": "scorpion"
+ },
+ {
+ "no": 1599,
+ "code": "1F490",
+ "char": "💐",
+ "name": "bouquet"
+ },
+ {
+ "no": 1600,
+ "code": "1F338",
+ "char": "🌸",
+ "name": "cherry blossom"
+ },
+ {
+ "no": 1601,
+ "code": "1F4AE",
+ "char": "💮",
+ "name": "white flower"
+ },
+ {
+ "no": 1602,
+ "code": "1F3F5",
+ "char": "🏵",
+ "name": "rosette"
+ },
+ {
+ "no": 1603,
+ "code": "1F339",
+ "char": "🌹",
+ "name": "rose"
+ },
+ {
+ "no": 1604,
+ "code": "1F940",
+ "char": "🥀",
+ "name": "wilted flower"
+ },
+ {
+ "no": 1605,
+ "code": "1F33A",
+ "char": "🌺",
+ "name": "hibiscus"
+ },
+ {
+ "no": 1606,
+ "code": "1F33B",
+ "char": "🌻",
+ "name": "sunflower"
+ },
+ {
+ "no": 1607,
+ "code": "1F33C",
+ "char": "🌼",
+ "name": "blossom"
+ },
+ {
+ "no": 1608,
+ "code": "1F337",
+ "char": "🌷",
+ "name": "tulip"
+ },
+ {
+ "no": 1609,
+ "code": "1F331",
+ "char": "🌱",
+ "name": "seedling"
+ },
+ {
+ "no": 1610,
+ "code": "1F332",
+ "char": "🌲",
+ "name": "evergreen tree"
+ },
+ {
+ "no": 1611,
+ "code": "1F333",
+ "char": "🌳",
+ "name": "deciduous tree"
+ },
+ {
+ "no": 1612,
+ "code": "1F334",
+ "char": "🌴",
+ "name": "palm tree"
+ },
+ {
+ "no": 1613,
+ "code": "1F335",
+ "char": "🌵",
+ "name": "cactus"
+ },
+ {
+ "no": 1614,
+ "code": "1F33E",
+ "char": "🌾",
+ "name": "sheaf of rice"
+ },
+ {
+ "no": 1615,
+ "code": "1F33F",
+ "char": "🌿",
+ "name": "herb"
+ },
+ {
+ "no": 1616,
+ "code": "2618",
+ "char": "☘",
+ "name": "shamrock"
+ },
+ {
+ "no": 1617,
+ "code": "1F340",
+ "char": "🍀",
+ "name": "four leaf clover"
+ },
+ {
+ "no": 1618,
+ "code": "1F341",
+ "char": "🍁",
+ "name": "maple leaf"
+ },
+ {
+ "no": 1619,
+ "code": "1F342",
+ "char": "🍂",
+ "name": "fallen leaf"
+ },
+ {
+ "no": 1620,
+ "code": "1F343",
+ "char": "🍃",
+ "name": "leaf fluttering in wind"
+ },
+ {
+ "no": 1621,
+ "code": "1F347",
+ "char": "🍇",
+ "name": "grapes"
+ },
+ {
+ "no": 1622,
+ "code": "1F348",
+ "char": "🍈",
+ "name": "melon"
+ },
+ {
+ "no": 1623,
+ "code": "1F349",
+ "char": "🍉",
+ "name": "watermelon"
+ },
+ {
+ "no": 1624,
+ "code": "1F34A",
+ "char": "🍊",
+ "name": "tangerine"
+ },
+ {
+ "no": 1625,
+ "code": "1F34B",
+ "char": "🍋",
+ "name": "lemon"
+ },
+ {
+ "no": 1626,
+ "code": "1F34C",
+ "char": "🍌",
+ "name": "banana"
+ },
+ {
+ "no": 1627,
+ "code": "1F34D",
+ "char": "🍍",
+ "name": "pineapple"
+ },
+ {
+ "no": 1628,
+ "code": "1F34E",
+ "char": "🍎",
+ "name": "red apple"
+ },
+ {
+ "no": 1629,
+ "code": "1F34F",
+ "char": "🍏",
+ "name": "green apple"
+ },
+ {
+ "no": 1630,
+ "code": "1F350",
+ "char": "🍐",
+ "name": "pear"
+ },
+ {
+ "no": 1631,
+ "code": "1F351",
+ "char": "🍑",
+ "name": "peach"
+ },
+ {
+ "no": 1632,
+ "code": "1F352",
+ "char": "🍒",
+ "name": "cherries"
+ },
+ {
+ "no": 1633,
+ "code": "1F353",
+ "char": "🍓",
+ "name": "strawberry"
+ },
+ {
+ "no": 1634,
+ "code": "1F95D",
+ "char": "🥝",
+ "name": "kiwi fruit"
+ },
+ {
+ "no": 1635,
+ "code": "1F345",
+ "char": "🍅",
+ "name": "tomato"
+ },
+ {
+ "no": 1636,
+ "code": "1F965",
+ "char": "🥥",
+ "name": "⊛ coconut"
+ },
+ {
+ "no": 1637,
+ "code": "1F951",
+ "char": "🥑",
+ "name": "avocado"
+ },
+ {
+ "no": 1638,
+ "code": "1F346",
+ "char": "🍆",
+ "name": "eggplant"
+ },
+ {
+ "no": 1639,
+ "code": "1F954",
+ "char": "🥔",
+ "name": "potato"
+ },
+ {
+ "no": 1640,
+ "code": "1F955",
+ "char": "🥕",
+ "name": "carrot"
+ },
+ {
+ "no": 1641,
+ "code": "1F33D",
+ "char": "🌽",
+ "name": "ear of corn"
+ },
+ {
+ "no": 1642,
+ "code": "1F336",
+ "char": "🌶",
+ "name": "hot pepper"
+ },
+ {
+ "no": 1643,
+ "code": "1F952",
+ "char": "🥒",
+ "name": "cucumber"
+ },
+ {
+ "no": 1644,
+ "code": "1F966",
+ "char": "🥦",
+ "name": "⊛ broccoli"
+ },
+ {
+ "no": 1645,
+ "code": "1F344",
+ "char": "🍄",
+ "name": "mushroom"
+ },
+ {
+ "no": 1646,
+ "code": "1F95C",
+ "char": "🥜",
+ "name": "peanuts"
+ },
+ {
+ "no": 1647,
+ "code": "1F330",
+ "char": "🌰",
+ "name": "chestnut"
+ },
+ {
+ "no": 1648,
+ "code": "1F35E",
+ "char": "🍞",
+ "name": "bread"
+ },
+ {
+ "no": 1649,
+ "code": "1F950",
+ "char": "🥐",
+ "name": "croissant"
+ },
+ {
+ "no": 1650,
+ "code": "1F956",
+ "char": "🥖",
+ "name": "baguette bread"
+ },
+ {
+ "no": 1651,
+ "code": "1F968",
+ "char": "🥨",
+ "name": "⊛ pretzel"
+ },
+ {
+ "no": 1652,
+ "code": "1F95E",
+ "char": "🥞",
+ "name": "pancakes"
+ },
+ {
+ "no": 1653,
+ "code": "1F9C0",
+ "char": "🧀",
+ "name": "cheese wedge"
+ },
+ {
+ "no": 1654,
+ "code": "1F356",
+ "char": "🍖",
+ "name": "meat on bone"
+ },
+ {
+ "no": 1655,
+ "code": "1F357",
+ "char": "🍗",
+ "name": "poultry leg"
+ },
+ {
+ "no": 1656,
+ "code": "1F969",
+ "char": "🥩",
+ "name": "⊛ cut of meat"
+ },
+ {
+ "no": 1657,
+ "code": "1F953",
+ "char": "🥓",
+ "name": "bacon"
+ },
+ {
+ "no": 1658,
+ "code": "1F354",
+ "char": "🍔",
+ "name": "hamburger"
+ },
+ {
+ "no": 1659,
+ "code": "1F35F",
+ "char": "🍟",
+ "name": "french fries"
+ },
+ {
+ "no": 1660,
+ "code": "1F355",
+ "char": "🍕",
+ "name": "pizza"
+ },
+ {
+ "no": 1661,
+ "code": "1F32D",
+ "char": "🌭",
+ "name": "hot dog"
+ },
+ {
+ "no": 1662,
+ "code": "1F96A",
+ "char": "🥪",
+ "name": "⊛ sandwich"
+ },
+ {
+ "no": 1663,
+ "code": "1F32E",
+ "char": "🌮",
+ "name": "taco"
+ },
+ {
+ "no": 1664,
+ "code": "1F32F",
+ "char": "🌯",
+ "name": "burrito"
+ },
+ {
+ "no": 1665,
+ "code": "1F959",
+ "char": "🥙",
+ "name": "stuffed flatbread"
+ },
+ {
+ "no": 1666,
+ "code": "1F95A",
+ "char": "🥚",
+ "name": "egg"
+ },
+ {
+ "no": 1667,
+ "code": "1F373",
+ "char": "🍳",
+ "name": "cooking"
+ },
+ {
+ "no": 1668,
+ "code": "1F958",
+ "char": "🥘",
+ "name": "shallow pan of food"
+ },
+ {
+ "no": 1669,
+ "code": "1F372",
+ "char": "🍲",
+ "name": "pot of food"
+ },
+ {
+ "no": 1670,
+ "code": "1F963",
+ "char": "🥣",
+ "name": "⊛ bowl with spoon"
+ },
+ {
+ "no": 1671,
+ "code": "1F957",
+ "char": "🥗",
+ "name": "green salad"
+ },
+ {
+ "no": 1672,
+ "code": "1F37F",
+ "char": "🍿",
+ "name": "popcorn"
+ },
+ {
+ "no": 1673,
+ "code": "1F96B",
+ "char": "🥫",
+ "name": "⊛ canned food"
+ },
+ {
+ "no": 1674,
+ "code": "1F371",
+ "char": "🍱",
+ "name": "bento box"
+ },
+ {
+ "no": 1675,
+ "code": "1F358",
+ "char": "🍘",
+ "name": "rice cracker"
+ },
+ {
+ "no": 1676,
+ "code": "1F359",
+ "char": "🍙",
+ "name": "rice ball"
+ },
+ {
+ "no": 1677,
+ "code": "1F35A",
+ "char": "🍚",
+ "name": "cooked rice"
+ },
+ {
+ "no": 1678,
+ "code": "1F35B",
+ "char": "🍛",
+ "name": "curry rice"
+ },
+ {
+ "no": 1679,
+ "code": "1F35C",
+ "char": "🍜",
+ "name": "steaming bowl"
+ },
+ {
+ "no": 1680,
+ "code": "1F35D",
+ "char": "🍝",
+ "name": "spaghetti"
+ },
+ {
+ "no": 1681,
+ "code": "1F360",
+ "char": "🍠",
+ "name": "roasted sweet potato"
+ },
+ {
+ "no": 1682,
+ "code": "1F362",
+ "char": "🍢",
+ "name": "oden"
+ },
+ {
+ "no": 1683,
+ "code": "1F363",
+ "char": "🍣",
+ "name": "sushi"
+ },
+ {
+ "no": 1684,
+ "code": "1F364",
+ "char": "🍤",
+ "name": "fried shrimp"
+ },
+ {
+ "no": 1685,
+ "code": "1F365",
+ "char": "🍥",
+ "name": "fish cake with swirl"
+ },
+ {
+ "no": 1686,
+ "code": "1F361",
+ "char": "🍡",
+ "name": "dango"
+ },
+ {
+ "no": 1687,
+ "code": "1F95F",
+ "char": "🥟",
+ "name": "⊛ dumpling"
+ },
+ {
+ "no": 1688,
+ "code": "1F960",
+ "char": "🥠",
+ "name": "⊛ fortune cookie"
+ },
+ {
+ "no": 1689,
+ "code": "1F961",
+ "char": "🥡",
+ "name": "⊛ takeout box"
+ },
+ {
+ "no": 1690,
+ "code": "1F366",
+ "char": "🍦",
+ "name": "soft ice cream"
+ },
+ {
+ "no": 1691,
+ "code": "1F367",
+ "char": "🍧",
+ "name": "shaved ice"
+ },
+ {
+ "no": 1692,
+ "code": "1F368",
+ "char": "🍨",
+ "name": "ice cream"
+ },
+ {
+ "no": 1693,
+ "code": "1F369",
+ "char": "🍩",
+ "name": "doughnut"
+ },
+ {
+ "no": 1694,
+ "code": "1F36A",
+ "char": "🍪",
+ "name": "cookie"
+ },
+ {
+ "no": 1695,
+ "code": "1F382",
+ "char": "🎂",
+ "name": "birthday cake"
+ },
+ {
+ "no": 1696,
+ "code": "1F370",
+ "char": "🍰",
+ "name": "shortcake"
+ },
+ {
+ "no": 1697,
+ "code": "1F967",
+ "char": "🥧",
+ "name": "⊛ pie"
+ },
+ {
+ "no": 1698,
+ "code": "1F36B",
+ "char": "🍫",
+ "name": "chocolate bar"
+ },
+ {
+ "no": 1699,
+ "code": "1F36C",
+ "char": "🍬",
+ "name": "candy"
+ },
+ {
+ "no": 1700,
+ "code": "1F36D",
+ "char": "🍭",
+ "name": "lollipop"
+ },
+ {
+ "no": 1701,
+ "code": "1F36E",
+ "char": "🍮",
+ "name": "custard"
+ },
+ {
+ "no": 1702,
+ "code": "1F36F",
+ "char": "🍯",
+ "name": "honey pot"
+ },
+ {
+ "no": 1703,
+ "code": "1F37C",
+ "char": "🍼",
+ "name": "baby bottle"
+ },
+ {
+ "no": 1704,
+ "code": "1F95B",
+ "char": "🥛",
+ "name": "glass of milk"
+ },
+ {
+ "no": 1705,
+ "code": "2615",
+ "char": "☕",
+ "name": "hot beverage"
+ },
+ {
+ "no": 1706,
+ "code": "1F375",
+ "char": "🍵",
+ "name": "teacup without handle"
+ },
+ {
+ "no": 1707,
+ "code": "1F376",
+ "char": "🍶",
+ "name": "sake"
+ },
+ {
+ "no": 1708,
+ "code": "1F37E",
+ "char": "🍾",
+ "name": "bottle with popping cork"
+ },
+ {
+ "no": 1709,
+ "code": "1F377",
+ "char": "🍷",
+ "name": "wine glass"
+ },
+ {
+ "no": 1710,
+ "code": "1F378",
+ "char": "🍸",
+ "name": "cocktail glass"
+ },
+ {
+ "no": 1711,
+ "code": "1F379",
+ "char": "🍹",
+ "name": "tropical drink"
+ },
+ {
+ "no": 1712,
+ "code": "1F37A",
+ "char": "🍺",
+ "name": "beer mug"
+ },
+ {
+ "no": 1713,
+ "code": "1F37B",
+ "char": "🍻",
+ "name": "clinking beer mugs"
+ },
+ {
+ "no": 1714,
+ "code": "1F942",
+ "char": "🥂",
+ "name": "clinking glasses"
+ },
+ {
+ "no": 1715,
+ "code": "1F943",
+ "char": "🥃",
+ "name": "tumbler glass"
+ },
+ {
+ "no": 1716,
+ "code": "1F964",
+ "char": "🥤",
+ "name": "⊛ cup with straw"
+ },
+ {
+ "no": 1717,
+ "code": "1F962",
+ "char": "🥢",
+ "name": "⊛ chopsticks"
+ },
+ {
+ "no": 1718,
+ "code": "1F37D",
+ "char": "🍽",
+ "name": "fork and knife with plate"
+ },
+ {
+ "no": 1719,
+ "code": "1F374",
+ "char": "🍴",
+ "name": "fork and knife"
+ },
+ {
+ "no": 1720,
+ "code": "1F944",
+ "char": "🥄",
+ "name": "spoon"
+ },
+ {
+ "no": 1721,
+ "code": "1F52A",
+ "char": "🔪",
+ "name": "kitchen knife"
+ },
+ {
+ "no": 1722,
+ "code": "1F3FA",
+ "char": "🏺",
+ "name": "amphora"
+ },
+ {
+ "no": 1723,
+ "code": "1F30D",
+ "char": "🌍",
+ "name": "globe showing Europe-Africa"
+ },
+ {
+ "no": 1724,
+ "code": "1F30E",
+ "char": "🌎",
+ "name": "globe showing Americas"
+ },
+ {
+ "no": 1725,
+ "code": "1F30F",
+ "char": "🌏",
+ "name": "globe showing Asia-Australia"
+ },
+ {
+ "no": 1726,
+ "code": "1F310",
+ "char": "🌐",
+ "name": "globe with meridians"
+ },
+ {
+ "no": 1727,
+ "code": "1F5FA",
+ "char": "🗺",
+ "name": "world map"
+ },
+ {
+ "no": 1728,
+ "code": "1F5FE",
+ "char": "🗾",
+ "name": "map of Japan"
+ },
+ {
+ "no": 1729,
+ "code": "1F3D4",
+ "char": "🏔",
+ "name": "snow-capped mountain"
+ },
+ {
+ "no": 1730,
+ "code": "26F0",
+ "char": "⛰",
+ "name": "mountain"
+ },
+ {
+ "no": 1731,
+ "code": "1F30B",
+ "char": "🌋",
+ "name": "volcano"
+ },
+ {
+ "no": 1732,
+ "code": "1F5FB",
+ "char": "🗻",
+ "name": "mount fuji"
+ },
+ {
+ "no": 1733,
+ "code": "1F3D5",
+ "char": "🏕",
+ "name": "camping"
+ },
+ {
+ "no": 1734,
+ "code": "1F3D6",
+ "char": "🏖",
+ "name": "beach with umbrella"
+ },
+ {
+ "no": 1735,
+ "code": "1F3DC",
+ "char": "🏜",
+ "name": "desert"
+ },
+ {
+ "no": 1736,
+ "code": "1F3DD",
+ "char": "🏝",
+ "name": "desert island"
+ },
+ {
+ "no": 1737,
+ "code": "1F3DE",
+ "char": "🏞",
+ "name": "national park"
+ },
+ {
+ "no": 1738,
+ "code": "1F3DF",
+ "char": "🏟",
+ "name": "stadium"
+ },
+ {
+ "no": 1739,
+ "code": "1F3DB",
+ "char": "🏛",
+ "name": "classical building"
+ },
+ {
+ "no": 1740,
+ "code": "1F3D7",
+ "char": "🏗",
+ "name": "building construction"
+ },
+ {
+ "no": 1741,
+ "code": "1F3D8",
+ "char": "🏘",
+ "name": "houses"
+ },
+ {
+ "no": 1742,
+ "code": "1F3D9",
+ "char": "🏙",
+ "name": "cityscape"
+ },
+ {
+ "no": 1743,
+ "code": "1F3DA",
+ "char": "🏚",
+ "name": "derelict house"
+ },
+ {
+ "no": 1744,
+ "code": "1F3E0",
+ "char": "🏠",
+ "name": "house"
+ },
+ {
+ "no": 1745,
+ "code": "1F3E1",
+ "char": "🏡",
+ "name": "house with garden"
+ },
+ {
+ "no": 1746,
+ "code": "1F3E2",
+ "char": "🏢",
+ "name": "office building"
+ },
+ {
+ "no": 1747,
+ "code": "1F3E3",
+ "char": "🏣",
+ "name": "Japanese post office"
+ },
+ {
+ "no": 1748,
+ "code": "1F3E4",
+ "char": "🏤",
+ "name": "post office"
+ },
+ {
+ "no": 1749,
+ "code": "1F3E5",
+ "char": "🏥",
+ "name": "hospital"
+ },
+ {
+ "no": 1750,
+ "code": "1F3E6",
+ "char": "🏦",
+ "name": "bank"
+ },
+ {
+ "no": 1751,
+ "code": "1F3E8",
+ "char": "🏨",
+ "name": "hotel"
+ },
+ {
+ "no": 1752,
+ "code": "1F3E9",
+ "char": "🏩",
+ "name": "love hotel"
+ },
+ {
+ "no": 1753,
+ "code": "1F3EA",
+ "char": "🏪",
+ "name": "convenience store"
+ },
+ {
+ "no": 1754,
+ "code": "1F3EB",
+ "char": "🏫",
+ "name": "school"
+ },
+ {
+ "no": 1755,
+ "code": "1F3EC",
+ "char": "🏬",
+ "name": "department store"
+ },
+ {
+ "no": 1756,
+ "code": "1F3ED",
+ "char": "🏭",
+ "name": "factory"
+ },
+ {
+ "no": 1757,
+ "code": "1F3EF",
+ "char": "🏯",
+ "name": "Japanese castle"
+ },
+ {
+ "no": 1758,
+ "code": "1F3F0",
+ "char": "🏰",
+ "name": "castle"
+ },
+ {
+ "no": 1759,
+ "code": "1F492",
+ "char": "💒",
+ "name": "wedding"
+ },
+ {
+ "no": 1760,
+ "code": "1F5FC",
+ "char": "🗼",
+ "name": "Tokyo tower"
+ },
+ {
+ "no": 1761,
+ "code": "1F5FD",
+ "char": "🗽",
+ "name": "Statue of Liberty"
+ },
+ {
+ "no": 1762,
+ "code": "26EA",
+ "char": "⛪",
+ "name": "church"
+ },
+ {
+ "no": 1763,
+ "code": "1F54C",
+ "char": "🕌",
+ "name": "mosque"
+ },
+ {
+ "no": 1764,
+ "code": "1F54D",
+ "char": "🕍",
+ "name": "synagogue"
+ },
+ {
+ "no": 1765,
+ "code": "26E9",
+ "char": "⛩",
+ "name": "shinto shrine"
+ },
+ {
+ "no": 1766,
+ "code": "1F54B",
+ "char": "🕋",
+ "name": "kaaba"
+ },
+ {
+ "no": 1767,
+ "code": "26F2",
+ "char": "⛲",
+ "name": "fountain"
+ },
+ {
+ "no": 1768,
+ "code": "26FA",
+ "char": "⛺",
+ "name": "tent"
+ },
+ {
+ "no": 1769,
+ "code": "1F301",
+ "char": "🌁",
+ "name": "foggy"
+ },
+ {
+ "no": 1770,
+ "code": "1F303",
+ "char": "🌃",
+ "name": "night with stars"
+ },
+ {
+ "no": 1771,
+ "code": "1F304",
+ "char": "🌄",
+ "name": "sunrise over mountains"
+ },
+ {
+ "no": 1772,
+ "code": "1F305",
+ "char": "🌅",
+ "name": "sunrise"
+ },
+ {
+ "no": 1773,
+ "code": "1F306",
+ "char": "🌆",
+ "name": "cityscape at dusk"
+ },
+ {
+ "no": 1774,
+ "code": "1F307",
+ "char": "🌇",
+ "name": "sunset"
+ },
+ {
+ "no": 1775,
+ "code": "1F309",
+ "char": "🌉",
+ "name": "bridge at night"
+ },
+ {
+ "no": 1776,
+ "code": "2668",
+ "char": "♨",
+ "name": "hot springs"
+ },
+ {
+ "no": 1777,
+ "code": "1F30C",
+ "char": "🌌",
+ "name": "milky way"
+ },
+ {
+ "no": 1778,
+ "code": "1F3A0",
+ "char": "🎠",
+ "name": "carousel horse"
+ },
+ {
+ "no": 1779,
+ "code": "1F3A1",
+ "char": "🎡",
+ "name": "ferris wheel"
+ },
+ {
+ "no": 1780,
+ "code": "1F3A2",
+ "char": "🎢",
+ "name": "roller coaster"
+ },
+ {
+ "no": 1781,
+ "code": "1F488",
+ "char": "💈",
+ "name": "barber pole"
+ },
+ {
+ "no": 1782,
+ "code": "1F3AA",
+ "char": "🎪",
+ "name": "circus tent"
+ },
+ {
+ "no": 1783,
+ "code": "1F3AD",
+ "char": "🎭",
+ "name": "performing arts"
+ },
+ {
+ "no": 1784,
+ "code": "1F5BC",
+ "char": "🖼",
+ "name": "framed picture"
+ },
+ {
+ "no": 1785,
+ "code": "1F3A8",
+ "char": "🎨",
+ "name": "artist palette"
+ },
+ {
+ "no": 1786,
+ "code": "1F3B0",
+ "char": "🎰",
+ "name": "slot machine"
+ },
+ {
+ "no": 1787,
+ "code": "1F682",
+ "char": "🚂",
+ "name": "locomotive"
+ },
+ {
+ "no": 1788,
+ "code": "1F683",
+ "char": "🚃",
+ "name": "railway car"
+ },
+ {
+ "no": 1789,
+ "code": "1F684",
+ "char": "🚄",
+ "name": "high-speed train"
+ },
+ {
+ "no": 1790,
+ "code": "1F685",
+ "char": "🚅",
+ "name": "bullet train"
+ },
+ {
+ "no": 1791,
+ "code": "1F686",
+ "char": "🚆",
+ "name": "train"
+ },
+ {
+ "no": 1792,
+ "code": "1F687",
+ "char": "🚇",
+ "name": "metro"
+ },
+ {
+ "no": 1793,
+ "code": "1F688",
+ "char": "🚈",
+ "name": "light rail"
+ },
+ {
+ "no": 1794,
+ "code": "1F689",
+ "char": "🚉",
+ "name": "station"
+ },
+ {
+ "no": 1795,
+ "code": "1F68A",
+ "char": "🚊",
+ "name": "tram"
+ },
+ {
+ "no": 1796,
+ "code": "1F69D",
+ "char": "🚝",
+ "name": "monorail"
+ },
+ {
+ "no": 1797,
+ "code": "1F69E",
+ "char": "🚞",
+ "name": "mountain railway"
+ },
+ {
+ "no": 1798,
+ "code": "1F68B",
+ "char": "🚋",
+ "name": "tram car"
+ },
+ {
+ "no": 1799,
+ "code": "1F68C",
+ "char": "🚌",
+ "name": "bus"
+ },
+ {
+ "no": 1800,
+ "code": "1F68D",
+ "char": "🚍",
+ "name": "oncoming bus"
+ },
+ {
+ "no": 1801,
+ "code": "1F68E",
+ "char": "🚎",
+ "name": "trolleybus"
+ },
+ {
+ "no": 1802,
+ "code": "1F690",
+ "char": "🚐",
+ "name": "minibus"
+ },
+ {
+ "no": 1803,
+ "code": "1F691",
+ "char": "🚑",
+ "name": "ambulance"
+ },
+ {
+ "no": 1804,
+ "code": "1F692",
+ "char": "🚒",
+ "name": "fire engine"
+ },
+ {
+ "no": 1805,
+ "code": "1F693",
+ "char": "🚓",
+ "name": "police car"
+ },
+ {
+ "no": 1806,
+ "code": "1F694",
+ "char": "🚔",
+ "name": "oncoming police car"
+ },
+ {
+ "no": 1807,
+ "code": "1F695",
+ "char": "🚕",
+ "name": "taxi"
+ },
+ {
+ "no": 1808,
+ "code": "1F696",
+ "char": "🚖",
+ "name": "oncoming taxi"
+ },
+ {
+ "no": 1809,
+ "code": "1F697",
+ "char": "🚗",
+ "name": "automobile"
+ },
+ {
+ "no": 1810,
+ "code": "1F698",
+ "char": "🚘",
+ "name": "oncoming automobile"
+ },
+ {
+ "no": 1811,
+ "code": "1F699",
+ "char": "🚙",
+ "name": "sport utility vehicle"
+ },
+ {
+ "no": 1812,
+ "code": "1F69A",
+ "char": "🚚",
+ "name": "delivery truck"
+ },
+ {
+ "no": 1813,
+ "code": "1F69B",
+ "char": "🚛",
+ "name": "articulated lorry"
+ },
+ {
+ "no": 1814,
+ "code": "1F69C",
+ "char": "🚜",
+ "name": "tractor"
+ },
+ {
+ "no": 1815,
+ "code": "1F6B2",
+ "char": "🚲",
+ "name": "bicycle"
+ },
+ {
+ "no": 1816,
+ "code": "1F6F4",
+ "char": "🛴",
+ "name": "kick scooter"
+ },
+ {
+ "no": 1817,
+ "code": "1F6F5",
+ "char": "🛵",
+ "name": "motor scooter"
+ },
+ {
+ "no": 1818,
+ "code": "1F68F",
+ "char": "🚏",
+ "name": "bus stop"
+ },
+ {
+ "no": 1819,
+ "code": "1F6E3",
+ "char": "🛣",
+ "name": "motorway"
+ },
+ {
+ "no": 1820,
+ "code": "1F6E4",
+ "char": "🛤",
+ "name": "railway track"
+ },
+ {
+ "no": 1821,
+ "code": "26FD",
+ "char": "⛽",
+ "name": "fuel pump"
+ },
+ {
+ "no": 1822,
+ "code": "1F6A8",
+ "char": "🚨",
+ "name": "police car light"
+ },
+ {
+ "no": 1823,
+ "code": "1F6A5",
+ "char": "🚥",
+ "name": "horizontal traffic light"
+ },
+ {
+ "no": 1824,
+ "code": "1F6A6",
+ "char": "🚦",
+ "name": "vertical traffic light"
+ },
+ {
+ "no": 1825,
+ "code": "1F6A7",
+ "char": "🚧",
+ "name": "construction"
+ },
+ {
+ "no": 1826,
+ "code": "1F6D1",
+ "char": "🛑",
+ "name": "stop sign"
+ },
+ {
+ "no": 1827,
+ "code": "2693",
+ "char": "⚓",
+ "name": "anchor"
+ },
+ {
+ "no": 1828,
+ "code": "26F5",
+ "char": "⛵",
+ "name": "sailboat"
+ },
+ {
+ "no": 1829,
+ "code": "1F6F6",
+ "char": "🛶",
+ "name": "canoe"
+ },
+ {
+ "no": 1830,
+ "code": "1F6A4",
+ "char": "🚤",
+ "name": "speedboat"
+ },
+ {
+ "no": 1831,
+ "code": "1F6F3",
+ "char": "🛳",
+ "name": "passenger ship"
+ },
+ {
+ "no": 1832,
+ "code": "26F4",
+ "char": "⛴",
+ "name": "ferry"
+ },
+ {
+ "no": 1833,
+ "code": "1F6E5",
+ "char": "🛥",
+ "name": "motor boat"
+ },
+ {
+ "no": 1834,
+ "code": "1F6A2",
+ "char": "🚢",
+ "name": "ship"
+ },
+ {
+ "no": 1835,
+ "code": "2708",
+ "char": "✈",
+ "name": "airplane"
+ },
+ {
+ "no": 1836,
+ "code": "1F6E9",
+ "char": "🛩",
+ "name": "small airplane"
+ },
+ {
+ "no": 1837,
+ "code": "1F6EB",
+ "char": "🛫",
+ "name": "airplane departure"
+ },
+ {
+ "no": 1838,
+ "code": "1F6EC",
+ "char": "🛬",
+ "name": "airplane arrival"
+ },
+ {
+ "no": 1839,
+ "code": "1F4BA",
+ "char": "💺",
+ "name": "seat"
+ },
+ {
+ "no": 1840,
+ "code": "1F681",
+ "char": "🚁",
+ "name": "helicopter"
+ },
+ {
+ "no": 1841,
+ "code": "1F69F",
+ "char": "🚟",
+ "name": "suspension railway"
+ },
+ {
+ "no": 1842,
+ "code": "1F6A0",
+ "char": "🚠",
+ "name": "mountain cableway"
+ },
+ {
+ "no": 1843,
+ "code": "1F6A1",
+ "char": "🚡",
+ "name": "aerial tramway"
+ },
+ {
+ "no": 1844,
+ "code": "1F6F0",
+ "char": "🛰",
+ "name": "satellite"
+ },
+ {
+ "no": 1845,
+ "code": "1F680",
+ "char": "🚀",
+ "name": "rocket"
+ },
+ {
+ "no": 1846,
+ "code": "1F6F8",
+ "char": "🛸",
+ "name": "⊛ flying saucer"
+ },
+ {
+ "no": 1847,
+ "code": "1F6CE",
+ "char": "🛎",
+ "name": "bellhop bell"
+ },
+ {
+ "no": 1848,
+ "code": "1F6AA",
+ "char": "🚪",
+ "name": "door"
+ },
+ {
+ "no": 1849,
+ "code": "1F6CF",
+ "char": "🛏",
+ "name": "bed"
+ },
+ {
+ "no": 1850,
+ "code": "1F6CB",
+ "char": "🛋",
+ "name": "couch and lamp"
+ },
+ {
+ "no": 1851,
+ "code": "1F6BD",
+ "char": "🚽",
+ "name": "toilet"
+ },
+ {
+ "no": 1852,
+ "code": "1F6BF",
+ "char": "🚿",
+ "name": "shower"
+ },
+ {
+ "no": 1853,
+ "code": "1F6C1",
+ "char": "🛁",
+ "name": "bathtub"
+ },
+ {
+ "no": 1854,
+ "code": "231B",
+ "char": "⌛",
+ "name": "hourglass done"
+ },
+ {
+ "no": 1855,
+ "code": "23F3",
+ "char": "⏳",
+ "name": "hourglass not done"
+ },
+ {
+ "no": 1856,
+ "code": "231A",
+ "char": "⌚",
+ "name": "watch"
+ },
+ {
+ "no": 1857,
+ "code": "23F0",
+ "char": "⏰",
+ "name": "alarm clock"
+ },
+ {
+ "no": 1858,
+ "code": "23F1",
+ "char": "⏱",
+ "name": "stopwatch"
+ },
+ {
+ "no": 1859,
+ "code": "23F2",
+ "char": "⏲",
+ "name": "timer clock"
+ },
+ {
+ "no": 1860,
+ "code": "1F570",
+ "char": "🕰",
+ "name": "mantelpiece clock"
+ },
+ {
+ "no": 1861,
+ "code": "1F55B",
+ "char": "🕛",
+ "name": "twelve o’clock"
+ },
+ {
+ "no": 1862,
+ "code": "1F567",
+ "char": "🕧",
+ "name": "twelve-thirty"
+ },
+ {
+ "no": 1863,
+ "code": "1F550",
+ "char": "🕐",
+ "name": "one o’clock"
+ },
+ {
+ "no": 1864,
+ "code": "1F55C",
+ "char": "🕜",
+ "name": "one-thirty"
+ },
+ {
+ "no": 1865,
+ "code": "1F551",
+ "char": "🕑",
+ "name": "two o’clock"
+ },
+ {
+ "no": 1866,
+ "code": "1F55D",
+ "char": "🕝",
+ "name": "two-thirty"
+ },
+ {
+ "no": 1867,
+ "code": "1F552",
+ "char": "🕒",
+ "name": "three o’clock"
+ },
+ {
+ "no": 1868,
+ "code": "1F55E",
+ "char": "🕞",
+ "name": "three-thirty"
+ },
+ {
+ "no": 1869,
+ "code": "1F553",
+ "char": "🕓",
+ "name": "four o’clock"
+ },
+ {
+ "no": 1870,
+ "code": "1F55F",
+ "char": "🕟",
+ "name": "four-thirty"
+ },
+ {
+ "no": 1871,
+ "code": "1F554",
+ "char": "🕔",
+ "name": "five o’clock"
+ },
+ {
+ "no": 1872,
+ "code": "1F560",
+ "char": "🕠",
+ "name": "five-thirty"
+ },
+ {
+ "no": 1873,
+ "code": "1F555",
+ "char": "🕕",
+ "name": "six o’clock"
+ },
+ {
+ "no": 1874,
+ "code": "1F561",
+ "char": "🕡",
+ "name": "six-thirty"
+ },
+ {
+ "no": 1875,
+ "code": "1F556",
+ "char": "🕖",
+ "name": "seven o’clock"
+ },
+ {
+ "no": 1876,
+ "code": "1F562",
+ "char": "🕢",
+ "name": "seven-thirty"
+ },
+ {
+ "no": 1877,
+ "code": "1F557",
+ "char": "🕗",
+ "name": "eight o’clock"
+ },
+ {
+ "no": 1878,
+ "code": "1F563",
+ "char": "🕣",
+ "name": "eight-thirty"
+ },
+ {
+ "no": 1879,
+ "code": "1F558",
+ "char": "🕘",
+ "name": "nine o’clock"
+ },
+ {
+ "no": 1880,
+ "code": "1F564",
+ "char": "🕤",
+ "name": "nine-thirty"
+ },
+ {
+ "no": 1881,
+ "code": "1F559",
+ "char": "🕙",
+ "name": "ten o’clock"
+ },
+ {
+ "no": 1882,
+ "code": "1F565",
+ "char": "🕥",
+ "name": "ten-thirty"
+ },
+ {
+ "no": 1883,
+ "code": "1F55A",
+ "char": "🕚",
+ "name": "eleven o’clock"
+ },
+ {
+ "no": 1884,
+ "code": "1F566",
+ "char": "🕦",
+ "name": "eleven-thirty"
+ },
+ {
+ "no": 1885,
+ "code": "1F311",
+ "char": "🌑",
+ "name": "new moon"
+ },
+ {
+ "no": 1886,
+ "code": "1F312",
+ "char": "🌒",
+ "name": "waxing crescent moon"
+ },
+ {
+ "no": 1887,
+ "code": "1F313",
+ "char": "🌓",
+ "name": "first quarter moon"
+ },
+ {
+ "no": 1888,
+ "code": "1F314",
+ "char": "🌔",
+ "name": "waxing gibbous moon"
+ },
+ {
+ "no": 1889,
+ "code": "1F315",
+ "char": "🌕",
+ "name": "full moon"
+ },
+ {
+ "no": 1890,
+ "code": "1F316",
+ "char": "🌖",
+ "name": "waning gibbous moon"
+ },
+ {
+ "no": 1891,
+ "code": "1F317",
+ "char": "🌗",
+ "name": "last quarter moon"
+ },
+ {
+ "no": 1892,
+ "code": "1F318",
+ "char": "🌘",
+ "name": "waning crescent moon"
+ },
+ {
+ "no": 1893,
+ "code": "1F319",
+ "char": "🌙",
+ "name": "crescent moon"
+ },
+ {
+ "no": 1894,
+ "code": "1F31A",
+ "char": "🌚",
+ "name": "new moon face"
+ },
+ {
+ "no": 1895,
+ "code": "1F31B",
+ "char": "🌛",
+ "name": "first quarter moon face"
+ },
+ {
+ "no": 1896,
+ "code": "1F31C",
+ "char": "🌜",
+ "name": "last quarter moon face"
+ },
+ {
+ "no": 1897,
+ "code": "1F321",
+ "char": "🌡",
+ "name": "thermometer"
+ },
+ {
+ "no": 1898,
+ "code": "2600",
+ "char": "☀",
+ "name": "sun"
+ },
+ {
+ "no": 1899,
+ "code": "1F31D",
+ "char": "🌝",
+ "name": "full moon face"
+ },
+ {
+ "no": 1900,
+ "code": "1F31E",
+ "char": "🌞",
+ "name": "sun with face"
+ },
+ {
+ "no": 1901,
+ "code": "2B50",
+ "char": "⭐",
+ "name": "white medium star"
+ },
+ {
+ "no": 1902,
+ "code": "1F31F",
+ "char": "🌟",
+ "name": "glowing star"
+ },
+ {
+ "no": 1903,
+ "code": "1F320",
+ "char": "🌠",
+ "name": "shooting star"
+ },
+ {
+ "no": 1904,
+ "code": "2601",
+ "char": "☁",
+ "name": "cloud"
+ },
+ {
+ "no": 1905,
+ "code": "26C5",
+ "char": "⛅",
+ "name": "sun behind cloud"
+ },
+ {
+ "no": 1906,
+ "code": "26C8",
+ "char": "⛈",
+ "name": "cloud with lightning and rain"
+ },
+ {
+ "no": 1907,
+ "code": "1F324",
+ "char": "🌤",
+ "name": "sun behind small cloud"
+ },
+ {
+ "no": 1908,
+ "code": "1F325",
+ "char": "🌥",
+ "name": "sun behind large cloud"
+ },
+ {
+ "no": 1909,
+ "code": "1F326",
+ "char": "🌦",
+ "name": "sun behind rain cloud"
+ },
+ {
+ "no": 1910,
+ "code": "1F327",
+ "char": "🌧",
+ "name": "cloud with rain"
+ },
+ {
+ "no": 1911,
+ "code": "1F328",
+ "char": "🌨",
+ "name": "cloud with snow"
+ },
+ {
+ "no": 1912,
+ "code": "1F329",
+ "char": "🌩",
+ "name": "cloud with lightning"
+ },
+ {
+ "no": 1913,
+ "code": "1F32A",
+ "char": "🌪",
+ "name": "tornado"
+ },
+ {
+ "no": 1914,
+ "code": "1F32B",
+ "char": "🌫",
+ "name": "fog"
+ },
+ {
+ "no": 1915,
+ "code": "1F32C",
+ "char": "🌬",
+ "name": "wind face"
+ },
+ {
+ "no": 1916,
+ "code": "1F300",
+ "char": "🌀",
+ "name": "cyclone"
+ },
+ {
+ "no": 1917,
+ "code": "1F308",
+ "char": "🌈",
+ "name": "rainbow"
+ },
+ {
+ "no": 1918,
+ "code": "1F302",
+ "char": "🌂",
+ "name": "closed umbrella"
+ },
+ {
+ "no": 1919,
+ "code": "2602",
+ "char": "☂",
+ "name": "umbrella"
+ },
+ {
+ "no": 1920,
+ "code": "2614",
+ "char": "☔",
+ "name": "umbrella with rain drops"
+ },
+ {
+ "no": 1921,
+ "code": "26F1",
+ "char": "⛱",
+ "name": "umbrella on ground"
+ },
+ {
+ "no": 1922,
+ "code": "26A1",
+ "char": "⚡",
+ "name": "high voltage"
+ },
+ {
+ "no": 1923,
+ "code": "2744",
+ "char": "❄",
+ "name": "snowflake"
+ },
+ {
+ "no": 1924,
+ "code": "2603",
+ "char": "☃",
+ "name": "snowman"
+ },
+ {
+ "no": 1925,
+ "code": "26C4",
+ "char": "⛄",
+ "name": "snowman without snow"
+ },
+ {
+ "no": 1926,
+ "code": "2604",
+ "char": "☄",
+ "name": "comet"
+ },
+ {
+ "no": 1927,
+ "code": "1F525",
+ "char": "🔥",
+ "name": "fire"
+ },
+ {
+ "no": 1928,
+ "code": "1F4A7",
+ "char": "💧",
+ "name": "droplet"
+ },
+ {
+ "no": 1929,
+ "code": "1F30A",
+ "char": "🌊",
+ "name": "water wave"
+ },
+ {
+ "no": 1930,
+ "code": "1F383",
+ "char": "🎃",
+ "name": "jack-o-lantern"
+ },
+ {
+ "no": 1931,
+ "code": "1F384",
+ "char": "🎄",
+ "name": "Christmas tree"
+ },
+ {
+ "no": 1932,
+ "code": "1F386",
+ "char": "🎆",
+ "name": "fireworks"
+ },
+ {
+ "no": 1933,
+ "code": "1F387",
+ "char": "🎇",
+ "name": "sparkler"
+ },
+ {
+ "no": 1934,
+ "code": "2728",
+ "char": "✨",
+ "name": "sparkles"
+ },
+ {
+ "no": 1935,
+ "code": "1F388",
+ "char": "🎈",
+ "name": "balloon"
+ },
+ {
+ "no": 1936,
+ "code": "1F389",
+ "char": "🎉",
+ "name": "party popper"
+ },
+ {
+ "no": 1937,
+ "code": "1F38A",
+ "char": "🎊",
+ "name": "confetti ball"
+ },
+ {
+ "no": 1938,
+ "code": "1F38B",
+ "char": "🎋",
+ "name": "tanabata tree"
+ },
+ {
+ "no": 1939,
+ "code": "1F38D",
+ "char": "🎍",
+ "name": "pine decoration"
+ },
+ {
+ "no": 1940,
+ "code": "1F38E",
+ "char": "🎎",
+ "name": "Japanese dolls"
+ },
+ {
+ "no": 1941,
+ "code": "1F38F",
+ "char": "🎏",
+ "name": "carp streamer"
+ },
+ {
+ "no": 1942,
+ "code": "1F390",
+ "char": "🎐",
+ "name": "wind chime"
+ },
+ {
+ "no": 1943,
+ "code": "1F391",
+ "char": "🎑",
+ "name": "moon viewing ceremony"
+ },
+ {
+ "no": 1944,
+ "code": "1F380",
+ "char": "🎀",
+ "name": "ribbon"
+ },
+ {
+ "no": 1945,
+ "code": "1F381",
+ "char": "🎁",
+ "name": "wrapped gift"
+ },
+ {
+ "no": 1946,
+ "code": "1F397",
+ "char": "🎗",
+ "name": "reminder ribbon"
+ },
+ {
+ "no": 1947,
+ "code": "1F39F",
+ "char": "🎟",
+ "name": "admission tickets"
+ },
+ {
+ "no": 1948,
+ "code": "1F3AB",
+ "char": "🎫",
+ "name": "ticket"
+ },
+ {
+ "no": 1949,
+ "code": "1F396",
+ "char": "🎖",
+ "name": "military medal"
+ },
+ {
+ "no": 1950,
+ "code": "1F3C6",
+ "char": "🏆",
+ "name": "trophy"
+ },
+ {
+ "no": 1951,
+ "code": "1F3C5",
+ "char": "🏅",
+ "name": "sports medal"
+ },
+ {
+ "no": 1952,
+ "code": "1F947",
+ "char": "🥇",
+ "name": "1st place medal"
+ },
+ {
+ "no": 1953,
+ "code": "1F948",
+ "char": "🥈",
+ "name": "2nd place medal"
+ },
+ {
+ "no": 1954,
+ "code": "1F949",
+ "char": "🥉",
+ "name": "3rd place medal"
+ },
+ {
+ "no": 1955,
+ "code": "26BD",
+ "char": "⚽",
+ "name": "soccer ball"
+ },
+ {
+ "no": 1956,
+ "code": "26BE",
+ "char": "⚾",
+ "name": "baseball"
+ },
+ {
+ "no": 1957,
+ "code": "1F3C0",
+ "char": "🏀",
+ "name": "basketball"
+ },
+ {
+ "no": 1958,
+ "code": "1F3D0",
+ "char": "🏐",
+ "name": "volleyball"
+ },
+ {
+ "no": 1959,
+ "code": "1F3C8",
+ "char": "🏈",
+ "name": "american football"
+ },
+ {
+ "no": 1960,
+ "code": "1F3C9",
+ "char": "🏉",
+ "name": "rugby football"
+ },
+ {
+ "no": 1961,
+ "code": "1F3BE",
+ "char": "🎾",
+ "name": "tennis"
+ },
+ {
+ "no": 1962,
+ "code": "1F3B1",
+ "char": "🎱",
+ "name": "pool 8 ball"
+ },
+ {
+ "no": 1963,
+ "code": "1F3B3",
+ "char": "🎳",
+ "name": "bowling"
+ },
+ {
+ "no": 1964,
+ "code": "1F3CF",
+ "char": "🏏",
+ "name": "cricket game"
+ },
+ {
+ "no": 1965,
+ "code": "1F3D1",
+ "char": "🏑",
+ "name": "field hockey"
+ },
+ {
+ "no": 1966,
+ "code": "1F3D2",
+ "char": "🏒",
+ "name": "ice hockey"
+ },
+ {
+ "no": 1967,
+ "code": "1F3D3",
+ "char": "🏓",
+ "name": "ping pong"
+ },
+ {
+ "no": 1968,
+ "code": "1F3F8",
+ "char": "🏸",
+ "name": "badminton"
+ },
+ {
+ "no": 1969,
+ "code": "1F94A",
+ "char": "🥊",
+ "name": "boxing glove"
+ },
+ {
+ "no": 1970,
+ "code": "1F94B",
+ "char": "🥋",
+ "name": "martial arts uniform"
+ },
+ {
+ "no": 1971,
+ "code": "1F945",
+ "char": "🥅",
+ "name": "goal net"
+ },
+ {
+ "no": 1972,
+ "code": "1F3AF",
+ "char": "🎯",
+ "name": "direct hit"
+ },
+ {
+ "no": 1973,
+ "code": "26F3",
+ "char": "⛳",
+ "name": "flag in hole"
+ },
+ {
+ "no": 1974,
+ "code": "26F8",
+ "char": "⛸",
+ "name": "ice skate"
+ },
+ {
+ "no": 1975,
+ "code": "1F3A3",
+ "char": "🎣",
+ "name": "fishing pole"
+ },
+ {
+ "no": 1976,
+ "code": "1F3BD",
+ "char": "🎽",
+ "name": "running shirt"
+ },
+ {
+ "no": 1977,
+ "code": "1F3BF",
+ "char": "🎿",
+ "name": "skis"
+ },
+ {
+ "no": 1978,
+ "code": "1F6F7",
+ "char": "🛷",
+ "name": "⊛ sled"
+ },
+ {
+ "no": 1979,
+ "code": "1F94C",
+ "char": "🥌",
+ "name": "⊛ curling stone"
+ },
+ {
+ "no": 1980,
+ "code": "1F3AE",
+ "char": "🎮",
+ "name": "video game"
+ },
+ {
+ "no": 1981,
+ "code": "1F579",
+ "char": "🕹",
+ "name": "joystick"
+ },
+ {
+ "no": 1982,
+ "code": "1F3B2",
+ "char": "🎲",
+ "name": "game die"
+ },
+ {
+ "no": 1983,
+ "code": "2660",
+ "char": "♠",
+ "name": "spade suit"
+ },
+ {
+ "no": 1984,
+ "code": "2665",
+ "char": "♥",
+ "name": "heart suit"
+ },
+ {
+ "no": 1985,
+ "code": "2666",
+ "char": "♦",
+ "name": "diamond suit"
+ },
+ {
+ "no": 1986,
+ "code": "2663",
+ "char": "♣",
+ "name": "club suit"
+ },
+ {
+ "no": 1987,
+ "code": "1F0CF",
+ "char": "🃏",
+ "name": "joker"
+ },
+ {
+ "no": 1988,
+ "code": "1F004",
+ "char": "🀄",
+ "name": "mahjong red dragon"
+ },
+ {
+ "no": 1989,
+ "code": "1F3B4",
+ "char": "🎴",
+ "name": "flower playing cards"
+ },
+ {
+ "no": 1990,
+ "code": "1F507",
+ "char": "🔇",
+ "name": "muted speaker"
+ },
+ {
+ "no": 1991,
+ "code": "1F508",
+ "char": "🔈",
+ "name": "speaker low volume"
+ },
+ {
+ "no": 1992,
+ "code": "1F509",
+ "char": "🔉",
+ "name": "speaker medium volume"
+ },
+ {
+ "no": 1993,
+ "code": "1F50A",
+ "char": "🔊",
+ "name": "speaker high volume"
+ },
+ {
+ "no": 1994,
+ "code": "1F4E2",
+ "char": "📢",
+ "name": "loudspeaker"
+ },
+ {
+ "no": 1995,
+ "code": "1F4E3",
+ "char": "📣",
+ "name": "megaphone"
+ },
+ {
+ "no": 1996,
+ "code": "1F4EF",
+ "char": "📯",
+ "name": "postal horn"
+ },
+ {
+ "no": 1997,
+ "code": "1F514",
+ "char": "🔔",
+ "name": "bell"
+ },
+ {
+ "no": 1998,
+ "code": "1F515",
+ "char": "🔕",
+ "name": "bell with slash"
+ },
+ {
+ "no": 1999,
+ "code": "1F3BC",
+ "char": "🎼",
+ "name": "musical score"
+ },
+ {
+ "no": 2000,
+ "code": "1F3B5",
+ "char": "🎵",
+ "name": "musical note"
+ },
+ {
+ "no": 2001,
+ "code": "1F3B6",
+ "char": "🎶",
+ "name": "musical notes"
+ },
+ {
+ "no": 2002,
+ "code": "1F399",
+ "char": "🎙",
+ "name": "studio microphone"
+ },
+ {
+ "no": 2003,
+ "code": "1F39A",
+ "char": "🎚",
+ "name": "level slider"
+ },
+ {
+ "no": 2004,
+ "code": "1F39B",
+ "char": "🎛",
+ "name": "control knobs"
+ },
+ {
+ "no": 2005,
+ "code": "1F3A4",
+ "char": "🎤",
+ "name": "microphone"
+ },
+ {
+ "no": 2006,
+ "code": "1F3A7",
+ "char": "🎧",
+ "name": "headphone"
+ },
+ {
+ "no": 2007,
+ "code": "1F4FB",
+ "char": "📻",
+ "name": "radio"
+ },
+ {
+ "no": 2008,
+ "code": "1F3B7",
+ "char": "🎷",
+ "name": "saxophone"
+ },
+ {
+ "no": 2009,
+ "code": "1F3B8",
+ "char": "🎸",
+ "name": "guitar"
+ },
+ {
+ "no": 2010,
+ "code": "1F3B9",
+ "char": "🎹",
+ "name": "musical keyboard"
+ },
+ {
+ "no": 2011,
+ "code": "1F3BA",
+ "char": "🎺",
+ "name": "trumpet"
+ },
+ {
+ "no": 2012,
+ "code": "1F3BB",
+ "char": "🎻",
+ "name": "violin"
+ },
+ {
+ "no": 2013,
+ "code": "1F941",
+ "char": "🥁",
+ "name": "drum"
+ },
+ {
+ "no": 2014,
+ "code": "1F4F1",
+ "char": "📱",
+ "name": "mobile phone"
+ },
+ {
+ "no": 2015,
+ "code": "1F4F2",
+ "char": "📲",
+ "name": "mobile phone with arrow"
+ },
+ {
+ "no": 2016,
+ "code": "260E",
+ "char": "☎",
+ "name": "telephone"
+ },
+ {
+ "no": 2017,
+ "code": "1F4DE",
+ "char": "📞",
+ "name": "telephone receiver"
+ },
+ {
+ "no": 2018,
+ "code": "1F4DF",
+ "char": "📟",
+ "name": "pager"
+ },
+ {
+ "no": 2019,
+ "code": "1F4E0",
+ "char": "📠",
+ "name": "fax machine"
+ },
+ {
+ "no": 2020,
+ "code": "1F50B",
+ "char": "🔋",
+ "name": "battery"
+ },
+ {
+ "no": 2021,
+ "code": "1F50C",
+ "char": "🔌",
+ "name": "electric plug"
+ },
+ {
+ "no": 2022,
+ "code": "1F4BB",
+ "char": "💻",
+ "name": "laptop computer"
+ },
+ {
+ "no": 2023,
+ "code": "1F5A5",
+ "char": "🖥",
+ "name": "desktop computer"
+ },
+ {
+ "no": 2024,
+ "code": "1F5A8",
+ "char": "🖨",
+ "name": "printer"
+ },
+ {
+ "no": 2025,
+ "code": "2328",
+ "char": "⌨",
+ "name": "keyboard"
+ },
+ {
+ "no": 2026,
+ "code": "1F5B1",
+ "char": "🖱",
+ "name": "computer mouse"
+ },
+ {
+ "no": 2027,
+ "code": "1F5B2",
+ "char": "🖲",
+ "name": "trackball"
+ },
+ {
+ "no": 2028,
+ "code": "1F4BD",
+ "char": "💽",
+ "name": "computer disk"
+ },
+ {
+ "no": 2029,
+ "code": "1F4BE",
+ "char": "💾",
+ "name": "floppy disk"
+ },
+ {
+ "no": 2030,
+ "code": "1F4BF",
+ "char": "💿",
+ "name": "optical disk"
+ },
+ {
+ "no": 2031,
+ "code": "1F4C0",
+ "char": "📀",
+ "name": "dvd"
+ },
+ {
+ "no": 2032,
+ "code": "1F3A5",
+ "char": "🎥",
+ "name": "movie camera"
+ },
+ {
+ "no": 2033,
+ "code": "1F39E",
+ "char": "🎞",
+ "name": "film frames"
+ },
+ {
+ "no": 2034,
+ "code": "1F4FD",
+ "char": "📽",
+ "name": "film projector"
+ },
+ {
+ "no": 2035,
+ "code": "1F3AC",
+ "char": "🎬",
+ "name": "clapper board"
+ },
+ {
+ "no": 2036,
+ "code": "1F4FA",
+ "char": "📺",
+ "name": "television"
+ },
+ {
+ "no": 2037,
+ "code": "1F4F7",
+ "char": "📷",
+ "name": "camera"
+ },
+ {
+ "no": 2038,
+ "code": "1F4F8",
+ "char": "📸",
+ "name": "camera with flash"
+ },
+ {
+ "no": 2039,
+ "code": "1F4F9",
+ "char": "📹",
+ "name": "video camera"
+ },
+ {
+ "no": 2040,
+ "code": "1F4FC",
+ "char": "📼",
+ "name": "videocassette"
+ },
+ {
+ "no": 2041,
+ "code": "1F50D",
+ "char": "🔍",
+ "name": "magnifying glass tilted left"
+ },
+ {
+ "no": 2042,
+ "code": "1F50E",
+ "char": "🔎",
+ "name": "magnifying glass tilted right"
+ },
+ {
+ "no": 2043,
+ "code": "1F52C",
+ "char": "🔬",
+ "name": "microscope"
+ },
+ {
+ "no": 2044,
+ "code": "1F52D",
+ "char": "🔭",
+ "name": "telescope"
+ },
+ {
+ "no": 2045,
+ "code": "1F4E1",
+ "char": "📡",
+ "name": "satellite antenna"
+ },
+ {
+ "no": 2046,
+ "code": "1F56F",
+ "char": "🕯",
+ "name": "candle"
+ },
+ {
+ "no": 2047,
+ "code": "1F4A1",
+ "char": "💡",
+ "name": "light bulb"
+ },
+ {
+ "no": 2048,
+ "code": "1F526",
+ "char": "🔦",
+ "name": "flashlight"
+ },
+ {
+ "no": 2049,
+ "code": "1F3EE",
+ "char": "🏮",
+ "name": "red paper lantern"
+ },
+ {
+ "no": 2050,
+ "code": "1F4D4",
+ "char": "📔",
+ "name": "notebook with decorative cover"
+ },
+ {
+ "no": 2051,
+ "code": "1F4D5",
+ "char": "📕",
+ "name": "closed book"
+ },
+ {
+ "no": 2052,
+ "code": "1F4D6",
+ "char": "📖",
+ "name": "open book"
+ },
+ {
+ "no": 2053,
+ "code": "1F4D7",
+ "char": "📗",
+ "name": "green book"
+ },
+ {
+ "no": 2054,
+ "code": "1F4D8",
+ "char": "📘",
+ "name": "blue book"
+ },
+ {
+ "no": 2055,
+ "code": "1F4D9",
+ "char": "📙",
+ "name": "orange book"
+ },
+ {
+ "no": 2056,
+ "code": "1F4DA",
+ "char": "📚",
+ "name": "books"
+ },
+ {
+ "no": 2057,
+ "code": "1F4D3",
+ "char": "📓",
+ "name": "notebook"
+ },
+ {
+ "no": 2058,
+ "code": "1F4D2",
+ "char": "📒",
+ "name": "ledger"
+ },
+ {
+ "no": 2059,
+ "code": "1F4C3",
+ "char": "📃",
+ "name": "page with curl"
+ },
+ {
+ "no": 2060,
+ "code": "1F4DC",
+ "char": "📜",
+ "name": "scroll"
+ },
+ {
+ "no": 2061,
+ "code": "1F4C4",
+ "char": "📄",
+ "name": "page facing up"
+ },
+ {
+ "no": 2062,
+ "code": "1F4F0",
+ "char": "📰",
+ "name": "newspaper"
+ },
+ {
+ "no": 2063,
+ "code": "1F5DE",
+ "char": "🗞",
+ "name": "rolled-up newspaper"
+ },
+ {
+ "no": 2064,
+ "code": "1F4D1",
+ "char": "📑",
+ "name": "bookmark tabs"
+ },
+ {
+ "no": 2065,
+ "code": "1F516",
+ "char": "🔖",
+ "name": "bookmark"
+ },
+ {
+ "no": 2066,
+ "code": "1F3F7",
+ "char": "🏷",
+ "name": "label"
+ },
+ {
+ "no": 2067,
+ "code": "1F4B0",
+ "char": "💰",
+ "name": "money bag"
+ },
+ {
+ "no": 2068,
+ "code": "1F4B4",
+ "char": "💴",
+ "name": "yen banknote"
+ },
+ {
+ "no": 2069,
+ "code": "1F4B5",
+ "char": "💵",
+ "name": "dollar banknote"
+ },
+ {
+ "no": 2070,
+ "code": "1F4B6",
+ "char": "💶",
+ "name": "euro banknote"
+ },
+ {
+ "no": 2071,
+ "code": "1F4B7",
+ "char": "💷",
+ "name": "pound banknote"
+ },
+ {
+ "no": 2072,
+ "code": "1F4B8",
+ "char": "💸",
+ "name": "money with wings"
+ },
+ {
+ "no": 2073,
+ "code": "1F4B3",
+ "char": "💳",
+ "name": "credit card"
+ },
+ {
+ "no": 2074,
+ "code": "1F4B9",
+ "char": "💹",
+ "name": "chart increasing with yen"
+ },
+ {
+ "no": 2075,
+ "code": "1F4B1",
+ "char": "💱",
+ "name": "currency exchange"
+ },
+ {
+ "no": 2076,
+ "code": "1F4B2",
+ "char": "💲",
+ "name": "heavy dollar sign"
+ },
+ {
+ "no": 2077,
+ "code": "2709",
+ "char": "✉",
+ "name": "envelope"
+ },
+ {
+ "no": 2078,
+ "code": "1F4E7",
+ "char": "📧",
+ "name": "e-mail"
+ },
+ {
+ "no": 2079,
+ "code": "1F4E8",
+ "char": "📨",
+ "name": "incoming envelope"
+ },
+ {
+ "no": 2080,
+ "code": "1F4E9",
+ "char": "📩",
+ "name": "envelope with arrow"
+ },
+ {
+ "no": 2081,
+ "code": "1F4E4",
+ "char": "📤",
+ "name": "outbox tray"
+ },
+ {
+ "no": 2082,
+ "code": "1F4E5",
+ "char": "📥",
+ "name": "inbox tray"
+ },
+ {
+ "no": 2083,
+ "code": "1F4E6",
+ "char": "📦",
+ "name": "package"
+ },
+ {
+ "no": 2084,
+ "code": "1F4EB",
+ "char": "📫",
+ "name": "closed mailbox with raised flag"
+ },
+ {
+ "no": 2085,
+ "code": "1F4EA",
+ "char": "📪",
+ "name": "closed mailbox with lowered flag"
+ },
+ {
+ "no": 2086,
+ "code": "1F4EC",
+ "char": "📬",
+ "name": "open mailbox with raised flag"
+ },
+ {
+ "no": 2087,
+ "code": "1F4ED",
+ "char": "📭",
+ "name": "open mailbox with lowered flag"
+ },
+ {
+ "no": 2088,
+ "code": "1F4EE",
+ "char": "📮",
+ "name": "postbox"
+ },
+ {
+ "no": 2089,
+ "code": "1F5F3",
+ "char": "🗳",
+ "name": "ballot box with ballot"
+ },
+ {
+ "no": 2090,
+ "code": "270F",
+ "char": "✏",
+ "name": "pencil"
+ },
+ {
+ "no": 2091,
+ "code": "2712",
+ "char": "✒",
+ "name": "black nib"
+ },
+ {
+ "no": 2092,
+ "code": "1F58B",
+ "char": "🖋",
+ "name": "fountain pen"
+ },
+ {
+ "no": 2093,
+ "code": "1F58A",
+ "char": "🖊",
+ "name": "pen"
+ },
+ {
+ "no": 2094,
+ "code": "1F58C",
+ "char": "🖌",
+ "name": "paintbrush"
+ },
+ {
+ "no": 2095,
+ "code": "1F58D",
+ "char": "🖍",
+ "name": "crayon"
+ },
+ {
+ "no": 2096,
+ "code": "1F4DD",
+ "char": "📝",
+ "name": "memo"
+ },
+ {
+ "no": 2097,
+ "code": "1F4BC",
+ "char": "💼",
+ "name": "briefcase"
+ },
+ {
+ "no": 2098,
+ "code": "1F4C1",
+ "char": "📁",
+ "name": "file folder"
+ },
+ {
+ "no": 2099,
+ "code": "1F4C2",
+ "char": "📂",
+ "name": "open file folder"
+ },
+ {
+ "no": 2100,
+ "code": "1F5C2",
+ "char": "🗂",
+ "name": "card index dividers"
+ },
+ {
+ "no": 2101,
+ "code": "1F4C5",
+ "char": "📅",
+ "name": "calendar"
+ },
+ {
+ "no": 2102,
+ "code": "1F4C6",
+ "char": "📆",
+ "name": "tear-off calendar"
+ },
+ {
+ "no": 2103,
+ "code": "1F5D2",
+ "char": "🗒",
+ "name": "spiral notepad"
+ },
+ {
+ "no": 2104,
+ "code": "1F5D3",
+ "char": "🗓",
+ "name": "spiral calendar"
+ },
+ {
+ "no": 2105,
+ "code": "1F4C7",
+ "char": "📇",
+ "name": "card index"
+ },
+ {
+ "no": 2106,
+ "code": "1F4C8",
+ "char": "📈",
+ "name": "chart increasing"
+ },
+ {
+ "no": 2107,
+ "code": "1F4C9",
+ "char": "📉",
+ "name": "chart decreasing"
+ },
+ {
+ "no": 2108,
+ "code": "1F4CA",
+ "char": "📊",
+ "name": "bar chart"
+ },
+ {
+ "no": 2109,
+ "code": "1F4CB",
+ "char": "📋",
+ "name": "clipboard"
+ },
+ {
+ "no": 2110,
+ "code": "1F4CC",
+ "char": "📌",
+ "name": "pushpin"
+ },
+ {
+ "no": 2111,
+ "code": "1F4CD",
+ "char": "📍",
+ "name": "round pushpin"
+ },
+ {
+ "no": 2112,
+ "code": "1F4CE",
+ "char": "📎",
+ "name": "paperclip"
+ },
+ {
+ "no": 2113,
+ "code": "1F587",
+ "char": "🖇",
+ "name": "linked paperclips"
+ },
+ {
+ "no": 2114,
+ "code": "1F4CF",
+ "char": "📏",
+ "name": "straight ruler"
+ },
+ {
+ "no": 2115,
+ "code": "1F4D0",
+ "char": "📐",
+ "name": "triangular ruler"
+ },
+ {
+ "no": 2116,
+ "code": "2702",
+ "char": "✂",
+ "name": "scissors"
+ },
+ {
+ "no": 2117,
+ "code": "1F5C3",
+ "char": "🗃",
+ "name": "card file box"
+ },
+ {
+ "no": 2118,
+ "code": "1F5C4",
+ "char": "🗄",
+ "name": "file cabinet"
+ },
+ {
+ "no": 2119,
+ "code": "1F5D1",
+ "char": "🗑",
+ "name": "wastebasket"
+ },
+ {
+ "no": 2120,
+ "code": "1F512",
+ "char": "🔒",
+ "name": "locked"
+ },
+ {
+ "no": 2121,
+ "code": "1F513",
+ "char": "🔓",
+ "name": "unlocked"
+ },
+ {
+ "no": 2122,
+ "code": "1F50F",
+ "char": "🔏",
+ "name": "locked with pen"
+ },
+ {
+ "no": 2123,
+ "code": "1F510",
+ "char": "🔐",
+ "name": "locked with key"
+ },
+ {
+ "no": 2124,
+ "code": "1F511",
+ "char": "🔑",
+ "name": "key"
+ },
+ {
+ "no": 2125,
+ "code": "1F5DD",
+ "char": "🗝",
+ "name": "old key"
+ },
+ {
+ "no": 2126,
+ "code": "1F528",
+ "char": "🔨",
+ "name": "hammer"
+ },
+ {
+ "no": 2127,
+ "code": "26CF",
+ "char": "⛏",
+ "name": "pick"
+ },
+ {
+ "no": 2128,
+ "code": "2692",
+ "char": "⚒",
+ "name": "hammer and pick"
+ },
+ {
+ "no": 2129,
+ "code": "1F6E0",
+ "char": "🛠",
+ "name": "hammer and wrench"
+ },
+ {
+ "no": 2130,
+ "code": "1F5E1",
+ "char": "🗡",
+ "name": "dagger"
+ },
+ {
+ "no": 2131,
+ "code": "2694",
+ "char": "⚔",
+ "name": "crossed swords"
+ },
+ {
+ "no": 2132,
+ "code": "1F52B",
+ "char": "🔫",
+ "name": "pistol"
+ },
+ {
+ "no": 2133,
+ "code": "1F3F9",
+ "char": "🏹",
+ "name": "bow and arrow"
+ },
+ {
+ "no": 2134,
+ "code": "1F6E1",
+ "char": "🛡",
+ "name": "shield"
+ },
+ {
+ "no": 2135,
+ "code": "1F527",
+ "char": "🔧",
+ "name": "wrench"
+ },
+ {
+ "no": 2136,
+ "code": "1F529",
+ "char": "🔩",
+ "name": "nut and bolt"
+ },
+ {
+ "no": 2137,
+ "code": "2699",
+ "char": "⚙",
+ "name": "gear"
+ },
+ {
+ "no": 2138,
+ "code": "1F5DC",
+ "char": "🗜",
+ "name": "clamp"
+ },
+ {
+ "no": 2139,
+ "code": "2697",
+ "char": "⚗",
+ "name": "alembic"
+ },
+ {
+ "no": 2140,
+ "code": "2696",
+ "char": "⚖",
+ "name": "balance scale"
+ },
+ {
+ "no": 2141,
+ "code": "1F517",
+ "char": "🔗",
+ "name": "link"
+ },
+ {
+ "no": 2142,
+ "code": "26D3",
+ "char": "⛓",
+ "name": "chains"
+ },
+ {
+ "no": 2143,
+ "code": "1F489",
+ "char": "💉",
+ "name": "syringe"
+ },
+ {
+ "no": 2144,
+ "code": "1F48A",
+ "char": "💊",
+ "name": "pill"
+ },
+ {
+ "no": 2145,
+ "code": "1F6AC",
+ "char": "🚬",
+ "name": "cigarette"
+ },
+ {
+ "no": 2146,
+ "code": "26B0",
+ "char": "⚰",
+ "name": "coffin"
+ },
+ {
+ "no": 2147,
+ "code": "26B1",
+ "char": "⚱",
+ "name": "funeral urn"
+ },
+ {
+ "no": 2148,
+ "code": "1F5FF",
+ "char": "🗿",
+ "name": "moai"
+ },
+ {
+ "no": 2149,
+ "code": "1F6E2",
+ "char": "🛢",
+ "name": "oil drum"
+ },
+ {
+ "no": 2150,
+ "code": "1F52E",
+ "char": "🔮",
+ "name": "crystal ball"
+ },
+ {
+ "no": 2151,
+ "code": "1F6D2",
+ "char": "🛒",
+ "name": "shopping cart"
+ },
+ {
+ "no": 2152,
+ "code": "1F3E7",
+ "char": "🏧",
+ "name": "ATM sign"
+ },
+ {
+ "no": 2153,
+ "code": "1F6AE",
+ "char": "🚮",
+ "name": "litter in bin sign"
+ },
+ {
+ "no": 2154,
+ "code": "1F6B0",
+ "char": "🚰",
+ "name": "potable water"
+ },
+ {
+ "no": 2155,
+ "code": "267F",
+ "char": "♿",
+ "name": "wheelchair symbol"
+ },
+ {
+ "no": 2156,
+ "code": "1F6B9",
+ "char": "🚹",
+ "name": "men’s room"
+ },
+ {
+ "no": 2157,
+ "code": "1F6BA",
+ "char": "🚺",
+ "name": "women’s room"
+ },
+ {
+ "no": 2158,
+ "code": "1F6BB",
+ "char": "🚻",
+ "name": "restroom"
+ },
+ {
+ "no": 2159,
+ "code": "1F6BC",
+ "char": "🚼",
+ "name": "baby symbol"
+ },
+ {
+ "no": 2160,
+ "code": "1F6BE",
+ "char": "🚾",
+ "name": "water closet"
+ },
+ {
+ "no": 2161,
+ "code": "1F6C2",
+ "char": "🛂",
+ "name": "passport control"
+ },
+ {
+ "no": 2162,
+ "code": "1F6C3",
+ "char": "🛃",
+ "name": "customs"
+ },
+ {
+ "no": 2163,
+ "code": "1F6C4",
+ "char": "🛄",
+ "name": "baggage claim"
+ },
+ {
+ "no": 2164,
+ "code": "1F6C5",
+ "char": "🛅",
+ "name": "left luggage"
+ },
+ {
+ "no": 2165,
+ "code": "26A0",
+ "char": "⚠",
+ "name": "warning"
+ },
+ {
+ "no": 2166,
+ "code": "1F6B8",
+ "char": "🚸",
+ "name": "children crossing"
+ },
+ {
+ "no": 2167,
+ "code": "26D4",
+ "char": "⛔",
+ "name": "no entry"
+ },
+ {
+ "no": 2168,
+ "code": "1F6AB",
+ "char": "🚫",
+ "name": "prohibited"
+ },
+ {
+ "no": 2169,
+ "code": "1F6B3",
+ "char": "🚳",
+ "name": "no bicycles"
+ },
+ {
+ "no": 2170,
+ "code": "1F6AD",
+ "char": "🚭",
+ "name": "no smoking"
+ },
+ {
+ "no": 2171,
+ "code": "1F6AF",
+ "char": "🚯",
+ "name": "no littering"
+ },
+ {
+ "no": 2172,
+ "code": "1F6B1",
+ "char": "🚱",
+ "name": "non-potable water"
+ },
+ {
+ "no": 2173,
+ "code": "1F6B7",
+ "char": "🚷",
+ "name": "no pedestrians"
+ },
+ {
+ "no": 2174,
+ "code": "1F4F5",
+ "char": "📵",
+ "name": "no mobile phones"
+ },
+ {
+ "no": 2175,
+ "code": "1F51E",
+ "char": "🔞",
+ "name": "no one under eighteen"
+ },
+ {
+ "no": 2176,
+ "code": "2622",
+ "char": "☢",
+ "name": "radioactive"
+ },
+ {
+ "no": 2177,
+ "code": "2623",
+ "char": "☣",
+ "name": "biohazard"
+ },
+ {
+ "no": 2178,
+ "code": "2B06",
+ "char": "⬆",
+ "name": "up arrow"
+ },
+ {
+ "no": 2179,
+ "code": "2197",
+ "char": "↗",
+ "name": "up-right arrow"
+ },
+ {
+ "no": 2180,
+ "code": "27A1",
+ "char": "➡",
+ "name": "right arrow"
+ },
+ {
+ "no": 2181,
+ "code": "2198",
+ "char": "↘",
+ "name": "down-right arrow"
+ },
+ {
+ "no": 2182,
+ "code": "2B07",
+ "char": "⬇",
+ "name": "down arrow"
+ },
+ {
+ "no": 2183,
+ "code": "2199",
+ "char": "↙",
+ "name": "down-left arrow"
+ },
+ {
+ "no": 2184,
+ "code": "2B05",
+ "char": "⬅",
+ "name": "left arrow"
+ },
+ {
+ "no": 2185,
+ "code": "2196",
+ "char": "↖",
+ "name": "up-left arrow"
+ },
+ {
+ "no": 2186,
+ "code": "2195",
+ "char": "↕",
+ "name": "up-down arrow"
+ },
+ {
+ "no": 2187,
+ "code": "2194",
+ "char": "↔",
+ "name": "left-right arrow"
+ },
+ {
+ "no": 2188,
+ "code": "21A9",
+ "char": "↩",
+ "name": "right arrow curving left"
+ },
+ {
+ "no": 2189,
+ "code": "21AA",
+ "char": "↪",
+ "name": "left arrow curving right"
+ },
+ {
+ "no": 2190,
+ "code": "2934",
+ "char": "⤴",
+ "name": "right arrow curving up"
+ },
+ {
+ "no": 2191,
+ "code": "2935",
+ "char": "⤵",
+ "name": "right arrow curving down"
+ },
+ {
+ "no": 2192,
+ "code": "1F503",
+ "char": "🔃",
+ "name": "clockwise vertical arrows"
+ },
+ {
+ "no": 2193,
+ "code": "1F504",
+ "char": "🔄",
+ "name": "counterclockwise arrows button"
+ },
+ {
+ "no": 2194,
+ "code": "1F519",
+ "char": "🔙",
+ "name": "BACK arrow"
+ },
+ {
+ "no": 2195,
+ "code": "1F51A",
+ "char": "🔚",
+ "name": "END arrow"
+ },
+ {
+ "no": 2196,
+ "code": "1F51B",
+ "char": "🔛",
+ "name": "ON! arrow"
+ },
+ {
+ "no": 2197,
+ "code": "1F51C",
+ "char": "🔜",
+ "name": "SOON arrow"
+ },
+ {
+ "no": 2198,
+ "code": "1F51D",
+ "char": "🔝",
+ "name": "TOP arrow"
+ },
+ {
+ "no": 2199,
+ "code": "1F6D0",
+ "char": "🛐",
+ "name": "place of worship"
+ },
+ {
+ "no": 2200,
+ "code": "269B",
+ "char": "⚛",
+ "name": "atom symbol"
+ },
+ {
+ "no": 2201,
+ "code": "1F549",
+ "char": "🕉",
+ "name": "om"
+ },
+ {
+ "no": 2202,
+ "code": "2721",
+ "char": "✡",
+ "name": "star of David"
+ },
+ {
+ "no": 2203,
+ "code": "2638",
+ "char": "☸",
+ "name": "wheel of dharma"
+ },
+ {
+ "no": 2204,
+ "code": "262F",
+ "char": "☯",
+ "name": "yin yang"
+ },
+ {
+ "no": 2205,
+ "code": "271D",
+ "char": "✝",
+ "name": "latin cross"
+ },
+ {
+ "no": 2206,
+ "code": "2626",
+ "char": "☦",
+ "name": "orthodox cross"
+ },
+ {
+ "no": 2207,
+ "code": "262A",
+ "char": "☪",
+ "name": "star and crescent"
+ },
+ {
+ "no": 2208,
+ "code": "262E",
+ "char": "☮",
+ "name": "peace symbol"
+ },
+ {
+ "no": 2209,
+ "code": "1F54E",
+ "char": "🕎",
+ "name": "menorah"
+ },
+ {
+ "no": 2210,
+ "code": "1F52F",
+ "char": "🔯",
+ "name": "dotted six-pointed star"
+ },
+ {
+ "no": 2211,
+ "code": "2648",
+ "char": "♈",
+ "name": "Aries"
+ },
+ {
+ "no": 2212,
+ "code": "2649",
+ "char": "♉",
+ "name": "Taurus"
+ },
+ {
+ "no": 2213,
+ "code": "264A",
+ "char": "♊",
+ "name": "Gemini"
+ },
+ {
+ "no": 2214,
+ "code": "264B",
+ "char": "♋",
+ "name": "Cancer"
+ },
+ {
+ "no": 2215,
+ "code": "264C",
+ "char": "♌",
+ "name": "Leo"
+ },
+ {
+ "no": 2216,
+ "code": "264D",
+ "char": "♍",
+ "name": "Virgo"
+ },
+ {
+ "no": 2217,
+ "code": "264E",
+ "char": "♎",
+ "name": "Libra"
+ },
+ {
+ "no": 2218,
+ "code": "264F",
+ "char": "♏",
+ "name": "Scorpius"
+ },
+ {
+ "no": 2219,
+ "code": "2650",
+ "char": "♐",
+ "name": "Sagittarius"
+ },
+ {
+ "no": 2220,
+ "code": "2651",
+ "char": "♑",
+ "name": "Capricorn"
+ },
+ {
+ "no": 2221,
+ "code": "2652",
+ "char": "♒",
+ "name": "Aquarius"
+ },
+ {
+ "no": 2222,
+ "code": "2653",
+ "char": "♓",
+ "name": "Pisces"
+ },
+ {
+ "no": 2223,
+ "code": "26CE",
+ "char": "⛎",
+ "name": "Ophiuchus"
+ },
+ {
+ "no": 2224,
+ "code": "1F500",
+ "char": "🔀",
+ "name": "shuffle tracks button"
+ },
+ {
+ "no": 2225,
+ "code": "1F501",
+ "char": "🔁",
+ "name": "repeat button"
+ },
+ {
+ "no": 2226,
+ "code": "1F502",
+ "char": "🔂",
+ "name": "repeat single button"
+ },
+ {
+ "no": 2227,
+ "code": "25B6",
+ "char": "▶",
+ "name": "play button"
+ },
+ {
+ "no": 2228,
+ "code": "23E9",
+ "char": "⏩",
+ "name": "fast-forward button"
+ },
+ {
+ "no": 2229,
+ "code": "23ED",
+ "char": "⏭",
+ "name": "next track button"
+ },
+ {
+ "no": 2230,
+ "code": "23EF",
+ "char": "⏯",
+ "name": "play or pause button"
+ },
+ {
+ "no": 2231,
+ "code": "25C0",
+ "char": "◀",
+ "name": "reverse button"
+ },
+ {
+ "no": 2232,
+ "code": "23EA",
+ "char": "⏪",
+ "name": "fast reverse button"
+ },
+ {
+ "no": 2233,
+ "code": "23EE",
+ "char": "⏮",
+ "name": "last track button"
+ },
+ {
+ "no": 2234,
+ "code": "1F53C",
+ "char": "🔼",
+ "name": "up button"
+ },
+ {
+ "no": 2235,
+ "code": "23EB",
+ "char": "⏫",
+ "name": "fast up button"
+ },
+ {
+ "no": 2236,
+ "code": "1F53D",
+ "char": "🔽",
+ "name": "down button"
+ },
+ {
+ "no": 2237,
+ "code": "23EC",
+ "char": "⏬",
+ "name": "fast down button"
+ },
+ {
+ "no": 2238,
+ "code": "23F8",
+ "char": "⏸",
+ "name": "pause button"
+ },
+ {
+ "no": 2239,
+ "code": "23F9",
+ "char": "⏹",
+ "name": "stop button"
+ },
+ {
+ "no": 2240,
+ "code": "23FA",
+ "char": "⏺",
+ "name": "record button"
+ },
+ {
+ "no": 2241,
+ "code": "23CF",
+ "char": "⏏",
+ "name": "eject button"
+ },
+ {
+ "no": 2242,
+ "code": "1F3A6",
+ "char": "🎦",
+ "name": "cinema"
+ },
+ {
+ "no": 2243,
+ "code": "1F505",
+ "char": "🔅",
+ "name": "dim button"
+ },
+ {
+ "no": 2244,
+ "code": "1F506",
+ "char": "🔆",
+ "name": "bright button"
+ },
+ {
+ "no": 2245,
+ "code": "1F4F6",
+ "char": "📶",
+ "name": "antenna bars"
+ },
+ {
+ "no": 2246,
+ "code": "1F4F3",
+ "char": "📳",
+ "name": "vibration mode"
+ },
+ {
+ "no": 2247,
+ "code": "1F4F4",
+ "char": "📴",
+ "name": "mobile phone off"
+ },
+ {
+ "no": 2248,
+ "code": "2640",
+ "char": "♀",
+ "name": "female sign"
+ },
+ {
+ "no": 2249,
+ "code": "2642",
+ "char": "♂",
+ "name": "male sign"
+ },
+ {
+ "no": 2250,
+ "code": "2695",
+ "char": "⚕",
+ "name": "medical symbol"
+ },
+ {
+ "no": 2251,
+ "code": "267B",
+ "char": "♻",
+ "name": "recycling symbol"
+ },
+ {
+ "no": 2252,
+ "code": "269C",
+ "char": "⚜",
+ "name": "fleur-de-lis"
+ },
+ {
+ "no": 2253,
+ "code": "1F531",
+ "char": "🔱",
+ "name": "trident emblem"
+ },
+ {
+ "no": 2254,
+ "code": "1F4DB",
+ "char": "📛",
+ "name": "name badge"
+ },
+ {
+ "no": 2255,
+ "code": "1F530",
+ "char": "🔰",
+ "name": "Japanese symbol for beginner"
+ },
+ {
+ "no": 2256,
+ "code": "2B55",
+ "char": "⭕",
+ "name": "heavy large circle"
+ },
+ {
+ "no": 2257,
+ "code": "2705",
+ "char": "✅",
+ "name": "white heavy check mark"
+ },
+ {
+ "no": 2258,
+ "code": "2611",
+ "char": "☑",
+ "name": "ballot box with check"
+ },
+ {
+ "no": 2259,
+ "code": "2714",
+ "char": "✔",
+ "name": "heavy check mark"
+ },
+ {
+ "no": 2260,
+ "code": "2716",
+ "char": "✖",
+ "name": "heavy multiplication x"
+ },
+ {
+ "no": 2261,
+ "code": "274C",
+ "char": "❌",
+ "name": "cross mark"
+ },
+ {
+ "no": 2262,
+ "code": "274E",
+ "char": "❎",
+ "name": "cross mark button"
+ },
+ {
+ "no": 2263,
+ "code": "2795",
+ "char": "➕",
+ "name": "heavy plus sign"
+ },
+ {
+ "no": 2264,
+ "code": "2796",
+ "char": "➖",
+ "name": "heavy minus sign"
+ },
+ {
+ "no": 2265,
+ "code": "2797",
+ "char": "➗",
+ "name": "heavy division sign"
+ },
+ {
+ "no": 2266,
+ "code": "27B0",
+ "char": "➰",
+ "name": "curly loop"
+ },
+ {
+ "no": 2267,
+ "code": "27BF",
+ "char": "➿",
+ "name": "double curly loop"
+ },
+ {
+ "no": 2268,
+ "code": "303D",
+ "char": "〽",
+ "name": "part alternation mark"
+ },
+ {
+ "no": 2269,
+ "code": "2733",
+ "char": "✳",
+ "name": "eight-spoked asterisk"
+ },
+ {
+ "no": 2270,
+ "code": "2734",
+ "char": "✴",
+ "name": "eight-pointed star"
+ },
+ {
+ "no": 2271,
+ "code": "2747",
+ "char": "❇",
+ "name": "sparkle"
+ },
+ {
+ "no": 2272,
+ "code": "203C",
+ "char": "‼",
+ "name": "double exclamation mark"
+ },
+ {
+ "no": 2273,
+ "code": "2049",
+ "char": "⁉",
+ "name": "exclamation question mark"
+ },
+ {
+ "no": 2274,
+ "code": "2753",
+ "char": "❓",
+ "name": "question mark"
+ },
+ {
+ "no": 2275,
+ "code": "2754",
+ "char": "❔",
+ "name": "white question mark"
+ },
+ {
+ "no": 2276,
+ "code": "2755",
+ "char": "❕",
+ "name": "white exclamation mark"
+ },
+ {
+ "no": 2277,
+ "code": "2757",
+ "char": "❗",
+ "name": "exclamation mark"
+ },
+ {
+ "no": 2278,
+ "code": "3030",
+ "char": "〰",
+ "name": "wavy dash"
+ },
+ {
+ "no": 2279,
+ "code": "00A9",
+ "char": "©",
+ "name": "copyright"
+ },
+ {
+ "no": 2280,
+ "code": "00AE",
+ "char": "®",
+ "name": "registered"
+ },
+ {
+ "no": 2281,
+ "code": "2122",
+ "char": "™",
+ "name": "trade mark"
+ },
+ {
+ "no": 2282,
+ "code": "0023 FE0F 20E3",
+ "char": "#️⃣",
+ "name": "keycap: #"
+ },
+ {
+ "no": 2283,
+ "code": "002A FE0F 20E3",
+ "char": "*️⃣",
+ "name": "keycap: *"
+ },
+ {
+ "no": 2284,
+ "code": "0030 FE0F 20E3",
+ "char": "0️⃣",
+ "name": "keycap: 0"
+ },
+ {
+ "no": 2285,
+ "code": "0031 FE0F 20E3",
+ "char": "1️⃣",
+ "name": "keycap: 1"
+ },
+ {
+ "no": 2286,
+ "code": "0032 FE0F 20E3",
+ "char": "2️⃣",
+ "name": "keycap: 2"
+ },
+ {
+ "no": 2287,
+ "code": "0033 FE0F 20E3",
+ "char": "3️⃣",
+ "name": "keycap: 3"
+ },
+ {
+ "no": 2288,
+ "code": "0034 FE0F 20E3",
+ "char": "4️⃣",
+ "name": "keycap: 4"
+ },
+ {
+ "no": 2289,
+ "code": "0035 FE0F 20E3",
+ "char": "5️⃣",
+ "name": "keycap: 5"
+ },
+ {
+ "no": 2290,
+ "code": "0036 FE0F 20E3",
+ "char": "6️⃣",
+ "name": "keycap: 6"
+ },
+ {
+ "no": 2291,
+ "code": "0037 FE0F 20E3",
+ "char": "7️⃣",
+ "name": "keycap: 7"
+ },
+ {
+ "no": 2292,
+ "code": "0038 FE0F 20E3",
+ "char": "8️⃣",
+ "name": "keycap: 8"
+ },
+ {
+ "no": 2293,
+ "code": "0039 FE0F 20E3",
+ "char": "9️⃣",
+ "name": "keycap: 9"
+ },
+ {
+ "no": 2294,
+ "code": "1F51F",
+ "char": "🔟",
+ "name": "keycap 10"
+ },
+ {
+ "no": 2295,
+ "code": "1F4AF",
+ "char": "💯",
+ "name": "hundred points"
+ },
+ {
+ "no": 2296,
+ "code": "1F520",
+ "char": "🔠",
+ "name": "input latin uppercase"
+ },
+ {
+ "no": 2297,
+ "code": "1F521",
+ "char": "🔡",
+ "name": "input latin lowercase"
+ },
+ {
+ "no": 2298,
+ "code": "1F522",
+ "char": "🔢",
+ "name": "input numbers"
+ },
+ {
+ "no": 2299,
+ "code": "1F523",
+ "char": "🔣",
+ "name": "input symbols"
+ },
+ {
+ "no": 2300,
+ "code": "1F524",
+ "char": "🔤",
+ "name": "input latin letters"
+ },
+ {
+ "no": 2301,
+ "code": "1F170",
+ "char": "🅰",
+ "name": "A button (blood type)"
+ },
+ {
+ "no": 2302,
+ "code": "1F18E",
+ "char": "🆎",
+ "name": "AB button (blood type)"
+ },
+ {
+ "no": 2303,
+ "code": "1F171",
+ "char": "🅱",
+ "name": "B button (blood type)"
+ },
+ {
+ "no": 2304,
+ "code": "1F191",
+ "char": "🆑",
+ "name": "CL button"
+ },
+ {
+ "no": 2305,
+ "code": "1F192",
+ "char": "🆒",
+ "name": "COOL button"
+ },
+ {
+ "no": 2306,
+ "code": "1F193",
+ "char": "🆓",
+ "name": "FREE button"
+ },
+ {
+ "no": 2307,
+ "code": "2139",
+ "char": "ℹ",
+ "name": "information"
+ },
+ {
+ "no": 2308,
+ "code": "1F194",
+ "char": "🆔",
+ "name": "ID button"
+ },
+ {
+ "no": 2309,
+ "code": "24C2",
+ "char": "Ⓜ",
+ "name": "circled M"
+ },
+ {
+ "no": 2310,
+ "code": "1F195",
+ "char": "🆕",
+ "name": "NEW button"
+ },
+ {
+ "no": 2311,
+ "code": "1F196",
+ "char": "🆖",
+ "name": "NG button"
+ },
+ {
+ "no": 2312,
+ "code": "1F17E",
+ "char": "🅾",
+ "name": "O button (blood type)"
+ },
+ {
+ "no": 2313,
+ "code": "1F197",
+ "char": "🆗",
+ "name": "OK button"
+ },
+ {
+ "no": 2314,
+ "code": "1F17F",
+ "char": "🅿",
+ "name": "P button"
+ },
+ {
+ "no": 2315,
+ "code": "1F198",
+ "char": "🆘",
+ "name": "SOS button"
+ },
+ {
+ "no": 2316,
+ "code": "1F199",
+ "char": "🆙",
+ "name": "UP! button"
+ },
+ {
+ "no": 2317,
+ "code": "1F19A",
+ "char": "🆚",
+ "name": "VS button"
+ },
+ {
+ "no": 2318,
+ "code": "1F201",
+ "char": "🈁",
+ "name": "Japanese “here” button"
+ },
+ {
+ "no": 2319,
+ "code": "1F202",
+ "char": "🈂",
+ "name": "Japanese “service charge” button"
+ },
+ {
+ "no": 2320,
+ "code": "1F237",
+ "char": "🈷",
+ "name": "Japanese “monthly amount” button"
+ },
+ {
+ "no": 2321,
+ "code": "1F236",
+ "char": "🈶",
+ "name": "Japanese “not free of charge” button"
+ },
+ {
+ "no": 2322,
+ "code": "1F22F",
+ "char": "🈯",
+ "name": "Japanese “reserved” button"
+ },
+ {
+ "no": 2323,
+ "code": "1F250",
+ "char": "🉐",
+ "name": "Japanese “bargain” button"
+ },
+ {
+ "no": 2324,
+ "code": "1F239",
+ "char": "🈹",
+ "name": "Japanese “discount” button"
+ },
+ {
+ "no": 2325,
+ "code": "1F21A",
+ "char": "🈚",
+ "name": "Japanese “free of charge” button"
+ },
+ {
+ "no": 2326,
+ "code": "1F232",
+ "char": "🈲",
+ "name": "Japanese “prohibited” button"
+ },
+ {
+ "no": 2327,
+ "code": "1F251",
+ "char": "🉑",
+ "name": "Japanese “acceptable” button"
+ },
+ {
+ "no": 2328,
+ "code": "1F238",
+ "char": "🈸",
+ "name": "Japanese “application” button"
+ },
+ {
+ "no": 2329,
+ "code": "1F234",
+ "char": "🈴",
+ "name": "Japanese “passing grade” button"
+ },
+ {
+ "no": 2330,
+ "code": "1F233",
+ "char": "🈳",
+ "name": "Japanese “vacancy” button"
+ },
+ {
+ "no": 2331,
+ "code": "3297",
+ "char": "㊗",
+ "name": "Japanese “congratulations” button"
+ },
+ {
+ "no": 2332,
+ "code": "3299",
+ "char": "㊙",
+ "name": "Japanese “secret” button"
+ },
+ {
+ "no": 2333,
+ "code": "1F23A",
+ "char": "🈺",
+ "name": "Japanese “open for business” button"
+ },
+ {
+ "no": 2334,
+ "code": "1F235",
+ "char": "🈵",
+ "name": "Japanese “no vacancy” button"
+ },
+ {
+ "no": 2335,
+ "code": "25AA",
+ "char": "▪",
+ "name": "black small square"
+ },
+ {
+ "no": 2336,
+ "code": "25AB",
+ "char": "▫",
+ "name": "white small square"
+ },
+ {
+ "no": 2337,
+ "code": "25FB",
+ "char": "◻",
+ "name": "white medium square"
+ },
+ {
+ "no": 2338,
+ "code": "25FC",
+ "char": "◼",
+ "name": "black medium square"
+ },
+ {
+ "no": 2339,
+ "code": "25FD",
+ "char": "◽",
+ "name": "white medium-small square"
+ },
+ {
+ "no": 2340,
+ "code": "25FE",
+ "char": "◾",
+ "name": "black medium-small square"
+ },
+ {
+ "no": 2341,
+ "code": "2B1B",
+ "char": "⬛",
+ "name": "black large square"
+ },
+ {
+ "no": 2342,
+ "code": "2B1C",
+ "char": "⬜",
+ "name": "white large square"
+ },
+ {
+ "no": 2343,
+ "code": "1F536",
+ "char": "🔶",
+ "name": "large orange diamond"
+ },
+ {
+ "no": 2344,
+ "code": "1F537",
+ "char": "🔷",
+ "name": "large blue diamond"
+ },
+ {
+ "no": 2345,
+ "code": "1F538",
+ "char": "🔸",
+ "name": "small orange diamond"
+ },
+ {
+ "no": 2346,
+ "code": "1F539",
+ "char": "🔹",
+ "name": "small blue diamond"
+ },
+ {
+ "no": 2347,
+ "code": "1F53A",
+ "char": "🔺",
+ "name": "red triangle pointed up"
+ },
+ {
+ "no": 2348,
+ "code": "1F53B",
+ "char": "🔻",
+ "name": "red triangle pointed down"
+ },
+ {
+ "no": 2349,
+ "code": "1F4A0",
+ "char": "💠",
+ "name": "diamond with a dot"
+ },
+ {
+ "no": 2350,
+ "code": "1F518",
+ "char": "🔘",
+ "name": "radio button"
+ },
+ {
+ "no": 2351,
+ "code": "1F532",
+ "char": "🔲",
+ "name": "black square button"
+ },
+ {
+ "no": 2352,
+ "code": "1F533",
+ "char": "🔳",
+ "name": "white square button"
+ },
+ {
+ "no": 2353,
+ "code": "26AA",
+ "char": "⚪",
+ "name": "white circle"
+ },
+ {
+ "no": 2354,
+ "code": "26AB",
+ "char": "⚫",
+ "name": "black circle"
+ },
+ {
+ "no": 2355,
+ "code": "1F534",
+ "char": "🔴",
+ "name": "red circle"
+ },
+ {
+ "no": 2356,
+ "code": "1F535",
+ "char": "🔵",
+ "name": "blue circle"
+ },
+ {
+ "no": 2357,
+ "code": "1F3C1",
+ "char": "🏁",
+ "name": "chequered flag"
+ },
+ {
+ "no": 2358,
+ "code": "1F6A9",
+ "char": "🚩",
+ "name": "triangular flag"
+ },
+ {
+ "no": 2359,
+ "code": "1F38C",
+ "char": "🎌",
+ "name": "crossed flags"
+ },
+ {
+ "no": 2360,
+ "code": "1F3F4",
+ "char": "🏴",
+ "name": "black flag"
+ },
+ {
+ "no": 2361,
+ "code": "1F3F3",
+ "char": "🏳",
+ "name": "white flag"
+ },
+ {
+ "no": 2362,
+ "code": "1F3F3 FE0F 200D 1F308",
+ "char": "🏳️‍🌈",
+ "name": "rainbow flag"
+ },
+ {
+ "no": 2363,
+ "code": "1F1E6 1F1E8",
+ "char": "🇦🇨",
+ "name": "Ascension Island"
+ },
+ {
+ "no": 2364,
+ "code": "1F1E6 1F1E9",
+ "char": "🇦🇩",
+ "name": "Andorra"
+ },
+ {
+ "no": 2365,
+ "code": "1F1E6 1F1EA",
+ "char": "🇦🇪",
+ "name": "United Arab Emirates"
+ },
+ {
+ "no": 2366,
+ "code": "1F1E6 1F1EB",
+ "char": "🇦🇫",
+ "name": "Afghanistan"
+ },
+ {
+ "no": 2367,
+ "code": "1F1E6 1F1EC",
+ "char": "🇦🇬",
+ "name": "Antigua & Barbuda"
+ },
+ {
+ "no": 2368,
+ "code": "1F1E6 1F1EE",
+ "char": "🇦🇮",
+ "name": "Anguilla"
+ },
+ {
+ "no": 2369,
+ "code": "1F1E6 1F1F1",
+ "char": "🇦🇱",
+ "name": "Albania"
+ },
+ {
+ "no": 2370,
+ "code": "1F1E6 1F1F2",
+ "char": "🇦🇲",
+ "name": "Armenia"
+ },
+ {
+ "no": 2371,
+ "code": "1F1E6 1F1F4",
+ "char": "🇦🇴",
+ "name": "Angola"
+ },
+ {
+ "no": 2372,
+ "code": "1F1E6 1F1F6",
+ "char": "🇦🇶",
+ "name": "Antarctica"
+ },
+ {
+ "no": 2373,
+ "code": "1F1E6 1F1F7",
+ "char": "🇦🇷",
+ "name": "Argentina"
+ },
+ {
+ "no": 2374,
+ "code": "1F1E6 1F1F8",
+ "char": "🇦🇸",
+ "name": "American Samoa"
+ },
+ {
+ "no": 2375,
+ "code": "1F1E6 1F1F9",
+ "char": "🇦🇹",
+ "name": "Austria"
+ },
+ {
+ "no": 2376,
+ "code": "1F1E6 1F1FA",
+ "char": "🇦🇺",
+ "name": "Australia"
+ },
+ {
+ "no": 2377,
+ "code": "1F1E6 1F1FC",
+ "char": "🇦🇼",
+ "name": "Aruba"
+ },
+ {
+ "no": 2378,
+ "code": "1F1E6 1F1FD",
+ "char": "🇦🇽",
+ "name": "Åland Islands"
+ },
+ {
+ "no": 2379,
+ "code": "1F1E6 1F1FF",
+ "char": "🇦🇿",
+ "name": "Azerbaijan"
+ },
+ {
+ "no": 2380,
+ "code": "1F1E7 1F1E6",
+ "char": "🇧🇦",
+ "name": "Bosnia & Herzegovina"
+ },
+ {
+ "no": 2381,
+ "code": "1F1E7 1F1E7",
+ "char": "🇧🇧",
+ "name": "Barbados"
+ },
+ {
+ "no": 2382,
+ "code": "1F1E7 1F1E9",
+ "char": "🇧🇩",
+ "name": "Bangladesh"
+ },
+ {
+ "no": 2383,
+ "code": "1F1E7 1F1EA",
+ "char": "🇧🇪",
+ "name": "Belgium"
+ },
+ {
+ "no": 2384,
+ "code": "1F1E7 1F1EB",
+ "char": "🇧🇫",
+ "name": "Burkina Faso"
+ },
+ {
+ "no": 2385,
+ "code": "1F1E7 1F1EC",
+ "char": "🇧🇬",
+ "name": "Bulgaria"
+ },
+ {
+ "no": 2386,
+ "code": "1F1E7 1F1ED",
+ "char": "🇧🇭",
+ "name": "Bahrain"
+ },
+ {
+ "no": 2387,
+ "code": "1F1E7 1F1EE",
+ "char": "🇧🇮",
+ "name": "Burundi"
+ },
+ {
+ "no": 2388,
+ "code": "1F1E7 1F1EF",
+ "char": "🇧🇯",
+ "name": "Benin"
+ },
+ {
+ "no": 2389,
+ "code": "1F1E7 1F1F1",
+ "char": "🇧🇱",
+ "name": "St. Barthélemy"
+ },
+ {
+ "no": 2390,
+ "code": "1F1E7 1F1F2",
+ "char": "🇧🇲",
+ "name": "Bermuda"
+ },
+ {
+ "no": 2391,
+ "code": "1F1E7 1F1F3",
+ "char": "🇧🇳",
+ "name": "Brunei"
+ },
+ {
+ "no": 2392,
+ "code": "1F1E7 1F1F4",
+ "char": "🇧🇴",
+ "name": "Bolivia"
+ },
+ {
+ "no": 2393,
+ "code": "1F1E7 1F1F6",
+ "char": "🇧🇶",
+ "name": "Caribbean Netherlands"
+ },
+ {
+ "no": 2394,
+ "code": "1F1E7 1F1F7",
+ "char": "🇧🇷",
+ "name": "Brazil"
+ },
+ {
+ "no": 2395,
+ "code": "1F1E7 1F1F8",
+ "char": "🇧🇸",
+ "name": "Bahamas"
+ },
+ {
+ "no": 2396,
+ "code": "1F1E7 1F1F9",
+ "char": "🇧🇹",
+ "name": "Bhutan"
+ },
+ {
+ "no": 2397,
+ "code": "1F1E7 1F1FB",
+ "char": "🇧🇻",
+ "name": "Bouvet Island"
+ },
+ {
+ "no": 2398,
+ "code": "1F1E7 1F1FC",
+ "char": "🇧🇼",
+ "name": "Botswana"
+ },
+ {
+ "no": 2399,
+ "code": "1F1E7 1F1FE",
+ "char": "🇧🇾",
+ "name": "Belarus"
+ },
+ {
+ "no": 2400,
+ "code": "1F1E7 1F1FF",
+ "char": "🇧🇿",
+ "name": "Belize"
+ },
+ {
+ "no": 2401,
+ "code": "1F1E8 1F1E6",
+ "char": "🇨🇦",
+ "name": "Canada"
+ },
+ {
+ "no": 2402,
+ "code": "1F1E8 1F1E8",
+ "char": "🇨🇨",
+ "name": "Cocos (Keeling) Islands"
+ },
+ {
+ "no": 2403,
+ "code": "1F1E8 1F1E9",
+ "char": "🇨🇩",
+ "name": "Congo - Kinshasa"
+ },
+ {
+ "no": 2404,
+ "code": "1F1E8 1F1EB",
+ "char": "🇨🇫",
+ "name": "Central African Republic"
+ },
+ {
+ "no": 2405,
+ "code": "1F1E8 1F1EC",
+ "char": "🇨🇬",
+ "name": "Congo - Brazzaville"
+ },
+ {
+ "no": 2406,
+ "code": "1F1E8 1F1ED",
+ "char": "🇨🇭",
+ "name": "Switzerland"
+ },
+ {
+ "no": 2407,
+ "code": "1F1E8 1F1EE",
+ "char": "🇨🇮",
+ "name": "Côte d’Ivoire"
+ },
+ {
+ "no": 2408,
+ "code": "1F1E8 1F1F0",
+ "char": "🇨🇰",
+ "name": "Cook Islands"
+ },
+ {
+ "no": 2409,
+ "code": "1F1E8 1F1F1",
+ "char": "🇨🇱",
+ "name": "Chile"
+ },
+ {
+ "no": 2410,
+ "code": "1F1E8 1F1F2",
+ "char": "🇨🇲",
+ "name": "Cameroon"
+ },
+ {
+ "no": 2411,
+ "code": "1F1E8 1F1F3",
+ "char": "🇨🇳",
+ "name": "China"
+ },
+ {
+ "no": 2412,
+ "code": "1F1E8 1F1F4",
+ "char": "🇨🇴",
+ "name": "Colombia"
+ },
+ {
+ "no": 2413,
+ "code": "1F1E8 1F1F5",
+ "char": "🇨🇵",
+ "name": "Clipperton Island"
+ },
+ {
+ "no": 2414,
+ "code": "1F1E8 1F1F7",
+ "char": "🇨🇷",
+ "name": "Costa Rica"
+ },
+ {
+ "no": 2415,
+ "code": "1F1E8 1F1FA",
+ "char": "🇨🇺",
+ "name": "Cuba"
+ },
+ {
+ "no": 2416,
+ "code": "1F1E8 1F1FB",
+ "char": "🇨🇻",
+ "name": "Cape Verde"
+ },
+ {
+ "no": 2417,
+ "code": "1F1E8 1F1FC",
+ "char": "🇨🇼",
+ "name": "Curaçao"
+ },
+ {
+ "no": 2418,
+ "code": "1F1E8 1F1FD",
+ "char": "🇨🇽",
+ "name": "Christmas Island"
+ },
+ {
+ "no": 2419,
+ "code": "1F1E8 1F1FE",
+ "char": "🇨🇾",
+ "name": "Cyprus"
+ },
+ {
+ "no": 2420,
+ "code": "1F1E8 1F1FF",
+ "char": "🇨🇿",
+ "name": "Czechia"
+ },
+ {
+ "no": 2421,
+ "code": "1F1E9 1F1EA",
+ "char": "🇩🇪",
+ "name": "Germany"
+ },
+ {
+ "no": 2422,
+ "code": "1F1E9 1F1EC",
+ "char": "🇩🇬",
+ "name": "Diego Garcia"
+ },
+ {
+ "no": 2423,
+ "code": "1F1E9 1F1EF",
+ "char": "🇩🇯",
+ "name": "Djibouti"
+ },
+ {
+ "no": 2424,
+ "code": "1F1E9 1F1F0",
+ "char": "🇩🇰",
+ "name": "Denmark"
+ },
+ {
+ "no": 2425,
+ "code": "1F1E9 1F1F2",
+ "char": "🇩🇲",
+ "name": "Dominica"
+ },
+ {
+ "no": 2426,
+ "code": "1F1E9 1F1F4",
+ "char": "🇩🇴",
+ "name": "Dominican Republic"
+ },
+ {
+ "no": 2427,
+ "code": "1F1E9 1F1FF",
+ "char": "🇩🇿",
+ "name": "Algeria"
+ },
+ {
+ "no": 2428,
+ "code": "1F1EA 1F1E6",
+ "char": "🇪🇦",
+ "name": "Ceuta & Melilla"
+ },
+ {
+ "no": 2429,
+ "code": "1F1EA 1F1E8",
+ "char": "🇪🇨",
+ "name": "Ecuador"
+ },
+ {
+ "no": 2430,
+ "code": "1F1EA 1F1EA",
+ "char": "🇪🇪",
+ "name": "Estonia"
+ },
+ {
+ "no": 2431,
+ "code": "1F1EA 1F1EC",
+ "char": "🇪🇬",
+ "name": "Egypt"
+ },
+ {
+ "no": 2432,
+ "code": "1F1EA 1F1ED",
+ "char": "🇪🇭",
+ "name": "Western Sahara"
+ },
+ {
+ "no": 2433,
+ "code": "1F1EA 1F1F7",
+ "char": "🇪🇷",
+ "name": "Eritrea"
+ },
+ {
+ "no": 2434,
+ "code": "1F1EA 1F1F8",
+ "char": "🇪🇸",
+ "name": "Spain"
+ },
+ {
+ "no": 2435,
+ "code": "1F1EA 1F1F9",
+ "char": "🇪🇹",
+ "name": "Ethiopia"
+ },
+ {
+ "no": 2436,
+ "code": "1F1EA 1F1FA",
+ "char": "🇪🇺",
+ "name": "European Union"
+ },
+ {
+ "no": 2437,
+ "code": "1F1EB 1F1EE",
+ "char": "🇫🇮",
+ "name": "Finland"
+ },
+ {
+ "no": 2438,
+ "code": "1F1EB 1F1EF",
+ "char": "🇫🇯",
+ "name": "Fiji"
+ },
+ {
+ "no": 2439,
+ "code": "1F1EB 1F1F0",
+ "char": "🇫🇰",
+ "name": "Falkland Islands"
+ },
+ {
+ "no": 2440,
+ "code": "1F1EB 1F1F2",
+ "char": "🇫🇲",
+ "name": "Micronesia"
+ },
+ {
+ "no": 2441,
+ "code": "1F1EB 1F1F4",
+ "char": "🇫🇴",
+ "name": "Faroe Islands"
+ },
+ {
+ "no": 2442,
+ "code": "1F1EB 1F1F7",
+ "char": "🇫🇷",
+ "name": "France"
+ },
+ {
+ "no": 2443,
+ "code": "1F1EC 1F1E6",
+ "char": "🇬🇦",
+ "name": "Gabon"
+ },
+ {
+ "no": 2444,
+ "code": "1F1EC 1F1E7",
+ "char": "🇬🇧",
+ "name": "United Kingdom"
+ },
+ {
+ "no": 2445,
+ "code": "1F1EC 1F1E9",
+ "char": "🇬🇩",
+ "name": "Grenada"
+ },
+ {
+ "no": 2446,
+ "code": "1F1EC 1F1EA",
+ "char": "🇬🇪",
+ "name": "Georgia"
+ },
+ {
+ "no": 2447,
+ "code": "1F1EC 1F1EB",
+ "char": "🇬🇫",
+ "name": "French Guiana"
+ },
+ {
+ "no": 2448,
+ "code": "1F1EC 1F1EC",
+ "char": "🇬🇬",
+ "name": "Guernsey"
+ },
+ {
+ "no": 2449,
+ "code": "1F1EC 1F1ED",
+ "char": "🇬🇭",
+ "name": "Ghana"
+ },
+ {
+ "no": 2450,
+ "code": "1F1EC 1F1EE",
+ "char": "🇬🇮",
+ "name": "Gibraltar"
+ },
+ {
+ "no": 2451,
+ "code": "1F1EC 1F1F1",
+ "char": "🇬🇱",
+ "name": "Greenland"
+ },
+ {
+ "no": 2452,
+ "code": "1F1EC 1F1F2",
+ "char": "🇬🇲",
+ "name": "Gambia"
+ },
+ {
+ "no": 2453,
+ "code": "1F1EC 1F1F3",
+ "char": "🇬🇳",
+ "name": "Guinea"
+ },
+ {
+ "no": 2454,
+ "code": "1F1EC 1F1F5",
+ "char": "🇬🇵",
+ "name": "Guadeloupe"
+ },
+ {
+ "no": 2455,
+ "code": "1F1EC 1F1F6",
+ "char": "🇬🇶",
+ "name": "Equatorial Guinea"
+ },
+ {
+ "no": 2456,
+ "code": "1F1EC 1F1F7",
+ "char": "🇬🇷",
+ "name": "Greece"
+ },
+ {
+ "no": 2457,
+ "code": "1F1EC 1F1F8",
+ "char": "🇬🇸",
+ "name": "South Georgia & South Sandwich Islands"
+ },
+ {
+ "no": 2458,
+ "code": "1F1EC 1F1F9",
+ "char": "🇬🇹",
+ "name": "Guatemala"
+ },
+ {
+ "no": 2459,
+ "code": "1F1EC 1F1FA",
+ "char": "🇬🇺",
+ "name": "Guam"
+ },
+ {
+ "no": 2460,
+ "code": "1F1EC 1F1FC",
+ "char": "🇬🇼",
+ "name": "Guinea-Bissau"
+ },
+ {
+ "no": 2461,
+ "code": "1F1EC 1F1FE",
+ "char": "🇬🇾",
+ "name": "Guyana"
+ },
+ {
+ "no": 2462,
+ "code": "1F1ED 1F1F0",
+ "char": "🇭🇰",
+ "name": "Hong Kong SAR China"
+ },
+ {
+ "no": 2463,
+ "code": "1F1ED 1F1F2",
+ "char": "🇭🇲",
+ "name": "Heard & McDonald Islands"
+ },
+ {
+ "no": 2464,
+ "code": "1F1ED 1F1F3",
+ "char": "🇭🇳",
+ "name": "Honduras"
+ },
+ {
+ "no": 2465,
+ "code": "1F1ED 1F1F7",
+ "char": "🇭🇷",
+ "name": "Croatia"
+ },
+ {
+ "no": 2466,
+ "code": "1F1ED 1F1F9",
+ "char": "🇭🇹",
+ "name": "Haiti"
+ },
+ {
+ "no": 2467,
+ "code": "1F1ED 1F1FA",
+ "char": "🇭🇺",
+ "name": "Hungary"
+ },
+ {
+ "no": 2468,
+ "code": "1F1EE 1F1E8",
+ "char": "🇮🇨",
+ "name": "Canary Islands"
+ },
+ {
+ "no": 2469,
+ "code": "1F1EE 1F1E9",
+ "char": "🇮🇩",
+ "name": "Indonesia"
+ },
+ {
+ "no": 2470,
+ "code": "1F1EE 1F1EA",
+ "char": "🇮🇪",
+ "name": "Ireland"
+ },
+ {
+ "no": 2471,
+ "code": "1F1EE 1F1F1",
+ "char": "🇮🇱",
+ "name": "Israel"
+ },
+ {
+ "no": 2472,
+ "code": "1F1EE 1F1F2",
+ "char": "🇮🇲",
+ "name": "Isle of Man"
+ },
+ {
+ "no": 2473,
+ "code": "1F1EE 1F1F3",
+ "char": "🇮🇳",
+ "name": "India"
+ },
+ {
+ "no": 2474,
+ "code": "1F1EE 1F1F4",
+ "char": "🇮🇴",
+ "name": "British Indian Ocean Territory"
+ },
+ {
+ "no": 2475,
+ "code": "1F1EE 1F1F6",
+ "char": "🇮🇶",
+ "name": "Iraq"
+ },
+ {
+ "no": 2476,
+ "code": "1F1EE 1F1F7",
+ "char": "🇮🇷",
+ "name": "Iran"
+ },
+ {
+ "no": 2477,
+ "code": "1F1EE 1F1F8",
+ "char": "🇮🇸",
+ "name": "Iceland"
+ },
+ {
+ "no": 2478,
+ "code": "1F1EE 1F1F9",
+ "char": "🇮🇹",
+ "name": "Italy"
+ },
+ {
+ "no": 2479,
+ "code": "1F1EF 1F1EA",
+ "char": "🇯🇪",
+ "name": "Jersey"
+ },
+ {
+ "no": 2480,
+ "code": "1F1EF 1F1F2",
+ "char": "🇯🇲",
+ "name": "Jamaica"
+ },
+ {
+ "no": 2481,
+ "code": "1F1EF 1F1F4",
+ "char": "🇯🇴",
+ "name": "Jordan"
+ },
+ {
+ "no": 2482,
+ "code": "1F1EF 1F1F5",
+ "char": "🇯🇵",
+ "name": "Japan"
+ },
+ {
+ "no": 2483,
+ "code": "1F1F0 1F1EA",
+ "char": "🇰🇪",
+ "name": "Kenya"
+ },
+ {
+ "no": 2484,
+ "code": "1F1F0 1F1EC",
+ "char": "🇰🇬",
+ "name": "Kyrgyzstan"
+ },
+ {
+ "no": 2485,
+ "code": "1F1F0 1F1ED",
+ "char": "🇰🇭",
+ "name": "Cambodia"
+ },
+ {
+ "no": 2486,
+ "code": "1F1F0 1F1EE",
+ "char": "🇰🇮",
+ "name": "Kiribati"
+ },
+ {
+ "no": 2487,
+ "code": "1F1F0 1F1F2",
+ "char": "🇰🇲",
+ "name": "Comoros"
+ },
+ {
+ "no": 2488,
+ "code": "1F1F0 1F1F3",
+ "char": "🇰🇳",
+ "name": "St. Kitts & Nevis"
+ },
+ {
+ "no": 2489,
+ "code": "1F1F0 1F1F5",
+ "char": "🇰🇵",
+ "name": "North Korea"
+ },
+ {
+ "no": 2490,
+ "code": "1F1F0 1F1F7",
+ "char": "🇰🇷",
+ "name": "South Korea"
+ },
+ {
+ "no": 2491,
+ "code": "1F1F0 1F1FC",
+ "char": "🇰🇼",
+ "name": "Kuwait"
+ },
+ {
+ "no": 2492,
+ "code": "1F1F0 1F1FE",
+ "char": "🇰🇾",
+ "name": "Cayman Islands"
+ },
+ {
+ "no": 2493,
+ "code": "1F1F0 1F1FF",
+ "char": "🇰🇿",
+ "name": "Kazakhstan"
+ },
+ {
+ "no": 2494,
+ "code": "1F1F1 1F1E6",
+ "char": "🇱🇦",
+ "name": "Laos"
+ },
+ {
+ "no": 2495,
+ "code": "1F1F1 1F1E7",
+ "char": "🇱🇧",
+ "name": "Lebanon"
+ },
+ {
+ "no": 2496,
+ "code": "1F1F1 1F1E8",
+ "char": "🇱🇨",
+ "name": "St. Lucia"
+ },
+ {
+ "no": 2497,
+ "code": "1F1F1 1F1EE",
+ "char": "🇱🇮",
+ "name": "Liechtenstein"
+ },
+ {
+ "no": 2498,
+ "code": "1F1F1 1F1F0",
+ "char": "🇱🇰",
+ "name": "Sri Lanka"
+ },
+ {
+ "no": 2499,
+ "code": "1F1F1 1F1F7",
+ "char": "🇱🇷",
+ "name": "Liberia"
+ },
+ {
+ "no": 2500,
+ "code": "1F1F1 1F1F8",
+ "char": "🇱🇸",
+ "name": "Lesotho"
+ },
+ {
+ "no": 2501,
+ "code": "1F1F1 1F1F9",
+ "char": "🇱🇹",
+ "name": "Lithuania"
+ },
+ {
+ "no": 2502,
+ "code": "1F1F1 1F1FA",
+ "char": "🇱🇺",
+ "name": "Luxembourg"
+ },
+ {
+ "no": 2503,
+ "code": "1F1F1 1F1FB",
+ "char": "🇱🇻",
+ "name": "Latvia"
+ },
+ {
+ "no": 2504,
+ "code": "1F1F1 1F1FE",
+ "char": "🇱🇾",
+ "name": "Libya"
+ },
+ {
+ "no": 2505,
+ "code": "1F1F2 1F1E6",
+ "char": "🇲🇦",
+ "name": "Morocco"
+ },
+ {
+ "no": 2506,
+ "code": "1F1F2 1F1E8",
+ "char": "🇲🇨",
+ "name": "Monaco"
+ },
+ {
+ "no": 2507,
+ "code": "1F1F2 1F1E9",
+ "char": "🇲🇩",
+ "name": "Moldova"
+ },
+ {
+ "no": 2508,
+ "code": "1F1F2 1F1EA",
+ "char": "🇲🇪",
+ "name": "Montenegro"
+ },
+ {
+ "no": 2509,
+ "code": "1F1F2 1F1EB",
+ "char": "🇲🇫",
+ "name": "St. Martin"
+ },
+ {
+ "no": 2510,
+ "code": "1F1F2 1F1EC",
+ "char": "🇲🇬",
+ "name": "Madagascar"
+ },
+ {
+ "no": 2511,
+ "code": "1F1F2 1F1ED",
+ "char": "🇲🇭",
+ "name": "Marshall Islands"
+ },
+ {
+ "no": 2512,
+ "code": "1F1F2 1F1F0",
+ "char": "🇲🇰",
+ "name": "Macedonia"
+ },
+ {
+ "no": 2513,
+ "code": "1F1F2 1F1F1",
+ "char": "🇲🇱",
+ "name": "Mali"
+ },
+ {
+ "no": 2514,
+ "code": "1F1F2 1F1F2",
+ "char": "🇲🇲",
+ "name": "Myanmar (Burma)"
+ },
+ {
+ "no": 2515,
+ "code": "1F1F2 1F1F3",
+ "char": "🇲🇳",
+ "name": "Mongolia"
+ },
+ {
+ "no": 2516,
+ "code": "1F1F2 1F1F4",
+ "char": "🇲🇴",
+ "name": "Macau SAR China"
+ },
+ {
+ "no": 2517,
+ "code": "1F1F2 1F1F5",
+ "char": "🇲🇵",
+ "name": "Northern Mariana Islands"
+ },
+ {
+ "no": 2518,
+ "code": "1F1F2 1F1F6",
+ "char": "🇲🇶",
+ "name": "Martinique"
+ },
+ {
+ "no": 2519,
+ "code": "1F1F2 1F1F7",
+ "char": "🇲🇷",
+ "name": "Mauritania"
+ },
+ {
+ "no": 2520,
+ "code": "1F1F2 1F1F8",
+ "char": "🇲🇸",
+ "name": "Montserrat"
+ },
+ {
+ "no": 2521,
+ "code": "1F1F2 1F1F9",
+ "char": "🇲🇹",
+ "name": "Malta"
+ },
+ {
+ "no": 2522,
+ "code": "1F1F2 1F1FA",
+ "char": "🇲🇺",
+ "name": "Mauritius"
+ },
+ {
+ "no": 2523,
+ "code": "1F1F2 1F1FB",
+ "char": "🇲🇻",
+ "name": "Maldives"
+ },
+ {
+ "no": 2524,
+ "code": "1F1F2 1F1FC",
+ "char": "🇲🇼",
+ "name": "Malawi"
+ },
+ {
+ "no": 2525,
+ "code": "1F1F2 1F1FD",
+ "char": "🇲🇽",
+ "name": "Mexico"
+ },
+ {
+ "no": 2526,
+ "code": "1F1F2 1F1FE",
+ "char": "🇲🇾",
+ "name": "Malaysia"
+ },
+ {
+ "no": 2527,
+ "code": "1F1F2 1F1FF",
+ "char": "🇲🇿",
+ "name": "Mozambique"
+ },
+ {
+ "no": 2528,
+ "code": "1F1F3 1F1E6",
+ "char": "🇳🇦",
+ "name": "Namibia"
+ },
+ {
+ "no": 2529,
+ "code": "1F1F3 1F1E8",
+ "char": "🇳🇨",
+ "name": "New Caledonia"
+ },
+ {
+ "no": 2530,
+ "code": "1F1F3 1F1EA",
+ "char": "🇳🇪",
+ "name": "Niger"
+ },
+ {
+ "no": 2531,
+ "code": "1F1F3 1F1EB",
+ "char": "🇳🇫",
+ "name": "Norfolk Island"
+ },
+ {
+ "no": 2532,
+ "code": "1F1F3 1F1EC",
+ "char": "🇳🇬",
+ "name": "Nigeria"
+ },
+ {
+ "no": 2533,
+ "code": "1F1F3 1F1EE",
+ "char": "🇳🇮",
+ "name": "Nicaragua"
+ },
+ {
+ "no": 2534,
+ "code": "1F1F3 1F1F1",
+ "char": "🇳🇱",
+ "name": "Netherlands"
+ },
+ {
+ "no": 2535,
+ "code": "1F1F3 1F1F4",
+ "char": "🇳🇴",
+ "name": "Norway"
+ },
+ {
+ "no": 2536,
+ "code": "1F1F3 1F1F5",
+ "char": "🇳🇵",
+ "name": "Nepal"
+ },
+ {
+ "no": 2537,
+ "code": "1F1F3 1F1F7",
+ "char": "🇳🇷",
+ "name": "Nauru"
+ },
+ {
+ "no": 2538,
+ "code": "1F1F3 1F1FA",
+ "char": "🇳🇺",
+ "name": "Niue"
+ },
+ {
+ "no": 2539,
+ "code": "1F1F3 1F1FF",
+ "char": "🇳🇿",
+ "name": "New Zealand"
+ },
+ {
+ "no": 2540,
+ "code": "1F1F4 1F1F2",
+ "char": "🇴🇲",
+ "name": "Oman"
+ },
+ {
+ "no": 2541,
+ "code": "1F1F5 1F1E6",
+ "char": "🇵🇦",
+ "name": "Panama"
+ },
+ {
+ "no": 2542,
+ "code": "1F1F5 1F1EA",
+ "char": "🇵🇪",
+ "name": "Peru"
+ },
+ {
+ "no": 2543,
+ "code": "1F1F5 1F1EB",
+ "char": "🇵🇫",
+ "name": "French Polynesia"
+ },
+ {
+ "no": 2544,
+ "code": "1F1F5 1F1EC",
+ "char": "🇵🇬",
+ "name": "Papua New Guinea"
+ },
+ {
+ "no": 2545,
+ "code": "1F1F5 1F1ED",
+ "char": "🇵🇭",
+ "name": "Philippines"
+ },
+ {
+ "no": 2546,
+ "code": "1F1F5 1F1F0",
+ "char": "🇵🇰",
+ "name": "Pakistan"
+ },
+ {
+ "no": 2547,
+ "code": "1F1F5 1F1F1",
+ "char": "🇵🇱",
+ "name": "Poland"
+ },
+ {
+ "no": 2548,
+ "code": "1F1F5 1F1F2",
+ "char": "🇵🇲",
+ "name": "St. Pierre & Miquelon"
+ },
+ {
+ "no": 2549,
+ "code": "1F1F5 1F1F3",
+ "char": "🇵🇳",
+ "name": "Pitcairn Islands"
+ },
+ {
+ "no": 2550,
+ "code": "1F1F5 1F1F7",
+ "char": "🇵🇷",
+ "name": "Puerto Rico"
+ },
+ {
+ "no": 2551,
+ "code": "1F1F5 1F1F8",
+ "char": "🇵🇸",
+ "name": "Palestinian Territories"
+ },
+ {
+ "no": 2552,
+ "code": "1F1F5 1F1F9",
+ "char": "🇵🇹",
+ "name": "Portugal"
+ },
+ {
+ "no": 2553,
+ "code": "1F1F5 1F1FC",
+ "char": "🇵🇼",
+ "name": "Palau"
+ },
+ {
+ "no": 2554,
+ "code": "1F1F5 1F1FE",
+ "char": "🇵🇾",
+ "name": "Paraguay"
+ },
+ {
+ "no": 2555,
+ "code": "1F1F6 1F1E6",
+ "char": "🇶🇦",
+ "name": "Qatar"
+ },
+ {
+ "no": 2556,
+ "code": "1F1F7 1F1EA",
+ "char": "🇷🇪",
+ "name": "Réunion"
+ },
+ {
+ "no": 2557,
+ "code": "1F1F7 1F1F4",
+ "char": "🇷🇴",
+ "name": "Romania"
+ },
+ {
+ "no": 2558,
+ "code": "1F1F7 1F1F8",
+ "char": "🇷🇸",
+ "name": "Serbia"
+ },
+ {
+ "no": 2559,
+ "code": "1F1F7 1F1FA",
+ "char": "🇷🇺",
+ "name": "Russia"
+ },
+ {
+ "no": 2560,
+ "code": "1F1F7 1F1FC",
+ "char": "🇷🇼",
+ "name": "Rwanda"
+ },
+ {
+ "no": 2561,
+ "code": "1F1F8 1F1E6",
+ "char": "🇸🇦",
+ "name": "Saudi Arabia"
+ },
+ {
+ "no": 2562,
+ "code": "1F1F8 1F1E7",
+ "char": "🇸🇧",
+ "name": "Solomon Islands"
+ },
+ {
+ "no": 2563,
+ "code": "1F1F8 1F1E8",
+ "char": "🇸🇨",
+ "name": "Seychelles"
+ },
+ {
+ "no": 2564,
+ "code": "1F1F8 1F1E9",
+ "char": "🇸🇩",
+ "name": "Sudan"
+ },
+ {
+ "no": 2565,
+ "code": "1F1F8 1F1EA",
+ "char": "🇸🇪",
+ "name": "Sweden"
+ },
+ {
+ "no": 2566,
+ "code": "1F1F8 1F1EC",
+ "char": "🇸🇬",
+ "name": "Singapore"
+ },
+ {
+ "no": 2567,
+ "code": "1F1F8 1F1ED",
+ "char": "🇸🇭",
+ "name": "St. Helena"
+ },
+ {
+ "no": 2568,
+ "code": "1F1F8 1F1EE",
+ "char": "🇸🇮",
+ "name": "Slovenia"
+ },
+ {
+ "no": 2569,
+ "code": "1F1F8 1F1EF",
+ "char": "🇸🇯",
+ "name": "Svalbard & Jan Mayen"
+ },
+ {
+ "no": 2570,
+ "code": "1F1F8 1F1F0",
+ "char": "🇸🇰",
+ "name": "Slovakia"
+ },
+ {
+ "no": 2571,
+ "code": "1F1F8 1F1F1",
+ "char": "🇸🇱",
+ "name": "Sierra Leone"
+ },
+ {
+ "no": 2572,
+ "code": "1F1F8 1F1F2",
+ "char": "🇸🇲",
+ "name": "San Marino"
+ },
+ {
+ "no": 2573,
+ "code": "1F1F8 1F1F3",
+ "char": "🇸🇳",
+ "name": "Senegal"
+ },
+ {
+ "no": 2574,
+ "code": "1F1F8 1F1F4",
+ "char": "🇸🇴",
+ "name": "Somalia"
+ },
+ {
+ "no": 2575,
+ "code": "1F1F8 1F1F7",
+ "char": "🇸🇷",
+ "name": "Suriname"
+ },
+ {
+ "no": 2576,
+ "code": "1F1F8 1F1F8",
+ "char": "🇸🇸",
+ "name": "South Sudan"
+ },
+ {
+ "no": 2577,
+ "code": "1F1F8 1F1F9",
+ "char": "🇸🇹",
+ "name": "São Tomé & Príncipe"
+ },
+ {
+ "no": 2578,
+ "code": "1F1F8 1F1FB",
+ "char": "🇸🇻",
+ "name": "El Salvador"
+ },
+ {
+ "no": 2579,
+ "code": "1F1F8 1F1FD",
+ "char": "🇸🇽",
+ "name": "Sint Maarten"
+ },
+ {
+ "no": 2580,
+ "code": "1F1F8 1F1FE",
+ "char": "🇸🇾",
+ "name": "Syria"
+ },
+ {
+ "no": 2581,
+ "code": "1F1F8 1F1FF",
+ "char": "🇸🇿",
+ "name": "Swaziland"
+ },
+ {
+ "no": 2582,
+ "code": "1F1F9 1F1E6",
+ "char": "🇹🇦",
+ "name": "Tristan da Cunha"
+ },
+ {
+ "no": 2583,
+ "code": "1F1F9 1F1E8",
+ "char": "🇹🇨",
+ "name": "Turks & Caicos Islands"
+ },
+ {
+ "no": 2584,
+ "code": "1F1F9 1F1E9",
+ "char": "🇹🇩",
+ "name": "Chad"
+ },
+ {
+ "no": 2585,
+ "code": "1F1F9 1F1EB",
+ "char": "🇹🇫",
+ "name": "French Southern Territories"
+ },
+ {
+ "no": 2586,
+ "code": "1F1F9 1F1EC",
+ "char": "🇹🇬",
+ "name": "Togo"
+ },
+ {
+ "no": 2587,
+ "code": "1F1F9 1F1ED",
+ "char": "🇹🇭",
+ "name": "Thailand"
+ },
+ {
+ "no": 2588,
+ "code": "1F1F9 1F1EF",
+ "char": "🇹🇯",
+ "name": "Tajikistan"
+ },
+ {
+ "no": 2589,
+ "code": "1F1F9 1F1F0",
+ "char": "🇹🇰",
+ "name": "Tokelau"
+ },
+ {
+ "no": 2590,
+ "code": "1F1F9 1F1F1",
+ "char": "🇹🇱",
+ "name": "Timor-Leste"
+ },
+ {
+ "no": 2591,
+ "code": "1F1F9 1F1F2",
+ "char": "🇹🇲",
+ "name": "Turkmenistan"
+ },
+ {
+ "no": 2592,
+ "code": "1F1F9 1F1F3",
+ "char": "🇹🇳",
+ "name": "Tunisia"
+ },
+ {
+ "no": 2593,
+ "code": "1F1F9 1F1F4",
+ "char": "🇹🇴",
+ "name": "Tonga"
+ },
+ {
+ "no": 2594,
+ "code": "1F1F9 1F1F7",
+ "char": "🇹🇷",
+ "name": "Turkey"
+ },
+ {
+ "no": 2595,
+ "code": "1F1F9 1F1F9",
+ "char": "🇹🇹",
+ "name": "Trinidad & Tobago"
+ },
+ {
+ "no": 2596,
+ "code": "1F1F9 1F1FB",
+ "char": "🇹🇻",
+ "name": "Tuvalu"
+ },
+ {
+ "no": 2597,
+ "code": "1F1F9 1F1FC",
+ "char": "🇹🇼",
+ "name": "Taiwan"
+ },
+ {
+ "no": 2598,
+ "code": "1F1F9 1F1FF",
+ "char": "🇹🇿",
+ "name": "Tanzania"
+ },
+ {
+ "no": 2599,
+ "code": "1F1FA 1F1E6",
+ "char": "🇺🇦",
+ "name": "Ukraine"
+ },
+ {
+ "no": 2600,
+ "code": "1F1FA 1F1EC",
+ "char": "🇺🇬",
+ "name": "Uganda"
+ },
+ {
+ "no": 2601,
+ "code": "1F1FA 1F1F2",
+ "char": "🇺🇲",
+ "name": "U.S. Outlying Islands"
+ },
+ {
+ "no": 2602,
+ "code": "1F1FA 1F1F3",
+ "char": "🇺🇳",
+ "name": "United Nations"
+ },
+ {
+ "no": 2603,
+ "code": "1F1FA 1F1F8",
+ "char": "🇺🇸",
+ "name": "United States"
+ },
+ {
+ "no": 2604,
+ "code": "1F1FA 1F1FE",
+ "char": "🇺🇾",
+ "name": "Uruguay"
+ },
+ {
+ "no": 2605,
+ "code": "1F1FA 1F1FF",
+ "char": "🇺🇿",
+ "name": "Uzbekistan"
+ },
+ {
+ "no": 2606,
+ "code": "1F1FB 1F1E6",
+ "char": "🇻🇦",
+ "name": "Vatican City"
+ },
+ {
+ "no": 2607,
+ "code": "1F1FB 1F1E8",
+ "char": "🇻🇨",
+ "name": "St. Vincent & Grenadines"
+ },
+ {
+ "no": 2608,
+ "code": "1F1FB 1F1EA",
+ "char": "🇻🇪",
+ "name": "Venezuela"
+ },
+ {
+ "no": 2609,
+ "code": "1F1FB 1F1EC",
+ "char": "🇻🇬",
+ "name": "British Virgin Islands"
+ },
+ {
+ "no": 2610,
+ "code": "1F1FB 1F1EE",
+ "char": "🇻🇮",
+ "name": "U.S. Virgin Islands"
+ },
+ {
+ "no": 2611,
+ "code": "1F1FB 1F1F3",
+ "char": "🇻🇳",
+ "name": "Vietnam"
+ },
+ {
+ "no": 2612,
+ "code": "1F1FB 1F1FA",
+ "char": "🇻🇺",
+ "name": "Vanuatu"
+ },
+ {
+ "no": 2613,
+ "code": "1F1FC 1F1EB",
+ "char": "🇼🇫",
+ "name": "Wallis & Futuna"
+ },
+ {
+ "no": 2614,
+ "code": "1F1FC 1F1F8",
+ "char": "🇼🇸",
+ "name": "Samoa"
+ },
+ {
+ "no": 2615,
+ "code": "1F1FD 1F1F0",
+ "char": "🇽🇰",
+ "name": "Kosovo"
+ },
+ {
+ "no": 2616,
+ "code": "1F1FE 1F1EA",
+ "char": "🇾🇪",
+ "name": "Yemen"
+ },
+ {
+ "no": 2617,
+ "code": "1F1FE 1F1F9",
+ "char": "🇾🇹",
+ "name": "Mayotte"
+ },
+ {
+ "no": 2618,
+ "code": "1F1FF 1F1E6",
+ "char": "🇿🇦",
+ "name": "South Africa"
+ },
+ {
+ "no": 2619,
+ "code": "1F1FF 1F1F2",
+ "char": "🇿🇲",
+ "name": "Zambia"
+ },
+ {
+ "no": 2620,
+ "code": "1F1FF 1F1FC",
+ "char": "🇿🇼",
+ "name": "Zimbabwe"
+ },
+ {
+ "no": 2621,
+ "code": "1F3F4 E0067 E0062 E0065 E006E E0067 E007F",
+ "char": "🏴󠁧󠁢󠁥󠁮󠁧󠁿",
+ "name": "⊛ England"
+ },
+ {
+ "no": 2622,
+ "code": "1F3F4 E0067 E0062 E0073 E0063 E0074 E007F",
+ "char": "🏴󠁧󠁢󠁳󠁣󠁴󠁿",
+ "name": "⊛ Scotland"
+ },
+ {
+ "no": 2623,
+ "code": "1F3F4 E0067 E0062 E0077 E006C E0073 E007F",
+ "char": "🏴󠁧󠁢󠁷󠁬󠁳󠁿",
+ "name": "⊛ Wales"
+ }
+]
diff --git a/data/gnome-shell-dbus-interfaces.gresource.xml b/data/gnome-shell-dbus-interfaces.gresource.xml
new file mode 100644
index 0000000..6682c46
--- /dev/null
+++ b/data/gnome-shell-dbus-interfaces.gresource.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/shell/dbus-interfaces">
+ <file preprocess="xml-stripblanks">net.hadess.PowerProfiles.xml</file>
+ <file preprocess="xml-stripblanks">net.hadess.SensorProxy.xml</file>
+ <file preprocess="xml-stripblanks">net.reactivated.Fprint.Device.xml</file>
+ <file preprocess="xml-stripblanks">net.reactivated.Fprint.Manager.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.Application.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.bolt1.Device.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.bolt1.Manager.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.DBus.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.GeoClue2.Agent.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.GeoClue2.Manager.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.impl.portal.Access.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.impl.portal.PermissionStore.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.impl.portal.Request.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.login1.Manager.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.login1.Session.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.login1.User.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.ModemManager1.Modem.Modem3gpp.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.ModemManager1.Modem.ModemCdma.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.ModemManager1.Modem.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.ModemManager.Modem.Cdma.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.ModemManager.Modem.Gsm.Network.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.Notifications.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.PackageKit.Offline.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.realmd.Provider.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.realmd.Realm.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.realmd.Service.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.UPower.Device.xml</file>
+ <file preprocess="xml-stripblanks">org.freedesktop.UPower.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Mutter.ScreenCast.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.ScreenSaver.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SessionManager.EndSessionDialog.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SessionManager.Inhibitor.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SessionManager.Presence.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SessionManager.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Color.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Power.Screen.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Rfkill.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Wacom.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.AudioDeviceSelection.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.CalendarServer.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.ClocksIntegration.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.Extensions.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.Introspect.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.HotplugSniffer.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.PerfHelper.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.PortalHelper.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.Screencast.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.Screenshot.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.Wacom.PadOsd.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.WeatherIntegration.xml</file>
+ <file preprocess="xml-stripblanks">org.gnome.Shell.xml</file>
+ <file preprocess="xml-stripblanks">org.Gtk.MountOperationHandler.xml</file>
+ <file preprocess="xml-stripblanks">org.gtk.Notifications.xml</file>
+ <file preprocess="xml-stripblanks">org.mpris.MediaPlayer2.Player.xml</file>
+ <file preprocess="xml-stripblanks">org.mpris.MediaPlayer2.xml</file>
+ </gresource>
+</gresources>
diff --git a/data/gnome-shell-icons.gresource.xml b/data/gnome-shell-icons.gresource.xml
new file mode 100644
index 0000000..9b4e6f3
--- /dev/null
+++ b/data/gnome-shell-icons.gresource.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/shell/icons">
+ <file>scalable/actions/color-pick.svg</file>
+ <file>scalable/actions/carousel-arrow-next-symbolic.svg</file>
+ <file>scalable/actions/carousel-arrow-previous-symbolic.svg</file>
+ <file>scalable/actions/dark-mode-symbolic.svg</file>
+ <file>scalable/actions/pointer-double-click-symbolic.svg</file>
+ <file>scalable/actions/pointer-drag-symbolic.svg</file>
+ <file>scalable/actions/pointer-primary-click-symbolic.svg</file>
+ <file>scalable/actions/pointer-secondary-click-symbolic.svg</file>
+ <file>scalable/actions/preview-close-symbolic.svg</file>
+ <file>scalable/actions/screenshot-ui-area-symbolic.svg</file>
+ <file>scalable/actions/screenshot-ui-display-symbolic.svg</file>
+ <file>scalable/actions/screenshot-ui-show-pointer-symbolic.svg</file>
+ <file>scalable/actions/screenshot-ui-window-symbolic.svg</file>
+ <file>scalable/actions/record-screen-symbolic.svg</file>
+ <file>scalable/actions/screencast-recorded-symbolic.svg</file>
+ <file>scalable/actions/screenshot-recorded-symbolic.svg</file>
+ <file>scalable/status/keyboard-caps-lock-symbolic.svg</file>
+ <file>scalable/status/keyboard-enter-symbolic.svg</file>
+ <file>scalable/status/keyboard-hide-symbolic.svg</file>
+ <file>scalable/status/keyboard-layout-symbolic.svg</file>
+ <file>scalable/status/keyboard-shift-symbolic.svg</file>
+ <file>scalable/status/message-indicator-symbolic.svg</file>
+ <file>scalable/status/no-events-symbolic.svg</file>
+ <file>scalable/status/no-notifications-symbolic.svg</file>
+ <file>scalable/status/screen-privacy-disabled-symbolic.svg</file>
+ <file>scalable/status/screen-privacy-symbolic.svg</file>
+ <file>scalable/status/stop-symbolic.svg</file>
+ </gresource>
+</gresources>
+
diff --git a/data/gnome-shell-osk-layouts.gresource.xml b/data/gnome-shell-osk-layouts.gresource.xml
new file mode 100644
index 0000000..b511291
--- /dev/null
+++ b/data/gnome-shell-osk-layouts.gresource.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/shell/osk-layouts">
+ <file>am.json</file>
+ <file>ara.json</file>
+ <file>at.json</file>
+ <file>be.json</file>
+ <file>bg.json</file>
+ <file>by.json</file>
+ <file>ca.json</file>
+ <file>ch.json</file>
+ <file>ch+fr.json</file>
+ <file>cz.json</file>
+ <file>de.json</file>
+ <file>dk.json</file>
+ <file>ee.json</file>
+ <file>epo.json</file>
+ <file>es+cat.json</file>
+ <file>es.json</file>
+ <file>fi.json</file>
+ <file>fr.json</file>
+ <file>ge.json</file>
+ <file>gr.json</file>
+ <file>hr.json</file>
+ <file>hu.json</file>
+ <file>id.json</file>
+ <file>il.json</file>
+ <file>in+bolnagri.json</file>
+ <file>in+mal.json</file>
+ <file>ir.json</file>
+ <file>is.json</file>
+ <file>it.json</file>
+ <file>ke.json</file>
+ <file>kg.json</file>
+ <file>kh.json</file>
+ <file>kr.json</file>
+ <file>la.json</file>
+ <file>latam.json</file>
+ <file>lt.json</file>
+ <file>lv.json</file>
+ <file>mk.json</file>
+ <file>mn.json</file>
+ <file>my.json</file>
+ <file>nl.json</file>
+ <file>no.json</file>
+ <file>ph.json</file>
+ <file>pl.json</file>
+ <file>pt.json</file>
+ <file>ro.json</file>
+ <file>rs.json</file>
+ <file>ru.json</file>
+ <file>se.json</file>
+ <file>si.json</file>
+ <file>sk.json</file>
+ <file>th.json</file>
+ <file>tr.json</file>
+ <file>ua.json</file>
+ <file>uk.json</file>
+ <file>us.json</file>
+ <file>us-extended.json</file>
+ <file>vn.json</file>
+ <file>za.json</file>
+ <file>emoji.json</file>
+ </gresource>
+</gresources>
diff --git a/data/gnome-shell-overrides-migration.desktop.in b/data/gnome-shell-overrides-migration.desktop.in
new file mode 100644
index 0000000..99452e6
--- /dev/null
+++ b/data/gnome-shell-overrides-migration.desktop.in
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=GNOME settings overrides migration
+NoDisplay=true
+Exec=@libexecdir@/gnome-shell-overrides-migration.sh
diff --git a/data/gnome-shell-theme.gresource.xml b/data/gnome-shell-theme.gresource.xml
new file mode 100644
index 0000000..24b3be8
--- /dev/null
+++ b/data/gnome-shell-theme.gresource.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/shell/theme">
+ <file>calendar-today.svg</file>
+ <file>calendar-today-light.svg</file>
+ <file>checkbox.svg</file>
+ <file>checkbox-focused.svg</file>
+ <file>checkbox-off-focused-light.svg</file>
+ <file>checkbox-off-focused.svg</file>
+ <file>checkbox-off-light.svg</file>
+ <file>checkbox-off.svg</file>
+ <file>gnome-shell.css</file>
+ <file>gnome-shell-high-contrast.css</file>
+ <file>gnome-shell-start.svg</file>
+ <file>pad-osd.css</file>
+ <file>process-working.svg</file>
+ <file>toggle-off.svg</file>
+ <file>toggle-off-hc.svg</file>
+ <file>toggle-off-light.svg</file>
+ <file>toggle-on.svg</file>
+ <file>toggle-on-hc.svg</file>
+ <file>toggle-on-light.svg</file>
+ <file>workspace-placeholder.svg</file>
+ </gresource>
+</gresources>
diff --git a/data/gnome-shell.portal b/data/gnome-shell.portal
new file mode 100644
index 0000000..a7cd0d5
--- /dev/null
+++ b/data/gnome-shell.portal
@@ -0,0 +1,4 @@
+[portal]
+DBusName=org.gnome.Shell.Portal
+Interfaces=org.freedesktop.impl.portal.Access
+UseIn=gnome
diff --git a/data/icons/hicolor/scalable/apps/org.gnome.Shell.Extensions.svg b/data/icons/hicolor/scalable/apps/org.gnome.Shell.Extensions.svg
new file mode 100644
index 0000000..496be6b
--- /dev/null
+++ b/data/icons/hicolor/scalable/apps/org.gnome.Shell.Extensions.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="10.23" y1="87.43" x2="133.236" y2="88.679" gradientTransform="translate(-8 -16)"><stop offset="0" stop-color="#208757"/><stop offset=".077" stop-color="#2ec27e"/><stop offset=".147" stop-color="#208a5a"/><stop offset=".198" stop-color="#26a269"/><stop offset=".364" stop-color="#1c774d"/><stop offset=".407" stop-color="#26a269"/><stop offset=".493" stop-color="#26a269"/><stop offset=".576" stop-color="#26a269"/><stop offset=".606" stop-color="#2ec27e"/><stop offset=".681" stop-color="#26a269"/><stop offset=".784" stop-color="#1d7a4e"/><stop offset=".945" stop-color="#28ab6f"/><stop offset="1" stop-color="#48d493"/></linearGradient></defs><path d="M21.965 18.363v4l2.039 17.75c.418 3.637 1.055 7.282.844 10.938-.106 1.828-.465 3.668-1.309 5.293-.426.812-.969 1.57-1.644 2.191-.676.621-1.485 1.106-2.364 1.356-1.047.297-2.156.261-3.238.164-1.094-.098-2.227-.282-3.137-.903-.394-.273-.73-.617-1.082-.945-.351-.328-.726-.645-1.168-.832-.691-.297-1.492-.254-2.207-.023-.715.23-1.355.64-1.972 1.066-1.188.824-2.325 1.758-3.184 2.922-1.734 2.344-2.203 5.46-1.711 8.336.367 2.152 1.258 4.238 2.688 5.89.722.836 1.582 1.563 2.566 2.063.98.504 2.09.773 3.195.71 1.446-.085 2.79-.73 4.114-1.327 1.32-.594 2.718-1.16 4.168-1.067a5.113 5.113 0 012.332.758 5.958 5.958 0 011.773 1.707c.848 1.242 1.234 2.742 1.531 4.219 1.832 9.133.778 18.82-2.98 27.344v4c6.812 2.347 13.8 4.199 20.886 5.535 2.356.441 4.801.824 7.13.246 1.163-.29 2.28-.832 3.148-1.66a5.39 5.39 0 001.441-2.34c.266-.887.278-1.828.254-2.754L54 110c-.426.09-.875.059-1.285-.082a2.751 2.751 0 01-1.16-.8 3.768 3.768 0 01-.7-1.231c-.316-.895-.351-1.864-.355-2.813-.008-1.57.063-3.187.684-4.629.629-1.465 1.793-2.652 3.117-3.535 2.64-1.758 5.898-2.375 9.07-2.414 2.91-.035 5.918.414 8.387 1.953 1.238.77 2.316 1.809 3.043 3.07.722 1.262 1.078 2.739.953 4.188-.098 1.117-.477 2.2-1.02 3.18A9.862 9.862 0 0172 110c.012.996.02 1.992.02 2.988.003.555.003 1.114.07 1.664.07.551.21 1.098.476 1.586.383.7 1.012 1.239 1.692 1.657.68.414 1.422.722 2.176.984 4.207 1.46 8.793 1.562 13.203.922a40.756 40.756 0 0015.86-5.88l-.005-4.003a60.146 60.146 0 01-3.433-25.465c.125-1.512.312-3.039.894-4.441.29-.7.68-1.364 1.18-1.93.504-.566 1.12-1.039 1.82-1.332.82-.344 1.727-.434 2.613-.367.887.07 1.75.297 2.598.57 1.688.547 3.344 1.297 5.117 1.418 1.492.098 2.996-.266 4.332-.937a9.894 9.894 0 004.016-3.715 9.91 9.91 0 001.418-5.285l.176-2.258a16.406 16.406 0 00-3.075-6.477c-.785-.996-1.707-1.914-2.835-2.496a5.247 5.247 0 00-1.805-.558 4.18 4.18 0 00-1.875.214c-.988.364-1.774 1.125-2.703 1.614-1.032.543-2.203.738-3.364.824-1.148.086-2.324.07-3.425-.274-1.48-.464-2.743-1.52-3.602-2.812-.855-1.293-1.332-2.805-1.59-4.328-.574-3.399-.117-6.871.266-10.297a227.56 227.56 0 001.34-19.973 497.333 497.333 0 01-21.118-5.117c-1.355-.36-2.718-.726-4.113-.894-1.394-.165-2.836-.118-4.168.332a7.108 7.108 0 00-2.93 1.894v4c0 .414.016.828.11 1.235.094.402.258.789.445 1.156.371.738.82 1.433 1.313 2.097.98 1.329 2.136 2.563 2.765 4.094.688 1.676.668 3.606.07 5.317-.597 1.707-1.75 3.203-3.179 4.312-1.43 1.113-3.133 1.852-4.895 2.262-1.765.414-3.593.508-5.402.406-2.926-.164-5.906-.875-8.25-2.633-1.172-.883-2.164-2.015-2.793-3.34-.625-1.324-.879-2.828-.668-4.277.293-1.973 1.399-3.71 2.45-5.406.421-.684.831-1.38 1.078-2.14.242-.763.296-1.571.308-2.372L54.633 18a1.89 1.89 0 01-1.695.777c-.516-.047-.989-.3-1.41-.601-.419-.301-.801-.653-1.231-.938-.871-.578-1.914-.855-2.957-.933-1.043-.075-2.09.039-3.125.191-2.403.356-4.77.91-7.176 1.266-2.402.351-4.832.46-7.262.496zm0 0" fill="url(#a)"/><path d="M21.965 18.363l2.039 17.746c.426 3.715 1.16 7.414 1.094 11.149-.035 1.867-.34 3.77-1.239 5.41-.449.816-1.047 1.562-1.785 2.133-.738.574-1.617.972-2.543 1.09-1.504.195-2.996-.344-4.379-.961-1.379-.621-2.75-1.344-4.246-1.555-1.465-.21-2.984.094-4.293.777-1.308.688-2.406 1.746-3.207 2.989-1.601 2.484-1.972 5.605-1.574 8.535.3 2.219 1.05 4.433 2.512 6.133.73.847 1.633 1.558 2.656 2.011 1.023.457 2.168.657 3.281.52 1.54-.192 2.926-1 4.324-1.676.704-.336 1.422-.644 2.176-.828.754-.184 1.551-.238 2.309-.066.82.183 1.566.628 2.164 1.218.601.586 1.05 1.313 1.375 2.086.64 1.551.762 3.258.84 4.934a123.2 123.2 0 01-2.25 29.965 133.42 133.42 0 0020.886 5.53c2.352.427 4.793.782 7.11.196 1.16-.293 2.27-.828 3.152-1.633.883-.804 1.528-1.89 1.711-3.074.133-.879.012-1.789-.25-2.64-.262-.852-.668-1.657-1.101-2.438-.868-1.559-1.887-3.086-2.227-4.84-.3-1.558-.023-3.207.676-4.633.703-1.425 1.816-2.629 3.12-3.535 2.614-1.804 5.9-2.383 9.075-2.41 2.91-.023 5.918.414 8.39 1.953 1.235.77 2.313 1.809 3.04 3.07.73 1.258 1.097 2.743.953 4.188-.117 1.113-.524 2.18-1.024 3.18-.5 1.004-1.093 1.957-1.609 2.949-.512.996-.95 2.047-1.11 3.152-.156 1.11-.019 2.29.555 3.25.41.684 1.02 1.23 1.696 1.649.675.422 1.418.722 2.168.992a30.23 30.23 0 0015.183 1.332 30.128 30.128 0 0013.88-6.293l-2.587-19.371c-.453-3.406-1.14-6.813-1.047-10.246.043-1.719.34-3.461 1.141-4.98a6.796 6.796 0 011.578-2.02 5.338 5.338 0 012.262-1.18c.8-.183 1.637-.164 2.441-.02.809.145 1.59.41 2.352.708 1.527.597 3.023 1.34 4.648 1.558 1.5.203 3.055-.062 4.426-.699 1.375-.64 2.566-1.648 3.469-2.863 1.808-2.434 2.418-5.625 2.047-8.63-.305-2.448-1.258-4.862-2.953-6.655-.848-.895-1.875-1.63-3.016-2.098-1.14-.473-2.395-.684-3.621-.563-1.637.157-3.153.875-4.688 1.461-.765.297-1.55.559-2.355.707-.809.149-1.649.176-2.45-.004-1.214-.27-2.296-1.007-3.081-1.972-.786-.965-1.286-2.14-1.547-3.36-.524-2.43-.125-4.953.23-7.414a199.18 199.18 0 002-24.914 119.325 119.325 0 00-22.3-4.867c-2.329-.281-4.75-.48-6.97.274-1.113.378-2.152 1-2.933 1.875-.781.87-1.289 2.011-1.312 3.183-.02.824.199 1.645.543 2.395.347.75.812 1.441 1.312 2.097 1 1.317 2.148 2.559 2.773 4.09.688 1.676.676 3.606.082 5.32-.593 1.711-1.746 3.207-3.175 4.32-1.434 1.114-3.137 1.852-4.903 2.263-1.765.41-3.593.5-5.406.394-2.918-.172-5.894-.89-8.23-2.652-1.168-.88-2.157-2.016-2.79-3.332-.632-1.317-.906-2.82-.69-4.266.296-1.965 1.437-3.68 2.398-5.418.48-.871.925-1.77 1.199-2.723.277-.953.379-1.976.168-2.945-.219-1.016-.774-1.941-1.512-2.672-.738-.726-1.652-1.261-2.629-1.617-1.949-.71-4.078-.707-6.148-.586a58.229 58.229 0 00-22.254 5.867zm0 0" fill="#33d17a"/></svg> \ No newline at end of file
diff --git a/data/icons/hicolor/symbolic/apps/org.gnome.Shell.Extensions-symbolic.svg b/data/icons/hicolor/symbolic/apps/org.gnome.Shell.Extensions-symbolic.svg
new file mode 100644
index 0000000..4208a4d
--- /dev/null
+++ b/data/icons/hicolor/symbolic/apps/org.gnome.Shell.Extensions-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M6.5 1.031c-.371 0-.742-.035-1.11.016-.367.05-.73.203-.972.476-.125.141-.215.309-.266.485-.047.18-.054.367-.02.55.032.184.102.356.192.516.09.164.203.309.317.457L5 4H2a1.8 1.8 0 00-.41.035.791.791 0 00-.36.195.791.791 0 00-.195.36C1 4.723 1 4.863 1 5v2.75l.77-.344c.265-.117.542-.23.832-.242.289-.016.586.074.812.254.227.18.383.441.465.723.082.277.101.57.121.859.02.316.04.637-.016.95-.058.312-.199.616-.43.831a1.264 1.264 0 01-.874.32c-.317-.007-.618-.128-.91-.257L1 10.5V14c0 .137.004.277.035.41a.791.791 0 00.195.36c.098.097.227.16.36.195.133.035.273.035.41.035h3l-.328-.68c-.14-.293-.274-.597-.29-.922-.015-.32.095-.652.31-.894.214-.242.523-.39.84-.453.316-.067.644-.059.968-.059.324 0 .652-.008.969.059.316.062.625.21.84.453.214.242.324.574.308.894-.015.325-.148.63-.289.922L8 15h3a1.8 1.8 0 00.41-.035.791.791 0 00.36-.195.791.791 0 00.195-.36C12 14.277 12 14.137 12 14v-3.563l.703.297c.29.125.59.239.902.246.313.004.63-.101.864-.308.238-.203.386-.496.46-.8C15 9.565 15 9.25 15 8.937c0-.313 0-.63-.07-.934-.075-.305-.223-.598-.461-.8a1.288 1.288 0 00-.864-.31c-.312.008-.613.122-.902.247L12 7.437V5a1.8 1.8 0 00-.035-.41.791.791 0 00-.195-.36.791.791 0 00-.36-.195C11.277 4 11.137 4 11 4H8l.36-.469c.113-.148.226-.293.316-.457.09-.16.16-.332.191-.515a1.248 1.248 0 00-.02-.551 1.256 1.256 0 00-.265-.485c-.242-.273-.605-.425-.973-.476-.367-.05-.738-.016-1.109-.016zm0 0" fill="#474747"/></svg> \ No newline at end of file
diff --git a/data/icons/meson.build b/data/icons/meson.build
new file mode 100644
index 0000000..eff6e4b
--- /dev/null
+++ b/data/icons/meson.build
@@ -0,0 +1 @@
+install_subdir('hicolor', install_dir: icondir)
diff --git a/data/icons/scalable/actions/carousel-arrow-next-symbolic.svg b/data/icons/scalable/actions/carousel-arrow-next-symbolic.svg
new file mode 100644
index 0000000..c8091b2
--- /dev/null
+++ b/data/icons/scalable/actions/carousel-arrow-next-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 10.832031 1.039062 c 0.386719 0.085938 0.722657 0.324219 0.9375 0.660157 l 6 9.5 c 0.339844 0.558593 0.308594 1.113281 0 1.601562 l -6 9.5 c -0.445312 0.699219 -1.371093 0.910157 -2.070312 0.46875 c -0.699219 -0.445312 -0.90625 -1.371093 -0.464844 -2.070312 l 5.492187 -8.699219 l -5.492187 -8.699219 c -0.441406 -0.699219 -0.234375 -1.625 0.464844 -2.070312 c 0.335937 -0.210938 0.742187 -0.28125 1.132812 -0.191407 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/carousel-arrow-previous-symbolic.svg b/data/icons/scalable/actions/carousel-arrow-previous-symbolic.svg
new file mode 100644
index 0000000..85ad3ae
--- /dev/null
+++ b/data/icons/scalable/actions/carousel-arrow-previous-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 13.167969 1.035156 c -0.386719 0.089844 -0.722657 0.328125 -0.9375 0.664063 l -6 9.5 c -0.308594 0.488281 -0.308594 1.113281 0 1.601562 l 6 9.5 c 0.445312 0.699219 1.371093 0.910157 2.070312 0.464844 c 0.699219 -0.441406 0.90625 -1.367187 0.464844 -2.066406 l -5.492187 -8.699219 l 5.492187 -8.699219 c 0.441406 -0.699219 0.234375 -1.625 -0.464844 -2.070312 c -0.335937 -0.210938 -0.742187 -0.28125 -1.132812 -0.195313 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/color-pick.svg b/data/icons/scalable/actions/color-pick.svg
new file mode 100644
index 0000000..d9af690
--- /dev/null
+++ b/data/icons/scalable/actions/color-pick.svg
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="5.4116011mm"
+ height="5.1374583mm"
+ viewBox="0 0 5.4116011 5.1374583"
+ version="1.1"
+ id="svg5595"
+ inkscape:version="0.92.4 (unknown)"
+ sodipodi:docname="color-pick.svg">
+ <defs
+ id="defs5589">
+ <filter
+ inkscape:collect="always"
+ x="-0.10291173"
+ width="1.2058235"
+ y="-0.065432459"
+ height="1.1308649"
+ id="filter5601"
+ style="color-interpolation-filters:sRGB">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.610872"
+ id="feGaussianBlur5603" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="15.839192"
+ inkscape:cx="39.387731"
+ inkscape:cy="12.554326"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata5592">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-103.12753,-146.26461)">
+ <circle
+ r="8.4810486"
+ cy="9.82623"
+ cx="10.226647"
+ id="circle7584"
+ style="color:#000000;display:inline;overflow:visible;opacity:0.6;vector-effect:none;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter:url(#filter5601)"
+ transform="matrix(0.26458333,0,0,0.26458333,103.12753,146.26461)" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.26399338;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
+ d="m 108.07728,148.64122 c 0,1.2393 -1.00465,2.24394 -2.24395,2.24394 -1.23929,0 -2.24716,-1.00465 -2.25221,-2.24394 l -0.009,-2.24458 2.26136,6.4e-4 c 1.2393,3.4e-4 2.24395,1.00464 2.24395,2.24394 z"
+ id="path7523-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssscss" />
+ <circle
+ style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#50dbb5;fill-opacity:1;stroke:none;stroke-width:0.36885914;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
+ id="path7482-1"
+ cx="105.83707"
+ cy="148.64352"
+ r="1.844296" />
+ </g>
+</svg>
diff --git a/data/icons/scalable/actions/dark-mode-symbolic.svg b/data/icons/scalable/actions/dark-mode-symbolic.svg
new file mode 100644
index 0000000..94eee23
--- /dev/null
+++ b/data/icons/scalable/actions/dark-mode-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 8 0 c -4.40625 0 -8 3.59375 -8 8 s 3.59375 8 8 8 s 8 -3.59375 8 -8 s -3.59375 -8 -8 -8 z m 0 1.941406 c 3.359375 0 6.058594 2.699219 6.058594 6.058594 s -2.699219 6.058594 -6.058594 6.058594 z m 0 0" fill="#222222"/></svg>
diff --git a/data/icons/scalable/actions/pointer-double-click-symbolic.svg b/data/icons/scalable/actions/pointer-double-click-symbolic.svg
new file mode 100644
index 0000000..f249aaa
--- /dev/null
+++ b/data/icons/scalable/actions/pointer-double-click-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="20px" viewBox="0 0 20 20" width="20px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 1 0 v 19.707031 l 4.242188 -4.144531 c 0.589843 1.191406 1.160156 2.386719 1.761718 3.570312 c 1.105469 1.640626 4.15625 0.464844 3.898438 -1.496093 c 0.007812 -0.46875 -0.40625 -0.90625 -0.4375 -1.3125 l 0.78125 -0.761719 c 0.585937 1.191406 1.15625 2.386719 1.757812 3.570312 c 1.105469 1.640626 4.15625 0.464844 3.898438 -1.496093 c -0.195313 -0.902344 -0.71875 -1.695313 -1.070313 -2.546875 l -0.714843 -1.539063 h 4.828124 l -12.945312 -13.550781 v 6.242188 z m 1.5 3.726562 l 6 6.242188 v -6.242188 l 7.921875 8.324219 h -3.664063 c 0.886719 1.902344 1.785157 3.800781 2.65625 5.707031 c 0.007813 0.625 -1.226562 0.980469 -1.324218 0.207032 l -2.410156 -4.917969 l -3.0625 2.984375 c 0.261718 0.609375 0.597656 1.191406 0.816406 1.816406 c -0.183594 0.601563 -1.269532 0.835938 -1.367188 0.066406 l -2.386718 -4.867187 l -3.179688 3.101563 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/pointer-drag-symbolic.svg b/data/icons/scalable/actions/pointer-drag-symbolic.svg
new file mode 100644
index 0000000..7ad8121
--- /dev/null
+++ b/data/icons/scalable/actions/pointer-drag-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="20px" viewBox="0 0 20 20" width="20px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 10.925781 2.320312 c -0.835937 0.074219 -1.816406 -0.136718 -2.445312 0.574219 c -0.179688 0.503907 -0.394531 0.730469 -0.945313 0.617188 c -0.796875 -0.050781 -1.703125 0.054687 -2.1875 0.773437 c -0.554687 0.832032 -0.316406 1.859375 -0.367187 2.792969 v 1.503906 c -0.511719 -0.320312 -1.132813 -0.132812 -1.699219 -0.183593 c -0.445312 -0.019532 -0.882812 0.027343 -1.320312 0.09375 c 0.058593 2.683593 0.074218 5.371093 0.121093 8.058593 c 0.09375 1.300781 1.230469 2.402344 2.546875 2.421875 c 0.632813 0.015625 1.480469 0.007813 2.222656 0.011719 c 3.222657 -0.011719 6.445313 0.050781 9.667969 -0.054687 c 1.160157 -0.121094 2.210938 -1.1875 2.070313 -2.398438 v -9.480469 c -0.707032 -0.9375 -1.816406 -1.78125 -3.058594 -1.570312 c -0.300781 -0.453125 -0.488281 -1.472657 -1.226562 -1.796875 c -0.355469 -0.292969 -0.839844 -0.109375 -1.261719 -0.164063 c -0.296875 -0.070312 -0.855469 0.195313 -0.867188 -0.25 c -0.160156 -0.523437 -0.71875 -0.910156 -1.25 -0.949219 z m -0.105469 1.503907 c 0.164063 0.269531 0.183594 0.753906 0.21875 1.125 c 0.007813 0.007812 0.019532 0.003906 0.027344 0.011719 c -0.007812 0 -0.019531 0 -0.027344 0 l 0.496094 5.898437 l 0.664063 -5.839844 l 1.34375 0.003907 c 0.535156 0.390624 0.414062 1.140624 0.554687 1.722656 c -0.011718 0.066406 0.007813 0.09375 0.015625 0.136718 l 0.011719 0.085938 l 0.480469 3.863281 l 0.664062 -3.917969 c 0.574219 -0.039062 1.15625 0.039063 1.558594 0.5 c 0.410156 0.21875 0.214844 0.691407 0.261719 1.058594 v 8.34375 c -0.339844 0.964844 -1.601563 0.546875 -2.375 0.660156 c -3.371094 0.011719 -6.742188 0.035157 -10.113282 -0.015624 c -0.976562 -0.027344 -1.140624 -1.070313 -1.039062 -1.839844 c -0.011719 -1.90625 -0.046875 -3.816406 -0.078125 -5.722656 c 0.324219 0.007812 0.675781 -0.011719 0.988281 0.011718 c 0.492188 0.914063 0.484375 2 0.507813 3.015625 c 0.492187 -0.007812 1.042969 0.039063 1.5 0 c 0.007812 -2.519531 -0.019531 -5.039062 0.011719 -7.558593 c 0.105468 -0.519532 0.730468 -0.308594 1.113281 -0.355469 h 0.675781 l 0.488281 5.847656 l 0.667969 -5.875 h -0.003906 c 0.03125 -0.382813 -0.148438 -0.894531 0.25 -1.121094 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/pointer-primary-click-symbolic.svg b/data/icons/scalable/actions/pointer-primary-click-symbolic.svg
new file mode 100644
index 0000000..7a99169
--- /dev/null
+++ b/data/icons/scalable/actions/pointer-primary-click-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="20px" viewBox="0 0 20 20" width="20px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 4 0 v 19.644531 l 4.242188 -4.140625 c 0.613281 1.214844 1.175781 2.460938 1.820312 3.65625 c 1.144531 1.476563 3.976562 0.390625 3.847656 -1.472656 c -0.09375 -0.824219 -0.59375 -1.523438 -0.898437 -2.277344 l -0.898438 -1.921875 h 5.269531 z m 1.5 3.726562 l 8.359375 8.261719 h -4.101563 c 0.886719 1.902344 1.78125 3.800781 2.660157 5.707031 c 0.035156 0.566407 -1.148438 0.988282 -1.28125 0.296876 l -2.457031 -5.011719 l -3.179688 3.105469 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/pointer-secondary-click-symbolic.svg b/data/icons/scalable/actions/pointer-secondary-click-symbolic.svg
new file mode 100644
index 0000000..4fccfb8
--- /dev/null
+++ b/data/icons/scalable/actions/pointer-secondary-click-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="20px" viewBox="0 0 20 20" width="20px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 16 0 v 19.769531 l -4.242188 -4.140625 c -0.613281 1.214844 -1.175781 2.460938 -1.820312 3.65625 c -1.144531 1.476563 -3.976562 0.390625 -3.847656 -1.472656 c 0.09375 -0.824219 0.59375 -1.523438 0.898437 -2.277344 l 0.898438 -1.921875 h -5.269531 z m -1.5 3.726562 l -8.359375 8.386719 h 4.101563 c -0.886719 1.902344 -1.78125 3.800781 -2.660157 5.707031 c -0.035156 0.566407 1.148438 0.988282 1.28125 0.296876 l 2.457031 -5.011719 l 3.179688 3.105469 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/preview-close-symbolic.svg b/data/icons/scalable/actions/preview-close-symbolic.svg
new file mode 100644
index 0000000..34f0823
--- /dev/null
+++ b/data/icons/scalable/actions/preview-close-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 7 6 c -0.265625 0 -0.519531 0.105469 -0.707031 0.292969 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 l 4.292969 4.292969 l -4.292969 4.292969 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 s 1.023437 0.390625 1.414062 0 l 4.292969 -4.292969 l 4.292969 4.292969 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 s 0.390625 -1.023437 0 -1.414062 l -4.292969 -4.292969 l 4.292969 -4.292969 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 s -0.519531 0.105469 -0.707031 0.292969 l -4.292969 4.292969 l -4.292969 -4.292969 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/record-screen-symbolic.svg b/data/icons/scalable/actions/record-screen-symbolic.svg
new file mode 100644
index 0000000..5e817ce
--- /dev/null
+++ b/data/icons/scalable/actions/record-screen-symbolic.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#2e3436">
+ <path d="m 2.175781 0 h 3.648438 c 1.203125 0 2.175781 0.972656 2.175781 2.175781 v 2.648438 c 0 1.203125 -0.972656 2.175781 -2.175781 2.175781 h -3.648438 c -1.203125 0 -2.175781 -0.972656 -2.175781 -2.175781 v -2.648438 c 0 -1.203125 0.972656 -2.175781 2.175781 -2.175781 z m 0 0"/>
+ <path d="m 7.796875 2.652344 l 2.050781 -1.523438 c 0.214844 -0.160156 0.503906 -0.1875 0.742188 -0.066406 c 0.242187 0.121094 0.394531 0.367188 0.394531 0.636719 v 3.605469 c 0 0.265624 -0.152344 0.511718 -0.394531 0.632812 c -0.238282 0.121094 -0.523438 0.09375 -0.742188 -0.066406 l -2.050781 -1.53125 c -0.265625 -0.199219 -0.421875 -0.511719 -0.421875 -0.84375 c 0 -0.332032 0.15625 -0.644532 0.421875 -0.84375 z m 0 0"/>
+ <path d="m 9.042969 7 h 2.914062 c 0.574219 0 1.042969 0.449219 1.042969 1 s -0.46875 1 -1.042969 1 h -2.914062 c -0.574219 0 -1.042969 -0.449219 -1.042969 -1 s 0.46875 -1 1.042969 -1 z m 0 0"/>
+ <path d="m 7.011719 8 c -1.113281 0 -2.011719 0.898438 -2.011719 2.011719 v 3.976562 c 0 1.113281 0.898438 2.011719 2.011719 2.011719 h 6.976562 c 1.113281 0 2.011719 -0.898438 2.011719 -2.011719 v -3.976562 c 0 -1.113281 -0.898438 -2.011719 -2.011719 -2.011719 z m 3.488281 1 c 1.378906 0 2.5 1.121094 2.5 2.5 s -1.121094 2.5 -2.5 2.5 s -2.5 -1.121094 -2.5 -2.5 s 1.121094 -2.5 2.5 -2.5 z m 0 0"/>
+ </g>
+</svg>
diff --git a/data/icons/scalable/actions/screencast-recorded-symbolic.svg b/data/icons/scalable/actions/screencast-recorded-symbolic.svg
new file mode 100644
index 0000000..cf522fd
--- /dev/null
+++ b/data/icons/scalable/actions/screencast-recorded-symbolic.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#2e3436">
+ <path d="m 6.929688 8.011719 h 4.140624 c 1.066407 0 1.929688 0.863281 1.929688 1.929687 v 3.140625 c 0 1.066407 -0.863281 1.929688 -1.929688 1.929688 h -4.140624 c -1.066407 0 -1.929688 -0.863281 -1.929688 -1.929688 v -3.140625 c 0 -1.066406 0.863281 -1.929687 1.929688 -1.929687 z m 0 0"/>
+ <path d="m 12.796875 10.664062 l 2.050781 -1.523437 c 0.214844 -0.160156 0.503906 -0.1875 0.742188 -0.066406 c 0.242187 0.121093 0.394531 0.367187 0.394531 0.636719 v 3.605468 c 0 0.265625 -0.152344 0.511719 -0.394531 0.632813 c -0.238282 0.121093 -0.523438 0.09375 -0.742188 -0.066407 l -2.050781 -1.53125 c -0.265625 -0.199218 -0.421875 -0.511718 -0.421875 -0.84375 c 0 -0.332031 0.15625 -0.644531 0.421875 -0.84375 z m 0 0"/>
+ <path d="m 0 4.011719 c 0 -1.652344 1.359375 -3.011719 3.011719 -3.011719 h 7.976562 c 1.652344 0 3.011719 1.359375 3.011719 3.011719 v 1.953125 c 0 0.554687 -0.449219 1 -1 1 s -1 -0.445313 -1 -1 v -1.953125 c 0 -0.578125 -0.433594 -1.011719 -1.011719 -1.011719 h -7.976562 c -0.578125 0 -1.011719 0.433594 -1.011719 1.011719 v 4.976562 c 0 0.578125 0.433594 1.011719 1.011719 1.011719 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 c -1.652344 0 -3.011719 -1.359375 -3.011719 -3.011719 z m 0 0"/>
+ </g>
+</svg>
diff --git a/data/icons/scalable/actions/screenshot-recorded-symbolic.svg b/data/icons/scalable/actions/screenshot-recorded-symbolic.svg
new file mode 100644
index 0000000..db9338b
--- /dev/null
+++ b/data/icons/scalable/actions/screenshot-recorded-symbolic.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#2e3436">
+ <path d="m 9.042969 6 h 1.914062 c 0.574219 0 1.042969 0.449219 1.042969 1 s -0.46875 1 -1.042969 1 h -1.914062 c -0.574219 0 -1.042969 -0.449219 -1.042969 -1 s 0.46875 -1 1.042969 -1 z m 0 0"/>
+ <path d="m 7.011719 6.964844 c -1.113281 0 -2.011719 0.898437 -2.011719 2.011718 v 4.011719 c 0 1.113281 0.898438 2.011719 2.011719 2.011719 h 5.976562 c 1.113281 0 2.011719 -0.898438 2.011719 -2.011719 v -4.011719 c 0 -1.113281 -0.898438 -2.011718 -2.011719 -2.011718 z m 2.988281 2.035156 c 1.105469 0 2 0.894531 2 2 s -0.894531 2 -2 2 s -2 -0.894531 -2 -2 s 0.894531 -2 2 -2 z m 0 0"/>
+ <path d="m 0 4.011719 c 0 -1.652344 1.359375 -3.011719 3.011719 -3.011719 h 7.976562 c 1.652344 0 3.011719 1.359375 3.011719 3.011719 v 0.988281 c 0 0.550781 -0.449219 1 -1 1 s -1 -0.449219 -1 -1 v -0.988281 c 0 -0.578125 -0.433594 -1.011719 -1.011719 -1.011719 h -7.976562 c -0.578125 0 -1.011719 0.433594 -1.011719 1.011719 v 4.976562 c 0 0.578125 0.433594 1.011719 1.011719 1.011719 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 c -1.652344 0 -3.011719 -1.359375 -3.011719 -3.011719 z m 0 0"/>
+ </g>
+</svg>
diff --git a/data/icons/scalable/actions/screenshot-ui-area-symbolic.svg b/data/icons/scalable/actions/screenshot-ui-area-symbolic.svg
new file mode 100644
index 0000000..6cf5870
--- /dev/null
+++ b/data/icons/scalable/actions/screenshot-ui-area-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="32px" viewBox="0 0 32 32" width="32px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 3.5 2 c -1.929688 0.007812 -3.4921875 1.570312 -3.5 3.5 c 0 1.386719 0.820312 2.59375 2 3.15625 v 3.34375 h 2 v -3.035156 c 1.53125 -0.230469 2.734375 -1.433594 2.964844 -2.964844 h 2.035156 v -2 h -2.34375 c -0.578125 -1.21875 -1.808594 -1.996094 -3.15625 -2 z m 25 0 c -1.347656 0.003906 -2.578125 0.78125 -3.15625 2 h -2.34375 v 2 h 2.035156 c 0.230469 1.53125 1.433594 2.734375 2.964844 2.964844 v 3.035156 h 2 v -3.34375 c 1.21875 -0.578125 1.996094 -1.808594 2 -3.15625 c 0 -1.921875 -1.578125 -3.5 -3.5 -3.5 z m -25 2 c 0.839844 0 1.5 0.660156 1.5 1.5 s -0.660156 1.5 -1.5 1.5 s -1.5 -0.660156 -1.5 -1.5 s 0.660156 -1.5 1.5 -1.5 z m 7.5 0 v 2 h 4 v -2 z m 6 0 v 2 h 4 v -2 z m 11.5 0 c 0.839844 0 1.5 0.660156 1.5 1.5 s -0.660156 1.5 -1.5 1.5 s -1.5 -0.660156 -1.5 -1.5 s 0.660156 -1.5 1.5 -1.5 z m -26.5 10 v 4 h 2 v -4 z m 26 0 v 4 h 2 v -4 z m -26 6 v 3.34375 c -1.21875 0.578125 -1.99609375 1.808594 -2 3.15625 c 0 1.921875 1.578125 3.5 3.5 3.5 c 1.347656 -0.003906 2.578125 -0.78125 3.15625 -2 h 2.34375 v -2 h -2.035156 c -0.230469 -1.53125 -1.433594 -2.734375 -2.964844 -2.964844 v -3.035156 z m 26 0 v 3.035156 c -1.53125 0.230469 -2.734375 1.433594 -2.964844 2.964844 h -2.035156 v 2 h 2.34375 c 0.578125 1.21875 1.808594 1.996094 3.15625 2 c 1.921875 0 3.5 -1.578125 3.5 -3.5 c -0.003906 -1.347656 -0.78125 -2.578125 -2 -3.15625 v -3.34375 z m -24.5 5 c 0.839844 0 1.5 0.660156 1.5 1.5 s -0.660156 1.5 -1.5 1.5 s -1.5 -0.660156 -1.5 -1.5 s 0.660156 -1.5 1.5 -1.5 z m 25 0 c 0.839844 0 1.5 0.660156 1.5 1.5 s -0.660156 1.5 -1.5 1.5 s -1.5 -0.660156 -1.5 -1.5 s 0.660156 -1.5 1.5 -1.5 z m -17.5 1 v 2 h 4 v -2 z m 6 0 v 2 h 4 v -2 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/screenshot-ui-display-symbolic.svg b/data/icons/scalable/actions/screenshot-ui-display-symbolic.svg
new file mode 100644
index 0000000..b8b3f4a
--- /dev/null
+++ b/data/icons/scalable/actions/screenshot-ui-display-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="32px" viewBox="0 0 32 32" width="32px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 4 4 c -1.660156 0 -3 1.339844 -3 3 v 14 c 0 1.660156 1.339844 3 3 3 h 10 v 3 h -3 c -1.09375 -0.003906 -2 0.90625 -2 2 h 14 c 0 -1.09375 -0.910156 -2 -2 -2 h -3 v -3 h 10 c 1.660156 0 3 -1.339844 3 -3 v -14 c 0 -1.660156 -1.339844 -3 -3 -3 z m 0 2 h 24 c 0.554688 0 1 0.445312 1 1 v 14 c 0 0.554688 -0.445312 1 -1 1 h -24 c -0.554688 0 -1 -0.445312 -1 -1 v -14 c 0 -0.554688 0.445312 -1 1 -1 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/actions/screenshot-ui-show-pointer-symbolic.svg b/data/icons/scalable/actions/screenshot-ui-show-pointer-symbolic.svg
new file mode 100644
index 0000000..36fab00
--- /dev/null
+++ b/data/icons/scalable/actions/screenshot-ui-show-pointer-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 2.953125 1.074219 l 2.417969 13.210937 l 3.238281 -2.398437 l 2.054687 2.648437 c 1.03125 1.433594 3.148438 -0.210937 2.011719 -1.5625 l -2.015625 -2.59375 l 2.984375 -2.175781 z m 0 0" fill="#2e3436"/>
+</svg>
diff --git a/data/icons/scalable/actions/screenshot-ui-window-symbolic.svg b/data/icons/scalable/actions/screenshot-ui-window-symbolic.svg
new file mode 100644
index 0000000..b667bed
--- /dev/null
+++ b/data/icons/scalable/actions/screenshot-ui-window-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="32px" viewBox="0 0 32 32" width="32px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 3 3 c -1.644531 0 -3 1.355469 -3 3 v 14 c 0 1.644531 1.355469 3 3 3 h 4 v 3 c 0 1.644531 1.351562 3 3 3 h 18.996094 c 1.644531 0 3 -1.355469 3 -3 v -14 c 0 -1.644531 -1.355469 -3 -3 -3 h -4 v -3 c 0 -1.644531 -1.355469 -3 -3 -3 z m 0 2 h 18.996094 c 0.570312 0 1 0.429688 1 1 v 3 h -12.996094 c -1.648438 0 -3 1.355469 -3 3 v 9 h -4 c -0.570312 0 -1 -0.429688 -1 -1 v -14 c 0 -0.570312 0.429688 -1 1 -1 z m 7 6 h 18.996094 c 0.570312 0 1 0.429688 1 1 v 14 c 0 0.570312 -0.429688 1 -1 1 h -18.996094 c -0.574219 0 -1 -0.429688 -1 -1 v -14 c 0 -0.570312 0.425781 -1 1 -1 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/keyboard-caps-lock-symbolic.svg b/data/icons/scalable/status/keyboard-caps-lock-symbolic.svg
new file mode 100644
index 0000000..9efd394
--- /dev/null
+++ b/data/icons/scalable/status/keyboard-caps-lock-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 12 2 l -7.5 8 h 4.5 v 5 h 6 v -5 h 4.5 z m -3 16 v 3 h 6 v -3 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/keyboard-enter-symbolic.svg b/data/icons/scalable/status/keyboard-enter-symbolic.svg
new file mode 100644
index 0000000..7fe839b
--- /dev/null
+++ b/data/icons/scalable/status/keyboard-enter-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 19 6 v 6.003906 c 0.007812 0.996094 -1 0.996094 -1 0.996094 h -11.585938 l 1.292969 -1.292969 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 s -0.519531 0.105469 -0.707031 0.292969 l -3 3 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 l 3 3 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 s 0.390625 -1.023437 0 -1.414062 l -1.289062 -1.292969 h 11.582031 c 3.007812 0 3 -3 3 -3 v -6 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/keyboard-hide-symbolic.svg b/data/icons/scalable/status/keyboard-hide-symbolic.svg
new file mode 100644
index 0000000..d18bfab
--- /dev/null
+++ b/data/icons/scalable/status/keyboard-hide-symbolic.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 2.49705,2.00665 c -0.554,0 -1,0.446 -1,1 v 11 c 0,0.554 0.446,1 1,1 h 19.0059 c 0.554,0 1,-0.446 1,-1 v -11 c 0,-0.554 -0.446,-1 -1,-1 z m 1.3653,2 h 1.6562 v 2 h -2 v -1.6074 c 0,-0.218 0.1538,-0.3926 0.3438,-0.3926 z m 2.6562,0 h 2 v 2 h -2 z m 3,0 h 2 v 2 h -2 z m 3,0 h 1.6563 c 0.19,0 0.3437,0.1746 0.3437,0.3926 v 1.6074 h -2 z m 2.9844,0 h 2 v 2 h -2 z m 3,0 h 2 v 2 h -2 z m -13.9844,3 h 2 v 2 h -2 z m 3,0 h 2 v 2 h -2 z m 3,0 h 2 v 2 h -2 z m 2.9844,0 h 2 v 2 h -2 z m 3,0 h 2 v 2 h -2 z m -9.6621,4 h 10.3125 c 0.19,0 0.3437,0.1746 0.3437,0.3926 v 1.2148 c 0,0.218 -0.1537,0.3926 -0.3437,0.3926 H 6.84085 c -0.19,0 -0.3438,-0.1746 -0.3438,-0.3926 v -1.2148 c 0,-0.218 0.1538,-0.3926 0.3438,-0.3926 z" fill="#241f31"/>
+ <path d="m 17,17.99335 -4.99512,4 -5.00488,-4 z" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/keyboard-layout-symbolic.svg b/data/icons/scalable/status/keyboard-layout-symbolic.svg
new file mode 100644
index 0000000..c93f935
--- /dev/null
+++ b/data/icons/scalable/status/keyboard-layout-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 2985,-2801.6382 c -0.2187,0 -0.4414,0.012 -0.6562,0.031 l -0.4063,2.0938 c -0.332,0.07 -0.6562,0.1758 -0.9687,0.3125 l -1.5313,-1.4648 c -0.3867,0.2265 -0.7266,0.5117 -1.0625,0.8085 l 0.9063,1.9063 c -0.2227,0.25 -0.4219,0.5234 -0.5938,0.8125 l -2.0937,-0.2773 c -0.1758,0.4101 -0.3125,0.8398 -0.4063,1.2773 l 1.8438,1 c -0.02,0.168 -0.027,0.332 -0.031,0.5 0,0.1719 0.016,0.332 0.031,0.5 l -1.8438,1 c 0.098,0.4453 0.2266,0.8711 0.4063,1.2812 l 2.0937,-0.2812 c 0.1719,0.2891 0.3711,0.5625 0.5938,0.8125 l -0.9063,1.9062 c 0.3359,0.3008 0.6758,0.5821 1.0625,0.8125 l 1.5313,-1.4687 c 0.3046,0.1367 0.6328,0.2383 0.9687,0.3125 l 0.4063,2.0937 c 0.4375,0.043 0.875,0.043 1.3125,0 l 0.4062,-2.0937 c 0.3359,-0.074 0.6641,-0.1758 0.9688,-0.3125 l 1.5312,1.4687 c 0.3867,-0.2304 0.7266,-0.5117 1.0625,-0.8125 l -0.9062,-1.9062 c 0.2226,-0.25 0.4218,-0.5234 0.5937,-0.8125 l 2.0938,0.2812 c 0.1757,-0.414 0.3125,-0.8398 0.4062,-1.2812 l -1.8437,-1 c 0.016,-0.168 0.031,-0.3281 0.031,-0.5 0,-0.1719 -0.016,-0.332 -0.031,-0.5 l 1.8437,-1 c -0.094,-0.4375 -0.2305,-0.8672 -0.4062,-1.2813 l -2.0938,0.2813 c -0.1719,-0.2891 -0.3672,-0.5625 -0.5937,-0.8125 l 0.9062,-1.9063 c -0.3359,-0.2968 -0.6758,-0.582 -1.0625,-0.8125 l -1.5312,1.4688 c -0.3125,-0.1367 -0.6368,-0.2422 -0.9688,-0.3125 l -0.4062,-2.0938 c -0.2188,-0.019 -0.4375,-0.031 -0.6563,-0.031 z m 0,4 c 1.6563,0 3,1.3437 3,3 0,1.6562 -1.3437,3 -3,3 -1.6562,0 -3,-1.3438 -3,-3 0,-1.6563 1.3438,-3 3,-3 z m 0,0" transform="matrix(1.5,0,0,1.5,-4465.5,4203.9569)" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/keyboard-shift-symbolic.svg b/data/icons/scalable/status/keyboard-shift-symbolic.svg
new file mode 100644
index 0000000..7255259
--- /dev/null
+++ b/data/icons/scalable/status/keyboard-shift-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 12 4 l -7.5 8 h 4.5 v 7 h 6 v -7 h 4.5 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/message-indicator-symbolic.svg b/data/icons/scalable/status/message-indicator-symbolic.svg
new file mode 100644
index 0000000..9873de5
--- /dev/null
+++ b/data/icons/scalable/status/message-indicator-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 12 8 c 0 2.210938 -1.789062 4 -4 4 s -4 -1.789062 -4 -4 s 1.789062 -4 4 -4 s 4 1.789062 4 4 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/no-events-symbolic.svg b/data/icons/scalable/status/no-events-symbolic.svg
new file mode 100644
index 0000000..9c7c889
--- /dev/null
+++ b/data/icons/scalable/status/no-events-symbolic.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="64px" viewBox="0 0 64 64" width="64px" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#241f31">
+ <path d="m 12 4 c -4.210938 0 -8 3.378906 -8 7.75 v 40.5 c 0 4.371094 3.789062 7.75 8 7.75 h 40 c 4.210938 0 8 -3.378906 8 -7.75 v -40.5 c 0 -4.371094 -3.789062 -7.75 -8 -7.75 z m -2 18 h 12 v 8 h -12 z m 16.125 0 h 11.875 v 8 h -11.875 z m 15.875 0 h 12 v 8 h -12 z m -32 12 h 12 v 8 h -12 z m 16.125 0 h 11.875 v 8 h -11.875 z m 15.875 0 h 12 v 8 h -12 z m -32 12 h 12 v 8 h -12 z m 16.125 0 h 11.875 v 8 h -11.875 z m 15.875 0 h 12 v 8 h -12 z m 0 0" fill-opacity="0.35"/>
+ <path d="m 42 34 h 12 v 8 h -12 z m 0 0" fill-opacity="0.1"/>
+ </g>
+</svg>
diff --git a/data/icons/scalable/status/no-notifications-symbolic.svg b/data/icons/scalable/status/no-notifications-symbolic.svg
new file mode 100644
index 0000000..d30f7e1
--- /dev/null
+++ b/data/icons/scalable/status/no-notifications-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="64px" viewBox="0 0 64 64" width="64px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 32 0 c -3.3125 0 -6 2.6875 -6 6 c 0 0.304688 0.023438 0.601562 0.066406 0.894531 c -8.148437 2.527344 -14.066406 10.125 -14.066406 19.105469 v 18 h -2 c -2.203125 0 -4 1.796875 -4 4 s 1.796875 4 4 4 h 44 c 2.203125 0 4 -1.796875 4 -4 s -1.796875 -4 -4 -4 h -2 v -18 c 0 -8.980469 -5.917969 -16.578125 -14.066406 -19.105469 c 0.042968 -0.292969 0.066406 -0.589843 0.066406 -0.894531 c 0 -3.3125 -2.6875 -6 -6 -6 z m -8 56 c 0 2.859375 1.53125 5.5 4 6.921875 c 2.46875 1.4375 5.53125 1.4375 8 0 c 2.46875 -1.421875 4 -4.0625 4 -6.921875 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/screen-privacy-disabled-symbolic.svg b/data/icons/scalable/status/screen-privacy-disabled-symbolic.svg
new file mode 100644
index 0000000..f64ee0b
--- /dev/null
+++ b/data/icons/scalable/status/screen-privacy-disabled-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 1.53125 0.46875 l -1.0625 1.0625 l 14 14 l 1.0625 -1.0625 l -1.945312 -1.945312 c 0.847656 -0.527344 1.414062 -1.449219 1.414062 -2.523438 v -6 c 0 -1.660156 -1.339844 -3 -3 -3 h -8 c -0.554688 0 -1.070312 0.160156 -1.515625 0.421875 z m 2.53125 2.53125 h 0.199219 l -0.097657 0.097656 z m 2.675781 0 h 0.699219 l -1.6875 1.6875 l -0.351562 -0.351562 z m 2.824219 0 h 1.230469 l -3.367188 3.363281 l -0.613281 -0.613281 z m 2.609375 0.035156 c 0.316406 0.054688 0.578125 0.246094 0.714844 0.523438 l -4.132813 4.132812 l -0.617187 -0.617187 z m -11.171875 1.023438 v 5.941406 c 0 1.660156 1.339844 3 3 3 h 5.941406 l -2 -2 h -1.785156 l 0.894531 -0.894531 l -0.355469 -0.355469 l -1.25 1.25 h -1.238281 l 1.867188 -1.867188 l -0.707031 -0.707031 l -2.160157 2.15625 c -0.121093 -0.164062 -0.207031 -0.359375 -0.207031 -0.582031 v -0.4375 l 1.75 -1.75 l -1.0625 -1.0625 l -0.6875 0.6875 v -0.699219 l 0.339844 -0.339843 z m 12 0.09375 v 5.847656 c 0 0.535156 -0.417969 0.960938 -0.949219 0.988281 l -2.941406 -2.941406 z m -5 9.847656 c -5 0 -5 1 -5 1 c 0 1 1 1 1 1 h 8 c 0.328125 0 0.539062 -0.109375 0.6875 -0.253906 l -1.542969 -1.546875 c -0.730469 -0.113281 -1.71875 -0.199219 -3.144531 -0.199219 z m 0 0" fill="#241f31" fill-rule="evenodd"/>
+</svg>
diff --git a/data/icons/scalable/status/screen-privacy-symbolic.svg b/data/icons/scalable/status/screen-privacy-symbolic.svg
new file mode 100644
index 0000000..c14da9d
--- /dev/null
+++ b/data/icons/scalable/status/screen-privacy-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 4 1 c -1.660156 0 -3 1.339844 -3 3 v 6 c 0 1.660156 1.339844 3 3 3 h 8 c 1.660156 0 3 -1.339844 3 -3 v -6 c 0 -1.660156 -1.339844 -3 -3 -3 z m 0 2 h 0.261719 l -1.261719 1.261719 v -0.261719 c 0 -0.554688 0.445312 -1 1 -1 z m 2.738281 0 h 0.699219 l -4.4375 4.4375 v -0.699219 z m 2.824219 0 h 1.230469 l -7.585938 7.582031 c -0.121093 -0.164062 -0.207031 -0.359375 -0.207031 -0.582031 v -0.4375 z m 2.609375 0.035156 c 0.316406 0.054688 0.578125 0.246094 0.714844 0.523438 l -7.441407 7.441406 h -1.238281 z m 0.828125 1.121094 v 5.84375 c 0 0.554688 -0.445312 1 -1 1 h -5.84375 z m -5 9.84375 c -5 0 -5 1 -5 1 c 0 1 1 1 1 1 h 8 c 1 0 1 -1 1 -1 s 0 -1 -5 -1 z m 0 0" fill="#241f31"/>
+</svg>
diff --git a/data/icons/scalable/status/stop-symbolic.svg b/data/icons/scalable/status/stop-symbolic.svg
new file mode 100644
index 0000000..256276a
--- /dev/null
+++ b/data/icons/scalable/status/stop-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 4 4 h 8 v 8 h -8 z m 0 0" fill="#2e3436"/></svg>
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 0000000..30d4917
--- /dev/null
+++ b/data/meson.build
@@ -0,0 +1,138 @@
+desktop_files = [
+ 'org.gnome.Shell.desktop',
+ 'org.gnome.Shell.Extensions.desktop',
+]
+service_files = []
+
+if have_networkmanager
+ desktop_files += 'org.gnome.Shell.PortalHelper.desktop'
+ service_files += 'org.gnome.Shell.PortalHelper.service'
+endif
+
+desktopconf = configuration_data()
+# We substitute in bindir so it works as an autostart
+# file when built in a non-system prefix
+desktopconf.set('bindir', bindir)
+desktopconf.set('systemd_hidden', have_systemd ? 'true' : 'false')
+
+foreach desktop_file : desktop_files
+ i18n.merge_file(
+ input: configure_file(
+ input: desktop_file + '.in.in',
+ output: desktop_file + '.in',
+ configuration: desktopconf
+ ),
+ output: desktop_file,
+ po_dir: po_dir,
+ install: true,
+ install_dir: desktopdir,
+ type: 'desktop'
+ )
+endforeach
+
+serviceconf = configuration_data()
+serviceconf.set('libexecdir', libexecdir)
+foreach service_file : service_files
+ configure_file(
+ input: service_file + '.in',
+ output: service_file,
+ configuration: serviceconf,
+ install_dir: servicedir
+ )
+endforeach
+
+theme_deps = []
+
+subdir('dbus-interfaces')
+subdir('icons')
+subdir('theme')
+
+data_resources = [
+ {'name': 'dbus-interfaces'},
+ {'name': 'icons'},
+ {'name': 'osk-layouts'},
+ {'name': 'theme', 'deps': theme_deps}
+]
+foreach resource : data_resources
+ name = resource.get('name')
+ deps = resource.get('deps', [])
+
+ gnome.compile_resources(
+ 'gnome-shell-' + name,
+ 'gnome-shell-@0@.gresource.xml'.format(name),
+ source_dir: name,
+ dependencies: deps,
+ gresource_bundle: true,
+ install: true,
+ install_dir: pkgdatadir
+ )
+endforeach
+
+perfconf = configuration_data()
+perfconf.set('datadir', datadir)
+configure_file(
+ input: 'perf-background.xml.in',
+ output: 'perf-background.xml',
+ configuration: perfconf,
+ install_dir: pkgdatadir
+)
+
+keybinding_files = [
+ '50-gnome-shell-launchers.xml',
+ '50-gnome-shell-screenshots.xml',
+ '50-gnome-shell-system.xml',
+]
+
+install_data('gnome-shell.portal', install_dir: portaldir)
+install_data(keybinding_files, install_dir: keysdir)
+
+
+schemaconf = configuration_data()
+schemaconf.set('GETTEXT_PACKAGE', meson.project_name())
+schema = configure_file(
+ input: 'org.gnome.shell.gschema.xml.in',
+ output: 'org.gnome.shell.gschema.xml',
+ configuration: schemaconf,
+ install_dir: schemadir
+)
+install_data('00_org.gnome.shell.gschema.override', install_dir: schemadir)
+
+overrides_migration_conf = configuration_data()
+overrides_migration_conf.set('libexecdir', libexecdir)
+overrides_migration = configure_file(
+ input: 'gnome-shell-overrides-migration.desktop.in',
+ output: 'gnome-shell-overrides-migration.desktop',
+ configuration: overrides_migration_conf,
+ install_dir: autostartdir
+)
+
+if have_systemd
+ unitconf = configuration_data()
+ unitconf.set('bindir', bindir)
+
+ configure_file(
+ input: 'org.gnome.Shell@x11.service.in',
+ output: 'org.gnome.Shell@x11.service',
+ configuration: unitconf,
+ install_dir: systemduserunitdir
+ )
+
+ configure_file(
+ input: 'org.gnome.Shell@wayland.service.in',
+ output: 'org.gnome.Shell@wayland.service',
+ configuration: unitconf,
+ install_dir: systemduserunitdir
+ )
+
+ units = files('org.gnome.Shell.target',
+ 'org.gnome.Shell-disable-extensions.service')
+
+ install_data(units, install_dir: systemduserunitdir)
+endif
+
+# for unit tests - gnome.compile_schemas() only looks in srcdir
+custom_target('compile-schemas',
+ input: schema,
+ output: 'gschemas.compiled',
+ command: [find_program('glib-compile-schemas'), meson.current_build_dir()],
+ build_by_default: true)
diff --git a/data/org.gnome.Shell-disable-extensions.service b/data/org.gnome.Shell-disable-extensions.service
new file mode 100644
index 0000000..de91167
--- /dev/null
+++ b/data/org.gnome.Shell-disable-extensions.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=Disable GNOME Shell extensions after failure
+# Note that this unit must not conflict with anything, and must
+# be able to run in parallel with the gnome-session-shutdown.target.
+DefaultDependencies=no
+
+# We want to disable extensions only if gnome-shell has flagged the extensions
+# to be a likely cause of trouble.
+ConditionPathExists=%t/gnome-shell-disable-extensions
+
+[Service]
+Type=simple
+# Disable extensions
+ExecStart=gsettings set org.gnome.shell disable-user-extensions true
+Restart=no
diff --git a/data/org.gnome.Shell-symbolic.Source.svg b/data/org.gnome.Shell-symbolic.Source.svg
new file mode 100644
index 0000000..b017734
--- /dev/null
+++ b/data/org.gnome.Shell-symbolic.Source.svg
@@ -0,0 +1,511 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="64"
+ height="64"
+ viewBox="0 0 64 64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="org.gnome.Shell-symbolic.Source.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="51.5"
+ inkscape:cy="258"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer2"
+ showgrid="false"
+ units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:snap-intersection-paths="false"
+ inkscape:object-paths="true"
+ inkscape:object-nodes="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:bbox-nodes="false"
+ inkscape:snap-global="true"
+ inkscape:pagecheckerboard="false"
+ showborder="false"
+ inkscape:snap-nodes="true"
+ inkscape:snap-others="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4514"
+ empspacing="4"
+ color="#5e57ff"
+ opacity="0.1254902"
+ empcolor="#5e57ff"
+ empopacity="0.25098039" />
+ </sodipodi:namedview>
+ <g
+ inkscape:label="status"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-161.53331)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:16px;line-height:1.25;font-family:sans-serif;fill:#241f31;fill-opacity:1;stroke:none"
+ x="-4"
+ y="145.53331"
+ id="text12175"><tspan
+ sodipodi:role="line"
+ id="tspan12173"
+ x="-4"
+ y="145.53331"
+ style="font-size:16px;fill:#241f31;fill-opacity:1">status</tspan></text>
+ <g
+ id="g16183"
+ transform="translate(-288,-312)"
+ inkscape:label="notifications alerts calendar events">
+ <title
+ id="title20651">no-notifications</title>
+ <path
+ id="path81"
+ d="m 320,473.53331 c -3.3125,0 -6,2.6875 -6,6 0,0.30385 0.0228,0.60279 0.0664,0.89453 C 305.91768,482.95558 300,490.55273 300,499.53331 v 18 h -2 c -2.20312,0 -4,1.79688 -4,4 0,2.20312 1.79688,4 4,4 h 44 c 2.20312,0 4,-1.79688 4,-4 0,-2.20312 -1.79688,-4 -4,-4 h -2 v -18 c 0,-8.98058 -5.91768,-16.57773 -14.06641,-19.10547 0.0436,-0.29174 0.0664,-0.59068 0.0664,-0.89453 0,-3.3125 -2.6875,-6 -6,-6 z m -8,56 c 0,2.85938 1.53125,5.5 4,6.92188 2.46875,1.43749 5.53125,1.43749 8,0 2.46875,-1.42188 4,-4.0625 4,-6.92188 z"
+ style="fill:#241f31;fill-opacity:1" />
+ <rect
+ transform="scale(-1,1)"
+ y="473.53333"
+ x="-352"
+ height="64"
+ width="64"
+ id="rect15794"
+ style="fill:none" />
+ </g>
+ <g
+ id="g21075"
+ transform="translate(0,-320)"
+ inkscape:label="notifications alerts calendar events">
+ <title
+ id="title36429">message-indicator</title>
+ <rect
+ transform="scale(-1,1)"
+ y="601.53333"
+ x="-16"
+ height="16"
+ width="16"
+ id="rect20973"
+ style="fill:none" />
+ <path
+ d="M 12,609.53332 A 3.9999999,3.9999999 0 0 1 8.0000002,613.53331 3.9999999,3.9999999 0 0 1 4.0000003,609.53332 3.9999999,3.9999999 0 0 1 8.0000002,605.53334 3.9999999,3.9999999 0 0 1 12,609.53332 Z"
+ id="path4485"
+ style="fill:#241f31;fill-opacity:1;stroke-width:3.77953" />
+ </g>
+ <g
+ id="g36685"
+ inkscape:label="screen shield privacy active"
+ transform="translate(-40,-382)">
+ <title
+ id="title41631">screen-privacy</title>
+ <rect
+ transform="scale(-1,1)"
+ y="663.53333"
+ x="-76"
+ height="16"
+ width="16"
+ id="rect36558"
+ style="fill:none" />
+ <path
+ id="path113"
+ style="fill:#241f31;fill-opacity:1"
+ d="m 64,664.53331 c -1.660156,0 -3,1.33984 -3,3 v 6 c 0,1.66016 1.339844,3 3,3 h 8 c 1.660156,0 3,-1.33984 3,-3 v -6 c 0,-1.66016 -1.339844,-3 -3,-3 z m 0,2 h 0.261719 L 63,667.79503 v -0.26172 c 0,-0.55469 0.445312,-1 1,-1 z m 2.738281,0 H 67.4375 L 63,670.97081 v -0.69922 z m 2.824219,0 h 1.230469 l -7.585938,7.58203 C 63.085938,673.95128 63,673.75597 63,673.53331 v -0.4375 z m 2.609375,0.0352 c 0.316406,0.0547 0.578125,0.24609 0.714844,0.52343 l -7.441407,7.44141 H 64.207031 Z M 73,667.68956 v 5.84375 c 0,0.55469 -0.445312,1 -1,1 h -5.84375 z m -5,9.84375 c -5,0 -5,1 -5,1 0,1 1,1 1,1 h 8 c 1,0 1,-1 1,-1 0,0 0,-1 -5,-1 z" />
+ </g>
+ <g
+ id="g41416"
+ inkscape:label="status"
+ transform="translate(-40,-382)">
+ <title
+ id="title41633">screen-privacy-disabled</title>
+ <rect
+ transform="scale(-1,1)"
+ y="663.53333"
+ x="-96"
+ height="16"
+ width="16"
+ id="rect36687"
+ style="fill:none" />
+ <path
+ d="m 81.53125,664.00213 -1.0625,1.0625 14,14 1.0625,-1.0625 -1.945312,-1.94531 C 94.433594,675.52947 95,674.6076 95,673.53338 v -6 c 0,-1.66016 -1.339844,-3 -3,-3 h -8 c -0.554688,0 -1.070312,0.16016 -1.515625,0.42188 z m 2.53125,2.53125 h 0.199219 l -0.09766,0.0977 z m 2.675781,0 H 87.4375 l -1.6875,1.6875 -0.351562,-0.35156 z m 2.824219,0 h 1.230469 l -3.367188,3.36328 -0.613281,-0.61328 z m 2.609375,0.0352 c 0.316406,0.0547 0.578125,0.24609 0.714844,0.52343 l -4.132813,4.13282 -0.617187,-0.61719 z M 81,667.59197 v 5.94141 c 0,1.66016 1.339844,3 3,3 h 5.941406 l -2,-2 H 86.15625 l 0.894531,-0.89453 -0.355469,-0.35547 -1.25,1.25 h -1.238281 l 1.867188,-1.86719 -0.707031,-0.70703 -2.160157,2.15625 C 83.085938,673.95135 83,673.75604 83,673.53338 v -0.4375 l 1.75,-1.75 -1.0625,-1.0625 -0.6875,0.6875 v -0.69922 l 0.339844,-0.33984 z m 12,0.0937 v 5.84766 c 0,0.53516 -0.417969,0.96094 -0.949219,0.98828 l -2.941406,-2.9414 z m -5,9.84766 c -5,0 -5,1 -5,1 0,1 1,1 1,1 h 8 c 0.328125,0 0.539062,-0.10937 0.6875,-0.25391 l -1.542969,-1.54687 C 90.414062,677.61927 89.425781,677.53333 88,677.53333 Z m 0,0"
+ fill="#2e3436"
+ fill-rule="evenodd"
+ id="path113-6"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g53379"
+ inkscape:label="notifications alerts calendar events"
+ transform="translate(0,-360)">
+ <title
+ id="title58232">no-events</title>
+ <rect
+ transform="scale(-1,1)"
+ y="521.53333"
+ x="-144"
+ height="64"
+ width="64"
+ id="rect53237"
+ style="fill:none" />
+ <path
+ d="m 92,525.53331 c -4.20948,0 -8,3.38032 -8,7.75 v 40.5 c 0,4.36968 3.79052,7.75 8,7.75 h 40 c 4.20948,0 8,-3.38032 8,-7.75 v -40.5 c 0,-4.36968 -3.79052,-7.75 -8,-7.75 z m -2,18 h 12 v 8 H 90 Z m 16.125,0 H 118 v 8 h -11.875 z m 15.875,0 h 12 v 8 h -12 z m -32,12 h 12 v 8 H 90 Z m 16.125,0 H 118 v 8 h -11.875 z m 15.875,0 h 12 v 8 h -12 z m -32,12 h 12 v 8 H 90 Z m 16.125,0 H 118 v 8 h -11.875 z m 15.875,0 h 12 v 8 h -12 z"
+ id="path19157"
+ style="fill:#241f31;fill-opacity:0.35" />
+ <rect
+ height="7.9999971"
+ id="rect19159"
+ width="11.999997"
+ x="121.99993"
+ y="555.53333"
+ style="fill:#241f31;fill-opacity:0.1" />
+ </g>
+ <g
+ id="g8970"
+ transform="translate(-24,-194.46669)"
+ inkscape:label="on-screen keyboard keys">
+ <title
+ id="title14941">keyboard-shift</title>
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="23.99999"
+ y="443.99997"
+ id="rect8636"
+ style="fill:none" />
+ <path
+ d="m 36,448 -7.5,8 H 33 v 7 h 6 v -7 h 4.5 z"
+ id="path1150"
+ style="fill:#241f31;fill-opacity:1;stroke-width:0.666667" />
+ </g>
+ <g
+ id="g8978"
+ transform="translate(8,-194.46669)"
+ inkscape:label="on-screen keyboard keys">
+ <title
+ id="title14939">keyboard-caps-lock</title>
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="23.99999"
+ y="443.99997"
+ id="rect8972"
+ style="fill:none" />
+ <path
+ id="path8974"
+ d="m 36,446 -7.5,8 H 33 v 5 h 6 v -5 h 4.5 z m -3,16 v 3 h 6 v -3 z"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g13560"
+ inkscape:label="on-screen keyboard keys"
+ transform="translate(-40,-214.46669)">
+ <title
+ id="title14945">keyboard-layout</title>
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="103.99999"
+ y="463.99997"
+ id="rect13441"
+ style="fill:none" />
+ <path
+ d="m 109,467 v 19 h 2 v -8 h 4.383 l 0.722,1.447 c 0.16942,0.33905 0.51597,0.55318 0.895,0.553 h 7 c 0.55228,0 1,-0.44772 1,-1 v -9 c 0,-0.55228 -0.44772,-1 -1,-1 h -5.383 l -0.722,-1.447 C 117.72558,467.21395 117.37903,466.99982 117,467 h -7 z"
+ id="path1085"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g13678"
+ inkscape:label="keyboard keys hide "
+ transform="translate(-40,-214.46669)">
+ <title
+ id="title14947">keyboard-hide</title>
+ <path
+ d="m 139.99985,472 a 1,1 0 0 0 -0.70703,0.29297 1,1 0 0 0 0,1.41406 l 8,8 a 1.0001,1.0001 0 0 0 1.41406,0 l 8,-8 a 1,1 0 0 0 0,-1.41406 1,1 0 0 0 -1.41406,0 l -7.29297,7.29297 -7.29297,-7.29297 A 1,1 0 0 0 139.99985,472 Z"
+ id="path4931"
+ style="fill:#241f31;fill-opacity:1" />
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="-487.99997"
+ y="-159.99998"
+ id="rect13562"
+ transform="matrix(0,-1,-1,0,0,0)"
+ style="fill:none" />
+ </g>
+ <g
+ id="g13796"
+ inkscape:label="on-screen keyboard keys"
+ transform="translate(-40,-214.46669)">
+ <title
+ id="title14943">keyboard-enter</title>
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="-487.99997"
+ y="-191.99998"
+ id="rect13680"
+ transform="matrix(0,-1,-1,0,0,0)"
+ style="fill:none" />
+ <path
+ id="path955"
+ style="fill:#241f31;fill-opacity:1"
+ d="m 187,470 v 6.00391 c 0.006,0.996 -1,0.99609 -1,0.99609 h -11.58594 l 1.29297,-1.29297 a 1,1 0 0 0 0,-1.41406 A 1,1 0 0 0 175,474 a 1,1 0 0 0 -0.70703,0.29297 l -3,3 a 1.0001,1.0001 0 0 0 0,1.41406 l 3,3 a 1,1 0 0 0 1.41406,0 1,1 0 0 0 0,-1.41406 L 174.41602,479 H 186 c 3.006,0 3,-3 3,-3 v -6 z" />
+ </g>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="actions">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:16px;line-height:1.25;font-family:sans-serif;fill:#241f31;fill-opacity:1;stroke:none"
+ x="-4"
+ y="184"
+ id="text12715"><tspan
+ sodipodi:role="line"
+ id="tspan12713"
+ x="-4"
+ y="184"
+ style="font-size:16px;fill:#241f31;fill-opacity:1">actions</tspan></text>
+ <g
+ id="g13433"
+ inkscape:label="overview previous left backward"
+ transform="translate(-40,-240)">
+ <title
+ id="title15448">carousel-arrow-previous</title>
+ <path
+ d="m 53.169107,497.03665 a 1.5,1.5 0 0 0 -0.9375,0.66211 l -6,9.5 a 1.50015,1.50015 0 0 0 0,1.60156 l 6,9.5 a 1.5,1.5 0 0 0 2.068359,0.4668 1.5,1.5 0 0 0 0.466797,-2.06836 l -5.49414,-8.69922 5.49414,-8.69922 a 1.5,1.5 0 0 0 -0.466797,-2.06836 1.5,1.5 0 0 0 -1.130859,-0.19531 z"
+ id="path13468"
+ style="fill:#241f31;fill-opacity:1" />
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="39.999992"
+ y="495.99997"
+ id="rect12821"
+ style="fill:none" />
+ </g>
+ <g
+ id="g13439"
+ transform="matrix(-1,0,0,1,95.99998,-240)"
+ inkscape:label="overview next right forward">
+ <title
+ id="title15446">carousel-arrow-next</title>
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="39.999992"
+ y="495.99997"
+ id="rect13435"
+ style="fill:none" />
+ <path
+ d="m 53.169235,497.03711 c -0.387918,0.0877 -0.725132,0.32585 -0.9375,0.66211 l -6,9.5 c -0.34001,0.56012 -0.308809,1.11236 0,1.60156 l 6,9.5 c 0.442487,0.69974 1.368227,0.90867 2.068359,0.4668 0.69974,-0.44249 0.908665,-1.36823 0.466797,-2.06836 L 49.272751,508 l 5.49414,-8.69922 c 0.441868,-0.70013 0.232943,-1.62587 -0.466797,-2.06836 -0.336162,-0.21243 -0.742919,-0.28268 -1.130859,-0.19531 z"
+ id="path13468-5"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g29998"
+ inkscape:label="mouse pointer dwell click"
+ transform="translate(28,-344)">
+ <title
+ id="title39373">pointer-secondary-click</title>
+ <path
+ d="m 16,640 v 19.77148 l -4.24414,-4.14258 c -0.612408,1.21538 -1.173049,2.46023 -1.819709,3.6561 -1.141947,1.47628 -3.975266,0.38901 -3.846009,-1.47361 0.09413,-0.82252 0.594755,-1.52142 0.899969,-2.2776 l 0.896608,-1.92051 H 2.617188 Z m -1.5,3.72656 -8.359375,8.38672 h 4.101563 c -0.885072,1.9032 -1.781799,3.80152 -2.659622,5.70774 -0.0349,0.56609 1.149057,0.98883 1.282093,0.29797 l 2.455699,-5.01348 3.179687,3.10351 z"
+ id="path5851"
+ style="fill:#241f31;fill-opacity:1" />
+ <rect
+ width="20"
+ height="20"
+ x="-7.6293945e-06"
+ y="640"
+ id="rect1156"
+ style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:1.781;marker:none;enable-background:new" />
+ </g>
+ <g
+ id="g29992"
+ inkscape:label="mouse pointer dwell click"
+ transform="translate(-28,-344)">
+ <title
+ id="title39371">pointer-primary-click</title>
+ <rect
+ width="20"
+ height="20"
+ x="27.999992"
+ y="640"
+ id="rect1244"
+ style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:1.781;marker:none;enable-background:new" />
+ <path
+ d="m 32,640 v 19.64648 l 4.24414,-4.14257 c 0.612408,1.21537 1.173049,2.46022 1.819709,3.65609 1.141947,1.47628 3.975266,0.38901 3.846009,-1.47361 -0.09413,-0.82252 -0.594755,-1.52142 -0.899969,-2.2776 l -0.896608,-1.92051 h 5.269531 z m 1.5,3.72656 8.359375,8.26172 h -4.101563 c 0.885072,1.9032 1.781799,3.80152 2.659622,5.70774 0.0349,0.56609 -1.149057,0.98883 -1.282093,0.29798 l -2.455699,-5.01349 -3.179687,3.10352 z"
+ id="path5565"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g14643"
+ inkscape:label="mouse pointer dwell click drag"
+ transform="translate(0,-344)">
+ <title
+ id="title39369">pointer-drag</title>
+ <rect
+ width="20"
+ height="20"
+ x="55.999992"
+ y="640"
+ id="rect9430"
+ style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:1.781;marker:none;enable-background:new" />
+ <path
+ d="m 66.92607,642.31969 c -0.836524,0.0747 -1.818151,-0.13657 -2.447266,0.57617 -0.179249,0.5031 -0.39107,0.72969 -0.945312,0.61718 -0.793816,-0.0511 -1.701912,0.0538 -2.1875,0.77344 -0.554908,0.83096 -0.313681,1.85623 -0.367187,2.79297 v 1.50195 c -0.509721,-0.31797 -1.13192,-0.13205 -1.699219,-0.18164 -0.442833,-0.0207 -0.881113,0.0252 -1.31836,0.0918 0.05693,2.68593 0.07373,5.37334 0.121094,8.05859 0.09369,1.3023 1.232044,2.40482 2.544922,2.42383 0.632949,0.0151 1.48233,0.007 2.224609,0.0117 3.221817,-0.0121 6.446114,0.0498 9.666016,-0.0566 1.162213,-0.12022 2.213268,-1.18845 2.070313,-2.39648 v -9.48047 c -0.706809,-0.94069 -1.815276,-1.78087 -3.058594,-1.57032 -0.299549,-0.45356 -0.486118,-1.47311 -1.226563,-1.79882 -0.354855,-0.29104 -0.839588,-0.10977 -1.259765,-0.16211 -0.297442,-0.0727 -0.8559,0.19254 -0.869141,-0.25196 -0.157351,-0.52152 -0.715235,-0.90856 -1.248047,-0.94921 z m -0.105469,1.5039 c 0.164522,0.27146 0.182773,0.75372 0.21875,1.125 0.0066,0.007 0.0198,0.006 0.02734,0.0117 -0.0091,-1.7e-4 -0.01824,1.7e-4 -0.02734,0 l 0.494141,5.90039 0.664062,-5.8418 1.345704,0.004 c 0.536079,0.38955 0.412986,1.14048 0.554687,1.72266 -0.01113,0.0675 0.0064,0.096 0.01563,0.13672 -6.5e-4,-10e-6 -0.0013,0 -0.002,0 l 0.01172,0.0859 0.482422,3.86524 0.664062,-3.91993 c 0.574035,-0.0371 1.157689,0.0385 1.558594,0.50196 0.40964,0.21686 0.215641,0.68897 0.259766,1.05859 v 8.3418 c -0.337059,0.96368 -1.599977,0.54827 -2.375,0.66016 -3.370517,0.0131 -6.742137,0.0338 -10.111329,-0.0137 -0.975625,-0.0283 -1.141898,-1.06997 -1.040974,-1.84179 -0.01164,-1.90706 -0.04474,-3.81393 -0.07813,-5.72071 0.326364,0.006 0.675958,-0.0127 0.988281,0.01 0.493884,0.91523 0.484615,2.00119 0.507813,3.01563 0.492636,-0.007 1.043381,0.0398 1.5,0 0.0091,-2.51961 -0.01844,-5.04003 0.01367,-7.5586 0.104898,-0.5199 0.730745,-0.30916 1.111329,-0.35352 h 0.675781 l 0.488281,5.84766 0.669922,-5.87695 h -0.0039 c 0.03095,-0.38284 -0.150626,-0.89584 0.24805,-1.11914 z"
+ id="path5630"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g30454"
+ inkscape:label="mouse pointer dwell click double"
+ transform="translate(0,-344)">
+ <title
+ id="title39367">pointer-double-click</title>
+ <rect
+ width="20"
+ height="20"
+ x="83.999992"
+ y="640"
+ id="rect14651"
+ style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:1.781;marker:none;enable-background:new" />
+ <path
+ d="m 85,640 v 19.70898 l 4.24414,-4.14453 c 0.5882,1.18866 1.158389,2.38656 1.758464,3.56913 1.108631,1.64026 4.157538,0.46513 3.89799,-1.49586 0.01057,-0.47022 -0.405577,-0.90844 -0.434651,-1.31364 l 0.778203,-0.75963 c 0.5882,1.18866 1.158389,2.38656 1.758464,3.56913 1.108631,1.64026 4.15754,0.46513 3.89799,-1.49586 -0.19232,-0.9043 -0.71785,-1.69802 -1.068629,-2.54897 l -0.716725,-1.53797 h 4.830074 L 91,640 v 6.24414 z m 1.5,3.72656 6,6.24414 v -6.24414 l 7.92188,8.32422 h -3.662115 c 0.883468,1.90344 1.781983,3.80027 2.656081,5.70782 0.0065,0.62278 -1.227555,0.98057 -1.325116,0.20712 l -2.412991,-4.92076 -3.060547,2.98633 c 0.263642,0.60805 0.596803,1.19246 0.814693,1.81614 -0.182662,0.60103 -1.26833,0.8373 -1.365856,0.0679 l -2.388284,-4.87037 -3.177734,3.10156 z"
+ id="path5565-5"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g1429"
+ transform="translate(-132,-248)"
+ inkscape:label="screenshooter capture area ">
+ <title
+ id="title1423">screenshot-ui-area</title>
+ <rect
+ width="31.999998"
+ height="31.999998"
+ x="-491.99994"
+ y="-163.99998"
+ id="rect1425"
+ transform="matrix(0,-1,-1,0,0,0)"
+ style="fill:none;stroke-width:1" />
+ <path
+ d="m 135.5,462 a 3.515,3.515 0 0 0 -3.5,3.5 c 0,1.385 0.822,2.593 2,3.158 V 472 h 2 v -3.037 A 3.521,3.521 0 0 0 138.963,466 H 141 v -2 h -2.342 a 3.516,3.516 0 0 0 -3.158,-2 z m 25,0 a 3.516,3.516 0 0 0 -3.158,2 H 155 v 2 h 2.037 A 3.521,3.521 0 0 0 160,468.963 V 472 h 2 v -3.342 a 3.516,3.516 0 0 0 2,-3.158 c 0,-1.921 -1.579,-3.5 -3.5,-3.5 z m -25,2 c 0.84,0 1.5,0.66 1.5,1.5 0,0.84 -0.66,1.5 -1.5,1.5 -0.84,0 -1.5,-0.66 -1.5,-1.5 0,-0.84 0.66,-1.5 1.5,-1.5 z m 7.5,0 v 2 h 4 v -2 z m 6,0 v 2 h 4 v -2 z m 11.5,0 c 0.84,0 1.5,0.66 1.5,1.5 0,0.84 -0.66,1.5 -1.5,1.5 -0.84,0 -1.5,-0.66 -1.5,-1.5 0,-0.84 0.66,-1.5 1.5,-1.5 z M 134,474 v 4 h 2 v -4 z m 26,0 v 4 h 2 v -4 z m -26,6 v 3.342 a 3.516,3.516 0 0 0 -2,3.158 c 0,1.921 1.579,3.5 3.5,3.5 a 3.516,3.516 0 0 0 3.158,-2 H 141 v -2 h -2.037 A 3.521,3.521 0 0 0 136,483.037 V 480 Z m 26,0 v 3.037 A 3.521,3.521 0 0 0 157.037,486 H 155 v 2 h 2.342 a 3.516,3.516 0 0 0 3.158,2 c 1.921,0 3.5,-1.579 3.5,-3.5 a 3.516,3.516 0 0 0 -2,-3.158 V 480 Z m -24.5,5 c 0.84,0 1.5,0.66 1.5,1.5 0,0.84 -0.66,1.5 -1.5,1.5 -0.84,0 -1.5,-0.66 -1.5,-1.5 0,-0.84 0.66,-1.5 1.5,-1.5 z m 25,0 c 0.84,0 1.5,0.66 1.5,1.5 0,0.84 -0.66,1.5 -1.5,1.5 -0.84,0 -1.5,-0.66 -1.5,-1.5 0,-0.84 0.66,-1.5 1.5,-1.5 z m -17.5,1 v 2 h 4 v -2 z m 6,0 v 2 h 4 v -2 z"
+ id="path2-3"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g4349"
+ transform="translate(-92,-248)"
+ inkscape:label="screenshooter capture display screen ">
+ <title
+ id="title4343">screenshot-ui-display</title>
+ <rect
+ width="31.999998"
+ height="31.999998"
+ x="-491.99994"
+ y="-163.99998"
+ id="rect4345"
+ transform="matrix(0,-1,-1,0,0,0)"
+ style="fill:none;stroke-width:1" />
+ <path
+ id="path9443"
+ d="m 136,464 c -1.662,0 -3,1.338 -3,3 v 14 c 0,1.662 1.338,3 3,3 h 10 v 2.99805 h -3.00195 c -1.0901,-7e-5 -1.99805,0.90995 -1.99805,2 H 142.99805 153 155 C 155,487.908 154.09,487.00007 153,487 h -2.99805 v -3 H 160 c 1.662,0 3,-1.338 3,-3 v -14 c 0,-1.662 -1.338,-3 -3,-3 z m 0,2 h 24 c 0.554,0 1,0.446 1,1 v 14 c 0,0.554 -0.446,1 -1,1 h -24 c -0.554,0 -1,-0.446 -1,-1 v -14 c 0,-0.554 0.446,-1 1,-1 z"
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+ <g
+ id="g4445"
+ transform="translate(-52,-248)"
+ inkscape:label="screenshooter capture windows">
+ <title
+ id="title4439">screenshot-ui-window</title>
+ <path
+ id="rect28516"
+ d="m 135,463 c -1.6447,0 -3,1.3553 -3,3 v 14 c 0,1.6447 1.3553,3 3,3 h 3.99805 v 3 c 0,1.6447 1.3553,3 3,3 h 18.99804 c 1.6447,0 3,-1.3553 3,-3 v -14 c 0,-1.6447 -1.3553,-3 -3,-3 h -4 v -3 c 0,-1.6447 -1.3553,-3 -3,-3 z m 0,2 h 18.99609 c 0.5713,0 1,0.4287 1,1 v 3 h -12.99804 c -1.6447,0 -3,1.3553 -3,3 v 9 H 135 c -0.5713,0 -1,-0.4287 -1,-1 v -14 c 0,-0.5713 0.4287,-1 1,-1 z m 6.99805,6 h 18.99804 c 0.5713,0 1,0.4287 1,1 v 14 c 0,0.5713 -0.4287,1 -1,1 h -18.99804 c -0.5713,0 -1,-0.4287 -1,-1 v -14 c 0,-0.5713 0.4287,-1 1,-1 z"
+ style="fill:#241f31;fill-opacity:1" />
+ <rect
+ width="31.999998"
+ height="31.999998"
+ x="-491.99994"
+ y="-163.99998"
+ id="rect4441"
+ transform="matrix(0,-1,-1,0,0,0)"
+ style="fill:none;stroke-width:1" />
+ </g>
+ <g
+ id="g42319"
+ transform="translate(-12,-248)"
+ inkscape:label="screenshooter capture windows">
+ <title
+ id="title42313">screenshot-ui-window</title>
+ <path
+ id="path42315"
+ d="m 135,463 c -1.6447,0 -3,1.3553 -3,3 v 14 c 0,1.6447 1.3553,3 3,3 h 3.99805 v 3 c 0,1.6447 1.3553,3 3,3 h 18.99804 c 1.6447,0 3,-1.3553 3,-3 v -14 c 0,-1.6447 -1.3553,-3 -3,-3 h -4 v -3 c 0,-1.6447 -1.3553,-3 -3,-3 z m 0,2 h 18.99609 c 0.5713,0 1,0.4287 1,1 v 3 h -12.99804 c -1.6447,0 -3,1.3553 -3,3 v 9 H 135 c -0.5713,0 -1,-0.4287 -1,-1 v -14 c 0,-0.5713 0.4287,-1 1,-1 z m 6.99805,6 h 18.99804 c 0.5713,0 1,0.4287 1,1 v 14 c 0,0.5713 -0.4287,1 -1,1 h -18.99804 c -0.5713,0 -1,-0.4287 -1,-1 v -14 c 0,-0.5713 0.4287,-1 1,-1 z"
+ style="fill:#241f31;fill-opacity:1" />
+ <rect
+ width="31.999998"
+ height="31.999998"
+ x="-491.99994"
+ y="-163.99998"
+ id="rect42317"
+ transform="matrix(0,-1,-1,0,0,0)"
+ style="fill:none;stroke-width:1" />
+ </g>
+ <g
+ id="g42429"
+ inkscape:label="screenshooter capture display screen "
+ transform="translate(-80,-335.53331)">
+ <title
+ id="title42423">screenshot-ui-show-pointer</title>
+ <rect
+ transform="scale(-1,1)"
+ y="663.53333"
+ x="-96"
+ height="16"
+ width="16"
+ id="rect42425"
+ style="fill:none" />
+ <path
+ d="m 82.95312,664.60753 2.41797,13.21094 3.23828,-2.39844 2.05469,2.64844 c 1.03125,1.43359 3.14844,-0.21094 2.01172,-1.5625 l -2.01562,-2.59375 2.98437,-2.17579 z m 0,0"
+ fill="#2e3436"
+ id="path2-35" />
+ </g>
+ <g
+ id="g14227"
+ transform="translate(-71.999987,-207.99997)"
+ inkscape:label="overview window close">
+ <title
+ id="title15444">preview-close</title>
+ <rect
+ width="23.999996"
+ height="23.999996"
+ x="-487.99997"
+ y="-159.99998"
+ id="rect14223"
+ transform="matrix(0,-1,-1,0,0,0)"
+ style="fill:none" />
+ </g>
+ <path
+ id="path986"
+ d="M 71 262 A 1 1 0 0 0 70.292969 262.29297 A 1 1 0 0 0 70.292969 263.70703 L 74.585938 268 L 70.292969 272.29297 A 1 1 0 0 0 70.292969 273.70703 A 1 1 0 0 0 71.707031 273.70703 L 76 269.41406 L 80.292969 273.70703 A 1 1 0 0 0 81.707031 273.70703 A 1 1 0 0 0 81.707031 272.29297 L 77.414062 268 L 81.707031 263.70703 A 1 1 0 0 0 81.707031 262.29297 A 1 1 0 0 0 81 262 A 1 1 0 0 0 80.292969 262.29297 L 76 266.58594 L 71.707031 262.29297 A 1 1 0 0 0 71 262 z "
+ style="fill:#241f31;fill-opacity:1" />
+ </g>
+</svg>
diff --git a/data/org.gnome.Shell.Extensions.desktop.in.in b/data/org.gnome.Shell.Extensions.desktop.in.in
new file mode 100644
index 0000000..9c3b6bc
--- /dev/null
+++ b/data/org.gnome.Shell.Extensions.desktop.in.in
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+# Keep in sync with subprojects/extensions-app
+Name=Extensions
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=org.gnome.Shell.Extensions
+# Never launch this, just provide name+icon to portal dialog
+Exec=false
+OnlyShowIn=GNOME;
+NoDisplay=true
diff --git a/data/org.gnome.Shell.PortalHelper.desktop.in.in b/data/org.gnome.Shell.PortalHelper.desktop.in.in
new file mode 100644
index 0000000..269669e
--- /dev/null
+++ b/data/org.gnome.Shell.PortalHelper.desktop.in.in
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Name=Network Login
+Type=Application
+Exec=gapplication launch org.gnome.Shell.PortalHelper
+DBusActivatable=true
+NoDisplay=true
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=network-workgroup
+StartupNotify=true
+OnlyShowIn=GNOME;
diff --git a/data/org.gnome.Shell.PortalHelper.service.in b/data/org.gnome.Shell.PortalHelper.service.in
new file mode 100644
index 0000000..5465a32
--- /dev/null
+++ b/data/org.gnome.Shell.PortalHelper.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Shell.PortalHelper
+Exec=@libexecdir@/gnome-shell-portal-helper
diff --git a/data/org.gnome.Shell.desktop.in.in b/data/org.gnome.Shell.desktop.in.in
new file mode 100644
index 0000000..59d1cd2
--- /dev/null
+++ b/data/org.gnome.Shell.desktop.in.in
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Type=Application
+Name=GNOME Shell
+Comment=Window management and application launching
+Exec=@bindir@/gnome-shell
+Categories=GNOME;GTK;Core;
+OnlyShowIn=GNOME;
+NoDisplay=true
+X-GNOME-Autostart-Phase=DisplayServer
+X-GNOME-Provides=panel;windowmanager;
+X-GNOME-Autostart-Notify=true
+X-GNOME-AutoRestart=false
+X-GNOME-HiddenUnderSystemd=@systemd_hidden@
diff --git a/data/org.gnome.Shell.target b/data/org.gnome.Shell.target
new file mode 100644
index 0000000..55f9539
--- /dev/null
+++ b/data/org.gnome.Shell.target
@@ -0,0 +1,10 @@
+[Unit]
+Description=GNOME Shell
+DefaultDependencies=no
+
+Requisite=gnome-session-initialized.target
+PartOf=gnome-session-initialized.target
+Before=gnome-session-initialized.target
+
+Wants=org.gnome.Shell@wayland.service
+Wants=org.gnome.Shell@x11.service
diff --git a/data/org.gnome.Shell@wayland.service.in b/data/org.gnome.Shell@wayland.service.in
new file mode 100644
index 0000000..76d5ccb
--- /dev/null
+++ b/data/org.gnome.Shell@wayland.service.in
@@ -0,0 +1,37 @@
+[Unit]
+Description=GNOME Shell on Wayland
+# On wayland, force a session shutdown
+OnFailure=org.gnome.Shell-disable-extensions.service gnome-session-shutdown.target
+OnFailureJobMode=replace-irreversibly
+CollectMode=inactive-or-failed
+RefuseManualStart=on
+RefuseManualStop=on
+
+After=gnome-session-manager.target
+
+Requisite=gnome-session-initialized.target
+PartOf=gnome-session-initialized.target
+Before=gnome-session-initialized.target
+
+[Service]
+Slice=session.slice
+Type=notify
+# NOTE: This can be replaced with ConditionEnvironment=XDG_SESSION_TYPE=%I in
+# the [Unit] section with systemd >= 246. Also, the current solution is
+# kind of painful as systemd had a bug where it retries the condition.
+# Only start if the template instance matches the session type.
+ExecCondition=/bin/sh -c 'test "$XDG_SESSION_TYPE" = "%I" || exit 2'
+ExecStart=@bindir@/gnome-shell
+# Exit code 1 means we are probably *not* dealing with an extension failure
+SuccessExitStatus=1
+
+# unset some environment variables that were set by the shell and won't work now that the shell is gone
+ExecStopPost=-/bin/sh -c 'test "$SERVICE_RESULT" != "exec-condition" && systemctl --user unset-environment GNOME_SETUP_DISPLAY WAYLAND_DISPLAY DISPLAY XAUTHORITY'
+
+# On wayland we cannot restart
+Restart=no
+# Kill any stubborn child processes after this long
+TimeoutStopSec=5
+
+# Lower down gnome-shell's OOM score to avoid being killed by OOM-killer too early
+OOMScoreAdjust=-1000
diff --git a/data/org.gnome.Shell@x11.service.in b/data/org.gnome.Shell@x11.service.in
new file mode 100644
index 0000000..078d87a
--- /dev/null
+++ b/data/org.gnome.Shell@x11.service.in
@@ -0,0 +1,42 @@
+[Unit]
+Description=GNOME Shell on X11
+# On X11, try to show the GNOME Session Failed screen
+OnFailure=org.gnome.Shell-disable-extensions.service gnome-session-failed.target
+OnFailureJobMode=replace
+CollectMode=inactive-or-failed
+RefuseManualStart=on
+RefuseManualStop=on
+
+After=gnome-session-manager.target
+
+Requisite=gnome-session-initialized.target
+PartOf=gnome-session-initialized.target
+Before=gnome-session-initialized.target
+
+# Limit startup frequency more than the default
+StartLimitIntervalSec=15s
+StartLimitBurst=3
+
+[Service]
+Slice=session.slice
+Type=notify
+# NOTE: This can be replaced with ConditionEnvironment=XDG_SESSION_TYPE=%I in
+# the [Unit] section with systemd >= 246. Also, the current solution is
+# kind of painful as systemd had a bug where it retries the condition.
+# Only start if the template instance matches the session type.
+ExecCondition=/bin/sh -c 'test "$XDG_SESSION_TYPE" = "%I" || exit 2'
+ExecStart=@bindir@/gnome-shell
+# Exit code 1 means we are probably *not* dealing with an extension failure
+SuccessExitStatus=1
+
+# On X11 we do not need to unset any variables
+
+# On X11 we want to restart on-success (Alt+F2 + r) and on-failure.
+Restart=always
+# Do not wait before restarting the shell
+RestartSec=0ms
+# Kill any stubborn child processes after this long
+TimeoutStopSec=5
+
+# Lower down gnome-shell's OOM score to avoid being killed by OOM-killer too early
+OOMScoreAdjust=-1000
diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in
new file mode 100644
index 0000000..86ad1b2
--- /dev/null
+++ b/data/org.gnome.shell.gschema.xml.in
@@ -0,0 +1,364 @@
+<schemalist>
+ <schema id="org.gnome.shell" path="/org/gnome/shell/"
+ gettext-domain="@GETTEXT_PACKAGE@">
+ <key name="development-tools" type="b">
+ <default>true</default>
+ <summary>
+ Enable internal tools useful for developers and testers from Alt-F2
+ </summary>
+ <description>
+ Allows access to internal debugging and monitoring tools
+ using the Alt-F2 dialog.
+ </description>
+ </key>
+ <key name="enabled-extensions" type="as">
+ <default>[]</default>
+ <summary>UUIDs of extensions to enable</summary>
+ <description>
+ GNOME Shell extensions have a UUID property; this key lists extensions
+ which should be loaded. Any extension that wants to be loaded needs
+ to be in this list. You can also manipulate this list with the
+ EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
+ </description>
+ </key>
+ <key name="disabled-extensions" type="as">
+ <default>[]</default>
+ <summary>UUIDs of extensions to force disabling</summary>
+ <description>
+ GNOME Shell extensions have a UUID property; this key lists extensions
+ which should be disabled, even if loaded as part of the current mode.
+ You can also manipulate this list with the EnableExtension and
+ DisableExtension D-Bus methods on org.gnome.Shell.
+ This key takes precedence over the “enabled-extensions” setting.
+ </description>
+ </key>
+ <key name="disable-user-extensions" type="b">
+ <default>false</default>
+ <summary>Disable user extensions</summary>
+ <description>
+ Disable all extensions the user has enabled without affecting
+ the “enabled-extension” setting.
+ </description>
+ </key>
+ <key name="disable-extension-version-validation" type="b">
+ <default>false</default>
+ <summary>Disables the validation of extension version compatibility</summary>
+ <description>
+ GNOME Shell will only load extensions that claim to support the current
+ running version. Enabling this option will disable this check and try to
+ load all extensions regardless of the versions they claim to support.
+ </description>
+ </key>
+ <key name="favorite-apps" type="as">
+ <default>[ 'org.gnome.Epiphany.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
+ <summary>List of desktop file IDs for favorite applications</summary>
+ <description>
+ The applications corresponding to these identifiers
+ will be displayed in the favorites area.
+ </description>
+ </key>
+ <key name="command-history" type="as">
+ <default>[]</default>
+ <summary>History for command (Alt-F2) dialog</summary>
+ </key>
+ <key name="looking-glass-history" type="as">
+ <default>[]</default>
+ <!-- Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass -->
+ <summary>History for the looking glass dialog</summary>
+ </key>
+ <key name="always-show-log-out" type="b">
+ <default>false</default>
+ <summary>Always show the “Log out” menu item in the user menu.</summary>
+ <description>
+ This key overrides the automatic hiding of the “Log out”
+ menu item in single-user, single-session situations.
+ </description>
+ </key>
+ <key name="remember-mount-password" type="b">
+ <default>false</default>
+ <summary>Whether to remember password for mounting encrypted or remote filesystems</summary>
+ <description>
+ The shell will request a password when an encrypted device or a
+ remote filesystem is mounted. If the password can be saved for
+ future use a “Remember Password” checkbox will be present.
+ This key sets the default state of the checkbox.
+ </description>
+ </key>
+ <key name="last-selected-power-profile" type="s">
+ <default>"power-saver"</default>
+ <summary>The last selected non-default power profile</summary>
+ <description>
+ Some systems support more than two power profiles. In order to still support
+ toggling between two profiles, this key records the last selected non-default
+ profile.
+ </description>
+ </key>
+ <key name="welcome-dialog-last-shown-version" type="s">
+ <default>''</default>
+ <summary>The last version the “Welcome to GNOME” dialog was shown for</summary>
+ <description>
+ This key determines for which version the “Welcome to GNOME” dialog was
+ last shown. An empty string represents the oldest possible version, and
+ a huge number will represent versions that do not exist yet. This huge
+ number can be used to effectively disable the dialog.
+ </description>
+ </key>
+ <key name="app-picker-layout" type="aa{sv}">
+ <default><![CDATA[
+ [{
+ 'org.gnome.Geary.desktop': <{'position': <0>}>,
+ 'org.gnome.Contacts.desktop': <{'position': <1>}>,
+ 'org.gnome.Weather.desktop': <{'position': <2>}>,
+ 'org.gnome.clocks.desktop': <{'position': <3>}>,
+ 'org.gnome.Maps.desktop': <{'position': <4>}>,
+ 'org.gnome.Books.desktop': <{'position': <5>}>,
+ 'org.gnome.Photos.desktop': <{'position': <6>}>,
+ 'org.gnome.Totem.desktop': <{'position': <7>}>,
+ 'org.gnome.Calculator.desktop': <{'position': <8>}>,
+ 'org.gnome.gedit.desktop': <{'position': <9>}>,
+ 'simple-scan.desktop': <{'position': <10>}>,
+ 'org.gnome.Settings.desktop': <{'position': <11>}>,
+ 'gnome-system-monitor.desktop': <{'position': <12>}>,
+ 'org.gnome.Boxes.desktop': <{'position': <13>}>,
+ 'org.gnome.Terminal.desktop': <{'position': <14>}>,
+ 'Utilities': <{'position': <15>}>,
+ 'org.gnome.Characters.desktop': <{'position': <16>}>,
+ 'yelp.desktop': <{'position': <17>}>,
+ 'org.gnome.Screenshot.desktop': <{'position': <18>}>,
+ 'org.gnome.Cheese.desktop': <{'position': <19>}>,
+ 'org.gnome.font-viewer.desktop': <{'position': <20>}>
+ }]
+ ]]></default>
+ <summary>Layout of the app picker</summary>
+ <description>
+ Layout of the app picker. Each entry in the array is a page. Pages are
+ stored in the order they appear in GNOME Shell. Each page contains an
+ “application id” → 'data' pair. Currently, the following values are
+ stored as 'data':
+ • “position”: the position of the application icon in the page
+ </description>
+ </key>
+ <child name="keybindings" schema="org.gnome.shell.keybindings"/>
+ </schema>
+
+ <schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/"
+ gettext-domain="@GETTEXT_PACKAGE@">
+ <key name="open-application-menu" type="as">
+ <default>["&lt;Super&gt;F10"]</default>
+ <summary>Keybinding to open the application menu</summary>
+ <description>
+ Keybinding to open the application menu.
+ </description>
+ </key>
+ <key name="shift-overview-up" type="as">
+ <default>["&lt;Super&gt;&lt;Alt&gt;Up"]</default>
+ <summary>Keybinding to shift between overview states</summary>
+ <description>
+ Keybinding to shift between session, window picker and app grid
+ </description>
+ </key>
+ <key name="shift-overview-down" type="as">
+ <default>["&lt;Super&gt;&lt;Alt&gt;Down"]</default>
+ <summary>Keybinding to shift between overview states</summary>
+ <description>
+ Keybinding to shift between app grid, window picker and session
+ </description>
+ </key>
+ <key name="toggle-application-view" type="as">
+ <default>["&lt;Super&gt;a"]</default>
+ <summary>Keybinding to open the “Show Applications” view</summary>
+ <description>
+ Keybinding to open the “Show Applications” view of the Activities
+ Overview.
+ </description>
+ </key>
+ <key name="toggle-overview" type="as">
+ <default>["&lt;Super&gt;s"]</default>
+ <summary>Keybinding to open the overview</summary>
+ <description>
+ Keybinding to open the Activities Overview.
+ </description>
+ </key>
+ <key name="toggle-message-tray" type="as">
+ <default>["&lt;Super&gt;v","&lt;Super&gt;m"]</default>
+ <summary>Keybinding to toggle the visibility of the notification list</summary>
+ <description>
+ Keybinding to toggle the visibility of the notification list.
+ </description>
+ </key>
+ <key name="focus-active-notification" type="as">
+ <default>["&lt;Super&gt;n"]</default>
+ <summary>Keybinding to focus the active notification</summary>
+ <description>
+ Keybinding to focus the active notification.
+ </description>
+ </key>
+ <key name="switch-to-application-1" type="as">
+ <default>["&lt;Super&gt;1"]</default>
+ <summary>Switch to application 1</summary>
+ </key>
+ <key name="switch-to-application-2" type="as">
+ <default>["&lt;Super&gt;2"]</default>
+ <summary>Switch to application 2</summary>
+ </key>
+ <key name="switch-to-application-3" type="as">
+ <default>["&lt;Super&gt;3"]</default>
+ <summary>Switch to application 3</summary>
+ </key>
+ <key name="switch-to-application-4" type="as">
+ <default>["&lt;Super&gt;4"]</default>
+ <summary>Switch to application 4</summary>
+ </key>
+ <key name="switch-to-application-5" type="as">
+ <default>["&lt;Super&gt;5"]</default>
+ <summary>Switch to application 5</summary>
+ </key>
+ <key name="switch-to-application-6" type="as">
+ <default>["&lt;Super&gt;6"]</default>
+ <summary>Switch to application 6</summary>
+ </key>
+ <key name="switch-to-application-7" type="as">
+ <default>["&lt;Super&gt;7"]</default>
+ <summary>Switch to application 7</summary>
+ </key>
+ <key name="switch-to-application-8" type="as">
+ <default>["&lt;Super&gt;8"]</default>
+ <summary>Switch to application 8</summary>
+ </key>
+ <key name="switch-to-application-9" type="as">
+ <default>["&lt;Super&gt;9"]</default>
+ <summary>Switch to application 9</summary>
+ </key>
+ <key name="show-screenshot-ui" type="as">
+ <default>["Print"]</default>
+ <summary>Take a screenshot interactively</summary>
+ </key>
+ <key name="show-screen-recording-ui" type="as">
+ <default>["&lt;Ctrl&gt;&lt;Shift&gt;&lt;Alt&gt;R"]</default>
+ <summary>Record a screencast interactively</summary>
+ </key>
+ <key name="screenshot-window" type="as">
+ <default>["&lt;Alt&gt;Print"]</default>
+ <summary>Take a screenshot of a window</summary>
+ </key>
+ <key name="screenshot" type="as">
+ <default>["&lt;Shift&gt;Print"]</default>
+ <summary>Take a screenshot</summary>
+ </key>
+ </schema>
+
+ <schema id="org.gnome.shell.app-switcher"
+ path="/org/gnome/shell/app-switcher/"
+ gettext-domain="@GETTEXT_PACKAGE@">
+ <key type="b" name="current-workspace-only">
+ <default>false</default>
+ <summary>Limit switcher to current workspace.</summary>
+ <description>
+ If true, only applications that have windows on the current workspace are shown in the switcher.
+ Otherwise, all applications are included.
+ </description>
+ </key>
+ </schema>
+
+ <enum id="org.gnome.shell.window-switcher.AppIconMode">
+ <value value="1" nick="thumbnail-only"/>
+ <value value="2" nick="app-icon-only"/>
+ <value value="3" nick="both"/>
+ </enum>
+ <schema id="org.gnome.shell.window-switcher"
+ path="/org/gnome/shell/window-switcher/"
+ gettext-domain="@GETTEXT_PACKAGE@">
+ <key name="app-icon-mode" enum="org.gnome.shell.window-switcher.AppIconMode">
+ <default>'both'</default>
+ <summary>The application icon mode.</summary>
+ <description>
+ Configures how the windows are shown in the switcher. Valid possibilities
+ are “thumbnail-only” (shows a thumbnail of the window), “app-icon-only”
+ (shows only the application icon) or “both”.
+ </description>
+ </key>
+ <key type="b" name="current-workspace-only">
+ <default>true</default>
+ <summary>Limit switcher to current workspace.</summary>
+ <description>
+ If true, only windows from the current workspace are shown in the switcher.
+ Otherwise, all windows are included.
+ </description>
+ </key>
+ </schema>
+
+ <schema id="org.gnome.shell.world-clocks" path="/org/gnome/shell/world-clocks/"
+ gettext-domain="@GETTEXT_PACKAGE@">
+ <key name="locations" type="av">
+ <summary>Locations</summary>
+ <description>
+ The locations to show in world clocks
+ </description>
+ <default>[]</default>
+ </key>
+ </schema>
+
+ <schema id="org.gnome.shell.weather" path="/org/gnome/shell/weather/"
+ gettext-domain="@GETTEXT_PACKAGE@">
+ <key name="automatic-location" type="b">
+ <summary>Automatic location</summary>
+ <description>
+ Whether to fetch the current location or not
+ </description>
+ <default>false</default>
+ </key>
+
+ <key name="locations" type="av">
+ <summary>Location</summary>
+ <description>
+ The location for which to show a forecast
+ </description>
+ <default>[]</default>
+ </key>
+ </schema>
+
+ <!-- unused, change 00_org.gnome.shell.gschema.override instead -->
+ <schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"
+ gettext-domain="@GETTEXT_PACKAGE@">
+ <key name="attach-modal-dialogs" type="b">
+ <default>true</default>
+ <summary>Attach modal dialog to the parent window</summary>
+ <description>
+ This key overrides the key in org.gnome.mutter when running
+ GNOME Shell.
+ </description>
+ </key>
+
+ <key name="edge-tiling" type="b">
+ <default>true</default>
+ <summary>Enable edge tiling when dropping windows on screen edges</summary>
+ <description>
+ This key overrides the key in org.gnome.mutter when running GNOME Shell.
+ </description>
+ </key>
+
+ <key name="dynamic-workspaces" type="b">
+ <default>true</default>
+ <summary>Workspaces are managed dynamically</summary>
+ <description>
+ This key overrides the key in org.gnome.mutter when running GNOME Shell.
+ </description>
+ </key>
+
+ <key name="workspaces-only-on-primary" type="b">
+ <default>true</default>
+ <summary>Workspaces only on primary monitor</summary>
+ <description>
+ This key overrides the key in org.gnome.mutter when running GNOME Shell.
+ </description>
+ </key>
+
+ <key name="focus-change-on-pointer-rest" type="b">
+ <default>true</default>
+ <summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
+ <description>
+ This key overrides the key in org.gnome.mutter when running GNOME Shell.
+ </description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/data/osk-layouts/am.json b/data/osk-layouts/am.json
new file mode 100644
index 0000000..8316774
--- /dev/null
+++ b/data/osk-layouts/am.json
@@ -0,0 +1,1051 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "է"
+ ]
+ },
+ {
+ "strings": [
+ "թ"
+ ]
+ },
+ {
+ "strings": [
+ "փ"
+ ]
+ },
+ {
+ "strings": [
+ "ձ"
+ ]
+ },
+ {
+ "strings": [
+ "ջ"
+ ]
+ },
+ {
+ "strings": [
+ "ր"
+ ]
+ },
+ {
+ "strings": [
+ "չ"
+ ]
+ },
+ {
+ "strings": [
+ "ճ"
+ ]
+ },
+ {
+ "strings": [
+ "ժ"
+ ]
+ },
+ {
+ "strings": [
+ "ծ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ք"
+ ]
+ },
+ {
+ "strings": [
+ "ո"
+ ]
+ },
+ {
+ "strings": [
+ "ե",
+ "և"
+ ]
+ },
+ {
+ "strings": [
+ "ռ"
+ ]
+ },
+ {
+ "strings": [
+ "տ"
+ ]
+ },
+ {
+ "strings": [
+ "ը"
+ ]
+ },
+ {
+ "strings": [
+ "ւ"
+ ]
+ },
+ {
+ "strings": [
+ "ի"
+ ]
+ },
+ {
+ "strings": [
+ "օ"
+ ]
+ },
+ {
+ "strings": [
+ "պ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ա"
+ ]
+ },
+ {
+ "strings": [
+ "ս"
+ ]
+ },
+ {
+ "strings": [
+ "դ"
+ ]
+ },
+ {
+ "strings": [
+ "ֆ"
+ ]
+ },
+ {
+ "strings": [
+ "գ"
+ ]
+ },
+ {
+ "strings": [
+ "հ"
+ ]
+ },
+ {
+ "strings": [
+ "յ"
+ ]
+ },
+ {
+ "strings": [
+ "կ"
+ ]
+ },
+ {
+ "strings": [
+ "լ"
+ ]
+ },
+ {
+ "strings": [
+ "խ"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "զ"
+ ]
+ },
+ {
+ "strings": [
+ "ղ"
+ ]
+ },
+ {
+ "strings": [
+ "ց"
+ ]
+ },
+ {
+ "strings": [
+ "վ"
+ ]
+ },
+ {
+ "strings": [
+ "բ"
+ ]
+ },
+ {
+ "strings": [
+ "ն"
+ ]
+ },
+ {
+ "strings": [
+ "մ"
+ ]
+ },
+ {
+ "strings": [
+ "շ"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "strings": [
+ "։"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Է"
+ ]
+ },
+ {
+ "strings": [
+ "Թ"
+ ]
+ },
+ {
+ "strings": [
+ "Փ"
+ ]
+ },
+ {
+ "strings": [
+ "Ձ"
+ ]
+ },
+ {
+ "strings": [
+ "Ջ"
+ ]
+ },
+ {
+ "strings": [
+ "Ր"
+ ]
+ },
+ {
+ "strings": [
+ "Չ"
+ ]
+ },
+ {
+ "strings": [
+ "Ճ"
+ ]
+ },
+ {
+ "strings": [
+ "Ժ"
+ ]
+ },
+ {
+ "strings": [
+ "Ծ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Ք"
+ ]
+ },
+ {
+ "strings": [
+ "Ո"
+ ]
+ },
+ {
+ "strings": [
+ "Ե",
+ "ԵՒ"
+ ]
+ },
+ {
+ "strings": [
+ "Ռ"
+ ]
+ },
+ {
+ "strings": [
+ "Տ"
+ ]
+ },
+ {
+ "strings": [
+ "Ը"
+ ]
+ },
+ {
+ "strings": [
+ "Ւ"
+ ]
+ },
+ {
+ "strings": [
+ "Ի"
+ ]
+ },
+ {
+ "strings": [
+ "Օ"
+ ]
+ },
+ {
+ "strings": [
+ "Պ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Ա"
+ ]
+ },
+ {
+ "strings": [
+ "Ս"
+ ]
+ },
+ {
+ "strings": [
+ "Դ"
+ ]
+ },
+ {
+ "strings": [
+ "Ֆ"
+ ]
+ },
+ {
+ "strings": [
+ "Գ"
+ ]
+ },
+ {
+ "strings": [
+ "Հ"
+ ]
+ },
+ {
+ "strings": [
+ "Յ"
+ ]
+ },
+ {
+ "strings": [
+ "Կ"
+ ]
+ },
+ {
+ "strings": [
+ "Լ"
+ ]
+ },
+ {
+ "strings": [
+ "Խ"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Զ"
+ ]
+ },
+ {
+ "strings": [
+ "Ղ"
+ ]
+ },
+ {
+ "strings": [
+ "Ց"
+ ]
+ },
+ {
+ "strings": [
+ "Վ"
+ ]
+ },
+ {
+ "strings": [
+ "Բ"
+ ]
+ },
+ {
+ "strings": [
+ "Ն"
+ ]
+ },
+ {
+ "strings": [
+ "Մ"
+ ]
+ },
+ {
+ "strings": [
+ "Շ"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "strings": [
+ "։"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "՜",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "՞",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ "՝"
+ ]
+ },
+ {
+ "strings": [
+ "։",
+ "!",
+ "?",
+ ",",
+ ".",
+ "֊",
+ "՜",
+ "՝",
+ "՞",
+ ":",
+ ";",
+ "@",
+ "ՙ",
+ "՚",
+ "՛",
+ "՟"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ "՝"
+ ]
+ },
+ {
+ "strings": [
+ "։",
+ "!",
+ "?",
+ ",",
+ ".",
+ "֊",
+ "՜",
+ "՝",
+ "՞",
+ ":",
+ ";",
+ "@",
+ "ՙ",
+ "՚",
+ "՛",
+ "՟"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "hy",
+ "name": "Armenian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/ara.json b/data/osk-layouts/ara.json
new file mode 100644
index 0000000..10095ce
--- /dev/null
+++ b/data/osk-layouts/ara.json
@@ -0,0 +1,793 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ض"
+ ]
+ },
+ {
+ "strings": [
+ "ص"
+ ]
+ },
+ {
+ "strings": [
+ "ث"
+ ]
+ },
+ {
+ "strings": [
+ "ق",
+ "ڨ"
+ ]
+ },
+ {
+ "strings": [
+ "ف",
+ "ڤ",
+ "ڢ",
+ "ڥ"
+ ]
+ },
+ {
+ "strings": [
+ "غ"
+ ]
+ },
+ {
+ "strings": [
+ "ع"
+ ]
+ },
+ {
+ "strings": [
+ "ه",
+ "ه‍"
+ ]
+ },
+ {
+ "strings": [
+ "خ"
+ ]
+ },
+ {
+ "strings": [
+ "ح"
+ ]
+ },
+ {
+ "strings": [
+ "ج",
+ "چ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ش",
+ "ڜ"
+ ]
+ },
+ {
+ "strings": [
+ "س"
+ ]
+ },
+ {
+ "strings": [
+ "ي",
+ "ئ",
+ "ى"
+ ]
+ },
+ {
+ "strings": [
+ "ب",
+ "پ"
+ ]
+ },
+ {
+ "strings": [
+ "ل",
+ "لا",
+ "لأ",
+ "لإ",
+ "لآ"
+ ]
+ },
+ {
+ "strings": [
+ "ا",
+ "آ",
+ "ء",
+ "أ",
+ "إ",
+ "ٱ"
+ ]
+ },
+ {
+ "strings": [
+ "ت"
+ ]
+ },
+ {
+ "strings": [
+ "ن"
+ ]
+ },
+ {
+ "strings": [
+ "م"
+ ]
+ },
+ {
+ "strings": [
+ "ك",
+ "گ",
+ "ک"
+ ]
+ },
+ {
+ "strings": [
+ "ط"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ذ"
+ ]
+ },
+ {
+ "strings": [
+ "ء"
+ ]
+ },
+ {
+ "strings": [
+ "ؤ"
+ ]
+ },
+ {
+ "strings": [
+ "ر"
+ ]
+ },
+ {
+ "strings": [
+ "ى",
+ "ئ"
+ ]
+ },
+ {
+ "strings": [
+ "ة"
+ ]
+ },
+ {
+ "strings": [
+ "و"
+ ]
+ },
+ {
+ "strings": [
+ "ز",
+ "ژ"
+ ]
+ },
+ {
+ "strings": [
+ "ظ"
+ ]
+ },
+ {
+ "strings": [
+ "د"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "،"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "\"",
+ "'",
+ "#",
+ "-",
+ ":",
+ "!",
+ "،",
+ "؟",
+ "@",
+ "&",
+ "%",
+ "+",
+ "؛",
+ "/",
+ ")",
+ "("
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "١",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "٢",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "٣",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "٤",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "٥",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "٦"
+ ]
+ },
+ {
+ "strings": [
+ "٧",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "٨"
+ ]
+ },
+ {
+ "strings": [
+ "٩"
+ ]
+ },
+ {
+ "strings": [
+ "٠",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "٪",
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "﴿",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ "﴾",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "★",
+ "٭"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ "؛",
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "؟",
+ "?"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ "،",
+ "؟",
+ "؛",
+ "!",
+ ":",
+ "-",
+ "/",
+ "'",
+ "\""
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "ٕ",
+ "ٔ",
+ "ْ",
+ "ٍ",
+ "ٌ",
+ "ً",
+ "ّ",
+ "ٖ",
+ "ٰ",
+ "ٓ",
+ "ِ",
+ "ُ",
+ "َ",
+ "ـ"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ "،",
+ "؟",
+ "؛",
+ "!",
+ ":",
+ "-",
+ "/",
+ "'",
+ "\""
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "ٕ",
+ "ٔ",
+ "ْ",
+ "ٍ",
+ "ٌ",
+ "ً",
+ "ّ",
+ "ٖ",
+ "ٰ",
+ "ٓ",
+ "ِ",
+ "ُ",
+ "َ",
+ "ـ"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ar",
+ "name": "Arabic"
+} \ No newline at end of file
diff --git a/data/osk-layouts/at.json b/data/osk-layouts/at.json
new file mode 100644
index 0000000..511dc90
--- /dev/null
+++ b/data/osk-layouts/at.json
@@ -0,0 +1,978 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "ė"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü",
+ "û",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ö",
+ "ô",
+ "ò",
+ "ó",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ä",
+ "â",
+ "à",
+ "á",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "Ė"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ä",
+ "Â",
+ "À",
+ "Á",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "de-AT",
+ "name": "Austrian German"
+}
diff --git a/data/osk-layouts/be.json b/data/osk-layouts/be.json
new file mode 100644
index 0000000..6e4f75d
--- /dev/null
+++ b/data/osk-layouts/be.json
@@ -0,0 +1,1021 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "ä",
+ "â",
+ "à",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ë",
+ "ê",
+ "è",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ÿ",
+ "ij"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "û",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï",
+ "ì",
+ "î",
+ "į",
+ "ī",
+ "ij"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ö",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Ä",
+ "Â",
+ "À",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ë",
+ "Ê",
+ "È",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ÿ",
+ "IJ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï",
+ "Ì",
+ "Î",
+ "Į",
+ "Ī",
+ "IJ"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "ẞ"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "nl-BE",
+ "name": "Dutch (Belgium)"
+}
diff --git a/data/osk-layouts/bg.json b/data/osk-layouts/bg.json
new file mode 100644
index 0000000..fb7385d
--- /dev/null
+++ b/data/osk-layouts/bg.json
@@ -0,0 +1,955 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "я"
+ ]
+ },
+ {
+ "strings": [
+ "в"
+ ]
+ },
+ {
+ "strings": [
+ "е"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "ъ"
+ ]
+ },
+ {
+ "strings": [
+ "у"
+ ]
+ },
+ {
+ "strings": [
+ "и",
+ "ѝ"
+ ]
+ },
+ {
+ "strings": [
+ "о"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "г"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "strings": [
+ "й"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "ш"
+ ]
+ },
+ {
+ "strings": [
+ "щ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "ь"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "н"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "ю"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Я"
+ ]
+ },
+ {
+ "strings": [
+ "В"
+ ]
+ },
+ {
+ "strings": [
+ "Е"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "Ъ"
+ ]
+ },
+ {
+ "strings": [
+ "У"
+ ]
+ },
+ {
+ "strings": [
+ "И",
+ "Ѝ"
+ ]
+ },
+ {
+ "strings": [
+ "О"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "Г"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "strings": [
+ "Й"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Ш"
+ ]
+ },
+ {
+ "strings": [
+ "Щ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "Ь"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Н"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "Ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "bg",
+ "name": "Bulgarian"
+}
diff --git a/data/osk-layouts/by.json b/data/osk-layouts/by.json
new file mode 100644
index 0000000..25543eb
--- /dev/null
+++ b/data/osk-layouts/by.json
@@ -0,0 +1,965 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "й"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "у"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "strings": [
+ "е",
+ "ё"
+ ]
+ },
+ {
+ "strings": [
+ "н"
+ ]
+ },
+ {
+ "strings": [
+ "г"
+ ]
+ },
+ {
+ "strings": [
+ "ш"
+ ]
+ },
+ {
+ "strings": [
+ "ў"
+ ]
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "ы"
+ ]
+ },
+ {
+ "strings": [
+ "в"
+ ]
+ },
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "о"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "strings": [
+ "э"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "я"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "і"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "ь",
+ "ъ"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Й"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "У"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "strings": [
+ "Е",
+ "Ё"
+ ]
+ },
+ {
+ "strings": [
+ "Н"
+ ]
+ },
+ {
+ "strings": [
+ "Г"
+ ]
+ },
+ {
+ "strings": [
+ "Ш"
+ ]
+ },
+ {
+ "strings": [
+ "Ў"
+ ]
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "Ы"
+ ]
+ },
+ {
+ "strings": [
+ "В"
+ ]
+ },
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "О"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "strings": [
+ "Э"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Я"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "І"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "Ь",
+ "Ъ"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "be",
+ "name": "Belarusian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/ca.json b/data/osk-layouts/ca.json
new file mode 100644
index 0000000..230a6ba
--- /dev/null
+++ b/data/osk-layouts/ca.json
@@ -0,0 +1,1007 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "%",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "%",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ù",
+ "û",
+ "%",
+ "ü",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "î",
+ "%",
+ "ï",
+ "ì",
+ "í",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ô",
+ "œ",
+ "%",
+ "ö",
+ "ò",
+ "ó",
+ "õ",
+ "ø",
+ "ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "â",
+ "%",
+ "æ",
+ "á",
+ "ä",
+ "ã",
+ "å",
+ "ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "%",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "%",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ù",
+ "Û",
+ "%",
+ "Ü",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Î",
+ "%",
+ "Ï",
+ "Ì",
+ "Í",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ô",
+ "Œ",
+ "%",
+ "Ö",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Ø",
+ "Ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Â",
+ "%",
+ "Æ",
+ "Á",
+ "Ä",
+ "Ã",
+ "Å",
+ "Ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "fr-CA",
+ "name": "French Canada"
+}
diff --git a/data/osk-layouts/ch+fr.json b/data/osk-layouts/ch+fr.json
new file mode 100644
index 0000000..7735c2e
--- /dev/null
+++ b/data/osk-layouts/ch+fr.json
@@ -0,0 +1,978 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "ė"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü",
+ "û",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ö",
+ "ô",
+ "ò",
+ "ó",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ä",
+ "â",
+ "à",
+ "á",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "Ė"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ä",
+ "Â",
+ "À",
+ "Á",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "fr-CH",
+ "name": "Swiss French"
+}
diff --git a/data/osk-layouts/ch.json b/data/osk-layouts/ch.json
new file mode 100644
index 0000000..aa062b0
--- /dev/null
+++ b/data/osk-layouts/ch.json
@@ -0,0 +1,978 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "ė"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü",
+ "û",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ö",
+ "ô",
+ "ò",
+ "ó",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ä",
+ "â",
+ "à",
+ "á",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "Ė"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ä",
+ "Â",
+ "À",
+ "Á",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "de-CH",
+ "name": "Swiss German"
+}
diff --git a/data/osk-layouts/cz.json b/data/osk-layouts/cz.json
new file mode 100644
index 0000000..7a3d09a
--- /dev/null
+++ b/data/osk-layouts/cz.json
@@ -0,0 +1,1021 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ě",
+ "è",
+ "ê",
+ "ë",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r",
+ "ř"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ť"
+ ]
+ },
+ {
+ "strings": [
+ "z",
+ "ž",
+ "ź",
+ "ż"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ů",
+ "û",
+ "ü",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "î",
+ "ï",
+ "ì",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ö",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "à",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š",
+ "ß",
+ "ś"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ď"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "č",
+ "ç",
+ "ć"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ň",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ě",
+ "È",
+ "Ê",
+ "Ë",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R",
+ "Ř"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ť"
+ ]
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž",
+ "Ź",
+ "Ż"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ů",
+ "Û",
+ "Ü",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Î",
+ "Ï",
+ "Ì",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "À",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š",
+ "SS",
+ "Ś"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ď"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Č",
+ "Ç",
+ "Ć"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ň",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "cs",
+ "name": "Czech"
+}
diff --git a/data/osk-layouts/de.json b/data/osk-layouts/de.json
new file mode 100644
index 0000000..d36e399
--- /dev/null
+++ b/data/osk-layouts/de.json
@@ -0,0 +1,978 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "ė"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü",
+ "û",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ö",
+ "ô",
+ "ò",
+ "ó",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ä",
+ "â",
+ "à",
+ "á",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "Ė"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ä",
+ "Â",
+ "À",
+ "Á",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "de",
+ "name": "German"
+}
diff --git a/data/osk-layouts/dk.json b/data/osk-layouts/dk.json
new file mode 100644
index 0000000..3b01ac8
--- /dev/null
+++ b/data/osk-layouts/dk.json
@@ -0,0 +1,1012 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ë"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "û",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "strings": [
+ "å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "ä",
+ "à",
+ "â",
+ "ã",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ð"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ł"
+ ]
+ },
+ {
+ "strings": [
+ "æ",
+ "ä"
+ ]
+ },
+ {
+ "strings": [
+ "ø",
+ "ö"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ë"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "strings": [
+ "Å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Ä",
+ "À",
+ "Â",
+ "Ã",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ð"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ł"
+ ]
+ },
+ {
+ "strings": [
+ "Æ",
+ "Ä"
+ ]
+ },
+ {
+ "strings": [
+ "Ø",
+ "Ö"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "da",
+ "name": "Danish"
+}
diff --git a/data/osk-layouts/ee.json b/data/osk-layouts/ee.json
new file mode 100644
index 0000000..f858a62
--- /dev/null
+++ b/data/osk-layouts/ee.json
@@ -0,0 +1,1088 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "ē",
+ "è",
+ "ė",
+ "é",
+ "ê",
+ "ë",
+ "ę",
+ "ě"
+ ]
+ },
+ {
+ "strings": [
+ "r",
+ "ŗ",
+ "ř",
+ "ŕ"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ţ",
+ "ť"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü",
+ "ū",
+ "ų",
+ "ù",
+ "ú",
+ "û",
+ "ů",
+ "ű"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "ī",
+ "ì",
+ "į",
+ "í",
+ "î",
+ "ï",
+ "ı"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ö",
+ "õ",
+ "ò",
+ "ó",
+ "ô",
+ "œ",
+ "ő",
+ "ø"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "strings": [
+ "ü"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ä",
+ "ā",
+ "à",
+ "á",
+ "â",
+ "ã",
+ "å",
+ "æ",
+ "ą"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š",
+ "ß",
+ "ś",
+ "ş"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ď"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g",
+ "ģ",
+ "ğ"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k",
+ "ķ"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ļ",
+ "ł",
+ "ĺ",
+ "ľ"
+ ]
+ },
+ {
+ "strings": [
+ "ö",
+ "õ"
+ ]
+ },
+ {
+ "strings": [
+ "ä"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "z",
+ "ž",
+ "ż",
+ "ź"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "č",
+ "ç",
+ "ć"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ņ",
+ "ñ",
+ "ń",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "Ē",
+ "È",
+ "Ė",
+ "É",
+ "Ê",
+ "Ë",
+ "Ę",
+ "Ě"
+ ]
+ },
+ {
+ "strings": [
+ "R",
+ "Ŗ",
+ "Ř",
+ "Ŕ"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ţ",
+ "Ť"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü",
+ "Ū",
+ "Ų",
+ "Ù",
+ "Ú",
+ "Û",
+ "Ů",
+ "Ű"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Ī",
+ "Ì",
+ "Į",
+ "Í",
+ "Î",
+ "Ï",
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ö",
+ "Õ",
+ "Ò",
+ "Ó",
+ "Ô",
+ "Œ",
+ "Ő",
+ "Ø"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "strings": [
+ "Ü"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ä",
+ "Ā",
+ "À",
+ "Á",
+ "Â",
+ "Ã",
+ "Å",
+ "Æ",
+ "Ą"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š",
+ "SS",
+ "Ś",
+ "Ş"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ď"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G",
+ "Ģ",
+ "Ğ"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K",
+ "Ķ"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ļ",
+ "Ł",
+ "Ĺ",
+ "Ľ"
+ ]
+ },
+ {
+ "strings": [
+ "Ö",
+ "Õ"
+ ]
+ },
+ {
+ "strings": [
+ "Ä"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž",
+ "Ż",
+ "Ź"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Č",
+ "Ç",
+ "Ć"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ņ",
+ "Ñ",
+ "Ń",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "et",
+ "name": "Estonian"
+}
diff --git a/data/osk-layouts/epo.json b/data/osk-layouts/epo.json
new file mode 100644
index 0000000..a6d262c
--- /dev/null
+++ b/data/osk-layouts/epo.json
@@ -0,0 +1,1123 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ŝ",
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "ĝ",
+ "w",
+ "ŵ"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ě",
+ "è",
+ "ê",
+ "ë",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r",
+ "ř",
+ "ŕ",
+ "ŗ"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ť",
+ "ț",
+ "ţ",
+ "ŧ"
+ ]
+ },
+ {
+ "strings": [
+ "ŭ",
+ "y",
+ "ý",
+ "ŷ",
+ "ÿ",
+ "þ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ů",
+ "û",
+ "ü",
+ "ù",
+ "ū",
+ "ũ",
+ "ű",
+ "ų",
+ "µ"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "î",
+ "ï",
+ "ĩ",
+ "ì",
+ "į",
+ "ī",
+ "ı",
+ "ij"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ö",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō",
+ "ő",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "à",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā",
+ "ă",
+ "ą",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß",
+ "š",
+ "ś",
+ "ș",
+ "ş"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ð",
+ "ď",
+ "đ"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g",
+ "ğ",
+ "ġ",
+ "ģ"
+ ]
+ },
+ {
+ "strings": [
+ "h",
+ "ĥ",
+ "ħ"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k",
+ "ķ",
+ "ĸ"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ĺ",
+ "ļ",
+ "ľ",
+ "ŀ",
+ "ł"
+ ]
+ },
+ {
+ "strings": [
+ "ĵ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z",
+ "ź",
+ "ż",
+ "ž"
+ ]
+ },
+ {
+ "strings": [
+ "ĉ",
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ć",
+ "č",
+ "ç",
+ "ċ"
+ ]
+ },
+ {
+ "strings": [
+ "v",
+ "w",
+ "ŵ"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń",
+ "ņ",
+ "ň",
+ "ʼn",
+ "ŋ"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Ŝ",
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "Ĝ",
+ "W",
+ "Ŵ"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ě",
+ "È",
+ "Ê",
+ "Ë",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R",
+ "Ř",
+ "Ŕ",
+ "Ŗ"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ť",
+ "Ț",
+ "Ţ",
+ "Ŧ"
+ ]
+ },
+ {
+ "strings": [
+ "Ŭ",
+ "Y",
+ "Ý",
+ "Ŷ",
+ "Ÿ",
+ "Þ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ů",
+ "Û",
+ "Ü",
+ "Ù",
+ "Ū",
+ "Ũ",
+ "Ű",
+ "Ų",
+ "Μ"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Î",
+ "Ï",
+ "Ĩ",
+ "Ì",
+ "Į",
+ "Ī",
+ "I",
+ "IJ"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō",
+ "Ő",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "À",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā",
+ "Ă",
+ "Ą",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS",
+ "Š",
+ "Ś",
+ "Ș",
+ "Ş"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ð",
+ "Ď",
+ "Đ"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G",
+ "Ğ",
+ "Ġ",
+ "Ģ"
+ ]
+ },
+ {
+ "strings": [
+ "H",
+ "Ĥ",
+ "Ħ"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K",
+ "Ķ",
+ "ĸ"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ĺ",
+ "Ļ",
+ "Ľ",
+ "Ŀ",
+ "Ł"
+ ]
+ },
+ {
+ "strings": [
+ "Ĵ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z",
+ "Ź",
+ "Ż",
+ "Ž"
+ ]
+ },
+ {
+ "strings": [
+ "Ĉ",
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ć",
+ "Č",
+ "Ç",
+ "Ċ"
+ ]
+ },
+ {
+ "strings": [
+ "V",
+ "W",
+ "Ŵ"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń",
+ "Ņ",
+ "Ň",
+ "ʼN",
+ "Ŋ"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "eo",
+ "name": "Esperanto"
+}
diff --git a/data/osk-layouts/es+cat.json b/data/osk-layouts/es+cat.json
new file mode 100644
index 0000000..fab8d4e
--- /dev/null
+++ b/data/osk-layouts/es+cat.json
@@ -0,0 +1,1014 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "è",
+ "é",
+ "ë",
+ "ê",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "ù",
+ "û",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï",
+ "ì",
+ "î",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ò",
+ "ó",
+ "ö",
+ "ô",
+ "õ",
+ "ø",
+ "œ",
+ "ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "á",
+ "ä",
+ "â",
+ "ã",
+ "å",
+ "ą",
+ "æ",
+ "ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "l·l",
+ "ł"
+ ]
+ },
+ {
+ "strings": [
+ "ç"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "·",
+ "!",
+ ",",
+ "?",
+ ":",
+ ";",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "È",
+ "É",
+ "Ë",
+ "Ê",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Ù",
+ "Û",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï",
+ "Ì",
+ "Î",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ò",
+ "Ó",
+ "Ö",
+ "Ô",
+ "Õ",
+ "Ø",
+ "Œ",
+ "Ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Á",
+ "Ä",
+ "Â",
+ "Ã",
+ "Å",
+ "Ą",
+ "Æ",
+ "Ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "L·L",
+ "Ł"
+ ]
+ },
+ {
+ "strings": [
+ "Ç"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "·",
+ "!",
+ ",",
+ "?",
+ ":",
+ ";",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?",
+ "·"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?",
+ "·"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ca",
+ "name": "Catalan"
+}
diff --git a/data/osk-layouts/es.json b/data/osk-layouts/es.json
new file mode 100644
index 0000000..652c07f
--- /dev/null
+++ b/data/osk-layouts/es.json
@@ -0,0 +1,1016 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ë",
+ "ê",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "ù",
+ "û",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï",
+ "ì",
+ "î",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ò",
+ "ö",
+ "ô",
+ "õ",
+ "ø",
+ "œ",
+ "ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "à",
+ "ä",
+ "â",
+ "ã",
+ "å",
+ "ą",
+ "æ",
+ "ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "strings": [
+ "ñ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ ";",
+ "!",
+ ",",
+ "?",
+ ":",
+ "¡",
+ "@",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ë",
+ "Ê",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Ù",
+ "Û",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï",
+ "Ì",
+ "Î",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ò",
+ "Ö",
+ "Ô",
+ "Õ",
+ "Ø",
+ "Œ",
+ "Ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "À",
+ "Ä",
+ "Â",
+ "Ã",
+ "Å",
+ "Ą",
+ "Æ",
+ "Ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "strings": [
+ "Ñ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ ";",
+ "!",
+ ",",
+ "?",
+ ":",
+ "¡",
+ "@",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ",",
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ",",
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "es",
+ "name": "Spanish"
+}
diff --git a/data/osk-layouts/fi.json b/data/osk-layouts/fi.json
new file mode 100644
index 0000000..b7d0d81
--- /dev/null
+++ b/data/osk-layouts/fi.json
@@ -0,0 +1,992 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ø",
+ "ô",
+ "ò",
+ "ó",
+ "õ",
+ "œ",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "strings": [
+ "å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "æ",
+ "à",
+ "á",
+ "â",
+ "ã",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š",
+ "ß",
+ "ś"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "strings": [
+ "ö",
+ "ø"
+ ]
+ },
+ {
+ "strings": [
+ "ä",
+ "æ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "z",
+ "ž",
+ "ź",
+ "ż"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ø",
+ "Ô",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Œ",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "strings": [
+ "Å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Æ",
+ "À",
+ "Á",
+ "Â",
+ "Ã",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š",
+ "SS",
+ "Ś"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "strings": [
+ "Ö",
+ "Ø"
+ ]
+ },
+ {
+ "strings": [
+ "Ä",
+ "Æ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž",
+ "Ź",
+ "Ż"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "fi",
+ "name": "Finnish"
+}
diff --git a/data/osk-layouts/fr.json b/data/osk-layouts/fr.json
new file mode 100644
index 0000000..bf1ba86
--- /dev/null
+++ b/data/osk-layouts/fr.json
@@ -0,0 +1,1026 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "â",
+ "%",
+ "æ",
+ "á",
+ "ä",
+ "ã",
+ "å",
+ "ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "%",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "%",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ù",
+ "û",
+ "%",
+ "ü",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "î",
+ "%",
+ "ï",
+ "ì",
+ "í",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ô",
+ "œ",
+ "%",
+ "ö",
+ "ò",
+ "ó",
+ "õ",
+ "ø",
+ "ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Â",
+ "%",
+ "Æ",
+ "Á",
+ "Ä",
+ "Ã",
+ "Å",
+ "Ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "%",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "%",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ù",
+ "Û",
+ "%",
+ "Ü",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Î",
+ "%",
+ "Ï",
+ "Ì",
+ "Í",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ô",
+ "Œ",
+ "%",
+ "Ö",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Ø",
+ "Ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "fr",
+ "name": "French"
+}
diff --git a/data/osk-layouts/ge.json b/data/osk-layouts/ge.json
new file mode 100644
index 0000000..7bc88d5
--- /dev/null
+++ b/data/osk-layouts/ge.json
@@ -0,0 +1,705 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ქ"
+ ]
+ },
+ {
+ "strings": [
+ "წ"
+ ]
+ },
+ {
+ "strings": [
+ "ე",
+ "ჱ"
+ ]
+ },
+ {
+ "strings": [
+ "რ"
+ ]
+ },
+ {
+ "strings": [
+ "ტ"
+ ]
+ },
+ {
+ "strings": [
+ "ყ",
+ "ჸ"
+ ]
+ },
+ {
+ "strings": [
+ "უ"
+ ]
+ },
+ {
+ "strings": [
+ "ი",
+ "ჲ"
+ ]
+ },
+ {
+ "strings": [
+ "ო"
+ ]
+ },
+ {
+ "strings": [
+ "პ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ა",
+ "ჺ"
+ ]
+ },
+ {
+ "strings": [
+ "ს"
+ ]
+ },
+ {
+ "strings": [
+ "დ"
+ ]
+ },
+ {
+ "strings": [
+ "ფ",
+ "ჶ"
+ ]
+ },
+ {
+ "strings": [
+ "გ",
+ "ჹ"
+ ]
+ },
+ {
+ "strings": [
+ "ჰ",
+ "ჵ"
+ ]
+ },
+ {
+ "strings": [
+ "ჯ",
+ "ჷ"
+ ]
+ },
+ {
+ "strings": [
+ "კ"
+ ]
+ },
+ {
+ "strings": [
+ "ლ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ზ"
+ ]
+ },
+ {
+ "strings": [
+ "ხ",
+ "ჴ"
+ ]
+ },
+ {
+ "strings": [
+ "ც"
+ ]
+ },
+ {
+ "strings": [
+ "ვ",
+ "ჳ"
+ ]
+ },
+ {
+ "strings": [
+ "ბ"
+ ]
+ },
+ {
+ "strings": [
+ "ნ",
+ "ჼ"
+ ]
+ },
+ {
+ "strings": [
+ "მ"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ka",
+ "name": "Georgian"
+}
diff --git a/data/osk-layouts/gr.json b/data/osk-layouts/gr.json
new file mode 100644
index 0000000..a60a186
--- /dev/null
+++ b/data/osk-layouts/gr.json
@@ -0,0 +1,940 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ ";",
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ "ς"
+ ]
+ },
+ {
+ "strings": [
+ "ε",
+ "έ"
+ ]
+ },
+ {
+ "strings": [
+ "ρ"
+ ]
+ },
+ {
+ "strings": [
+ "τ"
+ ]
+ },
+ {
+ "strings": [
+ "υ",
+ "ύ",
+ "ϋ",
+ "ΰ"
+ ]
+ },
+ {
+ "strings": [
+ "θ"
+ ]
+ },
+ {
+ "strings": [
+ "ι",
+ "ί",
+ "ϊ",
+ "ΐ"
+ ]
+ },
+ {
+ "strings": [
+ "ο",
+ "ό"
+ ]
+ },
+ {
+ "strings": [
+ "π"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "α",
+ "ά"
+ ]
+ },
+ {
+ "strings": [
+ "σ"
+ ]
+ },
+ {
+ "strings": [
+ "δ"
+ ]
+ },
+ {
+ "strings": [
+ "φ"
+ ]
+ },
+ {
+ "strings": [
+ "γ"
+ ]
+ },
+ {
+ "strings": [
+ "η",
+ "ή"
+ ]
+ },
+ {
+ "strings": [
+ "ξ"
+ ]
+ },
+ {
+ "strings": [
+ "κ"
+ ]
+ },
+ {
+ "strings": [
+ "λ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "ζ"
+ ]
+ },
+ {
+ "strings": [
+ "χ"
+ ]
+ },
+ {
+ "strings": [
+ "ψ"
+ ]
+ },
+ {
+ "strings": [
+ "ω",
+ "ώ"
+ ]
+ },
+ {
+ "strings": [
+ "β"
+ ]
+ },
+ {
+ "strings": [
+ "ν"
+ ]
+ },
+ {
+ "strings": [
+ "μ"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ ";",
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ "Σ"
+ ]
+ },
+ {
+ "strings": [
+ "Ε",
+ "Έ"
+ ]
+ },
+ {
+ "strings": [
+ "Ρ"
+ ]
+ },
+ {
+ "strings": [
+ "Τ"
+ ]
+ },
+ {
+ "strings": [
+ "Υ",
+ "Ύ",
+ "Ϋ",
+ "Ϋ́"
+ ]
+ },
+ {
+ "strings": [
+ "Θ"
+ ]
+ },
+ {
+ "strings": [
+ "Ι",
+ "Ί",
+ "Ϊ",
+ "Ϊ́"
+ ]
+ },
+ {
+ "strings": [
+ "Ο",
+ "Ό"
+ ]
+ },
+ {
+ "strings": [
+ "Π"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Α",
+ "Ά"
+ ]
+ },
+ {
+ "strings": [
+ "Σ"
+ ]
+ },
+ {
+ "strings": [
+ "Δ"
+ ]
+ },
+ {
+ "strings": [
+ "Φ"
+ ]
+ },
+ {
+ "strings": [
+ "Γ"
+ ]
+ },
+ {
+ "strings": [
+ "Η",
+ "Ή"
+ ]
+ },
+ {
+ "strings": [
+ "Ξ"
+ ]
+ },
+ {
+ "strings": [
+ "Κ"
+ ]
+ },
+ {
+ "strings": [
+ "Λ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Ζ"
+ ]
+ },
+ {
+ "strings": [
+ "Χ"
+ ]
+ },
+ {
+ "strings": [
+ "Ψ"
+ ]
+ },
+ {
+ "strings": [
+ "Ω",
+ "Ώ"
+ ]
+ },
+ {
+ "strings": [
+ "Β"
+ ]
+ },
+ {
+ "strings": [
+ "Ν"
+ ]
+ },
+ {
+ "strings": [
+ "Μ"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "el",
+ "name": "Greek"
+}
diff --git a/data/osk-layouts/hr.json b/data/osk-layouts/hr.json
new file mode 100644
index 0000000..444014b
--- /dev/null
+++ b/data/osk-layouts/hr.json
@@ -0,0 +1,939 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "z",
+ "ž",
+ "ź",
+ "ż"
+ ]
+ },
+ {
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š",
+ "ś",
+ "ß"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "đ"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "č",
+ "ć",
+ "ç"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž",
+ "Ź",
+ "Ż"
+ ]
+ },
+ {
+ "strings": [
+ "U"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š",
+ "Ś",
+ "SS"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Đ"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Č",
+ "Ć",
+ "Ç"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "hr",
+ "name": "Croatian"
+}
diff --git a/data/osk-layouts/hu.json b/data/osk-layouts/hu.json
new file mode 100644
index 0000000..f45f31c
--- /dev/null
+++ b/data/osk-layouts/hu.json
@@ -0,0 +1,987 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "ű",
+ "û",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "î",
+ "ï",
+ "ì",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ö",
+ "ő",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "à",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Ű",
+ "Û",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Î",
+ "Ï",
+ "Ì",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ö",
+ "Ő",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "À",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "hu",
+ "name": "Hungarian"
+}
diff --git a/data/osk-layouts/id.json b/data/osk-layouts/id.json
new file mode 100644
index 0000000..6d57095
--- /dev/null
+++ b/data/osk-layouts/id.json
@@ -0,0 +1,915 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "id",
+ "name": "Indonesian"
+}
diff --git a/data/osk-layouts/il.json b/data/osk-layouts/il.json
new file mode 100644
index 0000000..d797b74
--- /dev/null
+++ b/data/osk-layouts/il.json
@@ -0,0 +1,718 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "'",
+ "\""
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "ק"
+ ]
+ },
+ {
+ "strings": [
+ "ר"
+ ]
+ },
+ {
+ "strings": [
+ "א"
+ ]
+ },
+ {
+ "strings": [
+ "ט"
+ ]
+ },
+ {
+ "strings": [
+ "ו"
+ ]
+ },
+ {
+ "strings": [
+ "ן"
+ ]
+ },
+ {
+ "strings": [
+ "ם"
+ ]
+ },
+ {
+ "strings": [
+ "פ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ש"
+ ]
+ },
+ {
+ "strings": [
+ "ד"
+ ]
+ },
+ {
+ "strings": [
+ "ג",
+ "ג׳"
+ ]
+ },
+ {
+ "strings": [
+ "כ"
+ ]
+ },
+ {
+ "strings": [
+ "ע"
+ ]
+ },
+ {
+ "strings": [
+ "י",
+ "ײַ"
+ ]
+ },
+ {
+ "strings": [
+ "ח",
+ "ח׳"
+ ]
+ },
+ {
+ "strings": [
+ "ל"
+ ]
+ },
+ {
+ "strings": [
+ "ך"
+ ]
+ },
+ {
+ "strings": [
+ "ף"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ז",
+ "ז׳"
+ ]
+ },
+ {
+ "strings": [
+ "ס"
+ ]
+ },
+ {
+ "strings": [
+ "ב"
+ ]
+ },
+ {
+ "strings": [
+ "ה"
+ ]
+ },
+ {
+ "strings": [
+ "נ"
+ ]
+ },
+ {
+ "strings": [
+ "מ"
+ ]
+ },
+ {
+ "strings": [
+ "צ",
+ "צ׳"
+ ]
+ },
+ {
+ "strings": [
+ "ת",
+ "ת׳"
+ ]
+ },
+ {
+ "strings": [
+ "ץ",
+ "ץ׳"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "₪",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±",
+ "﬩"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ",",
+ "!"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ",",
+ "!"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "he",
+ "name": "Hebrew"
+} \ No newline at end of file
diff --git a/data/osk-layouts/in+bolnagri.json b/data/osk-layouts/in+bolnagri.json
new file mode 100644
index 0000000..e280633
--- /dev/null
+++ b/data/osk-layouts/in+bolnagri.json
@@ -0,0 +1,745 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "◌ौ"
+ ]
+ },
+ {
+ "strings": [
+ "◌ै"
+ ]
+ },
+ {
+ "strings": [
+ "◌ा"
+ ]
+ },
+ {
+ "strings": [
+ "◌ी"
+ ]
+ },
+ {
+ "strings": [
+ "◌ू"
+ ]
+ },
+ {
+ "strings": [
+ "ब",
+ "ब॒",
+ "%"
+ ]
+ },
+ {
+ "strings": [
+ "ह"
+ ]
+ },
+ {
+ "strings": [
+ "ग",
+ "ज्ञ",
+ "ग़",
+ "ग॒",
+ "%"
+ ]
+ },
+ {
+ "strings": [
+ "द"
+ ]
+ },
+ {
+ "strings": [
+ "ज",
+ "ज॒",
+ "ज्ञ",
+ "ज़",
+ "%"
+ ]
+ },
+ {
+ "strings": [
+ "ड",
+ "ड॒",
+ "ड़"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "◌ो"
+ ]
+ },
+ {
+ "strings": [
+ "◌े"
+ ]
+ },
+ {
+ "strings": [
+ "◌्"
+ ]
+ },
+ {
+ "strings": [
+ "◌ि"
+ ]
+ },
+ {
+ "strings": [
+ "◌ु"
+ ]
+ },
+ {
+ "strings": [
+ "प"
+ ]
+ },
+ {
+ "strings": [
+ "र",
+ "ऋ",
+ "ऱ",
+ "ॠ"
+ ]
+ },
+ {
+ "strings": [
+ "क",
+ "क़"
+ ]
+ },
+ {
+ "strings": [
+ "त",
+ "त्र"
+ ]
+ },
+ {
+ "strings": [
+ "च"
+ ]
+ },
+ {
+ "strings": [
+ "ट"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "◌ॉ"
+ ]
+ },
+ {
+ "strings": [
+ "◌ं"
+ ]
+ },
+ {
+ "strings": [
+ "म",
+ "ॐ"
+ ]
+ },
+ {
+ "strings": [
+ "न",
+ "ञ",
+ "ङ",
+ "ऩ"
+ ]
+ },
+ {
+ "strings": [
+ "व"
+ ]
+ },
+ {
+ "strings": [
+ "ल",
+ "ऌ",
+ "ॡ"
+ ]
+ },
+ {
+ "strings": [
+ "स"
+ ]
+ },
+ {
+ "strings": [
+ "य",
+ "य़"
+ ]
+ },
+ {
+ "strings": [
+ "◌़"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "१",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "२",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "३",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "४",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "५",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "६"
+ ]
+ },
+ {
+ "strings": [
+ "७",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "८"
+ ]
+ },
+ {
+ "strings": [
+ "९"
+ ]
+ },
+ {
+ "strings": [
+ "०",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "₹",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "hi",
+ "name": "Hindi"
+}
diff --git a/data/osk-layouts/in+mal.json b/data/osk-layouts/in+mal.json
new file mode 100644
index 0000000..49dcd6b
--- /dev/null
+++ b/data/osk-layouts/in+mal.json
@@ -0,0 +1,986 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ൌ"
+ ]
+ },
+ {
+ "strings": [
+ "ൈ"
+ ]
+ },
+ {
+ "strings": [
+ "ാ"
+ ]
+ },
+ {
+ "strings": [
+ "ീ"
+ ]
+ },
+ {
+ "strings": [
+ "ൂ"
+ ]
+ },
+ {
+ "strings": [
+ "ബ"
+ ]
+ },
+ {
+ "strings": [
+ "ഹ"
+ ]
+ },
+ {
+ "strings": [
+ "ഗ"
+ ]
+ },
+ {
+ "strings": [
+ "ദ"
+ ]
+ },
+ {
+ "strings": [
+ "ജ"
+ ]
+ },
+ {
+ "strings": [
+ "ഡ"
+ ]
+ },
+ {
+ "label": "ZWJ",
+ "strings": [
+ "‍"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ോ"
+ ]
+ },
+ {
+ "strings": [
+ "േ"
+ ]
+ },
+ {
+ "strings": [
+ "്"
+ ]
+ },
+ {
+ "strings": [
+ "ി"
+ ]
+ },
+ {
+ "strings": [
+ "ു"
+ ]
+ },
+ {
+ "strings": [
+ "പ"
+ ]
+ },
+ {
+ "strings": [
+ "ര"
+ ]
+ },
+ {
+ "strings": [
+ "ക"
+ ]
+ },
+ {
+ "strings": [
+ "ത"
+ ]
+ },
+ {
+ "strings": [
+ "ച"
+ ]
+ },
+ {
+ "strings": [
+ "ട"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1
+ },
+ {
+ "strings": [
+ "െ"
+ ]
+ },
+ {
+ "strings": [
+ "ം"
+ ]
+ },
+ {
+ "strings": [
+ "മ",
+ "ç"
+ ]
+ },
+ {
+ "strings": [
+ "ന"
+ ]
+ },
+ {
+ "strings": [
+ "വ"
+ ]
+ },
+ {
+ "strings": [
+ "ല",
+ "ñ"
+ ]
+ },
+ {
+ "strings": [
+ "സ"
+ ]
+ },
+ {
+ "strings": [
+ "ഷ"
+ ]
+ },
+ {
+ "strings": [
+ "യ"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 7
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ഐ"
+ ]
+ },
+ {
+ "strings": [
+ "ആ"
+ ]
+ },
+ {
+ "strings": [
+ "ഈ"
+ ]
+ },
+ {
+ "strings": [
+ "ഊ"
+ ]
+ },
+ {
+ "strings": [
+ "ഭ"
+ ]
+ },
+ {
+ "strings": [
+ "ങ"
+ ]
+ },
+ {
+ "strings": [
+ "ഘ"
+ ]
+ },
+ {
+ "strings": [
+ "ധ"
+ ]
+ },
+ {
+ "strings": [
+ "ഝ"
+ ]
+ },
+ {
+ "strings": [
+ "ഢ"
+ ]
+ },
+ {
+ "strings": [
+ "ഞ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 2.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ഓ"
+ ]
+ },
+ {
+ "strings": [
+ "ഏ"
+ ]
+ },
+ {
+ "strings": [
+ "അ"
+ ]
+ },
+ {
+ "strings": [
+ "ഇ"
+ ]
+ },
+ {
+ "strings": [
+ "ഉ"
+ ]
+ },
+ {
+ "strings": [
+ "ഫ"
+ ]
+ },
+ {
+ "strings": [
+ "റ"
+ ]
+ },
+ {
+ "strings": [
+ "ഖ"
+ ]
+ },
+ {
+ "strings": [
+ "ഥ"
+ ]
+ },
+ {
+ "strings": [
+ "ഛ"
+ ]
+ },
+ {
+ "strings": [
+ "ഠ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "എ"
+ ]
+ },
+ {
+ "label": "ZWJ",
+ "strings": [
+ "​"
+ ]
+ },
+ {
+ "strings": [
+ "ണ"
+ ]
+ },
+ {
+ "strings": [
+ "ന"
+ ]
+ },
+ {
+ "strings": [
+ "ഴ"
+ ]
+ },
+ {
+ "strings": [
+ "ള"
+ ]
+ },
+ {
+ "strings": [
+ "ശ"
+ ]
+ },
+ {
+ "strings": [
+ "ഷ"
+ ]
+ },
+ {
+ "strings": [
+ "യ"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 7
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "൧",
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "൨",
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "൩",
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "൪",
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "൫",
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "൬",
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "൭",
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "൮",
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "൯",
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "൦",
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "₹",
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ml",
+ "name": "Malayalam"
+}
diff --git a/data/osk-layouts/ir.json b/data/osk-layouts/ir.json
new file mode 100644
index 0000000..eab25bb
--- /dev/null
+++ b/data/osk-layouts/ir.json
@@ -0,0 +1,797 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ض"
+ ]
+ },
+ {
+ "strings": [
+ "ص"
+ ]
+ },
+ {
+ "strings": [
+ "ث"
+ ]
+ },
+ {
+ "strings": [
+ "ق",
+ "ڨ"
+ ]
+ },
+ {
+ "strings": [
+ "ف",
+ "ڤ",
+ "ڢ",
+ "ڥ"
+ ]
+ },
+ {
+ "strings": [
+ "غ"
+ ]
+ },
+ {
+ "strings": [
+ "ع"
+ ]
+ },
+ {
+ "strings": [
+ "ه",
+ "ه‍"
+ ]
+ },
+ {
+ "strings": [
+ "خ"
+ ]
+ },
+ {
+ "strings": [
+ "ح"
+ ]
+ },
+ {
+ "strings": [
+ "ج",
+ "چ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ش",
+ "ڜ"
+ ]
+ },
+ {
+ "strings": [
+ "س"
+ ]
+ },
+ {
+ "strings": [
+ "ي",
+ "ئ",
+ "ى"
+ ]
+ },
+ {
+ "strings": [
+ "ب",
+ "پ"
+ ]
+ },
+ {
+ "strings": [
+ "ل",
+ "لا",
+ "لأ",
+ "لإ",
+ "لآ"
+ ]
+ },
+ {
+ "strings": [
+ "ا",
+ "آ",
+ "ء",
+ "أ",
+ "إ",
+ "ٱ"
+ ]
+ },
+ {
+ "strings": [
+ "ت"
+ ]
+ },
+ {
+ "strings": [
+ "ن"
+ ]
+ },
+ {
+ "strings": [
+ "م"
+ ]
+ },
+ {
+ "strings": [
+ "ك",
+ "گ",
+ "ک"
+ ]
+ },
+ {
+ "strings": [
+ "ط"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ذ"
+ ]
+ },
+ {
+ "strings": [
+ "ء"
+ ]
+ },
+ {
+ "strings": [
+ "ؤ"
+ ]
+ },
+ {
+ "strings": [
+ "ر"
+ ]
+ },
+ {
+ "strings": [
+ "ى",
+ "ئ"
+ ]
+ },
+ {
+ "strings": [
+ "ة"
+ ]
+ },
+ {
+ "strings": [
+ "و"
+ ]
+ },
+ {
+ "strings": [
+ "ز",
+ "ژ"
+ ]
+ },
+ {
+ "strings": [
+ "ظ"
+ ]
+ },
+ {
+ "strings": [
+ "د"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "،"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "\"",
+ "'",
+ "#",
+ "-",
+ ":",
+ "!",
+ "،",
+ "؟",
+ "@",
+ "&",
+ "%",
+ "+",
+ "؛",
+ "/",
+ ")",
+ "("
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "۱",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "۲",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "۳",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "۴",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "۵",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "۶"
+ ]
+ },
+ {
+ "strings": [
+ "۷",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "۸"
+ ]
+ },
+ {
+ "strings": [
+ "۹"
+ ]
+ },
+ {
+ "strings": [
+ "۰",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "٬",
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "٫",
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "﷼",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "٪",
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "﴿",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ "﴾",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "★",
+ "٭"
+ ]
+ },
+ {
+ "strings": [
+ "«",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "»",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ "؛",
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "؟",
+ "?"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ "،",
+ ":",
+ "!",
+ "؟",
+ "؛",
+ "-",
+ "/",
+ "»",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "ٕ",
+ "ٔ",
+ "ْ",
+ "ٍ",
+ "ٌ",
+ "ً",
+ "ّ",
+ "ٖ",
+ "ٰ",
+ "ٓ",
+ "ِ",
+ "ُ",
+ "َ",
+ "ـ"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "«",
+ "›",
+ "≥",
+ ">"
+ ]
+ },
+ {
+ "strings": [
+ "»",
+ "‹",
+ "≤",
+ "<"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ "،",
+ ":",
+ "!",
+ "؟",
+ "؛",
+ "-",
+ "/",
+ "»",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "ٕ",
+ "ٔ",
+ "ْ",
+ "ٍ",
+ "ٌ",
+ "ً",
+ "ّ",
+ "ٖ",
+ "ٰ",
+ "ٓ",
+ "ِ",
+ "ُ",
+ "َ",
+ "ـ"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "fa",
+ "name": "Persian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/is.json b/data/osk-layouts/is.json
new file mode 100644
index 0000000..ac3176c
--- /dev/null
+++ b/data/osk-layouts/is.json
@@ -0,0 +1,991 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ë",
+ "è",
+ "ê",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "þ"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "û",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï",
+ "î",
+ "ì",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ö",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "ä",
+ "æ",
+ "å",
+ "à",
+ "â",
+ "ã",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ð"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ë",
+ "È",
+ "Ê",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Þ"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï",
+ "Î",
+ "Ì",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Ä",
+ "Æ",
+ "Å",
+ "À",
+ "Â",
+ "Ã",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ð"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "is",
+ "name": "Icelandic"
+}
diff --git a/data/osk-layouts/it.json b/data/osk-layouts/it.json
new file mode 100644
index 0000000..781aa0f
--- /dev/null
+++ b/data/osk-layouts/it.json
@@ -0,0 +1,988 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ù",
+ "ú",
+ "û",
+ "ü",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "ì",
+ "í",
+ "î",
+ "ï",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ò",
+ "ó",
+ "ô",
+ "ö",
+ "õ",
+ "œ",
+ "ø",
+ "ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "á",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ù",
+ "Ú",
+ "Û",
+ "Ü",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Ì",
+ "Í",
+ "Î",
+ "Ï",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ò",
+ "Ó",
+ "Ô",
+ "Ö",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Á",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "it",
+ "name": "Italian"
+}
diff --git a/data/osk-layouts/ke.json b/data/osk-layouts/ke.json
new file mode 100644
index 0000000..de7b8fc
--- /dev/null
+++ b/data/osk-layouts/ke.json
@@ -0,0 +1,985 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "û",
+ "ü",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "î",
+ "ï",
+ "í",
+ "ī",
+ "ì"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ô",
+ "ö",
+ "ò",
+ "ó",
+ "œ",
+ "ø",
+ "ō",
+ "õ"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "á",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g",
+ "g'"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Û",
+ "Ü",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Î",
+ "Ï",
+ "Í",
+ "Ī",
+ "Ì"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ô",
+ "Ö",
+ "Ò",
+ "Ó",
+ "Œ",
+ "Ø",
+ "Ō",
+ "Õ"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Á",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G",
+ "G'"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "sw",
+ "name": "Swahili"
+}
diff --git a/data/osk-layouts/kg.json b/data/osk-layouts/kg.json
new file mode 100644
index 0000000..1f806ad
--- /dev/null
+++ b/data/osk-layouts/kg.json
@@ -0,0 +1,971 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "й"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "у",
+ "ү"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "strings": [
+ "е",
+ "ё"
+ ]
+ },
+ {
+ "strings": [
+ "н",
+ "ң"
+ ]
+ },
+ {
+ "strings": [
+ "г"
+ ]
+ },
+ {
+ "strings": [
+ "ш"
+ ]
+ },
+ {
+ "strings": [
+ "щ"
+ ]
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "ы"
+ ]
+ },
+ {
+ "strings": [
+ "в"
+ ]
+ },
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "о",
+ "ө"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "strings": [
+ "э"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "я"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "и"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "ь",
+ "ъ"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Й"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "У",
+ "Ү"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "strings": [
+ "Е",
+ "Ё"
+ ]
+ },
+ {
+ "strings": [
+ "Н",
+ "Ң"
+ ]
+ },
+ {
+ "strings": [
+ "Г"
+ ]
+ },
+ {
+ "strings": [
+ "Ш"
+ ]
+ },
+ {
+ "strings": [
+ "Щ"
+ ]
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "Ы"
+ ]
+ },
+ {
+ "strings": [
+ "В"
+ ]
+ },
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "О",
+ "Ө"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "strings": [
+ "Э"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Я"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "И"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "Ь",
+ "Ъ"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ky",
+ "name": "Kirghiz"
+} \ No newline at end of file
diff --git a/data/osk-layouts/kh.json b/data/osk-layouts/kh.json
new file mode 100644
index 0000000..78591b5
--- /dev/null
+++ b/data/osk-layouts/kh.json
@@ -0,0 +1,817 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "១",
+ "៱"
+ ]
+ },
+ {
+ "strings": [
+ "២",
+ "៲"
+ ]
+ },
+ {
+ "strings": [
+ "៣",
+ "៳"
+ ]
+ },
+ {
+ "strings": [
+ "៤",
+ "៴"
+ ]
+ },
+ {
+ "strings": [
+ "៥",
+ "៵"
+ ]
+ },
+ {
+ "strings": [
+ "៦",
+ "៶"
+ ]
+ },
+ {
+ "strings": [
+ "៧",
+ "៷"
+ ]
+ },
+ {
+ "strings": [
+ "៨",
+ "៸"
+ ]
+ },
+ {
+ "strings": [
+ "៩",
+ "៹"
+ ]
+ },
+ {
+ "strings": [
+ "០",
+ "៰"
+ ]
+ },
+ {
+ "strings": [
+ "ឥ",
+ "",
+ "ឦ"
+ ]
+ },
+ {
+ "strings": [
+ "ឲ",
+ "ឱ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ឆ"
+ ]
+ },
+ {
+ "strings": [
+ "ឹ"
+ ]
+ },
+ {
+ "strings": [
+ "េ"
+ ]
+ },
+ {
+ "strings": [
+ "រ"
+ ]
+ },
+ {
+ "strings": [
+ "ត"
+ ]
+ },
+ {
+ "strings": [
+ "យ"
+ ]
+ },
+ {
+ "strings": [
+ "ុ"
+ ]
+ },
+ {
+ "strings": [
+ "ិ"
+ ]
+ },
+ {
+ "strings": [
+ "ោ"
+ ]
+ },
+ {
+ "strings": [
+ "ផ"
+ ]
+ },
+ {
+ "strings": [
+ "ៀ"
+ ]
+ },
+ {
+ "strings": [
+ "ឪ",
+ "ឧ",
+ "ឱ",
+ "ឳ",
+ "ឩ",
+ "ឨ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ា"
+ ]
+ },
+ {
+ "strings": [
+ "ស"
+ ]
+ },
+ {
+ "strings": [
+ "ដ"
+ ]
+ },
+ {
+ "strings": [
+ "ថ"
+ ]
+ },
+ {
+ "strings": [
+ "ង"
+ ]
+ },
+ {
+ "strings": [
+ "ហ"
+ ]
+ },
+ {
+ "strings": [
+ "្"
+ ]
+ },
+ {
+ "strings": [
+ "ក"
+ ]
+ },
+ {
+ "strings": [
+ "ល"
+ ]
+ },
+ {
+ "strings": [
+ "ើ"
+ ]
+ },
+ {
+ "strings": [
+ "់"
+ ]
+ },
+ {
+ "strings": [
+ "ឮ",
+ "ឭ",
+ "ឰ"
+ ]
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ឋ"
+ ]
+ },
+ {
+ "strings": [
+ "ខ"
+ ]
+ },
+ {
+ "strings": [
+ "ច"
+ ]
+ },
+ {
+ "strings": [
+ "វ"
+ ]
+ },
+ {
+ "strings": [
+ "ប"
+ ]
+ },
+ {
+ "strings": [
+ "ន"
+ ]
+ },
+ {
+ "strings": [
+ "ម"
+ ]
+ },
+ {
+ "strings": [
+ "ុំ"
+ ]
+ },
+ {
+ "strings": [
+ "។"
+ ]
+ },
+ {
+ "strings": [
+ "៊"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 7
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "៛",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "km",
+ "name": "Khmer"
+}
diff --git a/data/osk-layouts/kr.json b/data/osk-layouts/kr.json
new file mode 100644
index 0000000..d89f8c7
--- /dev/null
+++ b/data/osk-layouts/kr.json
@@ -0,0 +1,720 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "label": "ㅂ",
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "label": "ㅈ",
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "label": "ㄷ",
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "label": "ㄱ",
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "label": "ㅅ",
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "label": "ㅛ",
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "label": "ㅕ",
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "label": "ㅑ",
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "label": "ㅐ",
+ "strings": [
+ "o"
+ ]
+ },
+ {
+ "label": "ㅔ",
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "iconName": "edit-clear-symbolic",
+ "keyval": "0xff08",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "label": "ㅁ",
+ "strings": [
+ "a"
+ ]
+ },
+ {
+ "label": "ㄴ",
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "label": "ㅇ",
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "label": "ㄹ",
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "label": "ㅎ",
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "label": "ㅗ",
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "label": "ㅓ",
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "label": "ㅏ",
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "label": "ㅣ",
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "label": "ㅋ",
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "label": "ㅌ",
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "label": "ㅊ",
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "label": "ㅍ",
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "label": "ㅠ",
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "label": "ㅜ",
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "label": "ㅡ",
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "iconName": "edit-clear-symbolic",
+ "keyval": "0xff08",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "iconName": "edit-clear-symbolic",
+ "keyval": "0xff08",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "kr",
+ "name": "Korean (Hangul)"
+}
diff --git a/data/osk-layouts/la.json b/data/osk-layouts/la.json
new file mode 100644
index 0000000..65f0fee
--- /dev/null
+++ b/data/osk-layouts/la.json
@@ -0,0 +1,808 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ຢ",
+ "໑"
+ ]
+ },
+ {
+ "strings": [
+ "ຟ",
+ "໒"
+ ]
+ },
+ {
+ "strings": [
+ "ໂ",
+ "໓"
+ ]
+ },
+ {
+ "strings": [
+ "ຖ",
+ "໔"
+ ]
+ },
+ {
+ "strings": [
+ "ຸ"
+ ]
+ },
+ {
+ "strings": [
+ "ູ"
+ ]
+ },
+ {
+ "strings": [
+ "ຄ",
+ "໕"
+ ]
+ },
+ {
+ "strings": [
+ "ຕ",
+ "໖"
+ ]
+ },
+ {
+ "strings": [
+ "ຈ",
+ "໗"
+ ]
+ },
+ {
+ "strings": [
+ "ຂ",
+ "໘"
+ ]
+ },
+ {
+ "strings": [
+ "ຊ",
+ "໙"
+ ]
+ },
+ {
+ "strings": [
+ "ໍ"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ົ"
+ ]
+ },
+ {
+ "strings": [
+ "ໄ",
+ "໐"
+ ]
+ },
+ {
+ "strings": [
+ "ຳ"
+ ]
+ },
+ {
+ "strings": [
+ "ພ"
+ ]
+ },
+ {
+ "strings": [
+ "ະ"
+ ]
+ },
+ {
+ "strings": [
+ "ິ"
+ ]
+ },
+ {
+ "strings": [
+ "ີ"
+ ]
+ },
+ {
+ "strings": [
+ "ຮ"
+ ]
+ },
+ {
+ "strings": [
+ "ນ"
+ ]
+ },
+ {
+ "strings": [
+ "ຍ"
+ ]
+ },
+ {
+ "strings": [
+ "ບ"
+ ]
+ },
+ {
+ "strings": [
+ "ລ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ັ"
+ ]
+ },
+ {
+ "strings": [
+ "ຫ"
+ ]
+ },
+ {
+ "strings": [
+ "ກ"
+ ]
+ },
+ {
+ "strings": [
+ "ດ"
+ ]
+ },
+ {
+ "strings": [
+ "ເ"
+ ]
+ },
+ {
+ "strings": [
+ "້"
+ ]
+ },
+ {
+ "strings": [
+ "່"
+ ]
+ },
+ {
+ "strings": [
+ "າ"
+ ]
+ },
+ {
+ "strings": [
+ "ສ"
+ ]
+ },
+ {
+ "strings": [
+ "ວ"
+ ]
+ },
+ {
+ "strings": [
+ "ງ"
+ ]
+ },
+ {
+ "strings": [
+ "“"
+ ]
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ຜ"
+ ]
+ },
+ {
+ "strings": [
+ "ປ"
+ ]
+ },
+ {
+ "strings": [
+ "ແ"
+ ]
+ },
+ {
+ "strings": [
+ "ອ"
+ ]
+ },
+ {
+ "strings": [
+ "ຶ"
+ ]
+ },
+ {
+ "strings": [
+ "ື"
+ ]
+ },
+ {
+ "strings": [
+ "ທ"
+ ]
+ },
+ {
+ "strings": [
+ "ມ"
+ ]
+ },
+ {
+ "strings": [
+ "ໃ"
+ ]
+ },
+ {
+ "strings": [
+ "ຝ"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 7
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "₭",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "lo",
+ "name": "Lao"
+}
diff --git a/data/osk-layouts/latam.json b/data/osk-layouts/latam.json
new file mode 100644
index 0000000..01c67f8
--- /dev/null
+++ b/data/osk-layouts/latam.json
@@ -0,0 +1,1015 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ë",
+ "ê",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "ù",
+ "û",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï",
+ "ì",
+ "î",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ò",
+ "ö",
+ "ô",
+ "õ",
+ "ø",
+ "œ",
+ "ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "à",
+ "ä",
+ "â",
+ "ã",
+ "å",
+ "ą",
+ "æ",
+ "ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "strings": [
+ "ñ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ ";",
+ "!",
+ ",",
+ "?",
+ ":",
+ "¡",
+ "@",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ë",
+ "Ê",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Ù",
+ "Û",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï",
+ "Ì",
+ "Î",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ò",
+ "Ö",
+ "Ô",
+ "Õ",
+ "Ø",
+ "Œ",
+ "Ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "À",
+ "Ä",
+ "Â",
+ "Ã",
+ "Å",
+ "Ą",
+ "Æ",
+ "Ā",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "strings": [
+ "Ñ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ ";",
+ "!",
+ ",",
+ "?",
+ ":",
+ "¡",
+ "@",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ",",
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ",",
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "es-US",
+ "name": "Spanish United States"
+}
diff --git a/data/osk-layouts/lt.json b/data/osk-layouts/lt.json
new file mode 100644
index 0000000..3f678b5
--- /dev/null
+++ b/data/osk-layouts/lt.json
@@ -0,0 +1,1055 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "ė",
+ "ę",
+ "ē",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ě"
+ ]
+ },
+ {
+ "strings": [
+ "r",
+ "ŗ",
+ "ř",
+ "ŕ"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ţ",
+ "ť"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ū",
+ "ų",
+ "ü",
+ "ū",
+ "ù",
+ "ú",
+ "û",
+ "ů",
+ "ű"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "į",
+ "ī",
+ "ì",
+ "í",
+ "î",
+ "ï",
+ "ı"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ö",
+ "õ",
+ "ò",
+ "ó",
+ "ô",
+ "œ",
+ "ő",
+ "ø"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ą",
+ "ä",
+ "ā",
+ "à",
+ "á",
+ "â",
+ "ã",
+ "å",
+ "æ"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š",
+ "ß",
+ "ś",
+ "ş"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ď"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g",
+ "ģ",
+ "ğ"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k",
+ "ķ"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ļ",
+ "ł",
+ "ĺ",
+ "ľ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z",
+ "ž",
+ "ż",
+ "ź"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "č",
+ "ç",
+ "ć"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ņ",
+ "ñ",
+ "ń",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "Ė",
+ "Ę",
+ "Ē",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ě"
+ ]
+ },
+ {
+ "strings": [
+ "R",
+ "Ŗ",
+ "Ř",
+ "Ŕ"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ţ",
+ "Ť"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ū",
+ "Ų",
+ "Ü",
+ "Ū",
+ "Ù",
+ "Ú",
+ "Û",
+ "Ů",
+ "Ű"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Į",
+ "Ī",
+ "Ì",
+ "Í",
+ "Î",
+ "Ï",
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ö",
+ "Õ",
+ "Ò",
+ "Ó",
+ "Ô",
+ "Œ",
+ "Ő",
+ "Ø"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ą",
+ "Ä",
+ "Ā",
+ "À",
+ "Á",
+ "Â",
+ "Ã",
+ "Å",
+ "Æ"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š",
+ "SS",
+ "Ś",
+ "Ş"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ď"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G",
+ "Ģ",
+ "Ğ"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K",
+ "Ķ"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ļ",
+ "Ł",
+ "Ĺ",
+ "Ľ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž",
+ "Ż",
+ "Ź"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Č",
+ "Ç",
+ "Ć"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ņ",
+ "Ñ",
+ "Ń",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "lt",
+ "name": "Lithuanian"
+}
diff --git a/data/osk-layouts/lv.json b/data/osk-layouts/lv.json
new file mode 100644
index 0000000..c3141ff
--- /dev/null
+++ b/data/osk-layouts/lv.json
@@ -0,0 +1,1053 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "ē",
+ "ė",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ę",
+ "ě"
+ ]
+ },
+ {
+ "strings": [
+ "r",
+ "ŗ",
+ "ř",
+ "ŕ"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ţ",
+ "ť"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ū",
+ "ų",
+ "ù",
+ "ú",
+ "û",
+ "ü",
+ "ů",
+ "ű"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "ī",
+ "į",
+ "ì",
+ "í",
+ "î",
+ "ï",
+ "ı"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ò",
+ "ó",
+ "ô",
+ "õ",
+ "ö",
+ "œ",
+ "ő",
+ "ø"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ā",
+ "à",
+ "á",
+ "â",
+ "ã",
+ "ä",
+ "å",
+ "æ",
+ "ą"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š",
+ "ß",
+ "ś",
+ "ş"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ď"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g",
+ "ģ",
+ "ğ"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k",
+ "ķ"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ļ",
+ "ł",
+ "ĺ",
+ "ľ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z",
+ "ž",
+ "ż",
+ "ź"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "č",
+ "ç",
+ "ć"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ņ",
+ "ñ",
+ "ń",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "Ē",
+ "Ė",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ę",
+ "Ě"
+ ]
+ },
+ {
+ "strings": [
+ "R",
+ "Ŗ",
+ "Ř",
+ "Ŕ"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ţ",
+ "Ť"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ū",
+ "Ų",
+ "Ù",
+ "Ú",
+ "Û",
+ "Ü",
+ "Ů",
+ "Ű"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Ī",
+ "Į",
+ "Ì",
+ "Í",
+ "Î",
+ "Ï",
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ò",
+ "Ó",
+ "Ô",
+ "Õ",
+ "Ö",
+ "Œ",
+ "Ő",
+ "Ø"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ā",
+ "À",
+ "Á",
+ "Â",
+ "Ã",
+ "Ä",
+ "Å",
+ "Æ",
+ "Ą"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š",
+ "SS",
+ "Ś",
+ "Ş"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ď"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G",
+ "Ģ",
+ "Ğ"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K",
+ "Ķ"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ļ",
+ "Ł",
+ "Ĺ",
+ "Ľ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž",
+ "Ż",
+ "Ź"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Č",
+ "Ç",
+ "Ć"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ņ",
+ "Ñ",
+ "Ń",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "lv",
+ "name": "Latvian"
+}
diff --git a/data/osk-layouts/mk.json b/data/osk-layouts/mk.json
new file mode 100644
index 0000000..0c20ead
--- /dev/null
+++ b/data/osk-layouts/mk.json
@@ -0,0 +1,965 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "љ"
+ ]
+ },
+ {
+ "strings": [
+ "њ"
+ ]
+ },
+ {
+ "strings": [
+ "е",
+ "ѐ"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "ѕ"
+ ]
+ },
+ {
+ "strings": [
+ "у"
+ ]
+ },
+ {
+ "strings": [
+ "и",
+ "ѝ"
+ ]
+ },
+ {
+ "strings": [
+ "о"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "strings": [
+ "ш"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "г"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "strings": [
+ "ј"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "strings": [
+ "ќ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "џ"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "в"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "н"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "ѓ"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Љ"
+ ]
+ },
+ {
+ "strings": [
+ "Њ"
+ ]
+ },
+ {
+ "strings": [
+ "Е",
+ "Ѐ"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "Ѕ"
+ ]
+ },
+ {
+ "strings": [
+ "У"
+ ]
+ },
+ {
+ "strings": [
+ "И",
+ "Ѝ"
+ ]
+ },
+ {
+ "strings": [
+ "О"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "strings": [
+ "Ш"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "Г"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "strings": [
+ "Ј"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "strings": [
+ "Ќ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "Џ"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "В"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Н"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "Ѓ"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "mk",
+ "name": "Macedonian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/mn.json b/data/osk-layouts/mn.json
new file mode 100644
index 0000000..9cf41f5
--- /dev/null
+++ b/data/osk-layouts/mn.json
@@ -0,0 +1,971 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "у"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "strings": [
+ "э"
+ ]
+ },
+ {
+ "strings": [
+ "н"
+ ]
+ },
+ {
+ "strings": [
+ "г"
+ ]
+ },
+ {
+ "strings": [
+ "ш",
+ "щ"
+ ]
+ },
+ {
+ "strings": [
+ "ү"
+ ]
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "й"
+ ]
+ },
+ {
+ "strings": [
+ "ы"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "ө"
+ ]
+ },
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "о"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "я"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "strings": [
+ "ё",
+ "е"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "и"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "ь",
+ "ъ"
+ ]
+ },
+ {
+ "strings": [
+ "в",
+ "ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "У"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "strings": [
+ "Э"
+ ]
+ },
+ {
+ "strings": [
+ "Н"
+ ]
+ },
+ {
+ "strings": [
+ "Г"
+ ]
+ },
+ {
+ "strings": [
+ "Ш",
+ "Щ"
+ ]
+ },
+ {
+ "strings": [
+ "Ү"
+ ]
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Й"
+ ]
+ },
+ {
+ "strings": [
+ "Ы"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Ө"
+ ]
+ },
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "О"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Я"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "strings": [
+ "Ё",
+ "Е"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "И"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "Ь",
+ "Ъ"
+ ]
+ },
+ {
+ "strings": [
+ "В",
+ "Ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "₮",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "mn",
+ "name": "Mongolian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/my.json b/data/osk-layouts/my.json
new file mode 100644
index 0000000..1aedf20
--- /dev/null
+++ b/data/osk-layouts/my.json
@@ -0,0 +1,915 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ms",
+ "name": "Malay"
+}
diff --git a/data/osk-layouts/nl.json b/data/osk-layouts/nl.json
new file mode 100644
index 0000000..61bd4dd
--- /dev/null
+++ b/data/osk-layouts/nl.json
@@ -0,0 +1,992 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ë",
+ "ê",
+ "è",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ij"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "û",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï",
+ "ì",
+ "î",
+ "į",
+ "ī",
+ "ij"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ö",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "ä",
+ "â",
+ "à",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ë",
+ "Ê",
+ "È",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "IJ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï",
+ "Ì",
+ "Î",
+ "Į",
+ "Ī",
+ "IJ"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Ä",
+ "Â",
+ "À",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "nl",
+ "name": "Dutch"
+}
diff --git a/data/osk-layouts/no.json b/data/osk-layouts/no.json
new file mode 100644
index 0000000..4be4522
--- /dev/null
+++ b/data/osk-layouts/no.json
@@ -0,0 +1,1012 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ë"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "û",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ï"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "strings": [
+ "å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "ä",
+ "à",
+ "â",
+ "ã",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ð"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ł"
+ ]
+ },
+ {
+ "strings": [
+ "ø",
+ "ö"
+ ]
+ },
+ {
+ "strings": [
+ "æ",
+ "ä"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ë"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ï"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "strings": [
+ "Å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Ä",
+ "À",
+ "Â",
+ "Ã",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ð"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ł"
+ ]
+ },
+ {
+ "strings": [
+ "Ø",
+ "Ö"
+ ]
+ },
+ {
+ "strings": [
+ "Æ",
+ "Ä"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "nb",
+ "name": "Norwegian Bokmål"
+}
diff --git a/data/osk-layouts/ph.json b/data/osk-layouts/ph.json
new file mode 100644
index 0000000..0dbe4f2
--- /dev/null
+++ b/data/osk-layouts/ph.json
@@ -0,0 +1,915 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "fil",
+ "name": "Filipino"
+}
diff --git a/data/osk-layouts/pl.json b/data/osk-layouts/pl.json
new file mode 100644
index 0000000..b8019b1
--- /dev/null
+++ b/data/osk-layouts/pl.json
@@ -0,0 +1,987 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "ę",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ö",
+ "ô",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "ą",
+ "á",
+ "à",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ś",
+ "ß",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ł"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z",
+ "ż",
+ "ź",
+ "ž"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ć",
+ "ç",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ń",
+ "ñ"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "Ę",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ö",
+ "Ô",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Ą",
+ "Á",
+ "À",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Ś",
+ "SS",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ł"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z",
+ "Ż",
+ "Ź",
+ "Ž"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ć",
+ "Ç",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ń",
+ "Ñ"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "pl",
+ "name": "Polish"
+}
diff --git a/data/osk-layouts/pt.json b/data/osk-layouts/pt.json
new file mode 100644
index 0000000..11c8c6d
--- /dev/null
+++ b/data/osk-layouts/pt.json
@@ -0,0 +1,992 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ê",
+ "è",
+ "ę",
+ "ė",
+ "ē",
+ "ë"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ü",
+ "ù",
+ "û",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "î",
+ "ì",
+ "ï",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "õ",
+ "ô",
+ "ò",
+ "ö",
+ "œ",
+ "ø",
+ "ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "ã",
+ "à",
+ "â",
+ "ä",
+ "å",
+ "æ",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "č",
+ "ć"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ê",
+ "È",
+ "Ę",
+ "Ė",
+ "Ē",
+ "Ë"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ü",
+ "Ù",
+ "Û",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Î",
+ "Ì",
+ "Ï",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Õ",
+ "Ô",
+ "Ò",
+ "Ö",
+ "Œ",
+ "Ø",
+ "Ō",
+ "º"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Ã",
+ "À",
+ "Â",
+ "Ä",
+ "Å",
+ "Æ",
+ "ª"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Č",
+ "Ć"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "pt-PT",
+ "name": "Portuguese Portugal"
+}
diff --git a/data/osk-layouts/ro.json b/data/osk-layouts/ro.json
new file mode 100644
index 0000000..3af77e8
--- /dev/null
+++ b/data/osk-layouts/ro.json
@@ -0,0 +1,955 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ț"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "î",
+ "ï",
+ "ì",
+ "í",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "â",
+ "ã",
+ "ă",
+ "à",
+ "á",
+ "ä",
+ "æ",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ș",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ț"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Î",
+ "Ï",
+ "Ì",
+ "Í",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Â",
+ "Ã",
+ "Ă",
+ "À",
+ "Á",
+ "Ä",
+ "Æ",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Ș",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ro",
+ "name": "Romanian"
+}
diff --git a/data/osk-layouts/rs.json b/data/osk-layouts/rs.json
new file mode 100644
index 0000000..f0cdb28
--- /dev/null
+++ b/data/osk-layouts/rs.json
@@ -0,0 +1,965 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "љ"
+ ]
+ },
+ {
+ "strings": [
+ "њ"
+ ]
+ },
+ {
+ "strings": [
+ "е",
+ "ѐ"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "у"
+ ]
+ },
+ {
+ "strings": [
+ "и",
+ "ѝ"
+ ]
+ },
+ {
+ "strings": [
+ "о"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "strings": [
+ "ш"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "г"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "strings": [
+ "ј"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "strings": [
+ "ћ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "ѕ"
+ ]
+ },
+ {
+ "strings": [
+ "џ"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "в"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "н"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "ђ"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Љ"
+ ]
+ },
+ {
+ "strings": [
+ "Њ"
+ ]
+ },
+ {
+ "strings": [
+ "Е",
+ "Ѐ"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "У"
+ ]
+ },
+ {
+ "strings": [
+ "И",
+ "Ѝ"
+ ]
+ },
+ {
+ "strings": [
+ "О"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "strings": [
+ "Ш"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "Г"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "strings": [
+ "Ј"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "strings": [
+ "Ћ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Ѕ"
+ ]
+ },
+ {
+ "strings": [
+ "Џ"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "В"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Н"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "Ђ"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "sr",
+ "name": "Serbian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/ru.json b/data/osk-layouts/ru.json
new file mode 100644
index 0000000..154c22e
--- /dev/null
+++ b/data/osk-layouts/ru.json
@@ -0,0 +1,965 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "й"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "у"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "strings": [
+ "е",
+ "ё"
+ ]
+ },
+ {
+ "strings": [
+ "н"
+ ]
+ },
+ {
+ "strings": [
+ "г"
+ ]
+ },
+ {
+ "strings": [
+ "ш"
+ ]
+ },
+ {
+ "strings": [
+ "щ"
+ ]
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "ы"
+ ]
+ },
+ {
+ "strings": [
+ "в"
+ ]
+ },
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "о"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "strings": [
+ "э"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "я"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "и"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "ь",
+ "ъ"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Й"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "У"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "strings": [
+ "Е",
+ "Ё"
+ ]
+ },
+ {
+ "strings": [
+ "Н"
+ ]
+ },
+ {
+ "strings": [
+ "Г"
+ ]
+ },
+ {
+ "strings": [
+ "Ш"
+ ]
+ },
+ {
+ "strings": [
+ "Щ"
+ ]
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "Ы"
+ ]
+ },
+ {
+ "strings": [
+ "В"
+ ]
+ },
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "О"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "strings": [
+ "Э"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Я"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "И"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "Ь",
+ "Ъ"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "ru",
+ "name": "Russian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/se.json b/data/osk-layouts/se.json
new file mode 100644
index 0000000..87da9fe
--- /dev/null
+++ b/data/osk-layouts/se.json
@@ -0,0 +1,1046 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "ę"
+ ]
+ },
+ {
+ "strings": [
+ "r",
+ "ř"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ť",
+ "þ"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ",
+ "ü"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü",
+ "ú",
+ "ù",
+ "û",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ì",
+ "î",
+ "ï"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ò",
+ "ô",
+ "õ",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "strings": [
+ "å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "à",
+ "â",
+ "ą",
+ "ã"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ś",
+ "š",
+ "ş",
+ "ß"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ð",
+ "ď"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ł"
+ ]
+ },
+ {
+ "strings": [
+ "ö",
+ "ø",
+ "œ"
+ ]
+ },
+ {
+ "strings": [
+ "ä",
+ "æ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "z",
+ "ź",
+ "ž",
+ "ż"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ń",
+ "ñ",
+ "ň"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "Ę"
+ ]
+ },
+ {
+ "strings": [
+ "R",
+ "Ř"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ť",
+ "Þ"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ",
+ "Ü"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü",
+ "Ú",
+ "Ù",
+ "Û",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ì",
+ "Î",
+ "Ï"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ò",
+ "Ô",
+ "Õ",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "strings": [
+ "Å"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "À",
+ "Â",
+ "Ą",
+ "Ã"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Ś",
+ "Š",
+ "Ş",
+ "SS"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ð",
+ "Ď"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ł"
+ ]
+ },
+ {
+ "strings": [
+ "Ö",
+ "Ø",
+ "Œ"
+ ]
+ },
+ {
+ "strings": [
+ "Ä",
+ "Æ"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Z",
+ "Ź",
+ "Ž",
+ "Ż"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ń",
+ "Ñ",
+ "Ň"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "sv",
+ "name": "Swedish"
+}
diff --git a/data/osk-layouts/si.json b/data/osk-layouts/si.json
new file mode 100644
index 0000000..7325c40
--- /dev/null
+++ b/data/osk-layouts/si.json
@@ -0,0 +1,926 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u"
+ ]
+ },
+ {
+ "strings": [
+ "i"
+ ]
+ },
+ {
+ "strings": [
+ "o"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "đ"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z",
+ "ž"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "č",
+ "ć"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U"
+ ]
+ },
+ {
+ "strings": [
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Đ"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Č",
+ "Ć"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "sl",
+ "name": "Slovenian"
+}
diff --git a/data/osk-layouts/sk.json b/data/osk-layouts/sk.json
new file mode 100644
index 0000000..8f834cd
--- /dev/null
+++ b/data/osk-layouts/sk.json
@@ -0,0 +1,1056 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "ě",
+ "ē",
+ "ė",
+ "è",
+ "ê",
+ "ë",
+ "ę"
+ ]
+ },
+ {
+ "strings": [
+ "r",
+ "ŕ",
+ "ř",
+ "ŗ"
+ ]
+ },
+ {
+ "strings": [
+ "t",
+ "ť",
+ "ţ"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "ů",
+ "ü",
+ "ū",
+ "ų",
+ "ù",
+ "û",
+ "ű"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ī",
+ "į",
+ "ì",
+ "î",
+ "ï",
+ "ı"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ô",
+ "ó",
+ "ö",
+ "ò",
+ "õ",
+ "œ",
+ "ő",
+ "ø"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "ä",
+ "ā",
+ "à",
+ "â",
+ "ã",
+ "å",
+ "æ",
+ "ą"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "š",
+ "ß",
+ "ś",
+ "ş"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "ď"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g",
+ "ģ",
+ "ğ"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k",
+ "ķ"
+ ]
+ },
+ {
+ "strings": [
+ "l",
+ "ľ",
+ "ĺ",
+ "ļ",
+ "ł"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z",
+ "ž",
+ "ż",
+ "ź"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "č",
+ "ç",
+ "ć"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ň",
+ "ņ",
+ "ñ",
+ "ń",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "Ě",
+ "Ē",
+ "Ė",
+ "È",
+ "Ê",
+ "Ë",
+ "Ę"
+ ]
+ },
+ {
+ "strings": [
+ "R",
+ "Ŕ",
+ "Ř",
+ "Ŗ"
+ ]
+ },
+ {
+ "strings": [
+ "T",
+ "Ť",
+ "Ţ"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ÿ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Ů",
+ "Ü",
+ "Ū",
+ "Ų",
+ "Ù",
+ "Û",
+ "Ű"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ī",
+ "Į",
+ "Ì",
+ "Î",
+ "Ï",
+ "I"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ô",
+ "Ó",
+ "Ö",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ő",
+ "Ø"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Ä",
+ "Ā",
+ "À",
+ "Â",
+ "Ã",
+ "Å",
+ "Æ",
+ "Ą"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Š",
+ "SS",
+ "Ś",
+ "Ş"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Ď"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G",
+ "Ģ",
+ "Ğ"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K",
+ "Ķ"
+ ]
+ },
+ {
+ "strings": [
+ "L",
+ "Ľ",
+ "Ĺ",
+ "Ļ",
+ "Ł"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z",
+ "Ž",
+ "Ż",
+ "Ź"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Č",
+ "Ç",
+ "Ć"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ň",
+ "Ņ",
+ "Ñ",
+ "Ń",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "sk",
+ "name": "Slovak"
+}
diff --git a/data/osk-layouts/th.json b/data/osk-layouts/th.json
new file mode 100644
index 0000000..131e5eb
--- /dev/null
+++ b/data/osk-layouts/th.json
@@ -0,0 +1,1135 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "ๅ"
+ ]
+ },
+ {
+ "strings": [
+ "/",
+ "๑"
+ ]
+ },
+ {
+ "strings": [
+ "_",
+ "๒"
+ ]
+ },
+ {
+ "strings": [
+ "ภ",
+ "๓"
+ ]
+ },
+ {
+ "strings": [
+ "ถ",
+ "๔"
+ ]
+ },
+ {
+ "strings": [
+ "ุ"
+ ]
+ },
+ {
+ "strings": [
+ "ึ"
+ ]
+ },
+ {
+ "strings": [
+ "ค",
+ "๕"
+ ]
+ },
+ {
+ "strings": [
+ "ต",
+ "๖"
+ ]
+ },
+ {
+ "strings": [
+ "จ",
+ "๗"
+ ]
+ },
+ {
+ "strings": [
+ "ข",
+ "๘"
+ ]
+ },
+ {
+ "strings": [
+ "ช",
+ "๙"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ๆ",
+ "๐"
+ ]
+ },
+ {
+ "strings": [
+ "ไ"
+ ]
+ },
+ {
+ "strings": [
+ "ำ"
+ ]
+ },
+ {
+ "strings": [
+ "พ"
+ ]
+ },
+ {
+ "strings": [
+ "ะ"
+ ]
+ },
+ {
+ "strings": [
+ "ั"
+ ]
+ },
+ {
+ "strings": [
+ "ี"
+ ]
+ },
+ {
+ "strings": [
+ "ร"
+ ]
+ },
+ {
+ "strings": [
+ "น"
+ ]
+ },
+ {
+ "strings": [
+ "ย"
+ ]
+ },
+ {
+ "strings": [
+ "บ"
+ ]
+ },
+ {
+ "strings": [
+ "ล"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ฟ"
+ ]
+ },
+ {
+ "strings": [
+ "ห"
+ ]
+ },
+ {
+ "strings": [
+ "ก"
+ ]
+ },
+ {
+ "strings": [
+ "ด"
+ ]
+ },
+ {
+ "strings": [
+ "เ"
+ ]
+ },
+ {
+ "strings": [
+ "้"
+ ]
+ },
+ {
+ "strings": [
+ "่"
+ ]
+ },
+ {
+ "strings": [
+ "า"
+ ]
+ },
+ {
+ "strings": [
+ "ส"
+ ]
+ },
+ {
+ "strings": [
+ "ว"
+ ]
+ },
+ {
+ "strings": [
+ "ง"
+ ]
+ },
+ {
+ "strings": [
+ "ฃ"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "ผ"
+ ]
+ },
+ {
+ "strings": [
+ "ป"
+ ]
+ },
+ {
+ "strings": [
+ "แ"
+ ]
+ },
+ {
+ "strings": [
+ "อ"
+ ]
+ },
+ {
+ "strings": [
+ "ิ"
+ ]
+ },
+ {
+ "strings": [
+ "ื"
+ ]
+ },
+ {
+ "strings": [
+ "ท"
+ ]
+ },
+ {
+ "strings": [
+ "ม"
+ ]
+ },
+ {
+ "strings": [
+ "ใ"
+ ]
+ },
+ {
+ "strings": [
+ "ฝ"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 7
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@",
+ " ๎",
+ "๏",
+ "๛",
+ "๚"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "+"
+ ]
+ },
+ {
+ "strings": [
+ "๑"
+ ]
+ },
+ {
+ "strings": [
+ "๒"
+ ]
+ },
+ {
+ "strings": [
+ "๓"
+ ]
+ },
+ {
+ "strings": [
+ "๔"
+ ]
+ },
+ {
+ "strings": [
+ "ู"
+ ]
+ },
+ {
+ "strings": [
+ "฿"
+ ]
+ },
+ {
+ "strings": [
+ "๕"
+ ]
+ },
+ {
+ "strings": [
+ "๖"
+ ]
+ },
+ {
+ "strings": [
+ "๗"
+ ]
+ },
+ {
+ "strings": [
+ "๘"
+ ]
+ },
+ {
+ "strings": [
+ "๙"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "๐"
+ ]
+ },
+ {
+ "strings": [
+ "\""
+ ]
+ },
+ {
+ "strings": [
+ "ฎ"
+ ]
+ },
+ {
+ "strings": [
+ "ฑ"
+ ]
+ },
+ {
+ "strings": [
+ "ธ"
+ ]
+ },
+ {
+ "strings": [
+ "ํ"
+ ]
+ },
+ {
+ "strings": [
+ "๊"
+ ]
+ },
+ {
+ "strings": [
+ "ณ"
+ ]
+ },
+ {
+ "strings": [
+ "ฯ"
+ ]
+ },
+ {
+ "strings": [
+ "ญ"
+ ]
+ },
+ {
+ "strings": [
+ "ฐ"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ฤ"
+ ]
+ },
+ {
+ "strings": [
+ "ฆ"
+ ]
+ },
+ {
+ "strings": [
+ "ฏ"
+ ]
+ },
+ {
+ "strings": [
+ "โ"
+ ]
+ },
+ {
+ "strings": [
+ "ฌ"
+ ]
+ },
+ {
+ "strings": [
+ "็"
+ ]
+ },
+ {
+ "strings": [
+ "๋"
+ ]
+ },
+ {
+ "strings": [
+ "ษ"
+ ]
+ },
+ {
+ "strings": [
+ "ศ"
+ ]
+ },
+ {
+ "strings": [
+ "ซ"
+ ]
+ },
+ {
+ "strings": [
+ "."
+ ]
+ },
+ {
+ "strings": [
+ "ฅ"
+ ]
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "("
+ ]
+ },
+ {
+ "strings": [
+ ")"
+ ]
+ },
+ {
+ "strings": [
+ "ฉ"
+ ]
+ },
+ {
+ "strings": [
+ "ฮ"
+ ]
+ },
+ {
+ "strings": [
+ "ฺ"
+ ]
+ },
+ {
+ "strings": [
+ "์"
+ ]
+ },
+ {
+ "strings": [
+ "?"
+ ]
+ },
+ {
+ "strings": [
+ "ฒ"
+ ]
+ },
+ {
+ "strings": [
+ "ฬ"
+ ]
+ },
+ {
+ "strings": [
+ "ฦ"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 7
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@",
+ " ๎",
+ "๏",
+ "๛",
+ "๚"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "฿",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "th",
+ "name": "Thai"
+} \ No newline at end of file
diff --git a/data/osk-layouts/tr.json b/data/osk-layouts/tr.json
new file mode 100644
index 0000000..e32b3d9
--- /dev/null
+++ b/data/osk-layouts/tr.json
@@ -0,0 +1,974 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ü",
+ "û",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "ı",
+ "î",
+ "ï",
+ "ì",
+ "í",
+ "į",
+ "ī"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ö",
+ "ô",
+ "œ",
+ "ò",
+ "ó",
+ "õ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "â"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ş",
+ "ß",
+ "ś",
+ "š"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g",
+ "ğ"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç",
+ "ć",
+ "č"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ü",
+ "Û",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "İ",
+ "I",
+ "Î",
+ "Ï",
+ "Ì",
+ "Í",
+ "Į",
+ "Ī"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ö",
+ "Ô",
+ "Œ",
+ "Ò",
+ "Ó",
+ "Õ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Â"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "Ş",
+ "SS",
+ "Ś",
+ "Š"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G",
+ "Ğ"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç",
+ "Ć",
+ "Č"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "€",
+ "¢",
+ "£",
+ "$",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "tr",
+ "name": "Turkish"
+}
diff --git a/data/osk-layouts/ua.json b/data/osk-layouts/ua.json
new file mode 100644
index 0000000..b32b6b5
--- /dev/null
+++ b/data/osk-layouts/ua.json
@@ -0,0 +1,969 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "й"
+ ]
+ },
+ {
+ "strings": [
+ "ц"
+ ]
+ },
+ {
+ "strings": [
+ "у"
+ ]
+ },
+ {
+ "strings": [
+ "к"
+ ]
+ },
+ {
+ "strings": [
+ "е"
+ ]
+ },
+ {
+ "strings": [
+ "н"
+ ]
+ },
+ {
+ "strings": [
+ "г",
+ "ґ"
+ ]
+ },
+ {
+ "strings": [
+ "ш"
+ ]
+ },
+ {
+ "strings": [
+ "щ"
+ ]
+ },
+ {
+ "strings": [
+ "з"
+ ]
+ },
+ {
+ "strings": [
+ "х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "ф"
+ ]
+ },
+ {
+ "strings": [
+ "і",
+ "ї"
+ ]
+ },
+ {
+ "strings": [
+ "в"
+ ]
+ },
+ {
+ "strings": [
+ "а"
+ ]
+ },
+ {
+ "strings": [
+ "п"
+ ]
+ },
+ {
+ "strings": [
+ "р"
+ ]
+ },
+ {
+ "strings": [
+ "о"
+ ]
+ },
+ {
+ "strings": [
+ "л"
+ ]
+ },
+ {
+ "strings": [
+ "д"
+ ]
+ },
+ {
+ "strings": [
+ "ж"
+ ]
+ },
+ {
+ "strings": [
+ "є"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "я"
+ ]
+ },
+ {
+ "strings": [
+ "ч"
+ ]
+ },
+ {
+ "strings": [
+ "с"
+ ]
+ },
+ {
+ "strings": [
+ "м"
+ ]
+ },
+ {
+ "strings": [
+ "и"
+ ]
+ },
+ {
+ "strings": [
+ "т"
+ ]
+ },
+ {
+ "strings": [
+ "ь",
+ "ъ"
+ ]
+ },
+ {
+ "strings": [
+ "б"
+ ]
+ },
+ {
+ "strings": [
+ "ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Й"
+ ]
+ },
+ {
+ "strings": [
+ "Ц"
+ ]
+ },
+ {
+ "strings": [
+ "У"
+ ]
+ },
+ {
+ "strings": [
+ "К"
+ ]
+ },
+ {
+ "strings": [
+ "Е"
+ ]
+ },
+ {
+ "strings": [
+ "Н"
+ ]
+ },
+ {
+ "strings": [
+ "Г",
+ "Ґ"
+ ]
+ },
+ {
+ "strings": [
+ "Ш"
+ ]
+ },
+ {
+ "strings": [
+ "Щ"
+ ]
+ },
+ {
+ "strings": [
+ "З"
+ ]
+ },
+ {
+ "strings": [
+ "Х"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "Ф"
+ ]
+ },
+ {
+ "strings": [
+ "І",
+ "Ї"
+ ]
+ },
+ {
+ "strings": [
+ "В"
+ ]
+ },
+ {
+ "strings": [
+ "А"
+ ]
+ },
+ {
+ "strings": [
+ "П"
+ ]
+ },
+ {
+ "strings": [
+ "Р"
+ ]
+ },
+ {
+ "strings": [
+ "О"
+ ]
+ },
+ {
+ "strings": [
+ "Л"
+ ]
+ },
+ {
+ "strings": [
+ "Д"
+ ]
+ },
+ {
+ "strings": [
+ "Ж"
+ ]
+ },
+ {
+ "strings": [
+ "Є"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "Я"
+ ]
+ },
+ {
+ "strings": [
+ "Ч"
+ ]
+ },
+ {
+ "strings": [
+ "С"
+ ]
+ },
+ {
+ "strings": [
+ "М"
+ ]
+ },
+ {
+ "strings": [
+ "И"
+ ]
+ },
+ {
+ "strings": [
+ "Т"
+ ]
+ },
+ {
+ "strings": [
+ "Ь",
+ "Ъ"
+ ]
+ },
+ {
+ "strings": [
+ "Б"
+ ]
+ },
+ {
+ "strings": [
+ "Ю"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 6
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "₴",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "uk",
+ "name": "Ukrainian"
+} \ No newline at end of file
diff --git a/data/osk-layouts/uk.json b/data/osk-layouts/uk.json
new file mode 100644
index 0000000..91a94b2
--- /dev/null
+++ b/data/osk-layouts/uk.json
@@ -0,0 +1,984 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "û",
+ "ü",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "î",
+ "ï",
+ "í",
+ "ī",
+ "ì"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ô",
+ "ö",
+ "ò",
+ "ó",
+ "œ",
+ "ø",
+ "ō",
+ "õ"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "á",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Û",
+ "Ü",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Î",
+ "Ï",
+ "Í",
+ "Ī",
+ "Ì"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ô",
+ "Ö",
+ "Ò",
+ "Ó",
+ "Œ",
+ "Ø",
+ "Ō",
+ "Õ"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Á",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "£",
+ "¢",
+ "$",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "en-GB",
+ "name": "English Great Britain"
+}
diff --git a/data/osk-layouts/us-extended.json b/data/osk-layouts/us-extended.json
new file mode 100644
index 0000000..0370edb
--- /dev/null
+++ b/data/osk-layouts/us-extended.json
@@ -0,0 +1,1043 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "keyval": "0xff09",
+ "label": "Tab",
+ "width": 1
+ },
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "û",
+ "ü",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "î",
+ "ï",
+ "í",
+ "ī",
+ "ì"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ô",
+ "ö",
+ "ò",
+ "ó",
+ "œ",
+ "ø",
+ "ō",
+ "õ"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "iconName": "edit-clear-symbolic",
+ "action": "delete",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-caps-lock-symbolic",
+ "level": 1,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "a",
+ "à",
+ "á",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ ",",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "iconName": "go-up-symbolic",
+ "keyval": "0xff52"
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "modifier",
+ "keyval": "0xffe3",
+ "label": "Ctrl"
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2
+ },
+ {
+ "action": "modifier",
+ "keyval": "0xffe9",
+ "label": "Alt"
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 4
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "iconName": "go-previous-symbolic",
+ "keyval": "0xff51"
+ },
+ {
+ "iconName": "go-down-symbolic",
+ "keyval": "0xff54"
+ },
+ {
+ "iconName": "go-next-symbolic",
+ "keyval": "0xff53"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "keyval": "0xff09",
+ "label": "Tab",
+ "width": 1
+ },
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Û",
+ "Ü",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Î",
+ "Ï",
+ "Í",
+ "Ī",
+ "Ì"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ô",
+ "Ö",
+ "Ò",
+ "Ó",
+ "Œ",
+ "Ø",
+ "Ō",
+ "Õ"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "iconName": "edit-clear-symbolic",
+ "keyval": "0xff08",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-caps-lock-symbolic",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "A",
+ "À",
+ "Á",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ ",",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "iconName": "go-up-symbolic",
+ "keyval": "0xff52"
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "modifier",
+ "keyval": "0xffe3",
+ "label": "Ctrl"
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2
+ },
+ {
+ "action": "modifier",
+ "keyval": "0xffe9",
+ "label": "Alt"
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 4
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "iconName": "go-previous-symbolic",
+ "keyval": "0xff51"
+ },
+ {
+ "iconName": "go-down-symbolic",
+ "keyval": "0xff54"
+ },
+ {
+ "iconName": "go-next-symbolic",
+ "keyval": "0xff53"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "iconName": "edit-clear-symbolic",
+ "keyval": "0xff08",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "iconName": "edit-clear-symbolic",
+ "keyval": "0xff08",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "en",
+ "name": "English United States"
+}
diff --git a/data/osk-layouts/us.json b/data/osk-layouts/us.json
new file mode 100644
index 0000000..17c20d9
--- /dev/null
+++ b/data/osk-layouts/us.json
@@ -0,0 +1,983 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "è",
+ "é",
+ "ê",
+ "ë",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "û",
+ "ü",
+ "ù",
+ "ú",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "î",
+ "ï",
+ "í",
+ "ī",
+ "ì"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ô",
+ "ö",
+ "ò",
+ "ó",
+ "œ",
+ "ø",
+ "ō",
+ "õ"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "á",
+ "â",
+ "ä",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s",
+ "ß"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c",
+ "ç"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "È",
+ "É",
+ "Ê",
+ "Ë",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Û",
+ "Ü",
+ "Ù",
+ "Ú",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Î",
+ "Ï",
+ "Í",
+ "Ī",
+ "Ì"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ô",
+ "Ö",
+ "Ò",
+ "Ó",
+ "Œ",
+ "Ø",
+ "Ō",
+ "Õ"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Á",
+ "Â",
+ "Ä",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S",
+ "SS"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C",
+ "Ç"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "en",
+ "name": "English United States"
+}
diff --git a/data/osk-layouts/vn.json b/data/osk-layouts/vn.json
new file mode 100644
index 0000000..17c7f0e
--- /dev/null
+++ b/data/osk-layouts/vn.json
@@ -0,0 +1,1051 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "è",
+ "é",
+ "ẻ",
+ "ẽ",
+ "ẹ",
+ "ê",
+ "ề",
+ "ế",
+ "ể",
+ "ễ",
+ "ệ"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ỳ",
+ "ý",
+ "ỷ",
+ "ỹ",
+ "ỵ"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ù",
+ "ú",
+ "ủ",
+ "ũ",
+ "ụ",
+ "ư",
+ "ừ",
+ "ứ",
+ "ử",
+ "ữ",
+ "ự"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "ì",
+ "í",
+ "ỉ",
+ "ĩ",
+ "ị"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ò",
+ "ó",
+ "ỏ",
+ "õ",
+ "ọ",
+ "ô",
+ "ồ",
+ "ố",
+ "ổ",
+ "ỗ",
+ "ộ",
+ "ơ",
+ "ờ",
+ "ớ",
+ "ở",
+ "ỡ",
+ "ợ"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "à",
+ "á",
+ "ả",
+ "ã",
+ "ạ",
+ "ă",
+ "ằ",
+ "ắ",
+ "ẳ",
+ "ẵ",
+ "ặ",
+ "â",
+ "ầ",
+ "ấ",
+ "ẩ",
+ "ẫ",
+ "ậ"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d",
+ "đ"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "È",
+ "É",
+ "Ẻ",
+ "Ẽ",
+ "Ẹ",
+ "Ê",
+ "Ề",
+ "Ế",
+ "Ể",
+ "Ễ",
+ "Ệ"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ỳ",
+ "Ý",
+ "Ỷ",
+ "Ỹ",
+ "Ỵ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ù",
+ "Ú",
+ "Ủ",
+ "Ũ",
+ "Ụ",
+ "Ư",
+ "Ừ",
+ "Ứ",
+ "Ử",
+ "Ữ",
+ "Ự"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Ì",
+ "Í",
+ "Ỉ",
+ "Ĩ",
+ "Ị"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ò",
+ "Ó",
+ "Ỏ",
+ "Õ",
+ "Ọ",
+ "Ô",
+ "Ồ",
+ "Ố",
+ "Ổ",
+ "Ỗ",
+ "Ộ",
+ "Ơ",
+ "Ờ",
+ "Ớ",
+ "Ở",
+ "Ỡ",
+ "Ợ"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "À",
+ "Á",
+ "Ả",
+ "Ã",
+ "Ạ",
+ "Ă",
+ "Ằ",
+ "Ắ",
+ "Ẳ",
+ "Ẵ",
+ "Ặ",
+ "Â",
+ "Ầ",
+ "Ấ",
+ "Ẩ",
+ "Ẫ",
+ "Ậ"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D",
+ "Đ"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "₫",
+ "$",
+ "¢",
+ "€",
+ "£",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "vi",
+ "name": "Vietnamese"
+}
diff --git a/data/osk-layouts/za.json b/data/osk-layouts/za.json
new file mode 100644
index 0000000..0e8dd96
--- /dev/null
+++ b/data/osk-layouts/za.json
@@ -0,0 +1,997 @@
+{
+ "levels": [
+ {
+ "level": "",
+ "mode": "default",
+ "rows": [
+ [
+ {
+ "strings": [
+ "q"
+ ]
+ },
+ {
+ "strings": [
+ "w"
+ ]
+ },
+ {
+ "strings": [
+ "e",
+ "é",
+ "è",
+ "ê",
+ "ë",
+ "ę",
+ "ė",
+ "ē"
+ ]
+ },
+ {
+ "strings": [
+ "r"
+ ]
+ },
+ {
+ "strings": [
+ "t"
+ ]
+ },
+ {
+ "strings": [
+ "y",
+ "ý",
+ "ŷ",
+ "ÿ",
+ "ij"
+ ]
+ },
+ {
+ "strings": [
+ "u",
+ "ú",
+ "û",
+ "ü",
+ "ù",
+ "ū"
+ ]
+ },
+ {
+ "strings": [
+ "i",
+ "í",
+ "ì",
+ "ï",
+ "î",
+ "į",
+ "ī",
+ "ij"
+ ]
+ },
+ {
+ "strings": [
+ "o",
+ "ó",
+ "ô",
+ "ö",
+ "ò",
+ "õ",
+ "œ",
+ "ø",
+ "ō"
+ ]
+ },
+ {
+ "strings": [
+ "p"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "a",
+ "á",
+ "â",
+ "ä",
+ "à",
+ "æ",
+ "ã",
+ "å",
+ "ā"
+ ]
+ },
+ {
+ "strings": [
+ "s"
+ ]
+ },
+ {
+ "strings": [
+ "d"
+ ]
+ },
+ {
+ "strings": [
+ "f"
+ ]
+ },
+ {
+ "strings": [
+ "g"
+ ]
+ },
+ {
+ "strings": [
+ "h"
+ ]
+ },
+ {
+ "strings": [
+ "j"
+ ]
+ },
+ {
+ "strings": [
+ "k"
+ ]
+ },
+ {
+ "strings": [
+ "l"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1
+ },
+ {
+ "strings": [
+ "z"
+ ]
+ },
+ {
+ "strings": [
+ "x"
+ ]
+ },
+ {
+ "strings": [
+ "c"
+ ]
+ },
+ {
+ "strings": [
+ "v"
+ ]
+ },
+ {
+ "strings": [
+ "b"
+ ]
+ },
+ {
+ "strings": [
+ "n",
+ "ñ",
+ "ń"
+ ]
+ },
+ {
+ "strings": [
+ "m"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 1,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "shift",
+ "mode": "latched",
+ "rows": [
+ [
+ {
+ "strings": [
+ "Q"
+ ]
+ },
+ {
+ "strings": [
+ "W"
+ ]
+ },
+ {
+ "strings": [
+ "E",
+ "É",
+ "È",
+ "Ê",
+ "Ë",
+ "Ę",
+ "Ė",
+ "Ē"
+ ]
+ },
+ {
+ "strings": [
+ "R"
+ ]
+ },
+ {
+ "strings": [
+ "T"
+ ]
+ },
+ {
+ "strings": [
+ "Y",
+ "Ý",
+ "Ŷ",
+ "Ÿ",
+ "IJ"
+ ]
+ },
+ {
+ "strings": [
+ "U",
+ "Ú",
+ "Û",
+ "Ü",
+ "Ù",
+ "Ū"
+ ]
+ },
+ {
+ "strings": [
+ "I",
+ "Í",
+ "Ì",
+ "Ï",
+ "Î",
+ "Į",
+ "Ī",
+ "IJ"
+ ]
+ },
+ {
+ "strings": [
+ "O",
+ "Ó",
+ "Ô",
+ "Ö",
+ "Ò",
+ "Õ",
+ "Œ",
+ "Ø",
+ "Ō"
+ ]
+ },
+ {
+ "strings": [
+ "P"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "A",
+ "Á",
+ "Â",
+ "Ä",
+ "À",
+ "Æ",
+ "Ã",
+ "Å",
+ "Ā"
+ ]
+ },
+ {
+ "strings": [
+ "S"
+ ]
+ },
+ {
+ "strings": [
+ "D"
+ ]
+ },
+ {
+ "strings": [
+ "F"
+ ]
+ },
+ {
+ "strings": [
+ "G"
+ ]
+ },
+ {
+ "strings": [
+ "H"
+ ]
+ },
+ {
+ "strings": [
+ "J"
+ ]
+ },
+ {
+ "strings": [
+ "K"
+ ]
+ },
+ {
+ "strings": [
+ "L"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0
+ },
+ {
+ "strings": [
+ "Z"
+ ]
+ },
+ {
+ "strings": [
+ "X"
+ ]
+ },
+ {
+ "strings": [
+ "C"
+ ]
+ },
+ {
+ "strings": [
+ "V"
+ ]
+ },
+ {
+ "strings": [
+ "B"
+ ]
+ },
+ {
+ "strings": [
+ "N",
+ "Ñ",
+ "Ń"
+ ]
+ },
+ {
+ "strings": [
+ "M"
+ ]
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "#",
+ "!",
+ ",",
+ "?",
+ "-",
+ ":",
+ "'",
+ "@"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "iconName": "keyboard-shift-symbolic",
+ "level": 0,
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic",
+ "width": 1.5
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 5
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic",
+ "width": 1.5
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic",
+ "width": 2
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "1",
+ "¹",
+ "½",
+ "⅓",
+ "¼",
+ "⅛"
+ ]
+ },
+ {
+ "strings": [
+ "2",
+ "²",
+ "⅔"
+ ]
+ },
+ {
+ "strings": [
+ "3",
+ "³",
+ "¾",
+ "⅜"
+ ]
+ },
+ {
+ "strings": [
+ "4",
+ "⁴"
+ ]
+ },
+ {
+ "strings": [
+ "5",
+ "⅝"
+ ]
+ },
+ {
+ "strings": [
+ "6"
+ ]
+ },
+ {
+ "strings": [
+ "7",
+ "⅞"
+ ]
+ },
+ {
+ "strings": [
+ "8"
+ ]
+ },
+ {
+ "strings": [
+ "9"
+ ]
+ },
+ {
+ "strings": [
+ "0",
+ "ⁿ",
+ "∅"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "@"
+ ]
+ },
+ {
+ "strings": [
+ "#"
+ ]
+ },
+ {
+ "strings": [
+ "$",
+ "¢",
+ "£",
+ "€",
+ "¥",
+ "₱"
+ ]
+ },
+ {
+ "strings": [
+ "%",
+ "‰"
+ ]
+ },
+ {
+ "strings": [
+ "&"
+ ]
+ },
+ {
+ "strings": [
+ "-",
+ "_",
+ "–",
+ "—",
+ "·"
+ ]
+ },
+ {
+ "strings": [
+ "+",
+ "±"
+ ]
+ },
+ {
+ "strings": [
+ "(",
+ "<",
+ "{",
+ "["
+ ]
+ },
+ {
+ "strings": [
+ ")",
+ ">",
+ "}",
+ "]"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "*",
+ "†",
+ "‡",
+ "★"
+ ]
+ },
+ {
+ "strings": [
+ "\"",
+ "“",
+ "”",
+ "«",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ "'",
+ "‘",
+ "’",
+ "‹",
+ "›"
+ ]
+ },
+ {
+ "strings": [
+ ":"
+ ]
+ },
+ {
+ "strings": [
+ ";"
+ ]
+ },
+ {
+ "strings": [
+ "!",
+ "¡"
+ ]
+ },
+ {
+ "strings": [
+ "?",
+ "¿"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "=/<",
+ "level": 3,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "_"
+ ]
+ },
+ {
+ "strings": [
+ "/"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ },
+ {
+ "level": "opt+shift",
+ "mode": "locked",
+ "rows": [
+ [
+ {
+ "strings": [
+ "~"
+ ]
+ },
+ {
+ "strings": [
+ "`"
+ ]
+ },
+ {
+ "strings": [
+ "|"
+ ]
+ },
+ {
+ "strings": [
+ "•",
+ "♪",
+ "♥",
+ "♠",
+ "♦",
+ "♣"
+ ]
+ },
+ {
+ "strings": [
+ "√"
+ ]
+ },
+ {
+ "strings": [
+ "Π",
+ "π"
+ ]
+ },
+ {
+ "strings": [
+ "÷"
+ ]
+ },
+ {
+ "strings": [
+ "×"
+ ]
+ },
+ {
+ "strings": [
+ "¶",
+ "§"
+ ]
+ },
+ {
+ "strings": [
+ "∆"
+ ]
+ },
+ {
+ "action": "delete",
+ "iconName": "edit-clear-symbolic",
+ "width": 1.5
+ }
+ ],
+ [
+ {
+ "strings": [
+ "£"
+ ]
+ },
+ {
+ "strings": [
+ "¢"
+ ]
+ },
+ {
+ "strings": [
+ "€"
+ ]
+ },
+ {
+ "strings": [
+ "¥"
+ ]
+ },
+ {
+ "strings": [
+ "^",
+ "↑",
+ "↓",
+ "←",
+ "→"
+ ]
+ },
+ {
+ "strings": [
+ "°",
+ "′",
+ "″"
+ ]
+ },
+ {
+ "strings": [
+ "=",
+ "≠",
+ "≈",
+ "∞"
+ ]
+ },
+ {
+ "strings": [
+ "{"
+ ]
+ },
+ {
+ "strings": [
+ "}"
+ ]
+ },
+ {
+ "iconName": "keyboard-enter-symbolic",
+ "keyval": "0xff0d",
+ "width": 2
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "\\"
+ ]
+ },
+ {
+ "strings": [
+ "©"
+ ]
+ },
+ {
+ "strings": [
+ "®"
+ ]
+ },
+ {
+ "strings": [
+ "™"
+ ]
+ },
+ {
+ "strings": [
+ "℅"
+ ]
+ },
+ {
+ "strings": [
+ "["
+ ]
+ },
+ {
+ "strings": [
+ "]"
+ ]
+ },
+ {
+ "action": "levelSwitch",
+ "label": "?123",
+ "level": 2,
+ "width": 3
+ }
+ ],
+ [
+ {
+ "action": "levelSwitch",
+ "label": "ABC",
+ "level": 0,
+ "width": 1.5
+ },
+ {
+ "strings": [
+ "<",
+ "‹",
+ "≤",
+ "«"
+ ]
+ },
+ {
+ "strings": [
+ ">",
+ "›",
+ "≥",
+ "»"
+ ]
+ },
+ {
+ "strings": [
+ " "
+ ],
+ "width": 3
+ },
+ {
+ "strings": [
+ ","
+ ]
+ },
+ {
+ "strings": [
+ ".",
+ "…"
+ ]
+ },
+ {
+ "action": "emoji",
+ "iconName": "face-smile-symbolic"
+ },
+ {
+ "action": "languageMenu",
+ "iconName": "keyboard-layout-symbolic"
+ },
+ {
+ "action": "hide",
+ "iconName": "keyboard-hide-symbolic"
+ }
+ ]
+ ]
+ }
+ ],
+ "locale": "af",
+ "name": "Afrikaans"
+}
diff --git a/data/perf-background.xml.in b/data/perf-background.xml.in
new file mode 100644
index 0000000..752a552
--- /dev/null
+++ b/data/perf-background.xml.in
@@ -0,0 +1,31 @@
+<!-- With an animated background, performance will differ depending on whether
+ one layer or two layers are being blended together. This messes up our
+ benchmarks. We could just benchmark a single image, but since blended
+ images are present for much of the day with the GNOME default background,
+ we want to make sure that also performs well; for that reason we ship
+ an "animated" background that animates super-slowly to use during
+ performance tests; it will be in the blended state until 2030. -->
+<background>
+ <starttime>
+ <year>1990</year>
+ <month>1</month>
+ <day>1</day>
+ <hour>0</hour>
+ <minute>00</minute>
+ <second>00</second>
+ </starttime>
+
+<!-- One transition that takes 40 years -->
+<transition type="overlay">
+<duration>1261440000.0</duration>
+<from>@datadir@/backgrounds/gnome/adwaita-morning.jpg</from>
+<to>@datadir@/backgrounds/gnome/adwaita-day.jpg</to>
+</transition>
+
+<!-- A single slide doesn't work, so another slide for 1 minute after 40 years -->
+<static>
+<duration>60</duration>
+<file>/usr/share/backgrounds/gnome/Sandstone.jpg</file>
+</static>
+
+</background>
diff --git a/data/theme/README.md b/data/theme/README.md
new file mode 100644
index 0000000..192dc65
--- /dev/null
+++ b/data/theme/README.md
@@ -0,0 +1,32 @@
+## Summary
+
+Do not edit the CSS directly, edit the source SCSS files and the CSS files
+will be generated automatically when building with meson + ninja and left
+inside the build directory to be incorporated into the gresource XML (you'll
+need to have sassc installed).
+
+## How to tweak the theme
+
+Adwaita is a complex theme, so to keep it maintainable it's written and
+processed in SASS, the generated CSS is then transformed into a gresource
+file during gtk build and used at runtime in a non-legible or editable form.
+
+It is very likely your change will happen in the [_common.scss][common] file.
+That's where all the widget selectors are defined. Here's a rundown of
+the "supporting" stylesheets, that are unlikely to be the right place
+for a drive by stylesheet fix:
+
+| File | Description |
+| ------------------------ | ----------------- |
+| [_colors.scss][colors] | global color definitions. We keep the number of defined colors to a necessary minimum, most colors are derived from a handful of basics. It is an exact copy of the gtk+ counterpart. Light theme is used for the classic theme and dark is for GNOME3 shell default. |
+| [_drawing.scss][drawing] | drawing helper mixings/functions to allow easier definition of widget drawing under specific context. This is why Adwaita isn't 15000 LOC. |
+| [_common.scss][common] | actual definitions of style for each widget. This is where you are likely to add/remove your changes. |
+
+You can read about SASS on its [web page][sass-web]. Once you make your
+changes to the [_common.scss][common] file, you can run ninja to generate the
+final CSS files.
+
+[common]: data/theme/gnome-shell-sass/_common.scss
+[colors]: data/theme/gnome-shell-sass/_colors.scss
+[drawing]: data/theme/gnome-shell-sass/_drawing.scss
+[sass-web]: http://sass-lang.com/documentation/
diff --git a/data/theme/calendar-today-light.svg b/data/theme/calendar-today-light.svg
new file mode 100644
index 0000000..b63f6e0
--- /dev/null
+++ b/data/theme/calendar-today-light.svg
@@ -0,0 +1 @@
+<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg"><circle style="color:#000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000;solid-opacity:1;fill:#242424;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" cx="16" cy="28" r="2"/></svg> \ No newline at end of file
diff --git a/data/theme/calendar-today.svg b/data/theme/calendar-today.svg
new file mode 100644
index 0000000..daf86ec
--- /dev/null
+++ b/data/theme/calendar-today.svg
@@ -0,0 +1 @@
+<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg"><circle style="color:#000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000;solid-opacity:1;fill:#fff;fill-opacity:.858277;fill-rule:nonzero;stroke:none;stroke-width:.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" cx="16" cy="28" r="2"/></svg> \ No newline at end of file
diff --git a/data/theme/checkbox-focused.svg b/data/theme/checkbox-focused.svg
new file mode 100644
index 0000000..01eb733
--- /dev/null
+++ b/data/theme/checkbox-focused.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="b"><stop offset="0" style="stop-color:#515151;stop-opacity:1"/><stop offset="1" style="stop-color:#292929;stop-opacity:1"/></linearGradient><linearGradient id="a"><stop style="stop-color:#0b2e52;stop-opacity:1" offset="0"/><stop style="stop-color:#1862af;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="c"><stop style="stop-color:#333;stop-opacity:1" offset="0"/><stop style="stop-color:#292929;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="d"><stop style="stop-color:#16191a;stop-opacity:1" offset="0"/><stop style="stop-color:#2b3133;stop-opacity:1" offset="1"/></linearGradient></defs><g transform="translate(-342.5 -521.362)"><rect rx="3" y="524.362" x="345.5" height="18" width="18" style="color:#000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#3584e4;stroke-width:6;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:.34999999;marker:none;enable-background:accumulate" ry="3"/><rect rx="3" y="524.362" x="345.5" height="18" width="18" style="color:#000;display:inline;overflow:visible;visibility:visible;fill:#3584e4;fill-opacity:1;stroke:#3584e4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" ry="3"/><path style="color:#000;fill:#fff;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none" d="M361.998 527.733a1.5 1.5 0 0 0-2.117.127l-6.928 7.824-2.928-2.882a1.5 1.5 0 0 0-2.12.015 1.5 1.5 0 0 0 .015 2.121l4.055 3.993a1.5 1.5 0 0 0 2.175-.075l7.975-9.006a1.5 1.5 0 0 0-.127-2.117z"/></g></svg> \ No newline at end of file
diff --git a/data/theme/checkbox-off-focused-light.svg b/data/theme/checkbox-off-focused-light.svg
new file mode 100644
index 0000000..4767377
--- /dev/null
+++ b/data/theme/checkbox-off-focused-light.svg
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="24"
+ height="24"
+ id="svg3199"
+ version="1.1"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="checkbox-off-focused-dark.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs3201">
+ <linearGradient
+ id="linearGradient15404"
+ inkscape:collect="always">
+ <stop
+ id="stop15406"
+ offset="0"
+ style="stop-color:#515151;stop-opacity:1" />
+ <stop
+ id="stop15408"
+ offset="1"
+ style="stop-color:#292929;stop-opacity:1" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 528.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 528.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 352.78739 : 1"
+ id="perspective3207" />
+ <inkscape:perspective
+ id="perspective3187"
+ inkscape:persp3d-origin="0.5 : 2.3333333 : 1"
+ inkscape:vp_z="1 : 2.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 2.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5872-5-1"
+ id="linearGradient5891-0-4"
+ gradientUnits="userSpaceOnUse"
+ x1="205.84143"
+ y1="246.7094"
+ x2="206.74803"
+ y2="231.24142" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5872-5-1">
+ <stop
+ style="stop-color:#0b2e52;stop-opacity:1"
+ offset="0"
+ id="stop5874-4-4" />
+ <stop
+ style="stop-color:#1862af;stop-opacity:1"
+ offset="1"
+ id="stop5876-0-5" />
+ </linearGradient>
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect5837-4-6"
+ is_visible="true"
+ lpeversion="0" />
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect14768"
+ is_visible="true"
+ lpeversion="0" />
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect5884-4-7"
+ is_visible="true"
+ lpeversion="0" />
+ <linearGradient
+ y2="-388.72955"
+ x2="-93.031357"
+ y1="-396.34738"
+ x1="-93.031357"
+ gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient14219"
+ xlink:href="#linearGradient15404"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient10013-4-63-6">
+ <stop
+ style="stop-color:#333333;stop-opacity:1;"
+ offset="0"
+ id="stop10015-2-76-1" />
+ <stop
+ style="stop-color:#292929;stop-opacity:1"
+ offset="1"
+ id="stop10017-46-15-8" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient10597-5">
+ <stop
+ style="stop-color:#16191a;stop-opacity:1;"
+ offset="0"
+ id="stop10599-2" />
+ <stop
+ style="stop-color:#2b3133;stop-opacity:1"
+ offset="1"
+ id="stop10601-5" />
+ </linearGradient>
+ <linearGradient
+ y2="-322.16354"
+ x2="921.22498"
+ y1="-330.05121"
+ x1="921.32812"
+ gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient15374"
+ xlink:href="#linearGradient10013-4-63-6"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-1199.9852,216.38048)"
+ y2="-227.07961"
+ x2="1203.9177"
+ y1="-217.56708"
+ x1="1203.9177"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient15376"
+ xlink:href="#linearGradient10597-5"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient5581-5-2-4-6-8-7-35-8">
+ <stop
+ id="stop5583-0-92-8-0-7-6-5-1"
+ offset="0"
+ style="stop-color:#454c4c;stop-opacity:1;" />
+ <stop
+ style="stop-color:#393f3f;stop-opacity:1;"
+ offset="0.40000001"
+ id="stop5585-4-7-2-7-9-9-92-0" />
+ <stop
+ id="stop5587-6-7-2-0-3-1-21-5"
+ offset="1"
+ style="stop-color:#2d3232;stop-opacity:1;" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#a2a2a2"
+ bordercolor="#2d2d2d"
+ borderopacity="1"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.313708"
+ inkscape:cx="12.374369"
+ inkscape:cy="8.8388348"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ borderlayer="true"
+ inkscape:showpageshadow="false"
+ inkscape:snap-nodes="false"
+ inkscape:snap-bbox="true"
+ showborder="true"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid14843"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata3204">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-342.5,-521.36218)">
+ <rect
+ rx="3"
+ y="524.36218"
+ x="345.5"
+ height="18"
+ width="18"
+ id="rect11803"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#282828;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.15000001;marker:none;enable-background:accumulate"
+ ry="3" />
+ <rect
+ rx="5"
+ y="522.36218"
+ x="343.5"
+ height="22"
+ width="22"
+ id="rect2437"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#3584e4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.35;marker:none;enable-background:accumulate"
+ ry="5" />
+ </g>
+</svg>
diff --git a/data/theme/checkbox-off-focused.svg b/data/theme/checkbox-off-focused.svg
new file mode 100644
index 0000000..d5a042e
--- /dev/null
+++ b/data/theme/checkbox-off-focused.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="b"><stop offset="0" style="stop-color:#515151;stop-opacity:1"/><stop offset="1" style="stop-color:#292929;stop-opacity:1"/></linearGradient><linearGradient id="a"><stop style="stop-color:#0b2e52;stop-opacity:1" offset="0"/><stop style="stop-color:#1862af;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="c"><stop style="stop-color:#333;stop-opacity:1" offset="0"/><stop style="stop-color:#292929;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="d"><stop style="stop-color:#16191a;stop-opacity:1" offset="0"/><stop style="stop-color:#2b3133;stop-opacity:1" offset="1"/></linearGradient></defs><g transform="translate(-342.5 -521.362)"><rect rx="3" y="524.362" x="345.5" height="18" width="18" style="color:#000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#fff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:.15000001;marker:none;enable-background:accumulate" ry="3"/><rect rx="5" y="522.362" x="343.5" height="22" width="22" style="color:#000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#3584e4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:.35;marker:none;enable-background:accumulate" ry="5"/></g></svg> \ No newline at end of file
diff --git a/data/theme/checkbox-off-light.svg b/data/theme/checkbox-off-light.svg
new file mode 100644
index 0000000..5d37c85
--- /dev/null
+++ b/data/theme/checkbox-off-light.svg
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="24"
+ height="24"
+ id="svg3199"
+ version="1.1"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="checkbox-off-dark.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs3201">
+ <linearGradient
+ id="linearGradient15404"
+ inkscape:collect="always">
+ <stop
+ id="stop15406"
+ offset="0"
+ style="stop-color:#515151;stop-opacity:1" />
+ <stop
+ id="stop15408"
+ offset="1"
+ style="stop-color:#292929;stop-opacity:1" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 528.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 528.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 352.78739 : 1"
+ id="perspective3207" />
+ <inkscape:perspective
+ id="perspective3187"
+ inkscape:persp3d-origin="0.5 : 2.3333333 : 1"
+ inkscape:vp_z="1 : 2.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 2.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5872-5-1"
+ id="linearGradient5891-0-4"
+ gradientUnits="userSpaceOnUse"
+ x1="205.84143"
+ y1="246.7094"
+ x2="206.74803"
+ y2="231.24142" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5872-5-1">
+ <stop
+ style="stop-color:#0b2e52;stop-opacity:1"
+ offset="0"
+ id="stop5874-4-4" />
+ <stop
+ style="stop-color:#1862af;stop-opacity:1"
+ offset="1"
+ id="stop5876-0-5" />
+ </linearGradient>
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect5837-4-6"
+ is_visible="true"
+ lpeversion="0" />
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect14768"
+ is_visible="true"
+ lpeversion="0" />
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect5884-4-7"
+ is_visible="true"
+ lpeversion="0" />
+ <linearGradient
+ y2="-388.72955"
+ x2="-93.031357"
+ y1="-396.34738"
+ x1="-93.031357"
+ gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient14219"
+ xlink:href="#linearGradient15404"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient10013-4-63-6">
+ <stop
+ style="stop-color:#333333;stop-opacity:1;"
+ offset="0"
+ id="stop10015-2-76-1" />
+ <stop
+ style="stop-color:#292929;stop-opacity:1"
+ offset="1"
+ id="stop10017-46-15-8" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient10597-5">
+ <stop
+ style="stop-color:#16191a;stop-opacity:1;"
+ offset="0"
+ id="stop10599-2" />
+ <stop
+ style="stop-color:#2b3133;stop-opacity:1"
+ offset="1"
+ id="stop10601-5" />
+ </linearGradient>
+ <linearGradient
+ y2="-322.16354"
+ x2="921.22498"
+ y1="-330.05121"
+ x1="921.32812"
+ gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient15374"
+ xlink:href="#linearGradient10013-4-63-6"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-1199.9852,216.38048)"
+ y2="-227.07961"
+ x2="1203.9177"
+ y1="-217.56708"
+ x1="1203.9177"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient15376"
+ xlink:href="#linearGradient10597-5"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient5581-5-2-4-6-8-7-35-8">
+ <stop
+ id="stop5583-0-92-8-0-7-6-5-1"
+ offset="0"
+ style="stop-color:#454c4c;stop-opacity:1;" />
+ <stop
+ style="stop-color:#393f3f;stop-opacity:1;"
+ offset="0.40000001"
+ id="stop5585-4-7-2-7-9-9-92-0" />
+ <stop
+ id="stop5587-6-7-2-0-3-1-21-5"
+ offset="1"
+ style="stop-color:#2d3232;stop-opacity:1;" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#a2a2a2"
+ bordercolor="#2d2d2d"
+ borderopacity="1"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.313708"
+ inkscape:cx="12.374369"
+ inkscape:cy="8.8388348"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ borderlayer="true"
+ inkscape:showpageshadow="false"
+ inkscape:snap-nodes="false"
+ inkscape:snap-bbox="true"
+ showborder="true"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid14843"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata3204">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-342.5,-521.36218)">
+ <rect
+ rx="3"
+ y="524.36218"
+ x="345.5"
+ height="18"
+ width="18"
+ id="rect11803"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#282828;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.15000001;marker:none;enable-background:accumulate"
+ ry="3" />
+ </g>
+</svg>
diff --git a/data/theme/checkbox-off.svg b/data/theme/checkbox-off.svg
new file mode 100644
index 0000000..50eece1
--- /dev/null
+++ b/data/theme/checkbox-off.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="b"><stop offset="0" style="stop-color:#515151;stop-opacity:1"/><stop offset="1" style="stop-color:#292929;stop-opacity:1"/></linearGradient><linearGradient id="a"><stop style="stop-color:#0b2e52;stop-opacity:1" offset="0"/><stop style="stop-color:#1862af;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="c"><stop style="stop-color:#333;stop-opacity:1" offset="0"/><stop style="stop-color:#292929;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="d"><stop style="stop-color:#16191a;stop-opacity:1" offset="0"/><stop style="stop-color:#2b3133;stop-opacity:1" offset="1"/></linearGradient></defs><rect rx="3" y="524.362" x="345.5" height="18" width="18" style="color:#000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#fff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:.15000001;marker:none;enable-background:accumulate" ry="3" transform="translate(-342.5 -521.362)"/></svg> \ No newline at end of file
diff --git a/data/theme/checkbox.svg b/data/theme/checkbox.svg
new file mode 100644
index 0000000..da385b6
--- /dev/null
+++ b/data/theme/checkbox.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="b"><stop offset="0" style="stop-color:#515151;stop-opacity:1"/><stop offset="1" style="stop-color:#292929;stop-opacity:1"/></linearGradient><linearGradient id="a"><stop style="stop-color:#0b2e52;stop-opacity:1" offset="0"/><stop style="stop-color:#1862af;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="c"><stop style="stop-color:#333;stop-opacity:1" offset="0"/><stop style="stop-color:#292929;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="d"><stop style="stop-color:#16191a;stop-opacity:1" offset="0"/><stop style="stop-color:#2b3133;stop-opacity:1" offset="1"/></linearGradient></defs><g transform="translate(-342.5 -521.362)"><rect rx="3" y="524.362" x="345.5" height="18" width="18" style="color:#000;display:inline;overflow:visible;visibility:visible;fill:#3584e4;fill-opacity:1;stroke:#3584e4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" ry="3"/><path style="color:#000;fill:#fff;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none" d="M361.998 527.733a1.5 1.5 0 0 0-2.117.127l-6.928 7.824-2.928-2.882a1.5 1.5 0 0 0-2.12.015 1.5 1.5 0 0 0 .015 2.121l4.055 3.993a1.5 1.5 0 0 0 2.175-.075l7.975-9.006a1.5 1.5 0 0 0-.127-2.117z"/></g></svg> \ No newline at end of file
diff --git a/data/theme/dash-placeholder.svg b/data/theme/dash-placeholder.svg
new file mode 100644
index 0000000..cbae148
--- /dev/null
+++ b/data/theme/dash-placeholder.svg
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="76"
+ height="27"
+ id="svg11252"
+ version="1.1">
+ <defs
+ id="defs11254">
+ <radialGradient
+ xlink:href="#linearGradient39563-4-2"
+ id="radialGradient68155-2-3"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,0.3486842,0,317.8421)"
+ cx="49"
+ cy="488"
+ fx="49"
+ fy="488"
+ r="38" />
+ <linearGradient
+ id="linearGradient39563-4-2">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop39565-1-4" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop39567-7-9" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient39573-6-1"
+ id="radialGradient68157-0-8"
+ gradientUnits="userSpaceOnUse"
+ cx="50.5"
+ cy="487.5"
+ fx="50.5"
+ fy="487.5"
+ r="10.5" />
+ <linearGradient
+ id="linearGradient39573-6-1">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop39575-5-6" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop39577-1-2" />
+ </linearGradient>
+ </defs>
+ <g
+ id="layer1"
+ transform="translate(-337,-518.86218)">
+ <g
+ id="g99967"
+ style="display:inline"
+ transform="translate(326,44.862171)">
+ <rect
+ style="opacity:0.49375;color:#000000;fill:url(#radialGradient68155-2-3);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect99969"
+ width="76"
+ height="2"
+ x="11"
+ y="487"
+ rx="0"
+ ry="0" />
+ <path
+ style="opacity:0.43125;color:#000000;fill:url(#radialGradient68157-0-8);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path99971"
+ d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
+ transform="matrix(1.2857143,0,0,1.2857143,-14.428572,-139.28571)" />
+ <path
+ transform="matrix(0.43589747,0,0,0.43589747,28.487179,275)"
+ d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
+ id="path99973"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ </g>
+</svg>
diff --git a/data/theme/gnome-shell-high-contrast.css b/data/theme/gnome-shell-high-contrast.css
new file mode 100644
index 0000000..b32f6eb
--- /dev/null
+++ b/data/theme/gnome-shell-high-contrast.css
@@ -0,0 +1,2883 @@
+/* This stylesheet is generated, DO NOT EDIT */
+/* Copyright 2009, 2015 Red Hat, Inc.
+ *
+ * Portions adapted from Mx's data/style/default.css
+ * Copyright 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/* Global Values */
+stage {
+ font-size: 11pt;
+ color: white; }
+
+/* Common Stylings */
+.screenshot-ui-panel, .workspace-switcher, .switcher-list, .resize-popup, .osd-monitor-label, .osd-window {
+ color: white;
+ background-color: #000;
+ border: 1px solid #000;
+ border-radius: 999px;
+ padding: 12px; }
+
+.workspace-thumbnails .workspace-thumbnail {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1); }
+
+.app-well-app.app-folder .overview-icon, .app-folder.grid-search-result .overview-icon, .app-well-app .overview-icon, .grid-search-result .overview-icon, .dash-item-container .show-apps .overview-icon, .list-search-result, .search-provider-icon, .switcher-list .item-box {
+ border-radius: 16px;
+ padding: 6px;
+ spacing: 6px;
+ border: 2px solid transparent;
+ transition-duration: 200ms;
+ text-align: center; }
+
+.modal-dialog {
+ color: white;
+ background-color: #000;
+ border-radius: 11px;
+ border: 1px solid #2b2b2b; }
+
+#LookingGlassDialog > #Toolbar .lg-toolbar-button, .app-folder-dialog .folder-name-container .edit-folder-button, .button, .icon-button {
+ border-radius: 6px;
+ border-style: solid;
+ border-width: 1px;
+ font-weight: bold;
+ padding: 3px 24px;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #1a1a1a;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:focus, .app-folder-dialog .folder-name-container .edit-folder-button:focus, .button:focus, .icon-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #1d252e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:focus:hover, .app-folder-dialog .folder-name-container .edit-folder-button:focus:hover, .button:focus:hover, .icon-button:focus:hover {
+ background-color: #242b35;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:focus:active, .app-folder-dialog .folder-name-container .edit-folder-button:focus:active, .button:focus:active, .icon-button:focus:active {
+ background-color: #2a323c;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:hover, .app-folder-dialog .folder-name-container .edit-folder-button:hover, .button:hover, .icon-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #222222;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(144, 144, 144, 0.28); }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:insensitive, .app-folder-dialog .folder-name-container .edit-folder-button:insensitive, .button:insensitive, .icon-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0); }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:active, .app-folder-dialog .folder-name-container .edit-folder-button:active, .button:active, .icon-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #292929;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(148, 148, 148, 0.28); }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:checked, .app-folder-dialog .folder-name-container .edit-folder-button:checked, .button:checked, .icon-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #313131;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(152, 152, 152, 0.28); }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:checked:hover, .app-folder-dialog .folder-name-container .edit-folder-button:checked:hover, .button:checked:hover, .icon-button:checked:hover {
+ background-color: #393939; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:checked:active, .app-folder-dialog .folder-name-container .edit-folder-button:checked:active, .button:checked:active, .icon-button:checked:active {
+ background-color: #404040; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button, .app-folder-dialog .folder-name-container .flat.edit-folder-button, .flat.button, .flat.icon-button {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #1a1a1a;
+ background-color: transparent;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:focus, .app-folder-dialog .folder-name-container .flat.edit-folder-button:focus, .flat.button:focus, .flat.icon-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #1d252e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:focus:hover, .app-folder-dialog .folder-name-container .flat.edit-folder-button:focus:hover, .flat.button:focus:hover, .flat.icon-button:focus:hover {
+ background-color: #242b35;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:focus:active, .app-folder-dialog .folder-name-container .flat.edit-folder-button:focus:active, .flat.button:focus:active, .flat.icon-button:focus:active {
+ background-color: #2a323c;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:hover, .app-folder-dialog .folder-name-container .flat.edit-folder-button:hover, .flat.button:hover, .flat.icon-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #222222;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(144, 144, 144, 0.28); }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:insensitive, .app-folder-dialog .folder-name-container .flat.edit-folder-button:insensitive, .flat.button:insensitive, .flat.icon-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0); }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:active, .app-folder-dialog .folder-name-container .flat.edit-folder-button:active, .flat.button:active, .flat.icon-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #292929;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(148, 148, 148, 0.28); }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:checked, .app-folder-dialog .folder-name-container .flat.edit-folder-button:checked, .flat.button:checked, .flat.icon-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #313131;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(152, 152, 152, 0.28); }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:checked:hover, .app-folder-dialog .folder-name-container .flat.edit-folder-button:checked:hover, .flat.button:checked:hover, .flat.icon-button:checked:hover {
+ background-color: #393939; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:checked:active, .app-folder-dialog .folder-name-container .flat.edit-folder-button:checked:active, .flat.button:checked:active, .flat.icon-button:checked:active {
+ background-color: #404040; }
+
+.modal-dialog .modal-dialog-linked-button, .hotplug-notification-item, .notification-banner .notification-button {
+ padding: 12px;
+ font-weight: bold !important;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #616161;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
+ .modal-dialog .modal-dialog-linked-button:ltr, .hotplug-notification-item:ltr, .notification-banner .notification-button:ltr {
+ margin-right: 1px; }
+ .modal-dialog .modal-dialog-linked-button:rtl, .hotplug-notification-item:rtl, .notification-banner .notification-button:rtl {
+ margin-left: 1px; }
+ .modal-dialog .modal-dialog-linked-button:insensitive, .hotplug-notification-item:insensitive, .notification-banner .notification-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0); }
+ .modal-dialog .modal-dialog-linked-button:focus, .hotplug-notification-item:focus, .notification-banner .notification-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #5d656e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .modal-dialog .modal-dialog-linked-button:focus:hover, .hotplug-notification-item:focus:hover, .notification-banner .notification-button:focus:hover {
+ background-color: #636b75;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .modal-dialog .modal-dialog-linked-button:focus:active, .hotplug-notification-item:focus:active, .notification-banner .notification-button:focus:active {
+ background-color: #6a727c;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .modal-dialog .modal-dialog-linked-button:hover, .hotplug-notification-item:hover, .notification-banner .notification-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: dimgray;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(180, 180, 180, 0.28); }
+ .modal-dialog .modal-dialog-linked-button:active, .hotplug-notification-item:active, .notification-banner .notification-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #707070;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(184, 184, 184, 0.28); }
+ .modal-dialog .modal-dialog-linked-button:checked, .hotplug-notification-item:checked, .notification-banner .notification-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #787878;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(187, 187, 187, 0.28); }
+ .modal-dialog .modal-dialog-linked-button:checked:hover, .hotplug-notification-item:checked:hover, .notification-banner .notification-button:checked:hover {
+ background-color: gray; }
+ .modal-dialog .modal-dialog-linked-button:checked:active, .hotplug-notification-item:checked:active, .notification-banner .notification-button:checked:active {
+ background-color: #878787; }
+ .modal-dialog .modal-dialog-linked-button:first-child:ltr, .hotplug-notification-item:first-child:ltr, .notification-banner .notification-button:first-child:ltr {
+ border-radius: 0 0 0 10px; }
+ .modal-dialog .modal-dialog-linked-button:last-child:ltr, .hotplug-notification-item:last-child:ltr, .notification-banner .notification-button:last-child:ltr {
+ border-radius: 0 0 10px 0;
+ margin-right: 0 !important; }
+ .modal-dialog .modal-dialog-linked-button:first-child:rtl, .hotplug-notification-item:first-child:rtl, .notification-banner .notification-button:first-child:rtl {
+ border-radius: 0 0 10px 0; }
+ .modal-dialog .modal-dialog-linked-button:last-child:rtl, .hotplug-notification-item:last-child:rtl, .notification-banner .notification-button:last-child:rtl {
+ border-radius: 0 0 0 10px;
+ margin-left: 0 !important; }
+ .modal-dialog .modal-dialog-linked-button:first-child:last-child, .hotplug-notification-item:first-child:last-child, .notification-banner .notification-button:first-child:last-child {
+ border-radius: 0 0 10px 10px !important;
+ margin-left: 0 !important;
+ margin-right: 0 !important; }
+
+.screenshot-ui-show-pointer-button, .screenshot-ui-type-button {
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none; }
+ .screenshot-ui-show-pointer-button:insensitive, .screenshot-ui-type-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .screenshot-ui-show-pointer-button:insensitive, .screenshot-ui-type-button:insensitive {
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none; }
+ .screenshot-ui-show-pointer-button:insensitive:insensitive, .screenshot-ui-type-button:insensitive:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .screenshot-ui-show-pointer-button:focus, .screenshot-ui-type-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #1d252e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .screenshot-ui-show-pointer-button:focus:hover, .screenshot-ui-type-button:focus:hover {
+ background-color: #242b35;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .screenshot-ui-show-pointer-button:focus:active, .screenshot-ui-type-button:focus:active {
+ background-color: #2a323c;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .screenshot-ui-show-pointer-button:hover, .screenshot-ui-type-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #222222;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(144, 144, 144, 0.28); }
+ .screenshot-ui-show-pointer-button:active, .screenshot-ui-type-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #292929;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(148, 148, 148, 0.28); }
+ .screenshot-ui-show-pointer-button:outlined, .screenshot-ui-type-button:outlined, .screenshot-ui-show-pointer-button:checked, .screenshot-ui-type-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #313131;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(152, 152, 152, 0.28); }
+ .screenshot-ui-show-pointer-button:outlined:hover, .screenshot-ui-type-button:outlined:hover, .screenshot-ui-show-pointer-button:checked:hover, .screenshot-ui-type-button:checked:hover {
+ background-color: #393939; }
+ .screenshot-ui-show-pointer-button:outlined:active, .screenshot-ui-type-button:outlined:active, .screenshot-ui-show-pointer-button:checked:active, .screenshot-ui-type-button:checked:active {
+ background-color: #404040; }
+
+/* General Typography */
+.app-folder-dialog .folder-name-container .folder-name-label, .app-folder-dialog .folder-name-container .folder-name-entry, .search-statustext {
+ font-weight: 800;
+ font-size: 20pt; }
+
+.lg-debug-flags-header, .message-dialog-content .message-dialog-title, .message-list .message-list-placeholder, .datemenu-today-button .date-label {
+ font-weight: 800;
+ font-size: 15pt; }
+
+.quick-toggle-menu .header .title {
+ font-weight: 700;
+ font-size: 15pt; }
+
+.message-dialog-content .message-dialog-title.lightweight, .headline {
+ font-weight: 700;
+ font-size: 13pt; }
+
+.lg-extension-name, .osd-window, .dialog-list .dialog-list-title, .message-list-controls, .weather-button .weather-forecast-temp, .weather-button .weather-header, .world-clocks-button .world-clocks-time, .world-clocks-button .world-clocks-header, .events-button .events-title, .calendar .calendar-month-header .calendar-month-label, .datemenu-today-button .day-label, .popup-menu-ornament {
+ font-weight: 700;
+ font-size: 11pt; }
+
+.quick-toggle-menu .header .subtitle, .app-menu .popup-inactive-menu-item:first-child > StLabel {
+ font-weight: 700;
+ font-size: 9pt; }
+
+.login-dialog-not-listed-label, .lg-completions-text, .caps-lock-warning-label, #dash, .icon-label-button-container, .prompt-dialog-error-label,
+.prompt-dialog-info-label,
+.prompt-dialog-null-label, .run-dialog .run-dialog-description, .dialog-list .dialog-list-box .dialog-list-item .dialog-list-item-description, .weather-button .weather-forecast-time, .world-clocks-button .world-clocks-timezone, .events-button .event-time {
+ font-weight: 400;
+ font-size: 9pt; }
+
+.calendar .calendar-day-base.calendar-day-heading, .calendar .calendar-day-base {
+ font-weight: 400;
+ font-size: 8pt; }
+
+.unlock-dialog-clock-time, #panel, .weather-button .weather-forecast-time, .world-clocks-button .world-clocks-timezone, .world-clocks-button .world-clocks-time, .events-button .event-time, .calendar .calendar-day-base {
+ font-feature-settings: "tnum"; }
+
+/* WIDGETS */
+.shell-link {
+ color: #8fbbf0; }
+ .shell-link:hover {
+ color: #bcd6f6; }
+
+.lowres-icon {
+ icon-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
+
+.icon-dropshadow {
+ icon-shadow: 0 1px 5px rgba(0, 0, 0, 0.8); }
+
+/* Entries */
+StEntry {
+ border-radius: 8px;
+ padding: 8px;
+ color: white;
+ background-color: #0d0d0d;
+ color: rgba(255, 255, 255, 0.7);
+ border: 2px solid #0d0d0d;
+ selection-background-color: #3584e4;
+ selected-color: #ffffff; }
+ StEntry:hover {
+ background-color: #404040;
+ border-color: #404040;
+ color: rgba(255, 255, 255, 0.7); }
+ StEntry:focus {
+ background-color: #0f1318;
+ border-color: #3584e4;
+ color: white; }
+ StEntry:insensitive {
+ background-color: #0d0d0d;
+ border-color: #0d0d0d;
+ color: gray; }
+ StEntry StIcon.capslock-warning {
+ icon-size: 16px;
+ warning-color: #cd9309;
+ padding: 0 4px; }
+ StEntry StIcon.peek-password {
+ icon-size: 1.09em;
+ padding: 0 4px; }
+ StEntry StLabel.hint-text {
+ margin-left: 2px;
+ color: rgba(255, 255, 255, 0.7); }
+
+/* Buttons */
+.button, .icon-button {
+ min-height: 22px; }
+
+.icon-button {
+ border-radius: 99px;
+ padding: 12px;
+ min-height: 16px; }
+ .icon-button StIcon {
+ icon-size: 1.09em;
+ -st-icon-style: symbolic; }
+
+/* Check Boxes */
+.check-box StBoxLayout {
+ spacing: .8em; }
+
+.check-box StBin {
+ width: 24px;
+ height: 24px;
+ background-image: url("resource:///org/gnome/shell/theme/checkbox-off.svg"); }
+
+.check-box:focus StBin {
+ background-image: url("resource:///org/gnome/shell/theme/checkbox-off-focused.svg"); }
+
+.check-box:checked StBin {
+ background-image: url("resource:///org/gnome/shell/theme/checkbox.svg"); }
+
+.check-box:focus:checked StBin {
+ background-image: url("resource:///org/gnome/shell/theme/checkbox-focused.svg"); }
+
+/* Switches */
+.toggle-switch {
+ color: white;
+ height: 26px;
+ width: 48px;
+ background-size: contain;
+ background-image: url("resource:///org/gnome/shell/theme/toggle-off.svg"); }
+ .toggle-switch:checked {
+ background-image: url("resource:///org/gnome/shell/theme/toggle-on.svg"); }
+
+/* Slider */
+.slider {
+ -barlevel-height: 4px;
+ -barlevel-background-color: rgba(255, 255, 255, 0.2);
+ -barlevel-border-width: 2px;
+ -barlevel-border-color: transparent;
+ -barlevel-active-background-color: #3584e4;
+ -barlevel-active-border-color: transparent;
+ -barlevel-overdrive-color: #c01c28;
+ -barlevel-overdrive-border-color: transparent;
+ -barlevel-overdrive-separator-width: 1px;
+ -slider-handle-radius: 8px;
+ -slider-handle-border-width: 0;
+ -slider-handle-border-color: transparent; }
+
+/* Scrollbars */
+StScrollView.vfade {
+ -st-vfade-offset: 68px; }
+
+StScrollView.hfade {
+ -st-hfade-offset: 68px; }
+
+StScrollBar {
+ padding: 0; }
+ StScrollView StScrollBar {
+ min-width: 8px;
+ min-height: 8px; }
+ StScrollBar StBin#trough {
+ border-radius: 0;
+ background-color: transparent; }
+ StScrollBar StButton#vhandle, StScrollBar StButton#hhandle {
+ border-radius: 8px;
+ background-color: #4d4d4d;
+ border: 3px solid transparent;
+ transition: 500ms all ease; }
+ StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover {
+ background-color: gray; }
+ StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active {
+ background-color: #666666; }
+
+/* Popovers/Menus */
+.popup-menu-boxpointer, .candidate-popup-boxpointer {
+ -arrow-rise: 6px; }
+
+.popup-menu {
+ min-width: 15em;
+ color: white; }
+ .popup-menu.panel-menu {
+ margin-bottom: 1.75em; }
+
+.popup-menu-content, .candidate-popup-content {
+ padding: 6px;
+ border-radius: 20px;
+ border: 1px solid #2b2b2b;
+ box-shadow: 0 2px 4px 0 transparent;
+ background-color: #000; }
+
+.popup-menu-item {
+ padding: 9px 12px;
+ border-radius: 12px;
+ spacing: 6px;
+ transition-duration: 100ms;
+ background-color: transparent; }
+ .popup-menu-item:ltr {
+ padding-left: 6px; }
+ .popup-menu-item:rtl {
+ padding-right: 6px; }
+ .popup-menu-item:focus, .popup-menu-item:hover {
+ background-color: #333333 !important; }
+ .popup-menu-item:focus:active, .popup-menu-item:hover:active {
+ background-color: #383838 !important; }
+ .popup-menu-item:checked {
+ background-color: #2e2e2e !important; }
+ .popup-menu-item:checked {
+ margin-bottom: 0;
+ box-shadow: inset 0 -1px 0 0 #212121;
+ border-radius: 8px 8px 0 0; }
+ .popup-menu-item:checked:focus, .popup-menu-item:checked:hover {
+ background-color: #363636 !important; }
+ .popup-menu-item:checked:active {
+ background-color: #3b3b3b !important; }
+ .popup-menu-item:active {
+ background-color: #454545;
+ color: white; }
+ .popup-menu-item:insensitive {
+ color: rgba(255, 255, 255, 0.5); }
+ .popup-menu-item .toggle-switch:ltr {
+ margin-left: 4px; }
+ .popup-menu-item .toggle-switch:rtl {
+ margin-right: 4px; }
+
+.popup-inactive-menu-item {
+ color: white; }
+ .popup-inactive-menu-item:insensitive {
+ color: gray; }
+
+.popup-menu-arrow,
+.popup-menu-icon {
+ icon-size: 16px !important; }
+
+.popup-sub-menu {
+ background-color: #2e2e2e;
+ border-radius: 0 0 8px 8px; }
+ .popup-sub-menu .popup-menu-ornament {
+ min-width: 1.09em !important; }
+ .popup-sub-menu .popup-menu-item {
+ border-radius: 0;
+ margin: 0; }
+ .popup-sub-menu .popup-menu-item:last-child {
+ border-radius: 0 0 8px 8px; }
+ .popup-sub-menu .popup-menu-item:focus, .popup-sub-menu .popup-menu-item:hover {
+ background-color: #333333 !important; }
+ .popup-sub-menu .popup-menu-item:checked {
+ background-color: #2e2e2e !important; }
+ .popup-sub-menu .popup-menu-item:checked:focus, .popup-sub-menu .popup-menu-item:checked:hover {
+ background-color: #424242 !important; }
+ .popup-sub-menu .popup-menu-item:active {
+ background-color: #383838 !important; }
+ .popup-sub-menu .popup-menu-section .popup-menu-item:last-child:hover, .popup-sub-menu .popup-menu-section .popup-menu-item:last-child:focus {
+ border-radius: 0; }
+ .popup-sub-menu .popup-menu-section:last-child .popup-menu-item:last-child {
+ border-radius: 0 0 8px 8px; }
+
+.popup-menu-ornament {
+ width: 1.2em;
+ text-align: center !important; }
+ .popup-menu-ornament:ltr {
+ text-align: right; }
+ .popup-menu-ornament:rtl {
+ text-align: left; }
+
+.popup-separator-menu-item {
+ margin: 6px 0;
+ padding: 0 !important; }
+ .popup-separator-menu-item:ltr {
+ margin-right: 4px; }
+ .popup-separator-menu-item:rtl {
+ margin-left: 4px; }
+ .popup-separator-menu-item .popup-separator-menu-item-separator {
+ height: 1px;
+ background-color: #2b2b2b; }
+ .popup-separator-menu-item .popup-menu-ornament {
+ width: 0 !important; }
+ .popup-sub-menu .popup-separator-menu-item {
+ background-color: transparent; }
+ .popup-sub-menu .popup-separator-menu-item:ltr {
+ margin-right: 2.5em; }
+ .popup-sub-menu .popup-separator-menu-item:rtl {
+ margin-left: 2.5em; }
+ .popup-sub-menu .popup-separator-menu-item .popup-separator-menu-item-separator {
+ background-color: #3d3d3d; }
+
+.background-menu {
+ -boxpointer-gap: 0px;
+ -arrow-rise: 0px; }
+
+.app-menu {
+ max-width: 27.25em; }
+ .app-menu .popup-menu-ornament {
+ width: 0 !important; }
+ .app-menu .popup-inactive-menu-item:first-child > StLabel:ltr {
+ margin-right: 8px; }
+ .app-menu .popup-inactive-menu-item:first-child > StLabel:rtl {
+ margin-left: 8px; }
+
+/* Date/Time Menu */
+#calendarArea {
+ padding: 4px; }
+
+.datemenu-calendar-column {
+ spacing: 6px; }
+ .datemenu-calendar-column:ltr {
+ padding-left: 6px; }
+ .datemenu-calendar-column:rtl {
+ padding-right: 6px; }
+ .datemenu-calendar-column .datemenu-displays-box {
+ spacing: 6px; }
+
+/* today button (the date) */
+.datemenu-today-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px #1f1f1f;
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none;
+ box-shadow: none !important;
+ padding: 9px; }
+ .datemenu-today-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .datemenu-today-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .datemenu-today-button:focus:hover {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .datemenu-today-button:focus:active {
+ background-color: #434b54;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .datemenu-today-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(158, 158, 158, 0.28); }
+ .datemenu-today-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #444444;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(162, 162, 162, 0.28); }
+
+/* Calendar */
+.calendar {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px #1f1f1f;
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none;
+ box-shadow: none !important;
+ margin-top: 0; }
+ .calendar:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .calendar:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .calendar:focus:hover {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .calendar:focus:active {
+ background-color: #434b54;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .calendar:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(158, 158, 158, 0.28); }
+ .calendar:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #444444;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(162, 162, 162, 0.28); }
+ .calendar .calendar-month-header .calendar-change-month-back StIcon,
+ .calendar .calendar-month-header .calendar-change-month-forward StIcon {
+ icon-size: 1.09em; }
+ .calendar .calendar-month-header .calendar-month-label {
+ color: white;
+ padding: 8px 0; }
+ .calendar .calendar-month-header .pager-button {
+ background-color: transparent;
+ height: 32px;
+ width: 32px;
+ margin: 2px;
+ border-radius: 6px; }
+ .calendar .calendar-month-header .pager-button:hover, .calendar .calendar-month-header .pager-button:focus {
+ background-color: #333333; }
+ .calendar .calendar-month-header .pager-button:active {
+ background-color: #383838; }
+ .calendar .calendar-day-base {
+ text-align: center;
+ margin: 2px;
+ padding: 0 !important;
+ height: 3em !important;
+ width: 3em !important;
+ border-radius: 99px;
+ transition-duration: 100ms; }
+ .calendar .calendar-day-base:hover {
+ background-color: #333333; }
+ .calendar .calendar-day-base:focus {
+ background-color: #0b1a2e;
+ color: #ffffff;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .calendar .calendar-day-base:active, .calendar .calendar-day-base:selected {
+ color: white;
+ background-color: #383838; }
+ .calendar .calendar-day-base:active:focus, .calendar .calendar-day-base:selected:focus {
+ background-color: #37475a; }
+ .calendar .calendar-day-base.calendar-day-heading {
+ color: gray;
+ padding-top: 6px;
+ height: 16px !important;
+ font-weight: bold; }
+ .calendar .calendar-nonwork-day {
+ color: gray; }
+ .calendar .calendar-other-month-day {
+ color: rgba(255, 255, 255, 0.5); }
+ .calendar .calendar-other-month-day.calendar-nonwork-day {
+ color: rgba(128, 128, 128, 0.5); }
+ .calendar .calendar-today {
+ background-color: #3584e4;
+ font-weight: 800;
+ color: #ffffff !important; }
+ .calendar .calendar-today:hover, .calendar .calendar-today:focus {
+ background-color: #428ce6;
+ color: inherit; }
+ .calendar .calendar-today:active, .calendar .calendar-today:selected {
+ background-color: #3584e4;
+ color: inherit; }
+ .calendar .calendar-today:active:hover, .calendar .calendar-today:active:focus, .calendar .calendar-today:selected:hover, .calendar .calendar-today:selected:focus {
+ background-color: #428ce6;
+ color: inherit; }
+ .calendar .calendar-day-with-events {
+ background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg");
+ background-size: contain; }
+ .calendar .calendar-week-number {
+ font-size: 7pt;
+ font-weight: bold;
+ font-feature-settings: "tnum";
+ margin: 6px;
+ padding: 0 6px;
+ border-radius: 3px;
+ background-color: black;
+ color: gray; }
+
+/* Events */
+.events-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px #1f1f1f;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353535;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ padding: 12px; }
+ .events-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .events-button:focus:hover {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .events-button:focus:active {
+ background-color: #434b54;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .events-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(158, 158, 158, 0.28); }
+ .events-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #444444;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(162, 162, 162, 0.28); }
+ .events-button .events-box {
+ spacing: 6px; }
+ .events-button .events-list {
+ spacing: 12px; }
+ .events-button .events-title {
+ color: gray;
+ margin-bottom: 4px; }
+ .events-button .event-time {
+ color: gray; }
+
+/* World clocks */
+.world-clocks-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px #1f1f1f;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353535;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ padding: 12px; }
+ .world-clocks-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .world-clocks-button:focus:hover {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .world-clocks-button:focus:active {
+ background-color: #434b54;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .world-clocks-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(158, 158, 158, 0.28); }
+ .world-clocks-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #444444;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(162, 162, 162, 0.28); }
+ .world-clocks-button .world-clocks-grid {
+ spacing-rows: 6px;
+ spacing-columns: 12px; }
+ .world-clocks-button .world-clocks-header {
+ color: gray; }
+ .world-clocks-button .world-clocks-city {
+ color: white; }
+ .world-clocks-button .world-clocks-time {
+ color: white; }
+ .world-clocks-button .world-clocks-time:ltr {
+ text-align: right; }
+ .world-clocks-button .world-clocks-time:rtl {
+ text-align: left; }
+ .world-clocks-button .world-clocks-timezone {
+ color: gray; }
+
+/* Weather */
+.weather-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px #1f1f1f;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353535;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ padding: 12px; }
+ .weather-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .weather-button:focus:hover {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .weather-button:focus:active {
+ background-color: #434b54;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .weather-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(158, 158, 158, 0.28); }
+ .weather-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #444444;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(162, 162, 162, 0.28); }
+ .weather-button .weather-box {
+ spacing: 10px; }
+ .weather-button .weather-header-box {
+ spacing: 6px; }
+ .weather-button .weather-header {
+ color: gray; }
+ .weather-button .weather-header.location {
+ font-weight: normal; }
+ .weather-button .weather-grid {
+ spacing-rows: 6px;
+ spacing-columns: 12px; }
+ .weather-button .weather-forecast-time {
+ color: #b3b3b3;
+ padding-top: 0.2em;
+ padding-bottom: 0.4em; }
+ .weather-button .weather-forecast-icon {
+ icon-size: 2.18em; }
+
+/* Message List */
+.message-list {
+ width: 29em;
+ border: solid #2b2b2b; }
+ .message-list:ltr {
+ margin-left: 0;
+ margin-right: 4px;
+ padding-right: 6px;
+ border-right-width: 1px; }
+ .message-list:rtl {
+ margin-right: 0;
+ margin-left: 4px;
+ padding-left: 6px;
+ border-left-width: 1px; }
+ .message-list .message-list-placeholder {
+ color: rgba(128, 128, 128, 0.5); }
+ .message-list .message-list-placeholder > StIcon {
+ icon-size: 3.27em;
+ margin-bottom: 12px;
+ -st-icon-style: symbolic; }
+
+.message-list-sections {
+ spacing: 6px;
+ margin: 0;
+ padding-bottom: 6px; }
+ .message-list-sections:ltr {
+ margin-right: 12px; }
+ .message-list-sections:rtl {
+ margin-left: 12px; }
+
+.message-list-section,
+.message-list-section-list {
+ spacing: 6px; }
+
+.message-list-controls {
+ padding: 6px;
+ spacing: 6px; }
+ .message-list-controls .dnd-button {
+ border-width: 2px;
+ border-color: transparent;
+ border-radius: 32px;
+ border-style: solid; }
+ .message-list-controls .dnd-button:focus {
+ border-color: rgba(53, 132, 228, 0.6); }
+
+.message {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px #1f1f1f;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353535;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
+ .message:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .message:focus:hover {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .message:focus:active {
+ background-color: #434b54;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .message:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(158, 158, 158, 0.28); }
+ .message:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #444444;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(162, 162, 162, 0.28); }
+ .message .message-icon-bin {
+ padding: 18px; }
+ .message .message-icon-bin:ltr {
+ padding-right: 6px; }
+ .message .message-icon-bin:rtl {
+ padding-left: 6px; }
+ .message .message-icon-bin > StIcon {
+ icon-size: 2.18em;
+ -st-icon-style: symbolic; }
+ .message .message-icon-bin > .fallback-app-icon {
+ width: 1.09em;
+ height: 1.09em; }
+ .message .message-content {
+ spacing: 4px;
+ padding: 9px;
+ margin-bottom: 8px; }
+ .message .message-title {
+ font-weight: bold;
+ /* HACK: the label should be baseline-aligned with a 1em label, fake this with some bottom padding */
+ padding-top: 0.57em; }
+ .message .message-secondary-bin {
+ padding: 0 8px; }
+ .message .message-secondary-bin > .event-time {
+ color: rgba(255, 255, 255, 0.5);
+ font-size: 9pt;
+ /* HACK: the label should be baseline-aligned with a 1em label, fake this with some bottom padding */
+ padding-bottom: 0.13em; }
+ .message .message-secondary-bin > .event-time:ltr {
+ text-align: right; }
+ .message .message-secondary-bin > .event-time:rtl {
+ text-align: left; }
+ .message .message-close-button {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1);
+ border-radius: 99px;
+ padding: 5px;
+ margin: 1px; }
+ .message .message-close-button:hover {
+ background-color: rgba(255, 255, 255, 0.2); }
+ .message .message-close-button:active {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .message .message-close-button StIcon {
+ icon-size: 1.09em; }
+ .message .message-body {
+ color: #e6e6e6; }
+
+.url-highlighter {
+ link-color: #8fbbf0; }
+
+/* Media Controls */
+.message-media-control {
+ padding: 0 18px;
+ margin: 12px 0;
+ border-radius: 8px;
+ color: white; }
+ .message-media-control:hover {
+ background-color: #404040;
+ color: white; }
+ .message-media-control:active {
+ background-color: #454545;
+ color: white; }
+ .message-media-control:insensitive {
+ color: #8d8d8d; }
+ .message-media-control:last-child:ltr {
+ margin-right: 12px; }
+ .message-media-control:last-child:rtl {
+ margin-left: 12px; }
+ .message-media-control StIcon {
+ icon-size: 1.09em; }
+
+.media-message-cover-icon {
+ icon-size: 3.27em !important;
+ border-radius: 8px; }
+ .media-message-cover-icon.fallback {
+ color: #d4d4d4;
+ background-color: #000;
+ border: 1px solid transparent;
+ border-radius: 8px;
+ icon-size: 2.18em !important;
+ padding: 14px; }
+
+.candidate-popup-content {
+ padding: 6px;
+ spacing: 6px; }
+
+.candidate-index {
+ padding: 0;
+ padding-right: 6px;
+ color: gray; }
+
+.candidate-box {
+ padding: 6px 12px 6px 12px;
+ border-radius: 8px; }
+ .candidate-box:selected {
+ background-color: #3584e4;
+ color: #ffffff; }
+ .candidate-box:hover {
+ background-color: #333333;
+ color: white; }
+
+.candidate-page-button-box {
+ height: 2em; }
+ .vertical .candidate-page-button-box {
+ padding-top: 12px; }
+ .horizontal .candidate-page-button-box {
+ padding-left: 12px; }
+
+.candidate-page-button {
+ padding: 6px; }
+ .candidate-page-button StIcon {
+ icon-size: 1.09em; }
+
+.candidate-page-button-previous {
+ border-radius: 8px 0px 0px 8px;
+ border-right-width: 0;
+ box-shadow: none; }
+
+.candidate-page-button-next {
+ border-radius: 0px 8px 8px 0px;
+ box-shadow: none; }
+
+/* Notifications & Message Tray */
+.notification-banner {
+ min-height: 64px;
+ width: 34em;
+ box-shadow: 0 2px 4px 2px transparent;
+ border-radius: 12px;
+ margin: 4px; }
+ .notification-banner .notification-actions {
+ spacing: 0; }
+
+.summary-source-counter {
+ font-size: 10pt;
+ font-weight: bold;
+ height: 1.6em;
+ width: 1.6em;
+ -shell-counter-overlap-x: 3px;
+ -shell-counter-overlap-y: 3px;
+ background-color: #3584e4;
+ color: #ffffff;
+ border: 2px solid white;
+ box-shadow: 0 2px 2px rgba(0, 0, 0, 0.5);
+ border-radius: 0.9em; }
+
+.chat-body {
+ spacing: 5px; }
+
+.chat-response {
+ margin: 5px; }
+
+.chat-log-message {
+ color: #e6e6e6; }
+
+.chat-new-group {
+ padding-top: 1em; }
+
+.chat-received {
+ padding-left: 4px; }
+ .chat-received:rtl {
+ padding-left: 0px;
+ padding-right: 4px; }
+
+.chat-sent {
+ padding-left: 18pt;
+ color: white; }
+ .chat-sent:rtl {
+ padding-left: 0;
+ padding-right: 18pt; }
+
+.chat-meta-message {
+ padding-left: 4px;
+ font-size: 9pt;
+ font-weight: bold;
+ color: white; }
+ .chat-meta-message:rtl {
+ padding-left: 0;
+ padding-right: 4px; }
+
+.hotplug-notification-item-icon {
+ icon-size: 24px;
+ padding: 0 4px; }
+
+/* Modal Dialogs */
+.modal-dialog .modal-dialog-content-box {
+ margin: 32px 40px;
+ spacing: 32px;
+ max-width: 28em; }
+
+/* End Session Dialog */
+.end-session-dialog {
+ width: 30em; }
+ .end-session-dialog .end-session-dialog-battery-warning,
+ .end-session-dialog .dialog-list-title {
+ color: #cd9309; }
+
+/* Message Dialog */
+.message-dialog-content {
+ spacing: 18px; }
+ .message-dialog-content .message-dialog-title {
+ text-align: center; }
+ .message-dialog-content .message-dialog-description {
+ text-align: center; }
+
+/* Dialog List */
+.dialog-list {
+ spacing: 18px; }
+ .dialog-list .dialog-list-title {
+ text-align: center; }
+ .dialog-list .dialog-list-scrollview {
+ max-height: 200px; }
+ .dialog-list .dialog-list-box {
+ spacing: 1em; }
+ .dialog-list .dialog-list-box .dialog-list-item {
+ spacing: 1em; }
+ .dialog-list .dialog-list-box .dialog-list-item .dialog-list-item-title {
+ font-weight: bold; }
+ .dialog-list .dialog-list-box .dialog-list-item .dialog-list-item-description {
+ color: #f2f2f2; }
+
+/* Run Dialog */
+.run-dialog .modal-dialog-content-box {
+ margin-top: 24px;
+ margin-bottom: 14px; }
+
+.run-dialog .run-dialog-entry {
+ width: 20em; }
+
+.run-dialog .run-dialog-description {
+ text-align: center;
+ color: #cccccc; }
+
+/* Password or Authentication Dialog */
+.prompt-dialog {
+ width: 28em; }
+ .prompt-dialog .modal-dialog-content-box {
+ margin-bottom: 24px; }
+
+.prompt-dialog-password-grid {
+ spacing-rows: 8px;
+ spacing-columns: 4px; }
+ .prompt-dialog-password-grid .prompt-dialog-password-entry {
+ width: auto; }
+ .prompt-dialog-password-grid .prompt-dialog-password-entry:ltr {
+ margin-left: 20px; }
+ .prompt-dialog-password-grid .prompt-dialog-password-entry:rtl {
+ margin-right: 20px; }
+
+.prompt-dialog-password-layout {
+ spacing: 8px; }
+
+.prompt-dialog-password-entry {
+ width: 20em; }
+
+.prompt-dialog-error-label,
+.prompt-dialog-info-label,
+.prompt-dialog-null-label {
+ text-align: center; }
+
+.prompt-dialog-error-label {
+ color: #cd9309; }
+
+/* Polkit Dialog */
+.polkit-dialog-user-layout {
+ text-align: center;
+ spacing: 8px;
+ margin-bottom: 6px; }
+ .polkit-dialog-user-layout .polkit-dialog-user-root-label {
+ color: #cd9309; }
+
+/* Audio selection dialog */
+.audio-device-selection-dialog .modal-dialog-content-box {
+ margin-bottom: 28px; }
+
+.audio-device-selection-dialog .audio-selection-box {
+ spacing: 20px; }
+
+.audio-selection-device {
+ border-radius: 16px; }
+ .audio-selection-device:hover, .audio-selection-device:focus {
+ background-color: #333333; }
+ .audio-selection-device:active {
+ background-color: #3584e4;
+ color: #ffffff; }
+
+.audio-selection-device-box {
+ padding: 20px;
+ spacing: 20px; }
+
+.audio-selection-device-icon {
+ icon-size: 4.36em; }
+
+/* Welcome dialog */
+.welcome-dialog-image {
+ background-image: url("resource:///org/gnome/shell/theme/gnome-shell-start.svg");
+ background-size: contain;
+ /* Reasonable maximum dimensions */
+ height: 300px;
+ width: 300px; }
+
+/* Access portal dialog */
+.access-dialog {
+ text-align: center; }
+
+/* OSD */
+.osd-window {
+ text-align: center;
+ font-weight: bold;
+ spacing: 12px;
+ padding: 12px 18px;
+ margin-bottom: 4em; }
+ .osd-window > * {
+ spacing: 8px; }
+ .osd-window StIcon {
+ icon-size: 2.18em; }
+ .osd-window StLabel:ltr {
+ margin-right: 6px; }
+ .osd-window StLabel:rtl {
+ margin-left: 6px; }
+ .osd-window .level {
+ margin-bottom: 4px;
+ height: 6px;
+ min-width: 10.9em;
+ -barlevel-height: 6px;
+ -barlevel-background-color: rgba(255, 255, 255, 0.1);
+ -barlevel-active-background-color: white;
+ -barlevel-overdrive-color: #c01c28;
+ -barlevel-overdrive-separator-width: 3px; }
+ .osd-window .level:first-child {
+ margin-bottom: 0px; }
+ .osd-window .level:ltr {
+ margin-right: 6px; }
+ .osd-window .level:rtl {
+ margin-left: 6px; }
+
+.osd-monitor-label {
+ border-radius: 16px;
+ font-size: 3em;
+ font-weight: bold;
+ margin: 12px;
+ text-align: center;
+ min-width: 1.3em; }
+
+/* Pad OSD */
+.pad-osd-window {
+ padding: 32px;
+ background-color: rgba(0, 0, 0, 0.8); }
+ .pad-osd-window .pad-osd-title-box {
+ spacing: 12px; }
+ .pad-osd-window .pad-osd-title-menu-box {
+ spacing: 6px; }
+
+.combo-box-label {
+ width: 15em; }
+
+/* App Switcher */
+.switcher-popup {
+ padding: 0;
+ spacing: 24px; }
+
+.switcher-list {
+ padding: 10px;
+ border-radius: 24px;
+ box-shadow: 0 8px 8px 0 rgba(0, 0, 0, 0.1); }
+ .switcher-list .switcher-list-item-container {
+ spacing: 12px; }
+ .switcher-list .item-box {
+ background-color: transparent; }
+ .switcher-list .item-box:hover {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .switcher-list .item-box:selected, .switcher-list .item-box:focus {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .switcher-list .item-box:selected:hover, .switcher-list .item-box:focus:hover {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .switcher-list .item-box:selected:active, .switcher-list .item-box:focus:active {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .switcher-list .item-box:active {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .switcher-list .item-box:outlined, .switcher-list .item-box:checked {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .switcher-list .item-box:outlined:active, .switcher-list .item-box:checked:active {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .switcher-list .item-box:outlined:hover, .switcher-list .item-box:checked:hover {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .switcher-list .item-box:drop {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+ .switcher-list .item-box:hover {
+ background: none; }
+ .switcher-list .separator {
+ width: 1px;
+ background: #2b2b2b; }
+ .switcher-list .thumbnail-box {
+ padding: 2px;
+ spacing: 6px; }
+ .switcher-list .thumbnail {
+ width: 256px;
+ border-radius: 8px; }
+
+.switcher-arrow {
+ border-color: rgba(255, 255, 255, 0.8);
+ color: rgba(255, 255, 255, 0.8); }
+ .switcher-arrow:highlighted {
+ border-color: white;
+ color: white; }
+
+.input-source-switcher-symbol {
+ font-size: 34pt;
+ width: 96px;
+ height: 96px; }
+
+.cycler-highlight {
+ border: 5px solid #3584e4; }
+
+/* Workspace Switcher */
+.workspace-switcher {
+ margin-bottom: 4em;
+ spacing: 12px;
+ padding: 12px 18px; }
+
+.ws-switcher-indicator {
+ background-color: rgba(255, 255, 255, 0.5);
+ padding: 0.1816666667em;
+ margin: 0.9083333333em;
+ border-radius: 2.18em; }
+ .ws-switcher-indicator:active {
+ background-color: white;
+ padding: 0.3633333333em;
+ margin: 0.7266666667em; }
+
+.icon-label-button-container {
+ spacing: 6px; }
+ .icon-label-button-container StIcon {
+ icon-size: 32px; }
+
+.screenshot-ui-panel {
+ border-radius: 37px;
+ padding: 18px;
+ padding-bottom: 12px;
+ margin-bottom: 4em;
+ spacing: 12px; }
+
+.screenshot-ui-close-button {
+ padding: 6px !important;
+ margin-top: 12px;
+ box-shadow: 0 2px 4px 0 transparent; }
+ .screenshot-ui-close-button.left {
+ margin-left: 12px; }
+ .screenshot-ui-close-button.right {
+ margin-right: 12px; }
+
+.screenshot-ui-type-button {
+ min-width: 48px;
+ padding: 12px 18px !important;
+ border-radius: 19px; }
+
+.screenshot-ui-capture-button {
+ width: 36px;
+ height: 36px;
+ border-radius: 99px;
+ border: 4px white;
+ padding: 4px; }
+ .screenshot-ui-capture-button .screenshot-ui-capture-button-circle {
+ background-color: white;
+ transition-duration: 200ms;
+ border-radius: 99px; }
+ .screenshot-ui-capture-button .screenshot-ui-capture-button-circle:hover, .screenshot-ui-capture-button .screenshot-ui-capture-button-circle:focus {
+ background-color: #333333; }
+ .screenshot-ui-capture-button:hover .screenshot-ui-capture-button-circle, .screenshot-ui-capture-button:focus .screenshot-ui-capture-button-circle {
+ background-color: #d9d9d9; }
+ .screenshot-ui-capture-button:active .screenshot-ui-capture-button-circle {
+ background-color: gray; }
+ .screenshot-ui-capture-button:cast .screenshot-ui-capture-button-circle {
+ background-color: #c01c28; }
+ .screenshot-ui-capture-button:cast:hover .screenshot-ui-capture-button-circle, .screenshot-ui-capture-button:cast:focus .screenshot-ui-capture-button-circle {
+ background-color: #d61f2d; }
+ .screenshot-ui-capture-button:cast:active .screenshot-ui-capture-button-circle {
+ background-color: #a11722; }
+
+.screenshot-ui-shot-cast-container {
+ background-color: #0d0d0d;
+ border-radius: 16px;
+ padding: 3px;
+ spacing: 3px; }
+ .screenshot-ui-shot-cast-container:ltr {
+ margin-left: 3px; }
+ .screenshot-ui-shot-cast-container:rtl {
+ margin-right: 3px; }
+
+.screenshot-ui-shot-cast-button {
+ padding: 6px 12px;
+ background-color: transparent;
+ border-radius: 13px; }
+ .screenshot-ui-shot-cast-button:hover, .screenshot-ui-shot-cast-button:focus {
+ background-color: #1a1a1a; }
+ .screenshot-ui-shot-cast-button:active {
+ background-color: #454545; }
+ .screenshot-ui-shot-cast-button:checked {
+ background-color: white;
+ color: black; }
+ .screenshot-ui-shot-cast-button:insensitive {
+ color: rgba(255, 255, 255, 0.5); }
+ .screenshot-ui-shot-cast-button StIcon {
+ icon-size: 1.09em; }
+
+.screenshot-ui-show-pointer-button {
+ border-radius: 99px;
+ padding: 12px !important; }
+ .screenshot-ui-show-pointer-button StIcon {
+ icon-size: 1.09em; }
+
+.screenshot-ui-area-indicator-shade {
+ background-color: rgba(0, 0, 0, 0.3); }
+
+.screenshot-ui-area-selector .screenshot-ui-area-indicator-shade {
+ background-color: rgba(0, 0, 0, 0.5); }
+
+.screenshot-ui-area-selector .screenshot-ui-area-indicator-selection {
+ border: 2px white; }
+
+.screenshot-ui-area-selector-handle {
+ border-radius: 99px;
+ background-color: white;
+ box-shadow: 0 1px 3px 2px rgba(0, 0, 0, 0.2);
+ width: 24px;
+ height: 24px; }
+
+.screenshot-ui-window-selector {
+ background-color: #2b2b2b; }
+ .screenshot-ui-window-selector .screenshot-ui-window-selector-window-container {
+ margin: 100px; }
+ .screenshot-ui-window-selector:primary-monitor .screenshot-ui-window-selector-window-container {
+ margin-bottom: 200px; }
+
+.screenshot-ui-window-selector-window-border {
+ transition-duration: 200ms;
+ border-radius: 16px;
+ border: 6px transparent; }
+
+.screenshot-ui-window-selector-check {
+ transition-duration: 200ms;
+ color: transparent;
+ border-radius: 99px;
+ border-width: 12px;
+ icon-size: 24px; }
+
+.screenshot-ui-window-selector-window:hover .screenshot-ui-window-selector-window-border {
+ border-color: #185fb4; }
+
+.screenshot-ui-window-selector-window:checked .screenshot-ui-window-selector-window-border {
+ border-color: #3584e4;
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.screenshot-ui-window-selector-window:checked .screenshot-ui-window-selector-check {
+ color: #ffffff;
+ background-color: #3584e4; }
+
+.screenshot-ui-screen-selector {
+ transition-duration: 200ms;
+ background-color: rgba(0, 0, 0, 0.5); }
+ .screenshot-ui-screen-selector:hover {
+ background-color: rgba(0, 0, 0, 0.3); }
+ .screenshot-ui-screen-selector:active {
+ background-color: rgba(0, 0, 0, 0.7); }
+ .screenshot-ui-screen-selector:checked {
+ background-color: transparent;
+ border: 2px white; }
+
+.screenshot-ui-tooltip {
+ color: white;
+ background-color: #000;
+ border-radius: 99px;
+ padding: 6px 12px;
+ text-align: center;
+ -y-offset: 24px; }
+
+/* Top Bar */
+#panel {
+ background-color: #000;
+ font-weight: bold;
+ height: 2.2em;
+ transition-duration: 250ms; }
+ #panel.unlock-screen, #panel.login-screen, #panel:overview {
+ background-color: transparent; }
+ #panel .panel-button {
+ font-weight: bold;
+ color: #f2f2f2;
+ -natural-hpadding: 12px;
+ -minimum-hpadding: 6px;
+ transition-duration: 150ms;
+ border: 3px solid transparent;
+ border-radius: 99px; }
+ #panel .panel-button.clock-display .clock {
+ transition-duration: 150ms;
+ border: 3px solid transparent;
+ border-radius: 99px; }
+ #panel .panel-button.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px #c01c28; }
+ #panel .panel-button.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px #cd9309; }
+ #panel .panel-button.screen-sharing-indicator StBoxLayout {
+ margin: 0 6px; }
+ #panel .panel-button.screen-recording-indicator StBoxLayout, #panel .panel-button.screen-sharing-indicator StBoxLayout {
+ spacing: 6px; }
+ #panel .panel-button.screen-recording-indicator StIcon, #panel .panel-button.screen-sharing-indicator StIcon {
+ icon-size: 1.09em; }
+ #panel .panel-button:active, #panel .panel-button:overview, #panel .panel-button:focus, #panel .panel-button:checked {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.2); }
+ #panel .panel-button.clock-display:active, #panel .panel-button.clock-display:overview, #panel .panel-button.clock-display:focus, #panel .panel-button.clock-display:checked {
+ box-shadow: none; }
+ #panel .panel-button.clock-display:active .clock, #panel .panel-button.clock-display:overview .clock, #panel .panel-button.clock-display:focus .clock, #panel .panel-button.clock-display:checked .clock {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.2); }
+ #panel .panel-button.screen-recording-indicator:active, #panel .panel-button.screen-recording-indicator:overview, #panel .panel-button.screen-recording-indicator:focus, #panel .panel-button.screen-recording-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.85); }
+ #panel .panel-button.screen-sharing-indicator:active, #panel .panel-button.screen-sharing-indicator:overview, #panel .panel-button.screen-sharing-indicator:focus, #panel .panel-button.screen-sharing-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.85); }
+ #panel .panel-button:hover {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.15); }
+ #panel .panel-button.clock-display:hover {
+ box-shadow: none; }
+ #panel .panel-button.clock-display:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.15); }
+ #panel .panel-button.screen-recording-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.9); }
+ #panel .panel-button.screen-sharing-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.9); }
+ #panel .panel-button:active:hover, #panel .panel-button:overview:hover, #panel .panel-button:focus:hover, #panel .panel-button:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.25); }
+ #panel .panel-button.clock-display:active:hover, #panel .panel-button.clock-display:overview:hover, #panel .panel-button.clock-display:focus:hover, #panel .panel-button.clock-display:checked:hover {
+ box-shadow: none; }
+ #panel .panel-button.clock-display:active:hover .clock, #panel .panel-button.clock-display:overview:hover .clock, #panel .panel-button.clock-display:focus:hover .clock, #panel .panel-button.clock-display:checked:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.25); }
+ #panel .panel-button.screen-recording-indicator:active:hover, #panel .panel-button.screen-recording-indicator:overview:hover, #panel .panel-button.screen-recording-indicator:focus:hover, #panel .panel-button.screen-recording-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.8); }
+ #panel .panel-button.screen-sharing-indicator:active:hover, #panel .panel-button.screen-sharing-indicator:overview:hover, #panel .panel-button.screen-sharing-indicator:focus:hover, #panel .panel-button.screen-sharing-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.8); }
+ #panel .panel-button .system-status-icon {
+ icon-size: 1.09em;
+ padding: 5px;
+ margin: 0 4px; }
+ #panel .panel-button .panel-status-indicators-box .system-status-icon,
+ #panel .panel-button .panel-status-menu-box .system-status-icon {
+ margin: 0; }
+ #panel .panel-button .app-menu-icon {
+ -st-icon-style: symbolic; }
+ #panel #panelActivities.panel-button {
+ -natural-hpadding: 18px; }
+ #panel.unlock-screen .panel-button:active, #panel.unlock-screen .panel-button:overview, #panel.unlock-screen .panel-button:focus, #panel.unlock-screen .panel-button:checked, #panel.login-screen .panel-button:active, #panel.login-screen .panel-button:overview, #panel.login-screen .panel-button:focus, #panel.login-screen .panel-button:checked, #panel:overview .panel-button:active, #panel:overview .panel-button:overview, #panel:overview .panel-button:focus, #panel:overview .panel-button:checked {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.15); }
+ #panel.unlock-screen .panel-button.clock-display:active, #panel.unlock-screen .panel-button.clock-display:overview, #panel.unlock-screen .panel-button.clock-display:focus, #panel.unlock-screen .panel-button.clock-display:checked, #panel.login-screen .panel-button.clock-display:active, #panel.login-screen .panel-button.clock-display:overview, #panel.login-screen .panel-button.clock-display:focus, #panel.login-screen .panel-button.clock-display:checked, #panel:overview .panel-button.clock-display:active, #panel:overview .panel-button.clock-display:overview, #panel:overview .panel-button.clock-display:focus, #panel:overview .panel-button.clock-display:checked {
+ box-shadow: none; }
+ #panel.unlock-screen .panel-button.clock-display:active .clock, #panel.unlock-screen .panel-button.clock-display:overview .clock, #panel.unlock-screen .panel-button.clock-display:focus .clock, #panel.unlock-screen .panel-button.clock-display:checked .clock, #panel.login-screen .panel-button.clock-display:active .clock, #panel.login-screen .panel-button.clock-display:overview .clock, #panel.login-screen .panel-button.clock-display:focus .clock, #panel.login-screen .panel-button.clock-display:checked .clock, #panel:overview .panel-button.clock-display:active .clock, #panel:overview .panel-button.clock-display:overview .clock, #panel:overview .panel-button.clock-display:focus .clock, #panel:overview .panel-button.clock-display:checked .clock {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.15); }
+ #panel.unlock-screen .panel-button.screen-recording-indicator:active, #panel.unlock-screen .panel-button.screen-recording-indicator:overview, #panel.unlock-screen .panel-button.screen-recording-indicator:focus, #panel.unlock-screen .panel-button.screen-recording-indicator:checked, #panel.login-screen .panel-button.screen-recording-indicator:active, #panel.login-screen .panel-button.screen-recording-indicator:overview, #panel.login-screen .panel-button.screen-recording-indicator:focus, #panel.login-screen .panel-button.screen-recording-indicator:checked, #panel:overview .panel-button.screen-recording-indicator:active, #panel:overview .panel-button.screen-recording-indicator:overview, #panel:overview .panel-button.screen-recording-indicator:focus, #panel:overview .panel-button.screen-recording-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.85); }
+ #panel.unlock-screen .panel-button.screen-sharing-indicator:active, #panel.unlock-screen .panel-button.screen-sharing-indicator:overview, #panel.unlock-screen .panel-button.screen-sharing-indicator:focus, #panel.unlock-screen .panel-button.screen-sharing-indicator:checked, #panel.login-screen .panel-button.screen-sharing-indicator:active, #panel.login-screen .panel-button.screen-sharing-indicator:overview, #panel.login-screen .panel-button.screen-sharing-indicator:focus, #panel.login-screen .panel-button.screen-sharing-indicator:checked, #panel:overview .panel-button.screen-sharing-indicator:active, #panel:overview .panel-button.screen-sharing-indicator:overview, #panel:overview .panel-button.screen-sharing-indicator:focus, #panel:overview .panel-button.screen-sharing-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.85); }
+ #panel.unlock-screen .panel-button:hover, #panel.login-screen .panel-button:hover, #panel:overview .panel-button:hover {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.1); }
+ #panel.unlock-screen .panel-button.clock-display:hover, #panel.login-screen .panel-button.clock-display:hover, #panel:overview .panel-button.clock-display:hover {
+ box-shadow: none; }
+ #panel.unlock-screen .panel-button.clock-display:hover .clock, #panel.login-screen .panel-button.clock-display:hover .clock, #panel:overview .panel-button.clock-display:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.1); }
+ #panel.unlock-screen .panel-button.screen-recording-indicator:hover, #panel.login-screen .panel-button.screen-recording-indicator:hover, #panel:overview .panel-button.screen-recording-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.9); }
+ #panel.unlock-screen .panel-button.screen-sharing-indicator:hover, #panel.login-screen .panel-button.screen-sharing-indicator:hover, #panel:overview .panel-button.screen-sharing-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.9); }
+ #panel.unlock-screen .panel-button:active:hover, #panel.unlock-screen .panel-button:overview:hover, #panel.unlock-screen .panel-button:focus:hover, #panel.unlock-screen .panel-button:checked:hover, #panel.login-screen .panel-button:active:hover, #panel.login-screen .panel-button:overview:hover, #panel.login-screen .panel-button:focus:hover, #panel.login-screen .panel-button:checked:hover, #panel:overview .panel-button:active:hover, #panel:overview .panel-button:overview:hover, #panel:overview .panel-button:focus:hover, #panel:overview .panel-button:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.2); }
+ #panel.unlock-screen .panel-button.clock-display:active:hover, #panel.unlock-screen .panel-button.clock-display:overview:hover, #panel.unlock-screen .panel-button.clock-display:focus:hover, #panel.unlock-screen .panel-button.clock-display:checked:hover, #panel.login-screen .panel-button.clock-display:active:hover, #panel.login-screen .panel-button.clock-display:overview:hover, #panel.login-screen .panel-button.clock-display:focus:hover, #panel.login-screen .panel-button.clock-display:checked:hover, #panel:overview .panel-button.clock-display:active:hover, #panel:overview .panel-button.clock-display:overview:hover, #panel:overview .panel-button.clock-display:focus:hover, #panel:overview .panel-button.clock-display:checked:hover {
+ box-shadow: none; }
+ #panel.unlock-screen .panel-button.clock-display:active:hover .clock, #panel.unlock-screen .panel-button.clock-display:overview:hover .clock, #panel.unlock-screen .panel-button.clock-display:focus:hover .clock, #panel.unlock-screen .panel-button.clock-display:checked:hover .clock, #panel.login-screen .panel-button.clock-display:active:hover .clock, #panel.login-screen .panel-button.clock-display:overview:hover .clock, #panel.login-screen .panel-button.clock-display:focus:hover .clock, #panel.login-screen .panel-button.clock-display:checked:hover .clock, #panel:overview .panel-button.clock-display:active:hover .clock, #panel:overview .panel-button.clock-display:overview:hover .clock, #panel:overview .panel-button.clock-display:focus:hover .clock, #panel:overview .panel-button.clock-display:checked:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.2); }
+ #panel.unlock-screen .panel-button.screen-recording-indicator:active:hover, #panel.unlock-screen .panel-button.screen-recording-indicator:overview:hover, #panel.unlock-screen .panel-button.screen-recording-indicator:focus:hover, #panel.unlock-screen .panel-button.screen-recording-indicator:checked:hover, #panel.login-screen .panel-button.screen-recording-indicator:active:hover, #panel.login-screen .panel-button.screen-recording-indicator:overview:hover, #panel.login-screen .panel-button.screen-recording-indicator:focus:hover, #panel.login-screen .panel-button.screen-recording-indicator:checked:hover, #panel:overview .panel-button.screen-recording-indicator:active:hover, #panel:overview .panel-button.screen-recording-indicator:overview:hover, #panel:overview .panel-button.screen-recording-indicator:focus:hover, #panel:overview .panel-button.screen-recording-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.8); }
+ #panel.unlock-screen .panel-button.screen-sharing-indicator:active:hover, #panel.unlock-screen .panel-button.screen-sharing-indicator:overview:hover, #panel.unlock-screen .panel-button.screen-sharing-indicator:focus:hover, #panel.unlock-screen .panel-button.screen-sharing-indicator:checked:hover, #panel.login-screen .panel-button.screen-sharing-indicator:active:hover, #panel.login-screen .panel-button.screen-sharing-indicator:overview:hover, #panel.login-screen .panel-button.screen-sharing-indicator:focus:hover, #panel.login-screen .panel-button.screen-sharing-indicator:checked:hover, #panel:overview .panel-button.screen-sharing-indicator:active:hover, #panel:overview .panel-button.screen-sharing-indicator:overview:hover, #panel:overview .panel-button.screen-sharing-indicator:focus:hover, #panel:overview .panel-button.screen-sharing-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.8); }
+ #panel .panel-status-indicators-box,
+ #panel .panel-status-menu-box {
+ spacing: 2px; }
+ #panel .power-status.panel-status-indicators-box {
+ spacing: 0; }
+ #panel .screencast-indicator,
+ #panel .remote-access-indicator {
+ color: #cd9309; }
+
+#appMenu {
+ spacing: 6px; }
+ #appMenu .label-shadow {
+ color: transparent; }
+
+#appMenu .panel-status-menu-box {
+ padding: 0 6px;
+ spacing: 6px; }
+
+.clock-display-box {
+ spacing: 2px; }
+ .clock-display-box .clock {
+ padding-left: 12px;
+ padding-right: 12px; }
+
+/* Activities Ripple */
+.ripple-box {
+ background-color: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 0 2px 2px rgba(255, 255, 255, 0.2);
+ width: 52px;
+ height: 52px;
+ border-radius: 0 0 52px 0; }
+ .ripple-box:rtl {
+ border-radius: 0 0 0 52px; }
+
+.quick-settings {
+ padding: 18px;
+ border-radius: 36px; }
+ .quick-settings .icon-button, .quick-settings .button {
+ padding: 10.5px; }
+
+.quick-settings-grid {
+ spacing-rows: 12px;
+ spacing-columns: 12px; }
+
+.quick-toggle {
+ border-radius: 99px;
+ min-width: 12em;
+ max-width: 12em;
+ min-height: 40px;
+ border: none;
+ /* Move padding into the box; this is to allow menu arrows
+ to extend to the border */ }
+ .quick-toggle:checked {
+ transition-duration: 100ms;
+ background-color: #3584e4;
+ color: #ffffff;
+ box-shadow: none; }
+ .quick-toggle:checked:focus {
+ box-shadow: inset 0 0 0 2px #629fea; }
+ .quick-toggle:checked:hover, .quick-toggle:checked:focus {
+ background-color: #4b92e7;
+ color: white; }
+ .quick-toggle:checked:active {
+ background-color: #1d72d8;
+ color: #ededed; }
+ .quick-toggle:checked:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ background-color: rgba(53, 132, 228, 0.5);
+ color: rgba(255, 255, 255, 0.5); }
+ .quick-toggle > StBoxLayout {
+ spacing: 6px; }
+ .quick-toggle.button, .quick-toggle.icon-button {
+ padding: 0; }
+ .quick-toggle > StBoxLayout {
+ padding: 0 12px; }
+ .quick-toggle:ltr > StBoxLayout {
+ padding-left: 15px; }
+ .quick-toggle:rtl > StBoxLayout {
+ padding-right: 15px; }
+ .quick-toggle .quick-toggle-label {
+ font-weight: bold; }
+ .quick-toggle .quick-toggle-icon, .quick-toggle .quick-toggle-arrow {
+ icon-size: 1.09em; }
+
+.quick-menu-toggle:ltr > StBoxLayout {
+ padding-right: 0; }
+
+.quick-menu-toggle:rtl > StBoxLayout {
+ padding-left: 0; }
+
+.quick-menu-toggle .quick-toggle-arrow {
+ background-color: rgba(255, 255, 255, 0.1);
+ padding: 6px 10.5px; }
+ .quick-menu-toggle .quick-toggle-arrow:ltr {
+ border-radius: 0 99px 99px 0; }
+ .quick-menu-toggle .quick-toggle-arrow:rtl {
+ border-radius: 99px 0 0 99px; }
+
+.quick-slider > StBoxLayout {
+ spacing: 6px; }
+
+.quick-slider .slider-bin {
+ min-height: 16px;
+ padding: 6px;
+ border-radius: 99px; }
+ .quick-slider .slider-bin:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #1d252e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .quick-slider .slider-bin:focus:hover {
+ background-color: #242b35;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .quick-slider .slider-bin:focus:active {
+ background-color: #2a323c;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+
+.quick-slider .quick-toggle-icon {
+ icon-size: 1.09em; }
+ .quick-slider .quick-toggle-icon:ltr {
+ margin-left: 6px; }
+ .quick-slider .quick-toggle-icon:rtl {
+ margin-right: 6px; }
+
+.quick-toggle-menu {
+ background-color: #1f1f1f;
+ border-radius: 24px;
+ padding: 12px;
+ margin: 12px 18px 0; }
+ .quick-toggle-menu .popup-menu-item > StIcon {
+ -st-icon-style: symbolic; }
+ .quick-toggle-menu .header {
+ spacing-rows: 3px;
+ spacing-columns: 12px;
+ padding-bottom: 12px; }
+ .quick-toggle-menu .header .icon {
+ icon-size: 1.635em;
+ border-radius: 999px;
+ padding: 9px;
+ background-color: #1a1a1a; }
+ .quick-toggle-menu .header .icon.active {
+ background-color: #3584e4; }
+
+.quick-settings-system-item > StBoxLayout {
+ spacing: 12px; }
+
+.quick-settings-system-item .power-item {
+ min-height: 0;
+ min-width: 0; }
+ .quick-settings-system-item .power-item:insensitive {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #1a1a1a;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: transparent; }
+
+.nm-network-item .wireless-secure-icon {
+ icon-size: 0.545em; }
+
+/* OVERVIEW */
+.controls-manager, .secondary-monitor-workspaces {
+ spacing: 12px; }
+
+#overviewGroup {
+ background-color: #2b2b2b; }
+
+.overview-controls {
+ padding-bottom: 32px; }
+
+/* Window Picker */
+.window-picker {
+ spacing: 6px; }
+
+.window-caption {
+ color: white;
+ background-color: #0d0d0d;
+ border-radius: 99px;
+ padding: 6px 12px; }
+
+.window-close, .screenshot-ui-close-button {
+ background-color: rgba(18, 18, 18, 0.98);
+ color: white;
+ border-radius: 99px;
+ box-shadow: 0 2px 4px 0 transparent;
+ padding: 3px;
+ height: 30px;
+ width: 30px;
+ transition-duration: 100ms; }
+ .window-close StIcon, .screenshot-ui-close-button StIcon {
+ icon-size: 24px; }
+ .window-close:hover, .screenshot-ui-close-button:hover {
+ background-color: rgba(36, 36, 36, 0.98); }
+ .window-close:active, .screenshot-ui-close-button:active {
+ background-color: rgba(51, 51, 51, 0.98); }
+
+.workspace-background {
+ border-radius: 30px;
+ background-color: rgba(3, 2, 1, 0);
+ box-shadow: 0 4px 16px 4px rgba(0, 0, 0, 0.3); }
+
+
+.search-entry {
+ border-radius: 18px;
+ margin-top: 12px;
+ margin-bottom: 6px;
+ padding: 7px 9px;
+ width: 320px;
+ background-color: #0d0d0d;
+ color: rgba(255, 255, 255, 0.7);
+ border: 2px solid #0d0d0d; }
+
+ .search-entry:hover {
+ background-color: #404040;
+ border-color: #404040;
+ color: rgba(255, 255, 255, 0.7); }
+
+ .search-entry:focus {
+ background-color: #0f1318;
+ border-color: #3584e4;
+ color: white; }
+
+ .search-entry:insensitive {
+ background-color: #0d0d0d;
+ border-color: #0d0d0d;
+ color: gray; }
+
+ .search-entry .search-entry-icon {
+ color: inherit;
+ icon-size: 1.09em;
+ margin-top: 2px;
+ padding: 0 4px; }
+
+/* Search */
+#searchResults {
+ margin: 0 4px; }
+
+#searchResultsContent {
+ max-width: 1072px; }
+
+.search-section {
+ spacing: 18px; }
+ .search-section .search-section-separator {
+ height: 8px;
+ background-color: transparent; }
+
+.search-section-content {
+ background-color: #383838;
+ border-radius: 24px;
+ border: 1px solid #000;
+ color: white;
+ padding: 12px;
+ margin: 0 12px; }
+
+.list-search-result, .search-provider-icon {
+ background-color: transparent;
+ border-radius: 11px; }
+ .list-search-result:hover, .search-provider-icon:hover {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .list-search-result:selected, .search-provider-icon:selected, .list-search-result:focus, .search-provider-icon:focus {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .list-search-result:selected:hover, .search-provider-icon:selected:hover, .list-search-result:focus:hover, .search-provider-icon:focus:hover {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .list-search-result:selected:active, .search-provider-icon:selected:active, .list-search-result:focus:active, .search-provider-icon:focus:active {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .list-search-result:active, .search-provider-icon:active {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .list-search-result:outlined, .search-provider-icon:outlined, .list-search-result:checked, .search-provider-icon:checked {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .list-search-result:outlined:active, .search-provider-icon:outlined:active, .list-search-result:checked:active, .search-provider-icon:checked:active {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .list-search-result:outlined:hover, .search-provider-icon:outlined:hover, .list-search-result:checked:hover, .search-provider-icon:checked:hover {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .list-search-result:drop, .search-provider-icon:drop {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.grid-search-results {
+ spacing: 30px;
+ margin: 0 12px; }
+
+.search-provider-icon:ltr {
+ margin-right: 4px; }
+
+.search-provider-icon:rtl {
+ margin-left: 4px; }
+
+.search-provider-icon .list-search-provider-content {
+ spacing: 12px; }
+ .search-provider-icon .list-search-provider-content .list-search-provider-details {
+ width: 120px;
+ color: white; }
+
+.list-search-results {
+ spacing: 6px; }
+
+.list-search-result .list-search-result-content {
+ spacing: 6px; }
+
+.list-search-result .list-search-result-title {
+ spacing: 12px; }
+
+.list-search-result .list-search-result-description {
+ color: #b3b3b3; }
+
+/* Dash */
+#dash {
+ margin-top: 12px; }
+ #dash .placeholder {
+ background-image: none;
+ background-size: contain;
+ height: 32px; }
+ #dash .empty-dash-drop-target {
+ width: 32px;
+ height: 32px; }
+ #dash .app-well-app-running-dot {
+ margin-bottom: 14px; }
+
+.dash-background {
+ background-color: #383838;
+ border-radius: 28px;
+ padding: 12px;
+ spacing: 6px;
+ margin-bottom: 12px; }
+
+.dash-item-container > * {
+ margin: 0 2px; }
+
+.dash-item-container:ltr:first-child {
+ margin-left: 0; }
+
+.dash-item-container:rtl:first-child {
+ margin-right: 0; }
+
+.dash-item-container .app-well-app .overview-icon, .dash-item-container .grid-search-result .overview-icon {
+ padding: 6px; }
+
+.dash-item-container .show-apps {
+ transition-duration: 400ms; }
+ .dash-item-container .show-apps .overview-icon {
+ background-color: transparent; }
+ .dash-item-container .show-apps:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .dash-item-container .show-apps:selected .overview-icon, .dash-item-container .show-apps:focus .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .dash-item-container .show-apps:selected .overview-icon:hover .overview-icon, .dash-item-container .show-apps:focus .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .dash-item-container .show-apps:selected .overview-icon:active .overview-icon, .dash-item-container .show-apps:focus .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .dash-item-container .show-apps:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .dash-item-container .show-apps:outlined .overview-icon, .dash-item-container .show-apps:checked .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .dash-item-container .show-apps:outlined .overview-icon:active .overview-icon, .dash-item-container .show-apps:checked .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .dash-item-container .show-apps:outlined .overview-icon:hover .overview-icon, .dash-item-container .show-apps:checked .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .dash-item-container .show-apps:drop .overview-icon {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.dash-item-container .show-apps, .dash-item-container .app-well-app, .dash-item-container .grid-search-result {
+ padding-bottom: 12px; }
+
+.dash-separator {
+ width: 1px;
+ margin: 0 4px;
+ background-color: #2b2b2b;
+ margin-bottom: 12px; }
+
+.dash-label {
+ color: white;
+ background-color: #000;
+ border-radius: 99px;
+ padding: 6px 12px;
+ text-align: center;
+ -y-offset: 8px; }
+
+/* App Grid */
+.icon-grid {
+ row-spacing: 12px;
+ column-spacing: 12px;
+ max-row-spacing: 42px;
+ max-column-spacing: 42px;
+ page-padding-top: 24px;
+ page-padding-bottom: 24px;
+ page-padding-left: 18px;
+ page-padding-right: 18px; }
+
+/* App Icons */
+.app-well-app, .grid-search-result {
+ transition-duration: 400ms; }
+ .app-well-app .overview-icon, .grid-search-result .overview-icon {
+ background-color: transparent; }
+ .app-well-app:hover .overview-icon, .grid-search-result:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .app-well-app:selected .overview-icon, .grid-search-result:selected .overview-icon, .app-well-app:focus .overview-icon, .grid-search-result:focus .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app:selected .overview-icon:hover .overview-icon, .grid-search-result:selected .overview-icon:hover .overview-icon, .app-well-app:focus .overview-icon:hover .overview-icon, .grid-search-result:focus .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app:selected .overview-icon:active .overview-icon, .grid-search-result:selected .overview-icon:active .overview-icon, .app-well-app:focus .overview-icon:active .overview-icon, .grid-search-result:focus .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app:active .overview-icon, .grid-search-result:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app:outlined .overview-icon, .grid-search-result:outlined .overview-icon, .app-well-app:checked .overview-icon, .grid-search-result:checked .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .app-well-app:outlined .overview-icon:active .overview-icon, .grid-search-result:outlined .overview-icon:active .overview-icon, .app-well-app:checked .overview-icon:active .overview-icon, .grid-search-result:checked .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .app-well-app:outlined .overview-icon:hover .overview-icon, .grid-search-result:outlined .overview-icon:hover .overview-icon, .app-well-app:checked .overview-icon:hover .overview-icon, .grid-search-result:checked .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .app-well-app:drop .overview-icon, .grid-search-result:drop .overview-icon {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+ .app-well-app .overview-icon, .grid-search-result .overview-icon {
+ padding: 12px; }
+ .app-well-app .overview-icon.overview-icon-with-label > StBoxLayout, .grid-search-result .overview-icon.overview-icon-with-label > StBoxLayout {
+ spacing: 6px; }
+
+.app-well-app.app-folder, .app-folder.grid-search-result {
+ transition-duration: 400ms; }
+ .app-well-app.app-folder .overview-icon, .app-folder.grid-search-result .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .app-well-app.app-folder:hover .overview-icon, .app-folder.grid-search-result:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .app-well-app.app-folder:selected .overview-icon, .app-folder.grid-search-result:selected .overview-icon, .app-well-app.app-folder:focus .overview-icon, .app-folder.grid-search-result:focus .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app.app-folder:selected .overview-icon:hover .overview-icon, .app-folder.grid-search-result:selected .overview-icon:hover .overview-icon, .app-well-app.app-folder:focus .overview-icon:hover .overview-icon, .app-folder.grid-search-result:focus .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app.app-folder:selected .overview-icon:active .overview-icon, .app-folder.grid-search-result:selected .overview-icon:active .overview-icon, .app-well-app.app-folder:focus .overview-icon:active .overview-icon, .app-folder.grid-search-result:focus .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app.app-folder:active .overview-icon, .app-folder.grid-search-result:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app.app-folder:outlined .overview-icon, .app-folder.grid-search-result:outlined .overview-icon, .app-well-app.app-folder:checked .overview-icon, .app-folder.grid-search-result:checked .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .app-well-app.app-folder:outlined .overview-icon:active .overview-icon, .app-folder.grid-search-result:outlined .overview-icon:active .overview-icon, .app-well-app.app-folder:checked .overview-icon:active .overview-icon, .app-folder.grid-search-result:checked .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .app-well-app.app-folder:outlined .overview-icon:hover .overview-icon, .app-folder.grid-search-result:outlined .overview-icon:hover .overview-icon, .app-well-app.app-folder:checked .overview-icon:hover .overview-icon, .app-folder.grid-search-result:checked .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .app-well-app.app-folder:drop .overview-icon, .app-folder.grid-search-result:drop .overview-icon {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.app-folder-dialog {
+ border-radius: 32px;
+ background-color: #383838; }
+ .app-folder-dialog .folder-name-container {
+ padding: 24px 36px 0;
+ spacing: 12px;
+ /* FIXME: this is to keep the label in sync with the entry */ }
+ .app-folder-dialog .folder-name-container .folder-name-entry {
+ width: 300px; }
+ .app-folder-dialog .folder-name-container .folder-name-label {
+ padding: 5px 7px;
+ color: white; }
+ .app-folder-dialog .folder-name-container .edit-folder-button {
+ padding: 0;
+ width: 36px;
+ height: 36px;
+ border-radius: 99px; }
+ .app-folder-dialog .folder-name-container .edit-folder-button > StIcon {
+ icon-size: 1.09em; }
+ .app-folder-dialog .icon-grid {
+ row-spacing: 12px;
+ column-spacing: 30px;
+ page-padding-top: 0;
+ page-padding-bottom: 0;
+ page-padding-left: 0;
+ page-padding-right: 0; }
+ .app-folder-dialog .page-indicators {
+ margin-bottom: 18px; }
+
+.app-folder-dialog-container {
+ padding: 12px;
+ width: 720px;
+ height: 720px; }
+
+.app-well-app-running-dot {
+ height: 5px;
+ width: 5px;
+ border-radius: 5px;
+ margin-bottom: 8px;
+ background-color: white; }
+
+.rename-folder-popup .rename-folder-popup-item {
+ spacing: 6px; }
+ .rename-folder-popup .rename-folder-popup-item:ltr, .rename-folder-popup .rename-folder-popup-item:rtl {
+ padding: 0 12px; }
+
+.page-indicator {
+ padding: 6px 12px 0;
+ transition-duration: 400ms; }
+ .page-indicator .page-indicator-icon {
+ width: 10px;
+ height: 10px;
+ border-radius: 10px;
+ background-color: white; }
+
+.apps-scroll-view {
+ padding: 0; }
+
+.system-action-icon {
+ background-color: rgba(0, 0, 0, 0.8);
+ color: #fff;
+ border-radius: 99px;
+ icon-size: 48px; }
+
+.page-navigation-hint.dnd {
+ background: rgba(255, 255, 255, 0.1); }
+
+.page-navigation-hint.next:ltr, .page-navigation-hint.previous:rtl {
+ background-gradient-start: rgba(255, 255, 255, 0.05);
+ background-gradient-end: transparent;
+ background-gradient-direction: horizontal;
+ border-radius: 24px 0px 0px 24px; }
+
+.page-navigation-hint.previous:ltr, .page-navigation-hint.next:rtl {
+ background-gradient-start: transparent;
+ background-gradient-end: rgba(255, 255, 255, 0.05);
+ background-gradient-direction: horizontal;
+ border-radius: 0px 24px 24px 0px; }
+
+.page-navigation-arrow > StIcon {
+ margin: 6px;
+ padding: 18px;
+ width: 24px;
+ height: 24px;
+ border-radius: 99px; }
+
+.page-navigation-arrow:insensitive > StIcon {
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none; }
+ .page-navigation-arrow:insensitive > StIcon:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+
+.page-navigation-arrow:hover > StIcon {
+ transition-duration: 100ms;
+ color: white;
+ background-color: rgba(72, 72, 72, 0.55);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(221, 221, 221, 0.235); }
+
+.page-navigation-arrow:active > StIcon {
+ transition-duration: 100ms;
+ color: white;
+ background-color: rgba(79, 79, 79, 0.55);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(222, 222, 222, 0.235); }
+
+/* Workspace pager */
+.workspace-thumbnails {
+ visible-width: 32px;
+ spacing: 6px;
+ padding: 6px; }
+ .workspace-thumbnails .workspace-thumbnail {
+ border-radius: 3px; }
+ .workspace-thumbnails .placeholder {
+ background-image: url("resource:///org/gnome/shell/theme/workspace-placeholder.svg");
+ background-size: contain;
+ width: 18px; }
+
+.workspace-thumbnail-indicator {
+ border: 3px solid #3584e4;
+ border-radius: 3px;
+ padding: 0px; }
+
+.ripple-pointer-location {
+ width: 50px;
+ height: 50px;
+ border-radius: 25px;
+ background-color: rgba(188, 214, 246, 0.3);
+ box-shadow: 0 0 2px 2px #8fbbf0; }
+
+.pie-timer {
+ width: 60px;
+ height: 60px;
+ -pie-border-width: 3px;
+ -pie-border-color: #3584e4;
+ -pie-background-color: rgba(233, 242, 252, 0.3); }
+
+.magnifier-zoom-region {
+ border: 2px solid #3584e4; }
+ .magnifier-zoom-region.full-screen {
+ border-width: 0; }
+
+.select-area-rubberband {
+ background-color: rgba(53, 132, 228, 0.3);
+ border: 1px solid #3584e4; }
+
+.user-icon {
+ background-size: contain;
+ color: white;
+ border-radius: 99px;
+ icon-size: 4.36em; }
+ .user-icon:hover {
+ color: white; }
+ .user-icon StIcon {
+ background-color: rgba(255, 255, 255, 0.05);
+ border-radius: 99px;
+ padding: 12px;
+ width: 2.725em;
+ height: 2.725em; }
+ .user-icon.user-avatar {
+ border: 2px white; }
+
+.user-widget.vertical .user-icon {
+ icon-size: 6.54em; }
+ .user-widget.vertical .user-icon StIcon {
+ padding: 20px;
+ padding-top: 18px;
+ padding-bottom: 22px;
+ width: 5.995em;
+ height: 5.995em; }
+
+.lightbox {
+ background-color: black; }
+
+.flashspot {
+ background-color: white; }
+
+.hidden {
+ color: rgba(0, 0, 0, 0); }
+
+.caps-lock-warning-label {
+ text-align: center;
+ padding-bottom: 8px;
+ color: #cd9309; }
+
+/* Workspace animation */
+.workspace-animation {
+ background-color: #2b2b2b; }
+
+/* Tiled window previews */
+.tile-preview {
+ background-color: rgba(53, 132, 228, 0.5);
+ border: 1px solid #3584e4; }
+
+/* On-screen Keyboard */
+#keyboard {
+ background-color: #000;
+ box-shadow: inset 0 1px 0 0 #000; }
+ #keyboard .page-indicator {
+ padding: 6px; }
+ #keyboard .page-indicator .page-indicator-icon {
+ width: 8px;
+ height: 8px; }
+
+.key-container {
+ padding: 4px;
+ spacing: 4px; }
+
+.keyboard-key {
+ font-size: 16pt;
+ font-weight: bold;
+ min-height: 1.2em;
+ min-width: 1.2em;
+ border-width: 1px;
+ border-style: solid;
+ border-radius: 12px;
+ box-shadow: 0 1px 0 0 transparent;
+ color: white;
+ background-color: #545454; }
+ .keyboard-key:focus {
+ color: white;
+ background-color: #0c141e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .keyboard-key:focus:hover {
+ background-color: #182029;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-key:focus:active {
+ background-color: #1c242e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-key:hover {
+ color: white;
+ background-color: #5e5e5e; }
+ .keyboard-key:active {
+ color: white;
+ background-color: #666666; }
+ .keyboard-key:checked {
+ color: white;
+ background-color: #737373; }
+ .keyboard-key:grayed {
+ background-color: black;
+ color: white;
+ border-color: #000; }
+ .keyboard-key.default-key {
+ color: white;
+ background-color: #3b3b3b;
+ border-radius: 12px; }
+ .keyboard-key.default-key:hover {
+ color: white;
+ background-color: #454545; }
+ .keyboard-key.default-key:active {
+ color: white;
+ background-color: #4d4d4d; }
+ .keyboard-key.default-key:checked {
+ color: white;
+ background-color: #595959; }
+ .keyboard-key.default-key:latched {
+ border-color: #4b92e7;
+ background-color: #3584e4; }
+ .keyboard-key.enter-key {
+ color: #ffffff;
+ background-color: #428ce6;
+ border-radius: 12px;
+ color: white; }
+ .keyboard-key.enter-key:hover {
+ color: #ffffff;
+ background-color: #5497e8; }
+ .keyboard-key.enter-key:active {
+ color: #ffffff;
+ background-color: #629fea; }
+ .keyboard-key.enter-key:checked {
+ color: #ffffff;
+ background-color: #78aded; }
+ .keyboard-key.shift-key-uppercase {
+ background-color: gray;
+ color: #000; }
+ .keyboard-key.shift-key-uppercase:hover {
+ background-color: #8c8c8c;
+ color: #0d0d0d; }
+ .keyboard-key StIcon {
+ icon-size: 24px; }
+
+.keyboard-subkeys {
+ -arrow-border-radius: 16px;
+ -arrow-background-color: #000;
+ -arrow-border-width: 1px;
+ -arrow-border-color: #171717;
+ -arrow-base: 20px;
+ -arrow-rise: 10px;
+ -boxpointer-gap: 6px;
+ padding: 6px; }
+ .keyboard-subkeys .keyboard-key {
+ color: white;
+ background-color: #545454;
+ border-radius: 12px; }
+ .keyboard-subkeys .keyboard-key:focus {
+ color: white;
+ background-color: #0c141e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .keyboard-subkeys .keyboard-key:focus:hover {
+ background-color: #182029;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-subkeys .keyboard-key:focus:active {
+ background-color: #1c242e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-subkeys .keyboard-key:hover {
+ color: white;
+ background-color: #5e5e5e; }
+ .keyboard-subkeys .keyboard-key:active {
+ color: white;
+ background-color: #666666; }
+ .keyboard-subkeys .keyboard-key:checked {
+ color: white;
+ background-color: #737373; }
+
+.emoji-page .keyboard-key {
+ background-color: transparent;
+ border: none;
+ color: initial; }
+
+.emoji-panel .keyboard-key:latched {
+ border-color: #4b92e7;
+ background-color: #3584e4; }
+
+.word-suggestions {
+ font-size: 12pt;
+ spacing: 12px;
+ min-height: 17pt;
+ padding: 12px;
+ color: white; }
+ .word-suggestions StButton {
+ margin: 0 3px;
+ min-width: 32px;
+ border-radius: 6px;
+ padding: 0px 18px;
+ background-color: transparent;
+ background-image: none; }
+ .word-suggestions StButton:focus {
+ color: white;
+ background-color: #0c141e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .word-suggestions StButton:focus:hover {
+ background-color: #182029;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .word-suggestions StButton:focus:active {
+ background-color: #1c242e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .word-suggestions StButton:hover {
+ color: white;
+ background-color: #5e5e5e; }
+ .word-suggestions StButton:active {
+ color: white;
+ background-color: #666666; }
+ .word-suggestions StButton:checked {
+ color: white;
+ background-color: #737373; }
+
+/* Looking Glass */
+#LookingGlassDialog {
+ background-color: #000;
+ border-radius: 0 0 16px 16px;
+ border-top-width: 0;
+ border: 1px solid #000;
+ color: white;
+ padding: 6px;
+ spacing: 6px;
+ box-shadow: 0 2px 4px 0 transparent; }
+ #LookingGlassDialog > #Toolbar {
+ border: none;
+ padding: 6px;
+ border-radius: 0;
+ background-color: transparent;
+ spacing: 6px; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button {
+ padding: 6px 12px; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button > StIcon {
+ icon-size: 1.09em; }
+ #LookingGlassDialog .labels {
+ spacing: 6px; }
+ #LookingGlassDialog .notebook-tab {
+ -natural-hpadding: 12px;
+ -minimum-hpadding: 12px;
+ font-weight: bold;
+ padding: 6px 12px;
+ color: #d9d9d9;
+ transition-duration: 100ms;
+ box-shadow: none;
+ border: none;
+ border-radius: 6px;
+ background-color: transparent; }
+ #LookingGlassDialog .notebook-tab:hover {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.05); }
+ #LookingGlassDialog .notebook-tab:selected {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1); }
+ #LookingGlassDialog StBoxLayout#EvalBox {
+ padding: 4px;
+ spacing: 6px;
+ padding: 6px; }
+ #LookingGlassDialog StBoxLayout#ResultsArea {
+ spacing: 6px;
+ padding: 6px; }
+
+.lg-dialog StEntry {
+ background-color: rgba(13, 13, 13, 0.6);
+ color: white;
+ border-color: rgba(255, 255, 255, 0.2);
+ min-height: 22px;
+ selection-background-color: #3584e4;
+ selected-color: #ffffff; }
+
+.lg-dialog .shell-link {
+ color: #8fbbf0; }
+ .lg-dialog .shell-link:hover {
+ color: #bcd6f6; }
+ .lg-dialog .shell-link:active {
+ color: #629fea; }
+
+.lg-dialog .actor-link {
+ color: gray; }
+ .lg-dialog .actor-link:hover {
+ color: #b3b3b3; }
+ .lg-dialog .actor-link:active {
+ color: #4d4d4d; }
+ .lg-dialog .actor-link StIcon {
+ icon-size: 12px; }
+
+.lg-completions-text {
+ font-style: italic; }
+
+.lg-obj-inspector-title {
+ spacing: 6px; }
+
+.lg-obj-inspector-button {
+ border: 1px solid #000;
+ padding: 4px;
+ border-radius: 8px; }
+ .lg-obj-inspector-button:hover {
+ border: 1px solid #ffffff; }
+
+#lookingGlassExtensions {
+ padding: 6px; }
+
+.lg-extensions-list {
+ padding: 6px;
+ spacing: 6px; }
+
+.lg-extension {
+ padding: 12px;
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px #1f1f1f;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353535;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
+ .lg-extension:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #353d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .lg-extension:focus:hover {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .lg-extension:focus:active {
+ background-color: #434b54;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .lg-extension:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(158, 158, 158, 0.28); }
+ .lg-extension:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #444444;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+ background-color: rgba(162, 162, 162, 0.28); }
+
+.lg-extension-meta {
+ spacing: 6px; }
+
+#LookingGlassPropertyInspector {
+ background: #000;
+ border: 1px solid #000;
+ border-radius: 8px;
+ padding: 6px; }
+
+.lg-debug-flag-button {
+ color: white; }
+ .lg-debug-flag-button StLabel {
+ padding: 6px, 12px; }
+ .lg-debug-flag-button:hover {
+ color: white; }
+ .lg-debug-flag-button:active {
+ color: #cccccc; }
+
+.lg-debug-flags-header {
+ padding-top: 12px;
+ padding: 6px; }
+
+/* Login Dialog */
+.login-dialog-banner-view {
+ padding-top: 24px;
+ max-width: 23em; }
+
+.login-dialog,
+.unlock-dialog {
+ border: none;
+ background-color: transparent; }
+ .login-dialog .modal-dialog-button-box,
+ .unlock-dialog .modal-dialog-button-box {
+ spacing: 3px; }
+ .login-dialog .modal-dialog-button,
+ .unlock-dialog .modal-dialog-button {
+ padding: 4px 18px;
+ background-color: #242424;
+ border-color: #242424;
+ color: white; }
+ .login-dialog .modal-dialog-button:hover, .login-dialog .modal-dialog-button:focus,
+ .unlock-dialog .modal-dialog-button:hover,
+ .unlock-dialog .modal-dialog-button:focus {
+ background-color: #383838;
+ border-color: #383838; }
+ .login-dialog .modal-dialog-button:active,
+ .unlock-dialog .modal-dialog-button:active {
+ background-color: #1f1f1f;
+ border-color: #1f1f1f; }
+ .login-dialog .modal-dialog-button:insensitive,
+ .unlock-dialog .modal-dialog-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ border-color: #1f1f1f;
+ background-color: #1f1f1f;
+ color: rgba(255, 255, 255, 0.7); }
+ .login-dialog .modal-dialog-button:default,
+ .unlock-dialog .modal-dialog-button:default {
+ transition-duration: 100ms;
+ background-color: #3584e4;
+ color: #ffffff;
+ box-shadow: none; }
+ .login-dialog .modal-dialog-button:default:focus,
+ .unlock-dialog .modal-dialog-button:default:focus {
+ box-shadow: inset 0 0 0 2px #629fea; }
+ .login-dialog .modal-dialog-button:default:hover, .login-dialog .modal-dialog-button:default:focus,
+ .unlock-dialog .modal-dialog-button:default:hover,
+ .unlock-dialog .modal-dialog-button:default:focus {
+ background-color: #4b92e7;
+ color: white; }
+ .login-dialog .modal-dialog-button:default:active,
+ .unlock-dialog .modal-dialog-button:default:active {
+ background-color: #1d72d8;
+ color: #ededed; }
+ .login-dialog .modal-dialog-button:default:insensitive,
+ .unlock-dialog .modal-dialog-button:default:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0);
+ background-color: rgba(53, 132, 228, 0.5);
+ color: rgba(255, 255, 255, 0.5); }
+ .login-dialog .cancel-button,
+ .login-dialog .switch-user-button,
+ .login-dialog .login-dialog-session-list-button,
+ .unlock-dialog .cancel-button,
+ .unlock-dialog .switch-user-button,
+ .unlock-dialog .login-dialog-session-list-button {
+ padding: 0;
+ border-radius: 99px;
+ width: 2.18em;
+ height: 2.18em;
+ border-color: #242424;
+ background-color: #242424; }
+ .login-dialog .cancel-button StIcon,
+ .login-dialog .switch-user-button StIcon,
+ .login-dialog .login-dialog-session-list-button StIcon,
+ .unlock-dialog .cancel-button StIcon,
+ .unlock-dialog .switch-user-button StIcon,
+ .unlock-dialog .login-dialog-session-list-button StIcon {
+ icon-size: 1.09em; }
+ .login-dialog .caps-lock-warning-label,
+ .login-dialog .login-dialog-message-warning,
+ .unlock-dialog .caps-lock-warning-label,
+ .unlock-dialog .login-dialog-message-warning {
+ color: white; }
+
+.login-dialog-logo-bin {
+ padding: 24px 0px; }
+
+.login-dialog-banner {
+ color: #e6e6e6; }
+
+.login-dialog-button-box {
+ width: 23em;
+ spacing: 5px; }
+
+.login-dialog-message {
+ text-align: center; }
+
+.login-dialog-user-selection-box {
+ padding: 100px 0px; }
+
+.login-dialog-not-listed-label {
+ padding-left: 2px; }
+ .login-dialog-not-listed-button:focus .login-dialog-not-listed-label, .login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
+ color: white; }
+
+.login-dialog-not-listed-label {
+ font-weight: bold;
+ color: #b3b3b3;
+ padding-top: 1em; }
+
+.login-dialog-auth-list-view {
+ -st-vfade-offset: 1em; }
+
+.login-dialog-auth-list {
+ spacing: 6px;
+ margin-left: 2em; }
+
+.login-dialog-auth-list-title {
+ margin-left: 2em; }
+
+.login-dialog-auth-list-item {
+ border-radius: 12px;
+ padding: 6px;
+ color: #b3b3b3; }
+ .login-dialog-auth-list-item:focus, .login-dialog-auth-list-item:selected {
+ background-color: #3584e4;
+ color: #ffffff; }
+
+.login-dialog-auth-list-label {
+ font-size: 13pt;
+ font-weight: bold;
+ padding-left: 15px; }
+ .login-dialog-auth-list-label:ltr {
+ padding-left: 14px;
+ text-align: left; }
+ .login-dialog-auth-list-label:rtl {
+ padding-right: 14px;
+ text-align: right; }
+
+.login-dialog-user-list-view {
+ -st-vfade-offset: 1em; }
+
+.login-dialog-user-list {
+ spacing: 12px;
+ width: 23em; }
+ .login-dialog-user-list:expanded .login-dialog-user-list-item:selected {
+ background-color: #3584e4;
+ color: #ffffff; }
+ .login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
+ border-right: 2px solid #3584e4; }
+
+.login-dialog-user-list-item {
+ border-radius: 12px;
+ padding: 6px;
+ color: #b3b3b3; }
+ .login-dialog-user-list-item:ltr .user-widget {
+ padding-right: 1em; }
+ .login-dialog-user-list-item:rtl .user-widget {
+ padding-left: 1em; }
+ .login-dialog-user-list-item .login-dialog-timed-login-indicator {
+ height: 2px;
+ margin-top: 6px;
+ background-color: white; }
+ .login-dialog-user-list-item:focus .login-dialog-timed-login-indicator {
+ background-color: #ffffff; }
+
+.user-widget-label {
+ color: white; }
+
+.user-widget.horizontal .user-widget-label {
+ font-size: 13pt;
+ font-weight: bold;
+ padding-left: 15px; }
+ .user-widget.horizontal .user-widget-label:ltr {
+ padding-left: 14px;
+ text-align: left; }
+ .user-widget.horizontal .user-widget-label:rtl {
+ padding-right: 14px;
+ text-align: right; }
+
+.user-widget.vertical .user-widget-label {
+ font-size: 16pt;
+ text-align: center;
+ font-weight: normal;
+ padding-top: 16px; }
+
+.login-dialog-timed-login-indicator {
+ height: 2px;
+ background-color: #999999; }
+
+.login-dialog-prompt-layout {
+ padding-top: 24px;
+ padding-bottom: 12px;
+ spacing: 12px;
+ width: 23em; }
+
+.login-dialog-prompt-entry {
+ height: 1.5em; }
+
+.login-dialog-prompt-label {
+ color: #cccccc;
+ font-size: 12pt;
+ padding-top: 1em; }
+
+.login-dialog StEntry {
+ background-color: #242424;
+ color: white; }
+
+.unlock-dialog StEntry {
+ border: none !important; }
+ .unlock-dialog StEntry:focus {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .unlock-dialog StEntry:insensitive {
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05); }
+
+.unlock-dialog .cancel-button,
+.unlock-dialog .switch-user-button,
+.unlock-dialog .login-dialog-session-list-button {
+ border-color: transparent;
+ background-color: rgba(255, 255, 255, 0.1); }
+
+/* Screen Shield */
+.unlock-dialog-clock {
+ color: white;
+ font-weight: 300;
+ text-align: center;
+ spacing: 24px;
+ padding-bottom: 2.5em; }
+
+.unlock-dialog-clock-time {
+ font-size: 64pt;
+ padding-top: 42px; }
+
+.unlock-dialog-clock-date {
+ font-size: 16pt;
+ font-weight: normal; }
+
+.unlock-dialog-clock-hint {
+ font-weight: normal;
+ padding-top: 48px; }
+
+.unlock-dialog-notifications-container {
+ margin: 12px;
+ spacing: 6px;
+ width: 23em;
+ background-color: transparent; }
+ .unlock-dialog-notifications-container .summary-notification-stack-scrollview {
+ padding-top: 0;
+ padding-bottom: 0; }
+ .unlock-dialog-notifications-container .notification,
+ .unlock-dialog-notifications-container .unlock-dialog-notification-source {
+ padding: 12px 16px;
+ border: none;
+ background-color: rgba(255, 255, 255, 0.1);
+ color: white;
+ border-radius: 16px; }
+ .unlock-dialog-notifications-container .notification.critical,
+ .unlock-dialog-notifications-container .unlock-dialog-notification-source.critical {
+ background-color: rgba(255, 255, 255, 0.2); }
+
+.unlock-dialog-notification-label {
+ padding-left: 12px;
+ padding-right: 0; }
+ .unlock-dialog-notification-label:rtl {
+ padding-right: 12px;
+ padding-left: 0; }
+
+.unlock-dialog-notification-count-text {
+ font-weight: bold;
+ padding: 0 12px;
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1);
+ border-radius: 99px; }
+
+.screen-shield-background {
+ background: black;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6); }
+
+#lockDialogGroup {
+ background-color: #2b2b2b; }
+
+#unlockDialogNotifications StButton#vhandle, #unlockDialogNotifications StButton#hhandle {
+ background-color: rgba(0, 0, 0, 0.3); }
+ #unlockDialogNotifications StButton#vhandle:hover, #unlockDialogNotifications StButton#vhandle:focus, #unlockDialogNotifications StButton#hhandle:hover, #unlockDialogNotifications StButton#hhandle:focus {
+ background-color: rgba(0, 0, 0, 0.5); }
+ #unlockDialogNotifications StButton#vhandle:active, #unlockDialogNotifications StButton#hhandle:active {
+ background-color: rgba(53, 132, 228, 0.5); }
+
+stage {
+ -st-icon-style: symbolic; }
+
+.toggle-switch {
+ width: 48px; }
+
+.toggle-switch {
+ background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); }
+ .toggle-switch:checked {
+ background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); }
+
+#panel.login-screen, #panel.unlock-screen, #panel:overview {
+ background-color: #000; }
+
+#panel .panel-button {
+ color: #fff !important; }
+
+.window-picker .icon-dropshadow {
+ icon-shadow: none;
+ background-color: #000;
+ padding: 12px;
+ border-radius: 16px; }
diff --git a/data/theme/gnome-shell-high-contrast.scss b/data/theme/gnome-shell-high-contrast.scss
new file mode 100644
index 0000000..55745f1
--- /dev/null
+++ b/data/theme/gnome-shell-high-contrast.scss
@@ -0,0 +1,38 @@
+$variant: 'dark';
+
+@import "gnome-shell-sass/_high-contrast-colors"; //use gtk colors
+@import "gnome-shell-sass/_drawing";
+@import "gnome-shell-sass/_common";
+@import "gnome-shell-sass/_widgets";
+
+//force symbolic icons
+stage {
+ -st-icon-style: symbolic;
+}
+
+.toggle-switch { width: 48px; }
+.toggle-switch {
+ background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg");
+ &:checked { background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); }
+}
+
+//force opaque panel
+#panel {
+ &.login-screen,
+ &.unlock-screen,
+ &:overview {
+ background-color: #000;
+ }
+
+ .panel-button {
+ color: #fff !important;
+ }
+}
+
+//thumbnail app icons no dropshadow and forced color
+.window-picker .icon-dropshadow {
+ icon-shadow: none;
+ background-color: $osd_bg_color;
+ padding: $base_padding * 2;
+ border-radius: $modal_radius;
+}
diff --git a/data/theme/gnome-shell-sass/COPYING b/data/theme/gnome-shell-sass/COPYING
new file mode 100644
index 0000000..e55e5b8
--- /dev/null
+++ b/data/theme/gnome-shell-sass/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/data/theme/gnome-shell-sass/NEWS b/data/theme/gnome-shell-sass/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/data/theme/gnome-shell-sass/NEWS
diff --git a/data/theme/gnome-shell-sass/README.md b/data/theme/gnome-shell-sass/README.md
new file mode 100644
index 0000000..443b4dc
--- /dev/null
+++ b/data/theme/gnome-shell-sass/README.md
@@ -0,0 +1,16 @@
+# GNOME Shell Sass
+GNOME Shell Sass is a project intended to allow the sharing of the
+theme sources in sass between gnome-shell and other projects like
+gnome-shell-extensions.
+
+Any changes should be done in the [GNOME Shell subtree][shell-subtree]
+and not the stand-alone [gnome-shell-sass repository][sass-repo]. They
+will then be synchronized periodically before releases.
+
+## License
+GNOME Shell Sass is distributed under the terms of the GNU General Public
+License, version 2 or later. See the [COPYING][license] file for details.
+
+[shell-subtree]: https://gitlab.gnome.org/GNOME/gnome-shell/tree/HEAD/data/theme/gnome-shell-sass
+[sass-repo]: https://gitlab.gnome.org/GNOME/gnome-shell-sass
+[license]: COPYING
diff --git a/data/theme/gnome-shell-sass/_colors.scss b/data/theme/gnome-shell-sass/_colors.scss
new file mode 100644
index 0000000..b83b1ed
--- /dev/null
+++ b/data/theme/gnome-shell-sass/_colors.scss
@@ -0,0 +1,75 @@
+// When color definition differs for dark and light variant,
+// it gets @if-ed depending on $variant
+
+@import '_palette.scss';
+
+$is_highcontrast: "false";
+
+$_dark_base_color: darken(desaturate(#241f31, 100%), 2%);
+
+$base_color: if($variant == 'light', #fff, $_dark_base_color);
+$bg_color: if($variant == 'light', #f6f5f4, lighten($base_color, 5%));
+$fg_color: if($variant == 'light', transparentize(black, .2), white);
+
+$selected_fg_color: #fff;
+$selected_bg_color: #3584e4;
+$selected_borders_color: if($variant== 'light', darken($selected_bg_color, 15%), darken($selected_bg_color, 30%));
+
+$borders_color: if($variant == 'light', transparentize($fg_color, .5), transparentize($fg_color, .9));
+$borders_edge: if($variant == 'light', rgba(255,255,255,0.8), lighten($bg_color, 5%));
+
+$link_color: if($variant == 'light', darken($selected_bg_color, 10%), lighten($selected_bg_color, 20%));
+$link_visited_color: if($variant == 'light', darken($selected_bg_color, 20%), lighten($selected_bg_color, 10%));
+
+$warning_color: if($variant == 'light', $yellow_5, #cd9309);
+$error_color: if($variant == 'light', $red_3, $red_4);
+$success_color: if($variant == 'light', $green_4, $green_5);
+$destructive_color: $error_color;
+
+$osd_fg_color: white;
+$osd_bg_color: $_dark_base_color; //hardcoded for both light & dark
+$osd_insensitive_bg_color: transparentize(mix($osd_fg_color, opacify($osd_bg_color, 1), 10%), 0.5);
+$osd_insensitive_fg_color: if($variant == 'light', mix($osd_fg_color, $osd_bg_color, 80%), mix($osd_fg_color, $osd_bg_color, 70%));
+$osd_borders_color: transparentize(black, 0.3);
+$osd_outer_borders_color: transparentize($osd_fg_color, 0.98);
+
+$shadow_color: if($variant == 'light', rgba(0,0,0,0.1), rgba(0,0,0,0.2));
+
+// button
+$button_mix_factor: 5%;
+
+// cards
+$card_bg_color: if($variant == 'light', darken($bg_color, 5%), lighten($bg_color, 2%));
+$card_outer_borders_color: transparentize($fg_color, 0.98);
+
+// notifications
+$bubble_buttons_color: if($variant == 'light', darken($bg_color, 12%), lighten($bg_color, 10%));
+
+// overview background color
+$system_bg_color: $base_color;
+
+//insensitive state derived colors
+$insensitive_fg_color: mix($fg_color, $bg_color, 50%);
+$insensitive_bg_color: mix($bg_color, $base_color, 60%);
+$insensitive_borders_color: mix($borders_color, $base_color, 60%);
+
+//colors for the backdrop state, derived from the main colors.
+$backdrop_base_color: if($variant =='light', darken($base_color,1%), lighten($base_color,1%));
+$backdrop_bg_color: $bg_color;
+$backdrop_fg_color: mix($fg_color, $backdrop_bg_color, 80%);
+$backdrop_insensitive_color: if($variant =='light', darken($backdrop_bg_color,15%), lighten($backdrop_bg_color,15%));
+$backdrop_borders_color: mix($borders_color, $bg_color, 90%);
+$backdrop_dark_fill: mix($backdrop_borders_color,$backdrop_bg_color, 35%);
+
+
+// derived checked colors
+$checked_bg_color: if($variant=='light', darken($bg_color, 7%), lighten($bg_color, 7%));
+$checked_fg_color: if($variant=='light', darken($fg_color, 7%), lighten($fg_color, 7%));
+
+// derived hover colors
+$hover_bg_color: if($variant=='light', darken($bg_color, 3%), lighten($bg_color, 10%));
+$hover_fg_color: if($variant=='light', darken($fg_color, 5%), lighten($fg_color, 10%));
+
+// derived active colors
+$active_bg_color: if($variant=='light', darken($bg_color, 5%), lighten($bg_color, 12%));
+$active_fg_color: if($variant=='light', darken($fg_color, 5%), lighten($fg_color, 12%));
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
new file mode 100644
index 0000000..9ca89d1
--- /dev/null
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -0,0 +1,222 @@
+//This is the RIGHT PLACE to edit the stylesheet
+
+//let's start by telling people not to edit the generated CSS:
+$cakeisalie: "This stylesheet is generated, DO NOT EDIT";
+/* #{$cakeisalie} */
+
+/* Copyright 2009, 2015 Red Hat, Inc.
+ *
+ * Portions adapted from Mx's data/style/default.css
+ * Copyright 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Global Values */
+
+// padding, margin and spacing
+$base_padding: 6px;
+$base_margin: 4px;
+
+// border radii
+$base_border_radius: 8px;
+
+// radii of things that display over other things, e.g. popovers
+$modal_radius: $base_border_radius*2; // 24px
+
+// Chroma key to flag when a background-color is always occluded, not visible.
+// This allows any box-shadow behind it to be rendered more efficiently by
+// omitting the middle rectangle.
+$invisible_occluded_bg_color: rgba(3,2,1,0);
+
+// fonts
+$base_font_size: 11;
+$text_shadow_color: if($variant == 'light', rgba(255,255,255,0.3), rgba(0,0,0,0.2));
+
+// icons
+$base_icon_size: 1.09em;
+$large_icon_size: $base_icon_size*2; // 32px
+// $base_icon_size: 16px;
+
+// Stage
+stage {
+ @include fontsize($base_font_size);
+ color: $fg_color;
+}
+
+/* Common Stylings */
+
+// osd panels
+%osd_panel {
+ color: $osd_fg_color;
+ background-color: $osd_bg_color;
+ border: 1px solid $osd_outer_borders_color;
+ border-radius: 999px;
+ padding: $base_padding*2;
+}
+
+// Overview panels
+// for the dash and workspace switcher
+%overview_panel {
+ color: $osd_fg_color;
+ background-color: transparentize($osd_fg_color, 0.9);
+}
+
+// icon tiles
+%tile {
+ border-radius: $base_border_radius * 2; // 16px
+ padding: $base_padding;
+ spacing: $base_padding;
+ border: 2px solid transparent;
+ transition-duration: 200ms;
+ text-align: center;
+}
+
+// dialogs
+%bubble_panel {
+ color: $fg_color;
+ background-color: $bg_color;
+ border-radius: $base_border_radius*1.25 + 1px;
+ border: 1px solid $borders_edge;
+}
+
+// normal button styling
+%button {
+ border-radius: $base_border_radius - 2px; // 6px
+ border-style: solid;
+ border-width: 1px;
+ font-weight: bold;
+ padding: $base_padding*.5 $base_padding*4;
+
+ @include button(normal);
+ &:focus { @include button(focus);}
+ &:hover { @include button(hover);}
+ &:insensitive { @include button(insensitive);}
+ &:active { @include button(active);}
+ &:checked { @include button(checked);}
+
+ &.flat {
+ @include button(normal, $flat:true);
+ &:focus { @include button(focus, $flat:true);}
+ &:hover { @include button(hover, $flat:true);}
+ &:insensitive { @include button(insensitive, $flat:true);}
+ &:active { @include button(active, $flat:true);}
+ &:checked { @include button(checked, $flat:true);}
+ }
+}
+
+// buttons in dialogs/notifications
+// lighter in color and have a greater radius
+
+$bubble_button_radius:$base_border_radius*1.25;
+
+%bubble_button {
+ padding: $base_padding * 2;
+ font-weight: bold !important;
+
+ &:ltr {margin-right: 1px;}
+ &:rtl {margin-left: 1px;}
+
+ @include button(normal, $c:$bubble_buttons_color);
+ &:insensitive { @include button(insensitive, $c:$bubble_buttons_color);}
+ &:focus { @include button(focus, $c:$bubble_buttons_color);}
+ &:hover { @include button(hover, $c:$bubble_buttons_color);}
+ &:active { @include button(active, $c:$bubble_buttons_color);}
+ &:checked { @include button(checked, $c:$bubble_buttons_color);}
+
+ &:first-child:ltr {
+ border-radius: 0 0 0 $bubble_button_radius;
+ }
+
+ &:last-child:ltr {
+ border-radius: 0 0 $bubble_button_radius 0;
+ margin-right: 0 !important;
+ }
+
+ &:first-child:rtl {
+ border-radius: 0 0 $bubble_button_radius 0;
+ }
+
+ &:last-child:rtl {
+ border-radius: 0 0 0 $bubble_button_radius;
+ margin-left: 0 !important;
+ }
+
+ &:first-child:last-child {
+ border-radius: 0 0 $bubble_button_radius $bubble_button_radius !important;
+ margin-left: 0 !important;
+ margin-right: 0 !important;
+ }
+}
+
+// buttons on OSD elements
+// that are undecorated by default and use OSD colors
+%osd_button {
+ @include button(undecorated);
+ &:insensitive { @include button(undecorated, $tc:$osd_fg_color, $c:$osd_bg_color);}
+ &:focus { @include button(focus, $tc:$osd_fg_color, $c:$osd_bg_color);}
+ &:hover { @include button(hover, $tc:$osd_fg_color, $c:$osd_bg_color);}
+ &:active { @include button(active, $tc:$osd_fg_color, $c:$osd_bg_color);}
+ &:outlined,&:checked { @include button(checked, $tc:$osd_fg_color, $c:$osd_bg_color);}
+}
+
+/* General Typography */
+
+%large_title {
+ font-weight: 300;
+ @include fontsize(24);
+}
+
+%title_1 {
+ font-weight: 800;
+ @include fontsize(20);
+}
+
+%title_2 {
+ font-weight: 800;
+ @include fontsize(15);
+}
+
+%title_3 {
+ font-weight: 700;
+ @include fontsize(15);
+}
+
+%title_4 {
+ font-weight: 700;
+ @include fontsize(13);
+}
+
+%heading {
+ font-weight: 700;
+ @include fontsize(11);
+}
+
+%caption_heading {
+ font-weight: 700;
+ @include fontsize(9);
+}
+
+%caption {
+ font-weight: 400;
+ @include fontsize(9);
+}
+
+%smaller {
+ font-weight: 400;
+ @include fontsize(8);
+}
+
+%monospace {font-family: monospace;}
+%numeric { font-feature-settings: "tnum";}
diff --git a/data/theme/gnome-shell-sass/_drawing.scss b/data/theme/gnome-shell-sass/_drawing.scss
new file mode 100644
index 0000000..7c8959e
--- /dev/null
+++ b/data/theme/gnome-shell-sass/_drawing.scss
@@ -0,0 +1,336 @@
+// Drawing mixins
+
+// generic drawing of more complex things
+
+@function draw_widget_edge($c:$borders_edge) {
+// outer highlight "used" on most widgets
+ @return 0 1px $c;
+}
+
+// provide font size in rem, with px fallback
+@mixin fontsize($size: 24, $base: 16) {
+ font-size: round($size) + pt;
+ //font-size: ($size / $base) * 1rem;
+}
+
+@mixin draw_shadows($shadow1, $shadow2:none, $shadow3:none, $shadow4:none) {
+//
+// Helper function to stack up to 4 box-shadows;
+//
+ @if $shadow4!=none { box-shadow: $shadow1, $shadow2, $shadow3, $shadow4; }
+ @else if $shadow3!=none { box-shadow: $shadow1, $shadow2, $shadow3; }
+ @else if $shadow2!=none { box-shadow: $shadow1, $shadow2; }
+ @else { box-shadow: $shadow1; }
+}
+
+// entries
+
+@mixin entry($t, $fc:$selected_bg_color) {
+//
+// Entries drawing function
+//
+// $t: entry type
+// $fc: focus color
+//
+// possible $t values:
+// normal, focus, insensitive
+//
+
+ @if $t==normal {
+ background-color: lighten($bg_color, 5%);
+ color: transparentize($fg_color,0.3);
+ border: 2px solid lighten($bg_color, 5%);
+ }
+
+ @if $t==focus {
+ background-color: mix(lighten($bg_color, 5%), $selected_bg_color, 95%);
+ border-color: $fc;
+ color: $fg_color;
+ &:hover {}
+ }
+
+ @if $t==hover {
+ background-color:lighten($hover_bg_color, 5%);
+ border-color:lighten($hover_bg_color, 5%);
+ color: transparentize($fg_color,0.3);
+ }
+
+ @if $t==insensitive {
+ background-color:lighten($insensitive_bg_color, 5%);
+ border-color: lighten($insensitive_bg_color, 5%);
+ color: $insensitive_fg_color;
+ }
+}
+
+// On-screen Keyboard
+@mixin keyboard_key($t, $c:$osd_bg_color, $tc:$osd_fg_color) {
+//
+// Keyboard key drawing function
+//
+// $t: key type,
+// $c: base key color for colored* types
+// $tc: optional text color for colored* types
+//
+// possible $t values:
+// normal, hover, active, insensitive, insensitive-active,
+// backdrop, backdrop-active, backdrop-insensitive, backdrop-insensitive-active,
+// osd, osd-hover, osd-active, osd-insensitive, osd-backdrop, undecorated
+//
+
+ // normal key
+ @if $t==normal {
+ color: $tc;
+ background-color: lighten($c, 3%);
+ }
+
+ // focused key
+ @if $t==focus {
+ color: $tc;
+ background-color: mix(lighten($c, 3%), $selected_bg_color, 90%);
+ box-shadow: inset 0 0 0 2px transparentize($selected_bg_color, 0.4);
+ &:hover {
+ background-color: mix(lighten($c, 8%), $selected_bg_color, 90%);
+ box-shadow: inset 0 0 0 2px transparentize($selected_bg_color, 0.3);
+ }
+ &:active {
+ background-color: mix(lighten($c, 10%), $selected_bg_color, 90%);
+ box-shadow: inset 0 0 0 2px transparentize($selected_bg_color, 0.3);
+ }
+ }
+
+ // hover key
+ @else if $t==hover {
+ color: $tc;
+ background-color: lighten($c, 7%);
+ }
+
+ // active key
+ @else if $t==active {
+ color: $tc;
+ background-color: lighten($c, 10%);
+ }
+
+ // checked key
+ @else if $t==checked {
+ color: $tc;
+ background-color: lighten($c, 15%);
+ }
+
+ // insensitive key
+ @else if $t==insensitive {
+ color: $insensitive_fg_color;
+ background-color: $insensitive_bg_color;
+ }
+
+ // reset
+ @else if $t==undecorated {
+ background-color: transparent;
+ background-image: none;
+ }
+}
+
+//
+// Button drawing function
+//
+// $t: button type,
+// $c: base button colors, derived from fg_color
+// $tc: base button colors, derived from fg_color
+//
+// possible $t values:
+// normal, hover, active, insensitive, insensitive-active,
+// backdrop, backdrop-active, backdrop-insensitive, backdrop-insensitive-active,
+// osd, osd-hover, osd-active, osd-insensitive, osd-backdrop, undecorated
+//
+// since buttons are all flat an borderless now the mixin is simpler
+
+@mixin button($t, $tc:$fg_color, $c:$bg_color, $flat: false) {
+
+ $button_bg_color: mix($tc, $c, $button_mix_factor);
+ transition-duration: 100ms;
+
+ // normal button
+ @if $t==normal {
+ color: $tc;
+ background-color: $button_bg_color;
+ @if $flat {
+ background-color: transparent;
+ }
+ @if $is_highcontrast == "true" {
+ box-shadow: inset 0 0 0 1px $button_inset_color;
+ }
+ }
+
+ // focused button
+ @if $t==focus {
+ color: $tc;
+ background-color: mix($button_bg_color, $selected_bg_color, 90%);
+ box-shadow: inset 0 0 0 2px transparentize($selected_bg_color, 0.4) !important;
+ &:hover {
+ background-color: mix(lighten($button_bg_color, 3%), $selected_bg_color, 90%);
+ box-shadow: inset 0 0 0 2px transparentize($selected_bg_color, 0.3) !important;
+ }
+ &:active {
+ background-color: mix(lighten($button_bg_color, 6%), $selected_bg_color, 90%);
+ box-shadow: inset 0 0 0 2px transparentize($selected_bg_color, 0.3) !important;
+ }
+ }
+
+ // hover button
+ @else if $t==hover {
+ color: $tc;
+ background-color: lighten($button_bg_color, 3%);
+
+ @if $is_highcontrast == "true" {
+ box-shadow: inset 0 0 0 1px lighten($button_inset_color, 3%);
+ background-color: mix(lighten($button_bg_color, 3%), $button_inset_color, 10%);
+ }
+ }
+
+ // active button
+ @else if $t==active {
+ color: $tc;
+ background-color: lighten($button_bg_color, 6%);
+ @if $is_highcontrast == "true" {
+ box-shadow: inset 0 0 0 1px lighten($button_inset_color, 6%);
+ background-color: mix(lighten($button_bg_color, 6%), $button_inset_color, 10%);
+ }
+ }
+
+ // checked button
+ @else if $t==checked {
+ color: $tc;
+ background-color: lighten($button_bg_color, 9%);
+ @if $is_highcontrast == "true" {
+ box-shadow: inset 0 0 0 1px lighten($button_inset_color, 9%);
+ background-color: mix(lighten($button_bg_color, 9%), $button_inset_color, 10%);
+ }
+ &:hover { background-color: lighten($button_bg_color, 12%);}
+ &:active { background-color: lighten($button_bg_color, 15%);}
+ }
+
+ // insensitive button
+ @else if $t==insensitive {
+ color: transparentize($tc, 0.5);
+ background-color: transparentize($tc, .95);
+ @if $is_highcontrast == "true" {
+ box-shadow: inset 0 0 0 1px transparentize($button_inset_color, 0.5);
+ }
+ }
+
+ // default/suggested button
+ @else if $t==default {
+ background-color: $selected_bg_color;
+ color: $selected_fg_color;
+ box-shadow: none;
+ &:focus {
+ box-shadow: inset 0 0 0 2px lighten($selected_bg_color, 10%);
+ }
+ &:hover, &:focus {
+ background-color: lighten($selected_bg_color, 5%);
+ color: lighten($selected_fg_color, 5%);
+ }
+ &:active {
+ background-color: darken($selected_bg_color, 7%);
+ color: darken($selected_fg_color, 7%);
+ }
+ &:insensitive {
+ @include button(insensitive);
+ background-color: transparentize($selected_bg_color, .5);
+ color: transparentize($selected_fg_color, .5);
+ }
+ }
+
+ // reset
+ @else if $t==undecorated {
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none;
+ &:insensitive {
+ @include button(insensitive);
+ background-color: transparent;
+ color: transparentize($selected_fg_color, .5);
+ }
+ }
+}
+
+// tile
+@mixin tile_button($color, $flat: true) {
+ @extend %tile;
+ @if $flat {
+ background-color: transparent;
+ } @else {
+ background-color: transparentize($color, .84);
+ }
+ &:hover { background-color: transparentize($color, .9);}
+ &:selected, &:focus {
+ background-color: transparentize($color, .87);
+ &:hover { background-color: transparentize($color, .84);}
+ &:active { background-color: transparentize($color, .87);}
+ }
+ &:active { background-color: transparentize($color, .84);}
+ &:outlined, &:checked {
+ background-color: transparentize($color, .81);
+ &:active { background-color: transparentize($color, .78);}
+ &:hover { background-color: transparentize($color, .75);}
+ }
+ &:drop {
+ border: 2px solid transparentize($selected_bg_color, .2); //already 2px transparent so no jumping
+ background-color: transparentize($selected_bg_color, .8);
+ }
+}
+
+// overview icon, dash, app grid
+@mixin overview_icon($color, $flat: true) {
+ transition-duration: 400ms;
+ .overview-icon { @extend %tile; }
+ @if $flat {
+ .overview-icon { background-color: transparent;}
+ } @else {
+ .overview-icon { background-color: transparentize($color, .81);}
+ }
+ &:hover .overview-icon { background-color: transparentize($color, .9);}
+
+ &:selected .overview-icon,
+ &:focus .overview-icon {
+ background-color: transparentize($color, .87);
+ &:hover .overview-icon { background-color: transparentize($color, .84);}
+ &:active .overview-icon { background-color: transparentize($color, .87);}
+ }
+ &:active .overview-icon { background-color: transparentize($color, .84);}
+ &:outlined .overview-icon,
+ &:checked .overview-icon {
+ background-color: transparentize($color, .81);
+ &:active .overview-icon { background-color: transparentize($color, .78);}
+ &:hover .overview-icon { background-color: transparentize($color, .75);}
+ }
+ &:drop .overview-icon {
+ border: 2px solid transparentize($selected_bg_color, .2); //already 2px transparent so no jumping
+ background-color: transparentize($selected_bg_color, .8);
+ }
+}
+
+// styling for elements within popovers that look like notifications
+@mixin card($flat: false) {
+ border-radius: $base_border_radius;
+ margin: $base_margin;
+ box-shadow: inset 0 0 0 1px $card_outer_borders_color;
+
+ @if $flat {
+ @include button(undecorated);
+ box-shadow: none !important;
+ } @else {
+ @include button(normal, $c:$card_bg_color);
+ }
+
+ &:focus {
+ @include button(focus, $c:$card_bg_color);
+ }
+
+ &:hover {
+ @include button(hover, $c:$card_bg_color);
+ }
+
+ &:active {
+ @include button(active, $c:$card_bg_color);
+ }
+}
diff --git a/data/theme/gnome-shell-sass/_high-contrast-colors.scss b/data/theme/gnome-shell-sass/_high-contrast-colors.scss
new file mode 100644
index 0000000..15fdec3
--- /dev/null
+++ b/data/theme/gnome-shell-sass/_high-contrast-colors.scss
@@ -0,0 +1,72 @@
+// When color definition differs for dark and light variant,
+// it gets @if ed depending on $variant
+
+@import '_palette.scss';
+
+$is_highcontrast: 'true';
+
+$base_color: if($variant == 'light', #fff, #000);
+$bg_color: $base_color;
+$fg_color: if($variant == 'light', transparentize(black, .2), white);
+
+$selected_fg_color: #ffffff;
+$selected_bg_color: $blue_3;
+
+$selected_borders_color: darken($selected_bg_color, 20%);
+$borders_color: lighten($bg_color,17%);
+$borders_edge: $borders_color;
+$link_color: lighten($selected_bg_color,20%);
+$link_visited_color: lighten($selected_bg_color,10%);
+
+$warning_color: if($variant == 'light', $yellow_5, #cd9309);
+$error_color: if($variant == 'light', $red_3, $red_4);
+$success_color: if($variant == 'light', $green_4, $green_5);
+$destructive_color: $error_color;
+
+$osd_fg_color: white;
+$osd_bg_color: $base_color;
+$osd_insensitive_bg_color: transparentize(mix($osd_fg_color, opacify($osd_bg_color, 1), 10%), 0.5);
+$osd_insensitive_fg_color: if($variant == 'light', mix($osd_fg_color, $osd_bg_color, 80%), mix($osd_fg_color, $osd_bg_color, 70%));
+$osd_borders_color: $osd_bg_color;
+$osd_outer_borders_color: $osd_bg_color;
+
+$shadow_color: transparent;
+
+// button
+$button_inset_color: transparentize($fg_color, 0.8);
+$button_mix_factor: 10%;
+
+// cards
+$card_bg_color: if($variant == 'light', darken($bg_color, 12%), lighten($bg_color, 12%));
+$card_outer_borders_color: $card_bg_color;
+
+// notifications
+$bubble_buttons_color: if($variant == 'light', darken($bg_color, 22%), lighten($bg_color, 31%));
+
+// overview background color
+$system_bg_color: lighten($base_color, 17%);
+
+//insensitive state derived colors
+$insensitive_fg_color: mix($fg_color, $bg_color, 50%);
+$insensitive_bg_color: mix($bg_color, $base_color, 60%);
+$insensitive_borders_color: $borders_color;
+
+//colors for the backdrop state, derived from the main colors.
+$backdrop_base_color: lighten($base_color,1%);
+$backdrop_bg_color: $bg_color;
+$backdrop_fg_color: mix($fg_color, $backdrop_bg_color, 80%);
+$backdrop_insensitive_color: lighten($backdrop_bg_color,15%);
+$backdrop_borders_color: mix($borders_color, $bg_color, 90%);
+$backdrop_dark_fill: mix($backdrop_borders_color,$backdrop_bg_color, 35%);
+
+// derived checked colors
+$checked_bg_color: if($variant=='light', darken($bg_color, 12%), lighten($bg_color, 18%));
+$checked_fg_color: if($variant=='light', darken($fg_color, 12%), lighten($fg_color, 18%));
+
+// derived hover colors
+$hover_bg_color: if($variant=='light', darken($bg_color, 8%), lighten($bg_color, 20%));
+$hover_fg_color: if($variant=='light', darken($fg_color, 10%), lighten($fg_color, 20%));
+
+// derived active colors
+$active_bg_color: if($variant=='light', darken($bg_color, 10%), lighten($bg_color, 22%));
+$active_fg_color: if($variant=='light', darken($fg_color, 10%), lighten($fg_color, 22%));
diff --git a/data/theme/gnome-shell-sass/_palette.scss b/data/theme/gnome-shell-sass/_palette.scss
new file mode 100644
index 0000000..78847d9
--- /dev/null
+++ b/data/theme/gnome-shell-sass/_palette.scss
@@ -0,0 +1,46 @@
+//GNOME Color Palette
+$blue_1: #99c1f1;
+$blue_2: #62a0ea;
+$blue_3: #3584e4;
+$blue_4: #1c71d8;
+$blue_5: #1a5fb4;
+$green_1: #8ff0a4;
+$green_2: #57e389;
+$green_3: #33d17a;
+$green_4: #2ec27e;
+$green_5: #26a269;
+$yellow_1: #f9f06b;
+$yellow_2: #f8e45c;
+$yellow_3: #f6d32d;
+$yellow_4: #f5c211;
+$yellow_5: #e5a50a;
+$orange_1: #ffbe6f;
+$orange_2: #ffa348;
+$orange_3: #ff7800;
+$orange_4: #e66100;
+$orange_5: #c64600;
+$red_1: #f66151;
+$red_2: #ed333b;
+$red_3: #e01b24;
+$red_4: #c01c28;
+$red_5: #a51d2d;
+$purple_1: #dc8add;
+$purple_2: #c061cb;
+$purple_3: #9141ac;
+$purple_4: #813d9c;
+$purple_5: #613583;
+$brown_1: #cdab8f;
+$brown_2: #b5835a;
+$brown_3: #986a44;
+$brown_4: #865e3c;
+$brown_5: #63452c;
+$light_1: #ffffff;
+$light_2: #f6f5f4;
+$light_3: #deddda;
+$light_4: #c0bfbc;
+$light_5: #9a9996;
+$dark_1: #77767b;
+$dark_2: #5e5c64;
+$dark_3: #3d3846;
+$dark_4: #241f31;
+$dark_5: #000000; \ No newline at end of file
diff --git a/data/theme/gnome-shell-sass/_widgets.scss b/data/theme/gnome-shell-sass/_widgets.scss
new file mode 100644
index 0000000..a09e0b5
--- /dev/null
+++ b/data/theme/gnome-shell-sass/_widgets.scss
@@ -0,0 +1,51 @@
+//
+// Shell widgets stylesheets are placed in separate .scss files
+// in 'widgets' and imported into the main stylesheet in this file.
+// To create or update a widget for the shell modify the list below.
+//
+
+/* WIDGETS */
+
+// Primary widgets
+@import 'widgets/base';
+@import 'widgets/entries';
+@import 'widgets/buttons';
+@import 'widgets/check-box';
+@import 'widgets/switches';
+@import 'widgets/slider';
+@import 'widgets/scrollbars';
+// Popovers
+@import 'widgets/popovers';
+@import 'widgets/calendar';
+@import 'widgets/message-list';
+@import 'widgets/ibus-popup';
+// Notifications
+@import 'widgets/notifications';
+@import 'widgets/hotplug';
+// Dialogs
+@import 'widgets/dialogs';
+// OSDs
+@import 'widgets/osd';
+@import 'widgets/switcher-popup';
+@import 'widgets/workspace-switcher';
+@import 'widgets/screenshot';
+// Panel
+@import 'widgets/panel';
+@import 'widgets/corner-ripple';
+@import 'widgets/quick-settings';
+// Overview
+@import 'widgets/overview';
+@import 'widgets/window-picker';
+@import 'widgets/search-entry';
+@import 'widgets/search-results';
+@import 'widgets/dash';
+@import 'widgets/app-grid';
+@import 'widgets/workspace-thumbnails';
+// A11y / misc
+@import 'widgets/a11y';
+@import 'widgets/misc';
+@import 'widgets/keyboard';
+@import 'widgets/looking-glass';
+// Lock / login screens
+@import 'widgets/login-dialog';
+@import 'widgets/screen-shield';
diff --git a/data/theme/gnome-shell-sass/gnome-shell-sass.doap b/data/theme/gnome-shell-sass/gnome-shell-sass.doap
new file mode 100644
index 0000000..f18ec1b
--- /dev/null
+++ b/data/theme/gnome-shell-sass/gnome-shell-sass.doap
@@ -0,0 +1,37 @@
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:gnome="http://api.gnome.org/doap-extensions#"
+ xmlns="http://usefulinc.com/ns/doap#">
+
+ <name xml:lang="en">GNOME Shell Sass</name>
+ <shortdesc xml:lang="en">Sass sources of GNOME Shell</shortdesc>
+ <description>GNOME Shell Sass is a project intended to allow the sharing of the
+ sass theme sources between gnome-shell and other projects like gnome-shell-extensions.</description>
+
+ <category rdf:resource="http://api.gnome.org/doap-extensions#core" />
+ <programming-language>sass</programming-language>
+ <programming-language>css</programming-language>
+
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Carlos Soriano</foaf:name>
+ <foaf:mbox rdf:resource="mailto:csoriano@gnome.org" />
+ <gnome:userid>csoriano</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Florian Müllner</foaf:name>
+ <foaf:mbox rdf:resource="mailto:fmuellner@gnome.org" />
+ <gnome:userid>fmuellner</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Jakub Steiner</foaf:name>
+ <foaf:mbox rdf:resource="mailto:jimmac@gmail.com" />
+ <gnome:userid>jimmac</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+</Project>
diff --git a/data/theme/gnome-shell-sass/widgets/_a11y.scss b/data/theme/gnome-shell-sass/widgets/_a11y.scss
new file mode 100644
index 0000000..31b5c4b
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_a11y.scss
@@ -0,0 +1,24 @@
+// Pointer location
+.ripple-pointer-location {
+ width: $ripple_size;
+ height: $ripple_size;
+ border-radius: $ripple_size * 0.5; // radius equals the size of the box to give us the curve
+ background-color: lighten(transparentize($selected_bg_color, 0.7), 30%);
+ box-shadow: 0 0 2px 2px lighten($selected_bg_color, 20%);
+}
+
+// Pointer accessibility notifications
+.pie-timer {
+ width: 60px;
+ height: 60px;
+ -pie-border-width: 3px;
+ -pie-border-color: $selected_bg_color;
+ -pie-background-color: lighten(transparentize($selected_bg_color, 0.7), 40%);
+}
+
+// Screen zoom/Magnifier
+.magnifier-zoom-region {
+ border: 2px solid $selected_bg_color;
+
+ &.full-screen { border-width: 0; }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
new file mode 100644
index 0000000..9da650c
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
@@ -0,0 +1,161 @@
+/* App Grid */
+
+$app_icon_size: 96px;
+
+// app icons
+.icon-grid {
+ row-spacing: $base_padding * 2;
+ column-spacing: $base_padding * 2;
+ max-row-spacing: $base_padding * 7;
+ max-column-spacing: $base_padding * 7;
+ page-padding-top: $base_padding * 4;
+ page-padding-bottom: $base_padding * 4;
+ page-padding-left: $base_padding * 3;
+ page-padding-right: $base_padding * 3;
+}
+
+/* App Icons */
+
+// Icon tiles in the app grid
+.app-well-app {
+ @include overview_icon($osd_fg_color);
+
+ .overview-icon { padding: $base_padding*2;}
+ .overview-icon.overview-icon-with-label {
+ > StBoxLayout {
+ spacing: $base_padding;
+ }
+ }
+}
+
+// app folders
+.app-well-app.app-folder {
+ @include overview_icon($osd_fg_color, $flat: false);
+}
+
+// expanded folder
+.app-folder-dialog {
+ border-radius: $modal_radius*2;
+ background-color: $dash_background_color;
+
+ & .folder-name-container {
+ padding: 24px 36px 0;
+ spacing: 12px;
+
+ & .folder-name-label,
+ & .folder-name-entry {
+ @extend %title_1;
+ }
+
+ & .folder-name-entry { width: 300px }
+
+ /* FIXME: this is to keep the label in sync with the entry */
+ & .folder-name-label { padding: 5px 7px; color: $osd_fg_color; }
+
+ & .edit-folder-button {
+ @extend %button;
+ padding: 0;
+ width: 36px;
+ height: 36px;
+ border-radius: 99px;
+ & > StIcon { icon-size: $base_icon_size }
+ }
+ }
+
+ & .icon-grid {
+ row-spacing: $base_padding * 2;
+ column-spacing: $base_padding * 5;
+ page-padding-top: 0;
+ page-padding-bottom: 0;
+ page-padding-left: 0;
+ page-padding-right: 0;
+ }
+
+ & .page-indicators {
+ margin-bottom: 18px;
+ }
+}
+
+.app-folder-dialog-container {
+ padding: $base_padding*2;
+ width: 720px;
+ height: 720px;
+}
+
+// Running app indicator (also shown in dash)
+.app-well-app-running-dot {
+ height: 5px;
+ width: 5px;
+ border-radius:5px;
+ margin-bottom: 8px;
+ background-color: $osd_fg_color;
+}
+
+// Rename popup for app folders
+.rename-folder-popup {
+ .rename-folder-popup-item {
+ spacing: $base_padding;
+ &:ltr, &:rtl { padding: 0 $base_padding * 2; }
+ }
+}
+
+// App Grid pagination indicators
+.page-indicator {
+ padding: $base_padding $base_padding * 2 0;
+ transition-duration:400ms;
+
+ .page-indicator-icon {
+ width: 10px;
+ height: 10px;
+ border-radius: 10px; // the same as height&width
+ background-color: white;
+ }
+}
+
+.apps-scroll-view {
+ padding: 0;
+}
+
+// shutdown and other actions in the grid
+.system-action-icon {
+ background-color: rgba(0,0,0,0.8);
+ color: #fff;
+ border-radius: 99px;
+ icon-size: $app_icon_size * 0.5;
+}
+
+.page-navigation-hint {
+ &.dnd {
+ background: rgba(255, 255, 255, 0.1);
+ }
+
+ &.next:ltr,
+ &.previous:rtl {
+ background-gradient-start: rgba(255, 255, 255, 0.05);
+ background-gradient-end: transparent;
+ background-gradient-direction: horizontal;
+ border-radius: $modal_radius*1.5 0px 0px $modal_radius*1.5;
+ }
+
+ &.previous:ltr,
+ &.next:rtl {
+ background-gradient-start: transparent;
+ background-gradient-end: rgba(255, 255, 255, 0.05);
+ background-gradient-direction: horizontal;
+ border-radius: 0px $modal_radius*1.5 $modal_radius*1.5 0px;
+ }
+}
+
+.page-navigation-arrow {
+ & > StIcon {
+ margin: 6px;
+ padding: 18px;
+ width: 24px;
+ height: 24px;
+ border-radius: 99px;
+ }
+
+ &:insensitive > StIcon { @include button(undecorated, $osd_fg_color, transparentize($osd_bg_color, 0.5));}
+ &:hover > StIcon { @include button(hover, $osd_fg_color, transparentize($osd_bg_color, 0.5));}
+ &:active > StIcon { @include button(active, $osd_fg_color, transparentize($osd_bg_color, 0.5));}
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_base.scss b/data/theme/gnome-shell-sass/widgets/_base.scss
new file mode 100644
index 0000000..69ebb30
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_base.scss
@@ -0,0 +1,18 @@
+// Links
+.shell-link {
+ color: $link_color;
+
+ &:hover {
+ color: lighten($link_color, 10%);
+ }
+}
+
+// Outline for low res icons
+.lowres-icon {
+ icon-shadow: 0 1px 2px rgba(black, 0.3);
+}
+
+// Dropshadow for large icons
+.icon-dropshadow {
+ icon-shadow: 0 1px 5px rgba(black, 0.8);
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_buttons.scss b/data/theme/gnome-shell-sass/widgets/_buttons.scss
new file mode 100644
index 0000000..def96fe
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_buttons.scss
@@ -0,0 +1,19 @@
+/* Buttons */
+
+.button {
+ @extend %button; // that's it
+ min-height: 22px;
+}
+
+.icon-button {
+ @extend .button; // same style as buttons
+
+ border-radius: 99px;
+ padding: $base_padding*2;
+ min-height: 16px;
+
+ StIcon {
+ icon-size: $base_icon_size;
+ -st-icon-style: symbolic;
+ }
+} \ No newline at end of file
diff --git a/data/theme/gnome-shell-sass/widgets/_calendar.scss b/data/theme/gnome-shell-sass/widgets/_calendar.scss
new file mode 100644
index 0000000..fa7080b
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_calendar.scss
@@ -0,0 +1,257 @@
+/* Date/Time Menu */
+
+// overall menu
+#calendarArea {
+ padding: $base_padding - 2px;
+}
+
+// Calendar menu side column
+.datemenu-calendar-column {
+ spacing: $base_padding;
+ &:ltr {padding-left: $base_padding;}
+ &:rtl {padding-right: $base_padding;}
+ .datemenu-displays-box {spacing: $base_padding;}
+}
+
+/* today button (the date) */
+.datemenu-today-button {
+ @include card(flat);
+ padding: $base_padding * 1.5;
+
+ // weekday label
+ .day-label {
+ @extend %heading;
+ }
+
+ // date label
+ .date-label {
+ @extend %title_2;
+ }
+}
+
+/* Calendar */
+.calendar {
+ @include card(flat);
+ margin-top: 0;
+
+ // month header
+ .calendar-month-header {
+
+ // prev/next month icons
+ .calendar-change-month-back StIcon,
+ .calendar-change-month-forward StIcon {
+ icon-size: $base_icon_size;
+ }
+
+ // month label
+ .calendar-month-label {
+ color: lighten($fg_color,5%);
+ @extend %heading;
+ padding: 8px 0;
+ }
+ .pager-button {
+ background-color: transparent;
+ height: 32px;
+ width: 32px;
+ margin: 2px;
+ border-radius: $base_border_radius - 2px;
+ &:hover, &:focus {background-color: $hover_bg_color;}
+ &:active {background-color: $active_bg_color;}
+ }
+ }
+
+ $calendar_day_size: 3em;
+
+ .calendar-day-base {
+ text-align: center;
+ margin: 2px;
+ padding: 0 !important;
+ height: $calendar_day_size !important;
+ width: $calendar_day_size !important;
+ border-radius: 99px;
+ transition-duration: 100ms;
+ @extend %numeric;
+ @extend %smaller;
+
+ &:hover {background-color: $hover_bg_color;}
+ &:focus {
+ background-color: mix($bg_color, $selected_bg_color, 80%);
+ color: $selected_fg_color;
+ box-shadow:inset 0 0 0 2px transparentize($selected_bg_color, 0.4);
+ }
+
+ &:active,&:selected {
+ color: $active_fg_color;
+ background-color: $active_bg_color;
+ &:focus {
+ background-color: mix($active_bg_color, $selected_bg_color, 80%);
+ }
+ }
+
+ // day of week heading
+ &.calendar-day-heading {
+ color: $insensitive_fg_color;
+ padding-top: $base_padding;
+ height: 16px !important; // force heading to be smaller height
+ font-weight: bold;
+ @extend %smaller;
+ }
+ }
+
+ .calendar-day {}
+ .calendar-work-day {}
+ .calendar-nonwork-day {color: $insensitive_fg_color;}
+ .calendar-other-month-day {
+ color: transparentize($fg_color, 0.5);
+ &.calendar-nonwork-day {
+ color: transparentize($insensitive_fg_color, 0.5);
+ }
+ }
+
+ // Today
+ .calendar-today {
+ background-color: $selected_bg_color;
+ font-weight: 800;
+ color: $selected_fg_color !important;
+
+ &:hover,&:focus {
+ background-color:lighten($selected_bg_color, 3%);
+ color: inherit;
+ }
+
+ &:active,&:selected {
+ background-color: $selected_bg_color;
+ color: inherit;
+
+ &:hover,&:focus {
+ background-color:lighten($selected_bg_color, 3%);
+ color: inherit;
+ }
+ }
+ }
+
+ .calendar-day-with-events {
+ background-image: if($variant == 'light', url("resource:///org/gnome/shell/theme/calendar-today-light.svg"),url("resource:///org/gnome/shell/theme/calendar-today.svg"));
+ background-size: contain;
+ }
+
+ .calendar-week-number {
+ @include fontsize($base_font_size - 4);
+ font-weight: bold;
+ font-feature-settings: "tnum";
+ margin: 6px;
+ padding: 0 $base_padding;
+ border-radius: 3px;
+ background-color: darken($bg_color, 2%);
+ color: $insensitive_fg_color
+ }
+}
+
+/* Events */
+.events-button {
+ @include card;
+ padding: $base_padding * 2;
+
+ .events-box {
+ spacing: $base_padding;
+ }
+
+ .events-list {
+ spacing: 2 * $base_padding;
+ }
+
+ .events-title {
+ @extend %heading;
+ color: $insensitive_fg_color;
+ margin-bottom: $base_margin;
+ }
+
+ .event-time {
+ @extend %numeric;
+ @extend %caption;
+ color: $insensitive_fg_color;
+ }
+}
+
+/* World clocks */
+.world-clocks-button {
+ @include card;
+ padding: $base_padding * 2;
+
+ .world-clocks-grid {
+ spacing-rows: $base_padding;
+ spacing-columns: $base_padding * 2;
+ }
+
+ // title
+ .world-clocks-header {
+ @extend %heading;
+ color: $insensitive_fg_color;
+ }
+
+ // city label
+ .world-clocks-city {
+ color: $fg_color;
+ }
+
+ // timezone time
+ .world-clocks-time {
+ @extend %heading;
+ @extend %numeric;
+ color: $fg_color;
+
+ &:ltr {text-align: right;}
+ &:rtl {text-align: left;}
+ }
+
+ // timezone offset label
+ .world-clocks-timezone {
+ @extend %numeric;
+ @extend %caption;
+ color: $insensitive_fg_color;
+ }
+}
+
+/* Weather */
+.weather-button {
+ @include card;
+ padding: $base_padding * 2;
+
+ .weather-box {
+ spacing: $base_padding + $base_margin;
+ }
+
+ .weather-header-box {
+ spacing: $base_padding;
+ }
+
+ .weather-header {
+ color: $insensitive_fg_color;
+ @extend %heading;
+
+ &.location {
+ font-weight: normal;
+ }
+ }
+
+ .weather-grid {
+ spacing-rows: $base_padding;
+ spacing-columns: $base_padding * 2;
+ }
+
+ .weather-forecast-time {
+ @extend %numeric;
+ @extend %caption;
+ color: darken($fg_color,30%);
+ padding-top: 0.2em;
+ padding-bottom: 0.4em;
+ }
+
+ .weather-forecast-icon {
+ icon-size: $large_icon_size;
+ }
+
+ .weather-forecast-temp {
+ @extend %heading;
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_check-box.scss b/data/theme/gnome-shell-sass/widgets/_check-box.scss
new file mode 100644
index 0000000..1480ade
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_check-box.scss
@@ -0,0 +1,18 @@
+/* Check Boxes */
+
+// these are equal to the size of the SVG assets
+$check_height: 24px;
+$check_width: 24px;
+
+
+.check-box {
+ StBoxLayout { spacing: .8em; }
+ StBin {
+ width: $check_width;
+ height: $check_height;
+ background-image: if($variant == 'light', url("resource:///org/gnome/shell/theme/checkbox-off-light.svg"), url("resource:///org/gnome/shell/theme/checkbox-off.svg"));
+ }
+ &:focus StBin { background-image: if($variant == 'light', url("resource:///org/gnome/shell/theme/checkbox-off-focused-light.svg"), url("resource:///org/gnome/shell/theme/checkbox-off-focused.svg"));; }
+ &:checked StBin { background-image: url("resource:///org/gnome/shell/theme/checkbox.svg"); }
+ &:focus:checked StBin { background-image: url("resource:///org/gnome/shell/theme/checkbox-focused.svg"); }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_corner-ripple.scss b/data/theme/gnome-shell-sass/widgets/_corner-ripple.scss
new file mode 100644
index 0000000..9aff431
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_corner-ripple.scss
@@ -0,0 +1,15 @@
+/* Activities Ripple */
+
+$ripple_size: 50px;
+
+.ripple-box {
+ background-color: rgba(255,255,255,0.2);
+ box-shadow: 0 0 2px 2px rgba(255,255,255,0.2);
+ // plus + 2px for the border (box-shadow)
+ width: $ripple_size + 2px;
+ height: $ripple_size + 2px;
+ border-radius: 0 0 $ripple_size + 2px 0; // radius equals the size of the box to give us the curve
+
+ // just a simple change to the border radius position
+ &:rtl { border-radius: 0 0 0 $ripple_size + 2px; }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_dash.scss b/data/theme/gnome-shell-sass/widgets/_dash.scss
new file mode 100644
index 0000000..8f5d5f9
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_dash.scss
@@ -0,0 +1,75 @@
+/* Dash */
+
+$dash_background_color: lighten($system_bg_color, 5%);
+$dash_placeholder_size: 32px;
+$dash_padding: $base_padding*2; // 12px
+$dash_border_radius: $modal_radius + $dash_padding;
+
+// container for the dash
+#dash {
+ @extend %caption;
+ margin-top: $dash_padding;
+
+ .placeholder {
+ // background-image: url("resource:///org/gnome/shell/theme/dash-placeholder.svg");
+ background-image:none;
+ background-size: contain;
+ height: $dash_placeholder_size;
+ }
+
+ .empty-dash-drop-target {
+ width: $dash_placeholder_size;
+ height: $dash_placeholder_size;
+ }
+
+ // Running app indicator (also shown in app grid)
+ .app-well-app-running-dot {
+ margin-bottom: 14px; // hardcoded
+ }
+}
+
+// background of the dash behind app icons
+.dash-background {
+ background-color: $dash_background_color;
+ border-radius: $dash_border_radius;
+ padding: $dash_padding;
+ spacing: $base_padding;
+ margin-bottom: $dash_padding;
+}
+
+// items on the dash
+.dash-item-container {
+ > * {margin: 0 2px;}
+ &:ltr:first-child {margin-left: 0;}
+ &:rtl:first-child {margin-right: 0;}
+
+ // each app item on the dash
+ .app-well-app .overview-icon {
+ padding: $base_padding;
+ }
+
+ // show apps button
+ .show-apps { @include overview_icon($osd_fg_color);}
+
+ .show-apps, .app-well-app {
+ padding-bottom: $dash_padding;
+ }
+}
+
+// separator between favourites and running apps
+.dash-separator {
+ width: 1px;
+ margin: 0 $base_margin;
+ background-color: $borders_color;
+ margin-bottom: $dash_padding;
+}
+
+// OSD Tooltip
+.dash-label {
+ color: $osd_fg_color;
+ background-color: $osd_bg_color;
+ border-radius: 99px;
+ padding: $base_padding $base_padding * 2;
+ text-align: center;
+ -y-offset: $base_margin * 2; // distance from the dash edge
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_dialogs.scss b/data/theme/gnome-shell-sass/widgets/_dialogs.scss
new file mode 100644
index 0000000..defa20b
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_dialogs.scss
@@ -0,0 +1,172 @@
+/* Modal Dialogs */
+
+.headline {
+ @extend %title_4;
+}
+
+.modal-dialog {
+ @extend %bubble_panel;
+
+ .modal-dialog-content-box {
+ margin: 32px 40px;
+ spacing: 32px;
+ max-width: 28em;
+ }
+
+ .modal-dialog-linked-button {
+ @extend %bubble_button;
+ }
+}
+
+/* End Session Dialog */
+.end-session-dialog {
+ width: 30em;
+
+ .end-session-dialog-battery-warning,
+ .dialog-list-title {
+ color: $warning_color;
+ }
+}
+
+/* Message Dialog */
+.message-dialog-content {
+ spacing: 18px;
+
+ .message-dialog-title {
+ text-align: center;
+ @extend %title_2;
+
+ &.lightweight { @extend %title_4;}
+ }
+ .message-dialog-description { text-align: center; }
+}
+
+/* Dialog List */
+.dialog-list {
+ spacing: 18px;
+
+ .dialog-list-title {
+ text-align: center;
+ @extend %heading;
+ }
+
+ .dialog-list-scrollview { max-height: 200px; }
+ .dialog-list-box {
+ spacing: 1em;
+
+ .dialog-list-item {
+ spacing: 1em;
+
+ .dialog-list-item-title { font-weight: bold; }
+ .dialog-list-item-description {
+ color: darken($fg_color,5%);
+ @extend %caption;
+ }
+ }
+ }
+}
+
+/* Run Dialog */
+.run-dialog {
+ .modal-dialog-content-box {
+ margin-top: 24px;
+ margin-bottom: 14px;
+ }
+ .run-dialog-entry { width: 20em; }
+ .run-dialog-description {
+ @extend %caption;
+ text-align: center;
+ color: darken($fg_color, 20%);
+ }
+}
+
+/* Password or Authentication Dialog */
+
+.prompt-dialog {
+ width: 28em;
+
+ .modal-dialog-content-box {
+ margin-bottom: 24px;
+ }
+}
+
+.prompt-dialog-password-grid {
+ spacing-rows: 8px;
+ spacing-columns: 4px;
+
+ .prompt-dialog-password-entry {
+ width: auto;
+
+ // 4px (spacing) + 16px (spinner-width)
+ &:ltr { margin-left: 20px; }
+ &:rtl { margin-right: 20px; }
+ }
+}
+
+.prompt-dialog-password-layout {
+ spacing: 8px;
+}
+
+.prompt-dialog-password-entry {
+ width: 20em;
+}
+
+.prompt-dialog-error-label,
+.prompt-dialog-info-label,
+.prompt-dialog-null-label {
+ text-align: center;
+ @extend %caption;
+}
+
+.prompt-dialog-error-label {
+ color: $warning_color;
+}
+
+
+/* Polkit Dialog */
+
+.polkit-dialog-user-layout {
+ text-align: center;
+ spacing: 8px;
+ margin-bottom: 6px;
+
+ .polkit-dialog-user-root-label { color: $warning_color; }
+}
+
+/* Audio selection dialog */
+.audio-device-selection-dialog {
+ .modal-dialog-content-box { margin-bottom: 28px; }
+ .audio-selection-box { spacing: 20px; }
+}
+
+.audio-selection-device {
+ border-radius: $base_border_radius*2;
+ &:hover,&:focus { background-color: $hover_bg_color; }
+ &:active {
+ background-color: $selected_bg_color;
+ color: $selected_fg_color;
+ }
+}
+
+.audio-selection-device-box {
+ padding: 20px;
+ spacing: 20px;
+}
+
+.audio-selection-device-icon {
+ icon-size: $base_icon_size * 4;
+}
+
+/* Welcome dialog */
+.welcome-dialog-image {
+ background-image: url("resource:///org/gnome/shell/theme/gnome-shell-start.svg");
+ background-size: contain;
+ /* Reasonable maximum dimensions */
+ height: 300px;
+ width: 300px;
+}
+
+/* Access portal dialog */
+.access-dialog {
+ text-align: center;
+} \ No newline at end of file
diff --git a/data/theme/gnome-shell-sass/widgets/_entries.scss b/data/theme/gnome-shell-sass/widgets/_entries.scss
new file mode 100644
index 0000000..6be6760
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_entries.scss
@@ -0,0 +1,31 @@
+/* Entries */
+
+StEntry {
+ border-radius: $base_border_radius;
+ padding: 8px;
+ color: $fg_color;
+
+ @include entry(normal);
+ &:hover { @include entry(hover);}
+ &:focus { @include entry(focus);}
+ &:insensitive { @include entry(insensitive);}
+
+ selection-background-color: $selected_bg_color;
+ selected-color: $selected_fg_color;
+
+ StIcon.capslock-warning {
+ icon-size: 16px;
+ warning-color: $warning_color;
+ padding: 0 4px;
+ }
+
+ StIcon.peek-password {
+ icon-size: $base_icon_size;
+ padding: 0 4px;
+ }
+
+ StLabel.hint-text {
+ margin-left: 2px;
+ color: transparentize($fg_color, 0.3);
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_hotplug.scss b/data/theme/gnome-shell-sass/widgets/_hotplug.scss
new file mode 100644
index 0000000..acd0265
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_hotplug.scss
@@ -0,0 +1,10 @@
+// hotplug
+
+.hotplug-notification-item {
+ @extend %bubble_button;
+}
+
+.hotplug-notification-item-icon {
+ icon-size: 24px;
+ padding: 0 4px;
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_ibus-popup.scss b/data/theme/gnome-shell-sass/widgets/_ibus-popup.scss
new file mode 100644
index 0000000..8cbaa95
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_ibus-popup.scss
@@ -0,0 +1,39 @@
+// IBus Candidate Popup
+
+.candidate-popup-boxpointer {
+ @extend .popup-menu-boxpointer;
+}
+
+.candidate-popup-content {
+ padding: $base_padding;
+ spacing: $base_padding;
+ @extend .popup-menu-content;
+}
+
+.candidate-index {
+ padding: 0;
+ padding-right: $base_padding;
+ color: $insensitive_fg_color;
+}
+
+.candidate-box {
+ padding: $base_padding $base_padding*2 $base_padding $base_padding*2;
+ border-radius: $base_border_radius;
+ &:selected { background-color: $selected_bg_color; color: $selected_fg_color; }
+ &:hover { background-color: $hover_bg_color; color: $hover_fg_color; }
+}
+
+.candidate-page-button-box {
+ height: 2em;
+ .vertical & { padding-top: $base_padding*2; }
+ .horizontal & { padding-left: $base_padding*2; }
+}
+
+.candidate-page-button {
+ padding: $base_padding;
+
+ & StIcon { icon-size: $base_icon_size; }
+}
+
+.candidate-page-button-previous { border-radius: $base_border_radius 0px 0px $base_border_radius; border-right-width: 0;box-shadow: none;}
+.candidate-page-button-next { border-radius: 0px $base_border_radius $base_border_radius 0px;box-shadow: none;}
diff --git a/data/theme/gnome-shell-sass/widgets/_keyboard.scss b/data/theme/gnome-shell-sass/widgets/_keyboard.scss
new file mode 100644
index 0000000..1dc6b95
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_keyboard.scss
@@ -0,0 +1,159 @@
+/* On-screen Keyboard */
+
+$key_size: 1.2em;
+$key_border_radius: $base_border_radius + 4px; // 12px
+$key_bg_color: darken($osd_fg_color, 70%);
+// $default_key_bg_color: darken($key_bg_color, 4%);
+$default_key_bg_color: if($variant=='light', darken($key_bg_color, 11%), darken($key_bg_color, 10%));
+
+
+// draw keys using button function
+#keyboard {
+ // background-color: transparentize(if($variant=='light', darken($bg_color, 5%), darken($bg_color, 8%)), 0.1);
+ background-color: $osd_bg_color;
+ box-shadow: inset 0 1px 0 0 $osd_outer_borders_color;
+
+ .page-indicator {
+ padding: $base_padding;
+
+ .page-indicator-icon {
+ width: 8px;
+ height: 8px;
+ }
+ }
+}
+
+// the container for individual keys
+.key-container {
+ padding: $base_margin;
+ spacing: $base_margin;
+}
+
+// the keys
+.keyboard-key {
+
+ @include fontsize($base_font_size + 5);
+ font-weight: bold;
+ min-height: $key_size;
+ min-width: $key_size;
+ border-width: 1px;
+ border-style: solid;
+ border-radius: $key_border_radius;
+ box-shadow: 0 1px 0 0 $shadow_color;
+
+ @include keyboard_key(normal, $key_bg_color, $osd_fg_color);
+
+ &:focus { @include keyboard_key(focus);}
+ &:hover { @include keyboard_key(hover, $key_bg_color, $osd_fg_color);}
+ &:active { @include keyboard_key(active, $key_bg_color, $osd_fg_color); }
+ &:checked { @include keyboard_key(checked, $key_bg_color, $osd_fg_color); }
+
+ &:grayed { //FIXMEy
+ background-color: darken($bg_color, 3%);
+ color: $osd_fg_color;
+ border-color: $osd_borders_color;
+ }
+
+ // non-character keys
+ &.default-key {
+ @include keyboard_key(normal, $default_key_bg_color, $osd_fg_color);
+ &:hover {@include keyboard_key(hover, $default_key_bg_color, $osd_fg_color);}
+ &:active { @include keyboard_key(active, $default_key_bg_color, $osd_fg_color);}
+ &:checked { @include keyboard_key(checked, $default_key_bg_color, $osd_fg_color);}
+ border-radius: $key_border_radius;
+
+ // keys that may be latched: ctrl/alt/shift
+ &:latched {
+ border-color: lighten($selected_bg_color, 5%);
+ background-color: $selected_bg_color;
+ }
+ }
+
+ // enter key is suggested-action
+ &.enter-key {
+ @include keyboard_key(normal, $selected_bg_color, $selected_fg_color);
+ &:hover { @include keyboard_key(hover, $selected_bg_color, $selected_fg_color);}
+ &:active {@include keyboard_key(active, $selected_bg_color, $selected_fg_color);}
+ &:checked {@include keyboard_key(checked, $selected_bg_color, $selected_fg_color);}
+ border-radius: $key_border_radius;
+ color: $osd_fg_color;
+ }
+
+ &.shift-key-lowercase {}
+
+ // pressed shift has different style
+ &.shift-key-uppercase {
+ background-color: lighten($key_bg_color, 20%);
+ color: $osd_bg_color;
+ &:hover {
+ background-color: lighten($key_bg_color, 25%);
+ color: lighten($osd_bg_color, 5%);
+ }
+ }
+
+ // size of icons on keys
+ StIcon { icon-size: 24px; }
+}
+
+// long press on a key popup
+.keyboard-subkeys {
+ -arrow-border-radius: $base_border_radius*2;
+ -arrow-background-color: $osd_bg_color;
+ -arrow-border-width: 1px;
+ -arrow-border-color: lighten($osd_bg_color, 9%);
+ -arrow-base: 20px;
+ -arrow-rise: 10px;
+ -boxpointer-gap: $base_padding;
+ padding: $base_padding;
+
+ .keyboard-key {
+ @include keyboard_key(normal, $key_bg_color, $osd_fg_color);
+
+ &:focus { @include keyboard_key(focus);}
+ &:hover { @include keyboard_key(hover, $key_bg_color, $osd_fg_color);}
+ &:active { @include keyboard_key(active, $key_bg_color, $osd_fg_color); }
+ &:checked { @include keyboard_key(checked, $key_bg_color, $osd_fg_color); }
+
+ border-radius:$key_border_radius;
+ }
+}
+
+// emoji
+.emoji-page {
+ .keyboard-key {
+ background-color: transparent;
+ border: none;
+ color: initial;
+ }
+}
+
+.emoji-panel {
+ .keyboard-key:latched {
+ border-color: lighten($selected_bg_color, 5%);
+ background-color: $selected_bg_color;
+ }
+}
+
+// suggestions
+.word-suggestions {
+ @include fontsize($base_font_size + 1);
+ spacing: 12px;
+ min-height: 17pt;
+ padding: $base_padding*2;
+ color: $osd_fg_color;
+
+ // each suggestion
+ StButton {
+ margin: 0 3px;
+ min-width: 32px;
+ border-radius: $base_border_radius - 2px;
+ padding: 0px $base_padding*3;
+
+ @include keyboard_key(undecorated, $key_bg_color, $osd_fg_color);
+
+ &:focus { @include keyboard_key(focus);}
+ &:hover { @include keyboard_key(hover, $key_bg_color, $osd_fg_color);}
+ &:active { @include keyboard_key(active, $key_bg_color, $osd_fg_color); }
+ &:checked { @include keyboard_key(checked, $key_bg_color, $osd_fg_color); }
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_login-dialog.scss b/data/theme/gnome-shell-sass/widgets/_login-dialog.scss
new file mode 100644
index 0000000..208165d
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_login-dialog.scss
@@ -0,0 +1,201 @@
+/* Login Dialog */
+
+.login-dialog-banner-view {
+ padding-top: 24px;
+ max-width: 23em;
+}
+
+.login-dialog,
+.unlock-dialog {
+ //reset
+ border: none;
+ background-color: transparent;
+
+ $_gdm_bg: $system_bg_color;
+
+ .modal-dialog-button-box { spacing: 3px; }
+ .modal-dialog-button {
+ padding: 4px 18px;
+ background-color: darken($system_bg_color, 3%);
+ border-color: darken($system_bg_color, 3%);
+ color: $osd_fg_color;
+
+ $_hover_c: lighten($_gdm_bg, 5%);
+ &:hover, &:focus {
+ background-color: $_hover_c;
+ border-color: $_hover_c;
+ }
+ &:active {
+ $_active_c: darken($_gdm_bg, 5%);
+ background-color: $_active_c;
+ border-color: $_active_c;
+ }
+ &:insensitive {
+ @include button(insensitive);
+ border-color: darken($_gdm_bg, 5%);
+ background-color: darken($_gdm_bg, 5%);
+ color: transparentize($osd_fg_color, 0.3);
+ }
+ &:default {
+ @include button(default);
+ }
+ }
+
+ .cancel-button,
+ .switch-user-button,
+ .login-dialog-session-list-button {
+ padding: 0;
+ border-radius: 99px;
+ width: $large_icon_size;
+ height: $large_icon_size;
+ border-color: darken($system_bg_color, 3%);
+ background-color: darken($system_bg_color, 3%);
+
+ StIcon { icon-size: $base_icon_size; }
+ }
+
+ .caps-lock-warning-label,
+ .login-dialog-message-warning {
+ color: $osd_fg_color;
+ }
+}
+
+.login-dialog-logo-bin { padding: 24px 0px; }
+.login-dialog-banner { color: darken($osd_fg_color,10%); }
+.login-dialog-button-box { width: 23em; spacing: 5px; }
+.login-dialog-message { text-align: center; }
+.login-dialog-user-selection-box { padding: 100px 0px; }
+.login-dialog-not-listed-label {
+ padding-left: 2px;
+ .login-dialog-not-listed-button:focus &,
+ .login-dialog-not-listed-button:hover & {
+ color: $osd_fg_color;
+ }
+}
+
+.login-dialog-not-listed-label {
+ @extend %caption;
+ font-weight: bold;
+ color: darken($osd_fg_color,30%);
+ padding-top: 1em;
+}
+
+.login-dialog-auth-list-view { -st-vfade-offset: 1em; }
+.login-dialog-auth-list {
+ spacing: 6px;
+ margin-left: 2em;
+}
+
+.login-dialog-auth-list-title {
+ margin-left: 2em;
+}
+
+.login-dialog-auth-list-item {
+ border-radius: $base_border_radius + 4px;
+ padding: 6px;
+ color: darken($osd_fg_color,30%);
+ &:focus, &:selected { background-color: $selected_bg_color; color: $selected_fg_color; }
+}
+
+.login-dialog-auth-list-label {
+ @include fontsize($base_font_size + 2);
+ font-weight: bold;
+ padding-left: 15px;
+ &:ltr { padding-left: 14px; text-align: left; }
+ &:rtl { padding-right: 14px; text-align: right; }
+}
+
+.login-dialog-user-list-view { -st-vfade-offset: 1em; }
+.login-dialog-user-list {
+ spacing: 12px;
+ width: 23em;
+ &:expanded .login-dialog-user-list-item:selected { background-color: $selected_bg_color; color: $selected_fg_color; }
+ &:expanded .login-dialog-user-list-item:logged-in { border-right: 2px solid $selected_bg_color; }
+}
+
+.login-dialog-user-list-item {
+ border-radius: $base_border_radius + 4px;
+ padding: 6px;
+ color: darken($osd_fg_color,30%);
+ &:ltr .user-widget { padding-right: 1em; }
+ &:rtl .user-widget { padding-left: 1em; }
+ .login-dialog-timed-login-indicator {
+ height: 2px;
+ margin-top: 6px;
+ background-color: $osd_fg_color;
+ }
+ &:focus .login-dialog-timed-login-indicator { background-color: $selected_fg_color; }
+}
+
+.user-widget-label {
+ color: $osd_fg_color;
+}
+
+.user-widget.horizontal .user-widget-label {
+ @include fontsize($base_font_size + 2);
+ font-weight: bold;
+ padding-left: 15px;
+
+ &:ltr { padding-left: 14px; text-align: left; }
+ &:rtl { padding-right: 14px; text-align: right; }
+}
+
+.user-widget.vertical .user-widget-label {
+ @include fontsize($base_font_size + 5);
+ text-align: center;
+ font-weight: normal;
+ padding-top: 16px;
+}
+
+.login-dialog-timed-login-indicator {
+ height: 2px;
+ background-color: darken($fg_color,40%);
+}
+
+.login-dialog-prompt-layout {
+ padding-top: 24px;
+ padding-bottom: 12px;
+ spacing: $base_padding * 2;
+ width: 23em;
+}
+
+.login-dialog-prompt-entry {
+ height: 1.5em;
+}
+
+.login-dialog-prompt-label {
+ color: darken($osd_fg_color, 20%);
+ @include fontsize($base_font_size + 1);
+ padding-top: 1em;
+}
+
+.login-dialog {
+ StEntry {
+ @if $variant=='dark' {
+ $_gdm_entry_bg: darken($system_bg_color, 3%);
+ background-color: $_gdm_entry_bg;
+ color: $fg_color;
+ }
+ }
+}
+
+// Custom styling for unlock entry
+.unlock-dialog {
+ StEntry {
+ border:none !important;
+ &:focus {
+ background-color: if($variant == 'light', transparentize(white, 0.9), transparentize($fg_color, 0.9));
+ }
+ &:insensitive {
+ color: if($variant == 'light', transparentize(white, 0.5), transparentize($fg_color, 0.5));
+ background-color: if($variant == 'light', transparentize(white, 0.95), transparentize($fg_color, 0.95));
+ }
+ }
+
+ .cancel-button,
+ .switch-user-button,
+ .login-dialog-session-list-button {
+ border-color: transparent;
+ background-color: transparentize($fg_color, 0.9);
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_looking-glass.scss b/data/theme/gnome-shell-sass/widgets/_looking-glass.scss
new file mode 100644
index 0000000..00630c3
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_looking-glass.scss
@@ -0,0 +1,144 @@
+/* Looking Glass */
+
+// Dialog
+#LookingGlassDialog {
+ background-color: $osd_bg_color;
+ border-radius: 0 0 $modal_radius $modal_radius;
+ border-top-width: 0;
+ border: 1px solid $osd_outer_borders_color;
+ color: $osd_fg_color;
+ padding: $base_padding;
+ spacing: $base_padding;
+ box-shadow: 0 2px 4px 0 $shadow_color;
+
+ & > #Toolbar {
+ border: none;
+ padding: $base_padding;
+ border-radius: 0;
+ background-color: transparent;
+ spacing: $base_padding;
+
+ .lg-toolbar-button {
+ padding: $base_padding $base_padding*2;
+ @extend %button;
+
+ & > StIcon { icon-size: $base_icon_size; }
+ }
+ }
+
+ .labels {
+ spacing: $base_padding;
+ }
+
+ .notebook-tab {
+ -natural-hpadding: $base_padding*2;
+ -minimum-hpadding: $base_padding*2;
+
+ font-weight: bold;
+ padding: $base_padding $base_padding*2;
+ color: darken($osd_fg_color, 15%);
+ transition-duration: 100ms;
+ box-shadow:none;
+ border:none;
+ border-radius: $base_border_radius - 2px;
+ background-color: transparent;
+
+ &:hover {
+ color: $osd_fg_color;
+ background-color: transparentize($osd_fg_color, 0.95);
+ }
+
+ &:selected {
+ color: $osd_fg_color;
+ background-color: transparentize($osd_fg_color, 0.9);
+ }
+ }
+
+ StBoxLayout#EvalBox { padding: 4px; spacing: $base_padding; padding: $base_padding; }
+ StBoxLayout#ResultsArea { spacing: $base_padding; padding: $base_padding; }
+}
+
+.lg-dialog {
+
+ StEntry {
+ background-color: transparentize(lighten($osd_bg_color, 5%), 0.4);
+ color: $osd_fg_color;
+ border-color: transparentize($osd_fg_color, 0.8);
+ min-height: 22px;
+ selection-background-color: $selected_bg_color;
+ selected-color: $selected_fg_color;
+ }
+
+ .shell-link {
+ color: $link_color;
+ &:hover { color: lighten($link_color, 10%); }
+ &:active { color: darken($link_color, 10%); }
+ }
+
+ .actor-link {
+ color: $insensitive_fg_color;
+ &:hover { color: lighten($insensitive_fg_color, 20%); }
+ &:active { color: darken($insensitive_fg_color, 20%); }
+
+ & StIcon { icon-size: 12px; }
+ }
+}
+
+.lg-completions-text {
+ @extend %caption;
+ font-style: italic;
+}
+
+.lg-obj-inspector-title {
+ spacing: $base_padding;
+}
+
+.lg-obj-inspector-button {
+ border: 1px solid $osd_borders_color;
+ padding: 4px;
+ border-radius: $base_border_radius;
+ &:hover { border: 1px solid #ffffff; }
+}
+
+// Extensions
+#lookingGlassExtensions { padding: $base_padding; }
+
+.lg-extensions-list {
+ padding: $base_padding;
+ spacing: $base_padding;
+}
+
+.lg-extension {
+ padding: $base_padding*2;
+ @include card;
+}
+
+.lg-extension-name {
+ @extend %heading;
+}
+
+.lg-extension-meta {
+ spacing: $base_padding;
+}
+
+// Inspector
+#LookingGlassPropertyInspector {
+ background: $osd_bg_color;
+ border: 1px solid $osd_borders_color;
+ border-radius: $base_border_radius;
+ padding: $base_padding;
+}
+
+.lg-debug-flag-button {
+ StLabel { padding: $base_padding, 2 * $base_padding; }
+
+ color: $fg_color;
+ &:hover { color: lighten($fg_color, 20%); }
+ &:active { color: darken($fg_color, 20%); }
+}
+
+.lg-debug-flags-header {
+ padding-top: 2 * $base_padding;
+ padding: $base_padding;
+ @extend %title_2;
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_message-list.scss b/data/theme/gnome-shell-sass/widgets/_message-list.scss
new file mode 100644
index 0000000..5196661
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_message-list.scss
@@ -0,0 +1,180 @@
+/* Message List */
+// a.k.a. notifications in the menu
+
+// main list
+.message-list {
+ width: 29em;
+ border: solid $borders_color;
+
+ // padding and margins to account for scrollbar
+ &:ltr {margin-left: 0; margin-right: $base_margin; padding-right: $base_padding; border-right-width: 1px; }
+ &:rtl {margin-right: 0; margin-left: $base_margin; padding-left: $base_padding; border-left-width: 1px; }
+
+ .message-list-placeholder {
+ @extend %title_2;
+ color: transparentize($insensitive_fg_color, .5);
+
+ // icon size and color
+ > StIcon {
+ icon-size: $base_icon_size*3; // 48px
+ margin-bottom: $base_margin*3;
+ -st-icon-style: symbolic;
+ }
+ }
+}
+
+.message-list-sections {
+ spacing: $base_padding;
+ margin: 0;
+ padding-bottom: $base_padding;
+
+ // to account for scrollbar
+ &:ltr {margin-right: $base_margin * 3; }
+ &:rtl {margin-left: $base_margin * 3;}
+}
+
+.message-list-section,
+.message-list-section-list {
+ spacing: $base_padding;
+}
+
+// do-not-disturb + clear button
+.message-list-controls {
+ // NOTE: remove the padding if notification_bubble could remove margin for drop shadow
+ padding: $base_padding;
+ spacing: $base_padding;
+ @extend %heading;
+
+ .dnd-button {
+ // We need this because the focus outline isn't inset like for the buttons
+ // so the dnd button would grow when it gets focus if we didn't change only
+ // its color when focusing.
+ border-width: 2px;
+ border-color: transparent;
+ border-radius: 32px;
+ border-style: solid;
+
+ &:focus {
+ border-color: transparentize($selected_bg_color, 0.4);
+ }
+ }
+}
+
+// message bubbles
+.message {
+ @include card;
+
+ // icon container
+ .message-icon-bin {
+ padding: ($base_padding * 3);
+
+ &:ltr {padding-right:$base_padding;}
+ &:rtl {padding-left:$base_padding;}
+
+ // icon size and color
+ > StIcon {
+ icon-size: $large_icon_size; // 32px
+ -st-icon-style: symbolic;
+ }
+
+ // fallback
+ > .fallback-app-icon {
+ width: $base_icon_size;
+ height: $base_icon_size;
+ }
+ }
+
+ // content
+ .message-content {
+ spacing: 4px;
+ padding: ($base_padding*1.5);
+ margin-bottom: $base_margin*2;
+ }
+
+ // title
+ .message-title {
+ font-weight: bold;
+ /* HACK: the label should be baseline-aligned with a 1em label, fake this with some bottom padding */
+ padding-top: 0.57em;
+ }
+
+ // secondary container in title box
+ .message-secondary-bin {
+ padding: 0 $base_margin * 2;
+
+ // notification time stamp
+ > .event-time {
+ color: transparentize($fg_color, 0.5);
+ @include fontsize($base_font_size - 2);
+ /* HACK: the label should be baseline-aligned with a 1em label, fake this with some bottom padding */
+ padding-bottom: 0.13em;
+
+ &:ltr { text-align: right };
+ &:rtl { text-align: left };
+ }
+ }
+
+ // close button
+ .message-close-button {
+ color: $fg_color;
+ background-color: transparentize($fg_color, 0.9);
+ border-radius: 99px;
+ padding: $base_padding - 1px;
+ margin: 1px;
+ &:hover {background-color: transparentize($fg_color, 0.8);}
+ &:active {background-color: transparentize($fg_color, 0.9);}
+
+ & StIcon { icon-size: $base_icon_size; }
+ }
+
+ // body
+ .message-body {color: darken($fg_color, 10%);}
+}
+
+// URLs in messages
+.url-highlighter {
+ link-color: $link_color;
+}
+
+/* Media Controls */
+.message-media-control {
+ padding: 0 $base_padding*3;
+ margin: $base_padding*2 0;
+ border-radius: $base_border_radius;
+ color: $fg_color;
+
+ // colors are lightened since the media controls are in a card
+ &:hover {
+ background-color: lighten($hover_bg_color, 5%);
+ color: $fg_color;
+ }
+
+ &:active {
+ background-color: lighten($active_bg_color, 5%);
+ color: $fg_color;
+ }
+
+ &:insensitive { color: lighten($insensitive_fg_color, 5%); }
+
+ // fix margin for last button
+ &:last-child:ltr { margin-right: $base_margin*3; }
+ &:last-child:rtl { margin-left: $base_margin*3; }
+
+ & StIcon { icon-size: $base_icon_size; }
+}
+
+// album-art
+.media-message-cover-icon {
+ icon-size: $base_icon_size*3 !important; // 48px
+ border-radius: $base_border_radius;
+
+ // when there is no artwork
+ &.fallback {
+ color: darken($fg_color, 17%);
+ background-color: $bg_color;
+ border: 1px solid transparent;
+ border-radius: $base_border_radius;
+ icon-size: $large_icon_size !important; // 32px
+ padding: ($base_padding*2 + 2); // 16px
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_misc.scss b/data/theme/gnome-shell-sass/widgets/_misc.scss
new file mode 100644
index 0000000..7409278
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_misc.scss
@@ -0,0 +1,65 @@
+// Rubberband for select-area screenshots
+.select-area-rubberband {
+ background-color: transparentize($selected_bg_color,0.7);
+ border: 1px solid $selected_bg_color;
+}
+
+// User icon
+.user-icon {
+ background-size: contain;
+ color: $osd_fg_color;
+ border-radius: 99px;
+ icon-size: $base_icon_size * 4; // 64px
+ &:hover {
+ color: lighten($osd_fg_color,30%);
+ }
+
+ & StIcon {
+ background-color: transparentize($osd_fg_color,0.95);
+ border-radius: 99px;
+ padding: $base_padding * 2 ; // 12px
+ width: $base_icon_size * 2.5; height: $base_icon_size * 2.5; // 40px;
+ }
+
+ &.user-avatar {
+ border: 2px $osd_fg_color;
+ }
+}
+
+.user-widget.vertical .user-icon {
+ icon-size: $base_icon_size * 6; // 128px
+
+ & StIcon {
+ padding: $base_padding * 3 + 2px; // 20px
+ padding-top: $base_padding * 3; // 18 px
+ padding-bottom: $base_padding * 3 + 4px; // 22px
+ width: $base_icon_size * 5.5; height: $base_icon_size * 5.5; // 88px;
+ }
+}
+
+.lightbox { background-color: black; }
+.flashspot { background-color: white; }
+
+
+// Hidden
+.hidden { color: rgba(0,0,0,0);}
+
+// Caps-lock warning
+.caps-lock-warning-label {
+ text-align: center;
+ padding-bottom: 8px;
+ @extend %caption;
+ color: $warning_color;
+}
+
+/* Workspace animation */
+
+.workspace-animation {
+ background-color: $system_bg_color;
+}
+
+/* Tiled window previews */
+.tile-preview {
+ background-color: transparentize($selected_bg_color,0.5);
+ border: 1px solid $selected_bg_color;
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_notifications.scss b/data/theme/gnome-shell-sass/widgets/_notifications.scss
new file mode 100644
index 0000000..86480a8
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_notifications.scss
@@ -0,0 +1,61 @@
+/* Notifications & Message Tray */
+
+$notification_banner_height: 64px;
+$notification_banner_width: 34em;
+$notification_banner_radius: $base_border_radius*1.5;
+
+// Banner notifications
+.notification-banner {
+ min-height: $notification_banner_height;
+ width: $notification_banner_width;
+ box-shadow: 0 2px 4px 2px $shadow_color;
+ border-radius: $notification_banner_radius;
+ margin: $base_margin;
+
+ .notification-actions {
+ spacing: 0;
+ }
+
+ .notification-button {
+ @extend %bubble_button;
+ }
+}
+
+// counter
+.summary-source-counter {
+ font-size: $base_font_size - 1pt;
+ font-weight: bold;
+ height: 1.6em;
+ width: 1.6em;
+ -shell-counter-overlap-x: 3px;
+ -shell-counter-overlap-y: 3px;
+ background-color: $selected_bg_color;
+ color: $selected_fg_color;
+ border: 2px solid $fg_color;
+ box-shadow: 0 2px 2px rgba(0,0,0,0.5);
+ border-radius: 0.9em; // should be 0.8 but whatever; wish I could do 50%;
+}
+
+// chat bubbles
+.chat-body { spacing: 5px; }
+.chat-response { margin: 5px; }
+.chat-log-message { color: darken($fg_color,10%); }
+.chat-new-group { padding-top: 1em; }
+.chat-received {
+ padding-left: 4px;
+ &:rtl { padding-left: 0px; padding-right: 4px; }
+}
+
+.chat-sent {
+ padding-left: 18pt;
+ color: lighten($fg_color, 15%);
+ &:rtl { padding-left: 0; padding-right: 18pt; }
+}
+
+.chat-meta-message {
+ padding-left: 4px;
+ @include fontsize($base_font_size - 2);
+ font-weight: bold;
+ color: lighten($fg_color,18%);
+ &:rtl { padding-left: 0; padding-right: 4px; }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_osd.scss b/data/theme/gnome-shell-sass/widgets/_osd.scss
new file mode 100644
index 0000000..a8d1a55
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_osd.scss
@@ -0,0 +1,64 @@
+/* OSD */
+
+$osd_levelbar_height:6px;
+
+.osd-window {
+ @extend %osd_panel;
+ @extend %heading;
+ text-align: center;
+ font-weight: bold;
+ spacing: $base_padding * 2; // 12px
+ padding: $base_padding * 2 $base_padding * 3;
+ & > * { spacing: 8px; }
+ margin-bottom: 4em;
+
+ StIcon { icon-size:$large_icon_size;}
+
+ StLabel {
+ &:ltr { margin-right: 6px; }
+ &:rtl { margin-left: 6px; }
+ }
+
+ .level {
+ margin-bottom: 4px;
+ &:first-child { margin-bottom: 0px; }
+
+ height: $osd_levelbar_height;
+ min-width:$base_icon_size * 10;
+ -barlevel-height: $osd_levelbar_height;
+ -barlevel-background-color: transparentize($osd_fg_color, if($variant=='light', 0.7, 0.9));
+ -barlevel-active-background-color: $osd_fg_color;
+ -barlevel-overdrive-color: $destructive_color;
+ -barlevel-overdrive-separator-width: 3px;
+ &:ltr { margin-right: 6px; }
+ &:rtl { margin-left: 6px; }
+ }
+}
+
+// Monitor number label
+.osd-monitor-label {
+ @extend %osd_panel;
+ border-radius: $modal_radius;
+ font-size: 3em;
+ font-weight: bold;
+ margin: $base_margin * 3;
+ text-align: center;
+ min-width: 1.3em;
+}
+
+/* Pad OSD */
+.pad-osd-window {
+ padding: 32px;
+ background-color: transparentize(#000, 0.2);
+
+ .pad-osd-title-box { spacing: 12px; }
+ .pad-osd-title-menu-box { spacing: 6px; }
+}
+
+.combo-box-label {
+ width: 15em;
+}
+
+.resize-popup {
+ @extend %osd_panel;
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_overview.scss b/data/theme/gnome-shell-sass/widgets/_overview.scss
new file mode 100644
index 0000000..d8e70ea
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_overview.scss
@@ -0,0 +1,13 @@
+/* OVERVIEW */
+
+.controls-manager, .secondary-monitor-workspaces {
+ spacing: $base_padding * 2;
+}
+
+#overviewGroup {
+ background-color: $system_bg_color;
+}
+
+.overview-controls {
+ padding-bottom: 32px;
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_panel.scss b/data/theme/gnome-shell-sass/widgets/_panel.scss
new file mode 100644
index 0000000..fd3e7d2
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_panel.scss
@@ -0,0 +1,233 @@
+/* Top Bar */
+// a.k.a. the panel
+
+$panel_bg_color: #000;
+$panel_fg_color: if($variant == 'light', lighten($bg_color, 10%), darken($fg_color, 5%));
+$panel_height: 2.2em;
+$panel_transition_duration: 250ms; // same as the overview transition duration
+
+#panel {
+ background-color: $panel_bg_color;
+ font-weight: bold;
+ height: $panel_height;
+ @extend %numeric;
+ transition-duration: $panel_transition_duration;
+
+ // transparent panel on lock & login screens
+ &.unlock-screen,
+ &.login-screen,
+ &:overview {
+ background-color: transparent;
+ }
+
+ // panel menus
+ .panel-button {
+ font-weight: bold;
+ color: $panel_fg_color;
+ -natural-hpadding: $base_padding * 2;
+ -minimum-hpadding: $base_padding;
+ transition-duration: 150ms;
+ border: 3px solid transparent;
+ border-radius: 99px;
+
+ &.clock-display {
+ .clock {
+ transition-duration: 150ms;
+ border: 3px solid transparent;
+ border-radius: 99px;
+ }
+ }
+
+ &.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px $screenshot_ui_button_red;
+ }
+ &.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px $warning_color;
+ StBoxLayout { margin: 0 $base_padding; }
+ }
+
+ &.screen-recording-indicator,
+ &.screen-sharing-indicator {
+ StBoxLayout {
+ spacing: $base_padding;
+ }
+
+ StIcon {
+ icon-size: $base_icon_size;
+ }
+ }
+
+ &:active, &:overview, &:focus, &:checked {
+ box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.8);
+
+ // The clock display needs to have the background on .clock because
+ // we want to exclude the do-not-disturb indicator from the background
+ &.clock-display {
+ box-shadow: none;
+
+ .clock {
+ box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.8);
+ }
+ }
+
+ &.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15);
+ }
+ &.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.15);
+ }
+ }
+
+ &:hover {
+ box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.85);
+ &.clock-display {
+ box-shadow: none;
+ .clock {
+ box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.85);
+ }
+ }
+
+ &.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1);
+ }
+ &.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.1);
+ }
+ }
+
+ &:active:hover, &:overview:hover, &:focus:hover, &:checked:hover {
+ box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.75);
+ &.clock-display {
+ box-shadow: none;
+ .clock {
+ box-shadow: inset 0 0 0 100px transparentize($panel_fg_color, 0.75);
+ }
+ }
+
+ &.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2);
+ }
+ &.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.2);
+ }
+ }
+
+ // status area icons
+ .system-status-icon {
+ icon-size: $base_icon_size;
+ padding: $base_padding - 1px;
+ margin: 0 $base_margin;
+ }
+
+ .panel-status-indicators-box .system-status-icon,
+ .panel-status-menu-box .system-status-icon {
+ margin: 0;
+ }
+
+ // app menu icon
+ .app-menu-icon {
+ -st-icon-style: symbolic;
+ // dimensions of the icon are hardcoded
+ }
+
+ &#panelActivities {
+ -natural-hpadding: $base_padding * 3;
+ }
+ }
+
+ &.unlock-screen,
+ &.login-screen,
+ &:overview {
+ .panel-button {
+ &:active, &:overview, &:focus, &:checked {
+ box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.15);
+
+ &.clock-display {
+ box-shadow: none;
+
+ .clock {
+ box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.15);
+ }
+ }
+
+ &.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.15);
+ }
+ &.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.15);
+ }
+ }
+
+ &:hover {
+ box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.10);
+ &.clock-display {
+ box-shadow: none;
+ .clock {
+ box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.10);
+ }
+ }
+
+ &.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.1);
+ }
+ &.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.1);
+ }
+ }
+
+ &:active:hover, &:overview:hover, &:focus:hover, &:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.2);
+ &.clock-display {
+ box-shadow: none;
+ .clock {
+ box-shadow: inset 0 0 0 100px rgba(255,255,255, 0.2);
+ }
+ }
+
+ &.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($screenshot_ui_button_red, 0.2);
+ }
+ &.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px transparentize($warning_color, 0.2);
+ }
+ }
+ }
+ }
+
+ .panel-status-indicators-box,
+ .panel-status-menu-box {
+ spacing: 2px;
+ }
+
+ // spacing between power icon and (optional) percentage label
+ .power-status.panel-status-indicators-box {
+ spacing: 0;
+ }
+
+ // indicator for active
+ .screencast-indicator,
+ .remote-access-indicator { color: $warning_color; }
+}
+
+// App Menu
+#appMenu {
+ spacing: $base_padding;
+ .label-shadow { color: transparent; }
+}
+
+#appMenu .panel-status-menu-box {
+ padding: 0 $base_padding;
+ spacing: $base_padding;
+}
+
+
+// Clock
+
+.clock-display-box {
+ spacing: 2px;
+
+ .clock {
+ padding-left: $base_padding * 2;
+ padding-right: $base_padding * 2;
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_popovers.scss b/data/theme/gnome-shell-sass/widgets/_popovers.scss
new file mode 100644
index 0000000..138a4fc
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_popovers.scss
@@ -0,0 +1,182 @@
+/* Popovers/Menus */
+
+// the popover itself
+.popup-menu-boxpointer {
+ -arrow-rise: $base_margin+2px; // distance from the panel & screen edge
+}
+
+// container of the popover menu
+.popup-menu {
+ min-width: 15em;
+ color: $fg_color;
+
+ //.popup-status-menu-item {font-weight: normal; color: pink;} //dunno what that is
+ &.panel-menu {
+ margin-bottom: 1.75em; // so it doesn't touch the bottom of the screen
+ }
+}
+
+// popover content
+.popup-menu-content {
+ padding: $base_padding;
+ border-radius: $modal_radius*1.25;
+ border: 1px solid $borders_edge;
+ box-shadow: 0 2px 4px 0 $shadow_color;
+ background-color: $bg_color;
+}
+
+// menu items
+.popup-menu-item {
+ padding: $base_padding*1.5 $base_padding*2;
+ border-radius: $base_border_radius*1.5;
+ spacing: $base_padding;
+ transition-duration: 100ms;
+ background-color: transparent;
+
+ &:ltr {padding-left: $base_padding;}
+ &:rtl {padding-right: $base_padding;}
+
+ &:focus, &:hover {
+ background-color: $hover_bg_color !important;
+ &:active { background-color: $active_bg_color !important;}
+ }
+
+ &:checked {background-color: $checked_bg_color !important;}
+
+ &:checked {
+ margin-bottom: 0;
+ box-shadow: inset 0 -1px 0 0 darken($checked_bg_color, 5%);
+ border-radius: $base_border_radius $base_border_radius 0 0;
+ &:focus,&:hover { background-color: lighten($checked_bg_color, 3%) !important;}
+ &:active { background-color: lighten($checked_bg_color, 5%) !important;}
+ }
+
+ &:active {
+ background-color: lighten($active_bg_color, 5%);
+ color: $active_fg_color;
+ }
+
+ &:insensitive {color: transparentize($fg_color,0.5);}
+
+ // add margin to switches in menu items
+ .toggle-switch {
+ &:ltr { margin-left: $base_margin;}
+ &:rtl { margin-right: $base_margin;}
+ }
+}
+
+
+// all other graphical elements (sliders)
+.popup-inactive-menu-item {
+ color: $fg_color;
+ &:insensitive { color: $insensitive_fg_color; }
+}
+
+// symbolic icons in popover
+.popup-menu-arrow,
+.popup-menu-icon {
+ icon-size: 16px !important; // for some reason the variable doesn't work here
+}
+
+.popup-menu-arrow {
+}
+
+
+// popover submenus
+.popup-sub-menu {
+ background-color: $checked_bg_color;
+ border-radius: 0 0 $base_border_radius $base_border_radius;
+
+ .popup-menu-ornament {
+ min-width: $base_icon_size !important;
+ }
+
+ // submenu specific styles
+ .popup-menu-item {
+ border-radius: 0;
+ margin: 0;
+
+ &:last-child {
+ border-radius: 0 0 $base_border_radius $base_border_radius;
+ }
+
+ &:focus,&:hover { background-color: $hover_bg_color !important;}
+ &:checked {
+ background-color: $checked_bg_color !important;
+ &:focus,&:hover { background-color: lighten($checked_bg_color, 8%) !important;}
+ }
+ &:active { background-color: $active_bg_color !important;}
+ }
+
+ .popup-menu-section {
+ .popup-menu-item:last-child {
+ &:hover,&:focus { border-radius: 0;}
+ }
+ &:last-child .popup-menu-item:last-child {
+ border-radius: 0 0 $base_border_radius $base_border_radius;
+ }
+ }
+}
+
+// container for radio and check boxes
+.popup-menu-ornament {
+ @extend %heading;
+ width: 1.2em;
+ text-align: center !important;
+
+ &:ltr { text-align: right;}
+ &:rtl { text-align: left;}
+}
+
+// separator
+.popup-separator-menu-item {
+ margin: 6px 0;
+ padding:0 !important;
+ &:ltr { margin-right: $base_margin;}
+ &:rtl { margin-left: $base_margin;}
+
+ .popup-separator-menu-item-separator {
+ height: 1px; //not really the whole box
+ background-color: $borders_color;
+ }
+
+ .popup-menu-ornament {
+ width: 0 !important;
+ }
+
+ // separators in submenus
+ .popup-sub-menu & {
+ background-color: transparent;
+
+ // account for ornament
+ &:ltr { margin-right: 2.5em;}
+ &:rtl { margin-left: 2.5em;}
+
+ .popup-separator-menu-item-separator {
+ background-color: lighten($borders_color, 7%);
+ }
+ }
+}
+
+// desktop background menu
+.background-menu {
+ -boxpointer-gap: 0px;
+ -arrow-rise: 0px; // hide the beak on the menu
+}
+
+// right-click (and panel) app menu
+.app-menu {
+ max-width: 27.25em;
+
+ // this is unneeded at the top-level in this menu, hide it
+ .popup-menu-ornament { width: 0 !important; }
+
+ .popup-inactive-menu-item:first-child {
+ // "Open Windows" label
+ > StLabel {
+ @extend %caption_heading;
+ &:ltr {margin-right: $base_margin*2;}
+ &:rtl {margin-left: $base_margin*2;}
+ }
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_quick-settings.scss b/data/theme/gnome-shell-sass/widgets/_quick-settings.scss
new file mode 100644
index 0000000..b429a2d
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_quick-settings.scss
@@ -0,0 +1,118 @@
+.quick-settings {
+ padding: $base_padding*3;
+ border-radius: $modal_radius*2.25;
+
+ .icon-button, .button {
+ padding: $base_padding*1.75;
+ }
+}
+
+.quick-settings-grid {
+ spacing-rows: $base_padding*2;
+ spacing-columns: $base_padding*2;
+}
+
+.quick-toggle {
+ border-radius: 99px;
+ min-width: 12em;
+ max-width: 12em;
+ min-height: 40px;
+ border:none;
+
+ &:checked { @include button(default); }
+
+ & > StBoxLayout { spacing: $base_padding; }
+
+ /* Move padding into the box; this is to allow menu arrows
+ to extend to the border */
+ &.button { padding: 0; }
+ & > StBoxLayout { padding: 0 $base_padding*2; }
+ &:ltr > StBoxLayout { padding-left: $base_padding*2.5; }
+ &:rtl > StBoxLayout { padding-right: $base_padding*2.5; }
+
+ .quick-toggle-label { font-weight: bold; }
+ .quick-toggle-icon, .quick-toggle-arrow { icon-size: $base_icon_size; }
+}
+
+.quick-menu-toggle {
+ &:ltr > StBoxLayout { padding-right: 0; }
+ &:rtl > StBoxLayout { padding-left: 0; }
+
+ & .quick-toggle-arrow {
+ background-color: transparentize($fg_color, 0.9);
+ padding: $base_padding $base_padding*1.75;
+
+ &:ltr { border-radius: 0 99px 99px 0; }
+ &:rtl { border-radius: 99px 0 0 99px; }
+ }
+}
+
+.quick-slider {
+ & > StBoxLayout { spacing: $base_padding; }
+
+ .slider-bin {
+ &:focus {@include button(focus);}
+ min-height: 16px; // slider size
+ padding: $base_padding;
+ border-radius: 99px;
+ }
+ .quick-toggle-icon {
+ icon-size: $base_icon_size;
+
+ &:ltr { margin-left: $base_padding; }
+ &:rtl { margin-right: $base_padding; }
+ }
+}
+
+.quick-toggle-menu {
+ background-color: $card_bg_color;
+ border-radius: $base_border_radius*3;
+ padding: $base_padding*2;
+ margin: $base_padding*2 $base_padding*3 0;
+
+ .popup-menu-item > StIcon { -st-icon-style: symbolic; }
+
+ & .header {
+ spacing-rows: 0.5 * $base_padding;
+ spacing-columns: $base_padding*2;
+ padding-bottom: 2 * $base_padding;
+
+ & .icon {
+ icon-size: $base_icon_size*1.5; // a non-standard symbolic size but ok
+ border-radius: 999px;
+ padding: 1.5 * $base_padding;
+ background-color: lighten($bg_color, 10%);
+
+ &.active { background-color: $selected_bg_color; }
+ }
+
+ & .title {
+ @extend %title_3;
+ }
+
+ & .subtitle {
+ @extend %caption_heading;
+ }
+ }
+}
+
+.quick-toggle-menu-container {
+}
+
+.quick-settings-system-item {
+ & > StBoxLayout { spacing: 2 * $base_padding; }
+
+ & .power-item {
+ min-height: 0;
+ min-width: 0;
+
+ &:insensitive {
+ @include button(normal);
+ background-color: transparent;
+ }
+ }
+}
+
+.nm-network-item {
+ .wireless-secure-icon { icon-size: 0.5 * $base_icon_size; }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_screen-shield.scss b/data/theme/gnome-shell-sass/widgets/_screen-shield.scss
new file mode 100644
index 0000000..a459ba1
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_screen-shield.scss
@@ -0,0 +1,78 @@
+/* Screen Shield */
+
+.unlock-dialog-clock {
+ color: white;
+ font-weight: 300;
+ text-align: center;
+ spacing: 24px;
+ padding-bottom: 2.5em;
+}
+
+.unlock-dialog-clock-time {
+ font-size: 64pt;
+ padding-top: 42px;
+ @extend %numeric;
+}
+
+.unlock-dialog-clock-date {
+ font-size: 16pt;
+ font-weight: normal;
+}
+
+.unlock-dialog-clock-hint {
+ font-weight: normal;
+ padding-top: 48px;
+}
+
+.unlock-dialog-notifications-container {
+ margin: 12px;
+ spacing: 6px;
+ width: 23em;
+ background-color: transparent;
+
+ .summary-notification-stack-scrollview {
+ padding-top: 0;
+ padding-bottom: 0;
+ }
+
+ .notification,
+ .unlock-dialog-notification-source {
+ padding: 12px 16px;
+ border: none;
+ background-color: transparentize($osd_fg_color,0.9);
+ color: $osd_fg_color;
+ border-radius: $modal_radius;
+
+ &.critical { background-color: transparentize($osd_fg_color,0.8) }
+ }
+}
+
+.unlock-dialog-notification-label {
+ padding-left: 12px;
+ padding-right: 0;
+ &:rtl { padding-right: 12px; padding-left: 0; }
+}
+
+.unlock-dialog-notification-count-text {
+ font-weight: bold;
+ padding: 0 12px;
+ color: $osd_fg_color;
+ background-color: transparentize($osd_fg_color, 0.9);
+ border-radius: 99px;
+}
+
+.screen-shield-background { //just the shadow, really
+ background: black;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.6);
+}
+
+#lockDialogGroup {
+ background-color: $system_bg_color;
+}
+#unlockDialogNotifications {
+ StButton#vhandle, StButton#hhandle {
+ background-color: transparentize($bg_color,0.7);
+ &:hover, &:focus { background-color: transparentize($bg_color,0.5); }
+ &:active { background-color: transparentize($selected_bg_color,0.5); }
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_screenshot.scss b/data/theme/gnome-shell-sass/widgets/_screenshot.scss
new file mode 100644
index 0000000..150f273
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_screenshot.scss
@@ -0,0 +1,204 @@
+// Screenshot UI
+.icon-label-button-container {
+ spacing: $base_padding;
+ @extend %caption;
+
+ StIcon { icon-size: 32px;}
+}
+
+$screenshot_ui_panel_padding: $base_padding*3;
+$screenshot_ui_shot_cast_margin: 21px;
+$screenshot_ui_panel_border_radius: $modal_radius + $screenshot_ui_shot_cast_margin;
+$screenshot_ui_shot_cast_spacing: 3px;
+
+$screenshot_ui_button_red: $error_color;
+
+.screenshot-ui-panel {
+ @extend %osd_panel;
+ border-radius: $screenshot_ui_panel_border_radius;
+ padding: $screenshot_ui_panel_padding;
+ // Reduce the bottom padding a little to accommodate the large capture button.
+ padding-bottom: $screenshot_ui_panel_padding - 6px;
+ margin-bottom: 4em;
+ spacing: $base_padding * 2;
+}
+
+.screenshot-ui-close-button {
+ @extend .window-close; // copy window close button
+ padding: $base_padding !important; // but with more padding
+ margin-top: 12px;
+ box-shadow: 0 2px 4px 0 $shadow_color;
+ &.left { margin-left: 12px;}
+ &.right { margin-right: 12px;}
+}
+
+.screenshot-ui-type-button {
+ @extend %osd_button;
+ min-width: 48px;
+ padding: $base_padding * 2 $base_padding * 3 !important;
+ border-radius: $screenshot_ui_panel_border_radius - $screenshot_ui_panel_padding;
+}
+
+.screenshot-ui-capture-button {
+ width: 36px;
+ height: 36px;
+ border-radius: 99px;
+ border: 4px $osd_fg_color;
+ padding: 4px;
+
+ .screenshot-ui-capture-button-circle {
+ background-color: $osd_fg_color;
+ transition-duration: 200ms;
+ &:hover, &:focus { background-color: $hover_bg_color;}
+ border-radius: 99px;
+ }
+
+ &:hover, &:focus {
+ .screenshot-ui-capture-button-circle {
+ background-color: darken($osd_fg_color, 15%);
+ }
+ }
+
+ &:active {
+ .screenshot-ui-capture-button-circle {
+ background-color: darken($osd_fg_color, 50%);
+ }
+ }
+
+ &:cast {
+ .screenshot-ui-capture-button-circle {
+ background-color: $screenshot_ui_button_red;
+ }
+ &:hover, &:focus {
+ .screenshot-ui-capture-button-circle {
+ background-color: lighten($screenshot_ui_button_red, 5%);
+ }
+ }
+ &:active {
+ .screenshot-ui-capture-button-circle {
+ background-color: darken($screenshot_ui_button_red, 7%);
+ }
+ }
+ }
+}
+
+.screenshot-ui-shot-cast-container {
+ background-color: lighten($osd_bg_color,5%);
+ border-radius: $modal_radius;
+ padding: $screenshot_ui_shot_cast_spacing;
+ spacing: $screenshot_ui_shot_cast_spacing;
+
+ &:ltr { margin-left: $screenshot_ui_shot_cast_margin - $screenshot_ui_panel_padding;}
+ &:rtl { margin-right: $screenshot_ui_shot_cast_margin - $screenshot_ui_panel_padding;}
+}
+
+.screenshot-ui-shot-cast-button {
+ padding: $base_padding $base_padding*2;
+ background-color: transparent;
+ &:hover, &:focus { background-color: lighten($osd_bg_color, 10%);}
+ &:active { background-color: lighten($active_bg_color,5%);}
+ &:checked { background-color: white;color: black;}
+ &:insensitive { color: transparentize($osd_fg_color, 0.5);}
+
+ border-radius: $modal_radius - $screenshot_ui_shot_cast_spacing;
+
+ StIcon { icon-size: $base_icon_size;}
+}
+
+.screenshot-ui-show-pointer-button {
+ @extend %osd_button;
+ border-radius: 99px;
+ padding: $base_padding * 2 !important;
+ StIcon { icon-size: $base_icon_size;}
+}
+
+.screenshot-ui-area-indicator-shade {
+ background-color: rgba(0,0,0,.3);
+}
+
+.screenshot-ui-area-selector {
+ .screenshot-ui-area-indicator-shade {
+ background-color: rgba(0,0,0,.5);
+ }
+
+ .screenshot-ui-area-indicator-selection {
+ border: 2px white;
+ }
+}
+
+.screenshot-ui-area-selector-handle {
+ border-radius: 99px;
+ background-color: white;
+ box-shadow: 0 1px 3px 2px rgba(0,0,0,0.2);
+ width: 24px;
+ height: 24px;
+}
+
+.screenshot-ui-window-selector {
+ background-color: $system_bg_color;
+
+ .screenshot-ui-window-selector-window-container {
+ margin: 100px;
+ }
+
+ &:primary-monitor {
+ .screenshot-ui-window-selector-window-container {
+ // Make some room for the panel.
+ margin-bottom: 200px;
+ }
+ }
+}
+
+.screenshot-ui-window-selector-window-border {
+ transition-duration: 200ms;
+ border-radius: $modal_radius;
+ border: 6px transparent;
+}
+
+.screenshot-ui-window-selector-check {
+ transition-duration: 200ms;
+ color: transparent;
+ border-radius: 99px;
+ border-width: 12px;
+ icon-size: 24px;
+}
+
+.screenshot-ui-window-selector-window {
+ &:hover {
+ .screenshot-ui-window-selector-window-border {
+ border-color: darken($selected_bg_color, 15%);
+ }
+ }
+ &:checked {
+ .screenshot-ui-window-selector-window-border {
+ border-color: $selected_bg_color;
+ background-color: transparentize($selected_bg_color, 0.8);
+ }
+
+ .screenshot-ui-window-selector-check {
+ color: $selected_fg_color;
+ background-color: $selected_bg_color;
+ }
+ }
+}
+
+.screenshot-ui-screen-selector {
+ transition-duration: 200ms;
+ background-color: rgba(0,0,0,.5);
+
+ &:hover { background-color: rgba(0,0,0,.3);}
+ &:active { background-color: rgba(0,0,0,.7);}
+ &:checked {
+ background-color: transparent;
+ border: 2px white;
+ }
+}
+
+.screenshot-ui-tooltip {
+ color: $osd_fg_color;
+ background-color: $osd_bg_color;
+ border-radius: 99px;
+ padding: $base_padding $base_padding * 2;
+ text-align: center;
+ -y-offset: 24px;
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_scrollbars.scss b/data/theme/gnome-shell-sass/widgets/_scrollbars.scss
new file mode 100644
index 0000000..77cde65
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_scrollbars.scss
@@ -0,0 +1,29 @@
+/* Scrollbars */
+
+StScrollView {
+ &.vfade { -st-vfade-offset: 68px; }
+ &.hfade { -st-hfade-offset: 68px; }
+}
+
+StScrollBar {
+ padding: 0;
+
+ StScrollView & {
+ min-width: 8px;
+ min-height: 8px;
+ }
+
+ StBin#trough {
+ border-radius: 0;
+ background-color: transparent;
+ }
+
+ StButton#vhandle, StButton#hhandle {
+ border-radius: 8px;
+ background-color: mix($fg_color, $bg_color, 30%);
+ border: 3px solid transparent; //would be nice to margin or at least to transparent
+ transition: 500ms all ease;
+ &:hover {background-color: mix($fg_color, $bg_color, 50%);}
+ &:active {background-color: mix($fg_color, $bg_color, 40%);}
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_search-entry.scss b/data/theme/gnome-shell-sass/widgets/_search-entry.scss
new file mode 100644
index 0000000..faca50e
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_search-entry.scss
@@ -0,0 +1,26 @@
+// Search entry
+
+$search_entry_width: 320px;
+$search_entry_height: 36px;
+
+%search_entry,
+.search-entry {
+ border-radius: $search_entry_height * 0.5; // half the height
+
+ margin-top: $base_padding * 2;
+ margin-bottom: $base_padding;
+ padding: $base_padding+1 $base_padding+3;
+ width: $search_entry_width;
+
+ @include entry(normal);
+ &:hover { @include entry(hover);}
+ &:focus { @include entry(focus);}
+ &:insensitive { @include entry(insensitive);}
+
+ .search-entry-icon {
+ color: inherit;
+ icon-size: $base_icon_size;
+ margin-top: 2px; // center vertically
+ padding: 0 4px;
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_search-results.scss b/data/theme/gnome-shell-sass/widgets/_search-results.scss
new file mode 100644
index 0000000..644c53d
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_search-results.scss
@@ -0,0 +1,96 @@
+/* Search */
+
+// search overview container
+#searchResults {
+ margin: 0 $base_margin;
+}
+
+#searchResultsContent {
+ max-width: 1072px;
+}
+
+// search results sections "the boxes"
+.search-section {
+ // This should be equal to #searchResultsContent spacing
+ spacing: $base_padding*3;
+
+ // separator (unstyled)
+ .search-section-separator {
+ height: $base_margin*2; // use it as a spacer
+ background-color: transparent;
+ }
+}
+
+// content
+.search-section-content {
+ background-color: lighten($system_bg_color, 5%);
+ border-radius: $modal_radius*1.5;
+ border: 1px solid $osd_outer_borders_color;
+ color: $osd_fg_color;
+ padding: $base_padding*2;
+ margin:0 $base_margin*3;
+}
+
+%search_section_content_item {
+ @include tile_button($osd_fg_color);
+ border-radius: $base_border_radius+3px;
+}
+
+// "no results" text
+.search-statustext {
+ @extend %title_1;
+}
+
+.grid-search-results {
+ spacing: $base_padding*5;
+ margin:0 $base_margin*3;
+}
+
+// Search results with icons
+.grid-search-result {
+ @extend .app-well-app;
+}
+
+// search result provider
+.search-provider-icon {
+ @extend %search_section_content_item;
+ &:ltr {margin-right: $base_margin;}
+ &:rtl {margin-left: $base_margin;}
+
+ // content
+ .list-search-provider-content {
+ spacing: $base_padding * 2;
+
+ // provider labels
+ .list-search-provider-details {
+ width: 120px;
+ color: $osd_fg_color;
+ }
+ }
+}
+
+// search results list
+.list-search-results {
+ spacing: $base_padding;
+}
+
+// search result listitem
+.list-search-result {
+ @extend %search_section_content_item;
+
+ // content
+ .list-search-result-content {
+ spacing: $base_padding;
+ }
+
+ // list item title (with leading icon)
+ .list-search-result-title {
+ spacing: $base_padding * 2;
+ // font-weight: bold;
+ }
+
+ // list item description
+ .list-search-result-description {
+ color: $osd_insensitive_fg_color;
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_slider.scss b/data/theme/gnome-shell-sass/widgets/_slider.scss
new file mode 100644
index 0000000..500249a
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_slider.scss
@@ -0,0 +1,22 @@
+/* Slider */
+
+$slider_size: 16px;
+
+.slider {
+ // slider trough
+ -barlevel-height: 4px;
+ -barlevel-background-color: if($variant == 'light', transparentize($fg_color, 0.6), transparentize($fg_color, 0.8)); //background of the trough
+ -barlevel-border-width: 2px;
+ -barlevel-border-color: transparent; // trough border color
+ // fill style
+ -barlevel-active-background-color: $selected_bg_color;
+ -barlevel-active-border-color: transparent;
+ // overfill style (red in this case)
+ -barlevel-overdrive-color: $destructive_color;
+ -barlevel-overdrive-border-color: transparent; //trough border when red;
+ -barlevel-overdrive-separator-width:1px;
+ // slider handler
+ -slider-handle-radius: $slider_size * 0.5; // half the size of the size
+ -slider-handle-border-width: 0;
+ -slider-handle-border-color: transparent; // because 0 width
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_switcher-popup.scss b/data/theme/gnome-shell-sass/widgets/_switcher-popup.scss
new file mode 100644
index 0000000..cec6c9a
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_switcher-popup.scss
@@ -0,0 +1,72 @@
+/* App Switcher */
+
+// same as dash
+$switcher_padding: $base_padding + 4px; // 10px
+$switcher_border_radius: $modal_radius + 8px;
+
+
+// the full screen container of the switcher
+.switcher-popup {
+ padding: 0;
+ spacing: $base_padding * 4;
+}
+
+// switcher onscreen panel
+.switcher-list {
+ @extend %osd_panel;
+ padding: $switcher_padding;
+ border-radius: $switcher_border_radius;
+ box-shadow: 0 8px 8px 0 rgba(0,0,0,0.1);
+
+ // container for items in list
+ .switcher-list-item-container {
+ spacing: $base_padding * 2;
+ }
+
+ // each item in the list
+ .item-box {
+ @include tile_button($osd_fg_color);
+ // override over style so mouse doesn't steal focus
+ &:hover {background: none;}
+ }
+
+ .separator {
+ width: 1px;
+ background: $borders_color;
+ }
+
+ // container of thumbnails
+ .thumbnail-box {
+ padding: 2px;
+ spacing: $base_padding;
+ }
+
+ // window thumbnail itself
+ .thumbnail {
+ width: 256px; // equal to THUMBNAIL_DEFAULT_SIZE in altTab.js
+ border-radius:$base_border_radius;
+ }
+}
+
+// arrow if app has multiple windows
+.switcher-arrow {
+ border-color: transparentize($osd_fg_color, 0.2);
+ color: transparentize($osd_fg_color, 0.2);
+
+ &:highlighted {
+ border-color: $osd_fg_color;
+ color: $osd_fg_color;
+ }
+}
+
+// Input Source Switcher
+.input-source-switcher-symbol {
+ font-size: 34pt;
+ width: 96px;
+ height: 96px;
+}
+
+// Window cycler highlight
+.cycler-highlight {
+ border: 5px solid $selected_bg_color;
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_switches.scss b/data/theme/gnome-shell-sass/widgets/_switches.scss
new file mode 100644
index 0000000..b6d8fe8
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_switches.scss
@@ -0,0 +1,16 @@
+/* Switches */
+
+// these are equal to the size of the SVG assets
+$switch_height: 26px;
+$switch_width: 48px;
+
+.toggle-switch {
+ color: $fg_color;
+ height: $switch_height;
+ width: $switch_width;
+ background-size: contain;
+ background-image: if($variant == 'light', url("resource:///org/gnome/shell/theme/toggle-off-light.svg"),url("resource:///org/gnome/shell/theme/toggle-off.svg"));
+ &:checked {
+ background-image: if($variant == 'light', url("resource:///org/gnome/shell/theme/toggle-on-light.svg"),url("resource:///org/gnome/shell/theme/toggle-on.svg"));
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_window-picker.scss b/data/theme/gnome-shell-sass/widgets/_window-picker.scss
new file mode 100644
index 0000000..9067a6d
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_window-picker.scss
@@ -0,0 +1,53 @@
+/* Window Picker */
+
+$window_picker_spacing: $base_padding; // 6px
+$window_picker_padding: $base_padding * 2; // 12px
+
+$window_thumbnail_label_color: transparentize($osd_bg_color, 0.4);
+
+$window_close_button_color: transparentize(lighten($osd_bg_color, 7%), .02);
+$window_close_button_size: 30px;
+$window_close_button_padding: 3px;
+
+// Window picker
+.window-picker {
+ // Space between window thumbnails
+ spacing: $window_picker_spacing;
+}
+
+// Window titles
+.window-caption {
+ color: $osd_fg_color;
+ background-color: lighten($osd_bg_color, 5%);
+ border-radius: 99px;
+ padding: $base_padding $base_padding * 2;
+}
+
+// Close button
+.window-close {
+ background-color: $window_close_button_color;
+ color: $osd_fg_color;
+ border-radius: 99px;
+ box-shadow: 0 2px 4px 0 $shadow_color;
+ padding: $window_close_button_padding;
+ height: $window_close_button_size;
+ width: $window_close_button_size;
+ transition-duration: 100ms;
+
+ & StIcon { icon-size: 24px; } // uses non standard icon size
+
+ &:hover {
+ background-color: lighten($window_close_button_color, 7%);
+ }
+
+ &:active {
+ background-color: lighten($window_close_button_color, 13%);
+ }
+}
+
+.workspace-background {
+ // keep in sync with BACKGROUND_CORNER_RADIUS_PIXELS in workspace.js
+ border-radius: 30px;
+ background-color: $invisible_occluded_bg_color;
+ box-shadow: 0 4px 16px 4px transparentize(darken($osd_bg_color, 30%), 0.7);
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_workspace-switcher.scss b/data/theme/gnome-shell-sass/widgets/_workspace-switcher.scss
new file mode 100644
index 0000000..f06130b
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_workspace-switcher.scss
@@ -0,0 +1,25 @@
+/* Workspace Switcher */
+
+$ws_indicator_height: $large_icon_size;
+$ws_dot_active: $ws_indicator_height / 3;
+$ws_dot_inactive: $ws_indicator_height / 6;
+
+.workspace-switcher {
+ @extend %osd_panel;
+ margin-bottom: 4em;
+ spacing: $base_padding * 2;
+ padding: $base_padding * 2 $base_padding * 3;
+}
+
+.ws-switcher-indicator {
+ background-color: transparentize($osd_fg_color,0.5);
+ padding: $ws_dot_inactive / 2;
+ margin: ($ws_indicator_height - $ws_dot_inactive) / 2;
+ border-radius: $ws_indicator_height;
+
+ &:active {
+ background-color: $osd_fg_color;
+ padding: $ws_dot_active / 2;
+ margin: ($ws_indicator_height - $ws_dot_active) / 2;
+ }
+}
diff --git a/data/theme/gnome-shell-sass/widgets/_workspace-thumbnails.scss b/data/theme/gnome-shell-sass/widgets/_workspace-thumbnails.scss
new file mode 100644
index 0000000..0a1867c
--- /dev/null
+++ b/data/theme/gnome-shell-sass/widgets/_workspace-thumbnails.scss
@@ -0,0 +1,28 @@
+/* Workspace pager */
+
+// thumbnails in overview
+.workspace-thumbnails {
+ visible-width: 32px; //amount visible before hover
+ spacing: $base_padding;
+ padding: $base_padding;
+
+ .workspace-thumbnail {
+ @extend %overview_panel;
+ border-radius: 3px;
+ }
+
+ // drag and drop indicator
+ .placeholder {
+ background-image: url("resource:///org/gnome/shell/theme/workspace-placeholder.svg");
+ background-size: contain;
+ width: 18px;
+ }
+}
+
+// selected indicator
+.workspace-thumbnail-indicator {
+ border: 3px solid $selected_bg_color;
+ border-radius: 3px;
+ padding: 0px;
+ // background-color: transparentize($selected_bg_color, 0.9);
+}
diff --git a/data/theme/gnome-shell-start.svg b/data/theme/gnome-shell-start.svg
new file mode 100644
index 0000000..af139cf
--- /dev/null
+++ b/data/theme/gnome-shell-start.svg
@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
+ id="svg8"
+ version="1.1"
+ viewBox="0 0 600 400"
+ height="400"
+ width="600"
+ sodipodi:docname="gnome-shell-start.svg">
+ <defs
+ id="defs2">
+ <linearGradient
+ id="a">
+ <stop
+ offset="0"
+ stop-color="#eef4fc"
+ id="stop3"
+ style="stop-color:#b2cdf1;stop-opacity:1" />
+ <stop
+ offset="1"
+ stop-color="#fff"
+ id="stop5" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10562"
+ id="linearGradient10564"
+ x1="-1179.9999"
+ y1="1314.5198"
+ x2="-940.00006"
+ y2="1314.5198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.3993149,0,0,1.3993149,1306.25,-919.42736)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient10562">
+ <stop
+ style="stop-color:#3580e4;stop-opacity:1"
+ offset="0"
+ id="stop10558" />
+ <stop
+ style="stop-color:#8abfdd;stop-opacity:1"
+ offset="1"
+ id="stop10560" />
+ </linearGradient>
+ <clipPath
+ id="e-3"
+ clipPathUnits="userSpaceOnUse">
+ <path
+ d="m 65,177 c -18.502,0 -33.5,14.998 -33.5,33.5 0.008,7.371 4.131,15.807 11.388,23.526 l 12.617,13.171 H 75.02 l 12.137,-13.245 c 6.636,-7.059 11.33,-16.117 11.344,-23.452 0,-18.502 -14.998,-33.5 -33.5,-33.5 z"
+ style="marker:none"
+ fill="#ffffff"
+ stroke-width="2"
+ id="path34-6" />
+ </clipPath>
+ <radialGradient
+ r="33.5"
+ fy="48.405998"
+ fx="79.091003"
+ cy="48.405998"
+ cx="79.091003"
+ gradientTransform="matrix(0.00476,1.62228,-1.43419,0.00422,141.702,-82.047)"
+ gradientUnits="userSpaceOnUse"
+ id="f-7"
+ xlink:href="#b" />
+ <linearGradient
+ id="b">
+ <stop
+ offset="0"
+ stop-color="#fff"
+ id="stop11" />
+ <stop
+ offset=".519"
+ stop-color="#fff"
+ id="stop13" />
+ <stop
+ offset=".734"
+ stop-color="#cfcfcf"
+ id="stop15" />
+ <stop
+ offset="1"
+ stop-color="#dcdcdc"
+ id="stop17" />
+ </linearGradient>
+ <radialGradient
+ r="12"
+ fy="54.102001"
+ fx="71.473999"
+ cy="54.102001"
+ cx="71.473999"
+ gradientTransform="matrix(4.50607,0,0,4.76661,-259.306,-59.706)"
+ gradientUnits="userSpaceOnUse"
+ id="g-2"
+ xlink:href="#c" />
+ <linearGradient
+ id="c">
+ <stop
+ offset="0"
+ stop-color="#ed686f"
+ id="stop20" />
+ <stop
+ offset=".865"
+ stop-color="#a51d2d"
+ id="stop22" />
+ <stop
+ offset="1"
+ stop-color="#e01b24"
+ id="stop24" />
+ </linearGradient>
+ <radialGradient
+ r="14.583"
+ fy="49.777"
+ fx="40.152"
+ cy="49.777"
+ cx="40.152"
+ gradientTransform="matrix(0.87016,4.27869,-2.56693,0.52204,148.394,-163.306)"
+ gradientUnits="userSpaceOnUse"
+ id="h-7"
+ xlink:href="#d" />
+ <linearGradient
+ id="d">
+ <stop
+ offset="0"
+ stop-color="#ed686f"
+ id="stop27" />
+ <stop
+ offset=".911"
+ stop-color="#a51d2d"
+ id="stop29" />
+ <stop
+ offset="1"
+ stop-color="#e01b24"
+ id="stop31" />
+ </linearGradient>
+ <radialGradient
+ r="14.583"
+ fy="49.777"
+ fx="40.152"
+ cy="49.777"
+ cx="40.152"
+ gradientTransform="matrix(0.87016,4.27869,-2.56693,0.52204,148.394,-163.306)"
+ gradientUnits="userSpaceOnUse"
+ id="i-6"
+ xlink:href="#d" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient860"
+ id="linearGradient22"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.575111,0,0,1.575111,1217.8748,47.173672)"
+ x1="85"
+ y1="107.162"
+ x2="85"
+ y2="86" />
+ <linearGradient
+ id="linearGradient860">
+ <stop
+ offset="0"
+ stop-color="#eef4fc"
+ id="stop856"
+ style="stop-color:#c5dcf7;stop-opacity:1" />
+ <stop
+ offset="1"
+ stop-color="#fff"
+ id="stop858" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient860"
+ id="linearGradient24"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-2.1158457,0,0,2.1158457,1459.4994,-118.92278)"
+ x1="85"
+ y1="110.30392"
+ x2="85"
+ y2="86.986084" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient860"
+ id="linearGradient23"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.5463767,0,0,1.5463767,655.03375,-16.075361)"
+ x1="85"
+ y1="107.162"
+ x2="85"
+ y2="82.13472" />
+ </defs>
+ <sodipodi:namedview
+ units="px"
+ borderlayer="true"
+ inkscape:showpageshadow="false"
+ showgrid="false"
+ inkscape:document-rotation="0"
+ inkscape:current-layer="layer1"
+ inkscape:document-units="px"
+ inkscape:cy="75.603256"
+ inkscape:cx="199.00314"
+ inkscape:zoom="0.98994949"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0"
+ borderopacity="1"
+ bordercolor="#383838"
+ pagecolor="#1f1f1f"
+ id="base"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid900" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Reproduction" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Distribution" />
+ <cc:requires
+ rdf:resource="http://creativecommons.org/ns#Notice" />
+ <cc:requires
+ rdf:resource="http://creativecommons.org/ns#Attribution" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://creativecommons.org/ns#ShareAlike" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1"
+ transform="translate(-620,9.4371882)">
+ <g
+ id="g4043"
+ transform="translate(-15.793175)">
+ <circle
+ style="fill:url(#linearGradient10564);fill-opacity:1;stroke:none;stroke-width:3.66905;stroke-linecap:round;stroke-linejoin:round"
+ id="path10517"
+ cx="-177.02397"
+ cy="920"
+ r="167.91774"
+ transform="rotate(-90)" />
+ <path
+ d="m 1066.6646,147.98131 c 12.3048,0.0174 23.474,7.19166 28.6008,18.37784 a 23.631877,23.631877 0 0 1 10.7762,-2.62748 c 12.4288,0.0178 22.7209,9.65384 23.5474,22.05492 h 0.08 c 8.6991,0 15.7508,7.05171 15.7508,15.75083 0,8.69911 -7.0522,15.75083 -15.7513,15.75083 h -88.2056 a 18.901933,18.901933 0 0 1 -18.9019,-18.901 18.901933,18.901933 0 0 1 12.6566,-17.81853 33.12417,33.12417 0 0 1 -0.056,-1.08246 c 0,-17.39824 14.1038,-31.50213 31.5025,-31.50213 z"
+ id="path10"
+ style="fill:url(#linearGradient22);stroke-width:0.577558" />
+ <g
+ id="g245"
+ transform="matrix(1.5161629,0,0,1.5161629,396.40481,-143.42807)">
+ <g
+ id="g126"
+ transform="matrix(0.75000161,0,0,0.75000161,154.45946,111.99828)">
+ <g
+ id="g265">
+ <g
+ transform="matrix(3.12129,0,0,2.9344,91.063695,-499.94397)"
+ clip-path="url(#e-3)"
+ id="g47-0">
+ <path
+ transform="translate(-8,156)"
+ d="M 73,21 C 54.498,21 39.5,35.998 39.5,54.5 39.508,61.871 43,68.922 46.44,74.879 L 58,98 H 88 L 99.605,74.79 C 103,68.91 106.485,61.835 106.5,54.5 106.5,35.998 91.502,21 73,21 Z"
+ style="fill:url(#f-7);marker:none"
+ fill="url(#f)"
+ id="path39-6" />
+ <path
+ d="m 64,177 c 0,0 -18.332,12.132 -18.332,30.989 0,14.571 16.174,46.011 16.174,46.011 H 67 c 0,0 1.333,-31.44 1.333,-46.011 C 68.333,189.132 64,177 64,177 Z"
+ fill="url(#g)"
+ id="path41-2"
+ style="fill:url(#g-2)" />
+ <path
+ transform="translate(-8,156)"
+ d="m 71.983,21.106 c -8.207,0.147 -16.328,3.285 -22.5,8.695 -6.174,5.409 -10.35,13.047 -11.574,21.163 -1.24,8.225 0.567,16.874 4.995,23.915 L 61.902,98 h 6.432 L 55.002,81.27 C 50.702,75.875 46.53,70.2 44.406,63.636 42.283,57.072 42.24,49.836 44.409,43.288 A 32.824,32.824 0 0 1 54.925,28.13 32.824,32.824 0 0 1 71.983,21.105 Z"
+ fill="url(#h)"
+ id="path43-6"
+ style="fill:url(#h-7)" />
+ <path
+ transform="matrix(-1,0,0,1,137.16,155.823)"
+ d="M 73.003,21.303 C 65.294,19.756 57.033,21.149 50.257,25.138 43.481,29.127 38.255,35.675 35.867,43.167 A 32.484,32.484 0 0 0 35.11,59.972 32.484,32.484 0 0 0 42.904,74.879 L 54.11,98 H 67.493 L 54.16,74.356 C 51.292,69.269 48.603,63.95 47.733,58.175 46.863,52.4 47.769,46.404 50.107,41.052 54.28,31.502 62.943,24.03 73.002,21.304 Z"
+ fill="url(#i)"
+ id="path45-1"
+ style="fill:url(#i-6)" />
+ </g>
+ <path
+ style="marker:none"
+ d="m 271.0607,256.22128 v 19.397 a 9.702,9.702 0 0 0 9.724,9.726 h 26.326 c 5.388,0 8.594,-4.457 9.725,-9.726 v -19.397 z"
+ fill="#e5a50a"
+ id="path49-8" />
+ <path
+ style="marker:none"
+ d="m 271.0607,252.06028 v 19.397 a 9.702,9.702 0 0 0 9.724,9.724 h 26.326 c 5.388,0 8.594,-4.455 9.725,-9.724 v -19.397 z"
+ fill="#e5a50a"
+ id="path51-7" />
+ <path
+ style="marker:none"
+ d="m 300.1907,252.06028 v 29.121 h 6.92 c 5.388,0 8.594,-4.455 9.725,-9.724 v -19.397 z"
+ fill="#f3af0b"
+ id="path53-9" />
+ </g>
+ </g>
+ </g>
+ <path
+ style="font-variation-settings:normal;fill:url(#linearGradient24);fill-opacity:1;stroke-width:0.822282"
+ d="m 1051.0577,24.011554 a 37.111799,37.111799 0 0 0 -36.9465,35.120895 c -13.3251,2.089947 -23.45686,13.564137 -23.45686,27.494149 0,15.421302 12.41476,27.836692 27.83666,27.836692 h 72.3273 c 11.9284,0 21.5316,-9.60251 21.5316,-21.530902 0,-11.928386 -9.6032,-21.532163 -21.5316,-21.532163 h -3.9868 a 37.111799,37.111799 0 0 0 1.4509,-10.27624 37.111799,37.111799 0 0 0 -37.1118,-37.111798 37.111799,37.111799 0 0 0 -0.1123,0 z"
+ id="path12" />
+ <path
+ d="m 803.48555,82.893284 c -12.0805,0.0171 -23.046,7.06046 -28.0791,18.042576 a 23.200767,23.200767 0 0 0 -10.5796,-2.579546 c -12.2021,0.0175 -22.3064,9.477716 -23.1179,21.652566 h -0.079 c -8.5404,0 -15.4634,6.92307 -15.4634,15.46349 0,8.54042 6.9235,15.46349 15.4639,15.46349 h 86.5965 a 18.55711,18.55711 0 0 0 18.5571,-18.55619 18.55711,18.55711 0 0 0 -12.4257,-17.49347 32.519894,32.519894 0 0 0 0.055,-1.06272 c 0,-17.080836 -13.8466,-30.927426 -30.9279,-30.927426 z"
+ id="path21"
+ style="fill:url(#linearGradient23);fill-opacity:1;stroke-width:0.999999" />
+ </g>
+ </g>
+</svg>
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
new file mode 100644
index 0000000..6e4abda
--- /dev/null
+++ b/data/theme/gnome-shell.css
@@ -0,0 +1,2784 @@
+/* This stylesheet is generated, DO NOT EDIT */
+/* Copyright 2009, 2015 Red Hat, Inc.
+ *
+ * Portions adapted from Mx's data/style/default.css
+ * Copyright 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/* Global Values */
+stage {
+ font-size: 11pt;
+ color: white; }
+
+/* Common Stylings */
+.screenshot-ui-panel, .workspace-switcher, .switcher-list, .resize-popup, .osd-monitor-label, .osd-window {
+ color: white;
+ background-color: #232323;
+ border: 1px solid rgba(255, 255, 255, 0.02);
+ border-radius: 999px;
+ padding: 12px; }
+
+.workspace-thumbnails .workspace-thumbnail {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1); }
+
+.app-well-app.app-folder .overview-icon, .app-folder.grid-search-result .overview-icon, .app-well-app .overview-icon, .grid-search-result .overview-icon, .dash-item-container .show-apps .overview-icon, .list-search-result, .search-provider-icon, .switcher-list .item-box {
+ border-radius: 16px;
+ padding: 6px;
+ spacing: 6px;
+ border: 2px solid transparent;
+ transition-duration: 200ms;
+ text-align: center; }
+
+.modal-dialog {
+ color: white;
+ background-color: #303030;
+ border-radius: 11px;
+ border: 1px solid #3c3c3c; }
+
+#LookingGlassDialog > #Toolbar .lg-toolbar-button, .app-folder-dialog .folder-name-container .edit-folder-button, .button, .icon-button {
+ border-radius: 6px;
+ border-style: solid;
+ border-width: 1px;
+ font-weight: bold;
+ padding: 3px 24px;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3a3a3a; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:focus, .app-folder-dialog .folder-name-container .edit-folder-button:focus, .button:focus, .icon-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3a414b;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:focus:hover, .app-folder-dialog .folder-name-container .edit-folder-button:focus:hover, .button:focus:hover, .icon-button:focus:hover {
+ background-color: #404852;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:focus:active, .app-folder-dialog .folder-name-container .edit-folder-button:focus:active, .button:focus:active, .icon-button:focus:active {
+ background-color: #474f59;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:hover, .app-folder-dialog .folder-name-container .edit-folder-button:hover, .button:hover, .icon-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #424242; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:insensitive, .app-folder-dialog .folder-name-container .edit-folder-button:insensitive, .button:insensitive, .icon-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05); }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:active, .app-folder-dialog .folder-name-container .edit-folder-button:active, .button:active, .icon-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #494949; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:checked, .app-folder-dialog .folder-name-container .edit-folder-button:checked, .button:checked, .icon-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #515151; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:checked:hover, .app-folder-dialog .folder-name-container .edit-folder-button:checked:hover, .button:checked:hover, .icon-button:checked:hover {
+ background-color: #595959; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button:checked:active, .app-folder-dialog .folder-name-container .edit-folder-button:checked:active, .button:checked:active, .icon-button:checked:active {
+ background-color: #606060; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button, .app-folder-dialog .folder-name-container .flat.edit-folder-button, .flat.button, .flat.icon-button {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3a3a3a;
+ background-color: transparent; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:focus, .app-folder-dialog .folder-name-container .flat.edit-folder-button:focus, .flat.button:focus, .flat.icon-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3a414b;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:focus:hover, .app-folder-dialog .folder-name-container .flat.edit-folder-button:focus:hover, .flat.button:focus:hover, .flat.icon-button:focus:hover {
+ background-color: #404852;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:focus:active, .app-folder-dialog .folder-name-container .flat.edit-folder-button:focus:active, .flat.button:focus:active, .flat.icon-button:focus:active {
+ background-color: #474f59;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:hover, .app-folder-dialog .folder-name-container .flat.edit-folder-button:hover, .flat.button:hover, .flat.icon-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #424242; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:insensitive, .app-folder-dialog .folder-name-container .flat.edit-folder-button:insensitive, .flat.button:insensitive, .flat.icon-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05); }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:active, .app-folder-dialog .folder-name-container .flat.edit-folder-button:active, .flat.button:active, .flat.icon-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #494949; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:checked, .app-folder-dialog .folder-name-container .flat.edit-folder-button:checked, .flat.button:checked, .flat.icon-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #515151; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:checked:hover, .app-folder-dialog .folder-name-container .flat.edit-folder-button:checked:hover, .flat.button:checked:hover, .flat.icon-button:checked:hover {
+ background-color: #595959; }
+ #LookingGlassDialog > #Toolbar .flat.lg-toolbar-button:checked:active, .app-folder-dialog .folder-name-container .flat.edit-folder-button:checked:active, .flat.button:checked:active, .flat.icon-button:checked:active {
+ background-color: #606060; }
+
+.modal-dialog .modal-dialog-linked-button, .hotplug-notification-item, .notification-banner .notification-button {
+ padding: 12px;
+ font-weight: bold !important;
+ transition-duration: 100ms;
+ color: white;
+ background-color: #525252; }
+ .modal-dialog .modal-dialog-linked-button:ltr, .hotplug-notification-item:ltr, .notification-banner .notification-button:ltr {
+ margin-right: 1px; }
+ .modal-dialog .modal-dialog-linked-button:rtl, .hotplug-notification-item:rtl, .notification-banner .notification-button:rtl {
+ margin-left: 1px; }
+ .modal-dialog .modal-dialog-linked-button:insensitive, .hotplug-notification-item:insensitive, .notification-banner .notification-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05); }
+ .modal-dialog .modal-dialog-linked-button:focus, .hotplug-notification-item:focus, .notification-banner .notification-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4f5761;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .modal-dialog .modal-dialog-linked-button:focus:hover, .hotplug-notification-item:focus:hover, .notification-banner .notification-button:focus:hover {
+ background-color: #565e67;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .modal-dialog .modal-dialog-linked-button:focus:active, .hotplug-notification-item:focus:active, .notification-banner .notification-button:focus:active {
+ background-color: #5d656e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .modal-dialog .modal-dialog-linked-button:hover, .hotplug-notification-item:hover, .notification-banner .notification-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #5a5a5a; }
+ .modal-dialog .modal-dialog-linked-button:active, .hotplug-notification-item:active, .notification-banner .notification-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #616161; }
+ .modal-dialog .modal-dialog-linked-button:checked, .hotplug-notification-item:checked, .notification-banner .notification-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: dimgray; }
+ .modal-dialog .modal-dialog-linked-button:checked:hover, .hotplug-notification-item:checked:hover, .notification-banner .notification-button:checked:hover {
+ background-color: #717171; }
+ .modal-dialog .modal-dialog-linked-button:checked:active, .hotplug-notification-item:checked:active, .notification-banner .notification-button:checked:active {
+ background-color: #787878; }
+ .modal-dialog .modal-dialog-linked-button:first-child:ltr, .hotplug-notification-item:first-child:ltr, .notification-banner .notification-button:first-child:ltr {
+ border-radius: 0 0 0 10px; }
+ .modal-dialog .modal-dialog-linked-button:last-child:ltr, .hotplug-notification-item:last-child:ltr, .notification-banner .notification-button:last-child:ltr {
+ border-radius: 0 0 10px 0;
+ margin-right: 0 !important; }
+ .modal-dialog .modal-dialog-linked-button:first-child:rtl, .hotplug-notification-item:first-child:rtl, .notification-banner .notification-button:first-child:rtl {
+ border-radius: 0 0 10px 0; }
+ .modal-dialog .modal-dialog-linked-button:last-child:rtl, .hotplug-notification-item:last-child:rtl, .notification-banner .notification-button:last-child:rtl {
+ border-radius: 0 0 0 10px;
+ margin-left: 0 !important; }
+ .modal-dialog .modal-dialog-linked-button:first-child:last-child, .hotplug-notification-item:first-child:last-child, .notification-banner .notification-button:first-child:last-child {
+ border-radius: 0 0 10px 10px !important;
+ margin-left: 0 !important;
+ margin-right: 0 !important; }
+
+.screenshot-ui-show-pointer-button, .screenshot-ui-type-button {
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none; }
+ .screenshot-ui-show-pointer-button:insensitive, .screenshot-ui-type-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .screenshot-ui-show-pointer-button:insensitive, .screenshot-ui-type-button:insensitive {
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none; }
+ .screenshot-ui-show-pointer-button:insensitive:insensitive, .screenshot-ui-type-button:insensitive:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .screenshot-ui-show-pointer-button:focus, .screenshot-ui-type-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #2f3740;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .screenshot-ui-show-pointer-button:focus:hover, .screenshot-ui-type-button:focus:hover {
+ background-color: #363d47;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .screenshot-ui-show-pointer-button:focus:active, .screenshot-ui-type-button:focus:active {
+ background-color: #3c444e;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .screenshot-ui-show-pointer-button:hover, .screenshot-ui-type-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #363636; }
+ .screenshot-ui-show-pointer-button:active, .screenshot-ui-type-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3d3d3d; }
+ .screenshot-ui-show-pointer-button:outlined, .screenshot-ui-type-button:outlined, .screenshot-ui-show-pointer-button:checked, .screenshot-ui-type-button:checked {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #454545; }
+ .screenshot-ui-show-pointer-button:outlined:hover, .screenshot-ui-type-button:outlined:hover, .screenshot-ui-show-pointer-button:checked:hover, .screenshot-ui-type-button:checked:hover {
+ background-color: #4d4d4d; }
+ .screenshot-ui-show-pointer-button:outlined:active, .screenshot-ui-type-button:outlined:active, .screenshot-ui-show-pointer-button:checked:active, .screenshot-ui-type-button:checked:active {
+ background-color: #545454; }
+
+/* General Typography */
+.app-folder-dialog .folder-name-container .folder-name-label, .app-folder-dialog .folder-name-container .folder-name-entry, .search-statustext {
+ font-weight: 800;
+ font-size: 20pt; }
+
+.lg-debug-flags-header, .message-dialog-content .message-dialog-title, .message-list .message-list-placeholder, .datemenu-today-button .date-label {
+ font-weight: 800;
+ font-size: 15pt; }
+
+.quick-toggle-menu .header .title {
+ font-weight: 700;
+ font-size: 15pt; }
+
+.message-dialog-content .message-dialog-title.lightweight, .headline {
+ font-weight: 700;
+ font-size: 13pt; }
+
+.lg-extension-name, .osd-window, .dialog-list .dialog-list-title, .message-list-controls, .weather-button .weather-forecast-temp, .weather-button .weather-header, .world-clocks-button .world-clocks-time, .world-clocks-button .world-clocks-header, .events-button .events-title, .calendar .calendar-month-header .calendar-month-label, .datemenu-today-button .day-label, .popup-menu-ornament {
+ font-weight: 700;
+ font-size: 11pt; }
+
+.quick-toggle-menu .header .subtitle, .app-menu .popup-inactive-menu-item:first-child > StLabel {
+ font-weight: 700;
+ font-size: 9pt; }
+
+.login-dialog-not-listed-label, .lg-completions-text, .caps-lock-warning-label, #dash, .icon-label-button-container, .prompt-dialog-error-label,
+.prompt-dialog-info-label,
+.prompt-dialog-null-label, .run-dialog .run-dialog-description, .dialog-list .dialog-list-box .dialog-list-item .dialog-list-item-description, .weather-button .weather-forecast-time, .world-clocks-button .world-clocks-timezone, .events-button .event-time {
+ font-weight: 400;
+ font-size: 9pt; }
+
+.calendar .calendar-day-base.calendar-day-heading, .calendar .calendar-day-base {
+ font-weight: 400;
+ font-size: 8pt; }
+
+.unlock-dialog-clock-time, #panel, .weather-button .weather-forecast-time, .world-clocks-button .world-clocks-timezone, .world-clocks-button .world-clocks-time, .events-button .event-time, .calendar .calendar-day-base {
+ font-feature-settings: "tnum"; }
+
+/* WIDGETS */
+.shell-link {
+ color: #8fbbf0; }
+ .shell-link:hover {
+ color: #bcd6f6; }
+
+.lowres-icon {
+ icon-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
+
+.icon-dropshadow {
+ icon-shadow: 0 1px 5px rgba(0, 0, 0, 0.8); }
+
+/* Entries */
+StEntry {
+ border-radius: 8px;
+ padding: 8px;
+ color: white;
+ background-color: #3c3c3c;
+ color: rgba(255, 255, 255, 0.7);
+ border: 2px solid #3c3c3c;
+ selection-background-color: #3584e4;
+ selected-color: #fff; }
+ StEntry:hover {
+ background-color: #565656;
+ border-color: #565656;
+ color: rgba(255, 255, 255, 0.7); }
+ StEntry:focus {
+ background-color: #3c4045;
+ border-color: #3584e4;
+ color: white; }
+ StEntry:insensitive {
+ background-color: #383838;
+ border-color: #383838;
+ color: #979797; }
+ StEntry StIcon.capslock-warning {
+ icon-size: 16px;
+ warning-color: #cd9309;
+ padding: 0 4px; }
+ StEntry StIcon.peek-password {
+ icon-size: 1.09em;
+ padding: 0 4px; }
+ StEntry StLabel.hint-text {
+ margin-left: 2px;
+ color: rgba(255, 255, 255, 0.7); }
+
+/* Buttons */
+.button, .icon-button {
+ min-height: 22px; }
+
+.icon-button {
+ border-radius: 99px;
+ padding: 12px;
+ min-height: 16px; }
+ .icon-button StIcon {
+ icon-size: 1.09em;
+ -st-icon-style: symbolic; }
+
+/* Check Boxes */
+.check-box StBoxLayout {
+ spacing: .8em; }
+
+.check-box StBin {
+ width: 24px;
+ height: 24px;
+ background-image: url("resource:///org/gnome/shell/theme/checkbox-off.svg"); }
+
+.check-box:focus StBin {
+ background-image: url("resource:///org/gnome/shell/theme/checkbox-off-focused.svg"); }
+
+.check-box:checked StBin {
+ background-image: url("resource:///org/gnome/shell/theme/checkbox.svg"); }
+
+.check-box:focus:checked StBin {
+ background-image: url("resource:///org/gnome/shell/theme/checkbox-focused.svg"); }
+
+/* Switches */
+.toggle-switch {
+ color: white;
+ height: 26px;
+ width: 48px;
+ background-size: contain;
+ background-image: url("resource:///org/gnome/shell/theme/toggle-off.svg"); }
+ .toggle-switch:checked {
+ background-image: url("resource:///org/gnome/shell/theme/toggle-on.svg"); }
+
+/* Slider */
+.slider {
+ -barlevel-height: 4px;
+ -barlevel-background-color: rgba(255, 255, 255, 0.2);
+ -barlevel-border-width: 2px;
+ -barlevel-border-color: transparent;
+ -barlevel-active-background-color: #3584e4;
+ -barlevel-active-border-color: transparent;
+ -barlevel-overdrive-color: #c01c28;
+ -barlevel-overdrive-border-color: transparent;
+ -barlevel-overdrive-separator-width: 1px;
+ -slider-handle-radius: 8px;
+ -slider-handle-border-width: 0;
+ -slider-handle-border-color: transparent; }
+
+/* Scrollbars */
+StScrollView.vfade {
+ -st-vfade-offset: 68px; }
+
+StScrollView.hfade {
+ -st-hfade-offset: 68px; }
+
+StScrollBar {
+ padding: 0; }
+ StScrollView StScrollBar {
+ min-width: 8px;
+ min-height: 8px; }
+ StScrollBar StBin#trough {
+ border-radius: 0;
+ background-color: transparent; }
+ StScrollBar StButton#vhandle, StScrollBar StButton#hhandle {
+ border-radius: 8px;
+ background-color: #6e6e6e;
+ border: 3px solid transparent;
+ transition: 500ms all ease; }
+ StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover {
+ background-color: #979797; }
+ StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active {
+ background-color: #838383; }
+
+/* Popovers/Menus */
+.popup-menu-boxpointer, .candidate-popup-boxpointer {
+ -arrow-rise: 6px; }
+
+.popup-menu {
+ min-width: 15em;
+ color: white; }
+ .popup-menu.panel-menu {
+ margin-bottom: 1.75em; }
+
+.popup-menu-content, .candidate-popup-content {
+ padding: 6px;
+ border-radius: 20px;
+ border: 1px solid #3c3c3c;
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
+ background-color: #303030; }
+
+.popup-menu-item {
+ padding: 9px 12px;
+ border-radius: 12px;
+ spacing: 6px;
+ transition-duration: 100ms;
+ background-color: transparent; }
+ .popup-menu-item:ltr {
+ padding-left: 6px; }
+ .popup-menu-item:rtl {
+ padding-right: 6px; }
+ .popup-menu-item:focus, .popup-menu-item:hover {
+ background-color: #494949 !important; }
+ .popup-menu-item:focus:active, .popup-menu-item:hover:active {
+ background-color: #4e4e4e !important; }
+ .popup-menu-item:checked {
+ background-color: #424242 !important; }
+ .popup-menu-item:checked {
+ margin-bottom: 0;
+ box-shadow: inset 0 -1px 0 0 #353535;
+ border-radius: 8px 8px 0 0; }
+ .popup-menu-item:checked:focus, .popup-menu-item:checked:hover {
+ background-color: #494949 !important; }
+ .popup-menu-item:checked:active {
+ background-color: #4e4e4e !important; }
+ .popup-menu-item:active {
+ background-color: #5b5b5b;
+ color: white; }
+ .popup-menu-item:insensitive {
+ color: rgba(255, 255, 255, 0.5); }
+ .popup-menu-item .toggle-switch:ltr {
+ margin-left: 4px; }
+ .popup-menu-item .toggle-switch:rtl {
+ margin-right: 4px; }
+
+.popup-inactive-menu-item {
+ color: white; }
+ .popup-inactive-menu-item:insensitive {
+ color: #979797; }
+
+.popup-menu-arrow,
+.popup-menu-icon {
+ icon-size: 16px !important; }
+
+.popup-sub-menu {
+ background-color: #424242;
+ border-radius: 0 0 8px 8px; }
+ .popup-sub-menu .popup-menu-ornament {
+ min-width: 1.09em !important; }
+ .popup-sub-menu .popup-menu-item {
+ border-radius: 0;
+ margin: 0; }
+ .popup-sub-menu .popup-menu-item:last-child {
+ border-radius: 0 0 8px 8px; }
+ .popup-sub-menu .popup-menu-item:focus, .popup-sub-menu .popup-menu-item:hover {
+ background-color: #494949 !important; }
+ .popup-sub-menu .popup-menu-item:checked {
+ background-color: #424242 !important; }
+ .popup-sub-menu .popup-menu-item:checked:focus, .popup-sub-menu .popup-menu-item:checked:hover {
+ background-color: #565656 !important; }
+ .popup-sub-menu .popup-menu-item:active {
+ background-color: #4e4e4e !important; }
+ .popup-sub-menu .popup-menu-section .popup-menu-item:last-child:hover, .popup-sub-menu .popup-menu-section .popup-menu-item:last-child:focus {
+ border-radius: 0; }
+ .popup-sub-menu .popup-menu-section:last-child .popup-menu-item:last-child {
+ border-radius: 0 0 8px 8px; }
+
+.popup-menu-ornament {
+ width: 1.2em;
+ text-align: center !important; }
+ .popup-menu-ornament:ltr {
+ text-align: right; }
+ .popup-menu-ornament:rtl {
+ text-align: left; }
+
+.popup-separator-menu-item {
+ margin: 6px 0;
+ padding: 0 !important; }
+ .popup-separator-menu-item:ltr {
+ margin-right: 4px; }
+ .popup-separator-menu-item:rtl {
+ margin-left: 4px; }
+ .popup-separator-menu-item .popup-separator-menu-item-separator {
+ height: 1px;
+ background-color: rgba(255, 255, 255, 0.1); }
+ .popup-separator-menu-item .popup-menu-ornament {
+ width: 0 !important; }
+ .popup-sub-menu .popup-separator-menu-item {
+ background-color: transparent; }
+ .popup-sub-menu .popup-separator-menu-item:ltr {
+ margin-right: 2.5em; }
+ .popup-sub-menu .popup-separator-menu-item:rtl {
+ margin-left: 2.5em; }
+ .popup-sub-menu .popup-separator-menu-item .popup-separator-menu-item-separator {
+ background-color: rgba(255, 255, 255, 0.1); }
+
+.background-menu {
+ -boxpointer-gap: 0px;
+ -arrow-rise: 0px; }
+
+.app-menu {
+ max-width: 27.25em; }
+ .app-menu .popup-menu-ornament {
+ width: 0 !important; }
+ .app-menu .popup-inactive-menu-item:first-child > StLabel:ltr {
+ margin-right: 8px; }
+ .app-menu .popup-inactive-menu-item:first-child > StLabel:rtl {
+ margin-left: 8px; }
+
+/* Date/Time Menu */
+#calendarArea {
+ padding: 4px; }
+
+.datemenu-calendar-column {
+ spacing: 6px; }
+ .datemenu-calendar-column:ltr {
+ padding-left: 6px; }
+ .datemenu-calendar-column:rtl {
+ padding-right: 6px; }
+ .datemenu-calendar-column .datemenu-displays-box {
+ spacing: 6px; }
+
+/* today button (the date) */
+.datemenu-today-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none;
+ box-shadow: none !important;
+ padding: 9px; }
+ .datemenu-today-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .datemenu-today-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3e4650;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .datemenu-today-button:focus:hover {
+ background-color: #454d56;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .datemenu-today-button:focus:active {
+ background-color: #4c545d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .datemenu-today-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #474747; }
+ .datemenu-today-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4e4e4e; }
+
+/* Calendar */
+.calendar {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none;
+ box-shadow: none !important;
+ margin-top: 0; }
+ .calendar:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+ .calendar:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3e4650;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .calendar:focus:hover {
+ background-color: #454d56;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .calendar:focus:active {
+ background-color: #4c545d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .calendar:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #474747; }
+ .calendar:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4e4e4e; }
+ .calendar .calendar-month-header .calendar-change-month-back StIcon,
+ .calendar .calendar-month-header .calendar-change-month-forward StIcon {
+ icon-size: 1.09em; }
+ .calendar .calendar-month-header .calendar-month-label {
+ color: white;
+ padding: 8px 0; }
+ .calendar .calendar-month-header .pager-button {
+ background-color: transparent;
+ height: 32px;
+ width: 32px;
+ margin: 2px;
+ border-radius: 6px; }
+ .calendar .calendar-month-header .pager-button:hover, .calendar .calendar-month-header .pager-button:focus {
+ background-color: #494949; }
+ .calendar .calendar-month-header .pager-button:active {
+ background-color: #4e4e4e; }
+ .calendar .calendar-day-base {
+ text-align: center;
+ margin: 2px;
+ padding: 0 !important;
+ height: 3em !important;
+ width: 3em !important;
+ border-radius: 99px;
+ transition-duration: 100ms; }
+ .calendar .calendar-day-base:hover {
+ background-color: #494949; }
+ .calendar .calendar-day-base:focus {
+ background-color: #314154;
+ color: #fff;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .calendar .calendar-day-base:active, .calendar .calendar-day-base:selected {
+ color: white;
+ background-color: #4e4e4e; }
+ .calendar .calendar-day-base:active:focus, .calendar .calendar-day-base:selected:focus {
+ background-color: #49596c; }
+ .calendar .calendar-day-base.calendar-day-heading {
+ color: #979797;
+ padding-top: 6px;
+ height: 16px !important;
+ font-weight: bold; }
+ .calendar .calendar-nonwork-day {
+ color: #979797; }
+ .calendar .calendar-other-month-day {
+ color: rgba(255, 255, 255, 0.5); }
+ .calendar .calendar-other-month-day.calendar-nonwork-day {
+ color: rgba(151, 151, 151, 0.5); }
+ .calendar .calendar-today {
+ background-color: #3584e4;
+ font-weight: 800;
+ color: #fff !important; }
+ .calendar .calendar-today:hover, .calendar .calendar-today:focus {
+ background-color: #428ce6;
+ color: inherit; }
+ .calendar .calendar-today:active, .calendar .calendar-today:selected {
+ background-color: #3584e4;
+ color: inherit; }
+ .calendar .calendar-today:active:hover, .calendar .calendar-today:active:focus, .calendar .calendar-today:selected:hover, .calendar .calendar-today:selected:focus {
+ background-color: #428ce6;
+ color: inherit; }
+ .calendar .calendar-day-with-events {
+ background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg");
+ background-size: contain; }
+ .calendar .calendar-week-number {
+ font-size: 7pt;
+ font-weight: bold;
+ font-feature-settings: "tnum";
+ margin: 6px;
+ padding: 0 6px;
+ border-radius: 3px;
+ background-color: #2b2b2b;
+ color: #979797; }
+
+/* Events */
+.events-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3f3f3f;
+ padding: 12px; }
+ .events-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3e4650;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .events-button:focus:hover {
+ background-color: #454d56;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .events-button:focus:active {
+ background-color: #4c545d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .events-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #474747; }
+ .events-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4e4e4e; }
+ .events-button .events-box {
+ spacing: 6px; }
+ .events-button .events-list {
+ spacing: 12px; }
+ .events-button .events-title {
+ color: #979797;
+ margin-bottom: 4px; }
+ .events-button .event-time {
+ color: #979797; }
+
+/* World clocks */
+.world-clocks-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3f3f3f;
+ padding: 12px; }
+ .world-clocks-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3e4650;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .world-clocks-button:focus:hover {
+ background-color: #454d56;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .world-clocks-button:focus:active {
+ background-color: #4c545d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .world-clocks-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #474747; }
+ .world-clocks-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4e4e4e; }
+ .world-clocks-button .world-clocks-grid {
+ spacing-rows: 6px;
+ spacing-columns: 12px; }
+ .world-clocks-button .world-clocks-header {
+ color: #979797; }
+ .world-clocks-button .world-clocks-city {
+ color: white; }
+ .world-clocks-button .world-clocks-time {
+ color: white; }
+ .world-clocks-button .world-clocks-time:ltr {
+ text-align: right; }
+ .world-clocks-button .world-clocks-time:rtl {
+ text-align: left; }
+ .world-clocks-button .world-clocks-timezone {
+ color: #979797; }
+
+/* Weather */
+.weather-button {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3f3f3f;
+ padding: 12px; }
+ .weather-button:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3e4650;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .weather-button:focus:hover {
+ background-color: #454d56;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .weather-button:focus:active {
+ background-color: #4c545d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .weather-button:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #474747; }
+ .weather-button:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4e4e4e; }
+ .weather-button .weather-box {
+ spacing: 10px; }
+ .weather-button .weather-header-box {
+ spacing: 6px; }
+ .weather-button .weather-header {
+ color: #979797; }
+ .weather-button .weather-header.location {
+ font-weight: normal; }
+ .weather-button .weather-grid {
+ spacing-rows: 6px;
+ spacing-columns: 12px; }
+ .weather-button .weather-forecast-time {
+ color: #b3b3b3;
+ padding-top: 0.2em;
+ padding-bottom: 0.4em; }
+ .weather-button .weather-forecast-icon {
+ icon-size: 2.18em; }
+
+/* Message List */
+.message-list {
+ width: 29em;
+ border: solid rgba(255, 255, 255, 0.1); }
+ .message-list:ltr {
+ margin-left: 0;
+ margin-right: 4px;
+ padding-right: 6px;
+ border-right-width: 1px; }
+ .message-list:rtl {
+ margin-right: 0;
+ margin-left: 4px;
+ padding-left: 6px;
+ border-left-width: 1px; }
+ .message-list .message-list-placeholder {
+ color: rgba(151, 151, 151, 0.5); }
+ .message-list .message-list-placeholder > StIcon {
+ icon-size: 3.27em;
+ margin-bottom: 12px;
+ -st-icon-style: symbolic; }
+
+.message-list-sections {
+ spacing: 6px;
+ margin: 0;
+ padding-bottom: 6px; }
+ .message-list-sections:ltr {
+ margin-right: 12px; }
+ .message-list-sections:rtl {
+ margin-left: 12px; }
+
+.message-list-section,
+.message-list-section-list {
+ spacing: 6px; }
+
+.message-list-controls {
+ padding: 6px;
+ spacing: 6px; }
+ .message-list-controls .dnd-button {
+ border-width: 2px;
+ border-color: transparent;
+ border-radius: 32px;
+ border-style: solid; }
+ .message-list-controls .dnd-button:focus {
+ border-color: rgba(53, 132, 228, 0.6); }
+
+.message {
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3f3f3f; }
+ .message:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3e4650;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .message:focus:hover {
+ background-color: #454d56;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .message:focus:active {
+ background-color: #4c545d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .message:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #474747; }
+ .message:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4e4e4e; }
+ .message .message-icon-bin {
+ padding: 18px; }
+ .message .message-icon-bin:ltr {
+ padding-right: 6px; }
+ .message .message-icon-bin:rtl {
+ padding-left: 6px; }
+ .message .message-icon-bin > StIcon {
+ icon-size: 2.18em;
+ -st-icon-style: symbolic; }
+ .message .message-icon-bin > .fallback-app-icon {
+ width: 1.09em;
+ height: 1.09em; }
+ .message .message-content {
+ spacing: 4px;
+ padding: 9px;
+ margin-bottom: 8px; }
+ .message .message-title {
+ font-weight: bold;
+ /* HACK: the label should be baseline-aligned with a 1em label, fake this with some bottom padding */
+ padding-top: 0.57em; }
+ .message .message-secondary-bin {
+ padding: 0 8px; }
+ .message .message-secondary-bin > .event-time {
+ color: rgba(255, 255, 255, 0.5);
+ font-size: 9pt;
+ /* HACK: the label should be baseline-aligned with a 1em label, fake this with some bottom padding */
+ padding-bottom: 0.13em; }
+ .message .message-secondary-bin > .event-time:ltr {
+ text-align: right; }
+ .message .message-secondary-bin > .event-time:rtl {
+ text-align: left; }
+ .message .message-close-button {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1);
+ border-radius: 99px;
+ padding: 5px;
+ margin: 1px; }
+ .message .message-close-button:hover {
+ background-color: rgba(255, 255, 255, 0.2); }
+ .message .message-close-button:active {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .message .message-close-button StIcon {
+ icon-size: 1.09em; }
+ .message .message-body {
+ color: #e6e6e6; }
+
+.url-highlighter {
+ link-color: #8fbbf0; }
+
+/* Media Controls */
+.message-media-control {
+ padding: 0 18px;
+ margin: 12px 0;
+ border-radius: 8px;
+ color: white; }
+ .message-media-control:hover {
+ background-color: #565656;
+ color: white; }
+ .message-media-control:active {
+ background-color: #5b5b5b;
+ color: white; }
+ .message-media-control:insensitive {
+ color: #a4a4a4; }
+ .message-media-control:last-child:ltr {
+ margin-right: 12px; }
+ .message-media-control:last-child:rtl {
+ margin-left: 12px; }
+ .message-media-control StIcon {
+ icon-size: 1.09em; }
+
+.media-message-cover-icon {
+ icon-size: 3.27em !important;
+ border-radius: 8px; }
+ .media-message-cover-icon.fallback {
+ color: #d4d4d4;
+ background-color: #303030;
+ border: 1px solid transparent;
+ border-radius: 8px;
+ icon-size: 2.18em !important;
+ padding: 14px; }
+
+.candidate-popup-content {
+ padding: 6px;
+ spacing: 6px; }
+
+.candidate-index {
+ padding: 0;
+ padding-right: 6px;
+ color: #979797; }
+
+.candidate-box {
+ padding: 6px 12px 6px 12px;
+ border-radius: 8px; }
+ .candidate-box:selected {
+ background-color: #3584e4;
+ color: #fff; }
+ .candidate-box:hover {
+ background-color: #494949;
+ color: white; }
+
+.candidate-page-button-box {
+ height: 2em; }
+ .vertical .candidate-page-button-box {
+ padding-top: 12px; }
+ .horizontal .candidate-page-button-box {
+ padding-left: 12px; }
+
+.candidate-page-button {
+ padding: 6px; }
+ .candidate-page-button StIcon {
+ icon-size: 1.09em; }
+
+.candidate-page-button-previous {
+ border-radius: 8px 0px 0px 8px;
+ border-right-width: 0;
+ box-shadow: none; }
+
+.candidate-page-button-next {
+ border-radius: 0px 8px 8px 0px;
+ box-shadow: none; }
+
+/* Notifications & Message Tray */
+.notification-banner {
+ min-height: 64px;
+ width: 34em;
+ box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.2);
+ border-radius: 12px;
+ margin: 4px; }
+ .notification-banner .notification-actions {
+ spacing: 0; }
+
+.summary-source-counter {
+ font-size: 10pt;
+ font-weight: bold;
+ height: 1.6em;
+ width: 1.6em;
+ -shell-counter-overlap-x: 3px;
+ -shell-counter-overlap-y: 3px;
+ background-color: #3584e4;
+ color: #fff;
+ border: 2px solid white;
+ box-shadow: 0 2px 2px rgba(0, 0, 0, 0.5);
+ border-radius: 0.9em; }
+
+.chat-body {
+ spacing: 5px; }
+
+.chat-response {
+ margin: 5px; }
+
+.chat-log-message {
+ color: #e6e6e6; }
+
+.chat-new-group {
+ padding-top: 1em; }
+
+.chat-received {
+ padding-left: 4px; }
+ .chat-received:rtl {
+ padding-left: 0px;
+ padding-right: 4px; }
+
+.chat-sent {
+ padding-left: 18pt;
+ color: white; }
+ .chat-sent:rtl {
+ padding-left: 0;
+ padding-right: 18pt; }
+
+.chat-meta-message {
+ padding-left: 4px;
+ font-size: 9pt;
+ font-weight: bold;
+ color: white; }
+ .chat-meta-message:rtl {
+ padding-left: 0;
+ padding-right: 4px; }
+
+.hotplug-notification-item-icon {
+ icon-size: 24px;
+ padding: 0 4px; }
+
+/* Modal Dialogs */
+.modal-dialog .modal-dialog-content-box {
+ margin: 32px 40px;
+ spacing: 32px;
+ max-width: 28em; }
+
+/* End Session Dialog */
+.end-session-dialog {
+ width: 30em; }
+ .end-session-dialog .end-session-dialog-battery-warning,
+ .end-session-dialog .dialog-list-title {
+ color: #cd9309; }
+
+/* Message Dialog */
+.message-dialog-content {
+ spacing: 18px; }
+ .message-dialog-content .message-dialog-title {
+ text-align: center; }
+ .message-dialog-content .message-dialog-description {
+ text-align: center; }
+
+/* Dialog List */
+.dialog-list {
+ spacing: 18px; }
+ .dialog-list .dialog-list-title {
+ text-align: center; }
+ .dialog-list .dialog-list-scrollview {
+ max-height: 200px; }
+ .dialog-list .dialog-list-box {
+ spacing: 1em; }
+ .dialog-list .dialog-list-box .dialog-list-item {
+ spacing: 1em; }
+ .dialog-list .dialog-list-box .dialog-list-item .dialog-list-item-title {
+ font-weight: bold; }
+ .dialog-list .dialog-list-box .dialog-list-item .dialog-list-item-description {
+ color: #f2f2f2; }
+
+/* Run Dialog */
+.run-dialog .modal-dialog-content-box {
+ margin-top: 24px;
+ margin-bottom: 14px; }
+
+.run-dialog .run-dialog-entry {
+ width: 20em; }
+
+.run-dialog .run-dialog-description {
+ text-align: center;
+ color: #cccccc; }
+
+/* Password or Authentication Dialog */
+.prompt-dialog {
+ width: 28em; }
+ .prompt-dialog .modal-dialog-content-box {
+ margin-bottom: 24px; }
+
+.prompt-dialog-password-grid {
+ spacing-rows: 8px;
+ spacing-columns: 4px; }
+ .prompt-dialog-password-grid .prompt-dialog-password-entry {
+ width: auto; }
+ .prompt-dialog-password-grid .prompt-dialog-password-entry:ltr {
+ margin-left: 20px; }
+ .prompt-dialog-password-grid .prompt-dialog-password-entry:rtl {
+ margin-right: 20px; }
+
+.prompt-dialog-password-layout {
+ spacing: 8px; }
+
+.prompt-dialog-password-entry {
+ width: 20em; }
+
+.prompt-dialog-error-label,
+.prompt-dialog-info-label,
+.prompt-dialog-null-label {
+ text-align: center; }
+
+.prompt-dialog-error-label {
+ color: #cd9309; }
+
+/* Polkit Dialog */
+.polkit-dialog-user-layout {
+ text-align: center;
+ spacing: 8px;
+ margin-bottom: 6px; }
+ .polkit-dialog-user-layout .polkit-dialog-user-root-label {
+ color: #cd9309; }
+
+/* Audio selection dialog */
+.audio-device-selection-dialog .modal-dialog-content-box {
+ margin-bottom: 28px; }
+
+.audio-device-selection-dialog .audio-selection-box {
+ spacing: 20px; }
+
+.audio-selection-device {
+ border-radius: 16px; }
+ .audio-selection-device:hover, .audio-selection-device:focus {
+ background-color: #494949; }
+ .audio-selection-device:active {
+ background-color: #3584e4;
+ color: #fff; }
+
+.audio-selection-device-box {
+ padding: 20px;
+ spacing: 20px; }
+
+.audio-selection-device-icon {
+ icon-size: 4.36em; }
+
+/* Welcome dialog */
+.welcome-dialog-image {
+ background-image: url("resource:///org/gnome/shell/theme/gnome-shell-start.svg");
+ background-size: contain;
+ /* Reasonable maximum dimensions */
+ height: 300px;
+ width: 300px; }
+
+/* Access portal dialog */
+.access-dialog {
+ text-align: center; }
+
+/* OSD */
+.osd-window {
+ text-align: center;
+ font-weight: bold;
+ spacing: 12px;
+ padding: 12px 18px;
+ margin-bottom: 4em; }
+ .osd-window > * {
+ spacing: 8px; }
+ .osd-window StIcon {
+ icon-size: 2.18em; }
+ .osd-window StLabel:ltr {
+ margin-right: 6px; }
+ .osd-window StLabel:rtl {
+ margin-left: 6px; }
+ .osd-window .level {
+ margin-bottom: 4px;
+ height: 6px;
+ min-width: 10.9em;
+ -barlevel-height: 6px;
+ -barlevel-background-color: rgba(255, 255, 255, 0.1);
+ -barlevel-active-background-color: white;
+ -barlevel-overdrive-color: #c01c28;
+ -barlevel-overdrive-separator-width: 3px; }
+ .osd-window .level:first-child {
+ margin-bottom: 0px; }
+ .osd-window .level:ltr {
+ margin-right: 6px; }
+ .osd-window .level:rtl {
+ margin-left: 6px; }
+
+.osd-monitor-label {
+ border-radius: 16px;
+ font-size: 3em;
+ font-weight: bold;
+ margin: 12px;
+ text-align: center;
+ min-width: 1.3em; }
+
+/* Pad OSD */
+.pad-osd-window {
+ padding: 32px;
+ background-color: rgba(0, 0, 0, 0.8); }
+ .pad-osd-window .pad-osd-title-box {
+ spacing: 12px; }
+ .pad-osd-window .pad-osd-title-menu-box {
+ spacing: 6px; }
+
+.combo-box-label {
+ width: 15em; }
+
+/* App Switcher */
+.switcher-popup {
+ padding: 0;
+ spacing: 24px; }
+
+.switcher-list {
+ padding: 10px;
+ border-radius: 24px;
+ box-shadow: 0 8px 8px 0 rgba(0, 0, 0, 0.1); }
+ .switcher-list .switcher-list-item-container {
+ spacing: 12px; }
+ .switcher-list .item-box {
+ background-color: transparent; }
+ .switcher-list .item-box:hover {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .switcher-list .item-box:selected, .switcher-list .item-box:focus {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .switcher-list .item-box:selected:hover, .switcher-list .item-box:focus:hover {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .switcher-list .item-box:selected:active, .switcher-list .item-box:focus:active {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .switcher-list .item-box:active {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .switcher-list .item-box:outlined, .switcher-list .item-box:checked {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .switcher-list .item-box:outlined:active, .switcher-list .item-box:checked:active {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .switcher-list .item-box:outlined:hover, .switcher-list .item-box:checked:hover {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .switcher-list .item-box:drop {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+ .switcher-list .item-box:hover {
+ background: none; }
+ .switcher-list .separator {
+ width: 1px;
+ background: rgba(255, 255, 255, 0.1); }
+ .switcher-list .thumbnail-box {
+ padding: 2px;
+ spacing: 6px; }
+ .switcher-list .thumbnail {
+ width: 256px;
+ border-radius: 8px; }
+
+.switcher-arrow {
+ border-color: rgba(255, 255, 255, 0.8);
+ color: rgba(255, 255, 255, 0.8); }
+ .switcher-arrow:highlighted {
+ border-color: white;
+ color: white; }
+
+.input-source-switcher-symbol {
+ font-size: 34pt;
+ width: 96px;
+ height: 96px; }
+
+.cycler-highlight {
+ border: 5px solid #3584e4; }
+
+/* Workspace Switcher */
+.workspace-switcher {
+ margin-bottom: 4em;
+ spacing: 12px;
+ padding: 12px 18px; }
+
+.ws-switcher-indicator {
+ background-color: rgba(255, 255, 255, 0.5);
+ padding: 0.1816666667em;
+ margin: 0.9083333333em;
+ border-radius: 2.18em; }
+ .ws-switcher-indicator:active {
+ background-color: white;
+ padding: 0.3633333333em;
+ margin: 0.7266666667em; }
+
+.icon-label-button-container {
+ spacing: 6px; }
+ .icon-label-button-container StIcon {
+ icon-size: 32px; }
+
+.screenshot-ui-panel {
+ border-radius: 37px;
+ padding: 18px;
+ padding-bottom: 12px;
+ margin-bottom: 4em;
+ spacing: 12px; }
+
+.screenshot-ui-close-button {
+ padding: 6px !important;
+ margin-top: 12px;
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2); }
+ .screenshot-ui-close-button.left {
+ margin-left: 12px; }
+ .screenshot-ui-close-button.right {
+ margin-right: 12px; }
+
+.screenshot-ui-type-button {
+ min-width: 48px;
+ padding: 12px 18px !important;
+ border-radius: 19px; }
+
+.screenshot-ui-capture-button {
+ width: 36px;
+ height: 36px;
+ border-radius: 99px;
+ border: 4px white;
+ padding: 4px; }
+ .screenshot-ui-capture-button .screenshot-ui-capture-button-circle {
+ background-color: white;
+ transition-duration: 200ms;
+ border-radius: 99px; }
+ .screenshot-ui-capture-button .screenshot-ui-capture-button-circle:hover, .screenshot-ui-capture-button .screenshot-ui-capture-button-circle:focus {
+ background-color: #494949; }
+ .screenshot-ui-capture-button:hover .screenshot-ui-capture-button-circle, .screenshot-ui-capture-button:focus .screenshot-ui-capture-button-circle {
+ background-color: #d9d9d9; }
+ .screenshot-ui-capture-button:active .screenshot-ui-capture-button-circle {
+ background-color: gray; }
+ .screenshot-ui-capture-button:cast .screenshot-ui-capture-button-circle {
+ background-color: #c01c28; }
+ .screenshot-ui-capture-button:cast:hover .screenshot-ui-capture-button-circle, .screenshot-ui-capture-button:cast:focus .screenshot-ui-capture-button-circle {
+ background-color: #d61f2d; }
+ .screenshot-ui-capture-button:cast:active .screenshot-ui-capture-button-circle {
+ background-color: #a11722; }
+
+.screenshot-ui-shot-cast-container {
+ background-color: #303030;
+ border-radius: 16px;
+ padding: 3px;
+ spacing: 3px; }
+ .screenshot-ui-shot-cast-container:ltr {
+ margin-left: 3px; }
+ .screenshot-ui-shot-cast-container:rtl {
+ margin-right: 3px; }
+
+.screenshot-ui-shot-cast-button {
+ padding: 6px 12px;
+ background-color: transparent;
+ border-radius: 13px; }
+ .screenshot-ui-shot-cast-button:hover, .screenshot-ui-shot-cast-button:focus {
+ background-color: #3c3c3c; }
+ .screenshot-ui-shot-cast-button:active {
+ background-color: #5b5b5b; }
+ .screenshot-ui-shot-cast-button:checked {
+ background-color: white;
+ color: black; }
+ .screenshot-ui-shot-cast-button:insensitive {
+ color: rgba(255, 255, 255, 0.5); }
+ .screenshot-ui-shot-cast-button StIcon {
+ icon-size: 1.09em; }
+
+.screenshot-ui-show-pointer-button {
+ border-radius: 99px;
+ padding: 12px !important; }
+ .screenshot-ui-show-pointer-button StIcon {
+ icon-size: 1.09em; }
+
+.screenshot-ui-area-indicator-shade {
+ background-color: rgba(0, 0, 0, 0.3); }
+
+.screenshot-ui-area-selector .screenshot-ui-area-indicator-shade {
+ background-color: rgba(0, 0, 0, 0.5); }
+
+.screenshot-ui-area-selector .screenshot-ui-area-indicator-selection {
+ border: 2px white; }
+
+.screenshot-ui-area-selector-handle {
+ border-radius: 99px;
+ background-color: white;
+ box-shadow: 0 1px 3px 2px rgba(0, 0, 0, 0.2);
+ width: 24px;
+ height: 24px; }
+
+.screenshot-ui-window-selector {
+ background-color: #232323; }
+ .screenshot-ui-window-selector .screenshot-ui-window-selector-window-container {
+ margin: 100px; }
+ .screenshot-ui-window-selector:primary-monitor .screenshot-ui-window-selector-window-container {
+ margin-bottom: 200px; }
+
+.screenshot-ui-window-selector-window-border {
+ transition-duration: 200ms;
+ border-radius: 16px;
+ border: 6px transparent; }
+
+.screenshot-ui-window-selector-check {
+ transition-duration: 200ms;
+ color: transparent;
+ border-radius: 99px;
+ border-width: 12px;
+ icon-size: 24px; }
+
+.screenshot-ui-window-selector-window:hover .screenshot-ui-window-selector-window-border {
+ border-color: #185fb4; }
+
+.screenshot-ui-window-selector-window:checked .screenshot-ui-window-selector-window-border {
+ border-color: #3584e4;
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.screenshot-ui-window-selector-window:checked .screenshot-ui-window-selector-check {
+ color: #fff;
+ background-color: #3584e4; }
+
+.screenshot-ui-screen-selector {
+ transition-duration: 200ms;
+ background-color: rgba(0, 0, 0, 0.5); }
+ .screenshot-ui-screen-selector:hover {
+ background-color: rgba(0, 0, 0, 0.3); }
+ .screenshot-ui-screen-selector:active {
+ background-color: rgba(0, 0, 0, 0.7); }
+ .screenshot-ui-screen-selector:checked {
+ background-color: transparent;
+ border: 2px white; }
+
+.screenshot-ui-tooltip {
+ color: white;
+ background-color: #232323;
+ border-radius: 99px;
+ padding: 6px 12px;
+ text-align: center;
+ -y-offset: 24px; }
+
+/* Top Bar */
+#panel {
+ background-color: #000;
+ font-weight: bold;
+ height: 2.2em;
+ transition-duration: 250ms; }
+ #panel.unlock-screen, #panel.login-screen, #panel:overview {
+ background-color: transparent; }
+ #panel .panel-button {
+ font-weight: bold;
+ color: #f2f2f2;
+ -natural-hpadding: 12px;
+ -minimum-hpadding: 6px;
+ transition-duration: 150ms;
+ border: 3px solid transparent;
+ border-radius: 99px; }
+ #panel .panel-button.clock-display .clock {
+ transition-duration: 150ms;
+ border: 3px solid transparent;
+ border-radius: 99px; }
+ #panel .panel-button.screen-recording-indicator {
+ box-shadow: inset 0 0 0 100px #c01c28; }
+ #panel .panel-button.screen-sharing-indicator {
+ box-shadow: inset 0 0 0 100px #cd9309; }
+ #panel .panel-button.screen-sharing-indicator StBoxLayout {
+ margin: 0 6px; }
+ #panel .panel-button.screen-recording-indicator StBoxLayout, #panel .panel-button.screen-sharing-indicator StBoxLayout {
+ spacing: 6px; }
+ #panel .panel-button.screen-recording-indicator StIcon, #panel .panel-button.screen-sharing-indicator StIcon {
+ icon-size: 1.09em; }
+ #panel .panel-button:active, #panel .panel-button:overview, #panel .panel-button:focus, #panel .panel-button:checked {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.2); }
+ #panel .panel-button.clock-display:active, #panel .panel-button.clock-display:overview, #panel .panel-button.clock-display:focus, #panel .panel-button.clock-display:checked {
+ box-shadow: none; }
+ #panel .panel-button.clock-display:active .clock, #panel .panel-button.clock-display:overview .clock, #panel .panel-button.clock-display:focus .clock, #panel .panel-button.clock-display:checked .clock {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.2); }
+ #panel .panel-button.screen-recording-indicator:active, #panel .panel-button.screen-recording-indicator:overview, #panel .panel-button.screen-recording-indicator:focus, #panel .panel-button.screen-recording-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.85); }
+ #panel .panel-button.screen-sharing-indicator:active, #panel .panel-button.screen-sharing-indicator:overview, #panel .panel-button.screen-sharing-indicator:focus, #panel .panel-button.screen-sharing-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.85); }
+ #panel .panel-button:hover {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.15); }
+ #panel .panel-button.clock-display:hover {
+ box-shadow: none; }
+ #panel .panel-button.clock-display:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.15); }
+ #panel .panel-button.screen-recording-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.9); }
+ #panel .panel-button.screen-sharing-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.9); }
+ #panel .panel-button:active:hover, #panel .panel-button:overview:hover, #panel .panel-button:focus:hover, #panel .panel-button:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.25); }
+ #panel .panel-button.clock-display:active:hover, #panel .panel-button.clock-display:overview:hover, #panel .panel-button.clock-display:focus:hover, #panel .panel-button.clock-display:checked:hover {
+ box-shadow: none; }
+ #panel .panel-button.clock-display:active:hover .clock, #panel .panel-button.clock-display:overview:hover .clock, #panel .panel-button.clock-display:focus:hover .clock, #panel .panel-button.clock-display:checked:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(242, 242, 242, 0.25); }
+ #panel .panel-button.screen-recording-indicator:active:hover, #panel .panel-button.screen-recording-indicator:overview:hover, #panel .panel-button.screen-recording-indicator:focus:hover, #panel .panel-button.screen-recording-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.8); }
+ #panel .panel-button.screen-sharing-indicator:active:hover, #panel .panel-button.screen-sharing-indicator:overview:hover, #panel .panel-button.screen-sharing-indicator:focus:hover, #panel .panel-button.screen-sharing-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.8); }
+ #panel .panel-button .system-status-icon {
+ icon-size: 1.09em;
+ padding: 5px;
+ margin: 0 4px; }
+ #panel .panel-button .panel-status-indicators-box .system-status-icon,
+ #panel .panel-button .panel-status-menu-box .system-status-icon {
+ margin: 0; }
+ #panel .panel-button .app-menu-icon {
+ -st-icon-style: symbolic; }
+ #panel #panelActivities.panel-button {
+ -natural-hpadding: 18px; }
+ #panel.unlock-screen .panel-button:active, #panel.unlock-screen .panel-button:overview, #panel.unlock-screen .panel-button:focus, #panel.unlock-screen .panel-button:checked, #panel.login-screen .panel-button:active, #panel.login-screen .panel-button:overview, #panel.login-screen .panel-button:focus, #panel.login-screen .panel-button:checked, #panel:overview .panel-button:active, #panel:overview .panel-button:overview, #panel:overview .panel-button:focus, #panel:overview .panel-button:checked {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.15); }
+ #panel.unlock-screen .panel-button.clock-display:active, #panel.unlock-screen .panel-button.clock-display:overview, #panel.unlock-screen .panel-button.clock-display:focus, #panel.unlock-screen .panel-button.clock-display:checked, #panel.login-screen .panel-button.clock-display:active, #panel.login-screen .panel-button.clock-display:overview, #panel.login-screen .panel-button.clock-display:focus, #panel.login-screen .panel-button.clock-display:checked, #panel:overview .panel-button.clock-display:active, #panel:overview .panel-button.clock-display:overview, #panel:overview .panel-button.clock-display:focus, #panel:overview .panel-button.clock-display:checked {
+ box-shadow: none; }
+ #panel.unlock-screen .panel-button.clock-display:active .clock, #panel.unlock-screen .panel-button.clock-display:overview .clock, #panel.unlock-screen .panel-button.clock-display:focus .clock, #panel.unlock-screen .panel-button.clock-display:checked .clock, #panel.login-screen .panel-button.clock-display:active .clock, #panel.login-screen .panel-button.clock-display:overview .clock, #panel.login-screen .panel-button.clock-display:focus .clock, #panel.login-screen .panel-button.clock-display:checked .clock, #panel:overview .panel-button.clock-display:active .clock, #panel:overview .panel-button.clock-display:overview .clock, #panel:overview .panel-button.clock-display:focus .clock, #panel:overview .panel-button.clock-display:checked .clock {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.15); }
+ #panel.unlock-screen .panel-button.screen-recording-indicator:active, #panel.unlock-screen .panel-button.screen-recording-indicator:overview, #panel.unlock-screen .panel-button.screen-recording-indicator:focus, #panel.unlock-screen .panel-button.screen-recording-indicator:checked, #panel.login-screen .panel-button.screen-recording-indicator:active, #panel.login-screen .panel-button.screen-recording-indicator:overview, #panel.login-screen .panel-button.screen-recording-indicator:focus, #panel.login-screen .panel-button.screen-recording-indicator:checked, #panel:overview .panel-button.screen-recording-indicator:active, #panel:overview .panel-button.screen-recording-indicator:overview, #panel:overview .panel-button.screen-recording-indicator:focus, #panel:overview .panel-button.screen-recording-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.85); }
+ #panel.unlock-screen .panel-button.screen-sharing-indicator:active, #panel.unlock-screen .panel-button.screen-sharing-indicator:overview, #panel.unlock-screen .panel-button.screen-sharing-indicator:focus, #panel.unlock-screen .panel-button.screen-sharing-indicator:checked, #panel.login-screen .panel-button.screen-sharing-indicator:active, #panel.login-screen .panel-button.screen-sharing-indicator:overview, #panel.login-screen .panel-button.screen-sharing-indicator:focus, #panel.login-screen .panel-button.screen-sharing-indicator:checked, #panel:overview .panel-button.screen-sharing-indicator:active, #panel:overview .panel-button.screen-sharing-indicator:overview, #panel:overview .panel-button.screen-sharing-indicator:focus, #panel:overview .panel-button.screen-sharing-indicator:checked {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.85); }
+ #panel.unlock-screen .panel-button:hover, #panel.login-screen .panel-button:hover, #panel:overview .panel-button:hover {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.1); }
+ #panel.unlock-screen .panel-button.clock-display:hover, #panel.login-screen .panel-button.clock-display:hover, #panel:overview .panel-button.clock-display:hover {
+ box-shadow: none; }
+ #panel.unlock-screen .panel-button.clock-display:hover .clock, #panel.login-screen .panel-button.clock-display:hover .clock, #panel:overview .panel-button.clock-display:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.1); }
+ #panel.unlock-screen .panel-button.screen-recording-indicator:hover, #panel.login-screen .panel-button.screen-recording-indicator:hover, #panel:overview .panel-button.screen-recording-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.9); }
+ #panel.unlock-screen .panel-button.screen-sharing-indicator:hover, #panel.login-screen .panel-button.screen-sharing-indicator:hover, #panel:overview .panel-button.screen-sharing-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.9); }
+ #panel.unlock-screen .panel-button:active:hover, #panel.unlock-screen .panel-button:overview:hover, #panel.unlock-screen .panel-button:focus:hover, #panel.unlock-screen .panel-button:checked:hover, #panel.login-screen .panel-button:active:hover, #panel.login-screen .panel-button:overview:hover, #panel.login-screen .panel-button:focus:hover, #panel.login-screen .panel-button:checked:hover, #panel:overview .panel-button:active:hover, #panel:overview .panel-button:overview:hover, #panel:overview .panel-button:focus:hover, #panel:overview .panel-button:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.2); }
+ #panel.unlock-screen .panel-button.clock-display:active:hover, #panel.unlock-screen .panel-button.clock-display:overview:hover, #panel.unlock-screen .panel-button.clock-display:focus:hover, #panel.unlock-screen .panel-button.clock-display:checked:hover, #panel.login-screen .panel-button.clock-display:active:hover, #panel.login-screen .panel-button.clock-display:overview:hover, #panel.login-screen .panel-button.clock-display:focus:hover, #panel.login-screen .panel-button.clock-display:checked:hover, #panel:overview .panel-button.clock-display:active:hover, #panel:overview .panel-button.clock-display:overview:hover, #panel:overview .panel-button.clock-display:focus:hover, #panel:overview .panel-button.clock-display:checked:hover {
+ box-shadow: none; }
+ #panel.unlock-screen .panel-button.clock-display:active:hover .clock, #panel.unlock-screen .panel-button.clock-display:overview:hover .clock, #panel.unlock-screen .panel-button.clock-display:focus:hover .clock, #panel.unlock-screen .panel-button.clock-display:checked:hover .clock, #panel.login-screen .panel-button.clock-display:active:hover .clock, #panel.login-screen .panel-button.clock-display:overview:hover .clock, #panel.login-screen .panel-button.clock-display:focus:hover .clock, #panel.login-screen .panel-button.clock-display:checked:hover .clock, #panel:overview .panel-button.clock-display:active:hover .clock, #panel:overview .panel-button.clock-display:overview:hover .clock, #panel:overview .panel-button.clock-display:focus:hover .clock, #panel:overview .panel-button.clock-display:checked:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.2); }
+ #panel.unlock-screen .panel-button.screen-recording-indicator:active:hover, #panel.unlock-screen .panel-button.screen-recording-indicator:overview:hover, #panel.unlock-screen .panel-button.screen-recording-indicator:focus:hover, #panel.unlock-screen .panel-button.screen-recording-indicator:checked:hover, #panel.login-screen .panel-button.screen-recording-indicator:active:hover, #panel.login-screen .panel-button.screen-recording-indicator:overview:hover, #panel.login-screen .panel-button.screen-recording-indicator:focus:hover, #panel.login-screen .panel-button.screen-recording-indicator:checked:hover, #panel:overview .panel-button.screen-recording-indicator:active:hover, #panel:overview .panel-button.screen-recording-indicator:overview:hover, #panel:overview .panel-button.screen-recording-indicator:focus:hover, #panel:overview .panel-button.screen-recording-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.8); }
+ #panel.unlock-screen .panel-button.screen-sharing-indicator:active:hover, #panel.unlock-screen .panel-button.screen-sharing-indicator:overview:hover, #panel.unlock-screen .panel-button.screen-sharing-indicator:focus:hover, #panel.unlock-screen .panel-button.screen-sharing-indicator:checked:hover, #panel.login-screen .panel-button.screen-sharing-indicator:active:hover, #panel.login-screen .panel-button.screen-sharing-indicator:overview:hover, #panel.login-screen .panel-button.screen-sharing-indicator:focus:hover, #panel.login-screen .panel-button.screen-sharing-indicator:checked:hover, #panel:overview .panel-button.screen-sharing-indicator:active:hover, #panel:overview .panel-button.screen-sharing-indicator:overview:hover, #panel:overview .panel-button.screen-sharing-indicator:focus:hover, #panel:overview .panel-button.screen-sharing-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(205, 147, 9, 0.8); }
+ #panel .panel-status-indicators-box,
+ #panel .panel-status-menu-box {
+ spacing: 2px; }
+ #panel .power-status.panel-status-indicators-box {
+ spacing: 0; }
+ #panel .screencast-indicator,
+ #panel .remote-access-indicator {
+ color: #cd9309; }
+
+#appMenu {
+ spacing: 6px; }
+ #appMenu .label-shadow {
+ color: transparent; }
+
+#appMenu .panel-status-menu-box {
+ padding: 0 6px;
+ spacing: 6px; }
+
+.clock-display-box {
+ spacing: 2px; }
+ .clock-display-box .clock {
+ padding-left: 12px;
+ padding-right: 12px; }
+
+/* Activities Ripple */
+.ripple-box {
+ background-color: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 0 2px 2px rgba(255, 255, 255, 0.2);
+ width: 52px;
+ height: 52px;
+ border-radius: 0 0 52px 0; }
+ .ripple-box:rtl {
+ border-radius: 0 0 0 52px; }
+
+.quick-settings {
+ padding: 18px;
+ border-radius: 36px; }
+ .quick-settings .icon-button, .quick-settings .button {
+ padding: 10.5px; }
+
+.quick-settings-grid {
+ spacing-rows: 12px;
+ spacing-columns: 12px; }
+
+.quick-toggle {
+ border-radius: 99px;
+ min-width: 12em;
+ max-width: 12em;
+ min-height: 40px;
+ border: none;
+ /* Move padding into the box; this is to allow menu arrows
+ to extend to the border */ }
+ .quick-toggle:checked {
+ transition-duration: 100ms;
+ background-color: #3584e4;
+ color: #fff;
+ box-shadow: none; }
+ .quick-toggle:checked:focus {
+ box-shadow: inset 0 0 0 2px #629fea; }
+ .quick-toggle:checked:hover, .quick-toggle:checked:focus {
+ background-color: #4b92e7;
+ color: white; }
+ .quick-toggle:checked:active {
+ background-color: #1d72d8;
+ color: #ededed; }
+ .quick-toggle:checked:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ background-color: rgba(53, 132, 228, 0.5);
+ color: rgba(255, 255, 255, 0.5); }
+ .quick-toggle > StBoxLayout {
+ spacing: 6px; }
+ .quick-toggle.button, .quick-toggle.icon-button {
+ padding: 0; }
+ .quick-toggle > StBoxLayout {
+ padding: 0 12px; }
+ .quick-toggle:ltr > StBoxLayout {
+ padding-left: 15px; }
+ .quick-toggle:rtl > StBoxLayout {
+ padding-right: 15px; }
+ .quick-toggle .quick-toggle-label {
+ font-weight: bold; }
+ .quick-toggle .quick-toggle-icon, .quick-toggle .quick-toggle-arrow {
+ icon-size: 1.09em; }
+
+.quick-menu-toggle:ltr > StBoxLayout {
+ padding-right: 0; }
+
+.quick-menu-toggle:rtl > StBoxLayout {
+ padding-left: 0; }
+
+.quick-menu-toggle .quick-toggle-arrow {
+ background-color: rgba(255, 255, 255, 0.1);
+ padding: 6px 10.5px; }
+ .quick-menu-toggle .quick-toggle-arrow:ltr {
+ border-radius: 0 99px 99px 0; }
+ .quick-menu-toggle .quick-toggle-arrow:rtl {
+ border-radius: 99px 0 0 99px; }
+
+.quick-slider > StBoxLayout {
+ spacing: 6px; }
+
+.quick-slider .slider-bin {
+ min-height: 16px;
+ padding: 6px;
+ border-radius: 99px; }
+ .quick-slider .slider-bin:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3a414b;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .quick-slider .slider-bin:focus:hover {
+ background-color: #404852;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .quick-slider .slider-bin:focus:active {
+ background-color: #474f59;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+
+.quick-slider .quick-toggle-icon {
+ icon-size: 1.09em; }
+ .quick-slider .quick-toggle-icon:ltr {
+ margin-left: 6px; }
+ .quick-slider .quick-toggle-icon:rtl {
+ margin-right: 6px; }
+
+.quick-toggle-menu {
+ background-color: #353535;
+ border-radius: 24px;
+ padding: 12px;
+ margin: 12px 18px 0; }
+ .quick-toggle-menu .popup-menu-item > StIcon {
+ -st-icon-style: symbolic; }
+ .quick-toggle-menu .header {
+ spacing-rows: 3px;
+ spacing-columns: 12px;
+ padding-bottom: 12px; }
+ .quick-toggle-menu .header .icon {
+ icon-size: 1.635em;
+ border-radius: 999px;
+ padding: 9px;
+ background-color: #494949; }
+ .quick-toggle-menu .header .icon.active {
+ background-color: #3584e4; }
+
+.quick-settings-system-item > StBoxLayout {
+ spacing: 12px; }
+
+.quick-settings-system-item .power-item {
+ min-height: 0;
+ min-width: 0; }
+ .quick-settings-system-item .power-item:insensitive {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3a3a3a;
+ background-color: transparent; }
+
+.nm-network-item .wireless-secure-icon {
+ icon-size: 0.545em; }
+
+/* OVERVIEW */
+.controls-manager, .secondary-monitor-workspaces {
+ spacing: 12px; }
+
+#overviewGroup {
+ background-color: #232323; }
+
+.overview-controls {
+ padding-bottom: 32px; }
+
+/* Window Picker */
+.window-picker {
+ spacing: 6px; }
+
+.window-caption {
+ color: white;
+ background-color: #303030;
+ border-radius: 99px;
+ padding: 6px 12px; }
+
+.window-close, .screenshot-ui-close-button {
+ background-color: rgba(53, 53, 53, 0.98);
+ color: white;
+ border-radius: 99px;
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
+ padding: 3px;
+ height: 30px;
+ width: 30px;
+ transition-duration: 100ms; }
+ .window-close StIcon, .screenshot-ui-close-button StIcon {
+ icon-size: 24px; }
+ .window-close:hover, .screenshot-ui-close-button:hover {
+ background-color: rgba(71, 71, 71, 0.98); }
+ .window-close:active, .screenshot-ui-close-button:active {
+ background-color: rgba(86, 86, 86, 0.98); }
+
+.workspace-background {
+ border-radius: 30px;
+ background-color: rgba(3, 2, 1, 0);
+ box-shadow: 0 4px 16px 4px rgba(0, 0, 0, 0.3); }
+
+
+.search-entry {
+ border-radius: 18px;
+ margin-top: 12px;
+ margin-bottom: 6px;
+ padding: 7px 9px;
+ width: 320px;
+ background-color: #3c3c3c;
+ color: rgba(255, 255, 255, 0.7);
+ border: 2px solid #3c3c3c; }
+
+ .search-entry:hover {
+ background-color: #565656;
+ border-color: #565656;
+ color: rgba(255, 255, 255, 0.7); }
+
+ .search-entry:focus {
+ background-color: #3c4045;
+ border-color: #3584e4;
+ color: white; }
+
+ .search-entry:insensitive {
+ background-color: #383838;
+ border-color: #383838;
+ color: #979797; }
+
+ .search-entry .search-entry-icon {
+ color: inherit;
+ icon-size: 1.09em;
+ margin-top: 2px;
+ padding: 0 4px; }
+
+/* Search */
+#searchResults {
+ margin: 0 4px; }
+
+#searchResultsContent {
+ max-width: 1072px; }
+
+.search-section {
+ spacing: 18px; }
+ .search-section .search-section-separator {
+ height: 8px;
+ background-color: transparent; }
+
+.search-section-content {
+ background-color: #303030;
+ border-radius: 24px;
+ border: 1px solid rgba(255, 255, 255, 0.02);
+ color: white;
+ padding: 12px;
+ margin: 0 12px; }
+
+.list-search-result, .search-provider-icon {
+ background-color: transparent;
+ border-radius: 11px; }
+ .list-search-result:hover, .search-provider-icon:hover {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .list-search-result:selected, .search-provider-icon:selected, .list-search-result:focus, .search-provider-icon:focus {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .list-search-result:selected:hover, .search-provider-icon:selected:hover, .list-search-result:focus:hover, .search-provider-icon:focus:hover {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .list-search-result:selected:active, .search-provider-icon:selected:active, .list-search-result:focus:active, .search-provider-icon:focus:active {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .list-search-result:active, .search-provider-icon:active {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .list-search-result:outlined, .search-provider-icon:outlined, .list-search-result:checked, .search-provider-icon:checked {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .list-search-result:outlined:active, .search-provider-icon:outlined:active, .list-search-result:checked:active, .search-provider-icon:checked:active {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .list-search-result:outlined:hover, .search-provider-icon:outlined:hover, .list-search-result:checked:hover, .search-provider-icon:checked:hover {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .list-search-result:drop, .search-provider-icon:drop {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.grid-search-results {
+ spacing: 30px;
+ margin: 0 12px; }
+
+.search-provider-icon:ltr {
+ margin-right: 4px; }
+
+.search-provider-icon:rtl {
+ margin-left: 4px; }
+
+.search-provider-icon .list-search-provider-content {
+ spacing: 12px; }
+ .search-provider-icon .list-search-provider-content .list-search-provider-details {
+ width: 120px;
+ color: white; }
+
+.list-search-results {
+ spacing: 6px; }
+
+.list-search-result .list-search-result-content {
+ spacing: 6px; }
+
+.list-search-result .list-search-result-title {
+ spacing: 12px; }
+
+.list-search-result .list-search-result-description {
+ color: #bdbdbd; }
+
+/* Dash */
+#dash {
+ margin-top: 12px; }
+ #dash .placeholder {
+ background-image: none;
+ background-size: contain;
+ height: 32px; }
+ #dash .empty-dash-drop-target {
+ width: 32px;
+ height: 32px; }
+ #dash .app-well-app-running-dot {
+ margin-bottom: 14px; }
+
+.dash-background {
+ background-color: #303030;
+ border-radius: 28px;
+ padding: 12px;
+ spacing: 6px;
+ margin-bottom: 12px; }
+
+.dash-item-container > * {
+ margin: 0 2px; }
+
+.dash-item-container:ltr:first-child {
+ margin-left: 0; }
+
+.dash-item-container:rtl:first-child {
+ margin-right: 0; }
+
+.dash-item-container .app-well-app .overview-icon, .dash-item-container .grid-search-result .overview-icon {
+ padding: 6px; }
+
+.dash-item-container .show-apps {
+ transition-duration: 400ms; }
+ .dash-item-container .show-apps .overview-icon {
+ background-color: transparent; }
+ .dash-item-container .show-apps:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .dash-item-container .show-apps:selected .overview-icon, .dash-item-container .show-apps:focus .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .dash-item-container .show-apps:selected .overview-icon:hover .overview-icon, .dash-item-container .show-apps:focus .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .dash-item-container .show-apps:selected .overview-icon:active .overview-icon, .dash-item-container .show-apps:focus .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .dash-item-container .show-apps:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .dash-item-container .show-apps:outlined .overview-icon, .dash-item-container .show-apps:checked .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .dash-item-container .show-apps:outlined .overview-icon:active .overview-icon, .dash-item-container .show-apps:checked .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .dash-item-container .show-apps:outlined .overview-icon:hover .overview-icon, .dash-item-container .show-apps:checked .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .dash-item-container .show-apps:drop .overview-icon {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.dash-item-container .show-apps, .dash-item-container .app-well-app, .dash-item-container .grid-search-result {
+ padding-bottom: 12px; }
+
+.dash-separator {
+ width: 1px;
+ margin: 0 4px;
+ background-color: rgba(255, 255, 255, 0.1);
+ margin-bottom: 12px; }
+
+.dash-label {
+ color: white;
+ background-color: #232323;
+ border-radius: 99px;
+ padding: 6px 12px;
+ text-align: center;
+ -y-offset: 8px; }
+
+/* App Grid */
+.icon-grid {
+ row-spacing: 12px;
+ column-spacing: 12px;
+ max-row-spacing: 42px;
+ max-column-spacing: 42px;
+ page-padding-top: 24px;
+ page-padding-bottom: 24px;
+ page-padding-left: 18px;
+ page-padding-right: 18px; }
+
+/* App Icons */
+.app-well-app, .grid-search-result {
+ transition-duration: 400ms; }
+ .app-well-app .overview-icon, .grid-search-result .overview-icon {
+ background-color: transparent; }
+ .app-well-app:hover .overview-icon, .grid-search-result:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .app-well-app:selected .overview-icon, .grid-search-result:selected .overview-icon, .app-well-app:focus .overview-icon, .grid-search-result:focus .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app:selected .overview-icon:hover .overview-icon, .grid-search-result:selected .overview-icon:hover .overview-icon, .app-well-app:focus .overview-icon:hover .overview-icon, .grid-search-result:focus .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app:selected .overview-icon:active .overview-icon, .grid-search-result:selected .overview-icon:active .overview-icon, .app-well-app:focus .overview-icon:active .overview-icon, .grid-search-result:focus .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app:active .overview-icon, .grid-search-result:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app:outlined .overview-icon, .grid-search-result:outlined .overview-icon, .app-well-app:checked .overview-icon, .grid-search-result:checked .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .app-well-app:outlined .overview-icon:active .overview-icon, .grid-search-result:outlined .overview-icon:active .overview-icon, .app-well-app:checked .overview-icon:active .overview-icon, .grid-search-result:checked .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .app-well-app:outlined .overview-icon:hover .overview-icon, .grid-search-result:outlined .overview-icon:hover .overview-icon, .app-well-app:checked .overview-icon:hover .overview-icon, .grid-search-result:checked .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .app-well-app:drop .overview-icon, .grid-search-result:drop .overview-icon {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+ .app-well-app .overview-icon, .grid-search-result .overview-icon {
+ padding: 12px; }
+ .app-well-app .overview-icon.overview-icon-with-label > StBoxLayout, .grid-search-result .overview-icon.overview-icon-with-label > StBoxLayout {
+ spacing: 6px; }
+
+.app-well-app.app-folder, .app-folder.grid-search-result {
+ transition-duration: 400ms; }
+ .app-well-app.app-folder .overview-icon, .app-folder.grid-search-result .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .app-well-app.app-folder:hover .overview-icon, .app-folder.grid-search-result:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .app-well-app.app-folder:selected .overview-icon, .app-folder.grid-search-result:selected .overview-icon, .app-well-app.app-folder:focus .overview-icon, .app-folder.grid-search-result:focus .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app.app-folder:selected .overview-icon:hover .overview-icon, .app-folder.grid-search-result:selected .overview-icon:hover .overview-icon, .app-well-app.app-folder:focus .overview-icon:hover .overview-icon, .app-folder.grid-search-result:focus .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app.app-folder:selected .overview-icon:active .overview-icon, .app-folder.grid-search-result:selected .overview-icon:active .overview-icon, .app-well-app.app-folder:focus .overview-icon:active .overview-icon, .app-folder.grid-search-result:focus .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.13); }
+ .app-well-app.app-folder:active .overview-icon, .app-folder.grid-search-result:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.16); }
+ .app-well-app.app-folder:outlined .overview-icon, .app-folder.grid-search-result:outlined .overview-icon, .app-well-app.app-folder:checked .overview-icon, .app-folder.grid-search-result:checked .overview-icon {
+ background-color: rgba(255, 255, 255, 0.19); }
+ .app-well-app.app-folder:outlined .overview-icon:active .overview-icon, .app-folder.grid-search-result:outlined .overview-icon:active .overview-icon, .app-well-app.app-folder:checked .overview-icon:active .overview-icon, .app-folder.grid-search-result:checked .overview-icon:active .overview-icon {
+ background-color: rgba(255, 255, 255, 0.22); }
+ .app-well-app.app-folder:outlined .overview-icon:hover .overview-icon, .app-folder.grid-search-result:outlined .overview-icon:hover .overview-icon, .app-well-app.app-folder:checked .overview-icon:hover .overview-icon, .app-folder.grid-search-result:checked .overview-icon:hover .overview-icon {
+ background-color: rgba(255, 255, 255, 0.25); }
+ .app-well-app.app-folder:drop .overview-icon, .app-folder.grid-search-result:drop .overview-icon {
+ border: 2px solid rgba(53, 132, 228, 0.8);
+ background-color: rgba(53, 132, 228, 0.2); }
+
+.app-folder-dialog {
+ border-radius: 32px;
+ background-color: #303030; }
+ .app-folder-dialog .folder-name-container {
+ padding: 24px 36px 0;
+ spacing: 12px;
+ /* FIXME: this is to keep the label in sync with the entry */ }
+ .app-folder-dialog .folder-name-container .folder-name-entry {
+ width: 300px; }
+ .app-folder-dialog .folder-name-container .folder-name-label {
+ padding: 5px 7px;
+ color: white; }
+ .app-folder-dialog .folder-name-container .edit-folder-button {
+ padding: 0;
+ width: 36px;
+ height: 36px;
+ border-radius: 99px; }
+ .app-folder-dialog .folder-name-container .edit-folder-button > StIcon {
+ icon-size: 1.09em; }
+ .app-folder-dialog .icon-grid {
+ row-spacing: 12px;
+ column-spacing: 30px;
+ page-padding-top: 0;
+ page-padding-bottom: 0;
+ page-padding-left: 0;
+ page-padding-right: 0; }
+ .app-folder-dialog .page-indicators {
+ margin-bottom: 18px; }
+
+.app-folder-dialog-container {
+ padding: 12px;
+ width: 720px;
+ height: 720px; }
+
+.app-well-app-running-dot {
+ height: 5px;
+ width: 5px;
+ border-radius: 5px;
+ margin-bottom: 8px;
+ background-color: white; }
+
+.rename-folder-popup .rename-folder-popup-item {
+ spacing: 6px; }
+ .rename-folder-popup .rename-folder-popup-item:ltr, .rename-folder-popup .rename-folder-popup-item:rtl {
+ padding: 0 12px; }
+
+.page-indicator {
+ padding: 6px 12px 0;
+ transition-duration: 400ms; }
+ .page-indicator .page-indicator-icon {
+ width: 10px;
+ height: 10px;
+ border-radius: 10px;
+ background-color: white; }
+
+.apps-scroll-view {
+ padding: 0; }
+
+.system-action-icon {
+ background-color: rgba(0, 0, 0, 0.8);
+ color: #fff;
+ border-radius: 99px;
+ icon-size: 48px; }
+
+.page-navigation-hint.dnd {
+ background: rgba(255, 255, 255, 0.1); }
+
+.page-navigation-hint.next:ltr, .page-navigation-hint.previous:rtl {
+ background-gradient-start: rgba(255, 255, 255, 0.05);
+ background-gradient-end: transparent;
+ background-gradient-direction: horizontal;
+ border-radius: 24px 0px 0px 24px; }
+
+.page-navigation-hint.previous:ltr, .page-navigation-hint.next:rtl {
+ background-gradient-start: transparent;
+ background-gradient-end: rgba(255, 255, 255, 0.05);
+ background-gradient-direction: horizontal;
+ border-radius: 0px 24px 24px 0px; }
+
+.page-navigation-arrow > StIcon {
+ margin: 6px;
+ padding: 18px;
+ width: 24px;
+ height: 24px;
+ border-radius: 99px; }
+
+.page-navigation-arrow:insensitive > StIcon {
+ transition-duration: 100ms;
+ background-color: transparent;
+ background-color: none;
+ box-shadow: none; }
+ .page-navigation-arrow:insensitive > StIcon:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ background-color: transparent;
+ color: rgba(255, 255, 255, 0.5); }
+
+.page-navigation-arrow:hover > StIcon {
+ transition-duration: 100ms;
+ color: white;
+ background-color: rgba(73, 73, 73, 0.525); }
+
+.page-navigation-arrow:active > StIcon {
+ transition-duration: 100ms;
+ color: white;
+ background-color: rgba(80, 80, 80, 0.525); }
+
+/* Workspace pager */
+.workspace-thumbnails {
+ visible-width: 32px;
+ spacing: 6px;
+ padding: 6px; }
+ .workspace-thumbnails .workspace-thumbnail {
+ border-radius: 3px; }
+ .workspace-thumbnails .placeholder {
+ background-image: url("resource:///org/gnome/shell/theme/workspace-placeholder.svg");
+ background-size: contain;
+ width: 18px; }
+
+.workspace-thumbnail-indicator {
+ border: 3px solid #3584e4;
+ border-radius: 3px;
+ padding: 0px; }
+
+.ripple-pointer-location {
+ width: 50px;
+ height: 50px;
+ border-radius: 25px;
+ background-color: rgba(188, 214, 246, 0.3);
+ box-shadow: 0 0 2px 2px #8fbbf0; }
+
+.pie-timer {
+ width: 60px;
+ height: 60px;
+ -pie-border-width: 3px;
+ -pie-border-color: #3584e4;
+ -pie-background-color: rgba(233, 242, 252, 0.3); }
+
+.magnifier-zoom-region {
+ border: 2px solid #3584e4; }
+ .magnifier-zoom-region.full-screen {
+ border-width: 0; }
+
+.select-area-rubberband {
+ background-color: rgba(53, 132, 228, 0.3);
+ border: 1px solid #3584e4; }
+
+.user-icon {
+ background-size: contain;
+ color: white;
+ border-radius: 99px;
+ icon-size: 4.36em; }
+ .user-icon:hover {
+ color: white; }
+ .user-icon StIcon {
+ background-color: rgba(255, 255, 255, 0.05);
+ border-radius: 99px;
+ padding: 12px;
+ width: 2.725em;
+ height: 2.725em; }
+ .user-icon.user-avatar {
+ border: 2px white; }
+
+.user-widget.vertical .user-icon {
+ icon-size: 6.54em; }
+ .user-widget.vertical .user-icon StIcon {
+ padding: 20px;
+ padding-top: 18px;
+ padding-bottom: 22px;
+ width: 5.995em;
+ height: 5.995em; }
+
+.lightbox {
+ background-color: black; }
+
+.flashspot {
+ background-color: white; }
+
+.hidden {
+ color: rgba(0, 0, 0, 0); }
+
+.caps-lock-warning-label {
+ text-align: center;
+ padding-bottom: 8px;
+ color: #cd9309; }
+
+/* Workspace animation */
+.workspace-animation {
+ background-color: #232323; }
+
+/* Tiled window previews */
+.tile-preview {
+ background-color: rgba(53, 132, 228, 0.5);
+ border: 1px solid #3584e4; }
+
+/* On-screen Keyboard */
+#keyboard {
+ background-color: #232323;
+ box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.02); }
+ #keyboard .page-indicator {
+ padding: 6px; }
+ #keyboard .page-indicator .page-indicator-icon {
+ width: 8px;
+ height: 8px; }
+
+.key-container {
+ padding: 4px;
+ spacing: 4px; }
+
+.keyboard-key {
+ font-size: 16pt;
+ font-weight: bold;
+ min-height: 1.2em;
+ min-width: 1.2em;
+ border-width: 1px;
+ border-style: solid;
+ border-radius: 12px;
+ box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2);
+ color: white;
+ background-color: #545454; }
+ .keyboard-key:focus {
+ color: white;
+ background-color: #2c333d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .keyboard-key:focus:hover {
+ background-color: #373f49;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-key:focus:active {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-key:hover {
+ color: white;
+ background-color: #5e5e5e; }
+ .keyboard-key:active {
+ color: white;
+ background-color: #666666; }
+ .keyboard-key:checked {
+ color: white;
+ background-color: #737373; }
+ .keyboard-key:grayed {
+ background-color: #282828;
+ color: white;
+ border-color: rgba(0, 0, 0, 0.7); }
+ .keyboard-key.default-key {
+ color: white;
+ background-color: #3b3b3b;
+ border-radius: 12px; }
+ .keyboard-key.default-key:hover {
+ color: white;
+ background-color: #454545; }
+ .keyboard-key.default-key:active {
+ color: white;
+ background-color: #4d4d4d; }
+ .keyboard-key.default-key:checked {
+ color: white;
+ background-color: #595959; }
+ .keyboard-key.default-key:latched {
+ border-color: #4b92e7;
+ background-color: #3584e4; }
+ .keyboard-key.enter-key {
+ color: #fff;
+ background-color: #428ce6;
+ border-radius: 12px;
+ color: white; }
+ .keyboard-key.enter-key:hover {
+ color: #fff;
+ background-color: #5497e8; }
+ .keyboard-key.enter-key:active {
+ color: #fff;
+ background-color: #629fea; }
+ .keyboard-key.enter-key:checked {
+ color: #fff;
+ background-color: #78aded; }
+ .keyboard-key.shift-key-uppercase {
+ background-color: gray;
+ color: #232323; }
+ .keyboard-key.shift-key-uppercase:hover {
+ background-color: #8c8c8c;
+ color: #303030; }
+ .keyboard-key StIcon {
+ icon-size: 24px; }
+
+.keyboard-subkeys {
+ -arrow-border-radius: 16px;
+ -arrow-background-color: #232323;
+ -arrow-border-width: 1px;
+ -arrow-border-color: #3a3a3a;
+ -arrow-base: 20px;
+ -arrow-rise: 10px;
+ -boxpointer-gap: 6px;
+ padding: 6px; }
+ .keyboard-subkeys .keyboard-key {
+ color: white;
+ background-color: #545454;
+ border-radius: 12px; }
+ .keyboard-subkeys .keyboard-key:focus {
+ color: white;
+ background-color: #2c333d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .keyboard-subkeys .keyboard-key:focus:hover {
+ background-color: #373f49;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-subkeys .keyboard-key:focus:active {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .keyboard-subkeys .keyboard-key:hover {
+ color: white;
+ background-color: #5e5e5e; }
+ .keyboard-subkeys .keyboard-key:active {
+ color: white;
+ background-color: #666666; }
+ .keyboard-subkeys .keyboard-key:checked {
+ color: white;
+ background-color: #737373; }
+
+.emoji-page .keyboard-key {
+ background-color: transparent;
+ border: none;
+ color: initial; }
+
+.emoji-panel .keyboard-key:latched {
+ border-color: #4b92e7;
+ background-color: #3584e4; }
+
+.word-suggestions {
+ font-size: 12pt;
+ spacing: 12px;
+ min-height: 17pt;
+ padding: 12px;
+ color: white; }
+ .word-suggestions StButton {
+ margin: 0 3px;
+ min-width: 32px;
+ border-radius: 6px;
+ padding: 0px 18px;
+ background-color: transparent;
+ background-image: none; }
+ .word-suggestions StButton:focus {
+ color: white;
+ background-color: #2c333d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6); }
+ .word-suggestions StButton:focus:hover {
+ background-color: #373f49;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .word-suggestions StButton:focus:active {
+ background-color: #3c444d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7); }
+ .word-suggestions StButton:hover {
+ color: white;
+ background-color: #5e5e5e; }
+ .word-suggestions StButton:active {
+ color: white;
+ background-color: #666666; }
+ .word-suggestions StButton:checked {
+ color: white;
+ background-color: #737373; }
+
+/* Looking Glass */
+#LookingGlassDialog {
+ background-color: #232323;
+ border-radius: 0 0 16px 16px;
+ border-top-width: 0;
+ border: 1px solid rgba(255, 255, 255, 0.02);
+ color: white;
+ padding: 6px;
+ spacing: 6px;
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2); }
+ #LookingGlassDialog > #Toolbar {
+ border: none;
+ padding: 6px;
+ border-radius: 0;
+ background-color: transparent;
+ spacing: 6px; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button {
+ padding: 6px 12px; }
+ #LookingGlassDialog > #Toolbar .lg-toolbar-button > StIcon {
+ icon-size: 1.09em; }
+ #LookingGlassDialog .labels {
+ spacing: 6px; }
+ #LookingGlassDialog .notebook-tab {
+ -natural-hpadding: 12px;
+ -minimum-hpadding: 12px;
+ font-weight: bold;
+ padding: 6px 12px;
+ color: #d9d9d9;
+ transition-duration: 100ms;
+ box-shadow: none;
+ border: none;
+ border-radius: 6px;
+ background-color: transparent; }
+ #LookingGlassDialog .notebook-tab:hover {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.05); }
+ #LookingGlassDialog .notebook-tab:selected {
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1); }
+ #LookingGlassDialog StBoxLayout#EvalBox {
+ padding: 4px;
+ spacing: 6px;
+ padding: 6px; }
+ #LookingGlassDialog StBoxLayout#ResultsArea {
+ spacing: 6px;
+ padding: 6px; }
+
+.lg-dialog StEntry {
+ background-color: rgba(48, 48, 48, 0.6);
+ color: white;
+ border-color: rgba(255, 255, 255, 0.2);
+ min-height: 22px;
+ selection-background-color: #3584e4;
+ selected-color: #fff; }
+
+.lg-dialog .shell-link {
+ color: #8fbbf0; }
+ .lg-dialog .shell-link:hover {
+ color: #bcd6f6; }
+ .lg-dialog .shell-link:active {
+ color: #629fea; }
+
+.lg-dialog .actor-link {
+ color: #979797; }
+ .lg-dialog .actor-link:hover {
+ color: #cacaca; }
+ .lg-dialog .actor-link:active {
+ color: #646464; }
+ .lg-dialog .actor-link StIcon {
+ icon-size: 12px; }
+
+.lg-completions-text {
+ font-style: italic; }
+
+.lg-obj-inspector-title {
+ spacing: 6px; }
+
+.lg-obj-inspector-button {
+ border: 1px solid rgba(0, 0, 0, 0.7);
+ padding: 4px;
+ border-radius: 8px; }
+ .lg-obj-inspector-button:hover {
+ border: 1px solid #ffffff; }
+
+#lookingGlassExtensions {
+ padding: 6px; }
+
+.lg-extensions-list {
+ padding: 6px;
+ spacing: 6px; }
+
+.lg-extension {
+ padding: 12px;
+ border-radius: 8px;
+ margin: 4px;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3f3f3f; }
+ .lg-extension:focus {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #3e4650;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.6) !important; }
+ .lg-extension:focus:hover {
+ background-color: #454d56;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .lg-extension:focus:active {
+ background-color: #4c545d;
+ box-shadow: inset 0 0 0 2px rgba(53, 132, 228, 0.7) !important; }
+ .lg-extension:hover {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #474747; }
+ .lg-extension:active {
+ transition-duration: 100ms;
+ color: white;
+ background-color: #4e4e4e; }
+
+.lg-extension-meta {
+ spacing: 6px; }
+
+#LookingGlassPropertyInspector {
+ background: #232323;
+ border: 1px solid rgba(0, 0, 0, 0.7);
+ border-radius: 8px;
+ padding: 6px; }
+
+.lg-debug-flag-button {
+ color: white; }
+ .lg-debug-flag-button StLabel {
+ padding: 6px, 12px; }
+ .lg-debug-flag-button:hover {
+ color: white; }
+ .lg-debug-flag-button:active {
+ color: #cccccc; }
+
+.lg-debug-flags-header {
+ padding-top: 12px;
+ padding: 6px; }
+
+/* Login Dialog */
+.login-dialog-banner-view {
+ padding-top: 24px;
+ max-width: 23em; }
+
+.login-dialog,
+.unlock-dialog {
+ border: none;
+ background-color: transparent; }
+ .login-dialog .modal-dialog-button-box,
+ .unlock-dialog .modal-dialog-button-box {
+ spacing: 3px; }
+ .login-dialog .modal-dialog-button,
+ .unlock-dialog .modal-dialog-button {
+ padding: 4px 18px;
+ background-color: #1b1b1b;
+ border-color: #1b1b1b;
+ color: white; }
+ .login-dialog .modal-dialog-button:hover, .login-dialog .modal-dialog-button:focus,
+ .unlock-dialog .modal-dialog-button:hover,
+ .unlock-dialog .modal-dialog-button:focus {
+ background-color: #303030;
+ border-color: #303030; }
+ .login-dialog .modal-dialog-button:active,
+ .unlock-dialog .modal-dialog-button:active {
+ background-color: #161616;
+ border-color: #161616; }
+ .login-dialog .modal-dialog-button:insensitive,
+ .unlock-dialog .modal-dialog-button:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ border-color: #161616;
+ background-color: #161616;
+ color: rgba(255, 255, 255, 0.7); }
+ .login-dialog .modal-dialog-button:default,
+ .unlock-dialog .modal-dialog-button:default {
+ transition-duration: 100ms;
+ background-color: #3584e4;
+ color: #fff;
+ box-shadow: none; }
+ .login-dialog .modal-dialog-button:default:focus,
+ .unlock-dialog .modal-dialog-button:default:focus {
+ box-shadow: inset 0 0 0 2px #629fea; }
+ .login-dialog .modal-dialog-button:default:hover, .login-dialog .modal-dialog-button:default:focus,
+ .unlock-dialog .modal-dialog-button:default:hover,
+ .unlock-dialog .modal-dialog-button:default:focus {
+ background-color: #4b92e7;
+ color: white; }
+ .login-dialog .modal-dialog-button:default:active,
+ .unlock-dialog .modal-dialog-button:default:active {
+ background-color: #1d72d8;
+ color: #ededed; }
+ .login-dialog .modal-dialog-button:default:insensitive,
+ .unlock-dialog .modal-dialog-button:default:insensitive {
+ transition-duration: 100ms;
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+ background-color: rgba(53, 132, 228, 0.5);
+ color: rgba(255, 255, 255, 0.5); }
+ .login-dialog .cancel-button,
+ .login-dialog .switch-user-button,
+ .login-dialog .login-dialog-session-list-button,
+ .unlock-dialog .cancel-button,
+ .unlock-dialog .switch-user-button,
+ .unlock-dialog .login-dialog-session-list-button {
+ padding: 0;
+ border-radius: 99px;
+ width: 2.18em;
+ height: 2.18em;
+ border-color: #1b1b1b;
+ background-color: #1b1b1b; }
+ .login-dialog .cancel-button StIcon,
+ .login-dialog .switch-user-button StIcon,
+ .login-dialog .login-dialog-session-list-button StIcon,
+ .unlock-dialog .cancel-button StIcon,
+ .unlock-dialog .switch-user-button StIcon,
+ .unlock-dialog .login-dialog-session-list-button StIcon {
+ icon-size: 1.09em; }
+ .login-dialog .caps-lock-warning-label,
+ .login-dialog .login-dialog-message-warning,
+ .unlock-dialog .caps-lock-warning-label,
+ .unlock-dialog .login-dialog-message-warning {
+ color: white; }
+
+.login-dialog-logo-bin {
+ padding: 24px 0px; }
+
+.login-dialog-banner {
+ color: #e6e6e6; }
+
+.login-dialog-button-box {
+ width: 23em;
+ spacing: 5px; }
+
+.login-dialog-message {
+ text-align: center; }
+
+.login-dialog-user-selection-box {
+ padding: 100px 0px; }
+
+.login-dialog-not-listed-label {
+ padding-left: 2px; }
+ .login-dialog-not-listed-button:focus .login-dialog-not-listed-label, .login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
+ color: white; }
+
+.login-dialog-not-listed-label {
+ font-weight: bold;
+ color: #b3b3b3;
+ padding-top: 1em; }
+
+.login-dialog-auth-list-view {
+ -st-vfade-offset: 1em; }
+
+.login-dialog-auth-list {
+ spacing: 6px;
+ margin-left: 2em; }
+
+.login-dialog-auth-list-title {
+ margin-left: 2em; }
+
+.login-dialog-auth-list-item {
+ border-radius: 12px;
+ padding: 6px;
+ color: #b3b3b3; }
+ .login-dialog-auth-list-item:focus, .login-dialog-auth-list-item:selected {
+ background-color: #3584e4;
+ color: #fff; }
+
+.login-dialog-auth-list-label {
+ font-size: 13pt;
+ font-weight: bold;
+ padding-left: 15px; }
+ .login-dialog-auth-list-label:ltr {
+ padding-left: 14px;
+ text-align: left; }
+ .login-dialog-auth-list-label:rtl {
+ padding-right: 14px;
+ text-align: right; }
+
+.login-dialog-user-list-view {
+ -st-vfade-offset: 1em; }
+
+.login-dialog-user-list {
+ spacing: 12px;
+ width: 23em; }
+ .login-dialog-user-list:expanded .login-dialog-user-list-item:selected {
+ background-color: #3584e4;
+ color: #fff; }
+ .login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
+ border-right: 2px solid #3584e4; }
+
+.login-dialog-user-list-item {
+ border-radius: 12px;
+ padding: 6px;
+ color: #b3b3b3; }
+ .login-dialog-user-list-item:ltr .user-widget {
+ padding-right: 1em; }
+ .login-dialog-user-list-item:rtl .user-widget {
+ padding-left: 1em; }
+ .login-dialog-user-list-item .login-dialog-timed-login-indicator {
+ height: 2px;
+ margin-top: 6px;
+ background-color: white; }
+ .login-dialog-user-list-item:focus .login-dialog-timed-login-indicator {
+ background-color: #fff; }
+
+.user-widget-label {
+ color: white; }
+
+.user-widget.horizontal .user-widget-label {
+ font-size: 13pt;
+ font-weight: bold;
+ padding-left: 15px; }
+ .user-widget.horizontal .user-widget-label:ltr {
+ padding-left: 14px;
+ text-align: left; }
+ .user-widget.horizontal .user-widget-label:rtl {
+ padding-right: 14px;
+ text-align: right; }
+
+.user-widget.vertical .user-widget-label {
+ font-size: 16pt;
+ text-align: center;
+ font-weight: normal;
+ padding-top: 16px; }
+
+.login-dialog-timed-login-indicator {
+ height: 2px;
+ background-color: #999999; }
+
+.login-dialog-prompt-layout {
+ padding-top: 24px;
+ padding-bottom: 12px;
+ spacing: 12px;
+ width: 23em; }
+
+.login-dialog-prompt-entry {
+ height: 1.5em; }
+
+.login-dialog-prompt-label {
+ color: #cccccc;
+ font-size: 12pt;
+ padding-top: 1em; }
+
+.login-dialog StEntry {
+ background-color: #1b1b1b;
+ color: white; }
+
+.unlock-dialog StEntry {
+ border: none !important; }
+ .unlock-dialog StEntry:focus {
+ background-color: rgba(255, 255, 255, 0.1); }
+ .unlock-dialog StEntry:insensitive {
+ color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05); }
+
+.unlock-dialog .cancel-button,
+.unlock-dialog .switch-user-button,
+.unlock-dialog .login-dialog-session-list-button {
+ border-color: transparent;
+ background-color: rgba(255, 255, 255, 0.1); }
+
+/* Screen Shield */
+.unlock-dialog-clock {
+ color: white;
+ font-weight: 300;
+ text-align: center;
+ spacing: 24px;
+ padding-bottom: 2.5em; }
+
+.unlock-dialog-clock-time {
+ font-size: 64pt;
+ padding-top: 42px; }
+
+.unlock-dialog-clock-date {
+ font-size: 16pt;
+ font-weight: normal; }
+
+.unlock-dialog-clock-hint {
+ font-weight: normal;
+ padding-top: 48px; }
+
+.unlock-dialog-notifications-container {
+ margin: 12px;
+ spacing: 6px;
+ width: 23em;
+ background-color: transparent; }
+ .unlock-dialog-notifications-container .summary-notification-stack-scrollview {
+ padding-top: 0;
+ padding-bottom: 0; }
+ .unlock-dialog-notifications-container .notification,
+ .unlock-dialog-notifications-container .unlock-dialog-notification-source {
+ padding: 12px 16px;
+ border: none;
+ background-color: rgba(255, 255, 255, 0.1);
+ color: white;
+ border-radius: 16px; }
+ .unlock-dialog-notifications-container .notification.critical,
+ .unlock-dialog-notifications-container .unlock-dialog-notification-source.critical {
+ background-color: rgba(255, 255, 255, 0.2); }
+
+.unlock-dialog-notification-label {
+ padding-left: 12px;
+ padding-right: 0; }
+ .unlock-dialog-notification-label:rtl {
+ padding-right: 12px;
+ padding-left: 0; }
+
+.unlock-dialog-notification-count-text {
+ font-weight: bold;
+ padding: 0 12px;
+ color: white;
+ background-color: rgba(255, 255, 255, 0.1);
+ border-radius: 99px; }
+
+.screen-shield-background {
+ background: black;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6); }
+
+#lockDialogGroup {
+ background-color: #232323; }
+
+#unlockDialogNotifications StButton#vhandle, #unlockDialogNotifications StButton#hhandle {
+ background-color: rgba(48, 48, 48, 0.3); }
+ #unlockDialogNotifications StButton#vhandle:hover, #unlockDialogNotifications StButton#vhandle:focus, #unlockDialogNotifications StButton#hhandle:hover, #unlockDialogNotifications StButton#hhandle:focus {
+ background-color: rgba(48, 48, 48, 0.5); }
+ #unlockDialogNotifications StButton#vhandle:active, #unlockDialogNotifications StButton#hhandle:active {
+ background-color: rgba(53, 132, 228, 0.5); }
diff --git a/data/theme/gnome-shell.scss b/data/theme/gnome-shell.scss
new file mode 100644
index 0000000..8ee4999
--- /dev/null
+++ b/data/theme/gnome-shell.scss
@@ -0,0 +1,6 @@
+$variant: 'dark';
+
+@import "gnome-shell-sass/_colors"; //use gtk colors
+@import "gnome-shell-sass/_drawing";
+@import "gnome-shell-sass/_common";
+@import "gnome-shell-sass/_widgets";
diff --git a/data/theme/meson.build b/data/theme/meson.build
new file mode 100644
index 0000000..e6c1723
--- /dev/null
+++ b/data/theme/meson.build
@@ -0,0 +1,62 @@
+theme_sources = files([
+ 'gnome-shell-high-contrast.scss',
+ 'gnome-shell.scss',
+ 'gnome-shell-sass/_colors.scss',
+ 'gnome-shell-sass/_common.scss',
+ 'gnome-shell-sass/_drawing.scss',
+ 'gnome-shell-sass/_high-contrast-colors.scss',
+ 'gnome-shell-sass/_widgets.scss',
+ 'gnome-shell-sass/widgets/_a11y.scss',
+ 'gnome-shell-sass/widgets/_app-grid.scss',
+ 'gnome-shell-sass/widgets/_base.scss',
+ 'gnome-shell-sass/widgets/_buttons.scss',
+ 'gnome-shell-sass/widgets/_calendar.scss',
+ 'gnome-shell-sass/widgets/_check-box.scss',
+ 'gnome-shell-sass/widgets/_corner-ripple.scss',
+ 'gnome-shell-sass/widgets/_dash.scss',
+ 'gnome-shell-sass/widgets/_dialogs.scss',
+ 'gnome-shell-sass/widgets/_entries.scss',
+ 'gnome-shell-sass/widgets/_hotplug.scss',
+ 'gnome-shell-sass/widgets/_ibus-popup.scss',
+ 'gnome-shell-sass/widgets/_keyboard.scss',
+ 'gnome-shell-sass/widgets/_login-dialog.scss',
+ 'gnome-shell-sass/widgets/_looking-glass.scss',
+ 'gnome-shell-sass/widgets/_message-list.scss',
+ 'gnome-shell-sass/widgets/_misc.scss',
+ 'gnome-shell-sass/widgets/_notifications.scss',
+ 'gnome-shell-sass/widgets/_osd.scss',
+ 'gnome-shell-sass/widgets/_overview.scss',
+ 'gnome-shell-sass/widgets/_panel.scss',
+ 'gnome-shell-sass/widgets/_popovers.scss',
+ 'gnome-shell-sass/widgets/_quick-settings.scss',
+ 'gnome-shell-sass/widgets/_screen-shield.scss',
+ 'gnome-shell-sass/widgets/_screenshot.scss',
+ 'gnome-shell-sass/widgets/_scrollbars.scss',
+ 'gnome-shell-sass/widgets/_search-entry.scss',
+ 'gnome-shell-sass/widgets/_search-results.scss',
+ 'gnome-shell-sass/widgets/_slider.scss',
+ 'gnome-shell-sass/widgets/_switcher-popup.scss',
+ 'gnome-shell-sass/widgets/_switches.scss',
+ 'gnome-shell-sass/widgets/_window-picker.scss',
+ 'gnome-shell-sass/widgets/_workspace-switcher.scss',
+ 'gnome-shell-sass/widgets/_workspace-thumbnails.scss'
+])
+
+stylesheets = [
+ 'gnome-shell-high-contrast.css',
+ 'gnome-shell.css'
+]
+
+foreach stylesheet: stylesheets
+ if not fs.exists(stylesheet)
+ sassc = find_program('sassc')
+ theme_deps += custom_target(stylesheet,
+ input: fs.replace_suffix(stylesheet, '.scss'),
+ output: stylesheet,
+ command: [
+ sassc, '-a', '@INPUT@', '@OUTPUT@'
+ ],
+ depend_files: theme_sources)
+ endif
+endforeach
+
diff --git a/data/theme/pad-osd.css b/data/theme/pad-osd.css
new file mode 100644
index 0000000..e0c9048
--- /dev/null
+++ b/data/theme/pad-osd.css
@@ -0,0 +1,29 @@
+.Leader {
+ stroke-width: .5 !important;
+ stroke: #535353;
+ fill: none !important;
+}
+
+.Button {
+ stroke-width: .25;
+ stroke: #ededed;
+ fill: #ededed;
+}
+
+.Ring {
+ stroke-width: .5 !important;
+ stroke: #535353 !important;
+ fill: none !important;
+}
+
+.Label {
+ stroke: none !important;
+ stroke-width: .1 !important;
+ fill: transparent !important;
+}
+
+.TouchStrip, .TouchRing {
+ stroke-width: .1 !important;
+ stroke: #ededed !important;
+ fill: #535353 !important;
+}
diff --git a/data/theme/process-working.svg b/data/theme/process-working.svg
new file mode 100644
index 0000000..920a67d
--- /dev/null
+++ b/data/theme/process-working.svg
@@ -0,0 +1,3084 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg5369"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ width="512"
+ height="32"
+ sodipodi:docname="process-working.svg"
+ style="display:inline">
+ <metadata
+ id="metadata5375">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5373">
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient35326"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient8231-1-4-4-1">
+ <stop
+ id="stop8233-28-5-27-1"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop8235-7-3-94-3" />
+ <stop
+ id="stop8237-7-8-20-2"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop8239-2-9-1-9"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient35230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient5767-6">
+ <stop
+ id="stop5769-0"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop5771-1" />
+ <stop
+ id="stop5773-7"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop5775-8"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10255"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10257">
+ <stop
+ id="stop10259"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10261" />
+ <stop
+ id="stop10263"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10265"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10267"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10269">
+ <stop
+ id="stop10271"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10273" />
+ <stop
+ id="stop10275"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10277"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10279"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10281">
+ <stop
+ id="stop10283"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10285" />
+ <stop
+ id="stop10287"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10289"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10293">
+ <stop
+ id="stop10295"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10297" />
+ <stop
+ id="stop10299"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10301"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10305">
+ <stop
+ id="stop10307"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10309" />
+ <stop
+ id="stop10311"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10313"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10317">
+ <stop
+ id="stop10319"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10321" />
+ <stop
+ id="stop10323"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10325"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10329">
+ <stop
+ id="stop10331"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10333" />
+ <stop
+ id="stop10335"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10337"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10341">
+ <stop
+ id="stop10343"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10345" />
+ <stop
+ id="stop10347"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10349"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10353">
+ <stop
+ id="stop10355"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10357" />
+ <stop
+ id="stop10359"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10361"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10365">
+ <stop
+ id="stop10367"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10369" />
+ <stop
+ id="stop10371"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10373"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10377">
+ <stop
+ id="stop10379"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10381" />
+ <stop
+ id="stop10383"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10385"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10387"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10389">
+ <stop
+ id="stop10391"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10393" />
+ <stop
+ id="stop10395"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10397"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10399"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10401">
+ <stop
+ id="stop10403"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10405" />
+ <stop
+ id="stop10407"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10409"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10411"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10413">
+ <stop
+ id="stop10415"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10417" />
+ <stop
+ id="stop10419"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10421"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10423"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10425">
+ <stop
+ id="stop10427"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15428571"
+ offset="0.31861392"
+ id="stop10429" />
+ <stop
+ id="stop10431"
+ offset="0.54270232"
+ style="stop-color:#ffffff;stop-opacity:0.33714285" />
+ <stop
+ id="stop10433"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10435"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <linearGradient
+ id="linearGradient10437">
+ <stop
+ id="stop10439"
+ offset="0"
+ style="stop-color:#bebebe;stop-opacity:0" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="0.31861392"
+ id="stop10441" />
+ <stop
+ id="stop10443"
+ offset="0.75051737"
+ style="stop-color:#ffffff;stop-opacity:0.42857143" />
+ <stop
+ id="stop10445"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient10709"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient10711"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient11663"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient11665"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14140"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14212"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14272"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14296"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14308"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14320"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14332"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14440"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14452"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14464"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14476"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14488"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14500"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14512"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14524"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14536"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14548"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14560"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14572"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14584"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14596"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14608"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14620"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14632"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14644"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14656"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14668"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14680"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14692"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14716"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14776"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14836"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14896"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14944"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient14980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient14992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15028"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15100"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15160"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15232"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15292"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15304"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15316"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15328"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15436"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15448"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15460"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15472"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15484"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15496"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15508"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient15520"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient15532"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient8231-1-4-4-1"
+ id="radialGradient18026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.229454,-1.2865389,1.2087442,2.0939897,-228.90301,-208.08725)"
+ cx="-0.067823187"
+ cy="188.51917"
+ fx="-0.067823187"
+ fy="188.51917"
+ r="27.330345" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5767-6"
+ id="radialGradient18028"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1252013,-0.60470548,0.56813832,1.0568583,-107.67128,-11.948108)"
+ cx="0.053942412"
+ cy="189.15244"
+ fx="0.053942412"
+ fy="189.15244"
+ r="27.330345" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#808080"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1975"
+ inkscape:window-height="1098"
+ id="namedview5371"
+ showgrid="false"
+ borderlayer="true"
+ inkscape:showpageshadow="false"
+ inkscape:zoom="1"
+ inkscape:cx="346.23664"
+ inkscape:cy="-6.4057938"
+ inkscape:window-x="139"
+ inkscape:window-y="73"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="layer2"
+ inkscape:snap-bbox="true"
+ inkscape:snap-nodes="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid11933"
+ empspacing="16"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ empcolor="#0000ff"
+ empopacity="0.47843137" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="tiles"
+ style="display:none"
+ transform="translate(0,-16)">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ id="rect12451"
+ width="24"
+ height="24"
+ x="0"
+ y="0" />
+ <rect
+ y="24"
+ x="0"
+ height="24"
+ width="24"
+ id="rect12453"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" />
+ <rect
+ y="0"
+ x="24"
+ height="24"
+ width="24"
+ id="rect12455"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ id="rect12457"
+ width="24"
+ height="24"
+ x="24"
+ y="24" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ id="rect12459"
+ width="24"
+ height="24"
+ x="48"
+ y="0" />
+ <rect
+ y="24"
+ x="48"
+ height="24"
+ width="24"
+ id="rect12461"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" />
+ <rect
+ y="0"
+ x="72"
+ height="24"
+ width="24"
+ id="rect12463"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ id="rect12465"
+ width="24"
+ height="24"
+ x="72"
+ y="24" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="spinner"
+ transform="translate(0,-16)">
+ <g
+ style="display:inline"
+ id="g12246"
+ transform="matrix(0.29521872,0,0,0.2952381,149.03971,-388.51498)">
+ <path
+ transform="matrix(-0.16397381,0.61157081,-0.61162275,-0.16377992,-372.32298,1442.5061)"
+ d="m -3.4436513,184.72075 a 22.98097,22.98097 0 0 1 -25.9046347,17.42496 22.98097,22.98097 0 0 1 -19.37345,-24.4816 22.98097,22.98097 0 0 1 22.91234,-21.20622"
+ sodipodi:ry="22.98097"
+ sodipodi:rx="22.98097"
+ sodipodi:cy="179.43886"
+ sodipodi:cx="-25.809397"
+ id="path12248"
+ style="display:inline;fill:none;stroke:url(#radialGradient11663);stroke-width:17.83196449;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ sodipodi:start="0.23191105"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ inkscape:export-filename="/home/hbons/Moblin/git/carrick-ng/data/icons/network-connecting.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <path
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="/home/hbons/Moblin/git/carrick-ng/data/icons/network-connecting.png"
+ sodipodi:open="true"
+ sodipodi:end="4.712389"
+ sodipodi:start="0.23191105"
+ sodipodi:type="arc"
+ style="display:inline;fill:none;stroke:url(#radialGradient11665);stroke-width:17.83196449;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12250"
+ sodipodi:cx="-25.809397"
+ sodipodi:cy="179.43886"
+ sodipodi:rx="22.98097"
+ sodipodi:ry="22.98097"
+ d="m -3.4436513,184.72075 a 22.98097,22.98097 0 0 1 -25.9046347,17.42496 22.98097,22.98097 0 0 1 -19.37345,-24.4816 22.98097,22.98097 0 0 1 22.91234,-21.20622"
+ transform="matrix(-0.63300818,0.01438356,-0.01458424,-0.63300359,-491.4014,1510.996)" />
+ </g>
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,48.943073,-180.55304)"
+ id="use12258"
+ xlink:href="#g10450-5"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.97814761,0.20791169,-0.20791169,0.97814761,70.553652,-185.80321)"
+ id="use12260"
+ xlink:href="#use12000"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.95105653,0.30901699,-0.30901699,0.95105653,146.76602,-177.21804)"
+ id="use12266"
+ xlink:href="#g10450-5"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.91354547,0.40673664,-0.40673664,0.91354547,169.60833,-183.68101)"
+ id="use12270"
+ xlink:href="#use12000"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.86602542,0.5,-0.5,0.86602542,194.48539,-193.2587)"
+ id="use12272"
+ xlink:href="#use12002"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.80901701,0.58778525,-0.58778525,0.80901701,289.93475,-156.19404)"
+ id="use12278"
+ xlink:href="#g10450-5"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.74314484,0.66913061,-0.66913061,0.74314484,315.02774,-163.93338)"
+ id="use12282"
+ xlink:href="#use12000"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.66913062,0.74314483,-0.74314483,0.66913062,343.01848,-174.00634)"
+ id="use12284"
+ xlink:href="#use12002"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.58778527,0.809017,-0.809017,0.58778527,374.34035,-185.86931)"
+ id="use12290"
+ xlink:href="#use12008"
+ y="0"
+ x="0" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12290"
+ id="use12494"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,31.624213,-28.945572)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12494"
+ id="use12498"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,31.778114,-31.872282)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12498"
+ id="use12502"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,31.931711,-34.799662)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12502"
+ id="use12506"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,32.085178,-37.729332)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12506"
+ id="use12510"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,32.238466,-40.661342)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12510"
+ id="use12514"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,32.390823,-43.585942)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12514"
+ id="use12518"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,32.543439,-46.510902)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12518"
+ id="use12522"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,32.696338,-49.437422)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12522"
+ id="use12526"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,32.849272,-52.363942)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12526"
+ id="use12530"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,33.002239,-55.290422)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12530"
+ id="use12534"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,33.155242,-58.216862)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12534"
+ id="use12538"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,33.308287,-61.143262)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12538"
+ id="use12542"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,33.461378,-64.069632)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12542"
+ id="use12546"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,33.614517,-66.995962)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12546"
+ id="use12550"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,33.767708,-69.922282)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12550"
+ id="use12554"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,33.920952,-72.848572)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12554"
+ id="use12558"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,34.074252,-75.774862)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12558"
+ id="use12562"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,34.228224,-78.701442)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12562"
+ id="use12566"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,34.384481,-81.628162)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12566"
+ id="use12570"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,34.543085,-84.555062)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12570"
+ id="use12574"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,34.694273,-87.482889)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12574"
+ id="use12578"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,34.845821,-90.410449)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12578"
+ id="use12582"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,34.998931,-93.337739)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12582"
+ id="use12586"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,35.152046,-96.264989)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12586"
+ id="use12590"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,35.305116,-99.192209)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12590"
+ id="use12594"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,35.458143,-102.11938)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12594"
+ id="use12598"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,35.611131,-105.04652)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12598"
+ id="use12602"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,35.764086,-107.97361)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12602"
+ id="use12606"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,35.917013,-110.90066)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12606"
+ id="use12610"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,36.069915,-113.82765)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12610"
+ id="use12614"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,36.2228,-116.75459)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12614"
+ id="use12618"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,36.375673,-119.68147)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12618"
+ id="use12622"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,36.52885,-122.60769)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12622"
+ id="use12626"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,36.682157,-125.53161)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12626"
+ id="use12630"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,36.835642,-128.45319)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12630"
+ id="use12634"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,36.990059,-131.38219)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12634"
+ id="use12638"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,37.144216,-134.31083)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12638"
+ id="use12642"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,37.298091,-137.2379)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12642"
+ id="use12646"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,37.45193,-140.16497)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12646"
+ id="use12650"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,37.605737,-143.09209)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12650"
+ id="use12654"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,37.759507,-146.01924)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12654"
+ id="use12658"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,37.913236,-148.94644)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12658"
+ id="use12662"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,38.066918,-151.87367)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12662"
+ id="use12666"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,38.220552,-154.80093)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12666"
+ id="use12670"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,38.374134,-157.72821)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12670"
+ id="use12674"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,38.527663,-160.65551)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12674"
+ id="use12678"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,38.681136,-163.58282)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12678"
+ id="use12682"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,38.833937,-166.50983)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12682"
+ id="use12686"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,38.984453,-169.4367)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use12686"
+ id="use12690"
+ transform="matrix(0.9945219,0.10452846,-0.10452846,0.9945219,39.132623,-172.3634)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#g12246"
+ id="use17640"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,18.463174,-0.6860274)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17640"
+ id="use17642"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,18.545526,-2.3041175)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17642"
+ id="use17644"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,18.627869,-3.9222415)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17644"
+ id="use17646"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,18.710256,-5.5403957)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17646"
+ id="use17648"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,18.792682,-7.1585849)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17648"
+ id="use17650"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,18.875146,-8.776813)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17650"
+ id="use17652"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,18.957642,-10.395084)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17652"
+ id="use17654"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.040166,-12.0134)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17654"
+ id="use17656"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.122713,-13.631765)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17656"
+ id="use17658"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.205279,-15.250181)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17658"
+ id="use17660"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.287857,-16.868648)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17660"
+ id="use17662"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.370295,-18.487328)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17662"
+ id="use17664"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.452449,-20.10785)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17664"
+ id="use17666"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.534518,-21.730599)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17666"
+ id="use17668"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,19.616047,-23.351029)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#g12246"
+ id="use17670"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,288.32996,17.191525)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17640"
+ id="use17672"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,305.13809,1.2121247)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17642"
+ id="use17674"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,321.94624,-14.767309)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17644"
+ id="use17676"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,338.75439,-30.746723)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17646"
+ id="use17678"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,355.56257,-46.726112)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17648"
+ id="use17680"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,372.37079,-62.705487)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17650"
+ id="use17682"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,389.179,-78.684846)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17652"
+ id="use17684"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,405.98726,-94.664188)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17654"
+ id="use17686"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,422.79551,-110.64352)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17656"
+ id="use17688"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,439.60382,-126.62284)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17658"
+ id="use17690"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,456.41215,-142.60217)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17660"
+ id="use17692"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,473.22049,-158.58145)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17662"
+ id="use17694"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,490.02954,-174.56102)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17664"
+ id="use17696"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,506.83972,-190.54059)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17666"
+ id="use17698"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,523.65101,-206.52033)"
+ width="100%"
+ height="100%" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#use17668"
+ id="use17700"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,540.45771,-222.50042)"
+ width="100%"
+ height="100%" />
+ <g
+ transform="matrix(-0.29521867,7.2137245e-5,-7.2124844e-5,-0.29523807,-132.95323,452.47763)"
+ id="g17702"
+ style="display:inline">
+ <path
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="/home/hbons/Moblin/git/carrick-ng/data/icons/network-connecting.png"
+ sodipodi:open="true"
+ sodipodi:end="4.712389"
+ sodipodi:start="0.23191105"
+ sodipodi:type="arc"
+ style="display:inline;fill:none;stroke:url(#radialGradient18026);stroke-width:17.83196449;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path17704"
+ sodipodi:cx="-25.809397"
+ sodipodi:cy="179.43886"
+ sodipodi:rx="22.98097"
+ sodipodi:ry="22.98097"
+ d="m -3.4436513,184.72075 a 22.98097,22.98097 0 0 1 -25.9046347,17.42496 22.98097,22.98097 0 0 1 -19.37345,-24.4816 22.98097,22.98097 0 0 1 22.91234,-21.20622"
+ transform="matrix(-0.16397381,0.61157081,-0.61162275,-0.16377992,-372.32298,1442.5061)" />
+ <path
+ transform="matrix(-0.63300818,0.01438356,-0.01458424,-0.63300359,-491.4014,1510.996)"
+ d="m -3.4436513,184.72075 a 22.98097,22.98097 0 0 1 -25.9046347,17.42496 22.98097,22.98097 0 0 1 -19.37345,-24.4816 22.98097,22.98097 0 0 1 22.91234,-21.20622"
+ sodipodi:ry="22.98097"
+ sodipodi:rx="22.98097"
+ sodipodi:cy="179.43886"
+ sodipodi:cx="-25.809397"
+ id="path17706"
+ style="display:inline;fill:none;stroke:url(#radialGradient18028);stroke-width:17.83196449;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ sodipodi:start="0.23191105"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ inkscape:export-filename="/home/hbons/Moblin/git/carrick-ng/data/icons/network-connecting.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.081742,-0.6039496)"
+ id="use17708"
+ xlink:href="#g17702"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.164094,-2.2220396)"
+ id="use17710"
+ xlink:href="#use17708"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.246437,-3.8401636)"
+ id="use17712"
+ xlink:href="#use17710"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.328824,-5.4583176)"
+ id="use17714"
+ xlink:href="#use17712"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.41125,-7.0765066)"
+ id="use17716"
+ xlink:href="#use17714"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.493714,-8.6947346)"
+ id="use17718"
+ xlink:href="#use17716"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.57621,-10.313006)"
+ id="use17720"
+ xlink:href="#use17718"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.658734,-11.931322)"
+ id="use17722"
+ xlink:href="#use17720"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.741281,-13.549687)"
+ id="use17724"
+ xlink:href="#use17722"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.823847,-15.168103)"
+ id="use17726"
+ xlink:href="#use17724"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.906425,-16.78657)"
+ id="use17728"
+ xlink:href="#use17726"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,20.988863,-18.40525)"
+ id="use17730"
+ xlink:href="#use17728"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,21.071017,-20.025772)"
+ id="use17732"
+ xlink:href="#use17730"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,21.153086,-21.648521)"
+ id="use17734"
+ xlink:href="#use17732"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(0.99487012,0.10116048,-0.10116048,0.99487012,21.234615,-23.268951)"
+ id="use17736"
+ xlink:href="#use17734"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,304.30952,33.999897)"
+ id="use17738"
+ xlink:href="#g17702"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,321.11765,18.020497)"
+ id="use17740"
+ xlink:href="#use17708"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,337.9258,2.0410622)"
+ id="use17742"
+ xlink:href="#use17710"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,354.73395,-13.938351)"
+ id="use17744"
+ xlink:href="#use17712"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,371.54213,-29.91774)"
+ id="use17746"
+ xlink:href="#use17714"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,388.35035,-45.897115)"
+ id="use17748"
+ xlink:href="#use17716"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,405.15856,-61.876474)"
+ id="use17750"
+ xlink:href="#use17718"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,421.96682,-77.855816)"
+ id="use17752"
+ xlink:href="#use17720"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,438.77507,-93.835152)"
+ id="use17754"
+ xlink:href="#use17722"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,455.58338,-109.81448)"
+ id="use17756"
+ xlink:href="#use17724"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,472.39171,-125.79381)"
+ id="use17758"
+ xlink:href="#use17726"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,489.20005,-141.77309)"
+ id="use17760"
+ xlink:href="#use17728"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,506.0091,-157.75266)"
+ id="use17762"
+ xlink:href="#use17730"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,522.81928,-173.73223)"
+ id="use17764"
+ xlink:href="#use17732"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,539.63057,-189.71197)"
+ id="use17766"
+ xlink:href="#use17734"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="matrix(-0.0505232,0.99872288,-0.99872288,-0.0505232,556.43727,-205.69206)"
+ id="use17768"
+ xlink:href="#use17736"
+ y="0"
+ x="0" />
+ </g>
+</svg>
diff --git a/data/theme/running-indicator.svg b/data/theme/running-indicator.svg
new file mode 100644
index 0000000..ebe7ecf
--- /dev/null
+++ b/data/theme/running-indicator.svg
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="119.97824"
+ height="119.97824"
+ id="svg7355"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="running-indicator.svg">
+ <metadata
+ id="metadata4175">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#2c1cff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1141"
+ id="namedview4173"
+ showgrid="false"
+ inkscape:zoom="8.1348081"
+ inkscape:cx="81.120662"
+ inkscape:cy="58.117986"
+ inkscape:window-x="0"
+ inkscape:window-y="26"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g30864" />
+ <defs
+ id="defs7357">
+ <radialGradient
+ xlink:href="#linearGradient36429"
+ id="radialGradient7461"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.011539,0,0,0.57582113,-0.39262194,71.83807)"
+ cx="47.428951"
+ cy="167.16817"
+ fx="47.428951"
+ fy="167.16817"
+ r="37" />
+ <linearGradient
+ id="linearGradient36429">
+ <stop
+ id="stop36431"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop36433"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient36471"
+ id="radialGradient7463"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
+ cx="49.067139"
+ cy="242.50381"
+ fx="49.067139"
+ fy="242.50381"
+ r="37.00671" />
+ <linearGradient
+ id="linearGradient36471">
+ <stop
+ id="stop36473"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop36475"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <radialGradient
+ r="37.00671"
+ fy="242.50381"
+ fx="49.067139"
+ cy="242.50381"
+ cx="49.067139"
+ gradientTransform="matrix(1.1891549,0,0,0.15252127,-9.281289,132.52772)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient7488"
+ xlink:href="#linearGradient36471" />
+ </defs>
+ <g
+ id="layer1"
+ transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
+ <g
+ style="display:inline"
+ id="g30864"
+ transform="translate(255.223,70.118091)">
+ <rect
+ ry="3.4593496"
+ rx="3.4593496"
+ y="99.596962"
+ x="12.596948"
+ height="71.116341"
+ width="71.116341"
+ id="rect14000"
+ style="opacity:0.37187500000000001;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
+ <path
+ id="rect34520"
+ d="m 83.273151,166.72152 c 0,1.96759 -1.584022,3.55163 -3.551629,3.55163 l -63.443032,0 c -1.967608,0 -3.551648,-1.58402 -3.551643,-3.55164 0,-5.85318 0,-5.85318 0,0"
+ style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
+ connector-curvature="0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccscc" />
+ </g>
+ </g>
+</svg>
diff --git a/data/theme/toggle-off-hc.svg b/data/theme/toggle-off-hc.svg
new file mode 100644
index 0000000..036794f
--- /dev/null
+++ b/data/theme/toggle-off-hc.svg
@@ -0,0 +1 @@
+<svg width="48" height="26" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -291.18)"><rect ry="11" rx="11" y="299.18" x="30" height="10" width="10" style="fill:none;fill-opacity:1;stroke:#f8f7f7;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none" fill="#f8f7f7" stroke-linecap="round" stroke-linejoin="round"/><rect style="fill:#fff;stroke:none;stroke-width:1;marker:none;fill-opacity:.15000001" width="48" height="26" x="-48" y="291.18" ry="13" fill="#3081e3" rx="13" transform="scale(-1 1)"/><rect ry="11" rx="11" y="293.18" x="-24" height="22" width="22" style="stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7" transform="scale(-1 1)"/></g></svg> \ No newline at end of file
diff --git a/data/theme/toggle-off-light.svg b/data/theme/toggle-off-light.svg
new file mode 100644
index 0000000..14cb424
--- /dev/null
+++ b/data/theme/toggle-off-light.svg
@@ -0,0 +1 @@
+<svg width="48" height="26" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(-1 0 0 1 0 -291.18)"><rect style="fill:#c0bfbc;stroke:none;stroke-width:1;marker:none" width="48" height="26" x="-48" y="291.18" ry="13" fill="#3081e3" rx="13"/><rect ry="11" rx="11" y="294.18" x="-24" height="22" width="22" style="fill:#000;fill-opacity:.2;stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7"/><rect ry="11" rx="11" y="293.18" x="-24" height="22" width="22" style="stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7"/></g></svg> \ No newline at end of file
diff --git a/data/theme/toggle-off.svg b/data/theme/toggle-off.svg
new file mode 100644
index 0000000..323f73b
--- /dev/null
+++ b/data/theme/toggle-off.svg
@@ -0,0 +1 @@
+<svg width="48" height="26" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(-1 0 0 1 0 -291.18)"><rect style="fill:#fff;stroke:none;stroke-width:1;marker:none;fill-opacity:.15000001" width="48" height="26" x="-48" y="291.18" ry="13" fill="#3081e3" rx="13"/><rect ry="11" rx="11" y="294.18" x="-24" height="22" width="22" style="fill:#000;fill-opacity:.2;stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7"/><rect ry="11" rx="11" y="293.18" x="-24" height="22" width="22" style="stroke:none;stroke-width:.999999;marker:none;fill:#fff;fill-opacity:1" fill="#f8f7f7"/></g></svg> \ No newline at end of file
diff --git a/data/theme/toggle-on-hc.svg b/data/theme/toggle-on-hc.svg
new file mode 100644
index 0000000..2d553c6
--- /dev/null
+++ b/data/theme/toggle-on-hc.svg
@@ -0,0 +1 @@
+<svg width="48" height="26" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -291.18)" stroke="none"><rect style="fill:#3584e4;stroke:none;stroke-width:1;marker:none" width="48" height="26" y="291.18" ry="13" fill="#3081e3" rx="13"/><rect ry="11" rx="11" y="293.18" x="24" height="22" width="22" style="stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7"/></g><path style="fill:#f8f7f7;fill-opacity:1;stroke:none;stroke-width:2;stroke-linejoin:round;stroke-dashoffset:2" d="M14 7v12h-2V7Z"/></svg> \ No newline at end of file
diff --git a/data/theme/toggle-on-light.svg b/data/theme/toggle-on-light.svg
new file mode 100644
index 0000000..f3dbf31
--- /dev/null
+++ b/data/theme/toggle-on-light.svg
@@ -0,0 +1 @@
+<svg width="48" height="26" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -291.18)"><rect style="fill:#3584e4;stroke:none;stroke-width:1;marker:none" width="48" height="26" y="291.18" ry="13" fill="#3081e3"/><rect ry="11" rx="11" y="294.18" x="24" height="22" width="22" style="stroke:none;stroke-width:.999999;marker:none;fill:#000;fill-opacity:.2" fill="#f8f7f7"/><rect ry="11" rx="11" y="293.18" x="24" height="22" width="22" style="stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7"/></g></svg> \ No newline at end of file
diff --git a/data/theme/toggle-on.svg b/data/theme/toggle-on.svg
new file mode 100644
index 0000000..215760f
--- /dev/null
+++ b/data/theme/toggle-on.svg
@@ -0,0 +1 @@
+<svg width="48" height="26" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -291.18)"><rect style="fill:#3584e4;stroke:none;stroke-width:1;marker:none" width="48" height="26" y="291.18" ry="13" fill="#3081e3" rx="13"/><rect ry="11" rx="11" y="294.18" x="24" height="22" width="22" style="fill:#000;fill-opacity:.2;stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7"/><rect ry="11" rx="11" y="293.18" x="24" height="22" width="22" style="fill:#fff;stroke:none;stroke-width:.999999;marker:none" fill="#f8f7f7"/></g></svg> \ No newline at end of file
diff --git a/data/theme/workspace-placeholder.svg b/data/theme/workspace-placeholder.svg
new file mode 100644
index 0000000..ff5dc92
--- /dev/null
+++ b/data/theme/workspace-placeholder.svg
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="27"
+ height="76"
+ id="svg11252"
+ version="1.1"
+ sodipodi:docname="dash-placeholder-horizontal.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)">
+ <metadata
+ id="metadata19">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview17"
+ showgrid="false"
+ inkscape:pagecheckerboard="true"
+ inkscape:zoom="12.473684"
+ inkscape:cx="38"
+ inkscape:cy="26.327004"
+ inkscape:current-layer="g99967"
+ inkscape:document-rotation="0" />
+ <defs
+ id="defs11254">
+ <radialGradient
+ xlink:href="#linearGradient39563-4-2"
+ id="radialGradient68155-2-3"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,0.3486842,24.5,341.84211)"
+ cx="49"
+ cy="488"
+ fx="49"
+ fy="488"
+ r="38" />
+ <linearGradient
+ id="linearGradient39563-4-2">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop39565-1-4" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop39567-7-9" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient39573-6-1"
+ id="radialGradient68157-0-8"
+ gradientUnits="userSpaceOnUse"
+ cx="50.5"
+ cy="487.5"
+ fx="50.5"
+ fy="487.5"
+ r="10.5"
+ gradientTransform="matrix(1.2857143,0,0,1.2857143,8.571428,-114.78571)" />
+ <linearGradient
+ id="linearGradient39573-6-1">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop39575-5-6" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop39577-1-2" />
+ </linearGradient>
+ </defs>
+ <g
+ id="layer1"
+ transform="rotate(90,465.93109,104.43109)">
+ <g
+ id="g99967"
+ style="display:inline"
+ transform="translate(326,44.862171)">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.49375;fill:url(#radialGradient68155-2-3);fill-opacity:1;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ id="rect99969"
+ width="76"
+ height="2"
+ x="35.5"
+ y="511"
+ rx="0"
+ ry="0" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.43125;fill:url(#radialGradient68157-0-8);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.28571;marker:none;enable-background:accumulate"
+ id="path99971"
+ d="m 87,512.00001 c 0,7.45584 -6.044155,13.5 -13.5,13.5 -7.455844,0 -13.5,-6.04416 -13.5,-13.5 0,-7.45585 6.044156,-13.5 13.5,-13.5 7.455845,0 13.5,6.04415 13.5,13.5 z" />
+ <path
+ d="m 78.076923,512.00001 c 0,2.52776 -2.049159,4.57692 -4.576923,4.57692 -2.527766,0 -4.576923,-2.04916 -4.576923,-4.57692 0,-2.52777 2.049157,-4.57692 4.576923,-4.57692 2.527765,0 4.576923,2.04915 4.576923,4.57692 z"
+ id="path99973"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.435897;marker:none;enable-background:accumulate" />
+ </g>
+ </g>
+</svg>
diff --git a/data/update-osk-layouts.sh b/data/update-osk-layouts.sh
new file mode 100755
index 0000000..8d78c0e
--- /dev/null
+++ b/data/update-osk-layouts.sh
@@ -0,0 +1,47 @@
+#!/bin/env bash
+
+CLDR_LAYOUTS_TARBALL="http://www.unicode.org/Public/cldr/latest/keyboards.zip"
+
+WORKDIR=".osk-layout-workbench"
+CLDR2JSON="cldr2json/cldr2json.py"
+SRCDIR="$WORKDIR/keyboards/android"
+DESTDIR="osk-layouts"
+GRESOURCE_FILE="gnome-shell-osk-layouts.gresource.xml"
+TMP_GRESOURCE_FILE=".$GRESOURCE_FILE.tmp"
+
+cd `dirname $0`
+
+# Ensure work/dest dirs
+rm -rf $WORKDIR
+mkdir -p $WORKDIR
+mkdir -p "osk-layouts"
+
+# Download stuff on the work dir
+pushd $WORKDIR
+gio copy $CLDR_LAYOUTS_TARBALL .
+unzip keyboards.zip
+popd
+
+# Transform to JSON files
+$CLDR2JSON $SRCDIR $DESTDIR
+
+# Generate new gresources xml file
+cat >$TMP_GRESOURCE_FILE <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/shell/osk-layouts">
+EOF
+
+for f in $DESTDIR/*.json
+do
+ echo " <file>$(basename $f)</file>" >>$TMP_GRESOURCE_FILE
+done
+
+cat >>$TMP_GRESOURCE_FILE <<EOF
+ <file>emoji.json</file>
+ </gresource>
+</gresources>
+EOF
+
+# Rewrite old gresources xml
+mv $TMP_GRESOURCE_FILE $GRESOURCE_FILE
diff --git a/docs/reference/meson.build b/docs/reference/meson.build
new file mode 100644
index 0000000..77d9fde
--- /dev/null
+++ b/docs/reference/meson.build
@@ -0,0 +1,5 @@
+version_conf = configuration_data()
+version_conf.set('VERSION', meson.project_version())
+
+subdir('shell')
+subdir('st')
diff --git a/docs/reference/shell/meson.build b/docs/reference/shell/meson.build
new file mode 100644
index 0000000..96b0d08
--- /dev/null
+++ b/docs/reference/shell/meson.build
@@ -0,0 +1,51 @@
+private_headers = [
+ 'gactionmuxer.h',
+ 'gactionobservable.h',
+ 'gactionobserver.h',
+ 'shell-network-agent.h',
+]
+
+exclude_directories = [
+ 'calendar-server',
+ 'hotplug-sniffer',
+ 'st',
+ 'tray'
+]
+
+ifaces = [
+ ['org.gnome.Shell.Screenshot', 'org.gnome.Shell.Screenshot.xml'],
+ ['org.gnome.ShellSearchProvider', 'org.gnome.Shell.SearchProvider.xml'],
+ ['org.gnome.ShellSearchProvider2', 'org.gnome.Shell.SearchProvider2.xml']
+]
+foreach iface : ifaces
+ custom_target(iface[0] + ' docs',
+ input: '../../../data/dbus-interfaces/@0@.xml'.format(iface[0]),
+ output: 'doc-gen-' + iface[1],
+ command: [
+ 'gdbus-codegen',
+ '--interface-prefix=@0@.'.format(iface[0]),
+ '--generate-docbook', 'doc-gen',
+ '--output-directory', '@OUTDIR@',
+ '@INPUT@'
+ ],
+ build_by_default: true
+ )
+endforeach
+
+configure_file(
+ input: 'version.xml.in',
+ output: 'version.xml',
+ configuration: version_conf
+)
+
+gnome.gtkdoc('shell',
+ main_sgml: 'shell-docs.sgml',
+ src_dir: [
+ join_paths(meson.project_source_root(), 'src'),
+ join_paths(meson.project_build_root(), 'src')
+ ],
+ scan_args: [
+ '--ignore-headers=' + ' '.join(private_headers + exclude_directories)
+ ],
+ install: true
+)
diff --git a/docs/reference/shell/shell-docs.sgml b/docs/reference/shell/shell-docs.sgml
new file mode 100644
index 0000000..af2d543
--- /dev/null
+++ b/docs/reference/shell/shell-docs.sgml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
+[
+ <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index">
+ <bookinfo>
+ <title>Shell Reference Manual</title>
+ <releaseinfo>
+ for Shell &version;.
+ <!--The latest version of this documentation can be found on-line at
+ <ulink role="online-location" url="http://[SERVER]/shell/index.html">http://[SERVER]/shell/</ulink>.-->
+ </releaseinfo>
+ </bookinfo>
+
+ <chapter>
+ <title>Actors</title>
+ <xi:include href="xml/shell-stack.xml"/>
+ </chapter>
+ <chapter>
+ <title>Application tracking</title>
+ <xi:include href="xml/shell-app.xml"/>
+ <xi:include href="xml/shell-app-usage.xml"/>
+ <xi:include href="xml/shell-window-tracker.xml"/>
+ </chapter>
+ <chapter>
+ <title>Search</title>
+ <xi:include href="xml/shell-app-system.xml"/>
+ </chapter>
+ <chapter>
+ <title>Tray Icons</title>
+ <xi:include href="xml/shell-embedded-window.xml"/>
+ <xi:include href="xml/shell-gtk-embed.xml"/>
+ <xi:include href="xml/shell-tray-icon.xml"/>
+ <xi:include href="xml/shell-tray-manager.xml"/>
+ </chapter>
+ <chapter>
+ <title>Integration helpers and utilities</title>
+ <xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
+ <xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
+ <xi:include href="xml/shell-global.xml"/>
+ <xi:include href="xml/shell-action-modes.xml"/>
+ <xi:include href="xml/shell-wm.xml"/>
+ <xi:include href="xml/shell-util.xml"/>
+ <xi:include href="xml/shell-mount-operation.xml"/>
+ <xi:include href="xml/shell-polkit-authentication-agent.xml"/>
+ </chapter>
+ <!-- FIXME:
+ Not generated at the moment, find out whether to blame gtk-doc or meson
+ -->
+ <!--
+ <chapter id="object-tree">
+ <title>Object Hierarchy</title>
+ <xi:include href="xml/tree_index.sgml"/>
+ </chapter>
+ -->
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+ <index id="deprecated-api-index" role="deprecated">
+ <title>Index of deprecated API</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/docs/reference/shell/version.xml.in b/docs/reference/shell/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/docs/reference/shell/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/docs/reference/st/meson.build b/docs/reference/st/meson.build
new file mode 100644
index 0000000..0c4b420
--- /dev/null
+++ b/docs/reference/st/meson.build
@@ -0,0 +1,23 @@
+private_headers = [
+ 'st-private.h',
+ 'st-theme-node-private.h'
+]
+
+configure_file(
+ input: 'version.xml.in',
+ output: 'version.xml',
+ configuration: version_conf
+)
+
+gnome.gtkdoc('st',
+ main_sgml: 'st-docs.sgml',
+ src_dir: [
+ join_paths(meson.project_source_root(), 'src', 'st'),
+ join_paths(meson.project_build_root(), 'src', 'st')
+ ],
+ scan_args: [
+ '--ignore-headers=' + ' '.join(private_headers),
+ '--rebuild-sections'
+ ],
+ install: true
+)
diff --git a/docs/reference/st/st-docs.sgml b/docs/reference/st/st-docs.sgml
new file mode 100644
index 0000000..4e5fd99
--- /dev/null
+++ b/docs/reference/st/st-docs.sgml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
+[
+ <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index">
+ <bookinfo>
+ <title>St Reference Manual</title>
+ <releaseinfo>
+ for St &version;.
+ <!--The latest version of this documentation can be found on-line at
+ <ulink role="online-location" url="http://[SERVER]/st/index.html">http://[SERVER]/st/</ulink>.-->
+ </releaseinfo>
+ </bookinfo>
+
+ <part>
+ <title>API reference</title>
+ <chapter id="base">
+ <title>Abstract classes and Interfaces</title>
+ <xi:include href="xml/st-widget.xml"/>
+ <xi:include href="xml/st-widget-accessible.xml"/>
+ <xi:include href="xml/st-scrollable.xml"/>
+ </chapter>
+ <chapter id="widgets">
+ <title>Widgets</title>
+ <xi:include href="xml/st-button.xml"/>
+ <xi:include href="xml/st-drawing-area.xml"/>
+ <xi:include href="xml/st-entry.xml"/>
+ <xi:include href="xml/st-icon.xml"/>
+ <xi:include href="xml/st-label.xml"/>
+ </chapter>
+ <chapter id="containers">
+ <title>Containers</title>
+ <xi:include href="xml/st-bin.xml"/>
+ <xi:include href="xml/st-box-layout.xml"/>
+ <xi:include href="xml/st-scroll-view.xml"/>
+ </chapter>
+
+ <chapter id="styling">
+ <title>Styling</title>
+ <xi:include href="xml/st-theme.xml"/>
+ <xi:include href="xml/st-theme-context.xml"/>
+ <xi:include href="xml/st-theme-node.xml"/>
+ <xi:include href="xml/st-theme-node-transition.xml"/>
+ <xi:include href="xml/st-texture-cache.xml"/>
+ </chapter>
+ </part>
+ <!-- FIXME:
+ Not generated at the moment, find out whether to blame gtk-doc or meson
+ -->
+ <!--
+ <chapter id="object-tree">
+ <title>Object Hierarchy</title>
+ <xi:include href="xml/tree_index.sgml"/>
+ </chapter>
+ -->
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+ <index id="deprecated-api-index" role="deprecated">
+ <title>Index of deprecated API</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/docs/reference/st/version.xml.in b/docs/reference/st/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/docs/reference/st/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/gnome-shell.doap b/gnome-shell.doap
new file mode 100644
index 0000000..1339003
--- /dev/null
+++ b/gnome-shell.doap
@@ -0,0 +1,82 @@
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:gnome="http://api.gnome.org/doap-extensions#"
+ xmlns="http://usefulinc.com/ns/doap#">
+
+ <name xml:lang="en">GNOME Shell</name>
+ <shortdesc xml:lang="en">Next generation GNOME desktop shell</shortdesc>
+ <description>GNOME Shell provides core user interface functions for the GNOME 3
+desktop, like switching to windows and launching applications.
+GNOME Shell takes advantage of the capabilities of modern graphics
+hardware and introduces innovative user interface concepts to
+provide a visually attractive and easy to use experience.
+
+Tarball releases are provided largely for distributions to build
+packages. If you are interested in building GNOME Shell from source,
+we would recommend building from version control using the build
+script described at:
+
+ https://wiki.gnome.org/Projects/GnomeShell
+
+Not only will that give you the very latest version of this rapidly
+changing project, it will be much easier than get GNOME Shell and
+its dependencies to build from tarballs.</description>
+ <homepage rdf:resource="https://wiki.gnome.org/Projects/GnomeShell" />
+ <support-forum rdf:resource="https://discourse.gnome.org/tags/shell" />
+ <download-page rdf:resource="http://download.gnome.org/sources/gnome-shell/" />
+ <bug-database rdf:resource="https://gitlab.gnome.org/GNOME/gnome-shell/issues/" />
+
+ <category rdf:resource="http://api.gnome.org/doap-extensions#core" />
+ <programming-language>JavaScript</programming-language>
+ <programming-language>C</programming-language>
+
+ <author>
+ <foaf:Person>
+ <foaf:name>William Jon McCann</foaf:name>
+ <foaf:mbox rdf:resource="mailto:jmccann@redhat.com" />
+ <gnome:userid>mccann</gnome:userid>
+ </foaf:Person>
+ </author>
+ <author>
+ <foaf:Person>
+ <foaf:name>Owen Taylor</foaf:name>
+ <foaf:mbox rdf:resource="mailto:otaylor@redhat.com" />
+ <gnome:userid>otaylor</gnome:userid>
+ </foaf:Person>
+ </author>
+ <author>
+ <foaf:Person>
+ <foaf:name>Colin Walters</foaf:name>
+ <foaf:mbox rdf:resource="mailto:walters@verbum.org" />
+ <gnome:userid>walters</gnome:userid>
+ </foaf:Person>
+ </author>
+ <author>
+ <foaf:Person>
+ <foaf:name>Marina Zhurakhinskaya</foaf:name>
+ <foaf:mbox rdf:resource="mailto:marinaz@redhat.com" />
+ <gnome:userid>marinaz</gnome:userid>
+ </foaf:Person>
+ </author>
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Florian Müllner</foaf:name>
+ <foaf:mbox rdf:resource="mailto:fmuellner@gnome.org" />
+ <gnome:userid>fmuellner</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Georges Basile Stavracas Neto</foaf:name>
+ <foaf:mbox rdf:resource="mailto:gbsneto@gnome.org" />
+ <gnome:userid>gbsneto</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Marge Bot</foaf:name>
+ <gnome:userid>marge-bot</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+</Project>
diff --git a/js/dbusServices/dbus-service.in b/js/dbusServices/dbus-service.in
new file mode 100644
index 0000000..5241661
--- /dev/null
+++ b/js/dbusServices/dbus-service.in
@@ -0,0 +1,5 @@
+imports.package.start({
+ name: '@PACKAGE_NAME@',
+ prefix: '@prefix@',
+ libdir: '@libdir@',
+});
diff --git a/js/dbusServices/dbus-service.service.in b/js/dbusServices/dbus-service.service.in
new file mode 100644
index 0000000..3b0d09a
--- /dev/null
+++ b/js/dbusServices/dbus-service.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=@service@
+Exec=@gjs@ @pkgdatadir@/@service@
diff --git a/js/dbusServices/dbusService.js b/js/dbusServices/dbusService.js
new file mode 100644
index 0000000..8b6a0a1
--- /dev/null
+++ b/js/dbusServices/dbusService.js
@@ -0,0 +1,188 @@
+/* exported DBusService, ServiceImplementation */
+
+const { Gio, GLib } = imports.gi;
+
+const Signals = imports.signals;
+
+const IDLE_SHUTDOWN_TIME = 2; // s
+
+const { programArgs } = imports.system;
+
+var ServiceImplementation = class {
+ constructor(info, objectPath) {
+ this._objectPath = objectPath;
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(info, this);
+
+ this._injectTracking('return_dbus_error');
+ this._injectTracking('return_error_literal');
+ this._injectTracking('return_gerror');
+ this._injectTracking('return_value');
+ this._injectTracking('return_value_with_unix_fd_list');
+
+ this._senders = new Map();
+ this._holdCount = 0;
+
+ this._hasSignals = this._dbusImpl.get_info().signals.length > 0;
+ this._shutdownTimeoutId = 0;
+
+ // subclasses may override this to disable automatic shutdown
+ this._autoShutdown = true;
+
+ this._queueShutdownCheck();
+ }
+
+ // subclasses may override this to own additional names
+ register() {
+ }
+
+ export() {
+ this._dbusImpl.export(Gio.DBus.session, this._objectPath);
+ }
+
+ unexport() {
+ this._dbusImpl.unexport();
+ }
+
+ hold() {
+ this._holdCount++;
+ }
+
+ release() {
+ if (this._holdCount === 0) {
+ logError(new Error('Unmatched call to release()'));
+ return;
+ }
+
+ this._holdCount--;
+
+ if (this._holdCount === 0)
+ this._queueShutdownCheck();
+ }
+
+ /**
+ * _handleError:
+ * @param {Gio.DBusMethodInvocation}
+ * @param {Error}
+ *
+ * Complete @invocation with an appropriate error if @error is set;
+ * useful for implementing early returns from method implementations.
+ *
+ * @returns {bool} - true if @invocation was completed
+ */
+
+ _handleError(invocation, error) {
+ if (error === null)
+ return false;
+
+ if (error instanceof GLib.Error) {
+ invocation.return_gerror(error);
+ } else {
+ let name = error.name;
+ if (!name.includes('.')) // likely a normal JS error
+ name = `org.gnome.gjs.JSError.${name}`;
+ invocation.return_dbus_error(name, error.message);
+ }
+
+ return true;
+ }
+
+ _maybeShutdown() {
+ if (!this._autoShutdown)
+ return;
+
+ if (GLib.getenv('SHELL_DBUS_PERSIST'))
+ return;
+
+ if (this._holdCount > 0)
+ return;
+
+ this.emit('shutdown');
+ }
+
+ _queueShutdownCheck() {
+ if (this._shutdownTimeoutId)
+ GLib.source_remove(this._shutdownTimeoutId);
+
+ this._shutdownTimeoutId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT, IDLE_SHUTDOWN_TIME,
+ () => {
+ this._shutdownTimeoutId = 0;
+ this._maybeShutdown();
+
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _trackSender(sender) {
+ if (this._senders.has(sender))
+ return;
+
+ this.hold();
+ this._senders.set(sender,
+ this._dbusImpl.get_connection().watch_name(
+ sender,
+ Gio.BusNameWatcherFlags.NONE,
+ null,
+ () => this._untrackSender(sender)));
+ }
+
+ _untrackSender(sender) {
+ const id = this._senders.get(sender);
+
+ if (id)
+ this._dbusImpl.get_connection().unwatch_name(id);
+
+ if (this._senders.delete(sender))
+ this.release();
+ }
+
+ _injectTracking(methodName) {
+ const { prototype } = Gio.DBusMethodInvocation;
+ const origMethod = prototype[methodName];
+ const that = this;
+
+ prototype[methodName] = function (...args) {
+ origMethod.apply(this, args);
+
+ if (that._hasSignals)
+ that._trackSender(this.get_sender());
+
+ that._queueShutdownCheck();
+ };
+ }
+};
+Signals.addSignalMethods(ServiceImplementation.prototype);
+
+var DBusService = class {
+ constructor(name, service) {
+ this._name = name;
+ this._service = service;
+ this._loop = new GLib.MainLoop(null, false);
+
+ this._service.connect('shutdown', () => this._loop.quit());
+ }
+
+ run() {
+ // Bail out when not running under gnome-shell
+ Gio.DBus.watch_name(Gio.BusType.SESSION,
+ 'org.gnome.Shell',
+ Gio.BusNameWatcherFlags.NONE,
+ null,
+ () => this._loop.quit());
+
+ this._service.register();
+
+ let flags = Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT;
+ if (programArgs.includes('--replace'))
+ flags |= Gio.BusNameOwnerFlags.REPLACE;
+
+ Gio.DBus.own_name(Gio.BusType.SESSION,
+ this._name,
+ flags,
+ () => this._service.export(),
+ null,
+ () => this._loop.quit());
+
+ this._loop.run();
+ }
+};
diff --git a/js/dbusServices/extensions/css/application.css b/js/dbusServices/extensions/css/application.css
new file mode 100644
index 0000000..4ef4066
--- /dev/null
+++ b/js/dbusServices/extensions/css/application.css
@@ -0,0 +1,9 @@
+.error-page preferencespage { margin: 30px; }
+
+.expander { padding: 12px; }
+.expander.expanded { border: 0 solid @borders; border-bottom-width: 1px; }
+.expander-toolbar {
+ border: 0 solid @borders;
+ border-top-width: 1px;
+ padding: 3px;
+}
diff --git a/js/dbusServices/extensions/extensionPrefsDialog.js b/js/dbusServices/extensions/extensionPrefsDialog.js
new file mode 100644
index 0000000..7155c1a
--- /dev/null
+++ b/js/dbusServices/extensions/extensionPrefsDialog.js
@@ -0,0 +1,178 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ExtensionPrefsDialog */
+
+const { Adw, Gdk, Gio, GLib, GObject, Gtk } = imports.gi;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+
+var ExtensionPrefsDialog = GObject.registerClass({
+ GTypeName: 'ExtensionPrefsDialog',
+}, class ExtensionPrefsDialog extends Adw.PreferencesWindow {
+ _init(extension) {
+ super._init({
+ title: extension.metadata.name,
+ search_enabled: false,
+ });
+
+ try {
+ ExtensionUtils.installImporter(extension);
+
+ // give extension prefs access to their own extension object
+ ExtensionUtils.setCurrentExtension(extension);
+
+ const prefsModule = extension.imports.prefs;
+ prefsModule.init(extension.metadata);
+
+ if (prefsModule.fillPreferencesWindow) {
+ prefsModule.fillPreferencesWindow(this);
+
+ if (!this.visible_page)
+ throw new Error('Extension did not provide any UI');
+ } else {
+ const widget = prefsModule.buildPrefsWidget();
+ const page = this._wrapWidget(widget);
+ this.add(page);
+ }
+ } catch (e) {
+ this._showErrorPage(e);
+ logError(e, 'Failed to open preferences');
+ }
+ }
+
+ set titlebar(w) {
+ this.set_titlebar(w);
+ }
+
+ // eslint-disable-next-line camelcase
+ set_titlebar() {
+ // intercept fatal libadwaita error, show error page instead
+ GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this._showErrorPage(
+ new Error('set_titlebar() is not supported for Adw.Window'));
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _showErrorPage(e) {
+ while (this.visible_page)
+ this.remove(this.visible_page);
+
+ const extension = ExtensionUtils.getCurrentExtension();
+ this.add(new ExtensionPrefsErrorPage(extension, e));
+ }
+
+ _wrapWidget(widget) {
+ if (widget instanceof Adw.PreferencesPage)
+ return widget;
+
+ const page = new Adw.PreferencesPage();
+ if (widget instanceof Adw.PreferencesGroup) {
+ page.add(widget);
+ return page;
+ }
+
+ const group = new Adw.PreferencesGroup();
+ group.add(widget);
+ page.add(group);
+
+ return page;
+ }
+});
+
+const ExtensionPrefsErrorPage = GObject.registerClass({
+ GTypeName: 'ExtensionPrefsErrorPage',
+ Template: 'resource:///org/gnome/Shell/Extensions/ui/extension-error-page.ui',
+ InternalChildren: [
+ 'expander',
+ 'expanderArrow',
+ 'revealer',
+ 'errorView',
+ ],
+}, class ExtensionPrefsErrorPage extends Adw.PreferencesPage {
+ static _classInit(klass) {
+ super._classInit(klass);
+
+ klass.install_action('page.copy-error',
+ null,
+ self => {
+ const clipboard = self.get_display().get_clipboard();
+ clipboard.set(self._errorMarkdown);
+ });
+ klass.install_action('page.show-url',
+ null,
+ self => Gtk.show_uri(self.get_root(), self._url, Gdk.CURRENT_TIME));
+
+ return klass;
+ }
+
+ _init(extension, error) {
+ super._init();
+
+ this._addCustomStylesheet();
+
+ this._uuid = extension.uuid;
+ this._url = extension.metadata.url || '';
+
+ this.action_set_enabled('page.show-url', this._url !== '');
+
+ this._gesture = new Gtk.GestureClick({
+ button: 0,
+ exclusive: true,
+ });
+ this._expander.add_controller(this._gesture);
+
+ this._gesture.connect('released', (gesture, nPress) => {
+ if (nPress === 1)
+ this._revealer.reveal_child = !this._revealer.reveal_child;
+ });
+
+ this._revealer.connect('notify::reveal-child', () => {
+ this._expanderArrow.icon_name = this._revealer.reveal_child
+ ? 'pan-down-symbolic'
+ : 'pan-end-symbolic';
+ this._syncExpandedStyle();
+ });
+ this._revealer.connect('notify::child-revealed',
+ () => this._syncExpandedStyle());
+
+ this._errorView.buffer.text = `${error}\n\nStack trace:\n`;
+ // Indent stack trace.
+ this._errorView.buffer.text +=
+ error.stack.split('\n').map(line => ` ${line}`).join('\n');
+
+ // markdown for pasting in gitlab issues
+ let lines = [
+ `The settings of extension ${this._uuid} had an error:`,
+ '```',
+ `${error}`,
+ '```',
+ '',
+ 'Stack trace:',
+ '```',
+ error.stack.replace(/\n$/, ''), // stack without trailing newline
+ '```',
+ '',
+ ];
+ this._errorMarkdown = lines.join('\n');
+ }
+
+ _syncExpandedStyle() {
+ if (this._revealer.reveal_child)
+ this._expander.add_css_class('expanded');
+ else if (!this._revealer.child_revealed)
+ this._expander.remove_css_class('expanded');
+ }
+
+ _addCustomStylesheet() {
+ let provider = new Gtk.CssProvider();
+ let uri = 'resource:///org/gnome/Shell/Extensions/css/application.css';
+ try {
+ provider.load_from_file(Gio.File.new_for_uri(uri));
+ } catch (e) {
+ logError(e, 'Failed to add application style');
+ }
+ Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(),
+ provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+});
diff --git a/js/dbusServices/extensions/extensionsService.js b/js/dbusServices/extensions/extensionsService.js
new file mode 100644
index 0000000..d8234d2
--- /dev/null
+++ b/js/dbusServices/extensions/extensionsService.js
@@ -0,0 +1,161 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ExtensionsService */
+
+const { Gio, GLib, Shew } = imports.gi;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+
+const { loadInterfaceXML } = imports.misc.dbusUtils;
+const { ExtensionPrefsDialog } = imports.extensionPrefsDialog;
+const { ServiceImplementation } = imports.dbusService;
+
+const ExtensionsIface = loadInterfaceXML('org.gnome.Shell.Extensions');
+const ExtensionsProxy = Gio.DBusProxy.makeProxyWrapper(ExtensionsIface);
+
+var ExtensionsService = class extends ServiceImplementation {
+ constructor() {
+ super(ExtensionsIface, '/org/gnome/Shell/Extensions');
+
+ this._proxy = new ExtensionsProxy(Gio.DBus.session,
+ 'org.gnome.Shell', '/org/gnome/Shell');
+
+ this._proxy.connectSignal('ExtensionStateChanged',
+ (proxy, sender, params) => {
+ this._dbusImpl.emit_signal('ExtensionStateChanged',
+ new GLib.Variant('(sa{sv})', params));
+ });
+
+ this._proxy.connect('g-properties-changed', () => {
+ this._dbusImpl.emit_property_changed('UserExtensionsEnabled',
+ new GLib.Variant('b', this._proxy.UserExtensionsEnabled));
+ });
+ }
+
+ get ShellVersion() {
+ return this._proxy.ShellVersion;
+ }
+
+ get UserExtensionsEnabled() {
+ return this._proxy.UserExtensionsEnabled;
+ }
+
+ set UserExtensionsEnabled(enable) {
+ this._proxy.UserExtensionsEnabled = enable;
+ }
+
+ async ListExtensionsAsync(params, invocation) {
+ try {
+ const res = await this._proxy.ListExtensionsAsync(...params);
+ invocation.return_value(new GLib.Variant('(a{sa{sv}})', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async GetExtensionInfoAsync(params, invocation) {
+ try {
+ const res = await this._proxy.GetExtensionInfoAsync(...params);
+ invocation.return_value(new GLib.Variant('(a{sv})', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async GetExtensionErrorsAsync(params, invocation) {
+ try {
+ const res = await this._proxy.GetExtensionErrorsAsync(...params);
+ invocation.return_value(new GLib.Variant('(as)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async InstallRemoteExtensionAsync(params, invocation) {
+ try {
+ const res = await this._proxy.InstallRemoteExtensionAsync(...params);
+ invocation.return_value(new GLib.Variant('(s)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async UninstallExtensionAsync(params, invocation) {
+ try {
+ const res = await this._proxy.UninstallExtensionAsync(...params);
+ invocation.return_value(new GLib.Variant('(b)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async EnableExtensionAsync(params, invocation) {
+ try {
+ const res = await this._proxy.EnableExtensionAsync(...params);
+ invocation.return_value(new GLib.Variant('(b)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async DisableExtensionAsync(params, invocation) {
+ try {
+ const res = await this._proxy.DisableExtensionAsync(...params);
+ invocation.return_value(new GLib.Variant('(b)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ LaunchExtensionPrefsAsync([uuid], invocation) {
+ this.OpenExtensionPrefsAsync([uuid, '', {}], invocation);
+ }
+
+ async OpenExtensionPrefsAsync(params, invocation) {
+ const [uuid, parentWindow, options] = params;
+
+ try {
+ const [serialized] = await this._proxy.GetExtensionInfoAsync(uuid);
+
+ if (this._prefsDialog)
+ throw new Error('Already showing a prefs dialog');
+
+ const extension = ExtensionUtils.deserializeExtension(serialized);
+
+ this._prefsDialog = new ExtensionPrefsDialog(extension);
+ this._prefsDialog.connect('realize', () => {
+ let externalWindow = null;
+
+ if (parentWindow)
+ externalWindow = Shew.ExternalWindow.new_from_handle(parentWindow);
+
+ if (externalWindow)
+ externalWindow.set_parent_of(this._prefsDialog.get_surface());
+ });
+
+ if (options.modal)
+ this._prefsDialog.modal = options.modal.get_boolean();
+
+ this._prefsDialog.connect('close-request', () => {
+ delete this._prefsDialog;
+ this.release();
+ return false;
+ });
+ this.hold();
+
+ this._prefsDialog.show();
+
+ invocation.return_value(null);
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async CheckForUpdatesAsync(params, invocation) {
+ try {
+ await this._proxy.CheckForUpdatesAsync(...params);
+ invocation.return_value(null);
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+};
diff --git a/js/dbusServices/extensions/main.js b/js/dbusServices/extensions/main.js
new file mode 100644
index 0000000..306fe36
--- /dev/null
+++ b/js/dbusServices/extensions/main.js
@@ -0,0 +1,23 @@
+/* exported main */
+
+imports.gi.versions.Adw = '1';
+imports.gi.versions.Gdk = '4.0';
+imports.gi.versions.Gtk = '4.0';
+
+const { Adw, GObject } = imports.gi;
+const pkg = imports.package;
+
+const { DBusService } = imports.dbusService;
+const { ExtensionsService } = imports.extensionsService;
+
+function main() {
+ Adw.init();
+ pkg.initFormat();
+
+ GObject.gtypeNameBasedOnJSPath = true;
+
+ const service = new DBusService(
+ 'org.gnome.Shell.Extensions',
+ new ExtensionsService());
+ service.run();
+}
diff --git a/js/dbusServices/extensions/ui/extension-error-page.ui b/js/dbusServices/extensions/ui/extension-error-page.ui
new file mode 100644
index 0000000..5ce6a62
--- /dev/null
+++ b/js/dbusServices/extensions/ui/extension-error-page.ui
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="ExtensionPrefsErrorPage" parent="AdwPreferencesPage">
+ <style>
+ <class name="error-page"/>
+ </style>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Something’s gone wrong</property>
+ <style>
+ <class name="title-1"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">We’re very sorry, but there’s been a problem: the settings for this extension can’t be displayed. We recommend that you report the issue to the extension authors.</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="margin-top">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="expander">
+ <property name="spacing">6</property>
+ <style>
+ <class name="expander"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="expanderArrow">
+ <property name="icon-name">pan-end-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Technical Details</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkTextView" id="errorView">
+ <property name="monospace">True</property>
+ <property name="editable">False</property>
+ <property name="wrap-mode">word</property>
+ <property name="left-margin">12</property>
+ <property name="right-margin">12</property>
+ <property name="top-margin">12</property>
+ <property name="bottom-margin">12</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <style>
+ <class name="expander-toolbar"/>
+ </style>
+ <child>
+ <object class="GtkButton">
+ <property name="receives-default">True</property>
+ <property name="action-name">page.copy-error</property>
+ <property name="has-frame">False</property>
+ <property name="icon-name">edit-copy-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="homeButton">
+ <property name="visible"
+ bind-source="homeButton"
+ bind-property="sensitive"
+ bind-flags="sync-create"/>
+ <property name="hexpand">True</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">Homepage</property>
+ <property name="tooltip-text" translatable="yes">Visit extension homepage</property>
+ <property name="receives-default">True</property>
+ <property name="has-frame">False</property>
+ <property name="action-name">page.show-url</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/js/dbusServices/meson.build b/js/dbusServices/meson.build
new file mode 100644
index 0000000..48b7f89
--- /dev/null
+++ b/js/dbusServices/meson.build
@@ -0,0 +1,44 @@
+launcherconf = configuration_data()
+launcherconf.set('PACKAGE_NAME', meson.project_name())
+launcherconf.set('prefix', prefix)
+launcherconf.set('libdir', libdir)
+
+dbus_services = {
+ 'org.gnome.Shell.Extensions': 'extensions',
+ 'org.gnome.Shell.Notifications': 'notifications',
+ 'org.gnome.Shell.Screencast': 'screencast',
+ 'org.gnome.ScreenSaver': 'screensaver',
+}
+
+config_dir = '@0@/..'.format(meson.current_build_dir())
+
+foreach service, dir : dbus_services
+ configure_file(
+ input: 'dbus-service.in',
+ output: service,
+ configuration: launcherconf,
+ install_dir: pkgdatadir,
+ )
+
+ serviceconf = configuration_data()
+ serviceconf.set('service', service)
+ serviceconf.set('gjs', gjs.full_path())
+ serviceconf.set('pkgdatadir', pkgdatadir)
+
+ configure_file(
+ input: 'dbus-service.service.in',
+ output: service + '.service',
+ configuration: serviceconf,
+ install_dir: servicedir
+ )
+
+ gnome.compile_resources(
+ service + '.src',
+ service + '.src.gresource.xml',
+ dependencies: [config_js],
+ source_dir: ['.', '..', dir, config_dir],
+ gresource_bundle: true,
+ install: true,
+ install_dir: pkgdatadir
+ )
+endforeach
diff --git a/js/dbusServices/notifications/main.js b/js/dbusServices/notifications/main.js
new file mode 100644
index 0000000..7944cd1
--- /dev/null
+++ b/js/dbusServices/notifications/main.js
@@ -0,0 +1,11 @@
+/* exported main */
+
+const { DBusService } = imports.dbusService;
+const { NotificationDaemon } = imports.notificationDaemon;
+
+function main() {
+ const service = new DBusService(
+ 'org.gnome.Shell.Notifications',
+ new NotificationDaemon());
+ service.run();
+}
diff --git a/js/dbusServices/notifications/notificationDaemon.js b/js/dbusServices/notifications/notificationDaemon.js
new file mode 100644
index 0000000..b22f4ec
--- /dev/null
+++ b/js/dbusServices/notifications/notificationDaemon.js
@@ -0,0 +1,160 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported NotificationDaemon */
+
+const { Gio, GLib } = imports.gi;
+
+const { loadInterfaceXML } = imports.misc.dbusUtils;
+const { ServiceImplementation } = imports.dbusService;
+
+const NotificationsIface = loadInterfaceXML('org.freedesktop.Notifications');
+const NotificationsProxy = Gio.DBusProxy.makeProxyWrapper(NotificationsIface);
+
+Gio._promisify(Gio.DBusConnection.prototype, 'call');
+
+var NotificationDaemon = class extends ServiceImplementation {
+ constructor() {
+ super(NotificationsIface, '/org/freedesktop/Notifications');
+
+ this._autoShutdown = false;
+
+ this._activeNotifications = new Map();
+
+ this._proxy = new NotificationsProxy(Gio.DBus.session,
+ 'org.gnome.Shell',
+ '/org/freedesktop/Notifications',
+ (proxy, error) => {
+ if (error)
+ log(error.message);
+ });
+
+ this._proxy.connectSignal('ActionInvoked',
+ (proxy, sender, params) => {
+ const [id] = params;
+ this._emitSignal(
+ this._activeNotifications.get(id),
+ 'ActionInvoked',
+ new GLib.Variant('(us)', params));
+ });
+ this._proxy.connectSignal('NotificationClosed',
+ (proxy, sender, params) => {
+ const [id] = params;
+ this._emitSignal(
+ this._activeNotifications.get(id),
+ 'NotificationClosed',
+ new GLib.Variant('(uu)', params));
+ this._activeNotifications.delete(id);
+ });
+ }
+
+ _emitSignal(sender, signalName, params) {
+ if (!sender)
+ return;
+ this._dbusImpl.get_connection()?.emit_signal(
+ sender,
+ this._dbusImpl.get_object_path(),
+ 'org.freedesktop.Notifications',
+ signalName,
+ params);
+ }
+
+ _untrackSender(sender) {
+ super._untrackSender(sender);
+
+ this._activeNotifications.forEach((value, key) => {
+ if (value === sender)
+ this._activeNotifications.delete(key);
+ });
+ }
+
+ _checkNotificationId(invocation, id) {
+ if (id === 0)
+ return true;
+
+ if (!this._activeNotifications.has(id))
+ return true;
+
+ if (this._activeNotifications.get(id) === invocation.get_sender())
+ return true;
+
+ const error = new GLib.Error(Gio.DBusError,
+ Gio.DBusError.INVALID_ARGS, 'Invalid notification ID');
+ this._handleError(invocation, error);
+ return false;
+ }
+
+ register() {
+ Gio.DBus.session.own_name(
+ 'org.freedesktop.Notifications',
+ Gio.BusNameOwnerFlags.REPLACE,
+ null, null);
+ }
+
+ async NotifyAsync(params, invocation) {
+ const sender = invocation.get_sender();
+ const pid = await this._getSenderPid(sender);
+ const replaceId = params[1];
+ const hints = params[6];
+
+ if (!this._checkNotificationId(invocation, replaceId))
+ return;
+
+ params[6] = {
+ ...hints,
+ 'sender-pid': new GLib.Variant('u', pid),
+ };
+
+ try {
+ const [id] = await this._proxy.NotifyAsync(...params);
+ this._activeNotifications.set(id, sender);
+ invocation.return_value(new GLib.Variant('(u)', [id]));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async CloseNotificationAsync(params, invocation) {
+ const [id] = params;
+ if (!this._checkNotificationId(invocation, id))
+ return;
+
+ try {
+ await this._proxy.CloseNotificationAsync(...params);
+ invocation.return_value(null);
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async GetCapabilitiesAsync(params, invocation) {
+ try {
+ const res = await this._proxy.GetCapabilitiesAsync(...params);
+ invocation.return_value(new GLib.Variant('(as)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async GetServerInformationAsync(params, invocation) {
+ try {
+ const res = await this._proxy.GetServerInformationAsync(...params);
+ invocation.return_value(new GLib.Variant('(ssss)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async _getSenderPid(sender) {
+ const res = await Gio.DBus.session.call(
+ 'org.freedesktop.DBus',
+ '/',
+ 'org.freedesktop.DBus',
+ 'GetConnectionUnixProcessID',
+ new GLib.Variant('(s)', [sender]),
+ new GLib.VariantType('(u)'),
+ Gio.DBusCallFlags.NONE,
+ -1,
+ null);
+ const [pid] = res.deepUnpack();
+ return pid;
+ }
+};
diff --git a/js/dbusServices/org.gnome.ScreenSaver.src.gresource.xml b/js/dbusServices/org.gnome.ScreenSaver.src.gresource.xml
new file mode 100644
index 0000000..d77f72a
--- /dev/null
+++ b/js/dbusServices/org.gnome.ScreenSaver.src.gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/ScreenSaver/js">
+ <file>main.js</file>
+ <file>screenSaverService.js</file>
+ <file>dbusService.js</file>
+
+ <file>misc/config.js</file>
+ <file>misc/dbusUtils.js</file>
+ </gresource>
+</gresources>
diff --git a/js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml b/js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml
new file mode 100644
index 0000000..3ab92a2
--- /dev/null
+++ b/js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/Shell/Extensions/js">
+ <file>main.js</file>
+ <file>extensionsService.js</file>
+ <file>extensionPrefsDialog.js</file>
+ <file>dbusService.js</file>
+
+ <file>misc/config.js</file>
+ <file>misc/extensionUtils.js</file>
+ <file>misc/dbusUtils.js</file>
+ <file>misc/params.js</file>
+ </gresource>
+
+ <gresource prefix="/org/gnome/Shell/Extensions">
+ <file>css/application.css</file>
+ <file>ui/extension-error-page.ui</file>
+ </gresource>
+</gresources>
diff --git a/js/dbusServices/org.gnome.Shell.Notifications.src.gresource.xml b/js/dbusServices/org.gnome.Shell.Notifications.src.gresource.xml
new file mode 100644
index 0000000..4e039ba
--- /dev/null
+++ b/js/dbusServices/org.gnome.Shell.Notifications.src.gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/Shell/Notifications/js">
+ <file>main.js</file>
+ <file>notificationDaemon.js</file>
+ <file>dbusService.js</file>
+
+ <file>misc/config.js</file>
+ <file>misc/dbusUtils.js</file>
+ </gresource>
+</gresources>
diff --git a/js/dbusServices/org.gnome.Shell.Screencast.src.gresource.xml b/js/dbusServices/org.gnome.Shell.Screencast.src.gresource.xml
new file mode 100644
index 0000000..292f0f1
--- /dev/null
+++ b/js/dbusServices/org.gnome.Shell.Screencast.src.gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/Shell/Screencast/js">
+ <file>main.js</file>
+ <file>screencastService.js</file>
+ <file>dbusService.js</file>
+
+ <file>misc/config.js</file>
+ <file>misc/dbusUtils.js</file>
+ </gresource>
+</gresources>
diff --git a/js/dbusServices/screencast/main.js b/js/dbusServices/screencast/main.js
new file mode 100644
index 0000000..7c71971
--- /dev/null
+++ b/js/dbusServices/screencast/main.js
@@ -0,0 +1,14 @@
+/* exported main */
+
+const {DBusService} = imports.dbusService;
+
+function main() {
+ const {ScreencastService} = imports.screencastService;
+ if (!ScreencastService.canScreencast())
+ return;
+
+ const service = new DBusService(
+ 'org.gnome.Shell.Screencast',
+ new ScreencastService());
+ service.run();
+}
diff --git a/js/dbusServices/screencast/screencastService.js b/js/dbusServices/screencast/screencastService.js
new file mode 100644
index 0000000..eb3dc88
--- /dev/null
+++ b/js/dbusServices/screencast/screencastService.js
@@ -0,0 +1,498 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ScreencastService */
+
+imports.gi.versions.Gst = '1.0';
+imports.gi.versions.Gtk = '4.0';
+
+const { Gio, GLib, Gst, Gtk } = imports.gi;
+
+const { loadInterfaceXML, loadSubInterfaceXML } = imports.misc.dbusUtils;
+const { ServiceImplementation } = imports.dbusService;
+
+const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
+
+const IntrospectIface = loadInterfaceXML('org.gnome.Shell.Introspect');
+const IntrospectProxy = Gio.DBusProxy.makeProxyWrapper(IntrospectIface);
+
+const ScreenCastIface = loadSubInterfaceXML(
+ 'org.gnome.Mutter.ScreenCast', 'org.gnome.Mutter.ScreenCast');
+const ScreenCastSessionIface = loadSubInterfaceXML(
+ 'org.gnome.Mutter.ScreenCast.Session', 'org.gnome.Mutter.ScreenCast');
+const ScreenCastStreamIface = loadSubInterfaceXML(
+ 'org.gnome.Mutter.ScreenCast.Stream', 'org.gnome.Mutter.ScreenCast');
+const ScreenCastProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastIface);
+const ScreenCastSessionProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastSessionIface);
+const ScreenCastStreamProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastStreamIface);
+
+const DEFAULT_PIPELINE = 'videoconvert chroma-mode=GST_VIDEO_CHROMA_MODE_NONE dither=GST_VIDEO_DITHER_NONE matrix-mode=GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY n-threads=%T ! queue ! vp8enc cpu-used=16 max-quantizer=17 deadline=1 keyframe-mode=disabled threads=%T static-threshold=1000 buffer-size=20000 ! queue ! webmmux';
+const DEFAULT_FRAMERATE = 30;
+const DEFAULT_DRAW_CURSOR = true;
+
+const PipelineState = {
+ INIT: 0,
+ PLAYING: 1,
+ FLUSHING: 2,
+ STOPPED: 3,
+};
+
+const SessionState = {
+ INIT: 0,
+ ACTIVE: 1,
+ STOPPED: 2,
+};
+
+var Recorder = class {
+ constructor(sessionPath, x, y, width, height, filePath, options,
+ invocation,
+ onErrorCallback) {
+ this._startInvocation = invocation;
+ this._dbusConnection = invocation.get_connection();
+ this._onErrorCallback = onErrorCallback;
+ this._stopInvocation = null;
+
+ this._x = x;
+ this._y = y;
+ this._width = width;
+ this._height = height;
+ this._filePath = filePath;
+
+ try {
+ const dir = Gio.File.new_for_path(filePath).get_parent();
+ dir.make_directory_with_parents(null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+ throw e;
+ }
+
+ this._pipelineString = DEFAULT_PIPELINE;
+ this._framerate = DEFAULT_FRAMERATE;
+ this._drawCursor = DEFAULT_DRAW_CURSOR;
+
+ this._applyOptions(options);
+ this._watchSender(invocation.get_sender());
+
+ this._initSession(sessionPath);
+ }
+
+ _applyOptions(options) {
+ for (const option in options)
+ options[option] = options[option].deepUnpack();
+
+ if (options['pipeline'] !== undefined)
+ this._pipelineString = options['pipeline'];
+ if (options['framerate'] !== undefined)
+ this._framerate = options['framerate'];
+ if ('draw-cursor' in options)
+ this._drawCursor = options['draw-cursor'];
+ }
+
+ _addRecentItem() {
+ const file = Gio.File.new_for_path(this._filePath);
+ Gtk.RecentManager.get_default().add_item(file.get_uri());
+ }
+
+ _watchSender(sender) {
+ this._nameWatchId = this._dbusConnection.watch_name(
+ sender,
+ Gio.BusNameWatcherFlags.NONE,
+ null,
+ this._senderVanished.bind(this));
+ }
+
+ _unwatchSender() {
+ if (this._nameWatchId !== 0) {
+ this._dbusConnection.unwatch_name(this._nameWatchId);
+ this._nameWatchId = 0;
+ }
+ }
+
+ _senderVanished() {
+ this._unwatchSender();
+
+ this.stopRecording(null);
+ }
+
+ _notifyStopped() {
+ this._unwatchSender();
+ if (this._onStartedCallback)
+ this._onStartedCallback(this, false);
+ else if (this._onStoppedCallback)
+ this._onStoppedCallback(this);
+ else
+ this._onErrorCallback(this);
+ }
+
+ _onSessionClosed() {
+ switch (this._pipelineState) {
+ case PipelineState.STOPPED:
+ break;
+ default:
+ this._pipeline.set_state(Gst.State.NULL);
+ log(`Unexpected pipeline state: ${this._pipelineState}`);
+ break;
+ }
+ this._notifyStopped();
+ }
+
+ _initSession(sessionPath) {
+ this._sessionProxy = new ScreenCastSessionProxy(Gio.DBus.session,
+ 'org.gnome.Mutter.ScreenCast',
+ sessionPath);
+ this._sessionProxy.connectSignal('Closed', this._onSessionClosed.bind(this));
+ }
+
+ _startPipeline(nodeId) {
+ if (!this._ensurePipeline(nodeId))
+ return;
+
+ const bus = this._pipeline.get_bus();
+ bus.add_watch(bus, this._onBusMessage.bind(this));
+
+ this._pipeline.set_state(Gst.State.PLAYING);
+ this._pipelineState = PipelineState.PLAYING;
+
+ this._onStartedCallback(this, true);
+ this._onStartedCallback = null;
+ }
+
+ startRecording(onStartedCallback) {
+ this._onStartedCallback = onStartedCallback;
+
+ const [streamPath] = this._sessionProxy.RecordAreaSync(
+ this._x, this._y,
+ this._width, this._height,
+ {
+ 'is-recording': GLib.Variant.new('b', true),
+ 'cursor-mode': GLib.Variant.new('u', this._drawCursor ? 1 : 0),
+ });
+
+ this._streamProxy = new ScreenCastStreamProxy(Gio.DBus.session,
+ 'org.gnome.ScreenCast.Stream',
+ streamPath);
+
+ this._streamProxy.connectSignal('PipeWireStreamAdded',
+ (proxy, sender, params) => {
+ const [nodeId] = params;
+ this._startPipeline(nodeId);
+ });
+ this._sessionProxy.StartSync();
+ this._sessionState = SessionState.ACTIVE;
+ }
+
+ stopRecording(onStoppedCallback) {
+ this._pipelineState = PipelineState.FLUSHING;
+ this._onStoppedCallback = onStoppedCallback;
+ this._pipeline.send_event(Gst.Event.new_eos());
+ }
+
+ _stopSession() {
+ this._sessionProxy.StopSync();
+ this._sessionState = SessionState.STOPPED;
+ }
+
+ _onBusMessage(bus, message, _) {
+ switch (message.type) {
+ case Gst.MessageType.EOS:
+ this._pipeline.set_state(Gst.State.NULL);
+ this._addRecentItem();
+
+ switch (this._pipelineState) {
+ case PipelineState.FLUSHING:
+ this._pipelineState = PipelineState.STOPPED;
+ break;
+ default:
+ break;
+ }
+
+ switch (this._sessionState) {
+ case SessionState.ACTIVE:
+ this._stopSession();
+ break;
+ case SessionState.STOPPED:
+ this._notifyStopped();
+ break;
+ default:
+ break;
+ }
+
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ _substituteThreadCount(pipelineDescr) {
+ const numProcessors = GLib.get_num_processors();
+ const numThreads = Math.min(Math.max(1, numProcessors), 64);
+ return pipelineDescr.replaceAll('%T', numThreads);
+ }
+
+ _ensurePipeline(nodeId) {
+ const framerate = this._framerate;
+ const needsCopy =
+ Gst.Registry.get().check_feature_version('pipewiresrc', 0, 3, 57) &&
+ !Gst.Registry.get().check_feature_version('videoconvert', 1, 20, 4);
+
+ let fullPipeline = `
+ pipewiresrc path=${nodeId}
+ always-copy=${needsCopy}
+ do-timestamp=true
+ keepalive-time=1000
+ resend-last=true !
+ video/x-raw,max-framerate=${framerate}/1 !
+ ${this._pipelineString} !
+ filesink location="${this._filePath}"`;
+ fullPipeline = this._substituteThreadCount(fullPipeline);
+
+ try {
+ this._pipeline = Gst.parse_launch_full(fullPipeline,
+ null,
+ Gst.ParseFlags.FATAL_ERRORS);
+ } catch (e) {
+ log(`Failed to create pipeline: ${e}`);
+ this._notifyStopped();
+ }
+ return !!this._pipeline;
+ }
+};
+
+var ScreencastService = class extends ServiceImplementation {
+ static canScreencast() {
+ const elements = [
+ 'pipewiresrc',
+ 'filesink',
+ ...DEFAULT_PIPELINE.split('!').map(e => e.trim().split(' ').at(0)),
+ ];
+ return Gst.init_check(null) &&
+ elements.every(e => Gst.ElementFactory.find(e) != null);
+ }
+
+ constructor() {
+ super(ScreencastIface, '/org/gnome/Shell/Screencast');
+
+ this.hold(); // gstreamer initializing can take a bit
+ this._canScreencast = ScreencastService.canScreencast();
+
+ Gst.init(null);
+ Gtk.init();
+
+ this.release();
+
+ this._recorders = new Map();
+ this._senders = new Map();
+
+ this._lockdownSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.lockdown',
+ });
+
+ this._proxy = new ScreenCastProxy(Gio.DBus.session,
+ 'org.gnome.Mutter.ScreenCast',
+ '/org/gnome/Mutter/ScreenCast');
+
+ this._introspectProxy = new IntrospectProxy(Gio.DBus.session,
+ 'org.gnome.Shell.Introspect',
+ '/org/gnome/Shell/Introspect');
+ }
+
+ get ScreencastSupported() {
+ return this._canScreencast;
+ }
+
+ _removeRecorder(sender) {
+ this._recorders.delete(sender);
+ if (this._recorders.size === 0)
+ this.release();
+ }
+
+ _addRecorder(sender, recorder) {
+ this._recorders.set(sender, recorder);
+ if (this._recorders.size === 1)
+ this.hold();
+ }
+
+ _getAbsolutePath(filename) {
+ if (GLib.path_is_absolute(filename))
+ return filename;
+
+ const videoDir =
+ GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS) ||
+ GLib.get_home_dir();
+
+ return GLib.build_filenamev([videoDir, filename]);
+ }
+
+ _generateFilePath(template) {
+ let filename = '';
+ let escape = false;
+
+ [...template].forEach(c => {
+ if (escape) {
+ switch (c) {
+ case '%':
+ filename += '%';
+ break;
+ case 'd': {
+ const datetime = GLib.DateTime.new_now_local();
+ const datestr = datetime.format('%Y-%m-%d');
+
+ filename += datestr;
+ break;
+ }
+
+ case 't': {
+ const datetime = GLib.DateTime.new_now_local();
+ const datestr = datetime.format('%H-%M-%S');
+
+ filename += datestr;
+ break;
+ }
+
+ default:
+ log(`Warning: Unknown escape ${c}`);
+ }
+
+ escape = false;
+ } else if (c === '%') {
+ escape = true;
+ } else {
+ filename += c;
+ }
+ });
+
+ if (escape)
+ filename += '%';
+
+ return this._getAbsolutePath(filename);
+ }
+
+ ScreencastAsync(params, invocation) {
+ let returnValue = [false, ''];
+
+ if (this._lockdownSettings.get_boolean('disable-save-to-disk')) {
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ return;
+ }
+
+ const sender = invocation.get_sender();
+
+ if (this._recorders.get(sender)) {
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ return;
+ }
+
+ const [sessionPath] = this._proxy.CreateSessionSync({});
+
+ const [fileTemplate, options] = params;
+ const [screenWidth, screenHeight] = this._introspectProxy.ScreenSize;
+ const filePath = this._generateFilePath(fileTemplate);
+
+ let recorder;
+
+ try {
+ recorder = new Recorder(
+ sessionPath,
+ 0, 0,
+ screenWidth, screenHeight,
+ filePath,
+ options,
+ invocation,
+ _recorder => this._removeRecorder(sender));
+ } catch (error) {
+ log(`Failed to create recorder: ${error.message}`);
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ return;
+ }
+
+ this._addRecorder(sender, recorder);
+
+ try {
+ recorder.startRecording(
+ (_, result) => {
+ if (result) {
+ returnValue = [true, filePath];
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ } else {
+ this._removeRecorder(sender);
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ }
+ });
+ } catch (error) {
+ log(`Failed to start recorder: ${error.message}`);
+ this._removeRecorder(sender);
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ }
+ }
+
+ ScreencastAreaAsync(params, invocation) {
+ let returnValue = [false, ''];
+
+ if (this._lockdownSettings.get_boolean('disable-save-to-disk')) {
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ return;
+ }
+
+ const sender = invocation.get_sender();
+
+ if (this._recorders.get(sender)) {
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ return;
+ }
+
+ const [sessionPath] = this._proxy.CreateSessionSync({});
+
+ const [x, y, width, height, fileTemplate, options] = params;
+ const filePath = this._generateFilePath(fileTemplate);
+
+ let recorder;
+
+ try {
+ recorder = new Recorder(
+ sessionPath,
+ x, y,
+ width, height,
+ filePath,
+ options,
+ invocation,
+ _recorder => this._removeRecorder(sender));
+ } catch (error) {
+ log(`Failed to create recorder: ${error.message}`);
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ return;
+ }
+
+ this._addRecorder(sender, recorder);
+
+ try {
+ recorder.startRecording(
+ (_, result) => {
+ if (result) {
+ returnValue = [true, filePath];
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ } else {
+ this._removeRecorder(sender);
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ }
+ });
+ } catch (error) {
+ log(`Failed to start recorder: ${error.message}`);
+ this._removeRecorder(sender);
+ invocation.return_value(GLib.Variant.new('(bs)', returnValue));
+ }
+ }
+
+ StopScreencastAsync(params, invocation) {
+ const sender = invocation.get_sender();
+
+ const recorder = this._recorders.get(sender);
+ if (!recorder) {
+ invocation.return_value(GLib.Variant.new('(b)', [false]));
+ return;
+ }
+
+ recorder.stopRecording(() => {
+ this._removeRecorder(sender);
+ invocation.return_value(GLib.Variant.new('(b)', [true]));
+ });
+ }
+};
diff --git a/js/dbusServices/screensaver/main.js b/js/dbusServices/screensaver/main.js
new file mode 100644
index 0000000..2a08d14
--- /dev/null
+++ b/js/dbusServices/screensaver/main.js
@@ -0,0 +1,11 @@
+/* exported main */
+
+const { DBusService } = imports.dbusService;
+const { ScreenSaverService } = imports.screenSaverService;
+
+function main() {
+ const service = new DBusService(
+ 'org.gnome.ScreenSaver',
+ new ScreenSaverService());
+ service.run();
+}
diff --git a/js/dbusServices/screensaver/screenSaverService.js b/js/dbusServices/screensaver/screenSaverService.js
new file mode 100644
index 0000000..2c1546e
--- /dev/null
+++ b/js/dbusServices/screensaver/screenSaverService.js
@@ -0,0 +1,70 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ScreenSaverService */
+
+const { Gio, GLib } = imports.gi;
+
+const { loadInterfaceXML } = imports.misc.dbusUtils;
+const { ServiceImplementation } = imports.dbusService;
+
+const ScreenSaverIface = loadInterfaceXML('org.gnome.ScreenSaver');
+const ScreenSaverProxy = Gio.DBusProxy.makeProxyWrapper(ScreenSaverIface);
+
+var ScreenSaverService = class extends ServiceImplementation {
+ constructor() {
+ super(ScreenSaverIface, '/org/gnome/ScreenSaver');
+
+ this._autoShutdown = false;
+
+ this._proxy = new ScreenSaverProxy(Gio.DBus.session,
+ 'org.gnome.Shell.ScreenShield',
+ '/org/gnome/ScreenSaver',
+ (proxy, error) => {
+ if (error)
+ log(error.message);
+ });
+
+ this._proxy.connectSignal('ActiveChanged',
+ (proxy, sender, params) => {
+ this._dbusImpl.emit_signal('ActiveChanged',
+ new GLib.Variant('(b)', params));
+ });
+ this._proxy.connectSignal('WakeUpScreen',
+ () => this._dbusImpl.emit_signal('WakeUpScreen', null));
+ }
+
+ async LockAsync(params, invocation) {
+ try {
+ await this._proxy.LockAsync(...params);
+ invocation.return_value(null);
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async GetActiveAsync(params, invocation) {
+ try {
+ const res = await this._proxy.GetActiveAsync(...params);
+ invocation.return_value(new GLib.Variant('(b)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async SetActiveAsync(params, invocation) {
+ try {
+ await this._proxy.SetActiveAsync(...params);
+ invocation.return_value(null);
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+
+ async GetActiveTimeAsync(params, invocation) {
+ try {
+ const res = await this._proxy.GetActiveTimeAsync(...params);
+ invocation.return_value(new GLib.Variant('(u)', res));
+ } catch (error) {
+ this._handleError(invocation, error);
+ }
+ }
+};
diff --git a/js/gdm/authList.js b/js/gdm/authList.js
new file mode 100644
index 0000000..fb223a9
--- /dev/null
+++ b/js/gdm/authList.js
@@ -0,0 +1,176 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/*
+ * Copyright 2017 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+/* exported AuthList */
+
+const { Clutter, GObject, Meta, St } = imports.gi;
+
+const SCROLL_ANIMATION_TIME = 500;
+
+const AuthListItem = GObject.registerClass({
+ Signals: { 'activate': {} },
+}, class AuthListItem extends St.Button {
+ _init(key, text) {
+ this.key = key;
+ const label = new St.Label({
+ text,
+ style_class: 'login-dialog-auth-list-label',
+ y_align: Clutter.ActorAlign.CENTER,
+ x_expand: false,
+ });
+
+ super._init({
+ style_class: 'login-dialog-auth-list-item',
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ can_focus: true,
+ child: label,
+ reactive: true,
+ });
+
+ this.connect('key-focus-in',
+ () => this._setSelected(true));
+ this.connect('key-focus-out',
+ () => this._setSelected(false));
+ this.connect('notify::hover',
+ () => this._setSelected(this.hover));
+
+ this.connect('clicked', this._onClicked.bind(this));
+ }
+
+ _onClicked() {
+ this.emit('activate');
+ }
+
+ _setSelected(selected) {
+ if (selected) {
+ this.add_style_pseudo_class('selected');
+ this.grab_key_focus();
+ } else {
+ this.remove_style_pseudo_class('selected');
+ }
+ }
+});
+
+var AuthList = GObject.registerClass({
+ Signals: {
+ 'activate': { param_types: [GObject.TYPE_STRING] },
+ 'item-added': { param_types: [AuthListItem.$gtype] },
+ },
+}, class AuthList extends St.BoxLayout {
+ _init() {
+ super._init({
+ vertical: true,
+ style_class: 'login-dialog-auth-list-layout',
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this.label = new St.Label({ style_class: 'login-dialog-auth-list-title' });
+ this.add_child(this.label);
+
+ this._scrollView = new St.ScrollView({
+ style_class: 'login-dialog-auth-list-view',
+ });
+ this._scrollView.set_policy(
+ St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
+ this.add_child(this._scrollView);
+
+ this._box = new St.BoxLayout({
+ vertical: true,
+ style_class: 'login-dialog-auth-list',
+ pseudo_class: 'expanded',
+ });
+
+ this._scrollView.add_actor(this._box);
+ this._items = new Map();
+
+ this.connect('key-focus-in', this._moveFocusToItems.bind(this));
+ }
+
+ _moveFocusToItems() {
+ let hasItems = this.numItems > 0;
+
+ if (!hasItems)
+ return;
+
+ if (global.stage.get_key_focus() !== this)
+ return;
+
+ let focusSet = this.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ if (!focusSet) {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._moveFocusToItems();
+ return false;
+ });
+ }
+ }
+
+ _onItemActivated(activatedItem) {
+ this.emit('activate', activatedItem.key);
+ }
+
+ scrollToItem(item) {
+ let box = item.get_allocation_box();
+
+ let adjustment = this._scrollView.get_vscroll_bar().get_adjustment();
+
+ let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
+ adjustment.ease(value, {
+ duration: SCROLL_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ addItem(key, text) {
+ this.removeItem(key);
+
+ let item = new AuthListItem(key, text);
+ this._box.add(item);
+
+ this._items.set(key, item);
+
+ item.connect('activate', this._onItemActivated.bind(this));
+
+ // Try to keep the focused item front-and-center
+ item.connect('key-focus-in', () => this.scrollToItem(item));
+
+ this._moveFocusToItems();
+
+ this.emit('item-added', item);
+ }
+
+ removeItem(key) {
+ if (!this._items.has(key))
+ return;
+
+ let item = this._items.get(key);
+
+ item.destroy();
+
+ this._items.delete(key);
+ }
+
+ get numItems() {
+ return this._items.size;
+ }
+
+ clear() {
+ this.label.text = '';
+ this._box.destroy_all_children();
+ this._items.clear();
+ }
+});
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
new file mode 100644
index 0000000..14d09a1
--- /dev/null
+++ b/js/gdm/authPrompt.js
@@ -0,0 +1,707 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported AuthPrompt */
+
+const { Clutter, GLib, GObject, Pango, Shell, St } = imports.gi;
+
+const Animation = imports.ui.animation;
+const AuthList = imports.gdm.authList;
+const Batch = imports.gdm.batch;
+const GdmUtil = imports.gdm.util;
+const OVirt = imports.gdm.oVirt;
+const Vmware = imports.gdm.vmware;
+const Params = imports.misc.params;
+const ShellEntry = imports.ui.shellEntry;
+const UserWidget = imports.ui.userWidget;
+const Util = imports.misc.util;
+
+var DEFAULT_BUTTON_WELL_ICON_SIZE = 16;
+var DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1000;
+var DEFAULT_BUTTON_WELL_ANIMATION_TIME = 300;
+
+var MESSAGE_FADE_OUT_ANIMATION_TIME = 500;
+
+var AuthPromptMode = {
+ UNLOCK_ONLY: 0,
+ UNLOCK_OR_LOG_IN: 1,
+};
+
+var AuthPromptStatus = {
+ NOT_VERIFYING: 0,
+ VERIFYING: 1,
+ VERIFICATION_FAILED: 2,
+ VERIFICATION_SUCCEEDED: 3,
+ VERIFICATION_CANCELLED: 4,
+ VERIFICATION_IN_PROGRESS: 5,
+};
+
+var BeginRequestType = {
+ PROVIDE_USERNAME: 0,
+ DONT_PROVIDE_USERNAME: 1,
+ REUSE_USERNAME: 2,
+};
+
+var AuthPrompt = GObject.registerClass({
+ Signals: {
+ 'cancelled': {},
+ 'failed': {},
+ 'next': {},
+ 'prompted': {},
+ 'reset': { param_types: [GObject.TYPE_UINT] },
+ },
+}, class AuthPrompt extends St.BoxLayout {
+ _init(gdmClient, mode) {
+ super._init({
+ style_class: 'login-dialog-prompt-layout',
+ vertical: true,
+ x_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ reactive: true,
+ });
+
+ this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
+
+ this._gdmClient = gdmClient;
+ this._mode = mode;
+ this._defaultButtonWellActor = null;
+ this._cancelledRetries = 0;
+
+ let reauthenticationOnly;
+ if (this._mode == AuthPromptMode.UNLOCK_ONLY)
+ reauthenticationOnly = true;
+ else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
+ reauthenticationOnly = false;
+
+ this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly });
+
+ this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this));
+ this._userVerifier.connect('show-message', this._onShowMessage.bind(this));
+ this._userVerifier.connect('show-choice-list', this._onShowChoiceList.bind(this));
+ this._userVerifier.connect('verification-failed', this._onVerificationFailed.bind(this));
+ this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this));
+ this._userVerifier.connect('reset', this._onReset.bind(this));
+ this._userVerifier.connect('smartcard-status-changed', this._onSmartcardStatusChanged.bind(this));
+ this._userVerifier.connect('credential-manager-authenticated', this._onCredentialManagerAuthenticated.bind(this));
+ this.smartcardDetected = this._userVerifier.smartcardDetected;
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._userWell = new St.Bin({
+ x_expand: true,
+ y_expand: true,
+ });
+ this.add_child(this._userWell);
+
+ this._hasCancelButton = this._mode === AuthPromptMode.UNLOCK_OR_LOG_IN;
+
+ this._initInputRow();
+
+ let capsLockPlaceholder = new St.Label();
+ this.add_child(capsLockPlaceholder);
+
+ this._capsLockWarningLabel = new ShellEntry.CapsLockWarning({
+ x_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this._capsLockWarningLabel);
+
+ this._capsLockWarningLabel.bind_property('visible',
+ capsLockPlaceholder, 'visible',
+ GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
+
+ this._message = new St.Label({
+ opacity: 0,
+ styleClass: 'login-dialog-message',
+ y_expand: true,
+ x_expand: true,
+ y_align: Clutter.ActorAlign.START,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this._message.clutter_text.line_wrap = true;
+ this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this.add_child(this._message);
+ }
+
+ _onDestroy() {
+ this._inactiveEntry.destroy();
+ this._inactiveEntry = null;
+ this._userVerifier.destroy();
+ this._userVerifier = null;
+ }
+
+ vfunc_key_press_event(keyPressEvent) {
+ if (keyPressEvent.keyval == Clutter.KEY_Escape)
+ this.cancel();
+ return super.vfunc_key_press_event(keyPressEvent);
+ }
+
+ _initInputRow() {
+ this._mainBox = new St.BoxLayout({
+ style_class: 'login-dialog-button-box',
+ vertical: false,
+ });
+ this.add_child(this._mainBox);
+
+ this.cancelButton = new St.Button({
+ style_class: 'modal-dialog-button button cancel-button',
+ accessible_name: _('Cancel'),
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ reactive: this._hasCancelButton,
+ can_focus: this._hasCancelButton,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.CENTER,
+ icon_name: 'go-previous-symbolic',
+ });
+ if (this._hasCancelButton)
+ this.cancelButton.connect('clicked', () => this.cancel());
+ else
+ this.cancelButton.opacity = 0;
+ this._mainBox.add_child(this.cancelButton);
+
+ this._authList = new AuthList.AuthList();
+ this._authList.set({
+ visible: false,
+ });
+ this._authList.connect('activate', (list, key) => {
+ this._authList.reactive = false;
+ this._authList.ease({
+ opacity: 0,
+ duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._authList.clear();
+ this._authList.hide();
+ this._userVerifier.selectChoice(this._queryingService, key);
+ },
+ });
+ });
+ this._mainBox.add_child(this._authList);
+
+ let entryParams = {
+ style_class: 'login-dialog-prompt-entry',
+ can_focus: true,
+ x_expand: true,
+ };
+
+ this._entry = null;
+
+ this._textEntry = new St.Entry(entryParams);
+ ShellEntry.addContextMenu(this._textEntry, { actionMode: Shell.ActionMode.NONE });
+
+ this._passwordEntry = new St.PasswordEntry(entryParams);
+ ShellEntry.addContextMenu(this._passwordEntry, { actionMode: Shell.ActionMode.NONE });
+
+ this._entry = this._passwordEntry;
+ this._mainBox.add_child(this._entry);
+ this._entry.grab_key_focus();
+ this._inactiveEntry = this._textEntry;
+
+ this._timedLoginIndicator = new St.Bin({
+ style_class: 'login-dialog-timed-login-indicator',
+ scale_x: 0,
+ });
+
+ this.add_child(this._timedLoginIndicator);
+
+ [this._textEntry, this._passwordEntry].forEach(entry => {
+ entry.clutter_text.connect('text-changed', () => {
+ if (!this._userVerifier.hasPendingMessages)
+ this._fadeOutMessage();
+ });
+
+ entry.clutter_text.connect('activate', () => {
+ let shouldSpin = entry === this._passwordEntry;
+ if (entry.reactive)
+ this._activateNext(shouldSpin);
+ });
+ });
+
+ this._defaultButtonWell = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._defaultButtonWell.add_constraint(new Clutter.BindConstraint({
+ source: this.cancelButton,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+ this._mainBox.add_child(this._defaultButtonWell);
+
+ this._spinner = new Animation.Spinner(DEFAULT_BUTTON_WELL_ICON_SIZE);
+ this._defaultButtonWell.add_child(this._spinner);
+ }
+
+ showTimedLoginIndicator(time) {
+ let hold = new Batch.Hold();
+
+ this.hideTimedLoginIndicator();
+
+ const startTime = GLib.get_monotonic_time();
+
+ this._timedLoginTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 33,
+ () => {
+ const currentTime = GLib.get_monotonic_time();
+ const elapsedTime = (currentTime - startTime) / GLib.USEC_PER_SEC;
+ this._timedLoginIndicator.scale_x = elapsedTime / time;
+ if (elapsedTime >= time) {
+ this._timedLoginTimeoutId = 0;
+ hold.release();
+ return GLib.SOURCE_REMOVE;
+ }
+
+ return GLib.SOURCE_CONTINUE;
+ });
+
+ GLib.Source.set_name_by_id(this._timedLoginTimeoutId, '[gnome-shell] this._timedLoginTimeoutId');
+
+ return hold;
+ }
+
+ hideTimedLoginIndicator() {
+ if (this._timedLoginTimeoutId) {
+ GLib.source_remove(this._timedLoginTimeoutId);
+ this._timedLoginTimeoutId = 0;
+ }
+ this._timedLoginIndicator.scale_x = 0.;
+ }
+
+ _activateNext(shouldSpin) {
+ this.verificationStatus = AuthPromptStatus.VERIFICATION_IN_PROGRESS;
+ this.updateSensitivity(false);
+
+ if (shouldSpin)
+ this.startSpinning();
+
+ if (this._queryingService)
+ this._userVerifier.answerQuery(this._queryingService, this._entry.text);
+ else
+ this._preemptiveAnswer = this._entry.text;
+
+ this.emit('next');
+ }
+
+ _updateEntry(secret) {
+ if (secret && this._entry !== this._passwordEntry) {
+ this._mainBox.replace_child(this._entry, this._passwordEntry);
+ this._entry = this._passwordEntry;
+ this._inactiveEntry = this._textEntry;
+ } else if (!secret && this._entry !== this._textEntry) {
+ this._mainBox.replace_child(this._entry, this._textEntry);
+ this._entry = this._textEntry;
+ this._inactiveEntry = this._passwordEntry;
+ }
+ this._capsLockWarningLabel.visible = secret;
+ }
+
+ _onAskQuestion(verifier, serviceName, question, secret) {
+ if (this._queryingService)
+ this.clear();
+
+ this._queryingService = serviceName;
+ if (this._preemptiveAnswer) {
+ this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
+ this._preemptiveAnswer = null;
+ return;
+ }
+
+ this._updateEntry(secret);
+
+ // Hack: The question string comes directly from PAM, if it's "Password:"
+ // we replace it with our own to allow localization, if it's something
+ // else we remove the last colon and any trailing or leading spaces.
+ if (question === 'Password:' || question === 'Password: ')
+ this.setQuestion(_('Password'));
+ else
+ this.setQuestion(question.replace(/: *$/, '').trim());
+
+ this.updateSensitivity(true);
+ this.emit('prompted');
+ }
+
+ _onShowChoiceList(userVerifier, serviceName, promptMessage, choiceList) {
+ if (this._queryingService)
+ this.clear();
+
+ this._queryingService = serviceName;
+
+ if (this._preemptiveAnswer)
+ this._preemptiveAnswer = null;
+
+ this.setChoiceList(promptMessage, choiceList);
+ this.updateSensitivity(true);
+ this.emit('prompted');
+ }
+
+ _onCredentialManagerAuthenticated() {
+ if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
+ this.reset();
+ }
+
+ _onSmartcardStatusChanged() {
+ this.smartcardDetected = this._userVerifier.smartcardDetected;
+
+ // Most of the time we want to reset if the user inserts or removes
+ // a smartcard. Smartcard insertion "preempts" what the user was
+ // doing, and smartcard removal aborts the preemption.
+ // The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
+ // with a smartcard
+ // 2) Don't reset if we've already succeeded at verification and
+ // the user is getting logged in.
+ if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
+ this.verificationStatus == AuthPromptStatus.VERIFYING &&
+ this.smartcardDetected)
+ return;
+
+ if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
+ this.reset();
+ }
+
+ _onShowMessage(_userVerifier, serviceName, message, type) {
+ let wiggleParameters = {duration: 0};
+
+ if (type === GdmUtil.MessageType.ERROR &&
+ this._userVerifier.serviceIsFingerprint(serviceName)) {
+ // TODO: Use Await for wiggle to be over before unfreezing the user verifier queue
+ wiggleParameters = {
+ duration: 65,
+ wiggleCount: 3,
+ };
+ this._userVerifier.increaseCurrentMessageTimeout(
+ wiggleParameters.duration * (wiggleParameters.wiggleCount + 2));
+ }
+
+ this.setMessage(message, type, wiggleParameters);
+ this.emit('prompted');
+ }
+
+ _onVerificationFailed(userVerifier, serviceName, canRetry) {
+ const wasQueryingService = this._queryingService === serviceName;
+
+ if (wasQueryingService) {
+ this._queryingService = null;
+ this.clear();
+ }
+
+ this.updateSensitivity(canRetry);
+ this.setActorInDefaultButtonWell(null);
+
+ if (!canRetry)
+ this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
+
+ if (wasQueryingService)
+ Util.wiggle(this._entry);
+ }
+
+ _onVerificationComplete() {
+ this.setActorInDefaultButtonWell(null);
+ this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
+ this.cancelButton.reactive = false;
+ this.cancelButton.can_focus = false;
+ }
+
+ _onReset() {
+ this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
+ this.reset();
+ }
+
+ setActorInDefaultButtonWell(actor, animate) {
+ if (!this._defaultButtonWellActor &&
+ !actor)
+ return;
+
+ let oldActor = this._defaultButtonWellActor;
+
+ if (oldActor)
+ oldActor.remove_all_transitions();
+
+ let wasSpinner;
+ if (oldActor == this._spinner)
+ wasSpinner = true;
+ else
+ wasSpinner = false;
+
+ let isSpinner;
+ if (actor == this._spinner)
+ isSpinner = true;
+ else
+ isSpinner = false;
+
+ if (this._defaultButtonWellActor != actor && oldActor) {
+ if (!animate) {
+ oldActor.opacity = 0;
+
+ if (wasSpinner) {
+ if (this._spinner)
+ this._spinner.stop();
+ }
+ } else {
+ oldActor.ease({
+ opacity: 0,
+ duration: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
+ delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: () => {
+ if (wasSpinner) {
+ if (this._spinner)
+ this._spinner.stop();
+ }
+ },
+ });
+ }
+ }
+
+ if (actor) {
+ if (isSpinner)
+ this._spinner.play();
+
+ if (!animate) {
+ actor.opacity = 255;
+ } else {
+ actor.ease({
+ opacity: 255,
+ duration: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
+ delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
+ mode: Clutter.AnimationMode.LINEAR,
+ });
+ }
+ }
+
+ this._defaultButtonWellActor = actor;
+ }
+
+ startSpinning() {
+ this.setActorInDefaultButtonWell(this._spinner, true);
+ }
+
+ stopSpinning() {
+ this.setActorInDefaultButtonWell(null, false);
+ }
+
+ clear() {
+ this._entry.text = '';
+ this.stopSpinning();
+ this._authList.clear();
+ this._authList.hide();
+ }
+
+ setQuestion(question) {
+ this._entry.hint_text = question;
+
+ this._authList.hide();
+ this._entry.show();
+ this._entry.grab_key_focus();
+ }
+
+ _fadeInChoiceList() {
+ this._authList.set({
+ opacity: 0,
+ visible: true,
+ reactive: false,
+ });
+ this._authList.ease({
+ opacity: 255,
+ duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
+ transition: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => (this._authList.reactive = true),
+ });
+ }
+
+ setChoiceList(promptMessage, choiceList) {
+ this._authList.clear();
+ this._authList.label.text = promptMessage;
+ for (let key in choiceList) {
+ let text = choiceList[key];
+ this._authList.addItem(key, text);
+ }
+
+ this._entry.hide();
+ if (this._message.text === '')
+ this._message.hide();
+ this._fadeInChoiceList();
+ }
+
+ getAnswer() {
+ let text;
+
+ if (this._preemptiveAnswer) {
+ text = this._preemptiveAnswer;
+ this._preemptiveAnswer = null;
+ } else {
+ text = this._entry.get_text();
+ }
+
+ return text;
+ }
+
+ _fadeOutMessage() {
+ if (this._message.opacity == 0)
+ return;
+ this._message.remove_all_transitions();
+ this._message.ease({
+ opacity: 0,
+ duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ setMessage(message, type, wiggleParameters = {duration: 0}) {
+ if (type == GdmUtil.MessageType.ERROR)
+ this._message.add_style_class_name('login-dialog-message-warning');
+ else
+ this._message.remove_style_class_name('login-dialog-message-warning');
+
+ if (type == GdmUtil.MessageType.HINT)
+ this._message.add_style_class_name('login-dialog-message-hint');
+ else
+ this._message.remove_style_class_name('login-dialog-message-hint');
+
+ this._message.show();
+ if (message) {
+ this._message.remove_all_transitions();
+ this._message.text = message;
+ this._message.opacity = 255;
+ } else {
+ this._message.opacity = 0;
+ }
+
+ Util.wiggle(this._message, wiggleParameters);
+ }
+
+ updateSensitivity(sensitive) {
+ if (this._entry.reactive === sensitive)
+ return;
+
+ this._entry.reactive = sensitive;
+
+ if (sensitive) {
+ this._entry.grab_key_focus();
+ } else {
+ this.grab_key_focus();
+
+ if (this._entry === this._passwordEntry)
+ this._entry.password_visible = false;
+ }
+ }
+
+ vfunc_hide() {
+ this.setActorInDefaultButtonWell(null, true);
+ super.vfunc_hide();
+ this._message.opacity = 0;
+
+ this.setUser(null);
+
+ this.updateSensitivity(true);
+ this._entry.set_text('');
+ }
+
+ setUser(user) {
+ let oldChild = this._userWell.get_child();
+ if (oldChild)
+ oldChild.destroy();
+
+ let userWidget = new UserWidget.UserWidget(user, Clutter.Orientation.VERTICAL);
+ this._userWell.set_child(userWidget);
+
+ if (!user)
+ this._updateEntry(false);
+ }
+
+ reset() {
+ let oldStatus = this.verificationStatus;
+ this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
+ this.cancelButton.reactive = this._hasCancelButton;
+ this.cancelButton.can_focus = this._hasCancelButton;
+ this._preemptiveAnswer = null;
+
+ if (this._userVerifier)
+ this._userVerifier.cancel();
+
+ this._queryingService = null;
+ this.clear();
+ this._message.opacity = 0;
+ this.setUser(null);
+ this._updateEntry(true);
+ this.stopSpinning();
+
+ if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
+ this.emit('failed');
+ else if (oldStatus === AuthPromptStatus.VERIFICATION_CANCELLED)
+ this.emit('cancelled');
+
+ let beginRequestType;
+
+ if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
+ // The user is constant at the unlock screen, so it will immediately
+ // respond to the request with the username
+ if (oldStatus === AuthPromptStatus.VERIFICATION_CANCELLED)
+ return;
+ beginRequestType = BeginRequestType.PROVIDE_USERNAME;
+ } else if (this._userVerifier.serviceIsForeground(OVirt.SERVICE_NAME) ||
+ this._userVerifier.serviceIsForeground(Vmware.SERVICE_NAME) ||
+ this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
+ // We don't need to know the username if the user preempted the login screen
+ // with a smartcard or with preauthenticated oVirt credentials
+ beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
+ } else if (oldStatus === AuthPromptStatus.VERIFICATION_IN_PROGRESS) {
+ // We're going back to retry with current user
+ beginRequestType = BeginRequestType.REUSE_USERNAME;
+ } else {
+ // In all other cases, we should get the username up front.
+ beginRequestType = BeginRequestType.PROVIDE_USERNAME;
+ }
+
+ this.emit('reset', beginRequestType);
+ }
+
+ addCharacter(unichar) {
+ if (!this._entry.visible)
+ return;
+
+ this._entry.grab_key_focus();
+ this._entry.clutter_text.insert_unichar(unichar);
+ }
+
+ begin(params) {
+ params = Params.parse(params, {
+ userName: null,
+ hold: null,
+ });
+
+ this.updateSensitivity(false);
+
+ let hold = params.hold;
+ if (!hold)
+ hold = new Batch.Hold();
+
+ this._userVerifier.begin(params.userName, hold);
+ this.verificationStatus = AuthPromptStatus.VERIFYING;
+ }
+
+ finish(onComplete) {
+ if (!this._userVerifier.hasPendingMessages) {
+ this._userVerifier.clear();
+ onComplete();
+ return;
+ }
+
+ let signalId = this._userVerifier.connect('no-more-messages', () => {
+ this._userVerifier.disconnect(signalId);
+ this._userVerifier.clear();
+ onComplete();
+ });
+ }
+
+ cancel() {
+ if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED)
+ return;
+
+ if (this.verificationStatus === AuthPromptStatus.VERIFICATION_IN_PROGRESS) {
+ this._cancelledRetries++;
+ if (this._cancelledRetries > this._userVerifier.allowedFailures)
+ this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
+ } else {
+ this.verificationStatus = AuthPromptStatus.VERIFICATION_CANCELLED;
+ }
+
+ this.reset();
+ }
+});
diff --git a/js/gdm/batch.js b/js/gdm/batch.js
new file mode 100644
index 0000000..f841f0f
--- /dev/null
+++ b/js/gdm/batch.js
@@ -0,0 +1,206 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/*
+ * Copyright 2011 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * In order for transformation animations to look good, they need to be
+ * incremental and have some order to them (e.g., fade out hidden items,
+ * then shrink to close the void left over). Chaining animations in this way can
+ * be error-prone and wordy using just ease() callbacks.
+ *
+ * The classes in this file help with this:
+ *
+ * - Task. encapsulates schedulable work to be run in a specific scope.
+ *
+ * - ConsecutiveBatch. runs a series of tasks in order and completes
+ * when the last in the series finishes.
+ *
+ * - ConcurrentBatch. runs a set of tasks at the same time and completes
+ * when the last to finish completes.
+ *
+ * - Hold. prevents a batch from completing the pending task until
+ * the hold is released.
+ *
+ * The tasks associated with a batch are specified in a list at batch
+ * construction time as either task objects or plain functions.
+ * Batches are task objects, themselves, so they can be nested.
+ *
+ * These classes aren't specific to GDM, but were found to be unintuitive and so
+ * are not used elsewhere. These APIs may ultimately get dropped entirely and
+ * replaced by something else.
+ */
+/* exported ConcurrentBatch, ConsecutiveBatch */
+
+const { GObject } = imports.gi;
+const Signals = imports.misc.signals;
+
+var Task = class extends Signals.EventEmitter {
+ constructor(scope, handler) {
+ super();
+
+ if (scope)
+ this.scope = scope;
+ else
+ this.scope = this;
+
+ this.handler = handler;
+ }
+
+ run() {
+ if (this.handler)
+ return this.handler.call(this.scope);
+
+ return null;
+ }
+};
+
+var Hold = class extends Task {
+ constructor() {
+ super(null, () => this);
+
+ this._acquisitions = 1;
+ }
+
+ acquire() {
+ if (this._acquisitions <= 0)
+ throw new Error("Cannot acquire hold after it's been released");
+ this._acquisitions++;
+ }
+
+ acquireUntilAfter(hold) {
+ if (!hold.isAcquired())
+ return;
+
+ this.acquire();
+ let signalId = hold.connect('release', () => {
+ hold.disconnect(signalId);
+ this.release();
+ });
+ }
+
+ release() {
+ this._acquisitions--;
+
+ if (this._acquisitions == 0)
+ this.emit('release');
+ }
+
+ isAcquired() {
+ return this._acquisitions > 0;
+ }
+};
+
+var Batch = class extends Task {
+ constructor(scope, tasks) {
+ super();
+
+ this.tasks = [];
+
+ for (let i = 0; i < tasks.length; i++) {
+ let task;
+
+ if (tasks[i] instanceof Task)
+ task = tasks[i];
+ else if (typeof tasks[i] == 'function')
+ task = new Task(scope, tasks[i]);
+ else
+ throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
+
+ this.tasks.push(task);
+ }
+ }
+
+ process() {
+ throw new GObject.NotImplementedError(`process in ${this.constructor.name}`);
+ }
+
+ runTask() {
+ if (!(this._currentTaskIndex in this.tasks))
+ return null;
+
+ return this.tasks[this._currentTaskIndex].run();
+ }
+
+ _finish() {
+ this.hold.release();
+ }
+
+ nextTask() {
+ this._currentTaskIndex++;
+
+ // if the entire batch of tasks is finished, release
+ // the hold and notify anyone waiting on the batch
+ if (this._currentTaskIndex >= this.tasks.length) {
+ this._finish();
+ return;
+ }
+
+ this.process();
+ }
+
+ _start() {
+ // acquire a hold to get released when the entire
+ // batch of tasks is finished
+ this.hold = new Hold();
+ this._currentTaskIndex = 0;
+ this.process();
+ }
+
+ run() {
+ this._start();
+
+ // hold may be destroyed at this point
+ // if we're already done running
+ return this.hold;
+ }
+
+ cancel() {
+ this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
+ }
+};
+
+var ConcurrentBatch = class extends Batch {
+ process() {
+ let hold = this.runTask();
+
+ if (hold)
+ this.hold.acquireUntilAfter(hold);
+
+ // Regardless of the state of the just run task,
+ // fire off the next one, so all the tasks can run
+ // concurrently.
+ this.nextTask();
+ }
+};
+
+var ConsecutiveBatch = class extends Batch {
+ process() {
+ let hold = this.runTask();
+
+ if (hold && hold.isAcquired()) {
+ // This task is inhibiting the batch. Wait on it
+ // before processing the next one.
+ let signalId = hold.connect('release', () => {
+ hold.disconnect(signalId);
+ this.nextTask();
+ });
+ } else {
+ // This task finished, process the next one
+ this.nextTask();
+ }
+ }
+};
diff --git a/js/gdm/credentialManager.js b/js/gdm/credentialManager.js
new file mode 100644
index 0000000..2ea9f72
--- /dev/null
+++ b/js/gdm/credentialManager.js
@@ -0,0 +1,27 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported CredentialManager */
+
+const Signals = imports.misc.signals;
+
+var CredentialManager = class CredentialManager extends Signals.EventEmitter {
+ constructor(service) {
+ super();
+
+ this._token = null;
+ this._service = service;
+ }
+
+ get token() {
+ return this._token;
+ }
+
+ set token(t) {
+ this._token = t;
+ if (this._token)
+ this.emit('user-authenticated', this._token);
+ }
+
+ get service() {
+ return this._service;
+ }
+};
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
new file mode 100644
index 0000000..6071936
--- /dev/null
+++ b/js/gdm/loginDialog.js
@@ -0,0 +1,1293 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported LoginDialog */
+/*
+ * Copyright 2011 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+const {
+ AccountsService, Atk, Clutter, Gdm, Gio,
+ GLib, GObject, Meta, Pango, Shell, St,
+} = imports.gi;
+
+const AuthPrompt = imports.gdm.authPrompt;
+const Batch = imports.gdm.batch;
+const BoxPointer = imports.ui.boxpointer;
+const CtrlAltTab = imports.ui.ctrlAltTab;
+const GdmUtil = imports.gdm.util;
+const Layout = imports.ui.layout;
+const LoginManager = imports.misc.loginManager;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const Realmd = imports.gdm.realmd;
+const UserWidget = imports.ui.userWidget;
+
+const _FADE_ANIMATION_TIME = 250;
+const _SCROLL_ANIMATION_TIME = 500;
+const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
+
+var UserListItem = GObject.registerClass({
+ Signals: { 'activate': {} },
+}, class UserListItem extends St.Button {
+ _init(user) {
+ let layout = new St.BoxLayout({
+ vertical: true,
+ });
+ super._init({
+ style_class: 'login-dialog-user-list-item',
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ can_focus: true,
+ x_expand: true,
+ child: layout,
+ reactive: true,
+ });
+
+ this.user = user;
+ this.user.connectObject('changed', this._onUserChanged.bind(this), this);
+
+ this.connect('notify::hover', () => {
+ this._setSelected(this.hover);
+ });
+
+ this._userWidget = new UserWidget.UserWidget(this.user);
+ layout.add(this._userWidget);
+
+ this._userWidget.bind_property('label-actor', this, 'label-actor',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._timedLoginIndicator = new St.Bin({
+ style_class: 'login-dialog-timed-login-indicator',
+ scale_x: 0,
+ visible: false,
+ });
+ layout.add(this._timedLoginIndicator);
+
+ this._onUserChanged();
+ }
+
+ vfunc_key_focus_in() {
+ super.vfunc_key_focus_in();
+ this._setSelected(true);
+ }
+
+ vfunc_key_focus_out() {
+ super.vfunc_key_focus_out();
+ this._setSelected(false);
+ }
+
+ _onUserChanged() {
+ this._updateLoggedIn();
+ }
+
+ _updateLoggedIn() {
+ if (this.user.is_logged_in())
+ this.add_style_pseudo_class('logged-in');
+ else
+ this.remove_style_pseudo_class('logged-in');
+ }
+
+ vfunc_clicked() {
+ this.emit('activate');
+ }
+
+ _setSelected(selected) {
+ if (selected) {
+ this.add_style_pseudo_class('selected');
+ this.grab_key_focus();
+ } else {
+ this.remove_style_pseudo_class('selected');
+ }
+ }
+
+ showTimedLoginIndicator(time) {
+ let hold = new Batch.Hold();
+
+ this.hideTimedLoginIndicator();
+
+ this._timedLoginIndicator.visible = true;
+
+ let startTime = GLib.get_monotonic_time();
+
+ this._timedLoginTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 33,
+ () => {
+ let currentTime = GLib.get_monotonic_time();
+ let elapsedTime = (currentTime - startTime) / GLib.USEC_PER_SEC;
+ this._timedLoginIndicator.scale_x = elapsedTime / time;
+ if (elapsedTime >= time) {
+ this._timedLoginTimeoutId = 0;
+ hold.release();
+ return GLib.SOURCE_REMOVE;
+ }
+
+ return GLib.SOURCE_CONTINUE;
+ });
+
+ GLib.Source.set_name_by_id(this._timedLoginTimeoutId, '[gnome-shell] this._timedLoginTimeoutId');
+
+ return hold;
+ }
+
+ hideTimedLoginIndicator() {
+ if (this._timedLoginTimeoutId) {
+ GLib.source_remove(this._timedLoginTimeoutId);
+ this._timedLoginTimeoutId = 0;
+ }
+
+ this._timedLoginIndicator.visible = false;
+ this._timedLoginIndicator.scale_x = 0.;
+ }
+});
+
+var UserList = GObject.registerClass({
+ Signals: {
+ 'activate': { param_types: [UserListItem.$gtype] },
+ 'item-added': { param_types: [UserListItem.$gtype] },
+ },
+}, class UserList extends St.ScrollView {
+ _init() {
+ super._init({
+ style_class: 'login-dialog-user-list-view',
+ x_expand: true,
+ y_expand: true,
+ });
+ this.set_policy(St.PolicyType.NEVER,
+ St.PolicyType.AUTOMATIC);
+
+ this._box = new St.BoxLayout({
+ vertical: true,
+ style_class: 'login-dialog-user-list',
+ pseudo_class: 'expanded',
+ });
+
+ this.add_actor(this._box);
+ this._items = {};
+ }
+
+ vfunc_key_focus_in() {
+ super.vfunc_key_focus_in();
+ this._moveFocusToItems();
+ }
+
+ _moveFocusToItems() {
+ let hasItems = Object.keys(this._items).length > 0;
+
+ if (!hasItems)
+ return;
+
+ if (global.stage.get_key_focus() != this)
+ return;
+
+ let focusSet = this.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ if (!focusSet) {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._moveFocusToItems();
+ return false;
+ });
+ }
+ }
+
+ _onItemActivated(activatedItem) {
+ this.emit('activate', activatedItem);
+ }
+
+ updateStyle(isExpanded) {
+ if (isExpanded)
+ this._box.add_style_pseudo_class('expanded');
+ else
+ this._box.remove_style_pseudo_class('expanded');
+
+ for (let userName in this._items) {
+ let item = this._items[userName];
+ item.sync_hover();
+ }
+ }
+
+ scrollToItem(item) {
+ let box = item.get_allocation_box();
+
+ let adjustment = this.get_vscroll_bar().get_adjustment();
+
+ let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
+ adjustment.ease(value, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: _SCROLL_ANIMATION_TIME,
+ });
+ }
+
+ jumpToItem(item) {
+ let box = item.get_allocation_box();
+
+ let adjustment = this.get_vscroll_bar().get_adjustment();
+
+ let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
+
+ adjustment.set_value(value);
+ }
+
+ getItemFromUserName(userName) {
+ let item = this._items[userName];
+
+ if (!item)
+ return null;
+
+ return item;
+ }
+
+ containsUser(user) {
+ return this._items[user.get_user_name()] != null;
+ }
+
+ addUser(user) {
+ if (!user.is_loaded)
+ return;
+
+ if (user.is_system_account())
+ return;
+
+ if (user.locked)
+ return;
+
+ let userName = user.get_user_name();
+
+ if (!userName)
+ return;
+
+ this.removeUser(user);
+
+ let item = new UserListItem(user);
+ this._box.add_child(item);
+
+ this._items[userName] = item;
+
+ item.connect('activate', this._onItemActivated.bind(this));
+
+ // Try to keep the focused item front-and-center
+ item.connect('key-focus-in', () => this.scrollToItem(item));
+
+ this._moveFocusToItems();
+
+ this.emit('item-added', item);
+ }
+
+ removeUser(user) {
+ if (!user.is_loaded)
+ return;
+
+ let userName = user.get_user_name();
+
+ if (!userName)
+ return;
+
+ let item = this._items[userName];
+
+ if (!item)
+ return;
+
+ item.destroy();
+ delete this._items[userName];
+ }
+
+ numItems() {
+ return Object.keys(this._items).length;
+ }
+});
+
+var SessionMenuButton = GObject.registerClass({
+ Signals: { 'session-activated': { param_types: [GObject.TYPE_STRING] } },
+}, class SessionMenuButton extends St.Bin {
+ _init() {
+ let button = new St.Button({
+ style_class: 'modal-dialog-button button login-dialog-session-list-button',
+ icon_name: 'emblem-system-symbolic',
+ reactive: true,
+ track_hover: true,
+ can_focus: true,
+ accessible_name: _("Choose Session"),
+ accessible_role: Atk.Role.MENU,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ super._init({ child: button });
+ this._button = button;
+
+ this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.BOTTOM);
+ Main.uiGroup.add_actor(this._menu.actor);
+ this._menu.actor.hide();
+
+ this._menu.connect('open-state-changed', (menu, isOpen) => {
+ if (isOpen)
+ this._button.add_style_pseudo_class('active');
+ else
+ this._button.remove_style_pseudo_class('active');
+ });
+
+ this._manager = new PopupMenu.PopupMenuManager(this._button,
+ { actionMode: Shell.ActionMode.NONE });
+ this._manager.addMenu(this._menu);
+
+ this._button.connect('clicked', () => this._menu.toggle());
+
+ this._items = {};
+ this._activeSessionId = null;
+ this._populate();
+ }
+
+ updateSensitivity(sensitive) {
+ this._button.reactive = sensitive;
+ this._button.can_focus = sensitive;
+ this.opacity = sensitive ? 255 : 0;
+ this._menu.close(BoxPointer.PopupAnimation.NONE);
+ }
+
+ _updateOrnament() {
+ let itemIds = Object.keys(this._items);
+ for (let i = 0; i < itemIds.length; i++) {
+ if (itemIds[i] == this._activeSessionId)
+ this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.DOT);
+ else
+ this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.NONE);
+ }
+ }
+
+ setActiveSession(sessionId) {
+ if (sessionId == this._activeSessionId)
+ return;
+
+ this._activeSessionId = sessionId;
+ this._updateOrnament();
+ }
+
+ close() {
+ this._menu.close();
+ }
+
+ _populate() {
+ let ids = Gdm.get_session_ids();
+ ids.sort();
+
+ if (ids.length <= 1) {
+ this._button.hide();
+ return;
+ }
+
+ for (let i = 0; i < ids.length; i++) {
+ let [sessionName, sessionDescription_] = Gdm.get_session_name_and_description(ids[i]);
+
+ let id = ids[i];
+ let item = new PopupMenu.PopupMenuItem(sessionName);
+ this._menu.addMenuItem(item);
+ this._items[id] = item;
+
+ item.connect('activate', () => {
+ this.setActiveSession(id);
+ this.emit('session-activated', this._activeSessionId);
+ });
+ }
+ }
+});
+
+var LoginDialog = GObject.registerClass({
+ Signals: {
+ 'failed': {},
+ 'wake-up-screen': {},
+ },
+}, class LoginDialog extends St.Widget {
+ _init(parentActor) {
+ super._init({ style_class: 'login-dialog', visible: false });
+
+ this.get_accessible().set_role(Atk.Role.WINDOW);
+
+ this.add_constraint(new Layout.MonitorConstraint({ primary: true }));
+ this.connect('destroy', this._onDestroy.bind(this));
+ parentActor.add_child(this);
+
+ this._userManager = AccountsService.UserManager.get_default();
+ this._gdmClient = new Gdm.Client();
+
+ try {
+ this._gdmClient.set_enabled_extensions([Gdm.UserVerifierChoiceList.interface_info().name]);
+ } catch (e) {
+ }
+
+ this._settings = new Gio.Settings({ schema_id: GdmUtil.LOGIN_SCREEN_SCHEMA });
+
+ this._settings.connect(`changed::${GdmUtil.BANNER_MESSAGE_KEY}`,
+ this._updateBanner.bind(this));
+ this._settings.connect(`changed::${GdmUtil.BANNER_MESSAGE_TEXT_KEY}`,
+ this._updateBanner.bind(this));
+ this._settings.connect(`changed::${GdmUtil.DISABLE_USER_LIST_KEY}`,
+ this._updateDisableUserList.bind(this));
+ this._settings.connect(`changed::${GdmUtil.LOGO_KEY}`,
+ this._updateLogo.bind(this));
+
+ this._textureCache = St.TextureCache.get_default();
+ this._textureCache.connectObject('texture-file-changed',
+ this._updateLogoTexture.bind(this), this);
+
+ this._userSelectionBox = new St.BoxLayout({
+ style_class: 'login-dialog-user-selection-box',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ vertical: true,
+ visible: false,
+ });
+ this.add_child(this._userSelectionBox);
+
+ this._userList = new UserList();
+ this._userSelectionBox.add_child(this._userList);
+
+ this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
+ this._authPrompt.connect('prompted', this._onPrompted.bind(this));
+ this._authPrompt.connect('reset', this._onReset.bind(this));
+ this._authPrompt.hide();
+ this.add_child(this._authPrompt);
+
+ // translators: this message is shown below the user list on the
+ // login screen. It can be activated to reveal an entry for
+ // manually entering the username.
+ let notListedLabel = new St.Label({
+ text: _("Not listed?"),
+ style_class: 'login-dialog-not-listed-label',
+ });
+ this._notListedButton = new St.Button({
+ style_class: 'login-dialog-not-listed-button',
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ can_focus: true,
+ child: notListedLabel,
+ reactive: true,
+ x_align: Clutter.ActorAlign.START,
+ label_actor: notListedLabel,
+ });
+
+ this._notListedButton.connect('clicked', this._hideUserListAskForUsernameAndBeginVerification.bind(this));
+
+ this._notListedButton.hide();
+
+ this._userSelectionBox.add_child(this._notListedButton);
+
+ this._bannerView = new St.ScrollView({
+ style_class: 'login-dialog-banner-view',
+ opacity: 0,
+ vscrollbar_policy: St.PolicyType.AUTOMATIC,
+ hscrollbar_policy: St.PolicyType.NEVER,
+ });
+ this.add_child(this._bannerView);
+
+ let bannerBox = new St.BoxLayout({ vertical: true });
+
+ this._bannerView.add_actor(bannerBox);
+ this._bannerLabel = new St.Label({
+ style_class: 'login-dialog-banner',
+ text: '',
+ });
+ this._bannerLabel.clutter_text.line_wrap = true;
+ this._bannerLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ bannerBox.add_child(this._bannerLabel);
+ this._updateBanner();
+
+ this._sessionMenuButton = new SessionMenuButton();
+ this._sessionMenuButton.connect('session-activated',
+ (list, sessionId) => {
+ this._greeter.call_select_session_sync(sessionId, null);
+ });
+ this._sessionMenuButton.opacity = 0;
+ this._sessionMenuButton.show();
+ this.add_child(this._sessionMenuButton);
+
+ this._logoBin = new St.Widget({
+ style_class: 'login-dialog-logo-bin',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.END,
+ });
+ this._logoBin.connect('resource-scale-changed', () => {
+ this._updateLogoTexture(this._textureCache, this._logoFile);
+ });
+ this.add_child(this._logoBin);
+ this._updateLogo();
+
+ this._userList.connect('activate', (userList, item) => {
+ this._onUserListActivated(item);
+ });
+
+ this._disableUserList = undefined;
+ this._userListLoaded = false;
+
+ this._realmManager = new Realmd.Manager();
+ this._realmManager.connectObject('login-format-changed',
+ this._showRealmLoginHint.bind(this), this);
+
+ this._getGreeterSessionProxy();
+
+ // If the user list is enabled, it should take key focus; make sure the
+ // screen shield is initialized first to prevent it from stealing the
+ // focus later
+ Main.layoutManager.connectObject('startup-complete',
+ this._updateDisableUserList.bind(this), this);
+ }
+
+ _getBannerAllocation(dialogBox) {
+ let actorBox = new Clutter.ActorBox();
+
+ let [, , natWidth, natHeight] = this._bannerView.get_preferred_size();
+ let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2;
+
+ actorBox.x1 = Math.floor(centerX - natWidth / 2);
+ actorBox.y1 = dialogBox.y1 + Main.layoutManager.panelBox.height;
+ actorBox.x2 = actorBox.x1 + natWidth;
+ actorBox.y2 = actorBox.y1 + natHeight;
+
+ return actorBox;
+ }
+
+ _getLogoBinAllocation(dialogBox) {
+ let actorBox = new Clutter.ActorBox();
+
+ let [, , natWidth, natHeight] = this._logoBin.get_preferred_size();
+ let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2;
+
+ actorBox.x1 = Math.floor(centerX - natWidth / 2);
+ actorBox.y1 = dialogBox.y2 - natHeight;
+ actorBox.x2 = actorBox.x1 + natWidth;
+ actorBox.y2 = actorBox.y1 + natHeight;
+
+ return actorBox;
+ }
+
+ _getSessionMenuButtonAllocation(dialogBox) {
+ let actorBox = new Clutter.ActorBox();
+
+ let [, , natWidth, natHeight] = this._sessionMenuButton.get_preferred_size();
+
+ if (this.get_text_direction() === Clutter.TextDirection.RTL)
+ actorBox.x1 = dialogBox.x1 + natWidth;
+ else
+ actorBox.x1 = dialogBox.x2 - (natWidth * 2);
+
+ actorBox.y1 = dialogBox.y2 - (natHeight * 2);
+ actorBox.x2 = actorBox.x1 + natWidth;
+ actorBox.y2 = actorBox.y1 + natHeight;
+
+ return actorBox;
+ }
+
+ _getCenterActorAllocation(dialogBox, actor) {
+ let actorBox = new Clutter.ActorBox();
+
+ let [, , natWidth, natHeight] = actor.get_preferred_size();
+ let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2;
+ let centerY = dialogBox.y1 + (dialogBox.y2 - dialogBox.y1) / 2;
+
+ natWidth = Math.min(natWidth, dialogBox.x2 - dialogBox.x1);
+ natHeight = Math.min(natHeight, dialogBox.y2 - dialogBox.y1);
+
+ actorBox.x1 = Math.floor(centerX - natWidth / 2);
+ actorBox.y1 = Math.floor(centerY - natHeight / 2);
+ actorBox.x2 = actorBox.x1 + natWidth;
+ actorBox.y2 = actorBox.y1 + natHeight;
+
+ return actorBox;
+ }
+
+ vfunc_allocate(dialogBox) {
+ this.set_allocation(dialogBox);
+
+ let themeNode = this.get_theme_node();
+ dialogBox = themeNode.get_content_box(dialogBox);
+
+ let dialogWidth = dialogBox.x2 - dialogBox.x1;
+ let dialogHeight = dialogBox.y2 - dialogBox.y1;
+
+ // First find out what space the children require
+ let bannerAllocation = null;
+ let bannerHeight = 0;
+ if (this._bannerView.visible) {
+ bannerAllocation = this._getBannerAllocation(dialogBox);
+ bannerHeight = bannerAllocation.y2 - bannerAllocation.y1;
+ }
+
+ let authPromptAllocation = null;
+ let authPromptWidth = 0;
+ if (this._authPrompt.visible) {
+ authPromptAllocation = this._getCenterActorAllocation(dialogBox, this._authPrompt);
+ authPromptWidth = authPromptAllocation.x2 - authPromptAllocation.x1;
+ }
+
+ let userSelectionAllocation = null;
+ let userSelectionHeight = 0;
+ if (this._userSelectionBox.visible) {
+ userSelectionAllocation = this._getCenterActorAllocation(dialogBox, this._userSelectionBox);
+ userSelectionHeight = userSelectionAllocation.y2 - userSelectionAllocation.y1;
+ }
+
+ let logoAllocation = null;
+ let logoHeight = 0;
+ if (this._logoBin.visible) {
+ logoAllocation = this._getLogoBinAllocation(dialogBox);
+ logoHeight = logoAllocation.y2 - logoAllocation.y1;
+ }
+
+ let sessionMenuButtonAllocation = null;
+ if (this._sessionMenuButton.visible)
+ sessionMenuButtonAllocation = this._getSessionMenuButtonAllocation(dialogBox);
+
+ // Then figure out if we're overly constrained and need to
+ // try a different layout, or if we have what extra space we
+ // can hand out
+ if (bannerAllocation) {
+ let bannerSpace;
+
+ if (authPromptAllocation)
+ bannerSpace = authPromptAllocation.y1 - bannerAllocation.y1;
+ else
+ bannerSpace = 0;
+
+ let leftOverYSpace = bannerSpace - bannerHeight;
+
+ if (leftOverYSpace > 0) {
+ // First figure out how much left over space is up top
+ let leftOverTopSpace = leftOverYSpace / 2;
+
+ // Then, shift the banner into the middle of that extra space
+ let yShift = Math.floor(leftOverTopSpace / 2);
+
+ bannerAllocation.y1 += yShift;
+ bannerAllocation.y2 += yShift;
+ } else {
+ // Then figure out how much space there would be if we switched to a
+ // wide layout with banner on one side and authprompt on the other.
+ let leftOverXSpace = dialogWidth - authPromptWidth;
+
+ // In a wide view, half of the available space goes to the banner,
+ // and the other half goes to the margins.
+ let wideBannerWidth = leftOverXSpace / 2;
+ let wideSpacing = leftOverXSpace - wideBannerWidth;
+
+ // If we do go with a wide layout, we need there to be at least enough
+ // space for the banner and the auth prompt to be the same width,
+ // so it doesn't look unbalanced.
+ if (authPromptWidth > 0 && wideBannerWidth > authPromptWidth) {
+ let centerX = dialogBox.x1 + dialogWidth / 2;
+ let centerY = dialogBox.y1 + dialogHeight / 2;
+
+ // A small portion of the spacing goes down the center of the
+ // screen to help delimit the two columns of the wide view
+ let centerGap = wideSpacing / 8;
+
+ // place the banner along the left edge of the center margin
+ bannerAllocation.x2 = Math.floor(centerX - centerGap / 2);
+ bannerAllocation.x1 = Math.floor(bannerAllocation.x2 - wideBannerWidth);
+
+ // figure out how tall it would like to be and try to accommodate
+ // but don't let it get too close to the logo
+ let [, wideBannerHeight] = this._bannerView.get_preferred_height(wideBannerWidth);
+
+ let maxWideHeight = dialogHeight - 3 * logoHeight;
+ wideBannerHeight = Math.min(maxWideHeight, wideBannerHeight);
+ bannerAllocation.y1 = Math.floor(centerY - wideBannerHeight / 2);
+ bannerAllocation.y2 = bannerAllocation.y1 + wideBannerHeight;
+
+ // place the auth prompt along the right edge of the center margin
+ authPromptAllocation.x1 = Math.floor(centerX + centerGap / 2);
+ authPromptAllocation.x2 = authPromptAllocation.x1 + authPromptWidth;
+ } else {
+ // If we aren't going to do a wide view, then we need to limit
+ // the height of the banner so it will present scrollbars
+
+ // First figure out how much space there is without the banner
+ leftOverYSpace += bannerHeight;
+
+ // Then figure out how much of that space is up top
+ let availableTopSpace = Math.floor(leftOverYSpace / 2);
+
+ // Then give all of that space to the banner
+ bannerAllocation.y2 = bannerAllocation.y1 + availableTopSpace;
+ }
+ }
+ } else if (userSelectionAllocation) {
+ // Grow the user list to fill the space
+ let leftOverYSpace = dialogHeight - userSelectionHeight - logoHeight;
+
+ if (leftOverYSpace > 0) {
+ let topExpansion = Math.floor(leftOverYSpace / 2);
+ let bottomExpansion = topExpansion;
+
+ userSelectionAllocation.y1 -= topExpansion;
+ userSelectionAllocation.y2 += bottomExpansion;
+ }
+ }
+
+ // Finally hand out the allocations
+ if (bannerAllocation)
+ this._bannerView.allocate(bannerAllocation);
+
+ if (authPromptAllocation)
+ this._authPrompt.allocate(authPromptAllocation);
+
+ if (userSelectionAllocation)
+ this._userSelectionBox.allocate(userSelectionAllocation);
+
+ if (logoAllocation)
+ this._logoBin.allocate(logoAllocation);
+
+ if (sessionMenuButtonAllocation)
+ this._sessionMenuButton.allocate(sessionMenuButtonAllocation);
+ }
+
+ _ensureUserListLoaded() {
+ if (!this._userManager.is_loaded) {
+ this._userManager.connectObject('notify::is-loaded',
+ () => {
+ if (this._userManager.is_loaded) {
+ this._userManager.disconnectObject(this);
+ this._loadUserList();
+ }
+ });
+ } else {
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, this._loadUserList.bind(this));
+ GLib.Source.set_name_by_id(id, '[gnome-shell] _loadUserList');
+ }
+ }
+
+ _updateDisableUserList() {
+ let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
+
+ // Disable user list when there are no users.
+ if (this._userListLoaded && this._userList.numItems() == 0)
+ disableUserList = true;
+
+ if (disableUserList != this._disableUserList) {
+ this._disableUserList = disableUserList;
+
+ if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
+ this._authPrompt.reset();
+
+ if (this._disableUserList && this._timedLoginUserListHold)
+ this._timedLoginUserListHold.release();
+ }
+ }
+
+ _updateCancelButton() {
+ let cancelVisible;
+
+ // Hide the cancel button if the user list is disabled and we're asking for
+ // a username
+ if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING && this._disableUserList)
+ cancelVisible = false;
+ else
+ cancelVisible = true;
+
+ this._authPrompt.cancelButton.visible = cancelVisible;
+ }
+
+ _updateBanner() {
+ let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
+ let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
+
+ if (enabled && text) {
+ this._bannerLabel.set_text(text);
+ this._bannerLabel.show();
+ } else {
+ this._bannerLabel.hide();
+ }
+ }
+
+ _fadeInBannerView() {
+ this._bannerView.show();
+ this._bannerView.ease({
+ opacity: 255,
+ duration: _FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ _hideBannerView() {
+ this._bannerView.remove_all_transitions();
+ this._bannerView.opacity = 0;
+ this._bannerView.hide();
+ }
+
+ _updateLogoTexture(cache, file) {
+ if (this._logoFile && !this._logoFile.equal(file))
+ return;
+
+ this._logoBin.destroy_all_children();
+ const resourceScale = this._logoBin.get_resource_scale();
+ if (this._logoFile) {
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile,
+ -1, -1,
+ scaleFactor,
+ resourceScale));
+ }
+ }
+
+ _updateLogo() {
+ let path = this._settings.get_string(GdmUtil.LOGO_KEY);
+
+ this._logoFile = path ? Gio.file_new_for_path(path) : null;
+ this._updateLogoTexture(this._textureCache, this._logoFile);
+ }
+
+ _onPrompted() {
+ const showSessionMenu = this._shouldShowSessionMenuButton();
+
+ this._sessionMenuButton.updateSensitivity(showSessionMenu);
+ this._sessionMenuButton.visible = showSessionMenu;
+ this._showPrompt();
+ }
+
+ _resetGreeterProxy() {
+ if (GLib.getenv('GDM_GREETER_TEST') != '1') {
+ if (this._greeter)
+ this._greeter.run_dispose();
+
+ this._greeter = this._gdmClient.get_greeter_sync(null);
+
+ this._greeter.connectObject(
+ 'default-session-name-changed', this._onDefaultSessionChanged.bind(this),
+ 'session-opened', this._onSessionOpened.bind(this),
+ 'timed-login-requested', this._onTimedLoginRequested.bind(this), this);
+ }
+ }
+
+ _onReset(authPrompt, beginRequest) {
+ this._resetGreeterProxy();
+ this._sessionMenuButton.updateSensitivity(true);
+
+ const previousUser = this._user;
+ this._user = null;
+
+ if (this._nextSignalId) {
+ this._authPrompt.disconnect(this._nextSignalId);
+ this._nextSignalId = 0;
+ }
+
+ if (previousUser && beginRequest === AuthPrompt.BeginRequestType.REUSE_USERNAME) {
+ this._user = previousUser;
+ this._authPrompt.setUser(this._user);
+ this._authPrompt.begin({ userName: previousUser.get_user_name() });
+ } else if (beginRequest === AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
+ if (!this._disableUserList)
+ this._showUserList();
+ else
+ this._hideUserListAskForUsernameAndBeginVerification();
+ } else {
+ this._hideUserListAndBeginVerification();
+ }
+ }
+
+ _onDefaultSessionChanged(client, sessionId) {
+ this._sessionMenuButton.setActiveSession(sessionId);
+ }
+
+ _shouldShowSessionMenuButton() {
+ if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFYING &&
+ this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_FAILED)
+ return false;
+
+ if (this._user && this._user.is_loaded && this._user.is_logged_in())
+ return false;
+
+ return true;
+ }
+
+ _showPrompt() {
+ if (this._authPrompt.visible)
+ return;
+ this._authPrompt.opacity = 0;
+ this._authPrompt.show();
+ this._authPrompt.ease({
+ opacity: 255,
+ duration: _FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ this._fadeInBannerView();
+ }
+
+ _showRealmLoginHint(realmManager, hint) {
+ if (!hint)
+ return;
+
+ hint = hint.replace(/%U/g, 'user');
+ hint = hint.replace(/%D/g, 'DOMAIN');
+ hint = hint.replace(/%[^UD]/g, '');
+
+ // Translators: this message is shown below the username entry field
+ // to clue the user in on how to login to the local network realm
+ this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), GdmUtil.MessageType.HINT);
+ }
+
+ _askForUsernameAndBeginVerification() {
+ this._authPrompt.setUser(null);
+ this._authPrompt.setQuestion(_('Username'));
+
+ this._showRealmLoginHint(this._realmManager.loginFormat);
+
+ if (this._nextSignalId)
+ this._authPrompt.disconnect(this._nextSignalId);
+ this._nextSignalId = this._authPrompt.connect('next',
+ () => {
+ this._authPrompt.disconnect(this._nextSignalId);
+ this._nextSignalId = 0;
+ this._authPrompt.updateSensitivity(false);
+ let answer = this._authPrompt.getAnswer();
+ this._user = this._userManager.get_user(answer);
+ this._authPrompt.clear();
+ this._authPrompt.begin({ userName: answer });
+ this._updateCancelButton();
+ });
+ this._updateCancelButton();
+
+ this._sessionMenuButton.updateSensitivity(false);
+ this._authPrompt.updateSensitivity(true);
+ this._showPrompt();
+ }
+
+ _bindOpacity() {
+ this._bindings = Main.layoutManager.uiGroup.get_children()
+ .filter(c => c != Main.layoutManager.screenShieldGroup)
+ .map(c => this.bind_property('opacity', c, 'opacity', 0));
+ }
+
+ _unbindOpacity() {
+ this._bindings.forEach(b => b.unbind());
+ }
+
+ _loginScreenSessionActivated() {
+ if (this.opacity == 255 && this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
+ return;
+
+ if (this._authPrompt.verificationStatus !== AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
+ this._authPrompt.reset();
+
+ this._bindOpacity();
+ this.ease({
+ opacity: 255,
+ duration: _FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._unbindOpacity(),
+ });
+ }
+
+ async _getGreeterSessionProxy() {
+ const loginManager = LoginManager.getLoginManager();
+ this._greeterSessionProxy = await loginManager.getCurrentSessionProxy();
+ this._greeterSessionProxy?.connectObject('g-properties-changed', (proxy, properties) => {
+ const activeChanged = !!properties.lookup_value('Active', null);
+ if (activeChanged && proxy.Active)
+ this._loginScreenSessionActivated();
+ }, this);
+ }
+
+ _startSession(serviceName) {
+ this._bindOpacity();
+ this.ease({
+ opacity: 0,
+ duration: _FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
+ this._unbindOpacity();
+ },
+ });
+ }
+
+ _onSessionOpened(client, serviceName) {
+ this._authPrompt.finish(() => this._startSession(serviceName));
+ }
+
+ _waitForItemForUser(userName) {
+ let item = this._userList.getItemFromUserName(userName);
+
+ if (item)
+ return null;
+
+ let hold = new Batch.Hold();
+ let signalId = this._userList.connect('item-added',
+ () => {
+ item = this._userList.getItemFromUserName(userName);
+
+ if (item)
+ hold.release();
+ });
+
+ hold.connect('release', () => this._userList.disconnect(signalId));
+
+ return hold;
+ }
+
+ _blockTimedLoginUntilIdle() {
+ let hold = new Batch.Hold();
+
+ this._timedLoginIdleTimeOutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, _TIMED_LOGIN_IDLE_THRESHOLD,
+ () => {
+ this._timedLoginIdleTimeOutId = 0;
+ hold.release();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._timedLoginIdleTimeOutId, '[gnome-shell] this._timedLoginIdleTimeOutId');
+ return hold;
+ }
+
+ _startTimedLogin(userName, delay) {
+ let firstRun = true;
+
+ // Cancel execution of old batch
+ if (this._timedLoginBatch) {
+ this._timedLoginBatch.cancel();
+ this._timedLoginBatch = null;
+ firstRun = false;
+ }
+
+ // Reset previous idle-timeout
+ if (this._timedLoginIdleTimeOutId) {
+ GLib.source_remove(this._timedLoginIdleTimeOutId);
+ this._timedLoginIdleTimeOutId = 0;
+ }
+
+ let loginItem = null;
+ let animationTime;
+
+ let tasks = [
+ () => {
+ if (this._disableUserList)
+ return null;
+
+ this._timedLoginUserListHold = this._waitForItemForUser(userName);
+ return this._timedLoginUserListHold;
+ },
+
+ () => {
+ this._timedLoginUserListHold = null;
+
+ if (this._disableUserList)
+ loginItem = this._authPrompt;
+ else
+ loginItem = this._userList.getItemFromUserName(userName);
+
+ // If there is an animation running on the item, reset it.
+ loginItem.hideTimedLoginIndicator();
+ },
+
+ () => {
+ if (this._disableUserList)
+ return;
+
+ // If we're just starting out, start on the right item.
+ if (!this._userManager.is_loaded)
+ this._userList.jumpToItem(loginItem);
+ },
+
+ () => {
+ // This blocks the timed login animation until a few
+ // seconds after the user stops interacting with the
+ // login screen.
+
+ // We skip this step if the timed login delay is very short.
+ if (delay > _TIMED_LOGIN_IDLE_THRESHOLD) {
+ animationTime = delay - _TIMED_LOGIN_IDLE_THRESHOLD;
+ return this._blockTimedLoginUntilIdle();
+ } else {
+ animationTime = delay;
+ return null;
+ }
+ },
+
+ () => {
+ if (this._disableUserList)
+ return;
+
+ // If idle timeout is done, make sure the timed login indicator is shown
+ if (delay > _TIMED_LOGIN_IDLE_THRESHOLD &&
+ this._authPrompt.visible)
+ this._authPrompt.cancel();
+
+ if (delay > _TIMED_LOGIN_IDLE_THRESHOLD || firstRun) {
+ this._userList.scrollToItem(loginItem);
+ loginItem.grab_key_focus();
+ }
+ },
+
+ () => loginItem.showTimedLoginIndicator(animationTime),
+
+ () => {
+ this._timedLoginBatch = null;
+ this._greeter.call_begin_auto_login_sync(userName, null);
+ },
+ ];
+
+ this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
+
+ return this._timedLoginBatch.run();
+ }
+
+ _onTimedLoginRequested(client, userName, seconds) {
+ if (this._timedLoginBatch)
+ return;
+
+ this._startTimedLogin(userName, seconds);
+
+ // Restart timed login on user interaction
+ global.stage.connect('captured-event', (actor, event) => {
+ if (event.type() == Clutter.EventType.KEY_PRESS ||
+ event.type() == Clutter.EventType.BUTTON_PRESS)
+ this._startTimedLogin(userName, seconds);
+
+ return Clutter.EVENT_PROPAGATE;
+ });
+ }
+
+ _setUserListExpanded(expanded) {
+ this._userList.updateStyle(expanded);
+ this._userSelectionBox.visible = expanded;
+ }
+
+ _hideUserList() {
+ this._setUserListExpanded(false);
+ if (this._userSelectionBox.visible)
+ GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
+ }
+
+ _hideUserListAskForUsernameAndBeginVerification() {
+ this._hideUserList();
+ this._askForUsernameAndBeginVerification();
+ }
+
+ _hideUserListAndBeginVerification() {
+ this._hideUserList();
+ this._authPrompt.begin();
+ }
+
+ _showUserList() {
+ this._ensureUserListLoaded();
+ this._authPrompt.hide();
+ this._hideBannerView();
+ this._sessionMenuButton.close();
+ this._sessionMenuButton.hide();
+ this._setUserListExpanded(true);
+ this._notListedButton.show();
+ this._userList.grab_key_focus();
+ }
+
+ _beginVerificationForItem(item) {
+ this._authPrompt.setUser(item.user);
+
+ let userName = item.user.get_user_name();
+ let hold = new Batch.Hold();
+
+ this._authPrompt.begin({ userName, hold });
+ return hold;
+ }
+
+ _onUserListActivated(activatedItem) {
+ this._user = activatedItem.user;
+
+ this._updateCancelButton();
+
+ const batch = new Batch.ConcurrentBatch(this, [
+ GdmUtil.cloneAndFadeOutActor(this._userSelectionBox),
+ this._beginVerificationForItem(activatedItem),
+ ]);
+ batch.run();
+ }
+
+ _onDestroy() {
+ if (this._settings) {
+ this._settings.run_dispose();
+ this._settings = null;
+ }
+ this._greeter = null;
+ this._greeterSessionProxy = null;
+ this._realmManager?.release();
+ this._realmManager = null;
+ }
+
+ _loadUserList() {
+ if (this._userListLoaded)
+ return GLib.SOURCE_REMOVE;
+
+ this._userListLoaded = true;
+
+ let users = this._userManager.list_users();
+
+ for (let i = 0; i < users.length; i++)
+ this._userList.addUser(users[i]);
+
+ this._updateDisableUserList();
+
+ this._userManager.connectObject(
+ 'user-added', (userManager, user) => {
+ this._userList.addUser(user);
+ this._updateDisableUserList();
+ },
+ 'user-removed', (userManager, user) => {
+ this._userList.removeUser(user);
+ this._updateDisableUserList();
+ },
+ 'user-changed', (userManager, user) => {
+ if (this._userList.containsUser(user) && user.locked)
+ this._userList.removeUser(user);
+ else if (!this._userList.containsUser(user) && !user.locked)
+ this._userList.addUser(user);
+ this._updateDisableUserList();
+ }, this);
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ activate() {
+ this._userList.grab_key_focus();
+ this.show();
+ }
+
+ open() {
+ Main.ctrlAltTabManager.addGroup(this,
+ _("Login Window"),
+ 'dialog-password-symbolic',
+ { sortGroup: CtrlAltTab.SortGroup.MIDDLE });
+ this.activate();
+
+ this.opacity = 0;
+
+ this._grab = Main.pushModal(global.stage, { actionMode: Shell.ActionMode.LOGIN_SCREEN });
+
+ this.ease({
+ opacity: 255,
+ duration: 1000,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+
+ return true;
+ }
+
+ close() {
+ Main.popModal(this._grab);
+ this._grab = null;
+ Main.ctrlAltTabManager.removeGroup(this);
+ }
+
+ cancel() {
+ this._authPrompt.cancel();
+ }
+
+ addCharacter(_unichar) {
+ // Don't allow type ahead at the login screen
+ }
+
+ finish(onComplete) {
+ this._authPrompt.finish(onComplete);
+ }
+});
diff --git a/js/gdm/oVirt.js b/js/gdm/oVirt.js
new file mode 100644
index 0000000..32e0aa5
--- /dev/null
+++ b/js/gdm/oVirt.js
@@ -0,0 +1,51 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported getOVirtCredentialsManager */
+
+const Gio = imports.gi.Gio;
+const Credential = imports.gdm.credentialManager;
+
+var SERVICE_NAME = 'gdm-ovirtcred';
+
+const OVirtCredentialsIface = `
+<node>
+<interface name="org.ovirt.vdsm.Credentials">
+<signal name="UserAuthenticated">
+ <arg type="s" name="token"/>
+</signal>
+</interface>
+</node>`;
+
+const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
+
+let _oVirtCredentialsManager = null;
+
+function OVirtCredentials() {
+ var self = new Gio.DBusProxy({
+ g_connection: Gio.DBus.system,
+ g_interface_name: OVirtCredentialsInfo.name,
+ g_interface_info: OVirtCredentialsInfo,
+ g_name: 'org.ovirt.vdsm.Credentials',
+ g_object_path: '/org/ovirt/vdsm/Credentials',
+ g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
+ });
+ self.init(null);
+ return self;
+}
+
+var OVirtCredentialsManager = class OVirtCredentialsManager extends Credential.CredentialManager {
+ constructor() {
+ super(SERVICE_NAME);
+ this._credentials = new OVirtCredentials();
+ this._credentials.connectSignal('UserAuthenticated',
+ (proxy, sender, [token]) => {
+ this.token = token;
+ });
+ }
+};
+
+function getOVirtCredentialsManager() {
+ if (!_oVirtCredentialsManager)
+ _oVirtCredentialsManager = new OVirtCredentialsManager();
+
+ return _oVirtCredentialsManager;
+}
diff --git a/js/gdm/realmd.js b/js/gdm/realmd.js
new file mode 100644
index 0000000..52661c1
--- /dev/null
+++ b/js/gdm/realmd.js
@@ -0,0 +1,112 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Manager */
+
+const Gio = imports.gi.Gio;
+const Signals = imports.misc.signals;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const ProviderIface = loadInterfaceXML("org.freedesktop.realmd.Provider");
+const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
+
+const ServiceIface = loadInterfaceXML("org.freedesktop.realmd.Service");
+const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
+
+const RealmIface = loadInterfaceXML("org.freedesktop.realmd.Realm");
+const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
+
+var Manager = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._aggregateProvider = Provider(Gio.DBus.system,
+ 'org.freedesktop.realmd',
+ '/org/freedesktop/realmd',
+ this._reloadRealms.bind(this));
+ this._realms = {};
+ this._loginFormat = null;
+
+ this._aggregateProvider.connectObject('g-properties-changed',
+ (proxy, properties) => {
+ const realmsChanged = !!properties.lookup_value('Realms', null);
+ if (realmsChanged)
+ this._reloadRealms();
+ }, this);
+ }
+
+ _reloadRealms() {
+ let realmPaths = this._aggregateProvider.Realms;
+
+ if (!realmPaths)
+ return;
+
+ for (let i = 0; i < realmPaths.length; i++) {
+ Realm(Gio.DBus.system,
+ 'org.freedesktop.realmd',
+ realmPaths[i],
+ this._onRealmLoaded.bind(this));
+ }
+ }
+
+ _reloadRealm(realm) {
+ if (!realm.Configured) {
+ if (this._realms[realm.get_object_path()])
+ delete this._realms[realm.get_object_path()];
+
+ return;
+ }
+
+ this._realms[realm.get_object_path()] = realm;
+
+ this._updateLoginFormat();
+ }
+
+ _onRealmLoaded(realm, error) {
+ if (error)
+ return;
+
+ this._reloadRealm(realm);
+
+ realm.connect('g-properties-changed', (proxy, properties) => {
+ const configuredChanged = !!properties.lookup_value('Configured', null);
+ if (configuredChanged)
+ this._reloadRealm(realm);
+ });
+ }
+
+ _updateLoginFormat() {
+ let newLoginFormat;
+
+ for (let realmPath in this._realms) {
+ let realm = this._realms[realmPath];
+ if (realm.LoginFormats && realm.LoginFormats.length > 0) {
+ newLoginFormat = realm.LoginFormats[0];
+ break;
+ }
+ }
+
+ if (this._loginFormat != newLoginFormat) {
+ this._loginFormat = newLoginFormat;
+ this.emit('login-format-changed', newLoginFormat);
+ }
+ }
+
+ get loginFormat() {
+ if (this._loginFormat)
+ return this._loginFormat;
+
+ this._updateLoginFormat();
+
+ return this._loginFormat;
+ }
+
+ release() {
+ Service(Gio.DBus.system,
+ 'org.freedesktop.realmd',
+ '/org/freedesktop/realmd',
+ service => service.ReleaseAsync().catch(logError));
+ this._aggregateProvider.disconnectObject(this);
+ this._realms = { };
+ this._updateLoginFormat();
+ }
+};
diff --git a/js/gdm/util.js b/js/gdm/util.js
new file mode 100644
index 0000000..8d09356
--- /dev/null
+++ b/js/gdm/util.js
@@ -0,0 +1,785 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported BANNER_MESSAGE_KEY, BANNER_MESSAGE_TEXT_KEY, LOGO_KEY,
+ DISABLE_USER_LIST_KEY, fadeInActor, fadeOutActor, cloneAndFadeOutActor,
+ ShellUserVerifier */
+
+const { Clutter, Gdm, Gio, GLib } = imports.gi;
+const Signals = imports.misc.signals;
+
+const Batch = imports.gdm.batch;
+const OVirt = imports.gdm.oVirt;
+const Vmware = imports.gdm.vmware;
+const Main = imports.ui.main;
+const { loadInterfaceXML } = imports.misc.fileUtils;
+const Params = imports.misc.params;
+const SmartcardManager = imports.misc.smartcardManager;
+
+const FprintManagerIface = loadInterfaceXML('net.reactivated.Fprint.Manager');
+const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface);
+const FprintDeviceIface = loadInterfaceXML('net.reactivated.Fprint.Device');
+const FprintDeviceProxy = Gio.DBusProxy.makeProxyWrapper(FprintDeviceIface);
+
+Gio._promisify(Gdm.Client.prototype, 'open_reauthentication_channel');
+Gio._promisify(Gdm.Client.prototype, 'get_user_verifier');
+Gio._promisify(Gdm.UserVerifierProxy.prototype,
+ 'call_begin_verification_for_user');
+Gio._promisify(Gdm.UserVerifierProxy.prototype, 'call_begin_verification');
+
+var PASSWORD_SERVICE_NAME = 'gdm-password';
+var FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
+var SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
+var FADE_ANIMATION_TIME = 160;
+var CLONE_FADE_ANIMATION_TIME = 250;
+
+var LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
+var PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
+var FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
+var SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
+var BANNER_MESSAGE_KEY = 'banner-message-enable';
+var BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
+var ALLOWED_FAILURES_KEY = 'allowed-failures';
+
+var LOGO_KEY = 'logo';
+var DISABLE_USER_LIST_KEY = 'disable-user-list';
+
+// Give user 48ms to read each character of a PAM message
+var USER_READ_TIME = 48;
+const FINGERPRINT_ERROR_TIMEOUT_WAIT = 15;
+
+var MessageType = {
+ // Keep messages in order by priority
+ NONE: 0,
+ HINT: 1,
+ INFO: 2,
+ ERROR: 3,
+};
+
+const FingerprintReaderType = {
+ NONE: 0,
+ PRESS: 1,
+ SWIPE: 2,
+};
+
+function fadeInActor(actor) {
+ if (actor.opacity == 255 && actor.visible)
+ return null;
+
+ let hold = new Batch.Hold();
+ actor.show();
+ let [, naturalHeight] = actor.get_preferred_height(-1);
+
+ actor.opacity = 0;
+ actor.set_height(0);
+ actor.ease({
+ opacity: 255,
+ height: naturalHeight,
+ duration: FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this.set_height(-1);
+ hold.release();
+ },
+ });
+
+ return hold;
+}
+
+function fadeOutActor(actor) {
+ if (!actor.visible || actor.opacity == 0) {
+ actor.opacity = 0;
+ actor.hide();
+ return null;
+ }
+
+ let hold = new Batch.Hold();
+ actor.ease({
+ opacity: 0,
+ height: 0,
+ duration: FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this.hide();
+ this.set_height(-1);
+ hold.release();
+ },
+ });
+ return hold;
+}
+
+function cloneAndFadeOutActor(actor) {
+ // Immediately hide actor so its sibling can have its space
+ // and position, but leave a non-reactive clone on-screen,
+ // so from the user's point of view it smoothly fades away
+ // and reveals its sibling.
+ actor.hide();
+
+ const clone = new Clutter.Clone({
+ source: actor,
+ reactive: false,
+ });
+
+ Main.uiGroup.add_child(clone);
+
+ let [x, y] = actor.get_transformed_position();
+ clone.set_position(x, y);
+
+ let hold = new Batch.Hold();
+ clone.ease({
+ opacity: 0,
+ duration: CLONE_FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ clone.destroy();
+ hold.release();
+ },
+ });
+ return hold;
+}
+
+var ShellUserVerifier = class extends Signals.EventEmitter {
+ constructor(client, params) {
+ super();
+ params = Params.parse(params, { reauthenticationOnly: false });
+ this._reauthOnly = params.reauthenticationOnly;
+
+ this._client = client;
+
+ this._defaultService = null;
+ this._preemptingService = null;
+
+ this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
+ this._settings.connect('changed',
+ this._updateDefaultService.bind(this));
+ this._updateDefaultService();
+
+ this._fprintManager = new FprintManagerProxy(Gio.DBus.system,
+ 'net.reactivated.Fprint',
+ '/net/reactivated/Fprint/Manager',
+ null,
+ null,
+ Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
+ this._smartcardManager = SmartcardManager.getSmartcardManager();
+
+ // We check for smartcards right away, since an inserted smartcard
+ // at startup should result in immediately initiating authentication.
+ // This is different than fingerprint readers, where we only check them
+ // after a user has been picked.
+ this.smartcardDetected = false;
+ this._checkForSmartcard();
+
+ this._smartcardManager.connectObject(
+ 'smartcard-inserted', this._checkForSmartcard.bind(this),
+ 'smartcard-removed', this._checkForSmartcard.bind(this), this);
+
+ this._messageQueue = [];
+ this._messageQueueTimeoutId = 0;
+ this.reauthenticating = false;
+
+ this._failCounter = 0;
+ this._unavailableServices = new Set();
+
+ this._credentialManagers = {};
+ this._credentialManagers[OVirt.SERVICE_NAME] = OVirt.getOVirtCredentialsManager();
+ this._credentialManagers[Vmware.SERVICE_NAME] = Vmware.getVmwareCredentialsManager();
+
+ for (let service in this._credentialManagers) {
+ if (this._credentialManagers[service].token) {
+ this._onCredentialManagerAuthenticated(this._credentialManagers[service],
+ this._credentialManagers[service].token);
+ }
+
+ this._credentialManagers[service].connectObject('user-authenticated',
+ this._onCredentialManagerAuthenticated.bind(this), this);
+ }
+ }
+
+ get hasPendingMessages() {
+ return !!this._messageQueue.length;
+ }
+
+ get allowedFailures() {
+ return this._settings.get_int(ALLOWED_FAILURES_KEY);
+ }
+
+ get currentMessage() {
+ return this._messageQueue ? this._messageQueue[0] : null;
+ }
+
+ begin(userName, hold) {
+ this._cancellable = new Gio.Cancellable();
+ this._hold = hold;
+ this._userName = userName;
+ this.reauthenticating = false;
+
+ this._checkForFingerprintReader();
+
+ // If possible, reauthenticate an already running session,
+ // so any session specific credentials get updated appropriately
+ if (userName)
+ this._openReauthenticationChannel(userName);
+ else
+ this._getUserVerifier();
+ }
+
+ cancel() {
+ if (this._cancellable)
+ this._cancellable.cancel();
+
+ if (this._userVerifier) {
+ this._userVerifier.call_cancel_sync(null);
+ this.clear();
+ }
+ }
+
+ _clearUserVerifier() {
+ if (this._userVerifier) {
+ this._disconnectSignals();
+ this._userVerifier.run_dispose();
+ this._userVerifier = null;
+ if (this._userVerifierChoiceList) {
+ this._userVerifierChoiceList.run_dispose();
+ this._userVerifierChoiceList = null;
+ }
+ }
+ }
+
+ clear() {
+ if (this._cancellable) {
+ this._cancellable.cancel();
+ this._cancellable = null;
+ }
+
+ this._clearUserVerifier();
+ this._clearMessageQueue();
+ }
+
+ destroy() {
+ this.cancel();
+
+ this._settings.run_dispose();
+ this._settings = null;
+
+ this._smartcardManager.disconnectObject(this);
+ this._smartcardManager = null;
+
+ for (let service in this._credentialManagers) {
+ let credentialManager = this._credentialManagers[service];
+ credentialManager.disconnectObject(this);
+ credentialManager = null;
+ }
+ }
+
+ selectChoice(serviceName, key) {
+ this._userVerifierChoiceList.call_select_choice(serviceName, key, this._cancellable, null);
+ }
+
+ async answerQuery(serviceName, answer) {
+ try {
+ await this._handlePendingMessages();
+ this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }
+ }
+
+ _getIntervalForMessage(message) {
+ if (!message)
+ return 0;
+
+ // We probably could be smarter here
+ return message.length * USER_READ_TIME;
+ }
+
+ finishMessageQueue() {
+ if (!this.hasPendingMessages)
+ return;
+
+ this._messageQueue = [];
+
+ this.emit('no-more-messages');
+ }
+
+ increaseCurrentMessageTimeout(interval) {
+ if (!this._messageQueueTimeoutId && interval > 0)
+ this._currentMessageExtraInterval = interval;
+ }
+
+ _serviceHasPendingMessages(serviceName) {
+ return this._messageQueue.some(m => m.serviceName === serviceName);
+ }
+
+ _filterServiceMessages(serviceName, messageType) {
+ // This function allows to remove queued messages for the @serviceName
+ // whose type has lower priority than @messageType, replacing them
+ // with a null message that will lead to clearing the prompt once done.
+ if (this._serviceHasPendingMessages(serviceName))
+ this._queuePriorityMessage(serviceName, null, messageType);
+ }
+
+ _queueMessageTimeout() {
+ if (this._messageQueueTimeoutId != 0)
+ return;
+
+ const message = this.currentMessage;
+
+ delete this._currentMessageExtraInterval;
+ this.emit('show-message', message.serviceName, message.text, message.type);
+
+ this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ message.interval + (this._currentMessageExtraInterval | 0), () => {
+ this._messageQueueTimeoutId = 0;
+
+ if (this._messageQueue.length > 1) {
+ this._messageQueue.shift();
+ this._queueMessageTimeout();
+ } else {
+ this.finishMessageQueue();
+ }
+
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._messageQueueTimeoutId, '[gnome-shell] this._queueMessageTimeout');
+ }
+
+ _queueMessage(serviceName, message, messageType) {
+ let interval = this._getIntervalForMessage(message);
+
+ this._messageQueue.push({ serviceName, text: message, type: messageType, interval });
+ this._queueMessageTimeout();
+ }
+
+ _queuePriorityMessage(serviceName, message, messageType) {
+ const newQueue = this._messageQueue.filter(m => {
+ if (m.serviceName !== serviceName || m.type >= messageType)
+ return m.text !== message;
+ return false;
+ });
+
+ if (!newQueue.includes(this.currentMessage))
+ this._clearMessageQueue();
+
+ this._messageQueue = newQueue;
+ this._queueMessage(serviceName, message, messageType);
+ }
+
+ _clearMessageQueue() {
+ this.finishMessageQueue();
+
+ if (this._messageQueueTimeoutId != 0) {
+ GLib.source_remove(this._messageQueueTimeoutId);
+ this._messageQueueTimeoutId = 0;
+ }
+ this.emit('show-message', null, null, MessageType.NONE);
+ }
+
+ async _checkForFingerprintReader() {
+ this._fingerprintReaderType = FingerprintReaderType.NONE;
+
+ if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY) ||
+ this._fprintManager == null) {
+ this._updateDefaultService();
+ return;
+ }
+
+ try {
+ const [device] = await this._fprintManager.GetDefaultDeviceAsync(
+ Gio.DBusCallFlags.NONE, this._cancellable);
+ const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system,
+ 'net.reactivated.Fprint',
+ device);
+ const fprintDeviceType = fprintDeviceProxy['scan-type'];
+
+ this._fingerprintReaderType = fprintDeviceType === 'swipe'
+ ? FingerprintReaderType.SWIPE
+ : FingerprintReaderType.PRESS;
+ this._updateDefaultService();
+ } catch (e) {}
+ }
+
+ _onCredentialManagerAuthenticated(credentialManager, _token) {
+ this._preemptingService = credentialManager.service;
+ this.emit('credential-manager-authenticated');
+ }
+
+ _checkForSmartcard() {
+ let smartcardDetected;
+
+ if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
+ smartcardDetected = false;
+ else if (this._reauthOnly)
+ smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
+ else
+ smartcardDetected = this._smartcardManager.hasInsertedTokens();
+
+ if (smartcardDetected != this.smartcardDetected) {
+ this.smartcardDetected = smartcardDetected;
+
+ if (this.smartcardDetected)
+ this._preemptingService = SMARTCARD_SERVICE_NAME;
+ else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
+ this._preemptingService = null;
+
+ this.emit('smartcard-status-changed');
+ }
+ }
+
+ _reportInitError(where, error, serviceName) {
+ logError(error, where);
+ this._hold.release();
+
+ this._queueMessage(serviceName, _('Authentication error'), MessageType.ERROR);
+ this._failCounter++;
+ this._verificationFailed(serviceName, false);
+ }
+
+ async _openReauthenticationChannel(userName) {
+ try {
+ this._clearUserVerifier();
+ this._userVerifier = await this._client.open_reauthentication_channel(
+ userName, this._cancellable);
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ return;
+ if (e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) &&
+ !this._reauthOnly) {
+ // Gdm emits org.freedesktop.DBus.Error.AccessDenied when there
+ // is no session to reauthenticate. Fall back to performing
+ // verification from this login session
+ this._getUserVerifier();
+ return;
+ }
+
+ this._reportInitError('Failed to open reauthentication channel', e);
+ return;
+ }
+
+ if (this._client.get_user_verifier_choice_list)
+ this._userVerifierChoiceList = this._client.get_user_verifier_choice_list();
+ else
+ this._userVerifierChoiceList = null;
+
+ this.reauthenticating = true;
+ this._connectSignals();
+ this._beginVerification();
+ this._hold.release();
+ }
+
+ async _getUserVerifier() {
+ try {
+ this._clearUserVerifier();
+ this._userVerifier =
+ await this._client.get_user_verifier(this._cancellable);
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ return;
+ this._reportInitError('Failed to obtain user verifier', e);
+ return;
+ }
+
+ if (this._client.get_user_verifier_choice_list)
+ this._userVerifierChoiceList = this._client.get_user_verifier_choice_list();
+ else
+ this._userVerifierChoiceList = null;
+
+ this._connectSignals();
+ this._beginVerification();
+ this._hold.release();
+ }
+
+ _connectSignals() {
+ this._disconnectSignals();
+
+ this._userVerifier.connectObject(
+ 'info', this._onInfo.bind(this),
+ 'problem', this._onProblem.bind(this),
+ 'info-query', this._onInfoQuery.bind(this),
+ 'secret-info-query', this._onSecretInfoQuery.bind(this),
+ 'conversation-stopped', this._onConversationStopped.bind(this),
+ 'service-unavailable', this._onServiceUnavailable.bind(this),
+ 'reset', this._onReset.bind(this),
+ 'verification-complete', this._onVerificationComplete.bind(this),
+ this);
+
+ if (this._userVerifierChoiceList) {
+ this._userVerifierChoiceList.connectObject('choice-query',
+ this._onChoiceListQuery.bind(this), this);
+ }
+ }
+
+ _disconnectSignals() {
+ this._userVerifier?.disconnectObject(this);
+ this._userVerifierChoiceList?.disconnectObject(this);
+ }
+
+ _getForegroundService() {
+ if (this._preemptingService)
+ return this._preemptingService;
+
+ return this._defaultService;
+ }
+
+ serviceIsForeground(serviceName) {
+ return serviceName == this._getForegroundService();
+ }
+
+ serviceIsDefault(serviceName) {
+ return serviceName == this._defaultService;
+ }
+
+ serviceIsFingerprint(serviceName) {
+ return this._fingerprintReaderType !== FingerprintReaderType.NONE &&
+ serviceName === FINGERPRINT_SERVICE_NAME;
+ }
+
+ _updateDefaultService() {
+ if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
+ this._defaultService = PASSWORD_SERVICE_NAME;
+ else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
+ this._defaultService = SMARTCARD_SERVICE_NAME;
+ else if (this._fingerprintReaderType !== FingerprintReaderType.NONE)
+ this._defaultService = FINGERPRINT_SERVICE_NAME;
+
+ if (!this._defaultService) {
+ log("no authentication service is enabled, using password authentication");
+ this._defaultService = PASSWORD_SERVICE_NAME;
+ }
+ }
+
+ async _startService(serviceName) {
+ this._hold.acquire();
+ try {
+ if (this._userName) {
+ await this._userVerifier.call_begin_verification_for_user(
+ serviceName, this._userName, this._cancellable);
+ } else {
+ await this._userVerifier.call_begin_verification(
+ serviceName, this._cancellable);
+ }
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ return;
+ if (!this.serviceIsForeground(serviceName)) {
+ logError(e,
+ `Failed to start ${serviceName} for ${this._userName}`);
+ this._hold.release();
+ return;
+ }
+ this._reportInitError(
+ this._userName
+ ? `Failed to start ${serviceName} verification for user`
+ : `Failed to start ${serviceName} verification`,
+ e, serviceName);
+ return;
+ }
+ this._hold.release();
+ }
+
+ _beginVerification() {
+ this._startService(this._getForegroundService());
+
+ if (this._userName &&
+ this._fingerprintReaderType !== FingerprintReaderType.NONE &&
+ !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
+ this._startService(FINGERPRINT_SERVICE_NAME);
+ }
+
+ _onChoiceListQuery(client, serviceName, promptMessage, list) {
+ if (!this.serviceIsForeground(serviceName))
+ return;
+
+ this.emit('show-choice-list', serviceName, promptMessage, list.deepUnpack());
+ }
+
+ _onInfo(client, serviceName, info) {
+ if (this.serviceIsForeground(serviceName)) {
+ this._queueMessage(serviceName, info, MessageType.INFO);
+ } else if (this.serviceIsFingerprint(serviceName)) {
+ // We don't show fingerprint messages directly since it's
+ // not the main auth service. Instead we use the messages
+ // as a cue to display our own message.
+ if (this._fingerprintReaderType === FingerprintReaderType.SWIPE) {
+ // Translators: this message is shown below the password entry field
+ // to indicate the user can swipe their finger on the fingerprint reader
+ this._queueMessage(serviceName, _('(or swipe finger across reader)'),
+ MessageType.HINT);
+ } else {
+ // Translators: this message is shown below the password entry field
+ // to indicate the user can place their finger on the fingerprint reader instead
+ this._queueMessage(serviceName, _('(or place finger on reader)'),
+ MessageType.HINT);
+ }
+ }
+ }
+
+ _onProblem(client, serviceName, problem) {
+ const isFingerprint = this.serviceIsFingerprint(serviceName);
+
+ if (!this.serviceIsForeground(serviceName) && !isFingerprint)
+ return;
+
+ this._queuePriorityMessage(serviceName, problem, MessageType.ERROR);
+
+ if (isFingerprint) {
+ // pam_fprintd allows the user to retry multiple (maybe even infinite!
+ // times before failing the authentication conversation.
+ // We don't want this behavior to bypass the max-tries setting the user has set,
+ // so we count the problem messages to know how many times the user has failed.
+ // Once we hit the max number of failures we allow, it's time to failure the
+ // conversation from our side. We can't do that right away, however, because
+ // we may drop pending messages coming from pam_fprintd. In order to make sure
+ // the user sees everything, we queue the failure up to get handled in the
+ // near future, after we've finished up the current round of messages.
+ this._failCounter++;
+
+ if (!this._canRetry()) {
+ if (this._fingerprintFailedId)
+ GLib.source_remove(this._fingerprintFailedId);
+
+ const cancellable = this._cancellable;
+ this._fingerprintFailedId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ FINGERPRINT_ERROR_TIMEOUT_WAIT, () => {
+ this._fingerprintFailedId = 0;
+ if (!cancellable.is_cancelled())
+ this._verificationFailed(serviceName, false);
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+ }
+
+ _onInfoQuery(client, serviceName, question) {
+ if (!this.serviceIsForeground(serviceName))
+ return;
+
+ this.emit('ask-question', serviceName, question, false);
+ }
+
+ _onSecretInfoQuery(client, serviceName, secretQuestion) {
+ if (!this.serviceIsForeground(serviceName))
+ return;
+
+ let token = null;
+ if (this._credentialManagers[serviceName])
+ token = this._credentialManagers[serviceName].token;
+
+ if (token) {
+ this.answerQuery(serviceName, token);
+ return;
+ }
+
+ this.emit('ask-question', serviceName, secretQuestion, true);
+ }
+
+ _onReset() {
+ // Clear previous attempts to authenticate
+ this._failCounter = 0;
+ this._unavailableServices.clear();
+ this._updateDefaultService();
+
+ this.emit('reset');
+ }
+
+ _onVerificationComplete() {
+ this.emit('verification-complete');
+ }
+
+ _cancelAndReset() {
+ this.cancel();
+ this._onReset();
+ }
+
+ _retry(serviceName) {
+ this._hold = new Batch.Hold();
+ this._connectSignals();
+ this._startService(serviceName);
+ }
+
+ _canRetry() {
+ return this._userName &&
+ (this._reauthOnly || this._failCounter < this.allowedFailures);
+ }
+
+ async _verificationFailed(serviceName, shouldRetry) {
+ if (serviceName === FINGERPRINT_SERVICE_NAME) {
+ if (this._fingerprintFailedId)
+ GLib.source_remove(this._fingerprintFailedId);
+ }
+
+ // For Not Listed / enterprise logins, immediately reset
+ // the dialog
+ // Otherwise, when in login mode we allow ALLOWED_FAILURES attempts.
+ // After that, we go back to the welcome screen.
+ this._filterServiceMessages(serviceName, MessageType.ERROR);
+
+ const doneTrying = !shouldRetry || !this._canRetry();
+
+ this.emit('verification-failed', serviceName, !doneTrying);
+ try {
+ if (doneTrying) {
+ this._disconnectSignals();
+ await this._handlePendingMessages();
+ this._cancelAndReset();
+ } else {
+ await this._handlePendingMessages();
+ this._retry(serviceName);
+ }
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }
+ }
+
+ _handlePendingMessages() {
+ if (!this.hasPendingMessage)
+ return Promise.resolve();
+
+ const cancellable = this._cancellable;
+ return new Promise((resolve, reject) => {
+ let signalId = this.connect('no-more-messages', () => {
+ this.disconnect(signalId);
+ if (cancellable.is_cancelled())
+ reject(new GLib.Error(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED, 'Operation was cancelled'));
+ else
+ resolve();
+ });
+ });
+ }
+
+ _onServiceUnavailable(_client, serviceName, errorMessage) {
+ this._unavailableServices.add(serviceName);
+
+ if (!errorMessage)
+ return;
+
+ if (this.serviceIsForeground(serviceName) || this.serviceIsFingerprint(serviceName))
+ this._queueMessage(serviceName, errorMessage, MessageType.ERROR);
+ }
+
+ _onConversationStopped(client, serviceName) {
+ // If the login failed with the preauthenticated oVirt credentials
+ // then discard the credentials and revert to default authentication
+ // mechanism.
+ let foregroundService = Object.keys(this._credentialManagers).find(service =>
+ this.serviceIsForeground(service));
+ if (foregroundService) {
+ this._credentialManagers[foregroundService].token = null;
+ this._preemptingService = null;
+ this._verificationFailed(serviceName, false);
+ return;
+ }
+
+ this._filterServiceMessages(serviceName, MessageType.ERROR);
+
+ if (this._unavailableServices.has(serviceName))
+ return;
+
+ // if the password service fails, then cancel everything.
+ // But if, e.g., fingerprint fails, still give
+ // password authentication a chance to succeed
+ if (this.serviceIsForeground(serviceName))
+ this._failCounter++;
+
+ this._verificationFailed(serviceName, true);
+ }
+};
diff --git a/js/gdm/vmware.js b/js/gdm/vmware.js
new file mode 100644
index 0000000..260b7c8
--- /dev/null
+++ b/js/gdm/vmware.js
@@ -0,0 +1,54 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported getVmwareCredentialsManager */
+
+const Gio = imports.gi.Gio;
+const Credential = imports.gdm.credentialManager;
+
+const dbusPath = '/org/vmware/viewagent/Credentials';
+const dbusInterface = 'org.vmware.viewagent.Credentials';
+
+var SERVICE_NAME = 'gdm-vmwcred';
+
+const VmwareCredentialsIface = `<node>
+<interface name="${dbusInterface}">
+<signal name="UserAuthenticated">
+ <arg type="s" name="token"/>
+</signal>
+</interface>
+</node>`;
+
+
+const VmwareCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(VmwareCredentialsIface);
+
+let _vmwareCredentialsManager = null;
+
+function VmwareCredentials() {
+ var self = new Gio.DBusProxy({
+ g_connection: Gio.DBus.session,
+ g_interface_name: VmwareCredentialsInfo.name,
+ g_interface_info: VmwareCredentialsInfo,
+ g_name: dbusInterface,
+ g_object_path: dbusPath,
+ g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
+ });
+ self.init(null);
+ return self;
+}
+
+var VmwareCredentialsManager = class VmwareCredentialsManager extends Credential.CredentialManager {
+ constructor() {
+ super(SERVICE_NAME);
+ this._credentials = new VmwareCredentials();
+ this._credentials.connectSignal('UserAuthenticated',
+ (proxy, sender, [token]) => {
+ this.token = token;
+ });
+ }
+};
+
+function getVmwareCredentialsManager() {
+ if (!_vmwareCredentialsManager)
+ _vmwareCredentialsManager = new VmwareCredentialsManager();
+
+ return _vmwareCredentialsManager;
+}
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
new file mode 100644
index 0000000..76b5f95
--- /dev/null
+++ b/js/js-resources.gresource.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/shell">
+ <file>gdm/authList.js</file>
+ <file>gdm/authPrompt.js</file>
+ <file>gdm/batch.js</file>
+ <file>gdm/loginDialog.js</file>
+ <file>gdm/oVirt.js</file>
+ <file>gdm/credentialManager.js</file>
+ <file>gdm/vmware.js</file>
+ <file>gdm/realmd.js</file>
+ <file>gdm/util.js</file>
+
+ <file>misc/config.js</file>
+ <file>misc/extensionUtils.js</file>
+ <file>misc/fileUtils.js</file>
+ <file>misc/dbusUtils.js</file>
+ <file>misc/gnomeSession.js</file>
+ <file>misc/history.js</file>
+ <file>misc/ibusManager.js</file>
+ <file>misc/inputMethod.js</file>
+ <file>misc/introspect.js</file>
+ <file>misc/jsParse.js</file>
+ <file>misc/keyboardManager.js</file>
+ <file>misc/loginManager.js</file>
+ <file>misc/modemManager.js</file>
+ <file>misc/objectManager.js</file>
+ <file>misc/params.js</file>
+ <file>misc/parentalControlsManager.js</file>
+ <file>misc/permissionStore.js</file>
+ <file>misc/signalTracker.js</file>
+ <file>misc/smartcardManager.js</file>
+ <file>misc/signals.js</file>
+ <file>misc/systemActions.js</file>
+ <file>misc/util.js</file>
+ <file>misc/weather.js</file>
+
+ <file>perf/basic.js</file>
+ <file>perf/core.js</file>
+ <file>perf/hwtest.js</file>
+
+ <file>ui/accessDialog.js</file>
+ <file>ui/altTab.js</file>
+ <file>ui/animation.js</file>
+ <file>ui/appDisplay.js</file>
+ <file>ui/appFavorites.js</file>
+ <file>ui/appMenu.js</file>
+ <file>ui/audioDeviceSelection.js</file>
+ <file>ui/backgroundMenu.js</file>
+ <file>ui/background.js</file>
+ <file>ui/barLevel.js</file>
+ <file>ui/boxpointer.js</file>
+ <file>ui/calendar.js</file>
+ <file>ui/checkBox.js</file>
+ <file>ui/closeDialog.js</file>
+ <file>ui/ctrlAltTab.js</file>
+ <file>ui/dash.js</file>
+ <file>ui/dateMenu.js</file>
+ <file>ui/dialog.js</file>
+ <file>ui/dnd.js</file>
+ <file>ui/edgeDragAction.js</file>
+ <file>ui/endSessionDialog.js</file>
+ <file>ui/environment.js</file>
+ <file>ui/extensionDownloader.js</file>
+ <file>ui/extensionSystem.js</file>
+ <file>ui/focusCaretTracker.js</file>
+ <file>ui/grabHelper.js</file>
+ <file>ui/ibusCandidatePopup.js</file>
+ <file>ui/iconGrid.js</file>
+ <file>ui/inhibitShortcutsDialog.js</file>
+ <file>ui/init.js</file>
+ <file>ui/kbdA11yDialog.js</file>
+ <file>ui/keyboard.js</file>
+ <file>ui/layout.js</file>
+ <file>ui/lightbox.js</file>
+ <file>ui/locatePointer.js</file>
+ <file>ui/lookingGlass.js</file>
+ <file>ui/magnifier.js</file>
+ <file>ui/main.js</file>
+ <file>ui/messageTray.js</file>
+ <file>ui/messageList.js</file>
+ <file>ui/modalDialog.js</file>
+ <file>ui/mpris.js</file>
+ <file>ui/notificationDaemon.js</file>
+ <file>ui/osdWindow.js</file>
+ <file>ui/osdMonitorLabeler.js</file>
+ <file>ui/overview.js</file>
+ <file>ui/overviewControls.js</file>
+ <file>ui/padOsd.js</file>
+ <file>ui/pageIndicators.js</file>
+ <file>ui/panel.js</file>
+ <file>ui/panelMenu.js</file>
+ <file>ui/pointerA11yTimeout.js</file>
+ <file>ui/pointerWatcher.js</file>
+ <file>ui/popupMenu.js</file>
+ <file>ui/quickSettings.js</file>
+ <file>ui/remoteSearch.js</file>
+ <file>ui/ripples.js</file>
+ <file>ui/runDialog.js</file>
+ <file>ui/screenShield.js</file>
+ <file>ui/screenshot.js</file>
+ <file>ui/scripting.js</file>
+ <file>ui/search.js</file>
+ <file>ui/searchController.js</file>
+ <file>ui/sessionMode.js</file>
+ <file>ui/shellDBus.js</file>
+ <file>ui/shellEntry.js</file>
+ <file>ui/shellMountOperation.js</file>
+ <file>ui/slider.js</file>
+ <file>ui/swipeTracker.js</file>
+ <file>ui/switcherPopup.js</file>
+ <file>ui/switchMonitor.js</file>
+ <file>ui/unlockDialog.js</file>
+ <file>ui/userWidget.js</file>
+ <file>ui/welcomeDialog.js</file>
+ <file>ui/windowAttentionHandler.js</file>
+ <file>ui/windowMenu.js</file>
+ <file>ui/windowManager.js</file>
+ <file>ui/windowPreview.js</file>
+ <file>ui/workspace.js</file>
+ <file>ui/workspaceAnimation.js</file>
+ <file>ui/workspaceSwitcherPopup.js</file>
+ <file>ui/workspaceThumbnail.js</file>
+ <file>ui/workspacesView.js</file>
+ <file>ui/xdndHandler.js</file>
+
+ <file>ui/components/__init__.js</file>
+ <file>ui/components/autorunManager.js</file>
+ <file>ui/components/automountManager.js</file>
+ <file>ui/components/networkAgent.js</file>
+ <file>ui/components/polkitAgent.js</file>
+ <file>ui/components/telepathyClient.js</file>
+ <file>ui/components/keyring.js</file>
+
+ <file>ui/status/accessibility.js</file>
+ <file>ui/status/autoRotate.js</file>
+ <file>ui/status/brightness.js</file>
+ <file>ui/status/darkMode.js</file>
+ <file>ui/status/dwellClick.js</file>
+ <file>ui/status/location.js</file>
+ <file>ui/status/keyboard.js</file>
+ <file>ui/status/nightLight.js</file>
+ <file>ui/status/network.js</file>
+ <file>ui/status/powerProfiles.js</file>
+ <file>ui/status/rfkill.js</file>
+ <file>ui/status/volume.js</file>
+ <file>ui/status/bluetooth.js</file>
+ <file>ui/status/remoteAccess.js</file>
+ <file>ui/status/system.js</file>
+ <file>ui/status/thunderbolt.js</file>
+ </gresource>
+</gresources>
diff --git a/js/meson.build b/js/meson.build
new file mode 100644
index 0000000..4809f82
--- /dev/null
+++ b/js/meson.build
@@ -0,0 +1,16 @@
+subdir('misc')
+subdir('dbusServices')
+
+js_resources = gnome.compile_resources(
+ 'js-resources', 'js-resources.gresource.xml',
+ source_dir: ['.', meson.current_build_dir()],
+ c_name: 'shell_js_resources',
+ dependencies: [config_js]
+)
+
+portal_resources = gnome.compile_resources(
+ 'portal-resources', 'portal-resources.gresource.xml',
+ source_dir: ['.', meson.current_build_dir()],
+ c_name: 'portal_js_resources',
+ dependencies: [config_js]
+)
diff --git a/js/misc/config.js.in b/js/misc/config.js.in
new file mode 100644
index 0000000..8ef4270
--- /dev/null
+++ b/js/misc/config.js.in
@@ -0,0 +1,23 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+const pkg = imports.package;
+
+/* The name of this package (not localized) */
+var PACKAGE_NAME = '@PACKAGE_NAME@';
+/* The version of this package */
+var PACKAGE_VERSION = '@PACKAGE_VERSION@';
+/* 1 if networkmanager is available, 0 otherwise */
+var HAVE_NETWORKMANAGER = @HAVE_NETWORKMANAGER@;
+/* 1 if soup2 should be used instead of soup3, 0 otherwise */
+var HAVE_SOUP2 = @HAVE_SOUP2@;
+/* gettext package */
+var GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
+/* locale dir */
+var LOCALEDIR = '@datadir@/locale';
+/* other standard directories */
+var LIBEXECDIR = '@libexecdir@';
+var PKGDATADIR = '@datadir@/@PACKAGE_NAME@';
+/* g-i package versions */
+var LIBMUTTER_API_VERSION = '@LIBMUTTER_API_VERSION@'
+
+var HAVE_BLUETOOTH = pkg.checkSymbol('GnomeBluetooth', '3.0',
+ 'Client.default_adapter_state')
diff --git a/js/misc/dbusUtils.js b/js/misc/dbusUtils.js
new file mode 100644
index 0000000..ac26894
--- /dev/null
+++ b/js/misc/dbusUtils.js
@@ -0,0 +1,68 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported loadInterfaceXML, loadSubInterfaceXML */
+
+const Config = imports.misc.config;
+const { Gio, GLib } = imports.gi;
+
+let _ifaceResource = null;
+
+/**
+ * @private
+ */
+function _ensureIfaceResource() {
+ if (_ifaceResource)
+ return;
+
+ // don't use global.datadir so the method is usable from tests/tools
+ let dir = GLib.getenv('GNOME_SHELL_DATADIR') || Config.PKGDATADIR;
+ let path = `${dir}/gnome-shell-dbus-interfaces.gresource`;
+ _ifaceResource = Gio.Resource.load(path);
+ _ifaceResource._register();
+}
+
+/**
+ * @param {string} iface the interface name
+ * @returns {string | null} the XML string or null if it is not found
+ */
+function loadInterfaceXML(iface) {
+ _ensureIfaceResource();
+
+ let uri = `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`;
+ let f = Gio.File.new_for_uri(uri);
+
+ try {
+ let [ok_, bytes] = f.load_contents(null);
+ return new TextDecoder().decode(bytes);
+ } catch (e) {
+ log(`Failed to load D-Bus interface ${iface}`);
+ }
+
+ return null;
+}
+
+/**
+ * @param {string} iface the interface name
+ * @param {string} ifaceFile the interface filename
+ * @returns {string | null} the XML string or null if it is not found
+ */
+function loadSubInterfaceXML(iface, ifaceFile) {
+ let xml = loadInterfaceXML(ifaceFile);
+ if (!xml)
+ return null;
+
+ let ifaceStartTag = `<interface name="${iface}">`;
+ let ifaceStopTag = '</interface>';
+ let ifaceStartIndex = xml.indexOf(ifaceStartTag);
+ let ifaceEndIndex = xml.indexOf(ifaceStopTag, ifaceStartIndex + 1) + ifaceStopTag.length;
+
+ let xmlHeader = '<!DOCTYPE node PUBLIC\n' +
+ '\'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\'\n' +
+ '\'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\'>\n' +
+ '<node>\n';
+ let xmlFooter = '</node>';
+
+ return (
+ xmlHeader +
+ xml.substr(ifaceStartIndex, ifaceEndIndex - ifaceStartIndex) +
+ xmlFooter);
+}
diff --git a/js/misc/extensionUtils.js b/js/misc/extensionUtils.js
new file mode 100644
index 0000000..3e89a87
--- /dev/null
+++ b/js/misc/extensionUtils.js
@@ -0,0 +1,318 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ExtensionState, ExtensionType, getCurrentExtension,
+ getSettings, initTranslations, gettext, ngettext, pgettext,
+ openPrefs, isOutOfDate, installImporter, serializeExtension,
+ deserializeExtension, setCurrentExtension */
+
+// Common utils for the extension system and the extension
+// preferences tool
+
+const { Gio, GLib } = imports.gi;
+
+const Gettext = imports.gettext;
+
+const Config = imports.misc.config;
+
+let Main = null;
+
+try {
+ Main = imports.ui.main;
+} catch (error) {
+ // Only log the error if it is not due to the
+ // missing import.
+ if (error?.name !== 'ImportError')
+ console.error(error);
+}
+
+let _extension = null;
+
+var ExtensionType = {
+ SYSTEM: 1,
+ PER_USER: 2,
+};
+
+var ExtensionState = {
+ ENABLED: 1,
+ DISABLED: 2,
+ ERROR: 3,
+ OUT_OF_DATE: 4,
+ DOWNLOADING: 5,
+ INITIALIZED: 6,
+
+ // Used as an error state for operations on unknown extensions,
+ // should never be in a real extensionMeta object.
+ UNINSTALLED: 99,
+};
+
+const SERIALIZED_PROPERTIES = [
+ 'type',
+ 'state',
+ 'path',
+ 'error',
+ 'hasPrefs',
+ 'hasUpdate',
+ 'canChange',
+];
+
+/**
+ * @param {object} extension the extension object to use in utilities like `initTranslations()`
+ */
+function setCurrentExtension(extension) {
+ if (Main)
+ throw new Error('setCurrentExtension() can only be called from outside the shell');
+
+ _extension = extension;
+}
+
+/**
+ * getCurrentExtension:
+ *
+ * @returns {?object} - The current extension, or null if not called from
+ * an extension.
+ */
+function getCurrentExtension() {
+ if (_extension)
+ return _extension;
+
+ let stack = new Error().stack.split('\n');
+ let extensionStackLine;
+
+ // Search for an occurrence of an extension stack frame
+ // Start at 1 because 0 is the stack frame of this function
+ for (let i = 1; i < stack.length; i++) {
+ if (stack[i].includes('/gnome-shell/extensions/')) {
+ extensionStackLine = stack[i];
+ break;
+ }
+ }
+ if (!extensionStackLine)
+ return null;
+
+ // The stack line is like:
+ // init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
+ //
+ // In the case that we're importing from
+ // module scope, the first field is blank:
+ // @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
+ let match = new RegExp('@(.+):\\d+').exec(extensionStackLine);
+ if (!match)
+ return null;
+
+ // local import, as the module is used from outside the gnome-shell process
+ // as well (not this function though)
+ let extensionManager = imports.ui.main.extensionManager;
+
+ let path = match[1];
+ let file = Gio.File.new_for_path(path);
+
+ // Walk up the directory tree, looking for an extension with
+ // the same UUID as a directory name.
+ while (file != null) {
+ let extension = extensionManager.lookup(file.get_basename());
+ if (extension !== undefined)
+ return extension;
+ file = file.get_parent();
+ }
+
+ return null;
+}
+
+/**
+ * initTranslations:
+ * @param {string=} domain - the gettext domain to use
+ *
+ * Initialize Gettext to load translations from extensionsdir/locale.
+ * If @domain is not provided, it will be taken from metadata['gettext-domain']
+ */
+function initTranslations(domain) {
+ let extension = getCurrentExtension();
+
+ if (!extension)
+ throw new Error('initTranslations() can only be called from extensions');
+
+ domain ||= extension.metadata['gettext-domain'];
+
+ // Expect USER extensions to have a locale/ subfolder, otherwise assume a
+ // SYSTEM extension that has been installed in the same prefix as the shell
+ let localeDir = extension.dir.get_child('locale');
+ if (localeDir.query_exists(null))
+ Gettext.bindtextdomain(domain, localeDir.get_path());
+ else
+ Gettext.bindtextdomain(domain, Config.LOCALEDIR);
+
+ Object.assign(extension, Gettext.domain(domain));
+}
+
+/**
+ * gettext:
+ * @param {string} str - the string to translate
+ *
+ * Translate @str using the extension's gettext domain
+ *
+ * @returns {string} - the translated string
+ *
+ */
+function gettext(str) {
+ return callExtensionGettextFunc('gettext', str);
+}
+
+/**
+ * ngettext:
+ * @param {string} str - the string to translate
+ * @param {string} strPlural - the plural form of the string
+ * @param {number} n - the quantity for which translation is needed
+ *
+ * Translate @str and choose plural form using the extension's
+ * gettext domain
+ *
+ * @returns {string} - the translated string
+ *
+ */
+function ngettext(str, strPlural, n) {
+ return callExtensionGettextFunc('ngettext', str, strPlural, n);
+}
+
+/**
+ * pgettext:
+ * @param {string} context - context to disambiguate @str
+ * @param {string} str - the string to translate
+ *
+ * Translate @str in the context of @context using the extension's
+ * gettext domain
+ *
+ * @returns {string} - the translated string
+ *
+ */
+function pgettext(context, str) {
+ return callExtensionGettextFunc('pgettext', context, str);
+}
+
+function callExtensionGettextFunc(func, ...args) {
+ const extension = getCurrentExtension();
+
+ if (!extension)
+ throw new Error(`${func}() can only be called from extensions`);
+
+ if (!extension[func])
+ throw new Error(`${func}() is used without calling initTranslations() first`);
+
+ return extension[func](...args);
+}
+
+/**
+ * getSettings:
+ * @param {string=} schema - the GSettings schema id
+ * @returns {Gio.Settings} - a new settings object for @schema
+ *
+ * Builds and returns a GSettings schema for @schema, using schema files
+ * in extensionsdir/schemas. If @schema is omitted, it is taken from
+ * metadata['settings-schema'].
+ */
+function getSettings(schema) {
+ let extension = getCurrentExtension();
+
+ if (!extension)
+ throw new Error('getSettings() can only be called from extensions');
+
+ schema ||= extension.metadata['settings-schema'];
+
+ const GioSSS = Gio.SettingsSchemaSource;
+
+ // Expect USER extensions to have a schemas/ subfolder, otherwise assume a
+ // SYSTEM extension that has been installed in the same prefix as the shell
+ let schemaDir = extension.dir.get_child('schemas');
+ let schemaSource;
+ if (schemaDir.query_exists(null)) {
+ schemaSource = GioSSS.new_from_directory(schemaDir.get_path(),
+ GioSSS.get_default(),
+ false);
+ } else {
+ schemaSource = GioSSS.get_default();
+ }
+
+ let schemaObj = schemaSource.lookup(schema, true);
+ if (!schemaObj)
+ throw new Error(`Schema ${schema} could not be found for extension ${extension.metadata.uuid}. Please check your installation`);
+
+ return new Gio.Settings({ settings_schema: schemaObj });
+}
+
+/**
+ * openPrefs:
+ *
+ * Open the preference dialog of the current extension
+ */
+function openPrefs() {
+ const extension = getCurrentExtension();
+
+ if (!extension)
+ throw new Error('openPrefs() can only be called from extensions');
+
+ try {
+ const extensionManager = imports.ui.main.extensionManager;
+ extensionManager.openExtensionPrefs(extension.uuid, '', {});
+ } catch (e) {
+ if (e.name === 'ImportError')
+ throw new Error('openPrefs() cannot be called from preferences');
+ logError(e, 'Failed to open extension preferences');
+ }
+}
+
+function isOutOfDate(extension) {
+ const [major] = Config.PACKAGE_VERSION.split('.');
+ return !extension.metadata['shell-version'].some(v => v.startsWith(major));
+}
+
+function serializeExtension(extension) {
+ let obj = { ...extension.metadata };
+
+ SERIALIZED_PROPERTIES.forEach(prop => {
+ obj[prop] = extension[prop];
+ });
+
+ let res = {};
+ for (let key in obj) {
+ let val = obj[key];
+ let type;
+ switch (typeof val) {
+ case 'string':
+ type = 's';
+ break;
+ case 'number':
+ type = 'd';
+ break;
+ case 'boolean':
+ type = 'b';
+ break;
+ default:
+ continue;
+ }
+ res[key] = GLib.Variant.new(type, val);
+ }
+
+ return res;
+}
+
+function deserializeExtension(variant) {
+ let res = { metadata: {} };
+ for (let prop in variant) {
+ let val = variant[prop].unpack();
+ if (SERIALIZED_PROPERTIES.includes(prop))
+ res[prop] = val;
+ else
+ res.metadata[prop] = val;
+ }
+ // add the 2 additional properties to create a valid extension object, as createExtensionObject()
+ res.uuid = res.metadata.uuid;
+ res.dir = Gio.File.new_for_path(res.path);
+ return res;
+}
+
+function installImporter(extension) {
+ let oldSearchPath = imports.searchPath.slice(); // make a copy
+ imports.searchPath = [extension.dir.get_parent().get_path()];
+ // importing a "subdir" creates a new importer object that doesn't affect
+ // the global one
+ extension.imports = imports[extension.uuid];
+ imports.searchPath = oldSearchPath;
+}
diff --git a/js/misc/fileUtils.js b/js/misc/fileUtils.js
new file mode 100644
index 0000000..a6bb182
--- /dev/null
+++ b/js/misc/fileUtils.js
@@ -0,0 +1,68 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported collectFromDatadirs, recursivelyDeleteDir,
+ recursivelyMoveDir, loadInterfaceXML, loadSubInterfaceXML */
+
+const { Gio, GLib } = imports.gi;
+
+var { loadInterfaceXML } = imports.misc.dbusUtils;
+
+function collectFromDatadirs(subdir, includeUserDir, processFile) {
+ let dataDirs = GLib.get_system_data_dirs();
+ if (includeUserDir)
+ dataDirs.unshift(GLib.get_user_data_dir());
+
+ for (let i = 0; i < dataDirs.length; i++) {
+ let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
+ let dir = Gio.File.new_for_path(path);
+
+ let fileEnum;
+ try {
+ fileEnum = dir.enumerate_children('standard::name,standard::type',
+ Gio.FileQueryInfoFlags.NONE, null);
+ } catch (e) {
+ fileEnum = null;
+ }
+ if (fileEnum != null) {
+ let info;
+ while ((info = fileEnum.next_file(null)))
+ processFile(fileEnum.get_child(info), info);
+ }
+ }
+}
+
+function recursivelyDeleteDir(dir, deleteParent) {
+ let children = dir.enumerate_children('standard::name,standard::type',
+ Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
+
+ let info;
+ while ((info = children.next_file(null)) != null) {
+ let type = info.get_file_type();
+ let child = dir.get_child(info.get_name());
+ if (type == Gio.FileType.REGULAR)
+ child.delete(null);
+ else if (type == Gio.FileType.DIRECTORY)
+ recursivelyDeleteDir(child, true);
+ }
+
+ if (deleteParent)
+ dir.delete(null);
+}
+
+function recursivelyMoveDir(srcDir, destDir) {
+ let children = srcDir.enumerate_children('standard::name,standard::type',
+ Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
+
+ if (!destDir.query_exists(null))
+ destDir.make_directory_with_parents(null);
+
+ let info;
+ while ((info = children.next_file(null)) != null) {
+ let type = info.get_file_type();
+ let srcChild = srcDir.get_child(info.get_name());
+ let destChild = destDir.get_child(info.get_name());
+ if (type == Gio.FileType.REGULAR)
+ srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
+ else if (type == Gio.FileType.DIRECTORY)
+ recursivelyMoveDir(srcChild, destChild);
+ }
+}
diff --git a/js/misc/gnomeSession.js b/js/misc/gnomeSession.js
new file mode 100644
index 0000000..487644f
--- /dev/null
+++ b/js/misc/gnomeSession.js
@@ -0,0 +1,45 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported PresenceStatus, Presence, Inhibitor, SessionManager, InhibitFlags */
+
+const Gio = imports.gi.Gio;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const PresenceIface = loadInterfaceXML('org.gnome.SessionManager.Presence');
+
+var PresenceStatus = {
+ AVAILABLE: 0,
+ INVISIBLE: 1,
+ BUSY: 2,
+ IDLE: 3,
+};
+
+var PresenceProxy = Gio.DBusProxy.makeProxyWrapper(PresenceIface);
+function Presence(initCallback, cancellable) {
+ return new PresenceProxy(Gio.DBus.session, 'org.gnome.SessionManager',
+ '/org/gnome/SessionManager/Presence', initCallback, cancellable);
+}
+
+// Note inhibitors are immutable objects, so they don't
+// change at runtime (changes always come in the form
+// of new inhibitors)
+const InhibitorIface = loadInterfaceXML('org.gnome.SessionManager.Inhibitor');
+var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
+function Inhibitor(objectPath, initCallback, cancellable) {
+ return new InhibitorProxy(Gio.DBus.session, 'org.gnome.SessionManager', objectPath, initCallback, cancellable);
+}
+
+// Not the full interface, only the methods we use
+const SessionManagerIface = loadInterfaceXML('org.gnome.SessionManager');
+var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
+function SessionManager(initCallback, cancellable) {
+ return new SessionManagerProxy(Gio.DBus.session, 'org.gnome.SessionManager', '/org/gnome/SessionManager', initCallback, cancellable);
+}
+
+var InhibitFlags = {
+ LOGOUT: 1 << 0,
+ SWITCH: 1 << 1,
+ SUSPEND: 1 << 2,
+ IDLE: 1 << 3,
+ AUTOMOUNT: 1 << 4,
+};
diff --git a/js/misc/history.js b/js/misc/history.js
new file mode 100644
index 0000000..268f13b
--- /dev/null
+++ b/js/misc/history.js
@@ -0,0 +1,114 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported HistoryManager */
+
+const Signals = imports.misc.signals;
+const Clutter = imports.gi.Clutter;
+const Params = imports.misc.params;
+
+var DEFAULT_LIMIT = 512;
+
+var HistoryManager = class extends Signals.EventEmitter {
+ constructor(params) {
+ super();
+
+ params = Params.parse(params, {
+ gsettingsKey: null,
+ limit: DEFAULT_LIMIT,
+ entry: null,
+ });
+
+ this._key = params.gsettingsKey;
+ this._limit = params.limit;
+
+ this._historyIndex = 0;
+ if (this._key) {
+ this._history = global.settings.get_strv(this._key);
+ global.settings.connect(`changed::${this._key}`,
+ this._historyChanged.bind(this));
+ } else {
+ this._history = [];
+ }
+
+ this._entry = params.entry;
+
+ if (this._entry) {
+ this._entry.connect('key-press-event',
+ this._onEntryKeyPress.bind(this));
+ }
+ }
+
+ _historyChanged() {
+ this._history = global.settings.get_strv(this._key);
+ this._historyIndex = this._history.length;
+ }
+
+ _setPrevItem(text) {
+ if (this._historyIndex <= 0)
+ return false;
+
+ if (text)
+ this._history[this._historyIndex] = text;
+ this._historyIndex--;
+ this._indexChanged();
+ return true;
+ }
+
+ _setNextItem(text) {
+ if (this._historyIndex >= this._history.length)
+ return false;
+
+ if (text)
+ this._history[this._historyIndex] = text;
+ this._historyIndex++;
+ this._indexChanged();
+ return true;
+ }
+
+ lastItem() {
+ if (this._historyIndex !== this._history.length) {
+ this._historyIndex = this._history.length;
+ this._indexChanged();
+ }
+
+ return this._historyIndex ? this._history[this._historyIndex - 1] : null;
+ }
+
+ addItem(input) {
+ input = input.trim();
+ if (input &&
+ (this._history.length === 0 ||
+ this._history[this._history.length - 1] !== input)) {
+ this._history = this._history.filter(entry => entry !== input);
+ this._history.push(input);
+ this._save();
+ }
+ this._historyIndex = this._history.length;
+ return input; // trimmed
+ }
+
+ _onEntryKeyPress(entry, event) {
+ let symbol = event.get_key_symbol();
+ if (symbol === Clutter.KEY_Up)
+ return this._setPrevItem(entry.get_text().trim());
+ else if (symbol === Clutter.KEY_Down)
+ return this._setNextItem(entry.get_text().trim());
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _indexChanged() {
+ let current = this._history[this._historyIndex] || '';
+ this.emit('changed', current);
+
+ if (this._entry)
+ this._entry.set_text(current);
+ }
+
+ _save() {
+ if (this._history.length > this._limit)
+ this._history.splice(0, this._history.length - this._limit);
+
+ if (this._key)
+ global.settings.set_strv(this._key, this._history);
+ }
+};
diff --git a/js/misc/ibusManager.js b/js/misc/ibusManager.js
new file mode 100644
index 0000000..214db55
--- /dev/null
+++ b/js/misc/ibusManager.js
@@ -0,0 +1,398 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported getIBusManager */
+
+const { Gio, GLib, IBus, Meta, Shell } = imports.gi;
+const Signals = imports.misc.signals;
+const BoxPointer = imports.ui.boxpointer;
+
+const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
+
+Gio._promisify(IBus.Bus.prototype,
+ 'list_engines_async', 'list_engines_async_finish');
+Gio._promisify(IBus.Bus.prototype,
+ 'request_name_async', 'request_name_async_finish');
+Gio._promisify(IBus.Bus.prototype,
+ 'get_global_engine_async', 'get_global_engine_async_finish');
+Gio._promisify(IBus.Bus.prototype,
+ 'set_global_engine_async', 'set_global_engine_async_finish');
+Gio._promisify(Shell, 'util_systemd_unit_exists');
+
+// Ensure runtime version matches
+_checkIBusVersion(1, 5, 2);
+
+let _ibusManager = null;
+const IBUS_SYSTEMD_SERVICE = 'org.freedesktop.IBus.session.GNOME.service';
+
+const TYPING_BOOSTER_ENGINE = 'typing-booster';
+const IBUS_TYPING_BOOSTER_SCHEMA = 'org.freedesktop.ibus.engine.typing-booster';
+const KEY_EMOJIPREDICTIONS = 'emojipredictions';
+const KEY_DICTIONARY = 'dictionary';
+const KEY_INLINECOMPLETION = 'inlinecompletion';
+const KEY_INPUTMETHOD = 'inputmethod';
+
+function _checkIBusVersion(requiredMajor, requiredMinor, requiredMicro) {
+ if ((IBus.MAJOR_VERSION > requiredMajor) ||
+ (IBus.MAJOR_VERSION == requiredMajor && IBus.MINOR_VERSION > requiredMinor) ||
+ (IBus.MAJOR_VERSION == requiredMajor && IBus.MINOR_VERSION == requiredMinor &&
+ IBus.MICRO_VERSION >= requiredMicro))
+ return;
+
+ throw new Error(`Found IBus version ${
+ IBus.MAJOR_VERSION}.${IBus.MINOR_VERSION}.${IBus.MINOR_VERSION} ` +
+ `but required is ${requiredMajor}.${requiredMinor}.${requiredMicro}`);
+}
+
+function getIBusManager() {
+ if (_ibusManager == null)
+ _ibusManager = new IBusManager();
+ return _ibusManager;
+}
+
+var IBusManager = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ IBus.init();
+
+ // This is the longest we'll keep the keyboard frozen until an input
+ // source is active.
+ this._MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
+ this._PRELOAD_ENGINES_DELAY_TIME = 30; // sec
+
+
+ this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
+
+ this._panelService = null;
+ this._engines = new Map();
+ this._ready = false;
+ this._registerPropertiesId = 0;
+ this._currentEngineName = null;
+ this._preloadEnginesId = 0;
+
+ this._ibus = IBus.Bus.new_async();
+ this._ibus.connect('connected', this._onConnected.bind(this));
+ this._ibus.connect('disconnected', this._clear.bind(this));
+ // Need to set this to get 'global-engine-changed' emitions
+ this._ibus.set_watch_ibus_signal(true);
+ this._ibus.connect('global-engine-changed', this._engineChanged.bind(this));
+
+ this._queueSpawn();
+ }
+
+ async _ibusSystemdServiceExists() {
+ if (this._ibusIsSystemdService)
+ return true;
+
+ try {
+ this._ibusIsSystemdService =
+ await Shell.util_systemd_unit_exists(
+ IBUS_SYSTEMD_SERVICE, null);
+ } catch (e) {
+ this._ibusIsSystemdService = false;
+ }
+
+ return this._ibusIsSystemdService;
+ }
+
+ async _queueSpawn() {
+ const isSystemdService = await this._ibusSystemdServiceExists();
+ if (!isSystemdService)
+ this._spawn(Meta.is_wayland_compositor() ? [] : ['--xim']);
+ }
+
+ _tryAppendEnv(env, varname) {
+ const value = GLib.getenv(varname);
+ if (value)
+ env.push(`${varname}=${value}`);
+ }
+
+ _spawn(extraArgs = []) {
+ try {
+ const cmdLine = ['ibus-daemon', '--panel', 'disable', ...extraArgs];
+ const launchContext = global.create_app_launch_context(0, -1);
+ const env = launchContext.get_environment();
+ // Use DO_NOT_REAP_CHILD to avoid adouble-fork internally
+ // since ibus-daemon refuses to start with init as its parent.
+ const [success_, pid] = GLib.spawn_async(
+ null, cmdLine, env,
+ GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
+ () => {
+ try {
+ global.context.restore_rlimit_nofile();
+ } catch (err) {
+ }
+ }
+ );
+ GLib.child_watch_add(
+ GLib.PRIORITY_DEFAULT,
+ pid,
+ () => GLib.spawn_close_pid(pid)
+ );
+ } catch (e) {
+ log(`Failed to launch ibus-daemon: ${e.message}`);
+ }
+ }
+
+ async restartDaemon(extraArgs = []) {
+ const isSystemdService = await this._ibusSystemdServiceExists();
+ if (!isSystemdService)
+ this._spawn(['-r', ...extraArgs]);
+ }
+
+ _clear() {
+ if (this._cancellable) {
+ this._cancellable.cancel();
+ this._cancellable = null;
+ }
+
+ if (this._preloadEnginesId) {
+ GLib.source_remove(this._preloadEnginesId);
+ this._preloadEnginesId = 0;
+ }
+
+ if (this._panelService)
+ this._panelService.destroy();
+
+ this._panelService = null;
+ this._candidatePopup.setPanelService(null);
+ this._engines.clear();
+ this._ready = false;
+ this._registerPropertiesId = 0;
+ this._currentEngineName = null;
+
+ this.emit('ready', false);
+ }
+
+ _onConnected() {
+ this._cancellable = new Gio.Cancellable();
+ this._initEngines();
+ this._initPanelService();
+ }
+
+ async _initEngines() {
+ try {
+ const enginesList =
+ await this._ibus.list_engines_async(-1, this._cancellable);
+ for (let i = 0; i < enginesList.length; ++i) {
+ let name = enginesList[i].get_name();
+ this._engines.set(name, enginesList[i]);
+ }
+ this._updateReadiness();
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ return;
+
+ logError(e);
+ this._clear();
+ }
+ }
+
+ async _initPanelService() {
+ try {
+ await this._ibus.request_name_async(IBus.SERVICE_PANEL,
+ IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e);
+ this._clear();
+ }
+ return;
+ }
+
+ this._panelService = new IBus.PanelService({
+ connection: this._ibus.get_connection(),
+ object_path: IBus.PATH_PANEL,
+ });
+ this._candidatePopup.setPanelService(this._panelService);
+ this._panelService.connect('update-property', this._updateProperty.bind(this));
+ this._panelService.connect('set-cursor-location', (ps, x, y, w, h) => {
+ let cursorLocation = { x, y, width: w, height: h };
+ this.emit('set-cursor-location', cursorLocation);
+ });
+ this._panelService.connect('focus-in', (panel, path) => {
+ if (!GLib.str_has_suffix(path, '/InputContext_1'))
+ this.emit('focus-in');
+ });
+ this._panelService.connect('focus-out', () => this.emit('focus-out'));
+
+ try {
+ // IBus versions older than 1.5.10 have a bug which
+ // causes spurious set-content-type emissions when
+ // switching input focus that temporarily lose purpose
+ // and hints defeating its intended semantics and
+ // confusing users. We thus don't use it in that case.
+ _checkIBusVersion(1, 5, 10);
+ this._panelService.connect('set-content-type', this._setContentType.bind(this));
+ } catch (e) {
+ }
+ this._updateReadiness();
+
+ try {
+ // If an engine is already active we need to get its properties
+ const engine =
+ await this._ibus.get_global_engine_async(-1, this._cancellable);
+ this._engineChanged(this._ibus, engine.get_name());
+ } catch (e) {
+ }
+ }
+
+ _updateReadiness() {
+ this._ready = this._engines.size > 0 && this._panelService != null;
+ this.emit('ready', this._ready);
+ }
+
+ _engineChanged(bus, engineName) {
+ if (!this._ready)
+ return;
+
+ this._currentEngineName = engineName;
+ this._candidatePopup.close(BoxPointer.PopupAnimation.NONE);
+
+ if (this._registerPropertiesId != 0)
+ return;
+
+ this._registerPropertiesId =
+ this._panelService.connect('register-properties', (p, props) => {
+ if (!props.get(0))
+ return;
+
+ this._panelService.disconnect(this._registerPropertiesId);
+ this._registerPropertiesId = 0;
+
+ this.emit('properties-registered', this._currentEngineName, props);
+ });
+ }
+
+ _updateProperty(panel, prop) {
+ this.emit('property-updated', this._currentEngineName, prop);
+ }
+
+ _setContentType(panel, purpose, hints) {
+ this.emit('set-content-type', purpose, hints);
+ }
+
+ activateProperty(key, state) {
+ this._panelService.property_activate(key, state);
+ }
+
+ getEngineDesc(id) {
+ if (!this._ready || !this._engines.has(id))
+ return null;
+
+ return this._engines.get(id);
+ }
+
+ async _setEngine(id, callback) {
+ // Send id even if id == this._currentEngineName
+ // because 'properties-registered' signal can be emitted
+ // while this._ibusSources == null on a lock screen.
+ if (!this._ready) {
+ if (callback)
+ callback();
+ return;
+ }
+
+ try {
+ await this._ibus.set_global_engine_async(id,
+ this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
+ this._cancellable);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }
+
+ if (callback)
+ callback();
+ }
+
+ async setEngine(id, callback) {
+ if (this._preOskState)
+ this._preOskState.engine = id;
+
+ const isXkb = id.startsWith('xkb:');
+ if (this._oskCompletion && isXkb)
+ return;
+
+ if (this._oskCompletion)
+ this.setCompletionEnabled(false, callback);
+ else
+ await this._setEngine(id, callback);
+ }
+
+ preloadEngines(ids) {
+ if (!this._ibus || !this._ready)
+ return;
+
+ if (!ids.includes(TYPING_BOOSTER_ENGINE))
+ ids.push(TYPING_BOOSTER_ENGINE);
+
+ if (this._preloadEnginesId != 0) {
+ GLib.source_remove(this._preloadEnginesId);
+ this._preloadEnginesId = 0;
+ }
+
+ this._preloadEnginesId =
+ GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT,
+ this._PRELOAD_ENGINES_DELAY_TIME,
+ () => {
+ this._ibus.preload_engines_async(
+ ids,
+ -1,
+ this._cancellable,
+ null);
+ this._preloadEnginesId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ setCompletionEnabled(enabled, callback) {
+ /* Needs typing-booster available */
+ if (enabled && !this._engines.has(TYPING_BOOSTER_ENGINE))
+ return false;
+ /* Can do only on xkb engines */
+ if (enabled && !this._currentEngineName.startsWith('xkb:'))
+ return false;
+
+ if (this._oskCompletion === enabled)
+ return true;
+
+ this._oskCompletion = enabled;
+ let settings =
+ new Gio.Settings({schema_id: IBUS_TYPING_BOOSTER_SCHEMA});
+
+ if (enabled) {
+ this._preOskState = {
+ 'engine': this._currentEngineName,
+ 'emoji': settings.get_value(KEY_EMOJIPREDICTIONS),
+ 'langs': settings.get_value(KEY_DICTIONARY),
+ 'completion': settings.get_value(KEY_INLINECOMPLETION),
+ 'inputMethod': settings.get_value(KEY_INPUTMETHOD),
+ };
+ settings.reset(KEY_EMOJIPREDICTIONS);
+
+ const removeEncoding = l => l.replace(/\..*/, '');
+ const removeDups = (l, pos, arr) => {
+ return !pos || arr[pos - 1] !== l;
+ };
+ settings.set_string(
+ KEY_DICTIONARY,
+ GLib.get_language_names().map(removeEncoding)
+ .sort().filter(removeDups).join(','));
+
+ settings.reset(KEY_INLINECOMPLETION);
+ settings.set_string(KEY_INPUTMETHOD, 'NoIME');
+ this._setEngine(TYPING_BOOSTER_ENGINE, callback);
+ } else if (this._preOskState) {
+ const {engine, emoji, langs, completion, inputMethod} =
+ this._preOskState;
+ this._preOskState = null;
+ this._setEngine(engine, callback);
+ settings.set_value(KEY_EMOJIPREDICTIONS, emoji);
+ settings.set_value(KEY_DICTIONARY, langs);
+ settings.set_value(KEY_INLINECOMPLETION, completion);
+ settings.set_value(KEY_INPUTMETHOD, inputMethod);
+ }
+ return true;
+ }
+};
diff --git a/js/misc/inputMethod.js b/js/misc/inputMethod.js
new file mode 100644
index 0000000..e01eac8
--- /dev/null
+++ b/js/misc/inputMethod.js
@@ -0,0 +1,386 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported InputMethod */
+const { Clutter, GLib, Gio, GObject, IBus } = imports.gi;
+
+const Keyboard = imports.ui.status.keyboard;
+const Main = imports.ui.main;
+
+Gio._promisify(IBus.Bus.prototype,
+ 'create_input_context_async', 'create_input_context_async_finish');
+Gio._promisify(IBus.InputContext.prototype,
+ 'process_key_event_async', 'process_key_event_async_finish');
+
+var HIDE_PANEL_TIME = 50;
+
+const HAVE_REQUIRE_SURROUNDING_TEXT = GObject.signal_lookup('require-surrounding-text', IBus.InputContext);
+
+var InputMethod = GObject.registerClass({
+ Signals: {
+ 'surrounding-text-set': {},
+ 'terminal-mode-changed': {},
+ },
+}, class InputMethod extends Clutter.InputMethod {
+ _init() {
+ super._init();
+ this._hints = 0;
+ this._purpose = 0;
+ this._currentFocus = null;
+ this._preeditStr = '';
+ this._preeditPos = 0;
+ this._preeditAnchor = 0;
+ this._preeditVisible = false;
+ this._hidePanelId = 0;
+ this.terminalMode = false;
+ this._ibus = IBus.Bus.new_async();
+ this._ibus.connect('connected', this._onConnected.bind(this));
+ this._ibus.connect('disconnected', this._clear.bind(this));
+ this.connect('notify::can-show-preedit', this._updateCapabilities.bind(this));
+
+ this._inputSourceManager = Keyboard.getInputSourceManager();
+ this._sourceChangedId = this._inputSourceManager.connect('current-source-changed',
+ this._onSourceChanged.bind(this));
+ this._currentSource = this._inputSourceManager.currentSource;
+
+ if (this._ibus.is_connected())
+ this._onConnected();
+ }
+
+ get currentFocus() {
+ return this._currentFocus;
+ }
+
+ _updateCapabilities() {
+ let caps = IBus.Capabilite.PREEDIT_TEXT | IBus.Capabilite.FOCUS | IBus.Capabilite.SURROUNDING_TEXT;
+
+ if (Main.keyboard.visible)
+ caps |= IBus.Capabilite.OSK;
+
+ if (this._context)
+ this._context.set_capabilities(caps);
+ }
+
+ _onSourceChanged() {
+ this._currentSource = this._inputSourceManager.currentSource;
+ }
+
+ async _onConnected() {
+ this._cancellable = new Gio.Cancellable();
+ try {
+ this._context = await this._ibus.create_input_context_async(
+ 'gnome-shell', -1, this._cancellable);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e);
+ this._clear();
+ }
+ return;
+ }
+
+ this._context.set_client_commit_preedit(true);
+ this._context.connect('commit-text', this._onCommitText.bind(this));
+ this._context.connect('delete-surrounding-text', this._onDeleteSurroundingText.bind(this));
+ this._context.connect('update-preedit-text-with-mode', this._onUpdatePreeditText.bind(this));
+ this._context.connect('show-preedit-text', this._onShowPreeditText.bind(this));
+ this._context.connect('hide-preedit-text', this._onHidePreeditText.bind(this));
+ this._context.connect('forward-key-event', this._onForwardKeyEvent.bind(this));
+ this._context.connect('destroy', this._clear.bind(this));
+
+ if (HAVE_REQUIRE_SURROUNDING_TEXT)
+ this._context.connect('require-surrounding-text', this._onRequireSurroundingText.bind(this));
+
+ Main.keyboard.connectObject('visibility-changed', () => this._updateCapabilities());
+
+ this._updateCapabilities();
+ }
+
+ _clear() {
+ Main.keyboard.disconnectObject(this);
+
+ if (this._cancellable) {
+ this._cancellable.cancel();
+ this._cancellable = null;
+ }
+
+ this._context = null;
+ this._hints = 0;
+ this._purpose = 0;
+ this._preeditStr = '';
+ this._preeditPos = 0;
+ this._preeditAnchor = 0;
+ this._preeditVisible = false;
+ }
+
+ _emitRequestSurrounding() {
+ if (this._context.needs_surrounding_text())
+ this.emit('request-surrounding');
+ }
+
+ _onCommitText(_context, text) {
+ this.commit(text.get_text());
+ }
+
+ _onRequireSurroundingText(_context) {
+ this.request_surrounding();
+ }
+
+ _onDeleteSurroundingText(_context, offset, nchars) {
+ try {
+ this.delete_surrounding(offset, nchars);
+ } catch (e) {
+ // We may get out of bounds for negative offset on older mutter
+ this.delete_surrounding(0, nchars + offset);
+ }
+ }
+
+ _onUpdatePreeditText(_context, text, pos, visible, mode) {
+ if (text == null)
+ return;
+
+ let preedit = text.get_text();
+ if (preedit === '')
+ preedit = null;
+
+ const anchor = pos;
+ if (visible)
+ this.set_preedit_text(preedit, pos, anchor, mode);
+ else if (this._preeditVisible)
+ this.set_preedit_text(null, pos, anchor, mode);
+
+ this._preeditStr = preedit;
+ this._preeditPos = pos;
+ this._preeditAnchor = anchor;
+ this._preeditVisible = visible;
+ this._preeditCommitMode = mode;
+ }
+
+ _onShowPreeditText() {
+ this._preeditVisible = true;
+ this.set_preedit_text(
+ this._preeditStr, this._preeditPos, this._preeditAnchor,
+ this._preeditCommitMode);
+ }
+
+ _onHidePreeditText() {
+ this.set_preedit_text(
+ null, this._preeditPos, this._preeditAnchor,
+ this._preeditCommitMode);
+ this._preeditVisible = false;
+ }
+
+ _onForwardKeyEvent(_context, keyval, keycode, state) {
+ let press = (state & IBus.ModifierType.RELEASE_MASK) == 0;
+ state &= ~IBus.ModifierType.RELEASE_MASK;
+
+ let curEvent = Clutter.get_current_event();
+ let time;
+ if (curEvent)
+ time = curEvent.get_time();
+ else
+ time = global.display.get_current_time_roundtrip();
+
+ this.forward_key(keyval, keycode + 8, state & Clutter.ModifierType.MODIFIER_MASK, time, press);
+ }
+
+ vfunc_focus_in(focus) {
+ this._currentFocus = focus;
+ if (this._context) {
+ this.update();
+ this._context.focus_in();
+ this._emitRequestSurrounding();
+ }
+
+ if (this._hidePanelId) {
+ GLib.source_remove(this._hidePanelId);
+ this._hidePanelId = 0;
+ }
+ }
+
+ vfunc_focus_out() {
+ this._currentFocus = null;
+ if (this._context) {
+ this._fullReset();
+ this._context.focus_out();
+ }
+
+ if (this._preeditStr && this._preeditVisible) {
+ // Unset any preedit text
+ this.set_preedit_text(null, 0, 0, this._preeditCommitMode);
+ this._preeditStr = null;
+ }
+
+ this._hidePanelId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, HIDE_PANEL_TIME, () => {
+ this.set_input_panel_state(Clutter.InputPanelState.OFF);
+ this._hidePanelId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ vfunc_reset() {
+ if (this._context) {
+ this._context.reset();
+ this._emitRequestSurrounding();
+ }
+
+ this._surroundingText = null;
+ this._surroundingTextCursor = null;
+ this._preeditStr = null;
+ this._setTerminalMode(false);
+ }
+
+ vfunc_set_cursor_location(rect) {
+ if (this._context) {
+ this._cursorRect = {
+ x: rect.get_x(), y: rect.get_y(),
+ width: rect.get_width(), height: rect.get_height(),
+ };
+ this._context.set_cursor_location(
+ this._cursorRect.x, this._cursorRect.y,
+ this._cursorRect.width, this._cursorRect.height);
+ this._emitRequestSurrounding();
+ }
+ }
+
+ vfunc_set_surrounding(text, cursor, anchor) {
+ this._surroundingText = text;
+ this._surroundingTextCursor = cursor;
+ this.emit('surrounding-text-set');
+
+ if (!this._context || (!text && text !== ''))
+ return;
+
+ let ibusText = IBus.Text.new_from_string(text);
+ this._context.set_surrounding_text(ibusText, cursor, anchor);
+ }
+
+ vfunc_update_content_hints(hints) {
+ let ibusHints = 0;
+ if (hints & Clutter.InputContentHintFlags.COMPLETION)
+ ibusHints |= IBus.InputHints.WORD_COMPLETION;
+ if (hints & Clutter.InputContentHintFlags.SPELLCHECK)
+ ibusHints |= IBus.InputHints.SPELLCHECK;
+ if (hints & Clutter.InputContentHintFlags.AUTO_CAPITALIZATION)
+ ibusHints |= IBus.InputHints.UPPERCASE_SENTENCES;
+ if (hints & Clutter.InputContentHintFlags.LOWERCASE)
+ ibusHints |= IBus.InputHints.LOWERCASE;
+ if (hints & Clutter.InputContentHintFlags.UPPERCASE)
+ ibusHints |= IBus.InputHints.UPPERCASE_CHARS;
+ if (hints & Clutter.InputContentHintFlags.TITLECASE)
+ ibusHints |= IBus.InputHints.UPPERCASE_WORDS;
+
+ this._hints = ibusHints;
+ if (this._context)
+ this._context.set_content_type(this._purpose, this._hints);
+ }
+
+ vfunc_update_content_purpose(purpose) {
+ let ibusPurpose = 0;
+ if (purpose == Clutter.InputContentPurpose.NORMAL)
+ ibusPurpose = IBus.InputPurpose.FREE_FORM;
+ else if (purpose == Clutter.InputContentPurpose.ALPHA)
+ ibusPurpose = IBus.InputPurpose.ALPHA;
+ else if (purpose == Clutter.InputContentPurpose.DIGITS)
+ ibusPurpose = IBus.InputPurpose.DIGITS;
+ else if (purpose == Clutter.InputContentPurpose.NUMBER)
+ ibusPurpose = IBus.InputPurpose.NUMBER;
+ else if (purpose == Clutter.InputContentPurpose.PHONE)
+ ibusPurpose = IBus.InputPurpose.PHONE;
+ else if (purpose == Clutter.InputContentPurpose.URL)
+ ibusPurpose = IBus.InputPurpose.URL;
+ else if (purpose == Clutter.InputContentPurpose.EMAIL)
+ ibusPurpose = IBus.InputPurpose.EMAIL;
+ else if (purpose == Clutter.InputContentPurpose.NAME)
+ ibusPurpose = IBus.InputPurpose.NAME;
+ else if (purpose == Clutter.InputContentPurpose.PASSWORD)
+ ibusPurpose = IBus.InputPurpose.PASSWORD;
+ else if (purpose === Clutter.InputContentPurpose.TERMINAL &&
+ IBus.InputPurpose.TERMINAL)
+ ibusPurpose = IBus.InputPurpose.TERMINAL;
+
+ this._setTerminalMode(
+ purpose === Clutter.InputContentPurpose.TERMINAL);
+
+ this._purpose = ibusPurpose;
+ if (this._context)
+ this._context.set_content_type(this._purpose, this._hints);
+ }
+
+ _setTerminalMode(terminalMode) {
+ if (this.terminalMode !== terminalMode) {
+ this.terminalMode = terminalMode;
+ this.emit('terminal-mode-changed');
+ }
+ }
+
+ vfunc_filter_key_event(event) {
+ if (!this._context)
+ return false;
+ if (!this._currentSource)
+ return false;
+
+ let state = event.get_state();
+ if (state & IBus.ModifierType.IGNORED_MASK)
+ return false;
+
+ if (event.type() == Clutter.EventType.KEY_RELEASE)
+ state |= IBus.ModifierType.RELEASE_MASK;
+
+ this._context.process_key_event_async(
+ event.get_key_symbol(),
+ event.get_key_code() - 8, // Convert XKB keycodes to evcodes
+ state, -1, this._cancellable,
+ (context, res) => {
+ if (context != this._context)
+ return;
+
+ try {
+ let retval = context.process_key_event_async_finish(res);
+ this.notify_key_event(event, retval);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ log(`Error processing key on IM: ${e.message}`);
+ }
+ });
+ return true;
+ }
+
+ getSurroundingText() {
+ return [this._surroundingText, this._surroundingTextCursor];
+ }
+
+ hasPreedit() {
+ return this._preeditVisible && this._preeditStr !== '' && this._preeditStr !== null;
+ }
+
+ async handleVirtualKey(keyval) {
+ try {
+ if (!await this._context.process_key_event_async(
+ keyval, 0, 0, -1, null))
+ return false;
+
+ await this._context.process_key_event_async(
+ keyval, 0, IBus.ModifierType.RELEASE_MASK, -1, null);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ _fullReset() {
+ this._context.set_content_type(0, 0);
+ this._context.set_cursor_location(0, 0, 0, 0);
+ this._context.reset();
+ }
+
+ update() {
+ if (!this._context)
+ return;
+ this._updateCapabilities();
+ this._context.set_content_type(this._purpose, this._hints);
+ if (this._cursorRect) {
+ this._context.set_cursor_location(
+ this._cursorRect.x, this._cursorRect.y,
+ this._cursorRect.width, this._cursorRect.height);
+ }
+ this._emitRequestSurrounding();
+ }
+});
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
new file mode 100644
index 0000000..8916804
--- /dev/null
+++ b/js/misc/introspect.js
@@ -0,0 +1,217 @@
+/* exported IntrospectService */
+const { Gio, GLib, Meta, Shell, St } = imports.gi;
+
+const APP_ALLOWLIST = [
+ 'org.freedesktop.impl.portal.desktop.gtk',
+ 'org.freedesktop.impl.portal.desktop.gnome',
+];
+
+const INTROSPECT_DBUS_API_VERSION = 3;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+const { DBusSenderChecker } = imports.misc.util;
+
+const IntrospectDBusIface = loadInterfaceXML('org.gnome.Shell.Introspect');
+
+var IntrospectService = class {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(IntrospectDBusIface,
+ this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Introspect');
+ Gio.DBus.session.own_name('org.gnome.Shell.Introspect',
+ Gio.BusNameOwnerFlags.REPLACE,
+ null, null);
+
+ this._runningApplications = {};
+ this._runningApplicationsDirty = true;
+ this._activeApplication = null;
+ this._activeApplicationDirty = true;
+ this._animationsEnabled = true;
+
+ this._appSystem = Shell.AppSystem.get_default();
+ this._appSystem.connect('app-state-changed',
+ () => {
+ this._runningApplicationsDirty = true;
+ this._syncRunningApplications();
+ });
+
+ let tracker = Shell.WindowTracker.get_default();
+ tracker.connect('notify::focus-app',
+ () => {
+ this._activeApplicationDirty = true;
+ this._syncRunningApplications();
+ });
+
+ tracker.connect('tracked-windows-changed',
+ () => this._dbusImpl.emit_signal('WindowsChanged', null));
+
+ this._syncRunningApplications();
+
+ this._senderChecker = new DBusSenderChecker(APP_ALLOWLIST);
+
+ this._settings = St.Settings.get();
+ this._settings.connect('notify::enable-animations',
+ this._syncAnimationsEnabled.bind(this));
+ this._syncAnimationsEnabled();
+
+ const monitorManager = Meta.MonitorManager.get();
+ monitorManager.connect('monitors-changed',
+ this._syncScreenSize.bind(this));
+ this._syncScreenSize();
+ }
+
+ _isStandaloneApp(app) {
+ return app.get_windows().some(w => w.transient_for == null);
+ }
+
+ _getSandboxedAppId(app) {
+ let ids = app.get_windows().map(w => w.get_sandboxed_app_id());
+ return ids.find(id => id != null);
+ }
+
+ _syncRunningApplications() {
+ let tracker = Shell.WindowTracker.get_default();
+ let apps = this._appSystem.get_running();
+ let seatName = "seat0";
+ let newRunningApplications = {};
+
+ let newActiveApplication = null;
+ let focusedApp = tracker.focus_app;
+
+ for (let app of apps) {
+ let appInfo = {};
+ let isAppActive = focusedApp == app;
+
+ if (!this._isStandaloneApp(app))
+ continue;
+
+ if (isAppActive) {
+ appInfo['active-on-seats'] = new GLib.Variant('as', [seatName]);
+ newActiveApplication = app.get_id();
+ }
+
+ let sandboxedAppId = this._getSandboxedAppId(app);
+ if (sandboxedAppId)
+ appInfo['sandboxed-app-id'] = new GLib.Variant('s', sandboxedAppId);
+
+ newRunningApplications[app.get_id()] = appInfo;
+ }
+
+ if (this._runningApplicationsDirty ||
+ (this._activeApplicationDirty &&
+ this._activeApplication != newActiveApplication)) {
+ this._runningApplications = newRunningApplications;
+ this._activeApplication = newActiveApplication;
+
+ this._dbusImpl.emit_signal('RunningApplicationsChanged', null);
+ }
+ this._runningApplicationsDirty = false;
+ this._activeApplicationDirty = false;
+ }
+
+ _isEligibleWindow(window) {
+ if (window.is_override_redirect())
+ return false;
+
+ let type = window.get_window_type();
+ return type == Meta.WindowType.NORMAL ||
+ type == Meta.WindowType.DIALOG ||
+ type == Meta.WindowType.MODAL_DIALOG ||
+ type == Meta.WindowType.UTILITY;
+ }
+
+ async GetRunningApplicationsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ invocation.return_value(new GLib.Variant('(a{sa{sv}})', [this._runningApplications]));
+ }
+
+ async GetWindowsAsync(params, invocation) {
+ let focusWindow = global.display.get_focus_window();
+ let apps = this._appSystem.get_running();
+ let windowsList = {};
+
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ for (let app of apps) {
+ let windows = app.get_windows();
+ for (let window of windows) {
+ if (!this._isEligibleWindow(window))
+ continue;
+
+ let windowId = window.get_id();
+ let frameRect = window.get_frame_rect();
+ let title = window.get_title();
+ let wmClass = window.get_wm_class();
+ let sandboxedAppId = window.get_sandboxed_app_id();
+
+ windowsList[windowId] = {
+ 'app-id': GLib.Variant.new('s', app.get_id()),
+ 'client-type': GLib.Variant.new('u', window.get_client_type()),
+ 'is-hidden': GLib.Variant.new('b', window.is_hidden()),
+ 'has-focus': GLib.Variant.new('b', window == focusWindow),
+ 'width': GLib.Variant.new('u', frameRect.width),
+ 'height': GLib.Variant.new('u', frameRect.height),
+ };
+
+ // These properties may not be available for all windows:
+ if (title != null)
+ windowsList[windowId]['title'] = GLib.Variant.new('s', title);
+
+ if (wmClass != null)
+ windowsList[windowId]['wm-class'] = GLib.Variant.new('s', wmClass);
+
+ if (sandboxedAppId != null) {
+ windowsList[windowId]['sandboxed-app-id'] =
+ GLib.Variant.new('s', sandboxedAppId);
+ }
+ }
+ }
+ invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList]));
+ }
+
+ _syncAnimationsEnabled() {
+ let wasAnimationsEnabled = this._animationsEnabled;
+ this._animationsEnabled = this._settings.enable_animations;
+ if (wasAnimationsEnabled !== this._animationsEnabled) {
+ let variant = new GLib.Variant('b', this._animationsEnabled);
+ this._dbusImpl.emit_property_changed('AnimationsEnabled', variant);
+ }
+ }
+
+ _syncScreenSize() {
+ const oldScreenWidth = this._screenWidth;
+ const oldScreenHeight = this._screenHeight;
+ this._screenWidth = global.screen_width;
+ this._screenHeight = global.screen_height;
+
+ if (oldScreenWidth !== this._screenWidth ||
+ oldScreenHeight !== this._screenHeight) {
+ const variant = new GLib.Variant('(ii)',
+ [this._screenWidth, this._screenHeight]);
+ this._dbusImpl.emit_property_changed('ScreenSize', variant);
+ }
+ }
+
+ get AnimationsEnabled() {
+ return this._animationsEnabled;
+ }
+
+ get ScreenSize() {
+ return [this._screenWidth, this._screenHeight];
+ }
+
+ get version() {
+ return INTROSPECT_DBUS_API_VERSION;
+ }
+};
diff --git a/js/misc/jsParse.js b/js/misc/jsParse.js
new file mode 100644
index 0000000..c4e077f
--- /dev/null
+++ b/js/misc/jsParse.js
@@ -0,0 +1,236 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* exported getCompletions, getCommonPrefix, getDeclaredConstants */
+
+// Returns a list of potential completions for text. Completions either
+// follow a dot (e.g. foo.ba -> bar) or they are picked from globalCompletionList (e.g. fo -> foo)
+// commandHeader is prefixed on any expression before it is eval'ed. It will most likely
+// consist of global constants that might not carry over from the calling environment.
+//
+// This function is likely the one you want to call from external modules
+function getCompletions(text, commandHeader, globalCompletionList) {
+ let methods = [];
+ let expr_, base;
+ let attrHead = '';
+ if (globalCompletionList == null)
+ globalCompletionList = [];
+
+ let offset = getExpressionOffset(text, text.length - 1);
+ if (offset >= 0) {
+ text = text.slice(offset);
+
+ // Look for expressions like "Main.panel.foo" and match Main.panel and foo
+ let matches = text.match(/(.*)\.(.*)/);
+ if (matches) {
+ [expr_, base, attrHead] = matches;
+
+ methods = getPropertyNamesFromExpression(base, commandHeader).filter(
+ attr => attr.slice(0, attrHead.length) === attrHead);
+ }
+
+ // Look for the empty expression or partially entered words
+ // not proceeded by a dot and match them against global constants
+ matches = text.match(/^(\w*)$/);
+ if (text == '' || matches) {
+ [expr_, attrHead] = matches;
+ methods = globalCompletionList.filter(
+ attr => attr.slice(0, attrHead.length) === attrHead);
+ }
+ }
+
+ return [methods, attrHead];
+}
+
+
+//
+// A few functions for parsing strings of javascript code.
+//
+
+// Identify characters that delimit an expression. That is,
+// if we encounter anything that isn't a letter, '.', ')', or ']',
+// we should stop parsing.
+function isStopChar(c) {
+ return !c.match(/[\w.)\]]/);
+}
+
+// Given the ending position of a quoted string, find where it starts
+function findMatchingQuote(expr, offset) {
+ let quoteChar = expr.charAt(offset);
+ for (let i = offset - 1; i >= 0; --i) {
+ if (expr.charAt(i) == quoteChar && expr.charAt(i - 1) != '\\')
+ return i;
+ }
+ return -1;
+}
+
+// Given the ending position of a regex, find where it starts
+function findMatchingSlash(expr, offset) {
+ for (let i = offset - 1; i >= 0; --i) {
+ if (expr.charAt(i) == '/' && expr.charAt(i - 1) != '\\')
+ return i;
+ }
+ return -1;
+}
+
+// If expr.charAt(offset) is ')' or ']',
+// return the position of the corresponding '(' or '[' bracket.
+// This function does not check for syntactic correctness. e.g.,
+// findMatchingBrace("[(])", 3) returns 1.
+function findMatchingBrace(expr, offset) {
+ let closeBrace = expr.charAt(offset);
+ let openBrace = { ')': '(', ']': '[' }[closeBrace];
+
+ return findTheBrace(expr, offset - 1, openBrace, closeBrace);
+}
+
+function findTheBrace(expr, offset, ...braces) {
+ let [openBrace, closeBrace] = braces;
+
+ if (offset < 0)
+ return -1;
+
+ if (expr.charAt(offset) == openBrace)
+ return offset;
+
+ if (expr.charAt(offset).match(/['"]/))
+ return findTheBrace(expr, findMatchingQuote(expr, offset) - 1, ...braces);
+
+ if (expr.charAt(offset) == '/')
+ return findTheBrace(expr, findMatchingSlash(expr, offset) - 1, ...braces);
+
+ if (expr.charAt(offset) == closeBrace)
+ return findTheBrace(expr, findTheBrace(expr, offset - 1, ...braces) - 1, ...braces);
+
+ return findTheBrace(expr, offset - 1, ...braces);
+}
+
+// Walk expr backwards from offset looking for the beginning of an
+// expression suitable for passing to eval.
+// There is no guarantee of correct javascript syntax between the return
+// value and offset. This function is meant to take a string like
+// "foo(Obj.We.Are.Completing" and allow you to extract "Obj.We.Are.Completing"
+function getExpressionOffset(expr, offset) {
+ while (offset >= 0) {
+ let currChar = expr.charAt(offset);
+
+ if (isStopChar(currChar))
+ return offset + 1;
+
+ if (currChar.match(/[)\]]/))
+ offset = findMatchingBrace(expr, offset);
+
+ --offset;
+ }
+
+ return offset + 1;
+}
+
+// Things with non-word characters or that start with a number
+// are not accessible via .foo notation and so aren't returned
+function isValidPropertyName(w) {
+ return !(w.match(/\W/) || w.match(/^\d/));
+}
+
+// To get all properties (enumerable and not), we need to walk
+// the prototype chain ourselves
+function getAllProps(obj) {
+ if (obj === null || obj === undefined)
+ return [];
+
+ return Object.getOwnPropertyNames(obj).concat(getAllProps(Object.getPrototypeOf(obj)));
+}
+
+// Given a string _expr_, returns all methods
+// that can be accessed via '.' notation.
+// e.g., expr="({ foo: null, bar: null, 4: null })" will
+// return ["foo", "bar", ...] but the list will not include "4",
+// since methods accessed with '.' notation must star with a letter or _.
+function getPropertyNamesFromExpression(expr, commandHeader = '') {
+ let obj = {};
+ if (!isUnsafeExpression(expr)) {
+ try {
+ obj = eval(commandHeader + expr);
+ } catch (e) {
+ return [];
+ }
+ } else {
+ return [];
+ }
+
+ let propsUnique = {};
+ if (typeof obj === 'object') {
+ let allProps = getAllProps(obj);
+ // Get only things we are allowed to complete following a '.'
+ allProps = allProps.filter(isValidPropertyName);
+
+ // Make sure propsUnique contains one key for every
+ // property so we end up with a unique list of properties
+ allProps.map(p => (propsUnique[p] = null));
+ }
+ return Object.keys(propsUnique).sort();
+}
+
+// Given a list of words, returns the longest prefix they all have in common
+function getCommonPrefix(words) {
+ let word = words[0];
+ for (let i = 0; i < word.length; i++) {
+ for (let w = 1; w < words.length; w++) {
+ if (words[w].charAt(i) != word.charAt(i))
+ return word.slice(0, i);
+ }
+ }
+ return word;
+}
+
+// Remove any blocks that are quoted or are in a regex
+function removeLiterals(str) {
+ if (str.length == 0)
+ return '';
+
+ let currChar = str.charAt(str.length - 1);
+ if (currChar == '"' || currChar == '\'') {
+ return removeLiterals(
+ str.slice(0, findMatchingQuote(str, str.length - 1)));
+ } else if (currChar == '/') {
+ return removeLiterals(
+ str.slice(0, findMatchingSlash(str, str.length - 1)));
+ }
+
+ return removeLiterals(str.slice(0, str.length - 1)) + currChar;
+}
+
+// Returns true if there is reason to think that eval(str)
+// will modify the global scope
+function isUnsafeExpression(str) {
+ // Check for any sort of assignment
+ // The strategy used is dumb: remove any quotes
+ // or regexs and comparison operators and see if there is an '=' character.
+ // If there is, it might be an unsafe assignment.
+
+ let prunedStr = removeLiterals(str);
+ prunedStr = prunedStr.replace(/[=!]==/g, ''); // replace === and !== with nothing
+ prunedStr = prunedStr.replace(/[=<>!]=/g, ''); // replace ==, <=, >=, != with nothing
+
+ if (prunedStr.match(/[=]/)) {
+ return true;
+ } else if (prunedStr.match(/;/)) {
+ // If we contain a semicolon not inside of a quote/regex, assume we're unsafe as well
+ return true;
+ }
+
+ return false;
+}
+
+// Returns a list of global keywords derived from str
+function getDeclaredConstants(str) {
+ let ret = [];
+ str.split(';').forEach(s => {
+ let base_, keyword;
+ let match = s.match(/const\s+(\w+)\s*=/);
+ if (match) {
+ [base_, keyword] = match;
+ ret.push(keyword);
+ }
+ });
+
+ return ret;
+}
diff --git a/js/misc/keyboardManager.js b/js/misc/keyboardManager.js
new file mode 100644
index 0000000..142e2f4
--- /dev/null
+++ b/js/misc/keyboardManager.js
@@ -0,0 +1,163 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported getKeyboardManager, holdKeyboard, releaseKeyboard */
+
+const { GLib, GnomeDesktop } = imports.gi;
+
+const Main = imports.ui.main;
+
+var DEFAULT_LOCALE = 'en_US';
+var DEFAULT_LAYOUT = 'us';
+var DEFAULT_VARIANT = '';
+
+let _xkbInfo = null;
+
+function getXkbInfo() {
+ if (_xkbInfo == null)
+ _xkbInfo = new GnomeDesktop.XkbInfo();
+ return _xkbInfo;
+}
+
+let _keyboardManager = null;
+
+function getKeyboardManager() {
+ if (_keyboardManager == null)
+ _keyboardManager = new KeyboardManager();
+ return _keyboardManager;
+}
+
+function releaseKeyboard() {
+ if (Main.modalCount > 0)
+ global.display.unfreeze_keyboard(global.get_current_time());
+ else
+ global.display.ungrab_keyboard(global.get_current_time());
+}
+
+function holdKeyboard() {
+ global.display.freeze_keyboard(global.get_current_time());
+}
+
+var KeyboardManager = class {
+ constructor() {
+ // The XKB protocol doesn't allow for more that 4 layouts in a
+ // keymap. Wayland doesn't impose this limit and libxkbcommon can
+ // handle up to 32 layouts but since we need to support X clients
+ // even as a Wayland compositor, we can't bump this.
+ this.MAX_LAYOUTS_PER_GROUP = 4;
+
+ this._xkbInfo = getXkbInfo();
+ this._current = null;
+ this._localeLayoutInfo = this._getLocaleLayout();
+ this._layoutInfos = {};
+ this._currentKeymap = null;
+ }
+
+ _applyLayoutGroup(group) {
+ let options = this._buildOptionsString();
+ let [layouts, variants] = this._buildGroupStrings(group);
+
+ if (this._currentKeymap &&
+ this._currentKeymap.layouts == layouts &&
+ this._currentKeymap.variants == variants &&
+ this._currentKeymap.options == options)
+ return;
+
+ this._currentKeymap = { layouts, variants, options };
+ global.backend.set_keymap(layouts, variants, options);
+ }
+
+ _applyLayoutGroupIndex(idx) {
+ global.backend.lock_layout_group(idx);
+ }
+
+ apply(id) {
+ let info = this._layoutInfos[id];
+ if (!info)
+ return;
+
+ if (this._current && this._current.group == info.group) {
+ if (this._current.groupIndex != info.groupIndex)
+ this._applyLayoutGroupIndex(info.groupIndex);
+ } else {
+ this._applyLayoutGroup(info.group);
+ this._applyLayoutGroupIndex(info.groupIndex);
+ }
+
+ this._current = info;
+ }
+
+ reapply() {
+ if (!this._current)
+ return;
+
+ this._applyLayoutGroup(this._current.group);
+ this._applyLayoutGroupIndex(this._current.groupIndex);
+ }
+
+ setUserLayouts(ids) {
+ this._current = null;
+ this._layoutInfos = {};
+
+ for (let i = 0; i < ids.length; ++i) {
+ let [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(ids[i]);
+ if (found)
+ this._layoutInfos[ids[i]] = { id: ids[i], layout: _layout, variant: _variant };
+ }
+
+ let i = 0;
+ let group = [];
+ for (let id in this._layoutInfos) {
+ // We need to leave one slot on each group free so that we
+ // can add a layout containing the symbols for the
+ // language used in UI strings to ensure that toolkits can
+ // handle mnemonics like Alt+Ф even if the user is
+ // actually typing in a different layout.
+ let groupIndex = i % (this.MAX_LAYOUTS_PER_GROUP - 1);
+ if (groupIndex == 0)
+ group = [];
+
+ let info = this._layoutInfos[id];
+ group[groupIndex] = info;
+ info.group = group;
+ info.groupIndex = groupIndex;
+
+ i += 1;
+ }
+ }
+
+ _getLocaleLayout() {
+ let locale = GLib.get_language_names()[0];
+ if (!locale.includes('_'))
+ locale = DEFAULT_LOCALE;
+
+ let [found, , id] = GnomeDesktop.get_input_source_from_locale(locale);
+ if (!found)
+ [, , id] = GnomeDesktop.get_input_source_from_locale(DEFAULT_LOCALE);
+
+ let _layout, _variant;
+ [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id);
+ if (found)
+ return { layout: _layout, variant: _variant };
+ else
+ return { layout: DEFAULT_LAYOUT, variant: DEFAULT_VARIANT };
+ }
+
+ _buildGroupStrings(_group) {
+ let group = _group.concat(this._localeLayoutInfo);
+ let layouts = group.map(g => g.layout).join(',');
+ let variants = group.map(g => g.variant).join(',');
+ return [layouts, variants];
+ }
+
+ setKeyboardOptions(options) {
+ this._xkbOptions = options;
+ }
+
+ _buildOptionsString() {
+ let options = this._xkbOptions.join(',');
+ return options;
+ }
+
+ get currentLayout() {
+ return this._current;
+ }
+};
diff --git a/js/misc/loginManager.js b/js/misc/loginManager.js
new file mode 100644
index 0000000..94d62e8
--- /dev/null
+++ b/js/misc/loginManager.js
@@ -0,0 +1,247 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported canLock, getLoginManager, registerSessionWithGDM */
+
+const { GLib, Gio } = imports.gi;
+const Signals = imports.misc.signals;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const SystemdLoginManagerIface = loadInterfaceXML('org.freedesktop.login1.Manager');
+const SystemdLoginSessionIface = loadInterfaceXML('org.freedesktop.login1.Session');
+const SystemdLoginUserIface = loadInterfaceXML('org.freedesktop.login1.User');
+
+const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
+const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
+const SystemdLoginUser = Gio.DBusProxy.makeProxyWrapper(SystemdLoginUserIface);
+
+function haveSystemd() {
+ return GLib.access("/run/systemd/seats", 0) >= 0;
+}
+
+function versionCompare(required, reference) {
+ required = required.split('.');
+ reference = reference.split('.');
+
+ for (let i = 0; i < required.length; i++) {
+ let requiredInt = parseInt(required[i]);
+ let referenceInt = parseInt(reference[i]);
+ if (requiredInt != referenceInt)
+ return requiredInt < referenceInt;
+ }
+
+ return true;
+}
+
+function canLock() {
+ try {
+ let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
+ let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
+ '/org/gnome/DisplayManager/Manager',
+ 'org.freedesktop.DBus.Properties',
+ 'Get', params, null,
+ Gio.DBusCallFlags.NONE,
+ -1, null);
+
+ let version = result.deepUnpack()[0].deepUnpack();
+ return haveSystemd() && versionCompare('3.5.91', version);
+ } catch (e) {
+ return false;
+ }
+}
+
+
+async function registerSessionWithGDM() {
+ log("Registering session with GDM");
+ try {
+ await Gio.DBus.system.call(
+ 'org.gnome.DisplayManager',
+ '/org/gnome/DisplayManager/Manager',
+ 'org.gnome.DisplayManager.Manager',
+ 'RegisterSession',
+ GLib.Variant.new('(a{sv})', [{}]), null,
+ Gio.DBusCallFlags.NONE, -1, null);
+ } catch (e) {
+ if (!e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD))
+ log(`Error registering session with GDM: ${e.message}`);
+ else
+ log('Not calling RegisterSession(): method not exported, GDM too old?');
+ }
+}
+
+let _loginManager = null;
+
+/**
+ * getLoginManager:
+ * An abstraction over systemd/logind and ConsoleKit.
+ * @returns {object} - the LoginManager singleton
+ *
+ */
+function getLoginManager() {
+ if (_loginManager == null) {
+ if (haveSystemd())
+ _loginManager = new LoginManagerSystemd();
+ else
+ _loginManager = new LoginManagerDummy();
+ }
+
+ return _loginManager;
+}
+
+var LoginManagerSystemd = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._proxy = new SystemdLoginManager(Gio.DBus.system,
+ 'org.freedesktop.login1',
+ '/org/freedesktop/login1');
+ this._userProxy = new SystemdLoginUser(Gio.DBus.system,
+ 'org.freedesktop.login1',
+ '/org/freedesktop/login1/user/self');
+ this._proxy.connectSignal('PrepareForSleep',
+ this._prepareForSleep.bind(this));
+ }
+
+ async getCurrentSessionProxy() {
+ if (this._currentSession)
+ return this._currentSession;
+
+ let sessionId = GLib.getenv('XDG_SESSION_ID');
+ if (!sessionId) {
+ log('Unset XDG_SESSION_ID, getCurrentSessionProxy() called outside a user session. Asking logind directly.');
+ let [session, objectPath] = this._userProxy.Display;
+ if (session) {
+ log(`Will monitor session ${session}`);
+ sessionId = session;
+ } else {
+ log('Failed to find "Display" session; are we the greeter?');
+
+ for ([session, objectPath] of this._userProxy.Sessions) {
+ let sessionProxy = new SystemdLoginSession(Gio.DBus.system,
+ 'org.freedesktop.login1',
+ objectPath);
+ log(`Considering ${session}, class=${sessionProxy.Class}`);
+ if (sessionProxy.Class == 'greeter') {
+ log(`Yes, will monitor session ${session}`);
+ sessionId = session;
+ break;
+ }
+ }
+
+ if (!sessionId) {
+ log('No, failed to get session from logind.');
+ return null;
+ }
+ }
+ }
+
+ try {
+ const [objectPath] = await this._proxy.GetSessionAsync(sessionId);
+ this._currentSession = new SystemdLoginSession(Gio.DBus.system,
+ 'org.freedesktop.login1', objectPath);
+ return this._currentSession;
+ } catch (error) {
+ logError(error, 'Could not get a proxy for the current session');
+ return null;
+ }
+ }
+
+ async canSuspend() {
+ let canSuspend, needsAuth;
+
+ try {
+ const [result] = await this._proxy.CanSuspendAsync();
+ needsAuth = result === 'challenge';
+ canSuspend = needsAuth || result === 'yes';
+ } catch (error) {
+ canSuspend = false;
+ needsAuth = false;
+ }
+ return {canSuspend, needsAuth};
+ }
+
+ async canRebootToBootLoaderMenu() {
+ let canRebootToBootLoaderMenu, needsAuth;
+
+ try {
+ const [result] = await this._proxy.CanRebootToBootLoaderMenuAsync();
+ needsAuth = result === 'challenge';
+ canRebootToBootLoaderMenu = needsAuth || result === 'yes';
+ } catch (error) {
+ canRebootToBootLoaderMenu = false;
+ needsAuth = false;
+ }
+ return {canRebootToBootLoaderMenu, needsAuth};
+ }
+
+ setRebootToBootLoaderMenu() {
+ /* Parameter is timeout in usec, show to menu for 60 seconds */
+ this._proxy.SetRebootToBootLoaderMenuAsync(60000000);
+ }
+
+ async listSessions() {
+ try {
+ const [sessions] = await this._proxy.ListSessionsAsync();
+ return sessions;
+ } catch (e) {
+ return [];
+ }
+ }
+
+ suspend() {
+ this._proxy.SuspendAsync(true);
+ }
+
+ async inhibit(reason, cancellable) {
+ const inVariant = new GLib.Variant('(ssss)',
+ ['sleep', 'GNOME Shell', reason, 'delay']);
+ const [outVariant_, fdList] =
+ await this._proxy.call_with_unix_fd_list('Inhibit',
+ inVariant, 0, -1, null, cancellable);
+ const [fd] = fdList.steal_fds();
+ return new Gio.UnixInputStream({ fd });
+ }
+
+ _prepareForSleep(proxy, sender, [aboutToSuspend]) {
+ this.emit('prepare-for-sleep', aboutToSuspend);
+ }
+};
+
+var LoginManagerDummy = class extends Signals.EventEmitter {
+ getCurrentSessionProxy() {
+ // we could return a DummySession object that fakes whatever callers
+ // expect (at the time of writing: connect() and connectSignal()
+ // methods), but just never settling the promise should be safer
+ return new Promise(() => {});
+ }
+
+ canSuspend() {
+ return new Promise(resolve => resolve({
+ canSuspend: false,
+ needsAuth: false,
+ }));
+ }
+
+ canRebootToBootLoaderMenu() {
+ return new Promise(resolve => resolve({
+ canRebootToBootLoaderMenu: false,
+ needsAuth: false,
+ }));
+ }
+
+ setRebootToBootLoaderMenu() {
+ }
+
+ listSessions() {
+ return new Promise(resolve => resolve([]));
+ }
+
+ suspend() {
+ this.emit('prepare-for-sleep', true);
+ this.emit('prepare-for-sleep', false);
+ }
+
+ /* eslint-disable-next-line require-await */
+ async inhibit() {
+ return null;
+ }
+};
diff --git a/js/misc/meson.build b/js/misc/meson.build
new file mode 100644
index 0000000..2dff20d
--- /dev/null
+++ b/js/misc/meson.build
@@ -0,0 +1,15 @@
+jsconf = configuration_data()
+jsconf.set('PACKAGE_NAME', meson.project_name())
+jsconf.set('PACKAGE_VERSION', meson.project_version())
+jsconf.set('GETTEXT_PACKAGE', meson.project_name())
+jsconf.set('LIBMUTTER_API_VERSION', mutter_api_version)
+jsconf.set10('HAVE_NETWORKMANAGER', have_networkmanager)
+jsconf.set10('HAVE_SOUP2', have_soup2)
+jsconf.set('datadir', datadir)
+jsconf.set('libexecdir', libexecdir)
+
+config_js = configure_file(
+ input: 'config.js.in',
+ output: 'config.js',
+ configuration: jsconf
+)
diff --git a/js/misc/modemManager.js b/js/misc/modemManager.js
new file mode 100644
index 0000000..3c29795
--- /dev/null
+++ b/js/misc/modemManager.js
@@ -0,0 +1,298 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ModemBase, ModemGsm, ModemCdma, BroadbandModem */
+
+const { Gio, GObject, NM, NMA } = imports.gi;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+// _getMobileProvidersDatabase:
+//
+// Gets the database of mobile providers, with references between MCCMNC/SID and
+// operator name
+//
+let _mpd;
+function _getMobileProvidersDatabase() {
+ if (_mpd == null) {
+ try {
+ _mpd = new NMA.MobileProvidersDatabase();
+ _mpd.init(null);
+ } catch (e) {
+ log(e.message);
+ _mpd = null;
+ }
+ }
+
+ return _mpd;
+}
+
+// _findProviderForMccMnc:
+// @operatorName: operator name
+// @operatorCode: operator code
+//
+// Given an operator name string (which may not be a real operator name) and an
+// operator code string, tries to find a proper operator name to display.
+//
+function _findProviderForMccMnc(operatorName, operatorCode) {
+ if (operatorName) {
+ if (operatorName.length != 0 &&
+ (operatorName.length > 6 || operatorName.length < 5)) {
+ // this looks like a valid name, i.e. not an MCCMNC (that some
+ // devices return when not yet connected
+ return operatorName;
+ }
+
+ if (isNaN(parseInt(operatorName))) {
+ // name is definitely not a MCCMNC, so it may be a name
+ // after all; return that
+ return operatorName;
+ }
+ }
+
+ let needle;
+ if ((!operatorName || operatorName.length == 0) && operatorCode)
+ needle = operatorCode;
+ else if (operatorName && (operatorName.length == 6 || operatorName.length == 5))
+ needle = operatorName;
+ else // nothing to search
+ return null;
+
+ let mpd = _getMobileProvidersDatabase();
+ if (mpd) {
+ let provider = mpd.lookup_3gpp_mcc_mnc(needle);
+ if (provider)
+ return provider.get_name();
+ }
+ return null;
+}
+
+// _findProviderForSid:
+// @sid: System Identifier of the serving CDMA network
+//
+// Tries to find the operator name corresponding to the given SID
+//
+function _findProviderForSid(sid) {
+ if (!sid)
+ return null;
+
+ let mpd = _getMobileProvidersDatabase();
+ if (mpd) {
+ let provider = mpd.lookup_cdma_sid(sid);
+ if (provider)
+ return provider.get_name();
+ }
+ return null;
+}
+
+
+// ----------------------------------------------------- //
+// Support for the old ModemManager interface (MM < 0.7) //
+// ----------------------------------------------------- //
+
+
+// The following are not the complete interfaces, just the methods we need
+// (or may need in the future)
+
+const ModemGsmNetworkInterface = loadInterfaceXML('org.freedesktop.ModemManager.Modem.Gsm.Network');
+const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
+
+const ModemCdmaInterface = loadInterfaceXML('org.freedesktop.ModemManager.Modem.Cdma');
+const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
+
+var ModemBase = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+ Properties: {
+ 'operator-name': GObject.ParamSpec.string(
+ 'operator-name', 'operator-name', 'operator-name',
+ GObject.ParamFlags.READABLE,
+ null),
+ 'signal-quality': GObject.ParamSpec.int(
+ 'signal-quality', 'signal-quality', 'signal-quality',
+ GObject.ParamFlags.READABLE,
+ 0, 100, 0),
+ },
+}, class ModemBase extends GObject.Object {
+ _init() {
+ super._init();
+ this._operatorName = null;
+ this._signalQuality = 0;
+ }
+
+ get operatorName() {
+ return this._operatorName;
+ }
+
+ get signalQuality() {
+ return this._signalQuality;
+ }
+
+ _setOperatorName(operatorName) {
+ if (this._operatorName == operatorName)
+ return;
+ this._operatorName = operatorName;
+ this.notify('operator-name');
+ }
+
+ _setSignalQuality(signalQuality) {
+ if (this._signalQuality == signalQuality)
+ return;
+ this._signalQuality = signalQuality;
+ this.notify('signal-quality');
+ }
+});
+
+var ModemGsm = GObject.registerClass(
+class ModemGsm extends ModemBase {
+ _init(path) {
+ super._init();
+ this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
+
+ // Code is duplicated because the function have different signatures
+ this._proxy.connectSignal('SignalQuality', (proxy, sender, [quality]) => {
+ this._setSignalQuality(quality);
+ });
+ this._proxy.connectSignal('RegistrationInfo', (proxy, sender, [_status, code, name]) => {
+ this._setOperatorName(_findProviderForMccMnc(name, code));
+ });
+ this._getInitialState();
+ }
+
+ async _getInitialState() {
+ try {
+ const [
+ [status_, code, name],
+ [quality],
+ ] = await Promise.all([
+ this._proxy.GetRegistrationInfoAsync(),
+ this._proxy.GetSignalQualityAsync(),
+ ]);
+ this._setOperatorName(_findProviderForMccMnc(name, code));
+ this._setSignalQuality(quality);
+ } catch (err) {
+ // it will return an error if the device is not connected
+ this._setSignalQuality(0);
+ }
+ }
+});
+
+var ModemCdma = GObject.registerClass(
+class ModemCdma extends ModemBase {
+ _init(path) {
+ super._init();
+ this._proxy = new ModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
+
+ this._proxy.connectSignal('SignalQuality', (proxy, sender, params) => {
+ this._setSignalQuality(params[0]);
+
+ // receiving this signal means the device got activated
+ // and we can finally call GetServingSystem
+ if (this.operator_name == null)
+ this._refreshServingSystem();
+ });
+ this._getSignalQuality();
+ }
+
+ async _getSignalQuality() {
+ try {
+ const [quality] = await this._proxy.GetSignalQualityAsync();
+ this._setSignalQuality(quality);
+ } catch (err) {
+ // it will return an error if the device is not connected
+ this._setSignalQuality(0);
+ }
+ }
+
+ async _refreshServingSystem() {
+ try {
+ const [bandClass_, band_, sid] =
+ await this._proxy.GetServingSystemAsync();
+ this._setOperatorName(_findProviderForSid(sid));
+ } catch (err) {
+ // it will return an error if the device is not connected
+ this._setOperatorName(null);
+ }
+ }
+});
+
+
+// ------------------------------------------------------- //
+// Support for the new ModemManager1 interface (MM >= 0.7) //
+// ------------------------------------------------------- //
+
+const BroadbandModemInterface = loadInterfaceXML('org.freedesktop.ModemManager1.Modem');
+const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
+
+const BroadbandModem3gppInterface = loadInterfaceXML('org.freedesktop.ModemManager1.Modem.Modem3gpp');
+const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
+
+const BroadbandModemCdmaInterface = loadInterfaceXML('org.freedesktop.ModemManager1.Modem.ModemCdma');
+const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
+
+var BroadbandModem = GObject.registerClass({
+ Properties: {
+ 'capabilities': GObject.ParamSpec.flags(
+ 'capabilities', 'capabilities', 'capabilities',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ NM.DeviceModemCapabilities.$gtype,
+ NM.DeviceModemCapabilities.NONE),
+ },
+}, class BroadbandModem extends ModemBase {
+ _init(path, capabilities) {
+ super._init({ capabilities });
+ this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
+ this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
+ this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
+
+ this._proxy.connect('g-properties-changed', (proxy, properties) => {
+ const signalQualityChanged = !!properties.lookup_value('SignalQuality', null);
+ if (signalQualityChanged)
+ this._reloadSignalQuality();
+ });
+ this._reloadSignalQuality();
+
+ this._proxy_3gpp.connect('g-properties-changed', (proxy, properties) => {
+ let unpacked = properties.deepUnpack();
+ if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
+ this._reload3gppOperatorName();
+ });
+ this._reload3gppOperatorName();
+
+ this._proxy_cdma.connect('g-properties-changed', (proxy, properties) => {
+ let unpacked = properties.deepUnpack();
+ if ('Nid' in unpacked || 'Sid' in unpacked)
+ this._reloadCdmaOperatorName();
+ });
+ this._reloadCdmaOperatorName();
+ }
+
+ _reloadSignalQuality() {
+ let [quality, recent_] = this._proxy.SignalQuality;
+ this._setSignalQuality(quality);
+ }
+
+ _reloadOperatorName() {
+ let newName = "";
+ if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
+ newName += this.operator_name_3gpp;
+
+ if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
+ if (newName != "")
+ newName += ", ";
+ newName += this.operator_name_cdma;
+ }
+
+ this._setOperatorName(newName);
+ }
+
+ _reload3gppOperatorName() {
+ let name = this._proxy_3gpp.OperatorName;
+ let code = this._proxy_3gpp.OperatorCode;
+ this.operator_name_3gpp = _findProviderForMccMnc(name, code);
+ this._reloadOperatorName();
+ }
+
+ _reloadCdmaOperatorName() {
+ let sid = this._proxy_cdma.Sid;
+ this.operator_name_cdma = _findProviderForSid(sid);
+ this._reloadOperatorName();
+ }
+});
diff --git a/js/misc/objectManager.js b/js/misc/objectManager.js
new file mode 100644
index 0000000..a1dcde3
--- /dev/null
+++ b/js/misc/objectManager.js
@@ -0,0 +1,261 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ObjectManager */
+
+const { Gio, GLib } = imports.gi;
+const Params = imports.misc.params;
+const Signals = imports.misc.signals;
+
+// Specified in the D-Bus specification here:
+// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
+const ObjectManagerIface = `
+<node>
+<interface name="org.freedesktop.DBus.ObjectManager">
+ <method name="GetManagedObjects">
+ <arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
+ </method>
+ <signal name="InterfacesAdded">
+ <arg name="objectPath" type="o"/>
+ <arg name="interfaces" type="a{sa{sv}}" />
+ </signal>
+ <signal name="InterfacesRemoved">
+ <arg name="objectPath" type="o"/>
+ <arg name="interfaces" type="as" />
+ </signal>
+</interface>
+</node>`;
+
+const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
+
+var ObjectManager = class extends Signals.EventEmitter {
+ constructor(params) {
+ super();
+
+ params = Params.parse(params, {
+ connection: null,
+ name: null,
+ objectPath: null,
+ knownInterfaces: null,
+ cancellable: null,
+ onLoaded: null,
+ });
+
+ this._connection = params.connection;
+ this._serviceName = params.name;
+ this._managerPath = params.objectPath;
+ this._cancellable = params.cancellable;
+
+ this._managerProxy = new Gio.DBusProxy({
+ g_connection: this._connection,
+ g_interface_name: ObjectManagerInfo.name,
+ g_interface_info: ObjectManagerInfo,
+ g_name: this._serviceName,
+ g_object_path: this._managerPath,
+ g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START,
+ });
+
+ this._interfaceInfos = {};
+ this._objects = {};
+ this._interfaces = {};
+ this._onLoaded = params.onLoaded;
+
+ if (params.knownInterfaces)
+ this._registerInterfaces(params.knownInterfaces);
+
+ this._initManagerProxy();
+ }
+
+ _completeLoad() {
+ if (this._onLoaded)
+ this._onLoaded();
+ }
+
+ async _addInterface(objectPath, interfaceName) {
+ let info = this._interfaceInfos[interfaceName];
+
+ if (!info)
+ return;
+
+ const proxy = new Gio.DBusProxy({
+ g_connection: this._connection,
+ g_name: this._serviceName,
+ g_object_path: objectPath,
+ g_interface_name: interfaceName,
+ g_interface_info: info,
+ g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START,
+ });
+
+ try {
+ await proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable);
+ } catch (e) {
+ logError(e, `could not initialize proxy for interface ${interfaceName}`);
+ return;
+ }
+
+ let isNewObject;
+ if (!this._objects[objectPath]) {
+ this._objects[objectPath] = {};
+ isNewObject = true;
+ } else {
+ isNewObject = false;
+ }
+
+ this._objects[objectPath][interfaceName] = proxy;
+
+ if (!this._interfaces[interfaceName])
+ this._interfaces[interfaceName] = [];
+
+ this._interfaces[interfaceName].push(proxy);
+
+ if (isNewObject)
+ this.emit('object-added', objectPath);
+
+ this.emit('interface-added', interfaceName, proxy);
+ }
+
+ _removeInterface(objectPath, interfaceName) {
+ if (!this._objects[objectPath])
+ return;
+
+ let proxy = this._objects[objectPath][interfaceName];
+
+ if (this._interfaces[interfaceName]) {
+ let index = this._interfaces[interfaceName].indexOf(proxy);
+
+ if (index >= 0)
+ this._interfaces[interfaceName].splice(index, 1);
+
+ if (this._interfaces[interfaceName].length === 0)
+ delete this._interfaces[interfaceName];
+ }
+
+ this.emit('interface-removed', interfaceName, proxy);
+
+ delete this._objects[objectPath][interfaceName];
+
+ if (Object.keys(this._objects[objectPath]).length === 0) {
+ delete this._objects[objectPath];
+ this.emit('object-removed', objectPath);
+ }
+ }
+
+ async _initManagerProxy() {
+ try {
+ await this._managerProxy.init_async(
+ GLib.PRIORITY_DEFAULT, this._cancellable);
+ } catch (e) {
+ logError(e, `could not initialize object manager for object ${this._serviceName}`);
+
+ this._completeLoad();
+ return;
+ }
+
+ this._managerProxy.connectSignal('InterfacesAdded',
+ (objectManager, sender, [objectPath, interfaces]) => {
+ let interfaceNames = Object.keys(interfaces);
+ for (let i = 0; i < interfaceNames.length; i++)
+ this._addInterface(objectPath, interfaceNames[i]);
+ });
+ this._managerProxy.connectSignal('InterfacesRemoved',
+ (objectManager, sender, [objectPath, interfaceNames]) => {
+ for (let i = 0; i < interfaceNames.length; i++)
+ this._removeInterface(objectPath, interfaceNames[i]);
+ });
+
+ if (Object.keys(this._interfaceInfos).length === 0) {
+ this._completeLoad();
+ return;
+ }
+
+ this._managerProxy.connect('notify::g-name-owner', () => {
+ if (this._managerProxy.g_name_owner)
+ this._onNameAppeared();
+ else
+ this._onNameVanished();
+ });
+
+ if (this._managerProxy.g_name_owner)
+ this._onNameAppeared();
+ }
+
+ async _onNameAppeared() {
+ try {
+ const [objects] = await this._managerProxy.GetManagedObjectsAsync();
+
+ if (!objects) {
+ this._completeLoad();
+ return;
+ }
+
+ const objectPaths = Object.keys(objects);
+ await Promise.allSettled(objectPaths.flatMap(objectPath => {
+ const object = objects[objectPath];
+ const interfaceNames = Object.getOwnPropertyNames(object);
+ return interfaceNames.map(
+ ifaceName => this._addInterface(objectPath, ifaceName));
+ }));
+ } catch (error) {
+ logError(error, `could not get remote objects for service ${this._serviceName} path ${this._managerPath}`);
+ } finally {
+ this._completeLoad();
+ }
+ }
+
+ _onNameVanished() {
+ let objectPaths = Object.keys(this._objects);
+ for (let i = 0; i < objectPaths.length; i++) {
+ let objectPath = objectPaths[i];
+ let object = this._objects[objectPath];
+
+ let interfaceNames = Object.keys(object);
+ for (let j = 0; j < interfaceNames.length; j++) {
+ let interfaceName = interfaceNames[j];
+
+ if (object[interfaceName])
+ this._removeInterface(objectPath, interfaceName);
+ }
+ }
+ }
+
+ _registerInterfaces(interfaces) {
+ for (let i = 0; i < interfaces.length; i++) {
+ let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
+ this._interfaceInfos[info.name] = info;
+ }
+ }
+
+ getProxy(objectPath, interfaceName) {
+ let object = this._objects[objectPath];
+
+ if (!object)
+ return null;
+
+ return object[interfaceName];
+ }
+
+ getProxiesForInterface(interfaceName) {
+ let proxyList = this._interfaces[interfaceName];
+
+ if (!proxyList)
+ return [];
+
+ return proxyList;
+ }
+
+ getAllProxies() {
+ let proxies = [];
+
+ let objectPaths = Object.keys(this._objects);
+ for (let i = 0; i < objectPaths.length; i++) {
+ let object = this._objects[objectPaths];
+
+ let interfaceNames = Object.keys(object);
+ for (let j = 0; j < interfaceNames.length; j++) {
+ let interfaceName = interfaceNames[j];
+ if (object[interfaceName])
+ proxies.push(object(interfaceName));
+ }
+ }
+
+ return proxies;
+ }
+};
diff --git a/js/misc/params.js b/js/misc/params.js
new file mode 100644
index 0000000..817d66c
--- /dev/null
+++ b/js/misc/params.js
@@ -0,0 +1,28 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported parse */
+
+// parse:
+// @params: caller-provided parameter object, or %null
+// @defaults-provided defaults object
+// @allowExtras: whether or not to allow properties not in @default
+//
+// Examines @params and fills in default values from @defaults for
+// any properties in @defaults that don't appear in @params. If
+// @allowExtras is not %true, it will throw an error if @params
+// contains any properties that aren't in @defaults.
+//
+// If @params is %null, this returns the values from @defaults.
+//
+// Return value: a new object, containing the merged parameters from
+// @params and @defaults
+function parse(params = {}, defaults, allowExtras) {
+ if (!allowExtras) {
+ for (let prop in params) {
+ if (!(prop in defaults))
+ throw new Error(`Unrecognized parameter "${prop}"`);
+ }
+ }
+
+ let defaultsCopy = Object.assign({}, defaults);
+ return Object.assign(defaultsCopy, params);
+}
diff --git a/js/misc/parentalControlsManager.js b/js/misc/parentalControlsManager.js
new file mode 100644
index 0000000..48f0ca2
--- /dev/null
+++ b/js/misc/parentalControlsManager.js
@@ -0,0 +1,153 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+//
+// Copyright (C) 2018, 2019, 2020 Endless Mobile, Inc.
+//
+// This is a GNOME Shell component to wrap the interactions over
+// D-Bus with the malcontent library.
+//
+// Licensed under the GNU General Public License Version 2
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+/* exported getDefault */
+
+const { Gio, GObject, Shell } = imports.gi;
+
+// We require libmalcontent ≥ 0.6.0
+const HAVE_MALCONTENT = imports.package.checkSymbol(
+ 'Malcontent', '0', 'ManagerGetValueFlags');
+
+var Malcontent = null;
+if (HAVE_MALCONTENT) {
+ Malcontent = imports.gi.Malcontent;
+ Gio._promisify(Malcontent.Manager.prototype, 'get_app_filter_async');
+}
+
+let _singleton = null;
+
+function getDefault() {
+ if (_singleton === null)
+ _singleton = new ParentalControlsManager();
+
+ return _singleton;
+}
+
+// A manager class which provides cached access to the constructing user’s
+// parental controls settings. It’s possible for the user’s parental controls
+// to change at runtime if the Parental Controls application is used by an
+// administrator from within the user’s session.
+var ParentalControlsManager = GObject.registerClass({
+ Signals: {
+ 'app-filter-changed': {},
+ },
+}, class ParentalControlsManager extends GObject.Object {
+ _init() {
+ super._init();
+
+ this._initialized = false;
+ this._disabled = false;
+ this._appFilter = null;
+
+ this._initializeManager();
+ }
+
+ async _initializeManager() {
+ if (!HAVE_MALCONTENT) {
+ console.debug('Skipping parental controls support, malcontent not found');
+ this._initialized = true;
+ this.emit('app-filter-changed');
+ return;
+ }
+
+ try {
+ const connection = await Gio.DBus.get(Gio.BusType.SYSTEM, null);
+ this._manager = new Malcontent.Manager({ connection });
+ this._appFilter = await this._getAppFilter();
+ } catch (e) {
+ logError(e, 'Failed to get parental controls settings');
+ return;
+ }
+
+ this._manager.connect('app-filter-changed', this._onAppFilterChanged.bind(this));
+
+ // Signal initialisation is complete.
+ this._initialized = true;
+ this.emit('app-filter-changed');
+ }
+
+ async _getAppFilter() {
+ let appFilter = null;
+
+ try {
+ appFilter = await this._manager.get_app_filter_async(
+ Shell.util_get_uid(),
+ Malcontent.ManagerGetValueFlags.NONE,
+ null);
+ } catch (e) {
+ if (!e.matches(Malcontent.ManagerError, Malcontent.ManagerError.DISABLED))
+ throw e;
+
+ console.debug('Parental controls globally disabled');
+ this._disabled = true;
+ }
+
+ return appFilter;
+ }
+
+ async _onAppFilterChanged(manager, uid) {
+ // Emit 'changed' signal only if app-filter is changed for currently logged-in user.
+ let currentUid = Shell.util_get_uid();
+ if (currentUid !== uid)
+ return;
+
+ try {
+ this._appFilter = await this._getAppFilter();
+ this.emit('app-filter-changed');
+ } catch (e) {
+ // Log an error and keep the old app filter.
+ logError(e, `Failed to get new MctAppFilter for uid ${Shell.util_get_uid()} on app-filter-changed`);
+ }
+ }
+
+ get initialized() {
+ return this._initialized;
+ }
+
+ // Calculate whether the given app (a Gio.DesktopAppInfo) should be shown
+ // on the desktop, in search results, etc. The app should be shown if:
+ // - The .desktop file doesn’t say it should be hidden.
+ // - The executable from the .desktop file’s Exec line isn’t denied in
+ // the user’s parental controls.
+ // - None of the flatpak app IDs from the X-Flatpak and the
+ // X-Flatpak-RenamedFrom lines are denied in the user’s parental
+ // controls.
+ shouldShowApp(appInfo) {
+ // Quick decision?
+ if (!appInfo.should_show())
+ return false;
+
+ // Are parental controls enabled (at configure time or runtime)?
+ if (!HAVE_MALCONTENT || this._disabled)
+ return true;
+
+ // Have we finished initialising yet?
+ if (!this.initialized) {
+ console.debug(`Hiding app because parental controls not yet initialised: ${appInfo.get_id()}`);
+ return false;
+ }
+
+ return this._appFilter.is_appinfo_allowed(appInfo);
+ }
+});
diff --git a/js/misc/permissionStore.js b/js/misc/permissionStore.js
new file mode 100644
index 0000000..46c5d54
--- /dev/null
+++ b/js/misc/permissionStore.js
@@ -0,0 +1,16 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported PermissionStore */
+
+const Gio = imports.gi.Gio;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const PermissionStoreIface = loadInterfaceXML('org.freedesktop.impl.portal.PermissionStore');
+const PermissionStoreProxy = Gio.DBusProxy.makeProxyWrapper(PermissionStoreIface);
+
+function PermissionStore(initCallback, cancellable) {
+ return new PermissionStoreProxy(Gio.DBus.session,
+ 'org.freedesktop.impl.portal.PermissionStore',
+ '/org/freedesktop/impl/portal/PermissionStore',
+ initCallback, cancellable);
+}
diff --git a/js/misc/signalTracker.js b/js/misc/signalTracker.js
new file mode 100644
index 0000000..3b71fbc
--- /dev/null
+++ b/js/misc/signalTracker.js
@@ -0,0 +1,269 @@
+/* exported TransientSignalHolder, connectObject, disconnectObject */
+const { GObject } = imports.gi;
+
+const destroyableTypes = [];
+
+/**
+ * @private
+ * @param {Object} obj - an object
+ * @returns {bool} - true if obj has a 'destroy' GObject signal
+ */
+function _hasDestroySignal(obj) {
+ return destroyableTypes.some(type => obj instanceof type);
+}
+
+var TransientSignalHolder = GObject.registerClass(
+class TransientSignalHolder extends GObject.Object {
+ static [GObject.signals] = {
+ 'destroy': {},
+ };
+
+ constructor(owner) {
+ super();
+
+ if (_hasDestroySignal(owner))
+ owner.connectObject('destroy', () => this.destroy(), this);
+ }
+
+ destroy() {
+ this.emit('destroy');
+ }
+});
+registerDestroyableType(TransientSignalHolder);
+
+class SignalManager {
+ /**
+ * @returns {SignalManager} - the SignalManager singleton
+ */
+ static getDefault() {
+ if (!this._singleton)
+ this._singleton = new SignalManager();
+ return this._singleton;
+ }
+
+ constructor() {
+ this._signalTrackers = new Map();
+ }
+
+ /**
+ * @param {Object} obj - object to get signal tracker for
+ * @returns {SignalTracker} - the signal tracker for object
+ */
+ getSignalTracker(obj) {
+ let signalTracker = this._signalTrackers.get(obj);
+ if (signalTracker === undefined) {
+ signalTracker = new SignalTracker(obj);
+ this._signalTrackers.set(obj, signalTracker);
+ }
+ return signalTracker;
+ }
+
+ /**
+ * @param {Object} obj - object to get signal tracker for
+ * @returns {?SignalTracker} - the signal tracker for object if it exists
+ */
+ maybeGetSignalTracker(obj) {
+ return this._signalTrackers.get(obj) ?? null;
+ }
+
+ /*
+ * @param {Object} obj - object to remove signal tracker for
+ * @returns {void}
+ */
+ removeSignalTracker(obj) {
+ this._signalTrackers.delete(obj);
+ }
+}
+
+class SignalTracker {
+ /**
+ * @param {Object=} owner - object that owns the tracker
+ */
+ constructor(owner) {
+ if (_hasDestroySignal(owner))
+ this._ownerDestroyId = owner.connect_after('destroy', () => this.clear());
+
+ this._owner = owner;
+ this._map = new Map();
+ }
+
+ /**
+ * @typedef SignalData
+ * @property {number[]} ownerSignals - a list of handler IDs
+ * @property {number} destroyId - destroy handler ID of tracked object
+ */
+
+ /**
+ * @private
+ * @param {Object} obj - a tracked object
+ * @returns {SignalData} - signal data for object
+ */
+ _getSignalData(obj) {
+ let data = this._map.get(obj);
+ if (data === undefined) {
+ data = { ownerSignals: [], destroyId: 0 };
+ this._map.set(obj, data);
+ }
+ return data;
+ }
+
+ /**
+ * @private
+ * @param {GObject.Object} obj - tracked widget
+ */
+ _trackDestroy(obj) {
+ const signalData = this._getSignalData(obj);
+ if (signalData.destroyId)
+ return;
+ signalData.destroyId = obj.connect_after('destroy', () => this.untrack(obj));
+ }
+
+ _disconnectSignalForProto(proto, obj, id) {
+ proto['disconnect'].call(obj, id);
+ }
+
+ _getObjectProto(obj) {
+ return obj instanceof GObject.Object
+ ? GObject.Object.prototype
+ : Object.getPrototypeOf(obj);
+ }
+
+ _disconnectSignal(obj, id) {
+ this._disconnectSignalForProto(this._getObjectProto(obj), obj, id);
+ }
+
+ _removeTracker() {
+ if (this._ownerDestroyId)
+ this._disconnectSignal(this._owner, this._ownerDestroyId);
+
+ SignalManager.getDefault().removeSignalTracker(this._owner);
+
+ delete this._ownerDestroyId;
+ delete this._owner;
+ }
+
+ /**
+ * @param {Object} obj - tracked object
+ * @param {...number} handlerIds - tracked handler IDs
+ * @returns {void}
+ */
+ track(obj, ...handlerIds) {
+ if (_hasDestroySignal(obj))
+ this._trackDestroy(obj);
+
+ this._getSignalData(obj).ownerSignals.push(...handlerIds);
+ }
+
+ /**
+ * @param {Object} obj - tracked object instance
+ * @returns {void}
+ */
+ untrack(obj) {
+ const { ownerSignals, destroyId } = this._getSignalData(obj);
+ this._map.delete(obj);
+
+ const ownerProto = this._getObjectProto(this._owner);
+ ownerSignals.forEach(id =>
+ this._disconnectSignalForProto(ownerProto, this._owner, id));
+ if (destroyId)
+ this._disconnectSignal(obj, destroyId);
+
+ if (this._map.size === 0)
+ this._removeTracker();
+ }
+
+ /**
+ * @returns {void}
+ */
+ clear() {
+ this._map.forEach((_, obj) => this.untrack(obj));
+ }
+
+ /**
+ * @returns {void}
+ */
+ destroy() {
+ this.clear();
+ this._removeTracker();
+ }
+}
+
+/**
+ * Connect one or more signals, and associate the handlers
+ * with a tracked object.
+ *
+ * All handlers for a particular object can be disconnected
+ * by calling disconnectObject(). If object is a {Clutter.widget},
+ * this is done automatically when the widget is destroyed.
+ *
+ * @param {object} thisObj - the emitter object
+ * @param {...any} args - a sequence of signal-name/handler pairs
+ * with an optional flags value, followed by an object to track
+ * @returns {void}
+ */
+function connectObject(thisObj, ...args) {
+ const getParams = argArray => {
+ const [signalName, handler, arg, ...rest] = argArray;
+ if (typeof arg !== 'number')
+ return [signalName, handler, 0, arg, ...rest];
+
+ const flags = arg;
+ let flagsMask = 0;
+ Object.values(GObject.ConnectFlags).forEach(v => (flagsMask |= v));
+ if (!(flags & flagsMask))
+ throw new Error(`Invalid flag value ${flags}`);
+ if (flags & GObject.ConnectFlags.SWAPPED)
+ throw new Error('Swapped signals are not supported');
+ return [signalName, handler, flags, ...rest];
+ };
+
+ const connectSignal = (emitter, signalName, handler, flags) => {
+ const isGObject = emitter instanceof GObject.Object;
+ const func = (flags & GObject.ConnectFlags.AFTER) && isGObject
+ ? 'connect_after'
+ : 'connect';
+ const emitterProto = isGObject
+ ? GObject.Object.prototype
+ : Object.getPrototypeOf(emitter);
+ return emitterProto[func].call(emitter, signalName, handler);
+ };
+
+ const signalIds = [];
+ while (args.length > 1) {
+ const [signalName, handler, flags, ...rest] = getParams(args);
+ signalIds.push(connectSignal(thisObj, signalName, handler, flags));
+ args = rest;
+ }
+
+ const obj = args.at(0) ?? globalThis;
+ const tracker = SignalManager.getDefault().getSignalTracker(thisObj);
+ tracker.track(obj, ...signalIds);
+}
+
+/**
+ * Disconnect all signals that were connected for
+ * the specified tracked object
+ *
+ * @param {Object} thisObj - the emitter object
+ * @param {Object} obj - the tracked object
+ * @returns {void}
+ */
+function disconnectObject(thisObj, obj) {
+ SignalManager.getDefault().maybeGetSignalTracker(thisObj)?.untrack(obj);
+}
+
+/**
+ * Register a GObject type as having a 'destroy' signal
+ * that should disconnect all handlers
+ *
+ * @param {GObject.Type} gtype - a GObject type
+ */
+function registerDestroyableType(gtype) {
+ if (!GObject.type_is_a(gtype, GObject.Object))
+ throw new Error(`${gtype} is not a GObject subclass`);
+
+ if (!GObject.signal_lookup('destroy', gtype))
+ throw new Error(`${gtype} does not have a destroy signal`);
+
+ destroyableTypes.push(gtype);
+}
diff --git a/js/misc/signals.js b/js/misc/signals.js
new file mode 100644
index 0000000..f4acced
--- /dev/null
+++ b/js/misc/signals.js
@@ -0,0 +1,22 @@
+const Signals = imports.signals;
+const SignalTracker = imports.misc.signalTracker;
+
+var EventEmitter = class EventEmitter {
+ connectObject(...args) {
+ return SignalTracker.connectObject(this, ...args);
+ }
+
+ disconnectObject(...args) {
+ return SignalTracker.disconnectObject(this, ...args);
+ }
+
+ connect_object(...args) {
+ return this.connectObject(...args);
+ }
+
+ disconnect_object(...args) {
+ return this.disconnectObject(...args);
+ }
+};
+
+Signals.addSignalMethods(EventEmitter.prototype);
diff --git a/js/misc/smartcardManager.js b/js/misc/smartcardManager.js
new file mode 100644
index 0000000..661b337
--- /dev/null
+++ b/js/misc/smartcardManager.js
@@ -0,0 +1,119 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported getSmartcardManager */
+
+const Gio = imports.gi.Gio;
+const Signals = imports.misc.signals;
+
+const ObjectManager = imports.misc.objectManager;
+
+const SmartcardTokenIface = `
+<node>
+<interface name="org.gnome.SettingsDaemon.Smartcard.Token">
+ <property name="Name" type="s" access="read"/>
+ <property name="Driver" type="o" access="read"/>
+ <property name="IsInserted" type="b" access="read"/>
+ <property name="UsedToLogin" type="b" access="read"/>
+</interface>
+</node>`;
+
+let _smartcardManager = null;
+
+function getSmartcardManager() {
+ if (_smartcardManager == null)
+ _smartcardManager = new SmartcardManager();
+
+ return _smartcardManager;
+}
+
+var SmartcardManager = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._objectManager = new ObjectManager.ObjectManager({
+ connection: Gio.DBus.session,
+ name: 'org.gnome.SettingsDaemon.Smartcard',
+ objectPath: '/org/gnome/SettingsDaemon/Smartcard',
+ knownInterfaces: [SmartcardTokenIface],
+ onLoaded: this._onLoaded.bind(this),
+ });
+ this._insertedTokens = {};
+ this._loginToken = null;
+ }
+
+ _onLoaded() {
+ let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
+
+ for (let i = 0; i < tokens.length; i++)
+ this._addToken(tokens[i]);
+
+ this._objectManager.connect('interface-added', (objectManager, interfaceName, proxy) => {
+ if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
+ this._addToken(proxy);
+ });
+
+ this._objectManager.connect('interface-removed', (objectManager, interfaceName, proxy) => {
+ if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
+ this._removeToken(proxy);
+ });
+ }
+
+ _updateToken(token) {
+ let objectPath = token.get_object_path();
+
+ delete this._insertedTokens[objectPath];
+
+ if (token.IsInserted)
+ this._insertedTokens[objectPath] = token;
+
+ if (token.UsedToLogin)
+ this._loginToken = token;
+ }
+
+ _addToken(token) {
+ this._updateToken(token);
+
+ token.connect('g-properties-changed', (proxy, properties) => {
+ const isInsertedChanged = !!properties.lookup_value('IsInserted', null);
+ if (isInsertedChanged) {
+ this._updateToken(token);
+
+ if (token.IsInserted)
+ this.emit('smartcard-inserted', token);
+ else
+ this.emit('smartcard-removed', token);
+ }
+ });
+
+ // Emit a smartcard-inserted at startup if it's already plugged in
+ if (token.IsInserted)
+ this.emit('smartcard-inserted', token);
+ }
+
+ _removeToken(token) {
+ let objectPath = token.get_object_path();
+
+ if (this._insertedTokens[objectPath] == token) {
+ delete this._insertedTokens[objectPath];
+ this.emit('smartcard-removed', token);
+ }
+
+ if (this._loginToken == token)
+ this._loginToken = null;
+
+ token.disconnectAll();
+ }
+
+ hasInsertedTokens() {
+ return Object.keys(this._insertedTokens).length > 0;
+ }
+
+ hasInsertedLoginToken() {
+ if (!this._loginToken)
+ return false;
+
+ if (!this._loginToken.IsInserted)
+ return false;
+
+ return true;
+ }
+};
diff --git a/js/misc/systemActions.js b/js/misc/systemActions.js
new file mode 100644
index 0000000..c57afe5
--- /dev/null
+++ b/js/misc/systemActions.js
@@ -0,0 +1,474 @@
+/* exported getDefault */
+const { AccountsService, Clutter, Gdm, Gio, GLib, GObject, Meta } = imports.gi;
+
+const GnomeSession = imports.misc.gnomeSession;
+const LoginManager = imports.misc.loginManager;
+const Main = imports.ui.main;
+const Screenshot = imports.ui.screenshot;
+
+const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
+const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
+const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
+const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
+const DISABLE_LOG_OUT_KEY = 'disable-log-out';
+const DISABLE_RESTART_KEY = 'disable-restart-buttons';
+const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
+
+const POWER_OFF_ACTION_ID = 'power-off';
+const RESTART_ACTION_ID = 'restart';
+const LOCK_SCREEN_ACTION_ID = 'lock-screen';
+const LOGOUT_ACTION_ID = 'logout';
+const SUSPEND_ACTION_ID = 'suspend';
+const SWITCH_USER_ACTION_ID = 'switch-user';
+const LOCK_ORIENTATION_ACTION_ID = 'lock-orientation';
+const SCREENSHOT_UI_ACTION_ID = 'open-screenshot-ui';
+
+let _singleton = null;
+
+function getDefault() {
+ if (_singleton == null)
+ _singleton = new SystemActions();
+
+ return _singleton;
+}
+
+const SystemActions = GObject.registerClass({
+ Properties: {
+ 'can-power-off': GObject.ParamSpec.boolean(
+ 'can-power-off', 'can-power-off', 'can-power-off',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'can-restart': GObject.ParamSpec.boolean(
+ 'can-restart', 'can-restart', 'can-restart',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'can-suspend': GObject.ParamSpec.boolean(
+ 'can-suspend', 'can-suspend', 'can-suspend',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'can-lock-screen': GObject.ParamSpec.boolean(
+ 'can-lock-screen', 'can-lock-screen', 'can-lock-screen',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'can-switch-user': GObject.ParamSpec.boolean(
+ 'can-switch-user', 'can-switch-user', 'can-switch-user',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'can-logout': GObject.ParamSpec.boolean(
+ 'can-logout', 'can-logout', 'can-logout',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'can-lock-orientation': GObject.ParamSpec.boolean(
+ 'can-lock-orientation', 'can-lock-orientation', 'can-lock-orientation',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'orientation-lock-icon': GObject.ParamSpec.string(
+ 'orientation-lock-icon', 'orientation-lock-icon', 'orientation-lock-icon',
+ GObject.ParamFlags.READWRITE,
+ null),
+ },
+}, class SystemActions extends GObject.Object {
+ _init() {
+ super._init();
+
+ this._canHavePowerOff = true;
+ this._canHaveSuspend = true;
+
+ function tokenizeKeywords(keywords) {
+ return keywords.split(';').map(keyword => GLib.str_tokenize_and_fold(keyword, null)).flat(2);
+ }
+
+ this._actions = new Map();
+ this._actions.set(POWER_OFF_ACTION_ID, {
+ // Translators: The name of the power-off action in search
+ name: C_("search-result", "Power Off"),
+ iconName: 'system-shutdown-symbolic',
+ // Translators: A list of keywords that match the power-off action, separated by semicolons
+ keywords: tokenizeKeywords(_('power off;shutdown;halt;stop')),
+ available: false,
+ });
+ this._actions.set(RESTART_ACTION_ID, {
+ // Translators: The name of the restart action in search
+ name: C_('search-result', 'Restart'),
+ iconName: 'system-reboot-symbolic',
+ // Translators: A list of keywords that match the restart action, separated by semicolons
+ keywords: tokenizeKeywords(_('reboot;restart;')),
+ available: false,
+ });
+ this._actions.set(LOCK_SCREEN_ACTION_ID, {
+ // Translators: The name of the lock screen action in search
+ name: C_("search-result", "Lock Screen"),
+ iconName: 'system-lock-screen-symbolic',
+ // Translators: A list of keywords that match the lock screen action, separated by semicolons
+ keywords: tokenizeKeywords(_('lock screen')),
+ available: false,
+ });
+ this._actions.set(LOGOUT_ACTION_ID, {
+ // Translators: The name of the logout action in search
+ name: C_("search-result", "Log Out"),
+ iconName: 'system-log-out-symbolic',
+ // Translators: A list of keywords that match the logout action, separated by semicolons
+ keywords: tokenizeKeywords(_('logout;log out;sign off')),
+ available: false,
+ });
+ this._actions.set(SUSPEND_ACTION_ID, {
+ // Translators: The name of the suspend action in search
+ name: C_("search-result", "Suspend"),
+ iconName: 'media-playback-pause-symbolic',
+ // Translators: A list of keywords that match the suspend action, separated by semicolons
+ keywords: tokenizeKeywords(_('suspend;sleep')),
+ available: false,
+ });
+ this._actions.set(SWITCH_USER_ACTION_ID, {
+ // Translators: The name of the switch user action in search
+ name: C_("search-result", "Switch User"),
+ iconName: 'system-switch-user-symbolic',
+ // Translators: A list of keywords that match the switch user action, separated by semicolons
+ keywords: tokenizeKeywords(_('switch user')),
+ available: false,
+ });
+ this._actions.set(LOCK_ORIENTATION_ACTION_ID, {
+ name: '',
+ iconName: '',
+ // Translators: A list of keywords that match the lock orientation action, separated by semicolons
+ keywords: tokenizeKeywords(_('lock orientation;unlock orientation;screen;rotation')),
+ available: false,
+ });
+ this._actions.set(SCREENSHOT_UI_ACTION_ID, {
+ // Translators: The name of the screenshot UI action in search
+ name: C_('search-result', 'Take a Screenshot'),
+ iconName: 'record-screen-symbolic',
+ // Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+ keywords: tokenizeKeywords(_('screenshot;screencast;snip;capture;record')),
+ available: true,
+ });
+
+ this._loginScreenSettings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
+ this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA });
+ this._orientationSettings = new Gio.Settings({ schema_id: 'org.gnome.settings-daemon.peripherals.touchscreen' });
+
+ this._session = new GnomeSession.SessionManager();
+ this._loginManager = LoginManager.getLoginManager();
+ this._monitorManager = Meta.MonitorManager.get();
+
+ this._userManager = AccountsService.UserManager.get_default();
+
+ this._userManager.connect('notify::is-loaded',
+ () => this._updateMultiUser());
+ this._userManager.connect('notify::has-multiple-users',
+ () => this._updateMultiUser());
+ this._userManager.connect('user-added',
+ () => this._updateMultiUser());
+ this._userManager.connect('user-removed',
+ () => this._updateMultiUser());
+
+ this._lockdownSettings.connect(`changed::${DISABLE_USER_SWITCH_KEY}`,
+ () => this._updateSwitchUser());
+ this._lockdownSettings.connect(`changed::${DISABLE_LOG_OUT_KEY}`,
+ () => this._updateLogout());
+ global.settings.connect(`changed::${ALWAYS_SHOW_LOG_OUT_KEY}`,
+ () => this._updateLogout());
+
+ this._lockdownSettings.connect(`changed::${DISABLE_LOCK_SCREEN_KEY}`,
+ () => this._updateLockScreen());
+
+ this._lockdownSettings.connect(`changed::${DISABLE_LOG_OUT_KEY}`,
+ () => this._updateHaveShutdown());
+
+ this.forceUpdate();
+
+ this._orientationSettings.connect('changed::orientation-lock', () => {
+ this._updateOrientationLock();
+ this._updateOrientationLockStatus();
+ });
+ Main.layoutManager.connect('monitors-changed',
+ () => this._updateOrientationLock());
+ this._monitorManager.connect('notify::panel-orientation-managed',
+ () => this._updateOrientationLock());
+ this._updateOrientationLock();
+ this._updateOrientationLockStatus();
+
+ Main.sessionMode.connect('updated', () => this._sessionUpdated());
+ this._sessionUpdated();
+ }
+
+ get canPowerOff() {
+ return this._actions.get(POWER_OFF_ACTION_ID).available;
+ }
+
+ get canRestart() {
+ return this._actions.get(RESTART_ACTION_ID).available;
+ }
+
+ get canSuspend() {
+ return this._actions.get(SUSPEND_ACTION_ID).available;
+ }
+
+ get canLockScreen() {
+ return this._actions.get(LOCK_SCREEN_ACTION_ID).available;
+ }
+
+ get canSwitchUser() {
+ return this._actions.get(SWITCH_USER_ACTION_ID).available;
+ }
+
+ get canLogout() {
+ return this._actions.get(LOGOUT_ACTION_ID).available;
+ }
+
+ get canLockOrientation() {
+ return this._actions.get(LOCK_ORIENTATION_ACTION_ID).available;
+ }
+
+ get orientationLockIcon() {
+ return this._actions.get(LOCK_ORIENTATION_ACTION_ID).iconName;
+ }
+
+ _updateOrientationLock() {
+ const available = this._monitorManager.get_panel_orientation_managed();
+
+ this._actions.get(LOCK_ORIENTATION_ACTION_ID).available = available;
+
+ this.notify('can-lock-orientation');
+ }
+
+ _updateOrientationLockStatus() {
+ let locked = this._orientationSettings.get_boolean('orientation-lock');
+ let action = this._actions.get(LOCK_ORIENTATION_ACTION_ID);
+
+ // Translators: The name of the lock orientation action in search
+ // and in the system status menu
+ let name = locked
+ ? C_('search-result', 'Unlock Screen Rotation')
+ : C_('search-result', 'Lock Screen Rotation');
+ let iconName = locked
+ ? 'rotation-locked-symbolic'
+ : 'rotation-allowed-symbolic';
+
+ action.name = name;
+ action.iconName = iconName;
+
+ this.notify('orientation-lock-icon');
+ }
+
+ _sessionUpdated() {
+ this._updateLockScreen();
+ this._updatePowerOff();
+ this._updateSuspend();
+ this._updateMultiUser();
+ }
+
+ forceUpdate() {
+ // Whether those actions are available or not depends on both lockdown
+ // settings and Polkit policy - we don't get change notifications for the
+ // latter, so their value may be outdated; force an update now
+ this._updateHaveShutdown();
+ this._updateHaveSuspend();
+ }
+
+ getMatchingActions(terms) {
+ // terms is a list of strings
+ terms = terms.map(
+ term => GLib.str_tokenize_and_fold(term, null)[0]).flat(2);
+
+ // tokenizing may return an empty array
+ if (terms.length === 0)
+ return [];
+
+ let results = [];
+
+ for (let [key, { available, keywords }] of this._actions) {
+ if (available && terms.every(t => keywords.some(k => k.startsWith(t))))
+ results.push(key);
+ }
+
+ return results;
+ }
+
+ getName(id) {
+ return this._actions.get(id).name;
+ }
+
+ getIconName(id) {
+ return this._actions.get(id).iconName;
+ }
+
+ activateAction(id) {
+ switch (id) {
+ case POWER_OFF_ACTION_ID:
+ this.activatePowerOff();
+ break;
+ case RESTART_ACTION_ID:
+ this.activateRestart();
+ break;
+ case LOCK_SCREEN_ACTION_ID:
+ this.activateLockScreen();
+ break;
+ case LOGOUT_ACTION_ID:
+ this.activateLogout();
+ break;
+ case SUSPEND_ACTION_ID:
+ this.activateSuspend();
+ break;
+ case SWITCH_USER_ACTION_ID:
+ this.activateSwitchUser();
+ break;
+ case LOCK_ORIENTATION_ACTION_ID:
+ this.activateLockOrientation();
+ break;
+ case SCREENSHOT_UI_ACTION_ID:
+ this.activateScreenshotUI();
+ break;
+ }
+ }
+
+ _updateLockScreen() {
+ let showLock = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
+ let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
+ this._actions.get(LOCK_SCREEN_ACTION_ID).available = showLock && allowLockScreen && LoginManager.canLock();
+ this.notify('can-lock-screen');
+ }
+
+ async _updateHaveShutdown() {
+ try {
+ const [canShutdown] = await this._session.CanShutdownAsync();
+ this._canHavePowerOff = canShutdown;
+ } catch (e) {
+ this._canHavePowerOff = false;
+ }
+ this._updatePowerOff();
+ }
+
+ _updatePowerOff() {
+ let disabled = Main.sessionMode.isLocked ||
+ (Main.sessionMode.isGreeter &&
+ this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
+ this._actions.get(POWER_OFF_ACTION_ID).available = this._canHavePowerOff && !disabled;
+ this.notify('can-power-off');
+
+ this._actions.get(RESTART_ACTION_ID).available = this._canHavePowerOff && !disabled;
+ this.notify('can-restart');
+ }
+
+ async _updateHaveSuspend() {
+ const {canSuspend, needsAuth} = await this._loginManager.canSuspend();
+ this._canHaveSuspend = canSuspend;
+ this._suspendNeedsAuth = needsAuth;
+ this._updateSuspend();
+ }
+
+ _updateSuspend() {
+ let disabled = (Main.sessionMode.isLocked &&
+ this._suspendNeedsAuth) ||
+ (Main.sessionMode.isGreeter &&
+ this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
+ this._actions.get(SUSPEND_ACTION_ID).available = this._canHaveSuspend && !disabled;
+ this.notify('can-suspend');
+ }
+
+ _updateMultiUser() {
+ this._updateLogout();
+ this._updateSwitchUser();
+ }
+
+ _updateSwitchUser() {
+ let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
+ let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
+ let shouldShowInMode = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
+
+ let visible = allowSwitch && multiUser && shouldShowInMode;
+ this._actions.get(SWITCH_USER_ACTION_ID).available = visible;
+ this.notify('can-switch-user');
+
+ return visible;
+ }
+
+ _updateLogout() {
+ let user = this._userManager.get_user(GLib.get_user_name());
+
+ let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
+ let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
+ let systemAccount = user.system_account;
+ let localAccount = user.local_account;
+ let multiUser = this._userManager.has_multiple_users;
+ let multiSession = Gdm.get_session_ids().length > 1;
+ let shouldShowInMode = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
+
+ let visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount) && shouldShowInMode;
+ this._actions.get(LOGOUT_ACTION_ID).available = visible;
+ this.notify('can-logout');
+
+ return visible;
+ }
+
+ activateLockOrientation() {
+ if (!this._actions.get(LOCK_ORIENTATION_ACTION_ID).available)
+ throw new Error('The lock-orientation action is not available!');
+
+ let locked = this._orientationSettings.get_boolean('orientation-lock');
+ this._orientationSettings.set_boolean('orientation-lock', !locked);
+ }
+
+ activateLockScreen() {
+ if (!this._actions.get(LOCK_SCREEN_ACTION_ID).available)
+ throw new Error('The lock-screen action is not available!');
+
+ Main.screenShield.lock(true);
+ }
+
+ activateSwitchUser() {
+ if (!this._actions.get(SWITCH_USER_ACTION_ID).available)
+ throw new Error('The switch-user action is not available!');
+
+ if (Main.screenShield)
+ Main.screenShield.lock(false);
+
+ Clutter.threads_add_repaint_func_full(Clutter.RepaintFlags.POST_PAINT, () => {
+ Gdm.goto_login_session_sync(null);
+ return false;
+ });
+ }
+
+ activateLogout() {
+ if (!this._actions.get(LOGOUT_ACTION_ID).available)
+ throw new Error('The logout action is not available!');
+
+ Main.overview.hide();
+ this._session.LogoutAsync(0).catch(logError);
+ }
+
+ activatePowerOff() {
+ if (!this._actions.get(POWER_OFF_ACTION_ID).available)
+ throw new Error('The power-off action is not available!');
+
+ this._session.ShutdownAsync(0).catch(logError);
+ }
+
+ activateRestart() {
+ if (!this._actions.get(RESTART_ACTION_ID).available)
+ throw new Error('The restart action is not available!');
+
+ this._session.RebootAsync().catch(logError);
+ }
+
+ activateSuspend() {
+ if (!this._actions.get(SUSPEND_ACTION_ID).available)
+ throw new Error('The suspend action is not available!');
+
+ this._loginManager.suspend();
+ }
+
+ activateScreenshotUI() {
+ if (!this._actions.get(SCREENSHOT_UI_ACTION_ID).available)
+ throw new Error('The screenshot UI action is not available!');
+
+ if (this._overviewHiddenId)
+ return;
+
+ this._overviewHiddenId = Main.overview.connect('hidden', () => {
+ Main.overview.disconnect(this._overviewHiddenId);
+ delete this._overviewHiddenId;
+ Screenshot.showScreenshotUI();
+ });
+ }
+});
diff --git a/js/misc/util.js b/js/misc/util.js
new file mode 100644
index 0000000..e6065c4
--- /dev/null
+++ b/js/misc/util.js
@@ -0,0 +1,617 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine,
+ formatTime, formatTimeSpan, createTimeLabel, insertSorted,
+ ensureActorVisibleInScrollView, wiggle, lerp, GNOMEversionCompare,
+ DBusSenderChecker, Highlighter */
+
+const { Clutter, Gio, GLib, Shell, St, GnomeDesktop } = imports.gi;
+const Gettext = imports.gettext;
+
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+var SCROLL_TIME = 100;
+
+const WIGGLE_OFFSET = 6;
+const WIGGLE_DURATION = 65;
+const N_WIGGLES = 3;
+
+// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
+const _balancedParens = '\\([^\\s()<>]+\\)';
+const _leadingJunk = '[\\s`(\\[{\'\\"<\u00AB\u201C\u2018]';
+const _notTrailingJunk = '[^\\s`!()\\[\\]{};:\'\\".,<>?\u00AB\u00BB\u200E\u200F\u201C\u201D\u2018\u2019\u202A\u202C]';
+
+const _urlRegexp = new RegExp(
+ `(^|${_leadingJunk})` +
+ '(' +
+ '(?:' +
+ '(?:http|https|ftp)://' + // scheme://
+ '|' +
+ 'www\\d{0,3}[.]' + // www.
+ '|' +
+ '[a-z0-9.\\-]+[.][a-z]{2,4}/' + // foo.xx/
+ ')' +
+ '(?:' + // one or more:
+ '[^\\s()<>]+' + // run of non-space non-()
+ '|' + // or
+ `${_balancedParens}` + // balanced parens
+ ')+' +
+ '(?:' + // end with:
+ `${_balancedParens}` + // balanced parens
+ '|' + // or
+ `${_notTrailingJunk}` + // last non-junk char
+ ')' +
+ ')', 'gi');
+
+let _desktopSettings = null;
+
+// findUrls:
+// @str: string to find URLs in
+//
+// Searches @str for URLs and returns an array of objects with %url
+// properties showing the matched URL string, and %pos properties indicating
+// the position within @str where the URL was found.
+//
+// Return value: the list of match objects, as described above
+function findUrls(str) {
+ let res = [], match;
+ while ((match = _urlRegexp.exec(str)))
+ res.push({ url: match[2], pos: match.index + match[1].length });
+ return res;
+}
+
+// spawn:
+// @argv: an argv array
+//
+// Runs @argv in the background, handling any errors that occur
+// when trying to start the program.
+function spawn(argv) {
+ try {
+ trySpawn(argv);
+ } catch (err) {
+ _handleSpawnError(argv[0], err);
+ }
+}
+
+// spawnCommandLine:
+// @commandLine: a command line
+//
+// Runs @commandLine in the background, handling any errors that
+// occur when trying to parse or start the program.
+function spawnCommandLine(commandLine) {
+ try {
+ let [success_, argv] = GLib.shell_parse_argv(commandLine);
+ trySpawn(argv);
+ } catch (err) {
+ _handleSpawnError(commandLine, err);
+ }
+}
+
+// spawnApp:
+// @argv: an argv array
+//
+// Runs @argv as if it was an application, handling startup notification
+function spawnApp(argv) {
+ try {
+ let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
+ Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
+
+ let context = global.create_app_launch_context(0, -1);
+ app.launch([], context);
+ } catch (err) {
+ _handleSpawnError(argv[0], err);
+ }
+}
+
+// trySpawn:
+// @argv: an argv array
+//
+// Runs @argv in the background. If launching @argv fails,
+// this will throw an error.
+function trySpawn(argv) {
+ var success_, pid;
+ try {
+ [success_, pid] = GLib.spawn_async(
+ null, argv, null,
+ GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
+ () => {
+ try {
+ global.context.restore_rlimit_nofile();
+ } catch (err) {
+ }
+ }
+ );
+ } catch (err) {
+ /* Rewrite the error in case of ENOENT */
+ if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
+ throw new GLib.SpawnError({
+ code: GLib.SpawnError.NOENT,
+ message: _('Command not found'),
+ });
+ } else if (err instanceof GLib.Error) {
+ // The exception from gjs contains an error string like:
+ // Error invoking GLib.spawn_command_line_async: Failed to
+ // execute child process "foo" (No such file or directory)
+ // We are only interested in the part in the parentheses. (And
+ // we can't pattern match the text, since it gets localized.)
+ let message = err.message.replace(/.*\((.+)\)/, '$1');
+ throw new err.constructor({ code: err.code, message });
+ } else {
+ throw err;
+ }
+ }
+
+ // Async call, we don't need the reply though
+ GnomeDesktop.start_systemd_scope(argv[0], pid, null, null, null, () => {});
+
+ // Dummy child watch; we don't want to double-fork internally
+ // because then we lose the parent-child relationship, which
+ // can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
+ GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, () => {});
+}
+
+// trySpawnCommandLine:
+// @commandLine: a command line
+//
+// Runs @commandLine in the background. If launching @commandLine
+// fails, this will throw an error.
+function trySpawnCommandLine(commandLine) {
+ let success_, argv;
+
+ try {
+ [success_, argv] = GLib.shell_parse_argv(commandLine);
+ } catch (err) {
+ // Replace "Error invoking GLib.shell_parse_argv: " with
+ // something nicer
+ err.message = err.message.replace(/[^:]*: /, `${_('Could not parse command:')}\n`);
+ throw err;
+ }
+
+ trySpawn(argv);
+}
+
+function _handleSpawnError(command, err) {
+ let title = _("Execution of “%s” failed:").format(command);
+ Main.notifyError(title, err.message);
+}
+
+function formatTimeSpan(date) {
+ let now = GLib.DateTime.new_now_local();
+
+ let timespan = now.difference(date);
+
+ let minutesAgo = timespan / GLib.TIME_SPAN_MINUTE;
+ let hoursAgo = timespan / GLib.TIME_SPAN_HOUR;
+ let daysAgo = timespan / GLib.TIME_SPAN_DAY;
+ let weeksAgo = daysAgo / 7;
+ let monthsAgo = daysAgo / 30;
+ let yearsAgo = weeksAgo / 52;
+
+ if (minutesAgo < 5)
+ return _("Just now");
+ if (hoursAgo < 1) {
+ return Gettext.ngettext("%d minute ago",
+ "%d minutes ago", minutesAgo).format(minutesAgo);
+ }
+ if (daysAgo < 1) {
+ return Gettext.ngettext("%d hour ago",
+ "%d hours ago", hoursAgo).format(hoursAgo);
+ }
+ if (daysAgo < 2)
+ return _("Yesterday");
+ if (daysAgo < 15) {
+ return Gettext.ngettext("%d day ago",
+ "%d days ago", daysAgo).format(daysAgo);
+ }
+ if (weeksAgo < 8) {
+ return Gettext.ngettext("%d week ago",
+ "%d weeks ago", weeksAgo).format(weeksAgo);
+ }
+ if (yearsAgo < 1) {
+ return Gettext.ngettext("%d month ago",
+ "%d months ago", monthsAgo).format(monthsAgo);
+ }
+ return Gettext.ngettext("%d year ago",
+ "%d years ago", yearsAgo).format(yearsAgo);
+}
+
+function formatTime(time, params) {
+ let date;
+ // HACK: The built-in Date type sucks at timezones, which we need for the
+ // world clock; it's often more convenient though, so allow either
+ // Date or GLib.DateTime as parameter
+ if (time instanceof Date)
+ date = GLib.DateTime.new_from_unix_local(time.getTime() / 1000);
+ else
+ date = time;
+
+ let now = GLib.DateTime.new_now_local();
+
+ let daysAgo = now.difference(date) / (24 * 60 * 60 * 1000 * 1000);
+
+ let format;
+
+ if (_desktopSettings == null)
+ _desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
+ let clockFormat = _desktopSettings.get_string('clock-format');
+
+ params = Params.parse(params, {
+ timeOnly: false,
+ ampm: true,
+ });
+
+ if (clockFormat == '24h') {
+ // Show only the time if date is on today
+ if (daysAgo < 1 || params.timeOnly)
+ /* Translators: Time in 24h format */
+ format = N_("%H\u2236%M");
+ // Show the word "Yesterday" and time if date is on yesterday
+ else if (daysAgo < 2)
+ /* Translators: this is the word "Yesterday" followed by a
+ time string in 24h format. i.e. "Yesterday, 14:30" */
+ // xgettext:no-c-format
+ format = N_("Yesterday, %H\u2236%M");
+ // Show a week day and time if date is in the last week
+ else if (daysAgo < 7)
+ /* Translators: this is the week day name followed by a time
+ string in 24h format. i.e. "Monday, 14:30" */
+ // xgettext:no-c-format
+ format = N_("%A, %H\u2236%M");
+ else if (date.get_year() == now.get_year())
+ /* Translators: this is the month name and day number
+ followed by a time string in 24h format.
+ i.e. "May 25, 14:30" */
+ // xgettext:no-c-format
+ format = N_("%B %-d, %H\u2236%M");
+ else
+ /* Translators: this is the month name, day number, year
+ number followed by a time string in 24h format.
+ i.e. "May 25 2012, 14:30" */
+ // xgettext:no-c-format
+ format = N_("%B %-d %Y, %H\u2236%M");
+ } else {
+ // Show only the time if date is on today
+ if (daysAgo < 1 || params.timeOnly) // eslint-disable-line no-lonely-if
+ /* Translators: Time in 12h format */
+ format = N_("%l\u2236%M %p");
+ // Show the word "Yesterday" and time if date is on yesterday
+ else if (daysAgo < 2)
+ /* Translators: this is the word "Yesterday" followed by a
+ time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+ // xgettext:no-c-format
+ format = N_("Yesterday, %l\u2236%M %p");
+ // Show a week day and time if date is in the last week
+ else if (daysAgo < 7)
+ /* Translators: this is the week day name followed by a time
+ string in 12h format. i.e. "Monday, 2:30 pm" */
+ // xgettext:no-c-format
+ format = N_("%A, %l\u2236%M %p");
+ else if (date.get_year() == now.get_year())
+ /* Translators: this is the month name and day number
+ followed by a time string in 12h format.
+ i.e. "May 25, 2:30 pm" */
+ // xgettext:no-c-format
+ format = N_("%B %-d, %l\u2236%M %p");
+ else
+ /* Translators: this is the month name, day number, year
+ number followed by a time string in 12h format.
+ i.e. "May 25 2012, 2:30 pm"*/
+ // xgettext:no-c-format
+ format = N_("%B %-d %Y, %l\u2236%M %p");
+ }
+
+ // Time in short 12h format, without the equivalent of "AM" or "PM"; used
+ // when it is clear from the context
+ if (!params.ampm)
+ format = format.replace(/\s*%p/g, '');
+
+ let formattedTime = date.format(Shell.util_translate_time_string(format));
+ // prepend LTR-mark to colon/ratio to force a text direction on times
+ return formattedTime.replace(/([:\u2236])/g, '\u200e$1');
+}
+
+function createTimeLabel(date, params) {
+ if (_desktopSettings == null)
+ _desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
+
+ let label = new St.Label({ text: formatTime(date, params) });
+ _desktopSettings.connectObject(
+ 'changed::clock-format', () => (label.text = formatTime(date, params)),
+ label);
+ return label;
+}
+
+// lowerBound:
+// @array: an array or array-like object, already sorted
+// according to @cmp
+// @val: the value to add
+// @cmp: a comparator (or undefined to compare as numbers)
+//
+// Returns the position of the first element that is not
+// lower than @val, according to @cmp.
+// That is, returns the first position at which it
+// is possible to insert @val without violating the
+// order.
+// This is quite like an ordinary binary search, except
+// that it doesn't stop at first element comparing equal.
+
+function lowerBound(array, val, cmp) {
+ let min, max, mid, v;
+ cmp ||= (a, b) => a - b;
+
+ if (array.length == 0)
+ return 0;
+
+ min = 0;
+ max = array.length;
+ while (min < (max - 1)) {
+ mid = Math.floor((min + max) / 2);
+ v = cmp(array[mid], val);
+
+ if (v < 0)
+ min = mid + 1;
+ else
+ max = mid;
+ }
+
+ return min == max || cmp(array[min], val) < 0 ? max : min;
+}
+
+// insertSorted:
+// @array: an array sorted according to @cmp
+// @val: a value to insert
+// @cmp: the sorting function
+//
+// Inserts @val into @array, preserving the
+// sorting invariants.
+// Returns the position at which it was inserted
+function insertSorted(array, val, cmp) {
+ let pos = lowerBound(array, val, cmp);
+ array.splice(pos, 0, val);
+
+ return pos;
+}
+
+function ensureActorVisibleInScrollView(scrollView, actor) {
+ let adjustment = scrollView.vscroll.adjustment;
+ let [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values();
+
+ let offset = 0;
+ let vfade = scrollView.get_effect("fade");
+ if (vfade)
+ offset = vfade.fade_margins.top;
+
+ let box = actor.get_allocation_box();
+ let y1 = box.y1, y2 = box.y2;
+
+ let parent = actor.get_parent();
+ while (parent != scrollView) {
+ if (!parent)
+ throw new Error("actor not in scroll view");
+
+ box = parent.get_allocation_box();
+ y1 += box.y1;
+ y2 += box.y1;
+ parent = parent.get_parent();
+ }
+
+ if (y1 < value + offset)
+ value = Math.max(0, y1 - offset);
+ else if (y2 > value + pageSize - offset)
+ value = Math.min(upper, y2 + offset - pageSize);
+ else
+ return;
+
+ adjustment.ease(value, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: SCROLL_TIME,
+ });
+}
+
+function wiggle(actor, params) {
+ if (!St.Settings.get().enable_animations)
+ return;
+
+ params = Params.parse(params, {
+ offset: WIGGLE_OFFSET,
+ duration: WIGGLE_DURATION,
+ wiggleCount: N_WIGGLES,
+ });
+ actor.translation_x = 0;
+
+ // Accelerate before wiggling
+ actor.ease({
+ translation_x: -params.offset,
+ duration: params.duration,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ // Wiggle
+ actor.ease({
+ translation_x: params.offset,
+ duration: params.duration,
+ mode: Clutter.AnimationMode.LINEAR,
+ repeatCount: params.wiggleCount,
+ autoReverse: true,
+ onComplete: () => {
+ // Decelerate and return to the original position
+ actor.ease({
+ translation_x: 0,
+ duration: params.duration,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+ },
+ });
+ },
+ });
+}
+
+function lerp(start, end, progress) {
+ return start + progress * (end - start);
+}
+
+// _GNOMEversionToNumber:
+// @version: a GNOME version element
+//
+// Like Number() but returns sortable values for special-cases
+// 'alpha' and 'beta'. Returns NaN for unhandled 'versions'.
+function _GNOMEversionToNumber(version) {
+ let ret = Number(version);
+ if (!isNaN(ret))
+ return ret;
+ if (version === 'alpha')
+ return -2;
+ if (version === 'beta')
+ return -1;
+ return ret;
+}
+
+// GNOMEversionCompare:
+// @version1: a string containing a GNOME version
+// @version2: a string containing another GNOME version
+//
+// Returns an integer less than, equal to, or greater than
+// zero, if version1 is older, equal or newer than version2
+function GNOMEversionCompare(version1, version2) {
+ const v1Array = version1.split('.');
+ const v2Array = version2.split('.');
+
+ for (let i = 0; i < Math.max(v1Array.length, v2Array.length); i++) {
+ let elemV1 = _GNOMEversionToNumber(v1Array[i] || '0');
+ let elemV2 = _GNOMEversionToNumber(v2Array[i] || '0');
+ if (elemV1 < elemV2)
+ return -1;
+ if (elemV1 > elemV2)
+ return 1;
+ }
+
+ return 0;
+}
+
+var DBusSenderChecker = class {
+ /**
+ * @param {string[]} allowList - list of allowed well-known names
+ */
+ constructor(allowList) {
+ this._allowlistMap = new Map();
+
+ this._uninitializedNames = new Set(allowList);
+ this._initializedPromise = new Promise(resolve => {
+ this._resolveInitialized = resolve;
+ });
+
+ this._watchList = allowList.map(name => {
+ return Gio.DBus.watch_name(Gio.BusType.SESSION,
+ name,
+ Gio.BusNameWatcherFlags.NONE,
+ (conn_, name_, owner) => {
+ this._allowlistMap.set(name, owner);
+ this._checkAndResolveInitialized(name);
+ },
+ () => {
+ this._allowlistMap.delete(name);
+ this._checkAndResolveInitialized(name);
+ });
+ });
+ }
+
+ /**
+ * @param {string} name - bus name for which the watcher got initialized
+ */
+ _checkAndResolveInitialized(name) {
+ if (this._uninitializedNames.delete(name) &&
+ this._uninitializedNames.size === 0)
+ this._resolveInitialized();
+ }
+
+ /**
+ * @async
+ * @param {string} sender - the bus name that invoked the checked method
+ * @returns {bool}
+ */
+ async _isSenderAllowed(sender) {
+ await this._initializedPromise;
+ return [...this._allowlistMap.values()].includes(sender);
+ }
+
+ /**
+ * Check whether the bus name that invoked @invocation maps
+ * to an entry in the allow list.
+ *
+ * @async
+ * @throws
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async checkInvocation(invocation) {
+ if (global.context.unsafe_mode)
+ return;
+
+ if (await this._isSenderAllowed(invocation.get_sender()))
+ return;
+
+ throw new GLib.Error(Gio.DBusError,
+ Gio.DBusError.ACCESS_DENIED,
+ `${invocation.get_method_name()} is not allowed`);
+ }
+
+ /**
+ * @returns {void}
+ */
+ destroy() {
+ for (const id in this._watchList)
+ Gio.DBus.unwatch_name(id);
+ this._watchList = [];
+ }
+};
+
+/* @class Highlighter Highlight given terms in text using markup. */
+var Highlighter = class {
+ /**
+ * @param {?string[]} terms - list of terms to highlight
+ */
+ constructor(terms) {
+ if (!terms)
+ return;
+
+ const escapedTerms = terms
+ .map(term => Shell.util_regex_escape(term))
+ .filter(term => term.length > 0);
+
+ if (escapedTerms.length === 0)
+ return;
+
+ this._highlightRegex = new RegExp(
+ `(${escapedTerms.join('|')})`, 'gi');
+ }
+
+ /**
+ * Highlight all occurences of the terms defined for this
+ * highlighter in the provided text using markup.
+ *
+ * @param {string} text - text to highlight the defined terms in
+ * @returns {string}
+ */
+ highlight(text) {
+ if (!this._highlightRegex)
+ return GLib.markup_escape_text(text, -1);
+
+ let escaped = [];
+ let lastMatchEnd = 0;
+ let match;
+ while ((match = this._highlightRegex.exec(text))) {
+ if (match.index > lastMatchEnd) {
+ let unmatched = GLib.markup_escape_text(
+ text.slice(lastMatchEnd, match.index), -1);
+ escaped.push(unmatched);
+ }
+ let matched = GLib.markup_escape_text(match[0], -1);
+ escaped.push(`<b>${matched}</b>`);
+ lastMatchEnd = match.index + match[0].length;
+ }
+ let unmatched = GLib.markup_escape_text(
+ text.slice(lastMatchEnd), -1);
+ escaped.push(unmatched);
+
+ return escaped.join('');
+ }
+};
diff --git a/js/misc/weather.js b/js/misc/weather.js
new file mode 100644
index 0000000..2aa340a
--- /dev/null
+++ b/js/misc/weather.js
@@ -0,0 +1,326 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WeatherClient */
+
+const { Geoclue, Gio, GLib, GWeather, Shell } = imports.gi;
+const Signals = imports.misc.signals;
+
+const PermissionStore = imports.misc.permissionStore;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+Gio._promisify(Geoclue.Simple, 'new');
+
+const WeatherIntegrationIface = loadInterfaceXML('org.gnome.Shell.WeatherIntegration');
+
+const WEATHER_BUS_NAME = 'org.gnome.Weather';
+const WEATHER_OBJECT_PATH = '/org/gnome/Weather';
+const WEATHER_INTEGRATION_IFACE = 'org.gnome.Shell.WeatherIntegration';
+
+const WEATHER_APP_ID = 'org.gnome.Weather.desktop';
+
+// Minimum time between updates to show loading indication
+var UPDATE_THRESHOLD = 10 * GLib.TIME_SPAN_MINUTE;
+
+var WeatherClient = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._loading = false;
+ this._locationValid = false;
+ this._lastUpdate = GLib.DateTime.new_from_unix_local(0);
+
+ this._autoLocationRequested = false;
+ this._mostRecentLocation = null;
+
+ this._gclueService = null;
+ this._gclueStarted = false;
+ this._gclueStarting = false;
+ this._gclueLocationChangedId = 0;
+
+ this._needsAuth = true;
+ this._weatherAuthorized = false;
+ this._permStore = new PermissionStore.PermissionStore(async (proxy, error) => {
+ if (error) {
+ log(`Failed to connect to permissionStore: ${error.message}`);
+ return;
+ }
+
+ if (this._permStore.g_name_owner == null) {
+ // Failed to auto-start, likely because xdg-desktop-portal
+ // isn't installed; don't restrict access to location service
+ this._weatherAuthorized = true;
+ this._updateAutoLocation();
+ return;
+ }
+
+ let [perms, data] = [{}, null];
+ try {
+ [perms, data] = await this._permStore.LookupAsync('gnome', 'geolocation');
+ } catch (err) {
+ log(`Error looking up permission: ${err.message}`);
+ }
+
+ const params = ['gnome', 'geolocation', false, data, perms];
+ this._onPermStoreChanged(this._permStore, '', params);
+ });
+ this._permStore.connectSignal('Changed',
+ this._onPermStoreChanged.bind(this));
+
+ this._locationSettings = new Gio.Settings({ schema_id: 'org.gnome.system.location' });
+ this._locationSettings.connect('changed::enabled',
+ this._updateAutoLocation.bind(this));
+
+ this._world = GWeather.Location.get_world();
+
+ const providers =
+ GWeather.Provider.METAR |
+ GWeather.Provider.MET_NO |
+ GWeather.Provider.OWM;
+ this._weatherInfo = new GWeather.Info({
+ application_id: 'org.gnome.Shell',
+ contact_info: 'https://gitlab.gnome.org/GNOME/gnome-shell/-/raw/HEAD/gnome-shell.doap',
+ enabled_providers: providers,
+ });
+ this._weatherInfo.connect_after('updated', () => {
+ this._lastUpdate = GLib.DateTime.new_now_local();
+ this.emit('changed');
+ });
+
+ this._weatherApp = null;
+ this._weatherProxy = null;
+
+ this._createWeatherProxy();
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.shell.weather',
+ });
+ this._settings.connect('changed::automatic-location',
+ this._onAutomaticLocationChanged.bind(this));
+ this._onAutomaticLocationChanged();
+ this._settings.connect('changed::locations',
+ this._onLocationsChanged.bind(this));
+ this._onLocationsChanged();
+
+ this._appSystem = Shell.AppSystem.get_default();
+ this._appSystem.connect('installed-changed',
+ this._onInstalledChanged.bind(this));
+ this._onInstalledChanged();
+ }
+
+ get available() {
+ return this._weatherApp != null;
+ }
+
+ get loading() {
+ return this._loading;
+ }
+
+ get hasLocation() {
+ return this._locationValid;
+ }
+
+ get info() {
+ return this._weatherInfo;
+ }
+
+ activateApp() {
+ if (this._weatherApp)
+ this._weatherApp.activate();
+ }
+
+ update() {
+ if (!this._locationValid)
+ return;
+
+ let now = GLib.DateTime.new_now_local();
+ // Update without loading indication if the current info is recent enough
+ if (this._weatherInfo.is_valid() &&
+ now.difference(this._lastUpdate) < UPDATE_THRESHOLD)
+ this._weatherInfo.update();
+ else
+ this._loadInfo();
+ }
+
+ get _useAutoLocation() {
+ return this._autoLocationRequested &&
+ this._locationSettings.get_boolean('enabled') &&
+ (!this._needsAuth || this._weatherAuthorized);
+ }
+
+ async _createWeatherProxy() {
+ const nodeInfo = Gio.DBusNodeInfo.new_for_xml(WeatherIntegrationIface);
+ try {
+ this._weatherProxy = await Gio.DBusProxy.new(
+ Gio.DBus.session,
+ Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES,
+ nodeInfo.lookup_interface(WEATHER_INTEGRATION_IFACE),
+ WEATHER_BUS_NAME,
+ WEATHER_OBJECT_PATH,
+ WEATHER_INTEGRATION_IFACE,
+ null);
+ } catch (e) {
+ log(`Failed to create GNOME Weather proxy: ${e}`);
+ return;
+ }
+
+ this._weatherProxy.connect('g-properties-changed',
+ this._onWeatherPropertiesChanged.bind(this));
+ this._onWeatherPropertiesChanged();
+ }
+
+ _onWeatherPropertiesChanged() {
+ if (this._weatherProxy.g_name_owner == null)
+ return;
+
+ this._settings.set_boolean('automatic-location',
+ this._weatherProxy.AutomaticLocation);
+ this._settings.set_value('locations',
+ new GLib.Variant('av', this._weatherProxy.Locations));
+ }
+
+ _onInstalledChanged() {
+ let hadApp = this._weatherApp != null;
+ this._weatherApp = this._appSystem.lookup_app(WEATHER_APP_ID);
+ let haveApp = this._weatherApp != null;
+
+ if (hadApp !== haveApp)
+ this.emit('changed');
+
+ let neededAuth = this._needsAuth;
+ this._needsAuth = this._weatherApp === null ||
+ this._weatherApp.app_info.has_key('X-Flatpak');
+
+ if (neededAuth !== this._needsAuth)
+ this._updateAutoLocation();
+ }
+
+ _loadInfo() {
+ let id = this._weatherInfo.connect('updated', () => {
+ this._weatherInfo.disconnect(id);
+ this._loading = false;
+ });
+
+ this._loading = true;
+ this.emit('changed');
+
+ this._weatherInfo.update();
+ }
+
+ _locationsEqual(loc1, loc2) {
+ if (loc1 == loc2)
+ return true;
+
+ if (loc1 == null || loc2 == null)
+ return false;
+
+ return loc1.equal(loc2);
+ }
+
+ _setLocation(location) {
+ if (this._locationsEqual(this._weatherInfo.location, location))
+ return;
+
+ this._weatherInfo.abort();
+ this._weatherInfo.set_location(location);
+ this._locationValid = location != null;
+
+ if (location)
+ this._loadInfo();
+ else
+ this.emit('changed');
+ }
+
+ _updateLocationMonitoring() {
+ if (this._useAutoLocation) {
+ if (this._gclueLocationChangedId != 0 || this._gclueService == null)
+ return;
+
+ this._gclueLocationChangedId =
+ this._gclueService.connect('notify::location',
+ this._onGClueLocationChanged.bind(this));
+ this._onGClueLocationChanged();
+ } else {
+ if (this._gclueLocationChangedId)
+ this._gclueService.disconnect(this._gclueLocationChangedId);
+ this._gclueLocationChangedId = 0;
+ }
+ }
+
+ async _startGClueService() {
+ if (this._gclueStarting)
+ return;
+
+ this._gclueStarting = true;
+
+ try {
+ this._gclueService = await Geoclue.Simple.new(
+ 'org.gnome.Shell', Geoclue.AccuracyLevel.CITY, null);
+ } catch (e) {
+ log(`Failed to connect to Geoclue2 service: ${e.message}`);
+ this._setLocation(this._mostRecentLocation);
+ return;
+ }
+ this._gclueStarted = true;
+ this._gclueService.get_client().distance_threshold = 100;
+ this._updateLocationMonitoring();
+ }
+
+ _onGClueLocationChanged() {
+ let geoLocation = this._gclueService.location;
+ // Provide empty name so GWeather sets location name
+ const location = GWeather.Location.new_detached('',
+ null,
+ geoLocation.latitude,
+ geoLocation.longitude);
+ this._setLocation(location);
+ }
+
+ _onAutomaticLocationChanged() {
+ let useAutoLocation = this._settings.get_boolean('automatic-location');
+ if (this._autoLocationRequested == useAutoLocation)
+ return;
+
+ this._autoLocationRequested = useAutoLocation;
+
+ this._updateAutoLocation();
+ }
+
+ _updateAutoLocation() {
+ this._updateLocationMonitoring();
+
+ if (this._useAutoLocation)
+ this._startGClueService();
+ else
+ this._setLocation(this._mostRecentLocation);
+ }
+
+ _onLocationsChanged() {
+ let locations = this._settings.get_value('locations').deepUnpack();
+ let serialized = locations.shift();
+ let mostRecentLocation = null;
+
+ if (serialized)
+ mostRecentLocation = this._world.deserialize(serialized);
+
+ if (this._locationsEqual(this._mostRecentLocation, mostRecentLocation))
+ return;
+
+ this._mostRecentLocation = mostRecentLocation;
+
+ if (!this._useAutoLocation || !this._gclueStarted)
+ this._setLocation(this._mostRecentLocation);
+ }
+
+ _onPermStoreChanged(proxy, sender, params) {
+ let [table, id, deleted_, data_, perms] = params;
+
+ if (table != 'gnome' || id != 'geolocation')
+ return;
+
+ let permission = perms['org.gnome.Weather'] || ['NONE'];
+ let [accuracy] = permission;
+ this._weatherAuthorized = accuracy != 'NONE';
+
+ this._updateAutoLocation();
+ }
+};
diff --git a/js/perf/basic.js b/js/perf/basic.js
new file mode 100644
index 0000000..6d0ef53
--- /dev/null
+++ b/js/perf/basic.js
@@ -0,0 +1,146 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported run, finish, script_topBarNavDone, script_notificationShowDone,
+ script_notificationCloseDone, script_overviewShowDone,
+ script_applicationsShowStart, script_applicationsShowDone, METRICS,
+*/
+/* eslint camelcase: ["error", { properties: "never", allow: ["^script_"] }] */
+
+const { St } = imports.gi;
+
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const Scripting = imports.ui.scripting;
+
+// This script tests the most important (basic) functionality of the shell.
+
+var METRICS = {};
+
+async function run() {
+ /* eslint-disable no-await-in-loop */
+ Scripting.defineScriptEvent('topBarNavStart', 'Starting to navigate the top bar');
+ Scripting.defineScriptEvent('topBarNavDone', 'Done navigating the top bar');
+ Scripting.defineScriptEvent('notificationShowStart', 'Showing a notification');
+ Scripting.defineScriptEvent('notificationShowDone', 'Done showing a notification');
+ Scripting.defineScriptEvent('notificationCloseStart', 'Closing a notification');
+ Scripting.defineScriptEvent('notificationCloseDone', 'Done closing a notification');
+ Scripting.defineScriptEvent('overviewShowStart', 'Starting to show the overview');
+ Scripting.defineScriptEvent('overviewShowDone', 'Overview finished showing');
+ Scripting.defineScriptEvent('applicationsShowStart', 'Starting to switch to applications view');
+ Scripting.defineScriptEvent('applicationsShowDone', 'Done switching to applications view');
+
+ Main.overview.connect('shown',
+ () => Scripting.scriptEvent('overviewShowDone'));
+
+ await Scripting.sleep(1000);
+
+ // navigate through top bar
+ Scripting.scriptEvent('topBarNavStart');
+ Main.panel.statusArea.quickSettings.menu.open();
+ await Scripting.sleep(400);
+
+ const { menuManager } = Main.panel;
+ while (menuManager.activeMenu &&
+ Main.panel.navigate_focus(menuManager.activeMenu.sourceActor,
+ St.DirectionType.TAB_BACKWARD, false))
+ await Scripting.sleep(400);
+ Scripting.scriptEvent('topBarNavDone');
+
+ await Scripting.sleep(1000);
+
+ // notification
+ const source = new MessageTray.SystemNotificationSource();
+ Main.messageTray.add(source);
+
+ Scripting.scriptEvent('notificationShowStart');
+ source.connect('notification-show',
+ () => Scripting.scriptEvent('notificationShowDone'));
+
+ const notification = new MessageTray.Notification(source,
+ 'A test notification');
+ source.showNotification(notification);
+ await Scripting.sleep(400);
+
+ Main.panel.statusArea.dateMenu.menu.open();
+ await Scripting.sleep(400);
+
+ Scripting.scriptEvent('notificationCloseStart');
+ notification.connect('destroy',
+ () => Scripting.scriptEvent('notificationCloseDone'));
+
+ notification.destroy();
+ await Scripting.sleep(400);
+
+ Main.panel.statusArea.dateMenu.menu.close();
+ await Scripting.waitLeisure();
+
+ await Scripting.sleep(1000);
+
+ // overview (window picker)
+ Scripting.scriptEvent('overviewShowStart');
+ Main.overview.show();
+ await Scripting.waitLeisure();
+ Main.overview.hide();
+ await Scripting.waitLeisure();
+
+ await Scripting.sleep(1000);
+
+ // overview (app picker)
+ Main.overview.show();
+ await Scripting.waitLeisure();
+
+ Scripting.scriptEvent('applicationsShowStart');
+ // eslint-disable-next-line require-atomic-updates
+ Main.overview.dash.showAppsButton.checked = true;
+ await Scripting.waitLeisure();
+ Scripting.scriptEvent('applicationsShowDone');
+ // eslint-disable-next-line require-atomic-updates
+ Main.overview.dash.showAppsButton.checked = false;
+ await Scripting.waitLeisure();
+
+ Main.overview.hide();
+ await Scripting.waitLeisure();
+ /* eslint-enable no-await-in-loop */
+}
+
+let topBarNav = false;
+let notificationShown = false;
+let notificationClosed = false;
+let windowPickerShown = false;
+let appPickerShown = false;
+
+function script_topBarNavDone() {
+ topBarNav = true;
+}
+
+function script_notificationShowDone() {
+ notificationShown = true;
+}
+
+function script_notificationCloseDone() {
+ notificationClosed = true;
+}
+
+function script_overviewShowDone() {
+ windowPickerShown = true;
+}
+
+function script_applicationsShowDone() {
+ appPickerShown = true;
+}
+
+function finish() {
+ if (!topBarNav)
+ throw new Error('Failed to navigate top bar');
+
+ if (!notificationShown)
+ throw new Error('Failed to show notification');
+
+ if (!notificationClosed)
+ throw new Error('Failed to close notification');
+
+ if (!windowPickerShown)
+ throw new Error('Failed to show window picker');
+
+ if (!appPickerShown)
+ throw new Error('Failed to show app picker');
+}
diff --git a/js/perf/core.js b/js/perf/core.js
new file mode 100644
index 0000000..bd7e220
--- /dev/null
+++ b/js/perf/core.js
@@ -0,0 +1,271 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported run, script_overviewShowStart, script_overviewShowDone,
+ script_applicationsShowStart, script_applicationsShowDone,
+ script_afterShowHide, malloc_usedSize, glx_swapComplete,
+ clutter_stagePaintDone */
+/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^malloc", "^glx", "^clutter"] }] */
+
+const System = imports.system;
+
+const Main = imports.ui.main;
+const Scripting = imports.ui.scripting;
+
+// This performance script measure the most important (core) performance
+// metrics for the shell. By looking at the output metrics of this script
+// someone should be able to get an idea of how well the shell is performing
+// on a particular system.
+
+var METRICS = {
+ overviewLatencyFirst: {
+ description: 'Time to first frame after triggering overview, first time',
+ units: 'us',
+ },
+ overviewFpsFirst: {
+ description: 'Frame rate when going to the overview, first time',
+ units: 'frames / s',
+ },
+ overviewLatencySubsequent: {
+ description: 'Time to first frame after triggering overview, second time',
+ units: 'us',
+ },
+ overviewFpsSubsequent: {
+ description: 'Frames rate when going to the overview, second time',
+ units: 'frames / s',
+ },
+ overviewFps5Windows: {
+ description: 'Frames rate when going to the overview, 5 windows open',
+ units: 'frames / s',
+ },
+ overviewFps10Windows: {
+ description: 'Frames rate when going to the overview, 10 windows open',
+ units: 'frames / s',
+ },
+ overviewFps5Maximized: {
+ description: 'Frames rate when going to the overview, 5 maximized windows open',
+ units: 'frames / s',
+ },
+ overviewFps10Maximized: {
+ description: 'Frames rate when going to the overview, 10 maximized windows open',
+ units: 'frames / s',
+ },
+ overviewFps5Alpha: {
+ description: 'Frames rate when going to the overview, 5 alpha-transparent windows open',
+ units: 'frames / s',
+ },
+ overviewFps10Alpha: {
+ description: 'Frames rate when going to the overview, 10 alpha-transparent windows open',
+ units: 'frames / s',
+ },
+ usedAfterOverview: {
+ description: "Malloc'ed bytes after the overview is shown once",
+ units: 'B',
+ },
+ leakedAfterOverview: {
+ description: "Additional malloc'ed bytes the second time the overview is shown",
+ units: 'B',
+ },
+ applicationsShowTimeFirst: {
+ description: 'Time to switch to applications view, first time',
+ units: 'us',
+ },
+ applicationsShowTimeSubsequent: {
+ description: 'Time to switch to applications view, second time',
+ units: 'us',
+ },
+};
+
+const WINDOW_CONFIGS = [{
+ width: 640, height: 480,
+ alpha: false, maximized: false, count: 1, metric: 'overviewFpsSubsequent',
+}, {
+ width: 640, height: 480,
+ alpha: false, maximized: false, count: 5, metric: 'overviewFps5Windows',
+}, {
+ width: 640, height: 480,
+ alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows',
+}, {
+ width: 640, height: 480,
+ alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized',
+}, {
+ width: 640, height: 480,
+ alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized',
+}, {
+ width: 640, height: 480,
+ alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha',
+}, {
+ width: 640, height: 480,
+ alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha',
+}];
+
+async function run() {
+ /* eslint-disable no-await-in-loop */
+ Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
+ Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
+ Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview");
+ Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view");
+ Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view");
+
+ // Enable recording of timestamps for different points in the frame cycle
+ global.frame_timestamps = true;
+
+ Main.overview.connect('shown', () => {
+ Scripting.scriptEvent('overviewShowDone');
+ });
+
+ await Scripting.sleep(1000);
+
+ for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) {
+ // We go to the overview twice for each configuration; the first time
+ // to calculate the mipmaps for the windows, the second time to get
+ // a clean set of numbers.
+ if ((i % 2) == 0) {
+ let config = WINDOW_CONFIGS[i / 2];
+ await Scripting.destroyTestWindows();
+
+ for (let k = 0; k < config.count; k++) {
+ await Scripting.createTestWindow({
+ width: config.width,
+ height: config.height,
+ alpha: config.alpha,
+ maximized: config.maximized,
+ });
+ }
+
+ await Scripting.waitTestWindows();
+ await Scripting.sleep(1000);
+ await Scripting.waitLeisure();
+ }
+
+ Scripting.scriptEvent('overviewShowStart');
+ Main.overview.show();
+
+ await Scripting.waitLeisure();
+ Main.overview.hide();
+ await Scripting.waitLeisure();
+
+ System.gc();
+ await Scripting.sleep(1000);
+ Scripting.collectStatistics();
+ Scripting.scriptEvent('afterShowHide');
+ }
+
+ await Scripting.destroyTestWindows();
+ await Scripting.sleep(1000);
+
+ Main.overview.show();
+ await Scripting.waitLeisure();
+
+ for (let i = 0; i < 2; i++) {
+ Scripting.scriptEvent('applicationsShowStart');
+ // eslint-disable-next-line require-atomic-updates
+ Main.overview.dash.showAppsButton.checked = true;
+ await Scripting.waitLeisure();
+ Scripting.scriptEvent('applicationsShowDone');
+ // eslint-disable-next-line require-atomic-updates
+ Main.overview.dash.showAppsButton.checked = false;
+ await Scripting.waitLeisure();
+ }
+ /* eslint-enable no-await-in-loop */
+}
+
+let showingOverview = false;
+let finishedShowingOverview = false;
+let overviewShowStart;
+let overviewFrames;
+let overviewLatency;
+let mallocUsedSize = 0;
+let overviewShowCount = 0;
+let haveSwapComplete = false;
+let applicationsShowStart;
+let applicationsShowCount = 0;
+
+function script_overviewShowStart(time) {
+ showingOverview = true;
+ finishedShowingOverview = false;
+ overviewShowStart = time;
+ overviewFrames = 0;
+}
+
+function script_overviewShowDone(_time) {
+ // We've set up the state at the end of the zoom out, but we
+ // need to wait for one more frame to paint before we count
+ // ourselves as done.
+ finishedShowingOverview = true;
+}
+
+function script_applicationsShowStart(time) {
+ applicationsShowStart = time;
+}
+
+function script_applicationsShowDone(time) {
+ applicationsShowCount++;
+ if (applicationsShowCount == 1)
+ METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart;
+ else
+ METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart;
+}
+
+function script_afterShowHide(_time) {
+ if (overviewShowCount == 1)
+ METRICS.usedAfterOverview.value = mallocUsedSize;
+ else
+ METRICS.leakedAfterOverview.value = mallocUsedSize - METRICS.usedAfterOverview.value;
+}
+
+function malloc_usedSize(time, bytes) {
+ mallocUsedSize = bytes;
+}
+
+function _frameDone(time) {
+ if (showingOverview) {
+ if (overviewFrames == 0)
+ overviewLatency = time - overviewShowStart;
+
+ overviewFrames++;
+ }
+
+ if (finishedShowingOverview) {
+ showingOverview = false;
+ finishedShowingOverview = false;
+ overviewShowCount++;
+
+ let dt = (time - (overviewShowStart + overviewLatency)) / 1000000;
+
+ // If we see a start frame and an end frame, that would
+ // be 1 frame for a FPS computation, hence the '- 1'
+ let fps = (overviewFrames - 1) / dt;
+
+ if (overviewShowCount == 1) {
+ METRICS.overviewLatencyFirst.value = overviewLatency;
+ METRICS.overviewFpsFirst.value = fps;
+ } else if (overviewShowCount == 2) {
+ METRICS.overviewLatencySubsequent.value = overviewLatency;
+ }
+
+ // Other than overviewFpsFirst, we collect FPS metrics the second
+ // we show each window configuration. overviewShowCount is 1,2,3...
+ if (overviewShowCount % 2 == 0) {
+ let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1];
+ METRICS[config.metric].value = fps;
+ }
+ }
+}
+
+function glx_swapComplete(time, swapTime) {
+ haveSwapComplete = true;
+
+ _frameDone(swapTime);
+}
+
+function clutter_stagePaintDone(time) {
+ // If we aren't receiving GLXBufferSwapComplete events, then we approximate
+ // the time the user sees a frame with the time we finished doing drawing
+ // commands for the frame. This doesn't take into account the time for
+ // the GPU to finish painting, and the time for waiting for the buffer
+ // swap, but if this are uniform - every frame takes the same time to draw -
+ // then it won't upset our FPS calculation, though the latency value
+ // will be slightly too low.
+
+ if (!haveSwapComplete)
+ _frameDone(time);
+}
diff --git a/js/perf/hwtest.js b/js/perf/hwtest.js
new file mode 100644
index 0000000..63777bd
--- /dev/null
+++ b/js/perf/hwtest.js
@@ -0,0 +1,319 @@
+/* exported run, script_desktopShown, script_overviewShowStart,
+ script_overviewShowDone, script_applicationsShowStart,
+ script_applicationsShowDone, script_mainViewDrawStart,
+ script_mainViewDrawDone, script_overviewDrawStart,
+ script_overviewDrawDone, script_redrawTestStart,
+ script_redrawTestDone, script_collectTimings,
+ script_geditLaunch, script_geditFirstFrame,
+ clutter_stagePaintStart, clutter_paintCompletedTimestamp */
+/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^clutter"] }] */
+const { Clutter, Gio, Shell } = imports.gi;
+const Main = imports.ui.main;
+const Scripting = imports.ui.scripting;
+
+var METRICS = {
+ timeToDesktop: {
+ description: 'Time from starting graphical.target to desktop showing',
+ units: 'us',
+ },
+
+ overviewShowTime: {
+ description: 'Time to switch to overview view, first time',
+ units: 'us',
+ },
+
+ applicationsShowTime: {
+ description: 'Time to switch to applications view, first time',
+ units: 'us',
+ },
+
+ mainViewRedrawTime: {
+ description: 'Time to redraw the main view, full screen',
+ units: 'us',
+ },
+
+ overviewRedrawTime: {
+ description: 'Time to redraw the overview, full screen, 5 windows',
+ units: 'us',
+ },
+
+ applicationRedrawTime: {
+ description: 'Time to redraw frame with a maximized application update',
+ units: 'us',
+ },
+
+ geditStartTime: {
+ description: 'Time from gedit launch to window drawn',
+ units: 'us',
+ },
+};
+
+function waitAndDraw(milliseconds) {
+ let cb;
+
+ let timeline = new Clutter.Timeline({ duration: milliseconds });
+ timeline.start();
+
+ timeline.connect('new-frame', (_timeline, _frame) => {
+ global.stage.queue_redraw();
+ });
+
+ timeline.connect('completed', () => {
+ timeline.stop();
+ if (cb)
+ cb();
+ });
+
+ return callback => (cb = callback);
+}
+
+function waitSignal(object, signal) {
+ let cb;
+
+ let id = object.connect(signal, () => {
+ object.disconnect(id);
+ if (cb)
+ cb();
+ });
+
+ return callback => (cb = callback);
+}
+
+function extractBootTimestamp() {
+ const sp = Gio.Subprocess.new([
+ 'journalctl', '-b',
+ 'MESSAGE_ID=7d4958e842da4a758f6c1cdc7b36dcc5',
+ 'UNIT=graphical.target',
+ '-o',
+ 'json',
+ ], Gio.SubprocessFlags.STDOUT_PIPE);
+ let result = null;
+
+ let datastream = Gio.DataInputStream.new(sp.get_stdout_pipe());
+ while (true) { // eslint-disable-line no-constant-condition
+ let [line, length_] = datastream.read_line_utf8(null);
+ if (line === null)
+ break;
+
+ let fields = JSON.parse(line);
+ result = Number(fields['__MONOTONIC_TIMESTAMP']);
+ }
+ datastream.close(null);
+ return result;
+}
+
+async function run() {
+ /* eslint-disable no-await-in-loop */
+ Scripting.defineScriptEvent("desktopShown", "Finished initial animation");
+ Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
+ Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
+ Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view");
+ Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view");
+ Scripting.defineScriptEvent("mainViewDrawStart", "Drawing main view");
+ Scripting.defineScriptEvent("mainViewDrawDone", "Ending timing main view drawing");
+ Scripting.defineScriptEvent("overviewDrawStart", "Drawing overview");
+ Scripting.defineScriptEvent("overviewDrawDone", "Ending timing overview drawing");
+ Scripting.defineScriptEvent("redrawTestStart", "Drawing application window");
+ Scripting.defineScriptEvent("redrawTestDone", "Ending timing application window drawing");
+ Scripting.defineScriptEvent("collectTimings", "Accumulate frame timings from redraw tests");
+ Scripting.defineScriptEvent("geditLaunch", "gedit application launch");
+ Scripting.defineScriptEvent("geditFirstFrame", "first frame of gedit window drawn");
+
+ await Scripting.waitLeisure();
+ Scripting.scriptEvent('desktopShown');
+
+ let interfaceSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.interface',
+ });
+ interfaceSettings.set_boolean('enable-animations', false);
+
+ Scripting.scriptEvent('overviewShowStart');
+ Main.overview.show();
+ await Scripting.waitLeisure();
+ Scripting.scriptEvent('overviewShowDone');
+
+ await Scripting.sleep(1000);
+
+ Scripting.scriptEvent('applicationsShowStart');
+ // eslint-disable-next-line require-atomic-updates
+ Main.overview.dash.showAppsButton.checked = true;
+
+ await Scripting.waitLeisure();
+ Scripting.scriptEvent('applicationsShowDone');
+
+ await Scripting.sleep(1000);
+
+ Main.overview.hide();
+ await Scripting.waitLeisure();
+
+ // --------------------- //
+ // Tests of redraw speed //
+ // --------------------- //
+
+ global.frame_timestamps = true;
+ global.frame_finish_timestamp = true;
+
+ for (let k = 0; k < 5; k++)
+ await Scripting.createTestWindow({ maximized: true });
+ await Scripting.waitTestWindows();
+
+ await Scripting.sleep(1000);
+
+ Scripting.scriptEvent('mainViewDrawStart');
+ await waitAndDraw(1000);
+ Scripting.scriptEvent('mainViewDrawDone');
+
+ Main.overview.show();
+ Scripting.waitLeisure();
+
+ await Scripting.sleep(1500);
+
+ Scripting.scriptEvent('overviewDrawStart');
+ await waitAndDraw(1000);
+ Scripting.scriptEvent('overviewDrawDone');
+
+ await Scripting.destroyTestWindows();
+ Main.overview.hide();
+
+ await Scripting.createTestWindow({
+ maximized: true,
+ redraws: true,
+ });
+ await Scripting.waitTestWindows();
+
+ await Scripting.sleep(1000);
+
+ Scripting.scriptEvent('redrawTestStart');
+ await Scripting.sleep(1000);
+ Scripting.scriptEvent('redrawTestDone');
+
+ await Scripting.sleep(1000);
+ Scripting.scriptEvent('collectTimings');
+
+ await Scripting.destroyTestWindows();
+
+ global.frame_timestamps = false;
+ global.frame_finish_timestamp = false;
+
+ await Scripting.sleep(1000);
+
+ let appSys = Shell.AppSystem.get_default();
+ let app = appSys.lookup_app('org.gnome.gedit.desktop');
+
+ Scripting.scriptEvent('geditLaunch');
+ app.activate();
+
+ let windows = app.get_windows();
+ if (windows.length > 0)
+ throw new Error('gedit was already running');
+
+ while (windows.length == 0) {
+ await waitSignal(global.display, 'window-created');
+ windows = app.get_windows();
+ }
+
+ let actor = windows[0].get_compositor_private();
+ await waitSignal(actor, 'first-frame');
+ Scripting.scriptEvent('geditFirstFrame');
+
+ await Scripting.sleep(1000);
+
+ windows[0].delete(global.get_current_time());
+
+ await Scripting.sleep(1000);
+
+ interfaceSettings.set_boolean('enable-animations', true);
+ /* eslint-enable no-await-in-loop */
+}
+
+let overviewShowStart;
+let applicationsShowStart;
+let stagePaintStart;
+let redrawTiming;
+let redrawTimes = {};
+let geditLaunchTime;
+
+function script_desktopShown(time) {
+ let bootTimestamp = extractBootTimestamp();
+ METRICS.timeToDesktop.value = time - bootTimestamp;
+}
+
+function script_overviewShowStart(time) {
+ overviewShowStart = time;
+}
+
+function script_overviewShowDone(time) {
+ METRICS.overviewShowTime.value = time - overviewShowStart;
+}
+
+function script_applicationsShowStart(time) {
+ applicationsShowStart = time;
+}
+
+function script_applicationsShowDone(time) {
+ METRICS.applicationsShowTime.value = time - applicationsShowStart;
+}
+
+function script_mainViewDrawStart(_time) {
+ redrawTiming = 'mainView';
+}
+
+function script_mainViewDrawDone(_time) {
+ redrawTiming = null;
+}
+
+function script_overviewDrawStart(_time) {
+ redrawTiming = 'overview';
+}
+
+function script_overviewDrawDone(_time) {
+ redrawTiming = null;
+}
+
+function script_redrawTestStart(_time) {
+ redrawTiming = 'application';
+}
+
+function script_redrawTestDone(_time) {
+ redrawTiming = null;
+}
+
+function script_collectTimings(_time) {
+ for (let timing in redrawTimes) {
+ let times = redrawTimes[timing];
+ times.sort((a, b) => a - b);
+
+ let len = times.length;
+ let median;
+
+ if (len == 0)
+ median = -1;
+ else if (len % 2 == 1)
+ median = times[(len - 1) / 2];
+ else
+ median = Math.round((times[len / 2 - 1] + times[len / 2]) / 2);
+
+ METRICS[`${timing}RedrawTime`].value = median;
+ }
+}
+
+function script_geditLaunch(time) {
+ geditLaunchTime = time;
+}
+
+function script_geditFirstFrame(time) {
+ METRICS.geditStartTime.value = time - geditLaunchTime;
+}
+
+function clutter_stagePaintStart(time) {
+ stagePaintStart = time;
+}
+
+function clutter_paintCompletedTimestamp(time) {
+ if (redrawTiming != null && stagePaintStart != null) {
+ if (!(redrawTiming in redrawTimes))
+ redrawTimes[redrawTiming] = [];
+ redrawTimes[redrawTiming].push(time - stagePaintStart);
+ }
+ stagePaintStart = null;
+}
diff --git a/js/portal-resources.gresource.xml b/js/portal-resources.gresource.xml
new file mode 100644
index 0000000..ff36306
--- /dev/null
+++ b/js/portal-resources.gresource.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/shell">
+ <file>portalHelper/main.js</file>
+
+ <file>misc/config.js</file>
+ <file>misc/dbusUtils.js</file>
+ <file>misc/fileUtils.js</file>
+ </gresource>
+</gresources>
diff --git a/js/portalHelper/main.js b/js/portalHelper/main.js
new file mode 100644
index 0000000..cdf8b51
--- /dev/null
+++ b/js/portalHelper/main.js
@@ -0,0 +1,381 @@
+/* exported main */
+imports.gi.versions.Pango = '1.0';
+imports.gi.versions.Gtk = '3.0';
+imports.gi.versions.WebKit2 = '4.1';
+
+const Format = imports.format;
+const Gettext = imports.gettext;
+const { Gio, GLib, GObject, Gtk, Pango, WebKit2: WebKit } = imports.gi;
+
+const _ = Gettext.gettext;
+
+const Config = imports.misc.config;
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const PortalHelperResult = {
+ CANCELLED: 0,
+ COMPLETED: 1,
+ RECHECK: 2,
+};
+
+const PortalHelperSecurityLevel = {
+ NOT_YET_DETERMINED: 0,
+ SECURE: 1,
+ INSECURE: 2,
+};
+
+const HTTP_URI_FLAGS =
+ GLib.UriFlags.HAS_PASSWORD |
+ GLib.UriFlags.ENCODED_PATH |
+ GLib.UriFlags.ENCODED_QUERY |
+ GLib.UriFlags.ENCODED_FRAGMENT |
+ GLib.UriFlags.SCHEME_NORMALIZE |
+ GLib.UriFlags.PARSE_RELAXED;
+
+const CONNECTIVITY_CHECK_HOST = 'nmcheck.gnome.org';
+const CONNECTIVITY_CHECK_URI = `http://${CONNECTIVITY_CHECK_HOST}`;
+const CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT = 30 * GLib.USEC_PER_SEC;
+
+const HelperDBusInterface = loadInterfaceXML('org.gnome.Shell.PortalHelper');
+
+var PortalHeaderBar = GObject.registerClass(
+class PortalHeaderBar extends Gtk.HeaderBar {
+ _init() {
+ super._init({ show_close_button: true });
+
+ // See ephy-title-box.c in epiphany for the layout
+ const vbox = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ spacing: 0,
+ });
+ this.set_custom_title(vbox);
+
+ /* TRANSLATORS: this is the title of the wifi captive portal login window */
+ const titleLabel = new Gtk.Label({
+ label: _('Hotspot Login'),
+ wrap: false,
+ single_line_mode: true,
+ ellipsize: Pango.EllipsizeMode.END,
+ });
+ titleLabel.get_style_context().add_class('title');
+ vbox.add(titleLabel);
+
+ const hbox = new Gtk.Box({
+ orientation: Gtk.Orientation.HORIZONTAL,
+ spacing: 4,
+ halign: Gtk.Align.CENTER,
+ valign: Gtk.Align.BASELINE,
+ });
+ hbox.get_style_context().add_class('subtitle');
+ vbox.add(hbox);
+
+ this._lockImage = new Gtk.Image({
+ icon_size: Gtk.IconSize.MENU,
+ valign: Gtk.Align.BASELINE,
+ });
+ hbox.add(this._lockImage);
+
+ this.subtitleLabel = new Gtk.Label({
+ wrap: false,
+ single_line_mode: true,
+ ellipsize: Pango.EllipsizeMode.END,
+ valign: Gtk.Align.BASELINE,
+ selectable: true,
+ });
+ this.subtitleLabel.get_style_context().add_class('subtitle');
+ hbox.add(this.subtitleLabel);
+
+ vbox.show_all();
+ }
+
+ setSubtitle(label) {
+ this.subtitleLabel.set_text(label);
+ }
+
+ setSecurityIcon(securityLevel) {
+ switch (securityLevel) {
+ case PortalHelperSecurityLevel.NOT_YET_DETERMINED:
+ this._lockImage.hide();
+ break;
+ case PortalHelperSecurityLevel.SECURE:
+ this._lockImage.show();
+ this._lockImage.set_from_icon_name("channel-secure-symbolic", Gtk.IconSize.MENU);
+ this._lockImage.set_tooltip_text(null);
+ break;
+ case PortalHelperSecurityLevel.INSECURE:
+ this._lockImage.show();
+ this._lockImage.set_from_icon_name("channel-insecure-symbolic", Gtk.IconSize.MENU);
+ this._lockImage.set_tooltip_text(_('Your connection to this hotspot login is not secure. Passwords or other information you enter on this page can be viewed by people nearby.'));
+ break;
+ }
+ }
+});
+
+var PortalWindow = GObject.registerClass(
+class PortalWindow extends Gtk.ApplicationWindow {
+ _init(application, url, timestamp, doneCallback) {
+ super._init({ application });
+
+ this.connect('delete-event', this.destroyWindow.bind(this));
+ this._headerBar = new PortalHeaderBar();
+ this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.NOT_YET_DETERMINED);
+ this.set_titlebar(this._headerBar);
+ this._headerBar.show();
+
+ if (!url) {
+ url = CONNECTIVITY_CHECK_URI;
+ this._originalUrlWasGnome = true;
+ } else {
+ this._originalUrlWasGnome = false;
+ }
+ this._uri = GLib.Uri.parse(url, HTTP_URI_FLAGS);
+ this._everSeenRedirect = false;
+ this._originalUrl = url;
+ this._doneCallback = doneCallback;
+ this._lastRecheck = 0;
+ this._recheckAtExit = false;
+
+ this._webContext = WebKit.WebContext.new_ephemeral();
+ this._webContext.set_cache_model(WebKit.CacheModel.DOCUMENT_VIEWER);
+ this._webContext.set_network_proxy_settings(WebKit.NetworkProxyMode.NO_PROXY, null);
+ if (this._webContext.set_sandbox_enabled) {
+ // We have WebKitGTK 2.26 or newer.
+ this._webContext.set_sandbox_enabled(true);
+ }
+
+ this._webView = WebKit.WebView.new_with_context(this._webContext);
+ this._webView.connect('decide-policy', this._onDecidePolicy.bind(this));
+ this._webView.connect('load-changed', this._onLoadChanged.bind(this));
+ this._webView.connect('insecure-content-detected', this._onInsecureContentDetected.bind(this));
+ this._webView.connect('load-failed-with-tls-errors', this._onLoadFailedWithTlsErrors.bind(this));
+ this._webView.load_uri(url);
+ this._webView.connect('notify::uri', this._syncUri.bind(this));
+ this._syncUri();
+
+ this.add(this._webView);
+ this._webView.show();
+ this.set_size_request(600, 450);
+ this.maximize();
+ this.present_with_time(timestamp);
+
+ this.application.set_accels_for_action('app.quit', ['<Primary>q', '<Primary>w']);
+ }
+
+ destroyWindow() {
+ this.destroy();
+ }
+
+ _syncUri() {
+ let uri = this._webView.uri;
+ if (uri)
+ this._headerBar.setSubtitle(GLib.uri_unescape_string(uri, null));
+ else
+ this._headerBar.setSubtitle('');
+ }
+
+ refresh() {
+ this._everSeenRedirect = false;
+ this._webView.load_uri(this._originalUrl);
+ }
+
+ vfunc_delete_event(_event) {
+ if (this._recheckAtExit)
+ this._doneCallback(PortalHelperResult.RECHECK);
+ else
+ this._doneCallback(PortalHelperResult.CANCELLED);
+ return false;
+ }
+
+ _onLoadChanged(view, loadEvent) {
+ if (loadEvent == WebKit.LoadEvent.STARTED) {
+ this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.NOT_YET_DETERMINED);
+ } else if (loadEvent == WebKit.LoadEvent.COMMITTED) {
+ let tlsInfo = this._webView.get_tls_info();
+ let ret = tlsInfo[0];
+ let flags = tlsInfo[2];
+ if (ret && flags == 0)
+ this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.SECURE);
+ else
+ this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE);
+ }
+ }
+
+ _onInsecureContentDetected() {
+ this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE);
+ }
+
+ _onLoadFailedWithTlsErrors(view, failingURI, certificate, _errors) {
+ this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE);
+ let uri = GLib.Uri.parse(failingURI, HTTP_URI_FLAGS);
+ this._webContext.allow_tls_certificate_for_host(certificate, uri.get_host());
+ this._webView.load_uri(failingURI);
+ return true;
+ }
+
+ _onDecidePolicy(view, decision, type) {
+ if (type == WebKit.PolicyDecisionType.NEW_WINDOW_ACTION) {
+ let navigationAction = decision.get_navigation_action();
+ if (navigationAction.is_user_gesture()) {
+ // Even though the portal asks for a new window,
+ // perform the navigation in the current one. Some
+ // portals open a window as their last login step and
+ // ignoring that window causes them to not let the
+ // user go through. We don't risk popups taking over
+ // the page because we check that the navigation is
+ // user initiated.
+ this._webView.load_request(navigationAction.get_request());
+ }
+
+ decision.ignore();
+ return true;
+ }
+
+ if (type != WebKit.PolicyDecisionType.NAVIGATION_ACTION)
+ return false;
+
+ let request = decision.get_request();
+ const uri = GLib.Uri.parse(request.get_uri(), HTTP_URI_FLAGS);
+
+ if (uri.get_host() !== this._uri.get_host() && this._originalUrlWasGnome) {
+ if (uri.get_host() == CONNECTIVITY_CHECK_HOST && this._everSeenRedirect) {
+ // Yay, we got to gnome!
+ decision.ignore();
+ this._doneCallback(PortalHelperResult.COMPLETED);
+ return true;
+ } else if (uri.get_host() != CONNECTIVITY_CHECK_HOST) {
+ this._everSeenRedirect = true;
+ }
+ }
+
+ // We *may* have finished here, but we don't know for
+ // sure. Tell gnome-shell to run another connectivity check
+ // (but ratelimit the checks, we don't want to spam
+ // nmcheck.gnome.org for portals that have 10 or more internal
+ // redirects - and unfortunately they exist)
+ // If we hit the rate limit, we also queue a recheck
+ // when the window is closed, just in case we miss the
+ // final check and don't realize we're connected
+ // This should not be a problem in the cancelled logic,
+ // because if the user doesn't want to start the login,
+ // we should not see any redirect at all, outside this._uri
+
+ let now = GLib.get_monotonic_time();
+ let shouldRecheck = (now - this._lastRecheck) >
+ CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT;
+
+ if (shouldRecheck) {
+ this._lastRecheck = now;
+ this._recheckAtExit = false;
+ this._doneCallback(PortalHelperResult.RECHECK);
+ } else {
+ this._recheckAtExit = true;
+ }
+
+ // Update the URI, in case of chained redirects, so we still
+ // think we're doing the login until gnome-shell kills us
+ this._uri = uri;
+
+ decision.use();
+ return true;
+ }
+});
+
+var WebPortalHelper = GObject.registerClass(
+class WebPortalHelper extends Gtk.Application {
+ _init() {
+ super._init({
+ application_id: 'org.gnome.Shell.PortalHelper',
+ flags: Gio.ApplicationFlags.IS_SERVICE,
+ inactivity_timeout: 30000,
+ });
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(HelperDBusInterface, this);
+ this._queue = [];
+
+ let action = new Gio.SimpleAction({ name: 'quit' });
+ action.connect('activate', () => this.active_window.destroyWindow());
+ this.add_action(action);
+ }
+
+ vfunc_dbus_register(connection, path) {
+ this._dbusImpl.export(connection, path);
+ super.vfunc_dbus_register(connection, path);
+ return true;
+ }
+
+ vfunc_dbus_unregister(connection, path) {
+ this._dbusImpl.unexport_from_connection(connection);
+ super.vfunc_dbus_unregister(connection, path);
+ }
+
+ vfunc_activate() {
+ // If launched manually (for example for testing), force a dummy authentication
+ // session with the default url
+ this.Authenticate('/org/gnome/dummy', '', 0);
+ }
+
+ Authenticate(connection, url, timestamp) {
+ this._queue.push({ connection, url, timestamp });
+
+ this._processQueue();
+ }
+
+ Close(connection) {
+ for (let i = 0; i < this._queue.length; i++) {
+ let obj = this._queue[i];
+
+ if (obj.connection == connection) {
+ if (obj.window)
+ obj.window.destroyWindow();
+ this._queue.splice(i, 1);
+ break;
+ }
+ }
+
+ this._processQueue();
+ }
+
+ Refresh(connection) {
+ for (let i = 0; i < this._queue.length; i++) {
+ let obj = this._queue[i];
+
+ if (obj.connection == connection) {
+ if (obj.window)
+ obj.window.refresh();
+ break;
+ }
+ }
+ }
+
+ _processQueue() {
+ if (this._queue.length == 0)
+ return;
+
+ let top = this._queue[0];
+ if (top.window != null)
+ return;
+
+ top.window = new PortalWindow(this, top.url, top.timestamp, result => {
+ this._dbusImpl.emit_signal('Done', new GLib.Variant('(ou)', [top.connection, result]));
+ });
+ }
+});
+
+function initEnvironment() {
+ String.prototype.format = Format.format;
+}
+
+function main(argv) {
+ initEnvironment();
+
+ if (!WebKit.WebContext.new_ephemeral) {
+ log('WebKitGTK 2.16 is required for the portal-helper, see https://bugzilla.gnome.org/show_bug.cgi?id=780453');
+ return 1;
+ }
+
+ Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
+ Gettext.textdomain(Config.GETTEXT_PACKAGE);
+
+ let app = new WebPortalHelper();
+ return app.run(argv);
+}
diff --git a/js/ui/accessDialog.js b/js/ui/accessDialog.js
new file mode 100644
index 0000000..8788e47
--- /dev/null
+++ b/js/ui/accessDialog.js
@@ -0,0 +1,160 @@
+/* exported AccessDialogDBus */
+const { Clutter, Gio, GLib, GObject, Pango, Shell, St } = imports.gi;
+
+const CheckBox = imports.ui.checkBox;
+const Dialog = imports.ui.dialog;
+const ModalDialog = imports.ui.modalDialog;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const RequestIface = loadInterfaceXML('org.freedesktop.impl.portal.Request');
+const AccessIface = loadInterfaceXML('org.freedesktop.impl.portal.Access');
+
+var DialogResponse = {
+ OK: 0,
+ CANCEL: 1,
+ CLOSED: 2,
+};
+
+var AccessDialog = GObject.registerClass(
+class AccessDialog extends ModalDialog.ModalDialog {
+ _init(invocation, handle, title, description, body, options) {
+ super._init({ styleClass: 'access-dialog' });
+
+ this._invocation = invocation;
+ this._handle = handle;
+
+ this._requestExported = false;
+ this._request = Gio.DBusExportedObject.wrapJSObject(RequestIface, this);
+
+ for (let option in options)
+ options[option] = options[option].deepUnpack();
+
+ this._buildLayout(title, description, body, options);
+ }
+
+ _buildLayout(title, description, body, options) {
+ // No support for non-modal system dialogs, so ignore the option
+ // let modal = options['modal'] || true;
+ let denyLabel = options['deny_label'] || _("Deny Access");
+ let grantLabel = options['grant_label'] || _("Grant Access");
+ let choices = options['choices'] || [];
+
+ let content = new Dialog.MessageDialogContent({ title, description });
+ this.contentLayout.add_actor(content);
+
+ this._choices = new Map();
+
+ for (let i = 0; i < choices.length; i++) {
+ let [id, name, opts, selected] = choices[i];
+ if (opts.length > 0)
+ continue; // radio buttons, not implemented
+
+ let check = new CheckBox.CheckBox();
+ check.getLabelActor().text = name;
+ check.checked = selected == "true";
+ content.add_child(check);
+
+ this._choices.set(id, check);
+ }
+
+ let bodyLabel = new St.Label({
+ text: body,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ bodyLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ bodyLabel.clutter_text.line_wrap = true;
+ content.add_child(bodyLabel);
+
+ this.addButton({
+ label: denyLabel,
+ action: () => this._sendResponse(DialogResponse.CANCEL),
+ key: Clutter.KEY_Escape,
+ });
+ this.addButton({
+ label: grantLabel,
+ action: () => this._sendResponse(DialogResponse.OK),
+ });
+ }
+
+ open() {
+ if (!super.open())
+ return false;
+
+ let connection = this._invocation.get_connection();
+ this._requestExported = this._request.export(connection, this._handle);
+ return true;
+ }
+
+ CloseAsync(invocation, _params) {
+ if (this._invocation.get_sender() != invocation.get_sender()) {
+ invocation.return_error_literal(Gio.DBusError,
+ Gio.DBusError.ACCESS_DENIED,
+ '');
+ return;
+ }
+
+ this._sendResponse(DialogResponse.CLOSED);
+ }
+
+ _sendResponse(response) {
+ if (this._requestExported)
+ this._request.unexport();
+ this._requestExported = false;
+
+ let results = {};
+ if (response == DialogResponse.OK) {
+ for (let [id, check] of this._choices) {
+ let checked = check.checked ? 'true' : 'false';
+ results[id] = new GLib.Variant('s', checked);
+ }
+ }
+
+ // Delay actual response until the end of the close animation (if any)
+ this.connect('closed', () => {
+ this._invocation.return_value(new GLib.Variant('(ua{sv})',
+ [response, results]));
+ });
+ this.close();
+ }
+});
+
+var AccessDialogDBus = class {
+ constructor() {
+ this._accessDialog = null;
+
+ this._windowTracker = Shell.WindowTracker.get_default();
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AccessIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/portal/desktop');
+
+ Gio.DBus.session.own_name('org.gnome.Shell.Portal', Gio.BusNameOwnerFlags.REPLACE, null, null);
+ }
+
+ AccessDialogAsync(params, invocation) {
+ if (this._accessDialog) {
+ invocation.return_error_literal(Gio.DBusError,
+ Gio.DBusError.LIMITS_EXCEEDED,
+ 'Already showing a system access dialog');
+ return;
+ }
+
+ let [handle, appId, parentWindow_, title, description, body, options] = params;
+ // We probably want to use parentWindow and global.display.focus_window
+ // for this check in the future
+ if (appId && `${appId}.desktop` !== this._windowTracker.focus_app.id) {
+ invocation.return_error_literal(Gio.DBusError,
+ Gio.DBusError.ACCESS_DENIED,
+ 'Only the focused app is allowed to show a system access dialog');
+ return;
+ }
+
+ let dialog = new AccessDialog(
+ invocation, handle, title, description, body, options);
+ dialog.open();
+
+ dialog.connect('closed', () => (this._accessDialog = null));
+
+ this._accessDialog = dialog;
+ }
+};
diff --git a/js/ui/altTab.js b/js/ui/altTab.js
new file mode 100644
index 0000000..a3daebc
--- /dev/null
+++ b/js/ui/altTab.js
@@ -0,0 +1,1134 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported AppSwitcherPopup, GroupCyclerPopup, WindowSwitcherPopup,
+ WindowCyclerPopup */
+
+const { Atk, Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const Main = imports.ui.main;
+const SwitcherPopup = imports.ui.switcherPopup;
+
+var APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
+
+var THUMBNAIL_DEFAULT_SIZE = 256;
+var THUMBNAIL_POPUP_TIME = 500; // milliseconds
+var THUMBNAIL_FADE_TIME = 100; // milliseconds
+
+var WINDOW_PREVIEW_SIZE = 128;
+var APP_ICON_SIZE = 96;
+var APP_ICON_SIZE_SMALL = 48;
+
+const baseIconSizes = [96, 64, 48, 32, 22];
+
+var AppIconMode = {
+ THUMBNAIL_ONLY: 1,
+ APP_ICON_ONLY: 2,
+ BOTH: 3,
+};
+
+function _createWindowClone(window, size) {
+ let [width, height] = window.get_size();
+ let scale = Math.min(1.0, size / width, size / height);
+ return new Clutter.Clone({
+ source: window,
+ width: width * scale,
+ height: height * scale,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ // usual hack for the usual bug in ClutterBinLayout...
+ x_expand: true,
+ y_expand: true,
+ });
+}
+
+function getWindows(workspace) {
+ // We ignore skip-taskbar windows in switchers, but if they are attached
+ // to their parent, their position in the MRU list may be more appropriate
+ // than the parent; so start with the complete list ...
+ let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL,
+ workspace);
+ // ... map windows to their parent where appropriate ...
+ return windows.map(w => {
+ return w.is_attached_dialog() ? w.get_transient_for() : w;
+ // ... and filter out skip-taskbar windows and duplicates
+ }).filter((w, i, a) => !w.skip_taskbar && a.indexOf(w) == i);
+}
+
+var AppSwitcherPopup = GObject.registerClass(
+class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
+ _init() {
+ super._init();
+
+ this._thumbnails = null;
+ this._thumbnailTimeoutId = 0;
+ this._currentWindow = -1;
+
+ this.thumbnailsVisible = false;
+
+ let apps = Shell.AppSystem.get_default().get_running();
+
+ this._switcherList = new AppSwitcher(apps, this);
+ this._items = this._switcherList.icons;
+ }
+
+ vfunc_allocate(box) {
+ super.vfunc_allocate(box);
+
+ // Allocate the thumbnails
+ // We try to avoid overflowing the screen so we base the resulting size on
+ // those calculations
+ if (this._thumbnails) {
+ let childBox = this._switcherList.get_allocation_box();
+ let primary = Main.layoutManager.primaryMonitor;
+
+ let leftPadding = this.get_theme_node().get_padding(St.Side.LEFT);
+ let rightPadding = this.get_theme_node().get_padding(St.Side.RIGHT);
+ let bottomPadding = this.get_theme_node().get_padding(St.Side.BOTTOM);
+ let hPadding = leftPadding + rightPadding;
+
+ let icon = this._items[this._selectedIndex];
+ let [posX] = icon.get_transformed_position();
+ let thumbnailCenter = posX + icon.width / 2;
+ let [, childNaturalWidth] = this._thumbnails.get_preferred_width(-1);
+ childBox.x1 = Math.max(primary.x + leftPadding, Math.floor(thumbnailCenter - childNaturalWidth / 2));
+ if (childBox.x1 + childNaturalWidth > primary.x + primary.width - hPadding) {
+ let offset = childBox.x1 + childNaturalWidth - primary.width + hPadding;
+ childBox.x1 = Math.max(primary.x + leftPadding, childBox.x1 - offset - hPadding);
+ }
+
+ let spacing = this.get_theme_node().get_length('spacing');
+
+ childBox.x2 = childBox.x1 + childNaturalWidth;
+ if (childBox.x2 > primary.x + primary.width - rightPadding)
+ childBox.x2 = primary.x + primary.width - rightPadding;
+ childBox.y1 = this._switcherList.allocation.y2 + spacing;
+ this._thumbnails.addClones(primary.y + primary.height - bottomPadding - childBox.y1);
+ let [, childNaturalHeight] = this._thumbnails.get_preferred_height(-1);
+ childBox.y2 = childBox.y1 + childNaturalHeight;
+ this._thumbnails.allocate(childBox);
+ }
+ }
+
+ _initialSelection(backward, binding) {
+ if (binding == 'switch-group') {
+ if (backward)
+ this._select(0, this._items[0].cachedWindows.length - 1);
+ else if (this._items[0].cachedWindows.length > 1)
+ this._select(0, 1);
+ else
+ this._select(0, 0);
+ } else if (binding == 'switch-group-backward') {
+ this._select(0, this._items[0].cachedWindows.length - 1);
+ } else if (binding == 'switch-applications-backward') {
+ this._select(this._items.length - 1);
+ } else if (this._items.length == 1) {
+ this._select(0);
+ } else if (backward) {
+ this._select(this._items.length - 1);
+ } else {
+ this._select(1);
+ }
+ }
+
+ _nextWindow() {
+ // We actually want the second window if we're in the unset state
+ if (this._currentWindow == -1)
+ this._currentWindow = 0;
+ return SwitcherPopup.mod(this._currentWindow + 1,
+ this._items[this._selectedIndex].cachedWindows.length);
+ }
+
+ _previousWindow() {
+ // Also assume second window here
+ if (this._currentWindow == -1)
+ this._currentWindow = 1;
+ return SwitcherPopup.mod(this._currentWindow - 1,
+ this._items[this._selectedIndex].cachedWindows.length);
+ }
+
+ _closeAppWindow(appIndex, windowIndex) {
+ let appIcon = this._items[appIndex];
+ if (!appIcon)
+ return;
+
+ let window = appIcon.cachedWindows[windowIndex];
+ if (!window)
+ return;
+
+ window.delete(global.get_current_time());
+ }
+
+ _quitApplication(appIndex) {
+ let appIcon = this._items[appIndex];
+ if (!appIcon)
+ return;
+
+ appIcon.app.request_quit();
+ }
+
+ _keyPressHandler(keysym, action) {
+ const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+ if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
+ if (!this._thumbnailsFocused)
+ this._select(this._selectedIndex, 0);
+ else
+ this._select(this._selectedIndex, this._nextWindow());
+ } else if (action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
+ this._select(this._selectedIndex, this._previousWindow());
+ } else if (action == Meta.KeyBindingAction.SWITCH_APPLICATIONS) {
+ this._select(this._next());
+ } else if (action == Meta.KeyBindingAction.SWITCH_APPLICATIONS_BACKWARD) {
+ this._select(this._previous());
+ } else if (keysym == Clutter.KEY_q || keysym === Clutter.KEY_Q) {
+ this._quitApplication(this._selectedIndex);
+ } else if (this._thumbnailsFocused) {
+ if (keysym === Clutter.KEY_Left)
+ this._select(this._selectedIndex, rtl ? this._nextWindow() : this._previousWindow());
+ else if (keysym === Clutter.KEY_Right)
+ this._select(this._selectedIndex, rtl ? this._previousWindow() : this._nextWindow());
+ else if (keysym === Clutter.KEY_Up)
+ this._select(this._selectedIndex, null, true);
+ else if (keysym === Clutter.KEY_w || keysym === Clutter.KEY_W || keysym === Clutter.KEY_F4)
+ this._closeAppWindow(this._selectedIndex, this._currentWindow);
+ else
+ return Clutter.EVENT_PROPAGATE;
+ } else if (keysym == Clutter.KEY_Left) {
+ this._select(rtl ? this._next() : this._previous());
+ } else if (keysym == Clutter.KEY_Right) {
+ this._select(rtl ? this._previous() : this._next());
+ } else if (keysym == Clutter.KEY_Down) {
+ this._select(this._selectedIndex, 0);
+ } else {
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _scrollHandler(direction) {
+ if (direction == Clutter.ScrollDirection.UP) {
+ if (this._thumbnailsFocused) {
+ if (this._currentWindow == 0 || this._currentWindow == -1)
+ this._select(this._previous());
+ else
+ this._select(this._selectedIndex, this._previousWindow());
+ } else {
+ let nwindows = this._items[this._selectedIndex].cachedWindows.length;
+ if (nwindows > 1)
+ this._select(this._selectedIndex, nwindows - 1);
+ else
+ this._select(this._previous());
+ }
+ } else if (direction == Clutter.ScrollDirection.DOWN) {
+ if (this._thumbnailsFocused) {
+ if (this._currentWindow == this._items[this._selectedIndex].cachedWindows.length - 1)
+ this._select(this._next());
+ else
+ this._select(this._selectedIndex, this._nextWindow());
+ } else {
+ let nwindows = this._items[this._selectedIndex].cachedWindows.length;
+ if (nwindows > 1)
+ this._select(this._selectedIndex, 0);
+ else
+ this._select(this._next());
+ }
+ }
+ }
+
+ _itemActivatedHandler(n) {
+ // If the user clicks on the selected app, activate the
+ // selected window; otherwise (eg, they click on an app while
+ // !mouseActive) activate the clicked-on app.
+ if (n == this._selectedIndex && this._currentWindow >= 0)
+ this._select(n, this._currentWindow);
+ else
+ this._select(n);
+ }
+
+ _windowActivated(thumbnailSwitcher, n) {
+ let appIcon = this._items[this._selectedIndex];
+ Main.activateWindow(appIcon.cachedWindows[n]);
+ this.fadeAndDestroy();
+ }
+
+ _windowEntered(thumbnailSwitcher, n) {
+ if (!this.mouseActive)
+ return;
+
+ this._select(this._selectedIndex, n);
+ }
+
+ _windowRemoved(thumbnailSwitcher, n) {
+ let appIcon = this._items[this._selectedIndex];
+ if (!appIcon)
+ return;
+
+ if (appIcon.cachedWindows.length > 0) {
+ let newIndex = Math.min(n, appIcon.cachedWindows.length - 1);
+ this._select(this._selectedIndex, newIndex);
+ }
+ }
+
+ _finish(timestamp) {
+ let appIcon = this._items[this._selectedIndex];
+ if (this._currentWindow < 0)
+ appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp);
+ else if (appIcon.cachedWindows[this._currentWindow])
+ Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
+
+ super._finish(timestamp);
+ }
+
+ _onDestroy() {
+ if (this._thumbnailTimeoutId != 0)
+ GLib.source_remove(this._thumbnailTimeoutId);
+
+ super._onDestroy();
+ }
+
+ /**
+ * _select:
+ * @param {number} app: index of the app to select
+ * @param {number=} window: index of which of @app's windows to select
+ * @param {bool} forceAppFocus: optional flag, see below
+ *
+ * Selects the indicated @app, and optional @window, and sets
+ * this._thumbnailsFocused appropriately to indicate whether the
+ * arrow keys should act on the app list or the thumbnail list.
+ *
+ * If @app is specified and @window is unspecified or %null, then
+ * the app is highlighted (ie, given a light background), and the
+ * current thumbnail list, if any, is destroyed. If @app has
+ * multiple windows, and @forceAppFocus is not %true, then a
+ * timeout is started to open a thumbnail list.
+ *
+ * If @app and @window are specified (and @forceAppFocus is not),
+ * then @app will be outlined, a thumbnail list will be created
+ * and focused (if it hasn't been already), and the @window'th
+ * window in it will be highlighted.
+ *
+ * If @app and @window are specified and @forceAppFocus is %true,
+ * then @app will be highlighted, and @window outlined, and the
+ * app list will have the keyboard focus.
+ */
+ _select(app, window, forceAppFocus) {
+ if (app != this._selectedIndex || window == null) {
+ if (this._thumbnails)
+ this._destroyThumbnails();
+ }
+
+ if (this._thumbnailTimeoutId != 0) {
+ GLib.source_remove(this._thumbnailTimeoutId);
+ this._thumbnailTimeoutId = 0;
+ }
+
+ this._thumbnailsFocused = (window != null) && !forceAppFocus;
+
+ this._selectedIndex = app;
+ this._currentWindow = window ? window : -1;
+ this._switcherList.highlight(app, this._thumbnailsFocused);
+
+ if (window != null) {
+ if (!this._thumbnails)
+ this._createThumbnails();
+ this._currentWindow = window;
+ this._thumbnails.highlight(window, forceAppFocus);
+ } else if (this._items[this._selectedIndex].cachedWindows.length > 1 &&
+ !forceAppFocus) {
+ this._thumbnailTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ THUMBNAIL_POPUP_TIME,
+ this._timeoutPopupThumbnails.bind(this));
+ GLib.Source.set_name_by_id(this._thumbnailTimeoutId, '[gnome-shell] this._timeoutPopupThumbnails');
+ }
+ }
+
+ _timeoutPopupThumbnails() {
+ if (!this._thumbnails)
+ this._createThumbnails();
+ this._thumbnailTimeoutId = 0;
+ this._thumbnailsFocused = false;
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _destroyThumbnails() {
+ let thumbnailsActor = this._thumbnails;
+ this._thumbnails.ease({
+ opacity: 0,
+ duration: THUMBNAIL_FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ thumbnailsActor.destroy();
+ this.thumbnailsVisible = false;
+ },
+ });
+ this._thumbnails = null;
+ this._switcherList.removeAccessibleState(this._selectedIndex, Atk.StateType.EXPANDED);
+ }
+
+ _createThumbnails() {
+ this._thumbnails = new ThumbnailSwitcher(this._items[this._selectedIndex].cachedWindows);
+ this._thumbnails.connect('item-activated', this._windowActivated.bind(this));
+ this._thumbnails.connect('item-entered', this._windowEntered.bind(this));
+ this._thumbnails.connect('item-removed', this._windowRemoved.bind(this));
+ this._thumbnails.connect('destroy', () => {
+ this._thumbnails = null;
+ this._thumbnailsFocused = false;
+ });
+
+ this.add_actor(this._thumbnails);
+
+ // Need to force an allocation so we can figure out whether we
+ // need to scroll when selecting
+ this._thumbnails.get_allocation_box();
+
+ this._thumbnails.opacity = 0;
+ this._thumbnails.ease({
+ opacity: 255,
+ duration: THUMBNAIL_FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this.thumbnailsVisible = true;
+ },
+ });
+
+ this._switcherList.addAccessibleState(this._selectedIndex, Atk.StateType.EXPANDED);
+ }
+});
+
+var CyclerHighlight = GObject.registerClass(
+class CyclerHighlight extends St.Widget {
+ _init() {
+ super._init({ layout_manager: new Clutter.BinLayout() });
+ this._window = null;
+
+ this._clone = new Clutter.Clone();
+ this.add_actor(this._clone);
+
+ this._highlight = new St.Widget({ style_class: 'cycler-highlight' });
+ this.add_actor(this._highlight);
+
+ let coordinate = Clutter.BindCoordinate.ALL;
+ let constraint = new Clutter.BindConstraint({ coordinate });
+ this._clone.bind_property('source', constraint, 'source', 0);
+
+ this.add_constraint(constraint);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ set window(w) {
+ if (this._window == w)
+ return;
+
+ this._window?.disconnectObject(this);
+
+ this._window = w;
+
+ if (this._clone.source)
+ this._clone.source.sync_visibility();
+
+ const windowActor = this._window?.get_compositor_private() ?? null;
+
+ if (windowActor)
+ windowActor.hide();
+
+ this._clone.source = windowActor;
+
+ if (this._window) {
+ this._onSizeChanged();
+ this._window.connectObject('size-changed',
+ this._onSizeChanged.bind(this), this);
+ } else {
+ this._highlight.set_size(0, 0);
+ this._highlight.hide();
+ }
+ }
+
+ _onSizeChanged() {
+ const bufferRect = this._window.get_buffer_rect();
+ const rect = this._window.get_frame_rect();
+ this._highlight.set_size(rect.width, rect.height);
+ this._highlight.set_position(
+ rect.x - bufferRect.x,
+ rect.y - bufferRect.y);
+ this._highlight.show();
+ }
+
+ _onDestroy() {
+ this.window = null;
+ }
+});
+
+// We don't show an actual popup, so just provide what SwitcherPopup
+// expects instead of inheriting from SwitcherList
+var CyclerList = GObject.registerClass({
+ Signals: {
+ 'item-activated': { param_types: [GObject.TYPE_INT] },
+ 'item-entered': { param_types: [GObject.TYPE_INT] },
+ 'item-removed': { param_types: [GObject.TYPE_INT] },
+ 'item-highlighted': { param_types: [GObject.TYPE_INT] },
+ },
+}, class CyclerList extends St.Widget {
+ highlight(index, _justOutline) {
+ this.emit('item-highlighted', index);
+ }
+});
+
+var CyclerPopup = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+}, class CyclerPopup extends SwitcherPopup.SwitcherPopup {
+ _init() {
+ super._init();
+
+ this._items = this._getWindows();
+
+ this._highlight = new CyclerHighlight();
+ global.window_group.add_actor(this._highlight);
+
+ this._switcherList = new CyclerList();
+ this._switcherList.connect('item-highlighted', (list, index) => {
+ this._highlightItem(index);
+ });
+ }
+
+ _highlightItem(index, _justOutline) {
+ this._highlight.window = this._items[index];
+ global.window_group.set_child_above_sibling(this._highlight, null);
+ }
+
+ _finish() {
+ let window = this._items[this._selectedIndex];
+ let ws = window.get_workspace();
+ let workspaceManager = global.workspace_manager;
+ let activeWs = workspaceManager.get_active_workspace();
+
+ if (window.minimized) {
+ Main.wm.skipNextEffect(window.get_compositor_private());
+ window.unminimize();
+ }
+
+ if (activeWs == ws) {
+ Main.activateWindow(window);
+ } else {
+ // If the selected window is on a different workspace, we don't
+ // want it to disappear, then slide in with the workspace; instead,
+ // always activate it on the active workspace ...
+ activeWs.activate_with_focus(window, global.get_current_time());
+
+ // ... then slide it over to the original workspace if necessary
+ Main.wm.actionMoveWindow(window, ws);
+ }
+
+ super._finish();
+ }
+
+ _onDestroy() {
+ this._highlight.destroy();
+
+ super._onDestroy();
+ }
+});
+
+
+var GroupCyclerPopup = GObject.registerClass(
+class GroupCyclerPopup extends CyclerPopup {
+ _init() {
+ this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.app-switcher' });
+ super._init();
+ }
+
+ _getWindows() {
+ let app = Shell.WindowTracker.get_default().focus_app;
+ let appWindows = app?.get_windows() ?? [];
+
+ if (this._settings.get_boolean('current-workspace-only')) {
+ const workspaceManager = global.workspace_manager;
+ const workspace = workspaceManager.get_active_workspace();
+ appWindows = appWindows.filter(
+ window => window.located_on_workspace(workspace));
+ }
+
+ return appWindows;
+ }
+
+ _keyPressHandler(keysym, action) {
+ if (action == Meta.KeyBindingAction.CYCLE_GROUP)
+ this._select(this._next());
+ else if (action == Meta.KeyBindingAction.CYCLE_GROUP_BACKWARD)
+ this._select(this._previous());
+ else
+ return Clutter.EVENT_PROPAGATE;
+
+ return Clutter.EVENT_STOP;
+ }
+});
+
+var WindowSwitcherPopup = GObject.registerClass(
+class WindowSwitcherPopup extends SwitcherPopup.SwitcherPopup {
+ _init() {
+ super._init();
+ this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.window-switcher' });
+
+ let windows = this._getWindowList();
+
+ let mode = this._settings.get_enum('app-icon-mode');
+ this._switcherList = new WindowSwitcher(windows, mode);
+ this._items = this._switcherList.icons;
+ }
+
+ _getWindowList() {
+ let workspace = null;
+
+ if (this._settings.get_boolean('current-workspace-only')) {
+ let workspaceManager = global.workspace_manager;
+
+ workspace = workspaceManager.get_active_workspace();
+ }
+
+ return getWindows(workspace);
+ }
+
+ _closeWindow(windowIndex) {
+ let windowIcon = this._items[windowIndex];
+ if (!windowIcon)
+ return;
+
+ windowIcon.window.delete(global.get_current_time());
+ }
+
+ _keyPressHandler(keysym, action) {
+ const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+ if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
+ this._select(this._next());
+ else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD)
+ this._select(this._previous());
+ else if (keysym == Clutter.KEY_Left)
+ this._select(rtl ? this._next() : this._previous());
+ else if (keysym == Clutter.KEY_Right)
+ this._select(rtl ? this._previous() : this._next());
+ else if (keysym === Clutter.KEY_w || keysym === Clutter.KEY_W || keysym === Clutter.KEY_F4)
+ this._closeWindow(this._selectedIndex);
+ else
+ return Clutter.EVENT_PROPAGATE;
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _finish() {
+ Main.activateWindow(this._items[this._selectedIndex].window);
+
+ super._finish();
+ }
+});
+
+var WindowCyclerPopup = GObject.registerClass(
+class WindowCyclerPopup extends CyclerPopup {
+ _init() {
+ this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.window-switcher' });
+ super._init();
+ }
+
+ _getWindows() {
+ let workspace = null;
+
+ if (this._settings.get_boolean('current-workspace-only')) {
+ let workspaceManager = global.workspace_manager;
+
+ workspace = workspaceManager.get_active_workspace();
+ }
+
+ return getWindows(workspace);
+ }
+
+ _keyPressHandler(keysym, action) {
+ if (action == Meta.KeyBindingAction.CYCLE_WINDOWS)
+ this._select(this._next());
+ else if (action == Meta.KeyBindingAction.CYCLE_WINDOWS_BACKWARD)
+ this._select(this._previous());
+ else
+ return Clutter.EVENT_PROPAGATE;
+
+ return Clutter.EVENT_STOP;
+ }
+});
+
+var AppIcon = GObject.registerClass(
+class AppIcon extends St.BoxLayout {
+ _init(app) {
+ super._init({
+ style_class: 'alt-tab-app',
+ vertical: true,
+ });
+
+ this.app = app;
+ this.icon = null;
+ this._iconBin = new St.Bin();
+
+ this.add_child(this._iconBin);
+ this.label = new St.Label({
+ text: this.app.get_name(),
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this.label);
+ }
+
+ // eslint-disable-next-line camelcase
+ set_size(size) {
+ this.icon = this.app.create_icon_texture(size);
+ this._iconBin.child = this.icon;
+ }
+});
+
+var AppSwitcher = GObject.registerClass(
+class AppSwitcher extends SwitcherPopup.SwitcherList {
+ _init(apps, altTabPopup) {
+ super._init(true);
+
+ this.icons = [];
+ this._arrows = [];
+
+ let windowTracker = Shell.WindowTracker.get_default();
+ let settings = new Gio.Settings({ schema_id: 'org.gnome.shell.app-switcher' });
+
+ let workspace = null;
+ if (settings.get_boolean('current-workspace-only')) {
+ let workspaceManager = global.workspace_manager;
+
+ workspace = workspaceManager.get_active_workspace();
+ }
+
+ let allWindows = getWindows(workspace);
+
+ // Construct the AppIcons, add to the popup
+ for (let i = 0; i < apps.length; i++) {
+ let appIcon = new AppIcon(apps[i]);
+ // Cache the window list now; we don't handle dynamic changes here,
+ // and we don't want to be continually retrieving it
+ appIcon.cachedWindows = allWindows.filter(
+ w => windowTracker.get_window_app(w) === appIcon.app);
+ if (appIcon.cachedWindows.length > 0)
+ this._addIcon(appIcon);
+ }
+
+ this._altTabPopup = altTabPopup;
+ this._delayedHighlighted = -1;
+ this._mouseTimeOutId = 0;
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ if (this._mouseTimeOutId != 0)
+ GLib.source_remove(this._mouseTimeOutId);
+
+ this.icons.forEach(
+ icon => icon.app.disconnectObject(this));
+ }
+
+ _setIconSize() {
+ let j = 0;
+ while (this._items.length > 1 && this._items[j].style_class != 'item-box')
+ j++;
+
+ let themeNode = this._items[j].get_theme_node();
+ this._list.ensure_style();
+
+ let iconPadding = themeNode.get_horizontal_padding();
+ let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
+ let [, labelNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
+ let iconSpacing = labelNaturalHeight + iconPadding + iconBorder;
+ let totalSpacing = this._list.spacing * (this._items.length - 1);
+
+ // We just assume the whole screen here due to weirdness happening with the passed width
+ let primary = Main.layoutManager.primaryMonitor;
+ let parentPadding = this.get_parent().get_theme_node().get_horizontal_padding();
+ let availWidth = primary.width - parentPadding - this.get_theme_node().get_horizontal_padding();
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let iconSizes = baseIconSizes.map(s => s * scaleFactor);
+ let iconSize = baseIconSizes[0];
+
+ if (this._items.length > 1) {
+ for (let i = 0; i < baseIconSizes.length; i++) {
+ iconSize = baseIconSizes[i];
+ let height = iconSizes[i] + iconSpacing;
+ let w = height * this._items.length + totalSpacing;
+ if (w <= availWidth)
+ break;
+ }
+ }
+
+ this._iconSize = iconSize;
+
+ for (let i = 0; i < this.icons.length; i++) {
+ if (this.icons[i].icon != null)
+ break;
+ this.icons[i].set_size(iconSize);
+ }
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ if (!this._iconSize)
+ this._setIconSize();
+
+ return super.vfunc_get_preferred_height(forWidth);
+ }
+
+ vfunc_allocate(box) {
+ // Allocate the main list items
+ super.vfunc_allocate(box);
+
+ let contentBox = this.get_theme_node().get_content_box(box);
+
+ let arrowHeight = Math.floor(this.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
+ let arrowWidth = arrowHeight * 2;
+
+ // Now allocate each arrow underneath its item
+ let childBox = new Clutter.ActorBox();
+ for (let i = 0; i < this._items.length; i++) {
+ let itemBox = this._items[i].allocation;
+ childBox.x1 = contentBox.x1 + Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
+ childBox.x2 = childBox.x1 + arrowWidth;
+ childBox.y1 = contentBox.y1 + itemBox.y2 + arrowHeight;
+ childBox.y2 = childBox.y1 + arrowHeight;
+ this._arrows[i].allocate(childBox);
+ }
+ }
+
+ // We override SwitcherList's _onItemMotion method to delay
+ // activation when the thumbnail list is open
+ _onItemMotion(item) {
+ if (item === this._items[this._highlighted] ||
+ item === this._items[this._delayedHighlighted])
+ return Clutter.EVENT_PROPAGATE;
+
+ const index = this._items.indexOf(item);
+
+ if (this._mouseTimeOutId !== 0) {
+ GLib.source_remove(this._mouseTimeOutId);
+ this._delayedHighlighted = -1;
+ this._mouseTimeOutId = 0;
+ }
+
+ if (this._altTabPopup.thumbnailsVisible) {
+ this._delayedHighlighted = index;
+ this._mouseTimeOutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ APP_ICON_HOVER_TIMEOUT,
+ () => {
+ this._enterItem(index);
+ this._delayedHighlighted = -1;
+ this._mouseTimeOutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._mouseTimeOutId, '[gnome-shell] this._enterItem');
+ } else {
+ this._itemEntered(index);
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _enterItem(index) {
+ let [x, y] = global.get_pointer();
+ let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
+ if (this._items[index].contains(pickedActor))
+ this._itemEntered(index);
+ }
+
+ // We override SwitcherList's highlight() method to also deal with
+ // the AppSwitcher->ThumbnailSwitcher arrows. Apps with only 1 window
+ // will hide their arrows by default, but show them when their
+ // thumbnails are visible (ie, when the app icon is supposed to be
+ // in justOutline mode). Apps with multiple windows will normally
+ // show a dim arrow, but show a bright arrow when they are
+ // highlighted.
+ highlight(n, justOutline) {
+ if (this.icons[this._highlighted]) {
+ if (this.icons[this._highlighted].cachedWindows.length === 1)
+ this._arrows[this._highlighted].hide();
+ else
+ this._arrows[this._highlighted].remove_style_pseudo_class('highlighted');
+ }
+
+ super.highlight(n, justOutline);
+
+ if (this._highlighted !== -1) {
+ if (justOutline && this.icons[this._highlighted].cachedWindows.length === 1)
+ this._arrows[this._highlighted].show();
+ else
+ this._arrows[this._highlighted].add_style_pseudo_class('highlighted');
+ }
+ }
+
+ _addIcon(appIcon) {
+ this.icons.push(appIcon);
+ let item = this.addItem(appIcon, appIcon.label);
+
+ appIcon.app.connectObject('notify::state', app => {
+ if (app.state != Shell.AppState.RUNNING)
+ this._removeIcon(app);
+ }, this);
+
+ let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
+ arrow.connect('repaint', () => SwitcherPopup.drawArrow(arrow, St.Side.BOTTOM));
+ this.add_actor(arrow);
+ this._arrows.push(arrow);
+
+ if (appIcon.cachedWindows.length == 1)
+ arrow.hide();
+ else
+ item.add_accessible_state(Atk.StateType.EXPANDABLE);
+ }
+
+ _removeIcon(app) {
+ let index = this.icons.findIndex(icon => {
+ return icon.app == app;
+ });
+ if (index === -1)
+ return;
+
+ this._arrows[index].destroy();
+ this._arrows.splice(index, 1);
+
+ this.icons.splice(index, 1);
+ this.removeItem(index);
+ }
+});
+
+var ThumbnailSwitcher = GObject.registerClass(
+class ThumbnailSwitcher extends SwitcherPopup.SwitcherList {
+ _init(windows) {
+ super._init(false);
+
+ this._labels = [];
+ this._thumbnailBins = [];
+ this._clones = [];
+ this._windows = windows;
+
+ for (let i = 0; i < windows.length; i++) {
+ const box = new St.BoxLayout({
+ style_class: 'thumbnail-box',
+ vertical: true,
+ });
+
+ let bin = new St.Bin({ style_class: 'thumbnail' });
+
+ box.add_actor(bin);
+ this._thumbnailBins.push(bin);
+
+ const title = windows[i].get_title();
+ const name = new St.Label({
+ text: title,
+ // St.Label doesn't support text-align
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this._labels.push(name);
+ box.add_actor(name);
+
+ this.addItem(box, name);
+ }
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ addClones(availHeight) {
+ if (!this._thumbnailBins.length)
+ return;
+ let totalPadding = this._items[0].get_theme_node().get_horizontal_padding() + this._items[0].get_theme_node().get_vertical_padding();
+ totalPadding += this.get_theme_node().get_horizontal_padding() + this.get_theme_node().get_vertical_padding();
+ let [, labelNaturalHeight] = this._labels[0].get_preferred_height(-1);
+ let spacing = this._items[0].child.get_theme_node().get_length('spacing');
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let thumbnailSize = THUMBNAIL_DEFAULT_SIZE * scaleFactor;
+
+ availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, thumbnailSize);
+ let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.get_theme_node().get_vertical_padding() - spacing;
+ binHeight = Math.min(thumbnailSize, binHeight);
+
+ for (let i = 0; i < this._thumbnailBins.length; i++) {
+ let mutterWindow = this._windows[i].get_compositor_private();
+ if (!mutterWindow)
+ continue;
+
+ let clone = _createWindowClone(mutterWindow, thumbnailSize);
+ this._thumbnailBins[i].set_height(binHeight);
+ this._thumbnailBins[i].add_actor(clone);
+
+ mutterWindow.connectObject('destroy',
+ source => this._removeThumbnail(source, clone), this);
+ this._clones.push(clone);
+ }
+
+ // Make sure we only do this once
+ this._thumbnailBins = [];
+ }
+
+ _removeThumbnail(source, clone) {
+ let index = this._clones.indexOf(clone);
+ if (index === -1)
+ return;
+
+ this._clones.splice(index, 1);
+ this._windows.splice(index, 1);
+ this._labels.splice(index, 1);
+ this.removeItem(index);
+
+ if (this._clones.length > 0)
+ this.highlight(SwitcherPopup.mod(index, this._clones.length));
+ else
+ this.destroy();
+ }
+
+ _onDestroy() {
+ this._clones.forEach(
+ clone => clone?.source.disconnectObject(this));
+ }
+});
+
+var WindowIcon = GObject.registerClass(
+class WindowIcon extends St.BoxLayout {
+ _init(window, mode) {
+ super._init({
+ style_class: 'alt-tab-app',
+ vertical: true,
+ });
+
+ this.window = window;
+
+ this._icon = new St.Widget({ layout_manager: new Clutter.BinLayout() });
+
+ this.add_child(this._icon);
+ this.label = new St.Label({ text: window.get_title() });
+
+ let tracker = Shell.WindowTracker.get_default();
+ this.app = tracker.get_window_app(window);
+
+ let mutterWindow = this.window.get_compositor_private();
+ let size;
+
+ this._icon.destroy_all_children();
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+
+ switch (mode) {
+ case AppIconMode.THUMBNAIL_ONLY:
+ size = WINDOW_PREVIEW_SIZE;
+ this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor));
+ break;
+
+ case AppIconMode.BOTH:
+ size = WINDOW_PREVIEW_SIZE;
+ this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor));
+
+ if (this.app) {
+ this._icon.add_actor(this._createAppIcon(this.app,
+ APP_ICON_SIZE_SMALL));
+ }
+ break;
+
+ case AppIconMode.APP_ICON_ONLY:
+ size = APP_ICON_SIZE;
+ this._icon.add_actor(this._createAppIcon(this.app, size));
+ }
+
+ this._icon.set_size(size * scaleFactor, size * scaleFactor);
+ }
+
+ _createAppIcon(app, size) {
+ let appIcon = app
+ ? app.create_icon_texture(size)
+ : new St.Icon({ icon_name: 'icon-missing', icon_size: size });
+ appIcon.x_expand = appIcon.y_expand = true;
+ appIcon.x_align = appIcon.y_align = Clutter.ActorAlign.END;
+
+ return appIcon;
+ }
+});
+
+var WindowSwitcher = GObject.registerClass(
+class WindowSwitcher extends SwitcherPopup.SwitcherList {
+ _init(windows, mode) {
+ super._init(true);
+
+ this._label = new St.Label({
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_actor(this._label);
+
+ this.windows = windows;
+ this.icons = [];
+
+ for (let i = 0; i < windows.length; i++) {
+ let win = windows[i];
+ let icon = new WindowIcon(win, mode);
+
+ this.addItem(icon, icon.label);
+ this.icons.push(icon);
+
+ icon.window.connectObject('unmanaged',
+ window => this._removeWindow(window), this);
+ }
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ this.icons.forEach(
+ icon => icon.window.disconnectObject(this));
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ let [minHeight, natHeight] = super.vfunc_get_preferred_height(forWidth);
+
+ let spacing = this.get_theme_node().get_padding(St.Side.BOTTOM);
+ let [labelMin, labelNat] = this._label.get_preferred_height(-1);
+
+ minHeight += labelMin + spacing;
+ natHeight += labelNat + spacing;
+
+ return [minHeight, natHeight];
+ }
+
+ vfunc_allocate(box) {
+ let themeNode = this.get_theme_node();
+ let contentBox = themeNode.get_content_box(box);
+ const labelHeight = this._label.height;
+ const totalLabelHeight =
+ labelHeight + themeNode.get_padding(St.Side.BOTTOM);
+
+ box.y2 -= totalLabelHeight;
+ super.vfunc_allocate(box);
+
+ // Hooking up the parent vfunc will call this.set_allocation() with
+ // the height without the label height, so call it again with the
+ // correct size here.
+ box.y2 += totalLabelHeight;
+ this.set_allocation(box);
+
+ const childBox = new Clutter.ActorBox();
+ childBox.x1 = contentBox.x1;
+ childBox.x2 = contentBox.x2;
+ childBox.y2 = contentBox.y2;
+ childBox.y1 = childBox.y2 - labelHeight;
+ this._label.allocate(childBox);
+ }
+
+ highlight(index, justOutline) {
+ super.highlight(index, justOutline);
+
+ this._label.set_text(index == -1 ? '' : this.icons[index].label.text);
+ }
+
+ _removeWindow(window) {
+ let index = this.icons.findIndex(icon => {
+ return icon.window == window;
+ });
+ if (index === -1)
+ return;
+
+ this.icons.splice(index, 1);
+ this.removeItem(index);
+ }
+});
diff --git a/js/ui/animation.js b/js/ui/animation.js
new file mode 100644
index 0000000..c2ed248
--- /dev/null
+++ b/js/ui/animation.js
@@ -0,0 +1,196 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Animation, AnimatedIcon, Spinner */
+
+const { Clutter, GLib, GObject, Gio, St } = imports.gi;
+
+const Params = imports.misc.params;
+
+var ANIMATED_ICON_UPDATE_TIMEOUT = 16;
+var SPINNER_ANIMATION_TIME = 300;
+var SPINNER_ANIMATION_DELAY = 1000;
+
+var Animation = GObject.registerClass(
+class Animation extends St.Bin {
+ _init(file, width, height, speed) {
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+
+ super._init({
+ style: `width: ${width}px; height: ${height}px;`,
+ });
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ this.connect('resource-scale-changed',
+ this._loadFile.bind(this, file, width, height));
+
+ themeContext.connectObject('notify::scale-factor',
+ () => {
+ this._loadFile(file, width, height);
+ this.set_size(width * themeContext.scale_factor, height * themeContext.scale_factor);
+ }, this);
+
+ this._speed = speed;
+
+ this._isLoaded = false;
+ this._isPlaying = false;
+ this._timeoutId = 0;
+ this._frame = 0;
+
+ this._loadFile(file, width, height);
+ }
+
+ play() {
+ if (this._isLoaded && this._timeoutId == 0) {
+ if (this._frame == 0)
+ this._showFrame(0);
+
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_LOW, this._speed, this._update.bind(this));
+ GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._update');
+ }
+
+ this._isPlaying = true;
+ }
+
+ stop() {
+ if (this._timeoutId > 0) {
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+ }
+
+ this._isPlaying = false;
+ }
+
+ _loadFile(file, width, height) {
+ const resourceScale = this.get_resource_scale();
+ let wasPlaying = this._isPlaying;
+
+ if (this._isPlaying)
+ this.stop();
+
+ this._isLoaded = false;
+ this.destroy_all_children();
+
+ let textureCache = St.TextureCache.get_default();
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ this._animations = textureCache.load_sliced_image(file, width, height,
+ scaleFactor, resourceScale,
+ this._animationsLoaded.bind(this));
+ this._animations.set({
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.set_child(this._animations);
+
+ if (wasPlaying)
+ this.play();
+ }
+
+ _showFrame(frame) {
+ let oldFrameActor = this._animations.get_child_at_index(this._frame);
+ if (oldFrameActor)
+ oldFrameActor.hide();
+
+ this._frame = frame % this._animations.get_n_children();
+
+ let newFrameActor = this._animations.get_child_at_index(this._frame);
+ if (newFrameActor)
+ newFrameActor.show();
+ }
+
+ _update() {
+ this._showFrame(this._frame + 1);
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ _syncAnimationSize() {
+ if (!this._isLoaded)
+ return;
+
+ let [width, height] = this.get_size();
+
+ for (let i = 0; i < this._animations.get_n_children(); ++i)
+ this._animations.get_child_at_index(i).set_size(width, height);
+ }
+
+ _animationsLoaded() {
+ this._isLoaded = this._animations.get_n_children() > 0;
+
+ this._syncAnimationSize();
+
+ if (this._isPlaying)
+ this.play();
+ }
+
+ _onDestroy() {
+ this.stop();
+ }
+});
+
+var AnimatedIcon = GObject.registerClass(
+class AnimatedIcon extends Animation {
+ _init(file, size) {
+ super._init(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
+ }
+});
+
+var Spinner = GObject.registerClass(
+class Spinner extends AnimatedIcon {
+ _init(size, params) {
+ params = Params.parse(params, {
+ animate: false,
+ hideOnStop: false,
+ });
+ let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
+ super._init(file, size);
+
+ this.opacity = 0;
+ this._animate = params.animate;
+ this._hideOnStop = params.hideOnStop;
+ this.visible = !this._hideOnStop;
+ }
+
+ _onDestroy() {
+ this._animate = false;
+ super._onDestroy();
+ }
+
+ play() {
+ this.remove_all_transitions();
+ this.show();
+
+ if (this._animate) {
+ super.play();
+ this.ease({
+ opacity: 255,
+ delay: SPINNER_ANIMATION_DELAY,
+ duration: SPINNER_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.LINEAR,
+ });
+ } else {
+ this.opacity = 255;
+ super.play();
+ }
+ }
+
+ stop() {
+ this.remove_all_transitions();
+
+ if (this._animate) {
+ this.ease({
+ opacity: 0,
+ duration: SPINNER_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: () => {
+ super.stop();
+ if (this._hideOnStop)
+ this.hide();
+ },
+ });
+ } else {
+ this.opacity = 0;
+ super.stop();
+
+ if (this._hideOnStop)
+ this.hide();
+ }
+ }
+});
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
new file mode 100644
index 0000000..61fd0bc
--- /dev/null
+++ b/js/ui/appDisplay.js
@@ -0,0 +1,3273 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported AppDisplay, AppSearchProvider */
+
+const {
+ Clutter, Gio, GLib, GObject, Graphene, Pango, Shell, St,
+} = imports.gi;
+
+const AppFavorites = imports.ui.appFavorites;
+const { AppMenu } = imports.ui.appMenu;
+const BoxPointer = imports.ui.boxpointer;
+const DND = imports.ui.dnd;
+const GrabHelper = imports.ui.grabHelper;
+const IconGrid = imports.ui.iconGrid;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const PageIndicators = imports.ui.pageIndicators;
+const ParentalControlsManager = imports.misc.parentalControlsManager;
+const PopupMenu = imports.ui.popupMenu;
+const Search = imports.ui.search;
+const SwipeTracker = imports.ui.swipeTracker;
+const Params = imports.misc.params;
+const SystemActions = imports.misc.systemActions;
+
+var MENU_POPUP_TIMEOUT = 600;
+var POPDOWN_DIALOG_TIMEOUT = 500;
+
+var FOLDER_SUBICON_FRACTION = .4;
+
+var VIEWS_SWITCH_TIME = 400;
+var VIEWS_SWITCH_ANIMATION_DELAY = 100;
+
+var SCROLL_TIMEOUT_TIME = 150;
+
+var APP_ICON_SCALE_IN_TIME = 500;
+var APP_ICON_SCALE_IN_DELAY = 700;
+
+var APP_ICON_TITLE_EXPAND_TIME = 200;
+var APP_ICON_TITLE_COLLAPSE_TIME = 100;
+
+const FOLDER_DIALOG_ANIMATION_TIME = 200;
+
+const PAGE_PREVIEW_ANIMATION_TIME = 150;
+const PAGE_INDICATOR_FADE_TIME = 200;
+const PAGE_PREVIEW_RATIO = 0.20;
+
+const OVERSHOOT_THRESHOLD = 20;
+const OVERSHOOT_TIMEOUT = 1000;
+
+const DELAYED_MOVE_TIMEOUT = 200;
+
+const DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x000000cc);
+const DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000055);
+
+const DEFAULT_FOLDERS = {
+ 'Utilities': {
+ name: 'X-GNOME-Utilities.directory',
+ categories: ['X-GNOME-Utilities'],
+ apps: [
+ 'gnome-abrt.desktop',
+ 'gnome-system-log.desktop',
+ 'nm-connection-editor.desktop',
+ 'org.gnome.baobab.desktop',
+ 'org.gnome.Connections.desktop',
+ 'org.gnome.DejaDup.desktop',
+ 'org.gnome.Dictionary.desktop',
+ 'org.gnome.DiskUtility.desktop',
+ 'org.gnome.eog.desktop',
+ 'org.gnome.Evince.desktop',
+ 'org.gnome.FileRoller.desktop',
+ 'org.gnome.fonts.desktop',
+ 'org.gnome.seahorse.Application.desktop',
+ 'org.gnome.tweaks.desktop',
+ 'org.gnome.Usage.desktop',
+ 'vinagre.desktop',
+ ],
+ },
+ 'YaST': {
+ name: 'suse-yast.directory',
+ categories: ['X-SuSE-YaST'],
+ },
+};
+
+function _getCategories(info) {
+ let categoriesStr = info.get_categories();
+ if (!categoriesStr)
+ return [];
+ return categoriesStr.split(';');
+}
+
+function _listsIntersect(a, b) {
+ for (let itemA of a) {
+ if (b.includes(itemA))
+ return true;
+ }
+ return false;
+}
+
+function _getFolderName(folder) {
+ let name = folder.get_string('name');
+
+ if (folder.get_boolean('translate')) {
+ let translated = Shell.util_get_translated_folder_name(name);
+ if (translated !== null)
+ return translated;
+ }
+
+ return name;
+}
+
+function _getViewFromIcon(icon) {
+ for (let parent = icon.get_parent(); parent; parent = parent.get_parent()) {
+ if (parent instanceof BaseAppView)
+ return parent;
+ }
+ return null;
+}
+
+function _findBestFolderName(apps) {
+ let appInfos = apps.map(app => app.get_app_info());
+
+ let categoryCounter = {};
+ let commonCategories = [];
+
+ appInfos.reduce((categories, appInfo) => {
+ for (let category of _getCategories(appInfo)) {
+ if (!(category in categoryCounter))
+ categoryCounter[category] = 0;
+
+ categoryCounter[category] += 1;
+
+ // If a category is present in all apps, its counter will
+ // reach appInfos.length
+ if (category.length > 0 &&
+ categoryCounter[category] == appInfos.length)
+ categories.push(category);
+ }
+ return categories;
+ }, commonCategories);
+
+ for (let category of commonCategories) {
+ const directory = `${category}.directory`;
+ const translated = Shell.util_get_translated_folder_name(directory);
+ if (translated !== null)
+ return translated;
+ }
+
+ return null;
+}
+
+const AppGrid = GObject.registerClass({
+ Properties: {
+ 'indicators-padding': GObject.ParamSpec.boxed('indicators-padding',
+ 'Indicators padding', 'Indicators padding',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Margin.$gtype),
+ },
+}, class AppGrid extends IconGrid.IconGrid {
+ _init(layoutParams) {
+ super._init(layoutParams);
+
+ this._indicatorsPadding = new Clutter.Margin();
+ }
+
+ _updatePadding() {
+ const node = this.get_theme_node();
+ const {rowSpacing, columnSpacing} = this.layoutManager;
+
+ const padding = this._indicatorsPadding.copy();
+ padding.left += rowSpacing;
+ padding.right += rowSpacing;
+ padding.top += columnSpacing;
+ padding.bottom += columnSpacing;
+ ['top', 'right', 'bottom', 'left'].forEach(side => {
+ padding[side] += node.get_length(`page-padding-${side}`);
+ });
+
+ this.layoutManager.pagePadding = padding;
+ }
+
+ vfunc_style_changed() {
+ super.vfunc_style_changed();
+ this._updatePadding();
+ }
+
+ get indicatorsPadding() {
+ return this._indicatorsPadding;
+ }
+
+ set indicatorsPadding(v) {
+ if (this._indicatorsPadding === v)
+ return;
+
+ this._indicatorsPadding = v ? v : new Clutter.Margin();
+ this._updatePadding();
+ }
+});
+
+const BaseAppViewGridLayout = GObject.registerClass(
+class BaseAppViewGridLayout extends Clutter.BinLayout {
+ _init(grid, scrollView, nextPageIndicator, nextPageArrow,
+ previousPageIndicator, previousPageArrow) {
+ if (!(grid instanceof AppGrid))
+ throw new Error('Grid must be an AppGrid subclass');
+
+ super._init();
+
+ this._grid = grid;
+ this._scrollView = scrollView;
+ this._previousPageIndicator = previousPageIndicator;
+ this._previousPageArrow = previousPageArrow;
+ this._nextPageIndicator = nextPageIndicator;
+ this._nextPageArrow = nextPageArrow;
+
+ grid.connect('pages-changed', () => this._syncPageIndicatorsVisibility());
+
+ this._pageIndicatorsAdjustment = new St.Adjustment({
+ lower: 0,
+ upper: 1,
+ });
+ this._pageIndicatorsAdjustment.connect(
+ 'notify::value', () => this._syncPageIndicators());
+
+ this._showIndicators = false;
+ this._currentPage = 0;
+ this._pageWidth = 0;
+ }
+
+ _getIndicatorsWidth(box) {
+ const [width, height] = box.get_size();
+ const arrows = [
+ this._nextPageArrow,
+ this._previousPageArrow,
+ ];
+
+ const minArrowsWidth = arrows.reduce(
+ (previousWidth, accessory) => {
+ const [min] = accessory.get_preferred_width(height);
+ return Math.max(previousWidth, min);
+ }, 0);
+
+ const idealIndicatorWidth = (width * PAGE_PREVIEW_RATIO) / 2;
+
+ return Math.max(idealIndicatorWidth, minArrowsWidth);
+ }
+
+ _syncPageIndicatorsVisibility(animate = true) {
+ const previousIndicatorsVisible =
+ this._currentPage > 0 && this._showIndicators;
+
+ if (previousIndicatorsVisible)
+ this._previousPageIndicator.show();
+
+ this._previousPageIndicator.ease({
+ opacity: previousIndicatorsVisible ? 255 : 0,
+ duration: animate ? PAGE_INDICATOR_FADE_TIME : 0,
+ onComplete: () => {
+ if (!previousIndicatorsVisible)
+ this._previousPageIndicator.hide();
+ },
+ });
+
+ const previousArrowVisible =
+ this._currentPage > 0 && !previousIndicatorsVisible;
+
+ if (previousArrowVisible)
+ this._previousPageArrow.show();
+
+ this._previousPageArrow.ease({
+ opacity: previousArrowVisible ? 255 : 0,
+ duration: animate ? PAGE_INDICATOR_FADE_TIME : 0,
+ onComplete: () => {
+ if (!previousArrowVisible)
+ this._previousPageArrow.hide();
+ },
+ });
+
+ // Always show the next page indicator to allow dropping
+ // icons into new pages
+ const {allowIncompletePages, nPages} = this._grid.layoutManager;
+ const nextIndicatorsVisible = this._showIndicators &&
+ (allowIncompletePages ? true : this._currentPage < nPages - 1);
+
+ if (nextIndicatorsVisible)
+ this._nextPageIndicator.show();
+
+ this._nextPageIndicator.ease({
+ opacity: nextIndicatorsVisible ? 255 : 0,
+ duration: animate ? PAGE_INDICATOR_FADE_TIME : 0,
+ onComplete: () => {
+ if (!nextIndicatorsVisible)
+ this._nextPageIndicator.hide();
+ },
+ });
+
+ const nextArrowVisible =
+ this._currentPage < nPages - 1 &&
+ !nextIndicatorsVisible;
+
+ if (nextArrowVisible)
+ this._nextPageArrow.show();
+
+ this._nextPageArrow.ease({
+ opacity: nextArrowVisible ? 255 : 0,
+ duration: animate ? PAGE_INDICATOR_FADE_TIME : 0,
+ onComplete: () => {
+ if (!nextArrowVisible)
+ this._nextPageArrow.hide();
+ },
+ });
+ }
+
+ _getEndIcon(icons) {
+ const {columnsPerPage} = this._grid.layoutManager;
+ const index = Math.min(icons.length, columnsPerPage);
+ return icons[Math.max(index - 1, 0)];
+ }
+
+ _translatePreviousPageIcons(value, ltr) {
+ if (this._currentPage === 0)
+ return;
+
+ const previousPage = this._currentPage - 1;
+ const icons = this._grid.getItemsAtPage(previousPage).filter(i => i.visible);
+ if (icons.length === 0)
+ return;
+
+ const {left, right} = this._grid.indicatorsPadding;
+ const {columnSpacing} = this._grid.layoutManager;
+ const endIcon = this._getEndIcon(icons);
+ let iconOffset;
+
+ if (ltr) {
+ const currentPageOffset = this._pageWidth * this._currentPage;
+ iconOffset = currentPageOffset - endIcon.allocation.x2 + left - columnSpacing;
+ } else {
+ const rtlPage = this._grid.nPages - previousPage - 1;
+ const pageOffset = this._pageWidth * rtlPage;
+ iconOffset = pageOffset - endIcon.allocation.x1 - right + columnSpacing;
+ }
+
+ for (const icon of icons)
+ icon.translationX = iconOffset * value;
+ }
+
+ _translateNextPageIcons(value, ltr) {
+ if (this._currentPage >= this._grid.nPages - 1)
+ return;
+
+ const nextPage = this._currentPage + 1;
+ const icons = this._grid.getItemsAtPage(nextPage).filter(i => i.visible);
+ if (icons.length === 0)
+ return;
+
+ const {left, right} = this._grid.indicatorsPadding;
+ const {columnSpacing} = this._grid.layoutManager;
+ let iconOffset;
+
+ if (ltr) {
+ const pageOffset = this._pageWidth * nextPage;
+ iconOffset = pageOffset - icons[0].allocation.x1 - right + columnSpacing;
+ } else {
+ const rtlPage = this._grid.nPages - this._currentPage - 1;
+ const currentPageOffset = this._pageWidth * rtlPage;
+ iconOffset = currentPageOffset - icons[0].allocation.x2 + left - columnSpacing;
+ }
+
+ for (const icon of icons)
+ icon.translationX = iconOffset * value;
+ }
+
+ _syncPageIndicators() {
+ if (!this._container)
+ return;
+
+ const {value} = this._pageIndicatorsAdjustment;
+
+ const ltr = this._container.get_text_direction() !== Clutter.TextDirection.RTL;
+ const {left, right} = this._grid.indicatorsPadding;
+ const leftIndicatorOffset = -left * (1 - value);
+ const rightIndicatorOffset = right * (1 - value);
+
+ this._previousPageIndicator.translationX =
+ ltr ? leftIndicatorOffset : rightIndicatorOffset;
+ this._nextPageIndicator.translationX =
+ ltr ? rightIndicatorOffset : leftIndicatorOffset;
+
+ const leftArrowOffset = -left * value;
+ const rightArrowOffset = right * value;
+
+ this._previousPageArrow.translationX =
+ ltr ? leftArrowOffset : rightArrowOffset;
+ this._nextPageArrow.translationX =
+ ltr ? rightArrowOffset : leftArrowOffset;
+
+ // Page icons
+ this._translatePreviousPageIcons(value, ltr);
+ this._translateNextPageIcons(value, ltr);
+
+ if (this._grid.nPages > 0) {
+ this._grid.getItemsAtPage(this._currentPage).forEach(icon => {
+ icon.translationX = 0;
+ });
+ }
+ }
+
+ vfunc_set_container(container) {
+ this._container = container;
+ this._pageIndicatorsAdjustment.actor = container;
+ this._syncPageIndicators();
+ }
+
+ vfunc_allocate(container, box) {
+ const ltr = container.get_text_direction() !== Clutter.TextDirection.RTL;
+ const indicatorsWidth = this._getIndicatorsWidth(box);
+
+ this._grid.indicatorsPadding = new Clutter.Margin({
+ left: indicatorsWidth,
+ right: indicatorsWidth,
+ });
+
+ this._scrollView.allocate(box);
+
+ const leftBox = box.copy();
+ leftBox.x2 = leftBox.x1 + indicatorsWidth;
+
+ const rightBox = box.copy();
+ rightBox.x1 = rightBox.x2 - indicatorsWidth;
+
+ this._previousPageIndicator.allocate(ltr ? leftBox : rightBox);
+ this._previousPageArrow.allocate_align_fill(ltr ? leftBox : rightBox,
+ 0.5, 0.5, false, false);
+ this._nextPageIndicator.allocate(ltr ? rightBox : leftBox);
+ this._nextPageArrow.allocate_align_fill(ltr ? rightBox : leftBox,
+ 0.5, 0.5, false, false);
+
+ this._pageWidth = box.get_width();
+ }
+
+ goToPage(page, animate = true) {
+ if (this._currentPage === page)
+ return;
+
+ this._currentPage = page;
+ this._syncPageIndicatorsVisibility(animate);
+ this._syncPageIndicators();
+ }
+
+ showPageIndicators() {
+ if (this._showIndicators)
+ return;
+
+ this._pageIndicatorsAdjustment.ease(1, {
+ duration: PAGE_PREVIEW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ });
+
+ this._grid.clipToView = false;
+ this._showIndicators = true;
+ this._syncPageIndicatorsVisibility();
+ }
+
+ hidePageIndicators() {
+ if (!this._showIndicators)
+ return;
+
+ this._pageIndicatorsAdjustment.ease(0, {
+ duration: PAGE_PREVIEW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ onComplete: () => {
+ this._grid.clipToView = true;
+ },
+ });
+
+ this._showIndicators = false;
+ this._syncPageIndicatorsVisibility();
+ }
+});
+
+var BaseAppView = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+ Properties: {
+ 'gesture-modes': GObject.ParamSpec.flags(
+ 'gesture-modes', 'gesture-modes', 'gesture-modes',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Shell.ActionMode, Shell.ActionMode.OVERVIEW),
+ },
+ Signals: {
+ 'view-loaded': {},
+ },
+}, class BaseAppView extends St.Widget {
+ _init(params = {}) {
+ super._init(params);
+
+ this._grid = this._createGrid();
+ this._grid._delegate = this;
+ // Standard hack for ClutterBinLayout
+ this._grid.x_expand = true;
+ this._grid.connect('pages-changed', () => {
+ this.goToPage(this._grid.currentPage);
+ this._pageIndicators.setNPages(this._grid.nPages);
+ this._pageIndicators.setCurrentPosition(this._grid.currentPage);
+ });
+
+ // Scroll View
+ this._scrollView = new St.ScrollView({
+ style_class: 'apps-scroll-view',
+ clip_to_allocation: true,
+ x_expand: true,
+ y_expand: true,
+ reactive: true,
+ enable_mouse_scrolling: false,
+ });
+ this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER);
+
+ this._canScroll = true; // limiting scrolling speed
+ this._scrollTimeoutId = 0;
+ this._scrollView.connect('scroll-event', this._onScroll.bind(this));
+
+ this._scrollView.add_actor(this._grid);
+
+ const scroll = this._scrollView.hscroll;
+ this._adjustment = scroll.adjustment;
+ this._adjustment.connect('notify::value', adj => {
+ const value = adj.value / adj.page_size;
+ this._pageIndicators.setCurrentPosition(value);
+ });
+
+ // Page Indicators
+ this._pageIndicators =
+ new PageIndicators.PageIndicators(Clutter.Orientation.HORIZONTAL);
+
+ this._pageIndicators.y_expand = false;
+ this._pageIndicators.connect('page-activated',
+ (indicators, pageIndex) => {
+ this.goToPage(pageIndex);
+ });
+ this._pageIndicators.connect('scroll-event', (actor, event) => {
+ this._scrollView.event(event, false);
+ });
+
+ // Navigation indicators
+ this._nextPageIndicator = new St.Widget({
+ style_class: 'page-navigation-hint next',
+ opacity: 0,
+ visible: false,
+ reactive: true,
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.FILL,
+ y_align: Clutter.ActorAlign.FILL,
+ });
+
+ this._prevPageIndicator = new St.Widget({
+ style_class: 'page-navigation-hint previous',
+ opacity: 0,
+ visible: false,
+ reactive: true,
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.FILL,
+ y_align: Clutter.ActorAlign.FILL,
+ });
+
+ // Next/prev page arrows
+ const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+ this._nextPageArrow = new St.Button({
+ style_class: 'page-navigation-arrow',
+ icon_name: rtl
+ ? 'carousel-arrow-previous-symbolic'
+ : 'carousel-arrow-next-symbolic',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._nextPageArrow.connect('clicked',
+ () => this.goToPage(this._grid.currentPage + 1));
+
+ this._prevPageArrow = new St.Button({
+ style_class: 'page-navigation-arrow',
+ icon_name: rtl
+ ? 'carousel-arrow-next-symbolic'
+ : 'carousel-arrow-previous-symbolic',
+ opacity: 0,
+ visible: false,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._prevPageArrow.connect('clicked',
+ () => this.goToPage(this._grid.currentPage - 1));
+
+ const scrollContainer = new St.Widget({
+ clip_to_allocation: true,
+ y_expand: true,
+ });
+ scrollContainer.add_child(this._scrollView);
+ scrollContainer.add_child(this._prevPageIndicator);
+ scrollContainer.add_child(this._nextPageIndicator);
+ scrollContainer.add_child(this._nextPageArrow);
+ scrollContainer.add_child(this._prevPageArrow);
+ scrollContainer.layoutManager = new BaseAppViewGridLayout(
+ this._grid,
+ this._scrollView,
+ this._nextPageIndicator,
+ this._nextPageArrow,
+ this._prevPageIndicator,
+ this._prevPageArrow);
+ this._appGridLayout = scrollContainer.layoutManager;
+ scrollContainer._delegate = this;
+
+ this._box = new St.BoxLayout({
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ });
+ this._box.add_child(scrollContainer);
+ this._box.add_child(this._pageIndicators);
+
+ // Swipe
+ this._swipeTracker = new SwipeTracker.SwipeTracker(this._scrollView,
+ Clutter.Orientation.HORIZONTAL, this.gestureModes);
+ this._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL;
+ this._swipeTracker.connect('begin', this._swipeBegin.bind(this));
+ this._swipeTracker.connect('update', this._swipeUpdate.bind(this));
+ this._swipeTracker.connect('end', this._swipeEnd.bind(this));
+
+ this._orientation = Clutter.Orientation.HORIZONTAL;
+
+ this._items = new Map();
+ this._orderedItems = [];
+
+ // Filter the apps through the user’s parental controls.
+ this._parentalControlsManager = ParentalControlsManager.getDefault();
+ this._parentalControlsManager.connectObject('app-filter-changed',
+ () => this._redisplay(), this);
+
+ // Don't duplicate favorites
+ this._appFavorites = AppFavorites.getAppFavorites();
+ this._appFavorites.connectObject('changed',
+ () => this._redisplay(), this);
+
+ // Drag n' Drop
+ this._overshootTimeoutId = 0;
+ this._delayedMoveData = null;
+
+ this._dragBeginId = 0;
+ this._dragEndId = 0;
+ this._dragCancelledId = 0;
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._previewedPages = new Map();
+ }
+
+ _onDestroy() {
+ if (this._swipeTracker) {
+ this._swipeTracker.destroy();
+ delete this._swipeTracker;
+ }
+
+ this._removeDelayedMove();
+ this._disconnectDnD();
+ }
+
+ _createGrid() {
+ return new AppGrid({allow_incomplete_pages: true});
+ }
+
+ _onScroll(actor, event) {
+ if (this._swipeTracker.canHandleScrollEvent(event))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (!this._canScroll)
+ return Clutter.EVENT_STOP;
+
+ const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+ const vertical = this._orientation === Clutter.Orientation.VERTICAL;
+
+ let nextPage = this._grid.currentPage;
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP:
+ nextPage -= 1;
+ break;
+
+ case Clutter.ScrollDirection.DOWN:
+ nextPage += 1;
+ break;
+
+ case Clutter.ScrollDirection.LEFT:
+ if (vertical)
+ return Clutter.EVENT_STOP;
+ nextPage += rtl ? 1 : -1;
+ break;
+
+ case Clutter.ScrollDirection.RIGHT:
+ if (vertical)
+ return Clutter.EVENT_STOP;
+ nextPage += rtl ? -1 : 1;
+ break;
+
+ default:
+ return Clutter.EVENT_STOP;
+ }
+
+ this.goToPage(nextPage);
+
+ this._canScroll = false;
+ this._scrollTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ SCROLL_TIMEOUT_TIME, () => {
+ this._canScroll = true;
+ this._scrollTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _swipeBegin(tracker, monitor) {
+ if (monitor !== Main.layoutManager.primaryIndex)
+ return;
+
+ if (this._dragFocus) {
+ this._dragFocus.cancelActions();
+ this._dragFocus = null;
+ }
+
+ const adjustment = this._adjustment;
+ adjustment.remove_transition('value');
+
+ const progress = adjustment.value / adjustment.page_size;
+ const points = Array.from({ length: this._grid.nPages }, (v, i) => i);
+ const size = tracker.orientation === Clutter.Orientation.VERTICAL
+ ? this._grid.allocation.get_height() : this._grid.allocation.get_width();
+
+ tracker.confirmSwipe(size, points, progress, Math.round(progress));
+ }
+
+ _swipeUpdate(tracker, progress) {
+ const adjustment = this._adjustment;
+ adjustment.value = progress * adjustment.page_size;
+ }
+
+ _swipeEnd(tracker, duration, endProgress) {
+ const adjustment = this._adjustment;
+ const value = endProgress * adjustment.page_size;
+
+ adjustment.ease(value, {
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ duration,
+ onComplete: () => this.goToPage(endProgress, false),
+ });
+ }
+
+ _connectDnD() {
+ this._dragBeginId =
+ Main.overview.connect('item-drag-begin', this._onDragBegin.bind(this));
+ this._dragEndId =
+ Main.overview.connect('item-drag-end', this._onDragEnd.bind(this));
+ this._dragCancelledId =
+ Main.overview.connect('item-drag-cancelled', this._onDragCancelled.bind(this));
+ }
+
+ _disconnectDnD() {
+ if (this._dragBeginId > 0) {
+ Main.overview.disconnect(this._dragBeginId);
+ this._dragBeginId = 0;
+ }
+
+ if (this._dragEndId > 0) {
+ Main.overview.disconnect(this._dragEndId);
+ this._dragEndId = 0;
+ }
+
+ if (this._dragCancelledId > 0) {
+ Main.overview.disconnect(this._dragCancelledId);
+ this._dragCancelledId = 0;
+ }
+
+ if (this._dragMonitor) {
+ DND.removeDragMonitor(this._dragMonitor);
+ this._dragMonitor = null;
+ }
+ }
+
+ _maybeMoveItem(dragEvent) {
+ const [success, x, y] =
+ this._grid.transform_stage_point(dragEvent.x, dragEvent.y);
+
+ if (!success)
+ return;
+
+ const { source } = dragEvent;
+ const [page, position, dragLocation] =
+ this._getDropTarget(x, y, source);
+ const item = position !== -1
+ ? this._grid.getItemAt(page, position) : null;
+
+
+ // Dragging over invalid parts of the grid cancels the timeout
+ if (item === source ||
+ dragLocation === IconGrid.DragLocation.INVALID ||
+ dragLocation === IconGrid.DragLocation.ON_ICON) {
+ this._removeDelayedMove();
+ return;
+ }
+
+ if (!this._delayedMoveData ||
+ this._delayedMoveData.page !== page ||
+ this._delayedMoveData.position !== position) {
+ // Update the item with a small delay
+ this._removeDelayedMove();
+ this._delayedMoveData = {
+ page,
+ position,
+ source,
+ destroyId: source.connect('destroy', () => this._removeDelayedMove()),
+ timeoutId: GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ DELAYED_MOVE_TIMEOUT, () => {
+ this._moveItem(source, page, position);
+ this._delayedMoveData.timeoutId = 0;
+ this._removeDelayedMove();
+ return GLib.SOURCE_REMOVE;
+ }),
+ };
+ }
+ }
+
+ _removeDelayedMove() {
+ if (!this._delayedMoveData)
+ return;
+
+ const { source, destroyId, timeoutId } = this._delayedMoveData;
+
+ if (timeoutId > 0)
+ GLib.source_remove(timeoutId);
+
+ if (destroyId > 0)
+ source.disconnect(destroyId);
+
+ this._delayedMoveData = null;
+ }
+
+ _resetOvershoot() {
+ if (this._overshootTimeoutId)
+ GLib.source_remove(this._overshootTimeoutId);
+ this._overshootTimeoutId = 0;
+ }
+
+ _dragWithinOvershootRegion(dragEvent) {
+ const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+ const {x, y, targetActor: indicator} = dragEvent;
+ const [indicatorX, indicatorY] = indicator.get_transformed_position();
+ const [indicatorWidth, indicatorHeight] = indicator.get_transformed_size();
+
+ let overshootX = indicatorX;
+ if (indicator === this._nextPageIndicator || rtl)
+ overshootX += indicatorWidth - OVERSHOOT_THRESHOLD;
+
+ const overshootBox = new Clutter.ActorBox();
+ overshootBox.set_origin(overshootX, indicatorY);
+ overshootBox.set_size(OVERSHOOT_THRESHOLD, indicatorHeight);
+
+ return overshootBox.contains(x, y);
+ }
+
+ _handleDragOvershoot(dragEvent) {
+ // Already animating
+ if (this._adjustment.get_transition('value') !== null)
+ return;
+
+ const {targetActor} = dragEvent;
+
+ if (targetActor !== this._prevPageIndicator &&
+ targetActor !== this._nextPageIndicator) {
+ this._resetOvershoot();
+ return;
+ }
+
+ if (this._overshootTimeoutId > 0)
+ return;
+
+ let targetPage;
+ if (dragEvent.targetActor === this._prevPageIndicator)
+ targetPage = this._grid.currentPage - 1;
+ else
+ targetPage = this._grid.currentPage + 1;
+
+ if (targetPage < 0 || targetPage >= this._grid.nPages)
+ return; // don't go beyond first/last page
+
+ // If dragging over the drag overshoot threshold region, immediately
+ // switch pages
+ if (this._dragWithinOvershootRegion(dragEvent)) {
+ this._resetOvershoot();
+ this.goToPage(targetPage);
+ }
+
+ this._overshootTimeoutId =
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, OVERSHOOT_TIMEOUT, () => {
+ this._resetOvershoot();
+ this.goToPage(targetPage);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._overshootTimeoutId,
+ '[gnome-shell] this._overshootTimeoutId');
+ }
+
+ _onDragBegin() {
+ this._dragMonitor = {
+ dragMotion: this._onDragMotion.bind(this),
+ dragDrop: this._onDragDrop.bind(this),
+ };
+ DND.addDragMonitor(this._dragMonitor);
+ this._appGridLayout.showPageIndicators();
+ this._dragFocus = null;
+ this._swipeTracker.enabled = false;
+ }
+
+ _onDragMotion(dragEvent) {
+ if (!(dragEvent.source instanceof AppViewItem))
+ return DND.DragMotionResult.CONTINUE;
+
+ const appIcon = dragEvent.source;
+
+ // Handle the drag overshoot. When dragging to above the
+ // icon grid, move to the page above; when dragging below,
+ // move to the page below.
+ if (appIcon instanceof AppViewItem)
+ this._handleDragOvershoot(dragEvent);
+
+ this._maybeMoveItem(dragEvent);
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ _onDragDrop(dropEvent) {
+ // Because acceptDrop() does not receive the target actor, store it
+ // here and use this value in the acceptDrop() implementation below.
+ this._dropTarget = dropEvent.targetActor;
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ _onDragEnd() {
+ if (this._dragMonitor) {
+ DND.removeDragMonitor(this._dragMonitor);
+ this._dragMonitor = null;
+ }
+
+ this._resetOvershoot();
+ this._appGridLayout.hidePageIndicators();
+ this._swipeTracker.enabled = true;
+ }
+
+ _onDragCancelled() {
+ // At this point, the positions aren't stored yet, thus _redisplay()
+ // will move all items to their original positions
+ this._redisplay();
+ this._appGridLayout.hidePageIndicators();
+ this._swipeTracker.enabled = true;
+ }
+
+ _canAccept(source) {
+ return source instanceof AppViewItem;
+ }
+
+ handleDragOver(source) {
+ if (!this._canAccept(source))
+ return DND.DragMotionResult.NO_DROP;
+
+ return DND.DragMotionResult.MOVE_DROP;
+ }
+
+ acceptDrop(source) {
+ const dropTarget = this._dropTarget;
+ delete this._dropTarget;
+
+ if (!this._canAccept(source))
+ return false;
+
+ if (dropTarget === this._prevPageIndicator ||
+ dropTarget === this._nextPageIndicator) {
+ const increment = dropTarget === this._prevPageIndicator ? -1 : 1;
+ const { currentPage, nPages } = this._grid;
+ const page = Math.min(currentPage + increment, nPages);
+ const position = page < nPages ? -1 : 0;
+
+ this._moveItem(source, page, position);
+ this.goToPage(page);
+ } else if (this._delayedMoveData) {
+ // Dropped before the icon was moved
+ const { page, position } = this._delayedMoveData;
+
+ this._moveItem(source, page, position);
+ this._removeDelayedMove();
+ }
+
+ return true;
+ }
+
+ _findBestPageToAppend(startPage = 1) {
+ for (let i = startPage; i < this._grid.nPages; i++) {
+ const pageItems =
+ this._grid.getItemsAtPage(i).filter(c => c.visible);
+
+ if (pageItems.length < this._grid.itemsPerPage)
+ return i;
+ }
+
+ return -1;
+ }
+
+ _getLinearPosition(page, position) {
+ let itemIndex = 0;
+
+ if (this._grid.nPages > 0) {
+ const realPage = page === -1 ? this._grid.nPages - 1 : page;
+
+ itemIndex = position === -1
+ ? this._grid.getItemsAtPage(realPage).filter(c => c.visible).length - 1
+ : position;
+
+ for (let i = 0; i < realPage; i++) {
+ const pageItems = this._grid.getItemsAtPage(i).filter(c => c.visible);
+ itemIndex += pageItems.length;
+ }
+ }
+
+ return itemIndex;
+ }
+
+ _addItem(item, page, position) {
+ // Append icons to the first page with empty slot, starting from
+ // the second page
+ if (this._grid.nPages > 1 && page === -1 && position === -1)
+ page = this._findBestPageToAppend();
+
+ const itemIndex = this._getLinearPosition(page, position);
+
+ this._orderedItems.splice(itemIndex, 0, item);
+ this._items.set(item.id, item);
+ this._grid.addItem(item, page, position);
+ }
+
+ _removeItem(item) {
+ const iconIndex = this._orderedItems.indexOf(item);
+
+ this._orderedItems.splice(iconIndex, 1);
+ this._items.delete(item.id);
+ this._grid.removeItem(item);
+ }
+
+ _getItemPosition(item) {
+ const { itemsPerPage } = this._grid;
+
+ let iconIndex = this._orderedItems.indexOf(item);
+ if (iconIndex === -1)
+ iconIndex = this._orderedItems.length - 1;
+
+ const page = Math.floor(iconIndex / itemsPerPage);
+ const position = iconIndex % itemsPerPage;
+
+ return [page, position];
+ }
+
+ _redisplay() {
+ let oldApps = this._orderedItems.slice();
+ let oldAppIds = oldApps.map(icon => icon.id);
+
+ let newApps = this._loadApps().sort(this._compareItems.bind(this));
+ let newAppIds = newApps.map(icon => icon.id);
+
+ let addedApps = newApps.filter(icon => !oldAppIds.includes(icon.id));
+ let removedApps = oldApps.filter(icon => !newAppIds.includes(icon.id));
+
+ // Remove old app icons
+ removedApps.forEach(icon => {
+ this._removeItem(icon);
+ icon.destroy();
+ });
+
+ // Add new app icons, or move existing ones
+ newApps.forEach(icon => {
+ const [page, position] = this._getItemPosition(icon);
+ if (addedApps.includes(icon))
+ this._addItem(icon, page, position);
+ else if (page !== -1 && position !== -1)
+ this._moveItem(icon, page, position);
+ });
+
+ this.emit('view-loaded');
+ }
+
+ getAllItems() {
+ return this._orderedItems;
+ }
+
+ _compareItems(a, b) {
+ return a.name.localeCompare(b.name);
+ }
+
+ _selectAppInternal(id) {
+ if (this._items.has(id))
+ this._items.get(id).navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ else
+ log(`No such application ${id}`);
+ }
+
+ selectApp(id) {
+ if (this._items.has(id)) {
+ let item = this._items.get(id);
+
+ if (item.mapped) {
+ this._selectAppInternal(id);
+ } else {
+ // Need to wait until the view is mapped
+ let signalId = item.connect('notify::mapped', actor => {
+ if (actor.mapped) {
+ actor.disconnect(signalId);
+ this._selectAppInternal(id);
+ }
+ });
+ }
+ } else {
+ // Need to wait until the view is built
+ let signalId = this.connect('view-loaded', () => {
+ this.disconnect(signalId);
+ this.selectApp(id);
+ });
+ }
+ }
+
+ _getDropTarget(x, y, source) {
+ const { currentPage } = this._grid;
+
+ let [item, dragLocation] = this._grid.getDropTarget(x, y);
+
+ const [sourcePage, sourcePosition] = this._grid.getItemPosition(source);
+ const targetPage = currentPage;
+ let targetPosition = item
+ ? this._grid.getItemPosition(item)[1] : -1;
+
+ // In case we're hovering over the edge of an item but the
+ // reflow will happen in the opposite direction (the drag
+ // can't "naturally push the item away"), we instead set the
+ // drop target to the adjacent item that can be pushed away
+ // in the reflow-direction.
+ //
+ // We must avoid doing that if we're hovering over the first
+ // or last column though, in that case there is no adjacent
+ // icon we could push away.
+ if (dragLocation === IconGrid.DragLocation.START_EDGE &&
+ targetPosition > sourcePosition &&
+ targetPage === sourcePage) {
+ const nColumns = this._grid.layout_manager.columns_per_page;
+ const targetColumn = targetPosition % nColumns;
+
+ if (targetColumn > 0) {
+ targetPosition -= 1;
+ dragLocation = IconGrid.DragLocation.END_EDGE;
+ }
+ } else if (dragLocation === IconGrid.DragLocation.END_EDGE &&
+ (targetPosition < sourcePosition ||
+ targetPage !== sourcePage)) {
+ const nColumns = this._grid.layout_manager.columns_per_page;
+ const targetColumn = targetPosition % nColumns;
+
+ if (targetColumn < nColumns - 1) {
+ targetPosition += 1;
+ dragLocation = IconGrid.DragLocation.START_EDGE;
+ }
+ }
+
+ // Append to the page if dragging over empty area
+ if (dragLocation === IconGrid.DragLocation.EMPTY_SPACE) {
+ const pageItems =
+ this._grid.getItemsAtPage(currentPage).filter(c => c.visible);
+
+ targetPosition = pageItems.length;
+ }
+
+ return [targetPage, targetPosition, dragLocation];
+ }
+
+ _moveItem(item, newPage, newPosition) {
+ const [page, position] = this._grid.getItemPosition(item);
+ if (page === newPage && position === newPosition)
+ return;
+
+ // Update the _orderedItems array
+ let index = this._orderedItems.indexOf(item);
+ this._orderedItems.splice(index, 1);
+
+ index = this._getLinearPosition(newPage, newPosition);
+ this._orderedItems.splice(index, 0, item);
+
+ this._grid.moveItem(item, newPage, newPosition);
+ }
+
+ vfunc_map() {
+ this._swipeTracker.enabled = true;
+ this._connectDnD();
+ super.vfunc_map();
+ }
+
+ vfunc_unmap() {
+ if (this._swipeTracker)
+ this._swipeTracker.enabled = false;
+ this._disconnectDnD();
+ super.vfunc_unmap();
+ }
+
+ animateSwitch(animationDirection) {
+ this.remove_all_transitions();
+ this._grid.remove_all_transitions();
+
+ let params = {
+ duration: VIEWS_SWITCH_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ };
+ if (animationDirection == IconGrid.AnimationDirection.IN) {
+ this.show();
+ params.opacity = 255;
+ params.delay = VIEWS_SWITCH_ANIMATION_DELAY;
+ } else {
+ params.opacity = 0;
+ params.delay = 0;
+ params.onComplete = () => this.hide();
+ }
+
+ this._grid.ease(params);
+ }
+
+ goToPage(pageNumber, animate = true) {
+ pageNumber = Math.clamp(pageNumber, 0, Math.max(this._grid.nPages - 1, 0));
+
+ if (this._grid.currentPage === pageNumber)
+ return;
+
+ this._appGridLayout.goToPage(pageNumber, animate);
+ this._grid.goToPage(pageNumber, animate);
+ }
+
+ updateDragFocus(dragFocus) {
+ this._dragFocus = dragFocus;
+ }
+});
+
+var PageManager = GObject.registerClass({
+ Signals: { 'layout-changed': {} },
+}, class PageManager extends GObject.Object {
+ _init() {
+ super._init();
+
+ this._updatingPages = false;
+ this._loadPages();
+
+ global.settings.connect('changed::app-picker-layout',
+ this._loadPages.bind(this));
+ }
+
+ _loadPages() {
+ const layout = global.settings.get_value('app-picker-layout');
+ this._pages = layout.recursiveUnpack();
+ if (!this._updatingPages)
+ this.emit('layout-changed');
+ }
+
+ getAppPosition(appId) {
+ let position = -1;
+ let page = -1;
+
+ for (let pageIndex = 0; pageIndex < this._pages.length; pageIndex++) {
+ const pageData = this._pages[pageIndex];
+
+ if (appId in pageData) {
+ page = pageIndex;
+ position = pageData[appId].position;
+ break;
+ }
+ }
+
+ return [page, position];
+ }
+
+ set pages(p) {
+ const packedPages = [];
+
+ // Pack the icon properties as a GVariant
+ for (const page of p) {
+ const pageData = {};
+ for (const [appId, properties] of Object.entries(page))
+ pageData[appId] = new GLib.Variant('a{sv}', properties);
+ packedPages.push(pageData);
+ }
+
+ this._updatingPages = true;
+
+ const variant = new GLib.Variant('aa{sv}', packedPages);
+ global.settings.set_value('app-picker-layout', variant);
+
+ this._updatingPages = false;
+ }
+
+ get pages() {
+ return this._pages;
+ }
+});
+
+var AppDisplay = GObject.registerClass(
+class AppDisplay extends BaseAppView {
+ _init() {
+ super._init({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ });
+
+ this._pageManager = new PageManager();
+ this._pageManager.connect('layout-changed', () => this._redisplay());
+
+ this.add_child(this._box);
+
+ this._folderIcons = [];
+
+ this._currentDialog = null;
+ this._displayingDialog = false;
+
+ this._placeholder = null;
+
+ this._overviewHiddenId = 0;
+ this._redisplayWorkId = Main.initializeDeferredWork(this, () => {
+ this._redisplay();
+ if (this._overviewHiddenId === 0)
+ this._overviewHiddenId = Main.overview.connect('hidden', () => this.goToPage(0));
+ });
+
+ Shell.AppSystem.get_default().connect('installed-changed', () => {
+ Main.queueDeferredWork(this._redisplayWorkId);
+ });
+ this._folderSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' });
+ this._ensureDefaultFolders();
+ this._folderSettings.connect('changed::folder-children', () => {
+ Main.queueDeferredWork(this._redisplayWorkId);
+ });
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ if (this._scrollTimeoutId !== 0) {
+ GLib.source_remove(this._scrollTimeoutId);
+ this._scrollTimeoutId = 0;
+ }
+ }
+
+ vfunc_map() {
+ this._keyPressEventId =
+ global.stage.connect('key-press-event',
+ this._onKeyPressEvent.bind(this));
+ super.vfunc_map();
+ }
+
+ vfunc_unmap() {
+ if (this._keyPressEventId) {
+ global.stage.disconnect(this._keyPressEventId);
+ this._keyPressEventId = 0;
+ }
+ super.vfunc_unmap();
+ }
+
+ _redisplay() {
+ this._folderIcons.forEach(icon => {
+ icon.view._redisplay();
+ });
+
+ super._redisplay();
+ }
+
+ _savePages() {
+ const pages = [];
+
+ for (let i = 0; i < this._grid.nPages; i++) {
+ const pageItems =
+ this._grid.getItemsAtPage(i).filter(c => c.visible);
+ const pageData = {};
+
+ pageItems.forEach((item, index) => {
+ pageData[item.id] = {
+ position: GLib.Variant.new_int32(index),
+ };
+ });
+ pages.push(pageData);
+ }
+
+ this._pageManager.pages = pages;
+ }
+
+ _ensureDefaultFolders() {
+ if (this._folderSettings.get_strv('folder-children').length > 0)
+ return;
+
+ const folders = Object.keys(DEFAULT_FOLDERS);
+ this._folderSettings.set_strv('folder-children', folders);
+
+ const { path } = this._folderSettings;
+ for (const folder of folders) {
+ const { name, categories, apps } = DEFAULT_FOLDERS[folder];
+ const child = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.app-folders.folder',
+ path: `${path}folders/${folder}/`,
+ });
+ child.set_string('name', name);
+ child.set_boolean('translate', true);
+ child.set_strv('categories', categories);
+ if (apps)
+ child.set_strv('apps', apps);
+ }
+ }
+
+ _ensurePlaceholder(source) {
+ if (this._placeholder)
+ return;
+
+ const appSys = Shell.AppSystem.get_default();
+ const app = appSys.lookup_app(source.id);
+
+ const isDraggable =
+ global.settings.is_writable('favorite-apps') ||
+ global.settings.is_writable('app-picker-layout');
+
+ this._placeholder = new AppIcon(app, { isDraggable });
+ this._placeholder.connect('notify::pressed', icon => {
+ if (icon.pressed)
+ this.updateDragFocus(icon);
+ });
+ this._placeholder.scaleAndFade();
+ this._redisplay();
+ }
+
+ _removePlaceholder() {
+ if (this._placeholder) {
+ this._placeholder.undoScaleAndFade();
+ this._placeholder = null;
+ this._redisplay();
+ }
+ }
+
+ getAppInfos() {
+ return this._appInfoList;
+ }
+
+ _getItemPosition(item) {
+ if (item === this._placeholder) {
+ let [page, position] = this._grid.getItemPosition(item);
+
+ if (page === -1)
+ page = this._findBestPageToAppend(this._grid.currentPage);
+
+ return [page, position];
+ }
+
+ return this._pageManager.getAppPosition(item.id);
+ }
+
+ _compareItems(a, b) {
+ const [aPage, aPosition] = this._getItemPosition(a);
+ const [bPage, bPosition] = this._getItemPosition(b);
+
+ if (aPage === -1 && bPage === -1)
+ return a.name.localeCompare(b.name);
+ else if (aPage === -1)
+ return 1;
+ else if (bPage === -1)
+ return -1;
+
+ if (aPage !== bPage)
+ return aPage - bPage;
+
+ return aPosition - bPosition;
+ }
+
+ _loadApps() {
+ let appIcons = [];
+ this._appInfoList = Shell.AppSystem.get_default().get_installed().filter(appInfo => {
+ try {
+ appInfo.get_id(); // catch invalid file encodings
+ } catch (e) {
+ return false;
+ }
+ return !this._appFavorites.isFavorite(appInfo.get_id()) &&
+ this._parentalControlsManager.shouldShowApp(appInfo);
+ });
+
+ let apps = this._appInfoList.map(app => app.get_id());
+
+ let appSys = Shell.AppSystem.get_default();
+
+ const appsInsideFolders = new Set();
+ this._folderIcons = [];
+
+ let folders = this._folderSettings.get_strv('folder-children');
+ folders.forEach(id => {
+ let path = `${this._folderSettings.path}folders/${id}/`;
+ let icon = this._items.get(id);
+ if (!icon) {
+ icon = new FolderIcon(id, path, this);
+ icon.connect('apps-changed', () => {
+ this._redisplay();
+ this._savePages();
+ });
+ icon.connect('notify::pressed', () => {
+ if (icon.pressed)
+ this.updateDragFocus(icon);
+ });
+ }
+
+ // Don't try to display empty folders
+ if (!icon.visible) {
+ icon.destroy();
+ return;
+ }
+
+ appIcons.push(icon);
+ this._folderIcons.push(icon);
+
+ icon.getAppIds().forEach(appId => appsInsideFolders.add(appId));
+ });
+
+ // Allow dragging of the icon only if the Dash would accept a drop to
+ // change favorite-apps. There are no other possible drop targets from
+ // the app picker, so there's no other need for a drag to start,
+ // at least on single-monitor setups.
+ // This also disables drag-to-launch on multi-monitor setups,
+ // but we hope that is not used much.
+ const isDraggable =
+ global.settings.is_writable('favorite-apps') ||
+ global.settings.is_writable('app-picker-layout');
+
+ apps.forEach(appId => {
+ if (appsInsideFolders.has(appId))
+ return;
+
+ let icon = this._items.get(appId);
+ if (!icon) {
+ let app = appSys.lookup_app(appId);
+
+ icon = new AppIcon(app, { isDraggable });
+ icon.connect('notify::pressed', () => {
+ if (icon.pressed)
+ this.updateDragFocus(icon);
+ });
+ }
+
+ appIcons.push(icon);
+ });
+
+ // At last, if there's a placeholder available, add it
+ if (this._placeholder)
+ appIcons.push(this._placeholder);
+
+ return appIcons;
+ }
+
+ animateSwitch(animationDirection) {
+ super.animateSwitch(animationDirection);
+
+ if (this._currentDialog && this._displayingDialog &&
+ animationDirection == IconGrid.AnimationDirection.OUT) {
+ this._currentDialog.ease({
+ opacity: 0,
+ duration: VIEWS_SWITCH_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => (this.opacity = 255),
+ });
+ }
+ }
+
+ goToPage(pageNumber, animate = true) {
+ pageNumber = Math.clamp(pageNumber, 0, Math.max(this._grid.nPages - 1, 0));
+
+ if (this._grid.currentPage === pageNumber &&
+ this._displayingDialog &&
+ this._currentDialog)
+ return;
+ if (this._displayingDialog && this._currentDialog)
+ this._currentDialog.popdown();
+
+ super.goToPage(pageNumber, animate);
+ }
+
+ _onScroll(actor, event) {
+ if (this._displayingDialog || !this._scrollView.reactive)
+ return Clutter.EVENT_STOP;
+
+ return super._onScroll(actor, event);
+ }
+
+ _onKeyPressEvent(actor, event) {
+ if (this._displayingDialog)
+ return Clutter.EVENT_STOP;
+
+ if (event.get_key_symbol() === Clutter.KEY_Page_Up) {
+ this.goToPage(this._grid.currentPage - 1);
+ return Clutter.EVENT_STOP;
+ } else if (event.get_key_symbol() === Clutter.KEY_Page_Down) {
+ this.goToPage(this._grid.currentPage + 1);
+ return Clutter.EVENT_STOP;
+ } else if (event.get_key_symbol() === Clutter.KEY_Home) {
+ this.goToPage(0);
+ return Clutter.EVENT_STOP;
+ } else if (event.get_key_symbol() === Clutter.KEY_End) {
+ this.goToPage(this._grid.nPages - 1);
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ addFolderDialog(dialog) {
+ Main.layoutManager.overviewGroup.add_child(dialog);
+ dialog.connect('open-state-changed', (o, isOpen) => {
+ this._currentDialog?.disconnectObject(this);
+
+ this._currentDialog = null;
+
+ if (isOpen) {
+ this._currentDialog = dialog;
+ this._currentDialog.connectObject('destroy',
+ () => (this._currentDialog = null), this);
+ }
+ this._displayingDialog = isOpen;
+ });
+ }
+
+ _maybeMoveItem(dragEvent) {
+ const clonedEvent = {
+ ...dragEvent,
+ source: this._placeholder ? this._placeholder : dragEvent.source,
+ };
+
+ super._maybeMoveItem(clonedEvent);
+ }
+
+ _onDragBegin(overview, source) {
+ super._onDragBegin(overview, source);
+
+ // When dragging from a folder dialog, the dragged app icon doesn't
+ // exist in AppDisplay. We work around that by adding a placeholder
+ // icon that is either destroyed on cancel, or becomes the effective
+ // new icon when dropped.
+ if (_getViewFromIcon(source) instanceof FolderView ||
+ this._appFavorites.isFavorite(source.id))
+ this._ensurePlaceholder(source);
+ }
+
+ _onDragMotion(dragEvent) {
+ if (this._currentDialog)
+ return DND.DragMotionResult.CONTINUE;
+
+ return super._onDragMotion(dragEvent);
+ }
+
+ _onDragEnd() {
+ super._onDragEnd();
+ this._removePlaceholder();
+ this._savePages();
+ }
+
+ _onDragCancelled(overview, source) {
+ const view = _getViewFromIcon(source);
+
+ if (view instanceof FolderView)
+ return;
+
+ super._onDragCancelled(overview, source);
+ }
+
+ acceptDrop(source) {
+ if (!super.acceptDrop(source))
+ return false;
+
+ this._savePages();
+
+ let view = _getViewFromIcon(source);
+ if (view instanceof FolderView)
+ view.removeApp(source.app);
+
+ if (this._currentDialog)
+ this._currentDialog.popdown();
+
+ if (this._appFavorites.isFavorite(source.id))
+ this._appFavorites.removeFavorite(source.id);
+
+ return true;
+ }
+
+ createFolder(apps) {
+ let newFolderId = GLib.uuid_string_random();
+
+ let folders = this._folderSettings.get_strv('folder-children');
+ folders.push(newFolderId);
+ this._folderSettings.set_strv('folder-children', folders);
+
+ // Create the new folder
+ let newFolderPath = this._folderSettings.path.concat('folders/', newFolderId, '/');
+ let newFolderSettings;
+ try {
+ newFolderSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.app-folders.folder',
+ path: newFolderPath,
+ });
+ } catch (e) {
+ log('Error creating new folder');
+ return false;
+ }
+
+ // The hovered AppIcon always passes its own id as the first
+ // one, and this is where we want the folder to be created
+ let [folderPage, folderPosition] =
+ this._grid.getItemPosition(this._items.get(apps[0]));
+
+ // Adjust the final position
+ folderPosition -= apps.reduce((counter, appId) => {
+ const [page, position] =
+ this._grid.getItemPosition(this._items.get(appId));
+ if (page === folderPage && position < folderPosition)
+ counter++;
+ return counter;
+ }, 0);
+
+ let appItems = apps.map(id => this._items.get(id).app);
+ let folderName = _findBestFolderName(appItems);
+ if (!folderName)
+ folderName = _("Unnamed Folder");
+
+ newFolderSettings.delay();
+ newFolderSettings.set_string('name', folderName);
+ newFolderSettings.set_strv('apps', apps);
+ newFolderSettings.apply();
+
+ this._redisplay();
+
+ // Move the folder to where the icon target icon was
+ const folderItem = this._items.get(newFolderId);
+ this._moveItem(folderItem, folderPage, folderPosition);
+ this._savePages();
+
+ return true;
+ }
+});
+
+var AppSearchProvider = class AppSearchProvider {
+ constructor() {
+ this._appSys = Shell.AppSystem.get_default();
+ this.id = 'applications';
+ this.isRemoteProvider = false;
+ this.canLaunchSearch = false;
+
+ this._systemActions = new SystemActions.getDefault();
+
+ this._parentalControlsManager = ParentalControlsManager.getDefault();
+ }
+
+ getResultMetas(apps) {
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ let metas = [];
+ for (let id of apps) {
+ if (id.endsWith('.desktop')) {
+ let app = this._appSys.lookup_app(id);
+
+ metas.push({
+ id: app.get_id(),
+ name: app.get_name(),
+ createIcon: size => app.create_icon_texture(size),
+ });
+ } else {
+ let name = this._systemActions.getName(id);
+ let iconName = this._systemActions.getIconName(id);
+
+ const createIcon = size => new St.Icon({
+ icon_name: iconName,
+ width: size * scaleFactor,
+ height: size * scaleFactor,
+ style_class: 'system-action-icon',
+ });
+
+ metas.push({ id, name, createIcon });
+ }
+ }
+
+ return new Promise(resolve => resolve(metas));
+ }
+
+ filterResults(results, maxNumber) {
+ return results.slice(0, maxNumber);
+ }
+
+ getInitialResultSet(terms, cancellable) {
+ // Defer until the parental controls manager is initialised, so the
+ // results can be filtered correctly.
+ if (!this._parentalControlsManager.initialized) {
+ return new Promise(resolve => {
+ let initializedId = this._parentalControlsManager.connect('app-filter-changed', async () => {
+ if (this._parentalControlsManager.initialized) {
+ this._parentalControlsManager.disconnect(initializedId);
+ resolve(await this.getInitialResultSet(terms, cancellable));
+ }
+ });
+ });
+ }
+
+ let query = terms.join(' ');
+ let groups = Shell.AppSystem.search(query);
+ let usage = Shell.AppUsage.get_default();
+ let results = [];
+
+ groups.forEach(group => {
+ group = group.filter(appID => {
+ const app = this._appSys.lookup_app(appID);
+ return app && this._parentalControlsManager.shouldShowApp(app.app_info);
+ });
+ results = results.concat(group.sort(
+ (a, b) => usage.compare(a, b)));
+ });
+
+ results = results.concat(this._systemActions.getMatchingActions(terms));
+ return new Promise(resolve => resolve(results));
+ }
+
+ getSubsearchResultSet(previousResults, terms, cancellable) {
+ return this.getInitialResultSet(terms, cancellable);
+ }
+
+ createResultObject(resultMeta) {
+ if (resultMeta.id.endsWith('.desktop')) {
+ return new AppIcon(this._appSys.lookup_app(resultMeta['id']), {
+ expandTitleOnHover: false,
+ });
+ } else {
+ return new SystemActionIcon(this, resultMeta);
+ }
+ }
+};
+
+var AppViewItem = GObject.registerClass(
+class AppViewItem extends St.Button {
+ _init(params = {}, isDraggable = true, expandTitleOnHover = true) {
+ super._init({
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ reactive: true,
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
+ can_focus: true,
+ ...params,
+ });
+
+ this._delegate = this;
+
+ if (isDraggable) {
+ this._draggable = DND.makeDraggable(this, { timeoutThreshold: 200 });
+
+ this._draggable.connect('drag-begin', this._onDragBegin.bind(this));
+ this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this));
+ this._draggable.connect('drag-end', this._onDragEnd.bind(this));
+ }
+
+ this._otherIconIsHovering = false;
+ this._expandTitleOnHover = expandTitleOnHover;
+
+ if (expandTitleOnHover)
+ this.connect('notify::hover', this._onHover.bind(this));
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ if (this._dragMonitor) {
+ DND.removeDragMonitor(this._dragMonitor);
+ this._dragMonitor = null;
+ }
+
+ if (this._draggable) {
+ if (this._dragging)
+ Main.overview.endItemDrag(this);
+ this._draggable = null;
+ }
+ }
+
+ _updateMultiline() {
+ if (!this._expandTitleOnHover || !this.icon.label)
+ return;
+
+ const { label } = this.icon;
+ const { clutterText } = label;
+ const layout = clutterText.get_layout();
+ if (!layout.is_wrapped() && !layout.is_ellipsized())
+ return;
+
+ label.remove_transition('allocation');
+
+ const id = label.connect('notify::allocation', () => {
+ label.restore_easing_state();
+ label.disconnect(id);
+ });
+
+ const expand = this._forcedHighlight || this.hover || this.has_key_focus();
+ label.save_easing_state();
+ label.set_easing_duration(expand
+ ? APP_ICON_TITLE_EXPAND_TIME
+ : APP_ICON_TITLE_COLLAPSE_TIME);
+ clutterText.set({
+ line_wrap: expand,
+ line_wrap_mode: expand ? Pango.WrapMode.WORD_CHAR : Pango.WrapMode.NONE,
+ ellipsize: expand ? Pango.EllipsizeMode.NONE : Pango.EllipsizeMode.END,
+ });
+ }
+
+ _onHover() {
+ this._updateMultiline();
+ }
+
+ _onDragBegin() {
+ this._dragging = true;
+ this.scaleAndFade();
+ Main.overview.beginItemDrag(this);
+ }
+
+ _onDragCancelled() {
+ this._dragging = false;
+ Main.overview.cancelledItemDrag(this);
+ }
+
+ _onDragEnd() {
+ this._dragging = false;
+ this.undoScaleAndFade();
+ Main.overview.endItemDrag(this);
+ }
+
+ scaleIn() {
+ this.scale_x = 0;
+ this.scale_y = 0;
+
+ this.ease({
+ scale_x: 1,
+ scale_y: 1,
+ duration: APP_ICON_SCALE_IN_TIME,
+ delay: APP_ICON_SCALE_IN_DELAY,
+ mode: Clutter.AnimationMode.EASE_OUT_QUINT,
+ });
+ }
+
+ scaleAndFade() {
+ this.reactive = false;
+ this.ease({
+ scale_x: 0.5,
+ scale_y: 0.5,
+ opacity: 0,
+ });
+ }
+
+ undoScaleAndFade() {
+ this.reactive = true;
+ this.ease({
+ scale_x: 1.0,
+ scale_y: 1.0,
+ opacity: 255,
+ });
+ }
+
+ _canAccept(source) {
+ return source !== this;
+ }
+
+ _setHoveringByDnd(hovering) {
+ if (this._otherIconIsHovering === hovering)
+ return;
+
+ this._otherIconIsHovering = hovering;
+
+ if (hovering) {
+ this._dragMonitor = {
+ dragMotion: this._onDragMotion.bind(this),
+ };
+ DND.addDragMonitor(this._dragMonitor);
+ } else {
+ DND.removeDragMonitor(this._dragMonitor);
+ }
+ }
+
+ _onDragMotion(dragEvent) {
+ if (!this.contains(dragEvent.targetActor))
+ this._setHoveringByDnd(false);
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ _withinLeeways(x) {
+ return x < IconGrid.LEFT_DIVIDER_LEEWAY ||
+ x > this.width - IconGrid.RIGHT_DIVIDER_LEEWAY;
+ }
+
+ vfunc_key_focus_in() {
+ this._updateMultiline();
+ super.vfunc_key_focus_in();
+ }
+
+ vfunc_key_focus_out() {
+ this._updateMultiline();
+ super.vfunc_key_focus_out();
+ }
+
+ handleDragOver(source, _actor, x) {
+ if (source === this)
+ return DND.DragMotionResult.NO_DROP;
+
+ if (!this._canAccept(source))
+ return DND.DragMotionResult.CONTINUE;
+
+ if (this._withinLeeways(x)) {
+ this._setHoveringByDnd(false);
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ this._setHoveringByDnd(true);
+
+ return DND.DragMotionResult.MOVE_DROP;
+ }
+
+ acceptDrop(source, _actor, x) {
+ this._setHoveringByDnd(false);
+
+ if (!this._canAccept(source))
+ return false;
+
+ if (this._withinLeeways(x))
+ return false;
+
+ return true;
+ }
+
+ cancelActions() {
+ if (this._draggable)
+ this._draggable.fakeRelease();
+ this.fake_release();
+ }
+
+ get id() {
+ return this._id;
+ }
+
+ get name() {
+ return this._name;
+ }
+
+ setForcedHighlight(highlighted) {
+ this._forcedHighlight = highlighted;
+ this.set({
+ track_hover: !highlighted,
+ hover: highlighted,
+ });
+ }
+});
+
+var FolderGrid = GObject.registerClass(
+class FolderGrid extends AppGrid {
+ _init() {
+ super._init({
+ allow_incomplete_pages: false,
+ columns_per_page: 3,
+ rows_per_page: 3,
+ page_halign: Clutter.ActorAlign.CENTER,
+ page_valign: Clutter.ActorAlign.CENTER,
+ });
+
+ this.setGridModes([
+ {
+ rows: 3,
+ columns: 3,
+ },
+ ]);
+ }
+});
+
+var FolderView = GObject.registerClass(
+class FolderView extends BaseAppView {
+ _init(folder, id, parentView) {
+ super._init({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ gesture_modes: Shell.ActionMode.POPUP,
+ });
+
+ // If it not expand, the parent doesn't take into account its preferred_width when allocating
+ // the second time it allocates, so we apply the "Standard hack for ClutterBinLayout"
+ this._grid.x_expand = true;
+ this._id = id;
+ this._folder = folder;
+ this._parentView = parentView;
+ this._grid._delegate = this;
+
+ this.add_child(this._box);
+
+ let action = new Clutter.PanAction({ interpolate: true });
+ action.connect('pan', this._onPan.bind(this));
+ this._scrollView.add_action(action);
+
+ this._deletingFolder = false;
+ this._appIds = [];
+ this._redisplay();
+ }
+
+ _createGrid() {
+ return new FolderGrid();
+ }
+
+ _getFolderApps() {
+ const appIds = [];
+ const excludedApps = this._folder.get_strv('excluded-apps');
+ const appSys = Shell.AppSystem.get_default();
+ const addAppId = appId => {
+ if (excludedApps.includes(appId))
+ return;
+
+ if (this._appFavorites.isFavorite(appId))
+ return;
+
+ const app = appSys.lookup_app(appId);
+ if (!app)
+ return;
+
+ if (!this._parentalControlsManager.shouldShowApp(app.get_app_info()))
+ return;
+
+ if (appIds.indexOf(appId) !== -1)
+ return;
+
+ appIds.push(appId);
+ };
+
+ const folderApps = this._folder.get_strv('apps');
+ folderApps.forEach(addAppId);
+
+ const folderCategories = this._folder.get_strv('categories');
+ const appInfos = this._parentView.getAppInfos();
+ appInfos.forEach(appInfo => {
+ let appCategories = _getCategories(appInfo);
+ if (!_listsIntersect(folderCategories, appCategories))
+ return;
+
+ addAppId(appInfo.get_id());
+ });
+
+ return appIds;
+ }
+
+ _getItemPosition(item) {
+ const appIndex = this._appIds.indexOf(item.id);
+
+ if (appIndex === -1)
+ return [-1, -1];
+
+ const { itemsPerPage } = this._grid;
+ return [Math.floor(appIndex / itemsPerPage), appIndex % itemsPerPage];
+ }
+
+ _compareItems(a, b) {
+ const aPosition = this._appIds.indexOf(a.id);
+ const bPosition = this._appIds.indexOf(b.id);
+
+ if (aPosition === -1 && bPosition === -1)
+ return a.name.localeCompare(b.name);
+ else if (aPosition === -1)
+ return 1;
+ else if (bPosition === -1)
+ return -1;
+
+ return aPosition - bPosition;
+ }
+
+ createFolderIcon(size) {
+ const layout = new Clutter.GridLayout({
+ row_homogeneous: true,
+ column_homogeneous: true,
+ });
+ let icon = new St.Widget({
+ layout_manager: layout,
+ x_align: Clutter.ActorAlign.CENTER,
+ style: `width: ${size}px; height: ${size}px;`,
+ });
+
+ let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
+
+ let numItems = this._orderedItems.length;
+ let rtl = icon.get_text_direction() == Clutter.TextDirection.RTL;
+ for (let i = 0; i < 4; i++) {
+ const style = `width: ${subSize}px; height: ${subSize}px;`;
+ let bin = new St.Bin({ style });
+ if (i < numItems)
+ bin.child = this._orderedItems[i].app.create_icon_texture(subSize);
+ layout.attach(bin, rtl ? (i + 1) % 2 : i % 2, Math.floor(i / 2), 1, 1);
+ }
+
+ return icon;
+ }
+
+ _onPan(action) {
+ let [dist_, dx_, dy] = action.get_motion_delta(0);
+ let adjustment = this._scrollView.vscroll.adjustment;
+ adjustment.value -= (dy / this._scrollView.height) * adjustment.page_size;
+ return false;
+ }
+
+ _loadApps() {
+ let apps = [];
+ let appSys = Shell.AppSystem.get_default();
+
+ this._appIds.forEach(appId => {
+ const app = appSys.lookup_app(appId);
+
+ let icon = this._items.get(appId);
+ if (!icon)
+ icon = new AppIcon(app);
+
+ apps.push(icon);
+ });
+
+ return apps;
+ }
+
+ _redisplay() {
+ // Keep the app ids list cached
+ this._appIds = this._getFolderApps();
+
+ super._redisplay();
+ }
+
+ acceptDrop(source) {
+ if (!super.acceptDrop(source))
+ return false;
+
+ const folderApps = this._orderedItems.map(item => item.id);
+ this._folder.set_strv('apps', folderApps);
+
+ return true;
+ }
+
+ addApp(app) {
+ let folderApps = this._folder.get_strv('apps');
+ folderApps.push(app.id);
+
+ this._folder.set_strv('apps', folderApps);
+
+ // Also remove from 'excluded-apps' if the app id is listed
+ // there. This is only possible on categories-based folders.
+ let excludedApps = this._folder.get_strv('excluded-apps');
+ let index = excludedApps.indexOf(app.id);
+ if (index >= 0) {
+ excludedApps.splice(index, 1);
+ this._folder.set_strv('excluded-apps', excludedApps);
+ }
+ }
+
+ removeApp(app) {
+ let folderApps = this._folder.get_strv('apps');
+ let index = folderApps.indexOf(app.id);
+ if (index >= 0)
+ folderApps.splice(index, 1);
+
+ // Remove the folder if this is the last app icon; otherwise,
+ // just remove the icon
+ if (folderApps.length == 0) {
+ this._deletingFolder = true;
+
+ // Resetting all keys deletes the relocatable schema
+ let keys = this._folder.settings_schema.list_keys();
+ for (const key of keys)
+ this._folder.reset(key);
+
+ let settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' });
+ let folders = settings.get_strv('folder-children');
+ folders.splice(folders.indexOf(this._id), 1);
+ settings.set_strv('folder-children', folders);
+
+ this._deletingFolder = false;
+ } else {
+ // If this is a categories-based folder, also add it to
+ // the list of excluded apps
+ const categories = this._folder.get_strv('categories');
+ if (categories.length > 0) {
+ const excludedApps = this._folder.get_strv('excluded-apps');
+ excludedApps.push(app.id);
+ this._folder.set_strv('excluded-apps', excludedApps);
+ }
+
+ this._folder.set_strv('apps', folderApps);
+ }
+ }
+
+ get deletingFolder() {
+ return this._deletingFolder;
+ }
+});
+
+var FolderIcon = GObject.registerClass({
+ Signals: {
+ 'apps-changed': {},
+ },
+}, class FolderIcon extends AppViewItem {
+ _init(id, path, parentView) {
+ super._init({
+ style_class: 'app-well-app app-folder',
+ button_mask: St.ButtonMask.ONE,
+ toggle_mode: true,
+ can_focus: true,
+ }, global.settings.is_writable('app-picker-layout'));
+ this._id = id;
+ this._name = '';
+ this._parentView = parentView;
+
+ this._folder = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.app-folders.folder',
+ path,
+ });
+
+ this.icon = new IconGrid.BaseIcon('', {
+ createIcon: this._createIcon.bind(this),
+ setSizeManually: true,
+ });
+ this.set_child(this.icon);
+ this.label_actor = this.icon.label;
+
+ this.view = new FolderView(this._folder, id, parentView);
+
+ this._folder.connectObject(
+ 'changed', this._sync.bind(this), this);
+ this._sync();
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ if (this._dialog)
+ this._dialog.destroy();
+ else
+ this.view.destroy();
+ }
+
+ vfunc_clicked() {
+ this.open();
+ }
+
+ vfunc_unmap() {
+ if (this._dialog)
+ this._dialog.popdown();
+
+ super.vfunc_unmap();
+ }
+
+ open() {
+ this._ensureFolderDialog();
+ this.view._scrollView.vscroll.adjustment.value = 0;
+ this._dialog.popup();
+ }
+
+ getAppIds() {
+ return this.view.getAllItems().map(item => item.id);
+ }
+
+ _setHoveringByDnd(hovering) {
+ if (this._otherIconIsHovering == hovering)
+ return;
+
+ super._setHoveringByDnd(hovering);
+
+ if (hovering)
+ this.add_style_pseudo_class('drop');
+ else
+ this.remove_style_pseudo_class('drop');
+ }
+
+ _onDragMotion(dragEvent) {
+ if (!this._canAccept(dragEvent.source))
+ this._setHoveringByDnd(false);
+
+ return super._onDragMotion(dragEvent);
+ }
+
+ getDragActor() {
+ const iconParams = {
+ createIcon: this._createIcon.bind(this),
+ showLabel: this.icon.label !== null,
+ setSizeManually: false,
+ };
+
+ const icon = new IconGrid.BaseIcon(this.name, iconParams);
+ icon.style_class = this.style_class;
+
+ return icon;
+ }
+
+ getDragActorSource() {
+ return this;
+ }
+
+ _canAccept(source) {
+ if (!(source instanceof AppIcon))
+ return false;
+
+ let view = _getViewFromIcon(source);
+ if (!view || !(view instanceof AppDisplay))
+ return false;
+
+ if (this._folder.get_strv('apps').includes(source.id))
+ return false;
+
+ return true;
+ }
+
+ acceptDrop(source) {
+ const accepted = super.acceptDrop(source);
+
+ if (!accepted)
+ return false;
+
+ this.view.addApp(source.app);
+
+ return true;
+ }
+
+ _updateName() {
+ let name = _getFolderName(this._folder);
+ if (this.name == name)
+ return;
+
+ this._name = name;
+ this.icon.label.text = this.name;
+ }
+
+ _sync() {
+ if (this.view.deletingFolder)
+ return;
+
+ this.emit('apps-changed');
+ this._updateName();
+ this.visible = this.view.getAllItems().length > 0;
+ this.icon.update();
+ }
+
+ _createIcon(iconSize) {
+ return this.view.createFolderIcon(iconSize, this);
+ }
+
+ _ensureFolderDialog() {
+ if (this._dialog)
+ return;
+ if (!this._dialog) {
+ this._dialog = new AppFolderDialog(this, this._folder,
+ this._parentView);
+ this._parentView.addFolderDialog(this._dialog);
+ this._dialog.connect('open-state-changed', (popup, isOpen) => {
+ const duration = FOLDER_DIALOG_ANIMATION_TIME / 2;
+ const mode = isOpen
+ ? Clutter.AnimationMode.EASE_OUT_QUAD
+ : Clutter.AnimationMode.EASE_IN_QUAD;
+
+ this.ease({
+ opacity: isOpen ? 0 : 255,
+ duration,
+ mode,
+ delay: isOpen ? 0 : FOLDER_DIALOG_ANIMATION_TIME - duration,
+ });
+
+ if (!isOpen)
+ this.checked = false;
+ });
+ }
+ }
+});
+
+var AppFolderDialog = GObject.registerClass({
+ Signals: {
+ 'open-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
+ },
+}, class AppFolderDialog extends St.Bin {
+ _init(source, folder, appDisplay) {
+ super._init({
+ visible: false,
+ x_expand: true,
+ y_expand: true,
+ reactive: true,
+ });
+
+ this.add_constraint(new Layout.MonitorConstraint({ primary: true }));
+
+ const clickAction = new Clutter.ClickAction();
+ clickAction.connect('clicked', () => {
+ const [x, y] = clickAction.get_coords();
+ const actor =
+ global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
+
+ if (actor === this)
+ this.popdown();
+ });
+ this.add_action(clickAction);
+
+ this._source = source;
+ this._folder = folder;
+ this._view = source.view;
+ this._appDisplay = appDisplay;
+ this._delegate = this;
+
+ this._isOpen = false;
+
+ this._viewBox = new St.BoxLayout({
+ style_class: 'app-folder-dialog',
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.FILL,
+ y_align: Clutter.ActorAlign.FILL,
+ vertical: true,
+ });
+
+ this.child = new St.Bin({
+ style_class: 'app-folder-dialog-container',
+ child: this._viewBox,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._addFolderNameEntry();
+ this._viewBox.add_child(this._view);
+
+ global.focus_manager.add_group(this);
+
+ this._grabHelper = new GrabHelper.GrabHelper(this, {
+ actionMode: Shell.ActionMode.POPUP,
+ });
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._dragMonitor = null;
+ this._sourceMappedId = 0;
+ this._popdownTimeoutId = 0;
+ this._needsZoomAndFade = false;
+
+ this._popdownCallbacks = [];
+ }
+
+ _addFolderNameEntry() {
+ this._entryBox = new St.BoxLayout({
+ style_class: 'folder-name-container',
+ });
+ this._viewBox.add_child(this._entryBox);
+
+ // Empty actor to center the title
+ let ghostButton = new Clutter.Actor();
+ this._entryBox.add_child(ghostButton);
+
+ let stack = new Shell.Stack({
+ x_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this._entryBox.add_child(stack);
+
+ // Folder name label
+ this._folderNameLabel = new St.Label({
+ style_class: 'folder-name-label',
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ stack.add_child(this._folderNameLabel);
+
+ // Folder name entry
+ this._entry = new St.Entry({
+ style_class: 'folder-name-entry',
+ opacity: 0,
+ reactive: false,
+ });
+ this._entry.clutter_text.set({
+ x_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._entry.clutter_text.connect('activate', () => {
+ this._showFolderLabel();
+ });
+
+ stack.add_child(this._entry);
+
+ // Edit button
+ this._editButton = new St.Button({
+ style_class: 'edit-folder-button',
+ button_mask: St.ButtonMask.ONE,
+ toggle_mode: true,
+ reactive: true,
+ can_focus: true,
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.CENTER,
+ icon_name: 'document-edit-symbolic',
+ });
+
+ this._editButton.connect('notify::checked', () => {
+ if (this._editButton.checked)
+ this._showFolderEntry();
+ else
+ this._showFolderLabel();
+ });
+
+ this._entryBox.add_child(this._editButton);
+
+ ghostButton.add_constraint(new Clutter.BindConstraint({
+ source: this._editButton,
+ coordinate: Clutter.BindCoordinate.SIZE,
+ }));
+
+ this._folder.connect('changed::name', () => this._syncFolderName());
+ this._syncFolderName();
+ }
+
+ _syncFolderName() {
+ let newName = _getFolderName(this._folder);
+
+ this._folderNameLabel.text = newName;
+ this._entry.text = newName;
+ }
+
+ _switchActor(from, to) {
+ to.reactive = true;
+ to.ease({
+ opacity: 255,
+ duration: 300,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ from.ease({
+ opacity: 0,
+ duration: 300,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ from.reactive = false;
+ },
+ });
+ }
+
+ _showFolderLabel() {
+ if (this._editButton.checked)
+ this._editButton.checked = false;
+
+ this._maybeUpdateFolderName();
+ this._switchActor(this._entry, this._folderNameLabel);
+ }
+
+ _showFolderEntry() {
+ this._switchActor(this._folderNameLabel, this._entry);
+
+ this._entry.clutter_text.set_selection(0, -1);
+ this._entry.clutter_text.grab_key_focus();
+ }
+
+ _maybeUpdateFolderName() {
+ let folderName = _getFolderName(this._folder);
+ let newFolderName = this._entry.text.trim();
+
+ if (newFolderName.length === 0 || newFolderName === folderName)
+ return;
+
+ this._folder.set_string('name', newFolderName);
+ this._folder.set_boolean('translate', false);
+ }
+
+ _zoomAndFadeIn() {
+ let [sourceX, sourceY] =
+ this._source.get_transformed_position();
+ let [dialogX, dialogY] =
+ this.child.get_transformed_position();
+
+ this.child.set({
+ translation_x: sourceX - dialogX,
+ translation_y: sourceY - dialogY,
+ scale_x: this._source.width / this.child.width,
+ scale_y: this._source.height / this.child.height,
+ opacity: 0,
+ });
+
+ this.ease({
+ background_color: DIALOG_SHADE_NORMAL,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ this.child.ease({
+ translation_x: 0,
+ translation_y: 0,
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this._needsZoomAndFade = false;
+
+ if (this._sourceMappedId === 0) {
+ this._sourceMappedId = this._source.connect(
+ 'notify::mapped', this._zoomAndFadeOut.bind(this));
+ }
+ }
+
+ _zoomAndFadeOut() {
+ if (!this._isOpen)
+ return;
+
+ if (!this._source.mapped) {
+ this.hide();
+ return;
+ }
+
+ let [sourceX, sourceY] =
+ this._source.get_transformed_position();
+ let [dialogX, dialogY] =
+ this.child.get_transformed_position();
+
+ this.ease({
+ background_color: Clutter.Color.from_pixel(0x00000000),
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this.child.ease({
+ translation_x: sourceX - dialogX,
+ translation_y: sourceY - dialogY,
+ scale_x: this._source.width / this.child.width,
+ scale_y: this._source.height / this.child.height,
+ opacity: 0,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this.child.set({
+ translation_x: 0,
+ translation_y: 0,
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ });
+ this.hide();
+
+ this._popdownCallbacks.forEach(func => func());
+ this._popdownCallbacks = [];
+ },
+ });
+
+ this._needsZoomAndFade = false;
+ }
+
+ _removeDragMonitor() {
+ if (!this._dragMonitor)
+ return;
+
+ DND.removeDragMonitor(this._dragMonitor);
+ this._dragMonitor = null;
+ }
+
+ _removePopdownTimeout() {
+ if (this._popdownTimeoutId === 0)
+ return;
+
+ GLib.source_remove(this._popdownTimeoutId);
+ this._popdownTimeoutId = 0;
+ }
+
+ _onDestroy() {
+ if (this._isOpen) {
+ this._isOpen = false;
+ this._grabHelper.ungrab({ actor: this });
+ this._grabHelper = null;
+ }
+
+ if (this._sourceMappedId) {
+ this._source.disconnect(this._sourceMappedId);
+ this._sourceMappedId = 0;
+ }
+
+ this._removePopdownTimeout();
+ this._removeDragMonitor();
+ }
+
+ vfunc_allocate(box) {
+ super.vfunc_allocate(box);
+
+ // We can only start zooming after receiving an allocation
+ if (this._needsZoomAndFade)
+ this._zoomAndFadeIn();
+ }
+
+ vfunc_key_press_event(keyEvent) {
+ if (global.stage.get_key_focus() != this)
+ return Clutter.EVENT_PROPAGATE;
+
+ // Since we need to only grab focus on one item child when the user
+ // actually press a key we don't use navigate_focus when opening
+ // the popup.
+ // Instead of that, grab the focus on the AppFolderPopup actor
+ // and actually moves the focus to a child only when the user
+ // actually press a key.
+ // It should work with just grab_key_focus on the AppFolderPopup
+ // actor, but since the arrow keys are not wrapping_around the focus
+ // is not grabbed by a child when the widget that has the current focus
+ // is the same that is requesting focus, so to make it works with arrow
+ // keys we need to connect to the key-press-event and navigate_focus
+ // when that happens using TAB_FORWARD or TAB_BACKWARD instead of arrow
+ // keys
+
+ // Use TAB_FORWARD for down key and right key
+ // and TAB_BACKWARD for up key and left key on ltr
+ // languages
+ let direction;
+ let isLtr = Clutter.get_default_text_direction() == Clutter.TextDirection.LTR;
+ switch (keyEvent.keyval) {
+ case Clutter.KEY_Down:
+ direction = St.DirectionType.TAB_FORWARD;
+ break;
+ case Clutter.KEY_Right:
+ direction = isLtr
+ ? St.DirectionType.TAB_FORWARD
+ : St.DirectionType.TAB_BACKWARD;
+ break;
+ case Clutter.KEY_Up:
+ direction = St.DirectionType.TAB_BACKWARD;
+ break;
+ case Clutter.KEY_Left:
+ direction = isLtr
+ ? St.DirectionType.TAB_BACKWARD
+ : St.DirectionType.TAB_FORWARD;
+ break;
+ default:
+ return Clutter.EVENT_PROPAGATE;
+ }
+ return this.navigate_focus(null, direction, false);
+ }
+
+ _setLighterBackground(lighter) {
+ const backgroundColor = lighter
+ ? DIALOG_SHADE_HIGHLIGHT
+ : DIALOG_SHADE_NORMAL;
+
+ this.ease({
+ backgroundColor,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ _withinDialog(x, y) {
+ const childExtents = this.child.get_transformed_extents();
+ return childExtents.contains_point(new Graphene.Point({ x, y }));
+ }
+
+ _setupDragMonitor() {
+ if (this._dragMonitor)
+ return;
+
+ this._dragMonitor = {
+ dragMotion: dragEvent => {
+ const withinDialog =
+ this._withinDialog(dragEvent.x, dragEvent.y);
+
+ this._setLighterBackground(!withinDialog);
+
+ if (withinDialog) {
+ this._removePopdownTimeout();
+ this._removeDragMonitor();
+ }
+ return DND.DragMotionResult.CONTINUE;
+ },
+ };
+ DND.addDragMonitor(this._dragMonitor);
+ }
+
+ _setupPopdownTimeout() {
+ if (this._popdownTimeoutId > 0)
+ return;
+
+ this._popdownTimeoutId =
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, POPDOWN_DIALOG_TIMEOUT, () => {
+ this._popdownTimeoutId = 0;
+ this.popdown();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ handleDragOver(source, actor, x, y) {
+ if (this._withinDialog(x, y)) {
+ this._setLighterBackground(false);
+ this._removePopdownTimeout();
+ this._removeDragMonitor();
+ } else {
+ this._setupPopdownTimeout();
+ this._setupDragMonitor();
+ }
+
+ return DND.DragMotionResult.MOVE_DROP;
+ }
+
+ acceptDrop(source) {
+ const appId = source.id;
+
+ this.popdown(() => {
+ this._view.removeApp(source);
+ this._appDisplay.selectApp(appId);
+ });
+
+ return true;
+ }
+
+ toggle() {
+ if (this._isOpen)
+ this.popdown();
+ else
+ this.popup();
+ }
+
+ popup() {
+ if (this._isOpen)
+ return;
+
+ this._isOpen = this._grabHelper.grab({
+ actor: this,
+ onUngrab: () => this.popdown(),
+ });
+
+ if (!this._isOpen)
+ return;
+
+ this.get_parent().set_child_above_sibling(this, null);
+
+ this._needsZoomAndFade = true;
+ this.show();
+
+ this.emit('open-state-changed', true);
+ }
+
+ popdown(callback) {
+ // Either call the callback right away, or wait for the zoom out
+ // animation to finish
+ if (callback) {
+ if (this.visible)
+ this._popdownCallbacks.push(callback);
+ else
+ callback();
+ }
+
+ if (!this._isOpen)
+ return;
+
+ this._zoomAndFadeOut();
+ this._showFolderLabel();
+
+ this._isOpen = false;
+ this._grabHelper.ungrab({ actor: this });
+ this.emit('open-state-changed', false);
+ }
+});
+
+var AppIcon = GObject.registerClass({
+ Signals: {
+ 'menu-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
+ 'sync-tooltip': {},
+ },
+}, class AppIcon extends AppViewItem {
+ _init(app, iconParams = {}) {
+ // Get the isDraggable property without passing it on to the BaseIcon:
+ const appIconParams = Params.parse(iconParams, { isDraggable: true }, true);
+ const isDraggable = appIconParams['isDraggable'];
+ delete iconParams['isDraggable'];
+ const expandTitleOnHover = appIconParams['expandTitleOnHover'];
+ delete iconParams['expandTitleOnHover'];
+
+ super._init({ style_class: 'app-well-app' }, isDraggable, expandTitleOnHover);
+
+ this.app = app;
+ this._id = app.get_id();
+ this._name = app.get_name();
+
+ this._iconContainer = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ });
+
+ this.set_child(this._iconContainer);
+
+ this._folderPreviewId = 0;
+
+ iconParams['createIcon'] = this._createIcon.bind(this);
+ iconParams['setSizeManually'] = true;
+ this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
+ this._iconContainer.add_child(this.icon);
+
+ this._dot = new St.Widget({
+ style_class: 'app-well-app-running-dot',
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.END,
+ });
+ this._iconContainer.add_child(this._dot);
+
+ this.label_actor = this.icon.label;
+
+ this.connect('popup-menu', this._onKeyboardPopupMenu.bind(this));
+
+ this._menu = null;
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+
+ this._menuTimeoutId = 0;
+ this.app.connectObject('notify::state',
+ () => this._updateRunningStyle(), this);
+ this._updateRunningStyle();
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ if (this._folderPreviewId > 0) {
+ GLib.source_remove(this._folderPreviewId);
+ this._folderPreviewId = 0;
+ }
+
+ this._removeMenuTimeout();
+ }
+
+ _onDragBegin() {
+ if (this._menu)
+ this._menu.close(true);
+ this._removeMenuTimeout();
+ super._onDragBegin();
+ }
+
+ _createIcon(iconSize) {
+ return this.app.create_icon_texture(iconSize);
+ }
+
+ _removeMenuTimeout() {
+ if (this._menuTimeoutId > 0) {
+ GLib.source_remove(this._menuTimeoutId);
+ this._menuTimeoutId = 0;
+ }
+ }
+
+ _updateRunningStyle() {
+ if (this.app.state != Shell.AppState.STOPPED)
+ this._dot.show();
+ else
+ this._dot.hide();
+ }
+
+ _setPopupTimeout() {
+ this._removeMenuTimeout();
+ this._menuTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MENU_POPUP_TIMEOUT, () => {
+ this._menuTimeoutId = 0;
+ this.popupMenu();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._menuTimeoutId, '[gnome-shell] this.popupMenu');
+ }
+
+ vfunc_leave_event(crossingEvent) {
+ const ret = super.vfunc_leave_event(crossingEvent);
+
+ this.fake_release();
+ this._removeMenuTimeout();
+ return ret;
+ }
+
+ vfunc_button_press_event(buttonEvent) {
+ const ret = super.vfunc_button_press_event(buttonEvent);
+ if (buttonEvent.button == 1) {
+ this._setPopupTimeout();
+ } else if (buttonEvent.button == 3) {
+ this.popupMenu();
+ return Clutter.EVENT_STOP;
+ }
+ return ret;
+ }
+
+ vfunc_touch_event(touchEvent) {
+ const ret = super.vfunc_touch_event(touchEvent);
+ if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN)
+ this._setPopupTimeout();
+
+ return ret;
+ }
+
+ vfunc_clicked(button) {
+ this._removeMenuTimeout();
+ this.activate(button);
+ }
+
+ _onKeyboardPopupMenu() {
+ this.popupMenu();
+ this._menu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ }
+
+ getId() {
+ return this.app.get_id();
+ }
+
+ popupMenu(side = St.Side.LEFT) {
+ this.setForcedHighlight(true);
+ this._removeMenuTimeout();
+ this.fake_release();
+
+ if (!this._menu) {
+ this._menu = new AppMenu(this, side, {
+ favoritesSection: true,
+ showSingleWindows: true,
+ });
+ this._menu.setApp(this.app);
+ this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ Main.overview.connectObject('hiding',
+ () => this._menu.close(), this);
+
+ Main.uiGroup.add_actor(this._menu.actor);
+ this._menuManager.addMenu(this._menu);
+ }
+
+ this.emit('menu-state-changed', true);
+
+ this._menu.open(BoxPointer.PopupAnimation.FULL);
+ this._menuManager.ignoreRelease();
+ this.emit('sync-tooltip');
+
+ return false;
+ }
+
+ _onMenuPoppedDown() {
+ this.setForcedHighlight(false);
+ this.emit('menu-state-changed', false);
+ }
+
+ activate(button) {
+ let event = Clutter.get_current_event();
+ let modifiers = event ? event.get_state() : 0;
+ let isMiddleButton = button && button == Clutter.BUTTON_MIDDLE;
+ let isCtrlPressed = (modifiers & Clutter.ModifierType.CONTROL_MASK) != 0;
+ let openNewWindow = this.app.can_open_new_window() &&
+ this.app.state == Shell.AppState.RUNNING &&
+ (isCtrlPressed || isMiddleButton);
+
+ if (this.app.state == Shell.AppState.STOPPED || openNewWindow)
+ this.animateLaunch();
+
+ if (openNewWindow)
+ this.app.open_new_window(-1);
+ else
+ this.app.activate();
+
+ Main.overview.hide();
+ }
+
+ animateLaunch() {
+ this.icon.animateZoomOut();
+ }
+
+ animateLaunchAtPos(x, y) {
+ this.icon.animateZoomOutAtPos(x, y);
+ }
+
+ shellWorkspaceLaunch(params) {
+ let { stack } = new Error();
+ log(`shellWorkspaceLaunch is deprecated, use app.open_new_window() instead\n${stack}`);
+
+ params = Params.parse(params, {
+ workspace: -1,
+ timestamp: 0,
+ });
+
+ this.app.open_new_window(params.workspace);
+ }
+
+ getDragActor() {
+ return this.app.create_icon_texture(Main.overview.dash.iconSize);
+ }
+
+ // Returns the original actor that should align with the actor
+ // we show as the item is being dragged.
+ getDragActorSource() {
+ return this.icon.icon;
+ }
+
+ shouldShowTooltip() {
+ return this.hover && (!this._menu || !this._menu.isOpen);
+ }
+
+ _showFolderPreview() {
+ this.icon.label.opacity = 0;
+ this.icon.icon.ease({
+ scale_x: FOLDER_SUBICON_FRACTION,
+ scale_y: FOLDER_SUBICON_FRACTION,
+ });
+ }
+
+ _hideFolderPreview() {
+ this.icon.label.opacity = 255;
+ this.icon.icon.ease({
+ scale_x: 1.0,
+ scale_y: 1.0,
+ });
+ }
+
+ _canAccept(source) {
+ let view = _getViewFromIcon(source);
+
+ return source != this &&
+ (source instanceof this.constructor) &&
+ (view instanceof AppDisplay);
+ }
+
+ _setHoveringByDnd(hovering) {
+ if (this._otherIconIsHovering == hovering)
+ return;
+
+ super._setHoveringByDnd(hovering);
+
+ if (hovering) {
+ if (this._folderPreviewId > 0)
+ return;
+
+ this._folderPreviewId =
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
+ this.add_style_pseudo_class('drop');
+ this._showFolderPreview();
+ this._folderPreviewId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ } else {
+ if (this._folderPreviewId > 0) {
+ GLib.source_remove(this._folderPreviewId);
+ this._folderPreviewId = 0;
+ }
+ this._hideFolderPreview();
+ this.remove_style_pseudo_class('drop');
+ }
+ }
+
+ acceptDrop(source, actor, x) {
+ const accepted = super.acceptDrop(source, actor, x);
+ if (!accepted)
+ return false;
+
+ let view = _getViewFromIcon(this);
+ let apps = [this.id, source.id];
+
+ return view?.createFolder(apps);
+ }
+
+ cancelActions() {
+ if (this._menu)
+ this._menu.close(true);
+ this._removeMenuTimeout();
+ super.cancelActions();
+ }
+});
+
+var SystemActionIcon = GObject.registerClass(
+class SystemActionIcon extends Search.GridSearchResult {
+ activate() {
+ SystemActions.getDefault().activateAction(this.metaInfo['id']);
+ Main.overview.hide();
+ }
+});
diff --git a/js/ui/appFavorites.js b/js/ui/appFavorites.js
new file mode 100644
index 0000000..d8a3018
--- /dev/null
+++ b/js/ui/appFavorites.js
@@ -0,0 +1,212 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported getAppFavorites */
+
+const Shell = imports.gi.Shell;
+const ParentalControlsManager = imports.misc.parentalControlsManager;
+const Signals = imports.misc.signals;
+
+const Main = imports.ui.main;
+
+// In alphabetical order
+const RENAMED_DESKTOP_IDS = {
+ 'baobab.desktop': 'org.gnome.baobab.desktop',
+ 'cheese.desktop': 'org.gnome.Cheese.desktop',
+ 'dconf-editor.desktop': 'ca.desrt.dconf-editor.desktop',
+ 'empathy.desktop': 'org.gnome.Empathy.desktop',
+ 'eog.desktop': 'org.gnome.eog.desktop',
+ 'epiphany.desktop': 'org.gnome.Epiphany.desktop',
+ 'evolution.desktop': 'org.gnome.Evolution.desktop',
+ 'file-roller.desktop': 'org.gnome.FileRoller.desktop',
+ 'five-or-more.desktop': 'org.gnome.five-or-more.desktop',
+ 'four-in-a-row.desktop': 'org.gnome.Four-in-a-row.desktop',
+ 'gcalctool.desktop': 'org.gnome.Calculator.desktop',
+ 'geary.desktop': 'org.gnome.Geary.desktop',
+ 'gedit.desktop': 'org.gnome.gedit.desktop',
+ 'glchess.desktop': 'org.gnome.Chess.desktop',
+ 'glines.desktop': 'org.gnome.five-or-more.desktop',
+ 'gnect.desktop': 'org.gnome.Four-in-a-row.desktop',
+ 'gnibbles.desktop': 'org.gnome.Nibbles.desktop',
+ 'gnobots2.desktop': 'org.gnome.Robots.desktop',
+ 'gnome-boxes.desktop': 'org.gnome.Boxes.desktop',
+ 'gnome-calculator.desktop': 'org.gnome.Calculator.desktop',
+ 'gnome-chess.desktop': 'org.gnome.Chess.desktop',
+ 'gnome-clocks.desktop': 'org.gnome.clocks.desktop',
+ 'gnome-contacts.desktop': 'org.gnome.Contacts.desktop',
+ 'gnome-documents.desktop': 'org.gnome.Documents.desktop',
+ 'gnome-font-viewer.desktop': 'org.gnome.font-viewer.desktop',
+ 'gnome-klotski.desktop': 'org.gnome.Klotski.desktop',
+ 'gnome-nibbles.desktop': 'org.gnome.Nibbles.desktop',
+ 'gnome-mahjongg.desktop': 'org.gnome.Mahjongg.desktop',
+ 'gnome-mines.desktop': 'org.gnome.Mines.desktop',
+ 'gnome-music.desktop': 'org.gnome.Music.desktop',
+ 'gnome-photos.desktop': 'org.gnome.Photos.desktop',
+ 'gnome-robots.desktop': 'org.gnome.Robots.desktop',
+ 'gnome-screenshot.desktop': 'org.gnome.Screenshot.desktop',
+ 'gnome-software.desktop': 'org.gnome.Software.desktop',
+ 'gnome-terminal.desktop': 'org.gnome.Terminal.desktop',
+ 'gnome-tetravex.desktop': 'org.gnome.Tetravex.desktop',
+ 'gnome-tweaks.desktop': 'org.gnome.tweaks.desktop',
+ 'gnome-weather.desktop': 'org.gnome.Weather.desktop',
+ 'gnomine.desktop': 'org.gnome.Mines.desktop',
+ 'gnotravex.desktop': 'org.gnome.Tetravex.desktop',
+ 'gnotski.desktop': 'org.gnome.Klotski.desktop',
+ 'gtali.desktop': 'org.gnome.Tali.desktop',
+ 'iagno.desktop': 'org.gnome.Reversi.desktop',
+ 'nautilus.desktop': 'org.gnome.Nautilus.desktop',
+ 'org.gnome.gnome-2048.desktop': 'org.gnome.TwentyFortyEight.desktop',
+ 'org.gnome.taquin.desktop': 'org.gnome.Taquin.desktop',
+ 'org.gnome.Weather.Application.desktop': 'org.gnome.Weather.desktop',
+ 'polari.desktop': 'org.gnome.Polari.desktop',
+ 'seahorse.desktop': 'org.gnome.seahorse.Application.desktop',
+ 'shotwell.desktop': 'org.gnome.Shotwell.desktop',
+ 'tali.desktop': 'org.gnome.Tali.desktop',
+ 'totem.desktop': 'org.gnome.Totem.desktop',
+ 'evince.desktop': 'org.gnome.Evince.desktop',
+};
+
+class AppFavorites extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ // Filter the apps through the user’s parental controls.
+ this._parentalControlsManager = ParentalControlsManager.getDefault();
+ this._parentalControlsManager.connect('app-filter-changed', () => {
+ this.reload();
+ this.emit('changed');
+ });
+
+ this.FAVORITE_APPS_KEY = 'favorite-apps';
+ this._favorites = {};
+ global.settings.connect(`changed::${this.FAVORITE_APPS_KEY}`, this._onFavsChanged.bind(this));
+ this.reload();
+ }
+
+ _onFavsChanged() {
+ this.reload();
+ this.emit('changed');
+ }
+
+ reload() {
+ let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
+ let appSys = Shell.AppSystem.get_default();
+
+ // Map old desktop file names to the current ones
+ let updated = false;
+ ids = ids.map(id => {
+ let newId = RENAMED_DESKTOP_IDS[id];
+ if (newId !== undefined &&
+ appSys.lookup_app(newId) != null) {
+ updated = true;
+ return newId;
+ }
+ return id;
+ });
+ // ... and write back the updated desktop file names
+ if (updated)
+ global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
+
+ let apps = ids.map(id => appSys.lookup_app(id))
+ .filter(app => app !== null && this._parentalControlsManager.shouldShowApp(app.app_info));
+ this._favorites = {};
+ for (let i = 0; i < apps.length; i++) {
+ let app = apps[i];
+ this._favorites[app.get_id()] = app;
+ }
+ }
+
+ _getIds() {
+ let ret = [];
+ for (let id in this._favorites)
+ ret.push(id);
+ return ret;
+ }
+
+ getFavoriteMap() {
+ return this._favorites;
+ }
+
+ getFavorites() {
+ let ret = [];
+ for (let id in this._favorites)
+ ret.push(this._favorites[id]);
+ return ret;
+ }
+
+ isFavorite(appId) {
+ return appId in this._favorites;
+ }
+
+ _addFavorite(appId, pos) {
+ if (appId in this._favorites)
+ return false;
+
+ let app = Shell.AppSystem.get_default().lookup_app(appId);
+
+ if (!app)
+ return false;
+
+ if (!this._parentalControlsManager.shouldShowApp(app.app_info))
+ return false;
+
+ let ids = this._getIds();
+ if (pos == -1)
+ ids.push(appId);
+ else
+ ids.splice(pos, 0, appId);
+ global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
+ return true;
+ }
+
+ addFavoriteAtPos(appId, pos) {
+ if (!this._addFavorite(appId, pos))
+ return;
+
+ let app = Shell.AppSystem.get_default().lookup_app(appId);
+
+ let msg = _('%s has been pinned to the dash.').format(app.get_name());
+ Main.overview.setMessage(msg, {
+ forFeedback: true,
+ undoCallback: () => this._removeFavorite(appId),
+ });
+ }
+
+ addFavorite(appId) {
+ this.addFavoriteAtPos(appId, -1);
+ }
+
+ moveFavoriteToPos(appId, pos) {
+ this._removeFavorite(appId);
+ this._addFavorite(appId, pos);
+ }
+
+ _removeFavorite(appId) {
+ if (!(appId in this._favorites))
+ return false;
+
+ let ids = this._getIds().filter(id => id != appId);
+ global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
+ return true;
+ }
+
+ removeFavorite(appId) {
+ let ids = this._getIds();
+ let pos = ids.indexOf(appId);
+
+ let app = this._favorites[appId];
+ if (!this._removeFavorite(appId))
+ return;
+
+ let msg = _('%s has been unpinned from the dash.').format(app.get_name());
+ Main.overview.setMessage(msg, {
+ forFeedback: true,
+ undoCallback: () => this._addFavorite(appId, pos),
+ });
+ }
+}
+
+var appFavoritesInstance = null;
+function getAppFavorites() {
+ if (appFavoritesInstance == null)
+ appFavoritesInstance = new AppFavorites();
+ return appFavoritesInstance;
+}
diff --git a/js/ui/appMenu.js b/js/ui/appMenu.js
new file mode 100644
index 0000000..010fdb3
--- /dev/null
+++ b/js/ui/appMenu.js
@@ -0,0 +1,287 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported AppMenu */
+const { Clutter, Gio, GLib, Meta, Shell, St } = imports.gi;
+
+const AppFavorites = imports.ui.appFavorites;
+const Main = imports.ui.main;
+const ParentalControlsManager = imports.misc.parentalControlsManager;
+const PopupMenu = imports.ui.popupMenu;
+
+var AppMenu = class AppMenu extends PopupMenu.PopupMenu {
+ /**
+ * @param {Clutter.Actor} sourceActor - actor the menu is attached to
+ * @param {St.Side} side - arrow side
+ * @param {object} params - options
+ * @param {bool} params.favoritesSection - show items to add/remove favorite
+ * @param {bool} params.showSingleWindow - show window section for a single window
+ */
+ constructor(sourceActor, side = St.Side.TOP, params = {}) {
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
+ if (side === St.Side.LEFT)
+ side = St.Side.RIGHT;
+ else if (side === St.Side.RIGHT)
+ side = St.Side.LEFT;
+ }
+
+ super(sourceActor, 0.5, side);
+
+ this.actor.add_style_class_name('app-menu');
+
+ const {
+ favoritesSection = false,
+ showSingleWindows = false,
+ } = params;
+
+ this._app = null;
+ this._appSystem = Shell.AppSystem.get_default();
+ this._parentalControlsManager = ParentalControlsManager.getDefault();
+ this._appFavorites = AppFavorites.getAppFavorites();
+ this._enableFavorites = favoritesSection;
+ this._showSingleWindows = showSingleWindows;
+
+ this._windowsChangedId = 0;
+ this._updateWindowsLaterId = 0;
+
+ /* Translators: This is the heading of a list of open windows */
+ this._openWindowsHeader = new PopupMenu.PopupSeparatorMenuItem(_('Open Windows'));
+ this.addMenuItem(this._openWindowsHeader);
+
+ this._windowSection = new PopupMenu.PopupMenuSection();
+ this.addMenuItem(this._windowSection);
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._newWindowItem = this.addAction(_('New Window'), () => {
+ this._animateLaunch();
+ this._app.open_new_window(-1);
+ Main.overview.hide();
+ });
+
+ this._actionSection = new PopupMenu.PopupMenuSection();
+ this.addMenuItem(this._actionSection);
+
+ this._onGpuMenuItem = this.addAction('', () => {
+ this._animateLaunch();
+ this._app.launch(0, -1, this._getNonDefaultLaunchGpu());
+ Main.overview.hide();
+ });
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._toggleFavoriteItem = this.addAction('', () => {
+ const appId = this._app.get_id();
+ if (this._appFavorites.isFavorite(appId))
+ this._appFavorites.removeFavorite(appId);
+ else
+ this._appFavorites.addFavorite(appId);
+ });
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._detailsItem = this.addAction(_('Show Details'), async () => {
+ const id = this._app.get_id();
+ const args = GLib.Variant.new('(ss)', [id, '']);
+ const bus = await Gio.DBus.get(Gio.BusType.SESSION, null);
+ bus.call(
+ 'org.gnome.Software',
+ '/org/gnome/Software',
+ 'org.gtk.Actions', 'Activate',
+ new GLib.Variant('(sava{sv})', ['details', [args], null]),
+ null, 0, -1, null);
+ Main.overview.hide();
+ });
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._quitItem =
+ this.addAction(_('Quit'), () => this._app.request_quit());
+
+ this._appSystem.connectObject(
+ 'installed-changed', () => this._updateDetailsVisibility(),
+ 'app-state-changed', this._onAppStateChanged.bind(this),
+ this.actor);
+
+ this._parentalControlsManager.connectObject(
+ 'app-filter-changed', () => this._updateFavoriteItem(), this.actor);
+
+ this._appFavorites.connectObject(
+ 'changed', () => this._updateFavoriteItem(), this.actor);
+
+ global.settings.connectObject(
+ 'writable-changed::favorite-apps', () => this._updateFavoriteItem(),
+ this.actor);
+
+ global.connectObject(
+ 'notify::switcheroo-control', () => this._updateGpuItem(),
+ this.actor);
+
+ this._updateQuitItem();
+ this._updateFavoriteItem();
+ this._updateGpuItem();
+ this._updateDetailsVisibility();
+ }
+
+ _onAppStateChanged(sys, app) {
+ if (this._app !== app)
+ return;
+
+ this._updateQuitItem();
+ this._updateNewWindowItem();
+ this._updateGpuItem();
+ }
+
+ _updateQuitItem() {
+ this._quitItem.visible = this._app?.state === Shell.AppState.RUNNING;
+ }
+
+ _updateNewWindowItem() {
+ const actions = this._app?.appInfo?.list_actions() ?? [];
+ this._newWindowItem.visible =
+ this._app?.can_open_new_window() && !actions.includes('new-window');
+ }
+
+ _updateFavoriteItem() {
+ const appInfo = this._app?.app_info;
+ const canFavorite = appInfo &&
+ this._enableFavorites &&
+ global.settings.is_writable('favorite-apps') &&
+ this._parentalControlsManager.shouldShowApp(appInfo);
+
+ this._toggleFavoriteItem.visible = canFavorite;
+
+ if (!canFavorite)
+ return;
+
+ const { id } = this._app;
+ this._toggleFavoriteItem.label.text = this._appFavorites.isFavorite(id)
+ ? _('Unpin')
+ : _('Pin to Dash');
+ }
+
+ _updateGpuItem() {
+ const proxy = global.get_switcheroo_control();
+ const hasDualGpu = proxy?.get_cached_property('HasDualGpu')?.unpack();
+
+ const showItem =
+ this._app?.state === Shell.AppState.STOPPED && hasDualGpu;
+
+ this._onGpuMenuItem.visible = showItem;
+
+ if (!showItem)
+ return;
+
+ const launchGpu = this._getNonDefaultLaunchGpu();
+ this._onGpuMenuItem.label.text = launchGpu === Shell.AppLaunchGpu.DEFAULT
+ ? _('Launch using Integrated Graphics Card')
+ : _('Launch using Discrete Graphics Card');
+ }
+
+ _updateDetailsVisibility() {
+ const sw = this._appSystem.lookup_app('org.gnome.Software.desktop');
+ this._detailsItem.visible = sw !== null;
+ }
+
+ _animateLaunch() {
+ if (this.sourceActor.animateLaunch)
+ this.sourceActor.animateLaunch();
+ }
+
+ _getNonDefaultLaunchGpu() {
+ return this._app.appInfo.get_boolean('PrefersNonDefaultGPU')
+ ? Shell.AppLaunchGpu.DEFAULT
+ : Shell.AppLaunchGpu.DISCRETE;
+ }
+
+ /** */
+ destroy() {
+ super.destroy();
+
+ this.setApp(null);
+ }
+
+ /**
+ * @returns {bool} - true if the menu is empty
+ */
+ isEmpty() {
+ if (!this._app)
+ return true;
+ return super.isEmpty();
+ }
+
+ /**
+ * @param {Shell.App} app - the app the menu represents
+ */
+ setApp(app) {
+ if (this._app === app)
+ return;
+
+ this._app?.disconnectObject(this);
+
+ this._app = app;
+
+ this._app?.connectObject('windows-changed',
+ () => this._queueUpdateWindowsSection(), this);
+
+ this._updateWindowsSection();
+
+ const appInfo = app?.app_info;
+ const actions = appInfo?.list_actions() ?? [];
+
+ this._actionSection.removeAll();
+ actions.forEach(action => {
+ const label = appInfo.get_action_name(action);
+ this._actionSection.addAction(label, event => {
+ if (action === 'new-window')
+ this._animateLaunch();
+
+ this._app.launch_action(action, event.get_time(), -1);
+ Main.overview.hide();
+ });
+ });
+
+ this._updateQuitItem();
+ this._updateNewWindowItem();
+ this._updateFavoriteItem();
+ this._updateGpuItem();
+ }
+
+ _queueUpdateWindowsSection() {
+ if (this._updateWindowsLaterId)
+ return;
+
+ this._updateWindowsLaterId = Meta.later_add(
+ Meta.LaterType.BEFORE_REDRAW, () => {
+ this._updateWindowsSection();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _updateWindowsSection() {
+ if (this._updateWindowsLaterId)
+ Meta.later_remove(this._updateWindowsLaterId);
+ this._updateWindowsLaterId = 0;
+
+ this._windowSection.removeAll();
+ this._openWindowsHeader.hide();
+
+ if (!this._app)
+ return;
+
+ const minWindows = this._showSingleWindows ? 1 : 2;
+ const windows = this._app.get_windows().filter(w => !w.skip_taskbar);
+ if (windows.length < minWindows)
+ return;
+
+ this._openWindowsHeader.show();
+
+ windows.forEach(window => {
+ const title = window.title || this._app.get_name();
+ const item = this._windowSection.addAction(title, event => {
+ Main.activateWindow(window, event.get_time());
+ });
+ window.connectObject('notify::title', () => {
+ item.label.text = window.title || this._app.get_name();
+ }, item);
+ });
+ }
+};
diff --git a/js/ui/audioDeviceSelection.js b/js/ui/audioDeviceSelection.js
new file mode 100644
index 0000000..e284772
--- /dev/null
+++ b/js/ui/audioDeviceSelection.js
@@ -0,0 +1,207 @@
+/* exported AudioDeviceSelectionDBus */
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+var AudioDevice = {
+ HEADPHONES: 1 << 0,
+ HEADSET: 1 << 1,
+ MICROPHONE: 1 << 2,
+};
+
+const AudioDeviceSelectionIface = loadInterfaceXML('org.gnome.Shell.AudioDeviceSelection');
+
+var AudioDeviceSelectionDialog = GObject.registerClass({
+ Signals: { 'device-selected': { param_types: [GObject.TYPE_UINT] } },
+}, class AudioDeviceSelectionDialog extends ModalDialog.ModalDialog {
+ _init(devices) {
+ super._init({ styleClass: 'audio-device-selection-dialog' });
+
+ this._deviceItems = {};
+
+ this._buildLayout();
+
+ if (devices & AudioDevice.HEADPHONES)
+ this._addDevice(AudioDevice.HEADPHONES);
+ if (devices & AudioDevice.HEADSET)
+ this._addDevice(AudioDevice.HEADSET);
+ if (devices & AudioDevice.MICROPHONE)
+ this._addDevice(AudioDevice.MICROPHONE);
+
+ if (this._selectionBox.get_n_children() < 2)
+ throw new Error('Too few devices for a selection');
+ }
+
+ _buildLayout() {
+ let content = new Dialog.MessageDialogContent({
+ title: _('Select Audio Device'),
+ });
+
+ this._selectionBox = new St.BoxLayout({
+ style_class: 'audio-selection-box',
+ x_align: Clutter.ActorAlign.CENTER,
+ x_expand: true,
+ });
+ content.add_child(this._selectionBox);
+
+ this.contentLayout.add_child(content);
+
+ if (Main.sessionMode.allowSettings) {
+ this.addButton({
+ action: this._openSettings.bind(this),
+ label: _('Sound Settings'),
+ });
+ }
+ this.addButton({
+ action: () => this.close(),
+ label: _('Cancel'),
+ key: Clutter.KEY_Escape,
+ });
+ }
+
+ _getDeviceLabel(device) {
+ switch (device) {
+ case AudioDevice.HEADPHONES:
+ return _("Headphones");
+ case AudioDevice.HEADSET:
+ return _("Headset");
+ case AudioDevice.MICROPHONE:
+ return _("Microphone");
+ default:
+ return null;
+ }
+ }
+
+ _getDeviceIcon(device) {
+ switch (device) {
+ case AudioDevice.HEADPHONES:
+ return 'audio-headphones-symbolic';
+ case AudioDevice.HEADSET:
+ return 'audio-headset-symbolic';
+ case AudioDevice.MICROPHONE:
+ return 'audio-input-microphone-symbolic';
+ default:
+ return null;
+ }
+ }
+
+ _addDevice(device) {
+ const box = new St.BoxLayout({
+ style_class: 'audio-selection-device-box',
+ vertical: true,
+ });
+ box.connect('notify::height', () => {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ box.width = box.height;
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+
+ const icon = new St.Icon({
+ style_class: 'audio-selection-device-icon',
+ icon_name: this._getDeviceIcon(device),
+ });
+ box.add(icon);
+
+ const label = new St.Label({
+ style_class: 'audio-selection-device-label',
+ text: this._getDeviceLabel(device),
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add(label);
+
+ const button = new St.Button({
+ style_class: 'audio-selection-device',
+ can_focus: true,
+ child: box,
+ });
+ this._selectionBox.add(button);
+
+ button.connect('clicked', () => {
+ this.emit('device-selected', device);
+ this.close();
+ Main.overview.hide();
+ });
+ }
+
+ _openSettings() {
+ let desktopFile = 'gnome-sound-panel.desktop';
+ let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
+
+ if (!app) {
+ log(`Settings panel for desktop file ${desktopFile} could not be loaded!`);
+ return;
+ }
+
+ this.close();
+ Main.overview.hide();
+ app.activate();
+ }
+});
+
+var AudioDeviceSelectionDBus = class AudioDeviceSelectionDBus {
+ constructor() {
+ this._audioSelectionDialog = null;
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AudioDeviceSelectionIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/AudioDeviceSelection');
+
+ Gio.DBus.session.own_name('org.gnome.Shell.AudioDeviceSelection', Gio.BusNameOwnerFlags.REPLACE, null, null);
+ }
+
+ _onDialogClosed() {
+ this._audioSelectionDialog = null;
+ }
+
+ _onDeviceSelected(dialog, device) {
+ let connection = this._dbusImpl.get_connection();
+ let info = this._dbusImpl.get_info();
+ const deviceName = Object.keys(AudioDevice)
+ .filter(dev => AudioDevice[dev] === device)[0].toLowerCase();
+ connection.emit_signal(this._audioSelectionDialog._sender,
+ this._dbusImpl.get_object_path(),
+ info ? info.name : null,
+ 'DeviceSelected',
+ GLib.Variant.new('(s)', [deviceName]));
+ }
+
+ OpenAsync(params, invocation) {
+ if (this._audioSelectionDialog) {
+ invocation.return_value(null);
+ return;
+ }
+
+ let [deviceNames] = params;
+ let devices = 0;
+ deviceNames.forEach(n => (devices |= AudioDevice[n.toUpperCase()]));
+
+ let dialog;
+ try {
+ dialog = new AudioDeviceSelectionDialog(devices);
+ } catch (e) {
+ invocation.return_value(null);
+ return;
+ }
+ dialog._sender = invocation.get_sender();
+
+ dialog.connect('closed', this._onDialogClosed.bind(this));
+ dialog.connect('device-selected',
+ this._onDeviceSelected.bind(this));
+ dialog.open();
+
+ this._audioSelectionDialog = dialog;
+ invocation.return_value(null);
+ }
+
+ CloseAsync(params, invocation) {
+ if (this._audioSelectionDialog &&
+ this._audioSelectionDialog._sender == invocation.get_sender())
+ this._audioSelectionDialog.close();
+
+ invocation.return_value(null);
+ }
+};
diff --git a/js/ui/background.js b/js/ui/background.js
new file mode 100644
index 0000000..829ffb4
--- /dev/null
+++ b/js/ui/background.js
@@ -0,0 +1,842 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SystemBackground, BackgroundManager */
+
+// READ THIS FIRST
+// Background handling is a maze of objects, both objects in this file, and
+// also objects inside Mutter. They all have a role.
+//
+// BackgroundManager
+// The only object that other parts of GNOME Shell deal with; a
+// BackgroundManager creates background actors and adds them to
+// the specified container. When the background is changed by the
+// user it will fade out the old actor and fade in the new actor.
+// (This is separate from the fading for an animated background,
+// since using two actors is quite inefficient.)
+//
+// MetaBackgroundImage
+// An object represented an image file that will be used for drawing
+// the background. MetaBackgroundImage objects asynchronously load,
+// so they are first created in an unloaded state, then later emit
+// a ::loaded signal when the Cogl object becomes available.
+//
+// MetaBackgroundImageCache
+// A cache from filename to MetaBackgroundImage.
+//
+// BackgroundSource
+// An object that is created for each GSettings schema (separate
+// settings schemas are used for the lock screen and main background),
+// and holds a reference to shared Background objects.
+//
+// MetaBackground
+// Holds the specification of a background - a background color
+// or gradient and one or two images blended together.
+//
+// Background
+// JS delegate object that Connects a MetaBackground to the GSettings
+// schema for the background.
+//
+// Animation
+// A helper object that handles loading a XML-based animation; it is a
+// wrapper for GnomeDesktop.BGSlideShow
+//
+// MetaBackgroundActor
+// An actor that draws the background for a single monitor
+//
+// BackgroundCache
+// A cache of Settings schema => BackgroundSource and of a single Animation.
+// Also used to share file monitors.
+//
+// A static image, background color or gradient is relatively straightforward. The
+// calling code creates a separate BackgroundManager for each monitor. Since they
+// are created for the same GSettings schema, they will use the same BackgroundSource
+// object, which provides a single Background and correspondingly a single
+// MetaBackground object.
+//
+// BackgroundManager BackgroundManager
+// | \ / |
+// | BackgroundSource | looked up in BackgroundCache
+// | | |
+// | Background |
+// | | |
+// MetaBackgroundActor | MetaBackgroundActor
+// \ | /
+// `------- MetaBackground ------'
+// |
+// MetaBackgroundImage looked up in MetaBackgroundImageCache
+//
+// The animated case is tricker because the animation XML file can specify different
+// files for different monitor resolutions and aspect ratios. For this reason,
+// the BackgroundSource provides different Background share a single Animation object,
+// which tracks the animation, but use different MetaBackground objects. In the
+// common case, the different MetaBackground objects will be created for the
+// same filename and look up the *same* MetaBackgroundImage object, so there is
+// little wasted memory:
+//
+// BackgroundManager BackgroundManager
+// | \ / |
+// | BackgroundSource | looked up in BackgroundCache
+// | / \ |
+// | Background Background |
+// | | \ / | |
+// | | Animation | | looked up in BackgroundCache
+// MetaBackgroundA|tor Me|aBackgroundActor
+// \ | | /
+// MetaBackground MetaBackground
+// \ /
+// MetaBackgroundImage looked up in MetaBackgroundImageCache
+// MetaBackgroundImage
+//
+// But the case of different filenames and different background images
+// is possible as well:
+// ....
+// MetaBackground MetaBackground
+// | |
+// MetaBackgroundImage MetaBackgroundImage
+// MetaBackgroundImage MetaBackgroundImage
+
+const { Clutter, GDesktopEnums, Gio, GLib, GObject, GnomeDesktop, Meta } = imports.gi;
+const Signals = imports.misc.signals;
+
+const LoginManager = imports.misc.loginManager;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+Gio._promisify(Gio.File.prototype, 'query_info_async');
+
+var DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
+
+const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
+const PRIMARY_COLOR_KEY = 'primary-color';
+const SECONDARY_COLOR_KEY = 'secondary-color';
+const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
+const BACKGROUND_STYLE_KEY = 'picture-options';
+const PICTURE_URI_KEY = 'picture-uri';
+const PICTURE_URI_DARK_KEY = 'picture-uri-dark';
+
+const INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
+const COLOR_SCHEME_KEY = 'color-scheme';
+
+var FADE_ANIMATION_TIME = 1000;
+
+// These parameters affect how often we redraw.
+// The first is how different (percent crossfaded) the slide show
+// has to look before redrawing and the second is the minimum
+// frequency (in seconds) we're willing to wake up
+var ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
+var ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
+
+let _backgroundCache = null;
+
+function _fileEqual0(file1, file2) {
+ if (file1 == file2)
+ return true;
+
+ if (!file1 || !file2)
+ return false;
+
+ return file1.equal(file2);
+}
+
+var BackgroundCache = class BackgroundCache extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._fileMonitors = {};
+ this._backgroundSources = {};
+ this._animations = {};
+ }
+
+ monitorFile(file) {
+ let key = file.hash();
+ if (this._fileMonitors[key])
+ return;
+
+ let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
+ monitor.connect('changed',
+ (obj, theFile, otherFile, eventType) => {
+ // Ignore CHANGED and CREATED events, since in both cases
+ // we'll get a CHANGES_DONE_HINT event when done.
+ if (eventType != Gio.FileMonitorEvent.CHANGED &&
+ eventType != Gio.FileMonitorEvent.CREATED)
+ this.emit('file-changed', file);
+ });
+
+ this._fileMonitors[key] = monitor;
+ }
+
+ getAnimation(params) {
+ params = Params.parse(params, {
+ file: null,
+ settingsSchema: null,
+ onLoaded: null,
+ });
+
+ let animation = this._animations[params.settingsSchema];
+ if (animation && _fileEqual0(animation.file, params.file)) {
+ if (params.onLoaded) {
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ params.onLoaded(this._animations[params.settingsSchema]);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
+ }
+ return;
+ }
+
+ animation = new Animation({ file: params.file });
+
+ animation.load_async(null, () => {
+ this._animations[params.settingsSchema] = animation;
+
+ if (params.onLoaded) {
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ params.onLoaded(this._animations[params.settingsSchema]);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
+ }
+ });
+ }
+
+ getBackgroundSource(layoutManager, settingsSchema) {
+ // The layoutManager is always the same one; we pass in it since
+ // Main.layoutManager may not be set yet
+
+ if (!(settingsSchema in this._backgroundSources)) {
+ this._backgroundSources[settingsSchema] = new BackgroundSource(layoutManager, settingsSchema);
+ this._backgroundSources[settingsSchema]._useCount = 1;
+ } else {
+ this._backgroundSources[settingsSchema]._useCount++;
+ }
+
+ return this._backgroundSources[settingsSchema];
+ }
+
+ releaseBackgroundSource(settingsSchema) {
+ if (settingsSchema in this._backgroundSources) {
+ let source = this._backgroundSources[settingsSchema];
+ source._useCount--;
+ if (source._useCount == 0) {
+ delete this._backgroundSources[settingsSchema];
+ source.destroy();
+ }
+ }
+ }
+};
+
+function getBackgroundCache() {
+ if (!_backgroundCache)
+ _backgroundCache = new BackgroundCache();
+ return _backgroundCache;
+}
+
+var Background = GObject.registerClass({
+ Signals: { 'loaded': {}, 'bg-changed': {} },
+}, class Background extends Meta.Background {
+ _init(params) {
+ params = Params.parse(params, {
+ monitorIndex: 0,
+ layoutManager: Main.layoutManager,
+ settings: null,
+ file: null,
+ style: null,
+ });
+
+ super._init({ meta_display: global.display });
+
+ this._settings = params.settings;
+ this._file = params.file;
+ this._style = params.style;
+ this._monitorIndex = params.monitorIndex;
+ this._layoutManager = params.layoutManager;
+ this._fileWatches = {};
+ this._cancellable = new Gio.Cancellable();
+ this.isLoaded = false;
+
+ this._interfaceSettings = new Gio.Settings({ schema_id: INTERFACE_SCHEMA });
+
+ this._clock = new GnomeDesktop.WallClock();
+ this._clock.connectObject('notify::timezone',
+ () => {
+ if (this._animation)
+ this._loadAnimation(this._animation.file);
+ }, this);
+
+ let loginManager = LoginManager.getLoginManager();
+ loginManager.connectObject('prepare-for-sleep',
+ (lm, aboutToSuspend) => {
+ if (aboutToSuspend)
+ return;
+ this._refreshAnimation();
+ }, this);
+
+ this._settings.connectObject('changed',
+ this._emitChangedSignal.bind(this), this);
+
+ this._interfaceSettings.connectObject(`changed::${COLOR_SCHEME_KEY}`,
+ this._emitChangedSignal.bind(this), this);
+
+ this._load();
+ }
+
+ destroy() {
+ this._cancellable.cancel();
+ this._removeAnimationTimeout();
+
+ let i;
+ let keys = Object.keys(this._fileWatches);
+ for (i = 0; i < keys.length; i++)
+ this._cache.disconnect(this._fileWatches[keys[i]]);
+
+ this._fileWatches = null;
+
+ this._clock.disconnectObject(this);
+ this._clock = null;
+
+ LoginManager.getLoginManager().disconnectObject(this);
+ this._settings.disconnectObject(this);
+ this._interfaceSettings.disconnectObject(this);
+
+ if (this._changedIdleId) {
+ GLib.source_remove(this._changedIdleId);
+ this._changedIdleId = 0;
+ }
+ }
+
+ _emitChangedSignal() {
+ if (this._changedIdleId)
+ return;
+
+ this._changedIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this._changedIdleId = 0;
+ this.emit('bg-changed');
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._changedIdleId,
+ '[gnome-shell] Background._emitChangedSignal');
+ }
+
+ updateResolution() {
+ if (this._animation)
+ this._refreshAnimation();
+ }
+
+ _refreshAnimation() {
+ if (!this._animation)
+ return;
+
+ this._removeAnimationTimeout();
+ this._updateAnimation();
+ }
+
+ _setLoaded() {
+ if (this.isLoaded)
+ return;
+
+ this.isLoaded = true;
+ if (this._cancellable?.is_cancelled())
+ return;
+
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this.emit('loaded');
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] Background._setLoaded Idle');
+ }
+
+ _loadPattern() {
+ let colorString, res_, color, secondColor;
+
+ colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
+ [res_, color] = Clutter.Color.from_string(colorString);
+ colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
+ [res_, secondColor] = Clutter.Color.from_string(colorString);
+
+ let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
+
+ if (shadingType == GDesktopEnums.BackgroundShading.SOLID)
+ this.set_color(color);
+ else
+ this.set_gradient(shadingType, color, secondColor);
+ }
+
+ _watchFile(file) {
+ let key = file.hash();
+ if (this._fileWatches[key])
+ return;
+
+ this._cache.monitorFile(file);
+ let signalId = this._cache.connect('file-changed',
+ (cache, changedFile) => {
+ if (changedFile.equal(file)) {
+ let imageCache = Meta.BackgroundImageCache.get_default();
+ imageCache.purge(changedFile);
+ this._emitChangedSignal();
+ }
+ });
+ this._fileWatches[key] = signalId;
+ }
+
+ _removeAnimationTimeout() {
+ if (this._updateAnimationTimeoutId) {
+ GLib.source_remove(this._updateAnimationTimeoutId);
+ this._updateAnimationTimeoutId = 0;
+ }
+ }
+
+ _updateAnimation() {
+ this._updateAnimationTimeoutId = 0;
+
+ this._animation.update(this._layoutManager.monitors[this._monitorIndex]);
+ let files = this._animation.keyFrameFiles;
+
+ let finish = () => {
+ this._setLoaded();
+ if (files.length > 1) {
+ this.set_blend(files[0], files[1],
+ this._animation.transitionProgress,
+ this._style);
+ } else if (files.length > 0) {
+ this.set_file(files[0], this._style);
+ } else {
+ this.set_file(null, this._style);
+ }
+ this._queueUpdateAnimation();
+ };
+
+ let cache = Meta.BackgroundImageCache.get_default();
+ let numPendingImages = files.length;
+ for (let i = 0; i < files.length; i++) {
+ this._watchFile(files[i]);
+ let image = cache.load(files[i]);
+ if (image.is_loaded()) {
+ numPendingImages--;
+ if (numPendingImages == 0)
+ finish();
+ } else {
+ // eslint-disable-next-line no-loop-func
+ let id = image.connect('loaded', () => {
+ image.disconnect(id);
+ numPendingImages--;
+ if (numPendingImages == 0)
+ finish();
+ });
+ }
+ }
+ }
+
+ _queueUpdateAnimation() {
+ if (this._updateAnimationTimeoutId != 0)
+ return;
+
+ if (!this._cancellable || this._cancellable.is_cancelled())
+ return;
+
+ if (!this._animation.transitionDuration)
+ return;
+
+ let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
+ let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
+
+ let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
+ timePerStep);
+
+ if (interval > GLib.MAXUINT32)
+ return;
+
+ this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ interval,
+ () => {
+ this._updateAnimationTimeoutId = 0;
+ this._updateAnimation();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._updateAnimationTimeoutId, '[gnome-shell] this._updateAnimation');
+ }
+
+ _loadAnimation(file) {
+ this._cache.getAnimation({
+ file,
+ settingsSchema: this._settings.schema_id,
+ onLoaded: animation => {
+ this._animation = animation;
+
+ if (!this._animation || this._cancellable.is_cancelled()) {
+ this._setLoaded();
+ return;
+ }
+
+ this._updateAnimation();
+ this._watchFile(file);
+ },
+ });
+ }
+
+ _loadImage(file) {
+ this.set_file(file, this._style);
+ this._watchFile(file);
+
+ let cache = Meta.BackgroundImageCache.get_default();
+ let image = cache.load(file);
+ if (image.is_loaded()) {
+ this._setLoaded();
+ } else {
+ let id = image.connect('loaded', () => {
+ this._setLoaded();
+ image.disconnect(id);
+ });
+ }
+ }
+
+ async _loadFile(file) {
+ let info;
+ try {
+ info = await file.query_info_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ Gio.FileQueryInfoFlags.NONE,
+ 0,
+ this._cancellable);
+ } catch (e) {
+ this._setLoaded();
+ return;
+ }
+
+ const contentType = info.get_content_type();
+ if (contentType === 'application/xml')
+ this._loadAnimation(file);
+ else
+ this._loadImage(file);
+ }
+
+ _load() {
+ this._cache = getBackgroundCache();
+
+ this._loadPattern();
+
+ if (!this._file) {
+ this._setLoaded();
+ return;
+ }
+
+ this._loadFile(this._file);
+ }
+});
+
+let _systemBackground;
+
+var SystemBackground = GObject.registerClass({
+ Signals: { 'loaded': {} },
+}, class SystemBackground extends Meta.BackgroundActor {
+ _init() {
+ if (_systemBackground == null) {
+ _systemBackground = new Meta.Background({ meta_display: global.display });
+ _systemBackground.set_color(DEFAULT_BACKGROUND_COLOR);
+ }
+
+ super._init({
+ meta_display: global.display,
+ monitor: 0,
+ });
+ this.content.background = _systemBackground;
+
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this.emit('loaded');
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] SystemBackground.loaded');
+ }
+});
+
+var BackgroundSource = class BackgroundSource {
+ constructor(layoutManager, settingsSchema) {
+ // Allow override the background image setting for performance testing
+ this._layoutManager = layoutManager;
+ this._overrideImage = GLib.getenv('SHELL_BACKGROUND_IMAGE');
+ this._settings = new Gio.Settings({ schema_id: settingsSchema });
+ this._backgrounds = [];
+
+ let monitorManager = Meta.MonitorManager.get();
+ this._monitorsChangedId =
+ monitorManager.connect('monitors-changed',
+ this._onMonitorsChanged.bind(this));
+
+ this._interfaceSettings = new Gio.Settings({ schema_id: INTERFACE_SCHEMA });
+ }
+
+ _onMonitorsChanged() {
+ for (let monitorIndex in this._backgrounds) {
+ let background = this._backgrounds[monitorIndex];
+
+ if (monitorIndex < this._layoutManager.monitors.length) {
+ background.updateResolution();
+ } else {
+ background.disconnect(background._changedId);
+ background.destroy();
+ delete this._backgrounds[monitorIndex];
+ }
+ }
+ }
+
+ getBackground(monitorIndex) {
+ let file = null;
+ let style;
+
+ // We don't watch changes to settings here,
+ // instead we rely on Background to watch those
+ // and emit 'bg-changed' at the right time
+
+ if (this._overrideImage != null) {
+ file = Gio.File.new_for_path(this._overrideImage);
+ style = GDesktopEnums.BackgroundStyle.ZOOM; // Hardcode
+ } else {
+ style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
+ if (style != GDesktopEnums.BackgroundStyle.NONE) {
+ const colorScheme = this._interfaceSettings.get_enum('color-scheme');
+ const uri = this._settings.get_string(
+ colorScheme === GDesktopEnums.ColorScheme.PREFER_DARK
+ ? PICTURE_URI_DARK_KEY
+ : PICTURE_URI_KEY);
+
+ file = Gio.File.new_for_commandline_arg(uri);
+ }
+ }
+
+ // Animated backgrounds are (potentially) per-monitor, since
+ // they can have variants that depend on the aspect ratio and
+ // size of the monitor; for other backgrounds we can use the
+ // same background object for all monitors.
+ if (file == null || !file.get_basename().endsWith('.xml'))
+ monitorIndex = 0;
+
+ if (!(monitorIndex in this._backgrounds)) {
+ let background = new Background({
+ monitorIndex,
+ layoutManager: this._layoutManager,
+ settings: this._settings,
+ file,
+ style,
+ });
+
+ background._changedId = background.connect('bg-changed', () => {
+ background.disconnect(background._changedId);
+ background.destroy();
+ delete this._backgrounds[monitorIndex];
+ });
+
+ this._backgrounds[monitorIndex] = background;
+ }
+
+ return this._backgrounds[monitorIndex];
+ }
+
+ destroy() {
+ let monitorManager = Meta.MonitorManager.get();
+ monitorManager.disconnect(this._monitorsChangedId);
+
+ for (let monitorIndex in this._backgrounds) {
+ let background = this._backgrounds[monitorIndex];
+ background.disconnect(background._changedId);
+ background.destroy();
+ }
+
+ this._backgrounds = null;
+ }
+};
+
+var Animation = GObject.registerClass(
+class Animation extends GnomeDesktop.BGSlideShow {
+ _init(params) {
+ super._init(params);
+
+ this.keyFrameFiles = [];
+ this.transitionProgress = 0.0;
+ this.transitionDuration = 0.0;
+ this.loaded = false;
+ }
+
+ // eslint-disable-next-line camelcase
+ load_async(cancellable, callback) {
+ super.load_async(cancellable, () => {
+ this.loaded = true;
+
+ callback?.();
+ });
+ }
+
+ update(monitor) {
+ this.keyFrameFiles = [];
+
+ if (this.get_num_slides() < 1)
+ return;
+
+ let [progress, duration, isFixed_, filename1, filename2] =
+ this.get_current_slide(monitor.width, monitor.height);
+
+ this.transitionDuration = duration;
+ this.transitionProgress = progress;
+
+ if (filename1)
+ this.keyFrameFiles.push(Gio.File.new_for_path(filename1));
+
+ if (filename2)
+ this.keyFrameFiles.push(Gio.File.new_for_path(filename2));
+ }
+});
+
+var BackgroundManager = class BackgroundManager extends Signals.EventEmitter {
+ constructor(params) {
+ super();
+ params = Params.parse(params, {
+ container: null,
+ layoutManager: Main.layoutManager,
+ monitorIndex: null,
+ vignette: false,
+ controlPosition: true,
+ settingsSchema: BACKGROUND_SCHEMA,
+ useContentSize: true,
+ });
+
+ let cache = getBackgroundCache();
+ this._settingsSchema = params.settingsSchema;
+ this._backgroundSource = cache.getBackgroundSource(params.layoutManager, params.settingsSchema);
+
+ this._container = params.container;
+ this._layoutManager = params.layoutManager;
+ this._vignette = params.vignette;
+ this._monitorIndex = params.monitorIndex;
+ this._controlPosition = params.controlPosition;
+ this._useContentSize = params.useContentSize;
+
+ this.backgroundActor = this._createBackgroundActor();
+ this._newBackgroundActor = null;
+ }
+
+ destroy() {
+ let cache = getBackgroundCache();
+ cache.releaseBackgroundSource(this._settingsSchema);
+ this._backgroundSource = null;
+
+ if (this._newBackgroundActor) {
+ this._newBackgroundActor.destroy();
+ this._newBackgroundActor = null;
+ }
+
+ if (this.backgroundActor) {
+ this.backgroundActor.destroy();
+ this.backgroundActor = null;
+ }
+ }
+
+ _swapBackgroundActor() {
+ let oldBackgroundActor = this.backgroundActor;
+ this.backgroundActor = this._newBackgroundActor;
+ this._newBackgroundActor = null;
+ this.emit('changed');
+
+ if (Main.layoutManager.screenTransition.visible) {
+ oldBackgroundActor.destroy();
+ return;
+ }
+
+ oldBackgroundActor.ease({
+ opacity: 0,
+ duration: FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => oldBackgroundActor.destroy(),
+ });
+ }
+
+ _updateBackgroundActor() {
+ if (this._newBackgroundActor) {
+ /* Skip displaying existing background queued for load */
+ this._newBackgroundActor.destroy();
+ this._newBackgroundActor = null;
+ }
+
+ let newBackgroundActor = this._createBackgroundActor();
+
+ const oldContent = this.backgroundActor.content;
+ const newContent = newBackgroundActor.content;
+
+ newContent.vignette_sharpness = oldContent.vignette_sharpness;
+ newContent.brightness = oldContent.brightness;
+
+ newBackgroundActor.visible = this.backgroundActor.visible;
+
+ this._newBackgroundActor = newBackgroundActor;
+
+ const { background } = newBackgroundActor.content;
+
+ if (background.isLoaded) {
+ this._swapBackgroundActor();
+ } else {
+ newBackgroundActor.loadedSignalId = background.connect('loaded',
+ () => {
+ background.disconnect(newBackgroundActor.loadedSignalId);
+ newBackgroundActor.loadedSignalId = 0;
+
+ this._swapBackgroundActor();
+ });
+ }
+ }
+
+ _createBackgroundActor() {
+ let background = this._backgroundSource.getBackground(this._monitorIndex);
+ let backgroundActor = new Meta.BackgroundActor({
+ meta_display: global.display,
+ monitor: this._monitorIndex,
+ request_mode: this._useContentSize
+ ? Clutter.RequestMode.CONTENT_SIZE
+ : Clutter.RequestMode.HEIGHT_FOR_WIDTH,
+ x_expand: !this._useContentSize,
+ y_expand: !this._useContentSize,
+ });
+ backgroundActor.content.set({
+ background,
+ vignette: this._vignette,
+ vignette_sharpness: 0.5,
+ brightness: 0.5,
+ });
+
+ this._container.add_child(backgroundActor);
+
+ if (this._controlPosition) {
+ let monitor = this._layoutManager.monitors[this._monitorIndex];
+ backgroundActor.set_position(monitor.x, monitor.y);
+ this._container.set_child_below_sibling(backgroundActor, null);
+ }
+
+ let changeSignalId = background.connect('bg-changed', () => {
+ background.disconnect(changeSignalId);
+ changeSignalId = null;
+ this._updateBackgroundActor();
+ });
+
+ let loadedSignalId;
+ if (background.isLoaded) {
+ GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this.emit('loaded');
+ return GLib.SOURCE_REMOVE;
+ });
+ } else {
+ loadedSignalId = background.connect('loaded', () => {
+ background.disconnect(loadedSignalId);
+ loadedSignalId = null;
+ this.emit('loaded');
+ });
+ }
+
+ backgroundActor.connect('destroy', () => {
+ if (changeSignalId)
+ background.disconnect(changeSignalId);
+
+ if (loadedSignalId)
+ background.disconnect(loadedSignalId);
+
+ if (backgroundActor.loadedSignalId)
+ background.disconnect(backgroundActor.loadedSignalId);
+ });
+
+ return backgroundActor;
+ }
+};
diff --git a/js/ui/backgroundMenu.js b/js/ui/backgroundMenu.js
new file mode 100644
index 0000000..4c7372a
--- /dev/null
+++ b/js/ui/backgroundMenu.js
@@ -0,0 +1,67 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported addBackgroundMenu */
+
+const { Clutter, St } = imports.gi;
+
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+
+var BackgroundMenu = class BackgroundMenu extends PopupMenu.PopupMenu {
+ constructor(layoutManager) {
+ super(layoutManager.dummyCursor, 0, St.Side.TOP);
+
+ this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop');
+ this.addSettingsAction(_('Settings'), 'org.gnome.Settings.desktop');
+
+ this.actor.add_style_class_name('background-menu');
+
+ layoutManager.uiGroup.add_actor(this.actor);
+ this.actor.hide();
+ }
+};
+
+function addBackgroundMenu(actor, layoutManager) {
+ actor.reactive = true;
+ actor._backgroundMenu = new BackgroundMenu(layoutManager);
+ actor._backgroundManager = new PopupMenu.PopupMenuManager(actor);
+ actor._backgroundManager.addMenu(actor._backgroundMenu);
+
+ function openMenu(x, y) {
+ Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
+ actor._backgroundMenu.open(BoxPointer.PopupAnimation.FULL);
+ }
+
+ let clickAction = new Clutter.ClickAction();
+ clickAction.connect('long-press', (action, theActor, state) => {
+ if (state == Clutter.LongPressState.QUERY) {
+ return (action.get_button() == 0 ||
+ action.get_button() == 1) &&
+ !actor._backgroundMenu.isOpen;
+ }
+ if (state == Clutter.LongPressState.ACTIVATE) {
+ let [x, y] = action.get_coords();
+ openMenu(x, y);
+ actor._backgroundManager.ignoreRelease();
+ }
+ return true;
+ });
+ clickAction.connect('clicked', action => {
+ if (action.get_button() == 3) {
+ let [x, y] = action.get_coords();
+ openMenu(x, y);
+ }
+ });
+ actor.add_action(clickAction);
+
+ global.display.connectObject('grab-op-begin',
+ () => clickAction.release(), actor);
+
+ actor.connect('destroy', () => {
+ actor._backgroundMenu.destroy();
+ actor._backgroundMenu = null;
+ actor._backgroundManager = null;
+ });
+}
diff --git a/js/ui/barLevel.js b/js/ui/barLevel.js
new file mode 100644
index 0000000..da5b34a
--- /dev/null
+++ b/js/ui/barLevel.js
@@ -0,0 +1,262 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* exported BarLevel */
+
+const { Atk, Clutter, GObject, St } = imports.gi;
+
+var BarLevel = GObject.registerClass({
+ Properties: {
+ 'value': GObject.ParamSpec.double(
+ 'value', 'value', 'value',
+ GObject.ParamFlags.READWRITE,
+ 0, 2, 0),
+ 'maximum-value': GObject.ParamSpec.double(
+ 'maximum-value', 'maximum-value', 'maximum-value',
+ GObject.ParamFlags.READWRITE,
+ 1, 2, 1),
+ 'overdrive-start': GObject.ParamSpec.double(
+ 'overdrive-start', 'overdrive-start', 'overdrive-start',
+ GObject.ParamFlags.READWRITE,
+ 1, 2, 1),
+ },
+}, class BarLevel extends St.DrawingArea {
+ _init(params) {
+ this._maxValue = 1;
+ this._value = 0;
+ this._overdriveStart = 1;
+ this._barLevelWidth = 0;
+
+ let defaultParams = {
+ style_class: 'barlevel',
+ accessible_role: Atk.Role.LEVEL_BAR,
+ };
+ super._init(Object.assign(defaultParams, params));
+ this.connect('notify::allocation', () => {
+ this._barLevelWidth = this.allocation.get_width();
+ });
+
+ this._customAccessible = St.GenericAccessible.new_for_actor(this);
+ this.set_accessible(this._customAccessible);
+
+ this._customAccessible.connect('get-current-value', this._getCurrentValue.bind(this));
+ this._customAccessible.connect('get-minimum-value', this._getMinimumValue.bind(this));
+ this._customAccessible.connect('get-maximum-value', this._getMaximumValue.bind(this));
+ this._customAccessible.connect('set-current-value', this._setCurrentValue.bind(this));
+
+ this.connect('notify::value', this._valueChanged.bind(this));
+ }
+
+ get value() {
+ return this._value;
+ }
+
+ set value(value) {
+ value = Math.max(Math.min(value, this._maxValue), 0);
+
+ if (this._value == value)
+ return;
+
+ this._value = value;
+ this.notify('value');
+ this.queue_repaint();
+ }
+
+ get maximumValue() {
+ return this._maxValue;
+ }
+
+ set maximumValue(value) {
+ value = Math.max(value, 1);
+
+ if (this._maxValue == value)
+ return;
+
+ this._maxValue = value;
+ this._overdriveStart = Math.min(this._overdriveStart, this._maxValue);
+ this.notify('maximum-value');
+ this.queue_repaint();
+ }
+
+ get overdriveStart() {
+ return this._overdriveStart;
+ }
+
+ set overdriveStart(value) {
+ if (this._overdriveStart == value)
+ return;
+
+ if (value > this._maxValue) {
+ throw new Error(`Tried to set overdrive value to ${value}, ` +
+ `which is a number greater than the maximum allowed value ${this._maxValue}`);
+ }
+
+ this._overdriveStart = value;
+ this.notify('overdrive-start');
+ this.queue_repaint();
+ }
+
+ vfunc_repaint() {
+ let cr = this.get_context();
+ let themeNode = this.get_theme_node();
+ let [width, height] = this.get_surface_size();
+ const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+
+ let barLevelHeight = themeNode.get_length('-barlevel-height');
+ let barLevelBorderRadius = Math.min(width, barLevelHeight) / 2;
+ let fgColor = themeNode.get_foreground_color();
+
+ let barLevelColor = themeNode.get_color('-barlevel-background-color');
+ let barLevelActiveColor = themeNode.get_color('-barlevel-active-background-color');
+ let barLevelOverdriveColor = themeNode.get_color('-barlevel-overdrive-color');
+
+ let barLevelBorderWidth = Math.min(themeNode.get_length('-barlevel-border-width'), 1);
+ let [hasBorderColor, barLevelBorderColor] =
+ themeNode.lookup_color('-barlevel-border-color', false);
+ if (!hasBorderColor)
+ barLevelBorderColor = barLevelColor;
+ let [hasActiveBorderColor, barLevelActiveBorderColor] =
+ themeNode.lookup_color('-barlevel-active-border-color', false);
+ if (!hasActiveBorderColor)
+ barLevelActiveBorderColor = barLevelActiveColor;
+ let [hasOverdriveBorderColor, barLevelOverdriveBorderColor] =
+ themeNode.lookup_color('-barlevel-overdrive-border-color', false);
+ if (!hasOverdriveBorderColor)
+ barLevelOverdriveBorderColor = barLevelOverdriveColor;
+
+ const TAU = Math.PI * 2;
+
+ let endX = 0;
+ if (this._maxValue > 0) {
+ let progress = this._value / this._maxValue;
+ if (rtl)
+ progress = 1 - progress;
+ endX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * progress;
+ }
+
+ let overdriveRatio = this._overdriveStart / this._maxValue;
+ if (rtl)
+ overdriveRatio = 1 - overdriveRatio;
+ let overdriveSeparatorX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * overdriveRatio;
+
+ let overdriveActive = this._overdriveStart !== this._maxValue;
+ let overdriveSeparatorWidth = 0;
+ if (overdriveActive)
+ overdriveSeparatorWidth = themeNode.get_length('-barlevel-overdrive-separator-width');
+
+ let xcArcStart = barLevelBorderRadius + barLevelBorderWidth;
+ let xcArcEnd = width - xcArcStart;
+ if (rtl)
+ [xcArcStart, xcArcEnd] = [xcArcEnd, xcArcStart];
+
+ /* background bar */
+ if (!rtl)
+ cr.arc(xcArcEnd, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+ else
+ cr.arcNegative(xcArcEnd, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+ cr.lineTo(endX, (height + barLevelHeight) / 2);
+ cr.lineTo(endX, (height - barLevelHeight) / 2);
+ cr.lineTo(xcArcEnd, (height - barLevelHeight) / 2);
+ Clutter.cairo_set_source_color(cr, barLevelColor);
+ cr.fillPreserve();
+ Clutter.cairo_set_source_color(cr, barLevelBorderColor);
+ cr.setLineWidth(barLevelBorderWidth);
+ cr.stroke();
+
+ /* normal progress bar */
+ let x = 0;
+ if (!rtl) {
+ x = Math.min(endX, overdriveSeparatorX - overdriveSeparatorWidth / 2);
+ cr.arc(xcArcStart, height / 2, barLevelBorderRadius, TAU * (1 / 4), TAU * (3 / 4));
+ } else {
+ x = Math.max(endX, overdriveSeparatorX + overdriveSeparatorWidth / 2);
+ cr.arcNegative(xcArcStart, height / 2, barLevelBorderRadius, TAU * (1 / 4), TAU * (3 / 4));
+ }
+ cr.lineTo(x, (height - barLevelHeight) / 2);
+ cr.lineTo(x, (height + barLevelHeight) / 2);
+ cr.lineTo(xcArcStart, (height + barLevelHeight) / 2);
+ if (this._value > 0)
+ Clutter.cairo_set_source_color(cr, barLevelActiveColor);
+ cr.fillPreserve();
+ Clutter.cairo_set_source_color(cr, barLevelActiveBorderColor);
+ cr.setLineWidth(barLevelBorderWidth);
+ cr.stroke();
+
+ /* overdrive progress barLevel */
+ if (!rtl)
+ x = Math.min(endX, overdriveSeparatorX) + overdriveSeparatorWidth / 2;
+ else
+ x = Math.max(endX, overdriveSeparatorX) - overdriveSeparatorWidth / 2;
+ if (this._value > this._overdriveStart) {
+ cr.moveTo(x, (height - barLevelHeight) / 2);
+ cr.lineTo(endX, (height - barLevelHeight) / 2);
+ cr.lineTo(endX, (height + barLevelHeight) / 2);
+ cr.lineTo(x, (height + barLevelHeight) / 2);
+ cr.lineTo(x, (height - barLevelHeight) / 2);
+ Clutter.cairo_set_source_color(cr, barLevelOverdriveColor);
+ cr.fillPreserve();
+ Clutter.cairo_set_source_color(cr, barLevelOverdriveBorderColor);
+ cr.setLineWidth(barLevelBorderWidth);
+ cr.stroke();
+ }
+
+ /* end progress bar arc */
+ if (this._value > 0) {
+ if (this._value <= this._overdriveStart)
+ Clutter.cairo_set_source_color(cr, barLevelActiveColor);
+ else
+ Clutter.cairo_set_source_color(cr, barLevelOverdriveColor);
+ if (!rtl) {
+ cr.arc(endX, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+ cr.lineTo(Math.floor(endX), (height + barLevelHeight) / 2);
+ cr.lineTo(Math.floor(endX), (height - barLevelHeight) / 2);
+ } else {
+ cr.arcNegative(endX, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4));
+ cr.lineTo(Math.ceil(endX), (height + barLevelHeight) / 2);
+ cr.lineTo(Math.ceil(endX), (height - barLevelHeight) / 2);
+ }
+ cr.lineTo(endX, (height - barLevelHeight) / 2);
+ cr.fillPreserve();
+ cr.setLineWidth(barLevelBorderWidth);
+ cr.stroke();
+ }
+
+ /* draw overdrive separator */
+ if (overdriveActive) {
+ cr.moveTo(overdriveSeparatorX - overdriveSeparatorWidth / 2, (height - barLevelHeight) / 2);
+ cr.lineTo(overdriveSeparatorX + overdriveSeparatorWidth / 2, (height - barLevelHeight) / 2);
+ cr.lineTo(overdriveSeparatorX + overdriveSeparatorWidth / 2, (height + barLevelHeight) / 2);
+ cr.lineTo(overdriveSeparatorX - overdriveSeparatorWidth / 2, (height + barLevelHeight) / 2);
+ cr.lineTo(overdriveSeparatorX - overdriveSeparatorWidth / 2, (height - barLevelHeight) / 2);
+ if (this._value <= this._overdriveStart)
+ Clutter.cairo_set_source_color(cr, fgColor);
+ else
+ Clutter.cairo_set_source_color(cr, barLevelColor);
+ cr.fill();
+ }
+
+ cr.$dispose();
+ }
+
+ _getCurrentValue() {
+ return this._value;
+ }
+
+ _getOverdriveStart() {
+ return this._overdriveStart;
+ }
+
+ _getMinimumValue() {
+ return 0;
+ }
+
+ _getMaximumValue() {
+ return this._maxValue;
+ }
+
+ _setCurrentValue(_actor, value) {
+ this._value = value;
+ }
+
+ _valueChanged() {
+ this._customAccessible.notify("accessible-value");
+ }
+});
diff --git a/js/ui/boxpointer.js b/js/ui/boxpointer.js
new file mode 100644
index 0000000..3987d62
--- /dev/null
+++ b/js/ui/boxpointer.js
@@ -0,0 +1,654 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported BoxPointer */
+
+const { Clutter, GObject, Meta, St } = imports.gi;
+
+const Main = imports.ui.main;
+
+var PopupAnimation = {
+ NONE: 0,
+ SLIDE: 1 << 0,
+ FADE: 1 << 1,
+ FULL: ~0,
+};
+
+var POPUP_ANIMATION_TIME = 150;
+
+/**
+ * BoxPointer:
+ * @side: side to draw the arrow on
+ * @binProperties: Properties to set on contained bin
+ *
+ * An actor which displays a triangle "arrow" pointing to a given
+ * side. The .bin property is a container in which content can be
+ * placed. The arrow position may be controlled via
+ * setArrowOrigin(). The arrow side might be temporarily flipped
+ * depending on the box size and source position to keep the box
+ * totally inside the monitor workarea if possible.
+ *
+ */
+var BoxPointer = GObject.registerClass({
+ Signals: { 'arrow-side-changed': {} },
+}, class BoxPointer extends St.Widget {
+ _init(arrowSide, binProperties) {
+ super._init();
+
+ this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
+
+ this._arrowSide = arrowSide;
+ this._userArrowSide = arrowSide;
+ this._arrowOrigin = 0;
+ this._arrowActor = null;
+ this.bin = new St.Bin(binProperties);
+ this.add_actor(this.bin);
+ this._border = new St.DrawingArea();
+ this._border.connect('repaint', this._drawBorder.bind(this));
+ this.add_actor(this._border);
+ this.set_child_above_sibling(this.bin, this._border);
+ this._sourceAlignment = 0.5;
+ this._muteKeys = true;
+ this._muteInput = true;
+
+ this.connect('notify::visible', () => {
+ if (this.visible)
+ Meta.disable_unredirect_for_display(global.display);
+ else
+ Meta.enable_unredirect_for_display(global.display);
+ });
+ }
+
+ vfunc_captured_event(event) {
+ if (event.type() === Clutter.EventType.ENTER ||
+ event.type() === Clutter.EventType.LEAVE)
+ return Clutter.EVENT_PROPAGATE;
+
+ let mute = event.type() === Clutter.EventType.KEY_PRESS ||
+ event.type() === Clutter.EventType.KEY_RELEASE
+ ? this._muteKeys : this._muteInput;
+
+ if (mute)
+ return Clutter.EVENT_STOP;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ get arrowSide() {
+ return this._arrowSide;
+ }
+
+ open(animate, onComplete) {
+ let themeNode = this.get_theme_node();
+ let rise = themeNode.get_length('-arrow-rise');
+ let animationTime = animate & PopupAnimation.FULL ? POPUP_ANIMATION_TIME : 0;
+
+ if (animate & PopupAnimation.FADE)
+ this.opacity = 0;
+ else
+ this.opacity = 255;
+
+ this._muteKeys = false;
+ this.show();
+
+ if (animate & PopupAnimation.SLIDE) {
+ switch (this._arrowSide) {
+ case St.Side.TOP:
+ this.translation_y = -rise;
+ break;
+ case St.Side.BOTTOM:
+ this.translation_y = rise;
+ break;
+ case St.Side.LEFT:
+ this.translation_x = -rise;
+ break;
+ case St.Side.RIGHT:
+ this.translation_x = rise;
+ break;
+ }
+ }
+
+ this.ease({
+ opacity: 255,
+ translation_x: 0,
+ translation_y: 0,
+ duration: animationTime,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: () => {
+ this._muteInput = false;
+ if (onComplete)
+ onComplete();
+ },
+ });
+ }
+
+ close(animate, onComplete) {
+ if (!this.visible)
+ return;
+
+ let translationX = 0;
+ let translationY = 0;
+ let themeNode = this.get_theme_node();
+ let rise = themeNode.get_length('-arrow-rise');
+ let fade = animate & PopupAnimation.FADE;
+ let animationTime = animate & PopupAnimation.FULL ? POPUP_ANIMATION_TIME : 0;
+
+ if (animate & PopupAnimation.SLIDE) {
+ switch (this._arrowSide) {
+ case St.Side.TOP:
+ translationY = rise;
+ break;
+ case St.Side.BOTTOM:
+ translationY = -rise;
+ break;
+ case St.Side.LEFT:
+ translationX = rise;
+ break;
+ case St.Side.RIGHT:
+ translationX = -rise;
+ break;
+ }
+ }
+
+ this._muteInput = true;
+ this._muteKeys = true;
+
+ this.remove_all_transitions();
+ this.ease({
+ opacity: fade ? 0 : 255,
+ translation_x: translationX,
+ translation_y: translationY,
+ duration: animationTime,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: () => {
+ this.hide();
+ this.opacity = 0;
+ this.translation_x = 0;
+ this.translation_y = 0;
+ if (onComplete)
+ onComplete();
+ },
+ });
+ }
+
+ _adjustAllocationForArrow(isWidth, minSize, natSize) {
+ let themeNode = this.get_theme_node();
+ let borderWidth = themeNode.get_length('-arrow-border-width');
+ minSize += borderWidth * 2;
+ natSize += borderWidth * 2;
+ if ((!isWidth && (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM)) ||
+ (isWidth && (this._arrowSide == St.Side.LEFT || this._arrowSide == St.Side.RIGHT))) {
+ let rise = themeNode.get_length('-arrow-rise');
+ minSize += rise;
+ natSize += rise;
+ }
+
+ return [minSize, natSize];
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ let themeNode = this.get_theme_node();
+ forHeight = themeNode.adjust_for_height(forHeight);
+
+ let width = this.bin.get_preferred_width(forHeight);
+ width = this._adjustAllocationForArrow(true, ...width);
+
+ return themeNode.adjust_preferred_width(...width);
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ let themeNode = this.get_theme_node();
+ let borderWidth = themeNode.get_length('-arrow-border-width');
+ forWidth = themeNode.adjust_for_width(forWidth);
+
+ let height = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
+ height = this._adjustAllocationForArrow(false, ...height);
+
+ return themeNode.adjust_preferred_height(...height);
+ }
+
+ vfunc_allocate(box) {
+ if (this._sourceActor && this._sourceActor.mapped) {
+ this._reposition(box);
+ this._updateFlip(box);
+ }
+
+ this.set_allocation(box);
+
+ let themeNode = this.get_theme_node();
+ let borderWidth = themeNode.get_length('-arrow-border-width');
+ let rise = themeNode.get_length('-arrow-rise');
+ let childBox = new Clutter.ActorBox();
+ let [availWidth, availHeight] = themeNode.get_content_box(box).get_size();
+
+ childBox.x1 = 0;
+ childBox.y1 = 0;
+ childBox.x2 = availWidth;
+ childBox.y2 = availHeight;
+ this._border.allocate(childBox);
+
+ childBox.x1 = borderWidth;
+ childBox.y1 = borderWidth;
+ childBox.x2 = availWidth - borderWidth;
+ childBox.y2 = availHeight - borderWidth;
+ switch (this._arrowSide) {
+ case St.Side.TOP:
+ childBox.y1 += rise;
+ break;
+ case St.Side.BOTTOM:
+ childBox.y2 -= rise;
+ break;
+ case St.Side.LEFT:
+ childBox.x1 += rise;
+ break;
+ case St.Side.RIGHT:
+ childBox.x2 -= rise;
+ break;
+ }
+ this.bin.allocate(childBox);
+ }
+
+ _drawBorder(area) {
+ let themeNode = this.get_theme_node();
+
+ if (this._arrowActor) {
+ let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
+ let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
+ let [absX, absY] = this.get_transformed_position();
+
+ if (this._arrowSide == St.Side.TOP ||
+ this._arrowSide == St.Side.BOTTOM)
+ this._arrowOrigin = sourceX - absX + sourceWidth / 2;
+ else
+ this._arrowOrigin = sourceY - absY + sourceHeight / 2;
+ }
+
+ let borderWidth = themeNode.get_length('-arrow-border-width');
+ let base = themeNode.get_length('-arrow-base');
+ let rise = themeNode.get_length('-arrow-rise');
+ let borderRadius = themeNode.get_length('-arrow-border-radius');
+
+ let halfBorder = borderWidth / 2;
+ let halfBase = Math.floor(base / 2);
+
+ let [width, height] = area.get_surface_size();
+ let [boxWidth, boxHeight] = [width, height];
+ if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM)
+ boxHeight -= rise;
+ else
+ boxWidth -= rise;
+
+ let cr = area.get_context();
+
+ // Translate so that box goes from 0,0 to boxWidth,boxHeight,
+ // with the arrow poking out of that
+ if (this._arrowSide == St.Side.TOP)
+ cr.translate(0, rise);
+ else if (this._arrowSide == St.Side.LEFT)
+ cr.translate(rise, 0);
+
+ let [x1, y1] = [halfBorder, halfBorder];
+ let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
+
+ let skipTopLeft = false;
+ let skipTopRight = false;
+ let skipBottomLeft = false;
+ let skipBottomRight = false;
+
+ if (rise) {
+ switch (this._arrowSide) {
+ case St.Side.TOP:
+ if (this._arrowOrigin == x1)
+ skipTopLeft = true;
+ else if (this._arrowOrigin == x2)
+ skipTopRight = true;
+ break;
+
+ case St.Side.RIGHT:
+ if (this._arrowOrigin == y1)
+ skipTopRight = true;
+ else if (this._arrowOrigin == y2)
+ skipBottomRight = true;
+ break;
+
+ case St.Side.BOTTOM:
+ if (this._arrowOrigin == x1)
+ skipBottomLeft = true;
+ else if (this._arrowOrigin == x2)
+ skipBottomRight = true;
+ break;
+
+ case St.Side.LEFT:
+ if (this._arrowOrigin == y1)
+ skipTopLeft = true;
+ else if (this._arrowOrigin == y2)
+ skipBottomLeft = true;
+ break;
+ }
+ }
+
+ cr.moveTo(x1 + borderRadius, y1);
+ if (this._arrowSide == St.Side.TOP && rise) {
+ if (skipTopLeft) {
+ cr.moveTo(x1, y2 - borderRadius);
+ cr.lineTo(x1, y1 - rise);
+ cr.lineTo(x1 + halfBase, y1);
+ } else if (skipTopRight) {
+ cr.lineTo(x2 - halfBase, y1);
+ cr.lineTo(x2, y1 - rise);
+ cr.lineTo(x2, y1 + borderRadius);
+ } else {
+ cr.lineTo(this._arrowOrigin - halfBase, y1);
+ cr.lineTo(this._arrowOrigin, y1 - rise);
+ cr.lineTo(this._arrowOrigin + halfBase, y1);
+ }
+ }
+
+ if (!skipTopRight) {
+ cr.lineTo(x2 - borderRadius, y1);
+ cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
+ 3 * Math.PI / 2, Math.PI * 2);
+ }
+
+ if (this._arrowSide == St.Side.RIGHT && rise) {
+ if (skipTopRight) {
+ cr.lineTo(x2 + rise, y1);
+ cr.lineTo(x2 + rise, y1 + halfBase);
+ } else if (skipBottomRight) {
+ cr.lineTo(x2, y2 - halfBase);
+ cr.lineTo(x2 + rise, y2);
+ cr.lineTo(x2 - borderRadius, y2);
+ } else {
+ cr.lineTo(x2, this._arrowOrigin - halfBase);
+ cr.lineTo(x2 + rise, this._arrowOrigin);
+ cr.lineTo(x2, this._arrowOrigin + halfBase);
+ }
+ }
+
+ if (!skipBottomRight) {
+ cr.lineTo(x2, y2 - borderRadius);
+ cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
+ 0, Math.PI / 2);
+ }
+
+ if (this._arrowSide == St.Side.BOTTOM && rise) {
+ if (skipBottomLeft) {
+ cr.lineTo(x1 + halfBase, y2);
+ cr.lineTo(x1, y2 + rise);
+ cr.lineTo(x1, y2 - borderRadius);
+ } else if (skipBottomRight) {
+ cr.lineTo(x2, y2 + rise);
+ cr.lineTo(x2 - halfBase, y2);
+ } else {
+ cr.lineTo(this._arrowOrigin + halfBase, y2);
+ cr.lineTo(this._arrowOrigin, y2 + rise);
+ cr.lineTo(this._arrowOrigin - halfBase, y2);
+ }
+ }
+
+ if (!skipBottomLeft) {
+ cr.lineTo(x1 + borderRadius, y2);
+ cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
+ Math.PI / 2, Math.PI);
+ }
+
+ if (this._arrowSide == St.Side.LEFT && rise) {
+ if (skipTopLeft) {
+ cr.lineTo(x1, y1 + halfBase);
+ cr.lineTo(x1 - rise, y1);
+ cr.lineTo(x1 + borderRadius, y1);
+ } else if (skipBottomLeft) {
+ cr.lineTo(x1 - rise, y2);
+ cr.lineTo(x1 - rise, y2 - halfBase);
+ } else {
+ cr.lineTo(x1, this._arrowOrigin + halfBase);
+ cr.lineTo(x1 - rise, this._arrowOrigin);
+ cr.lineTo(x1, this._arrowOrigin - halfBase);
+ }
+ }
+
+ if (!skipTopLeft) {
+ cr.lineTo(x1, y1 + borderRadius);
+ cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
+ Math.PI, 3 * Math.PI / 2);
+ }
+
+ const [hasColor, bgColor] =
+ themeNode.lookup_color('-arrow-background-color', false);
+ if (hasColor) {
+ Clutter.cairo_set_source_color(cr, bgColor);
+ cr.fillPreserve();
+ }
+
+ if (borderWidth > 0) {
+ let borderColor = themeNode.get_color('-arrow-border-color');
+ Clutter.cairo_set_source_color(cr, borderColor);
+ cr.setLineWidth(borderWidth);
+ cr.stroke();
+ }
+
+ cr.$dispose();
+ }
+
+ setPosition(sourceActor, alignment) {
+ if (!this._sourceActor || sourceActor != this._sourceActor) {
+ this._sourceActor?.disconnectObject(this);
+
+ this._sourceActor = sourceActor;
+
+ this._sourceActor?.connectObject('destroy',
+ () => (this._sourceActor = null), this);
+ }
+
+ this._arrowAlignment = alignment;
+
+ this.queue_relayout();
+ }
+
+ setSourceAlignment(alignment) {
+ this._sourceAlignment = alignment;
+
+ if (!this._sourceActor)
+ return;
+
+ this.setPosition(this._sourceActor, this._arrowAlignment);
+ }
+
+ _reposition(allocationBox) {
+ let sourceActor = this._sourceActor;
+ let alignment = this._arrowAlignment;
+ let monitorIndex = Main.layoutManager.findIndexForActor(sourceActor);
+
+ this._sourceExtents = sourceActor.get_transformed_extents();
+ this._workArea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
+
+ // Position correctly relative to the sourceActor
+ const sourceAllocation = sourceActor.get_allocation_box();
+ const sourceContentBox = sourceActor instanceof St.Widget
+ ? sourceActor.get_theme_node().get_content_box(sourceAllocation)
+ : new Clutter.ActorBox({
+ x2: sourceAllocation.get_width(),
+ y2: sourceAllocation.get_height(),
+ });
+ let sourceTopLeft = this._sourceExtents.get_top_left();
+ let sourceBottomRight = this._sourceExtents.get_bottom_right();
+ let sourceCenterX = sourceTopLeft.x + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment;
+ let sourceCenterY = sourceTopLeft.y + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment;
+ let [, , natWidth, natHeight] = this.get_preferred_size();
+
+ // We also want to keep it onscreen, and separated from the
+ // edge by the same distance as the main part of the box is
+ // separated from its sourceActor
+ let workarea = this._workArea;
+ let themeNode = this.get_theme_node();
+ let borderWidth = themeNode.get_length('-arrow-border-width');
+ let arrowBase = themeNode.get_length('-arrow-base');
+ let borderRadius = themeNode.get_length('-arrow-border-radius');
+ let margin = 4 * borderRadius + borderWidth + arrowBase;
+
+ let gap = themeNode.get_length('-boxpointer-gap');
+ let padding = themeNode.get_length('-arrow-rise');
+
+ let resX, resY;
+
+ switch (this._arrowSide) {
+ case St.Side.TOP:
+ resY = sourceBottomRight.y + gap;
+ break;
+ case St.Side.BOTTOM:
+ resY = sourceTopLeft.y - natHeight - gap;
+ break;
+ case St.Side.LEFT:
+ resX = sourceBottomRight.x + gap;
+ break;
+ case St.Side.RIGHT:
+ resX = sourceTopLeft.x - natWidth - gap;
+ break;
+ }
+
+ // Now align and position the pointing axis, making sure it fits on
+ // screen. If the arrowOrigin is so close to the edge that the arrow
+ // will not be isosceles, we try to compensate as follows:
+ // - We skip the rounded corner and settle for a right angled arrow
+ // as shown below. See _drawBorder for further details.
+ // |\_____
+ // |
+ // |
+ // - If the arrow was going to be acute angled, we move the position
+ // of the box to maintain the arrow's accuracy.
+
+ let arrowOrigin;
+ let halfBase = Math.floor(arrowBase / 2);
+ let halfBorder = borderWidth / 2;
+ let halfMargin = margin / 2;
+ let [x1, y1] = [halfBorder, halfBorder];
+ let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
+
+ switch (this._arrowSide) {
+ case St.Side.TOP:
+ case St.Side.BOTTOM:
+ resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
+
+ resX = Math.max(resX, workarea.x + padding);
+ resX = Math.min(resX, workarea.x + workarea.width - (padding + natWidth));
+
+ arrowOrigin = sourceCenterX - resX;
+ if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
+ if (arrowOrigin > x1)
+ resX += arrowOrigin - x1;
+ arrowOrigin = x1;
+ } else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
+ if (arrowOrigin < x2)
+ resX -= x2 - arrowOrigin;
+ arrowOrigin = x2;
+ }
+ break;
+
+ case St.Side.LEFT:
+ case St.Side.RIGHT:
+ resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
+
+ resY = Math.max(resY, workarea.y + padding);
+ resY = Math.min(resY, workarea.y + workarea.height - (padding + natHeight));
+
+ arrowOrigin = sourceCenterY - resY;
+ if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
+ if (arrowOrigin > y1)
+ resY += arrowOrigin - y1;
+ arrowOrigin = y1;
+ } else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
+ if (arrowOrigin < y2)
+ resY -= y2 - arrowOrigin;
+ arrowOrigin = y2;
+ }
+ break;
+ }
+
+ this.setArrowOrigin(arrowOrigin);
+
+ let parent = this.get_parent();
+ let success, x, y;
+ while (!success) {
+ [success, x, y] = parent.transform_stage_point(resX, resY);
+ parent = parent.get_parent();
+ }
+
+ // Actually set the position
+ allocationBox.set_origin(Math.floor(x), Math.floor(y));
+ }
+
+ // @origin: Coordinate specifying middle of the arrow, along
+ // the Y axis for St.Side.LEFT, St.Side.RIGHT from the top and X axis from
+ // the left for St.Side.TOP and St.Side.BOTTOM.
+ setArrowOrigin(origin) {
+ if (this._arrowOrigin != origin) {
+ this._arrowOrigin = origin;
+ this._border.queue_repaint();
+ }
+ }
+
+ // @actor: an actor relative to which the arrow is positioned.
+ // Differently from setPosition, this will not move the boxpointer itself,
+ // on the arrow
+ setArrowActor(actor) {
+ if (this._arrowActor != actor) {
+ this._arrowActor = actor;
+ this._border.queue_repaint();
+ }
+ }
+
+ _calculateArrowSide(arrowSide) {
+ let sourceTopLeft = this._sourceExtents.get_top_left();
+ let sourceBottomRight = this._sourceExtents.get_bottom_right();
+ let [, , boxWidth, boxHeight] = this.get_preferred_size();
+ let workarea = this._workArea;
+
+ switch (arrowSide) {
+ case St.Side.TOP:
+ if (sourceBottomRight.y + boxHeight > workarea.y + workarea.height &&
+ boxHeight < sourceTopLeft.y - workarea.y)
+ return St.Side.BOTTOM;
+ break;
+ case St.Side.BOTTOM:
+ if (sourceTopLeft.y - boxHeight < workarea.y &&
+ boxHeight < workarea.y + workarea.height - sourceBottomRight.y)
+ return St.Side.TOP;
+ break;
+ case St.Side.LEFT:
+ if (sourceBottomRight.x + boxWidth > workarea.x + workarea.width &&
+ boxWidth < sourceTopLeft.x - workarea.x)
+ return St.Side.RIGHT;
+ break;
+ case St.Side.RIGHT:
+ if (sourceTopLeft.x - boxWidth < workarea.x &&
+ boxWidth < workarea.x + workarea.width - sourceBottomRight.x)
+ return St.Side.LEFT;
+ break;
+ }
+
+ return arrowSide;
+ }
+
+ _updateFlip(allocationBox) {
+ let arrowSide = this._calculateArrowSide(this._userArrowSide);
+ if (this._arrowSide != arrowSide) {
+ this._arrowSide = arrowSide;
+ this._reposition(allocationBox);
+
+ this.emit('arrow-side-changed');
+ }
+ }
+
+ updateArrowSide(side) {
+ this._arrowSide = side;
+ this._border.queue_repaint();
+
+ this.emit('arrow-side-changed');
+ }
+
+ getPadding(side) {
+ return this.bin.get_theme_node().get_padding(side);
+ }
+
+ getArrowHeight() {
+ return this.get_theme_node().get_length('-arrow-rise');
+ }
+});
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
new file mode 100644
index 0000000..9851536
--- /dev/null
+++ b/js/ui/calendar.js
@@ -0,0 +1,1031 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Calendar, CalendarMessageList, DBusEventSource */
+
+const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi;
+
+const Main = imports.ui.main;
+const MessageList = imports.ui.messageList;
+const MessageTray = imports.ui.messageTray;
+const Mpris = imports.ui.mpris;
+const PopupMenu = imports.ui.popupMenu;
+const Util = imports.misc.util;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+var SHOW_WEEKDATE_KEY = 'show-weekdate';
+
+var MESSAGE_ICON_SIZE = -1; // pick up from CSS
+
+var NC_ = (context, str) => `${context}\u0004${str}`;
+
+function sameYear(dateA, dateB) {
+ return dateA.getYear() == dateB.getYear();
+}
+
+function sameMonth(dateA, dateB) {
+ return sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
+}
+
+function sameDay(dateA, dateB) {
+ return sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
+}
+
+function _isWorkDay(date) {
+ /* Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday). */
+ let days = C_('calendar-no-work', "06");
+ return !days.includes(date.getDay().toString());
+}
+
+function _getBeginningOfDay(date) {
+ let ret = new Date(date.getTime());
+ ret.setHours(0);
+ ret.setMinutes(0);
+ ret.setSeconds(0);
+ ret.setMilliseconds(0);
+ return ret;
+}
+
+function _getEndOfDay(date) {
+ const ret = _getBeginningOfDay(date);
+ ret.setDate(ret.getDate() + 1);
+ return ret;
+}
+
+function _getCalendarDayAbbreviation(dayNumber) {
+ let abbreviations = [
+ /* Translators: Calendar grid abbreviation for Sunday.
+ *
+ * NOTE: These grid abbreviations are always shown together
+ * and in order, e.g. "S M T W T F S".
+ */
+ NC_("grid sunday", "S"),
+ /* Translators: Calendar grid abbreviation for Monday */
+ NC_("grid monday", "M"),
+ /* Translators: Calendar grid abbreviation for Tuesday */
+ NC_("grid tuesday", "T"),
+ /* Translators: Calendar grid abbreviation for Wednesday */
+ NC_("grid wednesday", "W"),
+ /* Translators: Calendar grid abbreviation for Thursday */
+ NC_("grid thursday", "T"),
+ /* Translators: Calendar grid abbreviation for Friday */
+ NC_("grid friday", "F"),
+ /* Translators: Calendar grid abbreviation for Saturday */
+ NC_("grid saturday", "S"),
+ ];
+ return Shell.util_translate_time_string(abbreviations[dayNumber]);
+}
+
+// Abstraction for an appointment/event in a calendar
+
+var CalendarEvent = class CalendarEvent {
+ constructor(id, date, end, summary) {
+ this.id = id;
+ this.date = date;
+ this.end = end;
+ this.summary = summary;
+ }
+};
+
+// Interface for appointments/events - e.g. the contents of a calendar
+//
+
+var EventSourceBase = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+ Properties: {
+ 'has-calendars': GObject.ParamSpec.boolean(
+ 'has-calendars', 'has-calendars', 'has-calendars',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'is-loading': GObject.ParamSpec.boolean(
+ 'is-loading', 'is-loading', 'is-loading',
+ GObject.ParamFlags.READABLE,
+ false),
+ },
+ Signals: { 'changed': {} },
+}, class EventSourceBase extends GObject.Object {
+ get isLoading() {
+ throw new GObject.NotImplementedError(`isLoading in ${this.constructor.name}`);
+ }
+
+ get hasCalendars() {
+ throw new GObject.NotImplementedError(`hasCalendars in ${this.constructor.name}`);
+ }
+
+ destroy() {
+ }
+
+ requestRange(_begin, _end) {
+ throw new GObject.NotImplementedError(`requestRange in ${this.constructor.name}`);
+ }
+
+ getEvents(_begin, _end) {
+ throw new GObject.NotImplementedError(`getEvents in ${this.constructor.name}`);
+ }
+
+ hasEvents(_day) {
+ throw new GObject.NotImplementedError(`hasEvents in ${this.constructor.name}`);
+ }
+});
+
+var EmptyEventSource = GObject.registerClass(
+class EmptyEventSource extends EventSourceBase {
+ get isLoading() {
+ return false;
+ }
+
+ get hasCalendars() {
+ return false;
+ }
+
+ requestRange(_begin, _end) {
+ }
+
+ getEvents(_begin, _end) {
+ let result = [];
+ return result;
+ }
+
+ hasEvents(_day) {
+ return false;
+ }
+});
+
+const CalendarServerIface = loadInterfaceXML('org.gnome.Shell.CalendarServer');
+
+const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
+
+function CalendarServer() {
+ return new Gio.DBusProxy({
+ g_connection: Gio.DBus.session,
+ g_interface_name: CalendarServerInfo.name,
+ g_interface_info: CalendarServerInfo,
+ g_name: 'org.gnome.Shell.CalendarServer',
+ g_object_path: '/org/gnome/Shell/CalendarServer',
+ });
+}
+
+function _datesEqual(a, b) {
+ if (a < b)
+ return false;
+ else if (a > b)
+ return false;
+ return true;
+}
+
+/**
+ * Checks whether an event overlaps a given interval
+ *
+ * @param {Date} e0 Beginning of the event
+ * @param {Date} e1 End of the event
+ * @param {Date} i0 Beginning of the interval
+ * @param {Date} i1 End of the interval
+ * @returns {boolean} Whether there was an overlap
+ */
+function _eventOverlapsInterval(e0, e1, i0, i1) {
+ // This also ensures zero-length events are included
+ if (e0 >= i0 && e1 < i1)
+ return true;
+
+ if (e1 <= i0)
+ return false;
+ if (i1 <= e0)
+ return false;
+
+ return true;
+}
+
+// an implementation that reads data from a session bus service
+var DBusEventSource = GObject.registerClass(
+class DBusEventSource extends EventSourceBase {
+ _init() {
+ super._init();
+ this._resetCache();
+ this._isLoading = false;
+
+ this._initialized = false;
+ this._dbusProxy = new CalendarServer();
+ this._initProxy();
+ }
+
+ async _initProxy() {
+ let loaded = false;
+
+ try {
+ await this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null);
+ loaded = true;
+ } catch (e) {
+ // Ignore timeouts and install signals as normal, because with high
+ // probability the service will appear later on, and we will get a
+ // NameOwnerChanged which will finish loading
+ //
+ // (But still _initialized to false, because the proxy does not know
+ // about the HasCalendars property and would cause an exception trying
+ // to read it)
+ if (!e.matches(Gio.DBusError, Gio.DBusError.TIMED_OUT)) {
+ log(`Error loading calendars: ${e.message}`);
+ return;
+ }
+ }
+
+ this._dbusProxy.connectSignal('EventsAddedOrUpdated',
+ this._onEventsAddedOrUpdated.bind(this));
+ this._dbusProxy.connectSignal('EventsRemoved',
+ this._onEventsRemoved.bind(this));
+ this._dbusProxy.connectSignal('ClientDisappeared',
+ this._onClientDisappeared.bind(this));
+
+ this._dbusProxy.connect('notify::g-name-owner', () => {
+ if (this._dbusProxy.g_name_owner)
+ this._onNameAppeared();
+ else
+ this._onNameVanished();
+ });
+
+ this._dbusProxy.connect('g-properties-changed', () => {
+ this.notify('has-calendars');
+ });
+
+ this._initialized = loaded;
+ if (loaded) {
+ this.notify('has-calendars');
+ this._onNameAppeared();
+ }
+ }
+
+ destroy() {
+ this._dbusProxy.run_dispose();
+ }
+
+ get hasCalendars() {
+ if (this._initialized)
+ return this._dbusProxy.HasCalendars;
+ else
+ return false;
+ }
+
+ get isLoading() {
+ return this._isLoading;
+ }
+
+ _resetCache() {
+ this._events = new Map();
+ this._lastRequestBegin = null;
+ this._lastRequestEnd = null;
+ }
+
+ _removeMatching(uidPrefix) {
+ let changed = false;
+ for (const id of this._events.keys()) {
+ if (id.startsWith(uidPrefix))
+ changed = this._events.delete(id) || changed;
+ }
+ return changed;
+ }
+
+ _onNameAppeared() {
+ this._initialized = true;
+ this._resetCache();
+ this._loadEvents(true);
+ }
+
+ _onNameVanished() {
+ this._resetCache();
+ this.emit('changed');
+ }
+
+ _onEventsAddedOrUpdated(dbusProxy, nameOwner, argArray) {
+ const [appointments = []] = argArray;
+ let changed = false;
+ const handledRemovals = new Set();
+
+ for (let n = 0; n < appointments.length; n++) {
+ const [id, summary, startTime, endTime] = appointments[n];
+ const date = new Date(startTime * 1000);
+ const end = new Date(endTime * 1000);
+ let event = new CalendarEvent(id, date, end, summary);
+ /* It's a recurring event */
+ if (!id.endsWith('\n')) {
+ const parentId = id.substr(0, id.lastIndexOf('\n') + 1);
+ if (!handledRemovals.has(parentId)) {
+ handledRemovals.add(parentId);
+ this._removeMatching(parentId);
+ }
+ }
+ this._events.set(event.id, event);
+
+ changed = true;
+ }
+
+ if (changed)
+ this.emit('changed');
+ }
+
+ _onEventsRemoved(dbusProxy, nameOwner, argArray) {
+ const [ids = []] = argArray;
+
+ let changed = false;
+ for (const id of ids)
+ changed = this._removeMatching(id) || changed;
+
+ if (changed)
+ this.emit('changed');
+ }
+
+ _onClientDisappeared(dbusProxy, nameOwner, argArray) {
+ let [sourceUid = ''] = argArray;
+ sourceUid += '\n';
+
+ if (this._removeMatching(sourceUid))
+ this.emit('changed');
+ }
+
+ _loadEvents(forceReload) {
+ // Ignore while loading
+ if (!this._initialized)
+ return;
+
+ if (this._curRequestBegin && this._curRequestEnd) {
+ if (forceReload) {
+ this._events.clear();
+ this.emit('changed');
+ }
+ this._dbusProxy.SetTimeRangeAsync(
+ this._curRequestBegin.getTime() / 1000,
+ this._curRequestEnd.getTime() / 1000,
+ forceReload,
+ Gio.DBusCallFlags.NONE).catch(logError);
+ }
+ }
+
+ requestRange(begin, end) {
+ if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
+ this._lastRequestBegin = begin;
+ this._lastRequestEnd = end;
+ this._curRequestBegin = begin;
+ this._curRequestEnd = end;
+ this._loadEvents(true);
+ }
+ }
+
+ *_getFilteredEvents(begin, end) {
+ for (const event of this._events.values()) {
+ if (_eventOverlapsInterval(event.date, event.end, begin, end))
+ yield event;
+ }
+ }
+
+ getEvents(begin, end) {
+ let result = [...this._getFilteredEvents(begin, end)];
+
+ result.sort((event1, event2) => {
+ // sort events by end time on ending day
+ let d1 = event1.date < begin && event1.end <= end ? event1.end : event1.date;
+ let d2 = event2.date < begin && event2.end <= end ? event2.end : event2.date;
+ return d1.getTime() - d2.getTime();
+ });
+ return result;
+ }
+
+ hasEvents(day) {
+ let dayBegin = _getBeginningOfDay(day);
+ let dayEnd = _getEndOfDay(day);
+
+ const { done } = this._getFilteredEvents(dayBegin, dayEnd).next();
+ return !done;
+ }
+});
+
+var Calendar = GObject.registerClass({
+ Signals: { 'selected-date-changed': { param_types: [GLib.DateTime.$gtype] } },
+}, class Calendar extends St.Widget {
+ _init() {
+ this._weekStart = Shell.util_get_week_start();
+ this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.calendar' });
+
+ this._settings.connect(`changed::${SHOW_WEEKDATE_KEY}`, this._onSettingsChange.bind(this));
+ this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
+
+ /**
+ * Translators: The header displaying just the month name
+ * standalone, when this is a month of the current year.
+ * "%OB" is the new format specifier introduced in glibc 2.27,
+ * in most cases you should not change it.
+ */
+ this._headerFormatWithoutYear = _('%OB');
+ /**
+ * Translators: The header displaying the month name and the year
+ * number, when this is a month of a different year. You can
+ * reorder the format specifiers or add other modifications
+ * according to the requirements of your language.
+ * "%OB" is the new format specifier introduced in glibc 2.27,
+ * in most cases you should not use the old "%B" here unless you
+ * absolutely know what you are doing.
+ */
+ this._headerFormat = _('%OB %Y');
+
+ // Start off with the current date
+ this._selectedDate = new Date();
+
+ this._shouldDateGrabFocus = false;
+
+ super._init({
+ style_class: 'calendar',
+ layout_manager: new Clutter.GridLayout(),
+ reactive: true,
+ });
+
+ this._buildHeader();
+ }
+
+ setEventSource(eventSource) {
+ if (!(eventSource instanceof EventSourceBase))
+ throw new Error('Event source is not valid type');
+
+ this._eventSource = eventSource;
+ this._eventSource.connect('changed', () => {
+ this._rebuildCalendar();
+ this._update();
+ });
+ this._rebuildCalendar();
+ this._update();
+ }
+
+ // Sets the calendar to show a specific date
+ setDate(date) {
+ if (sameDay(date, this._selectedDate))
+ return;
+
+ this._selectedDate = date;
+ this._update();
+
+ let datetime = GLib.DateTime.new_from_unix_local(
+ this._selectedDate.getTime() / 1000);
+ this.emit('selected-date-changed', datetime);
+ }
+
+ updateTimeZone() {
+ // The calendar need to be rebuilt after a time zone update because
+ // the date might have changed.
+ this._rebuildCalendar();
+ this._update();
+ }
+
+ _buildHeader() {
+ let layout = this.layout_manager;
+ let offsetCols = this._useWeekdate ? 1 : 0;
+ this.destroy_all_children();
+
+ // Top line of the calendar '<| September 2009 |>'
+ this._topBox = new St.BoxLayout({ style_class: 'calendar-month-header' });
+ layout.attach(this._topBox, 0, 0, offsetCols + 7, 1);
+
+ this._backButton = new St.Button({
+ style_class: 'calendar-change-month-back pager-button',
+ icon_name: 'pan-start-symbolic',
+ accessible_name: _('Previous month'),
+ can_focus: true,
+ });
+ this._topBox.add(this._backButton);
+ this._backButton.connect('clicked', this._onPrevMonthButtonClicked.bind(this));
+
+ this._monthLabel = new St.Label({
+ style_class: 'calendar-month-label',
+ can_focus: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ x_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._topBox.add_child(this._monthLabel);
+
+ this._forwardButton = new St.Button({
+ style_class: 'calendar-change-month-forward pager-button',
+ icon_name: 'pan-end-symbolic',
+ accessible_name: _('Next month'),
+ can_focus: true,
+ });
+ this._topBox.add(this._forwardButton);
+ this._forwardButton.connect('clicked', this._onNextMonthButtonClicked.bind(this));
+
+ // Add weekday labels...
+ //
+ // We need to figure out the abbreviated localized names for the days of the week;
+ // we do this by just getting the next 7 days starting from right now and then putting
+ // them in the right cell in the table. It doesn't matter if we add them in order
+ let iter = new Date(this._selectedDate);
+ iter.setSeconds(0); // Leap second protection. Hah!
+ iter.setHours(12);
+ for (let i = 0; i < 7; i++) {
+ // Could use iter.toLocaleFormat('%a') but that normally gives three characters
+ // and we want, ideally, a single character for e.g. S M T W T F S
+ let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
+ let label = new St.Label({
+ style_class: 'calendar-day-base calendar-day-heading',
+ text: customDayAbbrev,
+ can_focus: true,
+ });
+ label.accessible_name = iter.toLocaleFormat('%A');
+ let col;
+ if (this.get_text_direction() == Clutter.TextDirection.RTL)
+ col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
+ else
+ col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
+ layout.attach(label, col, 1, 1, 1);
+ iter.setDate(iter.getDate() + 1);
+ }
+
+ // All the children after this are days, and get removed when we update the calendar
+ this._firstDayIndex = this.get_n_children();
+ }
+
+ vfunc_scroll_event(scrollEvent) {
+ switch (scrollEvent.direction) {
+ case Clutter.ScrollDirection.UP:
+ case Clutter.ScrollDirection.LEFT:
+ this._onPrevMonthButtonClicked();
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ case Clutter.ScrollDirection.RIGHT:
+ this._onNextMonthButtonClicked();
+ break;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onPrevMonthButtonClicked() {
+ let newDate = new Date(this._selectedDate);
+ let oldMonth = newDate.getMonth();
+ if (oldMonth == 0) {
+ newDate.setMonth(11);
+ newDate.setFullYear(newDate.getFullYear() - 1);
+ if (newDate.getMonth() != 11) {
+ let day = 32 - new Date(newDate.getFullYear() - 1, 11, 32).getDate();
+ newDate = new Date(newDate.getFullYear() - 1, 11, day);
+ }
+ } else {
+ newDate.setMonth(oldMonth - 1);
+ if (newDate.getMonth() != oldMonth - 1) {
+ let day = 32 - new Date(newDate.getFullYear(), oldMonth - 1, 32).getDate();
+ newDate = new Date(newDate.getFullYear(), oldMonth - 1, day);
+ }
+ }
+
+ this._backButton.grab_key_focus();
+
+ this.setDate(newDate);
+ }
+
+ _onNextMonthButtonClicked() {
+ let newDate = new Date(this._selectedDate);
+ let oldMonth = newDate.getMonth();
+ if (oldMonth == 11) {
+ newDate.setMonth(0);
+ newDate.setFullYear(newDate.getFullYear() + 1);
+ if (newDate.getMonth() != 0) {
+ let day = 32 - new Date(newDate.getFullYear() + 1, 0, 32).getDate();
+ newDate = new Date(newDate.getFullYear() + 1, 0, day);
+ }
+ } else {
+ newDate.setMonth(oldMonth + 1);
+ if (newDate.getMonth() != oldMonth + 1) {
+ let day = 32 - new Date(newDate.getFullYear(), oldMonth + 1, 32).getDate();
+ newDate = new Date(newDate.getFullYear(), oldMonth + 1, day);
+ }
+ }
+
+ this._forwardButton.grab_key_focus();
+
+ this.setDate(newDate);
+ }
+
+ _onSettingsChange() {
+ this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
+ this._buildHeader();
+ this._rebuildCalendar();
+ this._update();
+ }
+
+ _rebuildCalendar() {
+ let now = new Date();
+
+ // Remove everything but the topBox and the weekday labels
+ let children = this.get_children();
+ for (let i = this._firstDayIndex; i < children.length; i++)
+ children[i].destroy();
+
+ this._buttons = [];
+
+ // Start at the beginning of the week before the start of the month
+ //
+ // We want to show always 6 weeks (to keep the calendar menu at the same
+ // height if there are no events), so we pad it according to the following
+ // policy:
+ //
+ // 1 - If a month has 6 weeks, we place no padding (example: Dec 2012)
+ // 2 - If a month has 5 weeks and it starts on week start, we pad one week
+ // before it (example: Apr 2012)
+ // 3 - If a month has 5 weeks and it starts on any other day, we pad one week
+ // after it (example: Nov 2012)
+ // 4 - If a month has 4 weeks, we pad one week before and one after it
+ // (example: Feb 2010)
+ //
+ // Actually computing the number of weeks is complex, but we know that the
+ // problematic categories (2 and 4) always start on week start, and that
+ // all months at the end have 6 weeks.
+ let beginDate = new Date(
+ this._selectedDate.getFullYear(), this._selectedDate.getMonth(), 1);
+
+ this._calendarBegin = new Date(beginDate);
+ this._markedAsToday = now;
+
+ let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
+ let startsOnWeekStart = daysToWeekStart == 0;
+ let weekPadding = startsOnWeekStart ? 7 : 0;
+
+ beginDate.setDate(beginDate.getDate() - (weekPadding + daysToWeekStart));
+
+ let layout = this.layout_manager;
+ let iter = new Date(beginDate);
+ let row = 2;
+ // nRows here means 6 weeks + one header + one navbar
+ let nRows = 8;
+ while (row < nRows) {
+ let button = new St.Button({
+ // xgettext:no-javascript-format
+ label: iter.toLocaleFormat(C_('date day number format', '%d')),
+ can_focus: true,
+ });
+ let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
+
+ if (this._eventSource instanceof EmptyEventSource)
+ button.reactive = false;
+
+ button._date = new Date(iter);
+ button.connect('clicked', () => {
+ this._shouldDateGrabFocus = true;
+ this.setDate(button._date);
+ this._shouldDateGrabFocus = false;
+ });
+
+ let hasEvents = this._eventSource.hasEvents(iter);
+ let styleClass = 'calendar-day-base calendar-day';
+
+ if (_isWorkDay(iter))
+ styleClass += ' calendar-work-day';
+ else
+ styleClass += ' calendar-nonwork-day';
+
+ // Hack used in lieu of border-collapse - see gnome-shell.css
+ if (row == 2)
+ styleClass = `calendar-day-top ${styleClass}`;
+
+ let leftMost = rtl
+ ? iter.getDay() == (this._weekStart + 6) % 7
+ : iter.getDay() == this._weekStart;
+ if (leftMost)
+ styleClass = `calendar-day-left ${styleClass}`;
+
+ if (sameDay(now, iter))
+ styleClass += ' calendar-today';
+ else if (iter.getMonth() != this._selectedDate.getMonth())
+ styleClass += ' calendar-other-month-day';
+
+ if (hasEvents)
+ styleClass += ' calendar-day-with-events';
+
+ button.style_class = styleClass;
+
+ let offsetCols = this._useWeekdate ? 1 : 0;
+ let col;
+ if (rtl)
+ col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
+ else
+ col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
+ layout.attach(button, col, row, 1, 1);
+
+ this._buttons.push(button);
+
+ if (this._useWeekdate && iter.getDay() == 4) {
+ const label = new St.Label({
+ text: iter.toLocaleFormat('%V'),
+ style_class: 'calendar-week-number',
+ can_focus: true,
+ });
+ let weekFormat = Shell.util_translate_time_string(N_("Week %V"));
+ label.clutter_text.y_align = Clutter.ActorAlign.CENTER;
+ label.accessible_name = iter.toLocaleFormat(weekFormat);
+ layout.attach(label, rtl ? 7 : 0, row, 1, 1);
+ }
+
+ iter.setDate(iter.getDate() + 1);
+
+ if (iter.getDay() == this._weekStart)
+ row++;
+ }
+
+ // Signal to the event source that we are interested in events
+ // only from this date range
+ this._eventSource.requestRange(beginDate, iter);
+ }
+
+ _update() {
+ let now = new Date();
+
+ if (sameYear(this._selectedDate, now))
+ this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
+ else
+ this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
+
+ if (!this._calendarBegin || !sameMonth(this._selectedDate, this._calendarBegin) || !sameDay(now, this._markedAsToday))
+ this._rebuildCalendar();
+
+ this._buttons.forEach(button => {
+ if (sameDay(button._date, this._selectedDate)) {
+ button.add_style_pseudo_class('selected');
+ if (this._shouldDateGrabFocus)
+ button.grab_key_focus();
+ } else {
+ button.remove_style_pseudo_class('selected');
+ }
+ });
+ }
+});
+
+var NotificationMessage = GObject.registerClass(
+class NotificationMessage extends MessageList.Message {
+ _init(notification) {
+ super._init(notification.title, notification.bannerBodyText);
+ this.setUseBodyMarkup(notification.bannerBodyMarkup);
+
+ this.notification = notification;
+
+ this.setIcon(this._getIcon());
+
+ this.connect('close', () => {
+ this._closed = true;
+ if (this.notification)
+ this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED);
+ });
+ notification.connectObject(
+ 'updated', this._onUpdated.bind(this),
+ 'destroy', () => {
+ this.notification = null;
+ if (!this._closed)
+ this.close();
+ }, this);
+ }
+
+ _getIcon() {
+ if (this.notification.gicon) {
+ return new St.Icon({
+ gicon: this.notification.gicon,
+ icon_size: MESSAGE_ICON_SIZE,
+ });
+ } else {
+ return this.notification.source.createIcon(MESSAGE_ICON_SIZE);
+ }
+ }
+
+ _onUpdated(n, _clear) {
+ this.setIcon(this._getIcon());
+ this.setTitle(n.title);
+ this.setBody(n.bannerBodyText);
+ this.setUseBodyMarkup(n.bannerBodyMarkup);
+ }
+
+ vfunc_clicked() {
+ this.notification.activate();
+ }
+
+ canClose() {
+ return true;
+ }
+});
+
+var TimeLabel = GObject.registerClass(
+class NotificationTimeLabel extends St.Label {
+ _init(datetime) {
+ super._init({
+ style_class: 'event-time',
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.END,
+ });
+ this._datetime = datetime;
+ }
+
+ vfunc_map() {
+ this.text = Util.formatTimeSpan(this._datetime);
+ super.vfunc_map();
+ }
+});
+
+var NotificationSection = GObject.registerClass(
+class NotificationSection extends MessageList.MessageListSection {
+ _init() {
+ super._init();
+
+ this._nUrgent = 0;
+
+ Main.messageTray.connect('source-added', this._sourceAdded.bind(this));
+ Main.messageTray.getSources().forEach(source => {
+ this._sourceAdded(Main.messageTray, source);
+ });
+ }
+
+ get allowed() {
+ return Main.sessionMode.hasNotifications &&
+ !Main.sessionMode.isGreeter;
+ }
+
+ _sourceAdded(tray, source) {
+ source.connectObject('notification-added',
+ this._onNotificationAdded.bind(this), this);
+ }
+
+ _onNotificationAdded(source, notification) {
+ let message = new NotificationMessage(notification);
+ message.setSecondaryActor(new TimeLabel(notification.datetime));
+
+ let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
+
+ notification.connectObject(
+ 'destroy', () => {
+ if (isUrgent)
+ this._nUrgent--;
+ },
+ 'updated', () => {
+ message.setSecondaryActor(new TimeLabel(notification.datetime));
+ this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.mapped);
+ }, this);
+
+ if (isUrgent) {
+ // Keep track of urgent notifications to keep them on top
+ this._nUrgent++;
+ } else if (this.mapped) {
+ // Only acknowledge non-urgent notifications in case it
+ // has important actions that are inaccessible when not
+ // shown as banner
+ notification.acknowledged = true;
+ }
+
+ let index = isUrgent ? 0 : this._nUrgent;
+ this.addMessageAtIndex(message, index, this.mapped);
+ }
+
+ vfunc_map() {
+ this._messages.forEach(message => {
+ if (message.notification.urgency != MessageTray.Urgency.CRITICAL)
+ message.notification.acknowledged = true;
+ });
+ super.vfunc_map();
+ }
+});
+
+var Placeholder = GObject.registerClass(
+class Placeholder extends St.BoxLayout {
+ _init() {
+ super._init({ style_class: 'message-list-placeholder', vertical: true });
+ this._date = new Date();
+
+ this._icon = new St.Icon({ icon_name: 'no-notifications-symbolic' });
+ this.add_actor(this._icon);
+
+ this._label = new St.Label({ text: _('No Notifications') });
+ this.add_actor(this._label);
+ }
+});
+
+const DoNotDisturbSwitch = GObject.registerClass(
+class DoNotDisturbSwitch extends PopupMenu.Switch {
+ _init() {
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.notifications',
+ });
+
+ super._init(this._settings.get_boolean('show-banners'));
+
+ this._settings.bind('show-banners',
+ this, 'state',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+
+ this.connect('destroy', () => {
+ Gio.Settings.unbind(this, 'state');
+ this._settings = null;
+ });
+ }
+});
+
+var CalendarMessageList = GObject.registerClass(
+class CalendarMessageList extends St.Widget {
+ _init() {
+ super._init({
+ style_class: 'message-list',
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ });
+
+ this._placeholder = new Placeholder();
+ this.add_actor(this._placeholder);
+
+ let box = new St.BoxLayout({
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ });
+ this.add_actor(box);
+
+ this._scrollView = new St.ScrollView({
+ style_class: 'vfade',
+ overlay_scrollbars: true,
+ x_expand: true, y_expand: true,
+ });
+ this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
+ box.add_actor(this._scrollView);
+
+ let hbox = new St.BoxLayout({ style_class: 'message-list-controls' });
+ box.add_child(hbox);
+
+ const dndLabel = new St.Label({
+ text: _('Do Not Disturb'),
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ hbox.add_child(dndLabel);
+
+ this._dndSwitch = new DoNotDisturbSwitch();
+ this._dndButton = new St.Button({
+ style_class: 'dnd-button',
+ can_focus: true,
+ toggle_mode: true,
+ child: this._dndSwitch,
+ label_actor: dndLabel,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._dndSwitch.bind_property('state',
+ this._dndButton, 'checked',
+ GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE);
+ hbox.add_child(this._dndButton);
+
+ this._clearButton = new St.Button({
+ style_class: 'message-list-clear-button button',
+ label: _('Clear'),
+ can_focus: true,
+ x_expand: true,
+ x_align: Clutter.ActorAlign.END,
+ });
+ this._clearButton.connect('clicked', () => {
+ this._sectionList.get_children().forEach(s => s.clear());
+ });
+ hbox.add_actor(this._clearButton);
+
+ this._placeholder.bind_property('visible',
+ this._clearButton, 'visible',
+ GObject.BindingFlags.INVERT_BOOLEAN);
+
+ this._sectionList = new St.BoxLayout({
+ style_class: 'message-list-sections',
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.START,
+ });
+ this._sectionList.connectObject(
+ 'actor-added', this._sync.bind(this),
+ 'actor-removed', this._sync.bind(this),
+ this);
+ this._scrollView.add_actor(this._sectionList);
+
+ this._mediaSection = new Mpris.MediaSection();
+ this._addSection(this._mediaSection);
+
+ this._notificationSection = new NotificationSection();
+ this._addSection(this._notificationSection);
+
+ Main.sessionMode.connect('updated', this._sync.bind(this));
+ }
+
+ _addSection(section) {
+ section.connectObject(
+ 'notify::visible', this._sync.bind(this),
+ 'notify::empty', this._sync.bind(this),
+ 'notify::can-clear', this._sync.bind(this),
+ 'destroy', () => this._sectionList.remove_actor(section),
+ 'message-focused', (_s, messageActor) => {
+ Util.ensureActorVisibleInScrollView(this._scrollView, messageActor);
+ }, this);
+ this._sectionList.add_actor(section);
+ }
+
+ _sync() {
+ let sections = this._sectionList.get_children();
+ let visible = sections.some(s => s.allowed);
+ this.visible = visible;
+ if (!visible)
+ return;
+
+ let empty = sections.every(s => s.empty || !s.visible);
+ this._placeholder.visible = empty;
+
+ let canClear = sections.some(s => s.canClear && s.visible);
+ this._clearButton.reactive = canClear;
+ }
+});
diff --git a/js/ui/checkBox.js b/js/ui/checkBox.js
new file mode 100644
index 0000000..d64bd0d
--- /dev/null
+++ b/js/ui/checkBox.js
@@ -0,0 +1,40 @@
+/* exported CheckBox */
+const { Atk, Clutter, GObject, Pango, St } = imports.gi;
+
+var CheckBox = GObject.registerClass(
+class CheckBox extends St.Button {
+ _init(label) {
+ let container = new St.BoxLayout({
+ x_expand: true,
+ y_expand: true,
+ });
+ super._init({
+ style_class: 'check-box',
+ child: container,
+ button_mask: St.ButtonMask.ONE,
+ toggle_mode: true,
+ can_focus: true,
+ });
+ this.set_accessible_role(Atk.Role.CHECK_BOX);
+
+ this._box = new St.Bin({ y_align: Clutter.ActorAlign.START });
+ container.add_actor(this._box);
+
+ this._label = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
+ this._label.clutter_text.set_line_wrap(true);
+ this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
+ this.set_label_actor(this._label);
+ container.add_actor(this._label);
+
+ if (label)
+ this.setLabel(label);
+ }
+
+ setLabel(label) {
+ this._label.set_text(label);
+ }
+
+ getLabelActor() {
+ return this._label;
+ }
+});
diff --git a/js/ui/closeDialog.js b/js/ui/closeDialog.js
new file mode 100644
index 0000000..f5ddecd
--- /dev/null
+++ b/js/ui/closeDialog.js
@@ -0,0 +1,207 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported CloseDialog */
+
+const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const Main = imports.ui.main;
+
+var FROZEN_WINDOW_BRIGHTNESS = -0.3;
+var DIALOG_TRANSITION_TIME = 150;
+var ALIVE_TIMEOUT = 5000;
+
+var CloseDialog = GObject.registerClass({
+ Implements: [Meta.CloseDialog],
+ Properties: {
+ 'window': GObject.ParamSpec.override('window', Meta.CloseDialog),
+ },
+}, class CloseDialog extends GObject.Object {
+ _init(window) {
+ super._init();
+ this._window = window;
+ this._dialog = null;
+ this._tracked = undefined;
+ this._timeoutId = 0;
+ }
+
+ get window() {
+ return this._window;
+ }
+
+ set window(window) {
+ this._window = window;
+ }
+
+ _createDialogContent() {
+ let tracker = Shell.WindowTracker.get_default();
+ let windowApp = tracker.get_window_app(this._window);
+
+ /* Translators: %s is an application name */
+ let title = _("“%s” is not responding.").format(windowApp.get_name());
+ let description = _('You may choose to wait a short while for it to ' +
+ 'continue or force the application to quit entirely.');
+ return new Dialog.MessageDialogContent({ title, description });
+ }
+
+ _updateScale() {
+ // Since this is a child of MetaWindowActor (which, for Wayland clients,
+ // applies the geometry scale factor to its children itself, see
+ // meta_window_actor_set_geometry_scale()), make sure we don't apply
+ // the factor twice in the end.
+ if (this._window.get_client_type() !== Meta.WindowClientType.WAYLAND)
+ return;
+
+ let { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ this._dialog.set_scale(1 / scaleFactor, 1 / scaleFactor);
+ }
+
+ _initDialog() {
+ if (this._dialog)
+ return;
+
+ let windowActor = this._window.get_compositor_private();
+ this._dialog = new Dialog.Dialog(windowActor, 'close-dialog');
+ this._dialog.width = windowActor.width;
+ this._dialog.height = windowActor.height;
+
+ this._dialog.contentLayout.add_child(this._createDialogContent());
+ this._dialog.addButton({
+ label: _('Force Quit'),
+ action: this._onClose.bind(this),
+ default: true,
+ });
+ this._dialog.addButton({
+ label: _('Wait'),
+ action: this._onWait.bind(this),
+ key: Clutter.KEY_Escape,
+ });
+
+ global.focus_manager.add_group(this._dialog);
+
+ let themeContext = St.ThemeContext.get_for_stage(global.stage);
+ themeContext.connect('notify::scale-factor', this._updateScale.bind(this));
+
+ this._updateScale();
+ }
+
+ _addWindowEffect() {
+ // We set the effect on the surface actor, so the dialog itself
+ // (which is a child of the MetaWindowActor) does not get the
+ // effect applied itself.
+ let windowActor = this._window.get_compositor_private();
+ let surfaceActor = windowActor.get_first_child();
+ let effect = new Clutter.BrightnessContrastEffect();
+ effect.set_brightness(FROZEN_WINDOW_BRIGHTNESS);
+ surfaceActor.add_effect_with_name("gnome-shell-frozen-window", effect);
+ }
+
+ _removeWindowEffect() {
+ let windowActor = this._window.get_compositor_private();
+ let surfaceActor = windowActor.get_first_child();
+ surfaceActor.remove_effect_by_name("gnome-shell-frozen-window");
+ }
+
+ _onWait() {
+ this.response(Meta.CloseDialogResponse.WAIT);
+ }
+
+ _onClose() {
+ this.response(Meta.CloseDialogResponse.FORCE_CLOSE);
+ }
+
+ _onFocusChanged() {
+ if (Meta.is_wayland_compositor())
+ return;
+
+ let focusWindow = global.display.focus_window;
+ let keyFocus = global.stage.key_focus;
+
+ let shouldTrack;
+ if (focusWindow != null)
+ shouldTrack = focusWindow == this._window;
+ else
+ shouldTrack = keyFocus && this._dialog.contains(keyFocus);
+
+ if (this._tracked === shouldTrack)
+ return;
+
+ if (shouldTrack) {
+ Main.layoutManager.trackChrome(this._dialog,
+ { affectsInputRegion: true });
+ } else {
+ Main.layoutManager.untrackChrome(this._dialog);
+ }
+
+ // The buttons are broken when they aren't added to the input region,
+ // so disable them properly in that case
+ this._dialog.buttonLayout.get_children().forEach(b => {
+ b.reactive = shouldTrack;
+ });
+
+ this._tracked = shouldTrack;
+ }
+
+ vfunc_show() {
+ if (this._dialog != null)
+ return;
+
+ Meta.disable_unredirect_for_display(global.display);
+
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, ALIVE_TIMEOUT,
+ () => {
+ this._window.check_alive(global.display.get_current_time_roundtrip());
+ return GLib.SOURCE_CONTINUE;
+ });
+
+ global.display.connectObject(
+ 'notify::focus-window', this._onFocusChanged.bind(this), this);
+
+ global.stage.connectObject(
+ 'notify::key-focus', this._onFocusChanged.bind(this), this);
+
+ this._addWindowEffect();
+ this._initDialog();
+
+ this._dialog._dialog.scale_y = 0;
+ this._dialog._dialog.set_pivot_point(0.5, 0.5);
+
+ this._dialog._dialog.ease({
+ scale_y: 1,
+ mode: Clutter.AnimationMode.LINEAR,
+ duration: DIALOG_TRANSITION_TIME,
+ onComplete: this._onFocusChanged.bind(this),
+ });
+ }
+
+ vfunc_hide() {
+ if (this._dialog == null)
+ return;
+
+ Meta.enable_unredirect_for_display(global.display);
+
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+
+ global.display.disconnectObject(this);
+ global.stage.disconnectObject(this);
+
+ this._dialog._dialog.remove_all_transitions();
+
+ let dialog = this._dialog;
+ this._dialog = null;
+ this._removeWindowEffect();
+
+ dialog.makeInactive();
+ dialog._dialog.ease({
+ scale_y: 0,
+ mode: Clutter.AnimationMode.LINEAR,
+ duration: DIALOG_TRANSITION_TIME,
+ onComplete: () => dialog.destroy(),
+ });
+ }
+
+ vfunc_focus() {
+ if (this._dialog)
+ this._dialog.grab_key_focus();
+ }
+});
diff --git a/js/ui/components/__init__.js b/js/ui/components/__init__.js
new file mode 100644
index 0000000..7430013
--- /dev/null
+++ b/js/ui/components/__init__.js
@@ -0,0 +1,58 @@
+/* exported ComponentManager */
+const Main = imports.ui.main;
+
+var ComponentManager = class {
+ constructor() {
+ this._allComponents = {};
+ this._enabledComponents = [];
+
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ this._sessionUpdated();
+ }
+
+ _sessionUpdated() {
+ let newEnabledComponents = Main.sessionMode.components;
+
+ newEnabledComponents
+ .filter(name => !this._enabledComponents.includes(name))
+ .forEach(name => this._enableComponent(name));
+
+ this._enabledComponents
+ .filter(name => !newEnabledComponents.includes(name))
+ .forEach(name => this._disableComponent(name));
+
+ this._enabledComponents = newEnabledComponents;
+ }
+
+ _importComponent(name) {
+ let module = imports.ui.components[name];
+ return module.Component;
+ }
+
+ _ensureComponent(name) {
+ let component = this._allComponents[name];
+ if (component)
+ return component;
+
+ if (Main.sessionMode.isLocked)
+ return null;
+
+ let constructor = this._importComponent(name);
+ component = new constructor();
+ this._allComponents[name] = component;
+ return component;
+ }
+
+ _enableComponent(name) {
+ let component = this._ensureComponent(name);
+ if (component)
+ component.enable();
+ }
+
+ _disableComponent(name) {
+ let component = this._allComponents[name];
+ if (component == null)
+ return;
+ component.disable();
+ }
+};
diff --git a/js/ui/components/automountManager.js b/js/ui/components/automountManager.js
new file mode 100644
index 0000000..4c0c223
--- /dev/null
+++ b/js/ui/components/automountManager.js
@@ -0,0 +1,256 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Component */
+
+const { Gio, GLib } = imports.gi;
+const Params = imports.misc.params;
+
+const GnomeSession = imports.misc.gnomeSession;
+const Main = imports.ui.main;
+const ShellMountOperation = imports.ui.shellMountOperation;
+
+var GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
+
+// GSettings keys
+const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
+const SETTING_ENABLE_AUTOMOUNT = 'automount';
+
+var AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
+
+var AutomountManager = class {
+ constructor() {
+ this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
+ this._activeOperations = new Map();
+ this._session = new GnomeSession.SessionManager();
+ this._session.connectSignal('InhibitorAdded',
+ this._InhibitorsChanged.bind(this));
+ this._session.connectSignal('InhibitorRemoved',
+ this._InhibitorsChanged.bind(this));
+ this._inhibited = false;
+
+ this._volumeMonitor = Gio.VolumeMonitor.get();
+ }
+
+ enable() {
+ this._volumeMonitor.connectObject(
+ 'volume-added', this._onVolumeAdded.bind(this),
+ 'volume-removed', this._onVolumeRemoved.bind(this),
+ 'drive-connected', this._onDriveConnected.bind(this),
+ 'drive-disconnected', this._onDriveDisconnected.bind(this),
+ 'drive-eject-button', this._onDriveEjectButton.bind(this), this);
+
+ this._mountAllId = GLib.idle_add(GLib.PRIORITY_DEFAULT, this._startupMountAll.bind(this));
+ GLib.Source.set_name_by_id(this._mountAllId, '[gnome-shell] this._startupMountAll');
+ }
+
+ disable() {
+ this._volumeMonitor.disconnectObject(this);
+
+ if (this._mountAllId > 0) {
+ GLib.source_remove(this._mountAllId);
+ this._mountAllId = 0;
+ }
+ }
+
+ async _InhibitorsChanged(_object, _senderName, [_inhibitor]) {
+ try {
+ const [inhibited] =
+ await this._session.IsInhibitedAsync(GNOME_SESSION_AUTOMOUNT_INHIBIT);
+ this._inhibited = inhibited;
+ } catch (e) {}
+ }
+
+ _startupMountAll() {
+ let volumes = this._volumeMonitor.get_volumes();
+ volumes.forEach(volume => {
+ this._checkAndMountVolume(volume, {
+ checkSession: false,
+ useMountOp: false,
+ allowAutorun: false,
+ });
+ });
+
+ this._mountAllId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _onDriveConnected() {
+ // if we're not in the current ConsoleKit session,
+ // or screensaver is active, don't play sounds
+ if (!this._session.SessionIsActive)
+ return;
+
+ let player = global.display.get_sound_player();
+ player.play_from_theme('device-added-media',
+ _("External drive connected"),
+ null);
+ }
+
+ _onDriveDisconnected() {
+ // if we're not in the current ConsoleKit session,
+ // or screensaver is active, don't play sounds
+ if (!this._session.SessionIsActive)
+ return;
+
+ let player = global.display.get_sound_player();
+ player.play_from_theme('device-removed-media',
+ _("External drive disconnected"),
+ null);
+ }
+
+ _onDriveEjectButton(monitor, drive) {
+ // TODO: this code path is not tested, as the GVfs volume monitor
+ // doesn't emit this signal just yet.
+ if (!this._session.SessionIsActive)
+ return;
+
+ // we force stop/eject in this case, so we don't have to pass a
+ // mount operation object
+ if (drive.can_stop()) {
+ drive.stop(Gio.MountUnmountFlags.FORCE, null, null,
+ (o, res) => {
+ try {
+ drive.stop_finish(res);
+ } catch (e) {
+ log(`Unable to stop the drive after drive-eject-button ${e.toString()}`);
+ }
+ });
+ } else if (drive.can_eject()) {
+ drive.eject_with_operation(Gio.MountUnmountFlags.FORCE, null, null,
+ (o, res) => {
+ try {
+ drive.eject_with_operation_finish(res);
+ } catch (e) {
+ log(`Unable to eject the drive after drive-eject-button ${e.toString()}`);
+ }
+ });
+ }
+ }
+
+ _onVolumeAdded(monitor, volume) {
+ this._checkAndMountVolume(volume);
+ }
+
+ _checkAndMountVolume(volume, params) {
+ params = Params.parse(params, {
+ checkSession: true,
+ useMountOp: true,
+ allowAutorun: true,
+ });
+
+ if (params.checkSession) {
+ // if we're not in the current ConsoleKit session,
+ // don't attempt automount
+ if (!this._session.SessionIsActive)
+ return;
+ }
+
+ if (this._inhibited)
+ return;
+
+ // Volume is already mounted, don't bother.
+ if (volume.get_mount())
+ return;
+
+ if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
+ !volume.should_automount() ||
+ !volume.can_mount()) {
+ // allow the autorun to run anyway; this can happen if the
+ // mount gets added programmatically later, even if
+ // should_automount() or can_mount() are false, like for
+ // blank optical media.
+ this._allowAutorun(volume);
+ this._allowAutorunExpire(volume);
+
+ return;
+ }
+
+ if (params.useMountOp) {
+ let operation = new ShellMountOperation.ShellMountOperation(volume);
+ this._mountVolume(volume, operation, params.allowAutorun);
+ } else {
+ this._mountVolume(volume, null, params.allowAutorun);
+ }
+ }
+
+ _mountVolume(volume, operation, allowAutorun) {
+ if (allowAutorun)
+ this._allowAutorun(volume);
+
+ const mountOp = operation?.mountOp ?? null;
+ this._activeOperations.set(volume, operation);
+
+ volume.mount(0, mountOp, null,
+ this._onVolumeMounted.bind(this));
+ }
+
+ _onVolumeMounted(volume, res) {
+ this._allowAutorunExpire(volume);
+
+ try {
+ volume.mount_finish(res);
+ this._closeOperation(volume);
+ } catch (e) {
+ // FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
+ // backend, see https://bugs.freedesktop.org/show_bug.cgi?id=51271
+ // To reask the password if the user input was empty or wrong, we
+ // will check for corresponding error messages. However, these
+ // error strings are not unique for the cases in the comments below.
+ if (e.message.includes('No key available with this passphrase') || // cryptsetup
+ e.message.includes('No key available to unlock device') || // udisks (no password)
+ // libblockdev wrong password opening LUKS device
+ e.message.includes('Failed to activate device: Incorrect passphrase') ||
+ // cryptsetup returns EINVAL in many cases, including wrong TCRYPT password/parameters
+ e.message.includes('Failed to load device\'s parameters: Invalid argument')) {
+ this._reaskPassword(volume);
+ } else {
+ if (e.message.includes('Compiled against a version of libcryptsetup that does not support the VeraCrypt PIM setting')) {
+ Main.notifyError(_("Unable to unlock volume"),
+ _("The installed udisks version does not support the PIM setting"));
+ }
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
+ log(`Unable to mount volume ${volume.get_name()}: ${e.toString()}`);
+ this._closeOperation(volume);
+ }
+ }
+ }
+
+ _onVolumeRemoved(monitor, volume) {
+ if (volume._allowAutorunExpireId && volume._allowAutorunExpireId > 0) {
+ GLib.source_remove(volume._allowAutorunExpireId);
+ delete volume._allowAutorunExpireId;
+ }
+ }
+
+ _reaskPassword(volume) {
+ let prevOperation = this._activeOperations.get(volume);
+ const existingDialog = prevOperation?.borrowDialog();
+ let operation =
+ new ShellMountOperation.ShellMountOperation(volume,
+ { existingDialog });
+ this._mountVolume(volume, operation);
+ }
+
+ _closeOperation(volume) {
+ let operation = this._activeOperations.get(volume);
+ if (!operation)
+ return;
+ operation.close();
+ this._activeOperations.delete(volume);
+ }
+
+ _allowAutorun(volume) {
+ volume.allowAutorun = true;
+ }
+
+ _allowAutorunExpire(volume) {
+ let id = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, AUTORUN_EXPIRE_TIMEOUT_SECS, () => {
+ volume.allowAutorun = false;
+ delete volume._allowAutorunExpireId;
+ return GLib.SOURCE_REMOVE;
+ });
+ volume._allowAutorunExpireId = id;
+ GLib.Source.set_name_by_id(id, '[gnome-shell] volume.allowAutorun');
+ }
+};
+var Component = AutomountManager;
diff --git a/js/ui/components/autorunManager.js b/js/ui/components/autorunManager.js
new file mode 100644
index 0000000..d94be39
--- /dev/null
+++ b/js/ui/components/autorunManager.js
@@ -0,0 +1,345 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Component */
+
+const { Clutter, Gio, GObject, St } = imports.gi;
+
+const GnomeSession = imports.misc.gnomeSession;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+
+Gio._promisify(Gio.Mount.prototype, 'guess_content_type');
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+// GSettings keys
+const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
+const SETTING_DISABLE_AUTORUN = 'autorun-never';
+const SETTING_START_APP = 'autorun-x-content-start-app';
+const SETTING_IGNORE = 'autorun-x-content-ignore';
+const SETTING_OPEN_FOLDER = 'autorun-x-content-open-folder';
+
+var AutorunSetting = {
+ RUN: 0,
+ IGNORE: 1,
+ FILES: 2,
+ ASK: 3,
+};
+
+// misc utils
+function shouldAutorunMount(mount) {
+ let root = mount.get_root();
+ let volume = mount.get_volume();
+
+ if (!volume || !volume.allowAutorun)
+ return false;
+
+ if (root.is_native() && isMountRootHidden(root))
+ return false;
+
+ return true;
+}
+
+function isMountRootHidden(root) {
+ let path = root.get_path();
+
+ // skip any mounts in hidden directory hierarchies
+ return path.includes('/.');
+}
+
+function isMountNonLocal(mount) {
+ // If the mount doesn't have an associated volume, that means it's
+ // an uninteresting filesystem. Most devices that we care about will
+ // have a mount, like media players and USB sticks.
+ let volume = mount.get_volume();
+ if (volume == null)
+ return true;
+
+ return volume.get_identifier("class") == "network";
+}
+
+function startAppForMount(app, mount) {
+ let files = [];
+ let root = mount.get_root();
+ let retval = false;
+
+ files.push(root);
+
+ try {
+ retval = app.launch(files,
+ global.create_app_launch_context(0, -1));
+ } catch (e) {
+ log(`Unable to launch the application ${app.get_name()}: ${e}`);
+ }
+
+ return retval;
+}
+
+const HotplugSnifferIface = loadInterfaceXML('org.gnome.Shell.HotplugSniffer');
+const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
+function HotplugSniffer() {
+ return new HotplugSnifferProxy(Gio.DBus.session,
+ 'org.gnome.Shell.HotplugSniffer',
+ '/org/gnome/Shell/HotplugSniffer');
+}
+
+var ContentTypeDiscoverer = class {
+ constructor() {
+ this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
+ }
+
+ async guessContentTypes(mount) {
+ let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN);
+ let shouldScan = autorunEnabled && !isMountNonLocal(mount);
+
+ let contentTypes = [];
+ if (shouldScan) {
+ try {
+ contentTypes = await mount.guess_content_type(false, null);
+ } catch (e) {
+ log(`Unable to guess content types on added mount ${mount.get_name()}: ${e}`);
+ }
+
+ if (contentTypes.length === 0) {
+ const root = mount.get_root();
+ const hotplugSniffer = new HotplugSniffer();
+ [contentTypes] = await hotplugSniffer.SniffURIAsync(root.get_uri());
+ }
+ }
+
+ // we're not interested in win32 software content types here
+ contentTypes = contentTypes.filter(
+ type => type !== 'x-content/win32-software');
+
+ const apps = [];
+ contentTypes.forEach(type => {
+ const app = Gio.app_info_get_default_for_type(type, false);
+
+ if (app)
+ apps.push(app);
+ });
+
+ if (apps.length === 0)
+ apps.push(Gio.app_info_get_default_for_type('inode/directory', false));
+
+ return [apps, contentTypes];
+ }
+};
+
+var AutorunManager = class {
+ constructor() {
+ this._session = new GnomeSession.SessionManager();
+ this._volumeMonitor = Gio.VolumeMonitor.get();
+
+ this._dispatcher = new AutorunDispatcher(this);
+ }
+
+ enable() {
+ this._volumeMonitor.connectObject(
+ 'mount-added', this._onMountAdded.bind(this),
+ 'mount-removed', this._onMountRemoved.bind(this), this);
+ }
+
+ disable() {
+ this._volumeMonitor.disconnectObject(this);
+ }
+
+ async _onMountAdded(monitor, mount) {
+ // don't do anything if our session is not the currently
+ // active one
+ if (!this._session.SessionIsActive)
+ return;
+
+ const discoverer = new ContentTypeDiscoverer();
+ const [apps, contentTypes] = await discoverer.guessContentTypes(mount);
+ this._dispatcher.addMount(mount, apps, contentTypes);
+ }
+
+ _onMountRemoved(monitor, mount) {
+ this._dispatcher.removeMount(mount);
+ }
+};
+
+var AutorunDispatcher = class {
+ constructor(manager) {
+ this._manager = manager;
+ this._sources = [];
+ this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
+ }
+
+ _getAutorunSettingForType(contentType) {
+ let runApp = this._settings.get_strv(SETTING_START_APP);
+ if (runApp.includes(contentType))
+ return AutorunSetting.RUN;
+
+ let ignore = this._settings.get_strv(SETTING_IGNORE);
+ if (ignore.includes(contentType))
+ return AutorunSetting.IGNORE;
+
+ let openFiles = this._settings.get_strv(SETTING_OPEN_FOLDER);
+ if (openFiles.includes(contentType))
+ return AutorunSetting.FILES;
+
+ return AutorunSetting.ASK;
+ }
+
+ _getSourceForMount(mount) {
+ let filtered = this._sources.filter(source => source.mount == mount);
+
+ // we always make sure not to add two sources for the same
+ // mount in addMount(), so it's safe to assume filtered.length
+ // is always either 1 or 0.
+ if (filtered.length == 1)
+ return filtered[0];
+
+ return null;
+ }
+
+ _addSource(mount, apps) {
+ // if we already have a source showing for this
+ // mount, return
+ if (this._getSourceForMount(mount))
+ return;
+
+ // add a new source
+ this._sources.push(new AutorunSource(this._manager, mount, apps));
+ }
+
+ addMount(mount, apps, contentTypes) {
+ // if autorun is disabled globally, return
+ if (this._settings.get_boolean(SETTING_DISABLE_AUTORUN))
+ return;
+
+ // if the mount doesn't want to be autorun, return
+ if (!shouldAutorunMount(mount))
+ return;
+
+ let setting;
+ if (contentTypes.length > 0)
+ setting = this._getAutorunSettingForType(contentTypes[0]);
+ else
+ setting = AutorunSetting.ASK;
+
+ // check at the settings for the first content type
+ // to see whether we should ask
+ if (setting == AutorunSetting.IGNORE)
+ return; // return right away
+
+ let success = false;
+ let app = null;
+
+ if (setting == AutorunSetting.RUN)
+ app = Gio.app_info_get_default_for_type(contentTypes[0], false);
+ else if (setting == AutorunSetting.FILES)
+ app = Gio.app_info_get_default_for_type('inode/directory', false);
+
+ if (app)
+ success = startAppForMount(app, mount);
+
+ // we fallback here also in case the settings did not specify 'ask',
+ // but we failed launching the default app or the default file manager
+ if (!success)
+ this._addSource(mount, apps);
+ }
+
+ removeMount(mount) {
+ let source = this._getSourceForMount(mount);
+
+ // if we aren't tracking this mount, don't do anything
+ if (!source)
+ return;
+
+ // destroy the notification source
+ source.destroy();
+ }
+};
+
+var AutorunSource = GObject.registerClass(
+class AutorunSource extends MessageTray.Source {
+ _init(manager, mount, apps) {
+ super._init(mount.get_name());
+
+ this._manager = manager;
+ this.mount = mount;
+ this.apps = apps;
+
+ this._notification = new AutorunNotification(this._manager, this);
+
+ // add ourselves as a source, and popup the notification
+ Main.messageTray.add(this);
+ this.showNotification(this._notification);
+ }
+
+ getIcon() {
+ return this.mount.get_icon();
+ }
+
+ _createPolicy() {
+ return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus');
+ }
+});
+
+var AutorunNotification = GObject.registerClass(
+class AutorunNotification extends MessageTray.Notification {
+ _init(manager, source) {
+ super._init(source, source.title);
+
+ this._manager = manager;
+ this._mount = source.mount;
+ }
+
+ createBanner() {
+ let banner = new MessageTray.NotificationBanner(this);
+
+ this.source.apps.forEach(app => {
+ let actor = this._buttonForApp(app);
+
+ if (actor)
+ banner.addButton(actor);
+ });
+
+ return banner;
+ }
+
+ _buttonForApp(app) {
+ let box = new St.BoxLayout({
+ x_expand: true,
+ x_align: Clutter.ActorAlign.START,
+ });
+ const icon = new St.Icon({
+ gicon: app.get_icon(),
+ style_class: 'hotplug-notification-item-icon',
+ });
+ box.add(icon);
+
+ let label = new St.Bin({
+ child: new St.Label({
+ text: _("Open with %s").format(app.get_name()),
+ y_align: Clutter.ActorAlign.CENTER,
+ }),
+ });
+ box.add(label);
+
+ const button = new St.Button({
+ child: box,
+ x_expand: true,
+ button_mask: St.ButtonMask.ONE,
+ style_class: 'hotplug-notification-item button',
+ });
+
+ button.connect('clicked', () => {
+ startAppForMount(app, this._mount);
+ this.destroy();
+ });
+
+ return button;
+ }
+
+ activate() {
+ super.activate();
+
+ let app = Gio.app_info_get_default_for_type('inode/directory', false);
+ startAppForMount(app, this._mount);
+ }
+});
+
+var Component = AutorunManager;
diff --git a/js/ui/components/keyring.js b/js/ui/components/keyring.js
new file mode 100644
index 0000000..cd7a81e
--- /dev/null
+++ b/js/ui/components/keyring.js
@@ -0,0 +1,229 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Component */
+
+const { Clutter, Gcr, Gio, GObject, Pango, Shell, St } = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+const CheckBox = imports.ui.checkBox;
+const Util = imports.misc.util;
+
+var KeyringDialog = GObject.registerClass(
+class KeyringDialog extends ModalDialog.ModalDialog {
+ _init() {
+ super._init({ styleClass: 'prompt-dialog' });
+
+ this.prompt = new Shell.KeyringPrompt();
+ this.prompt.connect('show-password', this._onShowPassword.bind(this));
+ this.prompt.connect('show-confirm', this._onShowConfirm.bind(this));
+ this.prompt.connect('prompt-close', this._onHidePrompt.bind(this));
+
+ let content = new Dialog.MessageDialogContent();
+
+ this.prompt.bind_property('message',
+ content, 'title', GObject.BindingFlags.SYNC_CREATE);
+ this.prompt.bind_property('description',
+ content, 'description', GObject.BindingFlags.SYNC_CREATE);
+
+ let passwordBox = new St.BoxLayout({
+ style_class: 'prompt-dialog-password-layout',
+ vertical: true,
+ });
+
+ this._passwordEntry = new St.PasswordEntry({
+ style_class: 'prompt-dialog-password-entry',
+ can_focus: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ ShellEntry.addContextMenu(this._passwordEntry);
+ this._passwordEntry.clutter_text.connect('activate', this._onPasswordActivate.bind(this));
+ this.prompt.bind_property('password-visible',
+ this._passwordEntry, 'visible', GObject.BindingFlags.SYNC_CREATE);
+ passwordBox.add_child(this._passwordEntry);
+
+ this._confirmEntry = new St.PasswordEntry({
+ style_class: 'prompt-dialog-password-entry',
+ can_focus: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ ShellEntry.addContextMenu(this._confirmEntry);
+ this._confirmEntry.clutter_text.connect('activate', this._onConfirmActivate.bind(this));
+ this.prompt.bind_property('confirm-visible',
+ this._confirmEntry, 'visible', GObject.BindingFlags.SYNC_CREATE);
+ passwordBox.add_child(this._confirmEntry);
+
+ this.prompt.set_password_actor(this._passwordEntry.clutter_text);
+ this.prompt.set_confirm_actor(this._confirmEntry.clutter_text);
+
+ let warningBox = new St.BoxLayout({ vertical: true });
+
+ let capsLockWarning = new ShellEntry.CapsLockWarning();
+ let syncCapsLockWarningVisibility = () => {
+ capsLockWarning.visible =
+ this.prompt.password_visible || this.prompt.confirm_visible;
+ };
+ this.prompt.connect('notify::password-visible', syncCapsLockWarningVisibility);
+ this.prompt.connect('notify::confirm-visible', syncCapsLockWarningVisibility);
+ warningBox.add_child(capsLockWarning);
+
+ let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
+ warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ warning.clutter_text.line_wrap = true;
+ this.prompt.bind_property('warning',
+ warning, 'text', GObject.BindingFlags.SYNC_CREATE);
+ this.prompt.connect('notify::warning-visible', () => {
+ warning.opacity = this.prompt.warning_visible ? 255 : 0;
+ });
+ this.prompt.connect('notify::warning', () => {
+ if (this._passwordEntry && this.prompt.warning !== '')
+ Util.wiggle(this._passwordEntry);
+ });
+ warningBox.add_child(warning);
+
+ passwordBox.add_child(warningBox);
+ content.add_child(passwordBox);
+
+ this._choice = new CheckBox.CheckBox();
+ this.prompt.bind_property('choice-label', this._choice.getLabelActor(),
+ 'text', GObject.BindingFlags.SYNC_CREATE);
+ this.prompt.bind_property('choice-chosen', this._choice,
+ 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
+ this.prompt.bind_property('choice-visible', this._choice,
+ 'visible', GObject.BindingFlags.SYNC_CREATE);
+ content.add_child(this._choice);
+
+ this.contentLayout.add_child(content);
+
+ this._cancelButton = this.addButton({
+ label: '',
+ action: this._onCancelButton.bind(this),
+ key: Clutter.KEY_Escape,
+ });
+ this._continueButton = this.addButton({
+ label: '',
+ action: this._onContinueButton.bind(this),
+ default: true,
+ });
+
+ this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
+ this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ _updateSensitivity(sensitive) {
+ if (this._passwordEntry)
+ this._passwordEntry.reactive = sensitive;
+
+ if (this._confirmEntry)
+ this._confirmEntry.reactive = sensitive;
+
+ this._continueButton.can_focus = sensitive;
+ this._continueButton.reactive = sensitive;
+ }
+
+ _ensureOpen() {
+ // NOTE: ModalDialog.open() is safe to call if the dialog is
+ // already open - it just returns true without side-effects
+ if (this.open())
+ return true;
+
+ // The above fail if e.g. unable to get input grab
+ //
+ // In an ideal world this wouldn't happen (because the
+ // Shell is in complete control of the session) but that's
+ // just not how things work right now.
+
+ log('keyringPrompt: Failed to show modal dialog.' +
+ ' Dismissing prompt request');
+ this.prompt.cancel();
+ return false;
+ }
+
+ _onShowPassword() {
+ this._ensureOpen();
+ this._updateSensitivity(true);
+ this._passwordEntry.text = '';
+ this._passwordEntry.grab_key_focus();
+ }
+
+ _onShowConfirm() {
+ this._ensureOpen();
+ this._updateSensitivity(true);
+ this._confirmEntry.text = '';
+ this._continueButton.grab_key_focus();
+ }
+
+ _onHidePrompt() {
+ this.close();
+ }
+
+ _onPasswordActivate() {
+ if (this.prompt.confirm_visible)
+ this._confirmEntry.grab_key_focus();
+ else
+ this._onContinueButton();
+ }
+
+ _onConfirmActivate() {
+ this._onContinueButton();
+ }
+
+ _onContinueButton() {
+ this._updateSensitivity(false);
+ this.prompt.complete();
+ }
+
+ _onCancelButton() {
+ this.prompt.cancel();
+ }
+});
+
+var KeyringDummyDialog = class {
+ constructor() {
+ this.prompt = new Shell.KeyringPrompt();
+ this.prompt.connect('show-password', this._cancelPrompt.bind(this));
+ this.prompt.connect('show-confirm', this._cancelPrompt.bind(this));
+ }
+
+ _cancelPrompt() {
+ this.prompt.cancel();
+ }
+};
+
+var KeyringPrompter = GObject.registerClass(
+class KeyringPrompter extends Gcr.SystemPrompter {
+ _init() {
+ super._init();
+ this.connect('new-prompt', () => {
+ let dialog = this._enabled
+ ? new KeyringDialog()
+ : new KeyringDummyDialog();
+ this._currentPrompt = dialog.prompt;
+ return this._currentPrompt;
+ });
+ this._dbusId = null;
+ this._registered = false;
+ this._enabled = false;
+ this._currentPrompt = null;
+ }
+
+ enable() {
+ if (!this._registered) {
+ this.register(Gio.DBus.session);
+ this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
+ Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
+ this._registered = true;
+ }
+ this._enabled = true;
+ }
+
+ disable() {
+ this._enabled = false;
+
+ if (this.prompting)
+ this._currentPrompt.cancel();
+ this._currentPrompt = null;
+ }
+});
+
+var Component = KeyringPrompter;
diff --git a/js/ui/components/networkAgent.js b/js/ui/components/networkAgent.js
new file mode 100644
index 0000000..ba02f88
--- /dev/null
+++ b/js/ui/components/networkAgent.js
@@ -0,0 +1,877 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Component */
+
+const { Clutter, Gio, GLib, GObject, NM, Pango, Shell, St } = imports.gi;
+const Signals = imports.misc.signals;
+
+const Dialog = imports.ui.dialog;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+
+Gio._promisify(Shell.NetworkAgent.prototype, 'init_async');
+Gio._promisify(Shell.NetworkAgent.prototype, 'search_vpn_plugin');
+
+const VPN_UI_GROUP = 'VPN Plugin UI';
+
+var NetworkSecretDialog = GObject.registerClass(
+class NetworkSecretDialog extends ModalDialog.ModalDialog {
+ _init(agent, requestId, connection, settingName, hints, flags, contentOverride) {
+ super._init({ styleClass: 'prompt-dialog' });
+
+ this._agent = agent;
+ this._requestId = requestId;
+ this._connection = connection;
+ this._settingName = settingName;
+ this._hints = hints;
+
+ if (contentOverride)
+ this._content = contentOverride;
+ else
+ this._content = this._getContent();
+
+ let contentBox = new Dialog.MessageDialogContent({
+ title: this._content.title,
+ description: this._content.message,
+ });
+
+ let initialFocusSet = false;
+ for (let i = 0; i < this._content.secrets.length; i++) {
+ let secret = this._content.secrets[i];
+ let reactive = secret.key != null;
+
+ let entryParams = {
+ style_class: 'prompt-dialog-password-entry',
+ hint_text: secret.label,
+ text: secret.value,
+ can_focus: reactive,
+ reactive,
+ x_align: Clutter.ActorAlign.CENTER,
+ };
+ if (secret.password)
+ secret.entry = new St.PasswordEntry(entryParams);
+ else
+ secret.entry = new St.Entry(entryParams);
+ ShellEntry.addContextMenu(secret.entry);
+ contentBox.add_child(secret.entry);
+
+ if (secret.validate)
+ secret.valid = secret.validate(secret);
+ else // no special validation, just ensure it's not empty
+ secret.valid = secret.value.length > 0;
+
+ if (reactive) {
+ if (!initialFocusSet) {
+ this.setInitialKeyFocus(secret.entry);
+ initialFocusSet = true;
+ }
+
+ secret.entry.clutter_text.connect('activate', this._onOk.bind(this));
+ secret.entry.clutter_text.connect('text-changed', () => {
+ secret.value = secret.entry.get_text();
+ if (secret.validate)
+ secret.valid = secret.validate(secret);
+ else
+ secret.valid = secret.value.length > 0;
+ this._updateOkButton();
+ });
+ } else {
+ secret.valid = true;
+ }
+ }
+
+ if (this._content.secrets.some(s => s.password)) {
+ let capsLockWarning = new ShellEntry.CapsLockWarning();
+ contentBox.add_child(capsLockWarning);
+ }
+
+ if (flags & NM.SecretAgentGetSecretsFlags.WPS_PBC_ACTIVE) {
+ let descriptionLabel = new St.Label({
+ text: _('Alternatively you can connect by pushing the “WPS” button on your router.'),
+ style_class: 'message-dialog-description',
+ });
+ descriptionLabel.clutter_text.line_wrap = true;
+ descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+
+ contentBox.add_child(descriptionLabel);
+ }
+
+ this.contentLayout.add_child(contentBox);
+
+ this._okButton = {
+ label: _("Connect"),
+ action: this._onOk.bind(this),
+ default: true,
+ };
+
+ this.setButtons([{
+ label: _("Cancel"),
+ action: this.cancel.bind(this),
+ key: Clutter.KEY_Escape,
+ }, this._okButton]);
+
+ this._updateOkButton();
+ }
+
+ _updateOkButton() {
+ let valid = true;
+ for (let i = 0; i < this._content.secrets.length; i++) {
+ let secret = this._content.secrets[i];
+ valid &&= secret.valid;
+ }
+
+ this._okButton.button.reactive = valid;
+ this._okButton.button.can_focus = valid;
+ }
+
+ _onOk() {
+ let valid = true;
+ for (let i = 0; i < this._content.secrets.length; i++) {
+ let secret = this._content.secrets[i];
+ valid &&= secret.valid;
+ if (secret.key !== null) {
+ if (this._settingName === 'vpn')
+ this._agent.add_vpn_secret(this._requestId, secret.key, secret.value);
+ else
+ this._agent.set_password(this._requestId, secret.key, secret.value);
+ }
+ }
+
+ if (valid) {
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.CONFIRMED);
+ this.close(global.get_current_time());
+ }
+ // do nothing if not valid
+ }
+
+ cancel() {
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
+ this.close(global.get_current_time());
+ }
+
+ _validateWpaPsk(secret) {
+ let value = secret.value;
+ if (value.length == 64) {
+ // must be composed of hexadecimal digits only
+ for (let i = 0; i < 64; i++) {
+ if (!((value[i] >= 'a' && value[i] <= 'f') ||
+ (value[i] >= 'A' && value[i] <= 'F') ||
+ (value[i] >= '0' && value[i] <= '9')))
+ return false;
+ }
+ return true;
+ }
+
+ return value.length >= 8 && value.length <= 63;
+ }
+
+ _validateStaticWep(secret) {
+ let value = secret.value;
+ if (secret.wep_key_type == NM.WepKeyType.KEY) {
+ if (value.length == 10 || value.length == 26) {
+ for (let i = 0; i < value.length; i++) {
+ if (!((value[i] >= 'a' && value[i] <= 'f') ||
+ (value[i] >= 'A' && value[i] <= 'F') ||
+ (value[i] >= '0' && value[i] <= '9')))
+ return false;
+ }
+ } else if (value.length == 5 || value.length == 13) {
+ for (let i = 0; i < value.length; i++) {
+ if (!((value[i] >= 'a' && value[i] <= 'z') ||
+ (value[i] >= 'A' && value[i] <= 'Z')))
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else if (secret.wep_key_type == NM.WepKeyType.PASSPHRASE) {
+ if (value.length < 0 || value.length > 64)
+ return false;
+ }
+ return true;
+ }
+
+ _getWirelessSecrets(secrets, _wirelessSetting) {
+ let wirelessSecuritySetting = this._connection.get_setting_wireless_security();
+
+ if (this._settingName == '802-1x') {
+ this._get8021xSecrets(secrets);
+ return;
+ }
+
+ switch (wirelessSecuritySetting.key_mgmt) {
+ // First the easy ones
+ case 'wpa-none':
+ case 'wpa-psk':
+ case 'sae':
+ secrets.push({
+ label: _('Password'),
+ key: 'psk',
+ value: wirelessSecuritySetting.psk || '',
+ validate: this._validateWpaPsk,
+ password: true,
+ });
+ break;
+ case 'none': // static WEP
+ secrets.push({
+ label: _('Key'),
+ key: `wep-key${wirelessSecuritySetting.wep_tx_keyidx}`,
+ value: wirelessSecuritySetting.get_wep_key(wirelessSecuritySetting.wep_tx_keyidx) || '',
+ wep_key_type: wirelessSecuritySetting.wep_key_type,
+ validate: this._validateStaticWep,
+ password: true,
+ });
+ break;
+ case 'ieee8021x':
+ if (wirelessSecuritySetting.auth_alg == 'leap') { // Cisco LEAP
+ secrets.push({
+ label: _('Password'),
+ key: 'leap-password',
+ value: wirelessSecuritySetting.leap_password || '',
+ password: true,
+ });
+ } else { // Dynamic (IEEE 802.1x) WEP
+ this._get8021xSecrets(secrets);
+ }
+ break;
+ case 'wpa-eap':
+ this._get8021xSecrets(secrets);
+ break;
+ default:
+ log(`Invalid wireless key management: ${wirelessSecuritySetting.key_mgmt}`);
+ }
+ }
+
+ _get8021xSecrets(secrets) {
+ let ieee8021xSetting = this._connection.get_setting_802_1x();
+
+ /* If hints were given we know exactly what we need to ask */
+ if (this._settingName == "802-1x" && this._hints.length) {
+ if (this._hints.includes('identity')) {
+ secrets.push({
+ label: _('Username'),
+ key: 'identity',
+ value: ieee8021xSetting.identity || '',
+ password: false,
+ });
+ }
+ if (this._hints.includes('password')) {
+ secrets.push({
+ label: _('Password'),
+ key: 'password',
+ value: ieee8021xSetting.password || '',
+ password: true,
+ });
+ }
+ if (this._hints.includes('private-key-password')) {
+ secrets.push({
+ label: _('Private key password'),
+ key: 'private-key-password',
+ value: ieee8021xSetting.private_key_password || '',
+ password: true,
+ });
+ }
+ return;
+ }
+
+ switch (ieee8021xSetting.get_eap_method(0)) {
+ case 'md5':
+ case 'leap':
+ case 'ttls':
+ case 'peap':
+ case 'fast':
+ // TTLS and PEAP are actually much more complicated, but this complication
+ // is not visible here since we only care about phase2 authentication
+ // (and don't even care of which one)
+ secrets.push({
+ label: _('Username'),
+ key: null,
+ value: ieee8021xSetting.identity || '',
+ password: false,
+ });
+ secrets.push({
+ label: _('Password'),
+ key: 'password',
+ value: ieee8021xSetting.password || '',
+ password: true,
+ });
+ break;
+ case 'tls':
+ secrets.push({
+ label: _('Identity'),
+ key: null,
+ value: ieee8021xSetting.identity || '',
+ password: false,
+ });
+ secrets.push({
+ label: _('Private key password'),
+ key: 'private-key-password',
+ value: ieee8021xSetting.private_key_password || '',
+ password: true,
+ });
+ break;
+ default:
+ log(`Invalid EAP/IEEE802.1x method: ${ieee8021xSetting.get_eap_method(0)}`);
+ }
+ }
+
+ _getPPPoESecrets(secrets) {
+ let pppoeSetting = this._connection.get_setting_pppoe();
+ secrets.push({
+ label: _('Username'),
+ key: 'username',
+ value: pppoeSetting.username || '',
+ password: false,
+ });
+ secrets.push({
+ label: _('Service'), key: 'service',
+ value: pppoeSetting.service || '',
+ password: false,
+ });
+ secrets.push({
+ label: _('Password'), key: 'password',
+ value: pppoeSetting.password || '',
+ password: true,
+ });
+ }
+
+ _getMobileSecrets(secrets, connectionType) {
+ let setting;
+ if (connectionType == 'bluetooth')
+ setting = this._connection.get_setting_cdma() || this._connection.get_setting_gsm();
+ else
+ setting = this._connection.get_setting_by_name(connectionType);
+ secrets.push({
+ label: _('Password'),
+ key: 'password',
+ value: setting.value || '',
+ password: true,
+ });
+ }
+
+ _getContent() {
+ let connectionSetting = this._connection.get_setting_connection();
+ let connectionType = connectionSetting.get_connection_type();
+ let wirelessSetting;
+ let ssid;
+
+ let content = { };
+ content.secrets = [];
+
+ switch (connectionType) {
+ case '802-11-wireless':
+ wirelessSetting = this._connection.get_setting_wireless();
+ ssid = NM.utils_ssid_to_utf8(wirelessSetting.get_ssid().get_data());
+ content.title = _('Authentication required');
+ content.message = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid);
+ this._getWirelessSecrets(content.secrets, wirelessSetting);
+ break;
+ case '802-3-ethernet':
+ content.title = _("Wired 802.1X authentication");
+ content.message = null;
+ content.secrets.push({
+ label: _('Network name'),
+ key: null,
+ value: connectionSetting.get_id(),
+ password: false,
+ });
+ this._get8021xSecrets(content.secrets);
+ break;
+ case 'pppoe':
+ content.title = _("DSL authentication");
+ content.message = null;
+ this._getPPPoESecrets(content.secrets);
+ break;
+ case 'gsm':
+ if (this._hints.includes('pin')) {
+ let gsmSetting = this._connection.get_setting_gsm();
+ content.title = _("PIN code required");
+ content.message = _("PIN code is needed for the mobile broadband device");
+ content.secrets.push({
+ label: _('PIN'),
+ key: 'pin',
+ value: gsmSetting.pin || '',
+ password: true,
+ });
+ break;
+ }
+ // fall through
+ case 'cdma':
+ case 'bluetooth':
+ content.title = _('Authentication required');
+ content.message = _("A password is required to connect to “%s”.").format(connectionSetting.get_id());
+ this._getMobileSecrets(content.secrets, connectionType);
+ break;
+ default:
+ log(`Invalid connection type: ${connectionType}`);
+ }
+
+ return content;
+ }
+});
+
+var VPNRequestHandler = class extends Signals.EventEmitter {
+ constructor(agent, requestId, authHelper, serviceType, connection, hints, flags) {
+ super();
+
+ this._agent = agent;
+ this._requestId = requestId;
+ this._connection = connection;
+ this._flags = flags;
+ this._pluginOutBuffer = [];
+ this._title = null;
+ this._description = null;
+ this._content = [];
+ this._shellDialog = null;
+
+ let connectionSetting = connection.get_setting_connection();
+
+ const argv = [
+ authHelper.fileName,
+ '-u', connectionSetting.uuid,
+ '-n', connectionSetting.id,
+ '-s', serviceType,
+ ];
+ if (authHelper.externalUIMode)
+ argv.push('--external-ui-mode');
+ if (flags & NM.SecretAgentGetSecretsFlags.ALLOW_INTERACTION)
+ argv.push('-i');
+ if (flags & NM.SecretAgentGetSecretsFlags.REQUEST_NEW)
+ argv.push('-r');
+ if (authHelper.supportsHints) {
+ for (let i = 0; i < hints.length; i++) {
+ argv.push('-t');
+ argv.push(hints[i]);
+ }
+ }
+
+ this._newStylePlugin = authHelper.externalUIMode;
+
+ try {
+ let [success_, pid, stdin, stdout, stderr] =
+ GLib.spawn_async_with_pipes(
+ null, /* pwd */
+ argv,
+ null, /* envp */
+ GLib.SpawnFlags.DO_NOT_REAP_CHILD,
+ () => {
+ try {
+ global.context.restore_rlimit_nofile();
+ } catch (err) {
+ }
+ });
+
+ this._childPid = pid;
+ this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
+ this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
+ GLib.close(stderr);
+ this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
+
+ if (this._newStylePlugin)
+ this._readStdoutNewStyle();
+ else
+ this._readStdoutOldStyle();
+
+ this._childWatch = GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid,
+ this._vpnChildFinished.bind(this));
+
+ this._writeConnection();
+ } catch (e) {
+ logError(e, 'error while spawning VPN auth helper');
+
+ this._agent.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
+ }
+ }
+
+ cancel(respond) {
+ if (respond)
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
+
+ if (this._newStylePlugin && this._shellDialog) {
+ this._shellDialog.close(global.get_current_time());
+ this._shellDialog.destroy();
+ } else {
+ try {
+ this._stdin.write('QUIT\n\n', null);
+ } catch (e) { /* ignore broken pipe errors */ }
+ }
+
+ this.destroy();
+ }
+
+ destroy() {
+ if (this._destroyed)
+ return;
+
+ this.emit('destroy');
+ if (this._childWatch)
+ GLib.source_remove(this._childWatch);
+
+ this._stdin.close(null);
+ // Stdout is closed when we finish reading from it
+
+ this._destroyed = true;
+ }
+
+ _vpnChildFinished(pid, status, _requestObj) {
+ this._childWatch = 0;
+ if (this._newStylePlugin) {
+ // For new style plugin, all work is done in the async reading functions
+ // Just reap the process here
+ return;
+ }
+
+ let [exited, exitStatus] = Shell.util_wifexited(status);
+
+ if (exited) {
+ if (exitStatus != 0)
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
+ else
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.CONFIRMED);
+ } else {
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
+ }
+
+ this.destroy();
+ }
+
+ _vpnChildProcessLineOldStyle(line) {
+ if (this._previousLine != undefined) {
+ // Two consecutive newlines mean that the child should be closed
+ // (the actual newlines are eaten by Gio.DataInputStream)
+ // Send a termination message
+ if (line == '' && this._previousLine == '') {
+ try {
+ this._stdin.write('QUIT\n\n', null);
+ } catch (e) { /* ignore broken pipe errors */ }
+ } else {
+ this._agent.add_vpn_secret(this._requestId, this._previousLine, line);
+ this._previousLine = undefined;
+ }
+ } else {
+ this._previousLine = line;
+ }
+ }
+
+ async _readStdoutOldStyle() {
+ const [line, len_] =
+ await this._dataStdout.read_line_async(GLib.PRIORITY_DEFAULT, null);
+
+ if (line === null) {
+ // end of file
+ this._stdout.close(null);
+ return;
+ }
+
+ const decoder = new TextDecoder();
+ this._vpnChildProcessLineOldStyle(decoder.decode(line));
+
+ // try to read more!
+ this._readStdoutOldStyle();
+ }
+
+ async _readStdoutNewStyle() {
+ const cnt =
+ await this._dataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null);
+
+ if (cnt === 0) {
+ // end of file
+ this._showNewStyleDialog();
+
+ this._stdout.close(null);
+ return;
+ }
+
+ // Try to read more
+ this._dataStdout.set_buffer_size(2 * this._dataStdout.get_buffer_size());
+ this._readStdoutNewStyle();
+ }
+
+ _showNewStyleDialog() {
+ let keyfile = new GLib.KeyFile();
+ let data;
+ let contentOverride;
+
+ try {
+ data = new GLib.Bytes(this._dataStdout.peek_buffer());
+ keyfile.load_from_bytes(data, GLib.KeyFileFlags.NONE);
+
+ if (keyfile.get_integer(VPN_UI_GROUP, 'Version') != 2)
+ throw new Error('Invalid plugin keyfile version, is %d');
+
+ contentOverride = {
+ title: keyfile.get_string(VPN_UI_GROUP, 'Title'),
+ message: keyfile.get_string(VPN_UI_GROUP, 'Description'),
+ secrets: [],
+ };
+
+ let [groups, len_] = keyfile.get_groups();
+ for (let i = 0; i < groups.length; i++) {
+ if (groups[i] == VPN_UI_GROUP)
+ continue;
+
+ let value = keyfile.get_string(groups[i], 'Value');
+ let shouldAsk = keyfile.get_boolean(groups[i], 'ShouldAsk');
+
+ if (shouldAsk) {
+ contentOverride.secrets.push({
+ label: keyfile.get_string(groups[i], 'Label'),
+ key: groups[i],
+ value,
+ password: keyfile.get_boolean(groups[i], 'IsSecret'),
+ });
+ } else {
+ if (!value.length) // Ignore empty secrets
+ continue;
+
+ this._agent.add_vpn_secret(this._requestId, groups[i], value);
+ }
+ }
+ } catch (e) {
+ // No output is a valid case it means "both secrets are stored"
+ if (data.length > 0) {
+ logError(e, 'error while reading VPN plugin output keyfile');
+
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
+ this.destroy();
+ return;
+ }
+ }
+
+ if (contentOverride && contentOverride.secrets.length) {
+ // Only show the dialog if we actually have something to ask
+ this._shellDialog = new NetworkSecretDialog(this._agent, this._requestId, this._connection, 'vpn', [], this._flags, contentOverride);
+ this._shellDialog.open(global.get_current_time());
+ } else {
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.CONFIRMED);
+ this.destroy();
+ }
+ }
+
+ _writeConnection() {
+ let vpnSetting = this._connection.get_setting_vpn();
+
+ try {
+ vpnSetting.foreach_data_item((key, value) => {
+ this._stdin.write(`DATA_KEY=${key}\n`, null);
+ this._stdin.write(`DATA_VAL=${value || ''}\n\n`, null);
+ });
+ vpnSetting.foreach_secret((key, value) => {
+ this._stdin.write(`SECRET_KEY=${key}\n`, null);
+ this._stdin.write(`SECRET_VAL=${value || ''}\n\n`, null);
+ });
+ this._stdin.write('DONE\n\n', null);
+ } catch (e) {
+ logError(e, 'internal error while writing connection to helper');
+
+ this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
+ this.destroy();
+ }
+ }
+};
+
+var NetworkAgent = class {
+ constructor() {
+ this._native = new Shell.NetworkAgent({
+ identifier: 'org.gnome.Shell.NetworkAgent',
+ capabilities: NM.SecretAgentCapabilities.VPN_HINTS,
+ auto_register: false,
+ });
+
+ this._dialogs = { };
+ this._vpnRequests = { };
+ this._notifications = { };
+
+ this._native.connect('new-request', this._newRequest.bind(this));
+ this._native.connect('cancel-request', this._cancelRequest.bind(this));
+
+ this._initialized = false;
+ this._initNative();
+ }
+
+ async _initNative() {
+ try {
+ await this._native.init_async(GLib.PRIORITY_DEFAULT, null);
+ this._initialized = true;
+ } catch (e) {
+ this._native = null;
+ logError(e, 'error initializing the NetworkManager Agent');
+ }
+ }
+
+ enable() {
+ if (!this._native)
+ return;
+
+ this._native.auto_register = true;
+ if (this._initialized && !this._native.registered)
+ this._native.register_async(null, null);
+ }
+
+ disable() {
+ let requestId;
+
+ for (requestId in this._dialogs)
+ this._dialogs[requestId].cancel();
+ this._dialogs = { };
+
+ for (requestId in this._vpnRequests)
+ this._vpnRequests[requestId].cancel(true);
+ this._vpnRequests = { };
+
+ for (requestId in this._notifications)
+ this._notifications[requestId].destroy();
+ this._notifications = { };
+
+ if (!this._native)
+ return;
+
+ this._native.auto_register = false;
+ if (this._initialized && this._native.registered)
+ this._native.unregister_async(null, null);
+ }
+
+ _showNotification(requestId, connection, settingName, hints, flags) {
+ let source = new MessageTray.Source(_("Network Manager"), 'network-transmit-receive');
+ source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
+
+ let title, body;
+
+ let connectionSetting = connection.get_setting_connection();
+ let connectionType = connectionSetting.get_connection_type();
+ switch (connectionType) {
+ case '802-11-wireless': {
+ let wirelessSetting = connection.get_setting_wireless();
+ let ssid = NM.utils_ssid_to_utf8(wirelessSetting.get_ssid().get_data());
+ title = _('Authentication required');
+ body = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid);
+ break;
+ }
+ case '802-3-ethernet':
+ title = _("Wired 802.1X authentication");
+ body = _('A password is required to connect to “%s”.').format(connection.get_id());
+ break;
+ case 'pppoe':
+ title = _("DSL authentication");
+ body = _('A password is required to connect to “%s”.').format(connection.get_id());
+ break;
+ case 'gsm':
+ if (hints.includes('pin')) {
+ title = _("PIN code required");
+ body = _("PIN code is needed for the mobile broadband device");
+ break;
+ }
+ // fall through
+ case 'cdma':
+ case 'bluetooth':
+ title = _('Authentication required');
+ body = _("A password is required to connect to “%s”.").format(connectionSetting.get_id());
+ break;
+ case 'vpn':
+ title = _("VPN password");
+ body = _("A password is required to connect to “%s”.").format(connectionSetting.get_id());
+ break;
+ default:
+ log(`Invalid connection type: ${connectionType}`);
+ this._native.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
+ return;
+ }
+
+ let notification = new MessageTray.Notification(source, title, body);
+
+ notification.connect('activated', () => {
+ notification.answered = true;
+ this._handleRequest(requestId, connection, settingName, hints, flags);
+ });
+
+ this._notifications[requestId] = notification;
+ notification.connect('destroy', () => {
+ if (!notification.answered)
+ this._native.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
+ delete this._notifications[requestId];
+ });
+
+ Main.messageTray.add(source);
+ source.showNotification(notification);
+ }
+
+ _newRequest(agent, requestId, connection, settingName, hints, flags) {
+ if (!(flags & NM.SecretAgentGetSecretsFlags.USER_REQUESTED))
+ this._showNotification(requestId, connection, settingName, hints, flags);
+ else
+ this._handleRequest(requestId, connection, settingName, hints, flags);
+ }
+
+ _handleRequest(requestId, connection, settingName, hints, flags) {
+ if (settingName == 'vpn') {
+ this._vpnRequest(requestId, connection, hints, flags);
+ return;
+ }
+
+ let dialog = new NetworkSecretDialog(this._native, requestId, connection, settingName, hints, flags);
+ dialog.connect('destroy', () => {
+ delete this._dialogs[requestId];
+ });
+ this._dialogs[requestId] = dialog;
+ dialog.open(global.get_current_time());
+ }
+
+ _cancelRequest(agent, requestId) {
+ if (this._dialogs[requestId]) {
+ this._dialogs[requestId].close(global.get_current_time());
+ this._dialogs[requestId].destroy();
+ delete this._dialogs[requestId];
+ } else if (this._vpnRequests[requestId]) {
+ this._vpnRequests[requestId].cancel(false);
+ delete this._vpnRequests[requestId];
+ }
+ }
+
+ async _vpnRequest(requestId, connection, hints, flags) {
+ let vpnSetting = connection.get_setting_vpn();
+ let serviceType = vpnSetting.service_type;
+
+ let binary = await this._findAuthBinary(serviceType);
+ if (!binary) {
+ log('Invalid VPN service type (cannot find authentication binary)');
+
+ /* cancel the auth process */
+ this._native.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
+ return;
+ }
+
+ let vpnRequest = new VPNRequestHandler(this._native, requestId, binary, serviceType, connection, hints, flags);
+ vpnRequest.connect('destroy', () => {
+ delete this._vpnRequests[requestId];
+ });
+ this._vpnRequests[requestId] = vpnRequest;
+ }
+
+ async _findAuthBinary(serviceType) {
+ let plugin;
+
+ try {
+ plugin = await this._native.search_vpn_plugin(serviceType);
+ } catch (e) {
+ logError(e);
+ return null;
+ }
+
+ const fileName = plugin.get_auth_dialog();
+ if (!GLib.file_test(fileName, GLib.FileTest.IS_EXECUTABLE)) {
+ log(`VPN plugin at ${fileName} is not executable`);
+ return null;
+ }
+
+ const prop = plugin.lookup_property('GNOME', 'supports-external-ui-mode');
+ const trimmedProp = prop?.trim().toLowerCase() ?? '';
+
+ return {
+ fileName,
+ supportsHints: plugin.supports_hints(),
+ externalUIMode: ['true', 'yes', 'on', '1'].includes(trimmedProp),
+ };
+ }
+};
+var Component = NetworkAgent;
diff --git a/js/ui/components/polkitAgent.js b/js/ui/components/polkitAgent.js
new file mode 100644
index 0000000..1da02e5
--- /dev/null
+++ b/js/ui/components/polkitAgent.js
@@ -0,0 +1,471 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Component */
+
+const {
+ AccountsService, Clutter, GLib, GObject,
+ Pango, PolkitAgent, Polkit, Shell, St,
+} = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+const UserWidget = imports.ui.userWidget;
+const Util = imports.misc.util;
+
+const DialogMode = {
+ AUTH: 0,
+ CONFIRM: 1,
+};
+
+const DIALOG_ICON_SIZE = 64;
+
+const DELAYED_RESET_TIMEOUT = 200;
+
+var AuthenticationDialog = GObject.registerClass({
+ Signals: { 'done': { param_types: [GObject.TYPE_BOOLEAN] } },
+}, class AuthenticationDialog extends ModalDialog.ModalDialog {
+ _init(actionId, description, cookie, userNames) {
+ super._init({ styleClass: 'prompt-dialog' });
+
+ this.actionId = actionId;
+ this.message = description;
+ this.userNames = userNames;
+
+ Main.sessionMode.connectObject('updated', () => {
+ this.visible = !Main.sessionMode.isLocked;
+ }, this);
+
+ this.connect('closed', this._onDialogClosed.bind(this));
+
+ let title = _("Authentication Required");
+
+ let headerContent = new Dialog.MessageDialogContent({ title, description });
+ this.contentLayout.add_child(headerContent);
+
+ let bodyContent = new Dialog.MessageDialogContent();
+
+ if (userNames.length > 1) {
+ log(`polkitAuthenticationAgent: Received ${userNames.length} ` +
+ 'identities that can be used for authentication. Only ' +
+ 'considering one.');
+ }
+
+ let userName = GLib.get_user_name();
+ if (!userNames.includes(userName))
+ userName = 'root';
+ if (!userNames.includes(userName))
+ userName = userNames[0];
+
+ this._user = AccountsService.UserManager.get_default().get_user(userName);
+
+ let userBox = new St.BoxLayout({
+ style_class: 'polkit-dialog-user-layout',
+ vertical: true,
+ });
+ bodyContent.add_child(userBox);
+
+ this._userAvatar = new UserWidget.Avatar(this._user, {
+ iconSize: DIALOG_ICON_SIZE,
+ });
+ this._userAvatar.x_align = Clutter.ActorAlign.CENTER;
+ userBox.add_child(this._userAvatar);
+
+ this._userLabel = new St.Label({
+ style_class: userName === 'root'
+ ? 'polkit-dialog-user-root-label'
+ : 'polkit-dialog-user-label',
+ });
+
+ if (userName === 'root')
+ this._userLabel.text = _('Administrator');
+
+ userBox.add_child(this._userLabel);
+
+ let passwordBox = new St.BoxLayout({
+ style_class: 'prompt-dialog-password-layout',
+ vertical: true,
+ });
+
+ this._passwordEntry = new St.PasswordEntry({
+ style_class: 'prompt-dialog-password-entry',
+ text: "",
+ can_focus: true,
+ visible: false,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ ShellEntry.addContextMenu(this._passwordEntry);
+ this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
+ this._passwordEntry.bind_property('reactive',
+ this._passwordEntry.clutter_text, 'editable',
+ GObject.BindingFlags.SYNC_CREATE);
+ passwordBox.add_child(this._passwordEntry);
+
+ let warningBox = new St.BoxLayout({ vertical: true });
+
+ let capsLockWarning = new ShellEntry.CapsLockWarning();
+ this._passwordEntry.bind_property('visible',
+ capsLockWarning, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ warningBox.add_child(capsLockWarning);
+
+ this._errorMessageLabel = new St.Label({
+ style_class: 'prompt-dialog-error-label',
+ visible: false,
+ });
+ this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._errorMessageLabel.clutter_text.line_wrap = true;
+ warningBox.add_child(this._errorMessageLabel);
+
+ this._infoMessageLabel = new St.Label({
+ style_class: 'prompt-dialog-info-label',
+ visible: false,
+ });
+ this._infoMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._infoMessageLabel.clutter_text.line_wrap = true;
+ warningBox.add_child(this._infoMessageLabel);
+
+ /* text is intentionally non-blank otherwise the height is not the same as for
+ * infoMessage and errorMessageLabel - but it is still invisible because
+ * gnome-shell.css sets the color to be transparent
+ */
+ this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label' });
+ this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._nullMessageLabel.clutter_text.line_wrap = true;
+ warningBox.add_child(this._nullMessageLabel);
+
+ passwordBox.add_child(warningBox);
+ bodyContent.add_child(passwordBox);
+
+ this._cancelButton = this.addButton({
+ label: _('Cancel'),
+ action: this.cancel.bind(this),
+ key: Clutter.KEY_Escape,
+ });
+ this._okButton = this.addButton({
+ label: _('Authenticate'),
+ action: this._onAuthenticateButtonPressed.bind(this),
+ reactive: false,
+ });
+ this._okButton.bind_property('reactive',
+ this._okButton, 'can-focus',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._passwordEntry.clutter_text.connect('text-changed', text => {
+ this._okButton.reactive = text.get_text().length > 0;
+ });
+
+ this.contentLayout.add_child(bodyContent);
+
+ this._doneEmitted = false;
+
+ this._mode = -1;
+
+ this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
+ this._cookie = cookie;
+
+ this._user.connectObject(
+ 'notify::is-loaded', this._onUserChanged.bind(this),
+ 'changed', this._onUserChanged.bind(this), this);
+ this._onUserChanged();
+ }
+
+ _initiateSession() {
+ this._destroySession(DELAYED_RESET_TIMEOUT);
+
+ this._session = new PolkitAgent.Session({
+ identity: this._identityToAuth,
+ cookie: this._cookie,
+ });
+ this._session.connectObject(
+ 'completed', this._onSessionCompleted.bind(this),
+ 'request', this._onSessionRequest.bind(this),
+ 'show-error', this._onSessionShowError.bind(this),
+ 'show-info', this._onSessionShowInfo.bind(this), this);
+ this._session.initiate();
+ }
+
+ _ensureOpen() {
+ // NOTE: ModalDialog.open() is safe to call if the dialog is
+ // already open - it just returns true without side-effects
+ if (!this.open(global.get_current_time())) {
+ // This can fail if e.g. unable to get input grab
+ //
+ // In an ideal world this wouldn't happen (because the
+ // Shell is in complete control of the session) but that's
+ // just not how things work right now.
+ //
+ // One way to make this happen is by running 'sleep 3;
+ // pkexec bash' and then opening a popup menu.
+ //
+ // We could add retrying if this turns out to be a problem
+
+ log('polkitAuthenticationAgent: Failed to show modal dialog. ' +
+ `Dismissing authentication request for action-id ${this.actionId} ` +
+ `cookie ${this._cookie}`);
+ this._emitDone(true);
+ }
+ }
+
+ _emitDone(dismissed) {
+ if (!this._doneEmitted) {
+ this._doneEmitted = true;
+ this.emit('done', dismissed);
+ }
+ }
+
+ _onEntryActivate() {
+ let response = this._passwordEntry.get_text();
+ if (response.length === 0)
+ return;
+
+ this._passwordEntry.reactive = false;
+ this._okButton.reactive = false;
+
+ this._session.response(response);
+ // When the user responds, dismiss already shown info and
+ // error texts (if any)
+ this._errorMessageLabel.hide();
+ this._infoMessageLabel.hide();
+ this._nullMessageLabel.show();
+ }
+
+ _onAuthenticateButtonPressed() {
+ if (this._mode === DialogMode.CONFIRM)
+ this._initiateSession();
+ else
+ this._onEntryActivate();
+ }
+
+ _onSessionCompleted(session, gainedAuthorization) {
+ if (this._completed || this._doneEmitted)
+ return;
+
+ this._completed = true;
+
+ /* Yay, all done */
+ if (gainedAuthorization) {
+ this._emitDone(false);
+ } else {
+ /* Unless we are showing an existing error message from the PAM
+ * module (the PAM module could be reporting the authentication
+ * error providing authentication-method specific information),
+ * show "Sorry, that didn't work. Please try again."
+ */
+ if (!this._errorMessageLabel.visible) {
+ /* Translators: "that didn't work" refers to the fact that the
+ * requested authentication was not gained; this can happen
+ * because of an authentication error (like invalid password),
+ * for instance. */
+ this._errorMessageLabel.set_text(_("Sorry, that didn’t work. Please try again."));
+ this._errorMessageLabel.show();
+ this._infoMessageLabel.hide();
+ this._nullMessageLabel.hide();
+
+ Util.wiggle(this._passwordEntry);
+ }
+
+ /* Try and authenticate again */
+ this._initiateSession();
+ }
+ }
+
+ _onSessionRequest(session, request, echoOn) {
+ if (this._sessionRequestTimeoutId) {
+ GLib.source_remove(this._sessionRequestTimeoutId);
+ this._sessionRequestTimeoutId = 0;
+ }
+
+ // Hack: The request string comes directly from PAM, if it's "Password:"
+ // we replace it with our own to allow localization, if it's something
+ // else we remove the last colon and any trailing or leading spaces.
+ if (request === 'Password:' || request === 'Password: ')
+ this._passwordEntry.hint_text = _('Password');
+ else
+ this._passwordEntry.hint_text = request.replace(/: *$/, '').trim();
+
+ this._passwordEntry.password_visible = echoOn;
+
+ this._passwordEntry.show();
+ this._passwordEntry.set_text('');
+ this._passwordEntry.reactive = true;
+ this._okButton.reactive = false;
+
+ this._ensureOpen();
+ this._passwordEntry.grab_key_focus();
+ }
+
+ _onSessionShowError(session, text) {
+ this._passwordEntry.set_text('');
+ this._errorMessageLabel.set_text(text);
+ this._errorMessageLabel.show();
+ this._infoMessageLabel.hide();
+ this._nullMessageLabel.hide();
+ this._ensureOpen();
+ }
+
+ _onSessionShowInfo(session, text) {
+ this._passwordEntry.set_text('');
+ this._infoMessageLabel.set_text(text);
+ this._infoMessageLabel.show();
+ this._errorMessageLabel.hide();
+ this._nullMessageLabel.hide();
+ this._ensureOpen();
+ }
+
+ _destroySession(delay = 0) {
+ this._session?.disconnectObject(this);
+
+ if (!this._completed)
+ this._session?.cancel();
+
+ this._completed = false;
+ this._session = null;
+
+ if (this._sessionRequestTimeoutId) {
+ GLib.source_remove(this._sessionRequestTimeoutId);
+ this._sessionRequestTimeoutId = 0;
+ }
+
+ let resetDialog = () => {
+ this._sessionRequestTimeoutId = 0;
+
+ if (this.state != ModalDialog.State.OPENED)
+ return GLib.SOURCE_REMOVE;
+
+ this._passwordEntry.hide();
+ this._cancelButton.grab_key_focus();
+ this._okButton.reactive = false;
+
+ return GLib.SOURCE_REMOVE;
+ };
+
+ if (delay) {
+ this._sessionRequestTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, resetDialog);
+ GLib.Source.set_name_by_id(this._sessionRequestTimeoutId, '[gnome-shell] this._sessionRequestTimeoutId');
+ } else {
+ resetDialog();
+ }
+ }
+
+ _onUserChanged() {
+ if (!this._user.is_loaded)
+ return;
+
+ let userName = this._user.get_user_name();
+ let realName = this._user.get_real_name();
+
+ if (userName !== 'root')
+ this._userLabel.set_text(realName);
+
+ this._userAvatar.update();
+
+ if (this._user.get_password_mode() === AccountsService.UserPasswordMode.NONE) {
+ if (this._mode === DialogMode.CONFIRM)
+ return;
+
+ this._mode = DialogMode.CONFIRM;
+ this._destroySession();
+
+ this._okButton.reactive = true;
+
+ /* We normally open the dialog when we get a "request" signal, but
+ * since in this case initiating a session would perform the
+ * authentication, only open the dialog and initiate the session
+ * when the user confirmed. */
+ this._ensureOpen();
+ } else {
+ if (this._mode === DialogMode.AUTH)
+ return;
+
+ this._mode = DialogMode.AUTH;
+ this._initiateSession();
+ }
+ }
+
+ close(timestamp) {
+ // Ensure cleanup if the dialog was never shown
+ if (this.state === ModalDialog.State.CLOSED)
+ this._onDialogClosed();
+ super.close(timestamp);
+ }
+
+ cancel() {
+ this._emitDone(true);
+ }
+
+ _onDialogClosed() {
+ Main.sessionMode.disconnectObject(this);
+
+ if (this._sessionRequestTimeoutId)
+ GLib.source_remove(this._sessionRequestTimeoutId);
+ this._sessionRequestTimeoutId = 0;
+
+ this._user?.disconnectObject(this);
+ this._user = null;
+
+ this._destroySession();
+ }
+});
+
+var AuthenticationAgent = GObject.registerClass(
+class AuthenticationAgent extends Shell.PolkitAuthenticationAgent {
+ _init() {
+ super._init();
+
+ this._currentDialog = null;
+ this.connect('initiate', this._onInitiate.bind(this));
+ this.connect('cancel', this._onCancel.bind(this));
+ this._sessionUpdatedId = 0;
+ }
+
+ enable() {
+ try {
+ this.register();
+ } catch (e) {
+ log('Failed to register AuthenticationAgent');
+ }
+ }
+
+ disable() {
+ try {
+ this.unregister();
+ } catch (e) {
+ log('Failed to unregister AuthenticationAgent');
+ }
+ }
+
+ _onInitiate(nativeAgent, actionId, message, iconName, cookie, userNames) {
+ // Don't pop up a dialog while locked
+ if (Main.sessionMode.isLocked) {
+ Main.sessionMode.connectObject('updated', () => {
+ Main.sessionMode.disconnectObject(this);
+
+ this._onInitiate(nativeAgent, actionId, message, iconName, cookie, userNames);
+ }, this);
+ return;
+ }
+
+ this._currentDialog = new AuthenticationDialog(actionId, message, cookie, userNames);
+ this._currentDialog.connect('done', this._onDialogDone.bind(this));
+ }
+
+ _onCancel(_nativeAgent) {
+ this._completeRequest(false);
+ }
+
+ _onDialogDone(_dialog, dismissed) {
+ this._completeRequest(dismissed);
+ }
+
+ _completeRequest(dismissed) {
+ this._currentDialog.close();
+ this._currentDialog = null;
+
+ Main.sessionMode.disconnectObject(this);
+
+ this.complete(dismissed);
+ }
+});
+
+var Component = AuthenticationAgent;
diff --git a/js/ui/components/telepathyClient.js b/js/ui/components/telepathyClient.js
new file mode 100644
index 0000000..d317822
--- /dev/null
+++ b/js/ui/components/telepathyClient.js
@@ -0,0 +1,1019 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Component */
+
+const { Clutter, Gio, GLib, GObject, St } = imports.gi;
+
+var Tpl = null;
+var Tp = null;
+try {
+ ({ TelepathyGLib: Tp, TelepathyLogger: Tpl } = imports.gi);
+
+ Gio._promisify(Tp.Channel.prototype, 'close_async');
+ Gio._promisify(Tp.TextChannel.prototype, 'send_message_async');
+ Gio._promisify(Tp.ChannelDispatchOperation.prototype, 'claim_with_async');
+ Gio._promisify(Tpl.LogManager.prototype, 'get_filtered_events_async');
+} catch (e) {
+ log('Telepathy is not available, chat integration will be disabled.');
+}
+
+const History = imports.misc.history;
+const Main = imports.ui.main;
+const MessageList = imports.ui.messageList;
+const MessageTray = imports.ui.messageTray;
+const Params = imports.misc.params;
+const Util = imports.misc.util;
+
+const HAVE_TP = Tp != null && Tpl != null;
+
+// See Notification.appendMessage
+var SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
+var SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
+var SCROLLBACK_RECENT_LENGTH = 20;
+var SCROLLBACK_IDLE_LENGTH = 5;
+
+// See Source._displayPendingMessages
+var SCROLLBACK_HISTORY_LINES = 10;
+
+// See Notification._onEntryChanged
+var COMPOSING_STOP_TIMEOUT = 5;
+
+var CHAT_EXPAND_LINES = 12;
+
+var NotificationDirection = {
+ SENT: 'chat-sent',
+ RECEIVED: 'chat-received',
+};
+
+const ChatMessage = HAVE_TP ? GObject.registerClass({
+ Properties: {
+ 'message-type': GObject.ParamSpec.int(
+ 'message-type', 'message-type', 'message-type',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Math.min(...Object.values(Tp.ChannelTextMessageType)),
+ Math.max(...Object.values(Tp.ChannelTextMessageType)),
+ Tp.ChannelTextMessageType.NORMAL),
+ 'text': GObject.ParamSpec.string(
+ 'text', 'text', 'text',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ null),
+ 'sender': GObject.ParamSpec.string(
+ 'sender', 'sender', 'sender',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ null),
+ 'timestamp': GObject.ParamSpec.int64(
+ 'timestamp', 'timestamp', 'timestamp',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ 0, Number.MAX_SAFE_INTEGER, 0),
+ 'direction': GObject.ParamSpec.string(
+ 'direction', 'direction', 'direction',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ null),
+ },
+}, class ChatMessageClass extends GObject.Object {
+ static newFromTpMessage(tpMessage, direction) {
+ return new ChatMessage({
+ 'message-type': tpMessage.get_message_type(),
+ 'text': tpMessage.to_text()[0],
+ 'sender': tpMessage.sender.alias,
+ 'timestamp': direction === NotificationDirection.RECEIVED
+ ? tpMessage.get_received_timestamp() : tpMessage.get_sent_timestamp(),
+ direction,
+ });
+ }
+
+ static newFromTplTextEvent(tplTextEvent) {
+ let direction =
+ tplTextEvent.get_sender().get_entity_type() === Tpl.EntityType.SELF
+ ? NotificationDirection.SENT : NotificationDirection.RECEIVED;
+
+ return new ChatMessage({
+ 'message-type': tplTextEvent.get_message_type(),
+ 'text': tplTextEvent.get_message(),
+ 'sender': tplTextEvent.get_sender().get_alias(),
+ 'timestamp': tplTextEvent.get_timestamp(),
+ direction,
+ });
+ }
+}) : null;
+
+
+var TelepathyComponent = class {
+ constructor() {
+ this._client = null;
+
+ if (!HAVE_TP)
+ return; // Telepathy isn't available
+
+ this._client = new TelepathyClient();
+ }
+
+ enable() {
+ if (!this._client)
+ return;
+
+ try {
+ this._client.register();
+ } catch (e) {
+ throw new Error(`Could not register Telepathy client. Error: ${e}`);
+ }
+
+ if (!this._client.account_manager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
+ this._client.account_manager.prepare_async(null, null);
+ }
+
+ disable() {
+ if (!this._client)
+ return;
+
+ this._client.unregister();
+ }
+};
+
+var TelepathyClient = HAVE_TP ? GObject.registerClass(
+class TelepathyClient extends Tp.BaseClient {
+ _init() {
+ // channel path -> ChatSource
+ this._chatSources = {};
+ this._chatState = Tp.ChannelChatState.ACTIVE;
+
+ // account path -> AccountNotification
+ this._accountNotifications = {};
+
+ // Define features we want
+ this._accountManager = Tp.AccountManager.dup();
+ let factory = this._accountManager.get_factory();
+ factory.add_account_features([Tp.Account.get_feature_quark_connection()]);
+ factory.add_connection_features([Tp.Connection.get_feature_quark_contact_list()]);
+ factory.add_channel_features([Tp.Channel.get_feature_quark_contacts()]);
+ factory.add_contact_features([
+ Tp.ContactFeature.ALIAS,
+ Tp.ContactFeature.AVATAR_DATA,
+ Tp.ContactFeature.PRESENCE,
+ Tp.ContactFeature.SUBSCRIPTION_STATES,
+ ]);
+
+ // Set up a SimpleObserver, which will call _observeChannels whenever a
+ // channel matching its filters is detected.
+ // The second argument, recover, means _observeChannels will be run
+ // for any existing channel as well.
+ super._init({
+ name: 'GnomeShell',
+ account_manager: this._accountManager,
+ uniquify_name: true,
+ });
+
+ // We only care about single-user text-based chats
+ let filter = {};
+ filter[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
+ filter[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE] = Tp.HandleType.CONTACT;
+
+ this.set_observer_recover(true);
+ this.add_observer_filter(filter);
+ this.add_approver_filter(filter);
+ this.add_handler_filter(filter);
+
+ // Allow other clients (such as Empathy) to preempt our channels if
+ // needed
+ this.set_delegated_channels_callback(
+ this._delegatedChannelsCb.bind(this));
+ }
+
+ vfunc_observe_channels(...args) {
+ let [account, conn, channels, dispatchOp_, requests_, context] = args;
+ let len = channels.length;
+ for (let i = 0; i < len; i++) {
+ let channel = channels[i];
+ let [targetHandle_, targetHandleType] = channel.get_handle();
+
+ if (channel.get_invalidated())
+ continue;
+
+ /* Only observe contact text channels */
+ if (!(channel instanceof Tp.TextChannel) ||
+ targetHandleType != Tp.HandleType.CONTACT)
+ continue;
+
+ this._createChatSource(account, conn, channel, channel.get_target_contact());
+ }
+
+ context.accept();
+ }
+
+ _createChatSource(account, conn, channel, contact) {
+ if (this._chatSources[channel.get_object_path()])
+ return;
+
+ let source = new ChatSource(account, conn, channel, contact, this);
+
+ this._chatSources[channel.get_object_path()] = source;
+ source.connect('destroy', () => {
+ delete this._chatSources[channel.get_object_path()];
+ });
+ }
+
+ vfunc_handle_channels(...args) {
+ let [account, conn, channels, requests_, userActionTime_, context] = args;
+ this._handlingChannels(account, conn, channels, true);
+ context.accept();
+ }
+
+ _handlingChannels(account, conn, channels, notify) {
+ let len = channels.length;
+ for (let i = 0; i < len; i++) {
+ let channel = channels[i];
+
+ // We can only handle text channel, so close any other channel
+ if (!(channel instanceof Tp.TextChannel)) {
+ channel.close_async();
+ continue;
+ }
+
+ if (channel.get_invalidated())
+ continue;
+
+ // 'notify' will be true when coming from an actual HandleChannels
+ // call, and not when from a successful Claim call. The point is
+ // we don't want to notify for a channel we just claimed which
+ // has no new messages (for example, a new channel which only has
+ // a delivery notification). We rely on _displayPendingMessages()
+ // and _messageReceived() to notify for new messages.
+
+ // But we should still notify from HandleChannels because the
+ // Telepathy spec states that handlers must foreground channels
+ // in HandleChannels calls which are already being handled.
+
+ if (notify && this.is_handling_channel(channel)) {
+ // We are already handling the channel, display the source
+ let source = this._chatSources[channel.get_object_path()];
+ if (source)
+ source.showNotification();
+ }
+ }
+ }
+
+ vfunc_add_dispatch_operation(...args) {
+ let [account, conn, channels, dispatchOp, context] = args;
+ let channel = channels[0];
+ let chanType = channel.get_channel_type();
+
+ if (channel.get_invalidated()) {
+ context.fail(new Tp.Error({
+ code: Tp.Error.INVALID_ARGUMENT,
+ message: 'Channel is invalidated',
+ }));
+ return;
+ }
+
+ if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT) {
+ this._approveTextChannel(account, conn, channel, dispatchOp, context);
+ } else {
+ context.fail(new Tp.Error({
+ code: Tp.Error.INVALID_ARGUMENT,
+ message: 'Unsupported channel type',
+ }));
+ }
+ }
+
+ async _approveTextChannel(account, conn, channel, dispatchOp, context) {
+ let [targetHandle_, targetHandleType] = channel.get_handle();
+
+ if (targetHandleType != Tp.HandleType.CONTACT) {
+ context.fail(new Tp.Error({
+ code: Tp.Error.INVALID_ARGUMENT,
+ message: 'Unsupported handle type',
+ }));
+ return;
+ }
+
+ context.accept();
+
+ // Approve private text channels right away as we are going to handle it
+ try {
+ await dispatchOp.claim_with_async(this);
+ this._handlingChannels(account, conn, [channel], false);
+ } catch (err) {
+ log(`Failed to claim channel: ${err}`);
+ }
+ }
+
+ _delegatedChannelsCb(_client, _channels) {
+ // Nothing to do as we don't make a distinction between observed and
+ // handled channels.
+ }
+}) : null;
+
+var ChatSource = HAVE_TP ? GObject.registerClass(
+class ChatSource extends MessageTray.Source {
+ _init(account, conn, channel, contact, client) {
+ this._account = account;
+ this._contact = contact;
+ this._client = client;
+
+ super._init(contact.get_alias());
+
+ this.isChat = true;
+ this._pendingMessages = [];
+
+ this._conn = conn;
+ this._channel = channel;
+
+ this._notifyTimeoutId = 0;
+
+ this._presence = contact.get_presence_type();
+
+ this._channel.connectObject(
+ 'invalidated', this._channelClosed.bind(this),
+ 'message-sent', this._messageSent.bind(this),
+ 'message-received', this._messageReceived.bind(this),
+ 'pending-message-removed', this._pendingRemoved.bind(this), this);
+
+ this._contact.connectObject(
+ 'notify::alias', this._updateAlias.bind(this),
+ 'notify::avatar-file', this._updateAvatarIcon.bind(this),
+ 'presence-changed', this._presenceChanged.bind(this), this);
+
+ // Add ourselves as a source.
+ Main.messageTray.add(this);
+
+ this._getLogMessages();
+ }
+
+ _ensureNotification() {
+ if (this._notification)
+ return;
+
+ this._notification = new ChatNotification(this);
+ this._notification.connectObject(
+ 'activated', this.open.bind(this),
+ 'destroy', () => (this._notification = null),
+ 'updated', () => {
+ if (this._banner && this._banner.expanded)
+ this._ackMessages();
+ }, this);
+ this.pushNotification(this._notification);
+ }
+
+ _createPolicy() {
+ if (this._account.protocol_name == 'irc')
+ return new MessageTray.NotificationApplicationPolicy('org.gnome.Polari');
+ return new MessageTray.NotificationApplicationPolicy('empathy');
+ }
+
+ createBanner() {
+ this._banner = new ChatNotificationBanner(this._notification);
+
+ // We ack messages when the user expands the new notification
+ this._banner.connectObject(
+ 'expanded', this._ackMessages.bind(this),
+ 'destroy', () => (this._banner = null), this);
+
+ return this._banner;
+ }
+
+ _updateAlias() {
+ let oldAlias = this.title;
+ let newAlias = this._contact.get_alias();
+
+ if (oldAlias == newAlias)
+ return;
+
+ this.setTitle(newAlias);
+ if (this._notification)
+ this._notification.appendAliasChange(oldAlias, newAlias);
+ }
+
+ getIcon() {
+ let file = this._contact.get_avatar_file();
+ if (file)
+ return new Gio.FileIcon({ file });
+ else
+ return new Gio.ThemedIcon({ name: 'avatar-default' });
+ }
+
+ getSecondaryIcon() {
+ let iconName;
+ let presenceType = this._contact.get_presence_type();
+
+ switch (presenceType) {
+ case Tp.ConnectionPresenceType.AVAILABLE:
+ iconName = 'user-available';
+ break;
+ case Tp.ConnectionPresenceType.BUSY:
+ iconName = 'user-busy';
+ break;
+ case Tp.ConnectionPresenceType.OFFLINE:
+ iconName = 'user-offline';
+ break;
+ case Tp.ConnectionPresenceType.HIDDEN:
+ iconName = 'user-invisible';
+ break;
+ case Tp.ConnectionPresenceType.AWAY:
+ iconName = 'user-away';
+ break;
+ case Tp.ConnectionPresenceType.EXTENDED_AWAY:
+ iconName = 'user-idle';
+ break;
+ default:
+ iconName = 'user-offline';
+ }
+ return new Gio.ThemedIcon({ name: iconName });
+ }
+
+ _updateAvatarIcon() {
+ this.iconUpdated();
+ if (this._notification) {
+ this._notification.update(this._notification.title,
+ this._notification.bannerBodyText,
+ { gicon: this.getIcon() });
+ }
+ }
+
+ open() {
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+
+ if (this._client.is_handling_channel(this._channel)) {
+ // We are handling the channel, try to pass it to Empathy or Polari
+ // (depending on the channel type)
+ // We don't check if either app is available - mission control will
+ // fallback to something else if activation fails
+
+ let target;
+ if (this._channel.connection.protocol_name == 'irc')
+ target = 'org.freedesktop.Telepathy.Client.Polari';
+ else
+ target = 'org.freedesktop.Telepathy.Client.Empathy.Chat';
+ this._client.delegate_channels_async([this._channel], global.get_current_time(), target, null);
+ } else {
+ // We are not the handler, just ask to present the channel
+ let dbus = Tp.DBusDaemon.dup();
+ let cd = Tp.ChannelDispatcher.new(dbus);
+
+ cd.present_channel_async(this._channel, global.get_current_time(), null);
+ }
+ }
+
+ async _getLogMessages() {
+ let logManager = Tpl.LogManager.dup_singleton();
+ let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
+
+ const [events] = await logManager.get_filtered_events_async(
+ this._account, entity,
+ Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
+ null);
+
+ let logMessages = events.map(e => ChatMessage.newFromTplTextEvent(e));
+ this._ensureNotification();
+
+ let pendingTpMessages = this._channel.get_pending_messages();
+ let pendingMessages = [];
+
+ for (let i = 0; i < pendingTpMessages.length; i++) {
+ let message = pendingTpMessages[i];
+
+ if (message.get_message_type() == Tp.ChannelTextMessageType.DELIVERY_REPORT)
+ continue;
+
+ pendingMessages.push(ChatMessage.newFromTpMessage(message,
+ NotificationDirection.RECEIVED));
+
+ this._pendingMessages.push(message);
+ }
+
+ this.countUpdated();
+
+ let showTimestamp = false;
+
+ for (let i = 0; i < logMessages.length; i++) {
+ let logMessage = logMessages[i];
+ let isPending = false;
+
+ // Skip any log messages that are also in pendingMessages
+ for (let j = 0; j < pendingMessages.length; j++) {
+ let pending = pendingMessages[j];
+ if (logMessage.timestamp == pending.timestamp && logMessage.text == pending.text) {
+ isPending = true;
+ break;
+ }
+ }
+
+ if (!isPending) {
+ showTimestamp = true;
+ this._notification.appendMessage(logMessage, true, ['chat-log-message']);
+ }
+ }
+
+ if (showTimestamp)
+ this._notification.appendTimestamp();
+
+ for (let i = 0; i < pendingMessages.length; i++)
+ this._notification.appendMessage(pendingMessages[i], true);
+
+ if (pendingMessages.length > 0)
+ this.showNotification();
+ }
+
+ destroy(reason) {
+ if (this._client.is_handling_channel(this._channel)) {
+ this._ackMessages();
+ // The chat box has been destroyed so it can't
+ // handle the channel any more.
+ this._channel.close_async();
+ } else {
+ // Don't indicate any unread messages when the notification
+ // that represents them has been destroyed.
+ this._pendingMessages = [];
+ this.countUpdated();
+ }
+
+ // Keep source alive while the channel is open
+ if (reason != MessageTray.NotificationDestroyedReason.SOURCE_CLOSED)
+ return;
+
+ if (this._destroyed)
+ return;
+
+ this._destroyed = true;
+ this._channel.disconnectObject(this);
+ this._contact.disconnectObject(this);
+
+ super.destroy(reason);
+ }
+
+ _channelClosed() {
+ this.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
+ }
+
+ /* All messages are new messages for Telepathy sources */
+ get count() {
+ return this._pendingMessages.length;
+ }
+
+ get unseenCount() {
+ return this.count;
+ }
+
+ get countVisible() {
+ return this.count > 0;
+ }
+
+ _messageReceived(channel, message) {
+ if (message.get_message_type() == Tp.ChannelTextMessageType.DELIVERY_REPORT)
+ return;
+
+ this._ensureNotification();
+ this._pendingMessages.push(message);
+ this.countUpdated();
+
+ message = ChatMessage.newFromTpMessage(message,
+ NotificationDirection.RECEIVED);
+ this._notification.appendMessage(message);
+
+ // Wait a bit before notifying for the received message, a handler
+ // could ack it in the meantime.
+ if (this._notifyTimeoutId != 0)
+ GLib.source_remove(this._notifyTimeoutId);
+ this._notifyTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500,
+ this._notifyTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._notifyTimeoutId, '[gnome-shell] this._notifyTimeout');
+ }
+
+ _notifyTimeout() {
+ if (this._pendingMessages.length != 0)
+ this.showNotification();
+
+ this._notifyTimeoutId = 0;
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ // This is called for both messages we send from
+ // our client and other clients as well.
+ _messageSent(channel, message, _flags, _token) {
+ this._ensureNotification();
+ message = ChatMessage.newFromTpMessage(message,
+ NotificationDirection.SENT);
+ this._notification.appendMessage(message);
+ }
+
+ showNotification() {
+ super.showNotification(this._notification);
+ }
+
+ respond(text) {
+ let type;
+ if (text.slice(0, 4) == '/me ') {
+ type = Tp.ChannelTextMessageType.ACTION;
+ text = text.slice(4);
+ } else {
+ type = Tp.ChannelTextMessageType.NORMAL;
+ }
+
+ let msg = Tp.ClientMessage.new_text(type, text);
+ this._channel.send_message_async(msg, 0);
+ }
+
+ setChatState(state) {
+ // We don't want to send COMPOSING every time a letter is typed into
+ // the entry. We send the state only when it changes. Telepathy/Empathy
+ // might change it behind our back if the user is using both
+ // gnome-shell's entry and the Empathy conversation window. We could
+ // keep track of it with the ChatStateChanged signal but it is good
+ // enough right now.
+ if (state != this._chatState) {
+ this._chatState = state;
+ this._channel.set_chat_state_async(state, null);
+ }
+ }
+
+ _presenceChanged(_contact, _presence, _status, _message) {
+ if (this._notification) {
+ this._notification.update(this._notification.title,
+ this._notification.bannerBodyText,
+ { secondaryGIcon: this.getSecondaryIcon() });
+ }
+ }
+
+ _pendingRemoved(channel, message) {
+ let idx = this._pendingMessages.indexOf(message);
+
+ if (idx >= 0) {
+ this._pendingMessages.splice(idx, 1);
+ this.countUpdated();
+ }
+
+ if (this._pendingMessages.length == 0 &&
+ this._banner && !this._banner.expanded)
+ this._banner.hide();
+ }
+
+ _ackMessages() {
+ // Don't clear our messages here, tp-glib will send a
+ // 'pending-message-removed' for each one.
+ this._channel.ack_all_pending_messages_async(null);
+ }
+}) : null;
+
+const ChatNotificationMessage = HAVE_TP ? GObject.registerClass(
+class ChatNotificationMessage extends GObject.Object {
+ _init(props = {}) {
+ super._init();
+ this.set(props);
+ }
+}) : null;
+
+var ChatNotification = HAVE_TP ? GObject.registerClass({
+ Signals: {
+ 'message-removed': { param_types: [ChatNotificationMessage.$gtype] },
+ 'message-added': { param_types: [ChatNotificationMessage.$gtype] },
+ 'timestamp-changed': { param_types: [ChatNotificationMessage.$gtype] },
+ },
+}, class ChatNotification extends MessageTray.Notification {
+ _init(source) {
+ super._init(source, source.title, null,
+ { secondaryGIcon: source.getSecondaryIcon() });
+ this.setUrgency(MessageTray.Urgency.HIGH);
+ this.setResident(true);
+
+ this.messages = [];
+ this._timestampTimeoutId = 0;
+ }
+
+ destroy(reason) {
+ if (this._timestampTimeoutId)
+ GLib.source_remove(this._timestampTimeoutId);
+ this._timestampTimeoutId = 0;
+ super.destroy(reason);
+ }
+
+ /**
+ * appendMessage:
+ * @param {Object} message: An object with the properties
+ * {string} message.text: the body of the message,
+ * {Tp.ChannelTextMessageType} message.messageType: the type
+ * {string} message.sender: the name of the sender,
+ * {number} message.timestamp: the time the message was sent
+ * {NotificationDirection} message.direction: a #NotificationDirection
+ *
+ * @param {bool} noTimestamp: Whether to add a timestamp. If %true,
+ * no timestamp will be added, regardless of the difference since
+ * the last timestamp
+ */
+ appendMessage(message, noTimestamp) {
+ let messageBody = GLib.markup_escape_text(message.text, -1);
+ let styles = [message.direction];
+
+ if (message.messageType == Tp.ChannelTextMessageType.ACTION) {
+ let senderAlias = GLib.markup_escape_text(message.sender, -1);
+ messageBody = `<i>${senderAlias}</i> ${messageBody}`;
+ styles.push('chat-action');
+ }
+
+ if (message.direction == NotificationDirection.RECEIVED) {
+ this.update(this.source.title, messageBody, {
+ datetime: GLib.DateTime.new_from_unix_local(message.timestamp),
+ bannerMarkup: true,
+ });
+ }
+
+ let group = message.direction == NotificationDirection.RECEIVED
+ ? 'received' : 'sent';
+
+ this._append({
+ body: messageBody,
+ group,
+ styles,
+ timestamp: message.timestamp,
+ noTimestamp,
+ });
+ }
+
+ _filterMessages() {
+ if (this.messages.length < 1)
+ return;
+
+ let lastMessageTime = this.messages[0].timestamp;
+ let currentTime = Date.now() / 1000;
+
+ // Keep the scrollback from growing too long. If the most
+ // recent message (before the one we just added) is within
+ // SCROLLBACK_RECENT_TIME, we will keep
+ // SCROLLBACK_RECENT_LENGTH previous messages. Otherwise
+ // we'll keep SCROLLBACK_IDLE_LENGTH messages.
+
+ let maxLength = lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME
+ ? SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH;
+
+ let filteredHistory = this.messages.filter(item => item.realMessage);
+ if (filteredHistory.length > maxLength) {
+ let lastMessageToKeep = filteredHistory[maxLength];
+ let expired = this.messages.splice(this.messages.indexOf(lastMessageToKeep));
+ for (let i = 0; i < expired.length; i++)
+ this.emit('message-removed', expired[i]);
+ }
+ }
+
+ /**
+ * _append:
+ * @param {Object} props: An object with the properties:
+ * {string} props.body: The text of the message.
+ * {string} props.group: The group of the message, one of:
+ * 'received', 'sent', 'meta'.
+ * {string[]} props.styles: Style class names for the message to have.
+ * {number} props.timestamp: The timestamp of the message.
+ * {bool} props.noTimestamp: suppress timestamp signal?
+ */
+ _append(props) {
+ let currentTime = Date.now() / 1000;
+ props = Params.parse(props, {
+ body: null,
+ group: null,
+ styles: [],
+ timestamp: currentTime,
+ noTimestamp: false,
+ });
+ const { noTimestamp } = props;
+ delete props.noTimestamp;
+
+ // Reset the old message timeout
+ if (this._timestampTimeoutId)
+ GLib.source_remove(this._timestampTimeoutId);
+ this._timestampTimeoutId = 0;
+
+ let message = new ChatNotificationMessage({
+ realMessage: props.group !== 'meta',
+ showTimestamp: false,
+ ...props,
+ });
+
+ this.messages.unshift(message);
+ this.emit('message-added', message);
+
+ if (!noTimestamp) {
+ let timestamp = props.timestamp;
+ if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) {
+ this.appendTimestamp();
+ } else {
+ // Schedule a new timestamp in SCROLLBACK_IMMEDIATE_TIME
+ // from the timestamp of the message.
+ this._timestampTimeoutId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT,
+ SCROLLBACK_IMMEDIATE_TIME - (currentTime - timestamp),
+ this.appendTimestamp.bind(this));
+ GLib.Source.set_name_by_id(this._timestampTimeoutId, '[gnome-shell] this.appendTimestamp');
+ }
+ }
+
+ this._filterMessages();
+ }
+
+ appendTimestamp() {
+ this._timestampTimeoutId = 0;
+
+ this.messages[0].showTimestamp = true;
+ this.emit('timestamp-changed', this.messages[0]);
+
+ this._filterMessages();
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ appendAliasChange(oldAlias, newAlias) {
+ oldAlias = GLib.markup_escape_text(oldAlias, -1);
+ newAlias = GLib.markup_escape_text(newAlias, -1);
+
+ /* Translators: this is the other person changing their old IM name to their new
+ IM name. */
+ const message = `<i>${
+ _('%s is now known as %s').format(oldAlias, newAlias)}</i>`;
+
+ this._append({
+ body: message,
+ group: 'meta',
+ styles: ['chat-meta-message'],
+ });
+
+ this._filterMessages();
+ }
+}) : null;
+
+var ChatLineBox = GObject.registerClass(
+class ChatLineBox extends St.BoxLayout {
+ vfunc_get_preferred_height(forWidth) {
+ let [, natHeight] = super.vfunc_get_preferred_height(forWidth);
+ return [natHeight, natHeight];
+ }
+});
+
+var ChatNotificationBanner = GObject.registerClass(
+class ChatNotificationBanner extends MessageTray.NotificationBanner {
+ _init(notification) {
+ super._init(notification);
+
+ this._responseEntry = new St.Entry({
+ style_class: 'chat-response',
+ x_expand: true,
+ can_focus: true,
+ });
+ this._responseEntry.clutter_text.connect('activate', this._onEntryActivated.bind(this));
+ this._responseEntry.clutter_text.connect('text-changed', this._onEntryChanged.bind(this));
+ this.setActionArea(this._responseEntry);
+
+ this._responseEntry.clutter_text.connect('key-focus-in', () => {
+ this.focused = true;
+ });
+ this._responseEntry.clutter_text.connect('key-focus-out', () => {
+ this.focused = false;
+ this.emit('unfocused');
+ });
+
+ this._scrollArea = new St.ScrollView({
+ style_class: 'chat-scrollview vfade',
+ vscrollbar_policy: St.PolicyType.AUTOMATIC,
+ hscrollbar_policy: St.PolicyType.NEVER,
+ visible: this.expanded,
+ });
+ this._contentArea = new St.BoxLayout({
+ style_class: 'chat-body',
+ vertical: true,
+ });
+ this._scrollArea.add_actor(this._contentArea);
+
+ this.setExpandedBody(this._scrollArea);
+ this.setExpandedLines(CHAT_EXPAND_LINES);
+
+ this._lastGroup = null;
+
+ // Keep track of the bottom position for the current adjustment and
+ // force a scroll to the bottom if things change while we were at the
+ // bottom
+ this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value;
+ this._scrollArea.vscroll.adjustment.connect('changed', adjustment => {
+ if (adjustment.value == this._oldMaxScrollValue)
+ this.scrollTo(St.Side.BOTTOM);
+ this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
+ });
+
+ this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text });
+
+ this._composingTimeoutId = 0;
+
+ this._messageActors = new Map();
+
+ this.notification.connectObject(
+ 'timestamp-changed', (n, message) => this._updateTimestamp(message),
+ 'message-added', (n, message) => this._addMessage(message),
+ 'message-removed', (n, message) => {
+ let actor = this._messageActors.get(message);
+ if (this._messageActors.delete(message))
+ actor.destroy();
+ }, this);
+
+ for (let i = this.notification.messages.length - 1; i >= 0; i--)
+ this._addMessage(this.notification.messages[i]);
+ }
+
+ scrollTo(side) {
+ let adjustment = this._scrollArea.vscroll.adjustment;
+ if (side == St.Side.TOP)
+ adjustment.value = adjustment.lower;
+ else if (side == St.Side.BOTTOM)
+ adjustment.value = adjustment.upper;
+ }
+
+ hide() {
+ this.emit('done-displaying');
+ }
+
+ _addMessage(message) {
+ let body = new MessageList.URLHighlighter(message.body, true, true);
+
+ let styles = message.styles;
+ for (let i = 0; i < styles.length; i++)
+ body.add_style_class_name(styles[i]);
+
+ let group = message.group;
+ if (group != this._lastGroup) {
+ this._lastGroup = group;
+ body.add_style_class_name('chat-new-group');
+ }
+
+ let lineBox = new ChatLineBox();
+ lineBox.add(body);
+ this._contentArea.add_actor(lineBox);
+ this._messageActors.set(message, lineBox);
+
+ this._updateTimestamp(message);
+ }
+
+ _updateTimestamp(message) {
+ let actor = this._messageActors.get(message);
+ if (!actor)
+ return;
+
+ while (actor.get_n_children() > 1)
+ actor.get_child_at_index(1).destroy();
+
+ if (message.showTimestamp) {
+ let lastMessageTime = message.timestamp;
+ let lastMessageDate = new Date(lastMessageTime * 1000);
+
+ let timeLabel = Util.createTimeLabel(lastMessageDate);
+ timeLabel.style_class = 'chat-meta-message';
+ timeLabel.x_expand = timeLabel.y_expand = true;
+ timeLabel.x_align = timeLabel.y_align = Clutter.ActorAlign.END;
+
+ actor.add_actor(timeLabel);
+ }
+ }
+
+ _onEntryActivated() {
+ let text = this._responseEntry.get_text();
+ if (text == '')
+ return;
+
+ this._inputHistory.addItem(text);
+
+ // Telepathy sends out the Sent signal for us.
+ // see Source._messageSent
+ this._responseEntry.set_text('');
+ this.notification.source.respond(text);
+ }
+
+ _composingStopTimeout() {
+ this._composingTimeoutId = 0;
+
+ this.notification.source.setChatState(Tp.ChannelChatState.PAUSED);
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _onEntryChanged() {
+ let text = this._responseEntry.get_text();
+
+ // If we're typing, we want to send COMPOSING.
+ // If we empty the entry, we want to send ACTIVE.
+ // If we've stopped typing for COMPOSING_STOP_TIMEOUT
+ // seconds, we want to send PAUSED.
+
+ // Remove composing timeout.
+ if (this._composingTimeoutId > 0) {
+ GLib.source_remove(this._composingTimeoutId);
+ this._composingTimeoutId = 0;
+ }
+
+ if (text != '') {
+ this.notification.source.setChatState(Tp.ChannelChatState.COMPOSING);
+
+ this._composingTimeoutId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT,
+ COMPOSING_STOP_TIMEOUT,
+ this._composingStopTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._composingTimeoutId, '[gnome-shell] this._composingStopTimeout');
+ } else {
+ this.notification.source.setChatState(Tp.ChannelChatState.ACTIVE);
+ }
+ }
+});
+
+var Component = TelepathyComponent;
diff --git a/js/ui/ctrlAltTab.js b/js/ui/ctrlAltTab.js
new file mode 100644
index 0000000..421fecf
--- /dev/null
+++ b/js/ui/ctrlAltTab.js
@@ -0,0 +1,203 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported CtrlAltTabManager */
+
+const { Clutter, GObject, Meta, Shell, St } = imports.gi;
+
+const Main = imports.ui.main;
+const SwitcherPopup = imports.ui.switcherPopup;
+const Params = imports.misc.params;
+
+var POPUP_APPICON_SIZE = 96;
+
+var SortGroup = {
+ TOP: 0,
+ MIDDLE: 1,
+ BOTTOM: 2,
+};
+
+var CtrlAltTabManager = class CtrlAltTabManager {
+ constructor() {
+ this._items = [];
+ this.addGroup(global.window_group,
+ _('Windows'),
+ 'focus-windows-symbolic', {
+ sortGroup: SortGroup.TOP,
+ focusCallback: this._focusWindows.bind(this),
+ });
+ }
+
+ addGroup(root, name, icon, params) {
+ const item = Params.parse(params, {
+ sortGroup: SortGroup.MIDDLE,
+ proxy: root,
+ focusCallback: null,
+ });
+
+ item.root = root;
+ item.name = name;
+ item.iconName = icon;
+
+ this._items.push(item);
+ root.connect('destroy', () => this.removeGroup(root));
+ if (root instanceof St.Widget)
+ global.focus_manager.add_group(root);
+ }
+
+ removeGroup(root) {
+ if (root instanceof St.Widget)
+ global.focus_manager.remove_group(root);
+ for (let i = 0; i < this._items.length; i++) {
+ if (this._items[i].root == root) {
+ this._items.splice(i, 1);
+ return;
+ }
+ }
+ }
+
+ focusGroup(item, timestamp) {
+ if (item.focusCallback)
+ item.focusCallback(timestamp);
+ else
+ item.root.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ }
+
+ // Sort the items into a consistent order; panel first, tray last,
+ // and everything else in between, sorted by X coordinate, so that
+ // they will have the same left-to-right ordering in the
+ // Ctrl-Alt-Tab dialog as they do onscreen.
+ _sortItems(a, b) {
+ if (a.sortGroup != b.sortGroup)
+ return a.sortGroup - b.sortGroup;
+
+ let [ax] = a.proxy.get_transformed_position();
+ let [bx] = b.proxy.get_transformed_position();
+
+ return ax - bx;
+ }
+
+ popup(backward, binding, mask) {
+ // Start with the set of focus groups that are currently mapped
+ let items = this._items.filter(item => item.proxy.mapped);
+
+ // And add the windows metacity would show in its Ctrl-Alt-Tab list
+ if (Main.sessionMode.hasWindows && !Main.overview.visible) {
+ let display = global.display;
+ let workspaceManager = global.workspace_manager;
+ let activeWorkspace = workspaceManager.get_active_workspace();
+ let windows = display.get_tab_list(Meta.TabList.DOCKS,
+ activeWorkspace);
+ let windowTracker = Shell.WindowTracker.get_default();
+ let textureCache = St.TextureCache.get_default();
+ for (let i = 0; i < windows.length; i++) {
+ let icon = null;
+ let iconName = null;
+ if (windows[i].get_window_type() == Meta.WindowType.DESKTOP) {
+ iconName = 'video-display-symbolic';
+ } else {
+ let app = windowTracker.get_window_app(windows[i]);
+ if (app) {
+ icon = app.create_icon_texture(POPUP_APPICON_SIZE);
+ } else {
+ icon = new St.Icon({
+ gicon: textureCache.bind_cairo_surface_property(windows[i], 'icon'),
+ icon_size: POPUP_APPICON_SIZE,
+ });
+ }
+ }
+
+ items.push({
+ name: windows[i].title,
+ proxy: windows[i].get_compositor_private(),
+ focusCallback: timestamp => {
+ Main.activateWindow(windows[i], timestamp);
+ },
+ iconActor: icon,
+ iconName,
+ sortGroup: SortGroup.MIDDLE,
+ });
+ }
+ }
+
+ if (!items.length)
+ return;
+
+ items.sort(this._sortItems.bind(this));
+
+ if (!this._popup) {
+ this._popup = new CtrlAltTabPopup(items);
+ this._popup.show(backward, binding, mask);
+
+ this._popup.connect('destroy',
+ () => {
+ this._popup = null;
+ });
+ }
+ }
+
+ _focusWindows(timestamp) {
+ global.display.focus_default_window(timestamp);
+ }
+};
+
+var CtrlAltTabPopup = GObject.registerClass(
+class CtrlAltTabPopup extends SwitcherPopup.SwitcherPopup {
+ _init(items) {
+ super._init(items);
+
+ this._switcherList = new CtrlAltTabSwitcher(this._items);
+ }
+
+ _keyPressHandler(keysym, action) {
+ if (action == Meta.KeyBindingAction.SWITCH_PANELS)
+ this._select(this._next());
+ else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD)
+ this._select(this._previous());
+ else if (keysym == Clutter.KEY_Left)
+ this._select(this._previous());
+ else if (keysym == Clutter.KEY_Right)
+ this._select(this._next());
+ else
+ return Clutter.EVENT_PROPAGATE;
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _finish(time) {
+ super._finish(time);
+ Main.ctrlAltTabManager.focusGroup(this._items[this._selectedIndex], time);
+ }
+});
+
+var CtrlAltTabSwitcher = GObject.registerClass(
+class CtrlAltTabSwitcher extends SwitcherPopup.SwitcherList {
+ _init(items) {
+ super._init(true);
+
+ for (let i = 0; i < items.length; i++)
+ this._addIcon(items[i]);
+ }
+
+ _addIcon(item) {
+ const box = new St.BoxLayout({
+ style_class: 'alt-tab-app',
+ vertical: true,
+ });
+
+ let icon = item.iconActor;
+ if (!icon) {
+ icon = new St.Icon({
+ icon_name: item.iconName,
+ icon_size: POPUP_APPICON_SIZE,
+ });
+ }
+ box.add_child(icon);
+
+ let text = new St.Label({
+ text: item.name,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add_child(text);
+
+ this.addItem(box, text);
+ }
+});
diff --git a/js/ui/dash.js b/js/ui/dash.js
new file mode 100644
index 0000000..165f8ea
--- /dev/null
+++ b/js/ui/dash.js
@@ -0,0 +1,992 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Dash */
+
+const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi;
+
+const AppDisplay = imports.ui.appDisplay;
+const AppFavorites = imports.ui.appFavorites;
+const DND = imports.ui.dnd;
+const IconGrid = imports.ui.iconGrid;
+const Main = imports.ui.main;
+const Overview = imports.ui.overview;
+
+var DASH_ANIMATION_TIME = 200;
+var DASH_ITEM_LABEL_SHOW_TIME = 150;
+var DASH_ITEM_LABEL_HIDE_TIME = 100;
+var DASH_ITEM_HOVER_TIMEOUT = 300;
+
+function getAppFromSource(source) {
+ if (source instanceof AppDisplay.AppIcon)
+ return source.app;
+ else
+ return null;
+}
+
+var DashIcon = GObject.registerClass(
+class DashIcon extends AppDisplay.AppIcon {
+ _init(app) {
+ super._init(app, {
+ setSizeManually: true,
+ showLabel: false,
+ });
+ }
+
+ popupMenu() {
+ super.popupMenu(St.Side.BOTTOM);
+ }
+
+ // Disable scale-n-fade methods used during DND by parent
+ scaleAndFade() {
+ }
+
+ undoScaleAndFade() {
+ }
+
+ handleDragOver() {
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ acceptDrop() {
+ return false;
+ }
+});
+
+// A container like StBin, but taking the child's scale into account
+// when requesting a size
+var DashItemContainer = GObject.registerClass(
+class DashItemContainer extends St.Widget {
+ _init() {
+ super._init({
+ style_class: 'dash-item-container',
+ pivot_point: new Graphene.Point({ x: .5, y: .5 }),
+ layout_manager: new Clutter.BinLayout(),
+ scale_x: 0,
+ scale_y: 0,
+ opacity: 0,
+ x_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._labelText = "";
+ this.label = new St.Label({ style_class: 'dash-label' });
+ this.label.hide();
+ Main.layoutManager.addChrome(this.label);
+ this.label.connectObject('destroy', () => (this.label = null), this);
+ this.label_actor = this.label;
+
+ this.child = null;
+ this.animatingOut = false;
+
+ this.connect('notify::scale-x', () => this.queue_relayout());
+ this.connect('notify::scale-y', () => this.queue_relayout());
+
+ this.connect('destroy', () => {
+ if (this.child != null)
+ this.child.destroy();
+ this.label?.destroy();
+ });
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ let themeNode = this.get_theme_node();
+ forWidth = themeNode.adjust_for_width(forWidth);
+ let [minHeight, natHeight] = super.vfunc_get_preferred_height(forWidth);
+ return themeNode.adjust_preferred_height(minHeight * this.scale_y,
+ natHeight * this.scale_y);
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ let themeNode = this.get_theme_node();
+ forHeight = themeNode.adjust_for_height(forHeight);
+ let [minWidth, natWidth] = super.vfunc_get_preferred_width(forHeight);
+ return themeNode.adjust_preferred_width(minWidth * this.scale_x,
+ natWidth * this.scale_x);
+ }
+
+ showLabel() {
+ if (!this._labelText)
+ return;
+
+ this.label.set_text(this._labelText);
+ this.label.opacity = 0;
+ this.label.show();
+
+ let [stageX, stageY] = this.get_transformed_position();
+
+ const itemWidth = this.allocation.get_width();
+
+ const labelWidth = this.label.get_width();
+ const xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ const x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth);
+
+ let node = this.label.get_theme_node();
+ const yOffset = node.get_length('-y-offset');
+
+ const y = stageY - this.label.height - yOffset;
+
+ this.label.set_position(x, y);
+ this.label.ease({
+ opacity: 255,
+ duration: DASH_ITEM_LABEL_SHOW_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ setLabelText(text) {
+ this._labelText = text;
+ this.child.accessible_name = text;
+ }
+
+ hideLabel() {
+ this.label.ease({
+ opacity: 0,
+ duration: DASH_ITEM_LABEL_HIDE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this.label.hide(),
+ });
+ }
+
+ setChild(actor) {
+ if (this.child == actor)
+ return;
+
+ this.destroy_all_children();
+
+ this.child = actor;
+ this.child.y_expand = true;
+ this.add_actor(this.child);
+ }
+
+ show(animate) {
+ if (this.child == null)
+ return;
+
+ let time = animate ? DASH_ANIMATION_TIME : 0;
+ this.ease({
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ duration: time,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ animateOutAndDestroy() {
+ this.label.hide();
+
+ if (this.child == null) {
+ this.destroy();
+ return;
+ }
+
+ this.animatingOut = true;
+ this.ease({
+ scale_x: 0,
+ scale_y: 0,
+ opacity: 0,
+ duration: DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this.destroy(),
+ });
+ }
+});
+
+var ShowAppsIcon = GObject.registerClass(
+class ShowAppsIcon extends DashItemContainer {
+ _init() {
+ super._init();
+
+ this.toggleButton = new St.Button({
+ style_class: 'show-apps',
+ track_hover: true,
+ can_focus: true,
+ toggle_mode: true,
+ });
+ this._iconActor = null;
+ this.icon = new IconGrid.BaseIcon(_('Show Applications'), {
+ setSizeManually: true,
+ showLabel: false,
+ createIcon: this._createIcon.bind(this),
+ });
+ this.icon.y_align = Clutter.ActorAlign.CENTER;
+
+ this.toggleButton.add_actor(this.icon);
+ this.toggleButton._delegate = this;
+
+ this.setChild(this.toggleButton);
+ this.setDragApp(null);
+ }
+
+ _createIcon(size) {
+ this._iconActor = new St.Icon({
+ icon_name: 'view-app-grid-symbolic',
+ icon_size: size,
+ style_class: 'show-apps-icon',
+ track_hover: true,
+ });
+ return this._iconActor;
+ }
+
+ _canRemoveApp(app) {
+ if (app == null)
+ return false;
+
+ if (!global.settings.is_writable('favorite-apps'))
+ return false;
+
+ let id = app.get_id();
+ let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
+ return isFavorite;
+ }
+
+ setDragApp(app) {
+ let canRemove = this._canRemoveApp(app);
+
+ this.toggleButton.set_hover(canRemove);
+ if (this._iconActor)
+ this._iconActor.set_hover(canRemove);
+
+ if (canRemove)
+ this.setLabelText(_('Unpin'));
+ else
+ this.setLabelText(_("Show Applications"));
+ }
+
+ handleDragOver(source, _actor, _x, _y, _time) {
+ if (!this._canRemoveApp(getAppFromSource(source)))
+ return DND.DragMotionResult.NO_DROP;
+
+ return DND.DragMotionResult.MOVE_DROP;
+ }
+
+ acceptDrop(source, _actor, _x, _y, _time) {
+ let app = getAppFromSource(source);
+ if (!this._canRemoveApp(app))
+ return false;
+
+ let id = app.get_id();
+
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ AppFavorites.getAppFavorites().removeFavorite(id);
+ return false;
+ });
+
+ return true;
+ }
+});
+
+var DragPlaceholderItem = GObject.registerClass(
+class DragPlaceholderItem extends DashItemContainer {
+ _init() {
+ super._init();
+ this.setChild(new St.Bin({ style_class: 'placeholder' }));
+ }
+});
+
+var EmptyDropTargetItem = GObject.registerClass(
+class EmptyDropTargetItem extends DashItemContainer {
+ _init() {
+ super._init();
+ this.setChild(new St.Bin({ style_class: 'empty-dash-drop-target' }));
+ }
+});
+
+const DashIconsLayout = GObject.registerClass(
+class DashIconsLayout extends Clutter.BoxLayout {
+ _init() {
+ super._init({
+ orientation: Clutter.Orientation.HORIZONTAL,
+ });
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ const [, natWidth] = super.vfunc_get_preferred_width(container, forHeight);
+ return [0, natWidth];
+ }
+});
+
+const baseIconSizes = [16, 22, 24, 32, 48, 64];
+
+var Dash = GObject.registerClass({
+ Signals: { 'icon-size-changed': {} },
+}, class Dash extends St.Widget {
+ _init() {
+ this._maxWidth = -1;
+ this._maxHeight = -1;
+ this.iconSize = 64;
+ this._shownInitially = false;
+
+ this._separator = null;
+ this._dragPlaceholder = null;
+ this._dragPlaceholderPos = -1;
+ this._animatingPlaceholdersCount = 0;
+ this._showLabelTimeoutId = 0;
+ this._resetHoverTimeoutId = 0;
+ this._labelShowing = false;
+
+ super._init({
+ name: 'dash',
+ offscreen_redirect: Clutter.OffscreenRedirect.ALWAYS,
+ layout_manager: new Clutter.BinLayout(),
+ });
+
+ this._dashContainer = new St.BoxLayout({
+ x_align: Clutter.ActorAlign.CENTER,
+ y_expand: true,
+ });
+
+ this._box = new St.Widget({
+ clip_to_allocation: true,
+ layout_manager: new DashIconsLayout(),
+ y_expand: true,
+ });
+ this._box._delegate = this;
+
+ this._dashContainer.add_child(this._box);
+
+ this._showAppsIcon = new ShowAppsIcon();
+ this._showAppsIcon.show(false);
+ this._showAppsIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(this._showAppsIcon);
+ this._dashContainer.add_child(this._showAppsIcon);
+
+ this.showAppsButton = this._showAppsIcon.toggleButton;
+
+ this._background = new St.Widget({
+ style_class: 'dash-background',
+ });
+
+ const sizerBox = new Clutter.Actor();
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: this._showAppsIcon.icon,
+ coordinate: Clutter.BindCoordinate.HEIGHT,
+ }));
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: this._dashContainer,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+ this._background.add_child(sizerBox);
+
+ this.add_child(this._background);
+ this.add_child(this._dashContainer);
+
+ this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this));
+
+ this._appSystem = Shell.AppSystem.get_default();
+
+ this._appSystem.connect('installed-changed', () => {
+ AppFavorites.getAppFavorites().reload();
+ this._queueRedisplay();
+ });
+ AppFavorites.getAppFavorites().connect('changed', this._queueRedisplay.bind(this));
+ this._appSystem.connect('app-state-changed', this._queueRedisplay.bind(this));
+
+ Main.overview.connect('item-drag-begin',
+ this._onItemDragBegin.bind(this));
+ Main.overview.connect('item-drag-end',
+ this._onItemDragEnd.bind(this));
+ Main.overview.connect('item-drag-cancelled',
+ this._onItemDragCancelled.bind(this));
+ Main.overview.connect('window-drag-begin',
+ this._onWindowDragBegin.bind(this));
+ Main.overview.connect('window-drag-cancelled',
+ this._onWindowDragEnd.bind(this));
+ Main.overview.connect('window-drag-end',
+ this._onWindowDragEnd.bind(this));
+
+ // Translators: this is the name of the dock/favorites area on
+ // the left of the overview
+ Main.ctrlAltTabManager.addGroup(this, _("Dash"), 'user-bookmarks-symbolic');
+ }
+
+ _onItemDragBegin() {
+ this._dragCancelled = false;
+ this._dragMonitor = {
+ dragMotion: this._onItemDragMotion.bind(this),
+ };
+ DND.addDragMonitor(this._dragMonitor);
+
+ if (this._box.get_n_children() == 0) {
+ this._emptyDropTarget = new EmptyDropTargetItem();
+ this._box.insert_child_at_index(this._emptyDropTarget, 0);
+ this._emptyDropTarget.show(true);
+ }
+ }
+
+ _onItemDragCancelled() {
+ this._dragCancelled = true;
+ this._endItemDrag();
+ }
+
+ _onItemDragEnd() {
+ if (this._dragCancelled)
+ return;
+
+ this._endItemDrag();
+ }
+
+ _endItemDrag() {
+ this._clearDragPlaceholder();
+ this._clearEmptyDropTarget();
+ this._showAppsIcon.setDragApp(null);
+ DND.removeDragMonitor(this._dragMonitor);
+ }
+
+ _onItemDragMotion(dragEvent) {
+ let app = getAppFromSource(dragEvent.source);
+ if (app == null)
+ return DND.DragMotionResult.CONTINUE;
+
+ let showAppsHovered =
+ this._showAppsIcon.contains(dragEvent.targetActor);
+
+ if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
+ this._clearDragPlaceholder();
+
+ if (showAppsHovered)
+ this._showAppsIcon.setDragApp(app);
+ else
+ this._showAppsIcon.setDragApp(null);
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ _onWindowDragBegin() {
+ this.ease({
+ opacity: 128,
+ duration: Overview.ANIMATION_TIME / 2,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ _onWindowDragEnd() {
+ this.ease({
+ opacity: 255,
+ duration: Overview.ANIMATION_TIME / 2,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+ }
+
+ _appIdListToHash(apps) {
+ let ids = {};
+ for (let i = 0; i < apps.length; i++)
+ ids[apps[i].get_id()] = apps[i];
+ return ids;
+ }
+
+ _queueRedisplay() {
+ Main.queueDeferredWork(this._workId);
+ }
+
+ _hookUpLabel(item, appIcon) {
+ item.child.connect('notify::hover', () => {
+ this._syncLabel(item, appIcon);
+ });
+
+ item.child.connect('clicked', () => {
+ this._labelShowing = false;
+ item.hideLabel();
+ });
+
+ Main.overview.connectObject('hiding', () => {
+ this._labelShowing = false;
+ item.hideLabel();
+ }, item.child);
+
+ if (appIcon) {
+ appIcon.connect('sync-tooltip', () => {
+ this._syncLabel(item, appIcon);
+ });
+ }
+ }
+
+ _createAppItem(app) {
+ let appIcon = new DashIcon(app);
+
+ appIcon.connect('menu-state-changed',
+ (o, opened) => {
+ this._itemMenuStateChanged(item, opened);
+ });
+
+ let item = new DashItemContainer();
+ item.setChild(appIcon);
+
+ // Override default AppIcon label_actor, now the
+ // accessible_name is set at DashItemContainer.setLabelText
+ appIcon.label_actor = null;
+ item.setLabelText(app.get_name());
+
+ appIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(item, appIcon);
+
+ return item;
+ }
+
+ _itemMenuStateChanged(item, opened) {
+ // When the menu closes, it calls sync_hover, which means
+ // that the notify::hover handler does everything we need to.
+ if (opened) {
+ if (this._showLabelTimeoutId > 0) {
+ GLib.source_remove(this._showLabelTimeoutId);
+ this._showLabelTimeoutId = 0;
+ }
+
+ item.hideLabel();
+ }
+ }
+
+ _syncLabel(item, appIcon) {
+ let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover();
+
+ if (shouldShow) {
+ if (this._showLabelTimeoutId == 0) {
+ let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
+ this._showLabelTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout,
+ () => {
+ this._labelShowing = true;
+ item.showLabel();
+ this._showLabelTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel');
+ if (this._resetHoverTimeoutId > 0) {
+ GLib.source_remove(this._resetHoverTimeoutId);
+ this._resetHoverTimeoutId = 0;
+ }
+ }
+ } else {
+ if (this._showLabelTimeoutId > 0)
+ GLib.source_remove(this._showLabelTimeoutId);
+ this._showLabelTimeoutId = 0;
+ item.hideLabel();
+ if (this._labelShowing) {
+ this._resetHoverTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, DASH_ITEM_HOVER_TIMEOUT,
+ () => {
+ this._labelShowing = false;
+ this._resetHoverTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
+ }
+ }
+ }
+
+ _adjustIconSize() {
+ // For the icon size, we only consider children which are "proper"
+ // icons (i.e. ignoring drag placeholders) and which are not
+ // animating out (which means they will be destroyed at the end of
+ // the animation)
+ let iconChildren = this._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.icon &&
+ !actor.animatingOut;
+ });
+
+ iconChildren.push(this._showAppsIcon);
+
+ if (this._maxWidth === -1 || this._maxHeight === -1)
+ return;
+
+ const themeNode = this.get_theme_node();
+ const maxAllocation = new Clutter.ActorBox({
+ x1: 0,
+ y1: 0,
+ x2: this._maxWidth,
+ y2: 42, /* whatever */
+ });
+ let maxContent = themeNode.get_content_box(maxAllocation);
+ let availWidth = maxContent.x2 - maxContent.x1;
+ let spacing = themeNode.get_length('spacing');
+
+ let firstButton = iconChildren[0].child;
+ let firstIcon = firstButton._delegate.icon;
+
+ // Enforce valid spacings during the size request
+ firstIcon.icon.ensure_style();
+ const [, , iconWidth, iconHeight] = firstIcon.icon.get_preferred_size();
+ const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size();
+
+ // Subtract icon padding and box spacing from the available width
+ availWidth -= iconChildren.length * (buttonWidth - iconWidth) +
+ (iconChildren.length - 1) * spacing;
+
+ let availHeight = this._maxHeight;
+ availHeight -= this.margin_top + this.margin_bottom;
+ availHeight -= this._background.get_theme_node().get_vertical_padding();
+ availHeight -= themeNode.get_vertical_padding();
+ availHeight -= buttonHeight - iconHeight;
+
+ const maxIconSize = Math.min(availWidth / iconChildren.length, availHeight);
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let iconSizes = baseIconSizes.map(s => s * scaleFactor);
+
+ let newIconSize = baseIconSizes[0];
+ for (let i = 0; i < iconSizes.length; i++) {
+ if (iconSizes[i] <= maxIconSize)
+ newIconSize = baseIconSizes[i];
+ }
+
+ if (newIconSize == this.iconSize)
+ return;
+
+ let oldIconSize = this.iconSize;
+ this.iconSize = newIconSize;
+ this.emit('icon-size-changed');
+
+ let scale = oldIconSize / newIconSize;
+ for (let i = 0; i < iconChildren.length; i++) {
+ let icon = iconChildren[i].child._delegate.icon;
+
+ // Set the new size immediately, to keep the icons' sizes
+ // in sync with this.iconSize
+ icon.setIconSize(this.iconSize);
+
+ // Don't animate the icon size change when the overview
+ // is transitioning, not visible or when initially filling
+ // the dash
+ if (!Main.overview.visible || Main.overview.animationInProgress ||
+ !this._shownInitially)
+ continue;
+
+ let [targetWidth, targetHeight] = icon.icon.get_size();
+
+ // Scale the icon's texture to the previous size and
+ // tween to the new size
+ icon.icon.set_size(icon.icon.width * scale,
+ icon.icon.height * scale);
+
+ icon.icon.ease({
+ width: targetWidth,
+ height: targetHeight,
+ duration: DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ if (this._separator) {
+ this._separator.ease({
+ height: this.iconSize,
+ duration: DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+
+ _redisplay() {
+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+ let running = this._appSystem.get_running();
+
+ let children = this._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.app;
+ });
+ // Apps currently in the dash
+ let oldApps = children.map(actor => actor.child._delegate.app);
+ // Apps supposed to be in the dash
+ let newApps = [];
+
+ for (let id in favorites)
+ newApps.push(favorites[id]);
+
+ for (let i = 0; i < running.length; i++) {
+ let app = running[i];
+ if (app.get_id() in favorites)
+ continue;
+ newApps.push(app);
+ }
+
+ // Figure out the actual changes to the list of items; we iterate
+ // over both the list of items currently in the dash and the list
+ // of items expected there, and collect additions and removals.
+ // Moves are both an addition and a removal, where the order of
+ // the operations depends on whether we encounter the position
+ // where the item has been added first or the one from where it
+ // was removed.
+ // There is an assumption that only one item is moved at a given
+ // time; when moving several items at once, everything will still
+ // end up at the right position, but there might be additional
+ // additions/removals (e.g. it might remove all the launchers
+ // and add them back in the new order even if a smaller set of
+ // additions and removals is possible).
+ // If above assumptions turns out to be a problem, we might need
+ // to use a more sophisticated algorithm, e.g. Longest Common
+ // Subsequence as used by diff.
+ let addedItems = [];
+ let removedActors = [];
+
+ let newIndex = 0;
+ let oldIndex = 0;
+ while (newIndex < newApps.length || oldIndex < oldApps.length) {
+ let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null;
+ let newApp = newApps.length > newIndex ? newApps[newIndex] : null;
+
+ // No change at oldIndex/newIndex
+ if (oldApp == newApp) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // App removed at oldIndex
+ if (oldApp && !newApps.includes(oldApp)) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // App added at newIndex
+ if (newApp && !oldApps.includes(newApp)) {
+ addedItems.push({
+ app: newApp,
+ item: this._createAppItem(newApp),
+ pos: newIndex,
+ });
+ newIndex++;
+ continue;
+ }
+
+ // App moved
+ let nextApp = newApps.length > newIndex + 1
+ ? newApps[newIndex + 1] : null;
+ let insertHere = nextApp && nextApp == oldApp;
+ let alreadyRemoved = removedActors.reduce((result, actor) => {
+ let removedApp = actor.child._delegate.app;
+ return result || removedApp == newApp;
+ }, false);
+
+ if (insertHere || alreadyRemoved) {
+ let newItem = this._createAppItem(newApp);
+ addedItems.push({
+ app: newApp,
+ item: newItem,
+ pos: newIndex + removedActors.length,
+ });
+ newIndex++;
+ } else {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ }
+ }
+
+ for (let i = 0; i < addedItems.length; i++) {
+ this._box.insert_child_at_index(addedItems[i].item,
+ addedItems[i].pos);
+ }
+
+ for (let i = 0; i < removedActors.length; i++) {
+ let item = removedActors[i];
+
+ // Don't animate item removal when the overview is transitioning
+ // or hidden
+ if (Main.overview.visible && !Main.overview.animationInProgress)
+ item.animateOutAndDestroy();
+ else
+ item.destroy();
+ }
+
+ this._adjustIconSize();
+
+ // Skip animations on first run when adding the initial set
+ // of items, to avoid all items zooming in at once
+
+ let animate = this._shownInitially && Main.overview.visible &&
+ !Main.overview.animationInProgress;
+
+ if (!this._shownInitially)
+ this._shownInitially = true;
+
+ for (let i = 0; i < addedItems.length; i++)
+ addedItems[i].item.show(animate);
+
+ // Update separator
+ const nFavorites = Object.keys(favorites).length;
+ const nIcons = children.length + addedItems.length - removedActors.length;
+ if (nFavorites > 0 && nFavorites < nIcons) {
+ if (!this._separator) {
+ this._separator = new St.Widget({
+ style_class: 'dash-separator',
+ y_align: Clutter.ActorAlign.CENTER,
+ height: this.iconSize,
+ });
+ this._box.add_child(this._separator);
+ }
+ let pos = nFavorites + this._animatingPlaceholdersCount;
+ if (this._dragPlaceholder)
+ pos++;
+ this._box.set_child_at_index(this._separator, pos);
+ } else if (this._separator) {
+ this._separator.destroy();
+ this._separator = null;
+ }
+
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
+ // Without it, StBoxLayout may use a stale size cache
+ this._box.queue_relayout();
+ }
+
+ _clearDragPlaceholder() {
+ if (this._dragPlaceholder) {
+ this._animatingPlaceholdersCount++;
+ this._dragPlaceholder.connect('destroy', () => {
+ this._animatingPlaceholdersCount--;
+ });
+ this._dragPlaceholder.animateOutAndDestroy();
+ this._dragPlaceholder = null;
+ }
+ this._dragPlaceholderPos = -1;
+ }
+
+ _clearEmptyDropTarget() {
+ if (this._emptyDropTarget) {
+ this._emptyDropTarget.animateOutAndDestroy();
+ this._emptyDropTarget = null;
+ }
+ }
+
+ handleDragOver(source, actor, x, _y, _time) {
+ let app = getAppFromSource(source);
+
+ // Don't allow favoriting of transient apps
+ if (app == null || app.is_window_backed())
+ return DND.DragMotionResult.NO_DROP;
+
+ if (!global.settings.is_writable('favorite-apps'))
+ return DND.DragMotionResult.NO_DROP;
+
+ let favorites = AppFavorites.getAppFavorites().getFavorites();
+ let numFavorites = favorites.length;
+
+ let favPos = favorites.indexOf(app);
+
+ let children = this._box.get_children();
+ let numChildren = children.length;
+ let boxWidth = this._box.width;
+
+ // Keep the placeholder out of the index calculation; assuming that
+ // the remove target has the same size as "normal" items, we don't
+ // need to do the same adjustment there.
+ if (this._dragPlaceholder) {
+ boxWidth -= this._dragPlaceholder.width;
+ numChildren--;
+ }
+
+ // Same with the separator
+ if (this._separator) {
+ boxWidth -= this._separator.width;
+ numChildren--;
+ }
+
+ let pos;
+ if (this._emptyDropTarget)
+ pos = 0; // always insert at the start when dash is empty
+ else if (this.text_direction === Clutter.TextDirection.RTL)
+ pos = numChildren - Math.floor(x * numChildren / boxWidth);
+ else
+ pos = Math.floor(x * numChildren / boxWidth);
+
+ // Put the placeholder after the last favorite if we are not
+ // in the favorites zone
+ if (pos > numFavorites)
+ pos = numFavorites;
+
+ if (pos !== this._dragPlaceholderPos && this._animatingPlaceholdersCount === 0) {
+ this._dragPlaceholderPos = pos;
+
+ // Don't allow positioning before or after self
+ if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
+ this._clearDragPlaceholder();
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ // If the placeholder already exists, we just move
+ // it, but if we are adding it, expand its size in
+ // an animation
+ let fadeIn;
+ if (this._dragPlaceholder) {
+ this._dragPlaceholder.destroy();
+ fadeIn = false;
+ } else {
+ fadeIn = true;
+ }
+
+ this._dragPlaceholder = new DragPlaceholderItem();
+ this._dragPlaceholder.child.set_width(this.iconSize);
+ this._dragPlaceholder.child.set_height(this.iconSize / 2);
+ this._box.insert_child_at_index(this._dragPlaceholder,
+ this._dragPlaceholderPos);
+ this._dragPlaceholder.show(fadeIn);
+ }
+
+ if (!this._dragPlaceholder)
+ return DND.DragMotionResult.NO_DROP;
+
+ let srcIsFavorite = favPos != -1;
+
+ if (srcIsFavorite)
+ return DND.DragMotionResult.MOVE_DROP;
+
+ return DND.DragMotionResult.COPY_DROP;
+ }
+
+ // Draggable target interface
+ acceptDrop(source, _actor, _x, _y, _time) {
+ let app = getAppFromSource(source);
+
+ // Don't allow favoriting of transient apps
+ if (app == null || app.is_window_backed())
+ return false;
+
+ if (!global.settings.is_writable('favorite-apps'))
+ return false;
+
+ let id = app.get_id();
+
+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+ let srcIsFavorite = id in favorites;
+
+ let favPos = 0;
+ let children = this._box.get_children();
+ for (let i = 0; i < this._dragPlaceholderPos; i++) {
+ if (this._dragPlaceholder &&
+ children[i] == this._dragPlaceholder)
+ continue;
+
+ let childId = children[i].child._delegate.app.get_id();
+ if (childId == id)
+ continue;
+ if (childId in favorites)
+ favPos++;
+ }
+
+ // No drag placeholder means we don't want to favorite the app
+ // and we are dragging it to its original position
+ if (!this._dragPlaceholder)
+ return true;
+
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ let appFavorites = AppFavorites.getAppFavorites();
+ if (srcIsFavorite)
+ appFavorites.moveFavoriteToPos(id, favPos);
+ else
+ appFavorites.addFavoriteAtPos(id, favPos);
+ return false;
+ });
+
+ return true;
+ }
+
+ setMaxSize(maxWidth, maxHeight) {
+ if (this._maxWidth === maxWidth &&
+ this._maxHeight === maxHeight)
+ return;
+
+ this._maxWidth = maxWidth;
+ this._maxHeight = maxHeight;
+ this._queueRedisplay();
+ }
+});
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
new file mode 100644
index 0000000..2c44f0c
--- /dev/null
+++ b/js/ui/dateMenu.js
@@ -0,0 +1,980 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported DateMenuButton */
+
+const {
+ Clutter, Gio, GLib, GnomeDesktop,
+ GObject, GWeather, Pango, Shell, St,
+} = imports.gi;
+
+const Util = imports.misc.util;
+const Main = imports.ui.main;
+const PanelMenu = imports.ui.panelMenu;
+const Calendar = imports.ui.calendar;
+const Weather = imports.misc.weather;
+const System = imports.system;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const NC_ = (context, str) => `${context}\u0004${str}`;
+const T_ = Shell.util_translate_time_string;
+
+const MAX_FORECASTS = 5;
+const EN_CHAR = '\u2013';
+
+const ClocksIntegrationIface = loadInterfaceXML('org.gnome.Shell.ClocksIntegration');
+const ClocksProxy = Gio.DBusProxy.makeProxyWrapper(ClocksIntegrationIface);
+
+function _isToday(date) {
+ let now = new Date();
+ return now.getYear() == date.getYear() &&
+ now.getMonth() == date.getMonth() &&
+ now.getDate() == date.getDate();
+}
+
+function _gDateTimeToDate(datetime) {
+ return new Date(datetime.to_unix() * 1000 + datetime.get_microsecond() / 1000);
+}
+
+var TodayButton = GObject.registerClass(
+class TodayButton extends St.Button {
+ _init(calendar) {
+ // Having the ability to go to the current date if the user is already
+ // on the current date can be confusing. So don't make the button reactive
+ // until the selected date changes.
+ super._init({
+ style_class: 'datemenu-today-button',
+ x_expand: true,
+ can_focus: true,
+ reactive: false,
+ });
+
+ let hbox = new St.BoxLayout({ vertical: true });
+ this.add_actor(hbox);
+
+ this._dayLabel = new St.Label({
+ style_class: 'day-label',
+ x_align: Clutter.ActorAlign.START,
+ });
+ hbox.add_actor(this._dayLabel);
+
+ this._dateLabel = new St.Label({ style_class: 'date-label' });
+ hbox.add_actor(this._dateLabel);
+
+ this._calendar = calendar;
+ this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
+ // Make the button reactive only if the selected date is not the
+ // current date.
+ this.reactive = !_isToday(_gDateTimeToDate(datetime));
+ });
+ }
+
+ vfunc_clicked() {
+ this._calendar.setDate(new Date(), false);
+ }
+
+ setDate(date) {
+ this._dayLabel.set_text(date.toLocaleFormat('%A'));
+
+ /* Translators: This is the date format to use when the calendar popup is
+ * shown - it is shown just below the time in the top bar (e.g.,
+ * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+ * "February 17 2015".
+ */
+ let dateFormat = Shell.util_translate_time_string(N_("%B %-d %Y"));
+ this._dateLabel.set_text(date.toLocaleFormat(dateFormat));
+
+ /* Translators: This is the accessible name of the date button shown
+ * below the time in the shell; it should combine the weekday and the
+ * date, e.g. "Tuesday February 17 2015".
+ */
+ dateFormat = Shell.util_translate_time_string(N_("%A %B %e %Y"));
+ this.accessible_name = date.toLocaleFormat(dateFormat);
+ }
+});
+
+var EventsSection = GObject.registerClass(
+class EventsSection extends St.Button {
+ _init() {
+ super._init({
+ style_class: 'events-button',
+ can_focus: true,
+ x_expand: true,
+ child: new St.BoxLayout({
+ style_class: 'events-box',
+ vertical: true,
+ x_expand: true,
+ }),
+ });
+
+ this._startDate = null;
+ this._endDate = null;
+
+ this._eventSource = null;
+ this._calendarApp = null;
+
+ this._title = new St.Label({
+ style_class: 'events-title',
+ });
+ this.child.add_child(this._title);
+
+ this._eventsList = new St.BoxLayout({
+ style_class: 'events-list',
+ vertical: true,
+ x_expand: true,
+ });
+ this.child.add_child(this._eventsList);
+
+ this._appSys = Shell.AppSystem.get_default();
+ this._appSys.connect('installed-changed',
+ this._appInstalledChanged.bind(this));
+ this._appInstalledChanged();
+ }
+
+ setDate(date) {
+ this._startDate =
+ new Date(date.getFullYear(), date.getMonth(), date.getDate());
+ this._endDate =
+ new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1);
+
+ this._updateTitle();
+ this._reloadEvents();
+ }
+
+ setEventSource(eventSource) {
+ if (!(eventSource instanceof Calendar.EventSourceBase))
+ throw new Error('Event source is not valid type');
+
+ this._eventSource = eventSource;
+ this._eventSource.connect('changed', this._reloadEvents.bind(this));
+ this._eventSource.connect('notify::has-calendars',
+ this._sync.bind(this));
+ this._sync();
+ }
+
+ _updateTitle() {
+ /* Translators: Shown on calendar heading when selected day occurs on current year */
+ const sameYearFormat = T_(NC_('calendar heading', '%B %-d'));
+
+ /* Translators: Shown on calendar heading when selected day occurs on different year */
+ const otherYearFormat = T_(NC_('calendar heading', '%B %-d %Y'));
+
+ const timeSpanDay = GLib.TIME_SPAN_DAY / 1000;
+ const now = new Date();
+
+ if (this._startDate <= now && now < this._endDate)
+ this._title.text = _('Today');
+ else if (this._endDate <= now && now - this._endDate < timeSpanDay)
+ this._title.text = _('Yesterday');
+ else if (this._startDate > now && this._startDate - now <= timeSpanDay)
+ this._title.text = _('Tomorrow');
+ else if (this._startDate.getFullYear() === now.getFullYear())
+ this._title.text = this._startDate.toLocaleFormat(sameYearFormat);
+ else
+ this._title.text = this._startDate.toLocaleFormat(otherYearFormat);
+ }
+
+ _isAtMidnight(eventTime) {
+ return eventTime.getHours() === 0 && eventTime.getMinutes() === 0 && eventTime.getSeconds() === 0;
+ }
+
+ _formatEventTime(event) {
+ const eventStart = event.date;
+ let eventEnd = event.end;
+
+ const allDay =
+ eventStart.getTime() === this._startDate.getTime() && eventEnd.getTime() === this._endDate.getTime();
+
+ const startsBeforeToday = eventStart < this._startDate;
+ const endsAfterToday = eventEnd > this._endDate;
+
+ const startTimeOnly = Util.formatTime(eventStart, { timeOnly: true });
+ const endTimeOnly = Util.formatTime(eventEnd, { timeOnly: true });
+
+ const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+
+ let title;
+ if (allDay) {
+ /* Translators: Shown in calendar event list for all day events
+ * Keep it short, best if you can use less then 10 characters
+ */
+ title = C_('event list time', 'All Day');
+ } else if (startsBeforeToday || endsAfterToday) {
+ const now = new Date();
+ const thisYear = now.getFullYear();
+
+ const startsAtMidnight = this._isAtMidnight(eventStart);
+ const endsAtMidnight = this._isAtMidnight(eventEnd);
+
+ const startYear = eventStart.getFullYear();
+
+ if (endsAtMidnight) {
+ eventEnd = new Date(eventEnd);
+ eventEnd.setDate(eventEnd.getDate() - 1);
+ }
+
+ const endYear = eventEnd.getFullYear();
+
+ let format;
+ if (startYear === thisYear && thisYear === endYear)
+ /* Translators: Shown in calendar event list as the start/end of events
+ * that only show day and month
+ */
+ format = T_(N_('%m/%d'));
+ else
+ format = '%x';
+
+ const startDateOnly = eventStart.toLocaleFormat(format);
+ const endDateOnly = eventEnd.toLocaleFormat(format);
+
+ if (startsAtMidnight && endsAtMidnight)
+ title = `${rtl ? endDateOnly : startDateOnly} ${EN_CHAR} ${rtl ? startDateOnly : endDateOnly}`;
+ else if (rtl)
+ title = `${endTimeOnly} ${endDateOnly} ${EN_CHAR} ${startTimeOnly} ${startDateOnly}`;
+ else
+ title = `${startDateOnly} ${startTimeOnly} ${EN_CHAR} ${endDateOnly} ${endTimeOnly}`;
+ } else if (eventStart === eventEnd) {
+ title = startTimeOnly;
+ } else {
+ title = `${rtl ? endTimeOnly : startTimeOnly} ${EN_CHAR} ${rtl ? startTimeOnly : endTimeOnly}`;
+ }
+
+ return title;
+ }
+
+ _reloadEvents() {
+ if (this._eventSource.isLoading || this._reloading)
+ return;
+
+ this._reloading = true;
+
+ [...this._eventsList].forEach(c => c.destroy());
+
+ const events =
+ this._eventSource.getEvents(this._startDate, this._endDate);
+
+ for (let event of events) {
+ const box = new St.BoxLayout({
+ style_class: 'event-box',
+ vertical: true,
+ });
+ box.add(new St.Label({
+ text: event.summary,
+ style_class: 'event-summary',
+ }));
+ box.add(new St.Label({
+ text: this._formatEventTime(event),
+ style_class: 'event-time',
+ }));
+ this._eventsList.add_child(box);
+ }
+
+ if (this._eventsList.get_n_children() === 0) {
+ const placeholder = new St.Label({
+ text: _('No Events'),
+ style_class: 'event-placeholder',
+ });
+ this._eventsList.add_child(placeholder);
+ }
+
+ this._reloading = false;
+ this._sync();
+ }
+
+ vfunc_clicked() {
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+
+ let appInfo = this._calendarApp;
+ if (appInfo.get_id() === 'org.gnome.Evolution.desktop') {
+ const app = this._appSys.lookup_app('evolution-calendar.desktop');
+ if (app)
+ appInfo = app.app_info;
+ }
+ appInfo.launch([], global.create_app_launch_context(0, -1));
+ }
+
+ _appInstalledChanged() {
+ const apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
+ if (apps && (apps.length > 0)) {
+ const app = Gio.AppInfo.get_default_for_type('text/calendar', false);
+ const defaultInRecommended = apps.some(a => a.equal(app));
+ this._calendarApp = defaultInRecommended ? app : apps[0];
+ } else {
+ this._calendarApp = null;
+ }
+
+ return this._sync();
+ }
+
+ _sync() {
+ this.visible = this._eventSource && this._eventSource.hasCalendars;
+ this.reactive = this._calendarApp !== null;
+ }
+});
+
+var WorldClocksSection = GObject.registerClass(
+class WorldClocksSection extends St.Button {
+ _init() {
+ super._init({
+ style_class: 'world-clocks-button',
+ can_focus: true,
+ x_expand: true,
+ });
+ this._clock = new GnomeDesktop.WallClock();
+ this._clockNotifyId = 0;
+ this._tzNotifyId = 0;
+
+ this._locations = [];
+
+ let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
+ this._grid = new St.Widget({
+ style_class: 'world-clocks-grid',
+ x_expand: true,
+ layout_manager: layout,
+ });
+ layout.hookup_style(this._grid);
+
+ this.child = this._grid;
+
+ this._clocksApp = null;
+ this._clocksProxy = new ClocksProxy(
+ Gio.DBus.session,
+ 'org.gnome.clocks',
+ '/org/gnome/clocks',
+ this._onProxyReady.bind(this),
+ null /* cancellable */,
+ Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.shell.world-clocks',
+ });
+ this._settings.connect('changed', this._clocksChanged.bind(this));
+ this._clocksChanged();
+
+ this._appSystem = Shell.AppSystem.get_default();
+ this._appSystem.connect('installed-changed',
+ this._sync.bind(this));
+ this._sync();
+ }
+
+ vfunc_clicked() {
+ if (this._clocksApp)
+ this._clocksApp.activate();
+
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+
+ _sync() {
+ this._clocksApp = this._appSystem.lookup_app('org.gnome.clocks.desktop');
+ this.visible = this._clocksApp != null;
+ }
+
+ _clocksChanged() {
+ this._grid.destroy_all_children();
+ this._locations = [];
+
+ let world = GWeather.Location.get_world();
+ let clocks = this._settings.get_value('locations').deepUnpack();
+ for (let i = 0; i < clocks.length; i++) {
+ let l = world.deserialize(clocks[i]);
+ if (l && l.get_timezone() != null)
+ this._locations.push({ location: l });
+ }
+
+ const unixtime = GLib.DateTime.new_now_local().to_unix();
+ this._locations.sort((a, b) => {
+ const tzA = a.location.get_timezone();
+ const tzB = b.location.get_timezone();
+ const intA = tzA.find_interval(GLib.TimeType.STANDARD, unixtime);
+ const intB = tzB.find_interval(GLib.TimeType.STANDARD, unixtime);
+ return tzA.get_offset(intA) - tzB.get_offset(intB);
+ });
+
+ let layout = this._grid.layout_manager;
+ let title = this._locations.length == 0
+ ? _("Add world clocks…")
+ : _("World Clocks");
+ const header = new St.Label({
+ style_class: 'world-clocks-header',
+ x_align: Clutter.ActorAlign.START,
+ text: title,
+ });
+ if (this._grid.text_direction === Clutter.TextDirection.RTL)
+ layout.attach(header, 2, 0, 1, 1);
+ else
+ layout.attach(header, 0, 0, 2, 1);
+ this.label_actor = header;
+
+ for (let i = 0; i < this._locations.length; i++) {
+ let l = this._locations[i].location;
+
+ let name = l.get_city_name() || l.get_name();
+ const label = new St.Label({
+ style_class: 'world-clocks-city',
+ text: name,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.CENTER,
+ x_expand: true,
+ });
+
+ let time = new St.Label({ style_class: 'world-clocks-time' });
+
+ const tz = new St.Label({
+ style_class: 'world-clocks-timezone',
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ time.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ tz.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+
+ if (this._grid.text_direction == Clutter.TextDirection.RTL) {
+ layout.attach(tz, 0, i + 1, 1, 1);
+ layout.attach(time, 1, i + 1, 1, 1);
+ layout.attach(label, 2, i + 1, 1, 1);
+ } else {
+ layout.attach(label, 0, i + 1, 1, 1);
+ layout.attach(time, 1, i + 1, 1, 1);
+ layout.attach(tz, 2, i + 1, 1, 1);
+ }
+
+ this._locations[i].timeLabel = time;
+ this._locations[i].tzLabel = tz;
+ }
+
+ if (this._grid.get_n_children() > 1) {
+ if (!this._clockNotifyId) {
+ this._clockNotifyId =
+ this._clock.connect('notify::clock', this._updateTimeLabels.bind(this));
+ }
+ if (!this._tzNotifyId) {
+ this._tzNotifyId =
+ this._clock.connect('notify::timezone', this._updateTimezoneLabels.bind(this));
+ }
+ this._updateTimeLabels();
+ this._updateTimezoneLabels();
+ } else {
+ if (this._clockNotifyId)
+ this._clock.disconnect(this._clockNotifyId);
+ this._clockNotifyId = 0;
+
+ if (this._tzNotifyId)
+ this._clock.disconnect(this._tzNotifyId);
+ this._tzNotifyId = 0;
+ }
+ }
+
+ _getTimezoneOffsetAtLocation(location) {
+ const tz = location.get_timezone();
+ const localOffset = GLib.DateTime.new_now_local().get_utc_offset();
+ const utcOffset = GLib.DateTime.new_now(tz).get_utc_offset();
+ const offsetCurrentTz = utcOffset - localOffset;
+ const offsetHours =
+ Math.floor(Math.abs(offsetCurrentTz) / GLib.TIME_SPAN_HOUR);
+ const offsetMinutes =
+ (Math.abs(offsetCurrentTz) % GLib.TIME_SPAN_HOUR) /
+ GLib.TIME_SPAN_MINUTE;
+
+ const prefix = offsetCurrentTz >= 0 ? '+' : '-';
+ const text = offsetMinutes === 0
+ ? `${prefix}${offsetHours}`
+ : `${prefix}${offsetHours}\u2236${offsetMinutes}`;
+ return text;
+ }
+
+ _updateTimeLabels() {
+ for (let i = 0; i < this._locations.length; i++) {
+ let l = this._locations[i];
+ const now = GLib.DateTime.new_now(l.location.get_timezone());
+ l.timeLabel.text = Util.formatTime(now, { timeOnly: true });
+ }
+ }
+
+ _updateTimezoneLabels() {
+ for (let i = 0; i < this._locations.length; i++) {
+ let l = this._locations[i];
+ l.tzLabel.text = this._getTimezoneOffsetAtLocation(l.location);
+ }
+ }
+
+ _onProxyReady(proxy, error) {
+ if (error) {
+ log(`Failed to create GNOME Clocks proxy: ${error}`);
+ return;
+ }
+
+ this._clocksProxy.connect('g-properties-changed',
+ this._onClocksPropertiesChanged.bind(this));
+ this._onClocksPropertiesChanged();
+ }
+
+ _onClocksPropertiesChanged() {
+ if (this._clocksProxy.g_name_owner == null)
+ return;
+
+ this._settings.set_value('locations',
+ new GLib.Variant('av', this._clocksProxy.Locations));
+ }
+});
+
+var WeatherSection = GObject.registerClass(
+class WeatherSection extends St.Button {
+ _init() {
+ super._init({
+ style_class: 'weather-button',
+ can_focus: true,
+ x_expand: true,
+ });
+
+ this._weatherClient = new Weather.WeatherClient();
+
+ let box = new St.BoxLayout({
+ style_class: 'weather-box',
+ vertical: true,
+ x_expand: true,
+ });
+
+ this.child = box;
+
+ let titleBox = new St.BoxLayout({ style_class: 'weather-header-box' });
+ this._titleLabel = new St.Label({
+ style_class: 'weather-header',
+ x_align: Clutter.ActorAlign.START,
+ x_expand: true,
+ y_align: Clutter.ActorAlign.END,
+ });
+ titleBox.add_child(this._titleLabel);
+ box.add_child(titleBox);
+
+ this._titleLocation = new St.Label({
+ style_class: 'weather-header location',
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.END,
+ });
+ titleBox.add_child(this._titleLocation);
+
+ let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
+ this._forecastGrid = new St.Widget({
+ style_class: 'weather-grid',
+ layout_manager: layout,
+ });
+ layout.hookup_style(this._forecastGrid);
+ box.add_child(this._forecastGrid);
+
+ this._weatherClient.connect('changed', this._sync.bind(this));
+ this._sync();
+ }
+
+ vfunc_map() {
+ this._weatherClient.update();
+ super.vfunc_map();
+ }
+
+ vfunc_clicked() {
+ this._weatherClient.activateApp();
+
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+
+ _getInfos() {
+ let forecasts = this._weatherClient.info.get_forecast_list();
+
+ let now = GLib.DateTime.new_now_local();
+ let current = GLib.DateTime.new_from_unix_local(0);
+ let infos = [];
+ for (let i = 0; i < forecasts.length; i++) {
+ const [valid, timestamp] = forecasts[i].get_value_update();
+ if (!valid || timestamp === 0)
+ continue; // 0 means 'never updated'
+
+ const datetime = GLib.DateTime.new_from_unix_local(timestamp);
+ if (now.difference(datetime) > 0)
+ continue; // Ignore earlier forecasts
+
+ if (datetime.difference(current) < GLib.TIME_SPAN_HOUR)
+ continue; // Enforce a minimum interval of 1h
+
+ if (infos.push(forecasts[i]) == MAX_FORECASTS)
+ break; // Use a maximum of five forecasts
+
+ current = datetime;
+ }
+ return infos;
+ }
+
+ _addForecasts() {
+ let layout = this._forecastGrid.layout_manager;
+
+ let infos = this._getInfos();
+ if (this._forecastGrid.text_direction == Clutter.TextDirection.RTL)
+ infos.reverse();
+
+ let col = 0;
+ infos.forEach(fc => {
+ const [valid_, timestamp] = fc.get_value_update();
+ let timeStr = Util.formatTime(new Date(timestamp * 1000), {
+ timeOnly: true,
+ ampm: false,
+ });
+ const [, tempValue] = fc.get_value_temp(GWeather.TemperatureUnit.DEFAULT);
+ const tempPrefix = Math.round(tempValue) >= 0 ? ' ' : '';
+
+ let time = new St.Label({
+ style_class: 'weather-forecast-time',
+ text: timeStr,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ let icon = new St.Icon({
+ style_class: 'weather-forecast-icon',
+ icon_name: fc.get_symbolic_icon_name(),
+ x_align: Clutter.ActorAlign.CENTER,
+ x_expand: true,
+ });
+ let temp = new St.Label({
+ style_class: 'weather-forecast-temp',
+ text: `${tempPrefix}${Math.round(tempValue)}°`,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+
+ temp.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ time.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+
+ layout.attach(time, col, 0, 1, 1);
+ layout.attach(icon, col, 1, 1, 1);
+ layout.attach(temp, col, 2, 1, 1);
+ col++;
+ });
+ }
+
+ _setStatusLabel(text) {
+ let layout = this._forecastGrid.layout_manager;
+ let label = new St.Label({ text });
+ layout.attach(label, 0, 0, 1, 1);
+ }
+
+ _findBestLocationName(loc) {
+ const locName = loc.get_name();
+
+ if (loc.get_level() === GWeather.LocationLevel.CITY ||
+ !loc.has_coords())
+ return locName;
+
+ const world = GWeather.Location.get_world();
+ const city = world.find_nearest_city(...loc.get_coords());
+ const cityName = city.get_name();
+
+ return locName.includes(cityName) ? cityName : locName;
+ }
+
+ _updateForecasts() {
+ this._forecastGrid.destroy_all_children();
+
+ if (!this._weatherClient.hasLocation)
+ return;
+
+ const { info } = this._weatherClient;
+ this._titleLocation.text = this._findBestLocationName(info.location);
+
+ if (this._weatherClient.loading) {
+ this._setStatusLabel(_("Loading…"));
+ return;
+ }
+
+ if (info.is_valid()) {
+ this._addForecasts();
+ return;
+ }
+
+ if (info.network_error())
+ this._setStatusLabel(_("Go online for weather information"));
+ else
+ this._setStatusLabel(_("Weather information is currently unavailable"));
+ }
+
+ _sync() {
+ this.visible = this._weatherClient.available;
+
+ if (!this.visible)
+ return;
+
+ if (this._weatherClient.hasLocation)
+ this._titleLabel.text = _('Weather');
+ else
+ this._titleLabel.text = _('Select weather location…');
+
+ this._forecastGrid.visible = this._weatherClient.hasLocation;
+ this._titleLocation.visible = this._weatherClient.hasLocation;
+
+ this._updateForecasts();
+ }
+});
+
+var MessagesIndicator = GObject.registerClass(
+class MessagesIndicator extends St.Icon {
+ _init() {
+ super._init({
+ icon_size: 16,
+ visible: false,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._sources = [];
+ this._count = 0;
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.notifications',
+ });
+ this._settings.connect('changed::show-banners', this._sync.bind(this));
+
+ Main.messageTray.connect('source-added', this._onSourceAdded.bind(this));
+ Main.messageTray.connect('source-removed', this._onSourceRemoved.bind(this));
+ Main.messageTray.connect('queue-changed', this._updateCount.bind(this));
+
+ let sources = Main.messageTray.getSources();
+ sources.forEach(source => this._onSourceAdded(null, source));
+
+ this._sync();
+
+ this.connect('destroy', () => {
+ this._settings.run_dispose();
+ this._settings = null;
+ });
+ }
+
+ _onSourceAdded(tray, source) {
+ source.connect('notify::count', this._updateCount.bind(this));
+ this._sources.push(source);
+ this._updateCount();
+ }
+
+ _onSourceRemoved(tray, source) {
+ this._sources.splice(this._sources.indexOf(source), 1);
+ this._updateCount();
+ }
+
+ _updateCount() {
+ let count = 0;
+ this._sources.forEach(source => (count += source.unseenCount));
+ this._count = count - Main.messageTray.queueCount;
+
+ this._sync();
+ }
+
+ _sync() {
+ let doNotDisturb = !this._settings.get_boolean('show-banners');
+ this.icon_name = doNotDisturb
+ ? 'notifications-disabled-symbolic'
+ : 'message-indicator-symbolic';
+ this.visible = doNotDisturb || this._count > 0;
+ }
+});
+
+var FreezableBinLayout = GObject.registerClass(
+class FreezableBinLayout extends Clutter.BinLayout {
+ _init() {
+ super._init();
+
+ this._frozen = false;
+ this._savedWidth = [NaN, NaN];
+ this._savedHeight = [NaN, NaN];
+ }
+
+ set frozen(v) {
+ if (this._frozen == v)
+ return;
+
+ this._frozen = v;
+ if (!this._frozen)
+ this.layout_changed();
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ if (!this._frozen || this._savedWidth.some(isNaN))
+ return super.vfunc_get_preferred_width(container, forHeight);
+ return this._savedWidth;
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ if (!this._frozen || this._savedHeight.some(isNaN))
+ return super.vfunc_get_preferred_height(container, forWidth);
+ return this._savedHeight;
+ }
+
+ vfunc_allocate(container, allocation) {
+ super.vfunc_allocate(container, allocation);
+
+ let [width, height] = allocation.get_size();
+ this._savedWidth = [width, width];
+ this._savedHeight = [height, height];
+ }
+});
+
+var CalendarColumnLayout = GObject.registerClass(
+class CalendarColumnLayout extends Clutter.BoxLayout {
+ _init(actors) {
+ super._init({ orientation: Clutter.Orientation.VERTICAL });
+ this._colActors = actors;
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ const actors =
+ this._colActors.filter(a => a.get_parent() === container);
+ if (actors.length === 0)
+ return super.vfunc_get_preferred_width(container, forHeight);
+ return actors.reduce(([minAcc, natAcc], child) => {
+ const [min, nat] = child.get_preferred_width(forHeight);
+ return [Math.max(minAcc, min), Math.max(natAcc, nat)];
+ }, [0, 0]);
+ }
+});
+
+var DateMenuButton = GObject.registerClass(
+class DateMenuButton extends PanelMenu.Button {
+ _init() {
+ let hbox;
+
+ super._init(0.5);
+
+ this._clockDisplay = new St.Label({ style_class: 'clock' });
+ this._clockDisplay.clutter_text.y_align = Clutter.ActorAlign.CENTER;
+ this._clockDisplay.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+
+ this._indicator = new MessagesIndicator();
+
+ const indicatorPad = new St.Widget();
+ this._indicator.bind_property('visible',
+ indicatorPad, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ indicatorPad.add_constraint(new Clutter.BindConstraint({
+ source: this._indicator,
+ coordinate: Clutter.BindCoordinate.SIZE,
+ }));
+
+ let box = new St.BoxLayout({ style_class: 'clock-display-box' });
+ box.add_actor(indicatorPad);
+ box.add_actor(this._clockDisplay);
+ box.add_actor(this._indicator);
+
+ this.label_actor = this._clockDisplay;
+ this.add_actor(box);
+ this.add_style_class_name('clock-display');
+
+ let layout = new FreezableBinLayout();
+ let bin = new St.Widget({ layout_manager: layout });
+ // For some minimal compatibility with PopupMenuItem
+ bin._delegate = this;
+ this.menu.box.add_child(bin);
+
+ hbox = new St.BoxLayout({ name: 'calendarArea' });
+ bin.add_actor(hbox);
+
+ this._calendar = new Calendar.Calendar();
+ this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
+ let date = _gDateTimeToDate(datetime);
+ layout.frozen = !_isToday(date);
+ this._eventsItem.setDate(date);
+ });
+ this._date = new TodayButton(this._calendar);
+
+ this.menu.connect('open-state-changed', (menu, isOpen) => {
+ // Whenever the menu is opened, select today
+ if (isOpen) {
+ let now = new Date();
+ this._calendar.setDate(now);
+ this._date.setDate(now);
+ this._eventsItem.setDate(now);
+ }
+ });
+
+ // Fill up the first column
+ this._messageList = new Calendar.CalendarMessageList();
+ hbox.add_child(this._messageList);
+
+ // Fill up the second column
+ const boxLayout = new CalendarColumnLayout([this._calendar, this._date]);
+ const vbox = new St.Widget({
+ style_class: 'datemenu-calendar-column',
+ layout_manager: boxLayout,
+ });
+ boxLayout.hookup_style(vbox);
+ hbox.add(vbox);
+
+ vbox.add_actor(this._date);
+ vbox.add_actor(this._calendar);
+
+ this._displaysSection = new St.ScrollView({
+ style_class: 'datemenu-displays-section vfade',
+ x_expand: true,
+ overlay_scrollbars: true,
+ });
+ this._displaysSection.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL);
+ vbox.add_actor(this._displaysSection);
+
+ const displaysBox = new St.BoxLayout({
+ vertical: true,
+ x_expand: true,
+ style_class: 'datemenu-displays-box',
+ });
+ this._displaysSection.add_actor(displaysBox);
+
+ this._eventsItem = new EventsSection();
+ displaysBox.add_child(this._eventsItem);
+
+ this._clocksItem = new WorldClocksSection();
+ displaysBox.add_child(this._clocksItem);
+
+ this._weatherItem = new WeatherSection();
+ displaysBox.add_child(this._weatherItem);
+
+ // Done with hbox for calendar and event list
+
+ this._clock = new GnomeDesktop.WallClock();
+ this._clock.bind_property('clock', this._clockDisplay, 'text', GObject.BindingFlags.SYNC_CREATE);
+ this._clock.connect('notify::timezone', this._updateTimeZone.bind(this));
+
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ this._sessionUpdated();
+ }
+
+ _getEventSource() {
+ return new Calendar.DBusEventSource();
+ }
+
+ _setEventSource(eventSource) {
+ if (this._eventSource)
+ this._eventSource.destroy();
+
+ this._calendar.setEventSource(eventSource);
+ this._eventsItem.setEventSource(eventSource);
+
+ this._eventSource = eventSource;
+ }
+
+ _updateTimeZone() {
+ // SpiderMonkey caches the time zone so we must explicitly clear it
+ // before we can update the calendar, see
+ // https://bugzilla.gnome.org/show_bug.cgi?id=678507
+ System.clearDateCaches();
+
+ this._calendar.updateTimeZone();
+ }
+
+ _sessionUpdated() {
+ let eventSource;
+ let showEvents = Main.sessionMode.showCalendarEvents;
+ if (showEvents)
+ eventSource = this._getEventSource();
+ else
+ eventSource = new Calendar.EmptyEventSource();
+
+ this._setEventSource(eventSource);
+
+ // Displays are not actually expected to launch Settings when activated
+ // but the corresponding app (clocks, weather); however we can consider
+ // that display-specific settings, so re-use "allowSettings" here ...
+ this._displaysSection.visible = Main.sessionMode.allowSettings;
+ }
+});
diff --git a/js/ui/dialog.js b/js/ui/dialog.js
new file mode 100644
index 0000000..414a3e4
--- /dev/null
+++ b/js/ui/dialog.js
@@ -0,0 +1,359 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Dialog, MessageDialogContent, ListSection, ListSectionItem */
+
+const { Clutter, GLib, GObject, Meta, Pango, St } = imports.gi;
+
+function _setLabel(label, value) {
+ label.set({
+ text: value || '',
+ visible: value !== null,
+ });
+}
+
+var Dialog = GObject.registerClass(
+class Dialog extends St.Widget {
+ _init(parentActor, styleClass) {
+ super._init({
+ layout_manager: new Clutter.BinLayout(),
+ reactive: true,
+ });
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._initialKeyFocus = null;
+ this._pressedKey = null;
+ this._buttonKeys = {};
+ this._createDialog();
+ this.add_child(this._dialog);
+
+ if (styleClass != null)
+ this._dialog.add_style_class_name(styleClass);
+
+ this._parentActor = parentActor;
+ this._parentActor.add_child(this);
+ }
+
+ _createDialog() {
+ this._dialog = new St.BoxLayout({
+ style_class: 'modal-dialog',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ vertical: true,
+ });
+
+ // modal dialogs are fixed width and grow vertically; set the request
+ // mode accordingly so wrapped labels are handled correctly during
+ // size requests.
+ this._dialog.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
+ this._dialog.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
+
+ this.contentLayout = new St.BoxLayout({
+ vertical: true,
+ style_class: 'modal-dialog-content-box',
+ y_expand: true,
+ });
+ this._dialog.add_child(this.contentLayout);
+
+ this.buttonLayout = new St.Widget({
+ layout_manager: new Clutter.BoxLayout({ homogeneous: true }),
+ });
+ this._dialog.add_child(this.buttonLayout);
+ }
+
+ makeInactive() {
+ this.buttonLayout.get_children().forEach(c => c.set_reactive(false));
+ }
+
+ _onDestroy() {
+ this.makeInactive();
+ }
+
+ vfunc_event(event) {
+ if (event.type() == Clutter.EventType.KEY_PRESS) {
+ this._pressedKey = event.get_key_symbol();
+ } else if (event.type() == Clutter.EventType.KEY_RELEASE) {
+ let pressedKey = this._pressedKey;
+ this._pressedKey = null;
+
+ let symbol = event.get_key_symbol();
+ if (symbol != pressedKey)
+ return Clutter.EVENT_PROPAGATE;
+
+ let buttonInfo = this._buttonKeys[symbol];
+ if (!buttonInfo)
+ return Clutter.EVENT_PROPAGATE;
+
+ let { button, action } = buttonInfo;
+
+ if (action && button.reactive) {
+ action();
+ return Clutter.EVENT_STOP;
+ }
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _setInitialKeyFocus(actor) {
+ this._initialKeyFocus?.disconnectObject(this);
+
+ this._initialKeyFocus = actor;
+
+ actor.connectObject('destroy',
+ () => (this._initialKeyFocus = null), this);
+ }
+
+ get initialKeyFocus() {
+ return this._initialKeyFocus || this;
+ }
+
+ addButton(buttonInfo) {
+ let { label, action, key } = buttonInfo;
+ let isDefault = buttonInfo['default'];
+ let keys;
+
+ if (key)
+ keys = [key];
+ else if (isDefault)
+ keys = [Clutter.KEY_Return, Clutter.KEY_KP_Enter, Clutter.KEY_ISO_Enter];
+ else
+ keys = [];
+
+ let button = new St.Button({
+ style_class: 'modal-dialog-linked-button',
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ reactive: true,
+ can_focus: true,
+ x_expand: true,
+ y_expand: true,
+ label,
+ });
+ button.connect('clicked', () => action());
+
+ buttonInfo['button'] = button;
+
+ if (isDefault)
+ button.add_style_pseudo_class('default');
+
+ if (this._initialKeyFocus == null || isDefault)
+ this._setInitialKeyFocus(button);
+
+ for (let i in keys)
+ this._buttonKeys[keys[i]] = buttonInfo;
+
+ this.buttonLayout.add_actor(button);
+
+ return button;
+ }
+
+ clearButtons() {
+ this.buttonLayout.destroy_all_children();
+ this._buttonKeys = {};
+ }
+});
+
+var MessageDialogContent = GObject.registerClass({
+ Properties: {
+ 'title': GObject.ParamSpec.string(
+ 'title', 'title', 'title',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT,
+ null),
+ 'description': GObject.ParamSpec.string(
+ 'description', 'description', 'description',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT,
+ null),
+ },
+}, class MessageDialogContent extends St.BoxLayout {
+ _init(params) {
+ this._title = new St.Label({ style_class: 'message-dialog-title' });
+ this._description = new St.Label({ style_class: 'message-dialog-description' });
+
+ this._description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._description.clutter_text.line_wrap = true;
+
+ let defaultParams = {
+ style_class: 'message-dialog-content',
+ x_expand: true,
+ vertical: true,
+ };
+ super._init(Object.assign(defaultParams, params));
+
+ this.connect('notify::size', this._updateTitleStyle.bind(this));
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this.add_child(this._title);
+ this.add_child(this._description);
+ }
+
+ _onDestroy() {
+ if (this._updateTitleStyleLater) {
+ Meta.later_remove(this._updateTitleStyleLater);
+ delete this._updateTitleStyleLater;
+ }
+ }
+
+ get title() {
+ return this._title.text;
+ }
+
+ get description() {
+ return this._description.text;
+ }
+
+ _updateTitleStyle() {
+ if (!this._title.mapped)
+ return;
+
+ this._title.ensure_style();
+ const [, titleNatWidth] = this._title.get_preferred_width(-1);
+
+ if (titleNatWidth > this.width) {
+ if (this._updateTitleStyleLater)
+ return;
+
+ this._updateTitleStyleLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._updateTitleStyleLater = 0;
+ this._title.add_style_class_name('lightweight');
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+
+ set title(title) {
+ if (this._title.text === title)
+ return;
+
+ _setLabel(this._title, title);
+
+ this._title.remove_style_class_name('lightweight');
+ this._updateTitleStyle();
+
+ this.notify('title');
+ }
+
+ set description(description) {
+ if (this._description.text === description)
+ return;
+
+ _setLabel(this._description, description);
+ this.notify('description');
+ }
+});
+
+var ListSection = GObject.registerClass({
+ Properties: {
+ 'title': GObject.ParamSpec.string(
+ 'title', 'title', 'title',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT,
+ null),
+ },
+}, class ListSection extends St.BoxLayout {
+ _init(params) {
+ this._title = new St.Label({ style_class: 'dialog-list-title' });
+
+ this._listScrollView = new St.ScrollView({
+ style_class: 'dialog-list-scrollview',
+ hscrollbar_policy: St.PolicyType.NEVER,
+ });
+
+ this.list = new St.BoxLayout({
+ style_class: 'dialog-list-box',
+ vertical: true,
+ });
+ this._listScrollView.add_actor(this.list);
+
+ let defaultParams = {
+ style_class: 'dialog-list',
+ x_expand: true,
+ vertical: true,
+ };
+ super._init(Object.assign(defaultParams, params));
+
+ this.label_actor = this._title;
+ this.add_child(this._title);
+ this.add_child(this._listScrollView);
+ }
+
+ get title() {
+ return this._title.text;
+ }
+
+ set title(title) {
+ _setLabel(this._title, title);
+ this.notify('title');
+ }
+});
+
+var ListSectionItem = GObject.registerClass({
+ Properties: {
+ 'icon-actor': GObject.ParamSpec.object(
+ 'icon-actor', 'icon-actor', 'Icon actor',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Actor.$gtype),
+ 'title': GObject.ParamSpec.string(
+ 'title', 'title', 'title',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT,
+ null),
+ 'description': GObject.ParamSpec.string(
+ 'description', 'description', 'description',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT,
+ null),
+ },
+}, class ListSectionItem extends St.BoxLayout {
+ _init(params) {
+ this._iconActorBin = new St.Bin();
+
+ let textLayout = new St.BoxLayout({
+ vertical: true,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._title = new St.Label({ style_class: 'dialog-list-item-title' });
+
+ this._description = new St.Label({
+ style_class: 'dialog-list-item-title-description',
+ });
+
+ textLayout.add_child(this._title);
+ textLayout.add_child(this._description);
+
+ let defaultParams = { style_class: 'dialog-list-item' };
+ super._init(Object.assign(defaultParams, params));
+
+ this.label_actor = this._title;
+ this.add_child(this._iconActorBin);
+ this.add_child(textLayout);
+ }
+
+ get iconActor() {
+ return this._iconActorBin.get_child();
+ }
+
+ set iconActor(actor) {
+ this._iconActorBin.set_child(actor);
+ this.notify('icon-actor');
+ }
+
+ get title() {
+ return this._title.text;
+ }
+
+ set title(title) {
+ _setLabel(this._title, title);
+ this.notify('title');
+ }
+
+ get description() {
+ return this._description.text;
+ }
+
+ set description(description) {
+ _setLabel(this._description, description);
+ this.notify('description');
+ }
+});
diff --git a/js/ui/dnd.js b/js/ui/dnd.js
new file mode 100644
index 0000000..613402d
--- /dev/null
+++ b/js/ui/dnd.js
@@ -0,0 +1,841 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported addDragMonitor, removeDragMonitor, makeDraggable */
+
+const { Clutter, GLib, Meta, Shell, St } = imports.gi;
+const Signals = imports.misc.signals;
+
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+// Time to scale down to maxDragActorSize
+var SCALE_ANIMATION_TIME = 250;
+// Time to animate to original position on cancel
+var SNAP_BACK_ANIMATION_TIME = 250;
+// Time to animate to original position on success
+var REVERT_ANIMATION_TIME = 750;
+
+var DragMotionResult = {
+ NO_DROP: 0,
+ COPY_DROP: 1,
+ MOVE_DROP: 2,
+ CONTINUE: 3,
+};
+
+var DragState = {
+ INIT: 0,
+ DRAGGING: 1,
+ CANCELLED: 2,
+};
+
+var DRAG_CURSOR_MAP = {
+ 0: Meta.Cursor.DND_UNSUPPORTED_TARGET,
+ 1: Meta.Cursor.DND_COPY,
+ 2: Meta.Cursor.DND_MOVE,
+};
+
+var DragDropResult = {
+ FAILURE: 0,
+ SUCCESS: 1,
+ CONTINUE: 2,
+};
+var dragMonitors = [];
+
+let eventHandlerActor = null;
+let currentDraggable = null;
+
+function _getEventHandlerActor() {
+ if (!eventHandlerActor) {
+ eventHandlerActor = new Clutter.Actor({ width: 0, height: 0, reactive: true });
+ Main.uiGroup.add_actor(eventHandlerActor);
+ // We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
+ // when you've grabbed the pointer.
+ eventHandlerActor.connect('event', (actor, event) => {
+ return currentDraggable._onEvent(actor, event);
+ });
+ }
+ return eventHandlerActor;
+}
+
+function _getRealActorScale(actor) {
+ let scale = 1.0;
+ while (actor) {
+ scale *= actor.scale_x;
+ actor = actor.get_parent();
+ }
+ return scale;
+}
+
+function addDragMonitor(monitor) {
+ dragMonitors.push(monitor);
+}
+
+function removeDragMonitor(monitor) {
+ for (let i = 0; i < dragMonitors.length; i++) {
+ if (dragMonitors[i] == monitor) {
+ dragMonitors.splice(i, 1);
+ return;
+ }
+ }
+}
+
+var _Draggable = class _Draggable extends Signals.EventEmitter {
+ constructor(actor, params) {
+ super();
+
+ params = Params.parse(params, {
+ manualMode: false,
+ timeoutThreshold: 0,
+ restoreOnSuccess: false,
+ dragActorMaxSize: undefined,
+ dragActorOpacity: undefined,
+ });
+
+ this.actor = actor;
+ this._dragState = DragState.INIT;
+
+ if (!params.manualMode) {
+ this.actor.connect('button-press-event',
+ this._onButtonPress.bind(this));
+ this.actor.connect('touch-event',
+ this._onTouchEvent.bind(this));
+ }
+
+ this.actor.connect('destroy', () => {
+ this._actorDestroyed = true;
+
+ if (this._dragState == DragState.DRAGGING && this._dragCancellable)
+ this._cancelDrag(global.get_current_time());
+ this.disconnectAll();
+ });
+ this._onEventId = null;
+ this._touchSequence = null;
+
+ this._restoreOnSuccess = params.restoreOnSuccess;
+ this._dragActorMaxSize = params.dragActorMaxSize;
+ this._dragActorOpacity = params.dragActorOpacity;
+ this._dragTimeoutThreshold = params.timeoutThreshold;
+
+ this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
+ this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
+ this._dragCancellable = true;
+ }
+
+ _onButtonPress(actor, event) {
+ if (event.get_button() != 1)
+ return Clutter.EVENT_PROPAGATE;
+
+ this._buttonDown = true;
+ this._grabActor(event.get_device());
+
+ let [stageX, stageY] = event.get_coords();
+ this._dragStartX = stageX;
+ this._dragStartY = stageY;
+ this._dragStartTime = event.get_time();
+ this._dragThresholdIgnored = false;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onTouchEvent(actor, event) {
+ // We only handle touch events here on wayland. On X11
+ // we do get emulated pointer events, which already works
+ // for single-touch cases. Besides, the X11 passive touch grab
+ // set up by Mutter will make us see first the touch events
+ // and later the pointer events, so it will look like two
+ // unrelated series of events, we want to avoid double handling
+ // in these cases.
+ if (!Meta.is_wayland_compositor())
+ return Clutter.EVENT_PROPAGATE;
+
+ if (event.type() != Clutter.EventType.TOUCH_BEGIN ||
+ !global.display.is_pointer_emulating_sequence(event.get_event_sequence()))
+ return Clutter.EVENT_PROPAGATE;
+
+ this._buttonDown = true;
+ this._grabActor(event.get_device(), event.get_event_sequence());
+ this._dragStartTime = event.get_time();
+ this._dragThresholdIgnored = false;
+
+ let [stageX, stageY] = event.get_coords();
+ this._dragStartX = stageX;
+ this._dragStartY = stageY;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _grabDevice(actor, pointer, touchSequence) {
+ this._grab = global.stage.grab(actor);
+ this._grabbedDevice = pointer;
+ this._touchSequence = touchSequence;
+ }
+
+ _ungrabDevice() {
+ if (this._grab) {
+ this._grab.dismiss();
+ this._grab = null;
+ }
+ this._touchSequence = null;
+ this._grabbedDevice = null;
+ }
+
+ _grabActor(device, touchSequence) {
+ this._grabDevice(this.actor, device, touchSequence);
+ this._onEventId = this.actor.connect('event',
+ this._onEvent.bind(this));
+ }
+
+ _ungrabActor() {
+ if (!this._onEventId)
+ return;
+
+ this._ungrabDevice();
+ this.actor.disconnect(this._onEventId);
+ this._onEventId = null;
+ }
+
+ _grabEvents(device, touchSequence) {
+ if (!this._eventsGrab) {
+ let grab = Main.pushModal(_getEventHandlerActor());
+ if ((grab.get_seat_state() & Clutter.GrabState.POINTER) !== 0) {
+ this._grabDevice(_getEventHandlerActor(), device, touchSequence);
+ this._eventsGrab = grab;
+ } else {
+ Main.popModal(grab);
+ }
+ }
+ }
+
+ _ungrabEvents() {
+ if (this._eventsGrab) {
+ this._ungrabDevice();
+ Main.popModal(this._eventsGrab);
+ this._eventsGrab = null;
+ }
+ }
+
+ _eventIsRelease(event) {
+ if (event.type() == Clutter.EventType.BUTTON_RELEASE) {
+ let buttonMask = Clutter.ModifierType.BUTTON1_MASK |
+ Clutter.ModifierType.BUTTON2_MASK |
+ Clutter.ModifierType.BUTTON3_MASK;
+ /* We only obey the last button release from the device,
+ * other buttons may get pressed/released during the DnD op.
+ */
+ return (event.get_state() & buttonMask) == 0;
+ } else if (event.type() == Clutter.EventType.TOUCH_END) {
+ /* For touch, we only obey the pointer emulating sequence */
+ return global.display.is_pointer_emulating_sequence(event.get_event_sequence());
+ }
+
+ return false;
+ }
+
+ _onEvent(actor, event) {
+ let device = event.get_device();
+
+ if (this._grabbedDevice &&
+ device != this._grabbedDevice &&
+ device.get_device_type() != Clutter.InputDeviceType.KEYBOARD_DEVICE)
+ return Clutter.EVENT_PROPAGATE;
+
+ // We intercept BUTTON_RELEASE event to know that the button was released in case we
+ // didn't start the drag, to drop the draggable in case the drag was in progress, and
+ // to complete the drag and ensure that whatever happens to be under the pointer does
+ // not get triggered if the drag was cancelled with Esc.
+ if (this._eventIsRelease(event)) {
+ this._buttonDown = false;
+ if (this._dragState == DragState.DRAGGING) {
+ return this._dragActorDropped(event);
+ } else if ((this._dragActor != null || this._dragState == DragState.CANCELLED) &&
+ !this._animationInProgress) {
+ // Drag must have been cancelled with Esc.
+ this._dragComplete();
+ return Clutter.EVENT_STOP;
+ } else {
+ // Drag has never started.
+ this._ungrabActor();
+ return Clutter.EVENT_PROPAGATE;
+ }
+ // We intercept MOTION event to figure out if the drag has started and to draw
+ // this._dragActor under the pointer when dragging is in progress
+ } else if (event.type() == Clutter.EventType.MOTION ||
+ (event.type() == Clutter.EventType.TOUCH_UPDATE &&
+ global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) {
+ if (this._dragActor && this._dragState == DragState.DRAGGING)
+ return this._updateDragPosition(event);
+ else if (this._dragActor == null && this._dragState != DragState.CANCELLED)
+ return this._maybeStartDrag(event);
+
+ // We intercept KEY_PRESS event so that we can process Esc key press to cancel
+ // dragging and ignore all other key presses.
+ } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragState == DragState.DRAGGING) {
+ let symbol = event.get_key_symbol();
+ if (symbol == Clutter.KEY_Escape) {
+ this._cancelDrag(event.get_time());
+ return Clutter.EVENT_STOP;
+ }
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ /**
+ * fakeRelease:
+ *
+ * Fake a release event.
+ * Must be called if you want to intercept release events on draggable
+ * actors for other purposes (for example if you're using
+ * PopupMenu.ignoreRelease())
+ */
+ fakeRelease() {
+ this._buttonDown = false;
+ this._ungrabActor();
+ }
+
+ /**
+ * startDrag:
+ * @param {number} stageX: X coordinate of event
+ * @param {number} stageY: Y coordinate of event
+ * @param {number} time: Event timestamp
+ * @param {Clutter.EventSequence=} sequence: Event sequence
+ * @param {Clutter.InputDevice=} device: device that originated the event
+ *
+ * Directly initiate a drag and drop operation from the given actor.
+ * This function is useful to call if you've specified manualMode
+ * for the draggable.
+ */
+ startDrag(stageX, stageY, time, sequence, device) {
+ if (currentDraggable)
+ return;
+
+ if (device == undefined) {
+ let event = Clutter.get_current_event();
+
+ if (event)
+ device = event.get_device();
+
+ if (device == undefined) {
+ let seat = Clutter.get_default_backend().get_default_seat();
+ device = seat.get_pointer();
+ }
+ }
+
+ currentDraggable = this;
+ this._dragState = DragState.DRAGGING;
+
+ // Special-case St.Button: the pointer grab messes with the internal
+ // state, so force a reset to a reasonable state here
+ if (this.actor instanceof St.Button) {
+ this.actor.fake_release();
+ this.actor.hover = false;
+ }
+
+ this.emit('drag-begin', time);
+ if (this._onEventId)
+ this._ungrabActor();
+
+ this._grabEvents(device, sequence);
+ global.display.set_cursor(Meta.Cursor.DND_IN_DRAG);
+
+ this._dragX = this._dragStartX = stageX;
+ this._dragY = this._dragStartY = stageY;
+
+ let scaledWidth, scaledHeight;
+
+ if (this.actor._delegate && this.actor._delegate.getDragActor) {
+ this._dragActor = this.actor._delegate.getDragActor();
+ Main.uiGroup.add_child(this._dragActor);
+ Main.uiGroup.set_child_above_sibling(this._dragActor, null);
+ Shell.util_set_hidden_from_pick(this._dragActor, true);
+
+ // Drag actor does not always have to be the same as actor. For example drag actor
+ // can be an image that's part of the actor. So to perform "snap back" correctly we need
+ // to know what was the drag actor source.
+ if (this.actor._delegate.getDragActorSource) {
+ this._dragActorSource = this.actor._delegate.getDragActorSource();
+ // If the user dragged from the source, then position
+ // the dragActor over it. Otherwise, center it
+ // around the pointer
+ let [sourceX, sourceY] = this._dragActorSource.get_transformed_position();
+ let x, y;
+ if (stageX > sourceX && stageX <= sourceX + this._dragActor.width &&
+ stageY > sourceY && stageY <= sourceY + this._dragActor.height) {
+ x = sourceX;
+ y = sourceY;
+ } else {
+ x = stageX - this._dragActor.width / 2;
+ y = stageY - this._dragActor.height / 2;
+ }
+ this._dragActor.set_position(x, y);
+
+ this._dragActorSourceDestroyId = this._dragActorSource.connect('destroy', () => {
+ this._dragActorSource = null;
+ });
+ } else {
+ this._dragActorSource = this.actor;
+ }
+ this._dragOrigParent = undefined;
+
+ this._dragOffsetX = this._dragActor.x - this._dragStartX;
+ this._dragOffsetY = this._dragActor.y - this._dragStartY;
+
+ [scaledWidth, scaledHeight] = this._dragActor.get_transformed_size();
+ } else {
+ this._dragActor = this.actor;
+
+ this._dragActorSource = undefined;
+ this._dragOrigParent = this.actor.get_parent();
+ this._dragActorHadFixedPos = this._dragActor.fixed_position_set;
+ this._dragOrigX = this._dragActor.allocation.x1;
+ this._dragOrigY = this._dragActor.allocation.y1;
+ this._dragActorHadNatWidth = this._dragActor.natural_width_set;
+ this._dragActorHadNatHeight = this._dragActor.natural_height_set;
+ this._dragOrigWidth = this._dragActor.allocation.get_width();
+ this._dragOrigHeight = this._dragActor.allocation.get_height();
+ this._dragOrigScale = this._dragActor.scale_x;
+
+ // Ensure actors with an allocation smaller than their natural size
+ // retain their size
+ this._dragActor.set_size(...this._dragActor.allocation.get_size());
+
+ const transformedExtents = this._dragActor.get_transformed_extents();
+
+ this._dragOffsetX = transformedExtents.origin.x - this._dragStartX;
+ this._dragOffsetY = transformedExtents.origin.y - this._dragStartY;
+
+ scaledWidth = transformedExtents.get_width();
+ scaledHeight = transformedExtents.get_height();
+
+ this._dragActor.scale_x = scaledWidth / this._dragOrigWidth;
+ this._dragActor.scale_y = scaledHeight / this._dragOrigHeight;
+
+ this._dragOrigParent.remove_actor(this._dragActor);
+ Main.uiGroup.add_child(this._dragActor);
+ Main.uiGroup.set_child_above_sibling(this._dragActor, null);
+ Shell.util_set_hidden_from_pick(this._dragActor, true);
+
+ this._dragOrigParentDestroyId = this._dragOrigParent.connect('destroy', () => {
+ this._dragOrigParent = null;
+ });
+ }
+
+ this._dragActorDestroyId = this._dragActor.connect('destroy', () => {
+ // Cancel ongoing animation (if any)
+ this._finishAnimation();
+
+ this._dragActor = null;
+ if (this._dragState == DragState.DRAGGING)
+ this._dragState = DragState.CANCELLED;
+ });
+ this._dragOrigOpacity = this._dragActor.opacity;
+ if (this._dragActorOpacity != undefined)
+ this._dragActor.opacity = this._dragActorOpacity;
+
+ this._snapBackX = this._dragStartX + this._dragOffsetX;
+ this._snapBackY = this._dragStartY + this._dragOffsetY;
+ this._snapBackScale = this._dragActor.scale_x;
+
+ let origDragOffsetX = this._dragOffsetX;
+ let origDragOffsetY = this._dragOffsetY;
+ let [transX, transY] = this._dragActor.get_translation();
+ this._dragOffsetX -= transX;
+ this._dragOffsetY -= transY;
+
+ this._dragActor.set_position(
+ this._dragX + this._dragOffsetX,
+ this._dragY + this._dragOffsetY);
+
+ if (this._dragActorMaxSize != undefined) {
+ let currentSize = Math.max(scaledWidth, scaledHeight);
+ if (currentSize > this._dragActorMaxSize) {
+ let scale = this._dragActorMaxSize / currentSize;
+ let origScale = this._dragActor.scale_x;
+
+ // The position of the actor changes as we scale
+ // around the drag position, but we can't just tween
+ // to the final position because that tween would
+ // fight with updates as the user continues dragging
+ // the mouse; instead we do the position computations in
+ // a ::new-frame handler.
+ this._dragActor.ease({
+ scale_x: scale * origScale,
+ scale_y: scale * origScale,
+ duration: SCALE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._updateActorPosition(origScale,
+ origDragOffsetX, origDragOffsetY, transX, transY);
+ },
+ });
+
+ this._dragActor.get_transition('scale-x').connect('new-frame', () => {
+ this._updateActorPosition(origScale,
+ origDragOffsetX, origDragOffsetY, transX, transY);
+ });
+ }
+ }
+ }
+
+ _updateActorPosition(origScale, origDragOffsetX, origDragOffsetY, transX, transY) {
+ const currentScale = this._dragActor.scale_x / origScale;
+ this._dragOffsetX = currentScale * origDragOffsetX - transX;
+ this._dragOffsetY = currentScale * origDragOffsetY - transY;
+ this._dragActor.set_position(
+ this._dragX + this._dragOffsetX,
+ this._dragY + this._dragOffsetY);
+ }
+
+ _maybeStartDrag(event) {
+ let [stageX, stageY] = event.get_coords();
+
+ if (this._dragThresholdIgnored)
+ return Clutter.EVENT_PROPAGATE;
+
+ // See if the user has moved the mouse enough to trigger a drag
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let threshold = St.Settings.get().drag_threshold * scaleFactor;
+ if (!currentDraggable &&
+ (Math.abs(stageX - this._dragStartX) > threshold ||
+ Math.abs(stageY - this._dragStartY) > threshold)) {
+ const deviceType = event.get_source_device().get_device_type();
+ const isPointerOrTouchpad =
+ deviceType === Clutter.InputDeviceType.POINTER_DEVICE ||
+ deviceType === Clutter.InputDeviceType.TOUCHPAD_DEVICE;
+ const ellapsedTime = event.get_time() - this._dragStartTime;
+
+ // Pointer devices (e.g. mouse) start the drag immediately
+ if (isPointerOrTouchpad || ellapsedTime > this._dragTimeoutThreshold) {
+ this.startDrag(stageX, stageY, event.get_time(), this._touchSequence, event.get_device());
+ this._updateDragPosition(event);
+ } else {
+ this._dragThresholdIgnored = true;
+ this._ungrabActor();
+ return Clutter.EVENT_PROPAGATE;
+ }
+ }
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _pickTargetActor() {
+ return this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
+ this._dragX, this._dragY);
+ }
+
+ _updateDragHover() {
+ this._updateHoverId = 0;
+ let target = this._pickTargetActor();
+
+ let dragEvent = {
+ x: this._dragX,
+ y: this._dragY,
+ dragActor: this._dragActor,
+ source: this.actor._delegate,
+ targetActor: target,
+ };
+
+ let targetActorDestroyHandlerId;
+ let handleTargetActorDestroyClosure;
+ handleTargetActorDestroyClosure = () => {
+ target = this._pickTargetActor();
+ dragEvent.targetActor = target;
+ targetActorDestroyHandlerId =
+ target.connect('destroy', handleTargetActorDestroyClosure);
+ };
+ targetActorDestroyHandlerId =
+ target.connect('destroy', handleTargetActorDestroyClosure);
+
+ for (let i = 0; i < dragMonitors.length; i++) {
+ let motionFunc = dragMonitors[i].dragMotion;
+ if (motionFunc) {
+ let result = motionFunc(dragEvent);
+ if (result != DragMotionResult.CONTINUE) {
+ global.display.set_cursor(DRAG_CURSOR_MAP[result]);
+ dragEvent.targetActor.disconnect(targetActorDestroyHandlerId);
+ return GLib.SOURCE_REMOVE;
+ }
+ }
+ }
+ dragEvent.targetActor.disconnect(targetActorDestroyHandlerId);
+
+ while (target) {
+ if (target._delegate && target._delegate.handleDragOver) {
+ let [r_, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);
+ // We currently loop through all parents on drag-over even if one of the children has handled it.
+ // We can check the return value of the function and break the loop if it's true if we don't want
+ // to continue checking the parents.
+ let result = target._delegate.handleDragOver(this.actor._delegate,
+ this._dragActor,
+ targX,
+ targY,
+ 0);
+ if (result != DragMotionResult.CONTINUE) {
+ global.display.set_cursor(DRAG_CURSOR_MAP[result]);
+ return GLib.SOURCE_REMOVE;
+ }
+ }
+ target = target.get_parent();
+ }
+ global.display.set_cursor(Meta.Cursor.DND_IN_DRAG);
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _queueUpdateDragHover() {
+ if (this._updateHoverId)
+ return;
+
+ this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
+ this._updateDragHover.bind(this));
+ GLib.Source.set_name_by_id(this._updateHoverId, '[gnome-shell] this._updateDragHover');
+ }
+
+ _updateDragPosition(event) {
+ let [stageX, stageY] = event.get_coords();
+ this._dragX = stageX;
+ this._dragY = stageY;
+ this._dragActor.set_position(stageX + this._dragOffsetX,
+ stageY + this._dragOffsetY);
+
+ this._queueUpdateDragHover();
+ return true;
+ }
+
+ _dragActorDropped(event) {
+ let [dropX, dropY] = event.get_coords();
+ let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
+ dropX, dropY);
+
+ // We call observers only once per motion with the innermost
+ // target actor. If necessary, the observer can walk the
+ // parent itself.
+ let dropEvent = {
+ dropActor: this._dragActor,
+ targetActor: target,
+ clutterEvent: event,
+ };
+ for (let i = 0; i < dragMonitors.length; i++) {
+ let dropFunc = dragMonitors[i].dragDrop;
+ if (dropFunc) {
+ switch (dropFunc(dropEvent)) {
+ case DragDropResult.FAILURE:
+ case DragDropResult.SUCCESS:
+ return true;
+ case DragDropResult.CONTINUE:
+ continue;
+ }
+ }
+ }
+
+ // At this point it is too late to cancel a drag by destroying
+ // the actor, the fate of which is decided by acceptDrop and its
+ // side-effects
+ this._dragCancellable = false;
+
+ while (target) {
+ if (target._delegate && target._delegate.acceptDrop) {
+ let [r_, targX, targY] = target.transform_stage_point(dropX, dropY);
+ let accepted = false;
+ try {
+ accepted = target._delegate.acceptDrop(this.actor._delegate,
+ this._dragActor, targX, targY, event.get_time());
+ } catch (e) {
+ // On error, skip this target
+ logError(e, "Skipping drag target");
+ }
+ if (accepted) {
+ // If it accepted the drop without taking the actor,
+ // handle it ourselves.
+ if (this._dragActor && this._dragActor.get_parent() == Main.uiGroup) {
+ if (this._restoreOnSuccess) {
+ this._restoreDragActor(event.get_time());
+ return true;
+ } else {
+ this._dragActor.destroy();
+ }
+ }
+
+ this._dragState = DragState.INIT;
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ this.emit('drag-end', event.get_time(), true);
+ this._dragComplete();
+ return true;
+ }
+ }
+ target = target.get_parent();
+ }
+
+ this._cancelDrag(event.get_time());
+
+ return true;
+ }
+
+ _getRestoreLocation() {
+ let x, y, scale;
+
+ if (this._dragActorSource && this._dragActorSource.visible) {
+ // Snap the clone back to its source
+ [x, y] = this._dragActorSource.get_transformed_position();
+ let [sourceScaledWidth] = this._dragActorSource.get_transformed_size();
+ scale = sourceScaledWidth ? sourceScaledWidth / this._dragActor.width : 0;
+ } else if (this._dragOrigParent) {
+ // Snap the actor back to its original position within
+ // its parent, adjusting for the fact that the parent
+ // may have been moved or scaled
+ let [parentX, parentY] = this._dragOrigParent.get_transformed_position();
+ let parentScale = _getRealActorScale(this._dragOrigParent);
+
+ x = parentX + parentScale * this._dragOrigX;
+ y = parentY + parentScale * this._dragOrigY;
+ scale = this._dragOrigScale * parentScale;
+ } else {
+ // Snap back actor to its original stage position
+ x = this._snapBackX;
+ y = this._snapBackY;
+ scale = this._snapBackScale;
+ }
+
+ return [x, y, scale];
+ }
+
+ _cancelDrag(eventTime) {
+ this.emit('drag-cancelled', eventTime);
+ let wasCancelled = this._dragState == DragState.CANCELLED;
+ this._dragState = DragState.CANCELLED;
+
+ if (this._actorDestroyed || wasCancelled) {
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ if (!this._buttonDown)
+ this._dragComplete();
+ this.emit('drag-end', eventTime, false);
+ if (!this._dragOrigParent && this._dragActor)
+ this._dragActor.destroy();
+
+ return;
+ }
+
+ let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
+
+ this._animateDragEnd(eventTime, {
+ x: snapBackX,
+ y: snapBackY,
+ scale_x: snapBackScale,
+ scale_y: snapBackScale,
+ duration: SNAP_BACK_ANIMATION_TIME,
+ });
+ }
+
+ _restoreDragActor(eventTime) {
+ this._dragState = DragState.INIT;
+ let [restoreX, restoreY, restoreScale] = this._getRestoreLocation();
+
+ // fade the actor back in at its original location
+ this._dragActor.set_position(restoreX, restoreY);
+ this._dragActor.set_scale(restoreScale, restoreScale);
+ this._dragActor.opacity = 0;
+
+ this._animateDragEnd(eventTime, {
+ duration: REVERT_ANIMATION_TIME,
+ });
+ }
+
+ _animateDragEnd(eventTime, params) {
+ this._animationInProgress = true;
+
+ // start the animation
+ this._dragActor.ease(Object.assign(params, {
+ opacity: this._dragOrigOpacity,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._onAnimationComplete(this._dragActor, eventTime);
+ },
+ }));
+ }
+
+ _finishAnimation() {
+ if (!this._animationInProgress)
+ return;
+
+ this._animationInProgress = false;
+ if (!this._buttonDown)
+ this._dragComplete();
+
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ }
+
+ _onAnimationComplete(dragActor, eventTime) {
+ if (this._dragOrigParent) {
+ Main.uiGroup.remove_child(this._dragActor);
+ this._dragOrigParent.add_actor(this._dragActor);
+ dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
+ if (this._dragActorHadFixedPos)
+ dragActor.set_position(this._dragOrigX, this._dragOrigY);
+ else
+ dragActor.fixed_position_set = false;
+ if (this._dragActorHadNatWidth)
+ this._dragActor.set_width(-1);
+ if (this._dragActorHadNatHeight)
+ this._dragActor.set_height(-1);
+ } else {
+ dragActor.destroy();
+ }
+
+ this.emit('drag-end', eventTime, false);
+ this._finishAnimation();
+ }
+
+ _dragComplete() {
+ if (!this._actorDestroyed && this._dragActor)
+ Shell.util_set_hidden_from_pick(this._dragActor, false);
+
+ this._ungrabEvents();
+
+ if (this._updateHoverId) {
+ GLib.source_remove(this._updateHoverId);
+ this._updateHoverId = 0;
+ }
+
+ if (this._dragActor) {
+ this._dragActor.disconnect(this._dragActorDestroyId);
+ this._dragActor = null;
+ }
+
+ if (this._dragOrigParent) {
+ this._dragOrigParent.disconnect(this._dragOrigParentDestroyId);
+ this._dragOrigParent = null;
+ }
+
+ if (this._dragActorSource) {
+ this._dragActorSource.disconnect(this._dragActorSourceDestroyId);
+ this._dragActorSource = null;
+ }
+
+ this._dragState = DragState.INIT;
+ currentDraggable = null;
+ }
+};
+
+/**
+ * makeDraggable:
+ * @param {Clutter.Actor} actor: Source actor
+ * @param {Object=} params: Additional parameters
+ * @returns {Object} a new Draggable
+ *
+ * Create an object which controls drag and drop for the given actor.
+ *
+ * If %manualMode is %true in @params, do not automatically start
+ * drag and drop on click
+ *
+ * If %dragActorMaxSize is present in @params, the drag actor will
+ * be scaled down to be no larger than that size in pixels.
+ *
+ * If %dragActorOpacity is present in @params, the drag actor will
+ * will be set to have that opacity during the drag.
+ *
+ * Note that when the drag actor is the source actor and the drop
+ * succeeds, the actor scale and opacity aren't reset; if the drop
+ * target wants to reuse the actor, it's up to the drop target to
+ * reset these values.
+ */
+function makeDraggable(actor, params) {
+ return new _Draggable(actor, params);
+}
diff --git a/js/ui/edgeDragAction.js b/js/ui/edgeDragAction.js
new file mode 100644
index 0000000..c0f9e4e
--- /dev/null
+++ b/js/ui/edgeDragAction.js
@@ -0,0 +1,89 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported EdgeDragAction */
+
+const { Clutter, GObject, Meta, St } = imports.gi;
+
+const Main = imports.ui.main;
+
+var EDGE_THRESHOLD = 20;
+var DRAG_DISTANCE = 80;
+
+var EdgeDragAction = GObject.registerClass({
+ Signals: {
+ 'activated': {},
+ 'progress': { param_types: [GObject.TYPE_DOUBLE] },
+ },
+}, class EdgeDragAction extends Clutter.GestureAction {
+ _init(side, allowedModes) {
+ super._init();
+ this._side = side;
+ this._allowedModes = allowedModes;
+ this.set_n_touch_points(1);
+ this.set_threshold_trigger_edge(Clutter.GestureTriggerEdge.AFTER);
+
+ global.display.connect('grab-op-begin', () => this.cancel());
+ }
+
+ _getMonitorRect(x, y) {
+ let rect = new Meta.Rectangle({ x: x - 1, y: y - 1, width: 1, height: 1 });
+ let monitorIndex = global.display.get_monitor_index_for_rect(rect);
+
+ return global.display.get_monitor_geometry(monitorIndex);
+ }
+
+ vfunc_gesture_prepare(_actor) {
+ if (this.get_n_current_points() == 0)
+ return false;
+
+ if (!(this._allowedModes & Main.actionMode))
+ return false;
+
+ let [x, y] = this.get_press_coords(0);
+ let monitorRect = this._getMonitorRect(x, y);
+
+ return (this._side == St.Side.LEFT && x < monitorRect.x + EDGE_THRESHOLD) ||
+ (this._side == St.Side.RIGHT && x > monitorRect.x + monitorRect.width - EDGE_THRESHOLD) ||
+ (this._side == St.Side.TOP && y < monitorRect.y + EDGE_THRESHOLD) ||
+ (this._side == St.Side.BOTTOM && y > monitorRect.y + monitorRect.height - EDGE_THRESHOLD);
+ }
+
+ vfunc_gesture_progress(_actor) {
+ let [startX, startY] = this.get_press_coords(0);
+ let [x, y] = this.get_motion_coords(0);
+ let offsetX = Math.abs(x - startX);
+ let offsetY = Math.abs(y - startY);
+
+ if (offsetX < EDGE_THRESHOLD && offsetY < EDGE_THRESHOLD)
+ return true;
+
+ if ((offsetX > offsetY &&
+ (this._side == St.Side.TOP || this._side == St.Side.BOTTOM)) ||
+ (offsetY > offsetX &&
+ (this._side == St.Side.LEFT || this._side == St.Side.RIGHT))) {
+ this.cancel();
+ return false;
+ }
+
+ if (this._side === St.Side.TOP ||
+ this._side === St.Side.BOTTOM)
+ this.emit('progress', offsetY);
+ else
+ this.emit('progress', offsetX);
+
+ return true;
+ }
+
+ vfunc_gesture_end(_actor) {
+ let [startX, startY] = this.get_press_coords(0);
+ let [x, y] = this.get_motion_coords(0);
+ let monitorRect = this._getMonitorRect(startX, startY);
+
+ if ((this._side == St.Side.TOP && y > monitorRect.y + DRAG_DISTANCE) ||
+ (this._side == St.Side.BOTTOM && y < monitorRect.y + monitorRect.height - DRAG_DISTANCE) ||
+ (this._side == St.Side.LEFT && x > monitorRect.x + DRAG_DISTANCE) ||
+ (this._side == St.Side.RIGHT && x < monitorRect.x + monitorRect.width - DRAG_DISTANCE))
+ this.emit('activated');
+ else
+ this.cancel();
+ }
+});
diff --git a/js/ui/endSessionDialog.js b/js/ui/endSessionDialog.js
new file mode 100644
index 0000000..ca24d06
--- /dev/null
+++ b/js/ui/endSessionDialog.js
@@ -0,0 +1,798 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported init, EndSessionDialog */
+/*
+ * Copyright 2010-2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+const {
+ AccountsService, Clutter, Gio, GLib, GObject,
+ Pango, Polkit, Shell, St, UPowerGlib: UPower,
+} = imports.gi;
+
+const CheckBox = imports.ui.checkBox;
+const Dialog = imports.ui.dialog;
+const GnomeSession = imports.misc.gnomeSession;
+const LoginManager = imports.misc.loginManager;
+const ModalDialog = imports.ui.modalDialog;
+const UserWidget = imports.ui.userWidget;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const _ITEM_ICON_SIZE = 64;
+
+const LOW_BATTERY_THRESHOLD = 30;
+
+const EndSessionDialogIface = loadInterfaceXML('org.gnome.SessionManager.EndSessionDialog');
+
+const logoutDialogContent = {
+ subjectWithUser: C_("title", "Log Out %s"),
+ subject: C_("title", "Log Out"),
+ descriptionWithUser(user, seconds) {
+ return ngettext(
+ '%s will be logged out automatically in %d second.',
+ '%s will be logged out automatically in %d seconds.',
+ seconds).format(user, seconds);
+ },
+ description(seconds) {
+ return ngettext(
+ 'You will be logged out automatically in %d second.',
+ 'You will be logged out automatically in %d seconds.',
+ seconds).format(seconds);
+ },
+ showBatteryWarning: false,
+ confirmButtons: [{
+ signal: 'ConfirmedLogout',
+ label: C_('button', 'Log Out'),
+ }],
+ showOtherSessions: false,
+};
+
+const shutdownDialogContent = {
+ subject: C_("title", "Power Off"),
+ subjectWithUpdates: C_("title", "Install Updates & Power Off"),
+ description(seconds) {
+ return ngettext(
+ 'The system will power off automatically in %d second.',
+ 'The system will power off automatically in %d seconds.',
+ seconds).format(seconds);
+ },
+ checkBoxText: C_("checkbox", "Install pending software updates"),
+ showBatteryWarning: true,
+ confirmButtons: [{
+ signal: 'ConfirmedShutdown',
+ label: C_('button', 'Power Off'),
+ }],
+ iconName: 'system-shutdown-symbolic',
+ showOtherSessions: true,
+};
+
+const restartDialogContent = {
+ subject: C_("title", "Restart"),
+ subjectWithUpdates: C_('title', 'Install Updates & Restart'),
+ description(seconds) {
+ return ngettext(
+ 'The system will restart automatically in %d second.',
+ 'The system will restart automatically in %d seconds.',
+ seconds).format(seconds);
+ },
+ checkBoxText: C_('checkbox', 'Install pending software updates'),
+ showBatteryWarning: true,
+ confirmButtons: [{
+ signal: 'ConfirmedReboot',
+ label: C_('button', 'Restart'),
+ }],
+ iconName: 'view-refresh-symbolic',
+ showOtherSessions: true,
+};
+
+const restartUpdateDialogContent = {
+
+ subject: C_("title", "Restart & Install Updates"),
+ description(seconds) {
+ return ngettext(
+ 'The system will automatically restart and install updates in %d second.',
+ 'The system will automatically restart and install updates in %d seconds.',
+ seconds).format(seconds);
+ },
+ showBatteryWarning: true,
+ confirmButtons: [{
+ signal: 'ConfirmedReboot',
+ label: C_('button', 'Restart &amp; Install'),
+ }],
+ unusedFutureButtonForTranslation: C_("button", "Install &amp; Power Off"),
+ unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"),
+ iconName: 'view-refresh-symbolic',
+ showOtherSessions: true,
+};
+
+const restartUpgradeDialogContent = {
+
+ subject: C_("title", "Restart & Install Upgrade"),
+ upgradeDescription(distroName, distroVersion) {
+ /* Translators: This is the text displayed for system upgrades in the
+ shut down dialog. First %s gets replaced with the distro name and
+ second %s with the distro version to upgrade to */
+ return _("%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you have backed up and that the computer is plugged in.").format(distroName, distroVersion);
+ },
+ disableTimer: true,
+ showBatteryWarning: false,
+ confirmButtons: [{
+ signal: 'ConfirmedReboot',
+ label: C_('button', 'Restart &amp; Install'),
+ }],
+ iconName: 'view-refresh-symbolic',
+ showOtherSessions: true,
+};
+
+const DialogType = {
+ LOGOUT: 0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */,
+ SHUTDOWN: 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */,
+ RESTART: 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */,
+ UPDATE_RESTART: 3,
+ UPGRADE_RESTART: 4,
+};
+
+const DialogContent = {
+ 0 /* DialogType.LOGOUT */: logoutDialogContent,
+ 1 /* DialogType.SHUTDOWN */: shutdownDialogContent,
+ 2 /* DialogType.RESTART */: restartDialogContent,
+ 3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent,
+ 4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent,
+};
+
+var MAX_USERS_IN_SESSION_DIALOG = 5;
+
+const LogindSessionIface = loadInterfaceXML('org.freedesktop.login1.Session');
+const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
+
+const PkOfflineIface = loadInterfaceXML('org.freedesktop.PackageKit.Offline');
+const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface);
+
+const UPowerIface = loadInterfaceXML('org.freedesktop.UPower.Device');
+const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
+
+function findAppFromInhibitor(inhibitor) {
+ let desktopFile;
+ try {
+ [desktopFile] = inhibitor.GetAppIdSync();
+ } catch (e) {
+ // XXX -- sometimes JIT inhibitors generated by gnome-session
+ // get removed too soon. Don't fail in this case.
+ log(`gnome-session gave us a dead inhibitor: ${inhibitor.get_object_path()}`);
+ return null;
+ }
+
+ if (!GLib.str_has_suffix(desktopFile, '.desktop'))
+ desktopFile += '.desktop';
+
+ return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
+}
+
+// The logout timer only shows updates every 10 seconds
+// until the last 10 seconds, then it shows updates every
+// second. This function takes a given time and returns
+// what we should show to the user for that time.
+function _roundSecondsToInterval(totalSeconds, secondsLeft, interval) {
+ let time;
+
+ time = Math.ceil(secondsLeft);
+
+ // Final count down is in decrements of 1
+ if (time <= interval)
+ return time;
+
+ // Round up higher than last displayable time interval
+ time += interval - 1;
+
+ // Then round down to that time interval
+ if (time > totalSeconds)
+ time = Math.ceil(totalSeconds);
+ else
+ time -= time % interval;
+
+ return time;
+}
+
+function _setCheckBoxLabel(checkBox, text) {
+ let label = checkBox.getLabelActor();
+
+ if (text) {
+ label.set_text(text);
+ checkBox.show();
+ } else {
+ label.set_text('');
+ checkBox.hide();
+ }
+}
+
+function init() {
+ // This always returns the same singleton object
+ // By instantiating it initially, we register the
+ // bus object, etc.
+ new EndSessionDialog();
+}
+
+var EndSessionDialog = GObject.registerClass(
+class EndSessionDialog extends ModalDialog.ModalDialog {
+ _init() {
+ super._init({
+ styleClass: 'end-session-dialog',
+ destroyOnClose: false,
+ });
+
+ this._loginManager = LoginManager.getLoginManager();
+ this._canRebootToBootLoaderMenu = false;
+ this._getCanRebootToBootLoaderMenu();
+
+ this._userManager = AccountsService.UserManager.get_default();
+ this._user = this._userManager.get_user(GLib.get_user_name());
+ this._updatesPermission = null;
+
+ this._pkOfflineProxy = new PkOfflineProxy(Gio.DBus.system,
+ 'org.freedesktop.PackageKit',
+ '/org/freedesktop/PackageKit',
+ this._onPkOfflineProxyCreated.bind(this));
+
+ this._powerProxy = new UPowerProxy(Gio.DBus.system,
+ 'org.freedesktop.UPower',
+ '/org/freedesktop/UPower/devices/DisplayDevice',
+ (proxy, error) => {
+ if (error) {
+ log(error.message);
+ return;
+ }
+ this._powerProxy.connect('g-properties-changed',
+ this._sync.bind(this));
+ this._sync();
+ });
+
+ this._secondsLeft = 0;
+ this._totalSecondsToStayOpen = 0;
+ this._applications = [];
+ this._sessions = [];
+ this._capturedEventId = 0;
+ this._rebootButton = null;
+ this._rebootButtonAlt = null;
+
+ this.connect('opened',
+ this._onOpened.bind(this));
+
+ this._user.connectObject(
+ 'notify::is-loaded', this._sync.bind(this),
+ 'changed', this._sync.bind(this), this);
+
+ this._messageDialogContent = new Dialog.MessageDialogContent();
+
+ this._checkBox = new CheckBox.CheckBox();
+ this._checkBox.connect('clicked', this._sync.bind(this));
+ this._messageDialogContent.add_child(this._checkBox);
+
+ this._batteryWarning = new St.Label({
+ style_class: 'end-session-dialog-battery-warning',
+ text: _('Low battery power: please plug in before installing updates.'),
+ });
+ this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._batteryWarning.clutter_text.line_wrap = true;
+ this._messageDialogContent.add_child(this._batteryWarning);
+
+ this.contentLayout.add_child(this._messageDialogContent);
+
+ this._applicationSection = new Dialog.ListSection({
+ title: _('Some applications are busy or have unsaved work'),
+ });
+ this.contentLayout.add_child(this._applicationSection);
+
+ this._sessionSection = new Dialog.ListSection({
+ title: _('Other users are logged in'),
+ });
+ this.contentLayout.add_child(this._sessionSection);
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
+ }
+
+ async _getCanRebootToBootLoaderMenu() {
+ const {canRebootToBootLoaderMenu} = await this._loginManager.canRebootToBootLoaderMenu();
+ this._canRebootToBootLoaderMenu = canRebootToBootLoaderMenu;
+ }
+
+ async _onPkOfflineProxyCreated(proxy, error) {
+ if (error) {
+ log(error.message);
+ return;
+ }
+
+ // Creating a D-Bus proxy won't propagate SERVICE_UNKNOWN or NAME_HAS_NO_OWNER
+ // errors if PackageKit is not available, but the GIO implementation will make
+ // sure in that case that the proxy's g-name-owner is set to null, so check that.
+ if (this._pkOfflineProxy.g_name_owner === null) {
+ this._pkOfflineProxy = null;
+ return;
+ }
+
+ // It only makes sense to check for this permission if PackageKit is available.
+ try {
+ this._updatesPermission = await Polkit.Permission.new(
+ 'org.freedesktop.packagekit.trigger-offline-update', null, null);
+ } catch (e) {
+ log(`No permission to trigger offline updates: ${e}`);
+ }
+ }
+
+ _isDischargingBattery() {
+ return this._powerProxy.IsPresent &&
+ this._powerProxy.State !== UPower.DeviceState.CHARGING &&
+ this._powerProxy.State !== UPower.DeviceState.FULLY_CHARGED;
+ }
+
+ _isBatteryLow() {
+ return this._isDischargingBattery() && this._powerProxy.Percentage < LOW_BATTERY_THRESHOLD;
+ }
+
+ _shouldShowLowBatteryWarning(dialogContent) {
+ if (!dialogContent.showBatteryWarning)
+ return false;
+
+ if (!this._isBatteryLow())
+ return false;
+
+ if (this._checkBox.checked)
+ return true;
+
+ // Show the warning if updates have already been triggered, but
+ // the user doesn't have enough permissions to cancel them.
+ let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
+ return this._updateInfo.UpdatePrepared && this._updateInfo.UpdateTriggered && !updatesAllowed;
+ }
+
+ _sync() {
+ let open = this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED;
+ if (!open)
+ return;
+
+ let dialogContent = DialogContent[this._type];
+
+ let subject = dialogContent.subject;
+
+ // Use different title when we are installing updates
+ if (dialogContent.subjectWithUpdates && this._checkBox.checked)
+ subject = dialogContent.subjectWithUpdates;
+
+ this._batteryWarning.visible = this._shouldShowLowBatteryWarning(dialogContent);
+
+ let description;
+ let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
+ this._secondsLeft,
+ 10);
+
+ if (this._user.is_loaded) {
+ let realName = this._user.get_real_name();
+
+ if (realName != null) {
+ if (dialogContent.subjectWithUser)
+ subject = dialogContent.subjectWithUser.format(realName);
+
+ if (dialogContent.descriptionWithUser)
+ description = dialogContent.descriptionWithUser(realName, displayTime);
+ }
+ }
+
+ // Use a different description when we are installing a system upgrade
+ // if the PackageKit proxy is available (i.e. PackageKit is available).
+ if (dialogContent.upgradeDescription) {
+ const { name, version } = this._updateInfo.PreparedUpgrade;
+ if (name != null && version != null)
+ description = dialogContent.upgradeDescription(name, version);
+ }
+
+ // Fall back to regular description
+ if (!description)
+ description = dialogContent.description(displayTime);
+
+ this._messageDialogContent.title = subject;
+ this._messageDialogContent.description = description;
+
+ let hasApplications = this._applications.length > 0;
+ let hasSessions = this._sessions.length > 0;
+
+ this._applicationSection.visible = hasApplications;
+ this._sessionSection.visible = hasSessions;
+ }
+
+ _onCapturedEvent(actor, event) {
+ let altEnabled = false;
+
+ let type = event.type();
+ if (type !== Clutter.EventType.KEY_PRESS && type !== Clutter.EventType.KEY_RELEASE)
+ return Clutter.EVENT_PROPAGATE;
+
+ let key = event.get_key_symbol();
+ if (key !== Clutter.KEY_Alt_L && key !== Clutter.KEY_Alt_R)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (type === Clutter.EventType.KEY_PRESS)
+ altEnabled = true;
+
+ this._rebootButton.visible = !altEnabled;
+ this._rebootButtonAlt.visible = altEnabled;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _updateButtons() {
+ this.clearButtons();
+
+ this.addButton({
+ action: this.cancel.bind(this),
+ label: _('Cancel'),
+ key: Clutter.KEY_Escape,
+ });
+
+ let dialogContent = DialogContent[this._type];
+ for (let i = 0; i < dialogContent.confirmButtons.length; i++) {
+ let signal = dialogContent.confirmButtons[i].signal;
+ let label = dialogContent.confirmButtons[i].label;
+ let button = this.addButton({
+ action: () => {
+ this.close(true);
+ let signalId = this.connect('closed', () => {
+ this.disconnect(signalId);
+ this._confirm(signal);
+ });
+ },
+ label,
+ });
+
+ // Add Alt "Boot Options" option to the Reboot button
+ if (this._canRebootToBootLoaderMenu && signal === 'ConfirmedReboot') {
+ this._rebootButton = button;
+ this._rebootButtonAlt = this.addButton({
+ action: () => {
+ this.close(true);
+ let signalId = this.connect('closed', () => {
+ this.disconnect(signalId);
+ this._confirmRebootToBootLoaderMenu();
+ });
+ },
+ label: C_('button', 'Boot Options'),
+ });
+ this._rebootButtonAlt.visible = false;
+ this._capturedEventId = this.connect('captured-event',
+ this._onCapturedEvent.bind(this));
+ }
+ }
+ }
+
+ _stopAltCapture() {
+ if (this._capturedEventId > 0) {
+ global.stage.disconnect(this._capturedEventId);
+ this._capturedEventId = 0;
+ }
+ this._rebootButton = null;
+ this._rebootButtonAlt = null;
+ }
+
+ close(skipSignal) {
+ super.close();
+
+ if (!skipSignal)
+ this._dbusImpl.emit_signal('Closed', null);
+ }
+
+ cancel() {
+ this._stopTimer();
+ this._stopAltCapture();
+ this._dbusImpl.emit_signal('Canceled', null);
+ this.close();
+ }
+
+ _confirmRebootToBootLoaderMenu() {
+ this._loginManager.setRebootToBootLoaderMenu();
+ this._confirm('ConfirmedReboot');
+ }
+
+ async _confirm(signal) {
+ if (this._checkBox.visible) {
+ // Trigger the offline update as requested
+ if (this._checkBox.checked) {
+ switch (signal) {
+ case 'ConfirmedReboot':
+ await this._triggerOfflineUpdateReboot();
+ break;
+ case 'ConfirmedShutdown':
+ // To actually trigger the offline update, we need to
+ // reboot to do the upgrade. When the upgrade is complete,
+ // the computer will shut down automatically.
+ signal = 'ConfirmedReboot';
+ await this._triggerOfflineUpdateShutdown();
+ break;
+ default:
+ break;
+ }
+ } else {
+ await this._triggerOfflineUpdateCancel();
+ }
+ }
+
+ this._fadeOutDialog();
+ this._stopTimer();
+ this._stopAltCapture();
+ this._dbusImpl.emit_signal(signal, null);
+ }
+
+ _onOpened() {
+ this._sync();
+ }
+
+ async _triggerOfflineUpdateReboot() {
+ // Handle this gracefully if PackageKit is not available.
+ if (!this._pkOfflineProxy)
+ return;
+
+ try {
+ await this._pkOfflineProxy.TriggerAsync('reboot');
+ } catch (error) {
+ log(error.message);
+ }
+ }
+
+ async _triggerOfflineUpdateShutdown() {
+ // Handle this gracefully if PackageKit is not available.
+ if (!this._pkOfflineProxy)
+ return;
+
+ try {
+ await this._pkOfflineProxy.TriggerAsync('power-off');
+ } catch (error) {
+ log(error.message);
+ }
+ }
+
+ async _triggerOfflineUpdateCancel() {
+ // Handle this gracefully if PackageKit is not available.
+ if (!this._pkOfflineProxy)
+ return;
+
+ try {
+ await this._pkOfflineProxy.CancelAsync();
+ } catch (error) {
+ log(error.message);
+ }
+ }
+
+ _startTimer() {
+ let startTime = GLib.get_monotonic_time();
+ this._secondsLeft = this._totalSecondsToStayOpen;
+
+ this._timerId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
+ let currentTime = GLib.get_monotonic_time();
+ let secondsElapsed = (currentTime - startTime) / 1000000;
+
+ this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
+ if (this._secondsLeft > 0) {
+ this._sync();
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ let dialogContent = DialogContent[this._type];
+ let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
+ this._confirm(button.signal);
+ this._timerId = 0;
+
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._timerId, '[gnome-shell] this._confirm');
+ }
+
+ _stopTimer() {
+ if (this._timerId > 0) {
+ GLib.source_remove(this._timerId);
+ this._timerId = 0;
+ }
+
+ this._secondsLeft = 0;
+ }
+
+ _onInhibitorLoaded(inhibitor) {
+ if (!this._applications.includes(inhibitor)) {
+ // Stale inhibitor
+ return;
+ }
+
+ let app = findAppFromInhibitor(inhibitor);
+ const [flags] = app ? inhibitor.GetFlagsSync() : [0];
+
+ if (app && flags & GnomeSession.InhibitFlags.LOGOUT) {
+ let [description] = inhibitor.GetReasonSync();
+ let listItem = new Dialog.ListSectionItem({
+ icon_actor: app.create_icon_texture(_ITEM_ICON_SIZE),
+ title: app.get_name(),
+ description,
+ });
+ this._applicationSection.list.add_child(listItem);
+ } else {
+ // inhibiting app is a service (not an application) or is not
+ // inhibiting logout/shutdown
+ this._applications.splice(this._applications.indexOf(inhibitor), 1);
+ }
+
+ this._sync();
+ }
+
+ async _loadSessions() {
+ let sessionId = GLib.getenv('XDG_SESSION_ID');
+ if (!sessionId) {
+ const currentSessionProxy = await this._loginManager.getCurrentSessionProxy();
+ sessionId = currentSessionProxy.Id;
+ log(`endSessionDialog: No XDG_SESSION_ID, fetched from logind: ${sessionId}`);
+ }
+
+ const sessions = await this._loginManager.listSessions();
+ for (const [id_, uid_, userName, seat_, sessionPath] of sessions) {
+ let proxy = new LogindSession(Gio.DBus.system, 'org.freedesktop.login1', sessionPath);
+
+ if (proxy.Class !== 'user')
+ continue;
+
+ if (proxy.State === 'closing')
+ continue;
+
+ if (proxy.Id === sessionId)
+ continue;
+
+ const session = {
+ user: this._userManager.get_user(userName),
+ username: userName,
+ type: proxy.Type,
+ remote: proxy.Remote,
+ };
+ const nSessions = this._sessions.push(session);
+
+ let userAvatar = new UserWidget.Avatar(session.user, {
+ iconSize: _ITEM_ICON_SIZE,
+ });
+ userAvatar.update();
+
+ const displayUserName =
+ session.user.get_real_name() ?? session.username;
+
+ let userLabelText;
+ if (session.remote)
+ /* Translators: Remote here refers to a remote session, like a ssh login */
+ userLabelText = _('%s (remote)').format(displayUserName);
+ else if (session.type === 'tty')
+ /* Translators: Console here refers to a tty like a VT console */
+ userLabelText = _('%s (console)').format(displayUserName);
+ else
+ userLabelText = userName;
+
+ let listItem = new Dialog.ListSectionItem({
+ icon_actor: userAvatar,
+ title: userLabelText,
+ });
+ this._sessionSection.list.add_child(listItem);
+
+ // limit the number of entries
+ if (nSessions === MAX_USERS_IN_SESSION_DIALOG)
+ break;
+ }
+
+ this._sync();
+ }
+
+ async _getUpdateInfo() {
+ const connection = this._pkOfflineProxy.get_connection();
+ const reply = await connection.call(
+ this._pkOfflineProxy.g_name,
+ this._pkOfflineProxy.g_object_path,
+ 'org.freedesktop.DBus.Properties',
+ 'GetAll',
+ new GLib.Variant('(s)', [this._pkOfflineProxy.g_interface_name]),
+ null,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ null);
+ const [info] = reply.recursiveUnpack();
+ return info;
+ }
+
+ async OpenAsync(parameters, invocation) {
+ let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
+ this._totalSecondsToStayOpen = totalSecondsToStayOpen;
+ this._type = type;
+
+ try {
+ this._updateInfo = await this._getUpdateInfo();
+ } catch (e) {
+ if (this._pkOfflineProxy !== null)
+ log(`Failed to get update info from PackageKit: ${e.message}`);
+
+ this._updateInfo = {
+ UpdateTriggered: false,
+ UpdatePrepared: false,
+ UpgradeTriggered: false,
+ PreparedUpgrade: {},
+ };
+ }
+
+ // Only consider updates and upgrades if PackageKit is available.
+ if (this._pkOfflineProxy && this._type == DialogType.RESTART) {
+ if (this._updateInfo.UpdateTriggered)
+ this._type = DialogType.UPDATE_RESTART;
+ else if (this._updateInfo.UpgradeTriggered)
+ this._type = DialogType.UPGRADE_RESTART;
+ }
+
+ this._applications = [];
+ this._applicationSection.list.destroy_all_children();
+
+ this._sessions = [];
+ this._sessionSection.list.destroy_all_children();
+
+ if (!(this._type in DialogContent)) {
+ invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
+ "Unknown dialog type requested");
+ return;
+ }
+
+ let dialogContent = DialogContent[this._type];
+
+ for (let i = 0; i < inhibitorObjectPaths.length; i++) {
+ let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], proxy => {
+ this._onInhibitorLoaded(proxy);
+ });
+
+ this._applications.push(inhibitor);
+ }
+
+ if (dialogContent.showOtherSessions)
+ this._loadSessions();
+
+ let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
+
+ _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || '');
+ this._checkBox.visible = dialogContent.checkBoxText && this._updateInfo.UpdatePrepared && updatesAllowed;
+
+ if (this._type === DialogType.UPGRADE_RESTART)
+ this._checkBox.checked = this._checkBox.visible && this._updateInfo.UpdateTriggered && !this._isDischargingBattery();
+ else
+ this._checkBox.checked = this._checkBox.visible && !this._isBatteryLow();
+
+ this._batteryWarning.visible = this._shouldShowLowBatteryWarning(dialogContent);
+
+ this._updateButtons();
+
+ if (!this.open(timestamp)) {
+ invocation.return_dbus_error('org.gnome.Shell.ModalDialog.GrabError',
+ "Cannot grab pointer and keyboard");
+ return;
+ }
+
+ if (!dialogContent.disableTimer)
+ this._startTimer();
+
+ this._sync();
+
+ let signalId = this.connect('opened', () => {
+ invocation.return_value(null);
+ this.disconnect(signalId);
+ });
+ }
+
+ Close(_parameters, _invocation) {
+ this.close();
+ }
+});
diff --git a/js/ui/environment.js b/js/ui/environment.js
new file mode 100644
index 0000000..8c790da
--- /dev/null
+++ b/js/ui/environment.js
@@ -0,0 +1,470 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported init */
+
+const Config = imports.misc.config;
+
+imports.gi.versions.AccountsService = '1.0';
+imports.gi.versions.Atk = '1.0';
+imports.gi.versions.Atspi = '2.0';
+imports.gi.versions.Clutter = Config.LIBMUTTER_API_VERSION;
+imports.gi.versions.Cogl = Config.LIBMUTTER_API_VERSION;
+imports.gi.versions.Gcr = '4';
+imports.gi.versions.Gdk = '3.0';
+imports.gi.versions.Gdm = '1.0';
+imports.gi.versions.Geoclue = '2.0';
+imports.gi.versions.Gio = '2.0';
+imports.gi.versions.GDesktopEnums = '3.0';
+imports.gi.versions.GdkPixbuf = '2.0';
+imports.gi.versions.GnomeBluetooth = '3.0';
+imports.gi.versions.GnomeDesktop = '3.0';
+imports.gi.versions.Graphene = '1.0';
+imports.gi.versions.Gtk = '3.0';
+imports.gi.versions.GWeather = '4.0';
+imports.gi.versions.IBus = '1.0';
+imports.gi.versions.Malcontent = '0';
+imports.gi.versions.NM = '1.0';
+imports.gi.versions.NMA = '1.0';
+imports.gi.versions.Pango = '1.0';
+imports.gi.versions.Polkit = '1.0';
+imports.gi.versions.PolkitAgent = '1.0';
+imports.gi.versions.Rsvg = '2.0';
+imports.gi.versions.Soup = '3.0';
+imports.gi.versions.TelepathyGLib = '0.12';
+imports.gi.versions.TelepathyLogger = '0.2';
+imports.gi.versions.UPowerGlib = '1.0';
+
+try {
+ if (Config.HAVE_SOUP2)
+ throw new Error('Soup3 support not enabled');
+ const Soup_ = imports.gi.Soup;
+} catch (e) {
+ imports.gi.versions.Soup = '2.4';
+ const { Soup } = imports.gi;
+ _injectSoup3Compat(Soup);
+}
+
+const { Clutter, Gio, GLib, GObject, Meta, Polkit, Shell, St } = imports.gi;
+const Gettext = imports.gettext;
+const System = imports.system;
+const SignalTracker = imports.misc.signalTracker;
+
+Gio._promisify(Gio.DataInputStream.prototype, 'fill_async');
+Gio._promisify(Gio.DataInputStream.prototype, 'read_line_async');
+Gio._promisify(Gio.DBus, 'get');
+Gio._promisify(Gio.DBusConnection.prototype, 'call');
+Gio._promisify(Gio.DBusProxy, 'new');
+Gio._promisify(Gio.DBusProxy.prototype, 'init_async');
+Gio._promisify(Gio.DBusProxy.prototype, 'call_with_unix_fd_list');
+Gio._promisify(Polkit.Permission, 'new');
+
+let _localTimeZone = null;
+
+// We can't import shell JS modules yet, because they may have
+// variable initializations, etc, that depend on init() already having
+// been run.
+
+
+// "monkey patch" in some varargs ClutterContainer methods; we need
+// to do this per-container class since there is no representation
+// of interfaces in Javascript
+function _patchContainerClass(containerClass) {
+ // This one is a straightforward mapping of the C method
+ containerClass.prototype.child_set = function (actor, props) {
+ let meta = this.get_child_meta(actor);
+ for (let prop in props)
+ meta[prop] = props[prop];
+ };
+
+ // clutter_container_add() actually is a an add-many-actors
+ // method. We conveniently, but somewhat dubiously, take the
+ // this opportunity to make it do something more useful.
+ containerClass.prototype.add = function (actor, props) {
+ this.add_actor(actor);
+ if (props)
+ this.child_set(actor, props);
+ };
+}
+
+function _patchLayoutClass(layoutClass, styleProps) {
+ if (styleProps) {
+ layoutClass.prototype.hookup_style = function (container) {
+ container.connect('style-changed', () => {
+ let node = container.get_theme_node();
+ for (let prop in styleProps) {
+ let [found, length] = node.lookup_length(styleProps[prop], false);
+ if (found)
+ this[prop] = length;
+ }
+ });
+ };
+ }
+}
+
+/**
+ * Mimick the Soup 3 APIs we use when falling back to Soup 2.4
+ *
+ * @param {object} Soup 2.4 namespace
+ * @returns {void}
+ */
+function _injectSoup3Compat(Soup) {
+ Soup.StatusCode = Soup.KnownStatusCode;
+
+ Soup.Message.new_from_encoded_form =
+ function (method, uri, form) {
+ const soupUri = new Soup.URI(uri);
+ soupUri.set_query(form);
+ return Soup.Message.new_from_uri(method, soupUri);
+ };
+ Soup.Message.prototype.set_request_body_from_bytes =
+ function (contentType, bytes) {
+ this.set_request(
+ contentType,
+ Soup.MemoryUse.COPY,
+ new TextDecoder().decode(bytes.get_data()));
+ };
+
+ Soup.Session.prototype.send_and_read_async =
+ function (message, prio, cancellable, callback) {
+ this.queue_message(message, () => callback(this, message));
+ };
+ Soup.Session.prototype.send_and_read_finish =
+ function (message) {
+ if (message.status_code !== Soup.KnownStatusCode.OK)
+ return null;
+
+ return message.response_body.flatten().get_as_bytes();
+ };
+}
+
+function _makeEaseCallback(params, cleanup) {
+ let onComplete = params.onComplete;
+ delete params.onComplete;
+
+ let onStopped = params.onStopped;
+ delete params.onStopped;
+
+ return isFinished => {
+ cleanup();
+
+ if (onStopped)
+ onStopped(isFinished);
+ if (onComplete && isFinished)
+ onComplete();
+ };
+}
+
+function _getPropertyTarget(actor, propName) {
+ if (!propName.startsWith('@'))
+ return [actor, propName];
+
+ let [type, name, prop] = propName.split('.');
+ switch (type) {
+ case '@layout':
+ return [actor.layout_manager, name];
+ case '@actions':
+ return [actor.get_action(name), prop];
+ case '@constraints':
+ return [actor.get_constraint(name), prop];
+ case '@content':
+ return [actor.content, name];
+ case '@effects':
+ return [actor.get_effect(name), prop];
+ }
+
+ throw new Error(`Invalid property name ${propName}`);
+}
+
+function _easeActor(actor, params) {
+ actor.save_easing_state();
+
+ if (params.duration != undefined)
+ actor.set_easing_duration(params.duration);
+ delete params.duration;
+
+ if (params.delay != undefined)
+ actor.set_easing_delay(params.delay);
+ delete params.delay;
+
+ let repeatCount = 0;
+ if (params.repeatCount != undefined)
+ repeatCount = params.repeatCount;
+ delete params.repeatCount;
+
+ let autoReverse = false;
+ if (params.autoReverse != undefined)
+ autoReverse = params.autoReverse;
+ delete params.autoReverse;
+
+ // repeatCount doesn't include the initial iteration
+ const numIterations = repeatCount + 1;
+ // whether the transition should finish where it started
+ const isReversed = autoReverse && numIterations % 2 === 0;
+
+ if (params.mode != undefined)
+ actor.set_easing_mode(params.mode);
+ delete params.mode;
+
+ const prepare = () => {
+ Meta.disable_unredirect_for_display(global.display);
+ global.begin_work();
+ };
+ const cleanup = () => {
+ Meta.enable_unredirect_for_display(global.display);
+ global.end_work();
+ };
+ let callback = _makeEaseCallback(params, cleanup);
+
+ // cancel overwritten transitions
+ let animatedProps = Object.keys(params).map(p => p.replace('_', '-', 'g'));
+ animatedProps.forEach(p => actor.remove_transition(p));
+
+ if (actor.get_easing_duration() > 0 || !isReversed)
+ actor.set(params);
+ actor.restore_easing_state();
+
+ const transitions = animatedProps
+ .map(p => actor.get_transition(p))
+ .filter(t => t !== null);
+
+ transitions.forEach(t => t.set({ repeatCount, autoReverse }));
+
+ const [transition] = transitions;
+
+ if (transition && transition.delay)
+ transition.connect('started', () => prepare());
+ else
+ prepare();
+
+ if (transition)
+ transition.connect('stopped', (t, finished) => callback(finished));
+ else
+ callback(true);
+}
+
+function _easeActorProperty(actor, propName, target, params) {
+ // Avoid pointless difference with ease()
+ if (params.mode)
+ params.progress_mode = params.mode;
+ delete params.mode;
+
+ if (params.duration)
+ params.duration = adjustAnimationTime(params.duration);
+ let duration = Math.floor(params.duration || 0);
+
+ let repeatCount = 0;
+ if (params.repeatCount != undefined)
+ repeatCount = params.repeatCount;
+ delete params.repeatCount;
+
+ let autoReverse = false;
+ if (params.autoReverse != undefined)
+ autoReverse = params.autoReverse;
+ delete params.autoReverse;
+
+ // repeatCount doesn't include the initial iteration
+ const numIterations = repeatCount + 1;
+ // whether the transition should finish where it started
+ const isReversed = autoReverse && numIterations % 2 === 0;
+
+ // Copy Clutter's behavior for implicit animations, see
+ // should_skip_implicit_transition()
+ if (actor instanceof Clutter.Actor && !actor.mapped)
+ duration = 0;
+
+ const prepare = () => {
+ Meta.disable_unredirect_for_display(global.display);
+ global.begin_work();
+ };
+ const cleanup = () => {
+ Meta.enable_unredirect_for_display(global.display);
+ global.end_work();
+ };
+ let callback = _makeEaseCallback(params, cleanup);
+
+ // cancel overwritten transition
+ actor.remove_transition(propName);
+
+ if (duration == 0) {
+ let [obj, prop] = _getPropertyTarget(actor, propName);
+
+ if (!isReversed)
+ obj[prop] = target;
+
+ prepare();
+ callback(true);
+
+ return;
+ }
+
+ let pspec = actor.find_property(propName);
+ let transition = new Clutter.PropertyTransition(Object.assign({
+ property_name: propName,
+ interval: new Clutter.Interval({ value_type: pspec.value_type }),
+ remove_on_complete: true,
+ repeat_count: repeatCount,
+ auto_reverse: autoReverse,
+ }, params));
+ actor.add_transition(propName, transition);
+
+ transition.set_to(target);
+
+ if (transition.delay)
+ transition.connect('started', () => prepare());
+ else
+ prepare();
+
+ transition.connect('stopped', (t, finished) => callback(finished));
+}
+
+function init() {
+ // Add some bindings to the global JS namespace
+ globalThis.global = Shell.Global.get();
+
+ globalThis._ = Gettext.gettext;
+ globalThis.C_ = Gettext.pgettext;
+ globalThis.ngettext = Gettext.ngettext;
+ globalThis.N_ = s => s;
+
+ GObject.gtypeNameBasedOnJSPath = true;
+
+ GObject.Object.prototype.connectObject = function (...args) {
+ SignalTracker.connectObject(this, ...args);
+ };
+ GObject.Object.prototype.connect_object = function (...args) {
+ SignalTracker.connectObject(this, ...args);
+ };
+ GObject.Object.prototype.disconnectObject = function (...args) {
+ SignalTracker.disconnectObject(this, ...args);
+ };
+ GObject.Object.prototype.disconnect_object = function (...args) {
+ SignalTracker.disconnectObject(this, ...args);
+ };
+
+ SignalTracker.registerDestroyableType(Clutter.Actor);
+
+ // Miscellaneous monkeypatching
+ _patchContainerClass(St.BoxLayout);
+
+ _patchLayoutClass(Clutter.GridLayout, {
+ row_spacing: 'spacing-rows',
+ column_spacing: 'spacing-columns',
+ });
+ _patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
+
+ let origSetEasingDuration = Clutter.Actor.prototype.set_easing_duration;
+ Clutter.Actor.prototype.set_easing_duration = function (msecs) {
+ origSetEasingDuration.call(this, adjustAnimationTime(msecs));
+ };
+ let origSetEasingDelay = Clutter.Actor.prototype.set_easing_delay;
+ Clutter.Actor.prototype.set_easing_delay = function (msecs) {
+ origSetEasingDelay.call(this, adjustAnimationTime(msecs));
+ };
+
+ Clutter.Actor.prototype.ease = function (props) {
+ _easeActor(this, props);
+ };
+ Clutter.Actor.prototype.ease_property = function (propName, target, params) {
+ _easeActorProperty(this, propName, target, params);
+ };
+ St.Adjustment.prototype.ease = function (target, params) {
+ // we're not an actor of course, but we implement the same
+ // transition API as Clutter.Actor, so this works anyway
+ _easeActorProperty(this, 'value', target, params);
+ };
+
+ Clutter.Actor.prototype[Symbol.iterator] = function* () {
+ for (let c = this.get_first_child(); c; c = c.get_next_sibling())
+ yield c;
+ };
+
+ Clutter.Actor.prototype.toString = function () {
+ return St.describe_actor(this);
+ };
+ // Deprecation warning for former JS classes turned into an actor subclass
+ Object.defineProperty(Clutter.Actor.prototype, 'actor', {
+ get() {
+ let klass = this.constructor.name;
+ let { stack } = new Error();
+ log(`Usage of object.actor is deprecated for ${klass}\n${stack}`);
+ return this;
+ },
+ });
+
+ Gio.File.prototype.touch_async = function (callback) {
+ Shell.util_touch_file_async(this, callback);
+ };
+ Gio.File.prototype.touch_finish = function (result) {
+ return Shell.util_touch_file_finish(this, result);
+ };
+
+ St.set_slow_down_factor = function (factor) {
+ let { stack } = new Error();
+ log(`St.set_slow_down_factor() is deprecated, use St.Settings.slow_down_factor\n${stack}`);
+ St.Settings.get().slow_down_factor = factor;
+ };
+
+ let origToString = Object.prototype.toString;
+ Object.prototype.toString = function () {
+ let base = origToString.call(this);
+ try {
+ if ('actor' in this && this.actor instanceof Clutter.Actor)
+ return base.replace(/\]$/, ` delegate for ${this.actor.toString().substring(1)}`);
+ else
+ return base;
+ } catch (e) {
+ return base;
+ }
+ };
+
+ // Override to clear our own timezone cache as well
+ const origClearDateCaches = System.clearDateCaches;
+ System.clearDateCaches = function () {
+ _localTimeZone = null;
+ origClearDateCaches();
+ };
+
+ // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
+ Date.prototype.toLocaleFormat = function (format) {
+ if (_localTimeZone === null)
+ _localTimeZone = GLib.TimeZone.new_local();
+
+ let dt = GLib.DateTime.new(_localTimeZone,
+ this.getFullYear(),
+ this.getMonth() + 1,
+ this.getDate(),
+ this.getHours(),
+ this.getMinutes(),
+ this.getSeconds());
+ return dt?.format(format) ?? '';
+ };
+
+ let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
+ if (slowdownEnv) {
+ let factor = parseFloat(slowdownEnv);
+ if (!isNaN(factor) && factor > 0.0)
+ St.Settings.get().slow_down_factor = factor;
+ }
+
+ // OK, now things are initialized enough that we can import shell JS
+ const Format = imports.format;
+
+ String.prototype.format = Format.format;
+
+ Math.clamp = function (x, lower, upper) {
+ return Math.min(Math.max(x, lower), upper);
+ };
+}
+
+// adjustAnimationTime:
+// @msecs: time in milliseconds
+//
+// Adjust @msecs to account for St's enable-animations
+// and slow-down-factor settings
+function adjustAnimationTime(msecs) {
+ let settings = St.Settings.get();
+
+ if (!settings.enable_animations)
+ return Math.min(msecs, 1);
+ return settings.slow_down_factor * msecs;
+}
+
diff --git a/js/ui/extensionDownloader.js b/js/ui/extensionDownloader.js
new file mode 100644
index 0000000..94ba8fa
--- /dev/null
+++ b/js/ui/extensionDownloader.js
@@ -0,0 +1,282 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported init, installExtension, uninstallExtension, checkForUpdates */
+
+const { Clutter, Gio, GLib, GObject, Soup } = imports.gi;
+
+const Config = imports.misc.config;
+const Dialog = imports.ui.dialog;
+const ExtensionUtils = imports.misc.extensionUtils;
+const FileUtils = imports.misc.fileUtils;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+
+Gio._promisify(Soup.Session.prototype, 'send_and_read_async');
+Gio._promisify(Gio.OutputStream.prototype, 'write_bytes_async');
+Gio._promisify(Gio.IOStream.prototype, 'close_async');
+Gio._promisify(Gio.Subprocess.prototype, 'wait_check_async');
+
+var REPOSITORY_URL_DOWNLOAD = 'https://extensions.gnome.org/download-extension/%s.shell-extension.zip';
+var REPOSITORY_URL_INFO = 'https://extensions.gnome.org/extension-info/';
+var REPOSITORY_URL_UPDATE = 'https://extensions.gnome.org/update-info/';
+
+let _httpSession;
+
+/**
+ * @param {string} uuid - extension uuid
+ * @param {Gio.DBusMethodInvocation} invocation - the caller
+ * @returns {void}
+ */
+async function installExtension(uuid, invocation) {
+ const params = {
+ uuid,
+ shell_version: Config.PACKAGE_VERSION,
+ };
+
+ const message = Soup.Message.new_from_encoded_form('GET',
+ REPOSITORY_URL_INFO,
+ Soup.form_encode_hash(params));
+
+ let info;
+ try {
+ const bytes = await _httpSession.send_and_read_async(
+ message,
+ GLib.PRIORITY_DEFAULT,
+ null);
+ checkResponse(message);
+ const decoder = new TextDecoder();
+ info = JSON.parse(decoder.decode(bytes.get_data()));
+ } catch (e) {
+ Main.extensionManager.logExtensionError(uuid, e);
+ invocation.return_dbus_error(
+ 'org.gnome.Shell.ExtensionError', e.message);
+ return;
+ }
+
+ const dialog = new InstallExtensionDialog(uuid, info, invocation);
+ dialog.open(global.get_current_time());
+}
+
+function uninstallExtension(uuid) {
+ let extension = Main.extensionManager.lookup(uuid);
+ if (!extension)
+ return false;
+
+ // Don't try to uninstall system extensions
+ if (extension.type !== ExtensionUtils.ExtensionType.PER_USER)
+ return false;
+
+ if (!Main.extensionManager.unloadExtension(extension))
+ return false;
+
+ FileUtils.recursivelyDeleteDir(extension.dir, true);
+
+ try {
+ const updatesDir = Gio.File.new_for_path(GLib.build_filenamev(
+ [global.userdatadir, 'extension-updates', extension.uuid]));
+ FileUtils.recursivelyDeleteDir(updatesDir, true);
+ } catch (e) {
+ // not an error
+ }
+
+ return true;
+}
+
+/**
+ * Check return status of reponse
+ *
+ * @param {Soup.Message} message - an http response
+ * @returns {void}
+ * @throws
+ */
+function checkResponse(message) {
+ const { statusCode } = message;
+ const phrase = Soup.Status.get_phrase(statusCode);
+ if (statusCode !== Soup.Status.OK)
+ throw new Error(`Unexpected response: ${phrase}`);
+}
+
+/**
+ * @param {GLib.Bytes} bytes - archive data
+ * @param {Gio.File} dir - target directory
+ * @returns {void}
+ */
+async function extractExtensionArchive(bytes, dir) {
+ if (!dir.query_exists(null))
+ dir.make_directory_with_parents(null);
+
+ const [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
+ await stream.output_stream.write_bytes_async(bytes,
+ GLib.PRIORITY_DEFAULT, null);
+ stream.close_async(GLib.PRIORITY_DEFAULT, null);
+
+ const unzip = Gio.Subprocess.new(
+ ['unzip', '-uod', dir.get_path(), '--', file.get_path()],
+ Gio.SubprocessFlags.NONE);
+ await unzip.wait_check_async(null);
+}
+
+/**
+ * @param {string} uuid - extension uuid
+ * @returns {void}
+ */
+async function downloadExtensionUpdate(uuid) {
+ if (!Main.extensionManager.updatesSupported)
+ return;
+
+ const dir = Gio.File.new_for_path(
+ GLib.build_filenamev([global.userdatadir, 'extension-updates', uuid]));
+
+ const params = { shell_version: Config.PACKAGE_VERSION };
+ const message = Soup.Message.new_from_encoded_form('GET',
+ REPOSITORY_URL_DOWNLOAD.format(uuid),
+ Soup.form_encode_hash(params));
+
+ try {
+ const bytes = await _httpSession.send_and_read_async(
+ message,
+ GLib.PRIORITY_DEFAULT,
+ null);
+ checkResponse(message);
+
+ await extractExtensionArchive(bytes, dir);
+ Main.extensionManager.notifyExtensionUpdate(uuid);
+ } catch (e) {
+ log(`Error while downloading update for extension ${uuid}: (${e.message})`);
+ }
+}
+
+/**
+ * Check extensions.gnome.org for updates
+ *
+ * @returns {void}
+ */
+async function checkForUpdates() {
+ if (!Main.extensionManager.updatesSupported)
+ return;
+
+ let metadatas = {};
+ Main.extensionManager.getUuids().forEach(uuid => {
+ let extension = Main.extensionManager.lookup(uuid);
+ if (extension.type !== ExtensionUtils.ExtensionType.PER_USER)
+ return;
+ if (extension.hasUpdate)
+ return;
+ metadatas[uuid] = {
+ version: extension.metadata.version,
+ };
+ });
+
+ if (Object.keys(metadatas).length === 0)
+ return; // nothing to update
+
+ const versionCheck = global.settings.get_boolean(
+ 'disable-extension-version-validation');
+ const params = {
+ shell_version: Config.PACKAGE_VERSION,
+ disable_version_validation: `${versionCheck}`,
+ };
+ const requestBody = new GLib.Bytes(JSON.stringify(metadatas));
+
+ const message = Soup.Message.new('POST',
+ `${REPOSITORY_URL_UPDATE}?${Soup.form_encode_hash(params)}`);
+ message.set_request_body_from_bytes('application/json', requestBody);
+
+ let json;
+ try {
+ const bytes = await _httpSession.send_and_read_async(
+ message,
+ GLib.PRIORITY_DEFAULT,
+ null);
+ checkResponse(message);
+ json = new TextDecoder().decode(bytes.get_data());
+ } catch (e) {
+ log(`Update check failed: ${e.message}`);
+ return;
+ }
+
+ const operations = JSON.parse(json);
+ const updates = [];
+ for (const uuid in operations) {
+ const operation = operations[uuid];
+ if (operation === 'upgrade' || operation === 'downgrade')
+ updates.push(uuid);
+ }
+
+ try {
+ await Promise.allSettled(
+ updates.map(uuid => downloadExtensionUpdate(uuid)));
+ } catch (e) {
+ log(`Some extension updates failed to download: ${e.message}`);
+ }
+}
+
+var InstallExtensionDialog = GObject.registerClass(
+class InstallExtensionDialog extends ModalDialog.ModalDialog {
+ _init(uuid, info, invocation) {
+ super._init({ styleClass: 'extension-dialog' });
+
+ this._uuid = uuid;
+ this._info = info;
+ this._invocation = invocation;
+
+ this.setButtons([{
+ label: _('Cancel'),
+ action: this._onCancelButtonPressed.bind(this),
+ key: Clutter.KEY_Escape,
+ }, {
+ label: _('Install'),
+ action: this._onInstallButtonPressed.bind(this),
+ default: true,
+ }]);
+
+ let content = new Dialog.MessageDialogContent({
+ title: _('Install Extension'),
+ description: _('Download and install “%s” from extensions.gnome.org?').format(info.name),
+ });
+
+ this.contentLayout.add(content);
+ }
+
+ _onCancelButtonPressed() {
+ this.close();
+ this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
+ }
+
+ async _onInstallButtonPressed() {
+ this.close();
+
+ const params = { shell_version: Config.PACKAGE_VERSION };
+ const message = Soup.Message.new_from_encoded_form('GET',
+ REPOSITORY_URL_DOWNLOAD.format(this._uuid),
+ Soup.form_encode_hash(params));
+
+ const dir = Gio.File.new_for_path(
+ GLib.build_filenamev([global.userdatadir, 'extensions', this._uuid]));
+
+ try {
+ const bytes = await _httpSession.send_and_read_async(
+ message,
+ GLib.PRIORITY_DEFAULT,
+ null);
+ checkResponse(message);
+
+ await extractExtensionArchive(bytes, dir);
+
+ const extension = Main.extensionManager.createExtensionObject(
+ this._uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
+ Main.extensionManager.loadExtension(extension);
+ if (!Main.extensionManager.enableExtension(this._uuid))
+ throw new Error(`Cannot enable ${this._uuid}`);
+
+ this._invocation.return_value(new GLib.Variant('(s)', ['successful']));
+ } catch (e) {
+ log(`Error while installing ${this._uuid}: ${e.message}`);
+ this._invocation.return_dbus_error(
+ 'org.gnome.Shell.ExtensionError', e.message);
+ }
+ }
+});
+
+function init() {
+ _httpSession = new Soup.Session();
+}
diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js
new file mode 100644
index 0000000..c21cc7c
--- /dev/null
+++ b/js/ui/extensionSystem.js
@@ -0,0 +1,687 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported init connect disconnect ExtensionManager */
+
+const { GLib, Gio, GObject, Shell, St } = imports.gi;
+const Signals = imports.misc.signals;
+
+const ExtensionDownloader = imports.ui.extensionDownloader;
+const ExtensionUtils = imports.misc.extensionUtils;
+const FileUtils = imports.misc.fileUtils;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+
+const { ExtensionState, ExtensionType } = ExtensionUtils;
+
+const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
+const DISABLED_EXTENSIONS_KEY = 'disabled-extensions';
+const DISABLE_USER_EXTENSIONS_KEY = 'disable-user-extensions';
+const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation';
+
+const UPDATE_CHECK_TIMEOUT = 24 * 60 * 60; // 1 day in seconds
+
+var ExtensionManager = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._initialized = false;
+ this._updateNotified = false;
+
+ this._extensions = new Map();
+ this._unloadedExtensions = new Map();
+ this._enabledExtensions = [];
+ this._extensionOrder = [];
+ this._checkVersion = false;
+
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ }
+
+ init() {
+ // The following file should exist for a period of time when extensions
+ // are enabled after start. If it exists, then the systemd unit will
+ // disable extensions should gnome-shell crash.
+ // Should the file already exist from a previous login, then this is OK.
+ let disableFilename = GLib.build_filenamev([GLib.get_user_runtime_dir(), 'gnome-shell-disable-extensions']);
+ let disableFile = Gio.File.new_for_path(disableFilename);
+ try {
+ disableFile.create(Gio.FileCreateFlags.REPLACE_DESTINATION, null);
+ } catch (e) {
+ log(`Failed to create file ${disableFilename}: ${e.message}`);
+ }
+
+ GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 60, () => {
+ disableFile.delete(null);
+ return GLib.SOURCE_REMOVE;
+ });
+
+ this._installExtensionUpdates();
+ this._sessionUpdated();
+
+ GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, UPDATE_CHECK_TIMEOUT, () => {
+ ExtensionDownloader.checkForUpdates();
+ return GLib.SOURCE_CONTINUE;
+ });
+ ExtensionDownloader.checkForUpdates();
+ }
+
+ get updatesSupported() {
+ const appSys = Shell.AppSystem.get_default();
+ return (appSys.lookup_app('org.gnome.Extensions.desktop') !== null) ||
+ (appSys.lookup_app('com.mattjakeman.ExtensionManager.desktop') !== null);
+ }
+
+ lookup(uuid) {
+ return this._extensions.get(uuid);
+ }
+
+ getUuids() {
+ return [...this._extensions.keys()];
+ }
+
+ _extensionSupportsSessionMode(uuid) {
+ const extension = this.lookup(uuid);
+
+ if (!extension)
+ return false;
+
+ if (extension.sessionModes.includes(Main.sessionMode.currentMode))
+ return true;
+
+ if (extension.sessionModes.includes(Main.sessionMode.parentMode))
+ return true;
+
+ return false;
+ }
+
+ _callExtensionDisable(uuid) {
+ let extension = this.lookup(uuid);
+ if (!extension)
+ return;
+
+ if (extension.state != ExtensionState.ENABLED)
+ return;
+
+ // "Rebase" the extension order by disabling and then enabling extensions
+ // in order to help prevent conflicts.
+
+ // Example:
+ // order = [A, B, C, D, E]
+ // user disables C
+ // this should: disable E, disable D, disable C, enable D, enable E
+
+ let orderIdx = this._extensionOrder.indexOf(uuid);
+ let order = this._extensionOrder.slice(orderIdx + 1);
+ let orderReversed = order.slice().reverse();
+
+ for (let i = 0; i < orderReversed.length; i++) {
+ let otherUuid = orderReversed[i];
+ try {
+ this.lookup(otherUuid).stateObj.disable();
+ } catch (e) {
+ this.logExtensionError(otherUuid, e);
+ }
+ }
+
+ try {
+ extension.stateObj.disable();
+ } catch (e) {
+ this.logExtensionError(uuid, e);
+ }
+
+ if (extension.stylesheet) {
+ let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
+ theme.unload_stylesheet(extension.stylesheet);
+ delete extension.stylesheet;
+ }
+
+ for (let i = 0; i < order.length; i++) {
+ let otherUuid = order[i];
+ try {
+ this.lookup(otherUuid).stateObj.enable();
+ } catch (e) {
+ this.logExtensionError(otherUuid, e);
+ }
+ }
+
+ this._extensionOrder.splice(orderIdx, 1);
+
+ if (extension.state != ExtensionState.ERROR) {
+ extension.state = ExtensionState.DISABLED;
+ this.emit('extension-state-changed', extension);
+ }
+ }
+
+ _callExtensionEnable(uuid) {
+ if (!this._extensionSupportsSessionMode(uuid))
+ return;
+
+ let extension = this.lookup(uuid);
+ if (!extension)
+ return;
+
+ if (extension.state == ExtensionState.INITIALIZED)
+ this._callExtensionInit(uuid);
+
+ if (extension.state != ExtensionState.DISABLED)
+ return;
+
+ let stylesheetNames = [`${global.session_mode}.css`, 'stylesheet.css'];
+ let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
+ for (let i = 0; i < stylesheetNames.length; i++) {
+ try {
+ let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
+ theme.load_stylesheet(stylesheetFile);
+ extension.stylesheet = stylesheetFile;
+ break;
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
+ continue; // not an error
+ this.logExtensionError(uuid, e);
+ return;
+ }
+ }
+
+ try {
+ extension.stateObj.enable();
+ extension.state = ExtensionState.ENABLED;
+ this._extensionOrder.push(uuid);
+ this.emit('extension-state-changed', extension);
+ } catch (e) {
+ if (extension.stylesheet) {
+ theme.unload_stylesheet(extension.stylesheet);
+ delete extension.stylesheet;
+ }
+ this.logExtensionError(uuid, e);
+ }
+ }
+
+ enableExtension(uuid) {
+ if (!this._extensions.has(uuid))
+ return false;
+
+ let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
+ let disabledExtensions = global.settings.get_strv(DISABLED_EXTENSIONS_KEY);
+
+ if (disabledExtensions.includes(uuid)) {
+ disabledExtensions = disabledExtensions.filter(item => item !== uuid);
+ global.settings.set_strv(DISABLED_EXTENSIONS_KEY, disabledExtensions);
+ }
+
+ if (!enabledExtensions.includes(uuid)) {
+ enabledExtensions.push(uuid);
+ global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
+ }
+
+ return true;
+ }
+
+ disableExtension(uuid) {
+ if (!this._extensions.has(uuid))
+ return false;
+
+ let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
+ let disabledExtensions = global.settings.get_strv(DISABLED_EXTENSIONS_KEY);
+
+ if (enabledExtensions.includes(uuid)) {
+ enabledExtensions = enabledExtensions.filter(item => item !== uuid);
+ global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
+ }
+
+ if (!disabledExtensions.includes(uuid)) {
+ disabledExtensions.push(uuid);
+ global.settings.set_strv(DISABLED_EXTENSIONS_KEY, disabledExtensions);
+ }
+
+ return true;
+ }
+
+ openExtensionPrefs(uuid, parentWindow, options) {
+ const extension = this.lookup(uuid);
+ if (!extension || !extension.hasPrefs)
+ return false;
+
+ Gio.DBus.session.call(
+ 'org.gnome.Shell.Extensions',
+ '/org/gnome/Shell/Extensions',
+ 'org.gnome.Shell.Extensions',
+ 'OpenExtensionPrefs',
+ new GLib.Variant('(ssa{sv})', [uuid, parentWindow, options]),
+ null,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ null);
+ return true;
+ }
+
+ notifyExtensionUpdate(uuid) {
+ let extension = this.lookup(uuid);
+ if (!extension)
+ return;
+
+ extension.hasUpdate = true;
+ this.emit('extension-state-changed', extension);
+
+ if (!this._updateNotified) {
+ this._updateNotified = true;
+
+ let source = new ExtensionUpdateSource();
+ Main.messageTray.add(source);
+
+ let notification = new MessageTray.Notification(source,
+ _('Extension Updates Available'),
+ _('Extension updates are ready to be installed.'));
+ notification.connect('activated',
+ () => source.open());
+ source.showNotification(notification);
+ }
+ }
+
+ logExtensionError(uuid, error) {
+ let extension = this.lookup(uuid);
+ if (!extension)
+ return;
+
+ const message = error instanceof Error
+ ? error.message : error.toString();
+
+ extension.error = message;
+ extension.state = ExtensionState.ERROR;
+ if (!extension.errors)
+ extension.errors = [];
+ extension.errors.push(message);
+
+ logError(error, `Extension ${uuid}`);
+ this._updateCanChange(extension);
+ this.emit('extension-state-changed', extension);
+ }
+
+ createExtensionObject(uuid, dir, type) {
+ let metadataFile = dir.get_child('metadata.json');
+ if (!metadataFile.query_exists(null))
+ throw new Error('Missing metadata.json');
+
+ let metadataContents, success_;
+ try {
+ [success_, metadataContents] = metadataFile.load_contents(null);
+ metadataContents = new TextDecoder().decode(metadataContents);
+ } catch (e) {
+ throw new Error(`Failed to load metadata.json: ${e}`);
+ }
+ let meta;
+ try {
+ meta = JSON.parse(metadataContents);
+ } catch (e) {
+ throw new Error(`Failed to parse metadata.json: ${e}`);
+ }
+
+ const requiredProperties = [{
+ prop: 'uuid',
+ typeName: 'string',
+ }, {
+ prop: 'name',
+ typeName: 'string',
+ }, {
+ prop: 'description',
+ typeName: 'string',
+ }, {
+ prop: 'shell-version',
+ typeName: 'string array',
+ typeCheck: v => Array.isArray(v) && v.length > 0 && v.every(e => typeof e === 'string'),
+ }];
+ for (let i = 0; i < requiredProperties.length; i++) {
+ const {
+ prop, typeName, typeCheck = v => typeof v === typeName,
+ } = requiredProperties[i];
+
+ if (!meta[prop])
+ throw new Error(`missing "${prop}" property in metadata.json`);
+ if (!typeCheck(meta[prop]))
+ throw new Error(`property "${prop}" is not of type ${typeName}`);
+ }
+
+ if (uuid != meta.uuid)
+ throw new Error(`uuid "${meta.uuid}" from metadata.json does not match directory name "${uuid}"`);
+
+ let extension = {
+ metadata: meta,
+ uuid: meta.uuid,
+ type,
+ dir,
+ path: dir.get_path(),
+ error: '',
+ hasPrefs: dir.get_child('prefs.js').query_exists(null),
+ hasUpdate: false,
+ canChange: false,
+ sessionModes: meta['session-modes'] ? meta['session-modes'] : ['user'],
+ };
+ this._extensions.set(uuid, extension);
+
+ return extension;
+ }
+
+ _canLoad(extension) {
+ if (!this._unloadedExtensions.has(extension.uuid))
+ return true;
+
+ const version = this._unloadedExtensions.get(extension.uuid);
+ return extension.metadata.version === version;
+ }
+
+ loadExtension(extension) {
+ // Default to error, we set success as the last step
+ extension.state = ExtensionState.ERROR;
+
+ if (this._checkVersion && ExtensionUtils.isOutOfDate(extension)) {
+ extension.state = ExtensionState.OUT_OF_DATE;
+ } else if (!this._canLoad(extension)) {
+ this.logExtensionError(extension.uuid, new Error(
+ 'A different version was loaded previously. You need to log out for changes to take effect.'));
+ } else {
+ let enabled = this._enabledExtensions.includes(extension.uuid) &&
+ this._extensionSupportsSessionMode(extension.uuid);
+ if (enabled) {
+ if (!this._callExtensionInit(extension.uuid))
+ return;
+ if (extension.state == ExtensionState.DISABLED)
+ this._callExtensionEnable(extension.uuid);
+ } else {
+ extension.state = ExtensionState.INITIALIZED;
+ }
+
+ this._unloadedExtensions.delete(extension.uuid);
+ }
+
+ this._updateCanChange(extension);
+ this.emit('extension-state-changed', extension);
+ }
+
+ unloadExtension(extension) {
+ const { uuid, type } = extension;
+
+ // Try to disable it -- if it's ERROR'd, we can't guarantee that,
+ // but it will be removed on next reboot, and hopefully nothing
+ // broke too much.
+ this._callExtensionDisable(uuid);
+
+ extension.state = ExtensionState.UNINSTALLED;
+ this.emit('extension-state-changed', extension);
+
+ // If we did install an importer, it is now cached and it's
+ // impossible to load a different version
+ if (type === ExtensionType.PER_USER && extension.imports)
+ this._unloadedExtensions.set(uuid, extension.metadata.version);
+
+ this._extensions.delete(uuid);
+ return true;
+ }
+
+ reloadExtension(oldExtension) {
+ // Grab the things we'll need to pass to createExtensionObject
+ // to reload it.
+ let { uuid, dir, type } = oldExtension;
+
+ // Then unload the old extension.
+ this.unloadExtension(oldExtension);
+
+ // Now, recreate the extension and load it.
+ let newExtension;
+ try {
+ newExtension = this.createExtensionObject(uuid, dir, type);
+ } catch (e) {
+ this.logExtensionError(uuid, e);
+ return;
+ }
+
+ this.loadExtension(newExtension);
+ }
+
+ _callExtensionInit(uuid) {
+ if (!this._extensionSupportsSessionMode(uuid))
+ return false;
+
+ let extension = this.lookup(uuid);
+ if (!extension)
+ throw new Error("Extension was not properly created. Call createExtensionObject first");
+
+ let dir = extension.dir;
+ let extensionJs = dir.get_child('extension.js');
+ if (!extensionJs.query_exists(null)) {
+ this.logExtensionError(uuid, new Error('Missing extension.js'));
+ return false;
+ }
+
+ let extensionModule;
+ let extensionState = null;
+
+ ExtensionUtils.installImporter(extension);
+ try {
+ extensionModule = extension.imports.extension;
+ } catch (e) {
+ this.logExtensionError(uuid, e);
+ return false;
+ }
+
+ if (extensionModule.init) {
+ try {
+ extensionState = extensionModule.init(extension);
+ } catch (e) {
+ this.logExtensionError(uuid, e);
+ return false;
+ }
+ }
+
+ if (!extensionState)
+ extensionState = extensionModule;
+ extension.stateObj = extensionState;
+
+ extension.state = ExtensionState.DISABLED;
+ this.emit('extension-loaded', uuid);
+ return true;
+ }
+
+ _getModeExtensions() {
+ if (Array.isArray(Main.sessionMode.enabledExtensions))
+ return Main.sessionMode.enabledExtensions;
+ return [];
+ }
+
+ _updateCanChange(extension) {
+ let hasError =
+ extension.state == ExtensionState.ERROR ||
+ extension.state == ExtensionState.OUT_OF_DATE;
+
+ let isMode = this._getModeExtensions().includes(extension.uuid);
+ let modeOnly = global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY);
+
+ let changeKey = isMode
+ ? DISABLE_USER_EXTENSIONS_KEY
+ : ENABLED_EXTENSIONS_KEY;
+
+ extension.canChange =
+ !hasError &&
+ global.settings.is_writable(changeKey) &&
+ (isMode || !modeOnly);
+ }
+
+ _getEnabledExtensions() {
+ let extensions = this._getModeExtensions();
+
+ if (!global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY))
+ extensions = extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY));
+
+ // filter out 'disabled-extensions' which takes precedence
+ let disabledExtensions = global.settings.get_strv(DISABLED_EXTENSIONS_KEY);
+ return extensions.filter(item => !disabledExtensions.includes(item));
+ }
+
+ _onUserExtensionsEnabledChanged() {
+ this._onEnabledExtensionsChanged();
+ this._onSettingsWritableChanged();
+ }
+
+ _onEnabledExtensionsChanged() {
+ let newEnabledExtensions = this._getEnabledExtensions();
+
+ // Find and enable all the newly enabled extensions: UUIDs found in the
+ // new setting, but not in the old one.
+ newEnabledExtensions
+ .filter(uuid => !this._enabledExtensions.includes(uuid) &&
+ this._extensionSupportsSessionMode(uuid))
+ .forEach(uuid => this._callExtensionEnable(uuid));
+
+ // Find and disable all the newly disabled extensions: UUIDs found in the
+ // old setting, but not in the new one.
+ this._extensionOrder
+ .filter(uuid => !newEnabledExtensions.includes(uuid) ||
+ !this._extensionSupportsSessionMode(uuid))
+ .reverse().forEach(uuid => this._callExtensionDisable(uuid));
+
+ this._enabledExtensions = newEnabledExtensions;
+ }
+
+ _onSettingsWritableChanged() {
+ for (let extension of this._extensions.values()) {
+ this._updateCanChange(extension);
+ this.emit('extension-state-changed', extension);
+ }
+ }
+
+ _onVersionValidationChanged() {
+ const checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY);
+ if (checkVersion === this._checkVersion)
+ return;
+
+ this._checkVersion = checkVersion;
+
+ // Disabling extensions modifies the order array, so use a copy
+ let extensionOrder = this._extensionOrder.slice();
+
+ // Disable enabled extensions in the reverse order first to avoid
+ // the "rebasing" done in _callExtensionDisable...
+ extensionOrder.slice().reverse().forEach(uuid => {
+ this._callExtensionDisable(uuid);
+ });
+
+ // ...and then reload and enable extensions in the correct order again.
+ [...this._extensions.values()].sort((a, b) => {
+ return extensionOrder.indexOf(a.uuid) - extensionOrder.indexOf(b.uuid);
+ }).forEach(extension => this.reloadExtension(extension));
+ }
+
+ _installExtensionUpdates() {
+ if (!this.updatesSupported)
+ return;
+
+ FileUtils.collectFromDatadirs('extension-updates', true, (dir, info) => {
+ let fileType = info.get_file_type();
+ if (fileType !== Gio.FileType.DIRECTORY)
+ return;
+ let uuid = info.get_name();
+ let extensionDir = Gio.File.new_for_path(
+ GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
+
+ try {
+ FileUtils.recursivelyDeleteDir(extensionDir, false);
+ FileUtils.recursivelyMoveDir(dir, extensionDir);
+ } catch (e) {
+ log(`Failed to install extension updates for ${uuid}`);
+ } finally {
+ FileUtils.recursivelyDeleteDir(dir, true);
+ }
+ });
+ }
+
+ _loadExtensions() {
+ global.settings.connect(`changed::${ENABLED_EXTENSIONS_KEY}`,
+ this._onEnabledExtensionsChanged.bind(this));
+ global.settings.connect(`changed::${DISABLED_EXTENSIONS_KEY}`,
+ this._onEnabledExtensionsChanged.bind(this));
+ global.settings.connect(`changed::${DISABLE_USER_EXTENSIONS_KEY}`,
+ this._onUserExtensionsEnabledChanged.bind(this));
+ global.settings.connect(`changed::${EXTENSION_DISABLE_VERSION_CHECK_KEY}`,
+ this._onVersionValidationChanged.bind(this));
+ global.settings.connect(`writable-changed::${ENABLED_EXTENSIONS_KEY}`,
+ this._onSettingsWritableChanged.bind(this));
+ global.settings.connect(`writable-changed::${DISABLED_EXTENSIONS_KEY}`,
+ this._onSettingsWritableChanged.bind(this));
+
+ this._onVersionValidationChanged();
+
+ this._enabledExtensions = this._getEnabledExtensions();
+
+ let perUserDir = Gio.File.new_for_path(global.userdatadir);
+ FileUtils.collectFromDatadirs('extensions', true, (dir, info) => {
+ let fileType = info.get_file_type();
+ if (fileType != Gio.FileType.DIRECTORY)
+ return;
+ let uuid = info.get_name();
+ let existing = this.lookup(uuid);
+ if (existing) {
+ log(`Extension ${uuid} already installed in ${existing.path}. ${dir.get_path()} will not be loaded`);
+ return;
+ }
+
+ let extension;
+ let type = dir.has_prefix(perUserDir)
+ ? ExtensionType.PER_USER
+ : ExtensionType.SYSTEM;
+ try {
+ extension = this.createExtensionObject(uuid, dir, type);
+ } catch (e) {
+ logError(e, `Could not load extension ${uuid}`);
+ return;
+ }
+ this.loadExtension(extension);
+ });
+ }
+
+ _enableAllExtensions() {
+ if (!this._initialized) {
+ this._loadExtensions();
+ this._initialized = true;
+ } else {
+ this._enabledExtensions.forEach(uuid => {
+ this._callExtensionEnable(uuid);
+ });
+ }
+ }
+
+ _disableAllExtensions() {
+ if (this._initialized) {
+ this._extensionOrder.slice().reverse().forEach(uuid => {
+ this._callExtensionDisable(uuid);
+ });
+ }
+ }
+
+ _sessionUpdated() {
+ // Take care of added or removed sessionMode extensions
+ this._onEnabledExtensionsChanged();
+ this._enableAllExtensions();
+ }
+};
+
+const ExtensionUpdateSource = GObject.registerClass(
+class ExtensionUpdateSource extends MessageTray.Source {
+ _init() {
+ let appSys = Shell.AppSystem.get_default();
+ this._app = appSys.lookup_app('org.gnome.Extensions.desktop');
+ if (!this._app)
+ this._app = appSys.lookup_app('com.mattjakeman.ExtensionManager.desktop');
+
+ super._init(this._app.get_name());
+ }
+
+ getIcon() {
+ return this._app.app_info.get_icon();
+ }
+
+ _createPolicy() {
+ return new MessageTray.NotificationApplicationPolicy(this._app.id);
+ }
+
+ open() {
+ this._app.activate();
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+});
diff --git a/js/ui/focusCaretTracker.js b/js/ui/focusCaretTracker.js
new file mode 100644
index 0000000..5cfe7a8
--- /dev/null
+++ b/js/ui/focusCaretTracker.js
@@ -0,0 +1,91 @@
+/** -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2012 Inclusive Design Research Centre, OCAD University.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Joseph Scheuhammer <clown@alum.mit.edu>
+ * Contributor:
+ * Magdalen Berns <m.berns@sms.ed.ac.uk>
+ */
+/* exported FocusCaretTracker */
+
+const Atspi = imports.gi.Atspi;
+const Signals = imports.misc.signals;
+
+const CARETMOVED = 'object:text-caret-moved';
+const STATECHANGED = 'object:state-changed';
+
+var FocusCaretTracker = class FocusCaretTracker extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._atspiListener = Atspi.EventListener.new(this._onChanged.bind(this));
+
+ this._atspiInited = false;
+ this._focusListenerRegistered = false;
+ this._caretListenerRegistered = false;
+ }
+
+ _onChanged(event) {
+ if (event.type.indexOf(STATECHANGED) == 0)
+ this.emit('focus-changed', event);
+ else if (event.type == CARETMOVED)
+ this.emit('caret-moved', event);
+ }
+
+ _initAtspi() {
+ if (!this._atspiInited && Atspi.init() == 0) {
+ Atspi.set_timeout(250, 250);
+ this._atspiInited = true;
+ }
+
+ return this._atspiInited;
+ }
+
+ registerFocusListener() {
+ if (!this._initAtspi() || this._focusListenerRegistered)
+ return;
+
+ this._atspiListener.register(`${STATECHANGED}:focused`);
+ this._atspiListener.register(`${STATECHANGED}:selected`);
+ this._focusListenerRegistered = true;
+ }
+
+ registerCaretListener() {
+ if (!this._initAtspi() || this._caretListenerRegistered)
+ return;
+
+ this._atspiListener.register(CARETMOVED);
+ this._caretListenerRegistered = true;
+ }
+
+ deregisterFocusListener() {
+ if (!this._focusListenerRegistered)
+ return;
+
+ this._atspiListener.deregister(`${STATECHANGED}:focused`);
+ this._atspiListener.deregister(`${STATECHANGED}:selected`);
+ this._focusListenerRegistered = false;
+ }
+
+ deregisterCaretListener() {
+ if (!this._caretListenerRegistered)
+ return;
+
+ this._atspiListener.deregister(CARETMOVED);
+ this._caretListenerRegistered = false;
+ }
+};
diff --git a/js/ui/grabHelper.js b/js/ui/grabHelper.js
new file mode 100644
index 0000000..650bec4
--- /dev/null
+++ b/js/ui/grabHelper.js
@@ -0,0 +1,291 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported GrabHelper */
+
+const { Clutter, St } = imports.gi;
+
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+// GrabHelper:
+// @owner: the actor that owns the GrabHelper
+// @params: optional parameters to pass to Main.pushModal()
+//
+// Creates a new GrabHelper object, for dealing with keyboard and pointer grabs
+// associated with a set of actors.
+//
+// Note that the grab can be automatically dropped at any time by the user, and
+// your code just needs to deal with it; you shouldn't adjust behavior directly
+// after you call ungrab(), but instead pass an 'onUngrab' callback when you
+// call grab().
+var GrabHelper = class GrabHelper {
+ constructor(owner, params) {
+ if (!(owner instanceof Clutter.Actor))
+ throw new Error('GrabHelper owner must be a Clutter.Actor');
+
+ this._owner = owner;
+ this._modalParams = params;
+
+ this._grabStack = [];
+
+ this._ignoreUntilRelease = false;
+
+ this._modalCount = 0;
+ }
+
+ _isWithinGrabbedActor(actor) {
+ let currentActor = this.currentGrab.actor;
+ while (actor) {
+ if (actor == currentActor)
+ return true;
+ actor = actor.get_parent();
+ }
+ return false;
+ }
+
+ get currentGrab() {
+ return this._grabStack[this._grabStack.length - 1] || {};
+ }
+
+ get grabbed() {
+ return this._grabStack.length > 0;
+ }
+
+ get grabStack() {
+ return this._grabStack;
+ }
+
+ _findStackIndex(actor) {
+ if (!actor)
+ return -1;
+
+ for (let i = 0; i < this._grabStack.length; i++) {
+ if (this._grabStack[i].actor === actor)
+ return i;
+ }
+ return -1;
+ }
+
+ _actorInGrabStack(actor) {
+ while (actor) {
+ let idx = this._findStackIndex(actor);
+ if (idx >= 0)
+ return idx;
+ actor = actor.get_parent();
+ }
+ return -1;
+ }
+
+ isActorGrabbed(actor) {
+ return this._findStackIndex(actor) >= 0;
+ }
+
+ // grab:
+ // @params: A bunch of parameters, see below
+ //
+ // The general effect of a "grab" is to ensure that the passed in actor
+ // and all actors inside the grab get exclusive control of the mouse and
+ // keyboard, with the grab automatically being dropped if the user tries
+ // to dismiss it. The actor is passed in through @params.actor.
+ //
+ // grab() can be called multiple times, with the scope of the grab being
+ // changed to a different actor every time. A nested grab does not have
+ // to have its grabbed actor inside the parent grab actors.
+ //
+ // Grabs can be automatically dropped if the user tries to dismiss it
+ // in one of two ways: the user clicking outside the currently grabbed
+ // actor, or the user typing the Escape key.
+ //
+ // If the user clicks outside the grabbed actors, and the clicked on
+ // actor is part of a previous grab in the stack, grabs will be popped
+ // until that grab is active. However, the click event will not be
+ // replayed to the actor.
+ //
+ // If the user types the Escape key, one grab from the grab stack will
+ // be popped.
+ //
+ // When a grab is popped by user interacting as described above, if you
+ // pass a callback as @params.onUngrab, it will be called with %true.
+ //
+ // If @params.focus is not null, we'll set the key focus directly
+ // to that actor instead of navigating in @params.actor. This is for
+ // use cases like menus, where we want to grab the menu actor, but keep
+ // focus on the clicked on menu item.
+ grab(params) {
+ params = Params.parse(params, {
+ actor: null,
+ focus: null,
+ onUngrab: null,
+ });
+
+ let focus = global.stage.key_focus;
+ let hadFocus = focus && this._isWithinGrabbedActor(focus);
+ let newFocus = params.actor;
+
+ if (this.isActorGrabbed(params.actor))
+ return true;
+
+ params.savedFocus = focus;
+
+ if (!this._takeModalGrab())
+ return false;
+
+ this._grabStack.push(params);
+
+ if (params.focus) {
+ params.focus.grab_key_focus();
+ } else if (newFocus && hadFocus) {
+ if (!newFocus.navigate_focus(null, St.DirectionType.TAB_FORWARD, false))
+ newFocus.grab_key_focus();
+ }
+
+ return true;
+ }
+
+ grabAsync(params) {
+ return new Promise((resolve, reject) => {
+ params.onUngrab = resolve;
+
+ if (!this.grab(params))
+ reject(new Error('Grab failed'));
+ });
+ }
+
+ _takeModalGrab() {
+ let firstGrab = this._modalCount == 0;
+ if (firstGrab) {
+ let grab = Main.pushModal(this._owner, this._modalParams);
+ if (grab.get_seat_state() !== Clutter.GrabState.ALL) {
+ Main.popModal(grab);
+ return false;
+ }
+
+ this._grab = grab;
+ this._capturedEventId = this._owner.connect('captured-event',
+ (actor, event) => {
+ return this.onCapturedEvent(event);
+ });
+ }
+
+ this._modalCount++;
+ return true;
+ }
+
+ _releaseModalGrab() {
+ this._modalCount--;
+ if (this._modalCount > 0)
+ return;
+
+ this._owner.disconnect(this._capturedEventId);
+ this._ignoreUntilRelease = false;
+
+ Main.popModal(this._grab);
+ this._grab = null;
+ }
+
+ // ignoreRelease:
+ //
+ // Make sure that the next button release event evaluated by the
+ // capture event handler returns false. This is designed for things
+ // like the ComboBoxMenu that go away on press, but need to eat
+ // the next release event.
+ ignoreRelease() {
+ this._ignoreUntilRelease = true;
+ }
+
+ // ungrab:
+ // @params: The parameters for the grab; see below.
+ //
+ // Pops @params.actor from the grab stack, potentially dropping
+ // the grab. If the actor is not on the grab stack, this call is
+ // ignored with no ill effects.
+ //
+ // If the actor is not at the top of the grab stack, grabs are
+ // popped until the grabbed actor is at the top of the grab stack.
+ // The onUngrab callback for every grab is called for every popped
+ // grab with the parameter %false.
+ ungrab(params) {
+ params = Params.parse(params, {
+ actor: this.currentGrab.actor,
+ isUser: false,
+ });
+
+ let grabStackIndex = this._findStackIndex(params.actor);
+ if (grabStackIndex < 0)
+ return;
+
+ let focus = global.stage.key_focus;
+ let hadFocus = focus && this._isWithinGrabbedActor(focus);
+
+ let poppedGrabs = this._grabStack.slice(grabStackIndex);
+ // "Pop" all newly ungrabbed actors off the grab stack
+ // by truncating the array.
+ this._grabStack.length = grabStackIndex;
+
+ for (let i = poppedGrabs.length - 1; i >= 0; i--) {
+ let poppedGrab = poppedGrabs[i];
+
+ if (poppedGrab.onUngrab)
+ poppedGrab.onUngrab(params.isUser);
+
+ this._releaseModalGrab();
+ }
+
+ if (hadFocus) {
+ let poppedGrab = poppedGrabs[0];
+ if (poppedGrab.savedFocus)
+ poppedGrab.savedFocus.grab_key_focus();
+ }
+ }
+
+ onCapturedEvent(event) {
+ let type = event.type();
+
+ if (type == Clutter.EventType.KEY_PRESS &&
+ event.get_key_symbol() == Clutter.KEY_Escape) {
+ this.ungrab({ isUser: true });
+ return Clutter.EVENT_STOP;
+ }
+
+ let motion = type == Clutter.EventType.MOTION;
+ let press = type == Clutter.EventType.BUTTON_PRESS;
+ let release = type == Clutter.EventType.BUTTON_RELEASE;
+ let button = press || release;
+
+ let touchUpdate = type == Clutter.EventType.TOUCH_UPDATE;
+ let touchBegin = type == Clutter.EventType.TOUCH_BEGIN;
+ let touchEnd = type == Clutter.EventType.TOUCH_END;
+ let touch = touchUpdate || touchBegin || touchEnd;
+
+ if (touch && !global.display.is_pointer_emulating_sequence(event.get_event_sequence()))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._ignoreUntilRelease && (motion || release || touch)) {
+ if (release || touchEnd)
+ this._ignoreUntilRelease = false;
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ const targetActor = global.stage.get_event_actor(event);
+
+ if (type === Clutter.EventType.ENTER ||
+ type === Clutter.EventType.LEAVE ||
+ this.currentGrab.actor.contains(targetActor))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (Main.keyboard.maybeHandleEvent(event))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (button || touchBegin) {
+ // If we have a press event, ignore the next
+ // motion/release events.
+ if (press || touchBegin)
+ this._ignoreUntilRelease = true;
+
+ let i = this._actorInGrabStack(targetActor) + 1;
+ this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_STOP;
+ }
+};
diff --git a/js/ui/ibusCandidatePopup.js b/js/ui/ibusCandidatePopup.js
new file mode 100644
index 0000000..268b324
--- /dev/null
+++ b/js/ui/ibusCandidatePopup.js
@@ -0,0 +1,359 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported CandidatePopup */
+
+const { Clutter, GObject, IBus, St } = imports.gi;
+
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+
+var MAX_CANDIDATES_PER_PAGE = 16;
+
+var DEFAULT_INDEX_LABELS = [
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
+ 'a', 'b', 'c', 'd', 'e', 'f',
+];
+
+var CandidateArea = GObject.registerClass({
+ Signals: {
+ 'candidate-clicked': {
+ param_types: [
+ GObject.TYPE_UINT, GObject.TYPE_UINT, Clutter.ModifierType.$gtype,
+ ],
+ },
+ 'cursor-down': {},
+ 'cursor-up': {},
+ 'next-page': {},
+ 'previous-page': {},
+ },
+}, class CandidateArea extends St.BoxLayout {
+ _init() {
+ super._init({
+ vertical: true,
+ reactive: true,
+ visible: false,
+ });
+ this._candidateBoxes = [];
+ for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
+ const box = new St.BoxLayout({
+ style_class: 'candidate-box',
+ reactive: true,
+ track_hover: true,
+ });
+ box._indexLabel = new St.Label({ style_class: 'candidate-index' });
+ box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
+ box.add_child(box._indexLabel);
+ box.add_child(box._candidateLabel);
+ this._candidateBoxes.push(box);
+ this.add(box);
+
+ let j = i;
+ box.connect('button-release-event', (actor, event) => {
+ this.emit('candidate-clicked', j, event.get_button(), event.get_state());
+ return Clutter.EVENT_PROPAGATE;
+ });
+ }
+
+ this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
+
+ this._previousButton = new St.Button({
+ style_class: 'candidate-page-button candidate-page-button-previous button',
+ x_expand: true,
+ });
+ this._buttonBox.add_child(this._previousButton);
+
+ this._nextButton = new St.Button({
+ style_class: 'candidate-page-button candidate-page-button-next button',
+ x_expand: true,
+ });
+ this._buttonBox.add_child(this._nextButton);
+
+ this.add(this._buttonBox);
+
+ this._previousButton.connect('button-press-event', () => {
+ this.emit('previous-page');
+ return Clutter.EVENT_STOP;
+ });
+ this._previousButton.connect('touch-event', (actor, event) => {
+ if (event.type() === Clutter.EventType.TOUCH_BEGIN) {
+ this.emit('previous-page');
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ });
+ this._nextButton.connect('button-press-event', () => {
+ this.emit('next-page');
+ return Clutter.EVENT_STOP;
+ });
+ this._nextButton.connect('touch-event', (actor, event) => {
+ if (event.type() === Clutter.EventType.TOUCH_BEGIN) {
+ this.emit('next-page');
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ });
+
+ this._orientation = -1;
+ this._cursorPosition = 0;
+ }
+
+ vfunc_scroll_event(scrollEvent) {
+ switch (scrollEvent.direction) {
+ case Clutter.ScrollDirection.UP:
+ this.emit('cursor-up');
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ this.emit('cursor-down');
+ break;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ setOrientation(orientation) {
+ if (this._orientation == orientation)
+ return;
+
+ this._orientation = orientation;
+
+ if (this._orientation == IBus.Orientation.HORIZONTAL) {
+ this.vertical = false;
+ this.remove_style_class_name('vertical');
+ this.add_style_class_name('horizontal');
+ this._previousButton.icon_name = 'go-previous-symbolic';
+ this._nextButton.icon_name = 'go-next-symbolic';
+ } else { // VERTICAL || SYSTEM
+ this.vertical = true;
+ this.add_style_class_name('vertical');
+ this.remove_style_class_name('horizontal');
+ this._previousButton.icon_name = 'go-up-symbolic';
+ this._nextButton.icon_name = 'go-down-symbolic';
+ }
+ }
+
+ setCandidates(indexes, candidates, cursorPosition, cursorVisible) {
+ for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
+ let visible = i < candidates.length;
+ let box = this._candidateBoxes[i];
+ box.visible = visible;
+
+ if (!visible)
+ continue;
+
+ box._indexLabel.text = indexes && indexes[i] ? indexes[i] : DEFAULT_INDEX_LABELS[i];
+ box._candidateLabel.text = candidates[i];
+ }
+
+ this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
+ this._cursorPosition = cursorPosition;
+ if (cursorVisible)
+ this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
+ }
+
+ updateButtons(wrapsAround, page, nPages) {
+ if (nPages < 2) {
+ this._buttonBox.hide();
+ return;
+ }
+ this._buttonBox.show();
+ this._previousButton.reactive = wrapsAround || page > 0;
+ this._nextButton.reactive = wrapsAround || page < nPages - 1;
+ }
+});
+
+var CandidatePopup = GObject.registerClass(
+class IbusCandidatePopup extends BoxPointer.BoxPointer {
+ _init() {
+ super._init(St.Side.TOP);
+ this.visible = false;
+ this.style_class = 'candidate-popup-boxpointer';
+
+ this._dummyCursor = new Clutter.Actor({ opacity: 0 });
+ Main.layoutManager.uiGroup.add_actor(this._dummyCursor);
+
+ Main.layoutManager.addTopChrome(this);
+
+ const box = new St.BoxLayout({
+ style_class: 'candidate-popup-content',
+ vertical: true,
+ });
+ this.bin.set_child(box);
+
+ this._preeditText = new St.Label({
+ style_class: 'candidate-popup-text',
+ visible: false,
+ });
+ box.add(this._preeditText);
+
+ this._auxText = new St.Label({
+ style_class: 'candidate-popup-text',
+ visible: false,
+ });
+ box.add(this._auxText);
+
+ this._candidateArea = new CandidateArea();
+ box.add(this._candidateArea);
+
+ this._candidateArea.connect('previous-page', () => {
+ this._panelService.page_up();
+ });
+ this._candidateArea.connect('next-page', () => {
+ this._panelService.page_down();
+ });
+
+ this._candidateArea.connect('cursor-up', () => {
+ this._panelService.cursor_up();
+ });
+ this._candidateArea.connect('cursor-down', () => {
+ this._panelService.cursor_down();
+ });
+
+ this._candidateArea.connect('candidate-clicked', (area, index, button, state) => {
+ this._panelService.candidate_clicked(index, button, state);
+ });
+
+ this._panelService = null;
+ }
+
+ setPanelService(panelService) {
+ this._panelService = panelService;
+ if (!panelService)
+ return;
+
+ panelService.connect('set-cursor-location', (ps, x, y, w, h) => {
+ this._setDummyCursorGeometry(x, y, w, h);
+ });
+ try {
+ panelService.connect('set-cursor-location-relative', (ps, x, y, w, h) => {
+ if (!global.display.focus_window)
+ return;
+ let window = global.display.focus_window.get_compositor_private();
+ this._setDummyCursorGeometry(window.x + x, window.y + y, w, h);
+ });
+ } catch (e) {
+ // Only recent IBus versions have support for this signal
+ // which is used for wayland clients. In order to work
+ // with older IBus versions we can silently ignore the
+ // signal's absence.
+ }
+ panelService.connect('update-preedit-text', (ps, text, cursorPosition, visible) => {
+ this._preeditText.visible = visible;
+ this._updateVisibility();
+
+ this._preeditText.text = text.get_text();
+
+ let attrs = text.get_attributes();
+ if (attrs) {
+ this._setTextAttributes(this._preeditText.clutter_text,
+ attrs);
+ }
+ });
+ panelService.connect('show-preedit-text', () => {
+ this._preeditText.show();
+ this._updateVisibility();
+ });
+ panelService.connect('hide-preedit-text', () => {
+ this._preeditText.hide();
+ this._updateVisibility();
+ });
+ panelService.connect('update-auxiliary-text', (_ps, text, visible) => {
+ this._auxText.visible = visible;
+ this._updateVisibility();
+
+ this._auxText.text = text.get_text();
+ });
+ panelService.connect('show-auxiliary-text', () => {
+ this._auxText.show();
+ this._updateVisibility();
+ });
+ panelService.connect('hide-auxiliary-text', () => {
+ this._auxText.hide();
+ this._updateVisibility();
+ });
+ panelService.connect('update-lookup-table', (_ps, lookupTable, visible) => {
+ this._candidateArea.visible = visible;
+ this._updateVisibility();
+
+ let nCandidates = lookupTable.get_number_of_candidates();
+ let cursorPos = lookupTable.get_cursor_pos();
+ let pageSize = lookupTable.get_page_size();
+ let nPages = Math.ceil(nCandidates / pageSize);
+ let page = cursorPos == 0 ? 0 : Math.floor(cursorPos / pageSize);
+ let startIndex = page * pageSize;
+ let endIndex = Math.min((page + 1) * pageSize, nCandidates);
+
+ let indexes = [];
+ let indexLabel;
+ for (let i = 0; (indexLabel = lookupTable.get_label(i)); ++i)
+ indexes.push(indexLabel.get_text());
+
+ Main.keyboard.resetSuggestions();
+ Main.keyboard.setSuggestionsVisible(visible);
+
+ let candidates = [];
+ for (let i = startIndex; i < endIndex; ++i) {
+ candidates.push(lookupTable.get_candidate(i).get_text());
+
+ Main.keyboard.addSuggestion(lookupTable.get_candidate(i).get_text(), () => {
+ let index = i;
+ this._panelService.candidate_clicked(index, 1, 0);
+ });
+ }
+
+ this._candidateArea.setCandidates(indexes,
+ candidates,
+ cursorPos % pageSize,
+ lookupTable.is_cursor_visible());
+ this._candidateArea.setOrientation(lookupTable.get_orientation());
+ this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
+ });
+ panelService.connect('show-lookup-table', () => {
+ Main.keyboard.setSuggestionsVisible(true);
+ this._candidateArea.show();
+ this._updateVisibility();
+ });
+ panelService.connect('hide-lookup-table', () => {
+ Main.keyboard.setSuggestionsVisible(false);
+ this._candidateArea.hide();
+ this._updateVisibility();
+ });
+ panelService.connect('focus-out', () => {
+ this.close(BoxPointer.PopupAnimation.NONE);
+ Main.keyboard.resetSuggestions();
+ });
+ }
+
+ _setDummyCursorGeometry(x, y, w, h) {
+ this._dummyCursor.set_position(Math.round(x), Math.round(y));
+ this._dummyCursor.set_size(Math.round(w), Math.round(h));
+
+ if (this.visible)
+ this.setPosition(this._dummyCursor, 0);
+ }
+
+ _updateVisibility() {
+ let isVisible = !Main.keyboard.visible &&
+ (this._preeditText.visible ||
+ this._auxText.visible ||
+ this._candidateArea.visible);
+
+ if (isVisible) {
+ this.setPosition(this._dummyCursor, 0);
+ this.open(BoxPointer.PopupAnimation.NONE);
+ // We shouldn't be above some components like the screenshot UI,
+ // so don't raise to the top.
+ // The on-screen keyboard is expected to be above any entries,
+ // so just above the keyboard gets us to the right layer.
+ const { keyboardBox } = Main.layoutManager;
+ this.get_parent().set_child_above_sibling(this, keyboardBox);
+ } else {
+ this.close(BoxPointer.PopupAnimation.NONE);
+ }
+ }
+
+ _setTextAttributes(clutterText, ibusAttrList) {
+ let attr;
+ for (let i = 0; (attr = ibusAttrList.get(i)); ++i) {
+ if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
+ clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
+ }
+ }
+});
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
new file mode 100644
index 0000000..ac8d3ec
--- /dev/null
+++ b/js/ui/iconGrid.js
@@ -0,0 +1,1415 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported BaseIcon, IconGrid, IconGridLayout */
+
+const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const Params = imports.misc.params;
+const Main = imports.ui.main;
+
+var ICON_SIZE = 96;
+
+var PAGE_SWITCH_TIME = 300;
+
+var IconSize = {
+ LARGE: 96,
+ MEDIUM: 64,
+ MEDIUM_SMALL: 48,
+ SMALL: 32,
+ SMALLER: 24,
+ TINY: 16,
+};
+
+var APPICON_ANIMATION_OUT_SCALE = 3;
+var APPICON_ANIMATION_OUT_TIME = 250;
+
+const ICON_POSITION_DELAY = 10;
+
+const defaultGridModes = [
+ {
+ rows: 8,
+ columns: 3,
+ },
+ {
+ rows: 6,
+ columns: 4,
+ },
+ {
+ rows: 4,
+ columns: 6,
+ },
+ {
+ rows: 3,
+ columns: 8,
+ },
+];
+
+var LEFT_DIVIDER_LEEWAY = 20;
+var RIGHT_DIVIDER_LEEWAY = 20;
+
+var DragLocation = {
+ INVALID: 0,
+ START_EDGE: 1,
+ ON_ICON: 2,
+ END_EDGE: 3,
+ EMPTY_SPACE: 4,
+};
+
+var BaseIcon = GObject.registerClass(
+class BaseIcon extends Shell.SquareBin {
+ _init(label, params) {
+ params = Params.parse(params, {
+ createIcon: null,
+ setSizeManually: false,
+ showLabel: true,
+ });
+
+ let styleClass = 'overview-icon';
+ if (params.showLabel)
+ styleClass += ' overview-icon-with-label';
+
+ super._init({ style_class: styleClass });
+
+ this._box = new St.BoxLayout({
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ });
+ this.set_child(this._box);
+
+ this.iconSize = ICON_SIZE;
+ this._iconBin = new St.Bin({ x_align: Clutter.ActorAlign.CENTER });
+
+ this._box.add_actor(this._iconBin);
+
+ if (params.showLabel) {
+ this.label = new St.Label({ text: label });
+ this.label.clutter_text.set({
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._box.add_actor(this.label);
+ } else {
+ this.label = null;
+ }
+
+ if (params.createIcon)
+ this.createIcon = params.createIcon;
+ this._setSizeManually = params.setSizeManually;
+
+ this.icon = null;
+
+ let cache = St.TextureCache.get_default();
+ cache.connectObject(
+ 'icon-theme-changed', this._onIconThemeChanged.bind(this), this);
+ }
+
+ // This can be overridden by a subclass, or by the createIcon
+ // parameter to _init()
+ createIcon(_size) {
+ throw new GObject.NotImplementedError(`createIcon in ${this.constructor.name}`);
+ }
+
+ setIconSize(size) {
+ if (!this._setSizeManually)
+ throw new Error('setSizeManually has to be set to use setIconsize');
+
+ if (size === this.iconSize)
+ return;
+
+ this._createIconTexture(size);
+ }
+
+ _createIconTexture(size) {
+ if (this.icon)
+ this.icon.destroy();
+ this.iconSize = size;
+ this.icon = this.createIcon(this.iconSize);
+
+ this._iconBin.child = this.icon;
+ }
+
+ vfunc_style_changed() {
+ super.vfunc_style_changed();
+ let node = this.get_theme_node();
+
+ let size;
+ if (this._setSizeManually) {
+ size = this.iconSize;
+ } else {
+ const { scaleFactor } =
+ St.ThemeContext.get_for_stage(global.stage);
+
+ let [found, len] = node.lookup_length('icon-size', false);
+ size = found ? len / scaleFactor : ICON_SIZE;
+ }
+
+ if (this.iconSize === size && this._iconBin.child)
+ return;
+
+ this._createIconTexture(size);
+ }
+
+ _onIconThemeChanged() {
+ this._createIconTexture(this.iconSize);
+ }
+
+ animateZoomOut() {
+ // Animate only the child instead of the entire actor, so the
+ // styles like hover and running are not applied while
+ // animating.
+ zoomOutActor(this.child);
+ }
+
+ animateZoomOutAtPos(x, y) {
+ zoomOutActorAtPos(this.child, x, y);
+ }
+
+ update() {
+ this._createIconTexture(this.iconSize);
+ }
+});
+
+function zoomOutActor(actor) {
+ let [x, y] = actor.get_transformed_position();
+ zoomOutActorAtPos(actor, x, y);
+}
+
+function zoomOutActorAtPos(actor, x, y) {
+ const monitor = Main.layoutManager.findMonitorForActor(actor);
+ if (!monitor)
+ return;
+
+ const actorClone = new Clutter.Clone({
+ source: actor,
+ reactive: false,
+ });
+ let [width, height] = actor.get_transformed_size();
+
+ actorClone.set_size(width, height);
+ actorClone.set_position(x, y);
+ actorClone.opacity = 255;
+ actorClone.set_pivot_point(0.5, 0.5);
+
+ Main.uiGroup.add_actor(actorClone);
+
+ // Avoid monitor edges to not zoom outside the current monitor
+ let scaledWidth = width * APPICON_ANIMATION_OUT_SCALE;
+ let scaledHeight = height * APPICON_ANIMATION_OUT_SCALE;
+ let scaledX = x - (scaledWidth - width) / 2;
+ let scaledY = y - (scaledHeight - height) / 2;
+ let containedX = Math.clamp(scaledX, monitor.x, monitor.x + monitor.width - scaledWidth);
+ let containedY = Math.clamp(scaledY, monitor.y, monitor.y + monitor.height - scaledHeight);
+
+ actorClone.ease({
+ scale_x: APPICON_ANIMATION_OUT_SCALE,
+ scale_y: APPICON_ANIMATION_OUT_SCALE,
+ translation_x: containedX - scaledX,
+ translation_y: containedY - scaledY,
+ opacity: 0,
+ duration: APPICON_ANIMATION_OUT_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => actorClone.destroy(),
+ });
+}
+
+function animateIconPosition(icon, box, nChangedIcons) {
+ if (!icon.has_allocation() || icon.allocation.equal(box) || icon.opacity === 0) {
+ icon.allocate(box);
+ return false;
+ }
+
+ icon.save_easing_state();
+ icon.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
+ icon.set_easing_delay(nChangedIcons * ICON_POSITION_DELAY);
+
+ icon.allocate(box);
+
+ icon.restore_easing_state();
+
+ return true;
+}
+
+function swap(value, length) {
+ return length - value - 1;
+}
+
+var IconGridLayout = GObject.registerClass({
+ Properties: {
+ 'allow-incomplete-pages': GObject.ParamSpec.boolean('allow-incomplete-pages',
+ 'Allow incomplete pages', 'Allow incomplete pages',
+ GObject.ParamFlags.READWRITE,
+ true),
+ 'column-spacing': GObject.ParamSpec.int('column-spacing',
+ 'Column spacing', 'Column spacing',
+ GObject.ParamFlags.READWRITE,
+ 0, GLib.MAXINT32, 0),
+ 'columns-per-page': GObject.ParamSpec.int('columns-per-page',
+ 'Columns per page', 'Columns per page',
+ GObject.ParamFlags.READWRITE,
+ 1, GLib.MAXINT32, 6),
+ 'fixed-icon-size': GObject.ParamSpec.int('fixed-icon-size',
+ 'Fixed icon size', 'Fixed icon size',
+ GObject.ParamFlags.READWRITE,
+ -1, GLib.MAXINT32, -1),
+ 'icon-size': GObject.ParamSpec.int('icon-size',
+ 'Icon size', 'Icon size',
+ GObject.ParamFlags.READABLE,
+ 0, GLib.MAXINT32, 0),
+ 'last-row-align': GObject.ParamSpec.enum('last-row-align',
+ 'Last row align', 'Last row align',
+ GObject.ParamFlags.READWRITE,
+ Clutter.ActorAlign.$gtype,
+ Clutter.ActorAlign.FILL),
+ 'max-column-spacing': GObject.ParamSpec.int('max-column-spacing',
+ 'Maximum column spacing', 'Maximum column spacing',
+ GObject.ParamFlags.READWRITE,
+ -1, GLib.MAXINT32, -1),
+ 'max-row-spacing': GObject.ParamSpec.int('max-row-spacing',
+ 'Maximum row spacing', 'Maximum row spacing',
+ GObject.ParamFlags.READWRITE,
+ -1, GLib.MAXINT32, -1),
+ 'orientation': GObject.ParamSpec.enum('orientation',
+ 'Orientation', 'Orientation',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Orientation.$gtype,
+ Clutter.Orientation.VERTICAL),
+ 'page-halign': GObject.ParamSpec.enum('page-halign',
+ 'Horizontal page align',
+ 'Horizontal page align',
+ GObject.ParamFlags.READWRITE,
+ Clutter.ActorAlign.$gtype,
+ Clutter.ActorAlign.FILL),
+ 'page-padding': GObject.ParamSpec.boxed('page-padding',
+ 'Page padding', 'Page padding',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Margin.$gtype),
+ 'page-valign': GObject.ParamSpec.enum('page-valign',
+ 'Vertical page align',
+ 'Vertical page align',
+ GObject.ParamFlags.READWRITE,
+ Clutter.ActorAlign.$gtype,
+ Clutter.ActorAlign.FILL),
+ 'row-spacing': GObject.ParamSpec.int('row-spacing',
+ 'Row spacing', 'Row spacing',
+ GObject.ParamFlags.READWRITE,
+ 0, GLib.MAXINT32, 0),
+ 'rows-per-page': GObject.ParamSpec.int('rows-per-page',
+ 'Rows per page', 'Rows per page',
+ GObject.ParamFlags.READWRITE,
+ 1, GLib.MAXINT32, 4),
+ },
+ Signals: {
+ 'pages-changed': {},
+ },
+}, class IconGridLayout extends Clutter.LayoutManager {
+ _init(params = {}) {
+ this._orientation = params.orientation ?? Clutter.Orientation.VERTICAL;
+
+ super._init(params);
+
+ if (!this.pagePadding)
+ this.pagePadding = new Clutter.Margin();
+
+ this._iconSize = this.fixedIconSize !== -1
+ ? this.fixedIconSize
+ : IconSize.LARGE;
+
+ this._pageSizeChanged = false;
+ this._pageHeight = 0;
+ this._pageWidth = 0;
+ this._nPages = -1;
+
+ // [
+ // {
+ // children: [ itemData, itemData, itemData, ... ],
+ // },
+ // {
+ // children: [ itemData, itemData, itemData, ... ],
+ // },
+ // {
+ // children: [ itemData, itemData, itemData, ... ],
+ // },
+ // ]
+ this._pages = [];
+
+ // {
+ // item: {
+ // actor: Clutter.Actor,
+ // pageIndex: <index>,
+ // },
+ // item: {
+ // actor: Clutter.Actor,
+ // pageIndex: <index>,
+ // },
+ // }
+ this._items = new Map();
+
+ this._containerDestroyedId = 0;
+ this._updateIconSizesLaterId = 0;
+
+ this._childrenMaxSize = -1;
+ }
+
+ _findBestIconSize() {
+ const nColumns = this.columnsPerPage;
+ const nRows = this.rowsPerPage;
+ const columnSpacingPerPage = this.columnSpacing * (nColumns - 1);
+ const rowSpacingPerPage = this.rowSpacing * (nRows - 1);
+ const [firstItem] = this._container;
+
+ if (this.fixedIconSize !== -1)
+ return this.fixedIconSize;
+
+ const iconSizes = Object.values(IconSize).sort((a, b) => b - a);
+ for (const size of iconSizes) {
+ let usedWidth, usedHeight;
+
+ if (firstItem) {
+ firstItem.icon.setIconSize(size);
+ const [firstItemWidth, firstItemHeight] =
+ firstItem.get_preferred_size();
+
+ const itemSize = Math.max(firstItemWidth, firstItemHeight);
+
+ usedWidth = itemSize * nColumns;
+ usedHeight = itemSize * nRows;
+ } else {
+ usedWidth = size * nColumns;
+ usedHeight = size * nRows;
+ }
+
+ const emptyHSpace =
+ this._pageWidth - usedWidth - columnSpacingPerPage -
+ this.pagePadding.left - this.pagePadding.right;
+ const emptyVSpace =
+ this._pageHeight - usedHeight - rowSpacingPerPage -
+ this.pagePadding.top - this.pagePadding.bottom;
+
+ if (emptyHSpace >= 0 && emptyVSpace > 0)
+ return size;
+ }
+
+ return IconSize.TINY;
+ }
+
+ _getChildrenMaxSize() {
+ if (this._childrenMaxSize === -1) {
+ let minWidth = 0;
+ let minHeight = 0;
+
+ const nPages = this._pages.length;
+ for (let pageIndex = 0; pageIndex < nPages; pageIndex++) {
+ const page = this._pages[pageIndex];
+ const nVisibleItems = page.visibleChildren.length;
+ for (let itemIndex = 0; itemIndex < nVisibleItems; itemIndex++) {
+ const item = page.visibleChildren[itemIndex];
+
+ const childMinHeight = item.get_preferred_height(-1)[0];
+ const childMinWidth = item.get_preferred_width(-1)[0];
+
+ minWidth = Math.max(minWidth, childMinWidth);
+ minHeight = Math.max(minHeight, childMinHeight);
+ }
+ }
+
+ this._childrenMaxSize = Math.max(minWidth, minHeight);
+ }
+
+ return this._childrenMaxSize;
+ }
+
+ _updateVisibleChildrenForPage(pageIndex) {
+ this._pages[pageIndex].visibleChildren =
+ this._pages[pageIndex].children.filter(actor => actor.visible);
+ }
+
+ _updatePages() {
+ for (let i = 0; i < this._pages.length; i++)
+ this._relocateSurplusItems(i);
+ }
+
+ _unlinkItem(item) {
+ const itemData = this._items.get(item);
+
+ item.disconnect(itemData.destroyId);
+ item.disconnect(itemData.visibleId);
+ item.disconnect(itemData.queueRelayoutId);
+
+ this._items.delete(item);
+ }
+
+ _removePage(pageIndex) {
+ // Make sure to not leave any icon left here
+ this._pages[pageIndex].children.forEach(item => {
+ this._unlinkItem(item);
+ });
+
+ // Adjust the page indexes of items after this page
+ for (const itemData of this._items.values()) {
+ if (itemData.pageIndex > pageIndex)
+ itemData.pageIndex--;
+ }
+
+ this._pages.splice(pageIndex, 1);
+ this.emit('pages-changed');
+ }
+
+ _fillItemVacancies(pageIndex) {
+ if (pageIndex >= this._pages.length - 1)
+ return;
+
+ const visiblePageItems = this._pages[pageIndex].visibleChildren;
+ const itemsPerPage = this.columnsPerPage * this.rowsPerPage;
+
+ // No reduce needed
+ if (visiblePageItems.length === itemsPerPage)
+ return;
+
+ const visibleNextPageItems = this._pages[pageIndex + 1].visibleChildren;
+ const nMissingItems = Math.min(itemsPerPage - visiblePageItems.length, visibleNextPageItems.length);
+
+ // Append to the current page the first items of the next page
+ for (let i = 0; i < nMissingItems; i++) {
+ const reducedItem = visibleNextPageItems[i];
+
+ this._removeItemData(reducedItem);
+ this._addItemToPage(reducedItem, pageIndex, -1);
+ }
+ }
+
+ _removeItemData(item) {
+ const itemData = this._items.get(item);
+ const pageIndex = itemData.pageIndex;
+ const page = this._pages[pageIndex];
+ const itemIndex = page.children.indexOf(item);
+
+ this._unlinkItem(item);
+
+ page.children.splice(itemIndex, 1);
+
+ this._updateVisibleChildrenForPage(pageIndex);
+
+ // Delete the page if this is the last icon in it
+ const visibleItems = this._pages[pageIndex].visibleChildren;
+ if (visibleItems.length === 0)
+ this._removePage(pageIndex);
+
+ if (!this.allowIncompletePages)
+ this._fillItemVacancies(pageIndex);
+ }
+
+ _relocateSurplusItems(pageIndex) {
+ const visiblePageItems = this._pages[pageIndex].visibleChildren;
+ const itemsPerPage = this.columnsPerPage * this.rowsPerPage;
+
+ // No overflow needed
+ if (visiblePageItems.length <= itemsPerPage)
+ return;
+
+ const nExtraItems = visiblePageItems.length - itemsPerPage;
+ for (let i = 0; i < nExtraItems; i++) {
+ const overflowIndex = visiblePageItems.length - i - 1;
+ const overflowItem = visiblePageItems[overflowIndex];
+
+ this._removeItemData(overflowItem);
+ this._addItemToPage(overflowItem, pageIndex + 1, 0);
+ }
+ }
+
+ _appendPage() {
+ this._pages.push({ children: [] });
+ this.emit('pages-changed');
+ }
+
+ _addItemToPage(item, pageIndex, index) {
+ // Ensure we have at least one page
+ if (this._pages.length === 0)
+ this._appendPage();
+
+ // Append a new page if necessary
+ if (pageIndex === this._pages.length)
+ this._appendPage();
+
+ if (pageIndex === -1)
+ pageIndex = this._pages.length - 1;
+
+ if (index === -1)
+ index = this._pages[pageIndex].children.length;
+
+ this._items.set(item, {
+ actor: item,
+ pageIndex,
+ destroyId: item.connect('destroy', () => this._removeItemData(item)),
+ visibleId: item.connect('notify::visible', () => {
+ const itemData = this._items.get(item);
+
+ this._updateVisibleChildrenForPage(itemData.pageIndex);
+
+ if (item.visible)
+ this._relocateSurplusItems(itemData.pageIndex);
+ else if (!this.allowIncompletePages)
+ this._fillItemVacancies(itemData.pageIndex);
+ }),
+ queueRelayoutId: item.connect('queue-relayout', () => {
+ this._childrenMaxSize = -1;
+ }),
+ });
+
+ item.icon.setIconSize(this._iconSize);
+
+ this._pages[pageIndex].children.splice(index, 0, item);
+ this._updateVisibleChildrenForPage(pageIndex);
+ this._relocateSurplusItems(pageIndex);
+ }
+
+ _calculateSpacing(childSize) {
+ const nColumns = this.columnsPerPage;
+ const nRows = this.rowsPerPage;
+ const usedWidth = childSize * nColumns;
+ const usedHeight = childSize * nRows;
+ const columnSpacingPerPage = this.columnSpacing * (nColumns - 1);
+ const rowSpacingPerPage = this.rowSpacing * (nRows - 1);
+
+ const emptyHSpace =
+ this._pageWidth - usedWidth - columnSpacingPerPage -
+ this.pagePadding.left - this.pagePadding.right;
+ const emptyVSpace =
+ this._pageHeight - usedHeight - rowSpacingPerPage -
+ this.pagePadding.top - this.pagePadding.bottom;
+ let leftEmptySpace = this.pagePadding.left;
+ let topEmptySpace = this.pagePadding.top;
+ let hSpacing;
+ let vSpacing;
+
+ switch (this.pageHalign) {
+ case Clutter.ActorAlign.START:
+ hSpacing = this.columnSpacing;
+ break;
+ case Clutter.ActorAlign.CENTER:
+ leftEmptySpace += Math.floor(emptyHSpace / 2);
+ hSpacing = this.columnSpacing;
+ break;
+ case Clutter.ActorAlign.END:
+ leftEmptySpace += emptyHSpace;
+ hSpacing = this.columnSpacing;
+ break;
+ case Clutter.ActorAlign.FILL:
+ hSpacing = this.columnSpacing + emptyHSpace / (nColumns - 1);
+
+ // Maybe constraint horizontal spacing
+ if (this.maxColumnSpacing !== -1 && hSpacing > this.maxColumnSpacing) {
+ const extraHSpacing =
+ (this.maxColumnSpacing - this.columnSpacing) * (nColumns - 1);
+
+ hSpacing = this.maxColumnSpacing;
+ leftEmptySpace +=
+ Math.max((emptyHSpace - extraHSpacing) / 2, 0);
+ }
+ break;
+ }
+
+ switch (this.pageValign) {
+ case Clutter.ActorAlign.START:
+ vSpacing = this.rowSpacing;
+ break;
+ case Clutter.ActorAlign.CENTER:
+ topEmptySpace += Math.floor(emptyVSpace / 2);
+ vSpacing = this.rowSpacing;
+ break;
+ case Clutter.ActorAlign.END:
+ topEmptySpace += emptyVSpace;
+ vSpacing = this.rowSpacing;
+ break;
+ case Clutter.ActorAlign.FILL:
+ vSpacing = this.rowSpacing + emptyVSpace / (nRows - 1);
+
+ // Maybe constraint vertical spacing
+ if (this.maxRowSpacing !== -1 && vSpacing > this.maxRowSpacing) {
+ const extraVSpacing =
+ (this.maxRowSpacing - this.rowSpacing) * (nRows - 1);
+
+ vSpacing = this.maxRowSpacing;
+ topEmptySpace +=
+ Math.max((emptyVSpace - extraVSpacing) / 2, 0);
+ }
+
+ break;
+ }
+
+ return [leftEmptySpace, topEmptySpace, hSpacing, vSpacing];
+ }
+
+ _getRowPadding(align, items, itemIndex, childSize, spacing) {
+ if (align === Clutter.ActorAlign.START ||
+ align === Clutter.ActorAlign.FILL)
+ return 0;
+
+ const nRows = Math.ceil(items.length / this.columnsPerPage);
+
+ let rowAlign = 0;
+ const row = Math.floor(itemIndex / this.columnsPerPage);
+
+ // Only apply to the last row
+ if (row < nRows - 1)
+ return 0;
+
+ const rowStart = row * this.columnsPerPage;
+ const rowEnd = Math.min((row + 1) * this.columnsPerPage - 1, items.length - 1);
+ const itemsInThisRow = rowEnd - rowStart + 1;
+ const nEmpty = this.columnsPerPage - itemsInThisRow;
+ const availableWidth = nEmpty * (spacing + childSize);
+
+ const isRtl =
+ Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+
+ switch (align) {
+ case Clutter.ActorAlign.CENTER:
+ rowAlign = availableWidth / 2;
+ break;
+ case Clutter.ActorAlign.END:
+ rowAlign = availableWidth;
+ break;
+ // START and FILL align are handled at the beginning of the function
+ }
+
+ return isRtl ? rowAlign * -1 : rowAlign;
+ }
+
+ _onDestroy() {
+ if (this._updateIconSizesLaterId >= 0) {
+ Meta.later_remove(this._updateIconSizesLaterId);
+ this._updateIconSizesLaterId = 0;
+ }
+ }
+
+ vfunc_set_container(container) {
+ this._container?.disconnectObject(this);
+
+ this._container = container;
+
+ if (this._container)
+ this._container.connectObject('destroy', this._onDestroy.bind(this), this);
+ }
+
+ vfunc_get_preferred_width(_container, _forHeight) {
+ let minWidth = -1;
+ let natWidth = -1;
+
+ switch (this._orientation) {
+ case Clutter.Orientation.VERTICAL:
+ minWidth = IconSize.TINY;
+ natWidth = this._pageWidth;
+ break;
+
+ case Clutter.Orientation.HORIZONTAL:
+ minWidth = this._pageWidth * this._pages.length;
+ natWidth = minWidth;
+ break;
+ }
+
+ return [minWidth, natWidth];
+ }
+
+ vfunc_get_preferred_height(_container, _forWidth) {
+ let minHeight = -1;
+ let natHeight = -1;
+
+ switch (this._orientation) {
+ case Clutter.Orientation.VERTICAL:
+ minHeight = this._pageHeight * this._pages.length;
+ natHeight = minHeight;
+ break;
+
+ case Clutter.Orientation.HORIZONTAL:
+ minHeight = IconSize.TINY;
+ natHeight = this._pageHeight;
+ break;
+ }
+
+ return [minHeight, natHeight];
+ }
+
+ vfunc_allocate() {
+ if (this._pageWidth === 0 || this._pageHeight === 0)
+ throw new Error('IconGridLayout.adaptToSize wasn\'t called before allocation');
+
+ const isRtl =
+ Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+ const childSize = this._getChildrenMaxSize();
+
+ const [leftEmptySpace, topEmptySpace, hSpacing, vSpacing] =
+ this._calculateSpacing(childSize);
+
+ const childBox = new Clutter.ActorBox();
+
+ let nChangedIcons = 0;
+ const columnsPerPage = this.columnsPerPage;
+ const orientation = this._orientation;
+ const pageWidth = this._pageWidth;
+ const pageHeight = this._pageHeight;
+ const pageSizeChanged = this._pageSizeChanged;
+ const lastRowAlign = this.lastRowAlign;
+ const shouldEaseItems = this._shouldEaseItems;
+
+ this._pages.forEach((page, pageIndex) => {
+ if (isRtl && orientation === Clutter.Orientation.HORIZONTAL)
+ pageIndex = swap(pageIndex, this._pages.length);
+
+ page.visibleChildren.forEach((item, itemIndex) => {
+ const row = Math.floor(itemIndex / columnsPerPage);
+ let column = itemIndex % columnsPerPage;
+
+ if (isRtl)
+ column = swap(column, columnsPerPage);
+
+ const rowPadding = this._getRowPadding(lastRowAlign,
+ page.visibleChildren, itemIndex, childSize, hSpacing);
+
+ // Icon position
+ let x = leftEmptySpace + rowPadding + column * (childSize + hSpacing);
+ let y = topEmptySpace + row * (childSize + vSpacing);
+
+ // Page start
+ switch (orientation) {
+ case Clutter.Orientation.HORIZONTAL:
+ x += pageIndex * pageWidth;
+ break;
+ case Clutter.Orientation.VERTICAL:
+ y += pageIndex * pageHeight;
+ break;
+ }
+
+ childBox.set_origin(Math.floor(x), Math.floor(y));
+
+ const [,, naturalWidth, naturalHeight] = item.get_preferred_size();
+ childBox.set_size(
+ Math.max(childSize, naturalWidth),
+ Math.max(childSize, naturalHeight));
+
+ if (!shouldEaseItems || pageSizeChanged)
+ item.allocate(childBox);
+ else if (animateIconPosition(item, childBox, nChangedIcons))
+ nChangedIcons++;
+ });
+ });
+
+ this._pageSizeChanged = false;
+ this._shouldEaseItems = false;
+ }
+
+ /**
+ * addItem:
+ * @param {Clutter.Actor} item: item to append to the grid
+ * @param {int} page: page number
+ * @param {int} index: position in the page
+ *
+ * Adds @item to the grid. @item must not be part of the grid.
+ *
+ * If @index exceeds the number of items per page, @item will
+ * be added to the next page.
+ *
+ * @page must be a number between 0 and the number of pages.
+ * Adding to the page after next will create a new page.
+ */
+ addItem(item, page = -1, index = -1) {
+ if (this._items.has(item))
+ throw new Error(`Item ${item} already added to IconGridLayout`);
+
+ if (page > this._pages.length)
+ throw new Error(`Cannot add ${item} to page ${page}`);
+
+ if (!this._container)
+ return;
+
+ this._shouldEaseItems = true;
+
+ this._container.add_child(item);
+ this._addItemToPage(item, page, index);
+ }
+
+ /**
+ * appendItem:
+ * @param {Clutter.Actor} item: item to append to the grid
+ *
+ * Appends @item to the grid. @item must not be part of the grid.
+ */
+ appendItem(item) {
+ this.addItem(item);
+ }
+
+ /**
+ * moveItem:
+ * @param {Clutter.Actor} item: item to move
+ * @param {int} newPage: new page of the item
+ * @param {int} newPosition: new page of the item
+ *
+ * Moves @item to the grid. @item must be part of the grid.
+ */
+ moveItem(item, newPage, newPosition) {
+ if (!this._items.has(item))
+ throw new Error(`Item ${item} is not part of the IconGridLayout`);
+
+ this._shouldEaseItems = true;
+
+ this._removeItemData(item);
+ this._addItemToPage(item, newPage, newPosition);
+ }
+
+ /**
+ * removeItem:
+ * @param {Clutter.Actor} item: item to remove from the grid
+ *
+ * Removes @item to the grid. @item must be part of the grid.
+ */
+ removeItem(item) {
+ if (!this._items.has(item))
+ throw new Error(`Item ${item} is not part of the IconGridLayout`);
+
+ if (!this._container)
+ return;
+
+ this._shouldEaseItems = true;
+
+ this._container.remove_child(item);
+ this._removeItemData(item);
+ }
+
+ /**
+ * getItemsAtPage:
+ * @param {int} pageIndex: page index
+ *
+ * Retrieves the children at page @pageIndex. Children may be invisible.
+ *
+ * @returns {Array} an array of {Clutter.Actor}s
+ */
+ getItemsAtPage(pageIndex) {
+ if (pageIndex >= this._pages.length)
+ throw new Error(`IconGridLayout does not have page ${pageIndex}`);
+
+ return [...this._pages[pageIndex].children];
+ }
+
+ /**
+ * getItemPosition:
+ * @param {BaseIcon} item: the item
+ *
+ * Retrieves the position of @item is its page, or -1 if @item is not
+ * part of the grid.
+ *
+ * @returns {[int, int]} the page and position of @item
+ */
+ getItemPosition(item) {
+ if (!this._items.has(item))
+ return [-1, -1];
+
+ const itemData = this._items.get(item);
+ const visibleItems = this._pages[itemData.pageIndex].visibleChildren;
+
+ return [itemData.pageIndex, visibleItems.indexOf(item)];
+ }
+
+ /**
+ * getItemAt:
+ * @param {int} page: the page
+ * @param {int} position: the position in page
+ *
+ * Retrieves the item at @page and @position.
+ *
+ * @returns {BaseItem} the item at @page and @position, or null
+ */
+ getItemAt(page, position) {
+ if (page < 0 || page >= this._pages.length)
+ return null;
+
+ const visibleItems = this._pages[page].visibleChildren;
+
+ if (position < 0 || position >= visibleItems.length)
+ return null;
+
+ return visibleItems[position];
+ }
+
+ /**
+ * getItemPage:
+ * @param {BaseIcon} item: the item
+ *
+ * Retrieves the page @item is in, or -1 if @item is not part of the grid.
+ *
+ * @returns {int} the page where @item is in
+ */
+ getItemPage(item) {
+ if (!this._items.has(item))
+ return -1;
+
+ const itemData = this._items.get(item);
+ return itemData.pageIndex;
+ }
+
+ ensureIconSizeUpdated() {
+ if (this._updateIconSizesLaterId === 0)
+ return Promise.resolve();
+
+ return new Promise(
+ resolve => this._iconSizeUpdateResolveCbs.push(resolve));
+ }
+
+ adaptToSize(pageWidth, pageHeight) {
+ if (this._pageWidth === pageWidth && this._pageHeight === pageHeight)
+ return;
+
+ this._pageWidth = pageWidth;
+ this._pageHeight = pageHeight;
+ this._pageSizeChanged = true;
+
+ if (this._updateIconSizesLaterId === 0) {
+ this._updateIconSizesLaterId =
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ const iconSize = this._findBestIconSize();
+
+ if (this._iconSize !== iconSize) {
+ this._iconSize = iconSize;
+
+ for (const child of this._container)
+ child.icon.setIconSize(iconSize);
+
+ this.notify('icon-size');
+ }
+
+ this._updateIconSizesLaterId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+
+ /**
+ * getDropTarget:
+ * @param {int} x: position of the horizontal axis
+ * @param {int} y: position of the vertical axis
+ *
+ * Retrieves the item located at (@x, @y), as well as the drag location.
+ * Both @x and @y are relative to the grid.
+ *
+ * @returns {[Clutter.Actor, DragLocation]} the item and drag location
+ * under (@x, @y)
+ */
+ getDropTarget(x, y) {
+ const childSize = this._getChildrenMaxSize();
+ const [leftEmptySpace, topEmptySpace, hSpacing, vSpacing] =
+ this._calculateSpacing(childSize);
+
+ const isRtl =
+ Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+
+ let page = this._orientation === Clutter.Orientation.VERTICAL
+ ? Math.floor(y / this._pageHeight)
+ : Math.floor(x / this._pageWidth);
+
+ // Out of bounds
+ if (page >= this._pages.length)
+ return [null, DragLocation.INVALID];
+
+ if (isRtl && this._orientation === Clutter.Orientation.HORIZONTAL)
+ page = swap(page, this._pages.length);
+
+ // Page-relative coordinates from now on
+ if (this._orientation === Clutter.Orientation.HORIZONTAL)
+ x %= this._pageWidth;
+ else
+ y %= this._pageHeight;
+
+ if (x < leftEmptySpace || y < topEmptySpace)
+ return [null, DragLocation.INVALID];
+
+ const gridWidth =
+ childSize * this.columnsPerPage +
+ hSpacing * (this.columnsPerPage - 1);
+ const gridHeight =
+ childSize * this.rowsPerPage +
+ vSpacing * (this.rowsPerPage - 1);
+
+ if (x > leftEmptySpace + gridWidth || y > topEmptySpace + gridHeight)
+ return [null, DragLocation.INVALID];
+
+ const halfHSpacing = hSpacing / 2;
+ const halfVSpacing = vSpacing / 2;
+ const visibleItems = this._pages[page].visibleChildren;
+
+ for (const item of visibleItems) {
+ const childBox = item.allocation.copy();
+
+ // Page offset
+ switch (this._orientation) {
+ case Clutter.Orientation.HORIZONTAL:
+ childBox.set_origin(childBox.x1 % this._pageWidth, childBox.y1);
+ break;
+ case Clutter.Orientation.VERTICAL:
+ childBox.set_origin(childBox.x1, childBox.y1 % this._pageHeight);
+ break;
+ }
+
+ // Outside the icon boundaries
+ if (x < childBox.x1 - halfHSpacing ||
+ x > childBox.x2 + halfHSpacing ||
+ y < childBox.y1 - halfVSpacing ||
+ y > childBox.y2 + halfVSpacing)
+ continue;
+
+ let dragLocation;
+
+ if (x < childBox.x1 + LEFT_DIVIDER_LEEWAY)
+ dragLocation = DragLocation.START_EDGE;
+ else if (x > childBox.x2 - RIGHT_DIVIDER_LEEWAY)
+ dragLocation = DragLocation.END_EDGE;
+ else
+ dragLocation = DragLocation.ON_ICON;
+
+ if (isRtl) {
+ if (dragLocation === DragLocation.START_EDGE)
+ dragLocation = DragLocation.END_EDGE;
+ else if (dragLocation === DragLocation.END_EDGE)
+ dragLocation = DragLocation.START_EDGE;
+ }
+
+ return [item, dragLocation];
+ }
+
+ return [null, DragLocation.EMPTY_SPACE];
+ }
+
+ get iconSize() {
+ return this._iconSize;
+ }
+
+ get nPages() {
+ return this._pages.length;
+ }
+
+ get orientation() {
+ return this._orientation;
+ }
+
+ set orientation(v) {
+ if (this._orientation === v)
+ return;
+
+ switch (v) {
+ case Clutter.Orientation.VERTICAL:
+ this.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
+ break;
+ case Clutter.Orientation.HORIZONTAL:
+ this.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
+ break;
+ }
+
+ this._orientation = v;
+ this.notify('orientation');
+ }
+
+ get pageHeight() {
+ return this._pageHeight;
+ }
+
+ get pageWidth() {
+ return this._pageWidth;
+ }
+});
+
+var IconGrid = GObject.registerClass({
+ Signals: {
+ 'pages-changed': {},
+ },
+}, class IconGrid extends St.Viewport {
+ _init(layoutParams = {}) {
+ layoutParams = Params.parse(layoutParams, {
+ allow_incomplete_pages: false,
+ orientation: Clutter.Orientation.HORIZONTAL,
+ columns_per_page: 6,
+ rows_per_page: 4,
+ page_halign: Clutter.ActorAlign.FILL,
+ page_padding: new Clutter.Margin(),
+ page_valign: Clutter.ActorAlign.FILL,
+ last_row_align: Clutter.ActorAlign.START,
+ column_spacing: 0,
+ row_spacing: 0,
+ });
+ const layoutManager = new IconGridLayout(layoutParams);
+ const pagesChangedId = layoutManager.connect('pages-changed',
+ () => this.emit('pages-changed'));
+
+ super._init({
+ style_class: 'icon-grid',
+ layoutManager,
+ x_expand: true,
+ y_expand: true,
+ });
+
+ this._gridModes = defaultGridModes;
+ this._currentPage = 0;
+ this._currentMode = -1;
+
+ this.connect('actor-added', this._childAdded.bind(this));
+ this.connect('actor-removed', this._childRemoved.bind(this));
+ this.connect('destroy', () => layoutManager.disconnect(pagesChangedId));
+ }
+
+ _childAdded(grid, child) {
+ child._iconGridKeyFocusInId = child.connect('key-focus-in', () => {
+ this._ensureItemIsVisible(child);
+ });
+ }
+
+ _ensureItemIsVisible(item) {
+ if (!this.contains(item))
+ throw new Error(`${item} is not a child of IconGrid`);
+
+ const itemPage = this.layout_manager.getItemPage(item);
+ this.goToPage(itemPage);
+ }
+
+ _setGridMode(modeIndex) {
+ if (this._currentMode === modeIndex)
+ return;
+
+ this._currentMode = modeIndex;
+
+ if (modeIndex !== -1) {
+ const newMode = this._gridModes[modeIndex];
+
+ this.layout_manager.rows_per_page = newMode.rows;
+ this.layout_manager.columns_per_page = newMode.columns;
+ }
+ }
+
+ _findBestModeForSize(width, height) {
+ const { pagePadding } = this.layout_manager;
+ width -= pagePadding.left + pagePadding.right;
+ height -= pagePadding.top + pagePadding.bottom;
+
+ const sizeRatio = width / height;
+ let closestRatio = Infinity;
+ let bestMode = -1;
+
+ for (let modeIndex in this._gridModes) {
+ const mode = this._gridModes[modeIndex];
+ const modeRatio = mode.columns / mode.rows;
+
+ if (Math.abs(sizeRatio - modeRatio) < Math.abs(sizeRatio - closestRatio)) {
+ closestRatio = modeRatio;
+ bestMode = modeIndex;
+ }
+ }
+
+ this._setGridMode(bestMode);
+ }
+
+ _childRemoved(grid, child) {
+ child.disconnect(child._iconGridKeyFocusInId);
+ delete child._iconGridKeyFocusInId;
+ }
+
+ vfunc_allocate(box) {
+ const [width, height] = box.get_size();
+ this._findBestModeForSize(width, height);
+ this.layout_manager.adaptToSize(width, height);
+ super.vfunc_allocate(box);
+ }
+
+ vfunc_style_changed() {
+ super.vfunc_style_changed();
+
+ const node = this.get_theme_node();
+ this.layout_manager.column_spacing = node.get_length('column-spacing');
+ this.layout_manager.row_spacing = node.get_length('row-spacing');
+
+ let [found, value] = node.lookup_length('max-column-spacing', false);
+ this.layout_manager.max_column_spacing = found ? value : -1;
+
+ [found, value] = node.lookup_length('max-row-spacing', false);
+ this.layout_manager.max_row_spacing = found ? value : -1;
+
+ const padding = new Clutter.Margin();
+ ['top', 'right', 'bottom', 'left'].forEach(side => {
+ padding[side] = node.get_length(`page-padding-${side}`);
+ });
+ this.layout_manager.page_padding = padding;
+ }
+
+ /**
+ * addItem:
+ * @param {Clutter.Actor} item: item to append to the grid
+ * @param {int} page: page number
+ * @param {int} index: position in the page
+ *
+ * Adds @item to the grid. @item must not be part of the grid.
+ *
+ * If @index exceeds the number of items per page, @item will
+ * be added to the next page.
+ *
+ * @page must be a number between 0 and the number of pages.
+ * Adding to the page after next will create a new page.
+ */
+ addItem(item, page = -1, index = -1) {
+ if (!(item.icon instanceof BaseIcon))
+ throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
+
+ this.layout_manager.addItem(item, page, index);
+ }
+
+ /**
+ * appendItem:
+ * @param {Clutter.Actor} item: item to append to the grid
+ *
+ * Appends @item to the grid. @item must not be part of the grid.
+ */
+ appendItem(item) {
+ this.layout_manager.appendItem(item);
+ }
+
+ /**
+ * moveItem:
+ * @param {Clutter.Actor} item: item to move
+ * @param {int} newPage: new page of the item
+ * @param {int} newPosition: new page of the item
+ *
+ * Moves @item to the grid. @item must be part of the grid.
+ */
+ moveItem(item, newPage, newPosition) {
+ this.layout_manager.moveItem(item, newPage, newPosition);
+ this.queue_relayout();
+ }
+
+ /**
+ * removeItem:
+ * @param {Clutter.Actor} item: item to remove from the grid
+ *
+ * Removes @item to the grid. @item must be part of the grid.
+ */
+ removeItem(item) {
+ if (!this.contains(item))
+ throw new Error(`Item ${item} is not part of the IconGrid`);
+
+ this.layout_manager.removeItem(item);
+ }
+
+ /**
+ * goToPage:
+ * @param {int} pageIndex: page index
+ * @param {boolean} animate: animate the page transition
+ *
+ * Moves the current page to @pageIndex. @pageIndex must be a valid page
+ * number.
+ */
+ goToPage(pageIndex, animate = true) {
+ if (pageIndex >= this.nPages)
+ throw new Error(`IconGrid does not have page ${pageIndex}`);
+
+ let newValue;
+ let adjustment;
+ switch (this.layout_manager.orientation) {
+ case Clutter.Orientation.VERTICAL:
+ adjustment = this.vadjustment;
+ newValue = pageIndex * this.layout_manager.pageHeight;
+ break;
+ case Clutter.Orientation.HORIZONTAL:
+ adjustment = this.hadjustment;
+ newValue = pageIndex * this.layout_manager.pageWidth;
+ break;
+ }
+
+ this._currentPage = pageIndex;
+
+ if (!this.mapped)
+ animate = false;
+
+ adjustment.ease(newValue, {
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ duration: animate ? PAGE_SWITCH_TIME : 0,
+ });
+ }
+
+ /**
+ * getItemPage:
+ * @param {BaseIcon} item: the item
+ *
+ * Retrieves the page @item is in, or -1 if @item is not part of the grid.
+ *
+ * @returns {int} the page where @item is in
+ */
+ getItemPage(item) {
+ return this.layout_manager.getItemPage(item);
+ }
+
+ /**
+ * getItemPosition:
+ * @param {BaseIcon} item: the item
+ *
+ * Retrieves the position of @item is its page, or -1 if @item is not
+ * part of the grid.
+ *
+ * @returns {[int, int]} the page and position of @item
+ */
+ getItemPosition(item) {
+ if (!this.contains(item))
+ return [-1, -1];
+
+ const layoutManager = this.layout_manager;
+ return layoutManager.getItemPosition(item);
+ }
+
+ /**
+ * getItemAt:
+ * @param {int} page: the page
+ * @param {int} position: the position in page
+ *
+ * Retrieves the item at @page and @position.
+ *
+ * @returns {BaseItem} the item at @page and @position, or null
+ */
+ getItemAt(page, position) {
+ const layoutManager = this.layout_manager;
+ return layoutManager.getItemAt(page, position);
+ }
+
+ /**
+ * getItemsAtPage:
+ * @param {int} page: the page index
+ *
+ * Retrieves the children at page @page, including invisible children.
+ *
+ * @returns {Array} an array of {Clutter.Actor}s
+ */
+ getItemsAtPage(page) {
+ if (page < 0 || page > this.nPages)
+ throw new Error(`Page ${page} does not exist at IconGrid`);
+
+ const layoutManager = this.layout_manager;
+ return layoutManager.getItemsAtPage(page);
+ }
+
+ get currentPage() {
+ return this._currentPage;
+ }
+
+ set currentPage(v) {
+ this.goToPage(v);
+ }
+
+ get nPages() {
+ return this.layout_manager.nPages;
+ }
+
+ setGridModes(modes) {
+ this._gridModes = modes ? modes : defaultGridModes;
+ this.queue_relayout();
+ }
+
+ getDropTarget(x, y) {
+ const layoutManager = this.layout_manager;
+ return layoutManager.getDropTarget(x, y, this._currentPage);
+ }
+
+ get itemsPerPage() {
+ const layoutManager = this.layout_manager;
+ return layoutManager.rows_per_page * layoutManager.columns_per_page;
+ }
+});
diff --git a/js/ui/inhibitShortcutsDialog.js b/js/ui/inhibitShortcutsDialog.js
new file mode 100644
index 0000000..7c3d159
--- /dev/null
+++ b/js/ui/inhibitShortcutsDialog.js
@@ -0,0 +1,160 @@
+/* exported InhibitShortcutsDialog */
+const {Clutter, Gio, GObject, Gtk, Meta, Pango, Shell, St} = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const ModalDialog = imports.ui.modalDialog;
+const PermissionStore = imports.misc.permissionStore;
+
+const WAYLAND_KEYBINDINGS_SCHEMA = 'org.gnome.mutter.wayland.keybindings';
+
+const APP_ALLOWLIST = ['org.gnome.Settings.desktop'];
+const APP_PERMISSIONS_TABLE = 'gnome';
+const APP_PERMISSIONS_ID = 'shortcuts-inhibitor';
+const GRANTED = 'GRANTED';
+const DENIED = 'DENIED';
+
+var DialogResponse = Meta.InhibitShortcutsDialogResponse;
+
+var InhibitShortcutsDialog = GObject.registerClass({
+ Implements: [Meta.InhibitShortcutsDialog],
+ Properties: {
+ 'window': GObject.ParamSpec.override('window', Meta.InhibitShortcutsDialog),
+ },
+}, class InhibitShortcutsDialog extends GObject.Object {
+ _init(window) {
+ super._init();
+ this._window = window;
+
+ this._dialog = new ModalDialog.ModalDialog();
+ this._buildLayout();
+ }
+
+ get window() {
+ return this._window;
+ }
+
+ set window(window) {
+ this._window = window;
+ }
+
+ get _app() {
+ let windowTracker = Shell.WindowTracker.get_default();
+ return windowTracker.get_window_app(this._window);
+ }
+
+ _getRestoreAccel() {
+ let settings = new Gio.Settings({ schema_id: WAYLAND_KEYBINDINGS_SCHEMA });
+ let accel = settings.get_strv('restore-shortcuts')[0] || '';
+ return Gtk.accelerator_get_label.apply(null,
+ Gtk.accelerator_parse(accel));
+ }
+
+ _shouldUsePermStore() {
+ return this._app && !this._app.is_window_backed();
+ }
+
+ async _saveToPermissionStore(grant) {
+ if (!this._shouldUsePermStore() || this._permStore == null)
+ return;
+
+ try {
+ await this._permStore.SetPermissionAsync(APP_PERMISSIONS_TABLE,
+ true,
+ APP_PERMISSIONS_ID,
+ this._app.get_id(),
+ [grant]);
+ } catch (error) {
+ log(error.message);
+ }
+ }
+
+ _buildLayout() {
+ const name = this._app?.get_name() ?? this._window.title;
+
+ let content = new Dialog.MessageDialogContent({
+ title: _('Allow inhibiting shortcuts'),
+ description: name
+ /* Translators: %s is an application name like "Settings" */
+ ? _('The application %s wants to inhibit shortcuts').format(name)
+ : _('An application wants to inhibit shortcuts'),
+ });
+
+ let restoreAccel = this._getRestoreAccel();
+ if (restoreAccel) {
+ let restoreLabel = new St.Label({
+ /* Translators: %s is a keyboard shortcut like "Super+x" */
+ text: _('You can restore shortcuts by pressing %s.').format(restoreAccel),
+ style_class: 'message-dialog-description',
+ });
+ restoreLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ restoreLabel.clutter_text.line_wrap = true;
+ content.add_child(restoreLabel);
+ }
+
+ this._dialog.contentLayout.add_child(content);
+
+ this._dialog.addButton({
+ label: _('Deny'),
+ action: () => {
+ this._saveToPermissionStore(DENIED);
+ this._emitResponse(DialogResponse.DENY);
+ },
+ key: Clutter.KEY_Escape,
+ });
+
+ this._dialog.addButton({
+ label: _('Allow'),
+ action: () => {
+ this._saveToPermissionStore(GRANTED);
+ this._emitResponse(DialogResponse.ALLOW);
+ },
+ default: true,
+ });
+ }
+
+ _emitResponse(response) {
+ this.emit('response', response);
+ this._dialog.close();
+ }
+
+ vfunc_show() {
+ if (this._app && APP_ALLOWLIST.includes(this._app.get_id())) {
+ this._emitResponse(DialogResponse.ALLOW);
+ return;
+ }
+
+ if (!this._shouldUsePermStore()) {
+ this._dialog.open();
+ return;
+ }
+
+ /* Check with the permission store */
+ let appId = this._app.get_id();
+ this._permStore = new PermissionStore.PermissionStore(async (proxy, error) => {
+ if (error) {
+ log(error.message);
+ this._dialog.open();
+ return;
+ }
+
+ try {
+ const [permissions] = await this._permStore.LookupAsync(
+ APP_PERMISSIONS_TABLE, APP_PERMISSIONS_ID);
+
+ if (permissions[appId] === undefined) // Not found
+ this._dialog.open();
+ else if (permissions[appId][0] === GRANTED)
+ this._emitResponse(DialogResponse.ALLOW);
+ else
+ this._emitResponse(DialogResponse.DENY);
+ } catch (err) {
+ this._dialog.open();
+ log(err.message);
+ }
+ });
+ }
+
+ vfunc_hide() {
+ this._dialog.close();
+ }
+});
diff --git a/js/ui/init.js b/js/ui/init.js
new file mode 100644
index 0000000..a0fe633
--- /dev/null
+++ b/js/ui/init.js
@@ -0,0 +1,6 @@
+import { setConsoleLogDomain } from 'console';
+
+setConsoleLogDomain('GNOME Shell');
+
+imports.ui.environment.init();
+imports.ui.main.start();
diff --git a/js/ui/kbdA11yDialog.js b/js/ui/kbdA11yDialog.js
new file mode 100644
index 0000000..6d1608c
--- /dev/null
+++ b/js/ui/kbdA11yDialog.js
@@ -0,0 +1,76 @@
+/* exported KbdA11yDialog */
+const { Clutter, Gio, GObject, Meta } = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const ModalDialog = imports.ui.modalDialog;
+
+const KEYBOARD_A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
+const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
+const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
+
+var KbdA11yDialog = GObject.registerClass(
+class KbdA11yDialog extends GObject.Object {
+ _init() {
+ super._init();
+
+ this._a11ySettings = new Gio.Settings({ schema_id: KEYBOARD_A11Y_SCHEMA });
+
+ let seat = Clutter.get_default_backend().get_default_seat();
+ seat.connect('kbd-a11y-flags-changed',
+ this._showKbdA11yDialog.bind(this));
+ }
+
+ _showKbdA11yDialog(seat, newFlags, whatChanged) {
+ let dialog = new ModalDialog.ModalDialog();
+ let title, description;
+ let key, enabled;
+
+ if (whatChanged & Meta.KeyboardA11yFlags.SLOW_KEYS_ENABLED) {
+ key = KEY_SLOW_KEYS_ENABLED;
+ enabled = (newFlags & Meta.KeyboardA11yFlags.SLOW_KEYS_ENABLED) > 0;
+ title = enabled
+ ? _("Slow Keys Turned On")
+ : _("Slow Keys Turned Off");
+ description = _('You just held down the Shift key for 8 seconds. This is the shortcut ' +
+ 'for the Slow Keys feature, which affects the way your keyboard works.');
+ } else if (whatChanged & Meta.KeyboardA11yFlags.STICKY_KEYS_ENABLED) {
+ key = KEY_STICKY_KEYS_ENABLED;
+ enabled = (newFlags & Meta.KeyboardA11yFlags.STICKY_KEYS_ENABLED) > 0;
+ title = enabled
+ ? _("Sticky Keys Turned On")
+ : _("Sticky Keys Turned Off");
+ description = enabled
+ ? _("You just pressed the Shift key 5 times in a row. This is the shortcut " +
+ "for the Sticky Keys feature, which affects the way your keyboard works.")
+ : _("You just pressed two keys at once, or pressed the Shift key 5 times in a row. " +
+ "This turns off the Sticky Keys feature, which affects the way your keyboard works.");
+ } else {
+ return;
+ }
+
+ let content = new Dialog.MessageDialogContent({ title, description });
+ dialog.contentLayout.add_child(content);
+
+ dialog.addButton({
+ label: enabled ? _('Leave On') : _('Turn On'),
+ action: () => {
+ this._a11ySettings.set_boolean(key, true);
+ dialog.close();
+ },
+ default: enabled,
+ key: !enabled ? Clutter.KEY_Escape : null,
+ });
+
+ dialog.addButton({
+ label: enabled ? _('Turn Off') : _('Leave Off'),
+ action: () => {
+ this._a11ySettings.set_boolean(key, false);
+ dialog.close();
+ },
+ default: !enabled,
+ key: enabled ? Clutter.KEY_Escape : null,
+ });
+
+ dialog.open();
+ }
+});
diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js
new file mode 100644
index 0000000..be128d3
--- /dev/null
+++ b/js/ui/keyboard.js
@@ -0,0 +1,2275 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported KeyboardManager */
+
+const {Clutter, Gio, GLib, GObject, Graphene, IBus, Meta, Shell, St} = imports.gi;
+const Signals = imports.misc.signals;
+
+const EdgeDragAction = imports.ui.edgeDragAction;
+const InputSourceManager = imports.ui.status.keyboard;
+const IBusManager = imports.misc.ibusManager;
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const PageIndicators = imports.ui.pageIndicators;
+const PopupMenu = imports.ui.popupMenu;
+const SwipeTracker = imports.ui.swipeTracker;
+
+var KEYBOARD_ANIMATION_TIME = 150;
+var KEYBOARD_REST_TIME = KEYBOARD_ANIMATION_TIME * 2;
+var KEY_LONG_PRESS_TIME = 250;
+
+const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
+const SHOW_KEYBOARD = 'screen-keyboard-enabled';
+const EMOJI_PAGE_SEPARATION = 32;
+
+/* KeyContainer puts keys in a grid where a 1:1 key takes this size */
+const KEY_SIZE = 2;
+
+const KEY_RELEASE_TIMEOUT = 50;
+const BACKSPACE_WORD_DELETE_THRESHOLD = 50;
+
+var AspectContainer = GObject.registerClass(
+class AspectContainer extends St.Widget {
+ _init(params) {
+ super._init(params);
+ this._ratio = 1;
+ }
+
+ setRatio(relWidth, relHeight) {
+ this._ratio = relWidth / relHeight;
+ this.queue_relayout();
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ let [min, nat] = super.vfunc_get_preferred_width(forHeight);
+
+ if (forHeight > 0)
+ nat = forHeight * this._ratio;
+
+ return [min, nat];
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ let [min, nat] = super.vfunc_get_preferred_height(forWidth);
+
+ if (forWidth > 0)
+ nat = forWidth / this._ratio;
+
+ return [min, nat];
+ }
+
+ vfunc_allocate(box) {
+ if (box.get_width() > 0 && box.get_height() > 0) {
+ let sizeRatio = box.get_width() / box.get_height();
+ if (sizeRatio >= this._ratio) {
+ /* Restrict horizontally */
+ let width = box.get_height() * this._ratio;
+ let diff = box.get_width() - width;
+
+ box.x1 += Math.floor(diff / 2);
+ box.x2 -= Math.ceil(diff / 2);
+ }
+ }
+
+ super.vfunc_allocate(box);
+ }
+});
+
+var KeyContainer = GObject.registerClass(
+class KeyContainer extends St.Widget {
+ _init() {
+ const gridLayout = new Clutter.GridLayout({
+ orientation: Clutter.Orientation.HORIZONTAL,
+ column_homogeneous: true,
+ row_homogeneous: true,
+ });
+ super._init({
+ layout_manager: gridLayout,
+ x_expand: true,
+ y_expand: true,
+ });
+ this._gridLayout = gridLayout;
+ this._currentRow = 0;
+ this._currentCol = 0;
+ this._maxCols = 0;
+
+ this._currentRow = null;
+ this._rows = [];
+ }
+
+ appendRow() {
+ this._currentRow++;
+ this._currentCol = 0;
+
+ let row = {
+ keys: [],
+ width: 0,
+ };
+ this._rows.push(row);
+ }
+
+ appendKey(key, width = 1, height = 1) {
+ let keyInfo = {
+ key,
+ left: this._currentCol,
+ top: this._currentRow,
+ width,
+ height,
+ };
+
+ let row = this._rows[this._rows.length - 1];
+ row.keys.push(keyInfo);
+ row.width += width;
+
+ this._currentCol += width;
+ this._maxCols = Math.max(this._currentCol, this._maxCols);
+ }
+
+ layoutButtons() {
+ let nCol = 0, nRow = 0;
+
+ for (let i = 0; i < this._rows.length; i++) {
+ let row = this._rows[i];
+
+ /* When starting a new row, see if we need some padding */
+ if (nCol == 0) {
+ let diff = this._maxCols - row.width;
+ if (diff >= 1)
+ nCol = diff * KEY_SIZE / 2;
+ else
+ nCol = diff * KEY_SIZE;
+ }
+
+ for (let j = 0; j < row.keys.length; j++) {
+ let keyInfo = row.keys[j];
+ let width = keyInfo.width * KEY_SIZE;
+ let height = keyInfo.height * KEY_SIZE;
+
+ this._gridLayout.attach(keyInfo.key, nCol, nRow, width, height);
+ nCol += width;
+ }
+
+ nRow += KEY_SIZE;
+ nCol = 0;
+ }
+ }
+
+ getRatio() {
+ return [this._maxCols, this._rows.length];
+ }
+});
+
+var Suggestions = GObject.registerClass(
+class Suggestions extends St.BoxLayout {
+ _init() {
+ super._init({
+ style_class: 'word-suggestions',
+ vertical: false,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this.show();
+ }
+
+ add(word, callback) {
+ let button = new St.Button({ label: word });
+ button.connect('button-press-event', () => {
+ callback();
+ return Clutter.EVENT_STOP;
+ });
+ button.connect('touch-event', (actor, event) => {
+ if (event.type() !== Clutter.EventType.TOUCH_BEGIN)
+ return Clutter.EVENT_PROPAGATE;
+
+ callback();
+ return Clutter.EVENT_STOP;
+ });
+ this.add_child(button);
+ }
+
+ clear() {
+ this.remove_all_children();
+ }
+
+ setVisible(visible) {
+ for (const child of this)
+ child.visible = visible;
+ }
+});
+
+var LanguageSelectionPopup = class extends PopupMenu.PopupMenu {
+ constructor(actor) {
+ super(actor, 0.5, St.Side.BOTTOM);
+
+ let inputSourceManager = InputSourceManager.getInputSourceManager();
+ let inputSources = inputSourceManager.inputSources;
+
+ let item;
+ for (let i in inputSources) {
+ let is = inputSources[i];
+
+ item = this.addAction(is.displayName, () => {
+ inputSourceManager.activateInputSource(is, true);
+ });
+ item.can_focus = false;
+ item.setOrnament(is === inputSourceManager.currentSource
+ ? PopupMenu.Ornament.DOT
+ : PopupMenu.Ornament.NONE);
+ }
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ item = this.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
+ item.can_focus = false;
+
+ actor.connectObject('notify::mapped', () => {
+ if (!actor.is_mapped())
+ this.close(true);
+ }, this);
+ }
+
+ _onCapturedEvent(actor, event) {
+ const targetActor = global.stage.get_event_actor(event);
+
+ if (targetActor === this.actor ||
+ this.actor.contains(targetActor))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (event.type() == Clutter.EventType.BUTTON_RELEASE || event.type() == Clutter.EventType.TOUCH_END)
+ this.close(true);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ open(animate) {
+ super.open(animate);
+ global.stage.connectObject(
+ 'captured-event', this._onCapturedEvent.bind(this), this);
+ }
+
+ close(animate) {
+ super.close(animate);
+ global.stage.disconnectObject(this);
+ }
+
+ destroy() {
+ global.stage.disconnectObject(this);
+ this.sourceActor.disconnectObject(this);
+ super.destroy();
+ }
+};
+
+var Key = GObject.registerClass({
+ Signals: {
+ 'long-press': {},
+ 'pressed': {},
+ 'released': {},
+ 'commit': {param_types: [GObject.TYPE_UINT, GObject.TYPE_STRING]},
+ },
+}, class Key extends St.BoxLayout {
+ _init(params, extendedKeys = []) {
+ const {label, iconName, commitString, keyval} = {keyval: 0, ...params};
+ super._init({ style_class: 'key-container' });
+
+ this._keyval = parseInt(keyval, 16);
+ this.keyButton = this._makeKey(commitString, label, iconName);
+
+ /* Add the key in a container, so keys can be padded without losing
+ * logical proportions between those.
+ */
+ this.add_child(this.keyButton);
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._extendedKeys = extendedKeys;
+ this._extendedKeyboard = null;
+ this._pressTimeoutId = 0;
+ this._capturedPress = false;
+ }
+
+ get iconName() {
+ return this._icon.icon_name;
+ }
+
+ set iconName(value) {
+ this._icon.icon_name = value;
+ }
+
+ _onDestroy() {
+ if (this._boxPointer) {
+ this._boxPointer.destroy();
+ this._boxPointer = null;
+ }
+
+ this.cancel();
+ }
+
+ _ensureExtendedKeysPopup() {
+ if (this._extendedKeys.length === 0)
+ return;
+
+ if (this._boxPointer)
+ return;
+
+ this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM);
+ this._boxPointer.hide();
+ Main.layoutManager.addTopChrome(this._boxPointer);
+ this._boxPointer.setPosition(this.keyButton, 0.5);
+
+ // Adds style to existing keyboard style to avoid repetition
+ this._boxPointer.add_style_class_name('keyboard-subkeys');
+ this._getExtendedKeys();
+ this.keyButton._extendedKeys = this._extendedKeyboard;
+ }
+
+ _getKeyvalFromString(string) {
+ let unicode = string?.length ? string.charCodeAt(0) : undefined;
+ return Clutter.unicode_to_keysym(unicode);
+ }
+
+ _press(button) {
+ if (button === this.keyButton) {
+ this._pressTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ KEY_LONG_PRESS_TIME,
+ () => {
+ this._pressTimeoutId = 0;
+
+ this.emit('long-press');
+
+ if (this._extendedKeys.length > 0) {
+ this._touchPressSlot = null;
+ this._ensureExtendedKeysPopup();
+ this.keyButton.set_hover(false);
+ this.keyButton.fake_release();
+ this._showSubkeys();
+ }
+
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ this.emit('pressed');
+ this._pressed = true;
+ }
+
+ _release(button, commitString) {
+ if (this._pressTimeoutId != 0) {
+ GLib.source_remove(this._pressTimeoutId);
+ this._pressTimeoutId = 0;
+ }
+
+ let keyval;
+ if (button === this.keyButton)
+ keyval = this._keyval;
+ if (!keyval && commitString)
+ keyval = this._getKeyvalFromString(commitString);
+ console.assert(keyval !== undefined, 'Need keyval or commitString');
+
+ if (this._pressed && (commitString || keyval))
+ this.emit('commit', keyval, commitString || '');
+
+ this.emit('released');
+ this._hideSubkeys();
+ this._pressed = false;
+ }
+
+ cancel() {
+ if (this._pressTimeoutId != 0) {
+ GLib.source_remove(this._pressTimeoutId);
+ this._pressTimeoutId = 0;
+ }
+ this._touchPressSlot = null;
+ this.keyButton.set_hover(false);
+ this.keyButton.fake_release();
+ }
+
+ _onCapturedEvent(actor, event) {
+ let type = event.type();
+ let press = type == Clutter.EventType.BUTTON_PRESS || type == Clutter.EventType.TOUCH_BEGIN;
+ let release = type == Clutter.EventType.BUTTON_RELEASE || type == Clutter.EventType.TOUCH_END;
+ const targetActor = global.stage.get_event_actor(event);
+
+ if (targetActor === this._boxPointer.bin ||
+ this._boxPointer.bin.contains(targetActor))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (press)
+ this._capturedPress = true;
+ else if (release && this._capturedPress)
+ this._hideSubkeys();
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _showSubkeys() {
+ this._boxPointer.open(BoxPointer.PopupAnimation.FULL);
+ global.stage.connectObject(
+ 'captured-event', this._onCapturedEvent.bind(this), this);
+ this.keyButton.connectObject('notify::mapped', () => {
+ if (!this.keyButton.is_mapped())
+ this._hideSubkeys();
+ }, this);
+ }
+
+ _hideSubkeys() {
+ if (this._boxPointer)
+ this._boxPointer.close(BoxPointer.PopupAnimation.FULL);
+ global.stage.disconnectObject(this);
+ this.keyButton.disconnectObject(this);
+ this._capturedPress = false;
+ }
+
+ _makeKey(commitString, label, icon) {
+ let button = new St.Button({
+ style_class: 'keyboard-key',
+ x_expand: true,
+ });
+
+ if (icon) {
+ const child = new St.Icon({icon_name: icon});
+ button.set_child(child);
+ this._icon = child;
+ } else if (label) {
+ button.set_label(label);
+ } else if (commitString) {
+ const str = GLib.markup_escape_text(commitString, -1);
+ button.set_label(str);
+ }
+
+ button.keyWidth = 1;
+ button.connect('button-press-event', () => {
+ this._press(button, commitString);
+ button.add_style_pseudo_class('active');
+ return Clutter.EVENT_STOP;
+ });
+ button.connect('button-release-event', () => {
+ this._release(button, commitString);
+ button.remove_style_pseudo_class('active');
+ return Clutter.EVENT_STOP;
+ });
+ button.connect('touch-event', (actor, event) => {
+ // We only handle touch events here on wayland. On X11
+ // we do get emulated pointer events, which already works
+ // for single-touch cases. Besides, the X11 passive touch grab
+ // set up by Mutter will make us see first the touch events
+ // and later the pointer events, so it will look like two
+ // unrelated series of events, we want to avoid double handling
+ // in these cases.
+ if (!Meta.is_wayland_compositor())
+ return Clutter.EVENT_PROPAGATE;
+
+ const slot = event.get_event_sequence().get_slot();
+
+ if (!this._touchPressSlot &&
+ event.type() == Clutter.EventType.TOUCH_BEGIN) {
+ this._touchPressSlot = slot;
+ this._press(button, commitString);
+ button.add_style_pseudo_class('active');
+ } else if (event.type() === Clutter.EventType.TOUCH_END) {
+ if (!this._touchPressSlot ||
+ this._touchPressSlot === slot) {
+ this._release(button, commitString);
+ button.remove_style_pseudo_class('active');
+ }
+
+ if (this._touchPressSlot === slot)
+ this._touchPressSlot = null;
+ }
+ return Clutter.EVENT_STOP;
+ });
+
+ return button;
+ }
+
+ _getExtendedKeys() {
+ this._extendedKeyboard = new St.BoxLayout({
+ style_class: 'key-container',
+ vertical: false,
+ });
+ for (let i = 0; i < this._extendedKeys.length; ++i) {
+ let extendedKey = this._extendedKeys[i];
+ let key = this._makeKey(extendedKey);
+
+ key.extendedKey = extendedKey;
+ this._extendedKeyboard.add(key);
+
+ key.set_size(...this.keyButton.allocation.get_size());
+ this.keyButton.connect('notify::allocation',
+ () => key.set_size(...this.keyButton.allocation.get_size()));
+ }
+ this._boxPointer.bin.add_actor(this._extendedKeyboard);
+ }
+
+ get subkeys() {
+ return this._boxPointer;
+ }
+
+ setWidth(width) {
+ this.keyButton.keyWidth = width;
+ }
+
+ setLatched(latched) {
+ if (latched)
+ this.keyButton.add_style_pseudo_class('latched');
+ else
+ this.keyButton.remove_style_pseudo_class('latched');
+ }
+});
+
+var KeyboardModel = class {
+ constructor(groupName) {
+ let names = [groupName];
+ if (groupName.includes('+'))
+ names.push(groupName.replace(/\+.*/, ''));
+ names.push('us');
+
+ for (let i = 0; i < names.length; i++) {
+ try {
+ this._model = this._loadModel(names[i]);
+ break;
+ } catch (e) {
+ }
+ }
+ }
+
+ _loadModel(groupName) {
+ const file = Gio.File.new_for_uri(
+ `resource:///org/gnome/shell/osk-layouts/${groupName}.json`);
+ let [success_, contents] = file.load_contents(null);
+
+ const decoder = new TextDecoder();
+ return JSON.parse(decoder.decode(contents));
+ }
+
+ getLevels() {
+ return this._model.levels;
+ }
+
+ getKeysForLevel(levelName) {
+ return this._model.levels.find(level => level == levelName);
+ }
+};
+
+var FocusTracker = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._rect = null;
+
+ global.display.connectObject(
+ 'notify::focus-window', () => {
+ this._setCurrentWindow(global.display.focus_window);
+ this.emit('window-changed', this._currentWindow);
+ },
+ 'grab-op-begin', (display, window, op) => {
+ if (window === this._currentWindow &&
+ (op === Meta.GrabOp.MOVING || op === Meta.GrabOp.KEYBOARD_MOVING))
+ this.emit('window-grabbed');
+ }, this);
+
+ this._setCurrentWindow(global.display.focus_window);
+
+ /* Valid for wayland clients */
+ Main.inputMethod.connectObject('cursor-location-changed',
+ (o, rect) => this._setCurrentRect(rect), this);
+
+ this._ibusManager = IBusManager.getIBusManager();
+ this._ibusManager.connectObject(
+ 'set-cursor-location', (manager, rect) => {
+ /* Valid for X11 clients only */
+ if (Main.inputMethod.currentFocus)
+ return;
+
+ const grapheneRect = new Graphene.Rect();
+ grapheneRect.init(rect.x, rect.y, rect.width, rect.height);
+
+ this._setCurrentRect(grapheneRect);
+ },
+ 'focus-in', () => this.emit('focus-changed', true),
+ 'focus-out', () => this.emit('focus-changed', false),
+ this);
+ }
+
+ destroy() {
+ this._currentWindow?.disconnectObject(this);
+ global.display.disconnectObject(this);
+ Main.inputMethod.disconnectObject(this);
+ this._ibusManager.disconnectObject(this);
+ }
+
+ get currentWindow() {
+ return this._currentWindow;
+ }
+
+ _setCurrentWindow(window) {
+ this._currentWindow?.disconnectObject(this);
+
+ this._currentWindow = window;
+
+ if (this._currentWindow) {
+ this._currentWindow.connectObject(
+ 'position-changed', () => this.emit('window-moved'), this);
+ }
+ }
+
+ _setCurrentRect(rect) {
+ // Some clients give us 0-sized rects, in that case set size to 1
+ if (rect.size.width <= 0)
+ rect.size.width = 1;
+ if (rect.size.height <= 0)
+ rect.size.height = 1;
+
+ if (this._currentWindow) {
+ const frameRect = this._currentWindow.get_frame_rect();
+ const grapheneFrameRect = new Graphene.Rect();
+ grapheneFrameRect.init(frameRect.x, frameRect.y,
+ frameRect.width, frameRect.height);
+
+ const rectInsideFrameRect = grapheneFrameRect.intersection(rect)[0];
+ if (!rectInsideFrameRect)
+ return;
+ }
+
+ if (this._rect && this._rect.equal(rect))
+ return;
+
+ this._rect = rect;
+ this.emit('position-changed');
+ }
+
+ getCurrentRect() {
+ const rect = {
+ x: this._rect.origin.x,
+ y: this._rect.origin.y,
+ width: this._rect.size.width,
+ height: this._rect.size.height,
+ };
+
+ return rect;
+ }
+};
+
+var EmojiPager = GObject.registerClass({
+ Properties: {
+ 'delta': GObject.ParamSpec.int(
+ 'delta', 'delta', 'delta',
+ GObject.ParamFlags.READWRITE,
+ GLib.MININT32, GLib.MAXINT32, 0),
+ },
+ Signals: {
+ 'emoji': { param_types: [GObject.TYPE_STRING] },
+ 'page-changed': {
+ param_types: [GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_INT],
+ },
+ },
+}, class EmojiPager extends St.Widget {
+ _init(sections) {
+ super._init({
+ layout_manager: new Clutter.BinLayout(),
+ reactive: true,
+ clip_to_allocation: true,
+ y_expand: true,
+ });
+ this._sections = sections;
+
+ this._pages = [];
+ this._panel = null;
+ this._curPage = null;
+ this._followingPage = null;
+ this._followingPanel = null;
+ this._currentKey = null;
+ this._delta = 0;
+ this._width = null;
+
+ const swipeTracker = new SwipeTracker.SwipeTracker(this,
+ Clutter.Orientation.HORIZONTAL,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ {allowDrag: true, allowScroll: true});
+ swipeTracker.connect('begin', this._onSwipeBegin.bind(this));
+ swipeTracker.connect('update', this._onSwipeUpdate.bind(this));
+ swipeTracker.connect('end', this._onSwipeEnd.bind(this));
+ this._swipeTracker = swipeTracker;
+
+ this.connect('destroy', () => this._onDestroy());
+
+ this.bind_property(
+ 'visible', this._swipeTracker, 'enabled',
+ GObject.BindingFlags.DEFAULT);
+ }
+
+ _onDestroy() {
+ if (this._swipeTracker) {
+ this._swipeTracker.destroy();
+ delete this._swipeTracker;
+ }
+ }
+
+ get delta() {
+ return this._delta;
+ }
+
+ set delta(value) {
+ if (this._delta == value)
+ return;
+
+ this._delta = value;
+ this.notify('delta');
+
+ let followingPage = this.getFollowingPage();
+
+ if (this._followingPage != followingPage) {
+ if (this._followingPanel) {
+ this._followingPanel.destroy();
+ this._followingPanel = null;
+ }
+
+ if (followingPage != null) {
+ this._followingPanel = this._generatePanel(followingPage);
+ this.add_child(this._followingPanel);
+ }
+
+ this._followingPage = followingPage;
+ }
+
+ const multiplier = this.text_direction === Clutter.TextDirection.RTL
+ ? -1 : 1;
+
+ this._panel.translation_x = value * multiplier;
+ if (this._followingPanel) {
+ const translation = value < 0
+ ? this._width + EMOJI_PAGE_SEPARATION
+ : -this._width - EMOJI_PAGE_SEPARATION;
+
+ this._followingPanel.translation_x =
+ (value * multiplier) + (translation * multiplier);
+ }
+ }
+
+ _prevPage(nPage) {
+ return (nPage + this._pages.length - 1) % this._pages.length;
+ }
+
+ _nextPage(nPage) {
+ return (nPage + 1) % this._pages.length;
+ }
+
+ getFollowingPage() {
+ if (this.delta == 0)
+ return null;
+
+ if (this.delta < 0)
+ return this._nextPage(this._curPage);
+ else
+ return this._prevPage(this._curPage);
+ }
+
+ _onSwipeUpdate(tracker, progress) {
+ this.delta = -progress * this._width;
+
+ if (this._currentKey != null) {
+ this._currentKey.cancel();
+ this._currentKey = null;
+ }
+
+ return false;
+ }
+
+ _onSwipeBegin(tracker) {
+ this._width = this.width;
+ const points = [-1, 0, 1];
+ tracker.confirmSwipe(this._width, points, 0, 0);
+ }
+
+ _onSwipeEnd(tracker, duration, endProgress) {
+ this.remove_all_transitions();
+ if (endProgress === 0) {
+ this.ease_property('delta', 0, {duration});
+ } else {
+ const value = endProgress < 0
+ ? this._width + EMOJI_PAGE_SEPARATION
+ : -this._width - EMOJI_PAGE_SEPARATION;
+ this.ease_property('delta', value, {
+ duration,
+ onComplete: () => {
+ this.setCurrentPage(this.getFollowingPage());
+ },
+ });
+ }
+ }
+
+ _initPagingInfo() {
+ this._pages = [];
+
+ for (let i = 0; i < this._sections.length; i++) {
+ let section = this._sections[i];
+ let itemsPerPage = this._nCols * this._nRows;
+ let nPages = Math.ceil(section.keys.length / itemsPerPage);
+ let page = -1;
+ let pageKeys;
+
+ for (let j = 0; j < section.keys.length; j++) {
+ if (j % itemsPerPage == 0) {
+ page++;
+ pageKeys = [];
+ this._pages.push({ pageKeys, nPages, page, section: this._sections[i] });
+ }
+
+ pageKeys.push(section.keys[j]);
+ }
+ }
+ }
+
+ _lookupSection(section, nPage) {
+ for (let i = 0; i < this._pages.length; i++) {
+ let page = this._pages[i];
+
+ if (page.section == section && page.page == nPage)
+ return i;
+ }
+
+ return -1;
+ }
+
+ _generatePanel(nPage) {
+ const gridLayout = new Clutter.GridLayout({
+ orientation: Clutter.Orientation.HORIZONTAL,
+ column_homogeneous: true,
+ row_homogeneous: true,
+ });
+ const panel = new St.Widget({
+ layout_manager: gridLayout,
+ style_class: 'emoji-page',
+ x_expand: true,
+ y_expand: true,
+ });
+
+ /* Set an expander actor so all proportions are right despite the panel
+ * not having all rows/cols filled in.
+ */
+ let expander = new Clutter.Actor();
+ gridLayout.attach(expander, 0, 0, this._nCols, this._nRows);
+
+ let page = this._pages[nPage];
+ let col = 0;
+ let row = 0;
+
+ for (let i = 0; i < page.pageKeys.length; i++) {
+ let modelKey = page.pageKeys[i];
+ let key = new Key({commitString: modelKey.label}, modelKey.variants);
+
+ key.keyButton.set_button_mask(0);
+
+ key.connect('pressed', () => {
+ this._currentKey = key;
+ });
+ key.connect('commit', (actor, keyval, str) => {
+ if (this._currentKey != key)
+ return;
+ this._currentKey = null;
+ this.emit('emoji', str);
+ });
+
+ gridLayout.attach(key, col, row, 1, 1);
+
+ col++;
+ if (col >= this._nCols) {
+ col = 0;
+ row++;
+ }
+ }
+
+ return panel;
+ }
+
+ setCurrentPage(nPage) {
+ if (this._curPage == nPage)
+ return;
+
+ this._curPage = nPage;
+
+ if (this._panel) {
+ this._panel.destroy();
+ this._panel = null;
+ }
+
+ /* Reuse followingPage if possible */
+ if (nPage == this._followingPage) {
+ this._panel = this._followingPanel;
+ this._followingPanel = null;
+ }
+
+ if (this._followingPanel)
+ this._followingPanel.destroy();
+
+ this._followingPanel = null;
+ this._followingPage = null;
+ this._delta = 0;
+
+ if (!this._panel) {
+ this._panel = this._generatePanel(nPage);
+ this.add_child(this._panel);
+ }
+
+ let page = this._pages[nPage];
+ this.emit('page-changed', page.section.label, page.page, page.nPages);
+ }
+
+ setCurrentSection(section, nPage) {
+ for (let i = 0; i < this._pages.length; i++) {
+ let page = this._pages[i];
+
+ if (page.section == section && page.page == nPage) {
+ this.setCurrentPage(i);
+ break;
+ }
+ }
+ }
+
+ setRatio(nCols, nRows) {
+ this._nCols = nCols;
+ this._nRows = nRows;
+ this._initPagingInfo();
+ }
+});
+
+var EmojiSelection = GObject.registerClass({
+ Signals: {
+ 'emoji-selected': { param_types: [GObject.TYPE_STRING] },
+ 'close-request': {},
+ 'toggle': {},
+ },
+}, class EmojiSelection extends St.Widget {
+ _init() {
+ const gridLayout = new Clutter.GridLayout({
+ orientation: Clutter.Orientation.HORIZONTAL,
+ column_homogeneous: true,
+ row_homogeneous: true,
+ });
+ super._init({
+ layout_manager: gridLayout,
+ style_class: 'emoji-panel',
+ x_expand: true,
+ y_expand: true,
+ text_direction: global.stage.text_direction,
+ });
+
+ this._sections = [
+ { first: 'grinning face', label: '🙂️' },
+ { first: 'selfie', label: '👍️' },
+ { first: 'monkey face', label: '🌷️' },
+ { first: 'grapes', label: '🍴️' },
+ { first: 'globe showing Europe-Africa', label: '✈️' },
+ { first: 'jack-o-lantern', label: '🏃️' },
+ { first: 'muted speaker', label: '🔔️' },
+ { first: 'ATM sign', label: '❤️' },
+ { first: 'chequered flag', label: '🚩️' },
+ ];
+
+ this._gridLayout = gridLayout;
+ this._populateSections();
+
+ this._pagerBox = new Clutter.Actor({
+ layout_manager: new Clutter.BoxLayout({
+ orientation: Clutter.Orientation.VERTICAL,
+ }),
+ });
+
+ this._emojiPager = new EmojiPager(this._sections);
+ this._emojiPager.connect('page-changed', (pager, sectionLabel, page, nPages) => {
+ this._onPageChanged(sectionLabel, page, nPages);
+ });
+ this._emojiPager.connect('emoji', (pager, str) => {
+ this.emit('emoji-selected', str);
+ });
+ this._pagerBox.add_child(this._emojiPager);
+
+ this._pageIndicator = new PageIndicators.PageIndicators(
+ Clutter.Orientation.HORIZONTAL);
+ this._pageIndicator.y_expand = false;
+ this._pageIndicator.y_align = Clutter.ActorAlign.START;
+ this._pagerBox.add_child(this._pageIndicator);
+ this._pageIndicator.setReactive(false);
+
+ this._emojiPager.connect('notify::delta', () => {
+ this._updateIndicatorPosition();
+ });
+
+ this._bottomRow = this._createBottomRow();
+
+ this._curPage = 0;
+ }
+
+ vfunc_map() {
+ this._emojiPager.setCurrentPage(0);
+ super.vfunc_map();
+ }
+
+ _onPageChanged(sectionLabel, page, nPages) {
+ this._curPage = page;
+ this._pageIndicator.setNPages(nPages);
+ this._updateIndicatorPosition();
+
+ for (let i = 0; i < this._sections.length; i++) {
+ let sect = this._sections[i];
+ sect.button.setLatched(sectionLabel == sect.label);
+ }
+ }
+
+ _updateIndicatorPosition() {
+ this._pageIndicator.setCurrentPosition(this._curPage -
+ this._emojiPager.delta / this._emojiPager.width);
+ }
+
+ _findSection(emoji) {
+ for (let i = 0; i < this._sections.length; i++) {
+ if (this._sections[i].first == emoji)
+ return this._sections[i];
+ }
+
+ return null;
+ }
+
+ _populateSections() {
+ let file = Gio.File.new_for_uri('resource:///org/gnome/shell/osk-layouts/emoji.json');
+ let [success_, contents] = file.load_contents(null);
+
+ let emoji = JSON.parse(new TextDecoder().decode(contents));
+
+ let variants = [];
+ let currentKey = 0;
+ let currentSection = null;
+
+ for (let i = 0; i < emoji.length; i++) {
+ /* Group variants of a same emoji so they appear on the key popover */
+ if (emoji[i].name.startsWith(emoji[currentKey].name)) {
+ variants.push(emoji[i].char);
+ if (i < emoji.length - 1)
+ continue;
+ }
+
+ let newSection = this._findSection(emoji[currentKey].name);
+ if (newSection != null) {
+ currentSection = newSection;
+ currentSection.keys = [];
+ }
+
+ /* Create the key */
+ let label = emoji[currentKey].char + String.fromCharCode(0xFE0F);
+ currentSection.keys.push({ label, variants });
+ currentKey = i;
+ variants = [];
+ }
+ }
+
+ _createBottomRow() {
+ let row = new KeyContainer();
+ let key;
+
+ row.appendRow();
+
+ key = new Key({label: 'ABC'}, []);
+ key.keyButton.add_style_class_name('default-key');
+ key.connect('released', () => this.emit('toggle'));
+ row.appendKey(key, 1.5);
+
+ for (let i = 0; i < this._sections.length; i++) {
+ let section = this._sections[i];
+
+ key = new Key({label: section.label}, []);
+ key.connect('released', () => this._emojiPager.setCurrentSection(section, 0));
+ row.appendKey(key);
+
+ section.button = key;
+ }
+
+ key = new Key({iconName: 'go-down-symbolic'});
+ key.keyButton.add_style_class_name('default-key');
+ key.keyButton.add_style_class_name('hide-key');
+ key.connect('released', () => {
+ this.emit('close-request');
+ });
+ row.appendKey(key);
+ row.layoutButtons();
+
+ const actor = new AspectContainer({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ });
+ actor.add_child(row);
+
+ return actor;
+ }
+
+ setRatio(nCols, nRows) {
+ this._emojiPager.setRatio(Math.floor(nCols), Math.floor(nRows) - 1);
+ this._bottomRow.setRatio(nCols, 1);
+
+ // (Re)attach actors so the emoji panel fits the ratio and
+ // the bottom row is ensured to take 1 row high.
+ if (this._pagerBox.get_parent())
+ this.remove_child(this._pagerBox);
+ if (this._bottomRow.get_parent())
+ this.remove_child(this._bottomRow);
+
+ this._gridLayout.attach(this._pagerBox, 0, 0, 1, Math.floor(nRows) - 1);
+ this._gridLayout.attach(this._bottomRow, 0, Math.floor(nRows) - 1, 1, 1);
+ }
+});
+
+var Keypad = GObject.registerClass({
+ Signals: {
+ 'keyval': { param_types: [GObject.TYPE_UINT] },
+ },
+}, class Keypad extends AspectContainer {
+ _init() {
+ let keys = [
+ { label: '1', keyval: Clutter.KEY_1, left: 0, top: 0 },
+ { label: '2', keyval: Clutter.KEY_2, left: 1, top: 0 },
+ { label: '3', keyval: Clutter.KEY_3, left: 2, top: 0 },
+ { label: '4', keyval: Clutter.KEY_4, left: 0, top: 1 },
+ { label: '5', keyval: Clutter.KEY_5, left: 1, top: 1 },
+ { label: '6', keyval: Clutter.KEY_6, left: 2, top: 1 },
+ { label: '7', keyval: Clutter.KEY_7, left: 0, top: 2 },
+ { label: '8', keyval: Clutter.KEY_8, left: 1, top: 2 },
+ { label: '9', keyval: Clutter.KEY_9, left: 2, top: 2 },
+ { label: '0', keyval: Clutter.KEY_0, left: 1, top: 3 },
+ { keyval: Clutter.KEY_BackSpace, icon: 'edit-clear-symbolic', left: 3, top: 0 },
+ { keyval: Clutter.KEY_Return, extraClassName: 'enter-key', icon: 'keyboard-enter-symbolic', left: 3, top: 1, height: 2 },
+ ];
+
+ super._init({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ });
+
+ const gridLayout = new Clutter.GridLayout({
+ orientation: Clutter.Orientation.HORIZONTAL,
+ column_homogeneous: true,
+ row_homogeneous: true,
+ });
+ this._box = new St.Widget({ layout_manager: gridLayout, x_expand: true, y_expand: true });
+ this.add_child(this._box);
+
+ for (let i = 0; i < keys.length; i++) {
+ let cur = keys[i];
+ let key = new Key({
+ label: cur.label,
+ iconName: cur.icon,
+ });
+
+ if (keys[i].extraClassName)
+ key.keyButton.add_style_class_name(cur.extraClassName);
+
+ let w, h;
+ w = cur.width || 1;
+ h = cur.height || 1;
+ gridLayout.attach(key, cur.left, cur.top, w, h);
+
+ key.connect('released', () => {
+ this.emit('keyval', cur.keyval);
+ });
+ }
+ }
+});
+
+var KeyboardManager = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._keyboard = null;
+ this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA });
+ this._a11yApplicationsSettings.connect('changed', this._syncEnabled.bind(this));
+
+ this._seat = Clutter.get_default_backend().get_default_seat();
+ this._seat.connect('notify::touch-mode', this._syncEnabled.bind(this));
+
+ this._lastDevice = null;
+ global.backend.connect('last-device-changed', (backend, device) => {
+ if (device.device_type === Clutter.InputDeviceType.KEYBOARD_DEVICE)
+ return;
+
+ this._lastDevice = device;
+ this._syncEnabled();
+ });
+
+ const mode = Shell.ActionMode.ALL & ~Shell.ActionMode.LOCK_SCREEN;
+ const bottomDragAction = new EdgeDragAction.EdgeDragAction(St.Side.BOTTOM, mode);
+ bottomDragAction.connect('activated', () => {
+ if (this._keyboard)
+ this._keyboard.gestureActivate(Main.layoutManager.bottomIndex);
+ });
+ bottomDragAction.connect('progress', (_action, progress) => {
+ if (this._keyboard)
+ this._keyboard.gestureProgress(progress);
+ });
+ bottomDragAction.connect('gesture-cancel', () => {
+ if (this._keyboard)
+ this._keyboard.gestureCancel();
+ });
+ global.stage.add_action_full('osk', Clutter.EventPhase.CAPTURE, bottomDragAction);
+ this._bottomDragAction = bottomDragAction;
+
+ this._syncEnabled();
+ }
+
+ _lastDeviceIsTouchscreen() {
+ if (!this._lastDevice)
+ return false;
+
+ let deviceType = this._lastDevice.get_device_type();
+ return deviceType == Clutter.InputDeviceType.TOUCHSCREEN_DEVICE;
+ }
+
+ _syncEnabled() {
+ let enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD);
+ let autoEnabled = this._seat.get_touch_mode() && this._lastDeviceIsTouchscreen();
+ let enabled = enableKeyboard || autoEnabled;
+
+ if (!enabled && !this._keyboard)
+ return;
+
+ if (enabled && !this._keyboard) {
+ this._keyboard = new Keyboard();
+ this._keyboard.connect('visibility-changed', () => {
+ this.emit('visibility-changed');
+ this._bottomDragAction.enabled = !this._keyboard.visible;
+ });
+ } else if (!enabled && this._keyboard) {
+ this._keyboard.setCursorLocation(null);
+ this._keyboard.destroy();
+ this._keyboard = null;
+ this._bottomDragAction.enabled = true;
+ }
+ }
+
+ get keyboardActor() {
+ return this._keyboard;
+ }
+
+ get visible() {
+ return this._keyboard && this._keyboard.visible;
+ }
+
+ open(monitor) {
+ Main.layoutManager.keyboardIndex = monitor;
+
+ if (this._keyboard)
+ this._keyboard.open();
+ }
+
+ close() {
+ if (this._keyboard)
+ this._keyboard.close();
+ }
+
+ addSuggestion(text, callback) {
+ if (this._keyboard)
+ this._keyboard.addSuggestion(text, callback);
+ }
+
+ resetSuggestions() {
+ if (this._keyboard)
+ this._keyboard.resetSuggestions();
+ }
+
+ setSuggestionsVisible(visible) {
+ this._keyboard?.setSuggestionsVisible(visible);
+ }
+
+ maybeHandleEvent(event) {
+ if (!this._keyboard)
+ return false;
+
+ const actor = global.stage.get_event_actor(event);
+
+ if (Main.layoutManager.keyboardBox.contains(actor) ||
+ !!actor._extendedKeys || !!actor.extendedKey) {
+ actor.event(event, true);
+ actor.event(event, false);
+ return true;
+ }
+
+ return false;
+ }
+};
+
+var Keyboard = GObject.registerClass({
+ Signals: {
+ 'visibility-changed': {},
+ },
+}, class Keyboard extends St.BoxLayout {
+ _init() {
+ super._init({
+ name: 'keyboard',
+ reactive: true,
+ // Keyboard models are defined in LTR, we must override
+ // the locale setting in order to avoid flipping the
+ // keyboard on RTL locales.
+ text_direction: Clutter.TextDirection.LTR,
+ vertical: true,
+ });
+ this._focusInExtendedKeys = false;
+ this._emojiActive = false;
+
+ this._languagePopup = null;
+ this._focusWindow = null;
+ this._focusWindowStartY = null;
+
+ this._latched = false; // current level is latched
+ this._modifiers = new Set();
+ this._modifierKeys = new Map();
+
+ this._suggestions = null;
+ this._emojiKeyVisible = Meta.is_wayland_compositor();
+
+ this._focusTracker = new FocusTracker();
+ this._focusTracker.connectObject(
+ 'position-changed', this._onFocusPositionChanged.bind(this),
+ 'window-grabbed', this._onFocusWindowMoving.bind(this), this);
+
+ this._windowMovedId = this._focusTracker.connect('window-moved',
+ this._onFocusWindowMoving.bind(this));
+
+ // Valid only for X11
+ if (!Meta.is_wayland_compositor()) {
+ this._focusTracker.connectObject('focus-changed', (_tracker, focused) => {
+ if (focused)
+ this.open(Main.layoutManager.focusIndex);
+ else
+ this.close();
+ }, this);
+ }
+
+ this._showIdleId = 0;
+
+ this._keyboardVisible = false;
+ this._keyboardRequested = false;
+ this._keyboardRestingId = 0;
+
+ Main.layoutManager.connectObject('monitors-changed',
+ this._relayout.bind(this), this);
+
+ this._setupKeyboard();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ get visible() {
+ return this._keyboardVisible && super.visible;
+ }
+
+ set visible(visible) {
+ super.visible = visible;
+ }
+
+ _onFocusPositionChanged(focusTracker) {
+ let rect = focusTracker.getCurrentRect();
+ this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height);
+ }
+
+ _onDestroy() {
+ if (this._windowMovedId) {
+ this._focusTracker.disconnect(this._windowMovedId);
+ delete this._windowMovedId;
+ }
+
+ if (this._focusTracker) {
+ this._focusTracker.destroy();
+ delete this._focusTracker;
+ }
+
+ this._clearShowIdle();
+
+ this._keyboardController.destroy();
+
+ Main.layoutManager.untrackChrome(this);
+ Main.layoutManager.keyboardBox.remove_actor(this);
+ Main.layoutManager.keyboardBox.hide();
+
+ if (this._languagePopup) {
+ this._languagePopup.destroy();
+ this._languagePopup = null;
+ }
+
+ IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
+ }
+
+ _setupKeyboard() {
+ Main.layoutManager.keyboardBox.add_actor(this);
+ Main.layoutManager.trackChrome(this);
+
+ this._keyboardController = new KeyboardController();
+
+ this._groups = {};
+ this._currentPage = null;
+
+ this._suggestions = new Suggestions();
+ this.add_child(this._suggestions);
+
+ this._aspectContainer = new AspectContainer({
+ layout_manager: new Clutter.BinLayout(),
+ y_expand: true,
+ });
+ this.add_child(this._aspectContainer);
+
+ this._emojiSelection = new EmojiSelection();
+ this._emojiSelection.connect('toggle', this._toggleEmoji.bind(this));
+ this._emojiSelection.connect('close-request', () => this.close());
+ this._emojiSelection.connect('emoji-selected', (selection, emoji) => {
+ this._keyboardController.commitString(emoji);
+ });
+
+ this._emojiSelection.hide();
+ this._aspectContainer.add_child(this._emojiSelection);
+
+ this._keypad = new Keypad();
+ this._keypad.connectObject('keyval', (_keypad, keyval) => {
+ this._keyboardController.keyvalPress(keyval);
+ this._keyboardController.keyvalRelease(keyval);
+ }, this);
+ this._aspectContainer.add_child(this._keypad);
+ this._keypad.hide();
+ this._keypadVisible = false;
+
+ this._ensureKeysForGroup(this._keyboardController.getCurrentGroup());
+ this._setActiveLayer(0);
+
+ Main.inputMethod.connectObject(
+ 'terminal-mode-changed', this._onTerminalModeChanged.bind(this),
+ this);
+
+ this._keyboardController.connectObject(
+ 'active-group', this._onGroupChanged.bind(this),
+ 'groups-changed', this._onKeyboardGroupsChanged.bind(this),
+ 'panel-state', this._onKeyboardStateChanged.bind(this),
+ 'keypad-visible', this._onKeypadVisible.bind(this),
+ this);
+ global.stage.connectObject('notify::key-focus',
+ this._onKeyFocusChanged.bind(this), this);
+
+ if (Meta.is_wayland_compositor()) {
+ this._keyboardController.connectObject('emoji-visible',
+ this._onEmojiKeyVisible.bind(this), this);
+ }
+
+ this._relayout();
+ }
+
+ _onKeyFocusChanged() {
+ let focus = global.stage.key_focus;
+
+ // Showing an extended key popup and clicking a key from the extended keys
+ // will grab focus, but ignore that
+ let extendedKeysWereFocused = this._focusInExtendedKeys;
+ this._focusInExtendedKeys = focus && (focus._extendedKeys || focus.extendedKey);
+ if (this._focusInExtendedKeys || extendedKeysWereFocused)
+ return;
+
+ if (!(focus instanceof Clutter.Text)) {
+ this.close();
+ return;
+ }
+
+ if (!this._showIdleId) {
+ this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ this.open(Main.layoutManager.focusIndex);
+ this._showIdleId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.open');
+ }
+ }
+
+ _createLayersForGroup(groupName) {
+ let keyboardModel = new KeyboardModel(groupName);
+ let layers = {};
+ let levels = keyboardModel.getLevels();
+ for (let i = 0; i < levels.length; i++) {
+ let currentLevel = levels[i];
+ /* There are keyboard maps which consist of 3 levels (no uppercase,
+ * basically). We however make things consistent by skipping that
+ * second level.
+ */
+ let level = i >= 1 && levels.length == 3 ? i + 1 : i;
+
+ let layout = new KeyContainer();
+ layout.shiftKeys = [];
+ layout.mode = currentLevel.mode;
+
+ this._loadRows(currentLevel, level, levels.length, layout);
+ layers[level] = layout;
+ this._aspectContainer.add_child(layout);
+ layout.layoutButtons();
+
+ layout.hide();
+ }
+
+ return layers;
+ }
+
+ _ensureKeysForGroup(group) {
+ if (!this._groups[group])
+ this._groups[group] = this._createLayersForGroup(group);
+ }
+
+ _addRowKeys(keys, layout) {
+ for (let i = 0; i < keys.length; ++i) {
+ const key = keys[i];
+ const {strings} = key;
+ const commitString = strings?.shift();
+
+ let button = new Key({
+ commitString,
+ label: key.label,
+ iconName: key.iconName,
+ keyval: key.keyval,
+ }, strings);
+
+ if (key.width !== null)
+ button.setWidth(key.width);
+
+ if (key.action !== 'modifier') {
+ button.connect('commit', (_actor, keyval, str) => {
+ this._commitAction(keyval, str).then(() => {
+ if (layout.mode === 'latched' && !this._latched)
+ this._setActiveLayer(0);
+ });
+ });
+ }
+
+ if (key.action !== null) {
+ button.connect('released', () => {
+ if (key.action === 'hide') {
+ this.close();
+ } else if (key.action === 'languageMenu') {
+ this._popupLanguageMenu(button);
+ } else if (key.action === 'emoji') {
+ this._toggleEmoji();
+ } else if (key.action === 'modifier') {
+ this._toggleModifier(key.keyval);
+ } else if (key.action === 'delete') {
+ this._toggleDelete(true);
+ this._toggleDelete(false);
+ } else if (!this._longPressed && key.action === 'levelSwitch') {
+ this._setActiveLayer(key.level);
+ this._setLatched(
+ key.level === 1 &&
+ key.iconName === 'keyboard-caps-lock-symbolic');
+ }
+
+ this._longPressed = false;
+ });
+ }
+
+ if (key.action === 'levelSwitch' &&
+ key.iconName === 'keyboard-shift-symbolic') {
+ layout.shiftKeys.push(button);
+ if (key.level === 1) {
+ button.connect('long-press', () => {
+ this._setActiveLayer(key.level);
+ this._setLatched(true);
+ this._longPressed = true;
+ });
+ }
+ }
+
+ if (key.action === 'delete') {
+ button.connect('long-press',
+ () => this._toggleDelete(true));
+ }
+
+ if (key.action === 'modifier') {
+ let modifierKeys = this._modifierKeys[key.keyval] || [];
+ modifierKeys.push(button);
+ this._modifierKeys[key.keyval] = modifierKeys;
+ }
+
+ if (key.action || key.keyval)
+ button.keyButton.add_style_class_name('default-key');
+
+ layout.appendKey(button, button.keyButton.keyWidth);
+ }
+ }
+
+ async _commitAction(keyval, str) {
+ if (this._modifiers.size === 0 && str !== '' &&
+ keyval && this._oskCompletionEnabled) {
+ if (await Main.inputMethod.handleVirtualKey(keyval))
+ return;
+ }
+
+ if (str === '' || !Main.inputMethod.currentFocus ||
+ (keyval && this._oskCompletionEnabled) ||
+ this._modifiers.size > 0 ||
+ !this._keyboardController.commitString(str, true)) {
+ if (keyval !== 0) {
+ this._forwardModifiers(this._modifiers, Clutter.EventType.KEY_PRESS);
+ this._keyboardController.keyvalPress(keyval);
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, KEY_RELEASE_TIMEOUT, () => {
+ this._keyboardController.keyvalRelease(keyval);
+ this._forwardModifiers(this._modifiers, Clutter.EventType.KEY_RELEASE);
+ this._disableAllModifiers();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+ }
+
+ _previousWordPosition(text, cursor) {
+ /* Skip word prior to cursor */
+ let pos = Math.max(0, text.slice(0, cursor).search(/\s+\S+\s*$/));
+ if (pos < 0)
+ return 0;
+
+ /* Skip contiguous spaces */
+ for (; pos >= 0; pos--) {
+ if (text.charAt(pos) !== ' ')
+ return GLib.utf8_strlen(text.slice(0, pos + 1), -1);
+ }
+
+ return 0;
+ }
+
+ _toggleDelete(enabled) {
+ if (this._deleteEnabled === enabled)
+ return;
+
+ this._deleteEnabled = enabled;
+ this._timesDeleted = 0;
+
+ if (!Main.inputMethod.currentFocus ||
+ Main.inputMethod.hasPreedit() ||
+ Main.inputMethod.terminalMode) {
+ /* If there is no IM focus or are in the middle of preedit,
+ * fallback to keypresses */
+ if (enabled)
+ this._keyboardController.keyvalPress(Clutter.KEY_BackSpace);
+ else
+ this._keyboardController.keyvalRelease(Clutter.KEY_BackSpace);
+ return;
+ }
+
+ if (enabled) {
+ let func = (text, cursor) => {
+ if (cursor === 0)
+ return;
+
+ let encoder = new TextEncoder();
+ let decoder = new TextDecoder();
+
+ /* Find cursor/anchor position in characters */
+ const cursorIdx = GLib.utf8_strlen(decoder.decode(encoder.encode(
+ text).slice(0, cursor)), -1);
+ const anchorIdx = this._timesDeleted < BACKSPACE_WORD_DELETE_THRESHOLD
+ ? cursorIdx - 1
+ : this._previousWordPosition(text, cursor);
+ /* Now get offset from cursor */
+ const offset = anchorIdx - cursorIdx;
+
+ this._timesDeleted++;
+ Main.inputMethod.delete_surrounding(offset, Math.abs(offset));
+ };
+
+ this._surroundingUpdateId = Main.inputMethod.connect(
+ 'surrounding-text-set', () => {
+ let [text, cursor] = Main.inputMethod.getSurroundingText();
+ if (this._timesDeleted === 0) {
+ func(text, cursor);
+ } else {
+ if (this._surroundingUpdateTimeoutId > 0) {
+ GLib.source_remove(this._surroundingUpdateTimeoutId);
+ this._surroundingUpdateTimeoutId = 0;
+ }
+ this._surroundingUpdateTimeoutId =
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, KEY_RELEASE_TIMEOUT, () => {
+ func(text, cursor);
+ this._surroundingUpdateTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ });
+
+ let [text, cursor] = Main.inputMethod.getSurroundingText();
+ if (text)
+ func(text, cursor);
+ else
+ Main.inputMethod.request_surrounding();
+ } else {
+ if (this._surroundingUpdateId > 0) {
+ Main.inputMethod.disconnect(this._surroundingUpdateId);
+ this._surroundingUpdateId = 0;
+ }
+ if (this._surroundingUpdateTimeoutId > 0) {
+ GLib.source_remove(this._surroundingUpdateTimeoutId);
+ this._surroundingUpdateTimeoutId = 0;
+ }
+ }
+ }
+
+ _setLatched(latched) {
+ this._latched = latched;
+ this._setCurrentLevelLatched(this._currentPage, this._latched);
+ }
+
+ _setModifierEnabled(keyval, enabled) {
+ if (enabled)
+ this._modifiers.add(keyval);
+ else
+ this._modifiers.delete(keyval);
+
+ for (const key of this._modifierKeys[keyval])
+ key.setLatched(enabled);
+ }
+
+ _toggleModifier(keyval) {
+ const isActive = this._modifiers.has(keyval);
+ this._setModifierEnabled(keyval, !isActive);
+ }
+
+ _forwardModifiers(modifiers, type) {
+ for (const keyval of modifiers) {
+ if (type === Clutter.EventType.KEY_PRESS)
+ this._keyboardController.keyvalPress(keyval);
+ else if (type === Clutter.EventType.KEY_RELEASE)
+ this._keyboardController.keyvalRelease(keyval);
+ }
+ }
+
+ _disableAllModifiers() {
+ for (const keyval of this._modifiers)
+ this._setModifierEnabled(keyval, false);
+ }
+
+ _popupLanguageMenu(keyActor) {
+ if (this._languagePopup)
+ this._languagePopup.destroy();
+
+ this._languagePopup = new LanguageSelectionPopup(keyActor);
+ Main.layoutManager.addTopChrome(this._languagePopup.actor);
+ this._languagePopup.open(true);
+ }
+
+ _updateCurrentPageVisible() {
+ if (this._currentPage)
+ this._currentPage.visible = !this._emojiActive && !this._keypadVisible;
+ }
+
+ _setEmojiActive(active) {
+ this._emojiActive = active;
+ this._emojiSelection.visible = this._emojiActive;
+ this._updateCurrentPageVisible();
+ }
+
+ _toggleEmoji() {
+ this._setEmojiActive(!this._emojiActive);
+ }
+
+ _setCurrentLevelLatched(layout, latched) {
+ for (let i = 0; i < layout.shiftKeys.length; i++) {
+ let key = layout.shiftKeys[i];
+ key.setLatched(latched);
+ key.iconName = latched
+ ? 'keyboard-caps-lock-symbolic' : 'keyboard-shift-symbolic';
+ }
+ }
+
+ _loadRows(model, level, numLevels, layout) {
+ let rows = model.rows;
+ for (let i = 0; i < rows.length; ++i) {
+ layout.appendRow();
+ this._addRowKeys(rows[i], layout);
+ }
+ }
+
+ _getGridSlots() {
+ let numOfHorizSlots = 0, numOfVertSlots;
+ let rows = this._currentPage.get_children();
+ numOfVertSlots = rows.length;
+
+ for (let i = 0; i < rows.length; ++i) {
+ let keyboardRow = rows[i];
+ let keys = keyboardRow.get_children();
+
+ numOfHorizSlots = Math.max(numOfHorizSlots, keys.length);
+ }
+
+ return [numOfHorizSlots, numOfVertSlots];
+ }
+
+ _relayout() {
+ let monitor = Main.layoutManager.keyboardMonitor;
+
+ if (!monitor)
+ return;
+
+ this.width = monitor.width;
+
+ if (monitor.width > monitor.height)
+ this.height = monitor.height / 3;
+ else
+ this.height = monitor.height / 4;
+ }
+
+ _updateKeys() {
+ this._ensureKeysForGroup(this._keyboardController.getCurrentGroup());
+ this._setActiveLayer(0);
+ }
+
+ _onGroupChanged() {
+ this._updateKeys();
+ }
+
+ _onTerminalModeChanged() {
+ this._updateKeys();
+ }
+
+ _onKeyboardGroupsChanged() {
+ let nonGroupActors = [this._emojiSelection, this._keypad];
+ this._aspectContainer.get_children().filter(c => !nonGroupActors.includes(c)).forEach(c => {
+ c.destroy();
+ });
+
+ this._groups = {};
+ this._onGroupChanged();
+ }
+
+ _onKeypadVisible(controller, visible) {
+ if (visible == this._keypadVisible)
+ return;
+
+ this._keypadVisible = visible;
+ this._keypad.visible = this._keypadVisible;
+ this._updateCurrentPageVisible();
+ }
+
+ _onEmojiKeyVisible(controller, visible) {
+ if (visible == this._emojiKeyVisible)
+ return;
+
+ this._emojiKeyVisible = visible;
+ /* Rebuild keyboard widgetry to include emoji button */
+ this._onKeyboardGroupsChanged();
+ }
+
+ _onKeyboardStateChanged(controller, state) {
+ let enabled;
+ if (state == Clutter.InputPanelState.OFF)
+ enabled = false;
+ else if (state == Clutter.InputPanelState.ON)
+ enabled = true;
+ else if (state == Clutter.InputPanelState.TOGGLE)
+ enabled = this._keyboardVisible == false;
+ else
+ return;
+
+ if (enabled)
+ this.open(Main.layoutManager.focusIndex);
+ else
+ this.close();
+ }
+
+ _setActiveLayer(activeLevel) {
+ let activeGroupName = this._keyboardController.getCurrentGroup();
+ let layers = this._groups[activeGroupName];
+ let currentPage = layers[activeLevel];
+
+ if (this._currentPage == currentPage) {
+ this._updateCurrentPageVisible();
+ return;
+ }
+
+ if (this._currentPage != null) {
+ this._setCurrentLevelLatched(this._currentPage, false);
+ this._currentPage.disconnect(this._currentPage._destroyID);
+ this._currentPage.hide();
+ delete this._currentPage._destroyID;
+ }
+
+ this._disableAllModifiers();
+ this._currentPage = currentPage;
+ this._currentPage._destroyID = this._currentPage.connect('destroy', () => {
+ this._currentPage = null;
+ });
+ this._updateCurrentPageVisible();
+ this._aspectContainer.setRatio(...this._currentPage.getRatio());
+ this._emojiSelection.setRatio(...this._currentPage.getRatio());
+ }
+
+ _clearKeyboardRestTimer() {
+ if (!this._keyboardRestingId)
+ return;
+ GLib.source_remove(this._keyboardRestingId);
+ this._keyboardRestingId = 0;
+ }
+
+ open(immediate = false) {
+ this._clearShowIdle();
+ this._keyboardRequested = true;
+
+ if (this._keyboardVisible) {
+ this._relayout();
+ return;
+ }
+
+ this._oskCompletionEnabled =
+ IBusManager.getIBusManager().setCompletionEnabled(true, () => Main.inputMethod.update());
+ this._clearKeyboardRestTimer();
+
+ if (immediate) {
+ this._open();
+ return;
+ }
+
+ this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ KEYBOARD_REST_TIME,
+ () => {
+ this._clearKeyboardRestTimer();
+ this._open();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
+ }
+
+ _open() {
+ if (!this._keyboardRequested)
+ return;
+
+ this._relayout();
+ this._animateShow();
+
+ this._setEmojiActive(false);
+ }
+
+ close(immediate = false) {
+ this._clearShowIdle();
+ this._keyboardRequested = false;
+
+ if (!this._keyboardVisible)
+ return;
+
+ IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
+ this._oskCompletionEnabled = false;
+ this._clearKeyboardRestTimer();
+
+ if (immediate) {
+ this._close();
+ return;
+ }
+
+ this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ KEYBOARD_REST_TIME,
+ () => {
+ this._clearKeyboardRestTimer();
+ this._close();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
+ }
+
+ _close() {
+ if (this._keyboardRequested)
+ return;
+
+ this._animateHide();
+ this.setCursorLocation(null);
+ this._disableAllModifiers();
+ }
+
+ _animateShow() {
+ if (this._focusWindow)
+ this._animateWindow(this._focusWindow, true);
+
+ Main.layoutManager.keyboardBox.show();
+ this.ease({
+ translation_y: -this.height,
+ opacity: 255,
+ duration: KEYBOARD_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._animateShowComplete();
+ },
+ });
+ this._keyboardVisible = true;
+ this.emit('visibility-changed');
+ }
+
+ _animateShowComplete() {
+ let keyboardBox = Main.layoutManager.keyboardBox;
+ this._keyboardHeightNotifyId = keyboardBox.connect('notify::height', () => {
+ this.translation_y = -this.height;
+ });
+
+ // Toggle visibility so the keyboardBox can update its chrome region.
+ if (!Meta.is_wayland_compositor()) {
+ keyboardBox.hide();
+ keyboardBox.show();
+ }
+ }
+
+ _animateHide() {
+ if (this._focusWindow)
+ this._animateWindow(this._focusWindow, false);
+
+ if (this._keyboardHeightNotifyId) {
+ Main.layoutManager.keyboardBox.disconnect(this._keyboardHeightNotifyId);
+ this._keyboardHeightNotifyId = 0;
+ }
+ this.ease({
+ translation_y: 0,
+ opacity: 0,
+ duration: KEYBOARD_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ onComplete: () => {
+ this._animateHideComplete();
+ },
+ });
+
+ this._keyboardVisible = false;
+ this.emit('visibility-changed');
+ }
+
+ _animateHideComplete() {
+ Main.layoutManager.keyboardBox.hide();
+ }
+
+ gestureProgress(delta) {
+ this._gestureInProgress = true;
+ Main.layoutManager.keyboardBox.show();
+ let progress = Math.min(delta, this.height) / this.height;
+ this.translation_y = -this.height * progress;
+ this.opacity = 255 * progress;
+ const windowActor = this._focusWindow?.get_compositor_private();
+ if (windowActor)
+ windowActor.y = this._focusWindowStartY - (this.height * progress);
+ }
+
+ gestureActivate() {
+ this.open(true);
+ this._gestureInProgress = false;
+ }
+
+ gestureCancel() {
+ if (this._gestureInProgress)
+ this._animateHide();
+ this._gestureInProgress = false;
+ }
+
+ resetSuggestions() {
+ if (this._suggestions)
+ this._suggestions.clear();
+ }
+
+ setSuggestionsVisible(visible) {
+ this._suggestions?.setVisible(visible);
+ }
+
+ addSuggestion(text, callback) {
+ if (!this._suggestions)
+ return;
+ this._suggestions.add(text, callback);
+ this._suggestions.show();
+ }
+
+ _clearShowIdle() {
+ if (!this._showIdleId)
+ return;
+ GLib.source_remove(this._showIdleId);
+ this._showIdleId = 0;
+ }
+
+ _windowSlideAnimationComplete(window, finalY) {
+ // Synchronize window positions again.
+ const frameRect = window.get_frame_rect();
+ const bufferRect = window.get_buffer_rect();
+
+ finalY += frameRect.y - bufferRect.y;
+
+ frameRect.y = finalY;
+
+ this._focusTracker.disconnect(this._windowMovedId);
+ window.move_frame(true, frameRect.x, frameRect.y);
+ this._windowMovedId = this._focusTracker.connect('window-moved',
+ this._onFocusWindowMoving.bind(this));
+ }
+
+ _animateWindow(window, show) {
+ let windowActor = window.get_compositor_private();
+ if (!windowActor)
+ return;
+
+ const finalY = show
+ ? this._focusWindowStartY - Main.layoutManager.keyboardBox.height
+ : this._focusWindowStartY;
+
+ windowActor.ease({
+ y: finalY,
+ duration: KEYBOARD_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => {
+ windowActor.y = finalY;
+ this._windowSlideAnimationComplete(window, finalY);
+ },
+ });
+ }
+
+ _onFocusWindowMoving() {
+ if (this._focusTracker.currentWindow === this._focusWindow) {
+ // Don't use _setFocusWindow() here because that would move the
+ // window while the user has grabbed it. Instead we simply "let go"
+ // of the window.
+ this._focusWindow = null;
+ this._focusWindowStartY = null;
+ }
+
+ this.close(true);
+ }
+
+ _setFocusWindow(window) {
+ if (this._focusWindow === window)
+ return;
+
+ if (this._keyboardVisible && this._focusWindow)
+ this._animateWindow(this._focusWindow, false);
+
+ const windowActor = window?.get_compositor_private();
+ windowActor?.remove_transition('y');
+ this._focusWindowStartY = windowActor ? windowActor.y : null;
+
+ if (this._keyboardVisible && window)
+ this._animateWindow(window, true);
+
+ this._focusWindow = window;
+ }
+
+ setCursorLocation(window, x, y, w, h) {
+ let monitor = Main.layoutManager.keyboardMonitor;
+
+ if (window && monitor) {
+ const keyboardHeight = Main.layoutManager.keyboardBox.height;
+ const keyboardY1 = (monitor.y + monitor.height) - keyboardHeight;
+
+ if (this._focusWindow === window) {
+ if (y + h + keyboardHeight < keyboardY1)
+ this._setFocusWindow(null);
+
+ return;
+ }
+
+ if (y + h >= keyboardY1)
+ this._setFocusWindow(window);
+ else
+ this._setFocusWindow(null);
+ } else {
+ this._setFocusWindow(null);
+ }
+ }
+});
+
+var KeyboardController = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ let seat = Clutter.get_default_backend().get_default_seat();
+ this._virtualDevice = seat.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE);
+
+ this._inputSourceManager = InputSourceManager.getInputSourceManager();
+ this._inputSourceManager.connectObject(
+ 'current-source-changed', this._onSourceChanged.bind(this),
+ 'sources-changed', this._onSourcesModified.bind(this), this);
+ this._currentSource = this._inputSourceManager.currentSource;
+
+ Main.inputMethod.connectObject(
+ 'notify::content-purpose', this._onContentPurposeHintsChanged.bind(this),
+ 'notify::content-hints', this._onContentPurposeHintsChanged.bind(this),
+ 'input-panel-state', (o, state) => this.emit('panel-state', state), this);
+ }
+
+ destroy() {
+ this._inputSourceManager.disconnectObject(this);
+ Main.inputMethod.disconnectObject(this);
+
+ // Make sure any buttons pressed by the virtual device are released
+ // immediately instead of waiting for the next GC cycle
+ this._virtualDevice.run_dispose();
+ }
+
+ _onSourcesModified() {
+ this.emit('groups-changed');
+ }
+
+ _onSourceChanged(inputSourceManager, _oldSource) {
+ let source = inputSourceManager.currentSource;
+ this._currentSource = source;
+ this.emit('active-group', source.id);
+ }
+
+ _onContentPurposeHintsChanged(method) {
+ let purpose = method.content_purpose;
+ let emojiVisible = false;
+ let keypadVisible = false;
+
+ if (purpose == Clutter.InputContentPurpose.NORMAL ||
+ purpose == Clutter.InputContentPurpose.ALPHA ||
+ purpose == Clutter.InputContentPurpose.PASSWORD ||
+ purpose == Clutter.InputContentPurpose.TERMINAL)
+ emojiVisible = true;
+ if (purpose == Clutter.InputContentPurpose.DIGITS ||
+ purpose == Clutter.InputContentPurpose.NUMBER ||
+ purpose == Clutter.InputContentPurpose.PHONE)
+ keypadVisible = true;
+
+ this.emit('emoji-visible', emojiVisible);
+ this.emit('keypad-visible', keypadVisible);
+ }
+
+ getGroups() {
+ let inputSources = this._inputSourceManager.inputSources;
+ let groups = [];
+
+ for (let i in inputSources) {
+ let is = inputSources[i];
+ groups[is.index] = is.xkbId;
+ }
+
+ return groups;
+ }
+
+ getCurrentGroup() {
+ if (Main.inputMethod.terminalMode)
+ return 'us-extended';
+
+ // Special case for Korean, if Hangul mode is disabled, use the 'us' keymap
+ if (this._currentSource.id === 'hangul') {
+ const inputSourceManager = InputSourceManager.getInputSourceManager();
+ const currentSource = inputSourceManager.currentSource;
+ let prop;
+ for (let i = 0; (prop = currentSource.properties.get(i)) !== null; ++i) {
+ if (prop.get_key() === 'InputMode' &&
+ prop.get_prop_type() === IBus.PropType.TOGGLE &&
+ prop.get_state() !== IBus.PropState.CHECKED)
+ return 'us';
+ }
+ }
+
+ return this._currentSource.xkbId;
+ }
+
+ commitString(string, fromKey) {
+ if (string == null)
+ return false;
+ /* Let ibus methods fall through keyval emission */
+ if (fromKey && this._currentSource.type == InputSourceManager.INPUT_SOURCE_TYPE_IBUS)
+ return false;
+
+ Main.inputMethod.commit(string);
+ return true;
+ }
+
+ keyvalPress(keyval) {
+ this._virtualDevice.notify_keyval(Clutter.get_current_event_time() * 1000,
+ keyval, Clutter.KeyState.PRESSED);
+ }
+
+ keyvalRelease(keyval) {
+ this._virtualDevice.notify_keyval(Clutter.get_current_event_time() * 1000,
+ keyval, Clutter.KeyState.RELEASED);
+ }
+};
diff --git a/js/ui/layout.js b/js/ui/layout.js
new file mode 100644
index 0000000..69bf148
--- /dev/null
+++ b/js/ui/layout.js
@@ -0,0 +1,1451 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported MonitorConstraint, LayoutManager */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+const Signals = imports.misc.signals;
+
+const Background = imports.ui.background;
+const BackgroundMenu = imports.ui.backgroundMenu;
+
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+const Ripples = imports.ui.ripples;
+
+var STARTUP_ANIMATION_TIME = 500;
+var BACKGROUND_FADE_ANIMATION_TIME = 1000;
+
+var HOT_CORNER_PRESSURE_THRESHOLD = 100; // pixels
+var HOT_CORNER_PRESSURE_TIMEOUT = 1000; // ms
+
+const SCREEN_TRANSITION_DELAY = 250; // ms
+const SCREEN_TRANSITION_DURATION = 500; // ms
+
+function isPopupMetaWindow(actor) {
+ switch (actor.meta_window.get_window_type()) {
+ case Meta.WindowType.DROPDOWN_MENU:
+ case Meta.WindowType.POPUP_MENU:
+ case Meta.WindowType.COMBO:
+ return true;
+ default:
+ return false;
+ }
+}
+
+var MonitorConstraint = GObject.registerClass({
+ Properties: {
+ 'primary': GObject.ParamSpec.boolean('primary',
+ 'Primary', 'Track primary monitor',
+ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
+ false),
+ 'index': GObject.ParamSpec.int('index',
+ 'Monitor index', 'Track specific monitor',
+ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
+ -1, 64, -1),
+ 'work-area': GObject.ParamSpec.boolean('work-area',
+ 'Work-area', 'Track monitor\'s work-area',
+ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
+ false),
+ },
+}, class MonitorConstraint extends Clutter.Constraint {
+ _init(props) {
+ this._primary = false;
+ this._index = -1;
+ this._workArea = false;
+
+ super._init(props);
+ }
+
+ get primary() {
+ return this._primary;
+ }
+
+ set primary(v) {
+ if (v)
+ this._index = -1;
+ this._primary = v;
+ if (this.actor)
+ this.actor.queue_relayout();
+ this.notify('primary');
+ }
+
+ get index() {
+ return this._index;
+ }
+
+ set index(v) {
+ this._primary = false;
+ this._index = v;
+ if (this.actor)
+ this.actor.queue_relayout();
+ this.notify('index');
+ }
+
+ get workArea() {
+ return this._workArea;
+ }
+
+ set workArea(v) {
+ if (v == this._workArea)
+ return;
+ this._workArea = v;
+ if (this.actor)
+ this.actor.queue_relayout();
+ this.notify('work-area');
+ }
+
+ vfunc_set_actor(actor) {
+ if (actor) {
+ if (!this._monitorsChangedId) {
+ this._monitorsChangedId =
+ Main.layoutManager.connect('monitors-changed', () => {
+ this.actor.queue_relayout();
+ });
+ }
+
+ if (!this._workareasChangedId) {
+ this._workareasChangedId =
+ global.display.connect('workareas-changed', () => {
+ if (this._workArea)
+ this.actor.queue_relayout();
+ });
+ }
+ } else {
+ if (this._monitorsChangedId)
+ Main.layoutManager.disconnect(this._monitorsChangedId);
+ this._monitorsChangedId = 0;
+
+ if (this._workareasChangedId)
+ global.display.disconnect(this._workareasChangedId);
+ this._workareasChangedId = 0;
+ }
+
+ super.vfunc_set_actor(actor);
+ }
+
+ vfunc_update_allocation(actor, actorBox) {
+ if (!this._primary && this._index < 0)
+ return;
+
+ if (!Main.layoutManager.primaryMonitor)
+ return;
+
+ let index;
+ if (this._primary)
+ index = Main.layoutManager.primaryIndex;
+ else
+ index = Math.min(this._index, Main.layoutManager.monitors.length - 1);
+
+ let rect;
+ if (this._workArea) {
+ let workspaceManager = global.workspace_manager;
+ let ws = workspaceManager.get_workspace_by_index(0);
+ rect = ws.get_work_area_for_monitor(index);
+ } else {
+ rect = Main.layoutManager.monitors[index];
+ }
+
+ actorBox.init_rect(rect.x, rect.y, rect.width, rect.height);
+ }
+});
+
+var Monitor = class Monitor {
+ constructor(index, geometry, geometryScale) {
+ this.index = index;
+ this.x = geometry.x;
+ this.y = geometry.y;
+ this.width = geometry.width;
+ this.height = geometry.height;
+ this.geometry_scale = geometryScale;
+ }
+
+ get inFullscreen() {
+ return global.display.get_monitor_in_fullscreen(this.index);
+ }
+};
+
+const UiActor = GObject.registerClass(
+class UiActor extends St.Widget {
+ vfunc_get_preferred_width(_forHeight) {
+ let width = global.stage.width;
+ return [width, width];
+ }
+
+ vfunc_get_preferred_height(_forWidth) {
+ let height = global.stage.height;
+ return [height, height];
+ }
+});
+
+const defaultParams = {
+ trackFullscreen: false,
+ affectsStruts: false,
+ affectsInputRegion: true,
+};
+
+var LayoutManager = GObject.registerClass({
+ Signals: {
+ 'hot-corners-changed': {},
+ 'startup-complete': {},
+ 'startup-prepared': {},
+ 'monitors-changed': {},
+ 'system-modal-opened': {},
+ },
+}, class LayoutManager extends GObject.Object {
+ _init() {
+ super._init();
+
+ this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
+ this.monitors = [];
+ this.primaryMonitor = null;
+ this.primaryIndex = -1;
+ this.hotCorners = [];
+
+ this._keyboardIndex = -1;
+ this._rightPanelBarrier = null;
+
+ this._inOverview = false;
+ this._updateRegionIdle = 0;
+
+ this._trackedActors = [];
+ this._topActors = [];
+ this._isPopupWindowVisible = false;
+ this._startingUp = true;
+ this._pendingLoadBackground = false;
+
+ // Set up stage hierarchy to group all UI actors under one container.
+ this.uiGroup = new UiActor({ name: 'uiGroup' });
+ this.uiGroup.set_flags(Clutter.ActorFlags.NO_LAYOUT);
+
+ global.stage.add_child(this.uiGroup);
+
+ global.stage.remove_actor(global.window_group);
+ this.uiGroup.add_actor(global.window_group);
+
+ // Using addChrome() to add actors to uiGroup will position actors
+ // underneath the top_window_group.
+ // To insert actors at the top of uiGroup, we use addTopChrome() or
+ // add the actor directly using uiGroup.add_actor().
+ global.stage.remove_actor(global.top_window_group);
+ this.uiGroup.add_actor(global.top_window_group);
+
+ this.overviewGroup = new St.Widget({
+ name: 'overviewGroup',
+ visible: false,
+ reactive: true,
+ constraints: new Clutter.BindConstraint({
+ source: this.uiGroup,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }),
+ });
+ this.addChrome(this.overviewGroup);
+
+ this.screenShieldGroup = new St.Widget({
+ name: 'screenShieldGroup',
+ visible: false,
+ clip_to_allocation: true,
+ layout_manager: new Clutter.BinLayout(),
+ constraints: new Clutter.BindConstraint({
+ source: this.uiGroup,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }),
+ });
+ this.addChrome(this.screenShieldGroup);
+
+ this.panelBox = new St.BoxLayout({
+ name: 'panelBox',
+ vertical: true,
+ });
+ this.addChrome(this.panelBox, {
+ affectsStruts: true,
+ trackFullscreen: true,
+ });
+ this.panelBox.connect('notify::allocation',
+ this._panelBoxChanged.bind(this));
+
+ this.modalDialogGroup = new St.Widget({
+ name: 'modalDialogGroup',
+ layout_manager: new Clutter.BinLayout(),
+ });
+ this.uiGroup.add_actor(this.modalDialogGroup);
+
+ this.keyboardBox = new St.BoxLayout({
+ name: 'keyboardBox',
+ reactive: true,
+ track_hover: true,
+ });
+ this.addTopChrome(this.keyboardBox);
+ this._keyboardHeightNotifyId = 0;
+
+ this.screenshotUIGroup = new St.Widget({
+ name: 'screenshotUIGroup',
+ layout_manager: new Clutter.BinLayout(),
+ });
+ this.addTopChrome(this.screenshotUIGroup);
+
+ // A dummy actor that tracks the mouse or text cursor, based on the
+ // position and size set in setDummyCursorGeometry.
+ this.dummyCursor = new St.Widget({ width: 0, height: 0, opacity: 0 });
+ this.uiGroup.add_actor(this.dummyCursor);
+
+ let feedbackGroup = Meta.get_feedback_group_for_display(global.display);
+ global.stage.remove_actor(feedbackGroup);
+ this.uiGroup.add_actor(feedbackGroup);
+
+ this._backgroundGroup = new Meta.BackgroundGroup();
+ global.window_group.add_child(this._backgroundGroup);
+ global.window_group.set_child_below_sibling(this._backgroundGroup, null);
+ this._bgManagers = [];
+
+ this._interfaceSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.interface',
+ });
+
+ this._interfaceSettings.connect('changed::enable-hot-corners',
+ this._updateHotCorners.bind(this));
+
+ // Need to update struts on new workspaces when they are added
+ let workspaceManager = global.workspace_manager;
+ workspaceManager.connect('notify::n-workspaces',
+ this._queueUpdateRegions.bind(this));
+
+ let display = global.display;
+ display.connect('restacked',
+ this._windowsRestacked.bind(this));
+ display.connect('in-fullscreen-changed',
+ this._updateFullscreen.bind(this));
+
+ let monitorManager = Meta.MonitorManager.get();
+ monitorManager.connect('monitors-changed',
+ this._monitorsChanged.bind(this));
+ this._monitorsChanged();
+
+ this.screenTransition = new ScreenTransition();
+ this.uiGroup.add_child(this.screenTransition);
+ this.screenTransition.add_constraint(new Clutter.BindConstraint({
+ source: this.uiGroup,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }));
+ }
+
+ // This is called by Main after everything else is constructed
+ init() {
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+
+ this._loadBackground();
+ }
+
+ showOverview() {
+ this.overviewGroup.show();
+ this.screenTransition.hide();
+
+ this._inOverview = true;
+ this._updateVisibility();
+ }
+
+ hideOverview() {
+ this.overviewGroup.hide();
+ this.screenTransition.hide();
+
+ this._inOverview = false;
+ this._updateVisibility();
+ }
+
+ _sessionUpdated() {
+ this._updateVisibility();
+ this._queueUpdateRegions();
+ }
+
+ _updateMonitors() {
+ let display = global.display;
+
+ this.monitors = [];
+ let nMonitors = display.get_n_monitors();
+ for (let i = 0; i < nMonitors; i++) {
+ this.monitors.push(new Monitor(i,
+ display.get_monitor_geometry(i),
+ display.get_monitor_scale(i)));
+ }
+
+ if (nMonitors == 0) {
+ this.primaryIndex = this.bottomIndex = -1;
+ } else if (nMonitors == 1) {
+ this.primaryIndex = this.bottomIndex = 0;
+ } else {
+ // If there are monitors below the primary, then we need
+ // to split primary from bottom.
+ this.primaryIndex = this.bottomIndex = display.get_primary_monitor();
+ for (let i = 0; i < this.monitors.length; i++) {
+ let monitor = this.monitors[i];
+ if (this._isAboveOrBelowPrimary(monitor)) {
+ if (monitor.y > this.monitors[this.bottomIndex].y)
+ this.bottomIndex = i;
+ }
+ }
+ }
+ if (this.primaryIndex != -1) {
+ this.primaryMonitor = this.monitors[this.primaryIndex];
+ this.bottomMonitor = this.monitors[this.bottomIndex];
+
+ if (this._pendingLoadBackground) {
+ this._loadBackground();
+ this._pendingLoadBackground = false;
+ }
+ } else {
+ this.primaryMonitor = null;
+ this.bottomMonitor = null;
+ }
+ }
+
+ _updateHotCorners() {
+ // destroy old hot corners
+ this.hotCorners.forEach(corner => {
+ if (corner)
+ corner.destroy();
+ });
+ this.hotCorners = [];
+
+ if (!this._interfaceSettings.get_boolean('enable-hot-corners')) {
+ this.emit('hot-corners-changed');
+ return;
+ }
+
+ let size = this.panelBox.height;
+
+ // build new hot corners
+ for (let i = 0; i < this.monitors.length; i++) {
+ let monitor = this.monitors[i];
+ let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
+ let cornerY = monitor.y;
+
+ let haveTopLeftCorner = true;
+
+ if (i != this.primaryIndex) {
+ // Check if we have a top left (right for RTL) corner.
+ // I.e. if there is no monitor directly above or to the left(right)
+ let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
+ let besideY = cornerY;
+ let aboveX = cornerX;
+ let aboveY = cornerY - 1;
+
+ for (let j = 0; j < this.monitors.length; j++) {
+ if (i == j)
+ continue;
+ let otherMonitor = this.monitors[j];
+ if (besideX >= otherMonitor.x &&
+ besideX < otherMonitor.x + otherMonitor.width &&
+ besideY >= otherMonitor.y &&
+ besideY < otherMonitor.y + otherMonitor.height) {
+ haveTopLeftCorner = false;
+ break;
+ }
+ if (aboveX >= otherMonitor.x &&
+ aboveX < otherMonitor.x + otherMonitor.width &&
+ aboveY >= otherMonitor.y &&
+ aboveY < otherMonitor.y + otherMonitor.height) {
+ haveTopLeftCorner = false;
+ break;
+ }
+ }
+ }
+
+ if (haveTopLeftCorner) {
+ let corner = new HotCorner(this, monitor, cornerX, cornerY);
+ corner.setBarrierSize(size);
+ this.hotCorners.push(corner);
+ } else {
+ this.hotCorners.push(null);
+ }
+ }
+
+ this.emit('hot-corners-changed');
+ }
+
+ _addBackgroundMenu(bgManager) {
+ BackgroundMenu.addBackgroundMenu(bgManager.backgroundActor, this);
+ }
+
+ _createBackgroundManager(monitorIndex) {
+ const bgManager = new Background.BackgroundManager({
+ container: this._backgroundGroup,
+ layoutManager: this,
+ monitorIndex,
+ });
+
+ bgManager.connect('changed', this._addBackgroundMenu.bind(this));
+ this._addBackgroundMenu(bgManager);
+
+ return bgManager;
+ }
+
+ _showSecondaryBackgrounds() {
+ for (let i = 0; i < this.monitors.length; i++) {
+ if (i != this.primaryIndex) {
+ let backgroundActor = this._bgManagers[i].backgroundActor;
+ backgroundActor.show();
+ backgroundActor.opacity = 0;
+ backgroundActor.ease({
+ opacity: 255,
+ duration: BACKGROUND_FADE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+ }
+
+ _waitLoaded(bgManager) {
+ return new Promise(resolve => {
+ const id = bgManager.connect('loaded', () => {
+ bgManager.disconnect(id);
+ resolve();
+ });
+ });
+ }
+
+ _updateBackgrounds() {
+ for (let i = 0; i < this._bgManagers.length; i++)
+ this._bgManagers[i].destroy();
+
+ this._bgManagers = [];
+
+ if (Main.sessionMode.isGreeter)
+ return Promise.resolve();
+
+ for (let i = 0; i < this.monitors.length; i++) {
+ let bgManager = this._createBackgroundManager(i);
+ this._bgManagers.push(bgManager);
+
+ if (i != this.primaryIndex && this._startingUp)
+ bgManager.backgroundActor.hide();
+ }
+
+ return Promise.all(this._bgManagers.map(this._waitLoaded));
+ }
+
+ _updateKeyboardBox() {
+ this.keyboardBox.set_position(this.keyboardMonitor.x,
+ this.keyboardMonitor.y + this.keyboardMonitor.height);
+ this.keyboardBox.set_size(this.keyboardMonitor.width, -1);
+ }
+
+ _updateBoxes() {
+ if (!this.primaryMonitor)
+ return;
+
+ this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
+ this.panelBox.set_size(this.primaryMonitor.width, -1);
+
+ this.keyboardIndex = this.primaryIndex;
+ }
+
+ _panelBoxChanged() {
+ this._updatePanelBarrier();
+
+ let size = this.panelBox.height;
+ this.hotCorners.forEach(corner => {
+ if (corner)
+ corner.setBarrierSize(size);
+ });
+ }
+
+ _updatePanelBarrier() {
+ if (this._rightPanelBarrier) {
+ this._rightPanelBarrier.destroy();
+ this._rightPanelBarrier = null;
+ }
+
+ if (!this.primaryMonitor)
+ return;
+
+ if (this.panelBox.height) {
+ let primary = this.primaryMonitor;
+
+ this._rightPanelBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: primary.x + primary.width, y1: primary.y,
+ x2: primary.x + primary.width, y2: primary.y + this.panelBox.height,
+ directions: Meta.BarrierDirection.NEGATIVE_X,
+ });
+ }
+ }
+
+ _monitorsChanged() {
+ this._updateMonitors();
+ this._updateBoxes();
+ this._updateHotCorners();
+ this._updateBackgrounds();
+ this._updateFullscreen();
+ this._updateVisibility();
+ this._queueUpdateRegions();
+
+ this.emit('monitors-changed');
+ }
+
+ _isAboveOrBelowPrimary(monitor) {
+ let primary = this.monitors[this.primaryIndex];
+ let monitorLeft = monitor.x, monitorRight = monitor.x + monitor.width;
+ let primaryLeft = primary.x, primaryRight = primary.x + primary.width;
+
+ if ((monitorLeft >= primaryLeft && monitorLeft < primaryRight) ||
+ (monitorRight > primaryLeft && monitorRight <= primaryRight) ||
+ (primaryLeft >= monitorLeft && primaryLeft < monitorRight) ||
+ (primaryRight > monitorLeft && primaryRight <= monitorRight))
+ return true;
+
+ return false;
+ }
+
+ get currentMonitor() {
+ let index = global.display.get_current_monitor();
+ return this.monitors[index];
+ }
+
+ get keyboardMonitor() {
+ return this.monitors[this.keyboardIndex];
+ }
+
+ get focusIndex() {
+ let i = Main.layoutManager.primaryIndex;
+
+ if (global.stage.key_focus != null)
+ i = this.findIndexForActor(global.stage.key_focus);
+ else if (global.display.focus_window != null)
+ i = global.display.focus_window.get_monitor();
+ return i;
+ }
+
+ get focusMonitor() {
+ if (this.focusIndex < 0)
+ return null;
+ return this.monitors[this.focusIndex];
+ }
+
+ set keyboardIndex(v) {
+ this._keyboardIndex = v;
+ this._updateKeyboardBox();
+ }
+
+ get keyboardIndex() {
+ return this._keyboardIndex;
+ }
+
+ _loadBackground() {
+ if (!this.primaryMonitor) {
+ this._pendingLoadBackground = true;
+ return;
+ }
+ this._systemBackground = new Background.SystemBackground();
+ this._systemBackground.hide();
+
+ global.stage.insert_child_below(this._systemBackground, null);
+
+ const constraint = new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ });
+ this._systemBackground.add_constraint(constraint);
+
+ let signalId = this._systemBackground.connect('loaded', () => {
+ this._systemBackground.disconnect(signalId);
+
+ // We're mostly prepared for the startup animation
+ // now, but since a lot is going on asynchronously
+ // during startup, let's defer the startup animation
+ // until the event loop is uncontended and idle.
+ // This helps to prevent us from running the animation
+ // when the system is bogged down
+ const id = GLib.idle_add(GLib.PRIORITY_LOW, () => {
+ if (this.primaryMonitor) {
+ this._systemBackground.show();
+ global.stage.show();
+ this._prepareStartupAnimation();
+ return GLib.SOURCE_REMOVE;
+ } else {
+ return GLib.SOURCE_CONTINUE;
+ }
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] Startup Animation');
+ });
+ }
+
+ // Startup Animations
+ //
+ // We have two different animations, depending on whether we're a greeter
+ // or a normal session.
+ //
+ // In the greeter, we want to animate the panel from the top, and smoothly
+ // fade the login dialog on top of whatever plymouth left on screen which
+ // we get as a still frame background before drawing anything else.
+ //
+ // Here we just have the code to animate the panel, and fade up the background.
+ // The login dialog animation is handled by modalDialog.js
+ //
+ // When starting a normal user session, we want to grow it out of the middle
+ // of the screen.
+
+ async _prepareStartupAnimation() {
+ // During the initial transition, add a simple actor to block all events,
+ // so they don't get delivered to X11 windows that have been transformed.
+ this._coverPane = new Clutter.Actor({
+ opacity: 0,
+ width: global.screen_width,
+ height: global.screen_height,
+ reactive: true,
+ });
+ this.addChrome(this._coverPane);
+
+ // Force an update of the regions before we scale the UI group to
+ // get the correct allocation for the struts.
+ // Do this even when we don't animate on restart, so that maximized
+ // windows restore to the right size.
+ this._updateRegions();
+
+ if (Meta.is_restart()) {
+ // On restart, we don't do an animation.
+ } else if (Main.sessionMode.isGreeter) {
+ this.panelBox.translation_y = -this.panelBox.height;
+ } else {
+ this.keyboardBox.hide();
+
+ let monitor = this.primaryMonitor;
+
+ if (!Main.sessionMode.hasOverview) {
+ const x = monitor.x + monitor.width / 2.0;
+ const y = monitor.y + monitor.height / 2.0;
+
+ this.uiGroup.set_pivot_point(
+ x / global.screen_width,
+ y / global.screen_height);
+ this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
+ this.uiGroup.opacity = 0;
+ }
+
+ global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
+
+ await this._updateBackgrounds();
+ }
+
+ this.emit('startup-prepared');
+
+ this._startupAnimation();
+ }
+
+ _startupAnimation() {
+ if (Meta.is_restart())
+ this._startupAnimationComplete();
+ else if (Main.sessionMode.isGreeter)
+ this._startupAnimationGreeter();
+ else
+ this._startupAnimationSession();
+ }
+
+ _startupAnimationGreeter() {
+ this.panelBox.ease({
+ translation_y: 0,
+ duration: STARTUP_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this._startupAnimationComplete(),
+ });
+ }
+
+ _startupAnimationSession() {
+ const onStopped = () => this._startupAnimationComplete();
+ if (Main.sessionMode.hasOverview) {
+ Main.overview.runStartupAnimation(onStopped);
+ } else {
+ this.uiGroup.ease({
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ duration: STARTUP_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped,
+ });
+ }
+ }
+
+ _startupAnimationComplete() {
+ this._coverPane.destroy();
+ this._coverPane = null;
+
+ this._systemBackground.destroy();
+ this._systemBackground = null;
+
+ this._startingUp = false;
+
+ this.keyboardBox.show();
+
+ if (!Main.sessionMode.isGreeter) {
+ this._showSecondaryBackgrounds();
+ global.window_group.remove_clip();
+ }
+
+ this._queueUpdateRegions();
+
+ this.emit('startup-complete');
+ }
+
+ // setDummyCursorGeometry:
+ //
+ // The cursor dummy is a standard widget commonly used for popup
+ // menus and box pointers to track, as the box pointer API only
+ // tracks actors. If you want to pop up a menu based on where the
+ // user clicked, or where the text cursor is, the cursor dummy
+ // is what you should use. Given that the menu should not track
+ // the actual mouse pointer as it moves, you need to call this
+ // function before you show the menu to ensure it is at the right
+ // position and has the right size.
+ setDummyCursorGeometry(x, y, w, h) {
+ this.dummyCursor.set_position(Math.round(x), Math.round(y));
+ this.dummyCursor.set_size(Math.round(w), Math.round(h));
+ }
+
+ // addChrome:
+ // @actor: an actor to add to the chrome
+ // @params: (optional) additional params
+ //
+ // Adds @actor to the chrome, and (unless %affectsInputRegion in
+ // @params is %false) extends the input region to include it.
+ // Changes in @actor's size, position, and visibility will
+ // automatically result in appropriate changes to the input
+ // region.
+ //
+ // If %affectsStruts in @params is %true (and @actor is along a
+ // screen edge), then @actor's size and position will also affect
+ // the window manager struts. Changes to @actor's visibility will
+ // NOT affect whether or not the strut is present, however.
+ //
+ // If %trackFullscreen in @params is %true, the actor's visibility
+ // will be bound to the presence of fullscreen windows on the same
+ // monitor (it will be hidden whenever a fullscreen window is visible,
+ // and shown otherwise)
+ addChrome(actor, params) {
+ this.uiGroup.add_actor(actor);
+ if (this.uiGroup.contains(global.top_window_group))
+ this.uiGroup.set_child_below_sibling(actor, global.top_window_group);
+ this._trackActor(actor, params);
+ }
+
+ // addTopChrome:
+ // @actor: an actor to add to the chrome
+ // @params: (optional) additional params
+ //
+ // Like addChrome(), but adds @actor above all windows, including popups.
+ addTopChrome(actor, params) {
+ this.uiGroup.add_actor(actor);
+ this._trackActor(actor, params);
+ }
+
+ // trackChrome:
+ // @actor: a descendant of the chrome to begin tracking
+ // @params: parameters describing how to track @actor
+ //
+ // Tells the chrome to track @actor. This can be used to extend the
+ // struts or input region to cover specific children.
+ //
+ // @params can have any of the same values as in addChrome(),
+ // though some possibilities don't make sense. By default, @actor has
+ // the same params as its chrome ancestor.
+ trackChrome(actor, params = {}) {
+ let ancestor = actor.get_parent();
+ let index = this._findActor(ancestor);
+ while (ancestor && index == -1) {
+ ancestor = ancestor.get_parent();
+ index = this._findActor(ancestor);
+ }
+
+ let ancestorData = ancestor
+ ? this._trackedActors[index]
+ : defaultParams;
+ // We can't use Params.parse here because we want to drop
+ // the extra values like ancestorData.actor
+ for (let prop in defaultParams) {
+ if (!Object.prototype.hasOwnProperty.call(params, prop))
+ params[prop] = ancestorData[prop];
+ }
+
+ this._trackActor(actor, params);
+ }
+
+ // untrackChrome:
+ // @actor: an actor previously tracked via trackChrome()
+ //
+ // Undoes the effect of trackChrome()
+ untrackChrome(actor) {
+ this._untrackActor(actor);
+ }
+
+ // removeChrome:
+ // @actor: a chrome actor
+ //
+ // Removes @actor from the chrome
+ removeChrome(actor) {
+ this.uiGroup.remove_actor(actor);
+ this._untrackActor(actor);
+ }
+
+ _findActor(actor) {
+ for (let i = 0; i < this._trackedActors.length; i++) {
+ let actorData = this._trackedActors[i];
+ if (actorData.actor == actor)
+ return i;
+ }
+ return -1;
+ }
+
+ _trackActor(actor, params) {
+ if (this._findActor(actor) != -1)
+ throw new Error('trying to re-track existing chrome actor');
+
+ let actorData = Params.parse(params, defaultParams);
+ actorData.actor = actor;
+ actor.connectObject(
+ 'notify::visible', this._queueUpdateRegions.bind(this),
+ 'notify::allocation', this._queueUpdateRegions.bind(this),
+ 'destroy', this._untrackActor.bind(this), this);
+ // Note that destroying actor will unset its parent, so we don't
+ // need to connect to 'destroy' too.
+
+ this._trackedActors.push(actorData);
+ this._updateActorVisibility(actorData);
+ this._queueUpdateRegions();
+ }
+
+ _untrackActor(actor) {
+ let i = this._findActor(actor);
+
+ if (i == -1)
+ return;
+
+ this._trackedActors.splice(i, 1);
+ actor.disconnectObject(this);
+
+ this._queueUpdateRegions();
+ }
+
+ _updateActorVisibility(actorData) {
+ if (!actorData.trackFullscreen)
+ return;
+
+ let monitor = this.findMonitorForActor(actorData.actor);
+ actorData.actor.visible = !(global.window_group.visible &&
+ monitor &&
+ monitor.inFullscreen);
+ }
+
+ _updateVisibility() {
+ let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
+
+ global.window_group.visible = windowsVisible;
+ global.top_window_group.visible = windowsVisible;
+
+ this._trackedActors.forEach(this._updateActorVisibility.bind(this));
+ }
+
+ getWorkAreaForMonitor(monitorIndex) {
+ // Assume that all workspaces will have the same
+ // struts and pick the first one.
+ let workspaceManager = global.workspace_manager;
+ let ws = workspaceManager.get_workspace_by_index(0);
+ return ws.get_work_area_for_monitor(monitorIndex);
+ }
+
+ // This call guarantees that we return some monitor to simplify usage of it
+ // In practice all tracked actors should be visible on some monitor anyway
+ findIndexForActor(actor) {
+ let [x, y] = actor.get_transformed_position();
+ let [w, h] = actor.get_transformed_size();
+ let rect = new Meta.Rectangle({ x, y, width: w, height: h });
+ return global.display.get_monitor_index_for_rect(rect);
+ }
+
+ findMonitorForActor(actor) {
+ let index = this.findIndexForActor(actor);
+ if (index >= 0 && index < this.monitors.length)
+ return this.monitors[index];
+ return null;
+ }
+
+ _queueUpdateRegions() {
+ if (!this._updateRegionIdle) {
+ this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
+ this._updateRegions.bind(this));
+ }
+ }
+
+ _updateFullscreen() {
+ this._updateVisibility();
+ this._queueUpdateRegions();
+ }
+
+ _windowsRestacked() {
+ let changed = false;
+
+ if (this._isPopupWindowVisible != global.top_window_group.get_children().some(isPopupMetaWindow))
+ changed = true;
+
+ if (changed) {
+ this._updateVisibility();
+ this._queueUpdateRegions();
+ }
+ }
+
+ _updateRegions() {
+ if (this._updateRegionIdle) {
+ Meta.later_remove(this._updateRegionIdle);
+ delete this._updateRegionIdle;
+ }
+
+ let rects = [], struts = [], i;
+ let isPopupMenuVisible = global.top_window_group.get_children().some(isPopupMetaWindow);
+ const wantsInputRegion =
+ !this._startingUp &&
+ !isPopupMenuVisible &&
+ Main.modalCount === 0 &&
+ !Meta.is_wayland_compositor();
+
+ for (i = 0; i < this._trackedActors.length; i++) {
+ let actorData = this._trackedActors[i];
+ if (!(actorData.affectsInputRegion && wantsInputRegion) && !actorData.affectsStruts)
+ continue;
+
+ let [x, y] = actorData.actor.get_transformed_position();
+ let [w, h] = actorData.actor.get_transformed_size();
+ x = Math.round(x);
+ y = Math.round(y);
+ w = Math.round(w);
+ h = Math.round(h);
+
+ if (actorData.affectsInputRegion && wantsInputRegion && actorData.actor.get_paint_visibility())
+ rects.push(new Meta.Rectangle({ x, y, width: w, height: h }));
+
+ let monitor = null;
+ if (actorData.affectsStruts)
+ monitor = this.findMonitorForActor(actorData.actor);
+
+ if (monitor) {
+ // Limit struts to the size of the screen
+ let x1 = Math.max(x, 0);
+ let x2 = Math.min(x + w, global.screen_width);
+ let y1 = Math.max(y, 0);
+ let y2 = Math.min(y + h, global.screen_height);
+
+ // Metacity wants to know what side of the monitor the
+ // strut is considered to be attached to. First, we find
+ // the monitor that contains the strut. If the actor is
+ // only touching one edge, or is touching the entire
+ // border of that monitor, then it's obvious which side
+ // to call it. If it's in a corner, we pick a side
+ // arbitrarily. If it doesn't touch any edges, or it
+ // spans the width/height across the middle of the
+ // screen, then we don't create a strut for it at all.
+
+ let side;
+ if (x1 <= monitor.x && x2 >= monitor.x + monitor.width) {
+ if (y1 <= monitor.y)
+ side = Meta.Side.TOP;
+ else if (y2 >= monitor.y + monitor.height)
+ side = Meta.Side.BOTTOM;
+ else
+ continue;
+ } else if (y1 <= monitor.y && y2 >= monitor.y + monitor.height) {
+ if (x1 <= monitor.x)
+ side = Meta.Side.LEFT;
+ else if (x2 >= monitor.x + monitor.width)
+ side = Meta.Side.RIGHT;
+ else
+ continue;
+ } else if (x1 <= monitor.x) {
+ side = Meta.Side.LEFT;
+ } else if (y1 <= monitor.y) {
+ side = Meta.Side.TOP;
+ } else if (x2 >= monitor.x + monitor.width) {
+ side = Meta.Side.RIGHT;
+ } else if (y2 >= monitor.y + monitor.height) {
+ side = Meta.Side.BOTTOM;
+ } else {
+ continue;
+ }
+
+ let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1 });
+ let strut = new Meta.Strut({ rect: strutRect, side });
+ struts.push(strut);
+ }
+ }
+
+ if (wantsInputRegion)
+ global.set_stage_input_region(rects);
+
+ this._isPopupWindowVisible = isPopupMenuVisible;
+
+ let workspaceManager = global.workspace_manager;
+ for (let w = 0; w < workspaceManager.n_workspaces; w++) {
+ let workspace = workspaceManager.get_workspace_by_index(w);
+ workspace.set_builtin_struts(struts);
+ }
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ modalEnded() {
+ // We don't update the stage input region while in a modal,
+ // so queue an update now.
+ this._queueUpdateRegions();
+ }
+});
+
+
+// HotCorner:
+//
+// This class manages a "hot corner" that can toggle switching to
+// overview.
+var HotCorner = GObject.registerClass(
+class HotCorner extends Clutter.Actor {
+ _init(layoutManager, monitor, x, y) {
+ super._init();
+
+ // We use this flag to mark the case where the user has entered the
+ // hot corner and has not left both the hot corner and a surrounding
+ // guard area (the "environs"). This avoids triggering the hot corner
+ // multiple times due to an accidental jitter.
+ this._entered = false;
+
+ this._monitor = monitor;
+
+ this._x = x;
+ this._y = y;
+
+ this._setupFallbackCornerIfNeeded(layoutManager);
+
+ this._pressureBarrier = new PressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD,
+ HOT_CORNER_PRESSURE_TIMEOUT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW);
+ this._pressureBarrier.connect('trigger', this._toggleOverview.bind(this));
+
+ let px = 0.0;
+ let py = 0.0;
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
+ px = 1.0;
+ py = 0.0;
+ }
+
+ this._ripples = new Ripples.Ripples(px, py, 'ripple-box');
+ this._ripples.addTo(layoutManager.uiGroup);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ setBarrierSize(size) {
+ if (this._verticalBarrier) {
+ this._pressureBarrier.removeBarrier(this._verticalBarrier);
+ this._verticalBarrier.destroy();
+ this._verticalBarrier = null;
+ }
+
+ if (this._horizontalBarrier) {
+ this._pressureBarrier.removeBarrier(this._horizontalBarrier);
+ this._horizontalBarrier.destroy();
+ this._horizontalBarrier = null;
+ }
+
+ if (size > 0) {
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
+ this._verticalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
+ directions: Meta.BarrierDirection.NEGATIVE_X,
+ });
+ this._horizontalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x - size, x2: this._x, y1: this._y, y2: this._y,
+ directions: Meta.BarrierDirection.POSITIVE_Y,
+ });
+ } else {
+ this._verticalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
+ directions: Meta.BarrierDirection.POSITIVE_X,
+ });
+ this._horizontalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x + size, y1: this._y, y2: this._y,
+ directions: Meta.BarrierDirection.POSITIVE_Y,
+ });
+ }
+
+ this._pressureBarrier.addBarrier(this._verticalBarrier);
+ this._pressureBarrier.addBarrier(this._horizontalBarrier);
+ }
+ }
+
+ _setupFallbackCornerIfNeeded(layoutManager) {
+ if (!global.display.supports_extended_barriers()) {
+ this.set({
+ name: 'hot-corner-environs',
+ x: this._x,
+ y: this._y,
+ width: 3,
+ height: 3,
+ reactive: true,
+ });
+
+ this._corner = new Clutter.Actor({
+ name: 'hot-corner',
+ width: 1,
+ height: 1,
+ opacity: 0,
+ reactive: true,
+ });
+ this._corner._delegate = this;
+
+ this.add_child(this._corner);
+ layoutManager.addChrome(this);
+
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
+ this._corner.set_position(this.width - this._corner.width, 0);
+ this.set_pivot_point(1.0, 0.0);
+ this.translation_x = -this.width;
+ } else {
+ this._corner.set_position(0, 0);
+ }
+
+ this._corner.connect('enter-event',
+ this._onCornerEntered.bind(this));
+ this._corner.connect('leave-event',
+ this._onCornerLeft.bind(this));
+ }
+ }
+
+ _onDestroy() {
+ this.setBarrierSize(0);
+ this._pressureBarrier.destroy();
+ this._pressureBarrier = null;
+
+ this._ripples.destroy();
+ }
+
+ _toggleOverview() {
+ if (this._monitor.inFullscreen && !Main.overview.visible)
+ return;
+
+ if (Main.overview.shouldToggleByCornerOrButton()) {
+ Main.overview.toggle();
+ if (Main.overview.animationInProgress)
+ this._ripples.playAnimation(this._x, this._y);
+ }
+ }
+
+ handleDragOver(source, _actor, _x, _y, _time) {
+ if (source != Main.xdndHandler)
+ return DND.DragMotionResult.CONTINUE;
+
+ this._toggleOverview();
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ _onCornerEntered() {
+ if (!this._entered) {
+ this._entered = true;
+ this._toggleOverview();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onCornerLeft(actor, event) {
+ if (event.get_related() != this)
+ this._entered = false;
+ // Consume event, otherwise this will confuse onEnvironsLeft
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_leave_event(crossingEvent) {
+ if (crossingEvent.related != this._corner)
+ this._entered = false;
+ return Clutter.EVENT_PROPAGATE;
+ }
+});
+
+var PressureBarrier = class PressureBarrier extends Signals.EventEmitter {
+ constructor(threshold, timeout, actionMode) {
+ super();
+
+ this._threshold = threshold;
+ this._timeout = timeout;
+ this._actionMode = actionMode;
+ this._barriers = [];
+ this._eventFilter = null;
+
+ this._isTriggered = false;
+ this._reset();
+ }
+
+ addBarrier(barrier) {
+ barrier._pressureHitId = barrier.connect('hit', this._onBarrierHit.bind(this));
+ barrier._pressureLeftId = barrier.connect('left', this._onBarrierLeft.bind(this));
+
+ this._barriers.push(barrier);
+ }
+
+ _disconnectBarrier(barrier) {
+ barrier.disconnect(barrier._pressureHitId);
+ barrier.disconnect(barrier._pressureLeftId);
+ }
+
+ removeBarrier(barrier) {
+ this._disconnectBarrier(barrier);
+ this._barriers.splice(this._barriers.indexOf(barrier), 1);
+ }
+
+ destroy() {
+ this._barriers.forEach(this._disconnectBarrier.bind(this));
+ this._barriers = [];
+ }
+
+ setEventFilter(filter) {
+ this._eventFilter = filter;
+ }
+
+ _reset() {
+ this._barrierEvents = [];
+ this._currentPressure = 0;
+ this._lastTime = 0;
+ }
+
+ _isHorizontal(barrier) {
+ return barrier.y1 == barrier.y2;
+ }
+
+ _getDistanceAcrossBarrier(barrier, event) {
+ if (this._isHorizontal(barrier))
+ return Math.abs(event.dy);
+ else
+ return Math.abs(event.dx);
+ }
+
+ _getDistanceAlongBarrier(barrier, event) {
+ if (this._isHorizontal(barrier))
+ return Math.abs(event.dx);
+ else
+ return Math.abs(event.dy);
+ }
+
+ _trimBarrierEvents() {
+ // Events are guaranteed to be sorted in time order from
+ // oldest to newest, so just look for the first old event,
+ // and then chop events after that off.
+ let i = 0;
+ let threshold = this._lastTime - this._timeout;
+
+ while (i < this._barrierEvents.length) {
+ let [time, distance_] = this._barrierEvents[i];
+ if (time >= threshold)
+ break;
+ i++;
+ }
+
+ let firstNewEvent = i;
+
+ for (i = 0; i < firstNewEvent; i++) {
+ let [time_, distance] = this._barrierEvents[i];
+ this._currentPressure -= distance;
+ }
+
+ this._barrierEvents = this._barrierEvents.slice(firstNewEvent);
+ }
+
+ _onBarrierLeft(barrier, _event) {
+ barrier._isHit = false;
+ if (this._barriers.every(b => !b._isHit)) {
+ this._reset();
+ this._isTriggered = false;
+ }
+ }
+
+ _trigger() {
+ this._isTriggered = true;
+ this.emit('trigger');
+ this._reset();
+ }
+
+ _onBarrierHit(barrier, event) {
+ barrier._isHit = true;
+
+ // If we've triggered the barrier, wait until the pointer has the
+ // left the barrier hitbox until we trigger it again.
+ if (this._isTriggered)
+ return;
+
+ if (this._eventFilter && this._eventFilter(event))
+ return;
+
+ // Throw out all events not in the proper keybinding mode
+ if (!(this._actionMode & Main.actionMode))
+ return;
+
+ let slide = this._getDistanceAlongBarrier(barrier, event);
+ let distance = this._getDistanceAcrossBarrier(barrier, event);
+
+ if (distance >= this._threshold) {
+ this._trigger();
+ return;
+ }
+
+ // Throw out events where the cursor is move more
+ // along the axis of the barrier than moving with
+ // the barrier.
+ if (slide > distance)
+ return;
+
+ this._lastTime = event.time;
+
+ this._trimBarrierEvents();
+ distance = Math.min(15, distance);
+
+ this._barrierEvents.push([event.time, distance]);
+ this._currentPressure += distance;
+
+ if (this._currentPressure >= this._threshold)
+ this._trigger();
+ }
+};
+
+var ScreenTransition = GObject.registerClass(
+class ScreenTransition extends Clutter.Actor {
+ _init() {
+ super._init({ visible: false });
+ }
+
+ vfunc_hide() {
+ this.content = null;
+ super.vfunc_hide();
+ }
+
+ run() {
+ if (this.visible)
+ return;
+
+ Main.uiGroup.set_child_above_sibling(this, null);
+
+ const rect = new imports.gi.cairo.RectangleInt({
+ x: 0,
+ y: 0,
+ width: global.screen_width,
+ height: global.screen_height,
+ });
+ const [, , , scale] = global.stage.get_capture_final_size(rect);
+ this.content = global.stage.paint_to_content(rect, scale, Clutter.PaintFlag.NO_CURSORS);
+
+ this.opacity = 255;
+ this.show();
+
+ this.ease({
+ opacity: 0,
+ duration: SCREEN_TRANSITION_DURATION,
+ delay: SCREEN_TRANSITION_DELAY,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this.hide(),
+ });
+ }
+});
diff --git a/js/ui/lightbox.js b/js/ui/lightbox.js
new file mode 100644
index 0000000..b0ca77a
--- /dev/null
+++ b/js/ui/lightbox.js
@@ -0,0 +1,289 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Lightbox */
+
+const { Clutter, GObject, Shell, St } = imports.gi;
+
+const Params = imports.misc.params;
+
+var DEFAULT_FADE_FACTOR = 0.4;
+var VIGNETTE_BRIGHTNESS = 0.5;
+var VIGNETTE_SHARPNESS = 0.7;
+
+const VIGNETTE_DECLARATIONS = ' \
+uniform float brightness; \n\
+uniform float vignette_sharpness; \n\
+float rand(vec2 p) { \n\
+ return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453123); \n\
+} \n';
+
+const VIGNETTE_CODE = ' \
+cogl_color_out.a = cogl_color_in.a; \n\
+cogl_color_out.rgb = vec3(0.0, 0.0, 0.0); \n\
+vec2 position = cogl_tex_coord_in[0].xy - 0.5; \n\
+float t = clamp(length(1.41421 * position), 0.0, 1.0); \n\
+float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t); \n\
+cogl_color_out.a *= 1.0 - pixel_brightness * brightness; \n\
+cogl_color_out.a += (rand(position) - 0.5) / 100.0; \n';
+
+
+var RadialShaderEffect = GObject.registerClass({
+ Properties: {
+ 'brightness': GObject.ParamSpec.float(
+ 'brightness', 'brightness', 'brightness',
+ GObject.ParamFlags.READWRITE,
+ 0, 1, 1),
+ 'sharpness': GObject.ParamSpec.float(
+ 'sharpness', 'sharpness', 'sharpness',
+ GObject.ParamFlags.READWRITE,
+ 0, 1, 0),
+ },
+}, class RadialShaderEffect extends Shell.GLSLEffect {
+ _init(params) {
+ this._brightness = undefined;
+ this._sharpness = undefined;
+
+ super._init(params);
+
+ this._brightnessLocation = this.get_uniform_location('brightness');
+ this._sharpnessLocation = this.get_uniform_location('vignette_sharpness');
+
+ this.brightness = 1.0;
+ this.sharpness = 0.0;
+ }
+
+ vfunc_build_pipeline() {
+ this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT,
+ VIGNETTE_DECLARATIONS, VIGNETTE_CODE, true);
+ }
+
+ get brightness() {
+ return this._brightness;
+ }
+
+ set brightness(v) {
+ if (this._brightness === v)
+ return;
+ this._brightness = v;
+ this.set_uniform_float(this._brightnessLocation,
+ 1, [this._brightness]);
+ this.notify('brightness');
+ }
+
+ get sharpness() {
+ return this._sharpness;
+ }
+
+ set sharpness(v) {
+ if (this._sharpness === v)
+ return;
+ this._sharpness = v;
+ this.set_uniform_float(this._sharpnessLocation,
+ 1, [this._sharpness]);
+ this.notify('sharpness');
+ }
+});
+
+/**
+ * Lightbox:
+ * @container: parent Clutter.Container
+ * @params: (optional) additional parameters:
+ * - inhibitEvents: whether to inhibit events for @container
+ * - width: shade actor width
+ * - height: shade actor height
+ * - fadeFactor: fading opacity factor
+ * - radialEffect: whether to enable the GLSL radial effect
+ *
+ * Lightbox creates a dark translucent "shade" actor to hide the
+ * contents of @container, and allows you to specify particular actors
+ * in @container to highlight by bringing them above the shade. It
+ * tracks added and removed actors in @container while the lightboxing
+ * is active, and ensures that all actors are returned to their
+ * original stacking order when the lightboxing is removed. (However,
+ * if actors are restacked by outside code while the lightboxing is
+ * active, the lightbox may later revert them back to their original
+ * order.)
+ *
+ * By default, the shade window will have the height and width of
+ * @container and will track any changes in its size. You can override
+ * this by passing an explicit width and height in @params.
+ */
+var Lightbox = GObject.registerClass({
+ Properties: {
+ 'active': GObject.ParamSpec.boolean(
+ 'active', 'active', 'active', GObject.ParamFlags.READABLE, false),
+ },
+}, class Lightbox extends St.Bin {
+ _init(container, params) {
+ params = Params.parse(params, {
+ inhibitEvents: false,
+ width: null,
+ height: null,
+ fadeFactor: DEFAULT_FADE_FACTOR,
+ radialEffect: false,
+ });
+
+ super._init({
+ reactive: params.inhibitEvents,
+ width: params.width,
+ height: params.height,
+ visible: false,
+ });
+
+ this._active = false;
+ this._container = container;
+ this._children = container.get_children();
+ this._fadeFactor = params.fadeFactor;
+ this._radialEffect = params.radialEffect;
+
+ if (this._radialEffect)
+ this.add_effect(new RadialShaderEffect({ name: 'radial' }));
+ else
+ this.set({ opacity: 0, style_class: 'lightbox' });
+
+ container.add_actor(this);
+ container.set_child_above_sibling(this, null);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ if (!params.width || !params.height) {
+ this.add_constraint(new Clutter.BindConstraint({
+ source: container,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }));
+ }
+
+ container.connectObject(
+ 'actor-added', this._actorAdded.bind(this),
+ 'actor-removed', this._actorRemoved.bind(this), this);
+
+ this._highlighted = null;
+ }
+
+ get active() {
+ return this._active;
+ }
+
+ _actorAdded(container, newChild) {
+ let children = this._container.get_children();
+ let myIndex = children.indexOf(this);
+ let newChildIndex = children.indexOf(newChild);
+
+ if (newChildIndex > myIndex) {
+ // The child was added above the shade (presumably it was
+ // made the new top-most child). Move it below the shade,
+ // and add it to this._children as the new topmost actor.
+ this._container.set_child_above_sibling(this, newChild);
+ this._children.push(newChild);
+ } else if (newChildIndex == 0) {
+ // Bottom of stack
+ this._children.unshift(newChild);
+ } else {
+ // Somewhere else; insert it into the correct spot
+ let prevChild = this._children.indexOf(children[newChildIndex - 1]);
+ if (prevChild != -1) // paranoia
+ this._children.splice(prevChild + 1, 0, newChild);
+ }
+ }
+
+ lightOn(fadeInTime) {
+ this.remove_all_transitions();
+
+ let easeProps = {
+ duration: fadeInTime || 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ };
+
+ let onComplete = () => {
+ this._active = true;
+ this.notify('active');
+ };
+
+ this.show();
+
+ if (this._radialEffect) {
+ this.ease_property(
+ '@effects.radial.brightness', VIGNETTE_BRIGHTNESS, easeProps);
+ this.ease_property(
+ '@effects.radial.sharpness', VIGNETTE_SHARPNESS,
+ Object.assign({ onComplete }, easeProps));
+ } else {
+ this.ease(Object.assign(easeProps, {
+ opacity: 255 * this._fadeFactor,
+ onComplete,
+ }));
+ }
+ }
+
+ lightOff(fadeOutTime) {
+ this.remove_all_transitions();
+
+ this._active = false;
+ this.notify('active');
+
+ let easeProps = {
+ duration: fadeOutTime || 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ };
+
+ let onComplete = () => this.hide();
+
+ if (this._radialEffect) {
+ this.ease_property(
+ '@effects.radial.brightness', 1.0, easeProps);
+ this.ease_property(
+ '@effects.radial.sharpness', 0.0, Object.assign({ onComplete }, easeProps));
+ } else {
+ this.ease(Object.assign(easeProps, { opacity: 0, onComplete }));
+ }
+ }
+
+ _actorRemoved(container, child) {
+ let index = this._children.indexOf(child);
+ if (index != -1) // paranoia
+ this._children.splice(index, 1);
+
+ if (child == this._highlighted)
+ this._highlighted = null;
+ }
+
+ /**
+ * highlight:
+ * @param {Clutter.Actor=} window: actor to highlight
+ *
+ * Highlights the indicated actor and unhighlights any other
+ * currently-highlighted actor. With no arguments or a false/null
+ * argument, all actors will be unhighlighted.
+ */
+ highlight(window) {
+ if (this._highlighted == window)
+ return;
+
+ // Walk this._children raising and lowering actors as needed.
+ // Things get a little tricky if the to-be-raised and
+ // to-be-lowered actors were originally adjacent, in which
+ // case we may need to indicate some *other* actor as the new
+ // sibling of the to-be-lowered one.
+
+ let below = this;
+ for (let i = this._children.length - 1; i >= 0; i--) {
+ if (this._children[i] == window)
+ this._container.set_child_above_sibling(this._children[i], null);
+ else if (this._children[i] == this._highlighted)
+ this._container.set_child_below_sibling(this._children[i], below);
+ else
+ below = this._children[i];
+ }
+
+ this._highlighted = window;
+ }
+
+ /**
+ * _onDestroy:
+ *
+ * This is called when the lightbox' actor is destroyed, either
+ * by destroying its container or by explicitly calling this.destroy().
+ */
+ _onDestroy() {
+ this.highlight(null);
+ }
+});
diff --git a/js/ui/locatePointer.js b/js/ui/locatePointer.js
new file mode 100644
index 0000000..6ae2941
--- /dev/null
+++ b/js/ui/locatePointer.js
@@ -0,0 +1,39 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported LocatePointer */
+
+const { Gio } = imports.gi;
+const Ripples = imports.ui.ripples;
+const Main = imports.ui.main;
+
+const LOCATE_POINTER_KEY = "locate-pointer";
+const LOCATE_POINTER_SCHEMA = "org.gnome.desktop.interface";
+
+var LocatePointer = class {
+ constructor() {
+ this._settings = new Gio.Settings({ schema_id: LOCATE_POINTER_SCHEMA });
+ this._settings.connect(`changed::${LOCATE_POINTER_KEY}`, () => this._syncEnabled());
+ this._syncEnabled();
+ }
+
+ _syncEnabled() {
+ let enabled = this._settings.get_boolean(LOCATE_POINTER_KEY);
+ if (enabled == !!this._ripples)
+ return;
+
+ if (enabled) {
+ this._ripples = new Ripples.Ripples(0.5, 0.5, 'ripple-pointer-location');
+ this._ripples.addTo(Main.uiGroup);
+ } else {
+ this._ripples.destroy();
+ this._ripples = null;
+ }
+ }
+
+ show() {
+ if (!this._ripples)
+ return;
+
+ let [x, y] = global.get_pointer();
+ this._ripples.playAnimation(x, y);
+ }
+};
diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js
new file mode 100644
index 0000000..6b6b65f
--- /dev/null
+++ b/js/ui/lookingGlass.js
@@ -0,0 +1,1670 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported LookingGlass */
+
+const {
+ Clutter, Cogl, Gio, GLib, GObject, Graphene, Meta, Pango, Shell, St,
+} = imports.gi;
+const Signals = imports.misc.signals;
+const System = imports.system;
+
+const History = imports.misc.history;
+const ExtensionUtils = imports.misc.extensionUtils;
+const PopupMenu = imports.ui.popupMenu;
+const ShellEntry = imports.ui.shellEntry;
+const Main = imports.ui.main;
+const JsParse = imports.misc.jsParse;
+
+const { ExtensionState } = ExtensionUtils;
+
+const CHEVRON = '>>> ';
+
+/* Imports...feel free to add here as needed */
+var commandHeader = 'const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; ' +
+ 'const Main = imports.ui.main; ' +
+ /* Utility functions...we should probably be able to use these
+ * in the shell core code too. */
+ 'const stage = global.stage; ' +
+ /* Special lookingGlass functions */
+ 'const inspect = Main.lookingGlass.inspect.bind(Main.lookingGlass); ' +
+ 'const it = Main.lookingGlass.getIt(); ' +
+ 'const r = Main.lookingGlass.getResult.bind(Main.lookingGlass); ';
+
+const HISTORY_KEY = 'looking-glass-history';
+// Time between tabs for them to count as a double-tab event
+var AUTO_COMPLETE_DOUBLE_TAB_DELAY = 500;
+var AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION = 200;
+var AUTO_COMPLETE_GLOBAL_KEYWORDS = _getAutoCompleteGlobalKeywords();
+
+const LG_ANIMATION_TIME = 500;
+
+const CLUTTER_DEBUG_FLAG_CATEGORIES = new Map([
+ // Paint debugging can easily result in a non-responsive session
+ ['DebugFlag', { argPos: 0, exclude: ['PAINT'] }],
+ ['DrawDebugFlag', { argPos: 1, exclude: [] }],
+ // Exluded due to the only current option likely to result in shooting ones
+ // foot
+ // ['PickDebugFlag', { argPos: 2, exclude: [] }],
+]);
+
+function _getAutoCompleteGlobalKeywords() {
+ const keywords = ['true', 'false', 'null', 'new'];
+ // Don't add the private properties of globalThis (i.e., ones starting with '_')
+ const windowProperties = Object.getOwnPropertyNames(globalThis).filter(
+ a => a.charAt(0) !== '_');
+ const headerProperties = JsParse.getDeclaredConstants(commandHeader);
+
+ return keywords.concat(windowProperties).concat(headerProperties);
+}
+
+var AutoComplete = class AutoComplete extends Signals.EventEmitter {
+ constructor(entry) {
+ super();
+
+ this._entry = entry;
+ this._entry.connect('key-press-event', this._entryKeyPressEvent.bind(this));
+ this._lastTabTime = global.get_current_time();
+ }
+
+ _processCompletionRequest(event) {
+ if (event.completions.length == 0)
+ return;
+
+ // Unique match = go ahead and complete; multiple matches + single tab = complete the common starting string;
+ // multiple matches + double tab = emit a suggest event with all possible options
+ if (event.completions.length == 1) {
+ this.additionalCompletionText(event.completions[0], event.attrHead);
+ this.emit('completion', { completion: event.completions[0], type: 'whole-word' });
+ } else if (event.completions.length > 1 && event.tabType === 'single') {
+ let commonPrefix = JsParse.getCommonPrefix(event.completions);
+
+ if (commonPrefix.length > 0) {
+ this.additionalCompletionText(commonPrefix, event.attrHead);
+ this.emit('completion', { completion: commonPrefix, type: 'prefix' });
+ this.emit('suggest', { completions: event.completions });
+ }
+ } else if (event.completions.length > 1 && event.tabType === 'double') {
+ this.emit('suggest', { completions: event.completions });
+ }
+ }
+
+ _entryKeyPressEvent(actor, event) {
+ let cursorPos = this._entry.clutter_text.get_cursor_position();
+ let text = this._entry.get_text();
+ if (cursorPos != -1)
+ text = text.slice(0, cursorPos);
+
+ if (event.get_key_symbol() == Clutter.KEY_Tab) {
+ let [completions, attrHead] = JsParse.getCompletions(text, commandHeader, AUTO_COMPLETE_GLOBAL_KEYWORDS);
+ let currTime = global.get_current_time();
+ if ((currTime - this._lastTabTime) < AUTO_COMPLETE_DOUBLE_TAB_DELAY) {
+ this._processCompletionRequest({
+ tabType: 'double',
+ completions,
+ attrHead,
+ });
+ } else {
+ this._processCompletionRequest({
+ tabType: 'single',
+ completions,
+ attrHead,
+ });
+ }
+ this._lastTabTime = currTime;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ // Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
+ // the string "bc" will be appended to this._entry
+ additionalCompletionText(text, head) {
+ let additionalCompletionText = text.slice(head.length);
+ let cursorPos = this._entry.clutter_text.get_cursor_position();
+
+ this._entry.clutter_text.insert_text(additionalCompletionText, cursorPos);
+ }
+};
+
+
+var Notebook = GObject.registerClass({
+ Signals: { 'selection': { param_types: [Clutter.Actor.$gtype] } },
+}, class Notebook extends St.BoxLayout {
+ _init() {
+ super._init({
+ vertical: true,
+ y_expand: true,
+ });
+
+ this.tabControls = new St.BoxLayout({ style_class: 'labels' });
+
+ this._selectedIndex = -1;
+ this._tabs = [];
+ }
+
+ appendPage(name, child) {
+ const labelBox = new St.BoxLayout({
+ style_class: 'notebook-tab',
+ reactive: true,
+ track_hover: true,
+ });
+ let label = new St.Button({ label: name });
+ label.connect('clicked', () => {
+ this.selectChild(child);
+ return true;
+ });
+ labelBox.add_child(label);
+ this.tabControls.add(labelBox);
+
+ let scrollview = new St.ScrollView({ y_expand: true });
+ scrollview.get_hscroll_bar().hide();
+ scrollview.add_actor(child);
+
+ const tabData = {
+ child,
+ labelBox,
+ label,
+ scrollView: scrollview,
+ _scrollToBottom: false,
+ };
+ this._tabs.push(tabData);
+ scrollview.hide();
+ this.add_child(scrollview);
+
+ let vAdjust = scrollview.vscroll.adjustment;
+ vAdjust.connect('changed', () => this._onAdjustScopeChanged(tabData));
+ vAdjust.connect('notify::value', () => this._onAdjustValueChanged(tabData));
+
+ if (this._selectedIndex == -1)
+ this.selectIndex(0);
+ }
+
+ _unselect() {
+ if (this._selectedIndex < 0)
+ return;
+ let tabData = this._tabs[this._selectedIndex];
+ tabData.labelBox.remove_style_pseudo_class('selected');
+ tabData.scrollView.hide();
+ this._selectedIndex = -1;
+ }
+
+ selectIndex(index) {
+ if (index == this._selectedIndex)
+ return;
+ if (index < 0) {
+ this._unselect();
+ this.emit('selection', null);
+ return;
+ }
+
+ // Focus the new tab before unmapping the old one
+ let tabData = this._tabs[index];
+ if (!tabData.scrollView.navigate_focus(null, St.DirectionType.TAB_FORWARD, false))
+ this.grab_key_focus();
+
+ this._unselect();
+
+ tabData.labelBox.add_style_pseudo_class('selected');
+ tabData.scrollView.show();
+ this._selectedIndex = index;
+ this.emit('selection', tabData.child);
+ }
+
+ selectChild(child) {
+ if (child == null) {
+ this.selectIndex(-1);
+ } else {
+ for (let i = 0; i < this._tabs.length; i++) {
+ let tabData = this._tabs[i];
+ if (tabData.child == child) {
+ this.selectIndex(i);
+ return;
+ }
+ }
+ }
+ }
+
+ scrollToBottom(index) {
+ let tabData = this._tabs[index];
+ tabData._scrollToBottom = true;
+ }
+
+ _onAdjustValueChanged(tabData) {
+ let vAdjust = tabData.scrollView.vscroll.adjustment;
+ if (vAdjust.value < (vAdjust.upper - vAdjust.lower - 0.5))
+ tabData._scrolltoBottom = false;
+ }
+
+ _onAdjustScopeChanged(tabData) {
+ if (!tabData._scrollToBottom)
+ return;
+ let vAdjust = tabData.scrollView.vscroll.adjustment;
+ vAdjust.value = vAdjust.upper - vAdjust.page_size;
+ }
+
+ nextTab() {
+ let nextIndex = this._selectedIndex;
+ if (nextIndex < this._tabs.length - 1)
+ ++nextIndex;
+
+ this.selectIndex(nextIndex);
+ }
+
+ prevTab() {
+ let prevIndex = this._selectedIndex;
+ if (prevIndex > 0)
+ --prevIndex;
+
+ this.selectIndex(prevIndex);
+ }
+});
+
+function objectToString(o) {
+ if (typeof o == typeof objectToString) {
+ // special case this since the default is way, way too verbose
+ return '<js function>';
+ } else if (o && o.toString === undefined) {
+ // eeks, something unprintable. we'll have to guess, probably a module
+ return typeof o === 'object' && !(o instanceof Object)
+ ? '<module>'
+ : '<unknown>';
+ } else {
+ return `${o}`;
+ }
+}
+
+var ObjLink = GObject.registerClass(
+class ObjLink extends St.Button {
+ _init(lookingGlass, o, title) {
+ let text;
+ if (title)
+ text = title;
+ else
+ text = objectToString(o);
+ text = GLib.markup_escape_text(text, -1);
+
+ super._init({
+ reactive: true,
+ track_hover: true,
+ style_class: 'shell-link',
+ label: text,
+ x_align: Clutter.ActorAlign.START,
+ });
+ this.get_child().single_line_mode = true;
+
+ this._obj = o;
+ this._lookingGlass = lookingGlass;
+ }
+
+ vfunc_clicked() {
+ this._lookingGlass.inspectObject(this._obj, this);
+ }
+});
+
+var Result = GObject.registerClass(
+class Result extends St.BoxLayout {
+ _init(lookingGlass, command, o, index) {
+ super._init({ vertical: true });
+
+ this.index = index;
+ this.o = o;
+
+ this._lookingGlass = lookingGlass;
+
+ let cmdTxt = new St.Label({ text: command });
+ cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
+ this.add(cmdTxt);
+ let box = new St.BoxLayout({});
+ this.add(box);
+ let resultTxt = new St.Label({ text: `r(${index}) = ` });
+ resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
+ box.add(resultTxt);
+ let objLink = new ObjLink(this._lookingGlass, o);
+ box.add(objLink);
+ }
+});
+
+var WindowList = GObject.registerClass({
+}, class WindowList extends St.BoxLayout {
+ _init(lookingGlass) {
+ super._init({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
+ let tracker = Shell.WindowTracker.get_default();
+ this._updateId = Main.initializeDeferredWork(this, this._updateWindowList.bind(this));
+ global.display.connect('window-created', this._updateWindowList.bind(this));
+ tracker.connect('tracked-windows-changed', this._updateWindowList.bind(this));
+
+ this._lookingGlass = lookingGlass;
+ }
+
+ _updateWindowList() {
+ if (!this._lookingGlass.isOpen)
+ return;
+
+ this.destroy_all_children();
+ let windows = global.get_window_actors();
+ let tracker = Shell.WindowTracker.get_default();
+ for (let i = 0; i < windows.length; i++) {
+ let metaWindow = windows[i].metaWindow;
+ // Avoid multiple connections
+ if (!metaWindow._lookingGlassManaged) {
+ metaWindow.connect('unmanaged', this._updateWindowList.bind(this));
+ metaWindow._lookingGlassManaged = true;
+ }
+ let box = new St.BoxLayout({ vertical: true });
+ this.add(box);
+ let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title);
+ box.add_child(windowLink);
+ let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
+ box.add(propsBox);
+ propsBox.add(new St.Label({ text: `wmclass: ${metaWindow.get_wm_class()}` }));
+ let app = tracker.get_window_app(metaWindow);
+ if (app != null && !app.is_window_backed()) {
+ let icon = app.create_icon_texture(22);
+ let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
+ propsBox.add(propBox);
+ propBox.add_child(new St.Label({ text: 'app: ' }));
+ let appLink = new ObjLink(this._lookingGlass, app, app.get_id());
+ propBox.add_child(appLink);
+ propBox.add_child(icon);
+ } else {
+ propsBox.add(new St.Label({ text: '<untracked>' }));
+ }
+ }
+ }
+
+ update() {
+ this._updateWindowList();
+ }
+});
+
+var ObjInspector = GObject.registerClass(
+class ObjInspector extends St.ScrollView {
+ _init(lookingGlass) {
+ super._init({
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ });
+
+ this._obj = null;
+ this._previousObj = null;
+
+ this._parentList = [];
+
+ this.get_hscroll_bar().hide();
+ this._container = new St.BoxLayout({
+ name: 'LookingGlassPropertyInspector',
+ style_class: 'lg-dialog',
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ });
+ this.add_actor(this._container);
+
+ this._lookingGlass = lookingGlass;
+ }
+
+ selectObject(obj, skipPrevious) {
+ if (!skipPrevious)
+ this._previousObj = this._obj;
+ else
+ this._previousObj = null;
+ this._obj = obj;
+
+ this._container.destroy_all_children();
+
+ let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
+ this._container.add_actor(hbox);
+ let label = new St.Label({
+ text: `Inspecting: ${typeof obj}: ${objectToString(obj)}`,
+ x_expand: true,
+ });
+ label.single_line_mode = true;
+ hbox.add_child(label);
+ let button = new St.Button({ label: 'Insert', style_class: 'lg-obj-inspector-button' });
+ button.connect('clicked', this._onInsert.bind(this));
+ hbox.add(button);
+
+ if (this._previousObj != null) {
+ button = new St.Button({ label: 'Back', style_class: 'lg-obj-inspector-button' });
+ button.connect('clicked', this._onBack.bind(this));
+ hbox.add(button);
+ }
+
+ button = new St.Button({
+ style_class: 'window-close',
+ icon_name: 'window-close-symbolic',
+ });
+ button.connect('clicked', this.close.bind(this));
+ hbox.add(button);
+ if (typeof obj == typeof {}) {
+ let properties = [];
+ for (let propName in obj)
+ properties.push(propName);
+ properties.sort();
+
+ for (let i = 0; i < properties.length; i++) {
+ let propName = properties[i];
+ let link;
+ try {
+ let prop = obj[propName];
+ link = new ObjLink(this._lookingGlass, prop);
+ } catch (e) {
+ link = new St.Label({ text: '<error>' });
+ }
+ let box = new St.BoxLayout();
+ box.add(new St.Label({ text: `${propName}: ` }));
+ box.add(link);
+ this._container.add_actor(box);
+ }
+ }
+ }
+
+ open(sourceActor) {
+ if (this._open)
+ return;
+
+ const grab = Main.pushModal(this, { actionMode: Shell.ActionMode.LOOKING_GLASS });
+ if (grab.get_seat_state() !== Clutter.GrabState.ALL) {
+ Main.popModal(grab);
+ return;
+ }
+
+ this._grab = grab;
+ this._previousObj = null;
+ this._open = true;
+ this.show();
+ if (sourceActor) {
+ this.set_scale(0, 0);
+ this.ease({
+ scale_x: 1,
+ scale_y: 1,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: 200,
+ });
+ } else {
+ this.set_scale(1, 1);
+ }
+ }
+
+ close() {
+ if (!this._open)
+ return;
+ Main.popModal(this._grab);
+ this._grab = null;
+ this._open = false;
+ this.hide();
+ this._previousObj = null;
+ this._obj = null;
+ }
+
+ vfunc_key_press_event(keyPressEvent) {
+ const symbol = keyPressEvent.keyval;
+ if (symbol === Clutter.KEY_Escape) {
+ this.close();
+ return Clutter.EVENT_STOP;
+ }
+ return super.vfunc_key_press_event(keyPressEvent);
+ }
+
+ _onInsert() {
+ let obj = this._obj;
+ this.close();
+ this._lookingGlass.insertObject(obj);
+ }
+
+ _onBack() {
+ this.selectObject(this._previousObj, true);
+ }
+});
+
+var RedBorderEffect = GObject.registerClass(
+class RedBorderEffect extends Clutter.Effect {
+ _init() {
+ super._init();
+ this._pipeline = null;
+ }
+
+ vfunc_paint_node(node, paintContext) {
+ let actor = this.get_actor();
+
+ const actorNode = new Clutter.ActorNode(actor, -1);
+ node.add_child(actorNode);
+
+ if (!this._pipeline) {
+ const framebuffer = paintContext.get_framebuffer();
+ const coglContext = framebuffer.get_context();
+
+ let color = new Cogl.Color();
+ color.init_from_4ub(0xff, 0, 0, 0xc4);
+
+ this._pipeline = new Cogl.Pipeline(coglContext);
+ this._pipeline.set_color(color);
+ }
+
+ let alloc = actor.get_allocation_box();
+ let width = 2;
+
+ const pipelineNode = new Clutter.PipelineNode(this._pipeline);
+ pipelineNode.set_name('Red Border');
+ node.add_child(pipelineNode);
+
+ const box = new Clutter.ActorBox();
+
+ // clockwise order
+ box.set_origin(0, 0);
+ box.set_size(alloc.get_width(), width);
+ pipelineNode.add_rectangle(box);
+
+ box.set_origin(alloc.get_width() - width, width);
+ box.set_size(width, alloc.get_height() - width);
+ pipelineNode.add_rectangle(box);
+
+ box.set_origin(0, alloc.get_height() - width);
+ box.set_size(alloc.get_width() - width, width);
+ pipelineNode.add_rectangle(box);
+
+ box.set_origin(0, width);
+ box.set_size(width, alloc.get_height() - width * 2);
+ pipelineNode.add_rectangle(box);
+ }
+});
+
+var Inspector = GObject.registerClass({
+ Signals: {
+ 'closed': {},
+ 'target': { param_types: [Clutter.Actor.$gtype, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
+ },
+}, class Inspector extends Clutter.Actor {
+ _init(lookingGlass) {
+ super._init({ width: 0, height: 0 });
+
+ Main.uiGroup.add_actor(this);
+
+ const eventHandler = new St.BoxLayout({
+ name: 'LookingGlassDialog',
+ vertical: false,
+ reactive: true,
+ });
+ this._eventHandler = eventHandler;
+ this.add_actor(eventHandler);
+ this._displayText = new St.Label({ x_expand: true });
+ eventHandler.add_child(this._displayText);
+
+ eventHandler.connect('key-press-event', this._onKeyPressEvent.bind(this));
+ eventHandler.connect('button-press-event', this._onButtonPressEvent.bind(this));
+ eventHandler.connect('scroll-event', this._onScrollEvent.bind(this));
+ eventHandler.connect('motion-event', this._onMotionEvent.bind(this));
+
+ this._grab = global.stage.grab(eventHandler);
+
+ // this._target is the actor currently shown by the inspector.
+ // this._pointerTarget is the actor directly under the pointer.
+ // Normally these are the same, but if you use the scroll wheel
+ // to drill down, they'll diverge until you either scroll back
+ // out, or move the pointer outside of _pointerTarget.
+ this._target = null;
+ this._pointerTarget = null;
+
+ this._lookingGlass = lookingGlass;
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ if (!this._eventHandler)
+ return;
+
+ let primary = Main.layoutManager.primaryMonitor;
+
+ let [, , natWidth, natHeight] =
+ this._eventHandler.get_preferred_size();
+
+ let childBox = new Clutter.ActorBox();
+ childBox.x1 = primary.x + Math.floor((primary.width - natWidth) / 2);
+ childBox.x2 = childBox.x1 + natWidth;
+ childBox.y1 = primary.y + Math.floor((primary.height - natHeight) / 2);
+ childBox.y2 = childBox.y1 + natHeight;
+ this._eventHandler.allocate(childBox);
+ }
+
+ _close() {
+ if (this._grab) {
+ this._grab.dismiss();
+ this._grab = null;
+ }
+ this._eventHandler.destroy();
+ this._eventHandler = null;
+ this.emit('closed');
+ }
+
+ _onKeyPressEvent(actor, event) {
+ if (event.get_key_symbol() === Clutter.KEY_Escape)
+ this._close();
+ return Clutter.EVENT_STOP;
+ }
+
+ _onButtonPressEvent(actor, event) {
+ if (this._target) {
+ let [stageX, stageY] = event.get_coords();
+ this.emit('target', this._target, stageX, stageY);
+ }
+ this._close();
+ return Clutter.EVENT_STOP;
+ }
+
+ _onScrollEvent(actor, event) {
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP: {
+ // select parent
+ let parent = this._target.get_parent();
+ if (parent != null) {
+ this._target = parent;
+ this._update(event);
+ }
+ break;
+ }
+
+ case Clutter.ScrollDirection.DOWN:
+ // select child
+ if (this._target != this._pointerTarget) {
+ let child = this._pointerTarget;
+ while (child) {
+ let parent = child.get_parent();
+ if (parent == this._target)
+ break;
+ child = parent;
+ }
+ if (child) {
+ this._target = child;
+ this._update(event);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return Clutter.EVENT_STOP;
+ }
+
+ _onMotionEvent(actor, event) {
+ this._update(event);
+ return Clutter.EVENT_STOP;
+ }
+
+ _update(event) {
+ let [stageX, stageY] = event.get_coords();
+ let target = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
+ stageX,
+ stageY);
+
+ if (target != this._pointerTarget)
+ this._target = target;
+ this._pointerTarget = target;
+
+ let position = `[inspect x: ${stageX} y: ${stageY}]`;
+ this._displayText.text = '';
+ this._displayText.text = `${position} ${this._target}`;
+
+ this._lookingGlass.setBorderPaintTarget(this._target);
+ }
+});
+
+var Extensions = GObject.registerClass({
+}, class Extensions extends St.BoxLayout {
+ _init(lookingGlass) {
+ super._init({ vertical: true, name: 'lookingGlassExtensions' });
+
+ this._lookingGlass = lookingGlass;
+ this._noExtensions = new St.Label({
+ style_class: 'lg-extensions-none',
+ text: _('No extensions installed'),
+ });
+ this._numExtensions = 0;
+ this._extensionsList = new St.BoxLayout({
+ vertical: true,
+ style_class: 'lg-extensions-list',
+ });
+ this._extensionsList.add(this._noExtensions);
+ this.add(this._extensionsList);
+
+ Main.extensionManager.getUuids().forEach(uuid => {
+ this._loadExtension(null, uuid);
+ });
+
+ Main.extensionManager.connect('extension-loaded',
+ this._loadExtension.bind(this));
+ }
+
+ _loadExtension(o, uuid) {
+ let extension = Main.extensionManager.lookup(uuid);
+ // There can be cases where we create dummy extension metadata
+ // that's not really a proper extension. Don't bother with these.
+ if (!extension.metadata.name)
+ return;
+
+ let extensionDisplay = this._createExtensionDisplay(extension);
+ if (this._numExtensions == 0)
+ this._extensionsList.remove_actor(this._noExtensions);
+
+ this._numExtensions++;
+ const { name } = extension.metadata;
+ const pos = [...this._extensionsList].findIndex(
+ dsp => dsp._extension.metadata.name.localeCompare(name) > 0);
+ this._extensionsList.insert_child_at_index(extensionDisplay, pos);
+ }
+
+ _onViewSource(actor) {
+ let extension = actor._extension;
+ let uri = extension.dir.get_uri();
+ Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context(0, -1));
+ this._lookingGlass.close();
+ }
+
+ _onWebPage(actor) {
+ let extension = actor._extension;
+ Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context(0, -1));
+ this._lookingGlass.close();
+ }
+
+ _onViewErrors(actor) {
+ let extension = actor._extension;
+ let shouldShow = !actor._isShowing;
+
+ if (shouldShow) {
+ let errors = extension.errors;
+ let errorDisplay = new St.BoxLayout({ vertical: true });
+ if (errors && errors.length) {
+ for (let i = 0; i < errors.length; i++)
+ errorDisplay.add(new St.Label({ text: errors[i] }));
+ } else {
+ /* Translators: argument is an extension UUID. */
+ let message = _("%s has not emitted any errors.").format(extension.uuid);
+ errorDisplay.add(new St.Label({ text: message }));
+ }
+
+ actor._errorDisplay = errorDisplay;
+ actor._parentBox.add(errorDisplay);
+ actor.label = _("Hide Errors");
+ } else {
+ actor._errorDisplay.destroy();
+ actor._errorDisplay = null;
+ actor.label = _("Show Errors");
+ }
+
+ actor._isShowing = shouldShow;
+ }
+
+ _stateToString(extensionState) {
+ switch (extensionState) {
+ case ExtensionState.ENABLED:
+ return _("Enabled");
+ case ExtensionState.DISABLED:
+ case ExtensionState.INITIALIZED:
+ return _("Disabled");
+ case ExtensionState.ERROR:
+ return _("Error");
+ case ExtensionState.OUT_OF_DATE:
+ return _("Out of date");
+ case ExtensionState.DOWNLOADING:
+ return _("Downloading");
+ }
+ return 'Unknown'; // Not translated, shouldn't appear
+ }
+
+ _createExtensionDisplay(extension) {
+ let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
+ box._extension = extension;
+ let name = new St.Label({
+ style_class: 'lg-extension-name',
+ text: extension.metadata.name,
+ x_expand: true,
+ });
+ box.add_child(name);
+ let description = new St.Label({
+ style_class: 'lg-extension-description',
+ text: extension.metadata.description || 'No description',
+ x_expand: true,
+ });
+ box.add_child(description);
+
+ let metaBox = new St.BoxLayout({ style_class: 'lg-extension-meta' });
+ box.add(metaBox);
+ const state = new St.Label({
+ style_class: 'lg-extension-state',
+ text: this._stateToString(extension.state),
+ });
+ metaBox.add(state);
+
+ const viewsource = new St.Button({
+ reactive: true,
+ track_hover: true,
+ style_class: 'shell-link',
+ label: _('View Source'),
+ });
+ viewsource._extension = extension;
+ viewsource.connect('clicked', this._onViewSource.bind(this));
+ metaBox.add(viewsource);
+
+ if (extension.metadata.url) {
+ const webpage = new St.Button({
+ reactive: true,
+ track_hover: true,
+ style_class: 'shell-link',
+ label: _('Web Page'),
+ });
+ webpage._extension = extension;
+ webpage.connect('clicked', this._onWebPage.bind(this));
+ metaBox.add(webpage);
+ }
+
+ const viewerrors = new St.Button({
+ reactive: true,
+ track_hover: true,
+ style_class: 'shell-link',
+ label: _('Show Errors'),
+ });
+ viewerrors._extension = extension;
+ viewerrors._parentBox = box;
+ viewerrors._isShowing = false;
+ viewerrors.connect('clicked', this._onViewErrors.bind(this));
+ metaBox.add(viewerrors);
+
+ return box;
+ }
+});
+
+
+var ActorLink = GObject.registerClass({
+ Signals: {
+ 'inspect-actor': {},
+ },
+}, class ActorLink extends St.Button {
+ _init(actor) {
+ this._arrow = new St.Icon({
+ icon_name: 'pan-end-symbolic',
+ icon_size: 8,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ });
+
+ const label = new St.Label({
+ text: actor.toString(),
+ x_align: Clutter.ActorAlign.START,
+ });
+
+ const inspectButton = new St.Button({
+ icon_name: 'insert-object-symbolic',
+ reactive: true,
+ x_expand: true,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ inspectButton.connect('clicked', () => this.emit('inspect-actor'));
+
+ const box = new St.BoxLayout();
+ box.add_child(this._arrow);
+ box.add_child(label);
+ box.add_child(inspectButton);
+
+ super._init({
+ reactive: true,
+ track_hover: true,
+ toggle_mode: true,
+ style_class: 'actor-link',
+ child: box,
+ x_align: Clutter.ActorAlign.START,
+ });
+
+ this._actor = actor;
+ }
+
+ vfunc_clicked() {
+ this._arrow.ease({
+ rotation_angle_z: this.checked ? 90 : 0,
+ duration: 250,
+ });
+ }
+});
+
+var ActorTreeViewer = GObject.registerClass(
+class ActorTreeViewer extends St.BoxLayout {
+ _init(lookingGlass) {
+ super._init();
+
+ this._lookingGlass = lookingGlass;
+ this._actorData = new Map();
+ }
+
+ _showActorChildren(actor) {
+ const data = this._actorData.get(actor);
+ if (!data || data.visible)
+ return;
+
+ data.visible = true;
+ data.actorAddedId = actor.connect('actor-added', (container, child) => {
+ this._addActor(data.children, child);
+ });
+ data.actorRemovedId = actor.connect('actor-removed', (container, child) => {
+ this._removeActor(child);
+ });
+
+ for (let child of actor)
+ this._addActor(data.children, child);
+ }
+
+ _hideActorChildren(actor) {
+ const data = this._actorData.get(actor);
+ if (!data || !data.visible)
+ return;
+
+ for (let child of actor)
+ this._removeActor(child);
+
+ data.visible = false;
+ if (data.actorAddedId > 0) {
+ actor.disconnect(data.actorAddedId);
+ data.actorAddedId = 0;
+ }
+ if (data.actorRemovedId > 0) {
+ actor.disconnect(data.actorRemovedId);
+ data.actorRemovedId = 0;
+ }
+ data.children.remove_all_children();
+ }
+
+ _addActor(container, actor) {
+ if (this._actorData.has(actor))
+ return;
+
+ if (actor === this._lookingGlass)
+ return;
+
+ const button = new ActorLink(actor);
+ button.connect('notify::checked', () => {
+ this._lookingGlass.setBorderPaintTarget(actor);
+ if (button.checked)
+ this._showActorChildren(actor);
+ else
+ this._hideActorChildren(actor);
+ });
+ button.connect('inspect-actor', () => {
+ this._lookingGlass.inspectObject(actor, button);
+ });
+
+ const mainContainer = new St.BoxLayout({ vertical: true });
+ const childrenContainer = new St.BoxLayout({
+ vertical: true,
+ style: 'padding: 0 0 0 18px',
+ });
+
+ mainContainer.add_child(button);
+ mainContainer.add_child(childrenContainer);
+
+ this._actorData.set(actor, {
+ button,
+ container: mainContainer,
+ children: childrenContainer,
+ visible: false,
+ actorAddedId: 0,
+ actorRemovedId: 0,
+ actorDestroyedId: actor.connect('destroy', () => this._removeActor(actor)),
+ });
+
+ let belowChild = null;
+ const nextSibling = actor.get_next_sibling();
+ if (nextSibling && this._actorData.has(nextSibling))
+ belowChild = this._actorData.get(nextSibling).container;
+
+ container.insert_child_above(mainContainer, belowChild);
+ }
+
+ _removeActor(actor) {
+ const data = this._actorData.get(actor);
+ if (!data)
+ return;
+
+ for (let child of actor)
+ this._removeActor(child);
+
+ if (data.actorAddedId > 0) {
+ actor.disconnect(data.actorAddedId);
+ data.actorAddedId = 0;
+ }
+ if (data.actorRemovedId > 0) {
+ actor.disconnect(data.actorRemovedId);
+ data.actorRemovedId = 0;
+ }
+ if (data.actorDestroyedId > 0) {
+ actor.disconnect(data.actorDestroyedId);
+ data.actorDestroyedId = 0;
+ }
+ data.container.destroy();
+ this._actorData.delete(actor);
+ }
+
+ vfunc_map() {
+ super.vfunc_map();
+ this._addActor(this, global.stage);
+ }
+
+ vfunc_unmap() {
+ super.vfunc_unmap();
+ this._removeActor(global.stage);
+ }
+});
+
+var DebugFlag = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+}, class DebugFlag extends St.Button {
+ _init(label) {
+ const box = new St.BoxLayout();
+
+ const flagLabel = new St.Label({
+ text: label,
+ x_expand: true,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add_child(flagLabel);
+
+ this._flagSwitch = new PopupMenu.Switch(false);
+ this._stateHandler = this._flagSwitch.connect('notify::state', () => {
+ if (this._flagSwitch.state)
+ this._enable();
+ else
+ this._disable();
+ });
+
+ // Update state whenever the switch is mapped, because most debug flags
+ // don't have a way of notifying us of changes.
+ this._flagSwitch.connect('notify::mapped', () => {
+ if (!this._flagSwitch.is_mapped())
+ return;
+
+ const state = this._isEnabled();
+ if (state === this._flagSwitch.state)
+ return;
+
+ this._flagSwitch.block_signal_handler(this._stateHandler);
+ this._flagSwitch.state = state;
+ this._flagSwitch.unblock_signal_handler(this._stateHandler);
+ });
+
+ box.add_child(this._flagSwitch);
+
+ super._init({
+ style_class: 'lg-debug-flag-button',
+ can_focus: true,
+ toggleMode: true,
+ child: box,
+ label_actor: flagLabel,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this.connect('clicked', () => this._flagSwitch.toggle());
+ }
+
+ _isEnabled() {
+ throw new Error('Method not implemented');
+ }
+
+ _enable() {
+ throw new Error('Method not implemented');
+ }
+
+ _disable() {
+ throw new Error('Method not implemented');
+ }
+});
+
+
+var ClutterDebugFlag = GObject.registerClass(
+class ClutterDebugFlag extends DebugFlag {
+ _init(categoryName, flagName) {
+ super._init(flagName);
+
+ this._argPos = CLUTTER_DEBUG_FLAG_CATEGORIES.get(categoryName).argPos;
+ this._enumValue = Clutter[categoryName][flagName];
+ }
+
+ _isEnabled() {
+ const enabledFlags = Meta.get_clutter_debug_flags();
+ return !!(enabledFlags[this._argPos] & this._enumValue);
+ }
+
+ _getArgs() {
+ const args = [0, 0, 0];
+ args[this._argPos] = this._enumValue;
+ return args;
+ }
+
+ _enable() {
+ Meta.add_clutter_debug_flags(...this._getArgs());
+ }
+
+ _disable() {
+ Meta.remove_clutter_debug_flags(...this._getArgs());
+ }
+});
+
+var MutterPaintDebugFlag = GObject.registerClass(
+class MutterPaintDebugFlag extends DebugFlag {
+ _init(flagName) {
+ super._init(flagName);
+
+ this._enumValue = Meta.DebugPaintFlag[flagName];
+ }
+
+ _isEnabled() {
+ return !!(Meta.get_debug_paint_flags() & this._enumValue);
+ }
+
+ _enable() {
+ Meta.add_debug_paint_flag(this._enumValue);
+ }
+
+ _disable() {
+ Meta.remove_debug_paint_flag(this._enumValue);
+ }
+});
+
+var MutterTopicDebugFlag = GObject.registerClass(
+class MutterTopicDebugFlag extends DebugFlag {
+ _init(flagName) {
+ super._init(flagName);
+
+ this._enumValue = Meta.DebugTopic[flagName];
+ }
+
+ _isEnabled() {
+ return Meta.is_topic_enabled(this._enumValue);
+ }
+
+ _enable() {
+ Meta.add_verbose_topic(this._enumValue);
+ }
+
+ _disable() {
+ Meta.remove_verbose_topic(this._enumValue);
+ }
+});
+
+var UnsafeModeDebugFlag = GObject.registerClass(
+class UnsafeModeDebugFlag extends DebugFlag {
+ _init() {
+ super._init('unsafe-mode');
+ }
+
+ _isEnabled() {
+ return global.context.unsafe_mode;
+ }
+
+ _enable() {
+ global.context.unsafe_mode = true;
+ }
+
+ _disable() {
+ global.context.unsafe_mode = false;
+ }
+});
+
+var DebugFlags = GObject.registerClass(
+class DebugFlags extends St.BoxLayout {
+ _init() {
+ super._init({
+ name: 'lookingGlassDebugFlags',
+ vertical: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+
+ // Clutter debug flags
+ for (const [categoryName, props] of CLUTTER_DEBUG_FLAG_CATEGORIES.entries()) {
+ this._addHeader(`Clutter${categoryName}`);
+ for (const flagName of this._getFlagNames(Clutter[categoryName])) {
+ if (props.exclude.includes(flagName))
+ continue;
+ this.add_child(new ClutterDebugFlag(categoryName, flagName));
+ }
+ }
+
+ // Meta paint flags
+ this._addHeader('MetaDebugPaintFlag');
+ for (const flagName of this._getFlagNames(Meta.DebugPaintFlag))
+ this.add_child(new MutterPaintDebugFlag(flagName));
+
+ // Meta debug topics
+ this._addHeader('MetaDebugTopic');
+ for (const flagName of this._getFlagNames(Meta.DebugTopic))
+ this.add_child(new MutterTopicDebugFlag(flagName));
+
+ // MetaContext::unsafe-mode
+ this._addHeader('MetaContext');
+ this.add_child(new UnsafeModeDebugFlag());
+ }
+
+ _addHeader(title) {
+ const header = new St.Label({
+ text: title,
+ style_class: 'lg-debug-flags-header',
+ x_align: Clutter.ActorAlign.START,
+ });
+ this.add_child(header);
+ }
+
+ *_getFlagNames(enumObject) {
+ for (const flagName of Object.getOwnPropertyNames(enumObject)) {
+ if (typeof enumObject[flagName] !== 'number')
+ continue;
+
+ if (enumObject[flagName] <= 0)
+ continue;
+
+ yield flagName;
+ }
+ }
+});
+
+
+var LookingGlass = GObject.registerClass(
+class LookingGlass extends St.BoxLayout {
+ _init() {
+ super._init({
+ name: 'LookingGlassDialog',
+ style_class: 'lg-dialog',
+ vertical: true,
+ visible: false,
+ reactive: true,
+ });
+
+ this._borderPaintTarget = null;
+ this._redBorderEffect = new RedBorderEffect();
+
+ this._open = false;
+
+ this._it = null;
+ this._offset = 0;
+
+ // Sort of magic, but...eh.
+ this._maxItems = 150;
+
+ this._interfaceSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
+ this._interfaceSettings.connect('changed::monospace-font-name',
+ this._updateFont.bind(this));
+ this._updateFont();
+
+ // We want it to appear to slide out from underneath the panel
+ Main.uiGroup.add_actor(this);
+ Main.uiGroup.set_child_below_sibling(this,
+ Main.layoutManager.panelBox);
+ Main.layoutManager.panelBox.connect('notify::allocation',
+ this._queueResize.bind(this));
+ Main.layoutManager.keyboardBox.connect('notify::allocation',
+ this._queueResize.bind(this));
+
+ this._objInspector = new ObjInspector(this);
+ Main.uiGroup.add_actor(this._objInspector);
+ this._objInspector.hide();
+
+ let toolbar = new St.BoxLayout({ name: 'Toolbar' });
+ this.add_actor(toolbar);
+ const inspectButton = new St.Button({
+ style_class: 'lg-toolbar-button',
+ icon_name: 'find-location-symbolic',
+ });
+ toolbar.add_actor(inspectButton);
+ inspectButton.connect('clicked', () => {
+ let inspector = new Inspector(this);
+ inspector.connect('target', (i, target, stageX, stageY) => {
+ this._pushResult(`inspect(${Math.round(stageX)}, ${Math.round(stageY)})`, target);
+ });
+ inspector.connect('closed', () => {
+ this.show();
+ global.stage.set_key_focus(this._entry);
+ });
+ this.hide();
+ return Clutter.EVENT_STOP;
+ });
+
+ const gcButton = new St.Button({
+ style_class: 'lg-toolbar-button',
+ icon_name: 'user-trash-full-symbolic',
+ });
+ toolbar.add_actor(gcButton);
+ gcButton.connect('clicked', () => {
+ gcButton.child.icon_name = 'user-trash-symbolic';
+ System.gc();
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
+ gcButton.child.icon_name = 'user-trash-full-symbolic';
+ this._timeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(
+ this._timeoutId,
+ '[gnome-shell] gcButton.child.icon_name = \'user-trash-full-symbolic\''
+ );
+ return Clutter.EVENT_PROPAGATE;
+ });
+
+ let notebook = new Notebook();
+ this._notebook = notebook;
+ this.add_child(notebook);
+
+ let emptyBox = new St.Bin({ x_expand: true });
+ toolbar.add_child(emptyBox);
+ toolbar.add_actor(notebook.tabControls);
+
+ this._evalBox = new St.BoxLayout({ name: 'EvalBox', vertical: true });
+ notebook.appendPage('Evaluator', this._evalBox);
+
+ this._resultsArea = new St.BoxLayout({
+ name: 'ResultsArea',
+ vertical: true,
+ y_expand: true,
+ });
+ this._evalBox.add_child(this._resultsArea);
+
+ this._entryArea = new St.BoxLayout({
+ name: 'EntryArea',
+ y_align: Clutter.ActorAlign.END,
+ });
+ this._evalBox.add_actor(this._entryArea);
+
+ let label = new St.Label({ text: CHEVRON });
+ this._entryArea.add(label);
+
+ this._entry = new St.Entry({
+ can_focus: true,
+ x_expand: true,
+ });
+ ShellEntry.addContextMenu(this._entry);
+ this._entryArea.add_child(this._entry);
+
+ this._windowList = new WindowList(this);
+ notebook.appendPage('Windows', this._windowList);
+
+ this._extensions = new Extensions(this);
+ notebook.appendPage('Extensions', this._extensions);
+
+ this._actorTreeViewer = new ActorTreeViewer(this);
+ notebook.appendPage('Actors', this._actorTreeViewer);
+
+ this._debugFlags = new DebugFlags();
+ notebook.appendPage('Flags', this._debugFlags);
+
+ this._entry.clutter_text.connect('activate', (o, _e) => {
+ // Hide any completions we are currently showing
+ this._hideCompletions();
+
+ let text = o.get_text();
+ // Ensure we don't get newlines in the command; the history file is
+ // newline-separated.
+ text = text.replace('\n', ' ');
+ this._evaluate(text);
+ return true;
+ });
+
+ this._history = new History.HistoryManager({
+ gsettingsKey: HISTORY_KEY,
+ entry: this._entry.clutter_text,
+ });
+
+ this._autoComplete = new AutoComplete(this._entry);
+ this._autoComplete.connect('suggest', (a, e) => {
+ this._showCompletions(e.completions);
+ });
+ // If a completion is completed unambiguously, the currently-displayed completion
+ // suggestions become irrelevant.
+ this._autoComplete.connect('completion', (a, e) => {
+ if (e.type == 'whole-word')
+ this._hideCompletions();
+ });
+
+ this._resize();
+ }
+
+ vfunc_captured_event(event) {
+ if (Main.keyboard.maybeHandleEvent(event))
+ return Clutter.EVENT_STOP;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _updateFont() {
+ let fontName = this._interfaceSettings.get_string('monospace-font-name');
+ let fontDesc = Pango.FontDescription.from_string(fontName);
+ // We ignore everything but size and style; you'd be crazy to set your system-wide
+ // monospace font to be bold/oblique/etc. Could easily be added here.
+ let size = fontDesc.get_size() / 1024.;
+ let unit = fontDesc.get_size_is_absolute() ? 'px' : 'pt';
+ this.style = `
+ font-size: ${size}${unit};
+ font-family: "${fontDesc.get_family()}";`;
+ }
+
+ setBorderPaintTarget(obj) {
+ if (this._borderPaintTarget != null)
+ this._borderPaintTarget.remove_effect(this._redBorderEffect);
+ this._borderPaintTarget = obj;
+ if (this._borderPaintTarget != null)
+ this._borderPaintTarget.add_effect(this._redBorderEffect);
+ }
+
+ _pushResult(command, obj) {
+ let index = this._resultsArea.get_n_children() + this._offset;
+ let result = new Result(this, CHEVRON + command, obj, index);
+ this._resultsArea.add(result);
+ if (obj instanceof Clutter.Actor)
+ this.setBorderPaintTarget(obj);
+
+ if (this._resultsArea.get_n_children() > this._maxItems) {
+ this._resultsArea.get_first_child().destroy();
+ this._offset++;
+ }
+ this._it = obj;
+
+ // Scroll to bottom
+ this._notebook.scrollToBottom(0);
+ }
+
+ _showCompletions(completions) {
+ if (!this._completionActor) {
+ this._completionActor = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
+ this._completionActor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._completionActor.clutter_text.line_wrap = true;
+ this._evalBox.insert_child_below(this._completionActor, this._entryArea);
+ }
+
+ this._completionActor.set_text(completions.join(', '));
+
+ // Setting the height to -1 allows us to get its actual preferred height rather than
+ // whatever was last set when animating
+ this._completionActor.set_height(-1);
+ let [, naturalHeight] = this._completionActor.get_preferred_height(this._resultsArea.get_width());
+
+ // Don't reanimate if we are already visible
+ if (this._completionActor.visible) {
+ this._completionActor.height = naturalHeight;
+ } else {
+ let settings = St.Settings.get();
+ let duration = AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION / settings.slow_down_factor;
+ this._completionActor.show();
+ this._completionActor.remove_all_transitions();
+ this._completionActor.ease({
+ height: naturalHeight,
+ opacity: 255,
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+
+ _hideCompletions() {
+ if (this._completionActor) {
+ let settings = St.Settings.get();
+ let duration = AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION / settings.slow_down_factor;
+ this._completionActor.remove_all_transitions();
+ this._completionActor.ease({
+ height: 0,
+ opacity: 0,
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._completionActor.hide();
+ },
+ });
+ }
+ }
+
+ _evaluate(command) {
+ command = this._history.addItem(command); // trims command
+ if (!command)
+ return;
+
+ let lines = command.split(';');
+ lines.push(`return ${lines.pop()}`);
+
+ let fullCmd = commandHeader + lines.join(';');
+
+ let resultObj;
+ try {
+ resultObj = Function(fullCmd)();
+ } catch (e) {
+ resultObj = `<exception ${e}>`;
+ }
+
+ this._pushResult(command, resultObj);
+ this._entry.text = '';
+ }
+
+ inspect(x, y) {
+ return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
+ }
+
+ getIt() {
+ return this._it;
+ }
+
+ getResult(idx) {
+ try {
+ return this._resultsArea.get_child_at_index(idx - this._offset).o;
+ } catch (e) {
+ throw new Error(`Unknown result at index ${idx}`);
+ }
+ }
+
+ toggle() {
+ if (this._open)
+ this.close();
+ else
+ this.open();
+ }
+
+ _queueResize() {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._resize();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _resize() {
+ let primary = Main.layoutManager.primaryMonitor;
+ let myWidth = primary.width * 0.7;
+ let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
+ let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
+ this.x = primary.x + (primary.width - myWidth) / 2;
+ this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight;
+ this._targetY = this._hiddenY + myHeight;
+ this.y = this._hiddenY;
+ this.width = myWidth;
+ this.height = myHeight;
+ this._objInspector.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
+ this._objInspector.set_position(this.x + Math.floor(myWidth * 0.1),
+ this._targetY + Math.floor(myHeight * 0.1));
+ }
+
+ insertObject(obj) {
+ this._pushResult('<insert>', obj);
+ }
+
+ inspectObject(obj, sourceActor) {
+ this._objInspector.open(sourceActor);
+ this._objInspector.selectObject(obj);
+ }
+
+ // Handle key events which are relevant for all tabs of the LookingGlass
+ vfunc_key_press_event(keyPressEvent) {
+ let symbol = keyPressEvent.keyval;
+ if (symbol == Clutter.KEY_Escape) {
+ this.close();
+ return Clutter.EVENT_STOP;
+ }
+ // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
+ if (keyPressEvent.modifier_state & Clutter.ModifierType.CONTROL_MASK) {
+ if (symbol == Clutter.KEY_Page_Up)
+ this._notebook.prevTab();
+ else if (symbol == Clutter.KEY_Page_Down)
+ this._notebook.nextTab();
+ }
+ return super.vfunc_key_press_event(keyPressEvent);
+ }
+
+ open() {
+ if (this._open)
+ return;
+
+ let grab = Main.pushModal(this, { actionMode: Shell.ActionMode.LOOKING_GLASS });
+ if (grab.get_seat_state() !== Clutter.GrabState.ALL) {
+ Main.popModal(grab);
+ return;
+ }
+
+ this._grab = grab;
+ this._notebook.selectIndex(0);
+ this.show();
+ this._open = true;
+ this._history.lastItem();
+
+ this.remove_all_transitions();
+
+ // We inverse compensate for the slow-down so you can change the factor
+ // through LookingGlass without long waits.
+ let duration = LG_ANIMATION_TIME / St.Settings.get().slow_down_factor;
+ this.ease({
+ y: this._targetY,
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this._windowList.update();
+ this._entry.grab_key_focus();
+ }
+
+ close() {
+ if (!this._open)
+ return;
+
+ this._objInspector.hide();
+
+ this._open = false;
+ this.remove_all_transitions();
+
+ this.setBorderPaintTarget(null);
+
+ let settings = St.Settings.get();
+ let duration = Math.min(LG_ANIMATION_TIME / settings.slow_down_factor,
+ LG_ANIMATION_TIME);
+ this.ease({
+ y: this._hiddenY,
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ Main.popModal(this._grab);
+ this._grab = null;
+ this.hide();
+ },
+ });
+ }
+
+ get isOpen() {
+ return this._open;
+ }
+});
diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js
new file mode 100644
index 0000000..bd69047
--- /dev/null
+++ b/js/ui/magnifier.js
@@ -0,0 +1,2093 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Magnifier */
+
+const {
+ Atspi, Clutter, GDesktopEnums, Gio, GLib, GObject, Meta, Shell, St,
+} = imports.gi;
+const Signals = imports.misc.signals;
+
+const Background = imports.ui.background;
+const FocusCaretTracker = imports.ui.focusCaretTracker;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+const PointerWatcher = imports.ui.pointerWatcher;
+
+var CROSSHAIRS_CLIP_SIZE = [100, 100];
+var NO_CHANGE = 0.0;
+
+var POINTER_REST_TIME = 1000; // milliseconds
+
+// Settings
+const MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier';
+const SCREEN_POSITION_KEY = 'screen-position';
+const MAG_FACTOR_KEY = 'mag-factor';
+const INVERT_LIGHTNESS_KEY = 'invert-lightness';
+const COLOR_SATURATION_KEY = 'color-saturation';
+const BRIGHT_RED_KEY = 'brightness-red';
+const BRIGHT_GREEN_KEY = 'brightness-green';
+const BRIGHT_BLUE_KEY = 'brightness-blue';
+const CONTRAST_RED_KEY = 'contrast-red';
+const CONTRAST_GREEN_KEY = 'contrast-green';
+const CONTRAST_BLUE_KEY = 'contrast-blue';
+const LENS_MODE_KEY = 'lens-mode';
+const CLAMP_MODE_KEY = 'scroll-at-edges';
+const MOUSE_TRACKING_KEY = 'mouse-tracking';
+const FOCUS_TRACKING_KEY = 'focus-tracking';
+const CARET_TRACKING_KEY = 'caret-tracking';
+const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs';
+const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
+const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color';
+const CROSS_HAIRS_OPACITY_KEY = 'cross-hairs-opacity';
+const CROSS_HAIRS_LENGTH_KEY = 'cross-hairs-length';
+const CROSS_HAIRS_CLIP_KEY = 'cross-hairs-clip';
+
+var MouseSpriteContent = GObject.registerClass({
+ Implements: [Clutter.Content],
+}, class MouseSpriteContent extends GObject.Object {
+ _init() {
+ super._init();
+ this._texture = null;
+ }
+
+ vfunc_get_preferred_size() {
+ if (!this._texture)
+ return [false, 0, 0];
+
+ return [true, this._texture.get_width(), this._texture.get_height()];
+ }
+
+ vfunc_paint_content(actor, node, _paintContext) {
+ if (!this._texture)
+ return;
+
+ let color = Clutter.Color.get_static(Clutter.StaticColor.WHITE);
+ let [minFilter, magFilter] = actor.get_content_scaling_filters();
+ let textureNode = new Clutter.TextureNode(this._texture,
+ color, minFilter, magFilter);
+ textureNode.set_name('MouseSpriteContent');
+ node.add_child(textureNode);
+
+ textureNode.add_rectangle(actor.get_content_box());
+ }
+
+ get texture() {
+ return this._texture;
+ }
+
+ set texture(coglTexture) {
+ if (this._texture == coglTexture)
+ return;
+
+ let oldTexture = this._texture;
+ this._texture = coglTexture;
+ this.invalidate();
+
+ if (!oldTexture || !coglTexture ||
+ oldTexture.get_width() != coglTexture.get_width() ||
+ oldTexture.get_height() != coglTexture.get_height())
+ this.invalidate_size();
+ }
+});
+
+var Magnifier = class Magnifier extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ // Magnifier is a manager of ZoomRegions.
+ this._zoomRegions = [];
+
+ // Create small clutter tree for the magnified mouse.
+ let cursorTracker = Meta.CursorTracker.get_for_display(global.display);
+ this._cursorTracker = cursorTracker;
+
+ this._mouseSprite = new Clutter.Actor({ request_mode: Clutter.RequestMode.CONTENT_SIZE });
+ this._mouseSprite.content = new MouseSpriteContent();
+
+ this._cursorRoot = new Clutter.Actor();
+ this._cursorRoot.add_actor(this._mouseSprite);
+
+ // Create the first ZoomRegion and initialize it according to the
+ // magnification settings.
+
+ [this.xMouse, this.yMouse] = global.get_pointer();
+
+ let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
+ this._zoomRegions.push(aZoomRegion);
+ this._settingsInit(aZoomRegion);
+ aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
+
+ St.Settings.get().connect('notify::magnifier-active', () => {
+ this.setActive(St.Settings.get().magnifier_active);
+ });
+
+ this.setActive(St.Settings.get().magnifier_active);
+ }
+
+ /**
+ * showSystemCursor:
+ * Show the system mouse pointer.
+ */
+ showSystemCursor() {
+ const seat = Clutter.get_default_backend().get_default_seat();
+
+ if (seat.is_unfocus_inhibited())
+ seat.uninhibit_unfocus();
+
+ if (this._cursorVisibilityChangedId) {
+ this._cursorTracker.disconnect(this._cursorVisibilityChangedId);
+ delete this._cursorVisibilityChangedId;
+
+ this._cursorTracker.set_pointer_visible(true);
+ }
+ }
+
+ /**
+ * hideSystemCursor:
+ * Hide the system mouse pointer.
+ */
+ hideSystemCursor() {
+ const seat = Clutter.get_default_backend().get_default_seat();
+
+ if (!seat.is_unfocus_inhibited())
+ seat.inhibit_unfocus();
+
+ if (!this._cursorVisibilityChangedId) {
+ this._cursorTracker.set_pointer_visible(false);
+ this._cursorVisibilityChangedId = this._cursorTracker.connect('visibility-changed', () => {
+ if (this._cursorTracker.get_pointer_visible())
+ this._cursorTracker.set_pointer_visible(false);
+ });
+ }
+ }
+
+ /**
+ * setActive:
+ * Show/hide all the zoom regions.
+ * @param {bool} activate: Boolean to activate or de-activate the magnifier.
+ */
+ setActive(activate) {
+ let isActive = this.isActive();
+
+ this._zoomRegions.forEach(zoomRegion => {
+ zoomRegion.setActive(activate);
+ });
+
+ if (isActive === activate)
+ return;
+
+ if (activate) {
+ this._updateMouseSprite();
+ this._cursorTracker.connectObject(
+ 'cursor-changed', this._updateMouseSprite.bind(this), this);
+ Meta.disable_unredirect_for_display(global.display);
+ this.startTrackingMouse();
+ } else {
+ this._cursorTracker.disconnectObject(this);
+ this._mouseSprite.content.texture = null;
+ Meta.enable_unredirect_for_display(global.display);
+ this.stopTrackingMouse();
+ }
+
+ if (this._crossHairs)
+ this._crossHairs.setEnabled(activate);
+
+ // Make sure system mouse pointer is shown when all zoom regions are
+ // invisible.
+ if (!activate)
+ this.showSystemCursor();
+
+ // Notify interested parties of this change
+ this.emit('active-changed', activate);
+ }
+
+ /**
+ * isActive:
+ * @returns {bool} Whether the magnifier is active.
+ */
+ isActive() {
+ // Sufficient to check one ZoomRegion since Magnifier's active
+ // state applies to all of them.
+ if (this._zoomRegions.length == 0)
+ return false;
+ else
+ return this._zoomRegions[0].isActive();
+ }
+
+ /**
+ * startTrackingMouse:
+ * Turn on mouse tracking, if not already doing so.
+ */
+ startTrackingMouse() {
+ if (!this._pointerWatch) {
+ let interval = 1000 / 60;
+ this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch(interval, this.scrollToMousePos.bind(this));
+
+ this.scrollToMousePos();
+ }
+ }
+
+ /**
+ * stopTrackingMouse:
+ * Turn off mouse tracking, if not already doing so.
+ */
+ stopTrackingMouse() {
+ if (this._pointerWatch)
+ this._pointerWatch.remove();
+
+ this._pointerWatch = null;
+ }
+
+ /**
+ * isTrackingMouse:
+ * @returns {bool} whether the magnifier is currently tracking the mouse
+ */
+ isTrackingMouse() {
+ return !!this._mouseTrackingId;
+ }
+
+ /**
+ * scrollToMousePos:
+ * Position all zoom regions' ROI relative to the current location of the
+ * system pointer.
+ */
+ scrollToMousePos(...args) {
+ const [xMouse, yMouse] = args.length ? args : global.get_pointer();
+
+ if (xMouse === this.xMouse && yMouse === this.yMouse)
+ return;
+
+ this.xMouse = xMouse;
+ this.yMouse = yMouse;
+
+ let sysMouseOverAny = false;
+ this._zoomRegions.forEach(zoomRegion => {
+ if (zoomRegion.scrollToMousePos())
+ sysMouseOverAny = true;
+ });
+ if (sysMouseOverAny)
+ this.hideSystemCursor();
+ else
+ this.showSystemCursor();
+ }
+
+ /**
+ * createZoomRegion:
+ * Create a ZoomRegion instance with the given properties.
+ * @param {number} xMagFactor:
+ * The power to set horizontal magnification of the ZoomRegion. A value
+ * of 1.0 means no magnification, a value of 2.0 doubles the size.
+ * @param {number} yMagFactor:
+ * The power to set the vertical magnification of the ZoomRegion.
+ * @param {{x: number, y: number, width: number, height: number}} roi:
+ * The reg Object that defines the region to magnify, given in
+ * unmagnified coordinates.
+ * @param {{x: number, y: number, width: number, height: number}} viewPort:
+ * Object that defines the position of the ZoomRegion on screen.
+ * @returns {ZoomRegion} the newly created ZoomRegion.
+ */
+ createZoomRegion(xMagFactor, yMagFactor, roi, viewPort) {
+ let zoomRegion = new ZoomRegion(this, this._cursorRoot);
+ zoomRegion.setViewPort(viewPort);
+
+ // We ignore the redundant width/height on the ROI
+ let fixedROI = Object.create(roi);
+ fixedROI.width = viewPort.width / xMagFactor;
+ fixedROI.height = viewPort.height / yMagFactor;
+ zoomRegion.setROI(fixedROI);
+
+ zoomRegion.addCrosshairs(this._crossHairs);
+ return zoomRegion;
+ }
+
+ /**
+ * addZoomRegion:
+ * Append the given ZoomRegion to the list of currently defined ZoomRegions
+ * for this Magnifier instance.
+ * @param {ZoomRegion} zoomRegion: The zoomRegion to add.
+ */
+ addZoomRegion(zoomRegion) {
+ if (zoomRegion) {
+ this._zoomRegions.push(zoomRegion);
+ if (!this.isTrackingMouse())
+ this.startTrackingMouse();
+ }
+ }
+
+ /**
+ * getZoomRegions:
+ * Return a list of ZoomRegion's for this Magnifier.
+ * @returns {number[]} The Magnifier's zoom region list.
+ */
+ getZoomRegions() {
+ return this._zoomRegions;
+ }
+
+ /**
+ * clearAllZoomRegions:
+ * Remove all the zoom regions from this Magnfier's ZoomRegion list.
+ */
+ clearAllZoomRegions() {
+ for (let i = 0; i < this._zoomRegions.length; i++)
+ this._zoomRegions[i].setActive(false);
+
+ this._zoomRegions.length = 0;
+ this.stopTrackingMouse();
+ this.showSystemCursor();
+ }
+
+ /**
+ * addCrosshairs:
+ * Add and show a cross hair centered on the magnified mouse.
+ */
+ addCrosshairs() {
+ if (!this._crossHairs)
+ this._crossHairs = new Crosshairs();
+
+ let thickness = this._settings.get_int(CROSS_HAIRS_THICKNESS_KEY);
+ let color = this._settings.get_string(CROSS_HAIRS_COLOR_KEY);
+ let opacity = this._settings.get_double(CROSS_HAIRS_OPACITY_KEY);
+ let length = this._settings.get_int(CROSS_HAIRS_LENGTH_KEY);
+ let clip = this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY);
+
+ this.setCrosshairsThickness(thickness);
+ this.setCrosshairsColor(color);
+ this.setCrosshairsOpacity(opacity);
+ this.setCrosshairsLength(length);
+ this.setCrosshairsClip(clip);
+
+ let theCrossHairs = this._crossHairs;
+ this._zoomRegions.forEach(zoomRegion => {
+ zoomRegion.addCrosshairs(theCrossHairs);
+ });
+ }
+
+ /**
+ * setCrosshairsVisible:
+ * Show or hide the cross hair.
+ * @param {bool} visible: Flag that indicates show (true) or hide (false).
+ */
+ setCrosshairsVisible(visible) {
+ if (visible) {
+ if (!this._crossHairs)
+ this.addCrosshairs();
+ this._crossHairs.show();
+ } else {
+ // eslint-disable-next-line no-lonely-if
+ if (this._crossHairs)
+ this._crossHairs.hide();
+ }
+ }
+
+ /**
+ * setCrosshairsColor:
+ * Set the color of the crosshairs for all ZoomRegions.
+ * @param {string} color: The color as a string, e.g. '#ff0000ff' or 'red'.
+ */
+ setCrosshairsColor(color) {
+ if (this._crossHairs) {
+ let [res_, clutterColor] = Clutter.Color.from_string(color);
+ this._crossHairs.setColor(clutterColor);
+ }
+ }
+
+ /**
+ * getCrosshairsColor:
+ * Get the color of the crosshairs.
+ * @returns {string} The color as a string, e.g. '#0000ffff' or 'blue'.
+ */
+ getCrosshairsColor() {
+ if (this._crossHairs) {
+ let clutterColor = this._crossHairs.getColor();
+ return clutterColor.to_string();
+ } else {
+ return '#00000000';
+ }
+ }
+
+ /**
+ * setCrosshairsThickness:
+ * Set the crosshairs thickness for all ZoomRegions.
+ * @param {number} thickness: The width of the vertical and
+ * horizontal lines of the crosshairs.
+ */
+ setCrosshairsThickness(thickness) {
+ if (this._crossHairs)
+ this._crossHairs.setThickness(thickness);
+ }
+
+ /**
+ * getCrosshairsThickness:
+ * Get the crosshairs thickness.
+ * @returns {number} The width of the vertical and horizontal
+ * lines of the crosshairs.
+ */
+ getCrosshairsThickness() {
+ if (this._crossHairs)
+ return this._crossHairs.getThickness();
+ else
+ return 0;
+ }
+
+ /**
+ * setCrosshairsOpacity:
+ * @param {number} opacity: Value between 0.0 (transparent)
+ * and 1.0 (fully opaque).
+ */
+ setCrosshairsOpacity(opacity) {
+ if (this._crossHairs)
+ this._crossHairs.setOpacity(opacity * 255);
+ }
+
+ /**
+ * getCrosshairsOpacity:
+ * @returns {number} Value between 0.0 (transparent) and 1.0 (fully opaque).
+ */
+ getCrosshairsOpacity() {
+ if (this._crossHairs)
+ return this._crossHairs.getOpacity() / 255.0;
+ else
+ return 0.0;
+ }
+
+ /**
+ * setCrosshairsLength:
+ * Set the crosshairs length for all ZoomRegions.
+ * @param {number} length: The length of the vertical and horizontal
+ * lines making up the crosshairs.
+ */
+ setCrosshairsLength(length) {
+ if (this._crossHairs) {
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ this._crossHairs.setLength(length / scaleFactor);
+ }
+ }
+
+ /**
+ * getCrosshairsLength:
+ * Get the crosshairs length.
+ * @returns {number} The length of the vertical and horizontal
+ * lines making up the crosshairs.
+ */
+ getCrosshairsLength() {
+ if (this._crossHairs)
+ return this._crossHairs.getLength();
+ else
+ return 0;
+ }
+
+ /**
+ * setCrosshairsClip:
+ * Set whether the crosshairs are clipped at their intersection.
+ * @param {bool} clip: Flag to indicate whether to clip the crosshairs.
+ */
+ setCrosshairsClip(clip) {
+ if (!this._crossHairs)
+ return;
+
+ // Setting no clipping on crosshairs means a zero sized clip rectangle.
+ this._crossHairs.setClip(clip ? CROSSHAIRS_CLIP_SIZE : [0, 0]);
+ }
+
+ /**
+ * getCrosshairsClip:
+ * Get whether the crosshairs are clipped by the mouse image.
+ * @returns {bool} Whether the crosshairs are clipped.
+ */
+ getCrosshairsClip() {
+ if (this._crossHairs) {
+ let [clipWidth, clipHeight] = this._crossHairs.getClip();
+ return clipWidth > 0 && clipHeight > 0;
+ } else {
+ return false;
+ }
+ }
+
+ // Private methods //
+
+ _updateMouseSprite() {
+ this._updateSpriteTexture();
+ let [xHot, yHot] = this._cursorTracker.get_hot();
+ this._mouseSprite.set({
+ translation_x: -xHot,
+ translation_y: -yHot,
+ });
+ }
+
+ _updateSpriteTexture() {
+ let sprite = this._cursorTracker.get_sprite();
+
+ if (sprite) {
+ this._mouseSprite.content.texture = sprite;
+ this._mouseSprite.show();
+ } else {
+ this._mouseSprite.hide();
+ }
+ }
+
+ _settingsInit(zoomRegion) {
+ this._settings = new Gio.Settings({ schema_id: MAGNIFIER_SCHEMA });
+
+ this._settings.connect(`changed::${SCREEN_POSITION_KEY}`,
+ this._updateScreenPosition.bind(this));
+ this._settings.connect(`changed::${MAG_FACTOR_KEY}`,
+ this._updateMagFactor.bind(this));
+ this._settings.connect(`changed::${LENS_MODE_KEY}`,
+ this._updateLensMode.bind(this));
+ this._settings.connect(`changed::${CLAMP_MODE_KEY}`,
+ this._updateClampMode.bind(this));
+ this._settings.connect(`changed::${MOUSE_TRACKING_KEY}`,
+ this._updateMouseTrackingMode.bind(this));
+ this._settings.connect(`changed::${FOCUS_TRACKING_KEY}`,
+ this._updateFocusTrackingMode.bind(this));
+ this._settings.connect(`changed::${CARET_TRACKING_KEY}`,
+ this._updateCaretTrackingMode.bind(this));
+
+ this._settings.connect(`changed::${INVERT_LIGHTNESS_KEY}`,
+ this._updateInvertLightness.bind(this));
+ this._settings.connect(`changed::${COLOR_SATURATION_KEY}`,
+ this._updateColorSaturation.bind(this));
+
+ this._settings.connect(`changed::${BRIGHT_RED_KEY}`,
+ this._updateBrightness.bind(this));
+ this._settings.connect(`changed::${BRIGHT_GREEN_KEY}`,
+ this._updateBrightness.bind(this));
+ this._settings.connect(`changed::${BRIGHT_BLUE_KEY}`,
+ this._updateBrightness.bind(this));
+
+ this._settings.connect(`changed::${CONTRAST_RED_KEY}`,
+ this._updateContrast.bind(this));
+ this._settings.connect(`changed::${CONTRAST_GREEN_KEY}`,
+ this._updateContrast.bind(this));
+ this._settings.connect(`changed::${CONTRAST_BLUE_KEY}`,
+ this._updateContrast.bind(this));
+
+ this._settings.connect(`changed::${SHOW_CROSS_HAIRS_KEY}`, () => {
+ this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
+ });
+
+ this._settings.connect(`changed::${CROSS_HAIRS_THICKNESS_KEY}`, () => {
+ this.setCrosshairsThickness(this._settings.get_int(CROSS_HAIRS_THICKNESS_KEY));
+ });
+
+ this._settings.connect(`changed::${CROSS_HAIRS_COLOR_KEY}`, () => {
+ this.setCrosshairsColor(this._settings.get_string(CROSS_HAIRS_COLOR_KEY));
+ });
+
+ this._settings.connect(`changed::${CROSS_HAIRS_OPACITY_KEY}`, () => {
+ this.setCrosshairsOpacity(this._settings.get_double(CROSS_HAIRS_OPACITY_KEY));
+ });
+
+ this._settings.connect(`changed::${CROSS_HAIRS_LENGTH_KEY}`, () => {
+ this.setCrosshairsLength(this._settings.get_int(CROSS_HAIRS_LENGTH_KEY));
+ });
+
+ this._settings.connect(`changed::${CROSS_HAIRS_CLIP_KEY}`, () => {
+ this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
+ });
+
+ if (zoomRegion) {
+ // Mag factor is accurate to two decimal places.
+ let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
+ if (aPref != 0.0)
+ zoomRegion.setMagFactor(aPref, aPref);
+
+ aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
+ if (aPref)
+ zoomRegion.setScreenPosition(aPref);
+
+ zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
+ zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
+
+ aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
+ if (aPref)
+ zoomRegion.setMouseTrackingMode(aPref);
+
+ aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
+ if (aPref)
+ zoomRegion.setFocusTrackingMode(aPref);
+
+ aPref = this._settings.get_enum(CARET_TRACKING_KEY);
+ if (aPref)
+ zoomRegion.setCaretTrackingMode(aPref);
+
+ aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
+ if (aPref)
+ zoomRegion.setInvertLightness(aPref);
+
+ aPref = this._settings.get_double(COLOR_SATURATION_KEY);
+ if (aPref)
+ zoomRegion.setColorSaturation(aPref);
+
+ let bc = {};
+ bc.r = this._settings.get_double(BRIGHT_RED_KEY);
+ bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
+ bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
+ zoomRegion.setBrightness(bc);
+
+ bc.r = this._settings.get_double(CONTRAST_RED_KEY);
+ bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
+ bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
+ zoomRegion.setContrast(bc);
+ }
+
+ let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
+ this.addCrosshairs();
+ this.setCrosshairsVisible(showCrosshairs);
+ }
+
+ _updateScreenPosition() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ let position = this._settings.get_enum(SCREEN_POSITION_KEY);
+ this._zoomRegions[0].setScreenPosition(position);
+ if (position != GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN)
+ this._updateLensMode();
+ }
+ }
+
+ _updateMagFactor() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ // Mag factor is accurate to two decimal places.
+ let magFactor = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
+ this._zoomRegions[0].setMagFactor(magFactor, magFactor);
+ }
+ }
+
+ _updateLensMode() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length)
+ this._zoomRegions[0].setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
+ }
+
+ _updateClampMode() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setClampScrollingAtEdges(
+ !this._settings.get_boolean(CLAMP_MODE_KEY));
+ }
+ }
+
+ _updateMouseTrackingMode() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setMouseTrackingMode(
+ this._settings.get_enum(MOUSE_TRACKING_KEY));
+ }
+ }
+
+ _updateFocusTrackingMode() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setFocusTrackingMode(
+ this._settings.get_enum(FOCUS_TRACKING_KEY));
+ }
+ }
+
+ _updateCaretTrackingMode() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setCaretTrackingMode(
+ this._settings.get_enum(CARET_TRACKING_KEY));
+ }
+ }
+
+ _updateInvertLightness() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setInvertLightness(
+ this._settings.get_boolean(INVERT_LIGHTNESS_KEY));
+ }
+ }
+
+ _updateColorSaturation() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setColorSaturation(
+ this._settings.get_double(COLOR_SATURATION_KEY));
+ }
+ }
+
+ _updateBrightness() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ let brightness = {};
+ brightness.r = this._settings.get_double(BRIGHT_RED_KEY);
+ brightness.g = this._settings.get_double(BRIGHT_GREEN_KEY);
+ brightness.b = this._settings.get_double(BRIGHT_BLUE_KEY);
+ this._zoomRegions[0].setBrightness(brightness);
+ }
+ }
+
+ _updateContrast() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ let contrast = {};
+ contrast.r = this._settings.get_double(CONTRAST_RED_KEY);
+ contrast.g = this._settings.get_double(CONTRAST_GREEN_KEY);
+ contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
+ this._zoomRegions[0].setContrast(contrast);
+ }
+ }
+};
+
+var ZoomRegion = class ZoomRegion {
+ constructor(magnifier, mouseSourceActor) {
+ this._magnifier = magnifier;
+ this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
+
+ this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
+ this._focusTrackingMode = GDesktopEnums.MagnifierFocusTrackingMode.NONE;
+ this._caretTrackingMode = GDesktopEnums.MagnifierCaretTrackingMode.NONE;
+ this._clampScrollingAtEdges = false;
+ this._lensMode = false;
+ this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
+ this._invertLightness = false;
+ this._colorSaturation = 1.0;
+ this._brightness = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
+ this._contrast = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
+
+ this._magView = null;
+ this._background = null;
+ this._uiGroupClone = null;
+ this._mouseSourceActor = mouseSourceActor;
+ this._mouseActor = null;
+ this._crossHairs = null;
+ this._crossHairsActor = null;
+
+ this._viewPortX = 0;
+ this._viewPortY = 0;
+ this._viewPortWidth = global.screen_width;
+ this._viewPortHeight = global.screen_height;
+ this._xCenter = this._viewPortWidth / 2;
+ this._yCenter = this._viewPortHeight / 2;
+ this._xMagFactor = 1;
+ this._yMagFactor = 1;
+ this._followingCursor = false;
+ this._xFocus = 0;
+ this._yFocus = 0;
+ this._xCaret = 0;
+ this._yCaret = 0;
+
+ this._pointerIdleMonitor = global.backend.get_core_idle_monitor();
+ this._scrollContentsTimerId = 0;
+ }
+
+ _connectSignals() {
+ if (this._signalConnections)
+ return;
+
+ this._signalConnections = [];
+ let id = Main.layoutManager.connect('monitors-changed',
+ this._monitorsChanged.bind(this));
+ this._signalConnections.push([Main.layoutManager, id]);
+
+ id = this._focusCaretTracker.connect('caret-moved', this._updateCaret.bind(this));
+ this._signalConnections.push([this._focusCaretTracker, id]);
+
+ id = this._focusCaretTracker.connect('focus-changed', this._updateFocus.bind(this));
+ this._signalConnections.push([this._focusCaretTracker, id]);
+ }
+
+ _disconnectSignals() {
+ for (let [obj, id] of this._signalConnections)
+ obj.disconnect(id);
+
+ delete this._signalConnections;
+ }
+
+ _updateScreenPosition() {
+ if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE) {
+ this._setViewPort({
+ x: this._viewPortX,
+ y: this._viewPortY,
+ width: this._viewPortWidth,
+ height: this._viewPortHeight,
+ });
+ } else {
+ this.setScreenPosition(this._screenPosition);
+ }
+ }
+
+ _convertExtentsToScreenSpace(accessible, extents) {
+ const toplevelWindowTypes = new Set([
+ Atspi.Role.FRAME,
+ Atspi.Role.DIALOG,
+ Atspi.Role.WINDOW,
+ ]);
+
+ try {
+ let app = null;
+ let parentWindow = null;
+ let iter = accessible;
+ while (iter) {
+ if (iter.get_role() === Atspi.Role.APPLICATION) {
+ app = iter;
+ /* This is the last Accessible we are interested in */
+ break;
+ } else if (toplevelWindowTypes.has(iter.get_role())) {
+ parentWindow = iter;
+ }
+ iter = iter.get_parent();
+ }
+
+ /* We don't want to translate our own events to the focus window.
+ * They are also already scaled by clutter before being sent, so
+ * we don't need to do that here either. */
+ if (app && app.get_name() === 'gnome-shell')
+ return extents;
+
+ /* Only events from the focused widget of the focused window. Some
+ * widgets seem to claim to have focus when the window does not so
+ * check both. */
+ const windowActive = parentWindow &&
+ parentWindow.get_state_set().contains(Atspi.StateType.ACTIVE);
+ const accessibleFocused =
+ accessible.get_state_set().contains(Atspi.StateType.FOCUSED);
+ if (!windowActive || !accessibleFocused)
+ return null;
+ } catch (e) {
+ throw new Error(`Failed to validate parent window: ${e}`);
+ }
+
+ const { focusWindow } = global.display;
+ if (!focusWindow)
+ return null;
+
+ let windowRect = focusWindow.get_frame_rect();
+ if (!focusWindow.is_client_decorated())
+ windowRect = focusWindow.frame_rect_to_client_rect(windowRect);
+
+ const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ const screenSpaceExtents = new Atspi.Rect({
+ x: windowRect.x + (scaleFactor * extents.x),
+ y: windowRect.y + (scaleFactor * extents.y),
+ width: scaleFactor * extents.width,
+ height: scaleFactor * extents.height,
+ });
+
+ return screenSpaceExtents;
+ }
+
+ _updateFocus(caller, event) {
+ let component = event.source.get_component_iface();
+ if (!component || event.detail1 != 1)
+ return;
+ let extents;
+ try {
+ extents = component.get_extents(Atspi.CoordType.WINDOW);
+ extents = this._convertExtentsToScreenSpace(event.source, extents);
+ if (!extents)
+ return;
+ } catch (e) {
+ log(`Failed to read extents of focused component: ${e.message}`);
+ return;
+ }
+
+ const [xFocus, yFocus] = [
+ extents.x + (extents.width / 2),
+ extents.y + (extents.height / 2),
+ ];
+
+ if (this._xFocus !== xFocus || this._yFocus !== yFocus) {
+ [this._xFocus, this._yFocus] = [xFocus, yFocus];
+ this._centerFromFocusPosition();
+ }
+ }
+
+ _updateCaret(caller, event) {
+ let text = event.source.get_text_iface();
+ if (!text)
+ return;
+ let extents;
+ try {
+ extents = text.get_character_extents(text.get_caret_offset(),
+ Atspi.CoordType.WINDOW);
+ extents = this._convertExtentsToScreenSpace(text, extents);
+ if (!extents)
+ return;
+ } catch (e) {
+ log(`Failed to read extents of text caret: ${e.message}`);
+ return;
+ }
+
+ const [xCaret, yCaret] = [extents.x, extents.y];
+
+ // Ignore event(s) if the caret size is none (0x0). This happens a lot if
+ // the cursor offset can't be translated into a location. This is a work
+ // around.
+ if (extents.width === 0 && extents.height === 0)
+ return;
+
+ if (this._xCaret !== xCaret || this._yCaret !== yCaret) {
+ [this._xCaret, this._yCaret] = [xCaret, yCaret];
+ this._centerFromCaretPosition();
+ }
+ }
+
+ /**
+ * setActive:
+ * @param {bool} activate: Boolean to show/hide the ZoomRegion.
+ */
+ setActive(activate) {
+ if (activate == this.isActive())
+ return;
+
+ if (activate) {
+ this._createActors();
+ if (this._isMouseOverRegion())
+ this._magnifier.hideSystemCursor();
+ this._updateScreenPosition();
+ this._updateMagViewGeometry();
+ this._updateCloneGeometry();
+ this._updateMousePosition();
+ this._connectSignals();
+ } else {
+ Main.uiGroup.set_opacity(255);
+ this._disconnectSignals();
+ this._destroyActors();
+ }
+
+ this._syncCaretTracking();
+ this._syncFocusTracking();
+ }
+
+ /**
+ * isActive:
+ * @returns {bool} Whether this ZoomRegion is active
+ */
+ isActive() {
+ return this._magView != null;
+ }
+
+ /**
+ * setMagFactor:
+ * @param {number} xMagFactor: The power to set the horizontal
+ * magnification factor to of the magnified view. A value of 1.0
+ * means no magnification. A value of 2.0 doubles the size.
+ * @param {number} yMagFactor: The power to set the vertical
+ * magnification factor to of the magnified view.
+ */
+ setMagFactor(xMagFactor, yMagFactor) {
+ this._changeROI({
+ xMagFactor,
+ yMagFactor,
+ redoCursorTracking: this._followingCursor,
+ animate: true,
+ });
+ }
+
+ /**
+ * getMagFactor:
+ * @returns {number[]} an array, [xMagFactor, yMagFactor], containing
+ * the horizontal and vertical magnification powers. A value of
+ * 1.0 means no magnification. A value of 2.0 means the contents
+ * are doubled in size, and so on.
+ */
+ getMagFactor() {
+ return [this._xMagFactor, this._yMagFactor];
+ }
+
+ /**
+ * setMouseTrackingMode
+ * @param {GDesktopEnums.MagnifierMouseTrackingMode} mode: the new mode
+ */
+ setMouseTrackingMode(mode) {
+ if (mode >= GDesktopEnums.MagnifierMouseTrackingMode.NONE &&
+ mode <= GDesktopEnums.MagnifierMouseTrackingMode.PUSH)
+ this._mouseTrackingMode = mode;
+ }
+
+ /**
+ * getMouseTrackingMode
+ * @returns {GDesktopEnums.MagnifierMouseTrackingMode} the current mode
+ */
+ getMouseTrackingMode() {
+ return this._mouseTrackingMode;
+ }
+
+ /**
+ * setFocusTrackingMode
+ * @param {GDesktopEnums.MagnifierFocusTrackingMode} mode: the new mode
+ */
+ setFocusTrackingMode(mode) {
+ this._focusTrackingMode = mode;
+ this._syncFocusTracking();
+ }
+
+ /**
+ * setCaretTrackingMode
+ * @param {GDesktopEnums.MagnifierCaretTrackingMode} mode: the new mode
+ */
+ setCaretTrackingMode(mode) {
+ this._caretTrackingMode = mode;
+ this._syncCaretTracking();
+ }
+
+ _syncFocusTracking() {
+ let enabled = this._focusTrackingMode != GDesktopEnums.MagnifierFocusTrackingMode.NONE &&
+ this.isActive();
+
+ if (enabled)
+ this._focusCaretTracker.registerFocusListener();
+ else
+ this._focusCaretTracker.deregisterFocusListener();
+ }
+
+ _syncCaretTracking() {
+ let enabled = this._caretTrackingMode != GDesktopEnums.MagnifierCaretTrackingMode.NONE &&
+ this.isActive();
+
+ if (enabled)
+ this._focusCaretTracker.registerCaretListener();
+ else
+ this._focusCaretTracker.deregisterCaretListener();
+ }
+
+ /**
+ * setViewPort
+ * Sets the position and size of the ZoomRegion on screen.
+ * @param {{x: number, y: number, width: number, height: number}} viewPort:
+ * Object defining the position and size of the view port.
+ * The values are in stage coordinate space.
+ */
+ setViewPort(viewPort) {
+ this._setViewPort(viewPort);
+ this._screenPosition = GDesktopEnums.MagnifierScreenPosition.NONE;
+ }
+
+ /**
+ * setROI
+ * Sets the "region of interest" that the ZoomRegion is magnifying.
+ * @param {{x: number, y: number, width: number, height: number}} roi:
+ * Object that defines the region of the screen to magnify.
+ * The values are in screen (unmagnified) coordinate space.
+ */
+ setROI(roi) {
+ if (roi.width <= 0 || roi.height <= 0)
+ return;
+
+ this._followingCursor = false;
+ this._changeROI({
+ xMagFactor: this._viewPortWidth / roi.width,
+ yMagFactor: this._viewPortHeight / roi.height,
+ xCenter: roi.x + roi.width / 2,
+ yCenter: roi.y + roi.height / 2,
+ });
+ }
+
+ /**
+ * getROI:
+ * Retrieves the "region of interest" -- the rectangular bounds of that part
+ * of the desktop that the magnified view is showing (x, y, width, height).
+ * The bounds are given in non-magnified coordinates.
+ * @returns {number[]} an array, [x, y, width, height], representing
+ * the bounding rectangle of what is shown in the magnified view.
+ */
+ getROI() {
+ let roiWidth = this._viewPortWidth / this._xMagFactor;
+ let roiHeight = this._viewPortHeight / this._yMagFactor;
+
+ return [
+ this._xCenter - roiWidth / 2,
+ this._yCenter - roiHeight / 2,
+ roiWidth, roiHeight,
+ ];
+ }
+
+ /**
+ * setLensMode:
+ * Turn lens mode on/off. In full screen mode, lens mode does nothing since
+ * a lens the size of the screen is pointless.
+ * @param {bool} lensMode: Whether lensMode should be active
+ */
+ setLensMode(lensMode) {
+ this._lensMode = lensMode;
+ if (!this._lensMode)
+ this.setScreenPosition(this._screenPosition);
+ }
+
+ /**
+ * isLensMode:
+ * Is lens mode on or off?
+ * @returns {bool} The lens mode state.
+ */
+ isLensMode() {
+ return this._lensMode;
+ }
+
+ /**
+ * setClampScrollingAtEdges:
+ * Stop vs. allow scrolling of the magnified contents when it scroll beyond
+ * the edges of the screen.
+ * @param {bool} clamp: Boolean to turn on/off clamping.
+ */
+ setClampScrollingAtEdges(clamp) {
+ this._clampScrollingAtEdges = clamp;
+ if (clamp)
+ this._changeROI();
+ }
+
+ /**
+ * setTopHalf:
+ * Magnifier view occupies the top half of the screen.
+ */
+ setTopHalf() {
+ let viewPort = {};
+ viewPort.x = 0;
+ viewPort.y = 0;
+ viewPort.width = global.screen_width;
+ viewPort.height = global.screen_height / 2;
+ this._setViewPort(viewPort);
+ this._screenPosition = GDesktopEnums.MagnifierScreenPosition.TOP_HALF;
+ }
+
+ /**
+ * setBottomHalf:
+ * Magnifier view occupies the bottom half of the screen.
+ */
+ setBottomHalf() {
+ let viewPort = {};
+ viewPort.x = 0;
+ viewPort.y = global.screen_height / 2;
+ viewPort.width = global.screen_width;
+ viewPort.height = global.screen_height / 2;
+ this._setViewPort(viewPort);
+ this._screenPosition = GDesktopEnums.MagnifierScreenPosition.BOTTOM_HALF;
+ }
+
+ /**
+ * setLeftHalf:
+ * Magnifier view occupies the left half of the screen.
+ */
+ setLeftHalf() {
+ let viewPort = {};
+ viewPort.x = 0;
+ viewPort.y = 0;
+ viewPort.width = global.screen_width / 2;
+ viewPort.height = global.screen_height;
+ this._setViewPort(viewPort);
+ this._screenPosition = GDesktopEnums.MagnifierScreenPosition.LEFT_HALF;
+ }
+
+ /**
+ * setRightHalf:
+ * Magnifier view occupies the right half of the screen.
+ */
+ setRightHalf() {
+ let viewPort = {};
+ viewPort.x = global.screen_width / 2;
+ viewPort.y = 0;
+ viewPort.width = global.screen_width / 2;
+ viewPort.height = global.screen_height;
+ this._setViewPort(viewPort);
+ this._screenPosition = GDesktopEnums.MagnifierScreenPosition.RIGHT_HALF;
+ }
+
+ /**
+ * setFullScreenMode:
+ * Set the ZoomRegion to full-screen mode.
+ * Note: disallows lens mode.
+ */
+ setFullScreenMode() {
+ let viewPort = {};
+ viewPort.x = 0;
+ viewPort.y = 0;
+ viewPort.width = global.screen_width;
+ viewPort.height = global.screen_height;
+ this.setViewPort(viewPort);
+
+ this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
+ }
+
+ /**
+ * setScreenPosition:
+ * Positions the zoom region to one of the enumerated positions on the
+ * screen.
+ * @param {GDesktopEnums.MagnifierScreenPosition} inPosition: the position
+ */
+ setScreenPosition(inPosition) {
+ switch (inPosition) {
+ case GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN:
+ this.setFullScreenMode();
+ break;
+ case GDesktopEnums.MagnifierScreenPosition.TOP_HALF:
+ this.setTopHalf();
+ break;
+ case GDesktopEnums.MagnifierScreenPosition.BOTTOM_HALF:
+ this.setBottomHalf();
+ break;
+ case GDesktopEnums.MagnifierScreenPosition.LEFT_HALF:
+ this.setLeftHalf();
+ break;
+ case GDesktopEnums.MagnifierScreenPosition.RIGHT_HALF:
+ this.setRightHalf();
+ break;
+ }
+ }
+
+ /**
+ * getScreenPosition:
+ * Tell the outside world what the current mode is -- magnifiying the
+ * top half, bottom half, etc.
+ * @returns {GDesktopEnums.MagnifierScreenPosition}: the current position.
+ */
+ getScreenPosition() {
+ return this._screenPosition;
+ }
+
+ _clearScrollContentsTimer() {
+ if (this._scrollContentsTimerId !== 0) {
+ GLib.source_remove(this._scrollContentsTimerId);
+ this._scrollContentsTimerId = 0;
+ }
+ }
+
+ /**
+ * scrollToMousePos:
+ * Set the region of interest based on the position of the system pointer.
+ * @returns {bool}: Whether the system mouse pointer is over the
+ * magnified view.
+ */
+ scrollToMousePos() {
+ this._followingCursor = true;
+ if (this._mouseTrackingMode != GDesktopEnums.MagnifierMouseTrackingMode.NONE)
+ this._changeROI({ redoCursorTracking: true });
+ else
+ this._updateMousePosition();
+
+ this._clearScrollContentsTimer();
+ this._scrollContentsTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, POINTER_REST_TIME, () => {
+ this._followingCursor = false;
+ if (this._xDelayed !== null && this._yDelayed !== null) {
+ this._scrollContentsToDelayed(this._xDelayed, this._yDelayed);
+ this._xDelayed = null;
+ this._yDelayed = null;
+ }
+
+ this._scrollContentsTimerId = 0;
+
+ return GLib.SOURCE_REMOVE;
+ });
+
+ // Determine whether the system mouse pointer is over this zoom region.
+ return this._isMouseOverRegion();
+ }
+
+ _scrollContentsToDelayed(x, y) {
+ if (this._followingCursor) {
+ this._xDelayed = x;
+ this._yDelayed = y;
+ } else {
+ this.scrollContentsTo(x, y);
+ }
+ }
+
+ /**
+ * scrollContentsTo:
+ * Shift the contents of the magnified view such it is centered on the given
+ * coordinate.
+ * @param {number} x: The x-coord of the point to center on.
+ * @param {number} y: The y-coord of the point to center on.
+ */
+ scrollContentsTo(x, y) {
+ if (x < 0 || x > global.screen_width ||
+ y < 0 || y > global.screen_height)
+ return;
+
+ this._clearScrollContentsTimer();
+
+ this._followingCursor = false;
+ this._changeROI({
+ xCenter: x,
+ yCenter: y,
+ animate: true,
+ });
+ }
+
+ /**
+ * addCrosshairs:
+ * Add crosshairs centered on the magnified mouse.
+ * @param {Crosshairs} crossHairs: Crosshairs instance
+ */
+ addCrosshairs(crossHairs) {
+ this._crossHairs = crossHairs;
+
+ // If the crossHairs is not already within a larger container, add it
+ // to this zoom region. Otherwise, add a clone.
+ if (crossHairs && this.isActive())
+ this._crossHairsActor = crossHairs.addToZoomRegion(this, this._mouseActor);
+ }
+
+ /**
+ * setInvertLightness:
+ * Set whether to invert the lightness of the magnified view.
+ * @param {bool} flag: whether brightness should be inverted
+ */
+ setInvertLightness(flag) {
+ this._invertLightness = flag;
+ if (this._magShaderEffects)
+ this._magShaderEffects.setInvertLightness(this._invertLightness);
+ }
+
+ /**
+ * getInvertLightness:
+ * Retrieve whether the lightness is inverted.
+ * @returns {bool} whether brightness should be inverted
+ */
+ getInvertLightness() {
+ return this._invertLightness;
+ }
+
+ /**
+ * setColorSaturation:
+ * Set the color saturation of the magnified view.
+ * @param {number} saturation: A value from 0.0 to 1.0 that defines
+ * the color saturation, with 0.0 defining no color (grayscale),
+ * and 1.0 defining full color.
+ */
+ setColorSaturation(saturation) {
+ this._colorSaturation = saturation;
+ if (this._magShaderEffects)
+ this._magShaderEffects.setColorSaturation(this._colorSaturation);
+ }
+
+ /**
+ * getColorSaturation:
+ * Retrieve the color saturation of the magnified view.
+ * @returns {number} the color saturation
+ */
+ getColorSaturation() {
+ return this._colorSaturation;
+ }
+
+ /**
+ * setBrightness:
+ * Alter the brightness of the magnified view.
+ * @param {Object} brightness: Object containing the contrast for the
+ * red, green, and blue channels. Values of 0.0 represent "standard"
+ * brightness (no change), whereas values less or greater than
+ * 0.0 indicate decreased or incresaed brightness, respectively.
+ *
+ * {number} brightness.r - the red component
+ * {number} brightness.g - the green component
+ * {number} brightness.b - the blue component
+ */
+ setBrightness(brightness) {
+ this._brightness.r = brightness.r;
+ this._brightness.g = brightness.g;
+ this._brightness.b = brightness.b;
+ if (this._magShaderEffects)
+ this._magShaderEffects.setBrightness(this._brightness);
+ }
+
+ /**
+ * setContrast:
+ * Alter the contrast of the magnified view.
+ * @param {Object} contrast: Object containing the contrast for the
+ * red, green, and blue channels. Values of 0.0 represent "standard"
+ * contrast (no change), whereas values less or greater than
+ * 0.0 indicate decreased or incresaed contrast, respectively.
+ *
+ * {number} contrast.r - the red component
+ * {number} contrast.g - the green component
+ * {number} contrast.b - the blue component
+ */
+ setContrast(contrast) {
+ this._contrast.r = contrast.r;
+ this._contrast.g = contrast.g;
+ this._contrast.b = contrast.b;
+ if (this._magShaderEffects)
+ this._magShaderEffects.setContrast(this._contrast);
+ }
+
+ /**
+ * getContrast:
+ * Retrieve the contrast of the magnified view.
+ * @returns {{r: number, g: number, b: number}}: Object containing
+ * the contrast for the red, green, and blue channels.
+ */
+ getContrast() {
+ let contrast = {};
+ contrast.r = this._contrast.r;
+ contrast.g = this._contrast.g;
+ contrast.b = this._contrast.b;
+ return contrast;
+ }
+
+ // Private methods //
+
+ _createActors() {
+ // The root actor for the zoom region
+ this._magView = new St.Bin({ style_class: 'magnifier-zoom-region' });
+ global.stage.add_actor(this._magView);
+
+ // hide the magnified region from CLUTTER_PICK_ALL
+ Shell.util_set_hidden_from_pick(this._magView, true);
+
+ // Add a group to clip the contents of the magnified view.
+ let mainGroup = new Clutter.Actor({ clip_to_allocation: true });
+ this._magView.set_child(mainGroup);
+
+ // Add a background for when the magnified uiGroup is scrolled
+ // out of view (don't want to see desktop showing through).
+ this._background = new Background.SystemBackground();
+ mainGroup.add_actor(this._background);
+
+ // Clone the group that contains all of UI on the screen. This is the
+ // chrome, the windows, etc.
+ this._uiGroupClone = new Clutter.Clone({
+ source: Main.uiGroup,
+ clip_to_allocation: true,
+ });
+ mainGroup.add_actor(this._uiGroupClone);
+
+ // Add either the given mouseSourceActor to the ZoomRegion, or a clone of
+ // it.
+ if (this._mouseSourceActor.get_parent() != null)
+ this._mouseActor = new Clutter.Clone({ source: this._mouseSourceActor });
+ else
+ this._mouseActor = this._mouseSourceActor;
+ mainGroup.add_actor(this._mouseActor);
+
+ if (this._crossHairs)
+ this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
+ else
+ this._crossHairsActor = null;
+
+ // Contrast and brightness effects.
+ this._magShaderEffects = new MagShaderEffects(mainGroup);
+ this._magShaderEffects.setColorSaturation(this._colorSaturation);
+ this._magShaderEffects.setInvertLightness(this._invertLightness);
+ this._magShaderEffects.setBrightness(this._brightness);
+ this._magShaderEffects.setContrast(this._contrast);
+ }
+
+ _destroyActors() {
+ if (this._mouseActor == this._mouseSourceActor)
+ this._mouseActor.get_parent().remove_actor(this._mouseActor);
+ if (this._crossHairs)
+ this._crossHairs.removeFromParent(this._crossHairsActor);
+
+ this._magShaderEffects.destroyEffects();
+ this._magShaderEffects = null;
+ this._magView.destroy();
+ this._magView = null;
+ this._background = null;
+ this._uiGroupClone = null;
+ this._mouseActor = null;
+ this._crossHairsActor = null;
+ }
+
+ _setViewPort(viewPort, fromROIUpdate) {
+ // Sets the position of the zoom region on the screen
+
+ let width = Math.round(Math.min(viewPort.width, global.screen_width));
+ let height = Math.round(Math.min(viewPort.height, global.screen_height));
+ let x = Math.max(viewPort.x, 0);
+ let y = Math.max(viewPort.y, 0);
+
+ x = Math.round(Math.min(x, global.screen_width - width));
+ y = Math.round(Math.min(y, global.screen_height - height));
+
+ this._viewPortX = x;
+ this._viewPortY = y;
+ this._viewPortWidth = width;
+ this._viewPortHeight = height;
+
+ this._updateMagViewGeometry();
+
+ if (!fromROIUpdate)
+ this._changeROI({ redoCursorTracking: this._followingCursor }); // will update mouse
+
+ if (this.isActive() && this._isMouseOverRegion())
+ this._magnifier.hideSystemCursor();
+
+ const uiGroupIsOccluded = this.isActive() && this._isFullScreen();
+ Main.uiGroup.set_opacity(uiGroupIsOccluded ? 0 : 255);
+ }
+
+ _changeROI(params) {
+ // Updates the area we are viewing; the magnification factors
+ // and center can be set explicitly, or we can recompute
+ // the position based on the mouse cursor position
+
+ params = Params.parse(params, {
+ xMagFactor: this._xMagFactor,
+ yMagFactor: this._yMagFactor,
+ xCenter: this._xCenter,
+ yCenter: this._yCenter,
+ redoCursorTracking: false,
+ animate: false,
+ });
+
+ if (params.xMagFactor <= 0)
+ params.xMagFactor = this._xMagFactor;
+ if (params.yMagFactor <= 0)
+ params.yMagFactor = this._yMagFactor;
+
+ this._xMagFactor = params.xMagFactor;
+ this._yMagFactor = params.yMagFactor;
+
+ if (params.redoCursorTracking &&
+ this._mouseTrackingMode != GDesktopEnums.MagnifierMouseTrackingMode.NONE) {
+ // This depends on this.xMagFactor/yMagFactor already being updated
+ [params.xCenter, params.yCenter] = this._centerFromMousePosition();
+ }
+
+ if (this._clampScrollingAtEdges) {
+ let roiWidth = this._viewPortWidth / this._xMagFactor;
+ let roiHeight = this._viewPortHeight / this._yMagFactor;
+
+ params.xCenter = Math.min(params.xCenter, global.screen_width - roiWidth / 2);
+ params.xCenter = Math.max(params.xCenter, roiWidth / 2);
+ params.yCenter = Math.min(params.yCenter, global.screen_height - roiHeight / 2);
+ params.yCenter = Math.max(params.yCenter, roiHeight / 2);
+ }
+
+ this._xCenter = params.xCenter;
+ this._yCenter = params.yCenter;
+
+ // If in lens mode, move the magnified view such that it is centered
+ // over the actual mouse. However, in full screen mode, the "lens" is
+ // the size of the screen -- pointless to move such a large lens around.
+ if (this._lensMode && !this._isFullScreen()) {
+ this._setViewPort({
+ x: this._xCenter - this._viewPortWidth / 2,
+ y: this._yCenter - this._viewPortHeight / 2,
+ width: this._viewPortWidth,
+ height: this._viewPortHeight,
+ }, true);
+ }
+
+ this._updateCloneGeometry(params.animate);
+ }
+
+ _isMouseOverRegion() {
+ // Return whether the system mouse sprite is over this ZoomRegion. If the
+ // mouse's position is not given, then it is fetched.
+ let mouseIsOver = false;
+ if (this.isActive()) {
+ let xMouse = this._magnifier.xMouse;
+ let yMouse = this._magnifier.yMouse;
+
+ mouseIsOver =
+ xMouse >= this._viewPortX && xMouse < (this._viewPortX + this._viewPortWidth) &&
+ yMouse >= this._viewPortY && yMouse < (this._viewPortY + this._viewPortHeight);
+ }
+ return mouseIsOver;
+ }
+
+ _isFullScreen() {
+ // Does the magnified view occupy the whole screen? Note that this
+ // doesn't necessarily imply
+ // this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
+
+ if (this._viewPortX != 0 || this._viewPortY != 0)
+ return false;
+ if (this._viewPortWidth != global.screen_width ||
+ this._viewPortHeight != global.screen_height)
+ return false;
+ return true;
+ }
+
+ _centerFromMousePosition() {
+ // Determines where the center should be given the current cursor
+ // position and mouse tracking mode
+
+ let xMouse = this._magnifier.xMouse;
+ let yMouse = this._magnifier.yMouse;
+
+ if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL)
+ return this._centerFromPointProportional(xMouse, yMouse);
+ else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH)
+ return this._centerFromPointPush(xMouse, yMouse);
+ else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED)
+ return this._centerFromPointCentered(xMouse, yMouse);
+
+ return null; // Should never be hit
+ }
+
+ _centerFromCaretPosition() {
+ let xCaret = this._xCaret;
+ let yCaret = this._yCaret;
+
+ if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PROPORTIONAL)
+ [xCaret, yCaret] = this._centerFromPointProportional(xCaret, yCaret);
+ else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PUSH)
+ [xCaret, yCaret] = this._centerFromPointPush(xCaret, yCaret);
+ else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
+ [xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
+
+ this._scrollContentsToDelayed(xCaret, yCaret);
+ }
+
+ _centerFromFocusPosition() {
+ let xFocus = this._xFocus;
+ let yFocus = this._yFocus;
+
+ if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PROPORTIONAL)
+ [xFocus, yFocus] = this._centerFromPointProportional(xFocus, yFocus);
+ else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PUSH)
+ [xFocus, yFocus] = this._centerFromPointPush(xFocus, yFocus);
+ else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
+ [xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
+
+ this._scrollContentsToDelayed(xFocus, yFocus);
+ }
+
+ _centerFromPointPush(xPoint, yPoint) {
+ let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
+ let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
+ let xPos = xRoi + widthRoi / 2;
+ let yPos = yRoi + heightRoi / 2;
+ let xRoiRight = xRoi + widthRoi - cursorWidth;
+ let yRoiBottom = yRoi + heightRoi - cursorHeight;
+
+ if (xPoint < xRoi)
+ xPos -= xRoi - xPoint;
+ else if (xPoint > xRoiRight)
+ xPos += xPoint - xRoiRight;
+
+ if (yPoint < yRoi)
+ yPos -= yRoi - yPoint;
+ else if (yPoint > yRoiBottom)
+ yPos += yPoint - yRoiBottom;
+
+ return [xPos, yPos];
+ }
+
+ _centerFromPointProportional(xPoint, yPoint) {
+ let [xRoi_, yRoi_, widthRoi, heightRoi] = this.getROI();
+ let halfScreenWidth = global.screen_width / 2;
+ let halfScreenHeight = global.screen_height / 2;
+ // We want to pad with a constant distance after zooming, so divide
+ // by the magnification factor.
+ let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
+ let xPadding = unscaledPadding / this._xMagFactor;
+ let yPadding = unscaledPadding / this._yMagFactor;
+ let xProportion = (xPoint - halfScreenWidth) / halfScreenWidth; // -1 ... 1
+ let yProportion = (yPoint - halfScreenHeight) / halfScreenHeight; // -1 ... 1
+ let xPos = xPoint - xProportion * (widthRoi / 2 - xPadding);
+ let yPos = yPoint - yProportion * (heightRoi / 2 - yPadding);
+
+ return [xPos, yPos];
+ }
+
+ _centerFromPointCentered(xPoint, yPoint) {
+ return [xPoint, yPoint];
+ }
+
+ _screenToViewPort(screenX, screenY) {
+ // Converts coordinates relative to the (unmagnified) screen to coordinates
+ // relative to the origin of this._magView
+ return [
+ this._viewPortWidth / 2 + (screenX - this._xCenter) * this._xMagFactor,
+ this._viewPortHeight / 2 + (screenY - this._yCenter) * this._yMagFactor,
+ ];
+ }
+
+ _updateMagViewGeometry() {
+ if (!this.isActive())
+ return;
+
+ if (this._isFullScreen())
+ this._magView.add_style_class_name('full-screen');
+ else
+ this._magView.remove_style_class_name('full-screen');
+
+ this._magView.set_size(this._viewPortWidth, this._viewPortHeight);
+ this._magView.set_position(this._viewPortX, this._viewPortY);
+ }
+
+ _updateCloneGeometry(animate = false) {
+ if (!this.isActive())
+ return;
+
+ let [x, y] = this._screenToViewPort(0, 0);
+ this._uiGroupClone.ease({
+ x: Math.round(x),
+ y: Math.round(y),
+ scale_x: this._xMagFactor,
+ scale_y: this._yMagFactor,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: animate ? 100 : 0,
+ });
+
+ let [mouseX, mouseY] = this._getMousePosition();
+ this._mouseActor.ease({
+ x: mouseX,
+ y: mouseY,
+ scale_x: this._xMagFactor,
+ scale_y: this._yMagFactor,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: animate ? 100 : 0,
+ });
+
+ if (this._crossHairsActor) {
+ let [crossX, crossY] = this._getCrossHairsPosition();
+ this._crossHairsActor.ease({
+ x: crossX,
+ y: crossY,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: animate ? 100 : 0,
+ });
+ }
+ }
+
+ _updateMousePosition() {
+ let [xMagMouse, yMagMouse] = this._getMousePosition();
+ this._mouseActor.set_position(xMagMouse, yMagMouse);
+
+ if (this._crossHairsActor) {
+ let [crossX, crossY] = this._getCrossHairsPosition();
+ this._crossHairsActor.set_position(crossX, crossY);
+ }
+ }
+
+ _getMousePosition() {
+ let [xMagMouse, yMagMouse] = this._screenToViewPort(
+ this._magnifier.xMouse, this._magnifier.yMouse);
+ return [Math.round(xMagMouse), Math.round(yMagMouse)];
+ }
+
+ _getCrossHairsPosition() {
+ let [xMagMouse, yMagMouse] = this._getMousePosition();
+ let [groupWidth, groupHeight] = this._crossHairsActor.get_size();
+
+ return [xMagMouse - groupWidth / 2, yMagMouse - groupHeight / 2];
+ }
+
+ _monitorsChanged() {
+ this._background.set_size(global.screen_width, global.screen_height);
+ this._updateScreenPosition();
+ }
+};
+
+var Crosshairs = GObject.registerClass(
+class Crosshairs extends Clutter.Actor {
+ _init() {
+ // Set the group containing the crosshairs to three times the desktop
+ // size in case the crosshairs need to appear to be infinite in
+ // length (i.e., extend beyond the edges of the view they appear in).
+ let groupWidth = global.screen_width * 3;
+ let groupHeight = global.screen_height * 3;
+
+ super._init({
+ clip_to_allocation: false,
+ width: groupWidth,
+ height: groupHeight,
+ });
+ this._horizLeftHair = new Clutter.Actor();
+ this._horizRightHair = new Clutter.Actor();
+ this._vertTopHair = new Clutter.Actor();
+ this._vertBottomHair = new Clutter.Actor();
+ this.add_actor(this._horizLeftHair);
+ this.add_actor(this._horizRightHair);
+ this.add_actor(this._vertTopHair);
+ this.add_actor(this._vertBottomHair);
+ this._clipSize = [0, 0];
+ this._clones = [];
+ this.reCenter();
+ this._monitorsChangedId = 0;
+ }
+
+ _monitorsChanged() {
+ this.set_size(global.screen_width * 3, global.screen_height * 3);
+ this.reCenter();
+ }
+
+ setEnabled(enabled) {
+ if (enabled && this._monitorsChangedId === 0) {
+ this._monitorsChangedId = Main.layoutManager.connect(
+ 'monitors-changed', this._monitorsChanged.bind(this));
+ } else if (!enabled && this._monitorsChangedId !== 0) {
+ Main.layoutManager.disconnect(this._monitorsChangedId);
+ this._monitorsChangedId = 0;
+ }
+ }
+
+ /**
+ * addToZoomRegion
+ * Either add the crosshairs actor to the given ZoomRegion, or, if it is
+ * already part of some other ZoomRegion, create a clone of the crosshairs
+ * actor, and add the clone instead. Returns either the original or the
+ * clone.
+ * @param {ZoomRegion} zoomRegion: The container to add the crosshairs
+ * group to.
+ * @param {Clutter.Actor} magnifiedMouse: The mouse actor for the
+ * zoom region -- used to position the crosshairs and properly
+ * layer them below the mouse.
+ * @returns {Clutter.Actor} The crosshairs actor, or its clone.
+ */
+ addToZoomRegion(zoomRegion, magnifiedMouse) {
+ let crosshairsActor = null;
+ if (zoomRegion && magnifiedMouse) {
+ let container = magnifiedMouse.get_parent();
+ if (container) {
+ crosshairsActor = this;
+ if (this.get_parent() != null) {
+ crosshairsActor = new Clutter.Clone({ source: this });
+ this._clones.push(crosshairsActor);
+
+ // Clones don't share visibility.
+ this.bind_property('visible', crosshairsActor, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ container.add_actor(crosshairsActor);
+ container.set_child_above_sibling(magnifiedMouse, crosshairsActor);
+ let [xMouse, yMouse] = magnifiedMouse.get_position();
+ let [crosshairsWidth, crosshairsHeight] = crosshairsActor.get_size();
+ crosshairsActor.set_position(xMouse - crosshairsWidth / 2, yMouse - crosshairsHeight / 2);
+ }
+ }
+ return crosshairsActor;
+ }
+
+ /**
+ * removeFromParent:
+ * @param {Clutter.Actor} childActor: the actor returned from
+ * addToZoomRegion
+ * Remove the crosshairs actor from its parent container, or destroy the
+ * child actor if it was just a clone of the crosshairs actor.
+ */
+ removeFromParent(childActor) {
+ if (childActor == this)
+ childActor.get_parent().remove_actor(childActor);
+ else
+ childActor.destroy();
+ }
+
+ /**
+ * setColor:
+ * Set the color of the crosshairs.
+ * @param {Clutter.Color} clutterColor: The color
+ */
+ setColor(clutterColor) {
+ this._horizLeftHair.background_color = clutterColor;
+ this._horizRightHair.background_color = clutterColor;
+ this._vertTopHair.background_color = clutterColor;
+ this._vertBottomHair.background_color = clutterColor;
+ }
+
+ /**
+ * getColor:
+ * Get the color of the crosshairs.
+ * @returns {ClutterColor} the crosshairs color
+ */
+ getColor() {
+ return this._horizLeftHair.get_color();
+ }
+
+ /**
+ * setThickness:
+ * Set the width of the vertical and horizontal lines of the crosshairs.
+ * @param {number} thickness: the new thickness value
+ */
+ setThickness(thickness) {
+ this._horizLeftHair.set_height(thickness);
+ this._horizRightHair.set_height(thickness);
+ this._vertTopHair.set_width(thickness);
+ this._vertBottomHair.set_width(thickness);
+ this.reCenter();
+ }
+
+ /**
+ * getThickness:
+ * Get the width of the vertical and horizontal lines of the crosshairs.
+ * @returns {number} The thickness of the crosshairs.
+ */
+ getThickness() {
+ return this._horizLeftHair.get_height();
+ }
+
+ /**
+ * setOpacity:
+ * Set how opaque the crosshairs are.
+ * @param {number} opacity: Value between 0 (fully transparent)
+ * and 255 (full opaque).
+ */
+ setOpacity(opacity) {
+ // set_opacity() throws an exception for values outside the range
+ // [0, 255].
+ if (opacity < 0)
+ opacity = 0;
+ else if (opacity > 255)
+ opacity = 255;
+
+ this._horizLeftHair.set_opacity(opacity);
+ this._horizRightHair.set_opacity(opacity);
+ this._vertTopHair.set_opacity(opacity);
+ this._vertBottomHair.set_opacity(opacity);
+ }
+
+ /**
+ * setLength:
+ * Set the length of the vertical and horizontal lines in the crosshairs.
+ * @param {number} length: The length of the crosshairs.
+ */
+ setLength(length) {
+ this._horizLeftHair.set_width(length);
+ this._horizRightHair.set_width(length);
+ this._vertTopHair.set_height(length);
+ this._vertBottomHair.set_height(length);
+ this.reCenter();
+ }
+
+ /**
+ * getLength:
+ * Get the length of the vertical and horizontal lines in the crosshairs.
+ * @returns {number} The length of the crosshairs.
+ */
+ getLength() {
+ return this._horizLeftHair.get_width();
+ }
+
+ /**
+ * setClip:
+ * Set the width and height of the rectangle that clips the crosshairs at
+ * their intersection
+ * @param {number[]} size: Array of [width, height] defining the size
+ * of the clip rectangle.
+ */
+ setClip(size) {
+ if (size) {
+ // Take a chunk out of the crosshairs where it intersects the
+ // mouse.
+ this._clipSize = size;
+ this.reCenter();
+ } else {
+ // Restore the missing chunk.
+ this._clipSize = [0, 0];
+ this.reCenter();
+ }
+ }
+
+ /**
+ * reCenter:
+ * Reposition the horizontal and vertical hairs such that they cross at
+ * the center of crosshairs group. If called with the dimensions of
+ * the clip rectangle, these are used to update the size of the clip.
+ * @param {number[]=} clipSize: If present, the clip's [width, height].
+ */
+ reCenter(clipSize) {
+ let [groupWidth, groupHeight] = this.get_size();
+ let leftLength = this._horizLeftHair.get_width();
+ let topLength = this._vertTopHair.get_height();
+ let thickness = this._horizLeftHair.get_height();
+
+ // Deal with clip rectangle.
+ if (clipSize)
+ this._clipSize = clipSize;
+ let clipWidth = this._clipSize[0];
+ let clipHeight = this._clipSize[1];
+
+ let left = groupWidth / 2 - clipWidth / 2 - leftLength - thickness / 2;
+ let right = groupWidth / 2 + clipWidth / 2 + thickness / 2;
+ let top = groupHeight / 2 - clipHeight / 2 - topLength - thickness / 2;
+ let bottom = groupHeight / 2 + clipHeight / 2 + thickness / 2;
+ this._horizLeftHair.set_position(left, (groupHeight - thickness) / 2);
+ this._horizRightHair.set_position(right, (groupHeight - thickness) / 2);
+ this._vertTopHair.set_position((groupWidth - thickness) / 2, top);
+ this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
+ }
+});
+
+var MagShaderEffects = class MagShaderEffects {
+ constructor(uiGroupClone) {
+ this._inverse = new Shell.InvertLightnessEffect();
+ this._brightnessContrast = new Clutter.BrightnessContrastEffect();
+ this._colorDesaturation = new Clutter.DesaturateEffect();
+ this._inverse.set_enabled(false);
+ this._brightnessContrast.set_enabled(false);
+ this._colorDesaturation.set_enabled(false);
+
+ this._magView = uiGroupClone;
+ this._magView.add_effect(this._inverse);
+ this._magView.add_effect(this._brightnessContrast);
+ this._magView.add_effect(this._colorDesaturation);
+ }
+
+ /**
+ * destroyEffects:
+ * Remove contrast and brightness effects from the magnified view, and
+ * lose the reference to the actor they were applied to. Don't use this
+ * object after calling this.
+ */
+ destroyEffects() {
+ this._magView.clear_effects();
+ this._colorDesaturation = null;
+ this._brightnessContrast = null;
+ this._inverse = null;
+ this._magView = null;
+ }
+
+ /**
+ * setInvertLightness:
+ * Enable/disable invert lightness effect.
+ * @param {bool} invertFlag: Enabled flag.
+ */
+ setInvertLightness(invertFlag) {
+ this._inverse.set_enabled(invertFlag);
+ }
+
+ setColorSaturation(factor) {
+ this._colorDesaturation.set_factor(1.0 - factor);
+ this._colorDesaturation.set_enabled(factor !== 1.0);
+ }
+
+ /**
+ * setBrightness:
+ * Set the brightness of the magnified view.
+ * @param {Object} brightness: Object containing the contrast for the
+ * red, green, and blue channels. Values of 0.0 represent "standard"
+ * brightness (no change), whereas values less or greater than
+ * 0.0 indicate decreased or incresaed brightness, respectively.
+ *
+ * {number} brightness.r - the red component
+ * {number} brightness.g - the green component
+ * {number} brightness.b - the blue component
+ */
+ setBrightness(brightness) {
+ let bRed = brightness.r;
+ let bGreen = brightness.g;
+ let bBlue = brightness.b;
+ this._brightnessContrast.set_brightness_full(bRed, bGreen, bBlue);
+
+ // Enable the effect if the brightness OR contrast change are such that
+ // it modifies the brightness and/or contrast.
+ let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
+ this._brightnessContrast.set_enabled(
+ bRed !== NO_CHANGE || bGreen !== NO_CHANGE || bBlue !== NO_CHANGE ||
+ cRed !== NO_CHANGE || cGreen !== NO_CHANGE || cBlue !== NO_CHANGE);
+ }
+
+ /**
+ * Set the contrast of the magnified view.
+ * @param {Object} contrast: Object containing the contrast for the
+ * red, green, and blue channels. Values of 0.0 represent "standard"
+ * contrast (no change), whereas values less or greater than
+ * 0.0 indicate decreased or incresaed contrast, respectively.
+ *
+ * {number} contrast.r - the red component
+ * {number} contrast.g - the green component
+ * {number} contrast.b - the blue component
+ */
+ setContrast(contrast) {
+ let cRed = contrast.r;
+ let cGreen = contrast.g;
+ let cBlue = contrast.b;
+
+ this._brightnessContrast.set_contrast_full(cRed, cGreen, cBlue);
+
+ // Enable the effect if the contrast OR brightness change are such that
+ // it modifies the brightness and/or contrast.
+ // should be able to use Clutter.color_equal(), but that complains of
+ // a null first argument.
+ let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
+ this._brightnessContrast.set_enabled(
+ cRed !== NO_CHANGE || cGreen !== NO_CHANGE || cBlue !== NO_CHANGE ||
+ bRed !== NO_CHANGE || bGreen !== NO_CHANGE || bBlue !== NO_CHANGE);
+ }
+};
diff --git a/js/ui/main.js b/js/ui/main.js
new file mode 100644
index 0000000..2d8804a
--- /dev/null
+++ b/js/ui/main.js
@@ -0,0 +1,958 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported componentManager, notificationDaemon, windowAttentionHandler,
+ ctrlAltTabManager, padOsdService, osdWindowManager,
+ osdMonitorLabeler, shellMountOpDBusService, shellDBusService,
+ shellAccessDialogDBusService, shellAudioSelectionDBusService,
+ screenSaverDBus, uiGroup, magnifier, xdndHandler, keyboard,
+ kbdA11yDialog, introspectService, start, pushModal, popModal,
+ activateWindow, moveWindowToMonitorAndWorkspace,
+ createLookingGlass, initializeDeferredWork,
+ getThemeStylesheet, setThemeStylesheet, screenshotUI */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const AccessDialog = imports.ui.accessDialog;
+const AudioDeviceSelection = imports.ui.audioDeviceSelection;
+const Components = imports.ui.components;
+const CtrlAltTab = imports.ui.ctrlAltTab;
+const EndSessionDialog = imports.ui.endSessionDialog;
+const ExtensionSystem = imports.ui.extensionSystem;
+const ExtensionDownloader = imports.ui.extensionDownloader;
+const InputMethod = imports.misc.inputMethod;
+const Introspect = imports.misc.introspect;
+const Keyboard = imports.ui.keyboard;
+const MessageTray = imports.ui.messageTray;
+const ModalDialog = imports.ui.modalDialog;
+const OsdWindow = imports.ui.osdWindow;
+const OsdMonitorLabeler = imports.ui.osdMonitorLabeler;
+const Overview = imports.ui.overview;
+const PadOsd = imports.ui.padOsd;
+const Panel = imports.ui.panel;
+const Params = imports.misc.params;
+const RunDialog = imports.ui.runDialog;
+const WelcomeDialog = imports.ui.welcomeDialog;
+const Layout = imports.ui.layout;
+const LoginManager = imports.misc.loginManager;
+const LookingGlass = imports.ui.lookingGlass;
+const NotificationDaemon = imports.ui.notificationDaemon;
+const WindowAttentionHandler = imports.ui.windowAttentionHandler;
+const Screenshot = imports.ui.screenshot;
+const ScreenShield = imports.ui.screenShield;
+const Scripting = imports.ui.scripting;
+const SessionMode = imports.ui.sessionMode;
+const ShellDBus = imports.ui.shellDBus;
+const ShellMountOperation = imports.ui.shellMountOperation;
+const WindowManager = imports.ui.windowManager;
+const Magnifier = imports.ui.magnifier;
+const XdndHandler = imports.ui.xdndHandler;
+const KbdA11yDialog = imports.ui.kbdA11yDialog;
+const LocatePointer = imports.ui.locatePointer;
+const PointerA11yTimeout = imports.ui.pointerA11yTimeout;
+const ParentalControlsManager = imports.misc.parentalControlsManager;
+const Config = imports.misc.config;
+const Util = imports.misc.util;
+
+const WELCOME_DIALOG_LAST_SHOWN_VERSION = 'welcome-dialog-last-shown-version';
+// Make sure to mention the point release, otherwise it will show every time
+// until this version is current
+const WELCOME_DIALOG_LAST_TOUR_CHANGE = '40.beta';
+const LOG_DOMAIN = 'GNOME Shell';
+const GNOMESHELL_STARTED_MESSAGE_ID = 'f3ea493c22934e26811cd62abe8e203a';
+
+var componentManager = null;
+var extensionManager = null;
+var panel = null;
+var overview = null;
+var runDialog = null;
+var lookingGlass = null;
+var welcomeDialog = null;
+var wm = null;
+var messageTray = null;
+var screenShield = null;
+var notificationDaemon = null;
+var windowAttentionHandler = null;
+var ctrlAltTabManager = null;
+var padOsdService = null;
+var osdWindowManager = null;
+var osdMonitorLabeler = null;
+var sessionMode = null;
+var screenshotUI = null;
+var shellAccessDialogDBusService = null;
+var shellAudioSelectionDBusService = null;
+var shellDBusService = null;
+var shellMountOpDBusService = null;
+var screenSaverDBus = null;
+var modalCount = 0;
+var actionMode = Shell.ActionMode.NONE;
+var modalActorFocusStack = [];
+var uiGroup = null;
+var magnifier = null;
+var xdndHandler = null;
+var keyboard = null;
+var layoutManager = null;
+var kbdA11yDialog = null;
+var inputMethod = null;
+var introspectService = null;
+var locatePointer = null;
+let _startDate;
+let _defaultCssStylesheet = null;
+let _cssStylesheet = null;
+let _themeResource = null;
+let _oskResource = null;
+let _iconResource = null;
+
+Gio._promisify(Gio.File.prototype, 'delete_async');
+Gio._promisify(Gio.File.prototype, 'touch_async');
+
+let _remoteAccessInhibited = false;
+
+function _sessionUpdated() {
+ if (sessionMode.isPrimary)
+ _loadDefaultStylesheet();
+
+ wm.allowKeybinding('overlay-key', Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW);
+
+ wm.allowKeybinding('locate-pointer-key', Shell.ActionMode.ALL);
+
+ wm.setCustomKeybindingHandler('panel-run-dialog',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ sessionMode.hasRunDialog ? openRunDialog : null);
+
+ if (!sessionMode.hasRunDialog) {
+ if (runDialog)
+ runDialog.close();
+ if (lookingGlass)
+ lookingGlass.close();
+ if (welcomeDialog)
+ welcomeDialog.close();
+ }
+
+ let remoteAccessController = global.backend.get_remote_access_controller();
+ if (remoteAccessController && !global.backend.is_headless()) {
+ if (sessionMode.allowScreencast && _remoteAccessInhibited) {
+ remoteAccessController.uninhibit_remote_access();
+ _remoteAccessInhibited = false;
+ } else if (!sessionMode.allowScreencast && !_remoteAccessInhibited) {
+ remoteAccessController.inhibit_remote_access();
+ _remoteAccessInhibited = true;
+ }
+ }
+}
+
+/**
+ * @param {any...} args a list of values to log
+ */
+function _loggingFunc(...args) {
+ let fields = { 'MESSAGE': args.join(', ') };
+ let domain = 'GNOME Shell';
+
+ // If the caller is an extension, add it as metadata
+ let extension = imports.misc.extensionUtils.getCurrentExtension();
+ if (extension != null) {
+ domain = extension.metadata.name;
+ fields['GNOME_SHELL_EXTENSION_UUID'] = extension.uuid;
+ fields['GNOME_SHELL_EXTENSION_NAME'] = extension.metadata.name;
+ }
+
+ GLib.log_structured(domain, GLib.LogLevelFlags.LEVEL_MESSAGE, fields);
+}
+
+function start() {
+ globalThis.log = _loggingFunc;
+
+ // These are here so we don't break compatibility.
+ global.logError = globalThis.log;
+ global.log = globalThis.log;
+
+ // Chain up async errors reported from C
+ global.connect('notify-error', (global, msg, detail) => {
+ notifyError(msg, detail);
+ });
+
+ let currentDesktop = GLib.getenv('XDG_CURRENT_DESKTOP');
+ if (!currentDesktop || !currentDesktop.split(':').includes('GNOME'))
+ Gio.DesktopAppInfo.set_desktop_env('GNOME');
+
+ sessionMode = new SessionMode.SessionMode();
+ sessionMode.connect('updated', _sessionUpdated);
+
+ St.Settings.get().connect('notify::high-contrast', _loadDefaultStylesheet);
+
+ // Initialize ParentalControlsManager before the UI
+ ParentalControlsManager.getDefault();
+
+ _initializeUI();
+
+ shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus();
+ shellAudioSelectionDBusService = new AudioDeviceSelection.AudioDeviceSelectionDBus();
+ shellDBusService = new ShellDBus.GnomeShell();
+ shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
+
+ const watchId = Gio.DBus.session.watch_name('org.gnome.Shell.Notifications',
+ Gio.BusNameWatcherFlags.AUTO_START,
+ bus => bus.unwatch_name(watchId),
+ bus => bus.unwatch_name(watchId));
+
+ _sessionUpdated();
+}
+
+function _initializeUI() {
+ // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
+ // also initialize ShellAppSystem first. ShellAppSystem
+ // needs to load all the .desktop files, and ShellWindowTracker
+ // will use those to associate with windows. Right now
+ // the Monitor doesn't listen for installed app changes
+ // and recalculate application associations, so to avoid
+ // races for now we initialize it here. It's better to
+ // be predictable anyways.
+ Shell.WindowTracker.get_default();
+ Shell.AppUsage.get_default();
+
+ reloadThemeResource();
+ _loadIcons();
+ _loadOskLayouts();
+ _loadDefaultStylesheet();
+
+ new AnimationsSettings();
+
+ // Setup the stage hierarchy early
+ layoutManager = new Layout.LayoutManager();
+
+ // Various parts of the codebase still refer to Main.uiGroup
+ // instead of using the layoutManager. This keeps that code
+ // working until it's updated.
+ uiGroup = layoutManager.uiGroup;
+
+ padOsdService = new PadOsd.PadOsdService();
+ xdndHandler = new XdndHandler.XdndHandler();
+ ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
+ osdWindowManager = new OsdWindow.OsdWindowManager();
+ osdMonitorLabeler = new OsdMonitorLabeler.OsdMonitorLabeler();
+ overview = new Overview.Overview();
+ kbdA11yDialog = new KbdA11yDialog.KbdA11yDialog();
+ wm = new WindowManager.WindowManager();
+ magnifier = new Magnifier.Magnifier();
+ locatePointer = new LocatePointer.LocatePointer();
+
+ if (LoginManager.canLock())
+ screenShield = new ScreenShield.ScreenShield();
+
+ inputMethod = new InputMethod.InputMethod();
+ Clutter.get_default_backend().set_input_method(inputMethod);
+
+ screenshotUI = new Screenshot.ScreenshotUI();
+
+ messageTray = new MessageTray.MessageTray();
+ panel = new Panel.Panel();
+ keyboard = new Keyboard.KeyboardManager();
+ notificationDaemon = new NotificationDaemon.NotificationDaemon();
+ windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
+ componentManager = new Components.ComponentManager();
+
+ introspectService = new Introspect.IntrospectService();
+
+ layoutManager.init();
+ overview.init();
+
+ new PointerA11yTimeout.PointerA11yTimeout();
+
+ global.connect('locate-pointer', () => {
+ locatePointer.show();
+ });
+
+ global.display.connect('show-restart-message', (display, message) => {
+ showRestartMessage(message);
+ return true;
+ });
+
+ global.display.connect('restart', () => {
+ global.reexec_self();
+ return true;
+ });
+
+ global.display.connect('gl-video-memory-purged', loadTheme);
+
+ global.context.connect('notify::unsafe-mode', () => {
+ if (!global.context.unsafe_mode)
+ return; // we're safe
+ if (lookingGlass?.isOpen)
+ return; // assume user action
+
+ const source = new MessageTray.SystemNotificationSource();
+ messageTray.add(source);
+ const notification = new MessageTray.Notification(source,
+ _('System was put in unsafe mode'),
+ _('Applications now have unrestricted access'));
+ notification.addAction(_('Undo'),
+ () => (global.context.unsafe_mode = false));
+ notification.setTransient(true);
+ source.showNotification(notification);
+ });
+
+ // Provide the bus object for gnome-session to
+ // initiate logouts.
+ EndSessionDialog.init();
+
+ // We're ready for the session manager to move to the next phase
+ GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ Shell.util_sd_notify();
+ global.context.notify_ready();
+ return GLib.SOURCE_REMOVE;
+ });
+
+ _startDate = new Date();
+
+ ExtensionDownloader.init();
+ extensionManager = new ExtensionSystem.ExtensionManager();
+ extensionManager.init();
+
+ if (sessionMode.isGreeter && screenShield) {
+ layoutManager.connect('startup-prepared', () => {
+ screenShield.showDialog();
+ });
+ }
+
+ layoutManager.connect('startup-complete', () => {
+ if (actionMode == Shell.ActionMode.NONE)
+ actionMode = Shell.ActionMode.NORMAL;
+
+ if (screenShield)
+ screenShield.lockIfWasLocked();
+
+ if (sessionMode.currentMode != 'gdm' &&
+ sessionMode.currentMode != 'initial-setup') {
+ GLib.log_structured(LOG_DOMAIN, GLib.LogLevelFlags.LEVEL_MESSAGE, {
+ 'MESSAGE': `GNOME Shell started at ${_startDate}`,
+ 'MESSAGE_ID': GNOMESHELL_STARTED_MESSAGE_ID,
+ });
+ }
+
+ let credentials = new Gio.Credentials();
+ if (credentials.get_unix_user() === 0) {
+ notify(_('Logged in as a privileged user'),
+ _('Running a session as a privileged user should be avoided for security reasons. If possible, you should log in as a normal user.'));
+ } else if (sessionMode.showWelcomeDialog) {
+ _handleShowWelcomeScreen();
+ }
+
+ if (sessionMode.currentMode !== 'gdm' &&
+ sessionMode.currentMode !== 'initial-setup')
+ _handleLockScreenWarning();
+
+ LoginManager.registerSessionWithGDM();
+
+ let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
+ if (perfModuleName) {
+ let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
+ let module = eval(`imports.perf.${perfModuleName};`);
+ Scripting.runPerfScript(module, perfOutput);
+ }
+ });
+}
+
+function _handleShowWelcomeScreen() {
+ const lastShownVersion = global.settings.get_string(WELCOME_DIALOG_LAST_SHOWN_VERSION);
+ if (Util.GNOMEversionCompare(WELCOME_DIALOG_LAST_TOUR_CHANGE, lastShownVersion) > 0) {
+ openWelcomeDialog();
+ global.settings.set_string(WELCOME_DIALOG_LAST_SHOWN_VERSION, Config.PACKAGE_VERSION);
+ }
+}
+
+async function _handleLockScreenWarning() {
+ const path = `${global.userdatadir}/lock-warning-shown`;
+ const file = Gio.File.new_for_path(path);
+
+ const hasLockScreen = screenShield !== null;
+ if (hasLockScreen) {
+ try {
+ await file.delete_async(0, null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
+ logError(e);
+ }
+ } else {
+ try {
+ if (!await file.touch_async())
+ return;
+ } catch (e) {
+ logError(e);
+ }
+
+ notify(
+ _('Screen Lock disabled'),
+ _('Screen Locking requires the GNOME display manager.'));
+ }
+}
+
+function _getStylesheet(name) {
+ let stylesheet;
+
+ stylesheet = Gio.File.new_for_uri(`resource:///org/gnome/shell/theme/${name}`);
+ if (stylesheet.query_exists(null))
+ return stylesheet;
+
+ let dataDirs = GLib.get_system_data_dirs();
+ for (let i = 0; i < dataDirs.length; i++) {
+ let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'theme', name]);
+ stylesheet = Gio.file_new_for_path(path);
+ if (stylesheet.query_exists(null))
+ return stylesheet;
+ }
+
+ stylesheet = Gio.File.new_for_path(`${global.datadir}/theme/${name}`);
+ if (stylesheet.query_exists(null))
+ return stylesheet;
+
+ return null;
+}
+
+function _getDefaultStylesheet() {
+ let stylesheet = null;
+ let name = sessionMode.stylesheetName;
+
+ // Look for a high-contrast variant first
+ if (St.Settings.get().high_contrast)
+ stylesheet = _getStylesheet(name.replace('.css', '-high-contrast.css'));
+
+ if (stylesheet == null)
+ stylesheet = _getStylesheet(sessionMode.stylesheetName);
+
+ return stylesheet;
+}
+
+function _loadDefaultStylesheet() {
+ let stylesheet = _getDefaultStylesheet();
+ if (_defaultCssStylesheet && _defaultCssStylesheet.equal(stylesheet))
+ return;
+
+ _defaultCssStylesheet = stylesheet;
+ loadTheme();
+}
+
+/**
+ * getThemeStylesheet:
+ *
+ * Get the theme CSS file that the shell will load
+ *
+ * @returns {?Gio.File}: A #GFile that contains the theme CSS,
+ * null if using the default
+ */
+function getThemeStylesheet() {
+ return _cssStylesheet;
+}
+
+/**
+ * setThemeStylesheet:
+ * @param {string=} cssStylesheet: A file path that contains the theme CSS,
+ * set it to null to use the default
+ *
+ * Set the theme CSS file that the shell will load
+ */
+function setThemeStylesheet(cssStylesheet) {
+ _cssStylesheet = cssStylesheet ? Gio.File.new_for_path(cssStylesheet) : null;
+}
+
+function reloadThemeResource() {
+ if (_themeResource)
+ _themeResource._unregister();
+
+ _themeResource = Gio.Resource.load(
+ `${global.datadir}/${sessionMode.themeResourceName}`);
+ _themeResource._register();
+}
+
+/** @private */
+function _loadIcons() {
+ _iconResource = Gio.Resource.load(`${global.datadir}/gnome-shell-icons.gresource`);
+ _iconResource._register();
+}
+
+function _loadOskLayouts() {
+ _oskResource = Gio.Resource.load(`${global.datadir}/gnome-shell-osk-layouts.gresource`);
+ _oskResource._register();
+}
+
+/**
+ * loadTheme:
+ *
+ * Reloads the theme CSS file
+ */
+function loadTheme() {
+ let themeContext = St.ThemeContext.get_for_stage(global.stage);
+ let previousTheme = themeContext.get_theme();
+
+ let theme = new St.Theme({
+ application_stylesheet: _cssStylesheet,
+ default_stylesheet: _defaultCssStylesheet,
+ });
+
+ if (theme.default_stylesheet == null)
+ throw new Error(`No valid stylesheet found for '${sessionMode.stylesheetName}'`);
+
+ if (previousTheme) {
+ let customStylesheets = previousTheme.get_custom_stylesheets();
+
+ for (let i = 0; i < customStylesheets.length; i++)
+ theme.load_stylesheet(customStylesheets[i]);
+ }
+
+ themeContext.set_theme(theme);
+}
+
+/**
+ * notify:
+ * @param {string} msg: A message
+ * @param {string} details: Additional information
+ */
+function notify(msg, details) {
+ let source = new MessageTray.SystemNotificationSource();
+ messageTray.add(source);
+ let notification = new MessageTray.Notification(source, msg, details);
+ notification.setTransient(true);
+ source.showNotification(notification);
+}
+
+/**
+ * notifyError:
+ * @param {string} msg: An error message
+ * @param {string} details: Additional information
+ *
+ * See shell_global_notify_problem().
+ */
+function notifyError(msg, details) {
+ // Also print to stderr so it's logged somewhere
+ if (details)
+ console.warn(`error: ${msg}: ${details}`);
+ else
+ console.warn(`error: ${msg}`);
+
+ notify(msg, details);
+}
+
+/**
+ * _findModal:
+ *
+ * @param {Clutter.Grab} grab - grab
+ *
+ * Private function.
+ *
+ */
+function _findModal(grab) {
+ for (let i = 0; i < modalActorFocusStack.length; i++) {
+ if (modalActorFocusStack[i].grab === grab)
+ return i;
+ }
+ return -1;
+}
+
+/**
+ * pushModal:
+ * @param {Clutter.Actor} actor: actor which will be given keyboard focus
+ * @param {Object=} params: optional parameters
+ *
+ * Ensure we are in a mode where all keyboard and mouse input goes to
+ * the stage, and focus @actor. Multiple calls to this function act in
+ * a stacking fashion; the effect will be undone when an equal number
+ * of popModal() invocations have been made.
+ *
+ * Next, record the current Clutter keyboard focus on a stack. If the
+ * modal stack returns to this actor, reset the focus to the actor
+ * which was focused at the time pushModal() was invoked.
+ *
+ * @params may be used to provide the following parameters:
+ * - timestamp: used to associate the call with a specific user initiated
+ * event. If not provided then the value of
+ * global.get_current_time() is assumed.
+ *
+ * - options: Meta.ModalOptions flags to indicate that the pointer is
+ * already grabbed
+ *
+ * - actionMode: used to set the current Shell.ActionMode to filter
+ * global keybindings; the default of NONE will filter
+ * out all keybindings
+ *
+ * @returns {Clutter.Grab}: the grab handle created
+ */
+function pushModal(actor, params) {
+ params = Params.parse(params, {
+ timestamp: global.get_current_time(),
+ options: 0,
+ actionMode: Shell.ActionMode.NONE,
+ });
+
+ let grab = global.stage.grab(actor);
+
+ if (modalCount === 0)
+ Meta.disable_unredirect_for_display(global.display);
+
+ modalCount += 1;
+ let actorDestroyId = actor.connect('destroy', () => {
+ let index = _findModal(grab);
+ if (index >= 0)
+ popModal(grab);
+ });
+
+ let prevFocus = global.stage.get_key_focus();
+ let prevFocusDestroyId;
+ if (prevFocus != null) {
+ prevFocusDestroyId = prevFocus.connect('destroy', () => {
+ const index = modalActorFocusStack.findIndex(
+ record => record.prevFocus === prevFocus);
+
+ if (index >= 0)
+ modalActorFocusStack[index].prevFocus = null;
+ });
+ }
+ modalActorFocusStack.push({
+ actor,
+ grab,
+ destroyId: actorDestroyId,
+ prevFocus,
+ prevFocusDestroyId,
+ actionMode,
+ });
+
+ actionMode = params.actionMode;
+ global.stage.set_key_focus(actor);
+ return grab;
+}
+
+/**
+ * popModal:
+ * @param {Clutter.Grab} grab - the grab given by pushModal()
+ * @param {number=} timestamp - optional timestamp
+ *
+ * Reverse the effect of pushModal(). If this invocation is undoing
+ * the topmost invocation, then the focus will be restored to the
+ * previous focus at the time when pushModal() was invoked.
+ *
+ * @timestamp is optionally used to associate the call with a specific user
+ * initiated event. If not provided then the value of
+ * global.get_current_time() is assumed.
+ */
+function popModal(grab, timestamp) {
+ if (timestamp == undefined)
+ timestamp = global.get_current_time();
+
+ let focusIndex = _findModal(grab);
+ if (focusIndex < 0) {
+ global.stage.set_key_focus(null);
+ actionMode = Shell.ActionMode.NORMAL;
+
+ throw new Error('incorrect pop');
+ }
+
+ modalCount -= 1;
+
+ let record = modalActorFocusStack[focusIndex];
+ record.actor.disconnect(record.destroyId);
+
+ record.grab.dismiss();
+
+ if (focusIndex == modalActorFocusStack.length - 1) {
+ if (record.prevFocus)
+ record.prevFocus.disconnect(record.prevFocusDestroyId);
+ actionMode = record.actionMode;
+ global.stage.set_key_focus(record.prevFocus);
+ } else {
+ // If we have:
+ // global.stage.set_focus(a);
+ // Main.pushModal(b);
+ // Main.pushModal(c);
+ // Main.pushModal(d);
+ //
+ // then we have the stack:
+ // [{ prevFocus: a, actor: b },
+ // { prevFocus: b, actor: c },
+ // { prevFocus: c, actor: d }]
+ //
+ // When actor c is destroyed/popped, if we only simply remove the
+ // record, then the focus stack will be [a, c], rather than the correct
+ // [a, b]. Shift the focus stack up before removing the record to ensure
+ // that we get the correct result.
+ let t = modalActorFocusStack[modalActorFocusStack.length - 1];
+ if (t.prevFocus)
+ t.prevFocus.disconnect(t.prevFocusDestroyId);
+ // Remove from the middle, shift the focus chain up
+ for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
+ modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
+ modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
+ modalActorFocusStack[i].actionMode = modalActorFocusStack[i - 1].actionMode;
+ }
+ }
+ modalActorFocusStack.splice(focusIndex, 1);
+
+ if (modalCount > 0)
+ return;
+
+ layoutManager.modalEnded();
+ Meta.enable_unredirect_for_display(global.display);
+ actionMode = Shell.ActionMode.NORMAL;
+}
+
+function createLookingGlass() {
+ if (lookingGlass == null)
+ lookingGlass = new LookingGlass.LookingGlass();
+
+ return lookingGlass;
+}
+
+function openRunDialog() {
+ if (runDialog == null)
+ runDialog = new RunDialog.RunDialog();
+
+ runDialog.open();
+}
+
+function openWelcomeDialog() {
+ if (welcomeDialog === null)
+ welcomeDialog = new WelcomeDialog.WelcomeDialog();
+
+ welcomeDialog.open();
+}
+
+/**
+ * activateWindow:
+ * @param {Meta.Window} window: the window to activate
+ * @param {number=} time: current event time
+ * @param {number=} workspaceNum: window's workspace number
+ *
+ * Activates @window, switching to its workspace first if necessary,
+ * and switching out of the overview if it's currently active
+ */
+function activateWindow(window, time, workspaceNum) {
+ let workspaceManager = global.workspace_manager;
+ let activeWorkspaceNum = workspaceManager.get_active_workspace_index();
+ let windowWorkspaceNum = workspaceNum !== undefined ? workspaceNum : window.get_workspace().index();
+
+ if (!time)
+ time = global.get_current_time();
+
+ if (windowWorkspaceNum != activeWorkspaceNum) {
+ let workspace = workspaceManager.get_workspace_by_index(windowWorkspaceNum);
+ workspace.activate_with_focus(window, time);
+ } else {
+ window.activate(time);
+ }
+
+ overview.hide();
+ panel.closeCalendar();
+}
+
+/**
+ * Move @window to the specified monitor and workspace.
+ *
+ * @param {Meta.Window} window - the window to move
+ * @param {number} monitorIndex - the requested monitor
+ * @param {number} workspaceIndex - the requested workspace
+ * @param {bool} append - create workspace if it doesn't exist
+ */
+function moveWindowToMonitorAndWorkspace(window, monitorIndex, workspaceIndex, append = false) {
+ // We need to move the window before changing the workspace, because
+ // the move itself could cause a workspace change if the window enters
+ // the primary monitor
+ if (window.get_monitor() !== monitorIndex) {
+ // Wait for the monitor change to take effect
+ const id = global.display.connect('window-entered-monitor',
+ (dsp, num, w) => {
+ if (w !== window)
+ return;
+ window.change_workspace_by_index(workspaceIndex, append);
+ global.display.disconnect(id);
+ });
+ window.move_to_monitor(monitorIndex);
+ } else {
+ window.change_workspace_by_index(workspaceIndex, append);
+ }
+}
+
+// TODO - replace this timeout with some system to guess when the user might
+// be e.g. just reading the screen and not likely to interact.
+var DEFERRED_TIMEOUT_SECONDS = 20;
+var _deferredWorkData = {};
+// Work scheduled for some point in the future
+var _deferredWorkQueue = [];
+// Work we need to process before the next redraw
+var _beforeRedrawQueue = [];
+// Counter to assign work ids
+var _deferredWorkSequence = 0;
+var _deferredTimeoutId = 0;
+
+function _runDeferredWork(workId) {
+ if (!_deferredWorkData[workId])
+ return;
+ let index = _deferredWorkQueue.indexOf(workId);
+ if (index < 0)
+ return;
+
+ _deferredWorkQueue.splice(index, 1);
+ _deferredWorkData[workId].callback();
+ if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) {
+ GLib.source_remove(_deferredTimeoutId);
+ _deferredTimeoutId = 0;
+ }
+}
+
+function _runAllDeferredWork() {
+ while (_deferredWorkQueue.length > 0)
+ _runDeferredWork(_deferredWorkQueue[0]);
+}
+
+function _runBeforeRedrawQueue() {
+ for (let i = 0; i < _beforeRedrawQueue.length; i++) {
+ let workId = _beforeRedrawQueue[i];
+ _runDeferredWork(workId);
+ }
+ _beforeRedrawQueue = [];
+}
+
+function _queueBeforeRedraw(workId) {
+ _beforeRedrawQueue.push(workId);
+ if (_beforeRedrawQueue.length == 1) {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ _runBeforeRedrawQueue();
+ return false;
+ });
+ }
+}
+
+/**
+ * initializeDeferredWork:
+ * @param {Clutter.Actor} actor: an actor
+ * @param {callback} callback: Function to invoke to perform work
+ *
+ * This function sets up a callback to be invoked when either the
+ * given actor is mapped, or after some period of time when the machine
+ * is idle. This is useful if your actor isn't always visible on the
+ * screen (for example, all actors in the overview), and you don't want
+ * to consume resources updating if the actor isn't actually going to be
+ * displaying to the user.
+ *
+ * Note that queueDeferredWork is called by default immediately on
+ * initialization as well, under the assumption that new actors
+ * will need it.
+ *
+ * @returns {string}: A string work identifier
+ */
+function initializeDeferredWork(actor, callback) {
+ // Turn into a string so we can use as an object property
+ let workId = `${++_deferredWorkSequence}`;
+ _deferredWorkData[workId] = {
+ actor,
+ callback,
+ };
+ actor.connect('notify::mapped', () => {
+ if (!(actor.mapped && _deferredWorkQueue.includes(workId)))
+ return;
+ _queueBeforeRedraw(workId);
+ });
+ actor.connect('destroy', () => {
+ let index = _deferredWorkQueue.indexOf(workId);
+ if (index >= 0)
+ _deferredWorkQueue.splice(index, 1);
+ delete _deferredWorkData[workId];
+ });
+ queueDeferredWork(workId);
+ return workId;
+}
+
+/**
+ * queueDeferredWork:
+ * @param {string} workId: work identifier
+ *
+ * Ensure that the work identified by @workId will be
+ * run on map or timeout. You should call this function
+ * for example when data being displayed by the actor has
+ * changed.
+ */
+function queueDeferredWork(workId) {
+ let data = _deferredWorkData[workId];
+ if (!data) {
+ let message = `Invalid work id ${workId}`;
+ logError(new Error(message), message);
+ return;
+ }
+ if (!_deferredWorkQueue.includes(workId))
+ _deferredWorkQueue.push(workId);
+ if (data.actor.mapped) {
+ _queueBeforeRedraw(workId);
+ } else if (_deferredTimeoutId == 0) {
+ _deferredTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, DEFERRED_TIMEOUT_SECONDS, () => {
+ _runAllDeferredWork();
+ _deferredTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(_deferredTimeoutId, '[gnome-shell] _runAllDeferredWork');
+ }
+}
+
+var RestartMessage = GObject.registerClass(
+class RestartMessage extends ModalDialog.ModalDialog {
+ _init(message) {
+ super._init({
+ shellReactive: true,
+ styleClass: 'restart-message headline',
+ shouldFadeIn: false,
+ destroyOnClose: true,
+ });
+
+ let label = new St.Label({
+ text: message,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this.contentLayout.add_child(label);
+ this.buttonLayout.hide();
+ }
+});
+
+function showRestartMessage(message) {
+ let restartMessage = new RestartMessage(message);
+ restartMessage.open();
+}
+
+var AnimationsSettings = class {
+ constructor() {
+ let backend = global.backend;
+ if (!backend.is_rendering_hardware_accelerated()) {
+ St.Settings.get().inhibit_animations();
+ return;
+ }
+
+ let isXvnc = Shell.util_has_x11_display_extension(
+ global.display, 'VNC-EXTENSION');
+ if (isXvnc) {
+ St.Settings.get().inhibit_animations();
+ return;
+ }
+
+ let remoteAccessController = backend.get_remote_access_controller();
+ if (!remoteAccessController)
+ return;
+
+ this._handles = new Set();
+ remoteAccessController.connect('new-handle',
+ (_, handle) => this._onNewRemoteAccessHandle(handle));
+ }
+
+ _onRemoteAccessHandleStopped(handle) {
+ let settings = St.Settings.get();
+
+ settings.uninhibit_animations();
+ this._handles.delete(handle);
+ }
+
+ _onNewRemoteAccessHandle(handle) {
+ if (!handle.get_disable_animations())
+ return;
+
+ let settings = St.Settings.get();
+
+ settings.inhibit_animations();
+ this._handles.add(handle);
+ handle.connect('stopped', this._onRemoteAccessHandleStopped.bind(this));
+ }
+};
diff --git a/js/ui/messageList.js b/js/ui/messageList.js
new file mode 100644
index 0000000..c910ca7
--- /dev/null
+++ b/js/ui/messageList.js
@@ -0,0 +1,760 @@
+/* exported MessageListSection */
+const {
+ Atk, Clutter, Gio, GLib, GObject, Graphene, Meta, Pango, St,
+} = imports.gi;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+
+const Util = imports.misc.util;
+
+var MESSAGE_ANIMATION_TIME = 100;
+
+var DEFAULT_EXPAND_LINES = 6;
+
+function _fixMarkup(text, allowMarkup) {
+ if (allowMarkup) {
+ // Support &amp;, &quot;, &apos;, &lt; and &gt;, escape all other
+ // occurrences of '&'.
+ let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&amp;');
+
+ // Support <b>, <i>, and <u>, escape anything else
+ // so it displays as raw markup.
+ // Ref: https://developer.gnome.org/notification-spec/#markup
+ _text = _text.replace(/<(?!\/?[biu]>)/g, '&lt;');
+
+ try {
+ Pango.parse_markup(_text, -1, '');
+ return _text;
+ } catch (e) {}
+ }
+
+ // !allowMarkup, or invalid markup
+ return GLib.markup_escape_text(text, -1);
+}
+
+var URLHighlighter = GObject.registerClass(
+class URLHighlighter extends St.Label {
+ _init(text = '', lineWrap, allowMarkup) {
+ super._init({
+ reactive: true,
+ style_class: 'url-highlighter',
+ x_expand: true,
+ x_align: Clutter.ActorAlign.START,
+ });
+ this._linkColor = '#ccccff';
+ this.connect('style-changed', () => {
+ let [hasColor, color] = this.get_theme_node().lookup_color('link-color', false);
+ if (hasColor) {
+ let linkColor = color.to_string().substr(0, 7);
+ if (linkColor != this._linkColor) {
+ this._linkColor = linkColor;
+ this._highlightUrls();
+ }
+ }
+ });
+ this.clutter_text.line_wrap = lineWrap;
+ this.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
+
+ this.setMarkup(text, allowMarkup);
+ }
+
+ vfunc_button_press_event(buttonEvent) {
+ // Don't try to URL highlight when invisible.
+ // The MessageTray doesn't actually hide us, so
+ // we need to check for paint opacities as well.
+ if (!this.visible || this.get_paint_opacity() == 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ // Keep Notification from seeing this and taking
+ // a pointer grab, which would block our button-release-event
+ // handler, if an URL is clicked
+ return this._findUrlAtPos(buttonEvent) != -1;
+ }
+
+ vfunc_button_release_event(buttonEvent) {
+ if (!this.visible || this.get_paint_opacity() == 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ let urlId = this._findUrlAtPos(buttonEvent);
+ if (urlId != -1) {
+ let url = this._urls[urlId].url;
+ if (!url.includes(':'))
+ url = `http://${url}`;
+
+ Gio.app_info_launch_default_for_uri(
+ url, global.create_app_launch_context(0, -1));
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_motion_event(motionEvent) {
+ if (!this.visible || this.get_paint_opacity() == 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ let urlId = this._findUrlAtPos(motionEvent);
+ if (urlId != -1 && !this._cursorChanged) {
+ global.display.set_cursor(Meta.Cursor.POINTING_HAND);
+ this._cursorChanged = true;
+ } else if (urlId == -1) {
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ this._cursorChanged = false;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_leave_event(crossingEvent) {
+ if (!this.visible || this.get_paint_opacity() == 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._cursorChanged) {
+ this._cursorChanged = false;
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ }
+ return super.vfunc_leave_event(crossingEvent);
+ }
+
+ setMarkup(text, allowMarkup) {
+ text = text ? _fixMarkup(text, allowMarkup) : '';
+ this._text = text;
+
+ this.clutter_text.set_markup(text);
+ /* clutter_text.text contain text without markup */
+ this._urls = Util.findUrls(this.clutter_text.text);
+ this._highlightUrls();
+ }
+
+ _highlightUrls() {
+ // text here contain markup
+ let urls = Util.findUrls(this._text);
+ let markup = '';
+ let pos = 0;
+ for (let i = 0; i < urls.length; i++) {
+ let url = urls[i];
+ let str = this._text.substr(pos, url.pos - pos);
+ markup += `${str}<span foreground="${this._linkColor}"><u>${url.url}</u></span>`;
+ pos = url.pos + url.url.length;
+ }
+ markup += this._text.substr(pos);
+ this.clutter_text.set_markup(markup);
+ }
+
+ _findUrlAtPos(event) {
+ let { x, y } = event;
+ [, x, y] = this.transform_stage_point(x, y);
+ let findPos = -1;
+ for (let i = 0; i < this.clutter_text.text.length; i++) {
+ let [, px, py, lineHeight] = this.clutter_text.position_to_coords(i);
+ if (py > y || py + lineHeight < y || x < px)
+ continue;
+ findPos = i;
+ }
+ if (findPos != -1) {
+ for (let i = 0; i < this._urls.length; i++) {
+ if (findPos >= this._urls[i].pos &&
+ this._urls[i].pos + this._urls[i].url.length > findPos)
+ return i;
+ }
+ }
+ return -1;
+ }
+});
+
+var ScaleLayout = GObject.registerClass(
+class ScaleLayout extends Clutter.BinLayout {
+ _init(params) {
+ this._container = null;
+ super._init(params);
+ }
+
+ _connectContainer(container) {
+ if (this._container == container)
+ return;
+
+ this._container?.disconnectObject(this);
+
+ this._container = container;
+
+ if (this._container) {
+ this._container.connectObject(
+ 'notify::scale-x', () => this.layout_changed(),
+ 'notify::scale-y', () => this.layout_changed(), this);
+ }
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ this._connectContainer(container);
+
+ let [min, nat] = super.vfunc_get_preferred_width(container, forHeight);
+ return [
+ Math.floor(min * container.scale_x),
+ Math.floor(nat * container.scale_x),
+ ];
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ this._connectContainer(container);
+
+ let [min, nat] = super.vfunc_get_preferred_height(container, forWidth);
+ return [
+ Math.floor(min * container.scale_y),
+ Math.floor(nat * container.scale_y),
+ ];
+ }
+});
+
+var LabelExpanderLayout = GObject.registerClass({
+ Properties: {
+ 'expansion': GObject.ParamSpec.double('expansion',
+ 'Expansion',
+ 'Expansion of the layout, between 0 (collapsed) ' +
+ 'and 1 (fully expanded',
+ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
+ 0, 1, 0),
+ },
+}, class LabelExpanderLayout extends Clutter.LayoutManager {
+ _init(params) {
+ this._expansion = 0;
+ this._expandLines = DEFAULT_EXPAND_LINES;
+
+ super._init(params);
+ }
+
+ get expansion() {
+ return this._expansion;
+ }
+
+ set expansion(v) {
+ if (v == this._expansion)
+ return;
+ this._expansion = v;
+ this.notify('expansion');
+
+ let visibleIndex = this._expansion > 0 ? 1 : 0;
+ for (let i = 0; this._container && i < this._container.get_n_children(); i++)
+ this._container.get_child_at_index(i).visible = i == visibleIndex;
+
+ this.layout_changed();
+ }
+
+ set expandLines(v) {
+ if (v == this._expandLines)
+ return;
+ this._expandLines = v;
+ if (this._expansion > 0)
+ this.layout_changed();
+ }
+
+ vfunc_set_container(container) {
+ this._container = container;
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ let [min, nat] = [0, 0];
+
+ for (let i = 0; i < container.get_n_children(); i++) {
+ if (i > 1)
+ break; // we support one unexpanded + one expanded child
+
+ let child = container.get_child_at_index(i);
+ let [childMin, childNat] = child.get_preferred_width(forHeight);
+ [min, nat] = [Math.max(min, childMin), Math.max(nat, childNat)];
+ }
+
+ return [min, nat];
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ let [min, nat] = [0, 0];
+
+ let children = container.get_children();
+ if (children[0])
+ [min, nat] = children[0].get_preferred_height(forWidth);
+
+ if (children[1]) {
+ let [min2, nat2] = children[1].get_preferred_height(forWidth);
+ const [expMin, expNat] = [
+ Math.min(min2, min * this._expandLines),
+ Math.min(nat2, nat * this._expandLines),
+ ];
+ [min, nat] = [
+ min + this._expansion * (expMin - min),
+ nat + this._expansion * (expNat - nat),
+ ];
+ }
+
+ return [min, nat];
+ }
+
+ vfunc_allocate(container, box) {
+ for (let i = 0; i < container.get_n_children(); i++) {
+ let child = container.get_child_at_index(i);
+
+ if (child.visible)
+ child.allocate(box);
+ }
+ }
+});
+
+
+var Message = GObject.registerClass({
+ Signals: {
+ 'close': {},
+ 'expanded': {},
+ 'unexpanded': {},
+ },
+}, class Message extends St.Button {
+ _init(title, body) {
+ super._init({
+ style_class: 'message',
+ accessible_role: Atk.Role.NOTIFICATION,
+ can_focus: true,
+ x_expand: true,
+ y_expand: true,
+ });
+
+ this.expanded = false;
+ this._useBodyMarkup = false;
+
+ let vbox = new St.BoxLayout({
+ vertical: true,
+ x_expand: true,
+ });
+ this.set_child(vbox);
+
+ let hbox = new St.BoxLayout();
+ vbox.add_actor(hbox);
+
+ this._actionBin = new St.Widget({
+ layout_manager: new ScaleLayout(),
+ visible: false,
+ });
+ vbox.add_actor(this._actionBin);
+
+ this._iconBin = new St.Bin({
+ style_class: 'message-icon-bin',
+ y_expand: true,
+ y_align: Clutter.ActorAlign.START,
+ visible: false,
+ });
+ hbox.add_actor(this._iconBin);
+
+ const contentBox = new St.BoxLayout({
+ style_class: 'message-content',
+ vertical: true,
+ x_expand: true,
+ });
+ hbox.add_actor(contentBox);
+
+ this._mediaControls = new St.BoxLayout();
+ hbox.add_actor(this._mediaControls);
+
+ let titleBox = new St.BoxLayout();
+ contentBox.add_actor(titleBox);
+
+ this.titleLabel = new St.Label({ style_class: 'message-title' });
+ this.setTitle(title);
+ titleBox.add_actor(this.titleLabel);
+
+ this._secondaryBin = new St.Bin({
+ style_class: 'message-secondary-bin',
+ x_expand: true, y_expand: true,
+ });
+ titleBox.add_actor(this._secondaryBin);
+
+ this._closeButton = new St.Button({
+ style_class: 'message-close-button',
+ icon_name: 'window-close-symbolic',
+ y_align: Clutter.ActorAlign.CENTER,
+ opacity: 0,
+ });
+ titleBox.add_actor(this._closeButton);
+
+ this._bodyStack = new St.Widget({ x_expand: true });
+ this._bodyStack.layout_manager = new LabelExpanderLayout();
+ contentBox.add_actor(this._bodyStack);
+
+ this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup);
+ this.bodyLabel.add_style_class_name('message-body');
+ this._bodyStack.add_actor(this.bodyLabel);
+ this.setBody(body);
+
+ this._closeButton.connect('clicked', this.close.bind(this));
+ let actorHoverId = this.connect('notify::hover', this._sync.bind(this));
+ this._closeButton.connect('destroy', this.disconnect.bind(this, actorHoverId));
+ this.connect('destroy', this._onDestroy.bind(this));
+ this._sync();
+ }
+
+ close() {
+ this.emit('close');
+ }
+
+ setIcon(actor) {
+ this._iconBin.child = actor;
+ this._iconBin.visible = actor != null;
+ }
+
+ setSecondaryActor(actor) {
+ this._secondaryBin.child = actor;
+ }
+
+ setTitle(text) {
+ let title = text ? _fixMarkup(text.replace(/\n/g, ' '), false) : '';
+ this.titleLabel.clutter_text.set_markup(title);
+ }
+
+ setBody(text) {
+ this._bodyText = text;
+ this.bodyLabel.setMarkup(text ? text.replace(/\n/g, ' ') : '',
+ this._useBodyMarkup);
+ if (this._expandedLabel)
+ this._expandedLabel.setMarkup(text, this._useBodyMarkup);
+ }
+
+ setUseBodyMarkup(enable) {
+ if (this._useBodyMarkup === enable)
+ return;
+ this._useBodyMarkup = enable;
+ if (this.bodyLabel)
+ this.setBody(this._bodyText);
+ }
+
+ setActionArea(actor) {
+ if (actor == null) {
+ if (this._actionBin.get_n_children() > 0)
+ this._actionBin.get_child_at_index(0).destroy();
+ return;
+ }
+
+ if (this._actionBin.get_n_children() > 0)
+ throw new Error('Message already has an action area');
+
+ this._actionBin.add_actor(actor);
+ this._actionBin.visible = this.expanded;
+ }
+
+ addMediaControl(iconName, callback) {
+ const button = new St.Button({
+ style_class: 'message-media-control',
+ iconName,
+ });
+ button.connect('clicked', callback);
+ this._mediaControls.add_actor(button);
+ return button;
+ }
+
+ setExpandedBody(actor) {
+ if (actor == null) {
+ if (this._bodyStack.get_n_children() > 1)
+ this._bodyStack.get_child_at_index(1).destroy();
+ return;
+ }
+
+ if (this._bodyStack.get_n_children() > 1)
+ throw new Error('Message already has an expanded body actor');
+
+ this._bodyStack.insert_child_at_index(actor, 1);
+ }
+
+ setExpandedLines(nLines) {
+ this._bodyStack.layout_manager.expandLines = nLines;
+ }
+
+ expand(animate) {
+ this.expanded = true;
+
+ this._actionBin.visible = this._actionBin.get_n_children() > 0;
+
+ if (this._bodyStack.get_n_children() < 2) {
+ this._expandedLabel = new URLHighlighter(this._bodyText,
+ true, this._useBodyMarkup);
+ this.setExpandedBody(this._expandedLabel);
+ }
+
+ if (animate) {
+ this._bodyStack.ease_property('@layout.expansion', 1, {
+ progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: MessageTray.ANIMATION_TIME,
+ });
+
+ this._actionBin.scale_y = 0;
+ this._actionBin.ease({
+ scale_y: 1,
+ duration: MessageTray.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ } else {
+ this._bodyStack.layout_manager.expansion = 1;
+ this._actionBin.scale_y = 1;
+ }
+
+ this.emit('expanded');
+ }
+
+ unexpand(animate) {
+ if (animate) {
+ this._bodyStack.ease_property('@layout.expansion', 0, {
+ progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: MessageTray.ANIMATION_TIME,
+ });
+
+ this._actionBin.ease({
+ scale_y: 0,
+ duration: MessageTray.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._actionBin.hide();
+ this.expanded = false;
+ },
+ });
+ } else {
+ this._bodyStack.layout_manager.expansion = 0;
+ this._actionBin.scale_y = 0;
+ this.expanded = false;
+ }
+
+ this.emit('unexpanded');
+ }
+
+ canClose() {
+ return false;
+ }
+
+ _sync() {
+ let visible = this.hover && this.canClose();
+ this._closeButton.opacity = visible ? 255 : 0;
+ this._closeButton.reactive = visible;
+ }
+
+ _onDestroy() {
+ }
+
+ vfunc_key_press_event(keyEvent) {
+ let keysym = keyEvent.keyval;
+
+ if (keysym === Clutter.KEY_Delete ||
+ keysym === Clutter.KEY_KP_Delete ||
+ keysym === Clutter.KEY_BackSpace) {
+ if (this.canClose()) {
+ this.close();
+ return Clutter.EVENT_STOP;
+ }
+ }
+ return super.vfunc_key_press_event(keyEvent);
+ }
+});
+
+var MessageListSection = GObject.registerClass({
+ Properties: {
+ 'can-clear': GObject.ParamSpec.boolean(
+ 'can-clear', 'can-clear', 'can-clear',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'empty': GObject.ParamSpec.boolean(
+ 'empty', 'empty', 'empty',
+ GObject.ParamFlags.READABLE,
+ true),
+ },
+ Signals: {
+ 'can-clear-changed': {},
+ 'empty-changed': {},
+ 'message-focused': { param_types: [Message.$gtype] },
+ },
+}, class MessageListSection extends St.BoxLayout {
+ _init() {
+ super._init({
+ style_class: 'message-list-section',
+ clip_to_allocation: true,
+ vertical: true,
+ x_expand: true,
+ });
+
+ this._list = new St.BoxLayout({
+ style_class: 'message-list-section-list',
+ vertical: true,
+ });
+ this.add_actor(this._list);
+
+ this._list.connect('actor-added', this._sync.bind(this));
+ this._list.connect('actor-removed', this._sync.bind(this));
+
+ Main.sessionMode.connectObject(
+ 'updated', () => this._sync(), this);
+
+ this._empty = true;
+ this._canClear = false;
+ this._sync();
+ }
+
+ get empty() {
+ return this._empty;
+ }
+
+ get canClear() {
+ return this._canClear;
+ }
+
+ get _messages() {
+ return this._list.get_children().map(i => i.child);
+ }
+
+ _onKeyFocusIn(messageActor) {
+ this.emit('message-focused', messageActor);
+ }
+
+ get allowed() {
+ return true;
+ }
+
+ addMessage(message, animate) {
+ this.addMessageAtIndex(message, -1, animate);
+ }
+
+ addMessageAtIndex(message, index, animate) {
+ if (this._messages.includes(message))
+ throw new Error('Message was already added previously');
+
+ let listItem = new St.Bin({
+ child: message,
+ layout_manager: new ScaleLayout(),
+ pivot_point: new Graphene.Point({ x: .5, y: .5 }),
+ });
+ listItem._connectionsIds = [];
+
+ listItem._connectionsIds.push(message.connect('key-focus-in',
+ this._onKeyFocusIn.bind(this)));
+ listItem._connectionsIds.push(message.connect('close', () => {
+ this.removeMessage(message, true);
+ }));
+ listItem._connectionsIds.push(message.connect('destroy', () => {
+ listItem._connectionsIds.forEach(id => message.disconnect(id));
+ listItem.destroy();
+ }));
+
+ this._list.insert_child_at_index(listItem, index);
+
+ if (animate) {
+ listItem.set({ scale_x: 0, scale_y: 0 });
+ listItem.ease({
+ scale_x: 1,
+ scale_y: 1,
+ duration: MESSAGE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+
+ moveMessage(message, index, animate) {
+ if (!this._messages.includes(message))
+ throw new Error(`Impossible to move untracked message`);
+
+ let listItem = message.get_parent();
+
+ if (!animate) {
+ this._list.set_child_at_index(listItem, index);
+ return;
+ }
+
+ let onComplete = () => {
+ this._list.set_child_at_index(listItem, index);
+ listItem.ease({
+ scale_x: 1,
+ scale_y: 1,
+ duration: MESSAGE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ };
+ listItem.ease({
+ scale_x: 0,
+ scale_y: 0,
+ duration: MESSAGE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete,
+ });
+ }
+
+ removeMessage(message, animate) {
+ const messages = this._messages;
+
+ if (!messages.includes(message))
+ throw new Error(`Impossible to remove untracked message`);
+
+ let listItem = message.get_parent();
+ listItem._connectionsIds.forEach(id => message.disconnect(id));
+
+ let nextMessage = null;
+
+ if (message.has_key_focus()) {
+ const index = messages.indexOf(message);
+ nextMessage =
+ messages[index + 1] ||
+ messages[index - 1] ||
+ this._list;
+ }
+
+ if (animate) {
+ listItem.ease({
+ scale_x: 0,
+ scale_y: 0,
+ duration: MESSAGE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ listItem.destroy();
+ nextMessage?.grab_key_focus();
+ },
+ });
+ } else {
+ listItem.destroy();
+ nextMessage?.grab_key_focus();
+ }
+ }
+
+ clear() {
+ let messages = this._messages.filter(msg => msg.canClose());
+
+ // If there are few messages, letting them all zoom out looks OK
+ if (messages.length < 2) {
+ messages.forEach(message => {
+ message.close();
+ });
+ } else {
+ // Otherwise we slide them out one by one, and then zoom them
+ // out "off-screen" in the end to smoothly shrink the parent
+ let delay = MESSAGE_ANIMATION_TIME / Math.max(messages.length, 5);
+ for (let i = 0; i < messages.length; i++) {
+ let message = messages[i];
+ message.get_parent().ease({
+ translation_x: this._list.width,
+ opacity: 0,
+ duration: MESSAGE_ANIMATION_TIME,
+ delay: i * delay,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => message.close(),
+ });
+ }
+ }
+ }
+
+ _shouldShow() {
+ return !this.empty;
+ }
+
+ _sync() {
+ let messages = this._messages;
+ let empty = messages.length == 0;
+
+ if (this._empty != empty) {
+ this._empty = empty;
+ this.notify('empty');
+ }
+
+ let canClear = messages.some(m => m.canClose());
+ if (this._canClear != canClear) {
+ this._canClear = canClear;
+ this.notify('can-clear');
+ }
+
+ this.visible = this.allowed && this._shouldShow();
+ }
+});
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
new file mode 100644
index 0000000..1edd932
--- /dev/null
+++ b/js/ui/messageTray.js
@@ -0,0 +1,1423 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported NotificationPolicy, NotificationGenericPolicy,
+ NotificationApplicationPolicy, Source, SourceActor,
+ SystemNotificationSource, MessageTray */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const Calendar = imports.ui.calendar;
+const GnomeSession = imports.misc.gnomeSession;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+const SignalTracker = imports.misc.signalTracker;
+
+const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
+
+var ANIMATION_TIME = 200;
+var NOTIFICATION_TIMEOUT = 4000;
+
+var HIDE_TIMEOUT = 200;
+var LONGER_HIDE_TIMEOUT = 600;
+
+var MAX_NOTIFICATIONS_IN_QUEUE = 3;
+var MAX_NOTIFICATIONS_PER_SOURCE = 3;
+var MAX_NOTIFICATION_BUTTONS = 3;
+
+// We delay hiding of the tray if the mouse is within MOUSE_LEFT_ACTOR_THRESHOLD
+// range from the point where it left the tray.
+var MOUSE_LEFT_ACTOR_THRESHOLD = 20;
+
+var IDLE_TIME = 1000;
+
+var State = {
+ HIDDEN: 0,
+ SHOWING: 1,
+ SHOWN: 2,
+ HIDING: 3,
+};
+
+// These reasons are useful when we destroy the notifications received through
+// the notification daemon. We use EXPIRED for notifications that we dismiss
+// and the user did not interact with, DISMISSED for all other notifications
+// that were destroyed as a result of a user action, SOURCE_CLOSED for the
+// notifications that were requested to be destroyed by the associated source,
+// and REPLACED for notifications that were destroyed as a consequence of a
+// newer version having replaced them.
+var NotificationDestroyedReason = {
+ EXPIRED: 1,
+ DISMISSED: 2,
+ SOURCE_CLOSED: 3,
+ REPLACED: 4,
+};
+
+// Message tray has its custom Urgency enumeration. LOW, NORMAL and CRITICAL
+// urgency values map to the corresponding values for the notifications received
+// through the notification daemon. HIGH urgency value is used for chats received
+// through the Telepathy client.
+var Urgency = {
+ LOW: 0,
+ NORMAL: 1,
+ HIGH: 2,
+ CRITICAL: 3,
+};
+
+// The privacy of the details of a notification. USER is for notifications which
+// contain private information to the originating user account (for example,
+// details of an e-mail they’ve received). SYSTEM is for notifications which
+// contain information private to the physical system (for example, battery
+// status) and hence the same for every user. This affects whether the content
+// of a notification is shown on the lock screen.
+var PrivacyScope = {
+ USER: 0,
+ SYSTEM: 1,
+};
+
+var FocusGrabber = class FocusGrabber {
+ constructor(actor) {
+ this._actor = actor;
+ this._prevKeyFocusActor = null;
+ this._focused = false;
+ }
+
+ grabFocus() {
+ if (this._focused)
+ return;
+
+ this._prevKeyFocusActor = global.stage.get_key_focus();
+
+ global.stage.connectObject('notify::key-focus',
+ this._focusActorChanged.bind(this), this);
+
+ if (!this._actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false))
+ this._actor.grab_key_focus();
+
+ this._focused = true;
+ }
+
+ _focusUngrabbed() {
+ if (!this._focused)
+ return false;
+
+ global.stage.disconnectObject(this);
+
+ this._focused = false;
+ return true;
+ }
+
+ _focusActorChanged() {
+ let focusedActor = global.stage.get_key_focus();
+ if (!focusedActor || !this._actor.contains(focusedActor))
+ this._focusUngrabbed();
+ }
+
+ ungrabFocus() {
+ if (!this._focusUngrabbed())
+ return;
+
+ if (this._prevKeyFocusActor) {
+ global.stage.set_key_focus(this._prevKeyFocusActor);
+ this._prevKeyFocusActor = null;
+ } else {
+ let focusedActor = global.stage.get_key_focus();
+ if (focusedActor && this._actor.contains(focusedActor))
+ global.stage.set_key_focus(null);
+ }
+ }
+};
+
+// NotificationPolicy:
+// An object that holds all bits of configurable policy related to a notification
+// source, such as whether to play sound or honour the critical bit.
+//
+// A notification without a policy object will inherit the default one.
+var NotificationPolicy = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+ Properties: {
+ 'enable': GObject.ParamSpec.boolean(
+ 'enable', 'enable', 'enable', GObject.ParamFlags.READABLE, true),
+ 'enable-sound': GObject.ParamSpec.boolean(
+ 'enable-sound', 'enable-sound', 'enable-sound',
+ GObject.ParamFlags.READABLE, true),
+ 'show-banners': GObject.ParamSpec.boolean(
+ 'show-banners', 'show-banners', 'show-banners',
+ GObject.ParamFlags.READABLE, true),
+ 'force-expanded': GObject.ParamSpec.boolean(
+ 'force-expanded', 'force-expanded', 'force-expanded',
+ GObject.ParamFlags.READABLE, false),
+ 'show-in-lock-screen': GObject.ParamSpec.boolean(
+ 'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen',
+ GObject.ParamFlags.READABLE, false),
+ 'details-in-lock-screen': GObject.ParamSpec.boolean(
+ 'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen',
+ GObject.ParamFlags.READABLE, false),
+ },
+}, class NotificationPolicy extends GObject.Object {
+ // Do nothing for the default policy. These methods are only useful for the
+ // GSettings policy.
+ store() { }
+
+ destroy() {
+ this.run_dispose();
+ }
+
+ get enable() {
+ return true;
+ }
+
+ get enableSound() {
+ return true;
+ }
+
+ get showBanners() {
+ return true;
+ }
+
+ get forceExpanded() {
+ return false;
+ }
+
+ get showInLockScreen() {
+ return false;
+ }
+
+ get detailsInLockScreen() {
+ return false;
+ }
+});
+
+var NotificationGenericPolicy = GObject.registerClass({
+}, class NotificationGenericPolicy extends NotificationPolicy {
+ _init() {
+ super._init();
+ this.id = 'generic';
+
+ this._masterSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications' });
+ this._masterSettings.connect('changed', this._changed.bind(this));
+ }
+
+ destroy() {
+ this._masterSettings.run_dispose();
+
+ super.destroy();
+ }
+
+ _changed(settings, key) {
+ if (this.constructor.find_property(key))
+ this.notify(key);
+ }
+
+ get showBanners() {
+ return this._masterSettings.get_boolean('show-banners');
+ }
+
+ get showInLockScreen() {
+ return this._masterSettings.get_boolean('show-in-lock-screen');
+ }
+});
+
+var NotificationApplicationPolicy = GObject.registerClass({
+}, class NotificationApplicationPolicy extends NotificationPolicy {
+ _init(id) {
+ super._init();
+
+ this.id = id;
+ this._canonicalId = this._canonicalizeId(id);
+
+ this._masterSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications' });
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.notifications.application',
+ path: `/org/gnome/desktop/notifications/application/${this._canonicalId}/`,
+ });
+
+ this._masterSettings.connect('changed', this._changed.bind(this));
+ this._settings.connect('changed', this._changed.bind(this));
+ }
+
+ store() {
+ this._settings.set_string('application-id', `${this.id}.desktop`);
+
+ let apps = this._masterSettings.get_strv('application-children');
+ if (!apps.includes(this._canonicalId)) {
+ apps.push(this._canonicalId);
+ this._masterSettings.set_strv('application-children', apps);
+ }
+ }
+
+ destroy() {
+ this._masterSettings.run_dispose();
+ this._settings.run_dispose();
+
+ super.destroy();
+ }
+
+ _changed(settings, key) {
+ if (this.constructor.find_property(key))
+ this.notify(key);
+ }
+
+ _canonicalizeId(id) {
+ // Keys are restricted to lowercase alphanumeric characters and dash,
+ // and two dashes cannot be in succession
+ return id.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/--+/g, '-');
+ }
+
+ get enable() {
+ return this._settings.get_boolean('enable');
+ }
+
+ get enableSound() {
+ return this._settings.get_boolean('enable-sound-alerts');
+ }
+
+ get showBanners() {
+ return this._masterSettings.get_boolean('show-banners') &&
+ this._settings.get_boolean('show-banners');
+ }
+
+ get forceExpanded() {
+ return this._settings.get_boolean('force-expanded');
+ }
+
+ get showInLockScreen() {
+ return this._masterSettings.get_boolean('show-in-lock-screen') &&
+ this._settings.get_boolean('show-in-lock-screen');
+ }
+
+ get detailsInLockScreen() {
+ return this._settings.get_boolean('details-in-lock-screen');
+ }
+});
+
+// Notification:
+// @source: the notification's Source
+// @title: the title
+// @banner: the banner text
+// @params: optional additional params
+//
+// Creates a notification. In the banner mode, the notification
+// will show an icon, @title (in bold) and @banner, all on a single
+// line (with @banner ellipsized if necessary).
+//
+// The notification will be expandable if either it has additional
+// elements that were added to it or if the @banner text did not
+// fit fully in the banner mode. When the notification is expanded,
+// the @banner text from the top line is always removed. The complete
+// @banner text is added as the first element in the content section,
+// unless 'customContent' parameter with the value 'true' is specified
+// in @params.
+//
+// Additional notification content can be added with addActor() and
+// addBody() methods. The notification content is put inside a
+// scrollview, so if it gets too tall, the notification will scroll
+// rather than continue to grow. In addition to this main content
+// area, there is also a single-row action area, which is not
+// scrolled and can contain a single actor. The action area can
+// be set by calling setActionArea() method. There is also a
+// convenience method addButton() for adding a button to the action
+// area.
+//
+// If @params contains a 'customContent' parameter with the value %true,
+// then @banner will not be shown in the body of the notification when the
+// notification is expanded and calls to update() will not clear the content
+// unless 'clear' parameter with value %true is explicitly specified.
+//
+// By default, the icon shown is the same as the source's.
+// However, if @params contains a 'gicon' parameter, the passed in gicon
+// will be used.
+//
+// You can add a secondary icon to the banner with 'secondaryGIcon'. There
+// is no fallback for this icon.
+//
+// If @params contains 'bannerMarkup', with the value %true, a subset (<b>,
+// <i> and <u>) of the markup in [1] will be interpreted within @banner. If
+// the parameter is not present, then anything that looks like markup
+// in @banner will appear literally in the output.
+//
+// If @params contains a 'clear' parameter with the value %true, then
+// the content and the action area of the notification will be cleared.
+// The content area is also always cleared if 'customContent' is false
+// because it might contain the @banner that didn't fit in the banner mode.
+//
+// If @params contains 'soundName' or 'soundFile', the corresponding
+// event sound is played when the notification is shown (if the policy for
+// @source allows playing sounds).
+//
+// [1] https://developer.gnome.org/notification-spec/#markup
+var Notification = GObject.registerClass({
+ Properties: {
+ 'acknowledged': GObject.ParamSpec.boolean(
+ 'acknowledged', 'acknowledged', 'acknowledged',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+ Signals: {
+ 'activated': {},
+ 'destroy': { param_types: [GObject.TYPE_UINT] },
+ 'updated': { param_types: [GObject.TYPE_BOOLEAN] },
+ },
+}, class Notification extends GObject.Object {
+ _init(source, title, banner, params) {
+ super._init();
+
+ this.source = source;
+ this.title = title;
+ this.urgency = Urgency.NORMAL;
+ // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
+ this.isTransient = false;
+ this.privacyScope = PrivacyScope.USER;
+ this.forFeedback = false;
+ this.bannerBodyText = null;
+ this.bannerBodyMarkup = false;
+ this._soundName = null;
+ this._soundFile = null;
+ this._soundPlayed = false;
+ this.actions = [];
+ this.setResident(false);
+
+ // If called with only one argument we assume the caller
+ // will call .update() later on. This is the case of
+ // NotificationDaemon, which wants to use the same code
+ // for new and updated notifications
+ if (arguments.length != 1)
+ this.update(title, banner, params);
+ }
+
+ // update:
+ // @title: the new title
+ // @banner: the new banner
+ // @params: as in the Notification constructor
+ //
+ // Updates the notification by regenerating its icon and updating
+ // the title/banner. If @params.clear is %true, it will also
+ // remove any additional actors/action buttons previously added.
+ update(title, banner, params) {
+ params = Params.parse(params, {
+ gicon: null,
+ secondaryGIcon: null,
+ bannerMarkup: false,
+ clear: false,
+ datetime: null,
+ soundName: null,
+ soundFile: null,
+ });
+
+ this.title = title;
+ this.bannerBodyText = banner;
+ this.bannerBodyMarkup = params.bannerMarkup;
+
+ if (params.datetime)
+ this.datetime = params.datetime;
+ else
+ this.datetime = GLib.DateTime.new_now_local();
+
+ if (params.gicon || params.clear)
+ this.gicon = params.gicon;
+
+ if (params.secondaryGIcon || params.clear)
+ this.secondaryGIcon = params.secondaryGIcon;
+
+ if (params.clear)
+ this.actions = [];
+
+ if (this._soundName != params.soundName ||
+ this._soundFile != params.soundFile) {
+ this._soundName = params.soundName;
+ this._soundFile = params.soundFile;
+ this._soundPlayed = false;
+ }
+
+ this.emit('updated', params.clear);
+ }
+
+ // addAction:
+ // @label: the label for the action's button
+ // @callback: the callback for the action
+ addAction(label, callback) {
+ this.actions.push({ label, callback });
+ }
+
+ setUrgency(urgency) {
+ this.urgency = urgency;
+ }
+
+ setResident(resident) {
+ this.resident = resident;
+ }
+
+ setTransient(isTransient) {
+ this.isTransient = isTransient;
+ }
+
+ setForFeedback(forFeedback) {
+ this.forFeedback = forFeedback;
+ }
+
+ setPrivacyScope(privacyScope) {
+ this.privacyScope = privacyScope;
+ }
+
+ playSound() {
+ if (this._soundPlayed)
+ return;
+
+ if (!this.source.policy.enableSound) {
+ this._soundPlayed = true;
+ return;
+ }
+
+ let player = global.display.get_sound_player();
+ if (this._soundName)
+ player.play_from_theme(this._soundName, this.title, null);
+ else if (this._soundFile)
+ player.play_from_file(this._soundFile, this.title, null);
+ }
+
+ // Allow customizing the banner UI:
+ // the default implementation defers the creation to
+ // the source (which will create a NotificationBanner),
+ // so customization can be done by subclassing either
+ // Notification or Source
+ createBanner() {
+ return this.source.createBanner(this);
+ }
+
+ activate() {
+ this.emit('activated');
+
+ if (!this.resident)
+ this.destroy();
+ }
+
+ destroy(reason = NotificationDestroyedReason.DISMISSED) {
+ this.emit('destroy', reason);
+ this.run_dispose();
+ }
+});
+SignalTracker.registerDestroyableType(Notification);
+
+var NotificationBanner = GObject.registerClass({
+ Signals: {
+ 'done-displaying': {},
+ 'unfocused': {},
+ },
+}, class NotificationBanner extends Calendar.NotificationMessage {
+ _init(notification) {
+ super._init(notification);
+
+ this.can_focus = false;
+ this.add_style_class_name('notification-banner');
+
+ this._buttonBox = null;
+
+ this._addActions();
+ this._addSecondaryIcon();
+
+ this.notification.connectObject('activated', () => {
+ // We hide all types of notifications once the user clicks on
+ // them because the common outcome of clicking should be the
+ // relevant window being brought forward and the user's
+ // attention switching to the window.
+ this.emit('done-displaying');
+ }, this);
+ }
+
+ _onUpdated(n, clear) {
+ super._onUpdated(n, clear);
+
+ if (clear) {
+ this.setSecondaryActor(null);
+ this.setActionArea(null);
+ this._buttonBox = null;
+ }
+
+ this._addActions();
+ this._addSecondaryIcon();
+ }
+
+ _addActions() {
+ this.notification.actions.forEach(action => {
+ this.addAction(action.label, action.callback);
+ });
+ }
+
+ _addSecondaryIcon() {
+ if (this.notification.secondaryGIcon) {
+ const icon = new St.Icon({
+ gicon: this.notification.secondaryGIcon,
+ x_align: Clutter.ActorAlign.END,
+ });
+ this.setSecondaryActor(icon);
+ }
+ }
+
+ addButton(button, callback) {
+ if (!this._buttonBox) {
+ this._buttonBox = new St.BoxLayout({
+ style_class: 'notification-actions',
+ x_expand: true,
+ });
+ this.setActionArea(this._buttonBox);
+ global.focus_manager.add_group(this._buttonBox);
+ }
+
+ if (this._buttonBox.get_n_children() >= MAX_NOTIFICATION_BUTTONS)
+ return null;
+
+ this._buttonBox.add(button);
+ button.connect('clicked', () => {
+ callback();
+
+ if (!this.notification.resident) {
+ // We don't hide a resident notification when the user invokes one of its actions,
+ // because it is common for such notifications to update themselves with new
+ // information based on the action. We'd like to display the updated information
+ // in place, rather than pop-up a new notification.
+ this.emit('done-displaying');
+ this.notification.destroy();
+ }
+ });
+
+ return button;
+ }
+
+ addAction(label, callback) {
+ const button = new St.Button({
+ style_class: 'notification-button',
+ label,
+ x_expand: true,
+ can_focus: true,
+ });
+
+ return this.addButton(button, callback);
+ }
+});
+
+var SourceActor = GObject.registerClass(
+class SourceActor extends St.Widget {
+ _init(source, size) {
+ super._init();
+
+ this._source = source;
+ this._size = size;
+
+ this.connect('destroy',
+ () => (this._actorDestroyed = true));
+ this._actorDestroyed = false;
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ this._iconBin = new St.Bin({
+ x_expand: true,
+ height: size * scaleFactor,
+ width: size * scaleFactor,
+ });
+
+ this.add_actor(this._iconBin);
+
+ this._source.connectObject('icon-updated',
+ this._updateIcon.bind(this), this);
+ this._updateIcon();
+ }
+
+ setIcon(icon) {
+ this._iconBin.child = icon;
+ this._iconSet = true;
+ }
+
+ _updateIcon() {
+ if (this._actorDestroyed)
+ return;
+
+ if (!this._iconSet)
+ this._iconBin.child = this._source.createIcon(this._size);
+ }
+});
+
+var Source = GObject.registerClass({
+ Properties: {
+ 'count': GObject.ParamSpec.int(
+ 'count', 'count', 'count',
+ GObject.ParamFlags.READABLE,
+ 0, GLib.MAXINT32, 0),
+ 'policy': GObject.ParamSpec.object(
+ 'policy', 'policy', 'policy',
+ GObject.ParamFlags.READWRITE,
+ NotificationPolicy.$gtype),
+ 'title': GObject.ParamSpec.string(
+ 'title', 'title', 'title',
+ GObject.ParamFlags.READWRITE,
+ null),
+ },
+ Signals: {
+ 'destroy': { param_types: [GObject.TYPE_UINT] },
+ 'icon-updated': {},
+ 'notification-added': { param_types: [Notification.$gtype] },
+ 'notification-show': { param_types: [Notification.$gtype] },
+ },
+}, class Source extends GObject.Object {
+ _init(title, iconName) {
+ super._init({ title });
+
+ this.SOURCE_ICON_SIZE = 48;
+
+ this.iconName = iconName;
+
+ this.isChat = false;
+
+ this.notifications = [];
+
+ this._policy = this._createPolicy();
+ }
+
+ get policy() {
+ return this._policy;
+ }
+
+ set policy(policy) {
+ if (this._policy)
+ this._policy.destroy();
+ this._policy = policy;
+ }
+
+ get count() {
+ return this.notifications.length;
+ }
+
+ get unseenCount() {
+ return this.notifications.filter(n => !n.acknowledged).length;
+ }
+
+ get countVisible() {
+ return this.count > 1;
+ }
+
+ countUpdated() {
+ this.notify('count');
+ }
+
+ _createPolicy() {
+ return new NotificationGenericPolicy();
+ }
+
+ get narrowestPrivacyScope() {
+ return this.notifications.every(n => n.privacyScope == PrivacyScope.SYSTEM)
+ ? PrivacyScope.SYSTEM
+ : PrivacyScope.USER;
+ }
+
+ setTitle(newTitle) {
+ if (this.title == newTitle)
+ return;
+
+ this.title = newTitle;
+ this.notify('title');
+ }
+
+ createBanner(notification) {
+ return new NotificationBanner(notification);
+ }
+
+ // Called to create a new icon actor.
+ // Provides a sane default implementation, override if you need
+ // something more fancy.
+ createIcon(size) {
+ return new St.Icon({
+ gicon: this.getIcon(),
+ icon_size: size,
+ });
+ }
+
+ getIcon() {
+ return new Gio.ThemedIcon({ name: this.iconName });
+ }
+
+ _onNotificationDestroy(notification) {
+ let index = this.notifications.indexOf(notification);
+ if (index < 0)
+ return;
+
+ this.notifications.splice(index, 1);
+ this.countUpdated();
+
+ if (this.notifications.length == 0)
+ this.destroy();
+ }
+
+ pushNotification(notification) {
+ if (this.notifications.includes(notification))
+ return;
+
+ while (this.notifications.length >= MAX_NOTIFICATIONS_PER_SOURCE)
+ this.notifications.shift().destroy(NotificationDestroyedReason.EXPIRED);
+
+ notification.connect('destroy', this._onNotificationDestroy.bind(this));
+ notification.connect('notify::acknowledged', this.countUpdated.bind(this));
+ this.notifications.push(notification);
+ this.emit('notification-added', notification);
+
+ this.countUpdated();
+ }
+
+ showNotification(notification) {
+ notification.acknowledged = false;
+ this.pushNotification(notification);
+
+ if (notification.urgency === Urgency.LOW)
+ return;
+
+ if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL)
+ this.emit('notification-show', notification);
+ }
+
+ destroy(reason) {
+ let notifications = this.notifications;
+ this.notifications = [];
+
+ for (let i = 0; i < notifications.length; i++)
+ notifications[i].destroy(reason);
+
+ this.emit('destroy', reason);
+
+ this.policy.destroy();
+ this.run_dispose();
+ }
+
+ iconUpdated() {
+ this.emit('icon-updated');
+ }
+
+ // To be overridden by subclasses
+ open() {
+ }
+
+ destroyNonResidentNotifications() {
+ for (let i = this.notifications.length - 1; i >= 0; i--) {
+ if (!this.notifications[i].resident)
+ this.notifications[i].destroy();
+ }
+ }
+});
+SignalTracker.registerDestroyableType(Source);
+
+var MessageTray = GObject.registerClass({
+ Signals: {
+ 'queue-changed': {},
+ 'source-added': { param_types: [Source.$gtype] },
+ 'source-removed': { param_types: [Source.$gtype] },
+ },
+}, class MessageTray extends St.Widget {
+ _init() {
+ super._init({
+ visible: false,
+ clip_to_allocation: true,
+ layout_manager: new Clutter.BinLayout(),
+ });
+
+ this._presence = new GnomeSession.Presence((proxy, _error) => {
+ this._onStatusChanged(proxy.status);
+ });
+ this._busy = false;
+ this._bannerBlocked = false;
+ this._presence.connectSignal('StatusChanged', (proxy, senderName, [status]) => {
+ this._onStatusChanged(status);
+ });
+
+ let constraint = new Layout.MonitorConstraint({ primary: true });
+ Main.layoutManager.panelBox.bind_property('visible',
+ constraint, 'work-area',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.add_constraint(constraint);
+
+ this._bannerBin = new St.Widget({
+ name: 'notification-container',
+ reactive: true,
+ track_hover: true,
+ y_align: Clutter.ActorAlign.START,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_expand: true,
+ x_expand: true,
+ layout_manager: new Clutter.BinLayout(),
+ });
+ this._bannerBin.connect('key-release-event',
+ this._onNotificationKeyRelease.bind(this));
+ this._bannerBin.connect('notify::hover',
+ this._onNotificationHoverChanged.bind(this));
+ this.add_actor(this._bannerBin);
+
+ this._notificationFocusGrabber = new FocusGrabber(this._bannerBin);
+ this._notificationQueue = [];
+ this._notification = null;
+ this._banner = null;
+
+ this._userActiveWhileNotificationShown = false;
+
+ this.idleMonitor = global.backend.get_core_idle_monitor();
+
+ this._useLongerNotificationLeftTimeout = false;
+
+ // pointerInNotification is sort of a misnomer -- it tracks whether
+ // a message tray notification should expand. The value is
+ // partially driven by the hover state of the notification, but has
+ // a lot of complex state related to timeouts and the current
+ // state of the pointer when a notification pops up.
+ this._pointerInNotification = false;
+
+ // This tracks this._bannerBin.hover and is used to fizzle
+ // out non-changing hover notifications in onNotificationHoverChanged.
+ this._notificationHovered = false;
+
+ this._notificationState = State.HIDDEN;
+ this._notificationTimeoutId = 0;
+ this._notificationRemoved = false;
+
+ Main.layoutManager.addChrome(this, { affectsInputRegion: false });
+ Main.layoutManager.trackChrome(this._bannerBin, { affectsInputRegion: true });
+
+ global.display.connect('in-fullscreen-changed', this._updateState.bind(this));
+
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+
+ Main.overview.connect('window-drag-begin',
+ this._onDragBegin.bind(this));
+ Main.overview.connect('window-drag-cancelled',
+ this._onDragEnd.bind(this));
+ Main.overview.connect('window-drag-end',
+ this._onDragEnd.bind(this));
+
+ Main.overview.connect('item-drag-begin',
+ this._onDragBegin.bind(this));
+ Main.overview.connect('item-drag-cancelled',
+ this._onDragEnd.bind(this));
+ Main.overview.connect('item-drag-end',
+ this._onDragEnd.bind(this));
+
+ Main.xdndHandler.connect('drag-begin',
+ this._onDragBegin.bind(this));
+ Main.xdndHandler.connect('drag-end',
+ this._onDragEnd.bind(this));
+
+ Main.wm.addKeybinding('focus-active-notification',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.NONE,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._expandActiveNotification.bind(this));
+
+ this._sources = new Set();
+
+ this._sessionUpdated();
+ }
+
+ _sessionUpdated() {
+ this._updateState();
+ }
+
+ _onDragBegin() {
+ Shell.util_set_hidden_from_pick(this, true);
+ }
+
+ _onDragEnd() {
+ Shell.util_set_hidden_from_pick(this, false);
+ }
+
+ get bannerAlignment() {
+ return this._bannerBin.get_x_align();
+ }
+
+ set bannerAlignment(align) {
+ this._bannerBin.set_x_align(align);
+ }
+
+ _onNotificationKeyRelease(actor, event) {
+ if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
+ this._expireNotification();
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _expireNotification() {
+ this._notificationExpired = true;
+ this._updateState();
+ }
+
+ get queueCount() {
+ return this._notificationQueue.length;
+ }
+
+ set bannerBlocked(v) {
+ if (this._bannerBlocked == v)
+ return;
+ this._bannerBlocked = v;
+ this._updateState();
+ }
+
+ contains(source) {
+ return this._sources.has(source);
+ }
+
+ add(source) {
+ if (this.contains(source)) {
+ log(`Trying to re-add source ${source.title}`);
+ return;
+ }
+
+ // Register that we got a notification for this source
+ source.policy.store();
+
+ source.policy.connect('notify::enable', () => {
+ this._onSourceEnableChanged(source.policy, source);
+ });
+ source.policy.connect('notify', this._updateState.bind(this));
+ this._onSourceEnableChanged(source.policy, source);
+ }
+
+ _addSource(source) {
+ this._sources.add(source);
+
+ source.connectObject(
+ 'notification-show', this._onNotificationShow.bind(this),
+ 'destroy', () => this._removeSource(source), this);
+
+ this.emit('source-added', source);
+ }
+
+ _removeSource(source) {
+ this._sources.delete(source);
+ source.disconnectObject(this);
+ this.emit('source-removed', source);
+ }
+
+ getSources() {
+ return [...this._sources.keys()];
+ }
+
+ _onSourceEnableChanged(policy, source) {
+ let wasEnabled = this.contains(source);
+ let shouldBeEnabled = policy.enable;
+
+ if (wasEnabled != shouldBeEnabled) {
+ if (shouldBeEnabled)
+ this._addSource(source);
+ else
+ this._removeSource(source);
+ }
+ }
+
+ _onNotificationDestroy(notification) {
+ if (this._notification === notification) {
+ this._notificationRemoved = true;
+ if (this._notificationState === State.SHOWN ||
+ this._notificationState === State.SHOWING) {
+ this._updateNotificationTimeout(0);
+ this._updateState();
+ }
+ } else {
+ const index = this._notificationQueue.indexOf(notification);
+ if (index !== -1) {
+ this._notificationQueue.splice(index, 1);
+ this.emit('queue-changed');
+ }
+ }
+ }
+
+ _onNotificationShow(_source, notification) {
+ if (this._notification == notification) {
+ // If a notification that is being shown is updated, we update
+ // how it is shown and extend the time until it auto-hides.
+ // If a new notification is updated while it is being hidden,
+ // we stop hiding it and show it again.
+ this._updateShowingNotification();
+ } else if (!this._notificationQueue.includes(notification)) {
+ // If the queue is "full", we skip banner mode and just show a small
+ // indicator in the panel; however do make an exception for CRITICAL
+ // notifications, as only banner mode allows expansion.
+ let bannerCount = this._notification ? 1 : 0;
+ let full = this.queueCount + bannerCount >= MAX_NOTIFICATIONS_IN_QUEUE;
+ if (!full || notification.urgency == Urgency.CRITICAL) {
+ notification.connect('destroy',
+ this._onNotificationDestroy.bind(this));
+ this._notificationQueue.push(notification);
+ this._notificationQueue.sort(
+ (n1, n2) => n2.urgency - n1.urgency);
+ this.emit('queue-changed');
+ }
+ }
+ this._updateState();
+ }
+
+ _resetNotificationLeftTimeout() {
+ this._useLongerNotificationLeftTimeout = false;
+ if (this._notificationLeftTimeoutId) {
+ GLib.source_remove(this._notificationLeftTimeoutId);
+ this._notificationLeftTimeoutId = 0;
+ this._notificationLeftMouseX = -1;
+ this._notificationLeftMouseY = -1;
+ }
+ }
+
+ _onNotificationHoverChanged() {
+ if (this._bannerBin.hover == this._notificationHovered)
+ return;
+
+ this._notificationHovered = this._bannerBin.hover;
+ if (this._notificationHovered) {
+ this._resetNotificationLeftTimeout();
+
+ if (this._showNotificationMouseX >= 0) {
+ let actorAtShowNotificationPosition =
+ global.stage.get_actor_at_pos(Clutter.PickMode.ALL, this._showNotificationMouseX, this._showNotificationMouseY);
+ this._showNotificationMouseX = -1;
+ this._showNotificationMouseY = -1;
+ // Don't set this._pointerInNotification to true if the pointer was initially in the area where the notification
+ // popped up. That way we will not be expanding notifications that happen to pop up over the pointer
+ // automatically. Instead, the user is able to expand the notification by mousing away from it and then
+ // mousing back in. Because this is an expected action, we set the boolean flag that indicates that a longer
+ // timeout should be used before popping down the notification.
+ if (this._bannerBin.contains(actorAtShowNotificationPosition)) {
+ this._useLongerNotificationLeftTimeout = true;
+ return;
+ }
+ }
+
+ this._pointerInNotification = true;
+ this._updateState();
+ } else {
+ // We record the position of the mouse the moment it leaves the tray. These coordinates are used in
+ // this._onNotificationLeftTimeout() to determine if the mouse has moved far enough during the initial timeout for us
+ // to consider that the user intended to leave the tray and therefore hide the tray. If the mouse is still
+ // close to its previous position, we extend the timeout once.
+ let [x, y] = global.get_pointer();
+ this._notificationLeftMouseX = x;
+ this._notificationLeftMouseY = y;
+
+ // We wait just a little before hiding the message tray in case the user quickly moves the mouse back into it.
+ // We wait for a longer period if the notification popped up where the mouse pointer was already positioned.
+ // That gives the user more time to mouse away from the notification and mouse back in in order to expand it.
+ let timeout = this._useLongerNotificationLeftTimeout ? LONGER_HIDE_TIMEOUT : HIDE_TIMEOUT;
+ this._notificationLeftTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout, this._onNotificationLeftTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._notificationLeftTimeoutId, '[gnome-shell] this._onNotificationLeftTimeout');
+ }
+ }
+
+ _onStatusChanged(status) {
+ if (status == GnomeSession.PresenceStatus.BUSY) {
+ // remove notification and allow the summary to be closed now
+ this._updateNotificationTimeout(0);
+ this._busy = true;
+ } else if (status != GnomeSession.PresenceStatus.IDLE) {
+ // We preserve the previous value of this._busy if the status turns to IDLE
+ // so that we don't start showing notifications queued during the BUSY state
+ // as the screensaver gets activated.
+ this._busy = false;
+ }
+
+ this._updateState();
+ }
+
+ _onNotificationLeftTimeout() {
+ let [x, y] = global.get_pointer();
+ // We extend the timeout once if the mouse moved no further than MOUSE_LEFT_ACTOR_THRESHOLD to either side.
+ if (this._notificationLeftMouseX > -1 &&
+ y < this._notificationLeftMouseY + MOUSE_LEFT_ACTOR_THRESHOLD &&
+ y > this._notificationLeftMouseY - MOUSE_LEFT_ACTOR_THRESHOLD &&
+ x < this._notificationLeftMouseX + MOUSE_LEFT_ACTOR_THRESHOLD &&
+ x > this._notificationLeftMouseX - MOUSE_LEFT_ACTOR_THRESHOLD) {
+ this._notificationLeftMouseX = -1;
+ this._notificationLeftTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ LONGER_HIDE_TIMEOUT,
+ this._onNotificationLeftTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._notificationLeftTimeoutId, '[gnome-shell] this._onNotificationLeftTimeout');
+ } else {
+ this._notificationLeftTimeoutId = 0;
+ this._useLongerNotificationLeftTimeout = false;
+ this._pointerInNotification = false;
+ this._updateNotificationTimeout(0);
+ this._updateState();
+ }
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _escapeTray() {
+ this._pointerInNotification = false;
+ this._updateNotificationTimeout(0);
+ this._updateState();
+ }
+
+ // All of the logic for what happens when occurs here; the various
+ // event handlers merely update variables such as
+ // 'this._pointerInNotification', 'this._traySummoned', etc, and
+ // _updateState() figures out what (if anything) needs to be done
+ // at the present time.
+ _updateState() {
+ let hasMonitor = Main.layoutManager.primaryMonitor != null;
+ this.visible = !this._bannerBlocked && hasMonitor && this._banner != null;
+ if (this._bannerBlocked || !hasMonitor)
+ return;
+
+ // If our state changes caused _updateState to be called,
+ // just exit now to prevent reentrancy issues.
+ if (this._updatingState)
+ return;
+
+ this._updatingState = true;
+
+ // Filter out acknowledged notifications.
+ let changed = false;
+ this._notificationQueue = this._notificationQueue.filter(n => {
+ changed ||= n.acknowledged;
+ return !n.acknowledged;
+ });
+
+ if (changed)
+ this.emit('queue-changed');
+
+ let hasNotifications = Main.sessionMode.hasNotifications;
+
+ if (this._notificationState == State.HIDDEN) {
+ let nextNotification = this._notificationQueue[0] || null;
+ if (hasNotifications && nextNotification) {
+ let limited = this._busy || Main.layoutManager.primaryMonitor.inFullscreen;
+ let showNextNotification = !limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL;
+ if (showNextNotification)
+ this._showNotification();
+ }
+ } else if (this._notificationState === State.SHOWING ||
+ this._notificationState === State.SHOWN) {
+ let expired = (this._userActiveWhileNotificationShown &&
+ this._notificationTimeoutId == 0 &&
+ this._notification.urgency != Urgency.CRITICAL &&
+ !this._banner.focused &&
+ !this._pointerInNotification) || this._notificationExpired;
+ let mustClose = this._notificationRemoved || !hasNotifications || expired;
+
+ if (mustClose) {
+ let animate = hasNotifications && !this._notificationRemoved;
+ this._hideNotification(animate);
+ } else if (this._notificationState === State.SHOWN &&
+ this._pointerInNotification) {
+ if (!this._banner.expanded)
+ this._expandBanner(false);
+ else
+ this._ensureBannerFocused();
+ }
+ }
+
+ this._updatingState = false;
+
+ // Clean transient variables that are used to communicate actions
+ // to updateState()
+ this._notificationExpired = false;
+ }
+
+ _onIdleMonitorBecameActive() {
+ this._userActiveWhileNotificationShown = true;
+ this._updateNotificationTimeout(2000);
+ this._updateState();
+ }
+
+ _showNotification() {
+ this._notification = this._notificationQueue.shift();
+ this.emit('queue-changed');
+
+ this._userActiveWhileNotificationShown = this.idleMonitor.get_idletime() <= IDLE_TIME;
+ if (!this._userActiveWhileNotificationShown) {
+ // If the user isn't active, set up a watch to let us know
+ // when the user becomes active.
+ this.idleMonitor.add_user_active_watch(this._onIdleMonitorBecameActive.bind(this));
+ }
+
+ this._banner = this._notification.createBanner();
+ this._banner.connectObject(
+ 'done-displaying', this._escapeTray.bind(this),
+ 'unfocused', () => this._updateState(), this);
+
+ this._bannerBin.add_actor(this._banner);
+
+ this._bannerBin.opacity = 0;
+ this._bannerBin.y = -this._banner.height;
+ this.show();
+
+ Meta.disable_unredirect_for_display(global.display);
+ this._updateShowingNotification();
+
+ let [x, y] = global.get_pointer();
+ // We save the position of the mouse at the time when we started showing the notification
+ // in order to determine if the notification popped up under it. We make that check if
+ // the user starts moving the mouse and _onNotificationHoverChanged() gets called. We don't
+ // expand the notification if it just happened to pop up under the mouse unless the user
+ // explicitly mouses away from it and then mouses back in.
+ this._showNotificationMouseX = x;
+ this._showNotificationMouseY = y;
+ // We save the coordinates of the mouse at the time when we started showing the notification
+ // and then we update it in _notificationTimeout(). We don't pop down the notification if
+ // the mouse is moving towards it or within it.
+ this._lastSeenMouseX = x;
+ this._lastSeenMouseY = y;
+
+ this._resetNotificationLeftTimeout();
+ }
+
+ _updateShowingNotification() {
+ this._notification.acknowledged = true;
+ this._notification.playSound();
+
+ // We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
+ // is on in the control center.
+ if (this._notification.urgency == Urgency.CRITICAL ||
+ this._notification.source.policy.forceExpanded)
+ this._expandBanner(true);
+
+ // We tween all notifications to full opacity. This ensures that both new notifications and
+ // notifications that might have been in the process of hiding get full opacity.
+ //
+ // We tween any notification showing in the banner mode to the appropriate height
+ // (which is banner height or expanded height, depending on the notification state)
+ // This ensures that both new notifications and notifications in the banner mode that might
+ // have been in the process of hiding are shown with the correct height.
+ //
+ // We use this._showNotificationCompleted() onComplete callback to extend the time the updated
+ // notification is being shown.
+
+ this._notificationState = State.SHOWING;
+ this._bannerBin.remove_all_transitions();
+ this._bannerBin.ease({
+ opacity: 255,
+ duration: ANIMATION_TIME,
+ mode: Clutter.AnimationMode.LINEAR,
+ });
+ this._bannerBin.ease({
+ y: 0,
+ duration: ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_BACK,
+ onComplete: () => {
+ this._notificationState = State.SHOWN;
+ this._showNotificationCompleted();
+ this._updateState();
+ },
+ });
+ }
+
+ _showNotificationCompleted() {
+ if (this._notification.urgency != Urgency.CRITICAL)
+ this._updateNotificationTimeout(NOTIFICATION_TIMEOUT);
+ }
+
+ _updateNotificationTimeout(timeout) {
+ if (this._notificationTimeoutId) {
+ GLib.source_remove(this._notificationTimeoutId);
+ this._notificationTimeoutId = 0;
+ }
+ if (timeout > 0) {
+ this._notificationTimeoutId =
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout,
+ this._notificationTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._notificationTimeoutId, '[gnome-shell] this._notificationTimeout');
+ }
+ }
+
+ _notificationTimeout() {
+ let [x, y] = global.get_pointer();
+ if (y < this._lastSeenMouseY - 10 && !this._notificationHovered) {
+ // The mouse is moving towards the notification, so don't
+ // hide it yet. (We just create a new timeout (and destroy
+ // the old one) each time because the bookkeeping is
+ // simpler.)
+ this._updateNotificationTimeout(1000);
+ } else if (this._useLongerNotificationLeftTimeout && !this._notificationLeftTimeoutId &&
+ (x != this._lastSeenMouseX || y != this._lastSeenMouseY)) {
+ // Refresh the timeout if the notification originally
+ // popped up under the pointer, and the pointer is hovering
+ // inside it.
+ this._updateNotificationTimeout(1000);
+ } else {
+ this._notificationTimeoutId = 0;
+ this._updateState();
+ }
+
+ this._lastSeenMouseX = x;
+ this._lastSeenMouseY = y;
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _hideNotification(animate) {
+ this._notificationFocusGrabber.ungrabFocus();
+
+ this._banner.disconnectObject(this);
+
+ this._resetNotificationLeftTimeout();
+ this._bannerBin.remove_all_transitions();
+
+ if (animate) {
+ this._notificationState = State.HIDING;
+ this._bannerBin.ease({
+ opacity: 0,
+ duration: ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_BACK,
+ });
+ this._bannerBin.ease({
+ y: -this._bannerBin.height,
+ duration: ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_BACK,
+ onComplete: () => {
+ this._notificationState = State.HIDDEN;
+ this._hideNotificationCompleted();
+ this._updateState();
+ },
+ });
+ } else {
+ this._bannerBin.y = -this._bannerBin.height;
+ this._bannerBin.opacity = 0;
+ this._notificationState = State.HIDDEN;
+ this._hideNotificationCompleted();
+ }
+ }
+
+ _hideNotificationCompleted() {
+ let notification = this._notification;
+ this._notification = null;
+ if (!this._notificationRemoved && notification.isTransient)
+ notification.destroy(NotificationDestroyedReason.EXPIRED);
+
+ this._pointerInNotification = false;
+ this._notificationRemoved = false;
+ Meta.enable_unredirect_for_display(global.display);
+
+ this._banner.destroy();
+ this._banner = null;
+ this.hide();
+ }
+
+ _expandActiveNotification() {
+ if (!this._banner)
+ return;
+
+ this._expandBanner(false);
+ }
+
+ _expandBanner(autoExpanding) {
+ // Don't animate changes in notifications that are auto-expanding.
+ this._banner.expand(!autoExpanding);
+
+ // Don't focus notifications that are auto-expanding.
+ if (!autoExpanding)
+ this._ensureBannerFocused();
+ }
+
+ _ensureBannerFocused() {
+ this._notificationFocusGrabber.grabFocus();
+ }
+});
+
+var SystemNotificationSource = GObject.registerClass(
+class SystemNotificationSource extends Source {
+ _init() {
+ super._init(_("System Information"), 'dialog-information-symbolic');
+ }
+
+ open() {
+ this.destroy();
+ }
+});
diff --git a/js/ui/modalDialog.js b/js/ui/modalDialog.js
new file mode 100644
index 0000000..0561b8b
--- /dev/null
+++ b/js/ui/modalDialog.js
@@ -0,0 +1,288 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ModalDialog */
+
+const { Atk, Clutter, GObject, Shell, St } = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const Layout = imports.ui.layout;
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+var OPEN_AND_CLOSE_TIME = 100;
+var FADE_OUT_DIALOG_TIME = 1000;
+
+var State = {
+ OPENED: 0,
+ CLOSED: 1,
+ OPENING: 2,
+ CLOSING: 3,
+ FADED_OUT: 4,
+};
+
+var ModalDialog = GObject.registerClass({
+ Properties: {
+ 'state': GObject.ParamSpec.int('state', 'Dialog state', 'state',
+ GObject.ParamFlags.READABLE,
+ Math.min(...Object.values(State)),
+ Math.max(...Object.values(State)),
+ State.CLOSED),
+ },
+ Signals: { 'opened': {}, 'closed': {} },
+}, class ModalDialog extends St.Widget {
+ _init(params) {
+ super._init({
+ visible: false,
+ reactive: true,
+ x: 0,
+ y: 0,
+ accessible_role: Atk.Role.DIALOG,
+ });
+
+ params = Params.parse(params, {
+ shellReactive: false,
+ styleClass: null,
+ actionMode: Shell.ActionMode.SYSTEM_MODAL,
+ shouldFadeIn: true,
+ shouldFadeOut: true,
+ destroyOnClose: true,
+ });
+
+ this._state = State.CLOSED;
+ this._hasModal = false;
+ this._actionMode = params.actionMode;
+ this._shellReactive = params.shellReactive;
+ this._shouldFadeIn = params.shouldFadeIn;
+ this._shouldFadeOut = params.shouldFadeOut;
+ this._destroyOnClose = params.destroyOnClose;
+
+ Main.layoutManager.modalDialogGroup.add_actor(this);
+
+ const constraint = new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ });
+ this.add_constraint(constraint);
+
+ this.backgroundStack = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ });
+ this._backgroundBin = new St.Bin({ child: this.backgroundStack });
+ this._monitorConstraint = new Layout.MonitorConstraint();
+ this._backgroundBin.add_constraint(this._monitorConstraint);
+ this.add_actor(this._backgroundBin);
+
+ this.dialogLayout = new Dialog.Dialog(this.backgroundStack, params.styleClass);
+ this.contentLayout = this.dialogLayout.contentLayout;
+ this.buttonLayout = this.dialogLayout.buttonLayout;
+
+ if (!this._shellReactive) {
+ this._lightbox = new Lightbox.Lightbox(this, {
+ inhibitEvents: true,
+ radialEffect: true,
+ });
+ this._lightbox.highlight(this._backgroundBin);
+
+ this._eventBlocker = new Clutter.Actor({ reactive: true });
+ this.backgroundStack.add_actor(this._eventBlocker);
+ }
+
+ global.focus_manager.add_group(this.dialogLayout);
+ this._initialKeyFocus = null;
+ this._initialKeyFocusDestroyId = 0;
+ this._savedKeyFocus = null;
+ }
+
+ get state() {
+ return this._state;
+ }
+
+ _setState(state) {
+ if (this._state == state)
+ return;
+
+ this._state = state;
+ this.notify('state');
+ }
+
+ vfunc_key_press_event() {
+ if (global.focus_manager.navigate_from_event(Clutter.get_current_event()))
+ return Clutter.EVENT_STOP;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_captured_event(event) {
+ if (Main.keyboard.maybeHandleEvent(event))
+ return Clutter.EVENT_STOP;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ clearButtons() {
+ this.dialogLayout.clearButtons();
+ }
+
+ setButtons(buttons) {
+ this.clearButtons();
+
+ for (let buttonInfo of buttons)
+ this.addButton(buttonInfo);
+ }
+
+ addButton(buttonInfo) {
+ return this.dialogLayout.addButton(buttonInfo);
+ }
+
+ _fadeOpen(onPrimary) {
+ if (onPrimary)
+ this._monitorConstraint.primary = true;
+ else
+ this._monitorConstraint.index = global.display.get_current_monitor();
+
+ this._setState(State.OPENING);
+
+ this.dialogLayout.opacity = 255;
+ if (this._lightbox)
+ this._lightbox.lightOn();
+ this.opacity = 0;
+ this.show();
+ this.ease({
+ opacity: 255,
+ duration: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._setState(State.OPENED);
+ this.emit('opened');
+ },
+ });
+ }
+
+ setInitialKeyFocus(actor) {
+ this._initialKeyFocus?.disconnectObject(this);
+
+ this._initialKeyFocus = actor;
+
+ actor.connectObject('destroy',
+ () => (this._initialKeyFocus = null), this);
+ }
+
+ open(timestamp, onPrimary) {
+ if (this.state == State.OPENED || this.state == State.OPENING)
+ return true;
+
+ if (!this.pushModal(timestamp))
+ return false;
+
+ this._fadeOpen(onPrimary);
+ return true;
+ }
+
+ _closeComplete() {
+ this._setState(State.CLOSED);
+ this.hide();
+ this.emit('closed');
+
+ if (this._destroyOnClose)
+ this.destroy();
+ }
+
+ close(timestamp) {
+ if (this.state == State.CLOSED || this.state == State.CLOSING)
+ return;
+
+ this._setState(State.CLOSING);
+ this.popModal(timestamp);
+ this._savedKeyFocus = null;
+
+ if (this._shouldFadeOut) {
+ this.ease({
+ opacity: 0,
+ duration: OPEN_AND_CLOSE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._closeComplete(),
+ });
+ } else {
+ this._closeComplete();
+ }
+ }
+
+ // Drop modal status without closing the dialog; this makes the
+ // dialog insensitive as well, so it needs to be followed shortly
+ // by either a close() or a pushModal()
+ popModal(timestamp) {
+ if (!this._hasModal)
+ return;
+
+ let focus = global.stage.key_focus;
+ if (focus && this.contains(focus))
+ this._savedKeyFocus = focus;
+ else
+ this._savedKeyFocus = null;
+ Main.popModal(this._grab, timestamp);
+ this._grab = null;
+ this._hasModal = false;
+
+ if (!this._shellReactive)
+ this.backgroundStack.set_child_above_sibling(this._eventBlocker, null);
+ }
+
+ pushModal(timestamp) {
+ if (this._hasModal)
+ return true;
+
+ let params = { actionMode: this._actionMode };
+ if (timestamp)
+ params['timestamp'] = timestamp;
+ let grab = Main.pushModal(this, params);
+ if (grab.get_seat_state() !== Clutter.GrabState.ALL) {
+ Main.popModal(grab);
+ return false;
+ }
+
+ this._grab = grab;
+ Main.layoutManager.emit('system-modal-opened');
+
+ this._hasModal = true;
+ if (this._savedKeyFocus) {
+ this._savedKeyFocus.grab_key_focus();
+ this._savedKeyFocus = null;
+ } else {
+ let focus = this._initialKeyFocus || this.dialogLayout.initialKeyFocus;
+ focus.grab_key_focus();
+ }
+
+ if (!this._shellReactive)
+ this.backgroundStack.set_child_below_sibling(this._eventBlocker, null);
+ return true;
+ }
+
+ // This method is like close, but fades the dialog out much slower,
+ // and leaves the lightbox in place. Once in the faded out state,
+ // the dialog can be brought back by an open call, or the lightbox
+ // can be dismissed by a close call.
+ //
+ // The main point of this method is to give some indication to the user
+ // that the dialog response has been acknowledged but will take a few
+ // moments before being processed.
+ // e.g., if a user clicked "Log Out" then the dialog should go away
+ // immediately, but the lightbox should remain until the logout is
+ // complete.
+ _fadeOutDialog(timestamp) {
+ if (this.state == State.CLOSED || this.state == State.CLOSING)
+ return;
+
+ if (this.state == State.FADED_OUT)
+ return;
+
+ this.popModal(timestamp);
+ this.dialogLayout.ease({
+ opacity: 0,
+ duration: FADE_OUT_DIALOG_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._setState(State.FADED_OUT),
+ });
+ }
+});
diff --git a/js/ui/mpris.js b/js/ui/mpris.js
new file mode 100644
index 0000000..f44f87e
--- /dev/null
+++ b/js/ui/mpris.js
@@ -0,0 +1,297 @@
+/* exported MediaSection */
+const { Gio, GObject, Shell, St } = imports.gi;
+const Signals = imports.misc.signals;
+
+const Main = imports.ui.main;
+const MessageList = imports.ui.messageList;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const DBusIface = loadInterfaceXML('org.freedesktop.DBus');
+const DBusProxy = Gio.DBusProxy.makeProxyWrapper(DBusIface);
+
+const MprisIface = loadInterfaceXML('org.mpris.MediaPlayer2');
+const MprisProxy = Gio.DBusProxy.makeProxyWrapper(MprisIface);
+
+const MprisPlayerIface = loadInterfaceXML('org.mpris.MediaPlayer2.Player');
+const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface);
+
+const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.';
+
+var MediaMessage = GObject.registerClass(
+class MediaMessage extends MessageList.Message {
+ _init(player) {
+ super._init('', '');
+
+ this._player = player;
+
+ this._icon = new St.Icon({ style_class: 'media-message-cover-icon' });
+ this.setIcon(this._icon);
+
+ // reclaim space used by unused elements
+ this._secondaryBin.hide();
+ this._closeButton.hide();
+
+ this._prevButton = this.addMediaControl('media-skip-backward-symbolic',
+ () => {
+ this._player.previous();
+ });
+
+ this._playPauseButton = this.addMediaControl('',
+ () => {
+ this._player.playPause();
+ });
+
+ this._nextButton = this.addMediaControl('media-skip-forward-symbolic',
+ () => {
+ this._player.next();
+ });
+
+ this._player.connectObject(
+ 'changed', this._update.bind(this),
+ 'closed', this.close.bind(this), this);
+ this._update();
+ }
+
+ vfunc_clicked() {
+ this._player.raise();
+ Main.panel.closeCalendar();
+ }
+
+ _updateNavButton(button, sensitive) {
+ button.reactive = sensitive;
+ }
+
+ _update() {
+ this.setTitle(this._player.trackTitle);
+ this.setBody(this._player.trackArtists.join(', '));
+
+ if (this._player.trackCoverUrl) {
+ let file = Gio.File.new_for_uri(this._player.trackCoverUrl);
+ this._icon.gicon = new Gio.FileIcon({ file });
+ this._icon.remove_style_class_name('fallback');
+ } else {
+ this._icon.icon_name = 'audio-x-generic-symbolic';
+ this._icon.add_style_class_name('fallback');
+ }
+
+ let isPlaying = this._player.status == 'Playing';
+ let iconName = isPlaying
+ ? 'media-playback-pause-symbolic'
+ : 'media-playback-start-symbolic';
+ this._playPauseButton.child.icon_name = iconName;
+
+ this._updateNavButton(this._prevButton, this._player.canGoPrevious);
+ this._updateNavButton(this._nextButton, this._player.canGoNext);
+ }
+});
+
+var MprisPlayer = class MprisPlayer extends Signals.EventEmitter {
+ constructor(busName) {
+ super();
+
+ this._mprisProxy = new MprisProxy(Gio.DBus.session, busName,
+ '/org/mpris/MediaPlayer2',
+ this._onMprisProxyReady.bind(this));
+ this._playerProxy = new MprisPlayerProxy(Gio.DBus.session, busName,
+ '/org/mpris/MediaPlayer2',
+ this._onPlayerProxyReady.bind(this));
+
+ this._visible = false;
+ this._trackArtists = [];
+ this._trackTitle = '';
+ this._trackCoverUrl = '';
+ this._busName = busName;
+ }
+
+ get status() {
+ return this._playerProxy.PlaybackStatus;
+ }
+
+ get trackArtists() {
+ return this._trackArtists;
+ }
+
+ get trackTitle() {
+ return this._trackTitle;
+ }
+
+ get trackCoverUrl() {
+ return this._trackCoverUrl;
+ }
+
+ playPause() {
+ this._playerProxy.PlayPauseAsync().catch(logError);
+ }
+
+ get canGoNext() {
+ return this._playerProxy.CanGoNext;
+ }
+
+ next() {
+ this._playerProxy.NextAsync().catch(logError);
+ }
+
+ get canGoPrevious() {
+ return this._playerProxy.CanGoPrevious;
+ }
+
+ previous() {
+ this._playerProxy.PreviousAsync().catch(logError);
+ }
+
+ raise() {
+ // The remote Raise() method may run into focus stealing prevention,
+ // so prefer activating the app via .desktop file if possible
+ let app = null;
+ if (this._mprisProxy.DesktopEntry) {
+ let desktopId = `${this._mprisProxy.DesktopEntry}.desktop`;
+ app = Shell.AppSystem.get_default().lookup_app(desktopId);
+ }
+
+ if (app)
+ app.activate();
+ else if (this._mprisProxy.CanRaise)
+ this._mprisProxy.RaiseAsync().catch(logError);
+ }
+
+ _close() {
+ this._mprisProxy.disconnectObject(this);
+ this._mprisProxy = null;
+
+ this._playerProxy.disconnectObject(this);
+ this._playerProxy = null;
+
+ this.emit('closed');
+ }
+
+ _onMprisProxyReady() {
+ this._mprisProxy.connectObject('notify::g-name-owner',
+ () => {
+ if (!this._mprisProxy.g_name_owner)
+ this._close();
+ }, this);
+ // It is possible for the bus to disappear before the previous signal
+ // is connected, so we must ensure that the bus still exists at this
+ // point.
+ if (!this._mprisProxy.g_name_owner)
+ this._close();
+ }
+
+ _onPlayerProxyReady() {
+ this._playerProxy.connectObject(
+ 'g-properties-changed', () => this._updateState(), this);
+ this._updateState();
+ }
+
+ _updateState() {
+ let metadata = {};
+ for (let prop in this._playerProxy.Metadata)
+ metadata[prop] = this._playerProxy.Metadata[prop].deepUnpack();
+
+ // Validate according to the spec; some clients send buggy metadata:
+ // https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata
+ this._trackArtists = metadata['xesam:artist'];
+ if (!Array.isArray(this._trackArtists) ||
+ !this._trackArtists.every(artist => typeof artist === 'string')) {
+ if (typeof this._trackArtists !== 'undefined') {
+ log(`Received faulty track artist metadata from ${
+ this._busName}; expected an array of strings, got ${
+ this._trackArtists} (${typeof this._trackArtists})`);
+ }
+ this._trackArtists = [_("Unknown artist")];
+ }
+
+ this._trackTitle = metadata['xesam:title'];
+ if (typeof this._trackTitle !== 'string') {
+ if (typeof this._trackTitle !== 'undefined') {
+ log(`Received faulty track title metadata from ${
+ this._busName}; expected a string, got ${
+ this._trackTitle} (${typeof this._trackTitle})`);
+ }
+ this._trackTitle = _("Unknown title");
+ }
+
+ this._trackCoverUrl = metadata['mpris:artUrl'];
+ if (typeof this._trackCoverUrl !== 'string') {
+ if (typeof this._trackCoverUrl !== 'undefined') {
+ log(`Received faulty track cover art metadata from ${
+ this._busName}; expected a string, got ${
+ this._trackCoverUrl} (${typeof this._trackCoverUrl})`);
+ }
+ this._trackCoverUrl = '';
+ }
+
+ this.emit('changed');
+
+ let visible = this._playerProxy.CanPlay;
+
+ if (this._visible != visible) {
+ this._visible = visible;
+ if (visible)
+ this.emit('show');
+ else
+ this.emit('hide');
+ }
+ }
+};
+
+var MediaSection = GObject.registerClass(
+class MediaSection extends MessageList.MessageListSection {
+ _init() {
+ super._init();
+
+ this._players = new Map();
+
+ this._proxy = new DBusProxy(Gio.DBus.session,
+ 'org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ this._onProxyReady.bind(this));
+ }
+
+ get allowed() {
+ return !Main.sessionMode.isGreeter;
+ }
+
+ _addPlayer(busName) {
+ if (this._players.get(busName))
+ return;
+
+ let player = new MprisPlayer(busName);
+ let message = null;
+ player.connect('closed',
+ () => {
+ this._players.delete(busName);
+ });
+ player.connect('show', () => {
+ message = new MediaMessage(player);
+ this.addMessage(message, true);
+ });
+ player.connect('hide', () => {
+ this.removeMessage(message, true);
+ message = null;
+ });
+
+ this._players.set(busName, player);
+ }
+
+ async _onProxyReady() {
+ const [names] = await this._proxy.ListNamesAsync();
+ names.forEach(name => {
+ if (!name.startsWith(MPRIS_PLAYER_PREFIX))
+ return;
+
+ this._addPlayer(name);
+ });
+ this._proxy.connectSignal('NameOwnerChanged',
+ this._onNameOwnerChanged.bind(this));
+ }
+
+ _onNameOwnerChanged(proxy, sender, [name, oldOwner, newOwner]) {
+ if (!name.startsWith(MPRIS_PLAYER_PREFIX))
+ return;
+
+ if (newOwner && !oldOwner)
+ this._addPlayer(name);
+ }
+});
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
new file mode 100644
index 0000000..b27158e
--- /dev/null
+++ b/js/ui/notificationDaemon.js
@@ -0,0 +1,771 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported NotificationDaemon */
+
+const { GdkPixbuf, Gio, GLib, GObject, Shell, St } = imports.gi;
+
+const Config = imports.misc.config;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const Params = imports.misc.params;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const FdoNotificationsIface = loadInterfaceXML('org.freedesktop.Notifications');
+
+var NotificationClosedReason = {
+ EXPIRED: 1,
+ DISMISSED: 2,
+ APP_CLOSED: 3,
+ UNDEFINED: 4,
+};
+
+var Urgency = {
+ LOW: 0,
+ NORMAL: 1,
+ CRITICAL: 2,
+};
+
+var FdoNotificationDaemon = class FdoNotificationDaemon {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
+
+ this._sources = [];
+ this._notifications = {};
+
+ this._nextNotificationId = 1;
+ }
+
+ _imageForNotificationData(hints) {
+ if (hints['image-data']) {
+ const [
+ width, height, rowStride, hasAlpha,
+ bitsPerSample, nChannels_, data,
+ ] = hints['image-data'];
+ return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
+ bitsPerSample, width, height, rowStride);
+ } else if (hints['image-path']) {
+ return this._iconForNotificationData(hints['image-path']);
+ }
+ return null;
+ }
+
+ _fallbackIconForNotificationData(hints) {
+ let stockIcon;
+ switch (hints.urgency) {
+ case Urgency.LOW:
+ case Urgency.NORMAL:
+ stockIcon = 'dialog-information';
+ break;
+ case Urgency.CRITICAL:
+ stockIcon = 'dialog-error';
+ break;
+ }
+ return new Gio.ThemedIcon({ name: stockIcon });
+ }
+
+ _iconForNotificationData(icon) {
+ if (icon) {
+ if (icon.substr(0, 7) == 'file://')
+ return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
+ else if (icon[0] == '/')
+ return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
+ else
+ return new Gio.ThemedIcon({ name: icon });
+ }
+ return null;
+ }
+
+ _lookupSource(title, pid) {
+ for (let i = 0; i < this._sources.length; i++) {
+ let source = this._sources[i];
+ if (source.pid == pid && source.initialTitle == title)
+ return source;
+ }
+ return null;
+ }
+
+ // Returns the source associated with ndata.notification if it is set.
+ // If the existing or requested source is associated with a tray icon
+ // and passed in pid matches a pid of an existing source, the title
+ // match is ignored to enable representing a tray icon and notifications
+ // from the same application with a single source.
+ //
+ // If no existing source is found, a new source is created as long as
+ // pid is provided.
+ _getSource(title, pid, ndata, sender) {
+ if (!pid && !(ndata && ndata.notification))
+ throw new Error('Either a pid or ndata.notification is needed');
+
+ // We use notification's source for the notifications we still have
+ // around that are getting replaced because we don't keep sources
+ // for transient notifications in this._sources, but we still want
+ // the notification associated with them to get replaced correctly.
+ if (ndata && ndata.notification)
+ return ndata.notification.source;
+
+ let source = this._lookupSource(title, pid);
+ if (source) {
+ source.setTitle(title);
+ return source;
+ }
+
+ const appId = ndata?.hints['desktop-entry'];
+ source = new FdoNotificationDaemonSource(title, pid, sender, appId);
+
+ this._sources.push(source);
+ source.connect('destroy', () => {
+ let index = this._sources.indexOf(source);
+ if (index >= 0)
+ this._sources.splice(index, 1);
+ });
+
+ Main.messageTray.add(source);
+ return source;
+ }
+
+ NotifyAsync(params, invocation) {
+ let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
+ let id;
+
+ for (let hint in hints) {
+ // unpack the variants
+ hints[hint] = hints[hint].deepUnpack();
+ }
+
+ hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
+
+ // Filter out chat, presence, calls and invitation notifications from
+ // Empathy, since we handle that information from telepathyClient.js
+ //
+ // Note that empathy uses im.received for one to one chats and
+ // x-empathy.im.mentioned for multi-user, so we're good here
+ if (appName == 'Empathy' && hints['category'] == 'im.received') {
+ // Ignore replacesId since we already sent back a
+ // NotificationClosed for that id.
+ id = this._nextNotificationId++;
+ let idleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(idleId, '[gnome-shell] this._emitNotificationClosed');
+ return invocation.return_value(GLib.Variant.new('(u)', [id]));
+ }
+
+ // Be compatible with the various hints for image data and image path
+ // 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
+
+ if (!hints['image-path'] && hints['image_path'])
+ hints['image-path'] = hints['image_path']; // version 1.1 of the spec
+
+ if (!hints['image-data']) {
+ if (hints['image_data'])
+ hints['image-data'] = hints['image_data']; // version 1.1 of the spec
+ else if (hints['icon_data'] && !hints['image-path'])
+ // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
+ hints['image-data'] = hints['icon_data'];
+ }
+
+ const ndata = {
+ appName,
+ icon,
+ summary,
+ body,
+ actions,
+ hints,
+ timeout,
+ };
+ if (replacesId != 0 && this._notifications[replacesId]) {
+ ndata.id = id = replacesId;
+ ndata.notification = this._notifications[replacesId].notification;
+ } else {
+ replacesId = 0;
+ ndata.id = id = this._nextNotificationId++;
+ }
+ this._notifications[id] = ndata;
+
+ let sender = invocation.get_sender();
+ let pid = hints['sender-pid'];
+
+ let source = this._getSource(appName, pid, ndata, sender, null);
+ this._notifyForSource(source, ndata);
+
+ return invocation.return_value(GLib.Variant.new('(u)', [id]));
+ }
+
+ _notifyForSource(source, ndata) {
+ const { icon, summary, body, actions, hints } = ndata;
+ let { notification } = ndata;
+
+ if (notification == null) {
+ notification = new MessageTray.Notification(source);
+ ndata.notification = notification;
+ notification.connect('destroy', (n, reason) => {
+ delete this._notifications[ndata.id];
+ let notificationClosedReason;
+ switch (reason) {
+ case MessageTray.NotificationDestroyedReason.EXPIRED:
+ notificationClosedReason = NotificationClosedReason.EXPIRED;
+ break;
+ case MessageTray.NotificationDestroyedReason.DISMISSED:
+ notificationClosedReason = NotificationClosedReason.DISMISSED;
+ break;
+ case MessageTray.NotificationDestroyedReason.SOURCE_CLOSED:
+ notificationClosedReason = NotificationClosedReason.APP_CLOSED;
+ break;
+ }
+ this._emitNotificationClosed(ndata.id, notificationClosedReason);
+ });
+ }
+
+ // 'image-data' (or 'image-path') takes precedence over 'app-icon'.
+ let gicon = this._imageForNotificationData(hints);
+
+ if (!gicon)
+ gicon = this._iconForNotificationData(icon);
+
+ if (!gicon)
+ gicon = this._fallbackIconForNotificationData(hints);
+
+ const soundFile = 'sound-file' in hints
+ ? Gio.File.new_for_path(hints['sound-file']) : null;
+
+ notification.update(summary, body, {
+ gicon,
+ bannerMarkup: true,
+ clear: true,
+ soundFile,
+ soundName: hints['sound-name'],
+ });
+
+ let hasDefaultAction = false;
+
+ if (actions.length) {
+ for (let i = 0; i < actions.length - 1; i += 2) {
+ let [actionId, label] = [actions[i], actions[i + 1]];
+ if (actionId == 'default') {
+ hasDefaultAction = true;
+ } else {
+ notification.addAction(label, () => {
+ this._emitActionInvoked(ndata.id, actionId);
+ });
+ }
+ }
+ }
+
+ if (hasDefaultAction) {
+ notification.connect('activated', () => {
+ this._emitActionInvoked(ndata.id, 'default');
+ });
+ } else {
+ notification.connect('activated', () => {
+ source.open();
+ });
+ }
+
+ switch (hints.urgency) {
+ case Urgency.LOW:
+ notification.setUrgency(MessageTray.Urgency.LOW);
+ break;
+ case Urgency.NORMAL:
+ notification.setUrgency(MessageTray.Urgency.NORMAL);
+ break;
+ case Urgency.CRITICAL:
+ notification.setUrgency(MessageTray.Urgency.CRITICAL);
+ break;
+ }
+ notification.setResident(!!hints.resident);
+ // 'transient' is a reserved keyword in JS, so we have to retrieve the value
+ // of the 'transient' hint with hints['transient'] rather than hints.transient
+ notification.setTransient(!!hints['transient']);
+
+ let privacyScope = hints['x-gnome-privacy-scope'] || 'user';
+ notification.setPrivacyScope(privacyScope == 'system'
+ ? MessageTray.PrivacyScope.SYSTEM
+ : MessageTray.PrivacyScope.USER);
+
+ let sourceGIcon = source.useNotificationIcon ? gicon : null;
+ source.processNotification(notification, sourceGIcon);
+ }
+
+ CloseNotification(id) {
+ let ndata = this._notifications[id];
+ if (ndata) {
+ if (ndata.notification)
+ ndata.notification.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
+ delete this._notifications[id];
+ }
+ }
+
+ GetCapabilities() {
+ return [
+ 'actions',
+ // 'action-icons',
+ 'body',
+ // 'body-hyperlinks',
+ // 'body-images',
+ 'body-markup',
+ // 'icon-multi',
+ 'icon-static',
+ 'persistence',
+ 'sound',
+ ];
+ }
+
+ GetServerInformation() {
+ return [
+ Config.PACKAGE_NAME,
+ 'GNOME',
+ Config.PACKAGE_VERSION,
+ '1.2',
+ ];
+ }
+
+ _emitNotificationClosed(id, reason) {
+ this._dbusImpl.emit_signal('NotificationClosed',
+ GLib.Variant.new('(uu)', [id, reason]));
+ }
+
+ _emitActionInvoked(id, action) {
+ this._dbusImpl.emit_signal('ActionInvoked',
+ GLib.Variant.new('(us)', [id, action]));
+ }
+};
+
+var FdoNotificationDaemonSource = GObject.registerClass(
+class FdoNotificationDaemonSource extends MessageTray.Source {
+ _init(title, pid, sender, appId) {
+ this.pid = pid;
+ this.initialTitle = title;
+ this.app = this._getApp(appId);
+
+ super._init(title);
+
+ if (this.app)
+ this.title = this.app.get_name();
+ else
+ this.useNotificationIcon = true;
+
+ if (sender) {
+ this._nameWatcherId = Gio.DBus.session.watch_name(sender,
+ Gio.BusNameWatcherFlags.NONE,
+ null,
+ this._onNameVanished.bind(this));
+ } else {
+ this._nameWatcherId = 0;
+ }
+ }
+
+ _createPolicy() {
+ if (this.app && this.app.get_app_info()) {
+ let id = this.app.get_id().replace(/\.desktop$/, '');
+ return new MessageTray.NotificationApplicationPolicy(id);
+ } else {
+ return new MessageTray.NotificationGenericPolicy();
+ }
+ }
+
+ _onNameVanished() {
+ // Destroy the notification source when its sender is removed from DBus.
+ // Only do so if this.app is set to avoid removing "notify-send" sources, senders
+ // of which аre removed from DBus immediately.
+ // Sender being removed from DBus would normally result in a tray icon being removed,
+ // so allow the code path that handles the tray icon being removed to handle that case.
+ if (this.app)
+ this.destroy();
+ }
+
+ processNotification(notification, gicon) {
+ if (gicon)
+ this._gicon = gicon;
+ this.iconUpdated();
+
+ let tracker = Shell.WindowTracker.get_default();
+ if (notification.resident && this.app && tracker.focus_app == this.app)
+ this.pushNotification(notification);
+ else
+ this.showNotification(notification);
+ }
+
+ _getApp(appId) {
+ const appSys = Shell.AppSystem.get_default();
+ let app;
+
+ app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
+ if (app != null)
+ return app;
+
+ if (appId)
+ app = appSys.lookup_app(`${appId}.desktop`);
+
+ if (!app)
+ app = appSys.lookup_app(`${this.initialTitle}.desktop`);
+
+ return app;
+ }
+
+ setTitle(title) {
+ // Do nothing if .app is set, we don't want to override the
+ // app name with whatever is provided through libnotify (usually
+ // garbage)
+ if (this.app)
+ return;
+
+ super.setTitle(title);
+ }
+
+ open() {
+ this.openApp();
+ this.destroyNonResidentNotifications();
+ }
+
+ openApp() {
+ if (this.app == null)
+ return;
+
+ this.app.activate();
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+
+ destroy() {
+ if (this._nameWatcherId) {
+ Gio.DBus.session.unwatch_name(this._nameWatcherId);
+ this._nameWatcherId = 0;
+ }
+
+ super.destroy();
+ }
+
+ createIcon(size) {
+ if (this.app) {
+ return this.app.create_icon_texture(size);
+ } else if (this._gicon) {
+ return new St.Icon({
+ gicon: this._gicon,
+ icon_size: size,
+ });
+ } else {
+ return null;
+ }
+ }
+});
+
+const PRIORITY_URGENCY_MAP = {
+ low: MessageTray.Urgency.LOW,
+ normal: MessageTray.Urgency.NORMAL,
+ high: MessageTray.Urgency.HIGH,
+ urgent: MessageTray.Urgency.CRITICAL,
+};
+
+var GtkNotificationDaemonNotification = GObject.registerClass(
+class GtkNotificationDaemonNotification extends MessageTray.Notification {
+ _init(source, notification) {
+ super._init(source);
+ this._serialized = GLib.Variant.new('a{sv}', notification);
+
+ const {
+ title,
+ body,
+ icon: gicon,
+ urgent,
+ priority,
+ buttons,
+ 'default-action': defaultAction,
+ 'default-action-target': defaultActionTarget,
+ timestamp: time,
+ } = notification;
+
+ if (priority) {
+ let urgency = PRIORITY_URGENCY_MAP[priority.unpack()];
+ this.setUrgency(urgency != undefined ? urgency : MessageTray.Urgency.NORMAL);
+ } else if (urgent) {
+ this.setUrgency(urgent.unpack()
+ ? MessageTray.Urgency.CRITICAL
+ : MessageTray.Urgency.NORMAL);
+ } else {
+ this.setUrgency(MessageTray.Urgency.NORMAL);
+ }
+
+ if (buttons) {
+ buttons.deepUnpack().forEach(button => {
+ this.addAction(button.label.unpack(), () => {
+ this._onButtonClicked(button);
+ });
+ });
+ }
+
+ this._defaultAction = defaultAction?.unpack();
+ this._defaultActionTarget = defaultActionTarget;
+
+ this.update(title.unpack(), body?.unpack(), {
+ gicon: gicon
+ ? Gio.icon_deserialize(gicon) : null,
+ datetime: time
+ ? GLib.DateTime.new_from_unix_local(time.unpack()) : null,
+ });
+ }
+
+ _activateAction(namespacedActionId, target) {
+ if (namespacedActionId) {
+ if (namespacedActionId.startsWith('app.')) {
+ let actionId = namespacedActionId.slice('app.'.length);
+ this.source.activateAction(actionId, target);
+ }
+ } else {
+ this.source.open();
+ }
+ }
+
+ _onButtonClicked(button) {
+ let { action, target } = button;
+ this._activateAction(action.unpack(), target);
+ }
+
+ activate() {
+ this._activateAction(this._defaultAction, this._defaultActionTarget);
+ super.activate();
+ }
+
+ serialize() {
+ return this._serialized;
+ }
+});
+
+const FdoApplicationIface = loadInterfaceXML('org.freedesktop.Application');
+const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
+
+function objectPathFromAppId(appId) {
+ return `/${appId.replace(/\./g, '/').replace(/-/g, '_')}`;
+}
+
+function getPlatformData() {
+ let startupId = GLib.Variant.new('s', `_TIME${global.get_current_time()}`);
+ return { "desktop-startup-id": startupId };
+}
+
+function InvalidAppError() {}
+
+var GtkNotificationDaemonAppSource = GObject.registerClass(
+class GtkNotificationDaemonAppSource extends MessageTray.Source {
+ _init(appId) {
+ let objectPath = objectPathFromAppId(appId);
+ if (!GLib.Variant.is_object_path(objectPath))
+ throw new InvalidAppError();
+
+ let app = Shell.AppSystem.get_default().lookup_app(`${appId}.desktop`);
+ if (!app)
+ throw new InvalidAppError();
+
+ this._appId = appId;
+ this._app = app;
+ this._objectPath = objectPath;
+
+ super._init(app.get_name());
+
+ this._notifications = {};
+ this._notificationPending = false;
+ }
+
+ createIcon(size) {
+ return this._app.create_icon_texture(size);
+ }
+
+ _createPolicy() {
+ return new MessageTray.NotificationApplicationPolicy(this._appId);
+ }
+
+ _createApp() {
+ return new Promise((resolve, reject) => {
+ new FdoApplicationProxy(Gio.DBus.session,
+ this._appId, this._objectPath, (proxy, err) => {
+ if (err)
+ reject(err);
+ else
+ resolve(proxy);
+ });
+ });
+ }
+
+ _createNotification(params) {
+ return new GtkNotificationDaemonNotification(this, params);
+ }
+
+ async activateAction(actionId, target) {
+ try {
+ const app = await this._createApp();
+ const params = target ? [target] : [];
+ app.ActivateActionAsync(actionId, params, getPlatformData());
+ } catch (error) {
+ logError(error, 'Failed to activate application proxy');
+ }
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+
+ async open() {
+ try {
+ const app = await this._createApp();
+ app.ActivateAsync(getPlatformData());
+ } catch (error) {
+ logError(error, 'Failed to open application proxy');
+ }
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+
+ addNotification(notificationId, notificationParams, showBanner) {
+ this._notificationPending = true;
+
+ if (this._notifications[notificationId])
+ this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.REPLACED);
+
+ let notification = this._createNotification(notificationParams);
+ notification.connect('destroy', () => {
+ delete this._notifications[notificationId];
+ });
+ this._notifications[notificationId] = notification;
+
+ if (showBanner)
+ this.showNotification(notification);
+ else
+ this.pushNotification(notification);
+
+ this._notificationPending = false;
+ }
+
+ destroy(reason) {
+ if (this._notificationPending)
+ return;
+ super.destroy(reason);
+ }
+
+ removeNotification(notificationId) {
+ if (this._notifications[notificationId])
+ this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
+ }
+
+ serialize() {
+ let notifications = [];
+ for (let notificationId in this._notifications) {
+ let notification = this._notifications[notificationId];
+ notifications.push([notificationId, notification.serialize()]);
+ }
+ return [this._appId, notifications];
+ }
+});
+
+const GtkNotificationsIface = loadInterfaceXML('org.gtk.Notifications');
+
+var GtkNotificationDaemon = class GtkNotificationDaemon {
+ constructor() {
+ this._sources = {};
+
+ this._loadNotifications();
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
+
+ Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
+ }
+
+ _ensureAppSource(appId) {
+ if (this._sources[appId])
+ return this._sources[appId];
+
+ let source = new GtkNotificationDaemonAppSource(appId);
+
+ source.connect('destroy', () => {
+ delete this._sources[appId];
+ this._saveNotifications();
+ });
+ source.connect('notify::count', this._saveNotifications.bind(this));
+ Main.messageTray.add(source);
+ this._sources[appId] = source;
+ return source;
+ }
+
+ _loadNotifications() {
+ this._isLoading = true;
+
+ try {
+ let value = global.get_persistent_state('a(sa(sv))', 'notifications');
+ if (value) {
+ let sources = value.deepUnpack();
+ sources.forEach(([appId, notifications]) => {
+ if (notifications.length == 0)
+ return;
+
+ let source;
+ try {
+ source = this._ensureAppSource(appId);
+ } catch (e) {
+ if (e instanceof InvalidAppError)
+ return;
+ throw e;
+ }
+
+ notifications.forEach(([notificationId, notification]) => {
+ source.addNotification(notificationId, notification.deepUnpack(), false);
+ });
+ });
+ }
+ } catch (e) {
+ logError(e, 'Failed to load saved notifications');
+ } finally {
+ this._isLoading = false;
+ }
+ }
+
+ _saveNotifications() {
+ if (this._isLoading)
+ return;
+
+ let sources = [];
+ for (let appId in this._sources) {
+ let source = this._sources[appId];
+ sources.push(source.serialize());
+ }
+
+ global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
+ }
+
+ AddNotificationAsync(params, invocation) {
+ let [appId, notificationId, notification] = params;
+
+ let source;
+ try {
+ source = this._ensureAppSource(appId);
+ } catch (e) {
+ if (e instanceof InvalidAppError) {
+ invocation.return_dbus_error('org.gtk.Notifications.InvalidApp',
+ `The app by ID "${appId}" could not be found`);
+ return;
+ }
+ throw e;
+ }
+
+ let timestamp = GLib.DateTime.new_now_local().to_unix();
+ notification['timestamp'] = new GLib.Variant('x', timestamp);
+
+ source.addNotification(notificationId, notification, true);
+
+ invocation.return_value(null);
+ }
+
+ RemoveNotificationAsync(params, invocation) {
+ let [appId, notificationId] = params;
+ let source = this._sources[appId];
+ if (source)
+ source.removeNotification(notificationId);
+
+ invocation.return_value(null);
+ }
+};
+
+var NotificationDaemon = class NotificationDaemon {
+ constructor() {
+ this._fdoNotificationDaemon = new FdoNotificationDaemon();
+ this._gtkNotificationDaemon = new GtkNotificationDaemon();
+ }
+};
diff --git a/js/ui/osdMonitorLabeler.js b/js/ui/osdMonitorLabeler.js
new file mode 100644
index 0000000..07c7d65
--- /dev/null
+++ b/js/ui/osdMonitorLabeler.js
@@ -0,0 +1,117 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported OsdMonitorLabeler */
+
+const { Clutter, Gio, GObject, Meta, St } = imports.gi;
+
+const Main = imports.ui.main;
+
+var OsdMonitorLabel = GObject.registerClass(
+class OsdMonitorLabel extends St.Widget {
+ _init(monitor, label) {
+ super._init({ x_expand: true, y_expand: true });
+
+ this._monitor = monitor;
+
+ this._box = new St.BoxLayout({
+ vertical: true,
+ });
+ this.add_actor(this._box);
+
+ this._label = new St.Label({
+ style_class: 'osd-monitor-label',
+ text: label,
+ });
+ this._box.add(this._label);
+
+ Main.uiGroup.add_child(this);
+ Main.uiGroup.set_child_above_sibling(this, null);
+ this._position();
+
+ Meta.disable_unredirect_for_display(global.display);
+ this.connect('destroy', () => {
+ Meta.enable_unredirect_for_display(global.display);
+ });
+ }
+
+ _position() {
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor);
+
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+ this._box.x = workArea.x + (workArea.width - this._box.width);
+ else
+ this._box.x = workArea.x;
+
+ this._box.y = workArea.y;
+ }
+});
+
+var OsdMonitorLabeler = class {
+ constructor() {
+ this._monitorManager = Meta.MonitorManager.get();
+ this._client = null;
+ this._clientWatchId = 0;
+ this._osdLabels = [];
+ this._monitorLabels = null;
+ Main.layoutManager.connect('monitors-changed',
+ this._reset.bind(this));
+ this._reset();
+ }
+
+ _reset() {
+ for (let i in this._osdLabels)
+ this._osdLabels[i].destroy();
+ this._osdLabels = [];
+ this._monitorLabels = new Map();
+ let monitors = Main.layoutManager.monitors;
+ for (let i in monitors)
+ this._monitorLabels.set(monitors[i].index, []);
+ }
+
+ _trackClient(client) {
+ if (this._client)
+ return this._client == client;
+
+ this._client = client;
+ this._clientWatchId = Gio.bus_watch_name(Gio.BusType.SESSION, client, 0, null,
+ (c, name) => {
+ this.hide(name);
+ });
+ return true;
+ }
+
+ _untrackClient(client) {
+ if (!this._client || this._client != client)
+ return false;
+
+ Gio.bus_unwatch_name(this._clientWatchId);
+ this._clientWatchId = 0;
+ this._client = null;
+ return true;
+ }
+
+ show(client, params) {
+ if (!this._trackClient(client))
+ return;
+
+ this._reset();
+
+ for (let connector in params) {
+ let monitor = this._monitorManager.get_monitor_for_connector(connector);
+ if (monitor == -1)
+ continue;
+ this._monitorLabels.get(monitor).push(params[connector].deepUnpack());
+ }
+
+ for (let [monitor, labels] of this._monitorLabels.entries()) {
+ labels.sort();
+ this._osdLabels.push(new OsdMonitorLabel(monitor, labels.join(' ')));
+ }
+ }
+
+ hide(client) {
+ if (!this._untrackClient(client))
+ return;
+
+ this._reset();
+ }
+};
diff --git a/js/ui/osdWindow.js b/js/ui/osdWindow.js
new file mode 100644
index 0000000..b183333
--- /dev/null
+++ b/js/ui/osdWindow.js
@@ -0,0 +1,192 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported OsdWindowManager */
+
+const { Clutter, GLib, GObject, Meta, St } = imports.gi;
+
+const BarLevel = imports.ui.barLevel;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+
+var HIDE_TIMEOUT = 1500;
+var FADE_TIME = 100;
+var LEVEL_ANIMATION_TIME = 100;
+
+var OsdWindow = GObject.registerClass(
+class OsdWindow extends Clutter.Actor {
+ _init(monitorIndex) {
+ super._init({
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.END,
+ });
+
+ this._monitorIndex = monitorIndex;
+ let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
+ this.add_constraint(constraint);
+
+ this._hbox = new St.BoxLayout({
+ style_class: 'osd-window',
+ });
+ this.add_actor(this._hbox);
+
+ this._icon = new St.Icon({ y_expand: true });
+ this._hbox.add_child(this._icon);
+
+ this._vbox = new St.BoxLayout({
+ vertical: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._hbox.add_child(this._vbox);
+
+ this._label = new St.Label();
+ this._vbox.add_child(this._label);
+
+ this._level = new BarLevel.BarLevel({
+ style_class: 'level',
+ value: 0,
+ });
+ this._vbox.add_child(this._level);
+
+ this._hideTimeoutId = 0;
+ this._reset();
+ Main.uiGroup.add_child(this);
+ }
+
+ _updateBoxVisibility() {
+ this._vbox.visible = [...this._vbox].some(c => c.visible);
+ }
+
+ setIcon(icon) {
+ this._icon.gicon = icon;
+ }
+
+ setLabel(label) {
+ this._label.visible = label != undefined;
+ if (label)
+ this._label.text = label;
+ this._updateBoxVisibility();
+ }
+
+ setLevel(value) {
+ this._level.visible = value != undefined;
+ if (value != undefined) {
+ if (this.visible) {
+ this._level.ease_property('value', value, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: LEVEL_ANIMATION_TIME,
+ });
+ } else {
+ this._level.value = value;
+ }
+ }
+ this._updateBoxVisibility();
+ }
+
+ setMaxLevel(maxLevel = 1) {
+ this._level.maximum_value = maxLevel;
+ }
+
+ show() {
+ if (!this._icon.gicon)
+ return;
+
+ if (!this.visible) {
+ Meta.disable_unredirect_for_display(global.display);
+ super.show();
+ this.opacity = 0;
+ this.get_parent().set_child_above_sibling(this, null);
+
+ this.ease({
+ opacity: 255,
+ duration: FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ if (this._hideTimeoutId)
+ GLib.source_remove(this._hideTimeoutId);
+ this._hideTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, HIDE_TIMEOUT, this._hide.bind(this));
+ GLib.Source.set_name_by_id(this._hideTimeoutId, '[gnome-shell] this._hide');
+ }
+
+ cancel() {
+ if (!this._hideTimeoutId)
+ return;
+
+ GLib.source_remove(this._hideTimeoutId);
+ this._hide();
+ }
+
+ _hide() {
+ this._hideTimeoutId = 0;
+ this.ease({
+ opacity: 0,
+ duration: FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._reset();
+ Meta.enable_unredirect_for_display(global.display);
+ },
+ });
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _reset() {
+ super.hide();
+ this.setLabel(null);
+ this.setMaxLevel(null);
+ this.setLevel(null);
+ }
+});
+
+var OsdWindowManager = class {
+ constructor() {
+ this._osdWindows = [];
+ Main.layoutManager.connect('monitors-changed',
+ this._monitorsChanged.bind(this));
+ this._monitorsChanged();
+ }
+
+ _monitorsChanged() {
+ for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
+ if (this._osdWindows[i] == undefined)
+ this._osdWindows[i] = new OsdWindow(i);
+ }
+
+ for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) {
+ this._osdWindows[i].destroy();
+ this._osdWindows[i] = null;
+ }
+
+ this._osdWindows.length = Main.layoutManager.monitors.length;
+ }
+
+ _showOsdWindow(monitorIndex, icon, label, level, maxLevel) {
+ this._osdWindows[monitorIndex].setIcon(icon);
+ this._osdWindows[monitorIndex].setLabel(label);
+ this._osdWindows[monitorIndex].setMaxLevel(maxLevel);
+ this._osdWindows[monitorIndex].setLevel(level);
+ this._osdWindows[monitorIndex].show();
+ }
+
+ show(monitorIndex, icon, label, level, maxLevel) {
+ if (monitorIndex != -1) {
+ for (let i = 0; i < this._osdWindows.length; i++) {
+ if (i == monitorIndex)
+ this._showOsdWindow(i, icon, label, level, maxLevel);
+ else
+ this._osdWindows[i].cancel();
+ }
+ } else {
+ for (let i = 0; i < this._osdWindows.length; i++)
+ this._showOsdWindow(i, icon, label, level, maxLevel);
+ }
+ }
+
+ hideAll() {
+ for (let i = 0; i < this._osdWindows.length; i++)
+ this._osdWindows[i].cancel();
+ }
+};
diff --git a/js/ui/overview.js b/js/ui/overview.js
new file mode 100644
index 0000000..757a8e4
--- /dev/null
+++ b/js/ui/overview.js
@@ -0,0 +1,715 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Overview, ANIMATION_TIME */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+const Signals = imports.misc.signals;
+
+// Time for initial animation going into Overview mode;
+// this is defined here to make it available in imports.
+var ANIMATION_TIME = 250;
+
+const DND = imports.ui.dnd;
+const LayoutManager = imports.ui.layout;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const OverviewControls = imports.ui.overviewControls;
+const Params = imports.misc.params;
+const SwipeTracker = imports.ui.swipeTracker;
+const WindowManager = imports.ui.windowManager;
+const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
+
+var DND_WINDOW_SWITCH_TIMEOUT = 750;
+
+var OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
+
+var ShellInfo = class {
+ constructor() {
+ this._source = null;
+ }
+
+ setMessage(text, options) {
+ options = Params.parse(options, {
+ undoCallback: null,
+ forFeedback: false,
+ });
+
+ let undoCallback = options.undoCallback;
+ let forFeedback = options.forFeedback;
+
+ if (this._source == null) {
+ this._source = new MessageTray.SystemNotificationSource();
+ this._source.connect('destroy', () => {
+ this._source = null;
+ });
+ Main.messageTray.add(this._source);
+ }
+
+ let notification = null;
+ if (this._source.notifications.length == 0) {
+ notification = new MessageTray.Notification(this._source, text, null);
+ notification.setTransient(true);
+ notification.setForFeedback(forFeedback);
+ } else {
+ notification = this._source.notifications[0];
+ notification.update(text, null, { clear: true });
+ }
+
+ if (undoCallback)
+ notification.addAction(_('Undo'), () => undoCallback());
+
+ this._source.showNotification(notification);
+ }
+};
+
+var OverviewActor = GObject.registerClass(
+class OverviewActor extends St.BoxLayout {
+ _init() {
+ super._init({
+ name: 'overview',
+ /* Translators: This is the main view to select
+ activities. See also note for "Activities" string. */
+ accessible_name: _("Overview"),
+ vertical: true,
+ });
+
+ this.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
+
+ this._controls = new OverviewControls.ControlsManager();
+ this.add_child(this._controls);
+ }
+
+ prepareToEnterOverview() {
+ this._controls.prepareToEnterOverview();
+ }
+
+ prepareToLeaveOverview() {
+ this._controls.prepareToLeaveOverview();
+ }
+
+ animateToOverview(state, callback) {
+ this._controls.animateToOverview(state, callback);
+ }
+
+ animateFromOverview(callback) {
+ this._controls.animateFromOverview(callback);
+ }
+
+ runStartupAnimation(callback) {
+ this._controls.runStartupAnimation(callback);
+ }
+
+ get dash() {
+ return this._controls.dash;
+ }
+
+ get searchEntry() {
+ return this._controls.searchEntry;
+ }
+
+ get controls() {
+ return this._controls;
+ }
+});
+
+const OverviewShownState = {
+ HIDDEN: 'HIDDEN',
+ HIDING: 'HIDING',
+ SHOWING: 'SHOWING',
+ SHOWN: 'SHOWN',
+};
+
+const OVERVIEW_SHOWN_TRANSITIONS = {
+ [OverviewShownState.HIDDEN]: {
+ signal: 'hidden',
+ allowedTransitions: [OverviewShownState.SHOWING],
+ },
+ [OverviewShownState.HIDING]: {
+ signal: 'hiding',
+ allowedTransitions:
+ [OverviewShownState.HIDDEN, OverviewShownState.SHOWING],
+ },
+ [OverviewShownState.SHOWING]: {
+ signal: 'showing',
+ allowedTransitions:
+ [OverviewShownState.SHOWN, OverviewShownState.HIDING],
+ },
+ [OverviewShownState.SHOWN]: {
+ signal: 'shown',
+ allowedTransitions: [OverviewShownState.HIDING],
+ },
+};
+
+var Overview = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._initCalled = false;
+ this._visible = false;
+
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ this._sessionUpdated();
+ }
+
+ get dash() {
+ return this._overview.dash;
+ }
+
+ get dashIconSize() {
+ logError(new Error('Usage of Overview.\'dashIconSize\' is deprecated, ' +
+ 'use \'dash.iconSize\' property instead'));
+ return this.dash.iconSize;
+ }
+
+ get animationInProgress() {
+ return this._animationInProgress;
+ }
+
+ get visible() {
+ return this._visible;
+ }
+
+ get visibleTarget() {
+ return this._visibleTarget;
+ }
+
+ get closing() {
+ return this._animationInProgress && !this._visibleTarget;
+ }
+
+ _createOverview() {
+ if (this._overview)
+ return;
+
+ if (this.isDummy)
+ return;
+
+ this._activationTime = 0;
+
+ this._visible = false; // animating to overview, in overview, animating out
+ this._shown = false; // show() and not hide()
+ this._modal = false; // have a modal grab
+ this._animationInProgress = false;
+ this._visibleTarget = false;
+ this._shownState = OverviewShownState.HIDDEN;
+
+ // During transitions, we raise this to the top to avoid having the overview
+ // area be reactive; it causes too many issues such as double clicks on
+ // Dash elements, or mouseover handlers in the workspaces.
+ this._coverPane = new Clutter.Actor({
+ opacity: 0,
+ reactive: true,
+ });
+ Main.layoutManager.overviewGroup.add_child(this._coverPane);
+ this._coverPane.connect('event', (_actor, event) => {
+ return event.type() === Clutter.EventType.ENTER ||
+ event.type() === Clutter.EventType.LEAVE
+ ? Clutter.EVENT_PROPAGATE : Clutter.EVENT_STOP;
+ });
+ this._coverPane.hide();
+
+ // XDND
+ this._dragMonitor = {
+ dragMotion: this._onDragMotion.bind(this),
+ };
+
+
+ Main.layoutManager.overviewGroup.connect('scroll-event',
+ this._onScrollEvent.bind(this));
+ Main.xdndHandler.connect('drag-begin', this._onDragBegin.bind(this));
+ Main.xdndHandler.connect('drag-end', this._onDragEnd.bind(this));
+
+ global.display.connect('restacked', this._onRestacked.bind(this));
+
+ this._windowSwitchTimeoutId = 0;
+ this._windowSwitchTimestamp = 0;
+ this._lastActiveWorkspaceIndex = -1;
+ this._lastHoveredWindow = null;
+
+ if (this._initCalled)
+ this.init();
+ }
+
+ _sessionUpdated() {
+ const { hasOverview } = Main.sessionMode;
+ if (!hasOverview)
+ this.hide();
+
+ this.isDummy = !hasOverview;
+ this._createOverview();
+ }
+
+ // The members we construct that are implemented in JS might
+ // want to access the overview as Main.overview to connect
+ // signal handlers and so forth. So we create them after
+ // construction in this init() method.
+ init() {
+ this._initCalled = true;
+
+ if (this.isDummy)
+ return;
+
+ this._overview = new OverviewActor();
+ this._overview._delegate = this;
+ Main.layoutManager.overviewGroup.add_child(this._overview);
+
+ this._shellInfo = new ShellInfo();
+
+ Main.layoutManager.connect('monitors-changed', this._relayout.bind(this));
+ this._relayout();
+
+ Main.wm.addKeybinding(
+ 'toggle-overview',
+ new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ this.toggle.bind(this));
+
+ const swipeTracker = new SwipeTracker.SwipeTracker(global.stage,
+ Clutter.Orientation.VERTICAL,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ { allowDrag: false, allowScroll: false });
+ swipeTracker.orientation = Clutter.Orientation.VERTICAL;
+ swipeTracker.connect('begin', this._gestureBegin.bind(this));
+ swipeTracker.connect('update', this._gestureUpdate.bind(this));
+ swipeTracker.connect('end', this._gestureEnd.bind(this));
+ this._swipeTracker = swipeTracker;
+ }
+
+ //
+ // options:
+ // - undoCallback (function): the callback to be called if undo support is needed
+ // - forFeedback (boolean): whether the message is for direct feedback of a user action
+ //
+ setMessage(text, options) {
+ if (this.isDummy)
+ return;
+
+ this._shellInfo.setMessage(text, options);
+ }
+
+ _changeShownState(state) {
+ const {allowedTransitions} =
+ OVERVIEW_SHOWN_TRANSITIONS[this._shownState];
+
+ if (!allowedTransitions.includes(state)) {
+ throw new Error('Invalid overview shown transition from ' +
+ `${this._shownState} to ${state}`);
+ }
+
+ this._shownState = state;
+ this.emit(OVERVIEW_SHOWN_TRANSITIONS[state].signal);
+ }
+
+ _onDragBegin() {
+ this._inXdndDrag = true;
+
+ DND.addDragMonitor(this._dragMonitor);
+ // Remember the workspace we started from
+ let workspaceManager = global.workspace_manager;
+ this._lastActiveWorkspaceIndex = workspaceManager.get_active_workspace_index();
+ }
+
+ _onDragEnd() {
+ this._inXdndDrag = false;
+
+ // In case the drag was canceled while in the overview
+ // we have to go back to where we started and hide
+ // the overview
+ if (this._shown) {
+ let workspaceManager = global.workspace_manager;
+ workspaceManager.get_workspace_by_index(this._lastActiveWorkspaceIndex)
+ .activate(global.get_current_time());
+ this.hide();
+ }
+ this._resetWindowSwitchTimeout();
+ this._lastHoveredWindow = null;
+ DND.removeDragMonitor(this._dragMonitor);
+ this.endItemDrag();
+ }
+
+ _resetWindowSwitchTimeout() {
+ if (this._windowSwitchTimeoutId != 0) {
+ GLib.source_remove(this._windowSwitchTimeoutId);
+ this._windowSwitchTimeoutId = 0;
+ }
+ }
+
+ _onDragMotion(dragEvent) {
+ let targetIsWindow = dragEvent.targetActor &&
+ dragEvent.targetActor._delegate &&
+ dragEvent.targetActor._delegate.metaWindow &&
+ !(dragEvent.targetActor._delegate instanceof WorkspaceThumbnail.WindowClone);
+
+ this._windowSwitchTimestamp = global.get_current_time();
+
+ if (targetIsWindow &&
+ dragEvent.targetActor._delegate.metaWindow == this._lastHoveredWindow)
+ return DND.DragMotionResult.CONTINUE;
+
+ this._lastHoveredWindow = null;
+
+ this._resetWindowSwitchTimeout();
+
+ if (targetIsWindow) {
+ this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
+ this._windowSwitchTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ DND_WINDOW_SWITCH_TIMEOUT,
+ () => {
+ this._windowSwitchTimeoutId = 0;
+ Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
+ this._windowSwitchTimestamp);
+ this.hide();
+ this._lastHoveredWindow = null;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._windowSwitchTimeoutId, '[gnome-shell] Main.activateWindow');
+ }
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ _onScrollEvent(actor, event) {
+ this.emit('scroll-event', event);
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _relayout() {
+ // To avoid updating the position and size of the workspaces
+ // we just hide the overview. The positions will be updated
+ // when it is next shown.
+ this.hide();
+
+ this._coverPane.set_position(0, 0);
+ this._coverPane.set_size(global.screen_width, global.screen_height);
+ }
+
+ _onRestacked() {
+ let stack = global.get_window_actors();
+ let stackIndices = {};
+
+ for (let i = 0; i < stack.length; i++) {
+ // Use the stable sequence for an integer to use as a hash key
+ stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
+ }
+
+ this.emit('windows-restacked', stackIndices);
+ }
+
+ _gestureBegin(tracker) {
+ this._overview.controls.gestureBegin(tracker);
+ }
+
+ _gestureUpdate(tracker, progress) {
+ if (!this._shown) {
+ Meta.disable_unredirect_for_display(global.display);
+
+ this._shown = true;
+ this._visible = true;
+ this._visibleTarget = true;
+ this._animationInProgress = true;
+
+ Main.layoutManager.overviewGroup.set_child_above_sibling(
+ this._coverPane, null);
+ this._coverPane.show();
+ this._changeShownState(OverviewShownState.SHOWING);
+
+ Main.layoutManager.showOverview();
+ this._syncGrab();
+ }
+
+ this._overview.controls.gestureProgress(progress);
+ }
+
+ _gestureEnd(tracker, duration, endProgress) {
+ let onComplete;
+ if (endProgress === 0) {
+ this._shown = false;
+ this._visibleTarget = false;
+ this._changeShownState(OverviewShownState.HIDING);
+ Main.panel.style = `transition-duration: ${duration}ms;`;
+ onComplete = () => this._hideDone();
+ } else {
+ onComplete = () => this._showDone();
+ }
+
+ this._overview.controls.gestureEnd(endProgress, duration, onComplete);
+ }
+
+ beginItemDrag(source) {
+ this.emit('item-drag-begin', source);
+ this._inItemDrag = true;
+ }
+
+ cancelledItemDrag(source) {
+ this.emit('item-drag-cancelled', source);
+ }
+
+ endItemDrag(source) {
+ if (!this._inItemDrag)
+ return;
+ this.emit('item-drag-end', source);
+ this._inItemDrag = false;
+ }
+
+ beginWindowDrag(window) {
+ this.emit('window-drag-begin', window);
+ this._inWindowDrag = true;
+ }
+
+ cancelledWindowDrag(window) {
+ this.emit('window-drag-cancelled', window);
+ }
+
+ endWindowDrag(window) {
+ if (!this._inWindowDrag)
+ return;
+ this.emit('window-drag-end', window);
+ this._inWindowDrag = false;
+ }
+
+ focusSearch() {
+ this.show();
+ this._overview.searchEntry.grab_key_focus();
+ }
+
+ // Checks if the Activities button is currently sensitive to
+ // clicks. The first call to this function within the
+ // OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
+ // triggered will return false. This avoids opening and closing
+ // the overview if the user both triggered the hot corner and
+ // clicked the Activities button.
+ shouldToggleByCornerOrButton() {
+ if (this._animationInProgress)
+ return false;
+ if (this._inItemDrag || this._inWindowDrag)
+ return false;
+ if (!this._activationTime ||
+ GLib.get_monotonic_time() / GLib.USEC_PER_SEC - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
+ return true;
+ return false;
+ }
+
+ _syncGrab() {
+ // We delay grab changes during animation so that when removing the
+ // overview we don't have a problem with the release of a press/release
+ // going to an application.
+ if (this._animationInProgress)
+ return true;
+
+ if (this._shown) {
+ let shouldBeModal = !this._inXdndDrag;
+ if (shouldBeModal && !this._modal) {
+ if (global.display.get_grab_op() !== Meta.GrabOp.NONE &&
+ global.display.get_grab_op() !== Meta.GrabOp.WAYLAND_POPUP) {
+ this.hide();
+ return false;
+ }
+
+ const grab = Main.pushModal(global.stage, {
+ actionMode: Shell.ActionMode.OVERVIEW,
+ });
+ if (grab.get_seat_state() !== Clutter.GrabState.ALL) {
+ Main.popModal(grab);
+ this.hide();
+ return false;
+ }
+
+ this._grab = grab;
+ this._modal = true;
+ }
+ } else {
+ // eslint-disable-next-line no-lonely-if
+ if (this._modal) {
+ Main.popModal(this._grab);
+ this._grab = false;
+ this._modal = false;
+ }
+ }
+ return true;
+ }
+
+ // show:
+ //
+ // Animates the overview visible and grabs mouse and keyboard input
+ show(state = OverviewControls.ControlsState.WINDOW_PICKER) {
+ if (state === OverviewControls.ControlsState.HIDDEN)
+ throw new Error('Invalid state, use hide() to hide');
+
+ if (this.isDummy)
+ return;
+ if (this._shown)
+ return;
+ this._shown = true;
+
+ if (!this._syncGrab())
+ return;
+
+ Main.layoutManager.showOverview();
+ this._animateVisible(state);
+ }
+
+
+ _animateVisible(state) {
+ if (this._visible || this._animationInProgress)
+ return;
+
+ this._visible = true;
+ this._animationInProgress = true;
+ this._visibleTarget = true;
+ this._activationTime = GLib.get_monotonic_time() / GLib.USEC_PER_SEC;
+
+ Meta.disable_unredirect_for_display(global.display);
+
+ Main.layoutManager.overviewGroup.set_child_above_sibling(
+ this._coverPane, null);
+ this._coverPane.show();
+
+ this._overview.prepareToEnterOverview();
+ this._changeShownState(OverviewShownState.SHOWING);
+ this._overview.animateToOverview(state, () => this._showDone());
+ }
+
+ _showDone() {
+ this._animationInProgress = false;
+ this._coverPane.hide();
+
+ if (this._shownState !== OverviewShownState.SHOWN)
+ this._changeShownState(OverviewShownState.SHOWN);
+
+ // Handle any calls to hide* while we were showing
+ if (!this._shown)
+ this._animateNotVisible();
+
+ this._syncGrab();
+ }
+
+ // hide:
+ //
+ // Reverses the effect of show()
+ hide() {
+ if (this.isDummy)
+ return;
+
+ if (!this._shown)
+ return;
+
+ let event = Clutter.get_current_event();
+ if (event) {
+ let type = event.type();
+ let button = type == Clutter.EventType.BUTTON_PRESS ||
+ type == Clutter.EventType.BUTTON_RELEASE;
+ let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
+ if (button && ctrl)
+ return;
+ }
+
+ this._shown = false;
+
+ this._animateNotVisible();
+ this._syncGrab();
+ }
+
+ _animateNotVisible() {
+ if (!this._visible || this._animationInProgress)
+ return;
+
+ this._animationInProgress = true;
+ this._visibleTarget = false;
+
+ Main.layoutManager.overviewGroup.set_child_above_sibling(
+ this._coverPane, null);
+ this._coverPane.show();
+
+ this._overview.prepareToLeaveOverview();
+ this._changeShownState(OverviewShownState.HIDING);
+ this._overview.animateFromOverview(() => this._hideDone());
+ }
+
+ _hideDone() {
+ // Re-enable unredirection
+ Meta.enable_unredirect_for_display(global.display);
+
+ this._coverPane.hide();
+
+ this._visible = false;
+ this._animationInProgress = false;
+
+ // Handle any calls to show* while we were hiding
+ if (this._shown) {
+ this._changeShownState(OverviewShownState.HIDDEN);
+ this._animateVisible(OverviewControls.ControlsState.WINDOW_PICKER);
+ } else {
+ Main.layoutManager.hideOverview();
+ this._changeShownState(OverviewShownState.HIDDEN);
+ }
+
+ Main.panel.style = null;
+
+ this._syncGrab();
+ }
+
+ toggle() {
+ if (this.isDummy)
+ return;
+
+ if (this._visible)
+ this.hide();
+ else
+ this.show();
+ }
+
+ showApps() {
+ this.show(OverviewControls.ControlsState.APP_GRID);
+ }
+
+ selectApp(id) {
+ this.showApps();
+ this._overview.controls.appDisplay.selectApp(id);
+ }
+
+ runStartupAnimation(callback) {
+ Main.panel.style = 'transition-duration: 0ms;';
+
+ this._shown = true;
+ this._visible = true;
+ this._visibleTarget = true;
+ Main.layoutManager.showOverview();
+ // We should call this._syncGrab() here, but moved it to happen after
+ // the animation because of a race in the xserver where the grab
+ // fails when requested very early during startup.
+
+ Meta.disable_unredirect_for_display(global.display);
+
+ this._changeShownState(OverviewShownState.SHOWING);
+
+ this._overview.runStartupAnimation(() => {
+ // Overview got hidden during startup animation
+ if (this._shownState !== OverviewShownState.SHOWING) {
+ callback();
+ return;
+ }
+
+ if (!this._syncGrab()) {
+ callback();
+ this.hide();
+ return;
+ }
+
+ Main.panel.style = null;
+ this._changeShownState(OverviewShownState.SHOWN);
+ callback();
+ });
+ }
+
+ getShowAppsButton() {
+ logError(new Error('Usage of Overview.\'getShowAppsButton\' is deprecated, ' +
+ 'use \'dash.showAppsButton\' property instead'));
+
+ return this.dash.showAppsButton;
+ }
+
+ get searchEntry() {
+ return this._overview.searchEntry;
+ }
+};
diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js
new file mode 100644
index 0000000..29aac35
--- /dev/null
+++ b/js/ui/overviewControls.js
@@ -0,0 +1,867 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ControlsManager */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const AppDisplay = imports.ui.appDisplay;
+const Dash = imports.ui.dash;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const Overview = imports.ui.overview;
+const SearchController = imports.ui.searchController;
+const Util = imports.misc.util;
+const WindowManager = imports.ui.windowManager;
+const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
+const WorkspacesView = imports.ui.workspacesView;
+
+const SMALL_WORKSPACE_RATIO = 0.15;
+const DASH_MAX_HEIGHT_RATIO = 0.15;
+
+const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
+
+var SIDE_CONTROLS_ANIMATION_TIME = Overview.ANIMATION_TIME;
+
+var ControlsState = {
+ HIDDEN: 0,
+ WINDOW_PICKER: 1,
+ APP_GRID: 2,
+};
+
+var ControlsManagerLayout = GObject.registerClass(
+class ControlsManagerLayout extends Clutter.BoxLayout {
+ _init(searchEntry, appDisplay, workspacesDisplay, workspacesThumbnails,
+ searchController, dash, stateAdjustment) {
+ super._init({ orientation: Clutter.Orientation.VERTICAL });
+
+ this._appDisplay = appDisplay;
+ this._workspacesDisplay = workspacesDisplay;
+ this._workspacesThumbnails = workspacesThumbnails;
+ this._stateAdjustment = stateAdjustment;
+ this._searchEntry = searchEntry;
+ this._searchController = searchController;
+ this._dash = dash;
+
+ this._cachedWorkspaceBoxes = new Map();
+ this._postAllocationCallbacks = [];
+
+ stateAdjustment.connect('notify::value', () => this.layout_changed());
+
+ this._workAreaBox = new Clutter.ActorBox();
+ global.display.connectObject(
+ 'workareas-changed', () => this._updateWorkAreaBox(),
+ this);
+ this._updateWorkAreaBox();
+ }
+
+ _updateWorkAreaBox() {
+ const monitor = Main.layoutManager.primaryMonitor;
+ if (!monitor)
+ return;
+
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
+ const startX = workArea.x - monitor.x;
+ const startY = workArea.y - monitor.y;
+ this._workAreaBox.set_origin(startX, startY);
+ this._workAreaBox.set_size(workArea.width, workArea.height);
+ }
+
+ _computeWorkspacesBoxForState(state, box, searchHeight, dashHeight, thumbnailsHeight) {
+ const workspaceBox = box.copy();
+ const [width, height] = workspaceBox.get_size();
+ const {y1: startY} = this._workAreaBox;
+ const {spacing} = this;
+ const {expandFraction} = this._workspacesThumbnails;
+
+ switch (state) {
+ case ControlsState.HIDDEN:
+ workspaceBox.set_origin(...this._workAreaBox.get_origin());
+ workspaceBox.set_size(...this._workAreaBox.get_size());
+ break;
+ case ControlsState.WINDOW_PICKER:
+ workspaceBox.set_origin(0,
+ startY + searchHeight + spacing +
+ thumbnailsHeight + spacing * expandFraction);
+ workspaceBox.set_size(width,
+ height -
+ dashHeight - spacing -
+ searchHeight - spacing -
+ thumbnailsHeight - spacing * expandFraction);
+ break;
+ case ControlsState.APP_GRID:
+ workspaceBox.set_origin(0, startY + searchHeight + spacing);
+ workspaceBox.set_size(
+ width,
+ Math.round(height * SMALL_WORKSPACE_RATIO));
+ break;
+ }
+
+ return workspaceBox;
+ }
+
+ _getAppDisplayBoxForState(state, box, searchHeight, dashHeight, appGridBox) {
+ const [width, height] = box.get_size();
+ const {y1: startY} = this._workAreaBox;
+ const appDisplayBox = new Clutter.ActorBox();
+ const {spacing} = this;
+
+ switch (state) {
+ case ControlsState.HIDDEN:
+ case ControlsState.WINDOW_PICKER:
+ appDisplayBox.set_origin(0, box.y2);
+ break;
+ case ControlsState.APP_GRID:
+ appDisplayBox.set_origin(0,
+ startY + searchHeight + spacing + appGridBox.get_height());
+ break;
+ }
+
+ appDisplayBox.set_size(width,
+ height -
+ searchHeight - spacing -
+ appGridBox.get_height() - spacing -
+ dashHeight);
+
+ return appDisplayBox;
+ }
+
+ _runPostAllocation() {
+ if (this._postAllocationCallbacks.length === 0)
+ return;
+
+ this._postAllocationCallbacks.forEach(cb => cb());
+ this._postAllocationCallbacks = [];
+ }
+
+ vfunc_set_container(container) {
+ this._container = container;
+ if (container)
+ this.hookup_style(container);
+ }
+
+ vfunc_get_preferred_width(_container, _forHeight) {
+ // The MonitorConstraint will allocate us a fixed size anyway
+ return [0, 0];
+ }
+
+ vfunc_get_preferred_height(_container, _forWidth) {
+ // The MonitorConstraint will allocate us a fixed size anyway
+ return [0, 0];
+ }
+
+ vfunc_allocate(container, box) {
+ const childBox = new Clutter.ActorBox();
+
+ const { spacing } = this;
+
+ const startY = this._workAreaBox.y1;
+ box.y1 += startY;
+ const [width, height] = box.get_size();
+ let availableHeight = height;
+
+ // Search entry
+ let [searchHeight] = this._searchEntry.get_preferred_height(width);
+ childBox.set_origin(0, startY);
+ childBox.set_size(width, searchHeight);
+ this._searchEntry.allocate(childBox);
+
+ availableHeight -= searchHeight + spacing;
+
+ // Dash
+ const maxDashHeight = Math.round(box.get_height() * DASH_MAX_HEIGHT_RATIO);
+ this._dash.setMaxSize(width, maxDashHeight);
+
+ let [, dashHeight] = this._dash.get_preferred_height(width);
+ dashHeight = Math.min(dashHeight, maxDashHeight);
+ childBox.set_origin(0, startY + height - dashHeight);
+ childBox.set_size(width, dashHeight);
+ this._dash.allocate(childBox);
+
+ availableHeight -= dashHeight + spacing;
+
+ // Workspace Thumbnails
+ let thumbnailsHeight = 0;
+ if (this._workspacesThumbnails.visible) {
+ const { expandFraction } = this._workspacesThumbnails;
+ [thumbnailsHeight] =
+ this._workspacesThumbnails.get_preferred_height(width);
+ thumbnailsHeight = Math.min(
+ thumbnailsHeight * expandFraction,
+ height * WorkspaceThumbnail.MAX_THUMBNAIL_SCALE);
+ childBox.set_origin(0, startY + searchHeight + spacing);
+ childBox.set_size(width, thumbnailsHeight);
+ this._workspacesThumbnails.allocate(childBox);
+ }
+
+ // Workspaces
+ let params = [box, searchHeight, dashHeight, thumbnailsHeight];
+ const transitionParams = this._stateAdjustment.getStateTransitionParams();
+
+ // Update cached boxes
+ for (const state of Object.values(ControlsState)) {
+ this._cachedWorkspaceBoxes.set(
+ state, this._computeWorkspacesBoxForState(state, ...params));
+ }
+
+ let workspacesBox;
+ if (!transitionParams.transitioning) {
+ workspacesBox = this._cachedWorkspaceBoxes.get(transitionParams.currentState);
+ } else {
+ const initialBox = this._cachedWorkspaceBoxes.get(transitionParams.initialState);
+ const finalBox = this._cachedWorkspaceBoxes.get(transitionParams.finalState);
+ workspacesBox = initialBox.interpolate(finalBox, transitionParams.progress);
+ }
+
+ this._workspacesDisplay.allocate(workspacesBox);
+
+ // AppDisplay
+ if (this._appDisplay.visible) {
+ const workspaceAppGridBox =
+ this._cachedWorkspaceBoxes.get(ControlsState.APP_GRID);
+
+ params = [box, searchHeight, dashHeight, workspaceAppGridBox];
+ let appDisplayBox;
+ if (!transitionParams.transitioning) {
+ appDisplayBox =
+ this._getAppDisplayBoxForState(transitionParams.currentState, ...params);
+ } else {
+ const initialBox =
+ this._getAppDisplayBoxForState(transitionParams.initialState, ...params);
+ const finalBox =
+ this._getAppDisplayBoxForState(transitionParams.finalState, ...params);
+
+ appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
+ }
+
+ this._appDisplay.allocate(appDisplayBox);
+ }
+
+ // Search
+ childBox.set_origin(0, startY + searchHeight + spacing);
+ childBox.set_size(width, availableHeight);
+
+ this._searchController.allocate(childBox);
+
+ this._runPostAllocation();
+ }
+
+ ensureAllocation() {
+ this.layout_changed();
+ return new Promise(
+ resolve => this._postAllocationCallbacks.push(resolve));
+ }
+
+ getWorkspacesBoxForState(state) {
+ return this._cachedWorkspaceBoxes.get(state);
+ }
+});
+
+var OverviewAdjustment = GObject.registerClass({
+ Properties: {
+ 'gesture-in-progress': GObject.ParamSpec.boolean(
+ 'gesture-in-progress', 'Gesture in progress', 'Gesture in progress',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+}, class OverviewAdjustment extends St.Adjustment {
+ _init(actor) {
+ super._init({
+ actor,
+ value: ControlsState.WINDOW_PICKER,
+ lower: ControlsState.HIDDEN,
+ upper: ControlsState.APP_GRID,
+ });
+ }
+
+ getStateTransitionParams() {
+ const currentState = this.value;
+
+ const transition = this.get_transition('value');
+ let initialState = transition
+ ? transition.get_interval().peek_initial_value()
+ : currentState;
+ let finalState = transition
+ ? transition.get_interval().peek_final_value()
+ : currentState;
+
+ if (initialState > finalState) {
+ initialState = Math.ceil(initialState);
+ finalState = Math.floor(finalState);
+ } else {
+ initialState = Math.floor(initialState);
+ finalState = Math.ceil(finalState);
+ }
+
+ const length = Math.abs(finalState - initialState);
+ const progress = length > 0
+ ? Math.abs((currentState - initialState) / length)
+ : 1;
+
+ return {
+ transitioning: transition !== null || this.gestureInProgress,
+ currentState,
+ initialState,
+ finalState,
+ progress,
+ };
+ }
+});
+
+var ControlsManager = GObject.registerClass(
+class ControlsManager extends St.Widget {
+ _init() {
+ super._init({
+ style_class: 'controls-manager',
+ x_expand: true,
+ y_expand: true,
+ clip_to_allocation: true,
+ });
+
+ this._ignoreShowAppsButtonToggle = false;
+
+ this._searchEntry = new St.Entry({
+ style_class: 'search-entry',
+ /* Translators: this is the text displayed
+ in the search entry when no search is
+ active; it should not exceed ~30
+ characters. */
+ hint_text: _('Type to search'),
+ track_hover: true,
+ can_focus: true,
+ });
+ this._searchEntry.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
+ this._searchEntryBin = new St.Bin({
+ child: this._searchEntry,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this.dash = new Dash.Dash();
+
+ let workspaceManager = global.workspace_manager;
+ let activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
+
+ this._workspaceAdjustment = new St.Adjustment({
+ actor: this,
+ value: activeWorkspaceIndex,
+ lower: 0,
+ page_increment: 1,
+ page_size: 1,
+ step_increment: 0,
+ upper: workspaceManager.n_workspaces,
+ });
+
+ this._stateAdjustment = new OverviewAdjustment(this);
+ this._stateAdjustment.connect('notify::value', this._update.bind(this));
+
+ workspaceManager.connectObject(
+ 'notify::n-workspaces', () => this._updateAdjustment(), this);
+
+ this._searchController = new SearchController.SearchController(
+ this._searchEntry,
+ this.dash.showAppsButton);
+ this._searchController.connect('notify::search-active', this._onSearchChanged.bind(this));
+
+ Main.layoutManager.connect('monitors-changed', () => {
+ this._thumbnailsBox.setMonitorIndex(Main.layoutManager.primaryIndex);
+ });
+ this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox(
+ this._workspaceAdjustment, Main.layoutManager.primaryIndex);
+ this._thumbnailsBox.connect('notify::should-show', () => {
+ this._thumbnailsBox.show();
+ this._thumbnailsBox.ease_property('expand-fraction',
+ this._thumbnailsBox.should_show ? 1 : 0, {
+ duration: SIDE_CONTROLS_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._updateThumbnailsBox(),
+ });
+ });
+
+ this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(
+ this,
+ this._workspaceAdjustment,
+ this._stateAdjustment);
+ this._appDisplay = new AppDisplay.AppDisplay();
+
+ this.add_child(this._searchEntryBin);
+ this.add_child(this._appDisplay);
+ this.add_child(this.dash);
+ this.add_child(this._searchController);
+ this.add_child(this._thumbnailsBox);
+ this.add_child(this._workspacesDisplay);
+
+ this.layout_manager = new ControlsManagerLayout(
+ this._searchEntryBin,
+ this._appDisplay,
+ this._workspacesDisplay,
+ this._thumbnailsBox,
+ this._searchController,
+ this.dash,
+ this._stateAdjustment);
+
+ this.dash.showAppsButton.connect('notify::checked',
+ this._onShowAppsButtonToggled.bind(this));
+
+ Main.ctrlAltTabManager.addGroup(
+ this.appDisplay,
+ _('Applications'),
+ 'view-app-grid-symbolic', {
+ proxy: this,
+ focusCallback: () => {
+ this.dash.showAppsButton.checked = true;
+ this.appDisplay.navigate_focus(
+ null, St.DirectionType.TAB_FORWARD, false);
+ },
+ });
+
+ Main.ctrlAltTabManager.addGroup(
+ this._workspacesDisplay,
+ _('Windows'),
+ 'focus-windows-symbolic', {
+ proxy: this,
+ focusCallback: () => {
+ this.dash.showAppsButton.checked = false;
+ this._workspacesDisplay.navigate_focus(
+ null, St.DirectionType.TAB_FORWARD, false);
+ },
+ });
+
+ this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
+
+ this._lastOverlayKeyTime = 0;
+ global.display.connect('overlay-key', () => {
+ if (this._a11ySettings.get_boolean('stickykeys-enable'))
+ return;
+
+ const { initialState, finalState, transitioning } =
+ this._stateAdjustment.getStateTransitionParams();
+
+ const time = GLib.get_monotonic_time() / 1000;
+ const timeDiff = time - this._lastOverlayKeyTime;
+ this._lastOverlayKeyTime = time;
+
+ const shouldShift = St.Settings.get().enable_animations
+ ? transitioning && finalState > initialState
+ : Main.overview.visible && timeDiff < Overview.ANIMATION_TIME;
+
+ if (shouldShift)
+ this._shiftState(Meta.MotionDirection.UP);
+ else
+ Main.overview.toggle();
+ });
+
+ // connect_after to give search controller first dibs on the event
+ global.stage.connect_after('key-press-event', (actor, event) => {
+ if (this._searchController.searchActive)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (global.stage.key_focus &&
+ !this.contains(global.stage.key_focus))
+ return Clutter.EVENT_PROPAGATE;
+
+ const { finalState } =
+ this._stateAdjustment.getStateTransitionParams();
+ let keynavDisplay;
+
+ if (finalState === ControlsState.WINDOW_PICKER)
+ keynavDisplay = this._workspacesDisplay;
+ else if (finalState === ControlsState.APP_GRID)
+ keynavDisplay = this._appDisplay;
+
+ if (!keynavDisplay)
+ return Clutter.EVENT_PROPAGATE;
+
+ const symbol = event.get_key_symbol();
+ if (symbol === Clutter.KEY_Tab || symbol === Clutter.KEY_Down) {
+ keynavDisplay.navigate_focus(
+ null, St.DirectionType.TAB_FORWARD, false);
+ return Clutter.EVENT_STOP;
+ } else if (symbol === Clutter.KEY_ISO_Left_Tab) {
+ keynavDisplay.navigate_focus(
+ null, St.DirectionType.TAB_BACKWARD, false);
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ });
+
+ Main.wm.addKeybinding(
+ 'toggle-application-view',
+ new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ this._toggleAppsPage.bind(this));
+
+ Main.wm.addKeybinding('shift-overview-up',
+ new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ () => this._shiftState(Meta.MotionDirection.UP));
+
+ Main.wm.addKeybinding('shift-overview-down',
+ new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ () => this._shiftState(Meta.MotionDirection.DOWN));
+
+ this._update();
+ }
+
+ _getFitModeForState(state) {
+ switch (state) {
+ case ControlsState.HIDDEN:
+ case ControlsState.WINDOW_PICKER:
+ return WorkspacesView.FitMode.SINGLE;
+ case ControlsState.APP_GRID:
+ return WorkspacesView.FitMode.ALL;
+ default:
+ return WorkspacesView.FitMode.SINGLE;
+ }
+ }
+
+ _getThumbnailsBoxParams() {
+ const { initialState, finalState, progress } =
+ this._stateAdjustment.getStateTransitionParams();
+
+ const paramsForState = s => {
+ let opacity, scale, translationY;
+ switch (s) {
+ case ControlsState.HIDDEN:
+ case ControlsState.WINDOW_PICKER:
+ opacity = 255;
+ scale = 1;
+ translationY = 0;
+ break;
+ case ControlsState.APP_GRID:
+ opacity = 0;
+ scale = 0.5;
+ translationY = this._thumbnailsBox.height / 2;
+ break;
+ default:
+ opacity = 255;
+ scale = 1;
+ translationY = 0;
+ break;
+ }
+
+ return { opacity, scale, translationY };
+ };
+
+ const initialParams = paramsForState(initialState);
+ const finalParams = paramsForState(finalState);
+
+ return [
+ Util.lerp(initialParams.opacity, finalParams.opacity, progress),
+ Util.lerp(initialParams.scale, finalParams.scale, progress),
+ Util.lerp(initialParams.translationY, finalParams.translationY, progress),
+ ];
+ }
+
+ _updateThumbnailsBox(animate = false) {
+ const { shouldShow } = this._thumbnailsBox;
+ const { searchActive } = this._searchController;
+ const [opacity, scale, translationY] = this._getThumbnailsBoxParams();
+
+ const thumbnailsBoxVisible = shouldShow && !searchActive && opacity !== 0;
+ if (thumbnailsBoxVisible) {
+ this._thumbnailsBox.opacity = 0;
+ this._thumbnailsBox.visible = thumbnailsBoxVisible;
+ }
+
+ const params = {
+ opacity: searchActive ? 0 : opacity,
+ duration: animate ? SIDE_CONTROLS_ANIMATION_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => (this._thumbnailsBox.visible = thumbnailsBoxVisible),
+ };
+
+ if (!searchActive) {
+ params.scale_x = scale;
+ params.scale_y = scale;
+ params.translation_y = translationY;
+ }
+
+ this._thumbnailsBox.ease(params);
+ }
+
+ _updateAppDisplayVisibility(stateTransitionParams = null) {
+ if (!stateTransitionParams)
+ stateTransitionParams = this._stateAdjustment.getStateTransitionParams();
+
+ const { initialState, finalState } = stateTransitionParams;
+ const state = Math.max(initialState, finalState);
+
+ this._appDisplay.visible =
+ state > ControlsState.WINDOW_PICKER &&
+ !this._searchController.searchActive;
+ }
+
+ _update() {
+ const params = this._stateAdjustment.getStateTransitionParams();
+
+ const fitMode = Util.lerp(
+ this._getFitModeForState(params.initialState),
+ this._getFitModeForState(params.finalState),
+ params.progress);
+
+ const { fitModeAdjustment } = this._workspacesDisplay;
+ fitModeAdjustment.value = fitMode;
+
+ this._updateThumbnailsBox();
+ this._updateAppDisplayVisibility(params);
+ }
+
+ _onSearchChanged() {
+ const { searchActive } = this._searchController;
+
+ if (!searchActive) {
+ this._updateAppDisplayVisibility();
+ this._workspacesDisplay.reactive = true;
+ this._workspacesDisplay.setPrimaryWorkspaceVisible(true);
+ } else {
+ this._searchController.show();
+ }
+
+ this._updateThumbnailsBox(true);
+
+ this._appDisplay.ease({
+ opacity: searchActive ? 0 : 255,
+ duration: SIDE_CONTROLS_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._updateAppDisplayVisibility(),
+ });
+ this._workspacesDisplay.ease({
+ opacity: searchActive ? 0 : 255,
+ duration: SIDE_CONTROLS_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._workspacesDisplay.reactive = !searchActive;
+ this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive);
+ },
+ });
+ this._searchController.ease({
+ opacity: searchActive ? 255 : 0,
+ duration: SIDE_CONTROLS_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => (this._searchController.visible = searchActive),
+ });
+ }
+
+ _onShowAppsButtonToggled() {
+ if (this._ignoreShowAppsButtonToggle)
+ return;
+
+ const checked = this.dash.showAppsButton.checked;
+
+ const value = checked
+ ? ControlsState.APP_GRID : ControlsState.WINDOW_PICKER;
+ this._stateAdjustment.remove_transition('value');
+ this._stateAdjustment.ease(value, {
+ duration: SIDE_CONTROLS_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ _toggleAppsPage() {
+ if (Main.overview.visible) {
+ const checked = this.dash.showAppsButton.checked;
+ this.dash.showAppsButton.checked = !checked;
+ } else {
+ Main.overview.show(ControlsState.APP_GRID);
+ }
+ }
+
+ _shiftState(direction) {
+ let { currentState, finalState } = this._stateAdjustment.getStateTransitionParams();
+
+ if (direction === Meta.MotionDirection.DOWN)
+ finalState = Math.max(finalState - 1, ControlsState.HIDDEN);
+ else if (direction === Meta.MotionDirection.UP)
+ finalState = Math.min(finalState + 1, ControlsState.APP_GRID);
+
+ if (finalState === currentState)
+ return;
+
+ if (currentState === ControlsState.HIDDEN &&
+ finalState === ControlsState.WINDOW_PICKER) {
+ Main.overview.show();
+ } else if (finalState === ControlsState.HIDDEN) {
+ Main.overview.hide();
+ } else {
+ this._stateAdjustment.ease(finalState, {
+ duration: SIDE_CONTROLS_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this.dash.showAppsButton.checked =
+ finalState === ControlsState.APP_GRID;
+ },
+ });
+ }
+ }
+
+ _updateAdjustment() {
+ let workspaceManager = global.workspace_manager;
+ let newNumWorkspaces = workspaceManager.n_workspaces;
+ let activeIndex = workspaceManager.get_active_workspace_index();
+
+ this._workspaceAdjustment.upper = newNumWorkspaces;
+
+ // A workspace might have been inserted or removed before the active
+ // one, causing the adjustment to go out of sync, so update the value
+ this._workspaceAdjustment.remove_transition('value');
+ this._workspaceAdjustment.value = activeIndex;
+ }
+
+ vfunc_unmap() {
+ super.vfunc_unmap();
+ this._workspacesDisplay.hide();
+ }
+
+ prepareToEnterOverview() {
+ this._searchController.prepareToEnterOverview();
+ this._workspacesDisplay.prepareToEnterOverview();
+ }
+
+ prepareToLeaveOverview() {
+ this._workspacesDisplay.prepareToLeaveOverview();
+ }
+
+ animateToOverview(state, callback) {
+ this._ignoreShowAppsButtonToggle = true;
+
+ this._stateAdjustment.value = ControlsState.HIDDEN;
+ this._stateAdjustment.ease(state, {
+ duration: Overview.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => {
+ if (callback)
+ callback();
+ },
+ });
+
+ this.dash.showAppsButton.checked =
+ state === ControlsState.APP_GRID;
+
+ this._ignoreShowAppsButtonToggle = false;
+ }
+
+ animateFromOverview(callback) {
+ this._ignoreShowAppsButtonToggle = true;
+
+ this._stateAdjustment.ease(ControlsState.HIDDEN, {
+ duration: Overview.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => {
+ this.dash.showAppsButton.checked = false;
+ this._ignoreShowAppsButtonToggle = false;
+
+ if (callback)
+ callback();
+ },
+ });
+ }
+
+ getWorkspacesBoxForState(state) {
+ return this.layoutManager.getWorkspacesBoxForState(state);
+ }
+
+ gestureBegin(tracker) {
+ const baseDistance = global.screen_height;
+ const progress = this._stateAdjustment.value;
+ const points = [
+ ControlsState.HIDDEN,
+ ControlsState.WINDOW_PICKER,
+ ControlsState.APP_GRID,
+ ];
+
+ const transition = this._stateAdjustment.get_transition('value');
+ const cancelProgress = transition
+ ? transition.get_interval().peek_final_value()
+ : Math.round(progress);
+ this._stateAdjustment.remove_transition('value');
+
+ tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
+ this.prepareToEnterOverview();
+ this._stateAdjustment.gestureInProgress = true;
+ }
+
+ gestureProgress(progress) {
+ this._stateAdjustment.value = progress;
+ }
+
+ gestureEnd(target, duration, onComplete) {
+ if (target === ControlsState.HIDDEN)
+ this.prepareToLeaveOverview();
+
+ this.dash.showAppsButton.checked =
+ target === ControlsState.APP_GRID;
+
+ this._stateAdjustment.remove_transition('value');
+ this._stateAdjustment.ease(target, {
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ onComplete,
+ });
+
+ this._stateAdjustment.gestureInProgress = false;
+ }
+
+ async runStartupAnimation(callback) {
+ this._ignoreShowAppsButtonToggle = true;
+
+ this.prepareToEnterOverview();
+
+ this._stateAdjustment.value = ControlsState.HIDDEN;
+ this._stateAdjustment.ease(ControlsState.WINDOW_PICKER, {
+ duration: Overview.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this.dash.showAppsButton.checked = false;
+ this._ignoreShowAppsButtonToggle = false;
+
+ // Set the opacity here to avoid a 1-frame flicker
+ this.opacity = 0;
+
+ // We can't run the animation before the first allocation happens
+ await this.layout_manager.ensureAllocation();
+
+ const { STARTUP_ANIMATION_TIME } = Layout;
+
+ // Opacity
+ this.ease({
+ opacity: 255,
+ duration: STARTUP_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.LINEAR,
+ });
+
+ // Search bar falls from the ceiling
+ const { primaryMonitor } = Main.layoutManager;
+ const [, y] = this._searchEntryBin.get_transformed_position();
+ const yOffset = y - primaryMonitor.y;
+
+ this._searchEntryBin.translation_y = -(yOffset + this._searchEntryBin.height);
+ this._searchEntryBin.ease({
+ translation_y: 0,
+ duration: STARTUP_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ // The Dash rises from the bottom. This is the last animation to finish,
+ // so run the callback there.
+ this.dash.translation_y = this.dash.height + this.dash.margin_bottom;
+ this.dash.ease({
+ translation_y: 0,
+ delay: STARTUP_ANIMATION_TIME,
+ duration: STARTUP_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => callback(),
+ });
+ }
+
+ get searchEntry() {
+ return this._searchEntry;
+ }
+
+ get appDisplay() {
+ return this._appDisplay;
+ }
+});
diff --git a/js/ui/padOsd.js b/js/ui/padOsd.js
new file mode 100644
index 0000000..e1e24f7
--- /dev/null
+++ b/js/ui/padOsd.js
@@ -0,0 +1,991 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported PadOsd, PadOsdService */
+
+const {
+ Atk, Clutter, GDesktopEnums, Gio,
+ GLib, GObject, Gtk, Meta, Pango, Rsvg, St,
+} = imports.gi;
+const Signals = imports.misc.signals;
+
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const Layout = imports.ui.layout;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const ACTIVE_COLOR = "#729fcf";
+
+const LTR = 0;
+const RTL = 1;
+
+const CW = 0;
+const CCW = 1;
+
+const UP = 0;
+const DOWN = 1;
+
+var PadChooser = GObject.registerClass({
+ Signals: { 'pad-selected': { param_types: [Clutter.InputDevice.$gtype] } },
+}, class PadChooser extends St.Button {
+ _init(device, groupDevices) {
+ super._init({
+ style_class: 'pad-chooser-button',
+ toggle_mode: true,
+ });
+ this.currentDevice = device;
+ this._padChooserMenu = null;
+
+ let arrow = new St.Icon({
+ style_class: 'popup-menu-arrow',
+ icon_name: 'pan-down-symbolic',
+ accessible_role: Atk.Role.ARROW,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.set_child(arrow);
+ this._ensureMenu(groupDevices);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ vfunc_clicked() {
+ if (this.get_checked()) {
+ if (this._padChooserMenu != null)
+ this._padChooserMenu.open(true);
+ else
+ this.set_checked(false);
+ } else {
+ this._padChooserMenu.close(true);
+ }
+ }
+
+ _ensureMenu(devices) {
+ this._padChooserMenu = new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP);
+ this._padChooserMenu.connect('menu-closed', () => {
+ this.set_checked(false);
+ });
+ this._padChooserMenu.actor.hide();
+ Main.uiGroup.add_actor(this._padChooserMenu.actor);
+
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._menuManager.addMenu(this._padChooserMenu);
+
+ for (let i = 0; i < devices.length; i++) {
+ let device = devices[i];
+ if (device == this.currentDevice)
+ continue;
+
+ this._padChooserMenu.addAction(device.get_device_name(), () => {
+ this.emit('pad-selected', device);
+ });
+ }
+ }
+
+ _onDestroy() {
+ this._padChooserMenu.destroy();
+ }
+
+ update(devices) {
+ if (this._padChooserMenu)
+ this._padChooserMenu.actor.destroy();
+ this.set_checked(false);
+ this._ensureMenu(devices);
+ }
+});
+
+var KeybindingEntry = GObject.registerClass({
+ Signals: { 'keybinding-edited': { param_types: [GObject.TYPE_STRING] } },
+}, class KeybindingEntry extends St.Entry {
+ _init() {
+ super._init({ hint_text: _("New shortcut…"), style: 'width: 10em' });
+ }
+
+ vfunc_captured_event(event) {
+ if (event.type() != Clutter.EventType.KEY_PRESS)
+ return Clutter.EVENT_PROPAGATE;
+
+ let str = Gtk.accelerator_name_with_keycode(null,
+ event.get_key_symbol(),
+ event.get_key_code(),
+ event.get_state());
+ this.set_text(str);
+ this.emit('keybinding-edited', str);
+ return Clutter.EVENT_STOP;
+ }
+});
+
+var ActionComboBox = GObject.registerClass({
+ Signals: { 'action-selected': { param_types: [GObject.TYPE_INT] } },
+}, class ActionComboBox extends St.Button {
+ _init() {
+ super._init({ style_class: 'button' });
+ this.set_toggle_mode(true);
+
+ const boxLayout = new Clutter.BoxLayout({
+ orientation: Clutter.Orientation.HORIZONTAL,
+ spacing: 6,
+ });
+ let box = new St.Widget({ layout_manager: boxLayout });
+ this.set_child(box);
+
+ this._label = new St.Label({ style_class: 'combo-box-label' });
+ box.add_child(this._label);
+
+ const arrow = new St.Icon({
+ style_class: 'popup-menu-arrow',
+ icon_name: 'pan-down-symbolic',
+ accessible_role: Atk.Role.ARROW,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add_child(arrow);
+
+ this._editMenu = new PopupMenu.PopupMenu(this, 0, St.Side.TOP);
+ this._editMenu.connect('menu-closed', () => {
+ this.set_checked(false);
+ });
+ this._editMenu.actor.hide();
+ Main.uiGroup.add_actor(this._editMenu.actor);
+
+ this._editMenuManager = new PopupMenu.PopupMenuManager(this);
+ this._editMenuManager.addMenu(this._editMenu);
+
+ this._actionLabels = new Map();
+ this._actionLabels.set(GDesktopEnums.PadButtonAction.NONE, _("Application defined"));
+ this._actionLabels.set(GDesktopEnums.PadButtonAction.HELP, _("Show on-screen help"));
+ this._actionLabels.set(GDesktopEnums.PadButtonAction.SWITCH_MONITOR, _("Switch monitor"));
+ this._actionLabels.set(GDesktopEnums.PadButtonAction.KEYBINDING, _("Assign keystroke"));
+
+ this._buttonItems = [];
+
+ for (let [action, label] of this._actionLabels.entries()) {
+ let selectedAction = action;
+ let item = this._editMenu.addAction(label, () => {
+ this._onActionSelected(selectedAction);
+ });
+
+ /* These actions only apply to pad buttons */
+ if (selectedAction == GDesktopEnums.PadButtonAction.HELP ||
+ selectedAction == GDesktopEnums.PadButtonAction.SWITCH_MONITOR)
+ this._buttonItems.push(item);
+ }
+
+ this.setAction(GDesktopEnums.PadButtonAction.NONE);
+ }
+
+ _onActionSelected(action) {
+ this.setAction(action);
+ this.popdown();
+ this.emit('action-selected', action);
+ }
+
+ setAction(action) {
+ this._label.set_text(this._actionLabels.get(action));
+ }
+
+ popup() {
+ this._editMenu.open(true);
+ }
+
+ popdown() {
+ this._editMenu.close(true);
+ }
+
+ vfunc_clicked() {
+ if (this.get_checked())
+ this.popup();
+ else
+ this.popdown();
+ }
+
+ setButtonActionsActive(active) {
+ this._buttonItems.forEach(item => item.setSensitive(active));
+ }
+});
+
+var ActionEditor = GObject.registerClass({
+ Signals: { 'done': {} },
+}, class ActionEditor extends St.Widget {
+ _init() {
+ const boxLayout = new Clutter.BoxLayout({
+ orientation: Clutter.Orientation.HORIZONTAL,
+ spacing: 12,
+ });
+
+ super._init({ layout_manager: boxLayout });
+
+ this._actionComboBox = new ActionComboBox();
+ this._actionComboBox.connect('action-selected', this._onActionSelected.bind(this));
+ this.add_actor(this._actionComboBox);
+
+ this._keybindingEdit = new KeybindingEntry();
+ this._keybindingEdit.connect('keybinding-edited', this._onKeybindingEdited.bind(this));
+ this.add_actor(this._keybindingEdit);
+
+ this._doneButton = new St.Button({
+ label: _('Done'),
+ style_class: 'button',
+ x_expand: false,
+ });
+ this._doneButton.connect('clicked', this._onEditingDone.bind(this));
+ this.add_actor(this._doneButton);
+ }
+
+ _updateKeybindingEntryState() {
+ if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) {
+ this._keybindingEdit.set_text(this._currentKeybinding);
+ this._keybindingEdit.show();
+ this._keybindingEdit.grab_key_focus();
+ } else {
+ this._keybindingEdit.hide();
+ }
+ }
+
+ setSettings(settings, action) {
+ this._buttonSettings = settings;
+
+ this._currentAction = this._buttonSettings.get_enum('action');
+ this._currentKeybinding = this._buttonSettings.get_string('keybinding');
+ this._actionComboBox.setAction(this._currentAction);
+ this._updateKeybindingEntryState();
+
+ let isButton = action == Meta.PadActionType.BUTTON;
+ this._actionComboBox.setButtonActionsActive(isButton);
+ }
+
+ close() {
+ this._actionComboBox.popdown();
+ this.hide();
+ }
+
+ _onKeybindingEdited(entry, keybinding) {
+ this._currentKeybinding = keybinding;
+ }
+
+ _onActionSelected(menu, action) {
+ this._currentAction = action;
+ this._updateKeybindingEntryState();
+ }
+
+ _storeSettings() {
+ if (!this._buttonSettings)
+ return;
+
+ let keybinding = null;
+
+ if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING)
+ keybinding = this._currentKeybinding;
+
+ this._buttonSettings.set_enum('action', this._currentAction);
+
+ if (keybinding)
+ this._buttonSettings.set_string('keybinding', keybinding);
+ else
+ this._buttonSettings.reset('keybinding');
+ }
+
+ _onEditingDone() {
+ this._storeSettings();
+ this.close();
+ this.emit('done');
+ }
+});
+
+var PadDiagram = GObject.registerClass({
+ Properties: {
+ 'left-handed': GObject.ParamSpec.boolean('left-handed',
+ 'left-handed', 'Left handed',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT_ONLY,
+ false),
+ 'image': GObject.ParamSpec.string('image', 'image', 'Image',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT_ONLY,
+ null),
+ 'editor-actor': GObject.ParamSpec.object('editor-actor',
+ 'editor-actor',
+ 'Editor actor',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT_ONLY,
+ Clutter.Actor.$gtype),
+ },
+}, class PadDiagram extends St.DrawingArea {
+ _init(params) {
+ let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/pad-osd.css');
+ let [success_, css] = file.load_contents(null);
+ this._curEdited = null;
+ this._prevEdited = null;
+ this._css = new TextDecoder().decode(css);
+ this._labels = [];
+ this._activeButtons = [];
+ super._init(params);
+ }
+
+ get image() {
+ return this._imagePath;
+ }
+
+ set image(imagePath) {
+ let originalHandle = Rsvg.Handle.new_from_file(imagePath);
+ let dimensions = originalHandle.get_dimensions();
+ this._imageWidth = dimensions.width;
+ this._imageHeight = dimensions.height;
+
+ this._imagePath = imagePath;
+ this._handle = this._composeStyledDiagram();
+ this._initLabels();
+ }
+
+ get editorActor() {
+ return this._editorActor;
+ }
+
+ set editorActor(actor) {
+ actor.hide();
+ this._editorActor = actor;
+ this.add_actor(actor);
+ }
+
+ _initLabels() {
+ let i = 0;
+ for (i = 0; ; i++) {
+ if (!this._addLabel(Meta.PadActionType.BUTTON, i))
+ break;
+ }
+
+ for (i = 0; ; i++) {
+ if (!this._addLabel(Meta.PadActionType.RING, i, CW) ||
+ !this._addLabel(Meta.PadActionType.RING, i, CCW))
+ break;
+ }
+
+ for (i = 0; ; i++) {
+ if (!this._addLabel(Meta.PadActionType.STRIP, i, UP) ||
+ !this._addLabel(Meta.PadActionType.STRIP, i, DOWN))
+ break;
+ }
+ }
+
+ _wrappingSvgHeader() {
+ return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' +
+ '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
+ 'xmlns:xi="http://www.w3.org/2001/XInclude" ' +
+ `width="${this._imageWidth}" height="${this._imageHeight}"> ` +
+ '<style type="text/css">';
+ }
+
+ _wrappingSvgFooter() {
+ return '%s%s%s'.format(
+ '</style>',
+ '<xi:include href="%s" />'.format(this._imagePath),
+ '</svg>');
+ }
+
+ _cssString() {
+ let css = this._css;
+
+ for (let i = 0; i < this._activeButtons.length; i++) {
+ let ch = String.fromCharCode('A'.charCodeAt() + this._activeButtons[i]);
+ css += `.${ch}.Leader { stroke: ${ACTIVE_COLOR} !important; }`;
+ css += `.${ch}.Button { stroke: ${ACTIVE_COLOR} !important; fill: ${ACTIVE_COLOR} !important; }`;
+ }
+
+ return css;
+ }
+
+ _composeStyledDiagram() {
+ let svgData = '';
+
+ if (!GLib.file_test(this._imagePath, GLib.FileTest.EXISTS))
+ return null;
+
+ svgData += this._wrappingSvgHeader();
+ svgData += this._cssString();
+ svgData += this._wrappingSvgFooter();
+
+ let istream = new Gio.MemoryInputStream();
+ istream.add_bytes(new GLib.Bytes(svgData));
+
+ return Rsvg.Handle.new_from_stream_sync(istream,
+ Gio.File.new_for_path(this._imagePath), 0, null);
+ }
+
+ _updateDiagramScale() {
+ [this._actorWidth, this._actorHeight] = this.get_size();
+ let dimensions = this._handle.get_dimensions();
+ let scaleX = this._actorWidth / dimensions.width;
+ let scaleY = this._actorHeight / dimensions.height;
+ this._scale = Math.min(scaleX, scaleY);
+ }
+
+ _allocateChild(child, x, y, direction) {
+ let [, natHeight] = child.get_preferred_height(-1);
+ let [, natWidth] = child.get_preferred_width(natHeight);
+ let childBox = new Clutter.ActorBox();
+
+ // I miss Cairo.Matrix
+ let dimensions = this._handle.get_dimensions();
+ x = x * this._scale + this._actorWidth / 2 - dimensions.width / 2 * this._scale;
+ y = y * this._scale + this._actorHeight / 2 - dimensions.height / 2 * this._scale;
+
+ if (direction == LTR) {
+ childBox.x1 = x;
+ childBox.x2 = x + natWidth;
+ } else {
+ childBox.x1 = x - natWidth;
+ childBox.x2 = x;
+ }
+
+ childBox.y1 = y - natHeight / 2;
+ childBox.y2 = y + natHeight / 2;
+ child.allocate(childBox);
+ }
+
+ vfunc_allocate(box) {
+ super.vfunc_allocate(box);
+ if (this._handle === null)
+ return;
+
+ this._updateDiagramScale();
+
+ for (let i = 0; i < this._labels.length; i++) {
+ const { label, x, y, arrangement } = this._labels[i];
+ this._allocateChild(label, x, y, arrangement);
+ }
+
+ if (this._editorActor && this._curEdited) {
+ const { x, y, arrangement } = this._curEdited;
+ this._allocateChild(this._editorActor, x, y, arrangement);
+ }
+ }
+
+ vfunc_repaint() {
+ if (this._handle == null)
+ return;
+
+ if (this._scale == null)
+ this._updateDiagramScale();
+
+ let [width, height] = this.get_surface_size();
+ let dimensions = this._handle.get_dimensions();
+ let cr = this.get_context();
+
+ cr.save();
+ cr.translate(width / 2, height / 2);
+ cr.scale(this._scale, this._scale);
+ if (this.leftHanded)
+ cr.rotate(Math.PI);
+ cr.translate(-dimensions.width / 2, -dimensions.height / 2);
+ this._handle.render_cairo(cr);
+ cr.restore();
+ cr.$dispose();
+ }
+
+ _getItemLabelCoords(labelName, leaderName) {
+ if (this._handle == null)
+ return [false];
+
+ const [labelFound, labelPos] = this._handle.get_position_sub(`#${labelName}`);
+ const [, labelSize] = this._handle.get_dimensions_sub(`#${labelName}`);
+ if (!labelFound)
+ return [false];
+
+ const [leaderFound, leaderPos] = this._handle.get_position_sub(`#${leaderName}`);
+ const [, leaderSize] = this._handle.get_dimensions_sub(`#${leaderName}`);
+ if (!leaderFound)
+ return [false];
+
+ let direction;
+ if (labelPos.x > leaderPos.x + leaderSize.width)
+ direction = LTR;
+ else
+ direction = RTL;
+
+ let pos = {x: labelPos.x, y: labelPos.y + labelSize.height};
+ if (this.leftHanded) {
+ direction = 1 - direction;
+ pos.x = this._imageWidth - pos.x;
+ pos.y = this._imageHeight - pos.y;
+ }
+
+ return [true, pos.x, pos.y, direction];
+ }
+
+ _getButtonLabels(button) {
+ let ch = String.fromCharCode('A'.charCodeAt() + button);
+ const labelName = `Label${ch}`;
+ const leaderName = `Leader${ch}`;
+ return [labelName, leaderName];
+ }
+
+ _getRingLabels(number, dir) {
+ let numStr = number > 0 ? (number + 1).toString() : '';
+ let dirStr = dir == CW ? 'CW' : 'CCW';
+ const labelName = `LabelRing${numStr}${dirStr}`;
+ const leaderName = `LeaderRing${numStr}${dirStr}`;
+ return [labelName, leaderName];
+ }
+
+ _getStripLabels(number, dir) {
+ let numStr = number > 0 ? (number + 1).toString() : '';
+ let dirStr = dir == UP ? 'Up' : 'Down';
+ const labelName = `LabelStrip${numStr}${dirStr}`;
+ const leaderName = `LeaderStrip${numStr}${dirStr}`;
+ return [labelName, leaderName];
+ }
+
+ _getLabelCoords(action, idx, dir) {
+ if (action == Meta.PadActionType.BUTTON)
+ return this._getItemLabelCoords(...this._getButtonLabels(idx));
+ else if (action == Meta.PadActionType.RING)
+ return this._getItemLabelCoords(...this._getRingLabels(idx, dir));
+ else if (action == Meta.PadActionType.STRIP)
+ return this._getItemLabelCoords(...this._getStripLabels(idx, dir));
+
+ return [false];
+ }
+
+ _invalidateSvg() {
+ if (this._handle == null)
+ return;
+ this._handle = this._composeStyledDiagram();
+ this.queue_repaint();
+ }
+
+ activateButton(button) {
+ this._activeButtons.push(button);
+ this._invalidateSvg();
+ }
+
+ deactivateButton(button) {
+ for (let i = 0; i < this._activeButtons.length; i++) {
+ if (this._activeButtons[i] == button)
+ this._activeButtons.splice(i, 1);
+ }
+ this._invalidateSvg();
+ }
+
+ _addLabel(action, idx, dir) {
+ let [found, x, y, arrangement] = this._getLabelCoords(action, idx, dir);
+ if (!found)
+ return false;
+
+ let label = new St.Label();
+ this._labels.push({ label, action, idx, dir, x, y, arrangement });
+ this.add_actor(label);
+ return true;
+ }
+
+ updateLabels(getText) {
+ for (let i = 0; i < this._labels.length; i++) {
+ const { label, action, idx, dir } = this._labels[i];
+ let str = getText(action, idx, dir);
+ label.set_text(str);
+ }
+
+ this.queue_relayout();
+ }
+
+ _applyLabel(label, action, idx, dir, str) {
+ if (str !== null)
+ label.set_text(str);
+ label.show();
+ }
+
+ stopEdition(continues, str) {
+ this._editorActor.hide();
+
+ if (this._prevEdited) {
+ const { label, action, idx, dir } = this._prevEdited;
+ this._applyLabel(label, action, idx, dir, str);
+ this._prevEdited = null;
+ }
+
+ if (this._curEdited) {
+ const { label, action, idx, dir } = this._curEdited;
+ this._applyLabel(label, action, idx, dir, str);
+ if (continues)
+ this._prevEdited = this._curEdited;
+ this._curEdited = null;
+ }
+
+ this.queue_relayout();
+ }
+
+ startEdition(action, idx, dir) {
+ let editedLabel;
+
+ if (this._curEdited)
+ return;
+
+ for (let i = 0; i < this._labels.length; i++) {
+ if (action == this._labels[i].action &&
+ idx == this._labels[i].idx && dir == this._labels[i].dir) {
+ this._curEdited = this._labels[i];
+ editedLabel = this._curEdited.label;
+ break;
+ }
+ }
+
+ if (this._curEdited == null)
+ return;
+ this._editorActor.show();
+ editedLabel.hide();
+ this.queue_relayout();
+ }
+});
+
+var PadOsd = GObject.registerClass({
+ Signals: {
+ 'pad-selected': { param_types: [Clutter.InputDevice.$gtype] },
+ 'closed': {},
+ },
+}, class PadOsd extends St.BoxLayout {
+ _init(padDevice, settings, imagePath, editionMode, monitorIndex) {
+ super._init({
+ style_class: 'pad-osd-window',
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ reactive: true,
+ });
+
+ this.padDevice = padDevice;
+ this._groupPads = [padDevice];
+ this._settings = settings;
+ this._imagePath = imagePath;
+ this._editionMode = editionMode;
+ this._padChooser = null;
+
+ let seat = Clutter.get_default_backend().get_default_seat();
+ seat.connectObject(
+ 'device-added', (_seat, device) => {
+ if (device.get_device_type() === Clutter.InputDeviceType.PAD_DEVICE &&
+ this.padDevice.is_grouped(device)) {
+ this._groupPads.push(device);
+ this._updatePadChooser();
+ }
+ },
+ 'device-removed', (_seat, device) => {
+ // If the device is being removed, destroy the padOsd.
+ if (device === this.padDevice) {
+ this.destroy();
+ } else if (this._groupPads.includes(device)) {
+ // Or update the pad chooser if the device belongs to
+ // the same group.
+ this._groupPads.splice(this._groupPads.indexOf(device), 1);
+ this._updatePadChooser();
+ }
+ }, this);
+
+ seat.list_devices().forEach(device => {
+ if (device != this.padDevice &&
+ device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE &&
+ this.padDevice.is_grouped(device))
+ this._groupPads.push(device);
+ });
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ Main.uiGroup.add_actor(this);
+
+ this._monitorIndex = monitorIndex;
+ let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
+ this.add_constraint(constraint);
+
+ this._titleBox = new St.BoxLayout({
+ style_class: 'pad-osd-title-box',
+ vertical: false,
+ x_expand: false,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_actor(this._titleBox);
+
+ const labelBox = new St.BoxLayout({
+ style_class: 'pad-osd-title-menu-box',
+ vertical: true,
+ });
+ this._titleBox.add_actor(labelBox);
+
+ this._titleLabel = new St.Label({
+ style: 'font-side: larger; font-weight: bold;',
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this._titleLabel.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
+ this._titleLabel.clutter_text.set_text(padDevice.get_device_name());
+ labelBox.add_actor(this._titleLabel);
+
+ this._tipLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER });
+ labelBox.add_actor(this._tipLabel);
+
+ this._updatePadChooser();
+
+ this._actionEditor = new ActionEditor();
+ this._actionEditor.connect('done', this._endActionEdition.bind(this));
+
+ this._padDiagram = new PadDiagram({
+ image: this._imagePath,
+ left_handed: settings.get_boolean('left-handed'),
+ editor_actor: this._actionEditor,
+ x_expand: true,
+ y_expand: true,
+ });
+ this.add_actor(this._padDiagram);
+ this._updateActionLabels();
+
+ const buttonBox = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_actor(buttonBox);
+ this._editButton = new St.Button({
+ label: _('Edit…'),
+ style_class: 'button',
+ can_focus: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this._editButton.connect('clicked', () => {
+ this.setEditionMode(true);
+ });
+ buttonBox.add_actor(this._editButton);
+
+ this._syncEditionMode();
+ this._grab = Main.pushModal(this);
+ }
+
+ _updatePadChooser() {
+ if (this._groupPads.length > 1) {
+ if (this._padChooser == null) {
+ this._padChooser = new PadChooser(this.padDevice, this._groupPads);
+ this._padChooser.connect('pad-selected', (chooser, pad) => {
+ this._requestForOtherPad(pad);
+ });
+ this._titleBox.add_child(this._padChooser);
+ } else {
+ this._padChooser.update(this._groupPads);
+ }
+ } else if (this._padChooser != null) {
+ this._padChooser.destroy();
+ this._padChooser = null;
+ }
+ }
+
+ _requestForOtherPad(pad) {
+ if (pad == this.padDevice || !this._groupPads.includes(pad))
+ return;
+
+ let editionMode = this._editionMode;
+ this.destroy();
+ global.display.request_pad_osd(pad, editionMode);
+ }
+
+ _getActionText(type, number) {
+ let str = global.display.get_pad_action_label(this.padDevice, type, number);
+ return str ?? _('None');
+ }
+
+ _updateActionLabels() {
+ this._padDiagram.updateLabels(this._getActionText.bind(this));
+ }
+
+ vfunc_captured_event(event) {
+ let isModeSwitch =
+ (event.type() == Clutter.EventType.PAD_BUTTON_PRESS ||
+ event.type() == Clutter.EventType.PAD_BUTTON_RELEASE) &&
+ this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0;
+
+ if (event.type() == Clutter.EventType.PAD_BUTTON_PRESS &&
+ event.get_source_device() == this.padDevice) {
+ this._padDiagram.activateButton(event.get_button());
+
+ /* Buttons that switch between modes cannot be edited */
+ if (this._editionMode && !isModeSwitch)
+ this._startButtonActionEdition(event.get_button());
+ return Clutter.EVENT_STOP;
+ } else if (event.type() == Clutter.EventType.PAD_BUTTON_RELEASE &&
+ event.get_source_device() == this.padDevice) {
+ this._padDiagram.deactivateButton(event.get_button());
+
+ if (isModeSwitch) {
+ this._endActionEdition();
+ this._updateActionLabels();
+ }
+ return Clutter.EVENT_STOP;
+ } else if (event.type() == Clutter.EventType.KEY_PRESS &&
+ (!this._editionMode || event.get_key_symbol() === Clutter.KEY_Escape)) {
+ if (this._editedAction != null)
+ this._endActionEdition();
+ else
+ this.destroy();
+ return Clutter.EVENT_STOP;
+ } else if (event.get_source_device() == this.padDevice &&
+ event.type() == Clutter.EventType.PAD_STRIP) {
+ if (this._editionMode) {
+ let [retval_, number, mode] = event.get_pad_event_details();
+ this._startStripActionEdition(number, UP, mode);
+ }
+ } else if (event.get_source_device() == this.padDevice &&
+ event.type() == Clutter.EventType.PAD_RING) {
+ if (this._editionMode) {
+ let [retval_, number, mode] = event.get_pad_event_details();
+ this._startRingActionEdition(number, CCW, mode);
+ }
+ }
+
+ // If the event comes from another pad in the same group,
+ // show the OSD for it.
+ if (this._groupPads.includes(event.get_source_device())) {
+ this._requestForOtherPad(event.get_source_device());
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _syncEditionMode() {
+ this._editButton.set_reactive(!this._editionMode);
+ this._editButton.save_easing_state();
+ this._editButton.set_easing_duration(200);
+ this._editButton.set_opacity(this._editionMode ? 128 : 255);
+ this._editButton.restore_easing_state();
+
+ let title;
+
+ if (this._editionMode) {
+ title = _("Press a button to configure");
+ this._tipLabel.set_text(_("Press Esc to exit"));
+ } else {
+ title = this.padDevice.get_device_name();
+ this._tipLabel.set_text(_("Press any key to exit"));
+ }
+
+ this._titleLabel.set_text(title);
+ }
+
+ _isEditedAction(type, number, dir) {
+ if (!this._editedAction)
+ return false;
+
+ return this._editedAction.type == type &&
+ this._editedAction.number == number &&
+ this._editedAction.dir == dir;
+ }
+
+ _followUpActionEdition(str) {
+ let { type, dir, number, mode } = this._editedAction;
+ let hasNextAction = type == Meta.PadActionType.RING && dir == CCW ||
+ type == Meta.PadActionType.STRIP && dir == UP;
+ if (!hasNextAction)
+ return false;
+
+ this._padDiagram.stopEdition(true, str);
+ this._editedAction = null;
+ if (type == Meta.PadActionType.RING)
+ this._startRingActionEdition(number, CW, mode);
+ else
+ this._startStripActionEdition(number, DOWN, mode);
+
+ return true;
+ }
+
+ _endActionEdition() {
+ this._actionEditor.close();
+
+ if (this._editedAction != null) {
+ let str = global.display.get_pad_action_label(this.padDevice,
+ this._editedAction.type,
+ this._editedAction.number);
+ if (this._followUpActionEdition(str))
+ return;
+
+ this._padDiagram.stopEdition(false, str ?? _('None'));
+ this._editedAction = null;
+ }
+
+ this._editedActionSettings = null;
+ }
+
+ _startActionEdition(key, type, number, dir, mode) {
+ if (this._isEditedAction(type, number, dir))
+ return;
+
+ this._endActionEdition();
+ this._editedAction = { type, number, dir, mode };
+
+ const settingsPath = `${this._settings.path}${key}/`;
+ this._editedActionSettings = Gio.Settings.new_with_path('org.gnome.desktop.peripherals.tablet.pad-button',
+ settingsPath);
+ this._actionEditor.setSettings(this._editedActionSettings, type);
+ this._padDiagram.startEdition(type, number, dir);
+ }
+
+ _startButtonActionEdition(button) {
+ let ch = String.fromCharCode('A'.charCodeAt() + button);
+ let key = `button${ch}`;
+ this._startActionEdition(key, Meta.PadActionType.BUTTON, button);
+ }
+
+ _startRingActionEdition(ring, dir, mode) {
+ let ch = String.fromCharCode('A'.charCodeAt() + ring);
+ const key = `ring${ch}-${dir === CCW ? 'ccw' : 'cw'}-mode-${mode}`;
+ this._startActionEdition(key, Meta.PadActionType.RING, ring, dir, mode);
+ }
+
+ _startStripActionEdition(strip, dir, mode) {
+ let ch = String.fromCharCode('A'.charCodeAt() + strip);
+ const key = `strip${ch}-${dir === UP ? 'up' : 'down'}-mode-${mode}`;
+ this._startActionEdition(key, Meta.PadActionType.STRIP, strip, dir, mode);
+ }
+
+ setEditionMode(editionMode) {
+ if (this._editionMode == editionMode)
+ return;
+
+ this._editionMode = editionMode;
+ this._syncEditionMode();
+ }
+
+ _onDestroy() {
+ Main.popModal(this._grab);
+ this._grab = null;
+ this._actionEditor.close();
+
+ this.emit('closed');
+ }
+});
+
+const PadOsdIface = loadInterfaceXML('org.gnome.Shell.Wacom.PadOsd');
+
+var PadOsdService = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(PadOsdIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Wacom');
+ Gio.DBus.session.own_name('org.gnome.Shell.Wacom.PadOsd', Gio.BusNameOwnerFlags.REPLACE, null, null);
+ }
+
+ ShowAsync(params, invocation) {
+ let [deviceNode, editionMode] = params;
+ let seat = Clutter.get_default_backend().get_default_seat();
+ let devices = seat.list_devices();
+ let padDevice = null;
+
+ devices.forEach(device => {
+ if (deviceNode == device.get_device_node() &&
+ device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE)
+ padDevice = device;
+ });
+
+ if (padDevice == null) {
+ invocation.return_error_literal(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.CANCELLED,
+ "Invalid params");
+ return;
+ }
+
+ global.display.request_pad_osd(padDevice, editionMode);
+ invocation.return_value(null);
+ }
+};
diff --git a/js/ui/pageIndicators.js b/js/ui/pageIndicators.js
new file mode 100644
index 0000000..18a376c
--- /dev/null
+++ b/js/ui/pageIndicators.js
@@ -0,0 +1,116 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported PageIndicators */
+
+const { Clutter, Graphene, GObject, St } = imports.gi;
+
+const INDICATOR_INACTIVE_OPACITY = 128;
+const INDICATOR_INACTIVE_OPACITY_HOVER = 255;
+const INDICATOR_INACTIVE_SCALE = 2 / 3;
+const INDICATOR_INACTIVE_SCALE_PRESSED = 0.5;
+
+var PageIndicators = GObject.registerClass({
+ Signals: { 'page-activated': { param_types: [GObject.TYPE_INT] } },
+}, class PageIndicators extends St.BoxLayout {
+ _init(orientation = Clutter.Orientation.VERTICAL) {
+ let vertical = orientation == Clutter.Orientation.VERTICAL;
+ super._init({
+ style_class: 'page-indicators',
+ vertical,
+ x_expand: true, y_expand: true,
+ x_align: vertical ? Clutter.ActorAlign.END : Clutter.ActorAlign.CENTER,
+ y_align: vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.END,
+ reactive: true,
+ clip_to_allocation: true,
+ });
+ this._nPages = 0;
+ this._currentPosition = 0;
+ this._reactive = true;
+ this._reactive = true;
+ this._orientation = orientation;
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ // We want to request the natural height of all our children as our
+ // natural height, so we chain up to St.BoxLayout, but we only request 0
+ // as minimum height, since it's not that important if some indicators
+ // are not shown
+ let [, natHeight] = super.vfunc_get_preferred_height(forWidth);
+ return [0, natHeight];
+ }
+
+ setReactive(reactive) {
+ let children = this.get_children();
+ for (let i = 0; i < children.length; i++)
+ children[i].reactive = reactive;
+
+ this._reactive = reactive;
+ }
+
+ setNPages(nPages) {
+ if (this._nPages == nPages)
+ return;
+
+ let diff = nPages - this._nPages;
+ if (diff > 0) {
+ for (let i = 0; i < diff; i++) {
+ let pageIndex = this._nPages + i;
+ const indicator = new St.Button({
+ style_class: 'page-indicator',
+ button_mask: St.ButtonMask.ONE |
+ St.ButtonMask.TWO |
+ St.ButtonMask.THREE,
+ reactive: this._reactive,
+ });
+ indicator.child = new St.Widget({
+ style_class: 'page-indicator-icon',
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ });
+ indicator.connect('clicked', () => {
+ this.emit('page-activated', pageIndex);
+ });
+ indicator.connect('notify::hover', () => {
+ this._updateIndicator(indicator, pageIndex);
+ });
+ indicator.connect('notify::pressed', () => {
+ this._updateIndicator(indicator, pageIndex);
+ });
+ this._updateIndicator(indicator, pageIndex);
+ this.add_actor(indicator);
+ }
+ } else {
+ let children = this.get_children().splice(diff);
+ for (let i = 0; i < children.length; i++)
+ children[i].destroy();
+ }
+ this._nPages = nPages;
+ this.visible = this._nPages > 1;
+ }
+
+ _updateIndicator(indicator, pageIndex) {
+ let progress =
+ Math.max(1 - Math.abs(this._currentPosition - pageIndex), 0);
+
+ let inactiveScale = indicator.pressed
+ ? INDICATOR_INACTIVE_SCALE_PRESSED : INDICATOR_INACTIVE_SCALE;
+ let inactiveOpacity = indicator.hover
+ ? INDICATOR_INACTIVE_OPACITY_HOVER : INDICATOR_INACTIVE_OPACITY;
+
+ let scale = inactiveScale + (1 - inactiveScale) * progress;
+ let opacity = inactiveOpacity + (255 - inactiveOpacity) * progress;
+
+ indicator.child.set_scale(scale, scale);
+ indicator.child.opacity = opacity;
+ }
+
+ setCurrentPosition(currentPosition) {
+ this._currentPosition = currentPosition;
+
+ let children = this.get_children();
+ for (let i = 0; i < children.length; i++)
+ this._updateIndicator(children[i], i);
+ }
+
+ get nPages() {
+ return this._nPages;
+ }
+});
diff --git a/js/ui/panel.js b/js/ui/panel.js
new file mode 100644
index 0000000..94dffda
--- /dev/null
+++ b/js/ui/panel.js
@@ -0,0 +1,774 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Panel */
+
+const { Atk, Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const Animation = imports.ui.animation;
+const { AppMenu } = imports.ui.appMenu;
+const Config = imports.misc.config;
+const CtrlAltTab = imports.ui.ctrlAltTab;
+const DND = imports.ui.dnd;
+const Overview = imports.ui.overview;
+const PopupMenu = imports.ui.popupMenu;
+const PanelMenu = imports.ui.panelMenu;
+const {QuickSettingsMenu, SystemIndicator} = imports.ui.quickSettings;
+const Main = imports.ui.main;
+
+var PANEL_ICON_SIZE = 16;
+var APP_MENU_ICON_MARGIN = 0;
+
+var BUTTON_DND_ACTIVATION_TIMEOUT = 250;
+
+const N_QUICK_SETTINGS_COLUMNS = 2;
+
+/**
+ * AppMenuButton:
+ *
+ * This class manages the "application menu" component. It tracks the
+ * currently focused application. However, when an app is launched,
+ * this menu also handles startup notification for it. So when we
+ * have an active startup notification, we switch modes to display that.
+ */
+var AppMenuButton = GObject.registerClass({
+ Signals: { 'changed': {} },
+}, class AppMenuButton extends PanelMenu.Button {
+ _init(panel) {
+ super._init(0.0, null, true);
+
+ this.accessible_role = Atk.Role.MENU;
+
+ this._startingApps = [];
+
+ this._menuManager = panel.menuManager;
+ this._targetApp = null;
+
+ let bin = new St.Bin({ name: 'appMenu' });
+ this.add_actor(bin);
+
+ this.bind_property("reactive", this, "can-focus", 0);
+ this.reactive = false;
+
+ this._container = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
+ bin.set_child(this._container);
+
+ let textureCache = St.TextureCache.get_default();
+ textureCache.connect('icon-theme-changed',
+ this._onIconThemeChanged.bind(this));
+
+ let iconEffect = new Clutter.DesaturateEffect();
+ this._iconBox = new St.Bin({
+ style_class: 'app-menu-icon',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._iconBox.add_effect(iconEffect);
+ this._container.add_actor(this._iconBox);
+
+ this._iconBox.connect('style-changed', () => {
+ let themeNode = this._iconBox.get_theme_node();
+ iconEffect.enabled = themeNode.get_icon_style() == St.IconStyle.SYMBOLIC;
+ });
+
+ this._label = new St.Label({
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._container.add_actor(this._label);
+
+ this._visible = !Main.overview.visible;
+ if (!this._visible)
+ this.hide();
+ Main.overview.connectObject(
+ 'hiding', this._sync.bind(this),
+ 'showing', this._sync.bind(this), this);
+
+ this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, {
+ animate: true,
+ hideOnStop: true,
+ });
+ this._container.add_actor(this._spinner);
+
+ let menu = new AppMenu(this);
+ this.setMenu(menu);
+ this._menuManager.addMenu(menu);
+
+ Shell.WindowTracker.get_default().connectObject('notify::focus-app',
+ this._focusAppChanged.bind(this), this);
+ Shell.AppSystem.get_default().connectObject('app-state-changed',
+ this._onAppStateChanged.bind(this), this);
+ global.window_manager.connectObject('switch-workspace',
+ this._sync.bind(this), this);
+
+ this._sync();
+ }
+
+ fadeIn() {
+ if (this._visible)
+ return;
+
+ this._visible = true;
+ this.reactive = true;
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 255,
+ duration: Overview.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ fadeOut() {
+ if (!this._visible)
+ return;
+
+ this._visible = false;
+ this.reactive = false;
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: Overview.ANIMATION_TIME,
+ });
+ }
+
+ _syncIcon(app) {
+ const icon = app.create_icon_texture(PANEL_ICON_SIZE - APP_MENU_ICON_MARGIN);
+ this._iconBox.set_child(icon);
+ }
+
+ _onIconThemeChanged() {
+ if (this._iconBox.child == null)
+ return;
+
+ if (this._targetApp)
+ this._syncIcon(this._targetApp);
+ }
+
+ stopAnimation() {
+ this._spinner.stop();
+ }
+
+ startAnimation() {
+ this._spinner.play();
+ }
+
+ _onAppStateChanged(appSys, app) {
+ let state = app.state;
+ if (state != Shell.AppState.STARTING)
+ this._startingApps = this._startingApps.filter(a => a != app);
+ else if (state == Shell.AppState.STARTING)
+ this._startingApps.push(app);
+ // For now just resync on all running state changes; this is mainly to handle
+ // cases where the focused window's application changes without the focus
+ // changing. An example case is how we map OpenOffice.org based on the window
+ // title which is a dynamic property.
+ this._sync();
+ }
+
+ _focusAppChanged() {
+ let tracker = Shell.WindowTracker.get_default();
+ let focusedApp = tracker.focus_app;
+ if (!focusedApp) {
+ // If the app has just lost focus to the panel, pretend
+ // nothing happened; otherwise you can't keynav to the
+ // app menu.
+ if (global.stage.key_focus != null)
+ return;
+ }
+ this._sync();
+ }
+
+ _findTargetApp() {
+ let workspaceManager = global.workspace_manager;
+ let workspace = workspaceManager.get_active_workspace();
+ let tracker = Shell.WindowTracker.get_default();
+ let focusedApp = tracker.focus_app;
+ if (focusedApp && focusedApp.is_on_workspace(workspace))
+ return focusedApp;
+
+ for (let i = 0; i < this._startingApps.length; i++) {
+ if (this._startingApps[i].is_on_workspace(workspace))
+ return this._startingApps[i];
+ }
+
+ return null;
+ }
+
+ _sync() {
+ let targetApp = this._findTargetApp();
+
+ if (this._targetApp != targetApp) {
+ this._targetApp?.disconnectObject(this);
+
+ this._targetApp = targetApp;
+
+ if (this._targetApp) {
+ this._targetApp.connectObject('notify::busy', this._sync.bind(this), this);
+ this._label.set_text(this._targetApp.get_name());
+ this.set_accessible_name(this._targetApp.get_name());
+
+ this._syncIcon(this._targetApp);
+ }
+ }
+
+ let visible = this._targetApp != null && !Main.overview.visibleTarget;
+ if (visible)
+ this.fadeIn();
+ else
+ this.fadeOut();
+
+ let isBusy = this._targetApp != null &&
+ (this._targetApp.get_state() == Shell.AppState.STARTING ||
+ this._targetApp.get_busy());
+ if (isBusy)
+ this.startAnimation();
+ else
+ this.stopAnimation();
+
+ this.reactive = visible && !isBusy;
+
+ this.menu.setApp(this._targetApp);
+ this.emit('changed');
+ }
+});
+
+var ActivitiesButton = GObject.registerClass(
+class ActivitiesButton extends PanelMenu.Button {
+ _init() {
+ super._init(0.0, null, true);
+ this.accessible_role = Atk.Role.TOGGLE_BUTTON;
+
+ this.name = 'panelActivities';
+
+ /* Translators: If there is no suitable word for "Activities"
+ in your language, you can use the word for "Overview". */
+ this._label = new St.Label({
+ text: _('Activities'),
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_actor(this._label);
+
+ this.label_actor = this._label;
+
+ Main.overview.connect('showing', () => {
+ this.add_style_pseudo_class('overview');
+ this.add_accessible_state(Atk.StateType.CHECKED);
+ });
+ Main.overview.connect('hiding', () => {
+ this.remove_style_pseudo_class('overview');
+ this.remove_accessible_state(Atk.StateType.CHECKED);
+ });
+
+ this._xdndTimeOut = 0;
+ }
+
+ handleDragOver(source, _actor, _x, _y, _time) {
+ if (source != Main.xdndHandler)
+ return DND.DragMotionResult.CONTINUE;
+
+ if (this._xdndTimeOut != 0)
+ GLib.source_remove(this._xdndTimeOut);
+ this._xdndTimeOut = GLib.timeout_add(GLib.PRIORITY_DEFAULT, BUTTON_DND_ACTIVATION_TIMEOUT, () => {
+ this._xdndToggleOverview();
+ });
+ GLib.Source.set_name_by_id(this._xdndTimeOut, '[gnome-shell] this._xdndToggleOverview');
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ vfunc_captured_event(event) {
+ if (event.type() == Clutter.EventType.BUTTON_PRESS ||
+ event.type() == Clutter.EventType.TOUCH_BEGIN) {
+ if (!Main.overview.shouldToggleByCornerOrButton())
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_event(event) {
+ if (event.type() == Clutter.EventType.TOUCH_END ||
+ event.type() == Clutter.EventType.BUTTON_RELEASE) {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ Main.overview.toggle();
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_key_release_event(keyEvent) {
+ let symbol = keyEvent.keyval;
+ if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
+ if (Main.overview.shouldToggleByCornerOrButton()) {
+ Main.overview.toggle();
+ return Clutter.EVENT_STOP;
+ }
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _xdndToggleOverview() {
+ let [x, y] = global.get_pointer();
+ let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
+
+ if (pickedActor == this && Main.overview.shouldToggleByCornerOrButton())
+ Main.overview.toggle();
+
+ GLib.source_remove(this._xdndTimeOut);
+ this._xdndTimeOut = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+});
+
+const UnsafeModeIndicator = GObject.registerClass(
+class UnsafeModeIndicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'channel-insecure-symbolic';
+
+ global.context.bind_property('unsafe-mode',
+ this._indicator, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+});
+
+var QuickSettings = GObject.registerClass(
+class QuickSettings extends PanelMenu.Button {
+ _init() {
+ super._init(0.0, C_('System menu in the top bar', 'System'), true);
+
+ this._indicators = new St.BoxLayout({
+ style_class: 'panel-status-indicators-box',
+ });
+ this.add_child(this._indicators);
+
+ this.setMenu(new QuickSettingsMenu(this, N_QUICK_SETTINGS_COLUMNS));
+
+ if (Config.HAVE_NETWORKMANAGER)
+ this._network = new imports.ui.status.network.Indicator();
+ else
+ this._network = null;
+
+ if (Config.HAVE_BLUETOOTH)
+ this._bluetooth = new imports.ui.status.bluetooth.Indicator();
+ else
+ this._bluetooth = null;
+
+ this._system = new imports.ui.status.system.Indicator();
+ this._volume = new imports.ui.status.volume.Indicator();
+ this._brightness = new imports.ui.status.brightness.Indicator();
+ this._remoteAccess = new imports.ui.status.remoteAccess.RemoteAccessApplet();
+ this._location = new imports.ui.status.location.Indicator();
+ this._thunderbolt = new imports.ui.status.thunderbolt.Indicator();
+ this._nightLight = new imports.ui.status.nightLight.Indicator();
+ this._darkMode = new imports.ui.status.darkMode.Indicator();
+ this._powerProfiles = new imports.ui.status.powerProfiles.Indicator();
+ this._rfkill = new imports.ui.status.rfkill.Indicator();
+ this._autoRotate = new imports.ui.status.autoRotate.Indicator();
+ this._unsafeMode = new UnsafeModeIndicator();
+
+ this._indicators.add_child(this._brightness);
+ this._indicators.add_child(this._remoteAccess);
+ this._indicators.add_child(this._thunderbolt);
+ this._indicators.add_child(this._location);
+ this._indicators.add_child(this._nightLight);
+ if (this._network)
+ this._indicators.add_child(this._network);
+ this._indicators.add_child(this._darkMode);
+ this._indicators.add_child(this._powerProfiles);
+ if (this._bluetooth)
+ this._indicators.add_child(this._bluetooth);
+ this._indicators.add_child(this._rfkill);
+ this._indicators.add_child(this._autoRotate);
+ this._indicators.add_child(this._volume);
+ this._indicators.add_child(this._unsafeMode);
+ this._indicators.add_child(this._system);
+
+ this._addItems(this._system.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS);
+ this._addItems(this._volume.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS);
+ this._addItems(this._brightness.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS);
+
+ this._addItems(this._remoteAccess.quickSettingsItems);
+ this._addItems(this._thunderbolt.quickSettingsItems);
+ this._addItems(this._location.quickSettingsItems);
+ if (this._network)
+ this._addItems(this._network.quickSettingsItems);
+ if (this._bluetooth)
+ this._addItems(this._bluetooth.quickSettingsItems);
+ this._addItems(this._powerProfiles.quickSettingsItems);
+ this._addItems(this._nightLight.quickSettingsItems);
+ this._addItems(this._darkMode.quickSettingsItems);
+ this._addItems(this._rfkill.quickSettingsItems);
+ this._addItems(this._autoRotate.quickSettingsItems);
+ this._addItems(this._unsafeMode.quickSettingsItems);
+ }
+
+ _addItems(items, colSpan = 1) {
+ items.forEach(item => this.menu.addItem(item, colSpan));
+ }
+});
+
+const PANEL_ITEM_IMPLEMENTATIONS = {
+ 'activities': ActivitiesButton,
+ 'appMenu': AppMenuButton,
+ 'quickSettings': QuickSettings,
+ 'dateMenu': imports.ui.dateMenu.DateMenuButton,
+ 'a11y': imports.ui.status.accessibility.ATIndicator,
+ 'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
+ 'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator,
+ 'screenRecording': imports.ui.status.remoteAccess.ScreenRecordingIndicator,
+ 'screenSharing': imports.ui.status.remoteAccess.ScreenSharingIndicator,
+};
+
+var Panel = GObject.registerClass(
+class Panel extends St.Widget {
+ _init() {
+ super._init({
+ name: 'panel',
+ reactive: true,
+ });
+
+ this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
+
+ this._sessionStyle = null;
+
+ this.statusArea = {};
+
+ this.menuManager = new PopupMenu.PopupMenuManager(this);
+
+ this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
+ this.add_child(this._leftBox);
+ this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
+ this.add_child(this._centerBox);
+ this._rightBox = new St.BoxLayout({ name: 'panelRight' });
+ this.add_child(this._rightBox);
+
+ this.connect('button-press-event', this._onButtonPress.bind(this));
+ this.connect('touch-event', this._onTouchEvent.bind(this));
+
+ Main.overview.connect('showing', () => {
+ this.add_style_pseudo_class('overview');
+ });
+ Main.overview.connect('hiding', () => {
+ this.remove_style_pseudo_class('overview');
+ });
+
+ Main.layoutManager.panelBox.add(this);
+ Main.ctrlAltTabManager.addGroup(this, _("Top Bar"), 'focus-top-bar-symbolic',
+ { sortGroup: CtrlAltTab.SortGroup.TOP });
+
+ Main.sessionMode.connect('updated', this._updatePanel.bind(this));
+
+ global.display.connect('workareas-changed', () => this.queue_relayout());
+ this._updatePanel();
+ }
+
+ vfunc_get_preferred_width(_forHeight) {
+ let primaryMonitor = Main.layoutManager.primaryMonitor;
+
+ if (primaryMonitor)
+ return [0, primaryMonitor.width];
+
+ return [0, 0];
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ let allocWidth = box.x2 - box.x1;
+ let allocHeight = box.y2 - box.y1;
+
+ let [, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
+ let [, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
+ let [, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
+
+ let sideWidth, centerWidth;
+ centerWidth = centerNaturalWidth;
+
+ // get workspace area and center date entry relative to it
+ let monitor = Main.layoutManager.findMonitorForActor(this);
+ let centerOffset = 0;
+ if (monitor) {
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
+ centerOffset = 2 * (workArea.x - monitor.x) + workArea.width - monitor.width;
+ }
+
+ sideWidth = Math.max(0, (allocWidth - centerWidth + centerOffset) / 2);
+
+ let childBox = new Clutter.ActorBox();
+
+ childBox.y1 = 0;
+ childBox.y2 = allocHeight;
+ if (this.get_text_direction() == Clutter.TextDirection.RTL) {
+ childBox.x1 = Math.max(allocWidth - Math.min(Math.floor(sideWidth),
+ leftNaturalWidth),
+ 0);
+ childBox.x2 = allocWidth;
+ } else {
+ childBox.x1 = 0;
+ childBox.x2 = Math.min(Math.floor(sideWidth),
+ leftNaturalWidth);
+ }
+ this._leftBox.allocate(childBox);
+
+ childBox.x1 = Math.ceil(sideWidth);
+ childBox.y1 = 0;
+ childBox.x2 = childBox.x1 + centerWidth;
+ childBox.y2 = allocHeight;
+ this._centerBox.allocate(childBox);
+
+ childBox.y1 = 0;
+ childBox.y2 = allocHeight;
+ if (this.get_text_direction() == Clutter.TextDirection.RTL) {
+ childBox.x1 = 0;
+ childBox.x2 = Math.min(Math.floor(sideWidth),
+ rightNaturalWidth);
+ } else {
+ childBox.x1 = Math.max(allocWidth - Math.min(Math.floor(sideWidth),
+ rightNaturalWidth),
+ 0);
+ childBox.x2 = allocWidth;
+ }
+ this._rightBox.allocate(childBox);
+ }
+
+ _tryDragWindow(event) {
+ if (Main.modalCount > 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ const targetActor = global.stage.get_event_actor(event);
+ if (targetActor !== this)
+ return Clutter.EVENT_PROPAGATE;
+
+ const [x, y] = event.get_coords();
+ let dragWindow = this._getDraggableWindowForPosition(x);
+
+ if (!dragWindow)
+ return Clutter.EVENT_PROPAGATE;
+
+ const button = event.type() === Clutter.EventType.BUTTON_PRESS
+ ? event.get_button() : -1;
+
+ return global.display.begin_grab_op(
+ dragWindow,
+ Meta.GrabOp.MOVING,
+ false, /* pointer grab */
+ true, /* frame action */
+ button,
+ event.get_state(),
+ event.get_time(),
+ x, y) ? Clutter.EVENT_STOP : Clutter.EVENT_PROPAGATE;
+ }
+
+ _onButtonPress(actor, event) {
+ if (event.get_button() !== Clutter.BUTTON_PRIMARY)
+ return Clutter.EVENT_PROPAGATE;
+
+ return this._tryDragWindow(event);
+ }
+
+ _onTouchEvent(actor, event) {
+ if (event.type() !== Clutter.EventType.TOUCH_BEGIN)
+ return Clutter.EVENT_PROPAGATE;
+
+ return this._tryDragWindow(event);
+ }
+
+ vfunc_key_press_event(keyEvent) {
+ let symbol = keyEvent.keyval;
+ if (symbol == Clutter.KEY_Escape) {
+ global.display.focus_default_window(keyEvent.time);
+ return Clutter.EVENT_STOP;
+ }
+
+ return super.vfunc_key_press_event(keyEvent);
+ }
+
+ _toggleMenu(indicator) {
+ if (!indicator || !indicator.mapped)
+ return; // menu not supported by current session mode
+
+ let menu = indicator.menu;
+ if (!indicator.reactive)
+ return;
+
+ menu.toggle();
+ if (menu.isOpen)
+ menu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ }
+
+ _closeMenu(indicator) {
+ if (!indicator || !indicator.mapped)
+ return; // menu not supported by current session mode
+
+ if (!indicator.reactive)
+ return;
+
+ indicator.menu.close();
+ }
+
+ toggleAppMenu() {
+ this._toggleMenu(this.statusArea.appMenu);
+ }
+
+ toggleCalendar() {
+ this._toggleMenu(this.statusArea.dateMenu);
+ }
+
+ closeCalendar() {
+ this._closeMenu(this.statusArea.dateMenu);
+ }
+
+ closeQuickSettings() {
+ this._closeMenu(this.statusArea.quickSettings);
+ }
+
+ set boxOpacity(value) {
+ let isReactive = value > 0;
+
+ this._leftBox.opacity = value;
+ this._leftBox.reactive = isReactive;
+ this._centerBox.opacity = value;
+ this._centerBox.reactive = isReactive;
+ this._rightBox.opacity = value;
+ this._rightBox.reactive = isReactive;
+ }
+
+ get boxOpacity() {
+ return this._leftBox.opacity;
+ }
+
+ _updatePanel() {
+ let panel = Main.sessionMode.panel;
+ this._hideIndicators();
+ this._updateBox(panel.left, this._leftBox);
+ this._updateBox(panel.center, this._centerBox);
+ this._updateBox(panel.right, this._rightBox);
+
+ if (panel.left.includes('dateMenu'))
+ Main.messageTray.bannerAlignment = Clutter.ActorAlign.START;
+ else if (panel.right.includes('dateMenu'))
+ Main.messageTray.bannerAlignment = Clutter.ActorAlign.END;
+ // Default to center if there is no dateMenu
+ else
+ Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER;
+
+ if (this._sessionStyle)
+ this.remove_style_class_name(this._sessionStyle);
+
+ this._sessionStyle = Main.sessionMode.panelStyle;
+ if (this._sessionStyle)
+ this.add_style_class_name(this._sessionStyle);
+ }
+
+ _hideIndicators() {
+ for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
+ let indicator = this.statusArea[role];
+ if (!indicator)
+ continue;
+ indicator.container.hide();
+ }
+ }
+
+ _ensureIndicator(role) {
+ let indicator = this.statusArea[role];
+ if (!indicator) {
+ let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
+ if (!constructor) {
+ // This icon is not implemented (this is a bug)
+ return null;
+ }
+ indicator = new constructor(this);
+ this.statusArea[role] = indicator;
+ }
+ return indicator;
+ }
+
+ _updateBox(elements, box) {
+ let nChildren = box.get_n_children();
+
+ for (let i = 0; i < elements.length; i++) {
+ let role = elements[i];
+ let indicator = this._ensureIndicator(role);
+ if (indicator == null)
+ continue;
+
+ this._addToPanelBox(role, indicator, i + nChildren, box);
+ }
+ }
+
+ _addToPanelBox(role, indicator, position, box) {
+ let container = indicator.container;
+ container.show();
+
+ let parent = container.get_parent();
+ if (parent)
+ parent.remove_actor(container);
+
+
+ box.insert_child_at_index(container, position);
+ this.statusArea[role] = indicator;
+ let destroyId = indicator.connect('destroy', emitter => {
+ delete this.statusArea[role];
+ emitter.disconnect(destroyId);
+ });
+ indicator.connect('menu-set', this._onMenuSet.bind(this));
+ this._onMenuSet(indicator);
+ }
+
+ addToStatusArea(role, indicator, position, box) {
+ if (this.statusArea[role])
+ throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`);
+
+ if (!(indicator instanceof PanelMenu.Button))
+ throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
+
+ position ??= 0;
+ let boxes = {
+ left: this._leftBox,
+ center: this._centerBox,
+ right: this._rightBox,
+ };
+ let boxContainer = boxes[box] || this._rightBox;
+ this.statusArea[role] = indicator;
+ this._addToPanelBox(role, indicator, position, boxContainer);
+ return indicator;
+ }
+
+ _onMenuSet(indicator) {
+ if (!indicator.menu || indicator.menu._openChangedId)
+ return;
+
+ this.menuManager.addMenu(indicator.menu);
+
+ indicator.menu._openChangedId = indicator.menu.connect('open-state-changed',
+ (menu, isOpen) => {
+ let boxAlignment;
+ if (this._leftBox.contains(indicator.container))
+ boxAlignment = Clutter.ActorAlign.START;
+ else if (this._centerBox.contains(indicator.container))
+ boxAlignment = Clutter.ActorAlign.CENTER;
+ else if (this._rightBox.contains(indicator.container))
+ boxAlignment = Clutter.ActorAlign.END;
+
+ if (boxAlignment == Main.messageTray.bannerAlignment)
+ Main.messageTray.bannerBlocked = isOpen;
+ });
+ }
+
+ _getDraggableWindowForPosition(stageX) {
+ let workspaceManager = global.workspace_manager;
+ const windows = workspaceManager.get_active_workspace().list_windows();
+ const allWindowsByStacking =
+ global.display.sort_windows_by_stacking(windows).reverse();
+
+ return allWindowsByStacking.find(metaWindow => {
+ let rect = metaWindow.get_frame_rect();
+ return metaWindow.is_on_primary_monitor() &&
+ metaWindow.showing_on_its_workspace() &&
+ metaWindow.get_window_type() != Meta.WindowType.DESKTOP &&
+ metaWindow.maximized_vertically &&
+ stageX > rect.x && stageX < rect.x + rect.width;
+ });
+ }
+});
diff --git a/js/ui/panelMenu.js b/js/ui/panelMenu.js
new file mode 100644
index 0000000..a5445ce
--- /dev/null
+++ b/js/ui/panelMenu.js
@@ -0,0 +1,233 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Button, SystemIndicator */
+
+const { Atk, Clutter, GObject, St } = imports.gi;
+
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+const PopupMenu = imports.ui.popupMenu;
+
+var ButtonBox = GObject.registerClass(
+class ButtonBox extends St.Widget {
+ _init(params) {
+ params = Params.parse(params, {
+ style_class: 'panel-button',
+ x_expand: true,
+ y_expand: true,
+ }, true);
+
+ super._init(params);
+
+ this._delegate = this;
+
+ this.container = new St.Bin({ child: this });
+
+ this.connect('style-changed', this._onStyleChanged.bind(this));
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._minHPadding = this._natHPadding = 0.0;
+ }
+
+ _onStyleChanged(actor) {
+ let themeNode = actor.get_theme_node();
+
+ this._minHPadding = themeNode.get_length('-minimum-hpadding');
+ this._natHPadding = themeNode.get_length('-natural-hpadding');
+ }
+
+ vfunc_get_preferred_width(_forHeight) {
+ let child = this.get_first_child();
+ let minimumSize, naturalSize;
+
+ if (child)
+ [minimumSize, naturalSize] = child.get_preferred_width(-1);
+ else
+ minimumSize = naturalSize = 0;
+
+ minimumSize += 2 * this._minHPadding;
+ naturalSize += 2 * this._natHPadding;
+
+ return [minimumSize, naturalSize];
+ }
+
+ vfunc_get_preferred_height(_forWidth) {
+ let child = this.get_first_child();
+
+ if (child)
+ return child.get_preferred_height(-1);
+
+ return [0, 0];
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ let child = this.get_first_child();
+ if (!child)
+ return;
+
+ let [, natWidth] = child.get_preferred_width(-1);
+
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+
+ let childBox = new Clutter.ActorBox();
+ if (natWidth + 2 * this._natHPadding <= availWidth) {
+ childBox.x1 = this._natHPadding;
+ childBox.x2 = availWidth - this._natHPadding;
+ } else {
+ childBox.x1 = this._minHPadding;
+ childBox.x2 = availWidth - this._minHPadding;
+ }
+
+ childBox.y1 = 0;
+ childBox.y2 = availHeight;
+
+ child.allocate(childBox);
+ }
+
+ _onDestroy() {
+ this.container.child = null;
+ this.container.destroy();
+ }
+});
+
+var Button = GObject.registerClass({
+ Signals: { 'menu-set': {} },
+}, class PanelMenuButton extends ButtonBox {
+ _init(menuAlignment, nameText, dontCreateMenu) {
+ super._init({
+ reactive: true,
+ can_focus: true,
+ track_hover: true,
+ accessible_name: nameText ?? '',
+ accessible_role: Atk.Role.MENU,
+ });
+
+ if (dontCreateMenu)
+ this.menu = new PopupMenu.PopupDummyMenu(this);
+ else
+ this.setMenu(new PopupMenu.PopupMenu(this, menuAlignment, St.Side.TOP, 0));
+
+ this.connect('key-press-event',
+ (o, ev) => global.focus_manager.navigate_from_event(ev));
+ }
+
+ setSensitive(sensitive) {
+ this.reactive = sensitive;
+ this.can_focus = sensitive;
+ this.track_hover = sensitive;
+ }
+
+ setMenu(menu) {
+ if (this.menu)
+ this.menu.destroy();
+
+ this.menu = menu;
+ if (this.menu) {
+ this.menu.actor.add_style_class_name('panel-menu');
+ this.menu.connect('open-state-changed', this._onOpenStateChanged.bind(this));
+ this.menu.actor.connect('key-press-event', this._onMenuKeyPress.bind(this));
+
+ Main.uiGroup.add_actor(this.menu.actor);
+ this.menu.actor.hide();
+ }
+ this.emit('menu-set');
+ }
+
+ vfunc_event(event) {
+ if (this.menu &&
+ (event.type() == Clutter.EventType.TOUCH_BEGIN ||
+ event.type() == Clutter.EventType.BUTTON_PRESS))
+ this.menu.toggle();
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_hide() {
+ super.vfunc_hide();
+
+ if (this.menu)
+ this.menu.close();
+ }
+
+ _onMenuKeyPress(actor, event) {
+ if (global.focus_manager.navigate_from_event(event))
+ return Clutter.EVENT_STOP;
+
+ let symbol = event.get_key_symbol();
+ if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
+ let group = global.focus_manager.get_group(this);
+ if (group) {
+ let direction = symbol == Clutter.KEY_Left ? St.DirectionType.LEFT : St.DirectionType.RIGHT;
+ group.navigate_focus(this, direction, false);
+ return Clutter.EVENT_STOP;
+ }
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onOpenStateChanged(menu, open) {
+ if (open)
+ this.add_style_pseudo_class('active');
+ else
+ this.remove_style_pseudo_class('active');
+
+ // Setting the max-height won't do any good if the minimum height of the
+ // menu is higher then the screen; it's useful if part of the menu is
+ // scrollable so the minimum height is smaller than the natural height
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let verticalMargins = this.menu.actor.margin_top + this.menu.actor.margin_bottom;
+
+ // The workarea and margin dimensions are in physical pixels, but CSS
+ // measures are in logical pixels, so make sure to consider the scale
+ // factor when computing max-height
+ let maxHeight = Math.round((workArea.height - verticalMargins) / scaleFactor);
+ this.menu.actor.style = `max-height: ${maxHeight}px;`;
+ }
+
+ _onDestroy() {
+ if (this.menu)
+ this.menu.destroy();
+ super._onDestroy();
+ }
+});
+
+/* SystemIndicator:
+ *
+ * This class manages one system indicator, which are the icons
+ * that you see at the top right. A system indicator is composed
+ * of an icon and a menu section, which will be composed into the
+ * aggregate menu.
+ */
+var SystemIndicator = GObject.registerClass(
+class SystemIndicator extends St.BoxLayout {
+ _init() {
+ super._init({
+ style_class: 'panel-status-indicators-box',
+ reactive: true,
+ visible: false,
+ });
+ this.menu = new PopupMenu.PopupMenuSection();
+ }
+
+ get indicators() {
+ let klass = this.constructor.name;
+ let { stack } = new Error();
+ log(`Usage of indicator.indicators is deprecated for ${klass}\n${stack}`);
+ return this;
+ }
+
+ _syncIndicatorsVisible() {
+ this.visible = this.get_children().some(a => a.visible);
+ }
+
+ _addIndicator() {
+ let icon = new St.Icon({ style_class: 'system-status-icon' });
+ this.add_actor(icon);
+ icon.connect('notify::visible', this._syncIndicatorsVisible.bind(this));
+ this._syncIndicatorsVisible();
+ return icon;
+ }
+});
diff --git a/js/ui/pointerA11yTimeout.js b/js/ui/pointerA11yTimeout.js
new file mode 100644
index 0000000..263cc3e
--- /dev/null
+++ b/js/ui/pointerA11yTimeout.js
@@ -0,0 +1,134 @@
+/* exported PointerA11yTimeout */
+const { Clutter, GObject, Meta, St } = imports.gi;
+const Main = imports.ui.main;
+const Cairo = imports.cairo;
+
+const SUCCESS_ZOOM_OUT_DURATION = 150;
+
+var PieTimer = GObject.registerClass({
+ Properties: {
+ 'angle': GObject.ParamSpec.double(
+ 'angle', 'angle', 'angle',
+ GObject.ParamFlags.READWRITE,
+ 0, 2 * Math.PI, 0),
+ },
+}, class PieTimer extends St.DrawingArea {
+ _init() {
+ this._angle = 0;
+ super._init({
+ style_class: 'pie-timer',
+ opacity: 0,
+ visible: false,
+ can_focus: false,
+ reactive: false,
+ });
+
+ this.set_pivot_point(0.5, 0.5);
+ }
+
+ get angle() {
+ return this._angle;
+ }
+
+ set angle(angle) {
+ if (this._angle == angle)
+ return;
+
+ this._angle = angle;
+ this.notify('angle');
+ this.queue_repaint();
+ }
+
+ vfunc_repaint() {
+ let node = this.get_theme_node();
+ let backgroundColor = node.get_color('-pie-background-color');
+ let borderColor = node.get_color('-pie-border-color');
+ let borderWidth = node.get_length('-pie-border-width');
+ let [width, height] = this.get_surface_size();
+ let radius = Math.min(width / 2, height / 2);
+
+ let startAngle = 3 * Math.PI / 2;
+ let endAngle = startAngle + this._angle;
+
+ let cr = this.get_context();
+ cr.setLineCap(Cairo.LineCap.ROUND);
+ cr.setLineJoin(Cairo.LineJoin.ROUND);
+ cr.translate(width / 2, height / 2);
+
+ if (this._angle < 2 * Math.PI)
+ cr.moveTo(0, 0);
+
+ cr.arc(0, 0, radius - borderWidth, startAngle, endAngle);
+
+ if (this._angle < 2 * Math.PI)
+ cr.lineTo(0, 0);
+
+ cr.closePath();
+
+ cr.setLineWidth(0);
+ Clutter.cairo_set_source_color(cr, backgroundColor);
+ cr.fillPreserve();
+
+ cr.setLineWidth(borderWidth);
+ Clutter.cairo_set_source_color(cr, borderColor);
+ cr.stroke();
+
+ cr.$dispose();
+ }
+
+ start(x, y, duration) {
+ this.x = x - this.width / 2;
+ this.y = y - this.height / 2;
+ this.show();
+
+ this.ease({
+ opacity: 255,
+ duration: duration / 4,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+
+ this.ease_property('angle', 2 * Math.PI, {
+ duration,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: this._onTransitionComplete.bind(this),
+ });
+ }
+
+ _onTransitionComplete() {
+ this.ease({
+ scale_x: 2,
+ scale_y: 2,
+ opacity: 0,
+ duration: SUCCESS_ZOOM_OUT_DURATION,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this.destroy(),
+ });
+ }
+});
+
+var PointerA11yTimeout = class PointerA11yTimeout {
+ constructor() {
+ let seat = Clutter.get_default_backend().get_default_seat();
+
+ seat.connect('ptr-a11y-timeout-started', (o, device, type, timeout) => {
+ let [x, y] = global.get_pointer();
+
+ this._pieTimer = new PieTimer();
+ Main.uiGroup.add_actor(this._pieTimer);
+ Main.uiGroup.set_child_above_sibling(this._pieTimer, null);
+
+ this._pieTimer.start(x, y, timeout);
+
+ if (type == Clutter.PointerA11yTimeoutType.GESTURE)
+ global.display.set_cursor(Meta.Cursor.CROSSHAIR);
+ });
+
+ seat.connect('ptr-a11y-timeout-stopped', (o, device, type, clicked) => {
+ if (!clicked)
+ this._pieTimer.destroy();
+
+ if (type == Clutter.PointerA11yTimeoutType.GESTURE)
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ });
+ }
+};
diff --git a/js/ui/pointerWatcher.js b/js/ui/pointerWatcher.js
new file mode 100644
index 0000000..2af35b6
--- /dev/null
+++ b/js/ui/pointerWatcher.js
@@ -0,0 +1,125 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported getPointerWatcher */
+
+const { GLib } = imports.gi;
+
+// We stop polling if the user is idle for more than this amount of time
+var IDLE_TIME = 1000;
+
+// This file implements a reasonably efficient system for tracking the position
+// of the mouse pointer. We simply query the pointer from the X server in a loop,
+// but we turn off the polling when the user is idle.
+
+let _pointerWatcher = null;
+function getPointerWatcher() {
+ if (_pointerWatcher == null)
+ _pointerWatcher = new PointerWatcher();
+
+ return _pointerWatcher;
+}
+
+var PointerWatch = class {
+ constructor(watcher, interval, callback) {
+ this.watcher = watcher;
+ this.interval = interval;
+ this.callback = callback;
+ }
+
+ // remove:
+ // remove this watch. This function may safely be called
+ // while the callback is executing.
+ remove() {
+ this.watcher._removeWatch(this);
+ }
+};
+
+var PointerWatcher = class {
+ constructor() {
+ this._idleMonitor = global.backend.get_core_idle_monitor();
+ this._idleMonitor.add_idle_watch(IDLE_TIME, this._onIdleMonitorBecameIdle.bind(this));
+ this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
+ this._watches = [];
+ this.pointerX = null;
+ this.pointerY = null;
+ }
+
+ // addWatch:
+ // @interval: hint as to the time resolution needed. When the user is
+ // not idle, the position of the pointer will be queried at least
+ // once every this many milliseconds.
+ // @callback to call when the pointer position changes - takes
+ // two arguments, X and Y.
+ //
+ // Set up a watch on the position of the mouse pointer. Returns a
+ // PointerWatch object which has a remove() method to remove the watch.
+ addWatch(interval, callback) {
+ // Avoid unreliably calling the watch for the current position
+ this._updatePointer();
+
+ let watch = new PointerWatch(this, interval, callback);
+ this._watches.push(watch);
+ this._updateTimeout();
+ return watch;
+ }
+
+ _removeWatch(watch) {
+ for (let i = 0; i < this._watches.length; i++) {
+ if (this._watches[i] == watch) {
+ this._watches.splice(i, 1);
+ this._updateTimeout();
+ return;
+ }
+ }
+ }
+
+ _onIdleMonitorBecameActive() {
+ this._idle = false;
+ this._updatePointer();
+ this._updateTimeout();
+ }
+
+ _onIdleMonitorBecameIdle() {
+ this._idle = true;
+ this._idleMonitor.add_user_active_watch(this._onIdleMonitorBecameActive.bind(this));
+ this._updateTimeout();
+ }
+
+ _updateTimeout() {
+ if (this._timeoutId) {
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+ }
+
+ if (this._idle || this._watches.length == 0)
+ return;
+
+ let minInterval = this._watches[0].interval;
+ for (let i = 1; i < this._watches.length; i++)
+ minInterval = Math.min(this._watches[i].interval, minInterval);
+
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, minInterval,
+ this._onTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._onTimeout');
+ }
+
+ _onTimeout() {
+ this._updatePointer();
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ _updatePointer() {
+ let [x, y] = global.get_pointer();
+ if (this.pointerX == x && this.pointerY == y)
+ return;
+
+ this.pointerX = x;
+ this.pointerY = y;
+
+ for (let i = 0; i < this._watches.length;) {
+ let watch = this._watches[i];
+ watch.callback(x, y);
+ if (watch == this._watches[i]) // guard against self-removal
+ i++;
+ }
+ }
+};
diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js
new file mode 100644
index 0000000..2f57c58
--- /dev/null
+++ b/js/ui/popupMenu.js
@@ -0,0 +1,1415 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported PopupMenuItem, PopupSeparatorMenuItem, Switch, PopupSwitchMenuItem,
+ PopupImageMenuItem, PopupMenu, PopupDummyMenu, PopupSubMenu,
+ PopupMenuSection, PopupSubMenuMenuItem, PopupMenuManager */
+
+const { Atk, Clutter, Gio, GObject, Graphene, Shell, St } = imports.gi;
+const Signals = imports.misc.signals;
+
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+var Ornament = {
+ NONE: 0,
+ DOT: 1,
+ CHECK: 2,
+ HIDDEN: 3,
+};
+
+function isPopupMenuItemVisible(child) {
+ if (child._delegate instanceof PopupMenuSection) {
+ if (child._delegate.isEmpty())
+ return false;
+ }
+ return child.visible;
+}
+
+/**
+ * arrowIcon
+ * @param {St.Side} side - Side to which the arrow points.
+ * @returns {St.Icon} a new arrow icon
+ */
+function arrowIcon(side) {
+ let iconName;
+ switch (side) {
+ case St.Side.TOP:
+ iconName = 'pan-up-symbolic';
+ break;
+ case St.Side.RIGHT:
+ iconName = 'pan-end-symbolic';
+ break;
+ case St.Side.BOTTOM:
+ iconName = 'pan-down-symbolic';
+ break;
+ case St.Side.LEFT:
+ iconName = 'pan-start-symbolic';
+ break;
+ }
+
+ const arrow = new St.Icon({
+ style_class: 'popup-menu-arrow',
+ icon_name: iconName,
+ accessible_role: Atk.Role.ARROW,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ return arrow;
+}
+
+var PopupBaseMenuItem = GObject.registerClass({
+ Properties: {
+ 'active': GObject.ParamSpec.boolean('active', 'active', 'active',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'sensitive': GObject.ParamSpec.boolean('sensitive', 'sensitive', 'sensitive',
+ GObject.ParamFlags.READWRITE,
+ true),
+ },
+ Signals: {
+ 'activate': { param_types: [Clutter.Event.$gtype] },
+ },
+}, class PopupBaseMenuItem extends St.BoxLayout {
+ _init(params) {
+ params = Params.parse(params, {
+ reactive: true,
+ activate: true,
+ hover: true,
+ style_class: null,
+ can_focus: true,
+ });
+ super._init({
+ style_class: 'popup-menu-item',
+ reactive: params.reactive,
+ track_hover: params.reactive,
+ can_focus: params.can_focus,
+ accessible_role: Atk.Role.MENU_ITEM,
+ });
+ this._delegate = this;
+
+ this._ornament = Ornament.NONE;
+ this._ornamentLabel = new St.Label({ style_class: 'popup-menu-ornament' });
+ this.add(this._ornamentLabel);
+
+ this._parent = null;
+ this._active = false;
+ this._activatable = params.reactive && params.activate;
+ this._sensitive = true;
+
+ if (!this._activatable)
+ this.add_style_class_name('popup-inactive-menu-item');
+
+ if (params.style_class)
+ this.add_style_class_name(params.style_class);
+
+ if (params.reactive && params.hover)
+ this.bind_property('hover', this, 'active', GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ get actor() {
+ /* This is kept for compatibility with current implementation, and we
+ don't want to warn here yet since PopupMenu depends on this */
+ return this;
+ }
+
+ _getTopMenu() {
+ if (this._parent)
+ return this._parent._getTopMenu();
+ else
+ return this;
+ }
+
+ _setParent(parent) {
+ this._parent = parent;
+ }
+
+ vfunc_button_press_event() {
+ if (!this._activatable)
+ return Clutter.EVENT_PROPAGATE;
+
+ // This is the CSS active state
+ this.add_style_pseudo_class('active');
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_button_release_event() {
+ if (!this._activatable)
+ return Clutter.EVENT_PROPAGATE;
+
+ this.remove_style_pseudo_class('active');
+ this.activate(Clutter.get_current_event());
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_touch_event(touchEvent) {
+ if (!this._activatable)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (touchEvent.type == Clutter.EventType.TOUCH_END) {
+ this.remove_style_pseudo_class('active');
+ this.activate(Clutter.get_current_event());
+ return Clutter.EVENT_STOP;
+ } else if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN) {
+ // This is the CSS active state
+ this.add_style_pseudo_class('active');
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_key_press_event(keyEvent) {
+ if (global.focus_manager.navigate_from_event(Clutter.get_current_event()))
+ return Clutter.EVENT_STOP;
+
+ if (!this._activatable)
+ return super.vfunc_key_press_event(keyEvent);
+
+ let state = keyEvent.modifier_state;
+
+ // if user has a modifier down (except capslock and numlock)
+ // then don't handle the key press here
+ state &= ~Clutter.ModifierType.LOCK_MASK;
+ state &= ~Clutter.ModifierType.MOD2_MASK;
+ state &= Clutter.ModifierType.MODIFIER_MASK;
+
+ if (state)
+ return Clutter.EVENT_PROPAGATE;
+
+ let symbol = keyEvent.keyval;
+ if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
+ this.activate(Clutter.get_current_event());
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_key_focus_in() {
+ super.vfunc_key_focus_in();
+ this.active = true;
+ }
+
+ vfunc_key_focus_out() {
+ super.vfunc_key_focus_out();
+ this.active = false;
+ }
+
+ activate(event) {
+ this.emit('activate', event);
+ }
+
+ get active() {
+ return this._active;
+ }
+
+ set active(active) {
+ let activeChanged = active != this.active;
+ if (activeChanged) {
+ this._active = active;
+ if (active) {
+ this.add_style_class_name('selected');
+ if (this.can_focus)
+ this.grab_key_focus();
+ } else {
+ this.remove_style_class_name('selected');
+ // Remove the CSS active state if the user press the button and
+ // while holding moves to another menu item, so we don't paint all items.
+ // The correct behaviour would be to set the new item with the CSS
+ // active state as well, but button-press-event is not triggered,
+ // so we should track it in our own, which would involve some work
+ // in the container
+ this.remove_style_pseudo_class('active');
+ }
+ this.notify('active');
+ }
+ }
+
+ syncSensitive() {
+ let sensitive = this.sensitive;
+ this.reactive = sensitive;
+ this.can_focus = sensitive;
+ this.notify('sensitive');
+ return sensitive;
+ }
+
+ getSensitive() {
+ const parentSensitive = this._parent?.sensitive ?? true;
+ return this._activatable && this._sensitive && parentSensitive;
+ }
+
+ setSensitive(sensitive) {
+ if (this._sensitive == sensitive)
+ return;
+
+ this._sensitive = sensitive;
+ this.syncSensitive();
+ }
+
+ get sensitive() {
+ return this.getSensitive();
+ }
+
+ set sensitive(sensitive) {
+ this.setSensitive(sensitive);
+ }
+
+ setOrnament(ornament) {
+ if (ornament == this._ornament)
+ return;
+
+ this._ornament = ornament;
+
+ if (ornament == Ornament.DOT) {
+ this._ornamentLabel.text = '\u2022';
+ this.add_accessible_state(Atk.StateType.CHECKED);
+ } else if (ornament == Ornament.CHECK) {
+ this._ornamentLabel.text = '\u2713';
+ this.add_accessible_state(Atk.StateType.CHECKED);
+ } else if (ornament == Ornament.NONE || ornament == Ornament.HIDDEN) {
+ this._ornamentLabel.text = '';
+ this.remove_accessible_state(Atk.StateType.CHECKED);
+ }
+
+ this._ornamentLabel.visible = ornament != Ornament.HIDDEN;
+ }
+});
+
+var PopupMenuItem = GObject.registerClass(
+class PopupMenuItem extends PopupBaseMenuItem {
+ _init(text, params) {
+ super._init(params);
+
+ this.label = new St.Label({
+ text,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this.label);
+ this.label_actor = this.label;
+ }
+});
+
+
+var PopupSeparatorMenuItem = GObject.registerClass(
+class PopupSeparatorMenuItem extends PopupBaseMenuItem {
+ _init(text) {
+ super._init({
+ style_class: 'popup-separator-menu-item',
+ reactive: false,
+ can_focus: false,
+ });
+
+ this.label = new St.Label({ text: text || '' });
+ this.add(this.label);
+ this.label_actor = this.label;
+
+ this.label.connect('notify::text',
+ this._syncVisibility.bind(this));
+ this._syncVisibility();
+
+ this._separator = new St.Widget({
+ style_class: 'popup-separator-menu-item-separator',
+ x_expand: true,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this._separator);
+ }
+
+ _syncVisibility() {
+ this.label.visible = this.label.text != '';
+ }
+});
+
+var Switch = GObject.registerClass({
+ Properties: {
+ 'state': GObject.ParamSpec.boolean(
+ 'state', 'state', 'state',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+}, class Switch extends St.Bin {
+ _init(state) {
+ this._state = false;
+
+ super._init({
+ style_class: 'toggle-switch',
+ accessible_role: Atk.Role.CHECK_BOX,
+ state,
+ });
+ }
+
+ get state() {
+ return this._state;
+ }
+
+ set state(state) {
+ if (this._state === state)
+ return;
+
+ if (state)
+ this.add_style_pseudo_class('checked');
+ else
+ this.remove_style_pseudo_class('checked');
+
+ this._state = state;
+ this.notify('state');
+ }
+
+ toggle() {
+ this.state = !this.state;
+ }
+});
+
+var PopupSwitchMenuItem = GObject.registerClass({
+ Signals: { 'toggled': { param_types: [GObject.TYPE_BOOLEAN] } },
+}, class PopupSwitchMenuItem extends PopupBaseMenuItem {
+ _init(text, active, params) {
+ super._init(params);
+
+ this.label = new St.Label({
+ text,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._switch = new Switch(active);
+
+ this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
+ this.checkAccessibleState();
+ this.label_actor = this.label;
+
+ this.add_child(this.label);
+
+ this._statusBin = new St.Bin({
+ x_align: Clutter.ActorAlign.END,
+ x_expand: true,
+ });
+ this.add_child(this._statusBin);
+
+ this._statusLabel = new St.Label({
+ text: '',
+ style_class: 'popup-status-menu-item',
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._statusBin.child = this._switch;
+ }
+
+ setStatus(text) {
+ if (text != null) {
+ this._statusLabel.text = text;
+ this._statusBin.child = this._statusLabel;
+ this.reactive = false;
+ this.accessible_role = Atk.Role.MENU_ITEM;
+ } else {
+ this._statusBin.child = this._switch;
+ this.reactive = true;
+ this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
+ }
+ this.checkAccessibleState();
+ }
+
+ activate(event) {
+ if (this._switch.mapped)
+ this.toggle();
+
+ // we allow pressing space to toggle the switch
+ // without closing the menu
+ if (event.type() == Clutter.EventType.KEY_PRESS &&
+ event.get_key_symbol() == Clutter.KEY_space)
+ return;
+
+ super.activate(event);
+ }
+
+ toggle() {
+ this._switch.toggle();
+ this.emit('toggled', this._switch.state);
+ this.checkAccessibleState();
+ }
+
+ get state() {
+ return this._switch.state;
+ }
+
+ setToggleState(state) {
+ this._switch.state = state;
+ this.checkAccessibleState();
+ }
+
+ checkAccessibleState() {
+ switch (this.accessible_role) {
+ case Atk.Role.CHECK_MENU_ITEM:
+ if (this._switch.state)
+ this.add_accessible_state(Atk.StateType.CHECKED);
+ else
+ this.remove_accessible_state(Atk.StateType.CHECKED);
+ break;
+ default:
+ this.remove_accessible_state(Atk.StateType.CHECKED);
+ }
+ }
+});
+
+var PopupImageMenuItem = GObject.registerClass(
+class PopupImageMenuItem extends PopupBaseMenuItem {
+ _init(text, icon, params) {
+ super._init(params);
+
+ this._icon = new St.Icon({
+ style_class: 'popup-menu-icon',
+ x_align: Clutter.ActorAlign.END,
+ });
+ this.add_child(this._icon);
+ this.label = new St.Label({
+ text,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this.label);
+ this.label_actor = this.label;
+
+ this.set_child_above_sibling(this._ornamentLabel, this.label);
+
+ this.setIcon(icon);
+ }
+
+ setIcon(icon) {
+ // The 'icon' parameter can be either a Gio.Icon or a string.
+ if (icon instanceof GObject.Object && GObject.type_is_a(icon, Gio.Icon))
+ this._icon.gicon = icon;
+ else
+ this._icon.icon_name = icon;
+ }
+});
+
+var PopupMenuBase = class extends Signals.EventEmitter {
+ constructor(sourceActor, styleClass) {
+ super();
+
+ if (this.constructor === PopupMenuBase)
+ throw new TypeError(`Cannot instantiate abstract class ${this.constructor.name}`);
+
+ this.sourceActor = sourceActor;
+ this.focusActor = sourceActor;
+ this._parent = null;
+
+ this.box = new St.BoxLayout({
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ });
+
+ if (styleClass !== undefined)
+ this.box.style_class = styleClass;
+ this.length = 0;
+
+ this.isOpen = false;
+
+ this._activeMenuItem = null;
+ this._settingsActions = { };
+
+ this._sensitive = true;
+
+ Main.sessionMode.connectObject('updated', () => this._sessionUpdated(), this);
+ }
+
+ _getTopMenu() {
+ if (this._parent)
+ return this._parent._getTopMenu();
+ else
+ return this;
+ }
+
+ _setParent(parent) {
+ this._parent = parent;
+ }
+
+ getSensitive() {
+ const parentSensitive = this._parent?.sensitive ?? true;
+ return this._sensitive && parentSensitive;
+ }
+
+ setSensitive(sensitive) {
+ this._sensitive = sensitive;
+ this.emit('notify::sensitive');
+ }
+
+ get sensitive() {
+ return this.getSensitive();
+ }
+
+ set sensitive(sensitive) {
+ this.setSensitive(sensitive);
+ }
+
+ _sessionUpdated() {
+ this._setSettingsVisibility(Main.sessionMode.allowSettings);
+ this.close();
+ }
+
+ addAction(title, callback, icon) {
+ let menuItem;
+ if (icon != undefined)
+ menuItem = new PopupImageMenuItem(title, icon);
+ else
+ menuItem = new PopupMenuItem(title);
+
+ this.addMenuItem(menuItem);
+ menuItem.connect('activate', (o, event) => {
+ callback(event);
+ });
+
+ return menuItem;
+ }
+
+ addSettingsAction(title, desktopFile) {
+ let menuItem = this.addAction(title, () => {
+ let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
+
+ if (!app) {
+ log(`Settings panel for desktop file ${desktopFile} could not be loaded!`);
+ return;
+ }
+
+ Main.overview.hide();
+ Main.panel.closeQuickSettings();
+ app.activate();
+ });
+
+ menuItem.visible = Main.sessionMode.allowSettings;
+ this._settingsActions[desktopFile] = menuItem;
+
+ return menuItem;
+ }
+
+ _setSettingsVisibility(visible) {
+ for (let id in this._settingsActions) {
+ let item = this._settingsActions[id];
+ item.visible = visible;
+ }
+ }
+
+ isEmpty() {
+ let hasVisibleChildren = this.box.get_children().some(child => {
+ if (child._delegate instanceof PopupSeparatorMenuItem)
+ return false;
+ return isPopupMenuItemVisible(child);
+ });
+
+ return !hasVisibleChildren;
+ }
+
+ itemActivated(animate) {
+ if (animate == undefined)
+ animate = BoxPointer.PopupAnimation.FULL;
+
+ this._getTopMenu().close(animate);
+ }
+
+ _subMenuActiveChanged(submenu, submenuItem) {
+ if (this._activeMenuItem && this._activeMenuItem != submenuItem)
+ this._activeMenuItem.active = false;
+ this._activeMenuItem = submenuItem;
+ this.emit('active-changed', submenuItem);
+ }
+
+ _connectItemSignals(menuItem) {
+ menuItem.connectObject(
+ 'notify::active', () => {
+ const { active } = menuItem;
+ if (active && this._activeMenuItem !== menuItem) {
+ if (this._activeMenuItem)
+ this._activeMenuItem.active = false;
+ this._activeMenuItem = menuItem;
+ this.emit('active-changed', menuItem);
+ } else if (!active && this._activeMenuItem === menuItem) {
+ this._activeMenuItem = null;
+ this.emit('active-changed', null);
+ }
+ },
+ 'notify::sensitive', () => {
+ const { sensitive } = menuItem;
+ if (!sensitive && this._activeMenuItem === menuItem) {
+ if (!this.actor.navigate_focus(menuItem.actor,
+ St.DirectionType.TAB_FORWARD, true))
+ this.actor.grab_key_focus();
+ } else if (sensitive && this._activeMenuItem === null) {
+ if (global.stage.get_key_focus() === this.actor)
+ menuItem.actor.grab_key_focus();
+ }
+ },
+ 'activate', () => {
+ this.emit('activate', menuItem);
+ this.itemActivated(BoxPointer.PopupAnimation.FULL);
+ }, GObject.ConnectFlags.AFTER,
+ 'destroy', () => {
+ if (menuItem === this._activeMenuItem)
+ this._activeMenuItem = null;
+ }, this);
+
+ this.connectObject('notify::sensitive',
+ () => menuItem.syncSensitive(), menuItem);
+ }
+
+ _updateSeparatorVisibility(menuItem) {
+ if (menuItem.label.text)
+ return;
+
+ let children = this.box.get_children();
+
+ let index = children.indexOf(menuItem.actor);
+
+ if (index < 0)
+ return;
+
+ let childBeforeIndex = index - 1;
+
+ while (childBeforeIndex >= 0 && !isPopupMenuItemVisible(children[childBeforeIndex]))
+ childBeforeIndex--;
+
+ if (childBeforeIndex < 0 ||
+ children[childBeforeIndex]._delegate instanceof PopupSeparatorMenuItem) {
+ menuItem.actor.hide();
+ return;
+ }
+
+ let childAfterIndex = index + 1;
+
+ while (childAfterIndex < children.length && !isPopupMenuItemVisible(children[childAfterIndex]))
+ childAfterIndex++;
+
+ if (childAfterIndex >= children.length ||
+ children[childAfterIndex]._delegate instanceof PopupSeparatorMenuItem) {
+ menuItem.actor.hide();
+ return;
+ }
+
+ menuItem.show();
+ }
+
+ moveMenuItem(menuItem, position) {
+ let items = this._getMenuItems();
+ let i = 0;
+
+ while (i < items.length && position > 0) {
+ if (items[i] != menuItem)
+ position--;
+ i++;
+ }
+
+ if (i < items.length) {
+ if (items[i] != menuItem)
+ this.box.set_child_below_sibling(menuItem.actor, items[i].actor);
+ } else {
+ this.box.set_child_above_sibling(menuItem.actor, null);
+ }
+ }
+
+ addMenuItem(menuItem, position) {
+ let beforeItem = null;
+ if (position == undefined) {
+ this.box.add(menuItem.actor);
+ } else {
+ let items = this._getMenuItems();
+ if (position < items.length) {
+ beforeItem = items[position].actor;
+ this.box.insert_child_below(menuItem.actor, beforeItem);
+ } else {
+ this.box.add(menuItem.actor);
+ }
+ }
+
+ if (menuItem instanceof PopupMenuSection) {
+ menuItem.connectObject(
+ 'active-changed', this._subMenuActiveChanged.bind(this),
+ 'destroy', () => this.length--, this);
+
+ this.connectObject(
+ 'open-state-changed', (self, open) => {
+ if (open)
+ menuItem.open();
+ else
+ menuItem.close();
+ },
+ 'menu-closed', () => menuItem.emit('menu-closed'),
+ 'notify::sensitive', () => menuItem.emit('notify::sensitive'),
+ menuItem);
+ } else if (menuItem instanceof PopupSubMenuMenuItem) {
+ if (beforeItem == null)
+ this.box.add(menuItem.menu.actor);
+ else
+ this.box.insert_child_below(menuItem.menu.actor, beforeItem);
+
+ this._connectItemSignals(menuItem);
+ menuItem.menu.connectObject('active-changed',
+ this._subMenuActiveChanged.bind(this), this);
+ this.connectObject('menu-closed', () => {
+ menuItem.menu.close(BoxPointer.PopupAnimation.NONE);
+ }, menuItem);
+ } else if (menuItem instanceof PopupSeparatorMenuItem) {
+ this._connectItemSignals(menuItem);
+
+ // updateSeparatorVisibility needs to get called any time the
+ // separator's adjacent siblings change visibility or position.
+ // open-state-changed isn't exactly that, but doing it in more
+ // precise ways would require a lot more bookkeeping.
+ this.connectObject('open-state-changed', () => {
+ this._updateSeparatorVisibility(menuItem);
+ }, menuItem);
+ } else if (menuItem instanceof PopupBaseMenuItem) {
+ this._connectItemSignals(menuItem);
+ } else {
+ throw TypeError("Invalid argument to PopupMenuBase.addMenuItem()");
+ }
+
+ menuItem._setParent(this);
+
+ this.length++;
+ }
+
+ _getMenuItems() {
+ return this.box.get_children().map(a => a._delegate).filter(item => {
+ return item instanceof PopupBaseMenuItem || item instanceof PopupMenuSection;
+ });
+ }
+
+ get firstMenuItem() {
+ let items = this._getMenuItems();
+ if (items.length)
+ return items[0];
+ else
+ return null;
+ }
+
+ get numMenuItems() {
+ return this._getMenuItems().length;
+ }
+
+ removeAll() {
+ let children = this._getMenuItems();
+ for (let i = 0; i < children.length; i++) {
+ let item = children[i];
+ item.destroy();
+ }
+ }
+
+ toggle() {
+ if (this.isOpen)
+ this.close(BoxPointer.PopupAnimation.FULL);
+ else
+ this.open(BoxPointer.PopupAnimation.FULL);
+ }
+
+ destroy() {
+ this.close();
+ this.removeAll();
+ this.actor.destroy();
+
+ this.emit('destroy');
+
+ Main.sessionMode.disconnectObject(this);
+ }
+};
+
+var PopupMenu = class extends PopupMenuBase {
+ constructor(sourceActor, arrowAlignment, arrowSide) {
+ super(sourceActor, 'popup-menu-content');
+
+ this._arrowAlignment = arrowAlignment;
+ this._arrowSide = arrowSide;
+
+ this._boxPointer = new BoxPointer.BoxPointer(arrowSide);
+ this.actor = this._boxPointer;
+ this.actor._delegate = this;
+ this.actor.style_class = 'popup-menu-boxpointer';
+
+ this._boxPointer.bin.set_child(this.box);
+ this.actor.add_style_class_name('popup-menu');
+
+ global.focus_manager.add_group(this.actor);
+ this.actor.reactive = true;
+
+ if (this.sourceActor) {
+ this.sourceActor.connectObject(
+ 'key-press-event', this._onKeyPress.bind(this),
+ 'notify::mapped', () => {
+ if (!this.sourceActor.mapped)
+ this.close();
+ }, this);
+ }
+
+ this._systemModalOpenedId = 0;
+ this._openedSubMenu = null;
+ }
+
+ _setOpenedSubMenu(submenu) {
+ if (this._openedSubMenu)
+ this._openedSubMenu.close(true);
+
+ this._openedSubMenu = submenu;
+ }
+
+ _onKeyPress(actor, event) {
+ // Disable toggling the menu by keyboard
+ // when it cannot be toggled by pointer
+ if (!actor.reactive)
+ return Clutter.EVENT_PROPAGATE;
+
+ let navKey;
+ switch (this._boxPointer.arrowSide) {
+ case St.Side.TOP:
+ navKey = Clutter.KEY_Down;
+ break;
+ case St.Side.BOTTOM:
+ navKey = Clutter.KEY_Up;
+ break;
+ case St.Side.LEFT:
+ navKey = Clutter.KEY_Right;
+ break;
+ case St.Side.RIGHT:
+ navKey = Clutter.KEY_Left;
+ break;
+ }
+
+ let state = event.get_state();
+
+ // if user has a modifier down (except capslock and numlock)
+ // then don't handle the key press here
+ state &= ~Clutter.ModifierType.LOCK_MASK;
+ state &= ~Clutter.ModifierType.MOD2_MASK;
+ state &= Clutter.ModifierType.MODIFIER_MASK;
+
+ if (state)
+ return Clutter.EVENT_PROPAGATE;
+
+ let symbol = event.get_key_symbol();
+
+ if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
+ this.toggle();
+ return Clutter.EVENT_STOP;
+ } else if (symbol == navKey) {
+ if (!this.isOpen)
+ this.toggle();
+ this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ return Clutter.EVENT_STOP;
+ } else {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ }
+
+ setArrowOrigin(origin) {
+ this._boxPointer.setArrowOrigin(origin);
+ }
+
+ setSourceAlignment(alignment) {
+ this._boxPointer.setSourceAlignment(alignment);
+ }
+
+ open(animate) {
+ if (this.isOpen)
+ return;
+
+ if (this.isEmpty())
+ return;
+
+ if (!this._systemModalOpenedId) {
+ this._systemModalOpenedId =
+ Main.layoutManager.connect('system-modal-opened', () => this.close());
+ }
+
+ this.isOpen = true;
+
+ this._boxPointer.setPosition(this.sourceActor, this._arrowAlignment);
+ this._boxPointer.open(animate);
+
+ this.actor.get_parent().set_child_above_sibling(this.actor, null);
+
+ this.emit('open-state-changed', true);
+ }
+
+ close(animate) {
+ if (this._activeMenuItem)
+ this._activeMenuItem.active = false;
+
+ if (this._boxPointer.visible) {
+ this._boxPointer.close(animate, () => {
+ this.emit('menu-closed');
+ });
+ }
+
+ if (!this.isOpen)
+ return;
+
+ this.isOpen = false;
+ this.emit('open-state-changed', false);
+ }
+
+ destroy() {
+ this.sourceActor?.disconnectObject(this);
+
+ if (this._systemModalOpenedId)
+ Main.layoutManager.disconnect(this._systemModalOpenedId);
+ this._systemModalOpenedId = 0;
+
+ super.destroy();
+ }
+};
+
+var PopupDummyMenu = class extends Signals.EventEmitter {
+ constructor(sourceActor) {
+ super();
+
+ this.sourceActor = sourceActor;
+ this.actor = sourceActor;
+ this.actor._delegate = this;
+ }
+
+ getSensitive() {
+ return true;
+ }
+
+ get sensitive() {
+ return this.getSensitive();
+ }
+
+ open() {
+ if (this.isOpen)
+ return;
+ this.isOpen = true;
+ this.emit('open-state-changed', true);
+ }
+
+ close() {
+ if (!this.isOpen)
+ return;
+ this.isOpen = false;
+ this.emit('open-state-changed', false);
+ }
+
+ toggle() {}
+
+ destroy() {
+ this.emit('destroy');
+ }
+};
+
+var PopupSubMenu = class extends PopupMenuBase {
+ constructor(sourceActor, sourceArrow) {
+ super(sourceActor);
+
+ this._arrow = sourceArrow;
+
+ // Since a function of a submenu might be to provide a "More.." expander
+ // with long content, we make it scrollable - the scrollbar will only take
+ // effect if a CSS max-height is set on the top menu.
+ this.actor = new St.ScrollView({
+ style_class: 'popup-sub-menu',
+ hscrollbar_policy: St.PolicyType.NEVER,
+ vscrollbar_policy: St.PolicyType.NEVER,
+ });
+
+ this.actor.add_actor(this.box);
+ this.actor._delegate = this;
+ this.actor.clip_to_allocation = true;
+ this.actor.connect('key-press-event', this._onKeyPressEvent.bind(this));
+ this.actor.hide();
+ }
+
+ _needsScrollbar() {
+ let topMenu = this._getTopMenu();
+ let [, topNaturalHeight] = topMenu.actor.get_preferred_height(-1);
+ let topThemeNode = topMenu.actor.get_theme_node();
+
+ let topMaxHeight = topThemeNode.get_max_height();
+ return topMaxHeight >= 0 && topNaturalHeight >= topMaxHeight;
+ }
+
+ getSensitive() {
+ return this._sensitive && this.sourceActor.sensitive;
+ }
+
+ get sensitive() {
+ return this.getSensitive();
+ }
+
+ open(animate) {
+ if (this.isOpen)
+ return;
+
+ if (this.isEmpty())
+ return;
+
+ this.isOpen = true;
+ this.emit('open-state-changed', true);
+
+ this.actor.show();
+
+ let needsScrollbar = this._needsScrollbar();
+
+ // St.ScrollView always requests space horizontally for a possible vertical
+ // scrollbar if in AUTOMATIC mode. Doing better would require implementation
+ // of width-for-height in St.BoxLayout and St.ScrollView. This looks bad
+ // when we *don't* need it, so turn off the scrollbar when that's true.
+ // Dynamic changes in whether we need it aren't handled properly.
+ this.actor.vscrollbar_policy =
+ needsScrollbar ? St.PolicyType.AUTOMATIC : St.PolicyType.NEVER;
+
+ if (needsScrollbar)
+ this.actor.add_style_pseudo_class('scrolled');
+ else
+ this.actor.remove_style_pseudo_class('scrolled');
+
+ // It looks funny if we animate with a scrollbar (at what point is
+ // the scrollbar added?) so just skip that case
+ if (animate && needsScrollbar)
+ animate = false;
+
+ let targetAngle = this.actor.text_direction == Clutter.TextDirection.RTL ? -90 : 90;
+
+ if (animate) {
+ let [, naturalHeight] = this.actor.get_preferred_height(-1);
+ this.actor.height = 0;
+ this.actor.ease({
+ height: naturalHeight,
+ duration: 250,
+ mode: Clutter.AnimationMode.EASE_OUT_EXPO,
+ onComplete: () => this.actor.set_height(-1),
+ });
+ this._arrow.ease({
+ rotation_angle_z: targetAngle,
+ duration: 250,
+ mode: Clutter.AnimationMode.EASE_OUT_EXPO,
+ });
+ } else {
+ this._arrow.rotation_angle_z = targetAngle;
+ }
+ }
+
+ close(animate) {
+ if (!this.isOpen)
+ return;
+
+ this.isOpen = false;
+ this.emit('open-state-changed', false);
+
+ if (this._activeMenuItem)
+ this._activeMenuItem.active = false;
+
+ if (animate && this._needsScrollbar())
+ animate = false;
+
+ if (animate) {
+ this.actor.ease({
+ height: 0,
+ duration: 250,
+ mode: Clutter.AnimationMode.EASE_OUT_EXPO,
+ onComplete: () => {
+ this.actor.hide();
+ this.actor.set_height(-1);
+ },
+ });
+ this._arrow.ease({
+ rotation_angle_z: 0,
+ duration: 250,
+ mode: Clutter.AnimationMode.EASE_OUT_EXPO,
+ });
+ } else {
+ this._arrow.rotation_angle_z = 0;
+ this.actor.hide();
+ }
+ }
+
+ _onKeyPressEvent(actor, event) {
+ // Move focus back to parent menu if the user types Left.
+
+ if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
+ this.close(BoxPointer.PopupAnimation.FULL);
+ this.sourceActor._delegate.active = true;
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+};
+
+/**
+ * PopupMenuSection:
+ *
+ * A section of a PopupMenu which is handled like a submenu
+ * (you can add and remove items, you can destroy it, you
+ * can add it to another menu), but is completely transparent
+ * to the user
+ */
+var PopupMenuSection = class extends PopupMenuBase {
+ constructor() {
+ super();
+
+ this.actor = this.box;
+ this.actor._delegate = this;
+ this.isOpen = true;
+
+ this.actor.add_style_class_name('popup-menu-section');
+ }
+
+ // deliberately ignore any attempt to open() or close(), but emit the
+ // corresponding signal so children can still pick it up
+ open() {
+ this.emit('open-state-changed', true);
+ }
+
+ close() {
+ this.emit('open-state-changed', false);
+ }
+};
+
+var PopupSubMenuMenuItem = GObject.registerClass(
+class PopupSubMenuMenuItem extends PopupBaseMenuItem {
+ _init(text, wantIcon) {
+ super._init();
+
+ this.add_style_class_name('popup-submenu-menu-item');
+
+ if (wantIcon) {
+ this.icon = new St.Icon({ style_class: 'popup-menu-icon' });
+ this.add_child(this.icon);
+ }
+
+ this.label = new St.Label({
+ text,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this.label);
+ this.label_actor = this.label;
+
+ let expander = new St.Bin({
+ style_class: 'popup-menu-item-expander',
+ x_expand: true,
+ });
+ this.add_child(expander);
+
+ this._triangle = arrowIcon(St.Side.RIGHT);
+ this._triangle.pivot_point = new Graphene.Point({ x: 0.5, y: 0.6 });
+
+ this._triangleBin = new St.Widget({
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._triangleBin.add_child(this._triangle);
+
+ this.add_child(this._triangleBin);
+ this.add_accessible_state(Atk.StateType.EXPANDABLE);
+
+ this.menu = new PopupSubMenu(this, this._triangle);
+ this.menu.connect('open-state-changed', this._subMenuOpenStateChanged.bind(this));
+ this.connect('destroy', () => this.menu.destroy());
+ }
+
+ _setParent(parent) {
+ super._setParent(parent);
+ this.menu._setParent(parent);
+ }
+
+ syncSensitive() {
+ let sensitive = super.syncSensitive();
+ this._triangle.visible = sensitive;
+ if (!sensitive)
+ this.menu.close(false);
+ }
+
+ _subMenuOpenStateChanged(menu, open) {
+ if (open) {
+ this.add_style_pseudo_class('open');
+ this._getTopMenu()._setOpenedSubMenu(this.menu);
+ this.add_accessible_state(Atk.StateType.EXPANDED);
+ this.add_style_pseudo_class('checked');
+ } else {
+ this.remove_style_pseudo_class('open');
+ this._getTopMenu()._setOpenedSubMenu(null);
+ this.remove_accessible_state(Atk.StateType.EXPANDED);
+ this.remove_style_pseudo_class('checked');
+ }
+ }
+
+ setSubmenuShown(open) {
+ if (open)
+ this.menu.open(BoxPointer.PopupAnimation.FULL);
+ else
+ this.menu.close(BoxPointer.PopupAnimation.FULL);
+ }
+
+ _setOpenState(open) {
+ this.setSubmenuShown(open);
+ }
+
+ _getOpenState() {
+ return this.menu.isOpen;
+ }
+
+ vfunc_key_press_event(keyPressEvent) {
+ let symbol = keyPressEvent.keyval;
+
+ if (symbol == Clutter.KEY_Right) {
+ this._setOpenState(true);
+ this.menu.actor.navigate_focus(null, St.DirectionType.DOWN, false);
+ return Clutter.EVENT_STOP;
+ } else if (symbol == Clutter.KEY_Left && this._getOpenState()) {
+ this._setOpenState(false);
+ return Clutter.EVENT_STOP;
+ }
+
+ return super.vfunc_key_press_event(keyPressEvent);
+ }
+
+ activate(_event) {
+ this._setOpenState(true);
+ }
+
+ vfunc_button_release_event() {
+ // Since we override the parent, we need to manage what the parent does
+ // with the active style class
+ this.remove_style_pseudo_class('active');
+ this._setOpenState(!this._getOpenState());
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_touch_event(touchEvent) {
+ if (touchEvent.type == Clutter.EventType.TOUCH_END) {
+ // Since we override the parent, we need to manage what the parent does
+ // with the active style class
+ this.remove_style_pseudo_class('active');
+ this._setOpenState(!this._getOpenState());
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+});
+
+/* Basic implementation of a menu manager.
+ * Call addMenu to add menus
+ */
+var PopupMenuManager = class {
+ constructor(owner, grabParams) {
+ this._grabParams = Params.parse(grabParams,
+ { actionMode: Shell.ActionMode.POPUP });
+ global.stage.connect('notify::key-focus', () => {
+ if (!this.activeMenu)
+ return;
+
+ let actor = global.stage.get_key_focus();
+ let newMenu = this._findMenuForSource(actor);
+
+ if (newMenu)
+ this._changeMenu(newMenu);
+ });
+ this._menus = [];
+ }
+
+ addMenu(menu, position) {
+ if (this._menus.includes(menu))
+ return;
+
+ menu.connectObject(
+ 'open-state-changed', this._onMenuOpenState.bind(this),
+ 'destroy', () => this.removeMenu(menu), this);
+ menu.actor.connectObject('captured-event',
+ this._onCapturedEvent.bind(this), this);
+
+ if (position == undefined)
+ this._menus.push(menu);
+ else
+ this._menus.splice(position, 0, menu);
+ }
+
+ removeMenu(menu) {
+ if (menu === this.activeMenu) {
+ Main.popModal(this._grab);
+ this._grab = null;
+ }
+
+ const position = this._menus.indexOf(menu);
+ if (position == -1) // not a menu we manage
+ return;
+
+ menu.disconnectObject(this);
+ menu.actor.disconnectObject(this);
+
+ this._menus.splice(position, 1);
+ }
+
+ ignoreRelease() {
+ }
+
+ _onMenuOpenState(menu, open) {
+ if (open && this.activeMenu === menu)
+ return;
+
+ if (open) {
+ const oldMenu = this.activeMenu;
+ const oldGrab = this._grab;
+ this._grab = Main.pushModal(menu.actor, this._grabParams);
+ this.activeMenu = menu;
+ oldMenu?.close(BoxPointer.PopupAnimation.FADE);
+ if (oldGrab)
+ Main.popModal(oldGrab);
+ } else if (this.activeMenu === menu) {
+ this.activeMenu = null;
+ Main.popModal(this._grab);
+ this._grab = null;
+ }
+ }
+
+ _changeMenu(newMenu) {
+ newMenu.open(this.activeMenu
+ ? BoxPointer.PopupAnimation.FADE
+ : BoxPointer.PopupAnimation.FULL);
+ }
+
+ _onCapturedEvent(actor, event) {
+ let menu = actor._delegate;
+ const targetActor = global.stage.get_event_actor(event);
+
+ if (event.type() === Clutter.EventType.KEY_PRESS) {
+ let symbol = event.get_key_symbol();
+ if (symbol === Clutter.KEY_Down &&
+ global.stage.get_key_focus() === menu.actor) {
+ actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ return Clutter.EVENT_STOP;
+ } else if (symbol === Clutter.KEY_Escape && menu.isOpen) {
+ menu.close(BoxPointer.PopupAnimation.FULL);
+ return Clutter.EVENT_STOP;
+ }
+ } else if (event.type() === Clutter.EventType.ENTER &&
+ (event.get_flags() & Clutter.EventFlags.FLAG_GRAB_NOTIFY) === 0) {
+ let hoveredMenu = this._findMenuForSource(targetActor);
+
+ if (hoveredMenu && hoveredMenu !== menu)
+ this._changeMenu(hoveredMenu);
+ } else if ((event.type() === Clutter.EventType.BUTTON_PRESS ||
+ event.type() === Clutter.EventType.TOUCH_BEGIN) &&
+ !actor.contains(targetActor)) {
+ menu.close(BoxPointer.PopupAnimation.FULL);
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _findMenuForSource(source) {
+ while (source) {
+ let actor = source;
+ const menu = this._menus.find(m => m.sourceActor === actor);
+ if (menu)
+ return menu;
+ source = source.get_parent();
+ }
+
+ return null;
+ }
+
+ _closeMenu(isUser, menu) {
+ // If this isn't a user action, we called close()
+ // on the BoxPointer ourselves, so we shouldn't
+ // reanimate.
+ if (isUser)
+ menu.close(BoxPointer.PopupAnimation.FULL);
+ }
+};
diff --git a/js/ui/quickSettings.js b/js/ui/quickSettings.js
new file mode 100644
index 0000000..7cfd4f4
--- /dev/null
+++ b/js/ui/quickSettings.js
@@ -0,0 +1,717 @@
+/* exported QuickToggle, QuickMenuToggle, QuickSlider, QuickSettingsMenu, SystemIndicator */
+const {Atk, Clutter, Gio, GLib, GObject, Graphene, Meta, Pango, St} = imports.gi;
+
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const {Slider} = imports.ui.slider;
+
+const {PopupAnimation} = imports.ui.boxpointer;
+
+const DIM_BRIGHTNESS = -0.4;
+const POPUP_ANIMATION_TIME = 400;
+
+var QuickSettingsItem = GObject.registerClass({
+ Properties: {
+ 'has-menu': GObject.ParamSpec.boolean(
+ 'has-menu', 'has-menu', 'has-menu',
+ GObject.ParamFlags.READWRITE |
+ GObject.ParamFlags.CONSTRUCT_ONLY,
+ false),
+ },
+}, class QuickSettingsItem extends St.Button {
+ _init(params) {
+ super._init(params);
+
+ if (this.hasMenu) {
+ this.menu = new QuickToggleMenu(this);
+ this.menu.actor.hide();
+
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._menuManager.addMenu(this.menu);
+ }
+ }
+});
+
+var QuickToggle = GObject.registerClass({
+ Properties: {
+ 'label': GObject.ParamSpec.override('label', St.Button),
+ 'icon-name': GObject.ParamSpec.override('icon-name', St.Button),
+ 'gicon': GObject.ParamSpec.object('gicon', '', '',
+ GObject.ParamFlags.READWRITE,
+ Gio.Icon),
+ },
+}, class QuickToggle extends QuickSettingsItem {
+ _init(params) {
+ super._init({
+ style_class: 'quick-toggle button',
+ accessible_role: Atk.Role.TOGGLE_BUTTON,
+ can_focus: true,
+ ...params,
+ });
+
+ this._box = new St.BoxLayout();
+ this.set_child(this._box);
+
+ const iconProps = {};
+ if (this.gicon)
+ iconProps['gicon'] = this.gicon;
+ if (this.iconName)
+ iconProps['icon-name'] = this.iconName;
+
+ this._icon = new St.Icon({
+ style_class: 'quick-toggle-icon',
+ x_expand: false,
+ ...iconProps,
+ });
+ this._box.add_child(this._icon);
+
+ // bindings are in the "wrong" direction, so we
+ // pick up StIcon's linking of the two properties
+ this._icon.bind_property('icon-name',
+ this, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE |
+ GObject.BindingFlags.BIDIRECTIONAL);
+ this._icon.bind_property('gicon',
+ this, 'gicon',
+ GObject.BindingFlags.SYNC_CREATE |
+ GObject.BindingFlags.BIDIRECTIONAL);
+
+ this._label = new St.Label({
+ style_class: 'quick-toggle-label',
+ y_align: Clutter.ActorAlign.CENTER,
+ x_align: Clutter.ActorAlign.START,
+ x_expand: true,
+ });
+ this.label_actor = this._label;
+ this._box.add_child(this._label);
+
+ this._label.clutter_text.ellipsize = Pango.EllipsizeMode.END;
+
+ this.bind_property('label',
+ this._label, 'text',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+});
+
+var QuickMenuToggle = GObject.registerClass({
+ Properties: {
+ 'menu-enabled': GObject.ParamSpec.boolean(
+ 'menu-enabled', '', '',
+ GObject.ParamFlags.READWRITE,
+ true),
+ },
+}, class QuickMenuToggle extends QuickToggle {
+ _init(params) {
+ super._init({
+ ...params,
+ hasMenu: true,
+ });
+
+ this.add_style_class_name('quick-menu-toggle');
+
+ this._menuButton = new St.Button({
+ child: new St.Icon({
+ style_class: 'quick-toggle-arrow',
+ icon_name: 'go-next-symbolic',
+ }),
+ x_expand: false,
+ y_expand: true,
+ });
+ this._box.add_child(this._menuButton);
+
+ this.bind_property('menu-enabled',
+ this._menuButton, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('reactive',
+ this._menuButton, 'reactive',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._menuButton.connect('clicked', () => this.menu.open());
+ this.connect('popup-menu', () => {
+ if (this.menuEnabled)
+ this.menu.open();
+ });
+ }
+});
+
+var QuickSlider = GObject.registerClass({
+ Properties: {
+ 'icon-name': GObject.ParamSpec.override('icon-name', St.Button),
+ 'gicon': GObject.ParamSpec.object('gicon', '', '',
+ GObject.ParamFlags.READWRITE,
+ Gio.Icon),
+ 'menu-enabled': GObject.ParamSpec.boolean(
+ 'menu-enabled', '', '',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+}, class QuickSlider extends QuickSettingsItem {
+ _init(params) {
+ super._init({
+ style_class: 'quick-slider',
+ ...params,
+ can_focus: false,
+ reactive: false,
+ hasMenu: true,
+ });
+
+ const box = new St.BoxLayout();
+ this.set_child(box);
+
+ const iconProps = {};
+ if (this.gicon)
+ iconProps['gicon'] = this.gicon;
+ if (this.iconName)
+ iconProps['icon-name'] = this.iconName;
+
+ this._icon = new St.Icon({
+ style_class: 'quick-toggle-icon',
+ ...iconProps,
+ });
+ box.add_child(this._icon);
+
+ // bindings are in the "wrong" direction, so we
+ // pick up StIcon's linking of the two properties
+ this._icon.bind_property('icon-name',
+ this, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE |
+ GObject.BindingFlags.BIDIRECTIONAL);
+ this._icon.bind_property('gicon',
+ this, 'gicon',
+ GObject.BindingFlags.SYNC_CREATE |
+ GObject.BindingFlags.BIDIRECTIONAL);
+
+ this.slider = new Slider(0);
+
+ // for focus indication
+ const sliderBin = new St.Bin({
+ style_class: 'slider-bin',
+ child: this.slider,
+ reactive: true,
+ can_focus: true,
+ x_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add_child(sliderBin);
+
+ // Make the slider bin transparent for a11y
+ const sliderAccessible = this.slider.get_accessible();
+ sliderAccessible.set_parent(sliderBin.get_parent().get_accessible());
+ sliderBin.set_accessible(sliderAccessible);
+ sliderBin.connect('event', (bin, event) => this.slider.event(event, false));
+
+ this._menuButton = new St.Button({
+ child: new St.Icon({icon_name: 'go-next-symbolic'}),
+ style_class: 'icon-button flat',
+ can_focus: true,
+ x_expand: false,
+ y_expand: true,
+ });
+ box.add_child(this._menuButton);
+
+ this.bind_property('menu-enabled',
+ this._menuButton, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._menuButton.connect('clicked', () => this.menu.open());
+ this.slider.connect('popup-menu', () => {
+ if (this.menuEnabled)
+ this.menu.open();
+ });
+ }
+});
+
+class QuickToggleMenu extends PopupMenu.PopupMenuBase {
+ constructor(sourceActor) {
+ super(sourceActor, 'quick-toggle-menu');
+
+ const constraints = new Clutter.BindConstraint({
+ coordinate: Clutter.BindCoordinate.Y,
+ source: sourceActor,
+ });
+ sourceActor.bind_property('height',
+ constraints, 'offset',
+ GObject.BindingFlags.DEFAULT);
+
+ this.actor = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ style_class: 'quick-toggle-menu-container',
+ reactive: true,
+ x_expand: true,
+ y_expand: false,
+ constraints,
+ });
+ this.actor._delegate = this;
+ this.actor.add_child(this.box);
+
+ global.focus_manager.add_group(this.actor);
+
+ const headerLayout = new Clutter.GridLayout();
+ this._header = new St.Widget({
+ style_class: 'header',
+ layout_manager: headerLayout,
+ visible: false,
+ });
+ headerLayout.hookup_style(this._header);
+ this.box.add_child(this._header);
+
+ this._headerIcon = new St.Icon({
+ style_class: 'icon',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._headerTitle = new St.Label({
+ style_class: 'title',
+ y_align: Clutter.ActorAlign.CENTER,
+ y_expand: true,
+ });
+ this._headerSubtitle = new St.Label({
+ style_class: 'subtitle',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._headerSpacer = new Clutter.Actor({x_expand: true});
+
+ const side = this.actor.text_direction === Clutter.TextDirection.RTL
+ ? Clutter.GridPosition.LEFT
+ : Clutter.GridPosition.RIGHT;
+
+ headerLayout.attach(this._headerIcon, 0, 0, 1, 2);
+ headerLayout.attach_next_to(this._headerTitle,
+ this._headerIcon, side, 1, 1);
+ headerLayout.attach_next_to(this._headerSpacer,
+ this._headerTitle, side, 1, 1);
+ headerLayout.attach_next_to(this._headerSubtitle,
+ this._headerTitle, Clutter.GridPosition.BOTTOM, 1, 1);
+
+ sourceActor.connect('notify::checked',
+ () => this._syncChecked());
+ this._syncChecked();
+ }
+
+ setHeader(icon, title, subtitle = '') {
+ if (icon instanceof Gio.Icon)
+ this._headerIcon.gicon = icon;
+ else
+ this._headerIcon.icon_name = icon;
+
+ this._headerTitle.text = title;
+ this._headerSubtitle.set({
+ text: subtitle,
+ visible: !!subtitle,
+ });
+
+ this._header.show();
+ }
+
+ addHeaderSuffix(actor) {
+ const {layoutManager: headerLayout} = this._header;
+ const side = this.actor.text_direction === Clutter.TextDirection.RTL
+ ? Clutter.GridPosition.LEFT
+ : Clutter.GridPosition.RIGHT;
+ this._header.remove_child(this._headerSpacer);
+ headerLayout.attach_next_to(actor, this._headerTitle, side, 1, 1);
+ headerLayout.attach_next_to(this._headerSpacer, actor, side, 1, 1);
+ }
+
+ open(animate) {
+ if (this.isOpen)
+ return;
+
+ this.actor.show();
+ this.isOpen = true;
+
+ this.actor.height = -1;
+ const [targetHeight] = this.actor.get_preferred_height(-1);
+
+ const duration = animate !== PopupAnimation.NONE
+ ? POPUP_ANIMATION_TIME / 2
+ : 0;
+
+ this.actor.height = 0;
+ this.box.opacity = 0;
+ this.actor.ease({
+ duration,
+ height: targetHeight,
+ onComplete: () => {
+ this.box.ease({
+ duration,
+ opacity: 255,
+ });
+ this.actor.height = -1;
+ },
+ });
+ this.emit('open-state-changed', true);
+ }
+
+ close(animate) {
+ if (!this.isOpen)
+ return;
+
+ const duration = animate !== PopupAnimation.NONE
+ ? POPUP_ANIMATION_TIME / 2
+ : 0;
+
+ this.box.ease({
+ duration,
+ opacity: 0,
+ onComplete: () => {
+ this.actor.ease({
+ duration,
+ height: 0,
+ onComplete: () => {
+ this.actor.hide();
+ this.emit('menu-closed');
+ },
+ });
+ },
+ });
+
+ this.isOpen = false;
+ this.emit('open-state-changed', false);
+ }
+
+ _syncChecked() {
+ if (this.sourceActor.checked)
+ this._headerIcon.add_style_class_name('active');
+ else
+ this._headerIcon.remove_style_class_name('active');
+ }
+
+ // expected on toplevel menus
+ _setOpenedSubMenu(submenu) {
+ this._openedSubMenu?.close(true);
+ this._openedSubMenu = submenu;
+ }
+}
+
+const QuickSettingsLayoutMeta = GObject.registerClass({
+ Properties: {
+ 'column-span': GObject.ParamSpec.int(
+ 'column-span', '', '',
+ GObject.ParamFlags.READWRITE,
+ 1, GLib.MAXINT32, 1),
+ },
+}, class QuickSettingsLayoutMeta extends Clutter.LayoutMeta {});
+
+const QuickSettingsLayout = GObject.registerClass({
+ Properties: {
+ 'row-spacing': GObject.ParamSpec.int(
+ 'row-spacing', 'row-spacing', 'row-spacing',
+ GObject.ParamFlags.READWRITE,
+ 0, GLib.MAXINT32, 0),
+ 'column-spacing': GObject.ParamSpec.int(
+ 'column-spacing', 'column-spacing', 'column-spacing',
+ GObject.ParamFlags.READWRITE,
+ 0, GLib.MAXINT32, 0),
+ 'n-columns': GObject.ParamSpec.int(
+ 'n-columns', 'n-columns', 'n-columns',
+ GObject.ParamFlags.READWRITE,
+ 1, GLib.MAXINT32, 1),
+ },
+}, class QuickSettingsLayout extends Clutter.LayoutManager {
+ _init(overlay, params) {
+ super._init(params);
+
+ this._overlay = overlay;
+ }
+
+ _containerStyleChanged() {
+ const node = this._container.get_theme_node();
+
+ let changed = false;
+ let found, length;
+ [found, length] = node.lookup_length('spacing-rows', false);
+ changed ||= found;
+ if (found)
+ this.rowSpacing = length;
+
+ [found, length] = node.lookup_length('spacing-columns', false);
+ changed ||= found;
+ if (found)
+ this.columnSpacing = length;
+
+ if (changed)
+ this.layout_changed();
+ }
+
+ _getColSpan(container, child) {
+ const {columnSpan} = this.get_child_meta(container, child);
+ return Math.clamp(columnSpan, 1, this.nColumns);
+ }
+
+ _getMaxChildWidth(container) {
+ let [minWidth, natWidth] = [0, 0];
+
+ for (const child of container) {
+ if (child === this._overlay)
+ continue;
+
+ const [childMin, childNat] = child.get_preferred_width(-1);
+ const colSpan = this._getColSpan(container, child);
+ minWidth = Math.max(minWidth, childMin / colSpan);
+ natWidth = Math.max(natWidth, childNat / colSpan);
+ }
+
+ return [minWidth, natWidth];
+ }
+
+ _getRows(container) {
+ const rows = [];
+ let lineIndex = 0;
+ let curRow;
+
+ /** private */
+ function appendRow() {
+ curRow = [];
+ rows.push(curRow);
+ lineIndex = 0;
+ }
+
+ for (const child of container) {
+ if (!child.visible)
+ continue;
+
+ if (child === this._overlay)
+ continue;
+
+ if (lineIndex === 0)
+ appendRow();
+
+ const colSpan = this._getColSpan(container, child);
+ const fitsRow = lineIndex + colSpan <= this.nColumns;
+
+ if (!fitsRow)
+ appendRow();
+
+ curRow.push(child);
+ lineIndex = (lineIndex + colSpan) % this.nColumns;
+ }
+
+ return rows;
+ }
+
+ _getRowHeight(children) {
+ let [minHeight, natHeight] = [0, 0];
+
+ children.forEach(child => {
+ const [childMin, childNat] = child.get_preferred_height(-1);
+ minHeight = Math.max(minHeight, childMin);
+ natHeight = Math.max(natHeight, childNat);
+ });
+
+ return [minHeight, natHeight];
+ }
+
+ vfunc_get_child_meta_type() {
+ return QuickSettingsLayoutMeta.$gtype;
+ }
+
+ vfunc_set_container(container) {
+ this._container?.disconnectObject(this);
+
+ this._container = container;
+
+ this._container?.connectObject('style-changed',
+ () => this._containerStyleChanged(), this);
+ }
+
+ vfunc_get_preferred_width(container, _forHeight) {
+ const [childMin, childNat] = this._getMaxChildWidth(container);
+ const spacing = (this.nColumns - 1) * this.column_spacing;
+ return [this.nColumns * childMin + spacing, this.nColumns * childNat + spacing];
+ }
+
+ vfunc_get_preferred_height(container, _forWidth) {
+ const rows = this._getRows(container);
+
+ let [minHeight, natHeight] = this._overlay
+ ? this._overlay.get_preferred_height(-1)
+ : [0, 0];
+
+ const spacing = (rows.length - 1) * this.row_spacing;
+ minHeight += spacing;
+ natHeight += spacing;
+
+ rows.forEach(row => {
+ const [rowMin, rowNat] = this._getRowHeight(row);
+ minHeight += rowMin;
+ natHeight += rowNat;
+ });
+
+ return [minHeight, natHeight];
+ }
+
+ vfunc_allocate(container, box) {
+ const rows = this._getRows(container);
+
+ const [, overlayHeight] = this._overlay
+ ? this._overlay.get_preferred_height(-1)
+ : [0, 0];
+
+ const availWidth = box.get_width() - (this.nColumns - 1) * this.column_spacing;
+ const childWidth = Math.floor(availWidth / this.nColumns);
+
+ this._overlay?.allocate_available_size(0, 0, box.get_width(), box.get_height());
+
+ const isRtl = container.text_direction === Clutter.TextDirection.RTL;
+
+ const childBox = new Clutter.ActorBox();
+ let y = box.y1;
+ rows.forEach(row => {
+ const [, rowNat] = this._getRowHeight(row);
+
+ let lineIndex = 0;
+ row.forEach(child => {
+ const colSpan = this._getColSpan(container, child);
+ const width =
+ childWidth * colSpan + this.column_spacing * (colSpan - 1);
+ let x = box.x1 + lineIndex * (childWidth + this.column_spacing);
+ if (isRtl)
+ x = box.x2 - width - x;
+
+ childBox.set_origin(x, y);
+ childBox.set_size(width, rowNat);
+ child.allocate(childBox);
+
+ lineIndex = (lineIndex + colSpan) % this.nColumns;
+ });
+
+ y += rowNat + this.row_spacing;
+
+ if (row.some(c => c.menu?.actor.visible))
+ y += overlayHeight;
+ });
+ }
+});
+
+var QuickSettingsMenu = class extends PopupMenu.PopupMenu {
+ constructor(sourceActor, nColumns = 1) {
+ super(sourceActor, 0, St.Side.TOP);
+
+ this.actor = new St.Widget({reactive: true, width: 0, height: 0});
+ this.actor.add_child(this._boxPointer);
+ this.actor._delegate = this;
+
+ this.connect('menu-closed', () => this.actor.hide());
+
+ Main.layoutManager.connectObject('system-modal-opened',
+ () => this.close(), this);
+
+ this._dimEffect = new Clutter.BrightnessContrastEffect({
+ enabled: false,
+ });
+ this._boxPointer.add_effect_with_name('dim', this._dimEffect);
+ this.box.add_style_class_name('quick-settings');
+
+ // Overlay layer for menus
+ this._overlay = new Clutter.Actor({
+ layout_manager: new Clutter.BinLayout(),
+ });
+
+ // "clone"
+ const placeholder = new Clutter.Actor({
+ constraints: new Clutter.BindConstraint({
+ coordinate: Clutter.BindCoordinate.HEIGHT,
+ source: this._overlay,
+ }),
+ });
+
+ this._grid = new St.Widget({
+ style_class: 'quick-settings-grid',
+ layout_manager: new QuickSettingsLayout(placeholder, {
+ nColumns,
+ }),
+ });
+ this.box.add_child(this._grid);
+ this._grid.add_child(placeholder);
+
+ const yConstraint = new Clutter.BindConstraint({
+ coordinate: Clutter.BindCoordinate.Y,
+ source: this._boxPointer,
+ });
+
+ // Pick up additional spacing from any intermediate actors
+ const updateOffset = () => {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ const offset = this._grid.apply_relative_transform_to_point(
+ this._boxPointer, new Graphene.Point3D());
+ yConstraint.offset = offset.y;
+ return GLib.SOURCE_REMOVE;
+ });
+ };
+ this._grid.connect('notify::y', updateOffset);
+ this.box.connect('notify::y', updateOffset);
+ this._boxPointer.bin.connect('notify::y', updateOffset);
+
+ this._overlay.add_constraint(yConstraint);
+ this._overlay.add_constraint(new Clutter.BindConstraint({
+ coordinate: Clutter.BindCoordinate.X,
+ source: this._boxPointer,
+ }));
+ this._overlay.add_constraint(new Clutter.BindConstraint({
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ source: this._boxPointer,
+ }));
+
+ this.actor.add_child(this._overlay);
+ }
+
+ addItem(item, colSpan = 1) {
+ this._grid.add_child(item);
+ this._grid.layout_manager.child_set_property(
+ this._grid, item, 'column-span', colSpan);
+
+ if (item.menu) {
+ this._overlay.add_child(item.menu.actor);
+
+ item.menu.connect('open-state-changed', (m, isOpen) => {
+ this._setDimmed(isOpen);
+ this._activeMenu = isOpen ? item.menu : null;
+ });
+ }
+ }
+
+ open(animate) {
+ this.actor.show();
+ super.open(animate);
+ }
+
+ close(animate) {
+ this._activeMenu?.close(animate);
+ super.close(animate);
+ }
+
+ _setDimmed(dim) {
+ const val = 127 * (1 + (dim ? 1 : 0) * DIM_BRIGHTNESS);
+ const color = Clutter.Color.new(val, val, val, 255);
+
+ this._boxPointer.ease_property('@effects.dim.brightness', color, {
+ mode: Clutter.AnimationMode.LINEAR,
+ duration: POPUP_ANIMATION_TIME,
+ onStopped: () => (this._dimEffect.enabled = dim),
+ });
+ this._dimEffect.enabled = true;
+ }
+};
+
+var SystemIndicator = GObject.registerClass(
+class SystemIndicator extends St.BoxLayout {
+ _init() {
+ super._init({
+ style_class: 'panel-status-indicators-box',
+ reactive: true,
+ visible: false,
+ });
+
+ this.quickSettingsItems = [];
+ }
+
+ _syncIndicatorsVisible() {
+ this.visible = this.get_children().some(a => a.visible);
+ }
+
+ _addIndicator() {
+ const icon = new St.Icon({style_class: 'system-status-icon'});
+ this.add_actor(icon);
+ icon.connect('notify::visible', () => this._syncIndicatorsVisible());
+ this._syncIndicatorsVisible();
+ return icon;
+ }
+});
diff --git a/js/ui/remoteSearch.js b/js/ui/remoteSearch.js
new file mode 100644
index 0000000..87ee384
--- /dev/null
+++ b/js/ui/remoteSearch.js
@@ -0,0 +1,332 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported loadRemoteSearchProviders */
+
+const { GdkPixbuf, Gio, GLib, Shell, St } = imports.gi;
+
+const FileUtils = imports.misc.fileUtils;
+
+const KEY_FILE_GROUP = 'Shell Search Provider';
+
+const SearchProviderIface = `
+<node>
+<interface name="org.gnome.Shell.SearchProvider">
+<method name="GetInitialResultSet">
+ <arg type="as" direction="in" />
+ <arg type="as" direction="out" />
+</method>
+<method name="GetSubsearchResultSet">
+ <arg type="as" direction="in" />
+ <arg type="as" direction="in" />
+ <arg type="as" direction="out" />
+</method>
+<method name="GetResultMetas">
+ <arg type="as" direction="in" />
+ <arg type="aa{sv}" direction="out" />
+</method>
+<method name="ActivateResult">
+ <arg type="s" direction="in" />
+</method>
+</interface>
+</node>`;
+
+const SearchProvider2Iface = `
+<node>
+<interface name="org.gnome.Shell.SearchProvider2">
+<method name="GetInitialResultSet">
+ <arg type="as" direction="in" />
+ <arg type="as" direction="out" />
+</method>
+<method name="GetSubsearchResultSet">
+ <arg type="as" direction="in" />
+ <arg type="as" direction="in" />
+ <arg type="as" direction="out" />
+</method>
+<method name="GetResultMetas">
+ <arg type="as" direction="in" />
+ <arg type="aa{sv}" direction="out" />
+</method>
+<method name="ActivateResult">
+ <arg type="s" direction="in" />
+ <arg type="as" direction="in" />
+ <arg type="u" direction="in" />
+</method>
+<method name="LaunchSearch">
+ <arg type="as" direction="in" />
+ <arg type="u" direction="in" />
+</method>
+</interface>
+</node>`;
+
+var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
+var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
+
+/**
+ * loadRemoteSearchProviders:
+ *
+ * @param {Gio.Settings} searchSettings - search settings
+ * @returns {RemoteSearchProvider[]} - the list of remote providers
+ */
+function loadRemoteSearchProviders(searchSettings) {
+ let objectPaths = {};
+ let loadedProviders = [];
+
+ function loadRemoteSearchProvider(file) {
+ let keyfile = new GLib.KeyFile();
+ let path = file.get_path();
+
+ try {
+ keyfile.load_from_file(path, 0);
+ } catch (e) {
+ return;
+ }
+
+ if (!keyfile.has_group(KEY_FILE_GROUP))
+ return;
+
+ let remoteProvider;
+ try {
+ let group = KEY_FILE_GROUP;
+ let busName = keyfile.get_string(group, 'BusName');
+ let objectPath = keyfile.get_string(group, 'ObjectPath');
+
+ if (objectPaths[objectPath])
+ return;
+
+ let appInfo = null;
+ try {
+ let desktopId = keyfile.get_string(group, 'DesktopId');
+ appInfo = Gio.DesktopAppInfo.new(desktopId);
+ if (!appInfo.should_show())
+ return;
+ } catch (e) {
+ log(`Ignoring search provider ${path}: missing DesktopId`);
+ return;
+ }
+
+ let autoStart = true;
+ try {
+ autoStart = keyfile.get_boolean(group, 'AutoStart');
+ } catch (e) {
+ // ignore error
+ }
+
+ let version = '1';
+ try {
+ version = keyfile.get_string(group, 'Version');
+ } catch (e) {
+ // ignore error
+ }
+
+ if (version >= 2)
+ remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath, autoStart);
+ else
+ remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath, autoStart);
+
+ remoteProvider.defaultEnabled = true;
+ try {
+ remoteProvider.defaultEnabled = !keyfile.get_boolean(group, 'DefaultDisabled');
+ } catch (e) {
+ // ignore error
+ }
+
+ objectPaths[objectPath] = remoteProvider;
+ loadedProviders.push(remoteProvider);
+ } catch (e) {
+ log(`Failed to add search provider ${path}: ${e}`);
+ }
+ }
+
+ if (searchSettings.get_boolean('disable-external'))
+ return [];
+
+ FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
+
+ let sortOrder = searchSettings.get_strv('sort-order');
+
+ // Special case gnome-control-center to be always active and always first
+ sortOrder.unshift('org.gnome.Settings.desktop');
+
+ const disabled = searchSettings.get_strv('disabled');
+ const enabled = searchSettings.get_strv('enabled');
+
+ loadedProviders = loadedProviders.filter(provider => {
+ let appId = provider.appInfo.get_id();
+
+ if (provider.defaultEnabled)
+ return !disabled.includes(appId);
+ else
+ return enabled.includes(appId);
+ });
+
+ loadedProviders.sort((providerA, providerB) => {
+ let idxA, idxB;
+ let appIdA, appIdB;
+
+ appIdA = providerA.appInfo.get_id();
+ appIdB = providerB.appInfo.get_id();
+
+ idxA = sortOrder.indexOf(appIdA);
+ idxB = sortOrder.indexOf(appIdB);
+
+ // if no provider is found in the order, use alphabetical order
+ if ((idxA == -1) && (idxB == -1)) {
+ let nameA = providerA.appInfo.get_name();
+ let nameB = providerB.appInfo.get_name();
+
+ return GLib.utf8_collate(nameA, nameB);
+ }
+
+ // if providerA isn't found, it's sorted after providerB
+ if (idxA == -1)
+ return 1;
+
+ // if providerB isn't found, it's sorted after providerA
+ if (idxB == -1)
+ return -1;
+
+ // finally, if both providers are found, return their order in the list
+ return idxA - idxB;
+ });
+
+ return loadedProviders;
+}
+
+var RemoteSearchProvider = class {
+ constructor(appInfo, dbusName, dbusPath, autoStart, proxyInfo) {
+ if (!proxyInfo)
+ proxyInfo = SearchProviderProxyInfo;
+
+ let gFlags = Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES;
+ if (autoStart)
+ gFlags |= Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION;
+ else
+ gFlags |= Gio.DBusProxyFlags.DO_NOT_AUTO_START;
+
+ this.proxy = new Gio.DBusProxy({
+ g_bus_type: Gio.BusType.SESSION,
+ g_name: dbusName,
+ g_object_path: dbusPath,
+ g_interface_info: proxyInfo,
+ g_interface_name: proxyInfo.name,
+ gFlags,
+ });
+ this.proxy.init_async(GLib.PRIORITY_DEFAULT, null);
+
+ this.appInfo = appInfo;
+ this.id = appInfo.get_id();
+ this.isRemoteProvider = true;
+ this.canLaunchSearch = false;
+ }
+
+ createIcon(size, meta) {
+ let gicon = null;
+ let icon = null;
+
+ if (meta['icon']) {
+ gicon = Gio.icon_deserialize(meta['icon']);
+ } else if (meta['gicon']) {
+ gicon = Gio.icon_new_for_string(meta['gicon']);
+ } else if (meta['icon-data']) {
+ const [
+ width, height, rowStride, hasAlpha,
+ bitsPerSample, nChannels_, data,
+ ] = meta['icon-data'];
+ gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
+ bitsPerSample, width, height, rowStride);
+ }
+
+ if (gicon)
+ icon = new St.Icon({ gicon, icon_size: size });
+ return icon;
+ }
+
+ filterResults(results, maxNumber) {
+ if (results.length <= maxNumber)
+ return results;
+
+ let regularResults = results.filter(r => !r.startsWith('special:'));
+ let specialResults = results.filter(r => r.startsWith('special:'));
+
+ return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
+ }
+
+ async getInitialResultSet(terms, cancellable) {
+ try {
+ const [results] = await this.proxy.GetInitialResultSetAsync(terms, cancellable);
+ return results;
+ } catch (error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ log(`Received error from D-Bus search provider ${this.id}: ${error}`);
+ return [];
+ }
+ }
+
+ async getSubsearchResultSet(previousResults, newTerms, cancellable) {
+ try {
+ const [results] = await this.proxy.GetSubsearchResultSetAsync(previousResults, newTerms, cancellable);
+ return results;
+ } catch (error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ log(`Received error from D-Bus search provider ${this.id}: ${error}`);
+ return [];
+ }
+ }
+
+ async getResultMetas(ids, cancellable) {
+ let metas;
+ try {
+ [metas] = await this.proxy.GetResultMetasAsync(ids, cancellable);
+ } catch (error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ log(`Received error from D-Bus search provider ${this.id} during GetResultMetas: ${error}`);
+ return [];
+ }
+
+ let resultMetas = [];
+ for (let i = 0; i < metas.length; i++) {
+ for (let prop in metas[i]) {
+ // we can use the serialized icon variant directly
+ if (prop !== 'icon')
+ metas[i][prop] = metas[i][prop].deepUnpack();
+ }
+
+ resultMetas.push({
+ id: metas[i]['id'],
+ name: metas[i]['name'],
+ description: metas[i]['description'],
+ createIcon: size => this.createIcon(size, metas[i]),
+ clipboardText: metas[i]['clipboardText'],
+ });
+ }
+ return resultMetas;
+ }
+
+ activateResult(id) {
+ this.proxy.ActivateResultAsync(id).catch(logError);
+ }
+
+ launchSearch(_terms) {
+ // the provider is not compatible with the new version of the interface, launch
+ // the app itself but warn so we can catch the error in logs
+ log(`Search provider ${this.appInfo.get_id()} does not implement LaunchSearch`);
+ this.appInfo.launch([], global.create_app_launch_context(0, -1));
+ }
+};
+
+var RemoteSearchProvider2 = class extends RemoteSearchProvider {
+ constructor(appInfo, dbusName, dbusPath, autoStart) {
+ super(appInfo, dbusName, dbusPath, autoStart, SearchProvider2ProxyInfo);
+
+ this.canLaunchSearch = true;
+ }
+
+ activateResult(id, terms) {
+ this.proxy.ActivateResultAsync(
+ id, terms, global.get_current_time()).catch(logError);
+ }
+
+ launchSearch(terms) {
+ this.proxy.LaunchSearchAsync(
+ terms, global.get_current_time()).catch(logError);
+ }
+};
diff --git a/js/ui/ripples.js b/js/ui/ripples.js
new file mode 100644
index 0000000..20ca9ed
--- /dev/null
+++ b/js/ui/ripples.js
@@ -0,0 +1,110 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Ripples */
+
+const { Clutter, St } = imports.gi;
+
+// Shamelessly copied from the layout "hotcorner" ripples implementation
+var Ripples = class Ripples {
+ constructor(px, py, styleClass) {
+ this._x = 0;
+ this._y = 0;
+
+ this._px = px;
+ this._py = py;
+
+ this._ripple1 = new St.BoxLayout({
+ style_class: styleClass,
+ opacity: 0,
+ can_focus: false,
+ reactive: false,
+ visible: false,
+ });
+ this._ripple1.set_pivot_point(px, py);
+
+ this._ripple2 = new St.BoxLayout({
+ style_class: styleClass,
+ opacity: 0,
+ can_focus: false,
+ reactive: false,
+ visible: false,
+ });
+ this._ripple2.set_pivot_point(px, py);
+
+ this._ripple3 = new St.BoxLayout({
+ style_class: styleClass,
+ opacity: 0,
+ can_focus: false,
+ reactive: false,
+ visible: false,
+ });
+ this._ripple3.set_pivot_point(px, py);
+ }
+
+ destroy() {
+ this._ripple1.destroy();
+ this._ripple2.destroy();
+ this._ripple3.destroy();
+ }
+
+ _animRipple(ripple, delay, duration, startScale, startOpacity, finalScale) {
+ // We draw a ripple by using a source image and animating it scaling
+ // outwards and fading away. We want the ripples to move linearly
+ // or it looks unrealistic, but if the opacity of the ripple goes
+ // linearly to zero it fades away too quickly, so we use a separate
+ // tween to give a non-linear curve to the fade-away and make
+ // it more visible in the middle section.
+
+ ripple.x = this._x;
+ ripple.y = this._y;
+ ripple.visible = true;
+ ripple.opacity = 255 * Math.sqrt(startOpacity);
+ ripple.scale_x = ripple.scale_y = startScale;
+ ripple.set_translation(-this._px * ripple.width, -this._py * ripple.height, 0.0);
+
+ ripple.ease({
+ opacity: 0,
+ delay,
+ duration,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+ ripple.ease({
+ scale_x: finalScale,
+ scale_y: finalScale,
+ delay,
+ duration,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: () => (ripple.visible = false),
+ });
+ }
+
+ addTo(stage) {
+ if (this._stage !== undefined)
+ throw new Error('Ripples already added');
+
+ this._stage = stage;
+ this._stage.add_actor(this._ripple1);
+ this._stage.add_actor(this._ripple2);
+ this._stage.add_actor(this._ripple3);
+ }
+
+ playAnimation(x, y) {
+ if (this._stage === undefined)
+ throw new Error('Ripples not added');
+
+ this._x = x;
+ this._y = y;
+
+ this._stage.set_child_above_sibling(this._ripple1, null);
+ this._stage.set_child_above_sibling(this._ripple2, this._ripple1);
+ this._stage.set_child_above_sibling(this._ripple3, this._ripple2);
+
+ // Show three concentric ripples expanding outwards; the exact
+ // parameters were found by trial and error, so don't look
+ // for them to make perfect sense mathematically
+
+ // delay time scale opacity => scale
+ this._animRipple(this._ripple1, 0, 830, 0.25, 1.0, 1.5);
+ this._animRipple(this._ripple2, 50, 1000, 0.0, 0.7, 1.25);
+ this._animRipple(this._ripple3, 350, 1000, 0.0, 0.3, 1);
+ }
+};
diff --git a/js/ui/runDialog.js b/js/ui/runDialog.js
new file mode 100644
index 0000000..fe9b33e
--- /dev/null
+++ b/js/ui/runDialog.js
@@ -0,0 +1,256 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported RunDialog */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+const Util = imports.misc.util;
+const History = imports.misc.history;
+
+const HISTORY_KEY = 'command-history';
+
+const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
+const DISABLE_COMMAND_LINE_KEY = 'disable-command-line';
+
+const TERMINAL_SCHEMA = 'org.gnome.desktop.default-applications.terminal';
+const EXEC_KEY = 'exec';
+const EXEC_ARG_KEY = 'exec-arg';
+
+var RunDialog = GObject.registerClass(
+class RunDialog extends ModalDialog.ModalDialog {
+ _init() {
+ super._init({
+ styleClass: 'run-dialog',
+ destroyOnClose: false,
+ });
+
+ this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA });
+ this._terminalSettings = new Gio.Settings({ schema_id: TERMINAL_SCHEMA });
+ global.settings.connect('changed::development-tools', () => {
+ this._enableInternalCommands = global.settings.get_boolean('development-tools');
+ });
+ this._enableInternalCommands = global.settings.get_boolean('development-tools');
+
+ this._internalCommands = {
+ 'lg': () => Main.createLookingGlass().open(),
+
+ 'r': this._restart.bind(this),
+
+ // Developer brain backwards compatibility
+ 'restart': this._restart.bind(this),
+
+ 'debugexit': () => global.context.terminate(),
+
+ // rt is short for "reload theme"
+ 'rt': () => {
+ Main.reloadThemeResource();
+ Main.loadTheme();
+ },
+
+ 'check_cloexec_fds': () => {
+ Shell.util_check_cloexec_fds();
+ },
+ };
+
+ let title = _('Run a Command');
+
+ let content = new Dialog.MessageDialogContent({ title });
+ this.contentLayout.add_actor(content);
+
+ let entry = new St.Entry({
+ style_class: 'run-dialog-entry',
+ can_focus: true,
+ });
+ ShellEntry.addContextMenu(entry);
+
+ this._entryText = entry.clutter_text;
+ content.add_child(entry);
+ this.setInitialKeyFocus(this._entryText);
+
+ let defaultDescriptionText = _('Press ESC to close');
+
+ this._descriptionLabel = new St.Label({
+ style_class: 'run-dialog-description',
+ text: defaultDescriptionText,
+ });
+ content.add_child(this._descriptionLabel);
+
+ this._commandError = false;
+
+ this._pathCompleter = new Gio.FilenameCompleter();
+
+ this._history = new History.HistoryManager({
+ gsettingsKey: HISTORY_KEY,
+ entry: this._entryText,
+ });
+ this._entryText.connect('activate', o => {
+ this.popModal();
+ this._run(o.get_text(),
+ Clutter.get_current_event().get_state() & Clutter.ModifierType.CONTROL_MASK);
+ if (!this._commandError ||
+ !this.pushModal())
+ this.close();
+ });
+ this._entryText.connect('key-press-event', (o, e) => {
+ let symbol = e.get_key_symbol();
+ if (symbol === Clutter.KEY_Tab) {
+ let text = o.get_text();
+ let prefix;
+ if (text.lastIndexOf(' ') == -1)
+ prefix = text;
+ else
+ prefix = text.substr(text.lastIndexOf(' ') + 1);
+ let postfix = this._getCompletion(prefix);
+ if (postfix != null && postfix.length > 0) {
+ o.insert_text(postfix, -1);
+ o.set_cursor_position(text.length + postfix.length);
+ }
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ });
+ this._entryText.connect('text-changed', () => {
+ this._descriptionLabel.set_text(defaultDescriptionText);
+ });
+ }
+
+ vfunc_key_release_event(event) {
+ if (event.keyval === Clutter.KEY_Escape) {
+ this.close();
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _getCommandCompletion(text) {
+ function _getCommon(s1, s2) {
+ if (s1 == null)
+ return s2;
+
+ let k = 0;
+ for (; k < s1.length && k < s2.length; k++) {
+ if (s1[k] != s2[k])
+ break;
+ }
+ if (k == 0)
+ return '';
+ return s1.substr(0, k);
+ }
+
+ let paths = GLib.getenv('PATH').split(':');
+ paths.push(GLib.get_home_dir());
+ let someResults = paths.map(path => {
+ let results = [];
+ try {
+ let file = Gio.File.new_for_path(path);
+ let fileEnum = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
+ let info;
+ while ((info = fileEnum.next_file(null))) {
+ let name = info.get_name();
+ if (name.slice(0, text.length) == text)
+ results.push(name);
+ }
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) &&
+ !e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_DIRECTORY))
+ log(e);
+ }
+ return results;
+ });
+ let results = someResults.reduce((a, b) => a.concat(b), []);
+
+ if (!results.length)
+ return null;
+
+ let common = results.reduce(_getCommon, null);
+ return common.substr(text.length);
+ }
+
+ _getCompletion(text) {
+ if (text.includes('/'))
+ return this._pathCompleter.get_completion_suffix(text);
+ else
+ return this._getCommandCompletion(text);
+ }
+
+ _run(input, inTerminal) {
+ input = this._history.addItem(input); // trims input
+ let command = input;
+
+ this._commandError = false;
+ let f;
+ if (this._enableInternalCommands)
+ f = this._internalCommands[input];
+ else
+ f = null;
+ if (f) {
+ f();
+ } else {
+ try {
+ if (inTerminal) {
+ let exec = this._terminalSettings.get_string(EXEC_KEY);
+ let execArg = this._terminalSettings.get_string(EXEC_ARG_KEY);
+ command = `${exec} ${execArg} ${input}`;
+ }
+ Util.trySpawnCommandLine(command);
+ } catch (e) {
+ // Mmmh, that failed - see if @input matches an existing file
+ let path = null;
+ if (input.charAt(0) == '/') {
+ path = input;
+ } else if (input) {
+ if (input.charAt(0) == '~')
+ input = input.slice(1);
+ path = `${GLib.get_home_dir()}/${input}`;
+ }
+
+ if (path && GLib.file_test(path, GLib.FileTest.EXISTS)) {
+ let file = Gio.file_new_for_path(path);
+ try {
+ Gio.app_info_launch_default_for_uri(file.get_uri(),
+ global.create_app_launch_context(0, -1));
+ } catch (err) {
+ // The exception from gjs contains an error string like:
+ // Error invoking Gio.app_info_launch_default_for_uri: No application
+ // is registered as handling this file
+ // We are only interested in the part after the first colon.
+ let message = err.message.replace(/[^:]*: *(.+)/, '$1');
+ this._showError(message);
+ }
+ } else {
+ this._showError(e.message);
+ }
+ }
+ }
+ }
+
+ _showError(message) {
+ this._commandError = true;
+ this._descriptionLabel.set_text(message);
+ }
+
+ _restart() {
+ if (Meta.is_wayland_compositor()) {
+ this._showError(_('Restart is not available on Wayland'));
+ return;
+ }
+ this._shouldFadeOut = false;
+ this.close();
+ Meta.restart(_('Restarting…'), global.context);
+ }
+
+ open() {
+ this._history.lastItem();
+ this._entryText.set_text('');
+ this._commandError = false;
+
+ if (this._lockdownSettings.get_boolean(DISABLE_COMMAND_LINE_KEY))
+ return false;
+
+ return super.open();
+ }
+});
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
new file mode 100644
index 0000000..325fbff
--- /dev/null
+++ b/js/ui/screenShield.js
@@ -0,0 +1,686 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ScreenShield */
+
+const {
+ AccountsService, Clutter, Gio,
+ GLib, Graphene, Meta, Shell, St,
+} = imports.gi;
+const Signals = imports.misc.signals;
+
+const GnomeSession = imports.misc.gnomeSession;
+const OVirt = imports.gdm.oVirt;
+const LoginManager = imports.misc.loginManager;
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const Overview = imports.ui.overview;
+const MessageTray = imports.ui.messageTray;
+const ShellDBus = imports.ui.shellDBus;
+const SmartcardManager = imports.misc.smartcardManager;
+
+const { adjustAnimationTime } = imports.ui.environment;
+
+const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
+const LOCK_ENABLED_KEY = 'lock-enabled';
+const LOCK_DELAY_KEY = 'lock-delay';
+
+const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
+const DISABLE_LOCK_KEY = 'disable-lock-screen';
+
+const LOCKED_STATE_STR = 'screenShield.locked';
+
+// ScreenShield animation time
+// - STANDARD_FADE_TIME is used when the session goes idle
+// - MANUAL_FADE_TIME is used for lowering the shield when asked by the user,
+// or when cancelling the dialog
+// - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking
+var STANDARD_FADE_TIME = 10000;
+var MANUAL_FADE_TIME = 300;
+var CURTAIN_SLIDE_TIME = 300;
+
+/**
+ * If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
+ * rather than through System Settings, you also need to set
+ * org.gnome.settings-daemon.plugins.power.sleep-display-ac and
+ * org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
+ * This will ensure that the screen blanks at the right time when it fades out.
+ * https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependency.
+ */
+var ScreenShield = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this.actor = Main.layoutManager.screenShieldGroup;
+
+ this._lockScreenState = MessageTray.State.HIDDEN;
+ this._lockScreenGroup = new St.Widget({
+ x_expand: true,
+ y_expand: true,
+ reactive: true,
+ can_focus: true,
+ name: 'lockScreenGroup',
+ visible: false,
+ });
+
+ this._lockDialogGroup = new St.Widget({
+ x_expand: true,
+ y_expand: true,
+ reactive: true,
+ can_focus: true,
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ name: 'lockDialogGroup',
+ });
+
+ this.actor.add_actor(this._lockScreenGroup);
+ this.actor.add_actor(this._lockDialogGroup);
+
+ this._presence = new GnomeSession.Presence((proxy, error) => {
+ if (error) {
+ logError(error, 'Error while reading gnome-session presence');
+ return;
+ }
+
+ this._onStatusChanged(proxy.status);
+ });
+ this._presence.connectSignal('StatusChanged', (proxy, senderName, [status]) => {
+ this._onStatusChanged(status);
+ });
+
+ this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
+
+ this._smartcardManager = SmartcardManager.getSmartcardManager();
+ this._smartcardManager.connect('smartcard-inserted',
+ (manager, token) => {
+ if (this._isLocked && token.UsedToLogin)
+ this._activateDialog();
+ });
+
+ this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
+ this._oVirtCredentialsManager.connect('user-authenticated',
+ () => {
+ if (this._isLocked)
+ this._activateDialog();
+ });
+
+ this._loginManager = LoginManager.getLoginManager();
+ this._loginManager.connect('prepare-for-sleep',
+ this._prepareForSleep.bind(this));
+
+ this._loginSession = null;
+ this._getLoginSession();
+
+ this._settings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA });
+ this._settings.connect(`changed::${LOCK_ENABLED_KEY}`, this._syncInhibitor.bind(this));
+
+ this._lockSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA });
+ this._lockSettings.connect(`changed::${DISABLE_LOCK_KEY}`, this._syncInhibitor.bind(this));
+
+ this._isModal = false;
+ this._isGreeter = false;
+ this._isActive = false;
+ this._isLocked = false;
+ this._inUnlockAnimation = false;
+ this._inhibited = false;
+ this._activationTime = 0;
+ this._becameActiveId = 0;
+ this._lockTimeoutId = 0;
+
+ // The "long" lightbox is used for the longer (20 seconds) fade from session
+ // to idle status, the "short" is used for quickly fading to black when locking
+ // manually
+ this._longLightbox = new Lightbox.Lightbox(Main.uiGroup, {
+ inhibitEvents: true,
+ fadeFactor: 1,
+ });
+ this._longLightbox.connect('notify::active', this._onLongLightbox.bind(this));
+ this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup, {
+ inhibitEvents: true,
+ fadeFactor: 1,
+ });
+ this._shortLightbox.connect('notify::active', this._onShortLightbox.bind(this));
+
+ this.idleMonitor = global.backend.get_core_idle_monitor();
+ this._cursorTracker = Meta.CursorTracker.get_for_display(global.display);
+
+ this._syncInhibitor();
+ }
+
+ async _getLoginSession() {
+ this._loginSession = await this._loginManager.getCurrentSessionProxy();
+ this._loginSession.connectSignal('Lock',
+ () => this.lock(false));
+ this._loginSession.connectSignal('Unlock',
+ () => this.deactivate(false));
+ this._loginSession.connect('g-properties-changed',
+ () => this._syncInhibitor());
+ this._syncInhibitor();
+ }
+
+ _setActive(active) {
+ let prevIsActive = this._isActive;
+ this._isActive = active;
+
+ if (prevIsActive != this._isActive)
+ this.emit('active-changed');
+
+ this._syncInhibitor();
+ }
+
+ _setLocked(locked) {
+ let prevIsLocked = this._isLocked;
+ this._isLocked = locked;
+
+ if (prevIsLocked !== this._isLocked)
+ this.emit('locked-changed');
+
+ if (this._loginSession)
+ this._loginSession.SetLockedHintAsync(locked).catch(logError);
+ }
+
+ _activateDialog() {
+ if (this._isLocked) {
+ this._ensureUnlockDialog(true /* allowCancel */);
+ this._dialog.activate();
+ } else {
+ this.deactivate(true /* animate */);
+ }
+ }
+
+ _maybeCancelDialog() {
+ if (!this._dialog)
+ return;
+
+ this._dialog.cancel();
+ if (this._isGreeter) {
+ // LoginDialog.cancel() will grab the key focus
+ // on its own, so ensure it stays on lock screen
+ // instead
+ this._dialog.grab_key_focus();
+ }
+ }
+
+ _becomeModal() {
+ if (this._isModal)
+ return true;
+
+ let grab = Main.pushModal(Main.uiGroup, { actionMode: Shell.ActionMode.LOCK_SCREEN });
+
+ // We expect at least a keyboard grab here
+ this._isModal = (grab.get_seat_state() & Clutter.GrabState.KEYBOARD) !== 0;
+ if (this._isModal)
+ this._grab = grab;
+ else
+ Main.popModal(grab);
+
+ return this._isModal;
+ }
+
+ async _syncInhibitor() {
+ const lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY);
+ const lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY);
+ const inhibit = !!this._loginSession && this._loginSession.Active &&
+ !this._isActive && lockEnabled && !lockLocked &&
+ !!Main.sessionMode.unlockDialog;
+
+ if (inhibit === this._inhibited)
+ return;
+
+ this._inhibited = inhibit;
+
+ this._inhibitCancellable?.cancel();
+ this._inhibitCancellable = new Gio.Cancellable();
+
+ if (inhibit) {
+ try {
+ this._inhibitor = await this._loginManager.inhibit(
+ _('GNOME needs to lock the screen'),
+ this._inhibitCancellable);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ log('Failed to inhibit suspend: %s'.format(e.message));
+ }
+ } else {
+ this._inhibitor?.close(null);
+ this._inhibitor = null;
+ }
+ }
+
+ _prepareForSleep(loginManager, aboutToSuspend) {
+ if (aboutToSuspend) {
+ if (this._settings.get_boolean(LOCK_ENABLED_KEY))
+ this.lock(true);
+ } else {
+ this._wakeUpScreen();
+ }
+ }
+
+ _onStatusChanged(status) {
+ if (status != GnomeSession.PresenceStatus.IDLE)
+ return;
+
+ this._maybeCancelDialog();
+
+ if (this._longLightbox.visible) {
+ // We're in the process of showing.
+ return;
+ }
+
+ if (!this._becomeModal()) {
+ // We could not become modal, so we can't activate the
+ // screenshield. The user is probably very upset at this
+ // point, but any application using global grabs is broken
+ // Just tell them to stop using this app
+ //
+ // XXX: another option is to kick the user into the gdm login
+ // screen, where we're not affected by grabs
+ Main.notifyError(_("Unable to lock"),
+ _("Lock was blocked by an application"));
+ return;
+ }
+
+ if (this._activationTime == 0)
+ this._activationTime = GLib.get_monotonic_time();
+
+ let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked;
+
+ if (shouldLock) {
+ let lockTimeout = Math.max(
+ adjustAnimationTime(STANDARD_FADE_TIME),
+ this._settings.get_uint(LOCK_DELAY_KEY) * 1000);
+ this._lockTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ lockTimeout,
+ () => {
+ this._lockTimeoutId = 0;
+ this.lock(false);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._lockTimeoutId, '[gnome-shell] this.lock');
+ }
+
+ this._activateFade(this._longLightbox, STANDARD_FADE_TIME);
+ }
+
+ _activateFade(lightbox, time) {
+ Main.uiGroup.set_child_above_sibling(lightbox, null);
+ lightbox.lightOn(time);
+
+ if (this._becameActiveId == 0)
+ this._becameActiveId = this.idleMonitor.add_user_active_watch(this._onUserBecameActive.bind(this));
+ }
+
+ _onUserBecameActive() {
+ // This function gets called here when the user becomes active
+ // after we activated a lightbox
+ // There are two possibilities here:
+ // - we're called when already locked; we just go back to the lock screen curtain
+ // - we're called because the session is IDLE but before the lightbox
+ // is fully shown; at this point isActive is false, so we just hide
+ // the lightbox, reset the activationTime and go back to the unlocked
+ // desktop
+ // using deactivate() is a little of overkill, but it ensures we
+ // don't forget of some bit like modal, DBus properties or idle watches
+ //
+ // Note: if the (long) lightbox is shown then we're necessarily
+ // active, because we call activate() without animation.
+
+ this.idleMonitor.remove_watch(this._becameActiveId);
+ this._becameActiveId = 0;
+
+ if (this._isLocked) {
+ this._longLightbox.lightOff();
+ this._shortLightbox.lightOff();
+ } else {
+ this.deactivate(false);
+ }
+ }
+
+ _onLongLightbox(lightBox) {
+ if (lightBox.active)
+ this.activate(false);
+ }
+
+ _onShortLightbox(lightBox) {
+ if (lightBox.active)
+ this._completeLockScreenShown();
+ }
+
+ showDialog() {
+ if (!this._becomeModal()) {
+ // In the login screen, this is a hard error. Fail-whale
+ const error = new GLib.Error(
+ Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED,
+ 'Could not acquire modal grab for the login screen. Aborting login process.');
+ global.context.terminate_with_error(error);
+ }
+
+ this.actor.show();
+ this._isGreeter = Main.sessionMode.isGreeter;
+ this._isLocked = true;
+ this._ensureUnlockDialog(true);
+ }
+
+ _hideLockScreenComplete() {
+ this._lockScreenState = MessageTray.State.HIDDEN;
+ this._lockScreenGroup.hide();
+
+ if (this._dialog) {
+ this._dialog.grab_key_focus();
+ this._dialog.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ }
+ }
+
+ _showPointer() {
+ this._cursorTracker.set_pointer_visible(true);
+
+ if (this._motionId) {
+ global.stage.disconnect(this._motionId);
+ this._motionId = 0;
+ }
+ }
+
+ _hidePointerUntilMotion() {
+ this._motionId = global.stage.connect('captured-event', (stage, event) => {
+ if (event.type() === Clutter.EventType.MOTION)
+ this._showPointer();
+
+ return Clutter.EVENT_PROPAGATE;
+ });
+ this._cursorTracker.set_pointer_visible(false);
+ }
+
+ _hideLockScreen(animate) {
+ if (this._lockScreenState == MessageTray.State.HIDDEN)
+ return;
+
+ this._lockScreenState = MessageTray.State.HIDING;
+
+ this._lockDialogGroup.remove_all_transitions();
+
+ if (animate) {
+ // Animate the lock screen out of screen
+ // if velocity is not specified (i.e. we come here from pressing ESC),
+ // use the same speed regardless of original position
+ // if velocity is specified, it's in pixels per milliseconds
+ let h = global.stage.height;
+ let delta = h + this._lockDialogGroup.translation_y;
+ let velocity = global.stage.height / CURTAIN_SLIDE_TIME;
+ let duration = delta / velocity;
+
+ this._lockDialogGroup.ease({
+ translation_y: -h,
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._hideLockScreenComplete(),
+ });
+ } else {
+ this._hideLockScreenComplete();
+ }
+
+ this._showPointer();
+ }
+
+ _ensureUnlockDialog(allowCancel) {
+ if (!this._dialog) {
+ let constructor = Main.sessionMode.unlockDialog;
+ if (!constructor) {
+ // This session mode has no locking capabilities
+ this.deactivate(true);
+ return false;
+ }
+
+ this._dialog = new constructor(this._lockDialogGroup);
+
+ let time = global.get_current_time();
+ if (!this._dialog.open(time)) {
+ // This is kind of an impossible error: we're already modal
+ // by the time we reach this...
+ log('Could not open login dialog: failed to acquire grab');
+ this.deactivate(true);
+ return false;
+ }
+
+ this._dialog.connect('failed', this._onUnlockFailed.bind(this));
+ this._wakeUpScreenId = this._dialog.connect(
+ 'wake-up-screen', this._wakeUpScreen.bind(this));
+ }
+
+ this._dialog.allowCancel = allowCancel;
+ this._dialog.grab_key_focus();
+ return true;
+ }
+
+ _onUnlockFailed() {
+ this._resetLockScreen({
+ animateLockScreen: true,
+ fadeToBlack: false,
+ });
+ }
+
+ _resetLockScreen(params) {
+ // Don't reset the lock screen unless it is completely hidden
+ // This prevents the shield going down if the lock-delay timeout
+ // fires while the user is dragging (which has the potential
+ // to confuse our state)
+ if (this._lockScreenState != MessageTray.State.HIDDEN)
+ return;
+
+ this._lockScreenGroup.show();
+ this._lockScreenState = MessageTray.State.SHOWING;
+
+ let fadeToBlack = params.fadeToBlack;
+
+ if (params.animateLockScreen) {
+ this._lockDialogGroup.translation_y = -global.screen_height;
+ this._lockDialogGroup.remove_all_transitions();
+ this._lockDialogGroup.ease({
+ translation_y: 0,
+ duration: Overview.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._lockScreenShown({ fadeToBlack, animateFade: true });
+ },
+ });
+ } else {
+ this._lockDialogGroup.translation_y = 0;
+ this._lockScreenShown({ fadeToBlack, animateFade: false });
+ }
+
+ this._dialog.grab_key_focus();
+ }
+
+ _lockScreenShown(params) {
+ this._hidePointerUntilMotion();
+
+ this._lockScreenState = MessageTray.State.SHOWN;
+
+ if (params.fadeToBlack && params.animateFade) {
+ // Take a beat
+
+ let id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MANUAL_FADE_TIME, () => {
+ this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] this._activateFade');
+ } else {
+ if (params.fadeToBlack)
+ this._activateFade(this._shortLightbox, 0);
+
+ this._completeLockScreenShown();
+ }
+ }
+
+ _completeLockScreenShown() {
+ this._setActive(true);
+ this.emit('lock-screen-shown');
+ }
+
+ _wakeUpScreen() {
+ if (!this.active)
+ return; // already woken up, or not yet asleep
+ this._onUserBecameActive();
+ this.emit('wake-up-screen');
+ }
+
+ get locked() {
+ return this._isLocked;
+ }
+
+ get active() {
+ return this._isActive;
+ }
+
+ get activationTime() {
+ return this._activationTime;
+ }
+
+ deactivate(animate) {
+ if (this._dialog)
+ this._dialog.finish(() => this._continueDeactivate(animate));
+ else
+ this._continueDeactivate(animate);
+ }
+
+ _continueDeactivate(animate) {
+ this._hideLockScreen(animate);
+
+ if (Main.sessionMode.currentMode == 'unlock-dialog')
+ Main.sessionMode.popMode('unlock-dialog');
+
+ this.emit('wake-up-screen');
+
+ if (this._isGreeter) {
+ // We don't want to "deactivate" any more than
+ // this. In particular, we don't want to drop
+ // the modal, hide ourselves or destroy the dialog
+ // But we do want to set isActive to false, so that
+ // gnome-session will reset the idle counter, and
+ // gnome-settings-daemon will stop blanking the screen
+
+ this._activationTime = 0;
+ this._setActive(false);
+ return;
+ }
+
+ if (this._dialog && !this._isGreeter)
+ this._dialog.popModal();
+
+ if (this._isModal) {
+ Main.popModal(this._grab);
+ this._grab = null;
+ this._isModal = false;
+ }
+
+ this._longLightbox.lightOff();
+ this._shortLightbox.lightOff();
+
+ this._lockDialogGroup.ease({
+ translation_y: -global.screen_height,
+ duration: Overview.ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._completeDeactivate(),
+ });
+ }
+
+ _completeDeactivate() {
+ if (this._dialog) {
+ this._dialog.destroy();
+ this._dialog = null;
+ }
+
+ this.actor.hide();
+
+ if (this._becameActiveId != 0) {
+ this.idleMonitor.remove_watch(this._becameActiveId);
+ this._becameActiveId = 0;
+ }
+
+ if (this._lockTimeoutId != 0) {
+ GLib.source_remove(this._lockTimeoutId);
+ this._lockTimeoutId = 0;
+ }
+
+ this._activationTime = 0;
+ this._setActive(false);
+ this._setLocked(false);
+ global.set_runtime_state(LOCKED_STATE_STR, null);
+ }
+
+ activate(animate) {
+ if (this._activationTime == 0)
+ this._activationTime = GLib.get_monotonic_time();
+
+ if (!this._ensureUnlockDialog(true))
+ return;
+
+ this.actor.show();
+
+ if (Main.sessionMode.currentMode !== 'unlock-dialog') {
+ this._isGreeter = Main.sessionMode.isGreeter;
+ if (!this._isGreeter)
+ Main.sessionMode.pushMode('unlock-dialog');
+ }
+
+ this._resetLockScreen({
+ animateLockScreen: animate,
+ fadeToBlack: true,
+ });
+ // On wayland, a crash brings down the entire session, so we don't
+ // need to defend against being restarted unlocked
+ if (!Meta.is_wayland_compositor())
+ global.set_runtime_state(LOCKED_STATE_STR, GLib.Variant.new('b', true));
+
+ // We used to set isActive and emit active-changed here,
+ // but now we do that from lockScreenShown, which means
+ // there is a 0.3 seconds window during which the lock
+ // screen is effectively visible and the screen is locked, but
+ // the DBus interface reports the screensaver is off.
+ // This is because when we emit ActiveChanged(true),
+ // gnome-settings-daemon blanks the screen, and we don't want
+ // blank during the animation.
+ // This is not a problem for the idle fade case, because we
+ // activate without animation in that case.
+ }
+
+ lock(animate) {
+ if (this._lockSettings.get_boolean(DISABLE_LOCK_KEY)) {
+ log('Screen lock is locked down, not locking'); // lock, lock - who's there?
+ return;
+ }
+
+ // Warn the user if we can't become modal
+ if (!this._becomeModal()) {
+ Main.notifyError(_("Unable to lock"),
+ _("Lock was blocked by an application"));
+ return;
+ }
+
+ // Clear the clipboard - otherwise, its contents may be leaked
+ // to unauthorized parties by pasting into the unlock dialog's
+ // password entry and unmasking the entry
+ St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, '');
+ St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, '');
+
+ let userManager = AccountsService.UserManager.get_default();
+ let user = userManager.get_user(GLib.get_user_name());
+
+ this.activate(animate);
+
+ const lock = this._isGreeter
+ ? true
+ : user.password_mode !== AccountsService.UserPasswordMode.NONE;
+ this._setLocked(lock);
+ }
+
+ // If the previous shell crashed, and gnome-session restarted us, then re-lock
+ lockIfWasLocked() {
+ if (!this._settings.get_boolean(LOCK_ENABLED_KEY))
+ return;
+ let wasLocked = global.get_runtime_state('b', LOCKED_STATE_STR);
+ if (wasLocked === null)
+ return;
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this.lock(false);
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+};
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
new file mode 100644
index 0000000..5139052
--- /dev/null
+++ b/js/ui/screenshot.js
@@ -0,0 +1,2897 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ScreenshotService, ScreenshotUI, showScreenshotUI, captureScreenshot */
+
+const { Clutter, Cogl, Gio, GObject, GLib, Graphene, Gtk, Meta, Shell, St } = imports.gi;
+
+const GrabHelper = imports.ui.grabHelper;
+const Layout = imports.ui.layout;
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const Workspace = imports.ui.workspace;
+
+Gio._promisify(Shell.Screenshot.prototype, 'pick_color');
+Gio._promisify(Shell.Screenshot.prototype, 'screenshot');
+Gio._promisify(Shell.Screenshot.prototype, 'screenshot_window');
+Gio._promisify(Shell.Screenshot.prototype, 'screenshot_area');
+Gio._promisify(Shell.Screenshot.prototype, 'screenshot_stage_to_content');
+Gio._promisify(Shell.Screenshot, 'composite_to_stream');
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+const { DBusSenderChecker } = imports.misc.util;
+
+const ScreenshotIface = loadInterfaceXML('org.gnome.Shell.Screenshot');
+
+const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
+const ScreencastProxy = Gio.DBusProxy.makeProxyWrapper(ScreencastIface);
+
+var IconLabelButton = GObject.registerClass(
+class IconLabelButton extends St.Button {
+ _init(iconName, label, params) {
+ super._init(params);
+
+ this._container = new St.BoxLayout({
+ vertical: true,
+ style_class: 'icon-label-button-container',
+ });
+ this.set_child(this._container);
+
+ this._container.add_child(new St.Icon({ icon_name: iconName }));
+ this._container.add_child(new St.Label({
+ text: label,
+ x_align: Clutter.ActorAlign.CENTER,
+ }));
+ }
+});
+
+var Tooltip = GObject.registerClass(
+class Tooltip extends St.Label {
+ _init(widget, params) {
+ super._init(params);
+
+ this._widget = widget;
+ this._timeoutId = null;
+
+ this._widget.connect('notify::hover', () => {
+ if (this._widget.hover)
+ this.open();
+ else
+ this.close();
+ });
+ }
+
+ open() {
+ if (this._timeoutId)
+ return;
+
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => {
+ this.opacity = 0;
+ this.show();
+
+ const extents = this._widget.get_transformed_extents();
+
+ const xOffset = Math.floor((extents.get_width() - this.width) / 2);
+ const x =
+ Math.clamp(extents.get_x() + xOffset, 0, global.stage.width - this.width);
+
+ const node = this.get_theme_node();
+ const yOffset = node.get_length('-y-offset');
+
+ const y = extents.get_y() - this.height - yOffset;
+
+ this.set_position(x, y);
+ this.ease({
+ opacity: 255,
+ duration: 150,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this._timeoutId = null;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] tooltip.open');
+ }
+
+ close() {
+ if (this._timeoutId) {
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = null;
+ return;
+ }
+
+ if (!this.visible)
+ return;
+
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 0,
+ duration: 100,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this.hide(),
+ });
+ }
+});
+
+var UIAreaIndicator = GObject.registerClass(
+class UIAreaIndicator extends St.Widget {
+ _init(params) {
+ super._init(params);
+
+ this._topRect = new St.Widget({ style_class: 'screenshot-ui-area-indicator-shade' });
+ this._topRect.add_constraint(new Clutter.BindConstraint({
+ source: this,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+ this._topRect.add_constraint(new Clutter.SnapConstraint({
+ source: this,
+ from_edge: Clutter.SnapEdge.TOP,
+ to_edge: Clutter.SnapEdge.TOP,
+ }));
+ this._topRect.add_constraint(new Clutter.SnapConstraint({
+ source: this,
+ from_edge: Clutter.SnapEdge.LEFT,
+ to_edge: Clutter.SnapEdge.LEFT,
+ }));
+ this.add_child(this._topRect);
+
+ this._bottomRect = new St.Widget({ style_class: 'screenshot-ui-area-indicator-shade' });
+ this._bottomRect.add_constraint(new Clutter.BindConstraint({
+ source: this,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+ this._bottomRect.add_constraint(new Clutter.SnapConstraint({
+ source: this,
+ from_edge: Clutter.SnapEdge.BOTTOM,
+ to_edge: Clutter.SnapEdge.BOTTOM,
+ }));
+ this._bottomRect.add_constraint(new Clutter.SnapConstraint({
+ source: this,
+ from_edge: Clutter.SnapEdge.LEFT,
+ to_edge: Clutter.SnapEdge.LEFT,
+ }));
+ this.add_child(this._bottomRect);
+
+ this._leftRect = new St.Widget({ style_class: 'screenshot-ui-area-indicator-shade' });
+ this._leftRect.add_constraint(new Clutter.SnapConstraint({
+ source: this,
+ from_edge: Clutter.SnapEdge.LEFT,
+ to_edge: Clutter.SnapEdge.LEFT,
+ }));
+ this._leftRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._topRect,
+ from_edge: Clutter.SnapEdge.TOP,
+ to_edge: Clutter.SnapEdge.BOTTOM,
+ }));
+ this._leftRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._bottomRect,
+ from_edge: Clutter.SnapEdge.BOTTOM,
+ to_edge: Clutter.SnapEdge.TOP,
+ }));
+ this.add_child(this._leftRect);
+
+ this._rightRect = new St.Widget({ style_class: 'screenshot-ui-area-indicator-shade' });
+ this._rightRect.add_constraint(new Clutter.SnapConstraint({
+ source: this,
+ from_edge: Clutter.SnapEdge.RIGHT,
+ to_edge: Clutter.SnapEdge.RIGHT,
+ }));
+ this._rightRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._topRect,
+ from_edge: Clutter.SnapEdge.TOP,
+ to_edge: Clutter.SnapEdge.BOTTOM,
+ }));
+ this._rightRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._bottomRect,
+ from_edge: Clutter.SnapEdge.BOTTOM,
+ to_edge: Clutter.SnapEdge.TOP,
+ }));
+ this.add_child(this._rightRect);
+
+ this._selectionRect = new St.Widget({ style_class: 'screenshot-ui-area-indicator-selection' });
+ this.add_child(this._selectionRect);
+
+ this._topRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._selectionRect,
+ from_edge: Clutter.SnapEdge.BOTTOM,
+ to_edge: Clutter.SnapEdge.TOP,
+ }));
+
+ this._bottomRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._selectionRect,
+ from_edge: Clutter.SnapEdge.TOP,
+ to_edge: Clutter.SnapEdge.BOTTOM,
+ }));
+
+ this._leftRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._selectionRect,
+ from_edge: Clutter.SnapEdge.RIGHT,
+ to_edge: Clutter.SnapEdge.LEFT,
+ }));
+
+ this._rightRect.add_constraint(new Clutter.SnapConstraint({
+ source: this._selectionRect,
+ from_edge: Clutter.SnapEdge.LEFT,
+ to_edge: Clutter.SnapEdge.RIGHT,
+ }));
+ }
+
+ setSelectionRect(x, y, width, height) {
+ this._selectionRect.set_position(x, y);
+ this._selectionRect.set_size(width, height);
+ }
+});
+
+var UIAreaSelector = GObject.registerClass({
+ Signals: { 'drag-started': {}, 'drag-ended': {} },
+}, class UIAreaSelector extends St.Widget {
+ _init(params) {
+ super._init(params);
+
+ // During a drag, this can be Clutter.BUTTON_PRIMARY,
+ // Clutter.BUTTON_SECONDARY or the string "touch" to identify the source
+ // of the drag operation.
+ this._dragButton = 0;
+ this._dragSequence = null;
+
+ this._areaIndicator = new UIAreaIndicator();
+ this._areaIndicator.add_constraint(new Clutter.BindConstraint({
+ source: this,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }));
+ this.add_child(this._areaIndicator);
+
+ this._topLeftHandle = new St.Widget({ style_class: 'screenshot-ui-area-selector-handle' });
+ this.add_child(this._topLeftHandle);
+ this._topRightHandle = new St.Widget({ style_class: 'screenshot-ui-area-selector-handle' });
+ this.add_child(this._topRightHandle);
+ this._bottomLeftHandle = new St.Widget({ style_class: 'screenshot-ui-area-selector-handle' });
+ this.add_child(this._bottomLeftHandle);
+ this._bottomRightHandle = new St.Widget({ style_class: 'screenshot-ui-area-selector-handle' });
+ this.add_child(this._bottomRightHandle);
+
+ // This will be updated before the first drawn frame.
+ this._handleSize = 0;
+ this._topLeftHandle.connect('style-changed', widget => {
+ this._handleSize = widget.get_theme_node().get_width();
+ this._updateSelectionRect();
+ });
+
+ this.connect('notify::mapped', () => {
+ if (this.mapped) {
+ const [x, y] = global.get_pointer();
+ this._updateCursor(x, y);
+ }
+ });
+
+ // Initialize area to out of bounds so reset() below resets it.
+ this._startX = -1;
+ this._startY = 0;
+ this._lastX = 0;
+ this._lastY = 0;
+
+ this.reset();
+ }
+
+ reset() {
+ this.stopDrag();
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+
+ // Preserve area selection if possible. If the area goes out of bounds,
+ // the monitors might have changed, so reset the area.
+ const [x, y, w, h] = this.getGeometry();
+ if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) {
+ // Initialize area to out of bounds so if there's no monitor,
+ // the area will be reset once a monitor does appear.
+ this._startX = -1;
+ this._startY = 0;
+ this._lastX = 0;
+ this._lastY = 0;
+
+ // This can happen when running headless without any monitors.
+ if (Main.layoutManager.primaryIndex !== -1) {
+ const monitor =
+ Main.layoutManager.monitors[Main.layoutManager.primaryIndex];
+
+ this._startX = monitor.x + Math.floor(monitor.width * 3 / 8);
+ this._startY = monitor.y + Math.floor(monitor.height * 3 / 8);
+ this._lastX = monitor.x + Math.floor(monitor.width * 5 / 8) - 1;
+ this._lastY = monitor.y + Math.floor(monitor.height * 5 / 8) - 1;
+ }
+
+ this._updateSelectionRect();
+ }
+ }
+
+ getGeometry() {
+ const leftX = Math.min(this._startX, this._lastX);
+ const topY = Math.min(this._startY, this._lastY);
+ const rightX = Math.max(this._startX, this._lastX);
+ const bottomY = Math.max(this._startY, this._lastY);
+
+ return [leftX, topY, rightX - leftX + 1, bottomY - topY + 1];
+ }
+
+ _updateSelectionRect() {
+ const [x, y, w, h] = this.getGeometry();
+ this._areaIndicator.setSelectionRect(x, y, w, h);
+
+ const offset = this._handleSize / 2;
+ this._topLeftHandle.set_position(x - offset, y - offset);
+ this._topRightHandle.set_position(x + w - 1 - offset, y - offset);
+ this._bottomLeftHandle.set_position(x - offset, y + h - 1 - offset);
+ this._bottomRightHandle.set_position(x + w - 1 - offset, y + h - 1 - offset);
+ }
+
+ _computeCursorType(cursorX, cursorY) {
+ const [leftX, topY, width, height] = this.getGeometry();
+ const [rightX, bottomY] = [leftX + width - 1, topY + height - 1];
+ const [x, y] = [cursorX, cursorY];
+
+ // Check if the cursor overlaps the handles first.
+ const limit = (this._handleSize / 2) ** 2;
+ if ((leftX - x) ** 2 + (topY - y) ** 2 <= limit)
+ return Meta.Cursor.NW_RESIZE;
+ else if ((rightX - x) ** 2 + (topY - y) ** 2 <= limit)
+ return Meta.Cursor.NE_RESIZE;
+ else if ((leftX - x) ** 2 + (bottomY - y) ** 2 <= limit)
+ return Meta.Cursor.SW_RESIZE;
+ else if ((rightX - x) ** 2 + (bottomY - y) ** 2 <= limit)
+ return Meta.Cursor.SE_RESIZE;
+
+ // Now check the rest of the rectangle.
+ const threshold =
+ 10 * St.ThemeContext.get_for_stage(global.stage).scaleFactor;
+
+ if (leftX - x >= 0 && leftX - x <= threshold) {
+ if (topY - y >= 0 && topY - y <= threshold)
+ return Meta.Cursor.NW_RESIZE;
+ else if (y - bottomY >= 0 && y - bottomY <= threshold)
+ return Meta.Cursor.SW_RESIZE;
+ else if (topY - y < 0 && y - bottomY < 0)
+ return Meta.Cursor.WEST_RESIZE;
+ } else if (x - rightX >= 0 && x - rightX <= threshold) {
+ if (topY - y >= 0 && topY - y <= threshold)
+ return Meta.Cursor.NE_RESIZE;
+ else if (y - bottomY >= 0 && y - bottomY <= threshold)
+ return Meta.Cursor.SE_RESIZE;
+ else if (topY - y < 0 && y - bottomY < 0)
+ return Meta.Cursor.EAST_RESIZE;
+ } else if (leftX - x < 0 && x - rightX < 0) {
+ if (topY - y >= 0 && topY - y <= threshold)
+ return Meta.Cursor.NORTH_RESIZE;
+ else if (y - bottomY >= 0 && y - bottomY <= threshold)
+ return Meta.Cursor.SOUTH_RESIZE;
+ else if (topY - y < 0 && y - bottomY < 0)
+ return Meta.Cursor.MOVE_OR_RESIZE_WINDOW;
+ }
+
+ return Meta.Cursor.CROSSHAIR;
+ }
+
+ stopDrag() {
+ if (!this._dragButton)
+ return;
+
+ if (this._dragGrab) {
+ this._dragGrab.dismiss();
+ this._dragGrab = null;
+ }
+
+ this._dragButton = 0;
+ this._dragSequence = null;
+
+ if (this._dragCursor === Meta.Cursor.CROSSHAIR &&
+ this._lastX === this._startX && this._lastY === this._startY) {
+ // The user clicked without dragging. Make up a larger selection
+ // to reduce confusion.
+ const offset =
+ 20 * St.ThemeContext.get_for_stage(global.stage).scaleFactor;
+ this._startX -= offset;
+ this._startY -= offset;
+ this._lastX += offset;
+ this._lastY += offset;
+
+ // Keep the coordinates inside the stage.
+ if (this._startX < 0) {
+ this._lastX -= this._startX;
+ this._startX = 0;
+ } else if (this._lastX >= this.width) {
+ this._startX -= this._lastX - this.width + 1;
+ this._lastX = this.width - 1;
+ }
+
+ if (this._startY < 0) {
+ this._lastY -= this._startY;
+ this._startY = 0;
+ } else if (this._lastY >= this.height) {
+ this._startY -= this._lastY - this.height + 1;
+ this._lastY = this.height - 1;
+ }
+
+ this._updateSelectionRect();
+ }
+
+ this.emit('drag-ended');
+ }
+
+ _updateCursor(x, y) {
+ const cursor = this._computeCursorType(x, y);
+ global.display.set_cursor(cursor);
+ }
+
+ _onPress(event, button, sequence) {
+ if (this._dragButton)
+ return Clutter.EVENT_PROPAGATE;
+
+ const cursor = this._computeCursorType(event.x, event.y);
+
+ // Clicking outside of the selection, or using the right mouse button,
+ // or with Ctrl results in dragging a new selection from scratch.
+ if (cursor === Meta.Cursor.CROSSHAIR ||
+ button === Clutter.BUTTON_SECONDARY ||
+ (event.modifier_state & Clutter.ModifierType.CONTROL_MASK)) {
+ this._dragButton = button;
+
+ this._dragCursor = Meta.Cursor.CROSSHAIR;
+ global.display.set_cursor(Meta.Cursor.CROSSHAIR);
+
+ [this._startX, this._startY] = [event.x, event.y];
+ this._lastX = this._startX = Math.floor(this._startX);
+ this._lastY = this._startY = Math.floor(this._startY);
+
+ this._updateSelectionRect();
+ } else {
+ // This is a move or resize operation.
+ this._dragButton = button;
+
+ this._dragCursor = cursor;
+ this._dragStartX = event.x;
+ this._dragStartY = event.y;
+
+ const [leftX, topY, width, height] = this.getGeometry();
+ const rightX = leftX + width - 1;
+ const bottomY = topY + height - 1;
+
+ // For moving, start X and Y are the top left corner, while
+ // last X and Y are the bottom right corner.
+ if (cursor === Meta.Cursor.MOVE_OR_RESIZE_WINDOW) {
+ this._startX = leftX;
+ this._startY = topY;
+ this._lastX = rightX;
+ this._lastY = bottomY;
+ }
+
+ // Start X and Y are set to the stationary sides, while last X
+ // and Y are set to the moving sides.
+ if (cursor === Meta.Cursor.NW_RESIZE ||
+ cursor === Meta.Cursor.WEST_RESIZE ||
+ cursor === Meta.Cursor.SW_RESIZE) {
+ this._startX = rightX;
+ this._lastX = leftX;
+ }
+ if (cursor === Meta.Cursor.NE_RESIZE ||
+ cursor === Meta.Cursor.EAST_RESIZE ||
+ cursor === Meta.Cursor.SE_RESIZE) {
+ this._startX = leftX;
+ this._lastX = rightX;
+ }
+ if (cursor === Meta.Cursor.NW_RESIZE ||
+ cursor === Meta.Cursor.NORTH_RESIZE ||
+ cursor === Meta.Cursor.NE_RESIZE) {
+ this._startY = bottomY;
+ this._lastY = topY;
+ }
+ if (cursor === Meta.Cursor.SW_RESIZE ||
+ cursor === Meta.Cursor.SOUTH_RESIZE ||
+ cursor === Meta.Cursor.SE_RESIZE) {
+ this._startY = topY;
+ this._lastY = bottomY;
+ }
+ }
+
+ if (this._dragButton) {
+ this._dragGrab = global.stage.grab(this);
+ this._dragSequence = sequence;
+
+ this.emit('drag-started');
+
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onRelease(event, button, sequence) {
+ if (this._dragButton !== button ||
+ this._dragSequence?.get_slot() !== sequence?.get_slot())
+ return Clutter.EVENT_PROPAGATE;
+
+ this.stopDrag();
+
+ // We might have finished creating a new selection, so we need to
+ // update the cursor.
+ this._updateCursor(event.x, event.y);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _onMotion(event, sequence) {
+ if (!this._dragButton) {
+ this._updateCursor(event.x, event.y);
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ if (sequence?.get_slot() !== this._dragSequence?.get_slot())
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._dragCursor === Meta.Cursor.CROSSHAIR) {
+ [this._lastX, this._lastY] = [event.x, event.y];
+ this._lastX = Math.floor(this._lastX);
+ this._lastY = Math.floor(this._lastY);
+ } else {
+ let dx = Math.round(event.x - this._dragStartX);
+ let dy = Math.round(event.y - this._dragStartY);
+
+ if (this._dragCursor === Meta.Cursor.MOVE_OR_RESIZE_WINDOW) {
+ const [,, selectionWidth, selectionHeight] = this.getGeometry();
+
+ let newStartX = this._startX + dx;
+ let newStartY = this._startY + dy;
+ let newLastX = this._lastX + dx;
+ let newLastY = this._lastY + dy;
+
+ let overshootX = 0;
+ let overshootY = 0;
+
+ // Keep the size intact if we bumped into the stage edge.
+ if (newStartX < 0) {
+ overshootX = 0 - newStartX;
+ newStartX = 0;
+ newLastX = newStartX + (selectionWidth - 1);
+ } else if (newLastX > this.width - 1) {
+ overshootX = (this.width - 1) - newLastX;
+ newLastX = this.width - 1;
+ newStartX = newLastX - (selectionWidth - 1);
+ }
+
+ if (newStartY < 0) {
+ overshootY = 0 - newStartY;
+ newStartY = 0;
+ newLastY = newStartY + (selectionHeight - 1);
+ } else if (newLastY > this.height - 1) {
+ overshootY = (this.height - 1) - newLastY;
+ newLastY = this.height - 1;
+ newStartY = newLastY - (selectionHeight - 1);
+ }
+
+ // Add the overshoot to the delta to create a "rubberbanding"
+ // behavior of the pointer when dragging.
+ dx += overshootX;
+ dy += overshootY;
+
+ this._startX = newStartX;
+ this._startY = newStartY;
+ this._lastX = newLastX;
+ this._lastY = newLastY;
+ } else {
+ if (this._dragCursor === Meta.Cursor.WEST_RESIZE ||
+ this._dragCursor === Meta.Cursor.EAST_RESIZE)
+ dy = 0;
+ if (this._dragCursor === Meta.Cursor.NORTH_RESIZE ||
+ this._dragCursor === Meta.Cursor.SOUTH_RESIZE)
+ dx = 0;
+
+ // Make sure last X and Y are clamped between 0 and size - 1,
+ // while always preserving the cursor dragging position relative
+ // to the selection rectangle.
+ this._lastX += dx;
+ if (this._lastX >= this.width) {
+ dx -= this._lastX - this.width + 1;
+ this._lastX = this.width - 1;
+ } else if (this._lastX < 0) {
+ dx -= this._lastX;
+ this._lastX = 0;
+ }
+
+ this._lastY += dy;
+ if (this._lastY >= this.height) {
+ dy -= this._lastY - this.height + 1;
+ this._lastY = this.height - 1;
+ } else if (this._lastY < 0) {
+ dy -= this._lastY;
+ this._lastY = 0;
+ }
+
+ // If we drag the handle past a selection side, update which
+ // handles are which.
+ if (this._lastX > this._startX) {
+ if (this._dragCursor === Meta.Cursor.NW_RESIZE)
+ this._dragCursor = Meta.Cursor.NE_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.SW_RESIZE)
+ this._dragCursor = Meta.Cursor.SE_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.WEST_RESIZE)
+ this._dragCursor = Meta.Cursor.EAST_RESIZE;
+ } else {
+ // eslint-disable-next-line no-lonely-if
+ if (this._dragCursor === Meta.Cursor.NE_RESIZE)
+ this._dragCursor = Meta.Cursor.NW_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.SE_RESIZE)
+ this._dragCursor = Meta.Cursor.SW_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.EAST_RESIZE)
+ this._dragCursor = Meta.Cursor.WEST_RESIZE;
+ }
+
+ if (this._lastY > this._startY) {
+ if (this._dragCursor === Meta.Cursor.NW_RESIZE)
+ this._dragCursor = Meta.Cursor.SW_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.NE_RESIZE)
+ this._dragCursor = Meta.Cursor.SE_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.NORTH_RESIZE)
+ this._dragCursor = Meta.Cursor.SOUTH_RESIZE;
+ } else {
+ // eslint-disable-next-line no-lonely-if
+ if (this._dragCursor === Meta.Cursor.SW_RESIZE)
+ this._dragCursor = Meta.Cursor.NW_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.SE_RESIZE)
+ this._dragCursor = Meta.Cursor.NE_RESIZE;
+ else if (this._dragCursor === Meta.Cursor.SOUTH_RESIZE)
+ this._dragCursor = Meta.Cursor.NORTH_RESIZE;
+ }
+
+ global.display.set_cursor(this._dragCursor);
+ }
+
+ this._dragStartX += dx;
+ this._dragStartY += dy;
+ }
+
+ this._updateSelectionRect();
+
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_button_press_event(event) {
+ if (event.button === Clutter.BUTTON_PRIMARY ||
+ event.button === Clutter.BUTTON_SECONDARY)
+ return this._onPress(event, event.button, null);
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_button_release_event(event) {
+ if (event.button === Clutter.BUTTON_PRIMARY ||
+ event.button === Clutter.BUTTON_SECONDARY)
+ return this._onRelease(event, event.button, null);
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_motion_event(event) {
+ return this._onMotion(event, null);
+ }
+
+ vfunc_touch_event(event) {
+ if (event.type === Clutter.EventType.TOUCH_BEGIN)
+ return this._onPress(event, 'touch', event.sequence);
+ else if (event.type === Clutter.EventType.TOUCH_END)
+ return this._onRelease(event, 'touch', event.sequence);
+ else if (event.type === Clutter.EventType.TOUCH_UPDATE)
+ return this._onMotion(event, event.sequence);
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_leave_event(event) {
+ // If we're dragging and go over the panel we still get a leave event
+ // for some reason, even though we have a grab. We don't want to switch
+ // the cursor when we're dragging.
+ if (!this._dragButton)
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+
+ return super.vfunc_leave_event(event);
+ }
+});
+
+var UIWindowSelectorLayout = GObject.registerClass(
+class UIWindowSelectorLayout extends Workspace.WorkspaceLayout {
+ _init(monitorIndex) {
+ super._init(null, monitorIndex, null);
+ }
+
+ vfunc_set_container(container) {
+ this._container = container;
+ this._syncWorkareaTracking();
+ }
+
+ vfunc_allocate(container, box) {
+ const containerBox = container.allocation;
+ const containerAllocationChanged =
+ this._lastBox === null || !this._lastBox.equal(containerBox);
+ this._lastBox = containerBox.copy();
+
+ let layoutChanged = false;
+ if (this._layout === null) {
+ this._layout = this._createBestLayout(this._workarea);
+ layoutChanged = true;
+ }
+
+ if (layoutChanged || containerAllocationChanged)
+ this._windowSlots = this._getWindowSlots(box.copy());
+
+ const childBox = new Clutter.ActorBox();
+
+ const nSlots = this._windowSlots.length;
+ for (let i = 0; i < nSlots; i++) {
+ let [x, y, width, height, child] = this._windowSlots[i];
+
+ childBox.set_origin(x, y);
+ childBox.set_size(width, height);
+
+ child.allocate(childBox);
+ }
+ }
+
+ addWindow(window) {
+ if (this._sortedWindows.includes(window))
+ return;
+
+ this._sortedWindows.push(window);
+
+ this._container.add_child(window);
+
+ this._layout = null;
+ this.layout_changed();
+ }
+
+ reset() {
+ for (const window of this._sortedWindows)
+ window.destroy();
+
+ this._sortedWindows = [];
+ this._windowSlots = [];
+ this._layout = null;
+ }
+
+ get windows() {
+ return this._sortedWindows;
+ }
+});
+
+var UIWindowSelectorWindow = GObject.registerClass(
+class UIWindowSelectorWindow extends St.Button {
+ _init(actor, params) {
+ super._init(params);
+
+ const window = actor.metaWindow;
+ this._boundingBox = window.get_frame_rect();
+ this._bufferRect = window.get_buffer_rect();
+ this._bufferScale = actor.get_resource_scale();
+ this._actor = new Clutter.Actor({
+ content: actor.paint_to_content(null),
+ });
+ this.add_child(this._actor);
+
+ this._border = new St.Bin({ style_class: 'screenshot-ui-window-selector-window-border' });
+ this._border.connect('style-changed', () => {
+ this._borderSize =
+ this._border.get_theme_node().get_border_width(St.Side.TOP);
+ });
+ this.add_child(this._border);
+
+ this._border.child = new St.Icon({
+ icon_name: 'object-select-symbolic',
+ style_class: 'screenshot-ui-window-selector-check',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._cursor = null;
+ this._cursorPoint = { x: 0, y: 0 };
+ this._shouldShowCursor = window.has_pointer && window.has_pointer();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ get boundingBox() {
+ return this._boundingBox;
+ }
+
+ get windowCenter() {
+ const boundingBox = this.boundingBox;
+ return {
+ x: boundingBox.x + boundingBox.width / 2,
+ y: boundingBox.y + boundingBox.height / 2,
+ };
+ }
+
+ chromeHeights() {
+ return [0, 0];
+ }
+
+ chromeWidths() {
+ return [0, 0];
+ }
+
+ overlapHeights() {
+ return [0, 0];
+ }
+
+ get cursorPoint() {
+ return {
+ x: this._cursorPoint.x + this._boundingBox.x - this._bufferRect.x,
+ y: this._cursorPoint.y + this._boundingBox.y - this._bufferRect.y,
+ };
+ }
+
+ get bufferScale() {
+ return this._bufferScale;
+ }
+
+ get windowContent() {
+ return this._actor.content;
+ }
+
+ _onDestroy() {
+ this.remove_child(this._actor);
+ this._actor.destroy();
+ this._actor = null;
+ this.remove_child(this._border);
+ this._border.destroy();
+ this._border = null;
+
+ if (this._cursor) {
+ this.remove_child(this._cursor);
+ this._cursor.destroy();
+ this._cursor = null;
+ }
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ // Border goes around the window.
+ const borderBox = box.copy();
+ borderBox.set_origin(0, 0);
+ borderBox.x1 -= this._borderSize;
+ borderBox.y1 -= this._borderSize;
+ borderBox.x2 += this._borderSize;
+ borderBox.y2 += this._borderSize;
+ this._border.allocate(borderBox);
+
+ // box should contain this._boundingBox worth of window. Compute
+ // origin and size for the actor box to satisfy that.
+ const xScale = box.get_width() / this._boundingBox.width;
+ const yScale = box.get_height() / this._boundingBox.height;
+
+ const [, windowW, windowH] = this._actor.content.get_preferred_size();
+
+ const actorBox = new Clutter.ActorBox();
+ actorBox.set_origin(
+ (this._bufferRect.x - this._boundingBox.x) * xScale,
+ (this._bufferRect.y - this._boundingBox.y) * yScale
+ );
+ actorBox.set_size(
+ windowW * xScale / this._bufferScale,
+ windowH * yScale / this._bufferScale
+ );
+ this._actor.allocate(actorBox);
+
+ // Allocate the cursor if we have one.
+ if (!this._cursor)
+ return;
+
+ let [, , w, h] = this._cursor.get_preferred_size();
+ w *= this._cursorScale;
+ h *= this._cursorScale;
+
+ const cursorBox = new Clutter.ActorBox({
+ x1: this._cursorPoint.x,
+ y1: this._cursorPoint.y,
+ x2: this._cursorPoint.x + w,
+ y2: this._cursorPoint.y + h,
+ });
+ cursorBox.x1 *= xScale;
+ cursorBox.x2 *= xScale;
+ cursorBox.y1 *= yScale;
+ cursorBox.y2 *= yScale;
+
+ this._cursor.allocate(cursorBox);
+ }
+
+ addCursorTexture(content, point, scale) {
+ if (!this._shouldShowCursor)
+ return;
+
+ // Add the cursor.
+ this._cursor = new St.Widget({
+ content,
+ request_mode: Clutter.RequestMode.CONTENT_SIZE,
+ });
+
+ this._cursorPoint = {
+ x: point.x - this._boundingBox.x,
+ y: point.y - this._boundingBox.y,
+ };
+ this._cursorScale = scale;
+
+ this.insert_child_below(this._cursor, this._border);
+ }
+
+ getCursorTexture() {
+ return this._cursor?.content;
+ }
+
+ setCursorVisible(visible) {
+ if (!this._cursor)
+ return;
+
+ this._cursor.visible = visible;
+ }
+});
+
+var UIWindowSelector = GObject.registerClass(
+class UIWindowSelector extends St.Widget {
+ _init(monitorIndex, params) {
+ super._init(params);
+ super.layout_manager = new Clutter.BinLayout();
+
+ this._monitorIndex = monitorIndex;
+
+ this._layoutManager = new UIWindowSelectorLayout(monitorIndex);
+
+ // Window screenshots
+ this._container = new St.Widget({
+ style_class: 'screenshot-ui-window-selector-window-container',
+ x_expand: true,
+ y_expand: true,
+ });
+ this._container.layout_manager = this._layoutManager;
+ this.add_child(this._container);
+ }
+
+ capture() {
+ for (const actor of global.get_window_actors()) {
+ let window = actor.metaWindow;
+ let workspaceManager = global.workspace_manager;
+ let activeWorkspace = workspaceManager.get_active_workspace();
+ if (window.is_override_redirect() ||
+ !window.located_on_workspace(activeWorkspace) ||
+ window.get_monitor() !== this._monitorIndex)
+ continue;
+
+ const widget = new UIWindowSelectorWindow(
+ actor,
+ {
+ style_class: 'screenshot-ui-window-selector-window',
+ reactive: true,
+ can_focus: true,
+ toggle_mode: true,
+ }
+ );
+
+ widget.connect('key-focus-in', win => {
+ Main.screenshotUI.grab_key_focus();
+ win.checked = true;
+ });
+
+ if (window.has_focus()) {
+ widget.checked = true;
+ widget.toggle_mode = false;
+ }
+
+ this._layoutManager.addWindow(widget);
+ }
+ }
+
+ reset() {
+ this._layoutManager.reset();
+ }
+
+ windows() {
+ return this._layoutManager.windows;
+ }
+});
+
+const UIMode = {
+ SCREENSHOT: 0,
+ SCREENCAST: 1,
+};
+
+var ScreenshotUI = GObject.registerClass({
+ Properties: {
+ 'screencast-in-progress': GObject.ParamSpec.boolean(
+ 'screencast-in-progress',
+ 'screencast-in-progress',
+ 'screencast-in-progress',
+ GObject.ParamFlags.READABLE,
+ false),
+ },
+}, class ScreenshotUI extends St.Widget {
+ _init() {
+ super._init({
+ name: 'screenshot-ui',
+ constraints: new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }),
+ layout_manager: new Clutter.BinLayout(),
+ opacity: 0,
+ visible: false,
+ reactive: true,
+ });
+
+ this._screencastInProgress = false;
+ this._screencastSupported = false;
+
+ this._screencastProxy = new ScreencastProxy(
+ Gio.DBus.session,
+ 'org.gnome.Shell.Screencast',
+ '/org/gnome/Shell/Screencast',
+ (object, error) => {
+ if (error !== null) {
+ log('Error connecting to the screencast service');
+ return;
+ }
+
+ this._screencastSupported = this._screencastProxy.ScreencastSupported;
+ this._castButton.visible = this._screencastSupported;
+ });
+
+ this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
+
+ // The full-screen screenshot has a separate container so that we can
+ // show it without the screenshot UI fade-in for a nicer animation.
+ this._stageScreenshotContainer = new St.Widget({ visible: false });
+ this._stageScreenshotContainer.add_constraint(new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }));
+ Main.layoutManager.screenshotUIGroup.add_child(
+ this._stageScreenshotContainer);
+
+ this._screencastAreaIndicator = new UIAreaIndicator({
+ style_class: 'screenshot-ui-screencast-area-indicator',
+ visible: false,
+ });
+ this._screencastAreaIndicator.add_constraint(new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }));
+ this.bind_property(
+ 'screencast-in-progress',
+ this._screencastAreaIndicator,
+ 'visible',
+ GObject.BindingFlags.DEFAULT);
+ // Add it directly to the stage so that it's above popup menus.
+ global.stage.add_child(this._screencastAreaIndicator);
+ Shell.util_set_hidden_from_pick(this._screencastAreaIndicator, true);
+
+ Main.layoutManager.screenshotUIGroup.add_child(this);
+
+ this._stageScreenshot = new St.Widget({ style_class: 'screenshot-ui-screen-screenshot' });
+ this._stageScreenshot.add_constraint(new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }));
+ this._stageScreenshotContainer.add_child(this._stageScreenshot);
+
+ this._cursor = new St.Widget();
+ this._stageScreenshotContainer.add_child(this._cursor);
+
+ this._openingCoroutineInProgress = false;
+ this._grabHelper = new GrabHelper.GrabHelper(this, {
+ actionMode: Shell.ActionMode.POPUP,
+ });
+
+ this._areaSelector = new UIAreaSelector({
+ style_class: 'screenshot-ui-area-selector',
+ x_expand: true,
+ y_expand: true,
+ reactive: true,
+ });
+ this.add_child(this._areaSelector);
+
+ this._primaryMonitorBin = new St.Widget({ layout_manager: new Clutter.BinLayout() });
+ this._primaryMonitorBin.add_constraint(
+ new Layout.MonitorConstraint({ 'primary': true }));
+ this.add_child(this._primaryMonitorBin);
+
+ this._panel = new St.BoxLayout({
+ style_class: 'screenshot-ui-panel',
+ y_align: Clutter.ActorAlign.END,
+ y_expand: true,
+ vertical: true,
+ offscreen_redirect: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY,
+ });
+ this._primaryMonitorBin.add_child(this._panel);
+
+ this._closeButton = new St.Button({
+ style_class: 'screenshot-ui-close-button',
+ icon_name: 'preview-close-symbolic',
+ });
+ this._closeButton.add_constraint(new Clutter.BindConstraint({
+ source: this._panel,
+ coordinate: Clutter.BindCoordinate.POSITION,
+ }));
+ this._closeButton.add_constraint(new Clutter.AlignConstraint({
+ source: this._panel,
+ align_axis: Clutter.AlignAxis.Y_AXIS,
+ pivot_point: new Graphene.Point({ x: -1, y: 0.5 }),
+ factor: 0,
+ }));
+ this._closeButtonXAlignConstraint = new Clutter.AlignConstraint({
+ source: this._panel,
+ align_axis: Clutter.AlignAxis.X_AXIS,
+ pivot_point: new Graphene.Point({ x: 0.5, y: -1 }),
+ });
+ this._closeButton.add_constraint(this._closeButtonXAlignConstraint);
+ this._closeButton.connect('clicked', () => this.close());
+ this._primaryMonitorBin.add_child(this._closeButton);
+
+ this._areaSelector.connect('drag-started', () => {
+ this._panel.ease({
+ opacity: 100,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ this._closeButton.ease({
+ opacity: 100,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ });
+ this._areaSelector.connect('drag-ended', () => {
+ this._panel.ease({
+ opacity: 255,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ this._closeButton.ease({
+ opacity: 255,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ });
+
+ this._typeButtonContainer = new St.Widget({
+ style_class: 'screenshot-ui-type-button-container',
+ layout_manager: new Clutter.BoxLayout({
+ spacing: 12,
+ homogeneous: true,
+ }),
+ });
+ this._panel.add_child(this._typeButtonContainer);
+
+ this._selectionButton = new IconLabelButton('screenshot-ui-area-symbolic', _('Selection'), {
+ style_class: 'screenshot-ui-type-button',
+ checked: true,
+ x_expand: true,
+ });
+ this._selectionButton.connect('notify::checked',
+ this._onSelectionButtonToggled.bind(this));
+ this._typeButtonContainer.add_child(this._selectionButton);
+
+ this.add_child(new Tooltip(this._selectionButton, {
+ text: _('Area Selection'),
+ style_class: 'screenshot-ui-tooltip',
+ visible: false,
+ }));
+
+ this._screenButton = new IconLabelButton('screenshot-ui-display-symbolic', _('Screen'), {
+ style_class: 'screenshot-ui-type-button',
+ toggle_mode: true,
+ x_expand: true,
+ });
+ this._screenButton.connect('notify::checked',
+ this._onScreenButtonToggled.bind(this));
+ this._typeButtonContainer.add_child(this._screenButton);
+
+ this.add_child(new Tooltip(this._screenButton, {
+ text: _('Screen Selection'),
+ style_class: 'screenshot-ui-tooltip',
+ visible: false,
+ }));
+
+ this._windowButton = new IconLabelButton('screenshot-ui-window-symbolic', _('Window'), {
+ style_class: 'screenshot-ui-type-button',
+ toggle_mode: true,
+ x_expand: true,
+ });
+ this._windowButton.connect('notify::checked',
+ this._onWindowButtonToggled.bind(this));
+ this._typeButtonContainer.add_child(this._windowButton);
+
+ this.add_child(new Tooltip(this._windowButton, {
+ text: _('Window Selection'),
+ style_class: 'screenshot-ui-tooltip',
+ visible: false,
+ }));
+
+ this._bottomRowContainer = new St.Widget({ layout_manager: new Clutter.BinLayout() });
+ this._panel.add_child(this._bottomRowContainer);
+
+ this._shotCastContainer = new St.BoxLayout({
+ style_class: 'screenshot-ui-shot-cast-container',
+ x_align: Clutter.ActorAlign.START,
+ x_expand: true,
+ });
+ this._bottomRowContainer.add_child(this._shotCastContainer);
+
+ this._shotButton = new St.Button({
+ style_class: 'screenshot-ui-shot-cast-button',
+ icon_name: 'camera-photo-symbolic',
+ checked: true,
+ });
+ this._shotButton.connect('notify::checked',
+ this._onShotButtonToggled.bind(this));
+ this._shotCastContainer.add_child(this._shotButton);
+
+ this._castButton = new St.Button({
+ style_class: 'screenshot-ui-shot-cast-button',
+ icon_name: 'camera-web-symbolic',
+ toggle_mode: true,
+ visible: false,
+ });
+ this._castButton.connect('notify::checked',
+ this._onCastButtonToggled.bind(this));
+ this._shotCastContainer.add_child(this._castButton);
+
+ this._shotButton.bind_property('checked', this._castButton, 'checked',
+ GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.INVERT_BOOLEAN);
+
+ this._shotCastTooltip = new Tooltip(this._shotCastContainer, {
+ text: _('Screenshot / Screencast'),
+ style_class: 'screenshot-ui-tooltip',
+ visible: false,
+ });
+ const shotCastCallback = () => {
+ if (this._shotButton.hover || this._castButton.hover)
+ this._shotCastTooltip.open();
+ else
+ this._shotCastTooltip.close();
+ };
+ this._shotButton.connect('notify::hover', shotCastCallback);
+ this._castButton.connect('notify::hover', shotCastCallback);
+ this.add_child(this._shotCastTooltip);
+
+ this._captureButton = new St.Button({ style_class: 'screenshot-ui-capture-button' });
+ this._captureButton.set_child(new St.Widget({
+ style_class: 'screenshot-ui-capture-button-circle',
+ }));
+ this._captureButton.connect('clicked',
+ this._onCaptureButtonClicked.bind(this));
+ this._bottomRowContainer.add_child(this._captureButton);
+
+ this._showPointerButtonContainer = new St.BoxLayout({
+ x_align: Clutter.ActorAlign.END,
+ x_expand: true,
+ });
+ this._bottomRowContainer.add_child(this._showPointerButtonContainer);
+
+ this._showPointerButton = new St.Button({
+ style_class: 'screenshot-ui-show-pointer-button',
+ icon_name: 'screenshot-ui-show-pointer-symbolic',
+ toggle_mode: true,
+ });
+ this._showPointerButtonContainer.add_child(this._showPointerButton);
+
+ this.add_child(new Tooltip(this._showPointerButton, {
+ text: _('Show Pointer'),
+ style_class: 'screenshot-ui-tooltip',
+ visible: false,
+ }));
+
+ this._showPointerButton.connect('notify::checked', () => {
+ const state = this._showPointerButton.checked;
+ this._cursor.visible = state;
+
+ const windows =
+ this._windowSelectors.flatMap(selector => selector.windows());
+ for (const window of windows)
+ window.setCursorVisible(state);
+ });
+ this._cursor.visible = false;
+
+ this._monitorBins = [];
+ this._windowSelectors = [];
+ this._rebuildMonitorBins();
+
+ Main.layoutManager.connect('monitors-changed', () => {
+ // Nope, not dealing with monitor changes.
+ this.close(true);
+ this._rebuildMonitorBins();
+ });
+
+ const uiModes =
+ Shell.ActionMode.ALL & ~Shell.ActionMode.LOGIN_SCREEN;
+ const restrictedModes =
+ uiModes &
+ ~(Shell.ActionMode.LOCK_SCREEN | Shell.ActionMode.UNLOCK_SCREEN);
+
+ Main.wm.addKeybinding(
+ 'show-screenshot-ui',
+ new Gio.Settings({ schema_id: 'org.gnome.shell.keybindings' }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ uiModes,
+ showScreenshotUI
+ );
+
+ Main.wm.addKeybinding(
+ 'show-screen-recording-ui',
+ new Gio.Settings({ schema_id: 'org.gnome.shell.keybindings' }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ restrictedModes,
+ showScreenRecordingUI
+ );
+
+ Main.wm.addKeybinding(
+ 'screenshot-window',
+ new Gio.Settings({ schema_id: 'org.gnome.shell.keybindings' }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT | Meta.KeyBindingFlags.PER_WINDOW,
+ restrictedModes,
+ async (_display, window, _binding) => {
+ try {
+ const actor = window.get_compositor_private();
+ const content = actor.paint_to_content(null);
+ const texture = content.get_texture();
+
+ await captureScreenshot(texture, null, 1, null);
+ } catch (e) {
+ logError(e, 'Error capturing screenshot');
+ }
+ }
+ );
+
+ Main.wm.addKeybinding(
+ 'screenshot',
+ new Gio.Settings({ schema_id: 'org.gnome.shell.keybindings' }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ uiModes,
+ async () => {
+ try {
+ const shooter = new Shell.Screenshot();
+ const [content] = await shooter.screenshot_stage_to_content();
+ const texture = content.get_texture();
+
+ await captureScreenshot(texture, null, 1, null);
+ } catch (e) {
+ logError(e, 'Error capturing screenshot');
+ }
+ }
+ );
+
+ Main.sessionMode.connect('updated',
+ () => this._sessionUpdated());
+ this._sessionUpdated();
+ }
+
+ _sessionUpdated() {
+ this.close(true);
+ this._castButton.reactive = Main.sessionMode.allowScreencast;
+ }
+
+ _syncWindowButtonSensitivity() {
+ const windows =
+ this._windowSelectors.flatMap(selector => selector.windows());
+
+ this._windowButton.reactive =
+ Main.sessionMode.hasWindows &&
+ windows.length > 0 &&
+ !this._castButton.checked;
+ }
+
+ _refreshButtonLayout() {
+ const buttonLayout = Meta.prefs_get_button_layout();
+
+ this._closeButton.remove_style_class_name('left');
+ this._closeButton.remove_style_class_name('right');
+
+ if (buttonLayout.left_buttons.includes(Meta.ButtonFunction.CLOSE)) {
+ this._closeButton.add_style_class_name('left');
+ this._closeButtonXAlignConstraint.factor = 0;
+ } else {
+ this._closeButton.add_style_class_name('right');
+ this._closeButtonXAlignConstraint.factor = 1;
+ }
+ }
+
+ _rebuildMonitorBins() {
+ for (const bin of this._monitorBins)
+ bin.destroy();
+
+ this._monitorBins = [];
+ this._windowSelectors = [];
+ this._screenSelectors = [];
+
+ for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
+ const bin = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ });
+ bin.add_constraint(new Layout.MonitorConstraint({ 'index': i }));
+ this.insert_child_below(bin, this._primaryMonitorBin);
+ this._monitorBins.push(bin);
+
+ const windowSelector = new UIWindowSelector(i, {
+ style_class: 'screenshot-ui-window-selector',
+ x_expand: true,
+ y_expand: true,
+ visible: this._windowButton.checked,
+ });
+ if (i === Main.layoutManager.primaryIndex)
+ windowSelector.add_style_pseudo_class('primary-monitor');
+
+ bin.add_child(windowSelector);
+ this._windowSelectors.push(windowSelector);
+
+ const screenSelector = new St.Button({
+ style_class: 'screenshot-ui-screen-selector',
+ x_expand: true,
+ y_expand: true,
+ visible: this._screenButton.checked,
+ reactive: true,
+ can_focus: true,
+ toggle_mode: true,
+ });
+ screenSelector.connect('key-focus-in', () => {
+ this.grab_key_focus();
+ screenSelector.checked = true;
+ });
+ bin.add_child(screenSelector);
+ this._screenSelectors.push(screenSelector);
+
+ screenSelector.connect('notify::checked', () => {
+ if (!screenSelector.checked)
+ return;
+
+ screenSelector.toggle_mode = false;
+
+ for (const otherSelector of this._screenSelectors) {
+ if (screenSelector === otherSelector)
+ continue;
+
+ otherSelector.toggle_mode = true;
+ otherSelector.checked = false;
+ }
+ });
+ }
+
+ if (Main.layoutManager.primaryIndex !== -1)
+ this._screenSelectors[Main.layoutManager.primaryIndex].checked = true;
+ }
+
+ async open(mode = UIMode.SCREENSHOT) {
+ if (this._openingCoroutineInProgress)
+ return;
+
+ if (this._screencastInProgress)
+ return;
+
+ if (mode === UIMode.SCREENCAST && !this._screencastSupported)
+ return;
+
+ this._castButton.checked = mode === UIMode.SCREENCAST;
+
+ if (!this.visible) {
+ // Screenshot UI is opening from completely closed state
+ // (rather than opening back from in process of closing).
+ for (const selector of this._windowSelectors)
+ selector.capture();
+
+ const windows =
+ this._windowSelectors.flatMap(selector => selector.windows());
+ for (const window of windows) {
+ window.connect('notify::checked', () => {
+ if (!window.checked)
+ return;
+
+ window.toggle_mode = false;
+
+ for (const otherWindow of windows) {
+ if (window === otherWindow)
+ continue;
+
+ otherWindow.toggle_mode = true;
+ otherWindow.checked = false;
+ }
+ });
+ }
+
+ this._syncWindowButtonSensitivity();
+ if (!this._windowButton.reactive)
+ this._selectionButton.checked = true;
+
+ this._shooter = new Shell.Screenshot();
+
+ this._openingCoroutineInProgress = true;
+ try {
+ const [content, scale, cursorContent, cursorPoint, cursorScale] =
+ await this._shooter.screenshot_stage_to_content();
+ this._stageScreenshot.set_content(content);
+ this._scale = scale;
+
+ if (cursorContent !== null) {
+ this._cursor.set_content(cursorContent);
+ this._cursor.set_position(cursorPoint.x, cursorPoint.y);
+
+ let [, w, h] = cursorContent.get_preferred_size();
+ w *= cursorScale;
+ h *= cursorScale;
+ this._cursor.set_size(w, h);
+
+ this._cursorScale = cursorScale;
+
+ for (const window of windows) {
+ window.addCursorTexture(cursorContent, cursorPoint, cursorScale);
+ window.setCursorVisible(this._showPointerButton.checked);
+ }
+ }
+
+ this._stageScreenshotContainer.show();
+ } catch (e) {
+ log(`Error capturing screenshot: ${e.message}`);
+ }
+ this._openingCoroutineInProgress = false;
+ }
+
+ // Get rid of any popup menus.
+ // We already have them captured on the screenshot anyway.
+ //
+ // This needs to happen before the grab below as closing menus will
+ // pop their grabs.
+ Main.layoutManager.emit('system-modal-opened');
+
+ const { screenshotUIGroup } = Main.layoutManager;
+ screenshotUIGroup.get_parent().set_child_above_sibling(
+ screenshotUIGroup, null);
+
+ const grabResult = this._grabHelper.grab({
+ actor: this,
+ onUngrab: () => this.close(),
+ });
+ if (!grabResult) {
+ this.close(true);
+ return;
+ }
+
+ this._refreshButtonLayout();
+
+ this.remove_all_transitions();
+ this.visible = true;
+ this.ease({
+ opacity: 255,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._stageScreenshotContainer.get_parent().remove_child(
+ this._stageScreenshotContainer);
+ this.insert_child_at_index(this._stageScreenshotContainer, 0);
+ },
+ });
+ }
+
+ _finishClosing() {
+ this.hide();
+
+ this._shooter = null;
+
+ // Switch back to screenshot mode.
+ this._shotButton.checked = true;
+
+ this._stageScreenshotContainer.get_parent().remove_child(
+ this._stageScreenshotContainer);
+ Main.layoutManager.screenshotUIGroup.insert_child_at_index(
+ this._stageScreenshotContainer, 0);
+ this._stageScreenshotContainer.hide();
+
+ this._stageScreenshot.set_content(null);
+ this._cursor.set_content(null);
+
+ this._areaSelector.reset();
+ for (const selector of this._windowSelectors)
+ selector.reset();
+ }
+
+ close(instantly = false) {
+ this._grabHelper.ungrab();
+
+ if (instantly) {
+ this._finishClosing();
+ return;
+ }
+
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 0,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: this._finishClosing.bind(this),
+ });
+ }
+
+ _onSelectionButtonToggled() {
+ if (this._selectionButton.checked) {
+ this._selectionButton.toggle_mode = false;
+ this._windowButton.checked = false;
+ this._screenButton.checked = false;
+
+ this._areaSelector.show();
+ this._areaSelector.remove_all_transitions();
+ this._areaSelector.reactive = true;
+ this._areaSelector.ease({
+ opacity: 255,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ } else {
+ this._selectionButton.toggle_mode = true;
+
+ this._areaSelector.stopDrag();
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+
+ this._areaSelector.remove_all_transitions();
+ this._areaSelector.reactive = false;
+ this._areaSelector.ease({
+ opacity: 0,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._areaSelector.hide(),
+ });
+ }
+ }
+
+ _onScreenButtonToggled() {
+ if (this._screenButton.checked) {
+ this._screenButton.toggle_mode = false;
+ this._selectionButton.checked = false;
+ this._windowButton.checked = false;
+
+ for (const selector of this._screenSelectors) {
+ selector.show();
+ selector.remove_all_transitions();
+ selector.ease({
+ opacity: 255,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ } else {
+ this._screenButton.toggle_mode = true;
+
+ for (const selector of this._screenSelectors) {
+ selector.remove_all_transitions();
+ selector.ease({
+ opacity: 0,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => selector.hide(),
+ });
+ }
+ }
+ }
+
+ _onWindowButtonToggled() {
+ if (this._windowButton.checked) {
+ this._windowButton.toggle_mode = false;
+ this._selectionButton.checked = false;
+ this._screenButton.checked = false;
+
+ for (const selector of this._windowSelectors) {
+ selector.show();
+ selector.remove_all_transitions();
+ selector.ease({
+ opacity: 255,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ } else {
+ this._windowButton.toggle_mode = true;
+
+ for (const selector of this._windowSelectors) {
+ selector.remove_all_transitions();
+ selector.ease({
+ opacity: 0,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => selector.hide(),
+ });
+ }
+ }
+ }
+
+ _onShotButtonToggled() {
+ if (this._shotButton.checked) {
+ this._shotButton.toggle_mode = false;
+
+ this._stageScreenshotContainer.show();
+ this._stageScreenshotContainer.remove_all_transitions();
+ this._stageScreenshotContainer.ease({
+ opacity: 255,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ } else {
+ this._shotButton.toggle_mode = true;
+ }
+ }
+
+ _onCastButtonToggled() {
+ if (this._castButton.checked) {
+ this._castButton.toggle_mode = false;
+
+ this._captureButton.add_style_pseudo_class('cast');
+
+ this._stageScreenshotContainer.remove_all_transitions();
+ this._stageScreenshotContainer.ease({
+ opacity: 0,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._stageScreenshotContainer.hide(),
+ });
+
+ // Screen recording doesn't support window selection yet.
+ if (this._windowButton.checked)
+ this._selectionButton.checked = true;
+
+ this._windowButton.reactive = false;
+ } else {
+ this._castButton.toggle_mode = true;
+
+ this._captureButton.remove_style_pseudo_class('cast');
+
+ this._syncWindowButtonSensitivity();
+ }
+ }
+
+ _getSelectedGeometry(rescale) {
+ let x, y, w, h;
+
+ if (this._selectionButton.checked) {
+ [x, y, w, h] = this._areaSelector.getGeometry();
+ } else if (this._screenButton.checked) {
+ const index =
+ this._screenSelectors.findIndex(screen => screen.checked);
+ const monitor = Main.layoutManager.monitors[index];
+
+ x = monitor.x;
+ y = monitor.y;
+ w = monitor.width;
+ h = monitor.height;
+ }
+
+ if (rescale) {
+ x *= this._scale;
+ y *= this._scale;
+ w *= this._scale;
+ h *= this._scale;
+ }
+
+ return [x, y, w, h];
+ }
+
+ _onCaptureButtonClicked() {
+ if (this._shotButton.checked) {
+ this._saveScreenshot();
+ this.close();
+ } else {
+ // Screencast closes the UI on its own.
+ this._startScreencast();
+ }
+ }
+
+ _saveScreenshot() {
+ if (this._selectionButton.checked || this._screenButton.checked) {
+ const content = this._stageScreenshot.get_content();
+ if (!content)
+ return; // Failed to capture the screenshot for some reason.
+
+ const texture = content.get_texture();
+ const geometry = this._getSelectedGeometry(true);
+
+ let cursorTexture = this._cursor.content?.get_texture();
+ if (!this._cursor.visible)
+ cursorTexture = null;
+
+ captureScreenshot(
+ texture, geometry, this._scale,
+ {
+ texture: cursorTexture ?? null,
+ x: this._cursor.x * this._scale,
+ y: this._cursor.y * this._scale,
+ scale: this._cursorScale,
+ }
+ ).catch(e => logError(e, 'Error capturing screenshot'));
+ } else if (this._windowButton.checked) {
+ const window =
+ this._windowSelectors.flatMap(selector => selector.windows())
+ .find(win => win.checked);
+ if (!window)
+ return;
+
+ const content = window.windowContent;
+ if (!content)
+ return;
+
+ const texture = content.get_texture();
+
+ let cursorTexture = window.getCursorTexture()?.get_texture();
+ if (!this._cursor.visible)
+ cursorTexture = null;
+
+ captureScreenshot(
+ texture,
+ null,
+ window.bufferScale,
+ {
+ texture: cursorTexture ?? null,
+ x: window.cursorPoint.x * window.bufferScale,
+ y: window.cursorPoint.y * window.bufferScale,
+ scale: this._cursorScale,
+ }
+ ).catch(e => logError(e, 'Error capturing screenshot'));
+ }
+ }
+
+ async _startScreencast() {
+ if (this._windowButton.checked)
+ return; // TODO
+
+ const [x, y, w, h] = this._getSelectedGeometry(false);
+ const drawCursor = this._cursor.visible;
+
+ // Set up the screencast indicator rect.
+ if (this._selectionButton.checked) {
+ this._screencastAreaIndicator.setSelectionRect(
+ ...this._areaSelector.getGeometry());
+ } else if (this._screenButton.checked) {
+ const index =
+ this._screenSelectors.findIndex(screen => screen.checked);
+ const monitor = Main.layoutManager.monitors[index];
+
+ this._screencastAreaIndicator.setSelectionRect(
+ monitor.x, monitor.y, monitor.width, monitor.height);
+ }
+
+ // Close instantly so the fade-out doesn't get recorded.
+ this.close(true);
+
+ // This is a bit awkward because creating a proxy synchronously hangs Shell.
+ let method =
+ this._screencastProxy.ScreencastAsync.bind(this._screencastProxy);
+ if (w !== -1) {
+ method = this._screencastProxy.ScreencastAreaAsync.bind(
+ this._screencastProxy, x, y, w, h);
+ }
+
+ // Set this before calling the method as the screen recording indicator
+ // will check it before the success callback fires.
+ this._setScreencastInProgress(true);
+
+ try {
+ const [success, path] = await method(
+ GLib.build_filenamev([
+ /* Translators: this is the folder where recorded
+ screencasts are stored. */
+ _('Screencasts'),
+ /* Translators: this is a filename used for screencast
+ * recording, where "%d" and "%t" date and time, e.g.
+ * "Screencast from 07-17-2013 10:00:46 PM.webm" */
+ /* xgettext:no-c-format */
+ _('Screencast from %d %t.webm'),
+ ]),
+ {'draw-cursor': new GLib.Variant('b', drawCursor)});
+ if (!success)
+ throw new Error();
+ this._screencastPath = path;
+ } catch (error) {
+ this._setScreencastInProgress(false);
+ const {message} = error;
+ if (message)
+ log(`Error starting screencast: ${message}`);
+ else
+ log('Error starting screencast');
+ }
+ }
+
+ async stopScreencast() {
+ if (!this._screencastInProgress)
+ return;
+
+ // Set this before calling the method as the screen recording indicator
+ // will check it before the success callback fires.
+ this._setScreencastInProgress(false);
+
+ try {
+ const [success] = await this._screencastProxy.StopScreencastAsync();
+ if (!success)
+ throw new Error();
+ } catch (error) {
+ const {message} = error;
+ if (message)
+ log(`Error stopping screencast: ${message}`);
+ else
+ log('Error stopping screencast');
+ return;
+ }
+
+ // Show a notification.
+ const file = Gio.file_new_for_path(this._screencastPath);
+
+ const source = new MessageTray.Source(
+ // Translators: notification source name.
+ _('Screenshot'),
+ 'screencast-recorded-symbolic'
+ );
+ const notification = new MessageTray.Notification(
+ source,
+ // Translators: notification title.
+ _('Screencast recorded'),
+ // Translators: notification body when a screencast was recorded.
+ _('Click here to view the video.')
+ );
+ // Translators: button on the screencast notification.
+ notification.addAction(_('Show in Files'), () => {
+ const app =
+ Gio.app_info_get_default_for_type('inode/directory', false);
+
+ if (app === null) {
+ // It may be null e.g. in a toolbox without nautilus.
+ log('Error showing in files: no default app set for inode/directory');
+ return;
+ }
+
+ app.launch([file], global.create_app_launch_context(0, -1));
+ });
+ notification.connect('activated', () => {
+ try {
+ Gio.app_info_launch_default_for_uri(
+ file.get_uri(), global.create_app_launch_context(0, -1));
+ } catch (err) {
+ logError(err, 'Error opening screencast');
+ }
+ });
+ notification.setTransient(true);
+
+ Main.messageTray.add(source);
+ source.showNotification(notification);
+ }
+
+ get screencast_in_progress() {
+ if (!('_screencastInProgress' in this))
+ return false;
+
+ return this._screencastInProgress;
+ }
+
+ _setScreencastInProgress(inProgress) {
+ if (this._screencastInProgress === inProgress)
+ return;
+
+ this._screencastInProgress = inProgress;
+ this.notify('screencast-in-progress');
+ }
+
+ vfunc_key_press_event(event) {
+ const symbol = event.keyval;
+ if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_space ||
+ ((event.modifier_state & Clutter.ModifierType.CONTROL_MASK) &&
+ (symbol === Clutter.KEY_c || symbol === Clutter.KEY_C))) {
+ this._onCaptureButtonClicked();
+ return Clutter.EVENT_STOP;
+ }
+
+ if (symbol === Clutter.KEY_s || symbol === Clutter.KEY_S) {
+ this._selectionButton.checked = true;
+ return Clutter.EVENT_STOP;
+ }
+
+ if (symbol === Clutter.KEY_c || symbol === Clutter.KEY_C) {
+ this._screenButton.checked = true;
+ return Clutter.EVENT_STOP;
+ }
+
+ if (this._windowButton.reactive &&
+ (symbol === Clutter.KEY_w || symbol === Clutter.KEY_W)) {
+ this._windowButton.checked = true;
+ return Clutter.EVENT_STOP;
+ }
+
+ if (symbol === Clutter.KEY_p || symbol === Clutter.KEY_P) {
+ this._showPointerButton.checked = !this._showPointerButton.checked;
+ return Clutter.EVENT_STOP;
+ }
+
+ if (this._castButton.reactive &&
+ (symbol === Clutter.KEY_v || symbol === Clutter.KEY_V)) {
+ this._castButton.checked = !this._castButton.checked;
+ return Clutter.EVENT_STOP;
+ }
+
+ if (symbol === Clutter.KEY_Left || symbol === Clutter.KEY_Right ||
+ symbol === Clutter.KEY_Up || symbol === Clutter.KEY_Down) {
+ let direction;
+ if (symbol === Clutter.KEY_Left)
+ direction = St.DirectionType.LEFT;
+ else if (symbol === Clutter.KEY_Right)
+ direction = St.DirectionType.RIGHT;
+ else if (symbol === Clutter.KEY_Up)
+ direction = St.DirectionType.UP;
+ else if (symbol === Clutter.KEY_Down)
+ direction = St.DirectionType.DOWN;
+
+ if (this._windowButton.checked) {
+ const window =
+ this._windowSelectors.flatMap(selector => selector.windows())
+ .find(win => win.checked) ?? null;
+ this.navigate_focus(window, direction, false);
+ } else if (this._screenButton.checked) {
+ const screen =
+ this._screenSelectors.find(selector => selector.checked) ?? null;
+ this.navigate_focus(screen, direction, false);
+ }
+
+ return Clutter.EVENT_STOP;
+ }
+
+ return super.vfunc_key_press_event(event);
+ }
+});
+
+/**
+ * Stores a PNG-encoded screenshot into the clipboard and a file, and shows a
+ * notification.
+ *
+ * @param {GLib.Bytes} bytes - The PNG-encoded screenshot.
+ * @param {GdkPixbuf.Pixbuf} pixbuf - The Pixbuf with the screenshot.
+ */
+function _storeScreenshot(bytes, pixbuf) {
+ // Store to the clipboard first in case storing to file fails.
+ const clipboard = St.Clipboard.get_default();
+ clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes);
+
+ const time = GLib.DateTime.new_now_local();
+
+ // This will be set in the first save to disk branch and then accessed
+ // in the second save to disk branch, so we need to declare it outside.
+ let file;
+
+ // The function is declared here rather than inside the condition to
+ // satisfy eslint.
+
+ /**
+ * Returns a filename suffix with an increasingly large index.
+ *
+ * @returns {Generator<string|*, void, *>} suffix string
+ */
+ function* suffixes() {
+ yield '';
+
+ for (let i = 1; ; i++)
+ yield `-${i}`;
+ }
+
+ const lockdownSettings =
+ new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
+ const disableSaveToDisk =
+ lockdownSettings.get_boolean('disable-save-to-disk');
+
+ if (!disableSaveToDisk) {
+ const dir = Gio.File.new_for_path(GLib.build_filenamev([
+ GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES) || GLib.get_home_dir(),
+ // Translators: name of the folder under ~/Pictures for screenshots.
+ _('Screenshots'),
+ ]));
+
+ try {
+ dir.make_directory_with_parents(null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+ throw e;
+ }
+
+ const timestamp = time.format('%Y-%m-%d %H-%M-%S');
+ // Translators: this is the name of the file that the screenshot is
+ // saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+ const name = _('Screenshot from %s').format(timestamp);
+
+ // If the target file already exists, try appending a suffix with an
+ // increasing number to it.
+ for (const suffix of suffixes()) {
+ file = Gio.File.new_for_path(GLib.build_filenamev([
+ dir.get_path(), `${name}${suffix}.png`,
+ ]));
+
+ try {
+ const stream = file.create(Gio.FileCreateFlags.NONE, null);
+ stream.write_bytes(bytes, null);
+ break;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+ throw e;
+ }
+ }
+
+ // Add it to recent files.
+ Gtk.RecentManager.get_default().add_item(file.get_uri());
+ }
+
+ // Create a St.ImageContent icon for the notification. We want
+ // St.ImageContent specifically because it preserves the aspect ratio when
+ // shown in a notification.
+ const pixels = pixbuf.read_pixel_bytes();
+ const content =
+ St.ImageContent.new_with_preferred_size(pixbuf.width, pixbuf.height);
+ content.set_bytes(
+ pixels,
+ Cogl.PixelFormat.RGBA_8888,
+ pixbuf.width,
+ pixbuf.height,
+ pixbuf.rowstride
+ );
+
+ // Show a notification.
+ const source = new MessageTray.Source(
+ // Translators: notification source name.
+ _('Screenshot'),
+ 'screenshot-recorded-symbolic'
+ );
+ const notification = new MessageTray.Notification(
+ source,
+ // Translators: notification title.
+ _('Screenshot captured'),
+ // Translators: notification body when a screenshot was captured.
+ _('You can paste the image from the clipboard.'),
+ { datetime: time, gicon: content }
+ );
+
+ if (!disableSaveToDisk) {
+ // Translators: button on the screenshot notification.
+ notification.addAction(_('Show in Files'), () => {
+ const app =
+ Gio.app_info_get_default_for_type('inode/directory', false);
+
+ if (app === null) {
+ // It may be null e.g. in a toolbox without nautilus.
+ log('Error showing in files: no default app set for inode/directory');
+ return;
+ }
+
+ app.launch([file], global.create_app_launch_context(0, -1));
+ });
+ notification.connect('activated', () => {
+ try {
+ Gio.app_info_launch_default_for_uri(
+ file.get_uri(), global.create_app_launch_context(0, -1));
+ } catch (err) {
+ logError(err, 'Error opening screenshot');
+ }
+ });
+ }
+
+ notification.setTransient(true);
+ Main.messageTray.add(source);
+ source.showNotification(notification);
+}
+
+/**
+ * Captures a screenshot from a texture, given a region, scale and optional
+ * cursor data.
+ *
+ * @param {Cogl.Texture} texture - The texture to take the screenshot from.
+ * @param {number[4]} [geometry] - The region to use: x, y, width and height.
+ * @param {number} scale - The texture scale.
+ * @param {Object} [cursor] - Cursor data to include in the screenshot.
+ * @param {Cogl.Texture} cursor.texture - The cursor texture.
+ * @param {number} cursor.x - The cursor x coordinate.
+ * @param {number} cursor.y - The cursor y coordinate.
+ * @param {number} cursor.scale - The cursor texture scale.
+ */
+async function captureScreenshot(texture, geometry, scale, cursor) {
+ const stream = Gio.MemoryOutputStream.new_resizable();
+ const [x, y, w, h] = geometry ?? [0, 0, -1, -1];
+ if (cursor === null)
+ cursor = { texture: null, x: 0, y: 0, scale: 1 };
+
+ global.display.get_sound_player().play_from_theme(
+ 'screen-capture', _('Screenshot taken'), null);
+
+ const pixbuf = await Shell.Screenshot.composite_to_stream(
+ texture,
+ x, y, w, h,
+ scale,
+ cursor.texture, cursor.x, cursor.y, cursor.scale,
+ stream
+ );
+
+ stream.close(null);
+ _storeScreenshot(stream.steal_as_bytes(), pixbuf);
+}
+
+/**
+ * Shows the screenshot UI.
+ */
+function showScreenshotUI() {
+ Main.screenshotUI.open().catch(err => {
+ logError(err, 'Error opening the screenshot UI');
+ });
+}
+
+/**
+ * Shows the screen recording UI.
+ */
+function showScreenRecordingUI() {
+ Main.screenshotUI.open(UIMode.SCREENCAST).catch(err => {
+ logError(err, 'Error opening the screenshot UI');
+ });
+}
+
+var ScreenshotService = class {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
+
+ this._screenShooter = new Map();
+ this._senderChecker = new DBusSenderChecker([
+ 'org.gnome.SettingsDaemon.MediaKeys',
+ 'org.freedesktop.impl.portal.desktop.gtk',
+ 'org.freedesktop.impl.portal.desktop.gnome',
+ 'org.gnome.Screenshot',
+ ]);
+
+ this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
+
+ Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
+ }
+
+ async _createScreenshot(invocation, needsDisk = true, restrictCallers = true) {
+ let lockedDown = false;
+ if (needsDisk)
+ lockedDown = this._lockdownSettings.get_boolean('disable-save-to-disk');
+
+ let sender = invocation.get_sender();
+ if (this._screenShooter.has(sender)) {
+ invocation.return_error_literal(
+ Gio.IOErrorEnum, Gio.IOErrorEnum.BUSY,
+ 'There is an ongoing operation for this sender');
+ return null;
+ } else if (lockedDown) {
+ invocation.return_error_literal(
+ Gio.IOErrorEnum, Gio.IOErrorEnum.PERMISSION_DENIED,
+ 'Saving to disk is disabled');
+ return null;
+ } else if (restrictCallers) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return null;
+ }
+ }
+
+ let shooter = new Shell.Screenshot();
+ shooter._watchNameId =
+ Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
+ this._onNameVanished.bind(this));
+
+ this._screenShooter.set(sender, shooter);
+
+ return shooter;
+ }
+
+ _onNameVanished(connection, name) {
+ this._removeShooterForSender(name);
+ }
+
+ _removeShooterForSender(sender) {
+ let shooter = this._screenShooter.get(sender);
+ if (!shooter)
+ return;
+
+ Gio.bus_unwatch_name(shooter._watchNameId);
+ this._screenShooter.delete(sender);
+ }
+
+ _checkArea(x, y, width, height) {
+ return x >= 0 && y >= 0 &&
+ width > 0 && height > 0 &&
+ x + width <= global.screen_width &&
+ y + height <= global.screen_height;
+ }
+
+ *_resolveRelativeFilename(filename) {
+ filename = filename.replace(/\.png$/, '');
+
+ let path = [
+ GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES),
+ GLib.get_home_dir(),
+ ].find(p => p && GLib.file_test(p, GLib.FileTest.EXISTS));
+
+ if (!path)
+ return null;
+
+ yield Gio.File.new_for_path(
+ GLib.build_filenamev([path, `${filename}.png`]));
+
+ for (let idx = 1; ; idx++) {
+ yield Gio.File.new_for_path(
+ GLib.build_filenamev([path, `${filename}-${idx}.png`]));
+ }
+ }
+
+ _createStream(filename, invocation) {
+ if (filename == '')
+ return [Gio.MemoryOutputStream.new_resizable(), null];
+
+ if (GLib.path_is_absolute(filename)) {
+ try {
+ let file = Gio.File.new_for_path(filename);
+ let stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
+ return [stream, file];
+ } catch (e) {
+ invocation.return_gerror(e);
+ this._removeShooterForSender(invocation.get_sender());
+ return [null, null];
+ }
+ }
+
+ let err;
+ for (let file of this._resolveRelativeFilename(filename)) {
+ try {
+ let stream = file.create(Gio.FileCreateFlags.NONE, null);
+ return [stream, file];
+ } catch (e) {
+ err = e;
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+ break;
+ }
+ }
+
+ invocation.return_gerror(err);
+ this._removeShooterForSender(invocation.get_sender());
+ return [null, null];
+ }
+
+ _flashAsync(shooter) {
+ return new Promise((resolve, _reject) => {
+ shooter.connect('screenshot_taken', (s, area) => {
+ const flashspot = new Flashspot(area);
+ flashspot.fire(resolve);
+
+ global.display.get_sound_player().play_from_theme(
+ 'screen-capture', _('Screenshot taken'), null);
+ });
+ });
+ }
+
+ _onScreenshotComplete(stream, file, invocation) {
+ stream.close(null);
+
+ let filenameUsed = '';
+ if (file) {
+ filenameUsed = file.get_path();
+ } else {
+ let bytes = stream.steal_as_bytes();
+ let clipboard = St.Clipboard.get_default();
+ clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes);
+ }
+
+ let retval = GLib.Variant.new('(bs)', [true, filenameUsed]);
+ invocation.return_value(retval);
+ }
+
+ _scaleArea(x, y, width, height) {
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ x *= scaleFactor;
+ y *= scaleFactor;
+ width *= scaleFactor;
+ height *= scaleFactor;
+ return [x, y, width, height];
+ }
+
+ _unscaleArea(x, y, width, height) {
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ x /= scaleFactor;
+ y /= scaleFactor;
+ width /= scaleFactor;
+ height /= scaleFactor;
+ return [x, y, width, height];
+ }
+
+ async ScreenshotAreaAsync(params, invocation) {
+ let [x, y, width, height, flash, filename] = params;
+ [x, y, width, height] = this._scaleArea(x, y, width, height);
+ if (!this._checkArea(x, y, width, height)) {
+ invocation.return_error_literal(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.CANCELLED,
+ "Invalid params");
+ return;
+ }
+ let screenshot = await this._createScreenshot(invocation);
+ if (!screenshot)
+ return;
+
+ let [stream, file] = this._createStream(filename, invocation);
+ if (!stream)
+ return;
+
+ try {
+ await Promise.all([
+ flash ? this._flashAsync(screenshot) : null,
+ screenshot.screenshot_area(x, y, width, height, stream),
+ ]);
+ this._onScreenshotComplete(stream, file, invocation);
+ } catch (e) {
+ invocation.return_value(new GLib.Variant('(bs)', [false, '']));
+ } finally {
+ this._removeShooterForSender(invocation.get_sender());
+ }
+ }
+
+ async ScreenshotWindowAsync(params, invocation) {
+ let [includeFrame, includeCursor, flash, filename] = params;
+ let screenshot = await this._createScreenshot(invocation);
+ if (!screenshot)
+ return;
+
+ let [stream, file] = this._createStream(filename, invocation);
+ if (!stream)
+ return;
+
+ try {
+ await Promise.all([
+ flash ? this._flashAsync(screenshot) : null,
+ screenshot.screenshot_window(includeFrame, includeCursor, stream),
+ ]);
+ this._onScreenshotComplete(stream, file, invocation);
+ } catch (e) {
+ invocation.return_value(new GLib.Variant('(bs)', [false, '']));
+ } finally {
+ this._removeShooterForSender(invocation.get_sender());
+ }
+ }
+
+ async ScreenshotAsync(params, invocation) {
+ let [includeCursor, flash, filename] = params;
+ let screenshot = await this._createScreenshot(invocation);
+ if (!screenshot)
+ return;
+
+ let [stream, file] = this._createStream(filename, invocation);
+ if (!stream)
+ return;
+
+ try {
+ await Promise.all([
+ flash ? this._flashAsync(screenshot) : null,
+ screenshot.screenshot(includeCursor, stream),
+ ]);
+ this._onScreenshotComplete(stream, file, invocation);
+ } catch (e) {
+ invocation.return_value(new GLib.Variant('(bs)', [false, '']));
+ } finally {
+ this._removeShooterForSender(invocation.get_sender());
+ }
+ }
+
+ async SelectAreaAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let selectArea = new SelectArea();
+ try {
+ let areaRectangle = await selectArea.selectAsync();
+ let retRectangle = this._unscaleArea(
+ areaRectangle.x, areaRectangle.y,
+ areaRectangle.width, areaRectangle.height);
+ invocation.return_value(GLib.Variant.new('(iiii)', retRectangle));
+ } catch (e) {
+ invocation.return_error_literal(
+ Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
+ 'Operation was cancelled');
+ }
+ }
+
+ async FlashAreaAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [x, y, width, height] = params;
+ [x, y, width, height] = this._scaleArea(x, y, width, height);
+ if (!this._checkArea(x, y, width, height)) {
+ invocation.return_error_literal(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.CANCELLED,
+ "Invalid params");
+ return;
+ }
+ let flashspot = new Flashspot({ x, y, width, height });
+ flashspot.fire();
+ invocation.return_value(null);
+ }
+
+ async PickColorAsync(params, invocation) {
+ const screenshot = await this._createScreenshot(invocation, false, false);
+ if (!screenshot)
+ return;
+
+ const pickPixel = new PickPixel(screenshot);
+ try {
+ const color = await pickPixel.pickAsync();
+ const { red, green, blue } = color;
+ const retval = GLib.Variant.new('(a{sv})', [{
+ color: GLib.Variant.new('(ddd)', [
+ red / 255.0,
+ green / 255.0,
+ blue / 255.0,
+ ]),
+ }]);
+ invocation.return_value(retval);
+ } catch (e) {
+ invocation.return_error_literal(
+ Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
+ 'Operation was cancelled');
+ } finally {
+ this._removeShooterForSender(invocation.get_sender());
+ }
+ }
+};
+
+var SelectArea = GObject.registerClass(
+class SelectArea extends St.Widget {
+ _init() {
+ this._startX = -1;
+ this._startY = -1;
+ this._lastX = 0;
+ this._lastY = 0;
+ this._result = null;
+
+ super._init({
+ visible: false,
+ reactive: true,
+ x: 0,
+ y: 0,
+ });
+ Main.uiGroup.add_actor(this);
+
+ this._grabHelper = new GrabHelper.GrabHelper(this);
+
+ const constraint = new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ });
+ this.add_constraint(constraint);
+
+ this._rubberband = new St.Widget({
+ style_class: 'select-area-rubberband',
+ visible: false,
+ });
+ this.add_actor(this._rubberband);
+ }
+
+ async selectAsync() {
+ global.display.set_cursor(Meta.Cursor.CROSSHAIR);
+ Main.uiGroup.set_child_above_sibling(this, null);
+ this.show();
+
+ try {
+ await this._grabHelper.grabAsync({ actor: this });
+ } finally {
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+
+ GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this.destroy();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ return this._result;
+ }
+
+ _getGeometry() {
+ return new Meta.Rectangle({
+ x: Math.min(this._startX, this._lastX),
+ y: Math.min(this._startY, this._lastY),
+ width: Math.abs(this._startX - this._lastX) + 1,
+ height: Math.abs(this._startY - this._lastY) + 1,
+ });
+ }
+
+ vfunc_motion_event(motionEvent) {
+ if (this._startX == -1 || this._startY == -1 || this._result)
+ return Clutter.EVENT_PROPAGATE;
+
+ [this._lastX, this._lastY] = [motionEvent.x, motionEvent.y];
+ this._lastX = Math.floor(this._lastX);
+ this._lastY = Math.floor(this._lastY);
+ let geometry = this._getGeometry();
+
+ this._rubberband.set_position(geometry.x, geometry.y);
+ this._rubberband.set_size(geometry.width, geometry.height);
+ this._rubberband.show();
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_button_press_event(buttonEvent) {
+ if (this._result)
+ return Clutter.EVENT_PROPAGATE;
+
+ [this._startX, this._startY] = [buttonEvent.x, buttonEvent.y];
+ this._startX = Math.floor(this._startX);
+ this._startY = Math.floor(this._startY);
+ this._rubberband.set_position(this._startX, this._startY);
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_button_release_event() {
+ if (this._startX === -1 || this._startY === -1 || this._result)
+ return Clutter.EVENT_PROPAGATE;
+
+ this._result = this._getGeometry();
+ this.ease({
+ opacity: 0,
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._grabHelper.ungrab(),
+ });
+ return Clutter.EVENT_PROPAGATE;
+ }
+});
+
+var RecolorEffect = GObject.registerClass({
+ Properties: {
+ color: GObject.ParamSpec.boxed(
+ 'color', 'color', 'replacement color',
+ GObject.ParamFlags.WRITABLE,
+ Clutter.Color.$gtype),
+ chroma: GObject.ParamSpec.boxed(
+ 'chroma', 'chroma', 'color to replace',
+ GObject.ParamFlags.WRITABLE,
+ Clutter.Color.$gtype),
+ threshold: GObject.ParamSpec.float(
+ 'threshold', 'threshold', 'threshold',
+ GObject.ParamFlags.WRITABLE,
+ 0.0, 1.0, 0.0),
+ smoothing: GObject.ParamSpec.float(
+ 'smoothing', 'smoothing', 'smoothing',
+ GObject.ParamFlags.WRITABLE,
+ 0.0, 1.0, 0.0),
+ },
+}, class RecolorEffect extends Shell.GLSLEffect {
+ _init(params) {
+ this._color = new Clutter.Color();
+ this._chroma = new Clutter.Color();
+ this._threshold = 0;
+ this._smoothing = 0;
+
+ this._colorLocation = null;
+ this._chromaLocation = null;
+ this._thresholdLocation = null;
+ this._smoothingLocation = null;
+
+ super._init(params);
+
+ this._colorLocation = this.get_uniform_location('recolor_color');
+ this._chromaLocation = this.get_uniform_location('chroma_color');
+ this._thresholdLocation = this.get_uniform_location('threshold');
+ this._smoothingLocation = this.get_uniform_location('smoothing');
+
+ this._updateColorUniform(this._colorLocation, this._color);
+ this._updateColorUniform(this._chromaLocation, this._chroma);
+ this._updateFloatUniform(this._thresholdLocation, this._threshold);
+ this._updateFloatUniform(this._smoothingLocation, this._smoothing);
+ }
+
+ _updateColorUniform(location, color) {
+ if (!location)
+ return;
+
+ this.set_uniform_float(location,
+ 3, [color.red / 255, color.green / 255, color.blue / 255]);
+ this.queue_repaint();
+ }
+
+ _updateFloatUniform(location, value) {
+ if (!location)
+ return;
+
+ this.set_uniform_float(location, 1, [value]);
+ this.queue_repaint();
+ }
+
+ set color(c) {
+ if (this._color.equal(c))
+ return;
+
+ this._color = c;
+ this.notify('color');
+
+ this._updateColorUniform(this._colorLocation, this._color);
+ }
+
+ set chroma(c) {
+ if (this._chroma.equal(c))
+ return;
+
+ this._chroma = c;
+ this.notify('chroma');
+
+ this._updateColorUniform(this._chromaLocation, this._chroma);
+ }
+
+ set threshold(value) {
+ if (this._threshold === value)
+ return;
+
+ this._threshold = value;
+ this.notify('threshold');
+
+ this._updateFloatUniform(this._thresholdLocation, this._threshold);
+ }
+
+ set smoothing(value) {
+ if (this._smoothing === value)
+ return;
+
+ this._smoothing = value;
+ this.notify('smoothing');
+
+ this._updateFloatUniform(this._smoothingLocation, this._smoothing);
+ }
+
+ vfunc_build_pipeline() {
+ // Conversion parameters from https://en.wikipedia.org/wiki/YCbCr
+ const decl = `
+ vec3 rgb2yCrCb(vec3 c) { \n
+ float y = 0.299 * c.r + 0.587 * c.g + 0.114 * c.b; \n
+ float cr = 0.7133 * (c.r - y); \n
+ float cb = 0.5643 * (c.b - y); \n
+ return vec3(y, cr, cb); \n
+ } \n
+ \n
+ uniform vec3 chroma_color; \n
+ uniform vec3 recolor_color; \n
+ uniform float threshold; \n
+ uniform float smoothing; \n`;
+ const src = `
+ vec3 mask = rgb2yCrCb(chroma_color.rgb); \n
+ vec3 yCrCb = rgb2yCrCb(cogl_color_out.rgb); \n
+ float blend = \n
+ smoothstep(threshold, \n
+ threshold + smoothing, \n
+ distance(yCrCb.gb, mask.gb)); \n
+ cogl_color_out.rgb = \n
+ mix(recolor_color, cogl_color_out.rgb, blend); \n`;
+
+ this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, decl, src, false);
+ }
+});
+
+var PickPixel = GObject.registerClass(
+class PickPixel extends St.Widget {
+ _init(screenshot) {
+ super._init({ visible: false, reactive: true });
+
+ this._screenshot = screenshot;
+
+ this._result = null;
+ this._color = null;
+ this._inPick = false;
+
+ Main.uiGroup.add_actor(this);
+
+ this._grabHelper = new GrabHelper.GrabHelper(this);
+
+ const constraint = new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ });
+ this.add_constraint(constraint);
+
+ const action = new Clutter.ClickAction();
+ action.connect('clicked', async () => {
+ await this._pickColor(...action.get_coords());
+ this._result = this._color;
+ this._grabHelper.ungrab();
+ });
+ this.add_action(action);
+
+ this._recolorEffect = new RecolorEffect({
+ chroma: new Clutter.Color({
+ red: 80,
+ green: 219,
+ blue: 181,
+ }),
+ threshold: 0.04,
+ smoothing: 0.07,
+ });
+ this._previewCursor = new St.Icon({
+ icon_name: 'color-pick',
+ icon_size: Meta.prefs_get_cursor_size(),
+ effect: this._recolorEffect,
+ visible: false,
+ });
+ Main.uiGroup.add_actor(this._previewCursor);
+ }
+
+ async pickAsync() {
+ global.display.set_cursor(Meta.Cursor.BLANK);
+ Main.uiGroup.set_child_above_sibling(this, null);
+ this.show();
+
+ this._pickColor(...global.get_pointer());
+
+ try {
+ await this._grabHelper.grabAsync({ actor: this });
+ } finally {
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ this._previewCursor.destroy();
+
+ GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this.destroy();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ return this._result;
+ }
+
+ async _pickColor(x, y) {
+ if (this._inPick)
+ return;
+
+ this._inPick = true;
+ this._previewCursor.set_position(x, y);
+ [this._color] = await this._screenshot.pick_color(x, y);
+ this._inPick = false;
+
+ if (!this._color)
+ return;
+
+ this._recolorEffect.color = this._color;
+ this._previewCursor.show();
+ }
+
+ vfunc_motion_event(motionEvent) {
+ const { x, y } = motionEvent;
+ this._pickColor(x, y);
+ return Clutter.EVENT_PROPAGATE;
+ }
+});
+
+var FLASHSPOT_ANIMATION_OUT_TIME = 500; // milliseconds
+
+var Flashspot = GObject.registerClass(
+class Flashspot extends Lightbox.Lightbox {
+ _init(area) {
+ super._init(Main.uiGroup, {
+ inhibitEvents: true,
+ width: area.width,
+ height: area.height,
+ });
+ this.style_class = 'flashspot';
+ this.set_position(area.x, area.y);
+ }
+
+ fire(doneCallback) {
+ this.set({ visible: true, opacity: 255 });
+ this.ease({
+ opacity: 0,
+ duration: FLASHSPOT_ANIMATION_OUT_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ if (doneCallback)
+ doneCallback();
+ this.destroy();
+ },
+ });
+ }
+});
diff --git a/js/ui/scripting.js b/js/ui/scripting.js
new file mode 100644
index 0000000..a4d80d7
--- /dev/null
+++ b/js/ui/scripting.js
@@ -0,0 +1,340 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported sleep, waitLeisure, createTestWindow, waitTestWindows,
+ destroyTestWindows, defineScriptEvent, scriptEvent,
+ collectStatistics, runPerfScript */
+
+const { Gio, GLib, Meta, Shell } = imports.gi;
+
+const Config = imports.misc.config;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+const Util = imports.misc.util;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+// This module provides functionality for driving the shell user interface
+// in an automated fashion. The primary current use case for this is
+// automated performance testing (see runPerfScript()), but it could
+// be applied to other forms of automation, such as testing for
+// correctness as well.
+//
+// When scripting an automated test we want to make a series of calls
+// in a linear fashion, but we also want to be able to let the main
+// loop run so actions can finish. For this reason we write the script
+// as an async function that uses await when it wants to let the main
+// loop run.
+//
+// await Scripting.sleep(1000);
+// main.overview.show();
+// await Scripting.waitLeisure();
+//
+
+/**
+ * sleep:
+ * @param {number} milliseconds - number of milliseconds to wait
+ * @returns {Promise} that resolves after @milliseconds ms
+ *
+ * Used within an automation script to pause the the execution of the
+ * current script for the specified amount of time. Use as
+ * 'yield Scripting.sleep(500);'
+ */
+function sleep(milliseconds) {
+ return new Promise(resolve => {
+ let id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, milliseconds, () => {
+ resolve();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] sleep');
+ });
+}
+
+/**
+ * waitLeisure:
+ * @returns {Promise} that resolves when the shell is idle
+ *
+ * Used within an automation script to pause the the execution of the
+ * current script until the shell is completely idle. Use as
+ * 'yield Scripting.waitLeisure();'
+ */
+function waitLeisure() {
+ return new Promise(resolve => {
+ global.run_at_leisure(resolve);
+ });
+}
+
+const PerfHelperIface = loadInterfaceXML('org.gnome.Shell.PerfHelper');
+var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
+function PerfHelper() {
+ return new PerfHelperProxy(Gio.DBus.session, 'org.gnome.Shell.PerfHelper', '/org/gnome/Shell/PerfHelper');
+}
+
+let _perfHelper = null;
+function _getPerfHelper() {
+ if (_perfHelper == null)
+ _perfHelper = new PerfHelper();
+
+ return _perfHelper;
+}
+
+function _spawnPerfHelper() {
+ let path = Config.LIBEXECDIR;
+ let command = `${path}/gnome-shell-perf-helper`;
+ Util.trySpawnCommandLine(command);
+}
+
+/**
+ * createTestWindow:
+ * @param {Object} params: options for window creation.
+ * {number} [params.width=640] - width of window, in pixels
+ * {number} [params.height=480] - height of window, in pixels
+ * {bool} [params.alpha=false] - whether the window should have an alpha channel
+ * {bool} [params.maximized=false] - whether the window should be created maximized
+ * {bool} [params.redraws=false] - whether the window should continually redraw itself
+ * @returns {Promise}
+ *
+ * Creates a window using gnome-shell-perf-helper for testing purposes.
+ * While this function can be used with yield in an automation
+ * script to pause until the D-Bus call to the helper process returns,
+ * because of the normal X asynchronous mapping process, to actually wait
+ * until the window has been mapped and exposed, use waitTestWindows().
+ */
+function createTestWindow(params) {
+ params = Params.parse(params, {
+ width: 640,
+ height: 480,
+ alpha: false,
+ maximized: false,
+ redraws: false,
+ });
+
+ let perfHelper = _getPerfHelper();
+ perfHelper.CreateWindowAsync(
+ params.width, params.height,
+ params.alpha, params.maximized, params.redraws).catch(logError);
+}
+
+/**
+ * waitTestWindows:
+ * @returns {Promise}
+ *
+ * Used within an automation script to pause until all windows previously
+ * created with createTestWindow have been mapped and exposed.
+ */
+function waitTestWindows() {
+ let perfHelper = _getPerfHelper();
+ perfHelper.WaitWindowsAsync().catch(logError);
+}
+
+/**
+ * destroyTestWindows:
+ * @returns {Promise}
+ *
+ * Destroys all windows previously created with createTestWindow().
+ * While this function can be used with yield in an automation
+ * script to pause until the D-Bus call to the helper process returns,
+ * this doesn't guarantee that Mutter has actually finished the destroy
+ * process because of normal X asynchronicity.
+ */
+function destroyTestWindows() {
+ let perfHelper = _getPerfHelper();
+ perfHelper.DestroyWindowsAsync().catch(logError);
+}
+
+/**
+ * defineScriptEvent
+ * @param {string} name: The event will be called script.<name>
+ * @param {string} description: Short human-readable description of the event
+ *
+ * Convenience function to define a zero-argument performance event
+ * within the 'script' namespace that is reserved for events defined locally
+ * within a performance automation script
+ */
+function defineScriptEvent(name, description) {
+ Shell.PerfLog.get_default().define_event(`script.${name}`,
+ description,
+ "");
+}
+
+/**
+ * scriptEvent
+ * @param {string} name: Name registered with defineScriptEvent()
+ *
+ * Convenience function to record a script-local performance event
+ * previously defined with defineScriptEvent
+ */
+function scriptEvent(name) {
+ Shell.PerfLog.get_default().event(`script.${name}`);
+}
+
+/**
+ * collectStatistics
+ *
+ * Convenience function to trigger statistics collection
+ */
+function collectStatistics() {
+ Shell.PerfLog.get_default().collect_statistics();
+}
+
+function _collect(scriptModule, outputFile) {
+ let eventHandlers = {};
+
+ for (let f in scriptModule) {
+ let m = /([A-Za-z]+)_([A-Za-z]+)/.exec(f);
+ if (m)
+ eventHandlers[`${m[1]}.${m[2]}`] = scriptModule[f];
+ }
+
+ Shell.PerfLog.get_default().replay(
+ (time, eventName, signature, arg) => {
+ if (eventName in eventHandlers)
+ eventHandlers[eventName](time, arg);
+ });
+
+ if ('finish' in scriptModule)
+ scriptModule.finish();
+
+ if (outputFile) {
+ let f = Gio.file_new_for_path(outputFile);
+ let raw = f.replace(null, false,
+ Gio.FileCreateFlags.NONE,
+ null);
+ let out = Gio.BufferedOutputStream.new_sized(raw, 4096);
+ Shell.write_string_to_stream(out, "{\n");
+
+ Shell.write_string_to_stream(out, '"events":\n');
+ Shell.PerfLog.get_default().dump_events(out);
+
+ let monitors = Main.layoutManager.monitors;
+ let primary = Main.layoutManager.primaryIndex;
+ Shell.write_string_to_stream(out, ',\n"monitors":\n[');
+ for (let i = 0; i < monitors.length; i++) {
+ let monitor = monitors[i];
+ if (i != 0)
+ Shell.write_string_to_stream(out, ', ');
+ const prefix = i === primary ? '*' : '';
+ Shell.write_string_to_stream(out,
+ `"${prefix}${monitor.width}x${monitor.height}+${monitor.x}+${monitor.y}"`);
+ }
+ Shell.write_string_to_stream(out, ' ]');
+
+ Shell.write_string_to_stream(out, ',\n"metrics":\n[ ');
+ let first = true;
+ for (let name in scriptModule.METRICS) {
+ let metric = scriptModule.METRICS[name];
+ // Extra checks here because JSON.stringify generates
+ // invalid JSON for undefined values
+ if (metric.description == null) {
+ log(`Error: No description found for metric ${name}`);
+ continue;
+ }
+ if (metric.units == null) {
+ log(`Error: No units found for metric ${name}`);
+ continue;
+ }
+ if (metric.value == null) {
+ log(`Error: No value found for metric ${name}`);
+ continue;
+ }
+
+ if (!first)
+ Shell.write_string_to_stream(out, ',\n ');
+ first = false;
+
+ Shell.write_string_to_stream(out,
+ `{ "name": ${JSON.stringify(name)},\n` +
+ ` "description": ${JSON.stringify(metric.description)},\n` +
+ ` "units": ${JSON.stringify(metric.units)},\n` +
+ ` "value": ${JSON.stringify(metric.value)} }`);
+ }
+ Shell.write_string_to_stream(out, ' ]');
+
+ Shell.write_string_to_stream(out, ',\n"log":\n');
+ Shell.PerfLog.get_default().dump_log(out);
+
+ Shell.write_string_to_stream(out, '\n}\n');
+ out.close(null);
+ } else {
+ let metrics = [];
+ for (let metric in scriptModule.METRICS)
+ metrics.push(metric);
+
+ metrics.sort();
+
+ print('------------------------------------------------------------');
+ for (let i = 0; i < metrics.length; i++) {
+ let metric = metrics[i];
+ print(`# ${scriptModule.METRICS[metric].description}`);
+ print(`${metric}: ${scriptModule.METRICS[metric].value}${scriptModule.METRICS[metric].units}`);
+ }
+ print('------------------------------------------------------------');
+ }
+}
+
+async function _runPerfScript(scriptModule, outputFile) {
+ try {
+ await scriptModule.run();
+ } catch (err) {
+ log(`Script failed: ${err}\n${err.stack}`);
+ Meta.exit(Meta.ExitCode.ERROR);
+ }
+
+ try {
+ _collect(scriptModule, outputFile);
+ } catch (err) {
+ log(`Script failed: ${err}\n${err.stack}`);
+ Meta.exit(Meta.ExitCode.ERROR);
+ }
+ Meta.exit(Meta.ExitCode.SUCCESS);
+}
+
+/**
+ * runPerfScript
+ * @param {Object} scriptModule: module object with run and finish
+ * functions and event handlers
+ * @param {string} outputFile: path to write output to
+ *
+ * Runs a script for automated collection of performance data. The
+ * script is defined as a Javascript module with specified contents.
+ *
+ * First the run() function within the module will be called as a
+ * generator to automate a series of actions. These actions will
+ * trigger performance events and the script can also record its
+ * own performance events.
+ *
+ * Then the recorded event log is replayed using handler functions
+ * within the module. The handler for the event 'foo.bar' is called
+ * foo_bar().
+ *
+ * Finally if the module has a function called finish(), that will
+ * be called.
+ *
+ * The event handler and finish functions are expected to fill in
+ * metrics to an object within the module called METRICS. Each
+ * property of this object represents an individual metric. The
+ * name of the property is the name of the metric, the value
+ * of the property is an object with the following properties:
+ *
+ * description: human readable description of the metric
+ * units: a string representing the units of the metric. It has
+ * the form '<unit> <unit> ... / <unit> / <unit> ...'. Certain
+ * unit values are recognized: s, ms, us, B, KiB, MiB. Other
+ * values can appear but are uninterpreted. Examples 's',
+ * '/ s', 'frames', 'frames / s', 'MiB / s / frame'
+ * value: computed value of the metric
+ *
+ * The resulting metrics will be written to @outputFile as JSON, or,
+ * if @outputFile is not provided, logged.
+ *
+ * After running the script and collecting statistics from the
+ * event log, GNOME Shell will exit.
+ **/
+function runPerfScript(scriptModule, outputFile) {
+ Shell.PerfLog.get_default().set_enabled(true);
+ _spawnPerfHelper();
+
+ Gio.bus_watch_name(Gio.BusType.SESSION,
+ 'org.gnome.Shell.PerfHelper',
+ Gio.BusNameWatcherFlags.NONE,
+ () => _runPerfScript(scriptModule, outputFile),
+ null);
+}
diff --git a/js/ui/search.js b/js/ui/search.js
new file mode 100644
index 0000000..1029f31
--- /dev/null
+++ b/js/ui/search.js
@@ -0,0 +1,945 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SearchResultsView */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const AppDisplay = imports.ui.appDisplay;
+const IconGrid = imports.ui.iconGrid;
+const Main = imports.ui.main;
+const ParentalControlsManager = imports.misc.parentalControlsManager;
+const RemoteSearch = imports.ui.remoteSearch;
+const Util = imports.misc.util;
+
+const { Highlighter } = imports.misc.util;
+
+const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
+
+var MAX_LIST_SEARCH_RESULTS_ROWS = 5;
+
+var MaxWidthBox = GObject.registerClass(
+class MaxWidthBox extends St.BoxLayout {
+ vfunc_allocate(box) {
+ let themeNode = this.get_theme_node();
+ let maxWidth = themeNode.get_max_width();
+ let availWidth = box.x2 - box.x1;
+ let adjustedBox = box;
+
+ if (availWidth > maxWidth) {
+ let excessWidth = availWidth - maxWidth;
+ adjustedBox.x1 += Math.floor(excessWidth / 2);
+ adjustedBox.x2 -= Math.floor(excessWidth / 2);
+ }
+
+ super.vfunc_allocate(adjustedBox);
+ }
+});
+
+var SearchResult = GObject.registerClass(
+class SearchResult extends St.Button {
+ _init(provider, metaInfo, resultsView) {
+ this.provider = provider;
+ this.metaInfo = metaInfo;
+ this._resultsView = resultsView;
+
+ super._init({
+ reactive: true,
+ can_focus: true,
+ track_hover: true,
+ });
+ }
+
+ vfunc_clicked() {
+ this.activate();
+ }
+
+ activate() {
+ this.provider.activateResult(this.metaInfo.id, this._resultsView.terms);
+
+ if (this.metaInfo.clipboardText) {
+ St.Clipboard.get_default().set_text(
+ St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText);
+ }
+ Main.overview.toggle();
+ }
+});
+
+var ListSearchResult = GObject.registerClass(
+class ListSearchResult extends SearchResult {
+ _init(provider, metaInfo, resultsView) {
+ super._init(provider, metaInfo, resultsView);
+
+ this.style_class = 'list-search-result';
+
+ let content = new St.BoxLayout({
+ style_class: 'list-search-result-content',
+ vertical: false,
+ x_align: Clutter.ActorAlign.START,
+ x_expand: true,
+ y_expand: true,
+ });
+ this.set_child(content);
+
+ let titleBox = new St.BoxLayout({
+ style_class: 'list-search-result-title',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ content.add_child(titleBox);
+
+ // An icon for, or thumbnail of, content
+ let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
+ if (icon)
+ titleBox.add(icon);
+
+ let title = new St.Label({
+ text: this.metaInfo['name'],
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ titleBox.add_child(title);
+
+ this.label_actor = title;
+
+ if (this.metaInfo['description']) {
+ this._descriptionLabel = new St.Label({
+ style_class: 'list-search-result-description',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ content.add_child(this._descriptionLabel);
+
+ this._resultsView.connectObject(
+ 'terms-changed', this._highlightTerms.bind(this), this);
+
+ this._highlightTerms();
+ }
+ }
+
+ get ICON_SIZE() {
+ return 24;
+ }
+
+ _highlightTerms() {
+ let markup = this._resultsView.highlightTerms(this.metaInfo['description'].split('\n')[0]);
+ this._descriptionLabel.clutter_text.set_markup(markup);
+ }
+});
+
+var GridSearchResult = GObject.registerClass(
+class GridSearchResult extends SearchResult {
+ _init(provider, metaInfo, resultsView) {
+ super._init(provider, metaInfo, resultsView);
+
+ this.style_class = 'grid-search-result';
+
+ this.icon = new IconGrid.BaseIcon(this.metaInfo['name'],
+ { createIcon: this.metaInfo['createIcon'] });
+ let content = new St.Bin({
+ child: this.icon,
+ x_align: Clutter.ActorAlign.START,
+ x_expand: true,
+ y_expand: true,
+ });
+ this.set_child(content);
+ this.label_actor = this.icon.label;
+ }
+});
+
+var SearchResultsBase = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+ Properties: {
+ 'focus-child': GObject.ParamSpec.object(
+ 'focus-child', 'focus-child', 'focus-child',
+ GObject.ParamFlags.READABLE,
+ Clutter.Actor.$gtype),
+ },
+}, class SearchResultsBase extends St.BoxLayout {
+ _init(provider, resultsView) {
+ super._init({ style_class: 'search-section', vertical: true });
+
+ this.provider = provider;
+ this._resultsView = resultsView;
+
+ this._terms = [];
+ this._focusChild = null;
+
+ this._resultDisplayBin = new St.Bin();
+ this.add_child(this._resultDisplayBin);
+
+ let separator = new St.Widget({ style_class: 'search-section-separator' });
+ this.add(separator);
+
+ this._resultDisplays = {};
+
+ this._cancellable = new Gio.Cancellable();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ this._terms = [];
+ }
+
+ _createResultDisplay(meta) {
+ if (this.provider.createResultObject)
+ return this.provider.createResultObject(meta, this._resultsView);
+
+ return null;
+ }
+
+ clear() {
+ this._cancellable.cancel();
+ for (let resultId in this._resultDisplays)
+ this._resultDisplays[resultId].destroy();
+ this._resultDisplays = {};
+ this._clearResultDisplay();
+ this.hide();
+ }
+
+ get focusChild() {
+ return this._focusChild;
+ }
+
+ _keyFocusIn(actor) {
+ if (this._focusChild == actor)
+ return;
+ this._focusChild = actor;
+ this.notify('focus-child');
+ }
+
+ _setMoreCount(_count) {
+ }
+
+ async _ensureResultActors(results) {
+ let metasNeeded = results.filter(
+ resultId => this._resultDisplays[resultId] === undefined);
+
+ if (metasNeeded.length === 0)
+ return;
+
+ this._cancellable.cancel();
+ this._cancellable.reset();
+
+ const metas = await this.provider.getResultMetas(metasNeeded, this._cancellable);
+
+ if (this._cancellable.is_cancelled()) {
+ if (metas.length > 0)
+ throw new Error(`Search provider ${this.provider.id} returned results after the request was canceled`);
+ }
+
+ if (metas.length !== metasNeeded.length) {
+ throw new Error(`Wrong number of result metas returned by search provider ${this.provider.id}: ` +
+ `expected ${metasNeeded.length} but got ${metas.length}`);
+ }
+
+ if (metas.some(meta => !meta.name || !meta.id))
+ throw new Error(`Invalid result meta returned from search provider ${this.provider.id}`);
+
+ metasNeeded.forEach((resultId, i) => {
+ let meta = metas[i];
+ let display = this._createResultDisplay(meta);
+ display.connect('key-focus-in', this._keyFocusIn.bind(this));
+ this._resultDisplays[resultId] = display;
+ });
+ }
+
+ async updateSearch(providerResults, terms, callback) {
+ this._terms = terms;
+ if (providerResults.length == 0) {
+ this._clearResultDisplay();
+ this.hide();
+ callback();
+ } else {
+ let maxResults = this._getMaxDisplayedResults();
+ let results = maxResults > -1
+ ? this.provider.filterResults(providerResults, maxResults)
+ : providerResults;
+ let moreCount = Math.max(providerResults.length - results.length, 0);
+
+ try {
+ await this._ensureResultActors(results);
+
+ // To avoid CSS transitions causing flickering when
+ // the first search result stays the same, we hide the
+ // content while filling in the results.
+ this.hide();
+ this._clearResultDisplay();
+ results.forEach(
+ resultId => this._addItem(this._resultDisplays[resultId]));
+ this._setMoreCount(this.provider.canLaunchSearch ? moreCount : 0);
+ this.show();
+ callback();
+ } catch (e) {
+ this._clearResultDisplay();
+ callback();
+ }
+ }
+ }
+});
+
+var ListSearchResults = GObject.registerClass(
+class ListSearchResults extends SearchResultsBase {
+ _init(provider, resultsView) {
+ super._init(provider, resultsView);
+
+ this._container = new St.BoxLayout({ style_class: 'search-section-content' });
+ this.providerInfo = new ProviderInfo(provider);
+ this.providerInfo.connect('key-focus-in', this._keyFocusIn.bind(this));
+ this.providerInfo.connect('clicked', () => {
+ this.providerInfo.animateLaunch();
+ provider.launchSearch(this._terms);
+ Main.overview.toggle();
+ });
+
+ this._container.add_child(this.providerInfo);
+
+ this._content = new St.BoxLayout({
+ style_class: 'list-search-results',
+ vertical: true,
+ x_expand: true,
+ });
+ this._container.add_child(this._content);
+
+ this._resultDisplayBin.set_child(this._container);
+ }
+
+ _setMoreCount(count) {
+ this.providerInfo.setMoreCount(count);
+ }
+
+ _getMaxDisplayedResults() {
+ return MAX_LIST_SEARCH_RESULTS_ROWS;
+ }
+
+ _clearResultDisplay() {
+ this._content.remove_all_children();
+ }
+
+ _createResultDisplay(meta) {
+ return super._createResultDisplay(meta) ||
+ new ListSearchResult(this.provider, meta, this._resultsView);
+ }
+
+ _addItem(display) {
+ this._content.add_actor(display);
+ }
+
+ getFirstResult() {
+ if (this._content.get_n_children() > 0)
+ return this._content.get_child_at_index(0);
+ else
+ return null;
+ }
+});
+
+var GridSearchResultsLayout = GObject.registerClass({
+ Properties: {
+ 'spacing': GObject.ParamSpec.int('spacing', 'Spacing', 'Spacing',
+ GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0),
+ },
+}, class GridSearchResultsLayout extends Clutter.LayoutManager {
+ _init() {
+ super._init();
+ this._spacing = 0;
+ }
+
+ vfunc_set_container(container) {
+ this._container = container;
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ let minWidth = 0;
+ let natWidth = 0;
+ let first = true;
+
+ for (let child of container) {
+ if (!child.visible)
+ continue;
+
+ const [childMinWidth, childNatWidth] = child.get_preferred_width(forHeight);
+
+ minWidth = Math.max(minWidth, childMinWidth);
+ natWidth += childNatWidth;
+
+ if (first)
+ first = false;
+ else
+ natWidth += this._spacing;
+ }
+
+ return [minWidth, natWidth];
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ let minHeight = 0;
+ let natHeight = 0;
+
+ for (let child of container) {
+ if (!child.visible)
+ continue;
+
+ const [childMinHeight, childNatHeight] = child.get_preferred_height(forWidth);
+
+ minHeight = Math.max(minHeight, childMinHeight);
+ natHeight = Math.max(natHeight, childNatHeight);
+ }
+
+ return [minHeight, natHeight];
+ }
+
+ vfunc_allocate(container, box) {
+ const width = box.get_width();
+
+ const childBox = new Clutter.ActorBox();
+ childBox.x1 = 0;
+ childBox.y1 = 0;
+
+ let first = true;
+ for (let child of container) {
+ if (!child.visible)
+ continue;
+
+ if (first)
+ first = false;
+ else
+ childBox.x1 += this._spacing;
+
+ const [childWidth] = child.get_preferred_width(-1);
+ const [childHeight] = child.get_preferred_height(-1);
+
+ if (childBox.x1 + childWidth <= width)
+ childBox.set_size(childWidth, childHeight);
+ else
+ childBox.set_size(0, 0);
+
+ child.allocate(childBox);
+ child.can_focus = childBox.get_area() > 0;
+
+ childBox.x1 += childWidth;
+ }
+ }
+
+ columnsForWidth(width) {
+ if (!this._container)
+ return -1;
+
+ const [minWidth] = this.get_preferred_width(this._container, -1);
+
+ if (minWidth === 0)
+ return -1;
+
+ let nCols = 0;
+ while (width > minWidth) {
+ width -= minWidth;
+ if (nCols > 0)
+ width -= this._spacing;
+ nCols++;
+ }
+
+ return nCols;
+ }
+
+ get spacing() {
+ return this._spacing;
+ }
+
+ set spacing(v) {
+ if (this._spacing === v)
+ return;
+ this._spacing = v;
+ this.layout_changed();
+ }
+});
+
+var GridSearchResults = GObject.registerClass(
+class GridSearchResults extends SearchResultsBase {
+ _init(provider, resultsView) {
+ super._init(provider, resultsView);
+
+ this._grid = new St.Widget({ style_class: 'grid-search-results' });
+ this._grid.layout_manager = new GridSearchResultsLayout();
+
+ this._grid.connect('style-changed', () => {
+ const node = this._grid.get_theme_node();
+ this._grid.layout_manager.spacing = node.get_length('spacing');
+ });
+
+ this._resultDisplayBin.set_child(new St.Bin({
+ child: this._grid,
+ x_align: Clutter.ActorAlign.CENTER,
+ }));
+ }
+
+ _onDestroy() {
+ if (this._updateSearchLater) {
+ Meta.later_remove(this._updateSearchLater);
+ delete this._updateSearchLater;
+ }
+
+ super._onDestroy();
+ }
+
+ updateSearch(...args) {
+ if (this._notifyAllocationId)
+ this.disconnect(this._notifyAllocationId);
+ if (this._updateSearchLater) {
+ Meta.later_remove(this._updateSearchLater);
+ delete this._updateSearchLater;
+ }
+
+ // Make sure the maximum number of results calculated by
+ // _getMaxDisplayedResults() is updated after width changes.
+ this._notifyAllocationId = this.connect('notify::allocation', () => {
+ if (this._updateSearchLater)
+ return;
+ this._updateSearchLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ delete this._updateSearchLater;
+ super.updateSearch(...args);
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+
+ super.updateSearch(...args);
+ }
+
+ _getMaxDisplayedResults() {
+ let width = this.allocation.get_width();
+ if (width == 0)
+ return -1;
+
+ return this._grid.layout_manager.columnsForWidth(width);
+ }
+
+ _clearResultDisplay() {
+ this._grid.remove_all_children();
+ }
+
+ _createResultDisplay(meta) {
+ return super._createResultDisplay(meta) ||
+ new GridSearchResult(this.provider, meta, this._resultsView);
+ }
+
+ _addItem(display) {
+ this._grid.add_child(display);
+ }
+
+ getFirstResult() {
+ for (let child of this._grid) {
+ if (child.visible)
+ return child;
+ }
+ return null;
+ }
+});
+
+var SearchResultsView = GObject.registerClass({
+ Signals: { 'terms-changed': {} },
+}, class SearchResultsView extends St.BoxLayout {
+ _init() {
+ super._init({
+ name: 'searchResults',
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ });
+
+ this._parentalControlsManager = ParentalControlsManager.getDefault();
+ this._parentalControlsManager.connect('app-filter-changed', this._reloadRemoteProviders.bind(this));
+
+ this._content = new MaxWidthBox({
+ name: 'searchResultsContent',
+ vertical: true,
+ x_expand: true,
+ });
+
+ this._scrollView = new St.ScrollView({
+ overlay_scrollbars: true,
+ style_class: 'search-display vfade',
+ x_expand: true,
+ y_expand: true,
+ });
+ this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
+ this._scrollView.add_actor(this._content);
+
+ let action = new Clutter.PanAction({ interpolate: true });
+ action.connect('pan', this._onPan.bind(this));
+ this._scrollView.add_action(action);
+
+ this.add_child(this._scrollView);
+
+ this._statusText = new St.Label({
+ style_class: 'search-statustext',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._statusBin = new St.Bin({ y_expand: true });
+ this.add_child(this._statusBin);
+ this._statusBin.add_actor(this._statusText);
+
+ this._highlightDefault = false;
+ this._defaultResult = null;
+ this._startingSearch = false;
+
+ this._terms = [];
+ this._results = {};
+
+ this._providers = [];
+
+ this._highlighter = new Highlighter();
+
+ this._searchSettings = new Gio.Settings({ schema_id: SEARCH_PROVIDERS_SCHEMA });
+ this._searchSettings.connect('changed::disabled', this._reloadRemoteProviders.bind(this));
+ this._searchSettings.connect('changed::enabled', this._reloadRemoteProviders.bind(this));
+ this._searchSettings.connect('changed::disable-external', this._reloadRemoteProviders.bind(this));
+ this._searchSettings.connect('changed::sort-order', this._reloadRemoteProviders.bind(this));
+
+ this._searchTimeoutId = 0;
+ this._cancellable = new Gio.Cancellable();
+
+ this._registerProvider(new AppDisplay.AppSearchProvider());
+
+ let appSystem = Shell.AppSystem.get_default();
+ appSystem.connect('installed-changed', this._reloadRemoteProviders.bind(this));
+ this._reloadRemoteProviders();
+ }
+
+ get terms() {
+ return this._terms;
+ }
+
+ _reloadRemoteProviders() {
+ let remoteProviders = this._providers.filter(p => p.isRemoteProvider);
+ remoteProviders.forEach(provider => {
+ this._unregisterProvider(provider);
+ });
+
+ const providers = RemoteSearch.loadRemoteSearchProviders(this._searchSettings);
+ providers.forEach(this._registerProvider.bind(this));
+ }
+
+ _registerProvider(provider) {
+ provider.searchInProgress = false;
+
+ // Filter out unwanted providers.
+ if (provider.appInfo && !this._parentalControlsManager.shouldShowApp(provider.appInfo))
+ return;
+
+ this._providers.push(provider);
+ this._ensureProviderDisplay(provider);
+ }
+
+ _unregisterProvider(provider) {
+ let index = this._providers.indexOf(provider);
+ this._providers.splice(index, 1);
+
+ if (provider.display)
+ provider.display.destroy();
+ }
+
+ _clearSearchTimeout() {
+ if (this._searchTimeoutId > 0) {
+ GLib.source_remove(this._searchTimeoutId);
+ this._searchTimeoutId = 0;
+ }
+ }
+
+ _reset() {
+ this._terms = [];
+ this._results = {};
+ this._clearDisplay();
+ this._clearSearchTimeout();
+ this._defaultResult = null;
+ this._startingSearch = false;
+
+ this._updateSearchProgress();
+ }
+
+ async _doProviderSearch(provider, previousResults) {
+ provider.searchInProgress = true;
+
+ let results;
+ if (this._isSubSearch && previousResults) {
+ results = await provider.getSubsearchResultSet(
+ previousResults,
+ this._terms,
+ this._cancellable);
+ } else {
+ results = await provider.getInitialResultSet(
+ this._terms,
+ this._cancellable);
+ }
+
+ this._results[provider.id] = results;
+ this._updateResults(provider, results);
+ }
+
+ _doSearch() {
+ this._startingSearch = false;
+
+ let previousResults = this._results;
+ this._results = {};
+
+ this._providers.forEach(provider => {
+ let previousProviderResults = previousResults[provider.id];
+ this._doProviderSearch(provider, previousProviderResults);
+ });
+
+ this._updateSearchProgress();
+
+ this._clearSearchTimeout();
+ }
+
+ _onSearchTimeout() {
+ this._searchTimeoutId = 0;
+ this._doSearch();
+ return GLib.SOURCE_REMOVE;
+ }
+
+ setTerms(terms) {
+ // Check for the case of making a duplicate previous search before
+ // setting state of the current search or cancelling the search.
+ // This will prevent incorrect state being as a result of a duplicate
+ // search while the previous search is still active.
+ let searchString = terms.join(' ');
+ let previousSearchString = this._terms.join(' ');
+ if (searchString == previousSearchString)
+ return;
+
+ this._startingSearch = true;
+
+ this._cancellable.cancel();
+ this._cancellable.reset();
+
+ if (terms.length == 0) {
+ this._reset();
+ return;
+ }
+
+ let isSubSearch = false;
+ if (this._terms.length > 0)
+ isSubSearch = searchString.indexOf(previousSearchString) == 0;
+
+ this._terms = terms;
+ this._isSubSearch = isSubSearch;
+ this._updateSearchProgress();
+
+ if (this._searchTimeoutId == 0)
+ this._searchTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 150, this._onSearchTimeout.bind(this));
+
+ this._highlighter = new Highlighter(this._terms);
+
+ this.emit('terms-changed');
+ }
+
+ _onPan(action) {
+ let [dist_, dx_, dy] = action.get_motion_delta(0);
+ let adjustment = this._scrollView.vscroll.adjustment;
+ adjustment.value -= (dy / this.height) * adjustment.page_size;
+ return false;
+ }
+
+ _focusChildChanged(provider) {
+ Util.ensureActorVisibleInScrollView(this._scrollView, provider.focusChild);
+ }
+
+ _ensureProviderDisplay(provider) {
+ if (provider.display)
+ return;
+
+ let providerDisplay;
+ if (provider.appInfo)
+ providerDisplay = new ListSearchResults(provider, this);
+ else
+ providerDisplay = new GridSearchResults(provider, this);
+
+ providerDisplay.connect('notify::focus-child', this._focusChildChanged.bind(this));
+ providerDisplay.hide();
+ this._content.add(providerDisplay);
+ provider.display = providerDisplay;
+ }
+
+ _clearDisplay() {
+ this._providers.forEach(provider => {
+ provider.display.clear();
+ });
+ }
+
+ _maybeSetInitialSelection() {
+ let newDefaultResult = null;
+
+ let providers = this._providers;
+ for (let i = 0; i < providers.length; i++) {
+ let provider = providers[i];
+ let display = provider.display;
+
+ if (!display.visible)
+ continue;
+
+ let firstResult = display.getFirstResult();
+ if (firstResult) {
+ newDefaultResult = firstResult;
+ break; // select this one!
+ }
+ }
+
+ if (newDefaultResult != this._defaultResult) {
+ this._setSelected(this._defaultResult, false);
+ this._setSelected(newDefaultResult, this._highlightDefault);
+
+ this._defaultResult = newDefaultResult;
+ }
+ }
+
+ get searchInProgress() {
+ if (this._startingSearch)
+ return true;
+
+ return this._providers.some(p => p.searchInProgress);
+ }
+
+ _updateSearchProgress() {
+ let haveResults = this._providers.some(provider => {
+ let display = provider.display;
+ return display.getFirstResult() != null;
+ });
+
+ this._scrollView.visible = haveResults;
+ this._statusBin.visible = !haveResults;
+
+ if (!haveResults) {
+ if (this.searchInProgress)
+ this._statusText.set_text(_("Searching…"));
+ else
+ this._statusText.set_text(_("No results."));
+ }
+ }
+
+ _updateResults(provider, results) {
+ let terms = this._terms;
+ let display = provider.display;
+
+ display.updateSearch(results, terms, () => {
+ provider.searchInProgress = false;
+
+ this._maybeSetInitialSelection();
+ this._updateSearchProgress();
+ });
+ }
+
+ activateDefault() {
+ // If we have a search queued up, force the search now.
+ if (this._searchTimeoutId > 0)
+ this._doSearch();
+
+ if (this._defaultResult)
+ this._defaultResult.activate();
+ }
+
+ highlightDefault(highlight) {
+ this._highlightDefault = highlight;
+ this._setSelected(this._defaultResult, highlight);
+ }
+
+ popupMenuDefault() {
+ // If we have a search queued up, force the search now.
+ if (this._searchTimeoutId > 0)
+ this._doSearch();
+
+ if (this._defaultResult)
+ this._defaultResult.popup_menu();
+ }
+
+ navigateFocus(direction) {
+ let rtl = this.get_text_direction() == Clutter.TextDirection.RTL;
+ if (direction == St.DirectionType.TAB_BACKWARD ||
+ direction == (rtl
+ ? St.DirectionType.RIGHT
+ : St.DirectionType.LEFT) ||
+ direction == St.DirectionType.UP) {
+ this.navigate_focus(null, direction, false);
+ return;
+ }
+
+ const from = this._defaultResult ?? null;
+ this.navigate_focus(from, direction, false);
+ }
+
+ _setSelected(result, selected) {
+ if (!result)
+ return;
+
+ if (selected) {
+ result.add_style_pseudo_class('selected');
+ Util.ensureActorVisibleInScrollView(this._scrollView, result);
+ } else {
+ result.remove_style_pseudo_class('selected');
+ }
+ }
+
+ highlightTerms(description) {
+ if (!description)
+ return '';
+
+ return this._highlighter.highlight(description);
+ }
+});
+
+var ProviderInfo = GObject.registerClass(
+class ProviderInfo extends St.Button {
+ _init(provider) {
+ this.provider = provider;
+ super._init({
+ style_class: 'search-provider-icon',
+ reactive: true,
+ can_focus: true,
+ accessible_name: provider.appInfo.get_name(),
+ track_hover: true,
+ y_align: Clutter.ActorAlign.START,
+ });
+
+ this._content = new St.BoxLayout({
+ vertical: false,
+ style_class: 'list-search-provider-content',
+ });
+ this.set_child(this._content);
+
+ const icon = new St.Icon({
+ icon_size: this.PROVIDER_ICON_SIZE,
+ gicon: provider.appInfo.get_icon(),
+ });
+
+ const detailsBox = new St.BoxLayout({
+ style_class: 'list-search-provider-details',
+ vertical: true,
+ x_expand: true,
+ });
+
+ const nameLabel = new St.Label({
+ text: provider.appInfo.get_name(),
+ x_align: Clutter.ActorAlign.START,
+ });
+
+ this._moreLabel = new St.Label({ x_align: Clutter.ActorAlign.START });
+
+ detailsBox.add_actor(nameLabel);
+ detailsBox.add_actor(this._moreLabel);
+
+
+ this._content.add_actor(icon);
+ this._content.add_actor(detailsBox);
+ }
+
+ get PROVIDER_ICON_SIZE() {
+ return 32;
+ }
+
+ animateLaunch() {
+ let appSys = Shell.AppSystem.get_default();
+ let app = appSys.lookup_app(this.provider.appInfo.get_id());
+ if (app.state == Shell.AppState.STOPPED)
+ IconGrid.zoomOutActor(this._content);
+ }
+
+ setMoreCount(count) {
+ this._moreLabel.text = ngettext("%d more", "%d more", count).format(count);
+ this._moreLabel.visible = count > 0;
+ }
+});
diff --git a/js/ui/searchController.js b/js/ui/searchController.js
new file mode 100644
index 0000000..ba743a9
--- /dev/null
+++ b/js/ui/searchController.js
@@ -0,0 +1,325 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SearchController */
+
+const { Clutter, GObject, St } = imports.gi;
+
+const Main = imports.ui.main;
+const Search = imports.ui.search;
+const ShellEntry = imports.ui.shellEntry;
+
+var FocusTrap = GObject.registerClass(
+class FocusTrap extends St.Widget {
+ vfunc_navigate_focus(from, direction) {
+ if (direction === St.DirectionType.TAB_FORWARD ||
+ direction === St.DirectionType.TAB_BACKWARD)
+ return super.vfunc_navigate_focus(from, direction);
+ return false;
+ }
+});
+
+function getTermsForSearchString(searchString) {
+ searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
+ if (searchString === '')
+ return [];
+ return searchString.split(/\s+/);
+}
+
+var SearchController = GObject.registerClass({
+ Properties: {
+ 'search-active': GObject.ParamSpec.boolean(
+ 'search-active', 'search-active', 'search-active',
+ GObject.ParamFlags.READABLE,
+ false),
+ },
+}, class SearchController extends St.Widget {
+ _init(searchEntry, showAppsButton) {
+ super._init({
+ name: 'searchController',
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ visible: false,
+ });
+
+ this._showAppsButton = showAppsButton;
+ this._showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this));
+
+ this._activePage = null;
+
+ this._searchActive = false;
+
+ this._entry = searchEntry;
+ ShellEntry.addContextMenu(this._entry);
+
+ this._text = this._entry.clutter_text;
+ this._text.connect('text-changed', this._onTextChanged.bind(this));
+ this._text.connect('key-press-event', this._onKeyPress.bind(this));
+ this._text.connect('key-focus-in', () => {
+ this._searchResults.highlightDefault(true);
+ });
+ this._text.connect('key-focus-out', () => {
+ this._searchResults.highlightDefault(false);
+ });
+ this._entry.connect('popup-menu', () => {
+ if (!this._searchActive)
+ return;
+
+ this._entry.menu.close();
+ this._searchResults.popupMenuDefault();
+ });
+ this._entry.connect('notify::mapped', this._onMapped.bind(this));
+ global.stage.connect('notify::key-focus', this._onStageKeyFocusChanged.bind(this));
+
+ this._entry.set_primary_icon(new St.Icon({
+ style_class: 'search-entry-icon',
+ icon_name: 'edit-find-symbolic',
+ }));
+ this._clearIcon = new St.Icon({
+ style_class: 'search-entry-icon',
+ icon_name: 'edit-clear-symbolic',
+ });
+
+ this._iconClickedId = 0;
+ this._capturedEventId = 0;
+
+ this._searchResults = new Search.SearchResultsView();
+ this.add_child(this._searchResults);
+ Main.ctrlAltTabManager.addGroup(this._entry, _('Search'), 'edit-find-symbolic');
+
+ // Since the entry isn't inside the results container we install this
+ // dummy widget as the last results container child so that we can
+ // include the entry in the keynav tab path
+ this._focusTrap = new FocusTrap({ can_focus: true });
+ this._focusTrap.connect('key-focus-in', () => {
+ this._entry.grab_key_focus();
+ });
+ this._searchResults.add_actor(this._focusTrap);
+
+ global.focus_manager.add_group(this._searchResults);
+
+ this._stageKeyPressId = 0;
+ Main.overview.connect('showing', () => {
+ this._stageKeyPressId =
+ global.stage.connect('key-press-event', this._onStageKeyPress.bind(this));
+ });
+ Main.overview.connect('hiding', () => {
+ if (this._stageKeyPressId !== 0) {
+ global.stage.disconnect(this._stageKeyPressId);
+ this._stageKeyPressId = 0;
+ }
+ });
+ }
+
+ prepareToEnterOverview() {
+ this.reset();
+ this._setSearchActive(false);
+ }
+
+ vfunc_unmap() {
+ this.reset();
+
+ super.vfunc_unmap();
+ }
+
+ _setSearchActive(searchActive) {
+ if (this._searchActive === searchActive)
+ return;
+
+ this._searchActive = searchActive;
+ this.notify('search-active');
+ }
+
+ _onShowAppsButtonToggled() {
+ this._setSearchActive(false);
+ }
+
+ _onStageKeyPress(actor, event) {
+ // Ignore events while anything but the overview has
+ // pushed a modal (system modals, looking glass, ...)
+ if (Main.modalCount > 1)
+ return Clutter.EVENT_PROPAGATE;
+
+ let symbol = event.get_key_symbol();
+
+ if (symbol === Clutter.KEY_Escape) {
+ if (this._searchActive)
+ this.reset();
+ else if (this._showAppsButton.checked)
+ this._showAppsButton.checked = false;
+ else
+ Main.overview.hide();
+ return Clutter.EVENT_STOP;
+ } else if (this._shouldTriggerSearch(symbol)) {
+ this.startSearch(event);
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _searchCancelled() {
+ this._setSearchActive(false);
+
+ // Leave the entry focused when it doesn't have any text;
+ // when replacing a selected search term, Clutter emits
+ // two 'text-changed' signals, one for deleting the previous
+ // text and one for the new one - the second one is handled
+ // incorrectly when we remove focus
+ // (https://bugzilla.gnome.org/show_bug.cgi?id=636341) */
+ if (this._text.text !== '')
+ this.reset();
+ }
+
+ reset() {
+ // Don't drop the key focus on Clutter's side if anything but the
+ // overview has pushed a modal (e.g. system modals when activated using
+ // the overview).
+ if (Main.modalCount <= 1)
+ global.stage.set_key_focus(null);
+
+ this._entry.text = '';
+
+ this._text.set_cursor_visible(true);
+ this._text.set_selection(0, 0);
+ }
+
+ _onStageKeyFocusChanged() {
+ let focus = global.stage.get_key_focus();
+ let appearFocused = this._entry.contains(focus) ||
+ this._searchResults.contains(focus);
+
+ this._text.set_cursor_visible(appearFocused);
+
+ if (appearFocused)
+ this._entry.add_style_pseudo_class('focus');
+ else
+ this._entry.remove_style_pseudo_class('focus');
+ }
+
+ _onMapped() {
+ if (this._entry.mapped) {
+ // Enable 'find-as-you-type'
+ this._capturedEventId =
+ global.stage.connect('captured-event', this._onCapturedEvent.bind(this));
+ this._text.set_cursor_visible(true);
+ this._text.set_selection(0, 0);
+ } else {
+ // Disable 'find-as-you-type'
+ if (this._capturedEventId > 0)
+ global.stage.disconnect(this._capturedEventId);
+ this._capturedEventId = 0;
+ }
+ }
+
+ _shouldTriggerSearch(symbol) {
+ if (symbol === Clutter.KEY_Multi_key)
+ return true;
+
+ if (symbol === Clutter.KEY_BackSpace && this._searchActive)
+ return true;
+
+ let unicode = Clutter.keysym_to_unicode(symbol);
+ if (unicode === 0)
+ return false;
+
+ if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0)
+ return true;
+
+ return false;
+ }
+
+ startSearch(event) {
+ global.stage.set_key_focus(this._text);
+ this._text.event(event, false);
+ }
+
+ // the entry does not show the hint
+ _isActivated() {
+ return this._text.text === this._entry.get_text();
+ }
+
+ _onTextChanged() {
+ let terms = getTermsForSearchString(this._entry.get_text());
+
+ const searchActive = terms.length > 0;
+ this._searchResults.setTerms(terms);
+
+ if (searchActive) {
+ this._setSearchActive(true);
+
+ this._entry.set_secondary_icon(this._clearIcon);
+
+ if (this._iconClickedId === 0) {
+ this._iconClickedId =
+ this._entry.connect('secondary-icon-clicked', this.reset.bind(this));
+ }
+ } else {
+ if (this._iconClickedId > 0) {
+ this._entry.disconnect(this._iconClickedId);
+ this._iconClickedId = 0;
+ }
+
+ this._entry.set_secondary_icon(null);
+ this._searchCancelled();
+ }
+ }
+
+ _onKeyPress(entry, event) {
+ let symbol = event.get_key_symbol();
+ if (symbol === Clutter.KEY_Escape) {
+ if (this._isActivated()) {
+ this.reset();
+ return Clutter.EVENT_STOP;
+ }
+ } else if (this._searchActive) {
+ let arrowNext, nextDirection;
+ if (entry.get_text_direction() === Clutter.TextDirection.RTL) {
+ arrowNext = Clutter.KEY_Left;
+ nextDirection = St.DirectionType.LEFT;
+ } else {
+ arrowNext = Clutter.KEY_Right;
+ nextDirection = St.DirectionType.RIGHT;
+ }
+
+ if (symbol === Clutter.KEY_Tab) {
+ this._searchResults.navigateFocus(St.DirectionType.TAB_FORWARD);
+ return Clutter.EVENT_STOP;
+ } else if (symbol === Clutter.KEY_ISO_Left_Tab) {
+ this._focusTrap.can_focus = false;
+ this._searchResults.navigateFocus(St.DirectionType.TAB_BACKWARD);
+ this._focusTrap.can_focus = true;
+ return Clutter.EVENT_STOP;
+ } else if (symbol === Clutter.KEY_Down) {
+ this._searchResults.navigateFocus(St.DirectionType.DOWN);
+ return Clutter.EVENT_STOP;
+ } else if (symbol === arrowNext && this._text.position === -1) {
+ this._searchResults.navigateFocus(nextDirection);
+ return Clutter.EVENT_STOP;
+ } else if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_KP_Enter) {
+ this._searchResults.activateDefault();
+ return Clutter.EVENT_STOP;
+ }
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onCapturedEvent(actor, event) {
+ if (event.type() === Clutter.EventType.BUTTON_PRESS) {
+ const targetActor = global.stage.get_event_actor(event);
+ if (targetActor !== this._text &&
+ this._text.has_key_focus() &&
+ this._text.text === '' &&
+ !this._text.has_preedit() &&
+ !Main.layoutManager.keyboardBox.contains(targetActor)) {
+ // the user clicked outside after activating the entry, but
+ // with no search term entered and no keyboard button pressed
+ // - cancel the search
+ this.reset();
+ }
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ get searchActive() {
+ return this._searchActive;
+ }
+});
diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
new file mode 100644
index 0000000..b38bcdf
--- /dev/null
+++ b/js/ui/sessionMode.js
@@ -0,0 +1,206 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SessionMode, listModes */
+
+const GLib = imports.gi.GLib;
+const Signals = imports.misc.signals;
+
+const FileUtils = imports.misc.fileUtils;
+const Params = imports.misc.params;
+
+const Config = imports.misc.config;
+
+const DEFAULT_MODE = 'restrictive';
+
+const USER_SESSION_COMPONENTS = [
+ 'polkitAgent', 'telepathyClient', 'keyring',
+ 'autorunManager', 'automountManager',
+];
+
+if (Config.HAVE_NETWORKMANAGER)
+ USER_SESSION_COMPONENTS.push('networkAgent');
+
+const _modes = {
+ 'restrictive': {
+ parentMode: null,
+ stylesheetName: 'gnome-shell.css',
+ themeResourceName: 'gnome-shell-theme.gresource',
+ hasOverview: false,
+ showCalendarEvents: false,
+ showWelcomeDialog: false,
+ allowSettings: false,
+ allowScreencast: false,
+ enabledExtensions: [],
+ hasRunDialog: false,
+ hasWorkspaces: false,
+ hasWindows: false,
+ hasNotifications: false,
+ hasWmMenus: false,
+ isLocked: false,
+ isGreeter: false,
+ isPrimary: false,
+ unlockDialog: null,
+ components: [],
+ panel: {
+ left: [],
+ center: [],
+ right: [],
+ },
+ panelStyle: null,
+ },
+
+ 'gdm': {
+ hasNotifications: true,
+ isGreeter: true,
+ isPrimary: true,
+ unlockDialog: imports.gdm.loginDialog.LoginDialog,
+ components: Config.HAVE_NETWORKMANAGER
+ ? ['networkAgent', 'polkitAgent']
+ : ['polkitAgent'],
+ panel: {
+ left: [],
+ center: ['dateMenu'],
+ right: ['dwellClick', 'a11y', 'keyboard', 'quickSettings'],
+ },
+ panelStyle: 'login-screen',
+ },
+
+ 'unlock-dialog': {
+ isLocked: true,
+ unlockDialog: undefined,
+ components: ['polkitAgent', 'telepathyClient'],
+ panel: {
+ left: [],
+ center: [],
+ right: ['dwellClick', 'a11y', 'keyboard', 'quickSettings'],
+ },
+ panelStyle: 'unlock-screen',
+ },
+
+ 'user': {
+ hasOverview: true,
+ showCalendarEvents: true,
+ showWelcomeDialog: true,
+ allowSettings: true,
+ allowScreencast: true,
+ hasRunDialog: true,
+ hasWorkspaces: true,
+ hasWindows: true,
+ hasWmMenus: true,
+ hasNotifications: true,
+ isLocked: false,
+ isPrimary: true,
+ unlockDialog: imports.ui.unlockDialog.UnlockDialog,
+ components: USER_SESSION_COMPONENTS,
+ panel: {
+ left: ['activities', 'appMenu'],
+ center: ['dateMenu'],
+ right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'],
+ },
+ },
+};
+
+function _loadMode(file, info) {
+ let name = info.get_name();
+ let suffix = name.indexOf('.json');
+ let modeName = suffix == -1 ? name : name.slice(name, suffix);
+
+ if (Object.prototype.hasOwnProperty.call(_modes, modeName))
+ return;
+
+ let fileContent, success_, newMode;
+ try {
+ [success_, fileContent] = file.load_contents(null);
+ const decoder = new TextDecoder();
+ newMode = JSON.parse(decoder.decode(fileContent));
+ } catch (e) {
+ return;
+ }
+
+ _modes[modeName] = {};
+ const excludedProps = ['unlockDialog'];
+ for (let prop in _modes[DEFAULT_MODE]) {
+ if (newMode[prop] !== undefined &&
+ !excludedProps.includes(prop))
+ _modes[modeName][prop] = newMode[prop];
+ }
+ _modes[modeName]['isPrimary'] = true;
+}
+
+function _loadModes() {
+ FileUtils.collectFromDatadirs('modes', false, _loadMode);
+}
+
+function listModes() {
+ _loadModes();
+ let loop = new GLib.MainLoop(null, false);
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ let names = Object.getOwnPropertyNames(_modes);
+ for (let i = 0; i < names.length; i++) {
+ if (_modes[names[i]].isPrimary)
+ print(names[i]);
+ }
+ loop.quit();
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] listModes');
+ loop.run();
+}
+
+var SessionMode = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ _loadModes();
+ let isPrimary = _modes[global.session_mode] &&
+ _modes[global.session_mode].isPrimary;
+ let mode = isPrimary ? global.session_mode : 'user';
+ this._modeStack = [mode];
+ this._sync();
+ }
+
+ pushMode(mode) {
+ console.debug(`sessionMode: Pushing mode ${mode}`);
+ this._modeStack.push(mode);
+ this._sync();
+ }
+
+ popMode(mode) {
+ if (this.currentMode != mode || this._modeStack.length === 1)
+ throw new Error("Invalid SessionMode.popMode");
+
+ console.debug(`sessionMode: Popping mode ${mode}`);
+ this._modeStack.pop();
+ this._sync();
+ }
+
+ switchMode(to) {
+ if (this.currentMode == to)
+ return;
+ this._modeStack[this._modeStack.length - 1] = to;
+ this._sync();
+ }
+
+ get currentMode() {
+ return this._modeStack[this._modeStack.length - 1];
+ }
+
+ _sync() {
+ let params = _modes[this.currentMode];
+ let defaults;
+ if (params.parentMode) {
+ defaults = Params.parse(_modes[params.parentMode],
+ _modes[DEFAULT_MODE]);
+ } else {
+ defaults = _modes[DEFAULT_MODE];
+ }
+ params = Params.parse(params, defaults);
+
+ // A simplified version of Lang.copyProperties, handles
+ // undefined as a special case for "no change / inherit from previous mode"
+ for (let prop in params) {
+ if (params[prop] !== undefined)
+ this[prop] = params[prop];
+ }
+
+ this.emit('updated');
+ }
+};
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
new file mode 100644
index 0000000..284d92b
--- /dev/null
+++ b/js/ui/shellDBus.js
@@ -0,0 +1,540 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported GnomeShell, ScreenSaverDBus */
+
+const { Gio, GLib, Meta, Shell } = imports.gi;
+
+const Config = imports.misc.config;
+const ExtensionDownloader = imports.ui.extensionDownloader;
+const ExtensionUtils = imports.misc.extensionUtils;
+const Main = imports.ui.main;
+const Screenshot = imports.ui.screenshot;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+const { DBusSenderChecker } = imports.misc.util;
+const { ControlsState } = imports.ui.overviewControls;
+
+const GnomeShellIface = loadInterfaceXML('org.gnome.Shell');
+const ScreenSaverIface = loadInterfaceXML('org.gnome.ScreenSaver');
+
+var GnomeShell = class {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
+
+ this._senderChecker = new DBusSenderChecker([
+ 'org.gnome.Settings',
+ 'org.gnome.SettingsDaemon.MediaKeys',
+ ]);
+
+ this._extensionsService = new GnomeShellExtensions();
+ this._screenshotService = new Screenshot.ScreenshotService();
+
+ this._grabbedAccelerators = new Map();
+ this._grabbers = new Map();
+
+ global.display.connect('accelerator-activated',
+ (display, action, device, timestamp) => {
+ this._emitAcceleratorActivated(action, device, timestamp);
+ });
+
+ this._cachedOverviewVisible = false;
+ Main.overview.connect('showing',
+ this._checkOverviewVisibleChanged.bind(this));
+ Main.overview.connect('hidden',
+ this._checkOverviewVisibleChanged.bind(this));
+ }
+
+ /**
+ * Eval:
+ * @param {string} code: A string containing JavaScript code
+ * @returns {Array}
+ *
+ * This function executes arbitrary code in the main
+ * loop, and returns a boolean success and
+ * JSON representation of the object as a string.
+ *
+ * If evaluation completes without throwing an exception,
+ * then the return value will be [true, JSON.stringify(result)].
+ * If evaluation fails, then the return value will be
+ * [false, JSON.stringify(exception)];
+ *
+ */
+ Eval(code) {
+ if (!global.context.unsafe_mode)
+ return [false, ''];
+
+ let returnValue;
+ let success;
+ try {
+ returnValue = JSON.stringify(eval(code));
+ // A hack; DBus doesn't have null/undefined
+ if (returnValue == undefined)
+ returnValue = '';
+ success = true;
+ } catch (e) {
+ returnValue = `${e}`;
+ success = false;
+ }
+ return [success, returnValue];
+ }
+
+ /**
+ * Focus the overview's search entry
+ *
+ * @async
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async FocusSearchAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ Main.overview.focusSearch();
+ invocation.return_value(null);
+ }
+
+ /**
+ * Show OSD with the specified parameters
+ *
+ * @async
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async ShowOSDAsync([params], invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ for (let param in params)
+ params[param] = params[param].deepUnpack();
+
+ const {
+ connector,
+ label,
+ level,
+ max_level: maxLevel,
+ icon: serializedIcon,
+ } = params;
+
+ let monitorIndex = -1;
+ if (connector) {
+ let monitorManager = Meta.MonitorManager.get();
+ monitorIndex = monitorManager.get_monitor_for_connector(connector);
+ }
+
+ let icon = null;
+ if (serializedIcon)
+ icon = Gio.Icon.new_for_string(serializedIcon);
+
+ Main.osdWindowManager.show(monitorIndex, icon, label, level, maxLevel);
+ invocation.return_value(null);
+ }
+
+ /**
+ * Focus specified app in the overview's app grid
+ *
+ * @async
+ * @param {string} id - an application ID
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async FocusAppAsync([id], invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ const appSys = Shell.AppSystem.get_default();
+ if (appSys.lookup_app(id) === null) {
+ invocation.return_error_literal(
+ Gio.DBusError,
+ Gio.DBusError.FILE_NOT_FOUND,
+ `No application with ID ${id}`);
+ return;
+ }
+
+ Main.overview.selectApp(id);
+ invocation.return_value(null);
+ }
+
+ /**
+ * Show the overview's app grid
+ *
+ * @async
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async ShowApplicationsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ Main.overview.show(ControlsState.APP_GRID);
+ invocation.return_value(null);
+ }
+
+ async GrabAcceleratorAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [accel, modeFlags, grabFlags] = params;
+ let sender = invocation.get_sender();
+ let bindingAction = this._grabAcceleratorForSender(accel, modeFlags, grabFlags, sender);
+ invocation.return_value(GLib.Variant.new('(u)', [bindingAction]));
+ }
+
+ async GrabAcceleratorsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [accels] = params;
+ let sender = invocation.get_sender();
+ let bindingActions = [];
+ for (let i = 0; i < accels.length; i++) {
+ let [accel, modeFlags, grabFlags] = accels[i];
+ bindingActions.push(this._grabAcceleratorForSender(accel, modeFlags, grabFlags, sender));
+ }
+ invocation.return_value(GLib.Variant.new('(au)', [bindingActions]));
+ }
+
+ async UngrabAcceleratorAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [action] = params;
+ let sender = invocation.get_sender();
+ let ungrabSucceeded = this._ungrabAcceleratorForSender(action, sender);
+
+ invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
+ }
+
+ async UngrabAcceleratorsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [actions] = params;
+ let sender = invocation.get_sender();
+ let ungrabSucceeded = true;
+
+ for (let i = 0; i < actions.length; i++)
+ ungrabSucceeded &= this._ungrabAcceleratorForSender(actions[i], sender);
+
+ invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
+ }
+
+ async ScreenTransitionAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ Main.layoutManager.screenTransition.run();
+
+ invocation.return_value(null);
+ }
+
+ _emitAcceleratorActivated(action, device, timestamp) {
+ let destination = this._grabbedAccelerators.get(action);
+ if (!destination)
+ return;
+
+ let connection = this._dbusImpl.get_connection();
+ let info = this._dbusImpl.get_info();
+ let params = {
+ 'timestamp': GLib.Variant.new('u', timestamp),
+ 'action-mode': GLib.Variant.new('u', Main.actionMode),
+ };
+
+ let deviceNode = device.get_device_node();
+ if (deviceNode)
+ params['device-node'] = GLib.Variant.new('s', deviceNode);
+
+ connection.emit_signal(
+ destination,
+ this._dbusImpl.get_object_path(),
+ info?.name ?? null,
+ 'AcceleratorActivated',
+ GLib.Variant.new('(ua{sv})', [action, params]));
+ }
+
+ _grabAcceleratorForSender(accelerator, modeFlags, grabFlags, sender) {
+ let bindingAction = global.display.grab_accelerator(accelerator, grabFlags);
+ if (bindingAction == Meta.KeyBindingAction.NONE)
+ return Meta.KeyBindingAction.NONE;
+
+ let bindingName = Meta.external_binding_name_for_action(bindingAction);
+ Main.wm.allowKeybinding(bindingName, modeFlags);
+
+ this._grabbedAccelerators.set(bindingAction, sender);
+
+ if (!this._grabbers.has(sender)) {
+ let id = Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
+ this._onGrabberBusNameVanished.bind(this));
+ this._grabbers.set(sender, id);
+ }
+
+ return bindingAction;
+ }
+
+ _ungrabAccelerator(action) {
+ let ungrabSucceeded = global.display.ungrab_accelerator(action);
+ if (ungrabSucceeded)
+ this._grabbedAccelerators.delete(action);
+
+ return ungrabSucceeded;
+ }
+
+ _ungrabAcceleratorForSender(action, sender) {
+ let grabbedBy = this._grabbedAccelerators.get(action);
+ if (sender != grabbedBy)
+ return false;
+
+ return this._ungrabAccelerator(action);
+ }
+
+ _onGrabberBusNameVanished(connection, name) {
+ let grabs = this._grabbedAccelerators.entries();
+ for (let [action, sender] of grabs) {
+ if (sender == name)
+ this._ungrabAccelerator(action);
+ }
+ Gio.bus_unwatch_name(this._grabbers.get(name));
+ this._grabbers.delete(name);
+ }
+
+ async ShowMonitorLabelsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let sender = invocation.get_sender();
+ let [dict] = params;
+ Main.osdMonitorLabeler.show(sender, dict);
+ invocation.return_value(null);
+ }
+
+ async HideMonitorLabelsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let sender = invocation.get_sender();
+ Main.osdMonitorLabeler.hide(sender);
+ invocation.return_value(null);
+ }
+
+ _checkOverviewVisibleChanged() {
+ if (Main.overview.visible !== this._cachedOverviewVisible) {
+ this._cachedOverviewVisible = Main.overview.visible;
+ this._dbusImpl.emit_property_changed('OverviewActive', new GLib.Variant('b', this._cachedOverviewVisible));
+ }
+ }
+
+ get Mode() {
+ return global.session_mode;
+ }
+
+ get OverviewActive() {
+ return this._cachedOverviewVisible;
+ }
+
+ set OverviewActive(visible) {
+ if (visible)
+ Main.overview.show();
+ else
+ Main.overview.hide();
+ }
+
+ get ShellVersion() {
+ return Config.PACKAGE_VERSION;
+ }
+};
+
+const GnomeShellExtensionsIface = loadInterfaceXML('org.gnome.Shell.Extensions');
+
+var GnomeShellExtensions = class {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
+
+ this._userExtensionsEnabled = this.UserExtensionsEnabled;
+ global.settings.connect('changed::disable-user-extensions', () => {
+ if (this._userExtensionsEnabled === this.UserExtensionsEnabled)
+ return;
+
+ this._userExtensionsEnabled = this.UserExtensionsEnabled;
+ this._dbusImpl.emit_property_changed('UserExtensionsEnabled',
+ new GLib.Variant('b', this._userExtensionsEnabled));
+ });
+
+ Main.extensionManager.connect('extension-state-changed',
+ this._extensionStateChanged.bind(this));
+ }
+
+ ListExtensions() {
+ let out = {};
+ Main.extensionManager.getUuids().forEach(uuid => {
+ let dbusObj = this.GetExtensionInfo(uuid);
+ out[uuid] = dbusObj;
+ });
+ return out;
+ }
+
+ GetExtensionInfo(uuid) {
+ let extension = Main.extensionManager.lookup(uuid) || {};
+ return ExtensionUtils.serializeExtension(extension);
+ }
+
+ GetExtensionErrors(uuid) {
+ let extension = Main.extensionManager.lookup(uuid);
+ if (!extension)
+ return [];
+
+ if (!extension.errors)
+ return [];
+
+ return extension.errors;
+ }
+
+ InstallRemoteExtensionAsync([uuid], invocation) {
+ return ExtensionDownloader.installExtension(uuid, invocation);
+ }
+
+ UninstallExtension(uuid) {
+ return ExtensionDownloader.uninstallExtension(uuid);
+ }
+
+ EnableExtension(uuid) {
+ return Main.extensionManager.enableExtension(uuid);
+ }
+
+ DisableExtension(uuid) {
+ return Main.extensionManager.disableExtension(uuid);
+ }
+
+ LaunchExtensionPrefs(uuid) {
+ this.OpenExtensionPrefs(uuid, '', {});
+ }
+
+ OpenExtensionPrefs(uuid, parentWindow, options) {
+ Main.extensionManager.openExtensionPrefs(uuid, parentWindow, options);
+ }
+
+ ReloadExtensionAsync(params, invocation) {
+ invocation.return_error_literal(
+ Gio.DBusError,
+ Gio.DBusError.NOT_SUPPORTED,
+ 'ReloadExtension is deprecated and does not work');
+ }
+
+ CheckForUpdates() {
+ ExtensionDownloader.checkForUpdates();
+ }
+
+ get ShellVersion() {
+ return Config.PACKAGE_VERSION;
+ }
+
+ get UserExtensionsEnabled() {
+ return !global.settings.get_boolean('disable-user-extensions');
+ }
+
+ set UserExtensionsEnabled(enable) {
+ global.settings.set_boolean('disable-user-extensions', !enable);
+ }
+
+ _extensionStateChanged(_, newState) {
+ let state = ExtensionUtils.serializeExtension(newState);
+ this._dbusImpl.emit_signal('ExtensionStateChanged',
+ new GLib.Variant('(sa{sv})', [newState.uuid, state]));
+
+ this._dbusImpl.emit_signal('ExtensionStatusChanged',
+ GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
+ }
+};
+
+var ScreenSaverDBus = class {
+ constructor(screenShield) {
+ this._screenShield = screenShield;
+ screenShield.connect('active-changed', shield => {
+ this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
+ });
+ screenShield.connect('wake-up-screen', () => {
+ this._dbusImpl.emit_signal('WakeUpScreen', null);
+ });
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
+
+ Gio.DBus.session.own_name('org.gnome.Shell.ScreenShield',
+ Gio.BusNameOwnerFlags.NONE, null, null);
+ }
+
+ LockAsync(parameters, invocation) {
+ let tmpId = this._screenShield.connect('lock-screen-shown', () => {
+ this._screenShield.disconnect(tmpId);
+
+ invocation.return_value(null);
+ });
+
+ this._screenShield.lock(true);
+ }
+
+ SetActive(active) {
+ if (active)
+ this._screenShield.activate(true);
+ else
+ this._screenShield.deactivate(false);
+ }
+
+ GetActive() {
+ return this._screenShield.active;
+ }
+
+ GetActiveTime() {
+ let started = this._screenShield.activationTime;
+ if (started > 0)
+ return Math.floor((GLib.get_monotonic_time() - started) / 1000000);
+ else
+ return 0;
+ }
+};
diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js
new file mode 100644
index 0000000..e6c1f37
--- /dev/null
+++ b/js/ui/shellEntry.js
@@ -0,0 +1,206 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported addContextMenu CapsLockWarning */
+
+const { Clutter, GObject, Pango, Shell, St } = imports.gi;
+
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+const PopupMenu = imports.ui.popupMenu;
+
+var EntryMenu = class extends PopupMenu.PopupMenu {
+ constructor(entry) {
+ super(entry, 0, St.Side.TOP);
+
+ this._entry = entry;
+ this._clipboard = St.Clipboard.get_default();
+
+ // Populate menu
+ let item;
+ item = new PopupMenu.PopupMenuItem(_("Copy"));
+ item.connect('activate', this._onCopyActivated.bind(this));
+ this.addMenuItem(item);
+ this._copyItem = item;
+
+ item = new PopupMenu.PopupMenuItem(_("Paste"));
+ item.connect('activate', this._onPasteActivated.bind(this));
+ this.addMenuItem(item);
+ this._pasteItem = item;
+
+ if (entry instanceof St.PasswordEntry)
+ this._makePasswordItem();
+
+ Main.uiGroup.add_actor(this.actor);
+ this.actor.hide();
+ }
+
+ _makePasswordItem() {
+ let item = new PopupMenu.PopupMenuItem('');
+ item.connect('activate', this._onPasswordActivated.bind(this));
+ this.addMenuItem(item);
+ this._passwordItem = item;
+
+ this._entry.bind_property('show-peek-icon',
+ this._passwordItem, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ open(animate) {
+ this._updatePasteItem();
+ this._updateCopyItem();
+ if (this._passwordItem)
+ this._updatePasswordItem();
+
+ super.open(animate);
+ this._entry.add_style_pseudo_class('focus');
+
+ let direction = St.DirectionType.TAB_FORWARD;
+ if (!this.actor.navigate_focus(null, direction, false))
+ this.actor.grab_key_focus();
+ }
+
+ _updateCopyItem() {
+ let selection = this._entry.clutter_text.get_selection();
+ this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
+ selection && selection != '');
+ }
+
+ _updatePasteItem() {
+ this._clipboard.get_text(St.ClipboardType.CLIPBOARD,
+ (clipboard, text) => {
+ this._pasteItem.setSensitive(text && text != '');
+ });
+ }
+
+ _updatePasswordItem() {
+ if (!this._entry.password_visible)
+ this._passwordItem.label.set_text(_("Show Text"));
+ else
+ this._passwordItem.label.set_text(_("Hide Text"));
+ }
+
+ _onCopyActivated() {
+ let selection = this._entry.clutter_text.get_selection();
+ this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
+ }
+
+ _onPasteActivated() {
+ this._clipboard.get_text(St.ClipboardType.CLIPBOARD,
+ (clipboard, text) => {
+ if (!text)
+ return;
+ this._entry.clutter_text.delete_selection();
+ let pos = this._entry.clutter_text.get_cursor_position();
+ this._entry.clutter_text.insert_text(text, pos);
+ });
+ }
+
+ _onPasswordActivated() {
+ this._entry.password_visible = !this._entry.password_visible;
+ }
+};
+
+function _setMenuAlignment(entry, stageX) {
+ let [success, entryX] = entry.transform_stage_point(stageX, 0);
+ if (success)
+ entry.menu.setSourceAlignment(entryX / entry.width);
+}
+
+function _onButtonPressEvent(actor, event, entry) {
+ if (entry.menu.isOpen) {
+ entry.menu.close(BoxPointer.PopupAnimation.FULL);
+ return Clutter.EVENT_STOP;
+ } else if (event.get_button() == 3) {
+ let [stageX] = event.get_coords();
+ _setMenuAlignment(entry, stageX);
+ entry.menu.open(BoxPointer.PopupAnimation.FULL);
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+}
+
+function _onPopup(actor, entry) {
+ let cursorPosition = entry.clutter_text.get_cursor_position();
+ let [success, textX, textY_, lineHeight_] = entry.clutter_text.position_to_coords(cursorPosition);
+ if (success)
+ entry.menu.setSourceAlignment(textX / entry.width);
+ entry.menu.open(BoxPointer.PopupAnimation.FULL);
+}
+
+function addContextMenu(entry, params) {
+ if (entry.menu)
+ return;
+
+ params = Params.parse(params, { actionMode: Shell.ActionMode.POPUP });
+
+ entry.menu = new EntryMenu(entry);
+ entry._menuManager = new PopupMenu.PopupMenuManager(entry,
+ { actionMode: params.actionMode });
+ entry._menuManager.addMenu(entry.menu);
+
+ // Add an event handler to both the entry and its clutter_text; the former
+ // so padding is included in the clickable area, the latter because the
+ // event processing of ClutterText prevents event-bubbling.
+ entry.clutter_text.connect('button-press-event', (actor, event) => {
+ _onButtonPressEvent(actor, event, entry);
+ });
+ entry.connect('button-press-event', (actor, event) => {
+ _onButtonPressEvent(actor, event, entry);
+ });
+
+ entry.connect('popup-menu', actor => _onPopup(actor, entry));
+
+ entry.connect('destroy', () => {
+ entry.menu.destroy();
+ entry.menu = null;
+ entry._menuManager = null;
+ });
+}
+
+var CapsLockWarning = GObject.registerClass(
+class CapsLockWarning extends St.Label {
+ _init(params) {
+ let defaultParams = { style_class: 'caps-lock-warning-label' };
+ super._init(Object.assign(defaultParams, params));
+
+ this.text = _('Caps lock is on.');
+
+ this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this.clutter_text.line_wrap = true;
+
+ let seat = Clutter.get_default_backend().get_default_seat();
+ this._keymap = seat.get_keymap();
+
+ this.connect('notify::mapped', () => {
+ if (this.is_mapped()) {
+ this._keymap.connectObject(
+ 'state-changed', () => this._sync(true), this);
+ } else {
+ this._keymap.disconnectObject(this);
+ }
+
+ this._sync(false);
+ });
+ }
+
+ _sync(animate) {
+ let capsLockOn = this._keymap.get_caps_lock_state();
+
+ this.remove_all_transitions();
+
+ const { naturalHeightSet } = this;
+ this.natural_height_set = false;
+ let [, height] = this.get_preferred_height(-1);
+ this.natural_height_set = naturalHeightSet;
+
+ this.ease({
+ height: capsLockOn ? height : 0,
+ opacity: capsLockOn ? 255 : 0,
+ duration: animate ? 200 : 0,
+ onComplete: () => {
+ if (capsLockOn)
+ this.height = -1;
+ },
+ });
+ }
+});
diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js
new file mode 100644
index 0000000..b04156d
--- /dev/null
+++ b/js/ui/shellMountOperation.js
@@ -0,0 +1,752 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ShellMountOperation, GnomeShellMountOpHandler */
+
+const { Clutter, Gio, GLib, GObject, Pango, Shell, St } = imports.gi;
+
+const Animation = imports.ui.animation;
+const CheckBox = imports.ui.checkBox;
+const Dialog = imports.ui.dialog;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const ModalDialog = imports.ui.modalDialog;
+const Params = imports.misc.params;
+const ShellEntry = imports.ui.shellEntry;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+const Util = imports.misc.util;
+
+var LIST_ITEM_ICON_SIZE = 48;
+var WORK_SPINNER_ICON_SIZE = 16;
+
+const REMEMBER_MOUNT_PASSWORD_KEY = 'remember-mount-password';
+
+/* ------ Common Utils ------- */
+function _setButtonsForChoices(dialog, oldChoices, choices) {
+ let buttons = [];
+ let buttonsChanged = oldChoices.length !== choices.length;
+
+ for (let idx = 0; idx < choices.length; idx++) {
+ let button = idx;
+
+ buttonsChanged ||= oldChoices[idx] !== choices[idx];
+
+ buttons.unshift({
+ label: choices[idx],
+ action: () => dialog.emit('response', button),
+ });
+ }
+
+ if (buttonsChanged)
+ dialog.setButtons(buttons);
+}
+
+function _setLabelsForMessage(content, message) {
+ let labels = message.split('\n');
+
+ content.title = labels.shift();
+ content.description = labels.join('\n');
+}
+
+/* -------------------------------------------------------- */
+
+var ShellMountOperation = class {
+ constructor(source, params) {
+ params = Params.parse(params, { existingDialog: null });
+
+ this._dialog = null;
+ this._existingDialog = params.existingDialog;
+ this._processesDialog = null;
+
+ this.mountOp = new Shell.MountOperation();
+
+ this.mountOp.connect('ask-question',
+ this._onAskQuestion.bind(this));
+ this.mountOp.connect('ask-password',
+ this._onAskPassword.bind(this));
+ this.mountOp.connect('show-processes-2',
+ this._onShowProcesses2.bind(this));
+ this.mountOp.connect('aborted',
+ this.close.bind(this));
+ this.mountOp.connect('show-unmount-progress',
+ this._onShowUnmountProgress.bind(this));
+ }
+
+ _closeExistingDialog() {
+ if (!this._existingDialog)
+ return;
+
+ this._existingDialog.close();
+ this._existingDialog = null;
+ }
+
+ _onAskQuestion(op, message, choices) {
+ this._closeExistingDialog();
+ this._dialog = new ShellMountQuestionDialog();
+
+ this._dialog.connectObject('response',
+ (object, choice) => {
+ this.mountOp.set_choice(choice);
+ this.mountOp.reply(Gio.MountOperationResult.HANDLED);
+
+ this.close();
+ }, this);
+
+ this._dialog.update(message, choices);
+ this._dialog.open();
+ }
+
+ _onAskPassword(op, message, defaultUser, defaultDomain, flags) {
+ if (this._existingDialog) {
+ this._dialog = this._existingDialog;
+ this._dialog.reaskPassword();
+ } else {
+ this._dialog = new ShellMountPasswordDialog(message, flags);
+ }
+
+ this._dialog.connectObject('response',
+ (object, choice, password, remember, hiddenVolume, systemVolume, pim) => {
+ if (choice == -1) {
+ this.mountOp.reply(Gio.MountOperationResult.ABORTED);
+ } else {
+ if (remember)
+ this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
+ else
+ this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
+
+ this.mountOp.set_password(password);
+ this.mountOp.set_is_tcrypt_hidden_volume(hiddenVolume);
+ this.mountOp.set_is_tcrypt_system_volume(systemVolume);
+ this.mountOp.set_pim(pim);
+ this.mountOp.reply(Gio.MountOperationResult.HANDLED);
+ }
+ }, this);
+ this._dialog.open();
+ }
+
+ close(_op) {
+ this._closeExistingDialog();
+ this._processesDialog = null;
+
+ if (this._dialog) {
+ this._dialog.close();
+ this._dialog = null;
+ }
+
+ if (this._notifier) {
+ this._notifier.done();
+ this._notifier = null;
+ }
+ }
+
+ _onShowProcesses2(op) {
+ this._closeExistingDialog();
+
+ let processes = op.get_show_processes_pids();
+ let choices = op.get_show_processes_choices();
+ let message = op.get_show_processes_message();
+
+ if (!this._processesDialog) {
+ this._processesDialog = new ShellProcessesDialog();
+ this._dialog = this._processesDialog;
+
+ this._processesDialog.connectObject('response',
+ (object, choice) => {
+ if (choice == -1) {
+ this.mountOp.reply(Gio.MountOperationResult.ABORTED);
+ } else {
+ this.mountOp.set_choice(choice);
+ this.mountOp.reply(Gio.MountOperationResult.HANDLED);
+ }
+
+ this.close();
+ }, this);
+ this._processesDialog.open();
+ }
+
+ this._processesDialog.update(message, processes, choices);
+ }
+
+ _onShowUnmountProgress(op, message, timeLeft, bytesLeft) {
+ if (!this._notifier)
+ this._notifier = new ShellUnmountNotifier();
+
+ if (bytesLeft == 0)
+ this._notifier.done(message);
+ else
+ this._notifier.show(message);
+ }
+
+ borrowDialog() {
+ this._dialog?.disconnectObject(this);
+ return this._dialog;
+ }
+};
+
+var ShellUnmountNotifier = GObject.registerClass(
+class ShellUnmountNotifier extends MessageTray.Source {
+ _init() {
+ super._init('', 'media-removable');
+
+ this._notification = null;
+ Main.messageTray.add(this);
+ }
+
+ show(message) {
+ let [header, text] = message.split('\n', 2);
+
+ if (!this._notification) {
+ this._notification = new MessageTray.Notification(this, header, text);
+ this._notification.setTransient(true);
+ this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
+ } else {
+ this._notification.update(header, text);
+ }
+
+ this.showNotification(this._notification);
+ }
+
+ done(message) {
+ if (this._notification) {
+ this._notification.destroy();
+ this._notification = null;
+ }
+
+ if (message) {
+ let notification = new MessageTray.Notification(this, message, null);
+ notification.setTransient(true);
+
+ this.showNotification(notification);
+ }
+ }
+});
+
+var ShellMountQuestionDialog = GObject.registerClass({
+ Signals: { 'response': { param_types: [GObject.TYPE_INT] } },
+}, class ShellMountQuestionDialog extends ModalDialog.ModalDialog {
+ _init() {
+ super._init({ styleClass: 'mount-question-dialog' });
+
+ this._oldChoices = [];
+
+ this._content = new Dialog.MessageDialogContent();
+ this.contentLayout.add_child(this._content);
+ }
+
+ vfunc_key_release_event(event) {
+ if (event.keyval === Clutter.KEY_Escape) {
+ this.emit('response', -1);
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ update(message, choices) {
+ _setLabelsForMessage(this._content, message);
+ _setButtonsForChoices(this, this._oldChoices, choices);
+ this._oldChoices = choices;
+ }
+});
+
+var ShellMountPasswordDialog = GObject.registerClass({
+ Signals: {
+ 'response': {
+ param_types: [
+ GObject.TYPE_INT,
+ GObject.TYPE_STRING,
+ GObject.TYPE_BOOLEAN,
+ GObject.TYPE_BOOLEAN,
+ GObject.TYPE_BOOLEAN,
+ GObject.TYPE_UINT,
+ ],
+ },
+ },
+}, class ShellMountPasswordDialog extends ModalDialog.ModalDialog {
+ _init(message, flags) {
+ let strings = message.split('\n');
+ let title = strings.shift() || null;
+ let description = strings.shift() || null;
+ super._init({ styleClass: 'prompt-dialog' });
+
+ let disksApp = Shell.AppSystem.get_default().lookup_app('org.gnome.DiskUtility.desktop');
+
+ let content = new Dialog.MessageDialogContent({ title, description });
+
+ let passwordGridLayout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
+ let passwordGrid = new St.Widget({
+ style_class: 'prompt-dialog-password-grid',
+ layout_manager: passwordGridLayout,
+ });
+ passwordGridLayout.hookup_style(passwordGrid);
+
+ let rtl = passwordGrid.get_text_direction() === Clutter.TextDirection.RTL;
+ let curGridRow = 0;
+
+ if (flags & Gio.AskPasswordFlags.TCRYPT) {
+ this._hiddenVolume = new CheckBox.CheckBox(_("Hidden Volume"));
+ content.add_child(this._hiddenVolume);
+
+ this._systemVolume = new CheckBox.CheckBox(_("Windows System Volume"));
+ content.add_child(this._systemVolume);
+
+ this._keyfilesCheckbox = new CheckBox.CheckBox(_("Uses Keyfiles"));
+ this._keyfilesCheckbox.connect("clicked", this._onKeyfilesCheckboxClicked.bind(this));
+ content.add_child(this._keyfilesCheckbox);
+
+ this._keyfilesLabel = new St.Label({ visible: false });
+ this._keyfilesLabel.clutter_text.set_markup(
+ /* Translators: %s is the Disks application */
+ _('To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead.')
+ .format(disksApp.get_name()));
+ this._keyfilesLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._keyfilesLabel.clutter_text.line_wrap = true;
+ content.add_child(this._keyfilesLabel);
+
+ this._pimEntry = new St.PasswordEntry({
+ style_class: 'prompt-dialog-password-entry',
+ hint_text: _('PIM Number'),
+ can_focus: true,
+ x_expand: true,
+ });
+ this._pimEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
+ ShellEntry.addContextMenu(this._pimEntry);
+
+ if (rtl)
+ passwordGridLayout.attach(this._pimEntry, 1, curGridRow, 1, 1);
+ else
+ passwordGridLayout.attach(this._pimEntry, 0, curGridRow, 1, 1);
+ curGridRow += 1;
+ } else {
+ this._hiddenVolume = null;
+ this._systemVolume = null;
+ this._pimEntry = null;
+ }
+
+ this._passwordEntry = new St.PasswordEntry({
+ style_class: 'prompt-dialog-password-entry',
+ hint_text: _('Password'),
+ can_focus: true,
+ x_expand: true,
+ });
+ this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
+ this.setInitialKeyFocus(this._passwordEntry);
+ ShellEntry.addContextMenu(this._passwordEntry);
+
+ this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, {
+ animate: true,
+ });
+
+ if (rtl) {
+ passwordGridLayout.attach(this._workSpinner, 0, curGridRow, 1, 1);
+ passwordGridLayout.attach(this._passwordEntry, 1, curGridRow, 1, 1);
+ } else {
+ passwordGridLayout.attach(this._passwordEntry, 0, curGridRow, 1, 1);
+ passwordGridLayout.attach(this._workSpinner, 1, curGridRow, 1, 1);
+ }
+ curGridRow += 1;
+
+ let warningBox = new St.BoxLayout({ vertical: true });
+
+ let capsLockWarning = new ShellEntry.CapsLockWarning();
+ warningBox.add_child(capsLockWarning);
+
+ this._errorMessageLabel = new St.Label({
+ style_class: 'prompt-dialog-error-label',
+ opacity: 0,
+ });
+ this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._errorMessageLabel.clutter_text.line_wrap = true;
+ warningBox.add_child(this._errorMessageLabel);
+
+ passwordGridLayout.attach(warningBox, 0, curGridRow, 2, 1);
+
+ content.add_child(passwordGrid);
+
+ if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
+ this._rememberChoice = new CheckBox.CheckBox(_("Remember Password"));
+ this._rememberChoice.checked =
+ global.settings.get_boolean(REMEMBER_MOUNT_PASSWORD_KEY);
+ content.add_child(this._rememberChoice);
+ } else {
+ this._rememberChoice = null;
+ }
+
+ this.contentLayout.add_child(content);
+
+ this._defaultButtons = [{
+ label: _("Cancel"),
+ action: this._onCancelButton.bind(this),
+ key: Clutter.KEY_Escape,
+ }, {
+ label: _("Unlock"),
+ action: this._onUnlockButton.bind(this),
+ default: true,
+ }];
+
+ this._usesKeyfilesButtons = [{
+ label: _("Cancel"),
+ action: this._onCancelButton.bind(this),
+ key: Clutter.KEY_Escape,
+ }, {
+ /* Translators: %s is the Disks application */
+ label: _("Open %s").format(disksApp.get_name()),
+ action: this._onOpenDisksButton.bind(this),
+ default: true,
+ }];
+
+ this.setButtons(this._defaultButtons);
+ }
+
+ reaskPassword() {
+ this._workSpinner.stop();
+ this._passwordEntry.set_text('');
+ this._errorMessageLabel.text = _('Sorry, that didn’t work. Please try again.');
+ this._errorMessageLabel.opacity = 255;
+
+ Util.wiggle(this._passwordEntry);
+ }
+
+ _onCancelButton() {
+ this.emit('response', -1, '', false, false, false, 0);
+ }
+
+ _onUnlockButton() {
+ this._onEntryActivate();
+ }
+
+ _onEntryActivate() {
+ let pim = 0;
+ if (this._pimEntry !== null) {
+ pim = this._pimEntry.get_text();
+
+ if (isNaN(pim)) {
+ this._pimEntry.set_text('');
+ this._errorMessageLabel.text = _('The PIM must be a number or empty.');
+ this._errorMessageLabel.opacity = 255;
+ return;
+ }
+
+ this._errorMessageLabel.opacity = 0;
+ }
+
+ global.settings.set_boolean(REMEMBER_MOUNT_PASSWORD_KEY,
+ this._rememberChoice && this._rememberChoice.checked);
+
+ this._workSpinner.play();
+ this.emit('response', 1,
+ this._passwordEntry.get_text(),
+ this._rememberChoice &&
+ this._rememberChoice.checked,
+ this._hiddenVolume &&
+ this._hiddenVolume.checked,
+ this._systemVolume &&
+ this._systemVolume.checked,
+ parseInt(pim));
+ }
+
+ _onKeyfilesCheckboxClicked() {
+ let useKeyfiles = this._keyfilesCheckbox.checked;
+ this._passwordEntry.reactive = !useKeyfiles;
+ this._passwordEntry.can_focus = !useKeyfiles;
+ this._pimEntry.reactive = !useKeyfiles;
+ this._pimEntry.can_focus = !useKeyfiles;
+ this._rememberChoice.reactive = !useKeyfiles;
+ this._rememberChoice.can_focus = !useKeyfiles;
+ this._keyfilesLabel.visible = useKeyfiles;
+ this.setButtons(useKeyfiles ? this._usesKeyfilesButtons : this._defaultButtons);
+ }
+
+ _onOpenDisksButton() {
+ let app = Shell.AppSystem.get_default().lookup_app('org.gnome.DiskUtility.desktop');
+ if (app) {
+ app.activate();
+ } else {
+ Main.notifyError(
+ /* Translators: %s is the Disks application */
+ _("Unable to start %s").format(app.get_name()),
+ /* Translators: %s is the Disks application */
+ _('Couldn’t find the %s application').format(app.get_name()));
+ }
+ this._onCancelButton();
+ }
+});
+
+var ShellProcessesDialog = GObject.registerClass({
+ Signals: { 'response': { param_types: [GObject.TYPE_INT] } },
+}, class ShellProcessesDialog extends ModalDialog.ModalDialog {
+ _init() {
+ super._init({ styleClass: 'processes-dialog' });
+
+ this._oldChoices = [];
+
+ this._content = new Dialog.MessageDialogContent();
+ this.contentLayout.add_child(this._content);
+
+ this._applicationSection = new Dialog.ListSection();
+ this._applicationSection.hide();
+ this.contentLayout.add_child(this._applicationSection);
+ }
+
+ vfunc_key_release_event(event) {
+ if (event.keyval === Clutter.KEY_Escape) {
+ this.emit('response', -1);
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _setAppsForPids(pids) {
+ // remove all the items
+ this._applicationSection.list.destroy_all_children();
+
+ pids.forEach(pid => {
+ let tracker = Shell.WindowTracker.get_default();
+ let app = tracker.get_app_from_pid(pid);
+
+ if (!app)
+ return;
+
+ let listItem = new Dialog.ListSectionItem({
+ icon_actor: app.create_icon_texture(LIST_ITEM_ICON_SIZE),
+ title: app.get_name(),
+ });
+ this._applicationSection.list.add_child(listItem);
+ });
+
+ this._applicationSection.visible =
+ this._applicationSection.list.get_n_children() > 0;
+ }
+
+ update(message, processes, choices) {
+ this._setAppsForPids(processes);
+ _setLabelsForMessage(this._content, message);
+ _setButtonsForChoices(this, this._oldChoices, choices);
+ this._oldChoices = choices;
+ }
+});
+
+const GnomeShellMountOpIface = loadInterfaceXML('org.Gtk.MountOperationHandler');
+
+var ShellMountOperationType = {
+ NONE: 0,
+ ASK_PASSWORD: 1,
+ ASK_QUESTION: 2,
+ SHOW_PROCESSES: 3,
+};
+
+var GnomeShellMountOpHandler = class {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
+ Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
+ Gio.BusNameOwnerFlags.REPLACE, null, null);
+
+ this._dialog = null;
+
+ this._ensureEmptyRequest();
+ }
+
+ _ensureEmptyRequest() {
+ this._currentId = null;
+ this._currentInvocation = null;
+ this._currentType = ShellMountOperationType.NONE;
+ }
+
+ _clearCurrentRequest(response, details) {
+ if (this._currentInvocation) {
+ this._currentInvocation.return_value(
+ GLib.Variant.new('(ua{sv})', [response, details]));
+ }
+
+ this._ensureEmptyRequest();
+ }
+
+ _setCurrentRequest(invocation, id, type) {
+ let oldId = this._currentId;
+ let oldType = this._currentType;
+ let requestId = `${id}@${invocation.get_sender()}`;
+
+ this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
+
+ this._currentInvocation = invocation;
+ this._currentId = requestId;
+ this._currentType = type;
+
+ if (this._dialog && (oldId == requestId) && (oldType == type))
+ return true;
+
+ return false;
+ }
+
+ _closeDialog() {
+ if (this._dialog) {
+ this._dialog.close();
+ this._dialog = null;
+ }
+ }
+
+ /**
+ * AskPassword:
+ * @param {Array} params
+ * {string} id: an opaque ID identifying the object for which
+ * the operation is requested
+ * {string} message: the message to display
+ * {string} icon_name: the name of an icon to display
+ * {string} default_user: the default username for display
+ * {string} default_domain: the default domain for display
+ * {Gio.AskPasswordFlags} flags: a set of GAskPasswordFlags
+ * {Gio.MountOperationResults} response: a GMountOperationResult
+ * {Object} response_details: a dictionary containing response details as
+ * entered by the user. The dictionary MAY contain the following
+ * properties:
+ * - "password" -> (s): a password to be used to complete the mount operation
+ * - "password_save" -> (u): a GPasswordSave
+ * @param {Gio.DBusMethodInvocation} invocation
+ * The ID must be unique in the context of the calling process.
+ *
+ * The dialog will stay visible until clients call the Close() method, or
+ * another dialog becomes visible.
+ * Calling AskPassword again for the same id will have the effect to clear
+ * the existing dialog and update it with a message indicating the previous
+ * attempt went wrong.
+ */
+ AskPasswordAsync(params, invocation) {
+ let [id, message, iconName_, defaultUser_, defaultDomain_, flags] = params;
+
+ if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
+ this._dialog.reaskPassword();
+ return;
+ }
+
+ this._closeDialog();
+
+ this._dialog = new ShellMountPasswordDialog(message, flags);
+ this._dialog.connect('response',
+ (object, choice, password, remember, hiddenVolume, systemVolume, pim) => {
+ let details = {};
+ let response;
+
+ if (choice == -1) {
+ response = Gio.MountOperationResult.ABORTED;
+ } else {
+ response = Gio.MountOperationResult.HANDLED;
+
+ let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
+ details['password_save'] = GLib.Variant.new('u', passSave);
+ details['password'] = GLib.Variant.new('s', password);
+ details['hidden_volume'] = GLib.Variant.new('b', hiddenVolume);
+ details['system_volume'] = GLib.Variant.new('b', systemVolume);
+ details['pim'] = GLib.Variant.new('u', pim);
+ }
+
+ this._clearCurrentRequest(response, details);
+ });
+ this._dialog.open();
+ }
+
+ /**
+ * AskQuestion:
+ * @param {Array} params - params
+ * {string} id: an opaque ID identifying the object for which
+ * the operation is requested
+ * The ID must be unique in the context of the calling process.
+ * {string} message: the message to display
+ * {string} icon_name: the name of an icon to display
+ * {string[]} choices: an array of choice strings
+ * @param {Gio.DBusMethodInvocation} invocation - invocation
+ *
+ * The dialog will stay visible until clients call the Close() method, or
+ * another dialog becomes visible.
+ * Calling AskQuestion again for the same id will have the effect to clear
+ * update the dialog with the new question.
+ */
+ AskQuestionAsync(params, invocation) {
+ let [id, message, iconName_, choices] = params;
+
+ if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
+ this._dialog.update(message, choices);
+ return;
+ }
+
+ this._closeDialog();
+
+ this._dialog = new ShellMountQuestionDialog(message);
+ this._dialog.connect('response', (object, choice) => {
+ let response;
+ let details = {};
+
+ if (choice == -1) {
+ response = Gio.MountOperationResult.ABORTED;
+ } else {
+ response = Gio.MountOperationResult.HANDLED;
+ details['choice'] = GLib.Variant.new('i', choice);
+ }
+
+ this._clearCurrentRequest(response, details);
+ });
+
+ this._dialog.update(message, choices);
+ this._dialog.open();
+ }
+
+ /**
+ * ShowProcesses:
+ * @param {Array} params - params
+ * {string} id: an opaque ID identifying the object for which
+ * the operation is requested
+ * The ID must be unique in the context of the calling process.
+ * {string} message: the message to display
+ * {string} icon_name: the name of an icon to display
+ * {number[]} application_pids: the PIDs of the applications to display
+ * {string[]} choices: an array of choice strings
+ * @param {Gio.DBusMethodInvocation} invocation - invocation
+ *
+ * The dialog will stay visible until clients call the Close() method, or
+ * another dialog becomes visible.
+ * Calling ShowProcesses again for the same id will have the effect to clear
+ * the existing dialog and update it with the new message and the new list
+ * of processes.
+ */
+ ShowProcessesAsync(params, invocation) {
+ let [id, message, iconName_, applicationPids, choices] = params;
+
+ if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
+ this._dialog.update(message, applicationPids, choices);
+ return;
+ }
+
+ this._closeDialog();
+
+ this._dialog = new ShellProcessesDialog();
+ this._dialog.connect('response', (object, choice) => {
+ let response;
+ let details = {};
+
+ if (choice == -1) {
+ response = Gio.MountOperationResult.ABORTED;
+ } else {
+ response = Gio.MountOperationResult.HANDLED;
+ details['choice'] = GLib.Variant.new('i', choice);
+ }
+
+ this._clearCurrentRequest(response, details);
+ });
+
+ this._dialog.update(message, applicationPids, choices);
+ this._dialog.open();
+ }
+
+ /**
+ * Close:
+ * @param {Array} _params - params
+ * @param {Gio.DBusMethodInvocation} _invocation - invocation
+ *
+ * Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
+ * If no dialog is open, does nothing.
+ */
+ Close(_params, _invocation) {
+ this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
+ this._closeDialog();
+ }
+};
diff --git a/js/ui/slider.js b/js/ui/slider.js
new file mode 100644
index 0000000..849599d
--- /dev/null
+++ b/js/ui/slider.js
@@ -0,0 +1,218 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* exported Slider */
+
+const { Atk, Clutter, GObject } = imports.gi;
+
+const BarLevel = imports.ui.barLevel;
+
+var SLIDER_SCROLL_STEP = 0.02; /* Slider scrolling step in % */
+
+var Slider = GObject.registerClass({
+ Signals: {
+ 'drag-begin': {},
+ 'drag-end': {},
+ },
+}, class Slider extends BarLevel.BarLevel {
+ _init(value) {
+ super._init({
+ value,
+ style_class: 'slider',
+ can_focus: true,
+ reactive: true,
+ accessible_role: Atk.Role.SLIDER,
+ x_expand: true,
+ });
+
+ this._releaseId = 0;
+ this._dragging = false;
+
+ this._customAccessible.connect('get-minimum-increment', this._getMinimumIncrement.bind(this));
+ }
+
+ vfunc_repaint() {
+ super.vfunc_repaint();
+
+ // Add handle
+ let cr = this.get_context();
+ let themeNode = this.get_theme_node();
+ let [width, height] = this.get_surface_size();
+ const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+
+ let handleRadius = themeNode.get_length('-slider-handle-radius');
+
+ let handleBorderWidth = themeNode.get_length('-slider-handle-border-width');
+ let [hasHandleColor, handleBorderColor] =
+ themeNode.lookup_color('-slider-handle-border-color', false);
+
+ const ceiledHandleRadius = Math.ceil(handleRadius + handleBorderWidth);
+ const handleY = height / 2;
+
+ let handleX = ceiledHandleRadius +
+ (width - 2 * ceiledHandleRadius) * this._value / this._maxValue;
+ if (rtl)
+ handleX = width - handleX;
+
+ let color = themeNode.get_foreground_color();
+ Clutter.cairo_set_source_color(cr, color);
+ cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
+ cr.fillPreserve();
+ if (hasHandleColor && handleBorderWidth) {
+ Clutter.cairo_set_source_color(cr, handleBorderColor);
+ cr.setLineWidth(handleBorderWidth);
+ cr.stroke();
+ }
+ cr.$dispose();
+ }
+
+ vfunc_button_press_event() {
+ return this.startDragging(Clutter.get_current_event());
+ }
+
+ startDragging(event) {
+ if (this._dragging)
+ return Clutter.EVENT_PROPAGATE;
+
+ this._dragging = true;
+
+ let device = event.get_device();
+ let sequence = event.get_event_sequence();
+
+ this._grab = global.stage.grab(this);
+
+ this._grabbedDevice = device;
+ this._grabbedSequence = sequence;
+
+ // We need to emit 'drag-begin' before moving the handle to make
+ // sure that no 'notify::value' signal is emitted before this one.
+ this.emit('drag-begin');
+
+ let absX, absY;
+ [absX, absY] = event.get_coords();
+ this._moveHandle(absX, absY);
+ return Clutter.EVENT_STOP;
+ }
+
+ _endDragging() {
+ if (this._dragging) {
+ if (this._releaseId) {
+ this.disconnect(this._releaseId);
+ this._releaseId = 0;
+ }
+
+ if (this._grab) {
+ this._grab.dismiss();
+ this._grab = null;
+ }
+
+ this._grabbedSequence = null;
+ this._grabbedDevice = null;
+ this._dragging = false;
+
+ this.emit('drag-end');
+ }
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_button_release_event() {
+ if (this._dragging && !this._grabbedSequence)
+ return this._endDragging();
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_touch_event() {
+ let event = Clutter.get_current_event();
+ let sequence = event.get_event_sequence();
+
+ if (!this._dragging &&
+ event.type() == Clutter.EventType.TOUCH_BEGIN) {
+ this.startDragging(event);
+ return Clutter.EVENT_STOP;
+ } else if (this._grabbedSequence &&
+ sequence.get_slot() === this._grabbedSequence.get_slot()) {
+ if (event.type() == Clutter.EventType.TOUCH_UPDATE)
+ return this._motionEvent(this, event);
+ else if (event.type() == Clutter.EventType.TOUCH_END)
+ return this._endDragging();
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ scroll(event) {
+ let direction = event.get_scroll_direction();
+ let delta;
+
+ if (event.is_pointer_emulated())
+ return Clutter.EVENT_PROPAGATE;
+
+ if (direction == Clutter.ScrollDirection.DOWN) {
+ delta = -SLIDER_SCROLL_STEP;
+ } else if (direction == Clutter.ScrollDirection.UP) {
+ delta = SLIDER_SCROLL_STEP;
+ } else if (direction == Clutter.ScrollDirection.SMOOTH) {
+ let [, dy] = event.get_scroll_delta();
+ // Even though the slider is horizontal, use dy to match
+ // the UP/DOWN above.
+ delta = -dy * SLIDER_SCROLL_STEP;
+ }
+
+ this.value = Math.min(Math.max(0, this._value + delta), this._maxValue);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_scroll_event() {
+ return this.scroll(Clutter.get_current_event());
+ }
+
+ vfunc_motion_event() {
+ if (this._dragging && !this._grabbedSequence)
+ return this._motionEvent(this, Clutter.get_current_event());
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _motionEvent(actor, event) {
+ let absX, absY;
+ [absX, absY] = event.get_coords();
+ this._moveHandle(absX, absY);
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_key_press_event(keyPressEvent) {
+ let key = keyPressEvent.keyval;
+ if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
+ let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
+ this.value = Math.max(0, Math.min(this._value + delta, this._maxValue));
+ return Clutter.EVENT_STOP;
+ }
+ return super.vfunc_key_press_event(keyPressEvent);
+ }
+
+ _moveHandle(absX, _absY) {
+ let relX, sliderX;
+ [sliderX] = this.get_transformed_position();
+ const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+ let width = this._barLevelWidth;
+
+ relX = absX - sliderX;
+ if (rtl)
+ relX = width - relX;
+
+ let handleRadius = this.get_theme_node().get_length('-slider-handle-radius');
+
+ let newvalue;
+ if (relX < handleRadius)
+ newvalue = 0;
+ else if (relX > width - handleRadius)
+ newvalue = 1;
+ else
+ newvalue = (relX - handleRadius) / (width - 2 * handleRadius);
+ this.value = newvalue * this._maxValue;
+ }
+
+ _getMinimumIncrement() {
+ return 0.1;
+ }
+});
diff --git a/js/ui/status/accessibility.js b/js/ui/status/accessibility.js
new file mode 100644
index 0000000..a4bad14
--- /dev/null
+++ b/js/ui/status/accessibility.js
@@ -0,0 +1,153 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ATIndicator */
+
+const { Gio, GLib, GObject, St } = imports.gi;
+
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+
+const A11Y_SCHEMA = 'org.gnome.desktop.a11y';
+const KEY_ALWAYS_SHOW = 'always-show-universal-access-status';
+
+const A11Y_KEYBOARD_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
+const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
+const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
+const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
+const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
+
+const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
+
+var DPI_FACTOR_LARGE = 1.25;
+
+const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
+const KEY_VISUAL_BELL = 'visual-bell';
+
+const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
+const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
+
+const A11Y_INTERFACE_SCHEMA = 'org.gnome.desktop.a11y.interface';
+const KEY_HIGH_CONTRAST = 'high-contrast';
+
+var ATIndicator = GObject.registerClass(
+class ATIndicator extends PanelMenu.Button {
+ _init() {
+ super._init(0.5, _("Accessibility"));
+
+ this.add_child(new St.Icon({
+ style_class: 'system-status-icon',
+ icon_name: 'org.gnome.Settings-accessibility-symbolic',
+ }));
+
+ this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
+ this._a11ySettings.connect(`changed::${KEY_ALWAYS_SHOW}`, this._queueSyncMenuVisibility.bind(this));
+
+ let highContrast = this._buildItem(_('High Contrast'), A11Y_INTERFACE_SCHEMA, KEY_HIGH_CONTRAST);
+ this.menu.addMenuItem(highContrast);
+
+ let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA,
+ 'screen-magnifier-enabled');
+ this.menu.addMenuItem(magnifier);
+
+ let textZoom = this._buildFontItem();
+ this.menu.addMenuItem(textZoom);
+
+ let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
+ 'screen-reader-enabled');
+ this.menu.addMenuItem(screenReader);
+
+ let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
+ 'screen-keyboard-enabled');
+ this.menu.addMenuItem(screenKeyboard);
+
+ let visualBell = this._buildItem(_("Visual Alerts"), WM_SCHEMA, KEY_VISUAL_BELL);
+ this.menu.addMenuItem(visualBell);
+
+ let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_KEYBOARD_SCHEMA, KEY_STICKY_KEYS_ENABLED);
+ this.menu.addMenuItem(stickyKeys);
+
+ let slowKeys = this._buildItem(_("Slow Keys"), A11Y_KEYBOARD_SCHEMA, KEY_SLOW_KEYS_ENABLED);
+ this.menu.addMenuItem(slowKeys);
+
+ let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_KEYBOARD_SCHEMA, KEY_BOUNCE_KEYS_ENABLED);
+ this.menu.addMenuItem(bounceKeys);
+
+ let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_KEYBOARD_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
+ this.menu.addMenuItem(mouseKeys);
+
+ this._syncMenuVisibility();
+ }
+
+ _syncMenuVisibility() {
+ this._syncMenuVisibilityIdle = 0;
+
+ let alwaysShow = this._a11ySettings.get_boolean(KEY_ALWAYS_SHOW);
+ let items = this.menu._getMenuItems();
+
+ this.visible = alwaysShow || items.some(f => !!f.state);
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _queueSyncMenuVisibility() {
+ if (this._syncMenuVisibilityIdle)
+ return;
+
+ this._syncMenuVisibilityIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, this._syncMenuVisibility.bind(this));
+ GLib.Source.set_name_by_id(this._syncMenuVisibilityIdle, '[gnome-shell] this._syncMenuVisibility');
+ }
+
+ _buildItemExtended(string, initialValue, writable, onSet) {
+ let widget = new PopupMenu.PopupSwitchMenuItem(string, initialValue);
+ if (!writable) {
+ widget.reactive = false;
+ } else {
+ widget.connect('toggled', item => {
+ onSet(item.state);
+ });
+ }
+ return widget;
+ }
+
+ _buildItem(string, schema, key) {
+ let settings = new Gio.Settings({ schema_id: schema });
+ let widget = this._buildItemExtended(string,
+ settings.get_boolean(key),
+ settings.is_writable(key),
+ enabled => settings.set_boolean(key, enabled));
+
+ settings.connect(`changed::${key}`, () => {
+ widget.setToggleState(settings.get_boolean(key));
+
+ this._queueSyncMenuVisibility();
+ });
+
+ return widget;
+ }
+
+ _buildFontItem() {
+ let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA });
+ let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
+ let initialSetting = factor > 1.0;
+ let widget = this._buildItemExtended(_("Large Text"),
+ initialSetting,
+ settings.is_writable(KEY_TEXT_SCALING_FACTOR),
+ enabled => {
+ if (enabled) {
+ settings.set_double(
+ KEY_TEXT_SCALING_FACTOR, DPI_FACTOR_LARGE);
+ } else {
+ settings.reset(KEY_TEXT_SCALING_FACTOR);
+ }
+ });
+
+ settings.connect(`changed::${KEY_TEXT_SCALING_FACTOR}`, () => {
+ factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
+ let active = factor > 1.0;
+ widget.setToggleState(active);
+
+ this._queueSyncMenuVisibility();
+ });
+
+ return widget;
+ }
+});
diff --git a/js/ui/status/autoRotate.js b/js/ui/status/autoRotate.js
new file mode 100644
index 0000000..bde3b80
--- /dev/null
+++ b/js/ui/status/autoRotate.js
@@ -0,0 +1,45 @@
+/* exported Indicator */
+const {Gio, GObject} = imports.gi;
+
+const SystemActions = imports.misc.systemActions;
+
+const {QuickToggle, SystemIndicator} = imports.ui.quickSettings;
+
+const RotationToggle = GObject.registerClass(
+class RotationToggle extends QuickToggle {
+ _init() {
+ this._systemActions = new SystemActions.getDefault();
+
+ super._init({
+ label: _('Auto Rotate'),
+ });
+
+ this._systemActions.bind_property('can-lock-orientation',
+ this, 'visible',
+ GObject.BindingFlags.DEFAULT |
+ GObject.BindingFlags.SYNC_CREATE);
+ this._systemActions.bind_property('orientation-lock-icon',
+ this, 'icon-name',
+ GObject.BindingFlags.DEFAULT |
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.settings-daemon.peripherals.touchscreen',
+ });
+ this._settings.bind('orientation-lock',
+ this, 'checked',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+
+ this.connect('clicked',
+ () => this._systemActions.activateLockOrientation());
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this.quickSettingsItems.push(new RotationToggle());
+ }
+});
diff --git a/js/ui/status/bluetooth.js b/js/ui/status/bluetooth.js
new file mode 100644
index 0000000..bbff62d
--- /dev/null
+++ b/js/ui/status/bluetooth.js
@@ -0,0 +1,211 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const {Gio, GLib, GnomeBluetooth, GObject} = imports.gi;
+
+const {QuickToggle, SystemIndicator} = imports.ui.quickSettings;
+
+const {loadInterfaceXML} = imports.misc.fileUtils;
+
+const {AdapterState} = GnomeBluetooth;
+
+const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
+const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
+
+const RfkillManagerInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Rfkill');
+const rfkillManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(RfkillManagerInterface);
+
+const BtClient = GObject.registerClass({
+ Properties: {
+ 'available': GObject.ParamSpec.boolean('available', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'active': GObject.ParamSpec.boolean('active', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'adapter-state': GObject.ParamSpec.enum('adapter-state', '', '',
+ GObject.ParamFlags.READABLE,
+ AdapterState, AdapterState.ABSENT),
+ },
+ Signals: {
+ 'devices-changed': {},
+ },
+}, class BtClient extends GObject.Object {
+ _init() {
+ super._init();
+
+ this._client = new GnomeBluetooth.Client();
+ this._client.connect('notify::default-adapter-powered', () => {
+ this.notify('active');
+ this.notify('available');
+ });
+ this._client.connect('notify::default-adapter-state',
+ () => this.notify('adapter-state'));
+ this._client.connect('notify::default-adapter', () => {
+ const newAdapter = this._client.default_adapter ?? null;
+
+ this._adapter = newAdapter;
+ this._deviceNotifyConnected.clear();
+ this.emit('devices-changed');
+
+ this.notify('active');
+ this.notify('available');
+ });
+
+ this._proxy = new Gio.DBusProxy({
+ g_connection: Gio.DBus.session,
+ g_name: BUS_NAME,
+ g_object_path: OBJECT_PATH,
+ g_interface_name: rfkillManagerInfo.name,
+ g_interface_info: rfkillManagerInfo,
+ });
+ this._proxy.connect('g-properties-changed', (p, properties) => {
+ const changedProperties = properties.unpack();
+ if ('BluetoothHardwareAirplaneMode' in changedProperties)
+ this.notify('available');
+ else if ('BluetoothHasAirplaneMode' in changedProperties)
+ this.notify('available');
+ });
+ this._proxy.init_async(GLib.PRIORITY_DEFAULT, null)
+ .catch(e => console.error(e.message));
+
+ this._adapter = null;
+
+ this._deviceNotifyConnected = new Set();
+
+ const deviceStore = this._client.get_devices();
+ for (let i = 0; i < deviceStore.get_n_items(); i++)
+ this._connectDeviceNotify(deviceStore.get_item(i));
+
+ this._client.connect('device-removed', (c, path) => {
+ this._deviceNotifyConnected.delete(path);
+ this.emit('devices-changed');
+ });
+ this._client.connect('device-added', (c, device) => {
+ this._connectDeviceNotify(device);
+ this.emit('devices-changed');
+ });
+ }
+
+ get available() {
+ // If we have an rfkill switch, make sure it's not a hardware
+ // one as we can't get out of it in software
+ return this._proxy.BluetoothHasAirplaneMode
+ ? !this._proxy.BluetoothHardwareAirplaneMode
+ : this.active;
+ }
+
+ get active() {
+ return this._client.default_adapter_powered;
+ }
+
+ get adapter_state() {
+ return this._client.default_adapter_state;
+ }
+
+ toggleActive() {
+ this._proxy.BluetoothAirplaneMode = this.active;
+ if (!this._client.default_adapter_powered)
+ this._client.default_adapter_powered = true;
+ }
+
+ *getDevices() {
+ const deviceStore = this._client.get_devices();
+
+ for (let i = 0; i < deviceStore.get_n_items(); i++) {
+ const device = deviceStore.get_item(i);
+
+ if (device.paired || device.trusted)
+ yield device;
+ }
+ }
+
+ _queueDevicesChanged() {
+ if (this._devicesChangedId)
+ return;
+ this._devicesChangedId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ delete this._devicesChangedId;
+ this.emit('devices-changed');
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _connectDeviceNotify(device) {
+ const path = device.get_object_path();
+
+ if (this._deviceNotifyConnected.has(path))
+ return;
+
+ device.connect('notify::alias', () => this._queueDevicesChanged());
+ device.connect('notify::paired', () => this._queueDevicesChanged());
+ device.connect('notify::trusted', () => this._queueDevicesChanged());
+ device.connect('notify::connected', () => this._queueDevicesChanged());
+
+ this._deviceNotifyConnected.add(path);
+ }
+});
+
+const BluetoothToggle = GObject.registerClass(
+class BluetoothToggle extends QuickToggle {
+ _init(client) {
+ super._init({label: _('Bluetooth')});
+
+ this._client = client;
+
+ this._client.bind_property('available',
+ this, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._client.bind_property('active',
+ this, 'checked',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._client.bind_property_full('adapter-state',
+ this, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE,
+ (bind, source) => [true, this._getIconNameFromState(source)],
+ null);
+
+ this.connect('clicked', () => this._client.toggleActive());
+ }
+
+ _getIconNameFromState(state) {
+ switch (state) {
+ case AdapterState.ON:
+ return 'bluetooth-active-symbolic';
+ case AdapterState.OFF:
+ case AdapterState.ABSENT:
+ return 'bluetooth-disabled-symbolic';
+ case AdapterState.TURNING_ON:
+ case AdapterState.TURNING_OFF:
+ return 'bluetooth-acquiring-symbolic';
+ default:
+ console.warn(`Unexpected state ${
+ GObject.enum_to_string(AdapterState, state)}`);
+ return '';
+ }
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._client = new BtClient();
+ this._client.connect('devices-changed', () => this._sync());
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'bluetooth-active-symbolic';
+
+ this.quickSettingsItems.push(new BluetoothToggle(this._client));
+
+ this._sync();
+ }
+
+ _sync() {
+ const devices = [...this._client.getDevices()];
+ const connectedDevices = devices.filter(dev => dev.connected);
+ const nConnectedDevices = connectedDevices.length;
+
+ this._indicator.visible = nConnectedDevices > 0;
+ }
+});
diff --git a/js/ui/status/brightness.js b/js/ui/status/brightness.js
new file mode 100644
index 0000000..4c0da67
--- /dev/null
+++ b/js/ui/status/brightness.js
@@ -0,0 +1,64 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const {Gio, GObject} = imports.gi;
+
+const {QuickSlider, SystemIndicator} = imports.ui.quickSettings;
+
+const {loadInterfaceXML} = imports.misc.fileUtils;
+
+const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
+const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
+
+const BrightnessInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Power.Screen');
+const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
+
+const BrightnessItem = GObject.registerClass(
+class BrightnessItem extends QuickSlider {
+ _init() {
+ super._init({
+ iconName: 'display-brightness-symbolic',
+ });
+
+ this._proxy = new BrightnessProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
+ (proxy, error) => {
+ if (error)
+ console.error(error.message);
+ else
+ this._proxy.connect('g-properties-changed', () => this._sync());
+ this._sync();
+ });
+
+ this._sliderChangedId = this.slider.connect('notify::value',
+ this._sliderChanged.bind(this));
+ this.slider.accessible_name = _('Brightness');
+ }
+
+ _sliderChanged() {
+ const percent = this.slider.value * 100;
+ this._proxy.Brightness = percent;
+ }
+
+ _changeSlider(value) {
+ this.slider.block_signal_handler(this._sliderChangedId);
+ this.slider.value = value;
+ this.slider.unblock_signal_handler(this._sliderChangedId);
+ }
+
+ _sync() {
+ const brightness = this._proxy.Brightness;
+ const visible = Number.isInteger(brightness) && brightness >= 0;
+ this.visible = visible;
+ if (visible)
+ this._changeSlider(this._proxy.Brightness / 100.0);
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this.quickSettingsItems.push(new BrightnessItem());
+ }
+});
diff --git a/js/ui/status/darkMode.js b/js/ui/status/darkMode.js
new file mode 100644
index 0000000..d1ec2bd
--- /dev/null
+++ b/js/ui/status/darkMode.js
@@ -0,0 +1,49 @@
+/* exported Indicator */
+const {Gio, GObject} = imports.gi;
+
+const Main = imports.ui.main;
+const {QuickToggle, SystemIndicator} = imports.ui.quickSettings;
+
+const DarkModeToggle = GObject.registerClass(
+class DarkModeToggle extends QuickToggle {
+ _init() {
+ super._init({
+ label: _('Dark Mode'),
+ iconName: 'dark-mode-symbolic',
+ });
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.interface',
+ });
+ this._changedId = this._settings.connect('changed::color-scheme',
+ () => this._sync());
+
+ this.connectObject(
+ 'destroy', () => this._settings.run_dispose(),
+ 'clicked', () => this._toggleMode(),
+ this);
+ this._sync();
+ }
+
+ _toggleMode() {
+ Main.layoutManager.screenTransition.run();
+ this._settings.set_string('color-scheme',
+ this.checked ? 'default' : 'prefer-dark');
+ }
+
+ _sync() {
+ const colorScheme = this._settings.get_string('color-scheme');
+ const checked = colorScheme === 'prefer-dark';
+ if (this.checked !== checked)
+ this.set({checked});
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this.quickSettingsItems.push(new DarkModeToggle());
+ }
+});
diff --git a/js/ui/status/dwellClick.js b/js/ui/status/dwellClick.js
new file mode 100644
index 0000000..82726e5
--- /dev/null
+++ b/js/ui/status/dwellClick.js
@@ -0,0 +1,83 @@
+/* exported DwellClickIndicator */
+const { Clutter, Gio, GLib, GObject, St } = imports.gi;
+
+const PanelMenu = imports.ui.panelMenu;
+
+const MOUSE_A11Y_SCHEMA = 'org.gnome.desktop.a11y.mouse';
+const KEY_DWELL_CLICK_ENABLED = 'dwell-click-enabled';
+const KEY_DWELL_MODE = 'dwell-mode';
+const DWELL_MODE_WINDOW = 'window';
+const DWELL_CLICK_MODES = {
+ primary: {
+ name: _("Single Click"),
+ icon: 'pointer-primary-click-symbolic',
+ type: Clutter.PointerA11yDwellClickType.PRIMARY,
+ },
+ double: {
+ name: _("Double Click"),
+ icon: 'pointer-double-click-symbolic',
+ type: Clutter.PointerA11yDwellClickType.DOUBLE,
+ },
+ drag: {
+ name: _("Drag"),
+ icon: 'pointer-drag-symbolic',
+ type: Clutter.PointerA11yDwellClickType.DRAG,
+ },
+ secondary: {
+ name: _("Secondary Click"),
+ icon: 'pointer-secondary-click-symbolic',
+ type: Clutter.PointerA11yDwellClickType.SECONDARY,
+ },
+};
+
+var DwellClickIndicator = GObject.registerClass(
+class DwellClickIndicator extends PanelMenu.Button {
+ _init() {
+ super._init(0.5, _("Dwell Click"));
+
+ this._icon = new St.Icon({
+ style_class: 'system-status-icon',
+ icon_name: 'pointer-primary-click-symbolic',
+ });
+ this.add_child(this._icon);
+
+ this._a11ySettings = new Gio.Settings({ schema_id: MOUSE_A11Y_SCHEMA });
+ this._a11ySettings.connect(`changed::${KEY_DWELL_CLICK_ENABLED}`, this._syncMenuVisibility.bind(this));
+ this._a11ySettings.connect(`changed::${KEY_DWELL_MODE}`, this._syncMenuVisibility.bind(this));
+
+ this._seat = Clutter.get_default_backend().get_default_seat();
+ this._seat.connect('ptr-a11y-dwell-click-type-changed', this._updateClickType.bind(this));
+
+ this._addDwellAction(DWELL_CLICK_MODES.primary);
+ this._addDwellAction(DWELL_CLICK_MODES.double);
+ this._addDwellAction(DWELL_CLICK_MODES.drag);
+ this._addDwellAction(DWELL_CLICK_MODES.secondary);
+
+ this._setClickType(DWELL_CLICK_MODES.primary);
+ this._syncMenuVisibility();
+ }
+
+ _syncMenuVisibility() {
+ this.visible =
+ this._a11ySettings.get_boolean(KEY_DWELL_CLICK_ENABLED) &&
+ this._a11ySettings.get_string(KEY_DWELL_MODE) == DWELL_MODE_WINDOW;
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _addDwellAction(mode) {
+ this.menu.addAction(mode.name, this._setClickType.bind(this, mode), mode.icon);
+ }
+
+ _updateClickType(manager, clickType) {
+ for (let mode in DWELL_CLICK_MODES) {
+ if (DWELL_CLICK_MODES[mode].type == clickType)
+ this._icon.icon_name = DWELL_CLICK_MODES[mode].icon;
+ }
+ }
+
+ _setClickType(mode) {
+ this._seat.set_pointer_a11y_dwell_click_type(mode.type);
+ this._icon.icon_name = mode.icon;
+ }
+});
diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js
new file mode 100644
index 0000000..b47375d
--- /dev/null
+++ b/js/ui/status/keyboard.js
@@ -0,0 +1,1095 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported InputSourceIndicator */
+
+const { Clutter, Gio, GLib, GObject, IBus, Meta, Shell, St } = imports.gi;
+const Gettext = imports.gettext;
+const Signals = imports.misc.signals;
+
+const IBusManager = imports.misc.ibusManager;
+const KeyboardManager = imports.misc.keyboardManager;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const PanelMenu = imports.ui.panelMenu;
+const SwitcherPopup = imports.ui.switcherPopup;
+const Util = imports.misc.util;
+
+var INPUT_SOURCE_TYPE_XKB = 'xkb';
+var INPUT_SOURCE_TYPE_IBUS = 'ibus';
+
+var LayoutMenuItem = GObject.registerClass(
+class LayoutMenuItem extends PopupMenu.PopupBaseMenuItem {
+ _init(displayName, shortName) {
+ super._init();
+
+ this.label = new St.Label({
+ text: displayName,
+ x_expand: true,
+ });
+ this.indicator = new St.Label({ text: shortName });
+ this.add_child(this.label);
+ this.add(this.indicator);
+ this.label_actor = this.label;
+ }
+});
+
+var InputSource = class extends Signals.EventEmitter {
+ constructor(type, id, displayName, shortName, index) {
+ super();
+
+ this.type = type;
+ this.id = id;
+ this.displayName = displayName;
+ this._shortName = shortName;
+ this.index = index;
+
+ this.properties = null;
+
+ this.xkbId = this._getXkbId();
+ }
+
+ get shortName() {
+ return this._shortName;
+ }
+
+ set shortName(v) {
+ this._shortName = v;
+ this.emit('changed');
+ }
+
+ activate(interactive) {
+ this.emit('activate', !!interactive);
+ }
+
+ _getXkbId() {
+ let engineDesc = IBusManager.getIBusManager().getEngineDesc(this.id);
+ if (!engineDesc)
+ return this.id;
+
+ if (engineDesc.variant && engineDesc.variant.length > 0)
+ return `${engineDesc.layout}+${engineDesc.variant}`;
+ else
+ return engineDesc.layout;
+ }
+};
+
+var InputSourcePopup = GObject.registerClass(
+class InputSourcePopup extends SwitcherPopup.SwitcherPopup {
+ _init(items, action, actionBackward) {
+ super._init(items);
+
+ this._action = action;
+ this._actionBackward = actionBackward;
+
+ this._switcherList = new InputSourceSwitcher(this._items);
+ }
+
+ _keyPressHandler(keysym, action) {
+ if (action == this._action)
+ this._select(this._next());
+ else if (action == this._actionBackward)
+ this._select(this._previous());
+ else if (keysym == Clutter.KEY_Left)
+ this._select(this._previous());
+ else if (keysym == Clutter.KEY_Right)
+ this._select(this._next());
+ else
+ return Clutter.EVENT_PROPAGATE;
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _finish() {
+ super._finish();
+
+ this._items[this._selectedIndex].activate(true);
+ }
+});
+
+var InputSourceSwitcher = GObject.registerClass(
+class InputSourceSwitcher extends SwitcherPopup.SwitcherList {
+ _init(items) {
+ super._init(true);
+
+ for (let i = 0; i < items.length; i++)
+ this._addIcon(items[i]);
+ }
+
+ _addIcon(item) {
+ let box = new St.BoxLayout({ vertical: true });
+
+ let bin = new St.Bin({ style_class: 'input-source-switcher-symbol' });
+ let symbol = new St.Label({
+ text: item.shortName,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ bin.set_child(symbol);
+ box.add_child(bin);
+
+ let text = new St.Label({
+ text: item.displayName,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add_child(text);
+
+ this.addItem(box, text);
+ }
+});
+
+var InputSourceSettings = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ if (this.constructor === InputSourceSettings)
+ throw new TypeError(`Cannot instantiate abstract class ${this.constructor.name}`);
+ }
+
+ _emitInputSourcesChanged() {
+ this.emit('input-sources-changed');
+ }
+
+ _emitKeyboardOptionsChanged() {
+ this.emit('keyboard-options-changed');
+ }
+
+ _emitPerWindowChanged() {
+ this.emit('per-window-changed');
+ }
+
+ get inputSources() {
+ return [];
+ }
+
+ get mruSources() {
+ return [];
+ }
+
+ set mruSources(sourcesList) {
+ // do nothing
+ }
+
+ get keyboardOptions() {
+ return [];
+ }
+
+ get perWindow() {
+ return false;
+ }
+};
+
+var InputSourceSystemSettings = class extends InputSourceSettings {
+ constructor() {
+ super();
+
+ this._BUS_NAME = 'org.freedesktop.locale1';
+ this._BUS_PATH = '/org/freedesktop/locale1';
+ this._BUS_IFACE = 'org.freedesktop.locale1';
+ this._BUS_PROPS_IFACE = 'org.freedesktop.DBus.Properties';
+
+ this._layouts = '';
+ this._variants = '';
+ this._options = '';
+
+ this._reload();
+
+ Gio.DBus.system.signal_subscribe(this._BUS_NAME,
+ this._BUS_PROPS_IFACE,
+ 'PropertiesChanged',
+ this._BUS_PATH,
+ null,
+ Gio.DBusSignalFlags.NONE,
+ this._reload.bind(this));
+ }
+
+ async _reload() {
+ let props;
+ try {
+ const result = await Gio.DBus.system.call(
+ this._BUS_NAME,
+ this._BUS_PATH,
+ this._BUS_PROPS_IFACE,
+ 'GetAll',
+ new GLib.Variant('(s)', [this._BUS_IFACE]),
+ null, Gio.DBusCallFlags.NONE, -1, null);
+ [props] = result.deepUnpack();
+ } catch (e) {
+ log(`Could not get properties from ${this._BUS_NAME}`);
+ return;
+ }
+
+ const layouts = props['X11Layout'].unpack();
+ const variants = props['X11Variant'].unpack();
+ const options = props['X11Options'].unpack();
+
+ if (layouts !== this._layouts ||
+ variants !== this._variants) {
+ this._layouts = layouts;
+ this._variants = variants;
+ this._emitInputSourcesChanged();
+ }
+ if (options !== this._options) {
+ this._options = options;
+ this._emitKeyboardOptionsChanged();
+ }
+ }
+
+ get inputSources() {
+ let sourcesList = [];
+ let layouts = this._layouts.split(',');
+ let variants = this._variants.split(',');
+
+ for (let i = 0; i < layouts.length && !!layouts[i]; i++) {
+ let id = layouts[i];
+ if (variants[i])
+ id += `+${variants[i]}`;
+ sourcesList.push({ type: INPUT_SOURCE_TYPE_XKB, id });
+ }
+ return sourcesList;
+ }
+
+ get keyboardOptions() {
+ return this._options.split(',');
+ }
+};
+
+var InputSourceSessionSettings = class extends InputSourceSettings {
+ constructor() {
+ super();
+
+ this._DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
+ this._KEY_INPUT_SOURCES = 'sources';
+ this._KEY_MRU_SOURCES = 'mru-sources';
+ this._KEY_KEYBOARD_OPTIONS = 'xkb-options';
+ this._KEY_PER_WINDOW = 'per-window';
+
+ this._settings = new Gio.Settings({ schema_id: this._DESKTOP_INPUT_SOURCES_SCHEMA });
+ this._settings.connect(`changed::${this._KEY_INPUT_SOURCES}`, this._emitInputSourcesChanged.bind(this));
+ this._settings.connect(`changed::${this._KEY_KEYBOARD_OPTIONS}`, this._emitKeyboardOptionsChanged.bind(this));
+ this._settings.connect(`changed::${this._KEY_PER_WINDOW}`, this._emitPerWindowChanged.bind(this));
+ }
+
+ _getSourcesList(key) {
+ let sourcesList = [];
+ let sources = this._settings.get_value(key);
+ let nSources = sources.n_children();
+
+ for (let i = 0; i < nSources; i++) {
+ let [type, id] = sources.get_child_value(i).deepUnpack();
+ sourcesList.push({ type, id });
+ }
+ return sourcesList;
+ }
+
+ get inputSources() {
+ return this._getSourcesList(this._KEY_INPUT_SOURCES);
+ }
+
+ get mruSources() {
+ return this._getSourcesList(this._KEY_MRU_SOURCES);
+ }
+
+ set mruSources(sourcesList) {
+ let sources = GLib.Variant.new('a(ss)', sourcesList);
+ this._settings.set_value(this._KEY_MRU_SOURCES, sources);
+ }
+
+ get keyboardOptions() {
+ return this._settings.get_strv(this._KEY_KEYBOARD_OPTIONS);
+ }
+
+ get perWindow() {
+ return this._settings.get_boolean(this._KEY_PER_WINDOW);
+ }
+};
+
+var InputSourceManager = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ // All valid input sources currently in the gsettings
+ // KEY_INPUT_SOURCES list indexed by their index there
+ this._inputSources = {};
+ // All valid input sources currently in the gsettings
+ // KEY_INPUT_SOURCES list of type INPUT_SOURCE_TYPE_IBUS
+ // indexed by the IBus ID
+ this._ibusSources = {};
+
+ this._currentSource = null;
+
+ // All valid input sources currently in the gsettings
+ // KEY_INPUT_SOURCES list ordered by most recently used
+ this._mruSources = [];
+ this._mruSourcesBackup = null;
+ this._keybindingAction =
+ Main.wm.addKeybinding('switch-input-source',
+ new Gio.Settings({ schema_id: "org.gnome.desktop.wm.keybindings" }),
+ Meta.KeyBindingFlags.NONE,
+ Shell.ActionMode.ALL,
+ this._switchInputSource.bind(this));
+ this._keybindingActionBackward =
+ Main.wm.addKeybinding('switch-input-source-backward',
+ new Gio.Settings({ schema_id: "org.gnome.desktop.wm.keybindings" }),
+ Meta.KeyBindingFlags.IS_REVERSED,
+ Shell.ActionMode.ALL,
+ this._switchInputSource.bind(this));
+ if (Main.sessionMode.isGreeter)
+ this._settings = new InputSourceSystemSettings();
+ else
+ this._settings = new InputSourceSessionSettings();
+ this._settings.connect('input-sources-changed', this._inputSourcesChanged.bind(this));
+ this._settings.connect('keyboard-options-changed', this._keyboardOptionsChanged.bind(this));
+
+ this._xkbInfo = KeyboardManager.getXkbInfo();
+ this._keyboardManager = KeyboardManager.getKeyboardManager();
+
+ this._ibusReady = false;
+ this._ibusManager = IBusManager.getIBusManager();
+ this._ibusManager.connect('ready', this._ibusReadyCallback.bind(this));
+ this._ibusManager.connect('properties-registered', this._ibusPropertiesRegistered.bind(this));
+ this._ibusManager.connect('property-updated', this._ibusPropertyUpdated.bind(this));
+ this._ibusManager.connect('set-content-type', this._ibusSetContentType.bind(this));
+
+ global.display.connect('modifiers-accelerator-activated', this._modifiersSwitcher.bind(this));
+
+ this._sourcesPerWindow = false;
+ this._focusWindowNotifyId = 0;
+ this._settings.connect('per-window-changed', this._sourcesPerWindowChanged.bind(this));
+ this._sourcesPerWindowChanged();
+ this._disableIBus = false;
+ this._reloading = false;
+ }
+
+ reload() {
+ this._reloading = true;
+ this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions);
+ this._inputSourcesChanged();
+ this._reloading = false;
+ }
+
+ _ibusReadyCallback(im, ready) {
+ if (this._ibusReady == ready)
+ return;
+
+ this._ibusReady = ready;
+ this._mruSources = [];
+ this._inputSourcesChanged();
+ }
+
+ _modifiersSwitcher() {
+ let sourceIndexes = Object.keys(this._inputSources);
+ if (sourceIndexes.length == 0) {
+ KeyboardManager.releaseKeyboard();
+ return true;
+ }
+
+ let is = this._currentSource;
+ if (!is)
+ is = this._inputSources[sourceIndexes[0]];
+
+ let nextIndex = is.index + 1;
+ if (nextIndex > sourceIndexes[sourceIndexes.length - 1])
+ nextIndex = 0;
+
+ while (!(is = this._inputSources[nextIndex]))
+ nextIndex += 1;
+
+ is.activate(true);
+ return true;
+ }
+
+ _switchInputSource(display, window, binding) {
+ if (this._mruSources.length < 2)
+ return;
+
+ // HACK: Fall back on simple input source switching since we
+ // can't show a popup switcher while a GrabHelper grab is in
+ // effect without considerable work to consolidate the usage
+ // of pushModal/popModal and grabHelper. See
+ // https://bugzilla.gnome.org/show_bug.cgi?id=695143 .
+ if (Main.actionMode == Shell.ActionMode.POPUP) {
+ this._modifiersSwitcher();
+ return;
+ }
+
+ this._switcherPopup = new InputSourcePopup(
+ this._mruSources, this._keybindingAction, this._keybindingActionBackward);
+ this._switcherPopup.connect('destroy', () => {
+ this._switcherPopup = null;
+ });
+ if (!this._switcherPopup.show(
+ binding.is_reversed(), binding.get_name(), binding.get_mask()))
+ this._switcherPopup.fadeAndDestroy();
+ }
+
+ _keyboardOptionsChanged() {
+ this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions);
+ this._keyboardManager.reapply();
+ }
+
+ _updateMruSettings() {
+ // If IBus is not ready we don't have a full picture of all
+ // the available sources, so don't update the setting
+ if (!this._ibusReady)
+ return;
+
+ // If IBus is temporarily disabled, don't update the setting
+ if (this._disableIBus)
+ return;
+
+ let sourcesList = [];
+ for (let i = 0; i < this._mruSources.length; ++i) {
+ let source = this._mruSources[i];
+ sourcesList.push([source.type, source.id]);
+ }
+
+ this._settings.mruSources = sourcesList;
+ }
+
+ _currentInputSourceChanged(newSource) {
+ let oldSource;
+ [oldSource, this._currentSource] = [this._currentSource, newSource];
+
+ this.emit('current-source-changed', oldSource);
+
+ for (let i = 1; i < this._mruSources.length; ++i) {
+ if (this._mruSources[i] == newSource) {
+ let currentSource = this._mruSources.splice(i, 1);
+ this._mruSources = currentSource.concat(this._mruSources);
+ break;
+ }
+ }
+ this._changePerWindowSource();
+ }
+
+ activateInputSource(is, interactive) {
+ // The focus changes during holdKeyboard/releaseKeyboard may trick
+ // the client into hiding UI containing the currently focused entry.
+ // So holdKeyboard/releaseKeyboard are not called when
+ // 'set-content-type' signal is received.
+ // E.g. Focusing on a password entry in a popup in Xorg Firefox
+ // will emit 'set-content-type' signal.
+ // https://gitlab.gnome.org/GNOME/gnome-shell/issues/391
+ if (!this._reloading)
+ KeyboardManager.holdKeyboard();
+ this._keyboardManager.apply(is.xkbId);
+
+ // All the "xkb:..." IBus engines simply "echo" back symbols,
+ // despite their naming implying differently, so we always set
+ // one in order for XIM applications to work given that we set
+ // XMODIFIERS=@im=ibus in the first place so that they can
+ // work without restarting when/if the user adds an IBus input
+ // source.
+ let engine;
+ if (is.type == INPUT_SOURCE_TYPE_IBUS)
+ engine = is.id;
+ else
+ engine = 'xkb:us::eng';
+
+ if (!this._reloading)
+ this._ibusManager.setEngine(engine, KeyboardManager.releaseKeyboard);
+ else
+ this._ibusManager.setEngine(engine);
+ this._currentInputSourceChanged(is);
+
+ if (interactive)
+ this._updateMruSettings();
+ }
+
+ _updateMruSources() {
+ let sourcesList = [];
+ for (let i of Object.keys(this._inputSources).sort((a, b) => a - b))
+ sourcesList.push(this._inputSources[i]);
+
+ this._keyboardManager.setUserLayouts(sourcesList.map(x => x.xkbId));
+
+ if (!this._disableIBus && this._mruSourcesBackup) {
+ this._mruSources = this._mruSourcesBackup;
+ this._mruSourcesBackup = null;
+ }
+
+ // Initialize from settings when we have no MRU sources list
+ if (this._mruSources.length == 0) {
+ let mruSettings = this._settings.mruSources;
+ for (let i = 0; i < mruSettings.length; i++) {
+ let mruSettingSource = mruSettings[i];
+ let mruSource = null;
+
+ for (let j = 0; j < sourcesList.length; j++) {
+ let source = sourcesList[j];
+ if (source.type == mruSettingSource.type &&
+ source.id == mruSettingSource.id) {
+ mruSource = source;
+ break;
+ }
+ }
+
+ if (mruSource)
+ this._mruSources.push(mruSource);
+ }
+ }
+
+ let mruSources = [];
+ if (this._mruSources.length > 1) {
+ for (let i = 0; i < this._mruSources.length; i++) {
+ for (let j = 0; j < sourcesList.length; j++) {
+ if (this._mruSources[i].type === sourcesList[j].type &&
+ this._mruSources[i].id === sourcesList[j].id) {
+ mruSources = mruSources.concat(sourcesList.splice(j, 1));
+ break;
+ }
+ }
+ }
+ }
+
+ this._mruSources = mruSources.concat(sourcesList);
+ }
+
+ _inputSourcesChanged() {
+ let sources = this._settings.inputSources;
+ let nSources = sources.length;
+
+ this._currentSource = null;
+ this._inputSources = {};
+ this._ibusSources = {};
+
+ let infosList = [];
+ for (let i = 0; i < nSources; i++) {
+ let displayName;
+ let shortName;
+ let type = sources[i].type;
+ let id = sources[i].id;
+ let exists = false;
+
+ if (type == INPUT_SOURCE_TYPE_XKB) {
+ [exists, displayName, shortName] =
+ this._xkbInfo.get_layout_info(id);
+ } else if (type == INPUT_SOURCE_TYPE_IBUS) {
+ if (this._disableIBus)
+ continue;
+ let engineDesc = this._ibusManager.getEngineDesc(id);
+ if (engineDesc) {
+ let language = IBus.get_language_name(engineDesc.get_language());
+ let longName = engineDesc.get_longname();
+ let textdomain = engineDesc.get_textdomain();
+ if (textdomain != '')
+ longName = Gettext.dgettext(textdomain, longName);
+ exists = true;
+ displayName = `${language} (${longName})`;
+ shortName = this._makeEngineShortName(engineDesc);
+ }
+ }
+
+ if (exists)
+ infosList.push({ type, id, displayName, shortName });
+ }
+
+ if (infosList.length == 0) {
+ let type = INPUT_SOURCE_TYPE_XKB;
+ let id = KeyboardManager.DEFAULT_LAYOUT;
+ let [, displayName, shortName] = this._xkbInfo.get_layout_info(id);
+ infosList.push({ type, id, displayName, shortName });
+ }
+
+ let inputSourcesByShortName = {};
+ for (let i = 0; i < infosList.length; i++) {
+ let is = new InputSource(infosList[i].type,
+ infosList[i].id,
+ infosList[i].displayName,
+ infosList[i].shortName,
+ i);
+ is.connect('activate', this.activateInputSource.bind(this));
+
+ if (!(is.shortName in inputSourcesByShortName))
+ inputSourcesByShortName[is.shortName] = [];
+ inputSourcesByShortName[is.shortName].push(is);
+
+ this._inputSources[is.index] = is;
+
+ if (is.type == INPUT_SOURCE_TYPE_IBUS)
+ this._ibusSources[is.id] = is;
+ }
+
+ for (let i in this._inputSources) {
+ let is = this._inputSources[i];
+ if (inputSourcesByShortName[is.shortName].length > 1) {
+ let sub = inputSourcesByShortName[is.shortName].indexOf(is) + 1;
+ is.shortName += String.fromCharCode(0x2080 + sub);
+ }
+ }
+
+ this.emit('sources-changed');
+
+ this._updateMruSources();
+
+ if (this._mruSources.length > 0)
+ this._mruSources[0].activate(false);
+
+ // All ibus engines are preloaded here to reduce the launching time
+ // when users switch the input sources.
+ this._ibusManager.preloadEngines(Object.keys(this._ibusSources));
+ }
+
+ _makeEngineShortName(engineDesc) {
+ let symbol = engineDesc.get_symbol();
+ if (symbol && symbol[0])
+ return symbol;
+
+ let langCode = engineDesc.get_language().split('_', 1)[0];
+ if (langCode.length == 2 || langCode.length == 3)
+ return langCode.toLowerCase();
+
+ return String.fromCharCode(0x2328); // keyboard glyph
+ }
+
+ _ibusPropertiesRegistered(im, engineName, props) {
+ let source = this._ibusSources[engineName];
+ if (!source)
+ return;
+
+ source.properties = props;
+
+ if (source == this._currentSource)
+ this.emit('current-source-changed', null);
+ }
+
+ _ibusPropertyUpdated(im, engineName, prop) {
+ let source = this._ibusSources[engineName];
+ if (!source)
+ return;
+
+ if (this._updateSubProperty(source.properties, prop) &&
+ source == this._currentSource)
+ this.emit('current-source-changed', null);
+ }
+
+ _updateSubProperty(props, prop) {
+ if (!props)
+ return false;
+
+ let p;
+ for (let i = 0; (p = props.get(i)) != null; ++i) {
+ if (p.get_key() == prop.get_key() && p.get_prop_type() == prop.get_prop_type()) {
+ p.update(prop);
+ return true;
+ } else if (p.get_prop_type() == IBus.PropType.MENU) {
+ if (this._updateSubProperty(p.get_sub_props(), prop))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ _ibusSetContentType(im, purpose, _hints) {
+ // Avoid purpose changes while the switcher popup is shown, likely due to
+ // the focus change caused by the switcher popup causing this purpose change.
+ if (this._switcherPopup)
+ return;
+ if (purpose == IBus.InputPurpose.PASSWORD) {
+ if (Object.keys(this._inputSources).length == Object.keys(this._ibusSources).length)
+ return;
+
+ if (this._disableIBus)
+ return;
+ this._disableIBus = true;
+ this._mruSourcesBackup = this._mruSources.slice();
+ } else {
+ if (!this._disableIBus)
+ return;
+ this._disableIBus = false;
+ }
+ this.reload();
+ }
+
+ _getNewInputSource(current) {
+ let sourceIndexes = Object.keys(this._inputSources);
+ if (sourceIndexes.length == 0)
+ return null;
+
+ if (current) {
+ for (let i in this._inputSources) {
+ let is = this._inputSources[i];
+ if (is.type == current.type &&
+ is.id == current.id)
+ return is;
+ }
+ }
+
+ return this._inputSources[sourceIndexes[0]];
+ }
+
+ _getCurrentWindow() {
+ if (Main.overview.visible)
+ return Main.overview;
+ else
+ return global.display.focus_window;
+ }
+
+ _setPerWindowInputSource() {
+ let window = this._getCurrentWindow();
+ if (!window)
+ return;
+
+ if (!window._inputSources ||
+ window._inputSources !== this._inputSources) {
+ window._inputSources = this._inputSources;
+ window._currentSource = this._getNewInputSource(window._currentSource);
+ }
+
+ if (window._currentSource)
+ window._currentSource.activate(false);
+ }
+
+ _sourcesPerWindowChanged() {
+ this._sourcesPerWindow = this._settings.perWindow;
+
+ if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
+ this._focusWindowNotifyId = global.display.connect('notify::focus-window',
+ this._setPerWindowInputSource.bind(this));
+ Main.overview.connectObject(
+ 'showing', this._setPerWindowInputSource.bind(this),
+ 'hidden', this._setPerWindowInputSource.bind(this), this);
+ } else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
+ global.display.disconnect(this._focusWindowNotifyId);
+ this._focusWindowNotifyId = 0;
+ Main.overview.disconnectObject(this);
+
+ let windows = global.get_window_actors().map(w => w.meta_window);
+ for (let i = 0; i < windows.length; ++i) {
+ delete windows[i]._inputSources;
+ delete windows[i]._currentSource;
+ }
+ delete Main.overview._inputSources;
+ delete Main.overview._currentSource;
+ }
+ }
+
+ _changePerWindowSource() {
+ if (!this._sourcesPerWindow)
+ return;
+
+ let window = this._getCurrentWindow();
+ if (!window)
+ return;
+
+ window._inputSources = this._inputSources;
+ window._currentSource = this._currentSource;
+ }
+
+ get currentSource() {
+ return this._currentSource;
+ }
+
+ get inputSources() {
+ return this._inputSources;
+ }
+
+ get keyboardManager() {
+ return this._keyboardManager;
+ }
+};
+
+let _inputSourceManager = null;
+
+function getInputSourceManager() {
+ if (_inputSourceManager == null)
+ _inputSourceManager = new InputSourceManager();
+ return _inputSourceManager;
+}
+
+var InputSourceIndicatorContainer = GObject.registerClass(
+class InputSourceIndicatorContainer extends St.Widget {
+ vfunc_get_preferred_width(forHeight) {
+ // Here, and in vfunc_get_preferred_height, we need to query
+ // for the height of all children, but we ignore the results
+ // for those we don't actually display.
+ return this.get_children().reduce((maxWidth, child) => {
+ let width = child.get_preferred_width(forHeight);
+ return [
+ Math.max(maxWidth[0], width[0]),
+ Math.max(maxWidth[1], width[1]),
+ ];
+ }, [0, 0]);
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ return this.get_children().reduce((maxHeight, child) => {
+ let height = child.get_preferred_height(forWidth);
+ return [
+ Math.max(maxHeight[0], height[0]),
+ Math.max(maxHeight[1], height[1]),
+ ];
+ }, [0, 0]);
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ // translate box to (0, 0)
+ box.x2 -= box.x1;
+ box.x1 = 0;
+ box.y2 -= box.y1;
+ box.y1 = 0;
+
+ this.get_children().forEach(c => {
+ c.allocate_align_fill(box, 0.5, 0.5, false, false);
+ });
+ }
+});
+
+var InputSourceIndicator = GObject.registerClass(
+class InputSourceIndicator extends PanelMenu.Button {
+ _init() {
+ super._init(0.5, _("Keyboard"));
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._menuItems = {};
+ this._indicatorLabels = {};
+
+ this._container = new InputSourceIndicatorContainer({ style_class: 'system-status-icon' });
+ this.add_child(this._container);
+
+ this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
+ this.menu.addMenuItem(this._propSeparator);
+ this._propSection = new PopupMenu.PopupMenuSection();
+ this.menu.addMenuItem(this._propSection);
+ this._propSection.actor.hide();
+
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), this._showLayout.bind(this));
+
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ this._sessionUpdated();
+
+ this._inputSourceManager = getInputSourceManager();
+ this._inputSourceManager.connectObject(
+ 'sources-changed', this._sourcesChanged.bind(this),
+ 'current-source-changed', this._currentSourceChanged.bind(this), this);
+ this._inputSourceManager.reload();
+ }
+
+ _onDestroy() {
+ this._inputSourceManager = null;
+ }
+
+ _sessionUpdated() {
+ // re-using "allowSettings" for the keyboard layout is a bit shady,
+ // but at least for now it is used as "allow popping up windows
+ // from shell menus"; we can always add a separate sessionMode
+ // option if need arises.
+ this._showLayoutItem.visible = Main.sessionMode.allowSettings;
+ }
+
+ _sourcesChanged() {
+ for (let i in this._menuItems)
+ this._menuItems[i].destroy();
+ for (let i in this._indicatorLabels)
+ this._indicatorLabels[i].destroy();
+
+ this._menuItems = {};
+ this._indicatorLabels = {};
+
+ let menuIndex = 0;
+ for (let i in this._inputSourceManager.inputSources) {
+ let is = this._inputSourceManager.inputSources[i];
+
+ let menuItem = new LayoutMenuItem(is.displayName, is.shortName);
+ menuItem.connect('activate', () => is.activate(true));
+
+ const indicatorLabel = new St.Label({
+ text: is.shortName,
+ visible: false,
+ });
+
+ this._menuItems[i] = menuItem;
+ this._indicatorLabels[i] = indicatorLabel;
+ is.connect('changed', () => {
+ menuItem.indicator.set_text(is.shortName);
+ indicatorLabel.set_text(is.shortName);
+ });
+
+ this.menu.addMenuItem(menuItem, menuIndex++);
+ this._container.add_actor(indicatorLabel);
+ }
+ }
+
+ _currentSourceChanged(manager, oldSource) {
+ let nVisibleSources = Object.keys(this._inputSourceManager.inputSources).length;
+ let newSource = this._inputSourceManager.currentSource;
+
+ if (oldSource) {
+ this._menuItems[oldSource.index].setOrnament(PopupMenu.Ornament.NONE);
+ this._indicatorLabels[oldSource.index].hide();
+ }
+
+ if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
+ // This source index might be invalid if we weren't able
+ // to build a menu item for it, so we hide ourselves since
+ // we can't fix it here. *shrug*
+
+ // We also hide if we have only one visible source unless
+ // it's an IBus source with properties.
+ this.menu.close();
+ this.hide();
+ return;
+ }
+
+ this.show();
+
+ this._buildPropSection(newSource.properties);
+
+ this._menuItems[newSource.index].setOrnament(PopupMenu.Ornament.DOT);
+ this._indicatorLabels[newSource.index].show();
+ }
+
+ _buildPropSection(properties) {
+ this._propSeparator.hide();
+ this._propSection.actor.hide();
+ this._propSection.removeAll();
+
+ this._buildPropSubMenu(this._propSection, properties);
+
+ if (!this._propSection.isEmpty()) {
+ this._propSection.actor.show();
+ this._propSeparator.show();
+ }
+ }
+
+ _buildPropSubMenu(menu, props) {
+ if (!props)
+ return;
+
+ let ibusManager = IBusManager.getIBusManager();
+ let radioGroup = [];
+ let p;
+ for (let i = 0; (p = props.get(i)) != null; ++i) {
+ let prop = p;
+
+ if (!prop.get_visible())
+ continue;
+
+ if (prop.get_key() == 'InputMode') {
+ let text;
+ if (prop.get_symbol)
+ text = prop.get_symbol().get_text();
+ else
+ text = prop.get_label().get_text();
+
+ let currentSource = this._inputSourceManager.currentSource;
+ if (currentSource) {
+ let indicatorLabel = this._indicatorLabels[currentSource.index];
+ if (text && text.length > 0 && text.length < 3)
+ indicatorLabel.set_text(text);
+ }
+ }
+
+ let item;
+ let type = prop.get_prop_type();
+ switch (type) {
+ case IBus.PropType.MENU:
+ item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
+ this._buildPropSubMenu(item.menu, prop.get_sub_props());
+ break;
+
+ case IBus.PropType.RADIO:
+ item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
+ item.prop = prop;
+ radioGroup.push(item);
+ item.radioGroup = radioGroup;
+ item.setOrnament(prop.get_state() == IBus.PropState.CHECKED
+ ? PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
+ item.connect('activate', () => {
+ if (item.prop.get_state() == IBus.PropState.CHECKED)
+ return;
+
+ let group = item.radioGroup;
+ for (let j = 0; j < group.length; ++j) {
+ if (group[j] == item) {
+ item.setOrnament(PopupMenu.Ornament.DOT);
+ item.prop.set_state(IBus.PropState.CHECKED);
+ ibusManager.activateProperty(item.prop.get_key(),
+ IBus.PropState.CHECKED);
+ } else {
+ group[j].setOrnament(PopupMenu.Ornament.NONE);
+ group[j].prop.set_state(IBus.PropState.UNCHECKED);
+ ibusManager.activateProperty(group[j].prop.get_key(),
+ IBus.PropState.UNCHECKED);
+ }
+ }
+ });
+ break;
+
+ case IBus.PropType.TOGGLE:
+ item = new PopupMenu.PopupSwitchMenuItem(prop.get_label().get_text(), prop.get_state() == IBus.PropState.CHECKED);
+ item.prop = prop;
+ item.connect('toggled', () => {
+ if (item.state) {
+ item.prop.set_state(IBus.PropState.CHECKED);
+ ibusManager.activateProperty(item.prop.get_key(),
+ IBus.PropState.CHECKED);
+ } else {
+ item.prop.set_state(IBus.PropState.UNCHECKED);
+ ibusManager.activateProperty(item.prop.get_key(),
+ IBus.PropState.UNCHECKED);
+ }
+ });
+ break;
+
+ case IBus.PropType.NORMAL:
+ item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
+ item.prop = prop;
+ item.connect('activate', () => {
+ ibusManager.activateProperty(item.prop.get_key(),
+ item.prop.get_state());
+ });
+ break;
+
+ case IBus.PropType.SEPARATOR:
+ item = new PopupMenu.PopupSeparatorMenuItem();
+ break;
+
+ default:
+ log(`IBus property ${prop.get_key()} has invalid type ${type}`);
+ continue;
+ }
+
+ item.setSensitive(prop.get_sensitive());
+ menu.addMenuItem(item);
+ }
+ }
+
+ _showLayout() {
+ Main.overview.hide();
+
+ let source = this._inputSourceManager.currentSource;
+ let xkbLayout = '';
+ let xkbVariant = '';
+
+ if (source.type == INPUT_SOURCE_TYPE_XKB) {
+ [, , , xkbLayout, xkbVariant] = KeyboardManager.getXkbInfo().get_layout_info(source.id);
+ } else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
+ let engineDesc = IBusManager.getIBusManager().getEngineDesc(source.id);
+ if (engineDesc) {
+ xkbLayout = engineDesc.get_layout();
+ xkbVariant = engineDesc.get_layout_variant();
+ }
+
+ // The `default` layout from ibus engine means to
+ // use the current keyboard layout.
+ if (xkbLayout === 'default') {
+ const current = this._inputSourceManager.keyboardManager.currentLayout;
+ xkbLayout = current.layout;
+ xkbVariant = current.variant;
+ }
+ }
+
+ if (!xkbLayout || xkbLayout.length == 0)
+ return;
+
+ let description = xkbLayout;
+ if (xkbVariant.length > 0)
+ description = `${description}\t${xkbVariant}`;
+
+ Util.spawn(['gkbd-keyboard-display', '-l', description]);
+ }
+});
diff --git a/js/ui/status/location.js b/js/ui/status/location.js
new file mode 100644
index 0000000..45f6b7a
--- /dev/null
+++ b/js/ui/status/location.js
@@ -0,0 +1,371 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi;
+
+const Dialog = imports.ui.dialog;
+const ModalDialog = imports.ui.modalDialog;
+const PermissionStore = imports.misc.permissionStore;
+const {SystemIndicator} = imports.ui.quickSettings;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const LOCATION_SCHEMA = 'org.gnome.system.location';
+const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
+const ENABLED = 'enabled';
+
+const APP_PERMISSIONS_TABLE = 'location';
+const APP_PERMISSIONS_ID = 'location';
+
+var GeoclueAccuracyLevel = {
+ NONE: 0,
+ COUNTRY: 1,
+ CITY: 4,
+ NEIGHBORHOOD: 5,
+ STREET: 6,
+ EXACT: 8,
+};
+
+function accuracyLevelToString(accuracyLevel) {
+ for (let key in GeoclueAccuracyLevel) {
+ if (GeoclueAccuracyLevel[key] == accuracyLevel)
+ return key;
+ }
+
+ return 'NONE';
+}
+
+var GeoclueIface = loadInterfaceXML('org.freedesktop.GeoClue2.Manager');
+const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface);
+
+var AgentIface = loadInterfaceXML('org.freedesktop.GeoClue2.Agent');
+
+let _geoclueAgent = null;
+function _getGeoclueAgent() {
+ if (_geoclueAgent === null)
+ _geoclueAgent = new GeoclueAgent();
+ return _geoclueAgent;
+}
+
+var GeoclueAgent = GObject.registerClass({
+ Properties: {
+ 'enabled': GObject.ParamSpec.boolean(
+ 'enabled', 'Enabled', 'Enabled',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'in-use': GObject.ParamSpec.boolean(
+ 'in-use', 'In use', 'In use',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'max-accuracy-level': GObject.ParamSpec.int(
+ 'max-accuracy-level', 'Max accuracy level', 'Max accuracy level',
+ GObject.ParamFlags.READABLE,
+ 0, 8, 0),
+ },
+}, class GeoclueAgent extends GObject.Object {
+ _init() {
+ super._init();
+
+ this._settings = new Gio.Settings({ schema_id: LOCATION_SCHEMA });
+ this._settings.connectObject(
+ `changed::${ENABLED}`, () => this.notify('enabled'),
+ `changed::${MAX_ACCURACY_LEVEL}`, () => this._onMaxAccuracyLevelChanged(),
+ this);
+
+ this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this);
+ this._agent.export(Gio.DBus.system, '/org/freedesktop/GeoClue2/Agent');
+
+ this.connect('notify::enabled', this._onMaxAccuracyLevelChanged.bind(this));
+
+ this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM,
+ 'org.freedesktop.GeoClue2',
+ 0,
+ this._connectToGeoclue.bind(this),
+ this._onGeoclueVanished.bind(this));
+ this._onMaxAccuracyLevelChanged();
+ this._connectToGeoclue();
+ this._connectToPermissionStore();
+ }
+
+ get enabled() {
+ return this._settings.get_boolean(ENABLED);
+ }
+
+ set enabled(value) {
+ this._settings.set_boolean(ENABLED, value);
+ }
+
+ get inUse() {
+ return this._managerProxy?.InUse ?? false;
+ }
+
+ get maxAccuracyLevel() {
+ if (this.enabled) {
+ let level = this._settings.get_string(MAX_ACCURACY_LEVEL);
+
+ return GeoclueAccuracyLevel[level.toUpperCase()] ||
+ GeoclueAccuracyLevel.NONE;
+ } else {
+ return GeoclueAccuracyLevel.NONE;
+ }
+ }
+
+ async AuthorizeAppAsync(params, invocation) {
+ let [desktopId, reqAccuracyLevel] = params;
+
+ let authorizer = new AppAuthorizer(desktopId,
+ reqAccuracyLevel, this._permStoreProxy, this.maxAccuracyLevel);
+
+ const accuracyLevel = await authorizer.authorize();
+ const ret = accuracyLevel !== GeoclueAccuracyLevel.NONE;
+ invocation.return_value(GLib.Variant.new('(bu)', [ret, accuracyLevel]));
+ }
+
+ get MaxAccuracyLevel() {
+ return this.maxAccuracyLevel;
+ }
+
+ _connectToGeoclue() {
+ if (this._managerProxy != null || this._connecting)
+ return false;
+
+ this._connecting = true;
+ new GeoclueManager(Gio.DBus.system,
+ 'org.freedesktop.GeoClue2',
+ '/org/freedesktop/GeoClue2/Manager',
+ this._onManagerProxyReady.bind(this));
+ return true;
+ }
+
+ async _onManagerProxyReady(proxy, error) {
+ if (error != null) {
+ log(error.message);
+ this._connecting = false;
+ return;
+ }
+
+ this._managerProxy = proxy;
+ this._managerProxy.connectObject('g-properties-changed',
+ this._onGeocluePropsChanged.bind(this), this);
+
+ this.notify('in-use');
+
+ try {
+ await this._managerProxy.AddAgentAsync('gnome-shell');
+ this._connecting = false;
+ this._notifyMaxAccuracyLevel();
+ } catch (e) {
+ log(e.message);
+ }
+ }
+
+ _onGeoclueVanished() {
+ this._managerProxy?.disconnectObject(this);
+ this._managerProxy = null;
+
+ this.notify('in-use');
+ }
+
+ _onMaxAccuracyLevelChanged() {
+ this.notify('max-accuracy-level');
+
+ // Gotta ensure geoclue is up and we are registered as agent to it
+ // before we emit the notify for this property change.
+ if (!this._connectToGeoclue())
+ this._notifyMaxAccuracyLevel();
+ }
+
+ _notifyMaxAccuracyLevel() {
+ let variant = new GLib.Variant('u', this.maxAccuracyLevel);
+ this._agent.emit_property_changed('MaxAccuracyLevel', variant);
+ }
+
+ _onGeocluePropsChanged(proxy, properties) {
+ const inUseChanged = !!properties.lookup_value('InUse', null);
+ if (inUseChanged)
+ this.notify('in-use');
+ }
+
+ _connectToPermissionStore() {
+ this._permStoreProxy = null;
+ new PermissionStore.PermissionStore(this._onPermStoreProxyReady.bind(this));
+ }
+
+ _onPermStoreProxyReady(proxy, error) {
+ if (error != null) {
+ log(error.message);
+ return;
+ }
+
+ this._permStoreProxy = proxy;
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._agent = _getGeoclueAgent();
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'location-services-active-symbolic';
+ this._agent.bind_property('in-use',
+ this._indicator,
+ 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+});
+
+var AppAuthorizer = class {
+ constructor(desktopId, reqAccuracyLevel, permStoreProxy, maxAccuracyLevel) {
+ this.desktopId = desktopId;
+ this.reqAccuracyLevel = reqAccuracyLevel;
+ this._permStoreProxy = permStoreProxy;
+ this._maxAccuracyLevel = maxAccuracyLevel;
+ this._permissions = {};
+
+ this._accuracyLevel = GeoclueAccuracyLevel.NONE;
+ }
+
+ async authorize() {
+ let appSystem = Shell.AppSystem.get_default();
+ this._app = appSystem.lookup_app(`${this.desktopId}.desktop`);
+ if (this._app == null || this._permStoreProxy == null)
+ return this._completeAuth();
+
+ try {
+ [this._permissions] = await this._permStoreProxy.LookupAsync(
+ APP_PERMISSIONS_TABLE,
+ APP_PERMISSIONS_ID);
+ } catch (error) {
+ if (error.domain === Gio.DBusError) {
+ // Likely no xdg-app installed, just authorize the app
+ this._accuracyLevel = this.reqAccuracyLevel;
+ this._permStoreProxy = null;
+ return this._completeAuth();
+ } else {
+ // Currently xdg-app throws an error if we lookup for
+ // unknown ID (which would be the case first time this code
+ // runs) so we continue with user authorization as normal
+ // and ID is added to the store if user says "yes".
+ log(error.message);
+ this._permissions = {};
+ }
+ }
+
+ let permission = this._permissions[this.desktopId];
+
+ if (permission == null) {
+ await this._userAuthorizeApp();
+ } else {
+ let [levelStr] = permission || ['NONE'];
+ this._accuracyLevel = GeoclueAccuracyLevel[levelStr] ||
+ GeoclueAccuracyLevel.NONE;
+ }
+
+ return this._completeAuth();
+ }
+
+ _userAuthorizeApp() {
+ let name = this._app.get_name();
+ let appInfo = this._app.get_app_info();
+ let reason = appInfo.get_locale_string("X-Geoclue-Reason");
+
+ this._dialog =
+ new GeolocationDialog(name, reason, this.reqAccuracyLevel);
+
+ return new Promise(resolve => {
+ const responseId = this._dialog.connect('response',
+ (dialog, level) => {
+ this._dialog.disconnect(responseId);
+ this._accuracyLevel = level;
+ resolve();
+ });
+ this._dialog.open();
+ });
+ }
+
+ _completeAuth() {
+ if (this._accuracyLevel != GeoclueAccuracyLevel.NONE) {
+ this._accuracyLevel = Math.clamp(this._accuracyLevel,
+ 0, this._maxAccuracyLevel);
+ }
+ this._saveToPermissionStore();
+
+ return this._accuracyLevel;
+ }
+
+ async _saveToPermissionStore() {
+ if (this._permStoreProxy == null)
+ return;
+
+ let levelStr = accuracyLevelToString(this._accuracyLevel);
+ let dateStr = Math.round(Date.now() / 1000).toString();
+ this._permissions[this.desktopId] = [levelStr, dateStr];
+
+ let data = GLib.Variant.new('av', {});
+
+ try {
+ await this._permStoreProxy.SetAsync(
+ APP_PERMISSIONS_TABLE,
+ true,
+ APP_PERMISSIONS_ID,
+ this._permissions,
+ data);
+ } catch (error) {
+ log(error.message);
+ }
+ }
+};
+
+var GeolocationDialog = GObject.registerClass({
+ Signals: { 'response': { param_types: [GObject.TYPE_UINT] } },
+}, class GeolocationDialog extends ModalDialog.ModalDialog {
+ _init(name, reason, reqAccuracyLevel) {
+ super._init({ styleClass: 'geolocation-dialog' });
+ this.reqAccuracyLevel = reqAccuracyLevel;
+
+ let content = new Dialog.MessageDialogContent({
+ title: _('Allow location access'),
+ /* Translators: %s is an application name */
+ description: _('The app %s wants to access your location').format(name),
+ });
+
+ let reasonLabel = new St.Label({
+ text: reason,
+ style_class: 'message-dialog-description',
+ });
+ content.add_child(reasonLabel);
+
+ let infoLabel = new St.Label({
+ text: _('Location access can be changed at any time from the privacy settings.'),
+ style_class: 'message-dialog-description',
+ });
+ content.add_child(infoLabel);
+
+ this.contentLayout.add_child(content);
+
+ const button = this.addButton({
+ label: _('Deny Access'),
+ action: this._onDenyClicked.bind(this),
+ key: Clutter.KEY_Escape,
+ });
+ this.addButton({
+ label: _('Grant Access'),
+ action: this._onGrantClicked.bind(this),
+ });
+
+ this.setInitialKeyFocus(button);
+ }
+
+ _onGrantClicked() {
+ this.emit('response', this.reqAccuracyLevel);
+ this.close();
+ }
+
+ _onDenyClicked() {
+ this.emit('response', GeoclueAccuracyLevel.NONE);
+ this.close();
+ }
+});
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
new file mode 100644
index 0000000..d9755a3
--- /dev/null
+++ b/js/ui/status/network.js
@@ -0,0 +1,2095 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+const {Atk, Clutter, Gio, GLib, GObject, NM, Polkit, St} = imports.gi;
+
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const MessageTray = imports.ui.messageTray;
+const ModemManager = imports.misc.modemManager;
+const Util = imports.misc.util;
+
+const {Spinner} = imports.ui.animation;
+const {QuickMenuToggle, SystemIndicator} = imports.ui.quickSettings;
+
+const {loadInterfaceXML} = imports.misc.fileUtils;
+const {registerDestroyableType} = imports.misc.signalTracker;
+
+Gio._promisify(Gio.DBusConnection.prototype, 'call');
+Gio._promisify(NM.Client, 'new_async');
+Gio._promisify(NM.Client.prototype, 'check_connectivity_async');
+Gio._promisify(NM.DeviceWifi.prototype, 'request_scan_async');
+
+const WIFI_SCAN_FREQUENCY = 15;
+const MAX_VISIBLE_NETWORKS = 8;
+
+// small optimization, to avoid using [] all the time
+const NM80211Mode = NM['80211Mode'];
+
+var PortalHelperResult = {
+ CANCELLED: 0,
+ COMPLETED: 1,
+ RECHECK: 2,
+};
+
+const PortalHelperIface = loadInterfaceXML('org.gnome.Shell.PortalHelper');
+const PortalHelperInfo = Gio.DBusInterfaceInfo.new_for_xml(PortalHelperIface);
+
+function signalToIcon(value) {
+ if (value < 20)
+ return 'none';
+ else if (value < 40)
+ return 'weak';
+ else if (value < 50)
+ return 'ok';
+ else if (value < 80)
+ return 'good';
+ else
+ return 'excellent';
+}
+
+function ssidToLabel(ssid) {
+ let label = NM.utils_ssid_to_utf8(ssid.get_data());
+ if (!label)
+ label = _("<unknown>");
+ return label;
+}
+
+function launchSettingsPanel(panel, ...args) {
+ const param = new GLib.Variant('(sav)',
+ [panel, args.map(s => new GLib.Variant('s', s))]);
+ const platformData = {
+ 'desktop-startup-id': new GLib.Variant('s',
+ `_TIME${global.get_current_time()}`),
+ };
+ try {
+ Gio.DBus.session.call(
+ 'org.gnome.Settings',
+ '/org/gnome/Settings',
+ 'org.freedesktop.Application',
+ 'ActivateAction',
+ new GLib.Variant('(sava{sv})',
+ ['launch-panel', [param], platformData]),
+ null,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ null);
+ } catch (e) {
+ log(`Failed to launch Settings panel: ${e.message}`);
+ }
+}
+
+class ItemSorter {
+ [Symbol.iterator] = this.items;
+
+ /**
+ * Maintains a list of sorted items. By default, items are
+ * assumed to be objects with a name property.
+ *
+ * Optionally items can have a secondary sort order by
+ * recency. If used, items must by objects with a timestamp
+ * property that can be used in substraction, and "bigger"
+ * must mean "more recent". Number and Date both qualify.
+ *
+ * @param {object=} options - property object with options
+ * @param {Function} options.sortFunc - a custom sort function
+ * @param {bool} options.trackMru - whether to track MRU order as well
+ **/
+ constructor(options = {}) {
+ const {sortFunc, trackMru} = {
+ sortFunc: this._sortByName.bind(this),
+ trackMru: false,
+ ...options,
+ };
+
+ this._trackMru = trackMru;
+ this._sortFunc = sortFunc;
+ this._sortFuncMru = this._sortByMru.bind(this);
+
+ this._itemsOrder = [];
+ this._itemsMruOrder = [];
+ }
+
+ *items() {
+ yield* this._itemsOrder;
+ }
+
+ *itemsByMru() {
+ console.assert(this._trackMru, 'itemsByMru: MRU tracking is disabled');
+ yield* this._itemsMruOrder;
+ }
+
+ _sortByName(one, two) {
+ return GLib.utf8_collate(one.name, two.name);
+ }
+
+ _sortByMru(one, two) {
+ return two.timestamp - one.timestamp;
+ }
+
+ _upsert(array, item, sortFunc) {
+ this._delete(array, item);
+ return Util.insertSorted(array, item, sortFunc);
+ }
+
+ _delete(array, item) {
+ const pos = array.indexOf(item);
+ if (pos >= 0)
+ array.splice(pos, 1);
+ }
+
+ /**
+ * Insert or update item.
+ *
+ * @param {any} item - the item to upsert
+ * @returns {number} - the sorted position of item
+ */
+ upsert(item) {
+ if (this._trackMru)
+ this._upsert(this._itemsMruOrder, item, this._sortFuncMru);
+
+ return this._upsert(this._itemsOrder, item, this._sortFunc);
+ }
+
+ /**
+ * @param {any} item - item to remove
+ */
+ delete(item) {
+ if (this._trackMru)
+ this._delete(this._itemsMruOrder, item);
+ this._delete(this._itemsOrder, item);
+ }
+}
+
+const NMMenuItem = GObject.registerClass({
+ Properties: {
+ 'radio-mode': GObject.ParamSpec.boolean('radio-mode', '', '',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'is-active': GObject.ParamSpec.boolean('is-active', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'name': GObject.ParamSpec.string('name', '', '',
+ GObject.ParamFlags.READWRITE,
+ ''),
+ 'icon-name': GObject.ParamSpec.string('icon-name', '', '',
+ GObject.ParamFlags.READWRITE,
+ ''),
+ },
+}, class NMMenuItem extends PopupMenu.PopupBaseMenuItem {
+ get state() {
+ return this._activeConnection?.state ??
+ NM.ActiveConnectionState.DEACTIVATED;
+ }
+
+ get is_active() {
+ return this.state <= NM.ActiveConnectionState.ACTIVATED;
+ }
+
+ get timestamp() {
+ return 0;
+ }
+
+ activate() {
+ super.activate(Clutter.get_current_event());
+ }
+
+ _activeConnectionStateChanged() {
+ this.notify('is-active');
+ this.notify('icon-name');
+
+ this._sync();
+ }
+
+ _setActiveConnection(activeConnection) {
+ this._activeConnection?.disconnectObject(this);
+
+ this._activeConnection = activeConnection;
+
+ this._activeConnection?.connectObject(
+ 'notify::state', () => this._activeConnectionStateChanged(),
+ this);
+ this._activeConnectionStateChanged();
+ }
+
+ _sync() {
+ // Overridden by subclasses
+ }
+});
+
+/**
+ * Item that contains a section, and can be collapsed
+ * into a submenu
+ */
+const NMSectionItem = GObject.registerClass({
+ Properties: {
+ 'use-submenu': GObject.ParamSpec.boolean('use-submenu', '', '',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+}, class NMSectionItem extends NMMenuItem {
+ constructor() {
+ super({
+ activate: false,
+ can_focus: false,
+ });
+
+ this._useSubmenu = false;
+
+ // Turn into an empty container with no padding
+ this.styleClass = '';
+ this.setOrnament(PopupMenu.Ornament.HIDDEN);
+
+ // Add intermediate section; we need this for submenu support
+ this._mainSection = new PopupMenu.PopupMenuSection();
+ this.add_child(this._mainSection.actor);
+
+ this._submenuItem = new PopupMenu.PopupSubMenuMenuItem('', true);
+ this._mainSection.addMenuItem(this._submenuItem);
+ this._submenuItem.hide();
+
+ this.section = new PopupMenu.PopupMenuSection();
+ this._mainSection.addMenuItem(this.section);
+
+ // Represents the item as a whole when shown
+ this.bind_property('name',
+ this._submenuItem.label, 'text',
+ GObject.BindingFlags.DEFAULT);
+ this.bind_property('icon-name',
+ this._submenuItem.icon, 'icon-name',
+ GObject.BindingFlags.DEFAULT);
+ }
+
+ _setParent(parent) {
+ super._setParent(parent);
+ this._mainSection._setParent(parent);
+
+ parent?.connect('menu-closed',
+ () => this._mainSection.emit('menu-closed'));
+ }
+
+ get use_submenu() {
+ return this._useSubmenu;
+ }
+
+ set use_submenu(useSubmenu) {
+ if (this._useSubmenu === useSubmenu)
+ return;
+
+ this._useSubmenu = useSubmenu;
+ this._submenuItem.visible = useSubmenu;
+
+ if (useSubmenu) {
+ this._mainSection.box.remove_child(this.section.actor);
+ this._submenuItem.menu.box.add_child(this.section.actor);
+ } else {
+ this._submenuItem.menu.box.remove_child(this.section.actor);
+ this._mainSection.box.add_child(this.section.actor);
+ }
+ }
+});
+
+const NMConnectionItem = GObject.registerClass(
+class NMConnectionItem extends NMMenuItem {
+ constructor(section, connection) {
+ super();
+
+ this._section = section;
+ this._connection = connection;
+ this._activeConnection = null;
+
+ this._icon = new St.Icon({
+ style_class: 'popup-menu-icon',
+ x_align: Clutter.ActorAlign.END,
+ visible: !this.radio_mode,
+ });
+ this.add_child(this._icon);
+
+ this._label = new St.Label({
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this._label);
+ this.label_actor = this._label;
+
+ this.bind_property('icon-name',
+ this._icon, 'icon-name',
+ GObject.BindingFlags.DEFAULT);
+ this.bind_property('radio-mode',
+ this._icon, 'visible',
+ GObject.BindingFlags.INVERT_BOOLEAN);
+
+ this.connectObject(
+ 'notify::radio-mode', () => this._sync(),
+ 'notify::name', () => this._sync(),
+ this);
+ this._sync();
+ }
+
+ get name() {
+ return this._connection.get_id();
+ }
+
+ get timestamp() {
+ return this._connection.get_setting_connection()?.get_timestamp() ?? 0;
+ }
+
+ updateForConnection(connection) {
+ // connection should always be the same object
+ // (and object path) as this._connection, but
+ // this can be false if NetworkManager was restarted
+ // and picked up connections in a different order
+ // Just to be safe, we set it here again
+
+ this._connection = connection;
+ this.notify('name');
+ this._sync();
+ }
+
+ _updateOrnament() {
+ this.setOrnament(this.radio_mode && this.is_active
+ ? PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
+ }
+
+ _getRegularLabel() {
+ return this.is_active
+ // Translators: %s is a device name like "MyPhone"
+ ? _('Disconnect %s').format(this.name)
+ // Translators: %s is a device name like "MyPhone"
+ : _('Connect to %s').format(this.name);
+ }
+
+ _sync() {
+ if (this.radioMode) {
+ this._label.text = this.name;
+ this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
+ } else {
+ this._label.text = this._getRegularLabel();
+ this.accessible_role = Atk.Role.MENU_ITEM;
+ }
+ this._updateOrnament();
+ }
+
+ activate() {
+ super.activate();
+
+ if (this.radio_mode && this._activeConnection != null)
+ return; // only activate in radio mode
+
+ if (this._activeConnection == null)
+ this._section.activateConnection(this._connection);
+ else
+ this._section.deactivateConnection(this._activeConnection);
+
+ this._sync();
+ }
+
+ setActiveConnection(connection) {
+ this._setActiveConnection(connection);
+ }
+});
+
+const NMDeviceConnectionItem = GObject.registerClass({
+ Properties: {
+ 'device-name': GObject.ParamSpec.string('device-name', '', '',
+ GObject.ParamFlags.READWRITE,
+ ''),
+ },
+}, class NMDeviceConnectionItem extends NMConnectionItem {
+ constructor(section, connection) {
+ super(section, connection);
+
+ this.connectObject(
+ 'notify::radio-mode', () => this.notify('name'),
+ 'notify::device-name', () => this.notify('name'),
+ this);
+ }
+
+ get name() {
+ return this.radioMode
+ ? this._connection.get_id()
+ : this.deviceName;
+ }
+});
+
+const NMDeviceItem = GObject.registerClass({
+ Properties: {
+ 'single-device-mode': GObject.ParamSpec.boolean('single-device-mode', '', '',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+}, class NMDeviceItem extends NMSectionItem {
+ constructor(client, device) {
+ super();
+
+ if (this.constructor === NMDeviceItem)
+ throw new TypeError(`Cannot instantiate abstract type ${this.constructor.name}`);
+
+ this._client = client;
+ this._device = device;
+ this._deviceName = '';
+
+ this._connectionItems = new Map();
+ this._itemSorter = new ItemSorter({trackMru: true});
+
+ // Item shown in the 0-connections case
+ this._autoConnectItem =
+ this.section.addAction(_('Connect'), () => this._autoConnect(), '');
+
+ // Represents the device as a whole when shown
+ this.bind_property('name',
+ this._autoConnectItem.label, 'text',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('icon-name',
+ this._autoConnectItem._icon, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._deactivateItem =
+ this.section.addAction(_('Turn Off'), () => this.deactivateConnection());
+
+ this._client.connectObject(
+ 'notify::connectivity', () => this.notify('icon-name'),
+ 'notify::primary-connection', () => this.notify('icon-name'),
+ this);
+
+ this._device.connectObject(
+ 'notify::available-connections', () => this._syncConnections(),
+ 'notify::active-connection', () => this._activeConnectionChanged(),
+ this);
+
+ this.connect('notify::single-device-mode', () => this._sync());
+
+ this._syncConnections();
+ this._activeConnectionChanged();
+ }
+
+ get timestamp() {
+ const [item] = this._itemSorter.itemsByMru();
+ return item?.timestamp ?? 0;
+ }
+
+ _canReachInternet() {
+ if (this._client.primary_connection !== this._device.active_connection)
+ return true;
+
+ return this._client.connectivity === NM.ConnectivityState.FULL;
+ }
+
+ _autoConnect() {
+ let connection = new NM.SimpleConnection();
+ this._client.add_and_activate_connection_async(connection, this._device, null, null, null);
+ }
+
+ _activeConnectionChanged() {
+ const oldItem = this._connectionItems.get(
+ this._activeConnection?.connection);
+ oldItem?.setActiveConnection(null);
+
+ this._setActiveConnection(this._device.active_connection);
+
+ const newItem = this._connectionItems.get(
+ this._activeConnection?.connection);
+ newItem?.setActiveConnection(this._activeConnection);
+ }
+
+ _syncConnections() {
+ const available = this._device.get_available_connections();
+ const removed = [...this._connectionItems.keys()]
+ .filter(conn => !available.includes(conn));
+
+ for (const conn of removed)
+ this._removeConnection(conn);
+
+ for (const conn of available)
+ this._addConnection(conn);
+ }
+
+ _getActivatableItem() {
+ const [lastUsed] = this._itemSorter.itemsByMru();
+ if (lastUsed?.timestamp > 0)
+ return lastUsed;
+
+ const [firstItem] = this._itemSorter;
+ if (firstItem)
+ return firstItem;
+
+ console.assert(this._autoConnectItem.visible,
+ `${this}'s autoConnect item should be visible when otherwise empty`);
+ return this._autoConnectItem;
+ }
+
+ activate() {
+ super.activate();
+
+ if (this._activeConnection)
+ this.deactivateConnection();
+ else
+ this._getActivatableItem()?.activate();
+ }
+
+ activateConnection(connection) {
+ this._client.activate_connection_async(connection, this._device, null, null, null);
+ }
+
+ deactivateConnection(_activeConnection) {
+ this._device.disconnect(null);
+ }
+
+ _onConnectionChanged(connection) {
+ const item = this._connectionItems.get(connection);
+ item.updateForConnection(connection);
+ }
+
+ _resortItem(item) {
+ const pos = this._itemSorter.upsert(item);
+ this.section.moveMenuItem(item, pos);
+ }
+
+ _addConnection(connection) {
+ if (this._connectionItems.has(connection))
+ return;
+
+ connection.connectObject(
+ 'changed', this._onConnectionChanged.bind(this),
+ this);
+
+ const item = new NMDeviceConnectionItem(this, connection);
+
+ this.bind_property('radio-mode',
+ item, 'radio-mode',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('name',
+ item, 'device-name',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('icon-name',
+ item, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE);
+ item.connectObject(
+ 'notify::name', () => this._resortItem(item),
+ this);
+
+ const pos = this._itemSorter.upsert(item);
+ this.section.addMenuItem(item, pos);
+ this._connectionItems.set(connection, item);
+ this._sync();
+ }
+
+ _removeConnection(connection) {
+ const item = this._connectionItems.get(connection);
+ if (!item)
+ return;
+
+ this._itemSorter.delete(item);
+ this._connectionItems.delete(connection);
+ item.destroy();
+
+ this._sync();
+ }
+
+ setDeviceName(name) {
+ this._deviceName = name;
+ this.notify('name');
+ }
+
+ _sync() {
+ const nItems = this._connectionItems.size;
+ this.radio_mode = nItems > 1;
+ this.useSubmenu = this.radioMode && !this.singleDeviceMode;
+ this._autoConnectItem.visible = nItems === 0;
+ this._deactivateItem.visible = this.radioMode && this.isActive;
+ }
+});
+
+const NMWiredDeviceItem = GObject.registerClass(
+class NMWiredDeviceItem extends NMDeviceItem {
+ get icon_name() {
+ switch (this.state) {
+ case NM.ActiveConnectionState.ACTIVATING:
+ return 'network-wired-acquiring-symbolic';
+ case NM.ActiveConnectionState.ACTIVATED:
+ return this._canReachInternet()
+ ? 'network-wired-symbolic'
+ : 'network-wired-no-route-symbolic';
+ default:
+ return 'network-wired-disconnected-symbolic';
+ }
+ }
+
+ get name() {
+ return this._deviceName;
+ }
+
+ _hasCarrier() {
+ if (this._device instanceof NM.DeviceEthernet)
+ return this._device.carrier;
+ else
+ return true;
+ }
+
+ _sync() {
+ this.visible = this._hasCarrier();
+ super._sync();
+ }
+});
+
+const NMModemDeviceItem = GObject.registerClass(
+class NMModemDeviceItem extends NMDeviceItem {
+ constructor(client, device) {
+ super(client, device);
+
+ this._mobileDevice = null;
+
+ let capabilities = device.current_capabilities;
+ if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0)
+ this._mobileDevice = new ModemManager.BroadbandModem(device.udi, capabilities);
+ else if (capabilities & NM.DeviceModemCapabilities.GSM_UMTS)
+ this._mobileDevice = new ModemManager.ModemGsm(device.udi);
+ else if (capabilities & NM.DeviceModemCapabilities.CDMA_EVDO)
+ this._mobileDevice = new ModemManager.ModemCdma(device.udi);
+ else if (capabilities & NM.DeviceModemCapabilities.LTE)
+ this._mobileDevice = new ModemManager.ModemGsm(device.udi);
+
+ this._mobileDevice?.connectObject(
+ 'notify::operator-name', this._sync.bind(this),
+ 'notify::signal-quality', () => this.notify('icon-name'), this);
+
+ Main.sessionMode.connectObject('updated',
+ this._sessionUpdated.bind(this), this);
+ this._sessionUpdated();
+ }
+
+ get icon_name() {
+ switch (this.state) {
+ case NM.ActiveConnectionState.ACTIVATING:
+ return 'network-cellular-acquiring-symbolic';
+ case NM.ActiveConnectionState.ACTIVATED: {
+ const qualityString = signalToIcon(this._mobileDevice.signal_quality);
+ return `network-cellular-signal-${qualityString}-symbolic`;
+ }
+ default:
+ return this._activeConnection
+ ? 'network-cellular-signal-none-symbolic'
+ : 'network-cellular-disabled-symbolic';
+ }
+ }
+
+ get name() {
+ return this._mobileDevice?.operator_name || this._deviceName;
+ }
+
+ get wwanPanelSupported() {
+ // Currently, wwan panel doesn't support CDMA_EVDO modems
+ const supportedCaps =
+ NM.DeviceModemCapabilities.GSM_UMTS |
+ NM.DeviceModemCapabilities.LTE;
+ return this._device.current_capabilities & supportedCaps;
+ }
+
+ _autoConnect() {
+ if (this.wwanPanelSupported)
+ launchSettingsPanel('wwan', 'show-device', this._device.udi);
+ else
+ launchSettingsPanel('network', 'connect-3g', this._device.get_path());
+ }
+
+ _sessionUpdated() {
+ this._autoConnectItem.sensitive = Main.sessionMode.hasWindows;
+ }
+});
+
+const NMBluetoothDeviceItem = GObject.registerClass(
+class NMBluetoothDeviceItem extends NMDeviceItem {
+ constructor(client, device) {
+ super(client, device);
+
+ this._device.bind_property('name',
+ this, 'name',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ get icon_name() {
+ switch (this.state) {
+ case NM.ActiveConnectionState.ACTIVATING:
+ return 'network-cellular-acquiring-symbolic';
+ case NM.ActiveConnectionState.ACTIVATED:
+ return 'network-cellular-connected-symbolic';
+ default:
+ return this._activeConnection
+ ? 'network-cellular-signal-none-symbolic'
+ : 'network-cellular-disabled-symbolic';
+ }
+ }
+
+ get name() {
+ return this._device.name;
+ }
+});
+
+const WirelessNetwork = GObject.registerClass({
+ Properties: {
+ 'name': GObject.ParamSpec.string(
+ 'name', '', '',
+ GObject.ParamFlags.READABLE,
+ ''),
+ 'icon-name': GObject.ParamSpec.string(
+ 'icon-name', '', '',
+ GObject.ParamFlags.READABLE,
+ ''),
+ 'secure': GObject.ParamSpec.boolean(
+ 'secure', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'is-active': GObject.ParamSpec.boolean(
+ 'is-active', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ },
+ Signals: {
+ 'destroy': {},
+ },
+}, class WirelessNetwork extends GObject.Object {
+ static _securityTypes =
+ Object.values(NM.UtilsSecurityType).sort((a, b) => b - a);
+
+ _init(device) {
+ super._init();
+
+ this._device = device;
+
+ this._device.connectObject(
+ 'notify::active-access-point', () => this.notify('is-active'),
+ this);
+
+ this._accessPoints = new Set();
+ this._connections = [];
+ this._name = '';
+ this._ssid = null;
+ this._bestAp = null;
+ this._mode = 0;
+ this._securityType = NM.UtilsSecurityType.NONE;
+ }
+
+ get _strength() {
+ return this._bestAp?.strength ?? 0;
+ }
+
+ get name() {
+ return this._name;
+ }
+
+ get icon_name() {
+ if (this._mode === NM80211Mode.ADHOC)
+ return 'network-workgroup-symbolic';
+
+ if (!this._bestAp)
+ return '';
+
+ return `network-wireless-signal-${signalToIcon(this._bestAp.strength)}-symbolic`;
+ }
+
+ get secure() {
+ return this._securityType !== NM.UtilsSecurityType.NONE;
+ }
+
+ get is_active() {
+ return this._accessPoints.has(this._device.activeAccessPoint);
+ }
+
+ hasAccessPoint(ap) {
+ return this._accessPoints.has(ap);
+ }
+
+ hasAccessPoints() {
+ return this._accessPoints.size > 0;
+ }
+
+ checkAccessPoint(ap) {
+ if (!ap.get_ssid())
+ return false;
+
+ const secType = this._getApSecurityType(ap);
+ if (secType === NM.UtilsSecurityType.INVALID)
+ return false;
+
+ if (this._accessPoints.size === 0)
+ return true;
+
+ return this._ssid.equal(ap.ssid) &&
+ this._mode === ap.mode &&
+ this._securityType === secType;
+ }
+
+ /**
+ * @param {NM.AccessPoint} ap - an access point
+ * @returns {bool} - whether the access point was added
+ */
+ addAccessPoint(ap) {
+ if (!this.checkAccessPoint(ap))
+ return false;
+
+ if (this._accessPoints.size === 0) {
+ this._ssid = ap.get_ssid();
+ this._mode = ap.mode;
+ this._securityType = this._getApSecurityType(ap);
+ this._name = NM.utils_ssid_to_utf8(this._ssid.get_data()) || '<unknown>';
+
+ this.notify('name');
+ this.notify('secure');
+ }
+
+ const wasActive = this.is_active;
+ this._accessPoints.add(ap);
+
+ ap.connectObject(
+ 'notify::strength', () => {
+ this.notify('icon-name');
+ this._updateBestAp();
+ }, this);
+ this._updateBestAp();
+
+ if (wasActive !== this.is_active)
+ this.notify('is-active');
+
+ return true;
+ }
+
+ /**
+ * @param {NM.AccessPoint} ap - an access point
+ * @returns {bool} - whether the access point was removed
+ */
+ removeAccessPoint(ap) {
+ const wasActive = this.is_active;
+ if (!this._accessPoints.delete(ap))
+ return false;
+
+ ap.disconnectObject(this);
+ this._updateBestAp();
+
+ if (wasActive !== this.is_active)
+ this.notify('is-active');
+
+ return true;
+ }
+
+ /**
+ * @param {WirelessNetwork} other - network to compare with
+ * @returns {number} - the sort order
+ */
+ compare(other) {
+ // place known connections first
+ const cmpConnections = other.hasConnections() - this.hasConnections();
+ if (cmpConnections !== 0)
+ return cmpConnections;
+
+ const cmpAps = other.hasAccessPoints() - this.hasAccessPoints();
+ if (cmpAps !== 0)
+ return cmpAps;
+
+ // place stronger connections first
+ const cmpStrength = other._strength - this._strength;
+ if (cmpStrength !== 0)
+ return cmpStrength;
+
+ // place secure connections first
+ const cmpSec = other.secure - this.secure;
+ if (cmpSec !== 0)
+ return cmpSec;
+
+ // sort alphabetically
+ return GLib.utf8_collate(this._name, other._name);
+ }
+
+ hasConnections() {
+ return this._connections.length > 0;
+ }
+
+ checkConnections(connections) {
+ const aps = [...this._accessPoints];
+ this._connections = connections.filter(
+ c => aps.some(ap => ap.connection_valid(c)));
+ }
+
+ canAutoconnect() {
+ const canAutoconnect =
+ this._securityTypes !== NM.UtilsSecurityType.WPA_ENTERPRISE &&
+ this._securityTypes !== NM.UtilsSecurityType.WPA2_ENTERPRISE;
+ return canAutoconnect;
+ }
+
+ activate() {
+ const [ap] = this._accessPoints;
+ let [conn] = this._connections;
+ if (conn) {
+ this._device.client.activate_connection_async(conn, this._device, null, null, null);
+ } else if (!this.canAutoconnect()) {
+ launchSettingsPanel('wifi', 'connect-8021x-wifi',
+ this._getDeviceDBusPath(), ap.get_path());
+ } else {
+ conn = new NM.SimpleConnection();
+ this._device.client.add_and_activate_connection_async(
+ conn, this._device, ap.get_path(), null, null);
+ }
+ }
+
+ destroy() {
+ this.emit('destroy');
+ }
+
+ _getDeviceDBusPath() {
+ // nm_object_get_path() is shadowed by nm_device_get_path()
+ return NM.Object.prototype.get_path.call(this._device);
+ }
+
+ _getApSecurityType(ap) {
+ const {wirelessCapabilities: caps} = this._device;
+ const {flags, wpaFlags, rsnFlags} = ap;
+ const haveAp = true;
+ const adHoc = ap.mode === NM80211Mode.ADHOC;
+ const bestType = WirelessNetwork._securityTypes
+ .find(t => NM.utils_security_valid(t, caps, haveAp, adHoc, flags, wpaFlags, rsnFlags));
+ return bestType ?? NM.UtilsSecurityType.INVALID;
+ }
+
+ _updateBestAp() {
+ const [bestAp] =
+ [...this._accessPoints].sort((a, b) => b.strength - a.strength);
+
+ if (this._bestAp === bestAp)
+ return;
+
+ this._bestAp = bestAp;
+ this.notify('icon-name');
+ }
+});
+registerDestroyableType(WirelessNetwork);
+
+const NMWirelessNetworkItem = GObject.registerClass(
+class NMWirelessNetworkItem extends PopupMenu.PopupBaseMenuItem {
+ _init(network) {
+ super._init({style_class: 'nm-network-item'});
+
+ this._network = network;
+
+ const icons = new St.BoxLayout();
+ this.add_child(icons);
+
+ this._signalIcon = new St.Icon({style_class: 'popup-menu-icon'});
+ icons.add_child(this._signalIcon);
+
+ this._secureIcon = new St.Icon({
+ style_class: 'wireless-secure-icon',
+ y_align: Clutter.ActorAlign.END,
+ });
+ icons.add_actor(this._secureIcon);
+
+ this._label = new St.Label();
+ this.label_actor = this._label;
+ this.add_child(this._label);
+
+ this._selectedIcon = new St.Icon({
+ style_class: 'popup-menu-icon',
+ icon_name: 'object-select-symbolic',
+ });
+ this.add(this._selectedIcon);
+
+ this._network.bind_property('icon-name',
+ this._signalIcon, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.bind_property('name',
+ this._label, 'text',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.bind_property('is-active',
+ this._selectedIcon, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.bind_property_full('secure',
+ this._secureIcon, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE,
+ (bind, source) => [true, source ? 'network-wireless-encrypted-symbolic' : ''],
+ null);
+ }
+
+ get network() {
+ return this._network;
+ }
+});
+
+const NMWirelessDeviceItem = GObject.registerClass({
+ Properties: {
+ 'is-hotspot': GObject.ParamSpec.boolean('is-hotspot', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'single-device-mode': GObject.ParamSpec.boolean('single-device-mode', '', '',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+}, class NMWirelessDeviceItem extends NMSectionItem {
+ constructor(client, device) {
+ super();
+
+ this._client = client;
+ this._device = device;
+
+ this._deviceName = '';
+
+ this._networkItems = new Map();
+ this._itemSorter = new ItemSorter({
+ sortFunc: (one, two) => one.network.compare(two.network),
+ });
+
+ this._client.connectObject(
+ 'notify::wireless-enabled', () => this.notify('icon-name'),
+ 'notify::connectivity', () => this.notify('icon-name'),
+ 'notify::primary-connection', () => this.notify('icon-name'),
+ this);
+
+ this._device.connectObject(
+ 'notify::active-access-point', this._activeApChanged.bind(this),
+ 'notify::active-connection', () => this._activeConnectionChanged(),
+ 'notify::available-connections', () => this._availableConnectionsChanged(),
+ 'state-changed', () => this.notify('is-hotspot'),
+ 'access-point-added', (d, ap) => {
+ this._addAccessPoint(ap);
+ this._updateItemsVisibility();
+ },
+ 'access-point-removed', (d, ap) => {
+ this._removeAccessPoint(ap);
+ this._updateItemsVisibility();
+ }, this);
+
+ this.bind_property('single-device-mode',
+ this, 'use-submenu',
+ GObject.BindingFlags.INVERT_BOOLEAN);
+
+ Main.sessionMode.connectObject('updated',
+ () => this._updateItemsVisibility(),
+ this);
+
+ for (const ap of this._device.get_access_points())
+ this._addAccessPoint(ap);
+
+ this._activeApChanged();
+ this._activeConnectionChanged();
+ this._availableConnectionsChanged();
+ this._updateItemsVisibility();
+
+ this.connect('destroy', () => {
+ for (const net of this._networkItems.keys())
+ net.destroy();
+ });
+ }
+
+ get icon_name() {
+ if (!this._device.client.wireless_enabled)
+ return 'network-wireless-disabled-symbolic';
+
+ switch (this.state) {
+ case NM.ActiveConnectionState.ACTIVATING:
+ return 'network-wireless-acquiring-symbolic';
+
+ case NM.ActiveConnectionState.ACTIVATED: {
+ if (this.is_hotspot)
+ return 'network-wireless-hotspot-symbolic';
+
+ if (!this._canReachInternet())
+ return 'network-wireless-no-route-symbolic';
+
+ if (!this._activeAccessPoint) {
+ if (this._device.mode !== NM80211Mode.ADHOC)
+ console.info('An active wireless connection, in infrastructure mode, involves no access point?');
+
+ return 'network-wireless-connected-symbolic';
+ }
+
+ const {strength} = this._activeAccessPoint;
+ return `network-wireless-signal-${signalToIcon(strength)}-symbolic`;
+ }
+ default:
+ return 'network-wireless-signal-none-symbolic';
+ }
+ }
+
+ get name() {
+ if (this.is_hotspot)
+ /* Translators: %s is a network identifier */
+ return _('%s Hotspot').format(this._deviceName);
+
+ const {ssid} = this._activeAccessPoint ?? {};
+ if (ssid)
+ return ssidToLabel(ssid);
+
+ return this._deviceName;
+ }
+
+ get is_hotspot() {
+ if (!this._device.active_connection)
+ return false;
+
+ const {connection} = this._device.active_connection;
+ if (!connection)
+ return false;
+
+ let ip4config = connection.get_setting_ip4_config();
+ if (!ip4config)
+ return false;
+
+ return ip4config.get_method() === NM.SETTING_IP4_CONFIG_METHOD_SHARED;
+ }
+
+ activate() {
+ if (!this.is_hotspot)
+ return;
+
+ const {activeConnection} = this._device;
+ this._client.deactivate_connection_async(activeConnection, null, null);
+ }
+
+ _activeApChanged() {
+ this._activeAccessPoint?.disconnectObject(this);
+ this._activeAccessPoint = this._device.active_access_point;
+ this._activeAccessPoint?.connectObject(
+ 'notify::strength', () => this.notify('icon-name'),
+ 'notify::ssid', () => this.notify('name'),
+ this);
+
+ this.notify('icon-name');
+ this.notify('name');
+ }
+
+ _activeConnectionChanged() {
+ this._setActiveConnection(this._device.active_connection);
+ }
+
+ _availableConnectionsChanged() {
+ const connections = this._device.get_available_connections();
+ for (const net of this._networkItems.keys())
+ net.checkConnections(connections);
+ }
+
+ _addAccessPoint(ap) {
+ if (ap.get_ssid() == null) {
+ // This access point is not visible yet
+ // Wait for it to get a ssid
+ ap.connectObject('notify::ssid', () => {
+ if (!ap.ssid)
+ return;
+ ap.disconnectObject(this);
+ this._addAccessPoint(ap);
+ }, this);
+ return;
+ }
+
+ let network = [...this._networkItems.keys()]
+ .find(n => n.checkAccessPoint(ap));
+
+ if (!network) {
+ network = new WirelessNetwork(this._device);
+
+ const item = new NMWirelessNetworkItem(network);
+ item.connect('activate', () => network.activate());
+
+ network.connectObject(
+ 'notify::icon-name', () => this._resortItem(item),
+ 'notify::is-active', () => this._resortItem(item),
+ this);
+
+ const pos = this._itemSorter.upsert(item);
+ this.section.addMenuItem(item, pos);
+ this._networkItems.set(network, item);
+ }
+
+ network.addAccessPoint(ap);
+ }
+
+ _removeAccessPoint(ap) {
+ const network = [...this._networkItems.keys()]
+ .find(n => n.removeAccessPoint(ap));
+
+ if (!network || network.hasAccessPoints())
+ return;
+
+ const item = this._networkItems.get(network);
+ this._itemSorter.delete(item);
+ this._networkItems.delete(network);
+
+ item?.destroy();
+ network.destroy();
+ }
+
+ _resortItem(item) {
+ const pos = this._itemSorter.upsert(item);
+ this.section.moveMenuItem(item, pos);
+
+ this._updateItemsVisibility();
+ }
+
+ _updateItemsVisibility() {
+ const {hasWindows} = Main.sessionMode;
+
+ let nVisible = 0;
+ for (const item of this._itemSorter) {
+ const {network: net} = item;
+ item.visible =
+ (hasWindows || net.hasConnections() || net.canAutoconnect()) &&
+ nVisible < MAX_VISIBLE_NETWORKS;
+ if (item.visible)
+ nVisible++;
+ }
+ }
+
+ setDeviceName(name) {
+ this._deviceName = name;
+ this.notify('name');
+ }
+
+ _canReachInternet() {
+ if (this._client.primary_connection !== this._device.active_connection)
+ return true;
+
+ return this._client.connectivity === NM.ConnectivityState.FULL;
+ }
+});
+
+const NMVpnConnectionItem = GObject.registerClass({
+ Signals: {
+ 'activation-failed': {},
+ },
+}, class NMVpnConnectionItem extends NMConnectionItem {
+ constructor(section, connection) {
+ super(section, connection);
+
+ this._label.x_expand = true;
+ this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
+ this._icon.hide();
+
+ this._switch = new PopupMenu.Switch(this.is_active);
+ this.add_child(this._switch);
+
+ this.bind_property('is-active',
+ this._switch, 'state',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('name',
+ this._label, 'text',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ _sync() {
+ if (this.is_active)
+ this.add_accessible_state(Atk.StateType.CHECKED);
+ else
+ this.remove_accessible_state(Atk.StateType.CHECKED);
+ }
+
+ _activeConnectionStateChanged() {
+ const state = this._activeConnection?.get_state();
+ const reason = this._activeConnection?.get_state_reason();
+
+ if (state === NM.ActiveConnectionState.DEACTIVATED &&
+ reason !== NM.ActiveConnectionStateReason.NO_SECRETS &&
+ reason !== NM.ActiveConnectionStateReason.USER_DISCONNECTED)
+ this.emit('activation-failed');
+
+ super._activeConnectionStateChanged();
+ }
+
+ get icon_name() {
+ switch (this.state) {
+ case NM.ActiveConnectionState.ACTIVATING:
+ return 'network-vpn-acquiring-symbolic';
+ case NM.ActiveConnectionState.ACTIVATED:
+ return 'network-vpn-symbolic';
+ default:
+ return 'network-vpn-disabled-symbolic';
+ }
+ }
+
+ set icon_name(_ignored) {
+ }
+});
+
+const NMToggle = GObject.registerClass({
+ Signals: {
+ 'activation-failed': {},
+ },
+}, class NMToggle extends QuickMenuToggle {
+ constructor() {
+ super();
+
+ this._items = new Map();
+ this._itemSorter = new ItemSorter({trackMru: true});
+
+ this._itemsSection = new PopupMenu.PopupMenuSection();
+ this.menu.addMenuItem(this._itemsSection);
+
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._itemBinding = new GObject.BindingGroup();
+ this._itemBinding.bind('icon-name',
+ this, 'icon-name', GObject.BindingFlags.DEFAULT);
+ this._itemBinding.bind_full('name',
+ this, 'label', GObject.BindingFlags.DEFAULT,
+ (bind, source) => [true, this._transformLabel(source)],
+ null);
+
+ this.connect('clicked', () => this.activate());
+ }
+
+ setClient(client) {
+ if (this._client === client)
+ return;
+
+ this._client?.disconnectObject(this);
+ this._client = client;
+ this._client?.connectObject(
+ 'notify::networking-enabled', () => this._sync(),
+ this);
+
+ this._items.forEach(item => item.destroy());
+ this._items.clear();
+
+ if (this._client)
+ this._loadInitialItems();
+ this._sync();
+ }
+
+ activate() {
+ const activeItems = [...this._getActiveItems()];
+
+ if (activeItems.length > 0)
+ activeItems.forEach(i => i.activate());
+ else
+ this._itemBinding.source?.activate();
+ }
+
+ _loadInitialItems() {
+ throw new GObject.NotImplementedError();
+ }
+
+ // transform function for property binding:
+ // Ignore the provided label if there are multiple active
+ // items, and replace it with something like "VPN (2)"
+ _transformLabel(source) {
+ const nActive = this.checked
+ ? [...this._getActiveItems()].length
+ : 0;
+ if (nActive > 1)
+ return `${this._getDefaultName()} (${nActive})`;
+ return source;
+ }
+
+ _updateItemsVisibility() {
+ [...this._itemSorter.itemsByMru()].forEach(
+ (item, i) => (item.visible = i < MAX_VISIBLE_NETWORKS));
+ }
+
+ _itemActiveChanged() {
+ // force an update in case we changed
+ // from or to multiple active items
+ this._itemBinding.source?.notify('name');
+ this._sync();
+ }
+
+ _updateChecked() {
+ const [firstActive] = this._getActiveItems();
+ this.checked = !!firstActive;
+ }
+
+ _resortItem(item) {
+ const pos = this._itemSorter.upsert(item);
+ this._itemsSection.moveMenuItem(item, pos);
+ }
+
+ _addItem(key, item) {
+ console.assert(!this._items.has(key),
+ `${this} already has an item for ${key}`);
+
+ item.connectObject(
+ 'notify::is-active', () => this._itemActiveChanged(),
+ 'notify::name', () => this._resortItem(item),
+ 'destroy', () => this._removeItem(key),
+ this);
+
+ this._items.set(key, item);
+ const pos = this._itemSorter.upsert(item);
+ this._itemsSection.addMenuItem(item, pos);
+ this._sync();
+ }
+
+ _removeItem(key) {
+ const item = this._items.get(key);
+ if (!item)
+ return;
+
+ this._itemSorter.delete(item);
+ this._items.delete(key);
+
+ item.destroy();
+ this._sync();
+ }
+
+ *_getActiveItems() {
+ for (const item of this._itemSorter) {
+ if (item.is_active)
+ yield item;
+ }
+ }
+
+ _getPrimaryItem() {
+ // prefer active items
+ const [firstActive] = this._getActiveItems();
+ if (firstActive)
+ return firstActive;
+
+ // otherwise prefer the most-recently used
+ const [lastUsed] = this._itemSorter.itemsByMru();
+ if (lastUsed?.timestamp > 0)
+ return lastUsed;
+
+ // as a last resort, return the top-most visible item
+ for (const item of this._itemSorter) {
+ if (item.visible)
+ return item;
+ }
+
+ console.assert(!this.visible,
+ `${this} should not be visible when empty`);
+
+ return null;
+ }
+
+ _sync() {
+ this.visible =
+ this._client?.networking_enabled && this._items.size > 0;
+ this._updateItemsVisibility();
+ this._updateChecked();
+ this._itemBinding.source = this._getPrimaryItem();
+ }
+});
+
+const NMVpnToggle = GObject.registerClass(
+class NMVpnToggle extends NMToggle {
+ constructor() {
+ super();
+
+ this.menu.setHeader('network-vpn-symbolic', _('VPN'));
+ this.menu.addSettingsAction(_('VPN Settings'),
+ 'gnome-network-panel.desktop');
+ }
+
+ setClient(client) {
+ super.setClient(client);
+
+ this._client?.connectObject(
+ 'connection-added', (c, conn) => this._addConnection(conn),
+ 'connection-removed', (c, conn) => this._removeConnection(conn),
+ 'notify::active-connections', () => this._syncActiveConnections(),
+ this);
+ }
+
+ _getDefaultName() {
+ return _('VPN');
+ }
+
+ _loadInitialItems() {
+ const connections = this._client.get_connections();
+ for (const conn of connections)
+ this._addConnection(conn);
+
+ this._syncActiveConnections();
+ }
+
+ _syncActiveConnections() {
+ const activeConnections =
+ this._client.get_active_connections().filter(
+ c => this._shouldHandleConnection(c.connection));
+
+ for (const item of this._items.values())
+ item.setActiveConnection(null);
+
+ for (const a of activeConnections)
+ this._items.get(a.connection)?.setActiveConnection(a);
+ }
+
+ _shouldHandleConnection(connection) {
+ const setting = connection.get_setting_connection();
+ if (!setting)
+ return false;
+
+ // Ignore slave connection
+ if (setting.get_master())
+ return false;
+
+ const handledTypes = [
+ NM.SETTING_VPN_SETTING_NAME,
+ NM.SETTING_WIREGUARD_SETTING_NAME,
+ ];
+ return handledTypes.includes(setting.type);
+ }
+
+ _onConnectionChanged(connection) {
+ const item = this._items.get(connection);
+ item.updateForConnection(connection);
+ }
+
+ _addConnection(connection) {
+ if (this._items.has(connection))
+ return;
+
+ if (!this._shouldHandleConnection(connection))
+ return;
+
+ connection.connectObject(
+ 'changed', this._onConnectionChanged.bind(this),
+ this);
+
+ const item = new NMVpnConnectionItem(this, connection);
+ item.connectObject(
+ 'activation-failed', () => this.emit('activation-failed'),
+ this);
+ this._addItem(connection, item);
+ }
+
+ _removeConnection(connection) {
+ this._removeItem(connection);
+ }
+
+ activateConnection(connection) {
+ this._client.activate_connection_async(connection, null, null, null, null);
+ }
+
+ deactivateConnection(activeConnection) {
+ this._client.deactivate_connection(activeConnection, null);
+ }
+});
+
+const NMDeviceToggle = GObject.registerClass(
+class NMDeviceToggle extends NMToggle {
+ constructor(deviceType) {
+ super();
+
+ this._deviceType = deviceType;
+ this._nmDevices = new Set();
+ this._deviceNames = new Map();
+ }
+
+ setClient(client) {
+ this._nmDevices.clear();
+
+ super.setClient(client);
+
+ this._client?.connectObject(
+ 'device-added', (c, dev) => {
+ this._addDevice(dev);
+ this._syncDeviceNames();
+ },
+ 'device-removed', (c, dev) => {
+ this._removeDevice(dev);
+ this._syncDeviceNames();
+ }, this);
+ }
+
+ _getDefaultName() {
+ const [dev] = this._nmDevices;
+ const [name] = NM.Device.disambiguate_names([dev]);
+ return name;
+ }
+
+ _loadInitialItems() {
+ const devices = this._client.get_devices();
+ for (const dev of devices)
+ this._addDevice(dev);
+ this._syncDeviceNames();
+ }
+
+ _shouldShowDevice(device) {
+ switch (device.state) {
+ case NM.DeviceState.DISCONNECTED:
+ case NM.DeviceState.ACTIVATED:
+ case NM.DeviceState.DEACTIVATING:
+ case NM.DeviceState.PREPARE:
+ case NM.DeviceState.CONFIG:
+ case NM.DeviceState.IP_CONFIG:
+ case NM.DeviceState.IP_CHECK:
+ case NM.DeviceState.SECONDARIES:
+ case NM.DeviceState.NEED_AUTH:
+ case NM.DeviceState.FAILED:
+ return true;
+ case NM.DeviceState.UNMANAGED:
+ case NM.DeviceState.UNAVAILABLE:
+ default:
+ return false;
+ }
+ }
+
+ _syncDeviceNames() {
+ const devices = [...this._nmDevices];
+ const names = NM.Device.disambiguate_names(devices);
+ this._deviceNames.clear();
+ devices.forEach(
+ (dev, i) => {
+ this._deviceNames.set(dev, names[i]);
+ this._items.get(dev)?.setDeviceName(names[i]);
+ });
+ }
+
+ _syncDeviceItem(device) {
+ if (this._shouldShowDevice(device))
+ this._ensureDeviceItem(device);
+ else
+ this._removeDeviceItem(device);
+ }
+
+ _deviceStateChanged(device, newState, oldState, reason) {
+ if (newState === oldState) {
+ console.info(`${device} emitted state-changed without actually changing state`);
+ return;
+ }
+
+ /* Emit a notification if activation fails, but don't do it
+ if the reason is no secrets, as that indicates the user
+ cancelled the agent dialog */
+ if (newState === NM.DeviceState.FAILED &&
+ reason !== NM.DeviceStateReason.NO_SECRETS)
+ this.emit('activation-failed');
+ }
+
+ _createDeviceMenuItem(_device) {
+ throw new GObject.NotImplementedError();
+ }
+
+ _ensureDeviceItem(device) {
+ if (this._items.has(device))
+ return;
+
+ const item = this._createDeviceMenuItem(device);
+ item.setDeviceName(this._deviceNames.get(device) ?? '');
+ this._addItem(device, item);
+ }
+
+ _removeDeviceItem(device) {
+ this._removeItem(device);
+ }
+
+ _addDevice(device) {
+ if (this._nmDevices.has(device))
+ return;
+
+ if (device.get_device_type() !== this._deviceType)
+ return;
+
+ device.connectObject(
+ 'state-changed', this._deviceStateChanged.bind(this),
+ 'notify::interface', () => this._syncDeviceNames(),
+ 'notify::state', () => this._syncDeviceItem(device),
+ this);
+
+ this._nmDevices.add(device);
+ this._syncDeviceItem(device);
+ }
+
+ _removeDevice(device) {
+ if (!this._nmDevices.delete(device))
+ return;
+
+ device.disconnectObject(this);
+ this._removeDeviceItem(device);
+ }
+
+ _sync() {
+ super._sync();
+
+ const nItems = this._items.size;
+ this._items.forEach(item => (item.singleDeviceMode = nItems === 1));
+ }
+});
+
+const NMWirelessToggle = GObject.registerClass(
+class NMWirelessToggle extends NMDeviceToggle {
+ constructor() {
+ super(NM.DeviceType.WIFI);
+
+ this._itemBinding.bind('is-hotspot',
+ this, 'menu-enabled',
+ GObject.BindingFlags.INVERT_BOOLEAN);
+
+ this._scanningSpinner = new Spinner(16);
+
+ this.menu.connectObject('open-state-changed', (m, isOpen) => {
+ if (isOpen)
+ this._startScanning();
+ else
+ this._stopScanning();
+ });
+
+ this.menu.setHeader('network-wireless-symbolic', _('Wi–Fi'));
+ this.menu.addHeaderSuffix(this._scanningSpinner);
+ this.menu.addSettingsAction(_('All Networks'),
+ 'gnome-wifi-panel.desktop');
+ }
+
+ setClient(client) {
+ super.setClient(client);
+
+ this._client?.bind_property('wireless-enabled',
+ this, 'checked',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._client?.bind_property('wireless-hardware-enabled',
+ this, 'reactive',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ activate() {
+ const primaryItem = this._itemBinding.source;
+ if (primaryItem?.is_hotspot)
+ primaryItem.activate();
+ else
+ this._client.wireless_enabled = !this._client.wireless_enabled;
+ }
+
+ async _scanDevice(device) {
+ const {lastScan} = device;
+ await device.request_scan_async(null);
+
+ // Wait for the lastScan property to update, which
+ // indicates the end of the scan
+ return new Promise(resolve => {
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1500, () => {
+ if (device.lastScan === lastScan)
+ return GLib.SOURCE_CONTINUE;
+
+ resolve();
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+ }
+
+ async _scanDevices() {
+ if (!this._client.wireless_enabled)
+ return;
+
+ this._scanningSpinner.play();
+
+ const devices = [...this._items.keys()];
+ await Promise.all(
+ devices.map(d => this._scanDevice(d)));
+
+ this._scanningSpinner.stop();
+ }
+
+ _startScanning() {
+ this._scanTimeoutId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT, WIFI_SCAN_FREQUENCY, () => {
+ this._scanDevices().catch(logError);
+ return GLib.SOURCE_CONTINUE;
+ });
+ this._scanDevices().catch(logError);
+ }
+
+ _stopScanning() {
+ if (this._scanTimeoutId)
+ GLib.source_remove(this._scanTimeoutId);
+ delete this._scanTimeoutId;
+ }
+
+ _createDeviceMenuItem(device) {
+ return new NMWirelessDeviceItem(this._client, device);
+ }
+
+ _updateChecked() {
+ // handled via a property binding
+ }
+
+ _getPrimaryItem() {
+ const hotspot = [...this._items.values()].find(i => i.is_hotspot);
+ if (hotspot)
+ return hotspot;
+
+ return super._getPrimaryItem();
+ }
+
+ _shouldShowDevice(device) {
+ // don't disappear if wireless-enabled is false
+ if (device.state === NM.DeviceState.UNAVAILABLE)
+ return true;
+ return super._shouldShowDevice(device);
+ }
+});
+
+const NMWiredToggle = GObject.registerClass(
+class NMWiredToggle extends NMDeviceToggle {
+ constructor() {
+ super(NM.DeviceType.ETHERNET);
+
+ this.menu.setHeader('network-wired-symbolic', _('Wired Connections'));
+ this.menu.addSettingsAction(_('Wired Settings'),
+ 'gnome-network-panel.desktop');
+ }
+
+ _createDeviceMenuItem(device) {
+ return new NMWiredDeviceItem(this._client, device);
+ }
+});
+
+const NMBluetoothToggle = GObject.registerClass(
+class NMBluetoothToggle extends NMDeviceToggle {
+ constructor() {
+ super(NM.DeviceType.BT);
+
+ this.menu.setHeader('network-cellular-symbolic', _('Bluetooth Tethers'));
+ this.menu.addSettingsAction(_('Bluetooth Settings'),
+ 'gnome-network-panel.desktop');
+ }
+
+ _createDeviceMenuItem(device) {
+ return new NMBluetoothDeviceItem(this._client, device);
+ }
+});
+
+const NMModemToggle = GObject.registerClass(
+class NMModemToggle extends NMDeviceToggle {
+ constructor() {
+ super(NM.DeviceType.MODEM);
+
+ this.menu.setHeader('network-cellular-symbolic', _('Mobile Connections'));
+
+ const settingsLabel = _('Mobile Broadband Settings');
+ this._wwanSettings = this.menu.addSettingsAction(settingsLabel,
+ 'gnome-wwan-panel.desktop');
+ this._legacySettings = this.menu.addSettingsAction(settingsLabel,
+ 'gnome-network-panel.desktop');
+ }
+
+ _createDeviceMenuItem(device) {
+ return new NMModemDeviceItem(this._client, device);
+ }
+
+ _sync() {
+ super._sync();
+
+ const useWwanPanel =
+ [...this._items.values()].some(i => i.wwanPanelSupported);
+ this._wwanSettings.visible = useWwanPanel;
+ this._legacySettings.visible = !useWwanPanel;
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._connectivityQueue = new Set();
+
+ this._mainConnection = null;
+
+ this._notification = null;
+
+ this._wiredToggle = new NMWiredToggle();
+ this._wirelessToggle = new NMWirelessToggle();
+ this._modemToggle = new NMModemToggle();
+ this._btToggle = new NMBluetoothToggle();
+ this._vpnToggle = new NMVpnToggle();
+
+ this._deviceToggles = new Map([
+ [NM.DeviceType.ETHERNET, this._wiredToggle],
+ [NM.DeviceType.WIFI, this._wirelessToggle],
+ [NM.DeviceType.MODEM, this._modemToggle],
+ [NM.DeviceType.BT, this._btToggle],
+ ]);
+ this.quickSettingsItems.push(...this._deviceToggles.values());
+ this.quickSettingsItems.push(this._vpnToggle);
+
+ this.quickSettingsItems.forEach(toggle => {
+ toggle.connectObject(
+ 'activation-failed', () => this._onActivationFailed(),
+ this);
+ });
+
+ this._primaryIndicator = this._addIndicator();
+ this._vpnIndicator = this._addIndicator();
+
+ this._primaryIndicatorBinding = new GObject.BindingGroup();
+ this._primaryIndicatorBinding.bind('icon-name',
+ this._primaryIndicator, 'icon-name',
+ GObject.BindingFlags.DEFAULT);
+
+ this._vpnToggle.bind_property('checked',
+ this._vpnIndicator, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._vpnToggle.bind_property('icon-name',
+ this._vpnIndicator, 'icon-name',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._getClient().catch(logError);
+ }
+
+ async _getClient() {
+ this._client = await NM.Client.new_async(null);
+
+ this.quickSettingsItems.forEach(
+ toggle => toggle.setClient(this._client));
+
+ this._client.bind_property('nm-running',
+ this, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._client.connectObject(
+ 'notify::primary-connection', () => this._syncMainConnection(),
+ 'notify::activating-connection', () => this._syncMainConnection(),
+ 'notify::connectivity', () => this._syncConnectivity(),
+ this);
+ this._syncMainConnection();
+
+ try {
+ this._configPermission = await Polkit.Permission.new(
+ 'org.freedesktop.NetworkManager.network-control', null, null);
+
+ this.quickSettingsItems.forEach(toggle => {
+ this._configPermission.bind_property('allowed',
+ toggle, 'reactive',
+ GObject.BindingFlags.SYNC_CREATE);
+ });
+ } catch (e) {
+ log(`No permission to control network connections: ${e}`);
+ this._configPermission = null;
+ }
+ }
+
+ _onActivationFailed() {
+ this._notification?.destroy();
+
+ const source = new MessageTray.Source(
+ _('Network Manager'), 'network-error-symbolic');
+ source.policy =
+ new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
+
+ this._notification = new MessageTray.Notification(source,
+ _('Connection failed'),
+ _('Activation of network connection failed'));
+ this._notification.setUrgency(MessageTray.Urgency.HIGH);
+ this._notification.setTransient(true);
+ this._notification.connect('destroy',
+ () => (this._notification = null));
+
+ Main.messageTray.add(source);
+ source.showNotification(this._notification);
+ }
+
+ _syncMainConnection() {
+ this._mainConnection?.disconnectObject(this);
+
+ this._mainConnection =
+ this._client.get_primary_connection() ||
+ this._client.get_activating_connection();
+
+ if (this._mainConnection) {
+ this._mainConnection.connectObject('notify::state',
+ this._mainConnectionStateChanged.bind(this), this);
+ this._mainConnectionStateChanged();
+ }
+
+ this._updateIcon();
+ this._syncConnectivity();
+ }
+
+ _mainConnectionStateChanged() {
+ if (this._mainConnection.state === NM.ActiveConnectionState.ACTIVATED)
+ this._notification?.destroy();
+ }
+
+ _flushConnectivityQueue() {
+ for (let item of this._connectivityQueue)
+ this._portalHelperProxy?.CloseAsync(item);
+ this._connectivityQueue.clear();
+ }
+
+ _closeConnectivityCheck(path) {
+ if (this._connectivityQueue.delete(path))
+ this._portalHelperProxy?.CloseAsync(path);
+ }
+
+ async _portalHelperDone(parameters) {
+ let [path, result] = parameters;
+
+ if (result == PortalHelperResult.CANCELLED) {
+ // Keep the connection in the queue, so the user is not
+ // spammed with more logins until we next flush the queue,
+ // which will happen once they choose a better connection
+ // or we get to full connectivity through other means
+ } else if (result == PortalHelperResult.COMPLETED) {
+ this._closeConnectivityCheck(path);
+ } else if (result == PortalHelperResult.RECHECK) {
+ try {
+ const state = await this._client.check_connectivity_async(null);
+ if (state >= NM.ConnectivityState.FULL)
+ this._closeConnectivityCheck(path);
+ } catch (e) { }
+ } else {
+ log(`Invalid result from portal helper: ${result}`);
+ }
+ }
+
+ async _syncConnectivity() {
+ if (this._mainConnection == null ||
+ this._mainConnection.state != NM.ActiveConnectionState.ACTIVATED) {
+ this._flushConnectivityQueue();
+ return;
+ }
+
+ let isPortal = this._client.connectivity == NM.ConnectivityState.PORTAL;
+ // For testing, allow interpreting any value != FULL as PORTAL, because
+ // LIMITED (no upstream route after the default gateway) is easy to obtain
+ // with a tethered phone
+ // NONE is also possible, with a connection configured to force no default route
+ // (but in general we should only prompt a portal if we know there is a portal)
+ if (GLib.getenv('GNOME_SHELL_CONNECTIVITY_TEST') != null)
+ isPortal ||= this._client.connectivity < NM.ConnectivityState.FULL;
+ if (!isPortal || Main.sessionMode.isGreeter)
+ return;
+
+ let path = this._mainConnection.get_path();
+ if (this._connectivityQueue.has(path))
+ return;
+
+ let timestamp = global.get_current_time();
+ if (!this._portalHelperProxy) {
+ this._portalHelperProxy = new Gio.DBusProxy({
+ g_connection: Gio.DBus.session,
+ g_name: 'org.gnome.Shell.PortalHelper',
+ g_object_path: '/org/gnome/Shell/PortalHelper',
+ g_interface_name: PortalHelperInfo.name,
+ g_interface_info: PortalHelperInfo,
+ });
+ this._portalHelperProxy.connectSignal('Done',
+ (proxy, emitter, params) => {
+ this._portalHelperDone(params).catch(logError);
+ });
+
+ try {
+ await this._portalHelperProxy.init_async(
+ GLib.PRIORITY_DEFAULT, null);
+ } catch (e) {
+ console.error(`Error launching the portal helper: ${e.message}`);
+ }
+ }
+
+ this._portalHelperProxy?.AuthenticateAsync(path, this._client.connectivity_check_uri, timestamp).catch(logError);
+
+ this._connectivityQueue.add(path);
+ }
+
+ _updateIcon() {
+ const [dev] = this._mainConnection?.get_devices() ?? [];
+ const primaryToggle = this._deviceToggles.get(dev?.device_type) ?? null;
+ this._primaryIndicatorBinding.source = primaryToggle;
+
+ if (!primaryToggle) {
+ if (this._client.connectivity === NM.ConnectivityState.FULL)
+ this._primaryIndicator.icon_name = 'network-wired-symbolic';
+ else
+ this._primaryIndicator.icon_name = 'network-wired-no-route-symbolic';
+ }
+
+ const state = this._client.get_state();
+ const connected = state === NM.State.CONNECTED_GLOBAL;
+ this._primaryIndicator.visible = (primaryToggle != null) || connected;
+ }
+});
diff --git a/js/ui/status/nightLight.js b/js/ui/status/nightLight.js
new file mode 100644
index 0000000..0d148e3
--- /dev/null
+++ b/js/ui/status/nightLight.js
@@ -0,0 +1,70 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const {Gio, GLib, GObject} = imports.gi;
+
+const {QuickToggle, SystemIndicator} = imports.ui.quickSettings;
+
+const {loadInterfaceXML} = imports.misc.fileUtils;
+
+const BUS_NAME = 'org.gnome.SettingsDaemon.Color';
+const OBJECT_PATH = '/org/gnome/SettingsDaemon/Color';
+
+const ColorInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Color');
+const colorInfo = Gio.DBusInterfaceInfo.new_for_xml(ColorInterface);
+
+const NightLightToggle = GObject.registerClass(
+class NightLightToggle extends QuickToggle {
+ _init() {
+ super._init({
+ label: _('Night Light'),
+ iconName: 'night-light-symbolic',
+ toggleMode: true,
+ });
+
+ const monitorManager = global.backend.get_monitor_manager();
+ monitorManager.bind_property('night-light-supported',
+ this, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.settings-daemon.plugins.color',
+ });
+ this._settings.bind('night-light-enabled',
+ this, 'checked',
+ Gio.SettingsBindFlags.DEFAULT);
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'night-light-symbolic';
+
+ this.quickSettingsItems.push(new NightLightToggle());
+
+ this._proxy = new Gio.DBusProxy({
+ g_connection: Gio.DBus.session,
+ g_name: BUS_NAME,
+ g_object_path: OBJECT_PATH,
+ g_interface_name: colorInfo.name,
+ g_interface_info: colorInfo,
+ });
+ this._proxy.connect('g-properties-changed', (p, properties) => {
+ const nightLightActiveChanged = !!properties.lookup_value('NightLightActive', null);
+ if (nightLightActiveChanged)
+ this._sync();
+ });
+ this._proxy.init_async(GLib.PRIORITY_DEFAULT, null)
+ .catch(e => console.error(e.message));
+
+ this._sync();
+ }
+
+ _sync() {
+ this._indicator.visible = this._proxy.NightLightActive;
+ }
+});
diff --git a/js/ui/status/powerProfiles.js b/js/ui/status/powerProfiles.js
new file mode 100644
index 0000000..e15208d
--- /dev/null
+++ b/js/ui/status/powerProfiles.js
@@ -0,0 +1,126 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const {Gio, GObject} = imports.gi;
+
+const {QuickMenuToggle, SystemIndicator} = imports.ui.quickSettings;
+
+const PopupMenu = imports.ui.popupMenu;
+
+const {loadInterfaceXML} = imports.misc.fileUtils;
+
+const BUS_NAME = 'net.hadess.PowerProfiles';
+const OBJECT_PATH = '/net/hadess/PowerProfiles';
+
+const PowerProfilesIface = loadInterfaceXML('net.hadess.PowerProfiles');
+const PowerProfilesProxy = Gio.DBusProxy.makeProxyWrapper(PowerProfilesIface);
+
+const PROFILE_PARAMS = {
+ 'performance': {
+ label: C_('Power profile', 'Performance'),
+ iconName: 'power-profile-performance-symbolic',
+ },
+
+ 'balanced': {
+ label: C_('Power profile', 'Balanced'),
+ iconName: 'power-profile-balanced-symbolic',
+ },
+
+ 'power-saver': {
+ label: C_('Power profile', 'Power Saver'),
+ iconName: 'power-profile-power-saver-symbolic',
+ },
+};
+
+const LAST_PROFILE_KEY = 'last-selected-power-profile';
+
+const PowerProfilesToggle = GObject.registerClass(
+class PowerProfilesToggle extends QuickMenuToggle {
+ _init() {
+ super._init();
+
+ this._profileItems = new Map();
+
+ this.connect('clicked', () => {
+ this._proxy.ActiveProfile = this.checked
+ ? 'balanced'
+ : global.settings.get_string(LAST_PROFILE_KEY);
+ });
+
+ this._proxy = new PowerProfilesProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
+ (proxy, error) => {
+ if (error) {
+ log(error.message);
+ } else {
+ this._proxy.connect('g-properties-changed', (p, properties) => {
+ const profilesChanged = !!properties.lookup_value('Profiles', null);
+ if (profilesChanged)
+ this._syncProfiles();
+ this._sync();
+ });
+
+ if (this._proxy.g_name_owner)
+ this._syncProfiles();
+ }
+ this._sync();
+ });
+
+ this._profileSection = new PopupMenu.PopupMenuSection();
+ this.menu.addMenuItem(this._profileSection);
+ this.menu.setHeader('power-profile-balanced-symbolic', _('Power Profiles'));
+
+ this._sync();
+ }
+
+ _syncProfiles() {
+ this._profileSection.removeAll();
+ this._profileItems.clear();
+
+ const profiles = this._proxy.Profiles
+ .map(p => p.Profile.unpack())
+ .reverse();
+ for (const profile of profiles) {
+ const {label, iconName} = PROFILE_PARAMS[profile];
+ if (!label)
+ continue;
+
+ const item = new PopupMenu.PopupImageMenuItem(label, iconName);
+ item.connect('activate',
+ () => (this._proxy.ActiveProfile = profile));
+ this._profileItems.set(profile, item);
+ this._profileSection.addMenuItem(item);
+ }
+
+ this.menuEnabled = this._profileItems.size > 2;
+ }
+
+ _sync() {
+ this.visible = this._proxy.g_name_owner !== null;
+
+ if (!this.visible)
+ return;
+
+ const {ActiveProfile: activeProfile} = this._proxy;
+
+ for (const [profile, item] of this._profileItems) {
+ item.setOrnament(profile === activeProfile
+ ? PopupMenu.Ornament.CHECK
+ : PopupMenu.Ornament.NONE);
+ }
+
+ this.set(PROFILE_PARAMS[activeProfile]);
+ this.checked = activeProfile !== 'balanced';
+
+ if (this.checked)
+ global.settings.set_string(LAST_PROFILE_KEY, activeProfile);
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this.quickSettingsItems.push(new PowerProfilesToggle());
+ }
+});
diff --git a/js/ui/status/remoteAccess.js b/js/ui/status/remoteAccess.js
new file mode 100644
index 0000000..1ed8793
--- /dev/null
+++ b/js/ui/status/remoteAccess.js
@@ -0,0 +1,230 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported RemoteAccessApplet, ScreenRecordingIndicator, ScreenSharingIndicator */
+
+const { Atk, Clutter, GLib, GObject, Meta, St } = imports.gi;
+
+const Main = imports.ui.main;
+const PanelMenu = imports.ui.panelMenu;
+const {SystemIndicator} = imports.ui.quickSettings;
+
+// Minimum amount of time the shared indicator is visible (in micro seconds)
+const MIN_SHARED_INDICATOR_VISIBLE_TIME_US = 5 * GLib.TIME_SPAN_SECOND;
+
+var RemoteAccessApplet = GObject.registerClass(
+class RemoteAccessApplet extends SystemIndicator {
+ _init() {
+ super._init();
+
+ let controller = global.backend.get_remote_access_controller();
+
+ if (!controller)
+ return;
+
+ this._handles = new Set();
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'media-record-symbolic';
+ this._indicator.add_style_class_name('screencast-indicator');
+
+ controller.connect('new-handle', (o, handle) => {
+ this._onNewHandle(handle);
+ });
+ this._sync();
+ }
+
+ _isRecording() {
+ // Screenshot UI screencasts have their own panel, so don't show this
+ // indicator if there's only a screenshot UI screencast.
+ if (Main.screenshotUI.screencast_in_progress)
+ return this._handles.size > 1;
+
+ return this._handles.size > 0;
+ }
+
+ _sync() {
+ this._indicator.visible = this._isRecording();
+ }
+
+ _onStopped(handle) {
+ this._handles.delete(handle);
+ this._sync();
+ }
+
+ _onNewHandle(handle) {
+ if (!handle.is_recording)
+ return;
+
+ this._handles.add(handle);
+ handle.connect('stopped', this._onStopped.bind(this));
+
+ this._sync();
+ }
+});
+
+var ScreenRecordingIndicator = GObject.registerClass({
+ Signals: { 'menu-set': {} },
+}, class ScreenRecordingIndicator extends PanelMenu.ButtonBox {
+ _init() {
+ super._init({
+ reactive: true,
+ can_focus: true,
+ track_hover: true,
+ accessible_name: _('Stop Screencast'),
+ accessible_role: Atk.Role.PUSH_BUTTON,
+ });
+ this.add_style_class_name('screen-recording-indicator');
+
+ this._box = new St.BoxLayout();
+ this.add_child(this._box);
+
+ this._label = new St.Label({
+ text: '0:00',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._box.add_child(this._label);
+
+ this._icon = new St.Icon({ icon_name: 'stop-symbolic' });
+ this._box.add_child(this._icon);
+
+ this.hide();
+ Main.screenshotUI.connect(
+ 'notify::screencast-in-progress',
+ this._onScreencastInProgressChanged.bind(this));
+ }
+
+ vfunc_event(event) {
+ if (event.type() === Clutter.EventType.TOUCH_BEGIN ||
+ event.type() === Clutter.EventType.BUTTON_PRESS)
+ Main.screenshotUI.stopScreencast();
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _updateLabel() {
+ const minutes = this._secondsPassed / 60;
+ const seconds = this._secondsPassed % 60;
+ this._label.text = '%d:%02d'.format(minutes, seconds);
+ }
+
+ _onScreencastInProgressChanged() {
+ if (Main.screenshotUI.screencast_in_progress) {
+ this.show();
+
+ this._secondsPassed = 0;
+ this._updateLabel();
+
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
+ this._secondsPassed += 1;
+ this._updateLabel();
+ return GLib.SOURCE_CONTINUE;
+ });
+ GLib.Source.set_name_by_id(
+ this._timeoutId, '[gnome-shell] screen recording indicator tick');
+ } else {
+ this.hide();
+
+ GLib.source_remove(this._timeoutId);
+ delete this._timeoutId;
+
+ delete this._secondsPassed;
+ }
+ }
+});
+
+var ScreenSharingIndicator = GObject.registerClass({
+ Signals: {'menu-set': {}},
+}, class ScreenSharingIndicator extends PanelMenu.ButtonBox {
+ _init() {
+ super._init({
+ reactive: true,
+ can_focus: true,
+ track_hover: true,
+ accessible_name: _('Stop Screen Sharing'),
+ accessible_role: Atk.Role.PUSH_BUTTON,
+ });
+ this.add_style_class_name('screen-sharing-indicator');
+
+ this._box = new St.BoxLayout();
+ this.add_child(this._box);
+
+ let icon = new St.Icon({icon_name: 'screen-shared-symbolic'});
+ this._box.add_child(icon);
+
+ icon = new St.Icon({icon_name: 'window-close-symbolic'});
+ this._box.add_child(icon);
+
+ this._controller = global.backend.get_remote_access_controller();
+
+ this._handles = new Set();
+
+ this._controller?.connect('new-handle',
+ (o, handle) => this._onNewHandle(handle));
+
+ this._sync();
+ }
+
+ _onNewHandle(handle) {
+ // We can't possibly know about all types of screen sharing on X11, so
+ // showing these controls on X11 might give a false sense of security.
+ // Thus, only enable these controls when using Wayland, where we are
+ // in control of sharing.
+ if (!Meta.is_wayland_compositor())
+ return;
+
+ if (handle.isRecording)
+ return;
+
+ this._handles.add(handle);
+ handle.connect('stopped', () => {
+ this._handles.delete(handle);
+ this._sync();
+ });
+ this._sync();
+ }
+
+ vfunc_event(event) {
+ if (event.type() === Clutter.EventType.TOUCH_BEGIN ||
+ event.type() === Clutter.EventType.BUTTON_PRESS)
+ this._stopSharing();
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _stopSharing() {
+ for (const handle of this._handles)
+ handle.stop();
+ }
+
+ _hideIndicator() {
+ this.hide();
+ delete this._hideIndicatorId;
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _sync() {
+ if (this._hideIndicatorId) {
+ GLib.source_remove(this._hideIndicatorId);
+ delete this._hideIndicatorId;
+ }
+
+ if (this._handles.size > 0) {
+ if (!this.visible)
+ this._visibleTimeUs = GLib.get_monotonic_time();
+ this.show();
+ } else if (this.visible) {
+ const currentTimeUs = GLib.get_monotonic_time();
+ const timeSinceVisibleUs = currentTimeUs - this._visibleTimeUs;
+
+ if (timeSinceVisibleUs >= MIN_SHARED_INDICATOR_VISIBLE_TIME_US) {
+ this._hideIndicator();
+ } else {
+ const timeUntilHideUs =
+ MIN_SHARED_INDICATOR_VISIBLE_TIME_US - timeSinceVisibleUs;
+ this._hideIndicatorId =
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ timeUntilHideUs / GLib.TIME_SPAN_MILLISECOND,
+ () => this._hideIndicator());
+ }
+ }
+ }
+});
diff --git a/js/ui/status/rfkill.js b/js/ui/status/rfkill.js
new file mode 100644
index 0000000..2e1f98f
--- /dev/null
+++ b/js/ui/status/rfkill.js
@@ -0,0 +1,136 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const {Gio, GLib, GObject} = imports.gi;
+
+const {QuickToggle, SystemIndicator} = imports.ui.quickSettings;
+
+const {loadInterfaceXML} = imports.misc.fileUtils;
+
+const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
+const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
+
+const RfkillManagerInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Rfkill');
+const rfkillManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(RfkillManagerInterface);
+
+const RfkillManager = GObject.registerClass({
+ Properties: {
+ 'airplane-mode': GObject.ParamSpec.boolean(
+ 'airplane-mode', '', '',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'hw-airplane-mode': GObject.ParamSpec.boolean(
+ 'hw-airplane-mode', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ 'show-airplane-mode': GObject.ParamSpec.boolean(
+ 'show-airplane-mode', '', '',
+ GObject.ParamFlags.READABLE,
+ false),
+ },
+}, class RfkillManager extends GObject.Object {
+ constructor() {
+ super();
+
+ this._proxy = new Gio.DBusProxy({
+ g_connection: Gio.DBus.session,
+ g_name: BUS_NAME,
+ g_object_path: OBJECT_PATH,
+ g_interface_name: rfkillManagerInfo.name,
+ g_interface_info: rfkillManagerInfo,
+ });
+ this._proxy.connect('g-properties-changed', this._changed.bind(this));
+ this._proxy.init_async(GLib.PRIORITY_DEFAULT, null)
+ .catch(e => console.error(e.message));
+ }
+
+ /* eslint-disable camelcase */
+ get airplane_mode() {
+ return this._proxy.AirplaneMode;
+ }
+
+ set airplane_mode(v) {
+ this._proxy.AirplaneMode = v;
+ }
+
+ get hw_airplane_mode() {
+ return this._proxy.HardwareAirplaneMode;
+ }
+
+ get show_airplane_mode() {
+ return this._proxy.HasAirplaneMode && this._proxy.ShouldShowAirplaneMode;
+ }
+ /* eslint-enable camelcase */
+
+ _changed(proxy, properties) {
+ for (const prop in properties.deepUnpack()) {
+ switch (prop) {
+ case 'AirplaneMode':
+ this.notify('airplane-mode');
+ break;
+ case 'HardwareAirplaneMode':
+ this.notify('hw-airplane-mode');
+ break;
+ case 'HasAirplaneMode':
+ case 'ShouldShowAirplaneMode':
+ this.notify('show-airplane-mode');
+ break;
+ }
+ }
+ }
+});
+
+var _manager;
+function getRfkillManager() {
+ if (_manager != null)
+ return _manager;
+
+ _manager = new RfkillManager();
+ return _manager;
+}
+
+const RfkillToggle = GObject.registerClass(
+class RfkillToggle extends QuickToggle {
+ _init() {
+ super._init({
+ label: _('Airplane Mode'),
+ iconName: 'airplane-mode-symbolic',
+ });
+
+ this._manager = getRfkillManager();
+ this._manager.bind_property('show-airplane-mode',
+ this, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._manager.bind_property('airplane-mode',
+ this, 'checked',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this.connect('clicked',
+ () => (this._manager.airplaneMode = !this._manager.airplaneMode));
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'airplane-mode-symbolic';
+
+ this._rfkillToggle = new RfkillToggle();
+ this._rfkillToggle.connectObject(
+ 'notify::visible', () => this._sync(),
+ 'notify::checked', () => this._sync(),
+ this);
+ this.quickSettingsItems.push(this._rfkillToggle);
+
+ this._sync();
+ }
+
+ _sync() {
+ // Only show indicator when airplane mode is on
+ const {visible, checked} = this._rfkillToggle;
+ this._indicator.visible = visible && checked;
+ }
+});
diff --git a/js/ui/status/system.js b/js/ui/status/system.js
new file mode 100644
index 0000000..5a2d92c
--- /dev/null
+++ b/js/ui/status/system.js
@@ -0,0 +1,348 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const {Atk, Clutter, Gio, GLib, GObject, Meta, Shell, St, UPowerGlib: UPower} = imports.gi;
+
+const SystemActions = imports.misc.systemActions;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const {PopupAnimation} = imports.ui.boxpointer;
+
+const {QuickSettingsItem, QuickToggle, SystemIndicator} = imports.ui.quickSettings;
+const {loadInterfaceXML} = imports.misc.fileUtils;
+
+const BUS_NAME = 'org.freedesktop.UPower';
+const OBJECT_PATH = '/org/freedesktop/UPower/devices/DisplayDevice';
+
+const DisplayDeviceInterface = loadInterfaceXML('org.freedesktop.UPower.Device');
+const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface);
+
+const SHOW_BATTERY_PERCENTAGE = 'show-battery-percentage';
+
+const PowerToggle = GObject.registerClass({
+ Properties: {
+ 'fallback-icon-name': GObject.ParamSpec.string('fallback-icon-name', '', '',
+ GObject.ParamFlags.READWRITE,
+ ''),
+ },
+}, class PowerToggle extends QuickToggle {
+ _init() {
+ super._init({
+ accessible_role: Atk.Role.PUSH_BUTTON,
+ });
+
+ this.add_style_class_name('power-item');
+
+ this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
+ (proxy, error) => {
+ if (error)
+ console.error(error.message);
+ else
+ this._proxy.connect('g-properties-changed', () => this._sync());
+ this._sync();
+ });
+
+ this.bind_property('fallback-icon-name',
+ this._icon, 'fallback-icon-name',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this.connect('clicked', () => {
+ const app = Shell.AppSystem.get_default().lookup_app('gnome-power-panel.desktop');
+ Main.overview.hide();
+ Main.panel.closeQuickSettings();
+ app.activate();
+ });
+
+ Main.sessionMode.connect('updated', () => this._sessionUpdated());
+ this._sessionUpdated();
+ this._sync();
+ }
+
+ _sessionUpdated() {
+ this.reactive = Main.sessionMode.allowSettings;
+ }
+
+ _sync() {
+ // Do we have batteries or a UPS?
+ this.visible = this._proxy.IsPresent;
+ if (!this.visible)
+ return;
+
+ // The icons
+ let chargingState = this._proxy.State === UPower.DeviceState.CHARGING
+ ? '-charging' : '';
+ let fillLevel = 10 * Math.floor(this._proxy.Percentage / 10);
+ const charged =
+ this._proxy.State === UPower.DeviceState.FULLY_CHARGED ||
+ (this._proxy.State === UPower.DeviceState.CHARGING && fillLevel === 100);
+ const icon = charged
+ ? 'battery-level-100-charged-symbolic'
+ : `battery-level-${fillLevel}${chargingState}-symbolic`;
+
+ // Make sure we fall back to fallback-icon-name and not GThemedIcon's
+ // default fallbacks
+ const gicon = new Gio.ThemedIcon({
+ name: icon,
+ use_default_fallbacks: false,
+ });
+
+ this.set({
+ label: _('%d\u2009%%').format(this._proxy.Percentage),
+ fallback_icon_name: this._proxy.IconName,
+ gicon,
+ });
+ }
+});
+
+const ScreenshotItem = GObject.registerClass(
+class ScreenshotItem extends QuickSettingsItem {
+ _init() {
+ super._init({
+ style_class: 'icon-button',
+ can_focus: true,
+ icon_name: 'camera-photo-symbolic',
+ visible: !Main.sessionMode.isGreeter,
+ accessible_name: _('Take Screenshot'),
+ });
+
+ this.connect('clicked', () => {
+ const topMenu = Main.panel.statusArea.quickSettings.menu;
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ Main.screenshotUI.open().catch(logError);
+ return GLib.SOURCE_REMOVE;
+ });
+ topMenu.close(PopupAnimation.NONE);
+ });
+ }
+});
+
+const SettingsItem = GObject.registerClass(
+class SettingsItem extends QuickSettingsItem {
+ _init() {
+ super._init({
+ style_class: 'icon-button',
+ can_focus: true,
+ child: new St.Icon(),
+ });
+
+ this._settingsApp = Shell.AppSystem.get_default().lookup_app(
+ 'org.gnome.Settings.desktop');
+
+ if (!this._settingsApp)
+ console.warn('Missing required core component Settings, expect trouble…');
+
+ this.child.gicon = this._settingsApp?.get_icon() ?? null;
+ this.accessible_name = this._settingsApp?.get_name() ?? null;
+
+ this.connect('clicked', () => {
+ Main.overview.hide();
+ Main.panel.closeQuickSettings();
+ this._settingsApp.activate();
+ });
+
+ Main.sessionMode.connectObject('updated', () => this._sync(), this);
+ this._sync();
+ }
+
+ _sync() {
+ this.visible =
+ this._settingsApp != null && Main.sessionMode.allowSettings;
+ }
+});
+
+const ShutdownItem = GObject.registerClass(
+class ShutdownItem extends QuickSettingsItem {
+ _init() {
+ super._init({
+ style_class: 'icon-button',
+ hasMenu: true,
+ canFocus: true,
+ icon_name: 'system-shutdown-symbolic',
+ accessible_name: _('Power Off Menu'),
+ });
+
+ this._systemActions = new SystemActions.getDefault();
+ this._items = [];
+
+ this.menu.setHeader('system-shutdown-symbolic', C_('title', 'Power Off'));
+
+ this._addSystemAction(_('Suspend'), 'can-suspend', () => {
+ this._systemActions.activateSuspend();
+ Main.panel.closeQuickSettings();
+ });
+
+ this._addSystemAction(_('Restart…'), 'can-restart', () => {
+ this._systemActions.activateRestart();
+ Main.panel.closeQuickSettings();
+ });
+
+ this._addSystemAction(_('Power Off…'), 'can-power-off', () => {
+ this._systemActions.activatePowerOff();
+ Main.panel.closeQuickSettings();
+ });
+
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._addSystemAction(_('Log Out…'), 'can-logout', () => {
+ this._systemActions.activateLogout();
+ Main.panel.closeQuickSettings();
+ });
+
+ this._addSystemAction(_('Switch User…'), 'can-switch-user', () => {
+ this._systemActions.activateSwitchUser();
+ Main.panel.closeQuickSettings();
+ });
+
+ // Whether shutdown is available or not depends on both lockdown
+ // settings (disable-log-out) and Polkit policy - the latter doesn't
+ // notify, so we update the item each time we become visible or
+ // the lockdown setting changes, which should be close enough.
+ this.connect('notify::mapped', () => {
+ if (!this.mapped)
+ return;
+
+ this._systemActions.forceUpdate();
+ });
+
+ this.connect('clicked', () => this.menu.open());
+ this.connect('popup-menu', () => this.menu.open());
+ }
+
+ _addSystemAction(label, propName, callback) {
+ const item = this.menu.addAction(label, callback);
+ this._items.push(item);
+
+ this._systemActions.bind_property(propName,
+ item, 'visible',
+ GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE);
+ item.connect('notify::visible', () => this._sync());
+ }
+
+ _sync() {
+ this.visible = this._items.some(i => i.visible);
+ }
+});
+
+const LockItem = GObject.registerClass(
+class LockItem extends QuickSettingsItem {
+ _init() {
+ this._systemActions = new SystemActions.getDefault();
+
+ super._init({
+ style_class: 'icon-button',
+ can_focus: true,
+ icon_name: 'system-lock-screen-symbolic',
+ accessible_name: C_('action', 'Lock Screen'),
+ });
+
+ this._systemActions.bind_property('can-lock-screen',
+ this, 'visible',
+ GObject.BindingFlags.DEFAULT |
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this.connect('clicked',
+ () => this._systemActions.activateLockScreen());
+ }
+});
+
+
+const SystemItem = GObject.registerClass(
+class SystemItem extends QuickSettingsItem {
+ _init() {
+ super._init({
+ style_class: 'quick-settings-system-item',
+ reactive: false,
+ });
+
+ this.child = new St.BoxLayout();
+
+ this._powerToggle = new PowerToggle();
+ this.child.add_child(this._powerToggle);
+
+ this._laptopSpacer = new Clutter.Actor({x_expand: true});
+ this._powerToggle.bind_property('visible',
+ this._laptopSpacer, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.child.add_child(this._laptopSpacer);
+
+ const screenshotItem = new ScreenshotItem();
+ this.child.add_child(screenshotItem);
+
+ const settingsItem = new SettingsItem();
+ this.child.add_child(settingsItem);
+
+ this._desktopSpacer = new Clutter.Actor({x_expand: true});
+ this._powerToggle.bind_property('visible',
+ this._desktopSpacer, 'visible',
+ GObject.BindingFlags.INVERT_BOOLEAN |
+ GObject.BindingFlags.SYNC_CREATE);
+ this.child.add_child(this._desktopSpacer);
+
+ const lockItem = new LockItem();
+ this.child.add_child(lockItem);
+
+ const shutdownItem = new ShutdownItem();
+ this.child.add_child(shutdownItem);
+
+ this.menu = shutdownItem.menu;
+ }
+
+ get powerToggle() {
+ return this._powerToggle;
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._desktopSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.interface',
+ });
+ this._desktopSettings.connectObject(
+ `changed::${SHOW_BATTERY_PERCENTAGE}`, () => this._sync(), this);
+
+ this._indicator = this._addIndicator();
+ this._percentageLabel = new St.Label({
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this._percentageLabel);
+ this.add_style_class_name('power-status');
+
+ this._systemItem = new SystemItem();
+
+ const {powerToggle} = this._systemItem;
+
+ powerToggle.bind_property('label',
+ this._percentageLabel, 'text',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ powerToggle.connectObject(
+ 'notify::visible', () => this._sync(),
+ 'notify::gicon', () => this._sync(),
+ 'notify::fallback-icon-name', () => this._sync(),
+ this);
+
+ this.quickSettingsItems.push(this._systemItem);
+
+ this._sync();
+ }
+
+ _sync() {
+ const {powerToggle} = this._systemItem;
+ if (powerToggle.visible) {
+ this._indicator.set({
+ gicon: powerToggle.gicon,
+ fallback_icon_name: powerToggle.fallback_icon_name,
+ });
+ this._percentageLabel.visible =
+ this._desktopSettings.get_boolean(SHOW_BATTERY_PERCENTAGE);
+ } else {
+ // If there's no battery, then we use the power icon.
+ this._indicator.icon_name = 'system-shutdown-symbolic';
+ this._percentageLabel.hide();
+ }
+ }
+});
diff --git a/js/ui/status/thunderbolt.js b/js/ui/status/thunderbolt.js
new file mode 100644
index 0000000..2e1236e
--- /dev/null
+++ b/js/ui/status/thunderbolt.js
@@ -0,0 +1,332 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+// the following is a modified version of bolt/contrib/js/client.js
+
+const { Gio, GLib, GObject, Polkit, Shell } = imports.gi;
+const Signals = imports.misc.signals;
+
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const {SystemIndicator} = imports.ui.quickSettings;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+/* Keep in sync with data/org.freedesktop.bolt.xml */
+
+const BoltClientInterface = loadInterfaceXML('org.freedesktop.bolt1.Manager');
+const BoltDeviceInterface = loadInterfaceXML('org.freedesktop.bolt1.Device');
+
+const BoltDeviceProxy = Gio.DBusProxy.makeProxyWrapper(BoltDeviceInterface);
+
+/* */
+
+var Status = {
+ DISCONNECTED: 'disconnected',
+ CONNECTING: 'connecting',
+ CONNECTED: 'connected',
+ AUTHORIZING: 'authorizing',
+ AUTH_ERROR: 'auth-error',
+ AUTHORIZED: 'authorized',
+};
+
+var Policy = {
+ DEFAULT: 'default',
+ MANUAL: 'manual',
+ AUTO: 'auto',
+};
+
+var AuthCtrl = {
+ NONE: 'none',
+};
+
+var AuthMode = {
+ DISABLED: 'disabled',
+ ENABLED: 'enabled',
+};
+
+const BOLT_DBUS_CLIENT_IFACE = 'org.freedesktop.bolt1.Manager';
+const BOLT_DBUS_NAME = 'org.freedesktop.bolt';
+const BOLT_DBUS_PATH = '/org/freedesktop/bolt';
+
+var Client = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ this._proxy = null;
+ this.probing = false;
+ this._getProxy();
+ }
+
+ async _getProxy() {
+ let nodeInfo = Gio.DBusNodeInfo.new_for_xml(BoltClientInterface);
+ try {
+ this._proxy = await Gio.DBusProxy.new(
+ Gio.DBus.system,
+ Gio.DBusProxyFlags.DO_NOT_AUTO_START,
+ nodeInfo.lookup_interface(BOLT_DBUS_CLIENT_IFACE),
+ BOLT_DBUS_NAME,
+ BOLT_DBUS_PATH,
+ BOLT_DBUS_CLIENT_IFACE,
+ null);
+ } catch (e) {
+ log(`error creating bolt proxy: ${e.message}`);
+ return;
+ }
+ this._proxy.connectObject('g-properties-changed',
+ this._onPropertiesChanged.bind(this), this);
+ this._deviceAddedId = this._proxy.connectSignal('DeviceAdded', this._onDeviceAdded.bind(this));
+
+ this.probing = this._proxy.Probing;
+ if (this.probing)
+ this.emit('probing-changed', this.probing);
+ }
+
+ _onPropertiesChanged(proxy, properties) {
+ const probingChanged = !!properties.lookup_value('Probing', null);
+ if (probingChanged) {
+ this.probing = this._proxy.Probing;
+ this.emit('probing-changed', this.probing);
+ }
+ }
+
+ _onDeviceAdded(proxy, emitter, params) {
+ let [path] = params;
+ let device = new BoltDeviceProxy(Gio.DBus.system,
+ BOLT_DBUS_NAME,
+ path);
+ this.emit('device-added', device);
+ }
+
+ /* public methods */
+ close() {
+ if (!this._proxy)
+ return;
+
+ this._proxy.disconnectSignal(this._deviceAddedId);
+ this._proxy.disconnectObject(this);
+ this._proxy = null;
+ }
+
+ async enrollDevice(id, policy) {
+ try {
+ const [path] = await this._proxy.EnrollDeviceAsync(id, policy, AuthCtrl.NONE);
+ const device = new BoltDeviceProxy(Gio.DBus.system, BOLT_DBUS_NAME, path);
+ return device;
+ } catch (error) {
+ Gio.DBusError.strip_remote_error(error);
+ throw error;
+ }
+ }
+
+ get authMode() {
+ return this._proxy.AuthMode;
+ }
+};
+
+/* helper class to automatically authorize new devices */
+var AuthRobot = class extends Signals.EventEmitter {
+ constructor(client) {
+ super();
+
+ this._client = client;
+
+ this._devicesToEnroll = [];
+ this._enrolling = false;
+
+ this._client.connect('device-added', this._onDeviceAdded.bind(this));
+ }
+
+ close() {
+ this.disconnectAll();
+ this._client = null;
+ }
+
+ /* the "device-added" signal will be emitted by boltd for every
+ * device that is not currently stored in the database. We are
+ * only interested in those devices, because all known devices
+ * will be handled by the user himself */
+ _onDeviceAdded(cli, dev) {
+ if (dev.Status !== Status.CONNECTED)
+ return;
+
+ /* check if authorization is enabled in the daemon. if not
+ * we won't even bother authorizing, because we will only
+ * get an error back. The exact contents of AuthMode might
+ * change in the future, but must contain AuthMode.ENABLED
+ * if it is enabled. */
+ if (!cli.authMode.split('|').includes(AuthMode.ENABLED))
+ return;
+
+ /* check if we should enroll the device */
+ let res = [false];
+ this.emit('enroll-device', dev, res);
+ if (res[0] !== true)
+ return;
+
+ /* ok, we should authorize the device, add it to the back
+ * of the list */
+ this._devicesToEnroll.push(dev);
+ this._enrollDevices();
+ }
+
+ /* The enrollment queue:
+ * - new devices will be added to the end of the array.
+ * - an idle callback will be scheduled that will keep
+ * calling itself as long as there a devices to be
+ * enrolled.
+ */
+ _enrollDevices() {
+ if (this._enrolling)
+ return;
+
+ this._enrolling = true;
+ GLib.idle_add(GLib.PRIORITY_DEFAULT,
+ this._enrollDevicesIdle.bind(this));
+ }
+
+ async _enrollDevicesIdle() {
+ let devices = this._devicesToEnroll;
+
+ let dev = devices.shift();
+ if (dev === undefined)
+ return GLib.SOURCE_REMOVE;
+
+ try {
+ await this._client.enrollDevice(dev.Uid, Policy.DEFAULT);
+
+ /* TODO: scan the list of devices to be authorized for children
+ * of this device and remove them (and their children and
+ * their children and ....) from the device queue
+ */
+ this._enrolling = this._devicesToEnroll.length > 0;
+
+ if (this._enrolling) {
+ GLib.idle_add(GLib.PRIORITY_DEFAULT,
+ this._enrollDevicesIdle.bind(this));
+ }
+ } catch (error) {
+ this.emit('enroll-failed', null, error);
+ }
+ return GLib.SOURCE_REMOVE;
+ }
+};
+
+/* eof client.js */
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'thunderbolt-symbolic';
+
+ this._client = new Client();
+ this._client.connect('probing-changed', this._onProbing.bind(this));
+
+ this._robot = new AuthRobot(this._client);
+
+ this._robot.connect('enroll-device', this._onEnrollDevice.bind(this));
+ this._robot.connect('enroll-failed', this._onEnrollFailed.bind(this));
+
+ Main.sessionMode.connect('updated', this._sync.bind(this));
+ this._sync();
+
+ this._source = null;
+ this._perm = null;
+ this._createPermission();
+ }
+
+ async _createPermission() {
+ try {
+ this._perm = await Polkit.Permission.new('org.freedesktop.bolt.enroll', null, null);
+ } catch (e) {
+ log(`Failed to get PolKit permission: ${e}`);
+ }
+ }
+
+ _onDestroy() {
+ this._robot.close();
+ this._client.close();
+ }
+
+ _ensureSource() {
+ if (!this._source) {
+ this._source = new MessageTray.Source(_("Thunderbolt"),
+ 'thunderbolt-symbolic');
+ this._source.connect('destroy', () => (this._source = null));
+
+ Main.messageTray.add(this._source);
+ }
+
+ return this._source;
+ }
+
+ _notify(title, body) {
+ if (this._notification)
+ this._notification.destroy();
+
+ let source = this._ensureSource();
+
+ this._notification = new MessageTray.Notification(source, title, body);
+ this._notification.setUrgency(MessageTray.Urgency.HIGH);
+ this._notification.connect('destroy', () => {
+ this._notification = null;
+ });
+ this._notification.connect('activated', () => {
+ let app = Shell.AppSystem.get_default().lookup_app('gnome-thunderbolt-panel.desktop');
+ if (app)
+ app.activate();
+ });
+ this._source.showNotification(this._notification);
+ }
+
+ /* Session callbacks */
+ _sync() {
+ let active = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
+ this._indicator.visible = active && this._client.probing;
+ }
+
+ /* Bolt.Client callbacks */
+ _onProbing(cli, probing) {
+ if (probing)
+ this._indicator.icon_name = 'thunderbolt-acquiring-symbolic';
+ else
+ this._indicator.icon_name = 'thunderbolt-symbolic';
+
+ this._sync();
+ }
+
+ /* AuthRobot callbacks */
+ _onEnrollDevice(obj, device, policy) {
+ /* only authorize new devices when in an unlocked user session */
+ let unlocked = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
+ /* and if we have the permission to do so, otherwise we trigger a PolKit dialog */
+ let allowed = this._perm && this._perm.allowed;
+
+ let auth = unlocked && allowed;
+ policy[0] = auth;
+
+ log(`thunderbolt: [${device.Name}] auto enrollment: ${auth ? 'yes' : 'no'} (allowed: ${allowed ? 'yes' : 'no'})`);
+
+ if (auth)
+ return; /* we are done */
+
+ if (!unlocked) {
+ const title = _("Unknown Thunderbolt device");
+ const body = _("New device has been detected while you were away. Please disconnect and reconnect the device to start using it.");
+ this._notify(title, body);
+ } else {
+ const title = _("Unauthorized Thunderbolt device");
+ const body = _("New device has been detected and needs to be authorized by an administrator.");
+ this._notify(title, body);
+ }
+ }
+
+ _onEnrollFailed(obj, device, error) {
+ const title = _("Thunderbolt authorization error");
+ const body = _("Could not authorize the Thunderbolt device: %s").format(error.message);
+ this._notify(title, body);
+ }
+});
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
new file mode 100644
index 0000000..bd49cc3
--- /dev/null
+++ b/js/ui/status/volume.js
@@ -0,0 +1,458 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Indicator */
+
+const {Clutter, Gio, GLib, GObject, Gvc} = imports.gi;
+
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+
+const {QuickSlider, SystemIndicator} = imports.ui.quickSettings;
+
+const ALLOW_AMPLIFIED_VOLUME_KEY = 'allow-volume-above-100-percent';
+
+// Each Gvc.MixerControl is a connection to PulseAudio,
+// so it's better to make it a singleton
+let _mixerControl;
+/**
+ * @returns {Gvc.MixerControl} - the mixer control singleton
+ */
+function getMixerControl() {
+ if (_mixerControl)
+ return _mixerControl;
+
+ _mixerControl = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
+ _mixerControl.open();
+
+ return _mixerControl;
+}
+
+const StreamSlider = GObject.registerClass({
+ Signals: {
+ 'stream-updated': {},
+ },
+}, class StreamSlider extends QuickSlider {
+ _init(control) {
+ super._init();
+
+ this._control = control;
+
+ this._inDrag = false;
+ this._notifyVolumeChangeId = 0;
+
+ this._soundSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.sound',
+ });
+ this._soundSettings.connect(`changed::${ALLOW_AMPLIFIED_VOLUME_KEY}`,
+ () => this._amplifySettingsChanged());
+ this._amplifySettingsChanged();
+
+ this._sliderChangedId = this.slider.connect('notify::value',
+ () => this._sliderChanged());
+ this.slider.connect('drag-begin', () => (this._inDrag = true));
+ this.slider.connect('drag-end', () => {
+ this._inDrag = false;
+ this._notifyVolumeChange();
+ });
+
+ this._deviceItems = new Map();
+
+ this._deviceSection = new PopupMenu.PopupMenuSection();
+ this.menu.addMenuItem(this._deviceSection);
+
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.menu.addSettingsAction(_('Sound Settings'),
+ 'gnome-sound-panel.desktop');
+
+ this._stream = null;
+ this._volumeCancellable = null;
+ this._icons = [];
+
+ this._sync();
+ }
+
+ get stream() {
+ return this._stream;
+ }
+
+ set stream(stream) {
+ this._stream?.disconnectObject(this);
+
+ this._stream = stream;
+
+ if (this._stream) {
+ this._connectStream(this._stream);
+ this._updateVolume();
+ } else {
+ this.emit('stream-updated');
+ }
+
+ this._sync();
+ }
+
+ _connectStream(stream) {
+ stream.connectObject(
+ 'notify::is-muted', this._updateVolume.bind(this),
+ 'notify::volume', this._updateVolume.bind(this), this);
+ }
+
+ _lookupDevice(_id) {
+ throw new GObject.NotImplementedError(
+ `_lookupDevice in ${this.constructor.name}`);
+ }
+
+ _activateDevice(_device) {
+ throw new GObject.NotImplementedError(
+ `_activateDevice in ${this.constructor.name}`);
+ }
+
+ _addDevice(id) {
+ if (this._deviceItems.has(id))
+ return;
+
+ const device = this._lookupDevice(id);
+ if (!device)
+ return;
+
+ const {description, origin} = device;
+ const name = origin
+ ? `${description} – ${origin}`
+ : description;
+ const item = new PopupMenu.PopupImageMenuItem(name, device.get_gicon());
+ item.connect('activate', () => this._activateDevice(device));
+
+ this._deviceSection.addMenuItem(item);
+ this._deviceItems.set(id, item);
+
+ this._sync();
+ }
+
+ _removeDevice(id) {
+ this._deviceItems.get(id)?.destroy();
+ if (this._deviceItems.delete(id))
+ this._sync();
+ }
+
+ _setActiveDevice(activeId) {
+ for (const [id, item] of this._deviceItems) {
+ item.setOrnament(id === activeId
+ ? PopupMenu.Ornament.CHECK
+ : PopupMenu.Ornament.NONE);
+ }
+ }
+
+ _shouldBeVisible() {
+ return this._stream != null;
+ }
+
+ _sync() {
+ this.visible = this._shouldBeVisible();
+ this.menuEnabled = this._deviceItems.size > 1;
+ }
+
+ _sliderChanged() {
+ if (!this._stream)
+ return;
+
+ let value = this.slider.value;
+ let volume = value * this._control.get_vol_max_norm();
+ let prevMuted = this._stream.is_muted;
+ let prevVolume = this._stream.volume;
+ if (volume < 1) {
+ this._stream.volume = 0;
+ if (!prevMuted)
+ this._stream.change_is_muted(true);
+ } else {
+ this._stream.volume = volume;
+ if (prevMuted)
+ this._stream.change_is_muted(false);
+ }
+ this._stream.push_volume();
+
+ let volumeChanged = this._stream.volume !== prevVolume;
+ if (volumeChanged && !this._notifyVolumeChangeId && !this._inDrag) {
+ this._notifyVolumeChangeId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 30, () => {
+ this._notifyVolumeChange();
+ this._notifyVolumeChangeId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._notifyVolumeChangeId,
+ '[gnome-shell] this._notifyVolumeChangeId');
+ }
+ }
+
+ _notifyVolumeChange() {
+ if (this._volumeCancellable)
+ this._volumeCancellable.cancel();
+ this._volumeCancellable = null;
+
+ if (this._stream.state === Gvc.MixerStreamState.RUNNING)
+ return; // feedback not necessary while playing
+
+ this._volumeCancellable = new Gio.Cancellable();
+ let player = global.display.get_sound_player();
+ player.play_from_theme('audio-volume-change',
+ _('Volume changed'), this._volumeCancellable);
+ }
+
+ _changeSlider(value) {
+ this.slider.block_signal_handler(this._sliderChangedId);
+ this.slider.value = value;
+ this.slider.unblock_signal_handler(this._sliderChangedId);
+ }
+
+ _updateVolume() {
+ let muted = this._stream.is_muted;
+ this._changeSlider(muted
+ ? 0 : this._stream.volume / this._control.get_vol_max_norm());
+ this.emit('stream-updated');
+ }
+
+ _amplifySettingsChanged() {
+ this._allowAmplified = this._soundSettings.get_boolean(ALLOW_AMPLIFIED_VOLUME_KEY);
+
+ this.slider.maximum_value = this._allowAmplified
+ ? this.getMaxLevel() : 1;
+
+ if (this._stream)
+ this._updateVolume();
+ }
+
+ getIcon() {
+ if (!this._stream)
+ return null;
+
+ let volume = this._stream.volume;
+ let n;
+ if (this._stream.is_muted || volume <= 0) {
+ n = 0;
+ } else {
+ n = Math.ceil(3 * volume / this._control.get_vol_max_norm());
+ n = Math.clamp(n, 1, this._icons.length - 1);
+ }
+ return this._icons[n];
+ }
+
+ getLevel() {
+ if (!this._stream)
+ return null;
+
+ return this._stream.volume / this._control.get_vol_max_norm();
+ }
+
+ getMaxLevel() {
+ let maxVolume = this._control.get_vol_max_norm();
+ if (this._allowAmplified)
+ maxVolume = this._control.get_vol_max_amplified();
+
+ return maxVolume / this._control.get_vol_max_norm();
+ }
+});
+
+const OutputStreamSlider = GObject.registerClass(
+class OutputStreamSlider extends StreamSlider {
+ _init(control) {
+ super._init(control);
+
+ this.slider.accessible_name = _('Volume');
+
+ this._control.connectObject(
+ 'output-added', (c, id) => this._addDevice(id),
+ 'output-removed', (c, id) => this._removeDevice(id),
+ 'active-output-update', (c, id) => this._setActiveDevice(id),
+ this);
+
+ this._icons = [
+ 'audio-volume-muted-symbolic',
+ 'audio-volume-low-symbolic',
+ 'audio-volume-medium-symbolic',
+ 'audio-volume-high-symbolic',
+ 'audio-volume-overamplified-symbolic',
+ ];
+
+ this.menu.setHeader('audio-headphones-symbolic', _('Sound Output'));
+ }
+
+ _connectStream(stream) {
+ super._connectStream(stream);
+ stream.connectObject('notify::port',
+ this._portChanged.bind(this), this);
+ this._portChanged();
+ }
+
+ _lookupDevice(id) {
+ return this._control.lookup_output_id(id);
+ }
+
+ _activateDevice(device) {
+ this._control.change_output(device);
+ }
+
+ _findHeadphones(sink) {
+ // This only works for external headphones (e.g. bluetooth)
+ if (sink.get_form_factor() == 'headset' ||
+ sink.get_form_factor() == 'headphone')
+ return true;
+
+ // a bit hackish, but ALSA/PulseAudio have a number
+ // of different identifiers for headphones, and I could
+ // not find the complete list
+ if (sink.get_ports().length > 0)
+ return sink.get_port().port.includes('headphone');
+
+ return false;
+ }
+
+ _portChanged() {
+ const hasHeadphones = this._findHeadphones(this._stream);
+ if (hasHeadphones === this._hasHeadphones)
+ return;
+
+ this._hasHeadphones = hasHeadphones;
+ this.iconName = this._hasHeadphones
+ ? 'audio-headphones-symbolic'
+ : 'audio-speakers-symbolic';
+ }
+});
+
+const InputStreamSlider = GObject.registerClass(
+class InputStreamSlider extends StreamSlider {
+ _init(control) {
+ super._init(control);
+
+ this.slider.accessible_name = _('Microphone');
+
+ this._control.connectObject(
+ 'input-added', (c, id) => this._addDevice(id),
+ 'input-removed', (c, id) => this._removeDevice(id),
+ 'active-input-update', (c, id) => this._setActiveDevice(id),
+ 'stream-added', () => this._maybeShowInput(),
+ 'stream-removed', () => this._maybeShowInput(),
+ this);
+
+ this.iconName = 'audio-input-microphone-symbolic';
+ this._icons = [
+ 'microphone-sensitivity-muted-symbolic',
+ 'microphone-sensitivity-low-symbolic',
+ 'microphone-sensitivity-medium-symbolic',
+ 'microphone-sensitivity-high-symbolic',
+ ];
+
+ this.menu.setHeader('audio-input-microphone-symbolic', _('Sound Input'));
+ }
+
+ _connectStream(stream) {
+ super._connectStream(stream);
+ this._maybeShowInput();
+ }
+
+ _lookupDevice(id) {
+ return this._control.lookup_input_id(id);
+ }
+
+ _activateDevice(device) {
+ this._control.change_input(device);
+ }
+
+ _maybeShowInput() {
+ // only show input widgets if any application is recording audio
+ let showInput = false;
+ if (this._stream) {
+ // skip gnome-volume-control and pavucontrol which appear
+ // as recording because they show the input level
+ let skippedApps = [
+ 'org.gnome.VolumeControl',
+ 'org.PulseAudio.pavucontrol',
+ ];
+
+ showInput = this._control.get_source_outputs().some(
+ output => !skippedApps.includes(output.get_application_id()));
+ }
+
+ this._showInput = showInput;
+ this._sync();
+ }
+
+ _shouldBeVisible() {
+ return super._shouldBeVisible() && this._showInput;
+ }
+});
+
+var Indicator = GObject.registerClass(
+class Indicator extends SystemIndicator {
+ _init() {
+ super._init();
+
+ this._primaryIndicator = this._addIndicator();
+ this._inputIndicator = this._addIndicator();
+
+ this._primaryIndicator.reactive = true;
+ this._inputIndicator.reactive = true;
+
+ this._primaryIndicator.connect('scroll-event',
+ (actor, event) => this._handleScrollEvent(this._output, event));
+ this._inputIndicator.connect('scroll-event',
+ (actor, event) => this._handleScrollEvent(this._input, event));
+
+ this._control = getMixerControl();
+ this._control.connectObject(
+ 'state-changed', () => this._onControlStateChanged(),
+ 'default-sink-changed', () => this._readOutput(),
+ 'default-source-changed', () => this._readInput(),
+ this);
+
+ this._output = new OutputStreamSlider(this._control);
+ this._output.connect('stream-updated', () => {
+ const icon = this._output.getIcon();
+
+ if (icon)
+ this._primaryIndicator.icon_name = icon;
+ this._primaryIndicator.visible = icon !== null;
+ });
+
+ this._input = new InputStreamSlider(this._control);
+ this._input.connect('stream-updated', () => {
+ const icon = this._input.getIcon();
+
+ if (icon)
+ this._inputIndicator.icon_name = icon;
+ });
+
+ this._input.bind_property('visible',
+ this._inputIndicator, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this.quickSettingsItems.push(this._output);
+ this.quickSettingsItems.push(this._input);
+
+ this._onControlStateChanged();
+ }
+
+ _onControlStateChanged() {
+ if (this._control.get_state() === Gvc.MixerControlState.READY) {
+ this._readInput();
+ this._readOutput();
+ } else {
+ this._primaryIndicator.hide();
+ }
+ }
+
+ _readOutput() {
+ this._output.stream = this._control.get_default_sink();
+ }
+
+ _readInput() {
+ this._input.stream = this._control.get_default_source();
+ }
+
+ _handleScrollEvent(item, event) {
+ const result = item.slider.scroll(event);
+ if (result === Clutter.EVENT_PROPAGATE || item.mapped)
+ return result;
+
+ const gicon = new Gio.ThemedIcon({name: item.getIcon()});
+ const level = item.getLevel();
+ const maxLevel = item.getMaxLevel();
+ Main.osdWindowManager.show(-1, gicon, null, level, maxLevel);
+ return result;
+ }
+});
diff --git a/js/ui/swipeTracker.js b/js/ui/swipeTracker.js
new file mode 100644
index 0000000..869f977
--- /dev/null
+++ b/js/ui/swipeTracker.js
@@ -0,0 +1,787 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SwipeTracker */
+
+const { Clutter, Gio, GObject, Meta } = imports.gi;
+
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+// FIXME: ideally these values matches physical touchpad size. We can get the
+// correct values for gnome-shell specifically, since mutter uses libinput
+// directly, but GTK apps cannot get it, so use an arbitrary value so that
+// it's consistent with apps.
+const TOUCHPAD_BASE_HEIGHT = 300;
+const TOUCHPAD_BASE_WIDTH = 400;
+
+const EVENT_HISTORY_THRESHOLD_MS = 150;
+
+const SCROLL_MULTIPLIER = 10;
+
+const MIN_ANIMATION_DURATION = 100;
+const MAX_ANIMATION_DURATION = 400;
+const VELOCITY_THRESHOLD_TOUCH = 0.3;
+const VELOCITY_THRESHOLD_TOUCHPAD = 0.6;
+const DECELERATION_TOUCH = 0.998;
+const DECELERATION_TOUCHPAD = 0.997;
+const VELOCITY_CURVE_THRESHOLD = 2;
+const DECELERATION_PARABOLA_MULTIPLIER = 0.35;
+const DRAG_THRESHOLD_DISTANCE = 16;
+
+// Derivative of easeOutCubic at t=0
+const DURATION_MULTIPLIER = 3;
+const ANIMATION_BASE_VELOCITY = 0.002;
+const EPSILON = 0.005;
+
+const GESTURE_FINGER_COUNT = 3;
+
+const State = {
+ NONE: 0,
+ SCROLLING: 1,
+};
+
+const TouchpadState = {
+ NONE: 0,
+ PENDING: 1,
+ HANDLING: 2,
+ IGNORED: 3,
+};
+
+const EventHistory = class {
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ this._data = [];
+ }
+
+ trim(time) {
+ const thresholdTime = time - EVENT_HISTORY_THRESHOLD_MS;
+ const index = this._data.findIndex(r => r.time >= thresholdTime);
+
+ this._data.splice(0, index);
+ }
+
+ append(time, delta) {
+ this.trim(time);
+
+ this._data.push({ time, delta });
+ }
+
+ calculateVelocity() {
+ if (this._data.length < 2)
+ return 0;
+
+ const firstTime = this._data[0].time;
+ const lastTime = this._data[this._data.length - 1].time;
+
+ if (firstTime === lastTime)
+ return 0;
+
+ const totalDelta = this._data.slice(1).map(a => a.delta).reduce((a, b) => a + b);
+ const period = lastTime - firstTime;
+
+ return totalDelta / period;
+ }
+};
+
+const TouchpadSwipeGesture = GObject.registerClass({
+ Properties: {
+ 'enabled': GObject.ParamSpec.boolean(
+ 'enabled', 'enabled', 'enabled',
+ GObject.ParamFlags.READWRITE,
+ true),
+ 'orientation': GObject.ParamSpec.enum(
+ 'orientation', 'orientation', 'orientation',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Orientation, Clutter.Orientation.HORIZONTAL),
+ },
+ Signals: {
+ 'begin': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
+ 'update': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
+ 'end': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE] },
+ },
+}, class TouchpadSwipeGesture extends GObject.Object {
+ _init(allowedModes) {
+ super._init();
+ this._allowedModes = allowedModes;
+ this._state = TouchpadState.NONE;
+ this._cumulativeX = 0;
+ this._cumulativeY = 0;
+ this._touchpadSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.peripherals.touchpad',
+ });
+
+ global.stage.connectObject(
+ 'captured-event::touchpad', this._handleEvent.bind(this), this);
+ }
+
+ _handleEvent(actor, event) {
+ if (event.type() !== Clutter.EventType.TOUCHPAD_SWIPE)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (event.get_gesture_phase() === Clutter.TouchpadGesturePhase.BEGIN)
+ this._state = TouchpadState.NONE;
+
+ if (event.get_touchpad_gesture_finger_count() !== GESTURE_FINGER_COUNT)
+ return Clutter.EVENT_PROPAGATE;
+
+ if ((this._allowedModes & Main.actionMode) === 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (!this.enabled)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._state === TouchpadState.IGNORED)
+ return Clutter.EVENT_PROPAGATE;
+
+ let time = event.get_time();
+
+ const [x, y] = event.get_coords();
+ const [dx, dy] = event.get_gesture_motion_delta_unaccelerated();
+
+ if (this._state === TouchpadState.NONE) {
+ if (dx === 0 && dy === 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ this._cumulativeX = 0;
+ this._cumulativeY = 0;
+ this._state = TouchpadState.PENDING;
+ }
+
+ if (this._state === TouchpadState.PENDING) {
+ this._cumulativeX += dx;
+ this._cumulativeY += dy;
+
+ const cdx = this._cumulativeX;
+ const cdy = this._cumulativeY;
+ const distance = Math.sqrt(cdx * cdx + cdy * cdy);
+
+ if (distance >= DRAG_THRESHOLD_DISTANCE) {
+ const gestureOrientation = Math.abs(cdx) > Math.abs(cdy)
+ ? Clutter.Orientation.HORIZONTAL
+ : Clutter.Orientation.VERTICAL;
+
+ this._cumulativeX = 0;
+ this._cumulativeY = 0;
+
+ if (gestureOrientation === this.orientation) {
+ this._state = TouchpadState.HANDLING;
+ this.emit('begin', time, x, y);
+ } else {
+ this._state = TouchpadState.IGNORED;
+ return Clutter.EVENT_PROPAGATE;
+ }
+ } else {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ }
+
+ const vertical = this.orientation === Clutter.Orientation.VERTICAL;
+ let delta = vertical ? dy : dx;
+ const distance = vertical ? TOUCHPAD_BASE_HEIGHT : TOUCHPAD_BASE_WIDTH;
+
+ switch (event.get_gesture_phase()) {
+ case Clutter.TouchpadGesturePhase.BEGIN:
+ case Clutter.TouchpadGesturePhase.UPDATE:
+ if (this._touchpadSettings.get_boolean('natural-scroll'))
+ delta = -delta;
+
+ this.emit('update', time, delta, distance);
+ break;
+
+ case Clutter.TouchpadGesturePhase.END:
+ case Clutter.TouchpadGesturePhase.CANCEL:
+ this.emit('end', time, distance);
+ this._state = TouchpadState.NONE;
+ break;
+ }
+
+ return this._state === TouchpadState.HANDLING
+ ? Clutter.EVENT_STOP
+ : Clutter.EVENT_PROPAGATE;
+ }
+
+ destroy() {
+ global.stage.disconnectObject(this);
+ }
+});
+
+const TouchSwipeGesture = GObject.registerClass({
+ Properties: {
+ 'distance': GObject.ParamSpec.double(
+ 'distance', 'distance', 'distance',
+ GObject.ParamFlags.READWRITE,
+ 0, Infinity, 0),
+ 'orientation': GObject.ParamSpec.enum(
+ 'orientation', 'orientation', 'orientation',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Orientation, Clutter.Orientation.HORIZONTAL),
+ },
+ Signals: {
+ 'begin': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
+ 'update': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
+ 'end': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE] },
+ 'cancel': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE] },
+ },
+}, class TouchSwipeGesture extends Clutter.GestureAction {
+ _init(allowedModes, nTouchPoints, thresholdTriggerEdge) {
+ super._init();
+ this.set_n_touch_points(nTouchPoints);
+ this.set_threshold_trigger_edge(thresholdTriggerEdge);
+
+ this._allowedModes = allowedModes;
+ this._distance = global.screen_height;
+
+ global.display.connect('grab-op-begin', () => {
+ this.cancel();
+ });
+
+ this._lastPosition = 0;
+ }
+
+ get distance() {
+ return this._distance;
+ }
+
+ set distance(distance) {
+ if (this._distance === distance)
+ return;
+
+ this._distance = distance;
+ this.notify('distance');
+ }
+
+ vfunc_gesture_prepare(actor) {
+ if (!super.vfunc_gesture_prepare(actor))
+ return false;
+
+ if ((this._allowedModes & Main.actionMode) === 0)
+ return false;
+
+ let time = this.get_last_event(0).get_time();
+ let [xPress, yPress] = this.get_press_coords(0);
+ let [x, y] = this.get_motion_coords(0);
+ const [xDelta, yDelta] = [x - xPress, y - yPress];
+ const swipeOrientation = Math.abs(xDelta) > Math.abs(yDelta)
+ ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL;
+
+ if (swipeOrientation !== this.orientation)
+ return false;
+
+ this._lastPosition =
+ this.orientation === Clutter.Orientation.VERTICAL ? y : x;
+
+ this.emit('begin', time, xPress, yPress);
+ return true;
+ }
+
+ vfunc_gesture_progress(_actor) {
+ let [x, y] = this.get_motion_coords(0);
+ let pos = this.orientation === Clutter.Orientation.VERTICAL ? y : x;
+
+ let delta = pos - this._lastPosition;
+ this._lastPosition = pos;
+
+ let time = this.get_last_event(0).get_time();
+
+ this.emit('update', time, -delta, this._distance);
+
+ return true;
+ }
+
+ vfunc_gesture_end(_actor) {
+ let time = this.get_last_event(0).get_time();
+
+ this.emit('end', time, this._distance);
+ }
+
+ vfunc_gesture_cancel(_actor) {
+ let time = Clutter.get_current_event_time();
+
+ this.emit('cancel', time, this._distance);
+ }
+});
+
+const ScrollGesture = GObject.registerClass({
+ Properties: {
+ 'enabled': GObject.ParamSpec.boolean(
+ 'enabled', 'enabled', 'enabled',
+ GObject.ParamFlags.READWRITE,
+ true),
+ 'orientation': GObject.ParamSpec.enum(
+ 'orientation', 'orientation', 'orientation',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Orientation, Clutter.Orientation.HORIZONTAL),
+ 'scroll-modifiers': GObject.ParamSpec.flags(
+ 'scroll-modifiers', 'scroll-modifiers', 'scroll-modifiers',
+ GObject.ParamFlags.READWRITE,
+ Clutter.ModifierType, 0),
+ },
+ Signals: {
+ 'begin': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
+ 'update': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
+ 'end': { param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE] },
+ },
+}, class ScrollGesture extends GObject.Object {
+ _init(actor, allowedModes) {
+ super._init();
+ this._allowedModes = allowedModes;
+ this._began = false;
+ this._enabled = true;
+
+ actor.connect('scroll-event', this._handleEvent.bind(this));
+ }
+
+ get enabled() {
+ return this._enabled;
+ }
+
+ set enabled(enabled) {
+ if (this._enabled === enabled)
+ return;
+
+ this._enabled = enabled;
+ this._began = false;
+
+ this.notify('enabled');
+ }
+
+ canHandleEvent(event) {
+ if (event.type() !== Clutter.EventType.SCROLL)
+ return false;
+
+ if (event.get_scroll_source() !== Clutter.ScrollSource.FINGER &&
+ event.get_source_device().get_device_type() !== Clutter.InputDeviceType.TOUCHPAD_DEVICE)
+ return false;
+
+ if (!this.enabled)
+ return false;
+
+ if ((this._allowedModes & Main.actionMode) === 0)
+ return false;
+
+ if (!this._began && this.scrollModifiers !== 0 &&
+ (event.get_state() & this.scrollModifiers) === 0)
+ return false;
+
+ return true;
+ }
+
+ _handleEvent(actor, event) {
+ if (!this.canHandleEvent(event))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (event.get_scroll_direction() !== Clutter.ScrollDirection.SMOOTH)
+ return Clutter.EVENT_PROPAGATE;
+
+ const vertical = this.orientation === Clutter.Orientation.VERTICAL;
+ const distance = vertical ? TOUCHPAD_BASE_HEIGHT : TOUCHPAD_BASE_WIDTH;
+
+ let time = event.get_time();
+ let [dx, dy] = event.get_scroll_delta();
+ if (dx === 0 && dy === 0) {
+ this.emit('end', time, distance);
+ this._began = false;
+ return Clutter.EVENT_STOP;
+ }
+
+ if (!this._began) {
+ let [x, y] = event.get_coords();
+ this.emit('begin', time, x, y);
+ this._began = true;
+ }
+
+ const delta = (vertical ? dy : dx) * SCROLL_MULTIPLIER;
+
+ this.emit('update', time, delta, distance);
+
+ return Clutter.EVENT_STOP;
+ }
+});
+
+// USAGE:
+//
+// To correctly implement the gesture, there must be handlers for the following
+// signals:
+//
+// begin(tracker, monitor)
+// The handler should check whether a deceleration animation is currently
+// running. If it is, it should stop the animation (without resetting
+// progress). Then it should call:
+// tracker.confirmSwipe(distance, snapPoints, currentProgress, cancelProgress)
+// If it's not called, the swipe would be ignored.
+// The parameters are:
+// * distance: the page size;
+// * snapPoints: an (sorted with ascending order) array of snap points;
+// * currentProgress: the current progress;
+// * cancelprogress: a non-transient value that would be used if the gesture
+// is cancelled.
+// If no animation was running, currentProgress and cancelProgress should be
+// same. The handler may set 'orientation' property here.
+//
+// update(tracker, progress)
+// The handler should set the progress to the given value.
+//
+// end(tracker, duration, endProgress)
+// The handler should animate the progress to endProgress. If endProgress is
+// 0, it should do nothing after the animation, otherwise it should change the
+// state, e.g. change the current page or switch workspace.
+// NOTE: duration can be 0 in some cases, in this case it should finish
+// instantly.
+
+/** A class for handling swipe gestures */
+var SwipeTracker = GObject.registerClass({
+ Properties: {
+ 'enabled': GObject.ParamSpec.boolean(
+ 'enabled', 'enabled', 'enabled',
+ GObject.ParamFlags.READWRITE,
+ true),
+ 'orientation': GObject.ParamSpec.enum(
+ 'orientation', 'orientation', 'orientation',
+ GObject.ParamFlags.READWRITE,
+ Clutter.Orientation, Clutter.Orientation.HORIZONTAL),
+ 'distance': GObject.ParamSpec.double(
+ 'distance', 'distance', 'distance',
+ GObject.ParamFlags.READWRITE,
+ 0, Infinity, 0),
+ 'allow-long-swipes': GObject.ParamSpec.boolean(
+ 'allow-long-swipes', 'allow-long-swipes', 'allow-long-swipes',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'scroll-modifiers': GObject.ParamSpec.flags(
+ 'scroll-modifiers', 'scroll-modifiers', 'scroll-modifiers',
+ GObject.ParamFlags.READWRITE,
+ Clutter.ModifierType, 0),
+ },
+ Signals: {
+ 'begin': { param_types: [GObject.TYPE_UINT] },
+ 'update': { param_types: [GObject.TYPE_DOUBLE] },
+ 'end': { param_types: [GObject.TYPE_UINT64, GObject.TYPE_DOUBLE] },
+ },
+}, class SwipeTracker extends GObject.Object {
+ _init(actor, orientation, allowedModes, params) {
+ super._init();
+ params = Params.parse(params, { allowDrag: true, allowScroll: true });
+
+ this.orientation = orientation;
+ this._allowedModes = allowedModes;
+ this._enabled = true;
+ this._distance = global.screen_height;
+ this._history = new EventHistory();
+ this._reset();
+
+ this._touchpadGesture = new TouchpadSwipeGesture(allowedModes);
+ this._touchpadGesture.connect('begin', this._beginGesture.bind(this));
+ this._touchpadGesture.connect('update', this._updateGesture.bind(this));
+ this._touchpadGesture.connect('end', this._endTouchpadGesture.bind(this));
+ this.bind_property('enabled', this._touchpadGesture, 'enabled', 0);
+ this.bind_property('orientation', this._touchpadGesture, 'orientation',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._touchGesture = new TouchSwipeGesture(allowedModes,
+ GESTURE_FINGER_COUNT,
+ Clutter.GestureTriggerEdge.AFTER);
+ this._touchGesture.connect('begin', this._beginTouchSwipe.bind(this));
+ this._touchGesture.connect('update', this._updateGesture.bind(this));
+ this._touchGesture.connect('end', this._endTouchGesture.bind(this));
+ this._touchGesture.connect('cancel', this._cancelTouchGesture.bind(this));
+ this.bind_property('enabled', this._touchGesture, 'enabled', 0);
+ this.bind_property('orientation', this._touchGesture, 'orientation',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('distance', this._touchGesture, 'distance', 0);
+ global.stage.add_action_full('swipe', Clutter.EventPhase.CAPTURE, this._touchGesture);
+
+ if (params.allowDrag) {
+ this._dragGesture = new TouchSwipeGesture(allowedModes, 1,
+ Clutter.GestureTriggerEdge.AFTER);
+ this._dragGesture.connect('begin', this._beginGesture.bind(this));
+ this._dragGesture.connect('update', this._updateGesture.bind(this));
+ this._dragGesture.connect('end', this._endTouchGesture.bind(this));
+ this._dragGesture.connect('cancel', this._cancelTouchGesture.bind(this));
+ this.bind_property('enabled', this._dragGesture, 'enabled', 0);
+ this.bind_property('orientation', this._dragGesture, 'orientation',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('distance', this._dragGesture, 'distance', 0);
+ actor.add_action_full('drag', Clutter.EventPhase.CAPTURE, this._dragGesture);
+ } else {
+ this._dragGesture = null;
+ }
+
+ if (params.allowScroll) {
+ this._scrollGesture = new ScrollGesture(actor, allowedModes);
+ this._scrollGesture.connect('begin', this._beginGesture.bind(this));
+ this._scrollGesture.connect('update', this._updateGesture.bind(this));
+ this._scrollGesture.connect('end', this._endTouchpadGesture.bind(this));
+ this.bind_property('enabled', this._scrollGesture, 'enabled', 0);
+ this.bind_property('orientation', this._scrollGesture, 'orientation',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('scroll-modifiers',
+ this._scrollGesture, 'scroll-modifiers', 0);
+ } else {
+ this._scrollGesture = null;
+ }
+ }
+
+ /**
+ * canHandleScrollEvent:
+ * @param {Clutter.Event} scrollEvent: an event to check
+ * @returns {bool} whether the event can be handled by the tracker
+ *
+ * This function can be used to combine swipe gesture and mouse
+ * scrolling.
+ */
+ canHandleScrollEvent(scrollEvent) {
+ if (!this.enabled || this._scrollGesture === null)
+ return false;
+
+ return this._scrollGesture.canHandleEvent(scrollEvent);
+ }
+
+ get enabled() {
+ return this._enabled;
+ }
+
+ set enabled(enabled) {
+ if (this._enabled === enabled)
+ return;
+
+ this._enabled = enabled;
+ if (!enabled && this._state === State.SCROLLING)
+ this._interrupt();
+ this.notify('enabled');
+ }
+
+ get distance() {
+ return this._distance;
+ }
+
+ set distance(distance) {
+ if (this._distance === distance)
+ return;
+
+ this._distance = distance;
+ this.notify('distance');
+ }
+
+ _reset() {
+ this._state = State.NONE;
+
+ this._snapPoints = [];
+ this._initialProgress = 0;
+ this._cancelProgress = 0;
+
+ this._prevOffset = 0;
+ this._progress = 0;
+
+ this._cancelled = false;
+
+ this._history.reset();
+ }
+
+ _interrupt() {
+ this.emit('end', 0, this._cancelProgress);
+ this._reset();
+ }
+
+ _beginTouchSwipe(gesture, time, x, y) {
+ if (this._dragGesture)
+ this._dragGesture.cancel();
+
+ this._beginGesture(gesture, time, x, y);
+ }
+
+ _beginGesture(gesture, time, x, y) {
+ if (this._state === State.SCROLLING)
+ return;
+
+ this._history.append(time, 0);
+
+ let rect = new Meta.Rectangle({ x, y, width: 1, height: 1 });
+ let monitor = global.display.get_monitor_index_for_rect(rect);
+
+ this.emit('begin', monitor);
+ }
+
+ _findClosestPoint(pos) {
+ const distances = this._snapPoints.map(x => Math.abs(x - pos));
+ const min = Math.min(...distances);
+ return distances.indexOf(min);
+ }
+
+ _findNextPoint(pos) {
+ return this._snapPoints.findIndex(p => p >= pos);
+ }
+
+ _findPreviousPoint(pos) {
+ const reversedIndex = this._snapPoints.slice().reverse().findIndex(p => p <= pos);
+ return this._snapPoints.length - 1 - reversedIndex;
+ }
+
+ _findPointForProjection(pos, velocity) {
+ const initial = this._findClosestPoint(this._initialProgress);
+ const prev = this._findPreviousPoint(pos);
+ const next = this._findNextPoint(pos);
+
+ if ((velocity > 0 ? prev : next) === initial)
+ return velocity > 0 ? next : prev;
+
+ return this._findClosestPoint(pos);
+ }
+
+ _getBounds(pos) {
+ if (this.allowLongSwipes)
+ return [this._snapPoints[0], this._snapPoints[this._snapPoints.length - 1]];
+
+ const closest = this._findClosestPoint(pos);
+
+ let prev, next;
+ if (Math.abs(this._snapPoints[closest] - pos) < EPSILON) {
+ prev = next = closest;
+ } else {
+ prev = this._findPreviousPoint(pos);
+ next = this._findNextPoint(pos);
+ }
+
+ const lowerIndex = Math.max(prev - 1, 0);
+ const upperIndex = Math.min(next + 1, this._snapPoints.length - 1);
+
+ return [this._snapPoints[lowerIndex], this._snapPoints[upperIndex]];
+ }
+
+ _updateGesture(gesture, time, delta, distance) {
+ if (this._state !== State.SCROLLING)
+ return;
+
+ if ((this._allowedModes & Main.actionMode) === 0 || !this.enabled) {
+ this._interrupt();
+ return;
+ }
+
+ if (this.orientation === Clutter.Orientation.HORIZONTAL &&
+ Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ delta = -delta;
+
+ this._progress += delta / distance;
+ this._history.append(time, delta);
+
+ this._progress = Math.clamp(this._progress, ...this._getBounds(this._initialProgress));
+
+ this.emit('update', this._progress);
+ }
+
+ _getEndProgress(velocity, distance, isTouchpad) {
+ if (this._cancelled)
+ return this._cancelProgress;
+
+ const threshold = isTouchpad ? VELOCITY_THRESHOLD_TOUCHPAD : VELOCITY_THRESHOLD_TOUCH;
+
+ if (Math.abs(velocity) < threshold)
+ return this._snapPoints[this._findClosestPoint(this._progress)];
+
+ const decel = isTouchpad ? DECELERATION_TOUCHPAD : DECELERATION_TOUCH;
+ const slope = decel / (1.0 - decel) / 1000.0;
+
+ let pos;
+ if (Math.abs(velocity) > VELOCITY_CURVE_THRESHOLD) {
+ const c = slope / 2 / DECELERATION_PARABOLA_MULTIPLIER;
+ const x = Math.abs(velocity) - VELOCITY_CURVE_THRESHOLD + c;
+
+ pos = slope * VELOCITY_CURVE_THRESHOLD +
+ DECELERATION_PARABOLA_MULTIPLIER * x * x -
+ DECELERATION_PARABOLA_MULTIPLIER * c * c;
+ } else {
+ pos = Math.abs(velocity) * slope;
+ }
+
+ pos = pos * Math.sign(velocity) + this._progress;
+ pos = Math.clamp(pos, ...this._getBounds(this._initialProgress));
+
+ const index = this._findPointForProjection(pos, velocity);
+
+ return this._snapPoints[index];
+ }
+
+ _endTouchGesture(_gesture, time, distance) {
+ this._endGesture(time, distance, false);
+ }
+
+ _endTouchpadGesture(_gesture, time, distance) {
+ this._endGesture(time, distance, true);
+ }
+
+ _endGesture(time, distance, isTouchpad) {
+ if (this._state !== State.SCROLLING)
+ return;
+
+ if ((this._allowedModes & Main.actionMode) === 0 || !this.enabled) {
+ this._interrupt();
+ return;
+ }
+
+ this._history.trim(time);
+
+ let velocity = this._history.calculateVelocity();
+ const endProgress = this._getEndProgress(velocity, distance, isTouchpad);
+
+ velocity /= distance;
+
+ if ((endProgress - this._progress) * velocity <= 0)
+ velocity = ANIMATION_BASE_VELOCITY;
+
+ const nPoints = Math.max(1, Math.ceil(Math.abs(this._progress - endProgress)));
+ const maxDuration = MAX_ANIMATION_DURATION * Math.log2(1 + nPoints);
+
+ let duration = Math.abs((this._progress - endProgress) / velocity * DURATION_MULTIPLIER);
+ if (duration > 0)
+ duration = Math.clamp(duration, MIN_ANIMATION_DURATION, maxDuration);
+
+ this._reset();
+ this.emit('end', duration, endProgress);
+ }
+
+ _cancelTouchGesture(_gesture, time, distance) {
+ if (this._state !== State.SCROLLING)
+ return;
+
+ this._cancelled = true;
+ this._endGesture(time, distance, false);
+ }
+
+ /**
+ * confirmSwipe:
+ * @param {number} distance: swipe distance in pixels
+ * @param {number[]} snapPoints:
+ * An array of snap points, sorted in ascending order
+ * @param {number} currentProgress: initial progress value
+ * @param {number} cancelProgress: the value to be used on cancelling
+ *
+ * Confirms a swipe. User has to call this in 'begin' signal handler,
+ * otherwise the swipe wouldn't start. If there's an animation running,
+ * it should be stopped first.
+ *
+ * @cancel_progress must always be a snap point, or a value matching
+ * some other non-transient state.
+ */
+ confirmSwipe(distance, snapPoints, currentProgress, cancelProgress) {
+ this.distance = distance;
+ this._snapPoints = snapPoints;
+ this._initialProgress = currentProgress;
+ this._progress = currentProgress;
+ this._cancelProgress = cancelProgress;
+
+ this._state = State.SCROLLING;
+ }
+
+ destroy() {
+ if (this._touchpadGesture) {
+ this._touchpadGesture.destroy();
+ delete this._touchpadGesture;
+ }
+
+ if (this._touchGesture) {
+ global.stage.remove_action(this._touchGesture);
+ delete this._touchGesture;
+ }
+ }
+});
diff --git a/js/ui/switchMonitor.js b/js/ui/switchMonitor.js
new file mode 100644
index 0000000..a95c5fa
--- /dev/null
+++ b/js/ui/switchMonitor.js
@@ -0,0 +1,122 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SwitchMonitorPopup */
+
+const { Clutter, GObject, Meta, St } = imports.gi;
+
+const SwitcherPopup = imports.ui.switcherPopup;
+
+var APP_ICON_SIZE = 96;
+
+var SwitchMonitorPopup = GObject.registerClass(
+class SwitchMonitorPopup extends SwitcherPopup.SwitcherPopup {
+ _init() {
+ let items = [];
+
+ items.push({
+ icon: 'view-mirror-symbolic',
+ /* Translators: this is for display mirroring i.e. cloning.
+ * Try to keep it under around 15 characters.
+ */
+ label: _('Mirror'),
+ configType: Meta.MonitorSwitchConfigType.ALL_MIRROR,
+ });
+
+ items.push({
+ icon: 'video-joined-displays-symbolic',
+ /* Translators: this is for the desktop spanning displays.
+ * Try to keep it under around 15 characters.
+ */
+ label: _('Join Displays'),
+ configType: Meta.MonitorSwitchConfigType.ALL_LINEAR,
+ });
+
+ if (global.backend.get_monitor_manager().has_builtin_panel) {
+ items.push({
+ icon: 'video-single-display-symbolic',
+ /* Translators: this is for using only an external display.
+ * Try to keep it under around 15 characters.
+ */
+ label: _('External Only'),
+ configType: Meta.MonitorSwitchConfigType.EXTERNAL,
+ });
+ items.push({
+ icon: 'computer-symbolic',
+ /* Translators: this is for using only the laptop display.
+ * Try to keep it under around 15 characters.
+ */
+ label: _('Built-in Only'),
+ configType: Meta.MonitorSwitchConfigType.BUILTIN,
+ });
+ }
+
+ super._init(items);
+
+ this._switcherList = new SwitchMonitorSwitcher(items);
+ }
+
+ show(backward, binding, mask) {
+ if (!Meta.MonitorManager.get().can_switch_config())
+ return false;
+
+ return super.show(backward, binding, mask);
+ }
+
+ _initialSelection() {
+ let currentConfig = Meta.MonitorManager.get().get_switch_config();
+ let selectConfig = (currentConfig + 1) % this._items.length;
+ this._select(selectConfig);
+ }
+
+ _keyPressHandler(keysym, action) {
+ if (action == Meta.KeyBindingAction.SWITCH_MONITOR)
+ this._select(this._next());
+ else if (keysym == Clutter.KEY_Left)
+ this._select(this._previous());
+ else if (keysym == Clutter.KEY_Right)
+ this._select(this._next());
+ else
+ return Clutter.EVENT_PROPAGATE;
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _finish() {
+ super._finish();
+
+ const monitorManager = global.backend.get_monitor_manager();
+ const item = this._items[this._selectedIndex];
+
+ monitorManager.switch_config(item.configType);
+ }
+});
+
+var SwitchMonitorSwitcher = GObject.registerClass(
+class SwitchMonitorSwitcher extends SwitcherPopup.SwitcherList {
+ _init(items) {
+ super._init(true);
+
+ for (let i = 0; i < items.length; i++)
+ this._addIcon(items[i]);
+ }
+
+ _addIcon(item) {
+ const box = new St.BoxLayout({
+ style_class: 'alt-tab-app',
+ vertical: true,
+ });
+
+ const icon = new St.Icon({
+ icon_name: item.icon,
+ icon_size: APP_ICON_SIZE,
+ });
+ box.add_child(icon);
+
+ let text = new St.Label({
+ text: item.label,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add_child(text);
+
+ this.addItem(box, text);
+ }
+});
diff --git a/js/ui/switcherPopup.js b/js/ui/switcherPopup.js
new file mode 100644
index 0000000..4b0479b
--- /dev/null
+++ b/js/ui/switcherPopup.js
@@ -0,0 +1,688 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SwitcherPopup, SwitcherList */
+
+const { Clutter, GLib, GObject, St } = imports.gi;
+
+const Main = imports.ui.main;
+
+var POPUP_DELAY_TIMEOUT = 150; // milliseconds
+
+var POPUP_SCROLL_TIME = 100; // milliseconds
+var POPUP_FADE_OUT_TIME = 100; // milliseconds
+
+var DISABLE_HOVER_TIMEOUT = 500; // milliseconds
+var NO_MODS_TIMEOUT = 1500; // milliseconds
+
+function mod(a, b) {
+ return (a + b) % b;
+}
+
+function primaryModifier(mask) {
+ if (mask == 0)
+ return 0;
+
+ let primary = 1;
+ while (mask > 1) {
+ mask >>= 1;
+ primary <<= 1;
+ }
+ return primary;
+}
+
+var SwitcherPopup = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+}, class SwitcherPopup extends St.Widget {
+ _init(items) {
+ super._init({
+ style_class: 'switcher-popup',
+ reactive: true,
+ visible: false,
+ });
+
+ this._switcherList = null;
+
+ this._items = items || [];
+ this._selectedIndex = 0;
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ Main.uiGroup.add_actor(this);
+
+ Main.layoutManager.connectObject(
+ 'system-modal-opened', () => this.destroy(), this);
+
+ this._haveModal = false;
+ this._modifierMask = 0;
+
+ this._motionTimeoutId = 0;
+ this._initialDelayTimeoutId = 0;
+ this._noModsTimeoutId = 0;
+
+ this.add_constraint(new Clutter.BindConstraint({
+ source: global.stage,
+ coordinate: Clutter.BindCoordinate.ALL,
+ }));
+
+ // Initially disable hover so we ignore the enter-event if
+ // the switcher appears underneath the current pointer location
+ this._disableHover();
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ let childBox = new Clutter.ActorBox();
+ let primary = Main.layoutManager.primaryMonitor;
+
+ let leftPadding = this.get_theme_node().get_padding(St.Side.LEFT);
+ let rightPadding = this.get_theme_node().get_padding(St.Side.RIGHT);
+ let hPadding = leftPadding + rightPadding;
+
+ // Allocate the switcherList
+ // We select a size based on an icon size that does not overflow the screen
+ let [, childNaturalHeight] = this._switcherList.get_preferred_height(primary.width - hPadding);
+ let [, childNaturalWidth] = this._switcherList.get_preferred_width(childNaturalHeight);
+ childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
+ childBox.x2 = Math.min(primary.x + primary.width - rightPadding, childBox.x1 + childNaturalWidth);
+ childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
+ childBox.y2 = childBox.y1 + childNaturalHeight;
+ this._switcherList.allocate(childBox);
+ }
+
+ _initialSelection(backward, _binding) {
+ if (backward)
+ this._select(this._items.length - 1);
+ else if (this._items.length == 1)
+ this._select(0);
+ else
+ this._select(1);
+ }
+
+ show(backward, binding, mask) {
+ if (this._items.length == 0)
+ return false;
+
+ let grab = Main.pushModal(this);
+ // We expect at least a keyboard grab here
+ if ((grab.get_seat_state() & Clutter.GrabState.KEYBOARD) === 0) {
+ Main.popModal(grab);
+ return false;
+ }
+ this._grab = grab;
+ this._haveModal = true;
+ this._modifierMask = primaryModifier(mask);
+
+ this.add_actor(this._switcherList);
+ this._switcherList.connect('item-activated', this._itemActivated.bind(this));
+ this._switcherList.connect('item-entered', this._itemEntered.bind(this));
+ this._switcherList.connect('item-removed', this._itemRemoved.bind(this));
+
+ // Need to force an allocation so we can figure out whether we
+ // need to scroll when selecting
+ this.opacity = 0;
+ this.visible = true;
+ this.get_allocation_box();
+
+ this._initialSelection(backward, binding);
+
+ // There's a race condition; if the user released Alt before
+ // we got the grab, then we won't be notified. (See
+ // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
+ // details.) So we check now. (Have to do this after updating
+ // selection.)
+ if (this._modifierMask) {
+ let [x_, y_, mods] = global.get_pointer();
+ if (!(mods & this._modifierMask)) {
+ this._finish(global.get_current_time());
+ return true;
+ }
+ } else {
+ this._resetNoModsTimeout();
+ }
+
+ // We delay showing the popup so that fast Alt+Tab users aren't
+ // disturbed by the popup briefly flashing.
+ this._initialDelayTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ POPUP_DELAY_TIMEOUT,
+ () => {
+ this._showImmediately();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._initialDelayTimeoutId, '[gnome-shell] Main.osdWindow.cancel');
+ return true;
+ }
+
+ _showImmediately() {
+ if (this._initialDelayTimeoutId === 0)
+ return;
+
+ GLib.source_remove(this._initialDelayTimeoutId);
+ this._initialDelayTimeoutId = 0;
+
+ Main.osdWindowManager.hideAll();
+ this.opacity = 255;
+ }
+
+ _next() {
+ return mod(this._selectedIndex + 1, this._items.length);
+ }
+
+ _previous() {
+ return mod(this._selectedIndex - 1, this._items.length);
+ }
+
+ _keyPressHandler(_keysym, _action) {
+ throw new GObject.NotImplementedError(`_keyPressHandler in ${this.constructor.name}`);
+ }
+
+ vfunc_key_press_event(keyEvent) {
+ let keysym = keyEvent.keyval;
+ let action = global.display.get_keybinding_action(
+ keyEvent.hardware_keycode, keyEvent.modifier_state);
+
+ this._disableHover();
+
+ if (this._keyPressHandler(keysym, action) != Clutter.EVENT_PROPAGATE) {
+ this._showImmediately();
+ return Clutter.EVENT_STOP;
+ }
+
+ // Note: pressing one of the below keys will destroy the popup only if
+ // that key is not used by the active popup's keyboard shortcut
+ if (keysym === Clutter.KEY_Escape || keysym === Clutter.KEY_Tab)
+ this.fadeAndDestroy();
+
+ // Allow to explicitly select the current item; this is particularly
+ // useful for no-modifier popups
+ if (keysym === Clutter.KEY_space ||
+ keysym === Clutter.KEY_Return ||
+ keysym === Clutter.KEY_KP_Enter ||
+ keysym === Clutter.KEY_ISO_Enter)
+ this._finish(keyEvent.time);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_key_release_event(keyEvent) {
+ if (this._modifierMask) {
+ let [x_, y_, mods] = global.get_pointer();
+ let state = mods & this._modifierMask;
+
+ if (state == 0)
+ this._finish(keyEvent.time);
+ } else {
+ this._resetNoModsTimeout();
+ }
+
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_button_press_event() {
+ /* We clicked outside */
+ this.fadeAndDestroy();
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _scrollHandler(direction) {
+ if (direction == Clutter.ScrollDirection.UP)
+ this._select(this._previous());
+ else if (direction == Clutter.ScrollDirection.DOWN)
+ this._select(this._next());
+ }
+
+ vfunc_scroll_event(scrollEvent) {
+ this._disableHover();
+
+ this._scrollHandler(scrollEvent.direction);
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _itemActivatedHandler(n) {
+ this._select(n);
+ }
+
+ _itemActivated(switcher, n) {
+ this._itemActivatedHandler(n);
+ this._finish(global.get_current_time());
+ }
+
+ _itemEnteredHandler(n) {
+ this._select(n);
+ }
+
+ _itemEntered(switcher, n) {
+ if (!this.mouseActive)
+ return;
+ this._itemEnteredHandler(n);
+ }
+
+ _itemRemovedHandler(n) {
+ if (this._items.length > 0) {
+ let newIndex;
+
+ if (n < this._selectedIndex)
+ newIndex = this._selectedIndex - 1;
+ else if (n === this._selectedIndex)
+ newIndex = Math.min(n, this._items.length - 1);
+ else if (n > this._selectedIndex)
+ return; // No need to select something new in this case
+
+ this._select(newIndex);
+ } else {
+ this.fadeAndDestroy();
+ }
+ }
+
+ _itemRemoved(switcher, n) {
+ this._itemRemovedHandler(n);
+ }
+
+ _disableHover() {
+ this.mouseActive = false;
+
+ if (this._motionTimeoutId != 0)
+ GLib.source_remove(this._motionTimeoutId);
+
+ this._motionTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, DISABLE_HOVER_TIMEOUT, this._mouseTimedOut.bind(this));
+ GLib.Source.set_name_by_id(this._motionTimeoutId, '[gnome-shell] this._mouseTimedOut');
+ }
+
+ _mouseTimedOut() {
+ this._motionTimeoutId = 0;
+ this.mouseActive = true;
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _resetNoModsTimeout() {
+ if (this._noModsTimeoutId != 0)
+ GLib.source_remove(this._noModsTimeoutId);
+
+ this._noModsTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ NO_MODS_TIMEOUT,
+ () => {
+ this._finish(global.display.get_current_time_roundtrip());
+ this._noModsTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _popModal() {
+ if (this._haveModal) {
+ Main.popModal(this._grab);
+ this._grab = null;
+ this._haveModal = false;
+ }
+ }
+
+ fadeAndDestroy() {
+ this._popModal();
+ if (this.opacity > 0) {
+ this.ease({
+ opacity: 0,
+ duration: POPUP_FADE_OUT_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this.destroy(),
+ });
+ } else {
+ this.destroy();
+ }
+ }
+
+ _finish(_timestamp) {
+ this.fadeAndDestroy();
+ }
+
+ _onDestroy() {
+ this._popModal();
+
+ if (this._motionTimeoutId != 0)
+ GLib.source_remove(this._motionTimeoutId);
+ if (this._initialDelayTimeoutId != 0)
+ GLib.source_remove(this._initialDelayTimeoutId);
+ if (this._noModsTimeoutId != 0)
+ GLib.source_remove(this._noModsTimeoutId);
+
+ // Make sure the SwitcherList is always destroyed, it may not be
+ // a child of the actor at this point.
+ if (this._switcherList)
+ this._switcherList.destroy();
+ }
+
+ _select(num) {
+ this._selectedIndex = num;
+ this._switcherList.highlight(num);
+ }
+});
+
+var SwitcherButton = GObject.registerClass(
+class SwitcherButton extends St.Button {
+ _init(square) {
+ super._init({
+ style_class: 'item-box',
+ reactive: true,
+ });
+
+ this._square = square;
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ if (this._square)
+ return this.get_preferred_height(-1);
+ else
+ return super.vfunc_get_preferred_width(forHeight);
+ }
+});
+
+var SwitcherList = GObject.registerClass({
+ Signals: {
+ 'item-activated': { param_types: [GObject.TYPE_INT] },
+ 'item-entered': { param_types: [GObject.TYPE_INT] },
+ 'item-removed': { param_types: [GObject.TYPE_INT] },
+ },
+}, class SwitcherList extends St.Widget {
+ _init(squareItems) {
+ super._init({ style_class: 'switcher-list' });
+
+ this._list = new St.BoxLayout({
+ style_class: 'switcher-list-item-container',
+ vertical: false,
+ x_expand: true,
+ y_expand: true,
+ });
+
+ let layoutManager = this._list.get_layout_manager();
+
+ this._list.spacing = 0;
+ this._list.connect('style-changed', () => {
+ this._list.spacing = this._list.get_theme_node().get_length('spacing');
+ });
+
+ this._scrollView = new St.ScrollView({
+ style_class: 'hfade',
+ enable_mouse_scrolling: false,
+ });
+ this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.NEVER);
+
+ this._scrollView.add_actor(this._list);
+ this.add_actor(this._scrollView);
+
+ // Those arrows indicate whether scrolling in one direction is possible
+ this._leftArrow = new St.DrawingArea({
+ style_class: 'switcher-arrow',
+ pseudo_class: 'highlighted',
+ });
+ this._leftArrow.connect('repaint', () => {
+ drawArrow(this._leftArrow, St.Side.LEFT);
+ });
+ this._rightArrow = new St.DrawingArea({
+ style_class: 'switcher-arrow',
+ pseudo_class: 'highlighted',
+ });
+ this._rightArrow.connect('repaint', () => {
+ drawArrow(this._rightArrow, St.Side.RIGHT);
+ });
+
+ this.add_actor(this._leftArrow);
+ this.add_actor(this._rightArrow);
+
+ this._items = [];
+ this._highlighted = -1;
+ this._squareItems = squareItems;
+ this._scrollableRight = true;
+ this._scrollableLeft = false;
+
+ layoutManager.homogeneous = squareItems;
+ }
+
+ addItem(item, label) {
+ let bbox = new SwitcherButton(this._squareItems);
+
+ bbox.set_child(item);
+ this._list.add_actor(bbox);
+
+ bbox.connect('clicked', () => this._onItemClicked(bbox));
+ bbox.connect('motion-event', () => this._onItemMotion(bbox));
+
+ bbox.label_actor = label;
+
+ this._items.push(bbox);
+
+ return bbox;
+ }
+
+ removeItem(index) {
+ let item = this._items.splice(index, 1);
+ item[0].destroy();
+ this.emit('item-removed', index);
+ }
+
+ addAccessibleState(index, state) {
+ this._items[index].add_accessible_state(state);
+ }
+
+ removeAccessibleState(index, state) {
+ this._items[index].remove_accessible_state(state);
+ }
+
+ _onItemClicked(item) {
+ this._itemActivated(this._items.indexOf(item));
+ }
+
+ _onItemMotion(item) {
+ // Avoid reentrancy
+ if (item !== this._items[this._highlighted])
+ this._itemEntered(this._items.indexOf(item));
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ highlight(index, justOutline) {
+ if (this._items[this._highlighted]) {
+ this._items[this._highlighted].remove_style_pseudo_class('outlined');
+ this._items[this._highlighted].remove_style_pseudo_class('selected');
+ }
+
+ if (this._items[index]) {
+ if (justOutline)
+ this._items[index].add_style_pseudo_class('outlined');
+ else
+ this._items[index].add_style_pseudo_class('selected');
+ }
+
+ this._highlighted = index;
+
+ let adjustment = this._scrollView.hscroll.adjustment;
+ let [value] = adjustment.get_values();
+ let [absItemX] = this._items[index].get_transformed_position();
+ let [result_, posX, posY_] = this.transform_stage_point(absItemX, 0);
+ let [containerWidth] = this.get_transformed_size();
+ if (posX + this._items[index].get_width() > containerWidth)
+ this._scrollToRight(index);
+ else if (this._items[index].allocation.x1 - value < 0)
+ this._scrollToLeft(index);
+ }
+
+ _scrollToLeft(index) {
+ let adjustment = this._scrollView.hscroll.adjustment;
+ let [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values();
+
+ let item = this._items[index];
+
+ if (item.allocation.x1 < value)
+ value = Math.max(0, item.allocation.x1);
+ else if (item.allocation.x2 > value + pageSize)
+ value = Math.min(upper, item.allocation.x2 - pageSize);
+
+ this._scrollableRight = true;
+ adjustment.ease(value, {
+ progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: POPUP_SCROLL_TIME,
+ onComplete: () => {
+ if (index === 0)
+ this._scrollableLeft = false;
+ this.queue_relayout();
+ },
+ });
+ }
+
+ _scrollToRight(index) {
+ let adjustment = this._scrollView.hscroll.adjustment;
+ let [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values();
+
+ let item = this._items[index];
+
+ if (item.allocation.x1 < value)
+ value = Math.max(0, item.allocation.x1);
+ else if (item.allocation.x2 > value + pageSize)
+ value = Math.min(upper, item.allocation.x2 - pageSize);
+
+ this._scrollableLeft = true;
+ adjustment.ease(value, {
+ progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: POPUP_SCROLL_TIME,
+ onComplete: () => {
+ if (index === this._items.length - 1)
+ this._scrollableRight = false;
+ this.queue_relayout();
+ },
+ });
+ }
+
+ _itemActivated(n) {
+ this.emit('item-activated', n);
+ }
+
+ _itemEntered(n) {
+ this.emit('item-entered', n);
+ }
+
+ _maxChildWidth(forHeight) {
+ let maxChildMin = 0;
+ let maxChildNat = 0;
+
+ for (let i = 0; i < this._items.length; i++) {
+ let [childMin, childNat] = this._items[i].get_preferred_width(forHeight);
+ maxChildMin = Math.max(childMin, maxChildMin);
+ maxChildNat = Math.max(childNat, maxChildNat);
+
+ if (this._squareItems) {
+ [childMin, childNat] = this._items[i].get_preferred_height(-1);
+ maxChildMin = Math.max(childMin, maxChildMin);
+ maxChildNat = Math.max(childNat, maxChildNat);
+ }
+ }
+
+ return [maxChildMin, maxChildNat];
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ let themeNode = this.get_theme_node();
+ let [maxChildMin] = this._maxChildWidth(forHeight);
+ let [minListWidth] = this._list.get_preferred_width(forHeight);
+
+ return themeNode.adjust_preferred_width(maxChildMin, minListWidth);
+ }
+
+ vfunc_get_preferred_height(_forWidth) {
+ let maxChildMin = 0;
+ let maxChildNat = 0;
+
+ for (let i = 0; i < this._items.length; i++) {
+ let [childMin, childNat] = this._items[i].get_preferred_height(-1);
+ maxChildMin = Math.max(childMin, maxChildMin);
+ maxChildNat = Math.max(childNat, maxChildNat);
+ }
+
+ if (this._squareItems) {
+ let [childMin] = this._maxChildWidth(-1);
+ maxChildMin = Math.max(childMin, maxChildMin);
+ maxChildNat = maxChildMin;
+ }
+
+ let themeNode = this.get_theme_node();
+ return themeNode.adjust_preferred_height(maxChildMin, maxChildNat);
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ let contentBox = this.get_theme_node().get_content_box(box);
+ let width = contentBox.x2 - contentBox.x1;
+ let height = contentBox.y2 - contentBox.y1;
+
+ let leftPadding = this.get_theme_node().get_padding(St.Side.LEFT);
+ let rightPadding = this.get_theme_node().get_padding(St.Side.RIGHT);
+
+ let [minListWidth] = this._list.get_preferred_width(height);
+
+ let childBox = new Clutter.ActorBox();
+ let scrollable = minListWidth > width;
+
+ this._scrollView.allocate(contentBox);
+
+ let arrowWidth = Math.floor(leftPadding / 3);
+ let arrowHeight = arrowWidth * 2;
+ childBox.x1 = leftPadding / 2;
+ childBox.y1 = this.height / 2 - arrowWidth;
+ childBox.x2 = childBox.x1 + arrowWidth;
+ childBox.y2 = childBox.y1 + arrowHeight;
+ this._leftArrow.allocate(childBox);
+ this._leftArrow.opacity = this._scrollableLeft && scrollable ? 255 : 0;
+
+ arrowWidth = Math.floor(rightPadding / 3);
+ arrowHeight = arrowWidth * 2;
+ childBox.x1 = this.width - arrowWidth - rightPadding / 2;
+ childBox.y1 = this.height / 2 - arrowWidth;
+ childBox.x2 = childBox.x1 + arrowWidth;
+ childBox.y2 = childBox.y1 + arrowHeight;
+ this._rightArrow.allocate(childBox);
+ this._rightArrow.opacity = this._scrollableRight && scrollable ? 255 : 0;
+ }
+});
+
+function drawArrow(area, side) {
+ let themeNode = area.get_theme_node();
+ let borderColor = themeNode.get_border_color(side);
+ let bodyColor = themeNode.get_foreground_color();
+
+ let [width, height] = area.get_surface_size();
+ let cr = area.get_context();
+
+ cr.setLineWidth(1.0);
+ Clutter.cairo_set_source_color(cr, borderColor);
+
+ switch (side) {
+ case St.Side.TOP:
+ cr.moveTo(0, height);
+ cr.lineTo(Math.floor(width * 0.5), 0);
+ cr.lineTo(width, height);
+ break;
+
+ case St.Side.BOTTOM:
+ cr.moveTo(width, 0);
+ cr.lineTo(Math.floor(width * 0.5), height);
+ cr.lineTo(0, 0);
+ break;
+
+ case St.Side.LEFT:
+ cr.moveTo(width, height);
+ cr.lineTo(0, Math.floor(height * 0.5));
+ cr.lineTo(width, 0);
+ break;
+
+ case St.Side.RIGHT:
+ cr.moveTo(0, 0);
+ cr.lineTo(width, Math.floor(height * 0.5));
+ cr.lineTo(0, height);
+ break;
+ }
+
+ cr.strokePreserve();
+
+ Clutter.cairo_set_source_color(cr, bodyColor);
+ cr.fill();
+ cr.$dispose();
+}
+
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
new file mode 100644
index 0000000..24c8160
--- /dev/null
+++ b/js/ui/unlockDialog.js
@@ -0,0 +1,899 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported UnlockDialog */
+
+const {
+ AccountsService, Atk, Clutter, Gdm, Gio,
+ GnomeDesktop, GLib, GObject, Meta, Shell, St,
+} = imports.gi;
+
+const Background = imports.ui.background;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+const SwipeTracker = imports.ui.swipeTracker;
+
+const AuthPrompt = imports.gdm.authPrompt;
+
+// The timeout before going back automatically to the lock screen (in seconds)
+const IDLE_TIMEOUT = 2 * 60;
+
+// The timeout before showing the unlock hint (in seconds)
+const HINT_TIMEOUT = 4;
+
+const CROSSFADE_TIME = 300;
+const FADE_OUT_TRANSLATION = 200;
+const FADE_OUT_SCALE = 0.3;
+
+const BLUR_BRIGHTNESS = 0.55;
+const BLUR_SIGMA = 60;
+
+const SUMMARY_ICON_SIZE = 32;
+
+var NotificationsBox = GObject.registerClass({
+ Signals: { 'wake-up-screen': {} },
+}, class NotificationsBox extends St.BoxLayout {
+ _init() {
+ super._init({
+ vertical: true,
+ name: 'unlockDialogNotifications',
+ style_class: 'unlock-dialog-notifications-container',
+ });
+
+ this._scrollView = new St.ScrollView({ hscrollbar_policy: St.PolicyType.NEVER });
+ this._notificationBox = new St.BoxLayout({
+ vertical: true,
+ style_class: 'unlock-dialog-notifications-container',
+ });
+ this._scrollView.add_actor(this._notificationBox);
+
+ this.add_child(this._scrollView);
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.notifications',
+ });
+
+ this._sources = new Map();
+ Main.messageTray.getSources().forEach(source => {
+ this._sourceAdded(Main.messageTray, source, true);
+ });
+ this._updateVisibility();
+
+ Main.messageTray.connectObject('source-added',
+ this._sourceAdded.bind(this), this);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ let items = this._sources.entries();
+ for (let [source, obj] of items)
+ this._removeSource(source, obj);
+ }
+
+ _updateVisibility() {
+ this._notificationBox.visible =
+ this._notificationBox.get_children().some(a => a.visible);
+
+ this.visible = this._notificationBox.visible;
+ }
+
+ _makeNotificationSource(source, box) {
+ let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
+ box.add_child(sourceActor);
+
+ let textBox = new St.BoxLayout({
+ x_expand: true,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ box.add_child(textBox);
+
+ let title = new St.Label({
+ text: source.title,
+ style_class: 'unlock-dialog-notification-label',
+ x_expand: true,
+ x_align: Clutter.ActorAlign.START,
+ });
+ textBox.add(title);
+
+ let count = source.unseenCount;
+ let countLabel = new St.Label({
+ text: `${count}`,
+ visible: count > 1,
+ style_class: 'unlock-dialog-notification-count-text',
+ });
+ textBox.add(countLabel);
+
+ box.visible = count !== 0;
+ return [title, countLabel];
+ }
+
+ _makeNotificationDetailedSource(source, box) {
+ let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
+ let sourceBin = new St.Bin({ child: sourceActor });
+ box.add(sourceBin);
+
+ let textBox = new St.BoxLayout({ vertical: true });
+ box.add_child(textBox);
+
+ let title = new St.Label({
+ text: source.title.replace(/\n/g, ' '),
+ style_class: 'unlock-dialog-notification-label',
+ });
+ textBox.add(title);
+
+ let visible = false;
+ for (let i = 0; i < source.notifications.length; i++) {
+ let n = source.notifications[i];
+
+ if (n.acknowledged)
+ continue;
+
+ let body = '';
+ if (n.bannerBodyText) {
+ const bodyText = n.bannerBodyText.replace(/\n/g, ' ');
+ body = n.bannerBodyMarkup
+ ? bodyText
+ : GLib.markup_escape_text(bodyText, -1);
+ }
+
+ let label = new St.Label({ style_class: 'unlock-dialog-notification-count-text' });
+ label.clutter_text.set_markup(`<b>${n.title}</b> ${body}`);
+ textBox.add(label);
+
+ visible = true;
+ }
+
+ box.visible = visible;
+ return [title, null];
+ }
+
+ _shouldShowDetails(source) {
+ return source.policy.detailsInLockScreen ||
+ source.narrowestPrivacyScope === MessageTray.PrivacyScope.SYSTEM;
+ }
+
+ _updateSourceBoxStyle(source, obj, box) {
+ let hasCriticalNotification =
+ source.notifications.some(n => n.urgency === MessageTray.Urgency.CRITICAL);
+
+ if (hasCriticalNotification !== obj.hasCriticalNotification) {
+ obj.hasCriticalNotification = hasCriticalNotification;
+
+ if (hasCriticalNotification)
+ box.add_style_class_name('critical');
+ else
+ box.remove_style_class_name('critical');
+ }
+ }
+
+ _showSource(source, obj, box) {
+ if (obj.detailed)
+ [obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box);
+ else
+ [obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box);
+
+ box.visible = obj.visible && (source.unseenCount > 0);
+
+ this._updateSourceBoxStyle(source, obj, box);
+ }
+
+ _wakeUpScreenForSource(source) {
+ if (!this._settings.get_boolean('show-banners'))
+ return;
+ const obj = this._sources.get(source);
+ if (obj?.sourceBox.visible)
+ this.emit('wake-up-screen');
+ }
+
+ _sourceAdded(tray, source, initial) {
+ let obj = {
+ visible: source.policy.showInLockScreen,
+ detailed: this._shouldShowDetails(source),
+ sourceBox: null,
+ titleLabel: null,
+ countLabel: null,
+ hasCriticalNotification: false,
+ };
+
+ obj.sourceBox = new St.BoxLayout({
+ style_class: 'unlock-dialog-notification-source',
+ x_expand: true,
+ });
+ this._showSource(source, obj, obj.sourceBox);
+ this._notificationBox.add_child(obj.sourceBox);
+
+ source.connectObject(
+ 'notify::count', () => this._countChanged(source, obj),
+ 'notify::title', () => this._titleChanged(source, obj),
+ 'destroy', () => {
+ this._removeSource(source, obj);
+ this._updateVisibility();
+ }, this);
+ obj.policyChangedId = source.policy.connect('notify', (policy, pspec) => {
+ if (pspec.name === 'show-in-lock-screen')
+ this._visibleChanged(source, obj);
+ else
+ this._detailedChanged(source, obj);
+ });
+
+ this._sources.set(source, obj);
+
+ if (!initial) {
+ // block scrollbars while animating, if they're not needed now
+ let boxHeight = this._notificationBox.height;
+ if (this._scrollView.height >= boxHeight)
+ this._scrollView.vscrollbar_policy = St.PolicyType.NEVER;
+
+ let widget = obj.sourceBox;
+ let [, natHeight] = widget.get_preferred_height(-1);
+ widget.height = 0;
+ widget.ease({
+ height: natHeight,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: 250,
+ onComplete: () => {
+ this._scrollView.vscrollbar_policy = St.PolicyType.AUTOMATIC;
+ widget.set_height(-1);
+ },
+ });
+
+ this._updateVisibility();
+ this._wakeUpScreenForSource(source);
+ }
+ }
+
+ _titleChanged(source, obj) {
+ obj.titleLabel.text = source.title;
+ }
+
+ _countChanged(source, obj) {
+ // A change in the number of notifications may change whether we show
+ // details.
+ let newDetailed = this._shouldShowDetails(source);
+ let oldDetailed = obj.detailed;
+
+ obj.detailed = newDetailed;
+
+ if (obj.detailed || oldDetailed !== newDetailed) {
+ // A new notification was pushed, or a previous notification was destroyed.
+ // Give up, and build the list again.
+
+ obj.sourceBox.destroy_all_children();
+ obj.titleLabel = obj.countLabel = null;
+ this._showSource(source, obj, obj.sourceBox);
+ } else {
+ let count = source.unseenCount;
+ obj.countLabel.text = `${count}`;
+ obj.countLabel.visible = count > 1;
+ }
+
+ obj.sourceBox.visible = obj.visible && (source.unseenCount > 0);
+
+ this._updateVisibility();
+ this._wakeUpScreenForSource(source);
+ }
+
+ _visibleChanged(source, obj) {
+ if (obj.visible === source.policy.showInLockScreen)
+ return;
+
+ obj.visible = source.policy.showInLockScreen;
+ obj.sourceBox.visible = obj.visible && source.unseenCount > 0;
+
+ this._updateVisibility();
+ this._wakeUpScreenForSource(source);
+ }
+
+ _detailedChanged(source, obj) {
+ let newDetailed = this._shouldShowDetails(source);
+ if (obj.detailed === newDetailed)
+ return;
+
+ obj.detailed = newDetailed;
+
+ obj.sourceBox.destroy_all_children();
+ obj.titleLabel = obj.countLabel = null;
+ this._showSource(source, obj, obj.sourceBox);
+ }
+
+ _removeSource(source, obj) {
+ obj.sourceBox.destroy();
+ obj.sourceBox = obj.titleLabel = obj.countLabel = null;
+
+ source.policy.disconnect(obj.policyChangedId);
+
+ this._sources.delete(source);
+ }
+});
+
+var Clock = GObject.registerClass(
+class UnlockDialogClock extends St.BoxLayout {
+ _init() {
+ super._init({ style_class: 'unlock-dialog-clock', vertical: true });
+
+ this._time = new St.Label({
+ style_class: 'unlock-dialog-clock-time',
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this._date = new St.Label({
+ style_class: 'unlock-dialog-clock-date',
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+ this._hint = new St.Label({
+ style_class: 'unlock-dialog-clock-hint',
+ x_align: Clutter.ActorAlign.CENTER,
+ opacity: 0,
+ });
+
+ this.add_child(this._time);
+ this.add_child(this._date);
+ this.add_child(this._hint);
+
+ this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
+ this._wallClock.connect('notify::clock', this._updateClock.bind(this));
+
+ this._seat = Clutter.get_default_backend().get_default_seat();
+ this._seat.connectObject('notify::touch-mode',
+ this._updateHint.bind(this), this);
+
+ this._monitorManager = Meta.MonitorManager.get();
+ this._monitorManager.connectObject('power-save-mode-changed',
+ () => (this._hint.opacity = 0), this);
+
+ this._idleMonitor = global.backend.get_core_idle_monitor();
+ this._idleWatchId = this._idleMonitor.add_idle_watch(HINT_TIMEOUT * 1000, () => {
+ this._hint.ease({
+ opacity: 255,
+ duration: CROSSFADE_TIME,
+ });
+ });
+
+ this._updateClock();
+ this._updateHint();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _updateClock() {
+ this._time.text = this._wallClock.clock;
+
+ let date = new Date();
+ /* Translators: This is a time format for a date in
+ long format */
+ let dateFormat = Shell.util_translate_time_string(N_('%A %B %-d'));
+ this._date.text = date.toLocaleFormat(dateFormat);
+ }
+
+ _updateHint() {
+ this._hint.text = this._seat.touch_mode
+ ? _('Swipe up to unlock')
+ : _('Click or press a key to unlock');
+ }
+
+ _onDestroy() {
+ this._wallClock.run_dispose();
+
+ this._idleMonitor.remove_watch(this._idleWatchId);
+ }
+});
+
+var UnlockDialogLayout = GObject.registerClass(
+class UnlockDialogLayout extends Clutter.LayoutManager {
+ _init(stack, notifications, switchUserButton) {
+ super._init();
+
+ this._stack = stack;
+ this._notifications = notifications;
+ this._switchUserButton = switchUserButton;
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ return this._stack.get_preferred_width(forHeight);
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ return this._stack.get_preferred_height(forWidth);
+ }
+
+ vfunc_allocate(container, box) {
+ let [width, height] = box.get_size();
+
+ let tenthOfHeight = height / 10.0;
+ let thirdOfHeight = height / 3.0;
+
+ let [, , stackWidth, stackHeight] =
+ this._stack.get_preferred_size();
+
+ let [, , notificationsWidth, notificationsHeight] =
+ this._notifications.get_preferred_size();
+
+ let columnWidth = Math.max(stackWidth, notificationsWidth);
+
+ let columnX1 = Math.floor((width - columnWidth) / 2.0);
+ let actorBox = new Clutter.ActorBox();
+
+ // Notifications
+ let maxNotificationsHeight = Math.min(
+ notificationsHeight,
+ height - tenthOfHeight - stackHeight);
+
+ actorBox.x1 = columnX1;
+ actorBox.y1 = height - maxNotificationsHeight;
+ actorBox.x2 = columnX1 + columnWidth;
+ actorBox.y2 = actorBox.y1 + maxNotificationsHeight;
+
+ this._notifications.allocate(actorBox);
+
+ // Authentication Box
+ let stackY = Math.min(
+ thirdOfHeight,
+ height - stackHeight - maxNotificationsHeight);
+
+ actorBox.x1 = columnX1;
+ actorBox.y1 = stackY;
+ actorBox.x2 = columnX1 + columnWidth;
+ actorBox.y2 = stackY + stackHeight;
+
+ this._stack.allocate(actorBox);
+
+ // Switch User button
+ if (this._switchUserButton.visible) {
+ let [, , natWidth, natHeight] =
+ this._switchUserButton.get_preferred_size();
+
+ const textDirection = this._switchUserButton.get_text_direction();
+ if (textDirection === Clutter.TextDirection.RTL)
+ actorBox.x1 = box.x1 + natWidth;
+ else
+ actorBox.x1 = box.x2 - (natWidth * 2);
+
+ actorBox.y1 = box.y2 - (natHeight * 2);
+ actorBox.x2 = actorBox.x1 + natWidth;
+ actorBox.y2 = actorBox.y1 + natHeight;
+
+ this._switchUserButton.allocate(actorBox);
+ }
+ }
+});
+
+var UnlockDialog = GObject.registerClass({
+ Signals: {
+ 'failed': {},
+ 'wake-up-screen': {},
+ },
+}, class UnlockDialog extends St.Widget {
+ _init(parentActor) {
+ super._init({
+ accessible_role: Atk.Role.WINDOW,
+ style_class: 'unlock-dialog',
+ visible: false,
+ reactive: true,
+ });
+
+ parentActor.add_child(this);
+
+ this._gdmClient = new Gdm.Client();
+
+ try {
+ this._gdmClient.set_enabled_extensions([
+ Gdm.UserVerifierChoiceList.interface_info().name,
+ ]);
+ } catch (e) {
+ }
+
+ this._adjustment = new St.Adjustment({
+ actor: this,
+ lower: 0,
+ upper: 2,
+ page_size: 1,
+ page_increment: 1,
+ });
+ this._adjustment.connect('notify::value', () => {
+ this._setTransitionProgress(this._adjustment.value);
+ });
+
+ this._swipeTracker = new SwipeTracker.SwipeTracker(this,
+ Clutter.Orientation.VERTICAL,
+ Shell.ActionMode.UNLOCK_SCREEN);
+ this._swipeTracker.connect('begin', this._swipeBegin.bind(this));
+ this._swipeTracker.connect('update', this._swipeUpdate.bind(this));
+ this._swipeTracker.connect('end', this._swipeEnd.bind(this));
+
+ this.connect('scroll-event', (o, event) => {
+ if (this._swipeTracker.canHandleScrollEvent(event))
+ return Clutter.EVENT_PROPAGATE;
+
+ let direction = event.get_scroll_direction();
+ if (direction === Clutter.ScrollDirection.UP)
+ this._showClock();
+ else if (direction === Clutter.ScrollDirection.DOWN)
+ this._showPrompt();
+ return Clutter.EVENT_STOP;
+ });
+
+ this._activePage = null;
+
+ let tapAction = new Clutter.TapAction();
+ tapAction.connect('tap', this._showPrompt.bind(this));
+ this.add_action(tapAction);
+
+ // Background
+ this._backgroundGroup = new Clutter.Actor();
+ this.add_child(this._backgroundGroup);
+
+ this._bgManagers = [];
+
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+ themeContext.connectObject('notify::scale-factor',
+ () => this._updateBackgroundEffects(), this);
+
+ this._updateBackgrounds();
+ Main.layoutManager.connectObject('monitors-changed',
+ this._updateBackgrounds.bind(this), this);
+
+ this._userManager = AccountsService.UserManager.get_default();
+ this._userName = GLib.get_user_name();
+ this._user = this._userManager.get_user(this._userName);
+
+ // Authentication & Clock stack
+ this._stack = new Shell.Stack();
+
+ this._promptBox = new St.BoxLayout({ vertical: true });
+ this._promptBox.set_pivot_point(0.5, 0.5);
+ this._promptBox.hide();
+ this._stack.add_child(this._promptBox);
+
+ this._clock = new Clock();
+ this._clock.set_pivot_point(0.5, 0.5);
+ this._stack.add_child(this._clock);
+ this._showClock();
+
+ this.allowCancel = false;
+
+ Main.ctrlAltTabManager.addGroup(this, _('Unlock Window'), 'dialog-password-symbolic');
+
+ // Notifications
+ this._notificationsBox = new NotificationsBox();
+ this._notificationsBox.connect('wake-up-screen', () => this.emit('wake-up-screen'));
+
+ // Switch User button
+ this._otherUserButton = new St.Button({
+ style_class: 'modal-dialog-button button switch-user-button',
+ accessible_name: _('Log in as another user'),
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ reactive: false,
+ opacity: 0,
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.END,
+ icon_name: 'system-users-symbolic',
+ });
+ this._otherUserButton.set_pivot_point(0.5, 0.5);
+ this._otherUserButton.connect('clicked', this._otherUserClicked.bind(this));
+
+ this._screenSaverSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.screensaver' });
+
+ this._screenSaverSettings.connectObject('changed::user-switch-enabled',
+ this._updateUserSwitchVisibility.bind(this), this);
+
+ this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
+ this._lockdownSettings.connect('changed::disable-user-switching',
+ this._updateUserSwitchVisibility.bind(this));
+
+ this._user.connectObject('notify::is-loaded',
+ this._updateUserSwitchVisibility.bind(this), this);
+
+ this._updateUserSwitchVisibility();
+
+ // Main Box
+ let mainBox = new St.Widget();
+ mainBox.add_constraint(new Layout.MonitorConstraint({ primary: true }));
+ mainBox.add_child(this._stack);
+ mainBox.add_child(this._notificationsBox);
+ mainBox.add_child(this._otherUserButton);
+ mainBox.layout_manager = new UnlockDialogLayout(
+ this._stack,
+ this._notificationsBox,
+ this._otherUserButton);
+ this.add_child(mainBox);
+
+ this._idleMonitor = global.backend.get_core_idle_monitor();
+ this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, this._escape.bind(this));
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ vfunc_key_press_event(keyEvent) {
+ if (this._activePage === this._promptBox ||
+ (this._promptBox && this._promptBox.visible))
+ return Clutter.EVENT_PROPAGATE;
+
+ const { keyval } = keyEvent;
+ if (keyval === Clutter.KEY_Shift_L ||
+ keyval === Clutter.KEY_Shift_R ||
+ keyval === Clutter.KEY_Shift_Lock ||
+ keyval === Clutter.KEY_Caps_Lock)
+ return Clutter.EVENT_PROPAGATE;
+
+ let unichar = keyEvent.unicode_value;
+
+ this._showPrompt();
+
+ if (GLib.unichar_isgraph(unichar))
+ this._authPrompt.addCharacter(unichar);
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_captured_event(event) {
+ if (Main.keyboard.maybeHandleEvent(event))
+ return Clutter.EVENT_STOP;
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _createBackground(monitorIndex) {
+ let monitor = Main.layoutManager.monitors[monitorIndex];
+ let widget = new St.Widget({
+ style_class: 'screen-shield-background',
+ x: monitor.x,
+ y: monitor.y,
+ width: monitor.width,
+ height: monitor.height,
+ effect: new Shell.BlurEffect({ name: 'blur' }),
+ });
+
+ let bgManager = new Background.BackgroundManager({
+ container: widget,
+ monitorIndex,
+ controlPosition: false,
+ });
+
+ this._bgManagers.push(bgManager);
+
+ this._backgroundGroup.add_child(widget);
+ }
+
+ _updateBackgroundEffects() {
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+
+ for (const widget of this._backgroundGroup) {
+ const effect = widget.get_effect('blur');
+
+ if (effect) {
+ effect.set({
+ brightness: BLUR_BRIGHTNESS,
+ sigma: BLUR_SIGMA * themeContext.scale_factor,
+ });
+ }
+ }
+ }
+
+ _updateBackgrounds() {
+ for (let i = 0; i < this._bgManagers.length; i++)
+ this._bgManagers[i].destroy();
+
+ this._bgManagers = [];
+ this._backgroundGroup.destroy_all_children();
+
+ for (let i = 0; i < Main.layoutManager.monitors.length; i++)
+ this._createBackground(i);
+ this._updateBackgroundEffects();
+ }
+
+ _ensureAuthPrompt() {
+ if (!this._authPrompt) {
+ this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient,
+ AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
+ this._authPrompt.connect('failed', this._fail.bind(this));
+ this._authPrompt.connect('cancelled', this._fail.bind(this));
+ this._authPrompt.connect('reset', this._onReset.bind(this));
+ this._promptBox.add_child(this._authPrompt);
+ }
+
+ this._authPrompt.reset();
+ this._authPrompt.updateSensitivity(true);
+ }
+
+ _maybeDestroyAuthPrompt() {
+ let focus = global.stage.key_focus;
+ if (focus === null ||
+ (this._authPrompt && this._authPrompt.contains(focus)) ||
+ (this._otherUserButton && focus === this._otherUserButton))
+ this.grab_key_focus();
+
+ if (this._authPrompt) {
+ this._authPrompt.destroy();
+ this._authPrompt = null;
+ }
+ }
+
+ _showClock() {
+ if (this._activePage === this._clock)
+ return;
+
+ this._activePage = this._clock;
+
+ this._adjustment.ease(0, {
+ duration: CROSSFADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._maybeDestroyAuthPrompt(),
+ });
+ }
+
+ _showPrompt() {
+ this._ensureAuthPrompt();
+
+ if (this._activePage === this._promptBox)
+ return;
+
+ this._activePage = this._promptBox;
+
+ this._adjustment.ease(1, {
+ duration: CROSSFADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ _setTransitionProgress(progress) {
+ this._promptBox.visible = progress > 0;
+ this._clock.visible = progress < 1;
+
+ this._otherUserButton.set({
+ reactive: progress > 0,
+ can_focus: progress > 0,
+ });
+
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+
+ this._promptBox.set({
+ opacity: 255 * progress,
+ scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
+ scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
+ translation_y: FADE_OUT_TRANSLATION * (1 - progress) * scaleFactor,
+ });
+
+ this._clock.set({
+ opacity: 255 * (1 - progress),
+ scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * (1 - progress),
+ scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * (1 - progress),
+ translation_y: -FADE_OUT_TRANSLATION * progress * scaleFactor,
+ });
+
+ this._otherUserButton.set({
+ opacity: 255 * progress,
+ scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
+ scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
+ });
+ }
+
+ _fail() {
+ this._showClock();
+ this.emit('failed');
+ }
+
+ _onReset(authPrompt, beginRequest) {
+ let userName;
+ if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
+ this._authPrompt.setUser(this._user);
+ userName = this._userName;
+ } else {
+ userName = null;
+ }
+
+ this._authPrompt.begin({ userName });
+ }
+
+ _escape() {
+ if (this._authPrompt && this.allowCancel)
+ this._authPrompt.cancel();
+ }
+
+ _swipeBegin(tracker, monitor) {
+ if (monitor !== Main.layoutManager.primaryIndex)
+ return;
+
+ this._adjustment.remove_transition('value');
+
+ this._ensureAuthPrompt();
+
+ let progress = this._adjustment.value;
+ tracker.confirmSwipe(this._stack.height,
+ [0, 1],
+ progress,
+ Math.round(progress));
+ }
+
+ _swipeUpdate(tracker, progress) {
+ this._adjustment.value = progress;
+ }
+
+ _swipeEnd(tracker, duration, endProgress) {
+ this._activePage = endProgress
+ ? this._promptBox
+ : this._clock;
+
+ this._adjustment.ease(endProgress, {
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ duration,
+ onComplete: () => {
+ if (this._activePage === this._clock)
+ this._maybeDestroyAuthPrompt();
+ },
+ });
+ }
+
+ _otherUserClicked() {
+ Gdm.goto_login_session_sync(null);
+
+ this._authPrompt.cancel();
+ }
+
+ _onDestroy() {
+ this.popModal();
+
+ if (this._idleWatchId) {
+ this._idleMonitor.remove_watch(this._idleWatchId);
+ this._idleWatchId = 0;
+ }
+
+ if (this._gdmClient) {
+ this._gdmClient = null;
+ delete this._gdmClient;
+ }
+ }
+
+ _updateUserSwitchVisibility() {
+ this._otherUserButton.visible = this._userManager.can_switch() &&
+ this._screenSaverSettings.get_boolean('user-switch-enabled') &&
+ !this._lockdownSettings.get_boolean('disable-user-switching');
+ }
+
+ cancel() {
+ if (this._authPrompt)
+ this._authPrompt.cancel();
+ }
+
+ finish(onComplete) {
+ if (!this._authPrompt) {
+ onComplete();
+ return;
+ }
+
+ this._authPrompt.finish(onComplete);
+ }
+
+ open(timestamp) {
+ this.show();
+
+ if (this._isModal)
+ return true;
+
+ let modalParams = {
+ timestamp,
+ actionMode: Shell.ActionMode.UNLOCK_SCREEN,
+ };
+ let grab = Main.pushModal(Main.uiGroup, modalParams);
+ if (grab.get_seat_state() !== Clutter.GrabState.ALL) {
+ Main.popModal(grab);
+ return false;
+ }
+
+ this._grab = grab;
+ this._isModal = true;
+
+ return true;
+ }
+
+ activate() {
+ this._showPrompt();
+ }
+
+ popModal(timestamp) {
+ if (this._isModal) {
+ Main.popModal(this._grab, timestamp);
+ this._grab = null;
+ this._isModal = false;
+ }
+ }
+});
diff --git a/js/ui/userWidget.js b/js/ui/userWidget.js
new file mode 100644
index 0000000..76139e1
--- /dev/null
+++ b/js/ui/userWidget.js
@@ -0,0 +1,212 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+//
+// A widget showing the user avatar and name
+/* exported UserWidget */
+
+const { Clutter, GLib, GObject, St } = imports.gi;
+
+const Params = imports.misc.params;
+
+var AVATAR_ICON_SIZE = 64;
+
+// Adapted from gdm/gui/user-switch-applet/applet.c
+//
+// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+// Copyright (C) 2008,2009 Red Hat, Inc.
+
+var Avatar = GObject.registerClass(
+class Avatar extends St.Bin {
+ _init(user, params) {
+ let themeContext = St.ThemeContext.get_for_stage(global.stage);
+ params = Params.parse(params, {
+ styleClass: 'user-icon',
+ reactive: false,
+ iconSize: AVATAR_ICON_SIZE,
+ });
+
+ super._init({
+ style_class: params.styleClass,
+ reactive: params.reactive,
+ width: params.iconSize * themeContext.scaleFactor,
+ height: params.iconSize * themeContext.scaleFactor,
+ });
+
+ this._iconSize = params.iconSize;
+ this._user = user;
+
+ this.bind_property('reactive', this, 'track-hover',
+ GObject.BindingFlags.SYNC_CREATE);
+ this.bind_property('reactive', this, 'can-focus',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ // Monitor the scaling factor to make sure we recreate the avatar when needed.
+ themeContext.connectObject('notify::scale-factor', this.update.bind(this), this);
+ }
+
+ vfunc_style_changed() {
+ super.vfunc_style_changed();
+
+ let node = this.get_theme_node();
+ let [found, iconSize] = node.lookup_length('icon-size', false);
+
+ if (!found)
+ return;
+
+ let themeContext = St.ThemeContext.get_for_stage(global.stage);
+
+ // node.lookup_length() returns a scaled value, but we
+ // need unscaled
+ this._iconSize = iconSize / themeContext.scaleFactor;
+ this.update();
+ }
+
+ setSensitive(sensitive) {
+ this.reactive = sensitive;
+ }
+
+ update() {
+ let iconFile = null;
+ if (this._user) {
+ iconFile = this._user.get_icon_file();
+ if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
+ iconFile = null;
+ }
+
+ let { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ this.set_size(
+ this._iconSize * scaleFactor,
+ this._iconSize * scaleFactor);
+
+ if (iconFile) {
+ this.child = null;
+ this.add_style_class_name('user-avatar');
+ this.style = `
+ background-image: url("${iconFile}");
+ background-size: cover;`;
+ } else {
+ this.style = null;
+ this.child = new St.Icon({
+ icon_name: 'avatar-default-symbolic',
+ icon_size: this._iconSize,
+ });
+ }
+ }
+});
+
+var UserWidgetLabel = GObject.registerClass(
+class UserWidgetLabel extends St.Widget {
+ _init(user) {
+ super._init({ layout_manager: new Clutter.BinLayout() });
+
+ this._user = user;
+
+ this._realNameLabel = new St.Label({
+ style_class: 'user-widget-label',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this._realNameLabel);
+
+ this._userNameLabel = new St.Label({
+ style_class: 'user-widget-label',
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this._userNameLabel);
+
+ this._currentLabel = null;
+
+ this._user.connectObject(
+ 'notify::is-loaded', this._updateUser.bind(this),
+ 'changed', this._updateUser.bind(this), this);
+ this._updateUser();
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+
+ let [, , natRealNameWidth] = this._realNameLabel.get_preferred_size();
+
+ let childBox = new Clutter.ActorBox();
+
+ let hiddenLabel;
+ if (natRealNameWidth <= availWidth) {
+ this._currentLabel = this._realNameLabel;
+ hiddenLabel = this._userNameLabel;
+ } else {
+ this._currentLabel = this._userNameLabel;
+ hiddenLabel = this._realNameLabel;
+ }
+ this.label_actor = this._currentLabel;
+
+ hiddenLabel.allocate(childBox);
+
+ childBox.set_size(availWidth, availHeight);
+
+ this._currentLabel.allocate(childBox);
+ }
+
+ vfunc_paint(paintContext) {
+ this._currentLabel.paint(paintContext);
+ }
+
+ _updateUser() {
+ if (this._user.is_loaded) {
+ this._realNameLabel.text = this._user.get_real_name();
+ this._userNameLabel.text = this._user.get_user_name();
+ } else {
+ this._realNameLabel.text = '';
+ this._userNameLabel.text = '';
+ }
+ }
+});
+
+var UserWidget = GObject.registerClass(
+class UserWidget extends St.BoxLayout {
+ _init(user, orientation = Clutter.Orientation.HORIZONTAL) {
+ // If user is null, that implies a username-based login authorization.
+ this._user = user;
+
+ let vertical = orientation == Clutter.Orientation.VERTICAL;
+ let xAlign = vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START;
+ let styleClass = vertical ? 'user-widget vertical' : 'user-widget horizontal';
+
+ super._init({
+ styleClass,
+ vertical,
+ xAlign,
+ });
+
+ this._avatar = new Avatar(user);
+ this._avatar.x_align = Clutter.ActorAlign.CENTER;
+ this.add_child(this._avatar);
+
+ this._userLoadedId = 0;
+ this._userChangedId = 0;
+ if (user) {
+ this._label = new UserWidgetLabel(user);
+ this.add_child(this._label);
+
+ this._label.bind_property('label-actor', this, 'label-actor',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._user.connectObject(
+ 'notify::is-loaded', this._updateUser.bind(this),
+ 'changed', this._updateUser.bind(this), this);
+ } else {
+ this._label = new St.Label({
+ style_class: 'user-widget-label',
+ text: 'Empty User',
+ opacity: 0,
+ });
+ this.add_child(this._label);
+ }
+
+ this._updateUser();
+ }
+
+ _updateUser() {
+ this._avatar.update();
+ }
+});
diff --git a/js/ui/welcomeDialog.js b/js/ui/welcomeDialog.js
new file mode 100644
index 0000000..63c6d90
--- /dev/null
+++ b/js/ui/welcomeDialog.js
@@ -0,0 +1,64 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WelcomeDialog */
+
+const { Clutter, GObject, Shell, St } = imports.gi;
+
+const Config = imports.misc.config;
+const Dialog = imports.ui.dialog;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+
+var DialogResponse = {
+ NO_THANKS: 0,
+ TAKE_TOUR: 1,
+};
+
+var WelcomeDialog = GObject.registerClass(
+class WelcomeDialog extends ModalDialog.ModalDialog {
+ _init() {
+ super._init({ styleClass: 'welcome-dialog' });
+
+ const appSystem = Shell.AppSystem.get_default();
+ this._tourAppInfo = appSystem.lookup_app('org.gnome.Tour.desktop');
+
+ this._buildLayout();
+ }
+
+ open() {
+ if (!this._tourAppInfo)
+ return false;
+
+ return super.open();
+ }
+
+ _buildLayout() {
+ const [majorVersion] = Config.PACKAGE_VERSION.split('.');
+ const title = _('Welcome to GNOME %s').format(majorVersion);
+ const description = _('If you want to learn your way around, check out the tour.');
+ const content = new Dialog.MessageDialogContent({ title, description });
+
+ const icon = new St.Widget({ style_class: 'welcome-dialog-image' });
+ content.insert_child_at_index(icon, 0);
+
+ this.contentLayout.add_child(content);
+
+ this.addButton({
+ label: _('No Thanks'),
+ action: () => this._sendResponse(DialogResponse.NO_THANKS),
+ key: Clutter.KEY_Escape,
+ });
+ this.addButton({
+ label: _('Take Tour'),
+ action: () => this._sendResponse(DialogResponse.TAKE_TOUR),
+ });
+ }
+
+ _sendResponse(response) {
+ if (response === DialogResponse.TAKE_TOUR) {
+ this._tourAppInfo.launch(0, -1, Shell.AppLaunchGpu.APP_PREF);
+ Main.overview.hide();
+ }
+
+ this.close();
+ }
+});
diff --git a/js/ui/windowAttentionHandler.js b/js/ui/windowAttentionHandler.js
new file mode 100644
index 0000000..8da3049
--- /dev/null
+++ b/js/ui/windowAttentionHandler.js
@@ -0,0 +1,100 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WindowAttentionHandler */
+
+const { GObject, Shell } = imports.gi;
+
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+
+var WindowAttentionHandler = class {
+ constructor() {
+ this._tracker = Shell.WindowTracker.get_default();
+ global.display.connectObject(
+ 'window-demands-attention', this._onWindowDemandsAttention.bind(this),
+ 'window-marked-urgent', this._onWindowDemandsAttention.bind(this),
+ this);
+ }
+
+ _getTitleAndBanner(app, window) {
+ let title = app.get_name();
+ let banner = _("“%s” is ready").format(window.get_title());
+ return [title, banner];
+ }
+
+ _onWindowDemandsAttention(display, window) {
+ // We don't want to show the notification when the window is already focused,
+ // because this is rather pointless.
+ // Some apps (like GIMP) do things like setting the urgency hint on the
+ // toolbar windows which would result into a notification even though GIMP itself is
+ // focused.
+ // We are just ignoring the hint on skip_taskbar windows for now.
+ // (Which is the same behaviour as with metacity + panel)
+
+ if (!window || window.has_focus() || window.is_skip_taskbar())
+ return;
+
+ let app = this._tracker.get_window_app(window);
+ let source = new WindowAttentionSource(app, window);
+ Main.messageTray.add(source);
+
+ let [title, banner] = this._getTitleAndBanner(app, window);
+
+ let notification = new MessageTray.Notification(source, title, banner);
+ notification.connect('activated', () => {
+ source.open();
+ });
+ notification.setForFeedback(true);
+
+ source.showNotification(notification);
+
+ window.connectObject('notify::title', () => {
+ [title, banner] = this._getTitleAndBanner(app, window);
+ notification.update(title, banner);
+ }, source);
+ }
+};
+
+var WindowAttentionSource = GObject.registerClass(
+class WindowAttentionSource extends MessageTray.Source {
+ _init(app, window) {
+ this._window = window;
+ this._app = app;
+
+ super._init(app.get_name());
+
+ this._window.connectObject(
+ 'notify::demands-attention', this._sync.bind(this),
+ 'notify::urgent', this._sync.bind(this),
+ 'focus', () => this.destroy(),
+ 'unmanaged', () => this.destroy(), this);
+ }
+
+ _sync() {
+ if (this._window.demands_attention || this._window.urgent)
+ return;
+ this.destroy();
+ }
+
+ _createPolicy() {
+ if (this._app && this._app.get_app_info()) {
+ let id = this._app.get_id().replace(/\.desktop$/, '');
+ return new MessageTray.NotificationApplicationPolicy(id);
+ } else {
+ return new MessageTray.NotificationGenericPolicy();
+ }
+ }
+
+ createIcon(size) {
+ return this._app.create_icon_texture(size);
+ }
+
+ destroy(params) {
+ this._window.disconnectObject(this);
+
+ super.destroy(params);
+ }
+
+ open() {
+ Main.activateWindow(this._window);
+ }
+});
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
new file mode 100644
index 0000000..d415412
--- /dev/null
+++ b/js/ui/windowManager.js
@@ -0,0 +1,1927 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WindowManager */
+
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+
+const AltTab = imports.ui.altTab;
+const AppFavorites = imports.ui.appFavorites;
+const Dialog = imports.ui.dialog;
+const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
+const InhibitShortcutsDialog = imports.ui.inhibitShortcutsDialog;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const WindowMenu = imports.ui.windowMenu;
+const PadOsd = imports.ui.padOsd;
+const EdgeDragAction = imports.ui.edgeDragAction;
+const CloseDialog = imports.ui.closeDialog;
+const SwitchMonitor = imports.ui.switchMonitor;
+const IBusManager = imports.misc.ibusManager;
+const WorkspaceAnimation = imports.ui.workspaceAnimation;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+var SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
+var MINIMIZE_WINDOW_ANIMATION_TIME = 400;
+var MINIMIZE_WINDOW_ANIMATION_MODE = Clutter.AnimationMode.EASE_OUT_EXPO;
+var SHOW_WINDOW_ANIMATION_TIME = 150;
+var DIALOG_SHOW_WINDOW_ANIMATION_TIME = 100;
+var DESTROY_WINDOW_ANIMATION_TIME = 150;
+var DIALOG_DESTROY_WINDOW_ANIMATION_TIME = 100;
+var WINDOW_ANIMATION_TIME = 250;
+var SCROLL_TIMEOUT_TIME = 150;
+var DIM_BRIGHTNESS = -0.3;
+var DIM_TIME = 500;
+var UNDIM_TIME = 250;
+var APP_MOTION_THRESHOLD = 30;
+
+var ONE_SECOND = 1000; // in ms
+
+var MIN_NUM_WORKSPACES = 2;
+
+const GSD_WACOM_BUS_NAME = 'org.gnome.SettingsDaemon.Wacom';
+const GSD_WACOM_OBJECT_PATH = '/org/gnome/SettingsDaemon/Wacom';
+
+const GsdWacomIface = loadInterfaceXML('org.gnome.SettingsDaemon.Wacom');
+const GsdWacomProxy = Gio.DBusProxy.makeProxyWrapper(GsdWacomIface);
+
+const WINDOW_DIMMER_EFFECT_NAME = "gnome-shell-window-dimmer";
+
+Gio._promisify(Shell, 'util_start_systemd_unit');
+Gio._promisify(Shell, 'util_stop_systemd_unit');
+
+var DisplayChangeDialog = GObject.registerClass(
+class DisplayChangeDialog extends ModalDialog.ModalDialog {
+ _init(wm) {
+ super._init();
+
+ this._wm = wm;
+
+ this._countDown = Meta.MonitorManager.get_display_configuration_timeout();
+
+ // Translators: This string should be shorter than 30 characters
+ let title = _('Keep these display settings?');
+ let description = this._formatCountDown();
+
+ this._content = new Dialog.MessageDialogContent({ title, description });
+ this.contentLayout.add_child(this._content);
+
+ /* Translators: this and the following message should be limited in length,
+ to avoid ellipsizing the labels.
+ */
+ this._cancelButton = this.addButton({
+ label: _('Revert Settings'),
+ action: this._onFailure.bind(this),
+ key: Clutter.KEY_Escape,
+ });
+ this._okButton = this.addButton({
+ label: _('Keep Changes'),
+ action: this._onSuccess.bind(this),
+ default: true,
+ });
+
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, ONE_SECOND, this._tick.bind(this));
+ GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._tick');
+ }
+
+ close(timestamp) {
+ if (this._timeoutId > 0) {
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+ }
+
+ super.close(timestamp);
+ }
+
+ _formatCountDown() {
+ const fmt = ngettext(
+ 'Settings changes will revert in %d second',
+ 'Settings changes will revert in %d seconds',
+ this._countDown);
+ return fmt.format(this._countDown);
+ }
+
+ _tick() {
+ this._countDown--;
+
+ if (this._countDown == 0) {
+ /* mutter already takes care of failing at timeout */
+ this._timeoutId = 0;
+ this.close();
+ return GLib.SOURCE_REMOVE;
+ }
+
+ this._content.description = this._formatCountDown();
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ _onFailure() {
+ this._wm.complete_display_change(false);
+ this.close();
+ }
+
+ _onSuccess() {
+ this._wm.complete_display_change(true);
+ this.close();
+ }
+});
+
+var WindowDimmer = GObject.registerClass(
+class WindowDimmer extends Clutter.BrightnessContrastEffect {
+ _init() {
+ super._init({
+ name: WINDOW_DIMMER_EFFECT_NAME,
+ enabled: false,
+ });
+ }
+
+ _syncEnabled(dimmed) {
+ let animating = this.actor.get_transition(`@effects.${this.name}.brightness`) !== null;
+
+ this.enabled = Meta.prefs_get_attach_modal_dialogs() && (animating || dimmed);
+ }
+
+ setDimmed(dimmed, animate) {
+ let val = 127 * (1 + (dimmed ? 1 : 0) * DIM_BRIGHTNESS);
+ let color = Clutter.Color.new(val, val, val, 255);
+
+ this.actor.ease_property(`@effects.${this.name}.brightness`, color, {
+ mode: Clutter.AnimationMode.LINEAR,
+ duration: (dimmed ? DIM_TIME : UNDIM_TIME) * (animate ? 1 : 0),
+ onStopped: () => this._syncEnabled(dimmed),
+ });
+
+ this._syncEnabled(dimmed);
+ }
+});
+
+function getWindowDimmer(actor) {
+ let effect = actor.get_effect(WINDOW_DIMMER_EFFECT_NAME);
+
+ if (!effect) {
+ effect = new WindowDimmer();
+ actor.add_effect(effect);
+ }
+ return effect;
+}
+
+/*
+ * When the last window closed on a workspace is a dialog or splash
+ * screen, we assume that it might be an initial window shown before
+ * the main window of an application, and give the app a grace period
+ * where it can map another window before we remove the workspace.
+ */
+var LAST_WINDOW_GRACE_TIME = 1000;
+
+var WorkspaceTracker = class {
+ constructor(wm) {
+ this._wm = wm;
+
+ this._workspaces = [];
+ this._checkWorkspacesId = 0;
+
+ this._pauseWorkspaceCheck = false;
+
+ let tracker = Shell.WindowTracker.get_default();
+ tracker.connect('startup-sequence-changed', this._queueCheckWorkspaces.bind(this));
+
+ let workspaceManager = global.workspace_manager;
+ workspaceManager.connect('notify::n-workspaces',
+ this._nWorkspacesChanged.bind(this));
+ workspaceManager.connect('workspaces-reordered', () => {
+ this._workspaces.sort((a, b) => a.index() - b.index());
+ });
+ global.window_manager.connect('switch-workspace',
+ this._queueCheckWorkspaces.bind(this));
+
+ global.display.connect('window-entered-monitor',
+ this._windowEnteredMonitor.bind(this));
+ global.display.connect('window-left-monitor',
+ this._windowLeftMonitor.bind(this));
+
+ this._workspaceSettings = new Gio.Settings({ schema_id: 'org.gnome.mutter' });
+ this._workspaceSettings.connect('changed::dynamic-workspaces', this._queueCheckWorkspaces.bind(this));
+
+ this._nWorkspacesChanged();
+ }
+
+ blockUpdates() {
+ this._pauseWorkspaceCheck = true;
+ }
+
+ unblockUpdates() {
+ this._pauseWorkspaceCheck = false;
+ }
+
+ _checkWorkspaces() {
+ let workspaceManager = global.workspace_manager;
+ let i;
+ let emptyWorkspaces = [];
+
+ if (!Meta.prefs_get_dynamic_workspaces()) {
+ this._checkWorkspacesId = 0;
+ return false;
+ }
+
+ // Update workspaces only if Dynamic Workspace Management has not been paused by some other function
+ if (this._pauseWorkspaceCheck)
+ return true;
+
+ for (i = 0; i < this._workspaces.length; i++) {
+ let lastRemoved = this._workspaces[i]._lastRemovedWindow;
+ if ((lastRemoved &&
+ (lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
+ lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
+ lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
+ this._workspaces[i]._keepAliveId)
+ emptyWorkspaces[i] = false;
+ else
+ emptyWorkspaces[i] = true;
+ }
+
+ let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
+ for (i = 0; i < sequences.length; i++) {
+ let index = sequences[i].get_workspace();
+ if (index >= 0 && index <= workspaceManager.n_workspaces)
+ emptyWorkspaces[index] = false;
+ }
+
+ let windows = global.get_window_actors();
+ for (i = 0; i < windows.length; i++) {
+ let actor = windows[i];
+ let win = actor.get_meta_window();
+
+ if (win.is_on_all_workspaces())
+ continue;
+
+ let workspaceIndex = win.get_workspace().index();
+ emptyWorkspaces[workspaceIndex] = false;
+ }
+
+ // If we don't have an empty workspace at the end, add one
+ if (!emptyWorkspaces[emptyWorkspaces.length - 1]) {
+ workspaceManager.append_new_workspace(false, global.get_current_time());
+ emptyWorkspaces.push(true);
+ }
+
+ // Enforce minimum number of workspaces
+ while (emptyWorkspaces.length < MIN_NUM_WORKSPACES) {
+ workspaceManager.append_new_workspace(false, global.get_current_time());
+ emptyWorkspaces.push(true);
+ }
+
+ let lastIndex = emptyWorkspaces.length - 1;
+ let lastEmptyIndex = emptyWorkspaces.lastIndexOf(false) + 1;
+ let activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
+ emptyWorkspaces[activeWorkspaceIndex] = false;
+
+ // Delete empty workspaces except for the last one; do it from the end
+ // to avoid index changes
+ for (i = lastIndex; i >= 0; i--) {
+ if (workspaceManager.n_workspaces === MIN_NUM_WORKSPACES)
+ break;
+ if (emptyWorkspaces[i] && i != lastEmptyIndex)
+ workspaceManager.remove_workspace(this._workspaces[i], global.get_current_time());
+ }
+
+ this._checkWorkspacesId = 0;
+ return false;
+ }
+
+ keepWorkspaceAlive(workspace, duration) {
+ if (workspace._keepAliveId)
+ GLib.source_remove(workspace._keepAliveId);
+
+ workspace._keepAliveId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, duration, () => {
+ workspace._keepAliveId = 0;
+ this._queueCheckWorkspaces();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(workspace._keepAliveId, '[gnome-shell] this._queueCheckWorkspaces');
+ }
+
+ _windowRemoved(workspace, window) {
+ workspace._lastRemovedWindow = window;
+ this._queueCheckWorkspaces();
+ let id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, LAST_WINDOW_GRACE_TIME, () => {
+ if (workspace._lastRemovedWindow == window) {
+ workspace._lastRemovedWindow = null;
+ this._queueCheckWorkspaces();
+ }
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] this._queueCheckWorkspaces');
+ }
+
+ _windowLeftMonitor(metaDisplay, monitorIndex, _metaWin) {
+ // If the window left the primary monitor, that
+ // might make that workspace empty
+ if (monitorIndex == Main.layoutManager.primaryIndex)
+ this._queueCheckWorkspaces();
+ }
+
+ _windowEnteredMonitor(metaDisplay, monitorIndex, _metaWin) {
+ // If the window entered the primary monitor, that
+ // might make that workspace non-empty
+ if (monitorIndex == Main.layoutManager.primaryIndex)
+ this._queueCheckWorkspaces();
+ }
+
+ _queueCheckWorkspaces() {
+ if (this._checkWorkspacesId == 0)
+ this._checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, this._checkWorkspaces.bind(this));
+ }
+
+ _nWorkspacesChanged() {
+ let workspaceManager = global.workspace_manager;
+ let oldNumWorkspaces = this._workspaces.length;
+ let newNumWorkspaces = workspaceManager.n_workspaces;
+
+ if (oldNumWorkspaces == newNumWorkspaces)
+ return false;
+
+ if (newNumWorkspaces > oldNumWorkspaces) {
+ let w;
+
+ // Assume workspaces are only added at the end
+ for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
+ this._workspaces[w] = workspaceManager.get_workspace_by_index(w);
+
+ for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
+ this._workspaces[w].connectObject(
+ 'window-added', this._queueCheckWorkspaces.bind(this),
+ 'window-removed', this._windowRemoved.bind(this), this);
+ }
+ } else {
+ // Assume workspaces are only removed sequentially
+ // (e.g. 2,3,4 - not 2,4,7)
+ let removedIndex;
+ let removedNum = oldNumWorkspaces - newNumWorkspaces;
+ for (let w = 0; w < oldNumWorkspaces; w++) {
+ let workspace = workspaceManager.get_workspace_by_index(w);
+ if (this._workspaces[w] != workspace) {
+ removedIndex = w;
+ break;
+ }
+ }
+
+ let lostWorkspaces = this._workspaces.splice(removedIndex, removedNum);
+ lostWorkspaces.forEach(workspace => workspace.disconnectObject(this));
+ }
+
+ this._queueCheckWorkspaces();
+
+ return false;
+ }
+};
+
+var TilePreview = GObject.registerClass(
+class TilePreview extends St.Widget {
+ _init() {
+ super._init();
+ global.window_group.add_actor(this);
+
+ this._reset();
+ this._showing = false;
+ }
+
+ open(window, tileRect, monitorIndex) {
+ let windowActor = window.get_compositor_private();
+ if (!windowActor)
+ return;
+
+ global.window_group.set_child_below_sibling(this, windowActor);
+
+ if (this._rect && this._rect.equal(tileRect))
+ return;
+
+ let changeMonitor = this._monitorIndex == -1 ||
+ this._monitorIndex != monitorIndex;
+
+ this._monitorIndex = monitorIndex;
+ this._rect = tileRect;
+
+ let monitor = Main.layoutManager.monitors[monitorIndex];
+
+ this._updateStyle(monitor);
+
+ if (!this._showing || changeMonitor) {
+ const monitorRect = new Meta.Rectangle({
+ x: monitor.x,
+ y: monitor.y,
+ width: monitor.width,
+ height: monitor.height,
+ });
+ let [, rect] = window.get_frame_rect().intersect(monitorRect);
+ this.set_size(rect.width, rect.height);
+ this.set_position(rect.x, rect.y);
+ this.opacity = 0;
+ }
+
+ this._showing = true;
+ this.show();
+ this.ease({
+ x: tileRect.x,
+ y: tileRect.y,
+ width: tileRect.width,
+ height: tileRect.height,
+ opacity: 255,
+ duration: WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ close() {
+ if (!this._showing)
+ return;
+
+ this._showing = false;
+ this.ease({
+ opacity: 0,
+ duration: WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this._reset(),
+ });
+ }
+
+ _reset() {
+ this.hide();
+ this._rect = null;
+ this._monitorIndex = -1;
+ }
+
+ _updateStyle(monitor) {
+ let styles = ['tile-preview'];
+ if (this._monitorIndex == Main.layoutManager.primaryIndex)
+ styles.push('on-primary');
+ if (this._rect.x == monitor.x)
+ styles.push('tile-preview-left');
+ if (this._rect.x + this._rect.width == monitor.x + monitor.width)
+ styles.push('tile-preview-right');
+
+ this.style_class = styles.join(' ');
+ }
+});
+
+var AppSwitchAction = GObject.registerClass({
+ Signals: { 'activated': {} },
+}, class AppSwitchAction extends Clutter.GestureAction {
+ _init() {
+ super._init();
+ this.set_n_touch_points(3);
+
+ global.display.connect('grab-op-begin', () => {
+ this.cancel();
+ });
+ }
+
+ vfunc_gesture_prepare(_actor) {
+ if (Main.actionMode != Shell.ActionMode.NORMAL) {
+ this.cancel();
+ return false;
+ }
+
+ return this.get_n_current_points() <= 4;
+ }
+
+ vfunc_gesture_begin(_actor) {
+ // in milliseconds
+ const LONG_PRESS_TIMEOUT = 250;
+
+ let nPoints = this.get_n_current_points();
+ let event = this.get_last_event(nPoints - 1);
+
+ if (nPoints == 3) {
+ this._longPressStartTime = event.get_time();
+ } else if (nPoints == 4) {
+ // Check whether the 4th finger press happens after a 3-finger long press,
+ // this only needs to be checked on the first 4th finger press
+ if (this._longPressStartTime != null &&
+ event.get_time() < this._longPressStartTime + LONG_PRESS_TIMEOUT) {
+ this.cancel();
+ } else {
+ this._longPressStartTime = null;
+ this.emit('activated');
+ }
+ }
+
+ return this.get_n_current_points() <= 4;
+ }
+
+ vfunc_gesture_progress(_actor) {
+ if (this.get_n_current_points() == 3) {
+ for (let i = 0; i < this.get_n_current_points(); i++) {
+ let [startX, startY] = this.get_press_coords(i);
+ let [x, y] = this.get_motion_coords(i);
+
+ if (Math.abs(x - startX) > APP_MOTION_THRESHOLD ||
+ Math.abs(y - startY) > APP_MOTION_THRESHOLD)
+ return false;
+ }
+ }
+
+ return true;
+ }
+});
+
+var ResizePopup = GObject.registerClass(
+class ResizePopup extends St.Widget {
+ _init() {
+ super._init({ layout_manager: new Clutter.BinLayout() });
+ this._label = new St.Label({
+ style_class: 'resize-popup',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ x_expand: true,
+ y_expand: true,
+ });
+ this.add_child(this._label);
+ Main.uiGroup.add_actor(this);
+ }
+
+ set(rect, displayW, displayH) {
+ /* Translators: This represents the size of a window. The first number is
+ * the width of the window and the second is the height. */
+ let text = _("%d × %d").format(displayW, displayH);
+ this._label.set_text(text);
+
+ this.set_position(rect.x, rect.y);
+ this.set_size(rect.width, rect.height);
+ }
+});
+
+var WindowManager = class {
+ constructor() {
+ this._shellwm = global.window_manager;
+
+ this._minimizing = new Set();
+ this._unminimizing = new Set();
+ this._mapping = new Set();
+ this._resizing = new Set();
+ this._resizePending = new Set();
+ this._destroying = new Set();
+
+ this._dimmedWindows = [];
+
+ this._skippedActors = new Set();
+
+ this._allowedKeybindings = {};
+
+ this._isWorkspacePrepended = false;
+ this._canScroll = true; // limiting scrolling speed
+
+ this._shellwm.connect('kill-window-effects', (shellwm, actor) => {
+ this._minimizeWindowDone(shellwm, actor);
+ this._mapWindowDone(shellwm, actor);
+ this._destroyWindowDone(shellwm, actor);
+ this._sizeChangeWindowDone(shellwm, actor);
+ });
+
+ this._shellwm.connect('switch-workspace', this._switchWorkspace.bind(this));
+ this._shellwm.connect('show-tile-preview', this._showTilePreview.bind(this));
+ this._shellwm.connect('hide-tile-preview', this._hideTilePreview.bind(this));
+ this._shellwm.connect('show-window-menu', this._showWindowMenu.bind(this));
+ this._shellwm.connect('minimize', this._minimizeWindow.bind(this));
+ this._shellwm.connect('unminimize', this._unminimizeWindow.bind(this));
+ this._shellwm.connect('size-change', this._sizeChangeWindow.bind(this));
+ this._shellwm.connect('size-changed', this._sizeChangedWindow.bind(this));
+ this._shellwm.connect('map', this._mapWindow.bind(this));
+ this._shellwm.connect('destroy', this._destroyWindow.bind(this));
+ this._shellwm.connect('filter-keybinding', this._filterKeybinding.bind(this));
+ this._shellwm.connect('confirm-display-change', this._confirmDisplayChange.bind(this));
+ this._shellwm.connect('create-close-dialog', this._createCloseDialog.bind(this));
+ this._shellwm.connect('create-inhibit-shortcuts-dialog', this._createInhibitShortcutsDialog.bind(this));
+
+ this._workspaceSwitcherPopup = null;
+ this._tilePreview = null;
+
+ this.allowKeybinding('switch-to-session-1', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-2', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-3', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-4', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-5', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-6', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-7', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-8', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-9', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-10', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-11', Shell.ActionMode.ALL);
+ this.allowKeybinding('switch-to-session-12', Shell.ActionMode.ALL);
+
+ this.setCustomKeybindingHandler('switch-to-workspace-left',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-right',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-up',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-down',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-last',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-left',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-right',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-up',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-down',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-1',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-2',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-3',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-4',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-5',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-6',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-7',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-8',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-9',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-10',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-11',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-to-workspace-12',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-1',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-2',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-3',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-4',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-5',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-6',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-7',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-8',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-9',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-10',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-11',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-12',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('move-to-workspace-last',
+ Shell.ActionMode.NORMAL,
+ this._showWorkspaceSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-applications',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-group',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-applications-backward',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-group-backward',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-windows',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-windows-backward',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('cycle-windows',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('cycle-windows-backward',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('cycle-group',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('cycle-group-backward',
+ Shell.ActionMode.NORMAL,
+ this._startSwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-panels',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW |
+ Shell.ActionMode.LOCK_SCREEN |
+ Shell.ActionMode.UNLOCK_SCREEN |
+ Shell.ActionMode.LOGIN_SCREEN,
+ this._startA11ySwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-panels-backward',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW |
+ Shell.ActionMode.LOCK_SCREEN |
+ Shell.ActionMode.UNLOCK_SCREEN |
+ Shell.ActionMode.LOGIN_SCREEN,
+ this._startA11ySwitcher.bind(this));
+ this.setCustomKeybindingHandler('switch-monitor',
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._startSwitcher.bind(this));
+
+ this.addKeybinding('open-application-menu',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.POPUP,
+ this._toggleAppMenu.bind(this));
+
+ this.addKeybinding('toggle-message-tray',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW |
+ Shell.ActionMode.POPUP,
+ this._toggleCalendar.bind(this));
+
+ this.addKeybinding('switch-to-application-1',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-2',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-3',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-4',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-5',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-6',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-7',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-8',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ this.addKeybinding('switch-to-application-9',
+ new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL |
+ Shell.ActionMode.OVERVIEW,
+ this._switchToApplication.bind(this));
+
+ global.stage.connect('scroll-event', (stage, event) => {
+ const allowedModes = Shell.ActionMode.NORMAL;
+ if ((allowedModes & Main.actionMode) === 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._workspaceAnimation.canHandleScrollEvent(event))
+ return Clutter.EVENT_PROPAGATE;
+
+ if ((event.get_state() & global.display.compositor_modifiers) === 0)
+ return Clutter.EVENT_PROPAGATE;
+
+ return this.handleWorkspaceScroll(event);
+ });
+
+ global.display.connect('show-resize-popup', this._showResizePopup.bind(this));
+ global.display.connect('show-pad-osd', this._showPadOsd.bind(this));
+ global.display.connect('show-osd', (display, monitorIndex, iconName, label) => {
+ let icon = Gio.Icon.new_for_string(iconName);
+ Main.osdWindowManager.show(monitorIndex, icon, label, null);
+ });
+
+ this._gsdWacomProxy = new GsdWacomProxy(Gio.DBus.session, GSD_WACOM_BUS_NAME,
+ GSD_WACOM_OBJECT_PATH,
+ (proxy, error) => {
+ if (error)
+ log(error.message);
+ });
+
+ global.display.connect('pad-mode-switch', (display, pad, _group, _mode) => {
+ let labels = [];
+
+ // FIXME: Fix num buttons
+ for (let i = 0; i < 50; i++) {
+ let str = display.get_pad_action_label(pad, Meta.PadActionType.BUTTON, i);
+ labels.push(str ?? '');
+ }
+
+ this._gsdWacomProxy?.SetOLEDLabelsAsync(
+ pad.get_device_node(), labels).catch(logError);
+ });
+
+ global.display.connect('init-xserver', (display, task) => {
+ IBusManager.getIBusManager().restartDaemon(['--xim']);
+
+ this._startX11Services(task);
+
+ return true;
+ });
+ global.display.connect('x11-display-closing', () => {
+ if (!Meta.is_wayland_compositor())
+ return;
+
+ this._stopX11Services(null);
+
+ IBusManager.getIBusManager().restartDaemon();
+ });
+
+ Main.overview.connect('showing', () => {
+ for (let i = 0; i < this._dimmedWindows.length; i++)
+ this._undimWindow(this._dimmedWindows[i]);
+ });
+ Main.overview.connect('hiding', () => {
+ for (let i = 0; i < this._dimmedWindows.length; i++)
+ this._dimWindow(this._dimmedWindows[i]);
+ });
+
+ this._windowMenuManager = new WindowMenu.WindowMenuManager();
+
+ if (Main.sessionMode.hasWorkspaces)
+ this._workspaceTracker = new WorkspaceTracker(this);
+
+ let appSwitchAction = new AppSwitchAction();
+ appSwitchAction.connect('activated', this._switchApp.bind(this));
+ global.stage.add_action_full('app-switch', Clutter.EventPhase.CAPTURE, appSwitchAction);
+
+ let mode = Shell.ActionMode.NORMAL;
+ let topDragAction = new EdgeDragAction.EdgeDragAction(St.Side.TOP, mode);
+ topDragAction.connect('activated', () => {
+ let currentWindow = global.display.focus_window;
+ if (currentWindow)
+ currentWindow.unmake_fullscreen();
+ });
+
+ let updateUnfullscreenGesture = () => {
+ let currentWindow = global.display.focus_window;
+ topDragAction.enabled = currentWindow && currentWindow.is_fullscreen();
+ };
+
+ global.display.connect('notify::focus-window', updateUnfullscreenGesture);
+ global.display.connect('in-fullscreen-changed', updateUnfullscreenGesture);
+ updateUnfullscreenGesture();
+
+ global.stage.add_action_full('unfullscreen', Clutter.EventPhase.CAPTURE, topDragAction);
+
+ this._workspaceAnimation =
+ new WorkspaceAnimation.WorkspaceAnimationController();
+
+ this._shellwm.connect('kill-switch-workspace', () => {
+ this._workspaceAnimation.cancelSwitchAnimation();
+ this._switchWorkspaceDone();
+ });
+ }
+
+ async _startX11Services(task) {
+ let status = true;
+ try {
+ await Shell.util_start_systemd_unit(
+ 'gnome-session-x11-services-ready.target', 'fail', null);
+ } catch (e) {
+ // Ignore NOT_SUPPORTED error, which indicates we are not systemd
+ // managed and gnome-session will have taken care of everything
+ // already.
+ // Note that we do log cancellation from here.
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_SUPPORTED)) {
+ log(`Error starting X11 services: ${e.message}`);
+ status = false;
+ }
+ } finally {
+ task.return_boolean(status);
+ }
+ }
+
+ async _stopX11Services(cancellable) {
+ try {
+ await Shell.util_stop_systemd_unit(
+ 'gnome-session-x11-services.target', 'fail', cancellable);
+ } catch (e) {
+ // Ignore NOT_SUPPORTED error, which indicates we are not systemd
+ // managed and gnome-session will have taken care of everything
+ // already.
+ // Note that we do log cancellation from here.
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_SUPPORTED))
+ log(`Error stopping X11 services: ${e.message}`);
+ }
+ }
+
+ _showPadOsd(display, device, settings, imagePath, editionMode, monitorIndex) {
+ this._currentPadOsd = new PadOsd.PadOsd(device, settings, imagePath, editionMode, monitorIndex);
+ this._currentPadOsd.connect('closed', () => (this._currentPadOsd = null));
+
+ return this._currentPadOsd;
+ }
+
+ _lookupIndex(windows, metaWindow) {
+ for (let i = 0; i < windows.length; i++) {
+ if (windows[i].metaWindow == metaWindow)
+ return i;
+ }
+ return -1;
+ }
+
+ _switchApp() {
+ let windows = global.get_window_actors().filter(actor => {
+ let win = actor.metaWindow;
+ let workspaceManager = global.workspace_manager;
+ let activeWorkspace = workspaceManager.get_active_workspace();
+ return !win.is_override_redirect() &&
+ win.located_on_workspace(activeWorkspace);
+ });
+
+ if (windows.length == 0)
+ return;
+
+ let focusWindow = global.display.focus_window;
+ let nextWindow;
+
+ if (focusWindow == null) {
+ nextWindow = windows[0].metaWindow;
+ } else {
+ let index = this._lookupIndex(windows, focusWindow) + 1;
+
+ if (index >= windows.length)
+ index = 0;
+
+ nextWindow = windows[index].metaWindow;
+ }
+
+ Main.activateWindow(nextWindow);
+ }
+
+ insertWorkspace(pos) {
+ let workspaceManager = global.workspace_manager;
+
+ if (!Meta.prefs_get_dynamic_workspaces())
+ return;
+
+ workspaceManager.append_new_workspace(false, global.get_current_time());
+
+ let windows = global.get_window_actors().map(a => a.meta_window);
+
+ // To create a new workspace, we slide all the windows on workspaces
+ // below us to the next workspace, leaving a blank workspace for us
+ // to recycle.
+ windows.forEach(window => {
+ // If the window is attached to an ancestor, we don't need/want
+ // to move it
+ if (window.get_transient_for() != null)
+ return;
+ // Same for OR windows
+ if (window.is_override_redirect())
+ return;
+ // Sticky windows don't need moving, in fact moving would
+ // unstick them
+ if (window.on_all_workspaces)
+ return;
+ // Windows on workspaces below pos don't need moving
+ let index = window.get_workspace().index();
+ if (index < pos)
+ return;
+ window.change_workspace_by_index(index + 1, true);
+ });
+
+ // If the new workspace was inserted before the active workspace,
+ // activate the workspace to which its windows went
+ let activeIndex = workspaceManager.get_active_workspace_index();
+ if (activeIndex >= pos) {
+ let newWs = workspaceManager.get_workspace_by_index(activeIndex + 1);
+ this._blockAnimations = true;
+ newWs.activate(global.get_current_time());
+ this._blockAnimations = false;
+ }
+ }
+
+ keepWorkspaceAlive(workspace, duration) {
+ if (!this._workspaceTracker)
+ return;
+
+ this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
+ }
+
+ skipNextEffect(actor) {
+ this._skippedActors.add(actor);
+ }
+
+ setCustomKeybindingHandler(name, modes, handler) {
+ if (Meta.keybindings_set_custom_handler(name, handler))
+ this.allowKeybinding(name, modes);
+ }
+
+ addKeybinding(name, settings, flags, modes, handler) {
+ let action = global.display.add_keybinding(name, settings, flags, handler);
+ if (action != Meta.KeyBindingAction.NONE)
+ this.allowKeybinding(name, modes);
+ return action;
+ }
+
+ removeKeybinding(name) {
+ if (global.display.remove_keybinding(name))
+ this.allowKeybinding(name, Shell.ActionMode.NONE);
+ }
+
+ allowKeybinding(name, modes) {
+ this._allowedKeybindings[name] = modes;
+ }
+
+ _shouldAnimate() {
+ const overviewOpen = Main.overview.visible && !Main.overview.closing;
+ return !(overviewOpen || this._workspaceAnimation.gestureActive);
+ }
+
+ _shouldAnimateActor(actor, types) {
+ if (this._skippedActors.delete(actor))
+ return false;
+
+ if (!this._shouldAnimate())
+ return false;
+
+ if (!actor.get_texture())
+ return false;
+
+ let type = actor.meta_window.get_window_type();
+ return types.includes(type);
+ }
+
+ _minimizeWindow(shellwm, actor) {
+ const types = [
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.MODAL_DIALOG,
+ Meta.WindowType.DIALOG,
+ ];
+ if (!this._shouldAnimateActor(actor, types)) {
+ shellwm.completed_minimize(actor);
+ return;
+ }
+
+ actor.set_scale(1.0, 1.0);
+
+ this._minimizing.add(actor);
+
+ if (actor.meta_window.is_monitor_sized()) {
+ actor.ease({
+ opacity: 0,
+ duration: MINIMIZE_WINDOW_ANIMATION_TIME,
+ mode: MINIMIZE_WINDOW_ANIMATION_MODE,
+ onStopped: () => this._minimizeWindowDone(shellwm, actor),
+ });
+ } else {
+ let xDest, yDest, xScale, yScale;
+ let [success, geom] = actor.meta_window.get_icon_geometry();
+ if (success) {
+ xDest = geom.x;
+ yDest = geom.y;
+ xScale = geom.width / actor.width;
+ yScale = geom.height / actor.height;
+ } else {
+ let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
+ if (!monitor) {
+ this._minimizeWindowDone();
+ return;
+ }
+ xDest = monitor.x;
+ yDest = monitor.y;
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+ xDest += monitor.width;
+ xScale = 0;
+ yScale = 0;
+ }
+
+ actor.ease({
+ scale_x: xScale,
+ scale_y: yScale,
+ x: xDest,
+ y: yDest,
+ duration: MINIMIZE_WINDOW_ANIMATION_TIME,
+ mode: MINIMIZE_WINDOW_ANIMATION_MODE,
+ onStopped: () => this._minimizeWindowDone(shellwm, actor),
+ });
+ }
+ }
+
+ _minimizeWindowDone(shellwm, actor) {
+ if (this._minimizing.delete(actor)) {
+ actor.remove_all_transitions();
+ actor.set_scale(1.0, 1.0);
+ actor.set_opacity(255);
+ actor.set_pivot_point(0, 0);
+
+ shellwm.completed_minimize(actor);
+ }
+ }
+
+ _unminimizeWindow(shellwm, actor) {
+ const types = [
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.MODAL_DIALOG,
+ Meta.WindowType.DIALOG,
+ ];
+ if (!this._shouldAnimateActor(actor, types)) {
+ shellwm.completed_unminimize(actor);
+ return;
+ }
+
+ this._unminimizing.add(actor);
+
+ if (actor.meta_window.is_monitor_sized()) {
+ actor.opacity = 0;
+ actor.set_scale(1.0, 1.0);
+ actor.ease({
+ opacity: 255,
+ duration: MINIMIZE_WINDOW_ANIMATION_TIME,
+ mode: MINIMIZE_WINDOW_ANIMATION_MODE,
+ onStopped: () => this._unminimizeWindowDone(shellwm, actor),
+ });
+ } else {
+ let [success, geom] = actor.meta_window.get_icon_geometry();
+ if (success) {
+ actor.set_position(geom.x, geom.y);
+ actor.set_scale(geom.width / actor.width,
+ geom.height / actor.height);
+ } else {
+ let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
+ if (!monitor) {
+ actor.show();
+ this._unminimizeWindowDone();
+ return;
+ }
+ actor.set_position(monitor.x, monitor.y);
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+ actor.x += monitor.width;
+ actor.set_scale(0, 0);
+ }
+
+ let rect = actor.meta_window.get_buffer_rect();
+ let [xDest, yDest] = [rect.x, rect.y];
+
+ actor.show();
+ actor.ease({
+ scale_x: 1,
+ scale_y: 1,
+ x: xDest,
+ y: yDest,
+ duration: MINIMIZE_WINDOW_ANIMATION_TIME,
+ mode: MINIMIZE_WINDOW_ANIMATION_MODE,
+ onStopped: () => this._unminimizeWindowDone(shellwm, actor),
+ });
+ }
+ }
+
+ _unminimizeWindowDone(shellwm, actor) {
+ if (this._unminimizing.delete(actor)) {
+ actor.remove_all_transitions();
+ actor.set_scale(1.0, 1.0);
+ actor.set_opacity(255);
+ actor.set_pivot_point(0, 0);
+
+ shellwm.completed_unminimize(actor);
+ }
+ }
+
+ _sizeChangeWindow(shellwm, actor, whichChange, oldFrameRect, _oldBufferRect) {
+ const types = [Meta.WindowType.NORMAL];
+ const shouldAnimate =
+ this._shouldAnimateActor(actor, types) &&
+ oldFrameRect.width > 0 &&
+ oldFrameRect.height > 0;
+
+ if (shouldAnimate)
+ this._prepareAnimationInfo(shellwm, actor, oldFrameRect, whichChange);
+ else
+ shellwm.completed_size_change(actor);
+ }
+
+ _prepareAnimationInfo(shellwm, actor, oldFrameRect, _change) {
+ // Position a clone of the window on top of the old position,
+ // while actor updates are frozen.
+ let actorContent = actor.paint_to_content(oldFrameRect);
+ let actorClone = new St.Widget({ content: actorContent });
+ actorClone.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
+ actorClone.set_position(oldFrameRect.x, oldFrameRect.y);
+ actorClone.set_size(oldFrameRect.width, oldFrameRect.height);
+
+ actor.freeze();
+
+ if (this._clearAnimationInfo(actor)) {
+ log(`Old animationInfo removed from actor ${actor}`);
+ this._shellwm.completed_size_change(actor);
+ }
+
+ actor.connectObject('destroy',
+ () => this._clearAnimationInfo(actor), actorClone);
+
+ this._resizePending.add(actor);
+ actor.__animationInfo = {
+ clone: actorClone,
+ oldRect: oldFrameRect,
+ frozen: true,
+ };
+ }
+
+ _sizeChangedWindow(shellwm, actor) {
+ if (!actor.__animationInfo)
+ return;
+ if (this._resizing.has(actor))
+ return;
+
+ let actorClone = actor.__animationInfo.clone;
+ let targetRect = actor.meta_window.get_frame_rect();
+ let sourceRect = actor.__animationInfo.oldRect;
+
+ let scaleX = targetRect.width / sourceRect.width;
+ let scaleY = targetRect.height / sourceRect.height;
+
+ this._resizePending.delete(actor);
+ this._resizing.add(actor);
+
+ Main.uiGroup.add_child(actorClone);
+
+ // Now scale and fade out the clone
+ actorClone.ease({
+ x: targetRect.x,
+ y: targetRect.y,
+ scale_x: scaleX,
+ scale_y: scaleY,
+ opacity: 0,
+ duration: WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ actor.translation_x = -targetRect.x + sourceRect.x;
+ actor.translation_y = -targetRect.y + sourceRect.y;
+
+ // Now set scale the actor to size it as the clone.
+ actor.scale_x = 1 / scaleX;
+ actor.scale_y = 1 / scaleY;
+
+ // Scale it to its actual new size
+ actor.ease({
+ scale_x: 1,
+ scale_y: 1,
+ translation_x: 0,
+ translation_y: 0,
+ duration: WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this._sizeChangeWindowDone(shellwm, actor),
+ });
+
+ // ease didn't animate and cleared the info, we are done
+ if (!actor.__animationInfo)
+ return;
+
+ // Now unfreeze actor updates, to get it to the new size.
+ // It's important that we don't wait until the animation is completed to
+ // do this, otherwise our scale will be applied to the old texture size.
+ actor.thaw();
+ actor.__animationInfo.frozen = false;
+ }
+
+ _clearAnimationInfo(actor) {
+ if (actor.__animationInfo) {
+ actor.__animationInfo.clone.destroy();
+ if (actor.__animationInfo.frozen)
+ actor.thaw();
+
+ delete actor.__animationInfo;
+ return true;
+ }
+ return false;
+ }
+
+ _sizeChangeWindowDone(shellwm, actor) {
+ if (this._resizing.delete(actor)) {
+ actor.remove_all_transitions();
+ actor.scale_x = 1.0;
+ actor.scale_y = 1.0;
+ actor.translation_x = 0;
+ actor.translation_y = 0;
+ this._clearAnimationInfo(actor);
+ this._shellwm.completed_size_change(actor);
+ }
+
+ if (this._resizePending.delete(actor)) {
+ this._clearAnimationInfo(actor);
+ this._shellwm.completed_size_change(actor);
+ }
+ }
+
+ _checkDimming(window) {
+ const shouldDim = window.has_attached_dialogs();
+
+ if (shouldDim && !window._dimmed) {
+ window._dimmed = true;
+ this._dimmedWindows.push(window);
+ this._dimWindow(window);
+ } else if (!shouldDim && window._dimmed) {
+ window._dimmed = false;
+ this._dimmedWindows =
+ this._dimmedWindows.filter(win => win != window);
+ this._undimWindow(window);
+ }
+ }
+
+ _dimWindow(window) {
+ let actor = window.get_compositor_private();
+ if (!actor)
+ return;
+ let dimmer = getWindowDimmer(actor);
+ if (!dimmer)
+ return;
+ dimmer.setDimmed(true, this._shouldAnimate());
+ }
+
+ _undimWindow(window) {
+ let actor = window.get_compositor_private();
+ if (!actor)
+ return;
+ let dimmer = getWindowDimmer(actor);
+ if (!dimmer)
+ return;
+ dimmer.setDimmed(false, this._shouldAnimate());
+ }
+
+ _waitForOverviewToHide() {
+ if (!Main.overview.visible)
+ return Promise.resolve();
+
+ return new Promise(resolve => {
+ const id = Main.overview.connect('hidden', () => {
+ Main.overview.disconnect(id);
+ resolve();
+ });
+ });
+ }
+
+ async _mapWindow(shellwm, actor) {
+ actor._windowType = actor.meta_window.get_window_type();
+ actor.meta_window.connectObject('notify::window-type', () => {
+ let type = actor.meta_window.get_window_type();
+ if (type === actor._windowType)
+ return;
+ if (type === Meta.WindowType.MODAL_DIALOG ||
+ actor._windowType === Meta.WindowType.MODAL_DIALOG) {
+ let parent = actor.get_meta_window().get_transient_for();
+ if (parent)
+ this._checkDimming(parent);
+ }
+
+ actor._windowType = type;
+ }, actor);
+ actor.meta_window.connect('unmanaged', window => {
+ let parent = window.get_transient_for();
+ if (parent)
+ this._checkDimming(parent);
+ });
+
+ if (actor.meta_window.is_attached_dialog())
+ this._checkDimming(actor.get_meta_window().get_transient_for());
+
+ const types = [
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.DIALOG,
+ Meta.WindowType.MODAL_DIALOG,
+ ];
+ if (!this._shouldAnimateActor(actor, types)) {
+ shellwm.completed_map(actor);
+ return;
+ }
+
+ switch (actor._windowType) {
+ case Meta.WindowType.NORMAL:
+ actor.set_pivot_point(0.5, 1.0);
+ actor.scale_x = 0.01;
+ actor.scale_y = 0.05;
+ actor.opacity = 0;
+ actor.show();
+ this._mapping.add(actor);
+
+ await this._waitForOverviewToHide();
+ actor.ease({
+ opacity: 255,
+ scale_x: 1,
+ scale_y: 1,
+ duration: SHOW_WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_EXPO,
+ onStopped: () => this._mapWindowDone(shellwm, actor),
+ });
+ break;
+ case Meta.WindowType.MODAL_DIALOG:
+ case Meta.WindowType.DIALOG:
+ actor.set_pivot_point(0.5, 0.5);
+ actor.scale_y = 0;
+ actor.opacity = 0;
+ actor.show();
+ this._mapping.add(actor);
+
+ await this._waitForOverviewToHide();
+ actor.ease({
+ opacity: 255,
+ scale_x: 1,
+ scale_y: 1,
+ duration: DIALOG_SHOW_WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this._mapWindowDone(shellwm, actor),
+ });
+ break;
+ default:
+ shellwm.completed_map(actor);
+ }
+ }
+
+ _mapWindowDone(shellwm, actor) {
+ if (this._mapping.delete(actor)) {
+ actor.remove_all_transitions();
+ actor.opacity = 255;
+ actor.set_pivot_point(0, 0);
+ actor.scale_y = 1;
+ actor.scale_x = 1;
+ actor.translation_y = 0;
+ actor.translation_x = 0;
+ shellwm.completed_map(actor);
+ }
+ }
+
+ _destroyWindow(shellwm, actor) {
+ let window = actor.meta_window;
+ window.disconnectObject(actor);
+ if (window._dimmed) {
+ this._dimmedWindows =
+ this._dimmedWindows.filter(win => win != window);
+ }
+
+ if (window.is_attached_dialog())
+ this._checkDimming(window.get_transient_for());
+
+ const types = [
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.DIALOG,
+ Meta.WindowType.MODAL_DIALOG,
+ ];
+ if (!this._shouldAnimateActor(actor, types)) {
+ shellwm.completed_destroy(actor);
+ return;
+ }
+
+ switch (actor.meta_window.window_type) {
+ case Meta.WindowType.NORMAL:
+ actor.set_pivot_point(0.5, 0.5);
+ this._destroying.add(actor);
+
+ actor.ease({
+ opacity: 0,
+ scale_x: 0.8,
+ scale_y: 0.8,
+ duration: DESTROY_WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this._destroyWindowDone(shellwm, actor),
+ });
+ break;
+ case Meta.WindowType.MODAL_DIALOG:
+ case Meta.WindowType.DIALOG:
+ actor.set_pivot_point(0.5, 0.5);
+ this._destroying.add(actor);
+
+ if (window.is_attached_dialog()) {
+ let parent = window.get_transient_for();
+ parent.connectObject('unmanaged', () => {
+ actor.remove_all_transitions();
+ this._destroyWindowDone(shellwm, actor);
+ }, actor);
+ }
+
+ actor.ease({
+ scale_y: 0,
+ duration: DIALOG_DESTROY_WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this._destroyWindowDone(shellwm, actor),
+ });
+ break;
+ default:
+ shellwm.completed_destroy(actor);
+ }
+ }
+
+ _destroyWindowDone(shellwm, actor) {
+ if (this._destroying.delete(actor)) {
+ const parent = actor.get_meta_window()?.get_transient_for();
+ parent?.disconnectObject(actor);
+ shellwm.completed_destroy(actor);
+ }
+ }
+
+ _filterKeybinding(shellwm, binding) {
+ if (Main.actionMode == Shell.ActionMode.NONE)
+ return true;
+
+ // There's little sense in implementing a keybinding in mutter and
+ // not having it work in NORMAL mode; handle this case generically
+ // so we don't have to explicitly allow all builtin keybindings in
+ // NORMAL mode.
+ if (Main.actionMode == Shell.ActionMode.NORMAL &&
+ binding.is_builtin())
+ return false;
+
+ return !(this._allowedKeybindings[binding.get_name()] & Main.actionMode);
+ }
+
+ _switchWorkspace(shellwm, from, to, direction) {
+ if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) {
+ shellwm.completed_switch_workspace();
+ return;
+ }
+
+ this._switchInProgress = true;
+
+ this._workspaceAnimation.animateSwitch(from, to, direction, () => {
+ this._shellwm.completed_switch_workspace();
+ this._switchInProgress = false;
+ });
+ }
+
+ _switchWorkspaceDone() {
+ if (!this._switchInProgress)
+ return;
+
+ this._shellwm.completed_switch_workspace();
+ this._switchInProgress = false;
+ }
+
+ _showTilePreview(shellwm, window, tileRect, monitorIndex) {
+ if (!this._tilePreview)
+ this._tilePreview = new TilePreview();
+ this._tilePreview.open(window, tileRect, monitorIndex);
+ }
+
+ _hideTilePreview() {
+ if (!this._tilePreview)
+ return;
+ this._tilePreview.close();
+ }
+
+ _showWindowMenu(shellwm, window, menu, rect) {
+ this._windowMenuManager.showWindowMenuForWindow(window, menu, rect);
+ }
+
+ _startSwitcher(display, window, binding) {
+ let constructor = null;
+ switch (binding.get_name()) {
+ case 'switch-applications':
+ case 'switch-applications-backward':
+ case 'switch-group':
+ case 'switch-group-backward':
+ constructor = AltTab.AppSwitcherPopup;
+ break;
+ case 'switch-windows':
+ case 'switch-windows-backward':
+ constructor = AltTab.WindowSwitcherPopup;
+ break;
+ case 'cycle-windows':
+ case 'cycle-windows-backward':
+ constructor = AltTab.WindowCyclerPopup;
+ break;
+ case 'cycle-group':
+ case 'cycle-group-backward':
+ constructor = AltTab.GroupCyclerPopup;
+ break;
+ case 'switch-monitor':
+ constructor = SwitchMonitor.SwitchMonitorPopup;
+ break;
+ }
+
+ if (!constructor)
+ return;
+
+ /* prevent a corner case where both popups show up at once */
+ if (this._workspaceSwitcherPopup != null)
+ this._workspaceSwitcherPopup.destroy();
+
+ let tabPopup = new constructor();
+
+ if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()))
+ tabPopup.destroy();
+ }
+
+ _startA11ySwitcher(display, window, binding) {
+ Main.ctrlAltTabManager.popup(binding.is_reversed(), binding.get_name(), binding.get_mask());
+ }
+
+ _allowFavoriteShortcuts() {
+ return Main.sessionMode.hasOverview;
+ }
+
+ _switchToApplication(display, window, binding) {
+ if (!this._allowFavoriteShortcuts())
+ return;
+
+ let [, , , target] = binding.get_name().split('-');
+ let apps = AppFavorites.getAppFavorites().getFavorites();
+ let app = apps[target - 1];
+ if (app) {
+ Main.overview.hide();
+ app.activate();
+ }
+ }
+
+ _toggleAppMenu() {
+ Main.panel.toggleAppMenu();
+ }
+
+ _toggleCalendar() {
+ Main.panel.toggleCalendar();
+ }
+
+ _showWorkspaceSwitcher(display, window, binding) {
+ let workspaceManager = display.get_workspace_manager();
+
+ if (!Main.sessionMode.hasWorkspaces)
+ return;
+
+ if (workspaceManager.n_workspaces == 1)
+ return;
+
+ let [action,,, target] = binding.get_name().split('-');
+ let newWs;
+ let direction;
+ let vertical = workspaceManager.layout_rows == -1;
+ let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
+
+ if (action == 'move') {
+ // "Moving" a window to another workspace doesn't make sense when
+ // it cannot be unstuck, and is potentially confusing if a new
+ // workspaces is added at the start/end
+ if (window.is_always_on_all_workspaces() ||
+ (Meta.prefs_get_workspaces_only_on_primary() &&
+ window.get_monitor() != Main.layoutManager.primaryIndex))
+ return;
+ }
+
+ if (target == 'last') {
+ if (vertical)
+ direction = Meta.MotionDirection.DOWN;
+ else if (rtl)
+ direction = Meta.MotionDirection.LEFT;
+ else
+ direction = Meta.MotionDirection.RIGHT;
+ newWs = workspaceManager.get_workspace_by_index(workspaceManager.n_workspaces - 1);
+ } else if (isNaN(target)) {
+ // Prepend a new workspace dynamically
+ let prependTarget;
+ if (vertical)
+ prependTarget = 'up';
+ else if (rtl)
+ prependTarget = 'right';
+ else
+ prependTarget = 'left';
+ if (workspaceManager.get_active_workspace_index() === 0 &&
+ action === 'move' && target === prependTarget &&
+ this._isWorkspacePrepended === false) {
+ this.insertWorkspace(0);
+ this._isWorkspacePrepended = true;
+ }
+
+ direction = Meta.MotionDirection[target.toUpperCase()];
+ newWs = workspaceManager.get_active_workspace().get_neighbor(direction);
+ } else if ((target > 0) && (target <= workspaceManager.n_workspaces)) {
+ target--;
+ newWs = workspaceManager.get_workspace_by_index(target);
+
+ if (workspaceManager.get_active_workspace().index() > target) {
+ if (vertical)
+ direction = Meta.MotionDirection.UP;
+ else if (rtl)
+ direction = Meta.MotionDirection.RIGHT;
+ else
+ direction = Meta.MotionDirection.LEFT;
+ } else {
+ if (vertical) // eslint-disable-line no-lonely-if
+ direction = Meta.MotionDirection.DOWN;
+ else if (rtl)
+ direction = Meta.MotionDirection.LEFT;
+ else
+ direction = Meta.MotionDirection.RIGHT;
+ }
+ }
+
+ if (workspaceManager.layout_rows == -1 &&
+ direction != Meta.MotionDirection.UP &&
+ direction != Meta.MotionDirection.DOWN)
+ return;
+
+ if (workspaceManager.layout_columns == -1 &&
+ direction != Meta.MotionDirection.LEFT &&
+ direction != Meta.MotionDirection.RIGHT)
+ return;
+
+ if (action == 'switch')
+ this.actionMoveWorkspace(newWs);
+ else
+ this.actionMoveWindow(window, newWs);
+
+ if (!Main.overview.visible) {
+ if (this._workspaceSwitcherPopup == null) {
+ this._workspaceTracker.blockUpdates();
+ this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
+ this._workspaceSwitcherPopup.connect('destroy', () => {
+ this._workspaceTracker.unblockUpdates();
+ this._workspaceSwitcherPopup = null;
+ this._isWorkspacePrepended = false;
+ });
+ }
+ this._workspaceSwitcherPopup.display(newWs.index());
+ }
+ }
+
+ actionMoveWorkspace(workspace) {
+ if (!Main.sessionMode.hasWorkspaces)
+ return;
+
+ if (!workspace.active)
+ workspace.activate(global.get_current_time());
+ }
+
+ actionMoveWindow(window, workspace) {
+ if (!Main.sessionMode.hasWorkspaces)
+ return;
+
+ if (!workspace.active) {
+ // This won't have any effect for "always sticky" windows
+ // (like desktop windows or docks)
+
+ this._workspaceAnimation.movingWindow = window;
+ window.change_workspace(workspace);
+
+ global.display.clear_mouse_mode();
+ workspace.activate_with_focus(window, global.get_current_time());
+ }
+ }
+
+ handleWorkspaceScroll(event) {
+ if (!this._canScroll)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (event.type() !== Clutter.EventType.SCROLL)
+ return Clutter.EVENT_PROPAGATE;
+
+ const direction = event.get_scroll_direction();
+ if (direction === Clutter.ScrollDirection.SMOOTH)
+ return Clutter.EVENT_PROPAGATE;
+
+ const workspaceManager = global.workspace_manager;
+ const vertical = workspaceManager.layout_rows === -1;
+ const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+ const activeWs = workspaceManager.get_active_workspace();
+ let ws;
+ switch (direction) {
+ case Clutter.ScrollDirection.UP:
+ if (vertical)
+ ws = activeWs.get_neighbor(Meta.MotionDirection.UP);
+ else if (rtl)
+ ws = activeWs.get_neighbor(Meta.MotionDirection.RIGHT);
+ else
+ ws = activeWs.get_neighbor(Meta.MotionDirection.LEFT);
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ if (vertical)
+ ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN);
+ else if (rtl)
+ ws = activeWs.get_neighbor(Meta.MotionDirection.LEFT);
+ else
+ ws = activeWs.get_neighbor(Meta.MotionDirection.RIGHT);
+ break;
+ case Clutter.ScrollDirection.LEFT:
+ ws = activeWs.get_neighbor(Meta.MotionDirection.LEFT);
+ break;
+ case Clutter.ScrollDirection.RIGHT:
+ ws = activeWs.get_neighbor(Meta.MotionDirection.RIGHT);
+ break;
+ default:
+ return Clutter.EVENT_PROPAGATE;
+ }
+ this.actionMoveWorkspace(ws);
+
+ this._canScroll = false;
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ SCROLL_TIMEOUT_TIME, () => {
+ this._canScroll = true;
+ return GLib.SOURCE_REMOVE;
+ });
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _confirmDisplayChange() {
+ let dialog = new DisplayChangeDialog(this._shellwm);
+ dialog.open();
+ }
+
+ _createCloseDialog(shellwm, window) {
+ return new CloseDialog.CloseDialog(window);
+ }
+
+ _createInhibitShortcutsDialog(shellwm, window) {
+ return new InhibitShortcutsDialog.InhibitShortcutsDialog(window);
+ }
+
+ _showResizePopup(display, show, rect, displayW, displayH) {
+ if (show) {
+ if (!this._resizePopup)
+ this._resizePopup = new ResizePopup();
+
+ this._resizePopup.set(rect, displayW, displayH);
+ } else {
+ if (!this._resizePopup)
+ return;
+
+ this._resizePopup.destroy();
+ this._resizePopup = null;
+ }
+ }
+};
diff --git a/js/ui/windowMenu.js b/js/ui/windowMenu.js
new file mode 100644
index 0000000..081645f
--- /dev/null
+++ b/js/ui/windowMenu.js
@@ -0,0 +1,252 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*
+/* exported WindowMenuManager */
+
+const { GLib, Meta, St } = imports.gi;
+
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const Screenshot = imports.ui.screenshot;
+
+var WindowMenu = class extends PopupMenu.PopupMenu {
+ constructor(window, sourceActor) {
+ super(sourceActor, 0, St.Side.TOP);
+
+ this.actor.add_style_class_name('window-menu');
+
+ Main.layoutManager.uiGroup.add_actor(this.actor);
+ this.actor.hide();
+
+ this._buildMenu(window);
+ }
+
+ _buildMenu(window) {
+ let type = window.get_window_type();
+
+ let item;
+
+ // Translators: entry in the window right click menu.
+ item = this.addAction(_('Take Screenshot'), async () => {
+ try {
+ const actor = window.get_compositor_private();
+ const content = actor.paint_to_content(null);
+ const texture = content.get_texture();
+
+ await Screenshot.captureScreenshot(texture, null, 1, null);
+ } catch (e) {
+ logError(e, 'Error capturing screenshot');
+ }
+ });
+
+ item = this.addAction(_('Hide'), () => {
+ window.minimize();
+ });
+ if (!window.can_minimize())
+ item.setSensitive(false);
+
+ if (window.get_maximized()) {
+ item = this.addAction(_('Restore'), () => {
+ window.unmaximize(Meta.MaximizeFlags.BOTH);
+ });
+ } else {
+ item = this.addAction(_("Maximize"), () => {
+ window.maximize(Meta.MaximizeFlags.BOTH);
+ });
+ }
+ if (!window.can_maximize())
+ item.setSensitive(false);
+
+ item = this.addAction(_("Move"), event => {
+ this._grabAction(window, Meta.GrabOp.KEYBOARD_MOVING, event.get_time());
+ });
+ if (!window.allows_move())
+ item.setSensitive(false);
+
+ item = this.addAction(_("Resize"), event => {
+ this._grabAction(window, Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, event.get_time());
+ });
+ if (!window.allows_resize())
+ item.setSensitive(false);
+
+ if (!window.titlebar_is_onscreen() && type != Meta.WindowType.DOCK && type != Meta.WindowType.DESKTOP) {
+ this.addAction(_("Move Titlebar Onscreen"), () => {
+ window.shove_titlebar_onscreen();
+ });
+ }
+
+ item = this.addAction(_("Always on Top"), () => {
+ if (window.is_above())
+ window.unmake_above();
+ else
+ window.make_above();
+ });
+ if (window.is_above())
+ item.setOrnament(PopupMenu.Ornament.CHECK);
+ if (window.get_maximized() == Meta.MaximizeFlags.BOTH ||
+ type == Meta.WindowType.DOCK ||
+ type == Meta.WindowType.DESKTOP ||
+ type == Meta.WindowType.SPLASHSCREEN)
+ item.setSensitive(false);
+
+ if (Main.sessionMode.hasWorkspaces &&
+ (!Meta.prefs_get_workspaces_only_on_primary() ||
+ window.is_on_primary_monitor())) {
+ let isSticky = window.is_on_all_workspaces();
+
+ item = this.addAction(_("Always on Visible Workspace"), () => {
+ if (isSticky)
+ window.unstick();
+ else
+ window.stick();
+ });
+ if (isSticky)
+ item.setOrnament(PopupMenu.Ornament.CHECK);
+ if (window.is_always_on_all_workspaces())
+ item.setSensitive(false);
+
+ if (!isSticky) {
+ let workspace = window.get_workspace();
+ if (workspace != workspace.get_neighbor(Meta.MotionDirection.LEFT)) {
+ this.addAction(_("Move to Workspace Left"), () => {
+ let dir = Meta.MotionDirection.LEFT;
+ window.change_workspace(workspace.get_neighbor(dir));
+ });
+ }
+ if (workspace != workspace.get_neighbor(Meta.MotionDirection.RIGHT)) {
+ this.addAction(_("Move to Workspace Right"), () => {
+ let dir = Meta.MotionDirection.RIGHT;
+ window.change_workspace(workspace.get_neighbor(dir));
+ });
+ }
+ if (workspace != workspace.get_neighbor(Meta.MotionDirection.UP)) {
+ this.addAction(_("Move to Workspace Up"), () => {
+ let dir = Meta.MotionDirection.UP;
+ window.change_workspace(workspace.get_neighbor(dir));
+ });
+ }
+ if (workspace != workspace.get_neighbor(Meta.MotionDirection.DOWN)) {
+ this.addAction(_("Move to Workspace Down"), () => {
+ let dir = Meta.MotionDirection.DOWN;
+ window.change_workspace(workspace.get_neighbor(dir));
+ });
+ }
+ }
+ }
+
+ let display = global.display;
+ let nMonitors = display.get_n_monitors();
+ let monitorIndex = window.get_monitor();
+ if (nMonitors > 1 && monitorIndex >= 0) {
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ let dir = Meta.DisplayDirection.UP;
+ let upMonitorIndex =
+ display.get_monitor_neighbor_index(monitorIndex, dir);
+ if (upMonitorIndex != -1) {
+ this.addAction(_("Move to Monitor Up"), () => {
+ window.move_to_monitor(upMonitorIndex);
+ });
+ }
+
+ dir = Meta.DisplayDirection.DOWN;
+ let downMonitorIndex =
+ display.get_monitor_neighbor_index(monitorIndex, dir);
+ if (downMonitorIndex != -1) {
+ this.addAction(_("Move to Monitor Down"), () => {
+ window.move_to_monitor(downMonitorIndex);
+ });
+ }
+
+ dir = Meta.DisplayDirection.LEFT;
+ let leftMonitorIndex =
+ display.get_monitor_neighbor_index(monitorIndex, dir);
+ if (leftMonitorIndex != -1) {
+ this.addAction(_("Move to Monitor Left"), () => {
+ window.move_to_monitor(leftMonitorIndex);
+ });
+ }
+
+ dir = Meta.DisplayDirection.RIGHT;
+ let rightMonitorIndex =
+ display.get_monitor_neighbor_index(monitorIndex, dir);
+ if (rightMonitorIndex != -1) {
+ this.addAction(_("Move to Monitor Right"), () => {
+ window.move_to_monitor(rightMonitorIndex);
+ });
+ }
+ }
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ item = this.addAction(_("Close"), event => {
+ window.delete(event.get_time());
+ });
+ if (!window.can_close())
+ item.setSensitive(false);
+ }
+
+ _grabAction(window, grabOp, time) {
+ if (global.display.get_grab_op() == Meta.GrabOp.NONE) {
+ window.begin_grab_op(grabOp, true, time);
+ return;
+ }
+
+ let waitId = 0;
+ let id = global.display.connect('grab-op-end', display => {
+ display.disconnect(id);
+ GLib.source_remove(waitId);
+
+ window.begin_grab_op(grabOp, true, time);
+ });
+
+ waitId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
+ global.display.disconnect(id);
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+};
+
+var WindowMenuManager = class {
+ constructor() {
+ this._manager = new PopupMenu.PopupMenuManager(Main.layoutManager.dummyCursor);
+
+ this._sourceActor = new St.Widget({ reactive: true, visible: false });
+ this._sourceActor.connect('button-press-event', () => {
+ this._manager.activeMenu.toggle();
+ });
+ Main.uiGroup.add_actor(this._sourceActor);
+ }
+
+ showWindowMenuForWindow(window, type, rect) {
+ if (!Main.sessionMode.hasWmMenus)
+ return;
+
+ if (type != Meta.WindowMenuType.WM)
+ throw new Error('Unsupported window menu type');
+ let menu = new WindowMenu(window, this._sourceActor);
+
+ this._manager.addMenu(menu);
+
+ menu.connect('activate', () => {
+ window.check_alive(global.get_current_time());
+ });
+ let destroyId = window.connect('unmanaged', () => {
+ menu.close();
+ });
+
+ this._sourceActor.set_size(Math.max(1, rect.width), Math.max(1, rect.height));
+ this._sourceActor.set_position(rect.x, rect.y);
+ this._sourceActor.show();
+
+ menu.open(BoxPointer.PopupAnimation.FADE);
+ menu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ menu.connect('open-state-changed', (menu_, isOpen) => {
+ if (isOpen)
+ return;
+
+ this._sourceActor.hide();
+ menu.destroy();
+ window.disconnect(destroyId);
+ });
+ }
+};
diff --git a/js/ui/windowPreview.js b/js/ui/windowPreview.js
new file mode 100644
index 0000000..c650414
--- /dev/null
+++ b/js/ui/windowPreview.js
@@ -0,0 +1,681 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WindowPreview */
+
+const {
+ Atk, Clutter, GLib, GObject, Graphene, Meta, Pango, Shell, St,
+} = imports.gi;
+
+const DND = imports.ui.dnd;
+const OverviewControls = imports.ui.overviewControls;
+
+var WINDOW_DND_SIZE = 256;
+
+var WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 750;
+var WINDOW_OVERLAY_FADE_TIME = 200;
+
+var WINDOW_SCALE_TIME = 200;
+var WINDOW_ACTIVE_SIZE_INC = 5; // in each direction
+
+var DRAGGING_WINDOW_OPACITY = 100;
+
+const ICON_SIZE = 64;
+const ICON_OVERLAP = 0.7;
+
+const ICON_TITLE_SPACING = 6;
+
+var WindowPreview = GObject.registerClass({
+ Properties: {
+ 'overlay-enabled': GObject.ParamSpec.boolean(
+ 'overlay-enabled', 'overlay-enabled', 'overlay-enabled',
+ GObject.ParamFlags.READWRITE,
+ true),
+ },
+ Signals: {
+ 'drag-begin': {},
+ 'drag-cancelled': {},
+ 'drag-end': {},
+ 'selected': { param_types: [GObject.TYPE_UINT] },
+ 'show-chrome': {},
+ 'size-changed': {},
+ },
+}, class WindowPreview extends Shell.WindowPreview {
+ _init(metaWindow, workspace, overviewAdjustment) {
+ this.metaWindow = metaWindow;
+ this.metaWindow._delegate = this;
+ this._windowActor = metaWindow.get_compositor_private();
+ this._workspace = workspace;
+ this._overviewAdjustment = overviewAdjustment;
+
+ super._init({
+ reactive: true,
+ can_focus: true,
+ accessible_role: Atk.Role.PUSH_BUTTON,
+ offscreen_redirect: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY,
+ });
+
+ const windowContainer = new Clutter.Actor({
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ });
+ this.window_container = windowContainer;
+
+ windowContainer.connect('notify::scale-x',
+ () => this._adjustOverlayOffsets());
+ // gjs currently can't handle setting an actors layout manager during
+ // the initialization of the actor if that layout manager keeps track
+ // of its container, so set the layout manager after creating the
+ // container
+ windowContainer.layout_manager = new Shell.WindowPreviewLayout();
+ this.add_child(windowContainer);
+
+ this._addWindow(metaWindow);
+
+ this._delegate = this;
+
+ this._stackAbove = null;
+
+ this._cachedBoundingBox = {
+ x: windowContainer.layout_manager.bounding_box.x1,
+ y: windowContainer.layout_manager.bounding_box.y1,
+ width: windowContainer.layout_manager.bounding_box.get_width(),
+ height: windowContainer.layout_manager.bounding_box.get_height(),
+ };
+
+ windowContainer.layout_manager.connect(
+ 'notify::bounding-box', layout => {
+ this._cachedBoundingBox = {
+ x: layout.bounding_box.x1,
+ y: layout.bounding_box.y1,
+ width: layout.bounding_box.get_width(),
+ height: layout.bounding_box.get_height(),
+ };
+
+ // A bounding box of 0x0 means all windows were removed
+ if (layout.bounding_box.get_area() > 0)
+ this.emit('size-changed');
+ });
+
+ this._windowActor.connectObject('destroy', () => this.destroy(), this);
+
+ this._updateAttachedDialogs();
+
+ let clickAction = new Clutter.ClickAction();
+ clickAction.connect('clicked', () => this._activate());
+ clickAction.connect('long-press', this._onLongPress.bind(this));
+ this.add_action(clickAction);
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._draggable = DND.makeDraggable(this, {
+ restoreOnSuccess: true,
+ manualMode: true,
+ dragActorMaxSize: WINDOW_DND_SIZE,
+ dragActorOpacity: DRAGGING_WINDOW_OPACITY,
+ });
+ this._draggable.connect('drag-begin', this._onDragBegin.bind(this));
+ this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this));
+ this._draggable.connect('drag-end', this._onDragEnd.bind(this));
+ this.inDrag = false;
+
+ this._selected = false;
+ this._overlayEnabled = true;
+ this._overlayShown = false;
+ this._closeRequested = false;
+ this._idleHideOverlayId = 0;
+
+ const tracker = Shell.WindowTracker.get_default();
+ const app = tracker.get_window_app(this.metaWindow);
+ this._icon = app.create_icon_texture(ICON_SIZE);
+ this._icon.add_style_class_name('icon-dropshadow');
+ this._icon.set({
+ reactive: true,
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ });
+ this._icon.add_constraint(new Clutter.BindConstraint({
+ source: windowContainer,
+ coordinate: Clutter.BindCoordinate.POSITION,
+ }));
+ this._icon.add_constraint(new Clutter.AlignConstraint({
+ source: windowContainer,
+ align_axis: Clutter.AlignAxis.X_AXIS,
+ factor: 0.5,
+ }));
+ this._icon.add_constraint(new Clutter.AlignConstraint({
+ source: windowContainer,
+ align_axis: Clutter.AlignAxis.Y_AXIS,
+ pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }),
+ factor: 1,
+ }));
+
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ this._title = new St.Label({
+ visible: false,
+ style_class: 'window-caption',
+ text: this._getCaption(),
+ reactive: true,
+ });
+ this._title.clutter_text.single_line_mode = true;
+ this._title.add_constraint(new Clutter.BindConstraint({
+ source: windowContainer,
+ coordinate: Clutter.BindCoordinate.X,
+ }));
+ const iconBottomOverlap = ICON_SIZE * (1 - ICON_OVERLAP);
+ this._title.add_constraint(new Clutter.BindConstraint({
+ source: windowContainer,
+ coordinate: Clutter.BindCoordinate.Y,
+ offset: scaleFactor * (iconBottomOverlap + ICON_TITLE_SPACING),
+ }));
+ this._title.add_constraint(new Clutter.AlignConstraint({
+ source: windowContainer,
+ align_axis: Clutter.AlignAxis.X_AXIS,
+ factor: 0.5,
+ }));
+ this._title.add_constraint(new Clutter.AlignConstraint({
+ source: windowContainer,
+ align_axis: Clutter.AlignAxis.Y_AXIS,
+ pivot_point: new Graphene.Point({ x: -1, y: 0 }),
+ factor: 1,
+ }));
+ this._title.clutter_text.ellipsize = Pango.EllipsizeMode.END;
+ this.label_actor = this._title;
+ this.metaWindow.connectObject(
+ 'notify::title', () => (this._title.text = this._getCaption()),
+ this);
+
+ const layout = Meta.prefs_get_button_layout();
+ this._closeButtonSide =
+ layout.left_buttons.includes(Meta.ButtonFunction.CLOSE)
+ ? St.Side.LEFT : St.Side.RIGHT;
+
+ this._closeButton = new St.Button({
+ visible: false,
+ style_class: 'window-close',
+ icon_name: 'preview-close-symbolic',
+ });
+ this._closeButton.add_constraint(new Clutter.BindConstraint({
+ source: windowContainer,
+ coordinate: Clutter.BindCoordinate.POSITION,
+ }));
+ this._closeButton.add_constraint(new Clutter.AlignConstraint({
+ source: windowContainer,
+ align_axis: Clutter.AlignAxis.X_AXIS,
+ pivot_point: new Graphene.Point({ x: 0.5, y: -1 }),
+ factor: this._closeButtonSide === St.Side.LEFT ? 0 : 1,
+ }));
+ this._closeButton.add_constraint(new Clutter.AlignConstraint({
+ source: windowContainer,
+ align_axis: Clutter.AlignAxis.Y_AXIS,
+ pivot_point: new Graphene.Point({ x: -1, y: 0.5 }),
+ factor: 0,
+ }));
+ this._closeButton.connect('clicked', () => this._deleteAll());
+
+ this.add_child(this._title);
+ this.add_child(this._icon);
+ this.add_child(this._closeButton);
+
+ this._overviewAdjustment.connectObject(
+ 'notify::value', () => this._updateIconScale(), this);
+ this._updateIconScale();
+
+ this.connect('notify::realized', () => {
+ if (!this.realized)
+ return;
+
+ this._title.ensure_style();
+ this._icon.ensure_style();
+ });
+ }
+
+ _updateIconScale() {
+ const { ControlsState } = OverviewControls;
+ const { currentState, initialState, finalState } =
+ this._overviewAdjustment.getStateTransitionParams();
+ const visible =
+ initialState === ControlsState.WINDOW_PICKER ||
+ finalState === ControlsState.WINDOW_PICKER;
+ const scale = visible
+ ? 1 - Math.abs(ControlsState.WINDOW_PICKER - currentState) : 0;
+
+ this._icon.set({
+ scale_x: scale,
+ scale_y: scale,
+ });
+ }
+
+ _windowCanClose() {
+ return this.metaWindow.can_close() &&
+ !this._hasAttachedDialogs();
+ }
+
+ _getCaption() {
+ if (this.metaWindow.title)
+ return this.metaWindow.title;
+
+ let tracker = Shell.WindowTracker.get_default();
+ let app = tracker.get_window_app(this.metaWindow);
+ return app.get_name();
+ }
+
+ overlapHeights() {
+ const [, titleHeight] = this._title.get_preferred_height(-1);
+
+ const topOverlap = 0;
+ const bottomOverlap = ICON_TITLE_SPACING + titleHeight;
+
+ return [topOverlap, bottomOverlap];
+ }
+
+ chromeHeights() {
+ const [, closeButtonHeight] = this._closeButton.get_preferred_height(-1);
+ const [, iconHeight] = this._icon.get_preferred_height(-1);
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * scaleFactor;
+
+ const topOversize = closeButtonHeight / 2;
+ const bottomOversize = (1 - ICON_OVERLAP) * iconHeight;
+
+ return [
+ topOversize + activeExtraSize,
+ bottomOversize + activeExtraSize,
+ ];
+ }
+
+ chromeWidths() {
+ const [, closeButtonWidth] = this._closeButton.get_preferred_width(-1);
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * scaleFactor;
+
+ const leftOversize = this._closeButtonSide === St.Side.LEFT
+ ? closeButtonWidth / 2
+ : 0;
+ const rightOversize = this._closeButtonSide === St.Side.LEFT
+ ? 0
+ : closeButtonWidth / 2;
+
+ return [
+ leftOversize + activeExtraSize,
+ rightOversize + activeExtraSize,
+ ];
+ }
+
+ showOverlay(animate) {
+ if (!this._overlayEnabled)
+ return;
+
+ if (this._overlayShown)
+ return;
+
+ this._overlayShown = true;
+ this._restack();
+
+ // If we're supposed to animate and an animation in our direction
+ // is already happening, let that one continue
+ const ongoingTransition = this._title.get_transition('opacity');
+ if (animate &&
+ ongoingTransition &&
+ ongoingTransition.get_interval().peek_final_value() === 255)
+ return;
+
+ const toShow = this._windowCanClose()
+ ? [this._title, this._closeButton]
+ : [this._title];
+
+ toShow.forEach(a => {
+ a.opacity = 0;
+ a.show();
+ a.ease({
+ opacity: 255,
+ duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ });
+
+ const [width, height] = this.window_container.get_size();
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * 2 * scaleFactor;
+ const origSize = Math.max(width, height);
+ const scale = (origSize + activeExtraSize) / origSize;
+
+ this.window_container.ease({
+ scale_x: scale,
+ scale_y: scale,
+ duration: animate ? WINDOW_SCALE_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this.emit('show-chrome');
+ }
+
+ hideOverlay(animate) {
+ if (!this._overlayShown)
+ return;
+
+ this._overlayShown = false;
+ this._restack();
+
+ // If we're supposed to animate and an animation in our direction
+ // is already happening, let that one continue
+ const ongoingTransition = this._title.get_transition('opacity');
+ if (animate &&
+ ongoingTransition &&
+ ongoingTransition.get_interval().peek_final_value() === 0)
+ return;
+
+ [this._title, this._closeButton].forEach(a => {
+ a.opacity = 255;
+ a.ease({
+ opacity: 0,
+ duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => a.hide(),
+ });
+ });
+
+ this.window_container.ease({
+ scale_x: 1,
+ scale_y: 1,
+ duration: animate ? WINDOW_SCALE_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ _adjustOverlayOffsets() {
+ // Assume that scale-x and scale-y update always set
+ // in lock-step; that allows us to not use separate
+ // handlers for horizontal and vertical offsets
+ const previewScale = this.window_container.scale_x;
+ const [previewWidth, previewHeight] =
+ this.window_container.allocation.get_size();
+
+ const heightIncrease =
+ Math.floor(previewHeight * (previewScale - 1) / 2);
+ const widthIncrease =
+ Math.floor(previewWidth * (previewScale - 1) / 2);
+
+ const closeAlign = this._closeButtonSide === St.Side.LEFT ? -1 : 1;
+
+ this._icon.translation_y = heightIncrease;
+ this._title.translation_y = heightIncrease;
+ this._closeButton.set({
+ translation_x: closeAlign * widthIncrease,
+ translation_y: -heightIncrease,
+ });
+ }
+
+ _addWindow(metaWindow) {
+ const clone = this.window_container.layout_manager.add_window(metaWindow);
+ if (!clone)
+ return;
+
+ // We expect this to be used for all interaction rather than
+ // the ClutterClone; as the former is reactive and the latter
+ // is not, this just works for most cases. However, for DND all
+ // actors are picked, so DND operations would operate on the clone.
+ // To avoid this, we hide it from pick.
+ Shell.util_set_hidden_from_pick(clone, true);
+ }
+
+ vfunc_has_overlaps() {
+ return this._hasAttachedDialogs() ||
+ this._icon.visible ||
+ this._closeButton.visible;
+ }
+
+ _deleteAll() {
+ const windows = this.window_container.layout_manager.get_windows();
+
+ // Delete all windows, starting from the bottom-most (most-modal) one
+ for (const window of windows.reverse())
+ window.delete(global.get_current_time());
+
+ this._closeRequested = true;
+ }
+
+ addDialog(win) {
+ let parent = win.get_transient_for();
+ while (parent.is_attached_dialog())
+ parent = parent.get_transient_for();
+
+ // Display dialog if it is attached to our metaWindow
+ if (win.is_attached_dialog() && parent == this.metaWindow)
+ this._addWindow(win);
+
+ // The dialog popped up after the user tried to close the window,
+ // assume it's a close confirmation and leave the overview
+ if (this._closeRequested)
+ this._activate();
+ }
+
+ _hasAttachedDialogs() {
+ return this.window_container.layout_manager.get_windows().length > 1;
+ }
+
+ _updateAttachedDialogs() {
+ let iter = win => {
+ let actor = win.get_compositor_private();
+
+ if (!actor)
+ return false;
+ if (!win.is_attached_dialog())
+ return false;
+
+ this._addWindow(win);
+ win.foreach_transient(iter);
+ return true;
+ };
+ this.metaWindow.foreach_transient(iter);
+ }
+
+ get boundingBox() {
+ return { ...this._cachedBoundingBox };
+ }
+
+ get windowCenter() {
+ return {
+ x: this._cachedBoundingBox.x + this._cachedBoundingBox.width / 2,
+ y: this._cachedBoundingBox.y + this._cachedBoundingBox.height / 2,
+ };
+ }
+
+ get overlayEnabled() {
+ return this._overlayEnabled;
+ }
+
+ set overlayEnabled(enabled) {
+ if (this._overlayEnabled === enabled)
+ return;
+
+ this._overlayEnabled = enabled;
+ this.notify('overlay-enabled');
+
+ if (!enabled)
+ this.hideOverlay(false);
+ else if (this['has-pointer'] || global.stage.key_focus === this)
+ this.showOverlay(true);
+ }
+
+ // Find the actor just below us, respecting reparenting done by DND code
+ _getActualStackAbove() {
+ if (this._stackAbove == null)
+ return null;
+
+ if (this.inDrag) {
+ if (this._stackAbove._delegate)
+ return this._stackAbove._delegate._getActualStackAbove();
+ else
+ return null;
+ } else {
+ return this._stackAbove;
+ }
+ }
+
+ setStackAbove(actor) {
+ this._stackAbove = actor;
+ if (this.inDrag)
+ // We'll fix up the stack after the drag
+ return;
+
+ let parent = this.get_parent();
+ let actualAbove = this._getActualStackAbove();
+ if (actualAbove == null)
+ parent.set_child_below_sibling(this, null);
+ else
+ parent.set_child_above_sibling(this, actualAbove);
+ }
+
+ _onDestroy() {
+ this.metaWindow._delegate = null;
+ this._delegate = null;
+ this._destroyed = true;
+
+ if (this._longPressLater) {
+ Meta.later_remove(this._longPressLater);
+ delete this._longPressLater;
+ }
+
+ if (this._idleHideOverlayId > 0) {
+ GLib.source_remove(this._idleHideOverlayId);
+ this._idleHideOverlayId = 0;
+ }
+
+ if (this.inDrag) {
+ this.emit('drag-end');
+ this.inDrag = false;
+ }
+ }
+
+ _activate() {
+ this._selected = true;
+ this.emit('selected', global.get_current_time());
+ }
+
+ vfunc_enter_event(crossingEvent) {
+ this.showOverlay(true);
+ return super.vfunc_enter_event(crossingEvent);
+ }
+
+ vfunc_leave_event(crossingEvent) {
+ if (this._destroyed)
+ return super.vfunc_leave_event(crossingEvent);
+
+ if ((crossingEvent.flags & Clutter.EventFlags.FLAG_GRAB_NOTIFY) !== 0 &&
+ global.stage.get_grab_actor() === this._closeButton)
+ return super.vfunc_leave_event(crossingEvent);
+
+ if (this._idleHideOverlayId > 0)
+ GLib.source_remove(this._idleHideOverlayId);
+
+ this._idleHideOverlayId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT, () => {
+ if (this._closeButton['has-pointer'] ||
+ this._title['has-pointer'])
+ return GLib.SOURCE_CONTINUE;
+
+ if (!this['has-pointer'])
+ this.hideOverlay(true);
+
+ this._idleHideOverlayId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+
+ GLib.Source.set_name_by_id(this._idleHideOverlayId, '[gnome-shell] this._idleHideOverlayId');
+
+ return super.vfunc_leave_event(crossingEvent);
+ }
+
+ vfunc_key_focus_in() {
+ super.vfunc_key_focus_in();
+ this.showOverlay(true);
+ }
+
+ vfunc_key_focus_out() {
+ super.vfunc_key_focus_out();
+
+ if (global.stage.get_grab_actor() !== this._closeButton)
+ this.hideOverlay(true);
+ }
+
+ vfunc_key_press_event(keyEvent) {
+ let symbol = keyEvent.keyval;
+ let isEnter = symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter;
+ if (isEnter) {
+ this._activate();
+ return true;
+ }
+
+ return super.vfunc_key_press_event(keyEvent);
+ }
+
+ _onLongPress(action, actor, state) {
+ // Take advantage of the Clutter policy to consider
+ // a long-press canceled when the pointer movement
+ // exceeds dnd-drag-threshold to manually start the drag
+ if (state == Clutter.LongPressState.CANCEL) {
+ let event = Clutter.get_current_event();
+ this._dragTouchSequence = event.get_event_sequence();
+
+ if (this._longPressLater)
+ return true;
+
+ // A click cancels a long-press before any click handler is
+ // run - make sure to not start a drag in that case
+ this._longPressLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ delete this._longPressLater;
+ if (this._selected) {
+ this._selected = false;
+ return;
+ }
+ let [x, y] = action.get_coords();
+ action.release();
+ this._draggable.startDrag(x, y, global.get_current_time(), this._dragTouchSequence, event.get_device());
+ });
+ } else {
+ this.showOverlay(true);
+ }
+ return true;
+ }
+
+ _restack() {
+ // We may not have a parent if DnD completed successfully, in
+ // which case our clone will shortly be destroyed and replaced
+ // with a new one on the target workspace.
+ const parent = this.get_parent();
+ if (parent !== null) {
+ if (this._overlayShown)
+ parent.set_child_above_sibling(this, null);
+ else if (this._stackAbove === null)
+ parent.set_child_below_sibling(this, null);
+ else if (!this._stackAbove._overlayShown)
+ parent.set_child_above_sibling(this, this._stackAbove);
+ }
+ }
+
+ _onDragBegin(_draggable, _time) {
+ this.inDrag = true;
+ this.hideOverlay(false);
+ this.emit('drag-begin');
+ }
+
+ handleDragOver(source, actor, x, y, time) {
+ return this._workspace.handleDragOver(source, actor, x, y, time);
+ }
+
+ acceptDrop(source, actor, x, y, time) {
+ return this._workspace.acceptDrop(source, actor, x, y, time);
+ }
+
+ _onDragCancelled(_draggable, _time) {
+ this.emit('drag-cancelled');
+ }
+
+ _onDragEnd(_draggable, _time, _snapback) {
+ this.inDrag = false;
+
+ this._restack();
+
+ if (this['has-pointer'])
+ this.showOverlay(true);
+
+ this.emit('drag-end');
+ }
+});
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
new file mode 100644
index 0000000..bf631a5
--- /dev/null
+++ b/js/ui/workspace.js
@@ -0,0 +1,1457 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported Workspace */
+
+const {Clutter, GLib, GObject, Graphene, Meta, Shell, St} = imports.gi;
+
+const Background = imports.ui.background;
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const OverviewControls = imports.ui.overviewControls;
+const Params = imports.misc.params;
+const Util = imports.misc.util;
+const { WindowPreview } = imports.ui.windowPreview;
+
+var WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95;
+
+var WINDOW_REPOSITIONING_DELAY = 750;
+
+// When calculating a layout, we calculate the scale of windows and the percent
+// of the available area the new layout uses. If the values for the new layout,
+// when weighted with the values as below, are worse than the previous layout's,
+// we stop looking for a new layout and use the previous layout.
+// Otherwise, we keep looking for a new layout.
+var LAYOUT_SCALE_WEIGHT = 1;
+var LAYOUT_SPACE_WEIGHT = 0.1;
+
+const BACKGROUND_CORNER_RADIUS_PIXELS = 30;
+
+// Window Thumbnail Layout Algorithm
+// =================================
+//
+// General overview
+// ----------------
+//
+// The window thumbnail layout algorithm calculates some optimal layout
+// by computing layouts with some number of rows, calculating how good
+// each layout is, and stopping iterating when it finds one that is worse
+// than the previous layout. A layout consists of which windows are in
+// which rows, row sizes and other general state tracking that would make
+// calculating window positions from this information fairly easy.
+//
+// After a layout is computed that's considered the best layout, we
+// compute the layout scale to fit it in the area, and then compute
+// slots (sizes and positions) for each thumbnail.
+//
+// Layout generation
+// -----------------
+//
+// Layout generation is naive and simple: we simply add windows to a row
+// until we've added too many windows to a row, and then make a new row,
+// until we have our required N rows. The potential issue with this strategy
+// is that we may have too many windows at the bottom in some pathological
+// cases, which tends to make the thumbnails have the shape of a pile of
+// sand with a peak, with one window at the top.
+//
+// Scaling factors
+// ---------------
+//
+// Thumbnail position is mostly straightforward -- the main issue is
+// computing an optimal scale for each window that fits the constraints,
+// and doesn't make the thumbnail too small to see. There are two factors
+// involved in thumbnail scale to make sure that these two goals are met:
+// the window scale (calculated by _computeWindowScale) and the layout
+// scale (calculated by computeSizeAndScale).
+//
+// The calculation logic becomes slightly more complicated because row
+// and column spacing are not scaled, they're constant, so we can't
+// simply generate a bunch of window positions and then scale it. In
+// practice, it's not too bad -- we can simply try to fit the layout
+// in the input area minus whatever spacing we have, and then add
+// it back afterwards.
+//
+// The window scale is constant for the window's size regardless of the
+// input area or the layout scale or rows or anything else, and right
+// now just enlarges the window if it's too small. The fact that this
+// factor is stable makes it easy to calculate, so there's no sense
+// in not applying it in most calculations.
+//
+// The layout scale depends on the input area, the rows, etc, but is the
+// same for the entire layout, rather than being per-window. After
+// generating the rows of windows, we basically do some basic math to
+// fit the full, unscaled layout to the input area, as described above.
+//
+// With these two factors combined, the final scale of each thumbnail is
+// simply windowScale * layoutScale... almost.
+//
+// There's one additional constraint: the thumbnail scale must never be
+// larger than WINDOW_PREVIEW_MAXIMUM_SCALE, which means that the inequality:
+//
+// windowScale * layoutScale <= WINDOW_PREVIEW_MAXIMUM_SCALE
+//
+// must always be true. This is for each individual window -- while we
+// could adjust layoutScale to make the largest thumbnail smaller than
+// WINDOW_PREVIEW_MAXIMUM_SCALE, it would shrink windows which are already
+// under the inequality. To solve this, we simply cheat: we simply keep
+// each window's "cell" area to be the same, but we shrink the thumbnail
+// and center it horizontally, and align it to the bottom vertically.
+
+var LayoutStrategy = class {
+ constructor(params) {
+ params = Params.parse(params, {
+ monitor: null,
+ rowSpacing: 0,
+ columnSpacing: 0,
+ });
+
+ if (!params.monitor)
+ throw new Error(`No monitor param passed to ${this.constructor.name}`);
+
+ this._monitor = params.monitor;
+ this._rowSpacing = params.rowSpacing;
+ this._columnSpacing = params.columnSpacing;
+ }
+
+ // Compute a strategy-specific overall layout given a list of WindowPreviews
+ // @windows and the strategy-specific @layoutParams.
+ //
+ // Returns a strategy-specific layout object that is opaque to the user.
+ computeLayout(_windows, _layoutParams) {
+ throw new GObject.NotImplementedError(`computeLayout in ${this.constructor.name}`);
+ }
+
+ // Given @layout and @area, compute the overall scale of the layout and
+ // space occupied by the layout.
+ //
+ // This method returns an array where the first element is the scale and
+ // the second element is the space.
+ //
+ // This method must be called before calling computeWindowSlots(), as it
+ // sets the fixed overall scale of the layout.
+ computeScaleAndSpace(_layout, _area) {
+ throw new GObject.NotImplementedError(`computeScaleAndSpace in ${this.constructor.name}`);
+ }
+
+ // Returns an array with final position and size information for each
+ // window of the layout, given a bounding area that it will be inside of.
+ computeWindowSlots(_layout, _area) {
+ throw new GObject.NotImplementedError(`computeWindowSlots in ${this.constructor.name}`);
+ }
+};
+
+var UnalignedLayoutStrategy = class extends LayoutStrategy {
+ _newRow() {
+ // Row properties:
+ //
+ // * x, y are the position of row, relative to area
+ //
+ // * width, height are the scaled versions of fullWidth, fullHeight
+ //
+ // * width also has the spacing in between windows. It's not in
+ // fullWidth, as the spacing is constant, whereas fullWidth is
+ // meant to be scaled
+ //
+ // * neither height/fullHeight have any sort of spacing or padding
+ return {
+ x: 0, y: 0,
+ width: 0, height: 0,
+ fullWidth: 0, fullHeight: 0,
+ windows: [],
+ };
+ }
+
+ // Computes and returns an individual scaling factor for @window,
+ // to be applied in addition to the overall layout scale.
+ _computeWindowScale(window) {
+ // Since we align windows next to each other, the height of the
+ // thumbnails is much more important to preserve than the width of
+ // them, so two windows with equal height, but maybe differering
+ // widths line up.
+ let ratio = window.boundingBox.height / this._monitor.height;
+
+ // The purpose of this manipulation here is to prevent windows
+ // from getting too small. For something like a calculator window,
+ // we need to bump up the size just a bit to make sure it looks
+ // good. We'll use a multiplier of 1.5 for this.
+
+ // Map from [0, 1] to [1.5, 1]
+ return Util.lerp(1.5, 1, ratio);
+ }
+
+ _computeRowSizes(layout) {
+ let { rows, scale } = layout;
+ for (let i = 0; i < rows.length; i++) {
+ let row = rows[i];
+ row.width = row.fullWidth * scale + (row.windows.length - 1) * this._columnSpacing;
+ row.height = row.fullHeight * scale;
+ }
+ }
+
+ _keepSameRow(row, window, width, idealRowWidth) {
+ if (row.fullWidth + width <= idealRowWidth)
+ return true;
+
+ let oldRatio = row.fullWidth / idealRowWidth;
+ let newRatio = (row.fullWidth + width) / idealRowWidth;
+
+ if (Math.abs(1 - newRatio) < Math.abs(1 - oldRatio))
+ return true;
+
+ return false;
+ }
+
+ _sortRow(row) {
+ // Sort windows horizontally to minimize travel distance.
+ // This affects in what order the windows end up in a row.
+ row.windows.sort((a, b) => a.windowCenter.x - b.windowCenter.x);
+ }
+
+ computeLayout(windows, layoutParams) {
+ layoutParams = Params.parse(layoutParams, {
+ numRows: 0,
+ });
+
+ if (layoutParams.numRows === 0)
+ throw new Error(`${this.constructor.name}: No numRows given in layout params`);
+
+ const numRows = layoutParams.numRows;
+
+ let rows = [];
+ let totalWidth = 0;
+ for (let i = 0; i < windows.length; i++) {
+ let window = windows[i];
+ let s = this._computeWindowScale(window);
+ totalWidth += window.boundingBox.width * s;
+ }
+
+ let idealRowWidth = totalWidth / numRows;
+
+ // Sort windows vertically to minimize travel distance.
+ // This affects what rows the windows get placed in.
+ let sortedWindows = windows.slice();
+ sortedWindows.sort((a, b) => a.windowCenter.y - b.windowCenter.y);
+
+ let windowIdx = 0;
+ for (let i = 0; i < numRows; i++) {
+ let row = this._newRow();
+ rows.push(row);
+
+ for (; windowIdx < sortedWindows.length; windowIdx++) {
+ let window = sortedWindows[windowIdx];
+ let s = this._computeWindowScale(window);
+ let width = window.boundingBox.width * s;
+ let height = window.boundingBox.height * s;
+ row.fullHeight = Math.max(row.fullHeight, height);
+
+ // either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
+ if (this._keepSameRow(row, window, width, idealRowWidth) || (i === numRows - 1)) {
+ row.windows.push(window);
+ row.fullWidth += width;
+ } else {
+ break;
+ }
+ }
+ }
+
+ let gridHeight = 0;
+ let maxRow;
+ for (let i = 0; i < numRows; i++) {
+ let row = rows[i];
+ this._sortRow(row);
+
+ if (!maxRow || row.fullWidth > maxRow.fullWidth)
+ maxRow = row;
+ gridHeight += row.fullHeight;
+ }
+
+ return {
+ numRows,
+ rows,
+ maxColumns: maxRow.windows.length,
+ gridWidth: maxRow.fullWidth,
+ gridHeight,
+ };
+ }
+
+ computeScaleAndSpace(layout, area) {
+ let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
+ let vspacing = (layout.numRows - 1) * this._rowSpacing;
+
+ let spacedWidth = area.width - hspacing;
+ let spacedHeight = area.height - vspacing;
+
+ let horizontalScale = spacedWidth / layout.gridWidth;
+ let verticalScale = spacedHeight / layout.gridHeight;
+
+ // Thumbnails should be less than 70% of the original size
+ let scale = Math.min(
+ horizontalScale, verticalScale, WINDOW_PREVIEW_MAXIMUM_SCALE);
+
+ let scaledLayoutWidth = layout.gridWidth * scale + hspacing;
+ let scaledLayoutHeight = layout.gridHeight * scale + vspacing;
+ let space = (scaledLayoutWidth * scaledLayoutHeight) / (area.width * area.height);
+
+ layout.scale = scale;
+
+ return [scale, space];
+ }
+
+ computeWindowSlots(layout, area) {
+ this._computeRowSizes(layout);
+
+ let { rows, scale } = layout;
+
+ let slots = [];
+
+ // Do this in three parts.
+ let heightWithoutSpacing = 0;
+ for (let i = 0; i < rows.length; i++) {
+ let row = rows[i];
+ heightWithoutSpacing += row.height;
+ }
+
+ let verticalSpacing = (rows.length - 1) * this._rowSpacing;
+ let additionalVerticalScale = Math.min(1, (area.height - verticalSpacing) / heightWithoutSpacing);
+
+ // keep track how much smaller the grid becomes due to scaling
+ // so it can be centered again
+ let compensation = 0;
+ let y = 0;
+
+ for (let i = 0; i < rows.length; i++) {
+ let row = rows[i];
+
+ // If this window layout row doesn't fit in the actual
+ // geometry, then apply an additional scale to it.
+ let horizontalSpacing = (row.windows.length - 1) * this._columnSpacing;
+ let widthWithoutSpacing = row.width - horizontalSpacing;
+ let additionalHorizontalScale = Math.min(1, (area.width - horizontalSpacing) / widthWithoutSpacing);
+
+ if (additionalHorizontalScale < additionalVerticalScale) {
+ row.additionalScale = additionalHorizontalScale;
+ // Only consider the scaling in addition to the vertical scaling for centering.
+ compensation += (additionalVerticalScale - additionalHorizontalScale) * row.height;
+ } else {
+ row.additionalScale = additionalVerticalScale;
+ // No compensation when scaling vertically since centering based on a too large
+ // height would undo what vertical scaling is trying to achieve.
+ }
+
+ row.x = area.x + (Math.max(area.width - (widthWithoutSpacing * row.additionalScale + horizontalSpacing), 0) / 2);
+ row.y = area.y + (Math.max(area.height - (heightWithoutSpacing + verticalSpacing), 0) / 2) + y;
+ y += row.height * row.additionalScale + this._rowSpacing;
+ }
+
+ compensation /= 2;
+
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+ const rowY = row.y + compensation;
+ const rowHeight = row.height * row.additionalScale;
+
+ let x = row.x;
+ for (let j = 0; j < row.windows.length; j++) {
+ let window = row.windows[j];
+
+ let s = scale * this._computeWindowScale(window) * row.additionalScale;
+ let cellWidth = window.boundingBox.width * s;
+ let cellHeight = window.boundingBox.height * s;
+
+ s = Math.min(s, WINDOW_PREVIEW_MAXIMUM_SCALE);
+ let cloneWidth = window.boundingBox.width * s;
+ const cloneHeight = window.boundingBox.height * s;
+
+ let cloneX = x + (cellWidth - cloneWidth) / 2;
+ let cloneY;
+
+ // If there's only one row, align windows vertically centered inside the row
+ if (rows.length === 1)
+ cloneY = rowY + (rowHeight - cloneHeight) / 2;
+ // If there are multiple rows, align windows to the bottom edge of the row
+ else
+ cloneY = rowY + rowHeight - cellHeight;
+
+ // Align with the pixel grid to prevent blurry windows at scale = 1
+ cloneX = Math.floor(cloneX);
+ cloneY = Math.floor(cloneY);
+
+ slots.push([cloneX, cloneY, cloneWidth, cloneHeight, window]);
+ x += cellWidth + this._columnSpacing;
+ }
+ }
+ return slots;
+ }
+};
+
+function animateAllocation(actor, box) {
+ actor.save_easing_state();
+ actor.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
+ actor.set_easing_duration(200);
+
+ actor.allocate(box);
+
+ actor.restore_easing_state();
+
+ return actor.get_transition('allocation');
+}
+
+var WorkspaceLayout = GObject.registerClass({
+ Properties: {
+ 'spacing': GObject.ParamSpec.double(
+ 'spacing', 'Spacing', 'Spacing',
+ GObject.ParamFlags.READWRITE,
+ 0, Infinity, 20),
+ 'layout-frozen': GObject.ParamSpec.boolean(
+ 'layout-frozen', 'Layout frozen', 'Layout frozen',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+}, class WorkspaceLayout extends Clutter.LayoutManager {
+ _init(metaWorkspace, monitorIndex, overviewAdjustment) {
+ super._init();
+
+ this._spacing = 20;
+ this._layoutFrozen = false;
+
+ this._metaWorkspace = metaWorkspace;
+ this._monitorIndex = monitorIndex;
+ this._overviewAdjustment = overviewAdjustment;
+
+ this._container = null;
+ this._windows = new Map();
+ this._sortedWindows = [];
+ this._lastBox = null;
+ this._windowSlots = [];
+ this._layout = null;
+
+ this._needsLayout = true;
+
+ this._stateAdjustment = new St.Adjustment({
+ value: 0,
+ lower: 0,
+ upper: 1,
+ });
+
+ this._stateAdjustment.connect('notify::value', () => {
+ this._syncOpacities();
+ this.syncOverlays();
+ this.layout_changed();
+ });
+
+ this._workarea = null;
+ this._workareasChangedId = 0;
+ }
+
+ _syncOpacity(actor, metaWindow) {
+ if (!metaWindow.showing_on_its_workspace())
+ actor.opacity = this._stateAdjustment.value * 255;
+ }
+
+ _syncOpacities() {
+ this._windows.forEach(({ metaWindow }, actor) => {
+ this._syncOpacity(actor, metaWindow);
+ });
+ }
+
+ _isBetterScaleAndSpace(oldScale, oldSpace, scale, space) {
+ let spacePower = (space - oldSpace) * LAYOUT_SPACE_WEIGHT;
+ let scalePower = (scale - oldScale) * LAYOUT_SCALE_WEIGHT;
+
+ if (scale > oldScale && space > oldSpace) {
+ // Win win -- better scale and better space
+ return true;
+ } else if (scale > oldScale && space <= oldSpace) {
+ // Keep new layout only if scale gain outweighs aspect space loss
+ return scalePower > spacePower;
+ } else if (scale <= oldScale && space > oldSpace) {
+ // Keep new layout only if aspect space gain outweighs scale loss
+ return spacePower > scalePower;
+ } else {
+ // Lose -- worse scale and space
+ return false;
+ }
+ }
+
+ _adjustSpacingAndPadding(rowSpacing, colSpacing, containerBox) {
+ if (this._sortedWindows.length === 0)
+ return [rowSpacing, colSpacing, containerBox];
+
+ // All of the overlays have the same chrome sizes,
+ // so just pick the first one.
+ const window = this._sortedWindows[0];
+
+ const [topOversize, bottomOversize] = window.chromeHeights();
+ const [leftOversize, rightOversize] = window.chromeWidths();
+
+ const oversize =
+ Math.max(topOversize, bottomOversize, leftOversize, rightOversize);
+
+ if (rowSpacing !== null)
+ rowSpacing += oversize;
+ if (colSpacing !== null)
+ colSpacing += oversize;
+
+ if (containerBox) {
+ const monitor = Main.layoutManager.monitors[this._monitorIndex];
+
+ const bottomPoint = new Graphene.Point3D({ y: containerBox.y2 });
+ const transformedBottomPoint =
+ this._container.apply_transform_to_point(bottomPoint);
+ const bottomFreeSpace =
+ (monitor.y + monitor.height) - transformedBottomPoint.y;
+
+ const [, bottomOverlap] = window.overlapHeights();
+
+ if ((bottomOverlap + oversize) > bottomFreeSpace)
+ containerBox.y2 -= (bottomOverlap + oversize) - bottomFreeSpace;
+ }
+
+ return [rowSpacing, colSpacing, containerBox];
+ }
+
+ _createBestLayout(area) {
+ const [rowSpacing, columnSpacing] =
+ this._adjustSpacingAndPadding(this._spacing, this._spacing, null);
+
+ // We look for the largest scale that allows us to fit the
+ // largest row/tallest column on the workspace.
+ this._layoutStrategy = new UnalignedLayoutStrategy({
+ monitor: Main.layoutManager.monitors[this._monitorIndex],
+ rowSpacing,
+ columnSpacing,
+ });
+
+ let lastLayout = null;
+ let lastNumColumns = -1;
+ let lastScale = 0;
+ let lastSpace = 0;
+
+ for (let numRows = 1; ; numRows++) {
+ const numColumns = Math.ceil(this._sortedWindows.length / numRows);
+
+ // If adding a new row does not change column count just stop
+ // (for instance: 9 windows, with 3 rows -> 3 columns, 4 rows ->
+ // 3 columns as well => just use 3 rows then)
+ if (numColumns === lastNumColumns)
+ break;
+
+ const layout = this._layoutStrategy.computeLayout(this._sortedWindows, {
+ numRows,
+ });
+
+ const [scale, space] = this._layoutStrategy.computeScaleAndSpace(layout, area);
+
+ if (lastLayout && !this._isBetterScaleAndSpace(lastScale, lastSpace, scale, space))
+ break;
+
+ lastLayout = layout;
+ lastNumColumns = numColumns;
+ lastScale = scale;
+ lastSpace = space;
+ }
+
+ return lastLayout;
+ }
+
+ _getWindowSlots(containerBox) {
+ [, , containerBox] =
+ this._adjustSpacingAndPadding(null, null, containerBox);
+
+ const availArea = {
+ x: parseInt(containerBox.x1),
+ y: parseInt(containerBox.y1),
+ width: parseInt(containerBox.get_width()),
+ height: parseInt(containerBox.get_height()),
+ };
+
+ return this._layoutStrategy.computeWindowSlots(this._layout, availArea);
+ }
+
+ _getAdjustedWorkarea(container) {
+ const workarea = this._workarea.copy();
+
+ if (container instanceof St.Widget) {
+ const themeNode = container.get_theme_node();
+ workarea.width -= themeNode.get_horizontal_padding();
+ workarea.height -= themeNode.get_vertical_padding();
+ }
+
+ return workarea;
+ }
+
+ _syncWorkareaTracking() {
+ if (this._container) {
+ if (this._workAreaChangedId)
+ return;
+ this._workarea = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex);
+ this._workareasChangedId =
+ global.display.connect('workareas-changed', () => {
+ this._workarea = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex);
+ this.layout_changed();
+ });
+ } else if (this._workareasChangedId) {
+ global.display.disconnect(this._workareasChangedId);
+ this._workareasChangedId = 0;
+ }
+ }
+
+ vfunc_set_container(container) {
+ this._container = container;
+ this._syncWorkareaTracking();
+ this._stateAdjustment.actor = container;
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ const workarea = this._getAdjustedWorkarea(container);
+ if (forHeight === -1)
+ return [0, workarea.width];
+
+ const workAreaAspectRatio = workarea.width / workarea.height;
+ const widthPreservingAspectRatio = forHeight * workAreaAspectRatio;
+
+ return [0, widthPreservingAspectRatio];
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ const workarea = this._getAdjustedWorkarea(container);
+ if (forWidth === -1)
+ return [0, workarea.height];
+
+ const workAreaAspectRatio = workarea.width / workarea.height;
+ const heightPreservingAspectRatio = forWidth / workAreaAspectRatio;
+
+ return [0, heightPreservingAspectRatio];
+ }
+
+ vfunc_allocate(container, box) {
+ const containerBox = container.allocation;
+ const [containerWidth, containerHeight] = containerBox.get_size();
+ const containerAllocationChanged =
+ this._lastBox === null || !this._lastBox.equal(containerBox);
+
+ // If the containers size changed, we can no longer keep around
+ // the old windowSlots, so we must unfreeze the layout.
+ //
+ // However, if the overview animation is in progress, don't unfreeze
+ // the layout. This is needed to prevent windows "snapping" to their
+ // new positions during the overview closing animation when the
+ // allocation subtly expands every frame.
+ if (this._layoutFrozen && containerAllocationChanged && !Main.overview.animationInProgress) {
+ this._layoutFrozen = false;
+ this.notify('layout-frozen');
+ }
+
+ const { ControlsState } = OverviewControls;
+ const { currentState } =
+ this._overviewAdjustment.getStateTransitionParams();
+ const inSessionTransition = currentState <= ControlsState.WINDOW_PICKER;
+
+ const window = this._sortedWindows[0];
+
+ if (inSessionTransition || !window) {
+ container.remove_clip();
+ } else {
+ const [, bottomOversize] = window.chromeHeights();
+ const [containerX, containerY] = containerBox.get_origin();
+
+ const extraHeightProgress =
+ currentState - OverviewControls.ControlsState.WINDOW_PICKER;
+
+ const extraClipHeight = bottomOversize * (1 - extraHeightProgress);
+
+ container.set_clip(containerX, containerY,
+ containerWidth, containerHeight + extraClipHeight);
+ }
+
+ let layoutChanged = false;
+ if (!this._layoutFrozen || !this._lastBox) {
+ if (this._needsLayout) {
+ this._layout = this._createBestLayout(this._workarea);
+ this._needsLayout = false;
+ layoutChanged = true;
+ }
+
+ if (layoutChanged || containerAllocationChanged) {
+ this._windowSlotsBox = box.copy();
+ this._windowSlots = this._getWindowSlots(this._windowSlotsBox);
+ }
+ }
+
+ const slotsScale = box.get_width() / this._windowSlotsBox.get_width();
+ const workareaX = this._workarea.x;
+ const workareaY = this._workarea.y;
+ const workareaWidth = this._workarea.width;
+ const stateAdjustementValue = this._stateAdjustment.value;
+
+ const allocationScale = containerWidth / workareaWidth;
+
+ const childBox = new Clutter.ActorBox();
+
+ const nSlots = this._windowSlots.length;
+ for (let i = 0; i < nSlots; i++) {
+ let [x, y, width, height, child] = this._windowSlots[i];
+ if (!child.visible)
+ continue;
+
+ x *= slotsScale;
+ y *= slotsScale;
+ width *= slotsScale;
+ height *= slotsScale;
+
+ const windowInfo = this._windows.get(child);
+
+ let workspaceBoxX, workspaceBoxY;
+ let workspaceBoxWidth, workspaceBoxHeight;
+
+ if (windowInfo.metaWindow.showing_on_its_workspace()) {
+ workspaceBoxX = (child.boundingBox.x - workareaX) * allocationScale;
+ workspaceBoxY = (child.boundingBox.y - workareaY) * allocationScale;
+ workspaceBoxWidth = child.boundingBox.width * allocationScale;
+ workspaceBoxHeight = child.boundingBox.height * allocationScale;
+ } else {
+ workspaceBoxX = workareaX * allocationScale;
+ workspaceBoxY = workareaY * allocationScale;
+ workspaceBoxWidth = 0;
+ workspaceBoxHeight = 0;
+ }
+
+ // Don't allow the scaled floating size to drop below
+ // the target layout size.
+ // We only want to apply this when the scaled floating size is
+ // actually larger than the target layout size, that is while
+ // animating between the session and the window picker.
+ if (inSessionTransition) {
+ workspaceBoxWidth = Math.max(workspaceBoxWidth, width);
+ workspaceBoxHeight = Math.max(workspaceBoxHeight, height);
+ }
+
+ x = Util.lerp(workspaceBoxX, x, stateAdjustementValue);
+ y = Util.lerp(workspaceBoxY, y, stateAdjustementValue);
+ width = Util.lerp(workspaceBoxWidth, width, stateAdjustementValue);
+ height = Util.lerp(workspaceBoxHeight, height, stateAdjustementValue);
+
+ childBox.set_origin(x, y);
+ childBox.set_size(width, height);
+
+ if (windowInfo.currentTransition) {
+ windowInfo.currentTransition.get_interval().set_final(childBox);
+
+ // The timeline of the transition might not have been updated
+ // before this allocation cycle, so make sure the child
+ // still updates needs_allocation to FALSE.
+ // Unfortunately, this relies on the fast paths in
+ // clutter_actor_allocate(), otherwise we'd start a new
+ // transition on the child, replacing the current one.
+ child.allocate(child.allocation);
+ continue;
+ }
+
+ // We want layout changes (ie. larger changes to the layout like
+ // reshuffling the window order) to be animated, but small changes
+ // like changes to the container size to happen immediately (for
+ // example if the container height is being animated, we want to
+ // avoid animating the children allocations to make sure they
+ // don't "lag behind" the other animation).
+ if (layoutChanged && !Main.overview.animationInProgress) {
+ const transition = animateAllocation(child, childBox);
+ if (transition) {
+ windowInfo.currentTransition = transition;
+ windowInfo.currentTransition.connect('stopped', () => {
+ windowInfo.currentTransition = null;
+ });
+ }
+ } else {
+ child.allocate(childBox);
+ }
+ }
+
+ this._lastBox = containerBox.copy();
+ }
+
+ _syncOverlay(preview) {
+ const active = this._metaWorkspace?.active ?? true;
+ preview.overlayEnabled = active && this._stateAdjustment.value === 1;
+ }
+
+ /**
+ * syncOverlays:
+ *
+ * Synchronizes the overlay state of all window previews.
+ */
+ syncOverlays() {
+ [...this._windows.keys()].forEach(preview => this._syncOverlay(preview));
+ }
+
+ /**
+ * addWindow:
+ * @param {WindowPreview} window: the window to add
+ * @param {Meta.Window} metaWindow: the MetaWindow of the window
+ *
+ * Adds @window to the workspace, it will be shown immediately if
+ * the layout isn't frozen using the layout-frozen property.
+ *
+ * If @window is already part of the workspace, nothing will happen.
+ */
+ addWindow(window, metaWindow) {
+ if (this._windows.has(window))
+ return;
+
+ this._windows.set(window, {
+ metaWindow,
+ sizeChangedId: metaWindow.connect('size-changed', () => {
+ this._needsLayout = true;
+ this.layout_changed();
+ }),
+ destroyId: window.connect('destroy', () =>
+ this.removeWindow(window)),
+ currentTransition: null,
+ });
+
+ this._sortedWindows.push(window);
+ this._sortedWindows.sort((a, b) => {
+ const winA = this._windows.get(a).metaWindow;
+ const winB = this._windows.get(b).metaWindow;
+
+ return winA.get_stable_sequence() - winB.get_stable_sequence();
+ });
+
+ this._syncOpacity(window, metaWindow);
+ this._syncOverlay(window);
+ this._container.add_child(window);
+
+ this._needsLayout = true;
+ this.layout_changed();
+ }
+
+ /**
+ * removeWindow:
+ * @param {WindowPreview} window: the window to remove
+ *
+ * Removes @window from the workspace if @window is a part of the
+ * workspace. If the layout-frozen property is set to true, the
+ * window will still be visible until the property is set to false.
+ */
+ removeWindow(window) {
+ const windowInfo = this._windows.get(window);
+ if (!windowInfo)
+ return;
+
+ windowInfo.metaWindow.disconnect(windowInfo.sizeChangedId);
+ window.disconnect(windowInfo.destroyId);
+ if (windowInfo.currentTransition)
+ window.remove_transition('allocation');
+
+ this._windows.delete(window);
+ this._sortedWindows.splice(this._sortedWindows.indexOf(window), 1);
+
+ // The layout might be frozen and we might not update the windowSlots
+ // on the next allocation, so remove the slot now already
+ const index = this._windowSlots.findIndex(s => s[4] === window);
+ if (index !== -1)
+ this._windowSlots.splice(index, 1);
+
+ // The window might have been reparented by DND
+ if (window.get_parent() === this._container)
+ this._container.remove_child(window);
+
+ this._needsLayout = true;
+ this.layout_changed();
+ }
+
+ syncStacking(stackIndices) {
+ const windows = [...this._windows.keys()];
+ windows.sort((a, b) => {
+ const seqA = this._windows.get(a).metaWindow.get_stable_sequence();
+ const seqB = this._windows.get(b).metaWindow.get_stable_sequence();
+
+ return stackIndices[seqA] - stackIndices[seqB];
+ });
+
+ let lastWindow = null;
+ for (const window of windows) {
+ window.setStackAbove(lastWindow);
+ lastWindow = window;
+ }
+
+ this._needsLayout = true;
+ this.layout_changed();
+ }
+
+ /**
+ * getFocusChain:
+ *
+ * Gets the focus chain of the workspace. This function will return
+ * an empty array if the floating window layout is used.
+ *
+ * @returns {Array} an array of {Clutter.Actor}s
+ */
+ getFocusChain() {
+ if (this._stateAdjustment.value === 0)
+ return [];
+
+ // The fifth element in the slot array is the WindowPreview
+ return this._windowSlots.map(s => s[4]);
+ }
+
+ /**
+ * An StAdjustment for controlling and transitioning between
+ * the alignment of windows using the layout strategy and the
+ * floating window layout.
+ *
+ * A value of 0 of the adjustment completely uses the floating
+ * window layout while a value of 1 completely aligns windows using
+ * the layout strategy.
+ *
+ * @type {St.Adjustment}
+ */
+ get stateAdjustment() {
+ return this._stateAdjustment;
+ }
+
+ get spacing() {
+ return this._spacing;
+ }
+
+ set spacing(s) {
+ if (this._spacing === s)
+ return;
+
+ this._spacing = s;
+
+ this._needsLayout = true;
+ this.notify('spacing');
+ this.layout_changed();
+ }
+
+ get layoutFrozen() {
+ return this._layoutFrozen;
+ }
+
+ set layoutFrozen(f) {
+ if (this._layoutFrozen === f)
+ return;
+
+ this._layoutFrozen = f;
+
+ this.notify('layout-frozen');
+ if (!this._layoutFrozen)
+ this.layout_changed();
+ }
+});
+
+var WorkspaceBackground = GObject.registerClass(
+class WorkspaceBackground extends Shell.WorkspaceBackground {
+ _init(monitorIndex, stateAdjustment) {
+ super._init({
+ style_class: 'workspace-background',
+ x_expand: true,
+ y_expand: true,
+ monitor_index: monitorIndex,
+ });
+
+ this._monitorIndex = monitorIndex;
+ this._workarea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
+
+ this._stateAdjustment = stateAdjustment;
+ this._stateAdjustment.connectObject('notify::value', () => {
+ this._updateBorderRadius();
+ this.queue_relayout();
+ }, this);
+ this._stateAdjustment.bind_property(
+ 'value', this, 'state-adjustment-value',
+ GObject.BindingFlags.SYNC_CREATE
+ );
+
+ this._bin = new Clutter.Actor({
+ layout_manager: new Clutter.BinLayout(),
+ clip_to_allocation: true,
+ });
+
+ this._backgroundGroup = new Meta.BackgroundGroup({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true,
+ y_expand: true,
+ });
+ this._bin.add_child(this._backgroundGroup);
+ this.add_child(this._bin);
+
+ this._bgManager = new Background.BackgroundManager({
+ container: this._backgroundGroup,
+ monitorIndex: this._monitorIndex,
+ controlPosition: false,
+ useContentSize: false,
+ });
+
+ this._bgManager.connect('changed', () => {
+ this._updateRoundedClipBounds();
+ this._updateBorderRadius();
+ });
+
+ global.display.connectObject('workareas-changed', () => {
+ this._workarea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
+ this._updateRoundedClipBounds();
+ this.queue_relayout();
+ }, this);
+ this._updateRoundedClipBounds();
+
+ this._updateBorderRadius();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _updateBorderRadius() {
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ const cornerRadius = scaleFactor * BACKGROUND_CORNER_RADIUS_PIXELS;
+
+ const backgroundContent = this._bgManager.backgroundActor.content;
+ backgroundContent.rounded_clip_radius =
+ Util.lerp(0, cornerRadius, this._stateAdjustment.value);
+ }
+
+ _updateRoundedClipBounds() {
+ const monitor = Main.layoutManager.monitors[this._monitorIndex];
+
+ const rect = new Graphene.Rect();
+ rect.origin.x = this._workarea.x - monitor.x;
+ rect.origin.y = this._workarea.y - monitor.y;
+ rect.size.width = this._workarea.width;
+ rect.size.height = this._workarea.height;
+
+ this._bgManager.backgroundActor.content.set_rounded_clip_bounds(rect);
+ }
+
+ _onDestroy() {
+ if (this._bgManager) {
+ this._bgManager.destroy();
+ this._bgManager = null;
+ }
+ }
+});
+
+/**
+ * @metaWorkspace: a #Meta.Workspace, or null
+ */
+var Workspace = GObject.registerClass(
+class Workspace extends St.Widget {
+ _init(metaWorkspace, monitorIndex, overviewAdjustment) {
+ super._init({
+ style_class: 'window-picker',
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ layout_manager: new Clutter.BinLayout(),
+ });
+
+ const layoutManager = new WorkspaceLayout(metaWorkspace, monitorIndex,
+ overviewAdjustment);
+
+ // Background
+ this._background =
+ new WorkspaceBackground(monitorIndex, layoutManager.stateAdjustment);
+ this.add_child(this._background);
+
+ // Window previews
+ this._container = new Clutter.Actor({
+ reactive: true,
+ x_expand: true,
+ y_expand: true,
+ });
+ this._container.layout_manager = layoutManager;
+ this.add_child(this._container);
+
+ this.metaWorkspace = metaWorkspace;
+
+ this._overviewAdjustment = overviewAdjustment;
+
+ this.monitorIndex = monitorIndex;
+ this._monitor = Main.layoutManager.monitors[this.monitorIndex];
+
+ if (monitorIndex != Main.layoutManager.primaryIndex)
+ this.add_style_class_name('external-monitor');
+
+ const clickAction = new Clutter.ClickAction();
+ clickAction.connect('clicked', action => {
+ // Switch to the workspace when not the active one, leave the
+ // overview otherwise.
+ if (action.get_button() === 1 || action.get_button() === 0) {
+ const leaveOverview = this._shouldLeaveOverview();
+
+ this.metaWorkspace?.activate(global.get_current_time());
+ if (leaveOverview)
+ Main.overview.hide();
+ }
+ });
+ this.bind_property('mapped', clickAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
+ this._container.add_action(clickAction);
+
+ this.connect('style-changed', this._onStyleChanged.bind(this));
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._skipTaskbarSignals = new Map();
+ const windows = global.get_window_actors().map(a => a.meta_window)
+ .filter(this._isMyWindow, this);
+
+ // Create clones for windows that should be
+ // visible in the Overview
+ this._windows = [];
+ for (let i = 0; i < windows.length; i++) {
+ if (this._isOverviewWindow(windows[i]))
+ this._addWindowClone(windows[i]);
+ }
+
+ // Track window changes, but let the window tracker process them first
+ this.metaWorkspace?.connectObject(
+ 'window-added', this._windowAdded.bind(this), GObject.ConnectFlags.AFTER,
+ 'window-removed', this._windowRemoved.bind(this), GObject.ConnectFlags.AFTER,
+ 'notify::active', () => layoutManager.syncOverlays(), this);
+ global.display.connectObject(
+ 'window-entered-monitor', this._windowEnteredMonitor.bind(this), GObject.ConnectFlags.AFTER,
+ 'window-left-monitor', this._windowLeftMonitor.bind(this), GObject.ConnectFlags.AFTER,
+ this);
+ this._layoutFrozenId = 0;
+
+ // DND requires this to be set
+ this._delegate = this;
+ }
+
+ _shouldLeaveOverview() {
+ if (!this.metaWorkspace || this.metaWorkspace.active)
+ return true;
+
+ const overviewState = this._overviewAdjustment.value;
+ return overviewState > OverviewControls.ControlsState.WINDOW_PICKER;
+ }
+
+ vfunc_get_focus_chain() {
+ return this._container.layout_manager.getFocusChain();
+ }
+
+ _lookupIndex(metaWindow) {
+ return this._windows.findIndex(w => w.metaWindow == metaWindow);
+ }
+
+ containsMetaWindow(metaWindow) {
+ return this._lookupIndex(metaWindow) >= 0;
+ }
+
+ isEmpty() {
+ return this._windows.length == 0;
+ }
+
+ syncStacking(stackIndices) {
+ this._container.layout_manager.syncStacking(stackIndices);
+ }
+
+ _doRemoveWindow(metaWin) {
+ let clone = this._removeWindowClone(metaWin);
+
+ if (!clone)
+ return;
+
+ clone.destroy();
+
+ // We need to reposition the windows; to avoid shuffling windows
+ // around while the user is interacting with the workspace, we delay
+ // the positioning until the pointer remains still for at least 750 ms
+ // or is moved outside the workspace
+ this._container.layout_manager.layout_frozen = true;
+
+ if (this._layoutFrozenId > 0) {
+ GLib.source_remove(this._layoutFrozenId);
+ this._layoutFrozenId = 0;
+ }
+
+ let [oldX, oldY] = global.get_pointer();
+
+ this._layoutFrozenId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ WINDOW_REPOSITIONING_DELAY,
+ () => {
+ const [newX, newY] = global.get_pointer();
+ const pointerHasMoved = oldX !== newX || oldY !== newY;
+ const actorUnderPointer = global.stage.get_actor_at_pos(
+ Clutter.PickMode.REACTIVE, newX, newY);
+
+ if ((pointerHasMoved && this.contains(actorUnderPointer)) ||
+ this._windows.some(w => w.contains(actorUnderPointer))) {
+ oldX = newX;
+ oldY = newY;
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ this._container.layout_manager.layout_frozen = false;
+ this._layoutFrozenId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+
+ GLib.Source.set_name_by_id(this._layoutFrozenId,
+ '[gnome-shell] this._layoutFrozenId');
+ }
+
+ _doAddWindow(metaWin) {
+ let win = metaWin.get_compositor_private();
+
+ if (!win) {
+ // Newly-created windows are added to a workspace before
+ // the compositor finds out about them...
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ if (metaWin.get_compositor_private() &&
+ metaWin.get_workspace() == this.metaWorkspace)
+ this._doAddWindow(metaWin);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] this._doAddWindow');
+ return;
+ }
+
+ // We might have the window in our list already if it was on all workspaces and
+ // now was moved to this workspace
+ if (this._lookupIndex(metaWin) != -1)
+ return;
+
+ if (!this._isMyWindow(metaWin))
+ return;
+
+ this._skipTaskbarSignals.set(metaWin,
+ metaWin.connect('notify::skip-taskbar', () => {
+ if (metaWin.skip_taskbar)
+ this._doRemoveWindow(metaWin);
+ else
+ this._doAddWindow(metaWin);
+ }));
+
+ if (!this._isOverviewWindow(metaWin)) {
+ if (metaWin.get_transient_for() == null)
+ return;
+
+ // Let the top-most ancestor handle all transients
+ let parent = metaWin.find_root_ancestor();
+ let clone = this._windows.find(c => c.metaWindow == parent);
+
+ // If no clone was found, the parent hasn't been created yet
+ // and will take care of the dialog when added
+ if (clone)
+ clone.addDialog(metaWin);
+
+ return;
+ }
+
+ const clone = this._addWindowClone(metaWin);
+
+ clone.set_pivot_point(0.5, 0.5);
+ clone.scale_x = 0;
+ clone.scale_y = 0;
+ clone.ease({
+ scale_x: 1,
+ scale_y: 1,
+ duration: 250,
+ onStopped: () => clone.set_pivot_point(0, 0),
+ });
+
+ if (this._layoutFrozenId > 0) {
+ // If a window was closed before, unfreeze the layout to ensure
+ // the new window is immediately shown
+ this._container.layout_manager.layout_frozen = false;
+
+ GLib.source_remove(this._layoutFrozenId);
+ this._layoutFrozenId = 0;
+ }
+ }
+
+ _windowAdded(metaWorkspace, metaWin) {
+ if (!Main.overview.closing)
+ this._doAddWindow(metaWin);
+ }
+
+ _windowRemoved(metaWorkspace, metaWin) {
+ this._doRemoveWindow(metaWin);
+ }
+
+ _windowEnteredMonitor(metaDisplay, monitorIndex, metaWin) {
+ if (monitorIndex === this.monitorIndex && !Main.overview.closing)
+ this._doAddWindow(metaWin);
+ }
+
+ _windowLeftMonitor(metaDisplay, monitorIndex, metaWin) {
+ if (monitorIndex == this.monitorIndex)
+ this._doRemoveWindow(metaWin);
+ }
+
+ // check for maximized windows on the workspace
+ hasMaximizedWindows() {
+ for (let i = 0; i < this._windows.length; i++) {
+ let metaWindow = this._windows[i].metaWindow;
+ if (metaWindow.showing_on_its_workspace() &&
+ metaWindow.maximized_horizontally &&
+ metaWindow.maximized_vertically)
+ return true;
+ }
+ return false;
+ }
+
+ _clearSkipTaskbarSignals() {
+ for (const [metaWin, id] of this._skipTaskbarSignals)
+ metaWin.disconnect(id);
+ this._skipTaskbarSignals.clear();
+ }
+
+ prepareToLeaveOverview() {
+ this._clearSkipTaskbarSignals();
+
+ for (let i = 0; i < this._windows.length; i++)
+ this._windows[i].remove_all_transitions();
+
+ if (this._layoutFrozenId > 0) {
+ GLib.source_remove(this._layoutFrozenId);
+ this._layoutFrozenId = 0;
+ }
+
+ this._container.layout_manager.layout_frozen = true;
+ Main.overview.connectObject(
+ 'hidden', this._doneLeavingOverview.bind(this), this);
+ }
+
+ _onDestroy() {
+ this._clearSkipTaskbarSignals();
+
+ if (this._layoutFrozenId > 0) {
+ GLib.source_remove(this._layoutFrozenId);
+ this._layoutFrozenId = 0;
+ }
+
+ this._windows = [];
+ }
+
+ _doneLeavingOverview() {
+ this._container.layout_manager.layout_frozen = false;
+ }
+
+ _doneShowingOverview() {
+ this._container.layout_manager.layout_frozen = false;
+ }
+
+ _isMyWindow(window) {
+ const isOnWorkspace = this.metaWorkspace === null ||
+ window.located_on_workspace(this.metaWorkspace);
+ const isOnMonitor = window.get_monitor() === this.monitorIndex;
+
+ return isOnWorkspace && isOnMonitor;
+ }
+
+ _isOverviewWindow(window) {
+ return !window.skip_taskbar;
+ }
+
+ // Create a clone of a (non-desktop) window and add it to the window list
+ _addWindowClone(metaWindow) {
+ let clone = new WindowPreview(metaWindow, this, this._overviewAdjustment);
+
+ clone.connect('selected',
+ this._onCloneSelected.bind(this));
+ clone.connect('drag-begin', () => {
+ Main.overview.beginWindowDrag(metaWindow);
+ });
+ clone.connect('drag-cancelled', () => {
+ Main.overview.cancelledWindowDrag(metaWindow);
+ });
+ clone.connect('drag-end', () => {
+ Main.overview.endWindowDrag(metaWindow);
+ });
+ clone.connect('show-chrome', () => {
+ let focus = global.stage.key_focus;
+ if (focus == null || this.contains(focus))
+ clone.grab_key_focus();
+
+ this._windows.forEach(c => {
+ if (c !== clone)
+ c.hideOverlay(true);
+ });
+ });
+ clone.connect('destroy', () => {
+ this._doRemoveWindow(metaWindow);
+ });
+
+ this._container.layout_manager.addWindow(clone, metaWindow);
+
+ if (this._windows.length == 0)
+ clone.setStackAbove(null);
+ else
+ clone.setStackAbove(this._windows[this._windows.length - 1]);
+
+ this._windows.push(clone);
+
+ return clone;
+ }
+
+ _removeWindowClone(metaWin) {
+ // find the position of the window in our list
+ let index = this._lookupIndex(metaWin);
+
+ if (index == -1)
+ return null;
+
+ this._container.layout_manager.removeWindow(this._windows[index]);
+
+ return this._windows.splice(index, 1).pop();
+ }
+
+ _onStyleChanged() {
+ const themeNode = this.get_theme_node();
+ this._container.layout_manager.spacing = themeNode.get_length('spacing');
+ }
+
+ _onCloneSelected(clone, time) {
+ const wsIndex = this.metaWorkspace?.index();
+
+ if (this._shouldLeaveOverview())
+ Main.activateWindow(clone.metaWindow, time, wsIndex);
+ else
+ this.metaWorkspace?.activate(time);
+ }
+
+ // Draggable target interface
+ handleDragOver(source, _actor, _x, _y, _time) {
+ if (source.metaWindow && !this._isMyWindow(source.metaWindow))
+ return DND.DragMotionResult.MOVE_DROP;
+ if (source.app && source.app.can_open_new_window())
+ return DND.DragMotionResult.COPY_DROP;
+ if (!source.app && source.shellWorkspaceLaunch)
+ return DND.DragMotionResult.COPY_DROP;
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ acceptDrop(source, actor, x, y, time) {
+ let workspaceManager = global.workspace_manager;
+ let workspaceIndex = this.metaWorkspace
+ ? this.metaWorkspace.index()
+ : workspaceManager.get_active_workspace_index();
+
+ if (source.metaWindow) {
+ const window = source.metaWindow;
+ if (this._isMyWindow(window))
+ return false;
+
+ Main.moveWindowToMonitorAndWorkspace(window,
+ this.monitorIndex, workspaceIndex);
+ return true;
+ } else if (source.app && source.app.can_open_new_window()) {
+ if (source.animateLaunchAtPos)
+ source.animateLaunchAtPos(actor.x, actor.y);
+
+ source.app.open_new_window(workspaceIndex);
+ return true;
+ } else if (!source.app && source.shellWorkspaceLaunch) {
+ // While unused in our own drag sources, shellWorkspaceLaunch allows
+ // extensions to define custom actions for their drag sources.
+ source.shellWorkspaceLaunch({
+ workspace: workspaceIndex,
+ timestamp: time,
+ });
+ return true;
+ }
+
+ return false;
+ }
+
+ get stateAdjustment() {
+ return this._container.layout_manager.stateAdjustment;
+ }
+});
diff --git a/js/ui/workspaceAnimation.js b/js/ui/workspaceAnimation.js
new file mode 100644
index 0000000..432044f
--- /dev/null
+++ b/js/ui/workspaceAnimation.js
@@ -0,0 +1,496 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WorkspaceAnimationController, WorkspaceGroup */
+
+const { Clutter, GObject, Meta, Shell, St } = imports.gi;
+
+const Background = imports.ui.background;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const SwipeTracker = imports.ui.swipeTracker;
+
+const WINDOW_ANIMATION_TIME = 250;
+const WORKSPACE_SPACING = 100;
+
+var WorkspaceGroup = GObject.registerClass(
+class WorkspaceGroup extends Clutter.Actor {
+ _init(workspace, monitor, movingWindow) {
+ super._init();
+
+ this._workspace = workspace;
+ this._monitor = monitor;
+ this._movingWindow = movingWindow;
+ this._windowRecords = [];
+
+ if (this._workspace) {
+ this._background = new Meta.BackgroundGroup();
+
+ this.add_actor(this._background);
+
+ this._bgManager = new Background.BackgroundManager({
+ container: this._background,
+ monitorIndex: this._monitor.index,
+ controlPosition: false,
+ });
+ }
+
+ this.width = monitor.width;
+ this.height = monitor.height;
+ this.clip_to_allocation = true;
+
+ this._createWindows();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ global.display.connectObject('restacked',
+ this._syncStacking.bind(this), this);
+ }
+
+ get workspace() {
+ return this._workspace;
+ }
+
+ _shouldShowWindow(window) {
+ if (!window.showing_on_its_workspace())
+ return false;
+
+ const geometry = global.display.get_monitor_geometry(this._monitor.index);
+ const [intersects] = window.get_frame_rect().intersect(geometry);
+ if (!intersects)
+ return false;
+
+ const isSticky =
+ window.is_on_all_workspaces() || window === this._movingWindow;
+
+ // No workspace means we should show windows that are on all workspaces
+ if (!this._workspace)
+ return isSticky;
+
+ // Otherwise only show windows that are (only) on that workspace
+ return !isSticky && window.located_on_workspace(this._workspace);
+ }
+
+ _syncStacking() {
+ const windowActors = global.get_window_actors().filter(w =>
+ this._shouldShowWindow(w.meta_window));
+
+ let lastRecord;
+ const bottomActor = this._background ?? null;
+
+ for (const windowActor of windowActors) {
+ const record = this._windowRecords.find(r => r.windowActor === windowActor);
+
+ this.set_child_above_sibling(record.clone,
+ lastRecord ? lastRecord.clone : bottomActor);
+ lastRecord = record;
+ }
+ }
+
+ _createWindows() {
+ const windowActors = global.get_window_actors().filter(w =>
+ this._shouldShowWindow(w.meta_window));
+
+ for (const windowActor of windowActors) {
+ const clone = new Clutter.Clone({
+ source: windowActor,
+ x: windowActor.x - this._monitor.x,
+ y: windowActor.y - this._monitor.y,
+ });
+
+ this.add_child(clone);
+
+ const record = { windowActor, clone };
+
+ windowActor.connectObject('destroy', () => {
+ clone.destroy();
+ this._windowRecords.splice(this._windowRecords.indexOf(record), 1);
+ }, this);
+
+ this._windowRecords.push(record);
+ }
+ }
+
+ _removeWindows() {
+ for (const record of this._windowRecords)
+ record.clone.destroy();
+
+ this._windowRecords = [];
+ }
+
+ _onDestroy() {
+ this._removeWindows();
+
+ if (this._workspace)
+ this._bgManager.destroy();
+ }
+});
+
+const MonitorGroup = GObject.registerClass({
+ Properties: {
+ 'progress': GObject.ParamSpec.double(
+ 'progress', 'progress', 'progress',
+ GObject.ParamFlags.READWRITE,
+ -Infinity, Infinity, 0),
+ },
+}, class MonitorGroup extends St.Widget {
+ _init(monitor, workspaceIndices, movingWindow) {
+ super._init({
+ clip_to_allocation: true,
+ style_class: 'workspace-animation',
+ });
+
+ this._monitor = monitor;
+
+ const constraint = new Layout.MonitorConstraint({ index: monitor.index });
+ this.add_constraint(constraint);
+
+ this._container = new Clutter.Actor();
+ this.add_child(this._container);
+
+ const stickyGroup = new WorkspaceGroup(null, monitor, movingWindow);
+ this.add_child(stickyGroup);
+
+ this._workspaceGroups = [];
+
+ const workspaceManager = global.workspace_manager;
+ const vertical = workspaceManager.layout_rows === -1;
+ const activeWorkspace = workspaceManager.get_active_workspace();
+
+ let x = 0;
+ let y = 0;
+
+ for (const i of workspaceIndices) {
+ const ws = workspaceManager.get_workspace_by_index(i);
+ const fullscreen = ws.list_windows().some(w => w.get_monitor() === monitor.index && w.is_fullscreen());
+
+ if (i > 0 && vertical && !fullscreen && monitor.index === Main.layoutManager.primaryIndex) {
+ // We have to shift windows up or down by the height of the panel to prevent having a
+ // visible gap between the windows while switching workspaces. Since fullscreen windows
+ // hide the panel, they don't need to be shifted up or down.
+ y -= Main.panel.height;
+ }
+
+ const group = new WorkspaceGroup(ws, monitor, movingWindow);
+
+ this._workspaceGroups.push(group);
+ this._container.add_child(group);
+ group.set_position(x, y);
+
+ if (vertical)
+ y += this.baseDistance;
+ else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ x -= this.baseDistance;
+ else
+ x += this.baseDistance;
+ }
+
+ this.progress = this.getWorkspaceProgress(activeWorkspace);
+ }
+
+ get baseDistance() {
+ const spacing = WORKSPACE_SPACING * St.ThemeContext.get_for_stage(global.stage).scale_factor;
+
+ if (global.workspace_manager.layout_rows === -1)
+ return this._monitor.height + spacing;
+ else
+ return this._monitor.width + spacing;
+ }
+
+ get progress() {
+ if (global.workspace_manager.layout_rows === -1)
+ return -this._container.y / this.baseDistance;
+ else if (this.get_text_direction() === Clutter.TextDirection.RTL)
+ return this._container.x / this.baseDistance;
+ else
+ return -this._container.x / this.baseDistance;
+ }
+
+ set progress(p) {
+ if (global.workspace_manager.layout_rows === -1)
+ this._container.y = -Math.round(p * this.baseDistance);
+ else if (this.get_text_direction() === Clutter.TextDirection.RTL)
+ this._container.x = Math.round(p * this.baseDistance);
+ else
+ this._container.x = -Math.round(p * this.baseDistance);
+ }
+
+ get index() {
+ return this._monitor.index;
+ }
+
+ getWorkspaceProgress(workspace) {
+ const group = this._workspaceGroups.find(g =>
+ g.workspace.index() === workspace.index());
+ return this._getWorkspaceGroupProgress(group);
+ }
+
+ _getWorkspaceGroupProgress(group) {
+ if (global.workspace_manager.layout_rows === -1)
+ return group.y / this.baseDistance;
+ else if (this.get_text_direction() === Clutter.TextDirection.RTL)
+ return -group.x / this.baseDistance;
+ else
+ return group.x / this.baseDistance;
+ }
+
+ getSnapPoints() {
+ return this._workspaceGroups.map(g =>
+ this._getWorkspaceGroupProgress(g));
+ }
+
+ findClosestWorkspace(progress) {
+ const distances = this.getSnapPoints().map(p =>
+ Math.abs(p - progress));
+ const index = distances.indexOf(Math.min(...distances));
+ return this._workspaceGroups[index].workspace;
+ }
+
+ _interpolateProgress(progress, monitorGroup) {
+ if (this.index === monitorGroup.index)
+ return progress;
+
+ const points1 = monitorGroup.getSnapPoints();
+ const points2 = this.getSnapPoints();
+
+ const upper = points1.indexOf(points1.find(p => p >= progress));
+ const lower = points1.indexOf(points1.slice().reverse().find(p => p <= progress));
+
+ if (points1[upper] === points1[lower])
+ return points2[upper];
+
+ const t = (progress - points1[lower]) / (points1[upper] - points1[lower]);
+
+ return points2[lower] + (points2[upper] - points2[lower]) * t;
+ }
+
+ updateSwipeForMonitor(progress, monitorGroup) {
+ this.progress = this._interpolateProgress(progress, monitorGroup);
+ }
+});
+
+var WorkspaceAnimationController = class {
+ constructor() {
+ this._movingWindow = null;
+ this._switchData = null;
+
+ Main.overview.connect('showing', () => {
+ if (this._switchData) {
+ if (this._switchData.gestureActivated)
+ this._finishWorkspaceSwitch(this._switchData);
+ this._swipeTracker.enabled = false;
+ }
+ });
+ Main.overview.connect('hiding', () => {
+ this._swipeTracker.enabled = true;
+ });
+
+ const swipeTracker = new SwipeTracker.SwipeTracker(global.stage,
+ Clutter.Orientation.HORIZONTAL,
+ Shell.ActionMode.NORMAL,
+ { allowDrag: false });
+ swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this));
+ swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this));
+ swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this));
+ this._swipeTracker = swipeTracker;
+
+ global.display.bind_property('compositor-modifiers',
+ this._swipeTracker, 'scroll-modifiers',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ _prepareWorkspaceSwitch(workspaceIndices) {
+ if (this._switchData)
+ return;
+
+ const workspaceManager = global.workspace_manager;
+ const nWorkspaces = workspaceManager.get_n_workspaces();
+
+ const switchData = {};
+
+ this._switchData = switchData;
+ switchData.monitors = [];
+
+ switchData.gestureActivated = false;
+ switchData.inProgress = false;
+
+ if (!workspaceIndices)
+ workspaceIndices = [...Array(nWorkspaces).keys()];
+
+ const monitors = Meta.prefs_get_workspaces_only_on_primary()
+ ? [Main.layoutManager.primaryMonitor] : Main.layoutManager.monitors;
+
+ for (const monitor of monitors) {
+ if (Meta.prefs_get_workspaces_only_on_primary() &&
+ monitor.index !== Main.layoutManager.primaryIndex)
+ continue;
+
+ const group = new MonitorGroup(monitor, workspaceIndices, this.movingWindow);
+
+ Main.uiGroup.insert_child_above(group, global.window_group);
+
+ switchData.monitors.push(group);
+ }
+
+ Meta.disable_unredirect_for_display(global.display);
+ }
+
+ _finishWorkspaceSwitch(switchData) {
+ Meta.enable_unredirect_for_display(global.display);
+
+ this._switchData = null;
+
+ switchData.monitors.forEach(m => m.destroy());
+
+ this.movingWindow = null;
+ }
+
+ animateSwitch(from, to, direction, onComplete) {
+ this._swipeTracker.enabled = false;
+
+ let workspaceIndices = [];
+
+ switch (direction) {
+ case Meta.MotionDirection.UP:
+ case Meta.MotionDirection.LEFT:
+ case Meta.MotionDirection.UP_LEFT:
+ case Meta.MotionDirection.UP_RIGHT:
+ workspaceIndices = [to, from];
+ break;
+
+ case Meta.MotionDirection.DOWN:
+ case Meta.MotionDirection.RIGHT:
+ case Meta.MotionDirection.DOWN_LEFT:
+ case Meta.MotionDirection.DOWN_RIGHT:
+ workspaceIndices = [from, to];
+ break;
+ }
+
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL &&
+ direction !== Meta.MotionDirection.UP &&
+ direction !== Meta.MotionDirection.DOWN)
+ workspaceIndices.reverse();
+
+ this._prepareWorkspaceSwitch(workspaceIndices);
+ this._switchData.inProgress = true;
+
+ const fromWs = global.workspace_manager.get_workspace_by_index(from);
+ const toWs = global.workspace_manager.get_workspace_by_index(to);
+
+ for (const monitorGroup of this._switchData.monitors) {
+ monitorGroup.progress = monitorGroup.getWorkspaceProgress(fromWs);
+ const progress = monitorGroup.getWorkspaceProgress(toWs);
+
+ const params = {
+ duration: WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ };
+
+ if (monitorGroup.index === Main.layoutManager.primaryIndex) {
+ params.onComplete = () => {
+ this._finishWorkspaceSwitch(this._switchData);
+ onComplete();
+ this._swipeTracker.enabled = true;
+ };
+ }
+
+ monitorGroup.ease_property('progress', progress, params);
+ }
+ }
+
+ canHandleScrollEvent(event) {
+ return this._swipeTracker.canHandleScrollEvent(event);
+ }
+
+ _findMonitorGroup(monitorIndex) {
+ return this._switchData.monitors.find(m => m.index === monitorIndex);
+ }
+
+ _switchWorkspaceBegin(tracker, monitor) {
+ if (Meta.prefs_get_workspaces_only_on_primary() &&
+ monitor !== Main.layoutManager.primaryIndex)
+ return;
+
+ const workspaceManager = global.workspace_manager;
+ const horiz = workspaceManager.layout_rows !== -1;
+ tracker.orientation = horiz
+ ? Clutter.Orientation.HORIZONTAL
+ : Clutter.Orientation.VERTICAL;
+
+ if (this._switchData && this._switchData.gestureActivated) {
+ for (const group of this._switchData.monitors)
+ group.remove_all_transitions();
+ } else {
+ this._prepareWorkspaceSwitch();
+ }
+
+ const monitorGroup = this._findMonitorGroup(monitor);
+ const baseDistance = monitorGroup.baseDistance;
+ const progress = monitorGroup.progress;
+
+ const closestWs = monitorGroup.findClosestWorkspace(progress);
+ const cancelProgress = monitorGroup.getWorkspaceProgress(closestWs);
+ const points = monitorGroup.getSnapPoints();
+
+ this._switchData.baseMonitorGroup = monitorGroup;
+
+ tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
+ }
+
+ _switchWorkspaceUpdate(tracker, progress) {
+ if (!this._switchData)
+ return;
+
+ for (const monitorGroup of this._switchData.monitors)
+ monitorGroup.updateSwipeForMonitor(progress, this._switchData.baseMonitorGroup);
+ }
+
+ _switchWorkspaceEnd(tracker, duration, endProgress) {
+ if (!this._switchData)
+ return;
+
+ const switchData = this._switchData;
+ switchData.gestureActivated = true;
+
+ const newWs = switchData.baseMonitorGroup.findClosestWorkspace(endProgress);
+ const endTime = Clutter.get_current_event_time();
+
+ for (const monitorGroup of this._switchData.monitors) {
+ const progress = monitorGroup.getWorkspaceProgress(newWs);
+
+ const params = {
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ };
+
+ if (monitorGroup.index === Main.layoutManager.primaryIndex) {
+ params.onComplete = () => {
+ if (!newWs.active)
+ newWs.activate(endTime);
+ this._finishWorkspaceSwitch(switchData);
+ };
+ }
+
+ monitorGroup.ease_property('progress', progress, params);
+ }
+ }
+
+ get gestureActive() {
+ return this._switchData !== null && this._switchData.gestureActivated;
+ }
+
+ cancelSwitchAnimation() {
+ if (!this._switchData)
+ return;
+
+ if (this._switchData.gestureActivated)
+ return;
+
+ this._finishWorkspaceSwitch(this._switchData);
+ }
+
+ set movingWindow(movingWindow) {
+ this._movingWindow = movingWindow;
+ }
+
+ get movingWindow() {
+ return this._movingWindow;
+ }
+};
diff --git a/js/ui/workspaceSwitcherPopup.js b/js/ui/workspaceSwitcherPopup.js
new file mode 100644
index 0000000..8744529
--- /dev/null
+++ b/js/ui/workspaceSwitcherPopup.js
@@ -0,0 +1,101 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WorkspaceSwitcherPopup */
+
+const { Clutter, GLib, GObject, St } = imports.gi;
+
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+
+var ANIMATION_TIME = 100;
+var DISPLAY_TIMEOUT = 600;
+
+
+var WorkspaceSwitcherPopup = GObject.registerClass(
+class WorkspaceSwitcherPopup extends Clutter.Actor {
+ _init() {
+ super._init({
+ offscreen_redirect: Clutter.OffscreenRedirect.ALWAYS,
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.END,
+ });
+
+ const constraint = new Layout.MonitorConstraint({ primary: true });
+ this.add_constraint(constraint);
+
+ Main.uiGroup.add_actor(this);
+
+ this._timeoutId = 0;
+
+ this._list = new St.BoxLayout({
+ style_class: 'workspace-switcher',
+ });
+ this.add_child(this._list);
+
+ this._redisplay();
+
+ this.hide();
+
+ let workspaceManager = global.workspace_manager;
+ workspaceManager.connectObject(
+ 'workspace-added', this._redisplay.bind(this),
+ 'workspace-removed', this._redisplay.bind(this), this);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _redisplay() {
+ let workspaceManager = global.workspace_manager;
+
+ this._list.destroy_all_children();
+
+ for (let i = 0; i < workspaceManager.n_workspaces; i++) {
+ const indicator = new St.Bin({
+ style_class: 'ws-switcher-indicator',
+ });
+
+ if (i === this._activeWorkspaceIndex)
+ indicator.add_style_pseudo_class('active');
+
+ this._list.add_actor(indicator);
+ }
+ }
+
+ display(activeWorkspaceIndex) {
+ this._activeWorkspaceIndex = activeWorkspaceIndex;
+
+ this._redisplay();
+ if (this._timeoutId != 0)
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, DISPLAY_TIMEOUT, this._onTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._onTimeout');
+
+ const duration = this.visible ? 0 : ANIMATION_TIME;
+ this.show();
+ this.opacity = 0;
+ this.ease({
+ opacity: 255,
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ _onTimeout() {
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+ this.ease({
+ opacity: 0.0,
+ duration: ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => this.destroy(),
+ });
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _onDestroy() {
+ if (this._timeoutId)
+ GLib.source_remove(this._timeoutId);
+ this._timeoutId = 0;
+ }
+});
diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js
new file mode 100644
index 0000000..45b938f
--- /dev/null
+++ b/js/ui/workspaceThumbnail.js
@@ -0,0 +1,1436 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WorkspaceThumbnail, ThumbnailsBox */
+
+const { Clutter, Gio, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi;
+
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const { TransientSignalHolder } = imports.misc.signalTracker;
+const Util = imports.misc.util;
+const Workspace = imports.ui.workspace;
+
+const NUM_WORKSPACES_THRESHOLD = 2;
+
+// The maximum size of a thumbnail is 5% the width and height of the screen
+var MAX_THUMBNAIL_SCALE = 0.05;
+
+var RESCALE_ANIMATION_TIME = 200;
+var SLIDE_ANIMATION_TIME = 200;
+
+// When we create workspaces by dragging, we add a "cut" into the top and
+// bottom of each workspace so that the user doesn't have to hit the
+// placeholder exactly.
+var WORKSPACE_CUT_SIZE = 10;
+
+var WORKSPACE_KEEP_ALIVE_TIME = 100;
+
+var MUTTER_SCHEMA = 'org.gnome.mutter';
+
+/* A layout manager that requests size only for primary_actor, but then allocates
+ all using a fixed layout */
+var PrimaryActorLayout = GObject.registerClass(
+class PrimaryActorLayout extends Clutter.FixedLayout {
+ _init(primaryActor) {
+ super._init();
+
+ this.primaryActor = primaryActor;
+ }
+
+ vfunc_get_preferred_width(container, forHeight) {
+ return this.primaryActor.get_preferred_width(forHeight);
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ return this.primaryActor.get_preferred_height(forWidth);
+ }
+});
+
+var WindowClone = GObject.registerClass({
+ Signals: {
+ 'drag-begin': {},
+ 'drag-cancelled': {},
+ 'drag-end': {},
+ 'selected': { param_types: [GObject.TYPE_UINT] },
+ },
+}, class WindowClone extends Clutter.Actor {
+ _init(realWindow) {
+ let clone = new Clutter.Clone({ source: realWindow });
+ super._init({
+ layout_manager: new PrimaryActorLayout(clone),
+ reactive: true,
+ });
+ this._delegate = this;
+
+ this.add_child(clone);
+ this.realWindow = realWindow;
+ this.metaWindow = realWindow.meta_window;
+
+ this.realWindow.connectObject(
+ 'notify::position', this._onPositionChanged.bind(this),
+ 'destroy', () => {
+ // First destroy the clone and then destroy everything
+ // This will ensure that we never see it in the _disconnectSignals loop
+ clone.destroy();
+ this.destroy();
+ }, this);
+ this._onPositionChanged();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._draggable = DND.makeDraggable(this, {
+ restoreOnSuccess: true,
+ dragActorMaxSize: Workspace.WINDOW_DND_SIZE,
+ dragActorOpacity: Workspace.DRAGGING_WINDOW_OPACITY,
+ });
+ this._draggable.connect('drag-begin', this._onDragBegin.bind(this));
+ this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this));
+ this._draggable.connect('drag-end', this._onDragEnd.bind(this));
+ this.inDrag = false;
+
+ let iter = win => {
+ let actor = win.get_compositor_private();
+
+ if (!actor)
+ return false;
+ if (!win.is_attached_dialog())
+ return false;
+
+ this._doAddAttachedDialog(win, actor);
+ win.foreach_transient(iter);
+
+ return true;
+ };
+ this.metaWindow.foreach_transient(iter);
+ }
+
+ // Find the actor just below us, respecting reparenting done
+ // by DND code
+ getActualStackAbove() {
+ if (this._stackAbove == null)
+ return null;
+
+ if (this.inDrag) {
+ if (this._stackAbove._delegate)
+ return this._stackAbove._delegate.getActualStackAbove();
+ else
+ return null;
+ } else {
+ return this._stackAbove;
+ }
+ }
+
+ setStackAbove(actor) {
+ this._stackAbove = actor;
+
+ // Don't apply the new stacking now, it will be applied
+ // when dragging ends and window are stacked again
+ if (actor.inDrag)
+ return;
+
+ let parent = this.get_parent();
+ let actualAbove = this.getActualStackAbove();
+ if (actualAbove == null)
+ parent.set_child_below_sibling(this, null);
+ else
+ parent.set_child_above_sibling(this, actualAbove);
+ }
+
+ addAttachedDialog(win) {
+ this._doAddAttachedDialog(win, win.get_compositor_private());
+ }
+
+ _doAddAttachedDialog(metaDialog, realDialog) {
+ let clone = new Clutter.Clone({ source: realDialog });
+ this._updateDialogPosition(realDialog, clone);
+
+ realDialog.connectObject(
+ 'notify::position', dialog => this._updateDialogPosition(dialog, clone),
+ 'destroy', () => clone.destroy(), this);
+ this.add_child(clone);
+ }
+
+ _updateDialogPosition(realDialog, cloneDialog) {
+ let metaDialog = realDialog.meta_window;
+ let dialogRect = metaDialog.get_frame_rect();
+ let rect = this.metaWindow.get_frame_rect();
+
+ cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y);
+ }
+
+ _onPositionChanged() {
+ this.set_position(this.realWindow.x, this.realWindow.y);
+ }
+
+ _onDestroy() {
+ this._delegate = null;
+
+ if (this.inDrag) {
+ this.emit('drag-end');
+ this.inDrag = false;
+ }
+ }
+
+ vfunc_button_press_event() {
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_button_release_event(buttonEvent) {
+ this.emit('selected', buttonEvent.time);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_touch_event(touchEvent) {
+ if (touchEvent.type != Clutter.EventType.TOUCH_END ||
+ !global.display.is_pointer_emulating_sequence(touchEvent.sequence))
+ return Clutter.EVENT_PROPAGATE;
+
+ this.emit('selected', touchEvent.time);
+ return Clutter.EVENT_STOP;
+ }
+
+ _onDragBegin(_draggable, _time) {
+ this.inDrag = true;
+ this.emit('drag-begin');
+ }
+
+ _onDragCancelled(_draggable, _time) {
+ this.emit('drag-cancelled');
+ }
+
+ _onDragEnd(_draggable, _time, _snapback) {
+ this.inDrag = false;
+
+ // We may not have a parent if DnD completed successfully, in
+ // which case our clone will shortly be destroyed and replaced
+ // with a new one on the target workspace.
+ let parent = this.get_parent();
+ if (parent !== null) {
+ if (this._stackAbove == null)
+ parent.set_child_below_sibling(this, null);
+ else
+ parent.set_child_above_sibling(this, this._stackAbove);
+ }
+
+
+ this.emit('drag-end');
+ }
+});
+
+
+var ThumbnailState = {
+ NEW: 0,
+ EXPANDING: 1,
+ EXPANDED: 2,
+ ANIMATING_IN: 3,
+ NORMAL: 4,
+ REMOVING: 5,
+ ANIMATING_OUT: 6,
+ ANIMATED_OUT: 7,
+ COLLAPSING: 8,
+ DESTROYED: 9,
+};
+
+/**
+ * @metaWorkspace: a #Meta.Workspace
+ */
+var WorkspaceThumbnail = GObject.registerClass({
+ Properties: {
+ 'collapse-fraction': GObject.ParamSpec.double(
+ 'collapse-fraction', 'collapse-fraction', 'collapse-fraction',
+ GObject.ParamFlags.READWRITE,
+ 0, 1, 0),
+ 'slide-position': GObject.ParamSpec.double(
+ 'slide-position', 'slide-position', 'slide-position',
+ GObject.ParamFlags.READWRITE,
+ 0, 1, 0),
+ },
+}, class WorkspaceThumbnail extends St.Widget {
+ _init(metaWorkspace, monitorIndex) {
+ super._init({
+ clip_to_allocation: true,
+ style_class: 'workspace-thumbnail',
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ });
+ this._delegate = this;
+
+ this.metaWorkspace = metaWorkspace;
+ this.monitorIndex = monitorIndex;
+
+ this._removed = false;
+
+ this._viewport = new Clutter.Actor();
+ this.add_child(this._viewport);
+
+ this._contents = new Clutter.Actor();
+ this._viewport.add_child(this._contents);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(this.monitorIndex);
+ this.setPorthole(workArea.x, workArea.y, workArea.width, workArea.height);
+
+ let windows = global.get_window_actors().filter(actor => {
+ let win = actor.meta_window;
+ return win.located_on_workspace(metaWorkspace);
+ });
+
+ // Create clones for windows that should be visible in the Overview
+ this._windows = [];
+ this._allWindows = [];
+ for (let i = 0; i < windows.length; i++) {
+ windows[i].meta_window.connectObject('notify::minimized',
+ this._updateMinimized.bind(this), this);
+ this._allWindows.push(windows[i].meta_window);
+
+ if (this._isMyWindow(windows[i]) && this._isOverviewWindow(windows[i]))
+ this._addWindowClone(windows[i]);
+ }
+
+ // Track window changes
+ this.metaWorkspace.connectObject(
+ 'window-added', this._windowAdded.bind(this),
+ 'window-removed', this._windowRemoved.bind(this), this);
+ global.display.connectObject(
+ 'window-entered-monitor', this._windowEnteredMonitor.bind(this),
+ 'window-left-monitor', this._windowLeftMonitor.bind(this), this);
+
+ this.state = ThumbnailState.NORMAL;
+ this._slidePosition = 0; // Fully slid in
+ this._collapseFraction = 0; // Not collapsed
+ }
+
+ setPorthole(x, y, width, height) {
+ this._viewport.set_size(width, height);
+ this._contents.set_position(-x, -y);
+ }
+
+ _lookupIndex(metaWindow) {
+ return this._windows.findIndex(w => w.metaWindow == metaWindow);
+ }
+
+ syncStacking(stackIndices) {
+ this._windows.sort((a, b) => {
+ let indexA = stackIndices[a.metaWindow.get_stable_sequence()];
+ let indexB = stackIndices[b.metaWindow.get_stable_sequence()];
+ return indexA - indexB;
+ });
+
+ for (let i = 1; i < this._windows.length; i++) {
+ let clone = this._windows[i];
+ const previousClone = this._windows[i - 1];
+ clone.setStackAbove(previousClone);
+ }
+ }
+
+ set slidePosition(slidePosition) {
+ if (this._slidePosition == slidePosition)
+ return;
+
+ const scale = Util.lerp(1, 0.75, slidePosition);
+ this.set_scale(scale, scale);
+ this.opacity = Util.lerp(255, 0, slidePosition);
+
+ this._slidePosition = slidePosition;
+ this.notify('slide-position');
+ this.queue_relayout();
+ }
+
+ get slidePosition() {
+ return this._slidePosition;
+ }
+
+ set collapseFraction(collapseFraction) {
+ if (this._collapseFraction == collapseFraction)
+ return;
+ this._collapseFraction = collapseFraction;
+ this.notify('collapse-fraction');
+ this.queue_relayout();
+ }
+
+ get collapseFraction() {
+ return this._collapseFraction;
+ }
+
+ _doRemoveWindow(metaWin) {
+ let clone = this._removeWindowClone(metaWin);
+ if (clone)
+ clone.destroy();
+ }
+
+ _doAddWindow(metaWin) {
+ if (this._removed)
+ return;
+
+ let win = metaWin.get_compositor_private();
+
+ if (!win) {
+ // Newly-created windows are added to a workspace before
+ // the compositor finds out about them...
+ let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ if (!this._removed &&
+ metaWin.get_compositor_private() &&
+ metaWin.get_workspace() == this.metaWorkspace)
+ this._doAddWindow(metaWin);
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[gnome-shell] this._doAddWindow');
+ return;
+ }
+
+ if (!this._allWindows.includes(metaWin)) {
+ metaWin.connectObject('notify::minimized',
+ this._updateMinimized.bind(this), this);
+ this._allWindows.push(metaWin);
+ }
+
+ // We might have the window in our list already if it was on all workspaces and
+ // now was moved to this workspace
+ if (this._lookupIndex(metaWin) != -1)
+ return;
+
+ if (!this._isMyWindow(win))
+ return;
+
+ if (this._isOverviewWindow(win)) {
+ this._addWindowClone(win);
+ } else if (metaWin.is_attached_dialog()) {
+ let parent = metaWin.get_transient_for();
+ while (parent.is_attached_dialog())
+ parent = parent.get_transient_for();
+
+ let idx = this._lookupIndex(parent);
+ if (idx < 0) {
+ // parent was not created yet, it will take care
+ // of the dialog when created
+ return;
+ }
+
+ let clone = this._windows[idx];
+ clone.addAttachedDialog(metaWin);
+ }
+ }
+
+ _windowAdded(metaWorkspace, metaWin) {
+ this._doAddWindow(metaWin);
+ }
+
+ _windowRemoved(metaWorkspace, metaWin) {
+ let index = this._allWindows.indexOf(metaWin);
+ if (index != -1) {
+ metaWin.disconnectObject(this);
+ this._allWindows.splice(index, 1);
+ }
+
+ this._doRemoveWindow(metaWin);
+ }
+
+ _windowEnteredMonitor(metaDisplay, monitorIndex, metaWin) {
+ if (monitorIndex == this.monitorIndex)
+ this._doAddWindow(metaWin);
+ }
+
+ _windowLeftMonitor(metaDisplay, monitorIndex, metaWin) {
+ if (monitorIndex == this.monitorIndex)
+ this._doRemoveWindow(metaWin);
+ }
+
+ _updateMinimized(metaWin) {
+ if (metaWin.minimized)
+ this._doRemoveWindow(metaWin);
+ else
+ this._doAddWindow(metaWin);
+ }
+
+ workspaceRemoved() {
+ if (this._removed)
+ return;
+
+ this._removed = true;
+
+ this.metaWorkspace.disconnectObject(this);
+ global.display.disconnectObject(this);
+ this._allWindows.forEach(w => w.disconnectObject(this));
+ }
+
+ _onDestroy() {
+ this.workspaceRemoved();
+ this._windows = [];
+ }
+
+ // Tests if @actor belongs to this workspace and monitor
+ _isMyWindow(actor) {
+ let win = actor.meta_window;
+ return win.located_on_workspace(this.metaWorkspace) &&
+ (win.get_monitor() == this.monitorIndex);
+ }
+
+ // Tests if @win should be shown in the Overview
+ _isOverviewWindow(win) {
+ return !win.get_meta_window().skip_taskbar &&
+ win.get_meta_window().showing_on_its_workspace();
+ }
+
+ // Create a clone of a (non-desktop) window and add it to the window list
+ _addWindowClone(win) {
+ let clone = new WindowClone(win);
+
+ clone.connect('selected', (o, time) => {
+ this.activate(time);
+ });
+ clone.connect('drag-begin', () => {
+ Main.overview.beginWindowDrag(clone.metaWindow);
+ });
+ clone.connect('drag-cancelled', () => {
+ Main.overview.cancelledWindowDrag(clone.metaWindow);
+ });
+ clone.connect('drag-end', () => {
+ Main.overview.endWindowDrag(clone.metaWindow);
+ });
+ clone.connect('destroy', () => {
+ this._removeWindowClone(clone.metaWindow);
+ });
+ this._contents.add_actor(clone);
+
+ if (this._windows.length > 0)
+ clone.setStackAbove(this._windows[this._windows.length - 1]);
+
+ this._windows.push(clone);
+
+ return clone;
+ }
+
+ _removeWindowClone(metaWin) {
+ // find the position of the window in our list
+ let index = this._lookupIndex(metaWin);
+
+ if (index == -1)
+ return null;
+
+ return this._windows.splice(index, 1).pop();
+ }
+
+ activate(time) {
+ if (this.state > ThumbnailState.NORMAL)
+ return;
+
+ // a click on the already current workspace should go back to the main view
+ if (this.metaWorkspace.active)
+ Main.overview.hide();
+ else
+ this.metaWorkspace.activate(time);
+ }
+
+ // Draggable target interface used only by ThumbnailsBox
+ handleDragOverInternal(source, actor, time) {
+ if (source == Main.xdndHandler) {
+ this.metaWorkspace.activate(time);
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ if (this.state > ThumbnailState.NORMAL)
+ return DND.DragMotionResult.CONTINUE;
+
+ if (source.metaWindow &&
+ !this._isMyWindow(source.metaWindow.get_compositor_private()))
+ return DND.DragMotionResult.MOVE_DROP;
+ if (source.app && source.app.can_open_new_window())
+ return DND.DragMotionResult.COPY_DROP;
+ if (!source.app && source.shellWorkspaceLaunch)
+ return DND.DragMotionResult.COPY_DROP;
+
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ acceptDropInternal(source, actor, time) {
+ if (this.state > ThumbnailState.NORMAL)
+ return false;
+
+ if (source.metaWindow) {
+ let win = source.metaWindow.get_compositor_private();
+ if (this._isMyWindow(win))
+ return false;
+
+ let metaWindow = win.get_meta_window();
+ Main.moveWindowToMonitorAndWorkspace(metaWindow,
+ this.monitorIndex, this.metaWorkspace.index());
+ return true;
+ } else if (source.app && source.app.can_open_new_window()) {
+ if (source.animateLaunchAtPos)
+ source.animateLaunchAtPos(actor.x, actor.y);
+
+ source.app.open_new_window(this.metaWorkspace.index());
+ return true;
+ } else if (!source.app && source.shellWorkspaceLaunch) {
+ // While unused in our own drag sources, shellWorkspaceLaunch allows
+ // extensions to define custom actions for their drag sources.
+ source.shellWorkspaceLaunch({
+ workspace: this.metaWorkspace.index(),
+ timestamp: time,
+ });
+ return true;
+ }
+
+ return false;
+ }
+
+ setScale(scaleX, scaleY) {
+ this._viewport.set_scale(scaleX, scaleY);
+ }
+});
+
+
+var ThumbnailsBox = GObject.registerClass({
+ Properties: {
+ 'expand-fraction': GObject.ParamSpec.double(
+ 'expand-fraction', 'expand-fraction', 'expand-fraction',
+ GObject.ParamFlags.READWRITE,
+ 0, 1, 1),
+ 'scale': GObject.ParamSpec.double(
+ 'scale', 'scale', 'scale',
+ GObject.ParamFlags.READWRITE,
+ 0, Infinity, 0),
+ 'should-show': GObject.ParamSpec.boolean(
+ 'should-show', 'should-show', 'should-show',
+ GObject.ParamFlags.READABLE,
+ true),
+ },
+}, class ThumbnailsBox extends St.Widget {
+ _init(scrollAdjustment, monitorIndex) {
+ super._init({
+ style_class: 'workspace-thumbnails',
+ reactive: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+ });
+
+ this._delegate = this;
+
+ let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator' });
+
+ // We don't want the indicator to affect drag-and-drop
+ Shell.util_set_hidden_from_pick(indicator, true);
+
+ this._indicator = indicator;
+ this.add_actor(indicator);
+
+ this._monitorIndex = monitorIndex;
+
+ this._dropWorkspace = -1;
+ this._dropPlaceholderPos = -1;
+ this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
+ this.add_actor(this._dropPlaceholder);
+ this._spliceIndex = -1;
+
+ this._targetScale = 0;
+ this._scale = 0;
+ this._expandFraction = 1;
+ this._updateStateId = 0;
+ this._pendingScaleUpdate = false;
+ this._animatingIndicator = false;
+
+ this._shouldShow = true;
+
+ this._stateCounts = {};
+ for (let key in ThumbnailState)
+ this._stateCounts[ThumbnailState[key]] = 0;
+
+ this._thumbnails = [];
+
+ Main.overview.connectObject(
+ 'showing', () => this._createThumbnails(),
+ 'hidden', () => this._destroyThumbnails(),
+ 'item-drag-begin', () => this._onDragBegin(),
+ 'item-drag-end', () => this._onDragEnd(),
+ 'item-drag-cancelled', () => this._onDragCancelled(),
+ 'window-drag-begin', () => this._onDragBegin(),
+ 'window-drag-end', () => this._onDragEnd(),
+ 'window-drag-cancelled', () => this._onDragCancelled(), this);
+
+ this._settings = new Gio.Settings({ schema_id: MUTTER_SCHEMA });
+ this._settings.connect('changed::dynamic-workspaces',
+ () => this._updateShouldShow());
+ this._updateShouldShow();
+
+ Main.layoutManager.connectObject('monitors-changed', () => {
+ this._destroyThumbnails();
+ if (Main.overview.visible)
+ this._createThumbnails();
+ }, this);
+
+ // The porthole is the part of the screen we're showing in the thumbnails
+ global.display.connectObject('workareas-changed',
+ () => this._updatePorthole(), this);
+ this._updatePorthole();
+
+ this.connect('notify::visible', () => {
+ if (!this.visible)
+ this._queueUpdateStates();
+ });
+ this.connect('destroy', () => this._onDestroy());
+
+ this._scrollAdjustment = scrollAdjustment;
+ this._scrollAdjustment.connectObject('notify::value',
+ () => this._updateIndicator(), this);
+ }
+
+ setMonitorIndex(monitorIndex) {
+ this._monitorIndex = monitorIndex;
+ }
+
+ _onDestroy() {
+ this._destroyThumbnails();
+ this._unqueueUpdateStates();
+
+ if (this._settings)
+ this._settings.run_dispose();
+ this._settings = null;
+ }
+
+ _updateShouldShow() {
+ const { nWorkspaces } = global.workspace_manager;
+ const shouldShow = this._settings.get_boolean('dynamic-workspaces')
+ ? nWorkspaces > NUM_WORKSPACES_THRESHOLD
+ : nWorkspaces > 1;
+
+ if (this._shouldShow === shouldShow)
+ return;
+
+ this._shouldShow = shouldShow;
+ this.notify('should-show');
+ }
+
+ _updateIndicator() {
+ const { value } = this._scrollAdjustment;
+ const { workspaceManager } = global;
+ const activeIndex = workspaceManager.get_active_workspace_index();
+
+ this._animatingIndicator = value !== activeIndex;
+
+ if (!this._animatingIndicator)
+ this._queueUpdateStates();
+
+ this.queue_relayout();
+ }
+
+ _activateThumbnailAtPoint(stageX, stageY, time) {
+ const [r_, x] = this.transform_stage_point(stageX, stageY);
+
+ const thumbnail = this._thumbnails.find(t => x >= t.x && x <= t.x + t.width);
+ if (thumbnail)
+ thumbnail.activate(time);
+ }
+
+ vfunc_button_release_event(buttonEvent) {
+ let { x, y } = buttonEvent;
+ this._activateThumbnailAtPoint(x, y, buttonEvent.time);
+ return Clutter.EVENT_STOP;
+ }
+
+ vfunc_touch_event(touchEvent) {
+ if (touchEvent.type == Clutter.EventType.TOUCH_END &&
+ global.display.is_pointer_emulating_sequence(touchEvent.sequence)) {
+ let { x, y } = touchEvent;
+ this._activateThumbnailAtPoint(x, y, touchEvent.time);
+ }
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _onDragBegin() {
+ this._dragCancelled = false;
+ this._dragMonitor = {
+ dragMotion: this._onDragMotion.bind(this),
+ };
+ DND.addDragMonitor(this._dragMonitor);
+ }
+
+ _onDragEnd() {
+ if (this._dragCancelled)
+ return;
+
+ this._endDrag();
+ }
+
+ _onDragCancelled() {
+ this._dragCancelled = true;
+ this._endDrag();
+ }
+
+ _endDrag() {
+ this._clearDragPlaceholder();
+ DND.removeDragMonitor(this._dragMonitor);
+ }
+
+ _onDragMotion(dragEvent) {
+ if (!this.contains(dragEvent.targetActor))
+ this._onLeave();
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ _onLeave() {
+ this._clearDragPlaceholder();
+ }
+
+ _clearDragPlaceholder() {
+ if (this._dropPlaceholderPos == -1)
+ return;
+
+ this._dropPlaceholderPos = -1;
+ this.queue_relayout();
+ }
+
+ _getPlaceholderTarget(index, spacing, rtl) {
+ const workspace = this._thumbnails[index];
+
+ let targetX1;
+ let targetX2;
+
+ if (rtl) {
+ const baseX = workspace.x + workspace.width;
+ targetX1 = baseX - WORKSPACE_CUT_SIZE;
+ targetX2 = baseX + spacing + WORKSPACE_CUT_SIZE;
+ } else {
+ targetX1 = workspace.x - spacing - WORKSPACE_CUT_SIZE;
+ targetX2 = workspace.x + WORKSPACE_CUT_SIZE;
+ }
+
+ if (index === 0) {
+ if (rtl)
+ targetX2 -= spacing + WORKSPACE_CUT_SIZE;
+ else
+ targetX1 += spacing + WORKSPACE_CUT_SIZE;
+ }
+
+ if (index === this._dropPlaceholderPos) {
+ const placeholderWidth = this._dropPlaceholder.get_width() + spacing;
+ if (rtl)
+ targetX2 += placeholderWidth;
+ else
+ targetX1 -= placeholderWidth;
+ }
+
+ return [targetX1, targetX2];
+ }
+
+ _withinWorkspace(x, index, rtl) {
+ const length = this._thumbnails.length;
+ const workspace = this._thumbnails[index];
+
+ let workspaceX1 = workspace.x + WORKSPACE_CUT_SIZE;
+ let workspaceX2 = workspace.x + workspace.width - WORKSPACE_CUT_SIZE;
+
+ if (index === length - 1) {
+ if (rtl)
+ workspaceX1 -= WORKSPACE_CUT_SIZE;
+ else
+ workspaceX2 += WORKSPACE_CUT_SIZE;
+ }
+
+ return x > workspaceX1 && x <= workspaceX2;
+ }
+
+ // Draggable target interface
+ handleDragOver(source, actor, x, y, time) {
+ if (!source.metaWindow &&
+ (!source.app || !source.app.can_open_new_window()) &&
+ (source.app || !source.shellWorkspaceLaunch) &&
+ source != Main.xdndHandler)
+ return DND.DragMotionResult.CONTINUE;
+
+ const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+ let canCreateWorkspaces = Meta.prefs_get_dynamic_workspaces();
+ let spacing = this.get_theme_node().get_length('spacing');
+
+ this._dropWorkspace = -1;
+ let placeholderPos = -1;
+ let length = this._thumbnails.length;
+ for (let i = 0; i < length; i++) {
+ const index = rtl ? length - i - 1 : i;
+
+ if (canCreateWorkspaces && source !== Main.xdndHandler) {
+ const [targetStart, targetEnd] =
+ this._getPlaceholderTarget(index, spacing, rtl);
+
+ if (x > targetStart && x <= targetEnd) {
+ placeholderPos = index;
+ break;
+ }
+ }
+
+ if (this._withinWorkspace(x, index, rtl)) {
+ this._dropWorkspace = index;
+ break;
+ }
+ }
+
+ if (this._dropPlaceholderPos != placeholderPos) {
+ this._dropPlaceholderPos = placeholderPos;
+ this.queue_relayout();
+ }
+
+ if (this._dropWorkspace != -1)
+ return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, actor, time);
+ else if (this._dropPlaceholderPos != -1)
+ return source.metaWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.COPY_DROP;
+ else
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ acceptDrop(source, actor, x, y, time) {
+ if (this._dropWorkspace != -1) {
+ return this._thumbnails[this._dropWorkspace].acceptDropInternal(source, actor, time);
+ } else if (this._dropPlaceholderPos != -1) {
+ if (!source.metaWindow &&
+ (!source.app || !source.app.can_open_new_window()) &&
+ (source.app || !source.shellWorkspaceLaunch))
+ return false;
+
+ let isWindow = !!source.metaWindow;
+
+ let newWorkspaceIndex;
+ [newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1];
+ this._spliceIndex = newWorkspaceIndex;
+
+ Main.wm.insertWorkspace(newWorkspaceIndex);
+
+ if (isWindow) {
+ // Move the window to our monitor first if necessary.
+ let thumbMonitor = this._thumbnails[newWorkspaceIndex].monitorIndex;
+ Main.moveWindowToMonitorAndWorkspace(source.metaWindow,
+ thumbMonitor, newWorkspaceIndex, true);
+ } else if (source.app && source.app.can_open_new_window()) {
+ if (source.animateLaunchAtPos)
+ source.animateLaunchAtPos(actor.x, actor.y);
+
+ source.app.open_new_window(newWorkspaceIndex);
+ } else if (!source.app && source.shellWorkspaceLaunch) {
+ // While unused in our own drag sources, shellWorkspaceLaunch allows
+ // extensions to define custom actions for their drag sources.
+ source.shellWorkspaceLaunch({
+ workspace: newWorkspaceIndex,
+ timestamp: time,
+ });
+ }
+
+ if (source.app || (!source.app && source.shellWorkspaceLaunch)) {
+ // This new workspace will be automatically removed if the application fails
+ // to open its first window within some time, as tracked by Shell.WindowTracker.
+ // Here, we only add a very brief timeout to avoid the _immediate_ removal of the
+ // workspace while we wait for the startup sequence to load.
+ let workspaceManager = global.workspace_manager;
+ Main.wm.keepWorkspaceAlive(workspaceManager.get_workspace_by_index(newWorkspaceIndex),
+ WORKSPACE_KEEP_ALIVE_TIME);
+ }
+
+ // Start the animation on the workspace (which is actually
+ // an old one which just became empty)
+ let thumbnail = this._thumbnails[newWorkspaceIndex];
+ this._setThumbnailState(thumbnail, ThumbnailState.NEW);
+ thumbnail.slide_position = 1;
+ thumbnail.collapse_fraction = 1;
+
+ this._queueUpdateStates();
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ _createThumbnails() {
+ if (this._thumbnails.length > 0)
+ return;
+
+ const { workspaceManager } = global;
+ this._transientSignalHolder = new TransientSignalHolder(this);
+ workspaceManager.connectObject(
+ 'notify::n-workspaces', this._workspacesChanged.bind(this),
+ 'active-workspace-changed', () => this._updateIndicator(),
+ 'workspaces-reordered', () => {
+ this._thumbnails.sort((a, b) => {
+ return a.metaWorkspace.index() - b.metaWorkspace.index();
+ });
+ this.queue_relayout();
+ }, this._transientSignalHolder);
+ Main.overview.connectObject('windows-restacked',
+ this._syncStacking.bind(this), this._transientSignalHolder);
+
+ this._targetScale = 0;
+ this._scale = 0;
+ this._pendingScaleUpdate = false;
+ this._unqueueUpdateStates();
+
+ this._stateCounts = {};
+ for (let key in ThumbnailState)
+ this._stateCounts[ThumbnailState[key]] = 0;
+
+ this.addThumbnails(0, workspaceManager.n_workspaces);
+
+ this._updateShouldShow();
+ }
+
+ _destroyThumbnails() {
+ if (this._thumbnails.length == 0)
+ return;
+
+ this._transientSignalHolder.destroy();
+ delete this._transientSignalHolder;
+
+ for (let w = 0; w < this._thumbnails.length; w++)
+ this._thumbnails[w].destroy();
+ this._thumbnails = [];
+ }
+
+ _workspacesChanged() {
+ let validThumbnails =
+ this._thumbnails.filter(t => t.state <= ThumbnailState.NORMAL);
+ let workspaceManager = global.workspace_manager;
+ let oldNumWorkspaces = validThumbnails.length;
+ let newNumWorkspaces = workspaceManager.n_workspaces;
+
+ if (newNumWorkspaces > oldNumWorkspaces) {
+ this.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
+ } else {
+ let removedIndex;
+ let removedNum = oldNumWorkspaces - newNumWorkspaces;
+ for (let w = 0; w < oldNumWorkspaces; w++) {
+ let metaWorkspace = workspaceManager.get_workspace_by_index(w);
+ if (this._thumbnails[w].metaWorkspace != metaWorkspace) {
+ removedIndex = w;
+ break;
+ }
+ }
+
+ this.removeThumbnails(removedIndex, removedNum);
+ }
+
+ this._updateShouldShow();
+ }
+
+ addThumbnails(start, count) {
+ let workspaceManager = global.workspace_manager;
+
+ for (let k = start; k < start + count; k++) {
+ let metaWorkspace = workspaceManager.get_workspace_by_index(k);
+ let thumbnail = new WorkspaceThumbnail(metaWorkspace, this._monitorIndex);
+ thumbnail.setPorthole(this._porthole.x, this._porthole.y,
+ this._porthole.width, this._porthole.height);
+ this._thumbnails.push(thumbnail);
+ this.add_actor(thumbnail);
+
+ if (this._shouldShow && start > 0 && this._spliceIndex === -1) {
+ // not the initial fill, and not splicing via DND
+ thumbnail.state = ThumbnailState.NEW;
+ thumbnail.slide_position = 1; // start slid out
+ thumbnail.collapse_fraction = 1; // start fully collapsed
+ this._haveNewThumbnails = true;
+ } else {
+ thumbnail.state = ThumbnailState.NORMAL;
+ }
+
+ this._stateCounts[thumbnail.state]++;
+ }
+
+ this._queueUpdateStates();
+
+ // The thumbnails indicator actually needs to be on top of the thumbnails
+ this.set_child_above_sibling(this._indicator, null);
+
+ // Clear the splice index, we got the message
+ this._spliceIndex = -1;
+ }
+
+ removeThumbnails(start, count) {
+ let currentPos = 0;
+ for (let k = 0; k < this._thumbnails.length; k++) {
+ let thumbnail = this._thumbnails[k];
+
+ if (thumbnail.state > ThumbnailState.NORMAL)
+ continue;
+
+ if (currentPos >= start && currentPos < start + count) {
+ thumbnail.workspaceRemoved();
+ this._setThumbnailState(thumbnail, ThumbnailState.REMOVING);
+ }
+
+ currentPos++;
+ }
+
+ this._queueUpdateStates();
+ }
+
+ _syncStacking(overview, stackIndices) {
+ for (let i = 0; i < this._thumbnails.length; i++)
+ this._thumbnails[i].syncStacking(stackIndices);
+ }
+
+ set scale(scale) {
+ if (this._scale == scale)
+ return;
+
+ this._scale = scale;
+ this.notify('scale');
+ this.queue_relayout();
+ }
+
+ get scale() {
+ return this._scale;
+ }
+
+ _setThumbnailState(thumbnail, state) {
+ this._stateCounts[thumbnail.state]--;
+ thumbnail.state = state;
+ this._stateCounts[thumbnail.state]++;
+ }
+
+ _iterateStateThumbnails(state, callback) {
+ if (this._stateCounts[state] == 0)
+ return;
+
+ for (let i = 0; i < this._thumbnails.length; i++) {
+ if (this._thumbnails[i].state == state)
+ callback.call(this, this._thumbnails[i]);
+ }
+ }
+
+ _updateStates() {
+ this._updateStateId = 0;
+
+ // If we are animating the indicator, wait
+ if (this._animatingIndicator)
+ return;
+
+ // Likewise if we are in the process of hiding
+ if (!this._shouldShow && this.visible)
+ return;
+
+ // Then slide out any thumbnails that have been destroyed
+ this._iterateStateThumbnails(ThumbnailState.REMOVING, thumbnail => {
+ this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_OUT);
+
+ thumbnail.ease_property('slide-position', 1, {
+ duration: SLIDE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: () => {
+ this._setThumbnailState(thumbnail, ThumbnailState.ANIMATED_OUT);
+ this._queueUpdateStates();
+ },
+ });
+ });
+
+ // As long as things are sliding out, don't proceed
+ if (this._stateCounts[ThumbnailState.ANIMATING_OUT] > 0)
+ return;
+
+ // Once that's complete, we can start scaling to the new size,
+ // collapse any removed thumbnails and expand added ones
+ this._iterateStateThumbnails(ThumbnailState.ANIMATED_OUT, thumbnail => {
+ this._setThumbnailState(thumbnail, ThumbnailState.COLLAPSING);
+ thumbnail.ease_property('collapse-fraction', 1, {
+ duration: RESCALE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._stateCounts[thumbnail.state]--;
+ thumbnail.state = ThumbnailState.DESTROYED;
+
+ let index = this._thumbnails.indexOf(thumbnail);
+ this._thumbnails.splice(index, 1);
+ thumbnail.destroy();
+
+ this._queueUpdateStates();
+ },
+ });
+ });
+
+ this._iterateStateThumbnails(ThumbnailState.NEW, thumbnail => {
+ this._setThumbnailState(thumbnail, ThumbnailState.EXPANDING);
+ thumbnail.ease_property('collapse-fraction', 0, {
+ duration: SLIDE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._setThumbnailState(thumbnail, ThumbnailState.EXPANDED);
+ this._queueUpdateStates();
+ },
+ });
+ });
+
+ if (this._pendingScaleUpdate) {
+ this.ease_property('scale', this._targetScale, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: RESCALE_ANIMATION_TIME,
+ onComplete: () => this._queueUpdateStates(),
+ });
+ this._pendingScaleUpdate = false;
+ }
+
+ // Wait until that's done
+ if (this._scale !== this._targetScale ||
+ this._stateCounts[ThumbnailState.COLLAPSING] > 0 ||
+ this._stateCounts[ThumbnailState.EXPANDING] > 0)
+ return;
+
+ // And then slide in any new thumbnails
+ this._iterateStateThumbnails(ThumbnailState.EXPANDED, thumbnail => {
+ this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_IN);
+ thumbnail.ease_property('slide-position', 0, {
+ duration: SLIDE_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._setThumbnailState(thumbnail, ThumbnailState.NORMAL);
+ },
+ });
+ });
+ }
+
+ _queueUpdateStates() {
+ if (this._updateStateId > 0)
+ return;
+
+ this._updateStateId = Meta.later_add(
+ Meta.LaterType.BEFORE_REDRAW, () => this._updateStates());
+ }
+
+ _unqueueUpdateStates() {
+ if (this._updateStateId)
+ Meta.later_remove(this._updateStateId);
+ this._updateStateId = 0;
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ let themeNode = this.get_theme_node();
+
+ forWidth = themeNode.adjust_for_width(forWidth);
+
+ let spacing = themeNode.get_length('spacing');
+ let nWorkspaces = this._thumbnails.length;
+ let totalSpacing = (nWorkspaces - 1) * spacing;
+
+ const avail = forWidth - totalSpacing;
+
+ let scale = (avail / nWorkspaces) / this._porthole.width;
+ scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
+
+ const height = Math.round(this._porthole.height * scale);
+ return themeNode.adjust_preferred_height(height, height);
+ }
+
+ vfunc_get_preferred_width(_forHeight) {
+ // Note that for getPreferredHeight/Width we cheat a bit and skip propagating
+ // the size request to our children because we know how big they are and know
+ // that the actors aren't depending on the virtual functions being called.
+ let themeNode = this.get_theme_node();
+
+ let spacing = themeNode.get_length('spacing');
+ let nWorkspaces = this._thumbnails.length;
+ let totalSpacing = (nWorkspaces - 1) * spacing;
+
+ const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail, index) => {
+ let workspaceSpacing = 0;
+
+ if (index > 0)
+ workspaceSpacing += spacing / 2;
+ if (index < this._thumbnails.length - 1)
+ workspaceSpacing += spacing / 2;
+
+ const progress = 1 - thumbnail.collapse_fraction;
+ const width = (this._porthole.width * MAX_THUMBNAIL_SCALE + workspaceSpacing) * progress;
+ return accumulator + width;
+ }, 0);
+
+ return themeNode.adjust_preferred_width(totalSpacing, naturalWidth);
+ }
+
+ _updatePorthole() {
+ if (!Main.layoutManager.monitors[this._monitorIndex]) {
+ const { x, y, width, height } = global.stage;
+ this._porthole = { x, y, width, height };
+ } else {
+ this._porthole =
+ Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex);
+ }
+
+ this.queue_relayout();
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
+
+ if (this._thumbnails.length == 0) // not visible
+ return;
+
+ let themeNode = this.get_theme_node();
+ box = themeNode.get_content_box(box);
+
+ const portholeWidth = this._porthole.width;
+ const portholeHeight = this._porthole.height;
+ const spacing = themeNode.get_length('spacing');
+
+ const nWorkspaces = this._thumbnails.length;
+
+ // Compute the scale we'll need once everything is updated,
+ // unless we are currently transitioning
+ if (this._expandFraction === 1) {
+ const totalSpacing = (nWorkspaces - 1) * spacing;
+ const availableWidth = (box.get_width() - totalSpacing) / nWorkspaces;
+
+ const hScale = availableWidth / portholeWidth;
+ const vScale = box.get_height() / portholeHeight;
+ const newScale = Math.min(hScale, vScale);
+
+ if (newScale !== this._targetScale) {
+ if (this._targetScale > 0) {
+ // We don't ease immediately because we need to observe the
+ // ordering in queueUpdateStates - if workspaces have been
+ // removed we need to slide them out as the first thing.
+ this._targetScale = newScale;
+ this._pendingScaleUpdate = true;
+ } else {
+ this._targetScale = this._scale = newScale;
+ }
+
+ this._queueUpdateStates();
+ }
+ }
+
+ const ratio = portholeWidth / portholeHeight;
+ const thumbnailFullHeight = Math.round(portholeHeight * this._scale);
+ const thumbnailWidth = Math.round(thumbnailFullHeight * ratio);
+ const thumbnailHeight = thumbnailFullHeight * this._expandFraction;
+ const roundedVScale = thumbnailHeight / portholeHeight;
+
+ // We always request size for MAX_THUMBNAIL_SCALE, distribute
+ // space evently if we use smaller thumbnails
+ const extraWidth =
+ (MAX_THUMBNAIL_SCALE * portholeWidth - thumbnailWidth) * nWorkspaces;
+ box.x1 += Math.round(extraWidth / 2);
+ box.x2 -= Math.round(extraWidth / 2);
+
+ let indicatorValue = this._scrollAdjustment.value;
+ let indicatorUpperWs = Math.ceil(indicatorValue);
+ let indicatorLowerWs = Math.floor(indicatorValue);
+
+ let indicatorLowerX1 = 0;
+ let indicatorLowerX2 = 0;
+ let indicatorUpperX1 = 0;
+ let indicatorUpperX2 = 0;
+
+ let indicatorThemeNode = this._indicator.get_theme_node();
+ let indicatorTopFullBorder = indicatorThemeNode.get_padding(St.Side.TOP) + indicatorThemeNode.get_border_width(St.Side.TOP);
+ let indicatorBottomFullBorder = indicatorThemeNode.get_padding(St.Side.BOTTOM) + indicatorThemeNode.get_border_width(St.Side.BOTTOM);
+ let indicatorLeftFullBorder = indicatorThemeNode.get_padding(St.Side.LEFT) + indicatorThemeNode.get_border_width(St.Side.LEFT);
+ let indicatorRightFullBorder = indicatorThemeNode.get_padding(St.Side.RIGHT) + indicatorThemeNode.get_border_width(St.Side.RIGHT);
+
+ let x = box.x1;
+
+ if (this._dropPlaceholderPos == -1) {
+ this._dropPlaceholder.allocate_preferred_size(
+ ...this._dropPlaceholder.get_position());
+
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.hide();
+ });
+ }
+
+ let childBox = new Clutter.ActorBox();
+
+ for (let i = 0; i < this._thumbnails.length; i++) {
+ const thumbnail = this._thumbnails[i];
+ if (i > 0)
+ x += spacing - Math.round(thumbnail.collapse_fraction * spacing);
+
+ const y1 = box.y1;
+ const y2 = y1 + thumbnailHeight;
+
+ if (i === this._dropPlaceholderPos) {
+ const [, placeholderWidth] = this._dropPlaceholder.get_preferred_width(-1);
+ childBox.y1 = y1;
+ childBox.y2 = y2;
+
+ if (rtl) {
+ childBox.x2 = box.x2 - Math.round(x);
+ childBox.x1 = box.x2 - Math.round(x + placeholderWidth);
+ } else {
+ childBox.x1 = Math.round(x);
+ childBox.x2 = Math.round(x + placeholderWidth);
+ }
+
+ this._dropPlaceholder.allocate(childBox);
+
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.show();
+ });
+ x += placeholderWidth + spacing;
+ }
+
+ // We might end up with thumbnailWidth being something like 99.33
+ // pixels. To make this work and not end up with a gap at the end,
+ // we need some thumbnails to be 99 pixels and some 100 pixels width;
+ // we compute an actual scale separately for each thumbnail.
+ const x1 = Math.round(x);
+ const x2 = Math.round(x + thumbnailWidth);
+ const roundedHScale = (x2 - x1) / portholeWidth;
+
+ // Allocating a scaled actor is funny - x1/y1 correspond to the origin
+ // of the actor, but x2/y2 are increased by the *unscaled* size.
+ if (rtl) {
+ childBox.x2 = box.x2 - x1;
+ childBox.x1 = box.x2 - (x1 + thumbnailWidth);
+ } else {
+ childBox.x1 = x1;
+ childBox.x2 = x1 + thumbnailWidth;
+ }
+ childBox.y1 = y1;
+ childBox.y2 = y1 + thumbnailHeight;
+
+ thumbnail.setScale(roundedHScale, roundedVScale);
+ thumbnail.allocate(childBox);
+
+ if (i === indicatorUpperWs) {
+ indicatorUpperX1 = childBox.x1;
+ indicatorUpperX2 = childBox.x2;
+ }
+ if (i === indicatorLowerWs) {
+ indicatorLowerX1 = childBox.x1;
+ indicatorLowerX2 = childBox.x2;
+ }
+
+ // We round the collapsing portion so that we don't get thumbnails resizing
+ // during an animation due to differences in rounded, but leave the uncollapsed
+ // portion unrounded so that non-animating we end up with the right total
+ x += thumbnailWidth - Math.round(thumbnailWidth * thumbnail.collapse_fraction);
+ }
+
+ childBox.y1 = box.y1;
+ childBox.y2 = box.y1 + thumbnailHeight;
+
+ const indicatorX1 = indicatorLowerX1 +
+ (indicatorUpperX1 - indicatorLowerX1) * (indicatorValue % 1);
+ const indicatorX2 = indicatorLowerX2 +
+ (indicatorUpperX2 - indicatorLowerX2) * (indicatorValue % 1);
+
+ childBox.x1 = indicatorX1 - indicatorLeftFullBorder;
+ childBox.x2 = indicatorX2 + indicatorRightFullBorder;
+ childBox.y1 -= indicatorTopFullBorder;
+ childBox.y2 += indicatorBottomFullBorder;
+ this._indicator.allocate(childBox);
+ }
+
+ get shouldShow() {
+ return this._shouldShow;
+ }
+
+ set expandFraction(expandFraction) {
+ if (this._expandFraction === expandFraction)
+ return;
+ this._expandFraction = expandFraction;
+ this.notify('expand-fraction');
+ this.queue_relayout();
+ }
+
+ get expandFraction() {
+ return this._expandFraction;
+ }
+});
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
new file mode 100644
index 0000000..660fcf6
--- /dev/null
+++ b/js/ui/workspacesView.js
@@ -0,0 +1,1156 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported WorkspacesView, WorkspacesDisplay */
+
+const { Clutter, Gio, GObject, Meta, Shell, St } = imports.gi;
+
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const OverviewControls = imports.ui.overviewControls;
+const SwipeTracker = imports.ui.swipeTracker;
+const Util = imports.misc.util;
+const Workspace = imports.ui.workspace;
+const { ThumbnailsBox, MAX_THUMBNAIL_SCALE } = imports.ui.workspaceThumbnail;
+
+var WORKSPACE_SWITCH_TIME = 250;
+
+const MUTTER_SCHEMA = 'org.gnome.mutter';
+
+const WORKSPACE_MIN_SPACING = 24;
+const WORKSPACE_MAX_SPACING = 80;
+
+const WORKSPACE_INACTIVE_SCALE = 0.94;
+
+const SECONDARY_WORKSPACE_SCALE = 0.80;
+
+var WorkspacesViewBase = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+}, class WorkspacesViewBase extends St.Widget {
+ _init(monitorIndex, overviewAdjustment) {
+ super._init({
+ style_class: 'workspaces-view',
+ x_expand: true,
+ y_expand: true,
+ });
+ this.connect('destroy', this._onDestroy.bind(this));
+ global.focus_manager.add_group(this);
+
+ this._monitorIndex = monitorIndex;
+
+ this._inDrag = false;
+ Main.overview.connectObject(
+ 'window-drag-begin', this._dragBegin.bind(this),
+ 'window-drag-end', this._dragEnd.bind(this), this);
+
+ this._overviewAdjustment = overviewAdjustment;
+ overviewAdjustment.connectObject('notify::value',
+ () => this._updateWorkspaceMode(), this);
+ }
+
+ _onDestroy() {
+ this._dragEnd();
+ }
+
+ _dragBegin() {
+ this._inDrag = true;
+ }
+
+ _dragEnd() {
+ this._inDrag = false;
+ }
+
+ _updateWorkspaceMode() {
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ for (const child of this)
+ child.allocate_available_size(0, 0, box.get_width(), box.get_height());
+ }
+
+ vfunc_get_preferred_width() {
+ return [0, 0];
+ }
+
+ vfunc_get_preferred_height() {
+ return [0, 0];
+ }
+});
+
+var FitMode = {
+ SINGLE: 0,
+ ALL: 1,
+};
+
+var WorkspacesView = GObject.registerClass(
+class WorkspacesView extends WorkspacesViewBase {
+ _init(monitorIndex, controls, scrollAdjustment, fitModeAdjustment, overviewAdjustment) {
+ let workspaceManager = global.workspace_manager;
+
+ super._init(monitorIndex, overviewAdjustment);
+
+ this._controls = controls;
+ this._fitModeAdjustment = fitModeAdjustment;
+ this._fitModeAdjustment.connectObject('notify::value', () => {
+ this._updateVisibility();
+ this._updateWorkspacesState();
+ this.queue_relayout();
+ }, this);
+
+ this._animating = false; // tweening
+ this._gestureActive = false; // touch(pad) gestures
+
+ this._scrollAdjustment = scrollAdjustment;
+ this._scrollAdjustment.connectObject('notify::value',
+ this._onScrollAdjustmentChanged.bind(this), this);
+
+ this._workspaces = [];
+ this._updateWorkspaces();
+ workspaceManager.connectObject(
+ 'notify::n-workspaces', this._updateWorkspaces.bind(this),
+ 'workspaces-reordered', () => {
+ this._workspaces.sort((a, b) => {
+ return a.metaWorkspace.index() - b.metaWorkspace.index();
+ });
+ this._workspaces.forEach(
+ (ws, i) => this.set_child_at_index(ws, i));
+ }, this);
+
+ global.window_manager.connectObject('switch-workspace',
+ this._activeWorkspaceChanged.bind(this), this);
+ }
+
+ _getFirstFitAllWorkspaceBox(box, spacing, vertical) {
+ const { nWorkspaces } = global.workspaceManager;
+ const [width, height] = box.get_size();
+ const [workspace] = this._workspaces;
+
+ const fitAllBox = new Clutter.ActorBox();
+
+ let [x1, y1] = box.get_origin();
+
+ // Spacing here is not only the space between workspaces, but also the
+ // space before the first workspace, and after the last one. This prevents
+ // workspaces from touching the edges of the allocation box.
+ if (vertical) {
+ const availableHeight = height - spacing * (nWorkspaces + 1);
+ let workspaceHeight = availableHeight / nWorkspaces;
+ let [, workspaceWidth] =
+ workspace.get_preferred_width(workspaceHeight);
+
+ y1 = spacing;
+ if (workspaceWidth > width) {
+ [, workspaceHeight] = workspace.get_preferred_height(width);
+ y1 += Math.max((availableHeight - workspaceHeight * nWorkspaces) / 2, 0);
+ }
+
+ fitAllBox.set_size(width, workspaceHeight);
+ } else {
+ const availableWidth = width - spacing * (nWorkspaces + 1);
+ let workspaceWidth = availableWidth / nWorkspaces;
+ let [, workspaceHeight] =
+ workspace.get_preferred_height(workspaceWidth);
+
+ x1 = spacing;
+ if (workspaceHeight > height) {
+ [, workspaceWidth] = workspace.get_preferred_width(height);
+ x1 += Math.max((availableWidth - workspaceWidth * nWorkspaces) / 2, 0);
+ }
+
+ fitAllBox.set_size(workspaceWidth, height);
+ }
+
+ fitAllBox.set_origin(x1, y1);
+
+ return fitAllBox;
+ }
+
+ _getFirstFitSingleWorkspaceBox(box, spacing, vertical) {
+ const [width, height] = box.get_size();
+ const [workspace] = this._workspaces;
+
+ const rtl = this.text_direction === Clutter.TextDirection.RTL;
+ const adj = this._scrollAdjustment;
+ const currentWorkspace = vertical || !rtl
+ ? adj.value : adj.upper - adj.value - 1;
+
+ // Single fit mode implies centered too
+ let [x1, y1] = box.get_origin();
+ if (vertical) {
+ const [, workspaceHeight] = workspace.get_preferred_height(width);
+ y1 += (height - workspaceHeight) / 2;
+ y1 -= currentWorkspace * (workspaceHeight + spacing);
+ } else {
+ const [, workspaceWidth] = workspace.get_preferred_width(height);
+ x1 += (width - workspaceWidth) / 2;
+ x1 -= currentWorkspace * (workspaceWidth + spacing);
+ }
+
+ const fitSingleBox = new Clutter.ActorBox({ x1, y1 });
+
+ if (vertical) {
+ const [, workspaceHeight] = workspace.get_preferred_height(width);
+ fitSingleBox.set_size(width, workspaceHeight);
+ } else {
+ const [, workspaceWidth] = workspace.get_preferred_width(height);
+ fitSingleBox.set_size(workspaceWidth, height);
+ }
+
+ return fitSingleBox;
+ }
+
+ _getSpacing(box, fitMode, vertical) {
+ const [width, height] = box.get_size();
+ const [workspace] = this._workspaces;
+
+ let availableSpace;
+ let workspaceSize;
+ if (vertical) {
+ [, workspaceSize] = workspace.get_preferred_height(width);
+ availableSpace = (height - workspaceSize) / 2;
+ } else {
+ [, workspaceSize] = workspace.get_preferred_width(height);
+ availableSpace = (width - workspaceSize) / 2;
+ }
+
+ const spacing = (availableSpace - workspaceSize * 0.4) * (1 - fitMode);
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+
+ return Math.clamp(spacing, WORKSPACE_MIN_SPACING * scaleFactor,
+ WORKSPACE_MAX_SPACING * scaleFactor);
+ }
+
+ _getWorkspaceModeForOverviewState(state) {
+ const { ControlsState } = OverviewControls;
+
+ switch (state) {
+ case ControlsState.HIDDEN:
+ return 0;
+ case ControlsState.WINDOW_PICKER:
+ return 1;
+ case ControlsState.APP_GRID:
+ return 0;
+ }
+
+ return 0;
+ }
+
+ _updateWorkspacesState() {
+ const adj = this._scrollAdjustment;
+ const fitMode = this._fitModeAdjustment.value;
+
+ const { initialState, finalState, progress } =
+ this._overviewAdjustment.getStateTransitionParams();
+
+ const workspaceMode = (1 - fitMode) * Util.lerp(
+ this._getWorkspaceModeForOverviewState(initialState),
+ this._getWorkspaceModeForOverviewState(finalState),
+ progress);
+
+ // Fade and scale inactive workspaces
+ this._workspaces.forEach((w, index) => {
+ w.stateAdjustment.value = workspaceMode;
+
+ const distanceToCurrentWorkspace = Math.abs(adj.value - index);
+
+ const scaleProgress = 1 - Math.clamp(distanceToCurrentWorkspace, 0, 1);
+
+ const scale = Util.lerp(WORKSPACE_INACTIVE_SCALE, 1, scaleProgress);
+ w.set_scale(scale, scale);
+ });
+ }
+
+ _getFitModeForState(state) {
+ const { ControlsState } = OverviewControls;
+
+ switch (state) {
+ case ControlsState.HIDDEN:
+ case ControlsState.WINDOW_PICKER:
+ return FitMode.SINGLE;
+ case ControlsState.APP_GRID:
+ return FitMode.ALL;
+ default:
+ return FitMode.SINGLE;
+ }
+ }
+
+ _getInitialBoxes(box) {
+ const offsetBox = new Clutter.ActorBox();
+ offsetBox.set_size(...box.get_size());
+
+ let fitSingleBox = offsetBox;
+ let fitAllBox = offsetBox;
+
+ const { transitioning, initialState, finalState } =
+ this._overviewAdjustment.getStateTransitionParams();
+
+ const isPrimary = Main.layoutManager.primaryIndex === this._monitorIndex;
+
+ if (isPrimary && transitioning) {
+ const initialFitMode = this._getFitModeForState(initialState);
+ const finalFitMode = this._getFitModeForState(finalState);
+
+ // Only use the relative boxes when the overview is in a state
+ // transition, and the corresponding fit modes are different.
+ if (initialFitMode !== finalFitMode) {
+ const initialBox =
+ this._controls.getWorkspacesBoxForState(initialState).copy();
+ const finalBox =
+ this._controls.getWorkspacesBoxForState(finalState).copy();
+
+ // Boxes are relative to ControlsManager, transform them;
+ // this.apply_relative_transform_to_point(controls,
+ // new Graphene.Point3D());
+ // would be more correct, but also more expensive
+ const [parentOffsetX, parentOffsetY] =
+ this.get_parent().allocation.get_origin();
+ [initialBox, finalBox].forEach(b => {
+ b.set_origin(b.x1 - parentOffsetX, b.y1 - parentOffsetY);
+ });
+
+ if (initialFitMode === FitMode.SINGLE)
+ [fitSingleBox, fitAllBox] = [initialBox, finalBox];
+ else
+ [fitAllBox, fitSingleBox] = [initialBox, finalBox];
+ }
+ }
+
+ return [fitSingleBox, fitAllBox];
+ }
+
+ _updateWorkspaceMode() {
+ this._updateWorkspacesState();
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ if (this.get_n_children() === 0)
+ return;
+
+ const vertical = global.workspaceManager.layout_rows === -1;
+ const rtl = this.text_direction === Clutter.TextDirection.RTL;
+
+ const fitMode = this._fitModeAdjustment.value;
+
+ let [fitSingleBox, fitAllBox] = this._getInitialBoxes(box);
+ const fitSingleSpacing =
+ this._getSpacing(fitSingleBox, FitMode.SINGLE, vertical);
+ fitSingleBox =
+ this._getFirstFitSingleWorkspaceBox(fitSingleBox, fitSingleSpacing, vertical);
+
+ const fitAllSpacing =
+ this._getSpacing(fitAllBox, FitMode.ALL, vertical);
+ fitAllBox =
+ this._getFirstFitAllWorkspaceBox(fitAllBox, fitAllSpacing, vertical);
+
+ // Account for RTL locales by reversing the list
+ const workspaces = this._workspaces.slice();
+ if (rtl)
+ workspaces.reverse();
+
+ const [fitSingleX1, fitSingleY1] = fitSingleBox.get_origin();
+ const [fitSingleWidth, fitSingleHeight] = fitSingleBox.get_size();
+ const [fitAllX1, fitAllY1] = fitAllBox.get_origin();
+ const [fitAllWidth, fitAllHeight] = fitAllBox.get_size();
+
+ workspaces.forEach(child => {
+ if (fitMode === FitMode.SINGLE)
+ box = fitSingleBox;
+ else if (fitMode === FitMode.ALL)
+ box = fitAllBox;
+ else
+ box = fitSingleBox.interpolate(fitAllBox, fitMode);
+
+ child.allocate_align_fill(box, 0.5, 0.5, false, false);
+
+ if (vertical) {
+ fitSingleBox.set_origin(
+ fitSingleX1,
+ fitSingleBox.y1 + fitSingleHeight + fitSingleSpacing);
+ fitAllBox.set_origin(
+ fitAllX1,
+ fitAllBox.y1 + fitAllHeight + fitAllSpacing);
+ } else {
+ fitSingleBox.set_origin(
+ fitSingleBox.x1 + fitSingleWidth + fitSingleSpacing,
+ fitSingleY1);
+ fitAllBox.set_origin(
+ fitAllBox.x1 + fitAllWidth + fitAllSpacing,
+ fitAllY1);
+ }
+ });
+ }
+
+ getActiveWorkspace() {
+ let workspaceManager = global.workspace_manager;
+ let active = workspaceManager.get_active_workspace_index();
+ return this._workspaces[active];
+ }
+
+ prepareToLeaveOverview() {
+ for (let w = 0; w < this._workspaces.length; w++)
+ this._workspaces[w].prepareToLeaveOverview();
+ }
+
+ syncStacking(stackIndices) {
+ for (let i = 0; i < this._workspaces.length; i++)
+ this._workspaces[i].syncStacking(stackIndices);
+ }
+
+ _scrollToActive() {
+ const { workspaceManager } = global;
+ const active = workspaceManager.get_active_workspace_index();
+
+ this._animating = true;
+ this._updateVisibility();
+
+ this._scrollAdjustment.remove_transition('value');
+ this._scrollAdjustment.ease(active, {
+ duration: WORKSPACE_SWITCH_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ onComplete: () => {
+ this._animating = false;
+ this._updateVisibility();
+ },
+ });
+ }
+
+ _updateVisibility() {
+ let workspaceManager = global.workspace_manager;
+ let active = workspaceManager.get_active_workspace_index();
+
+ const fitMode = this._fitModeAdjustment.value;
+ const singleFitMode = fitMode === FitMode.SINGLE;
+
+ for (let w = 0; w < this._workspaces.length; w++) {
+ let workspace = this._workspaces[w];
+
+ if (this._animating || this._gestureActive || !singleFitMode)
+ workspace.show();
+ else
+ workspace.visible = Math.abs(w - active) <= 1;
+ }
+ }
+
+ _updateWorkspaces() {
+ let workspaceManager = global.workspace_manager;
+ let newNumWorkspaces = workspaceManager.n_workspaces;
+
+ for (let j = 0; j < newNumWorkspaces; j++) {
+ let metaWorkspace = workspaceManager.get_workspace_by_index(j);
+ let workspace;
+
+ if (j >= this._workspaces.length) { /* added */
+ workspace = new Workspace.Workspace(
+ metaWorkspace,
+ this._monitorIndex,
+ this._overviewAdjustment);
+ this.add_actor(workspace);
+ this._workspaces[j] = workspace;
+ } else {
+ workspace = this._workspaces[j];
+
+ if (workspace.metaWorkspace != metaWorkspace) { /* removed */
+ workspace.destroy();
+ this._workspaces.splice(j, 1);
+ } /* else kept */
+ }
+ }
+
+ for (let j = this._workspaces.length - 1; j >= newNumWorkspaces; j--) {
+ this._workspaces[j].destroy();
+ this._workspaces.splice(j, 1);
+ }
+
+ this._updateWorkspacesState();
+ this._updateVisibility();
+ }
+
+ _activeWorkspaceChanged(_wm, _from, _to, _direction) {
+ this._scrollToActive();
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ this._workspaces = [];
+ }
+
+ startTouchGesture() {
+ this._gestureActive = true;
+
+ this._updateVisibility();
+ }
+
+ endTouchGesture() {
+ this._gestureActive = false;
+
+ // Make sure title captions etc are shown as necessary
+ this._scrollToActive();
+ this._updateVisibility();
+ }
+
+ // sync the workspaces' positions to the value of the scroll adjustment
+ // and change the active workspace if appropriate
+ _onScrollAdjustmentChanged() {
+ if (!this.has_allocation())
+ return;
+
+ const adj = this._scrollAdjustment;
+ const allowSwitch =
+ adj.get_transition('value') === null && !this._gestureActive;
+
+ let workspaceManager = global.workspace_manager;
+ let active = workspaceManager.get_active_workspace_index();
+ let current = Math.round(adj.value);
+
+ if (allowSwitch && active !== current) {
+ if (!this._workspaces[current]) {
+ // The current workspace was destroyed. This could happen
+ // when you are on the last empty workspace, and consolidate
+ // windows using the thumbnail bar.
+ // In that case, the intended behavior is to stay on the empty
+ // workspace, which is the last one, so pick it.
+ current = this._workspaces.length - 1;
+ }
+
+ let metaWorkspace = this._workspaces[current].metaWorkspace;
+ metaWorkspace.activate(global.get_current_time());
+ }
+
+ this._updateWorkspacesState();
+ this.queue_relayout();
+ }
+});
+
+var ExtraWorkspaceView = GObject.registerClass(
+class ExtraWorkspaceView extends WorkspacesViewBase {
+ _init(monitorIndex, overviewAdjustment) {
+ super._init(monitorIndex, overviewAdjustment);
+ this._workspace =
+ new Workspace.Workspace(null, monitorIndex, overviewAdjustment);
+ this.add_actor(this._workspace);
+ }
+
+ _updateWorkspaceMode() {
+ const overviewState = this._overviewAdjustment.value;
+
+ const progress = Math.clamp(overviewState,
+ OverviewControls.ControlsState.HIDDEN,
+ OverviewControls.ControlsState.WINDOW_PICKER);
+
+ this._workspace.stateAdjustment.value = progress;
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ const [width, height] = box.get_size();
+ const [, childWidth] = this._workspace.get_preferred_width(height);
+
+ const childBox = new Clutter.ActorBox();
+ childBox.set_origin(Math.round((width - childWidth) / 2), 0);
+ childBox.set_size(childWidth, height);
+ this._workspace.allocate(childBox);
+ }
+
+ getActiveWorkspace() {
+ return this._workspace;
+ }
+
+ prepareToLeaveOverview() {
+ this._workspace.prepareToLeaveOverview();
+ }
+
+ syncStacking(stackIndices) {
+ this._workspace.syncStacking(stackIndices);
+ }
+
+ startTouchGesture() {
+ }
+
+ endTouchGesture() {
+ }
+});
+
+const SecondaryMonitorDisplay = GObject.registerClass(
+class SecondaryMonitorDisplay extends St.Widget {
+ _init(monitorIndex, controls, scrollAdjustment, fitModeAdjustment, overviewAdjustment) {
+ this._monitorIndex = monitorIndex;
+ this._controls = controls;
+ this._scrollAdjustment = scrollAdjustment;
+ this._fitModeAdjustment = fitModeAdjustment;
+ this._overviewAdjustment = overviewAdjustment;
+
+ super._init({
+ style_class: 'secondary-monitor-workspaces',
+ constraints: new Layout.MonitorConstraint({
+ index: this._monitorIndex,
+ work_area: true,
+ }),
+ clip_to_allocation: true,
+ });
+
+ this.connect('destroy', () => this._onDestroy());
+
+ this._thumbnails = new ThumbnailsBox(
+ this._scrollAdjustment, monitorIndex);
+ this.add_child(this._thumbnails);
+
+ this._thumbnails.connect('notify::should-show',
+ () => this._updateThumbnailVisibility());
+
+ this._overviewAdjustment.connectObject('notify::value', () => {
+ this._updateThumbnailParams();
+ this.queue_relayout();
+ }, this);
+
+ this._settings = new Gio.Settings({ schema_id: MUTTER_SCHEMA });
+ this._settings.connect('changed::workspaces-only-on-primary',
+ () => this._workspacesOnPrimaryChanged());
+ this._workspacesOnPrimaryChanged();
+ }
+
+ _getThumbnailParamsForState(state) {
+ const { ControlsState } = OverviewControls;
+
+ let opacity, scale;
+ switch (state) {
+ case ControlsState.HIDDEN:
+ case ControlsState.WINDOW_PICKER:
+ opacity = 255;
+ scale = 1;
+ break;
+ case ControlsState.APP_GRID:
+ opacity = 0;
+ scale = 0.5;
+ break;
+ default:
+ opacity = 255;
+ scale = 1;
+ break;
+ }
+
+ return { opacity, scale };
+ }
+
+ _getThumbnailsHeight(box) {
+ if (!this._thumbnails.visible)
+ return 0;
+
+ const [width, height] = box.get_size();
+ const { expandFraction } = this._thumbnails;
+ const [thumbnailsHeight] = this._thumbnails.get_preferred_height(width);
+ return Math.min(
+ thumbnailsHeight * expandFraction,
+ height * MAX_THUMBNAIL_SCALE);
+ }
+
+ _getWorkspacesBoxForState(state, box, padding, thumbnailsHeight, spacing) {
+ const { ControlsState } = OverviewControls;
+ const workspaceBox = box.copy();
+ const [width, height] = workspaceBox.get_size();
+
+ switch (state) {
+ case ControlsState.HIDDEN:
+ break;
+ case ControlsState.WINDOW_PICKER:
+ workspaceBox.set_origin(0, padding + thumbnailsHeight + spacing);
+ workspaceBox.set_size(
+ width,
+ height - 2 * padding - thumbnailsHeight - spacing);
+ break;
+ case ControlsState.APP_GRID:
+ workspaceBox.set_origin(0, padding);
+ workspaceBox.set_size(
+ width,
+ height - 2 * padding);
+ break;
+ }
+
+ return workspaceBox;
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ const themeNode = this.get_theme_node();
+ const contentBox = themeNode.get_content_box(box);
+ const [width, height] = contentBox.get_size();
+ const { expandFraction } = this._thumbnails;
+ const spacing = themeNode.get_length('spacing') * expandFraction;
+ const padding =
+ Math.round((1 - SECONDARY_WORKSPACE_SCALE) * height / 2);
+
+ const thumbnailsHeight = this._getThumbnailsHeight(contentBox);
+
+ if (this._thumbnails.visible) {
+ const childBox = new Clutter.ActorBox();
+ childBox.set_origin(0, padding);
+ childBox.set_size(width, thumbnailsHeight);
+ this._thumbnails.allocate(childBox);
+ }
+
+ const {
+ currentState, initialState, finalState, transitioning, progress,
+ } = this._overviewAdjustment.getStateTransitionParams();
+
+ let workspacesBox;
+ const workspaceParams = [contentBox, padding, thumbnailsHeight, spacing];
+ if (!transitioning) {
+ workspacesBox =
+ this._getWorkspacesBoxForState(currentState, ...workspaceParams);
+ } else {
+ const initialBox =
+ this._getWorkspacesBoxForState(initialState, ...workspaceParams);
+ const finalBox =
+ this._getWorkspacesBoxForState(finalState, ...workspaceParams);
+ workspacesBox = initialBox.interpolate(finalBox, progress);
+ }
+ this._workspacesView.allocate(workspacesBox);
+ }
+
+ _onDestroy() {
+ if (this._settings)
+ this._settings.run_dispose();
+ this._settings = null;
+ }
+
+ _workspacesOnPrimaryChanged() {
+ this._updateWorkspacesView();
+ this._updateThumbnailVisibility();
+ }
+
+ _updateWorkspacesView() {
+ if (this._workspacesView)
+ this._workspacesView.destroy();
+
+ if (this._settings.get_boolean('workspaces-only-on-primary')) {
+ this._workspacesView = new ExtraWorkspaceView(
+ this._monitorIndex,
+ this._overviewAdjustment);
+ } else {
+ this._workspacesView = new WorkspacesView(
+ this._monitorIndex,
+ this._controls,
+ this._scrollAdjustment,
+ this._fitModeAdjustment,
+ this._overviewAdjustment);
+ }
+ this.add_child(this._workspacesView);
+ }
+
+ _updateThumbnailVisibility() {
+ const visible =
+ this._thumbnails.should_show &&
+ !this._settings.get_boolean('workspaces-only-on-primary');
+
+ if (this._thumbnails.visible === visible)
+ return;
+
+ this._thumbnails.show();
+ this._updateThumbnailParams();
+ this._thumbnails.ease_property('expand-fraction', visible ? 1 : 0, {
+ duration: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => (this._thumbnails.visible = visible),
+ });
+ }
+
+ _updateThumbnailParams() {
+ if (!this._thumbnails.visible)
+ return;
+
+ const { initialState, finalState, progress } =
+ this._overviewAdjustment.getStateTransitionParams();
+
+ const initialParams = this._getThumbnailParamsForState(initialState);
+ const finalParams = this._getThumbnailParamsForState(finalState);
+
+ const opacity =
+ Util.lerp(initialParams.opacity, finalParams.opacity, progress);
+ const scale =
+ Util.lerp(initialParams.scale, finalParams.scale, progress);
+
+ this._thumbnails.set({
+ opacity,
+ scale_x: scale,
+ scale_y: scale,
+ });
+ }
+
+ getActiveWorkspace() {
+ return this._workspacesView.getActiveWorkspace();
+ }
+
+ prepareToLeaveOverview() {
+ this._workspacesView.prepareToLeaveOverview();
+ }
+
+ syncStacking(stackIndices) {
+ this._workspacesView.syncStacking(stackIndices);
+ }
+
+ startTouchGesture() {
+ this._workspacesView.startTouchGesture();
+ }
+
+ endTouchGesture() {
+ this._workspacesView.endTouchGesture();
+ }
+});
+
+var WorkspacesDisplay = GObject.registerClass(
+class WorkspacesDisplay extends St.Widget {
+ _init(controls, scrollAdjustment, overviewAdjustment) {
+ super._init({
+ layout_manager: new Clutter.BinLayout(),
+ reactive: true,
+ });
+
+ this._controls = controls;
+ this._overviewAdjustment = overviewAdjustment;
+ this._fitModeAdjustment = new St.Adjustment({
+ actor: this,
+ value: FitMode.SINGLE,
+ lower: FitMode.SINGLE,
+ upper: FitMode.ALL,
+ });
+
+ let workspaceManager = global.workspace_manager;
+ this._scrollAdjustment = scrollAdjustment;
+
+ global.window_manager.connectObject('switch-workspace',
+ this._activeWorkspaceChanged.bind(this), this);
+
+ this._swipeTracker = new SwipeTracker.SwipeTracker(
+ Main.layoutManager.overviewGroup,
+ Clutter.Orientation.HORIZONTAL,
+ Shell.ActionMode.OVERVIEW,
+ { allowDrag: false });
+ this._swipeTracker.allowLongSwipes = true;
+ this._swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this));
+ this._swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this));
+ this._swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this));
+ this.connect('notify::mapped', this._updateSwipeTracker.bind(this));
+
+ workspaceManager.connectObject(
+ 'workspaces-reordered', this._workspacesReordered.bind(this),
+ 'notify::layout-rows', this._updateTrackerOrientation.bind(this), this);
+ this._updateTrackerOrientation();
+
+ Main.overview.connectObject(
+ 'window-drag-begin', this._windowDragBegin.bind(this),
+ 'window-drag-end', this._windowDragEnd.bind(this), this);
+
+ this._primaryVisible = true;
+ this._primaryIndex = Main.layoutManager.primaryIndex;
+ this._workspacesViews = [];
+
+ this._settings = new Gio.Settings({ schema_id: MUTTER_SCHEMA });
+
+ this._inWindowDrag = false;
+ this._leavingOverview = false;
+
+ this._gestureActive = false; // touch(pad) gestures
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ if (this._parentSetLater) {
+ Meta.later_remove(this._parentSetLater);
+ this._parentSetLater = 0;
+ }
+ }
+
+ _windowDragBegin() {
+ this._inWindowDrag = true;
+ this._updateSwipeTracker();
+ }
+
+ _windowDragEnd() {
+ this._inWindowDrag = false;
+ this._updateSwipeTracker();
+ }
+
+ _updateSwipeTracker() {
+ this._swipeTracker.enabled =
+ this.mapped &&
+ !this._inWindowDrag &&
+ !this._leavingOverview;
+ }
+
+ _workspacesReordered() {
+ let workspaceManager = global.workspace_manager;
+
+ this._scrollAdjustment.value =
+ workspaceManager.get_active_workspace_index();
+ }
+
+ _activeWorkspaceChanged(_wm, _from, to, _direction) {
+ if (this._gestureActive)
+ return;
+
+ this._scrollAdjustment.ease(to, {
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ duration: WORKSPACE_SWITCH_TIME,
+ });
+ }
+
+ _updateTrackerOrientation() {
+ const { layoutRows } = global.workspace_manager;
+ this._swipeTracker.orientation = layoutRows !== -1
+ ? Clutter.Orientation.HORIZONTAL
+ : Clutter.Orientation.VERTICAL;
+ }
+
+ _directionForProgress(progress) {
+ if (global.workspace_manager.layout_rows === -1) {
+ return progress > 0
+ ? Meta.MotionDirection.DOWN
+ : Meta.MotionDirection.UP;
+ } else if (this.text_direction === Clutter.TextDirection.RTL) {
+ return progress > 0
+ ? Meta.MotionDirection.LEFT
+ : Meta.MotionDirection.RIGHT;
+ } else {
+ return progress > 0
+ ? Meta.MotionDirection.RIGHT
+ : Meta.MotionDirection.LEFT;
+ }
+ }
+
+ _switchWorkspaceBegin(tracker, monitor) {
+ if (this._workspacesOnlyOnPrimary && monitor !== this._primaryIndex)
+ return;
+
+ let workspaceManager = global.workspace_manager;
+ let adjustment = this._scrollAdjustment;
+ if (this._gestureActive)
+ adjustment.remove_transition('value');
+
+ const distance = global.workspace_manager.layout_rows === -1
+ ? this.height : this.width;
+
+ for (let i = 0; i < this._workspacesViews.length; i++)
+ this._workspacesViews[i].startTouchGesture();
+
+ let progress = adjustment.value / adjustment.page_size;
+ let points = Array.from(
+ { length: workspaceManager.n_workspaces }, (v, i) => i);
+
+ tracker.confirmSwipe(distance, points, progress, Math.round(progress));
+
+ this._gestureActive = true;
+ }
+
+ _switchWorkspaceUpdate(tracker, progress) {
+ let adjustment = this._scrollAdjustment;
+ adjustment.value = progress * adjustment.page_size;
+ }
+
+ _switchWorkspaceEnd(tracker, duration, endProgress) {
+ let workspaceManager = global.workspace_manager;
+ let newWs = workspaceManager.get_workspace_by_index(endProgress);
+
+ this._scrollAdjustment.ease(endProgress, {
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ duration,
+ onComplete: () => {
+ if (!newWs.active)
+ newWs.activate(global.get_current_time());
+ this._endTouchGesture();
+ },
+ });
+ }
+
+ _endTouchGesture() {
+ for (let i = 0; i < this._workspacesViews.length; i++)
+ this._workspacesViews[i].endTouchGesture();
+ this._gestureActive = false;
+ }
+
+ vfunc_navigate_focus(from, direction) {
+ return this._getPrimaryView()?.navigate_focus(from, direction, false);
+ }
+
+ setPrimaryWorkspaceVisible(visible) {
+ if (this._primaryVisible === visible)
+ return;
+
+ this._primaryVisible = visible;
+
+ const primaryIndex = Main.layoutManager.primaryIndex;
+ const primaryWorkspace = this._workspacesViews[primaryIndex];
+ if (primaryWorkspace)
+ primaryWorkspace.visible = visible;
+ }
+
+ prepareToEnterOverview() {
+ this.show();
+ this._updateWorkspacesViews();
+
+ Main.overview.connectObject(
+ 'windows-restacked', this._onRestacked.bind(this),
+ 'scroll-event', this._onScrollEvent.bind(this), this);
+
+ global.stage.connectObject(
+ 'key-press-event', this._onKeyPressEvent.bind(this), this);
+ }
+
+ prepareToLeaveOverview() {
+ for (let i = 0; i < this._workspacesViews.length; i++)
+ this._workspacesViews[i].prepareToLeaveOverview();
+
+ this._leavingOverview = true;
+ this._updateSwipeTracker();
+ }
+
+ vfunc_hide() {
+ Main.overview.disconnectObject(this);
+ global.stage.disconnectObject(this);
+
+ for (let i = 0; i < this._workspacesViews.length; i++)
+ this._workspacesViews[i].destroy();
+ this._workspacesViews = [];
+
+ this._leavingOverview = false;
+
+ super.vfunc_hide();
+ }
+
+ _updateWorkspacesViews() {
+ for (let i = 0; i < this._workspacesViews.length; i++)
+ this._workspacesViews[i].destroy();
+
+ this._primaryIndex = Main.layoutManager.primaryIndex;
+ this._workspacesViews = [];
+ let monitors = Main.layoutManager.monitors;
+ for (let i = 0; i < monitors.length; i++) {
+ let view;
+ if (i === this._primaryIndex) {
+ view = new WorkspacesView(i,
+ this._controls,
+ this._scrollAdjustment,
+ this._fitModeAdjustment,
+ this._overviewAdjustment);
+
+ view.visible = this._primaryVisible;
+ this.bind_property('opacity', view, 'opacity', GObject.BindingFlags.SYNC_CREATE);
+ this.add_child(view);
+ } else {
+ view = new SecondaryMonitorDisplay(i,
+ this._controls,
+ this._scrollAdjustment,
+ this._fitModeAdjustment,
+ this._overviewAdjustment);
+ Main.layoutManager.overviewGroup.add_actor(view);
+ }
+
+ this._workspacesViews.push(view);
+ }
+ }
+
+ _getMonitorIndexForEvent(event) {
+ let [x, y] = event.get_coords();
+ let rect = new Meta.Rectangle({ x, y, width: 1, height: 1 });
+ return global.display.get_monitor_index_for_rect(rect);
+ }
+
+ _getPrimaryView() {
+ if (!this._workspacesViews.length)
+ return null;
+ return this._workspacesViews[this._primaryIndex];
+ }
+
+ activeWorkspaceHasMaximizedWindows() {
+ const primaryView = this._getPrimaryView();
+ return primaryView
+ ? primaryView.getActiveWorkspace().hasMaximizedWindows()
+ : false;
+ }
+
+ _onRestacked(overview, stackIndices) {
+ for (let i = 0; i < this._workspacesViews.length; i++)
+ this._workspacesViews[i].syncStacking(stackIndices);
+ }
+
+ _onScrollEvent(actor, event) {
+ if (this._swipeTracker.canHandleScrollEvent(event))
+ return Clutter.EVENT_PROPAGATE;
+
+ if (!this.mapped)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._workspacesOnlyOnPrimary &&
+ this._getMonitorIndexForEvent(event) != this._primaryIndex)
+ return Clutter.EVENT_PROPAGATE;
+
+ return Main.wm.handleWorkspaceScroll(event);
+ }
+
+ _onKeyPressEvent(actor, event) {
+ const { ControlsState } = OverviewControls;
+ if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (!this.reactive)
+ return Clutter.EVENT_PROPAGATE;
+
+ const { workspaceManager } = global;
+ const vertical = workspaceManager.layout_rows === -1;
+ const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+
+ let which;
+ switch (event.get_key_symbol()) {
+ case Clutter.KEY_Page_Up:
+ if (vertical)
+ which = Meta.MotionDirection.UP;
+ else if (rtl)
+ which = Meta.MotionDirection.RIGHT;
+ else
+ which = Meta.MotionDirection.LEFT;
+ break;
+ case Clutter.KEY_Page_Down:
+ if (vertical)
+ which = Meta.MotionDirection.DOWN;
+ else if (rtl)
+ which = Meta.MotionDirection.LEFT;
+ else
+ which = Meta.MotionDirection.RIGHT;
+ break;
+ case Clutter.KEY_Home:
+ which = 0;
+ break;
+ case Clutter.KEY_End:
+ which = workspaceManager.n_workspaces - 1;
+ break;
+ default:
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ let ws;
+ if (which < 0)
+ // Negative workspace numbers are directions
+ // with respect to the current workspace
+ ws = workspaceManager.get_active_workspace().get_neighbor(which);
+ else
+ // Otherwise it is a workspace index
+ ws = workspaceManager.get_workspace_by_index(which);
+
+ if (ws)
+ Main.wm.actionMoveWorkspace(ws);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ get _workspacesOnlyOnPrimary() {
+ return this._settings.get_boolean('workspaces-only-on-primary');
+ }
+
+ get fitModeAdjustment() {
+ return this._fitModeAdjustment;
+ }
+});
diff --git a/js/ui/xdndHandler.js b/js/ui/xdndHandler.js
new file mode 100644
index 0000000..3a4880b
--- /dev/null
+++ b/js/ui/xdndHandler.js
@@ -0,0 +1,116 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported XdndHandler */
+
+const { Clutter } = imports.gi;
+const Signals = imports.misc.signals;
+
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+
+var XdndHandler = class extends Signals.EventEmitter {
+ constructor() {
+ super();
+
+ // Used to display a clone of the cursor window when the
+ // window group is hidden (like it happens in the overview)
+ this._cursorWindowClone = null;
+
+ // Used as a drag actor in case we don't have a cursor window clone
+ this._dummy = new Clutter.Actor({ width: 1, height: 1, opacity: 0 });
+ Main.uiGroup.add_actor(this._dummy);
+ this._dummy.hide();
+
+ var dnd = global.backend.get_dnd();
+ dnd.connect('dnd-enter', this._onEnter.bind(this));
+ dnd.connect('dnd-position-change', this._onPositionChanged.bind(this));
+ dnd.connect('dnd-leave', this._onLeave.bind(this));
+ }
+
+ // Called when the user cancels the drag (i.e release the button)
+ _onLeave() {
+ global.window_group.disconnectObject(this);
+ if (this._cursorWindowClone) {
+ this._cursorWindowClone.destroy();
+ this._cursorWindowClone = null;
+ }
+
+ this.emit('drag-end');
+ }
+
+ _onEnter() {
+ global.window_group.connectObject('notify::visible',
+ this._onWindowGroupVisibilityChanged.bind(this), this);
+
+ this.emit('drag-begin', global.get_current_time());
+ }
+
+ _onWindowGroupVisibilityChanged() {
+ if (!global.window_group.visible) {
+ if (this._cursorWindowClone)
+ return;
+
+ let windows = global.get_window_actors();
+ let cursorWindow = windows[windows.length - 1];
+
+ // FIXME: more reliable way?
+ if (!cursorWindow.get_meta_window().is_override_redirect())
+ return;
+
+ const constraintPosition = new Clutter.BindConstraint({
+ coordinate: Clutter.BindCoordinate.POSITION,
+ source: cursorWindow,
+ });
+
+ this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow });
+ Main.uiGroup.add_actor(this._cursorWindowClone);
+
+ // Make sure that the clone has the same position as the source
+ this._cursorWindowClone.add_constraint(constraintPosition);
+ } else {
+ if (!this._cursorWindowClone)
+ return;
+
+ this._cursorWindowClone.destroy();
+ this._cursorWindowClone = null;
+ }
+ }
+
+ _onPositionChanged(obj, x, y) {
+ let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
+
+ // Make sure that the cursor window is on top
+ if (this._cursorWindowClone)
+ Main.uiGroup.set_child_above_sibling(this._cursorWindowClone, null);
+
+ let dragEvent = {
+ x,
+ y,
+ dragActor: this._cursorWindowClone ?? this._dummy,
+ source: this,
+ targetActor: pickedActor,
+ };
+
+ for (let i = 0; i < DND.dragMonitors.length; i++) {
+ let motionFunc = DND.dragMonitors[i].dragMotion;
+ if (motionFunc) {
+ let result = motionFunc(dragEvent);
+ if (result != DND.DragMotionResult.CONTINUE)
+ return;
+ }
+ }
+
+ while (pickedActor) {
+ if (pickedActor._delegate && pickedActor._delegate.handleDragOver) {
+ let [r_, targX, targY] = pickedActor.transform_stage_point(x, y);
+ let result = pickedActor._delegate.handleDragOver(this,
+ dragEvent.dragActor,
+ targX,
+ targY,
+ global.get_current_time());
+ if (result != DND.DragMotionResult.CONTINUE)
+ return;
+ }
+ pickedActor = pickedActor.get_parent();
+ }
+ }
+};
diff --git a/lint/eslintrc-gjs.yml b/lint/eslintrc-gjs.yml
new file mode 100644
index 0000000..62fd4a2
--- /dev/null
+++ b/lint/eslintrc-gjs.yml
@@ -0,0 +1,264 @@
+---
+# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+# SPDX-FileCopyrightText: 2018 Claudio André <claudioandre.br@gmail.com>
+env:
+ es2021: true
+extends: 'eslint:recommended'
+plugins:
+ - jsdoc
+rules:
+ array-bracket-newline:
+ - error
+ - consistent
+ array-bracket-spacing:
+ - error
+ - never
+ array-callback-return: error
+ arrow-parens:
+ - error
+ - as-needed
+ arrow-spacing: error
+ block-scoped-var: error
+ block-spacing: error
+ brace-style: error
+ # Waiting for this to have matured a bit in eslint
+ # camelcase:
+ # - error
+ # - properties: never
+ # allow: [^vfunc_, ^on_, _instance_init]
+ comma-dangle:
+ - error
+ - arrays: always-multiline
+ objects: always-multiline
+ functions: never
+ comma-spacing:
+ - error
+ - before: false
+ after: true
+ comma-style:
+ - error
+ - last
+ computed-property-spacing: error
+ curly:
+ - error
+ - multi-or-nest
+ - consistent
+ dot-location:
+ - error
+ - property
+ eol-last: error
+ eqeqeq: error
+ func-call-spacing: error
+ func-name-matching: error
+ func-style:
+ - error
+ - declaration
+ - allowArrowFunctions: true
+ indent:
+ - error
+ - 4
+ - ignoredNodes:
+ # Allow not indenting the body of GObject.registerClass, since in the
+ # future it's intended to be a decorator
+ - 'CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child'
+ # Allow dedenting chained member expressions
+ MemberExpression: 'off'
+ jsdoc/check-alignment: error
+ jsdoc/check-param-names: error
+ jsdoc/check-tag-names: error
+ jsdoc/check-types: error
+ jsdoc/implements-on-classes: error
+ jsdoc/newline-after-description: error
+ jsdoc/require-jsdoc: error
+ jsdoc/require-param: error
+ jsdoc/require-param-description: error
+ jsdoc/require-param-name: error
+ jsdoc/require-param-type: error
+ key-spacing:
+ - error
+ - beforeColon: false
+ afterColon: true
+ keyword-spacing:
+ - error
+ - before: true
+ after: true
+ linebreak-style:
+ - error
+ - unix
+ lines-between-class-members:
+ - error
+ - always
+ - exceptAfterSingleLine: true
+ max-nested-callbacks: error
+ max-statements-per-line: error
+ new-parens: error
+ no-array-constructor: error
+ no-await-in-loop: error
+ no-caller: error
+ no-constant-condition:
+ - error
+ - checkLoops: false
+ no-div-regex: error
+ no-empty:
+ - error
+ - allowEmptyCatch: true
+ no-extra-bind: error
+ no-extra-parens:
+ - error
+ - all
+ - conditionalAssign: false
+ nestedBinaryExpressions: false
+ returnAssign: false
+ no-implicit-coercion:
+ - error
+ - allow:
+ - '!!'
+ no-invalid-this: error
+ no-iterator: error
+ no-label-var: error
+ no-lonely-if: error
+ no-loop-func: error
+ no-nested-ternary: error
+ no-new-object: error
+ no-new-wrappers: error
+ no-octal-escape: error
+ no-proto: error
+ no-prototype-builtins: 'off'
+ no-restricted-globals: [error, window]
+ no-restricted-properties:
+ - error
+ - object: Lang
+ property: copyProperties
+ message: Use Object.assign()
+ - object: Lang
+ property: bind
+ message: Use arrow notation or Function.prototype.bind()
+ - object: Lang
+ property: Class
+ message: Use ES6 classes
+ no-restricted-syntax:
+ - error
+ - selector: >-
+ MethodDefinition[key.name="_init"] >
+ FunctionExpression[params.length=1] >
+ BlockStatement[body.length=1]
+ CallExpression[arguments.length=1][callee.object.type="Super"][callee.property.name="_init"] >
+ Identifier:first-child
+ message: _init() that only calls super._init() is unnecessary
+ - selector: >-
+ MethodDefinition[key.name="_init"] >
+ FunctionExpression[params.length=0] >
+ BlockStatement[body.length=1]
+ CallExpression[arguments.length=0][callee.object.type="Super"][callee.property.name="_init"]
+ message: _init() that only calls super._init() is unnecessary
+ - selector: BinaryExpression[operator="instanceof"][right.name="Array"]
+ message: Use Array.isArray()
+ no-return-assign: error
+ no-return-await: error
+ no-self-compare: error
+ no-shadow: error
+ no-shadow-restricted-names: error
+ no-spaced-func: error
+ no-tabs: error
+ no-template-curly-in-string: error
+ no-throw-literal: error
+ no-trailing-spaces: error
+ no-undef-init: error
+ no-unneeded-ternary: error
+ no-unused-expressions: error
+ no-unused-vars:
+ - error
+ # Vars use a suffix _ instead of a prefix because of file-scope private vars
+ - varsIgnorePattern: (^unused|_$)
+ argsIgnorePattern: ^(unused|_)
+ no-useless-call: error
+ no-useless-computed-key: error
+ no-useless-concat: error
+ no-useless-constructor: error
+ no-useless-rename: error
+ no-useless-return: error
+ no-whitespace-before-property: error
+ no-with: error
+ nonblock-statement-body-position:
+ - error
+ - below
+ object-curly-newline:
+ - error
+ - consistent: true
+ multiline: true
+ object-curly-spacing: error
+ object-shorthand: error
+ operator-assignment: error
+ operator-linebreak: error
+ padded-blocks:
+ - error
+ - never
+ # These may be a bit controversial, we can try them out and enable them later
+ # prefer-const: error
+ # prefer-destructuring: error
+ prefer-numeric-literals: error
+ prefer-promise-reject-errors: error
+ prefer-rest-params: error
+ prefer-spread: error
+ prefer-template: error
+ quotes:
+ - error
+ - single
+ - avoidEscape: true
+ require-await: error
+ rest-spread-spacing: error
+ semi:
+ - error
+ - always
+ semi-spacing:
+ - error
+ - before: false
+ after: true
+ semi-style: error
+ space-before-blocks: error
+ space-before-function-paren:
+ - error
+ - named: never
+ # for `function ()` and `async () =>`, preserve space around keywords
+ anonymous: always
+ asyncArrow: always
+ space-in-parens: error
+ space-infix-ops:
+ - error
+ - int32Hint: false
+ space-unary-ops: error
+ spaced-comment: error
+ switch-colon-spacing: error
+ symbol-description: error
+ template-curly-spacing: error
+ template-tag-spacing: error
+ unicode-bom: error
+ wrap-iife:
+ - error
+ - inside
+ yield-star-spacing: error
+ yoda: error
+settings:
+ jsdoc:
+ mode: typescript
+globals:
+ ARGV: readonly
+ Debugger: readonly
+ GIRepositoryGType: readonly
+ globalThis: readonly
+ imports: readonly
+ Intl: readonly
+ log: readonly
+ logError: readonly
+ print: readonly
+ printerr: readonly
+ window: readonly
+ TextEncoder: readonly
+ TextDecoder: readonly
+ console: readonly
+ setTimeout: readonly
+ setInterval: readonly
+ clearTimeout: readonly
+ clearInterval: readonly
+parserOptions:
+ ecmaVersion: 2022
diff --git a/lint/eslintrc-legacy.yml b/lint/eslintrc-legacy.yml
new file mode 100644
index 0000000..e0d4fa2
--- /dev/null
+++ b/lint/eslintrc-legacy.yml
@@ -0,0 +1,24 @@
+rules:
+ eqeqeq: off
+ indent:
+ - error
+ - 4
+ - ignoredNodes:
+ - 'CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child'
+ CallExpression:
+ arguments: first
+ jsdoc/check-alignment: off
+ jsdoc/check-param-names: off
+ jsdoc/check-tag-names: off
+ jsdoc/check-types: off
+ jsdoc/implements-on-classes: off
+ jsdoc/newline-after-description: off
+ jsdoc/require-jsdoc: off
+ jsdoc/require-param: off
+ jsdoc/require-param-description: off
+ jsdoc/require-param-name: off
+ jsdoc/require-param-type: off
+ object-curly-spacing:
+ - error
+ - always
+ quotes: off
diff --git a/lint/eslintrc-shell.yml b/lint/eslintrc-shell.yml
new file mode 100644
index 0000000..f0e1901
--- /dev/null
+++ b/lint/eslintrc-shell.yml
@@ -0,0 +1,32 @@
+rules:
+ camelcase:
+ - error
+ - properties: never
+ allow: [^vfunc_, ^on_]
+ consistent-return: error
+ eqeqeq:
+ - error
+ - smart
+ key-spacing:
+ - error
+ - mode: minimum
+ beforeColon: false
+ afterColon: true
+ prefer-arrow-callback: error
+
+overrides:
+ - files: js/**
+ excludedFiles:
+ - js/portalHelper/*
+ - js/misc/extensionUtils.js
+ globals:
+ global: readonly
+ _: readonly
+ C_: readonly
+ N_: readonly
+ ngettext: readonly
+ - files: subprojects/extensions-app/js/**
+ globals:
+ _: readonly
+ C_: readonly
+ N_: readonly
diff --git a/man/gnome-shell.1 b/man/gnome-shell.1
new file mode 100644
index 0000000..03a0dbd
--- /dev/null
+++ b/man/gnome-shell.1
@@ -0,0 +1,131 @@
+'\" t
+.\" Title: gnome-shell
+.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author]
+.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
+.\" Date: December 2020
+.\" Manual: User Commands
+.\" Source: GNOME-SHELL
+.\" Language: English
+.\"
+.TH "GNOME\-SHELL" "1" "December 2020" "GNOME\-SHELL" "User Commands"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+gnome-shell \- Graphical shell for the GNOME desktop
+.SH "SYNOPSIS"
+.sp
+\fBgnome\-shell\fR [\fIOPTION\fR\&...]
+.SH "DESCRIPTION"
+.sp
+GNOME shell provides core user interface functions for the GNOME 3 desktop, like switching to windows and launching applications\&. GNOME shell takes advantage of the capabilities of modern graphics hardware and introduces innovative user interface concepts to provide a visually attractive and easy to use experience\&.
+.sp
+gnome\-shell is a required component of the GNOME desktop, i\&.e\&. it is listed in the RequiredComponents field of /usr/share/gnome\-session/sessions/gnome\&.session\&. It is started in the window manager phase of the session\&.
+.SH "OPTIONS"
+.PP
+\fB\-\-wayland\fR
+.RS 4
+Run as a wayland compositor
+.RE
+.PP
+\fB\-\-display\-server\fR
+.RS 4
+Run as a full display server, rather than nested
+.RE
+.PP
+\fB\-\-nested\fR
+.RS 4
+Run as a nested compositor
+.RE
+.PP
+\fB\-\-no\-x11\fR
+.RS 4
+Run wayland compositor without starting Xwayland
+.RE
+.PP
+\fB\-\-x11\fR
+.RS 4
+Run with X11 backend
+.RE
+.PP
+\fB\-\-wayland\-display\fR=\fIDISPLAY\fR
+.RS 4
+Wayland display name to use
+.RE
+.PP
+\fB\-d\fR, \fB\-\-display\fR=\fIDISPLAY\fR
+.RS 4
+X display to use
+.RE
+.PP
+\fB\-r\fR, \fB\-\-replace\fR
+.RS 4
+Replace the running window manager
+.RE
+.PP
+\fB\-\-sm\-disable\fR
+.RS 4
+Disable connection to the session manager
+.RE
+.PP
+\fB\-\-sm\-client\-id\fR=\fIID\fR
+.RS 4
+Specify session management
+\fIID\fR
+.RE
+.PP
+\fB\-\-sm\-save\-file\fR=\fIFILE\fR
+.RS 4
+Initialize session from
+\fIFILE\fR
+.RE
+.PP
+\fB\-\-sync\fR
+.RS 4
+Make X calls synchronous
+.RE
+.PP
+\fB\-\-mode\fR=\fIMODE\fR
+.RS 4
+Use a specific mode, e\&.g\&. "gdm" for login screen
+.RE
+.PP
+\fB\-\-list\-modes\fR
+.RS 4
+List possible modes and exit
+.RE
+.PP
+\fB\-\-version\fR
+.RS 4
+Print version and exit
+.RE
+.PP
+\fB\-\-help\fR
+.RS 4
+Display help and exit
+.RE
+.SH "FILES"
+.sp
+/usr/share/gnome\-session/sessions/gnome\&.session, /usr/share/applications/org\&.gnome\&.Shell\&.desktop\&.
+.SH "BUGS"
+.sp
+The bug tracker can be reached by visiting the website \m[blue]\fBhttps://gitlab\&.gnome\&.org/GNOME/gnome\-shell/issues\fR\m[]\&. Before sending a bug report, please verify that you have the latest version of gnome\-shell\&. Many bugs (major and minor) are fixed at each release, and if yours is out of date, the problem may already have been solved\&.
+.SH "ADDITIONAL INFORMATION"
+.sp
+For further information, visit the website \m[blue]\fBhttps://wiki\&.gnome\&.org/Projects/GnomeShell\fR\m[]\&.
diff --git a/man/gnome-shell.txt b/man/gnome-shell.txt
new file mode 100644
index 0000000..fa41514
--- /dev/null
+++ b/man/gnome-shell.txt
@@ -0,0 +1,93 @@
+GNOME-SHELL(1)
+=============
+:man manual: User Commands
+:man source: GNOME-SHELL
+:doctype: manpage
+:date: December 2020
+
+NAME
+----
+gnome-shell - Graphical shell for the GNOME desktop
+
+SYNOPSIS
+--------
+*gnome-shell* ['OPTION'...]
+
+DESCRIPTION
+-----------
+GNOME shell provides core user interface functions for the GNOME 3 desktop,
+like switching to windows and launching applications. GNOME shell takes
+advantage of the capabilities of modern graphics hardware and introduces
+innovative user interface concepts to provide a visually attractive and
+easy to use experience.
+
+gnome-shell is a required component of the GNOME desktop, i.e. it is listed in
+the RequiredComponents field of /usr/share/gnome-session/sessions/gnome.session.
+It is started in the window manager phase of the session.
+
+OPTIONS
+-------
+*--wayland*::
+Run as a wayland compositor
+
+*--display-server*::
+Run as a full display server, rather than nested
+
+*--nested*::
+Run as a nested compositor
+
+*--no-x11*::
+Run wayland compositor without starting Xwayland
+
+*--x11*::
+Run with X11 backend
+
+*--wayland-display*='DISPLAY'::
+Wayland display name to use
+
+*-d*, *--display*='DISPLAY'::
+X display to use
+
+*-r*, *--replace*::
+Replace the running window manager
+
+*--sm-disable*::
+Disable connection to the session manager
+
+*--sm-client-id*='ID'::
+Specify session management 'ID'
+
+*--sm-save-file*='FILE'::
+Initialize session from 'FILE'
+
+*--sync*::
+Make X calls synchronous
+
+*--mode*='MODE'::
+Use a specific mode, e.g. "gdm" for login screen
+
+*--list-modes*::
+List possible modes and exit
+
+*--version*::
+Print version and exit
+
+*--help*::
+Display help and exit
+
+FILES
+-----
+/usr/share/gnome-session/sessions/gnome.session,
+/usr/share/applications/org.gnome.Shell.desktop.
+
+BUGS
+----
+The bug tracker can be reached by visiting the website
+https://gitlab.gnome.org/GNOME/gnome-shell/issues.
+Before sending a bug report, please verify that you have the latest version
+of gnome-shell. Many bugs (major and minor) are fixed at each release, and
+if yours is out of date, the problem may already have been solved.
+
+ADDITIONAL INFORMATION
+----------------------
+For further information, visit the website https://wiki.gnome.org/Projects/GnomeShell.
diff --git a/man/meson.build b/man/meson.build
new file mode 100644
index 0000000..aa37ef0
--- /dev/null
+++ b/man/meson.build
@@ -0,0 +1,7 @@
+custom_target('man page',
+ input: ['gnome-shell.txt', 'stylesheet.xsl'],
+ output: 'gnome-shell.1',
+ command: [a2x, '-D', '@OUTDIR@', '--xsl-file', '@INPUT1@', '-f' ,'manpage', '@INPUT0@'],
+ install_dir: mandir + '/man1',
+ install: true
+)
diff --git a/man/stylesheet.xsl b/man/stylesheet.xsl
new file mode 100644
index 0000000..047bd1b
--- /dev/null
+++ b/man/stylesheet.xsl
@@ -0,0 +1,27 @@
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'>
+<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"/>
+
+<xsl:template match="variablelist/title">
+ <xsl:text>.PP&#10;</xsl:text>
+ <xsl:call-template name="bold">
+ <xsl:with-param name="node" select="."/>
+ <xsl:with-param name="context" select=".."/>
+ </xsl:call-template>
+ <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+<xsl:template match="varlistentry[preceding-sibling::title]">
+ <xsl:if test="not(preceding-sibling::varlistentry)">
+ <xsl:text>.RS 4&#10;</xsl:text>
+ <!-- comment out the leading .PP added by the original template -->
+ <xsl:text>.\"</xsl:text>
+ </xsl:if>
+ <xsl:apply-imports/>
+ <xsl:if test="position() = last()">
+ <xsl:text>.RE&#10;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..1fa4311
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,332 @@
+project('gnome-shell', 'c',
+ version: '43.9',
+ meson_version: '>= 0.58.0',
+ license: 'GPLv2+'
+)
+
+split_version = meson.project_version().split('.')
+
+# We depend on a specific version of the libmutter API. The mutter variants of
+# the Cogl and Clutter libraries also use this API version.
+# The API version is increased each development cycle,
+# starting with 0 in 3.23.x
+mutter_api_version = '11'
+
+clutter_pc = 'mutter-clutter-' + mutter_api_version
+cogl_pc = 'mutter-cogl-' + mutter_api_version
+cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version
+libmutter_pc = 'libmutter-' + mutter_api_version
+libmutter_test_pc = 'libmutter-test-' + mutter_api_version
+
+ecal_req = '>= 3.33.1'
+eds_req = '>= 3.33.1'
+gcr_req = '>= 3.90.0'
+gio_req = '>= 2.56.0'
+gi_req = '>= 1.49.1'
+gjs_req = '>= 1.73.1'
+gtk_req = '>= 3.15.0'
+mutter_req = '>= 43.0'
+polkit_req = '>= 0.100'
+schemas_req = '>= 42.beta'
+startup_req = '>= 0.11'
+ibus_req = '>= 1.5.19'
+gnome_desktop_req = '>= 3.35.90'
+
+nm_req = '>= 1.10.4'
+secret_req = '>= 0.18'
+
+fs = import('fs')
+gnome = import('gnome')
+i18n = import('i18n')
+
+prefix = get_option('prefix')
+
+bindir = join_paths(prefix, get_option('bindir'))
+datadir = join_paths(prefix, get_option('datadir'))
+libdir = join_paths(prefix, get_option('libdir'))
+libexecdir = join_paths(prefix, get_option('libexecdir'))
+mandir = join_paths(prefix, get_option('mandir'))
+sysconfdir = join_paths(prefix, get_option('sysconfdir'))
+
+pkgdatadir = join_paths(datadir, meson.project_name())
+pkglibdir = join_paths(libdir, meson.project_name())
+
+autostartdir = join_paths(sysconfdir, 'xdg', 'autostart')
+desktopdir = join_paths(datadir, 'applications')
+icondir = join_paths(datadir, 'icons')
+ifacedir = join_paths(datadir, 'dbus-1', 'interfaces')
+localedir = join_paths(datadir, 'locale')
+metainfodir = join_paths(datadir, 'metainfo')
+portaldir = join_paths(datadir, 'xdg-desktop-portal', 'portals')
+schemadir = join_paths(datadir, 'glib-2.0', 'schemas')
+servicedir = join_paths(datadir, 'dbus-1', 'services')
+
+keybindings_dep = dependency('gnome-keybindings', required: false)
+if keybindings_dep.found()
+ keysdir = keybindings_dep.get_variable('keysdir', pkgconfig_define: ['datadir', datadir])
+else
+ keysdir = join_paths(datadir, 'gnome-control-center', 'keybindings')
+endif
+
+atk_bridge_dep = dependency('atk-bridge-2.0')
+ecal_dep = dependency('libecal-2.0', version: ecal_req)
+eds_dep = dependency('libedataserver-1.2', version: eds_req)
+gcr_dep = dependency('gcr-4', version: gcr_req)
+gdk_x11_dep = dependency('gdk-x11-3.0')
+gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0')
+gi_dep = dependency('gobject-introspection-1.0', version: gi_req)
+gio_dep = dependency('gio-2.0', version: gio_req)
+gio_unix_dep = dependency('gio-unix-2.0', version: gio_req)
+gjs_dep = dependency('gjs-1.0', version: gjs_req)
+gtk_dep = dependency('gtk+-3.0', version: gtk_req)
+libxml_dep = dependency('libxml-2.0')
+clutter_dep = dependency(clutter_pc, version: mutter_req)
+cogl_dep = dependency(cogl_pc, version: mutter_req)
+cogl_pango_dep = dependency(cogl_pango_pc, version: mutter_req)
+mutter_dep = dependency(libmutter_pc, version: mutter_req)
+polkit_dep = dependency('polkit-agent-1', version: polkit_req)
+startup_dep = dependency('libstartup-notification-1.0', version: startup_req)
+ibus_dep = dependency('ibus-1.0', version: ibus_req)
+x11_dep = dependency('x11')
+schemas_dep = dependency('gsettings-desktop-schemas', version: schemas_req)
+gnome_desktop_dep = dependency('gnome-desktop-3.0', version: gnome_desktop_req)
+
+nm_deps = []
+if get_option('networkmanager')
+ nm_deps += dependency('libnm', version: nm_req)
+ nm_deps += dependency('libsecret-1', version: secret_req)
+
+ have_networkmanager = true
+else
+ have_networkmanager = false
+endif
+
+if get_option('systemd')
+ libsystemd_dep = dependency('libsystemd')
+ systemd_dep = dependency('systemd')
+ systemduserunitdir = systemd_dep.get_variable('systemduserunitdir',
+ pkgconfig_define: ['prefix', prefix])
+ have_systemd = true
+else
+ libsystemd_dep = []
+ have_systemd = false
+endif
+
+have_soup2 = get_option('soup2')
+
+if get_option('man')
+ if fs.exists('man/gnome-shell.1')
+ install_man('man/gnome-shell.1')
+ else
+ a2x = find_program('a2x')
+ subdir('man')
+ endif
+endif
+
+mutter_typelibdir = mutter_dep.get_variable('typelibdir')
+python = find_program('python3')
+gjs = find_program('gjs')
+
+cc = meson.get_compiler('c')
+
+m_dep = cc.find_library('m', required: false)
+
+cdata = configuration_data()
+cdata.set_quoted('GETTEXT_PACKAGE', meson.project_name())
+cdata.set_quoted('VERSION', meson.project_version())
+cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
+
+cdata.set('HAVE_NETWORKMANAGER', have_networkmanager)
+cdata.set('HAVE_SYSTEMD', have_systemd)
+
+# New API added in glib-2.57.2
+cdata.set('HAVE_GIO_DESKTOP_LAUNCH_URIS_WITH_FDS',
+ cc.has_function('g_desktop_app_info_launch_uris_as_manager_with_fds',
+ dependencies : gio_dep)
+)
+cdata.set('HAVE_FDWALK', cc.has_function('fdwalk'))
+cdata.set('HAVE_MALLINFO', cc.has_function('mallinfo'))
+cdata.set('HAVE_MALLINFO2', cc.has_function('mallinfo2'))
+cdata.set('HAVE_SYS_RESOURCE_H', cc.has_header('sys/resource.h'))
+cdata.set('HAVE__NL_TIME_FIRST_WEEKDAY',
+ cc.has_header_symbol('langinfo.h', '_NL_TIME_FIRST_WEEKDAY')
+)
+
+cdata.set('HAVE_FDWALK',
+ cc.has_function('fdwalk')
+)
+
+polkit_has_autocleanup = cc.compiles(
+'''
+ #define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
+ #include <polkitagent/polkitagent.h>
+ void main(void) { g_autoptr(PolkitAgentListener) agent = NULL; }
+''',
+ dependencies: polkit_dep)
+cdata.set('HAVE_POLKIT_AUTOCLEANUP', polkit_has_autocleanup)
+
+buildtype = get_option('buildtype')
+if buildtype != 'plain'
+ all_warnings = [
+ '-fno-strict-aliasing',
+ '-Wpointer-arith',
+ '-Wmissing-declarations',
+ '-Wimplicit-function-declaration',
+ '-Wformat=2',
+ '-Wformat-nonliteral',
+ '-Wformat-security',
+ '-Wstrict-prototypes',
+ '-Wmissing-prototypes',
+ '-Wnested-externs',
+ '-Wold-style-definition',
+ '-Wundef',
+ '-Wunused',
+ '-Wcast-align',
+ '-Wmissing-noreturn',
+ '-Wmissing-format-attribute',
+ '-Wmissing-include-dirs',
+ '-Wlogical-op',
+ '-Wignored-qualifiers',
+ '-Werror=redundant-decls',
+ '-Werror=implicit',
+ '-Werror=nonnull',
+ '-Werror=init-self',
+ '-Werror=main',
+ '-Werror=missing-braces',
+ '-Werror=sequence-point',
+ '-Werror=return-type',
+ '-Werror=trigraphs',
+ '-Werror=array-bounds',
+ '-Werror=write-strings',
+ '-Werror=address',
+ '-Werror=int-to-pointer-cast',
+ '-Werror=pointer-to-int-cast',
+ '-Werror=empty-body',
+ '-Werror=write-strings',
+ ]
+ supported_warnings = cc.get_supported_arguments(all_warnings)
+ add_project_arguments(supported_warnings, language: 'c')
+endif
+
+if get_option('debug')
+ debug_c_args = [
+ '-DG_ENABLE_DEBUG',
+ '-fno-omit-frame-pointer'
+ ]
+ supported_debug_c_args = cc.get_supported_arguments(debug_c_args)
+ add_project_arguments(supported_debug_c_args, language: 'c')
+endif
+
+config_h = configure_file(
+ input: 'config.h.meson',
+ output: 'config.h',
+ configuration: cdata
+)
+
+conf_inc = include_directories('.')
+
+libgvc = subproject('gvc',
+ default_options: [
+ 'package_name=' + meson.project_name(),
+ 'package_version=' + meson.project_version(),
+ 'pkgdatadir=' + pkgdatadir,
+ 'pkglibdir=' + pkglibdir,
+ 'static=false',
+ 'introspection=true',
+ 'alsa=false'
+ ]
+)
+libgvc_gir = libgvc.get_variable('libgvc_gir')
+
+libshew = subproject('shew',
+ default_options: [
+ 'package_name=@0@'.format(meson.project_name()),
+ ]
+)
+
+libshew_version = libshew.get_variable('package_version')
+assert(libshew_version == meson.project_version(),
+ 'shew version does not match project version')
+
+if get_option('extensions_tool')
+ extension_tool = subproject('extensions-tool',
+ default_options: [
+ 'package_name=@0@'.format(meson.project_name()),
+ ]
+ )
+
+ extension_tool_version = extension_tool.get_variable('package_version')
+ assert(extension_tool_version == meson.project_version(),
+ 'extension-tool version does not match project version'
+ )
+endif
+
+if get_option('extensions_app')
+ extensions_app = subproject('extensions-app',
+ default_options: [
+ 'package_name=@0@'.format(meson.project_name()),
+ ]
+ )
+
+ extensions_app_version = extensions_app.get_variable('package_version')
+ assert(extensions_app_version == meson.project_version(),
+ 'Extensions app version does not match project version'
+ )
+endif
+
+
+po_dir = join_paths(meson.current_source_dir(), 'po')
+
+subdir('js')
+subdir('src')
+subdir('po')
+subdir('data')
+subdir('tools')
+
+if get_option('tests')
+ subdir('tests')
+endif
+
+if get_option('gtk_doc')
+ subdir('docs/reference')
+endif
+
+gnome.post_install(
+ glib_compile_schemas: true,
+ gtk_update_icon_cache: true
+)
+
+meson.add_dist_script('meson/generate-manpages.py')
+meson.add_dist_script('meson/generate-stylesheets.py')
+meson.add_dist_script('meson/check-version.py', meson.project_version(), 'NEWS')
+
+summary_options = {
+ 'networkmanager': get_option('networkmanager'),
+ 'systemd': get_option('systemd'),
+ 'extensions_app': get_option('extensions_app'),
+ 'extensions_tool': get_option('extensions_tool'),
+ 'man': get_option('man'),
+ 'gtk_doc': get_option('gtk_doc'),
+}
+
+summary_build = {
+ 'buildtype': get_option('buildtype'),
+ 'debug': get_option('debug'),
+}
+
+summary_dirs = {
+ 'prefix': get_option('prefix'),
+ 'bindir': get_option('bindir'),
+ 'libdir': get_option('libdir'),
+ 'libexecdir': get_option('libexecdir'),
+ 'datadir': get_option('datadir'),
+ 'sysconfdir': get_option('sysconfdir'),
+}
+
+if get_option('man')
+ summary_dirs += { 'mandir': get_option('mandir') }
+endif
+
+summary(summary_dirs, section: 'Directories')
+summary(summary_build, section: 'Build Configuration')
+summary(summary_options, section: 'Build Options')
diff --git a/meson/check-version.py b/meson/check-version.py
new file mode 100755
index 0000000..81750c9
--- /dev/null
+++ b/meson/check-version.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+import os, sys
+from pathlib import Path
+import argparse, subprocess
+
+def check_version(version, file, type='news'):
+ if type == 'news':
+ line = file.open().readline()
+ ok = line.startswith(version)
+ print("{}: {}".format(file, "OK" if ok else "FAILED"))
+ if not ok:
+ raise Exception("{} does not start with {}".format(file, version))
+ elif type == 'metainfo':
+ subprocess.run(['appstream-util', 'validate-version', file, version],
+ check=True)
+ else:
+ raise Exception('Not implemented')
+
+parser = argparse.ArgumentParser(description='Check release version information.')
+parser.add_argument('--type', choices=['metainfo','news'], default='news')
+parser.add_argument('version', help='the version to check for')
+parser.add_argument('files', nargs='+', help='files to check')
+args = parser.parse_args()
+
+distroot = os.environ.get('MESON_DIST_ROOT', './')
+
+try:
+ for file in args.files:
+ check_version(args.version, Path(distroot, file), args.type)
+except:
+ sys.exit(1)
diff --git a/meson/generate-manpages.py b/meson/generate-manpages.py
new file mode 100644
index 0000000..e12df61
--- /dev/null
+++ b/meson/generate-manpages.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+
+import os
+from pathlib import PurePath
+import subprocess
+
+man_pages = [
+ 'man/gnome-shell.1',
+ 'subprojects/extensions-tool/man/gnome-extensions.1',
+]
+
+sourceroot = os.environ.get('MESON_SOURCE_ROOT')
+distroot = os.environ.get('MESON_DIST_ROOT')
+
+for man_page in man_pages:
+ page_path = PurePath(man_page)
+ src = PurePath(sourceroot, page_path.with_suffix('.txt'))
+ dst = PurePath(distroot, page_path)
+ stylesheet = src.with_name('stylesheet.xsl')
+
+ subprocess.call(['a2x', '--xsl-file', os.fspath(stylesheet),
+ '--format', 'manpage', '--destination-dir', os.fspath(dst.parent),
+ os.fspath(src)])
diff --git a/meson/generate-stylesheets.py b/meson/generate-stylesheets.py
new file mode 100644
index 0000000..bd6b641
--- /dev/null
+++ b/meson/generate-stylesheets.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+import os
+from pathlib import PurePath
+import subprocess
+
+stylesheets = [
+ 'data/theme/gnome-shell-high-contrast.css',
+ 'data/theme/gnome-shell.css'
+]
+
+sourceroot = os.environ.get('MESON_SOURCE_ROOT')
+distroot = os.environ.get('MESON_DIST_ROOT')
+
+for stylesheet in stylesheets:
+ stylesheet_path = PurePath(stylesheet)
+ src = PurePath(sourceroot, stylesheet_path.with_suffix('.scss'))
+ dst = PurePath(distroot, stylesheet_path)
+ subprocess.run(['sassc', '-a', src, dst], check=True)
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..46ca8e7
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,47 @@
+option('extensions_tool',
+ type: 'boolean',
+ value: true,
+ description: 'Build gnome-extensions CLI tool'
+)
+
+option('extensions_app',
+ type: 'boolean',
+ value: true,
+ description: 'Build gnome-extensions GUI application'
+)
+
+option('gtk_doc',
+ type: 'boolean',
+ value: false,
+ description: 'Build API reference'
+)
+
+option('man',
+ type: 'boolean',
+ value: true,
+ description: 'Generate man pages'
+)
+
+option('tests',
+ type: 'boolean',
+ value: true,
+ description: 'Enable tests'
+)
+
+option('networkmanager',
+ type: 'boolean',
+ value: true,
+ description: 'Enable NetworkManager support'
+)
+
+option('systemd',
+ type: 'boolean',
+ value: true,
+ description: 'Enable systemd integration'
+)
+
+option('soup2',
+ type: 'boolean',
+ value: false,
+ description: 'Use Soup 2.4 instead of Soup 3. Must be in sync with libgweather'
+)
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644
index 0000000..b3075d3
--- /dev/null
+++ b/po/LINGUAS
@@ -0,0 +1,88 @@
+ab
+af
+an
+ar
+as
+ast
+be
+bg
+bn
+bn_IN
+bs
+ca
+ca@valencia
+ckb
+cs
+da
+de
+el
+en_GB
+eo
+es
+et
+eu
+fa
+fi
+fr
+fur
+fy
+ga
+gd
+gl
+gu
+he
+hi
+hr
+hu
+ia
+id
+ie
+is
+it
+ja
+ka
+kab
+kk
+km
+kn
+ko
+ku
+ky
+lt
+lv
+mjw
+mk
+ml
+mr
+ms
+nb
+ne
+nl
+nn
+oc
+or
+os
+pa
+pl
+pt
+pt_BR
+ro
+ru
+si
+sk
+sl
+sr
+sr@latin
+sv
+ta
+te
+tg
+th
+tr
+ug
+uk
+uz@cyrillic
+vi
+zh_CN
+zh_HK
+zh_TW
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..0ba8752
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,108 @@
+# List of source files containing translatable strings.
+# Please keep this file sorted alphabetically.
+data/50-gnome-shell-launchers.xml
+data/50-gnome-shell-screenshots.xml
+data/50-gnome-shell-system.xml
+data/org.gnome.Shell.desktop.in.in
+data/org.gnome.shell.gschema.xml.in
+data/org.gnome.Shell.Extensions.desktop.in.in
+data/org.gnome.Shell.PortalHelper.desktop.in.in
+js/dbusServices/extensions/ui/extension-error-page.ui
+js/gdm/authPrompt.js
+js/gdm/loginDialog.js
+js/gdm/util.js
+js/misc/systemActions.js
+js/misc/util.js
+js/portalHelper/main.js
+js/ui/accessDialog.js
+js/ui/appDisplay.js
+js/ui/appFavorites.js
+js/ui/appMenu.js
+js/ui/audioDeviceSelection.js
+js/ui/backgroundMenu.js
+js/ui/calendar.js
+js/ui/closeDialog.js
+js/ui/components/automountManager.js
+js/ui/components/autorunManager.js
+js/ui/components/keyring.js
+js/ui/components/networkAgent.js
+js/ui/components/polkitAgent.js
+js/ui/components/telepathyClient.js
+js/ui/ctrlAltTab.js
+js/ui/dash.js
+js/ui/dateMenu.js
+js/ui/endSessionDialog.js
+js/ui/extensionDownloader.js
+js/ui/extensionSystem.js
+js/ui/inhibitShortcutsDialog.js
+js/ui/kbdA11yDialog.js
+js/ui/keyboard.js
+js/ui/lookingGlass.js
+js/ui/main.js
+js/ui/messageList.js
+js/ui/messageTray.js
+js/ui/mpris.js
+js/ui/notificationDaemon.js
+js/ui/osdWindow.js
+js/ui/overviewControls.js
+js/ui/overview.js
+js/ui/padOsd.js
+js/ui/panel.js
+js/ui/popupMenu.js
+js/ui/runDialog.js
+js/ui/screenShield.js
+js/ui/screenshot.js
+js/ui/search.js
+js/ui/searchController.js
+js/ui/shellEntry.js
+js/ui/shellMountOperation.js
+js/ui/status/accessibility.js
+js/ui/status/autoRotate.js
+js/ui/status/bluetooth.js
+js/ui/status/brightness.js
+js/ui/status/darkMode.js
+js/ui/status/dwellClick.js
+js/ui/status/keyboard.js
+js/ui/status/location.js
+js/ui/status/network.js
+js/ui/status/nightLight.js
+js/ui/status/powerProfiles.js
+js/ui/status/remoteAccess.js
+js/ui/status/rfkill.js
+js/ui/status/system.js
+js/ui/status/thunderbolt.js
+js/ui/status/volume.js
+js/ui/switchMonitor.js
+js/ui/unlockDialog.js
+js/ui/welcomeDialog.js
+js/ui/windowAttentionHandler.js
+js/ui/windowManager.js
+js/ui/windowMenu.js
+src/calendar-server/evolution-calendar.desktop.in
+src/main.c
+src/shell-app.c
+src/shell-app-system.c
+src/shell-global.c
+src/shell-keyring-prompt.c
+src/shell-polkit-authentication-agent.c
+src/shell-util.c
+subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in
+subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in
+subprojects/extensions-app/js/main.js
+subprojects/extensions-app/data/ui/extension-row.ui
+subprojects/extensions-app/data/ui/extensions-window.ui
+subprojects/extensions-tool/src/command-create.c
+subprojects/extensions-tool/src/command-disable.c
+subprojects/extensions-tool/src/command-enable.c
+subprojects/extensions-tool/src/command-info.c
+subprojects/extensions-tool/src/command-install.c
+subprojects/extensions-tool/src/command-list.c
+subprojects/extensions-tool/src/command-pack.c
+subprojects/extensions-tool/src/command-prefs.c
+subprojects/extensions-tool/src/command-reset.c
+subprojects/extensions-tool/src/command-uninstall.c
+subprojects/extensions-tool/src/main.c
+subprojects/extensions-tool/src/templates/00-plain.desktop.in
+subprojects/extensions-tool/src/templates/indicator.desktop.in
+# Please do not remove this file from POTFILES.in. Run "git submodule init && git submodule update" to get it.
+subprojects/gvc/gvc-mixer-control.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
new file mode 100644
index 0000000..dd694f7
--- /dev/null
+++ b/po/POTFILES.skip
@@ -0,0 +1,5 @@
+data/gnome-shell-overrides-migration.desktop.in
+data/org.gnome.Shell@wayland.service.in
+data/org.gnome.Shell@x11.service.in
+js/ui/init.js
+subprojects/extensions-tool/src/templates/indicator/extension.js
diff --git a/po/ab.po b/po/ab.po
new file mode 100644
index 0000000..70451e0
--- /dev/null
+++ b/po/ab.po
@@ -0,0 +1,6952 @@
+msgid ""
+msgstr ""
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-03-19 21:02+0000\n"
+"Last-Translator: Нанба Наала <naala-nanba@rambler.ru>\n"
+"Language-Team: Abkhazian <daniel.abzakh@gmail.com>\n"
+"Language: ab\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr ""
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Иалкаау аҧшьы ахь аиасра 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Иалкаау аҧшьы ахь аиасра 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Иалкаау аҧшьы ахь аиасра 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Иалкаау аҧшьы ахь аиасра 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Иалкаау аҧшьы ахь аиасра 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Иалкаау аҧшьы ахь аиасра 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Иалкаау аҧшьы ахь аиасра 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Иалкаау аҧшьы ахь аиасра 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Иалкаау аҧшьы ахь аиасра 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Аекран ақәҭыхра"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Аскриншот аҟаҵара"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Аскриншот аҟаҵара"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Аскриншот аҟаҵара"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr ""
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Асистема"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr ""
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr ""
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr ""
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr ""
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Аԥшьы амениу аартра "
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Аҭҵааҩцәеи аԥышәара азызуои рзы Alt-F2 аҟынтәи аҩныҵҟатәи амаругақәа рҿакра"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID иаҿаку ахкырбагақәа"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID иаҿаку ахкырбагақәа"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Аԥшьы амениу аартра"
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Аҭыӡҭыԥқәа"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Автоматикла ҭыԥс аҟазаара аилкаара"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Аҭыӡҭыԥ"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Аԥенџьырқәа аекран акьыԥшь ахь реиҭагараан автоматикла аԥенџьыр ашәагаа "
+"аҽаԥсахра аҿакра."
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Аҩныҟатәи адаҟьа"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr ""
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Аԥыхры"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Ажәамаӡа"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr ""
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr ""
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr ""
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Ахархәаҩ ихьӡ"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr ""
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr ""
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr ""
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr ""
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr ""
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr ""
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr ""
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr ""
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr ""
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr ""
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr ""
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr ""
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr ""
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr ""
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr ""
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr ""
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr ""
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr ""
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr ""
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr ""
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr ""
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Уажәыцәҟьа"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Иацы"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Иацы, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Иацы, %-l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %-l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Анеира аҭыԥ аҟны ахаҭара шьақәыргылара"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr ""
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr ""
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr ""
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr ""
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr ""
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr ""
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Аҧенџьыр ҿыц"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Аԥкаарақәа рырбара"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Аҭыҵра"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr ""
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr ""
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr ""
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Алымҳахаҵа"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Аицраҳәа"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Амикрофон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr ""
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr ""
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Архиарақәа"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr ""
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Иаҧхьааиуа амз"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Анаҩстәи амз"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr ""
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr ""
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr ""
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr ""
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Арыцқьара"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr ""
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr ""
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr ""
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr ""
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr ""
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr ""
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ацхыраарала аартра"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Аҽаҿакра"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Ацвԥха"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Аиԥшрагәаҭара"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Амаҵуратә"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Ахаҭареилкаара Wired 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Ахаҭареилкаара DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1960
+msgid "Network Manager"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN Ажәамаӡа"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr ""
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Анаԥхгаҩы"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr ""
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr ""
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr ""
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Windows"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr ""
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr ""
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %-d %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr ""
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr ""
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Иахьа"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Уаҵәы"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr ""
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr ""
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr ""
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Адунеитә сааҭкәа"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Аҭагалара..."
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr ""
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr ""
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Амш зеиԥшроу"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr ""
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr ""
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr ""
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Ашьақәыргылара"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr ""
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr ""
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Азин амҭара"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Азин аҭара"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Иаҿакны ааныжьра"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Аҿакра"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Аҿакра"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Иаҿыхны ааныжьра"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Арегиони абызшәеи рырхиарақәа"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Аҭбаарақәа шьақәыргылаӡам"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s агхақәа ирызку ардырра ҟаимҵаӡеит ."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Агхақәа рҵәахра"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Агхақәа рырбара"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Иаҿакуп"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr ""
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr ""
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Иажәит"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr ""
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Акод арбара"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Веб-адаҟьа"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Асистема ишәарҭоу арежим ахь ииаган"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Аҧшьқәа шьҭа ирымоуп иҳәаақәҵам анеирҭа"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Архынҳәра"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Ахаҭара шьақәыргылоуп аԥыжәара змоу хархәаҩҵас"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Аекаран аусура аанкылара аҿыхуп"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Асистематә информациа"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Еилкаам анагӡаҩ"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Еилкаам ахьӡ"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Ааҧшаар адҵаалара аҭагалара"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Аԥшькәа"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Алаԥшхагара"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr ""
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr ""
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Аекрантә еилыркаа арбара"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Амонитор аиагара"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Арыдқәа рызкызаара"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Ихиоуп"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr ""
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Мап"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Архиаразы апытҟәыр шәақәыӷәӷәа"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Аҭаларазы шәақәыӷәӷәа Esc"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Аҭаларазы иарбанзаалак арыд шәақәыӷәӷәа"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Аҽазыҟаҵарақәа"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr ""
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Хыхьтәи апанель"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Адҵа анагӡара"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Аркразы шәақәыӷәӷәа Esc"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Wayland ахархәараан аиҭаҿакра ауам"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Аиҭаҿакра …"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME иаҭахуп аекран ааннакылар"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Аанкылара залымшеит"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Аусура аанкылара иаԥырхагахеит аԥшьы"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "_Алкаара"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Аҵакыра алхра"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr ""
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Аекран алхра"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Аԥенџьыр"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Аҧенџьыр алхра"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Аекран ақәҭыхра /Аекран анҵара"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Аҳәынаҧ ахырхага арбара"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Аекран анҵамҭақәа"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Аекран анҵара %d %t.webm аҟынтә"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Аскриншот"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Аекран анҵоуп"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Авидео ахәаҧшразы абра шәақәыӷәӷәа."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Афаилқәа рҟны арбара"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "%s аҟынтә аекран аҭыхра"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Аекран ақәҭыхра ҟаҵоуп"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Шәара ишәылшоит аҧсахра абуфер аҟынтә асахьа бжьашәыргылар."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Иаразнактәи аекран аҭыхра "
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Аҧшаара…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Акгьы ҧшааӡам."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Аҧшаара"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Ахкьыҧхьаара"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Абжьаргылара"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Атекст арбара"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Атекст аҵәахра"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps Lock аҿакуп."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Иҵәаху атом"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Асистематә том Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Ахархәара рынаҭоит афаил-ацаҧхақә"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Афаил-ацаҧхақәа зхы иазырхәо атом аушьҭразы шәхы иашәырхәа уи ацымхәрас "
+"аутилита <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Аномер PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Ажәамаӡа агәынкылара"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Аартра%s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM иамазароуп адаҟьақәа мамзар иҭацәызароуп"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Аус арура залымшеит %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Аҧшьы аҧшаара ауам %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Ахархәара аманшәалара"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Иҳараку аконтрастра"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zoom"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Аекрантә диктор"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Аекрантә рыдыркыра"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Лаҧшылатәи ардыррақәа"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Иҷабуа арыдқәа"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Имыццакуа арыдқәа"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Иадҧо арыдқәа"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Аҳәынаҧ арыдқәа"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr ""
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr ""
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Ашеишеира"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr ""
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Арыдыркыра"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr ""
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr ""
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr ""
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<еилкаам>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr ""
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1113
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Анеирҭа аҭыԥ %s аҿакуп"
+
+#: js/ui/status/network.js:1472 js/ui/status/network.js:1488
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1473
+#, fuzzy
+#| msgid "Settings"
+msgid "VPN Settings"
+msgstr "Архиарақәа"
+
+#: js/ui/status/network.js:1722
+msgid "Wi–Fi"
+msgstr ""
+
+#: js/ui/status/network.js:1724
+msgid "All Networks"
+msgstr ""
+
+#: js/ui/status/network.js:1821
+msgid "Wired Connections"
+msgstr ""
+
+#: js/ui/status/network.js:1822
+#, fuzzy
+#| msgid "Settings"
+msgid "Wired Settings"
+msgstr "Архиарақәа"
+
+#: js/ui/status/network.js:1836
+#, fuzzy
+#| msgid "Bluetooth"
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth"
+
+#: js/ui/status/network.js:1837
+#, fuzzy
+#| msgid "Bluetooth"
+msgid "Bluetooth Settings"
+msgstr "Bluetooth"
+
+#: js/ui/status/network.js:1851
+msgid "Mobile Connections"
+msgstr ""
+
+#: js/ui/status/network.js:1853
+msgid "Mobile Broadband Settings"
+msgstr ""
+
+#: js/ui/status/network.js:1965
+msgid "Connection failed"
+msgstr ""
+
+#: js/ui/status/network.js:1966
+msgid "Activation of network connection failed"
+msgstr ""
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Уахынлатәи алашара"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr ""
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr ""
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr ""
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr ""
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr ""
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr ""
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Авиарежим"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Аскриншот аҟаҵара"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr ""
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr ""
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr ""
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr ""
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr ""
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr ""
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr ""
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Амҽхак"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "%u ҭыҵрак"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "%u аҭалара"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Асаркьа"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr ""
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr ""
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr ""
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr ""
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr ""
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr ""
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Даҽа хархәаҩк ихьӡала аҭалара"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr ""
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Мап, иҭабуп"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr ""
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr ""
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr ""
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr ""
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr ""
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr ""
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Аиҭашьақәыргылара"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Аиҵыхра"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr ""
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr ""
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr ""
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr ""
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Еснагь иахьубарҭоу аусуратә ҭыԥ аҟны"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr ""
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr ""
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr ""
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr ""
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr ""
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr ""
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr ""
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr ""
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Аркра"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr ""
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr ""
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr ""
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr ""
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "«%s» аус арура залшом"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Ажәамаӡақәа еиқәшәаӡом"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr ""
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Ахаҭареилкаара ахархәаҩ ила мап ацәкын"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Арҭбаарақәа"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "The GNOME Project"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Нанба Наала <naala-nanba@rambler.ru>, 2022"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Веб-асаит"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Аилыркаа"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr ""
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Ахьӡ"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Ахцәажәара"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Ацәабла"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "АХЬӠ"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "АФАИЛ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Аверсиа"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Аҵакыра"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Ахархәара:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "АДҴА"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[АҴАҴӶӘҚӘА…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Имариоу"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr ""
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u ҭыҵрак"
+msgstr[1] "%u ҭыҵрак"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u аҭалара"
+msgstr[1] "%u аҭаларақәа"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Асистематә бжьқәа"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#, fuzzy
diff --git a/po/af.po b/po/af.po
new file mode 100644
index 0000000..6901842
--- /dev/null
+++ b/po/af.po
@@ -0,0 +1,2912 @@
+# Afrikaans translation for gnome-shell.
+# Copyright (C) 2011
+# This file is distributed under the same license as the gnome-shell package.
+# F Wolff <friedel@translate.org.za>, 2011, 2013, 2014, 2015, 2016.
+# Dawid Loubser <dawid.loubser@ibi.co.za>, 2013.
+# Pieter Schoeman <pieter@sonbesie.co.za>, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2019-01-16 00:12+0000\n"
+"PO-Revision-Date: 2018-02-10 11:12+0200\n"
+"Last-Translator: Pieter Schalk Schoeman <pieter@sonbesie.co.za>\n"
+"Language-Team: Afrikaans <pieter@sonbesie.co.za>\n"
+"Language: af\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.0.3\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Stelsel"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Wys die lys van kennisgewings"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokus op die aktiewe kennisgewing"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Wys die oorsig"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Wys alle toepassings"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Open die toepassingkieslys"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:150
+msgid "Shell Extensions"
+msgstr "Shell uitbreidings"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "Stel GNOME Shell-uitbreidings op"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Vensterbestuur en toepassinglansering"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Aktiveer interne gereedskap wat nuttig is vir ontwikkelaars en toetsers "
+"vanaf Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Laat toegang na interne ontfout- en moniteringsgereedskap toe deur die Alt-"
+"F2-dialoog te gebruik."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID's van uitbreidings om te aktiveer"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell-uitbreidings het elk 'n UUID-eienskap; hierdie sleutel lys "
+"uitbreidings wat gelaai moet word. Enige uitbreiding wat gelaai wil word "
+"moet in hierdie lys verskyn. U kan ook hierdie lys manipuleer met die "
+"EnableExtension- en DisableExtension-DBus-metodes op org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disable user extensions"
+msgstr "Skakel gebruikers uitbreidings af"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Skakel alle uitbreidings wat die gebruiker aangeskakel het af sonder om die "
+"\"aangeskakelde-uitbreiding\" instelling te verander."
+
+#: data/org.gnome.shell.gschema.xml.in:34
+msgid "Disables the validation of extension version compatibility"
+msgstr "Skakel die validasie van uitbreidings weergawe versoenbaarheid af"
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell sal slegs uitbreidings laai wat die huidig geïnstalleerde "
+"weergawe ondersteun. Deur die opsie te aktiveer sal die toets afgeskakel "
+"word en alle uitbreidings sal gelaai kan word ongeag of hulle die weergawe "
+"ondersteun of nie."
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lys van werkarealêer-ID's vir gunstelingtoepassings"
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Die toepassings wat ooreenstem met dié identifiseerders sal in die "
+"gunstelinge-area vertoon word."
+
+#: data/org.gnome.shell.gschema.xml.in:51
+msgid "App Picker View"
+msgstr "Toepassingkieser oorsig"
+
+#: data/org.gnome.shell.gschema.xml.in:52
+msgid "Index of the currently selected view in the application picker."
+msgstr "Indeks van die huidige aansig in die toepassing kieser."
+
+#: data/org.gnome.shell.gschema.xml.in:58
+msgid "History for command (Alt-F2) dialog"
+msgstr "Geskiedenis vir die opdrag-dialoogvenster (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "History for the looking glass dialog"
+msgstr "Geskiedenis vir die \"looking glass\"-dialoogvenster"
+
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Wys altyd die 'Meld af' opsie in die gebruikerkieslys."
+
+#: data/org.gnome.shell.gschema.xml.in:68
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Hierdie sleutel oorheers die outomatiese wegsteek van die 'Meld af'-"
+"kieslysitem in enkelgebruiker-, enkelsessie-situasies."
+
+#: data/org.gnome.shell.gschema.xml.in:75
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Of wagwoord onthou moet word vir die heg van geënkripteerde of afgeleë "
+"lêerstelsels"
+
+#: data/org.gnome.shell.gschema.xml.in:76
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"'n Wagwoord sal versoek word wanneer 'n geënkripteerde toestel of afgeleë "
+"lêerstelsel geheg word. As die wagwoord vir toekomstige gebruik gestoor kan "
+"word sal 'n 'Onthou wagwoord'-keuseblokkie teenwoordig wees. Hierdie sleutel "
+"stel die verstektoestand van die keuseblokkie stel."
+
+#: data/org.gnome.shell.gschema.xml.in:85
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Of die verstek Bluetooth verwerker opgestelde toestelle het wat daarmee "
+"verband hou"
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Die shell sal slegs 'n Bluetooth-kieslysitem wys as 'n Bluetooth-verwerker "
+"aangeskakel is, of as daar toestelle ingestel is wat verband hou met die "
+"verstekverwerker. Dit sal herstel word as die verstekverwerker ooit gesien "
+"word om nie toestelle wat daarmee verband hou, te hê nie."
+
+#: data/org.gnome.shell.gschema.xml.in:101
+msgid "Keybinding to open the application menu"
+msgstr "Sleutelbinding om die toepassingskieslys oop te maak"
+
+#: data/org.gnome.shell.gschema.xml.in:102
+msgid "Keybinding to open the application menu."
+msgstr "Sleutelbinding om die toepassingskieslys oop te maak."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Sleutelkortpad om die \"Vertoon toepassings\"-aansig oop te maak"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Sleutelkortpad om die \"Vertoon toepassings\"-aansig van die Aktiwiteite-"
+"oorsig oop te maak."
+
+#: data/org.gnome.shell.gschema.xml.in:116
+msgid "Keybinding to open the overview"
+msgstr "Sleutelbinding om die oorsig oop te maak"
+
+#: data/org.gnome.shell.gschema.xml.in:117
+msgid "Keybinding to open the Activities Overview."
+msgstr "Sleutelbinding om die Aktiwiteite-oorsig oop te maak."
+
+#: data/org.gnome.shell.gschema.xml.in:123
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Sleutelbinding om die sigbaarheid van die kennisgewinglys te swik"
+
+#: data/org.gnome.shell.gschema.xml.in:124
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Sleutelbinding om die sigbaarheid van die kennisgewinglys te swik."
+
+#: data/org.gnome.shell.gschema.xml.in:130
+msgid "Keybinding to focus the active notification"
+msgstr "Sleutelbinding om die aktiewe kennisgewing te fokus"
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to focus the active notification."
+msgstr "Sleutelbinding om die aktiewe kennisgewing te fokus."
+
+#: data/org.gnome.shell.gschema.xml.in:137
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"Sleutelkortpaaie wat alle uitvoerende tweens stop en weer begin vir "
+"ontvoutings doeleindes"
+
+#: data/org.gnome.shell.gschema.xml.in:146
+msgid "Which keyboard to use"
+msgstr "Watter sleutelbord om te gebruik"
+
+#: data/org.gnome.shell.gschema.xml.in:147
+msgid "The type of keyboard to use."
+msgstr "Die tipe sleutelbord om te gebruik."
+
+#: data/org.gnome.shell.gschema.xml.in:158
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Limit switcher to current workspace."
+msgstr "Beperk wisselaar tot huidige werkspasie."
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"As waar sal slegs toepassings wat vensters in die huidige werkspasie het in "
+"die wisselaar vertoon word. Anders sal alle toepassings ingesluit word."
+
+#: data/org.gnome.shell.gschema.xml.in:176
+msgid "The application icon mode."
+msgstr "Die toepassingsikoon-modus."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Stel op hoe die vensters vertoon word in die wisselaar. Geldige moontlikhede "
+"is 'slegs-duimnaelskets' (wys 'n duimnaelskets van die venster), 'slegs-toep-"
+"ikoon' (wys slegs die toepassing se ikoon), of 'altwee'."
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"As waar sal slegs vensters van die huidige werkspasie in die wisselaar "
+"vertoon word. Anders sal alle vensters ingesluit word."
+
+#: data/org.gnome.shell.gschema.xml.in:197
+msgid "Attach modal dialog to the parent window"
+msgstr "Heg modale dialoogvenster vas aan die ouervenster"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+#: data/org.gnome.shell.gschema.xml.in:207
+#: data/org.gnome.shell.gschema.xml.in:215
+#: data/org.gnome.shell.gschema.xml.in:223
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Hierdie sleutel oorskryf die sleutel in org.gnome.mutter wanneer GNOME Shell "
+"uitgevoer word."
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Aktiveer rand-tilering wanneer vensters op skermrand laat val word"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Workspaces are managed dynamically"
+msgstr "Werkspasies word dinamies bestuur"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Workspaces only on primary monitor"
+msgstr "Werkspasies slegs op primêre monitor"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Vertraag fokusverandering in muismodus totdat die wyser ophou beweeg"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Netwerkaanmelding"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:120
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr ""
+"'n Fout het voorgekom tydens die lansering van die voorkeuredialoog vir %s:"
+
+#: js/gdm/authPrompt.js:147 js/ui/audioDeviceSelection.js:71
+#: js/ui/components/networkAgent.js:117 js/ui/components/polkitAgent.js:153
+#: js/ui/endSessionDialog.js:482 js/ui/extensionDownloader.js:197
+#: js/ui/shellMountOperation.js:343 js/ui/status/network.js:921
+msgid "Cancel"
+msgstr "Kanselleer"
+
+#: js/gdm/authPrompt.js:166 js/gdm/authPrompt.js:209 js/gdm/authPrompt.js:441
+msgid "Next"
+msgstr "Volgende"
+
+#: js/gdm/authPrompt.js:205 js/ui/shellMountOperation.js:347
+#: js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "Sluit oop"
+
+#: js/gdm/authPrompt.js:207
+msgctxt "button"
+msgid "Sign In"
+msgstr "Meld aan"
+
+#: js/gdm/loginDialog.js:315
+msgid "Choose Session"
+msgstr "Kies sessie"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:458
+msgid "Not listed?"
+msgstr "Nie op die lys nie?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:887
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(bv. gebruiker of %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:892 js/ui/components/networkAgent.js:243
+#: js/ui/components/networkAgent.js:261
+msgid "Username: "
+msgstr "Gebruikernaam: "
+
+#: js/gdm/loginDialog.js:1228
+msgid "Login Window"
+msgstr "Aanmeldvenster"
+
+#: js/gdm/util.js:345
+msgid "Authentication error"
+msgstr "Verifiëringsfout"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:482
+msgid "(or swipe finger)"
+msgstr "(of veeg vinger)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:99
+#, fuzzy
+#| msgid "Power Off"
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Skakel af"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:102
+#, fuzzy
+#| msgid "power off;shutdown"
+msgid "power off;shutdown;reboot;restart"
+msgstr "skakel af;afskakel"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:106
+#, fuzzy
+#| msgctxt "search-result"
+#| msgid "Lock screen"
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Sluit skerm"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "lock screen"
+msgstr "sluit skerm"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:113
+#, fuzzy
+#| msgid "Log Out"
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Meld af"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:116
+msgid "logout;sign off"
+msgstr "meld af;teken af"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:120
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Sluimer"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:123
+msgid "suspend;sleep"
+msgstr "suspend;sleep;sluimer;slaap"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:127
+#, fuzzy
+#| msgid "Switch User"
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Wissel gebruiker"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:130
+msgid "switch user"
+msgstr "wissel gebruiker"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:134
+#, fuzzy
+#| msgctxt "search-result"
+#| msgid "Lock orientation"
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr "Sluit oriëntasie"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:137
+#, fuzzy
+#| msgid "lock orientation"
+msgid "lock orientation;screen;rotation"
+msgstr "sluit oriëntasie"
+
+#: js/misc/util.js:122
+msgid "Command not found"
+msgstr "Opdrag nie gevind nie"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:155
+msgid "Could not parse command:"
+msgstr "Kon nie opdrag ontleed nie:"
+
+#: js/misc/util.js:163
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Uitvoer van “%s” het misluk:"
+
+#: js/misc/util.js:180
+msgid "Just now"
+msgstr "Nounet"
+
+#: js/misc/util.js:182
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minuut gelede"
+msgstr[1] "%d minute gelede"
+
+#: js/misc/util.js:185
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d uur gelede"
+msgstr[1] "%d ure gelede"
+
+#: js/misc/util.js:188
+msgid "Yesterday"
+msgstr "Gister"
+
+#: js/misc/util.js:190
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dag gelede"
+msgstr[1] "%d dae gelede"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d week gelede"
+msgstr[1] "%d weke gelede"
+
+#: js/misc/util.js:196
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d maand gelede"
+msgstr[1] "%d maande gelede"
+
+#: js/misc/util.js:198
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d jaar gelede"
+msgstr[1] "%d jaar gelede"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:228
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:234
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Gister, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:240
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:246
+#, no-c-format
+msgid "%B %d, %H∶%M"
+msgstr "%e %B %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:252
+#, no-c-format
+msgid "%B %d %Y, %H∶%M"
+msgstr "%e %B %Y, %H∶%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:257
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:263
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Gister, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:269
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l:%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:275
+#, no-c-format
+msgid "%B %d, %l∶%M %p"
+msgstr "%e %B %l:%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:281
+#, no-c-format
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%e %B %Y, %l:%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:66
+msgid "Hotspot Login"
+msgstr "Kuberkolaanmelding"
+
+#: js/portalHelper/main.js:112
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Jou verbinding met die kuberkolaanmelding is nie veilig nie. Wagwoorde en "
+"ander inligting op die bladsy kan deur ander mense gesien word."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:63 js/ui/status/location.js:393
+msgid "Deny Access"
+msgstr "Wyer toegang"
+
+#: js/ui/accessDialog.js:64 js/ui/status/location.js:396
+msgid "Grant Access"
+msgstr "Verleen toegang"
+
+#: js/ui/appDisplay.js:793
+msgid "Frequently used applications will appear here"
+msgstr "Toepassings wat gereeld gebruik word sal hier verskyn"
+
+#: js/ui/appDisplay.js:914
+msgid "Frequent"
+msgstr "Gereeldes"
+
+#: js/ui/appDisplay.js:921
+msgid "All"
+msgstr "Almal"
+
+#: js/ui/appDisplay.js:1886
+msgid "New Window"
+msgstr "Nuwe venster"
+
+#: js/ui/appDisplay.js:1900
+msgid "Launch using Dedicated Graphics Card"
+msgstr "Begin met 'n Toegewyde Grafika Kaart"
+
+#: js/ui/appDisplay.js:1927 js/ui/dash.js:287
+msgid "Remove from Favorites"
+msgstr "Verwyder uit gunstelinge"
+
+#: js/ui/appDisplay.js:1933
+msgid "Add to Favorites"
+msgstr "Voeg by gunstelinge"
+
+#: js/ui/appDisplay.js:1943
+msgid "Show Details"
+msgstr "Wys besonderhede"
+
+#: js/ui/appFavorites.js:140
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s is by die gunstelinge gevoeg."
+
+#: js/ui/appFavorites.js:174
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s uit die gunstelinge verwyder."
+
+#: js/ui/audioDeviceSelection.js:59
+msgid "Select Audio Device"
+msgstr "Kies klanktoestel"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Sound Settings"
+msgstr "Klankinstellings"
+
+#: js/ui/audioDeviceSelection.js:78
+msgid "Headphones"
+msgstr "Oorfone"
+
+#: js/ui/audioDeviceSelection.js:80
+msgid "Headset"
+msgstr "Kopstuk"
+
+#: js/ui/audioDeviceSelection.js:82 js/ui/status/volume.js:221
+msgid "Microphone"
+msgstr "Mikrofoon"
+
+#: js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "Verander agtergrond…"
+
+#: js/ui/backgroundMenu.js:21 js/ui/status/nightLight.js:51
+msgid "Display Settings"
+msgstr "Vertooninstellings"
+
+#: js/ui/backgroundMenu.js:22 js/ui/status/system.js:267
+msgid "Settings"
+msgstr "Instellings"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:44
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:73
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:75
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:77
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:79
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:81
+msgctxt "grid thursday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:83
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:85
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:379
+msgid "%OB"
+msgstr ""
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:389
+msgid "%OB %Y"
+msgstr ""
+
+#: js/ui/calendar.js:446
+msgid "Previous month"
+msgstr "Vorige maand"
+
+#: js/ui/calendar.js:456
+msgid "Next month"
+msgstr "Volgende maand"
+
+#: js/ui/calendar.js:609
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:664
+msgid "Week %V"
+msgstr "Week %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:733
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Heel dag"
+
+#: js/ui/calendar.js:868
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A %e %B"
+
+#: js/ui/calendar.js:872
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A %e %B %Y"
+
+#: js/ui/calendar.js:1102
+msgid "No Notifications"
+msgstr "Geen kennisgewings"
+
+#: js/ui/calendar.js:1105
+msgid "No Events"
+msgstr "Geen gebeure"
+
+#: js/ui/calendar.js:1133
+msgid "Clear All"
+msgstr "Maak als skoon"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:44
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” reageer nie."
+
+#: js/ui/closeDialog.js:45
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Jy mag 'n wyle te wag vir die program om voort te gaan of jy kan die program "
+"forseer om af te sluit."
+
+#: js/ui/closeDialog.js:61
+msgid "Force Quit"
+msgstr "Forseer afsluit"
+
+#: js/ui/closeDialog.js:64
+msgid "Wait"
+msgstr "Wag"
+
+#: js/ui/components/automountManager.js:90
+msgid "External drive connected"
+msgstr "Eksterne skyf ingeprop"
+
+#: js/ui/components/automountManager.js:101
+msgid "External drive disconnected"
+msgstr "Eksterne skyf uitgeprop"
+
+#: js/ui/components/autorunManager.js:354
+#, javascript-format
+msgid "Open with %s"
+msgstr "Open met %s"
+
+#: js/ui/components/keyring.js:107 js/ui/components/polkitAgent.js:297
+msgid "Password:"
+msgstr "Wagwoord:"
+
+#: js/ui/components/keyring.js:140
+msgid "Type again:"
+msgstr "Tik weer:"
+
+#: js/ui/components/networkAgent.js:112 js/ui/status/network.js:245
+#: js/ui/status/network.js:336 js/ui/status/network.js:924
+msgid "Connect"
+msgstr "Verbind"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:205 js/ui/components/networkAgent.js:217
+#: js/ui/components/networkAgent.js:245 js/ui/components/networkAgent.js:265
+#: js/ui/components/networkAgent.js:275
+msgid "Password: "
+msgstr "Wagwoord: "
+
+#. static WEP
+#: js/ui/components/networkAgent.js:210
+msgid "Key: "
+msgstr "Sleutel: "
+
+#: js/ui/components/networkAgent.js:249
+msgid "Identity: "
+msgstr "Identiteit: "
+
+#: js/ui/components/networkAgent.js:251
+msgid "Private key password: "
+msgstr "Private sleutel se wagwoord: "
+
+#: js/ui/components/networkAgent.js:263
+msgid "Service: "
+msgstr "Diens: "
+
+#: js/ui/components/networkAgent.js:292 js/ui/components/networkAgent.js:659
+msgid "Authentication required by wireless network"
+msgstr "Verifiëring vereis deur draadlose netwerk"
+
+#: js/ui/components/networkAgent.js:293 js/ui/components/networkAgent.js:660
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Wagwoorde of enkripsiesleutels is nodig om toegang te verkry tot die "
+"draadlose netwerk “%s”."
+
+#: js/ui/components/networkAgent.js:297 js/ui/components/networkAgent.js:663
+msgid "Wired 802.1X authentication"
+msgstr "Verifiëring vir 802.1X met kabel"
+
+#: js/ui/components/networkAgent.js:299
+msgid "Network name: "
+msgstr "Netwerknaam: "
+
+#: js/ui/components/networkAgent.js:304 js/ui/components/networkAgent.js:667
+msgid "DSL authentication"
+msgstr "DSL-verifiëring"
+
+#: js/ui/components/networkAgent.js:311 js/ui/components/networkAgent.js:673
+msgid "PIN code required"
+msgstr "PIN-kode nodig"
+
+#: js/ui/components/networkAgent.js:312 js/ui/components/networkAgent.js:674
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN-kode is nodig vir die selfoonbreëbandtoestel"
+
+#: js/ui/components/networkAgent.js:313
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:320 js/ui/components/networkAgent.js:680
+msgid "Mobile broadband network password"
+msgstr "Selfoonbreëband se netwerkwagwoord"
+
+#: js/ui/components/networkAgent.js:321 js/ui/components/networkAgent.js:664
+#: js/ui/components/networkAgent.js:668 js/ui/components/networkAgent.js:681
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "'n Wagwoord is nodig om te verbind aan “%s”."
+
+#: js/ui/components/networkAgent.js:648 js/ui/status/network.js:1693
+msgid "Network Manager"
+msgstr "Netwerkbestuurder"
+
+#: js/ui/components/polkitAgent.js:48
+msgid "Authentication Required"
+msgstr "Verifiëringsversoek"
+
+#: js/ui/components/polkitAgent.js:76
+msgid "Administrator"
+msgstr "Administrateur"
+
+#: js/ui/components/polkitAgent.js:156
+msgid "Authenticate"
+msgstr "Verifieer"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:283 js/ui/shellMountOperation.js:327
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Jammer, dit het nie gewerk nie. Probeer gerus weer."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:795
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s staan nou bekend as %s"
+
+#: js/ui/ctrlAltTab.js:29 js/ui/viewSelector.js:186
+msgid "Windows"
+msgstr "Vensters"
+
+#: js/ui/dash.js:248 js/ui/dash.js:289
+msgid "Show Applications"
+msgstr "Wys toepassings"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:445
+msgid "Dash"
+msgstr "Toeps"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: js/ui/dateMenu.js:74
+msgid "%B %e %Y"
+msgstr "%e %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:81
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#: js/ui/dateMenu.js:145
+msgid "Add world clocks…"
+msgstr "Voeg wêreldhorlosies by…"
+
+#: js/ui/dateMenu.js:146
+msgid "World Clocks"
+msgstr "Wêreldhorlosies"
+
+#: js/ui/dateMenu.js:227
+msgid "Weather"
+msgstr "Weer"
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:291
+#, javascript-format
+msgid "%s all day."
+msgstr "%s heeldag."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:297
+#, javascript-format
+msgid "%s, then %s later."
+msgstr "%s, dan %s later."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:303
+#, javascript-format
+msgid "%s, then %s, followed by %s later."
+msgstr "%s, dan %s, en later %s."
+
+#: js/ui/dateMenu.js:314
+msgid "Select a location…"
+msgstr "Kies 'n ligging…"
+
+#: js/ui/dateMenu.js:317
+msgid "Loading…"
+msgstr "Laai tans…"
+
+#. Translators: %s is a temperature with unit, e.g. "23℃"
+#: js/ui/dateMenu.js:323
+#, javascript-format
+msgid "Feels like %s."
+msgstr "Voel soos %s."
+
+#: js/ui/dateMenu.js:326
+msgid "Go online for weather information"
+msgstr "Gaan aanlyn vir weer inligting"
+
+#: js/ui/dateMenu.js:328
+msgid "Weather information is currently unavailable"
+msgstr "Weer inligting is nie tans beskikbaar nie"
+
+#: js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Meld af as %s"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "Meld af"
+
+#: js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s sal outomaties afgemeld word oor %d sekonde."
+msgstr[1] "%s sal outomaties afgemeld word oor %d sekondes."
+
+#: js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "U sal outomaties afgemeld word oor %d sekonde."
+msgstr[1] "U sal outomaties afgemeld word oor %d sekondes."
+
+#: js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Meld af"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "Skakel af"
+
+#: js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installeer bywerkings en skakel af"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Die stelsel sal outomaties afgeskakel word oor %d sekonde."
+msgstr[1] "Die stelsel sal outomaties afgeskakel word oor %d sekondes."
+
+#: js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installeer wagtende sagtewarebywerkings"
+
+#: js/ui/endSessionDialog.js:94 js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "Herbegin"
+
+#: js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "Skakel af"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "Herbegin"
+
+#: js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Die stelsel sal outomaties herbegin oor %d sekonde."
+msgstr[1] "Die stelsel sal outomaties herbegin oor %d sekondes."
+
+#: js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Herbegin en installeer bywerkings"
+
+#: js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Die stelsel sal outomaties herbegin, en bywerkings sal geïnstalleer word oor "
+"%d sekonde."
+msgstr[1] ""
+"Die stelsel sal outomaties herbegin, en bywerkings sal geïnstalleer word oor "
+"%d sekondes."
+
+#: js/ui/endSessionDialog.js:127 js/ui/endSessionDialog.js:147
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Herbegin en installeer"
+
+#: js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installeer en skakel af"
+
+#: js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Skakel af nadat bywerkings geïnstalleer is"
+
+#: js/ui/endSessionDialog.js:137
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Herbegin en installeer bywerkings"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:142
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s sal na die rekenaar herbegin het geïnstalleer word. Die installasie "
+"van opgraderings kan lank neem: maak seker dat 'n rugsteen gemaak is en dat "
+"die rekenaar ingeprop is."
+
+#: js/ui/endSessionDialog.js:361
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "Tans op batterykrag: prop in voordat bywerkings geïnstalleer word."
+
+#: js/ui/endSessionDialog.js:378
+msgid "Some applications are busy or have unsaved work."
+msgstr "Sommige toepassings is besig of het ongestoorde werk."
+
+#: js/ui/endSessionDialog.js:385
+msgid "Other users are logged in."
+msgstr "Ander gebruikers is aangemeld."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:666
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (afgeleë)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:669
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsole)"
+
+#: js/ui/extensionDownloader.js:201
+msgid "Install"
+msgstr "Installeer"
+
+#: js/ui/extensionDownloader.js:206
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Wil u “%s” vanaf extensions.gnome.org aflaai en installeer?"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:59
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr "%s wil kortpaaie onderdruk"
+
+#: js/ui/inhibitShortcutsDialog.js:60
+msgid "Application wants to inhibit shortcuts"
+msgstr "Toepassing wil kortpaaie onderdruk"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:69
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Jy kan kortpaaie herstel deur %s te druk."
+
+#: js/ui/inhibitShortcutsDialog.js:74
+msgid "Deny"
+msgstr "Weier"
+
+#: js/ui/inhibitShortcutsDialog.js:80
+msgid "Allow"
+msgstr "Laat toe"
+
+#: js/ui/kbdA11yDialog.js:33
+#, fuzzy
+#| msgid "Slow Keys"
+msgid "Slow Keys Turned On"
+msgstr "Stadige sleutels"
+
+#: js/ui/kbdA11yDialog.js:34
+#, fuzzy
+#| msgid "Slow Keys"
+msgid "Slow Keys Turned Off"
+msgstr "Stadige sleutels"
+
+#: js/ui/kbdA11yDialog.js:35
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:42
+#, fuzzy
+#| msgid "Sticky Keys"
+msgid "Sticky Keys Turned On"
+msgstr "Kleefsleutels"
+
+#: js/ui/kbdA11yDialog.js:43
+#, fuzzy
+#| msgid "Sticky Keys"
+msgid "Sticky Keys Turned Off"
+msgstr "Kleefsleutels"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:47
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:59
+msgid "Leave On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:59 js/ui/status/bluetooth.js:143
+#: js/ui/status/network.js:1283
+msgid "Turn On"
+msgstr "Skakel aan"
+
+#: js/ui/kbdA11yDialog.js:67 js/ui/status/bluetooth.js:143
+#: js/ui/status/network.js:154 js/ui/status/network.js:337
+#: js/ui/status/network.js:1283 js/ui/status/network.js:1398
+#: js/ui/status/nightLight.js:47 js/ui/status/rfkill.js:90
+#: js/ui/status/rfkill.js:117
+msgid "Turn Off"
+msgstr "Skakel af"
+
+#: js/ui/kbdA11yDialog.js:67
+msgid "Leave Off"
+msgstr ""
+
+#: js/ui/keyboard.js:198
+#, fuzzy
+#| msgid "Region and Language Settings"
+msgid "Region & Language Settings"
+msgstr "Instellings vir streek en taal"
+
+#: js/ui/lookingGlass.js:644
+msgid "No extensions installed"
+msgstr "Geen uitbreidings geïnstalleer nie"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:698
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s het nie enige foute uitgevoer nie."
+
+#: js/ui/lookingGlass.js:704
+msgid "Hide Errors"
+msgstr "Versteek foute"
+
+#: js/ui/lookingGlass.js:708 js/ui/lookingGlass.js:768
+msgid "Show Errors"
+msgstr "Wys foute"
+
+#: js/ui/lookingGlass.js:717
+msgid "Enabled"
+msgstr "Geaktiveer"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:720 subprojects/gvc/gvc-mixer-control.c:1866
+msgid "Disabled"
+msgstr "Gedeaktiveer"
+
+#: js/ui/lookingGlass.js:722
+msgid "Error"
+msgstr "Fout"
+
+#: js/ui/lookingGlass.js:724
+msgid "Out of date"
+msgstr "Verouderd"
+
+#: js/ui/lookingGlass.js:726
+msgid "Downloading"
+msgstr "Laai tans af"
+
+#: js/ui/lookingGlass.js:750
+msgid "View Source"
+msgstr "Vertoon bronkode"
+
+#: js/ui/lookingGlass.js:759
+msgid "Web Page"
+msgstr "Webblad"
+
+#: js/ui/messageTray.js:1495
+msgid "System Information"
+msgstr "Stelselinligting"
+
+#: js/ui/mpris.js:211
+msgid "Unknown artist"
+msgstr "Onbekende kunstenaar"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Onbekende titel"
+
+#: js/ui/overview.js:83
+msgid "Undo"
+msgstr "Ontdoen"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:112
+msgid "Overview"
+msgstr "Oorsig"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:239
+msgid "Type to search…"
+msgstr "Tik om te soek…"
+
+#: js/ui/padOsd.js:105
+msgid "New shortcut…"
+msgstr "Nuwe kortpad…"
+
+#: js/ui/padOsd.js:156
+msgid "Application defined"
+msgstr "Toepassings gedefinieer"
+
+#: js/ui/padOsd.js:157
+msgid "Show on-screen help"
+msgstr "Wys hulp op die skerm"
+
+#: js/ui/padOsd.js:158
+msgid "Switch monitor"
+msgstr "Wissel skerms"
+
+#: js/ui/padOsd.js:159
+msgid "Assign keystroke"
+msgstr "Ken steutel toe"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Klaar"
+
+#: js/ui/padOsd.js:740
+msgid "Edit…"
+msgstr "Redigeer…"
+
+#: js/ui/padOsd.js:782 js/ui/padOsd.js:887
+msgid "None"
+msgstr "Geen"
+
+#: js/ui/padOsd.js:841
+msgid "Press a button to configure"
+msgstr "Druk 'n knoppie om in te stel"
+
+#: js/ui/padOsd.js:842
+msgid "Press Esc to exit"
+msgstr "Druk Esc om uit te gaan"
+
+#: js/ui/padOsd.js:845
+msgid "Press any key to exit"
+msgstr "Druk enige knoppie om uit te gaan"
+
+#: js/ui/panel.js:355
+msgid "Quit"
+msgstr "Sluit af"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:411
+msgid "Activities"
+msgstr "Aktiwiteite"
+
+#: js/ui/panel.js:692
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Stelsel"
+
+#: js/ui/panel.js:812
+msgid "Top Bar"
+msgstr "Boonste balk"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:300
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "Tik 'n opdrag"
+
+#: js/ui/runDialog.js:110 js/ui/windowMenu.js:174
+msgid "Close"
+msgstr "Sluit"
+
+#: js/ui/runDialog.js:271
+msgid "Restart is not available on Wayland"
+msgstr "Herbegin is nie op Wayland beskikbaar nie"
+
+#: js/ui/runDialog.js:276
+msgid "Restarting…"
+msgstr "Herbegin tans…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A %e %B"
+
+#: js/ui/screenShield.js:146
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d nuwe boodskap"
+msgstr[1] "%d nuwe boodskappe"
+
+#: js/ui/screenShield.js:148
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d nuwe kennisgewing"
+msgstr[1] "%d nuwe kennisgewings"
+
+#: js/ui/screenShield.js:451 js/ui/status/system.js:286
+msgid "Lock"
+msgstr "Sluit"
+
+#: js/ui/screenShield.js:715
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME moet die skerm sluit"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:836 js/ui/screenShield.js:1309
+msgid "Unable to lock"
+msgstr "Kan nie sluit nie"
+
+#: js/ui/screenShield.js:837 js/ui/screenShield.js:1310
+msgid "Lock was blocked by an application"
+msgstr "Sluit is gekeer deur 'n program"
+
+#: js/ui/search.js:660
+msgid "Searching…"
+msgstr "Soek tans…"
+
+#: js/ui/search.js:662
+msgid "No results."
+msgstr "Geen resultate nie."
+
+#: js/ui/search.js:786
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d meer"
+msgstr[1] "%d meer"
+
+#: js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "Kopieer"
+
+#: js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "Plak"
+
+#: js/ui/shellEntry.js:96
+msgid "Show Text"
+msgstr "Vertoon teks"
+
+#: js/ui/shellEntry.js:98
+msgid "Hide Text"
+msgstr "Versteek teks"
+
+#: js/ui/shellMountOperation.js:314
+msgid "Password"
+msgstr "Wagwoord"
+
+#: js/ui/shellMountOperation.js:335
+msgid "Remember Password"
+msgstr "Onthou wagwoord"
+
+#: js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "Toeganklikheid"
+
+#: js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "Zoem"
+
+#: js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "Skermleser"
+
+#: js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "Skermsleutelbord"
+
+#: js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "Visuele waarskuwings"
+
+#: js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "Kleefsleutels"
+
+#: js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "Stadige sleutels"
+
+#: js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "Bonssleutels"
+
+#: js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "Muissleutels"
+
+#: js/ui/status/accessibility.js:165
+msgid "High Contrast"
+msgstr "Hoë kontras"
+
+#: js/ui/status/accessibility.js:200
+msgid "Large Text"
+msgstr "Groot teks"
+
+#: js/ui/status/bluetooth.js:48
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:57 js/ui/status/network.js:616
+msgid "Bluetooth Settings"
+msgstr "Bluetooth-instellings"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:137
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d gekoppel"
+msgstr[1] "%d gekoppel"
+
+#: js/ui/status/bluetooth.js:139
+msgid "Off"
+msgstr "Af"
+
+#: js/ui/status/bluetooth.js:141
+msgid "On"
+msgstr "Aan"
+
+#: js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "Helderheid"
+
+#: js/ui/status/keyboard.js:795
+msgid "Keyboard"
+msgstr "Sleutelbord"
+
+#: js/ui/status/keyboard.js:818
+msgid "Show Keyboard Layout"
+msgstr "Wys sleutelborduitleg"
+
+#: js/ui/status/location.js:89 js/ui/status/location.js:197
+msgid "Location Enabled"
+msgstr "Ligging geaktiveer"
+
+#: js/ui/status/location.js:90 js/ui/status/location.js:198
+msgid "Disable"
+msgstr "Deaktiveer"
+
+#: js/ui/status/location.js:91
+msgid "Privacy Settings"
+msgstr "Instellings vir privaatheid"
+
+#: js/ui/status/location.js:196
+msgid "Location In Use"
+msgstr "Ligging in gebruik"
+
+#: js/ui/status/location.js:200
+msgid "Location Disabled"
+msgstr "Ligging gedeaktiveer"
+
+#: js/ui/status/location.js:201
+msgid "Enable"
+msgstr "Aktiveer"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:386
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "Gee %s toegang tot u ligging?"
+
+#: js/ui/status/location.js:387
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Toegang tot jou ligging kan enige tyd in privaatheid instellings verander "
+"word."
+
+#: js/ui/status/network.js:90
+msgid "<unknown>"
+msgstr "<onbekend>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:443 js/ui/status/network.js:1312
+#, javascript-format
+msgid "%s Off"
+msgstr "%s Af"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:446
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s Gekoppel"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:451
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s Nie bestuur nie"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s Ontkoppel tans"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:461 js/ui/status/network.js:1304
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s Koppel tans"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:464
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s Benodig identifisering"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:472
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Fermware ontbreek vir %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:476
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s nie beskikbaar nie"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:479
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s-verbinding het misluk"
+
+#: js/ui/status/network.js:495
+msgid "Wired Settings"
+msgstr "Kabelinstellings"
+
+#: js/ui/status/network.js:537
+msgid "Mobile Broadband Settings"
+msgstr "Selfoonbreëband-instellings"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:580 js/ui/status/network.js:1309
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s deur hardeware gedeaktiveer"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:584
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s gedeaktiveer"
+
+#: js/ui/status/network.js:624
+msgid "Connect to Internet"
+msgstr "Koppel aan die internet"
+
+#: js/ui/status/network.js:818
+msgid "Airplane Mode is On"
+msgstr "Vliegtuigmodus is aan"
+
+#: js/ui/status/network.js:819
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi word gedeaktiveer tydens vliegtuigmodus."
+
+#: js/ui/status/network.js:820
+msgid "Turn Off Airplane Mode"
+msgstr "Skakel vlugmodus af"
+
+#: js/ui/status/network.js:829
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi is af"
+
+#: js/ui/status/network.js:830
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Wi-Fi moet aangeskakel wees om aan 'n netwerk te koppel."
+
+#: js/ui/status/network.js:831
+msgid "Turn On Wi-Fi"
+msgstr "Skakel Wi-Fi aan"
+
+#: js/ui/status/network.js:856
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi-netwerke"
+
+#: js/ui/status/network.js:858
+msgid "Select a network"
+msgstr "Kies 'n netwerk"
+
+#: js/ui/status/network.js:888
+msgid "No Networks"
+msgstr "Geen netwerke"
+
+#: js/ui/status/network.js:909 js/ui/status/rfkill.js:115
+msgid "Use hardware switch to turn off"
+msgstr "Gebruik die hardewareskakelaar om mee af te skakel"
+
+#: js/ui/status/network.js:1175
+msgid "Select Network"
+msgstr "Kies netwerk"
+
+#: js/ui/status/network.js:1181
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi-instellings"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1300
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s Kuberkol Aktief"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1315
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s nie gekoppel nie"
+
+#: js/ui/status/network.js:1415
+msgid "connecting…"
+msgstr "verbind tans…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1418
+msgid "authentication required"
+msgstr "verifiëring benodig"
+
+#: js/ui/status/network.js:1420
+msgid "connection failed"
+msgstr "verbinding het misluk"
+
+#: js/ui/status/network.js:1474
+msgid "VPN Settings"
+msgstr "VPN-instellings"
+
+#: js/ui/status/network.js:1487
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1497
+msgid "VPN Off"
+msgstr "VPN af"
+
+#: js/ui/status/network.js:1561 js/ui/status/rfkill.js:93
+msgid "Network Settings"
+msgstr "Netwerkinstellings"
+
+#: js/ui/status/network.js:1590
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s Gekabelde verbinding"
+msgstr[1] "%s Gekabelde verbindings"
+
+#: js/ui/status/network.js:1594
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s Wi-Fi verbinding"
+msgstr[1] "%s Wi-Fi verbindings"
+
+#: js/ui/status/network.js:1598
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s Modem verbinding"
+msgstr[1] "%s Modem verbindings"
+
+#: js/ui/status/network.js:1730
+msgid "Connection failed"
+msgstr "Verbinding het misluk"
+
+#: js/ui/status/network.js:1731
+msgid "Activation of network connection failed"
+msgstr "Aktivering van netwerkverbinding het misluk"
+
+#: js/ui/status/nightLight.js:68
+msgid "Night Light Disabled"
+msgstr "Naglig af"
+
+#: js/ui/status/nightLight.js:69
+msgid "Night Light On"
+msgstr "Naglig aan"
+
+#: js/ui/status/nightLight.js:70
+msgid "Resume"
+msgstr "Hervat"
+
+#: js/ui/status/nightLight.js:71
+msgid "Disable Until Tomorrow"
+msgstr "Skakel af tot more"
+
+#: js/ui/status/power.js:61
+msgid "Power Settings"
+msgstr "Kraginstellings"
+
+#: js/ui/status/power.js:77
+msgid "Fully Charged"
+msgstr "Volgelaai"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:84 js/ui/status/power.js:90
+msgid "Estimating…"
+msgstr "Skat tans…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:98
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d oor (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:103
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d tot vol (%d%%)"
+
+#: js/ui/status/power.js:131 js/ui/status/power.js:133
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:88
+msgid "Airplane Mode On"
+msgstr "Vlugmodus is aan"
+
+#: js/ui/status/system.js:230
+msgid "Switch User"
+msgstr "Wissel gebruiker"
+
+#: js/ui/status/system.js:242
+msgid "Log Out"
+msgstr "Meld af"
+
+#: js/ui/status/system.js:254
+msgid "Account Settings"
+msgstr "Rekeningopstelling"
+
+#: js/ui/status/system.js:271
+msgid "Orientation Lock"
+msgstr "Oriëntasieslot"
+
+#: js/ui/status/system.js:297
+msgid "Suspend"
+msgstr "Sluimer"
+
+#: js/ui/status/system.js:307
+msgid "Power Off"
+msgstr "Skakel af"
+
+#: js/ui/status/thunderbolt.js:294
+msgid "Thunderbolt"
+msgstr ""
+
+#. we are done
+#: js/ui/status/thunderbolt.js:350
+msgid "Unknown Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:351
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:356
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:357
+#, javascript-format
+msgid "Could not authorize the thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:128
+msgid "Volume changed"
+msgstr "Volume het verander"
+
+#: js/ui/status/volume.js:170
+msgid "Volume"
+msgstr "Volume"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:21
+msgid "Mirror"
+msgstr "Spieëlbeeld"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:26
+msgid "Join Displays"
+msgstr "Koppel skerms"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:31
+msgid "External Only"
+msgstr "Slegs ekstern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:36
+msgid "Built-in Only"
+msgstr "Slegs ingebou"
+
+#: js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "Meld aan as 'n ander gebruiker"
+
+#: js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "Onsluit venster"
+
+#: js/ui/viewSelector.js:190
+msgid "Applications"
+msgstr "Toepassings"
+
+#: js/ui/viewSelector.js:194
+msgid "Search"
+msgstr "Soek"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” is gereed"
+
+#: js/ui/windowManager.js:72
+msgid "Do you want to keep these display settings?"
+msgstr "Wil u hierdie skerminstellings behou?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "Stel instellings terug"
+
+#: js/ui/windowManager.js:87
+msgid "Keep Changes"
+msgstr "Behou wysigings"
+
+#: js/ui/windowManager.js:105
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Wysigings gaan teruggestel word oor %d sekonde"
+msgstr[1] "Wysigings gaan teruggestel word oor %d sekondes"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:660
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "Minimeer"
+
+#: js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "Onmaksimeer"
+
+#: js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "Maksimeer"
+
+#: js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "Skuif"
+
+#: js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "Verander grootte"
+
+#: js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "Skuif titelbalk na skerm"
+
+#: js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "Altyd bo-op"
+
+#: js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "Altyd op sigbare werkspasie"
+
+#: js/ui/windowMenu.js:103
+msgid "Move to Workspace Left"
+msgstr "Skuif na werkspasie links"
+
+#: js/ui/windowMenu.js:109
+msgid "Move to Workspace Right"
+msgstr "Skuif na werkspasie regs"
+
+#: js/ui/windowMenu.js:115
+msgid "Move to Workspace Up"
+msgstr "Skuif na hoër werkspasie"
+
+#: js/ui/windowMenu.js:121
+msgid "Move to Workspace Down"
+msgstr "Skuif na laer werkspasie"
+
+#: js/ui/windowMenu.js:139
+msgid "Move to Monitor Up"
+msgstr "Skuif na hoër monitor"
+
+#: js/ui/windowMenu.js:148
+msgid "Move to Monitor Down"
+msgstr "Skuif na laer monitor"
+
+#: js/ui/windowMenu.js:157
+msgid "Move to Monitor Left"
+msgstr "Skuif na monitor links"
+
+#: js/ui/windowMenu.js:166
+msgid "Move to Monitor Right"
+msgstr "Skuif na monitor regs"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution-kalender"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: src/main.c:432
+msgid "Print version"
+msgstr "Druk weergawe"
+
+#: src/main.c:438
+msgid "Mode used by GDM for login screen"
+msgstr "Modus deur GDM gebruik vir aanmeldskerm"
+
+#: src/main.c:444
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Gebruik 'n spesifieke modus, bv. \"gdm\" vir aanmeldskerm"
+
+#: src/main.c:450
+msgid "List possible modes"
+msgstr "Lys van moontlike modusse"
+
+#: src/shell-app.c:270
+msgctxt "program"
+msgid "Unknown"
+msgstr "Onbekend"
+
+#: src/shell-app.c:511
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Kon nie “%s” lanseer nie"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "Wagwoorde stem nie ooreen nie."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "Wagwoord kan nie leeg wees nie"
+
+#: src/shell-polkit-authentication-agent.c:353
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Verifiëringsdialoog is gesluit deur gebruiker"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1873
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u Afvoer"
+msgstr[1] "%u Afvoere"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1883
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u Toevoer"
+msgstr[1] "%u Toevoere"
+
+#: subprojects/gvc/gvc-mixer-control.c:2738
+msgid "System Sounds"
+msgstr "Stelselklanke"
+
+#~ msgctxt "search-result"
+#~ msgid "Power off"
+#~ msgstr "Skakel af"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Meld af"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Wissel gebruiker"
+
+#~ msgid "GNOME Shell Extension Preferences"
+#~ msgstr "Voorkeure vir GNOME Shell-uitbreidings"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Wys die weekdatum in die kalender"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Indien waar, vertoon die ISO-weekdatum in die kalender."
+
+#, fuzzy
+#~| msgid "Authentication Required"
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Verifiëringsversoek"
+
+#~ msgid "Events"
+#~ msgstr "Gebeure"
+
+#~ msgid "Notifications"
+#~ msgstr "Kennisgewings"
+
+#~| msgid "Hide Text"
+#~ msgid "Hide tray"
+#~ msgstr "Versteek laai"
+
+#~ msgid "Status Icons"
+#~ msgstr "Statusikone"
+
+#~ msgid "Media"
+#~ msgstr "Media"
+
+#~| msgid "In Use"
+#~ msgid "Not In Use"
+#~ msgstr "Nie in gebruik nie"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s versoek toegang tot u ligging."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Gebruik as internetverbinding"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "Invitation"
+#~ msgstr "Uitnodiging"
+
+#~ msgid "Call"
+#~ msgstr "Oproep"
+
+#~ msgid "File Transfer"
+#~ msgstr "Lêeroordrag"
+
+#~ msgid "Chat"
+#~ msgstr "Gesels"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Uitnodiging na %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s nooi u uit om by %s aan te sluit"
+
+#~ msgid "Decline"
+#~ msgstr "Nee, dankie"
+
+#~ msgid "Accept"
+#~ msgstr "Aanvaar"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Video-oproep van %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Oproep van %s"
+
+#~ msgid "Answer"
+#~ msgstr "Antwoord"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s stuur %s aan u"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s vra toestemming om te sien wanneer u aanlyn is"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Verifiëring het misluk"
+
+#~ msgid "Encryption error"
+#~ msgstr "Enkripsiefout"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Sertifikaat nie voorsien nie"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Sertifikaat nie vertrou nie"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Sertifikaat het verval"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Sertifikaat nie geaktiveer nie"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Sertifikaat se rekenaarnaam stem nie ooreen nie"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Sertifikaat se vingerafdruk stem nie ooreen nie"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Sertifikaat is self-onderteken"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Status is nou vanlyn"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Sertifikaat is ongeldig"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Verbinding is geweier"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Verbinding kan nie gemaak word nie"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Verbinding is verloor"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Die rekening is reeds verbind aan die bediener"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "Verbinding is vervang met 'n nuwe verbinding wat die selfde hulpbron "
+#~ "gebruik"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Die rekening bestaan reeds op die bediener"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Die bediener is tans te besig om die verbinding te hanteer"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Die sertifikaat is opgehef"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Die sertifikaat gebruik 'n onveilige syferalgoritme of is kriptografies "
+#~ "swak"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Die lengte van die bedienersertifikaat, of die diepte van die "
+#~ "bedienersertifikaat-ketting, het die limiete van die kriptografie-"
+#~ "biblioteek oorskry"
+
+#~ msgid "Internal error"
+#~ msgstr "Interne fout"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "Kan nie verbind aan %s nie"
+
+#~ msgid "View account"
+#~ msgstr "Bekyk rekening"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d gekoppelde toestel"
+#~ msgstr[1] "%d gekoppelde toestelle"
+
+#~ msgid "Authentication required"
+#~ msgstr "Verifiëring benodig"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Battery"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Vliegtuigmodus"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Wys die boodskaplaai"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "So"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "M"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Di"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "W"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Do"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "V"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Sa"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Niks geskeduleer nie"
+
+#~ msgid "Today"
+#~ msgstr "Vandag"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Môre"
+
+#~ msgid "This week"
+#~ msgstr "Dié week"
+
+#~ msgid "Next week"
+#~ msgstr "Volgende week"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Verwyderbare toestelle"
+
+#~ msgid "Eject"
+#~ msgstr "Skiet uit"
+
+#~ msgid "Unmute"
+#~ msgstr "Hoor weer"
+
+#~ msgid "Mute"
+#~ msgstr "Maak stil"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Maak kalender oop"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Instellings vir datum en tyd"
+
+#~ msgid "Open"
+#~ msgstr "Open"
+
+#~ msgid "Remove"
+#~ msgstr "Verwyder"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Maak boodskappe skoon"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Kennisgewingopstellings"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Laai-kieslys"
+
+#~ msgid "No Messages"
+#~ msgstr "Geen boodskappe"
+
+#~ msgid "Message Tray"
+#~ msgstr "Boodskaplaai"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Lys van kategorieë wat as gidse vertoon moet word."
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Elke kategorienaam in hierdie lys sal as gids in die toepassingsaansig "
+#~ "voorgestel word, in plaas daarvan om inlyn in die hoofaansig vertoon te "
+#~ "word."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Rangskikking van die knoppies op die titelbalk"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Hierdie sleutel oorskryf die sleutel in org.gnome.desktop.wm.preferences "
+#~ "wanneer GNOME Shell uitgevoer word."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Kies 'n uitbreiding om op te stel uit die kombinasiekas hierbo."
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%e</b> <b>%B</b> <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%e</b> <b>%B</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Magtigingversoek van %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Die toestel %s wil met hierdie rekenaar oppaar"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Toestel %s wil toegang hê tot die '%s'-diens"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Verleen slegs nou toegang"
+
+#~ msgid "Reject"
+#~ msgstr "Weier"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Bevestig oppaar vir %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Bevestig asseblief of die wagkode '%06d' dieselfde is as die een op die "
+#~ "toestel."
+
+#~ msgid "Matches"
+#~ msgstr "Dit pas"
+
+#~ msgid "Does not match"
+#~ msgstr "Dit pas nie"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Versoek om op te paar vanaf %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Tik die PIN wat op die toestel gewys word."
+
+#~ msgid "OK"
+#~ msgstr "Regso"
+
+#~ msgid "unavailable"
+#~ msgstr "onbeskikbaar"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Jammer, geen pêrels om voor die swyne te gooi vandag nie:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "Die Orakel sê: %s"
+
+#~ msgid "Screenshots"
+#~ msgstr "Skermkiekies"
+
+#~ msgid "Extension"
+#~ msgstr "Uitbreiding"
+
+#~ msgid "Session"
+#~ msgstr "Sessie"
+
+#~ msgid "Power"
+#~ msgstr "Krag"
+
+#~ msgid "Restart"
+#~ msgstr "Herbegin"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Klik 'Meld af' om dié toepassings af te sluit en af te meld van die "
+#~ "stelsel."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "U meld nou van die stelsel af."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Klik 'Skakel af' om dié toepassings af te sluit en die stelsel af te "
+#~ "skakel."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Die stelsel skakel nou af."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Klik 'Herbegin' om dié toepassings af te sluit en die stelsel weer te "
+#~ "begin."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Die stelsel word nou herbegin."
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Volume, netwerk, battery"
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Deur af te skakel, kan hulle ongestoorde werk verloor."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Wys die datum in die horlosie"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Wys die tyd met sekondes"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Meld aan"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "TOEPASSINGS"
+
+#~ msgid "SETTINGS"
+#~ msgstr "INSTELLINGS"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Available"
+#~ msgstr "Beskikbaar"
+
+#~ msgid "Away"
+#~ msgstr "Weg"
+
+#~ msgid "Busy"
+#~ msgstr "Besig"
+
+#~ msgid "CONTACTS"
+#~ msgstr "KONTAKTE"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %e %b %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e %b %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e %b %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "ONLANGSE ITEMS"
+
+#~ msgid "Show password"
+#~ msgstr "Wys wagwoord"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Kon nie '%s' ontheg nie"
+
+#~ msgid "Retry"
+#~ msgstr "Herprobeer"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "PLEKKE EN TOESTELLE"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Verkeerde wagwoord. Probeer gerus weer."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Instellings vir universele toegang"
+
+#~ msgid "Set up a New Device..."
+#~ msgstr "Stel 'n nuwe toestel op..."
+
+#~ msgid "Send Files..."
+#~ msgstr "Stuur lêers..."
+
+#~ msgid "Browse Files..."
+#~ msgstr "Blaai deur lêers..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "Fout met blaai deur toestel"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr ""
+#~ "Die aangevraagde toestel kan nie deurgeblaai word nie. Die fout is '%s'."
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Sleutelbordinstellings"
+
+#~ msgid "disabled"
+#~ msgstr "gedeaktiveer"
+
+#~ msgid "cable unplugged"
+#~ msgstr "kabel uitgeprop"
+
+#~ msgid "More..."
+#~ msgstr "Meer..."
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Outo-Ethernet"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Outo-breëband"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Outo-inbel"
+
+#~ msgid "Auto %s"
+#~ msgstr "Outo-%s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Outo-Bluetooth"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Outo-draadloos"
+
+#~ msgid "Wired"
+#~ msgstr "Draad"
+
+#~ msgid "Wireless"
+#~ msgstr "Draadloos"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN-verbindings"
+
+#~ msgid "Connectivity lost"
+#~ msgstr "Verbinding verloor"
+
+#~ msgid "You're no longer connected to the network"
+#~ msgstr "U is nie meer aan die netwerk verbind nie"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d uur bly oor"
+#~ msgstr[1] "%d ure bly oor"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s en %d %s bly oor"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "uur"
+#~ msgstr[1] "ure"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minuut"
+#~ msgstr[1] "minute"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d minuut bly oor"
+#~ msgstr[1] "%d minute bly oor"
+
+#~ msgid "AC adapter"
+#~ msgstr "Muurkrag"
+
+#~ msgid "Laptop battery"
+#~ msgstr "Skootrekenaarbattery"
+
+#~ msgid "Monitor"
+#~ msgstr "Monitor"
+
+#~ msgid "Mouse"
+#~ msgstr "Muis"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Media player"
+#~ msgstr "Mediaspeler"
+
+#~ msgid "Tablet"
+#~ msgstr "Tablet"
+
+#~ msgid "Computer"
+#~ msgstr "Rekenaar"
+
+#~ msgid "%s is online."
+#~ msgstr "%s is aanlyn."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s is vanlyn."
+
+#~ msgid "%s is away."
+#~ msgstr "%s is weg."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s is besig."
+
+#, fuzzy
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Gestuur om <b>%X</b> op <b>%A</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Gestuur op <b>%A</b> <b>%e %B</b> %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Verbinding na %s het misluk"
+
+#~ msgid "Reconnect"
+#~ msgstr "Verbind weer"
+
+#~ msgid "Hidden"
+#~ msgstr "Versteek"
+
+#~ msgid "Idle"
+#~ msgstr "Ledig"
+
+#~ msgid "Power Off..."
+#~ msgstr "Skakel af..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "Aanlynrekeninge"
+
+#~ msgid "System Settings"
+#~ msgstr "Stelselinstellings"
+
+#~ msgid "Log Out..."
+#~ msgstr "Meld af..."
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s het sy aanvang geneem"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Verenigde Koninkryk"
+
+#~ msgid "Default"
+#~ msgstr "Verstek"
+
+#~ msgid "Home Folder"
+#~ msgstr "Tuisgids"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Localization Settings"
+#~ msgstr "Lokaliseringsinstellings"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "U is nou verbind aan die verbinding vir selfoonbreëband '%s'"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "U is nou verbind aan die VPN-netwerk '%s'"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "Minder as 'n minuut gelede"
+
+#~ msgid "Shut Down"
+#~ msgstr "Skakel af"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr ""
+#~ "Klik 'Skakel af' om dié toepassings af te sluit en die stelsel af te "
+#~ "skakel."
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "Die stelsel sal outomaties afgeskakel word oor %d sekondes."
+
+#~ msgid "Shutting down the system."
+#~ msgstr "Die stelsel word nou afgeskakel."
+
+#~ msgid "Confirm"
+#~ msgstr "Bevestig"
diff --git a/po/an.po b/po/an.po
new file mode 100644
index 0000000..4bc0d70
--- /dev/null
+++ b/po/an.po
@@ -0,0 +1,2315 @@
+# Aragonese translation for gnome-shell.
+# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# FULL NAME <EMAIL@ADDRESS>, 2011.
+# Daniel Martinez <entaltoaragon@gmail.com>, 2011, 2014, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2015-07-23 08:08+0000\n"
+"PO-Revision-Date: 2015-04-28 08:40+0200\n"
+"Last-Translator: Daniel <entaltoaragon@gmail.com>\n"
+"Language-Team: Aragonés <softaragones@googlegroups.com>\n"
+"Language: an\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 2.5.1.1\n"
+"X-POOTLE-MTIME: 1430206804.000000\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "Sistema"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the notification list"
+msgstr "Amostrar a lista de notificacions"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "Dar o foco a la notificación activa"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "Amostrar l'anvista cheneral"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "Amostrar todas as aplicacions"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "Ubrir o menú de l'aplicación"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "Chestión de finestras y inicio d'aplicacions"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "Preferencias d'as extensions de GNOME Shell"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configurar as extensions de GNOME Shell"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME Shell (composición wayland)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Activar as ferramientas internas, utils ta desembolicadors y probadores, "
+"dende Alt+F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permitir accedir a las ferramientas internas de depuración monitorización "
+"usando o dialogo Alt+F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "UUID d'as extensions que activar"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"As extensions de GNOME Shell tienen una propiedat UUID; ista clau lista as "
+"extensions que s'han de cargar. Qualsiquier extensión que se quiera cargar "
+"debe estar en ista lista. Tamién puetz manipular ista lista con os metodos "
+"de DBus «EnableExtension» y «DisableExtension» en org.gnome.Shell."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "Desactiva a validación d'a compatibilidat d'a versión d'a extensión"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell solament cargará as extensions que indiquen que suportan a "
+"versión actual en execución. Activar ista opción desactivará ista "
+"comprebación y intentará cargar todas as extensions, independientment d'as "
+"versions que indiquen que suportan."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lista d'ID de fichers d'escritorio ta las aplicacions favoritas"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"As aplicacions correspondients con ixos identificadors s'amostrarán en "
+"l'aria de favoritos."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "Anvista de selección d'aplicación"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "Indiz de l'anvista seleccionada actual en o selector d'aplicacion."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historico d'o dialogo de comandos (Alt+F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://live.gnome.org/GnomeShell/LookingGlass
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "History for the looking glass dialog"
+msgstr "Historico d'o dialogo de \"looking glass\""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr ""
+"Amostrar siempre l'elemento de menú \"Trancar sesión\" en o menú de "
+"l'usuario."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ista clau sobrescribe a ocultación automatica de l'elemento de menú "
+"\"Trancar sesión\" en situacions d'un solo usuario u d'una sola sesión."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Indica si se debe recordar a clau ta amontar sistemas de fichers remotos u "
+"zifraus"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"A shell solicitará una clau quan s'amonte un dispositivo zifrau u un sistema "
+"de fichers remoto. Si a clau se puede alzar ta usar-la en un futuro, "
+"s'amostrará a caixeta \"Remerar clau\". Ista clau estableix a valor "
+"predeterminada d'a caixeta."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "Show the week date in the calendar"
+msgstr "Amostrar a calendata d'a semana en o calendario"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "If true, display the ISO week date in the calendar."
+msgstr "Si ye cierta, amuestra a calendata de semana ISO en o calandario."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu"
+msgstr "Asociación de teclas ta ubrir o menú de l'aplicación"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the application menu."
+msgstr "Asociación de teclas ta ubrir o menú de l'aplicación."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "Asociación de teclas ta la vista \"Amostrar aplicacions\""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"Asociación de teclas ta ubrir la vista \"Amostrar aplicacions\" de la vista "
+"d'actividatz."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the overview"
+msgstr "Asociación de teclas ta l'anvista cheneral"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to open the Activities Overview."
+msgstr "Asociación de teclas ta ubrir l'anvista d'actividatz"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Asociación de teclas ta cambiar a visibilidat d'a lista de notificacions"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Asociación de teclas ta cambiar a visibilidat d'a lista de notificacions."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification"
+msgstr "Asociación de teclas ta dar o foco a la notificación activa"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid "Keybinding to focus the active notification."
+msgstr "Asociación de teclas ta dar o foco a la notificación activa."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"Asociación de teclas que pausan y continan todas as execucions de «tweens», "
+"ta propositos de depuraci'on"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "Which keyboard to use"
+msgstr "Que teclau usar"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "The type of keyboard to use."
+msgstr "O tipo de teclau que usar."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid "Limit switcher to current workspace."
+msgstr "Selector de limite ta l'aria de treballo actual."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Si en ye verdadero, nomás s'amostrarán en o selector aplicacions que tiengan "
+"finestras en l'aria de treballo actual. D'atra man, s'incluirán todas as "
+"aplicacions."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid "The application icon mode."
+msgstr "O modo d'icono de l'aplicación."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only' "
+"(shows only the application icon) or 'both'."
+msgstr ""
+"Configura cómo s'amuestran as finestras en o selector. Os valore posibles "
+"son \"thumbnail-only\" (amuestra una miniatura d'a finestra), \"app-icon-"
+"only\" (solament amuestra l'icono de l'aplicación) u \"both\"."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Si en ye verdadero, nomás s'amostrarán en o selector finestras de l'aria de "
+"treballo actual. D'atra man, s'incluirán todas as finestras."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid "Attach modal dialog to the parent window"
+msgstr "Acoplar un dialogo modal a la finestra pai"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ista clau sobrescribe a clau en org.gnome.mutter en executar GNOME Shell."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Activar o mosaico en os bordes en arrocegar finestras a os bordes d'a "
+"finestra"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces are managed dynamically"
+msgstr "As arias de treballo se chestionan dinámicamente"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Workspaces only on primary monitor"
+msgstr "Arias de treballo solament en a pantalla prencipal"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Retardo en cambiar o foco d'o churi dica que o puntero deixa de mover-se"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Network Login"
+msgstr "Encetar sesión en o rete"
+
+#: ../js/extensionPrefs/main.js:122
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "I habió una error en lanzar o diálogo de preferencias ta %s:"
+
+#: ../js/extensionPrefs/main.js:154
+msgid "GNOME Shell Extensions"
+msgstr "Extensions de GNOME Shell"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:145
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:916
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:215
+msgid "Next"
+msgstr "Siguient"
+
+#: ../js/gdm/authPrompt.js:211 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "Desbloquiar"
+
+#: ../js/gdm/authPrompt.js:213
+msgctxt "button"
+msgid "Sign In"
+msgstr "Encetar sesión"
+
+#: ../js/gdm/loginDialog.js:281
+msgid "Choose Session"
+msgstr "Esleyir a sesión"
+
+#: ../js/gdm/loginDialog.js:431
+msgid "Not listed?"
+msgstr "No ye en a lista?"
+
+#: ../js/gdm/loginDialog.js:847
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(eix., usuario u %s)"
+
+#: ../js/gdm/loginDialog.js:852 ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:289
+msgid "Username: "
+msgstr "Nombre d'usuario: "
+
+#: ../js/gdm/loginDialog.js:1180
+msgid "Login Window"
+msgstr "Finestra d'inicio de sesión"
+
+#: ../js/gdm/util.js:341
+msgid "Authentication error"
+msgstr "Error d'autenticación"
+
+#: ../js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr "(u pase o dido)"
+
+#: ../js/misc/util.js:119
+msgid "Command not found"
+msgstr "No s'ha trobau lo comando"
+
+#: ../js/misc/util.js:152
+msgid "Could not parse command:"
+msgstr "No s'ha puesto analizar o comando:"
+
+#: ../js/misc/util.js:160
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Ha fallau a execucion de \"%s\":"
+
+#. Translators: Time in 24h format */
+#: ../js/misc/util.js:191
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/misc/util.js:197
+msgid "Yesterday, %H∶%M"
+msgstr "Ahiere, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/misc/util.js:203
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/misc/util.js:209
+msgid "%B %d, %H∶%M"
+msgstr "%d de %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/misc/util.js:215
+msgid "%B %d %Y, %H∶%M"
+msgstr "%d de %B de %Y, %H∶%M"
+
+#. Translators: Time in 12h format */
+#: ../js/misc/util.js:220
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/misc/util.js:226
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ahiere a las %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/misc/util.js:232
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/misc/util.js:238
+msgid "%B %d, %l∶%M %p"
+msgstr "%d de %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/misc/util.js:244
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%d de %B de %Y, %l∶%M %"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login
+#. * window, until we know the title of the actual login page */
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "Rendrecera ta autentiación web"
+
+#: ../js/ui/appDisplay.js:789
+msgid "Frequently used applications will appear here"
+msgstr "As aplicaciones usadas freqüentment amaneixerán aquí"
+
+#: ../js/ui/appDisplay.js:909
+msgid "Frequent"
+msgstr "Freqüent"
+
+#: ../js/ui/appDisplay.js:916
+msgid "All"
+msgstr "Todas"
+
+#: ../js/ui/appDisplay.js:1845
+msgid "New Window"
+msgstr "Finestra nueva"
+
+#: ../js/ui/appDisplay.js:1873 ../js/ui/dash.js:289
+msgid "Remove from Favorites"
+msgstr "Sacar d'os favoritos"
+
+#: ../js/ui/appDisplay.js:1879
+msgid "Add to Favorites"
+msgstr "Anyadir a os favoritos"
+
+#: ../js/ui/appDisplay.js:1889
+msgid "Show Details"
+msgstr "Amostrar detalles"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "S'ha anyadiu %s a os suyos favoritos."
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "S'ha sacau %s d'os suyos favoritos"
+
+#: ../js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "Cambiar o fondo…"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Display Settings"
+msgstr "Preferencias de pantalla"
+
+#: ../js/ui/backgroundMenu.js:22 ../js/ui/panel.js:650
+#: ../js/ui/status/system.js:357
+msgid "Settings"
+msgstr "Configuración"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday). */
+#: ../js/ui/calendar.js:53
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:82
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:84
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:86
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:88
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "X"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:90
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ch"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:92
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:94
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:564
+msgid "Previous month"
+msgstr "Mes anterior"
+
+#: ../js/ui/calendar.js:574
+msgid "Next month"
+msgstr "Mes siguient"
+
+#: ../js/ui/calendar.js:781
+msgid "Week %V"
+msgstr "Semana %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:1187
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Tot o diya"
+
+#: ../js/ui/calendar.js:1289
+msgid "Clear section"
+msgstr "Limpiar a sección"
+
+#: ../js/ui/calendar.js:1516
+msgid "Events"
+msgstr "Eventos"
+
+#: ../js/ui/calendar.js:1525
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %d de %B"
+
+#: ../js/ui/calendar.js:1529
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %d de %B de %Y"
+
+#: ../js/ui/calendar.js:1614
+msgid "Notifications"
+msgstr "Notificacions"
+
+#: ../js/ui/calendar.js:1765
+msgid "No Notifications"
+msgstr "No i hai notificacions"
+
+#: ../js/ui/calendar.js:1768
+msgid "No Events"
+msgstr "No i hai eventos"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "Dispositivo externo enchegau"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "Dispositivo externo desenchegau"
+
+#: ../js/ui/components/autorunManager.js:354
+#, javascript-format
+msgid "Open with %s"
+msgstr "Ubrir con %s"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "Clau de paso:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "Escriba atra vegada:"
+
+#: ../js/ui/components/networkAgent.js:140 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:919
+msgid "Connect"
+msgstr "Connectar"
+
+#: ../js/ui/components/networkAgent.js:233
+#: ../js/ui/components/networkAgent.js:245
+#: ../js/ui/components/networkAgent.js:273
+#: ../js/ui/components/networkAgent.js:293
+#: ../js/ui/components/networkAgent.js:303
+msgid "Password: "
+msgstr "Clau de paso: "
+
+#: ../js/ui/components/networkAgent.js:238
+msgid "Key: "
+msgstr "Clau: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Identity: "
+msgstr "Identidat: "
+
+#: ../js/ui/components/networkAgent.js:279
+msgid "Private key password: "
+msgstr "Clau d'a clau privada: "
+
+#: ../js/ui/components/networkAgent.js:291
+msgid "Service: "
+msgstr "Servicio: "
+
+#: ../js/ui/components/networkAgent.js:320
+#: ../js/ui/components/networkAgent.js:658
+msgid "Authentication required by wireless network"
+msgstr "O ret sin cordón requiere autenticación"
+
+#: ../js/ui/components/networkAgent.js:321
+#: ../js/ui/components/networkAgent.js:659
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network “"
+"%s”."
+msgstr ""
+"S'amenesten claus u claus de zifrau ta accedir a o ret inalambrico\"%s\"."
+
+#: ../js/ui/components/networkAgent.js:325
+#: ../js/ui/components/networkAgent.js:662
+msgid "Wired 802.1X authentication"
+msgstr "Autenticación 802.1X cableada"
+
+#: ../js/ui/components/networkAgent.js:327
+msgid "Network name: "
+msgstr "Nombre d'o ret: "
+
+#: ../js/ui/components/networkAgent.js:332
+#: ../js/ui/components/networkAgent.js:666
+msgid "DSL authentication"
+msgstr "Autenticación DSL"
+
+#: ../js/ui/components/networkAgent.js:339
+#: ../js/ui/components/networkAgent.js:672
+msgid "PIN code required"
+msgstr "Codigo PIN requeriu"
+
+#: ../js/ui/components/networkAgent.js:340
+#: ../js/ui/components/networkAgent.js:673
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "S'ameneste un codigo PIN ta o dispositivo de banda ampla mobil"
+
+#: ../js/ui/components/networkAgent.js:341
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:348
+#: ../js/ui/components/networkAgent.js:679
+msgid "Mobile broadband network password"
+msgstr "Clau d'o ret de banda ampla mobil"
+
+#: ../js/ui/components/networkAgent.js:349
+#: ../js/ui/components/networkAgent.js:663
+#: ../js/ui/components/networkAgent.js:667
+#: ../js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Se requier una clau ta connectar-se a \"%s\"."
+
+#: ../js/ui/components/networkAgent.js:647 ../js/ui/status/network.js:1657
+msgid "Network Manager"
+msgstr "Chestor d'o rete"
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "S'ameneste autenticación"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "Administrador"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "Autenticar"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Prebe atra vegada."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:757
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Agora %s se dice %s"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:155
+msgid "Windows"
+msgstr "Finestras"
+
+#: ../js/ui/dash.js:250 ../js/ui/dash.js:291
+msgid "Show Applications"
+msgstr "Amostrar aplicacions"
+
+#: ../js/ui/dash.js:449
+msgid "Dash"
+msgstr "Tablero"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:73
+msgid "%B %e %Y"
+msgstr "%B %e %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#. */
+#: ../js/ui/dateMenu.js:80
+msgid "%A %B %e %Y"
+msgstr "%A %e de %B de %Y"
+
+#: ../js/ui/dateMenu.js:160
+msgid "Add world clocks…"
+msgstr "Adhibir reloches d'o mundo…"
+
+#: ../js/ui/dateMenu.js:161
+msgid "World Clocks"
+msgstr "Reloches d'o mundo"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Trancar a sesión %s"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "Trancar a sesión"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "se trancará automaticament a sesión de %s en %d segundo."
+msgstr[1] "se trancará automaticament a sesión de %s en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "A suya sesión se trancará automaticament en %d segundo."
+msgstr[1] "A suya sesión se trancará automaticament en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Trancar sesion"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "Amortar"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalar actualizacions y amortar"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "O sistema se reiniciara automaticament en %d segundos."
+msgstr[1] "O sistema se reiniciara automaticament en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalar as actualizacions de software pendients"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "Amortar"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "O sistema se reiniciara automaticament en %d segundo."
+msgstr[1] "O sistema se reiniciara automaticament en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reenchegar y instalar as actualizacions"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"O sistema se reenchegará y instalará as actualizacions automaticament en %d "
+"segundo."
+msgstr[1] ""
+"O sistema se reenchegará y instalará as actualizacions automaticament en %d "
+"segundos."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reenchegar y instalar"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalar y amortar"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Amortar dimpués d'instalar as actualizacions"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"Funcionando con batería: se connecte antes d'instalar as actualizacions."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "Bellas aplicacions son ocupadas u tienen treballo sin alzar."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "I hai atros usuarios con a sesión encetada"
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remoto)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consola)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "Instalar"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Descargar y instalar \"%s\" dende extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:747 ../js/ui/status/keyboard.js:713
+msgid "Keyboard"
+msgstr "Teclau"
+
+#. translators: 'Hide' is a verb */
+#: ../js/ui/legacyTray.js:66
+msgid "Hide tray"
+msgstr "Amagar servilla"
+
+#: ../js/ui/legacyTray.js:107
+msgid "Status Icons"
+msgstr "Iconos d'estau"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "No bi ha extensions instaladas"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s no ha chenerau garra error."
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "Amagar errors"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "Amostrar errors"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "Activau"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "Desactivau"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "Error"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "Caducau"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "Descargando"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "Veyer fuent"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "Pachina web"
+
+#: ../js/ui/messageTray.js:1486
+msgid "System Information"
+msgstr "Informacion d'o sistema"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "Desfer"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "Vista cheneral"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "Tecleyar ta mirar…"
+
+#: ../js/ui/panel.js:352
+msgid "Quit"
+msgstr "Salir"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:404
+msgid "Activities"
+msgstr "Actividatz"
+
+#: ../js/ui/panel.js:755
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#: ../js/ui/popupMenu.js:289
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "Introducir un comando"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:162
+msgid "Close"
+msgstr "Trancar"
+
+#: ../js/ui/runDialog.js:281
+msgid "Restarting…"
+msgstr "Reenchegando…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:85
+msgid "%A, %B %d"
+msgstr "%A, %d de %B"
+
+#: ../js/ui/screenShield.js:144
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d mensache nuevo"
+msgstr[1] "%d mensaches nuevos"
+
+#: ../js/ui/screenShield.js:146
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d notificación nueva"
+msgstr[1] "%d notificacions nuevas"
+
+#: ../js/ui/screenShield.js:432 ../js/ui/status/system.js:365
+msgid "Lock"
+msgstr "Blocar"
+
+#: ../js/ui/screenShield.js:684
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME ameneste blocar a pantalla"
+
+#: ../js/ui/screenShield.js:805 ../js/ui/screenShield.js:1271
+msgid "Unable to lock"
+msgstr "No se podió blocar"
+
+#: ../js/ui/screenShield.js:806 ../js/ui/screenShield.js:1272
+msgid "Lock was blocked by an application"
+msgstr "Una aplicación impidió o bloqueyo"
+
+#: ../js/ui/search.js:617
+msgid "Searching…"
+msgstr "Mirando…"
+
+#: ../js/ui/search.js:619
+msgid "No results."
+msgstr "No se troboron resultaus."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "Copiar"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "Apegar"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "Amostrar texto"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "Amagar texto"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "Clau de paso:"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "Remerar clau de paso:"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "Accesibilidat"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "Ampliación"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "Lector de pantalla"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "Teclau en pantalla"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "Alertas visuals"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "Teclas persistents"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "Teclas lentas"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "Refuso de teclas"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "Teclas d'o ratet"
+
+#: ../js/ui/status/accessibility.js:167
+msgid "High Contrast"
+msgstr "Contraste alto"
+
+#: ../js/ui/status/accessibility.js:202
+msgid "Large Text"
+msgstr "Texto gran"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1282
+#: ../js/ui/status/network.js:1393 ../js/ui/status/rfkill.js:91
+#: ../js/ui/status/rfkill.js:118
+msgid "Turn Off"
+msgstr "Desenchegar"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "Cofiguracion de Bluetooth"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d dispositivo connectau"
+msgstr[1] "%d dispositivos connectaus"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1310
+msgid "Not Connected"
+msgstr "No connectau"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "Brilo"
+
+#: ../js/ui/status/keyboard.js:736
+msgid "Show Keyboard Layout"
+msgstr "Amostrar a distribución d'o teclau"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "Ubicación"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "Desactivar"
+
+#: ../js/ui/status/location.js:73
+msgid "Privacy Settings"
+msgstr "Configuracion de privacidat"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "En uso"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "Activar"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<desconoxiu>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1308
+#: ../js/ui/status/network.js:1512
+msgid "Off"
+msgstr "Desenchegau"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "Connectau"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "no chestionada"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "desconnectando"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1302
+msgid "Connecting"
+msgstr "Connectando"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "S'amenista autenticación"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "Manca o \"firmware\""
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "No disponible"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1696
+msgid "Connection failed"
+msgstr "Falló a connexión"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "Opcions inalambricas"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "Opcions de Banda ampla mobil"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1306
+msgid "Hardware Disabled"
+msgstr "Hardware desactivau"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "Fer servir como una connexión a internet"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "O modo avión ye enchegau"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "O Wi-Fi ye desactivau quan o modo avión ye enchegau."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "Desenchegar o modo avión"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "O Wi-Fi ye desenchegau"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "O Wi-Fi ameniste estar enchegau ta connectar-se a un ret."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Enchegar o Wi-Fi"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "Retz inalambricos"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "Selecciona un ret"
+
+#: ../js/ui/status/network.js:883
+msgid "No Networks"
+msgstr "No bi ha retz"
+
+#: ../js/ui/status/network.js:904 ../js/ui/status/rfkill.js:116
+msgid "Use hardware switch to turn off"
+msgstr "Fer servir o selector de hardware ta desenchegar-lo"
+
+#: ../js/ui/status/network.js:1174
+msgid "Select Network"
+msgstr "Selecciona o ret"
+
+#: ../js/ui/status/network.js:1180
+msgid "Wi-Fi Settings"
+msgstr "Opcions d'o Wi-Fi"
+
+#: ../js/ui/status/network.js:1282
+msgid "Turn On"
+msgstr "Enchegar"
+
+#: ../js/ui/status/network.js:1299
+msgid "Hotspot Active"
+msgstr "Punto d'acceso activo"
+
+#: ../js/ui/status/network.js:1410
+msgid "connecting..."
+msgstr "conectando…"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1413
+msgid "authentication required"
+msgstr "s'amenista autenticacion"
+
+#: ../js/ui/status/network.js:1415
+msgid "connection failed"
+msgstr "falló a connexión"
+
+#: ../js/ui/status/network.js:1481 ../js/ui/status/rfkill.js:94
+msgid "Network Settings"
+msgstr "Configuracion d'o rete"
+
+#: ../js/ui/status/network.js:1483
+msgid "VPN Settings"
+msgstr "Configuración de VPN"
+
+#: ../js/ui/status/network.js:1502
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1697
+msgid "Activation of network connection failed"
+msgstr "Falló l'activación d'a connexión de ret"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "Configuracion d'enerchia"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "Cargada de raso"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "Estimando…"
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "En queda %d∶%02d (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d ta la carga plena (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "SAI"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "Batería"
+
+#: ../js/ui/status/rfkill.js:88
+msgid "Airplane Mode"
+msgstr "Modo avión"
+
+#: ../js/ui/status/rfkill.js:90
+msgid "On"
+msgstr "Enchegau"
+
+#: ../js/ui/status/system.js:337
+msgid "Switch User"
+msgstr "Cambear d'usuario"
+
+#: ../js/ui/status/system.js:342
+msgid "Log Out"
+msgstr "Trancar sesion"
+
+#: ../js/ui/status/system.js:361
+msgid "Orientation Lock"
+msgstr "Bloqueyo d'Orientación"
+
+#: ../js/ui/status/system.js:369
+msgid "Suspend"
+msgstr "Suspender"
+
+#: ../js/ui/status/system.js:372
+msgid "Power Off"
+msgstr "Amortar"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "Volumen modificau"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "Volumen"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "Microfono"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "Encetar sesión como unatro usuario"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "Desbloquiar finestra"
+
+#: ../js/ui/viewSelector.js:159
+msgid "Applications"
+msgstr "Aplicacions"
+
+#: ../js/ui/viewSelector.js:163
+msgid "Search"
+msgstr "Mirar"
+
+#: ../js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "\"%s\" ye parau"
+
+#: ../js/ui/windowManager.js:63
+msgid "Do you want to keep these display settings?"
+msgstr "Quiers mantener istas opcions de pantalla?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:82
+msgid "Revert Settings"
+msgstr "Revertir as opcions"
+
+#: ../js/ui/windowManager.js:86
+msgid "Keep Changes"
+msgstr "Mantener os cambeos"
+
+#: ../js/ui/windowManager.js:105
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Os cambeos d'as opcions serán revertius en %d segundo"
+msgstr[1] "Os cambeos d'as opcions serán revertius en %d segundos"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height. */
+#: ../js/ui/windowManager.js:660
+#, javascript-format
+msgid "%d x %d"
+msgstr "%d x %d"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "Minimizar"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "Restaurar"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "Maximizar"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "Mover"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "Mover abarra de titol adentro d'a pantalla"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "Siempre veyible"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "Siempre en l'aria de treballo veyible"
+
+#: ../js/ui/windowMenu.js:105
+#, fuzzy
+#| msgid "Move to Workspace Up"
+msgid "Move to Workspace Left"
+msgstr "Mover a l'aria de treballo d'a cucha"
+
+#: ../js/ui/windowMenu.js:110
+#, fuzzy
+#| msgid "Move to Workspace Up"
+msgid "Move to Workspace Right"
+msgstr "Mover a l'aria de treballo d'a dreita"
+
+#: ../js/ui/windowMenu.js:115
+msgid "Move to Workspace Up"
+msgstr "Mover ta l'aria de treballo d'alto"
+
+#: ../js/ui/windowMenu.js:120
+msgid "Move to Workspace Down"
+msgstr "Mover ta l'aria de treballo d'abaixo"
+
+#: ../js/ui/windowMenu.js:136
+#, fuzzy
+#| msgid "Move to Workspace Up"
+msgid "Move to Monitor Up"
+msgstr "Mover a la pantalla d'alto"
+
+#: ../js/ui/windowMenu.js:142
+#, fuzzy
+#| msgid "Move to Workspace Down"
+msgid "Move to Monitor Down"
+msgstr "Mover a la pantalla d'abaixo"
+
+#: ../js/ui/windowMenu.js:148
+#, fuzzy
+msgid "Move to Monitor Left"
+msgstr "Mover a la pantalla d'a cucha"
+
+#: ../js/ui/windowMenu.js:154
+#, fuzzy
+msgid "Move to Monitor Right"
+msgstr "Mover a la pantalla d'a dreita"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Calandario d'Evolution"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u surtida"
+msgstr[1] "%u surtidas"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u dentrada"
+msgstr[1] "%u dentradas"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "Sonius d'o sistema"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "Imprentar version"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "Modo usau por GDM ta la pantalla d'inicio"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr ""
+"Usar un modo especifico, por eixemplo, \"gdm\" ta la pantalla d'inicio de "
+"sesión"
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "Listar os modos posibles"
+
+#: ../src/shell-app.c:239
+msgctxt "program"
+msgid "Unknown"
+msgstr "Desconoxiu"
+
+#: ../src/shell-app.c:480
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Ha fallau en aventar \"%s\""
+
+#: ../src/shell-keyring-prompt.c:742
+msgid "Passwords do not match."
+msgstr "As claus de paso no coinciden."
+
+#: ../src/shell-keyring-prompt.c:750
+msgid "Password cannot be blank"
+msgstr "A clau de paso no puede estar vueda"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "L'usuario refusó o dialogo d'autenticación"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Amostrar a servilla de mensaches"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Dom"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "L"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "M"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "X"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Ch"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "V"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "D"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Cosa programau"
+
+#~ msgid "Today"
+#~ msgstr "Huei"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Maitin"
+
+#~ msgid "This week"
+#~ msgstr "Ista semana"
+
+#~ msgid "Next week"
+#~ msgstr "A semana vinient"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Dispositivos extraíbles"
+
+#~ msgid "Eject"
+#~ msgstr "Fer fuera"
+
+#~ msgid "Invitation"
+#~ msgstr "Convite"
+
+#~ msgid "Call"
+#~ msgstr "Gritar"
+
+#~ msgid "File Transfer"
+#~ msgstr "Transferencia de fichers"
+
+#~ msgid "Chat"
+#~ msgstr "Chat"
+
+#~ msgid "Unmute"
+#~ msgstr "Dar voz"
+
+#~ msgid "Mute"
+#~ msgstr "Silenciar"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Invitación a %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s ye convidando-le a unir-se a %s"
+
+#~ msgid "Decline"
+#~ msgstr "Refusar"
+
+#~ msgid "Accept"
+#~ msgstr "Acceptar"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Videogritada de %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Gritada de %s"
+
+#~ msgid "Answer"
+#~ msgstr "Responder"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s ye ninviando-le %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s solicita permiso ta veyer quán ye enchegau"
+
+#~ msgid "Network error"
+#~ msgstr "Error d'o ret"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Falló l'autenticación"
+
+#~ msgid "Encryption error"
+#~ msgstr "Error de zifrau"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Certificau no proporcionau"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "No se confía en o certificau"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certificau caducau"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certificau no activau"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "O nombre d'o servidor d'o certificau no coincide"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "O sinyal d'o certificau no coincide"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificau autofirmau"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "O estau ye establiu a \"desconnectau\""
+
+#~ msgid "Encryption is not available"
+#~ msgstr "O zifrau no ye disponible"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "O certificau no ye valido"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "S'ha refusau a connexión"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "No se puede establir a connexión"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "S'ha perdiu a conexión"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Ista cuenta ya ye connectada a o servidor"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "S'ha substituiu a connexión por una nueva connexión usando o mesmo recurso"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "A cuenta ya existe en o servidor"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr ""
+#~ "Actualment o servidor ye muit aqueferau intentando chestionar a connexión"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "S'ha revocau o certificau"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "O certificau usa un algorismo de zifrau inseguro u ye criptograficament "
+#~ "feble"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "A longaria d'o certificau d'o servidor u a profundidat d'a cadena d'o "
+#~ "certificau d'o servidor exceden os limites imposaus por a biblioteca de "
+#~ "criptografía"
+
+#~ msgid "Internal error"
+#~ msgstr "Error interna"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "No se podió connectar a %s"
+
+#~ msgid "View account"
+#~ msgstr "Veyer cuenta"
+
+#~ msgid "Unknown reason"
+#~ msgstr "Razón desconoixida"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Ubrir calendario"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Achustes de calendata y hora"
+
+#~ msgid "Open"
+#~ msgstr "Ubrir"
+
+#~ msgid "Remove"
+#~ msgstr "Sacar"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Escoscar mensaches"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Configuración d'as notificacions"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Servilla d'o menú"
+
+#~ msgid "No Messages"
+#~ msgstr "No i hai mensaches"
+
+#~ msgid "Message Tray"
+#~ msgstr "Servilla de mensaches"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Portal captivo"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "O maximo libel de precisión d'ubicación."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Configura o ran maximo de precisión d'ubicación que as aplicacions "
+#~ "pueden veyer. As opcions validas son 'off' (seguimiento d'ubicación "
+#~ "desenchegau), 'country', 'city', 'neighborhood', 'street', and 'exact' "
+#~ "(typicament requier GPS "
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Orden d'os botons en a barra de titol"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Ista clau sobrescribe a clau en org.gnome.desktop.wm.preferences en "
+#~ "executar GNOME Shell."
+
+#~ msgid "Extension"
+#~ msgstr "Extensión"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Seleccione una extensión que configurar usando a caixa combinada d'alto."
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> de %B <b></b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b> de %B <b></b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Lista de categorías que s'han d'amostrar como carpetas"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Cada nombre de categoría d'ista lista se representará como una carpeta en "
+#~ "a vista d'aplicacions, en cuenta d'amostrar-se en linia en la vista "
+#~ "prencipal."
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Screenshots"
+#~ msgstr "Capturas de pantalla"
+
+#~ msgid "Record a screencast"
+#~ msgstr "Gravar una captura de pantalla"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr ""
+#~ "Indica si s'han de recullir estatisticas sobre l'uso d'as aplicacions"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "A \"shell\" normalment monitoriza todas as aplicacions activas ta "
+#~ "presentar as mas usadas (eix. en os lanzadors). Encara que istos datos se "
+#~ "mantienen de forma privada, puede querer desactivar-lo por razons de "
+#~ "privacidat. Note que fendo-lo no eliminará os datos ya alzaus."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Usau internament ta alzar o zaguer IM de presencia establiu explicitament "
+#~ "por l'usuario. A valor aquí ye d'a enumeración TpConnectionPresenceType."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Usau internament ta alzar o zaguer estau de presencia d'a sesión de "
+#~ "l'usuario. A valor aquí ye d'a GsmPresenceStatus."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Asociación de teclas ta cambiar o gravador de pantalla"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Asociación de teclas ta encetar/aturar o gravador de pantalla."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Taxa de fotogramas usada ta gravar \"screencast\"."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "A taxa de fotogramas d'a gravación resultant gravada por o gravador de "
+#~ "\"screencast\" de GNOME Shell, en fotogramas por segundo."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "A canyería de gstreamer usada ta codificar o \"screencast\""
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Estableix a canyería GStreamer usada ta codificar gravacions. Sigue a "
+#~ "sintaxi usada ta gst-launch. A canyería habría de tener un sumidero "
+#~ "(«sink») desconnectau an gravar o video que se ye gravando. Cheneralment "
+#~ "tendrá un punto d'orichen desconnectau; a salida d'ixe punto s'escribirá "
+#~ "en o fichero de salida. Manimenos, a canyería tamién puede prener parte "
+#~ "en a suya propia salida; isto se puede usar ta ninviar a salida a un "
+#~ "servidor «icecast» a traviés de shout2send u semellant. Quan no ye "
+#~ "establiu u lo ye a una valor vueda, s'usará a canyería predeterminada. "
+#~ "Actualment ye «'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 "
+#~ "deadline=1000000 threads=%T ! queue ! webmmux» y grava en WEBM usando o "
+#~ "códec VP8. S'usa %T como marcador de posición ta o numero de filos "
+#~ "optimos en o sistema."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Extensión de fichero que usar ta almagazenar os \"screencast\""
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "O nombre de fichero ta os \"screencast\" será un nombre de fichero solo "
+#~ "basau en a calendata actual y usará ista extensión. S'habría de cambiar "
+#~ "en gravar en unatro formato contenedor diferent."
+
+#~ msgid "Session…"
+#~ msgstr "Sesión…"
+
+#~ msgid "Power"
+#~ msgstr "Enerchía"
+
+#~ msgid "Restart"
+#~ msgstr "Reiniciar"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "Screencast dende %d %t"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Prete \"Trancar sesion\" ta salir de ixas aplicacions y trancar a sesion "
+#~ "d'o sistema."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Trancando a sesion."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "Prete \"Amortar\" ta salir d'ixas aplicacions y amortar o sistema."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Amortando o sistema."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Prete \"Reiniciar\" ta salir d'ixas aplicacions y reiniciar o sistema."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Reiniciando lo sistema."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Configuración de l'acceso universal"
+
+#~ msgid "Visibility"
+#~ msgstr "Visibilidat"
+
+#~ msgid "Send Files to Device…"
+#~ msgstr "Ninviar fichers a o dispositivo…"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Configurar un dispositivo nuevo…"
+
+#~ msgid "Send Files…"
+#~ msgstr "Ninviar fichers…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Configuración d'o teclau"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Configuración d'o ratet…"
+
+#~ msgid "Sound Settings"
+#~ msgstr "Configuración d'o son"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Solicitut d'autorización de %s"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "O dispositivo %s quiere accedir a o servicio \"%s\""
+
+#~ msgid "Always grant access"
+#~ msgstr "Conceder acceso siempre"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Conceder solament ista vegada"
+
+#~ msgid "Reject"
+#~ msgstr "Refusar"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Confirmación d'emparellamiento ta \"%s\""
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "O dispositivo \"%s\" quiere emparellar-se con iste equipo"
+
+#~ msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Confirme que o PIN amostrau en \"%06d\" coincide con o d'o dispositivo."
+
+#~ msgid "Matches"
+#~ msgstr "Coincide"
+
+#~ msgid "Does not match"
+#~ msgstr "No coincide"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Solicitut d'emparejamiento ta \"%s\""
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Introduzca o PIN mencionau en o dispositivo."
+
+#~ msgid "OK"
+#~ msgstr "Aceptar"
+
+#~ msgid "Region & Language Settings"
+#~ msgstr "Configuración de rechión y luenga"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Volumen, ret, batería"
+
+#~ msgid "disabled"
+#~ msgstr "desactivada"
+
+#~ msgid "cable unplugged"
+#~ msgstr "cordon desenchegau"
+
+#~ msgid "More…"
+#~ msgstr "Más…"
+
+#~ msgid "Wired"
+#~ msgstr "Cableada"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Ethernet automatica"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Banda ampla automatica"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Marcau automatico"
+
+#~ msgid "Auto %s"
+#~ msgstr "%s automatica"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Bluetooth automatico"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Sin cordón automatica"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "Networking is disabled"
+#~ msgstr "O rete ye desactivau"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "Manca %d hora"
+#~ msgstr[1] "Mancan %d horas"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "Mancan %d %s %d %s"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "hora"
+#~ msgstr[1] "horas"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minuto"
+#~ msgstr[1] "minutos"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "Manca %d minuto"
+#~ msgstr[1] "Mancan %d minutos"
+
+#~ msgctxt "percent of battery remaining"
+#~ msgid "%d%%"
+#~ msgstr "%d%%"
+
+#~ msgid "AC Adapter"
+#~ msgstr "Adaptador de corrient"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Bateria d'o portatil"
+
+#~ msgid "Monitor"
+#~ msgstr "Monitor"
+
+#~ msgid "Mouse"
+#~ msgstr "Ratet"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Telefono movil"
+
+#~ msgid "Media Player"
+#~ msgstr "Reproductor multimeya"
+
+#~ msgid "Tablet"
+#~ msgstr "Tablet"
+
+#~ msgid "Computer"
+#~ msgstr "Equipo"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Desconoxiu"
+
+#~ msgid "Available"
+#~ msgstr "Disponible"
+
+#~ msgid "Busy"
+#~ msgstr "Aqueferau"
+
+#~ msgid "Invisible"
+#~ msgstr "Invisible"
+
+#~ msgid "Away"
+#~ msgstr "Ausent"
+
+#~ msgid "Idle"
+#~ msgstr "Inactivo"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "O suyo estau d'o chat s'establirá a \"Aqueferau\""
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "As notificacions son agora desactivadas, incluindo os mensaches de chat. "
+#~ "O suyo estau en linia s'ha achustau ta que atros saban que puede no leyer "
+#~ "os suyos mensaches."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Amortar puede fer que pierdan o treballo que no haigan alzau."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Hue no tiene garra mensache:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "L'oráculo %s diz"
+
+#~ msgid "Show date in clock"
+#~ msgstr "Amostrar a calendata en o reloch"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Amostrar a hora con segundos"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "proveedors OpenSearch desactivaus"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APLICACIONS"
+
+#~ msgid "SETTINGS"
+#~ msgstr "CONFIGURACION"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %e de %b, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e de %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e de %b, %H:%M:%S"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %H:%M:%S"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "ELEMENTOS RECIENTZ"
+
+#~ msgid "Retry"
+#~ msgstr "Prebar atra vegada"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "PUESTOS Y DISPOSITIVOS"
+
+#~ msgid "Power Off..."
+#~ msgstr "Amortar..."
+
+#~ msgid "System Settings"
+#~ msgstr "Configuracion d'o sistema"
+
+#~ msgid "Lock Screen"
+#~ msgstr "Bloqueyar a pantalla"
+
+#~ msgid "Log Out..."
+#~ msgstr "Trancar a sesion..."
+
+#~ msgid "More..."
+#~ msgstr "Mas..."
+
+#~ msgid "%s is online."
+#~ msgstr "%s ye enchegau/da."
+
+#~ msgid "%s is away."
+#~ msgstr "%s ye ausent."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s ye aqueferau/da."
+
+#~ msgid "Sent at %X on %A"
+#~ msgstr "Ninviau a las %X o %A"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Reino Uniu"
+
+#~ msgid "Default"
+#~ msgstr "Predeterminada"
+
+#~ msgid "Home Folder"
+#~ msgstr "Carpeta presonal"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
diff --git a/po/ar.po b/po/ar.po
new file mode 100644
index 0000000..346a1e9
--- /dev/null
+++ b/po/ar.po
@@ -0,0 +1,3168 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Khaled Hosny <khaledhosny@eglug.org>, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019.
+# Muhammed Abd-ulaziz Abd-ullah <thenubianmuha_idrecy@yahoo.co.uk>, 2012.
+# Abderrahim Kitouni <a.kitouni@gmail.com>, 2012, 2014.
+# Ibrahim Saed <ibraheem5000@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-03-18 14:28+0200\n"
+"PO-Revision-Date: 2019-03-18 14:37+0200\n"
+"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
+"Language-Team: Arabic <doc@arabeyes.org>\n"
+"Language: ar\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
+"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
+"X-Generator: Virtaal 0.7.1\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "النظام"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "اعرض قائمة التنبيهات"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "ركّز على التنبيه النشط"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "اعرض النظرة العامة"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "أظهر كل التطبيقات"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "افتح قائمة التطبيقات"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:216
+msgid "Shell Extensions"
+msgstr "امتدادات الصدفة"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "اضبط امتدادات صدفة جنوم"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "صدفة جنوم"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "إدارة النوافذ وإطلاق التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "تمكين الأدوات الداخلية المفيدة للمطورين والمختبرين من Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"السماح بالوصول إلى التنقيح الداخلي ومراقبة الأدوات باستخدام نافذة حوار Alt-"
+"F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "معرّفات الامتدادات التي ستُفعّل"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disable user extensions"
+msgstr "عطّل امتدادات المستخدمين"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:34
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:51
+msgid "App Picker View"
+msgstr "منظور منتقي التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:52
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:58
+msgid "History for command (Alt-F2) dialog"
+msgstr "التأريخ لنافذة حوار الأمر (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "History for the looking glass dialog"
+msgstr "التأريخ لنافذة المرآة"
+
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:68
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:75
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:76
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:85
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:95
+msgid "Enable introspection API"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Keybinding to open the application menu"
+msgstr "اختصار فتح قائمة التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:110
+msgid "Keybinding to open the application menu."
+msgstr "اختصار فتح قائمة التطبيقات."
+
+#: data/org.gnome.shell.gschema.xml.in:116
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "اختصار منظور ”اعرض التطبيقات“"
+
+#: data/org.gnome.shell.gschema.xml.in:117
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "اختصار منظور ”اعرض التطبيقات“ في النظرة العامة للتطبيقات."
+
+#: data/org.gnome.shell.gschema.xml.in:124
+msgid "Keybinding to open the overview"
+msgstr "اختصار فتح النظرة العامة"
+
+#: data/org.gnome.shell.gschema.xml.in:125
+msgid "Keybinding to open the Activities Overview."
+msgstr "اختصار فتح النظرة العامة للأنشطة"
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "اختصار تبديل حالة ظهور قائمة التنبيهات"
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "اختصار تبديل حالة ظهور قائمة التنبيهات."
+
+#: data/org.gnome.shell.gschema.xml.in:138
+msgid "Keybinding to focus the active notification"
+msgstr "اختصار التركيز على التنبيه النشط"
+
+#: data/org.gnome.shell.gschema.xml.in:139
+msgid "Keybinding to focus the active notification."
+msgstr "اختصار التركيز على التنبيه النشط."
+
+#: data/org.gnome.shell.gschema.xml.in:145
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:150
+#, fuzzy
+msgid "Switch to application 1"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:154
+#, fuzzy
+msgid "Switch to application 2"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+#, fuzzy
+msgid "Switch to application 3"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:162
+#, fuzzy
+msgid "Switch to application 4"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+#, fuzzy
+msgid "Switch to application 5"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+#, fuzzy
+msgid "Switch to application 6"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:174
+#, fuzzy
+msgid "Switch to application 7"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+#, fuzzy
+msgid "Switch to application 8"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:182
+#, fuzzy
+msgid "Switch to application 9"
+msgstr "أظهر التطبيقات"
+
+#: data/org.gnome.shell.gschema.xml.in:190
+msgid "Which keyboard to use"
+msgstr "أي لوحة مفاتيح ترغب باستخدامها"
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "The type of keyboard to use."
+msgstr "نوع لوحة المفاتيح التي ترغب باستخدامها."
+
+#: data/org.gnome.shell.gschema.xml.in:202
+#: data/org.gnome.shell.gschema.xml.in:229
+msgid "Limit switcher to current workspace."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "The application icon mode."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:221
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:243
+#: data/org.gnome.shell.gschema.xml.in:252
+#: data/org.gnome.shell.gschema.xml.in:260
+#: data/org.gnome.shell.gschema.xml.in:268
+#: data/org.gnome.shell.gschema.xml.in:276
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:251
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:259
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid "Workspaces only on primary monitor"
+msgstr "مساحات عمل على الشاشة الرئيسية فقط"
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "ولوج الشّبكة"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:116
+msgid "Something’s gone wrong"
+msgstr "حدثت مشكلة"
+
+#: js/extensionPrefs/main.js:123
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"نأسف جدا لهذا، لكن هناك مشكلة: لا يمكن عرض إعدادات هذا الامتداد. ننصح بإبلاغ "
+"مطوري هذا الامتداد عن هذه المشكلة."
+
+#: js/extensionPrefs/main.js:130
+msgid "Technical Details"
+msgstr "التفاصيل التقنية"
+
+#: js/extensionPrefs/main.js:165
+msgid "Copy Error"
+msgstr "انسخ الخطأ"
+
+#: js/extensionPrefs/main.js:185
+msgid "Homepage"
+msgstr "الصفحة الرئيسية"
+
+#: js/extensionPrefs/main.js:186
+msgid "Visit extension homepage"
+msgstr "اذهب إلى صفحة الامتداد الرئيسية"
+
+#: js/extensionPrefs/main.js:449
+msgid "No Extensions Installed"
+msgstr "لا امتدادات مثبّتة"
+
+#: js/extensionPrefs/main.js:459
+msgid ""
+"Extensions can be installed through Software or <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+msgstr ""
+"يمكن تثبيت الامتدادات من تطبيق «البرمجيات» أو من <a href=\""
+"https://extensions.gnome.org\">extensions.gnome.org</a> "
+
+#: js/extensionPrefs/main.js:474
+msgid "Browse in Software"
+msgstr "تصفح في «البرمجيات»"
+
+#: js/gdm/authPrompt.js:140 js/ui/audioDeviceSelection.js:55
+#: js/ui/components/networkAgent.js:117 js/ui/components/polkitAgent.js:136
+#: js/ui/endSessionDialog.js:430 js/ui/extensionDownloader.js:188
+#: js/ui/shellMountOperation.js:325 js/ui/status/network.js:888
+msgid "Cancel"
+msgstr "ألغِ"
+
+#: js/gdm/authPrompt.js:159 js/gdm/authPrompt.js:202 js/gdm/authPrompt.js:434
+msgid "Next"
+msgstr "التالي"
+
+#: js/gdm/authPrompt.js:198 js/ui/shellMountOperation.js:329
+#: js/ui/unlockDialog.js:41
+msgid "Unlock"
+msgstr "فك القفل"
+
+#: js/gdm/authPrompt.js:200
+msgctxt "button"
+msgid "Sign In"
+msgstr "ادخل"
+
+#: js/gdm/loginDialog.js:302
+msgid "Choose Session"
+msgstr "أغلق الجلسة"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:446
+msgid "Not listed?"
+msgstr "غير مدرج؟"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:884
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(مثلا مستخدم أو %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:889 js/ui/components/networkAgent.js:243
+#: js/ui/components/networkAgent.js:263 js/ui/components/networkAgent.js:281
+msgid "Username: "
+msgstr "اسم المستخدم: "
+
+#: js/gdm/loginDialog.js:1227
+msgid "Login Window"
+msgstr "نافذة الولوج"
+
+#: js/gdm/util.js:337
+msgid "Authentication error"
+msgstr "خطأ في الاستيثاق"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:477
+msgid "(or swipe finger)"
+msgstr "(أو مرر إصبع)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:88
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "أطفئ"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:91
+msgid "power off;shutdown;reboot;restart"
+msgstr "أطفئ;أغلق;أعد التشغيل"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:95
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "أوصد الشاشة"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:98
+msgid "lock screen"
+msgstr "أوصد الشاشة"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:102
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "اخرج"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:105
+msgid "logout;sign off"
+msgstr "اخرج"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:109
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "علّق"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:112
+msgid "suspend;sleep"
+msgstr "علّق"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "غيّر المستخدم"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "switch user"
+msgstr "بدّل المستخدم"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:123
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr "أوصد الاتجاه"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:126
+msgid "lock orientation;screen;rotation"
+msgstr "أوصد الاتجاه;شاشة;تدوير"
+
+#: js/misc/util.js:117
+msgid "Command not found"
+msgstr "لم يُعثَر على الأمر"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:150
+msgid "Could not parse command:"
+msgstr "تعذّر تحليل الأمر:"
+
+#: js/misc/util.js:158
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "فشل تنفيذ ”%s“:‏"
+
+#: js/misc/util.js:175
+msgid "Just now"
+msgstr "الآن"
+
+#: js/misc/util.js:177
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "منذ أقل من دقيقة"
+msgstr[1] "منذ دقيقة"
+msgstr[2] "منذ دقيقتين"
+msgstr[3] "منذ %d دقائق"
+msgstr[4] "منذ %d دقيقة"
+msgstr[5] "منذ %d دقيقة"
+
+#: js/misc/util.js:180
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "منذ أقل من ساعة"
+msgstr[1] "منذ ساعة"
+msgstr[2] "منذ ساعتين"
+msgstr[3] "منذ %d ساعات"
+msgstr[4] "منذ %d ساعة"
+msgstr[5] "منذ %d ساعة"
+
+#: js/misc/util.js:183
+msgid "Yesterday"
+msgstr "أمس"
+
+#: js/misc/util.js:185
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "منذ أقل من يوم"
+msgstr[1] "منذ يوم"
+msgstr[2] "منذ يومين"
+msgstr[3] "منذ %d أيام"
+msgstr[4] "منذ %d يوما"
+msgstr[5] "منذ %d يوم"
+
+#: js/misc/util.js:188
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "منذ أقل من أسبوع"
+msgstr[1] "منذ أسبوع"
+msgstr[2] "منذ أسبوعين"
+msgstr[3] "منذ %d أسابيع"
+msgstr[4] "منذ %d أسبوعا"
+msgstr[5] "منذ %d أسبوع"
+
+#: js/misc/util.js:191
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "منذ أقل من شهر"
+msgstr[1] "منذ شهر واحد"
+msgstr[2] "منذ شهرين"
+msgstr[3] "منذ %d أشهر"
+msgstr[4] "منذ %d شهرًا"
+msgstr[5] "منذ %d شهر"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "منذ أقل من سنة"
+msgstr[1] "منذ سنة واحدة"
+msgstr[2] "منذ سنتين"
+msgstr[3] "منذ %d سنوات"
+msgstr[4] "منذ %d سنة"
+msgstr[5] "منذ %d سنة"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:223
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:229
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "بالأمس، %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:235
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A، %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:241
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B، %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:247
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y، %H:%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:252
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:258
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "بالأمس، %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:264
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A، %l:%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:270
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B، %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:276
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y، %l:%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:40
+msgid "Hotspot Login"
+msgstr "ولوج نقطة البث"
+
+#: js/portalHelper/main.js:86
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"اتصالك بنقطة الاتصال هذه غير آمن. كلمات السر أو أي معلومات تُدخلها في هذه "
+"الصفحة يمكن أن يطلع عليها الآخرون."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:37 js/ui/status/location.js:360
+msgid "Deny Access"
+msgstr "ارفض منح الصلاحية"
+
+#: js/ui/accessDialog.js:38 js/ui/status/location.js:363
+msgid "Grant Access"
+msgstr "امنح الصلاحية"
+
+#: js/ui/appDisplay.js:660
+msgid "Frequently used applications will appear here"
+msgstr "التطبيقات شائعة الاستخدام ستظهر هنا"
+
+#: js/ui/appDisplay.js:775
+msgid "Frequent"
+msgstr "شائعة"
+
+#: js/ui/appDisplay.js:782
+msgid "All"
+msgstr "الكل"
+
+#: js/ui/appDisplay.js:1737 js/ui/panel.js:83
+msgid "New Window"
+msgstr "نافذة جديدة"
+
+#: js/ui/appDisplay.js:1751
+msgid "Launch using Dedicated Graphics Card"
+msgstr "شغل باستخدام بطاقة الرسوميات المنفصلة"
+
+#: js/ui/appDisplay.js:1778 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "أزِل من المفضّلة"
+
+#: js/ui/appDisplay.js:1784
+msgid "Add to Favorites"
+msgstr "أضِف إلى المفضّلة"
+
+#: js/ui/appDisplay.js:1794 js/ui/panel.js:94
+msgid "Show Details"
+msgstr "أظهر التفاصيل"
+
+#: js/ui/appFavorites.js:141
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "أُضيف %s إلى مفضلتك."
+
+#: js/ui/appFavorites.js:175
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "أُزيل %s من مفضّلتك."
+
+#: js/ui/audioDeviceSelection.js:42
+msgid "Select Audio Device"
+msgstr "اختر جهاز صوت"
+
+#: js/ui/audioDeviceSelection.js:53
+msgid "Sound Settings"
+msgstr "إعدادات الصوت"
+
+#: js/ui/audioDeviceSelection.js:62
+msgid "Headphones"
+msgstr "سماعات أذن"
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headset"
+msgstr "سماعة رأس"
+
+#: js/ui/audioDeviceSelection.js:66 js/ui/status/volume.js:247
+msgid "Microphone"
+msgstr "ميكروفون"
+
+#: js/ui/backgroundMenu.js:13
+msgid "Change Background…"
+msgstr "غيّر الخلفية…"
+
+#: js/ui/backgroundMenu.js:15 js/ui/status/nightLight.js:43
+msgid "Display Settings"
+msgstr "إعدادات العرض"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Settings"
+msgstr "الإعدادات"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:40
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "56"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:69
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ح"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:71
+msgctxt "grid monday"
+msgid "M"
+msgstr "ن"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:73
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "ث"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:75
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "ر"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:77
+msgctxt "grid thursday"
+msgid "T"
+msgstr "خ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:79
+msgctxt "grid friday"
+msgid "F"
+msgstr "ج"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:81
+msgctxt "grid saturday"
+msgid "S"
+msgstr "س"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:332
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:342
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:399
+msgid "Previous month"
+msgstr "الشهر السابق"
+
+#: js/ui/calendar.js:410
+msgid "Next month"
+msgstr "الشهر التالي"
+
+#: js/ui/calendar.js:564
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:619
+msgid "Week %V"
+msgstr "أسبوع %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:687
+msgctxt "event list time"
+msgid "All Day"
+msgstr "طوال اليوم"
+
+#: js/ui/calendar.js:819
+msgctxt "calendar heading"
+msgid "%A, %B %-d"
+msgstr "%A، %-d %B"
+
+#: js/ui/calendar.js:823
+msgctxt "calendar heading"
+msgid "%A, %B %-d, %Y"
+msgstr "%A، %-d %B، %Y"
+
+#: js/ui/calendar.js:1046
+msgid "No Notifications"
+msgstr "لا تنبيهات"
+
+#: js/ui/calendar.js:1049
+msgid "No Events"
+msgstr "لا أحداث"
+
+#: js/ui/calendar.js:1075
+msgid "Clear"
+msgstr "امسح"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "”‏%s“ لا يستجيب."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "ربما ترغب في الانتظار قليلا ليُكمِل أو إجبار التطبيق على الإنهاء كُلّية."
+
+#: js/ui/closeDialog.js:59
+msgid "Force Quit"
+msgstr "أجبر الإنهاء"
+
+#: js/ui/closeDialog.js:62
+msgid "Wait"
+msgstr "انتظر"
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr "وُصّل قرص خارجي"
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr "فُصل القرص الخارجي"
+
+#: js/ui/components/autorunManager.js:334
+#, javascript-format
+msgid "Open with %s"
+msgstr "افتح باستخدام %s"
+
+#: js/ui/components/keyring.js:77 js/ui/components/polkitAgent.js:255
+msgid "Password:"
+msgstr "كلمة السرّ:"
+
+#: js/ui/components/keyring.js:108
+msgid "Type again:"
+msgstr "أدخلها ثانية:"
+
+#: js/ui/components/networkAgent.js:102
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:112 js/ui/status/network.js:219
+#: js/ui/status/network.js:309 js/ui/status/network.js:891
+msgid "Connect"
+msgstr "اتصل"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:211 js/ui/components/networkAgent.js:223
+#: js/ui/components/networkAgent.js:246 js/ui/components/networkAgent.js:265
+#: js/ui/components/networkAgent.js:285 js/ui/components/networkAgent.js:295
+msgid "Password: "
+msgstr "كلمة السرّ: "
+
+#. static WEP
+#: js/ui/components/networkAgent.js:216
+msgid "Key: "
+msgstr "المفتاح: "
+
+#: js/ui/components/networkAgent.js:249 js/ui/components/networkAgent.js:271
+msgid "Private key password: "
+msgstr "كلمة سرّ المفتاح الخاص: "
+
+#: js/ui/components/networkAgent.js:269
+msgid "Identity: "
+msgstr "التعريف: "
+
+#: js/ui/components/networkAgent.js:283
+msgid "Service: "
+msgstr "الخدمة: "
+
+#: js/ui/components/networkAgent.js:312 js/ui/components/networkAgent.js:685
+msgid "Authentication required by wireless network"
+msgstr "تتطلب الشبكة اللاسلكية الاستيثاق"
+
+#: js/ui/components/networkAgent.js:313 js/ui/components/networkAgent.js:686
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "كلمات السر أو مفاتيح التعمية مطلوبة للوصول إلى الشبكة اللاسلكية ”%s“."
+
+#: js/ui/components/networkAgent.js:317 js/ui/components/networkAgent.js:689
+msgid "Wired 802.1X authentication"
+msgstr "استيثاق 802.1X سلكي"
+
+#: js/ui/components/networkAgent.js:319
+msgid "Network name: "
+msgstr "اسم الشبكة: "
+
+#: js/ui/components/networkAgent.js:324 js/ui/components/networkAgent.js:693
+msgid "DSL authentication"
+msgstr "استيثاق اتصال DSL"
+
+#: js/ui/components/networkAgent.js:331 js/ui/components/networkAgent.js:699
+msgid "PIN code required"
+msgstr "رمز PIN مطلوب"
+
+#: js/ui/components/networkAgent.js:332 js/ui/components/networkAgent.js:700
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "جهاز شبكة الهاتف المحمول يتطلب رمز PIN"
+
+#: js/ui/components/networkAgent.js:333
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:340 js/ui/components/networkAgent.js:706
+msgid "Mobile broadband network password"
+msgstr "كلمة سرّ شبكة الهاتف المحمول"
+
+#: js/ui/components/networkAgent.js:341 js/ui/components/networkAgent.js:690
+#: js/ui/components/networkAgent.js:694 js/ui/components/networkAgent.js:707
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "كلمة السرّ مطلوبة للاتصال ب‍ ”%s“."
+
+#: js/ui/components/networkAgent.js:674 js/ui/status/network.js:1664
+msgid "Network Manager"
+msgstr "مدير الشبكة"
+
+#: js/ui/components/polkitAgent.js:34
+msgid "Authentication Required"
+msgstr "الاستيثاق مطلوب"
+
+#: js/ui/components/polkitAgent.js:62
+msgid "Administrator"
+msgstr "المدير"
+
+#: js/ui/components/polkitAgent.js:139
+msgid "Authenticate"
+msgstr "استوثق"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:241 js/ui/shellMountOperation.js:309
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "للأسف لم يُفلح هذا. أعِد المحاولة."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:778
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "غيّر %s اسمه إلى %s"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:169
+msgid "Windows"
+msgstr "النوافذ"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr "أظهر التطبيقات"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:390
+msgid "Dash"
+msgstr "الشريط"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:62
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:69
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#: js/ui/dateMenu.js:131
+msgid "Add world clocks…"
+msgstr "أضف ساعات عالمية…"
+
+#: js/ui/dateMenu.js:132
+msgid "World Clocks"
+msgstr "ساعات عالمية"
+
+#: js/ui/dateMenu.js:222
+msgid "Weather"
+msgstr "الطقس"
+
+#: js/ui/dateMenu.js:305
+msgid "Select a location…"
+msgstr "اختر موقعا…"
+
+#: js/ui/dateMenu.js:313
+msgid "Loading…"
+msgstr "يحمّل…"
+
+#: js/ui/dateMenu.js:323
+msgid "Go online for weather information"
+msgstr "اتصل بالإنترنت لمعلومات الطقس"
+
+#: js/ui/dateMenu.js:325
+msgid "Weather information is currently unavailable"
+msgstr "معلومات الطقس غير متاحة حاليًا"
+
+#: js/ui/endSessionDialog.js:42
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "خروج %s"
+
+#: js/ui/endSessionDialog.js:43
+msgctxt "title"
+msgid "Log Out"
+msgstr "خروج"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "سيُخرج %s تلقائيا الآن."
+msgstr[1] "سيُخرج %s تلقائيا بعد ثانية."
+msgstr[2] "سيُخرج %s تلقائيا بعد ثانيتين."
+msgstr[3] "سيُخرج %s تلقائيا بعد %d ثوان."
+msgstr[4] "سيُخرج %s تلقائيا بعد %d ثانية."
+msgstr[5] "سيُخرج %s تلقائيا بعد %d ثانية."
+
+#: js/ui/endSessionDialog.js:50
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "ستُخرج تلقائيا الآن."
+msgstr[1] "ستُخرج تلقائيا بعد ثانية."
+msgstr[2] "ستُخرج تلقائيا بعد ثانيتين."
+msgstr[3] "ستُخرج تلقائيا بعد %d ثوان."
+msgstr[4] "ستُخرج تلقائيا بعد %d ثانية."
+msgstr[5] "ستُخرج تلقائيا بعد %d ثانية."
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "اخرج"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "إطفاء الحاسوب"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "ثبّت التحديثات ثم أطفئ"
+
+#: js/ui/endSessionDialog.js:65
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "سيُطفأ النظام تلقائيا الآن."
+msgstr[1] "سيُطفأ النظام تلقائيا بعد ثانية."
+msgstr[2] "سيُطفأ النظام تلقائيا بعد ثانيتين."
+msgstr[3] "سيُطفأ النظام تلقائيا بعد %d ثوان."
+msgstr[4] "سيُطفأ النظام تلقائيا بعد %d ثانية."
+msgstr[5] "سيُطفأ النظام تلقائيا بعد %d ثانية."
+
+#: js/ui/endSessionDialog.js:69
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "ثبّت تحديثات البرمجيات المعلّقة"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:89
+msgctxt "button"
+msgid "Restart"
+msgstr "أعِد التشغيل"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "أطفئ"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "إعادة التشغيل"
+
+#: js/ui/endSessionDialog.js:83
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "سيُعاد تشغيل النظام تلقائيا الآن."
+msgstr[1] "سيُعاد تشغيل النظام تلقائيا بعد ثانية."
+msgstr[2] "سيُعاد تشغيل النظام تلقائيا بعد ثانيتين."
+msgstr[3] "سيُعاد تشغيل النظام تلقائيا بعد %d ثوان."
+msgstr[4] "سيُعاد تشغيل النظام تلقائيا بعد %d ثانية."
+msgstr[5] "سيُعاد تشغيل النظام تلقائيا بعد %d ثانية."
+
+#: js/ui/endSessionDialog.js:97
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "أعد التشغيل و ثبّت التحديثات"
+
+#: js/ui/endSessionDialog.js:99
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "سيُعاد تشغيل النظام تلقائيا و تُثبت التحديثات الآن."
+msgstr[1] "سيُعاد تشغيل النظام تلقائيا و تُثبت التحديثات بعد ثانية."
+msgstr[2] "سيُعاد تشغيل النظام تلقائيا و تُثبت التحديثات بعد ثانيتين."
+msgstr[3] "سيُعاد تشغيل النظام تلقائيا و تُثبت التحديثات بعد %d ثوان."
+msgstr[4] "سيُعاد تشغيل النظام تلقائيا و تُثبت التحديثات بعد %d ثانية."
+msgstr[5] "سيُعاد تشغيل النظام تلقائيا و تُثبت التحديثات بعد %d ثانية."
+
+#: js/ui/endSessionDialog.js:105 js/ui/endSessionDialog.js:125
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "أعد التشغيل ثم ثبّت"
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ثبّت ثم أطفئ"
+
+#: js/ui/endSessionDialog.js:107
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "أطفئ بعد تثبيت التحديثات"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "أعد التشغيل و ثبّت الترقية"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:120
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"ستُثبّت %s %s بعد إعادة التشغيل. قد يستغرق تثبيت الترقية بعض الوقت: تأكد من "
+"الحفظ الاحتياطي، و توصيل الحاسوب بمقبس الطاقة."
+
+#: js/ui/endSessionDialog.js:309
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "يعمل على طاقة البطارية: رجاءً وصّل بمقبس التيار قبل تثبيت التحديثات."
+
+#: js/ui/endSessionDialog.js:326
+msgid "Some applications are busy or have unsaved work."
+msgstr "بعض التطبيقات مشغولة أو تحتوي عملًا لم يُحفظ."
+
+#: js/ui/endSessionDialog.js:333
+msgid "Other users are logged in."
+msgstr "مستخدمون آخرون والِجون"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:614
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (عن بعد)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:617
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (طرفية)"
+
+#: js/ui/extensionDownloader.js:192
+msgid "Install"
+msgstr "ثبت"
+
+#: js/ui/extensionDownloader.js:197
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "هل تريد تنزيل و تثبيت ”%s“ من extensions.gnome.org؟"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr "يريد %s تعطيل الاختصارات"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Application wants to inhibit shortcuts"
+msgstr "يريد التطبيق تعطيل الاختصارات"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:88
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "يمكنك استعادة الاختصارات بضغط %s."
+
+#: js/ui/inhibitShortcutsDialog.js:93
+msgid "Deny"
+msgstr "ارفض"
+
+#: js/ui/inhibitShortcutsDialog.js:100
+msgid "Allow"
+msgstr "اسمح"
+
+#: js/ui/kbdA11yDialog.js:31
+msgid "Slow Keys Turned On"
+msgstr "المفاتيح البطيئة مفعلة"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned Off"
+msgstr "المفاتيح البطيئة معطّلة"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"لقد ضغطت مفتاح Shift لثمان ثوان. هذا اختصار ميزة المفاتيح البطيئة المؤثّر على "
+"طريقة عمل لوحة مفاتيحك."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "المفاتيح اللاصقة مفعلة"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "المفاتيح اللاصقة معطّلة"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"لقد نقرت مفتاح Shift خمس مرات متتالية. هذا اختصار ميزة المفاتيح اللاصقة "
+"المؤثّر على طريقة عمل لوحة مفاتيحك."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"لقد ضغطت على مفتاحان في آن واحد أو نقرت مفتاح Shift خمس مرّات متتالية. يعطّل "
+"هذا ميزة المفاتيح اللاصقة المؤثّر على طريقة عمل لوحة مفاتيحك."
+
+#: js/ui/kbdA11yDialog.js:57
+msgid "Leave On"
+msgstr "اتركه مفعّلا"
+
+#: js/ui/kbdA11yDialog.js:57 js/ui/status/bluetooth.js:133
+#: js/ui/status/network.js:1263
+msgid "Turn On"
+msgstr "شغّل"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:133
+#: js/ui/status/network.js:128 js/ui/status/network.js:310
+#: js/ui/status/network.js:1263 js/ui/status/network.js:1375
+#: js/ui/status/nightLight.js:39 js/ui/status/rfkill.js:79
+#: js/ui/status/rfkill.js:106
+msgid "Turn Off"
+msgstr "أوقف"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "اتركه معطّلا"
+
+#: js/ui/keyboard.js:200
+msgid "Region & Language Settings"
+msgstr "إعدادات الإقليم و اللغة"
+
+#: js/ui/lookingGlass.js:615
+msgid "No extensions installed"
+msgstr "لا امتدادات مثبّتة"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:669
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "لم يصدر %s أي خطأ."
+
+#: js/ui/lookingGlass.js:675
+msgid "Hide Errors"
+msgstr "أخفِ الأخطاء"
+
+#: js/ui/lookingGlass.js:679 js/ui/lookingGlass.js:739
+msgid "Show Errors"
+msgstr "اظهر الأخطاء"
+
+#: js/ui/lookingGlass.js:688
+msgid "Enabled"
+msgstr "مفعّل"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:691 subprojects/gvc/gvc-mixer-control.c:1864
+msgid "Disabled"
+msgstr "معطّل"
+
+#: js/ui/lookingGlass.js:693
+msgid "Error"
+msgstr "خطأ"
+
+#: js/ui/lookingGlass.js:695
+msgid "Out of date"
+msgstr "قديم"
+
+#: js/ui/lookingGlass.js:697
+msgid "Downloading"
+msgstr "ينزّل"
+
+#: js/ui/lookingGlass.js:721
+msgid "View Source"
+msgstr "اعرض المصدر"
+
+#: js/ui/lookingGlass.js:730
+msgid "Web Page"
+msgstr "صفحة الوب"
+
+#: js/ui/messageTray.js:1480
+msgid "System Information"
+msgstr "معلومات النظام"
+
+#: js/ui/mpris.js:177
+msgid "Unknown artist"
+msgstr "فنان غير معروف"
+
+#: js/ui/mpris.js:178
+msgid "Unknown title"
+msgstr "عنوان غير معروف"
+
+#: js/ui/osdWindow.js:22 js/ui/status/volume.js:199
+msgid "Volume"
+msgstr "شدة الصوت"
+
+#: js/ui/overview.js:73
+msgid "Undo"
+msgstr "تراجع"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:100
+msgid "Overview"
+msgstr "نظرة عامة"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:226
+msgid "Type to search…"
+msgstr "اكتب نصا للبحث عنه…"
+
+#: js/ui/padOsd.js:92
+msgid "New shortcut…"
+msgstr "اختصار جديد…"
+
+#: js/ui/padOsd.js:141
+msgid "Application defined"
+msgstr "التطبيقات معرّفة"
+
+#: js/ui/padOsd.js:142
+msgid "Show on-screen help"
+msgstr "اعرض المساعدة على الشاشة"
+
+#: js/ui/padOsd.js:143
+msgid "Switch monitor"
+msgstr "بدّل الشاشة"
+
+#: js/ui/padOsd.js:144
+msgid "Assign keystroke"
+msgstr "أسند ضغطة مفتاح"
+
+#: js/ui/padOsd.js:209
+msgid "Done"
+msgstr "تم"
+
+#: js/ui/padOsd.js:721
+msgid "Edit…"
+msgstr "حرّر…"
+
+#: js/ui/padOsd.js:763 js/ui/padOsd.js:868
+msgid "None"
+msgstr "لا شيء"
+
+#: js/ui/padOsd.js:822
+msgid "Press a button to configure"
+msgstr "اضغط زر للضبط"
+
+#: js/ui/padOsd.js:823
+msgid "Press Esc to exit"
+msgstr "اضغط Esc للخروج"
+
+#: js/ui/padOsd.js:826
+msgid "Press any key to exit"
+msgstr "اضغط أي زر للخروج"
+
+#: js/ui/panel.js:108
+msgid "Quit"
+msgstr "أنْهِ"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:471
+msgid "Activities"
+msgstr "الأنشطة"
+
+#: js/ui/panel.js:746
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "النظام"
+
+#: js/ui/panel.js:866
+msgid "Top Bar"
+msgstr "الشريط العلوي"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:285
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: js/ui/runDialog.js:64
+msgid "Enter a Command"
+msgstr "اكتب أمرًا"
+
+#: js/ui/runDialog.js:104 js/ui/windowMenu.js:166
+msgid "Close"
+msgstr "أغلِق"
+
+#: js/ui/runDialog.js:266
+msgid "Restart is not available on Wayland"
+msgstr "إعادة التشغيل غير متاحة على وايلاند"
+
+#: js/ui/runDialog.js:271
+msgid "Restarting…"
+msgstr "يُعيد التشغيل…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:77
+msgid "%A, %B %d"
+msgstr "%A، %d %B"
+
+#: js/ui/screenShield.js:133
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "لا رسائل جديدة"
+msgstr[1] "رسالة جديدة"
+msgstr[2] "رسالتين جديدتين"
+msgstr[3] "%d رسائل جديدة"
+msgstr[4] "%d رسالة جديدة"
+msgstr[5] "%d رسالة جديدة"
+
+#: js/ui/screenShield.js:135
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "لا تنبيهات جديدة"
+msgstr[1] "تنبيه جديد"
+msgstr[2] "تنبيهين جديدين"
+msgstr[3] "%d تنبيهات جديدة"
+msgstr[4] "%d تنبيها جديدا"
+msgstr[5] "%d تنبيه جديد"
+
+#: js/ui/screenShield.js:449 js/ui/status/system.js:270
+msgid "Lock"
+msgstr "أوصِد"
+
+#: js/ui/screenShield.js:713
+msgid "GNOME needs to lock the screen"
+msgstr "تحتاج جنوم إلى إيصاد الشاشة"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:826 js/ui/screenShield.js:1299
+msgid "Unable to lock"
+msgstr "تعذّر الإيصاد"
+
+#: js/ui/screenShield.js:827 js/ui/screenShield.js:1300
+msgid "Lock was blocked by an application"
+msgstr "منع تطبيق الإيصاد"
+
+#: js/ui/search.js:635
+msgid "Searching…"
+msgstr "يبحث…"
+
+#: js/ui/search.js:637
+msgid "No results."
+msgstr "لا نتائج."
+
+#: js/ui/search.js:761
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "لا مزيد"
+msgstr[1] "%d أكثر"
+msgstr[2] "%d أكثر"
+msgstr[3] "%d أكثر"
+msgstr[4] "%d أكثر"
+msgstr[5] "%d أكثر"
+
+#: js/ui/shellEntry.js:19
+msgid "Copy"
+msgstr "انسخ"
+
+#: js/ui/shellEntry.js:24
+msgid "Paste"
+msgstr "ألصق"
+
+#: js/ui/shellEntry.js:90
+msgid "Show Text"
+msgstr "أظهر النص"
+
+#: js/ui/shellEntry.js:92
+msgid "Hide Text"
+msgstr "أخفِ النص"
+
+#: js/ui/shellMountOperation.js:296
+msgid "Password"
+msgstr "كلمة السر"
+
+#: js/ui/shellMountOperation.js:317
+msgid "Remember Password"
+msgstr "تذكر كلمة السر"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "الإتاحة"
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr "تقريب"
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr "قارئ الشاشة"
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr "لوحة مفاتيح على الشاشة"
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr "تنبيهات بصرية"
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr "مفاتيح لاصقة"
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr "مفاتيح بطيئة"
+
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr "مفاتيح لها صوت"
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr "مفاتيح الفأرة"
+
+#: js/ui/status/accessibility.js:151
+msgid "High Contrast"
+msgstr "تباين عال"
+
+#: js/ui/status/accessibility.js:182
+msgid "Large Text"
+msgstr "نص كبير"
+
+#: js/ui/status/bluetooth.js:38
+msgid "Bluetooth"
+msgstr "بلوتوث"
+
+#: js/ui/status/bluetooth.js:47 js/ui/status/network.js:585
+msgid "Bluetooth Settings"
+msgstr "إعدادات بلوتوث"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:127
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d مُتصل"
+msgstr[1] "%d مُتصل"
+msgstr[2] "%d مُتصل"
+msgstr[3] "%d مُتصل"
+msgstr[4] "%d مُتصل"
+msgstr[5] "%d مُتصل"
+
+#: js/ui/status/bluetooth.js:129
+msgid "Off"
+msgstr "مغلق"
+
+#: js/ui/status/bluetooth.js:131
+msgid "On"
+msgstr "مفعّل"
+
+#: js/ui/status/brightness.js:36
+msgid "Brightness"
+msgstr "السطوع"
+
+#: js/ui/status/keyboard.js:812
+msgid "Keyboard"
+msgstr "لوحة المفاتيح"
+
+#: js/ui/status/keyboard.js:834
+msgid "Show Keyboard Layout"
+msgstr "أظهر تخطيط لوحة المفاتيح"
+
+#: js/ui/status/location.js:64 js/ui/status/location.js:172
+msgid "Location Enabled"
+msgstr "التموضع مفعّل"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:173
+msgid "Disable"
+msgstr "عطّل"
+
+#: js/ui/status/location.js:66
+msgid "Privacy Settings"
+msgstr "إعدادات الخصوصية"
+
+#: js/ui/status/location.js:171
+msgid "Location In Use"
+msgstr "التموضع مستخدم"
+
+#: js/ui/status/location.js:175
+msgid "Location Disabled"
+msgstr "التموضع غير مفعّل"
+
+#: js/ui/status/location.js:176
+msgid "Enable"
+msgstr "فعّل"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:353
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "أتريد إعطاء %s صلاحية معرفة مكانك؟"
+
+#: js/ui/status/location.js:354
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "يمكن تغيير صلاحيات الوصول لمكانك في أي وقت من إعدادات الخصوصية."
+
+#: js/ui/status/network.js:66
+msgid "<unknown>"
+msgstr "<غير معروفة>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:416 js/ui/status/network.js:1292
+#, javascript-format
+msgid "%s Off"
+msgstr "‏%s مغلق"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:419
+#, javascript-format
+msgid "%s Connected"
+msgstr "‏%s مُتصل"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:424
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "‏%s غير مُدار"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:427
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "‏%s يقطع الاتّصال"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:434 js/ui/status/network.js:1284
+#, javascript-format
+msgid "%s Connecting"
+msgstr "‏%s يتّصل"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:437
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "يتطلب %s استيثاق"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:445
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "برمجيات %s المغروسة (Firmware) غير متاحة"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:449
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "‏%s غير متاح"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:452
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "فشل اتصال %s"
+
+#: js/ui/status/network.js:464
+msgid "Wired Settings"
+msgstr "إعدادات الاتصال السلكي"
+
+#: js/ui/status/network.js:506
+msgid "Mobile Broadband Settings"
+msgstr "إعدادات شبكة الهاتف المحمول"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:553 js/ui/status/network.js:1289
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "عتاد %s مُعَطَّل"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:557
+#, javascript-format
+msgid "%s Disabled"
+msgstr "‏%s معطّل"
+
+#: js/ui/status/network.js:597
+msgid "Connect to Internet"
+msgstr "اتصل بالإنترنت"
+
+#: js/ui/status/network.js:786
+msgid "Airplane Mode is On"
+msgstr "وضع الطائرة مُفعّل"
+
+#: js/ui/status/network.js:787
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "الاتصال اللاسلكي مُعطّل في وضع الطائرة."
+
+#: js/ui/status/network.js:788
+msgid "Turn Off Airplane Mode"
+msgstr "عطّل وضع الطائرة"
+
+#: js/ui/status/network.js:797
+msgid "Wi-Fi is Off"
+msgstr "الاتصال اللاسلكي مغلق"
+
+#: js/ui/status/network.js:798
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "تحتاج إلى تشغيل الاتصال اللاسلكي للاتصال بالشبكة."
+
+#: js/ui/status/network.js:799
+msgid "Turn On Wi-Fi"
+msgstr "شغّل الاتصال اللاسلكي"
+
+#: js/ui/status/network.js:824
+msgid "Wi-Fi Networks"
+msgstr "الشبكات اللاسلكية"
+
+#: js/ui/status/network.js:826
+msgid "Select a network"
+msgstr "اختر شبكة"
+
+#: js/ui/status/network.js:855
+msgid "No Networks"
+msgstr "لا شبكات"
+
+#: js/ui/status/network.js:876 js/ui/status/rfkill.js:104
+msgid "Use hardware switch to turn off"
+msgstr "استخدم زر العتاد للإغلاق"
+
+#: js/ui/status/network.js:1152
+msgid "Select Network"
+msgstr "اختر شبكة"
+
+#: js/ui/status/network.js:1158
+msgid "Wi-Fi Settings"
+msgstr "إعدادات الاتصال اللاسلكي"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1280
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "نقطة اتصال %s نشطة"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1295
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "‏%s غير متّصل"
+
+#: js/ui/status/network.js:1392
+msgid "connecting…"
+msgstr "يتّصل…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1395
+msgid "authentication required"
+msgstr "الاستيثاق مطلوب"
+
+#: js/ui/status/network.js:1397
+msgid "connection failed"
+msgstr "فشل الاتصال"
+
+#: js/ui/status/network.js:1447
+msgid "VPN Settings"
+msgstr "إعدادات VPN"
+
+#: js/ui/status/network.js:1464
+msgid "VPN"
+msgstr "‏VPN"
+
+#: js/ui/status/network.js:1474
+msgid "VPN Off"
+msgstr "‏VPN مُطفأ"
+
+#: js/ui/status/network.js:1535 js/ui/status/rfkill.js:82
+msgid "Network Settings"
+msgstr "إعدادات الشّبكة"
+
+#: js/ui/status/network.js:1564
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "اتصال سلكي"
+msgstr[1] "اتصال سلكي"
+msgstr[2] "اتصاليْن سلكيِّيْن"
+msgstr[3] "%s اتصالات سلكية"
+msgstr[4] "%s اتصالًا سلكيًا"
+msgstr[5] "%s اتصال سلكي"
+
+#: js/ui/status/network.js:1568
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "اتصال لاسكلي"
+msgstr[1] "اتصال لاسكلي"
+msgstr[2] "اتصاليْن لاسكليِّيْن"
+msgstr[3] "%s اتصالات لاسكلية"
+msgstr[4] "%s اتصالًا لاسكليًا"
+msgstr[5] "%s اتصال لاسكلي"
+
+#: js/ui/status/network.js:1572
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "اتصال مودم"
+msgstr[1] "اتصال مودم"
+msgstr[2] "اتصاليْ مودم"
+msgstr[3] "‏%s اتصالات مودم"
+msgstr[4] "‏%s اتصال مودم"
+msgstr[5] "‏%s اتصال مودم"
+
+#: js/ui/status/network.js:1701
+msgid "Connection failed"
+msgstr "فشل الاتصال"
+
+#: js/ui/status/network.js:1702
+msgid "Activation of network connection failed"
+msgstr "فشل تفعيل اتصال الشبكة"
+
+#: js/ui/status/nightLight.js:60
+msgid "Night Light Disabled"
+msgstr "الإضاءة الليلية موقفة"
+
+#: js/ui/status/nightLight.js:61
+msgid "Night Light On"
+msgstr "الإضاءة الليلية مفعّلة"
+
+#: js/ui/status/nightLight.js:62
+msgid "Resume"
+msgstr "استكمل"
+
+#: js/ui/status/nightLight.js:63
+msgid "Disable Until Tomorrow"
+msgstr "أوقف حتى الغد"
+
+#: js/ui/status/power.js:45
+msgid "Power Settings"
+msgstr "إعدادات الطاقة"
+
+#: js/ui/status/power.js:61
+msgid "Fully Charged"
+msgstr "مشحونة بالكامل"
+
+#: js/ui/status/power.js:67
+msgid "Not Charging"
+msgstr "لا تُشحن"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:70 js/ui/status/power.js:76
+msgid "Estimating…"
+msgstr "يَحسِب…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:84
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "بقي %d:%02d ‏(%d٪)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:89
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "بقي على الاكتمال %d:%02d ‏(%d٪)"
+
+#: js/ui/status/power.js:117 js/ui/status/power.js:119
+#, javascript-format
+msgid "%d %%"
+msgstr "%d ٪"
+
+#: js/ui/status/remoteAccess.js:42
+msgid "Screen is Being Shared"
+msgstr "تجري مشاركة الشاشة"
+
+#: js/ui/status/remoteAccess.js:44
+msgid "Turn off"
+msgstr "أوقف"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:77
+msgid "Airplane Mode On"
+msgstr "وضع الطائرة مُفعّل"
+
+#: js/ui/status/system.js:203
+msgid "Switch User"
+msgstr "بدّل المستخدم"
+
+#: js/ui/status/system.js:215
+msgid "Log Out"
+msgstr "اخرج"
+
+#: js/ui/status/system.js:227
+msgid "Account Settings"
+msgstr "إعدادات الحساب"
+
+#: js/ui/status/system.js:255
+msgid "Orientation Lock"
+msgstr "قفل الاتجاه"
+
+#: js/ui/status/system.js:281
+msgid "Suspend"
+msgstr "علّق"
+
+#: js/ui/status/system.js:291
+msgid "Power Off"
+msgstr "أطفئ الحاسوب"
+
+#: js/ui/status/thunderbolt.js:261
+msgid "Thunderbolt"
+msgstr "ثندربُلت"
+
+#: js/ui/status/thunderbolt.js:322
+msgid "Unknown Thunderbolt device"
+msgstr "جهاز ثندربُلت غير معروف"
+
+#: js/ui/status/thunderbolt.js:323
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"وصّل جهاز جديد و أنت غير موجود. رجاء أفصل الجهاز ثم أوصله لتستطيع استخدامه."
+
+#: js/ui/status/thunderbolt.js:326
+msgid "Unauthorized Thunderbolt device"
+msgstr "جهاز ثندربُلت غير مخوّل"
+
+#: js/ui/status/thunderbolt.js:327
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "اكتُشف جهاز جديد وعلى مدير النظام تخويله."
+
+#: js/ui/status/thunderbolt.js:333
+msgid "Thunderbolt authorization error"
+msgstr "عطل أثناء تخويل ثندربُلت"
+
+#: js/ui/status/thunderbolt.js:334
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "تعذّر تخويل جهاز ثندربُلت: %s"
+
+#: js/ui/status/volume.js:133
+msgid "Volume changed"
+msgstr "تغيّرت شدة الصوت"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:16
+msgid "Mirror"
+msgstr "طابق"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:21
+msgid "Join Displays"
+msgstr "ضم الشاشات"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:26
+msgid "External Only"
+msgstr "الخارجية فقط"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:31
+msgid "Built-in Only"
+msgstr "المدمجة فقط"
+
+#: js/ui/unlockDialog.js:49
+msgid "Log in as another user"
+msgstr "لِج كمستخدم آخر"
+
+#: js/ui/unlockDialog.js:66
+msgid "Unlock Window"
+msgstr "افتح قفل النافذة"
+
+#: js/ui/viewSelector.js:173
+msgid "Applications"
+msgstr "التطبيقات"
+
+#: js/ui/viewSelector.js:177
+msgid "Search"
+msgstr "ابحث"
+
+#: js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "‏”%s“ جاهز"
+
+#: js/ui/windowManager.js:53
+msgid "Do you want to keep these display settings?"
+msgstr "أتريد الإبقاء على هذه الإعدادات؟"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:65
+msgid "Revert Settings"
+msgstr "تراجع عن الإعدادات"
+
+#: js/ui/windowManager.js:68
+msgid "Keep Changes"
+msgstr "أبق على التغييرات"
+
+#: js/ui/windowManager.js:86
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "سيُتراجع عن التغييرات الآن"
+msgstr[1] "سيُتراجع عن التغييرات بعد ثانية"
+msgstr[2] "سيُتراجع عن التغييرات بعد ثانيتين"
+msgstr[3] "سيُتراجع عن التغييرات بعد %d ثوان"
+msgstr[4] "سيُتراجع عن التغييرات بعد %d ثانية"
+msgstr[5] "سيُتراجع عن التغييرات بعد %d ثانية"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:662
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:26
+msgid "Minimize"
+msgstr "صغّر"
+
+#: js/ui/windowMenu.js:33
+msgid "Unmaximize"
+msgstr "ألغِ التكبير"
+
+#: js/ui/windowMenu.js:37
+msgid "Maximize"
+msgstr "كبّر"
+
+#: js/ui/windowMenu.js:44
+msgid "Move"
+msgstr "انقل"
+
+#: js/ui/windowMenu.js:50
+msgid "Resize"
+msgstr "غيّر الحجم"
+
+#: js/ui/windowMenu.js:57
+msgid "Move Titlebar Onscreen"
+msgstr "انقل شريط العنوان على الشاشة"
+
+#: js/ui/windowMenu.js:62
+msgid "Always on Top"
+msgstr "دائما في القمّة"
+
+#: js/ui/windowMenu.js:81
+msgid "Always on Visible Workspace"
+msgstr "دائما على مساحة العمل المرئية"
+
+#: js/ui/windowMenu.js:95
+msgid "Move to Workspace Left"
+msgstr "انقل لمساحة العمل على اليسار"
+
+#: js/ui/windowMenu.js:101
+msgid "Move to Workspace Right"
+msgstr "انقل لمساحة العمل على اليمين"
+
+#: js/ui/windowMenu.js:107
+msgid "Move to Workspace Up"
+msgstr "انقل لمساحة العمل لأعلى"
+
+#: js/ui/windowMenu.js:113
+msgid "Move to Workspace Down"
+msgstr "انقل لمساحة العمل لأسفل"
+
+#: js/ui/windowMenu.js:131
+msgid "Move to Monitor Up"
+msgstr "انقل للشاشة الأعلى"
+
+#: js/ui/windowMenu.js:140
+msgid "Move to Monitor Down"
+msgstr "انقل للشاشة الأسفل"
+
+#: js/ui/windowMenu.js:149
+msgid "Move to Monitor Left"
+msgstr "انقل للشاشة اليسرى"
+
+#: js/ui/windowMenu.js:158
+msgid "Move to Monitor Right"
+msgstr "انقل للشاشة اليمنى"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "تقويم إيفُليوشِن"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: src/main.c:408
+msgid "Print version"
+msgstr "اطبع الإصدارة"
+
+#: src/main.c:414
+msgid "Mode used by GDM for login screen"
+msgstr "الوضع الذي يستخدمه مدير ولوج جنوم لشاشة الولوج"
+
+#: src/main.c:420
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "استخدم طورا معينا، مثلا: ”gdm“ لشاشة الولوج"
+
+#: src/main.c:426
+msgid "List possible modes"
+msgstr "اسرد كل الأطوار الممكنة"
+
+#: src/shell-app.c:264
+msgctxt "program"
+msgid "Unknown"
+msgstr "غير معروف"
+
+#: src/shell-app.c:515
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "فشل تشغيل ”%s“"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "لا تتطابق كلمتا السر."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "لا يمكن أن تكون كلمة السرّ فارغة"
+
+#: src/shell-polkit-authentication-agent.c:348
+msgid "Authentication dialog was dismissed by the user"
+msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1871
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "لا مخرَج"
+msgstr[1] "مخرَج واحد"
+msgstr[2] "مخرَجين"
+msgstr[3] "%u مخارج"
+msgstr[4] "%u مخرجا"
+msgstr[5] "%u مخرج"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1881
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "لا مدخل"
+msgstr[1] "مدخل واحد"
+msgstr[2] "مدخلين"
+msgstr[3] "%u مداخل"
+msgstr[4] "%u مدخلا"
+msgstr[5] "%u مدخل"
+
+#: subprojects/gvc/gvc-mixer-control.c:2736
+msgid "System Sounds"
+msgstr "أصوات النظام"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "حدث خطأ أثناء تشغيل حوار تفضيلات %s‏:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s طوال اليوم."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s، ثم %s فيما بعد."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s، ثم %s، و بعدها %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "تبدو مثل %s."
+
+#~ msgctxt "search-result"
+#~ msgid "Power off"
+#~ msgstr "أطفئ"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "اخرج"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "بدّل المستخدم"
+
+#~ msgid "Hide tray"
+#~ msgstr "أخفِ الصينية"
+
+#~ msgid "Status Icons"
+#~ msgstr "أيقونات الحالة"
+
+#~ msgid "GNOME Shell Extension Preferences"
+#~ msgstr "تفضيلات امتدادات صدفة جنوم"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "إعادة توجيه استيثاق عبر الوب"
+
+#~ msgid "Events"
+#~ msgstr "الأحداث"
+
+#~ msgid "Notifications"
+#~ msgstr "التنبيهات"
+
+#~ msgid "Clear section"
+#~ msgstr "امسح القسم"
+
+#~ msgid "Media"
+#~ msgstr "وسائط"
+
+#~ msgid "Not In Use"
+#~ msgstr "غير مستخدم"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d × %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "اعرض تاريخ الأسبوع في التقويم"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "استخدمه اتّصالًا بالإنترنت"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "يطلب %s صلاحية معرفة مكانك."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "صدفة جنوم (وايلاند)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "لا أجهزة موصّلة"
+#~ msgstr[1] "جهاز واحد موصّل"
+#~ msgstr[2] "جهازين موصّلين"
+#~ msgstr[3] "%d أجهزة موصّلة"
+#~ msgstr[4] "%d جهازًا موصّلا"
+#~ msgstr[5] "%d جهاز موصّل"
+
+#~ msgid "Authentication required"
+#~ msgstr "الاستيثاق مطلوب"
+
+#~ msgid "UPS"
+#~ msgstr "مزود طاقة لا منقطعة (UPS)"
+
+#~ msgid "Battery"
+#~ msgstr "البطارية"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "وضع الطائرة"
+
+#~ msgid "Show the message tray"
+#~ msgstr "أظهر لوحة الرسائل"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "الأحد"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "الاثنين"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "الثلاثاء"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "الأربعاء"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "الخميس"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "الجمعة"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "السبت"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "الجدول خال"
+
+#~ msgid "Today"
+#~ msgstr "اليوم"
+
+#~ msgid "Tomorrow"
+#~ msgstr "غدا"
+
+#~ msgid "This week"
+#~ msgstr "هذا الأسبوع"
+
+#~ msgid "Next week"
+#~ msgstr "الأسبوع القادم"
+
+#~ msgid "Removable Devices"
+#~ msgstr "الأجهزة المنفصلة"
+
+#~ msgid "Eject"
+#~ msgstr "أخرج"
+
+#~ msgid "Invitation"
+#~ msgstr "دعوة"
+
+#~ msgid "Call"
+#~ msgstr "اتصال"
+
+#~ msgid "File Transfer"
+#~ msgstr "نقل ملفات"
+
+#~ msgid "Chat"
+#~ msgstr "دردشة"
+
+#~ msgid "Unmute"
+#~ msgstr "أطلِق الصوت"
+
+#~ msgid "Mute"
+#~ msgstr "أصمِت"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "دعوة إلى %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "يدعوك %s للانضمام إلى %s"
+
+#~ msgid "Decline"
+#~ msgstr "أرفُض"
+
+#~ msgid "Accept"
+#~ msgstr "أقْبَل"
+
+#~ msgid "Video call from %s"
+#~ msgstr "مكالمة فيديو من %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "مكالمة من %s"
+
+#~ msgid "Answer"
+#~ msgstr "أجب"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "‏%s يرسل لك %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "يريد %s الإذن ليعرف عندما تكون متصلا"
+
+#~ msgid "Authentication failed"
+#~ msgstr "فشل الاستيثاق"
+
+#~ msgid "Encryption error"
+#~ msgstr "فشل التعمية"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "الشهادة غير متوفرة"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "الشهادة غير موثوقة"
+
+#~ msgid "Certificate expired"
+#~ msgstr "الشهادة منتهية الصلاحية"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "الشهادة غير مفعلة"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "الشهادة غير متوافقة مع اسم المستضيف"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "بصمة الشهادة غير متطابقة"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "الشهادة موقعة ذاتيا"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "الحالة مضبوطة إلى غير متصل"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "الشهادة غير صالحة"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "رُفض الاتصال"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "تعذر إنشاء الاتصال"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "فُقد الاتصال"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "هذا الحساب متصل بالخادوم بالفعل"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "تم تبديل الاتصال باتصال جديد باستخدام نفس المورد"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "الحساب موجود بالفعل في الخادوم"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "الخادوم مشغول للغاية حاليًا ولا يستطيع التعامل مع الاتصال"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "نُقِضت الشهادة"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "تستخدم الشهادة خوارزمية تعمية غير آمنة أو ضعيفة"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "طول شهادة الخادم أو طول سلسلة الشهادات إلى الخادم تجاوز الحد الذي تفرضه "
+#~ "مكتبة التعمية"
+
+#~ msgid "Internal error"
+#~ msgstr "خطأ داخلي"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "تعذّر الاتصال مع %s"
+
+#~ msgid "View account"
+#~ msgstr "أظهر الحساب"
+
+#~ msgid "Open Calendar"
+#~ msgstr "افتح التقويم"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "إعدادات الوقت و التّاريخ"
+
+#~ msgid "Open"
+#~ msgstr "افتح"
+
+#~ msgid "Remove"
+#~ msgstr "أزِل"
+
+#~ msgid "Clear Messages"
+#~ msgstr "امسح الرسائل"
+
+#~ msgid "Notification Settings"
+#~ msgstr "إعدادات التنبيهات"
+
+#~ msgid "Tray Menu"
+#~ msgstr "قائمة الصينية"
+
+#~ msgid "No Messages"
+#~ msgstr "لا رسائل"
+
+#~ msgid "Message Tray"
+#~ msgstr "لوحة الرسائل"
+
+#~ msgid "Extension"
+#~ msgstr "امتداد"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "اختر امتدادا لضبطه من القائمة أعلاه."
+
+#~ msgid "unavailable"
+#~ msgstr "غير متاح"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l:%M\\u2009%p"
+
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgid "%A, %H\\u2236%M"
+#~ msgstr "%A، %H\\u2236%M"
+
+#~ msgid "%B %d, %H\\u2236%M"
+#~ msgstr "%d %B، %H\\u2236%M"
+
+#~ msgid "%B %d %Y, %H\\u2236%M"
+#~ msgstr "%d %B %Y، %H\\u2236%M"
+
+#~ msgid "%l\\u2236%M %p"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgid "%A, %l\\u2236%M %p"
+#~ msgstr "%A، %l\\u2236%M %p"
+
+#~ msgid "%B %d, %l\\u2236%M %p"
+#~ msgstr "%d %B، %l\\u2236%M %p"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>، <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> <b>%B</b>، <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b> <b>%B</b> <b>%Y</b>، <b>%H:%M</b> "
+
+#~ msgid "Settings Menu"
+#~ msgstr "قائمة الإعدادات"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "طلب تخويل من %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "يريد الجهاز %s المزاوجة مع هذا الحاسوب"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "يريد الجهاز %s صلاحية الوصول للخدمة '%s'"
+
+#~ msgid "Grant this time only"
+#~ msgstr "امنح هذه المرة فقط"
+
+#~ msgid "Reject"
+#~ msgstr "ارفض"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "تأكيد مزاوجة %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "من فضلك أكد تطابق مفتاح السر '%06d' مع الموجود على الجهاز."
+
+#~ msgid "Matches"
+#~ msgstr "مطابق"
+
+#~ msgid "Does not match"
+#~ msgstr "غير مطابق"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "طلب مزاوجة من %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "من فضلك أدخل الرقم المذكور على الجهاز."
+
+#~ msgid "OK"
+#~ msgstr "حسنا"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "عذرًا, لا حكمة لك اليوم:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "يقول الحكيم %s"
+
+#~ msgid "Screenshots"
+#~ msgstr "لقطات الشاشة"
+
+#~ msgid "Record a screencast"
+#~ msgstr "سجّل فديو للشاشة"
+
+#~ msgid "Session"
+#~ msgstr "الجلسة"
+
+#~ msgid "Power"
+#~ msgstr "الطاقة"
+
+#~ msgid "Restart"
+#~ msgstr "أعِد التشغيل"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "تسجيل شاشة من %d %t"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "انقر على \"اخرج\" لغلق هذه التطبيقات والخروج من النظام."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "يخرج من النظام."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "انقر على \"أطفئ\" لغلق هذه التطبيقات وإطفاء النظام."
+
+#~ msgid "Powering off the system."
+#~ msgstr "يُطفأ النظام."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "انقر على \"أعِد التشغيل\" لغلق هذه التطبيقات وإعادة تشغيل النظام."
+
+#~ msgid "Restarting the system."
+#~ msgstr "يُعاد تشغيل النظام."
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "الصوت، الشبكة، البطارية"
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "إغلاق الحاسوب قد يتسبب في فقدانهم للعمل غير المحفوظ."
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "ما إذا كنت ترغب في تجميع الحالات عن استخدام التطبيقات"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "إعدادات الإتاحة"
+
+#~ msgid "Visibility"
+#~ msgstr "الرؤية"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "اضبط جهازًا جديدًا…"
+
+#~ msgid "Send Files…"
+#~ msgstr "أرسل ملفات…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "إعدادات لوحة المفاتيح"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "إعدادات الفأرة"
+
+#~ msgid "disabled"
+#~ msgstr "مُعَطَّل"
+
+#~ msgid "cable unplugged"
+#~ msgstr "الكبل مفصول"
+
+#~ msgid "More…"
+#~ msgstr "المزيد…"
+
+#~ msgid "Wired"
+#~ msgstr "سلكي"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "إيثرنت تلقائي"
+
+#~ msgid "Auto broadband"
+#~ msgstr "شبكة هاتف محمول تلقائية"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "اتصال هاتفي تلقائي"
+
+#~ msgid "Auto %s"
+#~ msgstr "‏%s تلقائي"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "بلوتوث تلقائي"
+
+#~ msgid "Auto wireless"
+#~ msgstr "لاسلكي تلقائي"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "واي فاي"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "بقي أقل من ساعة"
+#~ msgstr[1] "بقيت ساعة"
+#~ msgstr[2] "بقيت ساعتان"
+#~ msgstr[3] "بقيت %d ساعات"
+#~ msgstr[4] "بقيت %d ساعة"
+#~ msgstr[5] "بقيت %d ساعة"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "بقي %d %s و%d %s"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ساعة"
+#~ msgstr[1] "ساعة"
+#~ msgstr[2] "ساعتان"
+#~ msgstr[3] "ساعات"
+#~ msgstr[4] "ساعة"
+#~ msgstr[5] "ساعة"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "دقيقة"
+#~ msgstr[1] "دقيقة"
+#~ msgstr[2] "دقيقتان"
+#~ msgstr[3] "دقائق"
+#~ msgstr[4] "دقيقة"
+#~ msgstr[5] "دقيقة"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "بقي أقل من دقيقة"
+#~ msgstr[1] "بقيت دقيقة"
+#~ msgstr[2] "بقيت دقيقتان"
+#~ msgstr[3] "بقيت %d دقائق"
+#~ msgstr[4] "بقيت %d دقيقة"
+#~ msgstr[5] "بقيت %d دقيقة"
+
+#~ msgid "AC Adapter"
+#~ msgstr "مقبس طاقة"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "بطارية حاسوب"
+
+#~ msgid "Monitor"
+#~ msgstr "شاشة"
+
+#~ msgid "Mouse"
+#~ msgstr "فأرة"
+
+#~ msgid "PDA"
+#~ msgstr "مساعد رقمي (PDA)"
+
+#~ msgid "Cell Phone"
+#~ msgstr "هاتف محمول"
+
+#~ msgid "Media Player"
+#~ msgstr "مشغل وسائط"
+
+#~ msgid "Tablet"
+#~ msgstr "لوحي"
+
+#~ msgid "Computer"
+#~ msgstr "حاسوب"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "غير معروف"
+
+#~ msgid "Available"
+#~ msgstr "متفرّغ"
+
+#~ msgid "Busy"
+#~ msgstr "مشغول"
+
+#~ msgid "Invisible"
+#~ msgstr "خفي"
+
+#~ msgid "Away"
+#~ msgstr "غائب"
+
+#~ msgid "Idle"
+#~ msgstr "ساكن"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "ستُجعل حالة اتصالك ”مشغول“"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "التنبيهات معطلة الآن، بما فيها رسائل المحادثة. حالة اتصالك تغيرت حتى يعلم "
+#~ "الآخرون أنك قد لا ترى رسائلهم."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "ادخل"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "tray"
+#~ msgstr "لوحة النظام"
+
+#~ msgid "More..."
+#~ msgstr "المزيد..."
+
+#~ msgid "United Kingdom"
+#~ msgstr "المملكة المتحدة"
+
+#~ msgid "Default"
+#~ msgstr "المبدئي"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "التطبيقات"
+
+#~ msgid "SETTINGS"
+#~ msgstr "الإعدادات"
+
+#~ msgid "Subscription request"
+#~ msgstr "طلب اشتراك"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "أُرسلت يوم <b>%A</b> الساعة <b>%l:%M</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "أُرْسِلت يوم <b>%A</b>، <b>%d %B</b>، %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "فشل الاتصال ب‍ %s"
+
+#~ msgid "Reconnect"
+#~ msgstr "أعد الاتصال"
+
+#~ msgid "Browse Files..."
+#~ msgstr "تصفح الملفات..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "حدث عطل أثناء تصفّح الجهاز"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "تعذّر تصفح الجهاز، رسالة العطل '%s'"
+
+#~ msgid "Wireless"
+#~ msgstr "لاسلكي"
+
+#~ msgid "VPN Connections"
+#~ msgstr "اتصالات VPN"
+
+#~ msgid "System Settings"
+#~ msgstr "إعدادات النظام"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "فشل فصْل '%s'"
+
+#~ msgid "Retry"
+#~ msgstr "أعد المحاولة"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "الأماكن والأجهزة"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Connect to..."
+#~ msgstr "اتّصل ب‍..."
+
+#~ msgid "Passphrase"
+#~ msgstr "عبارة السر"
+
+#~ msgid "%s is online."
+#~ msgstr "‏%s متّصل."
+
+#~ msgid "%s is offline."
+#~ msgstr "‏%s غير متّصل."
+
+#~ msgid "%s is away."
+#~ msgstr "‏%s غائب."
+
+#~ msgid "%s is busy."
+#~ msgstr "‏%s مشغول."
+
+#~ msgctxt "contact"
+#~ msgid "Unknown"
+#~ msgstr "غير معروف"
+
+#~ msgid "CONTACTS"
+#~ msgstr "المتراسلون"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "اعرض الوقت بالثواني"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "إذا كان صحيحًا، ستعرض الثواني في الوقت."
+
+#~ msgid "Show date in clock"
+#~ msgstr "اعرض التاريخ في الساعة"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "إذا كان صحيحًا، سيعرض في الساعة التاريخ بالإضافة إلى الوقت."
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%A %e %B، %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%A %e %B، %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%A %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%A %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%A %e %B، %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%A %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "كلمة السرّ خاطئة، من فضلك أعد المحاولة"
+
+#~ msgid "Hidden"
+#~ msgstr "مخفي"
+
+#~ msgid "Power Off..."
+#~ msgstr "أطفئ..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "الحسابات المتصلة"
+
+#~ msgid "Log Out..."
+#~ msgstr "اخرج..."
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "العناصر الحديثة"
+
+#~ msgid "Localization Settings"
+#~ msgstr "إعدادات اللغة"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "صرت الآن متّصلا بشبكة الهاتف المحمول '%s'"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "صرت الآن متّصلا بالشبكة اللاسلكية '%s'"
+
+#~ msgid "You're now connected to wired network '%s'"
+#~ msgstr "صرت الآن متّصلا بالشبكة السلكية '%s'"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "صرت الآن متّصلا بشبكة ش‌خ‌ف '%s'"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "انتهى %s من البدء"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "منذ أقل من دقيقة"
+
+#~ msgid "Home Folder"
+#~ msgstr "مجلد المنزل"
+
+#~ msgid "Shut Down"
+#~ msgstr "أطفئ"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr "انقر على \"أطفئ\" لغلق هذه التطبيقات وإطفاء النظام."
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "سيُطفأ النظام تلقائيا خلال %d ثوان."
+
+#~ msgid "Shutting down the system."
+#~ msgstr "يُغلق النظام."
+
+#~ msgid "Confirm"
+#~ msgstr "أكّد"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "التفضيلات"
+
+#~ msgid "Shut Down..."
+#~ msgstr "أطفئ..."
+
+#~ msgid "Search your computer"
+#~ msgstr "ابحث في حاسوبك"
+
+#~ msgid "Bluetooth Agent"
+#~ msgstr "عميل بلوتوث"
+
+#~ msgid ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+#~ msgstr "تعذّر إضافة مساحة عمل جديدة، لتجاوز أقصى عدد من مساحات العمل."
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "لا يمكن حذف مساحة العمل الأولى."
+
+#~ msgid "Customize the panel clock"
+#~ msgstr "طوّع ساعة اللوحة"
+
+#~ msgid "Clock Preferences"
+#~ msgstr "تفضيلات الساعة"
+
+#~ msgid "_12 hour format"
+#~ msgstr "نسق _12 ساعة"
+
+#~ msgid "_24 hour format"
+#~ msgstr "نسق _24 ساعة"
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "اسحب إلى هنا ليضاف إلى المفضّلة"
+
+#~ msgid "Find"
+#~ msgstr "ابحث"
+
+#~ msgid "Preferences"
+#~ msgstr "التفضيلات"
+
+#~ msgid "System Preferences..."
+#~ msgstr "تفضيلات النظام..."
+
+#~ msgid "Recent Documents"
+#~ msgstr "المستندات الحديثة"
+
+#~ msgid "(see all)"
+#~ msgstr "(انظر الكل)"
+
+#~ msgid "PLACES"
+#~ msgstr "الأماكن"
+
+#~ msgid "SEARCH RESULTS"
+#~ msgstr "نتائج البحث"
+
+#~ msgid "Can't lock screen: %s"
+#~ msgstr "تعذّر إيصاد الشاشة: %s"
+
+#~ msgid "Can't temporarily set screensaver to blank screen: %s"
+#~ msgstr "تعذّر ضبك حافظة الشاشة مؤقتا لتكون شاشة خالية: %s"
+
+#~ msgid "Can't logout: %s"
+#~ msgstr "تعذّر الخروج: %s"
+
+#~ msgid "Sidebar"
+#~ msgstr "الشريط الجانبي"
+
+#~ msgid "Browse"
+#~ msgstr "استعرض"
diff --git a/po/as.po b/po/as.po
new file mode 100644
index 0000000..157499a
--- /dev/null
+++ b/po/as.po
@@ -0,0 +1,2396 @@
+# Assamese translation for gnome-shell.
+# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# ngoswami <ngoswami@redhat.com>, 2011.
+# Nilamdyuti Goswami <ngoswami@redhat.com>, 2011, 2012, 2013, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-09-15 07:36+0000\n"
+"PO-Revision-Date: 2014-09-15 14:59+0530\n"
+"Last-Translator: Nilamdyuti Goswami <ngoswami@redhat.com>\n"
+"Language-Team: Assamese <kde-i18n-doc@kde.org>\n"
+"Language: as\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "চিস্টেম"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "বাৰ্তা ট্ৰে দেখুৱাওক"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "সক্ৰিয় অধিসূচনাত ফকাচ কৰক"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "অভাৰভিউ দেখুৱাওক"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "সকলো এপ্লিকেচন দেখুৱাওক"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "এপ্লিকেচন মেনু খোলক"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME শ্বেল"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "উইন্ডো ব্যৱস্থাপনা আৰু এপ্লিকেচনৰ লঞ্চ"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME শ্বেল সম্প্ৰসাৰন পছন্দসমূহ"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME শ্বেল সম্প্ৰসাৰনসমূহ সংৰূপণ কৰক"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME শ্বেল (wayland compositor)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Alt-F2 ৰ পৰা উন্নয়নকাৰী আৰু পৰীক্ষকসমূহৰ কাৰণে লাভদায়ক অভ্যন্তৰীক সঁজুলিসমূহ "
+"সামৰ্থবান "
+"কৰক"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 ডাইলগ ব্যৱহাৰ কৰি অভ্যন্তৰীক ডিবাগিং আৰু মনিটৰিং সঁজুলিসমূহলে অভিগমৰ "
+"অনুমতি "
+"দিয়ে।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "সামৰ্থবান কৰিব লগিয়া সম্প্ৰসাৰণসমূহৰ UUIDs"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME শ্বেল সম্প্ৰসাৰণসমূহৰ এটা UUID বৈশিষ্ট্য আছে; এই কি'য়ে ল'ড হব লগিয়া "
+"সমপ্ৰসাৰণসমূহ তালিকাভুক্ত কৰে। যিকোনো সম্প্ৰসাৰন যি ল'ড হব বিচাৰে এই তালিকাত "
+"থাকিব "
+"লাগিব। আপুনি এই তালিকাক org.gnome.Shell ত EnableExtension আৰু "
+"DisableExtension "
+"D-Bus পদ্ধতিৰ সৈতে সলনি কৰিব পাৰিব।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "সম্প্ৰসাৰন সংস্কৰণ সংগতাৰ বৈধতাক অসামৰ্থবান কৰে"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME শ্বেলে কেৱল বৰ্তমান চলি থকা সংস্কৰণক সমৰ্থন কৰা সম্প্ৰসাৰনসমূহ ল'ড "
+"কৰিব। এই "
+"বিকল্প সামৰ্থবান কৰিলে এই নিৰীক্ষণ অসামৰ্থবান কৰা হব আৰু সিহতে সমৰ্থন কৰা "
+"সংস্কৰণসমূহৰ নিৰপেক্ষে সকলো সম্প্ৰসাৰন ল'ড কৰিব।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "পছন্দৰ এপ্লিকেচনসমূহৰ কাৰণে ডেস্কটপ ফাইল IDসমূহৰ তালিকা"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"এই পৰিচয়কসমূহৰ লগত আনুষংগিক এপ্লিকেচনসমূহ পছন্দৰ স্থানত প্ৰদৰ্শীত কৰা হব।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "এপ পিকাৰ দৰ্শন"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "এপ্লিকেচন পিকাৰত বৰ্তমানে নিৰ্বাচিত দৰ্শনৰ সূচী।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "কমান্ড (Alt-F2) ডাইলগৰ কাৰণে ইতিহাস"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "চোৱা কাঁচ ডাইলগৰ কাৰণে ইতিহাস"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "ব্যৱহাৰকাৰী মেনুত 'লগ আউট' মেনু আইটেম সদায় দেখুৱাব।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"এই কি'য়ে এটা-ব্যৱহাৰকাৰী, এটা-অধিবেশন অৱস্থাবোৰত 'লগ আউট' মেনুআইটেম "
+"স্বচালিতভাৱে "
+"লুকুৱা অভাৰৰাইড কৰে।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"ইনক্ৰিপ্টেড অথবা দূৰৱৰ্তী ফাইলচিস্টেমসমূহ মাউণ্ট কৰাৰ বাবে পাছৱৰ্ড মনত ৰখা হব "
+"নে"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"শ্বেলে এটা পাছৱৰ্ড অনুৰোধ কৰিব যেতিয়া এটা ইনক্ৰিপ্টেড ডিভাইচ অথবা এটা "
+"দূৰৱৰ্তী "
+"ফাইলচিস্টেম মাউণ্ট কৰা হয়। যদি পাছৱৰ্ডক ভৱিষ্যত ব্যৱহাৰৰ বাবে সংৰক্ষণ কৰিব "
+"পাৰি "
+"তেন্তে এটা 'পাছৱৰ্ড মনত ৰাখক' চেকবাকচ উপস্থিত থাকিব। এই কি'য়ে চেকবাকচৰ "
+"অবিকল্পিত "
+"অৱস্থা সংহতি কৰে।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "কেলেন্ডাৰত সপ্তাহৰ তাৰিখ দেখুৱাওক"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "যদি সত্য, ISO সপ্তাহ তাৰিখক কেলেণ্ডাৰত দেখুৱাওক।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "এপ্লিকেচন মেনু খোলিবলে Keybinding"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "এপ্লিকেচন মেনু খোলিবলে Keybinding।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"এপ্লিকেচনসমূহ দেখুৱাওক\" দৰ্শন খোলিবলে কি'বাইণ্ডিং"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"কাৰ্য্যসমূহ অভাৰভিউৰ \"এপ্লিকেচনসমূহ দেখুৱাওক\" দৰ্শন খোলিবলে কি'বাইণ্ডিং।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the overview"
+msgstr "অভাৰভিউ খোলিবলে কি'বাইণ্ডিং"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the Activities Overview."
+msgstr "কাৰ্য্যসমূহ অভাৰভিউ খোলিবলে কি'বাইণ্ডিং"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "বাৰ্তা ট্ৰেৰ দৃশ্যমানতা টগ'ল কৰিবলে Keybinding"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "বাৰ্তা ট্ৰেৰ দৃশ্যমানতা টগ'ল কৰিবলে Keybinding।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "সক্ৰিয় অধিসূচনা ফকাচ কৰিবলে কি'বাইণ্ডিং"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "সক্ৰিয় অধিসূচনা ফকাচ কৰিবলে কি'বাইণ্ডিং।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"কিবাইন্ডিং যি সকলো চলি থকা টুইনক বিৰাম দিয়ে আৰু চলায়, ডিবাগিং কাৰণসমূহৰ বাবে "
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "কোনটো কিবৰ্ড ব্যৱহাৰ কৰা হব"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "ব্যৱহাৰ কৰিব লগিয়া কিবৰ্ডৰ ধৰণ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "চুইচাৰক বৰ্তমান কাৰ্য্যস্থানলৈ সীমিত ৰাখক।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"যদি সত্য, কেৱল বৰ্তমান কাৰ্য্যস্থানক উইন্ডো থকা এপ্লিকেচনসমূক চুইচাৰত দেখুৱা "
+"হব। নহলে, "
+"সকলো এপ্লিকেচন অন্তৰ্ভুক্ত কৰা হয়।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "এপ্লিকেচন আইকন অৱস্থা।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"চুইচাৰত উইন্ডোসমূহ কেনেধৰণে দেখুৱা হয় সংৰূপণ কৰে। বৈধ সম্ভাবনাসমূহ হল "
+"'thumbnail-"
+"only' (উইন্ডোৰ এটা থাম্বনেইল দেখুৱায়), 'app-icon-only' (কেৱল এপ্লিকেচন আইকন "
+"দেখুৱায়) অথবা 'both'।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"যদি সত্য, কেৱল বৰ্তমান কাৰ্য্যস্থানৰ পৰা উইন্ডোসমূহক চুইচাৰত দেখুৱা হব। নহলে, "
+"সকলো "
+"উইন্ডো অন্তৰ্ভুক্ত কৰা হব।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "মডাল ডাইলগ উপধায়ক উইন্ডোৰ সৈতে সংলগ্ন কৰক"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "GNOME শ্বেল চলাওতে এই কি'য়ে org.gnome.mutter ত থকা কি' অভাৰৰাইড কৰে।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "উইন্ডোসমূহক পৰ্দাৰ প্ৰান্তসমূহত এৰোতে প্ৰান্ত টাইলিং সামৰ্থবান কৰক"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "কাৰ্য্যস্থানসমূহক চলমানভাৱে ব্যৱস্থাপনা কৰা হয়"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "কাৰ্য্যস্থানসমূহ কেৱল প্ৰাথমিক মনিটৰত"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"বিলম্বৰ ফকাচ মাউছ অৱস্থাত পৰিবৰ্তন হয় যেতিয়ালৈকে পইন্টাৰে গমন কৰা বন্ধ নকৰে"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "কেপটিভ পৰ্টেল"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s ৰ বাবে পছন্দসমূহ ডাইলগ ল'ড কৰোতে এটা ত্ৰুটি হৈছিল:"
+
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extensions"
+msgstr "GNOME শ্বেল সম্প্ৰসাৰনসমূহ"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "বাতিল কৰক"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "পৰৱৰ্তী"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "আনলক কৰক"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "ছাইন ইন কৰক"
+
+#: ../js/gdm/loginDialog.js:269
+msgid "Choose Session"
+msgstr "অধিবেশন বাছক"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "তালিকাভুক্ত নহয়?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(উদাহৰণ, ব্যৱহাৰকাৰী অথবা %s)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "ব্যৱহাৰকাৰীৰ নাম: "
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "লগিন উইন্ডো"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "প্ৰমাণীকৰণ ত্ৰুটি"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(অথবা আঙুলি স্বাইপ কৰক)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "কমান্ড পোৱা নগল"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "কমান্ড বিশ্লেষন কৰিব নোৱাৰি:"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” ৰ প্ৰেৰণ ব্যৰ্থ হল:"
+
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "ৱেব প্ৰমাণীকৰণৰ পুনৰনিৰ্দেশ"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "সঘনে ব্যৱহাৰ কৰা এপ্লিকেচনসমূহ ইয়াত উপস্থিত হব"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "সঘন"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "সকলো"
+
+#: ../js/ui/appDisplay.js:1789
+msgid "New Window"
+msgstr "নতুন উইন্ডো"
+
+#: ../js/ui/appDisplay.js:1815 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "পছন্দৰ পৰা আতৰাওক"
+
+#: ../js/ui/appDisplay.js:1821
+msgid "Add to Favorites"
+msgstr "পছন্দলে যোগ কৰক"
+
+#: ../js/ui/appDisplay.js:1830
+msgid "Show Details"
+msgstr "বিৱৰণসমূহ দেখুৱাওক"
+
+#: ../js/ui/appFavorites.js:124
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s ক আপোনাৰ পছন্দলে যোগ কৰা হৈছে।"
+
+#: ../js/ui/appFavorites.js:158
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s ক আপোনাৰ পছন্দৰ পৰা আতৰোৱা হৈছে।"
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "সংহতিসমূহ"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "পটভূমি পৰিবৰ্তন কৰক…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "গোটেই দিনটো"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Su"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Th"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "যোৱা মাহ"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "অহা মাহ"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "একো অনুসূচীত কৰা হোৱা নাই"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "আজি"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "কালি"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "এই সপ্তাহ"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "অহা সপ্তাহ"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "বহিৰ্তম ড্ৰাইভ সংযোগ কৰা হৈছে"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "বহিৰ্তম ড্ৰাইভ বিচ্ছিন্ন কৰা হৈছে"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "আতৰাব পৰা ডিভাইচসমূহ"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ৰ সৈতে খোলক"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "উলিৱাওক"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "পাছৱৰ্ড:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "আকৌ টাইপ কৰক:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "সংযোগ কৰক"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "পাছৱৰ্ড: "
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "কি': "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "পৰিচয়: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "ব্যক্তিগত কি' পাছৱৰ্ড: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "সেৱা: "
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "বেতাঁৰ নেটৱৰ্কৰ দ্বাৰা প্ৰমাণীকৰণৰ প্ৰয়োজন"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"বেতাঁৰ নেটৱৰ্ক “%s” অভিগম কৰিবলে পাছৱৰ্ডসমূহ অথবা ইনক্ৰিপষণ কি'সমূহৰ প্ৰয়োজন।"
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "তাঁৰযুক্ত 802.1X প্ৰমাণীকৰণ"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "নেটৱৰ্কৰ নাম: "
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "DSL প্ৰমাণীকৰণ"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "PIN ক'ডৰ প্ৰয়োজন"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "মবাইল ব্ৰডবেণ্ড সেৱাৰ বাবে PIN ক'ডৰ প্ৰয়োজন"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "মবাইল ব্ৰডবেণ্ড নেটৱৰ্ক পাছৱৰ্ড"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” লে সংযোগ কৰিবলে এটা পাছৱৰ্ডৰ প্ৰয়োজন।"
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "প্ৰমাণীকৰণৰ প্ৰয়োজন"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "প্ৰশাসক"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "প্ৰমাণীত কৰক"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "ক্ষমা কৰিব, সেয়া কাম নকৰিলে। অনুগ্ৰহ কৰি পুনৰ চেষ্টা কৰক।"
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "নিমন্ত্ৰণ"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "কল"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "ফাইল স্থানান্তৰ"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "চেট"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "অমৌন কৰক"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "মোন কৰক"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+msgid "Yesterday, %H∶%M"
+msgstr "যোৱাকালী, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "যোৱাকালী, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s এতিয়া %s হিচাপে জনাজাত"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%s লে নিমন্ত্ৰণ"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s এ আপোনাক %s ত অংশগ্ৰহণ কৰিবলে আমন্ত্ৰণ জনাইছে"
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "নাকচ কৰক"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "গ্ৰহন কৰক"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s ৰ পৰা ভিডিঅ' কল"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s ৰ পৰা কল"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "উত্তৰ দিয়ক"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s এ আপোনাক %s পঠাই আছে"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "আপুনি কেতিয়া অনলাইন আছে চাবলে %s এ অনুমতি বিচাৰিব"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "নেটৱৰ্ক ত্ৰুটি"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "প্ৰমাণীকৰণ ব্যৰ্থ"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "ইনক্ৰিপষণ ত্ৰুটি"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "প্ৰমাণপত্ৰ প্ৰদান কৰা হোৱা নাই"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "প্ৰমাণপত্ৰক ভৰষা কৰিব নোৱাৰি"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "প্ৰমাণপত্ৰৰ অৱসান ঘটিছে"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "প্ৰমাণপত্ৰ সক্ৰিয় কৰা হোৱা নাই"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "প্ৰমাণপত্ৰ হস্টনাম অমিল"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "প্ৰমাণপত্ৰ ফিংগাৰপ্ৰিণ্ট অমিল"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "প্ৰমাণপত্ৰ স্ব-স্বাক্ষৰীত"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "অৱস্থা অফলাইনলে সংহতি কৰা হৈছে"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "ইনক্ৰিপষণ উপলব্ধ নহয়"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "প্ৰমাণপত্ৰ অবৈধ"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "সংযোগ নাকচ কৰা হৈছে"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "সংযোগ স্থাপন কৰিব নোৱাৰি"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "সংযোগ হেৰাইছে"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "এই একাওণ্ট ইতিমধ্যে চাৰ্ভাৰৰ সৈতে সংযোগিত"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "সংযোগক একে সম্পদ ব্যৱহাৰ কৰি এটা নতুন সংযোগৰে প্ৰতিস্থাপন কৰা হৈছে"
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "একাওণ্ট ইতিমধ্যে চাৰ্ভাৰত উপস্থিত"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "চাৰ্ভাৰ সংযোগ ব্যৱস্থাপনা কৰিবলে বৰ্তমানে অতি ব্যস্ত"
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "প্ৰমাণপত্ৰ প্ৰত্যাহাৰ কৰা হৈছে"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"প্ৰমাণপত্ৰয় এটা অসুৰক্ষিত চিফাৰ এলগৰিথম ব্যৱহাৰ কৰে অথবা ক্ৰিপ্টোগ্ৰাফিয়ভাৱে "
+"দুৰ্বল"
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"চাৰ্ভাৰ প্ৰমাণপত্ৰৰ দৈৰ্ঘ, অথবা চাৰ্ভাৰ প্ৰমাণপত্ৰ শৃংখলৰ গভীৰতা, "
+"ক্ৰিপ্টোগ্ৰাফী "
+"লাইব্ৰেৰীয়ে প্ৰণয়ন কৰা সীমাসমূহত অতিক্ৰম কৰে"
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "অভ্যন্তৰীক ত্ৰুটি"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s ৰ সৈতে সংযোগ কৰিবলে অক্ষম"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "একাওণ্ট দৰ্শন কৰক"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "অজ্ঞাত কাৰণ"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "উইন্ডোসমূহ"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "এপ্লিকেচনসমূহ দেখুৱাওক"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "ডেশ"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "কেলেন্ডাৰ খোলক"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "ঘড়ীবোৰ খোলক"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "তাৰিখ আৰু সময সংহতিসমূহ"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s ক লগ আউট কৰক"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "লগ আউট কৰক"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s স্বচালিতভাৱে %d ছেকেণ্ডৰ পিছত লগ আউট হৈ যাব।"
+msgstr[1] "%s স্বচালিতভাৱে %d ছেকেণ্ডৰ পিছত লগ আউট হৈ যাব।"
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "আপুনি স্বচালিতভাৱে %d ছেকেণ্ড পিছত লগ আউট হৈ যাব।"
+msgstr[1] "আপুনি স্বচালিতভাৱে %d ছেকেণ্ড পিছত লগ আউট হৈ যাব।"
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "লগ আউট কৰক"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "বন্ধ কৰক"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "আপডেইটসমূহ ইনস্টল কৰি চিস্টেম বন্ধ কৰক"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "চিস্টেম %d ছেকেণ্ড পিছত স্বচালিতভাৱে বন্ধ কৰক হব। "
+msgstr[1] "চিস্টেম %d ছেকেণ্ডৰ পিছত স্বচালিতভাৱে বন্ধ হব। "
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "বাকি থকা চফ্টৱেৰ আপডেইটসমূহ ইনস্টল কৰক"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "পুনৰাম্ভ কৰক"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "বন্ধ কৰক"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "পুনৰাম্ভ কৰক"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "চিস্টেম %d ছেকেণ্ডত স্বচালিতভাৱে পুনৰাম্ভ হব।"
+msgstr[1] "চিস্টেম %d ছেকেণ্ডত স্বচালিতভাৱে পুনৰাম্ভ হব।"
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "পুনাৰম্ভ কৰক আৰু আপডেইটসমূহ ইনস্টল কৰক"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"চিস্টেম %d ছেকেণ্ডত স্বচালিতভাৱে পুনৰাম্ভ হব আৰু আপডেইটসমূহ ইনস্টল কৰিব।"
+msgstr[1] ""
+"চিস্টেম %d ছেকেণ্ডত স্বচালিতভাৱে পুনৰাম্ভ হব আৰু আপডেইটসমূহ ইনস্টল কৰিব।"
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "পুনৰাম্ভ কৰক &amp; ইনস্টল কৰক"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ইনস্টল কৰক &amp; চিস্টেম বন্ধ কৰক"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "আপডেইটসমূহ ইনস্টল কৰাৰ পিছত চিস্টেম বন্ধ কৰক"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"বেটাৰি শক্তিত চলি আছে: অনুগ্ৰহ কৰি আপডেইটসমূহ ইনস্টল কৰাৰ আগত চাৰ্জাৰ লগাওক।"
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr ""
+"কিছুমান এপ্লিকেচন ব্যস্ত হব পাৰে অথবা সিহতৰ কিছুমান অসংৰক্ষিত কাৰ্য্য থাকিব "
+"পাৰে।"
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "অন্য ব্যৱহাৰকাৰীসকল লগ্ড ইন আছে।"
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (দূৰৱৰ্তী)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (কনচৌল)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "ইনস্টল কৰক"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.org ৰ পৰা “%s” ক ডাউনল'ড আৰু ইনস্টল কৰিব নে?"
+
+#: ../js/ui/keyboard.js:692 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "কিবৰ্ড"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "কোনো সম্প্ৰসাৰন ইনস্টল কৰা হোৱা নাই"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s এ কোনো ত্ৰুটি প্ৰেৰণ কৰা নাই।"
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "ত্ৰুটিসমূহ লুকুৱাওক"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "ত্ৰুটিসমূহ দেখুৱাওক"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "সামৰ্থবান কৰা আছে"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "অসামৰ্থবান কৰা আছে"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "ত্ৰুটি"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "পুৰনি"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "ডাউনল'ড কৰা হৈছে"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "উৎস দৰ্শন কৰক"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "ৱেব পৃষ্ঠা"
+
+#: ../js/ui/messageTray.js:1326
+msgid "Open"
+msgstr "খোলক"
+
+#: ../js/ui/messageTray.js:1333
+msgid "Remove"
+msgstr "আতৰাওক"
+
+#: ../js/ui/messageTray.js:1630
+msgid "Notifications"
+msgstr "অধিসূচনাসমূহ"
+
+#: ../js/ui/messageTray.js:1637
+msgid "Clear Messages"
+msgstr "বাৰ্তাসমূহ পৰিষ্কাৰ কৰক"
+
+#: ../js/ui/messageTray.js:1656
+msgid "Notification Settings"
+msgstr "অধিসূচনা সংহতিসমূহ"
+
+#: ../js/ui/messageTray.js:1709
+msgid "Tray Menu"
+msgstr "ট্ৰে মেনু"
+
+#: ../js/ui/messageTray.js:1926
+msgid "No Messages"
+msgstr "কোনো বাৰ্তা নাই"
+
+#: ../js/ui/messageTray.js:1968
+msgid "Message Tray"
+msgstr "বাৰ্তা ট্ৰে"
+
+#: ../js/ui/messageTray.js:2971
+msgid "System Information"
+msgstr "চিস্টেম তথ্য"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "অজ্ঞাত"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d নতুন বাৰ্তা"
+msgstr[1] "%d নতুন বাৰ্তাসমূহ"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "কাৰ্য্য বাতিল কৰক"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "অভাৰভিউ"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "সন্ধান কৰিবলে টাইপ কৰক…"
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "প্ৰস্থান কৰক"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "কাৰ্য্যসমূহ"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "উপৰৰ বাৰ"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "এটা কমান্ড সুমুৱাওক"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "বন্ধ কৰক"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "পুনাৰম্ভ কৰা হৈছে…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d নতুন অধিসূচনা"
+msgstr[1] "%d নতুন অধিসূচনাসমূহ"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "লক কৰক"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME এ পৰ্দা লক কৰিব লাগিব"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "লক কৰিবলে অক্ষম"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "লক কাৰ্য্য এটা এপ্লিকেচন দ্বাৰা প্ৰতিৰোধ কৰা হৈছিল"
+
+#: ../js/ui/search.js:594
+msgid "Searching…"
+msgstr "সন্ধান কৰা হৈছে…"
+
+#: ../js/ui/search.js:596
+msgid "No results."
+msgstr "কোনো ফলাফল নাই।"
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "কপি কৰক"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "পেইস্ট কৰক"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "লিখনি দেখুৱাওক"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "লিখনি লুকুৱাওক"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "পাছৱৰ্ড"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "পাছৱৰ্ড মনত ৰাখক"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "অভিগম্যতা"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "জুম কৰক"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "পৰ্দা ৰিডাৰ"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "পৰ্দা কিবৰ্ড"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "চক্ষ সতৰ্কবাৰ্তাসমূহ"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "স্টিকি কি'সমূহ"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "মন্থৰ কি'সমূহ"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "বাউঞ্চ কি'সমূহ"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "মাউছ কি'সমূহ"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "উচ্চ কন্ট্ৰাস্ট"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "ডাঙৰ লিখনি"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "ব্লুটুথ"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "বন্ধ কৰক"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "ব্লুটুথ সংহতিসমূহ"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d সংযুক্ত ডিভাইচ"
+msgstr[1] "%d সংযুক্ত ডিভাইচসমূহ"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+msgid "Not Connected"
+msgstr "সংযুক্ত নহয়"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "উজ্জ্বলতা"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "কিবৰ্ড বিন্যাস দেখুৱাওক"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "অবস্থান"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "অসামৰ্থবান কৰক"
+
+#: ../js/ui/status/location.js:73
+#| msgid "Power Settings"
+msgid "Privacy Settings"
+msgstr "গোপনীয়তা সংহতিসমূহ"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "ব্যৱহৃত"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "সামৰ্থবান কৰক"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<unknown>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+msgid "Off"
+msgstr "অফ"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "সংযুক্ত"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "অব্যৱস্থাপিত"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "বিচ্ছিনিত কৰা হৈছে"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+msgid "Connecting"
+msgstr "সংযোগ কৰা হৈছে"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "প্ৰমাণীকৰণৰ প্ৰয়োজন"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "ফাৰ্মৱেৰ সন্ধানহীন"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "উপলব্ধ নাই"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "সংযোগ ব্যৰ্থ"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "তাঁৰযুক্ত সংহতিসমূহ"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "মবাইল ব্ৰডবেণ্ড সংহতিসমূহ"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+msgid "Hardware Disabled"
+msgstr "হাৰ্ডৱেৰ অসামৰ্থবান কৰা আছে"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "ইন্টাৰনেট সংযোগ ৰূপে ব্যৱহাৰ কৰক"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "বিমান অৱস্থা অন আছে"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "বিমান অৱস্থা অন থকা অৱস্থাত Wi-Fi অসামৰ্থবান থাকে।"
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "বিমান অৱস্থা বন্ধ কৰক"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi অফ আছে"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "এটা নেটৱৰ্কৰ সৈতে সংযোগ কৰিবলে Wi-Fi অন কৰিব লাগিব।"
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi অন কৰক"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi নেটৱৰ্কসমূহ"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "এটা নেটৱৰ্ক বাছক"
+
+#: ../js/ui/status/network.js:882
+msgid "No Networks"
+msgstr "কোনো নেটৱৰ্ক নাই"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "বন্ধ কৰিবলে হাৰ্ডৱেৰ চুইচ ব্যৱহাৰ কৰক"
+
+#: ../js/ui/status/network.js:1173
+msgid "Select Network"
+msgstr "নেটৱৰ্ক বাছক"
+
+#: ../js/ui/status/network.js:1179
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi সংহতিসমূহ"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "অন কৰক"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "হটস্পট সক্ৰিয়"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "সংযোগ কৰা হৈছে..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "প্ৰমাণীকৰণৰ প্ৰয়োজন"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "সংযোগ ব্যৰ্থ"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "নেটৱৰ্ক সংহতিসমূহ"
+
+#: ../js/ui/status/network.js:1482
+msgid "VPN Settings"
+msgstr "VPN সংহতিসমূহ"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "নেটৱৰ্ক ব্যৱস্থাপক"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "নেটৱৰ্ক সংযোগ সক্ৰিয়কৰণ ব্যৰ্থ হল"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "শক্তি সংহতিসমূহ"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "সম্পূৰ্ণভাৱে চাৰ্জড্"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "অনুমান কৰা হৈছে…"
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d অৱশিষ্ট (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d সম্পূৰ্ণ হোৱালৈ (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "বেটাৰি"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "বিমান অৱস্থা"
+
+#: ../js/ui/status/rfkill.js:85
+msgid "On"
+msgstr "অন"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "ব্যৱহাৰকাৰী পৰিবৰ্তন কৰক"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "লগ আউট কৰক"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "দিশনিৰ্ণয় লক"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "বাতিল কৰক"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "বন্ধ কৰক"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "ভলিউম পৰিবৰ্তন কৰা হৈছে"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "ভলিউম"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "মাইক্ৰোফোন"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "অন্য ব্যৱহাৰকাৰী হিচাপে লগিন কৰক"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "উইন্ডো আনলক কৰক"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "এপ্লিকেচনসমূহ"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "সন্ধান কৰক"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” প্ৰস্তুত"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "আপুনি এই প্ৰদৰ্শন সংহতিসমূহ ৰাখিব বিচাৰে নে?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "সংহতিসমূহক পূৰ্ব অৱস্থালৈ নিয়ক"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "পৰিৱৰ্তনসমূহ ৰাখক"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "সংহতিসমূহৰ পৰিবৰ্তন %d ছেকেণ্ডত পূৰ্ব অৱস্থালৈ যাব"
+msgstr[1] "সংহতিসমূহৰ পৰিবৰ্তন %d ছেকেণ্ডত পূৰ্ব অৱস্থালৈ যাব"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "সৰু কৰক"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "স্বাভাৱিক আকাৰ দিয়ক"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "ডাঙৰ কৰক"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "স্থানান্তৰ কৰক"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "পুনৰআকাৰ দিয়ক"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "অনস্ক্ৰিন শীৰ্ষকবাৰক স্থানান্তৰ কৰক"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "সদায় ওপৰত"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "সদায় দৃশ্যমান কাৰ্য্যস্থানত"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "ওপৰৰ কাৰ্য্যস্থানলৈ স্থানান্তৰ কৰক"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "তলৰ কাৰ্য্যস্থানলৈ স্থানান্তৰ কৰক"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution কেলেন্ডাৰ"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u আউটপুট"
+msgstr[1] "%u আউটপুটসমূহ"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ইনপুট"
+msgstr[1] "%u ইনপুটসমূহ"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "চিস্টেম শব্দসমূহ"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "প্ৰিণ্ট সংস্কৰণ"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "লগিন স্ক্ৰিনৰ বাবে GDM দ্বাৰা ব্যৱহাৰ কৰা অৱস্থা"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "লগিন পৰ্দাৰ বাবে এটা বিশেষ অৱস্থা, উদাহৰণস্বৰূপ \"gdm\" ব্যৱহাৰ কৰক"
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "সম্ভাব্য অৱস্থাসমূহ তালিকাভুক্ত কৰক"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” লঞ্চ কৰিবলে ব্যৰ্থ"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "পাছৱৰ্ডসমূহ মিল নাখায়।"
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "পাছৱৰ্ড ৰিক্ত হব নোৱাৰিব"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "প্ৰমাণীকৰণ ডাইলগ ব্যৱহাৰকাৰী দ্বাৰা নাকচ কৰা হৈছিল"
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "শীৰ্ষকবাৰত বুটামসমূহ সজোঁৱা"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "GNOME শ্বেল চলাওতে এই কি'য়ে org.gnome.desktop.wm.preferences ত থকা কি' "
+#~ "অভাৰৰাইড কৰে।"
+
+#~ msgid "Extension"
+#~ msgstr "প্ৰসাৰন"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "উপৰত দিয়া কম্বোবাকচ ব্যৱহাৰ কৰি সংৰূপণ কৰিবলে এটা সম্প্ৰসাৰন বাছক।"
+
+#~ msgid "calendar:MY"
+#~ msgstr "কেলেন্ডাৰ:MY"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "unavailable"
+#~ msgstr "উপলব্ধ নাই"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "ফোল্ডাৰ হিচাপে প্ৰদৰ্শন কৰিব লগিয়া বিভাগসমূহ তালিকাৰ"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "এই তালিকাৰ প্ৰতিটো বিভাগ নামক এপ্লিকেচন দৰ্শনত ফোল্ডাৰ হিচাপে প্ৰতিনিদ্ধিত্ব কৰা "
+#~ "হব, মূখ্য দৰ্শনত ইনলাইন হিচাপে প্ৰদৰ্শন কৰাৰ পৰিৱৰ্তে।"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s ৰ পৰা প্ৰমাণীকৰণ অনুৰোধ"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "ডিভাইচ %s এ এই কমপিউটাৰৰ লগত সংযোগ কৰিব বিচাৰে"
+
+#~ msgid "Allow"
+#~ msgstr "অনুমতি দিয়ক"
+
+#~ msgid "Deny"
+#~ msgstr "নাকচ কৰক"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "ডিভাইচ %s এ সেৱা '%s' লে অভিগম বিচাৰে"
+
+#~ msgid "Always grant access"
+#~ msgstr "সদায় অভিগম প্ৰদান কৰক"
+
+#~ msgid "Grant this time only"
+#~ msgstr "কেৱল এইবাৰৰ কাৰণে প্ৰদান কৰক"
+
+#~ msgid "Reject"
+#~ msgstr "নাকচ কৰক"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s ৰ কাৰণে যোৰ প্ৰতিশ্ৰুতি"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "অনুগ্ৰহ কৰি সুনিশ্চিত কৰক যে পাছকি '%06d' ডিভাইচত থকাটোৰ সৈতে মিল খায়।"
+
+#~ msgid "Matches"
+#~ msgstr "মিলসমূহ"
+
+#~ msgid "Does not match"
+#~ msgstr "মিল নাখায়"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s ৰ কাৰণে যোৰ অনুৰোধ"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "অনুগ্ৰহ কৰি ডিভাইচত উল্লেখ কৰা PIN সুমুৱাওক।"
+
+#~ msgid "OK"
+#~ msgstr "ঠিক আছে"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "ক্ষমা কৰিব, আপোনাৰ বাবে আজি কোনো জ্ঞান নাই:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s Oracle এ ক'য়"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Settings Menu"
+#~ msgstr "সংহতিসমূহ মেনু"
+
+#~ msgid "Screenshots"
+#~ msgstr "স্ক্ৰিনশ্বটসমূহ"
+
+#~ msgid "Record a screencast"
+#~ msgstr "এটা স্ক্ৰিনশ্বট ৰেকৰ্ড কৰক"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "এপ্লিকেচন ব্যৱহাৰ বিষয়ে পৰিসংখ্যা সংগ্ৰহ কৰা হব নে"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "শ্বেলে সাধাৰণত সক্ৰিয় এপ্লিকেচনসমূহ মনিটৰ কৰে যাতে সকলোতকৈ অধিক ব্যৱহৃতসমূহ "
+#~ "(উদাহৰনস্বৰুপে লঞ্চাৰসমূহত) ক দেখুৱাব পাৰে। যত এই তথ্য গোপন ৰখা হব, আপুনি ইয়াক "
+#~ "গোপনীয়তা কাৰণসমূহৰ কাৰণে অসামৰ্থবান কৰিব পাৰে। অনুগ্ৰহ কৰি মন কৰিব যে এনেকুৱা "
+#~ "কৰাত ইতিমধ্যে সংৰক্ষীত তথ্য আতৰি নাযায়।"
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "ব্যৱহাৰকাৰী দ্বাৰা স্পষ্টভাৱে সংহতি কৰা সৰ্বশেষ IM উপস্থিতি সংৰক্ষণ কৰাৰ বাবে "
+#~ "অভ্যন্তৰীয় বাবে ব্যৱহৃত। ইয়াত থকা মান TpConnectionPresenceType ইনুমাৰেষণৰ পৰা।"
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "ব্যৱহাৰকাৰীৰ বাবে সৰ্বশেষ অধিবেশন উপস্থিতি অৱস্থা সংৰক্ষণ কৰাৰ বাবে "
+#~ "অভ্যন্তৰীয়ভাৱে ব্যৱহৃত। ইয়াত থকা মান GsmPresenceStatus ইনুমাৰেষণৰ পৰা উপলব্ধ।"
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "পৰ্দা ৰেকৰ্ডাৰ অদল বদল কৰিবলে Keybinding"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "বিল্টইন পৰ্দা ৰেকৰ্ডাৰ আৰম্ভ/বন্ধ কৰিবলে Keybinding।"
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "screencasts ৰেকৰ্ড কৰাৰ কাৰণে ব্যৱহৃত Framerate"
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "GNOME শ্বেলৰ screencast ৰেকৰ্ডাৰে ফ্ৰেইম-প্ৰতি ছেকেণ্ডত ৰেকৰ্ড কৰা পৰিণাম "
+#~ "screencast ৰ framerate।"
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "screencast এনক'ড কৰিবলে ব্যৱহাৰ কৰা gstreamer পাইপলাইন"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "ৰেকৰ্ডিংসমূহ এনক'ড কৰোতে ব্যৱহৃত GStreamer পাইপলাইন সংহতি কৰে। ই gst-launch ৰ "
+#~ "কাৰণে ব্যৱহৃত বাক্যবিন্যাস অনুকৰন কৰে। পাইপলাইনৰ এটা অসংযোগিত চিঙ্ক পেড থাকিব "
+#~ "লাগে যত ৰেকৰ্ড কৰা ভিডিঅ' ৰেকৰ্ড কৰা হয়। ইয়াৰ সাধাৰণত এটা অসংযোগিত উৎস পেড "
+#~ "থাকিব; সেই পেডৰ পৰা আউটপুট এটা আউটপুট ফাইললে লিখা হব। যি কি নহওক পাইপলাইনে "
+#~ "নিজৰ আউটপুটৰ যত্ন লব পাৰে - ইয়াৰ হওতো আউটপুটক shout2send অথবা সমসাময়িকৰে এটা "
+#~ "icecast চাৰ্ভাৰত পঠাবলে ব্যৱহাৰ হব পাৰে। যেতিয়া এটা ৰিক্ত মানলে অসংহিত বা "
+#~ "সংহিত, অবিকল্পিত পাইপলাইন ব্যৱহাৰ কৰা হব। এইটো বৰ্তমানত 'vp8enc "
+#~ "min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads="
+#~ "%T ! queue ! webmmux' আৰু VP8 কডেক ব্যৱহাৰ কৰি WEBM ত ৰেকৰ্ড কৰে। %T ক "
+#~ "চিস্টেমত অনুকূলিত থ্ৰেড কাওণ্টত এটা অনুমানৰ প্লেইচহল্ডাৰ হিচাপে ব্যৱহাৰ কৰা হয়।"
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "screencast সংৰক্ষণ কৰাৰ কাৰণে লথিপত্ৰ সম্প্ৰসাৰন"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "ৰেকৰ্ড কৰা screencasts ৰ কাৰণে ফাইল নাম বৰ্তমান তাৰিখৰ উপৰত ভিত্তি কৰি এটা "
+#~ "অবিকল্প ফাইল নাম হব, আৰু এই সম্প্ৰসাৰন ব্যৱহাৰ কৰিব। ইয়াক এটা ভিন্ন অন্তৰ্ভুক্তক "
+#~ "বিন্যাসত ৰেকৰ্ড কৰোতে পৰিবৰ্তন কৰিব লাগিব।"
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "অধিবেশন…"
+
+#~ msgid "Power"
+#~ msgstr "শক্তি"
+
+#~ msgid "Restart"
+#~ msgstr "পুনৰাম্ভ কৰক"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d %t ৰ পৰা স্ক্ৰিনকাস্ট দেখুৱাওক"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "এই এপ্লিকেচনসমূহ প্ৰস্থান কৰি চিস্টেমৰ পৰা লগ আউট হবলে লগ আউট ক্লিক কৰক।"
+
+#~ msgid "Logging out of the system."
+#~ msgstr "চিস্টেমৰ পৰা লগ আউট কৰা হৈছে।"
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "এই এপ্লিকেচনসমূহক প্ৰস্থান কৰি চিস্টেম বন্ধ কৰিবলে বন্ধ কৰক ক্লিক কৰক।"
+
+#~ msgid "Powering off the system."
+#~ msgstr "চিস্টেম বন্ধ কৰা হৈছে।"
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "এই এপ্লিকেচনসমূহক প্ৰস্থান কৰি চিস্টেম পুনৰাম্ভ কৰিবলে পুনৰাম্ভ ক্লিক কৰক।"
+
+#~ msgid "Restarting the system."
+#~ msgstr "চিস্টেম পুনৰাম্ভ কৰা হৈছে।"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "সাৰ্বভৈমক অভিগম সংহতিসমূহ"
+
+#~ msgid "Visibility"
+#~ msgstr "দৃশ্যমানতা"
+
+#~| msgid "Send Files to Device..."
+#~ msgid "Send Files to Device…"
+#~ msgstr "ফাইলসমূহক ডিভাইচলে পঠাওক…"
+
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "এটা নতুন ডিভাইচ সংহতি কৰক…"
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "ফাইলসমূহ পঠাওক…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "কিবৰ্ড সংহতিসমূহ"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "মাউছ আৰু টাচপেড সংহতিসমূহ"
+
+#~ msgid "Sound Settings"
+#~ msgstr "শব্দ সংহতিসমূহ"
+
+#~| msgid "Region and Language Settings"
+#~ msgid "Region & Language Settings"
+#~ msgstr "অঞ্চল আৰু ভাষা সংহতিসমূহ"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "ভলিউম, নেটৱৰ্ক, বেটাৰি"
+
+#~ msgid "disabled"
+#~ msgstr "অসামৰ্থবান"
+
+#~ msgid "cable unplugged"
+#~ msgstr "কেবুল আনপ্লাগ্গড"
+
+#~ msgid "More…"
+#~ msgstr "অধিক…"
+
+#~ msgid "Wired"
+#~ msgstr "তাৰঁযুক্ত"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "স্বচালিত ইথাৰনেট"
+
+#~ msgid "Auto broadband"
+#~ msgstr "স্বচালিত ব্ৰডবেণ্ড"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "স্বচালিত ডায়েল-আপ"
+
+#~ msgid "Auto %s"
+#~ msgstr "স্বচালিত %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "স্বচালিত ব্লুটুথ"
+
+#~ msgid "Auto wireless"
+#~ msgstr "স্বচালিত বেতাঁৰ"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "Networking is disabled"
+#~ msgstr "নেটৱাৰ্কিং অসামৰ্থবান কৰা আছে"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d ঘন্টা অৱশিষ্ট"
+#~ msgstr[1] "%d ঘন্টা অৱশিষ্ট"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s অৱশিষ্ট"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ঘন্টা"
+#~ msgstr[1] "ঘন্টা"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "মিনিট"
+#~ msgstr[1] "মিনিট"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d মিনিট অৱশিষ্ট"
+#~ msgstr[1] "%d মিনিট অৱশিষ্ট"
+
+#~ msgctxt "percent of battery remaining"
+#~ msgid "%d%%"
+#~ msgstr "%d%%"
+
+#~| msgid "AC adapter"
+#~ msgid "AC Adapter"
+#~ msgstr "AC এডাপ্টাৰ"
+
+#~| msgid "Laptop battery"
+#~ msgid "Laptop Battery"
+#~ msgstr "লেপটপ বেটাৰি"
+
+#~ msgid "Monitor"
+#~ msgstr "মনিটৰ"
+
+#~ msgid "Mouse"
+#~ msgstr "মাউছ"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~| msgid "Cell phone"
+#~ msgid "Cell Phone"
+#~ msgstr "চেল ফোন"
+
+#~| msgid "Media player"
+#~ msgid "Media Player"
+#~ msgstr "মিডিয়া প্লেয়াৰ"
+
+#~ msgid "Tablet"
+#~ msgstr "টেবলেট"
+
+#~ msgid "Computer"
+#~ msgstr "কমপিউটাৰ"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "অজ্ঞাত"
+
+#~ msgid "Available"
+#~ msgstr "উপলব্ধ"
+
+#~ msgid "Busy"
+#~ msgstr "ব্যস্থ"
+
+#~ msgid "Invisible"
+#~ msgstr "অদৃশ্য"
+
+#~ msgid "Away"
+#~ msgstr "আতৰত"
+
+#~ msgid "Idle"
+#~ msgstr "অলস"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "আপোনাৰ চেট অৱস্থা ব্যস্থলে সংহতি কৰা হব"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "অধিসূচনাসমূহ এতিয়া অসামৰ্থবান কৰা হৈছে, চেট বাৰ্তাসমূহ অন্তৰ্ভুক্ত কৰাকৈ। আপোনাৰ "
+#~ "অনলাইন অৱস্থা ঠিক কৰা হৈছে যাতে আনে জানিব পাৰে যে আপুনি সিহতৰ বাৰ্তা চাব "
+#~ "নোৱাৰিবও পাৰে।"
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "বন্ধ কৰিলে তেওঁলোকৰ অসংৰক্ষিত কাৰ্য্য হেৰাব পাৰে।"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "ছাইন ইন কৰক"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "tray"
+#~ msgstr "ট্ৰে"
+
+#~ msgid "More..."
+#~ msgstr "অধিক..."
+
+#~ msgid "United Kingdom"
+#~ msgstr "ইংলেণ্ড"
+
+#~ msgid "Default"
+#~ msgstr "অবিকল্পিত"
+
+#~ msgid "Show full name in the user menu"
+#~ msgstr "ব্যৱহাৰকাৰী মেনুত সম্পূৰ্ণ নাম দেখুৱাওক"
+
+#~ msgid "Whether the users full name is shown in the user menu or not."
+#~ msgstr "ব্যৱহাৰকাৰী মেনুত ব্যৱহাৰকাৰীসকলৰ সম্পূৰ্ণ নাম দেখুৱা হয় নে নহয়।"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APPLICATIONS"
+
+#~ msgid "SETTINGS"
+#~ msgstr "SETTINGS"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "আপোনাৰ পছন্দৰ Easter Egg"
+
+#~ msgid "<b>%H:%M</b> on Yesterday"
+#~ msgstr "<b>%H:%M</b> যোৱাকালী"
+
+#~ msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
+#~ msgstr "<b>%A</b> ত <b>%H:%M</b>, <b>%B</b> <b>%d</b>, %Y"
+
+#~ msgid "Subscription request"
+#~ msgstr "স্বাক্ষৰণ অনুৰোধ"
+
+#~ msgid "Connection error"
+#~ msgstr "সংযোগ ত্ৰুটি"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "%s লে সংযোগ ব্যৰ্থ"
+
+#~ msgid "Reconnect"
+#~ msgstr "পুনৰ সংযোগ কৰক"
+
+#~ msgid "Browse Files..."
+#~ msgstr "ফাইলসমূহ ব্ৰাউছ কৰক..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "ডিভাইচ ব্ৰাউছ কৰোতে ত্ৰুটি"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "অনুৰোধিত ডিভাইচ ব্ৰাউছ কৰিব নোৱাৰি, ত্ৰুটি '%s'"
+
+#~ msgid "Wireless"
+#~ msgstr "বেতাঁৰ"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN সংযোগসমূহ"
+
+#~ msgid "System Settings"
+#~ msgstr "চিস্টেম সংহতিসমূহ"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "'%s' আনমাউণ্ট কৰিবলে ব্যৰ্থ"
+
+#~ msgid "Retry"
+#~ msgstr "পুনৰ চেষ্টা কৰক"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "PLACES & DEVICES"
+
+#~ msgid "Home"
+#~ msgstr "ঘৰ"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "অসামৰ্থবান কৰা OpenSearch প্ৰদানকাৰীসমূহ"
+
+#~ msgid "Connect to..."
+#~ msgstr "লে সংযোগ কৰক..."
+
+#~ msgid "Passphrase"
+#~ msgstr "পাচফ্ৰেইছ"
+
+#~ msgid "%s is online."
+#~ msgstr "%s অনলাইন আছে।"
+
+#~ msgid "%s is offline."
+#~ msgstr "%s অফলাইন আছে।"
+
+#~ msgid "%s is away."
+#~ msgstr "%s আতৰত আছে।"
+
+#~ msgid "%s is busy."
+#~ msgstr "%s ব্যস্ত আছে।"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "সময়ক ছেকেণ্ডসমূহৰ সৈতে দেখুৱাওক"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "যদি সত্য, ছেকেণ্ডসমূহ সময়ত দেখুৱাওক।"
+
+#~ msgid "Show date in clock"
+#~ msgstr "ঘড়িত তাৰিখ দেখুৱাওক"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "যদি সত্য, ঘড়িত তাৰিখ প্ৰদৰ্শন কৰক, সময়ৰ অতিৰিক্তভাৱে।"
+
+#~ msgid "CONTACTS"
+#~ msgstr "CONTACTS"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "ভুল পাছৱৰ্ড। অনুগ্ৰহ কৰি পুনৰ চেষ্টা কৰক।"
+
+#~ msgid "Hidden"
+#~ msgstr "লুকাই থকা"
+
+#~ msgid "Power Off..."
+#~ msgstr "বন্ধ কৰক"
+
+#~ msgid "Online Accounts"
+#~ msgstr "অনলাইন একাওণ্টসমূহ"
+
+#~ msgid "Lock Screen"
+#~ msgstr "পৰ্দা লক কৰক"
+
+#~ msgid "Log Out..."
+#~ msgstr "লগ আউট কৰক..."
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "RECENT ITEMS"
+
+#~ msgid "Show password"
+#~ msgstr "পাছৱৰ্ড দেখুৱাওক"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s এ আৰম্ভ কৰা সমাপ্ত কৰিছে"
+
+#~ msgid "Home Folder"
+#~ msgstr "ঘৰ ফোল্ডাৰ"
diff --git a/po/ast.po b/po/ast.po
new file mode 100644
index 0000000..72f49e9
--- /dev/null
+++ b/po/ast.po
@@ -0,0 +1,1630 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Xandru Armesto <xandru@softastur.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2011-09-30 18:14+0000\n"
+"PO-Revision-Date: 2011-09-30 20:56+0200\n"
+"Last-Translator: Xandru Armesto <xandru@softastur.org>\n"
+"Language-Team: Softastur <alministradores@softastur.org>\n"
+"Language: ast\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Virtaal 0.5.2\n"
+"X-Poedit-Language: asturian\n"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "Xestión de ventanes y aniciu d'aplicaciones"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:1
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permitir acceder a les ferramientes internes de depuración y monitorización "
+"usando'l diálogu Alt+F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:2
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Activar les ferramientes internes, útiles pa desendolcadores y probadores, "
+"dende Alt+F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:3
+msgid "File extension used for storing the screencast"
+msgstr "Estensión de ficheru qu'usar p'almacenar los «screencast»"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:4
+msgid "Framerate used for recording screencasts."
+msgstr "Tasa de fotogrames usada pa grabar «screencast»."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:5
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. disabled-extensions overrides this setting for extensions "
+"that appear in both lists."
+msgstr ""
+"Les estensiones de GNOME Shell tienen una propiedá uuid; esta contraseña "
+"llista les estensiones que nun se deben cargar. «disabled-extensions» "
+"sobrescribe esti axuste pa les estensiones qu'aparecen en dambes llistes."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:6
+msgid "History for command (Alt-F2) dialog"
+msgstr "Históricu del diálogu de comandos (Alt+F2)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:7
+msgid "History for the looking glass dialog"
+msgstr "Históricu del diálogu de «looking glass»"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:8
+msgid "If true, display date in the clock, in addition to time."
+msgstr "Si ye cierta, amuesa la data nel reló, arriendes de la hora."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:9
+msgid "If true, display seconds in time."
+msgstr "Si ye cierta, amuesa los segundos na hora."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:10
+msgid "If true, display the ISO week date in the calendar."
+msgstr "Si ye cierta, amuesa la data de selmana ISO nel calendariu."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:11
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Llista de ID de ficheros d'escritoriu pa les aplicaciones favorites"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:13
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
+"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
+"at the optimal thread count on the system."
+msgstr ""
+"Afita la tubería GStreamer usada pa codificar grabaciones. Sigue la sintaxis "
+"usada pa gst-launch. La tubería tendría tener un sumideru («sink») "
+"d'ensamblaxe/desensamblaxe au'l videu que se ta grabando, se graba. "
+"Xeneralmente tendrá un orixe d'ensambláu/desensambláu; la salida d'esi puntu "
+"escribiráse nel ficheru de salida. Sicasí, la tubería tamién pue tomar "
+"parte na so propia salida; esto pue usase pa unviar la salida a un sirvidor "
+"«icecast» a traviés de shout2send o similar. Cuando nun ta afitáu o lo ta a "
+"un valor baleru, va usase la tubería predeterminada. Anguaño te «videorate ! "
+"vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux» y graba en WEBM "
+"usando'l códec VP8. Úsase %T como suposición pal númberu de filos óptimos "
+"nel sistema."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:14
+msgid "Show date in clock"
+msgstr "Amosar la data nel reló"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:15
+msgid "Show the week date in the calendar"
+msgstr "Amosar la data de la selmana nel calendariu"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:16
+msgid "Show time with seconds"
+msgstr "Amosar la hora con segundos"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:17
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Les aplicaciones correspondientes con esos identificadores van amosase nel "
+"área de favoritos."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:18
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+"El nome de ficheru pa los «screencast» sedrá un nome de ficheru únicu basáu "
+"na data actual y usará esta estensión. Tendría de camudase al grabar n'otru "
+"formatu contenedor distintu."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:19
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+"La tasa de fotogrames de la grabación resultante grabada pol grabador de "
+"«screencast» de GNOME Shell, en fotogrames per segundu."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:20
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr "La tubería de gstreamer usada pa codificar el «screencast»"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:21
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+"La «shell» normalmente monitoriza toles aplicaciones actives pa presentar "
+"les más usaes (ex. nos lanzadores). Magar qu'estos datos se caltienen de "
+"forma privada, pues querer desactivalo por razones de privacidá. Nota que "
+"faciéndolo nun desanicies los datos yá guardaos."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:22
+msgid "The type of keyboard to use."
+msgstr "La triba de tecláu qu'usar."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:23
+msgid "Uuids of extensions to enable"
+msgstr "Uuid de les estensiones qu'activar"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:24
+msgid "Whether to collect stats about applications usage"
+msgstr ""
+"Indica si hai de recolectar estadístiques tocante al usu de les aplicaciones"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:25
+msgid "Which keyboard to use"
+msgstr "Qué tecláu usar"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:26
+msgid "disabled OpenSearch providers"
+msgstr "fornidores OpenSearch desactivaos"
+
+#: ../js/gdm/loginDialog.js:617
+msgid "Session..."
+msgstr "Sesión…"
+
+#: ../js/gdm/loginDialog.js:785
+msgctxt "title"
+msgid "Sign In"
+msgstr "Entamar sesión"
+
+#. translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/loginDialog.js:830
+msgid "(or swipe finger)"
+msgstr "(o pasa'l deu)"
+
+#: ../js/gdm/loginDialog.js:848
+msgid "Not listed?"
+msgstr "¿Nun ta llistáu?"
+
+#: ../js/gdm/loginDialog.js:1004 ../js/ui/endSessionDialog.js:426
+#: ../js/ui/extensionSystem.js:477 ../js/ui/networkAgent.js:165
+#: ../js/ui/polkitAuthenticationAgent.js:171 ../js/ui/status/bluetooth.js:480
+msgid "Cancel"
+msgstr "Encaboxar"
+
+#: ../js/gdm/loginDialog.js:1009
+msgctxt "button"
+msgid "Sign In"
+msgstr "Entamar sesión"
+
+#: ../js/gdm/loginDialog.js:1358
+msgid "Login Window"
+msgstr "Ventana d'aniciu de sesión"
+
+#: ../js/gdm/powerMenu.js:116 ../js/ui/userMenu.js:514
+#: ../js/ui/userMenu.js:516 ../js/ui/userMenu.js:585
+msgid "Suspend"
+msgstr "Suspender"
+
+#: ../js/gdm/powerMenu.js:121 ../js/ui/endSessionDialog.js:89
+#: ../js/ui/endSessionDialog.js:97 ../js/ui/endSessionDialog.js:106
+msgid "Restart"
+msgstr "Reaniciar"
+
+#: ../js/gdm/powerMenu.js:126 ../js/ui/endSessionDialog.js:80
+#: ../js/ui/endSessionDialog.js:91
+msgid "Power Off"
+msgstr "Apagar"
+
+#: ../js/misc/util.js:92
+msgid "Command not found"
+msgstr "Comandu non alcontráu"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:119
+msgid "Could not parse command:"
+msgstr "Nun pudo analizase'l comandu:"
+
+#: ../js/misc/util.js:127
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "Falló la execución de «%s»:"
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:255
+msgid "All"
+msgstr "Toes"
+
+#: ../js/ui/appDisplay.js:319
+msgid "APPLICATIONS"
+msgstr "APLICACIONES"
+
+#: ../js/ui/appDisplay.js:377
+msgid "SETTINGS"
+msgstr "CONFIGURACIÓN"
+
+#: ../js/ui/appDisplay.js:684
+msgid "New Window"
+msgstr "Ventana nueva"
+
+#: ../js/ui/appDisplay.js:687
+msgid "Remove from Favorites"
+msgstr "Quitar de los favoritos"
+
+#: ../js/ui/appDisplay.js:688
+msgid "Add to Favorites"
+msgstr "Amestar a los favoritos"
+
+#: ../js/ui/appFavorites.js:89
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "Amestóse %s a los favoritos."
+
+#: ../js/ui/appFavorites.js:120
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "Desanicióse %s de los favoritos."
+
+#: ../js/ui/autorunManager.js:280
+msgid "Removable Devices"
+msgstr "Preseos extraíbles"
+
+#: ../js/ui/autorunManager.js:590
+#, c-format
+msgid "Open with %s"
+msgstr "Abrir con %s"
+
+#: ../js/ui/autorunManager.js:616
+msgid "Eject"
+msgstr "Espulsar"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:63
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Tol día"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:68
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:75
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:115
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:117
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:119
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:121
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "m"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:123
+msgctxt "grid thursday"
+msgid "T"
+msgstr "X"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:125
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:127
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:140
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Dom"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:142
+msgctxt "list monday"
+msgid "M"
+msgstr "Llu"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:144
+msgctxt "list tuesday"
+msgid "T"
+msgstr "Mar"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:146
+msgctxt "list wednesday"
+msgid "W"
+msgstr "Mié"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:148
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Xue"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:150
+msgctxt "list friday"
+msgid "F"
+msgstr "Vie"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:152
+msgctxt "list saturday"
+msgid "S"
+msgstr "Sáb"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:687
+msgid "Nothing Scheduled"
+msgstr "Res programáu"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:703
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %d de %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:706
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %d de %B de %Y"
+
+#: ../js/ui/calendar.js:716
+msgid "Today"
+msgstr "Güei"
+
+#: ../js/ui/calendar.js:720
+msgid "Tomorrow"
+msgstr "Mañana"
+
+#: ../js/ui/calendar.js:729
+msgid "This week"
+msgstr "Esta selmana"
+
+#: ../js/ui/calendar.js:737
+msgid "Next week"
+msgstr "La selmana que vien"
+
+#: ../js/ui/contactDisplay.js:59 ../js/ui/notificationDaemon.js:444
+#: ../js/ui/status/power.js:223 ../src/shell-app.c:355
+msgid "Unknown"
+msgstr "Desconocíu"
+
+#: ../js/ui/contactDisplay.js:80 ../js/ui/userMenu.js:139
+msgid "Available"
+msgstr "Disponible"
+
+#: ../js/ui/contactDisplay.js:85 ../js/ui/userMenu.js:148
+msgid "Away"
+msgstr "Ausente"
+
+#: ../js/ui/contactDisplay.js:89 ../js/ui/userMenu.js:142
+msgid "Busy"
+msgstr "Ocupáu"
+
+#: ../js/ui/contactDisplay.js:93
+msgid "Offline"
+msgstr "Desconeutáu"
+
+#: ../js/ui/contactDisplay.js:140
+msgid "CONTACTS"
+msgstr "CONTAUTOS"
+
+#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1204
+msgid "Remove"
+msgstr "Desaniciar"
+
+#: ../js/ui/dateMenu.js:99
+msgid "Date and Time Settings"
+msgstr "Axustes d'hora y data"
+
+#: ../js/ui/dateMenu.js:125
+msgid "Open Calendar"
+msgstr "Abrir calendariu"
+
+#. Translators: This is the time format with date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:183
+msgid "%a %b %e, %R:%S"
+msgstr "%a %e de %b, %R:%S"
+
+#: ../js/ui/dateMenu.js:184
+msgid "%a %b %e, %R"
+msgstr "%a %e de %b, %R"
+
+#. Translators: This is the time format without date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:188
+msgid "%a %R:%S"
+msgstr "%a %R:%S"
+
+#: ../js/ui/dateMenu.js:189
+msgid "%a %R"
+msgstr "%a %R"
+
+#. Translators: This is a time format with date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:196
+msgid "%a %b %e, %l:%M:%S %p"
+msgstr "%a %e de %b, %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:197
+msgid "%a %b %e, %l:%M %p"
+msgstr "%a %e de %b, %l:%M %p"
+
+#. Translators: This is a time format without date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:201
+msgid "%a %l:%M:%S %p"
+msgstr "%a %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:202
+msgid "%a %l:%M %p"
+msgstr "%a %l:%M %p"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:213
+msgid "%A %B %e, %Y"
+msgstr "%A, %e de %B de %Y"
+
+#: ../js/ui/docDisplay.js:16
+msgid "RECENT ITEMS"
+msgstr "ELEMENTOS RECIENTES"
+
+#: ../js/ui/endSessionDialog.js:60
+#, c-format
+msgid "Log Out %s"
+msgstr "Zarrar la sesión %s"
+
+#: ../js/ui/endSessionDialog.js:61 ../js/ui/endSessionDialog.js:75
+msgid "Log Out"
+msgstr "Zarrar la sesión"
+
+#: ../js/ui/endSessionDialog.js:62
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr ""
+"Calca «Zarrar la sesión» pa colar d'eses aplicaciones y zarrar la sesión del "
+"sistema."
+
+#: ../js/ui/endSessionDialog.js:64
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "va zarrase automáticamente la sesión de %s en %d segundu."
+msgstr[1] "va zarrase automáticamente la sesión de %s en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:69
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "La sesión va zarrase automáticamente en %d segundu."
+msgstr[1] "La sesión va zarrase automáticamente en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:73
+msgid "Logging out of the system."
+msgstr "Zarrando la sesión."
+
+#: ../js/ui/endSessionDialog.js:81
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr "Calca «Apagar» pa colar d'eses aplicaciones y apagar el sistema."
+
+#: ../js/ui/endSessionDialog.js:83
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "El sistema va apagase automáticamente en %d segundu."
+msgstr[1] "El sistema va apagase automáticamente en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:87
+msgid "Powering off the system."
+msgstr "Apagando'l sistema."
+
+#: ../js/ui/endSessionDialog.js:98
+msgid "Click Restart to quit these applications and restart the system."
+msgstr "Calca «Reaniciar» pa colar d'eses aplicaciones y reaniciar el sistema."
+
+#: ../js/ui/endSessionDialog.js:100
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "El sistema va reaniciase automáticamente en %d segundu."
+msgstr[1] "El sistema va reaniciase automáticamente en %d segundos."
+
+#: ../js/ui/endSessionDialog.js:104
+msgid "Restarting the system."
+msgstr "Reaniciando'l sistema."
+
+#: ../js/ui/extensionSystem.js:481
+msgid "Install"
+msgstr "Instalar"
+
+#: ../js/ui/extensionSystem.js:485
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr "¿Baxar ya instalar «%s» dende extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:529 ../js/ui/status/power.js:211
+msgid "Keyboard"
+msgstr "Tecláu"
+
+#: ../js/ui/lookingGlass.js:645
+msgid "No extensions installed"
+msgstr "Nun hai estensiones instalaes"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:700
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr "%s nun emitió error nengún."
+
+#: ../js/ui/lookingGlass.js:706
+msgid "Hide Errors"
+msgstr "Anubrir Errores"
+
+#: ../js/ui/lookingGlass.js:710 ../js/ui/lookingGlass.js:760
+msgid "Show Errors"
+msgstr "Amosar Errores"
+
+#: ../js/ui/lookingGlass.js:719
+msgid "Enabled"
+msgstr "Activáu"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:721 ../src/gvc/gvc-mixer-control.c:1093
+msgid "Disabled"
+msgstr "Desactiváu"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Error"
+msgstr "Fallu"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Out of date"
+msgstr "Caducáu"
+
+#: ../js/ui/lookingGlass.js:727
+msgid "Downloading"
+msgstr "Descargando"
+
+#: ../js/ui/lookingGlass.js:748
+msgid "View Source"
+msgstr "Ver fonte"
+
+#: ../js/ui/lookingGlass.js:754
+msgid "Web Page"
+msgstr "Páxina web"
+
+#: ../js/ui/messageTray.js:1197
+msgid "Open"
+msgstr "Abrir"
+
+#: ../js/ui/messageTray.js:2406
+msgid "System Information"
+msgstr "Información del sistema"
+
+#: ../js/ui/networkAgent.js:145
+msgid "Show password"
+msgstr "Amosar contraseña"
+
+#: ../js/ui/networkAgent.js:160
+msgid "Connect"
+msgstr "Coneutar"
+
+#. Cisco LEAP
+#: ../js/ui/networkAgent.js:255 ../js/ui/networkAgent.js:267
+#: ../js/ui/networkAgent.js:294 ../js/ui/networkAgent.js:314
+#: ../js/ui/networkAgent.js:324
+msgid "Password: "
+msgstr "Contraseña: "
+
+#. static WEP
+#: ../js/ui/networkAgent.js:260
+msgid "Key: "
+msgstr "Contraseña:"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/ui/networkAgent.js:292 ../js/ui/networkAgent.js:310
+msgid "Username: "
+msgstr "Nome d'usuariu:"
+
+#: ../js/ui/networkAgent.js:298
+msgid "Identity: "
+msgstr "Identidá:"
+
+#: ../js/ui/networkAgent.js:300
+msgid "Private key password: "
+msgstr "Clave de la contraseña privada:"
+
+#: ../js/ui/networkAgent.js:312
+msgid "Service: "
+msgstr "Serviciu:"
+
+#: ../js/ui/networkAgent.js:341
+msgid "Authentication required by wireless network"
+msgstr "La rede inalámbrica requier autenticación"
+
+#: ../js/ui/networkAgent.js:342
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network '%"
+"s'."
+msgstr ""
+"Necesítense contraseñes o claves de cifráu p'acceder a la rede inalámbrica «"
+"%s»."
+
+#: ../js/ui/networkAgent.js:346
+msgid "Wired 802.1X authentication"
+msgstr "Autenticación 802.1X cableada"
+
+#: ../js/ui/networkAgent.js:348
+msgid "Network name: "
+msgstr "Nome de la rede"
+
+#: ../js/ui/networkAgent.js:353
+msgid "DSL authentication"
+msgstr "Autenticación DSL"
+
+#: ../js/ui/networkAgent.js:360
+msgid "PIN code required"
+msgstr "Códigu PIN requeríu"
+
+#: ../js/ui/networkAgent.js:361
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Necesítase un códigu PIN pal preséu de banda ancha móvil"
+
+#: ../js/ui/networkAgent.js:362
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/networkAgent.js:368
+msgid "Mobile broadband network password"
+msgstr "Contraseña de la rede de banda ancha móvil"
+
+#: ../js/ui/networkAgent.js:369
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr "Requierse una contraseña pa coneutar a «%s»."
+
+#: ../js/ui/overview.js:91
+msgid "Undo"
+msgstr "Desfacer"
+
+#: ../js/ui/overview.js:205
+msgid "Windows"
+msgstr "Ventanes"
+
+#: ../js/ui/overview.js:208
+msgid "Applications"
+msgstr "Aplicaciones"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:230
+msgid "Dash"
+msgstr "Tableru"
+
+#. TODO - _quit() doesn't really work on apps in state STARTING yet
+#: ../js/ui/panel.js:539
+#, c-format
+msgid "Quit %s"
+msgstr "Colar de %s"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:575
+msgid "Activities"
+msgstr "Actividaes"
+
+#: ../js/ui/panel.js:967
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#: ../js/ui/placeDisplay.js:120
+#, c-format
+msgid "Failed to unmount '%s'"
+msgstr "Falló al desmontar «%s»"
+
+#: ../js/ui/placeDisplay.js:123
+msgid "Retry"
+msgstr "Reintentar"
+
+#: ../js/ui/placeDisplay.js:163
+msgid "Connect to..."
+msgstr "Coneutar a…"
+
+#: ../js/ui/placeDisplay.js:375
+msgid "PLACES & DEVICES"
+msgstr "LLUGARES Y PRESEOS"
+
+#: ../js/ui/polkitAuthenticationAgent.js:72
+msgid "Authentication Required"
+msgstr "Necesítase autenticación"
+
+#: ../js/ui/polkitAuthenticationAgent.js:106
+msgid "Administrator"
+msgstr "Alministrador"
+
+#: ../js/ui/polkitAuthenticationAgent.js:175
+msgid "Authenticate"
+msgstr "Autenticar"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/polkitAuthenticationAgent.js:256
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Inténtalo de nueves,"
+
+#: ../js/ui/polkitAuthenticationAgent.js:268
+msgid "Password:"
+msgstr "Contraseña:"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:731
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:208
+msgid "Please enter a command:"
+msgstr "Introduz un comandu:"
+
+#: ../js/ui/searchDisplay.js:340
+msgid "Searching..."
+msgstr "Guetando…"
+
+#: ../js/ui/searchDisplay.js:363
+msgid "No matching results."
+msgstr "Nun s'atopó dengún resultáu coincidente."
+
+#: ../js/ui/shellMountOperation.js:285
+msgid "Wrong password, please try again"
+msgstr "Contraseña incorreuta; inténtalo de nueves"
+
+#: ../js/ui/status/accessibility.js:60
+msgid "Zoom"
+msgstr "Ampliación"
+
+#. let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
+#. 'screen-reader-enabled');
+#. this.menu.addMenuItem(screenReader);
+#: ../js/ui/status/accessibility.js:71
+msgid "Screen Keyboard"
+msgstr "Tecláu en pantalla"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Visual Alerts"
+msgstr "Alertes visuales"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Sticky Keys"
+msgstr "Tecles persistentes"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Slow Keys"
+msgstr "Tecles lentes"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Bounce Keys"
+msgstr "Refugu de tecles"
+
+#: ../js/ui/status/accessibility.js:87
+msgid "Mouse Keys"
+msgstr "Tecles del mur"
+
+#: ../js/ui/status/accessibility.js:91
+msgid "Universal Access Settings"
+msgstr "Configuración del accesu universal"
+
+#: ../js/ui/status/accessibility.js:141
+msgid "High Contrast"
+msgstr "Contraste altu"
+
+#: ../js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr "Testu grande"
+
+#: ../js/ui/status/bluetooth.js:39 ../js/ui/status/bluetooth.js:261
+#: ../js/ui/status/bluetooth.js:347 ../js/ui/status/bluetooth.js:381
+#: ../js/ui/status/bluetooth.js:421 ../js/ui/status/bluetooth.js:454
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: ../js/ui/status/bluetooth.js:52
+msgid "Visibility"
+msgstr "Visibilidá"
+
+#: ../js/ui/status/bluetooth.js:66
+msgid "Send Files to Device..."
+msgstr "Unviar ficheros al preséu…"
+
+#: ../js/ui/status/bluetooth.js:67
+msgid "Set up a New Device..."
+msgstr "Configurar un preséu nuevu…"
+
+#: ../js/ui/status/bluetooth.js:91
+msgid "Bluetooth Settings"
+msgstr "Configuración de Bluetooth"
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:111
+msgid "hardware disabled"
+msgstr "hardware desactiváu"
+
+#: ../js/ui/status/bluetooth.js:208
+msgid "Connection"
+msgstr "Conexón"
+
+#: ../js/ui/status/bluetooth.js:217 ../js/ui/status/network.js:486
+msgid "disconnecting..."
+msgstr "desconeutando…"
+
+#: ../js/ui/status/bluetooth.js:230 ../js/ui/status/network.js:492
+msgid "connecting..."
+msgstr "coneutando…"
+
+#: ../js/ui/status/bluetooth.js:248
+msgid "Send Files..."
+msgstr "Unviar ficheros…"
+
+#: ../js/ui/status/bluetooth.js:253
+msgid "Browse Files..."
+msgstr "Desaminar ficheros…"
+
+#: ../js/ui/status/bluetooth.js:262
+msgid "Error browsing device"
+msgstr "Fallu al desaminar el preséu"
+
+#: ../js/ui/status/bluetooth.js:263
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr "Nun se pue desaminar el preséu solicitáu, el fallu ye «%s»"
+
+#: ../js/ui/status/bluetooth.js:271
+msgid "Keyboard Settings"
+msgstr "Configuración del tecláu"
+
+#: ../js/ui/status/bluetooth.js:274
+msgid "Mouse Settings"
+msgstr "Axustes del mur…"
+
+#: ../js/ui/status/bluetooth.js:279 ../js/ui/status/volume.js:62
+msgid "Sound Settings"
+msgstr "Configuración del soníu"
+
+#: ../js/ui/status/bluetooth.js:382
+#, c-format
+msgid "Authorization request from %s"
+msgstr "Solicitú d'autorización de %s"
+
+#: ../js/ui/status/bluetooth.js:388
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "El preséu %s quier acceder al serviciu «%s»"
+
+#: ../js/ui/status/bluetooth.js:390
+msgid "Always grant access"
+msgstr "Conceder accesu siempre"
+
+#: ../js/ui/status/bluetooth.js:391
+msgid "Grant this time only"
+msgstr "Conceder namái esta vegada"
+
+#: ../js/ui/status/bluetooth.js:392 ../js/ui/telepathyClient.js:1200
+msgid "Reject"
+msgstr "Refugar"
+
+#: ../js/ui/status/bluetooth.js:422
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "Confirmación d'emparexamientu pa «%s»"
+
+#: ../js/ui/status/bluetooth.js:428 ../js/ui/status/bluetooth.js:462
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "El preséu «%s» quier emparexase con esti equipu"
+
+#: ../js/ui/status/bluetooth.js:429
+#, c-format
+msgid "Please confirm whether the PIN '%s' matches the one on the device."
+msgstr "Confirma que'l PIN amosáu en «%s» concasa col del preséu."
+
+#: ../js/ui/status/bluetooth.js:431
+msgid "Matches"
+msgstr "Concasa"
+
+#: ../js/ui/status/bluetooth.js:432
+msgid "Does not match"
+msgstr "Nun concasa"
+
+#: ../js/ui/status/bluetooth.js:455
+#, c-format
+msgid "Pairing request for %s"
+msgstr "Solicitú d'emparexamientu pa «%s»"
+
+#: ../js/ui/status/bluetooth.js:463
+msgid "Please enter the PIN mentioned on the device."
+msgstr "Introduz el PIN mencionáu nel preséu."
+
+#: ../js/ui/status/bluetooth.js:479
+msgid "OK"
+msgstr "Aceutar"
+
+#: ../js/ui/status/keyboard.js:73
+msgid "Show Keyboard Layout"
+msgstr "Amosar la distribución del tecláu"
+
+#: ../js/ui/status/keyboard.js:78
+msgid "Region and Language Settings"
+msgstr "Configuración de rexón y llingua"
+
+#: ../js/ui/status/network.js:97
+msgid "<unknown>"
+msgstr "<desconocíu>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:285
+msgid "disabled"
+msgstr "desactivada"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:484
+msgid "unmanaged"
+msgstr "non xestionada"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:495
+msgid "authentication required"
+msgstr "necesítase autenticación"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:505
+msgid "firmware missing"
+msgstr "falta'l «firmware»"
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:512
+msgid "cable unplugged"
+msgstr "cable desconeutáu"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:517
+msgid "unavailable"
+msgstr "non disponible"
+
+#: ../js/ui/status/network.js:519
+msgid "connection failed"
+msgstr "falló la conexón"
+
+#: ../js/ui/status/network.js:575 ../js/ui/status/network.js:1523
+msgid "More..."
+msgstr "Más…"
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:611 ../js/ui/status/network.js:1458
+msgid "Connected (private)"
+msgstr "Coneutada (privada)"
+
+#: ../js/ui/status/network.js:689
+msgid "Auto Ethernet"
+msgstr "Ethernet automática"
+
+#: ../js/ui/status/network.js:753
+msgid "Auto broadband"
+msgstr "Banda ancha automática"
+
+#: ../js/ui/status/network.js:756
+msgid "Auto dial-up"
+msgstr "Marcáu automáticu"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:878 ../js/ui/status/network.js:1470
+#, c-format
+msgid "Auto %s"
+msgstr "%s automática"
+
+#: ../js/ui/status/network.js:880
+msgid "Auto bluetooth"
+msgstr "Bluetooth automáticu"
+
+#: ../js/ui/status/network.js:1472
+msgid "Auto wireless"
+msgstr "Inalámbrica automática"
+
+#: ../js/ui/status/network.js:1566
+msgid "Enable networking"
+msgstr "Activar rede"
+
+#: ../js/ui/status/network.js:1578
+msgid "Wired"
+msgstr "Cableada"
+
+#: ../js/ui/status/network.js:1589
+msgid "Wireless"
+msgstr "Inalámbrica"
+
+#: ../js/ui/status/network.js:1599
+msgid "Mobile broadband"
+msgstr "Banda ancha móvil"
+
+#: ../js/ui/status/network.js:1609
+msgid "VPN Connections"
+msgstr "Conexones VPN"
+
+#: ../js/ui/status/network.js:1620
+msgid "Network Settings"
+msgstr "Configuración de la rede"
+
+#: ../js/ui/status/network.js:1757
+msgid "Connection failed"
+msgstr "Falló la conexón"
+
+#: ../js/ui/status/network.js:1758
+msgid "Activation of network connection failed"
+msgstr "Falló l'activación de la conexón de rede"
+
+#: ../js/ui/status/network.js:2008
+msgid "Networking is disabled"
+msgstr "La rede ta desactivada"
+
+#: ../js/ui/status/network.js:2133
+msgid "Network Manager"
+msgstr "Xestor de la rede"
+
+#: ../js/ui/status/power.js:82
+msgid "Power Settings"
+msgstr "Configuración d'enerxía"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:103
+msgid "Estimating..."
+msgstr "Estimando…"
+
+#: ../js/ui/status/power.js:110
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "Queda %d hora"
+msgstr[1] "Queden %d hores"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:113
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "Queden %d %s %d %s"
+
+#: ../js/ui/status/power.js:115
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "hora"
+msgstr[1] "hores"
+
+#: ../js/ui/status/power.js:115
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "minutu"
+msgstr[1] "minutos"
+
+#: ../js/ui/status/power.js:118
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "Queda %d minutu"
+msgstr[1] "Queden %d minutos"
+
+#: ../js/ui/status/power.js:121 ../js/ui/status/power.js:194
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d%%"
+
+#: ../js/ui/status/power.js:201
+msgid "AC adapter"
+msgstr "Adautador de corriente"
+
+#: ../js/ui/status/power.js:203
+msgid "Laptop battery"
+msgstr "Batería del portátil"
+
+#: ../js/ui/status/power.js:205
+msgid "UPS"
+msgstr "SAI"
+
+#: ../js/ui/status/power.js:207
+msgid "Monitor"
+msgstr "Monitor"
+
+#: ../js/ui/status/power.js:209
+msgid "Mouse"
+msgstr "Mur"
+
+#: ../js/ui/status/power.js:213
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:215
+msgid "Cell phone"
+msgstr "Teléfonu móvil"
+
+#: ../js/ui/status/power.js:217
+msgid "Media player"
+msgstr "Reproductor multimedia"
+
+#: ../js/ui/status/power.js:219
+msgid "Tablet"
+msgstr "Tableta"
+
+#: ../js/ui/status/power.js:221
+msgid "Computer"
+msgstr "Equipu"
+
+#: ../js/ui/status/volume.js:42
+msgid "Volume"
+msgstr "Volume"
+
+#: ../js/ui/status/volume.js:54
+msgid "Microphone"
+msgstr "Micrófonu"
+
+#. We got the TpContact
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/telepathyClient.js:259
+msgid "Invitation"
+msgstr "Invitación"
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:327
+msgid "Call"
+msgstr "Llamar"
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:357
+msgid "File Transfer"
+msgstr "Tresferencia de ficheros"
+
+#: ../js/ui/telepathyClient.js:438
+msgid "Subscription request"
+msgstr "Solicitú de suscripción"
+
+#: ../js/ui/telepathyClient.js:474
+msgid "Connection error"
+msgstr "Fally de conexón"
+
+#: ../js/ui/telepathyClient.js:737
+#, c-format
+msgid "%s is online."
+msgstr "%s ta coneutáu/ada."
+
+#: ../js/ui/telepathyClient.js:742
+#, c-format
+msgid "%s is offline."
+msgstr "%s ta desconeutáu/ada."
+
+#: ../js/ui/telepathyClient.js:745
+#, c-format
+msgid "%s is away."
+msgstr "%s ta ausente."
+
+#: ../js/ui/telepathyClient.js:748
+#, c-format
+msgid "%s is busy."
+msgstr "%s ta ocupáu/ada."
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/telepathyClient.js:982
+#, no-c-format
+msgid "Sent at <b>%X</b> on <b>%A</b>"
+msgstr "Unviáu el <b>%A</b> a les <b>%H:%M</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25",
+#. shown when you get a chat message in the same year.
+#: ../js/ui/telepathyClient.js:988
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+msgstr "Unviáu el <b>%A</b>, <b>%d de %B</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
+#. shown when you get a chat message in a different year.
+#: ../js/ui/telepathyClient.js:993
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+msgstr "Unviáu el <b>%A</b>, <b>%d de %B</b> de %Y"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/telepathyClient.js:1035
+#, c-format
+msgid "%s is now known as %s"
+msgstr "Agora %s llámase %s"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1144
+#, c-format
+msgid "Invitation to %s"
+msgstr "Invitación a %s"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/telepathyClient.js:1152
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr "%s ta invitándote a xunite a %s"
+
+#: ../js/ui/telepathyClient.js:1154 ../js/ui/telepathyClient.js:1243
+#: ../js/ui/telepathyClient.js:1347
+msgid "Decline"
+msgstr "Refugar"
+
+#: ../js/ui/telepathyClient.js:1155 ../js/ui/telepathyClient.js:1244
+#: ../js/ui/telepathyClient.js:1348
+msgid "Accept"
+msgstr "Aceutar"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1188
+#, c-format
+msgid "Video call from %s"
+msgstr "Videullamada de %s"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1191
+#, c-format
+msgid "Call from %s"
+msgstr "Llamada de %s"
+
+#: ../js/ui/telepathyClient.js:1201
+msgid "Answer"
+msgstr "Retrucar"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/telepathyClient.js:1237
+#, c-format
+msgid "%s is sending you %s"
+msgstr "%s ta unviándote %s"
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/telepathyClient.js:1312
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s solicita permisu pa ver cuándo tas en llinia"
+
+#: ../js/ui/telepathyClient.js:1410
+msgid "Network error"
+msgstr "Fallu de la rede"
+
+#: ../js/ui/telepathyClient.js:1412
+msgid "Authentication failed"
+msgstr "Falló l'autenticación"
+
+#: ../js/ui/telepathyClient.js:1414
+msgid "Encryption error"
+msgstr "Fallu de cifráu"
+
+#: ../js/ui/telepathyClient.js:1416
+msgid "Certificate not provided"
+msgstr "Certificáu non proporcionáu"
+
+#: ../js/ui/telepathyClient.js:1418
+msgid "Certificate untrusted"
+msgstr "Nun se confía nel certificáu"
+
+#: ../js/ui/telepathyClient.js:1420
+msgid "Certificate expired"
+msgstr "Certificáu caducáu"
+
+#: ../js/ui/telepathyClient.js:1422
+msgid "Certificate not activated"
+msgstr "Certificáu non activáu"
+
+#: ../js/ui/telepathyClient.js:1424
+msgid "Certificate hostname mismatch"
+msgstr "El nome del sirvidor del certificáu no concasa"
+
+#: ../js/ui/telepathyClient.js:1426
+msgid "Certificate fingerprint mismatch"
+msgstr "La buelga del certificáu nun concasa"
+
+#: ../js/ui/telepathyClient.js:1428
+msgid "Certificate self-signed"
+msgstr "Certificáu autofirmáu"
+
+#: ../js/ui/telepathyClient.js:1430
+msgid "Status is set to offline"
+msgstr "L'estáu ta afitáu a «desconeutáu»"
+
+#: ../js/ui/telepathyClient.js:1432
+msgid "Encryption is not available"
+msgstr "El cifráu nun ta disponible"
+
+#: ../js/ui/telepathyClient.js:1434
+msgid "Certificate is invalid"
+msgstr "El certificáu nun ye válidu"
+
+#: ../js/ui/telepathyClient.js:1436
+msgid "Connection has been refused"
+msgstr "Refugóse la conexón"
+
+#: ../js/ui/telepathyClient.js:1438
+msgid "Connection can't be established"
+msgstr "Nun pue afitase la conexón"
+
+#: ../js/ui/telepathyClient.js:1440
+msgid "Connection has been lost"
+msgstr "Perdióse la conexón"
+
+#: ../js/ui/telepathyClient.js:1442
+msgid "This resource is already connected to the server"
+msgstr "Esti recursu yá ta coneutáu al sirvidor"
+
+#: ../js/ui/telepathyClient.js:1444
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "Sustituyóse la conexón por una nueva conexón usando'l mesmu recursu"
+
+#: ../js/ui/telepathyClient.js:1446
+msgid "The account already exists on the server"
+msgstr "La cuenta yá esiste nel sirvidor"
+
+#: ../js/ui/telepathyClient.js:1448
+msgid "Server is currently too busy to handle the connection"
+msgstr "Anguaño, el sirvidor ta mui ocupáu intentando xestionar la conexón"
+
+#: ../js/ui/telepathyClient.js:1450
+msgid "Certificate has been revoked"
+msgstr "Revocóse'l certificáu"
+
+#: ../js/ui/telepathyClient.js:1452
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"El certificáu usa un algoritmu de cifráu inseguru o ye criptográficamente "
+"feble"
+
+#: ../js/ui/telepathyClient.js:1454
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"El llargor del certificáu del sirvidor o la profundidá de la cadena del "
+"certificáu del sirvidor perpasa llímites impuestos pola biblioteca de "
+"criptografía"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1463
+#, c-format
+msgid "Connection to %s failed"
+msgstr "Falló la conexón a %s"
+
+#: ../js/ui/telepathyClient.js:1472
+msgid "Reconnect"
+msgstr "Reconeutar"
+
+#: ../js/ui/telepathyClient.js:1473
+msgid "Edit account"
+msgstr "Editar cuenta"
+
+#: ../js/ui/telepathyClient.js:1519
+msgid "Unknown reason"
+msgstr "Razón desconocida"
+
+#: ../js/ui/userMenu.js:145
+msgid "Hidden"
+msgstr "Ocultu"
+
+#: ../js/ui/userMenu.js:151
+msgid "Idle"
+msgstr "Inactivu"
+
+#: ../js/ui/userMenu.js:154
+msgid "Unavailable"
+msgstr "Non disponible"
+
+#: ../js/ui/userMenu.js:512 ../js/ui/userMenu.js:516 ../js/ui/userMenu.js:586
+msgid "Power Off..."
+msgstr "Apagar…"
+
+#: ../js/ui/userMenu.js:548
+msgid "Notifications"
+msgstr "Notificaciones"
+
+#: ../js/ui/userMenu.js:556
+msgid "Online Accounts"
+msgstr "Cuentes en llinia"
+
+#: ../js/ui/userMenu.js:560
+msgid "System Settings"
+msgstr "Configuración del sistema"
+
+#: ../js/ui/userMenu.js:567
+msgid "Lock Screen"
+msgstr "Bloquiar la pantalla"
+
+#: ../js/ui/userMenu.js:572
+msgid "Switch User"
+msgstr "Camudar d'usuariu"
+
+#: ../js/ui/userMenu.js:577
+msgid "Log Out..."
+msgstr "Zarrar la sesión…"
+
+#: ../js/ui/userMenu.js:605
+msgid "Your chat status will be set to busy"
+msgstr "El to estáu del chat va afitase a «ocupáu»"
+
+#: ../js/ui/userMenu.js:606
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+"Les notificaciones tán agora desactivaes, incluyendo los mensaxes de chat. "
+"El to estáu en llinia axustóse pa qu'otros sepan que nun pues lleer los sos "
+"mensaxes."
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/viewSelector.js:120
+msgid "Type to search..."
+msgstr "Escribi pa guetar…"
+
+#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
+msgid "Search"
+msgstr "Guetar"
+
+#: ../js/ui/windowAttentionHandler.js:35
+#, c-format
+msgid "'%s' is ready"
+msgstr "«%s» ta preparáu"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1100
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u salida"
+msgstr[1] "%u salides"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1110
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entraes"
+
+#: ../src/gvc/gvc-mixer-control.c:1408
+msgid "System Sounds"
+msgstr "Soníos del sistema"
+
+#: ../src/main.c:480
+msgid "Print version"
+msgstr "Imprentar versión"
+
+#: ../src/main.c:486
+msgid "Mode used by GDM for login screen"
+msgstr "Mou usáu por GDM na pantalla d'aniciu"
+
+#: ../src/shell-app.c:581
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr "Falló al llanzar «%s»"
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr "Reinu Xuníu"
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr "Predeterminada"
+
+#: ../src/shell-polkit-authentication-agent.c:334
+msgid "Authentication dialog was dismissed by the user"
+msgstr "L'usuariu refugó'l diálogu d'autenticación"
+
+#: ../src/shell-util.c:100
+msgid "Home Folder"
+msgstr "Carpeta personal"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:115
+msgid "File System"
+msgstr "Sistema de ficheros"
+
+#. Translators: the first string is the name of a gvfs
+#. * method, and the second string is a path. For
+#. * example, "Trash: some-directory". It means that the
+#. * directory called "some-directory" is in the trash.
+#.
+#: ../src/shell-util.c:311
+#, c-format
+msgid "%1$s: %2$s"
+msgstr "%1$s: %2$s"
+
+#, c-format
+#~ msgid "%s has finished starting"
+#~ msgstr "%s finó'l so llanzamientu"
diff --git a/po/be.po b/po/be.po
new file mode 100644
index 0000000..73fa6ef
--- /dev/null
+++ b/po/be.po
@@ -0,0 +1,2917 @@
+# Ihar Hrachyshka <ihar.hrachyshka@gmail.com>, 2011, 2013, 2014.
+# Kasia Bondarava <kasia.bondarava@gmail.com>, 2012.
+# Yuras Shumovich <shumovichy@gmail.com>, 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell.gnome-43\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-11-22 22:09+0000\n"
+"PO-Revision-Date: 2022-11-28 20:12+0300\n"
+"Last-Translator: Yuras Shumovich <shumovichy@gmail.com>\n"
+"Language-Team: Belarusian <i18n-bel-gnome@googlegroups.com>\n"
+"Language: be\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Poedit 3.2\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Запуск праграм"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Актываваць абраную праграму 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Актываваць абраную праграму 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Актываваць абраную праграму 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Актываваць абраную праграму 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Актываваць абраную праграму 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Актываваць абраную праграму 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Актываваць абраную праграму 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Актываваць абраную праграму 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Актываваць абраную праграму 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Здымкі экрана"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Зрабіць здымак экрана інтэрактыўна"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Зрабіць здымак экрана"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Зрабіць здымак акна"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Зрабіць запіс экрана інтэрактыўна"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Сістэма"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Паказаць спіс апавяшчэнняў"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Перанесці фокус на актыўнае апавяшчэнне"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Паказаць агляд"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Паказаць усе праграмы"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Адкрыць меню праграм"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Абалонка GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Кіраванне вокнамі і запуск праграм"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Уключыць унутраныя інструменты з Alt-F2 для распрацоўшчыкаў і тэсціроўшчыкаў"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Дазваляе доступ да ўнутраных інструментаў адладкі і назірання праз "
+"дыялогавае акно Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID пашырэнняў, якія трэба ўключыць"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Пашырэнні абалонкі GNOME маюць уласцівасць UUID; у гэтым ключы пералічаны "
+"пашырэнні, якія неабходна загрузіць. Усе пашырэнні, якія вы хочаце "
+"загрузіць, трэба ўпісаць у гэты спіс. Спісам таксама можна кіраваць метадамі "
+"DBus EnableExtension і DisableExtension у org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID пашырэнняў, якія трэба прымусова адключыць"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Пашырэнні абалонкі GNOME маюць уласцівасць UUID; у гэтым ключы пералічаны "
+"пашырэнні, якія неабходна адключыць, нават калі яны загрузіліся як частка "
+"бягучага рэжыму. Спісам таксама можна кіраваць метадамі DBus EnableExtension "
+"і DisableExtension у org.gnome.Shell. Прыярытэт гэтага ключа вышэйшы за "
+"значэнне налады «enabled-extensions»."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Адключыць пашырэнні карыстальніка"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Адключыць усе пашырэнні, якія ўключыў карыстальнік без змянення налады "
+"«enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Адключае праверку сумяшчальнасці версій пашырэнняў"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Абалонка GNOME запускае толькі пашырэнні, для якіх заяўлена падтрымка яе "
+"бягучай версіі. Гэты параметр адключыць праверку сумяшчальнасці і абалонка "
+"паспрабуе загрузіць усе пашырэнні незалежна ад заяўленай падтрымкі."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Спіс ідэнтыфікатараў файлаў для абраных праграм"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "Праграмы з адпаведнымі ідэнтыфікатарамі будуць паказаны ў абраным."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Гісторыя каманд дыялогавага акна (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Гісторыя дыялогавага акна Looking Glass"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Заўсёды паказваць элемент «Завяршыць сеанс» у меню карыстальніка."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Гэты ключ перавызначае аўтаматычнае хаванне элемента меню «Завяршыць сеанс» "
+"для выпадкаў з адным карыстальнікам і адным сеансам."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Ці запамінаць пароль для падключэння зашыфраваных і аддаленых файлавых сістэм"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Абалонка запытвае пароль пры падключэнні зашыфраванай прылады або аддаленай "
+"файлавай сістэмы. Калі пароль магчыма захаваць на будучыню, будзе паказаны "
+"пераключальнік «Запомніць пароль». Гэты ключ вызначае прадвызначаны стан для "
+"пераключальніка."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Апошні выбраны нестандартны профіль электрасілкавання"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Некаторыя сістэмы падтрымліваюць больш за два профілі электрасілкавання. Каб "
+"захаваць падтрымку пераключэння паміж двума профілямі, гэты ключ запісвае "
+"апошні выбраны нестандартны профіль."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Апошняя версія дыялогу «Вітаем у GNOME» была паказана для"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Гэты ключ вызначае для якой версіі ў апошні раз паказваўся дыялог «Вітаем у "
+"GNOME». Пусты радок азначае самую старую з магчымых версій, а занадта вялікі "
+"нумар будзе імітаваць неіснуючую версію. Можна выкарыстаць занадта вялікі "
+"нумар версіі, каб адключыць дыялогавае акно."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Макет для сродку выбару праграм"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Макет для сродку выбару праграм. Кожны запіс у масіве - гэта старонка. "
+"Старонкі захоўваюцца ў парадку з'яўлення ў абалонцы GNOME. Кожная старонка "
+"змяшчае пару «id праграмы» → «даныя». Зараз як «даныя» захоўваюцца наступныя "
+"значэнні: • «position» – пазіцыя значка праграмы на старонцы"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Спалучэнне клавіш, каб адкрыць меню актыўнай праграмы"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Спалучэнне клавіш, каб адкрыць меню актыўнай праграмы."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Спалучэнне клавіш, каб пераключацца паміж станамі меню агляд"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Спалучэнне клавіш, каб пераключацца паміж сесіяй, выбарам акон і сеткай "
+"праграм"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Спалучэнне клавіш, каб пераключацца паміж сеткай праграм, выбарам акон і "
+"сесіяй"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Спалучэнне клавіш, каб адкрыць «Паказаць праграмы»"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Спалучэнне клавіш, каб адкрыць «Паказаць праграмы» ў меню агляд дзейнасці."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Спалучэнне клавіш, каб адкрыць меню агляд"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Спалучэнне клавіш, каб адкрыць меню агляд дзейнасці."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Спалучэнне клавіш, каб пераключыць бачнасць спіса апавяшчэнняў"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Спалучэнне клавіш, каб пераключыць бачнасць спіса апавяшчэнняў."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Спалучэнне клавіш, каб перанесці фокус на актыўнае апавяшчэнне"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Спалучэнне клавіш, каб перанесці фокус на актыўнае апавяшчэнне."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Пераключыцца на праграму 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Пераключыцца на праграму 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Пераключыцца на праграму 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Пераключыцца на праграму 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Пераключыцца на праграму 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Пераключыцца на праграму 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Пераключыцца на праграму 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Пераключыцца на праграму 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Пераключыцца на праграму 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Абмежаваць пераключальнік бягучай працоўнай прасторай."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Калі актыўная, у пераключальніку паказваюцца толькі праграмы з бягучай "
+"працоўнай прасторы. Іначай – паказваюцца ўсе праграмы."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Рэжым значкоў праграм."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Вызначае як паказваюцца вокны ў пераключальніку. Магчымыя значэнні: "
+"«thumbnail-only» (паказваць мініяцюры вокнаў), «app-icon-only» (паказваць "
+"толькі значкі праграм) і «both» (паказваць мініяцюры і значкі)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Калі актыўная, у пераключальніку паказваюцца толькі вокны з бягучай "
+"працоўнай прасторы. Іначай – паказваюцца ўсе вокны."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Месцазнаходжанні"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Месцазнаходжанне ў сусветных гадзінніках"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Аўтаматычна вызначаць месцазнаходжанне"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Ці атрымліваць бягучае месцазнаходжанне"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Месцазнаходжанне"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Месцазнаходжанне для якога паказваецца надвор'е"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Чапляць мадальнае дыялогавае акно да бацькоўскага акна"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Гэты ключ перавызначае ключ у org.gnome.mutter пры запуску абалонкі GNOME."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Уключыць парадкаванне на краі экрана пры перацягванні туды вокнаў"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Дынамічнае кіраванне працоўнымі прасторамі"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Працоўныя прасторы толькі на асноўным маніторы"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Затрымка да змянення фокусу пры спыненні ўказальніка ў рэжыме мышы"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Сеткавы ўваход"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Нешта пайшло не так"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"На жаль, узнікла праблема: немагчыма паказаць налады гэтага пашырэння. Мы "
+"раім вам паведаміць аб праблеме аўтарам пашырэння."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Тэхнічныя падрабязнасці"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Хатняя старонка"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Наведаць хатнюю старонку пашырэння"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Скасаваць"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Пароль"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Выбраць сеанс"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Няма ў спісе?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(напр. карыстальнік ці %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Імя карыстальніка"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Акно ўваходу ў сістэму"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Памылка праверкі сапраўднасці"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(або правядзіце пальцам па чытальніку)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(або прыкладзіце палец да чытальніка)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Выключыць"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;выключыць;спыніць;завяршыць"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Перазапусціць"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;перазапусціць;перазапуск;перазагрузіць;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Заблакіраваць экран"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "заблакіраваць экран"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Завяршыць сеанс"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;выйсці;завяршыць сеанс"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Прыпыніць працу"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;прыпыніць;заснуць;сон"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Змяніць карыстальніка"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "змяніць карыстальніка"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;rotation;заблакіраваць арыентацыю; разблакіраваць;экран;"
+"паварот"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Зрабіць здымак экрана"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "screenshot;capture;здымак;запіс;экрана;экранны здымак;нажніцы;захоп"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Разблакіраваць паварот экрана"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Заблакіраваць паварот экрана"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Каманда не знойдзена"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Не ўдалося разабраць каманду:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Не ўдалося выканаць «%s»:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Толькі што"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d хвіліну таму"
+msgstr[1] "%d хвіліны таму"
+msgstr[2] "%d хвілін таму"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d гадзіну таму"
+msgstr[1] "%d гадзіны таму"
+msgstr[2] "%d гадзін таму"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Учора"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d дзень таму"
+msgstr[1] "%d дні таму"
+msgstr[2] "%d дзён таму"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d тыдзень таму"
+msgstr[1] "%d тыдні таму"
+msgstr[2] "%d тыдняў таму"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d месяц таму"
+msgstr[1] "%d месяцы таму"
+msgstr[2] "%d месяцаў таму"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d год таму"
+msgstr[1] "%d гады таму"
+msgstr[2] "%d гадоў таму"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Учора, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Учора, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %-l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Уваход у хот-спот"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Злучэнне з гэтым хот-спотам не бяспечнае. Людзі вакол могуць атрымаць паролі "
+"або іншую інфармацыю, якую вы ўводзіце на старонцы."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Забараніць доступ"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Дазволіць доступ"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Неназваная папка"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s замацавана на панэлі праграм."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s адмацавана ад панэлі праграм."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Адкрытыя вокны"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Новае акно"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Паказаць падрабязнасці"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Выйсці"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Адмацаваць"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Замацаваць на панэлі праграм"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Запусціць з выкарыстаннем інтэграванай відэакарты"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Запусціць з выкарыстаннем дыскрэтнай відэакарты"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Выберыце аўдыяпрыладу"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Налады гуку"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Навушнікі"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Гарнітура"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Мікрафон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Змяніць фон…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Налады дысплэя"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Налады"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Н"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "А"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ч"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "С"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Папярэдні месяц"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Наступны месяц"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Тыдзень %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Няма апавяшчэнняў"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Не турбаваць"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Ачысціць"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» не адказвае."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "Вы можаце пачакаць адказу або прымусова завяршыць працу праграмы."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Прымусова завяршыць"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Пачакаць"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Падлучаны знешні дыск"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Адлучаны знешні дыск"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Немагчыма разблакіраваць том"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Усталяваная версія udisks не падтрымлівае налады PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Адкрыць праз %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Таксама можна злучыцца, націснуўшы кнопку «WPS» на маршрутызатары."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Злучыцца"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Ключ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Пароль прыватнага ключа"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Ідэнтычнасць"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Сэрвіс"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Патрабуецца праверка сапраўднасці"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Для доступу да бесправадной сеткі «%s» патрабуецца пароль або ключ "
+"шыфравання."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Правадны доступ, праверка сапраўднасці 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Назва сеткі"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL-аўтэнтыфікацыя"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Патрабуецца PIN-код"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Прыладзе шырокапалоснай мабільнай сеткі патрабуецца PIN-код"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Каб злучыцца з «%s», патрабуецца пароль."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Менеджар сетак"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Пароль VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Патрабуецца праверка сапраўднасці"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Адміністратар"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Пацвердзіць"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Не ўдалося пацвердзіць сапраўднасць. Паўтарыце спробу."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s змяніў імя на %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Вокны"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Паказаць праграмы"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Панэль праграм"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Сёння"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Заўтра"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Увесь дзень"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Няма падзей"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Дадаць сусветныя гадзіннікі…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Сусветныя гадзіннікі"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Загрузка…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Злучыцеся з інтэрнэтам, каб атрымаць інфармацыю пра надвор'е"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Інфармацыя пра надвор'е зараз недаступна"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Надвор'е"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Выбраць месцазнаходжанне…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Завяршэнне сеанса %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Завяршэнне сеанса"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Сеанс карыстальніка %s аўтаматычна завершыцца праз %d секунду."
+msgstr[1] "Сеанс карыстальніка %s аўтаматычна завершыцца праз %d секунды."
+msgstr[2] "Сеанс карыстальніка %s аўтаматычна завершыцца праз %d секунд."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Ваш сеанс аўтаматычна завершыцца праз %d секунду."
+msgstr[1] "Ваш сеанс аўтаматычна завершыцца праз %d секунды."
+msgstr[2] "Ваш сеанс аўтаматычна завершыцца праз %d секунд."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Выйсці"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Выключэнне камп'ютара"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Усталяваць абнаўленні і выключыць камп'ютар"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Сістэма аўтаматычна выключыцца праз %d секунду."
+msgstr[1] "Сістэма аўтаматычна выключыцца праз %d секунды."
+msgstr[2] "Сістэма аўтаматычна выключыцца праз %d секунд."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Усталяваць адкладзеныя абнаўленні ПЗ"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Выключыць"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Перазапуск"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Усталяваць абнаўленні і перазапусціць"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Сістэма аўтаматычна перазапусціцца праз %d секунду."
+msgstr[1] "Сістэма аўтаматычна перазапусціцца праз %d секунды."
+msgstr[2] "Сістэма аўтаматычна перазапусціцца праз %d секунд."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Перазапусціць"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Перазапусціць і ўсталяваць абнаўленні"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Сістэма аўтаматычна перазапусціцца і ўсталюе абнаўленні праз %d секунду."
+msgstr[1] ""
+"Сістэма аўтаматычна перазапусціцца і ўсталюе абнаўленні праз %d секунды."
+msgstr[2] ""
+"Сістэма аўтаматычна перазапусціцца і ўсталюе абнаўленні праз %d секунд."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Перазапусціць і ўсталяваць"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Усталяваць і выключыць камп'ютар"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Выключыць камп'ютар пасля ўсталявання абнаўленняў"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Перазапусціць і ўсталяваць абнаўленне"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s будзе ўсталявана пасля перазапуску. Абнаўленне можа заняць шмат часу, "
+"пераканайцеся, што вы маеце рэзервовую копію, а камп'ютар працуе ад "
+"электрасеткі."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Нізкі зарад акумулятара. Падключыце зарадную прыладу перад усталяваннем "
+"абнаўленняў."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Некаторыя праграмы заняты або маюць незахаваныя вынікі працы"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Іншыя карыстальнікі не завяршылі сеанс"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Параметры запуску"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (аддалены сеанс)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (кансоль)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Усталяваць"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Усталяваць пашырэнне"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Спампаваць і ўсталяваць «%s» з extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Даступны абнаўленні пашырэнняў"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Абнаўленні пашырэнняў гатовыя да ўсталявання."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Дазволіць блакіраванне спалучэнняў клавіш"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Праграма %s спрабуе заблакіраваць спалучэнні клавіш"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Праграма спрабуе заблакіраваць спалучэнні клавіш"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Каб аднавіць спалучэнні клавіш, націсніце %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Забараніць"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Дазволіць"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Павольныя клавішы ўключаны"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Павольныя клавішы выключаны"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Вы на працягу васьмі секунд утрымлівалі клавішу Shift націснутай. Гэта "
+"спалучэнне клавіш уключае функцыю «павольныя клавішы», якая ўплывае на працу "
+"клавіятуры."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Ліпкія клавішы ўключаны"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Ліпкія клавішы выключаны"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Вы націснулі клавішу Shift пяць разоў запар. Гэта спалучэнні клавіш уключае "
+"функцыю «ліпкія клавішы», якая ўплывае на працу клавіятуры."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Вы націснулі дзве клавішы адначасова або клавішу Shift пяць разоў запар. "
+"Гэта дзеянне выключае функцыю «ліпкія клавішы», якая ўплывае на працу "
+"клавіятуры."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Пакінуць уключанай"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Уключыць"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Выключыць"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Пакінуць выключанай"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Налады рэгіёну і мовы"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Няма ўсталяваных пашырэнняў"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s не генеруе ніякіх памылак."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Схаваць памылкі"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Паказаць памылкі"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Уключана"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Выключана"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Памылка"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Састарэла"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Спампоўванне"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Зыходны код"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Вэб-старонка"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Сістэма пераведзена ў небяспечны рэжым"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Цяпер праграмы маюць неабмежаваны доступ"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Адрабіць"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Увайшоў як прывілеяваны карыстальнік"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"У мэтах бяспекі неабходна пазбягаць запуску сеанса ў якасці прывілеяванага "
+"карыстальніка. Калі магчыма, увайдзіце як звычайны карыстальнік."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Блакіраванне экрана адключана"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Для блакіравання экрана патрабуецца GNOME display manager."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Сістэмная інфармацыя"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Невядомы выканаўца"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Невядомая назва"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Увядзіце пошукавы запыт"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Праграмы"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Агляд"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Новае спалучэнні клавіш…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Вызначана праграмай"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Паказваць даведку на экране"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Пераключыць манітор"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Прызначыць клавішу"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Гатова"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Рэдагаваць…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Няма"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Націсніце кнопку, каб наладзіць"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Націсніце Esc, каб выйсці"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Націсніце любую клавішу, каб выйсці"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Дзейнасць"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Сістэма"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Верхняя панэль"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Выканаць каманду"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Націсніце ESC, каб выйсці"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Перазапуск недаступны на Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Перазапуск…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME патрабуе заблакіраваць экран"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Не ўдалося заблакіраваць"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Блакіраванне спынена праграмай"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Вылучэнне"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Вылучэнне вобласці"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Экран"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Выбар экрана"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Акно"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Выбар акна"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Здымак экрана / Запіс экрана"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Паказваць указальнік мышы"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Запісы экрана"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Запіс экрана ад %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Здымак экрана"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Запіс экрана зроблены"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Націсніце сюды, каб праглядзець відэа."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Паказаць праз «Файлы»"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Здымак экрана ад %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Здымак экрана захоплены"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Вы можаце ўставіць выяву з буфера абмену."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Здымак экрана атрыманы"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Пошук…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Нічога не знойдзена."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "Яшчэ %d"
+msgstr[1] "Яшчэ %d"
+msgstr[2] "Яшчэ %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Пошук"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Капіяваць"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Уставіць"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Паказаць тэкст"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Схаваць тэкст"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock уключаны."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Схаваны том"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Сістэмны том Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Выкарыстоўвае файлы-ключы"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Каб разблакіраваць том, які выкарыстоўвае файлы-ключы, скарыстайцеся замест "
+"іх утылітай <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Нумар PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Запомніць пароль"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Разблакіраваць"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Адкрыць %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM павінен быць лічбавым або пустым."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Не ўдалося запусціць %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Не ўдалося знайсці праграму %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Спецыяльныя магчымасці"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Высокая кантраснасць"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Маштабаванне"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Чытанне з экрана"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Экранная клавіятура"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Візуальныя сігналы"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Ліпкія клавішы"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Павольныя клавішы"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Пругкія клавішы"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Кіраванне ўказальнікам мышы"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Вялікі тэкст"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Аўтапаварот"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Яркасць"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Цёмны рэжым"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Адзінарнае націсканне"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Падвойнае націсканне"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Перацягванне"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Націсканне правай кнопкай"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Націсканне пры спыненні ўказальніка"
+
+#: js/ui/status/keyboard.js:833
+msgid "Keyboard"
+msgstr "Клавіятура"
+
+#: js/ui/status/keyboard.js:850
+msgid "Show Keyboard Layout"
+msgstr "Паказаць раскладку клавіятуры"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Дазволіць доступ да месцазнаходжання"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Праграма %s спрабуе атрымаць доступ да вашага месцазнаходжання"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Доступ да месцазнаходжанне заўсёды можна змяніць праз налады прыватнасці."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<невядома>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Адлучыцца ад %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Злучыцца з %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Хот-спот %s"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Начное святло"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Прадукцыйнасць"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Збалансаваны"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Энергазберажэнне"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Профілі электрасілкавання"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Спыніць запіс экрана"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Спыніць супольны доступ да экрана"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Рэжым палёту"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Зрабіць экранны здымак"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Меню выключэння"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Прыпыніць працу"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Перазапуск…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Выключыць…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Выйсці…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Змяніць карыстальніка…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Заблакіраваць экран"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Невядомая прылада Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Падчас вашай адсутнасці была выяўлена новая прылада. Каб пачаць ёй "
+"карыстацца, адлучыце і зноў падлучыце прыладу."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Несанкцыянаваная прылада Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Выяўлена новая прылада. Патрабуецца аўтарызацыя ад адміністратара."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Памылка аўтарызацыі Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Не ўдалося аўтарызаваць прыладу Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Гучнасць зменена"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Гучнасць"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Аўдыявыхад"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Аўдыяўваход"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Адлюстраванне"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Аб'яднаць дысплэі"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Толькі знешні"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Толькі ўбудаваны"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Правядзіце пальцам, каб разблакіраваць"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Каб разблакіраваць, націсніце кнопку мышы ці клавіятуры"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Разблакіраваць акно"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Увайсці як іншы карыстальнік"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Вітаем у GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+"Калі хочаце азнаёміцца з новымі магчымасцямі, прапануем вам кароткі агляд."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Не, дзякуй"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Азнаёміцца"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» гатова"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Захаваць гэтыя налады дысплэя?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Скінуць змены налад"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Захаваць змены"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Змены налад будуць скінуты праз %d секунду"
+msgstr[1] "Змены налад будуць скінуты праз %d секунды"
+msgstr[2] "Змены налад будуць скінуты праз %d секунд"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Схаваць"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Аднавіць"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Разгарнуць"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Перамясціць"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Змяніць памер"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Перамясціць панэль загалоўка на экран"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Заўсёды зверху"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Заўсёды на бачнай працоўнай прасторы"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Перамясціць на працоўную прастору лявей"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Перамясціць на працоўную прастору правей"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Перамясціць на працоўную прастору вышэй"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Перамясціць на працоўную прастору ніжэй"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Перамясціць на манітор вышэй"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Перамясціць на манітор ніжэй"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Перамясціць на манітор лявей"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Перамясціць на манітор правей"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Закрыць"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Каляндар Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Вывесці нумар версіі"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Рэжым, які выкарыстоўвае GDM для экрана ўваходу"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Выкарыстоўваць вызначаны рэжым, напрыклад, «gdm» для экрана ўваходу"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Спіс магчымых рэжымаў"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Невядомая"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Не ўдалося запусціць «%s»"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Паролі не супадаюць."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Пароль не можа быць пустым"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Праверкі сапраўднасці адхілена карыстальнікам"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Пашырэнні"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Кіраванне пашырэннямі GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Праект GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Пашырэнні GNOME ажыццяўляюць абнаўленне і змяненне параметраў пашырэнняў, "
+"выдаленне або адключэнне непажаданых пашырэнняў."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Наладзіць пашырэнні абалонкі GNOME"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Няма супадзенняў"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Выдаліць «%s»?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Калі выдаліць пашырэнне і потым уключыць яго зноў, спатрэбіцца паўторнае "
+"спампоўванне"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Выдаліць"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Launchpad translators"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Падчас наступнага ўваходу ў сістэму абновіцца %d пашырэнне."
+msgstr[1] "Падчас наступнага ўваходу ў сістэму абновіцца %d пашырэнні."
+msgstr[2] "Падчас наступнага ўваходу ў сістэму абновіцца %d пашырэнняў."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Пашырэнне несумяшчальна з бягучай версіяй GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Пашырэнне змяшчае памылку"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Пашырэнне можа быць абноўлена"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Вэб-сайт"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Выдаліць…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Даведка"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Пра пашырэнні"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Пашырэнні могуць выклікаць праблемы з прадукцыйнасцю і стабільнасцю. "
+"Адключыце пашырэнні, калі вы сутыкнуліся з праблемамі ў сваёй сістэме."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Усталяваныя ўручную"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Каб знайсці і дадаць пашырэнні, наведайце <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Убудаваныя"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Няма ўсталяваных пашырэнняў"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"На жаль, немагчыма атрымаць спіс усталяваных пашырэнняў. Праверце, ці "
+"ўвайшлі вы ў GNOME і паўтарыце спробу."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Даступны абнаўленні пашырэнняў"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Новае пашырэнне створана ў %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Назва — кароткая (лепш за ўсё апісальная) фраза ці слова.\n"
+"Прыклады: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Назва"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Апісанне — тлумачэнне адным сказам, што робіць ваша пашырэнне.\n"
+"Прыклады: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Апісанне"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID — усеагульны ўнікальны ідэнтыфікатар вашага пашырэння.\n"
+"Ён павінен мець фармат адраса электроннай пошты (nazva@jankupala.pryklad."
+"com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Выберыце адзін з даступных шаблонаў:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Шаблон"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Унікальны ідэнтыфікатар новага пашырэння"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "НАЗВА"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Назва новага пашырэння, якую бачыць карыстальнік"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "АПІСАННЕ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Кароткае апісанне, што робіць пашырэнне"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ШАБЛОН"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Шаблон для новых пашырэнняў"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Інтэрактыўны ўвод інфармацыі аб пашырэнні"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Стварыць новае пашырэнне"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Невядомыя аргументы"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Патрабуецца UUID, назва і апісанне"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Не ўдалося злучыцца з абалонкай GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Пашырэнне «%s» не існуе\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Адключыць пашырэнне"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID не ўказаны"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Указана больш за адзін UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Уключыць пашырэнне"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Пашырэнне «%s» не існуе\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Паказаць звесткі аб пашырэнні"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Перазапісаць існуючае пашырэнне"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "ПАКЕТ_ПАШЫРЭННЯ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Усталяваць пакет пашырэння"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Не ўказаны пакет пашырэння"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Указана больш за адзін пакет пашырэння"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Паказаць пашырэнні ўсталяваныя карыстальнікам"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Паказаць пашырэнні ўсталяваныя сістэмай"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Паказаць уключаныя пашырэнні"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Паказаць адключаныя пашырэнні"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Паказаць пашырэнні з параметрамі"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Паказаць пашырэнні з абнаўленнямі"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Вывесці звесткі аб пашырэнні"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Спіс усталяваных пашырэнняў"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ФАЙЛ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Дадатковая крыніца для ўключэння ў пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "СХЕМА"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Схема GSettings якую трэба ўключыць"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "КАТАЛОГ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Каталог, у якім знаходзяцца пераклады"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ДАМЕН"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Дамен gettext, які выкарыстоўваецца для перакладу"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Перазапісаць існуючы пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Каталог, у якім будзе створаны пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ЗЫХОДНЫ_КАТАЛОГ"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Стварыць пакет пашырэння"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Указана больш за адзін зыходны каталог"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "У пашырэння «%s» няма параметраў\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Не ўдалося адкрыць параметры пашырэння «%s»: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Адкрывае параметры пашырэння"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Скінуць пашырэнне"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Нельга выдаляць сістэмныя пашырэнні\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Не ўдалося выдаліць «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Выдаліць пашырэнне"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Не выводзіць паведамленні пра памылкі"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Шлях"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL-адрас"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Першы аўтар"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Версія"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Рэгіён"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» не прымае аргументаў"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Выкарыстанне:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Вывесці звесткі пра версію і выйсці."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "КАМАНДА"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[АРГУМЕНТЫ…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Каманды:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Вывесці даведку"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Уключыць пашырэнне"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Адключыць пашырэнне"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Скінуць пашырэнне"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Выдаліць пашырэнне"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Спіс пашырэнняў"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Паказаць звесткі аб пашырэнні"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Адкрыць параметры пашырэння"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Стварыць пашырэнне"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Запакаваць пашырэнне"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Усталяваць пакет пашырэння"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Выкарыстоўвайце «%s», каб атрымаць падрабязную даведку.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Просты"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Пустое пашырэнне"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Індыкатар"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Дадаць значок у верхнюю панэль"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u выхад"
+msgstr[1] "%u выхады"
+msgstr[2] "%u выхадаў"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u аўдыяўваход"
+msgstr[1] "%u аўдыяўваходы"
+msgstr[2] "%u аўдыяўваходаў"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Сістэмныя гукі"
diff --git a/po/bg.po b/po/bg.po
new file mode 100644
index 0000000..22646ab
--- /dev/null
+++ b/po/bg.po
@@ -0,0 +1,3171 @@
+# Bulgarian translation of gnome-shell po-file.
+# Copyright (C) 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+# Copyright (C) 2015, 2016, 2017 Free Software Foundation, Inc.
+# Copyright (C) 2021, 2022 Alexander Shopov.
+# This file is distributed under the same license as the gnome-shell package.
+# Ivaylo Valkov <ivaylo@e-valkov.org>, 2010, 2011, 2012, 2013, 2014.
+# Alexander Shopov <ash@kambanaria.org>, 2012, 2015, 2016, 2017, 2021, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell main\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-20 09:42+0000\n"
+"PO-Revision-Date: 2022-10-02 18:34+0200\n"
+"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
+"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
+"Language: bg\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Стартери"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Стартиране на любима програма 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Стартиране на любима програма 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Стартиране на любима програма 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Стартиране на любима програма 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Стартиране на любима програма 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Стартиране на любима програма 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Стартиране на любима програма 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Стартиране на любима програма 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Стартиране на любима програма 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Снимки на екрана"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Интерактивно снимане на екрана"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Снимка на екрана"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Снимка на прозорец"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Интерактивен запис на екрана"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Системни"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Показване на списъка с известия"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Фокусиране на активното известие"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Показване на програмите"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Показване на всички програми"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Отваряне на менюто с програми"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Обвивка на GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Управление на прозорци и стартиране на програми"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Включване на вътрешни инструменти полезни за разработчици и изпитатели чрез "
+"Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Разрешаване на достъпа до вътрешни инструменти за наблюдение и изчистване "
+"грешки чрез Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Идентификатори на разширенията за включване"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Разширенията на „Обвивката на GNOME“ имат идентификатор (UUID). Този ключ "
+"съдържа списък на разширенията, които да се зареждат. Само разширения "
+"включени в списъка ще бъдат заредени. Можете да променяте този списък с "
+"методите на D-Bus — „EnableExtension“ и „DisableExtension“ на org.gnome."
+"Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Идентификатори на разширенията за изключване"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Разширенията на „Обвивката на GNOME“ имат идентификатор (UUID). Този ключ "
+"съдържа списък на разширенията, които да се изключат дори и да са част от "
+"текущия режим. Можете да променяте този списък с методите на D-Bus — "
+"„EnableExtension“ и „DisableExtension“ на org.gnome.Shell. Тази настройка е "
+"с превес над „enabled-estensions“."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Изключване на потребителските разширения"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Изключване на всички включени от потребителя разширения без промяна на "
+"настройката „enabled-extension“."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Изключване на проверката за съвместимостта на версиите на разширенията"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Обвивката на GNOME ще зарежда само разширения, които изрично са заявили, че "
+"поддържат текущата версия. Избирането на тази функция изключва проверката за "
+"съвместимост и зарежда разширенията независимо от версията."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Списък на идентификаторите на файловете във формат „.desktop“ за любими "
+"програми"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Програмите, които отговарят на тези идентификатор, ще бъдат показани в "
+"областта „Любими“."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "История на командата Alt-F2"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "История на прозореца на дебъгера"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Винаги да се показва „Изход“ в менюто на потребителя"
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Този ключ предефинира автоматичното скриване на обекта „Изход“ от менюто в "
+"режими с един потребител или единична сесия."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Дали да се запазват пароли за монтиране на шифрирани или отдалечени файлови "
+"системи"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Обвивката ще поиска парола, когато се монтира шифрирано устройство или "
+"отдалечена файлова система. Ще се предостави отметка „Запомняне на "
+"паролата“, ако е възможно запазването на паролата за по-късно използване."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Последно избран профил за захранване, различен от стандартния"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Някои системи разполагат с повече от два профила за захранване. За да работи "
+"превключването, този ключ съдържа последно избрания профил, който не е "
+"стандартния."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Последна версия, за която е показван прозореца „Добре дошли в GNOME“"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Ключът съдържа версията на GNOME, за която прозорецът „Добре дошли в GNOME“ "
+"е показван. Празен низ указва възможно най-стара версия, а прекалено голямо "
+"число ще сочи версия, която още не съществува. Ако попълнете такова голямо "
+"число, изключвате прозореца на практика."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Подредба на избора на програми"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Подредба на избора на програми. Всеки запис в масива е страница. Подредбата "
+"указва реда им в „Обвивката на GNOME“. Всеки запис на страница съдържа "
+"двойка „идентификатор на програма“ → „данни“. Текущо следните стойности се "
+"съхраняват като данни: • „position“: местоположение на иконата програмата на "
+"страницата"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Клавишна комбинация за менюто за програми"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Клавишна комбинация за отваряне на менюто за програми."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Клавишна комбинация за смяна на изгледа"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Клавишна комбинация за преминаване към сесия, към управлението на прозорци и "
+"към решетката на програмите"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Клавишна комбинация за преминаване към решетката на програмите, към "
+"управлението на прозорци и към сесия"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Клавишна комбинация за отваряне на изгледа „Показване на програмите“"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Клавишна комбинация за отваряне на изгледа „Показване на програмите“ за "
+"прегледа на дейностите."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Клавишна комбинация за отваряне на изгледа „Показване на програмите“"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Клавишна комбинация за отваряне на изгледа „Показване на програмите“."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Клавишна комбинация за показване/скриване на областта за известия"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Клавишна комбинация за показване/скриване на областта за известия."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Клавишна комбинация за фокусиране на текущото известие"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Клавишна комбинация за фокусиране на текущото известие."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Към програма 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Към програма 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Към програма 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Към програма 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Към програма 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Към програма 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Към програма 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Към програма 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Към програма 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Ограничаване на превключването до текущия работен плот."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ако е истина, само програмите с прозорци на текущият работен плот се "
+"превключват. В противен случай се превключва между всички програми."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Режим на иконите."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Определя как бутоните да се показват в областта за превключване. Възможните "
+"стойности са: „thumbnail-only“ (показване на миниатюра на прозореца), „app-"
+"icon-only“ (показване само на иконата на приложението) и „both“ (и двете)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ако е истина, само програмите с прозорци на текущият работен плот се "
+"превключват. В противен случай се превключва между всички програми."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Местоположения"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Местоположенията за световния часовник"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Автоматично местоположение"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Да се включи ли и текущото местоположение"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Местоположение"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Местоположение, за което да се показва прогноза на времето"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Прилепени модални диалогови прозорци"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Този ключ е с предимство пред ключа org.gnome.mutter, когато се използва "
+"GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Включване на прилепването и максимизирането на прозорците по границите на "
+"екрана"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Работните плотове се управляват динамично"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Работните плотове са само на основния монитор"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Отлагане на промените за фокусирането докато показалецът на мишката спре да "
+"се движи."
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Мрежов вход"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Нещо се обърка"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"За съжаление се появи проблем: настройките на разширението не може да се "
+"изведат. Препоръчваме ви да докладвате проблема на авторите му."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Технически детайли"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Уеб страница"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Към уеб страницата на разширението"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Отказване"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Парола"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Избор на сесия"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Липсва в списъка?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(напр. потребител или %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Потребител"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Екран за идентификация"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Грешка при удостоверяване"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(или прекарайте пръст през сензора за отпечатъци)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(или поставете пръст на сензора за отпечатъци)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Изключване"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;изключване;спиране"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Рестартиране"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;рестартиране"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Заключване на екрана"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen;заключване"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Изход"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;излизане;изход"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Приспиване"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "приспиване"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Смяна на потребител"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "смяна на потребител"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;unlock orientation;screen;rotation;ориентация;завъртане;"
+"обръщане;завъртане на екрана"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Снимка на екрана"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;снимка;шот;каст;записване;екран;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Завъртане на екрана, когато е отключен"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Завъртане на екрана, когато е заключен"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Командата не беше открита"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Неуспешно анализиране на командата:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Неуспешно изпълнение на „%s“:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Сега"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "преди %d минута"
+msgstr[1] "преди %d минути"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "преди %d час"
+msgstr[1] "преди %d часа"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Вчера"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "преди %d ден"
+msgstr[1] "преди %d дни"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "преди %d седмица"
+msgstr[1] "преди %d седмици"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "преди %d месец"
+msgstr[1] "преди %d месеца"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "преди %d година"
+msgstr[1] "преди %d години"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Вчера, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d% %B, %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Вчера, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l:%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l:%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l:%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Вход в безжична мрежа"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Връзката към тази безжична мрежа не е шифрирана. Информацията и паролите, "
+"които въведете на тази страница, са потенциално видими за всички наоколо."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Отказване на достъп"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Позволяване на достъп"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Папка без име"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "„%s“ е добавена в „Любими“"
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "„%s“ е махната от „Любими“"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Отворени прозорци"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Нов прозорец"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Показване на подробности"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Спиране на програмата"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Махане"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Добавяне в „Любими“"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Стартиране с вградената графична карта"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Стартиране с отделната графична карта"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Избор на устройство за аудио"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Настройки на звука"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Слушалки"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Малки слушалки"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Смяна на фона…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Настройки на екрана"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Настройки"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Н"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "В"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ч"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "С"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Предния месец"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Следващия месец"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V-а седмица"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Няма известия"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Не безпокойте!"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Изчистване"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "Програмата „%s“ не отговаря."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Може да изчакате известно време за отговор или принудително да спрете "
+"програмата."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Принудително спиране"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Изчакване"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Закачен е външен диск"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Отдалеченият диск е разкачен"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Неуспешно отключване на тома"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+"Инсталираната версия на „udisks“ не поддържа задаването на брой итерации "
+"(PIM)"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Отваряне с „%s“"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Може да се свържете и като натиснете бутона „WPS“ на рутъра."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Свързване"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Ключ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Парола за частния ключ"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Самоличност"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Услуга"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Изисква се удостоверяване"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"За достъп до безжичната мрежа „%s“ са необходими пароли или криптирани "
+"ключове."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Жична идентификация 802.1Х"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Име на мрежа"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Удостоверяване за DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Необходим е ПИН"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Мобилното устройство изисква ПИН"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "ПИН"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "За свързване към „%s“ се изисква парола."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Управление на мрежата"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Парола за ВЧМ"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Необходимо е удостоверяване"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Администратор"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Удостоверяване"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Неуспешно действие. Опитайте отново."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "„%s“ в момента е познат като „%s“"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Прозорци"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Показване на програмите"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Любими"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Днес"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Утре"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Цял ден"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d %b"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Няма събития"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Добавяне на световен часовник…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Световен часовник"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Зареждане…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "За метеорологичната обстановка трябва да сте онлайн."
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "В момента метеорологичната информация не е налична."
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Прогноза за времето"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Избор на местоположения…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Изход на „%s“"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Изход"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+"Потребителят „%s“ ще излезе от системата автоматично след %d секунда."
+msgstr[1] ""
+"Потребителят „%s“ ще излезе от системата автоматично след %d секунди."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Ще излезете от системата автоматично след %d секунда."
+msgstr[1] "Ще излезете от системата автоматично след %d секунди."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Изход"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Изключване"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Инсталиране на обновленията и изключване"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Системата ще се изключи автоматично след %d секунда."
+msgstr[1] "Системата ще се изключи автоматично след %d секунди."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Инсталиране на изчакващите обновления"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Изключване"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Рестартиране"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Инсталиране на обновленията и рестартиране"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Системата ще се рестартира автоматично след %d секунда."
+msgstr[1] "Системата ще се рестартира автоматично след %d секунди."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Рестартиране"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Рестартиране и инсталиране на обновленията"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Системата ще се рестартира автоматично и ще инсталира обновления след %d "
+"секунда."
+msgstr[1] ""
+"Системата ще се рестартира автоматично и ще инсталира обновления след %d "
+"секунди."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Рестартиране и инсталиране"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Инсталиране и изключване"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Изключване на компютъра след инсталиране на обновленията"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Рестартиране и инсталиране на обновленията"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"След рестартиране ще се инсталира %s %s. Обновяването отнема доста време, "
+"проверете дали имате резервно копие на данните си и дали компютърът има "
+"надежден източник на захранване."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Нисък заряд в батериите — включете захранването, преди да инсталирате "
+"обновленията."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Някои програми са заети или имат незапазени промени"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Има други влезли потребители"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Настройки за стартиране"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (отдалечен)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (конзола)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Инсталиране"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Инсталиране на разширение"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Да се изтегли и инсталира ли „%s“ от from extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Има обновления на разширенията"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Обновленията са готови за инсталиране."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Клавишните комбинации да може да се изключват"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Програмата „%s“ иска временно да изключи клавишните комбинации"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Програма иска временно да изключи клавишните комбинации"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Може да ги възстановите като натиснете комбинацията %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Забраняване"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Позволяване"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Бавни клавиши включени"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Бавни клавиши изключени"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Задържахте клавиша Shift натиснат за 8 секунди. Това включва „Бавни клавиши“ "
+"— настройка, която променя начина на работа на клавиатурата."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Лепкави клавиши включени"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Лепкави клавиши изключени"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Натиснахте клавиша Shift 5 пъти последователно. Това включва „Лепкави "
+"клавиши“ — настройка, която променя начина на работа на клавиатурата."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Току що натиснахте два клавиша едновременно или натиснахте клавиша Shift 5 "
+"пъти последователно. Това изключва „Лепкави клавиши“ — настройка, която "
+"променя начина на работа на клавиатурата."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Оставяне включено"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Включване"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Изключване"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Оставяне изключено"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Район и език"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Няма инсталирани разширения"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "Разширението %s не е обозначило никакви грешки."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Скриване на грешките"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Показване на грешките"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Включено"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Изключено"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Грешка"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Остаряло"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Изтегляне"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Преглед на изходния код"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Домашна страница"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Системата е в необезапасен режим"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Програмите работят с неограничен достъп"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Отмяна"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Влезли сте като администратор"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Избягвайте да стартирате сесия като администратор заради сигурността. "
+"Влезте в системата като обикновен потребител, ако е възможно."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Заключването на екрана е изключено"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+"Заключването на екрана изисква програмата за управление на екрани на GNOME — "
+"gdm."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Информация за системата"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Неизвестен изпълнител"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Неизвестно заглавие"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Текст за търсене"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Програми"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Преглед"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Нова комбинация…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Налични програми"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Извеждане на помощта"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Смяна на екран"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Задаване на клавишна комбинация"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Готово"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Редактиране…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Няма"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Натиснете клавиш за настройване"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Натиснете Esc за отмяна"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Натиснете произволен клавиш за изход"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Дейности"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Системни"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Горна лента"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Изпълнение на команда"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Натиснете Esc за отмяна"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Рестартирането не е налично в Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Рестартиране…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Екранът трябва да бъде заключен от GNOME"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Неуспешно заключване"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Заключването беше блокирано от програма"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Избор"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Избор на област"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Екран"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Избор на част от екрана"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Прозорeц"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Избор на прозорец"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Снимка/запис на екрана"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Включване на показалеца"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Записи на екрана"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Запис на екрана на %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Снимка на екрана"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Екранът е записан"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Натиснете, за да прегледате видеото."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Показване във „Файлове“"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Снимка на екрана на %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Екранът е заснет"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Изображението е в буфера за обмен и може да го поставите."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Екранът е заснет"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Търсене…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Няма съвпадения."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "още %d"
+msgstr[1] "още %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Търсене"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Копиране"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Поставяне"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Показване на текста"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Скриване на текста"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps Lock е натиснат."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Скрит дял"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Системен дял на Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Файл с ключ"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"За да отключите дял, който ползва ключове, ползвайте програмата <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Итерации (PIM)"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Запомняне на паролата"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Отключване"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Отваряне на „%s“"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "Броят итерации (PIM) трябва да е число или да е празно."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Неуспешно стартиране на „%s“"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Програмата „%s“ не може да се изтрие"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Достъпност"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Висок контраст"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Увеличаване"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Четец на екрана"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Екранна клавиатура"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Визуална помощ"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Лепкави клавиши"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Бавни клавиши"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Подскачащи клавиши"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Клавиши за мишка"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Едър текст"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Автоматично завъртане"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Яркост"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Тъмен режим"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Еднократно натискане"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Двукратно натискане"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Влачене"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Натискане на 2<sup>я</sup> бутон"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Натискане чрез задържане"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Клавиатура"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Показване на клавиатурната подредба"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Позволяване на достъп до местоположението"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Заявка от „%s“ за местоположението ви"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Може да промените правата за достъп до местоположението през настройките за "
+"лични данни."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<неизвестно>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Прекъсване на връзката към „%s“"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Свързване към „%s“"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Безжична точка за достъп „%s“"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Нощен режим"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Бързина"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Баланс"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Енергоспестяване"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Профили за захранване"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Край на записа на екрана"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Край на споделянето на екрана"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Самолетeн режим"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Снимка на екрана"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Меню за изключване"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Приспиване"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Рестартиране…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Изключване"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Изход…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Смяна на потребител…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Заключване на екрана"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Непознато устройство с Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Засечено е ново устройство докато отсъствахте. За да го ползвате, извадете "
+"го и го поставете наново."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Неодобрено устройство с Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Засечено е ново устройство, което трябва да се одобри от администратор."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Грешка при одобряване на устройство с Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Устройството с Thunderbolt не може да се одобри: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Променена сила на звука"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Сила на звука"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Аудио изход"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Аудио вход"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Еднакво изображение"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Обединяване на екраните"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Външни екрани"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Вградени екрани"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "За отключване прокарайте пръст"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "За отключване натиснете клавиш"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Отключване на прозореца"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Влизане като друг потребител"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Добре дошли в GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Въвеждащата демонстрация ще ви помогне да се ориентирате."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Няма нужда"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Демонстрация"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Програмата „%s“ е готова за употреба"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Запазване на настройките?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Връщане на предишните"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Запазване на промените"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Настройките ще се възстановят след %d секунда"
+msgstr[1] "Настройките ще се възстановят след %d секунди"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Скриване"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Възстановяване"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Максимизиране"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Преместване"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Оразмеряване"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Преместване на лентата за заглавието по екрана"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Винаги отгоре"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Винаги на видимия работен плот"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Преместване на левия работен плот"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Преместване на десния работен плот"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Преместване на горния работен плот"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Преместване на долния работен плот"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Преместване на горния монитор"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Преместване на долния монитор"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Преместване на левия монитор"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Преместване на десния монитор"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Затваряне"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Календар на Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Показване на версията"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Режим на GDM за екрана за влизане"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Използване на определен режим за екрана за идентификация, напр. „gdm“"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Списък на достъпните режими"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Неизвестна"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Неуспешно стартиране на „%s“"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Паролите не съвпадат."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Не може да не попълнете паролата"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Прозорецът за упълномощаване беше затворен от потребителя"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Разширения"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Управление на разширенията на GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Проект GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"С „Разширенията на GNOME“ може да променяте настройките им, да инсталирате "
+"нови, да деинсталирате или изключвате нежеланите разширения."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Настройки на разширенията на обвивката"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Няма открити"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Да се премахне ли „%s“?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Ако премахнете разширение, ще трябва да го изтеглите наново, за да го "
+"включите."
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Премахване"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Ивайло Вълков &lt;<a href='mailto:ivaylo@e-valkov.org'>ivaylo@e-valkov.org</"
+"a>&gt;\n"
+"Александър Шопов &lt;<a href='mailto:ash@kambanaria.org'>ash@kambanaria.org</"
+"a>&gt;\n"
+"\n"
+"\n"
+"Проектът за превод на GNOME има нужда от подкрепа.\n"
+"Научете повече за нас на <a href='https://fsa-bg.org/project/gtp'>уеб сайта</"
+"a> ни.\n"
+"Докладвайте за грешки в превода в <a href='https://fsa-bg.org/project/gtp/"
+"newticket'>съответния раздел</a>."
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "При следващото влизане ще се обнови %d разширение."
+msgstr[1] "При следващото влизане ще се обновят %d разширения."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Разширението не е съвместимо с текущата версия на GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Грешка от разширението"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Разширението може да се обнови"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Уеб страница"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Премахване…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Помощ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Относно разширенията"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Разширенията понякога създават проблеми с производителността и надеждността. "
+"Изключете ги, ако се натъкнете на това."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Ръчно инсталирано"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"За търсене и добавяне на разширения посетете <a href='https://extensions."
+"gnome.org'>extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Вградено"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Не са инсталирани разширения"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"За съжаление списъкът с инсталираните разширения не може да се получи. "
+"Влезте наново в GNOME и пробвайте пак."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Налични са обновления на разширенията"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Новото разширение бе успешно създадено в „%s“.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Името трябва да е кратко и описателно.\n"
+"Примери: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Име"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Описанието трябва да обясни какво прави разширението с едно изречение.\n"
+"Примери: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Описание"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"Това е уникален идентификатор (UUID) на разширението.\n"
+"Форматът е като на адрес за е-поща (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Избор на някой он съществуващите шаблони:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Шаблон"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Уникален ИДЕНТИФИКАТОР на разширението"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "ИМЕ"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "ИМЕ на приложението"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "ОПИСАНИЕ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Кратко ОПИСАНИЕ на функционалността на разширението"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ШАБЛОН"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Шаблон за новото разширение"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Интерактивно въвеждане на информацията за разширението"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Създаване на ново разширение"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Непознати аргументи"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Идентификаторът, името и описанието са задължителни"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Няма връзка с обвивката на GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Разширението „%s“ не съществува\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Изключване на разширение"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Не е зададен идентификатор"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Зададен е повече от един идентификатор"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Включване на разширение"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Разширението „%s“ не съществува\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Извеждане на информация за разширение"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Презаписване на съществуващо разширение"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "КОМПЛЕКТ_РАЗШИРЕНИЯ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Инсталиране на комплект разширения"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Не е указан комплект разширения"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Указани са повече от един комплекти разширения"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Потребителски разширения на обвивката"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Системни разширения на обвивката"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Включени разширения"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Изключени разширения"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Разширения с настройки"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Разширения с обновления"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Подробна информация за разширение"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Списък с инсталираните приложения"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ФАЙЛ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Допълнително да се включи в комплекта"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "СХЕМА"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "СХЕМА на GSettings, която да се включи"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ДИРЕКТОРИЯ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "ДИРЕКТОРИЯ, в която са преводите"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "КОНТЕКСТ"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "КОНТЕКСТ на gettext за преводите"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Презаписване на съществуващ пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "В коя директория да се създаде пакетът"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ДИРЕКТОРИЯ_ИЗТОЧНИК"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Създаване на комплект разширения"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Указана е повече от една директория-източник"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Разширението „%s“ няма настройки\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Настройките на разширението „%s“ не може да се отворят: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Отваряне на настройките на разширението"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Нулиране на разширение"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Системните разширения не може да се деинсталират\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Неуспешно деинсталиране на „%s“\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Деинсталиране на разширение"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Без извеждане на грешки"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Път"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "Адрес"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Първоначален автор"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Версия"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Състояние"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "Опцията „version“ не приема аргументи"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Употреба:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Извеждане на версията и изход."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "КОМАНДА"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[АРГУМЕНТ…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Команди:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Извеждане на помощта"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Включване на разширение"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Изключване на разширение"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Нулиране на разширение"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Деинсталиране на разширение"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Списък на разширенията"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Информация за разширение"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Настройки на разширение"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Създаване на разширение"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Пакетиране на разширение"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Инсталиране на комплект разширения"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "За подробна помощ ползвайте „%s“.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Примерно"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Примерно разширение"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Индикатор"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Икона в горната лента"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u изход"
+msgstr[1] "%u изхода"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u вход"
+msgstr[1] "%u входа"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Системни звуци"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "Дали има асоциирани устройства по Bluetooth към стандартното"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Менюто за Bluetooth ще се показва при включен адаптер или при асоциирани "
+#~ "към стандартното устройства. Ако в някой момент няма асоциирани към "
+#~ "стандартното устройство, състоянието се занулява."
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "„%s“: изключен"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "„%s“: свързан"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "„%s“: без управление"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "„%s“: свързване"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "„%s“: изисква идентификация"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Липсва фърмуер за „%s“"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "„%s“: недостъпен"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "„%s“: неуспешна връзка"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Настройки на жична връзка"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Настройки на мобилна широколентова връзка"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "„%s“: изключено устройство"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "„%s“: изключено"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Настройки на Bluetooth"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Свързване към Интернет"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Самолетният режим е включен"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Безжичната връзка е изключена при самолетен режим."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Изключване на самолетен режим"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Безжичната връзка е изключена"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr ""
+#~ "Безжичната връзка трябва да е включена, за да се свържете към мрежа."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Включване на безжичната връзка"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Безжични мрежи"
+
+#~ msgid "Select a network"
+#~ msgstr "Избор на мрежата"
+
+#~ msgid "No Networks"
+#~ msgstr "Липсват мрежи"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Изключване чрез физически бутон"
+
+#~ msgid "Select Network"
+#~ msgstr "Избор на мрежа"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Настройки на безжична връзка"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "„%s“: няма връзка"
+
+#~ msgid "unknown"
+#~ msgstr "неизвестно"
+
+#~ msgid "activating…"
+#~ msgstr "задействане…"
+
+#~ msgid "deactivating…"
+#~ msgstr "изключване…"
+
+#~ msgid "deactivated"
+#~ msgstr "изключено"
+
+#~ msgid "connecting…"
+#~ msgstr "свързване…"
+
+#~ msgid "authentication required"
+#~ msgstr "изисква се удостоверяване"
+
+#~ msgid "connection failed"
+#~ msgstr "връзката е неуспешна"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Настройки на ВЧМ (VPN)"
+
+#~ msgid "VPN"
+#~ msgstr "ВЧМ"
+
+#~ msgid "VPN Off"
+#~ msgstr "Без ВЧМ"
+
+#~ msgid "Network Settings"
+#~ msgstr "Настройки на мрежата"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s жична връзка"
+#~ msgstr[1] "%s жични връзки"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s безжична връзка"
+#~ msgstr[1] "%s безжични връзки"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s връзка по модем"
+#~ msgstr[1] "%s връзки по модем"
+
+#~ msgid "Connection failed"
+#~ msgstr "Връзката е неуспешна"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Неуспешна връзка към мрежата"
+
+#~ msgid "Power Settings"
+#~ msgstr "Настройки на захранването"
+
+#~ msgid "Lock"
+#~ msgstr "Заключване"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Изключване/Изход"
+
+#~ msgid "Log Out"
+#~ msgstr "Изход"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d свързано"
+#~ msgstr[1] "%d свързани"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth включен"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth изключен"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Местоположението е включено"
+
+#~ msgid "Disable"
+#~ msgstr "Изключване"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Настройки за поверителност"
+
+#~ msgid "Location In Use"
+#~ msgstr "Текущо местоположение"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Местоположението е изключено"
+
+#~ msgid "Enable"
+#~ msgstr "Включване"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Нощният режим е изключен"
+
+#~ msgid "Resume"
+#~ msgstr "Включване наново"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Изключване до утре сутрин"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Пълно зареждане"
+
+#~ msgid "Not Charging"
+#~ msgstr "Не се зарежда"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d:%02d до разреждане (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d:%02d до зареждане (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Споделен екран"
+
+#~ msgid "Turn off"
+#~ msgstr "Изключване"
diff --git a/po/bn.po b/po/bn.po
new file mode 100644
index 0000000..98e7728
--- /dev/null
+++ b/po/bn.po
@@ -0,0 +1,1203 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: bn\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2011-04-04 02:20+0000\n"
+"PO-Revision-Date: 2011-04-04 11:04+0600\n"
+"Last-Translator: Israt Jahan <israt@ankur.org.bd>\n"
+"Language-Team: Bengali <ankur-bd-l10n@googlegroups.com>\n"
+"Language: bn\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Translate Toolkit 1.8.0\n"
+"X-Language: bn_BD\n"
+"X-DamnedLies-Scope: partial\n"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "জিনোম সেল"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "উইন্ডো ব্যবস্থাপনা এবং অ্যাপ্লিকেশন চালু"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:1
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:2
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:3
+msgid "File extension used for storing the screencast"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:4
+msgid "Framerate used for recording screencasts."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:5
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should not be loaded."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:6
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:7
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:8
+msgid "If true, display date in the clock, in addition to time."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:9
+msgid "If true, display seconds in time."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:10
+msgid "If true, display the ISO week date in the calendar."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:11
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:13
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
+"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
+"at the optimal thread count on the system."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:14
+msgid "Show date in clock"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:15
+msgid "Show the week date in the calendar"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:16
+msgid "Show time with seconds"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:17
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:18
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:19
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:20
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:21
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:22
+#, fuzzy
+msgid "Uuids of extensions to disable"
+msgstr "কোনো এক্সটেনশন ইনস্টল করা হয়নি"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:23
+msgid "Whether to collect stats about applications usage"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:24
+msgid "disabled OpenSearch providers"
+msgstr ""
+
+#: ../js/misc/util.js:71
+msgid "Command not found"
+msgstr "কমান্ড পাওয়া যায়নি"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:98
+msgid "Could not parse command:"
+msgstr "কমান্ড পার্স করা যায়নি:"
+
+#: ../js/misc/util.js:106
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "'%s' কার্যকর করা ব্যর্থ:"
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:230
+msgid "All"
+msgstr "সব"
+
+#: ../js/ui/appDisplay.js:328
+msgid "APPLICATIONS"
+msgstr "অ্যাপ্লিকেশন"
+
+#: ../js/ui/appDisplay.js:354
+msgid "SETTINGS"
+msgstr "সেটিংস"
+
+#: ../js/ui/appDisplay.js:625
+msgid "New Window"
+msgstr "নতুন উইন্ডো"
+
+#: ../js/ui/appDisplay.js:628
+msgid "Remove from Favorites"
+msgstr "পছন্দ থেকে অপসারণ"
+
+#: ../js/ui/appDisplay.js:629
+msgid "Add to Favorites"
+msgstr "পছন্দে যোগ"
+
+#: ../js/ui/appFavorites.js:91
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "আপনার পছন্দে %s যোগ করা হয়েছে।"
+
+#: ../js/ui/appFavorites.js:122
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "আপনার পছন্দ থেকে %s বাদ হয়েছে।"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:66
+msgctxt "event list time"
+msgid "All Day"
+msgstr "পুরো দিন"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:71
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:78
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:118
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:120
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:122
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:124
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:126
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:128
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:130
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:143
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Su"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:145
+msgctxt "list monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:147
+msgctxt "list tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:149
+msgctxt "list wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:151
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Th"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:153
+msgctxt "list friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:155
+msgctxt "list saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:704
+msgid "Nothing Scheduled"
+msgstr "কোনো কিছুর সময় ঠিক করা হয়নি"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:490
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:723 ../js/ui/telepathyClient.js:493
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:733
+msgid "Today"
+msgstr "আজ"
+
+#: ../js/ui/calendar.js:737
+msgid "Tomorrow"
+msgstr "আগামীকাল"
+
+#: ../js/ui/calendar.js:746
+msgid "This week"
+msgstr "এ সপ্তাহ"
+
+#: ../js/ui/calendar.js:754
+msgid "Next week"
+msgstr "পরবর্তী সপ্তাহ"
+
+#: ../js/ui/dash.js:174 ../js/ui/messageTray.js:1007
+msgid "Remove"
+msgstr "অপসারণ"
+
+#: ../js/ui/dateMenu.js:91
+msgid "Date and Time Settings"
+msgstr "তারিখ ও সময় এর সেটিং"
+
+#: ../js/ui/dateMenu.js:111
+msgid "Open Calendar"
+msgstr "ক্যালেন্ডার খুলুন"
+
+#. Translators: This is the time format with date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:164
+msgid "%a %b %e, %R:%S"
+msgstr "%a %b %e, %R:%S"
+
+#: ../js/ui/dateMenu.js:165
+msgid "%a %b %e, %R"
+msgstr "%a %b %e, %R"
+
+#. Translators: This is the time format without date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:169
+msgid "%a %R:%S"
+msgstr "%a %R:%S"
+
+#: ../js/ui/dateMenu.js:170
+msgid "%a %R"
+msgstr "%a %R"
+
+#. Translators: This is a time format with date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:177
+msgid "%a %b %e, %l:%M:%S %p"
+msgstr "%a %b %e, %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:178
+msgid "%a %b %e, %l:%M %p"
+msgstr "%a %b %e, %l:%M %p"
+
+#. Translators: This is a time format without date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:182
+msgid "%a %l:%M:%S %p"
+msgstr "%a %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:183
+msgid "%a %l:%M %p"
+msgstr "%a %l:%M %p"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:194
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/docDisplay.js:19
+msgid "RECENT ITEMS"
+msgstr "সাম্প্রতিক আইটেম"
+
+#: ../js/ui/endSessionDialog.js:63
+#, c-format
+msgid "Log Out %s"
+msgstr "লগআউট %s"
+
+#: ../js/ui/endSessionDialog.js:64 ../js/ui/endSessionDialog.js:70
+msgid "Log Out"
+msgstr "লগআউট"
+
+#: ../js/ui/endSessionDialog.js:65
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr "লগআউট ক্লিক করে অ্যাপ্লিকেশন বন্ধ এবং সিস্টেম থেকে লগআউট।"
+
+#: ../js/ui/endSessionDialog.js:66
+#, c-format
+msgid "%s will be logged out automatically in %d seconds."
+msgstr "%s থেকে স্বয়ংক্রিয়ভাবে %d সেকেন্ডের মধ্যে লগআউট।"
+
+#: ../js/ui/endSessionDialog.js:67
+#, c-format
+msgid "You will be logged out automatically in %d seconds."
+msgstr "আপনি স্বয়ংক্রিয়ভাবে %d সেকেন্ডের মধ্যে লগআউট হবেন।"
+
+#: ../js/ui/endSessionDialog.js:68
+msgid "Logging out of the system."
+msgstr "সিস্টেম থেকে লগ আউট।"
+
+#: ../js/ui/endSessionDialog.js:75 ../js/ui/endSessionDialog.js:82
+#, fuzzy
+msgid "Power Off"
+msgstr "পাওয়ার বন্ধ..."
+
+#: ../js/ui/endSessionDialog.js:76
+#, fuzzy
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr "লগআউট ক্লিক করে অ্যাপ্লিকেশন বন্ধ এবং সিস্টেম থেকে লগআউট।"
+
+#: ../js/ui/endSessionDialog.js:77
+#, fuzzy, c-format
+msgid "The system will power off automatically in %d seconds."
+msgstr "সিস্টেম %d সেকেন্ডে স্বয়ংক্রিয়ভাবে রিস্টার্ট হবে।"
+
+#: ../js/ui/endSessionDialog.js:78
+#, fuzzy
+msgid "Powering off the system."
+msgstr "সিস্টেম থেকে লগ আউট।"
+
+#: ../js/ui/endSessionDialog.js:80 ../js/ui/endSessionDialog.js:88
+#: ../js/ui/endSessionDialog.js:93
+msgid "Restart"
+msgstr "রিস্টার্ট"
+
+#: ../js/ui/endSessionDialog.js:89
+msgid "Click Restart to quit these applications and restart the system."
+msgstr "এ অ্যাপ্লিকেশন বন্ধ করার জন্য রিস্টার্ট ক্লিক করে সিস্টেম রিস্টার্ট।"
+
+#: ../js/ui/endSessionDialog.js:90
+#, c-format
+msgid "The system will restart automatically in %d seconds."
+msgstr "সিস্টেম %d সেকেন্ডে স্বয়ংক্রিয়ভাবে রিস্টার্ট হবে।"
+
+#: ../js/ui/endSessionDialog.js:91
+msgid "Restarting the system."
+msgstr "সিস্টেম রিস্টার্ট করা হচ্ছে।"
+
+#: ../js/ui/endSessionDialog.js:417 ../js/ui/polkitAuthenticationAgent.js:172
+#: ../js/ui/status/bluetooth.js:466
+msgid "Cancel"
+msgstr "বাতিল"
+
+#: ../js/ui/lookingGlass.js:588
+msgid "No extensions installed"
+msgstr "কোনো এক্সটেনশন ইনস্টল করা হয়নি"
+
+#: ../js/ui/lookingGlass.js:625
+msgid "Enabled"
+msgstr "সক্রিয়"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:627 ../src/gvc/gvc-mixer-control.c:1091
+msgid "Disabled"
+msgstr "নিস্ক্রিয়"
+
+#: ../js/ui/lookingGlass.js:629
+msgid "Error"
+msgstr "ত্রুটি"
+
+#: ../js/ui/lookingGlass.js:631
+msgid "Out of date"
+msgstr "সময় শেষ"
+
+#: ../js/ui/lookingGlass.js:656
+msgid "View Source"
+msgstr "সোর্স দেখুন"
+
+#: ../js/ui/lookingGlass.js:662
+msgid "Web Page"
+msgstr "ওয়েব পেজ"
+
+#: ../js/ui/messageTray.js:1000
+msgid "Open"
+msgstr "খুলুন"
+
+#: ../js/ui/messageTray.js:2164
+msgid "System Information"
+msgstr "সিস্টেম তথ্য"
+
+#: ../js/ui/overview.js:91
+msgid "Undo"
+msgstr "পূর্বাবস্থায় ফিরিয়ে নেয়া"
+
+#: ../js/ui/overview.js:186
+msgid "Windows"
+msgstr "উইন্ডো"
+
+#: ../js/ui/overview.js:189
+msgid "Applications"
+msgstr "অ্যাপ্লিকেশন"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:205
+msgid "Dash"
+msgstr "ড্যাশ"
+
+#. TODO - _quit() doesn't really work on apps in state STARTING yet
+#: ../js/ui/panel.js:524
+#, c-format
+msgid "Quit %s"
+msgstr "%s বন্ধ"
+
+#. Button on the left side of the panel.
+#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:902
+msgid "Activities"
+msgstr "অ্যাক্টিভিটিজ"
+
+#: ../js/ui/panel.js:1003
+msgid "Top Bar"
+msgstr "শীর্ষ বার"
+
+#: ../js/ui/placeDisplay.js:122
+#, c-format
+msgid "Failed to unmount '%s'"
+msgstr "'%s' আনমাউন্ট করতে ব্যর্থ"
+
+#: ../js/ui/placeDisplay.js:125
+msgid "Retry"
+msgstr "পুনরায় চেষ্টা"
+
+#: ../js/ui/placeDisplay.js:165
+msgid "Connect to..."
+msgstr "সংযুক্ত..."
+
+#: ../js/ui/placeDisplay.js:380
+msgid "PLACES & DEVICES"
+msgstr "স্থান এবং ডিভাইস"
+
+#: ../js/ui/polkitAuthenticationAgent.js:74
+#, fuzzy
+msgid "Authentication Required"
+msgstr "প্রমাণীকরণ আবশ্যক"
+
+#: ../js/ui/polkitAuthenticationAgent.js:108
+msgid "Administrator"
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:176
+#, fuzzy
+msgid "Authenticate"
+msgstr "প্রমাণীকরণ আবশ্যক"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/polkitAuthenticationAgent.js:264
+msgid "Sorry, that didn't work. Please try again."
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:276
+msgid "Password:"
+msgstr ""
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:679
+msgid "toggle-switch-us"
+msgstr "টগল-পরিবর্তন-আমাদের"
+
+#: ../js/ui/runDialog.js:205
+msgid "Please enter a command:"
+msgstr "অনুগ্রহ করে একটি কমান্ড দিন:"
+
+#: ../js/ui/searchDisplay.js:311
+msgid "Searching..."
+msgstr "অনুসন্ধান..."
+
+#: ../js/ui/searchDisplay.js:325
+msgid "No matching results."
+msgstr "মিলযুক্ত কোনো ফলাফল নেই।"
+
+#: ../js/ui/statusMenu.js:161 ../js/ui/statusMenu.js:163
+#: ../js/ui/statusMenu.js:228
+msgid "Power Off..."
+msgstr "পাওয়ার বন্ধ..."
+
+#: ../js/ui/statusMenu.js:163 ../js/ui/statusMenu.js:227
+msgid "Suspend"
+msgstr "বাতিল"
+
+#: ../js/ui/statusMenu.js:184
+msgid "Available"
+msgstr "বিদ্যমান"
+
+#: ../js/ui/statusMenu.js:189
+msgid "Busy"
+msgstr "ব্যস্ত"
+
+#: ../js/ui/statusMenu.js:197
+msgid "My Account"
+msgstr "আমার অ্যাকাউন্ট"
+
+#: ../js/ui/statusMenu.js:201
+msgid "System Settings"
+msgstr "সিস্টেম সেটিং"
+
+#: ../js/ui/statusMenu.js:208
+msgid "Lock Screen"
+msgstr "লক স্ক্রিন"
+
+#: ../js/ui/statusMenu.js:213
+msgid "Switch User"
+msgstr "ব্যবহারকারী পরিবর্তন"
+
+#: ../js/ui/statusMenu.js:218
+msgid "Log Out..."
+msgstr "লগআউট..."
+
+#: ../js/ui/status/accessibility.js:62
+msgid "Zoom"
+msgstr "বড় আকারে প্রদর্শন"
+
+#. let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
+#. 'screen-reader-enabled');
+#. this.menu.addMenuItem(screenReader);
+#. let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
+#. 'screen-keyboard-enabled');
+#. this.menu.addMenuItem(screenKeyboard);
+#: ../js/ui/status/accessibility.js:77
+msgid "Visual Alerts"
+msgstr "দৃষ্টিনির্ভর সতর্কবানী"
+
+#: ../js/ui/status/accessibility.js:80
+msgid "Sticky Keys"
+msgstr "স্টিকি কী"
+
+#: ../js/ui/status/accessibility.js:83
+msgid "Slow Keys"
+msgstr "ধীর গতির কী"
+
+#: ../js/ui/status/accessibility.js:86
+msgid "Bounce Keys"
+msgstr "বাউন্স কী"
+
+#: ../js/ui/status/accessibility.js:89
+msgid "Mouse Keys"
+msgstr "মাউস কী"
+
+#: ../js/ui/status/accessibility.js:93
+msgid "Universal Access Settings"
+msgstr "সার্বজনীন ব্যবহার করার সেটিং"
+
+#: ../js/ui/status/accessibility.js:146
+msgid "High Contrast"
+msgstr "উচ্চ বৈশাদৃশ্য"
+
+#: ../js/ui/status/accessibility.js:183
+msgid "Large Text"
+msgstr "বড় টেক্সট"
+
+#: ../js/ui/status/bluetooth.js:42 ../js/ui/status/bluetooth.js:237
+#: ../js/ui/status/bluetooth.js:333 ../js/ui/status/bluetooth.js:367
+#: ../js/ui/status/bluetooth.js:407 ../js/ui/status/bluetooth.js:440
+msgid "Bluetooth"
+msgstr "ব্লুটুথ"
+
+#: ../js/ui/status/bluetooth.js:55
+msgid "Visibility"
+msgstr "দৃশ্যমানতা"
+
+#: ../js/ui/status/bluetooth.js:69
+msgid "Send Files to Device..."
+msgstr "ডিভাইসে ফাইল প্রেরণ..."
+
+#: ../js/ui/status/bluetooth.js:70
+msgid "Setup a New Device..."
+msgstr "নতুন ডিভাইস সেটআপ..."
+
+#: ../js/ui/status/bluetooth.js:95
+msgid "Bluetooth Settings"
+msgstr "ব্লুটুথ সেটিং"
+
+#: ../js/ui/status/bluetooth.js:188
+msgid "Connection"
+msgstr "সংযোগ"
+
+#: ../js/ui/status/bluetooth.js:224
+msgid "Send Files..."
+msgstr "ফাইল প্রেরণ..."
+
+#: ../js/ui/status/bluetooth.js:229
+msgid "Browse Files..."
+msgstr "ফাইল ব্রাউজ..."
+
+#: ../js/ui/status/bluetooth.js:238
+msgid "Error browsing device"
+msgstr "ডিভাইস ব্রাউজিং ত্রুটি"
+
+#: ../js/ui/status/bluetooth.js:239
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr "অনুরোধকৃত ডিভাইস ব্রাউজ করা যায়না, ত্রুটি '%s'"
+
+#: ../js/ui/status/bluetooth.js:247
+msgid "Keyboard Settings"
+msgstr "কীবোর্ড সেটিং"
+
+#: ../js/ui/status/bluetooth.js:252
+msgid "Mouse Settings"
+msgstr "মাউস সেটিং"
+
+#: ../js/ui/status/bluetooth.js:259 ../js/ui/status/volume.js:66
+msgid "Sound Settings"
+msgstr "শব্দ সেটিং"
+
+#: ../js/ui/status/bluetooth.js:368
+#, c-format
+msgid "Authorization request from %s"
+msgstr "%s হতে প্রমানীকরণের জন্য অনুরোধ"
+
+#: ../js/ui/status/bluetooth.js:374
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "সার্ভিস '%s' ডিভাইস %s ব্যবহার করতে চায়"
+
+#: ../js/ui/status/bluetooth.js:376
+msgid "Always grant access"
+msgstr "সর্বদা ব্যবহারের অনুমোদন"
+
+#: ../js/ui/status/bluetooth.js:377
+msgid "Grant this time only"
+msgstr "কেবল এইবার অনুমোদন"
+
+#: ../js/ui/status/bluetooth.js:378
+msgid "Reject"
+msgstr "বাতিল"
+
+#: ../js/ui/status/bluetooth.js:408
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "%s এ র জন্য নিশ্চিতকরণ মিলানো হচ্ছে"
+
+#: ../js/ui/status/bluetooth.js:414 ../js/ui/status/bluetooth.js:448
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "ডিভাইস %s কম্পিউটারের এর সাথে পেয়ার করতে চায়"
+
+#: ../js/ui/status/bluetooth.js:415
+#, c-format
+msgid "Please confirm whether the PIN '%s' matches the one on the device."
+msgstr "অনুগ্রহ করে নিশ্চিত হোন যে পিন '%s' ডিভাইসের পিনের সাথে মিলে কিনা।"
+
+#: ../js/ui/status/bluetooth.js:417
+msgid "Matches"
+msgstr "মিল"
+
+#: ../js/ui/status/bluetooth.js:418
+msgid "Does not match"
+msgstr "মিলে না"
+
+#: ../js/ui/status/bluetooth.js:441
+#, c-format
+msgid "Pairing request for %s"
+msgstr "%s এর সাথে পেয়ার করার অনুরোধ"
+
+#: ../js/ui/status/bluetooth.js:449
+msgid "Please enter the PIN mentioned on the device."
+msgstr "ডিভাইসে উল্লেখিত পিন দিন।"
+
+#: ../js/ui/status/bluetooth.js:465
+msgid "OK"
+msgstr "ঠিক আছে"
+
+#: ../js/ui/status/keyboard.js:73
+msgid "Show Keyboard Layout..."
+msgstr "কীবোর্ডের লেআউট প্রদর্শন..."
+
+#: ../js/ui/status/keyboard.js:76
+msgid "Localization Settings"
+msgstr "স্থানীয়করণ সেটিং"
+
+#: ../js/ui/status/network.js:105 ../js/ui/status/network.js:1467
+msgid "<unknown>"
+msgstr "<unknown>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:319
+msgid "disabled"
+msgstr "নিস্ক্রিয়"
+
+#: ../js/ui/status/network.js:502
+msgid "connecting..."
+msgstr "সংযুক্ত করা হচ্ছে..."
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:505
+msgid "authentication required"
+msgstr "প্রমাণীকরণ আবশ্যক"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:515
+msgid "firmware missing"
+msgstr ""
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:522
+msgid "cable unplugged"
+msgstr "ক্যাবল আনপ্লাগকৃত"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:527
+msgid "unavailable"
+msgstr "বিদ্যমান নয়"
+
+#: ../js/ui/status/network.js:529
+msgid "connection failed"
+msgstr "সংযোগ ব্যর্থ"
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:609 ../js/ui/status/network.js:1415
+msgid "Connected (private)"
+msgstr "সংযুক্ত (ব্যক্তিগত)"
+
+#: ../js/ui/status/network.js:690
+msgid "Auto Ethernet"
+msgstr "স্বয়ংক্রিয় ইথারনেট"
+
+#: ../js/ui/status/network.js:765
+msgid "Auto broadband"
+msgstr "স্বয়ংক্রিয় ব্রডব্যান্ড"
+
+#: ../js/ui/status/network.js:768
+msgid "Auto dial-up"
+msgstr "স্বয়ংক্রিয় ডায়াল-আপ"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:911 ../js/ui/status/network.js:1427
+#, c-format
+msgid "Auto %s"
+msgstr "স্বয়ংক্রিয় %s"
+
+#: ../js/ui/status/network.js:913
+msgid "Auto bluetooth"
+msgstr "স্বয়ংক্রিয় ব্লুটুথ"
+
+#: ../js/ui/status/network.js:1429
+msgid "Auto wireless"
+msgstr "স্বয়ংক্রিয় ওয়ারলেস"
+
+#: ../js/ui/status/network.js:1487
+msgid "More..."
+msgstr "আরও..."
+
+#: ../js/ui/status/network.js:1510
+msgid "Enable networking"
+msgstr "নেটওয়ার্কিং সক্রিয়"
+
+#: ../js/ui/status/network.js:1522
+msgid "Wired"
+msgstr "সংযুক্ত"
+
+#: ../js/ui/status/network.js:1533
+msgid "Wireless"
+msgstr "ওয়ারলেস"
+
+#: ../js/ui/status/network.js:1543
+msgid "Mobile broadband"
+msgstr "মোবাইল ব্রডব্যান্ড"
+
+#: ../js/ui/status/network.js:1553
+msgid "VPN Connections"
+msgstr "VPN সংযোগ"
+
+#: ../js/ui/status/network.js:1565
+msgid "Network Settings"
+msgstr "নেটওয়ার্ক সেটিং"
+
+#: ../js/ui/status/network.js:1855
+#, c-format
+msgid "You're now connected to mobile broadband connection '%s'"
+msgstr "আপনি এখন মোবাইল ব্রডব্যান্ড সংযোগ '%s' এ সংযুক্ত"
+
+#: ../js/ui/status/network.js:1859
+#, c-format
+msgid "You're now connected to wireless network '%s'"
+msgstr "আপনি এখন ওয়ারলেস নেটওয়ার্ক সংযোগে '%s' এ সংযুক্ত"
+
+#: ../js/ui/status/network.js:1863
+#, c-format
+msgid "You're now connected to wired network '%s'"
+msgstr "আপনি এখন তারযুক্ত সংযোগে '%s' এ সংযুক্ত"
+
+#: ../js/ui/status/network.js:1867
+#, c-format
+msgid "You're now connected to VPN network '%s'"
+msgstr "আপনি এখন VPN নেটওয়ার্ক সংযোগ '%s' এ সংযুক্ত"
+
+#: ../js/ui/status/network.js:1872
+#, c-format
+msgid "You're now connected to '%s'"
+msgstr "আপনি এখন '%s' এ সংযুক্ত"
+
+#: ../js/ui/status/network.js:1880
+msgid "Connection established"
+msgstr "সংযোগ প্রতিষ্ঠিত"
+
+#: ../js/ui/status/network.js:2006
+msgid "Networking is disabled"
+msgstr "নেটওয়ার্কিং বিচ্ছিন্ন"
+
+#: ../js/ui/status/network.js:2131
+msgid "Network Manager"
+msgstr "নেটওয়ার্ক ব্যবস্থাপক"
+
+#: ../js/ui/status/power.js:85
+msgid "Power Settings"
+msgstr "পাওয়ার সেটিং"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:111
+msgid "Estimating..."
+msgstr "গণনা করা হচ্ছে..."
+
+#: ../js/ui/status/power.js:118
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "%d ঘন্টা বাকি"
+msgstr[1] "%d ঘন্টা বাকি"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:121
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "%d %s %d %s বাকি"
+
+#: ../js/ui/status/power.js:123
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "ঘন্টা"
+msgstr[1] "ঘন্টা"
+
+#: ../js/ui/status/power.js:123
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "মিনিট"
+msgstr[1] "মিনিট"
+
+#: ../js/ui/status/power.js:126
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "%d মিনিট বাকি"
+msgstr[1] "%d মিনিট বাকি"
+
+#: ../js/ui/status/power.js:228
+msgid "AC adapter"
+msgstr "AC অ্যাডাপ্টার"
+
+#: ../js/ui/status/power.js:230
+msgid "Laptop battery"
+msgstr "ল্যাপটপ ব্যাটারি"
+
+#: ../js/ui/status/power.js:232
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:234
+msgid "Monitor"
+msgstr "মনিটর"
+
+#: ../js/ui/status/power.js:236
+msgid "Mouse"
+msgstr "মাউস"
+
+#: ../js/ui/status/power.js:238
+msgid "Keyboard"
+msgstr "কীবোর্ড"
+
+#: ../js/ui/status/power.js:240
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:242
+msgid "Cell phone"
+msgstr "সেল ফোন"
+
+#: ../js/ui/status/power.js:244
+msgid "Media player"
+msgstr "মিডিয়া প্লেয়ার"
+
+#: ../js/ui/status/power.js:246
+msgid "Tablet"
+msgstr "ট্যাবলেট"
+
+#: ../js/ui/status/power.js:248
+msgid "Computer"
+msgstr "কম্পিউটার"
+
+#: ../js/ui/status/power.js:250 ../src/shell-app-system.c:1088
+msgid "Unknown"
+msgstr "অজানা"
+
+#: ../js/ui/status/volume.js:45
+msgid "Volume"
+msgstr "ভলিউম"
+
+#: ../js/ui/status/volume.js:58
+msgid "Microphone"
+msgstr "মাইক্রোফোন"
+
+#: ../js/ui/telepathyClient.js:335
+#, c-format
+msgid "%s is online."
+msgstr "%s অনলাইনে।"
+
+#: ../js/ui/telepathyClient.js:340
+#, c-format
+msgid "%s is offline."
+msgstr "%s অফলাইনে।"
+
+#: ../js/ui/telepathyClient.js:343
+#, c-format
+msgid "%s is away."
+msgstr "%s কাছে নেই।"
+
+#: ../js/ui/telepathyClient.js:346
+#, c-format
+msgid "%s is busy."
+msgstr "%s ব্যস্ত।"
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/telepathyClient.js:482
+#, no-c-format
+msgid "Sent at %X on %A"
+msgstr "%A এ %X প্রেরণ"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/viewSelector.js:122
+msgid "Type to search..."
+msgstr "অনুসন্ধান করতে লিখুন..."
+
+#: ../js/ui/viewSelector.js:142 ../src/shell-util.c:250
+msgid "Search"
+msgstr "অনুসন্ধান"
+
+#: ../js/ui/windowAttentionHandler.js:42
+#, c-format
+msgid "%s has finished starting"
+msgstr "%s শুরু করা শেষ"
+
+#: ../js/ui/windowAttentionHandler.js:44
+#, c-format
+msgid "'%s' is ready"
+msgstr "'%s' প্রস্তুত"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1098
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u আউটপুট"
+msgstr[1] "%u আউটপুট"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1108
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ইনপুট"
+msgstr[1] "%u ইনপুট"
+
+#: ../src/gvc/gvc-mixer-control.c:1406
+msgid "System Sounds"
+msgstr "সিস্টেমের শব্দ"
+
+#: ../src/main.c:446
+msgid "Print version"
+msgstr "মুদ্রণ সংস্করণ"
+
+#: ../src/shell-app.c:454
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr "'%s' চালু করতে ব্যর্থ"
+
+#: ../src/shell-global.c:1395
+msgid "Less than a minute ago"
+msgstr "এক মিনিটেরও কম সময় আগে"
+
+#: ../src/shell-global.c:1399
+#, c-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d মিনিট আগে"
+msgstr[1] "%d মিনিট আগে"
+
+#: ../src/shell-global.c:1404
+#, c-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d ঘন্টা আগে"
+msgstr[1] "%d ঘন্টা আগে"
+
+#: ../src/shell-global.c:1409
+#, c-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d দিন আগে"
+msgstr[1] "%d দিন আগে"
+
+#: ../src/shell-global.c:1414
+#, c-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d সপ্তাহ আগে"
+msgstr[1] "%d সপ্তাহ আগে"
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr "যুক্তরাজ্য"
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr "পূর্বনির্ধারিত"
+
+#: ../src/shell-polkit-authentication-agent.c:334
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ব্যবহারকারী কর্তৃক প্রমাণীকরণ ডায়ালগ বাতিল"
+
+#: ../src/shell-util.c:89
+msgid "Home Folder"
+msgstr "হোম ফোল্ডার"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:104
+msgid "File System"
+msgstr "ফাইল সিস্টেম"
+
+#. Translators: the first string is the name of a gvfs
+#. * method, and the second string is a path. For
+#. * example, "Trash: some-directory". It means that the
+#. * directory called "some-directory" is in the trash.
+#.
+#: ../src/shell-util.c:300
+#, c-format
+msgid "%1$s: %2$s"
+msgstr "%1$s: %2$s"
+
+#~ msgid "Shut Down"
+#~ msgstr "বন্ধ"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr "এ অ্যাপ্লিকেশন বন্ধ করার জন্য বন্ধ করায় ক্লিক করে সিস্টেম বন্ধ।"
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "সিস্টেম %d সেকেন্ডে স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যাবে।"
+
+#~ msgid "Shutting down the system."
+#~ msgstr "সিস্টেম বন্ধ করা হচ্ছে।"
+
+#~ msgid "Confirm"
+#~ msgstr "নিশ্চিত"
diff --git a/po/bn_IN.po b/po/bn_IN.po
new file mode 100644
index 0000000..6146ae0
--- /dev/null
+++ b/po/bn_IN.po
@@ -0,0 +1,3225 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# , 2014.
+# Akarshan Biswas <akarshan.biswas@hotmail.com>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: unnamed project\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2020-10-08 00:42+0000\n"
+"PO-Revision-Date: 2020-10-11 12:33+0530\n"
+"Last-Translator: Akarshan Biswas <akarshan.biswas@hotmail.com>\n"
+"Language-Team: Bengali <anubad@lists.ankur.org.in>\n"
+"Language: bn_IN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Generator: Gtranslator 3.38.0\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "সিস্টেম"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "বিজ্ঞপ্তি তালিকা দেখান"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "বর্তমান বিজ্ঞপ্তিতে নজর দিন"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "পূর্বরূপ দেখান"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "সমস্ত অ্যাপ্লিকেশন দেখান"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "অ্যাপ্লিকেশন মেনু খুলুন"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME সেল"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "উইন্ডো পরিচালনা ও অ্যাপ্লিকেশন প্রারম্ভ"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "ডেভেলপার এবং টেস্টারদের কাজের সহায়ক অভ্যন্তরীণ সরঞ্জাম Alt-F2 থেকে চালু করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 ডায়ালগ ব্যবহার করে অভ্যন্তরীণ ডিবাগিং এবং নিয়ন্ত্রণ সরঞ্জামে অ্যাক্সেস দেয়"
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "চালুর করার UUID এক্সটেনশন"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME সেল এক্সটেনশনের রয়েছে UUID বিশিষ্টতা; লোড করা উচিত এমন এক্সটেনশনগুলি এই কী "
+"তালিকাভুক্ত করে। লোড হতে চাওয়া যেকোনো এক্সটেনশনকে এই তালিকায় থাকতে হবে। আপনি "
+"org.gnome.Shell-এ EnableExtension এবং DisableExtension D-Bus পদ্ধতি দিয়ে এই "
+"তালিকা ব্যবহার করতে পারবেন।"
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "জোর করে অক্ষম করা এক্সটেন্সানগুলির UUIDs"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"জিনোম শেল এক্সটেনশনের একটি UUID সম্পত্তি রয়েছে; এই কীটি এমন এক্সটেনশনগুলির তালিকা "
+"দেয় যা বর্তমান মোডের অংশ হিসাবে লোড হওয়া সত্ত্বেও অক্ষম করা উচিত। আপনি org.gnome."
+"Shell- এ সক্ষম করুন এক্সটেনশন এবং ডিজেবল এক্সটেনশন ডি-বাস পদ্ধতিগুলির সাহায্যে এই "
+"তালিকাটিও হেরফের করতে পারেন। এই কীটি \"enabled-extensions\" সেটিংসের চেয়ে "
+"অগ্রাধিকার নেয়।"
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "ব্যবহারকারীর এক্সটেনশনগুলি অক্ষম করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"ব্যবহারকারী \"সক্ষম-এক্সটেনশন\" সেটিংসকে প্রভাবিত না করে সক্ষম করেছেন এমন সমস্ত "
+"এক্সটেনশন অক্ষম করুন।"
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "এক্সটেনশন সংস্করণের সুসংগততার যাচাইকরণ নিষ্ক্রিয় করে"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell শুধুমাত্র সেই এক্সটেনশনগুলি লোড করবে যা বর্তমান চলা সংস্করণ সমর্থন করে "
+"বলে জানাচ্ছে। এই বিকল্প চালু করলে তা এই টিক চিহ্ন নিষ্ক্রিয় করবে এবং যে সংস্করণগুলিই "
+"সমর্থন করছে বলে জানাক না কেন তা সমস্ত এক্সটেনশনই লোড করার চেষ্টা করবে।"
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "পছন্দসই অ্যাপ্লিকেশনগুলির জন্য ডেস্কটপ ফাইল ID-গুলির তালিকা"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "এই চিহ্নিতকারীর সাথে সংশ্লিষ্ট অ্যাপ্লিকেশনগুলি পছন্দসই অংশে দেখানো হবে।"
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "App Picker View"
+msgstr "অ্যাপ চয়নকারীর রূপ"
+
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "Index of the currently selected view in the application picker."
+msgstr "অ্যাপ্লিকেশন চয়নকারীতে বর্তমানে নির্বাচিত রূপের সূচি।"
+
+#: data/org.gnome.shell.gschema.xml.in:69
+msgid "History for command (Alt-F2) dialog"
+msgstr "(Alt-F2) কম্যান্ড ডায়ালগের ইতিহাস"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:74
+msgid "History for the looking glass dialog"
+msgstr "লুকিং গ্লাস ডায়ালগের ইতিহাস"
+
+#: data/org.gnome.shell.gschema.xml.in:78
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "ব্যবহারকারী মেনুতে সর্বদা \"লগ আউট\" মেনু আইটেমটি দেখান।"
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"এই কীটি একক ব্যবহারকারী, একক-অধিবেশন পরিস্থিতিতে \"লগ আউট\" মেনু আইটেমের "
+"স্বয়ংক্রিয় আড়ালকরণকে ওভাররাইড করে।"
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"এনক্রিপ্ট করা বা রিমোট ফাইলসিস্টেম মাউন্ট করার জন্য পাসওয়ার্ড মনে রাখা হবে কিনা"
+
+#: data/org.gnome.shell.gschema.xml.in:87
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"কোনও এনক্রিপ্ট করা ডিভাইস বা একটি রিমোট ফাইল সিস্টেম মাউন্ট করা হলে শেলটি একটি "
+"পাসওয়ার্ডের জন্য অনুরোধ করবে। ভবিষ্যতে ব্যবহারের জন্য যদি পাসওয়ার্ডটি সংরক্ষণ করা "
+"যায় তবে একটি \"পাসওয়ার্ড মনে রাখবেন\" চেকবক্স উপস্থিত থাকবে। এই কীটি চেকবাক্সের "
+"ডিফল্ট অবস্থা সেট করে।"
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "ডিফল্ট ব্লুটুথ অ্যাডাপ্টারের সাথে সম্পর্কিত ডিভাইসগুলি সেট আপ করেছিল কিনা"
+
+#: data/org.gnome.shell.gschema.xml.in:97
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"শেলটি কেবলমাত্র একটি ব্লুটুথ মেনু আইটেম দেখায় যদি কোনও ব্লুটুথ অ্যাডাপ্টার চালিত হয় বা "
+"ডিফল্ট অ্যাডাপ্টারের সাথে যুক্ত ডিভাইসগুলি উপস্থিত থাকে। ডিফল্ট অ্যাডাপ্টারের সাথে এর "
+"সাথে যুক্ত ডিভাইস না দেখা গেলে এটি পুনরায় সেট করা হবে।"
+
+#: data/org.gnome.shell.gschema.xml.in:106
+msgid "Enable introspection API"
+msgstr "আত্মবিজ্ঞাপন API সক্ষম করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:107
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr "ডি-বাস এপিআই সক্ষম করে যা শেলের অ্যাপ্লিকেশন স্থিতিকে আত্ম-পরীক্ষা করতে দেয়।"
+
+#: data/org.gnome.shell.gschema.xml.in:114
+msgid "Layout of the app picker"
+msgstr "অ্যাপ পিকারের লেআউট"
+
+#: data/org.gnome.shell.gschema.xml.in:115
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"অ্যাপ পিকারের লেআউট। অ্যারের প্রতিটি এন্ট্রি একটি পৃষ্ঠা। পৃষ্ঠাগুলি তারা জিনোম "
+"শেলগুলিতে প্রদর্শিত ক্রমে সংরক্ষণ করা হয়। প্রতিটি পৃষ্ঠায় একটি \"অ্যাপ্লিকেশন আইডি\" "
+"→ 'ডেটা' জুটি থাকে। বর্তমানে, নিম্নলিখিত মানগুলি 'ডেটা' হিসাবে সংরক্ষণ করা হয়: • "
+"\"অবস্থান\": পৃষ্ঠায় অ্যাপ্লিকেশন আইকনের অবস্থান"
+
+#: data/org.gnome.shell.gschema.xml.in:130
+msgid "Keybinding to open the application menu"
+msgstr "অ্যাপ্লিকেশন মেনু খুলতে কীবাইন্ডিং"
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to open the application menu."
+msgstr "অ্যাপ্লিকেশন মেনু খুলতে কীবাইন্ডিং।"
+
+#: data/org.gnome.shell.gschema.xml.in:137
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "\"অ্যাপ্লিকেশনগুলি দেখান\" খুলতে কীবাইন্ডিং"
+
+#: data/org.gnome.shell.gschema.xml.in:138
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "ক্রিয়াকলাপের পূর্বরূপের \"অ্যাপ্লিকেশনগুলি দেখান\" খুলতে কীবাইন্ডিং।"
+
+#: data/org.gnome.shell.gschema.xml.in:145
+msgid "Keybinding to open the overview"
+msgstr "পূর্বরূপ খুলতে কীবাইন্ডিং"
+
+#: data/org.gnome.shell.gschema.xml.in:146
+msgid "Keybinding to open the Activities Overview."
+msgstr "ক্রিয়াকলাপের পূর্বরূপ খুলতে কীবাইন্ডিং।"
+
+#: data/org.gnome.shell.gschema.xml.in:152
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "বিজ্ঞপ্তি তালিকার দৃশ্যমানতা টগল করতে কী-বাইন্ডিং"
+
+#: data/org.gnome.shell.gschema.xml.in:153
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "বিজ্ঞপ্তি তালিকার দৃশ্যমানতা টগল করতে কী-বাইন্ডিং।"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to focus the active notification"
+msgstr "সক্রিয় বিজ্ঞপ্তি ফোকাস করতে কীবাইন্ডিং"
+
+#: data/org.gnome.shell.gschema.xml.in:160
+msgid "Keybinding to focus the active notification."
+msgstr "সক্রিয় বিজ্ঞপ্তি ফোকাস করতে কীবাইন্ডিং।"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Switch to application 1"
+msgstr "অ্যাপ্লিকেশন ১ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Switch to application 2"
+msgstr "অ্যাপ্লিকেশন ২ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:174
+msgid "Switch to application 3"
+msgstr "অ্যাপ্লিকেশন ৩ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Switch to application 4"
+msgstr "অ্যাপ্লিকেশন ৪ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:182
+msgid "Switch to application 5"
+msgstr "অ্যাপ্লিকেশন ৫ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Switch to application 6"
+msgstr "অ্যাপ্লিকেশন ৬ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:190
+msgid "Switch to application 7"
+msgstr "অ্যাপ্লিকেশন ৭ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Switch to application 8"
+msgstr "অ্যাপ্লিকেশন ৮ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 9"
+msgstr "অ্যাপ্লিকেশন ৯ এ স্যুইচ করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:207
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Limit switcher to current workspace."
+msgstr "বর্তমান কাজেরস্থানে স্যুইচার সীমাবদ্ধ করুন।"
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"সত্য হলে, বর্তমান কাজেরস্থানে উইন্ডো থাকা অ্যাপ্লিকেশনগুলিই শুধুমাত্র স্যুইচারে দেখানো "
+"হয়। অন্যথায়, সকল অ্যাপ্লিকেশন অন্তর্ভুক্ত।"
+
+#: data/org.gnome.shell.gschema.xml.in:225
+msgid "The application icon mode."
+msgstr "অ্যাপ্লিকেশন আইকন মোড।"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"সুইচারে উইন্ডোজগুলি কীভাবে প্রদর্শিত হয় তা কনফিগার করে। বৈধ সম্ভাবনাগুলি হ'ল \"কেবল "
+"থাম্বনেইল\" (উইন্ডোর একটি থাম্বনেইল দেখায়), \"অ্যাপ-আইকন-কেবল\" (কেবলমাত্র "
+"অ্যাপ্লিকেশন আইকন দেখায়) বা \"উভয়\"।"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"সত্য হলে, শুধুমাত্র বর্তমান ওয়ার্কস্পেসের উইন্ডোগুলি স্যুইচারে দেখানো হয়। অন্যথায়, সমস্ত "
+"উইন্ডো অন্তর্ভুক্ত করা হয়।"
+
+#: data/org.gnome.shell.gschema.xml.in:245
+msgid "Locations"
+msgstr "অবস্থানগুলি"
+
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "The locations to show in world clocks"
+msgstr "বিশ্বের ঘড়িতে দেখানোর জন্য অবস্থানগুলি"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Automatic location"
+msgstr "স্বয়ংক্রিয় অবস্থান"
+
+#: data/org.gnome.shell.gschema.xml.in:257
+msgid "Whether to fetch the current location or not"
+msgstr "বর্তমান অবস্থান আনতে হবে কিনা"
+
+#: data/org.gnome.shell.gschema.xml.in:264
+msgid "Location"
+msgstr "অবস্থান"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+msgid "The location for which to show a forecast"
+msgstr "পূর্বাভাস প্রদর্শন করার জন্য অবস্থান"
+
+#: data/org.gnome.shell.gschema.xml.in:277
+msgid "Attach modal dialog to the parent window"
+msgstr "প্রধান উইন্ডোতে মোডেল ডায়ালগ সংযুক্ত করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:278
+#: data/org.gnome.shell.gschema.xml.in:287
+#: data/org.gnome.shell.gschema.xml.in:295
+#: data/org.gnome.shell.gschema.xml.in:303
+#: data/org.gnome.shell.gschema.xml.in:311
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "GNOME সেল চালানোর ক্ষেত্রে এই কী in org.gnome.mutter এর কী ওভাররাইড করে।"
+
+#: data/org.gnome.shell.gschema.xml.in:286
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "উইন্ডো স্ক্রীন কিনারায় রাখার সময়ে কিনারা টাইলিং সক্রিয় করুন"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "Workspaces are managed dynamically"
+msgstr "ওয়ার্কস্পেসগুলি ডায়নামিক ভাবে ব্যবস্থাপিত হয়"
+
+#: data/org.gnome.shell.gschema.xml.in:302
+msgid "Workspaces only on primary monitor"
+msgstr "শুধুমাত্র প্রধান মনিটরে ওয়ার্কস্পেস"
+
+#: data/org.gnome.shell.gschema.xml.in:310
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"পয়েন্টার অবস্থান পরিবর্তন না থামানো পর্যন্ত মাউস মোডে ফোকাস পরিবর্তন বিলম্বিত করুন"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "নেটওয়ার্ক লগিন"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:36
+#: subprojects/extensions-app/data/ui/extensions-window.ui:224
+msgid "Something’s gone wrong"
+msgstr "কিছু ভুল হয়েছে"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:48
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"আমরা খুব দুঃখিত, কিন্তু একটি সমস্যা হয়েছে: এই এক্সটেনশনের সেটিংস প্রদর্শিত হতে পারে "
+"না। আমরা আপনাকে এক্সটেনশন লেখকদের কাছে সমস্যাটি প্রতিবেদন করার পরামর্শ দিচ্ছি।"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:82
+msgid "Technical Details"
+msgstr "প্রযুক্তিগত বিবরণ"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:165
+msgid "Homepage"
+msgstr "হোমপেজ "
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:166
+msgid "Visit extension homepage"
+msgstr "এক্সটেনশন হোমপেজে যান"
+
+#: js/gdm/authPrompt.js:137 js/ui/audioDeviceSelection.js:57
+#: js/ui/components/networkAgent.js:111 js/ui/components/polkitAgent.js:139
+#: js/ui/endSessionDialog.js:438 js/ui/extensionDownloader.js:183
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:940 subprojects/extensions-app/js/main.js:149
+msgid "Cancel"
+msgstr "বাতিল"
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:239 js/ui/components/networkAgent.js:206
+#: js/ui/components/networkAgent.js:222 js/ui/components/networkAgent.js:246
+#: js/ui/components/networkAgent.js:267 js/ui/components/networkAgent.js:287
+#: js/ui/components/networkAgent.js:297 js/ui/components/polkitAgent.js:277
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "পাসওয়ার্ড"
+
+#: js/gdm/loginDialog.js:318
+msgid "Choose Session"
+msgstr "সেশন বাছুন"
+
+#: js/gdm/loginDialog.js:457
+msgid "Not listed?"
+msgstr "তালিকায় নেই?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:913
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(উদাঃ, ব্যবহারকারী বা %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:918 js/ui/components/networkAgent.js:242
+#: js/ui/components/networkAgent.js:265 js/ui/components/networkAgent.js:283
+msgid "Username"
+msgstr "ব্যবহারকারীর নাম"
+
+#: js/gdm/loginDialog.js:1253
+msgid "Login Window"
+msgstr "লগিন উইন্ডো"
+
+#: js/gdm/util.js:355
+msgid "Authentication error"
+msgstr "অনুমোদন সংক্রান্ত সমস্যা"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:481
+msgid "(or swipe finger)"
+msgstr "(বা আঙুল চালানো)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:82
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "পাওয়ার বন্ধ"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:85
+msgid "power off;shutdown;halt;stop"
+msgstr "পাওয়ার অফ;শাটডাউন;থাম;বন্ধ করুন"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:90
+msgctxt "search-result"
+msgid "Restart"
+msgstr "পুনরায় আরম্ভ করুন"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:93
+msgid "reboot;restart;"
+msgstr "রিবুট;পুনরায় আরম্ভ;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:98
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "লক স্ক্রীন"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:101
+msgid "lock screen"
+msgstr "লক স্ক্রীন"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "লগ আউট"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "logout;log out;sign off"
+msgstr "লগআউট; লগ আউট; সাইন অফ"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:114
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "থামানো"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:117
+msgid "suspend;sleep"
+msgstr "থামানো; ঘুমানো"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:122
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "ব্যবহারকারী পাল্টান"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:125
+msgid "switch user"
+msgstr "ব্যবহারকারী পাল্টান"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "লক ওরিয়েন্টেশন; আনলক ওরিয়েন্টেশন; স্ক্রিন; রোটেশন"
+
+#: js/misc/systemActions.js:240
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "স্ক্রিন রোটেশন আনলক করুন"
+
+#: js/misc/systemActions.js:241
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "স্ক্রিন রোটেশন লক করুন"
+
+#: js/misc/util.js:120
+msgid "Command not found"
+msgstr "কম্যান্ড পাওয়া যায়নি"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:156
+msgid "Could not parse command:"
+msgstr "কম্যান্ড পার্জ করা যায়নি:"
+
+#: js/misc/util.js:164
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” -এর সম্পাদনা ব্যর্থ হয়েছে:"
+
+#: js/misc/util.js:181
+msgid "Just now"
+msgstr "এক্ষুনি"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d মিনিট আগে"
+msgstr[1] "%d মিনিট আগে"
+
+#: js/misc/util.js:187
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d ঘণ্টা আগে"
+msgstr[1] "%d ঘণ্টা আগে"
+
+#: js/misc/util.js:191 js/ui/dateMenu.js:162
+msgid "Yesterday"
+msgstr "গতকাল"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d দিন আগে"
+msgstr[1] "%d দিন আগে"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d সপ্তাহ আগে"
+msgstr[1] "%d সপ্তাহ আগে"
+
+#: js/misc/util.js:201
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d মাস আগে"
+msgstr[1] "%d মাস আগে"
+
+#: js/misc/util.js:204
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d বছর আগে"
+msgstr[1] "%d বছর আগে"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:237
+msgid "%H∶%M"
+msgstr "%ঘ∶%মি"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:243
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "গতকাল, %ঘ∶%মি"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:249
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:255
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:261
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:266
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:272
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "গতকাল, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:278
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:284
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:290
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:41
+msgid "Hotspot Login"
+msgstr "হটস্পট লগইন"
+
+#: js/portalHelper/main.js:87
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"এই হটস্পট লগইনে আপনার সংযোগ নিরাপদ নয়। এই পৃষ্ঠায় আপনি প্রবেশ করা পাসওয়ার্ড বা "
+"অন্যান্য তথ্য আশেপাশের লোকেরা দেখতে পাবেন।"
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:369
+msgid "Deny Access"
+msgstr "প্রবেশ নিষেধ"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:372
+msgid "Grant Access"
+msgstr "অনুদান অ্যাক্সেস"
+
+#: js/ui/appDisplay.js:1346
+msgid "Unnamed Folder"
+msgstr "নামহীন ফোল্ডার"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:2870 js/ui/panel.js:75
+msgid "Open Windows"
+msgstr "উইন্ডোজ খুলুন"
+
+#: js/ui/appDisplay.js:2889 js/ui/panel.js:82
+msgid "New Window"
+msgstr "নতুন উইন্ডো"
+
+#: js/ui/appDisplay.js:2905
+msgid "Launch using Integrated Graphics Card"
+msgstr "ইন্টিগ্রেটেড গ্রাফিক্স কার্ড ব্যবহার করে লঞ্চ করুন"
+
+#: js/ui/appDisplay.js:2906
+msgid "Launch using Discrete Graphics Card"
+msgstr "বিচ্ছিন্ন গ্রাফিক্স কার্ড ব্যবহার করে চালু করুন"
+
+#: js/ui/appDisplay.js:2934 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "পছন্দসই থেকে সরান"
+
+#: js/ui/appDisplay.js:2940
+msgid "Add to Favorites"
+msgstr "পছন্দসইতে যোগ করুন"
+
+#: js/ui/appDisplay.js:2950 js/ui/panel.js:93
+msgid "Show Details"
+msgstr "বিবরণ প্রদর্শন করা হবে"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s আপনার পছন্দসইতে যোগ করা হয়েছে।"
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "আপনার পছন্দসই থেকে %s সরানো হয়েছে।"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "অডিও ডিভাইস নির্বাচন করুন"
+
+#: js/ui/audioDeviceSelection.js:54
+msgid "Sound Settings"
+msgstr "ধ্বনি সেটিং"
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headphones"
+msgstr "হেডফোন"
+
+#: js/ui/audioDeviceSelection.js:66
+msgid "Headset"
+msgstr "হেডসেট"
+
+#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:272
+msgid "Microphone"
+msgstr "মাইক্রোফোন"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "ব্যাকগ্রাউন্ড পরিবর্তন করুন…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "ডিসপ্লে সেটিং"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "সেটিং"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:36
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:65
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:67
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:69
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:71
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:73
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:75
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:77
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:392
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:461
+msgid "Previous month"
+msgstr "পূর্ববর্তী মাস"
+
+#: js/ui/calendar.js:476
+msgid "Next month"
+msgstr "পরবর্তী মাস"
+
+#: js/ui/calendar.js:626
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:682
+msgid "Week %V"
+msgstr "সপ্তাহ %V"
+
+#: js/ui/calendar.js:896
+msgid "No Notifications"
+msgstr "কোন বিজ্ঞপ্তি নেই"
+
+#: js/ui/calendar.js:950
+msgid "Do Not Disturb"
+msgstr "বিরক্ত করবেন না"
+
+#: js/ui/calendar.js:969
+msgid "Clear"
+msgstr "পরিষ্কার"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” সাড়া দিচ্ছে না"
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"আপনি এটি চালিয়ে যাওয়ার জন্য অল্প সময়ের জন্য অপেক্ষা করতে বা অ্যাপ্লিকেশনটিকে "
+"পুরোপুরি ছাড়তে বাধ্য করতে পারেন।"
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "জোর করে বন্ধ"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "অপেক্ষা"
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr "বাহ্যিক ড্রাইভ সংযুক্ত"
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr "বাহ্যিক ড্রাইভ সংযোগ বিচ্ছিন্ন"
+
+#: js/ui/components/automountManager.js:208
+msgid "Unable to unlock volume"
+msgstr "ভলিউম আনলক করতে অক্ষম"
+
+#: js/ui/components/automountManager.js:209
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "ইনস্টল করা উডিস্কস সংস্করণ পিআইএম সেটিংস সমর্থন করে না"
+
+#: js/ui/components/autorunManager.js:332
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s সহযোগে খুলুন"
+
+#: js/ui/components/networkAgent.js:93
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "বিকল্পভাবে আপনি আপনার রাউটারের \"WPS\" বোতামটি টিপে সংযোগ করতে পারেন। "
+
+#: js/ui/components/networkAgent.js:105 js/ui/status/network.js:252
+#: js/ui/status/network.js:343 js/ui/status/network.js:943
+msgid "Connect"
+msgstr "সংযোগ করুন"
+
+#: js/ui/components/networkAgent.js:212
+msgid "Key"
+msgstr "কী"
+
+#: js/ui/components/networkAgent.js:250 js/ui/components/networkAgent.js:273
+msgid "Private key password"
+msgstr "ব্যক্তিগত কী পাসওয়ার্ড"
+
+#: js/ui/components/networkAgent.js:271
+msgid "Identity"
+msgstr "পরিচয়"
+
+#: js/ui/components/networkAgent.js:285
+msgid "Service"
+msgstr "পরিষেবা"
+
+#: js/ui/components/networkAgent.js:314 js/ui/components/networkAgent.js:342
+#: js/ui/components/networkAgent.js:675 js/ui/components/networkAgent.js:696
+msgid "Authentication required"
+msgstr "পরিচয় প্রমাণ করা আবশ্যক"
+
+#: js/ui/components/networkAgent.js:315 js/ui/components/networkAgent.js:676
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "“%s” বেতার নেটওয়ার্ক অ্যাক্সেস করতে পাসওয়ার্ড বা এনক্রিপশন কী'র প্রয়োজন।"
+
+#: js/ui/components/networkAgent.js:319 js/ui/components/networkAgent.js:680
+msgid "Wired 802.1X authentication"
+msgstr "তারযুক্ত 802.1X প্রমাণীকরণ"
+
+#: js/ui/components/networkAgent.js:321
+msgid "Network name"
+msgstr "নেটওয়ার্কের নাম"
+
+#: js/ui/components/networkAgent.js:326 js/ui/components/networkAgent.js:684
+msgid "DSL authentication"
+msgstr "DSL প্রমাণীকরণ"
+
+#: js/ui/components/networkAgent.js:333 js/ui/components/networkAgent.js:689
+msgid "PIN code required"
+msgstr "PIN কোড প্রয়োজনীয়"
+
+#: js/ui/components/networkAgent.js:334 js/ui/components/networkAgent.js:690
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "মোবাইল ব্রডব্যান্ড ডিভাইসের জন্য PIN কোড প্রয়োজন"
+
+#: js/ui/components/networkAgent.js:335
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:343 js/ui/components/networkAgent.js:681
+#: js/ui/components/networkAgent.js:685 js/ui/components/networkAgent.js:697
+#: js/ui/components/networkAgent.js:701
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” -এ সংযোগ করতে একটি পাসওয়ার্ডের প্রয়োজন"
+
+#: js/ui/components/networkAgent.js:664 js/ui/status/network.js:1718
+msgid "Network Manager"
+msgstr "নেটওয়ার্ক ম্যানেজার"
+
+#: js/ui/components/networkAgent.js:700
+msgid "VPN password"
+msgstr "VPN পাসওয়ার্ড"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "পরিচয় প্রমাণ করা আবশ্যক"
+
+#: js/ui/components/polkitAgent.js:80
+msgid "Administrator"
+msgstr "প্রশাসক"
+
+#: js/ui/components/polkitAgent.js:142
+msgid "Authenticate"
+msgstr "প্রমাণীকরণ"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:254 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "দুঃখিত, এটি কার্যকর হয়নি। অনুগ্রহপূর্বক আবার চেষ্টা করুন।"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:822
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s %s হিসাবে পরিচিত"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:178
+msgid "Windows"
+msgstr "Windows"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr "অ্যাপ্লিকেশনগুলি দেখান"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr "ড্যাশ"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:79
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:86
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:151
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:154
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:160
+msgid "Today"
+msgstr "আজ"
+
+#: js/ui/dateMenu.js:164
+msgid "Tomorrow"
+msgstr "আগামী কাল"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:180
+msgctxt "event list time"
+msgid "All Day"
+msgstr "সারাদিন"
+
+#: js/ui/dateMenu.js:231
+msgid "No Events"
+msgstr "কোনও ইভেন্ট নেই"
+
+#: js/ui/dateMenu.js:348
+msgid "Add world clocks…"
+msgstr "বিশ্বের ঘড়ি যুক্ত করুন ..."
+
+#: js/ui/dateMenu.js:349
+msgid "World Clocks"
+msgstr "বিশ্ব ঘড়ি"
+
+#: js/ui/dateMenu.js:629
+msgid "Loading…"
+msgstr "লোড হচ্ছে ..."
+
+#: js/ui/dateMenu.js:639
+msgid "Go online for weather information"
+msgstr "আবহাওয়ার তথ্যের জন্য অনলাইনে যান"
+
+#: js/ui/dateMenu.js:641
+msgid "Weather information is currently unavailable"
+msgstr "আবহাওয়ার তথ্য বর্তমানে অনুপলব্ধ"
+
+#: js/ui/dateMenu.js:651
+msgid "Weather"
+msgstr "আবহাওয়া"
+
+#: js/ui/dateMenu.js:653
+msgid "Select weather location…"
+msgstr "আবহাওয়ার অবস্থান নির্বাচন করুন ..."
+
+#: js/ui/endSessionDialog.js:39
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s লগ আউট করুন"
+
+#: js/ui/endSessionDialog.js:40
+msgctxt "title"
+msgid "Log Out"
+msgstr "লগ আউট করুন"
+
+#: js/ui/endSessionDialog.js:43
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s %d -এর মধ্যে স্বয়ংক্রিয় ভাবে লগ আউট হবে।"
+msgstr[1] "%s %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে লগ আউট হবে।"
+
+#: js/ui/endSessionDialog.js:49
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "আপনি %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে লগ আউট হবেন।"
+msgstr[1] "আপনি %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে লগ আউট হবেন।"
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "লগ আউট করুন"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "পাওয়ার বন্ধ"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "আপডেট ইনস্টল করুন এবং পাওয়ার বন্ধ করুন"
+
+#: js/ui/endSessionDialog.js:66
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "সিস্টেমের পাওয়ার %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে বন্ধ হয়ে যাবে।"
+msgstr[1] "সিস্টেমের পাওয়ার %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে বন্ধ হয়ে যাবে।"
+
+#: js/ui/endSessionDialog.js:70 js/ui/endSessionDialog.js:89
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "বাকি থাকা সফ্টওয়্যার আপডেট ইনস্টল করুন"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "পাওয়ার বন্ধ"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "পুনরায় আরম্ভ করুন"
+
+#: js/ui/endSessionDialog.js:82
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "আপডেট ইনস্টল করুন এবং পুনরায় আরম্ভ করুন"
+
+#: js/ui/endSessionDialog.js:85
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "সিস্টেম %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে শুরু হবে।"
+msgstr[1] "সিস্টেম %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে শুরু হবে।"
+
+#: js/ui/endSessionDialog.js:93
+msgctxt "button"
+msgid "Restart"
+msgstr "পুনরায় আরম্ভ করুন"
+
+#: js/ui/endSessionDialog.js:101
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "আবার চালু করুন ও আপডেটগুলি ইনস্টল করুন"
+
+#: js/ui/endSessionDialog.js:104
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"সিস্টেম %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে চালু হবে এবং আপডেট ইনস্টল করবে।"
+msgstr[1] ""
+"সিস্টেম %d সেকেন্ডের মধ্যে স্বয়ংক্রিয় ভাবে চালু হবে এবং আপডেট ইনস্টল করবে।"
+
+#: js/ui/endSessionDialog.js:111 js/ui/endSessionDialog.js:132
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "আবার চালু করুন ও ইনস্টল করুন"
+
+#: js/ui/endSessionDialog.js:113
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ইনস্টল করুন ও পাওয়ার বন্ধ করুন"
+
+#: js/ui/endSessionDialog.js:114
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "আপডেটগুলি ইনস্টল হয়ে যাওয়ার পরে পাওয়ার বন্ধ করুন"
+
+#: js/ui/endSessionDialog.js:121
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "পুনরায় আরম্ভ করুন এবং আপগ্রেড ইনস্টল করুন"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:126
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s পুনরায় চালু হওয়ার পরে ইনস্টল করা হবে। আপগ্রেড ইনস্টলেশনটি দীর্ঘ সময় নিতে "
+"পারে: আপনি ব্যাক আপ নিয়ে এসেছেন এবং কম্পিউটারটি প্লাগ ইন হয়েছে তা নিশ্চিত করুন।"
+
+#: js/ui/endSessionDialog.js:284
+msgid "Low battery power: please plug in before installing updates."
+msgstr "লো ব্যাটারি শক্তি: আপডেট ইনস্টল করার আগে প্লাগ ইন করুন।"
+
+#: js/ui/endSessionDialog.js:293
+msgid "Some applications are busy or have unsaved work"
+msgstr "কিছু অ্যাপ্লিকেশনগুলি ব্যস্ত বা অসুরক্ষিত কাজ রয়েছে"
+
+#: js/ui/endSessionDialog.js:298
+msgid "Other users are logged in"
+msgstr "অন্যান্য ব্যবহারকারীরা লগ ইন আছে"
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "বুট অপশনগুলি"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:685
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (দূরবর্তী)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:688
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (কনসোল)"
+
+#: js/ui/extensionDownloader.js:187
+msgid "Install"
+msgstr "ইনস্টল করুন"
+
+#: js/ui/extensionDownloader.js:193
+msgid "Install Extension"
+msgstr "এক্সটেনশন ইনস্টল করুন"
+
+#: js/ui/extensionDownloader.js:194
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "“%s” extensions.gnome.org থেকে ডাউনলোড এবং ইনস্টল করবেন?"
+
+#: js/ui/extensionSystem.js:253
+msgid "Extension Updates Available"
+msgstr "এক্সটেনশন আপডেট উপলব্ধ"
+
+#: js/ui/extensionSystem.js:254
+msgid "Extension updates are ready to be installed."
+msgstr "এক্সটেনশন আপডেটগুলি ইনস্টল করার জন্য প্রস্তুত।"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "বাধা শর্টকাটগুলির অনুমতি দিন"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "অ্যাপ্লিকেশন %s শর্টকাটগুলি বাধা দিতে চায়"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "একটি অ্যাপ্লিকেশন শর্টকাটগুলি বাধা দিতে চায়"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "আপনি %s টিপে শর্টকাটগুলি পুনরুদ্ধার করতে পারেন।"
+
+#: js/ui/inhibitShortcutsDialog.js:100
+msgid "Deny"
+msgstr "অস্বীকার"
+
+#: js/ui/inhibitShortcutsDialog.js:107
+msgid "Allow"
+msgstr "অনুমতি"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "স্লো কীগুলি চালু হয়েছে"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "স্লো কীগুলি বন্ধ হয়ে গেছে"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"আপনি মাত্র ৮ সেকেন্ডের জন্য শিফট কীটি ধরে রেখেছেন। এটি স্লো কীগুলির বৈশিষ্ট্যটির "
+"শর্টকাট, যা আপনার কীবোর্ডের কাজ করার পদ্ধতিকে প্রভাবিত করে।"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned On"
+msgstr "স্টিকি কীগুলি চালু হয়েছে"
+
+#: js/ui/kbdA11yDialog.js:42
+msgid "Sticky Keys Turned Off"
+msgstr "স্টিকি কীগুলি বন্ধ হয়ে গেছে"
+
+#: js/ui/kbdA11yDialog.js:44
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"আপনি একটানা ৫ বার শিফট কী টিপেছেন। এটি স্টিকি কীগুলির বৈশিষ্ট্যটির শর্টকাট, যা "
+"আপনার কীবোর্ডের কাজের পদ্ধতিকে প্রভাবিত করে।"
+
+#: js/ui/kbdA11yDialog.js:46
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"আপনি একবারে দুটি কী একসাথে টিপেছেন, অথবা ৫ বার শিফট কী টিপেছেন। এটি স্টিকি "
+"কীগুলি বৈশিষ্ট্যটি বন্ধ করে দেয় যা আপনার কীবোর্ডের কাজ করার পদ্ধতিকে প্রভাবিত করে।"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "লীভ চালু"
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:1315
+msgid "Turn On"
+msgstr "চালু করুন"
+
+#: js/ui/kbdA11yDialog.js:63 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:160 js/ui/status/network.js:344
+#: js/ui/status/network.js:1315 js/ui/status/network.js:1427
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "বন্ধ করুন"
+
+#: js/ui/kbdA11yDialog.js:63
+msgid "Leave Off"
+msgstr "লীভ বন্ধ"
+
+#: js/ui/keyboard.js:226
+msgid "Region & Language Settings"
+msgstr "অঞ্চল এবং ভাষা সেটিংস"
+
+#: js/ui/lookingGlass.js:664
+msgid "No extensions installed"
+msgstr "কোনো এক্সটেনশন ইনস্টল নেই"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:719
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s থেকে কোনো ত্রুটি আসেনি।"
+
+#: js/ui/lookingGlass.js:725
+msgid "Hide Errors"
+msgstr "ত্রুটি লুকান"
+
+#: js/ui/lookingGlass.js:729 js/ui/lookingGlass.js:794
+msgid "Show Errors"
+msgstr "ত্রুটি দেখান"
+
+#: js/ui/lookingGlass.js:738
+msgid "Enabled"
+msgstr "সক্রিয়"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:741 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "নিষ্ক্রিয়"
+
+#: js/ui/lookingGlass.js:743
+#: subprojects/extensions-app/data/ui/extension-row.ui:188
+msgid "Error"
+msgstr "ত্রুটি"
+
+#: js/ui/lookingGlass.js:745
+msgid "Out of date"
+msgstr "মেয়াদ উত্তীর্ণ"
+
+#: js/ui/lookingGlass.js:747
+msgid "Downloading"
+msgstr "ডাউনলোড হচ্ছে"
+
+#: js/ui/lookingGlass.js:776
+msgid "View Source"
+msgstr "উৎস দেখুন"
+
+#: js/ui/lookingGlass.js:785
+msgid "Web Page"
+msgstr "ওয়েব পেজ"
+
+#: js/ui/main.js:294
+msgid "Logged in as a privileged user"
+msgstr "একটি সুবিধাযুক্ত ব্যবহারকারী হয়ে লগ ইন আছে"
+
+#: js/ui/main.js:295
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"সুরক্ষার কারণে একটি বিশেষাধিকারী ব্যবহারকারী হয়ে একটি অধিবেশন চালানো এড়ানো "
+"উচিত। যদি সম্ভব হয় তবে আপনার সাধারণ ব্যবহারকারী হয়ে লগ ইন করা উচিত।"
+
+#: js/ui/main.js:334
+msgid "Screen Lock disabled"
+msgstr "স্ক্রীনের লক নিষ্ক্রিয় করা হয়েছে"
+
+#: js/ui/main.js:335
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "স্ক্রীন লক করার জন্য জিনোম প্রদর্শন পরিচালক প্রয়োজন।"
+
+#: js/ui/messageTray.js:1476
+msgid "System Information"
+msgstr "সিস্টেম তথ্য"
+
+#: js/ui/mpris.js:203
+msgid "Unknown artist"
+msgstr "অজানা শিল্পী"
+
+#: js/ui/mpris.js:213
+msgid "Unknown title"
+msgstr "অজানা শিরোনাম"
+
+#: js/ui/overview.js:74
+msgid "Undo"
+msgstr "পূর্বাবস্থা"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:87
+msgid "Overview"
+msgstr "সংক্ষিপ্ত বর্ণনা"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:108
+msgid "Type to search"
+msgstr "অনুসন্ধান করতে লিখুন"
+
+#: js/ui/padOsd.js:96
+msgid "New shortcut…"
+msgstr "নতুন শর্টকাট"
+
+#: js/ui/padOsd.js:143
+msgid "Application defined"
+msgstr "অ্যাপ্লিকেশন সংজ্ঞায়িত"
+
+#: js/ui/padOsd.js:144
+msgid "Show on-screen help"
+msgstr "অন-স্ক্রিন সহায়তা প্রদর্শন করুন"
+
+#: js/ui/padOsd.js:145
+msgid "Switch monitor"
+msgstr "মনিটর পাল্টান"
+
+#: js/ui/padOsd.js:146
+msgid "Assign keystroke"
+msgstr "কীস্ট্রোক বরাদ্দ করুন"
+
+#: js/ui/padOsd.js:212
+msgid "Done"
+msgstr "সম্পন্ন"
+
+#: js/ui/padOsd.js:732
+msgid "Edit…"
+msgstr "সম্পাদনা করুন ..."
+
+#: js/ui/padOsd.js:774 js/ui/padOsd.js:891
+msgid "None"
+msgstr "কোনটাই না"
+
+#: js/ui/padOsd.js:845
+msgid "Press a button to configure"
+msgstr "কনফিগার করতে একটি বোতাম টিপুন"
+
+#: js/ui/padOsd.js:846
+msgid "Press Esc to exit"
+msgstr "প্রস্থান করতে Esc টিপুন"
+
+#: js/ui/padOsd.js:849
+msgid "Press any key to exit"
+msgstr "প্রস্থান করার জন্য যে কোন কী টিপুন"
+
+#: js/ui/panel.js:107
+msgid "Quit"
+msgstr "প্রস্থান"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:435
+msgid "Activities"
+msgstr "ক্রিয়াকলাপ"
+
+#: js/ui/panel.js:719
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "সিস্টেম"
+
+#: js/ui/panel.js:830
+msgid "Top Bar"
+msgstr "শীর্ষ দন্ড"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "একটি কম্যান্ড চালান"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "বন্ধ করতে ESC টিপুন"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "পুনরায় চালু করা ওয়েল্যান্ড এ উপলব্ধ নয়"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "পুনরায় আরম্ভ করা হচ্ছে…"
+
+#: js/ui/screenShield.js:203
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME কে স্ক্রীন লক করতে হবে"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:244 js/ui/screenShield.js:602
+msgid "Unable to lock"
+msgstr "লক করা যায়নি"
+
+#: js/ui/screenShield.js:245 js/ui/screenShield.js:603
+msgid "Lock was blocked by an application"
+msgstr "লক করা একটি অ্যাপ্লিকেশন দ্বারা আটকানো হয়েছে"
+
+#: js/ui/search.js:823
+msgid "Searching…"
+msgstr "অনুসন্ধান করা হচ্ছে..."
+
+#: js/ui/search.js:825
+msgid "No results."
+msgstr "কোনো ফলাফল নেই।"
+
+#: js/ui/search.js:951
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "আরও %d"
+msgstr[1] "আরও %d"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "প্রতিলিপি"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "লেপন"
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr "টেক্সট দেখান"
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr "টেক্সট লুকান"
+
+#: js/ui/shellEntry.js:162
+msgid "Caps lock is on."
+msgstr "ক্যাপ্‌স লক সক্রিয় আছে"
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr "লুকানো ভলিউম"
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr "উইন্ডোজ সিস্টেম ভলিউম"
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr "কীফাইল ব্যবহার করে"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"কীফাইলগুলি ব্যবহার করে এমন একটি ভলিউম আনলক করতে, পরিবর্তে <i>%s</i> ইউটিলিটিটি "
+"ব্যবহার করুন।"
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr "PIM সংখ্যা"
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "পাসওয়ার্ড মনে রাখুন"
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr "আন-লক"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "%s খুলুন"
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr "PIM অবশ্যই একটি নম্বর বা খালি থাকতে হবে।"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:465
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s চালু করতে ব্যর্থ"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:467
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s অ্যাপ্লিকেশনটি খুঁজে পাওয়া যায় নি"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "অ্যাক্সেসযোগ্যতা"
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr "জুম"
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr "স্ক্রীন রিডার"
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr "স্ক্রীন কীবোর্ড"
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr "ভিজ্যুয়াল সর্তকর্তা"
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr "স্টিকি কী"
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr "মন্থর কী"
+
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr "বাউন্স কী"
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr "মাউস কী"
+
+#: js/ui/status/accessibility.js:136
+msgid "High Contrast"
+msgstr "উচ্চ বৈপরীত্য"
+
+#: js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr "অপেক্ষাকৃত বড় টেক্সট"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "ব্লুটুথ"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:619
+msgid "Bluetooth Settings"
+msgstr "ব্লুটুথ সেটিং"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:148
+#, javascript-format
+#| msgid "%s Connected"
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d সংযুক্ত"
+msgstr[1] "%d সংযুক্ত"
+
+#: js/ui/status/bluetooth.js:152
+msgid "Bluetooth Off"
+msgstr "ব্লুটুথ নিষ্ক্রিয়"
+
+#: js/ui/status/bluetooth.js:154
+msgid "Bluetooth On"
+msgstr "ব্লুটুথ সক্রিয়"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "উজ্জ্বলতা"
+
+#: js/ui/status/dwellClick.js:13
+msgid "Single Click"
+msgstr "একক ক্লিক"
+
+#: js/ui/status/dwellClick.js:18
+msgid "Double Click"
+msgstr "ডবল ক্লিক"
+
+#: js/ui/status/dwellClick.js:23
+msgid "Drag"
+msgstr "টানা"
+
+#: js/ui/status/dwellClick.js:28
+msgid "Secondary Click"
+msgstr "অপ্রধান ক্লিক"
+
+#: js/ui/status/dwellClick.js:37
+msgid "Dwell Click"
+msgstr "বাসা ক্লিক"
+
+#: js/ui/status/keyboard.js:826
+msgid "Keyboard"
+msgstr "কীবোর্ড"
+
+#: js/ui/status/keyboard.js:848
+msgid "Show Keyboard Layout"
+msgstr "কীবোর্ড সজ্জা দেখান"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:174
+msgid "Location Enabled"
+msgstr "অবস্থান সক্রিয়"
+
+#: js/ui/status/location.js:66 js/ui/status/location.js:175
+msgid "Disable"
+msgstr "নিষ্ক্রিয় করুন"
+
+#: js/ui/status/location.js:67
+msgid "Privacy Settings"
+msgstr "গোপনীয়তা সেটিং"
+
+#: js/ui/status/location.js:173
+msgid "Location In Use"
+msgstr "অবস্থান ব্যবহৃত"
+
+#: js/ui/status/location.js:177
+msgid "Location Disabled"
+msgstr "অবস্থান অক্ষম"
+
+#: js/ui/status/location.js:178
+msgid "Enable"
+msgstr "সক্রিয়"
+
+#: js/ui/status/location.js:350
+msgid "Allow location access"
+msgstr "অবস্থান অ্যাক্সেসের অনুমতি দিন"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:352
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "অ্যাপ্লিকেশন %s আপনার অবস্থান অ্যাক্সেস করতে চায়"
+
+#: js/ui/status/location.js:362
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"গোপনীয়তা সেটিংস থেকে যে কোনও সময় অবস্থানের অ্যাক্সেস পরিবর্তন করা যেতে পারে।"
+
+#: js/ui/status/network.js:71
+msgid "<unknown>"
+msgstr "<unknown>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:449 js/ui/status/network.js:1344
+#, javascript-format
+msgid "%s Off"
+msgstr "%s বন্ধ"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:452
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s সংযুক্ত"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:457
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s অপরিচালিত"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:460
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s সংযোগ বিচ্ছিন্ন করা হচ্ছে"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:467 js/ui/status/network.js:1336
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s সংযোগ করা হচ্ছে"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:470
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s এর অনুমোদন প্রয়োজন"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:478
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "ফার্মওয়্যার %s এর জন্য অনুপস্থিত"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:482
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s অনুপলব্ধ"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:485
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s সংযোগ ব্যর্থ হয়েছে"
+
+#: js/ui/status/network.js:497
+msgid "Wired Settings"
+msgstr "তারযুক্ত সেটিং"
+
+#: js/ui/status/network.js:540
+msgid "Mobile Broadband Settings"
+msgstr "মোবাইল ব্রডব্যান্ড সেটিং"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:586 js/ui/status/network.js:1341
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s হার্ডওয়্যার নিষ্ক্রিয়"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:590
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s নিষ্ক্রিয়"
+
+#: js/ui/status/network.js:631
+msgid "Connect to Internet"
+msgstr "ইন্টারনেটের সাথে সংযুক্ত করুন"
+
+#: js/ui/status/network.js:835
+msgid "Airplane Mode is On"
+msgstr "বিমান মোড চালু আছে"
+
+#: js/ui/status/network.js:836
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "বিমান মোড চালু অবস্থায় Wi-Fi নিষ্ক্রিয় করা হয়।"
+
+#: js/ui/status/network.js:837
+msgid "Turn Off Airplane Mode"
+msgstr "বিমান মোড বন্ধ করুন"
+
+#: js/ui/status/network.js:846
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi বন্ধ"
+
+#: js/ui/status/network.js:847
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "একটি নেটওয়ার্কের সাথে সংযুক্ত হতে Wi-Fi চালু করতে হবে।"
+
+#: js/ui/status/network.js:848
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi চালু করুন"
+
+#: js/ui/status/network.js:873
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi নেটওয়ার্ক"
+
+#: js/ui/status/network.js:875
+msgid "Select a network"
+msgstr "একটি নেটওয়ার্ক নির্বাচন করুন"
+
+#: js/ui/status/network.js:907
+msgid "No Networks"
+msgstr "কোনো নেটওয়ার্ক নেই"
+
+#: js/ui/status/network.js:928 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "বন্ধ করতে হার্ডওয়্যার স্যুইচ ব্যবহার করুন"
+
+#: js/ui/status/network.js:1205
+msgid "Select Network"
+msgstr "একটি নেটওয়ার্ক নির্বাচন করুন"
+
+#: js/ui/status/network.js:1211
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi সেটিং"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1332
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s Hotspot সক্রিয়"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1347
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s সংযুক্ত নয়"
+
+#: js/ui/status/network.js:1444
+msgid "connecting…"
+msgstr "সংযোগ করা হচ্ছে..."
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1447
+msgid "authentication required"
+msgstr "পরিচয় প্রমাণ করা আবশ্যক"
+
+#: js/ui/status/network.js:1449
+msgid "connection failed"
+msgstr "সংযোগ ব্যর্থ হয়েছে"
+
+#: js/ui/status/network.js:1500
+msgid "VPN Settings"
+msgstr "VPN সেটিং"
+
+#: js/ui/status/network.js:1517
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1527
+msgid "VPN Off"
+msgstr "VPN বন্ধ"
+
+#: js/ui/status/network.js:1588 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "নেটওয়ার্ক সেটিং"
+
+#: js/ui/status/network.js:1617
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s তারযুক্ত সংযোগ"
+msgstr[1] "%s তারযুক্ত সংযোগগুলি"
+
+#: js/ui/status/network.js:1621
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s ওয়াই-ফাই সংযোগ"
+msgstr[1] "%s ওয়াই-ফাই সংযোগগুলি"
+
+#: js/ui/status/network.js:1625
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s মডেম সংযোগ"
+msgstr[1] "%s মডেম সংযোগগুলি"
+
+#: js/ui/status/network.js:1759
+msgid "Connection failed"
+msgstr "সংযোগ ব্যর্থ হয়েছে"
+
+#: js/ui/status/network.js:1760
+msgid "Activation of network connection failed"
+msgstr "নেটওয়ার্ক সংযোগের সক্রিয়করণ ব্যর্থ হয়েছে"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "নাইট লাইট অক্ষম"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "নাইট লাইট সক্ষম"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "জীবনবৃত্তান্ত"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "আগামীকাল অবধি অক্ষম করুন"
+
+#: js/ui/status/power.js:47
+msgid "Power Settings"
+msgstr "পাওয়ার সেটিং"
+
+#: js/ui/status/power.js:63
+msgid "Fully Charged"
+msgstr "পুরোপুরি চার্জ রয়েছে"
+
+#: js/ui/status/power.js:69
+msgid "Not Charging"
+msgstr "চার্জ হচ্ছে না"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:72 js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "নির্ধারণ করা হচ্ছে…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d বাকি আছে (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d পুর হতে (%d%%)"
+
+#: js/ui/status/power.js:139 js/ui/status/power.js:141
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:45
+msgid "Screen is Being Shared"
+msgstr "স্ক্রিন ভাগ করা হচ্ছে"
+
+#: js/ui/status/remoteAccess.js:47
+msgid "Turn off"
+msgstr "বন্ধ করুন"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "বিমান মোড চালু আছে"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "লক"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "পাওয়ার বন্ধ / লগ আউট"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "থামানো"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "পুনরায় আরম্ভ করুন..."
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "পাওয়ার বন্ধ..."
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "লগ আউট করুন"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "ব্যবহারকারী পাল্টান..."
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "থান্ডারবোল্ট "
+
+#: js/ui/status/thunderbolt.js:325
+msgid "Unknown Thunderbolt device"
+msgstr "অজানা থান্ডারবোল্ট যন্ত্র"
+
+#: js/ui/status/thunderbolt.js:326
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"আপনি দূরে থাকাকালীন নতুন ডিভাইস সনাক্ত করা হয়েছে। দয়া করে ডিভাইসটি ব্যবহার শুরু "
+"করার জন্য এটি সংযোগ বিচ্ছিন্ন করুন এবং পুনরায় সংযোগ করুন।"
+
+#: js/ui/status/thunderbolt.js:329
+msgid "Unauthorized Thunderbolt device"
+msgstr "অননুমোদিত থান্ডারবোল্ট ডিভাইস"
+
+#: js/ui/status/thunderbolt.js:330
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "নতুন ডিভাইস সনাক্ত করা হয়েছে এবং প্রশাসকের দ্বারা অনুমোদিত হওয়া দরকার।"
+
+#: js/ui/status/thunderbolt.js:336
+msgid "Thunderbolt authorization error"
+msgstr "থান্ডারবোল্ট অনুমোদনের ত্রুটি"
+
+#: js/ui/status/thunderbolt.js:337
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "থান্ডারবোল্ট ডিভাইস অনুমোদন করতে পারেনি: %s"
+
+#: js/ui/status/volume.js:155
+msgid "Volume changed"
+msgstr "ভলিউম পরিবর্তিত"
+
+#: js/ui/status/volume.js:217
+msgid "Volume"
+msgstr "ভলিউম"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "আয়না"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "ডিসপ্লে যোগ করুন"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "কেবলমাত্র বাহ্যিক"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "কেবল অন্তর্নির্মিত"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:371
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:377
+msgid "Swipe up to unlock"
+msgstr "আনলক করতে অপরের দইকে সোয়াইপ করুন"
+
+#: js/ui/unlockDialog.js:378
+msgid "Click or press a key to unlock"
+msgstr "আনলক করতে একটি কী টিপুন বা ক্লিক করুন"
+
+#: js/ui/unlockDialog.js:555
+msgid "Unlock Window"
+msgstr "উইন্ডো আন-লক করুন"
+
+#: js/ui/unlockDialog.js:564
+msgid "Log in as another user"
+msgstr "অপর ব্যবহারকারী হিসাবে লগ ইন করুন"
+
+#: js/ui/viewSelector.js:182
+msgid "Applications"
+msgstr "অ্যাপ্লিকেশন"
+
+#: js/ui/viewSelector.js:186
+msgid "Search"
+msgstr "অনুসন্ধান"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” প্রস্তুত"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:60
+msgid "Keep these display settings?"
+msgstr "আপনি কি এই প্রদর্শন সেটিংগুলি রাখতে চান?"
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:69
+msgid "Revert Settings"
+msgstr "সেটিং পাল্টান"
+
+#: js/ui/windowManager.js:72
+msgid "Keep Changes"
+msgstr "পরিবর্তনগুলি রাখুন"
+
+#: js/ui/windowManager.js:91
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "সেটিং পরিবর্তন %d সেকেন্ডের মধ্যে পূর্বাবস্থা করা হবে"
+msgstr[1] "সেটিং পরিবর্তন %d সেকেন্ডের মধ্যে পূর্বাবস্থা করা হবে"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:551
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr "ছোট করুন"
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr "আন-ম্যাক্সিমাইজ"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "বড় করুন"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "স্থানান্তরণ"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "মাপ পরিবর্তন"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr "টাইটেল-বার অন-স্ক্রীন সরান"
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "সর্বদা উপরে"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr "সর্বদা দৃশ্যমান ওয়ার্কস্পেসে"
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr "বামদিকের কর্মক্ষেত্রে স্থানান্তরণ"
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr "ডান্ডিকের কর্মক্ষেত্রে স্থানান্তরণ"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "উপরের কর্মক্ষেত্রে স্থানান্তরণ"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "নীচের কর্মক্ষেত্রে স্থানান্তরণ"
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr "মনিটর অপরের দিকে এ সরান"
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr "মনিটর নিচের দিকে সরান"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "মনিটর বাম দিকে সরান"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "মনিটর দান দিকে সরান"
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "বন্ধ করুন"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution বর্ষপঞ্জি"
+
+#: src/main.c:458 subprojects/extensions-tool/src/main.c:317
+msgid "Print version"
+msgstr "মুদ্রণ সংস্করণ"
+
+#: src/main.c:464
+msgid "Mode used by GDM for login screen"
+msgstr "লগিন স্ক্রীনের জন্য GDM দ্বারা ব্যবহৃত মোড"
+
+#: src/main.c:470
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "একটি নির্দিষ্ট মোড ব্যবহার করুন, উদাঃ লগইন স্ক্রিনের জন্য \"gdm\""
+
+#: src/main.c:476
+msgid "List possible modes"
+msgstr "সম্ভাব্য মোডগুলির তালিকা করুন"
+
+#: src/shell-app.c:268
+msgctxt "program"
+msgid "Unknown"
+msgstr "অজানা"
+
+#: src/shell-app.c:519
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s\" লঞ্চ করা গেল না"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "পাসওয়ার্ডগুলি মিলছে না।"
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "পাসওয়ার্ড খালি থাকলে চলবে না"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "প্রমাণীকরণের ডায়ালগ ব্যবহারকারী দ্বারা বাতিল করা হয়েছে"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:183
+#: subprojects/extensions-app/data/ui/extensions-window.ui:61
+msgid "Extensions"
+msgstr "এক্সটেনশনগুলি"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:184
+msgid "Manage your GNOME Extensions"
+msgstr "আপনার জিনোম এক্সটেনশনগুলি পরিচালনা করুন"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "জিনোম প্রকল্প"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"জিনোম এক্সটেনশনস এক্সটেনশানগুলি আপডেট করতে, এক্সটেনশান পছন্দগুলি কনফিগার করে এবং "
+"অযাচিত এক্সটেনশানগুলি সরিয়ে বা অক্ষম করে।"
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME সেল এক্সটেনশন কনফিগার করুন"
+
+#: subprojects/extensions-app/js/main.js:145
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "“%s” সরাবেন? "
+
+#: subprojects/extensions-app/js/main.js:146
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"আপনি যদি এক্সটেনশনটি সরিয়ে থাকেন তবে আপনি যদি আবার সক্ষম করতে চান তবে এটি "
+"ডাউনলোড করতে আপনাকে ফিরে যেতে হবে"
+
+#: subprojects/extensions-app/js/main.js:150
+msgid "Remove"
+msgstr "মুছে ফেলুন"
+
+#: subprojects/extensions-app/js/main.js:182
+msgid "translator-credits"
+msgstr "অনুবাদক-ক্রেডিট"
+
+#: subprojects/extensions-app/js/main.js:314
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d টা এক্সটেনশন পরবর্তী লগইন আপডেট করা হবে।"
+msgstr[1] "%d টি এক্সটেনশনগুলি পরবর্তী লগইন আপডেট করা হবে।"
+
+#: subprojects/extensions-app/js/main.js:461
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "এক্সটেনশনটি বর্তমান জিনোম সংস্করণের সাথে বেমানান"
+
+#: subprojects/extensions-app/js/main.js:464
+msgid "The extension had an error"
+msgstr "এক্সটেনশানটিতে একটি ত্রুটি ঘটেছিল"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:109
+#: subprojects/extensions-tool/src/command-create.c:325
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "বিবরণ"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:132
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "সংস্করণ"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:160
+msgid "Author"
+msgstr "লেখক"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:216
+msgid "Website"
+msgstr "ওয়েবসাইট"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:233
+msgid "Remove…"
+msgstr "সরান..."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:8
+msgid "Help"
+msgstr "সাহায্য"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:12
+msgid "About Extensions"
+msgstr "এক্সটেনশন সম্পর্কে"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"এক্সটেনশানগুলি সন্ধান এবং যুক্ত করতে, <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a> দেখুন।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:35
+msgid "Warning"
+msgstr "সতর্কতা"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:46
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr ""
+"এক্সটেনশানগুলি পারফরম্যান্স সমস্যা সহ সিস্টেম সম্পর্কিত সমস্যা তৈরি করতে পারে। যদি "
+"আপনার সিস্টেমে সমস্যা দেখা দেয় তবে সমস্ত এক্সটেনশন অক্ষম করার পরামর্শ দেওয়া হচ্ছে।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:135
+msgid "Manually Installed"
+msgstr "ম্যানুয়ালি ইনস্টল করা হয়েছে"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:159
+msgid "Built-In"
+msgstr "অন্তর্নির্মিত"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:200
+msgid "No Installed Extensions"
+msgstr "কোনও ইনস্টল করা এক্সটেনশন নেই"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:236
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"আমরা খুব দুঃখিত, তবে ইনস্টল হওয়া এক্সটেনশনের তালিকা পাওয়া সম্ভব ছিল না। নিশ্চিত "
+"হয়ে নিন যে আপনি জিনোমে লগইন করেছেন এবং আবার চেষ্টা করুন।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:273
+msgid "Extension Updates Ready"
+msgstr "এক্সটেনশন আপডেট প্রস্তুত"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:289
+msgid "Log Out…"
+msgstr "লগ আউট..."
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:226
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "নতুন এক্সটেনশনটি সফলভাবে %s তে তৈরি হয়েছিল।\n"
+
+#: subprojects/extensions-tool/src/command-create.c:299
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"নাম খুব অল্প (আদর্শগতভাবে বর্ণনামূলক) স্ট্রিং হতে হবে।\n"
+"উদাহরণগুলি হল: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:305
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "নাম"
+
+#: subprojects/extensions-tool/src/command-create.c:319
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"বর্ণনাটি আপনার এক্সটেনশনটি কী করে তার একক বাক্য ব্যাখ্যা।\n"
+"উদাহরণগুলি হল: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:339
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID হ'ল আপনার এক্সটেনশনের জন্য বিশ্বব্যাপী অনন্য শনাক্তকারী।\n"
+"এটি কোনও ইমেল ঠিকানার ফর্ম্যাটে থাকতে হবে (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:366
+msgid "Choose one of the available templates:\n"
+msgstr "উপলব্ধ টেম্পলেটগুলির মধ্যে একটি চয়ন করুন:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:380
+msgid "Template"
+msgstr "টেমপ্লেট"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "The unique identifier of the new extension"
+msgstr "নতুন এক্সটেনশনের অনন্য শনাক্তকারী"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "NAME"
+msgstr "নাম"
+
+#: subprojects/extensions-tool/src/command-create.c:439
+msgid "The user-visible name of the new extension"
+msgstr "নতুন এক্সটেনশনের ব্যবহারকারী-দৃশ্যমান নাম"
+
+#: subprojects/extensions-tool/src/command-create.c:441
+msgid "DESCRIPTION"
+msgstr "বর্ণনা"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "A short description of what the extension does"
+msgstr "এক্সটেনশনটি কী করে তার একটি সংক্ষিপ্ত বিবরণ"
+
+#: subprojects/extensions-tool/src/command-create.c:446
+msgid "TEMPLATE"
+msgstr "টেমপ্লেট"
+
+#: subprojects/extensions-tool/src/command-create.c:447
+msgid "The template to use for the new extension"
+msgstr "নতুন এক্সটেনশনের জন্য ব্যবহৃত টেমপ্লেট"
+
+#: subprojects/extensions-tool/src/command-create.c:453
+msgid "Enter extension information interactively"
+msgstr "ইন্টারেক্টিভভাবে এক্সটেনশনের তথ্য লিখুন"
+
+#: subprojects/extensions-tool/src/command-create.c:461
+msgid "Create a new extension"
+msgstr "একটি নতুন এক্সটেনশন তৈরি করুন"
+
+#: subprojects/extensions-tool/src/command-create.c:479
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "অজানা যুক্তি"
+
+#: subprojects/extensions-tool/src/command-create.c:504
+msgid "UUID, name and description are required"
+msgstr "UUID, নাম এবং বিবরণ প্রয়োজন"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "জিনোম শেলের সাথে সংযোগ করতে ব্যর্থ\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "এক্সটেনশন \"%s\" বিদ্যমান নেই\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "একটি এক্সটেনশন অক্ষম করুন"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:97
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "কোনও UUID দেওয়া হয়নি"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:102
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "একাধিক UUID দেওয়া হয়েছে"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "একটি এক্সটেনশন সক্ষম করুন"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "এক্সটেনশন \"%s\" বিদ্যমান নেই\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "এক্সটেনশনের তথ্য দেখান"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "একটি বিদ্যমান এক্সটেনশান ওভাররাইট করুন"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "একটি এক্সটেনশন বান্ডিল ইনস্টল করুন"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "কোনও এক্সটেনশন বান্ডেল নির্দিষ্ট করা হয়নি"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "একাধিক এক্সটেনশন বান্ডিল নির্দিষ্ট করা হয়েছে"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "ব্যবহারকারী-ইনস্টল করা এক্সটেনশানগুলি দেখান"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "সিস্টেম-ইনস্টল করা এক্সটেনশানগুলি দেখান"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "সক্ষম এক্সটেনশনগুলি দেখান"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "অক্ষম এক্সটেনশানগুলি দেখান"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "পছন্দগুলি সহ এক্সটেনশানগুলি দেখান"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "আপডেটগুলি সহ এক্সটেনশানগুলি দেখান"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "এক্সটেনশান বিশদ প্রিন্ট করুন"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "ইনস্টল করা এক্সটেনশনের তালিকা দিন"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ফাইল"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "বান্ডলে অন্তর্ভুক্ত করার জন্য অতিরিক্ত উত্স"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "স্কিমা"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "একটি জিএসটিটিংস স্কিমা যা অন্তর্ভুক্ত করা উচিত"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ডিরেক্টরী"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "যে ডিরেক্টরিটি অনুবাদ খুঁজে পাওয়া যায়"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ডোমেইন"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "অনুবাদগুলির জন্য ব্যবহারের জন্য gettext ডোমেন"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "বিদ্যমান প্যাকটি ওভাররাইট করুন"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "যে ডিরেক্টরিটি প্যাকটি তৈরি করা উচিত"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "SOURCE_DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "একটি এক্সটেনশন বান্ডিল তৈরি করুন"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "একাধিক উত্স ডিরেক্টরি নির্দিষ্ট করা আছে"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "এক্সটেনশান \"%s\" এর পছন্দসইগুলি নেই\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:79
+msgid "Opens extension preferences"
+msgstr "এক্সটেনশন পছন্দগুলি খোলে"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "একটি এক্সটেনশন পুনরায় সেট করুন"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "সিস্টেম এক্সটেনশানগুলি আনইনস্টল করতে পারা যায় না\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "“%s\" আনইনস্টল করতে ব্যর্থ\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "একটি এক্সটেনশন আনইনস্টল করুন"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "ত্রুটি বার্তা মুদ্রণ করবেন না"
+
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell"
+msgstr "জিনোম শেলের সাথে সংযোগ করতে ব্যর্থ"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "পথ"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "মূল লেখক"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "রাষ্ট্র"
+
+#: subprojects/extensions-tool/src/main.c:290
+msgid "“version” takes no arguments"
+msgstr "\"সংস্করণ\" তে কোন যুক্তি লাগে না"
+
+#: subprojects/extensions-tool/src/main.c:292
+#: subprojects/extensions-tool/src/main.c:312
+msgid "Usage:"
+msgstr "ব্যবহার:"
+
+#: subprojects/extensions-tool/src/main.c:295
+msgid "Print version information and exit."
+msgstr "সংস্করণ তথ্য মুদ্রণ করুন এবং প্রস্থান করুন।"
+
+#: subprojects/extensions-tool/src/main.c:310
+#: subprojects/extensions-tool/src/main.c:313
+msgid "COMMAND"
+msgstr "কমান্ড"
+
+#: subprojects/extensions-tool/src/main.c:313
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:315
+msgid "Commands:"
+msgstr "কমান্ডগুলি:"
+
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Print help"
+msgstr "মুদ্রণ সহায়তা"
+
+#: subprojects/extensions-tool/src/main.c:318
+msgid "Enable extension"
+msgstr "এক্সটেনশন সক্ষম করুন"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Disable extension"
+msgstr "এক্সটেনশন অক্ষম করুন"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Reset extension"
+msgstr "এক্সটেনশন পুনঃসারণ করুন"
+
+#: subprojects/extensions-tool/src/main.c:321
+msgid "Uninstall extension"
+msgstr "এক্সটেনশন আনইনস্টল করুন"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "List extensions"
+msgstr "এক্সটেন্সানগুলির তালিকা দেখান"
+
+#: subprojects/extensions-tool/src/main.c:323
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Show extension info"
+msgstr "এক্সটেনশনের তথ্য দেখান"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Open extension preferences"
+msgstr "এক্সটেনশন পছন্দগুলি খুলুন"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "Create extension"
+msgstr "এক্সটেনশন তৈরি করুন"
+
+#: subprojects/extensions-tool/src/main.c:327
+msgid "Package extension"
+msgstr "এক্সটেন্সান প্যাকেজ করুন"
+
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Install extension bundle"
+msgstr "এক্সটেনশন বান্ডেল ইনস্টল করুন"
+
+#: subprojects/extensions-tool/src/main.c:330
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "বিস্তারিত সহায়তা পেতে \"%s\" ব্যবহার করুন।\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "সাধারণ"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "একটি ফাঁকা এক্সটেনশন"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "সূচক"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "উপরের বারে একটি আইকন যুক্ত করুন"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u আউটপুট"
+msgstr[1] "%u আউটপুট"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ইনপুট"
+msgstr[1] "%u ইনপুট"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "সিস্টেম সাউন্ড"
+
+#~ msgid "Show the message tray"
+#~ msgstr "বার্তা ট্রে দেখান"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME সেল (ওয়েল্যান্ড কম্পোজিটর)"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "ক্যালেন্ডারে সপ্তাহের তারিখ দেখান"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "সত্য হলে, ক্যালেন্ডারে ISO সপ্তাহ তারিখ দেখান।"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "সকল চলা জোড়া সাময়িক থামানো এবং আবার শুরু করার কীবাইন্ডিং, ডিবাগিং করার জন্য"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "কোন কীবোর্ড ব্যবহার করা হবে"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "যে ধরনের কীবোর্ড ব্যবহার করা হবে।"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "%s -এর অগ্রাধিকার ডায়ালগ লোড করতে একটি সমস্যা দেখা দিয়েছে:"
+
+#~ msgid "Next"
+#~ msgstr "পরবর্তী"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "সাইন ইন"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "ওয়েব প্রমাণীকরণ পুনঃনির্দেশ"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "প্রায়ই ব্যবহৃত অ্যাপ্লিকেশনগুলি এখানে দেখা যাবে"
+
+#~ msgid "Frequent"
+#~ msgstr "প্রায়ই"
+
+#~ msgid "All"
+#~ msgstr "সকল"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%ঘ∶%মি"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "রবি"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "সোম"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "মঙ্গল"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "বুধ"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "বৃহ"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "শুক্র"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "শনি"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "কিছু পরিকল্পিত নেই"
+
+#~ msgid "This week"
+#~ msgstr "এই সপ্তাহ"
+
+#~ msgid "Next week"
+#~ msgstr "পরবর্তী সপ্তাহ"
+
+#~ msgid "Removable Devices"
+#~ msgstr "অপসারণযোগ্য ডিভাইসগুলি"
+
+#~ msgid "Eject"
+#~ msgstr "ইজেক্ট"
+
+#~ msgid "Password:"
+#~ msgstr "পাসওয়ার্ড:"
+
+#~ msgid "Type again:"
+#~ msgstr " আবার লিখুন:"
+
+#~ msgid "Password: "
+#~ msgstr "পাসওয়ার্ড:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "বেতার নেটওয়ার্ক দ্বারা প্রমাণীকরণ প্রয়োজন"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "মোবাইল ব্রডব্যান্ড নেটওয়ার্ক পাসওয়ার্ড"
+
+#~ msgid "Invitation"
+#~ msgstr "আমন্ত্রণ"
+
+#~ msgid "Call"
+#~ msgstr "কল"
+
+#~ msgid "File Transfer"
+#~ msgstr "ফাইল স্থানান্তর"
+
+#~ msgid "Chat"
+#~ msgstr "চ্যাট"
+
+#~ msgid "Unmute"
+#~ msgstr "সরব"
+
+#~ msgid "Mute"
+#~ msgstr "নীরব"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "%s -এ আমন্ত্রণ"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s আপনাকে %s-এ আমন্ত্রণ জানাচ্ছেন"
+
+#~ msgid "Decline"
+#~ msgstr "প্রত্যাখ্যান করুন"
+
+#~ msgid "Accept"
+#~ msgstr "গ্রহণ করুন"
+
+#~ msgid "Video call from %s"
+#~ msgstr "%s থেকে ভিডিও কল"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s থেকে কল"
+
+#~ msgid "Answer"
+#~ msgstr "উত্তর দিন"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s আপনাকে %s পাঠাচ্ছেন"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s আপনি অনলাইনে থাকলে তা দেখার অনুমতি চায়"
+
+#~ msgid "Network error"
+#~ msgstr "নেটওয়ার্ক সংক্রান্ত ত্রুটি"
+
+#~ msgid "Authentication failed"
+#~ msgstr "অনুমোদন ব্যর্থ"
+
+#~ msgid "Encryption error"
+#~ msgstr "এনক্রিপশন সংক্রান্ত ত্রুটি"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "সার্টিফিকেট উপলব্ধ করা হয়নি"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "সার্টিফিকেট বিশ্বস্ত নয়"
+
+#~ msgid "Certificate expired"
+#~ msgstr "সার্টিফিকেটের মেয়াদ উত্তীর্ণ হয়েছে"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "সার্টিফিকেট সক্রিয় করা হয়নি"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "সার্টিফিকেটের হোস্টনেম-এ গরমিল"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "সার্টিফিকেটের ফিনঙ্গারপ্রিন্ট-এ গরমিল"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "স্বয়ং স্বাক্ষরিত সার্টিফিকেট"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "স্ট্যাটাস অফলাইনে সেট করা হয়েছে"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "সার্টিফিকেট অবৈধ"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "সংযোগ প্রত্যাখ্যান করা হয়েছে"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "সংযোগ স্থাপন করা যাচ্ছে না"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "সংযোগ হারিয়ে গেছে"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "এই অ্যাকাউন্ট সার্ভারের সংগে ইতিমধ্যেই সংযুক্ত"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "একই রিসোর্স ব্যবহার করে একটি নতুন সংযোগ দ্বারা সংযোগ প্রতিস্থাপন করা হয়েছে"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "অ্যাকাউন্ট ইতিমধ্যেই সার্ভারে উপস্থিত"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "সংযোগ ব্যবস্থাপনা করার দিক থেকে সার্ভার বর্তমানে অত্যন্ত ব্যস্ত"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "সার্টিফিকেট তুলে নেওয়া হয়েছে"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "সার্টিফিকেট এক অনিরাপদ cipher অ্যালগরিদম ব্যবহার করে বা ক্রিপটোগ্র্যাফিক দিক "
+#~ "থেকে দুর্বল"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "সার্ভার সার্টিফিকেটের দৈর্ঘ্য, বা সার্ভার সার্টিফিকেট চেনের গভীরতা, "
+#~ "ক্রিপটোগ্র্যাফি লাইব্রেরি দ্বারা নির্দিষ্ট সীমা ছাড়িয়ে গেছে"
+
+#~ msgid "Internal error"
+#~ msgstr "অভ্যন্তরীণ ত্রুটি"
+
+#~ msgid "View account"
+#~ msgstr "অ্যাকাউন্ট দেখুন"
+
+#~ msgid "Open Calendar"
+#~ msgstr "ক্যালেন্ডার খুলুন"
+
+#~ msgid "Open"
+#~ msgstr "খুলুন"
+
+#~ msgid "Clear Messages"
+#~ msgstr "বার্তাগুলি সাফ করুন"
+
+#~ msgid "Notification Settings"
+#~ msgstr "বিজ্ঞপ্তি সেটিং"
+
+#~ msgid "Tray Menu"
+#~ msgstr "ট্রে মেনু"
+
+#~ msgid "No Messages"
+#~ msgstr "কোনো বার্তা নেই"
+
+#~ msgid "Message Tray"
+#~ msgstr "বার্তা ট্রে"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d টি নতুন বার্তা"
+#~ msgstr[1] "%d টি নতুন বার্তা"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d টি সংযুক্ত ডিভাইস"
+#~ msgstr[1] "%d টি সংযুক্ত ডিভাইস"
+
+#~ msgid "In Use"
+#~ msgstr "ব্যবহৃত হচ্ছে"
+
+#~ msgid "Off"
+#~ msgstr "বন্ধ"
+
+#~ msgid "connecting..."
+#~ msgstr "সংযোগ করা হচ্ছে..."
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "ব্যাটারি"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "বিমান মোড"
+
+#~ msgid "On"
+#~ msgstr "চালু"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "সজ্জা লক"
diff --git a/po/bs.po b/po/bs.po
new file mode 100644
index 0000000..d2471f1
--- /dev/null
+++ b/po/bs.po
@@ -0,0 +1,1735 @@
+# Bosnian translation for bosnianuniversetranslation
+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
+# This file is distributed under the same license as the bosnianuniversetranslation package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: bosnianuniversetranslation\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2015-02-27 03:04+0000\n"
+"PO-Revision-Date: 2015-03-01 23:13+0100\n"
+"Last-Translator: Samir Ribić <Unknown>\n"
+"Language-Team: Bosnian <bs@li.org>\n"
+"Language: bs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%"
+"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Launchpad-Export-Date: 2015-02-05 07:05+0000\n"
+"X-Generator: Poedit 1.7.4\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "Sistem"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message list"
+msgstr "Prikaži listu poruka"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "Fokusirati se na aktivnu obavijest"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "Pokaži pregled"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "Prikaži sve aplikacije"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "Otvorite izbornik aplikacije"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME školjka"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "Upravljanje prozorima i pokretanje programa"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME Shell postavke proširenja"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "Podesi GNOME Shell proširenja"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME školjka (wayland compositor)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Omogućite interne alate korisne za programere i testere iz Alt-F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Omogućava pristup internom ispravljanju grešaka i praćenje alata pomoću "
+"dijaloga Alt-F2."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "UUID nastavci za omogućavanje"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell ekstenzije imaju UUID osobinu; ovaj ključ prikazuje nastavke "
+"koji bi trebali biti učitani. Svaka se ekstenzija koja želi da se učita "
+"treba biti na ovom popisu. Također možete manipulirati ovim popisom sa "
+"OmogućiNastavke i OnemogućiNastavke D - Bus metodama na org.gnome.Shell."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "Onemogućava validaciju kompatibilnosti verzije nastavaka"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell će učitati samo ekstenzije koje podržavaju trenutno pokrenutu "
+"verziju. Uključivanje ove opcije će onemogućiti ovu provjeru i pokušati "
+"učitati sve nastavke bez obzira na verzije koje podržavaju."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Popis ID-desktop datoteke za omiljene aplikacije"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Aplikacije koje odgovaraju ovim identifikatorima će biti prikazane u "
+"omiljenom području."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "Pregled birača aplikacije"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "Indeks trenutno odabranog pregleda u biraču aplikacija."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historija za dijalog komande (Alt-F2)."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "Historija za ogledalo dijalog."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "Uvijek prikaži 'Odjavi se' stavku menija u korisničkom meniju."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ovaj ključ nadjačava automatsko skrivanje 'Odjava' stavke izbornika jednog "
+"korisnika, u situacijama jedne sesije."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "Bilo da zapamti lozinku šifrovanog ili udaljenog fajl sistema."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Ljuska će zahtijevati lozinku kad je postavljen uređaj za šifrovanje ili "
+"udaljeni fajl sistem. Ako lozinka mozi biti spašena, u budućnosti će vam se "
+"prikazati kvadratić da oznacčte Zapamti lozinku. Ova tipka postavlja osnovno "
+"stanje kvadratića za označavanje."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "Prikaži datum sedmice u kalendaru."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "Ako je tačno, prikazi ISO datum sedmice u kalendaru."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "Prečica za otvaranje aplikacijskog menija."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "Prečica za otvaranje aplikacijskog menija."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "Prečica za otvaranje \"Prikaži aplikacije\" prikaza."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"Prečica za otvaranje \"Prikaži aplikacije\" prikaza u pregledu aktivnosti."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the overview"
+msgstr "Prečica za otvaranje pregleda."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the Activities Overview."
+msgstr "Prečica za otvaranje pregleda aktivnosti."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "Prečica za mijenjanje prikaza poruke."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "Prečica za mijenjanje prikaza poruke."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "Precica za fokusiranje aktivne obavijesti."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "Precica za fokusiranje aktivne obavijesti."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"Kratice tipkovnice koje pauziraju i nastavljaju sve pokrenute tweens, što za "
+"svrhu ima ispravljanje pogrešaka"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "Koju tastaturu koristiti"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "Tip tastature za koristenje."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "Ograničite prebacivanje na trenutnom radnom prostoru."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ako je tačno, samo aplikacije koje imaju prozore na trenutnom radnom "
+"prostoru su prikazane u prekidaču. Inače, sve alpikacije su uključene."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "Mod aplikacijske ikone."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"Konfigurise kako su prozori prikazani u preklopniku. Ispravne mogucnosti su "
+"'slicicica-–samo' (prikazuje slicicu prozora), 'app-icon- samo' (prikazuje "
+"samo aplikaciju) ili 'oboje'."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ako je tačno, samo forme na trenutnom radnom prostoru su prikazane u "
+"prekidaču. Inače, svi prozori su uključeni."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "Pričvrstite modalni dijalog na izvornom prozoru"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ovaj ključ nadjačava ključ u org.gnome.mutter kada se izvodi GNOME školjka."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Omogući poravnanje rubova prilikom spuštanja prozora na rubove ekrana"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "Radni prostor je upravljan dinamički"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "Radni prostori samo na glavnom monitoru"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Kašnjenje fokusa rezultuje promjenama u načinu miša dok pokazivač prestaje "
+"s kretanjem"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Network Login"
+msgstr "Mrežna prijava"
+
+#: ../js/extensionPrefs/main.js:123
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "Postoji greška koja se javila pri učitanju dijaloga postavki za %s:"
+
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extensions"
+msgstr "GNOME Shell Nastavci"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:916
+msgid "Cancel"
+msgstr "Otkaži"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "Sljedeći"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "Otključaj"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "Prijava"
+
+#: ../js/gdm/loginDialog.js:276
+msgid "Choose Session"
+msgstr "Odaberite sesiju"
+
+#: ../js/gdm/loginDialog.js:417
+msgid "Not listed?"
+msgstr "Nije izlistano?"
+
+#: ../js/gdm/loginDialog.js:826
+msgid "(e.g., user or %s)"
+msgstr "(npr., korisnik ili %s)"
+
+#: ../js/gdm/loginDialog.js:831 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "Korisničko ime: "
+
+#: ../js/gdm/loginDialog.js:1166
+msgid "Login Window"
+msgstr "Prozor za prijavu"
+
+#: ../js/gdm/util.js:341
+msgid "Authentication error"
+msgstr "Greška pri prijavljivanju"
+
+#: ../js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr "(ili pređi prstom)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "Naredba nije pronađena"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "Nije moguće analizirati naredbu:"
+
+#: ../js/misc/util.js:156
+msgid "Execution of “%s” failed:"
+msgstr "Završavanje \"%s\" nije uspjelo:"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login
+#. * window, until we know the title of the actual login page */
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "Preusmjerenje web provjere autentičnosti"
+
+#: ../js/ui/appDisplay.js:785
+msgid "Frequently used applications will appear here"
+msgstr "Često korištene aplikacije će se pojaviti ovdje"
+
+#: ../js/ui/appDisplay.js:905
+msgid "Frequent"
+msgstr "Često"
+
+#: ../js/ui/appDisplay.js:912
+msgid "All"
+msgstr "Svi"
+
+#: ../js/ui/appDisplay.js:1840
+msgid "New Window"
+msgstr "Novi prozor"
+
+#: ../js/ui/appDisplay.js:1868 ../js/ui/dash.js:289
+msgid "Remove from Favorites"
+msgstr "Ukloni iz omiljenih"
+
+#: ../js/ui/appDisplay.js:1874
+msgid "Add to Favorites"
+msgstr "Dodaj u omiljene"
+
+#: ../js/ui/appDisplay.js:1884
+msgid "Show Details"
+msgstr "Prikaži detalje"
+
+#: ../js/ui/appFavorites.js:132
+msgid "%s has been added to your favorites."
+msgstr "%s je dodan u vaše favorite."
+
+#: ../js/ui/appFavorites.js:166
+msgid "%s has been removed from your favorites."
+msgstr "%s je uklonjen iz vaših favorita."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:649
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "Podešavanja"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "Promjeni pozadinu"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday). */
+#: ../js/ui/calendar.js:52
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can usi less then 10 characters
+#. */
+#: ../js/ui/calendar.js:81
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Cijeli dan"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:88 ../js/ui/calendar.js:1562
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:97
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:111
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:113
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "U"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Č"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:590
+msgid "Previous month"
+msgstr "Prethodni mjesec"
+
+#: ../js/ui/calendar.js:600
+msgid "Next month"
+msgstr "Sljedeći mjesec"
+
+#: ../js/ui/calendar.js:1245
+msgid "Clear section"
+msgstr "Očisti sekciju"
+
+#: ../js/ui/calendar.js:1423
+msgid "Events"
+msgstr "Događaji"
+
+#: ../js/ui/calendar.js:1431
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/calendar.js:1435
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:1528
+msgid "Notifications"
+msgstr "Obavijesti"
+
+#: ../js/ui/calendar.js:1665
+msgid "No Notifications"
+msgstr "Nema obavijesti"
+
+#: ../js/ui/calendar.js:1668
+msgid "No Events"
+msgstr "Nema događaja"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "Vanjski disk povezan"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "Vanjski disk iskopčan"
+
+#: ../js/ui/components/autorunManager.js:354
+msgid "Open with %s"
+msgstr "Otvori pomoću %s"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "Lozinka:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "Upišite ponovo:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:919
+msgid "Connect"
+msgstr "Povezati"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "Lozinka: "
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "Kljuc: "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "Identitet: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "Osobni kljuc šifrovanja: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "Održavati: "
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "Bežična mreža zahtjeva provjeru vjerodostojnosti"
+
+#: ../js/ui/components/networkAgent.js:319
+msgid ""
+"Passwords or encryption keys are required to access the wireless network “%"
+"s”."
+msgstr ""
+"Lozinke ili kriptografski ključevi su potrebni za pristup bežičnoj mreži \"%s"
+"\"."
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "Žičana 802.1X provjera vjerodostojnosti"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "Naziv mreže: "
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "DSL provjera vjerodostojnosti"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "Potreban je PIN kod"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Potreban je PIN kod za povezivanje mobilnog širokopojasnog uređaja"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "Lozinka mobilne širokopojasne mreže"
+
+#: ../js/ui/components/networkAgent.js:346
+msgid "A password is required to connect to “%s”."
+msgstr "Potrebna je lozinka za povezivanje sa \"%s\"."
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "Neophodna provjera vjerodostojnosti"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "Administrator"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "Dokaži autentičnost"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (liki invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Žao nam je, nije uspjelo. Molimo pokušajte ponovno."
+
+#: ../js/ui/components/telepathyClient.js:243
+msgid "Invitation"
+msgstr "Pozivnica"
+
+#: ../js/ui/components/telepathyClient.js:303
+msgid "Call"
+msgstr "Poziv"
+
+#: ../js/ui/components/telepathyClient.js:319
+msgid "File Transfer"
+msgstr "Prijenos podataka"
+
+#: ../js/ui/components/telepathyClient.js:423
+msgid "Chat"
+msgstr "Ćaskanje"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:936 ../js/ui/dateMenu.js:209
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:943
+msgid "Yesterday, %H∶%M"
+msgstr "Jučer, %H∶%M"
+
+#. Translators: this is the week day nami followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:950
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month nami and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:957
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month nami, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:963
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 12h format */
+#: ../js/ui/components/telepathyClient.js:969 ../js/ui/dateMenu.js:212
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:976
+msgid "Yesterday, %l∶%M %p"
+msgstr "Jučer, %l∶%M %p"
+
+#. Translators: this is the week day nami followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:983
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month nami and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:990
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month nami, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:996
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM nami to their new
+#. IM nami. */
+#: ../js/ui/components/telepathyClient.js:1028
+msgid "%s is now known as %s"
+msgstr "%s je sada poznat kao %s"
+
+#. translators: argument is a room nami liki
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1132
+msgid "Invitation to %s"
+msgstr "Poziv za %s"
+
+#. translators: first argument is the nami of a contact and the second
+#. * one the nami of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1136
+msgid "%s is inviting you to join %s"
+msgstr "%s vas poziva da se pridružite %s"
+
+#: ../js/ui/components/telepathyClient.js:1140
+#: ../js/ui/components/telepathyClient.js:1175
+#: ../js/ui/components/telepathyClient.js:1207
+#: ../js/ui/components/telepathyClient.js:1273
+msgid "Decline"
+msgstr "Odbij"
+
+#: ../js/ui/components/telepathyClient.js:1146
+#: ../js/ui/components/telepathyClient.js:1213
+#: ../js/ui/components/telepathyClient.js:1278
+msgid "Accept"
+msgstr "Prihvati"
+
+#. translators: argument is a contact nami liki Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1165
+msgid "Video call from %s"
+msgstr "Video poziv od %s"
+
+#. translators: argument is a contact nami liki Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1168
+msgid "Call from %s"
+msgstr "Poziv od %s"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1182
+msgid "Answer"
+msgstr "Odgovor"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file nami. The string will bje something
+#. * liki: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1203
+msgid "%s is sending you %s"
+msgstr "%s vam šalje %s"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1230
+msgid "%s would like permission to see when you are online"
+msgstr "%s želi dozvolu da vidi kada ste na mreži"
+
+#: ../js/ui/components/telepathyClient.js:1321
+msgid "Network error"
+msgstr "Mrežna greška"
+
+#: ../js/ui/components/telepathyClient.js:1323
+msgid "Authentication failed"
+msgstr "Provjera vjerodostojnosti nije uspjela"
+
+#: ../js/ui/components/telepathyClient.js:1325
+msgid "Encryption error"
+msgstr "Greška pri šifrovanju"
+
+#: ../js/ui/components/telepathyClient.js:1327
+msgid "Certificate not provided"
+msgstr "Certifikat nije priložen"
+
+#: ../js/ui/components/telepathyClient.js:1329
+msgid "Certificate untrusted"
+msgstr "Nepovjerljiv certifikat"
+
+#: ../js/ui/components/telepathyClient.js:1331
+msgid "Certificate expired"
+msgstr "Certifikat je istekao"
+
+#: ../js/ui/components/telepathyClient.js:1333
+msgid "Certificate not activated"
+msgstr "Certifikat nije aktiviran"
+
+#: ../js/ui/components/telepathyClient.js:1335
+msgid "Certificate hostname mismatch"
+msgstr "Ime domaćina certifikata ne odgovara"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Certificate fingerprint mismatch"
+msgstr "Otisak certifikata ne odgovara"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Certificate self-signed"
+msgstr "Certifikat je potpisao sam sebe"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Status is set to offline"
+msgstr "Status je postavljen na odjavljen"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Encryption is not available"
+msgstr "Šifrovanje nije dostupno"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate is invalid"
+msgstr "Certifikat nije ispravan"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Connection has been refused"
+msgstr "Veza je odbijena"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Connection can't be established"
+msgstr "Veza ne može biti uspostavljena"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Connection has been lost"
+msgstr "Veza je izgubljena"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "This account is already connected to the server"
+msgstr "Ovaj nalog je već povezan sa serverom"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "Veza je zamijenjena novom vezom koristeći isti resurs"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "The account already exists on the server"
+msgstr "Ovaj nalog već postoji na serveru"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Server is currently too busy to handle the connection"
+msgstr "Server je trenutno previše zauzet da bi uspostavio vezu"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate has been revoked"
+msgstr "Certifikat je opozvan"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"Certifikat koristi nebezbjedni algoritam šifrovanja ili je kriptografski slab"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"Dužina certifikata servera, ili dubina lanca certifikata servera premašuje "
+"granice biblioteke za šifrovanje"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Internal error"
+msgstr "Greška unutar programa"
+
+#. translators: argument is the account nami, liki
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Unable to connect to %s"
+msgstr "Nemoguće se povezati na %s"
+
+#: ../js/ui/components/telepathyClient.js:1382
+msgid "View account"
+msgstr "Pogledaj nalog"
+
+#: ../js/ui/components/telepathyClient.js:1419
+msgid "Unknown reason"
+msgstr "Nepoznat razlog"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:155
+msgid "Windows"
+msgstr "Prozori"
+
+#: ../js/ui/dash.js:250 ../js/ui/dash.js:291
+msgid "Show Applications"
+msgstr "Prikaži aplikacije"
+
+#: ../js/ui/dash.js:451
+msgid "Dash"
+msgstr "Crta"
+
+#. Translators: This is the date format to usi when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:72
+msgid "%B %e %Y"
+msgstr "%B %e %Y"
+
+#. Translators: This is the accessible nami of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#. */
+#: ../js/ui/dateMenu.js:79
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#: ../js/ui/dateMenu.js:159
+msgid "Add world clocks…"
+msgstr "Dodaj svjetske satove.."
+
+#: ../js/ui/dateMenu.js:160
+msgid "World Clocks"
+msgstr "Svjetski časovnik"
+
+#: ../js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Odjavi %s"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "Odjavi se"
+
+# translations.
+#: ../js/ui/endSessionDialog.js:67
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s će biti automatski odjavljen u %d sekundi."
+msgstr[1] "%s će biti automatski odjavljen u %d sekunde."
+msgstr[2] "%s će biti automatski odjavljen u %d sekundi."
+
+# translations.
+#: ../js/ui/endSessionDialog.js:72
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Bit ćete automatski odjavljeni u %d sekundi."
+msgstr[1] "Bit ćete automatski odjavljeni u %d sekunde."
+msgstr[2] "Bit ćete automatski odjavljeni u %d sekundi."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Odjavi se"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "Ugasi"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instaliraj ažuriranja i ugasi"
+
+# translations.
+#: ../js/ui/endSessionDialog.js:87
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistem će biti automatski ugašen u %d sekundi."
+msgstr[1] "Sistem će biti automatski ugašen u %d sekunde."
+msgstr[2] "Sistem će biti automatski ugašen u %d sekundi."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instaliraj tekuća ažuriranja softvera"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "Ponovno pokretanje"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "Gašenje"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "Ponovo pokreni"
+
+# translations.
+#: ../js/ui/endSessionDialog.js:105
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistem će biti automatski ponovo pokrenut u %d sekundi."
+msgstr[1] "Sistem će biti automatski ponovo pokrenut u %d sekunde."
+msgstr[2] "Sistem će biti automatski ponovo pokrenut u %d sekundi."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Ponovo pokreni i instaliraj ažuriranja"
+
+# translations.
+#: ../js/ui/endSessionDialog.js:121
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistem će biti automatski ponovo pokrenut i instalirati ažuriranja u %d "
+"sekundi."
+msgstr[1] ""
+"Sistem će biti automatski ponovo pokrenut i instalirati ažuriranja u %d "
+"sekunde."
+msgstr[2] ""
+"Sistem će biti automatski ponovo pokrenut i instalirati ažuriranja u %d "
+"sekundi."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Ponovo pokreni &amp; instaliraj"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instaliraj &amp; ugasi"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Ugasi nakon instaliranja ažuriranja"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "Pokrenuto na baterije; molimo uključite prije instaliranja ažuriranja."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "Neke aplikacije su zauzete ili imaju nesačuvanih poslova."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "Drugi korisnici su prijavljeni."
+
+#. Translators: Remote here refers to a remote session, liki a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+msgid "%s (remote)"
+msgstr "%s (udaljen)"
+
+#. Translators: Console here refers to a tty liki a VT console */
+#: ../js/ui/endSessionDialog.js:643
+msgid "%s (console)"
+msgstr "%s (konzola)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "Instaliraj"
+
+#: ../js/ui/extensionDownloader.js:204
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Preuzeti i instalirati \"%s\" iz extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:706 ../js/ui/status/keyboard.js:576
+msgid "Keyboard"
+msgstr "Tastatura"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "Nema instaliranih nastavaka"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+msgid "%s has not emitted any errors."
+msgstr "%s nije emitovao nikakve greške."
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "Sakrij greške"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "Prikaži greške"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "Omogućeno"
+
+#. Translators: this is for a network djevice that cannot bje activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The djevice has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "Onemogućeno"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "Greška"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "Isteklo"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "Preuzimanje"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "Pogledaj Izvor"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "Web stranica"
+
+#: ../js/ui/messageTray.js:2091
+msgid "System Information"
+msgstr "Informacije o sistemu"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "Poništi"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "Pregled"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "Utipkajte za pretragu…"
+
+#: ../js/ui/panel.js:351
+msgid "Quit"
+msgstr "Izlaz"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can usi the word for "Overview". */
+#: ../js/ui/panel.js:403
+msgid "Activities"
+msgstr "Aktivnosti"
+
+#: ../js/ui/panel.js:754
+msgid "Top Bar"
+msgstr "Top bar"
+
+#: ../js/ui/popupMenu.js:288
+msgid "toggle-switch-us"
+msgstr "mijenjati-zamijeniti-nas"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "Unesi naredbu"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "Zatvori"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "Ponovno pokretanje…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:85
+msgid "%A, %B %d"
+msgstr "%A, %d. %B"
+
+#: ../js/ui/screenShield.js:144
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d nova poruka"
+msgstr[1] "%d nove poruke"
+msgstr[2] "%d novih poruka"
+
+#: ../js/ui/screenShield.js:146
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d nova obavijest"
+msgstr[1] "%d nove obavijesti"
+msgstr[2] "%d novih obavijesti"
+
+#: ../js/ui/screenShield.js:432 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "Zaključavanje"
+
+#: ../js/ui/screenShield.js:668
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME treba zaključavanje ekrana"
+
+#: ../js/ui/screenShield.js:795 ../js/ui/screenShield.js:1271
+msgid "Unable to lock"
+msgstr "U nemogućnosti zaključati"
+
+#: ../js/ui/screenShield.js:796 ../js/ui/screenShield.js:1272
+msgid "Lock was blocked by an application"
+msgstr "Zaključavanje je blokirano od strane neke aplikacije"
+
+#: ../js/ui/search.js:609
+msgid "Searching…"
+msgstr "Tražim…"
+
+#: ../js/ui/search.js:611
+msgid "No results."
+msgstr "Nema rezultata."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "Kopiranje"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "Ubaci"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "Prikaži tekst"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "Sakrij tekst"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "Loznka"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "Zapamti loziku"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "Pristupačnost"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "Uvećanje"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "Čitač ekrana"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "Ekranska Tastatura"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "Vizualna upozorenja"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "Upareni tasteri"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "Spori tasteri"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "Odskočni tasteri"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "Tipke miša"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "Veliki kontrast"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "Veliki Tekst"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1282
+#: ../js/ui/status/network.js:1393 ../js/ui/status/rfkill.js:91
+#: ../js/ui/status/rfkill.js:118
+msgid "Turn Off"
+msgstr "Isključi"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "Bluetooth postavke"
+
+#: ../js/ui/status/bluetooth.js:104
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d povezan uređaj"
+msgstr[1] "%d povezana uređaja"
+msgstr[2] "%d povezanih uređaja"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1310
+msgid "Not Connected"
+msgstr "Nije spojen"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "Osvjetljenje"
+
+#: ../js/ui/status/keyboard.js:599
+msgid "Show Keyboard Layout"
+msgstr "Prikaži raspored Tastature"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "Lokacija"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "Onemogući"
+
+#: ../js/ui/status/location.js:73
+msgid "Privacy Settings"
+msgstr "Postavke privatnosti"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "U upotrebi"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "Omogući"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<nepoznato>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1308
+#: ../js/ui/status/network.js:1512
+msgid "Off"
+msgstr "Isključen"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "Povezano"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot bje used in the mijenu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "Neupravljivo"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "Prekidam vezu"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1302
+msgid "Connecting"
+msgstr "Povezivanje"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "Autentifikacija potrebna"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "Nedostaje firmver"
+
+#. Translators: this is for a network djevice that cannot bje activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "Nedostupno"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1696
+msgid "Connection failed"
+msgstr "Veza nije uspjela"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "Wired Postavke"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "Mobilne širokopojasne postavke"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1306
+msgid "Hardware Disabled"
+msgstr "Hardware onemogućen"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "Koristi internet vezu"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "Režim U avionu uključen"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi je onemogućen kada je režim U avionu uključen."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "Isključi režim U avionu"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi je isključen"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Wi-Fi treba da bude uključen da bi se povezali na mrežu."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Uključi Wi-Fi"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi mreže"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "Odaberite mrežu"
+
+#: ../js/ui/status/network.js:883
+msgid "No Networks"
+msgstr "Nema mreže"
+
+#: ../js/ui/status/network.js:904 ../js/ui/status/rfkill.js:116
+msgid "Use hardware switch to turn off"
+msgstr "Koristi hardver prekidač za isključenje"
+
+#: ../js/ui/status/network.js:1174
+msgid "Select Network"
+msgstr "Odaberite mrežu"
+
+#: ../js/ui/status/network.js:1180
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi podešavanja"
+
+#: ../js/ui/status/network.js:1282
+msgid "Turn On"
+msgstr "Uključi"
+
+#: ../js/ui/status/network.js:1299
+msgid "Hotspot Active"
+msgstr "Hotspot Aktivan"
+
+#: ../js/ui/status/network.js:1410
+msgid "connecting..."
+msgstr "konektovanje..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1413
+msgid "authentication required"
+msgstr "potvrda vjerodostojnosti neophodna"
+
+#: ../js/ui/status/network.js:1415
+msgid "connection failed"
+msgstr "konekcija nije uspjela"
+
+#: ../js/ui/status/network.js:1481 ../js/ui/status/rfkill.js:94
+msgid "Network Settings"
+msgstr "Mrežne postavke"
+
+#: ../js/ui/status/network.js:1483
+msgid "VPN Settings"
+msgstr "VPN Postavke"
+
+#: ../js/ui/status/network.js:1502
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1657
+msgid "Network Manager"
+msgstr "Upravitelj mreže"
+
+#: ../js/ui/status/network.js:1697
+msgid "Activation of network connection failed"
+msgstr "Aktivacija mrežne veze nije uspješna"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "Postavke napajanja"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "Potpuno napunjeno"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "Procjena"
+
+#: ../js/ui/status/power.js:86
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d Preostalo (%d%%)"
+
+#: ../js/ui/status/power.js:91
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d Do potpunog (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "Baterija"
+
+#: ../js/ui/status/rfkill.js:88
+msgid "Airplane Mode"
+msgstr "Režim u avionu"
+
+#: ../js/ui/status/rfkill.js:90
+msgid "On"
+msgstr "Uključeno"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "Promijeni korisnika"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "Odjavite se"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "Orjentaciju zaključati"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "Suspenduj"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "Ugasi"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "Promjena jačine"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "Jačina"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "Prijavite se kao drugi korisnik"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "Otključaj prozor"
+
+#: ../js/ui/viewSelector.js:159
+msgid "Applications"
+msgstr "Aplikacije"
+
+#: ../js/ui/viewSelector.js:163
+msgid "Search"
+msgstr "Pretraži"
+
+#: ../js/ui/windowAttentionHandler.js:19
+msgid "“%s” is ready"
+msgstr "\"%s\" je spreman"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "Da li želite zadržati ove postavke prikaza?"
+
+#. Translators: this and the following message should bje limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "Vraćanje postavki"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "Sačuvaj promjene"
+
+#: ../js/ui/windowManager.js:107
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Promjene postavki će se vratiti u %d sekundi"
+msgstr[1] "Promjene postavki će se vratiti u %d sekunde"
+msgstr[2] "Promjene postavki će se vratiti u %d sekundi"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height. */
+#: ../js/ui/windowManager.js:599
+msgid "%d x %d"
+msgstr "%d x %d"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "Umanji"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "Poništi uvećavanje"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "Uvećaj"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "Premjesti"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "Promijeni veličinu"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "Pomjeri Titlebar Onscreen"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "Uvijek na vrhu"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "Uvijek na vidnom radnom mjestu"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "Premjesti na radni prostor gore"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "Premjesti na radni prostor dole"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Kalendar evolucije"
+
+#. translators:
+#. * The number of sound outputs on a particular djevice
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u izlaz"
+msgstr[1] "%u izlaza"
+msgstr[2] "%u izlaza"
+
+#. translators:
+#. * The number of sound inputs on a particular djevice
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ulaz"
+msgstr[1] "%u ulaza"
+msgstr[2] "%u ulaza"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "Sistemski zvuci"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "Ispiši izdanje"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "Mod koristen od strane GDM za prozor prijave."
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "Koristi specifični mod, npr. “\"gdm”\" za prozor prijave."
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "Spisak mogućih modova."
+
+#: ../src/shell-app.c:247
+msgctxt "program"
+msgid "Unknown"
+msgstr "Nepoznato"
+
+#: ../src/shell-app.c:488
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Nisam uspio pokrenuti \"%s\""
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "Lozinke se ne poklapaju."
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "Lozinka ne može biti prazna"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Autentifikacijski dijalog ugašen od strane korisnika"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the notification list"
+msgstr "Prikaži listu obavijesti"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Taster za izmjenu vidljivosti liste obavijesti"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Taster za izmjenu vidljivosti liste obavijesti"
diff --git a/po/ca.po b/po/ca.po
new file mode 100644
index 0000000..bd244bb
--- /dev/null
+++ b/po/ca.po
@@ -0,0 +1,3019 @@
+# Catalan gnome-shell translation.
+# Copyright (C) 2009 Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>
+# This file is distributed under the same license as the gnome-shell package.
+# Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>, 2009.
+# Jordi Serratosa <jordis@softcatala.cat>, 2012, 2017.
+# Gil Forcada <gilforcada@guifi.net>, 2010, 2011, 2013, 2014, 2016, 2018.
+# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2017-2022
+#
+# La terminologia que usem està documentada a la traducció del gnome-control-center
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: HEAD\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-08-15 11:56+0000\n"
+"PO-Revision-Date: 2022-09-20 19:05+0000\n"
+"Last-Translator: Jordi Mas i Hernàndez <jmas@softcatala.org>\n"
+"Language-Team: \n"
+"Language: ca\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Poedit 3.0.1\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Llançadors"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Activa l'aplicació preferida 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Activa l'aplicació preferida 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Activa l'aplicació preferida 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Activa l'aplicació preferida 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Activa l'aplicació preferida 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Activa l'aplicació preferida 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Activa l'aplicació preferida 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Activa l'aplicació preferida 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Activa l'aplicació preferida 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2070
+msgid "Screenshots"
+msgstr "Captures de pantalla"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Feu una captura de pantalla interactivament"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Feu una captura de pantalla"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Feu una captura d'una finestra"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Fes un enregistrament de pantalla interactivament"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostra la llista de notificacions"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Posa el focus en la notificació activa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostra la vista general"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostra totes les aplicacions"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Obre el menú d'aplicació"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gestor de finestres i llançador d'aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Habilita les eines internes en el diàleg de l'Alt+F2 que són útils per als "
+"desenvolupadors i provadors"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permet l'accés a les eines de depuració i de seguiment internes a través del"
+" diàleg de l'Alt+F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr ""
+"Identificadors universals únics de les extensions que s'han d'habilitar"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which"
+" should be loaded. Any extension that wants to be loaded needs to be in this"
+" list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Les extensions del GNOME Shell tenen un identificador universal. Aquesta "
+"clau conté una llista de les extensions que s'han de carregar. Qualsevol "
+"extensió que s'hagi de carregar ha de ser a la llista. Podeu modificar "
+"aquesta llista amb els mètodes de D-Bus «EnableExtension» (activa una "
+"extensió) i «DisableExtension» (desactiva una extensió) a org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr ""
+"Identificadors universals únics de les extensions que s'han d'inhabilitar"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which"
+" should be disabled, even if loaded as part of the current mode. You can "
+"also manipulate this list with the EnableExtension and DisableExtension "
+"D-Bus methods on org.gnome.Shell. This key takes precedence over the "
+"“enabled-extensions” setting."
+msgstr ""
+"Les extensions del GNOME Shell tenen un identificador universal. Aquesta "
+"clau conté una llista de les extensions que s'han de carregar. Qualsevol "
+"extensió que s'hagi de carregar ha de ser a la llista. Podeu modificar "
+"aquesta llista amb els mètodes de D-Bus «EnableExtension» (activa una "
+"extensió) i «DisableExtension» (desactiva una extensió) a "
+"org.gnome.Shell.Aquesta clau té preferència sobre el paràmetre «enabled-"
+"extensions»."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Inhabilita les extensions d'usuari"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Inhabilita totes les extensions que l'usuari ha habilitat sense afectar el "
+"paràmetre «enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Desactiva la validació de la compatibilitat de versió d'extensions"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"El GNOME Shell només carregarà extensions que afirmin ser compatibles amb la"
+" versió en execució. Si s'activa aquesta opció, es desactivarà la "
+"comprovació i es provarà de carregar totes les extensions sense tenir en "
+"compte les versions amb què afirmin ser compatibles."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Llista d'identificadors de fitxers d'escriptori de les aplicacions "
+"preferides"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the"
+" favorites area."
+msgstr ""
+"Es mostraran, a l'àrea de preferits, les aplicacions que corresponguin a "
+"aquests identificadors."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historial de les ordres utilitzades en el diàleg de l'Alt+F2"
+
+#. Translators: looking glass is a debugger and inspector tool, see
+#. https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historial del depurador del GNOME Shell"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Mostra sempre l'element de menú «Surt» al menú d'usuari."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in "
+"single-user, single-session situations."
+msgstr ""
+"Aquesta clau sobreescriu l'ocultació automàtica de l'element de menú «Surt» "
+"quan només hi ha un usuari i un sol tipus de sessió."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Si s'han de recordar les contrasenyes dels punts de muntatge xifrat o els "
+"sistemes de fitxers remots"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"El GNOME Shell us demanarà la contrasenya quan es munti un dispositiu xifrat"
+" o un sistema de fitxers remot. Si es pot desar la contrasenya per a "
+"utilitzar-la en el futur, es mostrarà la casella de selecció «Recorda la "
+"contrasenya». Aquesta clau estableix el valor per defecte d'aquesta casella "
+"de selecció."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Si l'adaptador de Bluetooth predeterminat té dispositius configurats "
+"associats"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"El Shell només mostrà una entrada de menú Bluetooth si un adaptador "
+"Bluetooth està engegat, o si hi ha dispositius configurats associats a "
+"l'adaptador predeterminat. Això es reiniciarà si l'adaptador predeterminat "
+"té dispositius associats."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "El darrer perfil d'energia no predeterminat seleccionat"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support"
+" toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Alguns sistemes suporten més de dos perfils d'energia. Per tal de continuar "
+"admetent el canvi entre dos perfils, aquesta clau registra l'últim perfil no"
+" predeterminat seleccionat."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"S'ha mostrat l'última versió per al diàleg «Us donem la benvinguda al GNOME»"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last"
+" shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Aquesta clau determina per a quina versió es va mostrar per última vegada "
+"del diàleg «Us donem la benvinguda al GNOME». Una cadena buida representa la"
+" versió més antiga possible, i un nombre enorme representarà versions que "
+"encara no existeixen. Aquest nombre enorme es pot utilitzar per a desactivar"
+" el diàleg."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Disposició del selector d'aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Disposició del selector d'aplicacions. Cada entrada de la matriu és una "
+"pàgina. Les pàgines s'emmagatzemen en l'ordre en què apareixen al GNOME "
+"Shell. Cada pàgina conté un «id de l'aplicació» → parell de «dades». "
+"Actualment, els valors següents s'emmagatzemen com a «dades»: • «posició» la"
+" posició de la icona de l'aplicació a la pàgina"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Vinculació per a obrir el menú d'aplicació"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "La vinculació per a obrir el menú d'aplicació."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Vinculació de tecla per a desplaçar entre els estats de resum"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Vinculació de tecla per a desplaçar entre sessió, selector de finestres i "
+"graella d'aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Vinculació de tecla per a desplaçar entre la graella d'aplicació, el "
+"selector de finestres i la sessió"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Vinculació de tecles per a obrir la vista «Mostra les aplicacions»"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Vinculació de tecles per a obrir la vista «Mostra les aplicacions» de les "
+"activitats de la vista general."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Vinculació per a obrir la vista general"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Vinculació per a obrir la vista general d'activitats."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"La vinculació per a commutar la visibilitat de la llista de notificacions"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"La vinculació per a commutar la visibilitat de la llista de notificacions."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Vinculació per a posar el focus a la notificació activa"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Vinculació per a posar el focus a la notificació activa."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Commuta a l'aplicació 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Commuta a l'aplicació 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Commuta a l'aplicació 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Commuta a l'aplicació 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Commuta a l'aplicació 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Commuta a l'aplicació 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Commuta a l'aplicació 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Commuta a l'aplicació 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Commuta a l'aplicació 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Limita el canviador a l'espai de treball actual."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Si és «true» (cert), només les aplicacions que tinguin finestres en l'espai "
+"de treball actual es mostren en el canviador. En cas contrari es mostren "
+"totes les aplicacions."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "El mode d'icona de les aplicacions."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-only” "
+"(shows only the application icon) or “both”."
+msgstr ""
+"Configureu com es mostren les finestres en l'intercanviador. Els valors "
+"possibles són: «thumbnail-only» (mostra una miniatura de la finestra), «app-"
+"icon-only» (mostra la icona de l'aplicació) o «both» (ambdues coses)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Si és «true» (cert), només les finestres de l'espai de treball actual es "
+"mostren en el canviador. En cas contrari, es mostren totes les finestres."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Ubicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Les ubicacions a mostrar en els rellotges del món"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Ubicació automàtica"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Si s'ha d'obtenir la ubicació actual o no"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Ubicació"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "La ubicació a mostrar la predicció del temps"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Adjunta el diàleg modal a la finestra pare"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Si s'executa el GNOME Shell, aquesta clau sobreescriu la clau "
+"«org.gnome.mutter»."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Habilita el mosaic a les vores en deixar anar les finestres a les vores de "
+"la pantalla"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Els espais de treball es gestionen dinàmicament"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Només en el monitor principal hi ha espais de treball"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Retarda el canvi del focus, quan s'està en mode ratolí, fins que el punter "
+"no estigui quiet"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Inici de sessió de xarxa"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Alguna cosa ha anat malament"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Hi ha un problema: no es poden mostrar els paràmetres per a aquesta "
+"extensió. Us recomanem que informeu del problema als autors de l'extensió."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detalls tècnics"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Pàgina d'inici"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visiteu la pàgina d'inici de l'extensió"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Cancel·la"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Contrasenya"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Trieu la sessió"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "No esteu llistat?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(p. ex. l'usuari o %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nom d'usuari"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Finestra d'entrada"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Error d'autenticació"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(o llisca el dit a través del lector)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader
+#. instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(o posa el dit al lector)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Apaga"
+
+#. Translators: A list of keywords that match the power-off action, separated
+#. by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "apaga;atura;reinicia"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Reinicia"
+
+#. Translators: A list of keywords that match the restart action, separated by
+#. semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reinicia;reinici;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bloqueja la pantalla"
+
+#. Translators: A list of keywords that match the lock screen action,
+#. separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "bloqueja;pantalla;bloca"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Tanca la sessió"
+
+#. Translators: A list of keywords that match the logout action, separated by
+#. semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "desconnecta;sortida;surt"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspèn"
+
+#. Translators: A list of keywords that match the suspend action, separated by
+#. semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspèn;dorm"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Canvia d'usuari"
+
+#. Translators: A list of keywords that match the switch user action,
+#. separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "canvia d'usuari"
+
+#. Translators: A list of keywords that match the lock orientation action,
+#. separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "bloqueja l'orientació;desbloqueja l'orientació;pantalla;rotació"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Fes una captura de pantalla"
+
+#. Translators: A list of keywords that match the screenshot UI action,
+#. separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "captura de pantalla;enregistrament de pantalla;tall;captura;registre"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Desbloqueja la rotació de la pantalla"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Bloqueja la rotació de la pantalla"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "No s'ha trobat l'ordre"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "No s'ha pogut analitzar l'ordre:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "No s'ha pogut executar «%s»:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Ara mateix"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "fa %d minut"
+msgstr[1] "fa %d minuts"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "fa %d hora"
+msgstr[1] "fa %d hores"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Ahir"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "fa %d dia"
+msgstr[1] "fa %d dies"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "fa %d setmana"
+msgstr[1] "fa %d setmanes"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "fa %d mes"
+msgstr[1] "fa %d mesos"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "fa %d any"
+msgstr[1] "fa %d anys"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%-H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ahir, a les %-H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A a les %-H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, a les %-H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B de %Y, a les %-H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ahir, a les %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, a les %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, a les %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B de %Y, a les %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Inici de sessió al punt d'accés Wi-Fi"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"La connexió a aquest punt d'accés no és segura. Contrasenyes o altra "
+"informació que introduïu en aquesta pàgina pot ser vista per persones "
+"properes."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Denega l'accés"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Permet l'accés"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Carpeta sense nom"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s s'ha ancorat al tauler."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s s'ha desancorat del tauler."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Obre finestres"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Finestra nova"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Mostra els detalls"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Surt"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Desancora"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Ancora-ho al tauler"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Inicia usant una targeta gràfica integrada"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Inicia usant una targeta gràfica externa"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Seleccioneu dispositiu d'àudio"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Paràmetres del so"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Auriculars"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Auriculars amb micròfon"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Micròfon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Canvia el fons de l'escriptori…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Paràmetres de la pantalla"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Paràmetres"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0"
+#. (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Dg"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Dl"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Dt"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Dc"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Dj"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Dv"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Ds"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#: js/ui/calendar.js:402
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#: js/ui/calendar.js:412
+msgid "%OB %Y"
+msgstr "%OB de %Y"
+
+#: js/ui/calendar.js:473
+msgid "Previous month"
+msgstr "Mes anterior"
+
+#: js/ui/calendar.js:491
+msgid "Next month"
+msgstr "Mes següent"
+
+#: js/ui/calendar.js:642
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%-d"
+
+#: js/ui/calendar.js:701
+msgid "Week %V"
+msgstr "Setmana %-V"
+
+#: js/ui/calendar.js:880
+msgid "No Notifications"
+msgstr "Cap notificació"
+
+#: js/ui/calendar.js:937
+msgid "Do Not Disturb"
+msgstr "No molesteu"
+
+#: js/ui/calendar.js:958
+msgid "Clear"
+msgstr "Neteja-ho"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» no està responent."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Podeu esperar un moment perquè continuï o podeu forçar-ne la sortida "
+"completa."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Força la sortida"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Espera"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "S'ha connectat un dispositiu extern"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "S'ha desconnectat un dispositiu extern"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "No es pot desbloquejar el volum"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+"La versió instal·lada del udisks no és compatible amb la configuració PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Obre amb %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"També us podeu connectar prement el botó «WPS» del vostre encaminador."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Connecta"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Clau"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Contrasenya de la clau privada"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitat"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Servei"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Cal autenticació"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Per a accedir a la xarxa sense fil «%s» calen les contrasenyes o les claus "
+"de xifratge."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autenticació 802.1X amb fil"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nom de la xarxa"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autenticació DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Cal que introduïu el codi PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Cal que introduïu el codi PIN del dispositiu de banda ampla mòbil"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Cal introduir una contrasenya per a connectar-vos a «%s»."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Gestor de connexions de xarxa"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Contrasenya VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Cal autenticació"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrador"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autentica"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "No ha funcionat. Torneu-ho a provar."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "En/na %s ara es diu %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Finestres"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Mostra les aplicacions"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Quadre d'aplicacions"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B de %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %-e %B de %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current
+#. year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on
+#. different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B de %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Avui"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Demà"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Tot el dia"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Cap cita"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Afegeix rellotges del món…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Rellotges del món"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "S'està carregant…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Ves en línia per a informació sobre el temps"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "La informació sobre el temps no està disponible"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "El temps"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Trieu una ubicació pel temps…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Tanca la sessió %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Tanca la sessió"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s tancarà la sessió automàticament d'aquí %d segon."
+msgstr[1] "%s tancarà la sessió automàticament d'aquí %d segons."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Es tancarà la sessió automàticament d'aquí %d segon."
+msgstr[1] "Es tancarà la sessió automàticament d'aquí %d segons."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Tanca la sessió"
+
+#: js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Power Off"
+msgstr "Apagada"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instal·la les actualitzacions i apaga"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "S'apagarà l'ordinador automàticament d'aquí %d segon."
+msgstr[1] "S'apagarà l'ordinador automàticament d'aquí %d segons."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instal·la les actualitzacions pendents"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Apaga"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Reinici"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instal·la les actualitzacions i reinicia"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Es reiniciarà l'ordinador automàticament d'aquí %d segon."
+msgstr[1] "Es reiniciarà l'ordinador automàticament d'aquí %d segons."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Reinicia"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reinicia i instal·la les actualitzacions"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid ""
+"The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Es reiniciarà l'ordinador automàticament i s'instal·laran les "
+"actualitzacions d'aquí %d segon."
+msgstr[1] ""
+"Es reiniciarà l'ordinador automàticament i s'instal·laran les "
+"actualitzacions d'aquí %d segons."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reinicia i instal·la"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instal·la i apaga"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Apaga després d'instal·lar les actualitzacions"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reinicia i instal·la l'actualització"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s s'instal·larà després del reinici. Una actualització de la "
+"instal·lació pot trigar força temps. Assegureu-vos que heu fet còpia de "
+"seguretat i que l'ordinador està connectat al corrent."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Hi ha poca bateria. Connecteu l'ordinador a la xarxa elèctrica abans "
+"d'instal·lar les actualitzacions."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+"Hi ha algunes aplicacions que estan ocupades o que tenen documents sense "
+"desar"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Altres usuaris tenen la sessió oberta"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opcions d'arrencada"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remot)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instal·la"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instal·la l'extensió"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Voleu baixar i instal·lar «%s» d'extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "No hi ha cap actualització d'extensions disponible"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr ""
+"Les actualitzacions de les extensions estan preparades per a ser "
+"instal·lades."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Permet inhabilitar les dreceres"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "L'aplicació %s vol inhabilitar les dreceres"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Una aplicació vol inhabilitar les dreceres"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Podeu restaurar les dreceres si premeu %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Denega"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Permet"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tecles lentes actives"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tecles lentes inactives"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the"
+" Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Heu mantingut premuda la tecla de majúscules durant 8 segons. Aquesta és la "
+"drecera per a la funcionalitat «tecles lentes», que afecta la forma de "
+"funcionar del teclat."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Tecles enganxoses actives"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Tecles enganxoses inactives"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Heu premut la tecla de majúscules 5 cops seguides. Aquesta és la drecera per"
+" la funcionalitat de les tecles enganxoses, que afecta la manera en què "
+"funciona el teclat."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Heu premut dues tecles juntes, o la tecla de majúscules 5 cops seguides. "
+"Això desactiva la funcionalitat de les tecles enganxoses, que afecta la "
+"manera en què funciona el teclat."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Deixa-ho actiu"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Activa"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Desactiva"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Deixa-ho desactivat"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Configuració de regió i idioma"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "No hi ha cap extensió instal·lada"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s no ha emès cap error."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Amaga els errors"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Mostra els errors"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Habilitat"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Inhabilitat"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Error"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Obsolet"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "S'està baixant"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Mostra el codi font"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Pàgina web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "El sistema s'ha posat en mode insegur"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Ara les aplicacions tenen accés sense restriccions"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Desfés"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Sessió iniciada com a usuari privilegiat"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Cal evitar iniciar sessions com a usuari privilegiat per raons de seguretat."
+" Si és possible, entreu com a un usuari normal."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "La pantalla de bloqueig està inhabilitada"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "El bloqueig de pantalla requereix el gestor de pantalla del GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informació de l'ordinador"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artista desconegut"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Títol desconegut"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Teclegeu per a cercar"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Aplicacions"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Vista general"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Drecera de teclat nova…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Definit per l'aplicació"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Mostra l'ajuda en pantalla"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Commuta el monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Assigna la pulsació de tecla"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Fet"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Edita…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Cap"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Premeu un botó per a configurar"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Premeu Esc per a sortir"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Premeu qualsevol tecla per a sortir"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Activitats"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Executa una ordre"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Premeu Esc per a tancar"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "El reinici no està disponible en Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "S'està reiniciant…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "El GNOME necessita bloquejar la pantalla"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "No es pot bloquejar"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Una aplicació està bloquejant el bloqueig"
+
+#: js/ui/screenshot.js:1149
+msgid "Selection"
+msgstr "Selecció"
+
+#: js/ui/screenshot.js:1159
+msgid "Area Selection"
+msgstr "Selecció d'àrea"
+
+#: js/ui/screenshot.js:1164
+msgid "Screen"
+msgstr "Pantalla"
+
+#: js/ui/screenshot.js:1174
+msgid "Screen Selection"
+msgstr "Selecció de pantalla"
+
+#: js/ui/screenshot.js:1179
+msgid "Window"
+msgstr "Finestra"
+
+#: js/ui/screenshot.js:1189
+msgid "Window Selection"
+msgstr "Selecció de finestra"
+
+#: js/ui/screenshot.js:1227
+msgid "Screenshot / Screencast"
+msgstr "Captura o enregistrament de pantalla"
+
+#: js/ui/screenshot.js:1263
+msgid "Show Pointer"
+msgstr "Mostra el punter"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1840
+msgid "Screencasts"
+msgstr "Enregistraments de pantalla"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1845
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Enregistrament de pantalla des de %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1911 js/ui/screenshot.js:2123
+msgid "Screenshot"
+msgstr "Captura de pantalla"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1917
+msgid "Screencast recorded"
+msgstr "Enregistrament de pantalla completat"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1919
+msgid "Click here to view the video."
+msgstr "Cliqueu aquí per a veure el vídeo."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1922 js/ui/screenshot.js:2137
+msgid "Show in Files"
+msgstr "Mostra-ho al Fitxers"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2083
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Captura de pantalla de %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2129
+msgid "Screenshot captured"
+msgstr "Captura de pantalla realitzada"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2131
+msgid "You can paste the image from the clipboard."
+msgstr "Podeu enganxar la imatge des del porta-retalls."
+
+#: js/ui/screenshot.js:2184 js/ui/screenshot.js:2349
+msgid "Screenshot taken"
+msgstr "S'ha fet una captura de pantalla"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "S'està cercant…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "No s'ha trobat cap resultat."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d més"
+msgstr[1] "%d més"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Cerca"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copia"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Enganxa"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Mostra el text"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Oculta el text"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Bloq Maj està activat."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volum ocult"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volum del sistema del Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Usa fitxers de claus"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Per a desbloquejar un volum que usa fitxers de claus, useu l'eina <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Número PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Recorda la contrasenya"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desbloqueja"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Obre %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "El PIM ha de ser un nombre o estar buit."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "No es pot iniciar %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "No s'ha pogut trobar l'aplicació %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accessibilitat"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Alt contrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Amplia"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Lector de pantalla"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Teclat en pantalla"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Avisos visuals"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Tecles enganxoses"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tecles lentes"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Tecles de salt"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Tecles del ratolí"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Text gran"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Gira automàticament"
+
+#: js/ui/status/bluetooth.js:160
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Brillantor"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Mode fosc"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Un sol clic"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Doble clic"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Arrossega"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clic secundari"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clic en passar per sobre"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Teclat"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Mostra la disposició del teclat"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Permet accés a la ubicació"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "L'aplicació %s vol accedir a la vostra ubicació"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Podeu canviar la configuració de l'accés a la ubicació sempre que vulgueu "
+"des de la configuració de la privacitat."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<desconegut>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Desconnecta %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Connecta a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Punt d'accés %s"
+
+#: ../js/ui/status/network.js:1467 ../js/ui/status/network.js:1483
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1468
+msgid "VPN Settings"
+msgstr "Paràmetres de la VPN"
+
+#: ../js/ui/status/network.js:1710
+msgid "Wi–Fi"
+msgstr "Wi-Fi"
+
+#: ../js/ui/status/network.js:1712
+msgid "All Networks"
+msgstr "Totes les xarxes"
+
+#: ../js/ui/status/network.js:1809
+msgid "Wired Connections"
+msgstr "Connexions per cable"
+
+#: ../js/ui/status/network.js:1810
+msgid "Wired Settings"
+msgstr "Paràmetres del cable"
+
+#: ../js/ui/status/network.js:1824
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth Tethers"
+
+#: ../js/ui/status/network.js:1825
+msgid "Bluetooth Settings"
+msgstr "Paràmetres del Bluetooth"
+
+#: ../js/ui/status/network.js:1839
+msgid "Mobile Connections"
+msgstr "Connexions mòbils"
+
+#: ../js/ui/status/network.js:1841
+msgid "Mobile Broadband Settings"
+msgstr "Paràmetres de la banda ampla mòbil"
+
+#: ../js/ui/status/network.js:1953
+msgid "Connection failed"
+msgstr "Ha fallat la connexió"
+
+#: ../js/ui/status/network.js:1954
+msgid "Activation of network connection failed"
+msgstr "Ha fallat l'activació de la connexió de xarxa"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Llum nocturna"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Rendiment"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Equilibrat"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Estalvi d'energia"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Perfils d'energia"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Atura l'enregistrament de pantalla"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Atura la compartició de pantalla"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Mode d'avió"
+
+#: js/ui/status/system.js:89
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/system.js:140
+msgid "Power Off Menu"
+msgstr "Menú d'apagada"
+
+#: js/ui/status/system.js:148
+msgid "Suspend"
+msgstr "Suspèn"
+
+#: js/ui/status/system.js:153
+msgid "Restart…"
+msgstr "Reinicia…"
+
+#: js/ui/status/system.js:158
+msgid "Power Off…"
+msgstr "Apaga…"
+
+#: js/ui/status/system.js:165
+msgid "Log Out"
+msgstr "Tanca la sessió"
+
+#: js/ui/status/system.js:170
+msgid "Switch User…"
+msgstr "Canvia d'usuari…"
+
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Bloqueja la pantalla"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Dispositiu Thunderbolt desconegut"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Un nou dispositiu s'ha detectat mentre estàveu absents. Desconnecteu i "
+"torneu a connectar el dispositiu per a començar a utilitzar-lo."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispositiu Thunderbolt no autoritzat"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"S'ha detectat un dispositiu nou i cal que sigui autoritzat per "
+"l'administrador."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "S'ha produït un error d'autorització a Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "No s'ha pogut autoritzar el dispositiu Thunderbolt: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "S'ha canviat el volum"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Volum"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Sortida del so"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Entrada del so"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Mirall"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Uneix pantalles"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Només extern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Només l'integrat"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Llisqueu amunt per a desbloquejar"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Feu clic o premeu una tecla per a desbloquejar"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Desbloqueja la finestra"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Entra amb un altre usuari"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Us donem la benvinguda al GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Si voleu aprendre com funciona, feu una visita guiada."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "No, gràcies"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Visita guiada"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» ja està a punt"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Mantenir aquesta configuració de la pantalla?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Descarta els canvis"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Mantén els canvis"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Es descartaran els canvis d'aquí %d segon"
+msgstr[1] "Es descartaran els canvis d'aquí %d segons"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Fes una captura de pantalla"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Oculta"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restaura"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximitza"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Mou"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Canvia la mida"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Mou la barra de títol a la pantalla"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Sempre per damunt"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Sempre a l'espai de treball visible"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Mou a l'espai de treball esquerre"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Mou a l'espai de treball dret"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Mou a l'espai de treball superior"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Mou a l'espai de treball inferior"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Mou a la pantalla de dalt"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Mou a la pantalla de baix"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Mou a la pantalla de l'esquerra"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Mou a la pantalla de la dreta"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Tanca"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendari de l'Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Mostra la versió"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "El mode que utilitzarà el GDM per a la pantalla d'entrada"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Utilitza un mode específic, p. ex. «gdm» per la pantalla d'entrada"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Llista els modes possibles"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Desconegut"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "No s'ha pogut iniciar «%s»"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Les contrasenyes no coincideixen."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "La contrasenya no pot ser buida"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "L'usuari ha descartat el diàleg d'autenticació"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensions"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Gestioneu les extensions del GNOME Shell"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "El projecte GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Les extensions del GNOME gestiona les actualitzacions de les extensions, "
+"configurant les preferències de l'extensió i suprimint o desactivant les "
+"extensions no desitjades."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configureu les extensions del GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Sense coincidències"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Voleu suprimir «%s»?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Si suprimiu una extensió, us cal tornar-la a baixar si voleu habilitar-la de"
+" nou"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Suprimeix"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>\n"
+"Jordi Serratosa <jordis@softcatala.cat>\n"
+"Gil Forcada <gilforcada@guifi.net>\n"
+"Jordi Mas i Hernàndez <jmas@softcatala.org>\n"
+"Miquel-Àngel Burgos i Fradeja <miquel.angel.burgos@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d extensió serà actualitzada la següent vegada que entreu."
+msgstr[1] "%d extensions seran actualitzades la següent vegada que entreu."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "L'extensió és incompatible amb la versió actual del GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "L'extensió ha tingut un error"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Aquesta extensió pot ser actualitzada"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Lloc web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Suprimeix…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Ajuda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Quant a les extensions"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if"
+" you encounter problems with your system."
+msgstr ""
+"Les extensions poden causar problemes de rendiment i estabilitat. "
+"Inhabiliteu les extensions si teniu problemes amb el vostre sistema."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Instal·lada manualment"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a "
+"href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
+msgstr ""
+"Per a visitar i afegir extensions, visiteu <a "
+"href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Integrada"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "No hi ha cap extensió instal·lada"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"No s'ha pogut obtenir la llista d'extensions instal·lades. Assegureu-vos que"
+" heu entrat al GNOME i torneu a provar-ho."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Les actualitzacions d'extensions preparades"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Tanca la sessió…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "L'extensió nova s'ha creat amb èxit a %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"El nom ha de ser una cadena molt curta (idealment descriptiva).\n"
+"Per exemple: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nom"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"La descripció és una explicació d'una frase de què fa l'extensió.\n"
+"Per exemple: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descripció"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe.example.com)\n"
+msgstr ""
+"L'UUID és un identificador únic global per l'extensió\n"
+"Ha de tenir el format d'una adreça de correu (exemple@softcatala.org)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Trieu una de les plantilles disponibles:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Plantilla"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "L'identificador únic de l'extensió nova"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOM"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "El nom visible a l'usuari de l'extensió nova"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPCIÓ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Una descripció curta del que fa l'extensió"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "PLANTILLA"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "La plantilla a usar per a l'extensió nova"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Introduïu la informació de l'extensió interactivament"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Crea una extensió nova"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Arguments desconeguts"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Calen l'UUID, el nom i la descripció"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Ha fallat la connexió amb el GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "L'extensió «%s» no existeix\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Inhabilita una extensió"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "No s'ha donat un UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "S'han donat més d'un UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Habilita una extensió"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "L'extensió «%s» no existeix\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostra la informació de les extensions"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Sobreescriu una extensió existent"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instal·la un paquet d'extensió"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "No s'ha especificat cap paquet d'extensió"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "S'ha especificat més un paquet d'extensió"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Mostra les extensions d'usuari instal·lades"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostra les extensions del sistema instal·lades"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostra les extensions habilitades"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostra les extensions inhabilitades"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostra les extensions amb preferències"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostra les extensions amb actualitzacions"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Mostra els detalls de l'extensió"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Llista les extensions instal·lades"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FITXER"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Font a addicional a incloure al paquet"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ESQUEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Un esquema del GSettings a incloure"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORI"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "El directori on es troben les traduccions"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMINI"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "El domini del gettext per a les traduccions"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Sobreescriu el paquet existent"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Directori on s'ha de crear el paquet"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIRECTORI_FONT"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Crea un paquet d'extensió"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "S'han especificat més d'un directori font"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "L'extensió «%s» no té preferències\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "No s'han pogut obrir les preferències per a l'extensió «%s»: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Obre les preferències de l'extensió"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Reinicia una extensió"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "No es poden desinstal·lar les extensions del sistema\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "No s'ha pogut desinstal·lar «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Desinstal·la una extensió"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "No imprimeixis els missatges d'error"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Camí"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autor original"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versió"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Estat"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» no té arguments"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Ús:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Mostra la informació de la versió i surt."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "ORDRE"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Ordres:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Mostra l'ajuda"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Habilita l'extensió"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Inhabilita l'extensió"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Reinicia l'extensió"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Desinstal·la l'extensió"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Llista les extensions"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Mostra la informació de l'extensió"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Obre les preferències de l'extensió"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Crea extensió"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Paquet d'extensió"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instal·la un paquet d'extensió"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Feu servir «%s» per a obtenir ajuda detallada.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Pla"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Una extensió buida"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicador"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Afegeix una icona a la barra superior"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u sortida"
+msgstr[1] "%u sortides"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entrades"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sons del sistema"
diff --git a/po/ca@valencia.po b/po/ca@valencia.po
new file mode 100644
index 0000000..20e7dee
--- /dev/null
+++ b/po/ca@valencia.po
@@ -0,0 +1,2395 @@
+# Catalan gnome-shell translation.
+# Copyright (C) 2009 Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>
+# This file is distributed under the same license as the gnome-shell package.
+# Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>, 2009.
+# Jordi Serratosa <jordis@softcatala.cat>, 2012, 2017.
+# Gil Forcada <gilforcada@guifi.net>, 2010, 2011, 2013, 2014, 2016.
+# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2017
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: HEAD\n"
+"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2017-09-10 06:17+0000\n"
+"PO-Revision-Date: 2017-08-23 09:48+0200\n"
+"Last-Translator: Xavi Ivars <xavi.ivars@gmail.com>\n"
+"Language-Team: Catalan <tradgnome@softcatala.org>\n"
+"Language: ca-valencia\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Poedit 2.0.1\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostra la llista de notificacions"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Posa el focus en la notificació activa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostra la vista general"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostra totes les aplicacions"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Obri el menú d'aplicació"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:152
+msgid "Shell Extensions"
+msgstr "Extensions del Shell"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configureu les extensions del GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gestor de finestres i llançador d'aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Habilita les eines internes en el diàleg de l'Alt+F2 que són útils per als "
+"desenvolupadors i provadors"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permet l'accés a les eines de depuració i de seguiment internes a través del "
+"diàleg de l'Alt+F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr ""
+"Identificadors universals únics de les extensions que s'han d'habilitar"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Les extensions del GNOME Shell tenen un identificador universal. Esta "
+"clau conté una llista de les extensions que s'han de carregar. Qualsevol "
+"extensió que s'haja de carregar ha de ser a la llista. Podeu modificar "
+"esta llista amb els mètodes de D-Bus «EnableExtension» (activa una "
+"extensió) i «DisableExtension» (desactiva una extensió) a org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disable user extensions"
+msgstr "Inhabilita les extensions d'usuari"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Inhabilita totes les extensions que l'usuari ha habilitat sense afectar el "
+"paràmetre «enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:34
+msgid "Disables the validation of extension version compatibility"
+msgstr "Desactiva la validació de la compatibilitat de versió d'extensions"
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"El GNOME Shell només carregarà extensions que afirmin ser compatibles amb la "
+"versió en execució. Si s'activa esta opció, es desactivarà la comprovació "
+"i es provarà de carregar totes les extensions sense tindre en compte les "
+"versions amb què afirmin ser compatibles."
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Llista d'identificadors de fitxers d'escriptori de les aplicacions preferides"
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Es mostraran, a l'àrea de preferits, les aplicacions que corresponguin a "
+"estos identificadors."
+
+#: data/org.gnome.shell.gschema.xml.in:51
+msgid "App Picker View"
+msgstr "Vista del seleccionador d'aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:52
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+"Índex de la vista seleccionada actualment en el seleccionador d'aplicacions."
+
+#: data/org.gnome.shell.gschema.xml.in:58
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historial de les ordes utilitzades en el diàleg de l'Alt+F2"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "History for the looking glass dialog"
+msgstr "Historial del depurador del GNOME Shell"
+
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Mostra sempre l'element de menú «Ix» al menú d'usuari."
+
+#: data/org.gnome.shell.gschema.xml.in:68
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Esta clau sobreescriu l'ocultació automàtica de l'element de menú «Ix» "
+"quan només hi ha un usuari i un sol tipus de sessió."
+
+#: data/org.gnome.shell.gschema.xml.in:75
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Si s'han de recordar les contrasenyes dels punts de muntatge encriptats o "
+"els sistemes de fitxers remots"
+
+#: data/org.gnome.shell.gschema.xml.in:76
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"El GNOME Shell vos demanarà la contrasenya quan es munti un dispositiu "
+"encriptat o un sistema de fitxers remot. Si es pot guardar la contrasenya per "
+"utilitzar-lo en el futur, es mostrarà la casella de verificació «Recorda la "
+"contrasenya». Esta clau estableix el valor per defecte d'esta casella "
+"de verificació."
+
+#: data/org.gnome.shell.gschema.xml.in:85
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Si l'adaptador de Bluetooth predeterminat té dispositius configurats "
+"associats"
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"El Shell només mostrà una entrada de menú Bluetooth si un adaptador "
+"Bluetooth està engegat, o si hi ha dispositius configurats associats a "
+"l'adaptador predeterminat. Això es reiniciarà si l'adaptador predeterminat "
+"té dispositius associats."
+
+#: data/org.gnome.shell.gschema.xml.in:101
+msgid "Keybinding to open the application menu"
+msgstr "Vinculació per obrir el menú d'aplicació"
+
+#: data/org.gnome.shell.gschema.xml.in:102
+msgid "Keybinding to open the application menu."
+msgstr "La vinculació per obrir el menú d'aplicació."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Vinculació de tecles per obrir la vista «Mostra les aplicacions»"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Vinculació de tecles per obrir la vista «Mostra les aplicacions» de les "
+"activitats de la vista general."
+
+#: data/org.gnome.shell.gschema.xml.in:116
+msgid "Keybinding to open the overview"
+msgstr "Vinculació per obrir la vista general"
+
+#: data/org.gnome.shell.gschema.xml.in:117
+msgid "Keybinding to open the Activities Overview."
+msgstr "Vinculació per obrir la vista general d'activitats."
+
+#: data/org.gnome.shell.gschema.xml.in:123
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"La vinculació per commutar la visibilitat de la llista de notificacions"
+
+#: data/org.gnome.shell.gschema.xml.in:124
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"La vinculació per commutar la visibilitat de la llista de notificacions."
+
+#: data/org.gnome.shell.gschema.xml.in:130
+msgid "Keybinding to focus the active notification"
+msgstr "Vinculació per posar el focus a la notificació activa"
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to focus the active notification."
+msgstr "Vinculació per posar el focus a la notificació activa."
+
+#: data/org.gnome.shell.gschema.xml.in:137
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"Vinculació que fa una pausa i continua tots els «tweens» en execució, per "
+"motius de depuració"
+
+#: data/org.gnome.shell.gschema.xml.in:146
+msgid "Which keyboard to use"
+msgstr "Quin tipus de teclat s'ha d'utilitzar"
+
+#: data/org.gnome.shell.gschema.xml.in:147
+msgid "The type of keyboard to use."
+msgstr "El tipus de teclat que s'utilitzarà."
+
+#: data/org.gnome.shell.gschema.xml.in:158
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Limit switcher to current workspace."
+msgstr "Limita el canviador a l'espai de treball actual."
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Si és «true» (cert), només les aplicacions que tinguen finestres en l'espai "
+"de treball actual es mostren en el canviador. En cas contrari es mostren "
+"totes les aplicacions."
+
+#: data/org.gnome.shell.gschema.xml.in:176
+msgid "The application icon mode."
+msgstr "El mode d'icona de les aplicacions."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configureu com es mostren les finestres en l'intercanviador. Els valors "
+"possibles són: «thumbnail-only» (mostra una miniatura de la finestra), «app-"
+"icon-only» (mostra la icona de l'aplicació) o «both» (ambdues coses)."
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Si és «true» (cert), només les finestres de l'espai de treball actual es "
+"mostren en el canviador. En cas contrari, es mostren totes les finestres."
+
+#: data/org.gnome.shell.gschema.xml.in:197
+msgid "Attach modal dialog to the parent window"
+msgstr "Adjunta el diàleg modal a la finestra pare"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+#: data/org.gnome.shell.gschema.xml.in:207
+#: data/org.gnome.shell.gschema.xml.in:215
+#: data/org.gnome.shell.gschema.xml.in:223
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Si s'executa el GNOME Shell, esta clau sobreescriu la clau «org.gnome."
+"mutter»."
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Habilita la tessel·lització a les vores en deixar anar les finestres a les "
+"vores de la pantalla"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Workspaces are managed dynamically"
+msgstr "Els espais de treball es gestionen dinàmicament"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Workspaces only on primary monitor"
+msgstr "Només en el monitor principal hi ha espais de treball"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Retarda el canvi del focus, quan s'està en mode ratolí, fins que el punter "
+"no estiga quiet"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Inici de sessió de xarxa"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:120
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "S'ha produït un error en carregar el diàleg de preferències de %s:"
+
+#: js/gdm/authPrompt.js:149 js/ui/audioDeviceSelection.js:71
+#: js/ui/components/networkAgent.js:117 js/ui/components/polkitAgent.js:148
+#: js/ui/endSessionDialog.js:482 js/ui/extensionDownloader.js:197
+#: js/ui/shellMountOperation.js:344 js/ui/status/network.js:936
+msgid "Cancel"
+msgstr "Cancel·la"
+
+#: js/gdm/authPrompt.js:171 js/gdm/authPrompt.js:218 js/gdm/authPrompt.js:450
+msgid "Next"
+msgstr "Següent"
+
+#: js/gdm/authPrompt.js:214 js/ui/shellMountOperation.js:348
+#: js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "Desbloqueja"
+
+#: js/gdm/authPrompt.js:216
+msgctxt "button"
+msgid "Sign In"
+msgstr "Entra"
+
+#: js/gdm/loginDialog.js:308
+msgid "Choose Session"
+msgstr "Trieu la sessió"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:458
+msgid "Not listed?"
+msgstr "No esteu llistat?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:888
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(p. ex. l'usuari o %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:893 js/ui/components/networkAgent.js:243
+#: js/ui/components/networkAgent.js:261
+msgid "Username: "
+msgstr "Nom d'usuari:"
+
+#: js/gdm/loginDialog.js:1236
+msgid "Login Window"
+msgstr "Finestra d'entrada"
+
+#: js/gdm/util.js:346
+msgid "Authentication error"
+msgstr "Error d'autenticació"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:478
+msgid "(or swipe finger)"
+msgstr "(o passeu el dit)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:99
+msgctxt "search-result"
+msgid "Power off"
+msgstr "Apaga"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:102
+msgid "power off;shutdown"
+msgstr "apaga;atura"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Lock screen"
+msgstr "Bloqueja la pantalla"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "lock screen"
+msgstr "bloca la pantalla"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:113
+msgctxt "search-result"
+msgid "Log out"
+msgstr "Desconnecta"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:116
+msgid "logout;sign off"
+msgstr "desconnecta;eixida"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:120
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Atura temporalment"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:123
+msgid "suspend;sleep"
+msgstr "atura temporalment;dorm"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:127
+msgctxt "search-result"
+msgid "Switch user"
+msgstr "Canvia d'usuari"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:130
+msgid "switch user"
+msgstr "canvia d'usuari"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:134
+msgctxt "search-result"
+msgid "Lock orientation"
+msgstr "Bloqueja l'orientació"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:137
+msgid "lock orientation"
+msgstr "bloqueja l'orientació"
+
+#: js/misc/util.js:122
+msgid "Command not found"
+msgstr "No s'ha trobat l'orde"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:155
+msgid "Could not parse command:"
+msgstr "No s'ha pogut analitzar l'orde:"
+
+#: js/misc/util.js:163
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "No s'ha pogut executar «%s»:"
+
+#: js/misc/util.js:180
+msgid "Just now"
+msgstr "Ara mateix"
+
+#: js/misc/util.js:182
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "fa %d minut"
+msgstr[1] "fa %d minuts"
+
+#: js/misc/util.js:185
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "fa %d hora"
+msgstr[1] "fa %d hores"
+
+#: js/misc/util.js:188
+msgid "Yesterday"
+msgstr "Ahir"
+
+#: js/misc/util.js:190
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "fa %d dia"
+msgstr[1] "fa %d dies"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "fa %d setmana"
+msgstr[1] "fa %d setmanes"
+
+#: js/misc/util.js:196
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "fa %d mes"
+msgstr[1] "fa %d mesos"
+
+#: js/misc/util.js:198
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "fa %d any"
+msgstr[1] "fa %d anys"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:228
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:234
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ahir, a les %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:240
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A a les %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:246
+#, no-c-format
+msgid "%B %d, %H∶%M"
+msgstr " %d de %B, a les %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:252
+#, no-c-format
+msgid "%B %d %Y, %H∶%M"
+msgstr "%d de %B de %Y, a les %H∶%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:257
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:263
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ahir, a les %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:269
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, a les %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:275
+#, no-c-format
+msgid "%B %d, %l∶%M %p"
+msgstr "%d de %B, a les %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:281
+#, no-c-format
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%d de %B de %Y, a les %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:66
+msgid "Hotspot Login"
+msgstr "Inici de sessió al punt d'accés Wi-Fi"
+
+#: js/portalHelper/main.js:112
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"La connexió a este punt d'accés no és segura. Contrasenyes o altra "
+"informació que introduïu en esta pàgina pot ser vista per persones "
+"properes."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:63 js/ui/status/location.js:395
+msgid "Deny Access"
+msgstr "Denega l'accés"
+
+#: js/ui/accessDialog.js:64 js/ui/status/location.js:398
+msgid "Grant Access"
+msgstr "Permet l'accés"
+
+#: js/ui/appDisplay.js:809
+msgid "Frequently used applications will appear here"
+msgstr "Les aplicacions utilitzades freqüentment apareixeran ací"
+
+#: js/ui/appDisplay.js:930
+msgid "Frequent"
+msgstr "Freqüent"
+
+#: js/ui/appDisplay.js:937
+msgid "All"
+msgstr "Totes"
+
+#: js/ui/appDisplay.js:1915
+msgid "New Window"
+msgstr "Finestra nova"
+
+#: js/ui/appDisplay.js:1929
+msgid "Launch using Dedicated Graphics Card"
+msgstr "Inicia usant una targeta gràfica dedicada"
+
+#: js/ui/appDisplay.js:1956 js/ui/dash.js:289
+msgid "Remove from Favorites"
+msgstr "Suprimeix dels preferits"
+
+#: js/ui/appDisplay.js:1962
+msgid "Add to Favorites"
+msgstr "Afig als preferits"
+
+#: js/ui/appDisplay.js:1972
+msgid "Show Details"
+msgstr "Mostra els detalls"
+
+#: js/ui/appFavorites.js:141
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "S'ha afegit %s als preferits."
+
+#: js/ui/appFavorites.js:175
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "S'ha suprimit %s dels preferits."
+
+#: js/ui/audioDeviceSelection.js:59
+msgid "Select Audio Device"
+msgstr "Seleccioneu dispositiu d'àudio"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Sound Settings"
+msgstr "Paràmetres del so"
+
+#: js/ui/audioDeviceSelection.js:78
+msgid "Headphones"
+msgstr "Auriculars"
+
+#: js/ui/audioDeviceSelection.js:80
+msgid "Headset"
+msgstr "Auriculars amb micròfon"
+
+#: js/ui/audioDeviceSelection.js:82 js/ui/status/volume.js:221
+msgid "Microphone"
+msgstr "Micròfon"
+
+#: js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "Canvia el fons de l'escriptori…"
+
+#: js/ui/backgroundMenu.js:21 js/ui/status/nightLight.js:51
+msgid "Display Settings"
+msgstr "Paràmetres de la pantalla"
+
+#: js/ui/backgroundMenu.js:22 js/ui/status/system.js:265
+msgid "Settings"
+msgstr "Paràmetres"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:47
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:76
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Dg"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:78
+msgctxt "grid monday"
+msgid "M"
+msgstr "Dl"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:80
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Dt"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:82
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Dc"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:84
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Dj"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:86
+msgctxt "grid friday"
+msgid "F"
+msgstr "Dv"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:88
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Ds"
+
+#: js/ui/calendar.js:442
+msgid "Previous month"
+msgstr "Mes anterior"
+
+#: js/ui/calendar.js:452
+msgid "Next month"
+msgstr "Mes següent"
+
+#: js/ui/calendar.js:605
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:660
+msgid "Week %V"
+msgstr "Setmana %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:729
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Tot el dia"
+
+#: js/ui/calendar.js:862
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A %d de %B"
+
+#: js/ui/calendar.js:866
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A %d de %B de %Y"
+
+#: js/ui/calendar.js:1086
+msgid "No Notifications"
+msgstr "Cap notificació"
+
+#: js/ui/calendar.js:1089
+msgid "No Events"
+msgstr "Cap cita"
+
+#: js/ui/calendar.js:1117
+msgid "Clear All"
+msgstr "Neteja-ho tot"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:44
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» no està responent."
+
+#: js/ui/closeDialog.js:45
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Podeu esperar un moment perquè continue o podeu forçar-ne l'eixida "
+"completa."
+
+#: js/ui/closeDialog.js:61
+msgid "Force Quit"
+msgstr "Força l'eixida"
+
+#: js/ui/closeDialog.js:64
+msgid "Wait"
+msgstr "Espera"
+
+#: js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "S'ha connectat un dispositiu extern"
+
+#: js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "S'ha desconnectat un dispositiu extern"
+
+#: js/ui/components/autorunManager.js:354
+#, javascript-format
+msgid "Open with %s"
+msgstr "Obri amb %s"
+
+#: js/ui/components/keyring.js:107 js/ui/components/polkitAgent.js:284
+msgid "Password:"
+msgstr "Contrasenya:"
+
+#: js/ui/components/keyring.js:140
+msgid "Type again:"
+msgstr "Torneu a escriure-la:"
+
+#: js/ui/components/networkAgent.js:112 js/ui/status/network.js:261
+#: js/ui/status/network.js:355 js/ui/status/network.js:939
+msgid "Connect"
+msgstr "Connecta"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:205 js/ui/components/networkAgent.js:217
+#: js/ui/components/networkAgent.js:245 js/ui/components/networkAgent.js:265
+#: js/ui/components/networkAgent.js:275
+msgid "Password: "
+msgstr "Contrasenya:"
+
+#. static WEP
+#: js/ui/components/networkAgent.js:210
+msgid "Key: "
+msgstr "Clau:"
+
+#: js/ui/components/networkAgent.js:249
+msgid "Identity: "
+msgstr "Identitat:"
+
+#: js/ui/components/networkAgent.js:251
+msgid "Private key password: "
+msgstr "Contrasenya de la clau privada:"
+
+#: js/ui/components/networkAgent.js:263
+msgid "Service: "
+msgstr "Servei:"
+
+#: js/ui/components/networkAgent.js:292 js/ui/components/networkAgent.js:638
+msgid "Authentication required by wireless network"
+msgstr "La xarxa sense fil requereix autenticació"
+
+#: js/ui/components/networkAgent.js:293 js/ui/components/networkAgent.js:639
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Per accedir a la xarxa sense fil «%s» calen les contrasenyes o les claus "
+"d'encriptació."
+
+#: js/ui/components/networkAgent.js:297 js/ui/components/networkAgent.js:642
+msgid "Wired 802.1X authentication"
+msgstr "Autenticació 802.1X amb fil"
+
+#: js/ui/components/networkAgent.js:299
+msgid "Network name: "
+msgstr "Nom de la xarxa: "
+
+#: js/ui/components/networkAgent.js:304 js/ui/components/networkAgent.js:646
+msgid "DSL authentication"
+msgstr "Autenticació DSL"
+
+#: js/ui/components/networkAgent.js:311 js/ui/components/networkAgent.js:652
+msgid "PIN code required"
+msgstr "Cal que introduïu el codi PIN"
+
+#: js/ui/components/networkAgent.js:312 js/ui/components/networkAgent.js:653
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Cal que introduïu el codi PIN del dispositiu de banda ampla mòbil"
+
+#: js/ui/components/networkAgent.js:313
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:320 js/ui/components/networkAgent.js:659
+msgid "Mobile broadband network password"
+msgstr "Contrasenya de la xarxa de banda ampla mòbil"
+
+#: js/ui/components/networkAgent.js:321 js/ui/components/networkAgent.js:643
+#: js/ui/components/networkAgent.js:647 js/ui/components/networkAgent.js:660
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Cal introduir una contrasenya per connectar-vos a «%s»."
+
+#: js/ui/components/networkAgent.js:627 js/ui/status/network.js:1720
+msgid "Network Manager"
+msgstr "Gestor de connexions de xarxa"
+
+#: js/ui/components/polkitAgent.js:43
+msgid "Authentication Required"
+msgstr "Cal autenticació"
+
+#: js/ui/components/polkitAgent.js:71
+msgid "Administrator"
+msgstr "Administrador"
+
+#: js/ui/components/polkitAgent.js:151
+msgid "Authenticate"
+msgstr "Autentica"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:270 js/ui/shellMountOperation.js:328
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "No ha funcionat. Torneu-ho a provar."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:799
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "En/na %s ara es diu %s"
+
+#: js/ui/ctrlAltTab.js:29 js/ui/viewSelector.js:186
+msgid "Windows"
+msgstr "Finestres"
+
+#: js/ui/dash.js:250 js/ui/dash.js:291
+msgid "Show Applications"
+msgstr "Mostra les aplicacions"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:449
+msgid "Dash"
+msgstr "Quadre d'aplicacions"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: js/ui/dateMenu.js:75
+msgid "%B %e %Y"
+msgstr "%e de %B de %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:82
+msgid "%A %B %e %Y"
+msgstr "%A, %e de %B de %Y"
+
+#: js/ui/dateMenu.js:144
+msgid "Add world clocks…"
+msgstr "Afig rellotges del món…"
+
+#: js/ui/dateMenu.js:145
+msgid "World Clocks"
+msgstr "Rellotges del món"
+
+#: js/ui/dateMenu.js:224
+msgid "Weather"
+msgstr "El temps"
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:286
+#, javascript-format
+msgid "%s all day."
+msgstr "%s tot el dia."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:292
+#, javascript-format
+msgid "%s, then %s later."
+msgstr "%s, llavors %s més tard."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:298
+#, javascript-format
+msgid "%s, then %s, followed by %s later."
+msgstr "%s, llavors %s, seguit per %s més tard."
+
+#: js/ui/dateMenu.js:309
+msgid "Select a location…"
+msgstr "Trieu una ubicació…"
+
+#: js/ui/dateMenu.js:312
+msgid "Loading…"
+msgstr "S'està carregant…"
+
+#. Translators: %s is a temperature with unit, e.g. "23℃"
+#: js/ui/dateMenu.js:318
+#, javascript-format
+msgid "Feels like %s."
+msgstr "Sensació tèrmica de %s."
+
+#: js/ui/dateMenu.js:321
+msgid "Go online for weather information"
+msgstr "Vés en línia per a informació sobre el temps"
+
+#: js/ui/dateMenu.js:323
+msgid "Weather information is currently unavailable"
+msgstr "La informació sobre el temps no està disponible"
+
+#: js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Eixida %s"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "Eixida"
+
+#: js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s eixirà de la sessió automàticament d'ací %d segon."
+msgstr[1] "%s eixirà de la sessió automàticament d'ací %d segons."
+
+#: js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Eixireu automàticament d'ací %d segon."
+msgstr[1] "Eixireu automàticament d'ací %d segons."
+
+#: js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Ix"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "Apagada"
+
+#: js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instal·la les actualitzacions i apaga"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "S'apagarà l'ordinador automàticament d'ací %d segon."
+msgstr[1] "S'apagarà l'ordinador automàticament d'ací %d segons."
+
+#: js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instal·la les actualitzacions pendents"
+
+#: js/ui/endSessionDialog.js:94 js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "Reinicia"
+
+#: js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "Apaga"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "Reinici"
+
+#: js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Es reiniciarà l'ordinador automàticament d'ací %d segon."
+msgstr[1] "Es reiniciarà l'ordinador automàticament d'ací %d segons."
+
+#: js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reinicia i instal·la les actualitzacions"
+
+#: js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Es reiniciarà l'ordinador automàticament i s'instal·laran les "
+"actualitzacions d'ací %d segon."
+msgstr[1] ""
+"Es reiniciarà l'ordinador automàticament i s'instal·laran les "
+"actualitzacions d'ací %d segons."
+
+#: js/ui/endSessionDialog.js:127 js/ui/endSessionDialog.js:147
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reinicia i instal·la"
+
+#: js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instal·la i apaga"
+
+#: js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Apaga després d'instal·lar les actualitzacions"
+
+#: js/ui/endSessionDialog.js:137
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reinicia i instal·la l'actualització"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:142
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s s'instal·larà després del reinici. Una actualització de la "
+"instal·lació pot trigar força temps. Assegureu-vos que heu fet còpia de "
+"seguretat i que l'ordinador està connectat al corrent."
+
+#: js/ui/endSessionDialog.js:361
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"S'està utilitzant la bateria: connecteu l'ordinador a la xarxa elèctrica "
+"abans d'instal·lar les actualitzacions."
+
+#: js/ui/endSessionDialog.js:378
+msgid "Some applications are busy or have unsaved work."
+msgstr ""
+"Hi ha algunes aplicacions que estan ocupades o que tenen documents sense "
+"guardar."
+
+#: js/ui/endSessionDialog.js:385
+msgid "Other users are logged in."
+msgstr "Altres usuaris tenen la sessió oberta."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:670
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remot)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:673
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consola)"
+
+#: js/ui/extensionDownloader.js:201
+msgid "Install"
+msgstr "Instal·la"
+
+#: js/ui/extensionDownloader.js:206
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Voleu baixar i instal·lar «%s» d'extensions.gnome.org?"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:59
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr "%s vol inhabilitar les dreceres"
+
+#: js/ui/inhibitShortcutsDialog.js:60
+msgid "Application wants to inhibit shortcuts"
+msgstr "L'aplicació vol inhabilitar les dreceres"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:69
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Podeu restaurar les dreceres si premeu %s."
+
+#: js/ui/inhibitShortcutsDialog.js:74
+msgid "Deny"
+msgstr "Denega"
+
+#: js/ui/inhibitShortcutsDialog.js:80
+msgid "Allow"
+msgstr "Permet"
+
+#: js/ui/keyboard.js:738 js/ui/status/keyboard.js:782
+msgid "Keyboard"
+msgstr "Teclat"
+
+#: js/ui/lookingGlass.js:642
+msgid "No extensions installed"
+msgstr "No hi ha cap extensió instal·lada"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:696
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s no ha emés cap error."
+
+#: js/ui/lookingGlass.js:702
+msgid "Hide Errors"
+msgstr "Amaga els errors"
+
+#: js/ui/lookingGlass.js:706 js/ui/lookingGlass.js:766
+msgid "Show Errors"
+msgstr "Mostra els errors"
+
+#: js/ui/lookingGlass.js:715
+msgid "Enabled"
+msgstr "Habilitat"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:718 subprojects/gvc/gvc-mixer-control.c:1866
+msgid "Disabled"
+msgstr "Inhabilitat"
+
+#: js/ui/lookingGlass.js:720
+msgid "Error"
+msgstr "Error"
+
+#: js/ui/lookingGlass.js:722
+msgid "Out of date"
+msgstr "Fora d'hora"
+
+#: js/ui/lookingGlass.js:724
+msgid "Downloading"
+msgstr "S'està baixant"
+
+#: js/ui/lookingGlass.js:748
+msgid "View Source"
+msgstr "Mostra el codi font"
+
+#: js/ui/lookingGlass.js:757
+msgid "Web Page"
+msgstr "Pàgina web"
+
+#: js/ui/messageTray.js:1493
+msgid "System Information"
+msgstr "Informació de l'ordinador"
+
+#: js/ui/mpris.js:211
+msgid "Unknown artist"
+msgstr "Artista desconegut"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Títol desconegut"
+
+#: js/ui/overview.js:84
+msgid "Undo"
+msgstr "Desfés"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:113
+msgid "Overview"
+msgstr "Vista general"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:240
+msgid "Type to search…"
+msgstr "Teclegeu per buscar…"
+
+#: js/ui/padOsd.js:103
+msgid "New shortcut…"
+msgstr "Drecera de teclat nova…"
+
+#: js/ui/padOsd.js:152
+msgid "Application defined"
+msgstr "Definit per l'aplicació"
+
+#: js/ui/padOsd.js:153
+msgid "Show on-screen help"
+msgstr "Mostra l'ajuda en pantalla"
+
+#: js/ui/padOsd.js:154
+msgid "Switch monitor"
+msgstr "Commuta el monitor "
+
+#: js/ui/padOsd.js:155
+msgid "Assign keystroke"
+msgstr "Assigna la pulsació de tecla"
+
+#: js/ui/padOsd.js:220
+msgid "Done"
+msgstr "Fet"
+
+#: js/ui/padOsd.js:734
+msgid "Edit…"
+msgstr "Edita…"
+
+#: js/ui/padOsd.js:774 js/ui/padOsd.js:879
+msgid "None"
+msgstr "Cap"
+
+#: js/ui/padOsd.js:833
+msgid "Press a button to configure"
+msgstr "Premeu un botó per a configurar"
+
+#: js/ui/padOsd.js:834
+msgid "Press Esc to exit"
+msgstr "Premeu Esc per eixir"
+
+#: js/ui/padOsd.js:837
+msgid "Press any key to exit"
+msgstr "Premeu qualsevol tecla per eixir"
+
+#: js/ui/panel.js:358
+msgid "Quit"
+msgstr "Ix"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:414
+msgid "Activities"
+msgstr "Activitats"
+
+#: js/ui/panel.js:695
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:812
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:291
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: js/ui/runDialog.js:71
+msgid "Enter a Command"
+msgstr "Introduïu una orde"
+
+#: js/ui/runDialog.js:111 js/ui/windowMenu.js:160
+msgid "Close"
+msgstr "Tanca"
+
+#: js/ui/runDialog.js:277
+msgid "Restart is not available on Wayland"
+msgstr "El reinici no està disponible en Wayland"
+
+#: js/ui/runDialog.js:282
+msgid "Restarting…"
+msgstr "S'està reiniciant…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A %d de %B"
+
+#: js/ui/screenShield.js:147
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d missatge nou"
+msgstr[1] "%d missatges nous"
+
+#: js/ui/screenShield.js:149
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d notificació nova"
+msgstr[1] "%d notificacions noves"
+
+#: js/ui/screenShield.js:452 js/ui/status/system.js:284
+msgid "Lock"
+msgstr "Bloqueja"
+
+#: js/ui/screenShield.js:715
+msgid "GNOME needs to lock the screen"
+msgstr "El GNOME necessita bloquejar la pantalla"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:836 js/ui/screenShield.js:1308
+msgid "Unable to lock"
+msgstr "No es pot blocar"
+
+#: js/ui/screenShield.js:837 js/ui/screenShield.js:1309
+msgid "Lock was blocked by an application"
+msgstr "Una aplicació està bloquejant el bloqueig"
+
+#: js/ui/search.js:651
+msgid "Searching…"
+msgstr "S'està cercant…"
+
+#: js/ui/search.js:653
+msgid "No results."
+msgstr "No s'ha trobat cap resultat."
+
+#: js/ui/search.js:777
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d més"
+msgstr[1] "%d més"
+
+#: js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "Copia"
+
+#: js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "Enganxa"
+
+#: js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "Mostra el text"
+
+#: js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "Oculta el text"
+
+#: js/ui/shellMountOperation.js:315
+msgid "Password"
+msgstr "Contrasenya"
+
+#: js/ui/shellMountOperation.js:336
+msgid "Remember Password"
+msgstr "Recorda la contrasenya"
+
+#: js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "Accessibilitat"
+
+#: js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "Amplia"
+
+#: js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "Lector de pantalla"
+
+#: js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "Teclat en pantalla"
+
+#: js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "Avisos visuals"
+
+#: js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "Tecles enganxoses"
+
+#: js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "Tecles lentes"
+
+#: js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "Tecles de salt"
+
+#: js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "Tecles del ratolí"
+
+#: js/ui/status/accessibility.js:167
+msgid "High Contrast"
+msgstr "Alt contrast"
+
+#: js/ui/status/accessibility.js:202
+msgid "Large Text"
+msgstr "Text gran"
+
+#: js/ui/status/bluetooth.js:47
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:56 js/ui/status/network.js:627
+msgid "Bluetooth Settings"
+msgstr "Paràmetres del Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:136
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d connectat"
+msgstr[1] "%d connectats"
+
+#: js/ui/status/bluetooth.js:138
+msgid "Off"
+msgstr "Desactivat"
+
+#: js/ui/status/bluetooth.js:140
+msgid "On"
+msgstr "Activat"
+
+#: js/ui/status/bluetooth.js:142 js/ui/status/network.js:1299
+msgid "Turn On"
+msgstr "Activa"
+
+#: js/ui/status/bluetooth.js:142 js/ui/status/network.js:170
+#: js/ui/status/network.js:356 js/ui/status/network.js:1299
+#: js/ui/status/network.js:1418 js/ui/status/nightLight.js:47
+#: js/ui/status/rfkill.js:90 js/ui/status/rfkill.js:117
+msgid "Turn Off"
+msgstr "Desactiva"
+
+#: js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "Brillantor"
+
+#: js/ui/status/keyboard.js:805
+msgid "Show Keyboard Layout"
+msgstr "Mostra la disposició del teclat"
+
+#: js/ui/status/location.js:89 js/ui/status/location.js:197
+msgid "Location Enabled"
+msgstr "Ubicació habilitada"
+
+#: js/ui/status/location.js:90 js/ui/status/location.js:198
+msgid "Disable"
+msgstr "Inhabilita"
+
+#: js/ui/status/location.js:91
+msgid "Privacy Settings"
+msgstr "Paràmetres de privacitat"
+
+#: js/ui/status/location.js:196
+msgid "Location In Use"
+msgstr "Ubicació en ús"
+
+#: js/ui/status/location.js:200
+msgid "Location Disabled"
+msgstr "Ubicació inhabilitada"
+
+#: js/ui/status/location.js:201
+msgid "Enable"
+msgstr "Habilita"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:388
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "Voleu donar a %s accés a la vostra ubicació?"
+
+#: js/ui/status/location.js:389
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Podeu canviar la configuració de l'accés a la ubicació sempre que vulgueu "
+"des de la configuració de la privacitat."
+
+#: js/ui/status/network.js:104
+msgid "<unknown>"
+msgstr "<desconegut>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454 js/ui/status/network.js:1328
+#, javascript-format
+msgid "%s Off"
+msgstr "%s apagat"
+
+# N.T.: p. ex. Connectat amb fil
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:457
+#, javascript-format
+msgid "%s Connected"
+msgstr "Connectat %s"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:462
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s no gestionat"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:465
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s s'està desconnectant"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:472 js/ui/status/network.js:1320
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s s'està connectant"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:475
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s requereix autenticació"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:483
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Falta el microprogramari per %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:487
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s no disponible"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:490
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s ha fallat la connexió"
+
+#: js/ui/status/network.js:506
+msgid "Wired Settings"
+msgstr "Paràmetres de la xarxa amb fil"
+
+#: js/ui/status/network.js:548
+msgid "Mobile Broadband Settings"
+msgstr "Configuració de la xarxa de banda ampla mòbil"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:591 js/ui/status/network.js:1325
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s maquinari inhabilitat"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:595
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s Inhabilitat"
+
+#: js/ui/status/network.js:635
+msgid "Connect to Internet"
+msgstr "Connecta a Internet"
+
+#: js/ui/status/network.js:833
+msgid "Airplane Mode is On"
+msgstr "El mode d'avió és actiu"
+
+#: js/ui/status/network.js:834
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Quan el mode d'avió és actiu es desactiva la xarxa sense fil."
+
+#: js/ui/status/network.js:835
+msgid "Turn Off Airplane Mode"
+msgstr "Desactiva el mode d'avió"
+
+#: js/ui/status/network.js:844
+msgid "Wi-Fi is Off"
+msgstr "La xarxa sense fil està desactivada"
+
+#: js/ui/status/network.js:845
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "S'ha d'activar la xarxa sense fil per poder-se connectar a una xarxa."
+
+#: js/ui/status/network.js:846
+msgid "Turn On Wi-Fi"
+msgstr "Activa la xarxa sense fil"
+
+#: js/ui/status/network.js:871
+msgid "Wi-Fi Networks"
+msgstr "Xarxes sense fil"
+
+#: js/ui/status/network.js:873
+msgid "Select a network"
+msgstr "Trieu una xarxa"
+
+#: js/ui/status/network.js:903
+msgid "No Networks"
+msgstr "Cap xarxa"
+
+#: js/ui/status/network.js:924 js/ui/status/rfkill.js:115
+msgid "Use hardware switch to turn off"
+msgstr "Utilitza l'interruptor de maquinari per desactivar-la"
+
+#: js/ui/status/network.js:1191
+msgid "Select Network"
+msgstr "Trieu una xarxa"
+
+#: js/ui/status/network.js:1197
+msgid "Wi-Fi Settings"
+msgstr "Paràmetres de la xarxa sense fil"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1316
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "Hostpot %s actiu"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1331
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s no està connectat"
+
+#: js/ui/status/network.js:1435
+msgid "connecting…"
+msgstr "s'està connectant..."
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1438
+msgid "authentication required"
+msgstr "cal autenticació"
+
+#: js/ui/status/network.js:1440
+msgid "connection failed"
+msgstr "ha fallat la connexió"
+
+#: js/ui/status/network.js:1494
+msgid "VPN Settings"
+msgstr "Paràmetres de la VPN"
+
+#: js/ui/status/network.js:1498
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1508
+msgid "VPN Off"
+msgstr "VPN apagada"
+
+#: js/ui/status/network.js:1572 js/ui/status/rfkill.js:93
+msgid "Network Settings"
+msgstr "Paràmetres de xarxa"
+
+#: js/ui/status/network.js:1603
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s connexió amb fil"
+msgstr[1] "%s connexions amb fil"
+
+#: js/ui/status/network.js:1607
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s connexió Wifi"
+msgstr[1] "%s connexions Wifi"
+
+#: js/ui/status/network.js:1611
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s connexió mòdem"
+msgstr[1] "%s connexions mòdem"
+
+#: js/ui/status/network.js:1759
+msgid "Connection failed"
+msgstr "Ha fallat la connexió"
+
+#: js/ui/status/network.js:1760
+msgid "Activation of network connection failed"
+msgstr "Ha fallat l'activació de la connexió de xarxa"
+
+#: js/ui/status/nightLight.js:68
+msgid "Night Light Disabled"
+msgstr "Llum nocturna inhabilitada"
+
+#: js/ui/status/nightLight.js:69
+msgid "Night Light On"
+msgstr "Llum nocturna habilitada"
+
+#: js/ui/status/nightLight.js:70
+msgid "Resume"
+msgstr "Reprén"
+
+#: js/ui/status/nightLight.js:71
+msgid "Disable Until Tomorrow"
+msgstr "Inhabilita fins demà"
+
+#: js/ui/status/power.js:61
+msgid "Power Settings"
+msgstr "Paràmetres d'energia"
+
+#: js/ui/status/power.js:77
+msgid "Fully Charged"
+msgstr "Totalment carregada"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:84 js/ui/status/power.js:90
+msgid "Estimating…"
+msgstr "S'està estimant la duració…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:98
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "Queden %d∶%02d (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:103
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d per completar la càrrega (%d %%)"
+
+#: js/ui/status/power.js:131 js/ui/status/power.js:133
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:88
+msgid "Airplane Mode On"
+msgstr "El mode d'avió és actiu"
+
+#: js/ui/status/system.js:228
+msgid "Switch User"
+msgstr "Canvia d'usuari"
+
+#: js/ui/status/system.js:240
+msgid "Log Out"
+msgstr "Ix"
+
+#: js/ui/status/system.js:252
+msgid "Account Settings"
+msgstr "Paràmetres del compte"
+
+#: js/ui/status/system.js:269
+msgid "Orientation Lock"
+msgstr "Bloqueja l'orientació"
+
+#: js/ui/status/system.js:295
+msgid "Suspend"
+msgstr "Atura temporalment"
+
+#: js/ui/status/system.js:305
+msgid "Power Off"
+msgstr "Apaga"
+
+#: js/ui/status/volume.js:128
+msgid "Volume changed"
+msgstr "S'ha canviat el volum"
+
+#: js/ui/status/volume.js:170
+msgid "Volume"
+msgstr "Volum"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:21
+msgid "Mirror"
+msgstr "Mirall"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:26
+msgid "Join Displays"
+msgstr "Uneix pantalles"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:31
+msgid "External Only"
+msgstr "Només extern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:36
+msgid "Built-in Only"
+msgstr "Només l'integrat"
+
+#: js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "Entra amb un altre usuari"
+
+#: js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "Desbloqueja la finestra"
+
+#: js/ui/viewSelector.js:190
+msgid "Applications"
+msgstr "Aplicacions"
+
+#: js/ui/viewSelector.js:194
+msgid "Search"
+msgstr "Busca"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» ja està a punt"
+
+#: js/ui/windowManager.js:72
+msgid "Do you want to keep these display settings?"
+msgstr "Voleu mantindre esta configuració de la pantalla?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "Descarta els canvis"
+
+#: js/ui/windowManager.js:87
+msgid "Keep Changes"
+msgstr "Mantén els canvis"
+
+#: js/ui/windowManager.js:105
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Es descartaran els canvis d'ací %d segon"
+msgstr[1] "Es descartaran els canvis d'ací %d segons"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:660
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "Minimitza"
+
+#: js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "Desmaximitza"
+
+#: js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "Maximitza"
+
+#: js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "Mou"
+
+#: js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "Reajusta"
+
+#: js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "Mou la barra de títol a la pantalla"
+
+#: js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "Sempre per damunt"
+
+#: js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "Sempre a l'espai de treball visible"
+
+#: js/ui/windowMenu.js:103
+msgid "Move to Workspace Left"
+msgstr "Mou a l'espai de treball esquerre"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Right"
+msgstr "Mou a l'espai de treball dret"
+
+#: js/ui/windowMenu.js:113
+msgid "Move to Workspace Up"
+msgstr "Mou a l'espai de treball superior"
+
+#: js/ui/windowMenu.js:118
+msgid "Move to Workspace Down"
+msgstr "Mou a l'espai de treball inferior"
+
+#: js/ui/windowMenu.js:134
+msgid "Move to Monitor Up"
+msgstr "Mou a la pantalla de dalt"
+
+#: js/ui/windowMenu.js:140
+msgid "Move to Monitor Down"
+msgstr "Mou a la pantalla de baix"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Left"
+msgstr "Mou a la pantalla de l'esquerra"
+
+#: js/ui/windowMenu.js:152
+msgid "Move to Monitor Right"
+msgstr "Mou a la pantalla de la dreta"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendari de l'Evolution"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: src/main.c:380
+msgid "Print version"
+msgstr "Mostra la versió"
+
+#: src/main.c:386
+msgid "Mode used by GDM for login screen"
+msgstr "El mode que utilitzarà el GDM per a la pantalla d'entrada"
+
+#: src/main.c:392
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Utilitza un mode específic, p. ex. «gdm» per la pantalla d'entrada"
+
+#: src/main.c:398
+msgid "List possible modes"
+msgstr "Llista els modes possibles"
+
+#: src/shell-app.c:270
+msgctxt "program"
+msgid "Unknown"
+msgstr "Desconegut"
+
+#: src/shell-app.c:511
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "No s'ha pogut iniciar «%s»"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "Les contrasenyes no coincideixen."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "La contrasenya no pot ser buida"
+
+#: src/shell-polkit-authentication-agent.c:353
+msgid "Authentication dialog was dismissed by the user"
+msgstr "L'usuari ha descartat el diàleg d'autenticació"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1873
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u eixida"
+msgstr[1] "%u eixides"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1883
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entrades"
+
+#: subprojects/gvc/gvc-mixer-control.c:2738
+msgid "System Sounds"
+msgstr "Sons del sistema"
+
+#~ msgid "Hide tray"
+#~ msgstr "Oculta la Safata"
+
+#~ msgid "Status Icons"
+#~ msgstr "Icones d'estat"
+
+#~ msgid "Events"
+#~ msgstr "Cites"
+
+#~ msgid "Notifications"
+#~ msgstr "Notificacions"
+
+#~ msgid "Clear section"
+#~ msgstr "Neteja la secció"
+
+#~ msgid "Media"
+#~ msgstr "Multimèdia"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Redirecció per l'autenticació web"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Not In Use"
+#~ msgstr "No s'usa"
+
+#~ msgid "GNOME Shell Extension Preferences"
+#~ msgstr "Preferències de les extensions del GNOME Shell"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Mostra el número de la setmana al calendari"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Si és «true» (cert) es mostra el número de la setmana al calendari."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Utilitza com a connexió a Internet"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s està demanant accés a la vostra ubicació."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (composició mitjançant Wayland)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d dispositiu connectat"
+#~ msgstr[1] "%d dispositius connectats"
+
+#~ msgid "Authentication required"
+#~ msgstr "Cal autenticació"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Bateria"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Mode d'avió"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Mostra la safata de missatges"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Portal captiu"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Dg."
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Dl."
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Dt."
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Dc."
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Dj."
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Dv."
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Ds."
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "No hi ha res apuntat"
+
+#~ msgid "Today"
+#~ msgstr "Avui"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Demà"
+
+#~ msgid "This week"
+#~ msgstr "Aquesta setmana"
+
+#~ msgid "Next week"
+#~ msgstr "La setmana que ve"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Dispositius extraïbles"
+
+#~ msgid "Eject"
+#~ msgstr "Expulsa"
+
+#~ msgid "Invitation"
+#~ msgstr "Invitació"
+
+#~ msgid "Call"
+#~ msgstr "Trucada"
+
+#~ msgid "File Transfer"
+#~ msgstr "Transferència de fitxers"
+
+#~ msgid "Chat"
+#~ msgstr "Xat"
+
+#~ msgid "Unmute"
+#~ msgstr "Amb so"
+
+#~ msgid "Mute"
+#~ msgstr "Silencia"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Us han convidat a entrar a %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "En/na %s us convida a entrar a %s"
+
+#~ msgid "Decline"
+#~ msgstr "Declina"
+
+#~ msgid "Accept"
+#~ msgstr "Accepta"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Videotrucada de %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Trucada de %s"
+
+#~ msgid "Answer"
+#~ msgstr "Contesta"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "En/na %s us envia %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s vol poder saber quan esteu en línia"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Ha fallat l'autenticació"
+
+#~ msgid "Encryption error"
+#~ msgstr "Error d'encriptació"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "No s'ha proporcionat el certificat"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "El certificat no és de confiança"
+
+#~ msgid "Certificate expired"
+#~ msgstr "El certificat ha vençut"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "El certificat no està activat"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "No coincideix el nom de la màquina del certificat"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "No coincideix l'empremta digital del certificat"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificat signat per un mateix"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "S'ha establert l'estat a fora de línia"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "El certificat no és vàlid"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "S'ha rebutjat la connexió"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "No es pot establir la connexió"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "S'ha perdut la connexió"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Aquest compte ja està connectat al servidor"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "S'ha reemplaçat la connexió per una altra de nova fent servir el mateix "
+#~ "recurs"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Ja existeix aquest compte al servidor"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "El servidor està massa ocupat per gestionar la connexió"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "S'ha revocat el certificat"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "El certificat utilitza un algorisme criptogràfic no segur o la seva "
+#~ "fortalesa criptogràfica és feble"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "La llargada del certificat del servidor o la profunditat de la cadena de "
+#~ "certificació excedeix els límits de la biblioteca criptogràfica"
+
+#~ msgid "Internal error"
+#~ msgstr "S'ha produït un error intern"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "No es pot connectar a %s"
+
+#~ msgid "View account"
+#~ msgstr "Mostra el compte"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Obre el calendari"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Configuració de la data i de l'hora"
+
+#~ msgid "Open"
+#~ msgstr "Obre"
+
+#~ msgid "Remove"
+#~ msgstr "Suprimeix"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Neteja els missatges"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Configuració de les notificacions"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Menú de la safata"
+
+#~ msgid "No Messages"
+#~ msgstr "No hi ha cap missatge"
+
+#~ msgid "Message Tray"
+#~ msgstr "Safata de missatges"
diff --git a/po/ckb.po b/po/ckb.po
new file mode 100644
index 0000000..53457e1
--- /dev/null
+++ b/po/ckb.po
@@ -0,0 +1,2619 @@
+# Kurdish (Sorani) translation for gnome-shell
+# Copyright (c) 2020 Rosetta Contributors and Canonical Ltd 2020
+# This file is distributed under the same license as the gnome-shell package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2020-01-13 09:13+0000\n"
+"PO-Revision-Date: 2020-03-28 18:37+0300\n"
+"Last-Translator: Jwtiyar Nariman <jwtiyar@gmail.com>\n"
+"Language-Team: Kurdish (Sorani) <ckb@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Launchpad-Export-Date: 2020-03-28 15:36+0000\n"
+"X-Generator: Poedit 2.3\n"
+"Language: ckb\n"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:211
+msgid "Shell Extensions"
+msgstr ""
+
+#: data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "ڕێکخستنی زیادکراوەکانی گنۆم شێڵ"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "شێڵی گنۆم"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "App Picker View"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:69
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:74
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:78
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:87
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:97
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:106
+msgid "Enable introspection API"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:107
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:119
+msgid "Keybinding to open the application menu"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:120
+msgid "Keybinding to open the application menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:126
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:127
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid "Keybinding to open the overview"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:135
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:141
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to focus the active notification"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to focus the active notification."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:155
+msgid "Switch to application 1"
+msgstr "گۆڕین بۆ داوانامەی ١"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Switch to application 2"
+msgstr "گۆڕین بۆ داوانامەی ٢"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Switch to application 3"
+msgstr "گۆڕین بۆ داوانامەی ٣"
+
+#: data/org.gnome.shell.gschema.xml.in:167
+msgid "Switch to application 4"
+msgstr "گۆڕین بۆ داوانامەی ٤"
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid "Switch to application 5"
+msgstr "گۆڕین بۆ داوانامەی ٥"
+
+#: data/org.gnome.shell.gschema.xml.in:175
+msgid "Switch to application 6"
+msgstr "گۆڕین بۆ داوانامەی ٦"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Switch to application 7"
+msgstr "گۆڕین بۆ داوانامەی ٧"
+
+#: data/org.gnome.shell.gschema.xml.in:183
+msgid "Switch to application 8"
+msgstr "گۆڕین بۆ داوانامەی ٨"
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Switch to application 9"
+msgstr "گۆڕین بۆ داوانامەی ٩"
+
+#: data/org.gnome.shell.gschema.xml.in:196
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Limit switcher to current workspace."
+msgstr "گۆڕەڕ سنوردار بکە تەنها بۆ ئەم شوێنکارە"
+
+#: data/org.gnome.shell.gschema.xml.in:197
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "The application icon mode."
+msgstr "دۆخی وێنۆچکەی داوانامە."
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Locations"
+msgstr "شوێنەکان"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "The locations to show in world clocks"
+msgstr "ئەو شوێنانەی پیشان بدرێت لە کاتژمێری جیهانی"
+
+#: data/org.gnome.shell.gschema.xml.in:245
+msgid "Automatic location"
+msgstr "شوێنی خۆکار"
+
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Whether to fetch the current location or not"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:253
+msgid "Location"
+msgstr "شوێن"
+
+#: data/org.gnome.shell.gschema.xml.in:254
+msgid "The location for which to show a forecast"
+msgstr "شوێنی کوێ بۆ پیشاندانی کەش"
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:267
+#: data/org.gnome.shell.gschema.xml.in:276
+#: data/org.gnome.shell.gschema.xml.in:284
+#: data/org.gnome.shell.gschema.xml.in:292
+#: data/org.gnome.shell.gschema.xml.in:300
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:291
+msgid "Workspaces only on primary monitor"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:299
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "چوونەژوورەوەی ڕایەڵە"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:261 js/extensionPrefs/ui/extensions-window.ui:222
+msgid "Something’s gone wrong"
+msgstr "هەندێک هەڵە ڕوویدا"
+
+#: js/extensionPrefs/main.js:268
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/extensionPrefs/main.js:275
+msgid "Technical Details"
+msgstr "زانیاری تەکنیکی"
+
+#: js/extensionPrefs/main.js:310
+msgid "Copy Error"
+msgstr "هەڵە لەبەربگرەوە"
+
+#: js/extensionPrefs/main.js:337
+msgid "Homepage"
+msgstr "پەڕەی سەرەکی"
+
+#: js/extensionPrefs/main.js:338
+msgid "Visit extension homepage"
+msgstr "سەردانی ماڵپەڕی زیادکراو بکە"
+
+#: js/extensionPrefs/main.js:477
+msgid "No Extensions Installed"
+msgstr ""
+
+#: js/extensionPrefs/main.js:487
+msgid ""
+"Extensions can be installed through Software or <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+msgstr ""
+
+#: js/extensionPrefs/main.js:502
+msgid "Browse in Software"
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:234
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: js/extensionPrefs/main.js:144 js/gdm/authPrompt.js:135
+#: js/ui/audioDeviceSelection.js:57 js/ui/components/networkAgent.js:107
+#: js/ui/components/polkitAgent.js:139 js/ui/endSessionDialog.js:374
+#: js/ui/extensionDownloader.js:177 js/ui/shellMountOperation.js:376
+#: js/ui/shellMountOperation.js:386 js/ui/status/network.js:913
+msgid "Cancel"
+msgstr "پاشگەزبوونەوە"
+
+#: js/gdm/authPrompt.js:158 js/gdm/authPrompt.js:201 js/gdm/authPrompt.js:433
+msgid "Next"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr "کردنەوە"
+
+#: js/gdm/authPrompt.js:199
+msgctxt "button"
+msgid "Sign In"
+msgstr ""
+
+#: js/gdm/loginDialog.js:318
+msgid "Choose Session"
+msgstr "دانیشتن هەڵبژێرە"
+
+#: js/gdm/loginDialog.js:457
+msgid "Not listed?"
+msgstr "لە لیستەکەدا نیە؟"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:912
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr ""
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:884 js/ui/components/networkAgent.js:248
+#: js/ui/components/networkAgent.js:268 js/ui/components/networkAgent.js:286
+msgid "Username: "
+msgstr ""
+
+#: js/gdm/loginDialog.js:1253
+msgid "Login Window"
+msgstr "پەنجەرەی چوونەژوورەوە"
+
+#: js/gdm/util.js:338
+msgid "Authentication error"
+msgstr "هەڵە لە ڕێپێدان"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr ""
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:89
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "بیکوژێنەوە"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:92
+msgid "power off;shutdown;reboot;restart"
+msgstr ""
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:97
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "پەردەی بەکلیلکراو"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:100
+msgid "lock screen"
+msgstr "پەردەی بەکلیلکراو"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:105
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "دەرچوون"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:108
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:113
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "ڕاگرتن"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:116
+msgid "suspend;sleep"
+msgstr "suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:121
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "گۆڕینی بەکارهێنەر"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:124
+msgid "switch user"
+msgstr "گۆڕینی بەکارهێنەر"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:129
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr ""
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;screen;rotation"
+msgstr ""
+
+#: js/misc/util.js:120
+msgid "Command not found"
+msgstr "فرمان نەدۆزرایەوە"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:156
+msgid "Could not parse command:"
+msgstr ""
+
+#: js/misc/util.js:164
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "جێبەجێکردنی “%s” سەرکەوتوو نەبوو:"
+
+#: js/misc/util.js:181
+msgid "Just now"
+msgstr "هەر ئێستا"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d خولەک پێش ئێستا"
+msgstr[1] "%d خولەک پێش ئێستا"
+
+#: js/misc/util.js:187
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d کاژێر پێش ئێستا"
+msgstr[1] "%d کاژێر پێش ئێستا"
+
+#: js/misc/util.js:191
+msgid "Yesterday"
+msgstr "دوێنێ"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d ڕۆژ پێش ئێستا"
+msgstr[1] "%d ڕۆژ پێش ئێستا"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d هەفتە پێش ئێستا"
+msgstr[1] "%d هەفتە پێش ئێستا"
+
+#: js/misc/util.js:201
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d مانگ پێش ئێستا"
+msgstr[1] "%d مانگ پێش ئێستا"
+
+#: js/misc/util.js:204
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d ساڵ پێش ئێستا"
+msgstr[1] "%d ساڵ پێش ئێستا"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:237
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:243
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "دوێنێ, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:249
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:255
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:261
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:266
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:272
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "دوێنێ, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:278
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:284
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:290
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:41
+msgid "Hotspot Login"
+msgstr "چوونەژوورەوەی هۆتسپۆت"
+
+#: js/portalHelper/main.js:87
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:374
+msgid "Deny Access"
+msgstr "چوونەناو رەتبکەرەوە"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:377
+msgid "Grant Access"
+msgstr "چوونەناو بەدەستبهێنە"
+
+#: js/ui/appDisplay.js:906
+msgid "Unnamed Folder"
+msgstr "بوخچەی بێ ناو"
+
+#: js/ui/appDisplay.js:929
+msgid "Frequently used applications will appear here"
+msgstr ""
+
+#: js/ui/appDisplay.js:1064
+msgid "Frequent"
+msgstr "نوێترین"
+
+#: js/ui/appDisplay.js:1071
+msgid "All"
+msgstr "هەمووی"
+
+#: js/ui/appDisplay.js:1789
+msgid "Rename"
+msgstr "ناولێنان"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:2450 js/ui/panel.js:75
+msgid "Open Windows"
+msgstr "پەنجەرەکان بکرەوە"
+
+#: js/ui/appDisplay.js:2470 js/ui/panel.js:82
+msgid "New Window"
+msgstr "پەنجەرەیەکی نوێ"
+
+#: js/ui/appDisplay.js:2481
+msgid "Launch using Dedicated Graphics Card"
+msgstr ""
+
+#: js/ui/appDisplay.js:2509 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "سڕینەوە لە دڵخوازەکان"
+
+#: js/ui/appDisplay.js:2515
+msgid "Add to Favorites"
+msgstr "زیادکردن بۆ دڵخوازەکان"
+
+#: js/ui/appDisplay.js:2525 js/ui/panel.js:93
+msgid "Show Details"
+msgstr "پیشاندانی وردەکارییەکان"
+
+#: js/ui/appFavorites.js:152
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s زیاد کرا بۆ دڵخوازەکان."
+
+#: js/ui/appFavorites.js:185
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s سڕایەوە لە دڵخوازەکان."
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "ئامێری دەنگ دیاریبکە"
+
+#: js/ui/audioDeviceSelection.js:54
+msgid "Sound Settings"
+msgstr "ڕێکخستنەکانی دەنگ"
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headphones"
+msgstr "بیستۆکی گوێ"
+
+#: js/ui/audioDeviceSelection.js:66
+msgid "Headset"
+msgstr "بیستۆک"
+
+#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:269
+msgid "Microphone"
+msgstr "مایکرۆفۆن"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "پاشبنەما بگۆڕە"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "ڕێکخستنی روونما"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "ڕێكخستنەكان"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:41
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "٠٦"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:70
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ی"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:72
+msgctxt "grid monday"
+msgid "M"
+msgstr "د"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:74
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "س"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:76
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "چ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:78
+msgctxt "grid thursday"
+msgid "T"
+msgstr "پ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:80
+msgctxt "grid friday"
+msgid "F"
+msgstr "هـ"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:82
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ش"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:371
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:381
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:440
+msgid "Previous month"
+msgstr "مانگی پێشوو"
+
+#: js/ui/calendar.js:455
+msgid "Next month"
+msgstr "مانگی داهاتوو"
+
+#: js/ui/calendar.js:605
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:661
+msgid "Week %V"
+msgstr "هەفتەی %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:730
+msgctxt "event list time"
+msgid "All Day"
+msgstr "هه‌موو ڕۆژێك"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/calendar.js:867
+msgctxt "calendar heading"
+msgid "%A, %B %-d"
+msgstr "%A, %B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/calendar.js:870
+msgctxt "calendar heading"
+msgid "%A, %B %-d, %Y"
+msgstr "%A, %B %-d, %Y"
+
+#: js/ui/calendar.js:1096
+msgid "No Notifications"
+msgstr "ئاگانامە نیە"
+
+#: js/ui/calendar.js:1099
+msgid "No Events"
+msgstr "ڕووداو نیە"
+
+#: js/ui/calendar.js:1167
+msgid "Clear"
+msgstr "پاککردنەوە"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” وەڵام ناداتەوە."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "دەرچوون بە زەبر"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "چاوەڕێبکە"
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr "وەگەڕخەری دەرەکی پەیوەستکرا"
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr "وەگەڕخەری دەرەکی پەیوەست نەکرا"
+
+#: js/ui/components/automountManager.js:208
+msgid "Unable to unlock volume"
+msgstr ""
+
+#: js/ui/components/automountManager.js:209
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:333
+#, javascript-format
+msgid "Open with %s"
+msgstr "بیکەرەوە لە ڕێگەی %s"
+
+#: js/ui/components/keyring.js:67 js/ui/components/polkitAgent.js:260
+msgid "Password:"
+msgstr "تێپه‌ڕه‌وشه‌:"
+
+#: js/ui/components/keyring.js:100
+msgid "Type again:"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:89
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:101 js/ui/status/network.js:223
+#: js/ui/status/network.js:314 js/ui/status/network.js:916
+msgid "Connect"
+msgstr "پەیوەندی ببەستە"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:217 js/ui/components/networkAgent.js:229
+#: js/ui/components/networkAgent.js:251 js/ui/components/networkAgent.js:270
+#: js/ui/components/networkAgent.js:290 js/ui/components/networkAgent.js:300
+msgid "Password: "
+msgstr ""
+
+#. static WEP
+#: js/ui/components/networkAgent.js:222
+msgid "Key: "
+msgstr ""
+
+#: js/ui/components/networkAgent.js:254 js/ui/components/networkAgent.js:276
+msgid "Private key password: "
+msgstr ""
+
+#: js/ui/components/networkAgent.js:274
+msgid "Identity: "
+msgstr ""
+
+#: js/ui/components/networkAgent.js:288
+msgid "Service: "
+msgstr ""
+
+#: js/ui/components/networkAgent.js:317 js/ui/components/networkAgent.js:692
+msgid "Authentication required by wireless network"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:311 js/ui/components/networkAgent.js:686
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:315 js/ui/components/networkAgent.js:690
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:324
+msgid "Network name: "
+msgstr ""
+
+#: js/ui/components/networkAgent.js:322 js/ui/components/networkAgent.js:694
+msgid "DSL authentication"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:329 js/ui/components/networkAgent.js:699
+msgid "PIN code required"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:330 js/ui/components/networkAgent.js:700
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:338
+msgid "PIN: "
+msgstr ""
+
+#: js/ui/components/networkAgent.js:345 js/ui/components/networkAgent.js:713
+msgid "Mobile broadband network password"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:339 js/ui/components/networkAgent.js:691
+#: js/ui/components/networkAgent.js:695 js/ui/components/networkAgent.js:707
+#: js/ui/components/networkAgent.js:711
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:674 js/ui/status/network.js:1691
+msgid "Network Manager"
+msgstr "ڕێکخەری ڕایەڵە"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "ڕێگەپێدان داواکراوە"
+
+#: js/ui/components/polkitAgent.js:80
+msgid "Administrator"
+msgstr "بەڕێوەبەر"
+
+#: js/ui/components/polkitAgent.js:142
+msgid "Authenticate"
+msgstr "رێگەپێدان"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:254 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "ببورە، ئەوە کاری نەکرد. تکایە هەوڵبدە رەوە"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:787
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr ""
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:177
+msgid "Windows"
+msgstr "پەنجەرەکان"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr "داوانامەکان پیشان بدە"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr ""
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:75
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:82
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#: js/ui/dateMenu.js:161
+msgid "Add world clocks…"
+msgstr "زیادکردنی کاتژمێری جیهانی..."
+
+#: js/ui/dateMenu.js:162
+msgid "World Clocks"
+msgstr "کاتژمێری جیهانی"
+
+#: js/ui/dateMenu.js:276
+msgid "Weather"
+msgstr "که‌ش وهه‌وا"
+
+#: js/ui/dateMenu.js:391
+msgid "Select a location…"
+msgstr "شوێن دیاریبکە..."
+
+#: js/ui/dateMenu.js:404
+msgid "Loading…"
+msgstr "باردەکرێت..."
+
+#: js/ui/dateMenu.js:414
+msgid "Go online for weather information"
+msgstr "بچۆ سەرهێڵ بۆ زانیاری کەش وهەوا"
+
+#: js/ui/dateMenu.js:416
+msgid "Weather information is currently unavailable"
+msgstr "زانیاری کەش وهەوا ئێستا بەردەست نیە"
+
+#: js/ui/endSessionDialog.js:37
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "بچۆ دەرەوە %s"
+
+#: js/ui/endSessionDialog.js:38
+msgctxt "title"
+msgid "Log Out"
+msgstr "دەرچوون"
+
+#: js/ui/endSessionDialog.js:40
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s دەچیتە دەرەوە خۆکارانە پاش %d چرکە."
+msgstr[1] "%s دەچیتە دەرەوە خۆکارانە پاش %d چرکە."
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "دەچیتە دەرەوە خۆکارانە پاش %d چرکە."
+msgstr[1] "دەچیتە دەرەوە خۆکارانە پاش %d چرکە."
+
+#: js/ui/endSessionDialog.js:51
+msgctxt "button"
+msgid "Log Out"
+msgstr "دەرچوون"
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "title"
+msgid "Power Off"
+msgstr "بیکوژێنەوە"
+
+#: js/ui/endSessionDialog.js:57
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "نوێکاری دابمەزرێنە و بیکوژێنەوە"
+
+#: js/ui/endSessionDialog.js:59
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "سیستمەکە خۆکارانە دەکوژێتەوە پاش %d چرکە."
+msgstr[1] "سیستمەکە خۆکارانە دەکوژێتەوە پاش %d چرکە."
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "دامەزراندنی نوێکارییە وەستێنراوەکان"
+
+#: js/ui/endSessionDialog.js:66 js/ui/endSessionDialog.js:82
+msgctxt "button"
+msgid "Restart"
+msgstr "دووبارە پێکردنەوە"
+
+#: js/ui/endSessionDialog.js:68
+msgctxt "button"
+msgid "Power Off"
+msgstr "بیکوژێنەوە"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "title"
+msgid "Restart"
+msgstr "دووبارە پێکردنەوە"
+
+#: js/ui/endSessionDialog.js:76
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "سیستم خۆکارانە پێدەکرێتەوە پاش %d چرکە."
+msgstr[1] "سیستم خۆکارانە پێدەکرێتەوە پاش %d چرکە."
+
+#: js/ui/endSessionDialog.js:89
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "پێکردنەوە و دامەزراندنی نوێکارییەکان"
+
+#: js/ui/endSessionDialog.js:91
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "سیستم خۆکارانە پێدەکرێتەوە و نوێکارییەکان دادەمەزرێنێت پاش %d چرکە."
+msgstr[1] "سیستم خۆکارانە پێدەکرێتەوە و نوێکارییەکان دادەمەزرێنێت پاش %d چرکە."
+
+#: js/ui/endSessionDialog.js:97 js/ui/endSessionDialog.js:116
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "پێکردنەوەی &amp; دامەزراندن"
+
+#: js/ui/endSessionDialog.js:98
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "دامەزراندنی &amp; کوژاندنەوە"
+
+#: js/ui/endSessionDialog.js:99
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "بیکوژێنەوە کاتێک نوێکارییەکان دادەمزرێن"
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "پێکردنەوە و دامەزراندنی بەرزکردنەوە"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:111
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:304
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:321
+msgid "Some applications are busy or have unsaved work."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:328
+msgid "Other users are logged in."
+msgstr ""
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:588
+#, javascript-format
+msgid "%s (remote)"
+msgstr ""
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:591
+#, javascript-format
+msgid "%s (console)"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:25
+#, javascript-format
+msgid "Can't install “%s”:"
+msgstr "ناتوانرێت “%s” دابمەزرێت:"
+
+#: js/ui/extensionDownloader.js:26
+msgid ""
+"This is an extension enabled by your current mode, you can't install "
+"manually any update in that session."
+msgstr ""
+
+#: js/ui/extensionDownloader.js:181
+msgid "Install"
+msgstr "دامەزراندن"
+
+#: js/ui/extensionDownloader.js:188
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "داگرتن و دامەزراندنی “%s” لە extensions.gnome.org؟"
+
+#: js/ui/inhibitShortcutsDialog.js:80
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:81
+msgid "Application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:98
+msgid "Deny"
+msgstr "رەتیبکەرەوە"
+
+#: js/ui/inhibitShortcutsDialog.js:105
+msgid "Allow"
+msgstr "ڕێدان"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:42
+msgid "Sticky Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:44
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:46
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:135
+#: js/ui/status/network.js:1288
+msgid "Turn On"
+msgstr "داگیرساندن"
+
+#: js/ui/kbdA11yDialog.js:63 js/ui/status/bluetooth.js:135
+#: js/ui/status/network.js:131 js/ui/status/network.js:315
+#: js/ui/status/network.js:1288 js/ui/status/network.js:1400
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:108
+msgid "Turn Off"
+msgstr "کوژاندنەوە"
+
+#: js/ui/kbdA11yDialog.js:63
+msgid "Leave Off"
+msgstr ""
+
+#: js/ui/keyboard.js:207
+msgid "Region & Language Settings"
+msgstr "ڕێکخستنی شوێن و زمان"
+
+#: js/ui/lookingGlass.js:665
+msgid "No extensions installed"
+msgstr "هیچ زیادکراوێک دانەمەزراوە"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:720
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: js/ui/lookingGlass.js:726
+msgid "Hide Errors"
+msgstr "هەڵە بشارەوە"
+
+#: js/ui/lookingGlass.js:730 js/ui/lookingGlass.js:795
+msgid "Show Errors"
+msgstr "هەڵەکان پیشان بدە"
+
+#: js/ui/lookingGlass.js:739
+msgid "Enabled"
+msgstr "چالاككراو"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:742 subprojects/gvc/gvc-mixer-control.c:1892
+msgid "Disabled"
+msgstr "ناچالاککراوە"
+
+#: js/ui/lookingGlass.js:744
+msgid "Error"
+msgstr "هەڵە"
+
+#: js/ui/lookingGlass.js:746
+msgid "Out of date"
+msgstr "بەسەرچووە"
+
+#: js/ui/lookingGlass.js:748
+msgid "Downloading"
+msgstr "داگرتن"
+
+#: js/ui/lookingGlass.js:777
+msgid "View Source"
+msgstr "سەرچاوە پیشان بدە"
+
+#: js/ui/lookingGlass.js:786
+msgid "Web Page"
+msgstr "پەڕەی وێب"
+
+#: js/ui/messageTray.js:1554
+msgid "System Information"
+msgstr "زانیاری سیستم"
+
+#: js/ui/mpris.js:199
+msgid "Unknown artist"
+msgstr "هونەرمەندی نەناسراو"
+
+#: js/ui/mpris.js:209
+msgid "Unknown title"
+msgstr "سەردێڕی نەناسراو"
+
+#: js/ui/overview.js:73
+msgid "Undo"
+msgstr "گەڕانەوە"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:86
+msgid "Overview"
+msgstr "پوختە"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:230
+msgid "Type to search…"
+msgstr ""
+
+#: js/ui/padOsd.js:95
+msgid "New shortcut…"
+msgstr "قەدبڕی نوێ..."
+
+#: js/ui/padOsd.js:142
+msgid "Application defined"
+msgstr "داوانامە ناسراوە"
+
+#: js/ui/padOsd.js:143
+msgid "Show on-screen help"
+msgstr ""
+
+#: js/ui/padOsd.js:144
+msgid "Switch monitor"
+msgstr "پیشاندەر بگۆڕە"
+
+#: js/ui/padOsd.js:145
+msgid "Assign keystroke"
+msgstr ""
+
+#: js/ui/padOsd.js:211
+msgid "Done"
+msgstr "تەواو"
+
+#: js/ui/padOsd.js:745
+msgid "Edit…"
+msgstr "دەستکاری..."
+
+#: js/ui/padOsd.js:787 js/ui/padOsd.js:910
+msgid "None"
+msgstr "نییە"
+
+#: js/ui/padOsd.js:863
+msgid "Press a button to configure"
+msgstr "پەنجە بنێ بە دوگمەکە بۆ ڕێکخستن"
+
+#: js/ui/padOsd.js:864
+msgid "Press Esc to exit"
+msgstr "پەنجە بنێ بە Esc بۆ دەرچوون"
+
+#: js/ui/padOsd.js:867
+msgid "Press any key to exit"
+msgstr "پەنجە بنێ هەر کلیلێک بۆ دەرچوون"
+
+#: js/ui/panel.js:109
+msgid "Quit"
+msgstr "دەرچوون"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:434
+msgid "Activities"
+msgstr "چالاکییەکان"
+
+#: js/ui/panel.js:713
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "سیستەم"
+
+#: js/ui/panel.js:826
+msgid "Top Bar"
+msgstr "توڵی سەرەوە"
+
+#: js/ui/runDialog.js:58
+msgid "Enter a Command"
+msgstr ""
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "داخستن"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "پێکردنەوە بەردەست نیە لەسەر وایلاند"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "پێکردنەوە..."
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:81
+msgid "%A, %B %d"
+msgstr ""
+
+#: js/ui/screenShield.js:137
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/screenShield.js:139
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/system.js:103
+msgid "Lock"
+msgstr "کلیلدان"
+
+#: js/ui/screenShield.js:206
+msgid "GNOME needs to lock the screen"
+msgstr "گنۆم پێوسیتت بە کلیلکردنی پەردەیە"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:247 js/ui/screenShield.js:601
+msgid "Unable to lock"
+msgstr "نەتوانرا کلیل بدرێ"
+
+#: js/ui/screenShield.js:248 js/ui/screenShield.js:602
+msgid "Lock was blocked by an application"
+msgstr ""
+
+#: js/ui/search.js:728
+msgid "Searching…"
+msgstr "گەڕان..."
+
+#: js/ui/search.js:730
+msgid "No results."
+msgstr "ئەنجام نیە."
+
+#: js/ui/search.js:856
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d زیاتر"
+msgstr[1] "%d زیاتر"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "لەبەرگرتنەوە"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "لكاندن"
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr "نیشاندانی دەق"
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr "دەق بشارەوە"
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr "قەبارەی شاراوە"
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr "پەنجەرەکانی قەبارەی سیستم"
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr ""
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:237 js/ui/components/networkAgent.js:202
+#: js/ui/components/networkAgent.js:218 js/ui/components/networkAgent.js:242
+#: js/ui/components/networkAgent.js:263 js/ui/components/networkAgent.js:283
+#: js/ui/components/networkAgent.js:293 js/ui/components/polkitAgent.js:277
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "تێپەڕەوشە"
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "تێپەڕەوشەم بیربهێنەوە"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "“%s” بکەرەوە"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:469
+#, javascript-format
+msgid "Unable to start %s"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:471
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr ""
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "دەستپێگەیشتن"
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr "دوورپێش"
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr "خوێنەرەوەی پەردە"
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr "تەختەکلیلی پەردە"
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr "ئاگانامەیی بینەیی"
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:136
+msgid "High Contrast"
+msgstr ""
+
+#: js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr "دەقی گەورە"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "بلووتووس"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:591
+msgid "Bluetooth Settings"
+msgstr "ڕێکخستنەکانی بلووتووس"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:129
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d پەیوەستە"
+msgstr[1] "%d پەیوەستە"
+
+#: js/ui/status/bluetooth.js:131
+msgid "Off"
+msgstr "کوژاوه‌"
+
+#: js/ui/status/bluetooth.js:133
+msgid "On"
+msgstr "هەڵکراوە"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "ڕوونی"
+
+#: js/ui/status/dwellClick.js:13
+msgid "Single Click"
+msgstr "یەک کرتە"
+
+#: js/ui/status/dwellClick.js:18
+msgid "Double Click"
+msgstr "دوو کرتە"
+
+#: js/ui/status/dwellClick.js:23
+msgid "Drag"
+msgstr "ڕاکێشان"
+
+#: js/ui/status/dwellClick.js:28
+msgid "Secondary Click"
+msgstr "کرتەی لاوەکی"
+
+#: js/ui/status/dwellClick.js:37
+msgid "Dwell Click"
+msgstr ""
+
+#: js/ui/status/keyboard.js:826
+msgid "Keyboard"
+msgstr "تەختەکلیل"
+
+#: js/ui/status/keyboard.js:848
+msgid "Show Keyboard Layout"
+msgstr "کڵێسەی تەختەکلیل پیشان بدە"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:174
+msgid "Location Enabled"
+msgstr "شوێن چالاکە"
+
+#: js/ui/status/location.js:66 js/ui/status/location.js:175
+msgid "Disable"
+msgstr "ناچالاککردن"
+
+#: js/ui/status/location.js:67
+msgid "Privacy Settings"
+msgstr "ڕێکخستنەکانی تایبەتمەندی"
+
+#: js/ui/status/location.js:173
+msgid "Location In Use"
+msgstr "شوێن لەکاردایە"
+
+#: js/ui/status/location.js:177
+msgid "Location Disabled"
+msgstr "شوێن ناچالاکە"
+
+#: js/ui/status/location.js:178
+msgid "Enable"
+msgstr "چالاک کردن"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:356
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr ""
+
+#: js/ui/status/location.js:367
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "چوونەناوی شوێن دەتوانرێت بگۆڕدرێت لە بەشی ڕێکخستنەکانی تایبەتمەندی."
+
+#: js/ui/status/network.js:66
+msgid "<unknown>"
+msgstr "<نەزانراو>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:420 js/ui/status/network.js:1317
+#, javascript-format
+msgid "%s Off"
+msgstr "%s کوژاوەتەوە"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:423
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s بەستراوە"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:428
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:431
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s دەرهێڵبوون"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:438 js/ui/status/network.js:1309
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s پەیوەستبوون"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:441
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s پێویستی بە ڕێپێدانە"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:449
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:453
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s بەردەست نیە"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:456
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s پەیوەندیگرتن سەرکەوتوو نەبوو"
+
+#: js/ui/status/network.js:468
+msgid "Wired Settings"
+msgstr "ڕێکخستنەکانی وایەری"
+
+#: js/ui/status/network.js:511
+msgid "Mobile Broadband Settings"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:558 js/ui/status/network.js:1314
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:562
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s ناچالاکە"
+
+#: js/ui/status/network.js:603
+msgid "Connect to Internet"
+msgstr "پەیوەندی بکە بە ئینتەرنێت"
+
+#: js/ui/status/network.js:808
+msgid "Airplane Mode is On"
+msgstr "دۆخی فرۆکە چالاکە"
+
+#: js/ui/status/network.js:809
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "وایفای ناچالاکە کاتێک دۆخی فرۆکە چالاکە."
+
+#: js/ui/status/network.js:810
+msgid "Turn Off Airplane Mode"
+msgstr "دۆخی فڕۆکە بکوژێنەوە"
+
+#: js/ui/status/network.js:819
+msgid "Wi-Fi is Off"
+msgstr "وایفای کوژاوەتەوە"
+
+#: js/ui/status/network.js:820
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "پێویستە وایفای چالاک بێت بۆ پەیوەندیگرتن بە ئینتەرنێتەوە."
+
+#: js/ui/status/network.js:821
+msgid "Turn On Wi-Fi"
+msgstr "وایفای دابگیرسێنە"
+
+#: js/ui/status/network.js:846
+msgid "Wi-Fi Networks"
+msgstr "ڕایەڵەکانی وایفای"
+
+#: js/ui/status/network.js:848
+msgid "Select a network"
+msgstr "ڕایەڵە دیاریبکە"
+
+#: js/ui/status/network.js:880
+msgid "No Networks"
+msgstr "ڕایەڵە نیە"
+
+#: js/ui/status/network.js:901 js/ui/status/rfkill.js:106
+msgid "Use hardware switch to turn off"
+msgstr ""
+
+#: js/ui/status/network.js:1178
+msgid "Select Network"
+msgstr "ڕایەڵە دیاریبکە"
+
+#: js/ui/status/network.js:1184
+msgid "Wi-Fi Settings"
+msgstr "ڕێکخستنەکانی وایفای"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1305
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1320
+#, javascript-format
+msgid "%s Not Connected"
+msgstr ""
+
+#: js/ui/status/network.js:1417
+msgid "connecting…"
+msgstr "پەیوەندی گرتن..."
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1420
+msgid "authentication required"
+msgstr "ڕێپێدان داواکراوە"
+
+#: js/ui/status/network.js:1422
+msgid "connection failed"
+msgstr "پەیوەندی سەرکەوتوو نەبوو"
+
+#: js/ui/status/network.js:1473
+msgid "VPN Settings"
+msgstr "ڕێکخستنی VPN"
+
+#: js/ui/status/network.js:1490
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1500
+msgid "VPN Off"
+msgstr "VPN کارناکات"
+
+#: js/ui/status/network.js:1561 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "ڕێکخستنەکانی ڕایەڵە"
+
+#: js/ui/status/network.js:1590
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s پەیوەستی وایەری"
+msgstr[1] "%s پەیوەستی وایەری"
+
+#: js/ui/status/network.js:1594
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s پەیوەستی وایفای"
+msgstr[1] "%s پەیوەستی وایفای"
+
+#: js/ui/status/network.js:1598
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s پەیوەستی مۆدیۆم"
+msgstr[1] "%s پەیوەستی مۆدیۆم"
+
+#: js/ui/status/network.js:1732
+msgid "Connection failed"
+msgstr "پەیوەندی سەرکەوتوونەبوو"
+
+#: js/ui/status/network.js:1733
+msgid "Activation of network connection failed"
+msgstr ""
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "ڕووناکی شەوانە ناچالاکە"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "ڕووناکی شەوانە چالاکە"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "بەردەوامبوونەوە"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "ناچالاکی بکە تا بەیانی"
+
+#: js/ui/status/power.js:47
+msgid "Power Settings"
+msgstr "ڕێکخستنی توانا"
+
+#: js/ui/status/power.js:63
+msgid "Fully Charged"
+msgstr "بەتواوی بارگاوی بووە"
+
+#: js/ui/status/power.js:69
+msgid "Not Charging"
+msgstr "بارگاوی ناکات"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:72 js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "بەنزیکەیی..."
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d ماوە (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d بۆ پڕبوون (%d %%)"
+
+#: js/ui/status/power.js:139 js/ui/status/power.js:141
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:44
+msgid "Screen is Being Shared"
+msgstr "پەردە هاوبەشیپێکرا"
+
+#: js/ui/status/remoteAccess.js:46
+msgid "Turn off"
+msgstr "بیکوژێنەوە"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "دۆخی فرۆکە هەڵکراوە"
+
+#: js/ui/status/system.js:192
+msgid "Switch User"
+msgstr "گۆڕینی بەکارهێنەر"
+
+#: js/ui/status/system.js:119
+msgid "Log Out"
+msgstr "دەرچوون"
+
+#: js/ui/status/system.js:216
+msgid "Account Settings"
+msgstr ""
+
+#: js/ui/status/system.js:245
+msgid "Orientation Lock"
+msgstr ""
+
+#: js/ui/status/system.js:145
+msgid "Suspend"
+msgstr "ڕاگرتن"
+
+#: js/ui/status/system.js:281
+msgid "Power Off"
+msgstr "بیکوژێنەوە"
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "سەندەربۆڵت"
+
+#: js/ui/status/thunderbolt.js:325
+msgid "Unknown Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:326
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:329
+msgid "Unauthorized Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:330
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:336
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:337
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:150
+msgid "Volume changed"
+msgstr ""
+
+#: js/ui/status/volume.js:221
+msgid "Volume"
+msgstr "پلەی دەنگ"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "ئاوێنە"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "پشاندەرەکان پەیوەستبکە"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "تەنها دەرەکی"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "تەنها ناوەکی"
+
+#: js/ui/unlockDialog.js:558
+msgid "Log in as another user"
+msgstr "بچۆژوورەوە وە بەکارهێنەرێکی تر"
+
+#: js/ui/unlockDialog.js:549
+msgid "Unlock Window"
+msgstr "کردنەوەی پەنجەرە"
+
+#: js/ui/viewSelector.js:181
+msgid "Applications"
+msgstr "داوانامەکان"
+
+#: js/ui/viewSelector.js:185
+msgid "Search"
+msgstr "گەڕان"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” ئامادەیە"
+
+#: js/ui/windowManager.js:54
+msgid "Do you want to keep these display settings?"
+msgstr ""
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:64
+msgid "Revert Settings"
+msgstr "گێڕانەوەی ڕێکخستنەکان"
+
+#: js/ui/windowManager.js:67
+msgid "Keep Changes"
+msgstr "گۆڕانکارییەکان بهێڵەوە"
+
+#: js/ui/windowManager.js:85
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "گۆڕینی ڕێکخستنەکان دەگەڕێنەوە دووای %d چرکە"
+msgstr[1] "گۆڕینی ڕێکخستنەکان دەگەڕێنەوە دووای %d چرکە"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr "بچوککردنەوە"
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr "گەورەنەکردن"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "گەورەکردن"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "جووڵان"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "فراوانکردنەوە"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr ""
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "هەموو کات لەسەرەوە"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr "هەموو لە شوێنکاری بینراو"
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr "ناردن بۆ شوێنکاری چەپ"
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr "ناردن بۆ شوێنکاری ڕاست"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "ناردن بۆ شوێنکاری سەرەوە"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "ناردن بۆ شوێنکاری خوارەوە"
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr "ناردن بۆ پیشاندەری سەرەوە"
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr "ناردن بۆ پیشاندەری خوارەوە"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "ناردن بۆ پیشاندەری چەپ"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "ناردن بۆ پیشاندەری ڕاست"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr ""
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: subprojects/extensions-tool/src/command-create.c:194
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:197
+#: subprojects/extensions-tool/src/main.c:170
+msgid "Name"
+msgstr "ناو"
+
+#: subprojects/extensions-tool/src/command-create.c:208
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: js/extensionPrefs/ui/extension-row.ui:100
+#: subprojects/extensions-tool/src/command-create.c:211
+#: subprojects/extensions-tool/src/main.c:173
+msgid "Description"
+msgstr "كورتەیەک"
+
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:244
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:247
+msgid "NAME"
+msgstr "ناو"
+
+#: subprojects/extensions-tool/src/command-create.c:248
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:250
+msgid "DESCRIPTION"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:252
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:255
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:263
+msgid "Create a new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:280
+#: subprojects/extensions-tool/src/command-list.c:168
+msgid "Unknown arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:289
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:57
+msgid "Disable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:74
+#: subprojects/extensions-tool/src/command-enable.c:74
+#: subprojects/extensions-tool/src/command-info.c:96
+#: subprojects/extensions-tool/src/command-prefs.c:107
+#: subprojects/extensions-tool/src/command-reset.c:74
+#: subprojects/extensions-tool/src/command-uninstall.c:89
+msgid "No UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:79
+#: subprojects/extensions-tool/src/command-enable.c:79
+#: subprojects/extensions-tool/src/command-info.c:101
+#: subprojects/extensions-tool/src/command-prefs.c:112
+#: subprojects/extensions-tool/src/command-reset.c:79
+#: subprojects/extensions-tool/src/command-uninstall.c:94
+msgid "More than one UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-enable.c:57
+msgid "Enable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:79
+msgid "Show extensions info"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:201
+msgid "No extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:207
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:125
+msgid "Show user-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show system-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show enabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show disabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show extensions with preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Print extension details"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:151
+msgid "List installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "پەڕگە"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:500
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:90
+msgid "Opens extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-reset.c:57
+msgid "Reset an extension"
+msgstr "سفرکردنەوەی زیادکراو"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:72
+msgid "Uninstall an extension"
+msgstr "زیادکراو بسڕەوە"
+
+#: subprojects/extensions-tool/src/main.c:176
+msgid "Path"
+msgstr "ڕێڕەو"
+
+#: subprojects/extensions-tool/src/main.c:179
+msgid "URL"
+msgstr "بەستەر"
+
+#: subprojects/extensions-tool/src/main.c:182
+msgid "Original author"
+msgstr "نووسەری سەرەکی"
+
+#: js/extensionPrefs/ui/extension-row.ui:123
+#: subprojects/extensions-tool/src/main.c:185
+msgid "Version"
+msgstr "وەشان"
+
+#: subprojects/extensions-tool/src/main.c:188
+msgid "State"
+msgstr "دۆخ"
+
+#: subprojects/extensions-tool/src/main.c:222
+msgid "“version” takes no arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:224
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Usage:"
+msgstr "بەکارهێنان:"
+
+#: subprojects/extensions-tool/src/main.c:227
+msgid "Print version information and exit."
+msgstr "چاپکردنی وەشانی زانیاری بچۆ دەرەوە."
+
+#: subprojects/extensions-tool/src/main.c:242
+#: subprojects/extensions-tool/src/main.c:245
+msgid "COMMAND"
+msgstr "فرمان"
+
+#: subprojects/extensions-tool/src/main.c:245
+msgid "[ARGS…]"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "Commands:"
+msgstr "فرمانەکان:"
+
+#: subprojects/extensions-tool/src/main.c:248
+msgid "Print help"
+msgstr "یارمەتی چاپکردن"
+
+#: src/main.c:538 subprojects/extensions-tool/src/main.c:249
+msgid "Print version"
+msgstr "وه‌شان چاپبکه‌"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Enable extension"
+msgstr "زیادکراو چالاک بکە"
+
+#: subprojects/extensions-tool/src/main.c:251
+msgid "Disable extension"
+msgstr "زیادکراو ناچالاک بکە"
+
+#: subprojects/extensions-tool/src/main.c:252
+msgid "Reset extension"
+msgstr "نوێکردنەوەی زیادکراو"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Uninstall extension"
+msgstr "سڕینەوەی زیادکراو"
+
+#: subprojects/extensions-tool/src/main.c:254
+msgid "List extensions"
+msgstr "لیستەکردنی زیادکراوەکان"
+
+#: subprojects/extensions-tool/src/main.c:255
+#: subprojects/extensions-tool/src/main.c:256
+msgid "Show extension info"
+msgstr "زانیارییەکانی زیادکراو پیشان بدە"
+
+#: subprojects/extensions-tool/src/main.c:257
+msgid "Open extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:258
+msgid "Create extension"
+msgstr "دروستکردنی زیادکراو"
+
+#: subprojects/extensions-tool/src/main.c:259
+msgid "Package extension"
+msgstr "زیادکراوەکانی گورزە"
+
+#: subprojects/extensions-tool/src/main.c:260
+msgid "Install extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:262
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr ""
+
+#: src/main.c:544
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: src/main.c:550
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+
+#: src/main.c:556
+msgid "List possible modes"
+msgstr "دۆخە گونجاوەکان لیست بکە"
+
+#: src/shell-app.c:279
+msgctxt "program"
+msgid "Unknown"
+msgstr "نەناسراو"
+
+#: src/shell-app.c:530
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr ""
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "تێپەڕوشە وەک یەک نین."
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "تێپەڕوشە نابێ بەتاڵ بێت"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ڕێگەپێدان لەکارخرا لە لایەن بەکارهینەرەوە"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1899
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1909
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/gvc/gvc-mixer-control.c:2766
+msgid "System Sounds"
+msgstr "دەنگەکانی سیستم"
diff --git a/po/cs.po b/po/cs.po
new file mode 100644
index 0000000..a036f8a
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,3008 @@
+# Czech translation of gnome-shell.
+# Copyright (C) 2009, 2010, 2011 the author(s) of gnome-shell.
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Andre Klapper <ak-47@gmx.net>, 2009.
+# Petr Kovar <pknbe@volny.cz>, 2009, 2010, 2011, 2012, 2014, 2015, 2020.
+# Adam Matoušek <adamatousek@gmail.com>, 2012, 2013.
+# Marek Černocký <marek@manet.cz>, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022.
+# Vojtěch Perník <translations@pervoj.cz>, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-08-23 12:52+0000\n"
+"PO-Revision-Date: 2023-09-08 16:52+0200\n"
+"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
+"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Poedit 3.3.2\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Spouštěče"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktivovat oblíbenou aplikaci č. 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktivovat oblíbenou aplikaci č. 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktivovat oblíbenou aplikaci č. 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktivovat oblíbenou aplikaci č. 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktivovat oblíbenou aplikaci č. 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktivovat oblíbenou aplikaci č. 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktivovat oblíbenou aplikaci č. 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktivovat oblíbenou aplikaci č. 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktivovat oblíbenou aplikaci č. 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2076
+msgid "Screenshots"
+msgstr "Snímky obrazovky"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Pořídit snímek obrazovky interaktivně"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Pořídit snímek obrazovky"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Pořídit snímek okna"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Zaznamenat dění na obrazovce interaktivně"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Systém"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Zobrazit seznam upozornění"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Zaměřovat aktivní upozornění"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Zobrazit přehled"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Zobrazit všechny aplikace"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Otevřít nabídku aplikací"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Správa oken a spouštění aplikací"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Povolit přes Alt-F2 vnitřní nástroje určené pro vývojáře a testery"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Poskytuje přístup k vnitřním ladicím a monitorovacím nástrojům pomocí okna "
+"ovládaného přes Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID rozšíření, která se mají zapnout"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Rozšíření GNOME Shell mají vlastnost UUID; tento klíč je seznamem rozšíření, "
+"která by měla být načítána. Každé rozšíření, které má být načítáno, musí být "
+"na tomto seznamu. S tímto seznamem můžete pracovat také pomocí metod "
+"EnableExtension a DisableExtension sběrnice DBus na org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID rozšíření, která se mají nuceně vypnout"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Rozšíření GNOME Shell mají vlastnost UUID; tento klíč je seznamem rozšíření, "
+"která by měla být vypnutá, i když se načtou jako součást aktuálního režimu. "
+"S tímto seznamem můžete pracovat také pomocí metod EnableExtension a "
+"DisableExtension sběrnice DBus na org.gnome.Shell. Tento klíč má přednost "
+"před nastavením „enabled-extensions“."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Zakázat uživatelská rozšíření"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Zakázat všechna rozšíření, která uživatel zapnul, bez vlivu na nastavení "
+"„enabled-extension“."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Vypíná kontrolu kompatibility verze rozšíření"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell načítá pouze rozšíření, která propagují podporu právě běžící "
+"verze. Zapnutím této volby zakážete tuto kontrolu a rozšíření se budou "
+"zkoušet načíst bez ohledu na to, jakou verzi podporují."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Seznam ID souborů desktop oblíbených aplikací"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Aplikace odpovídající těmto identifikátorům budou zobrazeny oblasti "
+"oblíbených."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historie příkazového dialogového okna (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historie dialogového okna „looking glass“ (Alt-F2)"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "V nabídce uživatele vždy zobrazovat položku „Odhlásit“."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Tento klíč ruší automatické skrývání položky nabídky „Odhlásit“ v situacích "
+"s jedním uživatelem a jedním sezením."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Zdali si pamatovat heslo k připojování šifrovaných či vzdálených souborových "
+"systémů"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Shell požaduje heslo při připojování šifrovaných či vzdálených souborových "
+"systémů. Může-li být heslo uloženo pro budoucí použití, zobrazí se "
+"zaškrtávací pole „Pamatovat si heslo“. Tento klíč určuje výchozí stav této "
+"volby."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Poslední vybraný profil napájení, jiný než výchozí"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Některé systémy podporují více než dva profily napájení. Aby bylo možné i "
+"nadále podporovat přepínání mezi dvěma profily, uchovává tento klíč poslední "
+"vybraný profil, jiný než výchozí."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Poslední verze „Vítá vás GNOME“ nabídnutá v dialogovém okně"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Klíč určuje, pro kterou verzi „Vítá vás GNOME“ bylo naposledy zobrazeno "
+"dialogové okno. Prázdný řetězec představuje nejstarší možnou verzi a velmi "
+"vysoké číslo představuje verzi, které doposud neexistuje. Takovýmto číslem "
+"se dá dialogové okno jednoduše zakázat."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Rozvržení výběru aplikace"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Rozvržení výběru aplikace. Jednotlivé položky pole představují stránky. "
+"Stránky jsou v pořadí, v jakém se objeví v GNOME Shellu. Každá stránka "
+"obsahuje pár „id aplikace“ → „data“. V současnosti se jako „data“ ukládají "
+"následující hodnoty: • „pozice“: pozice ikony aplikace na stránce."
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Klávesová zkratka otevírající nabidku aplikací"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Klávesová zkratka sloužící k otevření nabídky aplikací."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Klávesová zkratka přesouvající mezi stavy přehledu"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Klávesová zkratka přesouvající mezi sezením, výběrem oken a mřížkou aplikací"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Klávesová zkratka přesouvající mezi mřížkou aplikací, výběrem oken a sezením"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Klávesová zkratka otevírající zobrazení aplikací"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Klávesová zkratka sloužící k otevření seznamu aplikací v Přehledu činností."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Klávesová zkratka otevírající přehled"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Klávesová zkratka sloužící k otevření přehledu činností."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Klávesová zkratka přepínající viditelnost seznamu upozornění"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Klávesová zkratka sloužící k přepnutí viditelnosti seznamu upozornění."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Klávesová zkratka zaměřující aktivního upozornění"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Klávesová zkratka sloužící k zaměření aktivního upozornění."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Přepnout do aplikace 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Přepnout do aplikace 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Přepnout do aplikace 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Přepnout do aplikace 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Přepnout do aplikace 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Přepnout do aplikace 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Přepnout do aplikace 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Přepnout do aplikace 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Přepnout do aplikace 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Omezit přepínání jen na aktuální plochu."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Je-li zapnuto, objeví se v přepínači aplikací jen ty, co jsou na aktuální "
+"pracovní ploše. V opačném případě jsou zahrnuty všechny aplikace."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Režim ikon aplikací."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Nastavuje, jak jsou okna zobrazena v přepínači. Možnostmi jsou „thumbnail-"
+"only“ (zobrazí náhled okna), „app-icon-only“ (zobrazí pouze ikonu aplikace) "
+"a „both“ (zobrazí náhled i ikonu)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Je-li zapnuto, objeví se v přepínači oken jen ty, co jsou na aktuální "
+"pracovní ploše. V opačném případě jsou zahrnuta všechna okna."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Místa"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Místa, která se mají zobrazovat ve světových časech"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatické místo"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Zda si zjistit, či ne, aktuální místo"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Místo"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Místo, pro které se má zobrazovat předpověď počasí"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Modální dialogová okna připojovat k rodičovskému oknu"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "Když běží GNOME Shell, tento klíč přepíše klíč v org.gnome.mutter."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Okna upuštěná u okraje obrazovky nechat řadit jako dlaždice"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Pracovní plochy jsou spravovány dynamicky"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Pracovní plochy jen na hlavním monitoru"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Se změnou zaměření v režimu myši čekat na zastavení pohybu ukazatele"
+
+#. Keep in sync with subprojects/extensions-app
+#: data/org.gnome.Shell.Extensions.desktop.in.in:5
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Rozšíření"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Přihlášení do sítě"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Něco se stalo špatně"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Omlouváme se, ale vyskytl se problém: nastavení pro toto rozšíření nelze "
+"zobrazit. Doporučujeme problém nahlásit autorovi rozšíření."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Technické podrobnosti"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Domovská stránka"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Navštívit domovskou stránku rozšíření"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Zrušit"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Heslo"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Vybrat sezení"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Nejste na seznamu?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(např. uživatel nebo %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Uživatelské jméno"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Přihlašovací okno"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Chyba ověření"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(nebo přejeďte prstem přes čtečku)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(nebo přiložte prst na čtečku)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Vypnout"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "vypnout;vypnutí;zastavit;stopnout;"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Restartovat"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr ""
+"restart;restartování;restartovat;reboot;znovu zavést systém;zavedení systému;"
+"natažení systému;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Zamknout obrazovku"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr ""
+"zamknout obrazovku;zamknout počítač;uzamknout obrazovku;uzamknout počítač;"
+"zamčít obrazovku;zamčít počítač;uzamčít obrazovku;uzamčít počítač;zamknutí "
+"obrazovky;zamknutí počítače;uzamknutí obrazovky;uzamknutí počítače;zamčení "
+"obrazovky;zamčení počítače;uzamčení obrazovky;uzamčení počítače;"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Odhlásit se"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "odhlásit se;odhlášení;odlogovat se;odlogování;"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Uspat do paměti"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "uspat do paměti;režim spánku;"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Přepnout uživatele"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "přepnout uživatele;přepnutí uživatele;"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"zamknout otočení;zamknout orientaci;uzamknout otočení;uzamknout orientaci;"
+"zamčít otočení;zamčít orientaci;uzamčít otočení;uzamčít orientaci;zamknutí "
+"otočení;zamknutí orientace;uzamknutí otočení;uzamknutí orientace;zamčení "
+"otočení;zamčení orientace;uzamčení otočení;uzamčení orientace;odemknout "
+"otočení;odemknout orientaci;odemčít otočení;odemčít orientaci;odemknutí "
+"otočení;odemknutí orientace;odemčení otočení;odemčení orientace;obrazovka;"
+"otočení;orientace;"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Pořídit snímek obrazovky"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"snímek obrazovky;záznam obrazovky;nahrávání obrazovky;zachytit;sejmout;"
+"nahrát;zaznamenat;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Odemknout otočení obrazovky"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Zamknout otočení obrazovky"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Příkaz nenalezen"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Nelze analyzovat příkaz:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Vykonání „%s“ selhalo:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "právě teď"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "před minutou"
+msgstr[1] "před %d minutami"
+msgstr[2] "před %d minutami"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "před hodinou"
+msgstr[1] "před %d hodinami"
+msgstr[2] "před %d hodinami"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "včera"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "před %d dnem"
+msgstr[1] "před %d dny"
+msgstr[2] "před %d dny"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "před týdnem"
+msgstr[1] "před %d týdny"
+msgstr[2] "před %d týdny"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "před měsícem"
+msgstr[1] "před %d měsíci"
+msgstr[2] "před %d měsíci"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "před rokem"
+msgstr[1] "před %d roky"
+msgstr[2] "před %d lety"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%k∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "včera, %k∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A %k∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-e. %B %k∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-e. %B %Y, %k∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "včera %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-e. %B %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-e. %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Přihlášení k přístupovému bodu"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Vaše připojení k tomuto přístupovému bodu není bezpečné. Hesla a další "
+"informace, které zadáte na této stránce mohou vidět ostatní lidé v okolí."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Zamítnout přístup"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Schválit přístup"
+
+#: js/ui/appDisplay.js:1732
+msgid "Unnamed Folder"
+msgstr "Nepojmenovaná složka"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "Aplikace %s byla přidána mezi oblíbené."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Aplikace %s byla odebrána z oblíbených."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Otevřená okna"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nové okno"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Zobrazit podrobnosti"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Ukončit"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:250
+msgid "Unpin"
+msgstr "Odebrat z oblíbených"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Přidat mezi oblíbené"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Spustit pomocí integrované grafické karty"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Spustit pomocí samostatné grafické karty"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Výběr zvukového zařízení"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Nastavení zvuku"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Sluchátka"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Sluchátka s mikrofonem"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Změnit pozadí…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Nastavení displeje"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Nastavení"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Ne"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Po"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Út"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "St"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Čt"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Pá"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "So"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Předchozí měsíc"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Následující měsíc"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%-e"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V. týden"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Nejsou žádná upozornění"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Nevyrušovat"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Vymazat"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "Aplikace „%s“ neodpovídá."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Můžete si vybrat, jestli chvilku počkat, zda bude normálně pokračovat, nebo "
+"ji násilně celou ukončit."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Vynutit ukončení"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Počkat"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Externí svazek připojen"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Externí svazek odpojen"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Nelze uzamknout svazek"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Nainstalovaná verze udisks nepodporuje nastavení PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Otevřít pomocí %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Případně se můžete připojit zmáčknutím tlačítka „WPS“ na směrovači."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Připojit"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Klíč"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Heslo soukromého klíče"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identita"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Služba"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Vyžaduje ověření"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Pro přístup k bezdrátové síti „%s“ jsou vyžadována hesla nebo šifrovací "
+"klíče."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Ověření připojení po drátu 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Název sítě"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Ověření DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Požadován kód PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Pro mobilní širokopásmové zařízení je vyžadován kód PIN"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Pro připojení k „%s“ je vyžadováno heslo."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1960
+msgid "Network Manager"
+msgstr "Network Manager"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Heslo k VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Vyžaduje ověření"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Správce"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Ověřit"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Ověření bohužel nebylo úspěšné. Zkuste to prosím znovu."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s je teď znám jako %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Okna"
+
+#: js/ui/dash.js:206 js/ui/dash.js:252
+msgid "Show Applications"
+msgstr "Zobrazit aplikace"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:399
+msgid "Dash"
+msgstr "Oblíbené"
+
+# Not sure whether we've enough space for it, but anyway, looks more aesthetically with "%A".
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-e. %B %Y"
+
+# Not sure whether we've enough space for it, but anyway, looks more aesthetically with "%A".
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %-e. %B %Y"
+
+# Not sure whether we've enough space for it, but anyway, looks more aesthetically with "%A".
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-e. %B"
+
+# Not sure whether we've enough space for it, but anyway, looks more aesthetically with "%A".
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-e. %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Dnes"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Zítra"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Celý den"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%-e.%-m."
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nejsou žádné události"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Přidat světový čas…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Světové hodiny"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Načítá se…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Připojit se kvůli informacím o počasí"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Informace o počasí nejsou nyní dostupné"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Počasí"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Vybrat místo pro počasí…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Odhlásit uživatele %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Odhlášení"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Uživatel %s bude automaticky odhlášen za %d sekundu."
+msgstr[1] "Uživatel %s bude automaticky odhlášen za %d sekundy."
+msgstr[2] "Uživatel %s bude automaticky odhlášen za %d sekund."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Budete automaticky odhlášeni za %d sekundu."
+msgstr[1] "Budete automaticky odhlášeni za %d sekundy."
+msgstr[2] "Budete automaticky odhlášeni za %d sekund."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Odhlásit"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Vypnutí"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Nainstalování aktualizací a vypnutí"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Systém bude automaticky vypnut za %d sekundu."
+msgstr[1] "Systém bude automaticky vypnut za %d sekundy."
+msgstr[2] "Systém bude automaticky vypnut za %d sekund."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Nainstalovat čekající aktualizace softwaru"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Vypnout"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Restart"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Nainstalování aktualizací a restart"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Systém bude automaticky restartován za %d sekundu."
+msgstr[1] "Systém bude automaticky restartován za %d sekundy."
+msgstr[2] "Systém bude automaticky restartován za %d sekund."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Restartovat"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Restart a nainstalování aktualizací"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Systém bude automaticky restartován za %d sekundu a poté se nainstalují "
+"aktualizace."
+msgstr[1] ""
+"Systém bude automaticky restartován za %d sekundy a poté se nainstalují "
+"aktualizace."
+msgstr[2] ""
+"Systém bude automaticky restartován za %d sekund a poté se nainstalují "
+"aktualizace."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Restartovat a nainstalovat"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Nainstalovat a vypnout"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Po nainstalování aktualizací vypnout"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Restart a nainstalování povýšení"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Verze %2s distribuce %1s bude nainstalována po restartu. Instalace povýšení "
+"může zabrat hodně času: zkontrolujte, že máte vše zazálohováno a že počítač "
+"je připojen do elektriky."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Nízký stav baterie: před instalací prosím připojte k napájení."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Některé aplikace jsou zaneprázdněné nebo obsahují neuloženou práci"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Jsou přihlášeni jiní uživatelé"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Volby zavedení systému"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (vzdálený)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konzola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Nainstalovat"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalace rozšíření"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Stáhnout a nainstalovat „%s“ z extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Jsou dostupné aktualizace rozšíření"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Aktualizace rozšíření jsou připravené k instalaci."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Povolení znemožnit klávesové zkratky"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Aplikace %s chce znemožnit klávesové zkratky"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Některá aplikace chce znemožnit klávesové zkratky"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Obnovit klávesové zkratky můžete zmáčknutím %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Zamítnout"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Povolit"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Pomalé klávesy zapnuty"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Pomalé klávesy vypnuty"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Stačí podržet klávesu Shift po dobu 8 sekund. Jedná se o klávesovou zkratku "
+"pro funkci Pomalé klávesy, která ovlivňuje způsob, jakým funguje vaše "
+"klávesnice."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Kombinace kláves jedním prstem zapnuty"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Kombinace kláves jedním prstem vypnuty"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Stačí zmáčknout Shift pětkrát po sobě. Jedná se o klávesovou zkratku pro "
+"funkci Kombinace kláves jedním prstem, která ovlivňuje způsob, jakým funguje "
+"vaše klávesnice."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Stačí zmáčknout dvě klávesy naráz, nebo Shift pětkrát po sobě. Tím se vypne "
+"funkce Kombinace kláves jedním prstem, která ovlivňuje způsob, jakým funguje "
+"vaše klávesnice."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Ponechat zapnuté"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Zapnout"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Vypnout"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Ponechat vypnuté"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Nastavení regionu a jazyka"
+
+#: js/ui/lookingGlass.js:718
+msgid "No extensions installed"
+msgstr "Nejsou nainstalována žádná rozšíření"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:779
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "Rozšíření %s nevyvolalo žádné chyby."
+
+#: js/ui/lookingGlass.js:785
+msgid "Hide Errors"
+msgstr "Skrývat chyby"
+
+#: js/ui/lookingGlass.js:789 js/ui/lookingGlass.js:862
+msgid "Show Errors"
+msgstr "Zobrazovat chyby"
+
+#: js/ui/lookingGlass.js:798
+msgid "Enabled"
+msgstr "Povoleno"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:801 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Zakázáno"
+
+#: js/ui/lookingGlass.js:803
+msgid "Error"
+msgstr "Chyba"
+
+#: js/ui/lookingGlass.js:805
+msgid "Out of date"
+msgstr "Neaktuální"
+
+#: js/ui/lookingGlass.js:807
+msgid "Downloading"
+msgstr "Stahování"
+
+#: js/ui/lookingGlass.js:840
+msgid "View Source"
+msgstr "Zobrazit zdroj"
+
+#: js/ui/lookingGlass.js:851
+msgid "Web Page"
+msgstr "Webová stránka"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Systém byl přepnut do nebezpečného režimu"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Aplikace mají nyní neomezený přístup"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Zpět"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Přihlášeni jako privilegovaný uživatel"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Spouštění sezení pod privilegovaným uživatelem byste se měli z "
+"bezpečnostních důvodů vyhýbat. Pokud je to možné, přihlaste se jako běžný "
+"uživatel."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Zamykání obrazovky je vypnuto"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Zamykání obrazovky vyžaduje správce displeje GNOME."
+
+#: js/ui/messageTray.js:1417
+msgid "System Information"
+msgstr "Informace o systému"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Neznámý umělec"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Neznámý název"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "vyhledávejte psaním"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Aplikace"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Přehled"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nová klávesová zkratka…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Definováno aplikací"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Zobrazit nápovědu na obrazovce"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Přepnout monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Přířadit klávesu"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Hotovo"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Upravit…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Źádná"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Zmáčkněte tlačítko pro nastavení"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Zmáčknutím Esc ukončíte"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Zmáčknutím klávesy ukončíte"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Činnosti"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Systém"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Horní lišta"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Spuštění příkazu"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Zmáčknutím Esc zavřete"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Restart není na Waylandu k dispozici"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Restartuje se…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME potřebuje uzamknout obrazovku"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Nelze uzamknout obrazovku"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Zamknutí bylo zablokováno některou z aplikací"
+
+#: js/ui/screenshot.js:1165
+msgid "Selection"
+msgstr "Výběr"
+
+#: js/ui/screenshot.js:1175
+msgid "Area Selection"
+msgstr "Výběr oblasti"
+
+#: js/ui/screenshot.js:1180
+msgid "Screen"
+msgstr "Obrazovka"
+
+#: js/ui/screenshot.js:1190
+msgid "Screen Selection"
+msgstr "Výběr obrazovky"
+
+#: js/ui/screenshot.js:1195
+msgid "Window"
+msgstr "Okno"
+
+#: js/ui/screenshot.js:1205
+msgid "Window Selection"
+msgstr "Výběr okna"
+
+#: js/ui/screenshot.js:1243
+msgid "Screenshot / Screencast"
+msgstr "Snímek/záznam obrazovky"
+
+#: js/ui/screenshot.js:1279
+msgid "Show Pointer"
+msgstr "Zobrazit ukazatel"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1870
+msgid "Screencasts"
+msgstr "Záznamy obrazovky"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1875
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Záznam obrazovky z %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1917 js/ui/screenshot.js:2129
+msgid "Screenshot"
+msgstr "Snímek obrazovky"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1923
+msgid "Screencast recorded"
+msgstr "Bylo zaznamenáno dění na obrazovce"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1925
+msgid "Click here to view the video."
+msgstr "Kliknutím zde video zobrazíte."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1928 js/ui/screenshot.js:2143
+msgid "Show in Files"
+msgstr "Zobrazit v Souborech"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2089
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Snímek obrazovky z %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2135
+msgid "Screenshot captured"
+msgstr "Byl pořízen snímek obrazovky"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2137
+msgid "You can paste the image from the clipboard."
+msgstr "Obrázek můžete vložit ze schránky."
+
+#: js/ui/screenshot.js:2190 js/ui/screenshot.js:2355
+msgid "Screenshot taken"
+msgstr "Byl pořízen snímek obrazovky"
+
+#: js/ui/search.js:809
+msgid "Searching…"
+msgstr "Hledá se…"
+
+#: js/ui/search.js:811
+msgid "No results."
+msgstr "Žádné výsledky."
+
+#: js/ui/search.js:942
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d další"
+msgstr[1] "%d další"
+msgstr[2] "%d dalších"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Hledat"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopírovat"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Vložit"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Zobrazit text"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Skrýt text"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Je zapnutý Caps Lock."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Skrytý svazek"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Systémový svazek Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Používat soubory s klíči"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"K odemknutí svazku, který používá soubory s klíči, místo toho použijte "
+"pomůcku <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Číslo PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Pamatovat si heslo"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Odemknout"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Otevřít %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM musí být číslo nebo prázdné."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Nelze spustit %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nezdařilo se najít aplikaci %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Zpřístupnění"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Vysoký kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zvětšení"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Čtečka obrazovky"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Klávesnice na obrazovce"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Vizuální upozornění"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Lepící klávesy"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Pomalé klávesy"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Vícenásobné stisky kláves"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Myš klávesnicí"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Styl velkého textu"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automaticky otáčet"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Jas"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Tmavý režim"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Jednoduché kliknutí"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dvojité kliknutí"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Tažení"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Druhé kliknutí"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Kliknutí posečkáním"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Klávesnice"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Zobrazit rozložení klávesnice"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Povolení přístupu k informacím o poloze"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Aplikace %s chce přístup k informacím vaší poloze"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Přístup ke službám pro určování polohy lze kdykoliv změnit v nastavení "
+"soukromí."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<neznámé>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Odpojit %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Připojit %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1113
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Přístupový bod %s"
+
+#: js/ui/status/network.js:1472 js/ui/status/network.js:1488
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1473
+msgid "VPN Settings"
+msgstr "Nastavení VPN"
+
+#: js/ui/status/network.js:1722
+msgid "Wi–Fi"
+msgstr "Wi-Fi"
+
+#: js/ui/status/network.js:1724
+msgid "All Networks"
+msgstr "Všechny sítě"
+
+#: js/ui/status/network.js:1821
+msgid "Wired Connections"
+msgstr "Drátová připojení"
+
+#: js/ui/status/network.js:1822
+msgid "Wired Settings"
+msgstr "Nastavení drátových připojení"
+
+#: js/ui/status/network.js:1836
+msgid "Bluetooth Tethers"
+msgstr "Sdílená připojení přes Bluetooth"
+
+#: js/ui/status/network.js:1837
+msgid "Bluetooth Settings"
+msgstr "Nastavení Bluetooth"
+
+#: js/ui/status/network.js:1851
+msgid "Mobile Connections"
+msgstr "Mobilní připojení"
+
+#: js/ui/status/network.js:1853
+msgid "Mobile Broadband Settings"
+msgstr "Nastavení mobilního připojení"
+
+#: js/ui/status/network.js:1965
+msgid "Connection failed"
+msgstr "Připojení selhalo"
+
+#: js/ui/status/network.js:1966
+msgid "Activation of network connection failed"
+msgstr "Aktivace síťového připojení selhala"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Noční osvětlení"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Vysoký výkon"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Rovnováha"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Šetření energií"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Profily napájení"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Zastavit záznam obrazovky"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Zastavit sdílení obrazovky"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Režim „letadlo“"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Pořídit snímek obrazovky"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Nabídka vypnutí"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Uspat do paměti"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Restartovat…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Vypnout…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Odhlásit se…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Přepnout uživatele…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Uzamknout obrazovku"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Neznámé zařízení Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Zatímco jste byli pryč, bylo nalezeno nové zařízení. Odpojte jej prosím a "
+"znovu připojte, abyste jej mohli používat."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Neověřené zařízení Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Bylo zjištěno nové zařízení a je třeba jej ověřit správcem."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Chyba ověření Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nezdařilo se provést ověření zařízení Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Hlasitost změněna"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Hlasitost"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Zvukový výstup"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Zvukový vstup"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Duplikovat"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Sloučit displeje"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Jen externí"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Jen vestavěné"
+
+# Not sure whether we've enough space for it, but anyway, looks more aesthetically with "%A".
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-e. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Pro odemčení přejeďte prstem nahoru"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Pro odemčení klikněte nebo stiskněte klávesu"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Odemykací okno"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Přihlásit se jako jiný uživatel"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Vítá vás GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Jestli se potřebujete zorientovat, nabízíme vám úvodní prohlídku."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ne, děkuji"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Začít prohlídku"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Připraveno „%s“"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Zachovat toto nastavení displeje?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Obnovit původní"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Zachovat"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Nastavení se obnoví na původní za %d sekundu"
+msgstr[1] "Nastavení se obnoví na původní za %d sekundy"
+msgstr[2] "Nastavení se obnoví na původní za %d sekund"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Skrýt"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Obnovit"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximalizovat"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Přesunout"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Změnit velikost"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Posunout záhlaví na obrazovku"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Vždy nahoře"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Vždy na viditelné ploše"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Přesunout o plochu doleva"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Přesunout o plochu doprava"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Přesunout o plochu výš"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Přesunout o plochu níž"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Přesunout o monitor výš"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Přesunout o monitor níž"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Přesunout o monitor doleva"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Přesunout o monitor doprava"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Zavřít"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Kalendář Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Vypsat verzi"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Režim použitý GDM pro přihlašovací obrazovku"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Použít pro přihlašovací obrazovku zadaný režim, např. „gdm“"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Vypsat možné režimy"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Neznámé"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Nelze spustit „%s“"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Hesla si neodpovídají."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Heslo nemůže být prázdné."
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Dialogové okno ověření bylo uživatelem zrušeno"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Správa vašich rozšíření GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Projekt GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Aplikace Rozšíření GNOME umožňuje aktualizaci a nastavení předvoleb "
+"rozšíření a odstranění nebo vypnutí nechtěných rozšíření."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Nastavit rozšíření pro GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Žádná shoda"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Odebrat „%s“?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Pokud rozšíření odeberete a budete jej chtít znovu povolit, budete se muset "
+"vrátit k jeho stažení."
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Odebrat"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Marek Černocký <marek@manet.cz>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Při příštím přihlášení bude aktualizováno %d rozšíření."
+msgstr[1] "Při příštím přihlášení budou aktualizována %d rozšíření."
+msgstr[2] "Při příštím přihlášení bude aktualizováno %d rozšíření."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Rozšíření není kompatibilní s aktuální verzí GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "V rozšíření se vyskytla chyba"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Rozšíření lze aktualizovat"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Webové stránky"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Odebrat…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Nápověda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "O aplikaci Rozšíření"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Rozšíření mohou způsobit problémy s výkonem a stabilitou. Pokud ve svém "
+"systému narazíte na podobné problémy, rošíření zakažte."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Ručně nainstalované"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Jestli chcete vyhledat a nainstalovat rozšíření, navštivte stránku <a "
+"href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Vestavěné"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Není nainstalované žádné rozšíření"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Bohužel, ale není možné získat seznam nainstalovaných rozšíření. Ujistěte "
+"se, že jste přihlášení do GNOME, a zkuste to znovu."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Jsou připravené aktualizace rozšíření"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "V %s bylo úspěšně vytvořeno nové rozšíření.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Název by měl být velmi krátký (nejlépe samopopisný) řetězec.\n"
+"Příklady: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Název"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Popis by měl jednou větou vysvětlit, co vaše rozšíření vlastně dělá.\n"
+"Příklady: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Popis"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID je globálně jedinečnou identifikací vašeho rozšíření.\n"
+"Měl by být ve formátu e-mailové adresy (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Zvolte jednu z dostupných šablon:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Šablona"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Jednoznačná identifikace nového rozšíření"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NÁZEV"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Název nového rozšíření, jak jej uvidí uživatel"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "POPIS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Krátký popis toho, co rozšíření vlastně dělá"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ŠABLONA"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Šablona, která se má použít pro nové rozšíření"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Zadat informace o rozšíření interaktivně"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Vytvořit nové rozšíření"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Neznámý argument"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Jsou vyžadovány UUID, název a popis"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Rozšíření „%s“ neexistuje\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Zakázat rozšíření"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nebylo zadáno žádné UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Bylo zadáno více než jedno UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Povolit rozšíření"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Selhalo připojení ke GNOME Shellu\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Rozšíření „%s“ neexistuje\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Zobrazit informace o rozšíření"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Přepsat stávající rozšíření"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "BALÍČEK_S_ROZŠÍŘENÍM"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Nainstalovat balíček s rozšířením"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nebyl zadán žádný balíček s rozšířením"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Bylo zadáno více balíčků s rozšířením naráz"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Zobrazit rozšíření nainstalovaná pro uživatele"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Zobrazit rozšíření nainstalovaná pro systém"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Zobrazit povolená rošíření"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Zobrazit zakázaná rozšíření"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Zobrazit rozšíření s předvolbami"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Zobrazit rozšíření s aktualizacemi"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Vypsat podrobnosti o rozšíření"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Vypsat nainstalovaná rozšíření"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "SOUBOR"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Dodatečný zdroj, který se má přibalit"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHÉMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Schéma GSettings, které se má přibalit"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "SLOŽKA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Složka, ve které se nacházejí jazykové překlady"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMÉNA"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Doména gettext, která se má použít pro překlady"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Přepsat stávající balíček"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Složka, ve které se má balíček vytvořit"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ZDROJOVÁ_SLOŽKA"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Vytvořit balíček s rozšířením"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Bylo zadáno více zdrojových složek naráz"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Rozšíření „%s“ nemá žádné předvolby\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Selhalo otevření předvoleb pro rozšíření „%s“: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Otevřít předvolby rozšíření"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Uvést rozšíření od výchozího stavu"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Systémové rozšíření odinstalovat nelze\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Selhala odinstalace „%s“\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Odinstalovat rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Nevypisovat chybové zprávy"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Cesta"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "Adresa URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Původní autor"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Verze"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stav"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "„version“ nepřebírá žádné argumenty"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Použití:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Vypsat informace o verzi a skončit."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "PŘÍKAZ"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTY…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Příkazy:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Vypsat nápovědu"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Povolit rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Zakázat rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Uvést rozšíření do výchozího stavu"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Odinstalovat rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Vypsat rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Zobrazit informace o rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Otevřít předvolby rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Vytvořit rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Zabalit rozšíření"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Nainstalovat balíček s rozšířením"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Použijte „%s“ k vypsání podrobné nápovědy.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Holá aplikace"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Prázdné rozšíření"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Ukazatel"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Přidá ikonu na horní lištu"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u výstup"
+msgstr[1] "%u výstupy"
+msgstr[2] "%u výstupů"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u vstup"
+msgstr[1] "%u vstupy"
+msgstr[2] "%u vstupů"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Systémové zvuky"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "Zda měl adaptér Bluetooth nastavená zařízení pro spolupráci s ním"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Shell zobrazuje nabídku Bluetooth jen v případě, že je adaptér Bluetooth "
+#~ "zapnutý, nebo když existují nějaká zařízení nastavená pro spolupráci s "
+#~ "výchozím adaptérem. Nabídka zmizí, když se zjistí, že výchozí adaptér "
+#~ "nemá přiřazená žádná zařízení."
diff --git a/po/da.po b/po/da.po
new file mode 100644
index 0000000..9c1a81c
--- /dev/null
+++ b/po/da.po
@@ -0,0 +1,4189 @@
+# Danish translation of gnome-shell
+# Copyright (C) 2010-2011, 2014-2016, 2018 gnome-shell
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Kenneth Nielsen <k.nielsen81@gmail.com>, 2012.
+# Kris Thomsen <mail@kristhomsen.dk>, 2009-2014, 2016-2017.
+# Ask Hjorth Larsen <asklarsen@gmail.com>, 2014-2016, 2018.
+# Alan Mortensen <alanmortensen.am@gmail.com>, 2018-22.
+# scootergrisen, 2019-2020.
+#
+# Konventioner:
+#
+# dash => favoritområde
+# overview => overblik
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-14 22:12+0000\n"
+"PO-Revision-Date: 2022-09-16 16:20+0200\n"
+"Last-Translator: Alan Mortensen <alanmortensen.am@gmail.com>\n"
+"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.0.1\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Programstartere"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktivér favoritprogram 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktivér favoritprogram 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktivér favoritprogram 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktivér favoritprogram 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktivér favoritprogram 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktivér favoritprogram 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktivér favoritprogram 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktivér favoritprogram 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktivér favoritprogram 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Skærmbilleder"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Tag et skærmbillede interaktivt"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Tag et skærmbillede"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Tag et skærmbillede af et vindue"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Optag en skærmoptagelse interaktivt"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "System"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Vis påmindelseslisten"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokusér på den aktive påmindelse"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Vis oversigten"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Vis alle programmer"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Åbn programmenuen"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Vindueshåndtering og åbning af programmer"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Aktivér interne værktøjer, som er nyttige for udviklere og testere fra Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Tillad adgang til interne fejlsøgnings- og overvågningsværktøjer med brug af "
+"dialogen Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Uuid'er på udvidelser der aktiveres"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Udvidelser til GNOME Shell har en uuid-egenskab; denne nøgle oplister "
+"udvidelser, som skal indlæses. Alle udvidelser, som skal indlæses, skal være "
+"i denne liste. Du kan også redigere listen med D-Bus-metoderne "
+"EnableExtension og DisableExtension i org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Uuid'er på udvidelser der tvinges til deaktivering"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Udvidelser til GNOME Shell har en uuid-egenskab; denne nøgle oplister "
+"udvidelser, som skal deaktiveres, selv hvis de indlæses som del af den "
+"nuværende tilstand. Du kan også redigere listen med D-Bus-metoderne "
+"EnableExtension og DisableExtension i org.gnome.Shell. Nøglen har prioritet "
+"over indstillingen “enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Deaktivér brugerudvidelser"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Deaktivér alle udvidelser, som brugeren har aktiveret uden at ændre på "
+"“enabled-extension”-indstillingen."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Deaktiverer godkendelsen af udvidelsesversionskompatibilitet"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell vil kun indlæse udvidelser, som påstår at understøtte den i "
+"øjeblikket kørende version. Dette tilvalg vil deaktivere denne kontrol, og "
+"forsøge at indlæse alle udvidelser uanset hvilken version, de påstår at "
+"understøtte."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Liste over skrivebordsfil-id'er til favoritprogrammer"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programmerne som passer til disse identifikatorer vil blive vist i "
+"favoritområdet."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historik for kommandodialogen (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historik for looking glass-dialogen"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Vis altid “Log ud”-menupunktet i brugermenuen."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Denne nøgle overstyrer den automatiske skjulning af menupunktet “Log ud” i "
+"enkelt bruger, enkelt session-situationer."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Om adgangskoder skal huskes for montering af krypterede eller fjern-"
+"filsystemer"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Shell'en vil spørge efter en adgangskode når en krypteret enhed eller et "
+"fjern-filsystem bliver monteret. Hvis adgangskoden kan gemmes til fremtidig "
+"brug, vil et “Husk adgangskode”-afkrydsningsfelt være synlig. Denne nøgle "
+"angiver standardværdien af dette afkrydsningsfelt."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Om standard Bluetooth-adapteren er indstillet til at have enheder tilknyttet"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Shell'en vil kun vise menupunktet Bluetooth, hvis en Bluetooth-adapter er "
+"tændt, eller hvis der er enheder tilknyttet standard adapteren. Dette vil "
+"kun blive nulstillet hvis standard adapteren på et tidspunkt, ikke længere "
+"har enheder tilknyttet."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "Den sidst valgte ikke-standardstrømprofil"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Nogle systemer understøtter mere end to strømprofiler. For stadig at kunne "
+"skifte mellem to profiler registrerer denne nøgle den sidst valgte profil, "
+"der ikke er standardprofil."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Den sidste version “Velkommen til GNOME”-dialogen blev vist for"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Denne nøgle bestemmer, hvilken version “Velkommen til GNOME”-dialogen sidst "
+"blev vist for. En tom streng repræsenterer den ældst mulige version, og et "
+"kæmpe nummer repræsenterer versioner, som endnu ikke eksisterer. Et kæmpe "
+"nummer vil i praksis deaktivere dialogen."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Layout af programvælgeren"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Layout af programvælgeren. Hver indgang i tabellen er en side. Sider gemmes "
+"i den rækkefølge, de optræder i GNOME Shell. Hver side indeholder et par af "
+"“program-id” → 'data'. Aktuelt gemmes følgende værdier som 'data': • "
+"“placering”: programikonets placering på siden"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Genvejstast til at åbne programmenuen"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Genvejstast til at åbne programmenuen."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Genvejstast til at skifte mellem oversigtstilstande"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Genvejstast til at skifte mellem session, vinduesvælger og programgitter"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Genvejstast til at skifte mellem programgitter, vinduesvælger og session"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Genvejstast til at åbne “Vis programmer”-visningen"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Genvejstast til at åbne “Vis programmer”-visningen i Aktivitetsoversigten."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Genvejstast til at åbne oversigten"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Genvejstast til at åbne Aktivitetsoversigten."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Genvejstast til at slå synligheden af påmindelseslisten til eller fra"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Genvejstast til at slå synligheden af påmindelseslisten til eller fra."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Genvejstast til at fokusere på den aktive påmindelse"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Genvejstast til at fokusere på den aktive påmindelse."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Skift til program 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Skift til program 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Skift til program 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Skift til program 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Skift til program 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Skift til program 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Skift til program 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Skift til program 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Skift til program 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Begræns skifteren til det nuværende arbejdsområde."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Hvis sand, vises kun programmer, som har vinduer på det nuværende "
+"arbejdsområde, i skifteren. Ellers er alle programmer inkluderet."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "Ikon-tilstanden for programmet."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Konfigurerer hvordan vinduer bliver vist i omskifteren. Gyldige muligheder "
+"er “thumbnail-only” (viser forhåndsvisning af vinduet), “app-icon-"
+"only” (viser kun programikonet) eller “both” (begge)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Hvis sand, vises kun vinduer fra det nuværende arbejdsområde i skifteren. "
+"Ellers er alle programmer inkluderet."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Placeringer"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Placeringerne som skal vises i verdensure"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Automatisk placering"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Om den nuværende placering skal hentes eller ej"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Placering"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "Placeringen der skal vises en prognose for"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Hæft modal dialog til overliggende vindue"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Denne nøgle overstyrer nøglen i org.gnome.mutter, når du kører GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Aktivér kant-fliselægning, når vinduer placeres ved skærmkanter"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Arbejdsområder er håndteret dynamisk"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Arbejdsområder kun på den primære skærm"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Forsink fokusændringer i musetilstand indtil pegeren holder op med at bevæge "
+"sig"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Netværksindlogning"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Noget gik galt"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Vi er kede af det, men der er opstået et problem: Denne udvidelses "
+"indstillinger kan ikke vises. Vi anbefaler, at du rapporterer problemet til "
+"udvidelsens forfattere."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Tekniske detaljer"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Hjemmeside"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Besøg udvidelsens hjemmeside"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Annullér"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Adgangskode"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Vælg session"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Ikke listet?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(f.eks. bruger eller %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Brugernavn"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Indlogningsvindue"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Godkendelsesfejl"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(eller stryg fingeren over aflæseren)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(eller placér fingeren på aflæseren)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Sluk"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "sluk;luk ned;stands;stop"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Genstart"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;genstart;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Lås skærm"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lås skærm"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Log ud"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "log ud;log af;udmeld"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Hviletilstand"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "hviletilstand"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Skift bruger"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "skift bruger"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "lås orientering;deaktivér låst orientering;skærm;rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Tag et skærmbillede"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "screenshot;screencast;skærmbillede;snip;klip;optag"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Deaktivér låst skærmrotation"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Lås skærmrotation"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Kommando ikke fundet"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Kunne ikke fortolke kommando:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Kørsel af “%s” mislykkedes:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Lige nu"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minut siden"
+msgstr[1] "%d minutter siden"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d time siden"
+msgstr[1] "%d timer siden"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "I går"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dag siden"
+msgstr[1] "%d dage siden"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d uge siden"
+msgstr[1] "%d uger siden"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d måned siden"
+msgstr[1] "%d måneder siden"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d år siden"
+msgstr[1] "%d år siden"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "I går, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d. %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d. %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "I går, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d. %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d. %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Hotspot-login"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Din forbindelse til dette hotspot-login er ikke sikkert. Adgangskoder eller "
+"anden information du indtaster på denne side, kan ses af personer i nærheden."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Forbyd adgang"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Giv adgang"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Unavngivet mappe"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s er blevet fastgjort til favoritområdet."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s er blevet frigjort fra favoritområdet."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Åbne vinduer"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nyt vindue"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Vis detaljer"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Afslut"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Frigør"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Fastgør til favoritområde"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Start med integreret grafikkort"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Start med særskilt grafikkort"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Vælg lydenhed"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Indstillinger for lyd"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Hovedtelefoner"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Skift baggrund …"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Skærmindstillinger"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Indstillinger"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "O"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "L"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Forrige måned"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Næste måned"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Uge %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Ingen påmindelser"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Forstyr ikke"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Ryd"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” svarer ikke."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Du kan vælge at vente et øjeblik på at det fortsætter eller tvinge "
+"programmet til at lukke ned."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Tving lukning"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Vent"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Eksternt drev tilkoblet"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Eksternt drev frakoblet"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Kunne ikke låse op for diskenhed"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Den installerede version af udisks understøtter ikke PIM-indstillingen"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Åbn med %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Du kan også oprette forbindelse ved at trykke på “WPS”-knappen på din router."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Forbind"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Nøgle"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Adgangskode til privat nøgle"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitet"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Tjeneste"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Godkendelse påkrævet"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Adgangskoder eller krypteringsnøgler er påkrævet for at få adgang til det "
+"trådløse netværk “%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Trådet 802.1X-godkendelse"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Netværksnavn"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL-godkendelse"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN-kode er påkrævet"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN-kode er nødvendig for den mobile bredbåndsenhed"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Der kræves en adgangskode for at forbinde til “%s”."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Netværkshåndtering"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN-adgangskode"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Godkendelse er påkrævet"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Godkend"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Beklager, det fungerede ikke. Prøv igen."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s kalder sig nu %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Vinduer"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Vis programmer"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Favoritområde"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d. %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %-d. %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d. %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d. %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "I dag"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "I morgen"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Hele dagen"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Ingen begivenheder"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Tilføj verdensure …"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Verdensure"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Indlæser …"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Gå online for at se information om vejret"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Vejrinformation er ikke tilgængelig i øjeblikket"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Vejr"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Vælg en placering for vejr …"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Log %s ud"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Log ud"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s vil blive logget ud automatisk om %d sekund."
+msgstr[1] "%s vil blive logget ud automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Du vil blive logget ud automatisk om %d sekund."
+msgstr[1] "Du vil blive logget ud automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Log ud"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Sluk"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installér opdateringer og sluk"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Systemet vil lukke ned automatisk om %d sekund."
+msgstr[1] "Systemet vil lukke ned automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installér ventende softwareopdateringer"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Sluk"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Genstart"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Installér opdateringer og genstart"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Systemet vil genstarte automatisk om %d sekund."
+msgstr[1] "Systemet vil genstarte automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Genstart"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Genstart og installér opdateringer"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Systemet vil genstarte og installere opdateringer automatisk om %d sekund."
+msgstr[1] ""
+"Systemet vil genstarte og installere opdateringer automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Genstart og installér"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installér og sluk"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Sluk efter opdateringerne er installeret"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Genstart og installér opgradering"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s vil blive installeret efter genstart. Installering af opgradering kan "
+"vare længe: Sikr dig, at du har sikkerhedskopier, og at computeren er "
+"tilsluttet elnettet."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Lavt batteriniveau: Tilslut strøm før installation af opdateringer."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+"Nogle programmer er optagede eller indeholder arbejde, der ikke er gemt"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Andre brugere er logget ind"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Bootindstillinger"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (fjern)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsol)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installér"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Installér udvidelse"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Hent og installér “%s” fra extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Der er opdateringer til udvidelser"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Opdateringer til udvidelser er klar til at blive installeret."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Tillad at genveje hindres"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Programmet %s vil forhindre genveje"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Et program vil forhindre genveje"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Du kan gendanne genveje ved at trykke %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Nægt"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Tillad"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Langsomme taster slået til"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Langsomme taster slået fra"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Du har lige holdt skift-tasten nede i otte sekunder. Dette er genvejen til "
+"langsomme taster, som påvirker, hvordan dit tastatur virker."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Klæbetaster slået til"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Klæbetaster slået fra"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Du har lige trykket på skift-tasten fem gange i træk. Dette er genvejen til "
+"klæbetaster, som påvirker, hvordan dit tastatur virker."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Du har lige trykket på to taster samtidigt eller trykket på skift-tasten fem "
+"gange i træk. Dette slår klæbetaster fra, hvilket påvirker, hvordan dit "
+"tastatur virker."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Lad være tændt"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Tænd"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Sluk"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Lad være slukket"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Indstillinger for område og sprog"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Ingen udvidelser er installeret"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s er ikke kommet med nogen fejl."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Skjul fejl"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Vis fejl"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Aktiveret"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Deaktiveret"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Fejl"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Udløbet"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Henter"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Vis kilde"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Webside"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Systemet blev placeret i usikker tilstand"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Programmer har nu ubegrænset adgang"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Fortryd"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Logget ind som en privilegeret bruger"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Kørsel af en session som en privilegeret bruger bør undgås af "
+"sikkerhedsmæssige årsager. Log ind som en normal bruger, hvis det er muligt."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Skærmlås er deaktiveret"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Skærmlåsning kræver GNOME-skærmhåndteringen."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Systeminformation"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Ukendt kunstner"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Ukendt titel"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Skriv for at søge"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Programmer"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Oversigt"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Ny genvej …"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Program defineret"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Vis hjælp på skærmen"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Skift skærm"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Tildel tastekombination"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Færdig"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Redigér …"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Ingen"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Tryk på en knap for at konfigurere"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Tryk Esc for at afslutte"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Tryk en vilkårlig tast for at afslutte"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Aktiviteter"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "System"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Toppanel"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Kør en kommando"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Tryk Esc for at lukke"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Genstart er ikke tilgængelig på Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Genstarter …"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME er nødt til at låse skærmen"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Kunne ikke låse"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Lås blev blokeret af et program"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Markering"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Områdemarkering"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Skærm"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Skærmmarkering"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Vindue"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Vinduesmarkering"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Skærmbillede/-optagelse"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Vis markør"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Skærmoptagelser"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Skærmoptagelse fra %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Skærmbillede"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Skærmoptagelse optaget"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Klik her for at se videoen."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Vis i Filer"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Skærmbillede fra %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Skærmbillede taget"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Du kan indsætte billedet fra udklipsholderen."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Skærmbillede blev taget"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Søger …"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Ingen resultater."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d mere"
+msgstr[1] "%d mere"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Søg"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopiér"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Indsæt"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Vis tekst"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Skjul tekst"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock er slået til."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Skjult diskenhed"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows systemdiskenhed"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Bruger nøglefiler"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Brug i stedet <i>%s</i>-redskabet for at låse op for en diskenhed, som "
+"bruger nøglefiler."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-nummer"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Husk adgangskode"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Lås op"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Åbn %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM'et skal være et nummer eller tomt."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Kunne ikke starte %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Kunne ikke finde programmet %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Tilgængelighed"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Høj kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zoom"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Skærmlæser"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Skærmtastatur"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Visuelle påmindelser"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Klæbetaster"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Langsomme taster"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Hoppende taster"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Musetaster"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Stor tekst"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Autorotation"
+
+#: js/ui/status/bluetooth.js:171
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Lysstyrke"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Mørk tilstand"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Enkeltklik"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dobbeltklik"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Træk"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Sekundært klik"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Hvileklik"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Tastatur"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Vis tastaturlayout"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Tillad adgang til placering"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Programmet %s ønsker adgang til din placering"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Placeringsadgang kan ændres når som helst via privatlivsindstillingerne."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<ukendt>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Frakobl %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Forbind til %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s-hotspot"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Nattelys"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Ydelse"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Balanceret"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Strømsparetilstand"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Strømprofiler"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Stop en skærmoptagelse"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Stop skærmdeling"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Flytilstand"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Tag skærmbillede"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Slukmenu"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Hviletilstand"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Genstart …"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Sluk …"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Log ud …"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Skift bruger …"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Lås skærm"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Ukendt Thunderbolt-enhed"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"En ny enhed blev fundet, mens du var væk. Frakobl og tilkobl enheden igen "
+"for at kunne bruge den."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Ikkegodkendt Thunderbolt-enhed"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Ny enhed er fundet og skal godkendes af en administrator."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt-godkendelsesfejl"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Kunne ikke godkende Thunderbolt-enheden: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Lydstyrke ændret"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Lydstyrke"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Lydoutput"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Lydinput"
+
+# scootergrisen: tjek om det skal være Kloning frem for Klon.
+# scootergrisen: både i gnome-shell og gnome-control-center
+# scootergrisen: lader til Kloning er bedst i gnome-control-center
+# scootergrisen: men det ville nok være bedst hvis de var ens
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Klon"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Sammenføj skærme"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Kun ekstern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Kun indbygget"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Stryg op for at låse op"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Klik eller tryk en tast for at låse op"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Lås vindue op"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Log ind som en anden bruger"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Velkommen til GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Hvis du vil lære at finde rundt, så tjek rundvisningen."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nej tak"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Tag rundvisningen"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” er klar"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Behold disse skærmindstillinger?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Forkast indstillinger"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Behold ændringer"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Indstillingsændringer forkastes om %d sekund"
+msgstr[1] "Indstillingsændringer forkastes om %d sekunder"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Skjul"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Gendan"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maksimér"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Flyt"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Ændr størrelse"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Flyt titellinjen på skærmen"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Altid øverst"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Altid på synligt arbejdsområde"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Flyt til venstre arbejdsområde"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Flyt til højre arbejdsområde"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Flyt til arbejdsområdet ovenover"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Flyt til arbejdsområdet nedenunder"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Flyt til skærmen ovenover"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Flyt til skærmen nedenunder"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Flyt til venstre skærm"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Flyt til højre skærm"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Luk"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution kalender"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Udskriv version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Tilstand brugt af GDM til indlogningsskærm"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Brug en specifik tilstand, f.eks. “gdm” til indlogningsskærm"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Vis mulige tilstande"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ukendt"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Kunne ikke køre “%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Adgangskoder er ikke ens."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Adgangskode må ikke være tom"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Godkendelsesdialogen blev afvist af brugeren"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Udvidelser"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Håndtér dine GNOME-udvidelser"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME-projektet"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Udvidelser håndterer opdatering af udvidelser, konfigurering af deres "
+"indstillinger og fjernelse eller deaktivering af uønskede udvidelser."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfigurér GNOME Shell-udvidelser"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Ingen fundet"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Fjern “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr "Fjerner du udvidelsen, skal du hente den igen for at aktivere den"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Fjern"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Kenneth Nielsen\n"
+"Kris Thomsen\n"
+"Ask Hjorth Larsen\n"
+"Alan Mortensen\n"
+"scootergrisen\n"
+"\n"
+"Dansk-gruppen <dansk@dansk-gruppen.dk>\n"
+"Mere info: http://www.dansk-gruppen.dk"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d udvidelse vil blive opdateret ved næste login."
+msgstr[1] "%d udvidelser vil blive opdateret ved næste login."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Denne udvidelse er ikke kompatibel med denne version af GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Udvidelsen havde en fejl"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Udvidelsen kan opdateres"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Websted"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Fjern …"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Hjælp"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Om udvidelser"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Udvidelser kan skabe ydelses- og stabilitetsproblemer. Deaktivér udvidelser, "
+"hvis du får problemer med dit system."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Manuelt installeret"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Gå til <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a> for "
+"at finde og tilføje udvidelser."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Indbygget"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Ingen installerede udvidelser"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Vi beklager meget, men det var ikke muligt at hente listen over installerede "
+"udvidelser. Sørg for at du er logget ind i GNOME og prøv igen."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Opdateringer til udvidelser er klar"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Den nye udvidelse blev oprettet i %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Navnet skal være en meget kort (helst beskrivende) streng.\n"
+"Eksempler: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Navn"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Beskrivelsen er én sætning, som forklarer, hvad din udvidelse gør.\n"
+"Eksempler: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Beskrivelse"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"Uuid er en globalt unik identifikator til din udvidelse.\n"
+"Det skal udformes som en e-mailadresse (klikforfokus@rikkeræv.eksempel.org)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Vælg en af de tilgængelige skabeloner:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Skabelon"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Den nye udvidelses unikke identifikator"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAVN"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Navnet på den nye udvidelse, som brugeren kommer til at se"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "BESKRIVELSE"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "En kort beskrivelse af hvad udvidelsen gør"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "SKABELON"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Skabelonen der skal bruges til den nye udvidelse"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Indtast information om udvidelse interaktivt"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Opret en ny udvidelse"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Ukendte argumenter"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Uuid, navn og beskrivelse er påkrævet"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Kunne ikke forbinde til GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Udvidelsen “%s” findes ikke\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Deaktivér en udvidelse"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Der er ikke angivet noget uuid"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Der er angivet mere end ét uuid"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Aktivér en udvidelse"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Udvidelsen “%s” findes ikke\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Vis information om udvidelser"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Overskriv en eksisterende udvidelse"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "UDVIDELSESBUNDT"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Installér et udvidelsesbundt"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Der er ikke angivet noget udvidelsesbundt"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Der er angivet mere end ét udvidelsesbundt"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Vis brugerinstallerede udvidelser"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Vis systeminstallerede udvidelser"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Vis aktiverede udvidelser"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Vis deaktiverede udvidelser"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Vis udvidelser med indstillinger"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Vis udvidelser med opdateringer"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Vis detaljer om udvidelse"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Vis installerede udvidelser"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FIL"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Yderligere kilde som skal medtages i bundet"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SKEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Et GSettings-skema som skal medtages"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "MAPPE"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Mappen hvor oversættelser findes"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMÆNE"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Gettext-domænet som skal bruges til oversættelser"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Overskriv en eksisterende pakke"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Mappen hvor pakken skal oprettes"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "KILDEMAPPE"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Opret et udvidelsesbundt"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Der er angivet mere end én kildemappe"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Udvidelsen “%s” har ingen indstillinger\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Kunne ikke åbne indstillinger for udvidelsen “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Åbner udvidelsesindstillinger"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Nulstil en udvidelse"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Kan ikke afinstallere systemudvidelser\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Kunne ikke afinstallere “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Afinstallér en udvidelse"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Udskriv ikke fejlmeddelelser"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Sti"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Oprindelig forfatter"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Version"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Tilstand"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” modtager ikke argumenter"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Brug:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Udskriv versionsinformation og afslut."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "KOMMANDO"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTER …]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Kommandoer:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Udskriv hjælp"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Aktivér udvidelse"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Deaktivér udvidelse"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Nulstil udvidelse"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Afinstallér udvidelse"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Vis udvidelser"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Vis information om udvidelse"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Åbn udvidelsesindstillinger"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Opret udvidelse"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Pakkeudvidelse"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Installér udvidelsesbundt"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Brug “%s” for at få detaljeret hjælp.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Simpel"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "En tom udvidelse"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Tilføj et ikon til toppanelet"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u output"
+msgstr[1] "%u outputs"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u input"
+msgstr[1] "%u inputs"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Systemlyde"
+
+#~ msgid "Log Out"
+#~ msgstr "Log ud"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Indstillinger for Bluetooth"
+
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d forbundet"
+#~ msgstr[1] "%d forbundet"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth til"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth fra"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Placering aktiveret"
+
+#~ msgid "Disable"
+#~ msgstr "Slå fra"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Indstillinger for privatliv"
+
+#~ msgid "Location In Use"
+#~ msgstr "Placering i brug"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Placering deaktiveret"
+
+#~ msgid "Enable"
+#~ msgstr "Slå til"
+
+#~ msgid "%s Off"
+#~ msgstr "%s er slukket"
+
+#~ msgid "%s Connected"
+#~ msgstr "%s er forbundet"
+
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s er ikke håndteret"
+
+#~ msgid "%s Connecting"
+#~ msgstr "%s forbinder"
+
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s kræver godkendelse"
+
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Firmware mangler til %s"
+
+#~ msgid "%s Unavailable"
+#~ msgstr "%s utilgængelig"
+
+#~ msgid "%s Connection Failed"
+#~ msgstr "Forbindelse til %s mislykkedes"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Indstillinger for trådet netværk"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Indstillinger for mobilbredbånd"
+
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Hardwaren %s er deaktiveret"
+
+#~ msgid "%s Disabled"
+#~ msgstr "%s er deaktiveret"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Forbind til internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Flytilstand er slået til"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Trådløs er deaktiveret når flytilstand er slået til."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Slå flytilstand fra"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Trådløs er slået fra"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Trådløs skal tændes for at kunne forbinde til et netværk."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Slå trådløs til"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Trådløse netværk"
+
+#~ msgid "Select a network"
+#~ msgstr "Vælg et netværk"
+
+#~ msgid "No Networks"
+#~ msgstr "Ingen netværk"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Brug hardwareknap til at slukke"
+
+#~ msgid "Select Network"
+#~ msgstr "Vælg netværk"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Indstillinger for trådløs"
+
+#~ msgid "%s Not Connected"
+#~ msgstr "%s er ikke forbundet"
+
+#~ msgid "connecting…"
+#~ msgstr "forbinder …"
+
+#~ msgid "authentication required"
+#~ msgstr "godkendelse er påkrævet"
+
+#~ msgid "connection failed"
+#~ msgstr "forbindelse mislykkedes"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Indstillinger for VPN"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN slukket"
+
+#~ msgid "Network Settings"
+#~ msgstr "Netværksindstillinger"
+
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s trådet forbindelse"
+#~ msgstr[1] "%s trådet forbindelser"
+
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s trådløs forbindelse"
+#~ msgstr[1] "%s trådløse forbindelser"
+
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s modemforbindelse"
+#~ msgstr[1] "%s modemforbindelser"
+
+#~ msgid "Connection failed"
+#~ msgstr "Forbindelse mislykkedes"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Aktivering af netværksforbindelse mislykkedes"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Nattelys deaktiveret"
+
+#~ msgid "Resume"
+#~ msgstr "Genoptag"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Deaktivér indtil i morgen"
+
+#~ msgid "Power Settings"
+#~ msgstr "Indstillinger for strømstyring"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Fuldt opladet"
+
+#~ msgid "Not Charging"
+#~ msgstr "Oplader ikke"
+
+#~ msgid "Estimating…"
+#~ msgstr "Udregner …"
+
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d tilbage (%d %%)"
+
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d indtil fuld (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Skærmen er delt"
+
+#~ msgid "Turn off"
+#~ msgstr "Sluk"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Flytilstand er slået til"
+
+#~ msgid "Lock"
+#~ msgstr "Lås"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Sluk/log ud"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Fjern fra favoritter"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Føj til favoritter"
+
+#~ msgid "Author"
+#~ msgstr "Forfatter"
+
+#~ msgid "Warning"
+#~ msgstr "Advarsel"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Udvidelser kan medføre systemproblemer heriblandt ydelsesproblemer. "
+#~ "Støder du på problemer med dit system, anbefaler vi at deaktivere alle "
+#~ "udvidelser."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Kunne ikke forbinde til GNOME Shell"
+
+#~ msgid "App Picker View"
+#~ msgstr "Programvælger-visning"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Indeks for den valgte visning i programvælgeren."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Aktivér introspektions-API"
+
+# shell er andre steder ikke oversat
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Aktiverer et D-Bus-API, som tillader introspektion af shell'ens "
+#~ "programtilstand."
+
+#~ msgid "Minimize"
+#~ msgstr "Minimér"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Afmaksimér"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopieringsfejl"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Ofte brugte programmer vil blive vist her"
+
+#~ msgid "Frequent"
+#~ msgstr "Ofte"
+
+#~ msgid "All"
+#~ msgstr "Alle"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d. %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d. %B %Y"
+
+#~ msgid "Off"
+#~ msgstr "Slukket"
+
+#~ msgid "On"
+#~ msgstr "Tændt"
+
+#~ msgid "Username…"
+#~ msgstr "Brugernavn …"
+
+#~ msgid "Password: "
+#~ msgstr "Adgangskode: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Indtast adgangskode …"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Gennemse i Software"
+
+#~ msgid "Next"
+#~ msgstr "Næste"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Log ind"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Lås orientering"
+
+#~ msgid "Rename"
+#~ msgstr "Omdøb"
+
+#~ msgid "Password:"
+#~ msgstr "Adgangskode:"
+
+#~ msgid "Type again:"
+#~ msgstr "Indtast igen:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Godkendelse er påkrævet af trådløst netværk"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Adgangskode til mobilt bredbåndsnetværk"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %-d. %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d ny besked"
+#~ msgstr[1] "%d nye beskeder"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d ny påmindelse"
+#~ msgstr[1] "%d nye påmindelser"
+
+#~ msgid "Account Settings"
+#~ msgstr "Indstillinger for konti"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Orienteringslås"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Genvejstast, som pauser og genstarter alle kørende animationer, til "
+#~ "fejlsøgningsformål"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Hvilket tastatur bruges"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Tastaturtypen som bruges."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Der opstod in fejl ved indlæsning af indstillingsdialogen for %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s hele dagen."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, derefter %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, derefter %s, efterfulgt af %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Føles som %s."
+
+#~| msgid "Log Out"
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Log ud"
+
+#~| msgid "Switch User"
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Skift bruger"
+
+#~ msgid "Hide tray"
+#~ msgstr "Skjul statusfelt"
+
+#~ msgid "Status Icons"
+#~ msgstr "Statusikoner"
+
+#~ msgid "Events"
+#~ msgstr "Begivenheder"
+
+#~ msgid "Notifications"
+#~ msgstr "Beskeder"
+
+#~ msgid "Media"
+#~ msgstr "Medier"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Omdirigering af webgodkendelse"
+
+#~ msgid "Not In Use"
+#~ msgstr "Ikke i brug"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Vis ugenummer i kalenderen"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Hvis sand vises ISO-ugenummeret i kalenderen."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Brug som internetforbindelse"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s spørger efter adgang til din placering."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (wayland-sammensætter)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d forbundet enhed"
+#~ msgstr[1] "%d forbundne enheder"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Batteri"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Vis besked-statusfeltet"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Fangeportal"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "S"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "M"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Ti"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "O"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "To"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "F"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "L"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Intet planlagt"
+
+#~ msgid "This week"
+#~ msgstr "Denne uge"
+
+#~ msgid "Next week"
+#~ msgstr "Næste uge"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Flytbare enheder"
+
+#~ msgid "Eject"
+#~ msgstr "Skub ud"
+
+#~ msgid "Invitation"
+#~ msgstr "Invitation"
+
+#~ msgid "Call"
+#~ msgstr "Opkald"
+
+#~ msgid "File Transfer"
+#~ msgstr "Filoverførsel"
+
+#~ msgid "Chat"
+#~ msgstr "Chat"
+
+#~ msgid "Unmute"
+#~ msgstr "Slå lyd til"
+
+#~ msgid "Mute"
+#~ msgstr "Slå lyd fra"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Invitation til %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s inviterer dig til at deltage i %s"
+
+#~ msgid "Decline"
+#~ msgstr "Afvis"
+
+#~ msgid "Accept"
+#~ msgstr "Acceptér"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Videoopkald fra %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Opkald fra %s"
+
+#~ msgid "Answer"
+#~ msgstr "Svar"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s sender dig %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s vil gerne have tilladelse til at se, når du er online"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Godkendelse mislykkedes"
+
+#~ msgid "Encryption error"
+#~ msgstr "Krypteringsfejl"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Certifikat ikke angivet"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Utroværdigt certifikat"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certifikat udløbet"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certifikat ikke aktiveret"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Certifikat-værtsnavn stemmer ikke"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Certifikat-fingeraftryk stemmer ikke"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certifikat selv-underskrevet"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Status er angivet til offline"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Ugyldigt certifikat"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Forbindelse er blevet afvist"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Forbindelse kan ikke oprettes"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Forbindelse er mistet"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Denne konto er allerede forbundet til serveren"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "Forbindelsen er blevet erstattet af en ny forbindelse, som bruger samme "
+#~ "ressource"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Kontoen findes allerede på serveren"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Serveren er i øjeblikket for travl til at behandle forbindelsen"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Certifikat er blevet påberåbt"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Certifikat bruger en usikker cipher-algoritme eller er kryptografisk svag"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Længden på servercertifikatet, eller dybden af servercertifikat-kæden, "
+#~ "overskrider grænsen, som er fastsat af det kryptografiske-programbibliotek"
+
+#~ msgid "Internal error"
+#~ msgstr "Intern fejl"
+
+#~ msgid "View account"
+#~ msgstr "Vis konto"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Åbn kalender"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Indstillinger for dato og tid"
+
+#~ msgid "Open"
+#~ msgstr "Åbn"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Ryd beskeder"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Indstillinger for påmindelser"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Menu for statusfelt"
+
+#~ msgid "No Messages"
+#~ msgstr "Ingen beskeder"
+
+#~ msgid "Message Tray"
+#~ msgstr "Besked-statusfelt"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Det maksimale nøjagtighedsniveau for placering."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Konfigurerer det maksimale nøjagtighedsniveau for placering, som det er "
+#~ "tilladt programmer at se. Mulighederne er “off” (deaktivér sporing), "
+#~ "“country”, “city”, “neighborhood”, “street” og “exact” (kræver typisk GPS-"
+#~ "modtager). Bemærk at dette kun styrer hvad GeoClue vil tillade "
+#~ "programmerne at se - disse kan selv finde brugerens placering ved brug af "
+#~ "netværksressourcer (dog højst ned til gadepræcision)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Placering af knapper i titel-linjen"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Denne nøgle overstyrer nøglen i org.gnome.desktop.wm.preferences, når du "
+#~ "kører GNOME Shell."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Vælg en udvidelse at konfigurere ved hjælp af kombinationsboksen ovenfor."
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "unavailable"
+#~ msgstr "utilgængelig"
+
+# Dette her er kode for at skrive måned før år jvf. kommentar for samme streng i gtk+:
+#
+# Translate to calendar:YM if you want years to be displayed
+# before months; otherwise translate to calendar:MY.
+# Do *not* translate it to anything else, if it
+# it isn't calendar:YM or calendar:MY it will not work.
+#
+# Note that the ordering described here is logical order, which is
+# further influenced by BIDI ordering. Thus, if you have a default
+# text direction of RTL and specify "calendar:YM", then the year
+# will appear to the right of the month.
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Liste over kategorier, som skal vises som mapper"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Hvert kategorinavn i denne liste vil blive repræsenteret som mappe i "
+#~ "programoversigten, i stedet for at blive vist inden i hovedoversigten."
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d.</b> <b>%B</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d.</b> <b>%B</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Godkendelsesforespørgsel fra %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Enheden %s ønsker at blive parret med denne computer"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Enheden %s ønsker adgang til tjenesten “%s”"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Giv kun lov denne gang"
+
+#~ msgid "Reject"
+#~ msgstr "Afvis"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Bekræftelse af parring for %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Bekræft om adgangsnøglen “%06d” stemmer overens med den, som vises på "
+#~ "enheden."
+
+#~ msgid "Does not match"
+#~ msgstr "Stemmer ikke"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Forespørgsel om parring for %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Indtast PIN-koden nævnt på enheden."
+
+#~ msgid "OK"
+#~ msgstr "O.k."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Beklager, ingen visdom til dig i dag:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "Oraklet %s siger"
+
+#~ msgid "Settings Menu"
+#~ msgstr "Menu for indstillinger"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Om der skal indsamles statistik om programmers brug"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "Shell'ens overvåger normalt aktive programmer for at kunne vise de mest "
+#~ "brugte (f.eks. i genveje). Selvom disse data er holdt private, vil du "
+#~ "muligvis ønske at deaktivere dem af private grunde. Bemærk at selvom du "
+#~ "gør dette, vil det ikke fjerne de allerede gemte data."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Brugt internt til at gemme den seneste IM-tilstedeværelse angivet direkte "
+#~ "af brugeren. Værdien er fra tællingen TpConnectionPresenceType."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Brugt internt til at gemme den seneste tilstand for "
+#~ "sessionstilstedeværelse for brugeren. Værdien her er fra tællingen "
+#~ "GsmPresenceStatus."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Genvejstast til at slå skærmoptager til eller fra"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Genvejstast til at starte/stoppe den indbyggede skærmoptager."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Billedfrekvens brugt til skærmoptagelser."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "Billedfrekvensen på den endelige skærmoptagelse, optaget af GNOME-Shells "
+#~ "skærmoptager i billeder-per-sekund."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "Datakanalen for Gstreamer bruges til indkodning af skærmoptagelsen"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Angiver datakanalen for GStreamer som bruges til at indkode optagelser. "
+#~ "Dette følger syntaksen brugt af gst-launch. Datakanalen bør have et "
+#~ "afkoblet sink-område hvor den optagede video gemmes. Den vil sædvanligvis "
+#~ "have et afkoblet kildeområde; outputtet fra det område vil blive skrevet "
+#~ "til outputfilen. Dog kan datakanalen også tage sig af sit eget output - "
+#~ "dette kan bruges til at sende outputtet til en icecast-server via "
+#~ "shout2send eller lignende. Når der ændres til en tom værdi, vil "
+#~ "standarddatakanalen blive brugt. Dette er i øjeblikket “vp8enc "
+#~ "min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+#~ "threads=%T ! queue ! webmmux” og optager i WEBM-formatet med VP8-"
+#~ "codec'et. %T bruges som pladsholder for et gæt om det optimale trådantal "
+#~ "på systemet."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Filendelse til at gemme skærmoptagelser"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "Filnavnet på skærmoptagelser vil være et unikt filnavn baseret på dags "
+#~ "dato og bruge denne endelse. Det skal ændres når der optages i et andet "
+#~ "containerformat."
+
+#~ msgid "Session…"
+#~ msgstr "Session …"
+
+#~ msgid "Power"
+#~ msgstr "Strøm"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Tryk Log ud for at afslutte disse programmer og logge ud af systemet."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Logger ud af systemet."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "Tryk Sluk for at afslutte disse programmer og lukke systemet ned."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Lukker systemet ned."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Tryk Genstart for at afslutte disse programmer og genstarte systemet."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Genstarter systemet."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Indstillinger for universel adgang"
+
+#~ msgid "Visibility"
+#~ msgstr "Synlighed"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Indstil en ny enhed …"
+
+#~ msgid "Send Files…"
+#~ msgstr "Send filer …"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Indstillinger for tastatur"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Indstillinger for mus"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Lydniveau, netværk, batteri"
+
+#~ msgid "disabled"
+#~ msgstr "deaktiveret"
+
+#~ msgid "cable unplugged"
+#~ msgstr "kabel er ikke sat i"
+
+#~ msgid "More…"
+#~ msgstr "Flere …"
+
+#~ msgid "Wired"
+#~ msgstr "Trådet"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Auto ethernet"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Auto bredbånd"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Auto opringning"
+
+#~ msgid "Auto %s"
+#~ msgstr "Auto %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Auto bluetooth"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Auto trådløs"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d time tilbage"
+#~ msgstr[1] "%d timer tilbage"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s tilbage"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "time"
+#~ msgstr[1] "timer"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minut"
+#~ msgstr[1] "minutter"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d minut tilbage"
+#~ msgstr[1] "%d minutter tilbage"
+
+#~ msgid "AC Adapter"
+#~ msgstr "AC-adapter"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Batteri"
+
+#~ msgid "Monitor"
+#~ msgstr "Skærm"
+
+#~ msgid "Mouse"
+#~ msgstr "Mus"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Mobiltelefon"
+
+#~ msgid "Media Player"
+#~ msgstr "Medieafspiller"
+
+#~ msgid "Tablet"
+#~ msgstr "Tavle-pc"
+
+#~ msgid "Computer"
+#~ msgstr "Computer"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Ukendt"
+
+#~ msgid "Available"
+#~ msgstr "Tilgængelig"
+
+#~ msgid "Busy"
+#~ msgstr "Optaget"
+
+#~ msgid "Invisible"
+#~ msgstr "Usynlig"
+
+#~ msgid "Away"
+#~ msgstr "Ikke tilstede"
+
+#~ msgid "Idle"
+#~ msgstr "Tomgang"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Din chat-status vil blive angivet som optaget"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Meddelelser er nu deaktiveret, inklusiv chat-beskeder. Din online-status "
+#~ "er blevet justeret, for at fortælle andre, at du muligvis ikke ser deres "
+#~ "beskeder."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Nedlukning kan resultere i tab af arbejde, som ikke er gemt."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Log ind"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "PROGRAMMER"
+
+#~ msgid "SETTINGS"
+#~ msgstr "INDSTILLINGER"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "Subscription request"
+#~ msgstr "Godkendelsesforespørgsel"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Sendt <b>%A</b> kl. <b>%H:%M</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Sendt <b>%A</b> den <b>%e. %B</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Forbindelse til %s mislykkedes"
+
+#~ msgid "Reconnect"
+#~ msgstr "Forbind igen"
+
+#~ msgid "tray"
+#~ msgstr "statusfelt"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Gennemse filer …"
+
+#~ msgid "Error browsing device"
+#~ msgstr "Fejl under læsning af enhed"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "Den forespurgte enhed kan ikke læses, fejlen er “%s”"
+
+#~ msgid "More..."
+#~ msgstr "Mere …"
+
+#~ msgid "Wireless"
+#~ msgstr "Trådløs"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN-forbindelser"
+
+#~ msgid "System Settings"
+#~ msgstr "Systemindstillinger"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Dit yndlingspåskeæg"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Storbritannien"
+
+#~ msgid "Default"
+#~ msgstr "Standard"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Kunne ikke afmontere “%s”"
+
+#~ msgid "Retry"
+#~ msgstr "Forsøg igen"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "STEDER & ENHEDER"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "deaktiverede OpenSearch-udbydere"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Vis tid med sekunder"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Hvis sand vises sekunder i klokkeslæt."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Vis dato i uret"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Hvis sand vises datoen i uret, som tillæg til tiden."
+
+#~ msgid "CONTACTS"
+#~ msgstr "KONTAKTER"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %e. %b, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e. %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e. %b, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Forkert adgangskode, forsøg venligst igen"
+
+#~ msgid "%s is online."
+#~ msgstr "%s er online."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s er offline."
+
+#~ msgid "%s is away."
+#~ msgstr "%s er ikke til stede."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s er optaget."
+
+#~ msgid "Hidden"
+#~ msgstr "Skjult"
+
+#~ msgid "Power Off..."
+#~ msgstr "Sluk …"
+
+#~ msgid "Online Accounts"
+#~ msgstr "Online konti"
+
+#~ msgid "Log Out..."
+#~ msgstr "Log ud …"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "SENESTE ELEMENTER"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should be loaded. disabled-extensions overrides this setting for "
+#~ "extensions that appear in both lists."
+#~ msgstr ""
+#~ "GNOME-skallens udvidelser har en uuid-indstilling; denne nøgle oplister "
+#~ "udvidelser som ikke skal indlæses. disabled-extensions overskriver denne "
+#~ "indstilling for udvidelser, som vises i begge lister."
+
+#~ msgid "Show password"
+#~ msgstr "Vis adgangskode"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s er færdig med at starte"
+
+#~ msgid "If true, display onscreen keyboard."
+#~ msgstr "Hvis sand vises skærmtastatur."
+
+#~ msgid "Connectivity lost"
+#~ msgstr "Forbindelse mistet"
+
+#~ msgid "You're no longer connected to the network"
+#~ msgstr "Du er ikke længere forbundet til netværket"
+
+#~ msgid "Uuids of extensions to disable"
+#~ msgstr "Uuid'er for udvidelser der deaktiveres"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "Du er nu forbundet til den mobile bredbåndsforbindelse “%s”"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "Du er nu forbundet til det trådløse netværk “%s”"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "Du er nu forbundet til VPN-netværket “%s”"
+
+#~ msgid "Localization Settings"
+#~ msgstr "Indstillinger for sprog"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "Mindre end et minut siden"
+
+#~ msgid "Shut Down"
+#~ msgstr "Luk ned"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr ""
+#~ "Tryk Luk ned for at afslutte disse programmer og lukke systemet ned."
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "System lukker automatisk ned om %d sekunder."
+
+#~ msgid "Shutting down the system."
+#~ msgstr "Lukker systemet ned."
+
+#~ msgid "Confirm"
+#~ msgstr "Bekræft"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "INDSTILLINGER"
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "Træk hertil for at tilføje til favoritter"
+
+#~ msgid "Find..."
+#~ msgstr "Find …"
+
+#~ msgid "Sidebar"
+#~ msgstr "Sidebjælke"
+
+#~ msgid "System Preferences..."
+#~ msgstr "Systemindstillinger …"
+
+#~ msgid "Recent Documents"
+#~ msgstr "Seneste dokumenter"
+
+#~ msgid "(see all)"
+#~ msgstr "(se alle)"
+
+#~ msgid "PLACES"
+#~ msgstr "STEDER"
+
+#~ msgid "SEARCH RESULTS"
+#~ msgstr "SØGERESULTATER"
+
+#~ msgid "Can't lock screen: %s"
+#~ msgstr "Kan ikke låse skærm: %s"
+
+#~ msgid "Can't temporarily set screensaver to blank screen: %s"
+#~ msgstr "Kan ikke midlertidigt sætte pauseskærm til blank skærm: %s"
+
+#~ msgid "Can't logout: %s"
+#~ msgstr "Kan ikke logge ud: %s"
+
+#~ msgid "Browse"
+#~ msgstr "Gennemse"
+
+#~ msgid "Find apps or documents"
+#~ msgstr "Find programmer eller dokumenter"
+
+#~ msgid "The user manager object this user is controlled by."
+#~ msgstr "Brugerhåndteringsobjektet, denne bruger er styret af."
diff --git a/po/de.po b/po/de.po
new file mode 100644
index 0000000..eb59ec1
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,3395 @@
+# German gnome-shell translation.
+# Copyright (C) 2009 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+#
+# workspace - Arbeitsfläche
+#
+# Hendrik Brandt <heb@gnome-de.org>, 2009.
+# Mario Klug <mario@klug.me>, 2010.
+# Jakob Kramer <jakob.kramer@gmx.de>, 2010.
+# Hendrik Richter <hendrikr@gnome.org>, 2009-2011.
+# Paul Seyfert <pseyfert@mathphys.fsk.uni-heidelberg.de>, 2010-2011.
+# Tobias Endrigkeit <tobiasendrigkeit@outlook.com>, 2012-2013.
+# Benjamin Steinwender <b@stbe.at>, 2013-2014.
+# Franco Della-Monica <franco.della.monica@gmail.com>, 2016.
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2009-2013, 2015-2018.
+# Bernd Homuth <dev@hmt.im>, 2014-2015, 2016, 2019.
+# Wolfgang Stöggl <c72578@yahoo.de>, 2012, 2014, 2017-2018, 2020.
+# Tim Sabsch <tim@sabsch.com>, 2018-2020, 2022.
+# Philipp Kiemle <philipp.kiemle@gmail.com>, 2020, 2021.
+# Jürgen Benvenuti <gastornis@posteo.org>, 2022.
+# Christian Kirbach <christian.kirbach@gmail.com>, 2009-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-15 22:30+0000\n"
+"PO-Revision-Date: 2023-02-18 14:53+0100\n"
+"Last-Translator: Tim Sabsch <tim@sabsch.com>\n"
+"Language-Team: German <gnome-de@gnome.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-DamnedLies-Scope: partial\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Starter"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Favorisierte Anwendung 1 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Favorisierte Anwendung 2 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Favorisierte Anwendung 3 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Favorisierte Anwendung 4 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Favorisierte Anwendung 5 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Favorisierte Anwendung 6 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Favorisierte Anwendung 7 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Favorisierte Anwendung 8 aktivieren"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Favorisierte Anwendung 9 aktivieren"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Bildschirmfotos"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Ein Bildschirmfoto interaktiv aufnehmen"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Ein Bildschirmfoto aufnehmen"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Ein Bildschirmfoto eines Fensters aufnehmen"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Eine Bildschirmaufzeichnung interaktiv aufnehmen"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "System"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Die Benachrichtigungsliste anzeigen"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Auf aktive Benachrichtigungen ausrichten"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Die Übersicht anzeigen"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Alle Anwendungen anzeigen"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Das Anwendungsmenü öffnen"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME-Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Fenster verwalten und Anwendungen starten"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Interne Werkzeuge für Entwickler und Tester mit Alt+F2 einschalten"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "Erlaubt Zugriff auf interne Fehlerdiagnose- und Überwachungswerkzeuge."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs der zu aktivierenden Erweiterungen"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Die Erweiterungen der GNOME-Shell besitzen eine UUID-Eigenschaft. Dieser "
+"Schlüssel listet Erweiterungen auf, welche geladen werden sollen. Jede zu "
+"ladende Erweiterung muss in dieser Liste erscheinen. Sie können diese Liste "
+"auch mit den D-Bus-Methoden EnableExtension und DisableExtension in org."
+"gnome.Shell bearbeiten."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs der sofort zu deaktivierenden Erweiterungen"
+
+# Das ist gemeint mit "Sie", oder?
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Die Erweiterungen der GNOME-Shell besitzen eine UUID-Eigenschaft. Dieser "
+"Schlüssel listet Erweiterungen auf, welche deaktiviert werden sollten, "
+"selbst wenn sie als Teil des aktuellen Modus geladen sind. Sie können diese "
+"Liste auch mit den D-Bus-Methoden EnableExtension und DisableExtension in "
+"org.gnome.Shell bearbeiten. Dieser Schlüssel hat Vorrang vor der Einstellung "
+"»enabled-extensions«."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Shell-Erweiterungen deaktivieren"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Alle Shell-Erweiterungen, die der Benutzer aktiviert hat, deaktivieren. "
+"Beeinflusst nicht die Einstellung »enabled-extension«."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+"Legt fest, ob die Überprüfung der Versionskompatibilität der Erweiterung "
+"deaktiviert werden soll"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME-Shell lädt nur Erweiterungen, die die aktuelle Version unterstützen. "
+"Durch Einschalten dieser Option wird die interne Kompatibilitätsprüfung "
+"deaktiviert und alle Erweiterungen werden versucht zu laden, unabhängig von "
+"der Version, die die Erweiterung offiziell unterstützt."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Liste von Desktop-Dateikennungen und deren bevorzugte Anwendungen"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programme, welche auf diese Bezeichner zutreffen, werden im Favoriten-"
+"Bereich angezeigt."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Verlauf des Befehlsdialogs (Alt+F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Chronik des Dialogs »looking glass«"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Immer den Eintrag »Abmelden« im Benutzermenü anzeigen."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Dieser Schlüssel überschreibt das automatische Verbergen des Menüeintrags "
+"»Abmelden« in Einzelbenutzer, Einzelsitzungssituationen."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Legt fest, ob Passwörter für eingehängte, verschlüsselte oder entfernte "
+"Dateisysteme gespeichert werden"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Die Shell wird ein Passwort anfragen, sobald ein verschlüsseltes Gerät oder "
+"ein entferntes Dateisystem eingehängt wird. Falls das Passwort für die "
+"Zukunft gespeichert werden kann, wird ein Ankreuzfeld »An Passwort erinnern« "
+"angeboten. Dieser Schlüssel stellt den Standardstatus für dieses Ankreuzfeld "
+"ein."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Das zuletzt ausgewählte Nicht-Standard-Energieprofil"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Einige Systeme unterstützen mehr als zwei Energieprofile. Damit das "
+"Umschalten zwischen zwei Profilen weiterhin möglich ist, speichert dieser "
+"Schlüssel das zuletzt ausgewählte Nicht-Standardprofil."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"Die letzte Version, für die der »Willkommen bei GNOME«-Dialog angezeigt wurde"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Dieser Schlüssel bestimmt, für welche Version der »Willkommen bei GNOME«-"
+"Dialog zuletzt angezeigt wurde. Eine leere Zeichenkette repräsentiert die "
+"älteste mögliche Version, eine riesige Zahl repräsentiert Versionen, die "
+"noch nicht existieren. Diese riesige Zahl kann dazu verwendet werden, diesen "
+"Dialog praktisch zu deaktivieren."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Ausrichtung der Anwendungsauswahl"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Ausrichtung der Anwendungsauswahl. Jeder Eintrag in der Liste ist eine "
+"Seite. Seiten sind in der Reihenfolge sortiert, in der sie in GNOME-Shell "
+"erscheinen. Jede Seite enthält eine Zuordnung »application id« → »data«. "
+"Aktuell werden die folgenden Werte innerhalb von »data« gespeichert: • "
+"»position«: Die Position des Anwendungssymbols auf der Seite"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Tastenkombination zum Öffnen des Anwendungsmenüs"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Tastenkombination zum Öffnen des Anwendungsmenüs."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Tastenkombination zum Wechseln zwischen Übersichts-Modi"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Tastenkombination zum Wechseln zwischen Sitzung, Fensterwähler und "
+"Anwendungsraster"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Tastenkombination zum Wechseln zwischen Anwendungsraster, Fensterwähler und "
+"Sitzung"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Tastenkombination zum Öffnen der »Anwendungen anzeigen«-Ansicht"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Tastenkombination zum Öffnen der »Anwendungen anzeigen«-Ansicht der "
+"Aktivitätenübersicht."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Tastenkombination zum Öffnen der Übersicht"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Tastenkombination zum Öffnen der »Aktivitäten«-Übersicht."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Tastenkombination zum Umschalten der Sichtbarkeit der Benachrichtigungsliste"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Tastenkombination zum Umschalten der Sichtbarkeit der Benachrichtigungsliste."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Tastenkombination zur Ausrichtung auf die aktiven Benachrichtigungen"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Tastenkombination zur Ausrichtung auf die aktiven Benachrichtigungen."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Zu Anwendung 1 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Zu Anwendung 2 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Zu Anwendung 3 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Zu Anwendung 4 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Zu Anwendung 5 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Zu Anwendung 6 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Zu Anwendung 7 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Zu Anwendung 8 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Zu Anwendung 9 wechseln"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Fensterwechsler auf aktuelle Arbeitsfläche einschränken."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Legt fest, ob nur Anwendungen im Fensterwechsler aufgeführt werden, die "
+"Fenster auf der aktuellen Arbeitsfläche haben. Andernfalls sind alle "
+"Anwendungen enthalten."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Der Modus der Anwendungssymbole."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Legt fest, wie Fenster im Fensterwechsler dargestellt werden. Mögliche Werte "
+"sind »thumbnail-only« (Vorschaubilder der Fenster werden angezeigt), »app-"
+"icon-only« (Nur das Anwendungssymbol wird angezeigt) oder »both« (beides)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Legt fest, ob Fenster der aktuellen Arbeitsfläche im Fensterwechsler "
+"aufgeführt werden. Andernfalls sind alle Fenster enthalten."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Orte"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Die von den Weltuhren anzuzeigenden Orte"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatischer Ort"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Legt fest, ob der aktuelle Ort automatisch bestimmt werden soll"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Ort"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Der Ort, für den ein Wetterbericht angezeigt werden soll"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Modalen Dialog an übergeordnetes Fenster binden"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Dieser Schlüssel überschreibt den Schlüssel in org.gnome.mutter, wenn die "
+"GNOME-Shell läuft."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Größenanpassung einschalten, wenn ein Fenster an die Bildschirmkante "
+"verschoben wird"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Arbeitsflächen dynamisch verwalten"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Arbeitsflächen nur auf dem primären Bildschirm"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Fokuswechsel im Mausmodus verzögern, bis sich der Zeiger nicht mehr bewegt"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Netzwerkanmeldung"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Etwas ist schiefgelaufen"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Es tut uns leid, aber es gibt ein Problem: Die Einstellungen für diese "
+"Erweiterung können nicht angezeigt werden. Wir empfehlen, dass Sie diesen "
+"Fehler den Autoren der Erweiterung melden."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Technische Details"
+
+# Warum nicht »Startseite«? - jb
+# »Homepage« hat sich m.M.n. im Kontext von Webseiten durchgesetzt. - ts
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Homepage"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Internetseite der Erweiterung besuchen"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Passwort"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Sitzung wählen"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Nicht aufgeführt?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(z.B. Benutzer oder %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Benutzername"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Anmeldefenster"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Anmeldung fehlgeschlagen"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(oder wischen Sie mit dem Finger über den Leser)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(oder legen Sie einen Finger auf den Leser)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Ausschalten"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "ausschalten;herunterfahren;halten;stoppen"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Neu starten"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "neustarten;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bildschirm sperren"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "Bildschirm sperren"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Abmelden"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "abmelden;verlassen"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Bereitschaft"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "Bereitschaft;Schlafen"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Benutzer wechseln"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "Benutzer wechseln"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "Hochformat;Querformat;sperren;entsperren;Bildschirm;Drehung;drehen"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Ein Bildschirmfoto aufnehmen"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"Screenshot;Bildschirmfoto;Screencast;Bildschirmaufzeichnung;Bildschirmvideo;"
+"Schnipsel;Schnitt;aufnehmen;Aufnahme;aufzeichnen"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Bildschirmdrehung entsperren"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Bildschirmdrehung sperren"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Befehl nicht gefunden"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Befehl konnte nicht verarbeitet werden:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Ausführung von »%s« ist gescheitert:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Gerade eben"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "vor %d Minute"
+msgstr[1] "vor %d Minuten"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "vor %d Stunde"
+msgstr[1] "vor %d Stunden"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Gestern"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "vor %d Tag"
+msgstr[1] "vor %d Tagen"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "vor %d Woche"
+msgstr[1] "vor %d Wochen"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "vor %d Monat"
+msgstr[1] "vor %d Monaten"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "vor %d Jahr"
+msgstr[1] "vor %d Jahren"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Gestern, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%e. %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%e. %B %Y, %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Gestern, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%e. %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%e. %B %Y, %l:%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Hotspot-Anmeldung"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Ihre Verbindung zum Anmeldefenster dieses Hotspots ist nicht sicher. "
+"Passwörter oder andere Daten, die Sie auf dieser Seite eingeben, können von "
+"anderen in unmittelbarer Umgebung ausgelesen werden."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Zugriff verhindern"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Zugriff gewähren"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Unbenannter Ordner"
+
+# Ich bin mir nicht sicher, ob wir nicht vielleicht einfach bei "hinzufügen"
+# und "entfernen" bleiben sollten. - ts
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s wurde an das Dash angeheftet."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s wurde vom Dash losgelöst."
+
+# Fensterüberschrift
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Offene Fenster"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Neues Fenster"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Details anzeigen"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Beenden"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Loslösen"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "An Dash anheften"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Mit integrierter Grafikkarte starten"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Mit dedizierter Grafikkarte starten"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Audio-Gerät auswählen"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Klangeinstellungen"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Kopfhörer"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Hintergrund ändern …"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Anzeigeeinstellungen"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Einstellungen"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Vorheriger Monat"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Nächster Monat"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Woche %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Keine Benachrichtigungen"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Nicht stören"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Leeren"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "»%s« antwortet nicht."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Sie können der Anwendung noch etwas Zeit geben oder ein sofortiges Beenden "
+"erzwingen."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Beenden erzwingen"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Warten"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Externes Laufwerk verbunden"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Externes Laufwerk getrennt"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Datenträger konnte nicht entsperrt werden"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+"Die PIM-Einstellung wird von der installierten udisks-Version nicht "
+"unterstützt"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Öffnen mit %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternativ können Sie sich verbinden, indem Sie den »WPS«-Knopf an Ihrem "
+"Router drücken."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Verbinden"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Schlüssel"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Passwort für geheimen Schlüssel"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identität"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Dienst"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Anmeldung erforderlich"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Passwörter oder Schlüssel sind erforderlich, um auf das Drahtlosnetzwerk "
+"»%s« zuzugreifen."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Kabelgebundene 802.1X-Anmeldung"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Netzwerkname"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL-Anmeldung"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN-Code ist erforderlich"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Für das mobile Breitbandgerät wird ein PIN-Code benötigt"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Es wird ein Passwort benötigt, um sich mit »%s« zu verbinden."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Netzwerk-Verwaltung"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN-Passwort"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Anmeldung erforderlich"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Systemverwalter"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Anmelden"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr ""
+"Entschuldigung, das hat nicht funktioniert. Bitte versuchen Sie es erneut."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s heißt jetzt %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Fenster"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Anwendungen anzeigen"
+
+# Würde ich so übernehmen, oder evtl. »Dock«.
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%e. %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e. %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%e. %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%e. %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Heute"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Morgen"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Ganztägig"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d.%m."
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Keine Termine"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Weltuhren hinzufügen …"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Weltuhren"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Wird geladen …"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Gehen Sie Online, um Wetterinformationen beziehen zu können"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Wetterinformationen sind momentan nicht verfügbar"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Wetter"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Wetterstandort auswählen …"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s abmelden"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Abmelden"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s wird automatisch in %d Sekunde abgemeldet."
+msgstr[1] "%s wird automatisch in %d Sekunden abgemeldet."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Sie werden automatisch in %d Sekunde abgemeldet."
+msgstr[1] "Sie werden automatisch in %d Sekunden abgemeldet."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Abmelden"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Ausschalten"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Aktualisierungen installieren und ausschalten"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Das System schaltet sich automatisch in %d Sekunde ab."
+msgstr[1] "Das System schaltet sich automatisch in %d Sekunden ab."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Ausstehende Softwareaktualisierungen installieren"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Ausschalten"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Neu starten"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Aktualisierungen installieren und neu starten"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Das System wird automatisch in %d Sekunde neu gestartet."
+msgstr[1] "Das System wird automatisch in %d Sekunden neu gestartet."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Neu starten"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Neu starten und Aktualisierungen installieren"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Das System wird automatisch in %d Sekunde neu gestartet und aktualisiert."
+msgstr[1] ""
+"Das System wird automatisch in %d Sekunden neu gestartet und aktualisiert."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Neu starten und installieren"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installieren und ausschalten"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Ausschalten, nachdem Aktualisierungen installiert wurden"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Neu starten und Aktualisierung installieren"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s wird nach dem Neustart installiert, was möglicherweise viel Zeit "
+"beanspruchen kann. Vergewissern Sie sich, zuvor eine Sicherungskopie des "
+"Systems erstellt zu haben und dass eine zuverlässige Stromzufuhr besteht."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Niedrige Akkuladung: Bitte schließen Sie Ihr Gerät an das Stromnetz an, "
+"bevor Sie Aktualisierungen installieren."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Einige Anwendungen sind beschäftigt oder haben ungesicherte Änderungen"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Andere Benutzer sind angemeldet"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Systemstart-Einstellungen"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (Entfernt)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (Konsole)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installieren"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Erweiterung installieren"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "»%s« von extensions.gnome.org herunterladen und installieren?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Aktualisierungen für Erweiterungen verfügbar"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Aktualisierungen für Erweiterungen stehen bereit zur Installation."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Sperrung von Tastenkombinationen erlauben"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Die Anwendung %s möchte Tastenkombinationen sperren"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Eine Anwendung möchte Tastenkombinationen sperren"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Sie können Tastenkombinationen durch Drücken von %s wiederherstellen."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Ablehnen"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Zulassen"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tastenverzögerung eingeschaltet"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tastenverzögerung ausgeschaltet"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Sie haben die Umschalttaste 8 Sekunden lang gedrückt gehalten. Dies ist die "
+"Tastenkombination für die Tastenverzögerung, welche beeinflusst, wie Ihre "
+"Tastatur funktioniert."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Einrastfunktion eingeschaltet"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Einrastfunktion ausgeschaltet"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Sie haben die Umschalttaste 5 Mal hintereinander gedrückt. Dies ist die "
+"Tastenkombination für die Einrastfunktion, welche beeinflusst, wie Ihre "
+"Tastatur funktioniert."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Sie haben zwei Tasten gleichzeitig oder die Umschalttaste 5 Mal "
+"hintereinander gedrückt. Dies ist die Tastenkombination für die "
+"Einrastfunktion, welche beeinflusst, wie Ihre Tastatur funktioniert."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Eingeschaltet lassen"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Einschalten"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Ausschalten"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Ausgeschaltet lassen"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Regions- und Spracheinstellungen"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Keine Erweiterungen installiert"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s hat keine Fehler ausgegeben."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Fehler verbergen"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Fehler anzeigen"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Eingeschaltet"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Ausgeschaltet"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Fehler"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Veraltet"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Herunterladen"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Quelle zeigen"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Webseite"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Das System wurde in einen unsicheren Modus versetzt"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Anwendungen haben nun uneingeschränkten Zugriff"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Rückgängig"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Als privilegierter Benutzer angemeldet"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Der Betrieb einer Sitzung als privilegierter Benutzer sollte aus "
+"Sicherheitsgründen unterlassen werden. Falls möglich, melden Sie sich bitte "
+"als normaler Benutzer an."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Bildschirmsperre deaktiviert"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Die Sperrung des Bildschirms erfordert den GNOME Display-Manager."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Systeminformationen"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Unbekannter Künstler"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Unbekannter Titel"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Suchbegriff eingeben"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Anwendungen"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Übersicht"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Neue Tastenkombination …"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Anwendung festgelegt"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Bildschirmhilfe anzeigen"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Bildschirm wechseln"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Tastenkombination zuweisen"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Erledigt"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Bearbeiten …"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Keine"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Klicken Sie auf einen Knopf zum Einrichten"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Drücken Sie Esc zum Abbrechen"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Drücken Sie eine beliebige Taste zum Beenden"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Aktivitäten"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "System"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Obere Leiste"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Einen Befehl ausführen"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Drücken Sie Esc zum Schließen"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Neu Starten ist unter Wayland nicht verfügbar"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Neustart …"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME muss den Bildschirm sperren"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Sperrung fehlgeschlagen"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Sperrung wurde von einer Anwendung blockiert"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Auswahl"
+
+# Tooltip zu einer der verschiedenen Bildschirmfoto-Varianten. Ich halte die
+# wortwörtliche Übersetzung "Bereichauswahl" für sperrig, daher bisschen
+# freier übersetzt - ts
+# In Gnome 41 ist es "Aufnahmebereich" und erscheint über den Ausfwahlfelder "Bildschirm", "Fenster" und "Auswahl"
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Aufnahmebereich"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Bildschirm"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Bildschirm aufnehmen"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Fenster"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Fenster aufnehmen"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Bildschirmfoto / Bildschirmaufzeichnung"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Mauszeiger anzeigen"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Bildschirmaufzeichnungen"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Bildschirmaufzeichnung vom %d, %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Bildschirmfoto"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Bildschirminhalt aufgezeichnet"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Klicken Sie hier, um das Video anzusehen."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "In Dateien anzeigen"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Bildschirmfoto vom %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Bildschirmfoto aufgenommen"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Sie können das Bild aus der Zwischenablage einfügen."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Bildschirmfoto aufgenommen"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Suche läuft …"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Keine Ergebnisse."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d weiteres"
+msgstr[1] "%d weitere"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Suchen"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopieren"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Einfügen"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Text anzeigen"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Text verbergen"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Feststelltaste ist aktiviert."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Verborgener Datenträger"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows-Systemdatenträger"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Verwendet Schlüsseldateien"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Um einen Datenträger zu entsperren, der Schlüsseldateien verwendet, nutzen "
+"Sie stattdessen das Dienstprogramm <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-Nummer"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "An Passwort erinnern"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Entsperren"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s öffnen"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "Die PIM muss eine Nummer oder leer sein."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s konnte nicht gestartet werden"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Die Anwendung %s konnte nicht gefunden werden"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Barrierefreiheit"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Hoher Kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Vergrößern"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Bildschirmleser"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Bildschirmtastatur"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Visuelle Warnungen"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Einrastfunktion"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tastenverzögerung"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Tastenanschlagfunktion"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Tastaturmaus"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Große Schrift"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatisches Drehen"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Helligkeit"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Dunkle Ansicht"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Einfacher Klick"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Doppelklick"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Ziehen"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Kontextklick"
+
+# https://pilotmoon.com/dwellclick/
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Verweilender Klick"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Tastatur"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Tastaturbelegung anzeigen"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Standortzugriff erlauben"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Die Anwendung %s möchte auf Ihren Standort zugreifen"
+
+# Privacy: Datenschutz? - jb
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Standortzugriff kann in den Einstellungen zur Privatsphäre jederzeit "
+"geändert werden."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<Unbekannt>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "%s trennen"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Verbinden mit %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Hotspot %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "VPN-Einstellungen"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Drahtlosnetzwerk"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Alle Netzwerke"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Kabelverbindungen"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "LAN-Einstellungen"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Gemeinsam genutzte Bluetooth-Verbindungen"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Bluetooth-Einstellungen"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Mobile Verbindungen"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Einstellungen der mobilen Breitbandverbindung"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Verbindung gescheitert"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Aktivierung der Netzwerkverbindung ist gescheitert"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Nachtmodus"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Leistung"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Ausgeglichen"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Energie sparen"
+
+# Heißt das auf Deutsch tatsächlich »Energieprofile«? Bei mir (Fedora 36, GNOME 42.4) habe ich in den Einstellungen nämlich die Kategorie »Energie-Modus«. Im entsprechenden Hilfe-Eintrag wird »Profil« und »Modus« recht beliebig gemischt. Oder sind das zwei verschiedene Punkte? - jb
+# Ohne mich tief eingelesen zu haben, gehe ich davon aus, dass es willkürlich ist. - ts
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Energieprofile"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Bildschirmaufzeichnung beenden"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Bildschirmfreigabe beenden"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Flugzeugmodus"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Bildschirmfoto aufnehmen"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Ausschalten-Menü"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Bereitschaft"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Neustart …"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Ausschalten …"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Abmelden …"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Benutzer wechseln …"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Bildschirm sperren"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Unbekanntes Thunderbolt-Gerät"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Ein neues Gerät ist in der Zwischenzeit erkannt worden. Bitte trennen Sie "
+"die Verbindung des Gerätes und schließen Sie es wieder an, um es zu "
+"verwenden."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Nicht legitimiertes Thunderbolt-Gerät"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Neue Geräte wurden entdeckt und müssen durch einen Administrator legitimiert "
+"werden."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt-Legitimierungsfehler"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Das Thunderbolt-Gerät %s konnte nicht legitimiert werden"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Lautstärke geändert"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Lautstärke"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Tonausgang"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Toneingang"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Spiegeln"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Bildschirme verketten"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Nur extern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Nur eingebaut"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %d. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Nach oben wischen zum Entsperren"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Durch Mausklick oder Tastendruck entsperren"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Fenster entsperren"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Als anderer Benutzer anmelden"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Willkommen bei GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Nutzen Sie die Einführung, um sich zurechtzufinden."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nein danke"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Einführung starten"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "»%s« ist bereit"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Diese Anzeigeeinstellungen beibehalten?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Zurücksetzen"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Änderungen beibehalten"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Die Änderungen der Einstellungen werden in %d Sekunde zurückgesetzt"
+msgstr[1] ""
+"Die Änderungen der Einstellungen werden in %d Sekunden zurückgesetzt"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Verbergen"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Wiederherstellen"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximieren"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Verschieben"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Größe verändern"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Titelleiste auf den Bildschirm verschieben"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Immer im Vordergrund"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Immer auf der sichtbaren Arbeitsfläche"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Eine Arbeitsfläche nach links verschieben"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Eine Arbeitsfläche nach rechts verschieben"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Eine Arbeitsfläche nach oben verschieben"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Eine Arbeitsfläche nach unten verschieben"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Auf den Bildschirm nach oben verschieben"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Auf den Bildschirm nach unten verschieben"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Auf den Bildschirm nach links verschieben"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Auf den Bildschirm nach rechts verschieben"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Schließen"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution-Kalender"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Version ausgeben"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Der durch GDM im Anmeldefenster verwendete Modus"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Verwenden Sie einen spezifischen Modus wie z.B. »gdm« für den "
+"Anmeldebildschirm"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Die möglichen Modi auflisten"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Unbekannt"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "»%s« konnte nicht gestartet werden"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Passwörter stimmen nicht überein."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Es muss ein Passwort angegeben werden"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Der Dialog zur Anmeldung wurde vom Benutzer geschlossen"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Erweiterungen"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Ihre GNOME-Erweiterungen verwalten"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Das GNOME-Projekt"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME-Erweiterungen dient der Aktualisierung von Erweiterungen, dem "
+"Konfigurieren der Voreinstellungen von Erweiterungen und dem Entfernen oder "
+"Deaktivieren nicht gewünschter Erweiterungen."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME-Shell-Erweiterungen einrichten"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Keine Treffer"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "»%s« entfernen?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Wenn Sie die Erweiterung entfernen, müssen Sie sie erneut herunterladen, um "
+"sie wieder zu aktivieren"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Entfernen"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Tim Sabsch <tim@sabsch.com>, 2020, 2022\n"
+"Christian Kirbach <christian.kirbach@gmail.com>, 2020,2021.2022\n"
+"Philipp Kiemle <philipp.kiemle@gmail.com>, 2021\n"
+"Jürgen Benvenuti <gastornis@posteo.org>, 2022"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d Erweiterung wird bei der nächsten Anmeldung aktualisiert."
+msgstr[1] "%d Erweiterungen werden bei der nächsten Anmeldung aktualisiert."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Diese Erweiterung ist inkompatibel mit der derzeitigen GNOME-Version"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Die Erweiterung hatte einen Fehler"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Die Erweiterung kann aktualisiert werden"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Webseite"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Entfernen …"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Hilfe"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Info zu Erweiterungen"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Erweiterungen können zu Beeinträchtigungen der Leistung und Stabilität "
+"führen. Deaktivieren Sie Erweiterungen, wenn Sie auf Probleme mit Ihrem "
+"System stoßen."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Manuell installiert"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Besuchen Sie <a href=\"https://extensions.gnome.org\">extensions.gnome.org</"
+"a>, um Erweiterungen zu finden und hinzuzufügen."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Mitgeliefert"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Keine installierten Erweiterungen"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Tut uns sehr leid, wir konnten keine Liste der installierten Erweiterungen "
+"aufstellen. Stellen Sie sicher, dass Sie in GNOME angemeldet sind und "
+"versuchen Sie es dann erneut."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Aktualisierungen für Erweiterungen stehen bereit"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Die neue Erweiterung wurde erfolgreich in %s erstellt.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Der Name sollte eine sehr kurze (idealerweise anschauliche) Zeichenkette "
+"sein.\n"
+"Beispiele sind: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Name"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Die Beschreibung erklärt in einem Satz, was Ihre Erweiterung beinhaltet.\n"
+"Beispiele sind: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Beschreibung"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"Die UUID ist eine eindeutige Kennung Ihrer Erweiterung.\n"
+"Sie sollte das Format einer E-Mail-Adresse besitzen "
+"(klickfokus@erikamustermann.beispiel.de)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Wählen Sie eine der verfügbaren Vorlagen:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Vorlage"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Die eindeutige Kennung der neuen Erweiterung"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Der sichtbare Name der neuen Erweiterung"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "BESCHREIBUNG"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Eine kurze Beschreibung der Erweiterung"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "VORLAGE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Die Vorlage für eine neue Erweiterung"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Informationen zur Erweiterung interaktiv eingeben"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Eine neue Erweiterung erstellen"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Unbekannte Argumente"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Es werden eine UUID, ein Name und eine Beschreibung benötigt"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Erweiterung »%s« existiert nicht\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Eine Erweiterung deaktivieren"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Keine UUID angegeben"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Mehr als eine UUID angegeben"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Eine Erweiterung aktivieren"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Fehler beim Verbinden zur GNOME-Shell\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Erweiterung »%s« existiert nicht\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Informationen zur Erweiterung anzeigen"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Eine bestehende Erweiterung überschreiben"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "ERWEITERUNGS_PAKET"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Ein Erweiterungspaket installieren"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Kein Erweiterungspaket angegeben"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Mehr als ein Erweiterungspaket angegeben"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Vom Benutzer installierte Erweiterungen anzeigen"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Vom System installierte Erweiterungen anzeigen"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Aktivierte Erweiterungen anzeigen"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Deaktivierte Erweiterungen anzeigen"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Erweiterungen mit Einstellungen anzeigen"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Erweiterungen mit Aktualisierungen anzeigen"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Erweiterungsdetails ausgeben"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Installierte Erweiterungen auflisten"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "DATEI"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Zusätzliche Quelle, die in das Paket eingebunden wird"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Ein GSettings-Schema, das eingebunden werden soll"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ORDNER"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Der Ordner, in dem die Übersetzungen enthalten sind"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMÄNE"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Die von gettext zu verwendende Übersetzungsdomäne"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Ein bestehendes Pack überschreiben"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Der Ordner, in dem das Pack erstellt werden soll"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "QUELL_ORDNER"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Ein Erweiterungspaket erstellen"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Mehr als ein Quellordner angegeben"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Erweiterung »%s« hat keine Einstellungen\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr ""
+"Das Öffnen der Einstellungen für die Erweiterung »%s« ist fehlgeschlagen: "
+"%s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Öffnet die Erweiterungseinstellungen"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Eine Erweiterung zurücksetzen"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Systemerweiterungen können nicht deinstalliert werden\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "»%s« konnte nicht deinstalliert werden\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Eine Erweiterung deinstallieren"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Keine Fehlermeldungen ausgeben"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Pfad"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "Adresse"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Originalersteller"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Version"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Status"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "»version« akzeptiert keine Argumente"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Aufruf:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Versionsinformationen ausgeben und beenden."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "BEFEHL"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTE …]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Befehle:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Hilfe ausgeben"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Erweiterung aktivieren"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Erweiterung deaktivieren"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Erweiterung zurücksetzen"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Erweiterung deinstallieren"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Erweiterungen auflisten"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Erweiterungsinformationen anzeigen"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Erweiterungseinstellungen öffnen"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Erweiterung erstellen"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Paket-Erweiterung"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Erweiterungspaket installieren"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Verwenden Sie »%s«, um detaillierte Hilfe zu erhalten.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Schlicht"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Eine leere Erweiterung"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Ein Symbol zu der oberen Leiste hinzufügen"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u Ausgang"
+msgstr[1] "%u Ausgänge"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u Eingang"
+msgstr[1] "%u Eingänge"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Systemklänge"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "Legt fest, ob der Standard-Bluetooth-Adapter zugeordnete Geräte hat"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Die Shell zeigt nur dann ein Bluetooth-Menü an, wenn ein Bluetooth-"
+#~ "Adapter betriebsbereit ist, oder wenn dem Standard-Bluetooth-Adapter in "
+#~ "der Vergangenheit Geräte zugeordnet waren. Sollte keine Gerätezuordnung "
+#~ "mehr vorhanden sein, so wird das Menü ausgeblendet."
+
+#~ msgid "Log Out"
+#~ msgstr "Abmelden"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s ausgeschaltet"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s verbunden"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s nicht verwaltet"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s wird verbunden"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s erfordert eine Anmeldung"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Firmware fehlt für %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s nicht verfügbar"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Verbindung von %s gescheitert"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s-Hardware ausgeschaltet"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s ausgeschaltet"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Mit dem Internet verbinden"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Flugmodus ist eingeschaltet"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr ""
+#~ "Drahtlosnetzwerk ist ausgeschaltet, wenn sich Ihr Gerät im Flugmodus "
+#~ "befindet."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Flugmodus ausschalten"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Drahtlosnetzwerk ist ausgeschaltet"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr ""
+#~ "Drahtlosnetzwerk muss eingeschaltet werden, um eine Netzwerkverbindung "
+#~ "herzustellen."
+
+# Wi-Fi einschalten
+# oder
+# Schalten sie Wi-Fi ein
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Drahtlosnetzwerk einschalten"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Drahtlosnetzwerke"
+
+#~ msgid "Select a network"
+#~ msgstr "Wählen Sie ein Netzwerk"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Benutzen Sie zum Ausschalten den Gehäuseschalter"
+
+#~ msgid "Select Network"
+#~ msgstr "Wählen Sie ein Netzwerk aus"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Drahtlosnetzwerk-Einstellungen"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s nicht verbunden"
+
+#~ msgid "unknown"
+#~ msgstr "unbekannt"
+
+#~ msgid "activating…"
+#~ msgstr "wird aktiviert …"
+
+#~ msgid "deactivating…"
+#~ msgstr "wird deaktiviert …"
+
+#~ msgid "deactivated"
+#~ msgstr "deaktiviert"
+
+#~ msgid "connecting…"
+#~ msgstr "Verbindungsaufbau …"
+
+#~ msgid "authentication required"
+#~ msgstr "Anmeldung erforderlich"
+
+#~ msgid "connection failed"
+#~ msgstr "Verbindung gescheitert"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN ausgeschaltet"
+
+#~ msgid "Network Settings"
+#~ msgstr "Netzwerkeinstellungen"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Funknetzwerkverbindung"
+#~ msgstr[1] "%s Funknetzwerkverbindungen"
+
+#~ msgid "Power Settings"
+#~ msgstr "Energieeinstellungen"
+
+#~ msgid "Lock"
+#~ msgstr "Sperren"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Ausschalten / Abmelden"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d verbunden"
+#~ msgstr[1] "%d verbunden"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth An"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth Aus"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Standort eingeschaltet"
+
+#~ msgid "Disable"
+#~ msgstr "Ausschalten"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Einstellungen zur Privatsphäre"
+
+# Siehe https://mail.gnome.org/archives/gnome-de/2021-March/msg00080.html
+#~ msgid "Location In Use"
+#~ msgstr "Standort wird verwendet"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Standort ausgeschaltet"
+
+#~ msgid "Enable"
+#~ msgstr "Einschalten"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Nachtmodus deaktiviert"
+
+#~ msgid "Resume"
+#~ msgstr "Fortsetzen"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Bis morgen deaktivieren"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Vollständig geladen"
+
+#~ msgid "Not Charging"
+#~ msgstr "Lädt nicht"
+
+#~ msgid "Estimating…"
+#~ msgstr "Schätzung …"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d verbleibend (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d bis geladen (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Bildschirm wird zur Zeit geteilt"
+
+#~ msgid "Turn off"
+#~ msgstr "Ausschalten"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Aus Favoriten entfernen"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Zu Favoriten hinzufügen"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Warnung"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Erweiterungen können Probleme am System verursachen, darunter auch "
+#~ "Leistungsprobleme. Sollten Sie auf Probleme mit Ihrem System stoßen, wird "
+#~ "die Deaktivierung aller Erweiterungen empfohlen."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Introspection-API aktivieren"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Aktiviert eine D_Bus-API, die Einblicke in den Anwendungsstatus der Shell "
+#~ "gewährt."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Fehler beim Verbinden zur GNOME Shell"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimieren"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Maximieren aufheben"
+
+#~ msgid "App Picker View"
+#~ msgstr "Anwendungsauswahlanzeige"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Index der aktuell gewählten Ansicht in der Anwendungsauswahl."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Häufig genutzte Anwendungen erscheinen hier"
+
+#~ msgid "Frequent"
+#~ msgstr "Häufig"
+
+#~ msgid "All"
+#~ msgstr "Alle"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %d. %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %d. %B %Y"
+
+#~ msgid "Off"
+#~ msgstr "Aus"
+
+#~ msgid "On"
+#~ msgstr "An"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopierfehler"
+
+#~ msgid "Username…"
+#~ msgstr "Benutzername …"
+
+#~ msgid "Password: "
+#~ msgstr "Passwort: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Passwort eingeben …"
+
+#~ msgid "Browse in Software"
+#~ msgstr "»Software« durchsuchen"
+
+#~ msgid "Next"
+#~ msgstr "Weiter"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Anmelden"
+
+#~ msgid "Rename"
+#~ msgstr "Umbenennen"
+
+#~ msgid "Type again:"
+#~ msgstr "Erneut eingeben:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Anmeldung für Drahtlosnetzwerk wird benötigt"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Passwort der mobilen Breitbandverbindung"
+
+# %A - ausgeschriebener Name des Wochentages, abhängig von der gesetzten
+# Umgebung
+# %d - Tag des Monats als Zahl (Bereich 01 bis 31)
+# %B - ausgeschriebener Name des Monats, abhängig von der gesetzten Umgebung
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d. %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d neue Nachricht"
+#~ msgstr[1] "%d neue Nachrichten"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d neue Benachrichtigung"
+#~ msgstr[1] "%d neue Benachrichtigungen"
+
+#~ msgid "Account Settings"
+#~ msgstr "Kontoeinstellungen"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Hoch-/Querformats-Fixierung"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Tastenkombination, die alle laufenden Zwischenbilder anhält und "
+#~ "fortsetzt. Zur Fehlerdiagnose."
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Zu verwendende Tastatur"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Der Typ der zu verwendenden Tastatur"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr ""
+#~ "Beim Laden des Einstellungsdialogs für %s ist ein Fehler aufgetreten:"
+
+#~ msgid "%s all day."
+#~ msgstr "Ganztägig %s."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, später %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, später %s, gefolgt von %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Gefühlt wie %s."
diff --git a/po/el.po b/po/el.po
new file mode 100644
index 0000000..b732a6c
--- /dev/null
+++ b/po/el.po
@@ -0,0 +1,4266 @@
+# Kostas Papadimas <pkst@gnome.org>, 2012.
+# Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>, 2013, 2014, 2015.
+# Vangelis Skarmoutsos <skarmoutsosv@gmail.com>, 2013.
+# Efstathios Iosifidis <iosifidis@opensuse.org>, 2013-2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell.po.master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-20 09:42+0000\n"
+"PO-Revision-Date: 2022-10-03 00:38+0300\n"
+"Last-Translator: Kosmas Martakidis <kosmmartak@gmail.com>\n"
+"Language-Team: Greek, Modern (1453-) <gnome-el-list@gnome.org>\n"
+"Language: el\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.1.1\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Εκκινητές"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Ενεργοποίηση αγαπημένης εφαρμογής 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Στιγμιότυπα οθόνης"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Λήψη στιγμιότυπου οθόνης διαδραστικά"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Λήψη στιγμιότυπου οθόνης"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Λήψη στιγμιότυπου παραθύρου"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Καταγραφή ενός βίντεο οθόνης διαδραστικά"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Σύστημα"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Εμφάνιση της λίστας ειδοποιήσεων"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Εστίαση στην ενεργή ειδοποίηση"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Εμφάνιση της επισκόπησης"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Εμφάνιση όλων των εφαρμογών"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Άνοιγμα του μενού εφαρμογών"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Κέλυφος GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Διαχείριση παραθύρων και εκκίνηση εφαρμογών"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Ενεργοποίηση εσωτερικών εργαλείων χρήσιμων για τους προγραμματιστές και τους "
+"δοκιμαστές, με το Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Επιτρέπει την πρόσβαση σε εσωτερικά εργαλεία αποσφαλμάτωσης και "
+"παρακολούθησης, με τη χρήση του διαλόγου Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Τα UUID των επεκτάσεων που θα ενεργοποιηθούν"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Οι επεκτάσεις του Κελύφους GNOME έχουν μια ιδιότητα UUID. Αυτό το κλειδί "
+"εμφανίζει τις επεκτάσεις που πρέπει να φορτωθούν. Οποιαδήποτε επέκταση που "
+"επιθυμεί να φορτωθεί πρέπει να είναι στον κατάλογο. Μπορείτε ακόμη να "
+"χειρισθείτε αυτόν τον κατάλογο με τις μεθόδους διαύλου δεδομένων "
+"EnableExtension και DisableExtension στο org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Τα UUID των επεκτάσεων για εξαναγκαστική απενεργοποίηση"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Οι επεκτάσεις του Κελύφους GNOME έχουν μια ιδιότητα UUID. Αυτό το κλειδί "
+"εμφανίζει τις επεκτάσεις που πρέπει να απενεργοποιηθούν, ακόμα και αν "
+"φορτωθεί ως μέρος της τρέχουσας λειτουργίας. Μπορείτε επίσης να χειριστείτε "
+"αυτήν τη λίστα με τις μεθόδους EnableExtension και DisableExtension D-Bus "
+"στο org.gnome.Shell. Αυτό το κλειδί υπερισχύει της ρύθμισης «enabled-"
+"extensions»."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Απενεργοποίηση επεκτάσεων χρήστη"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Απενεργοποίηση όλων των ενεργοποιημένων επεκτάσεων του χρήστη χωρίς να "
+"επηρεαστεί η ρύθμιση «enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Απενεργοποιεί την επικύρωση της συμβατότητας έκδοσης επέκτασης"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Το Κέλυφος GNOME θα φορτώσει μόνο τις επεκτάσεις που ισχυρίζονται ότι "
+"υποστηρίξουν την τρέχουσα εκτελούμενη έκδοση. Η ενεργοποίηση αυτής της "
+"επιλογής θα απενεργοποιήσει αυτόν τον έλεγχο και θα προσπαθήσει να φορτώσει "
+"όλες τις επεκτάσεις ανεξάρτητα από τις εκδόσεις που ισχυρίζονται ότι "
+"υποστηρίζουν."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Κατάλογος αναγνωριστικών αρχείων επιφάνειας εργασίας για προτιμώμενες "
+"εφαρμογές"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Οι εφαρμογές που αντιστοιχούν σε αυτά τα αναγνωριστικά θα εμφανισθούν στην "
+"περιοχή αγαπημένων."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Ιστορικό του διαλόγου εντολών (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Ιστορικό του διαλόγου looking glass"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Να εμφανίζεται πάντα το στοιχείο μενού «Αποσύνδεση» στο μενού χρήστη."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Αυτό το κλειδί υπερισχύει της αυτόματης απόκρυψης του στοιχείου μενού "
+"«Αποσύνδεση» σε μεμονωμένο χρήστη, σε καταστάσεις μεμονωμένης συνεδρίας."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Αν θα θυμάται τον κωδικό πρόσβασης για προσάρτηση κρυπτογραφημένων ή "
+"απομακρυσμένων συστημάτων αρχείων"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Το κέλυφος θα ζητήσει έναν κωδικό πρόσβασης όταν προσαρτάται μια "
+"κρυπτογραφημένη συσκευή ή ένα απομακρυσμένο σύστημα αρχείων. Εάν ο κωδικός "
+"πρόσβασης μπορεί να αποθηκευθεί για μελλοντική χρήση, θα παρουσιάζεται ένα "
+"πλαίσιο ελέγχου «Απομνημόνευση κωδικού πρόσβασης». Αυτό το κλειδί ορίζει την "
+"προεπιλεγμένη κατάσταση του πλαισίου επιλογής."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Το τελευταία επιλεγμένο μη-προεπιλεγμένο προφίλ ισχύος"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Ορισμένα συστήματα υποστηρίζουν παραπάνω από δύο προφίλ ισχύος. Για να "
+"συνεχίζουν να υποστηρίζουν την εναλλαγή μεταξύ δύο προφίλ, αυτό το κλειδί "
+"καταγράφει το τελευταία επιλεγμένο μη-προεπιλεγμένο προφίλ ισχύος."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"Η τελευταία έκδοση του παραθύρου διαλόγου \"Καλωσορίσατε στο GNOME\" έχει "
+"προβληθεί για"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Αυτό το κλειδί καθορίζει για ποια έκδοση έχει προβληθεί τελευταία το "
+"παράθυρο διαλόγου \"Καλωσορίσατε στο GNOME\". Μια κενή συμβολοσειρά "
+"αντιπροσωπεύει την παλαιότερη δυνατή έκδοση, και ένας μεγάλος αριθμός θα "
+"αντιπροσωπεύει εκδόσεις που ακόμα δεν υπάρχουν. Αυτός ο μεγάλος αριθμός "
+"μπορεί να χρησιμοποιηθεί ουσιαστικά για να απενεργοποιηθεί το παράθυρο "
+"διαλόγου."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Διάταξη του επιλογέα εφαρμογών"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Διάταξη του επιλογέα εφαρμογών. Κάθε καταχώρηση στον πίνακα είναι μια "
+"σελίδα. Οι σελίδες αποθηκεύονται με τη σειρά που εμφανίζονται στο Κέλυφος "
+"GNOME. Κάθε σελίδα περιλαμβάνει ένα ζεύγος “application id” → 'data'. Μέχρι "
+"τώρα, οι ακόλουθες τιμές αποθηκεύονται ως 'data': • “position”: η θέση του "
+"εικονιδίου εφαρμογής στη σελίδα"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Συνδυασμός πλήκτρων για το άνοιγμα του μενού εφαρμογών"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Συνδυασμός πλήκτρων για το άνοιγμα του μενού εφαρμογών."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Συνδυασμός πλήκτρων για εναλλαγή μεταξύ καταστάσεων επισκόπησης"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Συνδυασμός πλήκτρων για εναλλαγή μεταξύ συνεδρίας, επιλογέα παραθύρου και "
+"πλέγματος εφαρμογών"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Συνδυασμός πλήκτρων για εναλλαγή μεταξύ πλέγματος εφαρμογών, επιλογέα "
+"παραθύρου και συνεδρίας"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Συνδυασμός πλήκτρων για το άνοιγμα της προβολής «Εμφάνιση εφαρμογών»"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Συνδυασμός πλήκτρων για το άνοιγμα της προβολής «Εμφάνιση εφαρμογών» της "
+"επισκόπησης δραστηριοτήτων."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Συνδυασμός πλήκτρων για το άνοιγμα της επισκόπησης"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Συνδυασμός πλήκτρων για το άνοιγμα της επισκόπησης Δραστηριοτήτων."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Συνδυασμός πλήκτρων για εναλλαγή ορατότητας της λίστας ειδοποιήσεων"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Συνδυασμός πλήκτρων για εναλλαγή ορατότητας της λίστας ειδοποιήσεων."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Εναλλαγή στην εφαρμογή 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Εναλλαγή στην εφαρμογή 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Εναλλαγή στην εφαρμογή 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Εναλλαγή στην εφαρμογή 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Εναλλαγή στην εφαρμογή 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Εναλλαγή στην εφαρμογή 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Εναλλαγή στην εφαρμογή 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Εναλλαγή στην εφαρμογή 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Εναλλαγή στην εφαρμογή 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Περιορισμός εναλλαγής παραθύρων στον τρέχοντα χώρο εργασίας."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Αν είναι αληθές, μόνο εφαρμογές που έχουν παράθυρα στον τρέχοντα χώρο "
+"εργασίας εμφανίζονται κατά την εναλλαγή παραθύρων. Αλλιώς, θα "
+"περιλαμβάνονται όλες οι εφαρμογές."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Η λειτουργία εικονιδίου εφαρμογής."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Διαμορφώνει πώς θα εμφανίζονται τα παράθυρα κατά την εναλλαγή των παραθύρων. "
+"Έγκυρες ρυθμίσεις είναι «thumbnail-only» (εμφανίζει μια μικρογραφία του "
+"παραθύρου), «app-icon-only» (εμφανίζει μόνο το εικονίδιο της εφαρμογής) ή "
+"«both» - και τα δύο."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Αν είναι αληθές, μόνο παράθυρα του τρέχοντα χώρου εργασίας εμφανίζονται κατά "
+"την εναλλαγή παραθύρων. Αλλιώς, θα περιλαμβάνονται όλα τα παράθυρα."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Τοποθεσίες"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Οι τοποθεσίες που θα εμφανίζονται στα παγκόσμια ρολόγια"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Αυτόματη τοποθεσία"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Εάν θα γίνεται λήψη της τρέχουσας τοποθεσίας ή όχι"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Τοποθεσία"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Η τοποθεσία για την οποία θα εμφανιστεί μια πρόβλεψη καιρού"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Προσάρτηση αποκλειστικού διαλόγου στο γονικό παράθυρο"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Αυτό το κλειδί υπερισχύει του κλειδιού που υπάρχει στο org.gnome.mutter όταν "
+"εκτελείται το GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Ενεργοποίηση της προσάρτησης άκρων, όταν αφήνετε παράθυρα στις άκρες της "
+"οθόνης"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Η διαχείριση των χώρων γίνεται δυναμικά"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Χώροι εργασίας μόνο στην κύρια οθόνη"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Καθυστέρηση εστίασης αλλαγών στην λειτουργία ποντικιού μέχρι να σταματήσει "
+"να κινείται ο δείκτης"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Σύνδεση δικτύου"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Κάτι πήγε στραβά"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Λυπούμαστε πολύ, αλλά παρουσιάστηκε πρόβλημα: οι ρυθμίσεις για αυτήν την "
+"επέκταση δεν είναι δυνατό να εμφανιστούν. Σας συνιστούμε να αναφέρετε το "
+"ζήτημα στους συγγραφείς επέκτασης."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Τεχνικές λεπτομέρειες"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Ιστοσελίδα"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Επισκεφθείτε την ιστοσελίδα της επέκτασης"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Ακύρωση"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Κωδικός πρόσβασης"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Επιλογή συνεδρίας"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Δεν είστε στη λίστα;"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(π.χ, χρήστης ή %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Όνομα χρήστη"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Παράθυρο σύνδεσης"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Σφάλμα πιστοποίησης"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(ή περάστε το δάκτυλο σας από τον αναγνώστη δακτυλικών αποτυπωμάτων)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(ή τοποθετήστε το δάκτυλο σας στον αναγνώστη δακτυλικών αποτυπωμάτων)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Τερματισμός"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "απενεργοποίηση;τερματισμός;αναστολή"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Επανεκκίνηση"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "επανεκκίνηση;επανεκκίνηση;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Κλείδωμα οθόνης"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "κλείδωμα οθόνης"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Αποσύνδεση"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "αποσύνδεση;αποσύνδεση;logout;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Αναστολή"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "αναστολή;αδράνεια;suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Αλλαγή χρήστη"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "αλλαγή χρήστη"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "κλείδωμα προσανατολισμού;ξεκλείδωμα προσανατολισμού;οθόνη;περιστροφή"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Λήψη στιγμιότυπου οθόνης"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "στιγμιότυπο οθόνης;βίντεο οθόνης;απόκομμα;καταγραφή;εγγραφή"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Ξεκλείδωμα περιστροφής οθόνης"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Κλείδωμα περιστροφής οθόνης"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Δε βρέθηκε η εντολή"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Δεν ήταν δυνατό να γίνει ανάλυση της εντολής:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Απέτυχε η εκτέλεση του «%s»:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Μόλις τώρα"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d λεπτό πριν"
+msgstr[1] "%d λεπτά πριν"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d ώρα πριν"
+msgstr[1] "%d ώρες πριν"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Χθες"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d ημέρα πριν"
+msgstr[1] "%d ημέρες πριν"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d εβδομάδα πριν"
+msgstr[1] "%d εβδομάδες πριν"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d μήνας πριν"
+msgstr[1] "%d μήνες πριν"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d χρόνο πριν"
+msgstr[1] "%d χρόνια πριν"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Χθες, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Χθες %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Σύνδεση σημείου πρόσβασης"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Η σύνδεσή σας σε αυτό το σημείο πρόσβασης δεν είναι ασφαλής. Κωδικοί "
+"πρόσβασης ή άλλες πληροφορίες που εισάγετε σε αυτή τη σελίδα είναι ορατά από "
+"τους ανθρώπους που βρίσκονται κοντά σας."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Άρνηση πρόσβασης"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Παραχώρηση πρόσβασης"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Άτιτλος φάκελος"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "Το %s προστέθηκε στο ταμπλό."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Το %s αφαιρέθηκε από το ταμπλό."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Άνοιγμα παραθύρων"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Νέο παράθυρο"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Εμφάνιση λεπτομερειών"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Έξοδος"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Ξεκαρφίτσωμα"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Καρφίτσωμα στο ταμπλό"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Εκκίνηση με χρήση της ενσωματωμένης κάρτας γραφικών"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Εκκίνηση με χρήση της διακριτικής κάρτας γραφικών"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Επιλογή συσκευής εισόδου ήχου"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Ρυθμίσεις ήχου"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Ακουστικά"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Ακουστικά ανοιχτής συνομιλίας"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Μικρόφωνο"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Αλλαγή παρασκηνίου…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Ρυθμίσεις οθόνης"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Ρυθμίσεις"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Κυ"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Δε"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Τρ"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Τε"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Πε"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Πα"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Σα"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Προηγούμενος μήνας"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Επόμενος μήνας"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Εβδομάδα %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Καμία ειδοποίηση"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Μην ενοχλείτε"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Εκκαθάριση"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "Το «%s» δεν ανταποκρίνεται."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Μπορείτε να επιλέξετε να περιμένετε λίγο για να συνεχίσει ή να αναγκάσει την "
+"εφαρμογή να τερματίσει τελείως."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Εξαναγκασμός εξόδου"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Αναμονή"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Συνδέθηκε εξωτερικός δίσκος"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Αποσυνδέθηκε εξωτερικός δίσκος"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Αδυναμία ξεκλειδώματος τόμου"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Η εγκατεστημένη έκδοση udisks δεν υποστηρίζει τη ρύθμιση PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Άνοιγμα με %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Εναλλακτικά, μπορείτε να συνδεθείτε πιέζοντας το κουμπί «WPS» στο "
+"δρομολογητή σας."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Σύνδεση"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Κλειδί"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Συνθηματικό ιδιωτικού κλειδιού"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Ταυτότητα"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Υπηρεσία"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Απαιτείται πιστοποίηση"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Απαιτούνται κωδικοί πρόσβασης ή κλειδιά κρυπτογράφησης για πρόσβαση στο "
+"ασύρματο δίκτυο “%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Πιστοποίηση ενσύρματου 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Όνομα δικτύου"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Πιστοποίηση DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Απαιτείται κωδικός PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Ο κωδικός PIN απαιτείται για τη συσκευή κινητής ευρυζωνικής σύνδεσης"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Απαιτείται κωδικός πρόσβασης για σύνδεση στο «%s»."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Διαχειριστής δικτύου"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Συνθηματικό VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Απαιτείται πιστοποίηση"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Διαχειριστής"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Πιστοποίηση"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Συγγνώμη, αυτό δεν λειτούργησε. Παρακαλούμε προσπαθήστε ξανά."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Ο %s είναι τώρα γνωστός ως %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Παράθυρα"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Εμφάνιση εφαρμογών"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Ταμπλό"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Σήμερα"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Αύριο"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Όλη μέρα"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Κανένα συμβάν"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Προσθήκη παγκόσμιων ρολογιών…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Παγκόσμια ρολόγια"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Φορτώνεται…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Συνδεθείτεγια πληροφορίες καιρού"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Οι πληροφορίες καιρού είναι προσωρινά μη διαθέσιμες"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Καιρός"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Επιλέξτε τοποθεσία καιρού…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Αποσύνδεση %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Αποσύνδεση"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Ο %s θα αποσυνδεθεί αυτόματα σε %d δευτερόλεπτο."
+msgstr[1] "Ο %s θα αποσυνδεθεί αυτόματα σε %d δευτερόλεπτα."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Θα αποσυνδεθείτε αυτόματα σε %d δευτερόλεπτο."
+msgstr[1] "Θα αποσυνδεθείτε αυτόματα σε %d δευτερόλεπτα."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Αποσύνδεση"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Τερματισμός"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Εγκατάσταση ενημερώσεων & τερματισμός"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Το σύστημα θα τερματισθεί αυτόματα σε %d δευτερόλεπτο."
+msgstr[1] "Το σύστημα θα τερματισθεί αυτόματα σε %d δευτερόλεπτα."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Εγκατάσταση εκκρεμών ενημερώσεων λογισμικού"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Τερματισμός"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Επανεκκίνηση"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Εγκατάσταση ενημερώσεων & επανεκκίνηση"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Το σύστημα θα επανεκκινήσει αυτόματα σε %d δευτερόλεπτο."
+msgstr[1] "Το σύστημα θα επανεκκινήσει αυτόματα σε %d δευτερόλεπτα."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Επανεκκίνηση"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Επανεκκίνηση & εγκατάσταση ενημερώσεων"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Το σύστημα θα επανεκκινήσει αυτόματα και θα εγκαταστήσει ενημερώσεις σε %d "
+"δευτερόλεπτο."
+msgstr[1] ""
+"Το σύστημα θα επανεκκινήσει αυτόματα και θα εγκαταστήσει ενημερώσεις σε %d "
+"δευτερόλεπτα."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Επανεκκίνηση &amp; εγκατάσταση"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Εγκατάσταση &amp; τερματισμός"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Τερματισμός μετά την εγκατάσταση ενημερώσεων"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Επανεκκίνηση & εγκατάσταση ενημερώσεων"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Το %s %s θα εγκατασταθεί μετά την επανεκκίνηση. Η διαδικασία αναβάθμισης "
+"μπορεί να πάρει αρκετή ώρα: σιγουρευτείτε ότι έχετε αντίγραφα ασφαλείας και "
+"πως ο υπολογιστής σας είναι συνδεδεμένος στην παροχή ρεύματος."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Χαμηλό επίπεδο μπαταρίας: Παρακαλούμε συνδεθείτε στην πρίζα πριν την "
+"εγκατάσταση των ενημερώσεων."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+"Κάποιες εφαρμογές είναι απασχολημένες ή έχουν δεδομένα που δεν αποθηκεύτηκαν"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Είναι συνδεδεμένοι άλλοι χρήστες"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Ρυθμήσεις Εκκίνησης"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (απομακρυσμένο)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (κονσόλα)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Εγκατάσταση"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Εγκατάσταση επέκτασης"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Λήψη και εγκατάσταση του «%s» από το extensions.gnome.org;"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Διαθέσιμες ενημερώσεις επεκτάσεων"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Οι ενημερώσεις των επεκτάσεων είναι έτοιμες προς εγκατάσταση."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Να επιτρέπεται η αναστολή των συντομεύσεων"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Η εφαρμογή %s θέλει να αναστείλει τις συντομεύσεις"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Μια εφαρμογή θέλει να αναστείλει τις συντομεύσεις"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Μπορείτε να επαναφέρετε τις συντομεύσεις πιέζοντας το %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Απόρριψη"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Αποδοχή"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Τα Αργά Πλήκτρα ενεργοποιήθηκαν"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Τα Αργά Πλήκτρα απενεργοποιήθηκαν"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Μόλις πατήσατε το πλήκτρο Shift για 8 δευτερόλεπτα. Αυτή είναι η συντόμευση "
+"για τη λειτουργία Αργά Πλήκτρα, που επηρεάζει τον τρόπο λειτουργίας του "
+"πληκτρολογίου σας."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Κολλώδη πλήκτρα ενεργοποιήθηκαν"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Κολλώδη πλήκτρα απενεργοποιήθηκαν"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Πατήσατε το πλήκτρο Shift 5 φορές στη σειρά. Αυτή είναι η συντόμευση για τη "
+"λειτουργία Κολλώδη πλήκτρα, που επηρεάζει τον τρόπο λειτουργίας του "
+"πληκτρολογίου σας."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Πατήσατε 2 πλήκτρα μονομιάς, ή πατήσατε το πλήκτρο Shift 5 φορές στη σειρά . "
+"Αυτό απενεργοποιεί τη λειτουργία Κολλώδη πλήκτρα, που επηρεάζει τον τρόπο "
+"λειτουργίας του πληκτρολογίου σας."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Άφησε ενεργό"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Ενεργοποίηση"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Απενεργοποίηση"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Άφησε ανενεργό"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Ρυθμίσεις περιοχής και γλώσσας"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Δεν υπάρχουν εγκατεστημένες επεκτάσεις"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "Το %s δεν έδωσε σφάλματα."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Απόκρυψη σφαλμάτων"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Εμφάνιση σφαλμάτων"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Ενεργοποιημένο"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Απενεργοποιημένο"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Σφάλμα"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Μη ενημερωμένο"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Γίνεται λήψη"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Προβολή πηγής"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Ιστοσελίδα"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Το σύστημα τέθηκε σε ανασφαλή λειτουργία"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Οι εφαρμογές πλέον έχουν απεριόριστη πρόσβαση"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Αναίρεση"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Συνδεθήκατε ως προνομιούχος χρήστης"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Η εκτέλεση μιας περιόδου σύνδεσης ως προνομιούχου χρήστη θα πρέπει να "
+"αποφεύγεται για λόγους ασφαλείας. Εάν είναι δυνατόν, θα πρέπει να συνδεθείτε "
+"ως κανονικός χρήστης."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Το κλείδωμα οθόνης απενεργοποιήθηκε"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Το κλείδωμα οθόνης απαιτεί τον διαχειριστή οθόνης του GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Πληροφορίες συστήματος"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Άγνωστος καλλιτέχνης"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Άγνωστος τίτλος"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Πληκτρολογήστε για αναζήτηση"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Εφαρμογές"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Επισκόπηση"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Νέα συντόμευση…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Καθορισμένη εφαρμογή"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Εμφάνιση βοήθειας στην οθόνη"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Εναλλαγή οθόνης"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Αντιστοίχη πλήκτρων"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Έτοιμο"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Επεξεργασία…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Κανένα"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Πατήστε ένα κουμπί για ρύθμιση"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Πατήστε Esc για έξοδο"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Πατήστε οποιοδήποτε πλήκτρο για έξοδο"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Δραστηριότητες"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Σύστημα"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Πάνω μπάρα"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Εκτέλεση εντολής"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Πατήστε Esc για κλείσιμο"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Η επανεκκίνηση δεν είναι διαθέσιμη στο Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Επανεκκίνηση…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Το GNOME χρειάζεται να κλειδώσει την οθόνη"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Αδυναμία κλειδώματος"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Το κλείδωμα εμποδίσθηκε από μια εφαρμογή"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Επιλογή"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Επιλογή περιοχής"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Οθόνη"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Επιλογή οθόνης"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Παράθυρο"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Επιλογή παραθύρου"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Στιγμιότυπο/βίντεο οθόνης"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Εμφάνιση δρομέα"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Βίντεο οθόνης"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Βίντεο οθόνης από %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Στιγμιότυπο οθόνης"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Καταγράφηκε βίντεο οθόνης"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Κάνετε κλικ εδώ για προβολή του βίντεο."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Εμφάνιση στα Αρχεία"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Στιγμιότυπο οθόνης από %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Καταγράφηκε στιγμιότυπο οθόνης"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Μπορείτε να επικολλήσετε την εικόνα από το πρόχειρο."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Ελήφθη στιγμιότυπο οθόνης"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Αναζήτηση…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Δε βρέθηκαν αποτελέσματα."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d περισσότερο"
+msgstr[1] "%d περισσότερες"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Αναζήτηση"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Αντιγραφή"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Επικόλληση"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Εμφάνιση κειμένου"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Απόκρυψη κειμένου"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Τα κεφαλαία είναι ενεργοποιημένα."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Κρυφός τόμος"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Τομέας συστήματος Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Χρησιμοποιεί αρχεία κλειδιά"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Για να ξεκλειδώσετε έναν τόμο που χρησιμοποιεί κλειδιά, χρησιμοποιήστε το "
+"βοηθητικό εργαλείο <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Αριθμός PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Απομνημόνευση κωδικού πρόσβασης"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Ξεκλείδωμα"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Άνοιγμα %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "Το PIM πρέπει να είναι νούμερο ή κενό."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Αδυναμία εκκίνησης %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Αδύνατη η εύρεση της εφαρμογής %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Προσιτότητα"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Υψηλή αντίθεση"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Εστίαση"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Αναγνώστης οθόνης"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Πληκτρολόγιο οθόνης"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Οπτικές ειδοποιήσεις"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Κολλώδη πλήκτρα"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Αργά πλήκτρα"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Πλήκτρα αναπήδησης"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Πλήκτρα ποντικιού"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Μεγάλο κείμενο"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Αυτόματη περιστροφή"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Φωτεινότητα"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Σκούρο θέμα"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Μονό κλικ"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Διπλό κλικ"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Σύρσιμο"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Δευτερεύον κλικ"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Κράτημα κλικ"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Πληκτρολόγιο"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Εμφάνιση διάταξης πληκτρολογίου"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Να επιτρέπεται η πρόσβαση τοποθεσίας"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Η εφαρμογή %s θέλει να έχει πρόσβαση στην τοποθεσία σας"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Η πρόσβαση τοποθεσίες μπορεί να αλλαχτεί οποιαδήποτε στιγμή από τις "
+"ρυθμίσεις ιδιωτικότητας."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<άγνωστο>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Αποσύνδεση %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Σύνδεση %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Σημείο πρόσβασης %s"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Λειτουργία νυχτός"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Απόδοση"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Ισοσταθμισμένα"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Εξοικονόμηση ενέργειας"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Προφίλ ισχύος"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Διακοπή καταγραφής βίντεο οθόνης"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Διακοπή κοινής χρήσης οθόνης"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Λειτουργία πτήσης"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Λήψη στιγμιότυπου οθόνης"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Μενού τερματισμού"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Αναστολή"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Επανεκκίνηση…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Τερματισμός…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Αποσύνδεση…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Εναλλαγή χρήστη…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Κλείδωμα οθόνης"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Άγνωστη συσκευή Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Έχει εντοπιστεί νέα συσκευή ενώ λείπατε. Παρακαλούμε αποσυνδέστε και "
+"επανασυνδέστε τη συσκευή για να την χρησιμοποιήσετε."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Μη εξουσιοδοτημένη συσκευή Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Έχει εντοπιστεί νέα συσκευή και πρέπει να εγκριθεί από διαχειριστή."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Σφάλμα πιστοποίησης Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Αδυναμία πιστοποίησης της συσκευής Thunderbolt: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Άλλαξε η ένταση"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Ένταση"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Έξοδος ήχου"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Είσοδος ήχου"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Κατοπτρισμός"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Ένωση οθονών"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Μόνο εξωτερικό"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Μόνο ενσωματωμένο"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Σύρετε για ξεκλείδωμα"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Κάντε κλικ ή πατήστε ένα πλήκτρο για ξεκλείδωμα"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Ξεκλείδωμα παραθύρου"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Σύνδεση ως άλλος χρήστης"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Καλωσήρθατε στο GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Εάν θέλετε να μάθετε να μάθετε περισσότερα, δοκιμάστε την περιήγηση."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Όχι, ευχαριστώ"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Κάντε περιήγηση"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Το «%s» είναι έτοιμο"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Διατήρηση ρυθμίσεων οθόνης;"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Επαναφορά ρυθμίσεων"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Διατήρηση αλλαγών"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Οι αλλαγές ρυθμίσεων θα επανέλθουν σε %d δευτερόλεπτο"
+msgstr[1] "Οι αλλαγές ρυθμίσεων θα επανέλθουν σε %d δευτερόλεπτα"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Απόκρυψη"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Επαναφορά"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Μεγιστοποίηση"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Μετακίνηση"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Αλλαγή μεγέθους"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Μετακίνηση μπάρας τίτλου στην οθόνη"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Πάντα στην κορυφή"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Πάντα σε ορατό χώρο εργασίας"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Μετακίνηση στο χώρο εργασίας αριστερά"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Μετακίνηση στο χώρο εργασίας δεξιά"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Μετακίνηση στο χώρο εργασίας πάνω"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Μετακίνηση στο χώρο εργασίας κάτω"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Μετακίνηση στην πάνω οθόνη"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Μετακίνηση στην κάτω οθόνη"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Μετακίνηση στην αριστερή οθόνη"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Μετακίνηση στη δεξιά οθόνη"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Κλείσιμο"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Ημερολόγιο Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Εμφάνιση έκδοσης"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Λειτουργία που χρησιμοποιείται από το GDM για την οθόνη σύνδεσης"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Χρήση μιας συγκεκριμένης λειτουργίας, π.χ. «gdm» για την οθόνη σύνδεσης"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Λίστα πιθανών λειτουργιών"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Άγνωστο"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Αποτυχία εκκίνησης του «%s»"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Οι κωδικοί πρόσβασης δεν ταιριάζουν."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Ο κωδικός πρόσβασης δεν μπορεί να είναι κενός"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Το παράθυρο πιστοποίησης έκλεισε από τον χρήστη"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Επεκτάσεις"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Διαχείριση επεκτάσεων GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Το έργο GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Το πρόγραμμα Επεκτάσεις GNOME χειρίζεται την ενημέρωση επεκτάσεων, τη "
+"ρύθμιση προτιμήσεων και την αφαίρεση ή απενεργοποίηση ανεπιθύμητων "
+"επεκτάσεων."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Ρύθμιση επεκτάσεων του GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Καμιά αντιστοιχία"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Αφαίρεση «%s»;"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Εάν αφαιρέσετε την επέκταση και θέλετε να την ενεργοποιήσετε ξανά, "
+"χρειάζεται να πραγματοποιηθεί λήψη εκ νέου"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Αφαίρεση"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Ελληνική μεταφραστική ομάδα GNOME\n"
+" Ευστάθιος Ιωσηφίδης <iosifidis@opensuse.org>\n"
+"\n"
+"\n"
+"Για περισσότερες πληροφορίες, επισκεφθείτε τη σελίδα\n"
+"http://gnome.gr/"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Η %d επέκταση θα ενημερωθεί στην επόμενη είσοδο."
+msgstr[1] "Οι %d επεκτάσεις θα ενημερωθούν στην επόμενη είσοδο."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Η επέκταση δεν είναι συμβατή με την τρέχουσα έκδοση GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Η επέκταση παρουσίασε σφάλμα"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Η επέκταση μπορεί να ενημερωθεί"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Ιστοσελίδα"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Αφαίρεση…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Βοήθεια"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Περί επεκτάσεων"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Οι επεκτάσεις ενδέχεται να προκαλέσουν θέματα απόδοσης και σταθερότητας. "
+"Απενεργοποιήστε τις επεκτάσεις αν αντιμετωπίσετε προβλήματα με το σύστημά "
+"σας."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Εγκατεστημένες χειροκίνητα"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Για να βρείτε και να εγκαταστήσετε επεκτάσεις, επισκεφθείτε την ιστοσελίδα "
+"<a href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Ενσωματωμένο"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Καμία εγκατεστημένη επέκταση"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Λυπούμαστε πολύ, αλλά δεν ήταν δυνατή η λήψη της λίστας εγκατεστημένων "
+"επεκτάσεων. Βεβαιωθείτε ότι έχετε συνδεθεί στο GNOME και προσπαθήστε ξανά."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Έτοιμες οι ενημερώσεις επεκτάσεων"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Η νέα επέκταση δημιουργήθηκε επιτυχώς την %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Το όνομα πρέπει να είναι μια πολύ σύντομη (ιδανικά περιγραφική) "
+"συμβολοσειρά.\n"
+"Παραδείγματα είναι: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Όνομα"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Η περιγραφή είναι μια εξήγηση μιας μόνο φράσης για το τι κάνει η επέκτασή "
+"σας.\n"
+"Παραδείγματα είναι: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Περιγραφή"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"Το UUID είναι ένα παγκόσμιο μοναδικό αναγνωριστικό για την επέκτασή σας.\n"
+"Αυτό θα πρέπει να έχει τη μορφή μιας διεύθυνσης ηλεκτρονικού ταχυδρομείου "
+"(clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Επιλέξτε ένα από τα διαθέσιμα πρότυπα:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Πρότυπο"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Το μοναδικό αναγνωριστικό της νέας επέκτασης"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "ΟΝΟΜΑ"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Το ορατό από το χρήστη όνομα της νέας επέκτασης"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "ΠΕΡΙΓΡΑΦΗ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Μια σύντομη περιγραφή του τι κάνει η επέκταση"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ΠΡΟΤΥΠΟ"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Το πρότυπο που θα χρησιμοποιηθεί για τη νέα επέκταση"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Εισαγάγετε τις πληροφορίες επέκτασης διαδραστικά"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Δημιουργία νέας επέκτασης"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Άγνωστα ορίσματα"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Απαιτούνται UUID, όνομα και περιγραφή"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Αποτυχία σύνδεσης στο κέλυφος GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Η επέκταση «%s» δεν υπάρχει\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Απενεργοποίηση επέκτασης"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Δεν δόθηκε UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Έχουν δοθεί περισσότερα από ένα UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Ενεργοποίηση επέκτασης"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Η επέκταση «%s» δεν υπάρχει\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Εμφάνιση πληροφοριών επεκτάσεων"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Αντικατάσταση μιας υπάρχουσας επέκτασης"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "ΔΕΣΜΗ_ΕΠΕΚΤΑΣΗΣ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Εγκατάσταση μιας δέσμης επεκτάσεων"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Δεν έχει οριστεί δέσμη επέκτασεων"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Έχουν καθοριστεί περισσότερες από μία δέσμες επέκτασης"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Εμφάνιση εγκατεστημένων επεκτάσεων χρήστη"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Εμφάνιση εγκατεστημένων επεκτάσεων συστήματος"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Εμφάνιση ενεργοποιημένων επεκτάσεων"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Εμφάνιση απενεργοποιημένων επεκτάσεων"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Εμφάνιση επεκτάσεων με προτιμήσεις"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Εμφάνιση επεκτάσεων με ενημερώσεις"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Εμφάνιση λεπτομερειών επέκτασης"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Λίστα εγκατεστημένων επεκτάσεων"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ΑΡΧΕΙΟ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Πρόσθετη πηγή για να συμπεριληφθεί στη δέσμη"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ΔΙΑΤΑΞΗ"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Μια διάταξη GSettings που πρέπει να συμπεριληφθεί"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ΚΑΤΑΛΟΓΟΣ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Ο κατάλογος όπου βρίσκονται οι μεταφράσεις"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ΤΟΜΕΑΣ"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Ο τομέας gettext που χρησιμοποιείται για μεταφράσεις"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Αντικαταστήστε ενός υπάρχοντος πακέτου"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Ο κατάλογος όπου πρέπει να δημιουργηθεί το πακέτο"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ΠΗΓΑΙΟΣ_ΚΑΤΑΛΟΓΟΣ"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Δημιουργία μια δέσμης επέκτασης"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Έχουν καθοριστεί περισσότεροι από ένας κατάλογος πηγών"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Η επέκταση «%s» δεν έχει προτιμήσεις\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Αποτυχία ανοίγματος των προτιμήσεων της επέκτασης “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Άνοιγμα προτιμήσεων επέκτασης"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Επαναφορά επέκτασης"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Αδυναμία απεγκατάστασης επεκτάσεων συστήματος\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Αποτυχία απεγκατάστασης του «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Απεγκατάσταση επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Να μην εμφανίζονται μηνύματα σφαλμάτων"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Διαδρομή"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Αρχικός δημιουργός"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Έκδοση"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Κατάσταση"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "Η \"έκδοση\" δεν λαμβάνει παραμέτρους"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Χρήση:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Προβολή πληροφοριών έκδοσης και έξοδος."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "ΕΝΤΟΛΗ"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Εντολές:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Εμφάνιση βοήθειας"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Ενεργοποίηση επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Απενεργοποίηση επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Επαναφορά επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Απεγκατάσταση επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Λίστα επεκτάσεων"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Εμφάνιση πληροφοριών επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Άνοιγμα προτιμήσεων επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Δημιουργία επέκτασης"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Επέκταση πακέτου"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Εγκατάσταση δέσμης επεκτάσεων"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Χρήση του «%s» για να λάβετε λεπτομερή βοήθεια.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Απλό"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Μια κενή επέκταση"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Δείκτης"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Προσθήκη εικονιδίου στην επάνω γραμμή"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u έξοδος"
+msgstr[1] "%u έξοδοι"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u είσοδος"
+msgstr[1] "%u είσοδοι"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Ήχοι συστήματος"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Αν ο προεπιλεγμένος προσαρμογέας Bluetooth έχει ρυθμιστεί με "
+#~ "συσχετισμένες συσκευές"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Το κέλυφος θα εμφανίσει ένα μενού Bluetooth αν είναι ενεργός ο "
+#~ "προσαρμογέας, ή αν υπάρχουν συσχετισμένες συσκευές με αυτόν. Αυτό θα "
+#~ "επαναφερθεί αν δεν υπάρχουν συσχετισμένες συσκευές με τον προσαρμογέα."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Αφαίρεση από τα αγαπημένα"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Προσθήκη στα αγαπημένα"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Ρυθμίσεις Bluetooth"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "Συνδέθηκε %d"
+#~ msgstr[1] "Συνδέθηκαν %d"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth ανενεργό"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth ενεργό"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Ενεργοποιήθηκε η τοποθεσία"
+
+#~ msgid "Disable"
+#~ msgstr "Απενεργοποίηση"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Ρυθμίσεις ιδιωτικότητας"
+
+#~ msgid "Location In Use"
+#~ msgstr "Τοποθεσία σε χρήση"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Απενεργοποιήθηκε η τοποθεσία"
+
+#~ msgid "Enable"
+#~ msgstr "Ενεργοποίηση"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "Απενεργοποιημένο %s"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s συνδεδεμένο"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "Χωρίς διαχείριση %s"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "Γίνεται σύνδεση %s"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "Το %s απαιτεί πιστοποίηση"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Λείπει υλικολογισμικό για το %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "Το %s δεν είναι διαθέσιμο"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Αποτυχία σύνδεσης του %s"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Ρυθμήσεις ενσύρματης σύνδεσης"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Ρυθμίσεις ευρυζωνικής σύνδεσης"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Το υλικό απενεργοποιήθηκε για το %s"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "Απενεργοποιήθηκε το %s"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Σύνδεση στο διαδίκτυο"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Η λειτουργία πτήσης είναι ενεργή"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr ""
+#~ "Η ασύρματη σύνδεση είναι ανενεργή όταν η λειτουργία πτήσηςα είναι ενεργή."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Απενεργοποίηση της λειτουργίας πτήσης"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Το Wi-Fi είναι ανενεργό"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr ""
+#~ "Το Wi-Fi χρειάζεται να ενεργοποιηθεί για να συνδεθείτε σε ένα δίκτυο."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Ενεργοποίηση Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Ασύρματα δίκτυα"
+
+#~ msgid "Select a network"
+#~ msgstr "Επιλογή δικτύου"
+
+#~ msgid "No Networks"
+#~ msgstr "Χωρίς δίκτυα"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Χρήση διακόπτη υλικού για απενεργοποίηση"
+
+#~ msgid "Select Network"
+#~ msgstr "Επιλογή δικτύου"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Ρυθμίσεις Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "Χωρίς σύνδεση το %s"
+
+#~ msgid "connecting…"
+#~ msgstr "γίνεται σύνδεση…"
+
+#~ msgid "authentication required"
+#~ msgstr "απαιτείται πιστοποίηση"
+
+#~ msgid "connection failed"
+#~ msgstr "αποτυχία σύνδεσης"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Ρυθμίσεις VPN"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "Απενεργοποιημένο VPN"
+
+#~ msgid "Network Settings"
+#~ msgstr "Ρυθμίσεις δικτύου"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s ενσύρματη σύνδεση"
+#~ msgstr[1] "%s ενσύρματη συνδέσεις"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Wi-Fi σύνδεση"
+#~ msgstr[1] "%s Wi-Fi συνδέσεις"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s σύνδεση μόντεμ"
+#~ msgstr[1] "%s συνδέσεις μόντεμ"
+
+#~ msgid "Connection failed"
+#~ msgstr "Αποτυχία σύνδεσης"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Απέτυχε η ενεργοποίηση της σύνδεσης δικτύου"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Λειτουργία νυχτός ανενεργή"
+
+#~ msgid "Resume"
+#~ msgstr "Συνέχιση"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Απενεργοποίηση έως αύριο"
+
+#~ msgid "Power Settings"
+#~ msgstr "Ρυθμίσεις τροφοδοσίας"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Πλήρως φορτισμένη"
+
+#~ msgid "Not Charging"
+#~ msgstr "Δεν φορτίζεται"
+
+#~ msgid "Estimating…"
+#~ msgstr "Εκτίμηση…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d Απομένει (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d Πλήρης μέχρι (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Οθόνη σε κοινή χρήση"
+
+#~ msgid "Turn off"
+#~ msgstr "Απενεργοποίηση"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Λειτουργία πτήσης ενεργή"
+
+#~ msgid "Lock"
+#~ msgstr "Κλείδωμα"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Τερματισμός / Αποσύνδεση"
+
+#~ msgid "Log Out"
+#~ msgstr "Αποσύνδεση"
+
+#~ msgid "Author"
+#~ msgstr "Συγγραφέας"
+
+#~ msgid "Warning"
+#~ msgstr "Προειδοποίηση"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Οι επεκτάσεις μπορεί να προκαλέσουν προβλήματα στο σύστημα, "
+#~ "συμπεριλαμβανομένων προβλημάτων απόδοσης. Εάν αντιμετωπίσετε προβλήματα "
+#~ "με το σύστημά σας, συνίσταται να απενεργοποιήσετε όλες τις επεκτάσεις."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Αποτυχία σύνδεσης στο κέλυφος GNOME"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Ενεργοποίηση ενδοσκόπησης API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Ενεργοποιεί ένα API D-Bus που επιτρέπει την εισαγωγή της κατάστασης "
+#~ "εφαρμογής του κελύφους."
+
+#~ msgid "Minimize"
+#~ msgstr "Ελαχιστοποίηση"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Απομεγιστοποίηση"
+
+#~ msgid "App Picker View"
+#~ msgstr "Προβολή επιλογέα εφαρμογών"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Ευρετήριο της τρέχουσας επιλεγμένης προβολής του επιλογέα εφαρμογών."
+
+#~ msgid "Copy Error"
+#~ msgstr "Αντιγραφή σφάλματος"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Οι πιο συχνά χρησιμοποιούμενες εφαρμογές θα εμφανιστούν εδώ"
+
+#~ msgid "Frequent"
+#~ msgstr "Συχνά"
+
+#~ msgid "All"
+#~ msgstr "Όλα"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %B %-d"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %B %-d, %Y"
+
+#~ msgid "Off"
+#~ msgstr "Εκτός σύνδεσης"
+
+#~ msgid "On"
+#~ msgstr "Ενεργοποίηση"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Εξερεύνηση στο Λογισμικό"
+
+#~ msgid "Next"
+#~ msgstr "Επόμενο"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Σύνδεση"
+
+#, fuzzy
+#~| msgctxt "search-result"
+#~| msgid "Lock orientation"
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Κλείδωμα προσανατολισμού"
+
+#~ msgid "Rename"
+#~ msgstr "Μετονομασία"
+
+#~ msgid "Password:"
+#~ msgstr "Συνθηματικό:"
+
+#~ msgid "Type again:"
+#~ msgstr "Πληκτρολογήστε ξανά:"
+
+#~ msgid "Password: "
+#~ msgstr "Κωδικός πρόσβασης: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Απαιτείται πιστοποίηση από το ασύρματο δίκτυο"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Κωδικός πρόσβασης δικτύου κινητής ευρυζωνικής σύνδεσης"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %B %d"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d νέο μήνυμα"
+#~ msgstr[1] "%d νέα μηνύματα"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d νέα ειδοποίηση"
+#~ msgstr[1] "%d νέες ειδοποιήσεις"
+
+#~ msgid "Account Settings"
+#~ msgstr "Ρυθμίσεις λογαριασμού"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Κλείδωμα προσανατολισμού"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Συνδυασμός πλήκτρων που κάνει παύση και συνέχιση όλων των tween που "
+#~ "εκτελούνται, για σκοπούς αποσφαλμάτωσης"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Ποιο πληκτρολόγιο θα χρησιμοποιηθεί"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Ο τύπος του πληκτρολογίου που θα χρησιμοποιηθεί."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr ""
+#~ "Προέκυψε ένα σφάλμα κατά τη φόρτωση του παραθύρου διαλόγου των "
+#~ "προτιμήσεων για %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "Όλη μέρα %s."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, αργότερα %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, αργότερα %s, στη συνέχεια %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Αισθητή θερμοκρασία %s."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~| msgid "Log Out"
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Αποσύνδεση"
+
+#~| msgid "Switch User"
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Αλλαγή χρήστη"
+
+#~ msgid "Hide tray"
+#~ msgstr "Απόκρυψη περιοχής ειδοποιήσεων"
+
+#~ msgid "Status Icons"
+#~ msgstr "Εικονίδια κατάστασης"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Ανακατεύθυνση πιστοποίησης ιστού"
+
+#~ msgid "Events"
+#~ msgstr "Συμβάντα"
+
+#~ msgid "Notifications"
+#~ msgstr "Ειδοποιήσεις"
+
+#~ msgid "Media"
+#~ msgstr "Πολυμέσα"
+
+#~ msgid "Not In Use"
+#~ msgstr "Εκτός χρήσης"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Εμφάνιση του αριθμού εβδομάδας στο ημερολόγιο"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr ""
+#~ "Αν είναι αληθές, εμφανίζει τον αριθμό εβδομάδας κατά ISO στο ημερολόγιο."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Χρήση ως σύνδεση διαδικτύου"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "Το %s ζητά πρόσβαση στην τοποθεσία σας."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (συνθετητής wayland)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d συνδεδεμένη συσκευή"
+#~ msgstr[1] "%d συνδεδεμένες συσκευές"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Μπαταρία"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgid "Invitation"
+#~ msgstr "Πρόσκληση"
+
+#~ msgid "Call"
+#~ msgstr "Κλήση"
+
+#~ msgid "File Transfer"
+#~ msgstr "Μεταφορά αρχείου"
+
+#~ msgid "Chat"
+#~ msgstr "Συνομιλία"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Πρόσκληση στο %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "Ο %s σας προσκαλεί να μπείτε στο %s"
+
+#~ msgid "Decline"
+#~ msgstr "Απόρριψη"
+
+#~ msgid "Accept"
+#~ msgstr "Αποδοχή"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Βιντεοκλήση από %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Κλήση από %s"
+
+#~ msgid "Answer"
+#~ msgstr "Απάντηση"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "Ο %s σας στέλνει το %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "Ο %s ζητάει άδεια για να βλέπει πότε είστε συνδεδεμένος"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Απέτυχε η πιστοποίηση"
+
+#~ msgid "Encryption error"
+#~ msgstr "Σφάλμα κρυπτογράφησης"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Δεν υπάρχει πιστοποιητικό"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Μη έμπιστο πιστοποιητικό"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Ληγμένο πιστοποιητικό"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Το πιστοποιητικό δεν ενεργοποιήθηκε"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Αναντιστοιχία ονόματος κεντρικού υπολογιστή στο πιστοποιητικό"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Αναντιστοιχία αποτυπώματος πιστοποιητικού"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Αυτο-υπογεγραμμένο πιστοποιητικό"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Η κατάσταση έχει ορισθεί σε εκτός σύνδεσης"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Μη έγκυρο πιστοποιητικό"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Άρνηση σύνδεσης"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Δεν είναι δυνατή η σύνδεση"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Χάθηκε η σύνδεση"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Αυτός ο λογαριασμός είναι ήδη συνδεδεμένος στον διακομιστή"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "Η σύνδεση έχει αντικατασταθεί από μια νέα σύνδεση που χρησιμοποιεί τον "
+#~ "ίδιο πόρο"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Ο λογαριασμός υπάρχει ήδη στον διακομιστή"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Ο διακομιστής είναι πολύ απασχολημένος ώστε να χειριστεί τη σύνδεση"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Το πιστοποιητικό έχει ανακληθεί"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Το πιστοποιητικό χρησιμοποιεί έναν μη ασφαλή αλγόριθμο αποκρυπτογράφησης "
+#~ "αποτυπώματος ή είναι κρυπτογραφικά ασθενές"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Το μέγεθος πιστοποιητικού του διακομιστή, ή το βάθος της αλυσίδας "
+#~ "πιστοποιητικού του διακομιστή υπερβαίνει τα όρια που επιτρέπει η "
+#~ "βιβλιοθήκη κρυπτογραφίας"
+
+#~ msgid "Internal error"
+#~ msgstr "Εσωτερικό σφάλμα"
+
+#~ msgid "View account"
+#~ msgstr "Προβολή λογαριασμού"
+
+#~ msgid "Unknown reason"
+#~ msgstr "Άγνωστος λόγος"
+
+#~ msgid "Show the message list"
+#~ msgstr "Εμφάνιση της λίστας μηνυμάτων"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Πύλη υποδοχής"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Κυ"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Δε"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Τρ"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Τε"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Πε"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Πα"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Σα"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Τίποτα προγραμματισμένο"
+
+#~ msgid "This week"
+#~ msgstr "Αυτή η εβδομάδα"
+
+#~ msgid "Next week"
+#~ msgstr "Επόμενη εβδομάδα"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Αφαιρούμενες συσκευές"
+
+#~ msgid "Eject"
+#~ msgstr "Εξαγωγή"
+
+#~ msgid "Unmute"
+#~ msgstr "Άρση σίγασης"
+
+#~ msgid "Mute"
+#~ msgstr "Σίγαση"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Άνοιγμα ημερολογίου"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Ρυθμίσεις ημερομηνίας & ώρας"
+
+#~ msgid "Open"
+#~ msgstr "Άνοιγμα"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Εκκαθάριση μηνυμάτων"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Ρυθμίσεις ειδοποιήσεων"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Μενού περιοχής ειδοποιήσεων"
+
+#~ msgid "No Messages"
+#~ msgstr "Χωρίς μηνύματα"
+
+#~ msgid "Message Tray"
+#~ msgstr "Περιοχή ειδοποιήσεων μηνυμάτων"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Το μέγιστο επίπεδο ακρίβειας της θέσης."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Ρυθμίζει το μέγιστο επίπεδο ακρίβειας στην εντόπιση θέσης το οποίο "
+#~ "επιτρέπεται να βλέπουν οι εφαρμογές. Οι έγκυρες επιλογές είναι "
+#~ "'όχι' (απενεργοποίηση εντοπισμού θέσης), 'χώρα', 'πόλη', 'γειτονιά', "
+#~ "'οδός' και 'ακριβώς' (τυπικά, απαιτεί δέκτη GPS). Παρακαλούμε να θυμάστε "
+#~ "ότι αυτό ελέγχει μόνο τι θα επιτρέπει το Geoclue να βλέπουν οι εφαρμογές, "
+#~ "ενώ αυτές μπορούν να βρουν τη θέση του χρήστη και από μόνες τους, "
+#~ "χρησιμοποιώντας πόρους δικτύου (αν και, στην καλύτερη περίπτωση, με "
+#~ "ακρίβεια επιπέδου οδού)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Διάταξη των κουμπιών στη γραμμή τίτλου"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Αυτό το κλειδί υπερισχύει του κλειδιού που υπάρχει στο org.gnome.desktop."
+#~ "wm.preferences όταν εκτελείται το GNOME Shell."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Επιλέξτε μια επέκταση για να την ρυθμίσετε χρησιμοποιώντας το σύνθετο "
+#~ "πλαίσιο παραπάνω."
+
+#~ msgid "calendar:MY"
+#~ msgstr "ημερολόγιο:MY"
+
+#~ msgid "unavailable"
+#~ msgstr "μη διαθέσιμο"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%ΗΗΗΗ</b>, <b>%Ω:%Λ</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%Η</b> <b>%ΜΜΜΜ</b>, <b>%Ω:%Λ</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%Η</b> <b>%ΜΜΜΜ</b> <b>%ΕΕΕΕ</b>, <b>%Ω:%Λ</b> "
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Η λίστα των κατηγοριών που πρέπει να εμφανίζονται ως φάκελοι"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Το όνομα κάθε κατηγορίας σε αυτή τη λίστα θα αναπαριστάται ως φάκελος "
+#~ "στην προβολή εφαρμογής, αντί να εμφανίζεται ενσωματωμένο στην κύρια "
+#~ "προβολή."
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Αίτηση εξουσιοδότησης από %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Η συσκευή %s επιθυμεί να συζευχθεί με αυτόν τον υπολογιστή"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Η συσκευή %s επιθυμεί πρόσβαση στην υπηρεσία '%s'"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Μόνο για αυτή τη φορά"
+
+#~ msgid "Reject"
+#~ msgstr "Απόρριψη"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Επιβεβαίωση σύζευξης για %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Παρακαλώ επιβεβαιώστε αν το κλειδί πρόσβασης '%06d' ταιριάζει με αυτό της "
+#~ "συσκευής."
+
+#~ msgid "Does not match"
+#~ msgstr "Δεν ταιριάζει"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Αίτηση σύζευξης για %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Παρακαλώ εισάγετε το PIN που αναφέρεται στη συσκευή."
+
+#~ msgid "OK"
+#~ msgstr "Εντάξει"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Συγγνώμη, κανένα απόφθεγμα για εσάς σήμερα:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s ο Προφήτης λέει"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%Ω\\u2236%Λ"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Εάν θα συλλέγονται στατιστικά χρήσης των εφαρμογών"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "Το κέλυφος κανονικά παρακολουθεί τις ενεργές εφαρμογές για να παρουσιάσει "
+#~ "τις πιο συχνά χρησιμοποιούμενες (π.χ. στους εκκινητές). Παρόλο που αυτά "
+#~ "τα δεδομένα θα παραμείνουν ιδιωτικά, μπορεί να επιθυμείτε να "
+#~ "απενεργοποιήσετε αυτή τη λειτουργία για λόγους απορρήτου. Παρακαλούμε "
+#~ "έχετε υπ' όψιν ότι η απενεργοποίηση δεν θα αφαιρέσει τα ήδη αποθηκευμένα "
+#~ "δεδομένα."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Χρησιμοποιείται εσωτερικά για την αποθήκευση της συνομιλίας που έχει "
+#~ "ορισθεί από τον χρήστη. Η τιμή εδώ είναι από την απαρίθμηση του "
+#~ "TpConnectionPresenceType."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Χρησιμοποιείται εσωτερικά για την αποθήκευση της κατάστασης παρουσίας της "
+#~ "τελευταίας συνόδου του χρήστη. Η τιμή εδώ είναι από την απαρίθμηση του "
+#~ "GsmPresenceStatus."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Συνδυασμός πλήκτρων για την εναλλαγή της μαγνητοσκόπησης οθόνης"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr ""
+#~ "Συνδυασμός πλήκτρων για το άνοιγμα/κλείσιμο της ενσωματωμένης "
+#~ "μαγνητοσκόπησης οθόνης."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr ""
+#~ "Ο ρυθμός καρέ που θα χρησιμοποιηθεί για την καταγραφή του βίντεο οθόνης."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "Ο ρυθμός καρέ του στιγμιότυπου που παράγεται από τον εγγραφέα βίντεο "
+#~ "οθόνης του GNOME Shell σε καρέ ανά δευτερόλεπτο."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr ""
+#~ "Ο δίαυλος του gstreamer που χρησιμοποιήθηκε για την κωδικοποίηση του "
+#~ "βίντεο οθόνης"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Ορίζει το δίαυλο του GStreamer που θα χρησιμοποιηθεί για την κωδικοποίηση "
+#~ "των εγγραφών. Ακολουθεί το συντακτικό που χρησιμοποιείται για το gst-"
+#~ "launch. Ο δίαυλος θα πρέπει να έχει έναν μη συνδεδεμένο απαγωγέα όπου θα "
+#~ "εγγράφεται το καταγραφέα βίντεο. Φυσιολογικά θα έχει μια μη συνδεδεμένη "
+#~ "πηγή· η έξοδος της πηγής θα εγγράφεται στο αρχείο εξόδου. Όμως, ο δίαυλος "
+#~ "μπορεί να επιληφθεί της δικής του εξόδου - αυτό μπορεί να χρησιμοποιηθεί "
+#~ "για την αποστολή της εξόδου σε έναν διακομιστή icecast μέσω του "
+#~ "shout2send ή κάποιου παρόμοιου. Όταν δεν έχει ορισθεί ή έχει ορισθεί σε "
+#~ "κενή τιμή, θα χρησιμοποιηθεί ο προεπιλεγμένος δίαυλος. Αυτός είναι αυτή "
+#~ "τη στιγμή ο 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 "
+#~ "deadline=1000000 threads=%T ! queue ! webmmux' και γράφει σε WEBM με "
+#~ "κωδικοποίηση VP8. Το %T χρησιμοποιείται ως παράδειγμα για το πιθανό "
+#~ "βέλτιστο αριθμό πυρήνων του συστήματος."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr ""
+#~ "Επέκταση αρχείου που θα χρησιμοποιηθεί για την αποθήκευση του βίντεο "
+#~ "οθόνης"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "Το όνομα αρχείου για τα καταγεγραμμένα βίντεο οθόνης θα είναι μοναδικό, "
+#~ "βασισμένο στην τρέχουσα ημερομηνία και θα χρησιμοποιεί αυτή την επέκταση. "
+#~ "Θα πρέπει να αλλάζει όταν γίνεται εγγραφή σε διαφορετικό πρότυπο περιέκτη."
+
+#~ msgid "Session…"
+#~ msgstr "Συνεδρία…"
+
+#~ msgid "Power"
+#~ msgstr "Ενέργεια"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Πατήστε Αποσύνδεση για να κλείσετε αυτές τις εφαρμογές και να "
+#~ "αποσυνδεθείτε από το σύστημα."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Αποσύνδεση από το σύστημα."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Πατήστε Απενεργοποίηση για να κλείσετε αυτές τις εφαρμογές και να "
+#~ "τερματίσετε το σύστημα."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Απενεργοποίηση του συστήματος."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Πατήστε Επανεκκίνηση για να κλείσετε αυτές τις εφαρμογές και να "
+#~ "επανεκκινήσετε το σύστημα."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Επανεκκινείται το σύστημα."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Ρυθμίσεις καθολικής πρόσβασης"
+
+#~ msgid "Visibility"
+#~ msgstr "Ορατότητα"
+
+#~ msgid "Send Files to Device…"
+#~ msgstr "Αποστολή αρχείων σε συσκευή…"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Ρύθμιση νέας συσκευής…"
+
+#~ msgid "Send Files…"
+#~ msgstr "Αποστολή αρχείων…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Ρυθμίσεις πληκτρολογίου"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Ρυθμίσεις ποντικιού"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Ένταση, δίκτυο, μπαταρία"
+
+#~ msgid "disabled"
+#~ msgstr "απενεργοποιημένο"
+
+#~ msgid "cable unplugged"
+#~ msgstr "το καλώδιο αποσυνδέθηκε"
+
+#~ msgid "More…"
+#~ msgstr "Περισσότερα…"
+
+#~ msgid "Wired"
+#~ msgstr "Ενσύρματο"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Αυτόματα Ethernet"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Αυτόματα ευρυζωνική"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Αυτόματα dial-up"
+
+#~ msgid "Auto %s"
+#~ msgstr "Αυτόματα %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Αυτόματα bluetooth"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Αυτόματα ασύρματο"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Ασύρματη πιστότητα"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d ώρα απομένει"
+#~ msgstr[1] "%d ώρες απομένουν"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "Απομένουν %d %s %d %s"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ώρα"
+#~ msgstr[1] "ώρες"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "λεπτό"
+#~ msgstr[1] "λεπτά"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d λεπτό απομένει"
+#~ msgstr[1] "%d λεπτά απομένουν"
+
+#~ msgid "AC Adapter"
+#~ msgstr "Μετασχηματιστής εναλλασσόμενου"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Μπαταρία φορητού"
+
+#~ msgid "Monitor"
+#~ msgstr "Οθόνη"
+
+#~ msgid "Mouse"
+#~ msgstr "Ποντίκι"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Κινητό τηλέφωνο"
+
+#~ msgid "Media Player"
+#~ msgstr "Αναπαραγωγή πολυμέσων"
+
+#~ msgid "Tablet"
+#~ msgstr "Ταμπλέτα"
+
+#~ msgid "Computer"
+#~ msgstr "Υπολογιστής"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Άγνωστο"
+
+#~ msgid "Available"
+#~ msgstr "Διαθέσιμος-η"
+
+#~ msgid "Busy"
+#~ msgstr "Απασχολημένος-η"
+
+#~ msgid "Invisible"
+#~ msgstr "Αόρατος-η"
+
+#~ msgid "Away"
+#~ msgstr "Απουσιάζει"
+
+#~ msgid "Idle"
+#~ msgstr "Αδρανής"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Η κατάσταση συνομιλίας σας θα ορισθεί σε απασχολημένος"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Οι ειδοποιήσεις είναι απενεργοποιημένες, όπως και τα μηνύματα συζητήσεων. "
+#~ "Η κατάσταση σας έχει ορισθεί έτσι ώστε να γίνεται γνωστό ότι πιθανόν να "
+#~ "μην δείτε τα μηνύματα τους."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr ""
+#~ "Κλείνοντας μπορεί να τους προκαλέσετε την απώλεια αναποθήκευτης εργασίας."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Σύνδεση"
+
+#~ msgid "tray"
+#~ msgstr "περιοχή"
+
+#~ msgid "More..."
+#~ msgstr "Περισσότερα..."
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "ΕΦΑΡΜΟΓΕΣ"
+
+#~ msgid "SETTINGS"
+#~ msgstr "ΡΥΘΜΙΣΕΙΣ"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "Subscription request"
+#~ msgstr "Αίτηση συνδρομής"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Στάλθηκε στις <b>%A</b> και ώρα <b>%X</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Στάλθηκε την <b>%A</b>, <b>%d %B</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Απέτυχε η σύνδεση στο %s"
+
+#~ msgid "Reconnect"
+#~ msgstr "Επανασύνδεση"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Περιήγηση αρχείων..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "Σφάλμα περιήγησης συσκευής"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr ""
+#~ "Δεν είναι δυνατή η περιήγηση στην αιτούμενη συσκευή, το σφάλμα είναι '%s'"
+
+#~ msgid "Wireless"
+#~ msgstr "Ασύρματο"
+
+#~ msgid "VPN Connections"
+#~ msgstr "Συνδέσεις VPN"
+
+#~ msgid "System Settings"
+#~ msgstr "Ρυθμίσεις συστήματος"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Το αγαπημένο σας Πασχαλινό Αυγό"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Ηνωμένο Βασίλειο"
+
+#~ msgid "Default"
+#~ msgstr "Προεπιλογή"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "απενεργοποιημένοι πάροχοι OpenSearch"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Αποτυχία αποπροσάρτησης '%s'"
+
+#~ msgid "Retry"
+#~ msgstr "Προσπάθεια ξανά"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ΤΟΠΟΘΕΣΙΕΣ & ΣΥΣΚΕΥΕΣ"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Passphrase"
+#~ msgstr "Συνθηματική φράση"
+
+#~ msgctxt "contact"
+#~ msgid "Unknown"
+#~ msgstr "Άγνωστο"
+
+#~ msgid "CONTACTS"
+#~ msgstr "ΕΠΑΦΕΣ"
+
+#~ msgid "%s is online."
+#~ msgstr "Ο/η %s είναι συνδεδεμένος"
+
+#~ msgid "%s is offline."
+#~ msgstr "Ο/η %s είναι αποσυνδεδεμένος"
+
+#~ msgid "%s is away."
+#~ msgstr "Ο/η '%s' είναι εκτός. "
+
+#~ msgid "%s is busy."
+#~ msgstr "%s είναι απασχολημένος/η"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Εμφάνιση ώρας με δευτερόλεπτα"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Αν αληθές, εμφανίζει δευτερόλεπτα στην ώρα."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Εμφάνιση ημερομηνίας στο ρολόι"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Αν αληθές, εμφανίζει την ημερομηνία στο ρολόι επιπλέον της ώρας."
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %e %b, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e %b, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Hidden"
+#~ msgstr "Αόρατος"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Εσφαλμένος κωδικός, προσπαθήστε ξανά"
+
+#~ msgid "Power Off..."
+#~ msgstr "Απενεργοποίηση..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "Διαδικτυακοί λογαριασμοί"
+
+#~ msgid "Log Out..."
+#~ msgstr "Αποσύνδεση..."
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "ΠΡΟΣΦΑΤΑ ΑΝΤΙΚΕΙΜΕΝΑ"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should not be loaded."
+#~ msgstr ""
+#~ "Οι επεκτάσεις του GNOME Shell έχουν μια ιδιότητα uuid. Αυτό το κλειδί "
+#~ "απαριθμεί ποιες επεκτάσεις δεν θα πρέπει να φορτωθούν."
+
+#~ msgid "Localization Settings"
+#~ msgstr "Ρυθμίσεις γλώσσας"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "Είστε συνδεμένοι στο δίκτυο κινητής ευρυζωνικής '%s'"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "Είστε συνδεμένοι στο ασύρματο δίκτυο '%s'"
+
+#~ msgid "You're now connected to wired network '%s'"
+#~ msgstr "Είστε συνδεμένοι στο ενσύρματο δίκτυο '%s'"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "Είστε συνδεμένοι στο VPN δίκτυο '%s'"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "Το %s ολοκλήρωσε την εκκίνηση "
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "Λιγότερο από ένα λεπτό πριν"
+
+#, fuzzy
+#~ msgid "Shut Down"
+#~ msgstr "Τερματισμός..."
+
+#~ msgid "Clip the crosshairs at the center"
+#~ msgstr "Περικοπή του σκοπεύτρου στο κέντρο"
+
+#~ msgid "Color of the crosshairs"
+#~ msgstr "Χρώμα του σκόπευτρου"
+
+#~ msgid ""
+#~ "Determines the length of the vertical and horizontal lines that make up "
+#~ "the crosshairs."
+#~ msgstr ""
+#~ "Καθορίζει το μήκος των κάθετων και οριζόντιων γραμμών που απαρτίζουν το "
+#~ "σκόπευτρο"
+
+#~ msgid "Enable lens mode"
+#~ msgstr "Ενεργοποίηση λειτουργίας φακού"
+
+#~ msgid ""
+#~ "Enables/disables display of crosshairs centered on the magnified mouse "
+#~ "sprite."
+#~ msgstr ""
+#~ "Ενεργοποιεί/απενεργοποιεί την εμφάνιση σκοπεύτρου στο δείκτη μεγέθυνσης "
+#~ "του ποντικιού"
+
+#~ msgid "Length of the crosshairs"
+#~ msgstr "Μήκος του σκοπεύτρου"
+
+#~ msgid "Magnification factor"
+#~ msgstr "Παράγοντας μεγέθυνσης"
+
+#~ msgid "Mouse Tracking Mode"
+#~ msgstr "Λειτουργία Mouse Tracking "
+
+#~ msgid "Opacity of the crosshairs"
+#~ msgstr "Αδιαφάνεια του σκοπεύτρου"
+
+#~ msgid "Scroll magnified contents beyond the edges of the desktop"
+#~ msgstr ""
+#~ "Κύλιση σε μεγεθυμένα περιεχόμενα που βρίσκονται έξω από την επιφάνεια"
+
+#~ msgid "Show or hide crosshairs"
+#~ msgstr "Προβολή ή απόκρυψη του σκοπεύτρου"
+
+#~ msgid "Show or hide the magnifier"
+#~ msgstr "Προβολή ή απόκρυψη του μεγεθυντή"
+
+#~ msgid "Show or hide the magnifier and all of its zoom regions."
+#~ msgstr "Προβολή ή απόκρυψη του μεγεθυντή και όλων των περιοχών μεγέθυνσης"
+
+#~ msgid ""
+#~ "The color of the the vertical and horizontal lines that make up the "
+#~ "crosshairs."
+#~ msgstr ""
+#~ "Το χρώμα των κάθετων και οριζόντιων γραμμών που απαρτίζουν το σκόπευτρο"
+
+#~ msgid "Thickness of the crosshairs"
+#~ msgstr "Πάχος των γραμμών σκοπεύτρου"
+
+#~ msgid ""
+#~ "Width of the vertical and horizontal lines that make up the crosshairs."
+#~ msgstr ""
+#~ " Το πάχος των κάθετων και οριζόντιων γραμμών που απαρτίζουν το σκόπευτρο"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "ΠΡΟΤΙΜΗΣΕΙΣ"
+
+#~ msgid "Shut Down..."
+#~ msgstr "Τερματισμός..."
+
+#~ msgid "Bluetooth Agent"
+#~ msgstr "Υποστήριξη Bluetooth"
+
+#~ msgid "Search your computer"
+#~ msgstr "Αναζήτηση στον υπολογιστή σας"
+
+#~ msgid ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+#~ msgstr ""
+#~ "Αδυναμία προσθήκης νέου χώρου εργασίας επειδή έχετε φτάσει το μέγιστο "
+#~ "αριθμό χώρων εργασίας."
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "Δεν είναι δυνατή η αφαίρεση του πρώτου χώρου εργασίας."
diff --git a/po/en_GB.po b/po/en_GB.po
new file mode 100644
index 0000000..6c29f38
--- /dev/null
+++ b/po/en_GB.po
@@ -0,0 +1,4231 @@
+# British English translation for gnome-shell.
+# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Philip Withnall <philip@tecnocode.co.uk>, 2009, 2010, 2014.
+# Chris Leonard <cjlhomeaddress@gmail.com>, 2012.
+# Zander Brown <zbrown@gnome.org>, 2019-2021.
+# Bruce Cowan <bruce@bcowan.me.uk>, 2010-2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-11-04 14:57+0000\n"
+"PO-Revision-Date: 2023-01-15 11:01+0000\n"
+"Last-Translator: Bruce Cowan <bruce@bcowan.me.uk>\n"
+"Language-Team: English - United Kingdom <en@li.org>\n"
+"Language: en_GB\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Gtranslator 42.0\n"
+"X-Project-Style: gnome\n"
+"X-DL-Team: en_GB\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: Translating\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Launchers"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Activate favourite application 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Activate favourite application 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Activate favourite application 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Activate favourite application 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Activate favourite application 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Activate favourite application 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Activate favourite application 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Activate favourite application 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Activate favourite application 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2070
+msgid "Screenshots"
+msgstr "Screenshots"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Take a screenshot interactively"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Take a screenshot"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Take a screenshot of a window"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Record a screencast interactively"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "System"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Show the notification list"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Focus the active notification"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Show the overview"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Show all applications"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Open the application menu"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Window management and application launching"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Enable internal tools useful for developers and testers from Alt+F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Allows access to internal debugging and monitoring tools using the Alt+F2 "
+"dialogue."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs of extensions to enable"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs of extensions to force disabling"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Disable user extensions"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Disables the validation of extension version compatibility"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "List of desktop file IDs for favourite applications"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favourites area."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "History for command (Alt+F2) dialogue"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "History for the looking glass dialogue"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Always show the “Log out” menu item in the user menu."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” tickbox will be present. This key sets the default state "
+"of the tickbox."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "The last selected non-default power profile"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "The last version the “Welcome to GNOME” dialogue was shown for"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"This key determines for which version the “Welcome to GNOME” dialogue was "
+"last shown. An empty string represents the oldest possible version, and a "
+"huge number will represent versions that do not exist yet. This huge number "
+"can be used to effectively disable the dialogue."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Layout of the app picker"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Keybinding to open the application menu"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Keybinding to open the application menu."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Keybinding to shift between overview states"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "Keybinding to shift between session, window picker and app grid"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "Keybinding to shift between app grid, window picker and session"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Keybinding to open the “Show Applications” view"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Keybinding to open the overview"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Keybinding to open the Activities Overview."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Keybinding to toggle the visibility of the notification list"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Keybinding to toggle the visibility of the notification list."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Keybinding to focus the active notification"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Keybinding to focus the active notification."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Switch to application 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Switch to application 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Switch to application 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Switch to application 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Switch to application 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Switch to application 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Switch to application 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Switch to application 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Switch to application 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Limit switcher to current workspace."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "The application icon mode."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Locations"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "The locations to show in world clocks"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatic location"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Whether to fetch the current location or not"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Location"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "The location for which to show a forecast"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Attach modal dialogue to the parent window"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Enable edge tiling when dropping windows on screen edges"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Workspaces are managed dynamically"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Workspaces only on primary monitor"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Delay focus changes in mouse mode until the pointer stops moving"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Network Login"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Something’s gone wrong"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Technical Details"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Homepage"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visit extension homepage"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Cancel"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Password"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Choose Session"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Not listed?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(e.g., user or %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Username"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Login Window"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Authentication error"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(or swipe finger across reader)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(or place finger on reader)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Power Off"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Restart"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Lock Screen"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Log Out"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspend"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Switch User"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "switch user"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "lock orientation;unlock orientation;screen;rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Take a Screenshot"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "screenshot;screencast;snip;capture;record"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Unlock Screen Rotation"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Lock Screen Rotation"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Command not found"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Could not parse command:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Execution of “%s” failed:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Just now"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minute ago"
+msgstr[1] "%d minutes ago"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d hour ago"
+msgstr[1] "%d hours ago"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Yesterday"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d day ago"
+msgstr[1] "%d days ago"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d week ago"
+msgstr[1] "%d weeks ago"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d month ago"
+msgstr[1] "%d months ago"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d year ago"
+msgstr[1] "%d years ago"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Yesterday, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Yesterday, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l:%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Hotspot Login"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Deny Access"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Grant Access"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Unnamed Folder"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s has been pinned to the dash."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s has been unpinned from the dash."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Open Windows"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "New Window"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Show Details"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Quit"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Unpin"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Pin to Dash"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Launch using Integrated Graphics Card"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Launch using Discrete Graphics Card"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Select Audio Device"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Sound Settings"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Headphones"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Microphone"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Change Background…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Display Settings"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Settings"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Previous month"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Next month"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Week %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "No Notifications"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Do Not Disturb"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Clear"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” is not responding."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Force Quit"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Wait"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "External drive connected"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "External drive disconnected"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Unable to unlock volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "The installed udisks version does not support the PIM setting"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Open with %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Connect"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Key"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Private key password"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identity"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Service"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Authentication required"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Wired 802.1X authentication"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Network name"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL authentication"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN code required"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN code is needed for the mobile broadband device"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "A password is required to connect to “%s”."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Network Manager"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN password"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Authentication Required"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Authenticate"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Sorry, that didn’t work. Please try again."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s is now known as %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Windows"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Show Applications"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Today"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Tomorrow"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "All Day"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "No Events"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Add world clocks…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "World Clocks"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Loading…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Go online for weather information"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Weather information is currently unavailable"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Weather"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Select weather location…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Log Out %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Log Out"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s will be logged out automatically in %d second."
+msgstr[1] "%s will be logged out automatically in %d seconds."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "You will be logged out automatically in %d second."
+msgstr[1] "You will be logged out automatically in %d seconds."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Log Out"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Power Off"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Install Updates & Power Off"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "The system will power off automatically in %d second."
+msgstr[1] "The system will power off automatically in %d seconds."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Install pending software updates"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Power Off"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Restart"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Install Updates & Restart"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "The system will restart automatically in %d second."
+msgstr[1] "The system will restart automatically in %d seconds."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Restart"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Restart & Install Updates"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"The system will automatically restart and install updates in %d second."
+msgstr[1] ""
+"The system will automatically restart and install updates in %d seconds."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Restart &amp; Install"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Install &amp; Power Off"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Power off after updates are installed"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Restart & Install Upgrade"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Low battery power: please plug in before installing updates."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Some applications are busy or have unsaved work"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Other users are logged in"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Boot Options"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remote)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (console)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Install"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Install Extension"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Download and install “%s” from extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Extension Updates Available"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Extension updates are ready to be installed."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Allow inhibiting shortcuts"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "The application %s wants to inhibit shortcuts"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "An application wants to inhibit shortcuts"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "You can restore shortcuts by pressing %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Deny"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Allow"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Slow Keys Turned On"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Slow Keys Turned Off"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Sticky Keys Turned On"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Sticky Keys Turned Off"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Leave On"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Turn On"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Turn Off"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Leave Off"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Region & Language Settings"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "No extensions installed"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s has not emitted any errors."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Hide Errors"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Show Errors"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Enabled"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Disabled"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Error"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Out of date"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Downloading"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "View Source"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Web Page"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "System was put in unsafe mode"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Applications now have unrestricted access"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Undo"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Logged in as a privileged user"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Screen Lock disabled"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Screen Locking requires the GNOME display manager."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "System Information"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Unknown artist"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Unknown title"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Type to search"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Applications"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Overview"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "New shortcut…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Application defined"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Show on-screen help"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Switch monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Assign keystroke"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Done"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Edit…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "None"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Press a button to configure"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Press Esc to exit"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Press any key to exit"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Activities"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "System"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Top Bar"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Run a Command"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Press ESC to close"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Restart is not available on Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Restarting…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME needs to lock the screen"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Unable to lock"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Lock was blocked by an application"
+
+#: js/ui/screenshot.js:1159
+msgid "Selection"
+msgstr "Selection"
+
+#: js/ui/screenshot.js:1169
+msgid "Area Selection"
+msgstr "Area Selection"
+
+#: js/ui/screenshot.js:1174
+msgid "Screen"
+msgstr "Screen"
+
+#: js/ui/screenshot.js:1184
+msgid "Screen Selection"
+msgstr "Screen Selection"
+
+#: js/ui/screenshot.js:1189
+msgid "Window"
+msgstr "Window"
+
+#: js/ui/screenshot.js:1199
+msgid "Window Selection"
+msgstr "Window Selection"
+
+#: js/ui/screenshot.js:1237
+msgid "Screenshot / Screencast"
+msgstr "Screenshot / Screencast"
+
+#: js/ui/screenshot.js:1273
+msgid "Show Pointer"
+msgstr "Show Pointer"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1864
+msgid "Screencasts"
+msgstr "Screencasts"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1869
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Screencast from %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1911 js/ui/screenshot.js:2123
+msgid "Screenshot"
+msgstr "Screenshot"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1917
+msgid "Screencast recorded"
+msgstr "Screencast recorded"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1919
+msgid "Click here to view the video."
+msgstr "Click here to view the video."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1922 js/ui/screenshot.js:2137
+msgid "Show in Files"
+msgstr "Show in Files"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2083
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Screenshot from %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2129
+msgid "Screenshot captured"
+msgstr "Screenshot captured"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2131
+msgid "You can paste the image from the clipboard."
+msgstr "You can paste the image from the clipboard."
+
+#: js/ui/screenshot.js:2184 js/ui/screenshot.js:2349
+msgid "Screenshot taken"
+msgstr "Screenshot taken"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Searching…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "No results."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d more"
+msgstr[1] "%d more"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Search"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copy"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Paste"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Show Text"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Hide Text"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock is on."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Hidden Volume"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows System Volume"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Uses Keyfiles"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM Number"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Remember Password"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Unlock"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Open %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "The PIM must be a number or empty."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Unable to start %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Couldn’t find the %s application"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accessibility"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "High Contrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zoom"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Screen Reader"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Screen Keyboard"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Visual Alerts"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Sticky Keys"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Slow Keys"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Bounce Keys"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Mouse Keys"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Large Text"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Auto Rotate"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Brightness"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Dark Mode"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Single Click"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Double Click"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Drag"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Secondary Click"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Dwell Click"
+
+#: js/ui/status/keyboard.js:833
+msgid "Keyboard"
+msgstr "Keyboard"
+
+#: js/ui/status/keyboard.js:850
+msgid "Show Keyboard Layout"
+msgstr "Show Keyboard Layout"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Allow location access"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "The app %s wants to access your location"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Location access can be changed at any time from the privacy settings."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<unknown>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Disconnect %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Connect to %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s Hotspot"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Night Light"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Performance"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Balanced"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Power Saver"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Power Profiles"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Stop Screencast"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Stop Screen Sharing"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Aeroplane Mode"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Take Screenshot"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Power Off Menu"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspend"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Restart…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Power Off…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Log Out…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Switch User…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Lock Screen"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Unknown Thunderbolt device"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Unauthorised Thunderbolt device"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"New device has been detected and needs to be authorised by an administrator."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt authorisation error"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Could not authorise the Thunderbolt device: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Volume changed"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volume"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Sound Output"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Sound Input"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Mirror"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Join Displays"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "External Only"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Built-in Only"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Swipe up to unlock"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Click or press a key to unlock"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Unlock Window"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Log in as another user"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Welcome to GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "If you want to learn your way around, check out the tour."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "No Thanks"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Take Tour"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” is ready"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Keep these display settings?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Revert Settings"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Keep Changes"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Settings changes will revert in %d second"
+msgstr[1] "Settings changes will revert in %d seconds"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Hide"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restore"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximise"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Move"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Resize"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Move Titlebar Onscreen"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Always on Top"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Always on Visible Workspace"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Move to Workspace Left"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Move to Workspace Right"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Move to Workspace Up"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Move to Workspace Down"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Move to Monitor Up"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Move to Monitor Down"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Move to Monitor Left"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Move to Monitor Right"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Close"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution Calendar"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Print version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Mode used by GDM for login screen"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Use a specific mode, e.g. “gdm” for login screen"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "List possible modes"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Unknown"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Failed to launch “%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Passwords do not match."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Password cannot be blank"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Authentication dialogue was dismissed by the user"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensions"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Manage your GNOME Extensions"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "The GNOME Project"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configure GNOME Shell Extensions"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "No Matches"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Remove “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Remove"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Philip Withnall <philip@tecnocode.co.uk>\n"
+"Bruce Cowan <bruce@bcowan.me.uk>\n"
+"Chris Leonard <cjl@laptop.org>\n"
+"David King <amigadave@amigadave.com>\n"
+"Zander Brown <zbrown@gnome.org>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d extension will be updated on next login."
+msgstr[1] "%d extensions will be updated on next login."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "The extension is incompatible with the current GNOME version"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "The extension had an error"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "The extension can be updated"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Website"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Remove…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Help"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "About Extensions"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Manually Installed"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Built-In"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "No Installed Extensions"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Extension Updates Ready"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "The new extension was successfully created in %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Name"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Description"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Choose one of the available templates:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Template"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "The unique identifier of the new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "The user-visible name of the new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPTION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "A short description of what the extension does"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "TEMPLATE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "The template to use for the new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Enter extension information interactively"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Create a new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Unknown arguments"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, name and description are required"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Failed to connect to GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Extension “%s” does not exist\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Disable an extension"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "No UUID given"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "More than one UUID given"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Enable an extension"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Extension “%s” doesn’t exist\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Show extensions info"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Overwrite an existing extension"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Install an extension bundle"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "No extension bundle specified"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "More than one extension bundle specified"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Show user-installed extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Show system-installed extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Show enabled extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Show disabled extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Show extensions with preferences"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Show extensions with updates"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Print extension details"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "List installed extensions"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Additional source to include in the bundle"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "A GSettings schema that should be included"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "The directory where translations are found"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "The gettext domain to use for translations"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Overwrite an existing pack"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "The directory where the pack should be created"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "SOURCE_DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Create an extension bundle"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "More than one source directory specified"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Extension “%s” doesn’t have preferences\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Failed to open prefs for extension “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Opens extension preferences"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Reset an extension"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Cannot uninstall system extensions\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Failed to uninstall “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Uninstall an extension"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Do not print error messages"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Path"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Original author"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Version"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "State"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” takes no arguments"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Usage:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Print version information and exit."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMMAND"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Commands:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Print help"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Enable extension"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Disable extension"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Reset extension"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Uninstall extension"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "List extensions"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Show extension info"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Open extension preferences"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Create extension"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Package extension"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Install extension bundle"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Use “%s” to get detailed help.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Plain"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "An empty extension"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Add an icon to the top bar"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u Output"
+msgstr[1] "%u Outputs"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u Input"
+msgstr[1] "%u Inputs"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "System Sounds"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Bluetooth Settings"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d Connected"
+#~ msgstr[1] "%d Connected"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth On"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth Off"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Location Enabled"
+
+#~ msgid "Disable"
+#~ msgstr "Disable"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Privacy Settings"
+
+#~ msgid "Location In Use"
+#~ msgstr "Location In Use"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Location Disabled"
+
+#~ msgid "Enable"
+#~ msgstr "Enable"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s Off"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s Connected"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s Unmanaged"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s Connecting"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s Requires Authentication"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Firmware Missing For %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s Unavailable"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s Connection Failed"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Wired Settings"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Mobile Broadband Settings"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s Hardware Disabled"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s Disabled"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Connect to Internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Aeroplane Mode is On"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Wi-Fi is disabled when aeroplane mode is on."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Turn Off Aeroplane Mode"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi is Off"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Wi-Fi needs to be turned on in order to connect to a network."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Turn On Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wi-Fi Networks"
+
+#~ msgid "Select a network"
+#~ msgstr "Select a network"
+
+#~ msgid "No Networks"
+#~ msgstr "No Networks"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Use hardware switch to turn off"
+
+#~ msgid "Select Network"
+#~ msgstr "Select Network"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wi-Fi Settings"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s Not Connected"
+
+#~ msgid "connecting…"
+#~ msgstr "connecting…"
+
+#~ msgid "authentication required"
+#~ msgstr "authentication required"
+
+#~ msgid "connection failed"
+#~ msgstr "connection failed"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN Settings"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN Off"
+
+#~ msgid "Network Settings"
+#~ msgstr "Network Settings"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s Wired Connection"
+#~ msgstr[1] "%s Wired Connections"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Wi-Fi Connection"
+#~ msgstr[1] "%s Wi-Fi Connections"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s Modem Connection"
+#~ msgstr[1] "%s Modem Connections"
+
+#~ msgid "Connection failed"
+#~ msgstr "Connection failed"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Activation of network connection failed"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Night Light Disabled"
+
+#~ msgid "Resume"
+#~ msgstr "Resume"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Disable Until Tomorrow"
+
+#~ msgid "Power Settings"
+#~ msgstr "Power Settings"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Fully Charged"
+
+#~ msgid "Not Charging"
+#~ msgstr "Not Charging"
+
+#~ msgid "Estimating…"
+#~ msgstr "Estimating…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d Remaining (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d Until Full (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Screen is Being Shared"
+
+#~ msgid "Turn off"
+#~ msgstr "Turn off"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Aeroplane Mode On"
+
+#~ msgid "Lock"
+#~ msgstr "Lock"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Power Off / Log Out"
+
+#~ msgid "Log Out"
+#~ msgstr "Log Out"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Enable introspection API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Remove from Favourites"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Add to Favourites"
+
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Warning"
+#~ msgstr "Warning"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Failed to connect to GNOME Shell"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimise"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Unmaximise"
+
+#~ msgid "App Picker View"
+#~ msgstr "App Picker View"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Index of the currently selected view in the application picker."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Frequently used applications will appear here"
+
+#~ msgid "Frequent"
+#~ msgstr "Frequent"
+
+#~ msgid "All"
+#~ msgstr "All"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d %B, %Y"
+
+#~ msgid "Off"
+#~ msgstr "Off"
+
+#~ msgid "On"
+#~ msgstr "On"
+
+#~ msgid "Copy Error"
+#~ msgstr "Copy Error"
+
+#~ msgid "Next"
+#~ msgstr "Next"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Sign In"
+
+#~ msgid "Password: "
+#~ msgstr "Password: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Enter Password…"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d new message"
+#~ msgstr[1] "%d new messages"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d new notification"
+#~ msgstr[1] "%d new notifications"
+
+#~| msgid "Shell Extensions"
+#~ msgid "org.gnome.Extensions"
+#~ msgstr "org.gnome.Extensions"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Browse in Software"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Lock Orientation"
+
+#~| msgid "Username: "
+#~ msgid "Rename"
+#~ msgstr "Rename"
+
+#~ msgid "Type again:"
+#~ msgstr "Type again:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Authentication required by wireless network"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Mobile broadband network password"
+
+#~ msgid "Account Settings"
+#~ msgstr "Account Settings"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Orientation Lock"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Which keyboard to use"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "The type of keyboard to use."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "There was an error loading the preferences dialogue for %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s all day."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, then %s later."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, then %s, followed by %s later."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Feels like %s."
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Web Authentication Redirect"
+
+#~ msgid "Events"
+#~ msgstr "Events"
+
+#~ msgid "Notifications"
+#~ msgstr "Notifications"
+
+#~ msgid "Hide tray"
+#~ msgstr "Hide tray"
+
+#~ msgid "Status Icons"
+#~ msgstr "Status Icons"
+
+#~ msgid "Media"
+#~ msgstr "Media"
+
+#~ msgid "Not In Use"
+#~ msgstr "Not In Use"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Show the week date in the calendar"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "If true, display the ISO week date in the calendar."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Use as Internet connection"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Show the message tray"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (Wayland compositor)"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "The maximum accuracy level of location."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Su"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "M"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Tu"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "W"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Th"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "F"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Sa"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Nothing Scheduled"
+
+#~ msgid "This week"
+#~ msgstr "This week"
+
+#~ msgid "Next week"
+#~ msgstr "Next week"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Removable Devices"
+
+#~ msgid "Eject"
+#~ msgstr "Eject"
+
+#~ msgid "Invitation"
+#~ msgstr "Invitation"
+
+#~ msgid "Call"
+#~ msgstr "Call"
+
+#~ msgid "File Transfer"
+#~ msgstr "File Transfer"
+
+#~ msgid "Chat"
+#~ msgstr "Chat"
+
+#~ msgid "Unmute"
+#~ msgstr "Unmute"
+
+#~ msgid "Mute"
+#~ msgstr "Mute"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Invitation to %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s is inviting you to join %s"
+
+#~ msgid "Decline"
+#~ msgstr "Decline"
+
+#~ msgid "Accept"
+#~ msgstr "Accept"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Video call from %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Call from %s"
+
+#~ msgid "Answer"
+#~ msgstr "Answer"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s is sending you %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s would like permission to see when you are online"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Authentication failed"
+
+#~ msgid "Encryption error"
+#~ msgstr "Encryption error"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Certificate not provided"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Certificate untrusted"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certificate expired"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certificate not activated"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Certificate hostname mismatch"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Certificate fingerprint mismatch"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificate self-signed"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Status is set to offline"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Certificate is invalid"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Connection has been refused"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Connection can't be established"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Connection has been lost"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "This account is already connected to the server"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "Connection has been replaced by a new connection using the same resource"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "The account already exists on the server"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Server is currently too busy to handle the connection"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Certificate has been revoked"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Certificate uses an insecure cypher algorithm or is cryptographically weak"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+
+#~ msgid "Internal error"
+#~ msgstr "Internal error"
+
+#~ msgid "View account"
+#~ msgstr "View account"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Open Calendar"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Date & Time Settings"
+
+#~ msgid "Open"
+#~ msgstr "Open"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Clear Messages"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Notification Settings"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Tray Menu"
+
+#~ msgid "No Messages"
+#~ msgstr "No Messages"
+
+#~ msgid "Message Tray"
+#~ msgstr "Message Tray"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d Connected Device"
+#~ msgstr[1] "%d Connected Devices"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Battery"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "List of categories that should be displayed as folders"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Arrangement of buttons on the titlebar"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Select an extension to configure using the combobox above."
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> <b>%B</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b> <b>%B</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~| msgid "Settings"
+#~ msgid "Settings Menu"
+#~ msgstr "Settings Menu"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Authorisation request from %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Device %s wants to pair with this computer"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Device %s wants access to the service '%s'"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Grant this time only"
+
+#~ msgid "Reject"
+#~ msgstr "Reject"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Pairing confirmation for %s"
+
+#~| msgid ""
+#~| "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+
+#~ msgid "Does not match"
+#~ msgstr "Does not match"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Pairing request for %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Please enter the PIN mentioned on the device."
+
+#~ msgid "OK"
+#~ msgstr "OK"
+
+#~ msgid "unavailable"
+#~ msgstr "unavailable"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s the Oracle says"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Whether to collect statistics about application usage"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove existing data."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Keybinding to toggle the screen recorder"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Keybinding to start/stop the builtin screen recorder."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Framerate used for recording screencasts."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames per second."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "The GStreamer pipeline used to encode the screencast"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "File extension used for storing the screencast"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "Session…"
+
+#~ msgid "Power"
+#~ msgstr "Power"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "Click Log Out to quit these applications and log out of the system."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Logging out of the system."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Click Power Off to quit these applications and power off the system."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Powering off the system."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "Click Restart to quit these applications and restart the system."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Restarting the system."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Universal Access Settings"
+
+#~ msgid "Visibility"
+#~ msgstr "Visibility"
+
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Set Up a New Device…"
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "Send Files…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Keyboard Settings"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Mouse Settings"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Volume, network, battery"
+
+#~ msgid "disabled"
+#~ msgstr "disabled"
+
+#~ msgid "cable unplugged"
+#~ msgstr "cable unplugged"
+
+#~ msgid "More…"
+#~ msgstr "More…"
+
+#~ msgid "Wired"
+#~ msgstr "Wired"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Auto Ethernet"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Auto broadband"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Auto dial-up"
+
+#~ msgid "Auto %s"
+#~ msgstr "Auto %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Auto bluetooth"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Auto wireless"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d hour remaining"
+#~ msgstr[1] "%d hours remaining"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s remaining"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "hour"
+#~ msgstr[1] "hours"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minute"
+#~ msgstr[1] "minutes"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d minute remaining"
+#~ msgstr[1] "%d minutes remaining"
+
+#~| msgid "AC adapter"
+#~ msgid "AC Adapter"
+#~ msgstr "AC Adapter"
+
+#~| msgid "Laptop battery"
+#~ msgid "Laptop Battery"
+#~ msgstr "Laptop Battery"
+
+#~ msgid "Monitor"
+#~ msgstr "Monitor"
+
+#~ msgid "Mouse"
+#~ msgstr "Mouse"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~| msgid "Cell phone"
+#~ msgid "Cell Phone"
+#~ msgstr "Mobile Phone"
+
+#~| msgid "Media player"
+#~ msgid "Media Player"
+#~ msgstr "Media Player"
+
+#~ msgid "Tablet"
+#~ msgstr "Tablet"
+
+#~ msgid "Computer"
+#~ msgstr "Computer"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Unknown"
+
+#~ msgid "Available"
+#~ msgstr "Available"
+
+#~ msgid "Busy"
+#~ msgstr "Busy"
+
+#~ msgid "Invisible"
+#~ msgstr "Invisible"
+
+#~ msgid "Away"
+#~ msgstr "Away"
+
+#~ msgid "Idle"
+#~ msgstr "Idle"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Your chat status will be set to busy"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Shutting down might cause them to lose unsaved work."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Sign In"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APPLICATIONS"
+
+#~ msgid "SETTINGS"
+#~ msgstr "SETTINGS"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Subscription request"
+#~ msgstr "Subscription request"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Sent at <b>%X</b> on <b>%A</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Sent on <b>%A</b>, <b>%d %B</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Connection to %s failed"
+
+#~ msgid "Reconnect"
+#~ msgstr "Reconnect"
+
+#~ msgid "tray"
+#~ msgstr "tray"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Browse Files…"
+
+#~ msgid "Error browsing device"
+#~ msgstr "Error browsing device"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "The requested device cannot be browsed, error is '%s'"
+
+#~ msgid "More..."
+#~ msgstr "More…"
+
+#~ msgid "Wireless"
+#~ msgstr "Wireless"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN Connections"
+
+#~ msgid "System Settings"
+#~ msgstr "System Settings"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Your favourite Easter Egg"
+
+#~ msgid "United Kingdom"
+#~ msgstr "United Kingdom"
+
+#~ msgid "Default"
+#~ msgstr "Default"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "disabled OpenSearch providers"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Failed to unmount '%s'"
+
+#~ msgid "Retry"
+#~ msgstr "Retry"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "PLACES & DEVICES"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Show time with seconds"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "If true, display seconds in time."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Show date in clock"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "If true, display date in the clock, in addition to time."
+
+#~ msgid "CONTACTS"
+#~ msgstr "CONTACTS"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %e %b, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e %b, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Wrong password, please try again"
+
+#~ msgid "%s is online."
+#~ msgstr "%s is online."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s is offline."
+
+#~ msgid "%s is away."
+#~ msgstr "%s is away."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s is busy."
+
+#~ msgid "Hidden"
+#~ msgstr "Hidden"
+
+#~ msgid "Power Off..."
+#~ msgstr "Power Off…"
+
+#~ msgid "Online Accounts"
+#~ msgstr "Online Accounts"
+
+#~ msgid "Log Out..."
+#~ msgstr "Log Out…"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "RECENT ITEMS"
+
+#~ msgid "Show password"
+#~ msgstr "Show password"
+
+#~ msgid "If true, display onscreen keyboard."
+#~ msgstr "If true, display on-screen keyboard."
+
+#~ msgid "Connectivity lost"
+#~ msgstr "Connectivity lost"
+
+#~ msgid "You're no longer connected to the network"
+#~ msgstr "You're no longer connected to the network"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s has finished starting"
+
+#~ msgid "Localization Settings"
+#~ msgstr "Localisation Settings"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "You're now connected to mobile broadband connection '%s'"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "You're now connected to wireless network '%s'"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "You're now connected to VPN network '%s'"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "Less than a minute ago"
+
+#~ msgid "Shut Down"
+#~ msgstr "Shut Down"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr ""
+#~ "Click Shut Down to quit these applications and shut down the system."
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "The system will shut down automatically in %d seconds."
+
+#~ msgid "Shutting down the system."
+#~ msgstr "Shutting down the system."
+
+#~ msgid "Confirm"
+#~ msgstr "Confirm"
+
+#~ msgid "Panel"
+#~ msgstr "Panel"
+
+#~ msgid "Customize the panel clock"
+#~ msgstr "Customise the panel clock"
+
+#~ msgid "Custom format of the clock"
+#~ msgstr "Custom format of the clock"
+
+#~ msgid "Hour format"
+#~ msgstr "Hour format"
+
+#~ msgid ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+#~ msgstr ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+
+#~ msgid "Overview workspace view mode"
+#~ msgstr "Overview workspace view mode"
+
+#~ msgid ""
+#~ "The selected workspace view mode in the overview. Supported values are "
+#~ "\"single\" and \"grid\"."
+#~ msgstr ""
+#~ "The selected workspace view mode in the overview. Supported values are "
+#~ "\"single\" and \"grid\"."
+
+#~ msgid ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+#~ msgstr ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+
+#~ msgid ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+#~ msgstr ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+
+#~ msgid "Clock Format"
+#~ msgstr "Clock Format"
+
+#~ msgid "Clock Preferences"
+#~ msgstr "Clock Preferences"
+
+#~ msgid "Show seco_nds"
+#~ msgstr "Show seco_nds"
+
+#~ msgid "_12 hour format"
+#~ msgstr "_12 hour format"
+
+#~ msgid "_24 hour format"
+#~ msgstr "_24 hour format"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "PREFERENCES"
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "Drag here to add favourites"
+
+#~ msgid "Find"
+#~ msgstr "Find"
+
+#~ msgid "Preferences"
+#~ msgstr "Preferences"
+
+#~ msgid "ON"
+#~ msgstr "ON"
+
+#~ msgid "OFF"
+#~ msgstr "OFF"
+
+#~ msgid "System Preferences..."
+#~ msgstr "System Preferences…"
+
+#~ msgid ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+#~ msgstr ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "Can't remove the first workspace."
+
+#~ msgid "Sidebar"
+#~ msgstr "Sidebar"
+
+#~ msgid "Recent Documents"
+#~ msgstr "Recent Documents"
+
+#~ msgid "Browse"
+#~ msgstr "Browse"
+
+#~ msgid "(see all)"
+#~ msgstr "(see all)"
+
+#~ msgid "PLACES"
+#~ msgstr "PLACES"
+
+#~ msgid "SEARCH RESULTS"
+#~ msgstr "SEARCH RESULTS"
+
+#~ msgid "Can't lock screen: %s"
+#~ msgstr "Can't lock screen: %s"
+
+#~ msgid "Can't temporarily set screensaver to blank screen: %s"
+#~ msgstr "Can't temporarily set screensaver to blank screen: %s"
+
+#~ msgid "Can't logout: %s"
+#~ msgstr "Can't logout: %s"
diff --git a/po/eo.po b/po/eo.po
new file mode 100644
index 0000000..0de8118
--- /dev/null
+++ b/po/eo.po
@@ -0,0 +1,3467 @@
+# Esperanto translation for gnome-shell.
+# Copyright (C) 2011 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+# Tiffany ANTOPOLSKI <tiffany.antopolski@gmail.com>, 2011, 2012.
+# Ryan LORTIE <desrt@desrt.ca>, 2013.
+# Daniel PUENTES <blatberk@openmailbox.org>, 2015.
+# Nicolas MAIA < >, 2015.
+# Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>, 2011-2020.
+# Carmen Bianca BAKKER <carmen@carmenbianca.eu>, 2018-2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2021-04-06 19:02+0000\n"
+"PO-Revision-Date: 2021-03-29 18:39+0200\n"
+"Last-Translator: Carmen Bianca BAKKER <carmen@carmenbianca.eu>\n"
+"Language-Team: Esperanto <gnome-eo-list@gnome.org>\n"
+"Language: eo\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"X-Generator: Gtranslator 3.38.0\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistemo"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Montri la sciig-liston"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokusi la aktivan sciigon"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Montri la superrigardon"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Montri ĉiujn aplikaĵojn"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Malfermi la aplikaĵo-menuon"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME-ŝelo"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Fenestroadministrado kaj aplikaĵo-lanĉado"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Ŝalti internajn ilojn por programistoj kaj testantoj per Alt+F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permesas aliron al interna sencimigaj kaj kontroladaj iloj uzante la Alt+F2-"
+"dialogon."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID-oj de ŝaltendaj kromprogramoj"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME-ŝelaj kromprogramoj havas UUID-atributon; ĉi tiu ŝlosilo listigas "
+"kromprogramojn kiuj devus ŝargitiĝi. Iu ajn kromprogramo kiu volas esti "
+"ŝargita devas esti en ĉi tiu listo. Vi povas ankaŭ ŝanĝi ĉi tiun liston per "
+"la ‘EnableExtension’- kaj ‘DisableExtension’ D-Bus-metodoj sur org.gnome."
+"Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID-oj de la etendaĵoj kiuj estu tuje ĉesigitaj"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME-ŝelaj kromprogramoj havas UUID-atributon; ĉi tiu ŝlosilo listigas "
+"etendaĵojn kiuj estu malŝaltitaj, eĉ se ili estas ŝargita de la nuna reĝimo. "
+"Vi povas ankaŭ ŝanĝi ĉi tiun liston per la ‘EnableExtension’- kaj "
+"‘DisableExtension’ D-Bus-metodoj sur org.gnome.Shell. Ĉi tiu ŝlosilo superas "
+"la “enabled-extensions”-agordon."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Malŝalti uzanto-etendaĵon"
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Malŝaltas la validigadon de kromprogramversia kongrueco"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME-ŝelo nur ŝargos kromprogramojn, kiuj asertas subteni la aktualan "
+"rulantan version. Ŝaltante tiun ĉi opcion, oni malŝaltas tiun kontrolon kaj "
+"ŝargos ĉiujn kromprogramojn, sendepende de la versioj, ke tiuj kromprogramoj "
+"asertas subteni."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Listo de la identiloj de la labortablaj dosieroj por preferataj aplikaĵoj"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"La aplikaĵoj kiuj korespondas al tiuj identigiloj estos montrita en la areo "
+"de la preferataj aplikaĵoj."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historio de la komando-dialogo (Alt+F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historio de la dialogo de ‘looking glass’"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Ĉiam montri la menueron “Adiaŭi” en la uzanto-menuo."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ĉi tiu ŝlosilo transpasas la aŭtomatan kaŝon de la menuero “Adiaŭi” por unu-"
+"uzantulaj, unu-seancaj situacioj."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "Ĉu memorigi pasvorton por surmeti ĉifritajn aŭ forajn dosiersistemojn"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"La ŝelo petos pasvorton kiam ĉifrita aparato aŭ defora dosiersistemo estas "
+"surmetata. Se la pasvorto konserveblas por estonta uzo, “Memorigi pasvorton” "
+"markobutono montritos. Ĉi ŝlosilo agordas la implicitan staton de la "
+"markobutono."
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Enable introspection API"
+msgstr "Ŝalti introspektan API-on"
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to open the application menu"
+msgstr "Klavkombino por malfermi la aplikaĵo-menuon"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu."
+msgstr "Klavkombino por malfermi la aplikaĵo-menuon."
+
+#: data/org.gnome.shell.gschema.xml.in:164
+#: data/org.gnome.shell.gschema.xml.in:171
+#, fuzzy
+#| msgid "Keybinding to open the overview"
+msgid "Keybinding to shift between overview states"
+msgstr "Klavkombino por malfermi la superrigardon"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Klavkombino por malfermi la vidon “Montri aplikaĵojn”"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Klavkombino por malfermi la vidon “Montri aplikaĵojn” de la Agoj-"
+"Superrigardo."
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to open the overview"
+msgstr "Klavkombino por malfermi la superrigardon"
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the Activities Overview."
+msgstr "Klavkombino por malfermi la Agoj-Superrigardon."
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Klavkombino por baskuligi la videblecon de la sciigolisto"
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Klavkombino por baskuligi la videblecon de la sciigolisto."
+
+#: data/org.gnome.shell.gschema.xml.in:200
+msgid "Keybinding to focus the active notification"
+msgstr "Klavkombino por fokusi la aktivan sciigon"
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification."
+msgstr "Klavkombino por fokusi la aktivan sciigon."
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 1"
+msgstr "Ŝanĝi al aplikaĵo 1"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 2"
+msgstr "Ŝanĝi al aplikaĵo 2"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 3"
+msgstr "Ŝanĝi al aplikaĵo 3"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 4"
+msgstr "Ŝanĝi al aplikaĵo 4"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 5"
+msgstr "Ŝanĝi al aplikaĵo 5"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 6"
+msgstr "Ŝanĝi al aplikaĵo 6"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 7"
+msgstr "Ŝanĝi al aplikaĵo 7"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "Switch to application 8"
+msgstr "Ŝanĝi al aplikaĵo 8"
+
+#: data/org.gnome.shell.gschema.xml.in:239
+msgid "Switch to application 9"
+msgstr "Ŝanĝi al aplikaĵo 9"
+
+#: data/org.gnome.shell.gschema.xml.in:248
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Limit switcher to current workspace."
+msgstr "Limigi ŝaltilon al aktuala laborspaco."
+
+#: data/org.gnome.shell.gschema.xml.in:249
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Se vera, nur aplikaĵoj, kiuj havas fenestrojn en la aktuala laborspaco, "
+"estas montritaj en la ŝaltilo. Alimaniere, ĉiuj aplikaĵoj estas inkluzivitaj."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid "The application icon mode."
+msgstr "La aplikaĵo bildsimbola reĝimo."
+
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Agordas kiel la fenestro montritas en la ŝaltilo. Eblaj valoroj estas "
+"“thumbnail-only” (nur bildeto), “app-icon-only” (nur aplikaĵa bildsimbolo) "
+"aŭ “both” (ambaŭ)."
+
+#: data/org.gnome.shell.gschema.xml.in:276
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Se vera, nur fenestroj de la nuna laborspaco estas montrataj en la ŝaltilo. "
+"Alimaniere, ĉiuj fenestroj estas inkluzivitaj."
+
+#: data/org.gnome.shell.gschema.xml.in:286
+msgid "Locations"
+msgstr "Lokoj"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Location"
+msgstr "Loko"
+
+#: data/org.gnome.shell.gschema.xml.in:318
+msgid "Attach modal dialog to the parent window"
+msgstr "Kunligi modalan dialogon al la patra fenestro"
+
+#: data/org.gnome.shell.gschema.xml.in:319
+#: data/org.gnome.shell.gschema.xml.in:328
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ĉi ŝlosilo transpasas la ŝlosilon en org.gnome.mutter kiam uzanta GNOME-"
+"ŝelon."
+
+#: data/org.gnome.shell.gschema.xml.in:327
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Aktivigi kahelan aranĝon kiam demetante fenestrojn sur ekranaj borderoj"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Workspaces are managed dynamically"
+msgstr "Laborspacoj administratas dinamike"
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Workspaces only on primary monitor"
+msgstr "Laborspacoj nur sur la ĉefa ekrano"
+
+#: data/org.gnome.shell.gschema.xml.in:351
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "En musa reĝimo, prokrasti ŝanĝi fokuson ĝis la musmontrilo haltas"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Reta saluto"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:28
+#: subprojects/extensions-app/data/ui/extensions-window.ui:241
+msgid "Something’s gone wrong"
+msgstr "Io fiaskis"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:39
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Ni pardonpetas, sed estis problemo: la agordoj de ĉi tiu etendaĵo ne "
+"montreblas. Ni rekomendas, ke vi raportu la problemon al la aŭtoroj de la "
+"etendaĵo."
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:64
+msgid "Technical Details"
+msgstr "Teĥnikaj detaloj"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:106
+msgid "Homepage"
+msgstr "Hejmpaĝo"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:107
+msgid "Visit extension homepage"
+msgstr "Viziti hejmpaĝon de etendaĵo"
+
+#: js/gdm/authPrompt.js:141 js/ui/audioDeviceSelection.js:58
+#: js/ui/components/networkAgent.js:111 js/ui/components/polkitAgent.js:138
+#: js/ui/endSessionDialog.js:438 js/ui/extensionDownloader.js:190
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:946 subprojects/extensions-app/js/main.js:183
+msgid "Cancel"
+msgstr "Nuligi"
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:244 js/ui/components/networkAgent.js:210
+#: js/ui/components/networkAgent.js:226 js/ui/components/networkAgent.js:250
+#: js/ui/components/networkAgent.js:271 js/ui/components/networkAgent.js:291
+#: js/ui/components/networkAgent.js:301 js/ui/components/polkitAgent.js:275
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "Pasvorto"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Elekti seancon"
+
+#: js/gdm/loginDialog.js:456
+msgid "Not listed?"
+msgstr "Ĉu ne en listo?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:918
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ekzemple, uzanto aŭ %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:923 js/ui/components/networkAgent.js:246
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:287
+msgid "Username"
+msgstr "Uzantonomo"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Salutfenestro"
+
+#: js/gdm/util.js:430
+msgid "Authentication error"
+msgstr "Aŭtentigo-eraro"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:589
+msgid "(or swipe finger across reader)"
+msgstr "(aŭ pasu fingron trans legilo)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:594
+msgid "(or place finger on reader)"
+msgstr "(aŭ metu fingron sur legilo)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:82
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Malŝalti"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:85
+msgid "power off;shutdown;halt;stop"
+msgstr "malŝalti;sistemfermo;paŭzigi;haltigi"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:90
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Restartigi"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:93
+msgid "reboot;restart;"
+msgstr "restartigi"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:98
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Ŝlosa ekrano"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:101
+msgid "lock screen"
+msgstr "ŝlosa ekrano"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Adiaŭi"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "logout;log out;sign off"
+msgstr "adiaŭi;elsaluti"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:114
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Halteti"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:117
+msgid "suspend;sleep"
+msgstr "halteti;dormi;dormeti"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:122
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Ŝanĝi uzanton"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:125
+msgid "switch user"
+msgstr "ŝanĝi uzanton"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "ŝlosekrana orientiĝo;malŝlosekrana orientiĝo;ekrano;rotacio"
+
+#: js/misc/systemActions.js:232
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Malŝlosekrana orientiĝo"
+
+#: js/misc/systemActions.js:233
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Ŝlosekrana orientiĝo"
+
+#: js/misc/util.js:120
+msgid "Command not found"
+msgstr "Komando ne trovita"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:156
+msgid "Could not parse command:"
+msgstr "Ne eblis analizi komandon:"
+
+#: js/misc/util.js:164
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Plenumo de “%s” malsukcesis:"
+
+#: js/misc/util.js:181
+msgid "Just now"
+msgstr "Ĵus"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Antaŭ %d minuto"
+msgstr[1] "Antaŭ %d minutoj"
+
+#: js/misc/util.js:187
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Antaŭ %d horo"
+msgstr[1] "Antaŭ %d horoj"
+
+#: js/misc/util.js:191 js/ui/dateMenu.js:162
+msgid "Yesterday"
+msgstr "Hieraŭ"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Antaŭ %d tago"
+msgstr[1] "Antaŭ %d tagoj"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Antau %d semajno"
+msgstr[1] "Antau %d semajnoj"
+
+#: js/misc/util.js:201
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Antaŭ %d monato"
+msgstr[1] "Antaŭ %d monatoj"
+
+#: js/misc/util.js:204
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Antaŭ %d jaro"
+msgstr[1] "Antaŭ %d jaroj"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:237
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:243
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Hieraŭ, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:249
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:255
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d-a de %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:261
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d-a de %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:266
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:272
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Hieraŭ, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:278
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:284
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d-a de %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:290
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d-a de %B %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:42
+msgid "Hotspot Login"
+msgstr "Retkapteja salutado"
+
+#: js/portalHelper/main.js:88
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Via konekto al ĉi tiu retkapetja salutado ne estas sekura. Pasvortojn kaj "
+"aliajn informojn, kiujn vi enigas en ĉi tiu paĝo, apudaj aliuloj povas vidi."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:369
+msgid "Deny Access"
+msgstr "Rifuzi aliron"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:372
+msgid "Grant Access"
+msgstr "Konsenti aliron"
+
+#: js/ui/appDisplay.js:1817
+msgid "Unnamed Folder"
+msgstr "Sennoma dosierujo"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:3394 js/ui/panel.js:33
+msgid "Open Windows"
+msgstr "Malfermaj fenestroj"
+
+#: js/ui/appDisplay.js:3413 js/ui/panel.js:40
+msgid "New Window"
+msgstr "Nova fenestro"
+
+#: js/ui/appDisplay.js:3429
+msgid "Launch using Integrated Graphics Card"
+msgstr "Lanĉi uzante integritan vidkarton"
+
+#: js/ui/appDisplay.js:3430
+msgid "Launch using Discrete Graphics Card"
+msgstr "Lanĉi uzante dediĉitan vidkarton"
+
+#: js/ui/appDisplay.js:3459 js/ui/dash.js:245
+msgid "Remove from Favorites"
+msgstr "Forigi el preferataj"
+
+#: js/ui/appDisplay.js:3465
+msgid "Add to Favorites"
+msgstr "Aldoni al preferataj"
+
+#: js/ui/appDisplay.js:3475 js/ui/panel.js:51
+msgid "Show Details"
+msgstr "Montri detalojn"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s estas aldonita de via preferataj aplikaĵoj."
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s estas forigita de via preferataj aplikaĵoj."
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Elekti sonaparaton"
+
+#: js/ui/audioDeviceSelection.js:55
+msgid "Sound Settings"
+msgstr "Sonagordoj"
+
+#: js/ui/audioDeviceSelection.js:65
+msgid "Headphones"
+msgstr "Kapaŭskultilo"
+
+#: js/ui/audioDeviceSelection.js:67
+msgid "Headset"
+msgstr "Kaptelefono"
+
+#: js/ui/audioDeviceSelection.js:69 js/ui/status/volume.js:277
+msgid "Microphone"
+msgstr "Mikrofono"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Ŝanĝi ekranfonon…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Ekran-agordoj"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "Agordoj"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:36
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:65
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:67
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:69
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:71
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:73
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ĵ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:75
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:77
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:392
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:461
+msgid "Previous month"
+msgstr "Antaŭa monato"
+
+#: js/ui/calendar.js:476
+msgid "Next month"
+msgstr "Sekva monato"
+
+#: js/ui/calendar.js:626
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%-d"
+
+#: js/ui/calendar.js:682
+msgid "Week %V"
+msgstr "Semajno %V"
+
+#: js/ui/calendar.js:896
+msgid "No Notifications"
+msgstr "Neniu sciigo"
+
+#: js/ui/calendar.js:950
+msgid "Do Not Disturb"
+msgstr "Ne interrompu"
+
+#: js/ui/calendar.js:969
+msgid "Clear"
+msgstr "Vakigi"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” ne respondas."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "Vi povas atendi por ke ĝi daŭrigu aŭ devigi ke la aplikaĵo ĉesu."
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "Devige ĉesigi"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "Atendi"
+
+#: js/ui/components/automountManager.js:85
+msgid "External drive connected"
+msgstr "Ekstera disko konektitas"
+
+#: js/ui/components/automountManager.js:97
+msgid "External drive disconnected"
+msgstr "Ekstera disko malkonektitas"
+
+#: js/ui/components/automountManager.js:206
+msgid "Unable to unlock volume"
+msgstr "Ne eblas malŝlosi datumportilon"
+
+#: js/ui/components/automountManager.js:207
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "La instalita versio de udisks ne subtenas la PIM-agordon"
+
+#: js/ui/components/autorunManager.js:332
+#, javascript-format
+msgid "Open with %s"
+msgstr "Malfermi per %s"
+
+#: js/ui/components/networkAgent.js:93
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternative vi povas konekti per premi la “WPS”-butonon de via enkursigilo."
+
+#: js/ui/components/networkAgent.js:105 js/ui/status/network.js:258
+#: js/ui/status/network.js:349 js/ui/status/network.js:949
+msgid "Connect"
+msgstr "Konekti"
+
+#: js/ui/components/networkAgent.js:216
+msgid "Key"
+msgstr "Ŝlosilo"
+
+#: js/ui/components/networkAgent.js:254 js/ui/components/networkAgent.js:277
+msgid "Private key password"
+msgstr "Pasvorto por privata ŝlosilo"
+
+#: js/ui/components/networkAgent.js:275
+msgid "Identity"
+msgstr "Identeco"
+
+#: js/ui/components/networkAgent.js:289
+msgid "Service"
+msgstr "Servo"
+
+#: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:346
+#: js/ui/components/networkAgent.js:679 js/ui/components/networkAgent.js:700
+msgid "Authentication required"
+msgstr "Aŭtentokontrolo estas bezonata"
+
+#: js/ui/components/networkAgent.js:319 js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Pasvortoj aŭ ĉifro-ŝlosiloj estas necesaj por aliro al sendrata reto “%s”."
+
+#: js/ui/components/networkAgent.js:323 js/ui/components/networkAgent.js:684
+msgid "Wired 802.1X authentication"
+msgstr "Dratita 802.1X aŭtentokontrolo"
+
+#: js/ui/components/networkAgent.js:325
+msgid "Network name"
+msgstr "Reta nomo"
+
+#: js/ui/components/networkAgent.js:330 js/ui/components/networkAgent.js:688
+msgid "DSL authentication"
+msgstr "DSL-a aŭtentokontrolo"
+
+#: js/ui/components/networkAgent.js:337 js/ui/components/networkAgent.js:693
+msgid "PIN code required"
+msgstr "PIN-kodo bezonata"
+
+#: js/ui/components/networkAgent.js:338 js/ui/components/networkAgent.js:694
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN-kodo necesas por la portebla larĝkapacita aparato"
+
+#: js/ui/components/networkAgent.js:339
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:347 js/ui/components/networkAgent.js:685
+#: js/ui/components/networkAgent.js:689 js/ui/components/networkAgent.js:701
+#: js/ui/components/networkAgent.js:705
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Pasvorto necesas por konekti al “%s”."
+
+#: js/ui/components/networkAgent.js:668 js/ui/status/network.js:1723
+msgid "Network Manager"
+msgstr "Retadministrilo"
+
+#: js/ui/components/networkAgent.js:704
+msgid "VPN password"
+msgstr "VPR-Pasvorto"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "Aŭtentigo bezonata"
+
+#: js/ui/components/polkitAgent.js:79
+msgid "Administrator"
+msgstr "Administranto"
+
+#: js/ui/components/polkitAgent.js:141
+msgid "Authenticate"
+msgstr "Aŭtentigi"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:252 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Pardonon, tio ne funkciis. Bonvolu provi denove."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:822
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s estas nun konata kiel %s"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/overviewControls.js:395
+msgid "Windows"
+msgstr "Fenestroj"
+
+#: js/ui/dash.js:204 js/ui/dash.js:247
+msgid "Show Applications"
+msgstr "Montri aplikaĵojn"
+
+# Kiel macOS «dock”. --Carmen
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr "Doko"
+
+# Komento por tradukantoj estas neĝusta? --Carmen
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:79
+msgid "%B %-d %Y"
+msgstr "%-d-a de %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:86
+msgid "%A %B %e %Y"
+msgstr "%A, %-d-a de %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:151
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d-a de %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:154
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d-a de %B %Y"
+
+#: js/ui/dateMenu.js:160
+msgid "Today"
+msgstr "Hodiaŭ"
+
+#: js/ui/dateMenu.js:164
+msgid "Tomorrow"
+msgstr "Morgaŭ"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:180
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Tuttaga"
+
+#: js/ui/dateMenu.js:231
+msgid "No Events"
+msgstr "Neniu evento"
+
+#: js/ui/dateMenu.js:348
+msgid "Add world clocks…"
+msgstr "Aldoni mondajn horloĝojn…"
+
+#: js/ui/dateMenu.js:349
+msgid "World Clocks"
+msgstr "Mondaj horloĝoj"
+
+#: js/ui/dateMenu.js:629
+msgid "Loading…"
+msgstr "Ŝargante…"
+
+#: js/ui/dateMenu.js:639
+msgid "Go online for weather information"
+msgstr "Enretigi por veterinformoj"
+
+#: js/ui/dateMenu.js:641
+msgid "Weather information is currently unavailable"
+msgstr "Veterinformoj ĉimomente nehaveblas"
+
+#: js/ui/dateMenu.js:651
+msgid "Weather"
+msgstr "Vetero"
+
+#: js/ui/dateMenu.js:653
+msgid "Select weather location…"
+msgstr "Elekti veteran lokon…"
+
+#: js/ui/endSessionDialog.js:39
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Adiaŭi %s"
+
+#: js/ui/endSessionDialog.js:40
+msgctxt "title"
+msgid "Log Out"
+msgstr "Adiaŭi"
+
+#: js/ui/endSessionDialog.js:43
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s aŭtomate adiaŭos post %d sekundo."
+msgstr[1] "%s aŭtomate adiaŭos post %d sekundoj."
+
+#: js/ui/endSessionDialog.js:49
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Vi aŭtomate adiaŭos post %d sekundo."
+msgstr[1] "Vi aŭtomate adiaŭos post %d sekundoj."
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "Adiaŭi"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "Malŝalti"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instali ĝisdatigojn & malŝalti"
+
+#: js/ui/endSessionDialog.js:66
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "La sistemo aŭtomate malŝaltiĝos post %d sekundo."
+msgstr[1] "La sistemo aŭtomate malŝaltiĝos post %d sekundoj."
+
+#: js/ui/endSessionDialog.js:70 js/ui/endSessionDialog.js:89
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instali okazontajn ĝisdatigojn"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "Malŝalti"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "Restartigi"
+
+#: js/ui/endSessionDialog.js:82
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instali ĝisdatigojn & restartigi"
+
+#: js/ui/endSessionDialog.js:85
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "La sistemo aŭtomate restartiĝos post %d sekundo."
+msgstr[1] "La sistemo aŭtomate restartiĝos post %d sekundoj."
+
+#: js/ui/endSessionDialog.js:93
+msgctxt "button"
+msgid "Restart"
+msgstr "Restartigi"
+
+#: js/ui/endSessionDialog.js:101
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Restartigi & instali ĝisdatigojn"
+
+#: js/ui/endSessionDialog.js:104
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"La sistemo aŭtomate restartiĝos kaj instalos ĝisdatigojn post %d sekundo."
+msgstr[1] ""
+"La sistemo aŭtomate restartiĝos kaj instalos ĝisdatigojn post %d sekundoj."
+
+#: js/ui/endSessionDialog.js:111 js/ui/endSessionDialog.js:132
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Restartigi &amp; instali"
+
+#: js/ui/endSessionDialog.js:113
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instali &amp; malŝalti"
+
+#: js/ui/endSessionDialog.js:114
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Malŝalti post instalado de ĝisdatigoj"
+
+#: js/ui/endSessionDialog.js:121
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Restarti & instali ĝisdatigon"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:126
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s instaliĝos post restarto. Instalado de tiu ĝisdatigo povas daŭri "
+"longe: certigu, ke viaj datumoj sekurkopiiĝis kaj via komputilo konektiĝis."
+
+#: js/ui/endSessionDialog.js:284
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Uzante baterielektron: bonvolu konekti antaŭ ol instali ĝisdatigojn."
+
+#: js/ui/endSessionDialog.js:293
+msgid "Some applications are busy or have unsaved work"
+msgstr "Iuj aplikaĵoj okupitas aŭ havas nekonservitan laboron"
+
+#: js/ui/endSessionDialog.js:298
+msgid "Other users are logged in"
+msgstr "Aliaj uzantoj estas salutitaj"
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Startigaj opcioj"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:686
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (defora)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:689
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konzolo)"
+
+#: js/ui/extensionDownloader.js:194
+msgid "Install"
+msgstr "Instali"
+
+#: js/ui/extensionDownloader.js:200
+msgid "Install Extension"
+msgstr "Instali etendaĵon"
+
+#: js/ui/extensionDownloader.js:201
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Ĉu elŝuti kaj instali “%s” de extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:253
+msgid "Extension Updates Available"
+msgstr "Ĝisdatigoj de etendaĵoj disponeblas"
+
+#: js/ui/extensionSystem.js:254
+msgid "Extension updates are ready to be installed."
+msgstr "Ĝisdatigoj de etendaĵoj pretas por instalado."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Permesi malebligadon de klavkombinoj"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "La aplikaĵo %s volas malebligi klavkombinojn"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Aplikaĵo volas malebligi klavkombinojn"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Vi povas restaŭri klavkombinojn per premi %s."
+
+#: js/ui/inhibitShortcutsDialog.js:100
+msgid "Deny"
+msgstr "Rifuzi"
+
+#: js/ui/inhibitShortcutsDialog.js:107
+msgid "Allow"
+msgstr "Akcepti"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Malrapidaj klavoj ŝaltitaj"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Malrapidaj klavoj malŝaltitaj"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Vi premis la majuskligan klavon por 8 sekundoj. Ĉi tiu estas la klavkombino "
+"por la malrapidaj klavoj funkcio, kiu ŝanĝas la konduton de via klavaro."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Fiksaj klavoj ŝaltitaj"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Fiksaj klavoj malŝaltitaj"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Vi premis la majuskligan klavon kvinfoje. Ĉi tiu estas la klavkombino por la "
+"fiksaj klavoj funkcio, kiu ŝanĝas la konduton de via klavaro."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Vi premis du klavojn samtempe, aŭ premis la majuskligan klavon kvinfoje. Ĉi "
+"tiu malŝaltas la fiksaj klavoj funkcion, kiu ŝanĝas la konduton de via "
+"klavaro."
+
+#: js/ui/kbdA11yDialog.js:54
+msgid "Leave On"
+msgstr "Lasi ŝaltita"
+
+#: js/ui/kbdA11yDialog.js:54 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:1321
+msgid "Turn On"
+msgstr "Ŝalti"
+
+#: js/ui/kbdA11yDialog.js:62 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:166 js/ui/status/network.js:350
+#: js/ui/status/network.js:1321 js/ui/status/network.js:1433
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Malŝalti"
+
+#: js/ui/kbdA11yDialog.js:62
+msgid "Leave Off"
+msgstr "Lasi malŝaltita"
+
+#: js/ui/keyboard.js:227
+msgid "Region & Language Settings"
+msgstr "Regiono & lingvo agordoj"
+
+#: js/ui/lookingGlass.js:676
+msgid "No extensions installed"
+msgstr "Neniu kromprogramo instalita"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:734
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ne eligintaj ajnaj eraroj."
+
+#: js/ui/lookingGlass.js:740
+msgid "Hide Errors"
+msgstr "Kaŝi erarojn"
+
+#: js/ui/lookingGlass.js:744 js/ui/lookingGlass.js:810
+msgid "Show Errors"
+msgstr "Montri erarojn"
+
+#: js/ui/lookingGlass.js:753
+msgid "Enabled"
+msgstr "Enŝaltita"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:756 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Elŝaltita"
+
+#: js/ui/lookingGlass.js:758
+#: subprojects/extensions-app/data/ui/extension-row.ui:158
+msgid "Error"
+msgstr "Eraro"
+
+#: js/ui/lookingGlass.js:760
+msgid "Out of date"
+msgstr "Neaktuala"
+
+#: js/ui/lookingGlass.js:762
+msgid "Downloading"
+msgstr "Elŝutante"
+
+#: js/ui/lookingGlass.js:792
+msgid "View Source"
+msgstr "Montri fonton"
+
+#: js/ui/lookingGlass.js:801
+msgid "Web Page"
+msgstr "Retpaĝo"
+
+#: js/ui/main.js:294
+msgid "Logged in as a privileged user"
+msgstr "Saluti kiel privilegiulo"
+
+#: js/ui/main.js:295
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Oni devus eviti ruli seancon kiel privilegiulo pro sekureco. Se eble, vi "
+"devus saluti kiel norma uzanto."
+
+#: js/ui/main.js:344
+msgid "Screen Lock disabled"
+msgstr "Ekranŝlosado malŝaltita"
+
+#: js/ui/main.js:345
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Ekranŝlosado bezonas la GNOME-ekranadministrilon."
+
+#: js/ui/messageTray.js:1437
+msgid "System Information"
+msgstr "Sisteminformoj"
+
+#: js/ui/mpris.js:207
+msgid "Unknown artist"
+msgstr "Nekonata artisto"
+
+#: js/ui/mpris.js:217
+msgid "Unknown title"
+msgstr "Nekonata titolo"
+
+# Imperativo
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:307
+msgid "Type to search"
+msgstr "Tajpu por serĉi"
+
+#: js/ui/overviewControls.js:383
+msgid "Applications"
+msgstr "Aplikaĵoj"
+
+#: js/ui/overview.js:69
+msgid "Undo"
+msgstr "Malfari"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:82
+msgid "Overview"
+msgstr "Superrigardo"
+
+#: js/ui/padOsd.js:96
+msgid "New shortcut…"
+msgstr "Nova klavkombino…"
+
+#: js/ui/padOsd.js:143
+msgid "Application defined"
+msgstr "Aplikaĵo difinita"
+
+#: js/ui/padOsd.js:144
+msgid "Show on-screen help"
+msgstr "Montri surekranan helpon"
+
+#: js/ui/padOsd.js:145
+msgid "Switch monitor"
+msgstr "Ŝanĝi ekranon"
+
+#: js/ui/padOsd.js:146
+msgid "Assign keystroke"
+msgstr "Asigni klavpremon"
+
+#: js/ui/padOsd.js:212
+msgid "Done"
+msgstr "Plenumita"
+
+#: js/ui/padOsd.js:718
+msgid "Edit…"
+msgstr "Redakti…"
+
+#: js/ui/padOsd.js:760 js/ui/padOsd.js:877
+msgid "None"
+msgstr "Neniu"
+
+#: js/ui/padOsd.js:831
+msgid "Press a button to configure"
+msgstr "Premu klavon por agordi"
+
+#: js/ui/padOsd.js:832
+msgid "Press Esc to exit"
+msgstr "Premu Esk-klavon por ĉesi"
+
+#: js/ui/padOsd.js:835
+msgid "Press any key to exit"
+msgstr "Premu iun ajn klavon por ĉesi"
+
+#: js/ui/panel.js:65
+msgid "Quit"
+msgstr "Ĉesi"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:388
+msgid "Activities"
+msgstr "Agoj"
+
+#: js/ui/panel.js:659
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistemo"
+
+#: js/ui/panel.js:771
+msgid "Top Bar"
+msgstr "Supra breto"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Ruli komandon"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Premu Esk-klavon por fermi"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Restartigado ne disponeblas sur Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Restartante…"
+
+#: js/ui/screenShield.js:211
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME bezonas ŝlosi la ekranon"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:252 js/ui/screenShield.js:620
+msgid "Unable to lock"
+msgstr "Ne eblas ŝlosi"
+
+#: js/ui/screenShield.js:253 js/ui/screenShield.js:621
+msgid "Lock was blocked by an application"
+msgstr "Ŝloso estis blokita de aplikaĵo"
+
+#: js/ui/screenshot.js:141
+msgid "Screenshot taken"
+msgstr "Faris ekrankopion"
+
+#: js/ui/search.js:824
+msgid "Searching…"
+msgstr "Serĉante…"
+
+#: js/ui/search.js:826
+msgid "No results."
+msgstr "Neniu rezulto."
+
+#: js/ui/search.js:952
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d pli"
+msgstr[1] "%d pli"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Serĉi"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopii"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Alglui"
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr "Montri tekston"
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr "Kaŝi tekston"
+
+#: js/ui/shellEntry.js:162
+msgid "Caps lock is on."
+msgstr "Majuskla baskulo estas ŝaltita."
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr "Kaŝita datumportilo"
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr "Datumportilo sistema de Vindozo"
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr "Uzas ŝlosildosierojn"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Por malŝlosi datumportilon kiu uzas ŝlosildosierojn, anstataŭe uzu la <i>%s</"
+"i> programon."
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr "PIM-numero"
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "Memorigi pasvorton"
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr "Malŝlosi"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "Malfermi aplikaĵon %s"
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr "La PIM devas esti aŭ numero aŭ malplena."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:465
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Ne povas ruli aplikaĵon %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:467
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Ne povis trovi aplikaĵon %s"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Atingeblo"
+
+#: js/ui/status/accessibility.js:48
+msgid "Zoom"
+msgstr "Zomi"
+
+#: js/ui/status/accessibility.js:55
+msgid "Screen Reader"
+msgstr "Ekrana legilo"
+
+#: js/ui/status/accessibility.js:59
+msgid "Screen Keyboard"
+msgstr "Ekrana klavaro"
+
+#: js/ui/status/accessibility.js:63
+msgid "Visual Alerts"
+msgstr "Vidaj avertoj"
+
+#: js/ui/status/accessibility.js:66
+msgid "Sticky Keys"
+msgstr "Fiksaj klavoj"
+
+#: js/ui/status/accessibility.js:69
+msgid "Slow Keys"
+msgstr "Malrapidaj klavoj"
+
+#: js/ui/status/accessibility.js:72
+msgid "Bounce Keys"
+msgstr "Saltaj klavoj"
+
+#: js/ui/status/accessibility.js:75
+msgid "Mouse Keys"
+msgstr "Musklavoj"
+
+#: js/ui/status/accessibility.js:134
+msgid "High Contrast"
+msgstr "Alta kontrasto"
+
+#: js/ui/status/accessibility.js:176
+msgid "Large Text"
+msgstr "Granda teksto"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "Bludento"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:625
+msgid "Bluetooth Settings"
+msgstr "Bludento-agordoj"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:148
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d Konektita"
+msgstr[1] "%d Konektitaj"
+
+#: js/ui/status/bluetooth.js:152
+msgid "Bluetooth Off"
+msgstr "Bludento malŝaltita"
+
+#: js/ui/status/bluetooth.js:154
+msgid "Bluetooth On"
+msgstr "Bludento ŝaltita"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Brileco"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Unuklako"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Duklako"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Tiro"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Duaranga klako"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Daŭra klako"
+
+#: js/ui/status/keyboard.js:829
+msgid "Keyboard"
+msgstr "Klavaro"
+
+#: js/ui/status/keyboard.js:846
+msgid "Show Keyboard Layout"
+msgstr "Montri klavararanĝon"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:174
+msgid "Location Enabled"
+msgstr "Loko ŝaltita"
+
+#: js/ui/status/location.js:66 js/ui/status/location.js:175
+msgid "Disable"
+msgstr "Malŝalti"
+
+#: js/ui/status/location.js:67
+msgid "Privacy Settings"
+msgstr "Privatecaj agordoj"
+
+#: js/ui/status/location.js:173
+msgid "Location In Use"
+msgstr "Loko uzata"
+
+#: js/ui/status/location.js:177
+msgid "Location Disabled"
+msgstr "Loko malŝaltita"
+
+#: js/ui/status/location.js:178
+msgid "Enable"
+msgstr "Ŝalti"
+
+#: js/ui/status/location.js:350
+msgid "Allow location access"
+msgstr "Permesi atingon al via loko"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:352
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "La aplikaĵo %s volas atingon al via loko"
+
+#: js/ui/status/location.js:362
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Vi povas ŝangi atingon al via loko iam ajn en la privateco-agordoj."
+
+#: js/ui/status/network.js:72
+msgid "<unknown>"
+msgstr "<nekonata>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:455 js/ui/status/network.js:1350
+#, javascript-format
+msgid "%s Off"
+msgstr "%s malŝaltita"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:458
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s konektita"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:463
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s nemastrumita"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:466
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s malkonektante"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:473 js/ui/status/network.js:1342
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s konektante"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:476
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s postulas aŭtentigon"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:484
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Mikroprogramo mankas por %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:488
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s nedisponebla"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:491
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s konekto malsukcesis"
+
+#: js/ui/status/network.js:503
+msgid "Wired Settings"
+msgstr "Drataj agordoj"
+
+#: js/ui/status/network.js:546
+msgid "Mobile Broadband Settings"
+msgstr "Porteblaj larĝkapacitaj agordoj"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:592 js/ui/status/network.js:1347
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s aparataro malŝaltita"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:596
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s malŝaltita"
+
+#: js/ui/status/network.js:637
+msgid "Connect to Internet"
+msgstr "Konekti al interreto"
+
+#: js/ui/status/network.js:841
+msgid "Airplane Mode is On"
+msgstr "Aviadila reĝimo ŝaltita"
+
+#: js/ui/status/network.js:842
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Vifio malebliĝas se aviadila reĝimo estas ŝaltita."
+
+#: js/ui/status/network.js:843
+msgid "Turn Off Airplane Mode"
+msgstr "Malŝalti aviadilan reĝimon"
+
+#: js/ui/status/network.js:852
+msgid "Wi-Fi is Off"
+msgstr "Vifio malŝaltita"
+
+#: js/ui/status/network.js:853
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Necesas ŝalti vifion por konekti al reto."
+
+#: js/ui/status/network.js:854
+msgid "Turn On Wi-Fi"
+msgstr "Ŝalti vifion"
+
+#: js/ui/status/network.js:879
+msgid "Wi-Fi Networks"
+msgstr "Vifiaj retoj"
+
+#: js/ui/status/network.js:881
+msgid "Select a network"
+msgstr "Elekti reton"
+
+#: js/ui/status/network.js:913
+msgid "No Networks"
+msgstr "Neniu reto"
+
+#: js/ui/status/network.js:934 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "Uzi aparataran ŝaltilon por malŝalti"
+
+#: js/ui/status/network.js:1211
+msgid "Select Network"
+msgstr "Elekti reton"
+
+#: js/ui/status/network.js:1217
+msgid "Wi-Fi Settings"
+msgstr "Vifio-agordoj"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1338
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s retkaptejo aktiva"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1353
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s ne konektita"
+
+#: js/ui/status/network.js:1450
+msgid "connecting…"
+msgstr "konektante…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1453
+msgid "authentication required"
+msgstr "aŭtentigo necesas"
+
+#: js/ui/status/network.js:1455
+msgid "connection failed"
+msgstr "konekto malsukcesis"
+
+#: js/ui/status/network.js:1506
+msgid "VPN Settings"
+msgstr "VPR-agordoj"
+
+#: js/ui/status/network.js:1523
+msgid "VPN"
+msgstr "VPR"
+
+#: js/ui/status/network.js:1533
+msgid "VPN Off"
+msgstr "VPR malŝaltita"
+
+#: js/ui/status/network.js:1594 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Ret-agordoj"
+
+#: js/ui/status/network.js:1622
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s drata konekto"
+msgstr[1] "%s drataj konektoj"
+
+#: js/ui/status/network.js:1626
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s vifia konekto"
+msgstr[1] "%s vifiaj konektoj"
+
+#: js/ui/status/network.js:1630
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s modema konekto"
+msgstr[1] "%s modemaj konektoj"
+
+#: js/ui/status/network.js:1764
+msgid "Connection failed"
+msgstr "Konekto malsukcesis"
+
+#: js/ui/status/network.js:1765
+msgid "Activation of network connection failed"
+msgstr "Aktivigo de reto-konekto malsukcesis"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Noktlumo malŝaltita"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Noktlumo ŝaltita"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Daŭrigi"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Malŝalti ĝis morgaŭ"
+
+#: js/ui/status/power.js:47
+msgid "Power Settings"
+msgstr "Elektro-agordoj"
+
+#: js/ui/status/power.js:63
+msgid "Fully Charged"
+msgstr "Plene ŝargita"
+
+#: js/ui/status/power.js:69
+msgid "Not Charging"
+msgstr "Ne ŝargante"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:72 js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "Taksante…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "Restas %d:%02d (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d:%02d ĝis plena (%d %%)"
+
+#: js/ui/status/power.js:139 js/ui/status/power.js:141
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:38
+msgid "Screen is Being Shared"
+msgstr "Ekrano estas kunhavigita"
+
+#: js/ui/status/remoteAccess.js:40
+msgid "Turn off"
+msgstr "Malŝalti"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Aviadila reĝimo ŝaltita"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "Ŝlosi"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "Malŝalti / Adiaŭi"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "Dormeti"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "Restartigi…"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "Malŝalti…"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Adiaŭi"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "Ŝanĝi uzanton…"
+
+#: js/ui/status/thunderbolt.js:262
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Nekonata Thunderbolt aparato"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Nova aparato detektiĝis kiam vi estis for. Bonvolu malkonekti kaj rekonekti "
+"vian aparaton por uzi ĝin."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr "Ne-aŭtentigita Thunderbolt aparato"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Detektis novan aparaton kiu bezonas aŭtentigon de administranto."
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt aŭtentiga eraro"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Ne povis aŭtentigi la Thunderbolt aparaton: %s"
+
+#: js/ui/status/volume.js:160
+msgid "Volume changed"
+msgstr "Laŭteco ŝanĝita"
+
+#: js/ui/status/volume.js:222
+msgid "Volume"
+msgstr "Laŭteco"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "Speguli"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "Kunigi ekranojn"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "Nur ekstera"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "Nur integrita"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:371
+msgid "%A %B %-d"
+msgstr "%A, %-d-a de %B"
+
+#: js/ui/unlockDialog.js:377
+msgid "Swipe up to unlock"
+msgstr "Movi supren por malŝlosi"
+
+#: js/ui/unlockDialog.js:378
+msgid "Click or press a key to unlock"
+msgstr "Klaku aŭ premi klavon por malŝlosi"
+
+#: js/ui/unlockDialog.js:556
+msgid "Unlock Window"
+msgstr "Malŝlosi fenestron"
+
+#: js/ui/unlockDialog.js:565
+msgid "Log in as another user"
+msgstr "Saluti kiel alia uzanto"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Bonvenon al GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Se vi volas kutimiĝi, prenu la rondvojaĝon."
+
+#: js/ui/welcomeDialog.js:45
+msgid "No Thanks"
+msgstr "Ne, dankon"
+
+#: js/ui/welcomeDialog.js:50
+msgid "Take Tour"
+msgstr "Prenu la rondvojaĝon"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” pretas"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:63
+msgid "Keep these display settings?"
+msgstr "Ĉu konservi ĉi tiujn ekranajn agordojn?"
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Malfari ŝanĝojn"
+
+#: js/ui/windowManager.js:75
+msgid "Keep Changes"
+msgstr "Konservi ŝanĝojn"
+
+#: js/ui/windowManager.js:94
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Agordaj ŝanĝoj malfaritos post %d sekundo"
+msgstr[1] "Agordaj ŝanĝoj malfaritos post %d sekundoj"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:550
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr "Minimumigi"
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr "Malmaksimumigi"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "Maksimumigi"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Movi"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "Regrandigi"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr "Movi titolbreton surekranen"
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "Ĉiam supre"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr "Ĉiam je videbla laborspaco"
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr "Movi al maldekstra laborspaco"
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr "Movi al dekstra laborspaco"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "Movi al supra laborspaco"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "Movi al suba laborspaco"
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr "Movi al supra ekrano"
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr "Movi al suba ekrano"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "Movi al maldekstra ekrano"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "Movi al dekstra ekrano"
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Fermi"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolucio-kalendaro"
+
+#: src/main.c:415 subprojects/extensions-tool/src/main.c:317
+msgid "Print version"
+msgstr "Presi version"
+
+#: src/main.c:421
+msgid "Mode used by GDM for login screen"
+msgstr "Reĝimo uzata de GDM por la salut-ekrano"
+
+#: src/main.c:427
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Uzi specifan reĝimon, ekz. “gdm” por la salut-ekrano"
+
+#: src/main.c:433
+msgid "List possible modes"
+msgstr "Listigi eblajn reĝimojn"
+
+#: src/shell-app.c:298
+msgctxt "program"
+msgid "Unknown"
+msgstr "Nekonata"
+
+#: src/shell-app.c:549
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Malsukcesis lanĉi “%s”"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "Pasvortoj ne kongruas."
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "Pasvorto ne povas esti malplena"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "La uzanto malakceptis la aŭtentigan dialogon"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:217
+#: subprojects/extensions-app/data/ui/extensions-window.ui:56
+msgid "Extensions"
+msgstr "Etendaĵoj"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:218
+msgid "Manage your GNOME Extensions"
+msgstr "Agordi viajn GNOME etendaĵojn"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "La GNOME-projekto"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME-etendaĵoj administras ĝisdatigon de etendaĵoj, adoptadon de etendaĵaj "
+"agordoj kaj forigo aŭ malŝaltigo de nebezonataj etendaĵoj."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Agordi GNOME-ŝelajn kromprogramojn"
+
+#: subprojects/extensions-app/js/main.js:142
+#: subprojects/extensions-app/js/main.js:152
+msgid "No Matches"
+msgstr "Neniu kongruo"
+
+#: subprojects/extensions-app/js/main.js:179
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Ĉu forigi “%s“?"
+
+#: subprojects/extensions-app/js/main.js:180
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Se vi forigas la etendaĵon, nepras ke vi reelŝutu ĝin se vi ŝatus reŝalti ĝin"
+
+#: subprojects/extensions-app/js/main.js:184
+msgid "Remove"
+msgstr "Forigi"
+
+#: subprojects/extensions-app/js/main.js:216
+msgid "translator-credits"
+msgstr ""
+"Carmen Bianca BAKKER <carmen@carmenbianca.eu>\n"
+"Kristjan SCHMIDT"
+
+#: subprojects/extensions-app/js/main.js:344
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d etendaĵo ĝisdatiĝos je via sekva saluto."
+msgstr[1] "%d etendaĵoj ĝisdatiĝos je via sekva saluto."
+
+#: subprojects/extensions-app/js/main.js:494
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "La etandaĵo ne kongruas je la aktuala GNOME-versio"
+
+#: subprojects/extensions-app/js/main.js:497
+msgid "The extension had an error"
+msgstr "La etendaĵo eraris"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:83
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Priskribo"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:104
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versio"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:131
+msgid "Author"
+msgstr "Aŭtoro"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:185
+msgid "Website"
+msgstr "Retejo"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:201
+msgid "Remove…"
+msgstr "Forigi…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:7
+msgid "Help"
+msgstr "Helpo"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:11
+msgid "About Extensions"
+msgstr "Pri etendaĵoj"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Por trovi kaj aldoni etendaĵojn, vizitu <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:34
+msgid "Warning"
+msgstr "Averto"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:44
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr ""
+"Etendaĵoj povas kaŭzi sistemproblemojn, inkluzive rendimentajn problemojn. "
+"Se via sistemo spertas problemojn, ni rekomendas ke vi malŝaltu ĉiujn "
+"etendaĵojn."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:150
+msgid "Manually Installed"
+msgstr "Instalita permane"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:174
+msgid "Built-In"
+msgstr "Integrita"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:217
+msgid "No Installed Extensions"
+msgstr "Neniu instalita etendaĵo"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:252
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Ni pardonpetas. Ŝajnas ke ne eblas akiri liston de instalitaj etendaĵoj. "
+"Certigu ke vi salutis al GNOME kaj reprovu."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:288
+msgid "Extension Updates Ready"
+msgstr "Ĝisdatigoj de etendaĵoj disponeblas"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:303
+msgid "Log Out…"
+msgstr "Adiaŭi…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:223
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "La nova etandaĵo estis sukcese kreita en %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Nomo devus esti tre mallonga ĉeno.\n"
+"Ekzemple: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nomo"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Priskribo estas unuopa frazo kiu klarigas la utilon de via etendaĵo.\n"
+"Ekzemple: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID estas malloke unika identigilo por via etendaĵo.\n"
+"Tiu devus esti retpoŝtadreso (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Elektu unu de la disponeblaj ŝablonoj:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Ŝablono"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "La unika identigilo de la nova etendaĵo"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOMO"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "La videbla nomo de la nova etendaĵo"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "PRISKRIBO"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Mallonga priskribo de la utilo de la etendaĵo"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ŜABLONO"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "La uzota ŝablono por la nova etendaĵo"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Enigu etendaĵajn informojn interage"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Krei novan etendaĵon"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Nekonataj argumentoj"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, nomo, kaj priskribo necesas"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Malsukcesis konekti kun GNOME-ŝelo\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "La etendaĵo “%s” ne ekzistas\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Malŝalti etendaĵon"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:97
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Ricevis neniun UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:102
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Ricevis pli ol unu UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Ŝalti etendaĵon"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "La etendaĵo “%s” ne ekzistas\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Montri informojn de etendaĵoj"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Superskribi ekzistantan etendaĵon"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "ETENDAĴA_PAKAĴO"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instali entendaĵan pakaĵon"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Neniu etendaĵa pakaĵo estas specifita"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Pli ol unu etendaĵa pakaĵo estas specifita"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Montri etendaĵojn de la uzanto"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Montri etendaĵojn de la sistemo"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Montri ŝaltitajn etendaĵojn"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Montri malŝaltitajn etendaĵojn"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Montri etendaĵojn kun agordoj"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Montri etendaĵojn kun ĝisdatigoj"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Presi detalojn de etendaĵo"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Listigi instalitajn etendaĵojn"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "DOSIERO"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Aldona fonto por aldoni al la pakaĵo"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SKEMO"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "GSettings-skemo kiun oni devus inkluzivi"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DOSIERUJO"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "La dosierujo kiu enhavas tradukojn"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAJNO"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "La uzenda gettext-domajno"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Superskribi ekzistantan pakaĵon"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "La dosierujo kie la pakajoj estu kreita"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "FONT_DOSIERUJO"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Krei entendaĵan pakaĵon"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Pli ol unu fontdosierujo specifita"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "La etendaĵo “%s” ne havas agordojn\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:79
+msgid "Opens extension preferences"
+msgstr "Malfermi agordojn de la etendaĵo"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Reagordi etendaĵon"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Ne eblas malinstali sistemajn etendaĵojn\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Malsukcesis malinstali na “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Malinstali etendaĵon"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ne eldoni erar-mesaĝojn"
+
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell"
+msgstr "Malsukcesis konekti kun GNOME-ŝelo"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Vojo"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Origina aŭtoro"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stato"
+
+#: subprojects/extensions-tool/src/main.c:290
+msgid "“version” takes no arguments"
+msgstr "“version” anticipas neniun argumenton"
+
+#: subprojects/extensions-tool/src/main.c:292
+#: subprojects/extensions-tool/src/main.c:312
+msgid "Usage:"
+msgstr "Uzo:"
+
+#: subprojects/extensions-tool/src/main.c:295
+msgid "Print version information and exit."
+msgstr "Presi versian informon kaj ĉesi."
+
+#: subprojects/extensions-tool/src/main.c:310
+#: subprojects/extensions-tool/src/main.c:313
+msgid "COMMAND"
+msgstr "KOMANDO"
+
+#: subprojects/extensions-tool/src/main.c:313
+msgid "[ARGS…]"
+msgstr "[ARG-oj…]"
+
+#: subprojects/extensions-tool/src/main.c:315
+msgid "Commands:"
+msgstr "Komandoj:"
+
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Print help"
+msgstr "Presi helpon"
+
+#: subprojects/extensions-tool/src/main.c:318
+msgid "Enable extension"
+msgstr "Ŝalti etendaĵon"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Disable extension"
+msgstr "Malŝalti etendaĵon"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Reset extension"
+msgstr "Reagordi etendaĵon"
+
+#: subprojects/extensions-tool/src/main.c:321
+msgid "Uninstall extension"
+msgstr "Malinstali etendaĵon"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "List extensions"
+msgstr "Listigi etendaĵojn"
+
+#: subprojects/extensions-tool/src/main.c:323
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Show extension info"
+msgstr "Montri informojn de etendaĵo"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Open extension preferences"
+msgstr "Malfermi agordojn de etendaĵo"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "Create extension"
+msgstr "Krei etendaĵo"
+
+#: subprojects/extensions-tool/src/main.c:327
+msgid "Package extension"
+msgstr "Pakaĵa etendaĵo"
+
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Install extension bundle"
+msgstr "Instali entendaĵan pakaĵon"
+
+#: subprojects/extensions-tool/src/main.c:330
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Uzu “%s” por detala helpo.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Simpla"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Malplena etendaĵo"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikilo"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Aldoni piktogramon al la supra breto"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u eligo"
+msgstr[1] "%u eligoj"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u enigo"
+msgstr[1] "%u enigoj"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sistemsonoj"
+
+#~ msgid "App Picker View"
+#~ msgstr "Aplikaĵa elektila vido"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Indekso de aktuala elektita vido ĉe la aplikaĵa elektilo."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Ofte uzataj aplikaĵoj aperos ĉi tie"
+
+# Oftaj aplikaĵoj --Carmen
+#~ msgid "Frequent"
+#~ msgstr "Oftaj"
+
+#~ msgid "All"
+#~ msgstr "Ĉiuj"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d-a de %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d-a de %B %Y"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopii eraron"
+
+#~ msgid "Off"
+#~ msgstr "Malŝaltita"
+
+#~ msgid "On"
+#~ msgstr "Ŝaltita"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Foliumi en Programaro"
+
+#~ msgid "Next"
+#~ msgstr "Sekva"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Saluti"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Ŝlosa orientiĝo"
+
+#~ msgid "Rename"
+#~ msgstr "Alinomi"
+
+#~ msgid "Password:"
+#~ msgstr "Pasvorto:"
+
+#~ msgid "Type again:"
+#~ msgstr "Tajpu denove:"
+
+#~ msgid "Password: "
+#~ msgstr "Pasvorto: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Sendrata reto bezonas aŭtentigon"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Portebla larĝkapacita retopasvorto"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %-d-a de %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nova mesaĝo"
+#~ msgstr[1] "%d novaj mesaĝoj"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d nova sciigo"
+#~ msgstr[1] "%d novaj sciigoj"
+
+#~ msgid "Account Settings"
+#~ msgstr "Kontoagordoj"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Orientiĝa ŝloso"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Klavkombino, kiu paŭzigas kaj malpaŭzigas ĉiujn rulantajn “tween”-ojn, "
+#~ "por sencimigaj aferoj"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Uzenda klavaro"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "La uzenda klavarspeco."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "evolution"
+#~ msgstr "evolution"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Eraro okazis dum ŝarĝado de la agorda dialogo de %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s tuttage."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, tiam %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, tiam %s, sekve %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Sentas kiel %s."
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Montri la semajnajntagon en la kalendaro"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Montri la semajndaton laŭ ISO en la kalendaro."
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Reta aŭtentokontrola alidirekto"
+
+#~ msgid "Events"
+#~ msgstr "Eventoj"
+
+#~ msgid "Notifications"
+#~ msgstr "Sciigoj"
+
+#~ msgid "Hide tray"
+#~ msgstr "Kaŝi pleton"
+
+#~ msgid "Status Icons"
+#~ msgstr "Stato-piktogramoj"
+
+#~ msgid "Clear section"
+#~ msgstr "Forviŝi sekcion"
+
+#~ msgid "Not In Use"
+#~ msgstr "Ne uzata"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Uzi kiel interreta konekto"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME-ŝelo (wayland kunmetilo)"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Montri la mesaĝo-pleton"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "di"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "lu"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "ma"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "me"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "ĵa"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "ve"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "sa"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Nenio estas planita"
+
+#~ msgid "This week"
+#~ msgstr "Ĉi tiu semajno"
+
+#~ msgid "Next week"
+#~ msgstr "Sekva semajno"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Demeteblaj aparatoj"
+
+#~ msgid "Eject"
+#~ msgstr "Elĵeti"
+
+#~ msgid "Invitation"
+#~ msgstr "Invito"
+
+#~ msgid "Call"
+#~ msgstr "Voki"
+
+#~ msgid "File Transfer"
+#~ msgstr "Dosiertransigo"
+
+#~ msgid "Chat"
+#~ msgstr "Babili"
+
+#~ msgid "Unmute"
+#~ msgstr "Malsilentigi"
+
+#~ msgid "Mute"
+#~ msgstr "Silentigi"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Invito al %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s invitas vin aliĝi al %s"
+
+#~ msgid "Decline"
+#~ msgstr "Refuzi"
+
+#~ msgid "Accept"
+#~ msgstr "Akcepti"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Videa voko de %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Voko de %s"
+
+#~ msgid "Answer"
+#~ msgstr "Repondo"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s sendas %s al vi"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s volus permiso por vidi kiam vi estas konektita"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Aŭtentigo malsukcesis"
+
+#~ msgid "Encryption error"
+#~ msgstr "Ĉifrado-eraro"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Atestilo ne donita"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Malfidita atestilo"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Posttempa atestilo"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Atestilo ne aktivigita"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Atestilo-nomo de gastiga komputilo miskongruas"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Atestilo-fingropremo miskongruas"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Memsubskribita atestilo"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Stato agordas kiam nekonektita."
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Nevalida atestilo"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Refuzinta konekto"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Ne povas establi konekton"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Konekto perdiĝis"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Ĉi tio konto estas jam konektata al servilo"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Konekto anstataŭigis per nova konekto uzanta la saman risurcon"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "La konto jam ekzistas sur la servilo"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Servilo estas nun tro okupata por akcepti la konekton"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Atestilo senvalidigita"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Atestilo uzas malsekuran ĉifrigan algoritmon aŭ estas kriptografie "
+#~ "malforta."
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "La longeco de la servilo-atestilo, aŭ la profundeco de la servilo-"
+#~ "atestilo ĉeno, transpasita la maksimumo altrudis per la biblioteko de "
+#~ "kriptografia."
+
+#~ msgid "Internal error"
+#~ msgstr "Interna eraro"
+
+#~ msgid "View account"
+#~ msgstr "Vidi konton"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Malfermi kalendaron"
+
+#~ msgid "Open"
+#~ msgstr "Malfermi"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Vakigi mesaĝojn"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Sciigaj agordoj"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Pleto-menuo"
+
+#~ msgid "No Messages"
+#~ msgstr "Neniuj mesaĝoj"
+
+#~ msgid "Message Tray"
+#~ msgstr "Mesaĝa pleto"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d konektita aparato"
+#~ msgstr[1] "%d konektitaj aparatoj"
+
+#~ msgid "UPS"
+#~ msgstr "vicnutrilo"
+
+#~ msgid "Battery"
+#~ msgstr "Baterio"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Aviadila reĝimo"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Listo de kategorioj kiu devus montriĝi kiel dosierujoj"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Ĉiu kategoria nomo en ĉi listo prezentitos kiel dosierujoj en la aplikaĵa "
+#~ "vido anstantaŭ montriĝi entekste en la ĉefa vido."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Aranĝo de butonoj sur la titola breto"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Ĉi ŝlosilo atutas la ŝlosilon en 'org.gnome.desktop.wm.preferences' kiam "
+#~ "uzanta GNOME-ŝelon."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Elekti kromprogramon por agordi uzanta la redaktebla falmenuo supre."
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Rajtigpeto de %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Aparato %s volas pariĝi kun tiu ĉi komputilo"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Aparato '%s' volas aliron al servo '%s'"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Konsenti nur ĉi tiun fojon"
+
+#~ msgid "Reject"
+#~ msgstr "Rifuzi"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Pariĝ-konfirmo por %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Bonvole konfirmu ĉu la PIN-kodo '%06d' kongruas kun tiu sur la aparato."
+
+#~ msgid "Does not match"
+#~ msgstr "Ne kongruas"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Pariĝ-peto por '%s'"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Bonvole enigu la PIN-kodon montranta de la aparato."
+
+#~ msgid "OK"
+#~ msgstr "Akcepti"
+
+#~ msgid "unavailable"
+#~ msgstr "nedisponebla"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Pardonu, neniu saĝo por vi hodiaŭ:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s la Orakolo diras"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Record a screencast"
+#~ msgstr "Registri ekrankopion"
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Bildrapido uzata por ekranregistraĵoj."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "La ĉenstablo de Gstreamer uzata por kodi la ekranregistraĵon"
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "La dosiersufikso uzata por konservi la ekranregistraĵon"
+
+#~ msgid "Power"
+#~ msgstr "kurento"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "Ekranregistraĵo de %d %t"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Alklaku \"Elsaluti\" por ĉesi tiujn aplikaĵojn kaj elsaluti de la sistemo."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Elsalutanta de la sistemo."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Alklaku \"Elŝalti\" por ĉesi tiujn aplikaĵojn kaj elŝalti la sistemon."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Elŝaltanta la sistemon."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Alklaku \"Restartigi\" por ĉesi tiujn aplikaĵojn kaj restartigi la "
+#~ "sistemon."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Restartiganta la sistemon."
+
+#~ msgid "disabled"
+#~ msgstr "elŝaltita"
+
+#~ msgid "Connected (private)"
+#~ msgstr "Konektita (private)"
+
+#~ msgid "Wired"
+#~ msgstr "Drata"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Ĉu kolekti statistikojn pri uzo de aplikaĵoj?"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "La ŝelo kutime kontrolas la ruliĝajn aplikaĵojn por montri la plej "
+#~ "uzitajn aplikaĵojn (ekz. en lanĉiloj). Kvankam ĉi tiu datumoj estos "
+#~ "tenata privata, vi eble volas elŝalti ĉi tion pro privateca kialo. "
+#~ "Bonvole atentu ke faranta ĉi tion ne forigos jam konservitajn datumojn."
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "elŝaltitaj OpenSearch-provizantoj"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Montri horon kun sekundoj"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Se vera, montri sekundojn en horo."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Montri daton en horloĝo"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Se vera, montri daton en la horloĝo, krom la horo."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Ensaluti"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APLIKAĴOJ"
+
+#~ msgid "SETTINGS"
+#~ msgstr "AGORDOJ"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
diff --git a/po/es.po b/po/es.po
new file mode 100644
index 0000000..c14f72d
--- /dev/null
+++ b/po/es.po
@@ -0,0 +1,4478 @@
+# Spanish translation of gnome-shell.
+# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Jorge González <jorgegonz@svn.gnome.org>, 2009, 2010, 2011.
+# Benjamín Valero Espinosa <benjavalero@gmail.com>, 2011.
+# Daniel Mustieles <daniel.mustieles@gmail.com>, 2010-2022.
+# Daniel Mustieles García <daniel.mustieles@gmail.com>, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell.master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-14 22:12+0000\n"
+"PO-Revision-Date: 2022-09-16 10:38+0200\n"
+"Last-Translator: Daniel Mustieles García <daniel.mustieles@gmail.com>\n"
+"Language-Team: Spanish - Spain <gnome-es-list@gnome.org>\n"
+"Language: es_ES\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"X-Generator: Gtranslator 42.0\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Lanzadores"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Activar la aplicación favorita 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Activar la aplicación favorita 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Activar la aplicación favorita 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Activar la aplicación favorita 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Activar la aplicación favorita 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Activar la aplicación favorita 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Activar la aplicación favorita 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Activar la aplicación favorita 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Activar la aplicación favorita 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Capturas de pantalla"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Hacer una captura de pantalla de manera interactiva"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Hacer una captura de pantalla"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Capturar una ventana"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Grabar la pantalla de manera interactiva"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostrar la lista de notificaciones"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Dar el foco a la notificación activa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostrar la vista general"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostrar todas las aplicaciones"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Abrir el menú de la aplicación"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gestión de ventanas e inicio de aplicaciones"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Activar las herramientas internas, útiles para desarrolladores y probadores, "
+"desde Alt+F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permitir acceder a las herramientas internas de depuración monitorización "
+"usando el diálogo Alt+F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID de las extensiones que activar"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Las extensiones de GNOME Shell tienen una propiedad UUID; esta clave lista "
+"las extensiones que se deben cargar. Cualquier extensión que se quiere "
+"cargar debe estar en la lista. También puede manipular esta lista con los "
+"métodos de DBus «EnableExtension» y «DisableExtension» en org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID de las extensiones que desactivar forzosamente"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Las extensiones de GNOME Shell tienen una propiedad UUID; esta clave lista "
+"las extensiones que se deben desactivar incluso si están cargadas como parte "
+"del modo actual. También puede manipular esta lista con los métodos de DBus "
+"«EnableExtension» y «DisableExtension» en org.gnome.Shell. Esta clave "
+"prevalece sobre la opción “enabled-extensions”. "
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Desactivar extensiones de usuario"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Desactivar todas las extensiones que ha activado el usuario sin afectar a la "
+"configuración «enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+"Desactiva la validación de la compatibilidad de la versión de la extensión"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell sólo cargará las extensiones que indiquen que soportan la "
+"versión actual en ejecución. Activar esta opción desactivará esta "
+"comprobación e intentará cargar todas las extensiones, independientemente de "
+"las versiones que indiquen que soportan."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lista de ID de archivos de escritorio para las aplicaciones favoritas"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Las aplicaciones correspondientes con esos identificadores se mostrarán en "
+"el área de favoritos."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Histórico del diálogo de comandos (Alt+F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Histórico del diálogo de «looking glass»"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+"Mostrar siempre el elemento de menú «Cerrar sesión» en el menú del usuario."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Esta clave sobrescribe la ocultación automática del elemento de menú «Cerrar "
+"sesión» en situaciones de un único usuario o de una única sesión."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Indica si se debe recordar la contraseña para montar sistemas de archivos "
+"remotos o cifrados"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"La shell solicitará una contraseña cuando se monte un dispositivo cifrado o "
+"un sistema de archivos remoto. Si la contraseña se puede guardar para usarla "
+"en un futuro, se mostrará la casilla «Recordar contraseña». Esta clave "
+"establece el valor predeterminado de la casilla."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Indica si el adaptador Bluetooth predeterminado tiene dispositivos asociados "
+"configurados"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"La shell sólo mostrará un menú de Bluetooth si hay conectado un adaptador de "
+"Bluetooth, o si hay dispositivos asociados configurados con el adaptador "
+"predeterminado. Esto se restablecerá si se ve que el adaptador "
+"predeterminado no tiene dispositivos asociados."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "El último perfil de energía no predeterminado seleccionado"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Algunos sistemas soportan más de dos perfiles de energía. Para seguir "
+"soportando cambiar entre dos perfiles esta clave guarda el último perfil no "
+"predeterminado seleccionado."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"La última versión para la que se mostró el diálogo «Bienvenido a GNOME»"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Esta clave determina para qué versión se mostró por última vez el diálogo "
+"«Bienvenido a GNOME». Una cadena vacía representa la versión más antigua "
+"posible y un número muy grande representará una versión que todavía no "
+"existe. Este número muy grande se puede usar para desactivar el diálogo."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Distribución del selector de aplicaciones"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Distribución del selector de aplicaciones Cada entrada en el vector es una "
+"página. Las páginas se guardan en el orden en que aparecen en GNOME Shell. "
+"Cada página contiene una pareja “application id” → 'data'. Actualmente se "
+"guardan los siguientes valores como 'data': • “position”:la posición del "
+"icono de la aplicación en la página"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Asociación de teclas para abrir el menú de la aplicación"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Asociación de teclas para abrir el menú de la aplicación."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Asociación de teclas para cambiar entre estados de la vista general"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Asociación de teclas para cambiar entre sesión, selector de ventanas y "
+"cuadrícula de aplicaciones"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Asociación de teclas para cambiar entre cuadrícula de aplicaciones, selector "
+"de ventanas y sesión"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Asociación de teclas para la vista «Mostrar aplicaciones»"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Asociación de teclas para abrir la vista «Mostrar aplicaciones» de la vista "
+"de actividades."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Asociación de teclas para la vista general"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Asociación de teclas para abrir la Vista de actividades."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Asociación de teclas para cambiar la visibilidad de la lista de "
+"notificaciones"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Asociación de teclas para cambiar la visibilidad de la lista de "
+"notificaciones."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Asociación de teclas para dar el foco a la notificación activa"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Asociación de teclas para dar el foco a la notificación activa."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Cambiar a la aplicación 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Cambiar a la aplicación 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Cambiar a la aplicación 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Cambiar a la aplicación 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Cambiar a la aplicación 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Cambiar a la aplicación 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Cambiar a la aplicación 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Cambiar a la aplicación 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Cambiar a la aplicación 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Limitar el intercambiador al área de trabajo actual."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Si es cierto, sólo las aplicaciones que tengan ventanas en el área de "
+"trabajo actual se muestran en el selector. Si no, se incluyen todas las "
+"aplicaciones."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "El modo de icono de la aplicación."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configura cómo se muestran las ventanas en el selector. Los valore posibles "
+"son «thumbnail-only» (muestra una miniatura de la ventana), «app-icon-"
+"only» (sólo muestra el icono de la aplicación) «both»."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Si es cierto, sólo se muestran en el selector las ventanas del área de "
+"trabajo actual. Si no, se incluyen todas las ventanas."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Ubicaciones"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Las ubicaciones que mostrar en los relojes del mundo"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Ubicación automática"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Indica si se debe o no obtener la ubicación actual"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Ubicación"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "La ubicación para la que mostrar la predicción"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Acoplar un diálogo modal a la ventana padre"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Esta clave sobrescribe la clave en org.gnome.mutter al ejecutar GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Activar el mosaico en los bordes al arrastrar ventanas a los bordes de la "
+"ventana"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Las áreas de trabajo se gestionan dinámicamente"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Áreas de trabajo solo en la pantalla principal"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Retardo al cambiar el foco del ratón hasta que el puntero deja de moverse"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Inicio de sesión de la red"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Algo ha fallado"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Ha habido un problema: no se puede mostrar la configuración para esta "
+"extensión. Se recomienda que informe del error a los autores de la extensión."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detalles técnicos"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Página web"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visitar la página web de la extensión"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Contraseña"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Elegir sesión"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "¿No está en la lista?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ej., usuario o %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nombre de usuario"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Ventana de inicio de sesión"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Error de autenticación"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(o pase el dedo por el lector)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(o coloque el dedo en el lector)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Apagar"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "apagar;apagado;reinicio;reiniciar;detener;parar"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reiniciar;rebotar;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bloquear la pantalla"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "bloquear;pantalla"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Cerrar la sesión"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "cerrar;sesión;salir"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspender"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspender;dormir"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Cambiar de usuario"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "cambiar;usuario"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "bloquear orientación;desbloquear orientación;pantalla;rotación"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Hacer una captura de pantalla"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "captura;pantalla;grabación;recorte;grabación"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Desbloquear la rotación de la pantalla"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Bloquear la rotación de la pantalla"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Comando no encontrado"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "No se pudo analizar el comando:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Falló la ejecución de «%s»:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Ahora mismo"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Hace %d minuto"
+msgstr[1] "Hace %d minutos"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Hace %d hora"
+msgstr[1] "Hace %d horas"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Ayer"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Hace %d día"
+msgstr[1] "Hace %d días"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Hace %d semana"
+msgstr[1] "Hace %d semanas"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Hace %d mes"
+msgstr[1] "Hace %d meses"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Hace %d año"
+msgstr[1] "Hace %d años"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ayer, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d de %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d de %B de %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ayer a las %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d de %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d de %B de %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Inicio de sesión en el punto de acceso"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Su conexión a este punto de acceso no es segura. Las contraseñas y otra "
+"información que introduzca en esta página la podrían ver personas cercanas."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Denegar acceso"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Conceder acceso"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Carpeta sin nombre"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s se ha fijado al tablero."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Se ha quitado %s del tablero."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Ventanas abiertas"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Ventana nueva"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Mostrar detalles"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Salir"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Quitar"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Fijar al tablero"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Lanzar usando la tarjeta gráfica integrada"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Lanzar usando la tarjeta gráfica discreta"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Seleccionar dispositivo de sonido"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Configuración de sonido"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Auriculares"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Manos libres"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Micrófono"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Cambiar el fondo…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Configuración de pantalla"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Configuración"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "X"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB de %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Mes anterior"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Siguiente mes"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Semana %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "No hay notificaciones"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "No molestar"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Limpiar"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» no responde."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Puede elegir esperar un momento para que continúe o forzar a la aplicación a "
+"terminar."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Forzar la salida"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Esperar"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Dispositivo externo conectado"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Dispositivo externo desconectado"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "No se puede desbloquear el volumen"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "La versión de udisks instalada no soporta la configuración PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Abrir con %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternativamente puede conectarse pulsando el botón «WPS» de su router."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Conectar"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Clave"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Contraseña de la clave privada"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identidad"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Servicio"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autenticación requerida"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Se necesitan contraseñas o claves de cifrado para acceder a la red "
+"inalámbrica «%s»."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autenticación 802.1X cableada"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nombre de la red"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autenticación DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Código PIN requerido"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Se necesita un código PIN para el dispositivo de banda ancha móvil"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Se requiere una contraseña para conectarse a «%s»."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Gestor de la red"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Contraseña de la VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Se necesita autenticación"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrador"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autenticar"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Eso no ha funcionado. Inténtelo de nuevo."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Ahora %s se llama %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Ventanas"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Mostrar aplicaciones"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Tablero"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %-d de %B de %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Hoy"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Mañana"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Todo el día"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "No hay eventos"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Añadir relojes del mundo…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Relojes del mundo"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Cargando…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Conectarse para obtener la información meteorológica"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "La información meteorológica no está disponible actualmente."
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Meteorología"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Seleccionar ubicación meteorológica…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Cerrar la sesión %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Cerrar la sesión"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "se cerrará automáticamente la sesión de %s en %d segundo."
+msgstr[1] "se cerrará automáticamente la sesión de %s en %d segundos."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Su sesión se cerrará automáticamente en %d segundo."
+msgstr[1] "Su sesión se cerrará automáticamente en %d segundos."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Cerrar la sesión"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Apagar"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalar actualizaciones y apagar"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "El sistema se apagará automáticamente en %d segundo."
+msgstr[1] "El sistema se apagará automáticamente en %d segundos."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalar las actualizaciones de software pendientes"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Apagar"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalar actualizaciones y reiniciar"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "El sistema se reiniciará automáticamente en %d segundo."
+msgstr[1] "El sistema se reiniciará automáticamente en %d segundos."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reiniciar e instalar actualizaciones"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"El sistema se reiniciará automáticamente e instalará las actualizaciones en "
+"%d segundo."
+msgstr[1] ""
+"El sistema se reiniciará automáticamente e instalará las actualizaciones en "
+"%d segundos."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reiniciar e instalar"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalar y apagar"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Apagar después de instalar las actualizaciones"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reiniciar e instalar actualizaciones"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s se instalará después de reiniciar. La instalación de la actualización "
+"puede tardar mucho tiempo: asegúrese de que tiene una copia de respaldo y de "
+"que el equipo está enchufado."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Queda poca batería: enchufe el equipo antes de instalar las actualizaciones."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Algunas aplicaciones están ocupadas o tienen trabajo sin guardar"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Hay otros usuarios con la sesión iniciada"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opciones de arranque"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remoto)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instalar"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalar extensión"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "¿Descargar e instalar «%s» desde extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Actualizaciones de extensiones disponibles"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Las actualizaciones de las extensiones están listas para instalarse."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Permitir inhibir los atajos"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "La aplicación %s quiere inhibir los atajos"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Una aplicación quiere inhibir los atajos"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Puede restaurar los atajos pulsando %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Denegar"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Permitir"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Teclas lentas activadas"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Teclas lentas desactivadas"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Acaba de pulsar la tecla «Mayúsculas» durante 8 segundos. Ésto es el atajo "
+"del teclado para la característica «Teclas lentas», que afecta la forma en "
+"que funciona el teclado."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Teclas persistentes activadas"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Teclas persistentes desactivadas"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Acaba de pulsar la tecla «Mayúsculas» 5 veces. Este es el acceso rápido para "
+"la característica «Teclas persistentes», que afecta la manera en que "
+"funciona el teclado."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Acaba de pulsar dos teclas a la vez, o ha pulsado la tecla «Mayúsculas» 5 "
+"veces seguidas. Esto desactiva la característica de «Teclas persistentes», "
+"lo cual afecta a la forma en que funciona su teclado."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Dejar activada"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Encender"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Apagar"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Dejar apagado"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Configuración de región e idioma"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "No hay extensiones instaladas"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s no ha generado ningún error."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Ocultar errores"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Mostrar errores"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Activado"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Desactivado"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Error"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Caducado"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Descargando"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Ver fuente"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Página web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "El sistema se puso en modo no seguro"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Las aplicaciones tienen ahora acceso sin restricciones"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Deshacer"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Sesión iniciada como usuario con privilegios"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Se debe evitar ejecutar una sesión como usuario con privilegios por motivos "
+"de seguridad. Si es posible, inicie sesión como un usuario normal."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Pantalla de bloqueo desactivada"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "La pantalla de bloqueo necesita el gestor de pantallas de GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Información del sistema"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artista desconocido"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Título desconocido"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Escribir para buscar"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Aplicaciones"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Vista general"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Atajo nuevo…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplicación definida"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Mostrar la ayuda en pantalla"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Cambiar monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Asignar pulsación"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Hecho"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Editar…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nada"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Pulse un botón para configurar"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Pulse Esc para salir"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Pulse cualquier tecla para salir"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Actividades"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Ejecutar un comando"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Pulse Esc para cerrar"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Reiniciar no está disponible en Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Reiniciando…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME necesita bloquear la pantalla"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "No se pudo bloquear"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Una aplicación impidió el bloqueo"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Selección"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Selección de área"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Pantalla"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Selección de pantalla"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Ventana"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Selección de ventana"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Captura/grabación de pantalla"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Mostrar puntero"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Grabaciones de la pantalla"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Grabación de pantalla desde %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Captura de pantalla"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Grabación de pantalla realizada"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Pulse aquí para ver el vídeo."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Mostrar en Archivos"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Captura desde %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Captura de pantalla realizada"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Puede pegar la imagen desde el portapapeles."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Captura de pantalla realizada"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Buscando…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "No se encontraron resultados."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d más"
+msgstr[1] "%d más"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Buscar"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copiar"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Pegar"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Mostrar texto"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ocultar texto"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Bloq. Mayús. está activo"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Ocultar volumen"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volumen de sistema Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Usa archivos de claves"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Para desbloquear un volumen que usa archivos de claves use la herramienta <i>"
+"%s</i> en su lugar."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Número PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Recordar contraseña"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desbloquear"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Abrir %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "El PIM debe ser un número o estar vacío."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "No se puede iniciar %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "No se pudo encontrar la aplicación %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accesibilidad"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Contraste alto"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Ampliación"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Lector de pantalla"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Teclado en pantalla"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Alertas visuales"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Teclas persistentes"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Teclas lentas"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Rechazo de teclas"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Teclas del ratón"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Texto grande"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Rotar automáticamente"
+
+#: js/ui/status/bluetooth.js:171
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Brillo"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Modo oscuro"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Una sola pulsación"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Doble pulsación"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Arrastrar"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Pulsación secundaria"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Pulsación al posarse"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Teclado"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Mostrar la distribución del teclado"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Permitir acceso a la ubicación"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "La aplicación %s quiere acceder a su ubicación"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Los servicios de ubicación se pueden cambiar en cualquier momento desde la "
+"configuración de privacidad."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<desconocido>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Desconectar %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Conectar a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Punto de acceso %s"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Luz nocturna"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Rendimiento"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Balanceado"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Ahorro de energía"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Perfiles de energía"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Detener la grabación de la pantalla"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Detener la compartición de la pantalla"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Modo avión"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Capturar la pantalla"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menú Apagar"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspender"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Reiniciar…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Apagar…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Cerrar la sesión…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Cambiar de usuario…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Bloquear la pantalla"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Dispositivo Thunderbolt desconocido"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Se ha detectado un dispositivo nuevo mientras estaba fuera. Desconéctelo y "
+"vuélvalo a conectar para empezar a usarlo."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispositivo Thunderbolt no autorizado"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Se ha detectado un dispositivo nuevo y un administrador debe autorizarlo."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Error de autorización de Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "No se pudo autorizar el dispositivo Thunderbolt: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Volumen modificado"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Volumen"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Salida de sonido"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Entrada de sonido"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Espejo"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Unir pantallas"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Sólo la externa"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Sólo la integrada"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %d de %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Deslizar para desbloquear"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Pulse con el ratón o una tecla para desbloquear"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Desbloquear ventana"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Iniciar sesión como otro usuario"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Bienvenido a GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Si quiere aprender a moverse realice el tour."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "No gracias"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Hacer el tour"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» está preparado"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "¿Quiere mantener esta configuración de la pantalla?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Revertir configuración"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Mantener cambios"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "La configuración se revertirá en %d segundo"
+msgstr[1] "La configuración se revertirá en %d segundos"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Ocultar"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restaurar"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximizar"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Mover"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Mover la barra de título a la pantalla"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Siempre encima"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Siempre en el área de trabajo visible"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Mover al área de trabajo de la izquierda"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Mover al área de trabajo de la derecha"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Subir a un área de trabajo"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Bajar a un área de trabajo"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Mover a la pantalla de arriba"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Mover a la pantalla de abajo"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Mover a la pantalla de la izquierda"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Mover a la pantalla de la derecha"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Cerrar"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendario de Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Imprimir versión"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Modo usado por GDM para la pantalla de inicio"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Usar un modo específico, por ejemplo, «gdm» para la pantalla de inicio de "
+"sesión"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Listar los modos posibles"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Desconocido"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Falló al lanzar «%s»"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Las contraseñas no coinciden."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "La contraseña no puede estar vacía"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "El usuario rechazó el diálogo de autenticación"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensiones"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Gestionar sus extensiones de GNOME Shell"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "El Proyecto GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Extensiones de GNOME gestiona las actualizaciones de las extensiones, "
+"configurando sus preferencias y quitando o desactivando las que no quiera."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configurar las extensiones de GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "No hay coincidencias"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "¿Quitar «%s»?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Si quita la extensión necesitará volver a descargarla si quiere activarla de "
+"nuevo"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Quitar"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Daniel Mustieles <daniel.mustieles@gmail.com>, 2010-2020\n"
+"Benjamín Valero Espinosa <benjavalero@gmail.com>, 2011\n"
+"Jorge González <jorgegonz@svn.gnome.org>, 2009, 2010, 2011"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "la próxima vez que inicie sesión se actualizará %d extensión"
+msgstr[1] "la próxima vez que inicie sesión se actualizarán %d extensiones"
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "La extensión no es compatible con la versión de GNOME actual"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "La extensión contiene un error"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "La extensión se puede con actualizar"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Página web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Quitar…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Ayuda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Acerca de extensiones"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Las extensiones pueden provocar problemas de rendimiento y estabilidad. "
+"Desactive las extensiones si tiene problemas con su sistema."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Instalada manualmente"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Para buscar y añadir extensiones visite <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Integrada"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "No hay extensiones instaladas"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"No es posible obtener la lista de extensiones instaladas. asegúrese de que "
+"ha iniciado sesión en GNOME e inténtelo de nuevo."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Actualizaciones de extensiones disponibles"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Se ha creado correctamente la nueva extensión en %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"El nombre debe ser una cadena (descriptiva) muy corta.\n"
+"Algunos ejemplos: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nombre"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"La descripción es una única frase para explicar qué hace su extensión.\n"
+"Algunos ejemplos: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descripción"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"El UUID es un identificador único global para su extensión.\n"
+"Normalmente debe estar en formato de dirección de correo electrónico "
+"(pulsarparaenfocar@usuario.ejemplo.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Elija una de las plantillas disponibles:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Plantilla"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "El identificador único de la nueva extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOMBRE"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "El nombre visible por el usuario de esta extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPCIÓN"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Una breve descripción de qué hace la extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "PLANTILLA"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "La plantilla que usar para la nueva extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Introduzca la información de la extensión interactivamente"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Crear una extensión nueva"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argumentos desconocidos"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Se necesita el UUID, el nombre y la descripción"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Falló al conectar a GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "La extensión «%s» no existe\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Desactivar una extensión"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "No se ha especificado el UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Se ha proporcionado más de un UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Activar una extensión"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "La extensión «%s» no existe\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostrar información de las extensiones"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Sobrescribir una extensión que ya exista"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instalar un conjunto de extensiones"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "No se ha especificado un conjunto de extensiones"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Se ha especificado más de un conjunto de extensiones"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Mostrar las extensiones de usuario instaladas"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostrar las extensiones del sistema instaladas"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostrar extensiones activadas"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostrar extensiones desactivadas"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostrar extensiones con preferencias"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostrar extensiones con actualizaciones"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Mostrar detalles de la extensión"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Listar las extensiones instaladas"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ARCHIVO"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Fuente adicional que incluir en el conjunto"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ESQUEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Se debe incluir un esquema de GSettings"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "CARPETA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "La carpeta donde están las traducciones"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMINIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "El dominio de gettext que usar para las traducciones"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Sobrescribir un paquete existente"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "La carpeta donde se debe crear el paquete"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "CARPETA_FUENTE"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Crear un conjunto de extensiones"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Se ha especificado más de una carpeta fuente"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "La extensión «%s» no tiene opciones\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Falló al abrir las preferencias para la extensión «%s»: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "abre las preferencias de la extensión"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Restablecer una extensión"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "No se pueden desinstalar extensiones del sistema\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Falló al desinstalar «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Desinstalar una extensión"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "No mostrar mensajes de errores"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Ruta"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autor original"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versión"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Estado"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» no lleva argumentos"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Uso:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Mostrar información de la versión y salir."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMANDO"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Comandos:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Mostrar ayuda"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Activar extensión"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Desactivar extensión"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Restablecer extensión"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Desinstalar extensión"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Listar extensiones"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Mostrar información de la extensión"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Abrir las preferencias de la extensión"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Crear extensión"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Extensión del paquete"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instalar conjunto de extensiones"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Use «%s» para obtener ayuda detallada.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Plano"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Una extensión vacía"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicador"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Añadir un icono a la barra superior"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u salida"
+msgstr[1] "%u salidas"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entradas"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sonidos del sistema"
+
+#~ msgid "Log Out"
+#~ msgstr "Cerrar la sesión"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s apagada"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s conectada"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s sin gestionar"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "Conectando %s"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s requiere autenticación"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Falta el «firmware» para %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s no disponible"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Falló la conexión %s"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Configuración de red cableada"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Configuración de banda ancha móvil"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Hardware %s desactivado"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s desactivado"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Configuración de Bluetooth"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Conectar a Internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "El modo avión está activado"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "La Wi-Fi se desactiva cuando se activa el modo avión."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Apagar el modo avión"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "La Wi-Fi está desactivada"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Se debe activar la Wi-Fi para poder conectarse a la red."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Activar la Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Redes Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Seleccionar una red"
+
+#~ msgid "No Networks"
+#~ msgstr "No hay redes"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Usar el interruptor hardware para apagar"
+
+#~ msgid "Select Network"
+#~ msgstr "Seleccionar red"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Configuración de Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s no conectado"
+
+#~ msgid "unknown"
+#~ msgstr "desconocido"
+
+#~ msgid "activating…"
+#~ msgstr "activando…"
+
+#~ msgid "deactivating…"
+#~ msgstr "desactivando…"
+
+#~ msgid "deactivated"
+#~ msgstr "desactivado"
+
+#~ msgid "connecting…"
+#~ msgstr "conectando…"
+
+#~ msgid "authentication required"
+#~ msgstr "se necesita autenticación"
+
+#~ msgid "connection failed"
+#~ msgstr "falló la conexión"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Configuración de VPN"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN apagada"
+
+#~ msgid "Network Settings"
+#~ msgstr "Configuración de la red"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s conexión cableada"
+#~ msgstr[1] "%s conexiones cableadas"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s conexión inalámbrica"
+#~ msgstr[1] "%s conexiones inalámbricas"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s conexión por módem"
+#~ msgstr[1] "%s conexiones por módem"
+
+#~ msgid "Connection failed"
+#~ msgstr "Falló la conexión"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Falló la activación de la conexión de red"
+
+#~ msgid "Power Settings"
+#~ msgstr "Configuración de energía"
+
+#~ msgid "Lock"
+#~ msgstr "Bloquear"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Apagar / cerrar sesión"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d conectado"
+#~ msgstr[1] "%d conectados"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth encendido"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth apagado"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Ubicación activada"
+
+#~ msgid "Disable"
+#~ msgstr "Desactivar"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Configuración de privacidad"
+
+#~ msgid "Location In Use"
+#~ msgstr "Ubicación en uso"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Ubicación desactivada"
+
+#~ msgid "Enable"
+#~ msgstr "Activar"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Luz nocturna desactivada"
+
+#~ msgid "Resume"
+#~ msgstr "Reanudar"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Desactivar hasta mañana"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Cargada completamente"
+
+#~ msgid "Not Charging"
+#~ msgstr "No está cargando"
+
+#~ msgid "Estimating…"
+#~ msgstr "Estimando…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d queda (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d para la carga completa (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Se está compartiendo la pantalla"
+
+#~ msgid "Turn off"
+#~ msgstr "Apagar"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Modo avión activado"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "Mostrar IU de captura de pantalla"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Quitar de los favoritos"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Añadir a los favoritos"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Grabación de pantalla en curso"
+
+#~ msgid "Stop"
+#~ msgstr "Parada"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Advertencia"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Las extensiones pueden causar problemas en el sistema, incluso problemas "
+#~ "de rendimiento. Si tiene problemas con sus sistema se recomienda "
+#~ "desactivar todas las extensiones."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Activar la introspección de API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Activa una API de D-Bus que permite la introspección del estado de la "
+#~ "aplicación de la shell."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Falló al conectar a GNOME Shell"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizar"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Desmaximizar"
+
+#~ msgid "App Picker View"
+#~ msgstr "Visor del selector de aplicaciones"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Índice de la vista seleccionada actualmente en el selector de "
+#~ "aplicaciones."
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %d de %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %d de %B de %Y"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Las aplicaciones usadas frecuentemente aparecerán aquí"
+
+#~ msgid "Frequent"
+#~ msgstr "Frecuentes"
+
+#~ msgid "All"
+#~ msgstr "Todas"
+
+#~ msgid "Copy Error"
+#~ msgstr "Copiar error"
+
+#~ msgid "Off"
+#~ msgstr "Desconectado"
+
+#~ msgid "On"
+#~ msgstr "Encendido"
+
+#~| msgid "Username: "
+#~ msgid "Username…"
+#~ msgstr "Nombre de usuario…"
+
+#~ msgid "Password: "
+#~ msgstr "Contraseña: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Introduzca la contraseña…"
+
+#~ msgid "Next"
+#~ msgstr "Siguiente"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Iniciar sesión"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d de %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d mensaje nuevo"
+#~ msgstr[1] "%d mensajes nuevos"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d notificación nueva"
+#~ msgstr[1] "%d notificaciones nuevas"
+
+#~ msgid "Logout…"
+#~ msgstr "Cerrar sesión…"
+
+#~ msgid "org.gnome.Extensions"
+#~ msgstr "org.gnome.Extensions"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Buscar en Software"
+
+#~ msgid "Password:"
+#~ msgstr "Contraseña:"
+
+#~ msgid "Type again:"
+#~ msgstr "Escriba de nuevo:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "La red inalámbrica requiere autenticación"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Contraseña de la red de banda ancha móvil"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Bloquear la orientación"
+
+#~ msgid "Rename"
+#~ msgstr "Renombrar"
+
+#~ msgid "Account Settings"
+#~ msgstr "Configuración de la cuenta"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Bloqueo de orientación"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Asociación de teclas que pausa y reanuda los «tweens» en ejecución, para "
+#~ "depuración."
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Qué teclado usar"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "El tipo de teclado que usar."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Hubo un error al lanzar el diálogo de preferencias para %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s todo el día."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, luego %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, luego %s seguido de %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Sensación térmica de %s."
+
+#~| msgid "Log Out"
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Cerrar la sesión"
+
+#~| msgid "Switch User"
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Cambiar de usuario"
+
+#~ msgid "Hide tray"
+#~ msgstr "Ocultar bandeja"
+
+#~ msgid "Status Icons"
+#~ msgstr "Iconos de estado"
+
+#~ msgid "Events"
+#~ msgstr "Eventos"
+
+#~ msgid "Notifications"
+#~ msgstr "Notificaciones"
+
+#~ msgid "Media"
+#~ msgstr "Medios"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Redirección para autenticación web"
+
+#~ msgid "Not In Use"
+#~ msgstr "No está en uso"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Mostrar la fecha de la semana en el calendario"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Si es cierta, muestra la fecha de semana ISO en el calendario."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Usar como conexión a Internet"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s solicita acceso a su ubicación."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (composición wayland)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d dispositivo conectado"
+#~ msgstr[1] "%d dispositivos conectados"
+
+#~ msgid "UPS"
+#~ msgstr "SAI"
+
+#~ msgid "Battery"
+#~ msgstr "Batería"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgid "Invitation"
+#~ msgstr "Invitación"
+
+#~ msgid "Call"
+#~ msgstr "Llamar"
+
+#~ msgid "File Transfer"
+#~ msgstr "Transferencia de archivos"
+
+#~ msgid "Chat"
+#~ msgstr "Chat"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Invitación a %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s le está invitando a unirse a %s"
+
+#~ msgid "Decline"
+#~ msgstr "Rechazar"
+
+#~ msgid "Accept"
+#~ msgstr "Aceptar"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Videollamada de %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Llamada de %s"
+
+#~ msgid "Answer"
+#~ msgstr "Responder"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s le está enviando %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s solicita permiso para ver cuándo está en línea"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Falló la autenticación"
+
+#~ msgid "Encryption error"
+#~ msgstr "Error de cifrado"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Certificado no proporcionado"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "No se confía en el certificado"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certificado caducado"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certificado no activado"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "El nombre del servidor dle certificado no coincide"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "La huella del certificado no coincide"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificado autofirmado"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "El estado está establecido a «desconectado»"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "El certificado no es válido"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Se ha rechazado la conexión"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "No se puede establecer la conexión"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Se ha perdido la conexión"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Esta cuenta ya está conectada al servidor"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "Se ha sustituido la conexión por una nueva conexión usando el mismo "
+#~ "recurso"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "La cuenta ya existe en el servidor"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr ""
+#~ "Actualmente el servidor está muy ocupado intentando gestionar la conexión"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Se ha revocado el certificado"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "El certificado usa un algoritmo de cifrado inseguro o es "
+#~ "criptográficamente débil"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "La longitud del certificado del servidor o la profundidad de la cadena "
+#~ "del certificado del servidor exceden los límites impuestos por la "
+#~ "biblioteca de criptografía"
+
+#~ msgid "Internal error"
+#~ msgstr "Error interno"
+
+#~ msgid "View account"
+#~ msgstr "Ver cuenta"
+
+#~ msgid "Show the message list"
+#~ msgstr "Mostrar la lista de mensajes"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Dom"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "L"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "M"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "X"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "J"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "V"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "S"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Nada programado"
+
+#~ msgid "This week"
+#~ msgstr "Esta semana"
+
+#~ msgid "Next week"
+#~ msgstr "La semana que viene"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Dispositivos extraíbles"
+
+#~ msgid "Eject"
+#~ msgstr "Expulsar"
+
+#~ msgid "Unmute"
+#~ msgstr "Dar voz"
+
+#~ msgid "Mute"
+#~ msgstr "Silenciar"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Abrir calendario"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Configuración de hora y fecha"
+
+#~ msgid "Open"
+#~ msgstr "Abrir"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Limpiar mensajes"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Configuración de las notificaciones"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Bandeja de menú"
+
+#~ msgid "No Messages"
+#~ msgstr "No hay mensajes"
+
+#~ msgid "Message Tray"
+#~ msgstr "Bandeja de mensajes"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Portal captivo"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "El nivel máximo de precisión de la ubicación."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Configura el nivel máximo de precisión de la ubicación que se permite ver "
+#~ "a las aplicaciones. Las opciones válidas son «off» (desactivar el rastreo "
+#~ "de la ubicación), «country», «city», «neighborhood», «street», y "
+#~ "«exact» (normalmente necesita un receptor GPS). Tenga en cuenta que esto "
+#~ "sólo controla lo que GeoClue permitirá ver a las aplicaciones y lo que "
+#~ "pueden encontrar sobre la ubicación de usuario usando los recursos de la "
+#~ "red (aunque con precisión a nivel de la calle, en el mejor de los casos)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Orden de los botones en la barra de título"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Esta clave sobrescribe la clave en org.gnome.desktop.wm.preferences al "
+#~ "ejecutar GNOME Shell."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Seleccione una extensión que configurar usando la caja combinada de "
+#~ "arriba."
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendario:MA"
+
+#~ msgid "unavailable"
+#~ msgstr "no disponible"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> de <b>%B</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b> de <b>%B</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Lista de categorías que se deben mostrar como carpetas"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Cada nombre de categoría de esta lista se representará como una carpeta "
+#~ "en la vista de aplicaciones, en lugar de mostrarse en línea en la vista "
+#~ "principal."
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Solicitud de autorización de %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "El dispositivo «%s» quiere emparejarse con este equipo"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "El dispositivo %s quiere acceder al servicio «%s»"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Conceder sólo esta vez"
+
+#~ msgid "Reject"
+#~ msgstr "Rechazar"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Confirmación de emparejamiento para «%s»"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Confirme que la clave mostrada en «%06d» coincide con la del dispositivo."
+
+#~ msgid "Does not match"
+#~ msgstr "No coincide"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Solicitud de emparejamiento para «%s»"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Introduzca el PIN mencionado en el dispositivo."
+
+#~ msgid "OK"
+#~ msgstr "Aceptar"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Hoy no tiene ningún mensaje:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s el oráculo dice"
+
+#~ msgid "Settings Menu"
+#~ msgstr "Menú de configuración"
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Usado internamente para guardar el último IM de presencia establecido "
+#~ "explícitamente por el usuario. El valor aquí es de la enumeración "
+#~ "TpConnectionPresenceType."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Usado internamente para guardar el último estado de presencia de la "
+#~ "sesión del usuario. El valor aquí es de la GsmPresenceStatus."
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Pulse «Cerrar la sesión» para salir de esas aplicaciones y cerrar la "
+#~ "sesión del sistema."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Cerrando la sesión."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "Pulse «Apagar» para salir de esas aplicaciones y apagar el sistema."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Apagando el sistema."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Pulse «Reiniciar» para salir de esas aplicaciones y reiniciar el sistema."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Reiniciando el sistema."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Apagar puede hacer que pierdan el trabajo que no hayan guardado."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Asociación de teclas cambiar el grabador de pantalla"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Asociación de teclas para iniciar/detener el grabador de pantalla."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Tasa de fotogramas usada para grabar «screencast»."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "La tasa de fotogramas de la grabación resultante grabada por el grabador "
+#~ "de «screencast» de GNOME Shell, en fotogramas por segundo."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "La tubería de gstreamer usada para codificar el «screencast»"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Establece la tubería GStreamer usada para codificar grabaciones. Sigue la "
+#~ "sintaxis usada para gst-launch. La tubería debería tener un sumidero "
+#~ "(«sink») desconectado donde grabar el vídeo que se está grabando. "
+#~ "Generalmente tendrá un punto de origen desconectado; la salida de ese "
+#~ "punto se escribirá en el archivo de salida. Sin embargo, la tubería "
+#~ "también puede tomar parte en su propia salida; esto se puede usar para "
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Extensión de archivo que usar para almacenar los «screencast»"
+
+#~ msgid "Power"
+#~ msgstr "Energía"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Volumen, red, batería"
+
+#~ msgid "Visibility"
+#~ msgstr "Visibilidad"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Configurar un dispositivo nuevo…"
+
+#~ msgid "Send Files…"
+#~ msgstr "Enviar archivos…"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "disabled"
+#~ msgstr "desactivada"
+
+#~ msgid "More…"
+#~ msgstr "Más…"
+
+#~ msgid "Wired"
+#~ msgstr "Cableada"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Configuración del acceso universal"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Configuración del teclado"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Configuración del ratón…"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "Queda %d hora"
+#~ msgstr[1] "Queda %d horas"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "Quedan %d %s %d %s"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "hora"
+#~ msgstr[1] "horas"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minuto"
+#~ msgstr[1] "minutos"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "Queda %d minuto"
+#~ msgstr[1] "Quedan %d minutos"
+
+#~ msgid "AC Adapter"
+#~ msgstr "Adaptador de corriente"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Batería del portátil"
+
+#~ msgid "Monitor"
+#~ msgstr "Monitor"
+
+#~ msgid "Mouse"
+#~ msgstr "Ratón"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Teléfono móvil"
+
+#~ msgid "Media Player"
+#~ msgstr "Reproductor multimedia"
+
+#~ msgid "Tablet"
+#~ msgstr "Tableta"
+
+#~ msgid "Computer"
+#~ msgstr "Equipo"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Desconocido"
+
+#~ msgid "Available"
+#~ msgstr "Disponible"
+
+#~ msgid "Busy"
+#~ msgstr "Ocupado"
+
+#~ msgid "Invisible"
+#~ msgstr "Invisible"
+
+#~ msgid "Away"
+#~ msgstr "Ausente"
+
+#~ msgid "Idle"
+#~ msgstr "Inactivo"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Su estado del chat se establecerá a «ocupado»"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Las notificaciones están ahora desactivadas, incluyendo los mensajes de "
+#~ "chat. Su estado en línea se ha ajustado para que otros sepan que puede no "
+#~ "leer sus mensajes."
+
+#~ msgid "cable unplugged"
+#~ msgstr "cable desconectado"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr ""
+#~ "Indica si se deben recolectar estadísticas acerca del uso de las "
+#~ "aplicaciones"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "La «shell» normalmente monitoriza todas las aplicaciones activas para "
+#~ "presentar las más usadas (ej. en los lanzadores). Aunque estos datos se "
+#~ "mantienen de forma privada, puede querer desactivarlo por razones de "
+#~ "privacidad. Note que haciéndolo no eliminará los datos ya guardados."
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Ethernet automática"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Banda ancha automática"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Marcado automático"
+
+#~ msgid "Auto %s"
+#~ msgstr "%s automática"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Bluetooth automático"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Inalámbrica automática"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Iniciar sesión"
+
+#~ msgid "tray"
+#~ msgstr "bandeja"
+
+#~ msgid "More..."
+#~ msgstr "Más…"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Reino Unido"
+
+#~ msgid "Default"
+#~ msgstr "Predeterminada"
+
+#~ msgid "Show full name in the user menu"
+#~ msgstr "Mostrar el nombre completo en el menú del usuario"
+
+#~ msgid "Whether the users full name is shown in the user menu or not."
+#~ msgstr ""
+#~ "Indica si se muestra el nombre del usuario en el menú del usuario o no."
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APLICACIONES"
+
+#~ msgid "SETTINGS"
+#~ msgstr "CONFIGURACIÓN"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Su mensaje oculto favorito"
+
+#~ msgid "Subscription request"
+#~ msgstr "Solicitud de suscripción"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Enviado el <b>%A</b> a las <b>%H:%M</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Enviado el <b>%A</b>, <b>%d de %B</b> de %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Falló la conexión a %s"
+
+#~ msgid "Reconnect"
+#~ msgstr "Reconectar"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Examinar archivos…"
+
+#~ msgid "Error browsing device"
+#~ msgstr "Error al examinar el dispositivo"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "No se puede examinar el dispositivo solicitado, el error es «%s»"
+
+#~ msgid "Wireless"
+#~ msgstr "Inalámbrica"
+
+#~ msgid "VPN Connections"
+#~ msgstr "Conexiones VPN"
+
+#~ msgid "System Settings"
+#~ msgstr "Configuración del sistema"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "proveedores OpenSearch desactivados"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Falló al desmontar «%s»"
+
+#~ msgid "Retry"
+#~ msgstr "Reintentar"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "LUGARES Y DISPOSITIVOS"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Passphrase"
+#~ msgstr "Contraseña"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Mostrar la hora con segundos"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Si es cierta, muestra los segundos en la hora."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Mostrar la fecha en el reloj"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Si es cierta, muestra la fecha en el reloj, además de la hora."
+
+#~ msgctxt "contact"
+#~ msgid "Unknown"
+#~ msgstr "Desconocido"
+
+#~ msgid "CONTACTS"
+#~ msgstr "CONTACTOS"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %e de %b, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e de %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e de %b, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "%s is online."
+#~ msgstr "%s está conectado/a."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s está desconectado/a."
+
+#~ msgid "%s is away."
+#~ msgstr "%s está ausente."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s está ocupado/a."
+
+#~ msgid "Hidden"
+#~ msgstr "Oculto"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Contraseña incorrecta; inténtelo de nuevo"
+
+#~ msgid "Power Off..."
+#~ msgstr "Apagar…"
+
+#~ msgid "Online Accounts"
+#~ msgstr "Cuentas en línea"
+
+#~ msgid "Log Out..."
+#~ msgstr "Cerrar la sesión…"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "ELEMENTOS RECIENTES"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should be loaded. disabled-extensions overrides this setting for "
+#~ "extensions that appear in both lists."
+#~ msgstr ""
+#~ "Las extensiones de GNOME Shell tienen una propiedad uuid; esta clave "
+#~ "lista las extensiones que no se deben cargar. «disabled-extensions» "
+#~ "sobrescribe este ajuste para las extensiones que aparecen en ambas listas."
+
+#~ msgid "Show password"
+#~ msgstr "Mostrar contraseña"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s finalizó su lanzamiento"
+
+#~ msgid "If true, display onscreen keyboard."
+#~ msgstr "Si es cierta, muestra el teclado en pantalla."
+
+#~ msgid "Connectivity lost"
+#~ msgstr "Conectividad perdida"
+
+#~| msgid "You're no longer connected to the network"
+#~ msgid "You are no longer connected to the network"
+#~ msgstr "Ya no está conectado a la red"
+
+#~ msgid "calendar:week_start:0"
+#~ msgstr "calendar:week_start:1"
+
+#, fuzzy
+#~ msgid "The length of the server certificate, or the depth of the "
+#~ msgstr "La longitud del certificado del servidor o la profundidad del"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "Ahora está conectado a la red de banda ancha móvil «%s»"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "Ahora está conectado a la red inalámbrica «%s»"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "Ahora está conectado a la VPN «%s»"
+
+#~ msgid "Uuids of extensions to disable"
+#~ msgstr "Uuid de las extensiones que desactivar"
+
+#~ msgid "Localization Settings"
+#~ msgstr "Configuración regional"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "Hace menos de un minuto"
+
+#~ msgid "Shut Down"
+#~ msgstr "Apagar"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr "Pulse «Apagar» para salir de esas aplicaciones y apagar el sistema."
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "El sistema se apagará automáticamente en %d segundos."
+
+#~ msgid "Shutting down the system."
+#~ msgstr "Apagando el sistema."
+
+#~ msgid "Confirm"
+#~ msgstr "Confirmar"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "PREFERENCIAS"
+
+#~ msgid "Shut Down..."
+#~ msgstr "Apagar…"
+
+#~ msgid "Clip the crosshairs at the center"
+#~ msgstr "Fijar los cursores en el centro"
+
+#~ msgid "Color of the crosshairs"
+#~ msgstr "Color de las cruces"
+
+#~ msgid ""
+#~ "Determines the length of the vertical and horizontal lines that make up "
+#~ "the crosshairs."
+#~ msgstr ""
+#~ "Determina la longitud de las líneas verticales y horizontales que "
+#~ "conforman los cursores."
+
+#~ msgid ""
+#~ "Determines the position of the magnified mouse image within the magnified "
+#~ "view and how it reacts to system mouse movement. The values are - none: "
+#~ "no mouse tracking; - centered: the mouse image is displayed at the center "
+#~ "of the zoom region (which also represents the point under the system "
+#~ "mouse) and the magnified contents are scrolled as the system mouse moves; "
+#~ "- proportional: the position of the magnified mouse in the zoom region is "
+#~ "proportionally the same as the position of the system mouse on screen; - "
+#~ "push: when the magnified mouse intersects a boundary of the zoom region, "
+#~ "the contents are scrolled into view."
+#~ msgstr ""
+#~ "Determina la posición de la imagen magnificada del ratón dentro de la "
+#~ "vista magnificada y cómo reacciona al movimiento del ratón del sistema. "
+#~ "Los valores son «none» (ninguno): sin seguimiento del ratón; "
+#~ "«centered» (centrado): la imagen del ratón se muestra en el centro de la "
+#~ "región ampliada (que también representa el punto bajo el ratón del "
+#~ "sistema) y el contenido magnificado se desliza según se mueve el ratón "
+#~ "del sistema; «proporcional»: la posición del ratón magnificado en la "
+#~ "región de ampliación es proporcionalmente la misma que la posición del "
+#~ "ratón del sistema en el ratón; «push» (empujar): cuando el ratón "
+#~ "magnificado cruza un límite de la región de ampliación, el contenido se "
+#~ "desliza en la vista."
+
+#~ msgid ""
+#~ "Determines the transparency of the crosshairs, from fully opaque to fully "
+#~ "transparent."
+#~ msgstr ""
+#~ "Determina la transparencia de los cursores, de completamente opacos a "
+#~ "completamente transparentes."
+
+#~ msgid ""
+#~ "Determines whether the crosshairs intersect the magnified mouse sprite, "
+#~ "or are clipped such that the ends of the horizontal and vertical lines "
+#~ "surround the mouse image."
+#~ msgstr ""
+#~ "Determina si las cruces cruzan el «sprite» magnificado del ratón o se "
+#~ "fijan de tal forma que los finales de las líneas horizontales y "
+#~ "verticales rodean la imagen del ratón."
+
+#~ msgid "Enable lens mode"
+#~ msgstr "Activar el modo lente"
+
+#~ msgid ""
+#~ "Enables/disables display of crosshairs centered on the magnified mouse "
+#~ "sprite."
+#~ msgstr ""
+#~ "Activa/desactiva el mostrado de los cursores centrados en el «sprite» "
+#~ "magnificado del ratón."
+
+#~ msgid ""
+#~ "For centered mouse tracking, when the system pointer is at or near the "
+#~ "edge of the screen, the magnified contents continue to scroll such that "
+#~ "the screen edge moves into the magnified view."
+#~ msgstr ""
+#~ "Para el seguimiento del centro del ratón, cuando el puntero del sistema "
+#~ "está cerca del borde de la pantalla, el contenido magnificado continúa "
+#~ "desplazándose de tal forma que el borde de la pantalla se mueve dentro de "
+#~ "la vista magnificada."
+
+#~ msgid "Length of the crosshairs"
+#~ msgstr "Longitud de las cruces"
+
+#~ msgid "Magnification factor"
+#~ msgstr "Factor de magnificación"
+
+#~ msgid "Mouse Tracking Mode"
+#~ msgstr "Modo de seguimiento del ratón"
+
+#~ msgid "Opacity of the crosshairs"
+#~ msgstr "Opacidad de las cruces"
+
+#~ msgid "Scroll magnified contents beyond the edges of the desktop"
+#~ msgstr ""
+#~ "Desplazar el contenido magnificado más allá de los bordes del escritorio"
+
+#~ msgid "Show or hide crosshairs"
+#~ msgstr "Mostrar u ocultar las cruces"
+
+#~ msgid "Show or hide the magnifier"
+#~ msgstr "Mostrar u ocultar el magnificador"
+
+#~ msgid "Show or hide the magnifier and all of its zoom regions."
+#~ msgstr ""
+#~ "Mostrar u ocultar el magnificador y todas sus regiones de ampliación."
+
+#~ msgid ""
+#~ "The color of the the vertical and horizontal lines that make up the "
+#~ "crosshairs."
+#~ msgstr ""
+#~ "El color de las líneas verticales y horizontales que conforman las cruces."
+
+#~ msgid ""
+#~ "The magnified view either fills the entire screen, or occupies the top-"
+#~ "half, bottom-half, left-half, or right-half of the screen."
+#~ msgstr ""
+#~ "La vista magnificada o llena la pantalla u ocupa la mitad superior, mitad "
+#~ "inferior, mitad izquierda o mitad derecha de la pantalla."
+
+#~ msgid ""
+#~ "The power of the magnification. A value of 1.0 means no magnification. A "
+#~ "value of 2.0 doubles the size."
+#~ msgstr ""
+#~ "El poder de la magnificación. Un valor de 1.0 significa sin "
+#~ "magnificación. Un valor de 2.0 duplica el tamaño."
+
+#~ msgid "Thickness of the crosshairs"
+#~ msgstr "Grosor de las cruces"
+
+#~ msgid ""
+#~ "Whether the magnified view should be centered over the location of the "
+#~ "system mouse and move with it."
+#~ msgstr ""
+#~ "Indica si la vista magnificada se debe centrar sobre la ubicación del "
+#~ "ratón del sistema y moverse con ella."
+
+#~ msgid ""
+#~ "Width of the vertical and horizontal lines that make up the crosshairs."
+#~ msgstr ""
+#~ "Anchura de las líneas verticales y horizontales que conforman las cruces."
+
+#~ msgid "Bluetooth Agent"
+#~ msgstr "Agente Bluetooth"
+
+#~ msgid "Search your computer"
+#~ msgstr "Buscar en su equipo"
+
+#~ msgid ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+#~ msgstr ""
+#~ "No se puede añadir un área de trabajo nueva porque se ha llegado al "
+#~ "límite de áreas de trabajo."
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "No se puede quitar el primer área de trabajo."
+
+#~ msgid "Customize the panel clock"
+#~ msgstr "Personalizar el reloj del panel"
+
+#~ msgid "Custom format of the clock"
+#~ msgstr "Formato personalizado del reloj"
+
+#~ msgid "Hour format"
+#~ msgstr "Formato de la hora"
+
+#~ msgid ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+#~ msgstr ""
+#~ "Si es cierta y el formato es «12-horas» o «24-horas», muestra los "
+#~ "segundos en la hora."
+
+#~ msgid ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+#~ msgstr ""
+#~ "Esta clave especifica el formato usado por el reloj del panel cuando la "
+#~ "clave del formato se establece a «custom» (personalizado). Puede usar "
+#~ "especificadores de conversión que entienda strftime() para obtener un "
+#~ "formato específico. Consulte el manual de strftime() para obtener más "
+#~ "información."
+
+#~ msgid ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+#~ msgstr ""
+#~ "Esta clave especifica el formato de la hora especificado por el reloj del "
+#~ "panel. Los valores posibles son «12-hour» (12 horas), «24-hour» (24 "
+#~ "horas), «unix» y «custom» (personalizado).Si se establece a «unix» el "
+#~ "reloj mostrará la hora en segundos desde la época (1 de enero de 1970). "
+#~ "Si se establece a «custom» el reloj mostrará la hora según el formato "
+#~ "especificado en la clave «custom_format». Note que si se establece a "
+#~ "«unix» o «custom» se ignoran las claves «show_date» y «show_seconds»."
+
+#~ msgid "Clock Preferences"
+#~ msgstr "Preferencias del reloj"
+
+#~ msgid "Show seco_nds"
+#~ msgstr "Mostrar los segu_ndos"
+
+#~ msgid "_12 hour format"
+#~ msgstr "Formato _12 horas"
+
+#~ msgid "_24 hour format"
+#~ msgstr "Formato _24 horas"
+
+#~ msgid "Preferences"
+#~ msgstr "Preferencias"
+
+#~ msgid "What's using power..."
+#~ msgstr "Lo que está usando energía…"
+
+#~ msgid "Overview workspace view mode"
+#~ msgstr "Modo de visualización de la vista previa del área de trabajo"
+
+#~ msgid ""
+#~ "The selected workspace view mode in the overview. Supported values are "
+#~ "\"single\" and \"grid\"."
+#~ msgstr ""
+#~ "El modo de vista del área de trabajo seleccionada en la vista general. "
+#~ "Los valores soportados son «single» (sencillo) y «grid» (rejilla)."
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "Arrastrar aquí para añadir a los favoritos"
+
+#~ msgid "Find"
+#~ msgstr "Buscar"
+
+#~ msgid "ON"
+#~ msgstr "ENCENDIDO"
+
+#~ msgid "OFF"
+#~ msgstr "APAGADO"
+
+#~ msgid "Sidebar"
+#~ msgstr "Barra lateral"
+
+#~ msgid "Recent Documents"
+#~ msgstr "Documentos recientes"
+
+#~ msgid "PLACES"
+#~ msgstr "LUGARES"
+
+#~ msgid "(see all)"
+#~ msgstr "(ver todo)"
+
+#~ msgid "SEARCH RESULTS"
+#~ msgstr "RESULTADOS DE LA BÚSQUEDA"
+
+#~ msgid "Can't lock screen: %s"
+#~ msgstr "No se puede bloquear la pantalla: %s"
+
+#~ msgid "Can't temporarily set screensaver to blank screen: %s"
+#~ msgstr ""
+#~ "No se puede establecer temporalmente el salvapantallas a oscurecer "
+#~ "pantalla: %s"
+
+#~ msgid "Can't logout: %s"
+#~ msgstr "No se puede salir de la sesión: %s"
+
+#~ msgid "Browse"
+#~ msgstr "Examine"
+
+#~ msgid "Find apps or documents"
+#~ msgstr "Encuentre aplicaciones o documentos"
+
+#~| msgid "RECENT DOCUMENTS"
+#~ msgid "DOCUMENTS"
+#~ msgstr "DOCUMENTOS"
+
+#~ msgid "The user manager object this user is controlled by."
+#~ msgstr "El objeto de gestión de usuarios que controla a este usuario."
diff --git a/po/et.po b/po/et.po
new file mode 100644
index 0000000..9552907
--- /dev/null
+++ b/po/et.po
@@ -0,0 +1,2885 @@
+# GNOME kesta eesti keele tõlge.
+# Estonian translation for gnome-shell.
+#
+# Copyright (C) 2010, 2011 The Gnome Project
+#
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Ivar Smolin <okul linux ee>, 2011.
+# Mattias Põldaru <mahfiaz@gmail.com>, 2010, 2011, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell MASTER\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2019-03-02 10:57+0000\n"
+"PO-Revision-Date: 2019-03-10 18:49+0200\n"
+"Last-Translator: Mart Raudsepp <leio@gentoo.org>\n"
+"Language-Team: Estonian <>\n"
+"Language: et\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Generator: Poedit 2.2\n"
+"X-DamnedLies-Scope: partial\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Arvuti"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Teadete nimekirja kuvamine"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Aktiivse teate fookustamine"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Ülevaate näitamine"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Kõigi rakenduste kuvamine"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Rakenduste menüü avamine"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:216
+msgid "Shell Extensions"
+msgstr "Shelli laiendused"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Shelli laienduste seadistamine"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Aknahaldur ja rakenduste käivitaja"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Arendajate ja testijate jaoks sisemiste tööriistade lubamine Alt-F2 alt"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Lubab ligipääsu sisemistele silumise ja monitoorimise tööriistadele Alt-F2 "
+"dialoogi kaudu."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Lubatavate laienduste UUID-d"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shelli laiendustel on UUID omadus; selles võtmes loetletud laiendused "
+"tuleks laadida. Seda loendit saab muuta kasutades EnableExtension ja "
+"DisableExtension DBus-i meetodit kohas org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disable user extensions"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:34
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lemmikrakenduste töölauafailide ID-de loend"
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "Nendele tunnustele vastavaid rakendusi kuvatakse lemmikutes."
+
+#: data/org.gnome.shell.gschema.xml.in:51
+msgid "App Picker View"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:52
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:58
+msgid "History for command (Alt-F2) dialog"
+msgstr "Käsudialoogi (Alt-F2) ajalugu"
+
+# suurendusklaasidialoog? miks ka mitte :)
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "History for the looking glass dialog"
+msgstr "Otsingudialoogi ajalugu"
+
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Kasutajamenüüs näidatakse alati „Logi välja“ menüükirjet."
+
+#: data/org.gnome.shell.gschema.xml.in:68
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"See võti keelab automaatse „Logi välja“ menüükirje peitmise, kui arvutis on "
+"üks kasutaja ning avatud üks seanss."
+
+#: data/org.gnome.shell.gschema.xml.in:75
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Kas pidada meeles krüpteeritud või kaugfailisüsteemide haakimise paroole"
+
+#: data/org.gnome.shell.gschema.xml.in:76
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Shell küsib parooli, kui haagitakse krüpteeritud seade või kaugfailisüsteem. "
+"Kui parooli on võimalik salvestada edaspidiseks kasutuseks, näidatakse "
+"„Salvesta parool“ märkeruutu. See võti määrab selle märkeruudu vaikimisi "
+"oleku."
+
+#: data/org.gnome.shell.gschema.xml.in:85
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:95
+msgid "Enable introspection API"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Keybinding to open the application menu"
+msgstr "Rakenduste menüü avamise kiirklahv"
+
+#: data/org.gnome.shell.gschema.xml.in:110
+msgid "Keybinding to open the application menu."
+msgstr "Kiirklahv rakenduste menüü avamiseks."
+
+#: data/org.gnome.shell.gschema.xml.in:116
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "„Rakenduste kuvamise“ kiirklahv"
+
+#: data/org.gnome.shell.gschema.xml.in:117
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "„Rakenduste kuvamise“ kiirklahv tegevuste ülevaates."
+
+#: data/org.gnome.shell.gschema.xml.in:124
+msgid "Keybinding to open the overview"
+msgstr "Ülevaate avamise kiirklahv"
+
+#: data/org.gnome.shell.gschema.xml.in:125
+msgid "Keybinding to open the Activities Overview."
+msgstr "Tegevuste ülevaate avamise kiirklahv."
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Teadete loendi nähtavuse muutmise kiirklahv"
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Kiirklahv teadete loendi nähtavuse muutmiseks."
+
+#: data/org.gnome.shell.gschema.xml.in:138
+msgid "Keybinding to focus the active notification"
+msgstr "Aktiivse teate fookustamise kiirklahv"
+
+#: data/org.gnome.shell.gschema.xml.in:139
+msgid "Keybinding to focus the active notification."
+msgstr "Aktiivse teate fookustamise kiirklahv."
+
+#: data/org.gnome.shell.gschema.xml.in:145
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:150
+msgid "Switch to application 1"
+msgstr "Liikumine 1. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:154
+msgid "Switch to application 2"
+msgstr "Liikumine 2. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Switch to application 3"
+msgstr "Liikumine 3. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Switch to application 4"
+msgstr "Liikumine 4. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Switch to application 5"
+msgstr "Liikumine 5. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Switch to application 6"
+msgstr "Liikumine 6. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:174
+msgid "Switch to application 7"
+msgstr "Liikumine 7. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Switch to application 8"
+msgstr "Liikumine 8. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:182
+msgid "Switch to application 9"
+msgstr "Liikumine 9. rakendusele"
+
+#: data/org.gnome.shell.gschema.xml.in:190
+msgid "Which keyboard to use"
+msgstr "Millist klaviatuuri kasutada"
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "The type of keyboard to use."
+msgstr "Kasutatava klaviatuuri liik."
+
+#: data/org.gnome.shell.gschema.xml.in:202
+#: data/org.gnome.shell.gschema.xml.in:229
+msgid "Limit switcher to current workspace."
+msgstr "Piira aknavahetaja käesolevale tööalale."
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "The application icon mode."
+msgstr "Rakenduste ikooni režiim."
+
+#: data/org.gnome.shell.gschema.xml.in:221
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Seadistab, kuidas aknaid aknavahetajas kuvatakse. Sobivad väärtused on "
+"„thumbnail-only“ (näidatakse ainult akna pisipilti), „app-icon-"
+"only“ (näidatakse ainult akna ikooni) või „both“ (mõlemad)."
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Attach modal dialog to the parent window"
+msgstr "Modaaldialoog kuulub vanemakna juurde"
+
+#: data/org.gnome.shell.gschema.xml.in:243
+#: data/org.gnome.shell.gschema.xml.in:252
+#: data/org.gnome.shell.gschema.xml.in:260
+#: data/org.gnome.shell.gschema.xml.in:268
+#: data/org.gnome.shell.gschema.xml.in:276
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"See võti on ülimuslik võtme org.gnome.mutter suhtes, kui kasutatakse GNOME "
+"Shelli."
+
+#: data/org.gnome.shell.gschema.xml.in:251
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Akende lohistamisel ekraani serva lubatakse serva paigutamine"
+
+#: data/org.gnome.shell.gschema.xml.in:259
+msgid "Workspaces are managed dynamically"
+msgstr "Tööalasid hallatakse dünaamiliselt"
+
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid "Workspaces only on primary monitor"
+msgstr "Tööalad peamisel monitoril"
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Hiire all asuv aken saab fookuse alles hiire peatumisel"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Võrku sisselogimine"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:116
+msgid "Something’s gone wrong"
+msgstr "Midagi läks nihu"
+
+#: js/extensionPrefs/main.js:123
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/extensionPrefs/main.js:130
+msgid "Technical Details"
+msgstr "Tehnilised üksikasjad"
+
+#: js/extensionPrefs/main.js:165
+msgid "Copy Error"
+msgstr "Kopeeri veateade"
+
+#: js/extensionPrefs/main.js:185
+msgid "Homepage"
+msgstr "Koduleht"
+
+#: js/extensionPrefs/main.js:186
+msgid "Visit extension homepage"
+msgstr "Külasta laienduse kodulehte"
+
+#: js/extensionPrefs/main.js:449
+msgid "No Extensions Installed"
+msgstr "Ühtegi laiendust pole paigaldatud"
+
+#: js/extensionPrefs/main.js:459
+msgid ""
+"Extensions can be installed through Software or <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+msgstr ""
+
+#: js/extensionPrefs/main.js:474
+msgid "Browse in Software"
+msgstr ""
+
+#: js/gdm/authPrompt.js:140 js/ui/audioDeviceSelection.js:55
+#: js/ui/components/networkAgent.js:117 js/ui/components/polkitAgent.js:136
+#: js/ui/endSessionDialog.js:430 js/ui/extensionDownloader.js:188
+#: js/ui/shellMountOperation.js:325 js/ui/status/network.js:888
+msgid "Cancel"
+msgstr "Katkesta"
+
+#: js/gdm/authPrompt.js:159 js/gdm/authPrompt.js:202 js/gdm/authPrompt.js:434
+msgid "Next"
+msgstr "Järgmine"
+
+#: js/gdm/authPrompt.js:198 js/ui/shellMountOperation.js:329
+#: js/ui/unlockDialog.js:41
+msgid "Unlock"
+msgstr "Võta lukust lahti"
+
+#: js/gdm/authPrompt.js:200
+msgctxt "button"
+msgid "Sign In"
+msgstr "Logi sisse"
+
+#: js/gdm/loginDialog.js:302
+msgid "Choose Session"
+msgstr "Seansi valimine"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:446
+msgid "Not listed?"
+msgstr "Pole loendis?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:884
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(nt 'user' või %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:889 js/ui/components/networkAgent.js:243
+#: js/ui/components/networkAgent.js:263 js/ui/components/networkAgent.js:281
+msgid "Username: "
+msgstr "Kasutajanimi: "
+
+#: js/gdm/loginDialog.js:1227
+msgid "Login Window"
+msgstr "Sisselogimisaken"
+
+#: js/gdm/util.js:337
+msgid "Authentication error"
+msgstr "Viga autentimisel"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:477
+msgid "(or swipe finger)"
+msgstr "(või libista näpp üle lugeja)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:88
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Lülita välja"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:91
+msgid "power off;shutdown;reboot;restart"
+msgstr ""
+"power off;shutdown;reboot;restart;lülita välja;taaskäivitamine;"
+"väljalülitamine"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:95
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Lukusta ekraan"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:98
+msgid "lock screen"
+msgstr "lock screen;lukusta ekraan;ekraanilukk;ekraani lukustamine"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:102
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Logi välja"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:105
+msgid "logout;sign off"
+msgstr "logout;sign off;logi välja;väljalogimine"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:109
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Uinak"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:112
+msgid "suspend;sleep"
+msgstr "suspend;sleep;uinak;unerežiim"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Vaheta kasutajat"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "switch user"
+msgstr "switch user;vaheta kasutajat;kasutajavahetus"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:123
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr "Lukusta asend"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:126
+msgid "lock orientation;screen;rotation"
+msgstr ""
+"lock orientation;screen;rotation;asendilukk;lukusta asend;kuvar;pööre;"
+"pööramine"
+
+#: js/misc/util.js:117
+msgid "Command not found"
+msgstr "Käsku ei leitud"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:150
+msgid "Could not parse command:"
+msgstr "Käsku pole võimalik analüüsida:"
+
+#: js/misc/util.js:158
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "„%s“ käivitamine nurjus:"
+
+#: js/misc/util.js:175
+msgid "Just now"
+msgstr "Praegu"
+
+#: js/misc/util.js:177
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minut tagasi"
+msgstr[1] "%d minutit tagasi"
+
+#: js/misc/util.js:180
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d tund tagasi"
+msgstr[1] "%d tundi tagasi"
+
+#: js/misc/util.js:183
+msgid "Yesterday"
+msgstr "Eile"
+
+#: js/misc/util.js:185
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d päev tagasi"
+msgstr[1] "%d päeva tagasi"
+
+#: js/misc/util.js:188
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d nädal tagasi"
+msgstr[1] "%d nädalat tagasi"
+
+#: js/misc/util.js:191
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d kuu tagasi"
+msgstr[1] "%d kuud tagasi"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d aasta tagasi"
+msgstr[1] "%d aastat tagasi"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:223
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:229
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Eile %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:235
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:241
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d. %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:247
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d. %B %Y, %H∶%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:252
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:258
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Eile %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:264
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:270
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d. %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:276
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d. %B %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:40
+msgid "Hotspot Login"
+msgstr "Kuumkoha sisselogimine"
+
+#: js/portalHelper/main.js:86
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Sinu ühendus selle kuumkoha sisselogimisse pole turvaline. Paroolid või muu "
+"info, mille sisestad sellele lehele on vaadatavad lähedalasuvate inimeste "
+"poolt."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:37 js/ui/status/location.js:360
+msgid "Deny Access"
+msgstr "Keela juurdepääs"
+
+#: js/ui/accessDialog.js:38 js/ui/status/location.js:363
+msgid "Grant Access"
+msgstr "Anna ligipääs"
+
+#: js/ui/appDisplay.js:660
+msgid "Frequently used applications will appear here"
+msgstr "Siia ilmuvad tihti kasutatud rakendused"
+
+#: js/ui/appDisplay.js:775
+msgid "Frequent"
+msgstr "Tihti"
+
+#: js/ui/appDisplay.js:782
+msgid "All"
+msgstr "Kõik"
+
+#: js/ui/appDisplay.js:1737 js/ui/panel.js:83
+msgid "New Window"
+msgstr "Uus aken"
+
+#: js/ui/appDisplay.js:1751
+msgid "Launch using Dedicated Graphics Card"
+msgstr "Käivita eraldiseisva graafikakaardiga"
+
+#: js/ui/appDisplay.js:1778 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "Eemalda lemmikutest"
+
+#: js/ui/appDisplay.js:1784
+msgid "Add to Favorites"
+msgstr "Lisa lemmikutesse"
+
+#: js/ui/appDisplay.js:1794 js/ui/panel.js:94
+msgid "Show Details"
+msgstr "Kuva üksikasju"
+
+#: js/ui/appFavorites.js:141
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s lisati lemmikutesse."
+
+#: js/ui/appFavorites.js:175
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s eemaldati lemmikutest."
+
+#: js/ui/audioDeviceSelection.js:42
+msgid "Select Audio Device"
+msgstr "Vali heliseade"
+
+#: js/ui/audioDeviceSelection.js:53
+msgid "Sound Settings"
+msgstr "Helisätted"
+
+#: js/ui/audioDeviceSelection.js:62
+msgid "Headphones"
+msgstr "Kõrvaklapid"
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headset"
+msgstr "Peakomplekt"
+
+#: js/ui/audioDeviceSelection.js:66 js/ui/status/volume.js:247
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:13
+msgid "Change Background…"
+msgstr "Muuda tausta…"
+
+#: js/ui/backgroundMenu.js:15 js/ui/status/nightLight.js:43
+msgid "Display Settings"
+msgstr "Kuvari sätted"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Settings"
+msgstr "Sätted"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:40
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "60"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:69
+msgctxt "grid sunday"
+msgid "S"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:71
+msgctxt "grid monday"
+msgid "M"
+msgstr "E"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:73
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:75
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "K"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:77
+msgctxt "grid thursday"
+msgid "T"
+msgstr "N"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:79
+msgctxt "grid friday"
+msgid "F"
+msgstr "R"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:81
+msgctxt "grid saturday"
+msgid "S"
+msgstr "L"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:332
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:342
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:399
+msgid "Previous month"
+msgstr "Eelmine kuu"
+
+#: js/ui/calendar.js:410
+msgid "Next month"
+msgstr "Järgmine kuu"
+
+#: js/ui/calendar.js:564
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:619
+msgid "Week %V"
+msgstr "%V. nädal"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:687
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Kogu päev"
+
+#: js/ui/calendar.js:819
+msgctxt "calendar heading"
+msgid "%A, %B %-d"
+msgstr "%A, %-d. %B"
+
+#: js/ui/calendar.js:823
+msgctxt "calendar heading"
+msgid "%A, %B %-d, %Y"
+msgstr "%A, %-d. %B %Y"
+
+#: js/ui/calendar.js:1046
+msgid "No Notifications"
+msgstr "Teateid pole"
+
+#: js/ui/calendar.js:1049
+msgid "No Events"
+msgstr "Sündmusi pole"
+
+#: js/ui/calendar.js:1075
+msgid "Clear"
+msgstr "Tühjenda"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "„%s“ ei vasta."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Võid veidi oodata, et see jätkaks tööd, või sundida rakenduse täielikult "
+"sulguma."
+
+#: js/ui/closeDialog.js:59
+msgid "Force Quit"
+msgstr "Sundsulge"
+
+#: js/ui/closeDialog.js:62
+msgid "Wait"
+msgstr "Oota"
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr "Väline ketas ühendati"
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr "Väline ketas eemaldati"
+
+#: js/ui/components/autorunManager.js:334
+#, javascript-format
+msgid "Open with %s"
+msgstr "Ava programmiga %s"
+
+#: js/ui/components/keyring.js:77 js/ui/components/polkitAgent.js:255
+msgid "Password:"
+msgstr "Parool:"
+
+#: js/ui/components/keyring.js:108
+msgid "Type again:"
+msgstr "Sisesta uuesti:"
+
+#: js/ui/components/networkAgent.js:102
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:112 js/ui/status/network.js:219
+#: js/ui/status/network.js:309 js/ui/status/network.js:891
+msgid "Connect"
+msgstr "Ühendu"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:211 js/ui/components/networkAgent.js:223
+#: js/ui/components/networkAgent.js:246 js/ui/components/networkAgent.js:265
+#: js/ui/components/networkAgent.js:285 js/ui/components/networkAgent.js:295
+msgid "Password: "
+msgstr "Parool: "
+
+#. static WEP
+#: js/ui/components/networkAgent.js:216
+msgid "Key: "
+msgstr "Võti: "
+
+#: js/ui/components/networkAgent.js:249 js/ui/components/networkAgent.js:271
+msgid "Private key password: "
+msgstr "Privaatvõtme parool: "
+
+#: js/ui/components/networkAgent.js:269
+msgid "Identity: "
+msgstr "Identiteet: "
+
+#: js/ui/components/networkAgent.js:283
+msgid "Service: "
+msgstr "Teenus: "
+
+#: js/ui/components/networkAgent.js:312 js/ui/components/networkAgent.js:685
+msgid "Authentication required by wireless network"
+msgstr "Juhtmeta võrgu jaoks on vajalik autentimine"
+
+#: js/ui/components/networkAgent.js:313 js/ui/components/networkAgent.js:686
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Juhtmeta võrguga „%s“ ühendumiseks on vajalik parool või krüptimisvõti."
+
+#: js/ui/components/networkAgent.js:317 js/ui/components/networkAgent.js:689
+msgid "Wired 802.1X authentication"
+msgstr "Juhtmega ühenduse 802.1X autentimine"
+
+#: js/ui/components/networkAgent.js:319
+msgid "Network name: "
+msgstr "Võrgu nimi: "
+
+#: js/ui/components/networkAgent.js:324 js/ui/components/networkAgent.js:693
+msgid "DSL authentication"
+msgstr "DSL autentimine"
+
+#: js/ui/components/networkAgent.js:331 js/ui/components/networkAgent.js:699
+msgid "PIN code required"
+msgstr "Vajalik on PIN-kood"
+
+#: js/ui/components/networkAgent.js:332 js/ui/components/networkAgent.js:700
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Mobiiliühenduse loomiseks on vaja PIN-koodi"
+
+#: js/ui/components/networkAgent.js:333
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:340 js/ui/components/networkAgent.js:706
+msgid "Mobile broadband network password"
+msgstr "Mobiiliühenduse võrgu parool"
+
+#: js/ui/components/networkAgent.js:341 js/ui/components/networkAgent.js:690
+#: js/ui/components/networkAgent.js:694 js/ui/components/networkAgent.js:707
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "„%s“-ga ühenduse loomiseks on vaja parooli."
+
+#: js/ui/components/networkAgent.js:674 js/ui/status/network.js:1665
+msgid "Network Manager"
+msgstr "Võrguhaldur"
+
+#: js/ui/components/polkitAgent.js:34
+msgid "Authentication Required"
+msgstr "Vajalik on autentimine"
+
+#: js/ui/components/polkitAgent.js:62
+msgid "Administrator"
+msgstr "Administraator"
+
+#: js/ui/components/polkitAgent.js:139
+msgid "Authenticate"
+msgstr "Autendi"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:241 js/ui/shellMountOperation.js:309
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Kahjuks see ei sobinud. Palun proovi uuesti."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:778
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s nimi on nüüd %s"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:169
+msgid "Windows"
+msgstr "Aknad"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr "Rakenduste kuvamine"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:390
+msgid "Dash"
+msgstr "Dokk"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:62
+msgid "%B %-d %Y"
+msgstr "%-d. %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:69
+msgid "%A %B %e %Y"
+msgstr "%A, %e. %B %Y"
+
+#: js/ui/dateMenu.js:131
+msgid "Add world clocks…"
+msgstr "Lisa maailmakellasid…"
+
+#: js/ui/dateMenu.js:132
+msgid "World Clocks"
+msgstr "Maailmakellad"
+
+#: js/ui/dateMenu.js:222
+msgid "Weather"
+msgstr "Ilm"
+
+#: js/ui/dateMenu.js:305
+msgid "Select a location…"
+msgstr "Vali asukoht…"
+
+#: js/ui/dateMenu.js:313
+msgid "Loading…"
+msgstr "Laadimine…"
+
+#: js/ui/dateMenu.js:323
+msgid "Go online for weather information"
+msgstr "Ilmateate nägemiseks ühendu võrku"
+
+#: js/ui/dateMenu.js:325
+msgid "Weather information is currently unavailable"
+msgstr "Ilmateade pole hetkel saadaval"
+
+#: js/ui/endSessionDialog.js:42
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s väljalogimine"
+
+#: js/ui/endSessionDialog.js:43
+msgctxt "title"
+msgid "Log Out"
+msgstr "Väljalogimine"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s logitakse %d sekundi pärast automaatselt välja."
+msgstr[1] "%s logitakse %d sekundi pärast automaatselt välja."
+
+#: js/ui/endSessionDialog.js:50
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Sind logitakse %d sekundi pärast automaatselt välja."
+msgstr[1] "Sind logitakse %d sekundi pärast automaatselt välja."
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "Logi välja"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "Väljalülitamine"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Paigalda uuendused ja lülita välja"
+
+#: js/ui/endSessionDialog.js:65
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "%d sekundi pärast lülitub süsteem automaatselt välja."
+msgstr[1] "%d sekundi pärast lülitub süsteem automaatselt välja."
+
+#: js/ui/endSessionDialog.js:69
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Paigalda ootel tarkvarauuendused"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:89
+msgctxt "button"
+msgid "Restart"
+msgstr "Taaskäivita"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "Lülita välja"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "Taaskäivitamine"
+
+#: js/ui/endSessionDialog.js:83
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Süsteem taaskäivitub automaatselt %d sekundi pärast."
+msgstr[1] "Süsteem taaskäivitub automaatselt %d sekundi pärast."
+
+#: js/ui/endSessionDialog.js:97
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Taaskäivitamine ja uuenduste paigaldamine"
+
+#: js/ui/endSessionDialog.js:99
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "Süsteem taaskäivitub ja paigaldab uuendused %d sekundi pärast."
+msgstr[1] "Süsteem taaskäivitub ja paigaldab uuendused %d sekundi pärast."
+
+#: js/ui/endSessionDialog.js:105 js/ui/endSessionDialog.js:125
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Taaskäivita ja paigalda"
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Paigalda ja lülita välja"
+
+#: js/ui/endSessionDialog.js:107
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Lülita välja pärast uuenduste paigaldamist"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Taaskäivita ja paigalda täiendus"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:120
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s paigaldatakse pärast taaskäivitust. Täienduse paigaldamine võib võtta "
+"kaua aega: veendu, et su andmed on varundatud ning arvuti on vooluvõrku "
+"ühendatud."
+
+#: js/ui/endSessionDialog.js:309
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"Töötad aku peal: palun ühenda arvuti vooluvõrku enne uuenduste paigaldamist."
+
+#: js/ui/endSessionDialog.js:326
+msgid "Some applications are busy or have unsaved work."
+msgstr "Mõned rakendused on tegevuses või on neis salvestamata asju."
+
+#: js/ui/endSessionDialog.js:333
+msgid "Other users are logged in."
+msgstr "Teised kasutajad on sisse logitud."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:614
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (kaugühendus)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:617
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsool)"
+
+#: js/ui/extensionDownloader.js:192
+msgid "Install"
+msgstr "Paigalda"
+
+#: js/ui/extensionDownloader.js:197
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Kas laadida alla ja paigaldada „%s“ aadressilt extensions.gnome.org?"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:50
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr "%s soovib kiirklahve üle võtta"
+
+#: js/ui/inhibitShortcutsDialog.js:51
+msgid "Application wants to inhibit shortcuts"
+msgstr "Rakendus soovib kiirklahve üle võtta"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:60
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Kiirklahve saad taastada, vajutades %s."
+
+#: js/ui/inhibitShortcutsDialog.js:65
+msgid "Deny"
+msgstr "Keela"
+
+#: js/ui/inhibitShortcutsDialog.js:71
+msgid "Allow"
+msgstr "Luba"
+
+#: js/ui/kbdA11yDialog.js:31
+msgid "Slow Keys Turned On"
+msgstr "Aeglased klahvid sisselülitatud"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned Off"
+msgstr "Aeglased klahvid väljalülitatud"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Sa hoidsid tõstuklahvi all 8 sekundit. See on otsetee aeglaste klahvide "
+"funktsioonile, mis muudab sinu klaviatuuri tööviisi."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Kleepuvad klahvid sisselülitatud"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Kleepuvad klahvid väljalülitatud"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Sa vajutasid tõstuklahvi 5 korda järjest. See on otsetee kleepuvate klahvide "
+"funktsioonile, mis muudab sinu klaviatuuri tööviisi."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Sa vajutasid kahte klahvi korraga või vajutasid tõstuklahvi 5 korda järjest. "
+"See lülitab välja kleepuvate klahvide funktsiooni, mis muudab sinu "
+"klaviatuuri tööviisi."
+
+#: js/ui/kbdA11yDialog.js:57
+msgid "Leave On"
+msgstr "Jäta sisse"
+
+#: js/ui/kbdA11yDialog.js:57 js/ui/status/bluetooth.js:133
+#: js/ui/status/network.js:1264
+msgid "Turn On"
+msgstr "Lülita sisse"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:133
+#: js/ui/status/network.js:128 js/ui/status/network.js:310
+#: js/ui/status/network.js:1264 js/ui/status/network.js:1376
+#: js/ui/status/nightLight.js:39 js/ui/status/rfkill.js:79
+#: js/ui/status/rfkill.js:106
+msgid "Turn Off"
+msgstr "Lülita välja"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Jäta välja"
+
+#: js/ui/keyboard.js:203
+msgid "Region & Language Settings"
+msgstr "Asukoha ja keele sätted"
+
+#: js/ui/lookingGlass.js:615
+msgid "No extensions installed"
+msgstr "Ühtegi laiendust pole paigaldatud"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:669
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ei ole väljastanud ühtegi veateadet."
+
+#: js/ui/lookingGlass.js:675
+msgid "Hide Errors"
+msgstr "Peida vead"
+
+#: js/ui/lookingGlass.js:679 js/ui/lookingGlass.js:739
+msgid "Show Errors"
+msgstr "Näita vigu"
+
+#: js/ui/lookingGlass.js:688
+msgid "Enabled"
+msgstr "Lubatud"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:691 subprojects/gvc/gvc-mixer-control.c:1864
+msgid "Disabled"
+msgstr "Keelatud"
+
+#: js/ui/lookingGlass.js:693
+msgid "Error"
+msgstr "Viga"
+
+#: js/ui/lookingGlass.js:695
+msgid "Out of date"
+msgstr "Pole värske"
+
+#: js/ui/lookingGlass.js:697
+msgid "Downloading"
+msgstr "Allalaadimine"
+
+#: js/ui/lookingGlass.js:721
+msgid "View Source"
+msgstr "Kuva lähtekoodi"
+
+#: js/ui/lookingGlass.js:730
+msgid "Web Page"
+msgstr "Veebileht"
+
+#: js/ui/messageTray.js:1474
+msgid "System Information"
+msgstr "Süsteemi andmed"
+
+#: js/ui/mpris.js:177
+msgid "Unknown artist"
+msgstr "Tundmatu esitaja"
+
+#: js/ui/mpris.js:178
+msgid "Unknown title"
+msgstr "Tundmatu pealkiri"
+
+#: js/ui/osdWindow.js:22 js/ui/status/volume.js:199
+msgid "Volume"
+msgstr "Helivaljus"
+
+#: js/ui/overview.js:73
+msgid "Undo"
+msgstr "Võta tagasi"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:100
+msgid "Overview"
+msgstr "Ülevaade"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:226
+msgid "Type to search…"
+msgstr "Otsing…"
+
+#: js/ui/padOsd.js:92
+msgid "New shortcut…"
+msgstr "Uus kiirklahv…"
+
+#: js/ui/padOsd.js:141
+msgid "Application defined"
+msgstr "Rakenduse määratud"
+
+#: js/ui/padOsd.js:142
+msgid "Show on-screen help"
+msgstr "Kuva ekraaniabi"
+
+#: js/ui/padOsd.js:143
+msgid "Switch monitor"
+msgstr "Vaheta ekraani"
+
+#: js/ui/padOsd.js:144
+msgid "Assign keystroke"
+msgstr "Omista klahvivajutus"
+
+#: js/ui/padOsd.js:209
+msgid "Done"
+msgstr "Valmis"
+
+#: js/ui/padOsd.js:721
+msgid "Edit…"
+msgstr "Muuda…"
+
+#: js/ui/padOsd.js:763 js/ui/padOsd.js:868
+msgid "None"
+msgstr "Puudub"
+
+#: js/ui/padOsd.js:822
+msgid "Press a button to configure"
+msgstr "Vajuta seadistamiseks klahvi"
+
+#: js/ui/padOsd.js:823
+msgid "Press Esc to exit"
+msgstr "Väljumiseks vajuta Esc"
+
+#: js/ui/padOsd.js:826
+msgid "Press any key to exit"
+msgstr "Väljumiseks vajuta mistahes klahvi"
+
+#: js/ui/panel.js:108
+msgid "Quit"
+msgstr "Sulge"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:466
+msgid "Activities"
+msgstr "Tegevused"
+
+#: js/ui/panel.js:741
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Süsteem"
+
+#: js/ui/panel.js:861
+msgid "Top Bar"
+msgstr "Ülemine riba"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:285
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: js/ui/runDialog.js:64
+msgid "Enter a Command"
+msgstr "Palun sisesta käsk"
+
+#: js/ui/runDialog.js:104 js/ui/windowMenu.js:166
+msgid "Close"
+msgstr "Sulge"
+
+#: js/ui/runDialog.js:266
+msgid "Restart is not available on Wayland"
+msgstr "Taaskäivitus ei ole Waylandis saadaval"
+
+#: js/ui/runDialog.js:271
+msgid "Restarting…"
+msgstr "Taaskäivitamine…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:77
+msgid "%A, %B %d"
+msgstr "%A, %d. %B"
+
+#: js/ui/screenShield.js:133
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d uus sõnum"
+msgstr[1] "%d uut sõnumit"
+
+#: js/ui/screenShield.js:135
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d uus teade"
+msgstr[1] "%d uut teadet"
+
+#: js/ui/screenShield.js:449 js/ui/status/system.js:270
+msgid "Lock"
+msgstr "Lukusta"
+
+#: js/ui/screenShield.js:713
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME peab ekraani lukustama"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:834 js/ui/screenShield.js:1307
+msgid "Unable to lock"
+msgstr "Pole võimalik lukustada"
+
+#: js/ui/screenShield.js:835 js/ui/screenShield.js:1308
+msgid "Lock was blocked by an application"
+msgstr "Rakendus blokeeris lukustamise"
+
+#: js/ui/search.js:635
+msgid "Searching…"
+msgstr "Otsing…"
+
+#: js/ui/search.js:637
+msgid "No results."
+msgstr "Tulemused puuduvad."
+
+#: js/ui/search.js:761
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d veel"
+msgstr[1] "%d veel"
+
+#: js/ui/shellEntry.js:19
+msgid "Copy"
+msgstr "Kopeeri"
+
+#: js/ui/shellEntry.js:24
+msgid "Paste"
+msgstr "Aseta"
+
+#: js/ui/shellEntry.js:90
+msgid "Show Text"
+msgstr "Teksti näidatakse"
+
+#: js/ui/shellEntry.js:92
+msgid "Hide Text"
+msgstr "Tekst on peidetud"
+
+#: js/ui/shellMountOperation.js:296
+msgid "Password"
+msgstr "Parool"
+
+#: js/ui/shellMountOperation.js:317
+msgid "Remember Password"
+msgstr "Parooli meelespidamine"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Kasutaja abivahendid"
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr "Suurendus"
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr "Ekraanilugeja"
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr "Ekraaniklaviatuur"
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr "Visuaalsed märguanded"
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr "Kleepuvad klahvid"
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr "Aeglased klahvid"
+
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr "Põrkeklahvid"
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr "Hiireklahvid"
+
+#: js/ui/status/accessibility.js:151
+msgid "High Contrast"
+msgstr "Kõrgkontrastne"
+
+#: js/ui/status/accessibility.js:182
+msgid "Large Text"
+msgstr "Suur tekst"
+
+#: js/ui/status/bluetooth.js:38
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:47 js/ui/status/network.js:585
+msgid "Bluetooth Settings"
+msgstr "Bluetoothi sätted"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:127
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d ühendatud"
+msgstr[1] "%d ühendatud"
+
+#: js/ui/status/bluetooth.js:129
+msgid "Off"
+msgstr "Väljas"
+
+#: js/ui/status/bluetooth.js:131
+msgid "On"
+msgstr "Sees"
+
+#: js/ui/status/brightness.js:36
+msgid "Brightness"
+msgstr "Heledus"
+
+#: js/ui/status/keyboard.js:812
+msgid "Keyboard"
+msgstr "Klaviatuur"
+
+#: js/ui/status/keyboard.js:834
+msgid "Show Keyboard Layout"
+msgstr "Klaviatuuripaigutuse kuvamine"
+
+#: js/ui/status/location.js:64 js/ui/status/location.js:172
+msgid "Location Enabled"
+msgstr "Asukoha jagamine lubatud"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:173
+msgid "Disable"
+msgstr "Keela"
+
+#: js/ui/status/location.js:66
+msgid "Privacy Settings"
+msgstr "Privaatsussätted"
+
+#: js/ui/status/location.js:171
+msgid "Location In Use"
+msgstr "Asukoha jagamine sees"
+
+#: js/ui/status/location.js:175
+msgid "Location Disabled"
+msgstr "Asukoha jagamine väljas"
+
+#: js/ui/status/location.js:176
+msgid "Enable"
+msgstr "Luba"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:353
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "Kas jagad rakendusele %s juurdepääsu oma asukohale?"
+
+#: js/ui/status/location.js:354
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Asukoha ligipääse saab igal ajal privaatsussätetest muuta."
+
+#: js/ui/status/network.js:66
+msgid "<unknown>"
+msgstr "<tundmatu>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:416 js/ui/status/network.js:1293
+#, javascript-format
+msgid "%s Off"
+msgstr "%s väljas"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:419
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s ühendatud"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:424
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s pole hallatud"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:427
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s ühenduse katkestamine"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:434 js/ui/status/network.js:1285
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s ühendamine"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:437
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s vajab autentimist"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:445
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "%s püsivara puudub"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:449
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s pole saadaval"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:452
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s ühendus nurjus"
+
+#: js/ui/status/network.js:464
+msgid "Wired Settings"
+msgstr "Juhtmeühenduse sätted"
+
+#: js/ui/status/network.js:506
+msgid "Mobile Broadband Settings"
+msgstr "Mobiiliühenduse sätted"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:553 js/ui/status/network.js:1290
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s riistvaraliselt keelatud"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:557
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s keelatud"
+
+#: js/ui/status/network.js:597
+msgid "Connect to Internet"
+msgstr "Ühendu Internetiga"
+
+#: js/ui/status/network.js:786
+msgid "Airplane Mode is On"
+msgstr "Lennurežiim sees"
+
+#: js/ui/status/network.js:787
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi on keelatud, kui lennurežiim on sees."
+
+#: js/ui/status/network.js:788
+msgid "Turn Off Airplane Mode"
+msgstr "Lülita lennurežiim välja"
+
+#: js/ui/status/network.js:797
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi väljas"
+
+#: js/ui/status/network.js:798
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Wi-Fi tuleb sisse lülitada, et võrku ühenduda."
+
+#: js/ui/status/network.js:799
+msgid "Turn On Wi-Fi"
+msgstr "Lülita Wi-Fi sisse"
+
+#: js/ui/status/network.js:824
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi võrgud"
+
+#: js/ui/status/network.js:826
+msgid "Select a network"
+msgstr "Vali võrk"
+
+#: js/ui/status/network.js:855
+msgid "No Networks"
+msgstr "Võrke pole"
+
+#: js/ui/status/network.js:876 js/ui/status/rfkill.js:104
+msgid "Use hardware switch to turn off"
+msgstr "Kasuta riistvaralist lülitit väljalülitamiseks"
+
+#: js/ui/status/network.js:1152
+msgid "Select Network"
+msgstr "Võrgu valimine"
+
+#: js/ui/status/network.js:1158
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi sätted"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1281
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s kuumkoht sees"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1296
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s pole ühendatud"
+
+#: js/ui/status/network.js:1393
+msgid "connecting…"
+msgstr "ühendumine…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1396
+msgid "authentication required"
+msgstr "vajalik on autentimine"
+
+#: js/ui/status/network.js:1398
+msgid "connection failed"
+msgstr "ühendumine nurjus"
+
+#: js/ui/status/network.js:1448
+msgid "VPN Settings"
+msgstr "VPN-i sätted"
+
+#: js/ui/status/network.js:1465
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1475
+msgid "VPN Off"
+msgstr "VPN väljas"
+
+#: js/ui/status/network.js:1536 js/ui/status/rfkill.js:82
+msgid "Network Settings"
+msgstr "Võrgusätted"
+
+#: js/ui/status/network.js:1565
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s juhtmega ühendus"
+msgstr[1] "%s juhtmega ühendust"
+
+#: js/ui/status/network.js:1569
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s Wi-Fi ühendus"
+msgstr[1] "%s Wi-Fi ühendust"
+
+#: js/ui/status/network.js:1573
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s modemi ühendus"
+msgstr[1] "%s modemi ühendust"
+
+#: js/ui/status/network.js:1702
+msgid "Connection failed"
+msgstr "Ühendus nurjus"
+
+#: js/ui/status/network.js:1703
+msgid "Activation of network connection failed"
+msgstr "Võrguühenduse aktiveerimine nurjus"
+
+#: js/ui/status/nightLight.js:60
+msgid "Night Light Disabled"
+msgstr "Öövalgustus keelatud"
+
+#: js/ui/status/nightLight.js:61
+msgid "Night Light On"
+msgstr "Öövalgustus sees"
+
+#: js/ui/status/nightLight.js:62
+msgid "Resume"
+msgstr "Jätka"
+
+#: js/ui/status/nightLight.js:63
+msgid "Disable Until Tomorrow"
+msgstr "Keela homseni"
+
+#: js/ui/status/power.js:45
+msgid "Power Settings"
+msgstr "Toitesätted"
+
+#: js/ui/status/power.js:61
+msgid "Fully Charged"
+msgstr "Täiesti täis"
+
+#: js/ui/status/power.js:67
+msgid "Not Charging"
+msgstr "Ei lae"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:70 js/ui/status/power.js:76
+msgid "Estimating…"
+msgstr "Andmete kogumine…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:84
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "Jäänud %d∶%02d (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:89
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "Täitumiseni %d∶%02d (%d %%)"
+
+#: js/ui/status/power.js:117 js/ui/status/power.js:119
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:42
+msgid "Screen is Being Shared"
+msgstr "Ekraani jagatakse"
+
+#: js/ui/status/remoteAccess.js:44
+msgid "Turn off"
+msgstr "Lülita välja"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:77
+msgid "Airplane Mode On"
+msgstr "Lennurežiim"
+
+#: js/ui/status/system.js:203
+msgid "Switch User"
+msgstr "Vaheta kasutajat"
+
+#: js/ui/status/system.js:215
+msgid "Log Out"
+msgstr "Logi välja"
+
+#: js/ui/status/system.js:227
+msgid "Account Settings"
+msgstr "Kasutaja sätted"
+
+#: js/ui/status/system.js:255
+msgid "Orientation Lock"
+msgstr "Asendilukk"
+
+#: js/ui/status/system.js:281
+msgid "Suspend"
+msgstr "Uinak"
+
+#: js/ui/status/system.js:291
+msgid "Power Off"
+msgstr "Lülita välja"
+
+#: js/ui/status/thunderbolt.js:261
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid "Unknown Thunderbolt device"
+msgstr "Tundmatu Thunderbolt-seade"
+
+#: js/ui/status/thunderbolt.js:323
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Sinu äraolekul tuvastati uus seade. Palun ühenda see seade lahti ja tagasi, "
+"et selle kasutamist alustada."
+
+#: js/ui/status/thunderbolt.js:326
+msgid "Unauthorized Thunderbolt device"
+msgstr "Autoriseerimata Thunderbolt-seade"
+
+#: js/ui/status/thunderbolt.js:327
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:333
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolti autoriseerimise viga"
+
+#: js/ui/status/thunderbolt.js:334
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Thunderbolti seadet ei saanud autoriseerida: %s"
+
+#: js/ui/status/volume.js:133
+msgid "Volume changed"
+msgstr "Helivaljus muutus"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:16
+msgid "Mirror"
+msgstr "Peegelda"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:21
+msgid "Join Displays"
+msgstr "Ühenda kuvarid"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:26
+msgid "External Only"
+msgstr "Ainult väline"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:31
+msgid "Built-in Only"
+msgstr "Ainult sisemine"
+
+#: js/ui/unlockDialog.js:49
+msgid "Log in as another user"
+msgstr "Logi sisse teise kasutajana"
+
+#: js/ui/unlockDialog.js:66
+msgid "Unlock Window"
+msgstr "Võta aken lukust lahti"
+
+#: js/ui/viewSelector.js:173
+msgid "Applications"
+msgstr "Rakendused"
+
+#: js/ui/viewSelector.js:177
+msgid "Search"
+msgstr "Otsing"
+
+#: js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "„%s“ on valmis"
+
+#: js/ui/windowManager.js:53
+msgid "Do you want to keep these display settings?"
+msgstr "Kas tahad säilitada need kuvasätted?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:65
+msgid "Revert Settings"
+msgstr "Taasta sätted"
+
+#: js/ui/windowManager.js:68
+msgid "Keep Changes"
+msgstr "Säilita muudatused"
+
+#: js/ui/windowManager.js:86
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Sätete muudatused ennistatakse %d sekundi pärast"
+msgstr[1] "Sätete muudatused ennistatakse %d sekundi pärast"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:662
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:26
+msgid "Minimize"
+msgstr "Minimeeri"
+
+#: js/ui/windowMenu.js:33
+msgid "Unmaximize"
+msgstr "Tühista maksimeerimine"
+
+#: js/ui/windowMenu.js:37
+msgid "Maximize"
+msgstr "Maksimeeri"
+
+#: js/ui/windowMenu.js:44
+msgid "Move"
+msgstr "Liiguta"
+
+#: js/ui/windowMenu.js:50
+msgid "Resize"
+msgstr "Muuda suurust"
+
+#: js/ui/windowMenu.js:57
+msgid "Move Titlebar Onscreen"
+msgstr "Liiguta tiitliriba ekraanile"
+
+#: js/ui/windowMenu.js:62
+msgid "Always on Top"
+msgstr "Alati pealmine"
+
+#: js/ui/windowMenu.js:81
+msgid "Always on Visible Workspace"
+msgstr "Alati kuvataval töölaual"
+
+#: js/ui/windowMenu.js:95
+msgid "Move to Workspace Left"
+msgstr "Liiguta vasakpoolsele töölauale"
+
+#: js/ui/windowMenu.js:101
+msgid "Move to Workspace Right"
+msgstr "Liiguta parempoolsele töölauale"
+
+#: js/ui/windowMenu.js:107
+msgid "Move to Workspace Up"
+msgstr "Liiguta ülemisele töölauale"
+
+#: js/ui/windowMenu.js:113
+msgid "Move to Workspace Down"
+msgstr "Liiguta alumisele töölauale"
+
+#: js/ui/windowMenu.js:131
+msgid "Move to Monitor Up"
+msgstr "Liiguta ülemisele kuvarile"
+
+#: js/ui/windowMenu.js:140
+msgid "Move to Monitor Down"
+msgstr "Liiguta alumisele kuvarile"
+
+#: js/ui/windowMenu.js:149
+msgid "Move to Monitor Left"
+msgstr "Liiguta vasakpoolsele kuvarile"
+
+#: js/ui/windowMenu.js:158
+msgid "Move to Monitor Right"
+msgstr "Liiguta parempoolsele kuvarile"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolutioni kalender"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: src/main.c:408
+msgid "Print version"
+msgstr "Versiooniteabe kuvamine"
+
+#: src/main.c:414
+msgid "Mode used by GDM for login screen"
+msgstr "Sisselogimise kuval kasutatav GDM-i režiim"
+
+#: src/main.c:420
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Erirežiimi kasutamine, nt sisselogimisakna jaoks „gdm“"
+
+#: src/main.c:426
+msgid "List possible modes"
+msgstr "Võimalike režiimide loetelu"
+
+#: src/shell-app.c:260
+msgctxt "program"
+msgid "Unknown"
+msgstr "Tundmatu"
+
+#: src/shell-app.c:511
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "„%s“ käivitamine nurjus"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "Paroolid ei kattu."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "Parool ei saa olla tühi"
+
+#: src/shell-polkit-authentication-agent.c:348
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Kasutaja katkestas autentimisdialoogi"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1871
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u väljund"
+msgstr[1] "%u väljundit"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1881
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u sisend"
+msgstr[1] "%u sisendit"
+
+#: subprojects/gvc/gvc-mixer-control.c:2736
+msgid "System Sounds"
+msgstr "Süsteemi helid"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "%s jaoks eelistuste dialoogi laadimisel esines viga:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s terve päeva."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, hiljem %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, siis %s, hiljem %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Tundub nagu %s."
+
+#~ msgid "Show the message tray"
+#~ msgstr "Teateala näitamine"
+
+#~ msgid "GNOME Shell Extension Preferences"
+#~ msgstr "GNOME Shelli laienduste eelistused"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (waylandi komposiitor)"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Kategooriad, mida tuleb kuvada kaustadena"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Kõiki selles loetelus olevaid kategooriaid kuvatakse rakenduste vaates "
+#~ "kaustadena, mitte peavaates teistega koos."
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Kalendris näidatakse kuupäeva nädalavormingus"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Kui määratud, kuvatakse kalendris kuupäeva ISO nädalavormingus."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Nuppude järjekord tiitliribal"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "See võti on ülimuslik võtme org.gnome.desktop.wm.preferences suhtes, kui "
+#~ "kasutatakse GNOME Shelli."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Vali seadistatav laiendus kasutades ülemist valikukasti."
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Ühtegi sündmust pole plaanitud"
+
+#~ msgid "Today"
+#~ msgstr "Täna"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Homme"
+
+#~ msgid "This week"
+#~ msgstr "Käesolev nädal"
+
+#~ msgid "Next week"
+#~ msgstr "Järgmine nädal"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Eemaldatavad seadmed"
+
+#~ msgid "Eject"
+#~ msgstr "Väljasta"
+
+#~ msgid "Invitation"
+#~ msgstr "Kutse"
+
+#~ msgid "Call"
+#~ msgstr "Helista"
+
+#~ msgid "File Transfer"
+#~ msgstr "Failiülekanne"
+
+#~ msgid "Chat"
+#~ msgstr "Vestlus"
+
+#~ msgid "Unmute"
+#~ msgstr "Heli peale"
+
+#~ msgid "Mute"
+#~ msgstr "Heli maha"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d. %B</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d. %B %Y</b>, <b>%H:%M</b>"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Kutse: %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s kutsub sind liituma: %s"
+
+#~ msgid "Decline"
+#~ msgstr "Keeldu"
+
+#~ msgid "Accept"
+#~ msgstr "Nõustu"
+
+#~ msgid "Video call from %s"
+#~ msgstr "%s tahab alustada videokõnet"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s helistab"
+
+#~ msgid "Answer"
+#~ msgstr "Vasta"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s saadab sulle %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s palub sinu luba, et näha, kui sa oled võrgus"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Autentimine nurjus"
+
+#~ msgid "Encryption error"
+#~ msgstr "Krüptimise viga"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Sertifikaati pole"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Sertifikaat pole usaldusväärne"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Sertifikaat on aegunud"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Sertifikaat pole aktiivne"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Sertifikaadi hostinimi ei klapi"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Sertifikaadi sõrmejälg ei klapi"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Sertifikaat on ise-allkirjastatud"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Oled nüüd ühenduseta olekus"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Sertifikaat on vigane"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Ühendusest keelduti"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Ühendust pole võimalik luua"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Ühendus katkes"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Selle kontoga on juba serverisse ühendutud"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Ühendus on asendatud sama kontot kasutades uue ühendusega"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "See konto on serveris juba olemas"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Server on praegu ühendusega toimetulekuks liiga hõivatud"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Sertifikaat on tühistatud"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Sertifikaat kasutab ebaturvalist šifrialgoritmini ning on nõrgalt "
+#~ "krüptitud"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Serveri sertifikaadi pikkus ehk sertifikaatide keti sügavus ületab "
+#~ "krüptograafiateegi määratud piiri"
+
+#~ msgid "Internal error"
+#~ msgstr "Sisemine viga"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "Pole võimalik ühenduda võrguga %s"
+
+#~ msgid "View account"
+#~ msgstr "Konto kuvamine"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Ava kalender"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Kuupäeva ja kella sätted"
+
+#~ msgid "Open"
+#~ msgstr "Ava"
+
+#~ msgid "Remove"
+#~ msgstr "Eemalda"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Kustuta teated"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Märguannete sätted"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Paneelimenüü"
+
+#~ msgid "No Messages"
+#~ msgstr "Teateid pole"
+
+#~ msgid "Message Tray"
+#~ msgstr "Teateala"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d ühendatud seade"
+#~ msgstr[1] "%d ühendatud seadet"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Aku"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Autoriseerimise päring seadmelt %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Seade '%s' tahab selle arvutiga paarduda"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Seade %s soovib ligipääsu teenusele '%s'"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Luba ainult seekord"
+
+#~ msgid "Reject"
+#~ msgstr "Lükka tagasi"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Paardumise kinnitus seadmele %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "Palun kontrolli, kas parool '%06d' kattub seadme parooliga."
+
+#~ msgid "Matches"
+#~ msgstr "Kattub"
+
+#~ msgid "Does not match"
+#~ msgstr "Ei kattu"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Seadmega %s paardumise päring"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Palun sisesta seadme poolt öeldav PIN-kood."
+
+#~ msgid "OK"
+#~ msgstr "Olgu"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Vabandust, tänaseks tarkuseteri pole:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "Oraakel %s ütleb"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Settings Menu"
+#~ msgstr "Sätete menüü"
+
+#~ msgid "Screenshots"
+#~ msgstr "Ekraanipildid"
+
+#~ msgid "Record a screencast"
+#~ msgstr "Ekraanivideo salvestamine"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Kas rakenduste kasutuse kohta kogutakse andmeid"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "Shell tavaliselt seirab aktiivseid rakendusi, et näidata enamkasutatavaid "
+#~ "(näiteks käivitajaid). Kuigi neid andmeid hoitakse privaatselt, võid "
+#~ "privaatsuse suurendamiseks selle keelata. Selle keelamine siiski ei "
+#~ "eemalda juba salvestatud andmeid."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Kasutatakse kasutaja poolt viimati vahetult määratud kiirsuhtluse oleku "
+#~ "salvestamiseks. See väärtus pärineb TpConnectionPresenceType nummerdusest."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Kasutatakse kasutaja poolt viimati vahetult määratud kiirsuhtluse oleku "
+#~ "salvestamiseks. See väärtus pärineb GsmPresenceStatus nummerdusest."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Erkaanilindistaja lülitamise kiirklahv"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Kiirklahv erkaanilindistaja käivitamiseks/peatamiseks."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Ekraanivideo lindistamisel kasutatav kaadrikiirus."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "GNOME Shelli lindistatava ekraanivideo kaadrisagedus (kaadrit sekundis)."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "GStreameri toru, mida ekraanivideo kodeerimiseks kasutatakse"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Määrab GStreameri toru, mida kasutatakse lindistuste kodeerimiseks. See "
+#~ "peab vastama gst-launch'i süntaksile. Torul peaks olema vaba plokk (sink "
+#~ "pad), kuhu lindistatav video salvestatakse. Tavaliselt on vaba plokk "
+#~ "olemas; selle ploki väljund kirjutatakse väljundfaili. Toru võib "
+#~ "hoolitseda ka enda väljundi eest - seda võib kasutada väljundi saatmiseks "
+#~ "icecast serverisse shout2send või sarnase tehnoloogia abil. Kui see on "
+#~ "määramata või väärtus on tühi, kasutatakse vaikimisi toru. See on praegu "
+#~ "'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+#~ "threads=%T ! queue ! webmmux' ning lindistab WEBM vormingusse VP8 "
+#~ "koodekiga. %T asendatakse süsteemi oletatava optimaalseima lõimede "
+#~ "(thread) arvuga."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Ekraanivideo salvestamisel kasutatav faililaiend"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "Ekraanivideo jaoks kasutatav failinimi on unikaalne, see sisaldab "
+#~ "salvestamise kuupäeva ja ka käesoleva võtmega määratud laiendit. Mõnda "
+#~ "teise konteinervormingusse salvestades tuleks ka sellele vormingule "
+#~ "vastav laiend määrata."
+
+#~ msgid "Session…"
+#~ msgstr "Seanss…"
+
+#~ msgid "Power"
+#~ msgstr "Vool"
+
+#~ msgid "Restart"
+#~ msgstr "Taaskäivita"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "Ekraanivideo %d %t"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Nende rakenduste sulgemiseks ja süsteemist väljalogimiseks klõpsa nupule "
+#~ "Logi välja."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Süsteemist väljalogimine"
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Nende rakenduste sulgemiseks ja süsteemi väljalülitamiseks klõpsa nupule "
+#~ "Lülita välja."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Süsteemi väljalülitamine."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Nende rakenduste sulgemiseks ja süsteemi taaskäivitamiseks klõpsa "
+#~ "\"Taaskäivita\"."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Süsteemi taaskäivitamine."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Universaalse ligipääsu sätted"
+
+#~ msgid "Visibility"
+#~ msgstr "Nähtavus"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Uue seadme häälestamine…"
+
+#~ msgid "Send Files…"
+#~ msgstr "Failide saatmine…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Klaviatuurisätted"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Hiiresätted"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Helivaljus, võrk, aku"
+
+#~ msgid "disabled"
+#~ msgstr "keelatud"
+
+#~ msgid "cable unplugged"
+#~ msgstr "juhe eemaldatud"
+
+#~ msgid "More…"
+#~ msgstr "Veel…"
+
+#~ msgid "Wired"
+#~ msgstr "Juhtmega"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Automaatne ethernet"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Automaatne lairibaühendus"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Automaatne sissehelistamine"
+
+#~ msgid "Auto %s"
+#~ msgstr "Automaatne %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Automaatne bluetooth"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Automaatne juhtmeta ühendus"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "jäänud %d %s ja %d %s"
+
+#~ msgid "AC Adapter"
+#~ msgstr "Laadija"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Sülearvuti aku"
+
+#~ msgid "Mouse"
+#~ msgstr "Hiir"
+
+#~ msgid "PDA"
+#~ msgstr "Elektronmärkmik"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Mobiiltelefon"
+
+#~ msgid "Media Player"
+#~ msgstr "Meediaesitaja"
+
+#~ msgid "Tablet"
+#~ msgstr "Graafikalaud"
+
+#~ msgid "Computer"
+#~ msgstr "Arvuti"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Tundmatu"
+
+#~ msgid "Available"
+#~ msgstr "Saadaval"
+
+#~ msgid "Busy"
+#~ msgstr "Hõivatud"
+
+#~ msgid "Invisible"
+#~ msgstr "Nähtamatu"
+
+#~ msgid "Away"
+#~ msgstr "Eemal"
+
+#~ msgid "Idle"
+#~ msgstr "Jõude"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Sinu vestluse olekuks määratakse hõivatud"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Märguanded on nüüd keelatud, sealhulgas vestlusteated. Sinu netiolekut "
+#~ "muudeti, et teised teaksid, et sa ei pruugi nende teateid näha."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Väljalülitamine võib põhjustada salvestamata töö kaotsimineku."
+
+#~ msgid "Show full name in the user menu"
+#~ msgstr "Kasutajamenüüs näidatakse kasutaja tervet nime"
+
+#~ msgid "Whether the users full name is shown in the user menu or not."
+#~ msgstr "Kas kasutajamenüüs näidatakse kasutaja kogu nime või mitte."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Sisselogimine"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "RAKENDUSED"
+
+#~ msgid "SETTINGS"
+#~ msgstr "SEADED"
+
+#~ msgid "tray"
+#~ msgstr "salv"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Sinu lemmiküllatusmuna"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Suurbritannia"
+
+#~ msgid "Default"
+#~ msgstr "Vaikimisi"
+
+#~ msgid "Subscription request"
+#~ msgstr "Tellimuse päring"
+
+#~ msgid "<b>%H:%M</b> on Yesterday"
+#~ msgstr "Eile <b>%H:%M</b>"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Ühendus %s nurjus"
+
+#~ msgid "Reconnect"
+#~ msgstr "Ühendu uuesti"
+
+#~ msgid "Wireless"
+#~ msgstr "Juhtmeta"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "keelatud OpenSearch pakkujad"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Kellaaega näidatakse sekunditega"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Kui määratud, siis kuvatakse aja juures ka sekundeid."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Kell näitab kuupäeva"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Kui määratud, siis kuvatakse kellaaja kõrval ka kuupäeva."
+
+#~ msgid "CONTACTS"
+#~ msgstr "KONTAKTID"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "'%s' lahtihaakimine nurjus"
+
+#~ msgid "Retry"
+#~ msgstr "Proovi uuesti"
+
+#~ msgid "Connect to..."
+#~ msgstr "Ühendumine..."
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ASUKOHAD JA SEADMED"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Vale parool, palun proovi uuesti"
+
+#~ msgid "%s is busy."
+#~ msgstr "%s on hõivatud."
+
+#~ msgid "Hidden"
+#~ msgstr "Nähtamatu"
+
+#~ msgid "Power Off..."
+#~ msgstr "Lülita välja..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "Netikontod"
diff --git a/po/eu.po b/po/eu.po
new file mode 100644
index 0000000..bc598c7
--- /dev/null
+++ b/po/eu.po
@@ -0,0 +1,3197 @@
+# Basque translation for gnome-shell.
+# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Julen Ruiz Aizpuru <julenx@gmail.com>, 2011.
+# Iñaki Larrañaga Murgoitio <dooteo@zundan.com>, 2011, 2012, 2013, 2014, 2015, 2016, 2017.
+# Ibai Oihanguren Sala <ibai@oihanguren.com>, 2020.
+# Asier Sarasua Garmendia <asiersarasua@ni.eus>, 2013, 2019, 2020, 2021, 2022.
+#
+msgid ""
+msgstr "Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-14 22:12+0000\n"
+"PO-Revision-Date: 2022-09-15 12:15+0100\n"
+"Last-Translator: Asier Sarasua Garmendia <asiersarasua@ni.eus>\n"
+"Language-Team: Basque <librezale@librezale.eus>\n"
+"Language: eu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Abiarazleak"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktibatu gogoko 1 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktibatu gogoko 2 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktibatu gogoko 3 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktibatu gogoko 4 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktibatu gogoko 5 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktibatu gogoko 6 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktibatu gogoko 7 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktibatu gogoko 8 aplikazioa"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktibatu gogoko 9 aplikazioa"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Pantaila-argazkiak"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Atera pantaila-argazki bat modu dinamikoan"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Atera pantaila-argazki bat"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Atera leiho baten pantaila-argazkia"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Grabatu pantailaren bideo labur bat modu dinamikoan"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Erakutsi jakinarazpen-zerrenda"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokuratu jakinarazpen aktiboa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Erakutsi ikuspegi orokorra"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Erakutsi aplikazio guztiak"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Ireki aplikazioen menua"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Leiho-kudeaketa eta aplikazioak abiaraztea"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Gaitu garatzaile eta probatzaileentzako barne-tresnak Alt-F2 bitartez"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "Barneko arazte- eta gainbegiraketa-tresnen atzipena onartzen du Alt-F2 elkarrizketa-koadroa erabiliz."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Gaituko diren hedapenen UUIDak"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr "GNOME Shell gehigarriek UUID propietatea dute; kargatu beharko liratekeen gehigarriak zerrendatzen ditu gako honek. Kargatzea nahi diren gehigarri guztiak zerrenda honetan agertu behar dute. Zerrenda hau alda dezakezu DBus-eko 'EnableExtension' (gehitu hedapena) eta 'DisableExtension' (desgaitu hedapena) metodoekin 'org.gnome.Shell'-en."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Desgaitzea behartuko den hedapenen UUIDak"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr "GNOME Shell gehigarriek UUID propietatea dute; nahiz eta uneko moduan kargatuak izan, desgaitu egin beharko liratekeen gehigarriak zerrendatzen ditu gako honek. Zerrenda hau alda dezakezu DBus-eko 'EnableExtension' (gaitu hedapena) eta 'DisableExtension' (desgaitu hedapena) metodoekin 'org.gnome.Shell'-en. Gako honek lehentasuna du “enabled-extensions” ezarpenaren gainetik."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Desgaitu erabiltzailearen gehigarriak"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr "“enabled-extension” ezarpenari eragin gabe erabiltzaileak gaitutako gehigarri guztiak desgaitu."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Hedapenaren bertsioaren bateragarritasunaren balidazioa desgaitzen du"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr "GNOME Shell-ek unean exekutatzen ari den bertsioarekin bat datozen hedapenak soilik kargatzen ditu. Aukera hau gaitzean egiaztapen hau desgaitzen da eta hedapen guztiak kargatzen saiatzen da, onartzen duten bertsioari ez ikusi eginez."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Gogoko aplikazioen mahaigaineko fitxategien IDen zerrenda"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "Identifikadore horiei dagozkien aplikazioak bistaratuko dira gogokoen arean."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Komandoen (Alt-F2) elkarrizketa-koadroaren historia"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Ikuskatzailearen elkarrizketa-koadroaren historia"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Erakutsi beti erabiltzaile-menuko “Amaitu saioa” menu-elementua."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr "Gako honek “Amaitu saioa” menu-elementua automatikoki ezkutatzea gainidazten du, saio bakarreko egoeran."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "Zifratutako edo urruneko fitxategi-sistemak muntatzeko pasahitza gogoratuko den ala ez"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr "Shell-ak pasahitza eskatuko du zifratutako gailu bat edo urruneko fitxategi-sistema bat muntatzen denean. Pasahitza geroago erabiltzeko gorde badaiteke, “Gogoratu pasahitza” kontrol-laukia agertuko da. Gako honek kontrol-laukiaren egoera lehenetsia ezartzen du."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "Bluetooth lehenetsiaren moldatzaileak berarekin esleitutako gailurik duen ala ez"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr "Shell-ak Bluetooth moldagailu bat entxufatuta dagoenean soilik Bluetooth-aren menua erakutsiko duen, edo moldagailu lehenetsiarekin esleitutako gailuak konfiguratuta daudenean. Hau leheneratu egingo da moldagailu lehenetsiak berarekin esleitutako gailurik ez daukanean."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "Hautatutako azken energia-profil ez lehenetsia"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr "Zenbait sistemak bi energia-profil baino gehiago onartzen dituzte. Bi profilen artean txandakatzea ahalbidetzeko, gako honek hautatutako azken profil ez lehenetsia erregistratzen du."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "“Ongi etorri GNOMEra“ elkarrizketa-koadroa zein bertsioarekin erakutsi zen azken aldian"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr "Gako honek zehazten du “Ongi etorri GNOMEra“ elkarrizketa-koadroa zein bertsiorekin erakutsi zen azken aldian. Kate hutsak adierazten du bertsio zaharrena, eta zenbaki handi batek adierazten du oraindik exisititzen ez den bertsio bat. Zenbaki handi hori elkarrizketa-koadroa desgaitzeko erabili daiteke."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Aplikazio-hautatzailearen diseinua"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr "Aplikazio-hautatzailearen diseinua. Matrizeko sarrera bakoitza orri bat da. Orriak GNOME Shell-en agertzen diren ordenan biltegiratzen dira. Orri bakoizak “aplikazioaren ida” → 'datuak' bikote bat du. Une honetan, honako balioak gordetzen dira 'datuak': • “posizioa” moduan: aplikazio-ikonoak orrian duen posizioa."
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Aplikazioen menua irekitzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Aplikazioen menua irekitzeko laster-tekla."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Ikuspegi orokorraren egoeren artean aldatzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "Saioaren, leiho-aukeratzailearen eta aplikazio-saretaren artean aldatzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "Aplikazio-saretaren, leiho-aukeratzailearen eta saioaren artean aldatzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "“Erakutsi aplikazioak” bista irekitzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Jarduerak ikuspegi orokorraren “Erakutsi aplikazioak” ikuspegia irekitzeko laster-tekla."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Ikuspegi orokorra bista irekitzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Jardueren ikuspegi orokorra irekitzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Jakinarazpenen zerrendaren ikusgaitasuna txandakatzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Jakinarazpenen zerrendaren ikusgaitasuna txandakatzeko laster-tekla."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Jakinarazpen aktiboa fokuratzeko laster-tekla"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Jakinarazpen aktiboa fokuratzeko laster-tekla."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Aldatu 1 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Aldatu 2 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Aldatu 3 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Aldatu 4 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Aldatu 5 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Aldatu 6 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Aldatu 7 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Aldatu 8 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Aldatu 9 aplikaziora"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Mugatu aldatzailea uneko laneko areara."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr "TRUE (egia) bada, uneko laneko arean leihoak dituzten aplikazioak soilik erakutsiko dira aldatzailean. Bestela, aplikazio guztiak egongo dira."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "Aplikazio-ikonoaren modua."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr "Leihoak aldatzaileak nola erakusten diren konfiguratzen du. Aukera posibleak “thumbnail-only” (leihoaren miniatura erakustea), “app-icon-only” (aplikazio-ikonoa erakustea soilik) edo “both” (biak)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr "TRUE (egia) bada, uneko laneko arean leihoak soilik erakutsiko dira aldatzailean. Bestela, leiho guztiak egongo dira."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Kokalekuak"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Munduko ordularietan erakutsiko diren kokalekuak"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Kokaleku automatikoa"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Uneko kokalekua atzituko den ala ez"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Kokalekua"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "Iragarpena erakutsiko zaion kokalekua"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Erantsi elkarrizketa-leiho modala leiho gurasoari"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "Gako honek org.gnome.mutter gakoa gainidazten du GNOME Shell exekutatzean."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Gaitu lauzatzea leihoak pantailaren ertzetan jaregitean"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Laneko areak dinamikoki kudeatzen dira"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Laneko areak monitore nagusian soilik"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Fokuaren aldaketaren atzerapena saguaren moduan erakusleak mugitzeari utzi arte"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Sareko saio-hasiera"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Zerbait gaizki joan da"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr "Barkatu, arazo bat gertatu da: hedapen honen ezarpenak ezin dira erakutsi. Arazoa hedapenaren egileei jakinarazi diezaiezun gomendatzen dizugu."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Xehetasun teknikoak"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Webgune nagusia"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Bisitatu hedapenaren webgunea"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Utzi"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Pasahitza"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Aukeratu saioa"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Ez zaude zerrendan?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(adib., erabiltzailea edo %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Erabiltzaile-izena"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Saio-hasierako leihoa"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Autentifikazio-errorea"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(edo lerratu hatza irakurlean zehar)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(edo kokatu hatza irakurlean)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Itzali"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "itzali;amatatu;gelditu;geratu"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Berrabiarazi"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "berrabiarazi;berrekin;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Blokeatu pantaila"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "blokeatu pantaila"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Amaitu saioa"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "amaitu saioa;saioa amaitu;deskonektatu"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Eseki"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "eseki;lo"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Aldatu erabiltzailea"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "aldatu erabiltzailea"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "blokeatu orientazioa;desblokeatu orientazioa;pantaila;biraketa"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Atera pantaila-argazki bat"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "pantaila-argazkia;pantaila-bideoa;mozketa;kaptura;grabazioa"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Desblokeatu pantailaren biraketa"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Pantailaren blokeoaren biraketa"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Ez da komandoa aurkitu"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Ezin izan da komandoa analizatu:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Huts egin du '%s' exekutatzean:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Orain"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "duela minutu %d"
+msgstr[1] "duela %d minutu"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "duela ordu %d"
+msgstr[1] "duela %d ordu"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Atzo"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "duela egun %d"
+msgstr[1] "duela %d egun"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "duela aste %d"
+msgstr[1] "duela %d aste"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "duela hilabete %d"
+msgstr[1] "duela %d hilabete"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "duela urte %d"
+msgstr[1] "duela %d urte"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Atzo, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%Y %B %-d, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Atzo, %I:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%Y %B %-d, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Wifigunearen saio-hasiera"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr "Wifigune honetarako saio-hasierako zure konexioa ez da segurua. Orri honetan sartzen dituzun pasahitzak edo bestelako informazioak inguruko beste jendeek ikus ditzakete."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Ukatu sarbidea"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Baimendu sarbidea"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Izenik gabeko karpeta"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s panelean ainguratu da."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s paneletik askatu da."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Leiho irekiak"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Leiho berria"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Erakutsi xehetasunak"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Irten"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Askatu"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Ainguratu panelean"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Abiarazi txartel grafiko integratua erabiliz"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Abiarazi txartel grafiko diskretua erabiliz"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Hautatu audioaren gailua"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Soinu-ezarpenak"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Entzungailua"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Entzungailua+mikrofonoa"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Mikrofonoa"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Aldatu atzeko planoa…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Pantailaren ezarpenak"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Ezarpenak"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "I"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "A"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "A"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "A"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "O"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "O"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "L"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Aurreko hila"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Hurrengo hila"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V. astea"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Jakinarazpenik ez"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Ez gogaitu"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Garbitu"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s”(e)k ez du erantzuten."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "Pixka bat itxoitearen edo aplikazioa irtetera derrigortzearen artean aukera dezakezu."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Behartu irtetera"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Itxaron"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Kanpoko unitatea konektatuta"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Kanpoko unitatea deskonektatuta"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Ezin da bolumena desblokeatu"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Instalatutako udisks bertsioak ez du onartzen PIM ezarpena"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Ireki honekin: %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Bestela, zure routerreko \"WPS\" botoia sakatuta konektatu zaitezke."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Konektatu"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Gakoa"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Gako pribatuaren pasahitza"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitatea"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Zerbitzua"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autentifikazioa behar da"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "Pasahitzak edo zifratze-gakoak behar dira haririk gabeko '%s' sarera sarbidetzeko."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Haridun 802.1X autentifikazioa"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Sarearen izena"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL autentifikazioa"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN kodea behar da"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN kodea behar da banda zabal mugikorreko gailuarentzako"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PINa"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Pasahitza behar da '%s'(e)ra konektatzeko."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Sarearen kudeatzailea"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN pasahitza"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Autentifikazioa behar da"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administratzailea"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autentifikatu"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Huts egin du. Saiatu berriro."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s orain %s izenarekin ezagutzen da"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Leihoak"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Erakutsi aplikazioak"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Atrakagarria"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%Y %B %-d"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %Y %B %e"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y %B %-d"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Gaur"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Bihar"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Egun osoa"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Gertaerarik ez"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Gehitu munduko erlojuak…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Munduko erlojuak"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Kargatzen…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Konektatu eguraldiaren informazioa lortzeko"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Eguraldiaren informazioa unean ez dago eskuragarri"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Eguraldia"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Hautatu eguraldiaren kokalekua…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Amaitu %s(r)en saioa"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Amaitu saioa"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s erabiltzailearen saioa automatikoki %d segundotan amaituko da."
+msgstr[1] "%s erabiltzailearen saioa automatikoki %d segundotan amaituko da."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Zure saioa automatikoki %d segundotan amaituko da."
+msgstr[1] "Zure saioa automatikoki %d segundotan amaituko da."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Amaitu saioa"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Itzali"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalatu eguneratzeak eta itzali"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistema automatikoki segundo %dean itzaliko da."
+msgstr[1] "Sistema automatikoki %d segundotan itzaliko da."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalatu falta diren softwareen eguneratzeak"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Itzali"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Berrabiarazi"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalatu eguneratzeak eta berrabiarazi"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistema automatikoki segundo %dean berrabiaraziko da."
+msgstr[1] "Sistema automatikoki %d segundotan berrabiaraziko da."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Berrabiarazi"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Berrabiarazi eta instalatu eguneratzeak"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "Sistema automatikoki berrabiaraziko da eta eguneratzeak instalatuko ditu segundo %dean."
+msgstr[1] "Sistema automatikoki berrabiaraziko da eta eguneratzeak instalatuko ditu %d segundotan."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Berrabiarazi eta instalatu"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalatu eta itzali"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Itzali eguneratze guztiak instalatu ondoren"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Berrabiarazi eta instalatu bertsio-berritzea"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr "%s %s instalatuko da berrabiarazi ondoren. Bertsio-berritzearen instalazioak denbora luzea beharko du, ziurtatu zaitez babeskopia eginda daukazula eta ordenagailua entxufatuta dagoela."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Energia gutxi baterian: entxufatu eguneratzeak instalatu aurretik."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Aplikazio batzuk lanpetuta daude edo gorde gabeko lanak dituzte."
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Beste erabiltzaile batzuek saioa hasita dute."
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Abioko aukerak"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (urrunekoa)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (kontsola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instalatu"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalatu hedapena"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Deskargatu eta instalatu “%s” extensions.gnome.org gunetik?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Hedapenen eguneratzeak eskuragarri"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Hedapenen eguneratzeak instalatzeko prest daude."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Onartu lasterbideak galaraztea"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "%s aplikazioak lasterbideak galarazi nahi ditu"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Aplikazio batek lasterbideak galarazi nahi ditu"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Lasterbideak lehenera ditzakezu “%s\" sakatuz."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Ukatu"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Baimendu"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tekla motelak aktibatuta"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tekla motelak desaktibatuta"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr "Shift teklari 8 segundo baino denbora gehiago eutsi diozu sakatuta. Hori tekla motelen eginbidea aktibatzeko lasterbidea da, eta zure teklatuaren funtzionamendua aldatzen du."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Tekla itsaskorrak aktibatuta"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Tekla itsaskorrak desaktibatuta"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr "Shift teklari 5 aldiz jarraian sakatu diozu. Hori tekla itsaskorren eginbidea aktibatzeko lasterbidea da, eta zure teklatuaren funtzionamendua aldatzen du."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr "Bi tekla aldi berean sakatu dituzu, edo Shift teklari 5 aldiz jarraian sakatu diozu. Hori tekla itsaskorren eginbidea desaktibatzeko lasterbidea da, eta zure teklatuaren funtzionamendua aldatzen du."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Utzi piztuta"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Piztu"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Itzali"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Utzi itzalita"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Eskualdearen eta hizkuntzaren ezarpenak"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Hedapenik ez dago instalatuta"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s(e)k ez du inolako errorerik igorri."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Ezkutatu erroreak"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Erakutsi erroreak"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Gaituta"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Desgaituta"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Errorea"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Zaharkituta"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Deskargatzen"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Ikusi iturburua"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Web orria"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Sistema segurua ez den moduan jarri da"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Orain aplikazioek mugarik gabeko sarbidea dute"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Desegin"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Erabiltzaile pribilegiatu gisa saioa hasita"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr "Erabiltzaile pribilegiatu gisa saio bat exekutatzea saihestu beharko litzateke segurtasuneko arrazoiak direla eta. Posible bada, erabiltzaile arrunt gisa hasi beharko zenuke saioa."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Pantailaren blokeoa desgaituta dago"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Pantaila blokeatzeko GNOMEren pantaila-kudeatzailea behar da."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Sistemaren informazioa"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artista ezezaguna"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Titulu ezezaguna"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Idatzi bilatzeko"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Aplikazioak"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Aurkezpen orokorra"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Laster-tekla berria…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplikazioan definituta"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Erakutsi pantailako laguntzan"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Aldatu monitorea"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Esleitu tekla-sakatzea"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Eginda"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Editatu…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Bat ere ez"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Sakatu botoi bat konfiguratzeko"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Sakatu Esc irteteko"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Sakatu edozer tekla irteteko"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Jarduerak"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Goiko barra"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Exekutatu komando bat"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Sakatu Esc ixteko"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Berrabiaraztea ez dago erabilgarri Wayland-en"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Berrabiarazten…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOMEk pantaila blokeatzea behar du"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Ezin da blokeatu"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Beste aplikazio batek blokeatu du"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Hautapena"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Area-hautapena"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Pantaila"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Pantaila-hautapena"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Leihoa"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Leiho-hautapena"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Pantaila-argazkia/bideoa"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Erakutsi erakuslea"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Pantaila-bideoak"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "%d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Pantaila-argazkia"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Pantaila-bideoa grabatu da"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Klik hemen bideoa ikusteko."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Erakutsi Fitxategiak aplikazioan"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "%s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Pantaila-argazkia hartu da"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Irudia arbeletik itsatsi daiteke."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Pantaila-argazkia hartu da"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Bilatzen…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Emaitzarik ez."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d gehiago"
+msgstr[1] "%d gehiago"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Bilatu"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopiatu"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Itsatsi"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Erakutsi testua"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ezkutatu testua"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Maiuskulen tekla aktibatuta dago"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Ezkutuko bolumena"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows sistema-bolumena"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Gako-fitxategiak erabiltzen ditu"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "Gako-fitxategiak erabiltzen dituen bolumen bat desmuntatzeko, erabili <i>%s</i> utilitatea"
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM zenbakia"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Gogoratu pasahitza"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desblokeatu"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Ireki %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM balioak zenbaki bat izan behar du edo hutsik egon behar du."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Ezin da %s abiarazi"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Ez da %s aplikazioa aurkitu"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Erabilerraztasuna"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Kontraste handia"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zooma"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Pantaila irakurlea"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Pantailako teklatua"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Abisu bisualak"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Tekla itsaskorrak"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tekla motelak"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Errebote-teklak"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Sagu-teklak"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Testu handia"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatikoki biratu"
+
+#: js/ui/status/bluetooth.js:171
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Distira"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Modu iluna"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Klik bakuna"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Klik bikoitza"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Arrastatu"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "2. mailako klik egitea"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Ordezko klika"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Teklatua"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Erakutsi teklatuaren diseinua"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Onartu kokalekua atzitzea"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "%s aplikazioak zure kokalekua atzitu nahi du"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Kokalekuaren sarbidea edozer unetan alda daiteke pribatutasunen ezarpenetatik."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<ezezaguna>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Deskonektatu %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Konektatu honekin: %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s wifigunea"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Gaueko argia"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Errendimendua"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Orekatua"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Energiaren aurrezpena"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Energia-profilak"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Gelditu pantaila-bideoa"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Gelditu pantaila partekatzea"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Hegazkin modua"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%% %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Atera pantaila-argazkia"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Itzali menua"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Eseki"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Berrabiarazi…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Itzali…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Amaitu saioa…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Aldatu erabiltzailea…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Blokeatu pantaila"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Thunderbolt gailu ezezaguna"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr "Gailu berria detektatu da ez zeunden bitartean. Deskonektatu eta berriro konektatu gailua hura erabiltzen hasi ahal izateko."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Baimenik gabeko Thunderbolt gailua"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Gailu berria detektatu da eta administratzaile batek baimena eman behar dio."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt baimen-errorea"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Ezin izan da Thunderbolt gailua baimendu: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Bolumena aldatuta"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Bolumena"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Soinu-irteera"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Soinu-sarrera"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Ispilua"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Batu pantailak"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Kanpokoa soilik"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Barnekoa soilik"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Igaro hatza desblokeatzeko"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Egin klik edo sakatu tekla bat desblokeatzeko"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Desblokeatu leihoa"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Hasi saioa beste erabiltzaile baten gisan"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Ongi etorri GNOME %s aplikaziora"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "GNOME erabiltzen ikasi nahi baduzu, jarraitu bisita."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ez, eskerrik asko"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Egin bisita"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "'%s' prest dago"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Mantendu bistaratze-ezarpen hauek?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Alderantzikatu ezarpenak"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Mantendu aldaketak"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Ezarpenen aldaketak segundo %dean alderantzikatuko dira"
+msgstr[1] "Ezarpenen aldaketak %d segundotan alderantzikatuko dira"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Ezkutatu"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Leheneratu"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximizatu"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Mugitu"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Aldatu tamaina"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Mugitu titulu-barra pantailan"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Beti gainean"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Beti laneko area ikusgaian"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Eraman ezkerreko laneko areara"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Eraman eskuineko laneko areara"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Eraman gaineko laneko areara"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Eraman azpiko laneko areara"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Eraman gaineko pantailara"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Eraman azpiko pantailara"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Eraman ezkerreko pantailara"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Eraman eskuineko pantailara"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Itxi"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution egutegia"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Bistaratu bertsioa"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "GDMk erabiltzen duen modua saio-hasierako pantailarako"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Erabili modu jakin bat, adib. “gdm“, saioa hasteko pantailarako"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Zerrendatu modu posibleak"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ezezaguna"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Huts egin du '%s' abiaraztean"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Pasahitzak ez datoz bat"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Pasahitza ezin da hutsa izan"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Erabiltzaileak autentifikatzeko elkarrizketa-koadroa itxi du"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Hedapenak"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Kudeatu zure GNOME hedapenak"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME proiektua"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr "GNOME Hedapenak aplikazioak hedapenak eguneratzea, hedapenen hobespenak konfiguratzea eta gehiago nahi ez diren hedapenak kentzea edo desgaitzea ahalbidetzen du."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfiguratu GNOME Shell-eko gehigarriak"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Bat etortzerik ez"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Kendu “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr "Hedapena kentzen bada, berriro deskargatu beharko da atzera gaitu nahi bada."
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Kendu"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "translator-credits"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Hedapen %d eguneratuko da hurrengo saio-hasieran."
+msgstr[1] "%d hedapen eguneratuko dira hurrengo saio-hasieran."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Hedapena ez da bateragarria uneko GNOME bertsioarekin"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Hedapenak errore bat izan du"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Hedapena eguneratu egin daiteke"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Webgunea"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Kendu…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Laguntza"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Hedapenei buruz"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr "Hedapenek errendimendu- eta egonkortasun-arazoak sortu ditzakete. Desgaitu hedapenak zure sistemak arazoak baditu."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Eskuz instalatua"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr "Hedapenak aurkitu eta gehitzeko, bisitatu <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Integratua"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Instalatu gabeko luzapenak"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr "Barkatu, ezin izan da instalatutako hedapenen zerrenda eskuratu. Ziurtatu GNOMEn saioa hasi duzula eta saiatu berriro."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Hedapenen eguneratzeak prest"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Hedapen berria ongi sortu da %s kokalekuan.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr "Izenak kate laburra izan behar du (hobe deskribatzailea bada).\n"
+"Adibideak: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Izena"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr "Deskribapena zure hedapenak egiten duena azaltzen duen esaldi bakarra da.\n"
+"Adibideak: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Azalpena"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr "UUIDa zure hedapenaren identifikatzaile esklusiboa da.\n"
+"Posta elektronikoko helbide baten formatukoa izan behar du (klikirekitzeko@asier.adibidea.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Aukeratu erabilgarri dauden txantiloietako bat:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Txantiloia"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Hedapen berriaren identifikatzaile bakarra"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "IZENA"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Hedapen berriaren izena, erabiltzaileak ikusiko duena"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESKRIBAPENA"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Hedapenak egiten duenaren deskribapen laburra"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "TXANTILOIA"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Hedapen berrirako erabiliko den txantiloia"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Sartu hedapenaren informazioa dinamikoki"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Sortu hedapen berria"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argumentu ezezagunak"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUIDa, izena eta deskribapena derrigorrezkoak dira"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Huts egin du GNOME Shell-ekin konektatzeak\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Ez dago “%s” hedapenik\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Desgaitu hedapen bat"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Ez da UUIDrik eman"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "UUID bat baino gehiago eman da"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Gaitu hedapen bat"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Ez dago “%s” hedapenik.\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Hedapenen informazioa erakusten du"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Gainidatzi lehendik dagoen hedapena"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "HEDAPEN_BILDUMA"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instalatu hedapen-bilduma"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Ez da hedapen-bildumarik zehaztu"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Hedapen-bilduma bat baino gehiago zehaztu da"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Erakutsi erabiltzaileak instalatutako hedapenak"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Erakutsi sistemak instalatutako hedapenak"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Erakutsi gaitutako hedapenak"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Erakutsi desgaitutako hedapenak"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Erakutsi hobespenak dituzten hedapenak"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Erakutsi eguneratzeak dituzten hedapenak"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Inprimatu hedapenaren xehetasunak"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Zerrendatu instalatutako hedapenak"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FITXATEGIA"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Bilduman sartuko zen iturburu gehigarria"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ESKEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Sartu behar den GSettings eskema"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIREKTORIOA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Itzulpenak dituen direktorioa"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMEINUA"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Itzulpenetarako erabiliko den gettext domeinua"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Gainidatzi lehendik dagoen pakete bat"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Paketea sortzeko direktorioa"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ITURBURU_DIREKTORIOA"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Sortu hedapen-bilduma bat"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Iturburu-direktorio bat baino gehiago zehaztu da"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "“%s” hedapenak ez dauka hobespenik\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Ezin izan dira hobespenak ireki “%s” hedapenerako: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Hedapenaren hobespenak irekitzen ditu"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Berrezarri hedapen bat"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Ezin dira sistemaren hedapenak desinstalatu\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Huts egin du “%s” instalatzeak\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Desinstalatu hedapen bat"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ez inprimatu errore-mezuak"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Bide-izena"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URLa"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Jatorrizko egilea"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Bertsioa"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Egoera"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” ez du argumenturik hartzen"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Erabilera:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Erakutsi bertsioaren informazioa eta irten"
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "KOMANDOA"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTUAK…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Komandoak:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Erakutsi laguntza"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Gaitu hedapena"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Desgaitu hedapena"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Berrezarri hedapena"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Desinstalatu hedapena"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Zerrendatu hedapenak"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Erakutsi hedapenaren informazioa"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Ireki hedapenaren hobespenak"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Sortu hedapena"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Paketatu hedapena"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instalatu hedapen-bilduma"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Erabili “%s” laguntza xehea lortzeko.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Soila"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Hedapen huts bat"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Adierazlea"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Gehitu ikono bat goiko barrari"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "Irteera %u"
+msgstr[1] "%u irteera"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "Sarrera %u"
+msgstr[1] "%u sarrera"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sistemaren soinuak"
+
+#~ msgid "Log Out"
+#~ msgstr "Amaitu saioa"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "'%s' itzalita"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "'%s' konektatuta"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "'%s' kudeatu gabea"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "'%s' konektatzen"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "'%s'(e)k autentifikazioa eskatzen du"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "'%s'(r)en firmwarea falta da"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "'%s' ez dago erabilgarri"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "'%s'(r)en konexioak huts egin du"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Haridunaren ezarpenak"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Banda zabal mugikorraren ezarpenak"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "'%s' hardwarea desgaituta"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "'%s' desgaituta"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Bluetooth ezarpenak"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Konektatu Internetera"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Hegazkin modua piztuta"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Haririk gabekoa desgaitu egiten da hegazkin modua piztean."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Itzali hegazkin modua"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Haririk gabekoa itzalita"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Haririk gabekoa piztu egin behar da sare batera konektatzeko."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Piztu haririk gabekoa"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wifi sareak"
+
+#~ msgid "Select a network"
+#~ msgstr "Hautatu sare bat"
+
+#~ msgid "No Networks"
+#~ msgstr "Sarerik ez"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Erabili hardwarearen aldatzailea itzaltzeko"
+
+#~ msgid "Select Network"
+#~ msgstr "Hautatu sarea"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Haririk gabekoaren ezarpenak"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "'%s' konektatu gabe"
+
+#~ msgid "unknown"
+#~ msgstr "ezezaguna"
+
+#~ msgid "activating…"
+#~ msgstr "aktibatzen…"
+
+#~ msgid "deactivating…"
+#~ msgstr "desaktibatzen…"
+
+#~ msgid "deactivated"
+#~ msgstr "desaktibatuta"
+
+#~ msgid "connecting…"
+#~ msgstr "konektatzen…"
+
+#~ msgid "authentication required"
+#~ msgstr "autentifikazioa behar da"
+
+#~ msgid "connection failed"
+#~ msgstr "konexioak huts egin du"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPNaren ezarpenak"
+
+#~ msgid "VPN"
+#~ msgstr "VPNa"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPNa itzalita"
+
+#~ msgid "Network Settings"
+#~ msgstr "Sarearen ezarpenak"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s haridun konexioa"
+#~ msgstr[1] "%s haridun konexioak"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s haririk gabeko konexioa"
+#~ msgstr[1] "%s haririk gabeko konexioak"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s modem konexioa"
+#~ msgstr[1] "%s modem konexioak"
+
+#~ msgid "Connection failed"
+#~ msgstr "Konexioak huts egin du"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Sareko konexioa aktibatzeak huts egin du"
+
+#~ msgid "Power Settings"
+#~ msgstr "Energiaren ezarpenak"
+
+#~ msgid "Lock"
+#~ msgstr "Blokeatu"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Itzaltzea / saioa amaitzea"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d konektatuta"
+#~ msgstr[1] "%d konektatuta"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth-a aktibatuta"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth-a desaktibatuta"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Kokalekua gaituta"
+
+#~ msgid "Disable"
+#~ msgstr "Desgaitu"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Pribatutasunaren ezarpenak"
+
+#~ msgid "Location In Use"
+#~ msgstr "Kokalekua erabilita"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Kokalekua desgaituta"
+
+#~ msgid "Enable"
+#~ msgstr "Gaitu"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "“Gaueko argia” desgaituta"
+
+#~ msgid "Resume"
+#~ msgstr "Berrekin"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Desgaitu bihar arte"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Erabat kargatuta"
+
+#~ msgid "Not Charging"
+#~ msgstr "Kargarik ez"
+
+#~ msgid "Estimating…"
+#~ msgstr "Kalkulatzen…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d falta da (%% %d)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d bete arte (%% %d)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Pantaila partekatzen ari da"
+
+#~ msgid "Turn off"
+#~ msgstr "Desaktibatu"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "Erakutsi pantaila-argazkiaren interfazea"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Kendu gogokoetatik"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Gehitu gogokoei"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Pantailaren grabazioa abian"
+
+#~ msgid "Stop"
+#~ msgstr "Gelditu"
+
+#~ msgid "Author"
+#~ msgstr "Egilea"
+
+#~ msgid "Warning"
+#~ msgstr "Abisua"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Hedapenek arazoak sor ditzakete sisteman, errendimendu-arazoak barne. "
+#~ "Sisteman arazoak aurkitzen badituzu, desgaitu hedapen guztiak."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Gaitu introspekzio APIa"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Shell-en aplikazio-egoeraren introspekzioa ahalbidetzen duen D-Bus API "
+#~ "bat gaitzen du."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Huts egin du GNOME Shell-ekin konektatzeak"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizatu"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Desmaximizatu"
+
+#~ msgid "App Picker View"
+#~ msgstr "Aplikazio-hautatzailearen ikuspegia"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Unean hautatutako ikuspegiaren indizea aplikazio-hautatzailean."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Maiztasunez erabilitako aplikazioak hemen agertuko dira"
+
+#~ msgid "Frequent"
+#~ msgstr "Erabilienak"
+
+#~ msgid "All"
+#~ msgstr "Denak"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %B %-d"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %B %-d, %Y"
+
+#~ msgid "Off"
+#~ msgstr "Desaktibatuta"
+
+#~ msgid "On"
+#~ msgstr "Aktibatuta"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopiatze-errorea"
+
+#~ msgid "Next"
+#~ msgstr "Hurrengoa"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Hasi saioa"
+
+#~ msgid "Password: "
+#~ msgstr "Pasahitza: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Sartu pasahitza…"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %B %d"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "Mezu berri %d"
+#~ msgstr[1] "%d mezu berri"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "Jakinarazpen berri %d"
+#~ msgstr[1] "%d jakinarazpen berri"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Arakatu softwarean"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Blokeatu orientazioa"
+
+#~ msgid "Rename"
+#~ msgstr "Aldatu izena"
+
+#~ msgid "Type again:"
+#~ msgstr "Idatzi berriro:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Haririk gabeko sareak autentifikazioa eskatzen du"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Bandazabal mugikorreko sarearen pasahitza"
+
+#~ msgid "Account Settings"
+#~ msgstr "Kontuaren ezarpenak"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Orientazioaren blokeoa"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Laster-teklak exekutatzen ari diren 'tween' guztiak pausarazi eta "
+#~ "jarraitzen ditu arazketa lantzeko"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Erabili beharreko teklatua"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Erabili beharreko teklatu mota."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
diff --git a/po/fa.po b/po/fa.po
new file mode 100644
index 0000000..26d01d8
--- /dev/null
+++ b/po/fa.po
@@ -0,0 +1,4027 @@
+# Persian translation for gnome-shell.
+# Copyright (C) 2010-2011 Iranian Free Software Users Group (IFSUG.org)translation team.
+# This file is distributed under the same license as the gnome-shell package.
+# Mahyar Moghimi <mahyar.moqimi@gmail.com>, 2010.
+# Arash Mousavi <mousavi.arash@gmail.com>, 2010-2017.
+# Danial behzadi <dani.behzi@ubuntu.com>, 2018-2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-07-06 18:09+0000\n"
+"PO-Revision-Date: 2023-08-07 13:04+0330\n"
+"Last-Translator: Danial Behzadi <dani.behzi@ubuntu.com>\n"
+"Language-Team: Persian <>\n"
+"Language: fa\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "اجراگرها"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۱"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۲"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۳"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۴"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۵"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۶"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۷"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۸"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "فعّال‌سازی برنامهٔ مورد علاقهٔ ۹"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2076
+msgid "Screenshots"
+msgstr "نماگرفت‌ها"
+
+#: data/50-gnome-shell-screenshots.xml:9 data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "گرفتن کنش‌پذیر یم نماگرفت"
+
+#: data/50-gnome-shell-screenshots.xml:12 data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "گرفتن یک نماگرفت"
+
+#: data/50-gnome-shell-screenshots.xml:15 data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "گرفتن نماگرفتی از یک پنچره"
+
+#: data/50-gnome-shell-screenshots.xml:18 data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "ضبط کنش‌پذیر صفحه"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "سامانه"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "نمایش فهرست آگاهی‌ها"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "تمرکز روی آگاهی فعّال"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "نمایش نمای کلّی"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "نمایش همهٔ برنامه‌ها"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "گشودن فهرست برنامه"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "پوستهٔ گنوم"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "مدیریت پنجره‌ها و اجرا کنندهٔ برنامه‌ها"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "به کار انداختن ابزارهای داخلی مفید برای توسعه‌دهندگان و آزمونگران از Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 dialog."
+msgstr "اجازه دسترسی به ابزارهای اشکال‌زدا و پایشگر داخلی با استفاده از محاورهٔ Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "شناسه‌های یکتای افزونه‌ها برای به کار اندازی"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which should "
+"be loaded. Any extension that wants to be loaded needs to be in this list. You can "
+"also manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell."
+msgstr ""
+"افزونه‌های گنوم‌شل مشخصهٔ UUID دارند؛ این کلید، افزونه‌هایی که باید بار شوند را فهرست "
+"می‌کند. هر افزونه‌ای که می‌خواهد بار شود، باید در این فهرست باشد. شما همچنین می‌توانید "
+"این فهرست را از طریق EnableExtension و DisableExtension (روش‌های D-Bus) در org.gnome."
+"Shell نیز بسازید."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "شناسه‌های یکتای افزونه‌ها برای از کار اندازی اجباری"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which should "
+"be disabled, even if loaded as part of the current mode. You can also manipulate "
+"this list with the EnableExtension and DisableExtension D-Bus methods on org.gnome."
+"Shell. This key takes precedence over the “enabled-extensions” setting."
+msgstr ""
+"افزونه‌های پوستهٔ گنوم شل شناسه‌ای یکتا دارند؛ این کلید، افزونه‌هایی که باید غیرفعّال "
+"شوند را فهرست می‌کند، حتا اگر به عنوان بخشی از حالت فعلی بار شده باشند. می‌توانید این "
+"فهرست را از طریق روش‌های دی‌باس EnableExtension و DisableExtension در org.gnome.Shell "
+"نیز تغییر دهید."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "از کار انداختن افزونه‌های کاربر"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"از کار انداختن تمام افزونه‌هایی که کاربر به کار انداخته، بدون اثر روی تنظیمات "
+"«افزونهٔ فعّال»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "از کار انداختن بررسی سازگاری نسخهٔ افزونه"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current running "
+"version. Enabling this option will disable this check and try to load all "
+"extensions regardless of the versions they claim to support."
+msgstr ""
+"پوستهٔ گنوم تنها نگارش‌هایی از افزونه‌ها را که ادّعا می‌کنند از نگارش درحال اجرا "
+"پشتیبانی می‌کند، بار می‌کند. به کار اندازی این گزینه، بررسی این مورد را از کار "
+"انداخته و سعی می‌کند تمام افزونه‌ها، بدون درنظر گرفتن سازگاری آنها بار شوند."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "فهرست شناسه‌های پروندهٔ میزکار برای برنامه‌های مورد علاقه"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "برنامه‌های مشابه این شناسه‌ها در قسمت مورد علاقه‌ها نمایش داده می‌شود."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "تاریخچهٔ فرمان برای محاورهٔ (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "تاریخچه برای محاورهٔ آینه"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "نمایش همیشگی «خروج» در فهرست کاربر."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-user, "
+"single-session situations."
+msgstr ""
+"این کلید، نهفتن خودکار «خروج» در فهرست را در حالت‌های تک‌کاربر، تک‌نشست بازنویسی می‌کند."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid "Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"اینکه آیا گذرواژه برای سوارکردن سامانه‌های پروندهٔ رمزنگاری شده و یا دوردست به‌خاطر "
+"سپرده شود یا خیر"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote filesystem "
+"is mounted. If the password can be saved for future use a “Remember Password” "
+"checkbox will be present. This key sets the default state of the checkbox."
+msgstr ""
+"هنگامی که دستگاهی رمزنگاری شده یا سامانه‌پرونده‌ای دوردست سوار می‌شود، پوسته درخواست "
+"گذرواژه می‌کند. اگر گذرواژه را بتوان برای استفادهٔ آینده ذخیره کرد، گزینهٔ «به‌خاطر "
+"سپردن گذرواژه» موجود خواهد بود. این کلید، حالت پیش‌گزیدهٔ این گزینه را تنظیم می‌کند."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "آخرین نمایهٔ نیروی گزیدهٔ غیر پیش فرض"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-default "
+"profile."
+msgstr ""
+"برخی سامانه‌ها بیش از دو نمایهٔ نیرو را پشتیبانی می‌کنند. به منظور ادامهٔ پشتیبانی از "
+"تغییر وضعیت بین دو نمایه، این ملید آخرین نمایهٔ نیروی گزیدهٔ غیر پیش‌فرض را ضبط می‌کند."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "آخرین نگارش گفت‌وگوی «به گنوم خوش آمدید» نشان‌داده شده برای"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last shown. "
+"An empty string represents the oldest possible version, and a huge number will "
+"represent versions that do not exist yet. This huge number can be used to "
+"effectively disable the dialog."
+msgstr ""
+"این کلید مشخّص می‌کند گفت‌وگوی «به گنوم خوش آمدید»، برای کدام نگارش نشان داده شده. یک "
+"رشتهٔ خالی، نمایانگر قدیمی‌ترین نگارش ممکن بوده و یه عدد بزرگ، نمایانگر نگارش‌هاییست "
+"که هنوز وجود ندارند. این عدد بزرگ می‌تواند برای از کار انداختن مؤثّر گفت‌وگو استفاده "
+"شود."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "چینش گزینشگر کاره"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are stored in "
+"the order they appear in GNOME Shell. Each page contains an “application id” → "
+"'data' pair. Currently, the following values are stored as 'data': • “position”: "
+"the position of the application icon in the page"
+msgstr ""
+"چینش گزینشگر کاره. هر ورودی در آرایه، یک صفحه است. صفحه‌ها به ترتیبی که در پوستهٔ "
+"گنوم ظاهر می‌شوند، ذخیره می‌شوند. هر صفحه دارای یک جفت «شناسهٔ برنامه» ← «داده» است. "
+"درحال حاضر، مقدارهای زیر به عنوان «داده»: • «موقعیت»: موقعیت نقشک برنامه در صفحه "
+"ذخیره شده‌اند"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "ترکیب کلید برای گشودن فهرست برنامه"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "ترکیب کلید برای گشودن فهرست برنامه."
+
+#: data/org.gnome.shell.gschema.xml.in:155 data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "ترکیب کلید برای تعویض بین حالت‌های نمای کلّی"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "ترکیب کلید برای تبدیل بین نشست، پنجره‌گزین و شبکهٔ کاره"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "ترکیب کلید برای تبدیل بین شبکهٔ کاره، پنجره‌گزین و نشست"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "ترکیب کلید برای گشودن نمای «نمایش برنامه‌ها»"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "ترکیب کلید برای گشودن نمای «نمایش برنامه‌ها» در نمای کلّی فعّالیت‌ها."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "ترکیب کلید برای گشودن نمای کلّی"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "ترکیب کلید برای گشودن نمای کلّی فعّالیت‌ها."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "ترکیب کلید برای تغییر وضعیت نمایش فهرست پیام‌ها"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "ترکیب کلید برای تغییر وضعیت نمایش فهرست پیام‌ها."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "ترکیب کلید برای متمرکز کردن آگاهی فعّال"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "ترکیب کلید برای متمرکز کردن آگاهی فعّال."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "تعویض به برنامهٔ ۱"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "تعویض به برنامهٔ ۲"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "تعویض به برنامهٔ ۳"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "تعویض به برنامهٔ ۴"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "تعویض به برنامهٔ ۵"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "تعویض به برنامهٔ ۶"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "تعویض به برنامهٔ ۷"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "تعویض به برنامهٔ ۸"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "تعویض به برنامهٔ ۹"
+
+#: data/org.gnome.shell.gschema.xml.in:255 data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "محدود کردن تعویض‌گر به فضای‌کاری فعلی."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are shown in "
+"the switcher. Otherwise, all applications are included."
+msgstr ""
+"اگر درست باشد، تنها برنامه‌هایی که در فضای‌کاری فعلی پنجره دارند در تعویض‌گر نشان داده "
+"می‌شوند. در غیر این صورت، تمام برنامه‌ها نشان داده می‌شوند."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "حالت شمایلی برنامه."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities are "
+"“thumbnail-only” (shows a thumbnail of the window), “app-icon-only” (shows only the "
+"application icon) or “both”."
+msgstr ""
+"نحوه نمایش پنجره‌ها را در تعویضگر پیکربندی می‌کند. مقدارهای ممکن عبارتند از "
+"«thumbnail-only» (نمایش تصویر بندانگشتی از پنجره)، «app-icon-only» (نمایش تنها "
+"شمایل برنامه) یا «both» (هر دو)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"اگر درست باشد، تنها پنجره‌های فضای‌کاری فعلی در تعویض‌گر نمایش داده می‌شود. در غیر این "
+"صورت، تمام پنجره‌ها اضافه می‌شوند."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "مکان‌ها"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "مکان‌ها برای نمایش در ساعت‌های جهانی"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "مکان خودکار"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "این که مکان فعلی گرفته شود یا نه"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "مکان"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "مکان برای نمایش پیش‌بینی"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "چسباندن محاورهٔ مقید به پنجره والد"
+
+#: data/org.gnome.shell.gschema.xml.in:326 data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343 data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid "This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "این کلید، کلید org.gnome.mutter را در زمان اجرای گنوم‌شل بازنویسی می‌کند."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "به کار انداختن کاشی‌کاری گوشته‌ها هنگام رها کردن پنجره‌ها در گوشه‌های صفحه"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "فضاهای کاری به‌صورت پویا مدیریت می‌شوند"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "فضاهای کاری تنها در صفحه‌نمایش اصلی"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"به تأخیر انداختن تغییر تمرکز در حالت موشی تا زمانی که نشانگر از حرکت باز ایستد"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "ورود شبکه‌ای"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "اشتباهی صورت گرفت"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this extension can’t "
+"be displayed. We recommend that you report the issue to the extension authors."
+msgstr ""
+"متأسفیم، ولی مشکلی وجود داشت: تنظیمات این افزونه نمی‌تواند نشان داده شوند. توصیه "
+"می‌کنیم مشکل را به نگارندهٔ افزونه گزارش دهید."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "جزییات فنّی"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "صفحهٔ خانگی"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "مشاهدهٔ صفحهٔ خانگی افزونه"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "لغو"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "گذرواژه"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "گزینش نشست"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "فهرست نشده؟"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(برای مثال، کاربر یا %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "نام‌کاربری"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "پنجرهٔ ورود به سامانه"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "خطا تأیید هویت"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(یا انگشتتان را روی خواننده بکشید)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(یا انگشتتان را روی خواننده بگذارید)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "خاموش کردن"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;خاموش;توقف"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "راه‌اندازی دوباره"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;شروع دوباره;خاموش و روشن;راه‌اندازی دوباره;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "صفحهٔ قفل"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "قفل کردن صفحه"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "خروج"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;خروج;خارج"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "تعلیق"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;تعلیق;خواب"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "تعویض کاربر"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "تعویض کاربر"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "lock orientation;unlock orientation;screen;rotation;قفل;جهت;چرخش;صفحه"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "گرفتن یک نماگرفت"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;نماگرفت;اسکرین‌شات;اسکرین‌کست;ضبط صفحه"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "قفل‌گشایی چرخش صفحه"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "قفل چرخش صفحه"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "فرمان پیدا نشد"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "عدم توانایی در تجزیه فرمان:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "اجرای «%s» شکست خورد:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "همین الآن"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%Id دقیقه پیش"
+msgstr[1] "%Id دقیقه پیش"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%Id ساعت پیش"
+msgstr[1] "%Id ساعت پیش"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "دیروز"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%Id روز پیش"
+msgstr[1] "%Id روز پیش"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%Id هفته پیش"
+msgstr[1] "%Id هفته پیش"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%Id ماه پیش"
+msgstr[1] "%Id ماه پیش"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%Id سال پیش"
+msgstr[1] "%Id سال پیش"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "⁦%OH∶%OM⁩"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "دیروز، ⁦%OH∶%OM⁩"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A، ⁦%OH∶%OM⁩"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-Od %OB، ⁦%OH∶%OM⁩"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-Od %OB %OY، ⁦%OH∶%OM⁩"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "⁦%Ol∶%OM⁩ %Op"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "دیروز، ⁦%Ol∶%OM⁩ %p"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A، ⁦%Ol∶%OM⁩ %Op"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-Od %OB، ⁦%Ol∶%OM⁩ %Op"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-Od %OB %OY، ⁦%Ol∶%OM⁩ %Op"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "ورود به نقطهٔ داغ"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other information "
+"you enter on this page can be viewed by people nearby."
+msgstr ""
+"اتّصال شما به این ورود نقطهٔ داغ امن نیست. گذرواژه‌ها یا دیگر اطّلاعاتی که در این صفحه "
+"وارد می‌کنید، می‌تواند توسّط افراد اطراف دیده شود."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "رد دسترسی"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "پذیرفتن دسترسی"
+
+#: js/ui/appDisplay.js:1732
+msgid "Unnamed Folder"
+msgstr "پوشهٔ بی‌نام"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s به دش سنحاق شد."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "سنجاق %s از دش برداشته شد."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "پنجره‌های باز"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "پنجرهٔ جدید"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "نمایش جزییات"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "خروج"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:250
+msgid "Unpin"
+msgstr "سنجاق نکردن"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "سنجاق به دش"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "اجرا با کارت گرافیک یکپارچه"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "اجرا با کارت گرافیک جداگانه"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "گزینش افزارهٔ صوتی"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "تنظیمات صدا"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "هدفون‌ها"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "هدست"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "میکروفون"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "تغییر پس‌زمینه…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "تنظیمات نمایشگر"
+
+#: js/ui/backgroundMenu.js:17 subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "تنظیمات"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "45"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ی"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "د"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "س"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "چ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "پ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "آ"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ش"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %OY"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "ماه پیش"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "ماه بعد"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%Od"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "هفته %OV"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "بدون آگاهی"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "مزاحم نشوید"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "پاک‌سازی"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» پاسخگو نیست."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the application to "
+"quit entirely."
+msgstr ""
+"ممکن است بخواهید مدت کوتاهی منتظر مانده تا ادامه دهد یا برنامه را مجبور به خروج "
+"کامل کنید."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "خروج اجباری"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "انتظار"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "گردانندهٔ خارجی وصل شد"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "اتّصال گردانندهٔ خارجی قطع شد"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "ناتوانی در قفل‌گشایی حجم"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "نگارش نصب‌شدهٔ udisk از تنظیمات PIM پشتیبانی نمی‌کند"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "گشودن با %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid "Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "به عنوان جایگزین می‌توانید با فشردن دکمهٔ WPS روی مسیریابتان، وصل شوید."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "اتّصال"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "کلید"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "گذرواژهٔ کلید خصوصی"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "شناسه"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "خدمت"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "نیاز به تأیید هویت است"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid "Passwords or encryption keys are required to access the wireless network “%s”."
+msgstr "برای دسترسی به شبکهٔ وای‌فای «%s» نیاز به گذرواژه یا کلیدهای رمزنگاری است."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "تأیید هویت 802.1X سیمی"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "نام شبکه"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "تأیید هویت DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "رمز پین لازم است"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "رمز پین برای دستگاه پهن‌باند همراه لازم است"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "پین"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "برای اتّصال به «%s» گذرواژه لازم است."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1960
+msgid "Network Manager"
+msgstr "مدیر شبکه"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "گذرواژهٔ وی‌پی‌ان"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "تأیید هویت لازم است"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "مدیر"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "تأیید هویت"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "متاسفانه اثری نداشت! لطفاً دوباره تلاش کنید."
+
+# در این رشته از نویسه RLM استفاده شده است
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "‏%s با عنوان %s شناخته می‌شود"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "پنجره‌‌ها"
+
+#: js/ui/dash.js:206 js/ui/dash.js:252
+msgid "Show Applications"
+msgstr "نمایش برنامه‌ها"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:399
+msgid "Dash"
+msgstr "دَش"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-Od %OB %OY"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %Od %OB %OY"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-Od %OB"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-Od %OB %OY"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "امروز"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "فردا"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "تمام روز"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "بدون رویداد"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "افزودن ساعت‌های جهانی…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "ساعت‌های جهانی"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "درحال بار کردن…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "برای اطّلاعات آب‌وهوا برخط شوید"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "اطّلاعات آب‌وهو درحال حاضر موجود نیست"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "آب‌وهوا"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "گزینش مکان آب‌وهوا…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "خروج از %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "خروج"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s به طور خودکار پس از %Id ثانیه از سامانه خارج خواهد شد."
+msgstr[1] "%s به طور خودکار پس از %Id ثانیه از سامانه خارج خواهد شد."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "پس از %Id ثانیه به طور خودکار از سامانه خارج می‌شوید."
+msgstr[1] "پس از %Id ثانیه به طور خودکار از سامانه خارج می‌شوید."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "خروج"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "خاموش کردن"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "نصب به‌روز رسانی‌ها و خاموش کردن"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "سامانه پس از %Id ثانیه به طور خودکار خاموش می‌شود."
+msgstr[1] "سامانه پس از %Id ثانیه به طور خودکار خاموش می‌شود."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "نصب به‌روز رسانی‌های منتظر"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "خاموش کردن"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "راه‌اندازی دوباره"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "نصب به‌روز رسانی‌ها و شروع دوباره"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "سامانه پس از %Id ثانیه به طور خودکار دوباره راه‌اندازی می‌شود."
+msgstr[1] "سامانه پس از %Id ثانیه به طور خودکار دوباره راه‌اندازی می‌شود."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "راه‌اندازی دوباره"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "راه‌اندازی دوباره و نصب به‌روز رسانی‌ها"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"سامانه پس از %Id ثانیه به طور خودکار دوباره راه‌اندازی می‌شود و به‌روز رسانی‌ها را نصب "
+"می‌کند."
+msgstr[1] ""
+"سامانه پس از %Id ثانیه به طور خودکار دوبارها راه‌اندازی می‌شود و به‌روز رسانی‌ها را نصب "
+"می‌کند."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "راه‌اندازی دوباره و نصب"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "نصب و خاموش کردن"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "خاموش کردن پس از نصب به‌روز رسانی‌ها"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "راه‌اندازی دوباره و نصب ارتقا"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long time: "
+"ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"پس از راه‌اندازی دوباره %s %s نصب خواهد شد. نصب ارتقا ممکن است زمان زیادی طول بکشد: "
+"مطمئن شوید که پشتیبان دارید و رایانه به برق وصل است."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "باتری کم است: لطفاً پیش از نصب به‌روز رسانی‌ها، به برق بزنید."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "برخی برنامه‌ها مشغول بوده یا کار ذخیره نشده دارند"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "کاربران دیگری وارد شده هستند"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "گزینه‌های راه‌اندازی"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (دوردست)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (پایانه)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "نصب"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "نصب افزونه"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "بارگیری و نصب «%s» از extensions.gnome.org؟"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "به‌روز رسانی‌های افزونه موجودند"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "به‌روز رسانی‌های افزونه‌ها آمادهٔ نصبند."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "اجازه به جلوگیری از میان‌بر ها"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "برنامهٔ %s می‌خواهد جلوی میان‌برها را بگیرد"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "برنامه‌ای می‌خواهد جلوی میان‌برها را بگیرد"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "می‌توانید با فشردن %s میان‌برها را برگردانید."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "رد کردن"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "اجازه دادن"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "کلیدهای آهسته روشن شدند"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "کلیدهای آهسته خاموش شدند"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the Slow "
+"Keys feature, which affects the way your keyboard works."
+msgstr ""
+"دکمهٔ تبدیل را ۸ ثانیه نگه داشتید. این میان‌بری برای ویژگی دکمه‌های آهسته است که بر "
+"نحوهٔ کار صفحه‌کلیدتان اثر می‌گذارد."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "کلیدهای چسبان روشن شدند"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "کلیدهای چسبان خاموش شدند"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for the "
+"Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"کلید تبدیل را ۵ بار پشت سر هم زدید. این میان‌بری برای ویژگی کلید‌های چسبان است که بر "
+"نحوهٔ کار صفحه‌کلیدتان اثر می‌گذارد."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a row. This "
+"turns off the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"دو کلید را هم‌زمان فشرده یا کلید تبدیل را ۵ بار پشت سر هم زدید. این میان‌بری برای "
+"ویژگی کلید‌های چسبان است که بر نحوهٔ کار صفحه‌کلیدتان اثر می‌گذارد."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "روشن گذاشتن"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "روشن کردن"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "خاموش کردن"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "خاموش گذاشتن"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "تنظیمات ناحیه و زبان"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "هیچ افزونه‌ای نصب نشده است"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "افزونه %s هیچ خطایی منتشر نکرده است."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "نهفتن خطاها"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "نمایش خطاها"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "به کار افتاده"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "از کار افتاده"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "خطا"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "قدیمی"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "درحال بارگیری"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "نمایش منبع"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "صفحهٔ وب"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "سامانه در حالت ناامن گذاشته شده بود"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "برنامه اکنون دسترسی نامحدود دارد"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "برگردان"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "واردشده به عنوان کاربری ممتاز"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security reasons. If "
+"possible, you should log in as a normal user."
+msgstr ""
+"به دلایل امنیتی باید از اجرای یک نشست به عنوان کاربری ممتاز خودداری کرد. در صورت "
+"امکان، با کاربری عادی وارد شوید."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "قفل صفحه از کار افتاده"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "قفل صفحه نیاز به مدیر نمایش گنوم دارد."
+
+#: js/ui/messageTray.js:1417
+msgid "System Information"
+msgstr "اطّلاعات سامانه"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "هنرمند ناشناس"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "عنوان ناشناس"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "نوشتن برای جست‌وجو"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "برنامه‌ها"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "نمای کلّی"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "میان‌بر جدید…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "تعریف شده در برنامه"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "نمایش راهنمای روی صفحه"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "تعویض نمایشگر"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "تخصیص دنبالهٔ کلید"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "انجام شد"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "ویرایش…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "هیچ‌کدام"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "برای پیکربندی، کلیدی را بزنید"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "برای خروج، گریز را بزنید"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "برای خروج، هر کلیدی را بزنید"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "فعّالیت‌ها"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "سامانه"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "نوار بالا"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "اجرای یک فرمان"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "برای بستن، گریز را بزنید"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "راه‌اندازی دوباره در وی‌لند موجود نیست"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "درحال راه‌اندازی دوباره…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "لازم است گنوم صفحه را قفل کند"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "نمی‌توان قفل کرد"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "قفل توسط یک برنامه مسدود شده بود"
+
+#: js/ui/screenshot.js:1165
+msgid "Selection"
+msgstr "گزینش"
+
+#: js/ui/screenshot.js:1175
+msgid "Area Selection"
+msgstr "گزینش ناحیه"
+
+#: js/ui/screenshot.js:1180
+msgid "Screen"
+msgstr "صفحه"
+
+#: js/ui/screenshot.js:1190
+msgid "Screen Selection"
+msgstr "گزینش صفحه"
+
+#: js/ui/screenshot.js:1195
+msgid "Window"
+msgstr "پنجره‌"
+
+#: js/ui/screenshot.js:1205
+msgid "Window Selection"
+msgstr "گزینش پنجره"
+
+#: js/ui/screenshot.js:1243
+msgid "Screenshot / Screencast"
+msgstr "نماگرفت / ضبط صفحه"
+
+#: js/ui/screenshot.js:1279
+msgid "Show Pointer"
+msgstr "نمایش اشاره‌گر"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1870
+msgid "Screencasts"
+msgstr "ضبط‌های صفحه"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1875
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "ضبط صفحه از %t %d.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1917 js/ui/screenshot.js:2129
+msgid "Screenshot"
+msgstr "نماگرفت"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1923
+msgid "Screencast recorded"
+msgstr "صفحه ضبط شد"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1925
+msgid "Click here to view the video."
+msgstr "برای دیدن ویدیو این‌جا بزنید."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1928 js/ui/screenshot.js:2143
+msgid "Show in Files"
+msgstr "نمایش در پرونده‌ها"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2089
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "نماگرفت از %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2135
+msgid "Screenshot captured"
+msgstr "نماگرفت ضبط شد"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2137
+msgid "You can paste the image from the clipboard."
+msgstr "می‌توانید تصویر را از تخته‌گیره بچسبانید."
+
+#: js/ui/screenshot.js:2190 js/ui/screenshot.js:2355
+msgid "Screenshot taken"
+msgstr "نماگرفت انجام شد"
+
+#: js/ui/search.js:809
+msgid "Searching…"
+msgstr "درحال جست‌وجو…"
+
+#: js/ui/search.js:811
+msgid "No results."
+msgstr "بدون نتیجه."
+
+#: js/ui/search.js:942
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%Id بیش‌تر"
+msgstr[1] "%Id بیش‌تر"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "جست‌وجو"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "رونوشت"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "جای‌گذاری"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "نمایش متن"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "نهفتن متن"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "قفل تبدیل روشن است."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "حجم مخفی"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "حجم سامانه‌ای ویندوز"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "از پرونده‌های کلید استفاده می‌کند"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid "To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"برای قفل‌گشایی حجمی که از پرونده‌های کلید استفاده می‌کند، از ابزار <i>%s</i> استفاده "
+"کنید."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "شمارهٔ PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "به‌خاطر سپردن گذرواژه"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "قفل‌گشایی"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "گشودن %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM باید یک شماره یا خالی باشد."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "نمی‌توان %s را آغاز کرد"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "نمی‌توان برنامهٔ %s را یافت"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "دسترسی‌پذیری"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "کنتراست بالا"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "بزرگنمایی"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "صفحه‌نمایش خوان"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "صفحه‌کلید مجازی"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "هشدارهای نمایشی"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "کلیدهای چسبان"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "کلیدهای آهسته"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "کلیدهای پرشی"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "کلیدهای موشی"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "متن درشت"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "چرخاندن خودکار"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "بلوتوث"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "روشنایی"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "حالت تیره"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "تک کلیک"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "دوبار کلیک"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "کشیدن"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "کلیک دوم"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "کلیک طولانی"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "صفحه‌کلید"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "نمایش چیدمان صفحه‌کلید"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "اجازهٔ دسترسی به مکان"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "برنامهٔ %s می‌خواهد به مکانتان دسترسی داشته باشد"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "دسترسی به مکان در هر زمانی می‌تواند از طریق تنظیمات حریم‌خصوصی تغییر کند."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<ناشناخته>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "قطع شدن از %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "وصل شدن به %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1113
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "نقطهٔ داغ %s"
+
+#: js/ui/status/network.js:1472 js/ui/status/network.js:1488
+msgid "VPN"
+msgstr "وی‌پی‌ان"
+
+#: js/ui/status/network.js:1473
+msgid "VPN Settings"
+msgstr "تنظیمات وی‌پی‌ان"
+
+#: js/ui/status/network.js:1722
+msgid "Wi–Fi"
+msgstr "وای‌فای"
+
+#: js/ui/status/network.js:1724
+msgid "All Networks"
+msgstr "همهٔ شبکه‌ها"
+
+#: js/ui/status/network.js:1821
+msgid "Wired Connections"
+msgstr "اتّصال‌های سیمی"
+
+#: js/ui/status/network.js:1822
+msgid "Wired Settings"
+msgstr "تنظیمات اتّصال سیمی"
+
+#: js/ui/status/network.js:1836
+msgid "Bluetooth Tethers"
+msgstr "چترهای بلوتوث"
+
+#: js/ui/status/network.js:1837
+msgid "Bluetooth Settings"
+msgstr "تنظیمات بلوتوث"
+
+#: js/ui/status/network.js:1851
+msgid "Mobile Connections"
+msgstr "اتّصال‌های همراه"
+
+#: js/ui/status/network.js:1853
+msgid "Mobile Broadband Settings"
+msgstr "تنظیمات پهن‌باند همراه"
+
+#: js/ui/status/network.js:1965
+msgid "Connection failed"
+msgstr "اتّصال شکست خورد"
+
+#: js/ui/status/network.js:1966
+msgid "Activation of network connection failed"
+msgstr "فعّال‌سازی اتّصال شبکه شکست خورد"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "نور شب"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "کارایی"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "تعادل"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "ذخیرهٔ نیرو"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "نمایه‌های نیرو"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "توقّف ضبط صفحه"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "توقّف هم‌رسانی صفحه"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "حالت هواپیمایی"
+
+# در این ترجمه از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "⁦%Id٪⁩"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "گرفتن نماگرفت"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "فهرست خاموشی"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "تعلیق"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "شروع دوباره…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "خاموش کردن…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "خروج…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "تعویض کاربر…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "قفل صفحه"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "تاندربولت"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "افزارهٔ تاندربولت ناشناخته"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and reconnect "
+"the device to start using it."
+msgstr ""
+"هنگام نبودنتان، افزارهٔ جدید شناسایی شد. لطفاً برای شروع به استفاده از آن، قطعش کرده "
+"و دوباره وصل کنید."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "افزارهٔ تاندربولت تأییدنشده"
+
+#: js/ui/status/thunderbolt.js:322
+msgid "New device has been detected and needs to be authorized by an administrator."
+msgstr "افزارهٔ جدیدی شناسایی شده و نیازه به تأیید هویت توسّط یک مدیر دارد."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "خطای تأیید هویت تاندربولت"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "نمی‌توان افزارهٔ تاندربولت را تأیید هویت کرد: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "حجم صدا تغییر کرد"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "حجم صدا"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "خروجی صدا"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "ورودی صدا"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "آینه"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "پیوند نمایشگرها"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "فقط خارجی"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "فقط داخلی"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-Od %OB"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "برای قفل‌گشایی، بالا بکشید"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "برای قفل‌گشایی، کلیک کرده یا دکمه‌ای را بزنید"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "گشودن قفل پنجره"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "ورود به عنوان کاربری دیگر"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "به گنوم %s خوش آمدید"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "اگر می‌خواهید راهتان را در اطراف بیاموزید، گردشمان را بررسی کنید."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "نه، ممنون"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "گردش کردن"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» آماده است"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "تنظیمات نمایشگر نگه‌داشته شود؟"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "بازنشانی تنظیمات"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "نگه‌داری تغییرات"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "تغییرات تنظیمات در %Id ثانیه بازنشانی می‌شود"
+msgstr[1] "تغییرات تنظیمات در %Id ثانیه بازنشانی می‌شود"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%Id × %Id"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "نهفتن"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "بازآوری"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "بیشینه کردن"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "جابه‌جایی"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "تغییر اندازه"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "جابه‌جایی نوار عنوان روی صفحه"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "همیشه در بالا"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "همیشه روی فضای کاری قابل دیدن"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "انتقال به فضای کاری چپ"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "انتقال به فضای کاری راست"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "انتقال به فضای کاری بالا"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "انتقال به فضای کاری پایین"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "انتقال به نمایشگر بالا"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "انتقال به نمایشگر پایین"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "انتقال به نمایشگر چپ"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "انتقال به نمایشگر راست"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "بستن"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "تقویم اوولوشن"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "چاپ نگارش"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "حالت استفاده شده توسط GDM برای صفحهٔ ورود"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "استفاده از یک حالت مشخص، مثلاً «gdm» برای صفحهٔ ورود"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "فهرست کردن حالت‌های ممکن"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "ناشناس"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "راه‌اندازی «%s» شکست خورد"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "گذرواژه‌های منطبق نیستند."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "گذرواژه نمی‌تواند خالی باشد"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "محاوره تأیید هویت از طرف کاربر رد شد"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "افزونه‌ها"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "مدیریت افزونه‌های پوستهٔ گنوم"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "پروژهٔ گنوم"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension preferences and "
+"removing or disabling unwanted extensions."
+msgstr ""
+"افزونه‌های گنوم؛ به‌روز رسانی، پیکربندی، برداشتن یا از کار انداختن افزونه‌ها را مدیریت "
+"می‌کند."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "پیکربندی افزونه‌های پوستهٔ گنوم"
+
+#: subprojects/extensions-app/js/main.js:130 subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "بدون مطابقت"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "برداشتن «%s»؟"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want to "
+"enable it again"
+msgstr "اگر افزونه را بردارید، باید برای به کار انداختن دوباره‌اش، بارگیریش کنید"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "برداشتن"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"دانیال بهزادی <dani.behzi@ubuntu.com>\n"
+"سید اسحاق مقیم شهیدانی <eshagh094@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "تعداد %Id افزونه در ورود بعدی به‌روز خواهد شد."
+msgstr[1] "تعداد %Id افزونه در ورود بعدی به‌روز خواهند شد."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "افزونه با نگارش جاری گنوم ناسازگار است"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "افزونه خطایی داشت"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "افزونه می‌تواند به‌روز شود"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "پایگاه وب"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "برداشتن…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "راهنما"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "دربارهٔ افزونه‌ها"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if you "
+"encounter problems with your system."
+msgstr ""
+"افزونه‌ها می‌توانند باعث مشکلات پایداری و کارایی شوند. اگر مشکلاتی با سامانه‌تان "
+"یافتید، افزونه‌ها را از کار بیندازید."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "نصب‌شده به صورت دستی"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"برای یافتن و افزودن افزونه‌ها، <a href=\"https://extensions.gnome.org\">پایگاه "
+"افزونه‌های گنوم</a> را ببینید."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "توکار"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "هیچ افزونه‌ای نصب نشده"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed extensions. "
+"Make sure you are logged into GNOME and try again."
+msgstr ""
+"متأسفیم، ولی امکان گرفتن فهرست افزونه‌های نصب‌شده نبود. مطمئن شوید که به گنوم وارد "
+"شده‌اید و دوباره تلاش کنید."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "به‌روز رسانی‌های افزونه آماده‌اند"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "افزونهٔ جدید با موفقبت در %s ایجاد شد.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"نام باید رشته‌ای بسیار کوتاه و توصیفی باشد.\n"
+"مانند: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "نام"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"شرح تک‌جمله‌ای است که توضیح می‌دهد افزونه‌تان چه می‌کند.\n"
+"مانند: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "شرح"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe.example."
+"com)\n"
+msgstr ""
+"شناسهٔ یکتا، شناسهٔ بی‌تکراری برای افزونه‌تان است.\n"
+"این شناسه باید در قالب یک نشانی رایانامه باشد (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "گزینش یکی از الگوهای موجود:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "الگو"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "شناسهٔ یکتای افزونهٔ جدید"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "نام قابل مشاهده توسّط کاربر برای افزونهٔ جدید"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPTION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "شرح کوتاهی از کارکرد افزونه"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "TEMPLATE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "الگوی استفاده شده برای افزونهٔ جدید"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "اطّلاعات افزونه را به صورت تعاملی وارد کنید"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "ایجاد افزونه‌ای جدید"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "آرگومان ناشناخته"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "شناسهٔ یکتا، نام و شرح مورد نیازند"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "افزونهٔ «%s» وجود ندارد\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "از کار انداختن یک افزونه"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "هیچ شناسهٔ یکتایی داده نشده"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "بیش از یک شناسهٔ یکتا داده شده"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "به کار انداختن یک افزونه"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "شکست در وصل شدن به پوستهٔ گنوم\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "افزونهٔ «%s» وجود ندارد\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "نمایش اطّلاعات افزونه"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "بازنویسی روی افزونهٔ موجود"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "نصب یک دستهٔ افزونه"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "هیچ دستهٔ افزونه‌ای مشخّص نشده است"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "بیش از یک دسته افزونه مشخّص شده است"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "نمایش افزونه‌های نصب شده توسّط کاربر"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "نمایش افزونه‌های نصب شده توسّط سامانه"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "نمایش افزونه‌های به کار افتاده"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "نمایش افزونه‌های از کار افتاده"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "نمایش افزونه‌های با ترجیحات"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "نمایش افزونه‌های با به‌روز رسانی"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "چاپ جزییات افزونه"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "فهرست افزونه‌های نصب‌شده"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "منبع اضافی برای قرار گرفتن در دسته"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "یک شمای Gsettings که باید شامل شود"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "شاخه‌ای که ترجمه‌ها پیدا می‌شوند"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "دامنهٔ gettext برای استفاده توسّط ترجمه‌ها"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "بازنویسی روی یک بستهٔ موجود"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "شاخه‌ای که بسته باید ایجاد شود"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "SOURCE_DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "ایجاد یک دستهٔ افزونه"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "بیش از یک شاخهٔ منبع مشخّص شده است"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "افزونهٔ «%s» ترجیحات ندارد\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "شکست در گشودن ترجیحات برای افزونهٔ «%s»: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "ترجیحات افزونه را می‌گشاید"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "بازنشانی یک افزونه"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "نمی‌توان افزونه‌های سامانه‌ای را حذف نصب کرد\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "شکست در حذف نصب «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "حذف نصب یک افزونه"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "چاپ نکردن پیام‌های خطا"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "مسیر"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "نشانی"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "نگارندهٔ اصلی"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "نگارش"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "وضعیت"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» آرگومانی نمی‌گیرد"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "استفاده:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "چاپ اطّلاعات نگارش و خروج."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMMAND"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "دستورات:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "چاپ راهنما"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "به کار انداختن افزونه"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "از کار انداختن افزونه"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "بازنشانی افزونه"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "حذف نصب افزونه"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "فهرست افزونه‌ها"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "نمایش اطّلاعات افزونه"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "گشودن ترجیحات افزونه"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "ایجاد افزونه"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "بسته‌بندی افزونه"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "نصب دستهٔ افزونه"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "برای گرفتن راهنمای با جزییات از «%s» استفاده کنید.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "خام"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "افزونه‌ای خالی"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "نشانگر"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "افزودن نقشکی به نوار بالا"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%Iu خروجی"
+msgstr[1] "%Iu خروجی"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%Iu ورودی"
+msgstr[1] "%Iu ورودی"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "صداهای سامانه"
+
+#~ msgid "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "اینکه آداپتور بلوتوث پیش‌گزیده دستگاه‌هایی متّصل به خود دارد یا خیر"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default adapter. "
+#~ "This will be reset if the default adapter is ever seen not to have devices "
+#~ "associated to it."
+#~ msgstr ""
+#~ "پوسته تنها زمانی فهرست بلوتوث را نمایش می‌دهد که آداپتور بلوتوث روشن باشد، یا "
+#~ "دستگاه‌هایی به گونه‌ای تنظیم شده باشند که به آداپتور پیش‌گزیده متّصل شوند."
+
+#~ msgid "Log Out"
+#~ msgstr "خروج"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "وصل شدن به اینترنت"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "حالت هواپیمایی روشن است"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "در زمان روشن بودن حالت هواپیمایی، وای‌فای از کار می‌افتد."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "خاموش کردن حالت هواپیمایی"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "وای‌فای خاموش است"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "برای وصل شدن به شبکه باید وای‌فای روشن شود."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "روشن کردن وای‌فای"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "شبکه‌های وای‌فای"
+
+#~ msgid "Select a network"
+#~ msgstr "شبکه‌ای برگزینید"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "برای خاموش کردن از کلید سخت‌افزاری استفاده کنید"
+
+#~ msgid "Select Network"
+#~ msgstr "گزینش شبکه"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "تنظیمات وای‌فای"
+
+#~ msgid "VPN Off"
+#~ msgstr "وی‌پی‌ان خاموش است"
+
+#~ msgid "Network Settings"
+#~ msgstr "تنظیمات شبکه"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "اتّصال وای‌فای %s"
+#~ msgstr[1] "اتّصال وای‌فای %s"
+
+#, javascript-format
+#~ msgid "%s Bluetooth Connection"
+#~ msgid_plural "%s Bluetooth Connections"
+#~ msgstr[0] "اتّصال بلوتوثی %s"
+#~ msgstr[1] "اتّصال‌های بلوتوثی %s"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s خاموش است"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s وصل‌شده"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s مدیریت نشده"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "درحال اتّصال %s"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s نیاز به تأیید هویت دارد"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "میان‌افزار برای %s موجود نیست"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s در دسترس نیست"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "اتّصال %s شکست خورد"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "سخت‌افزار %s از کار افتاده"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s از کار افتاده"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s متّصل نشد"
+
+#~ msgid "unknown"
+#~ msgstr "ناشناخته"
+
+#~ msgid "activating…"
+#~ msgstr "فعّال کردن…"
+
+#~ msgid "deactivating…"
+#~ msgstr "غیرفعّال کردن…"
+
+#~ msgid "deactivated"
+#~ msgstr "غیرفعّال شده"
+
+#~ msgid "connecting…"
+#~ msgstr "درحال اتّصال…"
+
+#~ msgid "authentication required"
+#~ msgstr "تأیید هویت لازم است"
+
+#~ msgid "connection failed"
+#~ msgstr "اتّصال شکست خورد"
+
+#~ msgid "Power Settings"
+#~ msgstr "تنظیمات انرژی"
+
+#~ msgid "Lock"
+#~ msgstr "قفل کردن"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "خاموش کردن / خروج"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%Id وصل‌شده"
+#~ msgstr[1] "%Id وصل‌شده"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "بلوتوث روشن"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "بلوتوث خاموش"
+
+#~ msgid "Location Enabled"
+#~ msgstr "مکان به کار افتاده"
+
+#~ msgid "Disable"
+#~ msgstr "از کار انداختن"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "تنظیمات محرمانگی"
+
+#~ msgid "Location In Use"
+#~ msgstr "مکان درحال استفاده است"
+
+#~ msgid "Location Disabled"
+#~ msgstr "مکان از کار افتاده"
+
+#~ msgid "Enable"
+#~ msgstr "به کار انداختن"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "نور شب از کار افتاده است"
+
+#~ msgid "Resume"
+#~ msgstr "از سرگیری"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "از کار انداختن تا فردا"
+
+#~ msgid "Fully Charged"
+#~ msgstr "کاملاً شارژ شده"
+
+#~ msgid "Not Charging"
+#~ msgstr "درحال شارژ نیست"
+
+#~ msgid "Estimating…"
+#~ msgstr "درحال برآورد…"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+# پدینگ برداشته شده. زیرا با صفر انگلیسی پر می‌شد
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "⁦%Id∶%Id⁩ باقی‌مانده (⁦%Id٪⁩)"
+
+# در این رشته از نویسه‌های ایزوله چپ‌به‌راست استفاده شده است
+# پدینگ برداشته شده. زیرا با صفر انگلیسی پر می‌شد
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "⁦%Id∶%Id⁩ تا شارژ کامل (⁦%Id٪⁩)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "صفحه هم‌رسانی می‌شود"
+
+#~ msgid "Turn off"
+#~ msgstr "خاموش کردن"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "برداشتن از مورد علاقه‌ها"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "افزودن به مورد علاقه‌ها"
+
+#~ msgid "Author"
+#~ msgstr "نگارنده"
+
+#~ msgid "Warning"
+#~ msgstr "هشدار"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If you "
+#~ "encounter problems with your system, it is recommended to disable all extensions."
+#~ msgstr ""
+#~ "افزونه‌ها می‌توانند موجب اشکال‌های سامانه‌ای مانند مشکلات اجرایی شوند. اگر با "
+#~ "مشکلاتی روی سامانه‌تان مواجه شدید، پیشنهاد می‌شود تمام افزونه‌ها را از کار بندازید."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "شکست در وصل شدن به پوستهٔ گنوم"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "به کار انداختن رابط برنامه‌نویسی درون‌نگر"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of the shell."
+#~ msgstr ""
+#~ "رابط برنامه‌‌نویسی دی‌باسی را به کار می‌اندازد که اجازهٔ درون‌نگری وضعیت برنامهٔ پوسته "
+#~ "را می‌دهد."
+
+#~ msgid "Minimize"
+#~ msgstr "کمینه کردن"
+
+#~ msgid "Unmaximize"
+#~ msgstr "نابیشینه کردن"
+
+#~ msgid "App Picker View"
+#~ msgstr "نمای گزینش برنامه"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "اندیس نمای گزیده در گزینشگر برنامه."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "برنامه‌های بیشتر استفاده شده در اینجا نمایش داده می‌شود"
+
+#~ msgid "Frequent"
+#~ msgstr "پُر استفاده"
+
+#~ msgid "All"
+#~ msgstr "همه"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A %-Od %OB"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A %-Od %OB %Y"
+
+#~ msgid "Off"
+#~ msgstr "خاموش"
+
+#~ msgid "On"
+#~ msgstr "روشن"
+
+#~ msgid "Copy Error"
+#~ msgstr "رونوشت از خطا"
+
+#~ msgid "Username…"
+#~ msgstr "نام‌کاربری…"
+
+#~ msgid "Password: "
+#~ msgstr "گذرواژه: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "ورود گذرواژه…"
+
+#~ msgid "Next"
+#~ msgstr "بعدی"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "ورود"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A، %Od %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%Id پیام جدید"
+#~ msgstr[1] "%Id پیام جدید"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%Id آگاهی جدید"
+#~ msgstr[1] "%Id آگاهی جدید"
+
+#~ msgid "Browse in Software"
+#~ msgstr "مرور در نرم‌افزارها"
+
+#~ msgid "Rename"
+#~ msgstr "تغییر نام"
+
+#~ msgid "Type again:"
+#~ msgstr "تلاش دوباره:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "تایید هویت برای شبکه بی‌سیم لازم است"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "گذرواژه شبکه پهن‌باند تلفن همراه"
+
+#~ msgid "Account Settings"
+#~ msgstr "تنظیمات حساب"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "قفل جهت"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging purposes"
+#~ msgstr ""
+#~ "کلیدهای مقید برای مکث کردن و اجرا کردن تمام دوغلوهای در حال اجرا، برای مصارف رفع "
+#~ "اشکال"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "استفاده از کدام صفحه‌کلید"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "نوع صفحه‌کلید برای استفاده."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "خطایی هنگام باز کردن محاوره‌ی ترجیحات برای %s رُخ داد:"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "خروج"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "تعویض کاربر"
+
+#~ msgid "%s all day."
+#~ msgstr "کل روز %s."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s سپس %s بعد."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s، بعد %s، در ادامه %s بعد."
+
+#~ msgid "Feels like %s."
+#~ msgstr "حسی شبیه به %s."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "تغییر مسیر برای تایید هویت وب"
+
+#~ msgid "Events"
+#~ msgstr "رویدادها"
+
+#~ msgid "Notifications"
+#~ msgstr "اعلان‌ها"
+
+#~ msgid "Hide tray"
+#~ msgstr "مخفی‌کردن سینی"
+
+#~ msgid "Status Icons"
+#~ msgstr "شمایل‌های وضعیت"
+
+#~ msgid "Media"
+#~ msgstr "رسانه"
+
+#~ msgid "Not In Use"
+#~ msgstr "در حال استفاده نیست"
+
+#~ msgid "%d x %d"
+#~ msgstr "%Id x %Id"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "نمایش هفته در تقویم"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "در صورت تنظیم بر روی «درست»، تاریخ هفتگی ایزو را در تقویم نشان می‌دهد."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "استفاده به عنوان اتصال اینترنت"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "گنوم‌شل (چینشگر wayland)"
+
+#~ msgid "Show the message tray"
+#~ msgstr "نمایش سینی پیام"
+
+#~ msgid "Captive Portal"
+#~ msgstr "پرتال درگیر"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%OH∶%OM"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%Ol∶%OM %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "۱ش"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "۲ش"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "۳ش"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "۴ش"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "۵ش"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "ج"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "ش"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "بدون برنامه‌ریزی"
+
+#~ msgid "This week"
+#~ msgstr "این هفته"
+
+#~ msgid "Next week"
+#~ msgstr "هفته آینده"
+
+#~ msgid "Removable Devices"
+#~ msgstr "دستگاه‌های جدا شدنی"
+
+#~ msgid "Eject"
+#~ msgstr "بیرون دادن"
+
+#~ msgid "Invitation"
+#~ msgstr "دعوتنامه"
+
+#~ msgid "Call"
+#~ msgstr "تماس"
+
+#~ msgid "File Transfer"
+#~ msgstr "انتقال پرونده"
+
+#~ msgid "Chat"
+#~ msgstr "گپ"
+
+#~ msgid "Unmute"
+#~ msgstr "باصدا"
+
+#~ msgid "Mute"
+#~ msgstr "بی‌صدا"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "دعوتنامه به %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "‏%s از شما دعوت می‌کند که به %s بپیوندید"
+
+#~ msgid "Decline"
+#~ msgstr "رد کردن"
+
+#~ msgid "Accept"
+#~ msgstr "پذیرفتن"
+
+#~ msgid "Video call from %s"
+#~ msgstr "تماس ویدئویی از طریق %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "تماس از طرف %s"
+
+#~ msgid "Answer"
+#~ msgstr "پاسخگویی"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "‏%s در حال ارسال %s به شما است"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "‏%s اجازه دسترسی برای دیدن زمان‌هایی که شما برخط هستید را دارد"
+
+#~ msgid "Authentication failed"
+#~ msgstr "تایید هویت شکست خورد"
+
+#~ msgid "Encryption error"
+#~ msgstr "خطا رمزنگاری"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "گواهینامه ارائه نشده"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "گواهینامه نامعتبر است"
+
+#~ msgid "Certificate expired"
+#~ msgstr "گواهینامه منقضی شده"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "گواهینامه فعال نشده"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "نام کارگزار گواهینامه نامنطبق است"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "اثرانگشت گواهینامه نامنطبق است"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "گواهینامه خود-امضا شده"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "وضعیت بر روی برون خط تنظیم شده"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "گواهینامه نامعتبر است"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "اتصال رد شده است"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "اتصال نمی‌تواند برقرار شود"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "اتصال از دست رفته است"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "این حساب قبلا به کارگزار متصل شده است"
+
+#~ msgid "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "اتصال توسط یک اتصال جدید که از منبع مشابه استفاده می‌کند، جایگزین شده است"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "حساب از قبل بر روی کارگزار وجود دارد"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "کارگزار در حال حاضر برای دست گرفتن اتصال بسیار مشغول است"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "گواهینامه لغو شده است"
+
+#~ msgid "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "گواهینامه از الگوریتم رمزی نامطمئنی استفاده می‌کند یا از نظر cryptography ضعیف است"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server certificate "
+#~ "chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "اندازه گواهینامه کارگزار، یا عمق حلقه‌ی گواهینامه کارگزار، از محدودیت اعمال شده "
+#~ "توسط کتابخانه cryptography تجاوز کرد"
+
+#~ msgid "Internal error"
+#~ msgstr "خطای داخلی"
+
+#~ msgid "View account"
+#~ msgstr "نمایش حساب"
+
+#~ msgid "Open Calendar"
+#~ msgstr "بازکردن تقویم"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "تنظیمات تاریخ و ساعت"
+
+#~ msgid "Open"
+#~ msgstr "بازکردن"
+
+#~ msgid "Clear Messages"
+#~ msgstr "پاک کردن پیام‌ها"
+
+#~ msgid "Notification Settings"
+#~ msgstr "تنظیمات اعلان‌ها"
+
+#~ msgid "Tray Menu"
+#~ msgstr "منو سینی"
+
+#~ msgid "No Messages"
+#~ msgstr "پیامی وجود ندارد"
+
+#~ msgid "Message Tray"
+#~ msgstr "سینی پیام"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%Id دستگاه متصل شد"
+#~ msgstr[1] "%Id دستگاه متصل شد"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "باتری"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "فهرست دسته‌هایی که باید به شکل پوشه نمایش داده شوند"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the application "
+#~ "view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "هر نام دسته در این فهرست، بجای اینکه پشت سر هم در نمای اصلی نمایش داده شود، "
+#~ "به‌عنوان پوشه در نمای برنامه‌ها نشان داده می‌شود."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "چیدمان دکمه‌ها در نوار عنوان"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when running "
+#~ "GNOME Shell."
+#~ msgstr ""
+#~ "این کلید، کلید org.gnome.desktop.wm.preferences را در زمان اجرای گنوم‌شل بازنویسی "
+#~ "می‌کند."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "با استفاده از جعبه‌ی بالا یک افزونه برای پیکربندی انتخاب کنید."
+
+#~ msgid "calendar:MY"
+#~ msgstr "تقویم:من"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>، <b>%OH:%OM</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%Od</b>، <b>%OH:%OM</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%Od</b> <b>%y</b>, <b>%OH:%OM</b> "
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "درخواست تایید هویت از %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "دستگاه %s می‌خواهد با این رایانه جفت شود"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "دستگاه %s می‌خواهد به سرویس «%s» دسترسی داشته باشد"
+
+#~ msgid "Grant this time only"
+#~ msgstr "موافقت تنها در همین زمان"
+
+#~ msgid "Reject"
+#~ msgstr "رد کردن"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "تایید جهت جفت‌شدن با %s"
+
+#~ msgid "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "لطفا تایید کنید که آیا کلید عبور مربوط به «%I06d» با همتای آن در دستگاه مطابقت "
+#~ "دارد یا خیر."
+
+#~ msgid "Does not match"
+#~ msgstr "منطبق نیست"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "درخواست جفت‌شدن برای %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "لطفا PIN ذکر شده در دستگاه را وارد کنید."
+
+#~ msgid "OK"
+#~ msgstr "تایید"
+
+#~ msgid "unavailable"
+#~ msgstr "خارج از دسترس"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "شرمنده، هیچ تعبیری امروز برای شما وجود ندارد:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "‏%s پیشگو می‌گوید"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "اینکه اطلاعات برنامه‌ها درباره‌ی میزان استفاده از منابع جمع‌آوری شود یا خیر"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the most "
+#~ "used ones (e.g. in launchers). While this data will be kept private, you may "
+#~ "want to disable this for privacy reasons. Please note that doing so won't remove "
+#~ "already saved data."
+#~ msgstr ""
+#~ "پوسته گنوم در حالت عادی برنامه‌های فعال را جهت ارائه برنامه‌های بیشتر استفاده شده "
+#~ "پایش می کند. (برای مثال در اجرا کننده‌ها). با اینکه که این اطلاعات به صورت خصوصی "
+#~ "نگاه‌داری می‌شود، ممکن است شما بخواهید این امکان را به دلایل امنیتی غیرفعال کنید. "
+#~ "لطفا توجه کنید این کار باعث پاک شدن اطلاعاتی که تاکنون ذخیره شده‌اند نمی‌شود."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. The "
+#~ "value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "به‌طور داخلی جهت ذخیره‌ی آخرین وضعیتِ حاضرِ ثبت شده‌ی توسط کاربر استفاده می‌شود. مقدار "
+#~ "اینجا از محل محاسبه‌ی TpConnectionPresenceType است."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. The "
+#~ "value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "به‌طور داخلی برای ذخیره آخرین نشستی که کاربر در آن وضعیت حاضر داشته است استفاده "
+#~ "می‌شود. مقدار اینجا از محاسبه GsmPresenceStatus است."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "کلید مقید برای تغییر وضعیت ضبط کننده‌ی صفحه"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "کلید مقید برای شروع/توقف ضبط کننده‌ی صفحه پیش ساخته."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "سرعت فریم استفاده شده در تصویربرداری از صفحه‌نمایش."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's screencast "
+#~ "recorder in frames-per-second."
+#~ msgstr ""
+#~ "سرعت فریم حاصل از تصویربرداری از صفحه نمایش با استفاده از ضبط کننده نمایشگر "
+#~ "پوسته‌ی گنوم بر اساس فریم بر ثانیه"
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "مجرای ارتباطی gstreamer برای کدگذاری تصویربرداری از صفحه نمایش"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+#~ "used for gst-launch. The pipeline should have an unconnected sink pad where the "
+#~ "recorded video is recorded. It will normally have a unconnected source pad; "
+#~ "output from that pad will be written into the output file. However the pipeline "
+#~ "can also take care of its own output - this might be used to send the output to "
+#~ "an icecast server via shout2send or similar. When unset or set to an empty "
+#~ "value, the default pipeline will be used. This is currently 'vp8enc "
+#~ "min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! "
+#~ "queue ! webmmux' and records to WEBM using the VP8 codec. %T is used as a "
+#~ "placeholder for a guess at the optimal thread count on the system."
+#~ msgstr ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+#~ "used for gst-launch. The pipeline should have an unconnected sink pad where the "
+#~ "recorded video is recorded. It will normally have a unconnected source pad; "
+#~ "output from that pad will be written into the output file. However the pipeline "
+#~ "can also take care of its own output - this might be used to send the output to "
+#~ "an icecast server via shout2send or similar. When unset or set to an empty "
+#~ "value, the default pipeline will be used. This is currently 'vp8enc "
+#~ "min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! "
+#~ "queue ! webmmux' and records to WEBM using the VP8 codec. %T is used as a "
+#~ "placeholder for a guess at the optimal thread count on the system."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "پسوند پرونده‌ی قابل استفاده برای ذخیره تصویربرداری از صفحه‌نمایش"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on the "
+#~ "current date, and use this extension. It should be changed when recording to a "
+#~ "different container format."
+#~ msgstr ""
+#~ "نام پرونده‌ی ضبط شده برای تصویربرداری از صفحه‌نمایش یکتا و براساس تاریخ جاری خواهد "
+#~ "بود و از این افزونه استفاده خواهد کرد. اگر در زمان ضبط از قالب دیگری استفاده "
+#~ "کنید باید تغییر کند."
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "نشست..."
+
+#~ msgid "Power"
+#~ msgstr "انرژی"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%OH\\u2236%OM"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%Ol\\u2236%OM\\u2009%p"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "بر روی خروج از سیستم کلیک کنید تا از این برنامه‌ها خارج و از سیستم خارج شوید."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "درحال خروج از سیستم."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "بر روی خاموش کردن کلیک کنید تا از این برنامه‌ها خارج و سیستم خاموش شود."
+
+#~ msgid "Powering off the system."
+#~ msgstr "درحال خاموش کردن سیستم."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "بر روی راه‌اندازی مجدد کلیک کنید تا از این برنامه‌ها خارج و سیستم مجددا راه‌اندازی "
+#~ "گردد."
+
+#~ msgid "Restarting the system."
+#~ msgstr "درحال راه‌اندازی مجدد سیستم."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "تنظیمات دسترسی جامع"
+
+#~ msgid "Visibility"
+#~ msgstr "قابلیت دیدن"
+
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "برپاسازی یک دستگاه جدید..."
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "ارسال پرونده‌ها..."
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "تنظیمات صفحه‌کلید"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "تنظیمات موشی"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "جلد، شبکه، باتری"
+
+#~ msgid "disabled"
+#~ msgstr "از کار افتاد"
+
+#~ msgid "cable unplugged"
+#~ msgstr "کابل قطع شد"
+
+#~ msgid "More…"
+#~ msgstr "بیشتر..."
+
+#~ msgid "Wired"
+#~ msgstr "سیمی"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "اترنت خودکار"
+
+#~ msgid "Auto broadband"
+#~ msgstr "پهن‌باند خودکار"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "دایال‌آپ خودکار"
+
+#~ msgid "Auto %s"
+#~ msgstr "اتصال بیسیم %s خودکار"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "بلوتوث خودکار"
+
+#~ msgid "Auto wireless"
+#~ msgstr "بیسیم خودکار"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "وای-فای"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%Id %s %Id %s باقی‌مانده"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ساعت"
+#~ msgstr[1] "ساعت"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "%Id دقیقه‌"
+#~ msgstr[1] "%Id دقیقه‌"
+
+#~| msgid "AC adapter"
+#~ msgid "AC Adapter"
+#~ msgstr "آداپتور برق مستقیم"
+
+#~| msgid "Laptop battery"
+#~ msgid "Laptop Battery"
+#~ msgstr "باتری لپ‌تاپ"
+
+#~ msgid "Monitor"
+#~ msgstr "صفحه نمایش"
+
+#~ msgid "Mouse"
+#~ msgstr "موشی"
+
+#~ msgid "PDA"
+#~ msgstr "دستیار دیجیتال شخصی"
+
+#~| msgid "Cell phone"
+#~ msgid "Cell Phone"
+#~ msgstr "تلفن سلولی"
+
+#~| msgid "Media player"
+#~ msgid "Media Player"
+#~ msgstr "پخش‌کننده‌ی رسانه"
+
+#~ msgid "Tablet"
+#~ msgstr "لوح‌رایانه"
+
+#~ msgid "Computer"
+#~ msgstr "رایانه"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "ناشناس"
+
+#~ msgid "Available"
+#~ msgstr "در دسترس"
+
+#~ msgid "Busy"
+#~ msgstr "مشغول"
+
+#~ msgid "Invisible"
+#~ msgstr "نامرئی"
+
+#~ msgid "Away"
+#~ msgstr "غائب"
+
+#~ msgid "Idle"
+#~ msgstr "بی‌کار"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "وضعیت گپ شما «مشغول» تنظیم می‌شود"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online status has "
+#~ "been adjusted to let others know that you might not see their messages."
+#~ msgstr ""
+#~ "هم‌اکنون اعلان‌ها، از جمله پیام‌های گپ، غیرفعال هستند. وضعیتِ برخطِ شما به گونه‌ای "
+#~ "تنظیم شده است که به دیگران نشان دهد ممکن است شما پیام‌هایشان را نبینید."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "خاموش‌کردن ممکن است باعث شود که کارهای ذخیره نشده خود را از دست بدهند."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "ورود"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "برنامه‌ها"
+
+#~ msgid "SETTINGS"
+#~ msgstr "تنظیمات"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%OH:%OM"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%OI:%OM"
+
+#~ msgid "Subscription request"
+#~ msgstr "درخواست اشتراک"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "ارسال در <b>%OH:%OM</b> در <b>%A</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "ارسال در <b>%A</b>, <b>%B %d</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "اتصال به %s شکست خورد"
+
+#~ msgid "Reconnect"
+#~ msgstr "اتصال مجدد"
+
+#~ msgid "tray"
+#~ msgstr "سینی"
+
+#~ msgid "Browse Files..."
+#~ msgstr "مرور پرونده‌ها..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "خطا در مرور دستگاه"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "دستگاه درخواست شده قابلیت مرور ندارد، خطا: «%s»"
+
+#~ msgid "More..."
+#~ msgstr "بیشتر..."
+
+#~ msgid "Wireless"
+#~ msgstr "بیسیم"
+
+#~ msgid "VPN Connections"
+#~ msgstr "اتصال‌های وی‌پی‌ان"
+
+#~ msgid "System Settings"
+#~ msgstr "تنظیمات سیستم"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "تخم‌مرغ شانسی مورد علاقه شما"
+
+#~ msgid "United Kingdom"
+#~ msgstr "بریتانیا"
+
+#~ msgid "Default"
+#~ msgstr "پیش‌فرض"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "غیرفعال کردنِ تامین‌کننده‌گان OpenSearch"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "عدم توانایی در پیاده کردن «%s»"
+
+#~ msgid "Retry"
+#~ msgstr "سعی مجدد"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "محل‌ها و ابزارها"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Passphrase"
+#~ msgstr "عبارت‌عبور"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "نمایش ساعت همراه با ثانیه"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "در صورت تنظیم بر روی «درست»، ثانیه‌ها را در ساعت نشان می‌دهد."
+
+#~ msgid "Show date in clock"
+#~ msgstr "نمایش تاریخ در ساعت"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "اگر روی «درست» تنظیم شود، تاریخ را در کنار ساعت نشان می‌دهد."
+
+#~ msgid "CONTACTS"
+#~ msgstr "CONTACTS"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %Od %b %OH:%OM:%OS"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %Od %b %OH:%OM"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %OH:%OM:%OS"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %OH:%OM"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a Od% %b %OH:%OM:%OS"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %OH:%OM:%OS"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "گذرواژه‌ی نادرست، لطفا دوباره تلاش کنید"
+
+#~ msgid "%s is online."
+#~ msgstr "‏%s بر خط است."
+
+#~ msgid "%s is offline."
+#~ msgstr "‏%s برون خط است."
+
+#~ msgid "%s is away."
+#~ msgstr "‏%s غایب است."
+
+#~ msgid "%s is busy."
+#~ msgstr "‏%s مشغول است."
+
+#~ msgid "Hidden"
+#~ msgstr "نامرئی"
+
+#~ msgid "Power Off..."
+#~ msgstr "خاموش کردن..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "حساب‌های برخط"
+
+#~ msgid "Log Out..."
+#~ msgstr "خروج از سیستم..."
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "موارد اخیر"
+
+#~ msgid "Show password"
+#~ msgstr "نمایش گذرواژه"
+
+#~ msgid "If true, display onscreen keyboard."
+#~ msgstr "در صورت تنظیم بر روی «درست»، صفحه‌کلید مجازی نمایش داده می‌شود."
+
+#~ msgid "%s has finished starting"
+#~ msgstr "راه‌اندازی %s پایان یافته است"
diff --git a/po/fi.po b/po/fi.po
new file mode 100644
index 0000000..b703f42
--- /dev/null
+++ b/po/fi.po
@@ -0,0 +1,4027 @@
+# gnome-shell Finnish translation
+# Copyright (C) 2009-2011 Timo Jyrinki
+# This file is distributed under the same license as the gnome-shell package.
+#
+# IMPORTANT NOTICE!
+#
+# Some date / time strings are currently non-optimal. The optimal CLDR
+# compliant versions are included as comments in the respective strings.
+#
+# CLDR: http://kotoistus.fi/suositukset/vahv_kalenterit_cldr1_4.htm
+#
+# They should be taken into use as soon as the related bugs are fixed:
+# https://bugzilla.gnome.org/show_bug.cgi?id=647320
+# https://bugzilla.gnome.org/show_bug.cgi?id=648678
+#
+# Gnome 2012-03 Finnish translation sprint participants:
+# Helsingforce
+# Niklas Laxström
+# Timo Jyrinki <timo.jyrinki@iki.fi>, 2009-2011.
+# Marko Myllynen <myllynen@redhat.com>, 2011.
+# Ville-Pekka Vainio <vpvainio@iki.fi>, 2011.
+# Jiri Grönroos <jiri.gronroos+l10n@iki.fi>, 2012, 2013, 2014, 2015, 2016, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 05:16+0000\n"
+"PO-Revision-Date: 2023-02-14 09:17+0200\n"
+"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
+"Language-Team: suomi <lokalisointi-lista@googlegroups.com>\n"
+"Language: fi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Project-Style: gnome\n"
+"X-POT-Import-Date: 2012-03-05 15:06:10+0000\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Käynnistimet"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktivoi suosikkisovellus 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktivoi suosikkisovellus 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktivoi suosikkisovellus 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktivoi suosikkisovellus 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktivoi suosikkisovellus 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktivoi suosikkisovellus 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktivoi suosikkisovellus 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktivoi suosikkisovellus 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktivoi suosikkisovellus 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Kuvakaappaukset"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Ota kuvakaappaus vuorovaikutteisesti"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Ota kuvakaappaus"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Ota kuvakaappaus ikkunasta"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Nauhoita näyttötallennevideo vuorovaikutteisesti"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Järjestelmä"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Näytä ilmoitusluettelo"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Kohdista aktiiviseen ilmoitukseen"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Näytä yleisnäkymä"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Näytä kaikki sovellukset"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Avaa sovellusvalikko"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Gnome Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Ikkunanhallinta ja sovelluksien käynnistäminen"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Ota käyttöön sisäiset kehittäjiä ja testaajia hyödyttävät työkalut Alt-F2:sta"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Salli pääsy sisäiseen vianselvitys- ja monitorointityökaluun Alt-F2-ikkunan "
+"kautta."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Käyttöön otettavien laajennusten UUID:t"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOMEn ikkunanhallinnan laajennuksilla on uuid-tunnisteominaisuus, ja tämä "
+"avain luettelee laajennukset, jotka ladataan. Niiden laajennosten, jotka "
+"halutaan ladata, on oltava tässä luettelossa. Tätä luetteloa voi muokata "
+"myös org.gnome.Shell-DBus-metodeilla EnableExtension ja DisableExtension."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Käytöstä pakotetusti estettyjen laajennusten UUID:t"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOMEn ikkunanhallinnan laajennuksilla on uuid-tunnisteominaisuus, ja tämä "
+"avain luettelee laajennukset, joiden ei pitäisi olla käytössä, vaikka ne "
+"ladattaisiin osana nykyistä tilaa. Tätä luetteloa voi muokata myös org.gnome."
+"Shell-DBus-metodeilla EnableExtension ja DisableExtension. Tämä avain on "
+"etusijalla liitännäisten käyttöönoton asetukseen nähden."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Poista käytöstä käyttäjälaajennukset"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Poista käytöstä kaikki käyttäjän käyttöönottamat laajennukset vaikuttamatta "
+"“enabled-extension”-asetukseen."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Poistaa käytöstä laajennusten versioyhteensopivuuden tarkistuksen"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Gnome Shell lataa vain ne laajennukset, jotka väittävät tukevan käytössä "
+"olevaa Shell-versiota. Tämä valinta poistaa tarkistuksen käytöstä ja yrittää "
+"ladata kaikki laajennukset siitä huolimatta, mitä versiota laajennukset "
+"väittävät tukevansa."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Luettelo työpöytätiedostojen tunnisteista lempisovelluksille"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "Näitä tunnisteita vastaavat sovellukset näytetään suosikkien alueella."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historia komentoikkunalle (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historia näyttölasi-ikkunalle"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Näytä aina “Kirjaudu ulos”-toiminto käyttäjävalikossa."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Tämä avain ohittaa “Kirjaudu ulos”-valikkokohdan automaattisen piilotuksen "
+"\"yksi käyttäjä, yksi istunto\"-tilanteissa."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Muistetaanko salattujen tai etätiedostojärjestelmien liittämiseen tarvittava "
+"salasana"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Shell pyytää salasanaa, kun salattu laite tai etätiedostojärjestelmä "
+"liitetään. Jos salasana voidaan tallentaa tulevaa käyttöä varten, “Muista "
+"salasana”-valintaruutu näytetään. Tämä avain asettaa valintaruudun "
+"oletustilan."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Sovellusvalitsimen asettelu"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Sovellusvalikon avaukseen käytettävä pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Sovellusvalikon avaukseen käytettävä pikanäppäin."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Yleisnäkymän avaukseen käytettävä pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Istunnon, ikkunavalitsimen ja sovellusruudukon välillä vaihtoon käytettävä "
+"pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Sovellusruudukon, ikkunavalitsimen ja istunnon välillä vaihtoon käytettävä "
+"pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Näytä sovellukset -näkymän avaukseen käytettävä pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Yleisnäkymän Näytä sovellukset -näkymän avaukseen käytettävä pikanäppäin."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Yleisnäkymän avaukseen käytettävä pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Yleisnäkymän avaukseen käytettävä pikanäppäin."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Ilmoitusalueen näyttämiseen/piilottamiseen tarkoitettu pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Ilmoitusluettelon näyttämiseen/piilottamiseen tarkoitettu pikanäppäin."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Aktiiviseen ilmoitukseen kohdistava pikanäppäin"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Aktiiviseen ilmoitukseen kohdistava pikanäppäin."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Vaihda sovellukseen 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Vaihda sovellukseen 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Vaihda sovellukseen 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Vaihda sovellukseen 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Vaihda sovellukseen 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Vaihda sovellukseen 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Vaihda sovellukseen 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Vaihda sovellukseen 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Vaihda sovellukseen 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Rajoita vaihtaja nykyiseen työtilaan."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Jos tosi, näytetään vaihtajassa vain niiden sovellusten ikkunat, jotka "
+"sijaitsevat nykyisessä työtilassa. Muussa tapauksessa kaikki sovellukset "
+"sisällytetään."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Sovelluskuvakkeen tila."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Määrittää kuinka ikkunat näytetään ikkunavaihtimessa. Mahdolliset arvot ovat "
+"'thumbnail-only' (näyttää ikkunan pienoiskuvan), 'app-icon-only' (näyttää "
+"vain sovelluksen kuvakkeen) tai 'both'(molemmat)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Jos tosi,vain nykyisessä työtilassa olevat ikkunat näytetään vaihtajassa. "
+"Muussa tapauksessa kaikki ikkunat näytetään."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Sijainnit"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Maailmankellossa näytettävät sijainnit"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automaattinen sijainti"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Noudetaanko sijainti vai ei"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Sijainti"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Sijainti jonka ennuste näytetään"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Kiinnitä modaali-ikkuna isäntäikkunaan"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Tämä avain syrjäyttää avaimen org.gnome.mutter Gnome Shelliä käyttäessä."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Käytä reunakiinnitystä tiputtaessa ikkunoita näytön reunoille"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Työtilojen hallinta on dynaamista"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Työtilat ainoastaan ensisijaisella näytöllä"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Viivästytä kohdistuksen vaihtumista hiiritilassa, kunnes osoitin lopettaa "
+"liikkumisen"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Verkkokirjautuminen"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Jokin meni pieleen"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Ongelma havaittu: tämän laajennuksen asetuksia ei voi näyttää. Suosittelemme "
+"ilmoittamaan ongelmasta laajennuksen tekijälle."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Tekniset tiedot"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Verkkosivu"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Käy laajennuksen verkkosivulla"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Peru"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Salasana"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Valitse istunto"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Ei luettelossa?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(esim. käyttäjä tai %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Käyttäjätunnus"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Kirjautumisikkuna"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Tunnistautumisvirhe"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(tai pyyhkäise sormella sormenjälkilukijaa)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(tai aseta sormi sormenjälkilukijaan)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Sammuta"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;sammuta"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Käynnistä uudelleen"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;uudelleenkäynnistys;käynnistä uudelleen;boottaa;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Lukitse näyttö"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Kirjaudu ulos"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;kirjaudu ulos;uloskirjautuminen;poistu"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Valmiustila"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Vaihda käyttäjää"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "switch user"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;unlock orientation;screen;rotation;lukitse kierto;lukitse "
+"suunta;suunta;kääntö"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Ota kuvakaappaus"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;kuvakaappaus;näyttökaappaus;"
+"näyttövideo;nauhoita"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Vapauta näytön kierto"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Lukitse näytön kierto"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Komentoa ei löydy"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Komentoa ei voi jäsentää:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Kohteen ”%s” suorittaminen epäonnistui:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Juuri nyt"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minuutti sitten"
+msgstr[1] "%d minuuttia sitten"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d tunti sitten"
+msgstr[1] "%d tuntia sitten"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Eilen"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d päivä sitten"
+msgstr[1] "%d päivää sitten"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d viikko sitten"
+msgstr[1] "%d viikkoa sitten"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d kuukausi sitten"
+msgstr[1] "%d kuukautta sitten"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d vuosi sitten"
+msgstr[1] "%d vuotta sitten"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H.%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Eilen, %H.%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H.%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d. %Bta, %H.%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d. %Bta %Y, %H.%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Eilen, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Tukiasemaan kirjautuminen"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Yhteytesi tukiaseman kirjautumissivuun ei ole salattu. Salasanat ja muut "
+"välittämäsi tiedot voivat olla muiden ihmisten nähtävissä."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Estä pääsy"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Salli pääsy"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Nimetön kansio"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s on kiinnitetty telakkaan."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s ei ole enää kiinnitetty telakkaan."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Avoimet ikkunat"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Uusi ikkuna"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Näytä tiedot"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Lopeta"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Poista kiinnitys"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Kiinnitä telakkaan"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Käynnistä integroitua näytönohjainta käyttäen"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Käynnistä erillistä näytönohjainta käyttäen"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Valitse äänilaite"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Ääniasetukset"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Kuulokkeet"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Headset-kuulokkeet"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofoni"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Muuta työpöydän taustaa…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Näytön asetukset"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Asetukset"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "K"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "L"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Edellinen kuukausi"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Seuraava kuukausi"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Viikko %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Ei ilmoituksia"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Älä häiritse"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Tyhjennä"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” ei vastaa."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Voit odottaa hetken sovelluksen jatkamista tai pakottaa sovelluksen "
+"lopettamisen."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Pakota lopetus"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Odota"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Erillinen asema yhdistetty"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Erillinen asema irrotettu"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Taltion lukituksen avaaminen epäonnistui"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Asennettu udisks-versio ei tue PIM-asetusta"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Avaa käyttäen sovellusta %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Vaihtoehtoisesti voit yhdistää painamalla reitittimesi “WPS”-painiketta."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Yhdistä"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Avain"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Yksityisen avaimen salasana"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identiteetti"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Palvelu"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Tunnistautuminen vaaditaan"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "Langaton verkko \"%s\" vaatii salasanan tai salausavaimia."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Kiinteän 802.1X-yhteyden tunnistautuminen"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Verkon nimi"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL-tunnistautuminen"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN-koodi vaaditaan"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Mobiililaajakaista vaatii PIN-koodin"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Salasana vaaditaan kohteeseen \"%s\" yhdistämiseksi."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Verkon hallinta"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN-salasana"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Tunnistautuminen vaaditaan"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Ylläpitäjä"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Tunnistaudu"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Kirjautuminen epäonnistui. Yritä uudelleen."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s on nyt nimeltään %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Ikkunat"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Näytä sovellukset"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Pikavalikko"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d. %Bta %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e. %Bta %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%e. %Bta"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%e. %Bta %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Tänään"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Huomenna"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Koko päivä"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr ""
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Ei tapahtumia"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Lisää maailmankelloja…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Maailmankellot"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Ladataan…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Yhdistä verkkoon saadaksesi säätietoja"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Säätiedot eivät ole juuri nyt saatavilla"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Sää"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Valitse sään sijainti…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Kirjaa %s ulos"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Kirjaudu ulos"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s - kirjaudutaan ulos automaattisesti %d sekunnin kuluttua."
+msgstr[1] "%s - kirjaudutaan ulos automaattisesti %d sekunnin kuluttua."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Sinut kirjataan ulos automaattisesti %d sekunnin kuluttua."
+msgstr[1] "Sinut kirjataan ulos automaattisesti %d sekunnin kuluttua."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Kirjaudu ulos"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Sammuta"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Asenna päivitykset ja sammuta"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Järjestelmä sammuu automaattisesti %d sekunnin kuluttua."
+msgstr[1] "Järjestelmä sammuu automaattisesti %d sekunnin kuluttua."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Asenna odottavat ohjelmistopäivitykset"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Sammuta"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Käynnistä uudelleen"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Asenna päivitykset ja käynnistä uudelleen"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] ""
+"Järjestelmä käynnistyy automaattisesti uudelleen %d sekunnin kuluttua."
+msgstr[1] ""
+"Järjestelmä käynnistyy automaattisesti uudelleen %d sekunnin kuluttua."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Käynnistä uudelleen"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Käynnistä uudelleen ja asenna päivitykset"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Järjestelmä käynnistyy automaattisesti uudelleen ja asentaa päivitykset %d "
+"sekunnin kuluttua."
+msgstr[1] ""
+"Järjestelmä käynnistyy automaattisesti uudelleen ja asentaa päivitykset %d "
+"sekunnin kuluttua."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Käynnistä uudelleen ja asenna"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Asenna ja sammuta"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Sammuta päivitysten asennuksen jälkeen"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Käynnistä uudelleen ja asenna päivitys"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s asennetaan uudelleenkäynnistyksen jälkeen. Versiopäivityksen asennus "
+"voi kestää kauan: varmista varmuuskopioidesi ajantasaisuus ja toimivuus. "
+"Kiinnitä kone myös verkkovirtaan."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Laitteen akkuvirta vähissä: kiinnitä verkkovirtaan ennen päivitysten "
+"asennusta."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Jotkin sovellukset ovat kiireisiä tai sisältävät tallentamatonta työtä"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Muita käyttäjiä on kirjautuneena"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Käynnistysvalinnat"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (etä)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsoli)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Asenna"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Asenna laajennus"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Ladataanko ja asennetaanko ”%s” sivustolta extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Laajennusten päivityksiä saatavilla"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Laajennusten päivitykset ovat valmiina asennettavaksi."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Salli pikanäppäimien rajoittaminen"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Sovellus %s haluaa rajoittaa pikanäppäinten toimintaa"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Sovellus haluaa rajoittaa pikanäppäinten toimintaa"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Voit palauttaa pikanäppäinten toiminnan painamalla %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Kiellä"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Salli"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Hitaat näppäimet otettu käyttöön"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Hitaat näppäimet poistettu käytöstä"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Pidit juuri Shift-näppäintä pohjassa kahdeksan sekuntia. Se toimii "
+"pikanäppäimenä hitaiden näppäinten ominaisuudelle, joka taasen vaikuttaa "
+"näppäimistön toimintatapaan."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Pohjaan jäävät näppäimet otettu käyttöön"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Pohjaan jäävät näppäimet poistettu käytöstä"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Painoit juuri Shift-näppäintä viisi kertaa peräkkäin. Se toimii "
+"pikanäppäimenä pohjaan jäävien näppäinten ominaisuudelle, joka taasen "
+"vaikuttaa näppäimistön toimintatapaan."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Painoit juuri kahta näppäintä samanaikaisesti, tai painoit Shift-näppäintä "
+"viisi kertaa peräkkäin. Tämä kytkee pohjaan jäävien näppäinten ominaisuuden "
+"pois käytöstä, joka taasen vaikuttaa näppäimistön toimintatapaan."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Jätä käyttöön"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Ota käyttöön"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Sammuta"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Poista käytöstä"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Kielen & alueen asetukset"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Laajennuksia ei asennettu"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ei ole lähettänyt virheitä."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Piilota virheet"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Näytä virheet"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Käytössä"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Ei käytössä"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Virhe"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Ei ajan tasalla"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Noudetaan"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Näytä lähde"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Verkkosivusto"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Järjestelmä asetettiin turvattomaan tilaan"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Sovelluksilla on nyt rajoittamaton pääsy"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Kumoa"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Kirjautuneena etuoikeutettuna käyttäjänä"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Istunnon suorittamista etuoikeutettuna käyttäjänä tulisi välttää "
+"tietoturvasyistä. Jos mahdollista, kirjaudu tavallisena käyttäjänä."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Näytön lukitus pois käytöstä"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Näytön lukitus vaatii Gnomen kirjautumishallinnan."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Järjestelmän tiedot"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Tuntematon esittäjä"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Tuntematon kappale"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Kirjoita hakeaksesi"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Sovellukset"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Yleisnäkymä"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Uusi pikanäppäin…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Sovelluksen määrittämä"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Näytä ohje"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Vaihda näyttöä"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Määritä painallus"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Valmis"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Muokkaa…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Ei mitään"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Paina näppäintä määrittääksesi asetukset"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Paina Esc poistuaksesi"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Paina mitä tahansa näppäintä poistuaksesi"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Toiminnot"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Järjestelmä"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Yläpalkki"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Suorita komento"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Paina ESC poistuaksesi"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Uudelleenkäynnistys ei ole käytettävissä Waylandilla"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Käynnistetään uudelleen…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Gnomen täytyy lukita näyttö"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Lukitus epäonnistui"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Lukitus estettiin sovelluksen toimesta"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Valinta"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Aluevalinta"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Näyttö"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Näytön valinta"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Ikkuna"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Ikkunan valinta"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Kuvakaappaus / näyttötallennevideo"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Näytä osoitin"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Näyttötallennevideot"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Näyttötallennevideo - %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Kuvakaappaus"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Näyttötallennevideo otettu"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Napsauta tästä katsoaksesi videon."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Näytä tiedostohallinnassa"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Kuvakaappaus - %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Kuvakaappaus otettu"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Voit liittää kuvan leikepöydältä."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Kuvakaappaus otettu"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Etsitään…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Ei tuloksia."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d lisää"
+msgstr[1] "%d lisää"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Haku"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopioi"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Liitä"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Näytä teksti"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Piilota teksti"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps Lock on päällä."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Piilotettu taltio"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows-järjestelmätaltio"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Käyttää avaintiedostoja"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "Avataksesi avaintiedostoja käyttävän taltion, käytä <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-numero"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Muista salasana"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Avaa lukitus"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Avaa %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM tulee olla numeerinen tai tyhjä."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Sovelluksen %s käynnistäminen ei onnistunut"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Sovellusta %s ei löytynyt"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Esteettömyys"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Korkea kontrasti"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Lähennys"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Näytönlukija"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Näyttönäppäimistö"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Visuaaliset hälytykset"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Pohjaan jäävät näppäimet"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Hitaat näppäimet"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Kimmonäppäimet"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Hiirinäppäimet"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Suuri tekstin koko"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automaattinen kääntö"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Kirkkaus"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Tumma tila"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Kertanapsautus"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Kaksoisnapsautus"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Vedä"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Toissijainen napsautus"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Napsautus kohdistamalla"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Näppäimistö"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Näytä näppäimistön asettelu"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Salli sijainnin käyttö"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "%s haluaa käyttää sijaintitietoasi"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Sijaintitiedon käyttöoikeuksia voi muuttaa milloin tahansa "
+"yksityisyysasetuksista."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<tuntematon>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Katkaise yhteys kohteeseen %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Yhdistä kohteeseen %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s - Yhteyspiste"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "VPN-asetukset"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wifi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Kaikki verkot"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Kiinteät yhteydet"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Kiinteän yhteyden asetukset"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Bluetoothilla jaetut yhteydet"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Bluetooth-asetukset"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Mobiiliyhteydet"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Mobiililaajakaistan asetukset"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Yhteys epäonnistui"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Verkkoyhteyden aktivointi epäonnistui"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Yövalo"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Suorituskyky"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Tasapainotettu"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Virransäästö"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Virtaprofiilit"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Pysäytä näyttötallennevideo"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Lopeta näytön jakaminen"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Lentokonetila"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Ota kuvakaappaus"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Sammutusvalikko"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Valmiustila"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Käynnistä uudelleen…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Sammuta…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Kirjaudu ulos…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Vaihda käyttäjää…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Lukitse näyttö"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Tuntematon Thunderbolt-laite"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Uusi laite havaittiin, kun olit pois. Katkaise laitteen yhteys ja yhdistä se "
+"uudelleen, jotta voit aloittaa sen käyttämisen."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Thunderbolt-laite ilman valtuutusta"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Uusi laite on havaittu. Se tulee valtuuttaa ylläpitäjän toimesta."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt-valtuutusvirhe"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Thunderbolt-laitetta ei voitu valtuuttaa: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Äänenvoimakkuutta muutettu"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Äänenvoimakkuus"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Äänen ulostulo"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Äänen sisääntulo"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Peili"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Liitä näytöt"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Vain ulkoinen"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Vain sisäinen"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-e. %Bta"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Vedä ylös avataksesi lukituksen"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Napsauta tai paina näppäintä avataksesi lukituksen"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Lukituksen avausikkuna"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Kirjaudu toisena käyttäjänä"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Tervetuloa, tämä on Gnome %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Lähde tutustumiskierrokselle oppiaksesi oleelliset asiat."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ei kiitos"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Aloita tutustumiskierros"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” on valmis"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Haluatko säilyttää nämä näyttöasetukset?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Palauta asetukset"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Säilytä muutokset"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Asetusmuutokset palautetaan %d sekunnissa"
+msgstr[1] "Asetusmuutokset palautetaan %d sekunnissa"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Piilota"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Palauta"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Suurenna"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Siirrä"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Muuta kokoa"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Siirrä ikkunapalkki näytölle"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Aina päällimmäisenä"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Aina näkyvissä olevassa työtilassa"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Siirrä vasemmalla olevaan työtilaan"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Siirrä oikealla olevaan työtilaan"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Siirrä ylempänä olevaan työtilaan"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Siirrä alempana olevaan työtilaan"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Siirrä ylempänä olevaan näyttöön"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Siirrä alempana olevaan näyttöön"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Siirrä vasemmalla olevaan näyttöön"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Siirrä oikealla olevaan näyttöön"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Sulje"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution-kalenteri"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Tulosta versio"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "GDM:n kirjautumisruudussa käyttämä tila"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Käytä tiettyä tilaa (esim. “gdm”) kirjautumisnäkymää varten"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Listaa mahdolliset tilat"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Tuntematon"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Sovelluksen “%s” käynnistäminen epäonnistui"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Salasanat eivät täsmää."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Salasana ei voi olla tyhjä"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Käyttäjä poistui tunnistautumisvalintaikkunasta"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Laajennukset"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Hallitse Gnome-laajennuksia"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Gnome-projekti"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Gnomen laajennussovellus Laajennukset käsittelee laajennusten päivitykset, "
+"niiden asetukset ja sen avulla voi poistaa laajennuksia käytöstä tai "
+"kokonaan järjestelmästä."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Hallitse Gnome Shell -laajennuksia"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Ei tuloksia"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Poista “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Ota huomioon jos poistat laajennuksen; sinun tulee palata ja ladata se "
+"uudelleen, jos haluat sen uudelleen käyttöön"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Poista"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Jiri Grönroos"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d laajennus päivitetään seuraavan kerran, kun kirjaudut sisään."
+msgstr[1] "%d laajennusta päivitetään seuraavan kerran, kun kirjaudut sisään."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Laajennus ei ole yhteensopiva nykyisen Gnome-version kanssa"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Laajennuksessa ilmeni virhe"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Laajennus on päivitettävissä"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Verkkosivusto"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Poista…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Tuki"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Tietoja - Laajennukset"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Laajennukset voivat aiheuttaa suorituskyky- tai vakausongelmia. Poista "
+"laajennukset käytöstä, jos kohtaat ongelmia järjestelmän kanssa."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Manuaalisesti asennettu"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Etsi ja asenna laajennuksia osoitteessa <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Sisäänrakennettu"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Ei asennettuja laajennuksia"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Valitettavasti asennettujen laajennusten listaa ei voitu muodostaa. Varmista "
+"että olet kirjautunut Gnomeen ja yritä uudelleen."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Laajennuspäivitykset valmiina"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Uusi laajennus luotiin polkuun %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Nimen tulisi olla erittäin lyhyt, mieluiten kuvaava, merkkijono.\n"
+"Esimerkkejä: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nimi"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Kuvaus on yhden lauseen selvitys laajennuksen käyttötarkoituksesta.\n"
+"Esimerkkejä: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Kuvaus"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID on yksilöllinen tunniste laajennukselle.\n"
+"Tämän tulee olla sähköpostiosoitteen muodossa (clicktofocus@janedoe.example."
+"com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Valitse yksi mallipohjista:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Mallipohja"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Yksilöllinen tunniste uudelle laajennukselle"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NIMI"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Uuden laajennuksen käyttäjälle näkyvä nimi"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "KUVAUS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Lyhyt kuvaus siitä, mitä laajennus tekee"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MALLIPOHJA"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Mallipohja uudelle laajennukselle"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Kirjoita laajennuksen tiedot vuorovaikutteisesti"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Luo uusi laajennus"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Tuntemattomat argumentit"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, nimi ja kuvaus ovat pakollisia tietoja"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Laajennusta “%s” ei ole olemassa\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Poista laajennus käytöstä"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID:tä ei annettu"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Enemmän kuin yksi UUID annettu"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Ota laajennus käyttöön"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Gnome Shelliin yhdistäminen epäonnistui\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Laajennusta “%s” ei ole olemassa\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Näytä laajennuksen tiedot"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Korvaa olemassa oleva laajennus"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Asenna laajennuspaketti"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Laajennuspakettia ei määritetty"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Enemmän kuin yksi laajennuspaketti määritetty"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Näytä käyttäjän asentamat laajennukset"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Näytä järjestelmän asentamat laajennukset"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Näytä käytössä olevat laajennukset"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Näytä käytöstä poistetut laajennukset"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Näytä laajennukset, joilla on asetuksia"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Näytä laajennukset, joihin on päivityksiä"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Tulosta laajennuksen tiedot"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Listaa asennetut laajennukset"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "TIEDOSTO"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Lisälähde pakettiin sisällytettäväksi"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SKEEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Sisällytettävä GSettings-skeema"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "KANSIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Kansio, jossa kielikäännökset ovat"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Kielikäännöksiin käytettävä gettext-domain"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Korvaa olemassa oleva paketti"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Kansio, johon paketti luodaan"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "SOURCE_DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Luo laajennuspaketti"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Enemmän kuin yksi lähdekansio määritetty"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Laajennuksella “%s” ei ole asetuksia\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Laajennuksen “%s” asetusten avaaminen epäonnistui: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Avaa laajennuksen asetukset"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Nollaa laajennus"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Järjestelmän laajennuksia ei voi poistaa\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Ei voitu poistaa “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Poista laajennus"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Älä tulosta virheviestejä"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Polku"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL-osoite"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Alkuperäinen tekijä"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versio"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Tila"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” ei kelpuuta argumentteja"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Käyttö:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Tulosta versiotiedot ja poistu."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "KOMENTO"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Komennot:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Tulosta ohje"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Ota laajennus käyttöön"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Poista laajennus käytöstä"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Nollaa laajennus"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Poista laajennus"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Listaa laajennukset"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Näytä laajennuksen tiedot"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Avaa laajennuksen asetukset"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Luo laajennus"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Paketoi laajennus"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Asenna laajennuspaketti"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Suorita “%s” nähdäksesi yksityiskohtaisia ohjeita.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Yksinkertainen"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Tyhjä laajennus"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Ilmaisin"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Lisää kuvake yläpalkkiin"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u ulostulo"
+msgstr[1] "%u ulostuloa"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u sisääntulo"
+msgstr[1] "%u sisääntuloa"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Järjestelmän äänet"
+
+#~ msgid "Log Out"
+#~ msgstr "Kirjaudu ulos"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d yhdistetty"
+#~ msgstr[1] "%d yhdistetty"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth päällä"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth pois"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Sijainti käytettävissä"
+
+#~ msgid "Disable"
+#~ msgstr "Poista käytöstä"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Yksityisyysasetukset"
+
+#~ msgid "Location In Use"
+#~ msgstr "Sijainti käytössä"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Sijainti pois käytöstä"
+
+#~ msgid "Enable"
+#~ msgstr "Käytä"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s pois"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s - Yhdistetty"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s - Ei hallinnassa"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s - Yhdistetään"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s vaatii tunnistautumisen"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s - Laiteohjelmisto puuttuu"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s - Ei saatavilla"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s - Yhteys epäonnistui"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s - Laite poistettu käytöstä"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s - Poistettu käytöstä"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Yhdistä internetiin"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Lentokonetila on päällä"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Wifi on pois päältä lentokonetilan ollessa päällä."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Käytä lentokonetilaa"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wifi on pois"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Wifin tulee olla päällä, jotta yhteys verkkoon on mahdollinen."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Ota wifi käyttöön"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wifi-verkot"
+
+#~ msgid "Select a network"
+#~ msgstr "Valitse verkko"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Käytä laitepainiketta sammuttaaksesi"
+
+#~ msgid "Select Network"
+#~ msgstr "Valitse verkko"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wifin asetukset"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s - Ei yhdistetty"
+
+#~ msgid "connecting…"
+#~ msgstr "yhdistetään…"
+
+#~ msgid "authentication required"
+#~ msgstr "tunnistautuminen vaaditaan"
+
+#~ msgid "connection failed"
+#~ msgstr "yhteys katkesi"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN pois"
+
+#~ msgid "Network Settings"
+#~ msgstr "Verkkoasetukset"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s wifi-yhteys"
+#~ msgstr[1] "%s wifi-yhteyttä"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Yövalo pois käytöstä"
+
+#~ msgid "Resume"
+#~ msgstr "Palauta"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Poista käytöstä huomiseen asti"
+
+#~ msgid "Power Settings"
+#~ msgstr "Virranhallinta"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Ladattu täyteen"
+
+#~ msgid "Not Charging"
+#~ msgstr "Ei lataudu"
+
+#~ msgid "Estimating…"
+#~ msgstr "Arvioidaan…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d jäljellä (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d kunnes täynnä (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Näyttö jaetaan"
+
+#~ msgid "Turn off"
+#~ msgstr "Sammuta"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Lentokonetila on päällä"
+
+#~ msgid "Lock"
+#~ msgstr "Lukitse"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Sammuta / Kirjaudu ulos"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Poista suosikeista"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Lisää suosikkeihin"
+
+#~ msgid "Author"
+#~ msgstr "Tekijä"
+
+#~ msgid "Warning"
+#~ msgstr "Varoitus"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Laajennukset voivat aiheuttaa ongelmia järjestelmässä, myös "
+#~ "suorituskykyyn. Jos kohtaat ongelmia järjestelmän kanssa, on "
+#~ "suositeltavaa poistaa kaikki laajennukset käytöstä."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Gnome Shelliin yhdistäminen epäonnistui"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Käytä introspektiivistä API:a"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Ottaa käyttöön D-Bus-rajapinnan, joka mahdollistaa shellin sovellustilan "
+#~ "itsetutkiskelun."
+
+#~ msgid "Minimize"
+#~ msgstr "Pienennä"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Palauta koko"
+
+#~ msgid "App Picker View"
+#~ msgstr "Sovellusvalitsimen näkymä"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Parhaillaan valitun näkymän indeksi sovellusvalitsimessa."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Usein käytetyt sovellukset ilmestyvät tänne"
+
+#~ msgid "Frequent"
+#~ msgstr "Käytetyimmät"
+
+#~ msgid "All"
+#~ msgstr "Kaikki"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d. %Bta"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d. %Bta %Y"
+
+#~ msgid "Off"
+#~ msgstr "Pois"
+
+#~ msgid "On"
+#~ msgstr "Päällä"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopiointivirhe"
+
+#~ msgid "Username…"
+#~ msgstr "Käyttäjänimi…"
+
+#~ msgid "Password: "
+#~ msgstr "Salasana: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Kirjoita salasana…"
+
+#~ msgid "Next"
+#~ msgstr "Seuraava"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Kirjaudu sisään"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %e. %Bta"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d uusi viesti"
+#~ msgstr[1] "%d uutta viestiä"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d uusi ilmoitus"
+#~ msgstr[1] "%d uutta ilmoitusta"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Selaa ohjelmistokeskuksessa"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Lukitse näytön kierto"
+
+#~ msgid "Password:"
+#~ msgstr "Salasana:"
+
+#~ msgid "Type again:"
+#~ msgstr "Uudelleen:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Langaton verkko vaatii tunnistautumisen"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Mobiililaajakaistan verkkosalasana"
+
+#~ msgid "Rename"
+#~ msgstr "Nimeä uudelleen"
+
+#~ msgid "Account Settings"
+#~ msgstr "Tilin asetukset"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Kiertolukitus"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Mitä näppäimistöä käytetään"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Käytettävän näppäimistön tyyppi."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Kohteen %s asetusikkunaa ladataessa tapahtui virhe:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s koko päivän."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s. %s myöhemmin."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s. Sen jälkeen %s. Myöhemmin %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Tuntuu kuin %s."
+
+#~| msgid "Log Out"
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Kirjaudu ulos"
+
+#~| msgid "Switch User"
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Vaihda käyttäjää"
+
+#~ msgid "Hide tray"
+#~ msgstr "Piilota palkki"
+
+#~ msgid "Status Icons"
+#~ msgstr "Tilakuvakkeet"
+
+#~ msgid "Events"
+#~ msgstr "Tapahtumat"
+
+#~ msgid "Notifications"
+#~ msgstr "Ilmoitukset"
+
+#~ msgid "Media"
+#~ msgstr "Media"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Verkkotunnistautumisen uudelleenohjaus"
+
+#~ msgid "Not In Use"
+#~ msgstr "Ei käytössä"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Näytä viikonpäivä kalenterissa"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Jos tosi, näytä ISO-viikonpäivät kalenterissa."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Käytä internetyhteytenä"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Gnome Shell (Wayland-komposiointi)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d yhdistetty laite"
+#~ msgstr[1] "%d yhdistettyä laitetta"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Akku"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Näytä viestialue"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "su"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "ma"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "ti"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "ke"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "to"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "pe"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "la"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Ei merkintöjä"
+
+#~ msgid "This week"
+#~ msgstr "Tällä viikolla"
+
+#~ msgid "Next week"
+#~ msgstr "Ensi viikolla"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Irrotettavat laitteet"
+
+#~ msgid "Eject"
+#~ msgstr "Poista asemasta"
+
+#~ msgid "Invitation"
+#~ msgstr "Kutsu"
+
+#~ msgid "Call"
+#~ msgstr "Soita"
+
+#~ msgid "File Transfer"
+#~ msgstr "Tiedostonsiirto"
+
+#~ msgid "Chat"
+#~ msgstr "Keskustelu"
+
+#~ msgid "Unmute"
+#~ msgstr "Palauta ääni"
+
+#~ msgid "Mute"
+#~ msgstr "Vaimenna"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Kutsu huoneeseen %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s kutsuu sinut huoneeseen %s"
+
+#~ msgid "Decline"
+#~ msgstr "Kieltäydy"
+
+#~ msgid "Accept"
+#~ msgstr "Hyväksy"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Videopuhelu käyttäjältä %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Puhelu käyttäjältä %s"
+
+#~ msgid "Answer"
+#~ msgstr "Vastaa"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s on lähettämässä sinulle tiedostoa %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s haluaisi saada luvan nähdä, milloin olet linjoilla"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Tunnistautuminen epäonnistui"
+
+#~ msgid "Encryption error"
+#~ msgstr "Salausvirhe"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Varmennetta ei annettu"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Varmenteeseen ei luoteta"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Varmenne on vanhentunut"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Varmennetta ei ole aktivoitu"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Varmenteen konenimiristiriita"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Varmenteen sormenjälkiristiriita"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Varmenne on itseallekirjoitettu"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Tilaksi on asetettu ”poissa linjoilta”"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Varmenne ei kelpaa"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Yhteys on evätty"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Yhteyttä ei voida muodostaa"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Yhteys on katkennut"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Tämä tili on jo yhteydessä palvelimeen"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Yhteys on korvattu uudella samaa resurssia käyttävällä yhteydellä"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Tili on jo olemassa palvelimella"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Palvelin on tällä hetkellä liian kiireinen käsittelemään yhteyttä"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Varmenne on kumottu"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "Varmenne käyttää turvatonta salausmenetelmää"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Palvelinvarmenne tai palvelinvarmenneketju on liian pitkä "
+#~ "salauskirjastolle"
+
+#~ msgid "Internal error"
+#~ msgstr "Sisäinen virhe"
+
+#~ msgid "View account"
+#~ msgstr "Näytä tili"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Avaa kalenteri"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Ajan & päiväyksen asetukset"
+
+#~ msgid "Open"
+#~ msgstr "Avaa"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Tyhjennä viestit"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Ilmoitusasetukset"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Viestialuevalikko"
+
+#~ msgid "No Messages"
+#~ msgstr "Ei viestejä"
+
+#~ msgid "Message Tray"
+#~ msgstr "Viestialue"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Sijainnin suurin tarkkuustaso."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Painikkeiden järjestys otsikkopalkissa"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Tämä avain syrjäyttää avaimen org.gnome.desktop.wm.preferences Gnome "
+#~ "Shelliä käyttäessä."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Valitse muokattava laajennus alta."
+
+#~ msgid "unavailable"
+#~ msgstr "ei käytettävissä"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Luettelo luokista, jotka tulee esittää kansioina"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%e.</b> <b>%Bta</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%e.</b> <b>%Bta</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Valtuutuspyyntö kohteesta %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Laite %s haluaa muodostaa parituksen tämän tietokoneen kanssa"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Laite %s pyytää pääsyä palveluun %s"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Salli vain tällä kerralla"
+
+#~ msgid "Reject"
+#~ msgstr "Hylkää"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Paritusvahvistus kohteelle %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "Vahvista, täsmääkö koodi \"%06d\" laitteen koodin kanssa."
+
+#~ msgid "Does not match"
+#~ msgstr "Ei täsmää"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Parituspyyntö kohteelle %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Kirjoita laitteella mainittu PIN-koodi."
+
+#~ msgid "OK"
+#~ msgstr "OK"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Ei viisauksia tälle päivälle:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s, sanoo oraakkeli"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Settings Menu"
+#~ msgstr "Asetusvalikko"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Kerätäänkö sovellusten käytöstä tilastoja"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "Ikkunanhallinta seuraa normaalisti aktiivisia sovelluksia, jotta eniten "
+#~ "käytetyt voidaan esittää (esim. käynnistimissä). Vaikka nämä tiedot "
+#~ "pysyvät yksityisinä, voit silti haluta ottaa ominaisuuden pois käytöstä "
+#~ "yksityisyyden vuoksi. Huomaa että pois kytkeminen ei poista aiemmin "
+#~ "tallennettuja tietoja."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr ""
+#~ "Kuvakaappausvideon nauhoitukseen/lopetukseen tarkoitettu pikanäppäin"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr ""
+#~ "Kuvakaappausvideon nauhoitukseen/lopetukseen tarkoitettu pikanäppäin."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Kuvakaappausvideoiden kuvataajuus."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "Nauhoituksesta tuloksena saatavan kuvakaappausvideon kuvataajuus kun "
+#~ "käytössä on GNOMEn ikkunanhallinnan nauhoitin, kuvaa per sekunti."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "Gstreamer-liukuhihna jolla kuvakaappausvideot pakataan"
+
+#, fuzzy
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Asettaa GStreamer-liukuhihnan, jota käytetään nauhoitusten pakkaamiseen. "
+#~ "Asetus noudattaa gst-launch-komennon syntaksia. Liukuhihnalla tulee olla "
+#~ "yhdistämätön viemärialusta, jonne nauhoitettu video syötetään. "
+#~ "Normaalisti sillä on yhdistämätön lähde, jonka tuloste kirjoitetaan "
+#~ "tulostetiedostoon. Liukuhihna voi kuitenkin huolehtia itse tulosteesta - "
+#~ "tätä voidaan käyttää vaikkapa lähettämään tuloste Icecast-palvelimelle "
+#~ "shout2send- tai vastaavalla komponentilla. Jos tätä ei aseteta tai arvona "
+#~ "on tyhjää, käytetään oletusliukuhihnaa. Oletusliukuhihna on tällä "
+#~ "hetkellä ”videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! "
+#~ "webmmux” ja nauhoittaa WEBM-muotoon VP8-koodekilla. %T korvautuu "
+#~ "arvauksella parhaan suorituskyvyn antavasta säiemäärästä järjestelmässä."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Tiedostopääte kuvakaappausvideoiden tallentamiseen"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "Nauhoitetuille kuvakaappausvideoille valitaan yksikäsitteinen "
+#~ "tiedostonimi nykyisen päivämäärän ja tämän tiedostopäätteen mukaisesti. "
+#~ "Tiedostopääte tulisi vaihtaa mikäli nauhoitetaan eri tallennusmuotoon."
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "Istunto…"
+
+#~| msgid "Power Off"
+#~ msgid "Power"
+#~ msgstr "Virta"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Napsauta Kirjaudu ulos poistuaksesi näistä sovelluksista ja "
+#~ "kirjautuaksesi ulos järjestelmästä."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Kirjaudutaan ulos järjestelmästä."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Napsauta Sammuta poistuaksesi näistä sovelluksista ja sammuttaaksesi "
+#~ "järjestelmän."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Sammutetaan järjestelmää"
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Napsauta Käynnistä uudelleen poistuaksesi näistä sovelluksista ja "
+#~ "käynnistääksesi järjestelmän uudelleen."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Käynnistetään järjestelmää uudelleen."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Esteettömyysasetukset"
+
+#~ msgid "Visibility"
+#~ msgstr "Näkyvyys"
+
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Asenna uusi laite…"
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "Lähetä tiedostoja…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Näppäimistön asetukset"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Hiiren asetukset"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Äänenvoimakkuus, verkko, akku"
+
+#~ msgid "disabled"
+#~ msgstr "ei käytössä"
+
+#~ msgid "cable unplugged"
+#~ msgstr "kaapeli ei kytketty"
+
+#~ msgid "More…"
+#~ msgstr "Lisää…"
+
+#~ msgid "Wired"
+#~ msgstr "Kiinteä"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Automaattinen, kiinteä"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Automaattinen, mobiililaajakaista"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Automaattinen, puhelinverkko"
+
+#~ msgid "Auto %s"
+#~ msgstr "Automaattinen: %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Automaattinen: Bluetooth"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Automaattinen: langaton"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s jäljellä"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "tunti"
+#~ msgstr[1] "tuntia"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minuutti"
+#~ msgstr[1] "minuuttia"
+
+#~| msgid "AC adapter"
+#~ msgid "AC Adapter"
+#~ msgstr "Virtalähde"
+
+#~| msgid "Laptop battery"
+#~ msgid "Laptop Battery"
+#~ msgstr "Kannettavan akku"
+
+#~ msgid "Monitor"
+#~ msgstr "Näyttö"
+
+#~ msgid "Mouse"
+#~ msgstr "Hiiri"
+
+#~ msgid "PDA"
+#~ msgstr "Kämmentietokone"
+
+#~| msgid "Cell phone"
+#~ msgid "Cell Phone"
+#~ msgstr "Matkapuhelin"
+
+#~| msgid "Media player"
+#~ msgid "Media Player"
+#~ msgstr "Mediasoitin"
+
+#~ msgid "Tablet"
+#~ msgstr "Taulutietokone"
+
+#~ msgid "Computer"
+#~ msgstr "Tietokone"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Tuntematon"
+
+#~ msgid "Available"
+#~ msgstr "Tavoitettavissa"
+
+#~ msgid "Busy"
+#~ msgstr "Kiireinen"
+
+#~ msgid "Invisible"
+#~ msgstr "Näkymätön"
+
+#~ msgid "Away"
+#~ msgstr "Poissa"
+
+#~ msgid "Idle"
+#~ msgstr "Jouten"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Pikaviestitilaksi asetetaan ”kiireinen”"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Ilmoitukset, kuten keskusteluviestit, ovat nyt poissa käytöstä. "
+#~ "Pikaviestitilaasi on muutettu, jotta muut tietävät, että et ehkä näe "
+#~ "heidän viestejään."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr ""
+#~ "Sammuttaminen saattaa aiheuttaa muiden käyttäjien menettävän "
+#~ "tallentamattomia tietoja."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Kirjaudu sisään"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "SOVELLUKSET"
+
+#~ msgid "SETTINGS"
+#~ msgstr "ASETUKSET"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H.%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l.%M %p"
+
+#~ msgid "Subscription request"
+#~ msgstr "Tilauspyyntö"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Lähetetty <b>%Ana</b> kello <b>%H.%M</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Lähetetty <b>%Ana</b> <b>%d. %Bta</b> %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Yhteys kohteeseen %s katkesi"
+
+#~ msgid "Reconnect"
+#~ msgstr "Yhdistä uudelleen"
+
+#~ msgid "tray"
+#~ msgstr "ilmoitusalue"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Selaa tiedostoja…"
+
+#~ msgid "Error browsing device"
+#~ msgstr "Virhe selattaessa laitetta"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "Pyydettyä laitetta ei voida selata, virhe on ”%s”"
+
+#~ msgid "More..."
+#~ msgstr "Lisää…"
+
+#~ msgid "Wireless"
+#~ msgstr "Langaton"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN-yhteydet"
+
+#~ msgid "System Settings"
+#~ msgstr "Järjestelmän asetukset"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Suosikkiyllätyksesi"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Yhdistynyt kuningaskunta"
+
+#~ msgid "Default"
+#~ msgstr "Oletus"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "käytöstä poistetut OpenSearch-tarjoajat"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Laitteen ”%s” irrottaminen epäonnistui"
+
+#~ msgid "Retry"
+#~ msgstr "Yritä uudelleen"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "SIJAINNIT JA LAITTEET"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Näytä aika sekuntien kera"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Jos tosi, näytä sekunnit ajassa."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Näytä päivämäärä kellossa"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Jos tosi, näytä päivämäärä kellossa ajan lisäksi."
+
+#~ msgid "CONTACTS"
+#~ msgstr "YHTEYSTIEDOT"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %d.%m., %H.%M.%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %d.%m., %H.%M"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %H.%M.%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %H.%M"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %d.%m., %I.%M.%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %I.%M.%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Väärä salasana, yritä uudelleen"
+
+#~ msgid "%s is online."
+#~ msgstr "%s on linjoilla."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s on poissa linjoilta."
+
+#~ msgid "%s is away."
+#~ msgstr "%s on poissa."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s on kiireinen."
+
+#~ msgid "Hidden"
+#~ msgstr "Piilotettu"
+
+#~ msgid "Power Off..."
+#~ msgstr "Sammuta…"
+
+#~ msgid "Online Accounts"
+#~ msgstr "Verkkotilit"
+
+#~ msgid "Log Out..."
+#~ msgstr "Kirjaudu ulos…"
diff --git a/po/fr.po b/po/fr.po
new file mode 100644
index 0000000..1455c67
--- /dev/null
+++ b/po/fr.po
@@ -0,0 +1,3366 @@
+# French translations for gnome-shell package.
+# Copyright (C) 2009-2020 Listed translators
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Mathieu Bridon <bochecha@fedoraproject.org>, 2009.
+# Pablo Martin-Gomez <pablo.martin-gomez@laposte.net>, 2010.
+# Bruno Brouard <annoa.b@gmail.com>, 2011-2012.
+# Cyril Arnaud <cyril dot arnaud at gmail dot com>, 2011.
+# Luc Guillemin <luc.guillemin@gmail.com>, 2011.
+# Luc Pionchon <pionchon.luc@gmail.com>, 2011.
+# Pierre Henry <pierrehenry73@yahoo.fr>, 2012.
+# Mathieu Stumpf <psychoslave@culture-libre.org>, 2013.
+# Alain Lojewski <allomervan@gmail.com>, 2014-2018.
+# Erwan Georget <egeorget@opmbx.org>, 2016.
+# Julien Humbert <julroy67@gmail.com>, 2019.
+# Claude Paroz <claude@2xlibre.net>, 2010-2020.
+# Thibault Martin <mail@thibaultmart.in>, 2020.
+# Guillaume Bernard <associations@guillaume-bernard.fr>, 2021.
+# Charles Monzat <charles.monzat@free.fr>, 2016-2022.
+# Irénée THIRION <irenee.thirion@e.email>, 2022.
+# Alexandre Franke <afranke@gnome.org>, 2012-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master fr\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 05:16+0000\n"
+"PO-Revision-Date: 2023-03-01 14:36+0100\n"
+"Last-Translator: Alan Paris <Alan90000@yahoo.com>\n"
+"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 2.0.6\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Lanceurs"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Activer l’application favorite 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Activer l’application favorite 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Activer l’application favorite 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Activer l’application favorite 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Activer l’application favorite 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Activer l’application favorite 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Activer l’application favorite 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Activer l’application favorite 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Activer l’application favorite 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Captures d’écran"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Effectuer une capture d’écran interactivement"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Effectuer une capture d’écran"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Effectuer la capture d’écran d’une fenêtre"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Enregistrer une capture vidéo interactivement"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Système"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Afficher la liste des notifications"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Donner le focus à la notification active"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Afficher la vue d’ensemble"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Afficher toutes les applications"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Ouvrir le menu de l’application"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Shell de GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gestion des fenêtres et lancement des applications"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Activer les outils internes pour les développeurs et les testeurs depuis Alt-"
+"F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Autoriser l’accès aux outils internes de débogage et de surveillance grâce à "
+"la boîte de dialogue de lancement d’applications (Alt-F2)."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Identifiants UUID des extensions à activer"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Les extensions Shell de GNOME ont une propriété UUID. Cette clé énumère les "
+"extensions qui doivent être chargées. Toute extension qui voudrait être "
+"chargée doit être ajoutée dans cette liste. Vous pouvez aussi manipuler "
+"cette liste avec les méthodes DBus EnableExtension et DisableExtension de "
+"org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Identifiants UUID des extensions à désactiver de force"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Les extensions Shell de GNOME ont une propriété UUID. Cette clé énumère les "
+"extensions qui doivent être désactivées, même si elles sont chargées dans le "
+"mode actuel. Vous pouvez aussi manipuler cette liste avec les méthodes DBus "
+"EnableExtension et DisableExtension de org.gnome.Shell. Cette clé est "
+"prioritaire sur la clé « enabled-extensions »."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Désactiver les extensions utilisateur"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Désactiver l’ensemble des extensions activées par l’utilisateur sans "
+"affecter la propriété « enabled-extension »."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Désactive la vérification de compatibilité de version"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Shell de GNOME charge uniquement les extensions annonçant leur gestion de la "
+"version en cours. Activer cette option désactive cette vérification et "
+"essaie de charger toutes les extensions, sans considérer les versions "
+"qu’elles annoncent prendre en charge."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Liste d’identifiants de fichiers desktop pour les applications favorites"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Les applications correspondant à ces identifiants sont affichées dans la "
+"zone des favoris."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historique de la boîte de dialogue des commandes (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historique de la boîte de dialogue d’inspection"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+"Toujours afficher l’élément « Se déconnecter » dans le menu utilisateur."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Cette clé affiche l’élément « Se déconnecter » du menu utilisateur "
+"normalement caché automatiquement dans le cas d’une session ou d’un "
+"utilisateur unique."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Se souvenir des mots de passe pour les montages des systèmes de fichiers "
+"chiffrés ou distants"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Le shell demandera un mot de passe au montage d’un périphérique chiffré ou "
+"d’un système de fichiers distant. Si le mot de passe peut être enregistré "
+"pour une utilisation ultérieure, une case à cocher « Se souvenir du mot de "
+"passe » sera présente. Cette clé détermine l’état par défaut de cette case à "
+"cocher."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Le dernier profil d’énergie hors défaut sélectionné"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Certains systèmes prennent en charge plus de deux profils d’énergie. Afin de "
+"continuer à prendre en charge la bascule entre deux profils, cette clé "
+"enregistre le dernier sélectionné en dehors de celui par défaut."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"La dernière version pour laquelle le dialogue « Bienvenue dans GNOME » s’est "
+"affichée"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Cette clé détermine la version affichée dans le dialogue « Bienvenue dans "
+"GNOME ». Une chaîne vide représente la dernière version possible de GNOME et "
+"un nombre très élevé représentera des versions qui n’existent pas encore. Ce "
+"nombre très élevé peut être utilisé pour désactiver le dialogue."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Disposition du sélecteur d’applications"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Disposition du sélecteur d’applications. Chaque entrée du tableau est une "
+"page. Les pages sont stockées dans l’ordre dans lequel elles apparaissent "
+"dans Shell de GNOME. Chaque page contient une paire « identifiant "
+"d’application » → « données ». Actuellement, les valeurs suivantes sont "
+"stockées comme « données » : • « position » : la position de l’icône de "
+"l’application sur la page"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Combinaison de touches pour ouvrir le menu de l’application"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Combinaison de touches pour ouvrir le menu de l’application."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr ""
+"Combinaison de touches pour basculer entre les états de la vue d’ensemble"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Combinaison de touches pour basculer entre la session, le sélecteur de "
+"fenêtre et la grille des applications"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Combinaison de touches pour basculer entre la grille des applications, le "
+"sélecteur de fenêtre et la session"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+"Combinaison de touches pour ouvrir la vue « Afficher les applications »"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Combinaison de touches pour ouvrir la vue « Afficher les applications » de "
+"la vue d’ensemble des activités."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Combinaison de touches pour ouvrir la vue d’ensemble"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Combinaison de touches pour ouvrir la vue d’ensemble des activités."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Combinaison de touches pour inverser la visibilité de la liste des "
+"notifications"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Combinaison de touches pour inverser la visibilité de la liste des "
+"notifications."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Combinaison de touches pour donner le focus à la notification active"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Combinaison de touches pour donner le focus à la notification active."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Passer à l’application 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Passer à l’application 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Passer à l’application 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Passer à l’application 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Passer à l’application 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Passer à l’application 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Passer à l’application 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Passer à l’application 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Passer à l’application 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Limite le sélecteur de fenêtres à l’espace de travail actuel."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Si vrai, seules les applications ayant des fenêtres ouvertes sur l’espace de "
+"travail actuel sont affichées dans le sélecteur. Sinon, toutes les "
+"applications y sont incluses."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Le type d’icône des applications."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configure la façon dont les fenêtres sont affichées dans le sélecteur. Les "
+"choix possibles sont « thumbnail-only » (affiche une miniature de la "
+"fenêtre), « app-icon-only » (affiche uniquement l’icône de l’application), "
+"ou « both » (affiche les deux)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Si vrai, seules les fenêtres de l’espace de travail actuel sont affichées "
+"dans le sélecteur. Sinon, toutes les fenêtres y sont incluses."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Emplacements"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Les emplacements à afficher dans les horloges mondiales"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Emplacement automatique"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Indique s’il faut récupérer l’emplacement actuel ou non"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Emplacement"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "L’emplacement pour lequel afficher les prévisions"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Attacher les dialogues modaux à leur fenêtre parente"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Cette clé prend le pas sur la clé dans org.gnome.mutter lorsque Shell de "
+"GNOME est lancé."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Activer l’empilage des fenêtres déposées sur les bords de l’écran"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Les espaces de travail sont gérés dynamiquement"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Les espaces de travail sont uniquement sur l’écran principal"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Retarder les changements de focus en mode souris jusqu’à ce que le pointeur "
+"arrête de bouger"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Identification réseau"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Quelque chose s’est mal passé"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Nous sommes désolés, mais il y a eu un problème : les paramètres de cette "
+"extension ne peuvent être affichés. Nous vous recommandons de signaler ce "
+"problème aux auteurs de l’extension."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Détails techniques"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Site web"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visiter le site web de l’extension"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Annuler"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Mot de passe"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Choisir une session"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Absent de la liste ?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(par ex. utilisateur ou %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nom d’utilisateur"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Fenêtre de connexion"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Erreur d’authentification"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(ou faites glisser le doigt à travers le lecteur)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(ou placez le doigt sur le lecteur)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Éteindre"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "éteindre;fermer;débrancher;arrêter;stop"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Redémarrer"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "redémarrer;rallumer;reboot;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Verrouiller l’écran"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "verrouillage;verrouiller;écran;"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Fermer la session"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "fermer;session;déconnecter;"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Mettre en veille"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "mettre en veille;sommeil;"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Changer d’utilisateur"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "changer;utilisateur;"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "verrouillage;déverrouillage;orientation;écran;rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Effectuer une capture d’écran"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "capture d’écran;capture vidéo;couper;capturer;enregistrer"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Déverrouiller la rotation de l’écran"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Verrouiller la rotation de l’écran"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Commande non trouvée"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Impossible d’analyser la commande :"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Exécution de « %s » impossible :"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "À l’instant"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Il y a %d minute"
+msgstr[1] "Il y a %d minutes"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Il y a %d heure"
+msgstr[1] "Il y a %d heures"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Hier"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Il y a %d jour"
+msgstr[1] "Il y a %d jours"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Il y a %d semaine"
+msgstr[1] "Il y a %d semaines"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Il y a %d mois"
+msgstr[1] "Il y a %d mois"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Il y a %d an"
+msgstr[1] "Il y a %d ans"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Hier, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Hier, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Identification du point d’accès"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"La connexion à ce point d’accès n’est pas sécurisée. Les mots de passe et "
+"les autres informations que vous saisissez sur cette page peuvent être "
+"interceptés par des personnes autour de vous."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Refuser l’accès"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Accorder l’accès"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Dossier sans nom"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s a été épinglé au dash."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s a été désépinglé du dash."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Fenêtres ouvertes"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nouvelle fenêtre"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Afficher les détails"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Quitter"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Désépingler"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Épingler au Dash"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Démarrer en utilisant la carte graphique intégrée"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Démarrer en utilisant la carte graphique dédiée"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Sélectionner le périphérique audio"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Paramètres de son"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Casque audio"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Micro-casque"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Microphone"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Modifier l’arrière-plan…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Paramètres d’affichage"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Paramètres"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Mois précédent"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Mois suivant"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Semaine %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Aucune notification"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Ne pas déranger"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Effacer"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "« %s » ne répond pas."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Vous pouvez soit attendre un peu pour continuer, soit forcer l’application à "
+"quitter."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Forcer à quitter"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Attendre"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Disque externe connecté"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Disque externe déconnecté"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Impossible de déverrouiller le volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "La version installée de udisks ne gère pas le réglage PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Ouvrir avec %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Vous pouvez également vous connecter en appuyant sur le bouton « WPS » sur "
+"votre routeur."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Se connecter"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Clé"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Mot de passe de la clé privée"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identité"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Service"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Authentification nécessaire"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Il faut un mot de passe ou une clé de chiffrement pour accéder au réseau "
+"sans fil « %s »."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Authentification filaire 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nom du réseau"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Authentification DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Code PIN requis"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Un code PIN est nécessaire pour les connexions mobiles"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Un mot de passe est requis pour se connecter à « %s »."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Gestionnaire de réseau"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Mot de passe VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Authentification nécessaire"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrateur"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "S’authentifier"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Échec de l’authentification. Essayez à nouveau."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s est maintenant connu sous le nom de %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Fenêtres"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Afficher les applications"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Aujourd’hui"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Demain"
+
+# luc: Trying alternatives:
+# 123456789|123456789|
+# Toute la journée bad
+# Journée entière bad
+# Jour entier
+# La journée
+# Journée
+#
+# All Day
+#
+# le calendrier (mal) francisé :
+# http://blog.arkezis.com/wp-content/uploads/2011/04/Capture.png
+#
+# See also https://bugzilla.gnome.org/show_bug.cgi?id=658679
+#
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Journée"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Aucun évènement"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Ajouter des horloges mondiales…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Horloges mondiales"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Chargement…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Chercher les informations météorologiques en ligne"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Les informations météorologiques ne sont pas disponibles actuellement"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Météo"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Choisir un emplacement pour la météo…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Fermer la session de %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Fermer la session"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s sera déconnecté automatiquement dans %d seconde."
+msgstr[1] "%s sera déconnecté automatiquement dans %d secondes."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Vous allez être déconnecté automatiquement dans %d seconde."
+msgstr[1] "Vous allez être déconnecté automatiquement dans %d secondes."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Fermer la session"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Éteindre"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installer les mises à jour et éteindre"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Cet ordinateur s’éteindra automatiquement dans %d seconde."
+msgstr[1] "Cet ordinateur s’éteindra automatiquement dans %d secondes."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installer les mises à jour logicielles en attente"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Éteindre"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Redémarrer"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Installer les mises à jour et redémarrer"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Cet ordinateur redémarrera automatiquement dans %d seconde."
+msgstr[1] "Cet ordinateur redémarrera automatiquement dans %d secondes."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Redémarrer"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Redémarrer et installer les mises à jour"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "Le système redémarrera automatiquement dans %d seconde."
+msgstr[1] "Le système redémarrera automatiquement dans %d secondes."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Redémarrer et installer"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installer et éteindre"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Éteindre après l’installation des mises à jour"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Redémarrer et installer la mise à niveau"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s sera installé après le redémarrage. L’installation d’une mise à niveau "
+"peut prendre beaucoup de temps : vérifiez que vous disposez de sauvegardes "
+"et que l’ordinateur est branché sur le secteur."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Batterie faible : veuillez vous brancher avant d’installer les mises à jour."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+"Certaines applications sont occupées ou des documents ne sont pas enregistrés"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "D’autres utilisateurs sont connectés"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Options de démarrage"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (distant)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (console)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installer"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Installer l’extension"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Télécharger et installer « %s » à partir de extensions.gnome.org ?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Mises à jour d’extensions disponibles"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Des mises à jour d’extensions sont prêtes à être installées."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Permettre de neutraliser les raccourcis"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "L’application %s veut neutraliser les raccourcis"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Une application veut neutraliser les raccourcis"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Pour restaurer les raccourcis, appuyez sur %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Refuser"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Autoriser"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Touches lentes activées"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Touches lentes désactivées"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Vous venez d’appuyer pendant 8 secondes sur la touche Maj. Ceci est le "
+"raccourci d’activation des touches lentes et affecte la façon dont votre "
+"clavier réagit."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Touches rémanentes activées"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Touches rémanentes désactivées"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Vous venez d’appuyer 5 fois de suite sur la touche Maj. Ceci est le "
+"raccourci d’activation des touches rémanentes et affecte la façon dont votre "
+"clavier réagit."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Vous venez d’appuyer sur deux touches en même temps, ou 5 fois de suite sur "
+"la touche Maj. Ceci désactive la fonction touches rémanentes et affecte la "
+"façon dont votre clavier réagit."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Laisser activé"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Activer"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Éteindre"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Laisser éteint"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Paramètres de langue et région"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Aucune extension installée"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s n’a émis aucune erreur."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Masquer les erreurs"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Afficher les erreurs"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Activé"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Désactivé"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Erreur"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Périmé"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Téléchargement"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Afficher la source"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Page web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Le système a été mis en mode non sécurisé"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Les applications ont maintenant un accès illimité"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Annuler"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Connecté en tant qu’utilisateur privilégié"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Ouvrir une session en tant qu’utilisateur privilégié devrait être évité pour "
+"des raisons de sécurité. Si possible, connectez-vous comme utilisateur "
+"normal."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Verrouillage d’écran désactivé"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+"Le verrouillage de l’écran nécessite le gestionnaire d’affichage GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informations du système"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artiste inconnu"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Titre inconnu"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Saisissez votre recherche"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Applications"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Vue d’ensemble"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nouveau raccourci…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Applications définies"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Afficher l’aide à l’écran"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Changer d’écran"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Associer une touche"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Terminé"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Édition…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Aucun"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Appuyez sur un bouton pour le configurer"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Appuyez sur Échap pour quitter"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Appuyez sur une touche pour quitter"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Activités"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Système"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Barre supérieure"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Lancer une commande"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Appuyez sur Échap pour fermer"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Le redémarrage n’est pas disponible sur Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Redémarrage…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME a besoin de verrouiller l’écran"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Impossible de verrouiller"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Le verrouillage a été bloqué par une application"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Sélection"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Sélection de zone"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Écran"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Sélection d’écran"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Fenêtre"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Sélection de fenêtre"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Capture d’écran / capture vidéo"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Afficher le pointeur"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Captures vidéo"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Capture vidéo du %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Capture d’écran"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Capture vidéo enregistrée"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Cliquez ici pour visionner la vidéo."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Afficher dans Fichiers"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Capture d’écran du %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Capture d’écran effectuée"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Vous pouvez coller l’image depuis le presse-papiers."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Capture d’écran effectuée"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Recherche…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Aucun résultat."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d de plus"
+msgstr[1] "%d de plus"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Recherche"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copier"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Coller"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Afficher le texte"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Masquer le texte"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "La touche Verr. Maj. est enfoncée."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volume masqué"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volume système Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Utilise des fichiers de clés"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Pour déverrouiller un volume qui utilise des fichiers de clés, utilisez "
+"plutôt l’utilitaire <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Numéro PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Se souvenir du mot de passe"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Déverrouiller"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Ouvrir %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "Le numéro PIM doit être un nombre ou être vide."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Impossible de démarrer %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Impossible de trouver l’application %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accessibilité"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Contraste élevé"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zoom"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Lecteur d’écran"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Clavier visuel"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Alertes visuelles"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Touches rémanentes"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Touches lentes"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Touches rebond"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Touches de la souris"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Grand texte"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Rotation automatique"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Luminosité"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Mode sombre"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clic simple"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Double-clic"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Glisser"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clic secondaire"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clic prolongé"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Clavier"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Afficher la disposition du clavier"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Permettre l’accès à l’emplacement"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "L’application %s souhaite accéder à votre emplacement géographique"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Les règles d’accès à la localisation peuvent à tout moment être modifiées "
+"dans les paramètres de confidentialité."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<inconnu>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Se déconnecter de %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Se connecter à %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Point d’accès %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "Paramètres VPN"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi-Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Tous les réseaux"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Connexions filaires"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Paramètres filaire"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Partages de connexion via Bluetooth"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Paramètres Bluetooth"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Connexion à des appareils mobiles"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Paramètres connexion mobile"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Échec de connexion"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "L’activation de la connexion réseau a échoué"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Mode nuit"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Performance"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Équilibré"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Économie d’énergie"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Profils d’énergie"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Arrêter la capture vidéo"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Arrêter le partage d’écran"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Mode avion"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Effectuer une capture d’écran"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menu d’extinction"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Mettre en veille"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Redémarrer…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Éteindre…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Fermer la session…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Changer d’utilisateur…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Verrouiller l’écran"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Interface Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Périphérique Thunderbolt inconnu"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Un nouveau périphérique a été détecté pendant votre absence. Veuillez le "
+"débrancher et rebrancher avant de commencer à l’utiliser."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Périphérique Thunderbolt non autorisé"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Un nouvel appareil a été détecté et a besoin d’être autorisé par un "
+"administrateur."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Erreur d’autorisation Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Impossible d’autoriser le périphérique Thunderbolt : %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Volume modifié"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volume"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Sortie son"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Entrée son"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Cloner"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Joindre écrans"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Externe seulement"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Intégré seulement"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Faire glisser vers le haut pour déverrouiller"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Cliquer ou appuyer sur une touche pour déverrouiller"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Fenêtre de déverrouillage"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Se connecter en tant qu’autre utilisateur"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Bienvenue dans GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Si vous désirez en apprendre davantage, consultez la visite guidée."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Non merci"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Effectuer la visite guidée"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "« %s » est prêt"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Garder ces paramètres d’affichage ?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Restaurer les paramètres"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Conserver les modifications"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Les paramètres seront restaurés dans %d seconde"
+msgstr[1] "Les paramètres seront restaurés dans %d secondes"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Masquer"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restaurer"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximiser"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Déplacer"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Redimensionner"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Déplacer la barre de titre sur l’écran"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Toujours au premier plan"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Toujours sur l’espace de travail visible"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Déplacer vers l’espace de travail de gauche"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Déplacer vers l’espace de travail de droite"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Déplacer vers l’espace de travail supérieur"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Déplacer vers l’espace de travail inférieur"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Déplacer vers l’écran du haut"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Déplacer vers l’écran du bas"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Déplacer vers l’écran de gauche"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Déplacer vers l’écran de droite"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Fermer"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Agenda d’Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Afficher la version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Mode utilisé par GDM pour l’écran de connexion"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Utiliser un mode particulier, par ex. « gdm » pour l’écran de connexion"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Lister les modes possibles"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Inconnu"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Impossible de lancer « %s »"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Les mots de passe ne correspondent pas."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Le mot de passe ne peut pas être vide"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "La fenêtre d’authentification a été écartée par l’utilisateur"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensions"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Gérer vos extensions GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Le projet GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Extensions de GNOME gère la mise à jour des extensions, la configuration des "
+"préférences des extensions et la suppression ou la désactivation des "
+"extensions non désirées."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configurer les extensions Shell de GNOME"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Aucune correspondance"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Enlever « %s » ?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Si vous enlevez cette extension, vous devrez recommencer son téléchargement "
+"si vous souhaitez la réactiver plus tard"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Enlever"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Mathieu Bridon <bochecha@fedoraproject.org>\n"
+"Pablo Martin-Gomez <pablo.martin-gomez@laposte.net>\n"
+"Bruno Brouard <annoa.b@gmail.com>\n"
+"Cyril Arnaud <cyril dot arnaud at gmail dot com>\n"
+"Luc Guillemin <luc.guillemin@gmail.com>\n"
+"Luc Pionchon <pionchon.luc@gmail.com>\n"
+"Pierre Henry <pierrehenry73@yahoo.fr>\n"
+"Alexandre Franke <alexandre.franke@gmail.com>\n"
+"Mathieu Stumpf <psychoslave@culture-libre.org>\n"
+"Alain Lojewski <allomervan@gmail.com>\n"
+"Erwan Georget <egeorget@opmbx.org>\n"
+"Julien Humbert <julroy67@gmail.com>\n"
+"Claude Paroz <claude@2xlibre.net>\n"
+"Charles Monzat <charles.monzat@free.fr>\n"
+"Thibault Martin <thibaultamartin@gnome.org>\n"
+"Guillaume Bernard <associations@guillaume-bernard.fr>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d extension sera mise à jour à la prochaine connexion."
+msgstr[1] "%d extensions seront mises à jour à la prochaine connexion."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Cette extension est incompatible avec votre version de GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "L’extension a rencontré un problème"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "L’extension peut être mise à jour"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Site web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Enlever…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Aide"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "À propos des extensions"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Les extensions peuvent entraîner des problèmes de performances et de "
+"stabilité. Désactivez les extensions si vous rencontrez des problèmes avec "
+"votre système."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Installée manuellement"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Pour trouver et ajouter des extensions, visitez <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Intégrée"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Aucune extension installée"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Nous sommes désolés, mais il n’est pas possible d’obtenir la liste des "
+"extensions installées. Vérifiez que vous êtes bien connecté à GNOME et "
+"essayez encore une fois."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Mises à jour d’extensions prêtes"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "La nouvelle extension a bien été créée dans %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Le nom doit être une chaîne très courte (idéalement descriptive).\n"
+"Exemples : %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nom"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"La description est composée d’une seule phrase expliquant ce que fait votre "
+"extension.\n"
+"Exemples : %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Description"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"L’UUID est un identifiant globalement unique pour votre extension.\n"
+"Son format doit correspondre à celui d’une adresse électronique "
+"(clicpourfocus@toto.exemple.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Sélectionnez un des modèles disponibles :\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Modèle"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "L’identifiant unique de la nouvelle extension"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOM"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Le nom d’extension visible par l’utilisateur"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPTION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Une description courte de ce que fait l’extension"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODÈLE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Le modèle à utiliser pour la nouvelle extension"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Saisissez interactivement les informations de l’extension"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Créer une nouvelle extension"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Paramètres inconnus"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Les champs UUID, nom et description sont obligatoires"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "L’extension « %s » n’existe pas\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Désactiver une extension"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Aucun UUID indiqué"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Plus d’un UUID indiqué"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Activer une extension"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Erreur lors de la connexion à Shell de GNOME\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "L’extension « %s » n’existe pas\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Afficher les infos des extensions"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Écraser une extension existante"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_EMPAQUETÉE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Installer une extension empaquetée"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Aucune extension empaquetée indiquée"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Plus d’une extension empaquetée indiquée"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Afficher les extensions installées par l’utilisateur"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Afficher les extensions installées par le système"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Afficher les extensions actives"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Afficher les extensions inactives"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Afficher les extensions avec des préférences"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Afficher les extensions avec des mises à jour"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Afficher les détails de l’extension"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Afficher la liste des extensions installées"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FICHIER"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Source supplémentaire à inclure dans le paquet"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHÉMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Un schéma GSettings qui doit être inclus"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "RÉPERTOIRE"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Le répertoire où se trouvent les traductions"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAINE"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Le domaine gettext à utiliser pour les traductions"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Écraser un paquet existant"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Le répertoire dans lequel le paquet doit être créé"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "RÉPERTOIRE_SOURCE"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Créer une extension empaquetée"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Plus d’un répertoire source indiqué"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "L’extension « %s » n’a pas de préférences\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Impossible d’ouvrir les préférences de l’extension « %s » : %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Ouvre les préférences de l’extension"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Réinitialiser une extension"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Impossible de désinstaller les extensions système\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Impossible de désinstaller « %s »\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Désinstaller une extension"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ne pas afficher les messages d’erreur"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Chemin"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Auteur d’origine"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Version"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "État"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "« version » ne prend pas de paramètre"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Utilisation :"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Afficher les informations de version et quitter."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMMANDE"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[PARAMÈTRES…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Commandes :"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Afficher l’aide"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Activer l’extension"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Désactiver l’extension"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Réinitialiser l’extension"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Désinstaller l’extension"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Afficher la liste des extensions"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Afficher les infos de l’extension"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Ouvrir les préférences de l’extension"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Créer une extension"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Mettre en paquet l’extension"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Installer l’extension empaquetée"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Utilisez « %s » pour obtenir une aide détaillée.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Basique"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Une extension vide"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicateur"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Ajouter une icône dans la barre supérieure"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u sortie"
+msgstr[1] "%u sorties"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrée"
+msgstr[1] "%u entrées"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Sons système"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "Indique si l’adaptateur Bluetooth a des périphériques associés"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Le shell n’affichera d’élément de menu Bluetooth que si un adaptateur "
+#~ "Bluetooth est allumé ou s’il y a des périphériques associés avec "
+#~ "l’adaptateur par défaut. Cela sera réinitialisé si l’adaptateur par "
+#~ "défaut se retrouve sans aucun périphérique associé."
+
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d connecté"
+#~ msgstr[1] "%d connectés"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth activé"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth désactivé"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Localisation activée"
+
+#~ msgid "Disable"
+#~ msgstr "Désactiver"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Paramètres de confidentialité"
+
+#~ msgid "Location In Use"
+#~ msgstr "Localisation en cours d’utilisation"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Localisation désactivée"
+
+#~ msgid "Enable"
+#~ msgstr "Activer"
+
+#~ msgid "%s Off"
+#~ msgstr "%s éteint"
+
+#~ msgid "%s Connected"
+#~ msgstr "%s connecté"
+
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s non géré"
+
+#~ msgid "%s Connecting"
+#~ msgstr "Connexion de %s en cours"
+
+#~ msgid "%s Requires Authentication"
+#~ msgstr "Authentification nécessaire pour %s"
+
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Micrologiciel manquant pour %s"
+
+#~ msgid "%s Unavailable"
+#~ msgstr "%s non disponible"
+
+#~ msgid "%s Connection Failed"
+#~ msgstr "Échec de connexion à %s"
+
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Équipement %s désactivé"
+
+#~ msgid "%s Disabled"
+#~ msgstr "%s désactivé"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Se connecter à Internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Le mode avion est activé"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Le Wi-Fi est désactivé quand le mode avion est activé."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Désactiver le mode avion"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Le Wi-Fi est désactivé"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Le Wi-Fi a besoin d’être activé pour se connecter à un réseau."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Activer le Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Réseaux Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Choisir un réseau"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Utiliser l’interrupteur matériel pour éteindre"
+
+#~ msgid "Select Network"
+#~ msgstr "Sélectionner un réseau"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Paramètres Wi-Fi"
+
+#~ msgid "%s Not Connected"
+#~ msgstr "%s non connecté"
+
+#~ msgid "unknown"
+#~ msgstr "inconnu"
+
+#~ msgid "activating…"
+#~ msgstr "activation…"
+
+#~ msgid "deactivating…"
+#~ msgstr "désactivation…"
+
+#~ msgid "deactivated"
+#~ msgstr "désactivé"
+
+#~ msgid "connecting…"
+#~ msgstr "connexion…"
+
+#~ msgid "authentication required"
+#~ msgstr "authentification nécessaire"
+
+#~ msgid "connection failed"
+#~ msgstr "échec de connexion"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN désactivé"
+
+#~ msgid "Network Settings"
+#~ msgstr "Paramètres du réseau"
+
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s connexion Wi-Fi."
+#~ msgstr[1] "%s connexions Wi-Fi."
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Mode nuit désactivé"
+
+#~ msgid "Resume"
+#~ msgstr "Reprendre"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Désactiver jusqu’à demain"
+
+#~ msgid "Power Settings"
+#~ msgstr "Paramètres de gestion de l’énergie"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Charge complète"
+
+#~ msgid "Not Charging"
+#~ msgstr "Pas en charge"
+
+#~ msgid "Estimating…"
+#~ msgstr "Estimation en cours…"
+
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d restant (%d %%)"
+
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d avant chargement complet (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "L’écran est partagé"
+
+#~ msgid "Turn off"
+#~ msgstr "Éteindre"
+
+#~ msgid "Lock"
+#~ msgstr "Verrouiller"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Éteindre / Fermer la session"
+
+#~ msgid "Log Out"
+#~ msgstr "Fermer la session"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Enlever des favoris"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Ajouter aux favoris"
+
+#~ msgid "Author"
+#~ msgstr "Auteur"
+
+#~ msgid "Warning"
+#~ msgstr "Avertissement"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Les extensions peuvent poser des problèmes au système, y compris au "
+#~ "niveau des performances. Si vous pensez être dans ce cas, il est "
+#~ "recommandé de désactiver toutes les extensions."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Activer l’API d’introspection"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Active l’API D-Bus, qui permet d’introspecter l’état d’application du "
+#~ "shell."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Impossible de se connecter à Shell de GNOME"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimiser"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Réduire"
+
+#~ msgid "App Picker View"
+#~ msgstr "Vue du sélecteur d’applications"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Index de la vue actuellement sélectionnée dans le sélecteur "
+#~ "d’applications."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Les applications fréquemment utilisées apparaîtront ici"
+
+#~ msgid "Frequent"
+#~ msgstr "Fréquemment utilisées"
+
+#~ msgid "All"
+#~ msgstr "Toutes"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A %-d %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A %-d %B, %Y"
+
+#~ msgid "Off"
+#~ msgstr "Éteint"
+
+#~ msgid "On"
+#~ msgstr "Actif"
+
+#~ msgid "Copy Error"
+#~ msgstr "Copier l’erreur"
+
+#~| msgid "Username: "
+#~ msgid "Username…"
+#~ msgstr "Nom d’utilisateur…"
+
+#~ msgid "Password: "
+#~ msgstr "Mot de passe : "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Saisissez un mot de passe…"
+
+#~ msgid "Next"
+#~ msgstr "Suivant"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Se connecter"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nouveau message"
+#~ msgstr[1] "%d nouveaux messages"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d nouvelle notification"
+#~ msgstr[1] "%d nouvelles notifications"
+
+#~ msgid "Rename"
+#~ msgstr "Renommer"
+
+#~ msgid "Type again:"
+#~ msgstr "Saisissez à nouveau :"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "L’authentification est requise par le réseau sans fil"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Mot de passe de la connexion mobile"
+
+#~ msgid "Account Settings"
+#~ msgstr "Paramètres du compte"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Verrouillage de l’orientation"
diff --git a/po/fur.po b/po/fur.po
new file mode 100644
index 0000000..631188a
--- /dev/null
+++ b/po/fur.po
@@ -0,0 +1,3491 @@
+# Friulian translation for video-subtitles.
+# Copyright (C) 2016 video-subtitles's COPYRIGHT HOLDER
+# This file is distributed under the same license as the video-subtitles package.
+# Fabio Tomat <f.t.public@gmail.com>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: video-subtitles master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-27 11:20+0000\n"
+"PO-Revision-Date: 2022-10-25 02:41+0200\n"
+"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
+"Language-Team: Friulian <f.t.public@gmail.com>\n"
+"Language: fur\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Editor: HaiPO 1.2 b1\n"
+"X-Generator: Poedit 3.1.1\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Inviadôrs"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Ative aplicazion preferide 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Ative aplicazion preferide 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Ative aplicazion preferide 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Ative aplicazion preferide 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Ative aplicazion preferide 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Ative aplicazion preferide 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Ative aplicazion preferide 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Ative aplicazion preferide 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Ative aplicazion preferide 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Videadis"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Cature une videade in maniere interative"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Cature une videade"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Cature une videade di un barcon"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Regjistre un filmât dal schermi in maniere interative"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sisteme"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostre la liste des notifichis"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Met il focus ae notifiche ative"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostre la panoramiche"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostre dutis lis aplicazions"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Vierç il menù aplicazions"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gjestion barcons e inviament aplicazions"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Abilite i struments internis utii pai svilupadôrs e beta-tester vie Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permet l'acès ai struments internis di debug e di analisi doprant il dialic "
+"ALT-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID des estensions di abilitâ"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Lis estensions par GNOME Shell a àn une proprietât UUID: cheste clâf a met "
+"in liste lis estensions di cjamâ. Si scugne meti in cheste liste dutis lis "
+"estensions che si vûl di cjariâ. Al è pussibil ancje modificâ cheste liste "
+"cun la modalitât DBus EnableExtension e DisableExtension su org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID des estensions di sfuarçâ ae disabilitazion"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Lis estensions par GNOME Shell a àn une proprietât UUID: cheste clâf e liste "
+"lis estensions che si à di disabilitâ, ancje se cjariadis come part di une "
+"modalitât atuâl. Al è pussibil ancje modificâ cheste liste cun la modalitât "
+"DBus EnableExtension e DisableExtension su org.gnome.Shell. Cheste clâf e à "
+"precedence su la impostazion “enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Disabilite lis estensions dal utent"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Disabilite dutis lis estensions che l'utent al à abilitât cence vê "
+"interessât la impostazion “enabled-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Disabilite la convalide de compatibilitât di version des estensions"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell al cjarie nome lis estensions che a disin di supuartâ la version "
+"di GNOME Shell atualmentri in esecuzion. Abilitant cheste opzion il control "
+"nol vignarà plui fat e al cirarà di cjariâ dutis lis estensions ignorant la "
+"version che a disin di supuartâ."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Liste di ID di file dal scritori par lis aplicazions preferidis"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Lis aplicazions che a corispuindin a chescj identificadôrs a vignaran "
+"mostrâts te aree dai preferîts."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Storic comants pal dialic dai comants (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Storic pal dialic looking glass"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Mostre simpri l'element “Jes” tal menu dal utent."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Cheste clâf e anule il platâ automatic de vôs di menù “Jes” cuant che si è "
+"te modalitât singul-utent, singule-session."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Specifiche se visâsi la password par montâ i filesystem lontans o cifrâts"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"La shell e domandarà une password cuant che un dispositîf cifrât o un "
+"filesystem lontan al vignarà montât. Se tu desideris salvâ la password par "
+"un ûs futûr, e sarà presinte une casele di selezion “Visasi Password”. "
+"Cheste clâf e stabilìs il valôr predefinît di cheste casele di selezion."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "L'ultin profîl energjetic no-predefinît che al è stât selezionât"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Cualchi sisteme al supuarte plui di doi profîi energjetics. Par podê "
+"supuartâ ancjemò il passaç tra doi profîi, cheste clâf e regjistre l'ultin "
+"profîl no-predefinît selezionât."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"La ultime version che par chê il dialic “Welcome to GNOME” al è stât mostrât"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Cheste clâf e determine par cualis version il dialic “Benvignûts su GNOME” "
+"al è stât mostrât la ultime volte. Une stringhe vueide e rapresente la "
+"version plui vecje pussibile e un numar enormi al rapresentarà lis versions "
+"che ancjemò no esistin. Si pues doprâ il numar enormi par disabilitâ a ducj "
+"i efiets il dialic."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Disposizion dal seletôr aplicazions"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Disposizion dal seletôr des aplicazions. Ogni vôs intal array e je une "
+"pagjine. Lis pagjinis a son archiviadis tal ordin che a vegnin fûr in GNOME "
+"Shell. Ogni pagjine e conten une cubie “application id” → 'data'. Pal "
+"moment, chescj valôrs a vegnin archiviâts come 'data': • “position”: la "
+"posizion de icone de aplicazion inte pagjine"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Tast rapit associât par vierzi il menù des aplicazions"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Tast rapit associât par vierzi il menù des aplicazions."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "La associazion i tascj par passâ tra i stâts de panoramiche"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"La associazion di tascj par passâ tra lis sessions, il seletôr di barcons e "
+"la gridele des aplicazions"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"La associazion di tascj par passâ tra la gridele des aplicazions, il seletôr "
+"dai barcons e lis sessions"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Cumbinazion di tascj par vierzi la visuâl “Mostre Aplicazions”"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Cumbinazion di tascj par vierzi la visuâl “Mostre Aplicazions” inte "
+"Panoramiche Ativitâts."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Tast rapit associât par vierzi la panoramiche"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Tast rapit associât par vierzi la visuâl Panoramiche Ativitâts."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Associazion tascj par cambiâ la visibilitât de liste des notifichis"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Associazion tascj par cambiâ la visibilitât de liste des notifichis."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Tast rapit associât par meti a fûc la notifiche ative"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Tast rapit associât par meti a fûc la notifiche ative."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Passe ae aplicazion 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Passe ae aplicazion 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Passe ae aplicazion 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Passe ae aplicazion 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Passe ae aplicazion 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Passe ae aplicazion 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Passe ae aplicazion 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Passe ae aplicazion 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Passe ae aplicazion 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Limite il seletôr al spazi di lavôr in ûs."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Se vêr, nome lis aplicazions che a àn barcons tal spazi di lavôr curint a "
+"son mostrâts tal seletôr. Se no, dutis lis aplicazions a vegnin includudis."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "La modalitât de icone de aplicazion."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configure cemût che i barcons a saran viodûts intal scambiadôr. Lis "
+"pussibilitâts validis a son “thumbnail-only” (al mostre une anteprime dal "
+"barcon), “app-icon-only” (al mostre nome la icone de aplicazion) o "
+"“both” (al mostre ducj e doi)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Se vêr, nome i barcons dal spazi di lavôr curint a vegnin mostrâts intal "
+"seletôr. Se no, ducj i barcons a vegnin includûts."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Posizions"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "I lûcs di mostrâ tai orlois mondiâi"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Ubicazion automatiche"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Indiche se recuperâ la posizion atuâl o mancul"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Posizion"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Il lûc che di chel mostrâ lis previsions"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Lee il barcon modâl al barcon gjenitôr"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Cheste clâf e sorplante chê in org.gnome.mutter cuant che al è in esecuzion "
+"GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Abilite la tasseladure sul ôr cuant che si strissine i barcons tal ôr dal "
+"visôr"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "I spazis di vore a son ministrât in maniere dinamiche"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Spazis di lavôr dome sul visôr principâl"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Tardâ il cambiament dal focus te modalitât mouse fintant che il pontadôr no "
+"si ferme"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Acès di rêt"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Alc al è lât stuart"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Si scusin, ma si à vût un probleme: lis impostazions par cheste estension no "
+"puedin jessi mostradis. Si consee di segnalâ il probleme ai autôrs de "
+"estension."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detais tecnics"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Pagjine principâl"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visite la pagjine principâl de estension"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Anule"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Password"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Selezione Session"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "No in liste?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(p.e., utent o %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Non utent"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Barcon di acès"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Erôr di autenticazion"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(o passe cul dêt sul letôr)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(o place il dêt sul letôr)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Distudâ"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "distudâ;studâ;fermâ"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Torne invie"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "torne invie;torne fâs partî;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bloc schermi"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "bloc schermi"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Jessî"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "jessî;sierâ session;disconeti"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Sospindi"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "sospindi;polse"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Cambi Utent"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "cambiâ utent"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "bloc orientament;sbloc orientament;schermi;rotazion"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Cature une videade"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "videade;screenshot;filmât;screencast;taie;cature;regjistre"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Sbloche rotazion schermi"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Bloche rotazion schermi"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Comant no cjatât"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Impussibil analizâ il comant:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Esecuzion di '%s' falide:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Propite cumò"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minût indaûr"
+msgstr[1] "%d minûts indaûr"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d ore indaûr"
+msgstr[1] "%d oris indaûr"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Îr"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d zornade indaûr"
+msgstr[1] "%d zornadis indaûr"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d setemane indaûr"
+msgstr[1] "%d setemanis indaûr"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d mês indaûr"
+msgstr[1] "%d mês indaûr"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d an indaûr"
+msgstr[1] "%d agns indaûr"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Îr, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d di %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d di %B dal %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%I∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Îr, %I∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %I∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d di %B, %I∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d di %B dal %Y, %I∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Acès Hotspot"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"La tô conession a chest acès hotspot no je sigure. Lis password o altris "
+"informazions che tu inserirâs su cheste pagjine e podaran jessi viodudis des "
+"personis chi ator."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Dinee acès"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Garantìs l'acès"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Cartele cence non"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s al è stât fissât tal slanç."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s al è stât molât dal slanç."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Barcons vierts"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Gnûf barcon"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Mostre Detais"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Jes"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Mole"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Fisse sul Slanç"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Invie doprant la schede grafiche integrade"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Invie doprant une schede grafiche discrete"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Selezione dispositîf sonôr"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Impostazions suns"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Cufis"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Cufis cun microfon"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Microfon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Cambie fonts…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Impostazions visôr"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Impostazions"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB dal %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Mês indaûr"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Prossim mês"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Setemane %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Nissune notifiche"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "No sta disturbâ"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Nete"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” nol rispuint."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Si pues sielzi di spietâ un tic che al continui o sfuarçâ la aplicazion a "
+"jessî dal dut."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Sfuarce jessude"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Spiete"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Dispositîf esterni tacât"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Dispositîf esterni distacât"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Impussibil sblocâ il volum"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "La version di udisks instalade no supuarte la impostazion PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Vierç cun %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"In alternative al è pussibil conetisi fracant il boton “WPS” sul router."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Conet"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Clâf"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Password clâf privade"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitât"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Servizi"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autenticazion necessarie"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Si scugne meti une password o une clâf di cifradure par jentrâ te rêt cence "
+"fîl \"%s\"."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autenticazion vie fîl 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Non rêt"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autenticazion DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Si pretint un codiç PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Si scugne meti un codiç PIN pal dispositîf a bande largje mobil"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "E covente une password par tacâsi a '%s'."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Ministradôr di rêt"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Password VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Autenticazion Necessarie"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Aministradôr"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autentiche"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Mi displâs, no je lade drete. Prove di gnûf."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "L'utent %s al è cognossût cumò come %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Barcons"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Mostre Aplicazions"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Slanç"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d di %B dal %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e di %B dal %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d di %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d di %B dal %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Vuê"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Doman"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Dut il dì"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nissun event"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Zonte orlois mondiâi…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Orlois mondiâi"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Daûr a cjariâ…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Va in rêt pes informazions sul timp"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Lis informazions sul timp al moment no son disponibilis"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Timp"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Selezione posizion meteo…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Termine session di %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Termine session"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s al jessarà in automatic chi di %d secont."
+msgstr[1] "%s al jessarà in automatic chi di %d seconts."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Tu jessarâs in automatic chi di %d secont."
+msgstr[1] "Tu jessarâs in automatic chi di %d seconts."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Jes"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Distude"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instale inzornaments e distude"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Il sisteme si distudarà in automatic chi di %d secont."
+msgstr[1] "Il sisteme si distudarà in automatic chi di %d seconts."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instale i inzornaments software in spiete"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Distude"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Torne invie"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instale inzornaments e torne invie"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Il sisteme si tornarà a inviâ in automatic chi di %d secont."
+msgstr[1] "Il sisteme si tornarà a inviâ in automatic chi di %d secont."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Torne invie"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Torne a inviâ e instale inzornaments"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Il sisteme al tornarà a inviâsi in automatic instalant i inzornaments chi di "
+"%d secont."
+msgstr[1] ""
+"Il sisteme al tornarà a inviâsi in automatic instalant i inzornaments chi di "
+"%d seconts."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Torne invie e instale"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instale e distude"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Distude dopo vê instalât i inzornaments"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Torne invie e instale avanzament"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s al vignarà instalât dopo vê tornât a inviâ il computer. La instalazion "
+"dal avanzament e pues tirâ a dilunc: siguriti di vê fat i backup e che il to "
+"computer al sedi tacât."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Cjare de batarie basse: par plasê tache la spine prime di instalâ i "
+"inzornaments."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Cualchi aplicazion e je impegnade opûr e à lavôrs no salvâts"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Altris utents a son jentrâts"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opzions di inviament"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (esterni)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (locâl vie tastiere)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instale"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instale estension"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Scjariâ e instalâ '%s' da extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Inzonraments di estensions disponibii"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "I inzornaments des estensions a son pronts par jessi instalâts."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Permet inibizion scurtis"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "La aplicazion %s e vûl inibî lis scurtis"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Une aplicazion e vûl inibî lis scurtis"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Si pues ripristinâ lis scurtis fracant %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Dinee"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Pemet"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tascj lents ativâts"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tascj lents disativâts"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Si à apene tignût fracât par 8 seconts il tast Maiusc. Chê e je la scurte pe "
+"funzion tascj lents, che e determine il mût di lavorâ de tastiere."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Tascj tacadiçs ativâts"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Tascj tacadiçs disativâts"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Si à apene fracât par 5 voltis, une daûr di chê altre, il tast Maiusc. Chê e "
+"je la scurte pe funzion Tascj singui/tacadiçs, che e determine il mût di "
+"lavorâ de tastiere."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"A son stâts fracâts doi tascj adun o cinc voltis in file il tast Maiusc. "
+"Chest al disative la funzion tascj singui/tacadiçs, che al determine il mût "
+"di lavorâ de tastiere."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Lasse ativât"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Impie"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Distude"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Lasse disativât"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Impostazions regjon e lenghe"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nissune estension instalade"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nol a dât fûr nissun erôr."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Tapone Erôrs"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Mostre Erôrs"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Abilitât"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Disabilitât"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Erôr"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "No inzornât"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Daur a scjamâ"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Viôt sorzint"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Pagjine Web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Il sisteme al è stât metût in modalitât no sigure"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Cumò lis aplicazions a àn acès ilimitât"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Anule"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Jentre come utent privilegjât"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Si varès di evitâ di eseguî une session come utent privilegjât par resons di "
+"sigurece. Se pussibil, tu varessis di jentrâ come utent normâl."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Bloc dal schermi disabilitât"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Il bloc dal schermi al à bisugne dal gjestôr dai visôrs di GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informazion di sisteme"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artist no cognossût"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Titul no cognossût"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Scrîf par cirî"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Aplicazions"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Panoramiche"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Gnove scurte…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplicazion definide"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Mostre jutori a schermi"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Cambie visôr"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Assegne batidure"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Fat"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Modifiche…"
+
+# masculin o feminin
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nissune"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Frache un boton par configurâ"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Frache Esc par jessî"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Frache un tast par jessî"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Ativitâts"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sisteme"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Sbare parsore"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Eseguìs un comant"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Frache Esc par sierâ"
+
+# torne invie o torne tache
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Il tornâ a inviâ nol è disponibil in Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Daûr a tornâ a inviâ…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME al à di blocâ il visôr"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Impussibil blocâ"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Il bloc al è stât dineât di une aplicazion"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Selezion"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Selezion de aree"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Schermi"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Selezion dal schermi"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Barcon"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Selezion dal barcon"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Videade / Filmât"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Mostre pontadôr"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Filmâts di schermi"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Filmât di %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Videade"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Filmât dal schermi regjistrât"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Fâs clic achì par viodi il video."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Mostre in Files"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Videade di %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Videade caturade"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Tu puedis tacâ la imagjin da lis notis."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Videade caturade"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Daûr a cirî…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Nissun risultât."
+
+# o ancjemò %d
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d altri"
+msgstr[1] "%d altris"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Cîr"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copie"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Tache"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Mostre Test"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Plate Test"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "BlocMaiusc al è atîf."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volum platât"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volum di sisteme Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Al dopre i File-clâf"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Par sblocâ un volum che al dopre i file-clâf dopre invezit il program di "
+"utilitât <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Numar PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Visâsi Password"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Sbloche"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Vierç %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "Il PIM al scugne jessi un numar o vueit."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Impussibil inviâ %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Impussibil cjatâ la aplicazion %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Acessibilitât"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Contrast elevât"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Ingrandiment"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Letôr schermi"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Tastiere a Visôr"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Avertencis visuâls"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Tascj tacadiçs"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tascj Lents"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Tascj che si sbalcin"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Mouse di tastiere"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Test Larc"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Ribalte in automatic"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Luminositât"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Modalitât scure"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clic singul"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dopli clic"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Strissinâ"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clic secondari"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clic in polse"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Tastiere"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Mostre la disposizion de tastiere"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Permet acès ae posizion"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "La aplicazion %s e vûl acedi ae to posizion"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Al è pussibil cambiâ l'acès ae posizion cuant che tu vuelis su lis "
+"impostazions de riservatece."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<no cognossût>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Disconet %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Conet a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Hotspot %s"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Lûs noturne"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Prestazions"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Belançât"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Sparagn energjetic"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Profîi energjetics"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Ferme filmât dal schermi"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Ferme condivision schermi"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Modalitât avion"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Cature Videade"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menù par distudâ"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Sospint"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Torne invie…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Distude…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Jes…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Cambie Utent…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Bloc schermi"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Dispositîf Thunderbolt no cognossût"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Un gnûf dispositîf al è stât rilevât intant che tu jeris vie. Par plasê "
+"disconet e torne conet il dispositîf par tacâ a doprâlu."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispositîf Thunderbolt cence autorizazion"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Al è stât cjatât un gnûf dispositîf e al covente jessi autorizâts di un "
+"aministradôr."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Erôr di autorizazion di Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Impussibil autorizâ il dispositîf Thunderbolt: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Volum modificât"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Volum"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Jessude sonore"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Jentrade sonore"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Sdopleâ"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Unìs schermis"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Dome esterni"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Dome incorporât"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d di %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Scor in sù par sblocâ"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Fâs clic o frache un tast par sblocâ"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Sbloche barcon"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Jentre come altri utent"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Benvignûts su GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Se tu desideris imparâ cemût moviti, bute un voli ae visite vuidade."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "No gracie"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Partecipe ae visite vuidade"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” al è pront"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Tignî chescj valôrs pal visôr?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Ripristine impostazions"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Ten lis modifichis"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] ""
+"Lis modifichis as impostazions a vignaran ripristinadis chi di %d secont"
+msgstr[1] ""
+"Lis modifichis as impostazions a vignaran ripristinadis chi di %d seconts"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Plate"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Ripristine"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Slargje"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Sposte"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Ridimensione"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Môf la sbare dal titul a schermi"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Simpri Denant"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Simpri sul spazi di lavôr visibil"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Sposte tal spazi di lavôr a çampe"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Sposte tal spazi di lavôr a drete"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Sposte tal spazi di lavôr Parsore"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Sposte tal spazi di lavôr Disot"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Sposte tal visôr parsore"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Sposte tal visôr disot"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Sposte tal visôr a çampe"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Sposte tal visôr a drete"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Siere"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendari di Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Stampe version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Modalitât doprade da GDM pe videade di acès"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Dopre une modalitât specifiche, par esempli “gdm” pe videade di acès"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Liste modalitâts pussibilis"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "No cognossût"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "No soi rivât a eseguî '%s'"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Lis password no corispuindin."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "La password no pues jessi vueide"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Dialic di autenticazion anulât dal utent"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Estensions"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Gjestìs lis tôs Estensions di GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Il progjet GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Estensions di GNOME al gjestìs l'inzornament des estensions, la "
+"configurazion des preferencis des estensions e la rimozion o la "
+"disabilitazion des estensions che no son desideradis."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configure lis estensions di GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Nissune corispondence"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Gjavâ “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Se tu gjavis la estension tu scugnarâs tornâ a discjariâle se tu desideris "
+"abilitâle di gnûf"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Gjave"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Fabio Tomat <f.t.public@gmail.com>, 2022"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d estension e vignarà inzornade al prossim acès."
+msgstr[1] "%d estensions a vignaran inzornadis al prossim acès."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "La estension no je compatibile cu la version atuâl di GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "La estension e à vût un erôr"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Al è pussibil inzornâ la estension"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Sît web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Gjave…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Jutori"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Informazions su Estensions"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Estensions al pues causâ problemis di prestazions e stabilitât. Disabilite "
+"estensions se ti capitin problemis cul sisteme."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Instalât a man"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Par cirî e zontâ estensions, visite <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Integrade"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Nissune estension instalade"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Nus displâs, ma nol è stât pussibil otignî la liste des estensions "
+"instaladis. Controle di jessi jentrât in GNOME e torne prove."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Inzornaments di estensions pronts"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "La gnove estension e je stade creade cun sucès in %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Il non al à di jessi une stringhe (descritive in mût ideâl) une vore curte.\n"
+"Esemplis a son: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Non"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"La descrizion e je une spiegazion fate di une frase singule su ce che la "
+"estension e fâs.\n"
+"Esemplis a son: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descrizion"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"Il UUID al è un identificadôr unic e globâl pe tô estension.\n"
+"Chest al à di jessi tal formât di une direzion e-mail "
+"(clicparfocalizâ@titedalfari.esempli.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Sielç un dai modei disponibii:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Model"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "L'identificadôr univoc de gnove estension"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NON"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Il non visibil dal utent de gnove estension"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIZION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Une curte descrizion di ce che la estension e fâs"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODEL"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Il model di doprâ pe gnove estension"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Inserìs lis informazions de estension in maniere interative"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Cree une gnove estension"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argoments no cognossûts"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "A coventin UUID, non e descrizion"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "No si è rivâts a conetisi a GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "La estension “%s” no esist\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Disabilite une estension"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nissun UUID indicât"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Plui di un UUID indicât"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Abilite une estension"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "La estension “%s” no esist\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostre informazions de estension"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Sorescrîf une estension esistente"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instale un complès di estensions"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nissun complès di estensions specificât"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Plui di un complès di estensions specificât"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Mostre estensions instaladis dal utent"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostre estensions instaladis dal sisteme"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostre estensions abilitadis"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostre estensions disabilitadis"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostre lis estensions cu lis preferencis"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostre lis estensions cui inzornaments"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Stampe detais de estension"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Liste lis estensions instaladis"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Sorzint adizionâl di includi tal complès"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEME"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Un scheme di GSettings che al à di jessi includût"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "CARTELE"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "La cartele dulà cjatâ lis traduzions"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMINI"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Il domini di gettext di doprâ pes traduzions"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Sorescrîf un pachet esistent"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "La cartele dulà che il pachet al à die jessi creât"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "CARTELE_SORZINT"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Cree un complès di estensions"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Plui di une cartele sorzint specificade"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "La estension “%s” no à preferencis\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Nol è stât pussibil vierzi lis preferencis pe estension “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Al vierç lis preferencis de estension"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Azere une estension"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Impussibil disinstalâ lis estensions di sisteme\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "No si è rivâts a disinstalâ “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Disinstale une estension"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "No sta stampâ i messaçs di erôr"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Percors"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autôr origjinâl"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Version"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stât"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” no vûl nissun argoment"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Ûs:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Stampe informazions su la version e jes."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMANT"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Comants:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Stampe jutori"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Abilite estension"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Disabilite estension"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Azere estension"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Disinstale estension"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Liste lis estensions"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Mostre informazions de estension"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Vierç preferencis de estension"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Cree estension"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Estension dal pachet"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instale complès di estensions"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Dopre “%s” par vê un jutori detaiât.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Sempliç"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Une estension vueide"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicadôr"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Zonte une icone te sbare superiôr"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u jessude"
+msgstr[1] "%u jessudis"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u jentrade"
+msgstr[1] "%u jentradis"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Suns di sisteme"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Indiche se il dispositîf Bluetooth predefinît al à vût dispositîfs "
+#~ "associâts"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "La shell e mostrarà un menù Bluetooth nome se un adatatôr Bluetooth al è "
+#~ "alimentât, o se a son dispositîfs configurâts e associâts cul adatatôr "
+#~ "predefinît. Chest al vignarà azerât se l'adatatôr predefinît nol à mai "
+#~ "vût dispositîfs associâts."
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Impostazions Bluetooth"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d tacât"
+#~ msgstr[1] "%d tacâts"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth On"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth Off"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Posizion abilitade"
+
+#~ msgid "Disable"
+#~ msgstr "Disabilite"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Impostazions privacy"
+
+#~ msgid "Location In Use"
+#~ msgstr "Posizion in ûs"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Posizion no abilitade"
+
+#~ msgid "Enable"
+#~ msgstr "Abilite"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s distudât"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s tacât"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s no ministrât"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s in conession"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s al à dibisugne di autenticazion"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Al mancje il firmware par %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s no disponibil"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Conession falide su %s"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Impostazions rêt vie fîl"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Impostazions bande largje mobil"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s disabilitât vie hardware"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s disabilitât"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Conet a internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Modalitât avion piade"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr ""
+#~ "Il Wi-Fi al è disabilitât cuant che la modalitât avion e je impiade."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Distude modalitât avion"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Il Wi-Fi al è distudât"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Il Wi-Fi al scugne jessi impiât par podêsi tacâ a une rêt."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Impie il Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Rêts Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Selezione une rêt"
+
+#~ msgid "No Networks"
+#~ msgstr "Nissune rêt"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Dopre interutôr fisic par distudâ"
+
+#~ msgid "Select Network"
+#~ msgstr "Selezione rêt"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Impostazions Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s distacât"
+
+#~ msgid "unknown"
+#~ msgstr "no cognossût"
+
+#~ msgid "activating…"
+#~ msgstr "in ativazion…"
+
+#~ msgid "deactivating…"
+#~ msgstr "disativazion…"
+
+#~ msgid "deactivated"
+#~ msgstr "disativât"
+
+#~ msgid "connecting…"
+#~ msgstr "daûr a coneti…"
+
+#~ msgid "authentication required"
+#~ msgstr "autenticazion necessarie"
+
+#~ msgid "connection failed"
+#~ msgstr "conession falide"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Impostazions VPN"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN distudât"
+
+#~ msgid "Network Settings"
+#~ msgstr "Impostazions rêt"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s conession cablade"
+#~ msgstr[1] "%s conessions cabladis"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s conession cence fîi"
+#~ msgstr[1] "%s conessions cence fîi"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s conession modem"
+#~ msgstr[1] "%s conessions modem"
+
+#~ msgid "Connection failed"
+#~ msgstr "Conession falide"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Ativazion de conession di rêt falide"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Lûs noturne disabilitade"
+
+#~ msgid "Resume"
+#~ msgstr "Ripie"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Disabilite fintremai doman"
+
+#~ msgid "Power Settings"
+#~ msgstr "Impostazions di alimentazion"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Cjarie"
+
+#~ msgid "Not Charging"
+#~ msgstr "Nol sta cjariant"
+
+#~ msgid "Estimating…"
+#~ msgstr "In stime…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "A restin %d∶%02d (%d%%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d par jessi plene (%d%%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Il schermi al è condividût"
+
+#~ msgid "Turn off"
+#~ msgstr "Distude"
+
+#~ msgid "Lock"
+#~ msgstr "Bloche"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Distude / Jes de session"
+
+#~ msgid "Log Out"
+#~ msgstr "Jes"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Gjave dai preferîts"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Zonte tai preferîts"
+
+#~ msgid "Author"
+#~ msgstr "Autôr"
+
+#~ msgid "Warning"
+#~ msgstr "Avertiment"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Lis estensions a puedin causâ problemis al sisteme, includûts problemis "
+#~ "di prestazion. Se si verifichin problemis cul vuestri sisteme, si consee "
+#~ "di disabilitâ dutis lis estensions."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Abilite la API di introspezion"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Al abilite une API di D-Bus che e permet la introspezion dal stât de "
+#~ "aplicazion de shell."
+
+#~ msgid "Minimize"
+#~ msgstr "Minimize"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Gjave slargjament"
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "No si è rivâts a conetisi a GNOME Shell"
+
+#~ msgid "App Picker View"
+#~ msgstr "Viodude seletôr app"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Indiç de viodude cumò selezionade tal seletôr di aplicazions."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Lis aplicazions dopradis dispès a vignaran mostradis culì"
+
+#~ msgid "Frequent"
+#~ msgstr "Dispès"
+
+#~ msgid "All"
+#~ msgstr "Dutis"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d di %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d di %B dal %Y"
+
+#~ msgid "Off"
+#~ msgstr "Distudât"
+
+#~ msgid "On"
+#~ msgstr "Impiât"
+
+#~ msgid "Copy Error"
+#~ msgstr "Copie erôr"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Esplore in Software"
+
+#~ msgid "Next"
+#~ msgstr "Indenant"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Jentre"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Bloc orientament"
+
+#~ msgid "Password:"
+#~ msgstr "Password:"
+
+#~ msgid "Type again:"
+#~ msgstr "Scîf di gnûf:"
+
+#~ msgid "Password: "
+#~ msgstr "Password: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "La rêt cence fîl e domande autenticazion"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Passowrd rêt mobil a bande largje"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d di %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d gnûf messaç"
+#~ msgstr[1] "%d gnûfs messaçs"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d gnove notifiche"
+#~ msgstr[1] "%d gnovis modifichis"
+
+#~ msgid "Rename"
+#~ msgstr "Cambie non"
+
+#~ msgid "Account Settings"
+#~ msgstr "Impostazions account"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Bloc Orientament"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Associazion di tascj par meti in pause e ripiâ ducj i tween in esecuzion, "
+#~ "par motîfs di debug"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Cuale tastiere doprâ"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Il stamp di tastiere di doprâ."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "%s all day."
+#~ msgstr "%s dut il dì."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, plui tart %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, chi di un pôc %s, e plui tart %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Si sintin %s."
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr ""
+#~ "Al è vignût fûr un erôr cuant che si cjamave il dialic des preferencis "
+#~ "par %s:"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Jessî"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Cambiâ utent"
+
+#~ msgid "Hide tray"
+#~ msgstr "Plate casset"
+
+#~ msgid "Status Icons"
+#~ msgstr "Iconis di stât"
+
+#~ msgid "Events"
+#~ msgstr "Events"
+
+#~ msgid "Notifications"
+#~ msgstr "Notifichis"
+
+#~ msgid "Media"
+#~ msgstr "Supuart"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Indreçament pe autenticazion web"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Not In Use"
+#~ msgstr "No in ûs"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Mostre il dì de setemane tal calendari"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Se VÊR, al mostre la date ISO de setemane tal calendari."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Dopre come conession internet"
+
+#~ msgid "GNOME Delivers a full desktop experience"
+#~ msgstr "GNOME al da fûr une esperience di scritori complete "
+
+#~ msgid "Part of this means making it easy for users.."
+#~ msgstr "Part di chest al significhe rindi facil pai utents.."
+
+#~ msgid "..to explore new features and workflows."
+#~ msgstr "..esplorâ gnovis carateristichis e flus di lavôr."
+
+#~ msgid "This is where GNOME Help comes into the picture."
+#~ msgstr "Chest al è dulà che Jutori di GNOME al jentre in sene."
+
+#~ msgid "Need to know the keyboard shortcuts of gedit?"
+#~ msgstr "Covential savê lis scurtis de tastiere di gedit?"
+
+#~ msgid "GNOME Help lets you explore them easily through its interface."
+#~ msgstr ""
+#~ "Il Jutori di GNOME ti permet di esplorâju in mût facil cun la sô "
+#~ "interface."
+
+#~ msgid "Want to know what documents have to offer?"
+#~ msgstr "Vûstu cognossi ce che documents al à di ufrî?"
+
+#~ msgid "Press F1 and you can get an overview of.."
+#~ msgstr "Frache F1 e tu puedis vê une panoramiche des.."
+
+#~ msgid "..Documents' features at a glance."
+#~ msgstr "..carateristichis di Documents cuntune cucade."
+
+#~ msgid "GNOME Help is your offline assistant.."
+#~ msgstr "Jutori di GNOME al è il to assistent fûr linie.."
+
+#~ msgid "..ready to lend a hand with any application adventure you may take."
+#~ msgstr "..pront par dâti une man cun cualsisei aplicazion che ti covente."
+
+#~ msgid "The documentation is there to help you speed up.."
+#~ msgstr "La documentazion e je lì par judâ a niçâti.."
+
+#~ msgid "..and get work done."
+#~ msgstr "..e finî il lavôr."
+
+#~ msgid "Similarly, the GNOME project aims to provide good documentation.."
+#~ msgstr ""
+#~ "Te stesse maniere, il progjet GNOME al smire a furnî une buine "
+#~ "documentazion.."
+
+#~ msgid "..for developers and administrators."
+#~ msgstr "..par svilupadôrs e aministradôrs."
+
+#~ msgid "For developers, help is provided through Devhelp.."
+#~ msgstr "Pai svilupadôrs, il jutori al è furnît vie Devhelp.."
+
+#~ msgid "..and online at the GNOME Developer Center."
+#~ msgstr "..e in rêt tal Centri dal Svilupadôr GNOME."
+
+#~ msgid "Combined you have a library of knowledge and tools.."
+#~ msgstr "Cumbinâts tu âs une librarie di cognossince e struments.."
+
+#~ msgid "..to get you up to date with the latest happenings.."
+#~ msgstr "..par vêti inzornât cun lis ultimis novitâts.."
+
+#~ msgid "..in everything from glib to gnome-shell.."
+#~ msgstr "..su dut a partî di glib fin a gnome-shell.."
+
+#~ msgid "..in terms of source code, API and plug-in development."
+#~ msgstr "..in tiermins di codiç sorzint, API e plug-in di svilup."
+
+#~ msgid "For system administrators and tinkerers.."
+#~ msgstr "Pai aministradôrs di sisteme e traficons.."
+
+#~ msgid "..we provide the System Administration Guide online.."
+#~ msgstr "..o furnin la Guide par Aministradôrs di Sisteme in linie.."
+
+#~ msgid "at help.gnome.org."
+#~ msgstr "su help.gnome.org."
+
+#~ msgid "This guide aims at addressing your needs.."
+#~ msgstr "Cheste guide a ponte a indreçâ lis tôs dibisugnis.."
+
+#~ msgid ""
+#~ "..whether it be managing the configuration of several computers from one "
+#~ "place.."
+#~ msgstr ""
+#~ "..che a sedin ministrâ la configurazion di diviers computer da un puest "
+#~ "sôl.."
+
+#~ msgid "..or playing with the logo on the login screen."
+#~ msgstr "..o zuiâ cul logo de schermade di acès."
+
+#~ msgid "Together, Help, Developer Center and the Sysadmin Guide.."
+#~ msgstr ""
+#~ "Adun, Jutori, Centri dal Svilupadôr e la Guide dal Aministradôr di "
+#~ "sisteme.."
+
+#~ msgid "..makes up GNOMEs documentation."
+#~ msgstr "..a fasin la documentazion di GNOME."
+
+#~ msgid "Of course, we are always looking to make the documentation better."
+#~ msgstr "Sigûr, nô o cirin simpri di miorâ la documentazion."
+
+#~ msgid "Got an idea, found a typo.."
+#~ msgstr "Vût une idee, cjatât un erôr di scriture.."
+
+#~ msgid "..or just want to find a way to help?"
+#~ msgstr "..o nome vê gole di cjatâ une maniere par judâ?"
+
+#~ msgid "File a bug at bugzilla.gnome.org.."
+#~ msgstr "Invie un erôr a bugzilla.gnome.org.."
+
+#~ msgid "..against \"gnome-user-docs\"."
+#~ msgstr "..su \"gnome-user-docs\"."
+
+#~ msgid "Or come hangout at in the #docs channel.."
+#~ msgstr "O vent te cunvigne al canâl #docs.."
+
+#~ msgid "..at irc.gnome.org"
+#~ msgstr "..su irc.gnome.org"
+
+#~ msgid "This is Karen Sandler, thank you for using GNOME."
+#~ msgstr "O soi Karen Sandler, gracie di doprâ GNOME."
diff --git a/po/fy.po b/po/fy.po
new file mode 100644
index 0000000..5a5a6ac
--- /dev/null
+++ b/po/fy.po
@@ -0,0 +1,2981 @@
+# Frisian translation for gnome-shell.
+# Copyright (C) 2021 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# vancha <tjipke@tutanota.com>, 2021.
+# Tjipke van der Heide <tjipke@tutanota.com>, 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell main\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2021-07-27 20:34+0000\n"
+"PO-Revision-Date: 2021-08-05 14:34+0200\n"
+"Last-Translator: Tjipke van der Heide <tjipke@tutanota.com>\n"
+"Language-Team: Western Frisian <tjipke@tutanota.com>\n"
+"Language: fy\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"X-DL-Team: fy\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: None\n"
+"X-Generator: Gtranslator 40.0\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr ""
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktivearje favorite applikaasje 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktivearje favorite applikaasje 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktivearje favorite applikaasje 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktivearje favorite applikaasje 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktivearje favorite applikaasje 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktivearje favorite applikaasje 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktivearje favorite applikaasje 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktivearje favorite applikaasje 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktivearje favorite applikaasje 9"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Systeem"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Lit de notifikaasje list sjen"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokus op de aktive notifikaasje"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Lit it oersjoch sjen"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Alle applikaasjes sjen litte"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "It applikaasje menu iepenje"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Finster behear en applikaasjes útfiere"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Intern ark brûkber is foar ûntwikkelders en testers ynskeakelje mei Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs fan it yn te skeakeljen taheaksel"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs fan it forseart út te skeakeljen taheaksel"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Brûkersútwreidingen útskeakelje"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Enable introspection API"
+msgstr "Yntrospeksje API ynskeakelje"
+
+#: data/org.gnome.shell.gschema.xml.in:110
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:141
+msgid "Layout of the app picker"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to open the application menu"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:164
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid "Keybinding to shift between overview states"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:165
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to open the overview"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:200
+msgid "Keybinding to focus the active notification"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 1"
+msgstr "Wikselje nei applikaasje 1"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 2"
+msgstr "Wikselje nei applikaasje 2"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 3"
+msgstr "Wikselje nei applikaasje 3"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 4"
+msgstr "Wikselje nei applikaasje 4"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 5"
+msgstr "Wikselje nei applikaasje 5"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 6"
+msgstr "Wikselje nei applikaasje 6"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 7"
+msgstr "Wikselje nei applikaasje 7"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "Switch to application 8"
+msgstr "Wikselje nei applikaasje 8"
+
+#: data/org.gnome.shell.gschema.xml.in:239
+msgid "Switch to application 9"
+msgstr "Wikselje nei applikaasje 9"
+
+#: data/org.gnome.shell.gschema.xml.in:248
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Limit switcher to current workspace."
+msgstr "Skeakelder ta de aktyve wurkromte limitearje"
+
+#: data/org.gnome.shell.gschema.xml.in:249
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"As dit wier is, wurde allinich applikaasjes mei finsters op de aktive "
+"wurkromte sjen litten yn de skeakelder. Sa net, dan wurde alle applikaasjes "
+"sjen litten."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid "The application icon mode."
+msgstr "De ikoan modus fan de applikaasje"
+
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:276
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:286
+msgid "Locations"
+msgstr "Lokaasjes"
+
+#: data/org.gnome.shell.gschema.xml.in:287
+msgid "The locations to show in world clocks"
+msgstr "De lokaasje om wrâldklokken sjen te litten"
+
+#: data/org.gnome.shell.gschema.xml.in:297
+msgid "Automatic location"
+msgstr "Automatyske lokaasje"
+
+#: data/org.gnome.shell.gschema.xml.in:298
+msgid "Whether to fetch the current location or not"
+msgstr "Oft de aktuele lokaasje ophele wurde moat of net."
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Location"
+msgstr "Lokaasje"
+
+#: data/org.gnome.shell.gschema.xml.in:306
+msgid "The location for which to show a forecast"
+msgstr "De lokaasje wer in waarsfoarsizzing for sjen litte wurde moat"
+
+#: data/org.gnome.shell.gschema.xml.in:318
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:319
+#: data/org.gnome.shell.gschema.xml.in:328
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:327
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Workspaces only on primary monitor"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:351
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Netwurk Ynlog"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:28
+#: subprojects/extensions-app/data/ui/extensions-window.ui:241
+msgid "Something’s gone wrong"
+msgstr "Der is wat ferkeard gien"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:39
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"It spiit ús hiel bot, mar der hat harren in flater foar dien: de ynstellings "
+"foar dit taheaksel kinne net sjen litten wurde. We riede jo oan it probleem "
+"te rapportearjen by de skriuwers fan it taheaksel."
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:64
+msgid "Technical Details"
+msgstr "Technyske Details"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:106
+msgid "Homepage"
+msgstr "Thússide"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:107
+msgid "Visit extension homepage"
+msgstr "Thússide fan taheaksel besykje"
+
+#: js/gdm/authPrompt.js:141 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:111 js/ui/components/polkitAgent.js:138
+#: js/ui/endSessionDialog.js:438 js/ui/extensionDownloader.js:190
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:963 subprojects/extensions-app/js/main.js:183
+msgid "Cancel"
+msgstr "Ôfbrekke"
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:285 js/ui/components/networkAgent.js:210
+#: js/ui/components/networkAgent.js:226 js/ui/components/networkAgent.js:250
+#: js/ui/components/networkAgent.js:271 js/ui/components/networkAgent.js:291
+#: js/ui/components/networkAgent.js:301 js/ui/components/polkitAgent.js:275
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "Wachtwurd"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Sesje kieze"
+
+#: js/gdm/loginDialog.js:456
+msgid "Not listed?"
+msgstr "Net yn de list?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:921
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr ""
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:926 js/ui/components/networkAgent.js:246
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:287
+msgid "Username"
+msgstr "Brûkersnamme"
+
+#: js/gdm/loginDialog.js:1279
+msgid "Login Window"
+msgstr "Ynlog Finster"
+
+#: js/gdm/util.js:430
+msgid "Authentication error"
+msgstr ""
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:589
+msgid "(or swipe finger across reader)"
+msgstr ""
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:594
+msgid "(or place finger on reader)"
+msgstr ""
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:82
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Útskeakelje"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:85
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:90
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Opnij starte"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:93
+msgid "reboot;restart;"
+msgstr "reboot;restart;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:98
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr ""
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:101
+msgid "lock screen"
+msgstr ""
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Útlogge"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "logout;log out;sign off"
+msgstr ""
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:114
+msgctxt "search-result"
+msgid "Suspend"
+msgstr ""
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:117
+msgid "suspend;sleep"
+msgstr ""
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:122
+msgctxt "search-result"
+msgid "Switch User"
+msgstr ""
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:125
+msgid "switch user"
+msgstr ""
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+
+#: js/misc/systemActions.js:232
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr ""
+
+#: js/misc/systemActions.js:233
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr ""
+
+#: js/misc/util.js:120
+msgid "Command not found"
+msgstr "Kommando net fûn"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:156
+msgid "Could not parse command:"
+msgstr "Koe kommando net ferwurkje:"
+
+#: js/misc/util.js:164
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Útfieren fan “%s” mislearre:"
+
+#: js/misc/util.js:181
+msgid "Just now"
+msgstr "No krekt"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minút lyn"
+msgstr[1] "%d minúten lyn"
+
+#: js/misc/util.js:187
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d oere lyn"
+msgstr[1] "%d oeren lyn"
+
+#: js/misc/util.js:191 js/ui/dateMenu.js:162
+msgid "Yesterday"
+msgstr "Juster"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dei lyn"
+msgstr[1] "%d dagen lien"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d wike lyn"
+msgstr[1] "%d wiken lien"
+
+#: js/misc/util.js:201
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d moanne lyn"
+msgstr[1] "%d moannen lyn"
+
+#: js/misc/util.js:204
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d jier lyn"
+msgstr[1] "%d jieren lyn"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:237
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:243
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Juster, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:249
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:255
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:261
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:266
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:272
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Juster, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:278
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:284
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:290
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:42
+msgid "Hotspot Login"
+msgstr "Hotspot Ynlog"
+
+#: js/portalHelper/main.js:88
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Jo ferbining mei dit hotspot is net feilig. Wachtwurden as oare ynformaasje "
+"dy jo op dizze side ynfiere kin troch minsken ticht by jo sjûn wurde."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:417
+msgid "Deny Access"
+msgstr "Tagong Ûntsizze"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:420
+msgid "Grant Access"
+msgstr "Tagong Tastean"
+
+#: js/ui/appDisplay.js:1860
+msgid "Unnamed Folder"
+msgstr "Namleaze Folder"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:3453 js/ui/panel.js:33
+msgid "Open Windows"
+msgstr "Iepen Finsters"
+
+#: js/ui/appDisplay.js:3472 js/ui/panel.js:41
+msgid "New Window"
+msgstr "Nij Finster"
+
+#: js/ui/appDisplay.js:3488
+msgid "Launch using Integrated Graphics Card"
+msgstr "Útfiere mei Yntegrearre Grafyske Kaart"
+
+#: js/ui/appDisplay.js:3489
+msgid "Launch using Discrete Graphics Card"
+msgstr "Útfiere mei Losse Grafyske Kaart"
+
+#: js/ui/appDisplay.js:3518 js/ui/dash.js:245
+msgid "Remove from Favorites"
+msgstr "Út Favoriten wei smite"
+
+#: js/ui/appDisplay.js:3524
+msgid "Add to Favorites"
+msgstr "Oan Favoriten tafoegje"
+
+#: js/ui/appDisplay.js:3534 js/ui/panel.js:52
+msgid "Show Details"
+msgstr "Details sjen litte"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s is oan jo favoriten tafoegt."
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s is út jo favoriten wei helle."
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Audio Apparaat Selektearje"
+
+#: js/ui/audioDeviceSelection.js:56
+msgid "Sound Settings"
+msgstr "Ynstellingen foar lûd"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Koptelefoan"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:277
+msgid "Microphone"
+msgstr "Mikrofoan"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Êftergrûn feroarje"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Skerm Ynstellingen"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "Ynstellingen"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:36
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:65
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:67
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:69
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:71
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:73
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:75
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:77
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:392
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:461
+msgid "Previous month"
+msgstr "Ferline moanne"
+
+#: js/ui/calendar.js:476
+msgid "Next month"
+msgstr "Oankommende moanne"
+
+#: js/ui/calendar.js:626
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:682
+msgid "Week %V"
+msgstr "Wike %V"
+
+#: js/ui/calendar.js:896
+msgid "No Notifications"
+msgstr "Gjin Notifikaasjes"
+
+#: js/ui/calendar.js:950
+msgid "Do Not Disturb"
+msgstr "Net Steure"
+
+#: js/ui/calendar.js:971
+msgid "Clear"
+msgstr "Wiskje"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” reagearet net."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Jo kinne der foar kieze om in koart skotsje te wachtsjen tot de applikaasje "
+"wer reageart of de applikaasje forseart ôf te slúten."
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "Ôfsluten forsearje"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "Wachtsje"
+
+#: js/ui/components/automountManager.js:85
+msgid "External drive connected"
+msgstr "Ekstern stasjon oankeppele"
+
+#: js/ui/components/automountManager.js:97
+msgid "External drive disconnected"
+msgstr "Ekstern stasjon ôfkeppele"
+
+#: js/ui/components/automountManager.js:206
+msgid "Unable to unlock volume"
+msgstr ""
+
+#: js/ui/components/automountManager.js:207
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:332
+#, javascript-format
+msgid "Open with %s"
+msgstr "Iepenje mei %s"
+
+#: js/ui/components/networkAgent.js:93
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:105 js/ui/status/network.js:258
+#: js/ui/status/network.js:349 js/ui/status/network.js:966
+msgid "Connect"
+msgstr "Ferbine"
+
+#: js/ui/components/networkAgent.js:216
+msgid "Key"
+msgstr "Kaai"
+
+#: js/ui/components/networkAgent.js:254 js/ui/components/networkAgent.js:277
+msgid "Private key password"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:275
+msgid "Identity"
+msgstr "Identiteit"
+
+#: js/ui/components/networkAgent.js:289
+msgid "Service"
+msgstr "Tsjinst"
+
+#: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:346
+#: js/ui/components/networkAgent.js:679 js/ui/components/networkAgent.js:700
+msgid "Authentication required"
+msgstr "Ferifikaasje nedich"
+
+#: js/ui/components/networkAgent.js:319 js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:323 js/ui/components/networkAgent.js:684
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:325
+msgid "Network name"
+msgstr "Netwurk namme"
+
+#: js/ui/components/networkAgent.js:330 js/ui/components/networkAgent.js:688
+msgid "DSL authentication"
+msgstr "DSL ferifikaasje"
+
+#: js/ui/components/networkAgent.js:337 js/ui/components/networkAgent.js:693
+msgid "PIN code required"
+msgstr "PIN koade nedich"
+
+#: js/ui/components/networkAgent.js:338 js/ui/components/networkAgent.js:694
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:339
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:347 js/ui/components/networkAgent.js:685
+#: js/ui/components/networkAgent.js:689 js/ui/components/networkAgent.js:701
+#: js/ui/components/networkAgent.js:705
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Der is in wachtwurd nedich om te ferbinen mei “%s”."
+
+#: js/ui/components/networkAgent.js:668 js/ui/status/network.js:1774
+msgid "Network Manager"
+msgstr "Netwurk Behearder"
+
+#: js/ui/components/networkAgent.js:704
+msgid "VPN password"
+msgstr "VPN wachtwurd"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "Ferifikaasje Nedich"
+
+#: js/ui/components/polkitAgent.js:79
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:141
+msgid "Authenticate"
+msgstr "Ferifiearje"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:252 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Sorry, dat wurke net. Probearje it noch ris."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:822
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s stiet no bekend as %s"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/overviewControls.js:404
+msgid "Windows"
+msgstr "Finsters"
+
+#: js/ui/dash.js:204 js/ui/dash.js:247
+msgid "Show Applications"
+msgstr "Applikaasjes sjen litte"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr ""
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:79
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:86
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:151
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:154
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:160
+msgid "Today"
+msgstr "Hjoed"
+
+#: js/ui/dateMenu.js:164
+msgid "Tomorrow"
+msgstr "Moarn"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:180
+msgctxt "event list time"
+msgid "All Day"
+msgstr "De Folsleine Dei"
+
+#: js/ui/dateMenu.js:231
+msgid "No Events"
+msgstr "Gjin Eveneminten"
+
+#: js/ui/dateMenu.js:348
+msgid "Add world clocks…"
+msgstr "Wrâld klokken tafoegje…"
+
+#: js/ui/dateMenu.js:349
+msgid "World Clocks"
+msgstr "Wrâld Klokken"
+
+#: js/ui/dateMenu.js:629
+msgid "Loading…"
+msgstr "Lade…"
+
+#: js/ui/dateMenu.js:639
+msgid "Go online for weather information"
+msgstr "Gean online foar ynformaasje oer it waar"
+
+#: js/ui/dateMenu.js:641
+msgid "Weather information is currently unavailable"
+msgstr "Ynformaasje oer it waar is no net beskikber"
+
+#: js/ui/dateMenu.js:651
+msgid "Weather"
+msgstr "Waar"
+
+#: js/ui/dateMenu.js:653
+msgid "Select weather location…"
+msgstr "Lokaasje foar it waar selektearje…"
+
+#: js/ui/endSessionDialog.js:39
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Log %s út"
+
+#: js/ui/endSessionDialog.js:40
+msgctxt "title"
+msgid "Log Out"
+msgstr "Útlogge"
+
+#: js/ui/endSessionDialog.js:43
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s wurd oer %d sekonde automatysk útlogd."
+msgstr[1] "%s wurd oer %d sekonden automatysk útlogd. "
+
+#: js/ui/endSessionDialog.js:49
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Jo wurde oer %d sekonde automatysk útlogd. "
+msgstr[1] "Jo wurde oer %d sekonden automatysk útlogd. "
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "Útlogge"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "Útskeakelje"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Fernijingen Ynstallearre & Útskeakelje"
+
+#: js/ui/endSessionDialog.js:66
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "It systeem sil automatysk ôfslute oer %d sekonde."
+msgstr[1] "It systeem sil automatysk ôfslute oer %d sekonden."
+
+#: js/ui/endSessionDialog.js:70 js/ui/endSessionDialog.js:89
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Fernijingen yn ôfwachting ynstallearje"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "Ôfslute"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "Opnij Starte"
+
+#: js/ui/endSessionDialog.js:82
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Fernijingen Ynstallearre & Opnij Starte"
+
+#: js/ui/endSessionDialog.js:85
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "It systeem sil opnij starte oer %d sekonde."
+msgstr[1] "It systeem sil opnij starte oer %d sekonden."
+
+#: js/ui/endSessionDialog.js:93
+msgctxt "button"
+msgid "Restart"
+msgstr "Opnij Starte"
+
+#: js/ui/endSessionDialog.js:101
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Opnij Starte & Fernijingen Ynsteallearre"
+
+#: js/ui/endSessionDialog.js:104
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"It systeem sil automatysk opnij starte en fernijingen ynstallearje oer %d "
+"sekonde."
+msgstr[1] ""
+"It systeem sil automatysk opnij starte en fernijingen ynstallearje oer %d "
+"sekonden."
+
+#: js/ui/endSessionDialog.js:111 js/ui/endSessionDialog.js:132
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Opnij Starte &amp; Ynstallearre"
+
+#: js/ui/endSessionDialog.js:113
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Ynstallearre & Útskeakelje"
+
+#: js/ui/endSessionDialog.js:114
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Útskeakelje nei dat de fernijingen ynstallearre binne"
+
+#: js/ui/endSessionDialog.js:121
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr ""
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:126
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s sil ynstallearre wurde nei it opnij opstarten. In ynstallaasje "
+"opwurdearje kin lang duorje: wês der wis fan dat jo in backup hawwe en dat "
+"jo kompûter oan de netstroom stiet."
+
+#: js/ui/endSessionDialog.js:284
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:293
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+"Guon applikaasjes binne dwaande of hawwe wiizigingen dy nog net opslein binne"
+
+#: js/ui/endSessionDialog.js:298
+msgid "Other users are logged in"
+msgstr "Oare brûkers binne ynlogd"
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opstart Opsjes"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:686
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (op ôfstân)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:689
+#, javascript-format
+msgid "%s (console)"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:194
+msgid "Install"
+msgstr "Ynstallearje"
+
+#: js/ui/extensionDownloader.js:200
+msgid "Install Extension"
+msgstr "Ynstallearre Útwreidingen"
+
+#: js/ui/extensionDownloader.js:201
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+
+#: js/ui/extensionSystem.js:253
+msgid "Extension Updates Available"
+msgstr "Útwreidings Fernijingen Beskikber"
+
+#: js/ui/extensionSystem.js:254
+msgid "Extension updates are ready to be installed."
+msgstr "Útwreidings fernijingen binne klear om ynstallearre te wurden."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr ""
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "De applikaasje %s wol fluchtoetsen tsjinhâlde"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:100
+msgid "Deny"
+msgstr "Net tastean"
+
+#: js/ui/inhibitShortcutsDialog.js:107
+msgid "Allow"
+msgstr "Tastean"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Drêge Toetsen Oan set"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Drêge Toetsen Út set"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Jo hawwe de shift toets foar 8 sekonden ynhâlden. Dit is de fluchtoets foar "
+"de funksje \"drêge toetsen\", dy fan ynfloed is op hoe as jo toetseboerd "
+"wurket."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Plakkeriche Toetsen Oan set"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Plakkeriche Toetsen Út set"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Jo hawwe de shift toets 5 keer achter inoar yndrukt. Dit is de fluchtoets "
+"foar de funksje \"plakkeriche toetsen\", dy fan ynfloed is op hoe as jo "
+"toetseboerd wurket."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:54
+msgid "Leave On"
+msgstr "Oan litte"
+
+#: js/ui/kbdA11yDialog.js:54 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:1362
+msgid "Turn On"
+msgstr "Oan sette"
+
+#: js/ui/kbdA11yDialog.js:62 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:166 js/ui/status/network.js:350
+#: js/ui/status/network.js:1362 js/ui/status/network.js:1474
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Út sette"
+
+#: js/ui/kbdA11yDialog.js:62
+msgid "Leave Off"
+msgstr "Út litte"
+
+#: js/ui/keyboard.js:227
+msgid "Region & Language Settings"
+msgstr "Regio & Taal Ynstellingen"
+
+#: js/ui/lookingGlass.js:676
+msgid "No extensions installed"
+msgstr "Gjin útwreidingen ynstallearre"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:734
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: js/ui/lookingGlass.js:740
+msgid "Hide Errors"
+msgstr "Flaters ferbergje"
+
+#: js/ui/lookingGlass.js:744 js/ui/lookingGlass.js:810
+msgid "Show Errors"
+msgstr "Flaters sjen litte"
+
+#: js/ui/lookingGlass.js:753
+msgid "Enabled"
+msgstr "Ynskeakele"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:756 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Útskeakele"
+
+#: js/ui/lookingGlass.js:758
+#: subprojects/extensions-app/data/ui/extension-row.ui:158
+msgid "Error"
+msgstr "Flater"
+
+#: js/ui/lookingGlass.js:760
+msgid "Out of date"
+msgstr "Ferâldere"
+
+#: js/ui/lookingGlass.js:762
+msgid "Downloading"
+msgstr "Delhelje"
+
+#: js/ui/lookingGlass.js:792
+msgid "View Source"
+msgstr "Boarne besjen"
+
+#: js/ui/lookingGlass.js:801
+msgid "Web Page"
+msgstr "Web Side"
+
+#: js/ui/main.js:294
+msgid "Logged in as a privileged user"
+msgstr "As befoarrjochte brûker ynlogd"
+
+#: js/ui/main.js:295
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+
+#: js/ui/main.js:344
+msgid "Screen Lock disabled"
+msgstr ""
+
+#: js/ui/main.js:345
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+
+#: js/ui/messageTray.js:1440
+msgid "System Information"
+msgstr "Systeem ynformaasje"
+
+#: js/ui/mpris.js:207
+msgid "Unknown artist"
+msgstr "Ûnbekende artyst"
+
+#: js/ui/mpris.js:217
+msgid "Unknown title"
+msgstr "Ûnbekende titel"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:313
+msgid "Type to search"
+msgstr "Tikje om te sykjen"
+
+#: js/ui/overviewControls.js:392
+msgid "Applications"
+msgstr "Aplikaasjes"
+
+#: js/ui/overview.js:58
+msgid "Undo"
+msgstr "Ûngedien meitsje"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Oersjoch"
+
+#: js/ui/padOsd.js:96
+msgid "New shortcut…"
+msgstr "Nije fluchtoets…"
+
+#: js/ui/padOsd.js:143
+msgid "Application defined"
+msgstr "Applikaasje definiearre"
+
+#: js/ui/padOsd.js:144
+msgid "Show on-screen help"
+msgstr ""
+
+#: js/ui/padOsd.js:145
+msgid "Switch monitor"
+msgstr "Fan skerm wikselje"
+
+#: js/ui/padOsd.js:146
+msgid "Assign keystroke"
+msgstr ""
+
+#: js/ui/padOsd.js:212
+msgid "Done"
+msgstr "Klear"
+
+#: js/ui/padOsd.js:718
+msgid "Edit…"
+msgstr "Feroarje…"
+
+#: js/ui/padOsd.js:760 js/ui/padOsd.js:877
+msgid "None"
+msgstr "Gjin"
+
+#: js/ui/padOsd.js:831
+msgid "Press a button to configure"
+msgstr "Druk op in toets om yn te stellen"
+
+#: js/ui/padOsd.js:832
+msgid "Press Esc to exit"
+msgstr "Druk op Esc om te sluten"
+
+#: js/ui/padOsd.js:835
+msgid "Press any key to exit"
+msgstr "Druk op in toets om ôf te sluten"
+
+#: js/ui/panel.js:66
+msgid "Quit"
+msgstr "Ôfslute"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:395
+msgid "Activities"
+msgstr "Aktiviteiten"
+
+#: js/ui/panel.js:666
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Systeem"
+
+#: js/ui/panel.js:778
+msgid "Top Bar"
+msgstr "Boppeste Balke"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "In Kommando útfiere"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Druk op ESC om te sluten"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Kin net opnij starte ûnder Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Opnij oan it starten…"
+
+#: js/ui/screenShield.js:211
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME moat it skerm op slot sette"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:252 js/ui/screenShield.js:622
+msgid "Unable to lock"
+msgstr "Kin net op slot sette"
+
+#: js/ui/screenShield.js:253 js/ui/screenShield.js:623
+msgid "Lock was blocked by an application"
+msgstr ""
+
+#: js/ui/screenshot.js:141
+msgid "Screenshot taken"
+msgstr "Skermôfbylding makke"
+
+#: js/ui/search.js:825
+msgid "Searching…"
+msgstr "Oan it sykjen…"
+
+#: js/ui/search.js:827
+msgid "No results."
+msgstr "Gjin resultaten."
+
+#: js/ui/search.js:953
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d mear"
+msgstr[1] "%d mear"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Sykje"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopiearje"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Plakke"
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr "Tekst sjen litte"
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr "Tekst Ferbergje"
+
+#: js/ui/shellEntry.js:162
+msgid "Caps lock is on."
+msgstr "Caps lock stiet oan"
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr "Brûkt Kaaibestannen"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr "PIM Nûmer"
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "Wachtwurd Ûnthâlde"
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "Iepenje %s"
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr "De PIM moat in nûmer wêze, of leech."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:465
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Koe %s net starte"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:467
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Koe de %s applikaasje net fine"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Tagonklikheid"
+
+#: js/ui/status/accessibility.js:48
+msgid "Zoom"
+msgstr "Grutter meitsje"
+
+#: js/ui/status/accessibility.js:55
+msgid "Screen Reader"
+msgstr "Skerm lêzer"
+
+#: js/ui/status/accessibility.js:59
+msgid "Screen Keyboard"
+msgstr "Skerm Toetseboerd"
+
+#: js/ui/status/accessibility.js:63
+msgid "Visual Alerts"
+msgstr ""
+
+#: js/ui/status/accessibility.js:66
+msgid "Sticky Keys"
+msgstr "Plakkeriche Toetsen"
+
+#: js/ui/status/accessibility.js:69
+msgid "Slow Keys"
+msgstr "Drêge Toetsen"
+
+#: js/ui/status/accessibility.js:72
+msgid "Bounce Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:75
+msgid "Mouse Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:134
+msgid "High Contrast"
+msgstr "Heech Kontrast"
+
+#: js/ui/status/accessibility.js:176
+msgid "Large Text"
+msgstr "Grutte Tekst"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:637
+msgid "Bluetooth Settings"
+msgstr "Bluetooth Ynstellingen"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:148
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d Connected"
+msgstr[1] "%d Connected"
+
+#: js/ui/status/bluetooth.js:152
+msgid "Bluetooth Off"
+msgstr "Bluetooth Út"
+
+#: js/ui/status/bluetooth.js:154
+msgid "Bluetooth On"
+msgstr "Bluetooth Oan"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Helderheid"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Inkelde Klik"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dûbelde Klik"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Slepe"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Twadde Klik"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr ""
+
+#: js/ui/status/keyboard.js:829
+msgid "Keyboard"
+msgstr "Toetseboerd"
+
+#: js/ui/status/keyboard.js:846
+msgid "Show Keyboard Layout"
+msgstr "Toetseboerd Yndieling Besjen"
+
+#: js/ui/status/location.js:234 js/ui/status/location.js:267
+msgid "Location Enabled"
+msgstr "Lokaasje Ynskeakele"
+
+#: js/ui/status/location.js:235 js/ui/status/location.js:268
+msgid "Disable"
+msgstr "Útskeakelje"
+
+#: js/ui/status/location.js:236
+msgid "Privacy Settings"
+msgstr "Privacy Ynstellingen"
+
+#: js/ui/status/location.js:266
+msgid "Location In Use"
+msgstr "Lokaasje Yn Gebrûkt"
+
+#: js/ui/status/location.js:270
+msgid "Location Disabled"
+msgstr "Lokaasje Útskeakele"
+
+#: js/ui/status/location.js:271
+msgid "Enable"
+msgstr "Ynskeakelje"
+
+#: js/ui/status/location.js:398
+msgid "Allow location access"
+msgstr "Tagong ta lokaasje tastean"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:400
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "De applikaasje %s wol tagong ta jo lokaasje"
+
+#: js/ui/status/location.js:410
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Lokaasje tagong kin altyd feroare wurde yn de privacy ynstellingen."
+
+#: js/ui/status/network.js:72
+msgid "<unknown>"
+msgstr "<ûnbekend>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:455 js/ui/status/network.js:1391
+#, javascript-format
+msgid "%s Off"
+msgstr "%s Út"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:458
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s Ferbûn"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:463
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s Unbehearre"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:466
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s Ferbining oan it ferbrekken"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:473 js/ui/status/network.js:1383
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s Oan it ferbinen"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:476
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s Ferifikaasje Nedich"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:484
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:488
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s Net beskikber"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:491
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s Ferbining Mislearre"
+
+#: js/ui/status/network.js:503
+msgid "Wired Settings"
+msgstr "Betriede Ynstellingen"
+
+#: js/ui/status/network.js:546
+msgid "Mobile Broadband Settings"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:604 js/ui/status/network.js:1388
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:608
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s Útskeakele"
+
+#: js/ui/status/network.js:649
+msgid "Connect to Internet"
+msgstr "Mei it ynternet ferbine"
+
+#: js/ui/status/network.js:858
+msgid "Airplane Mode is On"
+msgstr "Fleantúch Stân stiet Oan"
+
+#: js/ui/status/network.js:859
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi is útskeakele as fleantúch stân oan stiet."
+
+#: js/ui/status/network.js:860
+msgid "Turn Off Airplane Mode"
+msgstr "Fleantúch Stân Út Sette"
+
+#: js/ui/status/network.js:869
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi stiet Út"
+
+#: js/ui/status/network.js:870
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Wi-Fi moat ynskeakele wêze om mei in netwurk te ferbinen."
+
+#: js/ui/status/network.js:871
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi Ynskeakelje"
+
+#: js/ui/status/network.js:896
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi Netwurken"
+
+#: js/ui/status/network.js:898
+msgid "Select a network"
+msgstr "Netwurk selektearje"
+
+#: js/ui/status/network.js:930
+msgid "No Networks"
+msgstr "Gjin Netwurken"
+
+#: js/ui/status/network.js:951 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "Brûk de hardware knop om út te skeakeljen"
+
+#: js/ui/status/network.js:1252
+msgid "Select Network"
+msgstr "Netwurk Selektearje"
+
+#: js/ui/status/network.js:1258
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi Ynstellingen"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1379
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s Hotspot Aktyf"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1394
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s Net Ferbûn"
+
+#: js/ui/status/network.js:1491
+msgid "connecting…"
+msgstr "oan it ferbinen…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1494
+msgid "authentication required"
+msgstr "ferifikaasje nedich"
+
+#: js/ui/status/network.js:1496
+msgid "connection failed"
+msgstr "ferbining mislearre"
+
+#: js/ui/status/network.js:1547
+msgid "VPN Settings"
+msgstr "VPN Ynstellingen"
+
+#: js/ui/status/network.js:1564
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1574
+msgid "VPN Off"
+msgstr "VPN Út"
+
+#: js/ui/status/network.js:1635 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Netwurk Ynstellingen"
+
+#: js/ui/status/network.js:1663
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s Bekabelde Ferbining"
+msgstr[1] "%s Bekabelde Ferbiningen"
+
+#: js/ui/status/network.js:1667
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s Wi-Fi Ferbining"
+msgstr[1] "%s Wi-Fi Ferbiningen"
+
+#: js/ui/status/network.js:1671
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/network.js:1815
+msgid "Connection failed"
+msgstr "Ferbining mislearre"
+
+#: js/ui/status/network.js:1816
+msgid "Activation of network connection failed"
+msgstr "Aktifaasje fan netwurk ferbining mislearre"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Nachtlampe útskeakele"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Nachtlampe oan"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Troch gean"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Útskeakelje Oant Moarn"
+
+#: js/ui/status/power.js:51
+msgid "Power Settings"
+msgstr "Energy Ynstellingen"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr "Folslein Opladen"
+
+#: js/ui/status/power.js:74
+msgid "Not Charging"
+msgstr "Net oan it Opladen"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:77 js/ui/status/power.js:83
+msgid "Estimating…"
+msgstr "Skatting oan it meitsjen…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d Oerbleaun (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:97
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d Oant Fol (%d %%)"
+
+#. The icon label
+#: js/ui/status/power.js:145
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:38
+msgid "Screen is Being Shared"
+msgstr "Skerm wurd Dield"
+
+#: js/ui/status/remoteAccess.js:40
+msgid "Turn off"
+msgstr "Út sette"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Fleanmasine Modus Oan"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr ""
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "Útskeakelje / Útlogge"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr ""
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "Opnij Starte…"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "Útskeakelje…"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Útlogge"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "Fan brûker wikselje…"
+
+#: js/ui/status/thunderbolt.js:262
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Ûnbekend Thunderbolt apperaat"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Der is in nij apparaat detektearre wylst jo fuort wienen. Keppelje dit "
+"apparaat ôf en wer oan om it te brûken."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:160
+msgid "Volume changed"
+msgstr "Stasjon feroare"
+
+#: js/ui/status/volume.js:222
+msgid "Volume"
+msgstr "Stasjon"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr ""
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "Skermen Gearfoegje"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "Allinnich Ekstern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "Allinnich Ynboud"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:371
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:377
+msgid "Swipe up to unlock"
+msgstr ""
+
+#: js/ui/unlockDialog.js:378
+msgid "Click or press a key to unlock"
+msgstr "Klik of druk op in toets of fan't slot te heljen"
+
+#: js/ui/unlockDialog.js:556
+msgid "Unlock Window"
+msgstr "Finster fan't slot helje"
+
+#: js/ui/unlockDialog.js:565
+msgid "Log in as another user"
+msgstr "Ynlogge as in oare brûker"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Wolkom by GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+"As jo graach witte wolle hoe oft dit ollegearre wurket, besykje dan de "
+"rûnlieding ris."
+
+#: js/ui/welcomeDialog.js:45
+msgid "No Thanks"
+msgstr "Nee Tanke"
+
+#: js/ui/welcomeDialog.js:50
+msgid "Take Tour"
+msgstr "Nim de Rûnlieding"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” is klear"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:63
+msgid "Keep these display settings?"
+msgstr "Disse skerm ynstellingen bewarje?"
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Ynstellingen Weromsette"
+
+#: js/ui/windowManager.js:75
+msgid "Keep Changes"
+msgstr "Feroaringen Bewarje"
+
+#: js/ui/windowManager.js:94
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:550
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr "Minimalisearje"
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr "Net mear maximalisearje"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "Maksimalisearje"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Ferpleatse"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "Grutte feroarje"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr "Titelbalke op it skerm pleatse"
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "Altyd Boppe oan"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr "Altyd op de Sichtbere Wurkrome"
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr "In Wurkromte nei Links ferplaetse"
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr "In Wurkromte nei Rjochts ferplaetse"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "In Wurkromte nei Omheech ferplaetse"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "Nei Wurkromte nei Ûnderen ferplaetse"
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr "In Skerm Omheech ferplaetse"
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr "In Skerm nei Ûnderen ferplaetse"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "In Skerm nei Links ferplaetse"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "In Skerm nei Rjochts ferplaetse"
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Slute"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution Kalinder"
+
+#: src/main.c:419 subprojects/extensions-tool/src/main.c:317
+msgid "Print version"
+msgstr "Ferzje útprinte"
+
+#: src/main.c:425
+msgid "Mode used by GDM for login screen"
+msgstr "Modus dy brûkt wurd troch GDM foar it ynlogskerm"
+
+#: src/main.c:431
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+
+#: src/main.c:437
+msgid "List possible modes"
+msgstr ""
+
+#: src/shell-app.c:298
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ûnbekend"
+
+#: src/shell-app.c:549
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Koe “%s” net iepenje"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "Wachtwurden komme net oerien."
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "Wachtwurd mei net leech wêze"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:217
+#: subprojects/extensions-app/data/ui/extensions-window.ui:56
+msgid "Extensions"
+msgstr "Útwreidingen"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:218
+msgid "Manage your GNOME Extensions"
+msgstr "Jo GNOME Útwreidingen behearre"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "It GNOME Projekt"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME shell Útwreidingen Konfigurearje"
+
+#: subprojects/extensions-app/js/main.js:142
+#: subprojects/extensions-app/js/main.js:152
+msgid "No Matches"
+msgstr "Gjin Oerienkomsten"
+
+#: subprojects/extensions-app/js/main.js:179
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Wolle jo “%s” fuortsmite?"
+
+#: subprojects/extensions-app/js/main.js:180
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:184
+msgid "Remove"
+msgstr "Fuortsmite"
+
+#: subprojects/extensions-app/js/main.js:216
+msgid "translator-credits"
+msgstr "Tjipke van der Heide"
+
+#: subprojects/extensions-app/js/main.js:344
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d útwreiding sil mij de oankommende kear ynloggen fernijd wurde."
+msgstr[1] ""
+"%d útwreidingen sille mij de oankommende kear ynloggen fernijd wurde."
+
+#: subprojects/extensions-app/js/main.js:494
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "De útwreiding is net kompatibel mei de lêste GNOME ferzje"
+
+#: subprojects/extensions-app/js/main.js:497
+msgid "The extension had an error"
+msgstr "De útwreiding hat in flater"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:83
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Beskriuwing"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:104
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Ferzje"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:131
+msgid "Author"
+msgstr "Auteur"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:185
+msgid "Website"
+msgstr "Webside"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:201
+msgid "Remove…"
+msgstr "Fuortsmite…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:7
+msgid "Help"
+msgstr "Help"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:11
+msgid "About Extensions"
+msgstr "Oer Útwreidingen"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Om útwreidingen te finen en ta te foegjen, sjoch op <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:34
+msgid "Warning"
+msgstr "Warskôging"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:44
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr "Útwreidingen kinne foar systeemproblemen soargje, lykas "
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:150
+msgid "Manually Installed"
+msgstr "Hânmjittich Ynstallearre"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:174
+msgid "Built-In"
+msgstr "Ynboud"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:217
+msgid "No Installed Extensions"
+msgstr "Gjin Ynstallearre Útwreidingen"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:252
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:288
+msgid "Extension Updates Ready"
+msgstr "Útwreidings Fernijingen Beskikber"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:303
+msgid "Log Out…"
+msgstr "Útlogge…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "De nije útwreiding is mei sukses oanmakke yn %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Namme moat in hiel koart (it leafst beskriuwend) stikje tekst wêze.\n"
+"Foarbylden binne: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Namme"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Beskriuwing is in inkelde rigel oan ferheldering fan wat de útwreiding "
+"docht.\n"
+"Foarbylden binne: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Kies út ien fan de beskikbere foarbylden:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Foarbyld"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPTION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "TEMPLATE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Útwreidings ynformaasje interaktyf ynfiere"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "In nije útwreiding oanmeitsje"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Ûnbekende arguminten"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+msgid "Failed to connect to GNOME Shell\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Útwreiding “%s” bestiet net\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "In útwreiding útskeakelje"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:97
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Gjin UUID opjûn"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:102
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Mear as ien UUID opjûn"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "In útwreiding ynskeakelje"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Útwreiding “%s” bestiet net\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Útwreidings ynformaasje sjen litte"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "In besteande útwreiding oerskriuwe"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Troch de brûker ynstallearre útwreidingen sjen litte"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Troch it systeem ynstallearre útwreidingen sjen litte"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Ynskeakele útwreidingen sjen litte"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Únskeakele útwreidingen sjen litte"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Útwreidingen mei fernijingen sjen litte"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "BESTÂN"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Útwreiding “%s” hat gjin foarkarren\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:79
+msgid "Opens extension preferences"
+msgstr "Iepent de útwreidings foarkarren"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Oarspronklike auteur"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Steat"
+
+#: subprojects/extensions-tool/src/main.c:290
+msgid "“version” takes no arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:292
+#: subprojects/extensions-tool/src/main.c:312
+msgid "Usage:"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:295
+msgid "Print version information and exit."
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:310
+#: subprojects/extensions-tool/src/main.c:313
+msgid "COMMAND"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:313
+msgid "[ARGS…]"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:315
+msgid "Commands:"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Print help"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:318
+msgid "Enable extension"
+msgstr "Útwreiding ynskeakelje"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Disable extension"
+msgstr "Útwreiding útskeakelje"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Reset extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:321
+msgid "Uninstall extension"
+msgstr "Útwreiding fuortsmite"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "List extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:323
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Show extension info"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Open extension preferences"
+msgstr "Útwreidings foarkarren iepenje"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "Create extension"
+msgstr "Útwreiding oanmeitsje"
+
+#: subprojects/extensions-tool/src/main.c:327
+msgid "Package extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Install extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:330
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "In lege útwreiding"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr ""
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Systeem Lûd"
diff --git a/po/ga.po b/po/ga.po
new file mode 100644
index 0000000..cda4411
--- /dev/null
+++ b/po/ga.po
@@ -0,0 +1,1668 @@
+# Irish translations for gnome-shell package.
+# Copyright (C) 2009-2015 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+# Seán de Búrca <leftmostcat@gmail.com>, 2009-2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell.master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-09-30 22:09-0600\n"
+"PO-Revision-Date: 2015-09-30 22:29-0600\n"
+"Last-Translator: Seán de Búrca <leftmostcat@gmail.com>\n"
+"Language-Team: Irish <gaeilge-gnulinux@lists.sourceforge.net>\n"
+"Language: ga\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : "
+"4;\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "Córas"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the notification list"
+msgstr "Taispeáin an liosta fógraí"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "Cuir an fócas san fhógra gníomhach"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "Taispeáin an foramharc"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "Taispeáin gach feidhmchlár"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "Oscail roghchlár an fheidhmchláir"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr ""
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "Airíonna Eisínteachtaí GNOME Shell"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "Cumraigh Eisínteachtaí GNOME Shell"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+#, fuzzy
+msgid "UUIDs of extensions to enable"
+msgstr "Eisínteacht gan suiteáil"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#. Translators: looking glass is a debugger and inspector tool, see https://live.gnome.org/GnomeShell/LookingGlass
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "Show the week date in the calendar"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "If true, display the ISO week date in the calendar."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the application menu."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the overview"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid "Keybinding to focus the active notification."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "Which keyboard to use"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "The type of keyboard to use."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid "Limit switcher to current workspace."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid "The application icon mode."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Workspaces only on primary monitor"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Network Login"
+msgstr "Logáil Isteach Líonra"
+
+#: ../js/extensionPrefs/main.js:122
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "Tharla earráid agus luchtú na dialóige sainroghanna do %s:"
+
+#: ../js/extensionPrefs/main.js:154
+msgid "GNOME Shell Extensions"
+msgstr "Eisínteachtaí GNOME Shell"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:145
+#: ../js/ui/components/polkitAgent.js:179 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:916
+msgid "Cancel"
+msgstr "Cealaigh"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:215
+#: ../js/gdm/authPrompt.js:447
+msgid "Next"
+msgstr "Ar Aghaidh"
+
+#: ../js/gdm/authPrompt.js:211 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "Díghlasáil"
+
+#: ../js/gdm/authPrompt.js:213
+msgctxt "button"
+msgid "Sign In"
+msgstr "Logáil Isteach"
+
+#: ../js/gdm/loginDialog.js:281
+msgid "Choose Session"
+msgstr "Roghnaigh Seisiún"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:431
+msgid "Not listed?"
+msgstr "Gan liostáil?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: ../js/gdm/loginDialog.js:850
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(m.s., úsáideoir nó %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/gdm/loginDialog.js:855 ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:289
+msgid "Username: "
+msgstr "Ainm úsáideora: "
+
+#: ../js/gdm/loginDialog.js:1184
+msgid "Login Window"
+msgstr "Fuinneog Logála Isteach"
+
+#: ../js/gdm/util.js:341
+msgid "Authentication error"
+msgstr "Earráid fhíordheimhnithe"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr "(nó faidhpeáil méar)"
+
+#: ../js/misc/util.js:119
+msgid "Command not found"
+msgstr "Ordú gan aimsiú"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:152
+msgid "Could not parse command:"
+msgstr "Níorbh fhéidir ordú a pharsáil:"
+
+#: ../js/misc/util.js:160
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Theip ar rith “%s”:"
+
+#. Translators: Time in 24h format
+#: ../js/misc/util.js:191
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: ../js/misc/util.js:197
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Inné, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: ../js/misc/util.js:203
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: ../js/misc/util.js:209
+#, no-c-format
+msgid "%B %d, %H∶%M"
+msgstr "%d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: ../js/misc/util.js:215
+#, no-c-format
+msgid "%B %d %Y, %H∶%M"
+msgstr "%d %B %Y, %H∶%M"
+
+#. Translators: Time in 12h format
+#: ../js/misc/util.js:220
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: ../js/misc/util.js:226
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Inné, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: ../js/misc/util.js:232
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: ../js/misc/util.js:238
+#, no-c-format
+msgid "%B %d, %l∶%M %p"
+msgstr "%d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: ../js/misc/util.js:244
+#, no-c-format
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%d %B %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login
+#. * window, until we know the title of the actual login page
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr ""
+
+#: ../js/ui/appDisplay.js:794
+msgid "Frequently used applications will appear here"
+msgstr ""
+
+#: ../js/ui/appDisplay.js:914
+msgid "Frequent"
+msgstr "Feidhmchláir Is Minice"
+
+#: ../js/ui/appDisplay.js:921
+msgid "All"
+msgstr "Gach Feidhmchlár"
+
+#: ../js/ui/appDisplay.js:1853
+msgid "New Window"
+msgstr "Fuinneog Nua"
+
+#: ../js/ui/appDisplay.js:1881 ../js/ui/dash.js:289
+msgid "Remove from Favorites"
+msgstr "Bain ó na Ceanáin"
+
+#: ../js/ui/appDisplay.js:1887
+msgid "Add to Favorites"
+msgstr "Cuir Leis na Ceanáin"
+
+#: ../js/ui/appDisplay.js:1897
+msgid "Show Details"
+msgstr "Taispeáin Mionsonraí"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "Cuireadh %s leis na ceanáin."
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "Baineadh %s ó na ceanáin."
+
+#: ../js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "Athraigh Cúlra…"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Display Settings"
+msgstr "Socruithe Taispeána"
+
+#: ../js/ui/backgroundMenu.js:22 ../js/ui/status/system.js:366
+msgid "Settings"
+msgstr "Socruithe"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: ../js/ui/calendar.js:55
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:84
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:86
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:88
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:90
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "C"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:92
+msgctxt "grid thursday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:94
+msgctxt "grid friday"
+msgid "F"
+msgstr "A"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:96
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:566
+msgid "Previous month"
+msgstr "An mhí roimhe seo"
+
+#: ../js/ui/calendar.js:576
+msgid "Next month"
+msgstr "An chéad mhí eile"
+
+#: ../js/ui/calendar.js:783
+msgid "Week %V"
+msgstr "Seachtain %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:1188
+msgctxt "event list time"
+msgid "All Day"
+msgstr "An Lá ar Fad"
+
+#: ../js/ui/calendar.js:1295
+msgid "Clear section"
+msgstr ""
+
+#: ../js/ui/calendar.js:1522
+msgid "Events"
+msgstr "Imeachtaí"
+
+#: ../js/ui/calendar.js:1531
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %d %B"
+
+#: ../js/ui/calendar.js:1535
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %d %B, %Y"
+
+#: ../js/ui/calendar.js:1620
+msgid "Notifications"
+msgstr "Fógraí"
+
+#: ../js/ui/calendar.js:1771
+msgid "No Notifications"
+msgstr "Gan Fhógra"
+
+#: ../js/ui/calendar.js:1774
+msgid "No Events"
+msgstr "Gan Imeacht"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "Ceanglaíodh tiomántán seachtrach"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "Dícheanglaíodh tiomántán seachtrach"
+
+#: ../js/ui/components/autorunManager.js:354
+#, javascript-format
+msgid "Open with %s"
+msgstr "Oscail le %s"
+
+#: ../js/ui/components/keyring.js:120 ../js/ui/components/polkitAgent.js:315
+msgid "Password:"
+msgstr "Focal faire:"
+
+#: ../js/ui/components/keyring.js:153
+msgid "Type again:"
+msgstr "Clóscríobh arís:"
+
+#: ../js/ui/components/networkAgent.js:140 ../js/ui/status/network.js:269
+#: ../js/ui/status/network.js:352 ../js/ui/status/network.js:919
+msgid "Connect"
+msgstr "Ceangal"
+
+#. Cisco LEAP
+#: ../js/ui/components/networkAgent.js:233
+#: ../js/ui/components/networkAgent.js:245
+#: ../js/ui/components/networkAgent.js:273
+#: ../js/ui/components/networkAgent.js:293
+#: ../js/ui/components/networkAgent.js:303
+msgid "Password: "
+msgstr "Focal faire: "
+
+#. static WEP
+#: ../js/ui/components/networkAgent.js:238
+msgid "Key: "
+msgstr "Eochair:"
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Identity: "
+msgstr "Aitheantas:"
+
+#: ../js/ui/components/networkAgent.js:279
+msgid "Private key password: "
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:291
+msgid "Service: "
+msgstr "Seirbhís:"
+
+#: ../js/ui/components/networkAgent.js:320
+#: ../js/ui/components/networkAgent.js:658
+msgid "Authentication required by wireless network"
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:321
+#: ../js/ui/components/networkAgent.js:659
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:325
+#: ../js/ui/components/networkAgent.js:662
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:327
+msgid "Network name: "
+msgstr "Ainm líonra: "
+
+#: ../js/ui/components/networkAgent.js:332
+#: ../js/ui/components/networkAgent.js:666
+msgid "DSL authentication"
+msgstr "Fíordheimhniú DSL"
+
+#: ../js/ui/components/networkAgent.js:339
+#: ../js/ui/components/networkAgent.js:672
+msgid "PIN code required"
+msgstr "UAP de dhíth"
+
+#: ../js/ui/components/networkAgent.js:340
+#: ../js/ui/components/networkAgent.js:673
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:341
+msgid "PIN: "
+msgstr "UAP: "
+
+#: ../js/ui/components/networkAgent.js:348
+#: ../js/ui/components/networkAgent.js:679
+msgid "Mobile broadband network password"
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:349
+#: ../js/ui/components/networkAgent.js:663
+#: ../js/ui/components/networkAgent.js:667
+#: ../js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Tá focal faire de dhíth chun ceangal le “%s”."
+
+#: ../js/ui/components/networkAgent.js:647 ../js/ui/status/network.js:1658
+msgid "Network Manager"
+msgstr "Bainisteoir Líonra"
+
+#: ../js/ui/components/polkitAgent.js:60
+msgid "Authentication Required"
+msgstr "Fíordheimhniú de Dhíth"
+
+#: ../js/ui/components/polkitAgent.js:102
+msgid "Administrator"
+msgstr "Riarthóir"
+
+#: ../js/ui/components/polkitAgent.js:182
+msgid "Authenticate"
+msgstr "Fíordheimhnigh"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/components/polkitAgent.js:301 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Tá brón orm, theip sé sin. Bain triail eile as, le do thoil."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/components/telepathyClient.js:759
+#, fuzzy, javascript-format
+msgid "%s is now known as %s"
+msgstr "Tugtar %2$s ar %1$s anois"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:155
+msgid "Windows"
+msgstr "Fuinneoga"
+
+#: ../js/ui/dash.js:250 ../js/ui/dash.js:291
+msgid "Show Applications"
+msgstr "Taispeáin Feidhmchláir"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/dash.js:449
+msgid "Dash"
+msgstr "Deais"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:73
+msgid "%B %e %Y"
+msgstr "%e %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: ../js/ui/dateMenu.js:80
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#: ../js/ui/dateMenu.js:160
+msgid "Add world clocks…"
+msgstr "Cuir cloig an domhain leis…"
+
+#: ../js/ui/dateMenu.js:161
+msgid "World Clocks"
+msgstr "Cloig an Domhain"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Logáil %s Amach"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "Logáil Amach"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Logálfar %s amach go huathoibríoch i gceann %d soicind."
+msgstr[1] "Logálfar %s amach go huathoibríoch i gceann %d shoicind."
+msgstr[2] "Logálfar %s amach go huathoibríoch i gceann %d shoicind."
+msgstr[3] "Logálfar %s amach go huathoibríoch i gceann %d soicind."
+msgstr[4] "Logálfar %s amach go huathoibríoch i gceann %d soicind."
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Logálfar thú amach go huathoibríoch i gceann %d soicind."
+msgstr[1] "Logálfar thú amach go huathoibríoch i gceann %d shoicind."
+msgstr[2] "Logálfar thú amach go huathoibríoch i gceann %d shoicind."
+msgstr[3] "Logálfar thú amach go huathoibríoch i gceann %d soicind."
+msgstr[4] "Logálfar thú amach go huathoibríoch i gceann %d soicind."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Logáil Amach"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "Múch"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Suiteáil Nuashonruithe & Múch"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Múchfar an córas go huathoibríoch i gceann %d soicind."
+msgstr[1] "Múchfar an córas go huathoibríoch i gceann %d shoicind."
+msgstr[2] "Múchfar an córas go huathoibríoch i gceann %d shoicind."
+msgstr[3] "Múchfar an córas go huathoibríoch i gceann %d soicind."
+msgstr[4] "Múchfar an córas go huathoibríoch i gceann %d soicind."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Suiteáil nuashonruithe bogearraí ar feitheamh"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "Atosaigh"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "Múch"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "Atosaigh"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Atosófar an córas go huathoibríoch i gceann %d soicind."
+msgstr[1] "Atosófar an córas go huathoibríoch i gceann %d shoicind."
+msgstr[2] "Atosófar an córas go huathoibríoch i gceann %d shoicind."
+msgstr[3] "Atosófar an córas go huathoibríoch i gceann %d soicind."
+msgstr[4] "Atosófar an córas go huathoibríoch i gceann %d soicind."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Atosaigh & Suiteáil Nuashonruithe"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Atosófar an córas agus suiteálfar nuashonruithe go huathoibríoch i gceann %d "
+"soicind."
+msgstr[1] ""
+"Atosófar an córas agus suiteálfar nuashonruithe go huathoibríoch i gceann %d "
+"shoicind."
+msgstr[2] ""
+"Atosófar an córas agus suiteálfar nuashonruithe go huathoibríoch i gceann %d "
+"shoicind."
+msgstr[3] ""
+"Atosófar an córas agus suiteálfar nuashonruithe go huathoibríoch i gceann %d "
+"soicind."
+msgstr[4] ""
+"Atosófar an córas agus suiteálfar nuashonruithe go huathoibríoch i gceann %d "
+"soicind."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Atosaigh &amp; Suiteáil"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Suiteáil &amp; Múch"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Múch tar éis suiteáil nuashonruithe"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "Tá cúpla feidhmchlár gnóthach nó tá obair gan sábháil acu."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "Tá úsáideoirí eile logáilte isteach."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (cianda)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consól)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "Suiteáil"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Íosluchtaigh agus suiteáil “%s” ó extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:741 ../js/ui/status/keyboard.js:713
+msgid "Keyboard"
+msgstr "Méarchlár"
+
+#. translators: 'Hide' is a verb
+#: ../js/ui/legacyTray.js:66
+msgid "Hide tray"
+msgstr "Folaigh tráidire"
+
+#: ../js/ui/legacyTray.js:107
+msgid "Status Icons"
+msgstr "Deilbhíní Stádais"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "Eisínteacht gan suiteáil"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "Folaigh Earráidí"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "Taispeáin Earráidí"
+
+#: ../js/ui/lookingGlass.js:716
+msgid "Enabled"
+msgstr "Cumasaithe"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../src/gvc/gvc-mixer-control.c:1828
+msgid "Disabled"
+msgstr "Díchumasaithe"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "Earráid"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "As dáta"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "Á íosluchtú"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "Féach Foinse"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "Suíomh Gréasáin"
+
+#: ../js/ui/messageTray.js:1486
+msgid "System Information"
+msgstr "Eolas an Chórais"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "Cealaigh"
+
+#: ../js/ui/overview.js:117
+msgid "Overview"
+msgstr "Foramharc"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/overview.js:244
+msgid "Type to search…"
+msgstr "Clóscríobh chun chuardach…"
+
+#: ../js/ui/panel.js:352
+msgid "Quit"
+msgstr "Scoir"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:404
+msgid "Activities"
+msgstr "Gníomhartha"
+
+#: ../js/ui/panel.js:650
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Córas"
+
+#: ../js/ui/panel.js:754
+msgid "Top Bar"
+msgstr "Barra ag an mBarr"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:289
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "Iontráil Ordú"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:162
+msgid "Close"
+msgstr "Dún"
+
+#: ../js/ui/runDialog.js:281
+msgid "Restarting…"
+msgstr "Á atosú…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: ../js/ui/screenShield.js:85
+msgid "%A, %B %d"
+msgstr "%A, %d %B"
+
+#: ../js/ui/screenShield.js:144
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d teachtaireacht nua"
+msgstr[1] "%d theachtaireacht nua"
+msgstr[2] "%d theachtaireacht nua"
+msgstr[3] "%d dteachtaireacht nua"
+msgstr[4] "%d teachtaireacht nua"
+
+#: ../js/ui/screenShield.js:146
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d fhógra nua"
+msgstr[1] "%d fhógra nua"
+msgstr[2] "%d fhógra nua"
+msgstr[3] "%d bhfógra nua"
+msgstr[4] "%d fógra nua"
+
+#: ../js/ui/screenShield.js:432 ../js/ui/status/system.js:374
+msgid "Lock"
+msgstr "Cuir Faoi Ghlas"
+
+#: ../js/ui/screenShield.js:684
+msgid "GNOME needs to lock the screen"
+msgstr "Teastaíonn ó GNOME an scáileán a chur faoi ghlas"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: ../js/ui/screenShield.js:805 ../js/ui/screenShield.js:1271
+msgid "Unable to lock"
+msgstr "Ní féidir cur faoi ghlas"
+
+#: ../js/ui/screenShield.js:806 ../js/ui/screenShield.js:1272
+msgid "Lock was blocked by an application"
+msgstr "Cuireadh cosc ar chur faoi ghlas ag feidmhchlár"
+
+#: ../js/ui/search.js:617
+msgid "Searching…"
+msgstr "Á chuardach…"
+
+#: ../js/ui/search.js:619
+msgid "No results."
+msgstr "Gan torthaí."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "Cóipeáil"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "Greamaigh"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "Taispeáin Téacs"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "Folaigh Téacs"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "Focal Faire"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "Meabhraigh Focal Faire"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "Inrochtaineacht"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "Súmáil"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "Léitheoir Scáileáin"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "Méarchlár Scáileáin"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "Foláirimh Amhairc"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "Eochracha Greamaitheacha"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "Eochracha Malla"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "Eochracha Preabtha"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "Eochracha Luiche"
+
+#: ../js/ui/status/accessibility.js:167
+msgid "High Contrast"
+msgstr "Ardchodarsnacht"
+
+#: ../js/ui/status/accessibility.js:202
+msgid "Large Text"
+msgstr "Téacs Mór"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:353 ../js/ui/status/network.js:1279
+#: ../js/ui/status/network.js:1394 ../js/ui/status/rfkill.js:90
+#: ../js/ui/status/rfkill.js:117
+msgid "Turn Off"
+msgstr "Díchumasaigh"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "Socruithe Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: ../js/ui/status/bluetooth.js:105
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d Ceangailte"
+msgstr[1] "%d Ceangailte"
+msgstr[2] "%d Ceangailte"
+msgstr[3] "%d Ceangailte"
+msgstr[4] "%d Ceangailte"
+
+#: ../js/ui/status/bluetooth.js:107
+msgid "Not In Use"
+msgstr "Níl in Úsáid"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "Gile"
+
+#: ../js/ui/status/keyboard.js:736
+msgid "Show Keyboard Layout"
+msgstr "Taispeáin Leagan Amach an Mhéarchláir"
+
+#: ../js/ui/status/location.js:71 ../js/ui/status/location.js:177
+msgid "Location Enabled"
+msgstr "Suíomh Cumasaithe"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:178
+msgid "Disable"
+msgstr "Díchumasaigh"
+
+#: ../js/ui/status/location.js:73
+msgid "Privacy Settings"
+msgstr "Socruithe Príobháideachais"
+
+#: ../js/ui/status/location.js:176
+msgid "Location In Use"
+msgstr "Suíomh in Úsáid"
+
+#: ../js/ui/status/location.js:180
+msgid "Location Disabled"
+msgstr "Suíomh Díchumasaithe"
+
+#: ../js/ui/status/location.js:181
+msgid "Enable"
+msgstr "Cumasaigh"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<anaithnid>"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:451 ../js/ui/status/network.js:1308
+#, javascript-format
+msgid "%s Off"
+msgstr "%s Múchta"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:454
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s Ceangailte"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: ../js/ui/status/network.js:459
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s Gan Bhainistiú"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:462
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s ag Dícheangal"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1300
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s ag Ceangal"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: ../js/ui/status/network.js:472
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "Fíordheimhniú de Dhíth ar %s"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: ../js/ui/status/network.js:480
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Dochtearraí do %s ar Iarraidh"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: ../js/ui/status/network.js:484
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "Níl %s ar Fáil"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:487
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "Theip Ceangal %s"
+
+#: ../js/ui/status/network.js:503
+msgid "Wired Settings"
+msgstr "Socruithe Sreangaithe"
+
+#: ../js/ui/status/network.js:545 ../js/ui/status/network.js:624
+#, fuzzy
+msgid "Mobile Broadband Settings"
+msgstr "Socruithe Cumhachta"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: ../js/ui/status/network.js:592
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s Díchumasaithe"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr ""
+
+#: ../js/ui/status/network.js:813
+#, fuzzy
+msgid "Airplane Mode is On"
+msgstr "Mód Eitleáin"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr ""
+
+#: ../js/ui/status/network.js:815
+#, fuzzy
+msgid "Turn Off Airplane Mode"
+msgstr "Mód Eitleáin"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Tá Wi-Fi Múchta"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr ""
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Cuir Wi-Fi ar Siúl"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "Líonraí Wi-Fi"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "Roghnaigh líonra"
+
+#: ../js/ui/status/network.js:883
+msgid "No Networks"
+msgstr "Gan Líonra"
+
+#: ../js/ui/status/network.js:904 ../js/ui/status/rfkill.js:115
+msgid "Use hardware switch to turn off"
+msgstr ""
+
+#: ../js/ui/status/network.js:1171
+msgid "Select Network"
+msgstr "Roghnaigh Líonra"
+
+#: ../js/ui/status/network.js:1177
+msgid "Wi-Fi Settings"
+msgstr "Socruithe Wi-Fi"
+
+#: ../js/ui/status/network.js:1279
+msgid "Turn On"
+msgstr "Cumasaigh"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:1296
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:1311
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s Gan Cheangal"
+
+#: ../js/ui/status/network.js:1411
+msgid "connecting..."
+msgstr "á cheangal..."
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:1414
+msgid "authentication required"
+msgstr "fíordheimhniú de dhíth"
+
+#: ../js/ui/status/network.js:1416
+msgid "connection failed"
+msgstr "theip an ceangal"
+
+#: ../js/ui/status/network.js:1482 ../js/ui/status/rfkill.js:93
+msgid "Network Settings"
+msgstr "Socruithe Líonra"
+
+#: ../js/ui/status/network.js:1484
+msgid "VPN Settings"
+msgstr "Socruithe VPN"
+
+#: ../js/ui/status/network.js:1503
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1513
+msgid "VPN Off"
+msgstr "VPN Múchta"
+
+#: ../js/ui/status/network.js:1697
+msgid "Connection failed"
+msgstr "Theip an ceangal"
+
+#: ../js/ui/status/network.js:1698
+msgid "Activation of network connection failed"
+msgstr ""
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "Socruithe Cumhachta"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "Luchtaithe go hIomlán"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "Á mheasúnú…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d Fágtha (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d go dTí Lán (%d%%)"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: ../js/ui/status/rfkill.js:88
+msgid "Airplane Mode On"
+msgstr "Mód Eitleáin ar Siúl"
+
+#: ../js/ui/status/system.js:343
+msgid "Switch User"
+msgstr "Athraigh Úsáideoir"
+
+#: ../js/ui/status/system.js:348
+msgid "Log Out"
+msgstr "Logáil Amach"
+
+#: ../js/ui/status/system.js:353
+msgid "Account Settings"
+msgstr "Socruithe Cuntais"
+
+#: ../js/ui/status/system.js:370
+msgid "Orientation Lock"
+msgstr "Glas Treoshuímh"
+
+#: ../js/ui/status/system.js:378
+msgid "Suspend"
+msgstr "Cuir ar Fionraí"
+
+#: ../js/ui/status/system.js:381
+msgid "Power Off"
+msgstr "Múch"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "Athraíodh an airde"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "Airde"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "Micreafón"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "Logáil isteach mar úsáideoir eile"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "Fuinneog Dhíghlasála"
+
+#: ../js/ui/viewSelector.js:159
+msgid "Applications"
+msgstr "Feidhmchláir"
+
+#: ../js/ui/viewSelector.js:163
+msgid "Search"
+msgstr "Cuardaigh"
+
+#: ../js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Tá “%s” réidh"
+
+#: ../js/ui/windowManager.js:63
+msgid "Do you want to keep these display settings?"
+msgstr "Ar mhaith leat socruithe na taispeána seo a choimeád?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: ../js/ui/windowManager.js:82
+msgid "Revert Settings"
+msgstr "Fill Socruithe"
+
+#: ../js/ui/windowManager.js:85
+msgid "Keep Changes"
+msgstr "Coimeád Athruithe"
+
+#: ../js/ui/windowManager.js:103
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Fillfear athruithe socruithe i gceann %d soicind"
+msgstr[1] "Fillfear athruithe socruithe i gceann %d shoicind"
+msgstr[2] "Fillfear athruithe socruithe i gceann %d shoicind"
+msgstr[3] "Fillfear athruithe socruithe i gceann %d soicind"
+msgstr[4] "Fillfear athruithe socruithe i gceann %d soicind"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: ../js/ui/windowManager.js:658
+#, javascript-format
+msgid "%d x %d"
+msgstr "%d x %d"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "Íoslaghdaigh"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "Uasmhéadaigh"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "Bog"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "Ar Barr i gCónaí"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:105
+msgid "Move to Workspace Left"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:110
+msgid "Move to Workspace Right"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:115
+msgid "Move to Workspace Up"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:120
+msgid "Move to Workspace Down"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:136
+msgid "Move to Monitor Up"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:142
+msgid "Move to Monitor Down"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:148
+msgid "Move to Monitor Left"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:154
+msgid "Move to Monitor Right"
+msgstr ""
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Féilire Evolution"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1835
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u Aschur"
+msgstr[1] "%u Aschur"
+msgstr[2] "%u Aschur"
+msgstr[3] "%u nAschur"
+msgstr[4] "%u Aschur"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1845
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u Ionchur"
+msgstr[1] "%u Ionchur"
+msgstr[2] "%u Ionchur"
+msgstr[3] "%u nIonchur"
+msgstr[4] "%u Ionchur"
+
+#: ../src/gvc/gvc-mixer-control.c:2371
+msgid "System Sounds"
+msgstr "Fuaimeanna Córais"
+
+#: ../src/main.c:381
+msgid "Print version"
+msgstr "Taispeáin leagan"
+
+#: ../src/main.c:387
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: ../src/main.c:393
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr ""
+
+#: ../src/main.c:399
+msgid "List possible modes"
+msgstr ""
+
+#: ../src/shell-app.c:239
+msgctxt "program"
+msgid "Unknown"
+msgstr "Anaithnid"
+
+#: ../src/shell-app.c:480
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Theip ar luchtú “%s”"
+
+#: ../src/shell-keyring-prompt.c:742
+msgid "Passwords do not match."
+msgstr "Ní ionann na focail fhaire."
+
+#: ../src/shell-keyring-prompt.c:750
+msgid "Password cannot be blank"
+msgstr "Ní féidir an focal faire a bheith bán"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr ""
diff --git a/po/gd.po b/po/gd.po
new file mode 100644
index 0000000..9660bd8
--- /dev/null
+++ b/po/gd.po
@@ -0,0 +1,3623 @@
+# Scottish Gaelic translation for gnome-shell.
+# Copyright (C) 2015 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# GunChleoc <fios@foramnagaidhlig.net>, 2015, 2016, 2017, 2018.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-03-20 22:19+0000\n"
+"PO-Revision-Date: 2018-02-26 12:55+0100\n"
+"Last-Translator: GunChleoc <fios@foramnagaidhlig.net>\n"
+"Language-Team: Fòram na Gàidhlig\n"
+"Language: gd\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
+"(n > 2 && n < 20) ? 2 : 3;\n"
+"X-Generator: Virtaal 0.7.1\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr ""
+
+#: data/50-gnome-shell-launchers.xml:10
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 1"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:14
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 2"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:18
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 3"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:22
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 4"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:26
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 5"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:30
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 6"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:34
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 7"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:38
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 8"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/50-gnome-shell-launchers.xml:42
+#, fuzzy
+#| msgid "Open the application menu"
+msgid "Activate favorite application 9"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2062
+msgid "Screenshots"
+msgstr ""
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "Take a screenshot interactively"
+msgstr ""
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:247
+msgid "Take a screenshot"
+msgstr ""
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:243
+msgid "Take a screenshot of a window"
+msgstr ""
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:239
+msgid "Record a screencast interactively"
+msgstr ""
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "An siostam"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Seall liosta nam brathan"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Cuir am fòcas air a’ bhrath ghnìomhach"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Seall am foir-shealladh"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Seall a h-uile aplacaid"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Fosgail clàr-taice nan aplacaidean"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Slige GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Stiùireadh uinneagan is cur gu dol aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Cuir an comas innealan an luchd-leasachaidh ’s deuchainne o Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Ceadaich inntrigeadh dha na h-innealan a chum dì-bhugachaidh ’s sgrùdaidh le "
+"còmhradh Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs nan leudachan ri an cur an comas"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Tha buadh UUID aig leudachain Slige GNOME; seallaidh an iuchair sin na "
+"leudachain a bu chòir luchdadh. Feumaidh gach leudachan a tha ag iarraidh "
+"luchdadh a bhith air an liosta seo. ’S urrainn dhut an liosta seo "
+"atharrachadh cuideachd leis na modhan D-Bus EnableExtension agus "
+"DisableExtension air org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+#, fuzzy
+#| msgid "UUIDs of extensions to enable"
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs nan leudachan ri an cur an comas"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+#, fuzzy
+#| msgid ""
+#| "GNOME Shell extensions have a UUID property; this key lists extensions "
+#| "which should be loaded. Any extension that wants to be loaded needs to be "
+#| "in this list. You can also manipulate this list with the EnableExtension "
+#| "and DisableExtension D-Bus methods on org.gnome.Shell."
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Tha buadh UUID aig leudachain Slige GNOME; seallaidh an iuchair sin na "
+"leudachain a bu chòir luchdadh. Feumaidh gach leudachan a tha ag iarraidh "
+"luchdadh a bhith air an liosta seo. ’S urrainn dhut an liosta seo "
+"atharrachadh cuideachd leis na modhan D-Bus EnableExtension agus "
+"DisableExtension air org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Cuir à comas gach leudachan a chuir an cleachdaiche an comas gun bhuaidh air "
+"an roghainn “enabled-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+"Cuiridh seo à comas dearbhadh na co-chòrdalachd airson tionndadh nan "
+"leudachan"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Cha luchdaich Slige GNOME ach leudachain a dh’innseas gu bheil iad co-"
+"chòrdail leis an tionndadh a tha ’ga ruith. Ma chuireas tu an roghainn seo "
+"an comas, thèid an dearbhadh a chur à comas agus feuchaidh sinn ris a h-uile "
+"leudachan a luchdadh ge b’ e dè na tionndaidhean ris an cuir iad taic dhan "
+"rèir-san."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Liosta dhe IDan nam faidhlichean desktop airson nan aplacaidean as annsa leat"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Thèid na h-aplacaidean a fhreagras ris na h-aithnichearan seo a shealltainn "
+"air raon nan annsachdan."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Eachdraidh a’ chòmhraidh-àithne (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Eachdraidh a’ chòmhraidh aig looking glass"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+"Seall nì “Clàraich a-mach” air clàr-taice a’ chleachdaiche an-còmhnaidh."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Tar-àithnidh an iuchair seo am falach fèin-obrachail aig “Clàraich a-mach” "
+"air a’ chlàr-taice ann an suidheachaidhean le aon chleachdaiche ’s aon "
+"seisean a-mhàin."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Co-dhiù an dèid am facal-faire a chuimhneachadh airson munntachadh "
+"shiostaman-fhaidhlichean crioptaichte no cèine"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Iarraidh an t-slige facal-faire ort nuair a thèid siostam-fhaidhlichean "
+"crioptaichte no cèin a mhunntachadh. Ma ghabhas am facal-faire sàbhaladh "
+"airson na h-ama ri teachd, bidh bogsa-cromaige “Cuimhnich am facal-faire” "
+"ann. Suidhichidh an iuchair seo staid thùsail na bogsa-cromaige ud."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Co-dhiù an robh uidheaman co-cheangailte ris an adaptar Bluetooth gus nach "
+"robh"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Cha seall an t-slige nì clàir-thaice “Bluetooth” ach ma tha cumhachd aig "
+"adaptar Bluetooth no ma chaidh uidheaman co-cheangailte ris an adaptar "
+"tùsail roimhe. Thèid seo ath-shuidheachadh ma chithear an t-adaptar tùsail "
+"gun uidheam co-cheangailte ris."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid "Layout of the app picker"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu"
+msgstr "Nasgadh iuchrach gus clàr-taice nan aplacaidean fhosgladh"
+
+#: data/org.gnome.shell.gschema.xml.in:150
+msgid "Keybinding to open the application menu."
+msgstr "Nasgadh iuchrach gus clàr-taice nan aplacaidean fhosgladh."
+
+#: data/org.gnome.shell.gschema.xml.in:156
+#: data/org.gnome.shell.gschema.xml.in:163
+#, fuzzy
+#| msgid "Keybinding to open the overview"
+msgid "Keybinding to shift between overview states"
+msgstr "Nasgadh iuchrach gus am foir-shealladh fhosgladh"
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:164
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Nasgadh iuchrach gus an sealladh “Seall na h-aplacaidean” fhosgladh"
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Nasgadh iuchrach gus an sealladh “Seall na h-aplacaidean” aig foir-shealladh "
+"nan gnìomhachdan fhosgladh."
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the overview"
+msgstr "Nasgadh iuchrach gus am foir-shealladh fhosgladh"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the Activities Overview."
+msgstr "Nasgadh iuchrach gus foir-shealladh nan gnìomhachdan fhosgladh."
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Nasgadh iuchrach gus liosta nam brathan a shealltainn no fhalach"
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Nasgadh iuchrach gus liosta nam brathan a shealltainn no fhalach."
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification"
+msgstr "Nasgadh iuchrach gus am fòcas a chur air a’ bhrath ghnìomhach"
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to focus the active notification."
+msgstr "Nasgadh iuchrach gus am fòcas a chur air a’ bhrath ghnìomhach."
+
+#: data/org.gnome.shell.gschema.xml.in:199
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 1"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:203
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 2"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:207
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 3"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 4"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 5"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 6"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 7"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 8"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 9"
+msgstr "Seall na h-aplacaidean"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Limit switcher to current workspace."
+msgstr "Cuingich an t-suidsear air an rùm-obrach làithreach."
+
+#: data/org.gnome.shell.gschema.xml.in:257
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Mas e true a th’ ann, cha dèid a shealltainn san t-suidsear ach na h-"
+"aplacaidean aig a bheil uinneag san rùm-obrach làithreach. Mur e, thèid a h-"
+"uile aplacaid a ghabhail a-staigh."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid "The application icon mode."
+msgstr "Modh ìomhaigheagan nan aplacaid."
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Rèitichidh seo an dòigh sa thèid na h-uinneagan a shealltainn san t-"
+"suidsear. ’S e “thumbnail-only” (seallaidh seo dealbhag na h-uinneige), “app-"
+"icon-only” (seallaidh seo ìomhaigheag na h-aplacaid) no “both” (seallaidh "
+"seo an dà chuid dhiubh) a tha sna luachan dligheach."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Mas e true a th’ ann, cha dèid a shealltainn san t-suidsear ach na h-"
+"uinneagan a tha san rùm-obrach làithreach. Mur e, thèid a h-uile uinneag a "
+"ghabhail a-staigh."
+
+#: data/org.gnome.shell.gschema.xml.in:294
+#, fuzzy
+#| msgid "Location In Use"
+msgid "Locations"
+msgstr "Tha an t-ionad ’ga chleachdadh"
+
+#: data/org.gnome.shell.gschema.xml.in:295
+msgid "The locations to show in world clocks"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Automatic location"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:306
+msgid "Whether to fetch the current location or not"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:313
+#, fuzzy
+#| msgid "Location In Use"
+msgid "Location"
+msgstr "Tha an t-ionad ’ga chleachdadh"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "The location for which to show a forecast"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:326
+msgid "Attach modal dialog to the parent window"
+msgstr "Ceangail còmhradh mòdach ris an uinneag-pàrant"
+
+#: data/org.gnome.shell.gschema.xml.in:327
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+#: data/org.gnome.shell.gschema.xml.in:352
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Tar-àithnidh an iuchair seo an iuchair ann an org.gnome.mutter nuair a thèid "
+"Slige GNOME a ruith."
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Cuir leacadh nan oirean an comas nuair a thèid uinneagan a leigeil às aig "
+"oir na sgrìn"
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Workspaces are managed dynamically"
+msgstr "Thèid na rumannan-obrach a stiùireadh gu h-innsgineach"
+
+#: data/org.gnome.shell.gschema.xml.in:351
+msgid "Workspaces only on primary monitor"
+msgstr "Rumannan-obrach air a’ phrìomh-mhonatair a-mhàin"
+
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Cuir dàil air atharraichean an fòcais ann am modh luchaige gus an sguir an "
+"tomhaire air gluasad"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Clàradh a-steach lìonraidh"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+#, fuzzy
+#| msgid "Show Details"
+msgid "Technical Details"
+msgstr "Seall am mion-fhiosrachadh"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+#, fuzzy
+#| msgid "UUIDs of extensions to enable"
+msgid "Visit extension homepage"
+msgstr "UUIDs nan leudachan ri an cur an comas"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:437 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: js/ui/status/network.js:956 subprojects/extensions-app/js/main.js:174
+msgid "Cancel"
+msgstr "Sguir dheth"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Facal-faire "
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Tagh seisean"
+
+#: js/gdm/loginDialog.js:463
+msgid "Not listed?"
+msgstr "Nach eil d’ ainm air an liosta?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:931
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(m.e., cleachdaiche no %s)"
+
+#: js/gdm/loginDialog.js:936 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+#, fuzzy
+#| msgid "Username: "
+msgid "Username"
+msgstr "Ainm-cleachdaiche:"
+
+#: js/gdm/loginDialog.js:1257
+msgid "Login Window"
+msgstr "Uinneag a’ chlàraidh a-steach"
+
+#: js/gdm/util.js:434
+msgid "Authentication error"
+msgstr "Mearachd leis an dearbhadh"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:606
+#, fuzzy
+#| msgid "(or swipe finger)"
+msgid "(or swipe finger across reader)"
+msgstr "(no grad-shlaighd le do mheur)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:611
+#, fuzzy
+#| msgid "(or swipe finger)"
+msgid "(or place finger on reader)"
+msgstr "(no grad-shlaighd le do mheur)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Cuir dheth"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+#, fuzzy
+#| msgid "power off;shutdown;reboot;restart"
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+"power off;shutdown;reboot;restart;cumhachd;dheth;cur sìos;cuir sìos;cuir "
+"dheth;cur dheth;dùin;dùin sìos;ath-thòisich;ath-thòiseachadh;tòisich"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+#, fuzzy
+#| msgctxt "button"
+#| msgid "Restart"
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Ath-thòisich"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr ""
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Glais an sgrìn"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "sgrìn-ghlasaidh;glais;glas;sgrìn"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Clàraich a-mach"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+#, fuzzy
+#| msgid "logout;sign off"
+msgid "logout;log out;sign off"
+msgstr "logout;sign off;clàraidh a-mach;clàradh a-mach;logadh a-mach;"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Cuir ’na dhàil"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;cuir 'na dhàil;cur 'na dhàil;cuir na dhàil;cur na dhàil;"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Leum gu cleachdaiche eile"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "switch user;suids;cleachdaiche"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+#, fuzzy
+#| msgid "lock orientation;screen;rotation"
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;screen;rotation;glais;comhair;glais a' chomhair;sgrìn;"
+"cuairteachadh;cuairtich"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr ""
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+
+#: js/misc/systemActions.js:242
+#, fuzzy
+#| msgctxt "search-result"
+#| msgid "Lock Orientation"
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Glais a’ chomhair"
+
+#: js/misc/systemActions.js:243
+#, fuzzy
+#| msgctxt "search-result"
+#| msgid "Lock Orientation"
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Glais a’ chomhair"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Cha deach an àithne a lorg"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Cha b’ urrainn dhuinn an àithne a pharsadh:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Cha b’ urrainn dhuinn “%s” a ruith:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "An-dràsta fhèin"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d mhionaid air ais"
+msgstr[1] "%d mhionaid air ais"
+msgstr[2] "%d mionaidean air ais"
+msgstr[3] "%d mionaid air ais"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d uair a thìde air ais"
+msgstr[1] "%d uair a thìde air ais"
+msgstr[2] "%d uairean a thìde air ais"
+msgstr[3] "%d uair a thìde air ais"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "An-dè"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d latha air ais"
+msgstr[1] "%d latha air ais"
+msgstr[2] "%d làithean air ais"
+msgstr[3] "%d latha air ais"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d seachdain air ais"
+msgstr[1] "%d sheachdain air ais"
+msgstr[2] "%d seachdainean air ais"
+msgstr[3] "%d seachdain air ais"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d mhìos air ais"
+msgstr[1] "%d mhìos air ais"
+msgstr[2] "%d mìosan air ais"
+msgstr[3] "%d mìos air ais"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d bhliadhna air ais"
+msgstr[1] "%d bhliadhna air ais"
+msgstr[2] "%d bliadhnaichean air ais"
+msgstr[3] "%d bliadhna air ais"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "An-dè aig %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, fuzzy, no-c-format
+#| msgid "%B %d, %H∶%M"
+msgid "%B %-d, %H∶%M"
+msgstr "%d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, fuzzy, no-c-format
+#| msgid "%B %d %Y, %H∶%M"
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M%p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "An-dè aig %l:%M%p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M%p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, fuzzy, no-c-format
+#| msgid "%B %d, %l∶%M %p"
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d %B, %l∶%M%p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, fuzzy, no-c-format
+#| msgid "%B %d %Y, %l∶%M %p"
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d %B %Y, %l∶%M%p"
+
+#: js/portalHelper/main.js:53
+msgid "Hotspot Login"
+msgstr "Clàradh a-steach Hotspot"
+
+#: js/portalHelper/main.js:106
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Chan eil an ceangal agad gu clàradh a-steach a’ hotspot seo tèarainte. Chì "
+"daoine a tha faisg ort na faclan-faire agus fiosrachadh sam bith eile a "
+"chuireas tu a-steach air an duilleag seo."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:406
+msgid "Deny Access"
+msgstr "Diùlt an t-inntrigeadh"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:411
+msgid "Grant Access"
+msgstr "Ceadaich an t-inntrigeadh"
+
+#: js/ui/appFavorites.js:164
+#, fuzzy, javascript-format
+#| msgid "%s has been added to your favorites."
+msgid "%s has been pinned to the dash."
+msgstr "Chaidh %s a chur ris na h-annsachdan agad."
+
+#: js/ui/appFavorites.js:197
+#, fuzzy, javascript-format
+#| msgid "%s has been removed from your favorites."
+msgid "%s has been unpinned from the dash."
+msgstr "Chaidh %s a toirt air falbh o na h-annsachdan agad."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+#, fuzzy
+#| msgid "Windows"
+msgid "Open Windows"
+msgstr "Uinneagan"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Uinneag ùr"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Seall am mion-fhiosrachadh"
+
+#: js/ui/appMenu.js:96
+msgid "Quit"
+msgstr "Fàg an-seo"
+
+#: js/ui/appMenu.js:156 js/ui/dash.js:249
+msgid "Unpin"
+msgstr ""
+
+#: js/ui/appMenu.js:157
+msgid "Pin to Dash"
+msgstr ""
+
+#: js/ui/appMenu.js:174
+#, fuzzy
+#| msgid "Launch using Dedicated Graphics Card"
+msgid "Launch using Integrated Graphics Card"
+msgstr "Cuir gu dol le cairt-ghrafaigeachd shònraichte"
+
+#: js/ui/appMenu.js:175
+#, fuzzy
+#| msgid "Launch using Dedicated Graphics Card"
+msgid "Launch using Discrete Graphics Card"
+msgstr "Cuir gu dol le cairt-ghrafaigeachd shònraichte"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Tagh uidheam fuaime"
+
+#: js/ui/audioDeviceSelection.js:56
+msgid "Sound Settings"
+msgstr "Roghainnean na fuaime"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Headphones"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:270
+msgid "Microphone"
+msgstr "Micreofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Atharraich an cùlaibh…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Roghainnean an t-seallaidh"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Roghainnean"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Dò"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Lu"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Mà"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ci"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Da"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "hA"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Dò"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:412
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:472
+msgid "Previous month"
+msgstr "Am mìos roimhe"
+
+#: js/ui/calendar.js:490
+msgid "Next month"
+msgstr "An ath mhìos"
+
+#: js/ui/calendar.js:642
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:701
+msgid "Week %V"
+msgstr "Seachdain %V"
+
+#: js/ui/calendar.js:880
+msgid "No Notifications"
+msgstr "Chan eil brath ann"
+
+#: js/ui/calendar.js:937
+msgid "Do Not Disturb"
+msgstr ""
+
+#: js/ui/calendar.js:958
+#, fuzzy
+#| msgid "Clear All"
+msgid "Clear"
+msgstr "Falamhaich na h-uile"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "Chan eil “%s” a’ freagairt."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Faodaidh tu feitheamh greis gus an lean seo air adhart no sparradh air an "
+"aplacaid gum fàg i gu lèir."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Spàrr fàgail air"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Fuirich air"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Chaidh draibh on taobh a-muigh a cheangal"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Chaidh draibh on taobh a-muigh a dhì-cheangal"
+
+#: js/ui/components/automountManager.js:207
+#, fuzzy
+#| msgid "Unable to lock"
+msgid "Unable to unlock volume"
+msgstr "Cha ghabh a ghlasadh"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:334
+#, javascript-format
+msgid "Open with %s"
+msgstr "Fosgail le %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:244
+#: js/ui/status/network.js:335 js/ui/status/network.js:961
+msgid "Connect"
+msgstr "Ceangail"
+
+#: js/ui/components/networkAgent.js:218
+#, fuzzy
+#| msgid "Key: "
+msgid "Key"
+msgstr "Iuchair: "
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+#, fuzzy
+#| msgid "Private key password: "
+msgid "Private key password"
+msgstr "Facal-faire na h-iuchrach prìobhaidich: "
+
+#: js/ui/components/networkAgent.js:302
+#, fuzzy
+#| msgid "Identity: "
+msgid "Identity"
+msgstr "Dearbh-aithne: "
+
+#: js/ui/components/networkAgent.js:328
+#, fuzzy
+#| msgid "Service: "
+msgid "Service"
+msgstr "Seirbheis: "
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:746 js/ui/components/networkAgent.js:767
+#, fuzzy
+#| msgid "authentication required"
+msgid "Authentication required"
+msgstr "tha feum air dearbhadh"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:747
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Tha feum air faclan-faire no iuchraichean crioptachaidh gus an lìonra "
+"uèirleas “%s” inntrigeadh."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:751
+msgid "Wired 802.1X authentication"
+msgstr "Dearbhadh Wired 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+#, fuzzy
+#| msgid "Network name: "
+msgid "Network name"
+msgstr "Ainm an lìonraidh: "
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:755
+msgid "DSL authentication"
+msgstr "Dearbhadh DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:760
+msgid "PIN code required"
+msgstr "Tha feum air còd PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:761
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Tha feum air còd PIN airson an uidheim mobile banna-leathainn"
+
+#: js/ui/components/networkAgent.js:392
+#, fuzzy
+#| msgid "PIN: "
+msgid "PIN"
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:752
+#: js/ui/components/networkAgent.js:756 js/ui/components/networkAgent.js:768
+#: js/ui/components/networkAgent.js:772
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Tha feum air facal-faire airson ceangal ri “%s”."
+
+#: js/ui/components/networkAgent.js:735 js/ui/status/network.js:1751
+msgid "Network Manager"
+msgstr "Manaidsear nan lìonra"
+
+#: js/ui/components/networkAgent.js:771
+#, fuzzy
+#| msgid "Password"
+msgid "VPN password"
+msgstr "Facal-faire "
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Tha feum air dearbhadh"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Rianaire"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Dearbhaich"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr ""
+"Tha sinn duilich ach cha do dh’obraich sin. Am feuch thu ris a-rithist?"
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Thug %s %s air"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:405
+msgid "Windows"
+msgstr "Uinneagan"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Seall na h-aplacaidean"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Deas-bhòrd"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+#, fuzzy
+#| msgid "%B %e %Y"
+msgid "%B %-d %Y"
+msgstr "%e %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+#, fuzzy
+#| msgctxt "calendar heading"
+#| msgid "%A, %B %d"
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%A, %d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+#, fuzzy
+#| msgid "%B %e %Y"
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%e %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr ""
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr ""
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Fad an latha"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr ""
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Chan eil tachartas ann"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Cuir uaireadairean an t-saoghail ris…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Uaireadairean an t-saoghail"
+
+#: js/ui/dateMenu.js:677
+msgid "Loading…"
+msgstr "’Ga luchdadh…"
+
+#: js/ui/dateMenu.js:687
+msgid "Go online for weather information"
+msgstr "Rach air loidhne airson fiosrachadh mun aimsir"
+
+#: js/ui/dateMenu.js:689
+msgid "Weather information is currently unavailable"
+msgstr "Chan eil fiosrachadh mun aimsir ri fhaighinn an-dràsta"
+
+#: js/ui/dateMenu.js:699
+msgid "Weather"
+msgstr "An aimsir"
+
+#: js/ui/dateMenu.js:701
+#, fuzzy
+#| msgid "Select a location…"
+msgid "Select weather location…"
+msgstr "Tagh ionad…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Clàraich a-mach %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Clàraich a-mach"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Thèid %s a chlàradh a-mach gu fèin-obrachail an ceann %d diog."
+msgstr[1] "Thèid %s a chlàradh a-mach gu fèin-obrachail an ceann %d dhiog."
+msgstr[2] "Thèid %s a chlàradh a-mach gu fèin-obrachail an ceann %d diogan."
+msgstr[3] "Thèid %s a chlàradh a-mach gu fèin-obrachail an ceann %d diog."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Thèid thu a chlàradh a-mach gu fèin-obrachail an ceann %d diog."
+msgstr[1] "Thèid thu a chlàradh a-mach gu fèin-obrachail an ceann %d dhiog."
+msgstr[2] "Thèid thu a chlàradh a-mach gu fèin-obrachail an ceann %d diogan."
+msgstr[3] "Thèid thu a chlàradh a-mach gu fèin-obrachail an ceann %d diog."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Clàraich a-mach"
+
+#: js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Power Off"
+msgstr "Cuir dheth"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Stàlaich na h-ùrachaidhean ⁊ cuir dheth"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Thèid an siostam a chur dheth gu fèin-obrachail an ceann %d diog."
+msgstr[1] "Thèid an siostam a chur dheth gu fèin-obrachail an ceann %d dhiog."
+msgstr[2] "Thèid an siostam a chur dheth gu fèin-obrachail an ceann %d diogan."
+msgstr[3] "Thèid an siostam a chur dheth gu fèin-obrachail an ceann %d diog."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Stàlaich na h-ùrachaidhean bathair-bhog a tha a’ feitheamh"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Cuir dheth"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Ath-thòisich"
+
+#: js/ui/endSessionDialog.js:84
+#, fuzzy
+#| msgctxt "title"
+#| msgid "Install Updates & Power Off"
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Stàlaich na h-ùrachaidhean ⁊ cuir dheth"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] ""
+"Thèid an siostam a thòiseachadh às ùr gu fèin-obrachail an ceann %d diog."
+msgstr[1] ""
+"Thèid an siostam a thòiseachadh às ùr gu fèin-obrachail an ceann %d dhiog."
+msgstr[2] ""
+"Thèid an siostam a thòiseachadh às ùr gu fèin-obrachail an ceann %d diogan."
+msgstr[3] ""
+"Thèid an siostam a thòiseachadh às ùr gu fèin-obrachail an ceann %d diog."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Ath-thòisich"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Ath-thòisich ⁊ stàlaich na h-ùrachaidhean"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Tòisichidh an siostam às ùr is stàlaichidh e ùrachaidhean gu fèin-obrachail "
+"an ceann %d diog."
+msgstr[1] ""
+"Tòisichidh an siostam às ùr is stàlaichidh e ùrachaidhean gu fèin-obrachail "
+"an ceann %d dhiog."
+msgstr[2] ""
+"Tòisichidh an siostam às ùr is stàlaichidh e ùrachaidhean gu fèin-obrachail "
+"an ceann %d diogan."
+msgstr[3] ""
+"Tòisichidh an siostam às ùr is stàlaichidh e ùrachaidhean gu fèin-obrachail "
+"an ceann %d diog."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Ath-thòisich ⁊ stàlaich"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Stàlaich ⁊ cuir dheth"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Dùin sìos nuair a bhios na h-ùrachaidhean air an stàladh"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Ath-thòisich ⁊ stàlaich an t-àrdachadh"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Thèid %s %s a stàladh às dèidh an ath-thòiseachaidh. Dh’fhaoidte gun doir e "
+"fada an t-àrdachadh a stàladh: dèan cinnteach gun do rinn thu lethbhreac-"
+"glèidhidh agus gu bheil an coimpiutair ceangailte ri bun-dealain."
+
+#: js/ui/endSessionDialog.js:287
+#, fuzzy
+#| msgid "Running on battery power: please plug in before installing updates."
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"A’ ruith air cumhachd a’ bhataraidh: ceangail gu bun-dealain mus stàlaich "
+"thu ùrachaidhean."
+
+#: js/ui/endSessionDialog.js:296
+#, fuzzy
+#| msgid "Some applications are busy or have unsaved work."
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+"Tha cuid a dh’aplacaidean trang no tha obair aca nach deach a shàbhaladh."
+
+#: js/ui/endSessionDialog.js:301
+#, fuzzy
+#| msgid "Other users are logged in."
+msgid "Other users are logged in"
+msgstr "Tha daoine eile air an clàradh a-steach."
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr ""
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:688
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (cèin)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:691
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consoil)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Stàlaich"
+
+#: js/ui/extensionDownloader.js:233
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "Install Extension"
+msgstr "Leudachain na slige"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+"A bheil thu airson “%s” a luchdadh a-nuas is a stàladh o extensions.gnome."
+"org?"
+
+#: js/ui/extensionSystem.js:267
+msgid "Extension Updates Available"
+msgstr ""
+
+#: js/ui/extensionSystem.js:268
+msgid "Extension updates are ready to be installed."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:79
+#, fuzzy
+#| msgid "%s wants to inhibit shortcuts"
+msgid "Allow inhibiting shortcuts"
+msgstr "Tha %s airson ath-ghoiridean a bhacadh"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, fuzzy, javascript-format
+#| msgid "Application wants to inhibit shortcuts"
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Tha aplacaid airson ath-ghoiridean a bhacadh"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+#, fuzzy
+#| msgid "Application wants to inhibit shortcuts"
+msgid "An application wants to inhibit shortcuts"
+msgstr "Tha aplacaid airson ath-ghoiridean a bhacadh"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "’S urrainn dut na h-ath-ghoiridean aiseag ’s tu a’ brùthadh air %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Diùlt"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Ceadaich"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tha na h-iuchraichean slaodach air"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tha na h-iuchraichean slaodach dheth"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Chum thu sìos an iuchair Shift fad 8 diogan. Seo an ath-ghoirid airson gleus "
+"nan iuchraichean slaodach agus tha buaidh aige air an dòigh air a "
+"dh’obraicheas am meur-chlàr agad."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Tha na h-iuchraichean steigeach air"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Tha na h-iuchraichean steigeach dheth"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Bhrùth thu an iuchair Shift 5 tursan às dèidh a chèile. Seo an ath-ghoirid "
+"airson gleus nan iuchraichean steigeach agus tha buaidh aige air an dòigh "
+"air a dh’obraicheas am meur-chlàr agad."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Bhrùth thu dà iuchair aig an aon àm no bhrùth thu an iuchair Shift 5 tursan "
+"às dèidh a chèile. Cuiridh seo dheth gleus nan iuchraichean steigeach agus "
+"tha buaidh aige air an dòigh air a dh’obraicheas am meur-chlàr agad."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Fàg air"
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:1341
+msgid "Turn On"
+msgstr "Cuir air"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:160 js/ui/status/network.js:336
+#: js/ui/status/network.js:1341 js/ui/status/network.js:1456
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Cuir dheth"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Fàg dheth"
+
+#: js/ui/keyboard.js:250
+msgid "Region & Language Settings"
+msgstr "Roghainnean roinn-dùthcha ⁊ cànain"
+
+#: js/ui/lookingGlass.js:701
+msgid "No extensions installed"
+msgstr "Cha deach leudachan a stàladh"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:762
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "Cha robh mearachd sam bith aig %s."
+
+#: js/ui/lookingGlass.js:768
+msgid "Hide Errors"
+msgstr "Falaich na mearachdan"
+
+#: js/ui/lookingGlass.js:772 js/ui/lookingGlass.js:845
+msgid "Show Errors"
+msgstr "Seall na mearachdan"
+
+#: js/ui/lookingGlass.js:781
+msgid "Enabled"
+msgstr "An comas"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:784 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "À comas"
+
+#: js/ui/lookingGlass.js:786
+msgid "Error"
+msgstr "Mearachd"
+
+#: js/ui/lookingGlass.js:788
+msgid "Out of date"
+msgstr "Ro shean"
+
+#: js/ui/lookingGlass.js:790
+msgid "Downloading"
+msgstr "’Ga luchdadh a-nuas"
+
+#: js/ui/lookingGlass.js:823
+msgid "View Source"
+msgstr "Seall am bun-tùs"
+
+#: js/ui/lookingGlass.js:834
+msgid "Web Page"
+msgstr "Duilleag-lìn"
+
+#: js/ui/main.js:265
+msgid "System was put in unsafe mode"
+msgstr ""
+
+#: js/ui/main.js:266
+msgid "Applications now have unrestricted access"
+msgstr ""
+
+#: js/ui/main.js:267 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Neo-dhèan"
+
+#: js/ui/main.js:313
+#, fuzzy
+#| msgid "Log in as another user"
+msgid "Logged in as a privileged user"
+msgstr "Clàraich a-steach mar chleachdaiche eile"
+
+#: js/ui/main.js:314
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Fiosrachadh an t-siostaim"
+
+#: js/ui/mpris.js:200
+msgid "Unknown artist"
+msgstr "Chan eil fhios cò an neach-ciùil"
+
+#: js/ui/mpris.js:210
+msgid "Unknown title"
+msgstr "Chan eil fhios dè an tiotal"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:315
+#, fuzzy
+#| msgid "Type to search…"
+msgid "Type to search"
+msgstr "Sgrìobh airson lorg…"
+
+#: js/ui/overviewControls.js:393
+msgid "Applications"
+msgstr "Aplacaidean"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Foir-shealladh"
+
+#: js/ui/padOsd.js:97
+msgid "New shortcut…"
+msgstr "Ath-ghoirid ùr…"
+
+#: js/ui/padOsd.js:148
+msgid "Application defined"
+msgstr "A-rèir na h-aplacaid"
+
+#: js/ui/padOsd.js:149
+msgid "Show on-screen help"
+msgstr "Seall a’ chobhair air an sgrìn"
+
+#: js/ui/padOsd.js:150
+msgid "Switch monitor"
+msgstr "Gearr leum gu monatair eile"
+
+#: js/ui/padOsd.js:151
+msgid "Assign keystroke"
+msgstr "Sònraich buille-iuchrach dha"
+
+#: js/ui/padOsd.js:220
+msgid "Done"
+msgstr "Deiseil"
+
+#: js/ui/padOsd.js:737
+msgid "Edit…"
+msgstr "Deasaich…"
+
+#: js/ui/padOsd.js:779 js/ui/padOsd.js:896
+msgid "None"
+msgstr "Chan eil gin"
+
+#: js/ui/padOsd.js:850
+msgid "Press a button to configure"
+msgstr "Brùth air putan gus a rèiteachadh"
+
+#: js/ui/padOsd.js:851
+msgid "Press Esc to exit"
+msgstr "Brùth air Esc airson fàgail"
+
+#: js/ui/padOsd.js:854
+msgid "Press any key to exit"
+msgstr "Brùth air iuchair sam bith airson fàgail"
+
+#: js/ui/panel.js:241
+msgid "Activities"
+msgstr "Gnìomhachdan"
+
+#: js/ui/panel.js:364
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "An siostam"
+
+#: js/ui/panel.js:480
+msgid "Top Bar"
+msgstr "Bàr a’ bharra"
+
+#: js/ui/runDialog.js:58
+#, fuzzy
+#| msgid "Enter a Command"
+msgid "Run a Command"
+msgstr "Cuir a-steach àithne"
+
+#: js/ui/runDialog.js:73
+#, fuzzy
+#| msgid "Press Esc to exit"
+msgid "Press ESC to close"
+msgstr "Brùth air Esc airson fàgail"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Chan eil ath-thòiseachadh ri làimh air Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "’Ga ath-thòiseachadh…"
+
+#: js/ui/screenShield.js:229
+msgid "GNOME needs to lock the screen"
+msgstr "Feumaidh GNOME an sgrìn a ghlasadh"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:269 js/ui/screenShield.js:644
+msgid "Unable to lock"
+msgstr "Cha ghabh a ghlasadh"
+
+#: js/ui/screenShield.js:270 js/ui/screenShield.js:645
+msgid "Lock was blocked by an application"
+msgstr "Chaidh a’ ghlas a bhacadh le aplacaid"
+
+#: js/ui/screenshot.js:1148
+#, fuzzy
+#| msgid "evolution"
+msgid "Selection"
+msgstr "evolution"
+
+#: js/ui/screenshot.js:1158
+#, fuzzy
+#| msgid "Clear section"
+msgid "Area Selection"
+msgstr "Falamhaich an earrann"
+
+#: js/ui/screenshot.js:1163
+#, fuzzy
+#| msgctxt "search-result"
+#| msgid "Lock Screen"
+msgid "Screen"
+msgstr "Glais an sgrìn"
+
+#: js/ui/screenshot.js:1173
+#, fuzzy
+#| msgid "Screen Reader"
+msgid "Screen Selection"
+msgstr "Leughadair sgrìn"
+
+#: js/ui/screenshot.js:1178
+#, fuzzy
+#| msgid "Windows"
+msgid "Window"
+msgstr "Uinneagan"
+
+#: js/ui/screenshot.js:1188
+msgid "Window Selection"
+msgstr ""
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr ""
+
+#: js/ui/screenshot.js:1261
+#, fuzzy
+#| msgid "Show Errors"
+msgid "Show Pointer"
+msgstr "Seall na mearachdan"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1828
+msgid "Screencasts"
+msgstr ""
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1833
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr ""
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1902 js/ui/screenshot.js:2115
+msgid "Screenshot"
+msgstr ""
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1908
+#, fuzzy
+#| msgid "Screen Keyboard"
+msgid "Screencast recorded"
+msgstr "Meur-chlàr air an sgrìn"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1910
+msgid "Click here to view the video."
+msgstr ""
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2129
+#, fuzzy
+#| msgid "Show Details"
+msgid "Show in Files"
+msgstr "Seall am mion-fhiosrachadh"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2075
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr ""
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2121
+msgid "Screenshot captured"
+msgstr ""
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2123
+msgid "You can paste the image from the clipboard."
+msgstr ""
+
+#: js/ui/screenshot.js:2176 js/ui/screenshot.js:2341
+#, fuzzy
+#| msgid "Screen Reader"
+msgid "Screenshot taken"
+msgstr "Leughadair sgrìn"
+
+#: js/ui/search.js:815
+msgid "Searching…"
+msgstr "’Ga lorg…"
+
+#: js/ui/search.js:817
+msgid "No results."
+msgstr "Chan eil toradh ann."
+
+#: js/ui/search.js:948
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d a bharrachd"
+msgstr[1] "%d a bharrachd"
+msgstr[2] "%d a bharrachd"
+msgstr[3] "%d a bharrachd"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Lorg"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Dèan lethbhreac"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Cuir ann"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Seall an teacsa"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Falaich an teacsa"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:286
+#, fuzzy
+#| msgid "Volume"
+msgid "Hidden Volume"
+msgstr "Àirde na fuaime"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:292
+#, fuzzy
+#| msgid "Mouse Keys"
+msgid "Uses Keyfiles"
+msgstr "Iuchraichean luchaige"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Cuimhnich am facal-faire"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Neo-ghlais"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, fuzzy, javascript-format
+#| msgid "Open with %s"
+msgid "Open %s"
+msgstr "Fosgail le %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, fuzzy, javascript-format
+#| msgid "Unable to lock"
+msgid "Unable to start %s"
+msgstr "Cha ghabh a ghlasadh"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr ""
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "So-ruigsinneachd"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Iomsgaradh àrd"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Sùm"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Leughadair sgrìn"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Meur-chlàr air an sgrìn"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Rabhaidhean lèirsinneach"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Iuchraichean steigeach"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Iuchraichean slaodach"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Iuchraichean bocaidh"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Iuchraichean an luch-chlàir"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Teacsa mòr"
+
+#: js/ui/status/bluetooth.js:54
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:67 js/ui/status/network.js:626
+msgid "Bluetooth Settings"
+msgstr "Roghainnean Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:166
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d uidheam ceangailte"
+msgstr[1] "%d uidheam ceangailte"
+msgstr[2] "%d uidheaman ceangailte"
+msgstr[3] "%d uidheam ceangailte"
+
+#: js/ui/status/bluetooth.js:170
+#, fuzzy
+#| msgid "Bluetooth"
+msgid "Bluetooth On"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:172
+#, fuzzy
+#| msgid "Bluetooth"
+msgid "Bluetooth Off"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Soilleireachd"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr ""
+
+#: js/ui/status/keyboard.js:827
+msgid "Keyboard"
+msgstr "Meur-chlàr"
+
+#: js/ui/status/keyboard.js:844
+msgid "Show Keyboard Layout"
+msgstr "Seall co-dhealbhachd a’ mheur-chlàir"
+
+#: js/ui/status/location.js:231 js/ui/status/location.js:255
+msgid "Location Enabled"
+msgstr "Tha an t-ionad an comas"
+
+#: js/ui/status/location.js:232 js/ui/status/location.js:256
+msgid "Disable"
+msgstr "Cuir à comas"
+
+#: js/ui/status/location.js:234
+msgid "Privacy Settings"
+msgstr "Roghainnean na prìobhaideachd"
+
+#: js/ui/status/location.js:254
+msgid "Location In Use"
+msgstr "Tha an t-ionad ’ga chleachdadh"
+
+#: js/ui/status/location.js:258
+msgid "Location Disabled"
+msgstr "Tha an t-ionad à comas"
+
+#: js/ui/status/location.js:259
+msgid "Enable"
+msgstr "Cuir an comas"
+
+#: js/ui/status/location.js:386
+msgid "Allow location access"
+msgstr ""
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:388
+#, fuzzy, javascript-format
+#| msgid "Give %s access to your location?"
+msgid "The app %s wants to access your location"
+msgstr "A bheil thu airson inntrigeadh dhan ionad agad a thoirt dha %s?"
+
+#: js/ui/status/location.js:398
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"’S urrainn dhut inntrigeadh an ionaid atharrachadh uair sam bith ann an "
+"roghainnean na prìobhaideachd."
+
+#: js/ui/status/network.js:71
+msgid "<unknown>"
+msgstr "<neo-aithnichte>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:436 js/ui/status/network.js:1370
+#, javascript-format
+msgid "%s Off"
+msgstr "Tha %s dheth"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:439
+#, javascript-format
+msgid "%s Connected"
+msgstr "Ceangailte ri %s"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:444
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "Chan eil %s ‘ga stiùireadh"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:447
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "A’ dì-cheangal o %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454 js/ui/status/network.js:1362
+#, javascript-format
+msgid "%s Connecting"
+msgstr "A’ ceangal ri %s"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:457
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "Tha %s ag iarraidh dearbhadh"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:465
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Tha bathar-an-sàs a dhìth air %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:469
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "Chan eil %s ri fhaighinn"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:472
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "Cha deach leinn ceangal ri %s"
+
+#: js/ui/status/network.js:484
+msgid "Wired Settings"
+msgstr "Roghainnean le uèir"
+
+#: js/ui/status/network.js:531
+msgid "Mobile Broadband Settings"
+msgstr "Roghainnean banna-leathainn mobile"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:587 js/ui/status/network.js:1367
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "Tha bathar-cruaidh %s à comas"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:591
+#, javascript-format
+msgid "%s Disabled"
+msgstr "Tha %s à comas"
+
+#: js/ui/status/network.js:638
+msgid "Connect to Internet"
+msgstr "Ceangail ris an eadar-lìon"
+
+#: js/ui/status/network.js:836
+msgid "Airplane Mode is On"
+msgstr "Tha am modh itealain air"
+
+#: js/ui/status/network.js:837
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Tha WiFi à comas fhad ’s a tha am modh itealain air."
+
+#: js/ui/status/network.js:838
+msgid "Turn Off Airplane Mode"
+msgstr "Cuir am modh itealain dheth"
+
+#: js/ui/status/network.js:847
+msgid "Wi-Fi is Off"
+msgstr "Tha WiFi dheth"
+
+#: js/ui/status/network.js:848
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Feumaidh tu WiFi a chur air gus ceangal ri lìonra."
+
+#: js/ui/status/network.js:849
+msgid "Turn On Wi-Fi"
+msgstr "Cuir WiFi air"
+
+#: js/ui/status/network.js:877
+msgid "Wi-Fi Networks"
+msgstr "Lìonraidhean WiFi"
+
+#: js/ui/status/network.js:881
+msgid "Select a network"
+msgstr "Tagh lìonra"
+
+#: js/ui/status/network.js:917
+msgid "No Networks"
+msgstr "Chan eil lìonra ann"
+
+#: js/ui/status/network.js:942 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "Cleachd suidse bathair-chruaidh gus a chur dheth"
+
+#: js/ui/status/network.js:1253
+msgid "Select Network"
+msgstr "Tagh lìonra"
+
+#: js/ui/status/network.js:1259
+msgid "Wi-Fi Settings"
+msgstr "Roghainnean WiFi"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1358
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "Tha hotspot %s gnìomhach"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1373
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "Chan eil %s ceangailte"
+
+#: js/ui/status/network.js:1473
+msgid "connecting…"
+msgstr "a’ dèanamh ceangal…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1476
+msgid "authentication required"
+msgstr "tha feum air dearbhadh"
+
+#: js/ui/status/network.js:1478
+msgid "connection failed"
+msgstr "dh’fhàillig leis a’ cheangal"
+
+#: js/ui/status/network.js:1524
+msgid "VPN Settings"
+msgstr "Roghainnean VPN"
+
+#: js/ui/status/network.js:1541
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1551
+msgid "VPN Off"
+msgstr "Tha VPN dheth"
+
+#: js/ui/status/network.js:1612 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Roghainnean an lìonraidh"
+
+#: js/ui/status/network.js:1640
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s cheangal uèirichte"
+msgstr[1] "%s cheangal uèirichte"
+msgstr[2] "%s ceanglaichean uèirichte"
+msgstr[3] "%s ceangal uèirichte"
+
+#: js/ui/status/network.js:1644
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s cheangal WiFi"
+msgstr[1] "%s cheangal WiFi"
+msgstr[2] "%s ceanglaichean WiFi"
+msgstr[3] "%s ceangal WiFi"
+
+#: js/ui/status/network.js:1648
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s cheangal mòdaim"
+msgstr[1] "%s cheangal mòdaim"
+msgstr[2] "%s ceanglaichean mòdaim"
+msgstr[3] "%s ceangal mòdaim"
+
+#: js/ui/status/network.js:1792
+msgid "Connection failed"
+msgstr "Dh’fhàillig leis a’ cheangal"
+
+#: js/ui/status/network.js:1793
+msgid "Activation of network connection failed"
+msgstr "Dh’fhàillig gnìomhachadh a’ cheangail ris an lìonra"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Tha an solas-oidhche à comas"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Tha an solas-oidhche air"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Lean air"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Cuir à comas gus a-màireach"
+
+#: js/ui/status/power.js:51 js/ui/status/powerProfiles.js:57
+msgid "Power Settings"
+msgstr "Roghainnean na cumhachd"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr "Air a làn-teàirrdseadh"
+
+#: js/ui/status/power.js:74
+msgid "Not Charging"
+msgstr ""
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:77 js/ui/status/power.js:83
+msgid "Estimating…"
+msgstr "’Ga thuairmeas…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d air fhàgail (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:97
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d gus am bi e làn (%d%%)"
+
+#. The icon label
+#: js/ui/status/power.js:145
+#, javascript-format
+msgid "%d %%"
+msgstr "%d%%"
+
+#: js/ui/status/powerProfiles.js:19
+msgctxt "Power profile"
+msgid "Performance"
+msgstr ""
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr ""
+
+#: js/ui/status/powerProfiles.js:21
+#, fuzzy
+#| msgid "Power Settings"
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Roghainnean na cumhachd"
+
+#: js/ui/status/remoteAccess.js:43
+msgid "Screen is Being Shared"
+msgstr "Tha an sgrìn ’ga cho-roinneadh"
+
+#: js/ui/status/remoteAccess.js:45
+msgid "Turn off"
+msgstr "Cuir dheth"
+
+#: js/ui/status/remoteAccess.js:149
+#, fuzzy
+#| msgctxt "search-result"
+#| msgid "Lock Screen"
+msgid "Stop Screencast"
+msgstr "Glais an sgrìn"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Tha am modh itealain air"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "Glais"
+
+#: js/ui/status/system.js:116
+#, fuzzy
+#| msgid "Power Off"
+msgid "Power Off / Log Out"
+msgstr "Cuir dheth"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "Cuir ’na dhàil"
+
+#: js/ui/status/system.js:130
+#, fuzzy
+#| msgid "Restarting…"
+msgid "Restart…"
+msgstr "’Ga ath-thòiseachadh…"
+
+#: js/ui/status/system.js:141
+#, fuzzy
+#| msgid "Power Off"
+msgid "Power Off…"
+msgstr "Cuir dheth"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Clàraich a-mach"
+
+#: js/ui/status/system.js:165
+#, fuzzy
+#| msgid "Switch User"
+msgid "Switch User…"
+msgstr "Leum gu cleachdaiche eile"
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Uidheam Thunderbolt nach aithne dhuinn"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Mhothaich sinn do dh’uidheam ùr fhad ’s an robh thu air falbh. Bris an "
+"ceangal is ceangail an uidheam a-rithist gus a chleachdadh."
+
+#: js/ui/status/thunderbolt.js:328
+#, fuzzy
+#| msgid "Unknown Thunderbolt device"
+msgid "Unauthorized Thunderbolt device"
+msgstr "Uidheam Thunderbolt nach aithne dhuinn"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Mearachd dearbhadh Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Cha b’ urrainn dhuinn an t-uidheam Thunderbolt ùghdarrachadh: %s"
+
+#: js/ui/status/volume.js:158
+msgid "Volume changed"
+msgstr "Dh’atharraich àirde na fuaime"
+
+#: js/ui/status/volume.js:220
+msgid "Volume"
+msgstr "Àirde na fuaime"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Sgàthanaich"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Cuir iad còmhla"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Taobh a-muigh a-mhàin"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "’Na bhroinn a-mhàin"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Thoir a’ ghlas far na h-uinneige"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Clàraich a-steach mar chleachdaiche eile"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr ""
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr ""
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr ""
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Tha “%s” deiseil"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+#, fuzzy
+#| msgid "Do you want to keep these display settings?"
+msgid "Keep these display settings?"
+msgstr "A bheil thu airson na roghainnean seallaidh seo a chumail?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Till na roghainnean"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Cùm na h-atharraichean"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] ""
+"Thèid na h-atharraichean air na roghainnean a thilleadh an ceann %d diog"
+msgstr[1] ""
+"Thèid na h-atharraichean air na roghainnean a thilleadh an ceann %d dhiog"
+msgstr[2] ""
+"Thèid na h-atharraichean air na roghainnean a thilleadh an ceann %d diogan"
+msgstr[3] ""
+"Thèid na h-atharraichean air na roghainnean a thilleadh an ceann %d diog"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:553
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/windowMenu.js:29
+#, fuzzy
+#| msgctxt "search-result"
+#| msgid "Lock Screen"
+msgid "Take Screenshot"
+msgstr "Glais an sgrìn"
+
+#: js/ui/windowMenu.js:41
+#, fuzzy
+#| msgid "Hide Text"
+msgid "Hide"
+msgstr "Falaich an teacsa"
+
+#: js/ui/windowMenu.js:48
+#, fuzzy
+#| msgctxt "button"
+#| msgid "Restart"
+msgid "Restore"
+msgstr "Ath-thòisich"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Làn-mheudaich"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Gluais"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Ath-mheudaich"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Gluais am bàr-tiotail air an sgrìn"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Air a’ bhàrr an-còmhnaidh"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Air rùm-obrach a tha ri fhaicinn an-còmhnaidh"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Gluais dhan rùm-obrach air an taobh chlì"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Gluais dhan rùm-obrach air an taobh deas"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Gluais dhan rùm-obrach aig a’ bhàrr"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Gluais dhan rùm-obrach aig a’ bhonn"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Gluais dhan mhonatair aig a’ bhàrr"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Gluais dhan mhonatair aig a’ bhonn"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Gluais dhan mhonatair air an taobh chlì"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Gluais dhan mhonatair air an taobh deas"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Dùin"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Mìosachan Evolution"
+
+#: src/main.c:419 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Seall an tionndadh"
+
+#: src/main.c:425
+msgid "Mode used by GDM for login screen"
+msgstr "Am modh a chleachdas GDM airson na sgrìn clàraidh a-steach"
+
+#: src/main.c:431
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Cleachd modh sònraichte airson na sgrìn clàraidh a-steach, can “gdm”"
+
+#: src/main.c:437
+msgid "List possible modes"
+msgstr "Seall na modhan a tha ann"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Chan eil fhios"
+
+#: src/shell-app.c:569
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Cha b’ urrainn dhuinn “%s” a chur gu dol"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Chan eil an dà fhacal-faire co-ionann."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Chan fhaod am facal-faire a bhith bàn"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Leig an cleachdaiche an dearbhadh seachad"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:209
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "Extensions"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:210
+#, fuzzy
+#| msgid "Configure GNOME Shell Extensions"
+msgid "Manage your GNOME Extensions"
+msgstr "Rèitich leudachain Slige GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Rèitich leudachain Slige GNOME"
+
+#: subprojects/extensions-app/js/main.js:131
+#: subprojects/extensions-app/js/main.js:142
+msgid "No Matches"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:170
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:171
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:175
+msgid "Remove"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:208
+msgid "translator-credits"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:336
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: subprojects/extensions-app/js/main.js:478
+msgid "The extension is incompatible with the current GNOME version"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:481
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "About Extensions"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+#, fuzzy
+#| msgid "Built-in Only"
+msgid "Built-In"
+msgstr "’Na bhroinn a-mhàin"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "No Installed Extensions"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+#, fuzzy
+#| msgid "Log Out"
+msgid "Log Out…"
+msgstr "Clàraich a-mach"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:458
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Create a new extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+#, fuzzy
+#| msgid "Unknown artist"
+msgid "Unknown arguments"
+msgstr "Chan eil fhios cò an neach-ciùil"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Disable an extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Enable an extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:85
+#, fuzzy
+#| msgid "No extensions installed"
+msgid "Show extensions info"
+msgstr "Cha deach leudachan a stàladh"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:184
+#, fuzzy
+#| msgid "UUIDs of extensions to enable"
+msgid "Install an extension bundle"
+msgstr "UUIDs nan leudachan ri an cur an comas"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+#, fuzzy
+#| msgid "No extensions installed"
+msgid "No extension bundle specified"
+msgstr "Cha deach leudachan a stàladh"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "Show user-installed extensions"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "Show system-installed extensions"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "Show enabled extensions"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Show disabled extensions"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+#, fuzzy
+#| msgid "GNOME Shell Extension Preferences"
+msgid "Show extensions with preferences"
+msgstr "Roghainnean airson leudachain na Slige GNOME"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+#, fuzzy
+#| msgid "No extensions installed"
+msgid "Show extensions with updates"
+msgstr "Cha deach leudachan a stàladh"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+#, fuzzy
+#| msgid "Print version"
+msgid "Print extension details"
+msgstr "Seall an tionndadh"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "List installed extensions"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+#, fuzzy
+#| msgid "GNOME Shell Extension Preferences"
+msgid "Opens extension preferences"
+msgstr "Roghainnean airson leudachain na Slige GNOME"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Reset an extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Cannot uninstall system extensions\n"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, fuzzy, c-format
+#| msgid "Failed to launch “%s”"
+msgid "Failed to uninstall “%s”\n"
+msgstr "Cha b’ urrainn dhuinn “%s” a chur gu dol"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Uninstall an extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:253
+#, fuzzy
+#| msgid "Print version"
+msgid "Version"
+msgstr "Seall an tionndadh"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:320
+#, fuzzy
+#| msgid "Print version"
+msgid "Print help"
+msgstr "Seall an tionndadh"
+
+#: subprojects/extensions-tool/src/main.c:322
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Enable extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/main.c:323
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Disable extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/main.c:324
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Reset extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/main.c:325
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "Uninstall extension"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/main.c:326
+#, fuzzy
+#| msgid "Disable user extensions"
+msgid "List extensions"
+msgstr "Cuir à comas leudachain a’ chleachdaiche"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+#, fuzzy
+#| msgid "No extensions installed"
+msgid "Show extension info"
+msgstr "Cha deach leudachan a stàladh"
+
+#: subprojects/extensions-tool/src/main.c:329
+#, fuzzy
+#| msgid "GNOME Shell Extension Preferences"
+msgid "Open extension preferences"
+msgstr "Roghainnean airson leudachain na Slige GNOME"
+
+#: subprojects/extensions-tool/src/main.c:330
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "Create extension"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-tool/src/main.c:331
+#, fuzzy
+#| msgid "Shell Extensions"
+msgid "Package extension"
+msgstr "Leudachain na slige"
+
+#: subprojects/extensions-tool/src/main.c:332
+#, fuzzy
+#| msgid "UUIDs of extensions to enable"
+msgid "Install extension bundle"
+msgstr "UUIDs nan leudachan ri an cur an comas"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr ""
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u às-chur"
+msgstr[1] "%u às-chur"
+msgstr[2] "%u às-chuir"
+msgstr[3] "%u às-chur"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ion-chur"
+msgstr[1] "%u ion-chur"
+msgstr[2] "%u ion-chuir"
+msgstr[3] "%u ion-chur"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Fuaimean an t-siostaim"
+
+#~ msgid "App Picker View"
+#~ msgstr "Sealladh roghnaichear nan aplacaid"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Inneacs an t-seallaidh làithrich ann an roghnaichear nan aplacaid."
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Nasgadh iuchrach a chuireas gach tween a tha ’ga ruith ’na stad ’s a "
+#~ "thòisicheas orra a-rithist a chum dì-bhugachaidh"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Am meur-chlàr ri chleachdadh"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "An seòrsa de mheur-chlàr a thèid a chleachdadh."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Thachair mearachd le luchdadh còmhradh nan roghainnean airson %s:"
+
+#~ msgid "Next"
+#~ msgstr "Air adhart"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Clàraich a-steach"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Nochdaidh aplacaidean a chleachdas tu gu tric an-seo"
+
+#~ msgid "Frequent"
+#~ msgstr "An fheadhainn thric"
+
+#~ msgid "All"
+#~ msgstr "Na h-uile"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Thoir air falbh o na h-annsachdan"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Cuir ris na h-annsachdan"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %d, %Y"
+#~ msgstr "%A, %d %B, %Y"
+
+#~ msgid "Password:"
+#~ msgstr "Facal-faire:"
+
+#~ msgid "Type again:"
+#~ msgstr "Am facal-faire a-rithist:"
+
+#~ msgid "Password: "
+#~ msgstr "Facal-faire: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Tha an lìonra uèirleas ag iarraidh dearbhadh"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Facal-faire an lìonraidh banna-leathainn mobile"
+
+#~ msgid "%s all day."
+#~ msgstr "%s fad an latha."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s ’s %s an uairsin."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s ’s %s an uairsin ’s %s an uairsin."
+
+#~ msgid "Feels like %s."
+#~ msgstr "A’ faireachdainn mar %s."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d teachdaireachd ùr"
+#~ msgstr[1] "%d theachdaireachd ùr"
+#~ msgstr[2] "%d teachdaireachdan ùra"
+#~ msgstr[3] "%d teachdaireachd ùr"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d bhrath ùr"
+#~ msgstr[1] "%d bhrath ùr"
+#~ msgstr[2] "%d brathan ùra"
+#~ msgstr[3] "%d brath ùr"
+
+#~ msgid "Off"
+#~ msgstr "Dheth"
+
+#~ msgid "On"
+#~ msgstr "Air"
+
+#~ msgid "Account Settings"
+#~ msgstr "Roghainnean a’ chunntais"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Glais na comhair"
+
+#~ msgid "Minimize"
+#~ msgstr "Fìor-lùghdaich"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Neo-làn-mheudaich"
+
+#~ msgctxt "search-result"
+#~ msgid "Power off"
+#~ msgstr "Cuir a’ chumhachd dheth"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Clàraich a-mach"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Dèan suids eadar cleachdaichean"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Ath-stiùireadh an dearbhaidh-lìn"
+
+#~ msgid "Events"
+#~ msgstr "Tachartasan"
+
+#~ msgid "Notifications"
+#~ msgstr "Brathan"
+
+#~ msgid "Hide tray"
+#~ msgstr "Falaich an treidhe"
+
+#~ msgid "Status Icons"
+#~ msgstr "Ìomhaigheagan staide"
+
+#~ msgid "Media"
+#~ msgstr "Meadhanan"
+
+#~ msgid "Not In Use"
+#~ msgstr "Chan eil e 'ga chleachdadh"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Cleachd e mar cheangal ris an eadar-lìon"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Slige GNOME (co-dhèanadair wayland)"
diff --git a/po/gl.po b/po/gl.po
new file mode 100644
index 0000000..5b682d4
--- /dev/null
+++ b/po/gl.po
@@ -0,0 +1,3306 @@
+# Galician translation for gnome-shell.
+# Copyright (C) 2012 Leandro Regueiro
+# This file is distributed under the same license as the gnome-shell package.
+# Proxecto Trasno - Adaptación do software libre á lingua galega: Se desexas
+# colaborar connosco, podes atopar máis información en http://www.trasno.net
+#
+#
+# Antón Méixome <meixome@certima.net>, 2009.
+# Leandro Regueiro <leandro.regueiro@gmail.com>, 2012.
+# Fran Diéguez <frandieguez@gnome.org>, 2009-2022.
+# Fran Dieguez <frandieguez@gnome.org>, 2012-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-14 22:12+0000\n"
+"PO-Revision-Date: 2022-09-15 18:48+0200\n"
+"Last-Translator: Fran Dieguez <frandieguez@gnome.org>\n"
+"Language-Team: Galician <proxecto@trasno.gal>\n"
+"Language: gl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.1\n"
+"X-Project-Style: gnome\n"
+"X-DL-Team: gl\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: Translating\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Iniciadores"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Activar aplicación favorita 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Activar aplicación favorita 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Activar aplicación favorita 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Activar aplicación favorita 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Activar aplicación favorita 4"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Activar aplicación favorita 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Activar aplicación favorita 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Activar aplicación favorita 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Activar aplicación favorita 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Capturas de pantalla"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Tomar unha captura de forma interactiva"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Tomar unha captura"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Tomar unha captura dunha xanela"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Gravar un vídeo da pantalla de forma interactiva"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostrar a lista de notificacións"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Enfocar a notificación activa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostrar a vista xeral"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostrar todas as aplicacións"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Abrir o menú de aplicación"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Xestor de xanelas e inicio de aplicacións"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Activar as ferramentas internas útiles para os desenvolvedores e probadores "
+"usando Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permite o acceso ás ferramentas de depuración e monitorización internas "
+"usando o diálogo Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs das extensións a activar"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"As extensións do GNOME Shell teñen unha propiedade UUID; esta chave lista as "
+"extensións que deberían cargarse. Calquera extensión que queira ser cargada "
+"debe estar nesta lista. Pode modificar esta lista cos métodos "
+"EnableExtension e DisableExtension de D-Bus en org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs das extensións para forzar a súa desactivación"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"As extensións do GNOME Shell teñen unha propiedade UUID; esta chave lista as "
+"extensións que deberían cargarse, incluso se se cargan como parte do modo "
+"actual. Tamén pode manipular esta lista cos métodos de D-Bus EnableExtension "
+"e DisableExtension en org.gnome.Shell. Esta chave toma precedencia sobre a "
+"configuración «enabled-extensions»."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Desactivar as extensións do usuario"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Desactiva todas as extensións que o usuario activou sen cambiar á "
+"preferencia «enabled-extensions»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Desactiva a validación da compatibilidade de versión das extensións"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell só cargará extensións que reclamen compatibilidade coa versión "
+"executándose actualmente. Ao activar esta opción desactivarase esta "
+"comprobación e probarase a cagar todas as extensións sen importar que "
+"versión reclaman."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lista de ID de ficheiros de escritorio para as aplicacións preferidas"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"As aplicacións que corresponden a estes identificadores mostraranse na área "
+"de preferidos."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historial do diálogo de ordes (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historial do diálogo de «looking glass»"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Mostrar sempre o elemento de menú «Saír da sesión» no menú de usuario."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Esta chave sobrescribe a ocultación automática do elemento de menú «Saír da "
+"sesión» en situacións con un único usuario ou única sesión."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Indica se se debe lembrar o contrasinal para montar os sistemas de ficheiros "
+"cifrados ou remotos"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"O shell non solicitará o contrasinal cando un dispositivo cifrado ou un "
+"sistema de ficheiros remoto se monta. Se o contrasinal se pode gardar para o "
+"futuro aparecerá a opción «Lembrar contrasinal» Esta chave estabelece o "
+"estado por omisión da opción."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Indica se o adaptador de Bluetooth por omisión ten configurados dispositivos "
+"asociados a el"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"O shell só mostrará o elemento de menú de Bluetooth se hai un adaptador de "
+"Bluetooth enchufado ou se houbera dispositivos asociados co adaptador "
+"principal. Isto restabelecerase se o adaptador por omisión non se ve ou non "
+"ten dispositivos asociados a el."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "O último perfil de enerxía seleccionado non predeterminado"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Algúns sistemas admiten máis dun perfil de enerxía. Para continuar a "
+"compatibilidade de trocar entre os dous perfiles esta chave rexistra o "
+"último perfil non predeterminado seleccionado."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "A última versión do diálogo de «Benvida a GNOME» mostrouse para"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Esta chave determina para que versión do diálogo de «Benvida de GNOME» se "
+"mostrou por última vez. Unha cadea baleira representa unha versión antiga "
+"posíbel, e un número moi grande significa versións que non existen aínda. "
+"Este número enorme pode usarse para desactivar efectivamente o diálogo."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Disposición do selector de aplicacións"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Disposición do selector de aplicacións. Cada entrada no array é unha páxina. "
+"As páxinas están almacenadas na orde na que aparecen no Shell de GNOME. Cada "
+"páxina contén un par «id de aplicación» → «datos». Actualmente, gárdanse os "
+"seguintes valores: • «position»: A posición da icona da aplicación na páxina"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Combinación de teclas para abrir o menú de aplicación"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Combinación de teclas para abrir o menú de aplicación."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Combinación de teclas para cambiar entre os estados da vista"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Combinación de teclas para cambiar entre a sesión, o selector de xanelas e a "
+"grella de aplicacións"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Combinación de teclas para cambiar entre a grella de aplicacións, o selector "
+"de xanelas e a sesión"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Combinación de teclas para abrir a vista «Mostrar aplicacións»"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Combinación de teclas para abrir a vista \"Mostrar aplicacións\" da Vista "
+"xera de Actividades."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Combinación de teclas para abrir a Vista xeral"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Combinación de teclas para abrir a Vista de actividades."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Combinación de teclas para trocar a visibilidade da área de notificacións"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Combinación de teclas para trocar a visibilidade da lista de notificacións."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Combinación de teclas para enfocar a notificación activa"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Combinación de teclas para enfocar a notificación activa."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Trocar á aplicación 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Trocar á aplicación 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Trocar á aplicación 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Trocar á aplicación 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Trocar á aplicación 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Trocar á aplicación 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Trocar á aplicación 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Trocar á aplicación 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Trocar á aplicación 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Restrinxir o trocador ao espazo de traballo actual."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Se é verdadeiro, só se mostran no trocador as aplicacións que teñen xanelas "
+"no espazo de traballo actual. No outro caso, incluiranse todas as "
+"aplicacións."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "O modo da icona da aplicación."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configura como se mostran as xanelas no intercambiador. As opcións posíbeis "
+"son «thumbnail-only» (mostra unha miniatura da xanela, «app-icon-only» (só "
+"mostra a icona da aplicación) ou «both» (móstranse ambas cosas)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Se é verdadeiro, só se mostrarán no trocador as xanelas do espazo de "
+"traballo actual. Doutra maneira inclúense todos as aplicacións."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Localizacións"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "As localizacións a mostrar nos reloxos do mundo"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Localización automática"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Indica se obter a localización actual ou non"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Localización"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "A localización para cal obter a predición meteorolóxica"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Anexar o diálogo modal á xanela pai"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Esta chave sobrescribe a chave en org.gnome.mutter cando executa GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Activar o mosaico nos bordos ao arrastrar xanelas aos bordos da xanela"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Os espazos de traballo xestiónanse dinamicamente"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Espazos de traballo só no monitor primario"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Atrasar os cambios de foco no modo rato até que o punteiro se pare"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Inicio de sesión de rede"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Algo foi mal"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Sentímolo, pero houbo un problema: a configuración para esta extensión non "
+"pode mostrarse. Recomendámoslle que informe do problema aos autores da "
+"extensión."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detalles técnicos"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Sitio web"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visitar o sitio web da extensión"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Contrasinal"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Escolla unha sesión"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Non está na lista?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(p.ex., usuario ou %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nome de usuario"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Xanela de inicio de sesión"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Erro de autenticación"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(ou pase o dedo sobre o lector)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(ou pouse o dedo sobre o lector)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Apagar"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "apagar;reiniciar;deter;parar"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reiniciar;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bloquear a pantalla"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "bloquear pantalla;bloquear"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Saír da sesión"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "pechar a sesión;desloguearse;saír da sesión"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspender"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspender;durmir"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Cambiar de usuario"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "cambiar de usuario"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "bloquear orientación;desbloquear orientación;pantalla;rotación"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Tomar unha captura"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "captura;gravación de pantalla;recorte;capturar;gravar"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Desbloquear a rotación de pantalla"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Bloquear a rotación de pantalla"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Orde non atopada"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Non foi posíbel analizar a orde:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Produciuse un fallo na execución de «%s»:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Xusto agora"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "hai %d minuto"
+msgstr[1] "hai %d minutos"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "hai %d hora"
+msgstr[1] "hai %d horas"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Onte"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "hai %d día"
+msgstr[1] "hai %d días"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "hai %d semana"
+msgstr[1] "hai %d semanas"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "hai %d mes"
+msgstr[1] "hai %d meses"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "hai %d ano"
+msgstr[1] "hai %d anos"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Onte, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A ás %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d de %B ás %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d de %B de %Y ás %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Onte, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d de %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d de %B de %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Inicio de sesión do Hotspot"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"A súa conexión a este inicio de sesión de hotspot non é segura. Os "
+"contrasinais e outra información que escriba nesta páxina pode ser vista "
+"porque persoas próximas."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Denegar acceso"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Conceder acceso"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Cartafol sen nome"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s fixouse no taboleiro."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s desfixouse do taboleiro."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Abrir Xanelas"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Xanela nova"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Mostrar detalles"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Saír"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Desfixar"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Fixar no taboleiro"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Iniciar usando a Tarxeta Gráfica Integrada"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Iniciar usando a Tarxeta Gráfica Dedicada"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Seleccionar dispositivo de son"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Preferencias de son"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Cascos auriculares"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Auriculares"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Micrófono"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Cambiar fondo de escritorio…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Preferencias da pantalla"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Preferencias"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "X"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%B de %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Anterior mes"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Seguinte mes"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Semana %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Non hai notificacións"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Non molestar"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Limpar"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» non está respondendo."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Debe escoller se quere agardar que continúe ou forzar que a aplicación peche "
+"por completo."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Forzar peche"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Agardar"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Unidade externa conectada"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Unidade externa desconectada"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Non foi posíbel bloquear o volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "A versión de udisks instalada non admite a configuración PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Abrir con %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"De forma alternativa pode conectarse premendo o botón «WPS» do seu enrutador."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Conectar"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Chave"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Contrasinal da chave privada"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identidade"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Servizo"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Requírese autenticación"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Requírense contrasinais ou chaves de cifrado para acceder á rede sen fíos "
+"«%s»."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autenticación con fíos 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nome da rede"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autenticación DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Requírese un código PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "É necesario un código PIN para o dispositivo de banda larga móbil"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Requírese un contrasinal para conectarse a «%s»."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Xestor da rede"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Contrasinal da VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Requírese autenticación"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrador"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autenticar"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Desculpe, iso non funcionou. Ténteo de novo."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Agora %s chámase %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Xanelas"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Mostrar aplicacións"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Taboleiro"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%e de %B de %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e de %B de %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%e de %B de %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%e de %B de %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Hoxe"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Mañá"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Todo o día"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Non hai eventos"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Engadir reloxos do mundo…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Reloxos do mundo"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Cargando…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Conectarse para var a información do tempo"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "A información do tempo non está dispoñíbel actualmente"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Tempo"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Seleccione unha localización meteorolóxica…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Saír da sesión de %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Saír da sesión"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Vaise pechar a sesión de %s en %d segundo."
+msgstr[1] "Vaise pechar a sesión de %s en %d segundos."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "A súa sesión pecharase automaticamente en %d segundo."
+msgstr[1] "A súa sesión pecharase automaticamente en %d segundos."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Saír da sesión"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Apagar"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalar actualizacións e apagar"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "O sistema apagarase automaticamente en %d segundo."
+msgstr[1] "O sistema apagarase automaticamente en %d segundos."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalar actualizacións de software pendentes"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Apagar"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalar actualizacións e reiniciar"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "O sistema reiniciarase automaticamente en %d segundo."
+msgstr[1] "O sistema reiniciarase automaticamente en %d segundos."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reiniciar e instalar actualizacións"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"O sistema reiniciarase automaticamente e instalará as actualizacións en %d "
+"segundo."
+msgstr[1] ""
+"O sistema reiniciarase automaticamente e instalará as actualizacións en %d "
+"segundos."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reiniciar e instalar"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalar e apagar"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Apagar despois de instalar as actualizacións"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reiniciar e instalar anovación"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s instalarase despois do reinicio. A instalación de anovacións pode "
+"levar algún tempo: asegúrese que ten unha copia de respaldo dos seus datos e "
+"que o seu computador está enchufado."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Correndo con batería baixa: por favor conecte a corrente antes de instalar "
+"actualizacións."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Algunhas aplicacións están ocupadas ou teñen traballo sen gardar"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Hai outros usuarios conectados"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opcións de arrinque"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remoto)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instalar"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalar extensión"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Desexa descargar e instalar «%s» desde extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Hai actualizacións de extensións dispoñíbeis"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "As actualizacións das extensións están listas para instalarse."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Permitir atallos de inhibición"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "A aplicación %s quere inhibir os atallos de teclado"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "A aplicación quere inhibir os atallos de teclado"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Pode restaurar os atallos de teclado premendo %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Denegar"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Permitir"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Teclas lentas activadas"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Teclas lentas desactivadas"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Acabas de manter a tecla Maiús durante 8 segundos. Isto é un atallo para a "
+"característica de Teclas lentas, o cal afecta á forma na que funciona o "
+"teclado."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Teclas persistentes activadas"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Teclas persistentes desactivadas"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Acabas de manter a tecla Maiús durante 5 segundos. Isto é un atallo para a "
+"característica de Teclas persistentes, o cal afecta á forma na que funciona "
+"o teclado."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Acabas de premer dúas teclas á vez, ou premendo a tecla Maiús 5 veces "
+"seguidas. Isto é un atallo para a característica de Teclas persistentes, o "
+"cal afecta á forma na que funciona o teclado."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Deixar activado"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Activar"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Apagar"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Deixar desactivado"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Preferencias de rexión e idioma"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Non hai ningunha extensión instalada"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s non emitiu ningún erro."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Ocultar erros"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Mostrar erros"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Activado"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Desactivado"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Erro"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Obsoleto"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Descargando"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Ver fonte"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Páxina web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "O sistema púxose en modo non seguro"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "As aplicacións agora teñen acceso sen restricións"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Desfacer"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Sesión iniciada como un usuario privilexiado"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Por razóns de seguranza debe evitar executar unha sesión como un usuario "
+"privilexiado. Se é posíbel, debería iniciar sesión como un usuario normal."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Bloqueo de pantalla desactivado"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "O bloqueo de pantalla require o xestor de pantalla de GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Información do sistema"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artista descoñecido"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Título descoñecido"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Escriba para buscar"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Aplicacións"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Vista xeral"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Novo atallo…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplicación definida"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Mostrar a axuda en pantalla"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Cambiar monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Asignar combinación de teclas"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Feito"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Editar…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Ningún"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Prema un botón para continuar"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Prema Esc para saír"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Prema calquera tecla para saír"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Actividades"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Executar unha orde"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Prema ESC para saír"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Reiniciar non está dispoñíbel en Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Reiniciando…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME precisa bloquear a pantalla"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Non foi posíbel bloquear"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Unha aplicación impediu o bloqueo"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Selección"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Seleccionar área"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Pantalla"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Seleccionar pantalla"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Xanela"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Seleccionar xanela"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Captura de pantalla / Captura de vídeo de pantalla"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Mostrar punteiro"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Capturas en vídeo da pantalla"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Captura en vídeo da pantalla"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Captura de pantalla"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Captura en vídeo da pantalla realizada"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Prema aquí para ver o vídeo."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Mostrar en Ficheiros"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Captura de pantalla de %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Captura de pantalla realizada"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Pode pegar a imaxe desde o portapapeis."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Captura de pantalla realizada"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Buscando…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Sen resultados."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d máis"
+msgstr[1] "%d máis"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Buscar"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copiar"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Pegar"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Mostrar texto"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ocultar texto"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "O bloqueo de maiúsculas está activado."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volume agochado"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volume de sistema de Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Usa ficheiros de chaves"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Para desbloquear un volume que usa ficheiros de chave, use no lugar a "
+"utilidade <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Número PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Lembrar contrasinal"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desbloquear"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Abrir %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "O PIM debe ser un número ou baleiro."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Non foi posíbel iniciar %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Non se puido atopar a aplicación %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accesibilidade"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Contraste alto"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Ampliación"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Lector da pantalla"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Teclado en pantalla"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Alertas visuais"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Teclas persistentes"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Teclas lentas"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Rexeite de teclas"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Teclas do rato"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Texto grande"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Autorotar"
+
+#: js/ui/status/bluetooth.js:171
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Brillo"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Modo escuro"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clic único"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Clic dobre"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Arrastrar"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clic secundario"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clic atrasado"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Teclado"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Mostrar a distribución do teclado"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Permitir acceso á localización"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "A aplicación %s solicita acceso á súa localización"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Pode cambiar o acceso á localización en calquera momento desde as "
+"preferencias de privacidade."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<descoñecido>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Desconectarse de %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Conectarse a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s punto wifi"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Luz nocturna"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Rendemento"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Balanceado"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Aforro de enerxía"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Perfiles de enerxía"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Deter captura de vídeo"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Deter compartición de pantalla"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Modo avión"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Tomar unha captura"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menú apagar"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspender"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Reiniciar…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Apagar…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Saír da sesión…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Cambiar de usuario…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Bloquear pantalla"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Dispositivo Thunderbolt descoñecido"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Detectouse un novo dispositivo mentres non estabas. Por favor desconecte e "
+"reconecte o dispositivo para comezar a usalo."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispositivo Thunderbolt non autorizado"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Detectouse un novo dispositivo e precisa ser autorizado por un administrador."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Erro de autorización de Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Non foi posíbel autorizar o dispositivo Thunderbolt: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Volume cambiado"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Volume"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Saída de son"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Entrada de son"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Espello"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Unir pantallas"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Só a externa"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Só a interna"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %d de %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Deslizar para desbloquear"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Prema o rato ou unha tecla para desbloquear"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Desbloquear xanela"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Iniciar sesión como outro usuario"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Benvida a GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Se desexa votar unha ollada, realice o percorrido."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Non grazas"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Facer o percorrido"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» está preparado"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Desexa manter estas preferencias de pantalla?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Reverter preferencias"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Manter cambios"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Os cambios das preferencias revertiranse en %d segundo"
+msgstr[1] "Os cambios das preferencias revertiranse en %d segundos"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Ocultar"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restaurar"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximizar"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Mover"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Mover a barra de título na pantalla"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Sempre enriba"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Sempre no espazo de traballo visible"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Mover ao espazo da esquerda"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Mover ao espazo da dereita"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Mover ao espazo de arriba"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Mover ao espazo de abaixo"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Mover ao monitor de arriba"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Mover ao monitor de abaixo"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Mover ao monitor da esquerda"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Mover ao monitor da dereita"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Pechar"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendario de Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Imprimir versión"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Modo usado por GDM para a pantalla de inicio"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Usar un modo específico, por exemplo, «gdm» para a pantalla de inicio de "
+"sesión"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Listar os modos posíbeis"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Descoñecido"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Produciuse un erro ao iniciar «%s»"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Os contrasinais non coinciden."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "O contrasinal non pode estar baleiro"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "O usuario rexeitou o diálogo de autenticación"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensións"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Xestione as súas extensións de GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "O Proxecto GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Extensións de GNOME xestiona a actualización de extensions, a súa "
+"configuración, eliminación e a desactivación das extensións que non quere."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configure as extensións de GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Non hai coincidencias"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Quitar «%s»?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr "Se quita a extensión debe descargala se quere activala de novo"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Quitar"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Fran Diéguez <frandieguez@gnome.org>, 2012-2022"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Vaise actualizar %d extensión no seguinte inicio de sesión."
+msgstr[1] "Vanse actualizar %d extensións no seguinte inicio de sesión."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "A extensión non é compatíbel coa versión actual de GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "A extensión tivo un erro"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "A extensións pode actualizarse"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Sitio web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Quitar…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Axuda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Sobre Extensións"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"As extensións poden causar problemas de rendemento e estabilidade. Desactive "
+"as extensións se atopa problemas no seu sistema."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Instaladas manualmente"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Para atopar e engadir extensións visite <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Internas"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Non hai extensións instaladas"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Desculpe, non foi posíbel obter a lista das extensións instaladas. Asegúrese "
+"que está nunha sesión en GNOME e ténteo de novo."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Actualizacións de extensión dispoñíbeis"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Creouse con éxito a nova extensión en %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"O nome debe ser unha cadea moi curta (idealmente descritiva).\n"
+"Exemplos son: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nome"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"A descrición debe ser unha explicación dunha única frase do que fai a "
+"extensión.\n"
+"Exemplos son: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descrición"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID é un identificador único global para a súa extensión.\n"
+"Isto debería estar no formato de enderezo de correo (clicktofocus@pepiño."
+"souto.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Seleccione entre unha dos modelos:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Modelo"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "O identificador único da nova extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "O nome visíbel polo usuario da nova extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRICIÓN"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Unha descrición curta do que a súa extensión fai"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODELO"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "O modelo a usar para a nova extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Escriba a información da extensión de forma interactiva"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Crear unha nova extensión"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argumentos descoñecidos"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Requírese o UUID, o nome e a descrición"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Produciuse un fallo ao conectarse á Shell de GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "A extensión «%s» non existe\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Desactivar unha extensión"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Non se forneceu un UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Forneceuse máis dun UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Activar unha extensión"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "A extensión «%s» non existe\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostrar a información das extensións"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Sobrescribir unha extensión existente"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instala un paquete de extensión"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Non se especificou ningún paquete"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Especificouse máis dun paquete de extensión"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Mostra as extensións instaladas polo usuario"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostra as extensións instaladas no sistema"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostra as extensións activadas"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostra as extensións desactivadas"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostra as extensións con preferencias"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostra as extensións con actualizacións"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Imprime os detalles da extensión"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Mostra as extensións instaladas"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FICHEIRO"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Orixe adicional a incluír no paquete"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ESQUEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Un esquema de GSettings que debería incluírse"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "O directorio onde se atopan as traducións"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMINIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "O dominio de gettext a usar nas traducións"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Sobrescribir un pack existente"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "O directorio onde debería crearse o paquete"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIRECTORIO_ORIXE"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Crear un paquete de extensión"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Especificouse máis dun directorio de orixe"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "A extensión «%s» non ten preferencias\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr ""
+"Produciuse un erro ao abrir as preferencias para a extensión «%s»: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Abre as preferencias da extensión"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Restabelece unha extensión"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Non é posíbel desinstalar as extensións do sistema\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Produciuse un erro ao desinstalar «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Desinstala unha extensión"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Non imprimir os mensaxes de erro"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Ruta"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autor orixinal"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versión"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Estado"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» non ten argumentos"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Uso:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Imprimir a información da versión e saír."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "ORDE"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Ordes:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Imprime a axuda"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Activar extensión"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Desactivar extensión"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Restabelecer extensión"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Desinstalar extensión"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Listar as extensións"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Mostrar a información da extensión"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Abrir as preferencias da extensión"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Crear extensión"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Empaquetar extensión"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instalar paquete de extensión"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Use «%s» obter axuda detallada.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Plana"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Unha extensión baleira"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicador"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Engade unha icona á barra superior"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u saída"
+msgstr[1] "%u saídas"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entradas"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sons do sistema"
+
+#~ msgid "Log Out"
+#~ msgstr "Saír da sesión"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Preferencias do Bluetooth"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d conectado"
+#~ msgstr[1] "%d conectados"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth activado"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth desactivado"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Localización activada"
+
+#~ msgid "Disable"
+#~ msgstr "Desactivar"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Preferencias da privacidade"
+
+#~ msgid "Location In Use"
+#~ msgstr "Localización en uso"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Localización desactivada"
+
+#~ msgid "Enable"
+#~ msgstr "Activar"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s apagado"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s conectado"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s sen xestionar"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s conectando"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s require autenticación"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Falta o firmware para %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s non dispoñíbel"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s conexión fallada"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Preferencias da rede con fíos"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Preferencias da banda larga móbil"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s hardware desactivado"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s desactivado"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Conectarse a internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Modo avión activado"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "A Wifi está desactivada cando o modo avión está activo."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Desactivar o modo avión"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wifi desactivada"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "A Wifi precisa activarse para poder conectarse á rede."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Activar Wifi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Redes Wifi"
+
+#~ msgid "Select a network"
+#~ msgstr "Seleccione unha rede"
+
+#~ msgid "No Networks"
+#~ msgstr "Sen redes"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Usar interruptor hardware para apagar"
+
+#~ msgid "Select Network"
+#~ msgstr "Seleccione unha rede"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Preferencias da Wifi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s non conectado"
+
+#~| msgid "<unknown>"
+#~ msgid "unknown"
+#~ msgstr "descoñecido"
+
+#~| msgid "Estimating…"
+#~ msgid "activating…"
+#~ msgstr "activando…"
+
+#~| msgid "Estimating…"
+#~ msgid "deactivating…"
+#~ msgstr "desactivando…"
+
+#~ msgid "deactivated"
+#~ msgstr "desactivado"
+
+#~ msgid "connecting…"
+#~ msgstr "conectando…"
+
+#~ msgid "authentication required"
+#~ msgstr "requírese autenticación"
+
+#~ msgid "connection failed"
+#~ msgstr "conexión fallada"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Preferencias de VPN"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN desactivada"
+
+#~ msgid "Network Settings"
+#~ msgstr "Preferencias da rede"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s conectando con fíos"
+#~ msgstr[1] "%s conectando con fíos"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s conectando sen fíos"
+#~ msgstr[1] "%s con fíos conectando"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s con módem conectando"
+#~ msgstr[1] "%s conectando sen fíos"
+
+#~ msgid "Connection failed"
+#~ msgstr "Produciuse un fallo na conexión"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Produciuse un fallo na activación da conexión de rede"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Luz nocturna desactivada"
+
+#~ msgid "Resume"
+#~ msgstr "Reiniciar"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Desactivar ate mañá"
+
+#~ msgid "Power Settings"
+#~ msgstr "Preferencias de enerxía"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Carga completa"
+
+#~ msgid "Not Charging"
+#~ msgstr "Non cargándose"
+
+#~ msgid "Estimating…"
+#~ msgstr "Estimando…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d Faltan (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d ate cargado (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "A pantalla está compartíndose"
+
+#~ msgid "Turn off"
+#~ msgstr "Desactivar"
+
+#~ msgid "Lock"
+#~ msgstr "Bloquear"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Apagar / Saír da sesión"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "Mostrar UI de captura de pantalla"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Gravación de pantalla en curso"
+
+#~ msgid "Stop"
+#~ msgstr "Deter"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Aviso"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "As extensións poden causar problemas no sistema, o que inclúe problemas "
+#~ "de rendemento. Se atopa problemas no seu sistema recomendámoslle "
+#~ "desactivar todas as extensións."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Retirar dos marcadores"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Engadir aos favoritos"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Activar o API de introspección"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Activar o API de D-Bus que permite introspeccionar o estado da aplicación "
+#~ "do shell."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Produciuse un fallo ao conectarse ao Shell de GNOME"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizar"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Restaurar"
+
+#~ msgid "App Picker View"
+#~ msgstr "Visualización da selección de aplicacións"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Índice da vista seleccionada actualmente no selector de aplicación."
+
+#~ msgid "Copy Error"
+#~ msgstr "Copiar erro"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "As aplicacións usadas recentemente aparecerán aquí"
+
+#~ msgid "Frequent"
+#~ msgstr "Frecuentes"
+
+#~ msgid "All"
+#~ msgstr "Todos"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %d de %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %d de %B de %Y"
+
+#~ msgid "Off"
+#~ msgstr "Desactivado"
+
+#~ msgid "On"
+#~ msgstr "Activado"
+
+#~ msgid "Username…"
+#~ msgstr "Nome de usuario…"
+
+#~ msgid "Password: "
+#~ msgstr "Contrasinal: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Escriba o contrasinal…"
+
+#~ msgid "Next"
+#~ msgstr "Seguinte"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Iniciar sesión"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d de %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d mensaxe nova"
+#~ msgstr[1] "%d mensaxes novas"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d notificación nova"
+#~ msgstr[1] "%d notificacións novas"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Explorar en Software"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Bloquear orientación"
+
+#~ msgid "Rename"
+#~ msgstr "Renomear"
+
+#~ msgid "Type again:"
+#~ msgstr "Escriba de novo:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "A rede sen fíos require autenticación"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Contrasinal da rede de banda larga móbil"
diff --git a/po/gu.po b/po/gu.po
new file mode 100644
index 0000000..ad80384
--- /dev/null
+++ b/po/gu.po
@@ -0,0 +1,2375 @@
+# translation of gu.po to Gujarati
+# Gujarati translation for gnome-shell.
+# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Sweta Kothari <swkothar@redhat.com>, 2011, 2012, 2013, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: gu\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug."
+"cgi?product=gnome-shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-10-01 07:39+0000\n"
+"PO-Revision-Date: 2014-10-01 15:51+0530\n"
+"Last-Translator: \n"
+"Language-Team: American English <kde-i18n-doc@kde.org>\n"
+"Language: gu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Generator: Lokalize 1.0\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "સિસ્ટમ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "સંદેશો ટ્રેને બતાવો"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "સક્રિય સૂચના પર પ્રકાશ નાંખો"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "ઝાંખી બતાવો"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "બધા કાર્યક્રમો બતાવો"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "કાર્યક્રમ મેનુને ખોલો"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME શેલ"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "વિન્ડો સંચાલન અને કાર્યક્રમ શરૂઆત"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME Shell ઍક્સટેન્શન પસંદગીઓ"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Shell ઍક્સટેન્શનને રૂપરેખાંકિત કરો"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME Shell (વેલૅન્ડ કંપોઝીટર)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Alt-F2 માંથી ડેવલપર અને ટેસ્ટર માટે ઉપયોગી આંતરિક સાધનોને સક્રિય કરો"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "Alt-F2 સંવાદની મદદથી આંતરિક ડિબગીંગ અને મોનિટરીંગ સાધનોને વાપરવા પરવાનગી આપે છે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "સક્રિય કરવા માટે એક્સટેન્શનનું UUIDs"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+#| msgid ""
+#| "GNOME Shell extensions have a uuid property; this key lists extensions "
+#| "which should be loaded. Any extension that wants to be loaded needs to be "
+#| "in this list. You can also manipulate this list with the EnableExtension "
+#| "and DisableExtension DBus methods on org.gnome.Shell."
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell ઍક્સટેન્શન પાસે UUID ગુણધર્મ છે; આ કી ઍક્સટેન્શનની યાદી કરે છે કે જેને લાવેલ હોવુ "
+"જોઇએ. કોઇપણ ઍક્સટેન્શન કે જેને આ યાદીમાં લાવવાની જરૂર છે. તમે પણ org.gnome.Shell પર "
+"EnableExtension અને DisableExtension DBus પદ્દતિઓ સાથે આ યાદીને કુશળતાપૂર્વક વાપરી "
+"શકો છો."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "ઍક્સટેન્શન આવૃત્તિ સુસંગતતાની માન્યતાને નિષ્ક્રિય કરે છે"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell એ ફક્ત ઍક્સટેન્શનને લાવશે કે જે હાલની ચાલતી આવૃત્તિને આધાર આપવા માટે દાવો કરે છે. "
+"આ વિકલ્પને સક્રિય કરવાનું આ ચકાસણીને નિષ્ક્રિય કરશે અને આવૃત્તિને બાદ કરતા બધા ઍક્સટેન્શનને "
+"લાવવાનો પ્રયત્ન કરે છે જે તેઓ આધાર આપવા માટે દાવો કરે છે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "પસંદીદા કાર્યક્રમો માટે ડેસ્કટોપ ફાઇલ IDs ની યાદી કરો"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "કાર્યક્રમો આ ઓળખકર્તાઓ સાથે સંકળાયેલ છે તે પસંદીદા વિસ્તારમાં દેખાશે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "કાર્યક્રમ પસંદકર્તા દૃશ્ય"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "કાર્યક્રમ પસંદકર્તામાં હાલમાં પસંદ થયેલ દૃશ્યની અનુક્રમણિકા."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "આદેશ (Alt-F2) સંવાદ માટે ઇતિહાસ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "ગ્લાસ સંવાદને જોવા માટે ઇતિહાસ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "વપરાશકર્તા મેનુમાં હંમેશા 'બહાર નીકળો' મેનુ વસ્તુને બતાવો."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+#| msgid ""
+#| "This key overrides the automatic hiding of the 'Log out' menuitem in "
+#| "single-user, single-session situations."
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"આ કી એકજ વપરાશકર્તા, એકજ-સત્ર પરિસ્થિતિઓમાં 'બહાર નીકળો' મેનુવસ્તુને આપમેળે છુપાવવા આ કીને "
+"ઓવરરાઇડ કરવામાં "
+"આવી છે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid "Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "એનક્રિપ્ટ થયેલ અથવા દૂરસ્થ ફાઇલ સિસ્ટમોને માઉન્ટ કરવા માટે ક્યાંતો પાસવર્ડ યાદ રાખો"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"શેલ પાસવર્ડને સૂચિત કરશે જ્યારે એનક્રિપ્ટ થયેલ ઉપકરણ અથવા દૂસ્થ ફાઇલસિસ્ટમ માઉન્ટ થયેલ હોય. "
+"જો પાસવર્ડ ભવિષ્યનાં વપરાશ માટે સંગ્રહી શકાય છે 'પાસવર્ડ યાદ રાખો' ચેકબોક્સ હાજર રહેશે. "
+"આ કી ચેકબોક્સની મૂળભૂત સ્થિતિને સુયોજિત કરે છે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "કૅલેન્ડરમાં અઠવાડિયા તારીખને બતાવો"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "જો true હોય તો, કૅલેન્ડરમાં ISO અઠવાડિયાની તારીખને દર્શાવો."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "કાર્યક્રમ મેનુને ખોલવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "કાર્યક્રમ મેનુને ખોલવા માટે કિબાઇન્ડીંગ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"કાર્યક્રમો બતાવો\" દૃશ્યને ખોલવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr "પ્રવૃત્તિ ઝાંખીનાં \"કાર્યક્રમો બતાવો\" દૃશ્યને ખોલવા માટે કિબાઇન્ડીંગ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the overview"
+msgstr "ઝાંખીને ખોલવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the Activities Overview."
+msgstr "પ્રવૃત્તિ ઝાંખીને ખોલવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "કાર્યક્રમ મેનુને ખોલવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "કાર્યક્રમ મેનુને ખોલવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "સક્રિય સૂચના પર પ્રકાશ નાંખવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "સક્રિય સૂચના પર પ્રકાશ નાંખવા માટે કિબાઇન્ડીંગ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid "Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr "કીબાઇન્ડીંગ કે જે બધી ચાલતી ટ્વીનને અટકાવે છે અને પાછુ લાવે છે, ડિબગીંગ હેતુઓ માટે"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "ક્યુ કિબોર્ડ વાપરવુ છે"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "વાપરવા માટે કિબોર્ડનો પ્રકાર."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "વર્તમાન કામ કરવાની જગ્યા માટે સ્વિચર મર્યાદિત કરી છે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"જો true હોય તો, ફક્ત કાર્યક્રમો કે જેની પાસે હાલની કાર્ય કરવાની જગ્યા પર વિન્ડો છે તે "
+"સ્વીચરમાં બતાવેલ છે. નહિંતો, બધા કાર્યક્રમનો સમાવેલ છે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "કાર્યક્રમ સંકેત સ્થિતિ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"રૂપરેખાંકિત કરે છે કે સ્વીચરમાં કેવી રીતે વિન્ડો બતાવેલ છે. યોગ્ય શક્યતાઓ 'થમ્ભનેઇલ-"
+"ફક્ત' (વિન્ડોનાં થમ્ભનેઇલને બતાવે છે), 'કાર્યક્રમ-ચિહ્ન-ફક્ત' (ફક્ત કાર્યક્રમ ચિહ્ન બતાવે છે) "
+"અથવા 'બંને' છે. "
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"જો true હોય તો, હાલની કાર્ય કરવાની જગ્યામાંથી વિન્ડો સ્વીચરમાં બતાવેલ છે. નહિં તો, બધી "
+"વિન્ડોને સમાવેલ છે."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "મુખ્ય વિન્ડોમાં મોડલ સંવાદને જોડો"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid "This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "આ કી org.gnome.mutter માં કી ઉપર લખાઇ જશે જ્યારે GNOME Shell ચાલી રહ્યુ હોય."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "જ્યારે સ્ક્રીન બાજુ પર વિન્ડોને પડતી મૂકી રહ્યા હોય ત્યારે ટાઇલીંગ સક્રિય કરો"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "કામ કરવાની જગ્યા ગતિશીલ રીતે સંચાલિત થયેલ છે"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "ફક્ત પ્રાથમિક મોનિટર પર કામ કરવાની જગ્યા"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "માઉસ સ્થિતિમાં ફેરફારો કરવામાં વિલંબ જ્યાં સુધી પોઇંટર ખસેડવાનું બંધ થાય"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "કૅપ્ટીવ પોર્ટલ"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "ત્યાં %s માટે પસંદગી સંવાદને લાવવામાં ભૂલ હતી:"
+
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extensions"
+msgstr "GNOME Shell ઍક્સટેન્શન"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "રદ કરો"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "આગળ"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "તાળું ખોલો"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "પ્રવેશો"
+
+#: ../js/gdm/loginDialog.js:269
+msgid "Choose Session"
+msgstr "સત્ર પસંદ કરો"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "શું યાદી થયેલ નથી?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(દા.ત., વપરાશકર્તા અથવા %s)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "વપરાશકર્તાનામ:"
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "પ્રવેશ વિન્ડો"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "સત્તાધિકરણ ભૂલ"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(અથવા સ્વાઇપ આંગળી)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "આદેશ મળ્યો નથી"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "આદેશનું પદચ્છેદન કરી શક્યા નથી:"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” ને અમલમાં મૂકવાનુ નિષ્ફળ:"
+
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "વેબ સત્તાધિકરણ દિશામાન"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "વારંવાર વપરાતા કાર્યક્રમો અહિંયા દેખાશે"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "સામાન્ય"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "બધા"
+
+#: ../js/ui/appDisplay.js:1790
+msgid "New Window"
+msgstr "નવી વિન્ડો"
+
+#: ../js/ui/appDisplay.js:1816 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "પસંદીદાઓ માંથી દૂર કરો"
+
+#: ../js/ui/appDisplay.js:1822
+msgid "Add to Favorites"
+msgstr "પસંદીદાને ઉમેરો"
+
+#: ../js/ui/appDisplay.js:1831
+msgid "Show Details"
+msgstr "વિગતો બતાવો"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s ને તમારી પસંદીદામાં ઉમેરી દેવામાં આવ્યુ છે."
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s ને તમારી પસંદીદામાંથી દૂર કરી દેવામાં આવ્યુ છે."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "સુયોજનો"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "પાશ્ર્વભાગને બદલો..."
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "બધા દિવસ"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Su"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Th"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "પહેલાંનો મહિનો"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "આગળનો મહિનો"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "અનુસૂચિત કંઇ નથી"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "આજે"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "આવતી કાલે"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "આ અઠવાડિયે"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "આગળનું અઠવાડિયું"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "બહારની ડ્રાઇવ જોડાયેલ છે"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "બહારની ડ્રાઇવનું જોડાણ તૂટી ગયુ"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "દૂર કરી શકાય તેવા ઉપકરણો"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s સાથે ખોલો"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "રદ કરો"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "પાસવર્ડ:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "ફરીથી પ્રયત્ન કરો:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "જોડાવો"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "પાસવર્ડ:"
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "કી:"
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "ઓળખાણ: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "ખાનગી કી પાસવર્ડ:"
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "સેવા:"
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "વાયરલેસ નેટવર્ક દ્દારાસત્તાધિકરણ જરૂરી"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "પાસવર્ડ અથવા એનક્રિપ્શન કીઓને વાયરલેસ નેટવર્ક “%s” માં પ્રવેશની જરૂર છે."
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "વાયર થયેલ 802.1X સત્તાધિકરણ"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "નેટવર્ક નામ:"
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "DSL સત્તાધિકરણ"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "PIN કોડ જરૂરી"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "મોબાઇલ બ્રોડબેન્ડ ઉપકરણ માટે PIN કોડ જરૂરી છે"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "મોબાઇલ બ્રોડબેન્ડ નેટવર્ક પાસવર્ડ"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "પાસવર્ડ “%s” સાથે જોડાવા માટે જરૂરી છે."
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "સત્તાધિકરણ જરૂરી"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "વહીવટકર્તા"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "સત્તાધિકરણ"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "દિલગીર છું, કામ કરતુ નથી. મહેરબાની કરીને ફરીથી પ્રયત્ન કરો."
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "આમંત્રણ"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "કોલ"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "ફાઇલ પરિવહન"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "વાતચીત"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "અવાજ ચાલુ રાખો"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "મૂંગુ"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+msgid "Yesterday, %H∶%M"
+msgstr "ગઇ કાલે, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "ગઈકાલે ∶%l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s એ હવે %s તરીકે જાણીતુ છે"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%s માં આમંત્રણ"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s એ તમને %s માં જોડાવા માટે આમંત્રણ આપી રહ્યા છે"
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "ના પાડવી"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "સ્વીકારો"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s માંથી વિડીયો કોલ"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s માંથી કોલ"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "જવાબ"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s એ %s માં મોકલી રહ્યા છે"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s ને જોવા માટે પરવાનગી આપવાનું ગમે થે જ્યારે તમે ઓનલાઇન હોય"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "નેટવર્ક ભૂલ"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "સત્તાધિકરણ નિષ્ક્રિય"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "એનક્રિપ્શન ભૂલ"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "પ્રમાણપત્ર પૂરુ પાડેલ નથી"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "પ્રમાણપત્ર વિશ્ર્વાસપાત્ર નથી"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "પ્રમાણપત્ર નિવૃત્ત થયેલ છે"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "પ્રમાણપત્ર સક્રિય થયેલ નથી"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "પ્રમાણપત્ર યજમાનનામ બંધબેસતુ નથી"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "પ્રમાણપજ્ઞ ફિંગરપ્રિન્ટ બંધબેસતુ નથી"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "જાતે હસ્તાક્ષર થયેલ પ્રમાણપત્ર"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "સ્થિતિ ઓફલાઇન તરીકે સુયોજિત છે"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "એનક્રિપ્શન ઉપલબ્ધ નથી"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "પ્રમાણપત્ર અમાન્ય છે"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "જોડાણને નામંજૂર કરી દેવામાં આવ્યુ છે"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "જોડાણ સ્થાપિત કરી શકાતુ નથી"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "જોડાણ ખોવાઈ ગયેલ છે"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "આ ખાતુ પહેલેથી જ સર્વર સાથે જોડાયેલ છે"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid "Connection has been replaced by a new connection using the same resource"
+msgstr "સરખા સ્ત્રોતની મદદથી જોડાણને નવાં જોડાણ દ્દારા બદલી દેવામાં આવ્યુ છે"
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "ખાતુ પહેલેથી સર્વર પર અસ્તિત્વ ધરાવે છે"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "સર્વર એ જોડાણને સંચાલિત કરવા માટે હાલમાં વ્યસ્ત છે"
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "પ્રમાણપત્રને રદ કરી દેવામાં આવ્યુ છે"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr "પ્રમાણપત્ર અસુરક્ષિત સાઇફર અલ્ગોરિધમને વાપરે છે અથવા ક્રિપ્ટોગ્રાફિકલી નબળુ છે"
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"સર્વર પ્રમાણપત્રની લંબાઇ, સર્વર પ્રમાણપત્ર કતારની ઊંચાઇ, ક્રિપ્ટોગ્રાફી લાઇબ્રેરી દ્દારા "
+"બનાવેલ મર્યાદાને વધારે છે"
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "આંતરિક ભૂલ"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s સાથે જોડાવાનું અસમર્થ"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "ખાતામાં જુઓ"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "અજ્ઞાત કારણ"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "વિન્ડો"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "કાર્યક્રમો બતાવો"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "ડૅશ"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "કૅલેન્ડરને ખોલો"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "ઘડિયાળ ખોલો"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "તારીખ અને સમય સુયોજનો"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s માંથી બહાર નીકળો"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "બહાર નીકળો"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s એ %d સેકંડમાં આપમેળે બહાર નીકળી જશે."
+msgstr[1] "%s એ %d સેકંડોમાં આપમેળે બહાર નીકળી જશે."
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "તમે %d સેકંડમાં આપમેળે બહાર નીકળી જશે."
+msgstr[1] "તમે %d સેકંડોમાં આપમેળે બહાર નીકળી જશે."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "બહાર નીકળો"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "પાવર બંધ"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "સુધારા સ્થાપિત કરો અને પાવર બંધ કરો"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "સિસ્ટમ %d સેકંડમાં આપમેળે પાવર બંધ થઇ જશે."
+msgstr[1] "સિસ્ટમ %d સેકંડમાં આપમેળે પાવર બંધ થઇ જશે."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "બાકી રહેલા સોફ્ટવેર સુધારાને સ્થાપિત કરો"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "પુન:શરૂ કરો"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "પાવર બંધ"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "પુન:શરૂ કરો"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "સિસ્ટમ %d સેકંડમાં આપમેળે પુન:શરૂ થઇ જશે."
+msgstr[1] "સિસ્ટમ %d સેકંડોમાં આપમેળે પુન:શરૂ થઇ જશે."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "સુધારા સ્થાપિત કરો અને પુન:શરૂ કરો"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+#| msgid "The system will restart automatically in %d second."
+#| msgid_plural "The system will restart automatically in %d seconds."
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural "The system will automatically restart and install updates in %d seconds."
+msgstr[0] "સિસ્ટમ %d સેકંડમાં આપમેળે સુધારાઓને પુન:શરૂ અને સ્થાપિત કરશે."
+msgstr[1] "સિસ્ટમ %d સેકંડોમાં આપમેળે સુધારાઓને પુન:શરૂ અને સ્થાપિત કરશે."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "પુન:શરૂ કરો અને સ્થાપિત કરો"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "સ્થાપિત કરો અને પાવર બંધ "
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "સુધારા સ્થાપિત થાય પછી પાવર બંધ કરો"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "બેટરી પાવર પર ચાલી રહ્યુ છે: મહેરબાની કરીને સુધારા સ્થાપિત કરતા પહેલાં પ્લગઇન કરો."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "અમુક કાર્યક્રમો વ્યસ્ત છે અથવા તેની પાસે અસંગ્રહેલ કામ છે."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "બીજા વપરાશકર્તાઓ પ્રવેશેલ છે."
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (દૂરસ્થ)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (કન્સોલ)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "સ્થાપિત કરો"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.org માંથી “%s” ને સ્થાપિત અને ડાઉનલોડ કરો?"
+
+#: ../js/ui/keyboard.js:700 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "કિબોર્ડ"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "એક્સટેન્શનો સ્થાપિત થયેલ નથી"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s એ કોઇપણ ભૂલોને બહાર કાઢતા નથી."
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "ભૂલો છુપાડો"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "ભૂલો બતાવો"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "સક્રિય"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "નિષ્ક્રિય"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "ભૂલ"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "અપ્રચલિત"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "ડાઉનલોડ કરી રહ્યા છે"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "સ્ત્રોત દર્શાવો"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "વેબ પાનું"
+
+#: ../js/ui/messageTray.js:1327
+msgid "Open"
+msgstr "ખોલો"
+
+#: ../js/ui/messageTray.js:1334
+msgid "Remove"
+msgstr "દૂર કરો"
+
+#: ../js/ui/messageTray.js:1631
+msgid "Notifications"
+msgstr "સૂચનાઓ"
+
+#: ../js/ui/messageTray.js:1638
+msgid "Clear Messages"
+msgstr "સંદેશા સાફ કરો"
+
+#: ../js/ui/messageTray.js:1657
+msgid "Notification Settings"
+msgstr "સૂચના સુયોજનો"
+
+#: ../js/ui/messageTray.js:1710
+msgid "Tray Menu"
+msgstr "ટ્રે મેનુ"
+
+#: ../js/ui/messageTray.js:1934
+msgid "No Messages"
+msgstr "સંદેશા નથી"
+
+#: ../js/ui/messageTray.js:1979
+msgid "Message Tray"
+msgstr "સંદેશો ટ્રે"
+
+#: ../js/ui/messageTray.js:2992
+msgid "System Information"
+msgstr "સિસ્ટમ જાણકારી"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "અજ્ઞાત"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d નવો સંદેશો"
+msgstr[1] "%d નવા સંદેશા"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "રદ કરો"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "ઝાંખી"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "શોધવા માટે ટાઇપ કરો..."
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "બહાર નીકળો"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "પ્રવૃત્તિઓ"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "ટોચની પટ્ટી"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "આદેશને દાખલ કરો"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "બંધ કરો"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "પુન:શરૂ કરી રહ્યા છે…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d નવી સૂચના"
+msgstr[1] "%d નવી સૂચનાઓ"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "તાળુ"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME ને સ્ક્રીનને તાલુ મારવાની જરૂર છે"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "તાળુ મારવામાં અસમર્થ"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "તાળુ કાર્યક્રમ દ્દારા બ્લોક થયેલ છે"
+
+#: ../js/ui/search.js:607
+msgid "Searching…"
+msgstr "શોધી રહ્યા છે..."
+
+#: ../js/ui/search.js:609
+msgid "No results."
+msgstr "પરિણામો નથી."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "નકલ કરો"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "ચોંટાડો"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "લખાણ બતાવો"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "લખાણ છુપાડો"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "પાસવર્ડ"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "પાસવર્ડને યાદ રાખો"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "ઉપલબ્ધતા"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "નાનુ મોટુ કરો"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "સ્ક્રીન રીડર"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "સ્ક્રીન કિબોર્ડ"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "દેખાતી ચેતવણીઓ"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "સ્ટીકી કી"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "ધીમી કી"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "બાઉન્સ કી"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "માઉસ કીઓ"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "ઉચ્ચ વિરોધાભાસ"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "લાંબુ લખાણ"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "બ્લુટુથ"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "બંધ કરો"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "બ્લુટુથ સુયોજનો"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d જોડાયેલ ઉપકરણ"
+msgstr[1] "%d જોડાયેલ ઉપકરણો"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+msgid "Not Connected"
+msgstr "જોડાયેલ નથી"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "તેજસ્વિતા"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "કીબોર્ડ લેઆઉટને બતાવો"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "સ્થાન"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "નિષ્ક્રિય"
+
+#: ../js/ui/status/location.js:73
+#| msgid "Power Settings"
+msgid "Privacy Settings"
+msgstr "ખાનગી સુયોજનો"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "વપરાશમાં"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "સક્રિય"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<અજ્ઞાત>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+msgid "Off"
+msgstr "બંધ"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "જોડાયેલ"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "સંચાલિત થયેલ નથી"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "જોડાઇ તૂટી રહ્યુ છે"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+msgid "Connecting"
+msgstr "જોડાઇ રહ્યા છે"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "સત્તાધિકરણ જરૂરી"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "ફર્મવેર ગેરહાજર"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "બિનઉપલબ્ધ"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "જોડાણ નિષ્ફળ"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "વાયર થયેલ સુયોજનો"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "મોબાઇલ બ્રોડબેન્ડ સુયોજનો"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+msgid "Hardware Disabled"
+msgstr "હાર્ડવેર નિષ્ક્રિય"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "ઇન્ટરનેટ જોડાણ તરીકે વાપરો"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "ઍરપ્લેન સ્થિતિ ચાલુ છે"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi નિષ્ક્રિય થયેલ છે જ્યારે ઍરપ્લેન સ્થિતિ ચાલુ હોય."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "ઍરપ્લેન સ્થિતિને બંધ કરો"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi બંધ છે"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "નેટવર્કમાં જોડાવા માટે ક્રમમાં Wi-Fi ને ચાલુ કરવાની જરૂર છે."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi ચાલુ કરો"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi નેટવર્ક"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "નેટવર્ક પસંદ કરો"
+
+#: ../js/ui/status/network.js:882
+msgid "No Networks"
+msgstr "નેટવર્ક નથી"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "બંધ કરવા માટે હાર્ડવેર સ્વીચને વાપરો"
+
+#: ../js/ui/status/network.js:1173
+msgid "Select Network"
+msgstr "નેટવર્ક પસંદ કરો"
+
+#: ../js/ui/status/network.js:1179
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi સુયોજનો"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "ચાલુ કરો"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "હૉટસ્પોટ સક્રિય"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "જોડાઇ રહ્યા છે..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "સત્તાધિકરણ જરૂરી"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "જોડાણ નિષ્ફળ"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "નેટવર્ક સુયોજનો"
+
+#: ../js/ui/status/network.js:1482
+msgid "VPN Settings"
+msgstr "VPN સુયોજનો"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "નેટવર્ક સંચાલક"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "નેટવર્ક જોડાણનું સક્રિયકરણ નિષ્ફળ થયેલ છે"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "પાવર સુયોજનો"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "સંપૂર્ણપણે ચાર્જ થયેલ છે"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "અંદાજ કરી રહ્યા છે..."
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d બાકી રહેલ છે (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d જ્યાં સુધી સંપૂર્ણ થાય (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "બેટરી"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "ઍરપ્લેન સ્થિતિ"
+
+#: ../js/ui/status/rfkill.js:85
+msgid "On"
+msgstr "ચાલુ"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "વપરાશકર્તાને બદલો"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "બહાર નીકળો"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "દિશા તાળુ"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "અટકાવો"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "પાવર બંધ"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "બદલાયેલ વોલ્યુમ"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "વોલ્યુમ"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "માઇક્રોફોન"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "બીજા વપરાશકર્તા તરીકે પ્રવેશો"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "વિન્ડોનું તાળુ ખોલો"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "કાર્યક્રમો"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "શોધો"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” તૈયાર છે"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "શું તમને આ દર્શાવ સુયોજનોને રાખવા માંગો છો?ે"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "સુયોજનોને પાછા લાવો"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "ફેરફારો કરો"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "સુયોજન ફેરફારો એ %d સેકંડમાં પાછા આવશે"
+msgstr[1] "સુયોજન ફેરફારો એ %d સેકંડોમાં પાછા આવશે"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "ન્યુનતમ કરો"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "મહત્તમમાંથી પાછુ લાવો"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "મહત્તમ કરો"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "ખસેડો"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "માપ બદલો"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "શીર્ષકપટ્ટી ઓનસ્ક્રીનને ખસેડો"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "હંમેશા ટોચ ઉપર"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "હંમેશા દૃશ્ય કામ કરવાની જગ્યા પર"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "ઉપર કામ કરવાની જગ્યા પર ખસેડો"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "નીચે કામ કરવાની જગ્યાએ ખસેડો"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution કૅલેન્ડર"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u આઉટપુટ"
+msgstr[1] "%u આઉટપુટો"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ઇનપુટ"
+msgstr[1] "%u ઇનપુટો"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "સિસ્ટમ અવાજો"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "ા"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "લૉગિન સ્ક્રીન માટે GDM દ્દારા વાપરેલ સ્થિતિ"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "ખાસ સ્થિતિને વાપરો, દા.ત. પ્રવેશ સ્ક્રીન માટે \"gdm\""
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "શક્ય સ્થિતિઓની યાદી કરો"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” ને શરૂ કરવામાં નિષ્ફળતા"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "પાસવર્ડ બંધબેસતો નથી"
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "પાસવર્ડને ખાલી રાખી શકાતો નથી"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "સત્તાધિકરણ વપરાશકર્તા દ્દારા અસ્વીકાર થયેલ છે"
+
+#~ msgid "Screenshots"
+#~ msgstr "સ્ક્રીનશોટ"
+
+#~ msgid "Record a screencast"
+#~ msgstr "સ્ક્રીનકાસ્ટ રેકોર્ડ કરો"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "શું કાર્યક્રમ વપરાશ વિશે આંકડા એકત્ર કરવા જોઇએ"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "શેલ સામાન્ય રીતે મોટાભાગનાં એકને હાજર રાખવા માટે ક્રમમાં સક્રિય કાર્યક્રમોને મોનિટર "
+#~ "કરવામાં આવે છે (દા.ત. લૉન્ચર). જ્યારે આ માહિતીને ખાનગી રાખવામાં આવશે, તમે ખાનગી "
+#~ "કારણો માટે આને નિષ્ક્રિય કરવા માંગી શકો છો. મહેરબાની કરીને આવુ કરવાનું નોંધો તેથી "
+#~ "પહેલેથી સંગ્રહેલ માહિતીને દૂર કરાતુ નથી."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "વર્ગોની યાદી કે જે ફોલ્ડરો તરીકે દર્શાવેલ હોવા જોઇએ"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "આ યાદીમાં દરેક વર્ગનામ કાર્યક્રમ દૃશ્યમાં ફોલ્ડર તરીકે રજૂ થયેલ છે, મુખ્ય વિન્ડોમાં ઇનલાઇન "
+#~ "દર્શાવવા કરતા."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "વપરાશકર્તા દ્દારા છેલ્લુ IM સુયોજનને સંગ્રહવા આંતરિક રીતે વાપરેલ છે. અહિંયા કિંમત "
+#~ "TpConnectionPresenceType ઍન્યુમરેશનમાંથી છે."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "વપરાશકર્તા માટે છેલ્લી સત્રની હાજર પરિસ્થિતિને સંગ્રહવા આંતરિક રીતે વાપરેલ છે. કિંમત "
+#~ "અહિંયા GsmPresenceStatus ઍન્યુમરેશનમાંથી છે."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "કાર્યક્રમ મેનુને ખોલવા માટે કિબાઇન્ડીંગ"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "બિલ્ટઇન સ્ક્રીન રેકોર્ડરને શરૂ/બંધ કરવા માટે કfબાઇન્ડીંગ."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "રેકોર્ડીંગ સ્ક્રીનકાસ્ટ માટે વાપરેલ ફ્રેમરેટ."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "પરિણામી સ્ક્રીનકાસ્ટની framerate એ દરેક સેકંડે ફ્રેમમાં GNOME Shell નાં સ્ક્રીનકાસ્ટ "
+#~ "રેકોર્ડર દ્દારા અહેવાલ થયેલ છે."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "સ્ક્રીનકાસ્ટને એનકોડ કરવા માટે વાપરેલ gstreamer પાઇપલાઇન"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "રેકોર્ડીંગ ને એનકોડ કરવા માટે વાપરેલ GStreamer પાઇપલાઇનને સુયોજિત કરે છે. તે gst-"
+#~ "launch માટે વાપરેલ બંધારણને અનુસરે છે. પાઇપલાઇન પાસે બિનજોડાયેલ સીંક પેડ હોય છે જ્યાં "
+#~ "રેકોર્ડ થયેલ વિડિયો રેકોર્ડ થયેલ છે. સામાન્ય રીતે તેમની પાસે બિનજોડાયેલ સ્ત્રોત પેડ હશે; "
+#~ "તે પેડમાંથી આઉટપુટ એ આઉટપુટ ફાઇલમાં લખાયેલ હશે. છતાંપણ પાઇપલાઇન પણ તેનાં પોતાના "
+#~ "આઉટપુટની સંભાળ લઇ શકે છે - આ shout2send મારફતે અથવા તેનાં જેવા મારફતે icecast "
+#~ "સર્વરમાં આઉટપુટને મોકલવા વાપરી શકાય છે. જ્યારે ખાલી ફાઇલને સુયોજિત અથવા અસુયોજિત "
+#~ "કરો, મૂળભૂત પાઇપલાઇન વાપરેલ હે. આ હાલમાં 'vp8enc min_quantizer=13 "
+#~ "max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! "
+#~ "webmmux' છે અને VP8 કોડેકની મદદતી WEBM માં રેકોર્ડ થાય છે. %T એ સિસ્ટમ પર ઓપ્ટીમલ "
+#~ "થ્રેડ ગણતરી પર ધારવા માટે પ્લેસહોલ્ડરને વાપરેલ છે."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "સ્ક્રીનકાસ્ટને સંગ્રહ કરવા માટે વાપરેલ ફાઇલ ઍક્સટેન્શન"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "રેકોર્ડ થયેલ સ્ક્રીનકાસ્ટ માટે ફાઇલનામ વર્તમાન તારીખ પર આધારિત અનન્ય ફાઇલનામ હશે, "
+#~ "અને આ ઍક્સટેન્શનને વાપરો. તેને બદલેલ હોવુ જોઇએ જ્યારે વિવિધ પાત્ર બંધારણમાં રેકોર્ડ કરી "
+#~ "રહ્યા છે."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "શીર્ષકપટ્ટી પર બટનોની ગોઠવણ"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "આ કી org.gnome.desktop.wm.preferences માં કી ઉપર લખાઇ જશે જ્યારે GNOME શેલ "
+#~ "ચાલી રહ્યુ હોય."
+
+#~ msgid "Extension"
+#~ msgstr "ઍક્સટેન્શન"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "ઉપર કોમ્બોબોક્સની મદદથી રૂપરેખાંકિત કરવા માટે ઍક્સટેન્શનને પસંદ કરો."
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "સત્ર..."
+
+#~| msgid "Power Off"
+#~ msgid "Power"
+#~ msgstr "પાવર"
+
+#~ msgid "Restart"
+#~ msgstr "પુન:શરૂ કરો"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d %t માંથી સ્ક્રીનકાસ્ટ"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "આ કાર્યક્રમોને બહાર નીકળવા માટે બહાર નીકળો પર ક્લિક કરો અને સિસ્ટમમાંથી બહાર "
+#~ "નીકળો."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "સિસ્ટમની બહાર નીકળી રહ્યા છે."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "આ કાર્યક્રમોને બહાર નીકાળવા માટે પાવર બંધ કરો પર ક્લિક કરો અને સિસ્ટમનો પાવર બંધ "
+#~ "કરો"
+
+#~ msgid "Powering off the system."
+#~ msgstr "સિસ્ટમનો પાવર બંધ કરી રહ્યા છે."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "આ કાર્યક્રમોમાંથી બહાર નીકળવા માટે પુન:શરૂ કરો પર ક્લિક કરો અને સિસ્ટમને પુન:શરૂ કરો."
+
+#~ msgid "Restarting the system."
+#~ msgstr "સિસ્ટમને પુન:શરૂ કરી રહ્યા છે."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "યુનિવર્સલ વપરાશ સુયોજનો"
+
+#~ msgid "Visibility"
+#~ msgstr "દૃશ્યતા"
+
+#~| msgid "Send Files to Device..."
+#~ msgid "Send Files to Device…"
+#~ msgstr "ઉપકરણમાં ફાઇલોને મોકલો..."
+
+#~| msgid "Set Up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "નવા ઉપકરણને સુયોજિત કરો..."
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "ફાઇલોને મોકલો..."
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "કીબોર્ડ સુયોજનો"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "માઉસ સુયોજનો"
+
+#~ msgid "Sound Settings"
+#~ msgstr "સાઇન્ડ સુયોજનો"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s માંથી સત્તાધિકરણ માંગણી"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "ઉપકરણ %s એ સેવા %s' માટે પ્રવેશ ઇચ્છે છે"
+
+#~ msgid "Always grant access"
+#~ msgstr "હંમેશા વપરાશ મંજૂર કરો"
+
+#~ msgid "Grant this time only"
+#~ msgstr "ફક્ત આ સમયે સંમતિ આપો"
+
+#~ msgid "Reject"
+#~ msgstr "રદ કરો"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s માટો જોડીની ખાતરી"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "ઉપકરણ %s આ કમ્પ્યૂટર સાથે જોડી કરવા માંગે છે"
+
+#~ msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgstr "મહેરબાની કરીને ખાતરી કરો કે શું PIN '%06d' એ ઉપકરણ પર એક સાથે બંધબેસે છે."
+
+#~ msgid "Matches"
+#~ msgstr "બંધબેસે છે"
+
+#~ msgid "Does not match"
+#~ msgstr "બંધબેસતુ નથી"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s માટે જોડી માંગણી"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "મહેરબાની કરીને ઉપકરણ પર દર્શાવેલ PIN દાખલ કરો."
+
+#~ msgid "OK"
+#~ msgstr "બરાબર"
+
+#~ msgid "Region & Language Settings"
+#~ msgstr "વિસ્તાર અને ભાષા સુયોજનો"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "વોલ્યુમ, નેટવર્ક, બેટરી"
+
+#~ msgid "disabled"
+#~ msgstr "નિષ્ક્રિય"
+
+#~ msgid "cable unplugged"
+#~ msgstr "કેબલ પ્લગ થયેલ નથી"
+
+#~ msgid "unavailable"
+#~ msgstr "બિનઉપલબ્ધ"
+
+#~ msgid "More…"
+#~ msgstr "વધારે…"
+
+#~ msgid "Wired"
+#~ msgstr "વાયરવાળું"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "આપમેળે ઇથરનેટ"
+
+#~ msgid "Auto broadband"
+#~ msgstr "આપમેળે બ્રોડબેન્ડ"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "આપમેળે ડાયલ અપ"
+
+#~ msgid "Auto %s"
+#~ msgstr "આપમેળે %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "આપમેળે બ્લુટુથ"
+
+#~ msgid "Auto wireless"
+#~ msgstr "આપમેળે વાયરલેસ"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "Networking is disabled"
+#~ msgstr "નેટવર્કીંગ નિષ્ક્રિય થયેલ છે"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d કલાક બાકી રહેલ છે"
+#~ msgstr[1] "%d કલાક બાકી રહેલ છે"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s બાકી રહેલ છે"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "કલાક"
+#~ msgstr[1] "કલાકો"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "મિનિટ"
+#~ msgstr[1] "મિનિટો"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d મિનિટ બાકી રહી છે"
+#~ msgstr[1] "%d મિનિટ બાકી રહી છે"
+
+#~ msgctxt "percent of battery remaining"
+#~ msgid "%d%%"
+#~ msgstr "%d%%"
+
+#~ msgid "AC Adapter"
+#~ msgstr "AC ઍડપ્ટર"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "લેપટોપ બેટરી"
+
+#~ msgid "Monitor"
+#~ msgstr "મોનિટર"
+
+#~ msgid "Mouse"
+#~ msgstr "માઉસ"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "સેલ ફોન"
+
+#~ msgid "Media Player"
+#~ msgstr "મીડિયા પ્લેયર"
+
+#~ msgid "Tablet"
+#~ msgstr "ટૅબલેટ"
+
+#~ msgid "Computer"
+#~ msgstr "કમ્પ્યૂટર"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "અજ્ઞાત"
+
+#~ msgid "Available"
+#~ msgstr "ઉપલબ્ધ"
+
+#~ msgid "Busy"
+#~ msgstr "વ્યસ્ત"
+
+#~ msgid "Invisible"
+#~ msgstr "અદૃશ્ય"
+
+#~ msgid "Away"
+#~ msgstr "દૂર"
+
+#~ msgid "Idle"
+#~ msgstr "નિષ્ક્રિય"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "તમારી વાર્તાલાપ પરિસ્થિતિ વ્યસ્ત તરીકે સુયોજિત હશે"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "સૂચનાઓ હવે નિષ્ક્રિય થયેલ છે, વાર્તાલાપ સંદેશાને સમાવી રહ્યા છે, બીજાને જણાવવા માટે "
+#~ "તમારી ઓનલાઇન સ્થિતિને ગોઠવી દેવામાં આવી છે કે જે તમે તેનાં સંદેશાને જોઇ શકશો નહિં."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "બંધ કરવાથી તેઓ અસંગ્રહેલ કામને ગુમાવી શકે છે."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "માફ કરો, આજે તમારા માટે શાણપણ નથી:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s એ એવી કિંમત છે જે Oracle કહે છે"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "પ્રવેશો"
+
+#~ msgid "tray"
+#~ msgstr "ટ્રે"
+
+#~ msgid "Clear"
+#~ msgstr "સાફ કરો"
+
+#~ msgid "More..."
+#~ msgstr "વધારે..."
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APPLICATIONS"
+
+#~ msgid "SETTINGS"
+#~ msgstr "SETTINGS"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Subscription request"
+#~ msgstr "ઉમેદવારી માંગણી"
+
+#~ msgid "Connection error"
+#~ msgstr "જોડાણ ભૂલ"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "<b>%A</b> પર <b>%X</b> અહિંયા મોકલેલ છે"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "<b>%A</b> પર મોકલેલ છે, <b>%B %d</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "%s માટે જોડાણ નિષ્ફળ"
+
+#~ msgid "Reconnect"
+#~ msgstr "પુન:જોડાવો"
+
+#~ msgid "Browse Files..."
+#~ msgstr "ફાઇલોને બ્રાઉઝ કરો..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "ઉપકરણને બ્રાઉઝ કરતી વખતે ભૂલ"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "સુચિત ઉપકરણને બ્રાઉઝ કરી શકાતુ નથી, ભૂલ '%s' છે"
+
+#~ msgid "Wireless"
+#~ msgstr "વાયરલેસ"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN જોડાણો"
+
+#~ msgid "System Settings"
+#~ msgstr "સિસ્ટમ સુયોજનો"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "તમારા મનગમતા ઇસ્ટર ઇંડા"
+
+#~ msgid "United Kingdom"
+#~ msgstr "United Kingdom"
+
+#~ msgid "Default"
+#~ msgstr "મૂળભૂત"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "નિષ્ક્રિય થયેલ OpenSearch પ્રબંધક"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "'%s' ને અનમાઉન્ટ કરવામાં નિષ્ફળતા"
+
+#~ msgid "Retry"
+#~ msgstr "પુન:પ્રયત્ન કરો"
+
+#~ msgid "Connect to..."
+#~ msgstr "ની સાથે જોડાવો..."
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "સ્થાનો અને ઉપકરણો"
+
+#~ msgid "Passphrase"
+#~ msgstr "પાસફ્રેજ"
+
+#~ msgid "Home"
+#~ msgstr "ઘર"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "સેકંડોમાં સમયને બતાવો"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "જો true હોય તો, સમયમાં સેકંડ બતાવો."
+
+#~ msgid "Show date in clock"
+#~ msgstr "ઘડિયાળમાં તારીખને બતાવો"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "જો true હોય તો, ઘડિયાળમાં તારીખ બતાવો, સમય સાથે વધુમાં."
+
+#~ msgid "CONTACTS"
+#~ msgstr "સંપર્કો"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "ખોટો પાસવર્ડ, મહેરબાની કરીને ફરીથી પ્રયત્ન કરો"
+
+#~ msgid "%s is online."
+#~ msgstr "%s ઓનલાઇન છે."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s ઓફલાઇન છે."
+
+#~ msgid "%s is away."
+#~ msgstr "%s એ દૂર છે."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s વ્યસ્ત છે."
+
+#~ msgid "Hidden"
+#~ msgstr "છુપાયેલ"
+
+#~ msgid "Power Off..."
+#~ msgstr "પાવર બંધ..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "ઓનલાઇન ખાતુ"
+
+#~ msgid "Lock Screen"
+#~ msgstr "સ્ક્રીનને તાળુ મારો"
+
+#~ msgid "Log Out..."
+#~ msgstr "બહાર નીકળો..."
+
+#~| msgid "Password:"
+#~ msgid "Show password"
+#~ msgstr "પાસવર્ડ બતાવો:"
+
+#~ msgid "Home Folder"
+#~ msgstr "ઘર ફોલ્ડર"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "તમે હવે મોબાઇલ બ્રોડબેન્ડ જોડાણ '%s' સાથે જોડાયેલ છો"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "તમે હવે વાયરલેસ નેટવર્ક '%s' સાથે જોડાયેલ છો"
+
+#~ msgid "You're now connected to wired network '%s'"
+#~ msgstr "તમે હવે વાયરવાળું નેટવર્ક '%s' સાથે જોડાયેલ છો"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "તમે હવે VPN નેટવર્ક '%s' સાથે જોડાયેલ છો"
+
+#~ msgid "%d minute ago"
+#~ msgid_plural "%d minutes ago"
+#~ msgstr[0] "%d મિનિટ પહેલાં"
+#~ msgstr[1] "%d મિનિટો પહેલાં"
+
+#~ msgid "%d hour ago"
+#~ msgid_plural "%d hours ago"
+#~ msgstr[0] "%d કલાક પહેલાં"
+#~ msgstr[1] "%d કલાકો પહેલાં"
+
+#~ msgid "%d day ago"
+#~ msgid_plural "%d days ago"
+#~ msgstr[0] "%d દિવસ પહેલાં"
+#~ msgstr[1] "%d દિવસો પહેલાં"
+
+#~ msgid "%d week ago"
+#~ msgid_plural "%d weeks ago"
+#~ msgstr[0] "%d અઠવાડિયુ પહેલાં"
+#~ msgstr[1] "%d અઠવાડિયાઓ પહેલાં"
diff --git a/po/he.po b/po/he.po
new file mode 100644
index 0000000..0227300
--- /dev/null
+++ b/po/he.po
@@ -0,0 +1,4432 @@
+# Hebrew translation for gnome-shell.
+# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# liel <lielft@gmail.com>, 2009.
+# Yaron Shahrabani <sh.yaron@gmail.com>, 2010.
+# Yosef Or Boczko <yoseforb@gmail.com>, 2013-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-17 18:27+0000\n"
+"PO-Revision-Date: 2022-09-15 13:50+0300\n"
+"Last-Translator: Yosef Or Boczko <yoseforb@gmail.com>\n"
+"Language-Team: Hebrew <>\n"
+"Language: he\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : n==2 ? 1 : n>10 && n%10==0 ? "
+"2 : 3)\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Generator: Gtranslator 40.0\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "משגרים"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "הפעלת יישום מועדף 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "הפעלת יישום מועדף 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "הפעלת יישום מועדף 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "הפעלת יישום מועדף 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "הפעלת יישום מועדף 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "הפעלת יישום מועדף 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "הפעלת יישום מועדף 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "הפעלת יישום מועדף 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "הפעלת יישום מועדף 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "צילומי מסך"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "צילום מסך באופן הידודי"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "צילום המסך"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "צילום של חלון"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "הסרטת המסך באופן הידודי"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "מערכת"
+
+# javascript-format
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "הצגת רשימת ההתרעות"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "התמקדות על ההתרעה הפעילה"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "הצגת הסקירה"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "הצגת כל היישומים"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "פתיחת תפריט היישום"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "מעטפת GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "ניהול חלונות והרצת יישומים"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Enable internal tools useful for developers and testers from Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs of extensions to enable"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs of extensions to force disabling"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Disable user extensions"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Disables the validation of extension version compatibility"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "List of desktop file IDs for favorite applications"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "History for command (Alt-F2) dialog"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "History for the looking glass dialog"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Always show the “Log out” menu item in the user menu."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "The last selected non-default power profile"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "The last version the “Welcome to GNOME” dialog was shown for"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Layout of the app picker"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Keybinding to open the application menu"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Keybinding to open the application menu."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Keybinding to shift between overview states"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "Keybinding to shift between session, window picker and app grid"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "Keybinding to shift between app grid, window picker and session"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Keybinding to open the “Show Applications” view"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Keybinding to open the overview"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Keybinding to open the Activities Overview."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Keybinding to toggle the visibility of the notification list"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Keybinding to toggle the visibility of the notification list."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Keybinding to focus the active notification"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Keybinding to focus the active notification."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Switch to application 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Switch to application 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Switch to application 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Switch to application 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Switch to application 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Switch to application 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Switch to application 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Switch to application 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Switch to application 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Limit switcher to current workspace."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "The application icon mode."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Locations"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "The locations to show in world clocks"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatic location"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Whether to fetch the current location or not"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Location"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "The location for which to show a forecast"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Attach modal dialog to the parent window"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Enable edge tiling when dropping windows on screen edges"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Workspaces are managed dynamically"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Workspaces only on primary monitor"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Delay focus changes in mouse mode until the pointer stops moving"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "חיבורי רשת"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "משהו השתבת"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"אנו מצטערים, אבל ישנה בעיה: לא ניתן להציג את הגדרות ההרחבה. אנו ממליצים "
+"לדווח על בעיה ליוצרי ההרחבה."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "פרטים טכניים"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "עמוד הבית"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "לבקר באתר ההרחבה"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "ביטול"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "ססמה"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "בחירת הפעלה"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "לא רשום?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(משתמש או %s לדוגמה)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "שם משתמש"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "חלון כניסה"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "שגיאת אימות"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(או להחליק אצבע על הקורא)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(או לשים אצבע על הקורא)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "כיבוי"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "כיבוי;סגירה;יציאה;הפעלה מחדש;עצירה;הפסקה"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "הפעלה מחדש"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "הפעלה מחדש; כיבוי והדלקה;הדלקה מחדש;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "נעילת המסך"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "נעילת המסך"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "יציאה"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "יציאה;התנתקות;התנתק;ניתוק;הינתקות;הנתקות"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "השהיה"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "השהיה;שינה;תרדמת"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "החלפת משתמש"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "החלפת משתמש"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "נעילת כיוון;שחרור נעילת כיוון;מסך;סיבוב;כיוון;הטיה;היפוך"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "צילום המסך"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "צילום מסך;תפיסה;הקלטה;סקרין שוט;סקרינשוט;לכידה;חיתוך;תמונה"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "שחרור נעילת סיבוב מסך"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "נעילת סיבוב מסך"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "הפקודה לא נמצאה"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "לא ניתן לפענח את הפקודה:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "ההרצה של „%s” נכשלה:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "לפני רגע"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "לפני דקה"
+msgstr[1] "לפני שתי דקות"
+msgstr[2] "לפני %d דקות"
+msgstr[3] "‫לפני %d דקות"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "לפני שעה"
+msgstr[1] "לפני שעתיים"
+msgstr[2] "‫לפני %d שעות"
+msgstr[3] "‫לפני %d שעות"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "אתמול"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "אתמול"
+msgstr[1] "שלשום"
+msgstr[2] "‫לפני %d ימים"
+msgstr[3] "‫לפני %d ימים"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "לפני שבוע"
+msgstr[1] "לפני שבועיים"
+msgstr[2] "‫לפני %d שבועות"
+msgstr[3] "‫לפני %d שבועות"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "לפני חודש"
+msgstr[1] "לפני חודשיים"
+msgstr[2] "‫לפני %d חודשים"
+msgstr[3] "‫לפני %d חודשים"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "לפני שנה"
+msgstr[1] "לפני שנתיים"
+msgstr[2] "‫לפני %d שנים"
+msgstr[3] "‫לפני %d שנים"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "‎%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "אתמול, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d ב%B, %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d ב%B %Y, %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "אתמול, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l:%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d ב%B, %l:%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d ב%B %Y, %l:%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "התחברות לנקודה חמה"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"ההתחברות שלך לנקודה חמה זו אינה בטוחה. ססמאות ומידע אחר שהזנת עשויים להיות "
+"גלויים לאנשים בקרבת מקום."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "מניעת גישה"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "אפשור גישה"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "תיקייה ללא שם"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "‏%s הוצמד לחלונית היישומים."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "‏%s הוסר מחלונית היישומים."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "חלונות פתוחים"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "חלון חדש"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "הצגת פרטים"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "יציאה"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "ביטול הצמדה"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "הצמדה לחלונית היישומים"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "הפעלה בשימוש כרטיס מסך משולב"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "הפעלה בשימוש כרטיס מסך נפרד"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "בחירת התקן שמע"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "הגדרות שמע"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "אזניות"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "אזניות עם מיקרופון"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "מיקרופון"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "החלפת הרקע…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "הגדרות תצוגה"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "הגדרות"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "65"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "א"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "ב"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "ג"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "ד"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "ה"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "ו"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ש"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "חודש קודם"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "חודש הבא"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "שבוע %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "אין התרעות"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "נא לא להפריע"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "ניקוי"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "‏„%s” לא מגיב."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "באפשרותך להמתין זמן קצר או לכפות על היישום להיסגר לחלוטין."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "לכפות סגירה"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "המתנה"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "חובר כונן חיצוני"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "נותק כונן חיצוני"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "לא ניתן לנעול את העצמה"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "גרסת udisks המותקנת לא תומכת בהגדרת PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "פתיחה באמצעות %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "לחלופין ניתן להתחבר על ידי לחיצה על כפתור ה־„WPS” בנתב שלך."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "התחברות"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "מפתח"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "ססמת מפתח פרטי"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "זהות"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "שירות"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "נדרש אימות"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "ססמאות או מפתחות הצפנה נדרשים כדי לגשת לרשת האלחוטית „%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "אימות Wired 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "שם הרשת"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "אימות DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "נדרש קוד PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "נדרש קוד PIN עבור התקן החיבור האלחוטי בפס רחב"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "נדרשת ססמה כדי להתחבר אל „%s”."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "מנהל הרשתות"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "ססמת VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "נדרש אימות"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "מנהל"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "אימות"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "פעולה זו לא הצליחה, נא לנסות שוב. עמך הסליחה."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "השם של %s הוחלף ל־%s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "חלונות"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "הצגת יישומים"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "חלונית"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d ב%B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e ב%B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d ב%B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d ב%B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "היום"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "מחר"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "יום שלם"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "אין אירועים"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "הוספת שעונים מהעולם…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "שעוני העולם"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "בטעינה…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "יש להתחבר בשביל מידע על מזג האוויר"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "מידע על מזג האוויר לא זמין כרגע"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "מזג אוויר"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "בחירת מיקום למזג אוויר…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "הוצאת %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "יציאה"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "המשתמש %s יצא אוטומטית בעוד שנייה."
+msgstr[1] "המשתמש %s יצא אוטומטית בעוד שתי שניות."
+msgstr[2] "המשתמש %s יצא אוטומטית בעוד %d שניות."
+msgstr[3] "‫המשתמש %s יצא אוטומטית בעוד %d שניות."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "המשתמש שלך יצא אוטומטית מהמערכת בעוד שנייה."
+msgstr[1] "‫המשתמש שלך יצא אוטומטית מהמערכת בעוד שתי שניות."
+msgstr[2] "‫‫המשתמש שלך יצא אוטומטית מהמערכת בעוד %d שניות."
+msgstr[3] "‫‫‫המשתמש שלך יצא אוטומטית מהמערכת בעוד %d שניות."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "יציאה"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "כיבוי"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "התקנת עדכונים וכיבוי"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "המערכת תכבה אוטומטית בעוד שנייה."
+msgstr[1] "המערכת תכבה אוטומטית בעוד שתי שניות."
+msgstr[2] "המערכת תכבה אוטומטית בעוד %d שניות."
+msgstr[3] "‫המערכת תכבה אוטומטית בעוד %d שניות."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "התקנת עדכוני תכנה בהמתנה"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "כיבוי"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "הפעלה מחדש"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "התקנת עדכונים והפעלה מחדש"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "המערכת תופעל אוטומטית מחדש בעוד שנייה."
+msgstr[1] "‫המערכת תופעל אוטומטית מחדש בעוד שתי שניות."
+msgstr[2] "המערכת תופעל אוטומטית מחדש בעוד %d שניות."
+msgstr[3] "‫המערכת תופעל אוטומטית מחדש בעוד %d שניות."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "הפעלה מחדש"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "הפעלה מחדש והתקנת עדכונים"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "המערכת תופעל מחדש ותתקין עדכונים בעוד שנייה."
+msgstr[1] "המערכת תופעל מחדש ותקין עדכונים בעוד שתי שניות."
+msgstr[2] "המערכת תופעל מחדש ותקין עדכונים בעוד %d שניות."
+msgstr[3] "‫המערכת תופעל מחדש ותתקין עדכונים בעוד %d שניות."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "הפעלה מחדש והתקנה"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "התקנה וכיבוי"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "כיבוי לאחר התקנת עדכונים"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "הפעלה מחדש ושדרוג התקנה"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s יותקן לאחר הפעלה מחדש. שדרוג ההתקנה עשוי לקחת זמר רב: יש לוודאשגִבית "
+"ושהמחשב מחובר למקור חשמל."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "פועל על סוללה נמוכה: יש לחבר לחשמל לפני התקנת עדכונים."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "כמה יישומים עסוקים או שיש עבודה שלא נשמרה"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "משתמשים נוספים מחוברים"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "אפשרויות אתחול"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (מרוחק)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (מסוף)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "התקנה"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "התקנת הרחבות"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "הורדה והתקנה של „%s” מ־extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "עדכוני הרחבות זמינים"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "עדכוני הרחבות מוכנים להתקנה."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "לאפשר השבתת צירופי מקשים"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "היישום %s הרוצה למנוע צירופי מקשים"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "יישום הרוצה למנוע צירופי מקשים"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "נתן לשחזר צירופי מקשים בהקשה על %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "לדחות"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "לאפשר"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "מצב מקשים אטיים פועל"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "מצב מקשים אטיים כבוי"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"יש ללחוץ על מקש ה־Shift במשך 8 שניות. זה הקיצור לתכונת המקשים האטיים, "
+"המשפיעה על המקלדת שלך."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "מצב מקשים דביקים פועל"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "מצב מקשים דביקים כבוי"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"יש ללחוץ 5 פעמים ברציפות על מקש ה־Shift. זה הקיצור לתכונת המקשים הדביקים, "
+"המשפיעה על המקלדת שלך."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"יש ללחוץ על שני מקשים בו־זמנית, או ללחוץ 5 פעמים ברציפות על מקש ה־Shift. זה "
+"יכבה את תכונת המקשים הדביקים, המשפיעה על המקלדת שלך."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "להשאיר פועל"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "הפעלה"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "כיבוי"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "להשאיר כבוי"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "הגדרות אזור ושפה"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "לא מותקנות הרחבות"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "‏%s לא העלה שגיאות כלשהן."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "הסתרת השגיאות"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "הצגת השגיאות"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "פעיל"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "מנוטרל"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "שגיאה"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "לא בתוקף"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "בהורדה"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "צפייה במקור"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "דף אינטרנט"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "המערכת הועברה למצב בלתי בטוח"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "ליישומים יש עכשיו גישה בלתי מוגבלת"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "ביטול"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "מחובר כמשתמש בעל הרשאות מיוחדות"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"יש להימנע מהרצת הפעלה כמשתמש בעל הרשאות מיוחדות מסיבות אבטחה. אם אפשרי, יש "
+"להתחבר כמשתמש רגיל."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "מסך נעילה מושבת"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "מסך נעיל דורש הרצת מנהל תצוגה של GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "פרטי המערכת"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "אמן לא ידוע"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "כותרת לא ידועה"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "יש להקליד כדי לחפש"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "יישומים"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "סקירה"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "צירוף מקשים חדש…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "הגדרת יישום"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "הצגת עזרה על המסך"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "החלפת צגים"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "הקצאת מקש"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "בוצע"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "עריכה…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "לא כלום"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "יש ללחוץ על כפתור להגדרה"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "יש להקיש Esc ליציאה"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "יש להקיש על מנת לצאת"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "פעילויות"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "מערכת"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "הסרגל העליון"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "הרצת פקודה"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "יש להקיש Esc לסגירה"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "הפעלה מחדש אינה זמינה תחת Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "מופעל מחדש…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "על GNOME לנעול את המסך"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "לא ניתן לנעול"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "הנעילה נחסמה ע״י היישום"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "בחירה"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "בחירת אזור"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "מסך"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "בחירת מסך"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "חלון"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "בחירת חלון"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "צילום / הקלטת מסך"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "הצגת הסמן"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "הקלטות מסך"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "הקלטת מסך מ־%d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "צילום מסך"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "המסך הוקלט"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "לחיצה כאן מנגנת את הווידאו."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "הצגה בקבצים"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "צילום מסך מ־%s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "המסך צולם"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "אפשר להדביק את התמונה מלוח הגזירים."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "המסך צולם"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "בחיפוש…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "אין תוצאות."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "עוד אחד"
+msgstr[1] "עוד שניים"
+msgstr[2] "עוד %d"
+msgstr[3] "‫עוד %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "חיפוש"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "העתקה"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "הדבקה"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "הצגת טקסט"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "הסתרת טקסט"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "מקש Caps Lock פועל."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "כרך מוסתר"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "מחיצת המערכת"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "משתמש בקובצי מפתחות"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "כדי לשחרר נעילה שמשתמשת בקובצי מפתחות יש להשתמש בכלי <i>%s</i> במקום."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "מספר PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "שמירת הססמה"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "שחרור"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "פתיחה באמצעות %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "ה־PIM חייב להיות ריק או מספר."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "לא ניתן להפעיל את %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "לא ניתן למצוא את היישום %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "נגישות"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "ניגודיות גבוהה"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "תקריב"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "מקריא מסך"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "מקלדת מסך"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "התראות חזותיות"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "מקשים דביקים"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "מקשים אטיים"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "מקשים קופצים"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "מקשי עכבר"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "טקסט גדול"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "סיבוב אוטומטי"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "בלוטות'"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "בהירות"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "מצב כהה"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "לחיצה בודדת"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "לחיצה כפולה"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "גרירה"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "לחיצה משנית"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "לחיצה בהשהיה"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "מקלדת"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "הצגת פריסת המקלדת"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "לאפשר גישה למיקום שלך"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "היישום %s רוצה גישה למיקום שלך"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "ניתן לשנות בכל עת את הרשאות הגישה למיקום שלך מהגדרות הפרטיות."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<לא ידוע>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "ניתוק %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "התחברות אל %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "נקודה חמה %s"
+
+#: js/ui/status/network.js:1464
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1465
+msgid "VPN Settings"
+msgstr "הגדרות VPN"
+
+#: js/ui/status/network.js:1708
+msgid "Wi–Fi"
+msgstr "רשת אלחוטית"
+
+#: js/ui/status/network.js:1710
+msgid "All Networks"
+msgstr "כל הרשתות"
+
+#: js/ui/status/network.js:1807
+msgid "Wired Connections"
+msgstr "התחברויות קוויות"
+
+#: js/ui/status/network.js:1808
+msgid "Wired Settings"
+msgstr "הגדרות רשת קווית"
+
+#: js/ui/status/network.js:1822
+msgid "Bluetooth Tethers"
+msgstr "קישרת בלוטות׳"
+
+#: js/ui/status/network.js:1823
+msgid "Bluetooth Settings"
+msgstr "הגדרות בלוטות׳"
+
+#: js/ui/status/network.js:1837
+msgid "Mobile Connections"
+msgstr "התחברויות ניידות"
+
+#: js/ui/status/network.js:1839
+msgid "Mobile Broadband Settings"
+msgstr "הגדרות פס־רחב נייד"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "תאורת לילה"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "ביצועים"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "מאוזן"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "חיסכון בחשמל"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "פרופילי חשמל"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "עצירת הקלטת המסך"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "עצירת שיתוף מסך"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "מצב טיסה"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "צילום המסך"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "תפריט כיבוי"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "השהיה"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "הפעלה מחדש…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "כיבוי…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "יציאה…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "החלפת משתמש…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "נעילת מסך"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "התקן Thunderbolt לא ידוע"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"התקן חדש זוהה כאשר לא היית בנמצא. נא לנתק ולחבר מחדש את ההתקן על מנת להתחיל "
+"להשתמש בו."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "התקן Thunderbolt לא מאומת"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "התקן חדש זוהה וצריך לעבור אימות על ידי מנהל."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "שיגאת אימות Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "לא ניתן לאמת את התקן ה־Thunderbolt: ‏%s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "עצמת השמע השתנתה"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "עצמה"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "פלט שמע"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "קלט שמע"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "מראה"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "צירוף תצוגות"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "חיצוני בלבד"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "מובנה בלבד"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %d ב%B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "יש להחליק למעלה כדי לשחרר"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "יש ללחוץ או להקיש כדי לשחרר"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "שחרור החלון"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "כניסה בתור משתמש אחר"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "ברוכים הבאים אל ‎GNOME ‏‎%s‎‎"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "אם ברצונך ללמוד ולהכיר את הסביבה שלך, כדאי להעיף מבט בסיור."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "לא תודה"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "אקח את הסיור"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "‏„%s” מוכן"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "האם ברצונך לשמור הגדרות תצוגה אלה?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "שחזור הגדרות"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "שמירת שינויים"
+
+# javascript-format
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "‫שינויי הגדרות ישוחזרו בעוד שנייה"
+msgstr[1] "שינויי הגדרות ישוחזרו בעוד שתי שניות"
+msgstr[2] "שינויי הגדרות ישוחזרו בעוד %d שניות"
+msgstr[3] "‫שינויי הגדרות ישוחזרו בעוד %d שניות"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "הסתרה"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "שחזור"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "הגדלה"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "הזזה"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "שינוי גודל"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "הזזת שורת הכותרת על המסך"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "תמיד עליון"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "הצגה בכל מרחבי העבודה"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "הזזה למרחב העבודה שמשמאל"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "הזזה למרחב העבודה שמימין"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "הזזה למרחב העבודה שמלמעלה"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "הזזה למרחב העבודה שמלמטה"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "הזה לצג שמלמעלה"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "הזזה לצג שמלמטה"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "הזזה לצג שמשמאל"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "הזזה לצג שמימין"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "סגירה"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "יומן אבולושן"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Print version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "המצב בו GDM יעשה שימוש לצורך מסך הכניסה"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "שימוש במצב מסוים, לדוגמה: „gdm“ למסך הכניסה"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "הצגת המצבים האפשריים"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "לא ידוע"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "אירע כשל בטעינת „%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "הססמאות אינן תואמות."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "הססמה אינה יכולה להישאר ריקה"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "המשתמש בחר להתעלם מתיבת דו־שיח האימות"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "הרחבות"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "ניהול הרחבות GNOME שלך"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "מיזם GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"הרחבות GNOME מטפל בעדכון הרחבות, הגדרת אפשרויות של כל הרחבה, הסרה והשבתת "
+"הרחבות לא רצויות."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "הגדרת הרחבות GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "אין התאמות"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "להסיר את „%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr "אם ההרחבה תוסר, על מנת להפעיל אותה שוב עליך יהיה להוריד אותה שוב"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "הסרה"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "יוסף אור בוצ׳קו <yoseforb@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "הרחבה אחת תעודכן בכניסה הבאה למערכת."
+msgstr[1] "שתי הרחבות תעודכנה בכניסה הבאה למערכת."
+msgstr[2] "‏%d הרחבות תעודכנה בכניסה הבאה למערכת."
+msgstr[3] "‫‏%d הרחבות תעודכנה בכניסה הבאה למערכת."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "ההרחבה אינה תואמת לגרסת GNOME הנוכחית"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "ארעה שגיאה להרחבה"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "ההרחבה ניתנת לעדכון"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "אתר אינטרנט"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "הסרה…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "עזרה"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "על אודות הרחבות"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"הרחבות עשויות לגרום לבעיות ביצועים ויציבות. יש להשבית הרחבות במידה ונתקלת "
+"בבעיות במערכת שלך."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "הותקן ידנית"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"על מנת למצוא ולהוסיף הרחבות, יש לבקר באתר <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "מובנה"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "אין אף הרחבה מותקנת"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"אנו מצטערים, אך לא ניתן לקבל את רשימת ההרחבות המותקנות. יש לוודא שהתחברת "
+"לתוך GNOME ולנסות שוב."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "עדכוני הרחבות מוכנים"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "The new extension was successfully created in %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Name"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "תיאור"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Choose one of the available templates:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Template"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "The unique identifier of the new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "The user-visible name of the new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPTION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "A short description of what the extension does"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "TEMPLATE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "The template to use for the new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Enter extension information interactively"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Create a new extension"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Unknown arguments"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, name and description are required"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Failed to connect to GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Extension “%s” does not exist\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Disable an extension"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "No UUID given"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "More than one UUID given"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Enable an extension"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Extension “%s” doesn't exist\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Show extensions info"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Overwrite an existing extension"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Install an extension bundle"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "No extension bundle specified"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "More than one extension bundle specified"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Show user-installed extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Show system-installed extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Show enabled extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Show disabled extensions"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Show extensions with preferences"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Show extensions with updates"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Print extension details"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "הצגת ההרחבות המותקנות"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Additional source to include in the bundle"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "A GSettings schema that should be included"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "The directory where translations are found"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "The gettext domain to use for translations"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Overwrite an existing pack"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "The directory where the pack should be created"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "SOURCE_DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Create an extension bundle"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "צוינה יותר מתיקיית מקור אחת"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "להרחבה „%s” אין העדפות\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "פתיחת ההעדפות של ההרחבה „%s” נכשלה: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Opens extension preferences"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Reset an extension"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Cannot uninstall system extensions\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Failed to uninstall “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Uninstall an extension"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Do not print error messages"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Path"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Original author"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "גרסה"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "State"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” takes no arguments"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Usage:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Print version information and exit."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMMAND"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Commands:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Print help"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Enable extension"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Disable extension"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Reset extension"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Uninstall extension"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "List extensions"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Show extension info"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Open extension preferences"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Create extension"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Package extension"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Install extension bundle"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Use “%s” to get detailed help.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "תבנית"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "הרחבה ריקה"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "מכוון"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "הוספת סמליל לסרגל העליון"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "ערוץ פלט אחד"
+msgstr[1] "שני ערוצי פלט"
+msgstr[2] "‫%u ערוצי פלט"
+msgstr[3] "‏‫%u ערוצי פלט"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "ערוץ קלט אחד"
+msgstr[1] "שני ערוצי קלט"
+msgstr[2] "‫%u ערוצי קלט"
+msgstr[3] "‏‫%u ערוצי קלט"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "צלילי מערכת"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+
+#~ msgid "Log Out"
+#~ msgstr "יציאה"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "התחברות לאינטרנט"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "מצב טיסה מופעל"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "רשת אלחוטית מושבתת כאשר מצב טיסה מופעל."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "כיבוי מצב טיסה"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "רשת אלחוטית כבויה"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "רשת אלחוטית צריכה להיות מופעלת על מנת להתחבר לרשת."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "הפעלת רשת אלחוטית"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "רשתות אלחוטיות"
+
+#~ msgid "Select a network"
+#~ msgstr "בחירת רשת"
+
+#~ msgid "No Networks"
+#~ msgstr "אין רשתות"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "שימוש במתג של החומרה לכיבוי"
+
+#~ msgid "Select Network"
+#~ msgstr "בחירת רשת"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "הגדרות רשת אלחוטית"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN כבוי"
+
+#~ msgid "Network Settings"
+#~ msgstr "הגדרות הרשת"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "‏%s התחברות אלחוטית"
+#~ msgstr[1] "שתי התחברויות אלחוטיות"
+#~ msgstr[2] "‏%s התחברויות אלחוטיות"
+#~ msgstr[3] "‫‏%s התחברויות אלחוטיות"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "התחברות קווית %s"
+#~ msgstr[1] "התחברויות קוויות %s"
+#~ msgstr[2] "התחברויות קוויות %s"
+#~ msgstr[3] "התחברויות קויות %s"
+
+#, javascript-format
+#~| msgid "%s Wired Connection"
+#~| msgid_plural "%s Wired Connections"
+#~ msgid "%s Bluetooth Connection"
+#~ msgid_plural "%s Bluetooth Connections"
+#~ msgstr[0] "התחברות קווית %s"
+#~ msgstr[1] "התחברויות קוויות %s"
+#~ msgstr[2] "התחברויות קוויות %s"
+#~ msgstr[3] "התחברויות קוויות %s"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "התחברות מודם %s"
+#~ msgstr[1] "התחברויות מודם %s"
+#~ msgstr[2] "‫התחברויות מודם %s"
+#~ msgstr[3] "‫התחברויות מודם %s"
+
+#~ msgid "Connection failed"
+#~ msgstr "ההתחברות נכשלה"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "הפעלת חיבור הרשת נכשלה"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "‏%s כבוי"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "‏%s מחובר"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "‏%s לא מנוהל"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "‏%s בהתחברות"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "‏%s דורש אימות"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "חסרה קושחה עבור %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "‏%s לא זמין"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "נכשלה ההתחברות אל %s"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "‏%s מושבת חומרתית"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "‏%s מושבת"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s לא מחובר"
+
+#~ msgid "unknown"
+#~ msgstr "לא ידוע"
+
+#~ msgid "activating…"
+#~ msgstr "מפעיל…"
+
+#~ msgid "deactivating…"
+#~ msgstr "משבית…"
+
+#~ msgid "deactivated"
+#~ msgstr "מושבת"
+
+#~ msgid "connecting…"
+#~ msgstr "בהתחברות…"
+
+#~ msgid "authentication required"
+#~ msgstr "נדרש אימות"
+
+#~ msgid "connection failed"
+#~ msgstr "ההתחברות נכשלה"
+
+#~ msgid "Power Settings"
+#~ msgstr "הגדרות צריכת החשמל"
+
+#~ msgid "Lock"
+#~ msgstr "נעילה"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "כיבוי / יציאה"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "אחד מחובר"
+#~ msgstr[1] "‫שניים מחוברים"
+#~ msgstr[2] "‫%d מחוברים"
+#~ msgstr[3] "‫‫%d מחוברים"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "בלוטות' פועל"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "בלוטות' כבוי"
+
+#~ msgid "Location Enabled"
+#~ msgstr "איכון מאופשר"
+
+#~ msgid "Disable"
+#~ msgstr "השבתה"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "הגדרות פרטיות"
+
+#~ msgid "Location In Use"
+#~ msgstr "איכון בשימוש"
+
+#~ msgid "Location Disabled"
+#~ msgstr "איכון מושבת"
+
+#~ msgid "Enable"
+#~ msgstr "אפשור"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "תאורת לילה מושבתת"
+
+#~ msgid "Resume"
+#~ msgstr "המשך"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "להשבית עד מחר"
+
+#~ msgid "Fully Charged"
+#~ msgstr "בטעינה מלאה"
+
+#~ msgid "Not Charging"
+#~ msgstr "לא בטעינה"
+
+#~ msgid "Estimating…"
+#~ msgstr "מתבצע שערוך…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "‏‎‎%d∶%02d נותרו (%d%%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d:%02d עד לטעינה מלאה (%d%%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "המסך משותף"
+
+#~ msgid "Turn off"
+#~ msgstr "כיבוי"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "הצגת מנשק לכידת מסך"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "הסרה מהמועדפים"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "הוספה למועדפים"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "המסך מוקלט"
+
+#~ msgid "Stop"
+#~ msgstr "עצירה"
+
+#~ msgid "Author"
+#~ msgstr "מחבר"
+
+#~ msgid "Warning"
+#~ msgstr "אזהרה"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "הרחבות עשויות לגרום לבעיות במערכת, לרבות בעיות ביצועים. אם מצאת בעיות "
+#~ "במערכת שלך, מומלץ להשבית את כל ההרחבות."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Failed to connect to GNOME Shell"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Enable introspection API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+
+#~ msgid "Minimize"
+#~ msgstr "מזעור"
+
+#~ msgid "Unmaximize"
+#~ msgstr "ביטול הגדלה"
+
+#~ msgid "App Picker View"
+#~ msgstr "App Picker View"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Index of the currently selected view in the application picker."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "יישומים בשימוש תדיר יופיעו כאן"
+
+#~ msgid "Frequent"
+#~ msgstr "תדיר"
+
+#~ msgid "All"
+#~ msgstr "הכול"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, ה־%-d ב%B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, ה־%-d ב%B, %Y"
+
+#~ msgid "Off"
+#~ msgstr "כבוי"
+
+#~ msgid "On"
+#~ msgstr "פועל"
+
+#~| msgid "Show Errors"
+#~ msgid "Copy Error"
+#~ msgstr "שגיאת העתקה"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "אירעה שגיאה בעת טעינת דו־שיח ההעדפות עבור %s:"
+
+#~ msgid "Next"
+#~ msgstr "הבא"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "כניסה"
+
+#~ msgid "Password:"
+#~ msgstr "ססמה:"
+
+#~ msgid "Type again:"
+#~ msgstr "נא להקליד שוב:"
+
+#~ msgid "Password: "
+#~ msgstr "ססמה:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "הרשת האלחוטית דורשת אימות"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "ססמת רשת הפס הרחב הניידת"
+
+#~ msgid "%s all day."
+#~ msgstr "%s לאורך היום"
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, ומאוחר יותר %s"
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, לאחר מכן %s וממשיך אל %s מאוחר יותר."
+
+#~ msgid "Feels like %s."
+#~ msgstr "מרגיש כמו %s."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+# javascript-format
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "הודעה חדשה אחת"
+#~ msgstr[1] "%d הודעות חדשות"
+#~ msgstr[2] "שתי הודעות חדשות"
+
+# javascript-format
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "התרעה חדשה אחת"
+#~ msgstr[1] "%d התרעות חדשות"
+#~ msgstr[2] "שתי התרעות חדשות"
+
+#~ msgid "Account Settings"
+#~ msgstr "הגדרות חשבון"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "נעילת כיוון"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Which keyboard to use"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "The type of keyboard to use."
+
+#~| msgid "Network error"
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "אימות ניתוב הרשת"
+
+#~ msgid "Events"
+#~ msgstr "אירועים"
+
+#~ msgid "Notifications"
+#~ msgstr "התרעות"
+
+#~ msgid "Hide tray"
+#~ msgstr "הסתרת המגש"
+
+#~ msgid "Status Icons"
+#~ msgstr "סמלי מצב"
+
+#~ msgid "Media"
+#~ msgstr "מדיה"
+
+#~ msgid "Not In Use"
+#~ msgstr "לא בשימוש"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Show the week date in the calendar"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "If true, display the ISO week date in the calendar."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "שימוש כחיבור לאינטרנט"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "‏%s דורש גישה למיקום שלך."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "‏GNOME Shell (מסדר wayland)"
+
+# javascript-format
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "התקן אחד מחובר"
+#~ msgstr[1] "%d התקנים מחוברים"
+
+#~ msgid "UPS"
+#~ msgstr "אל־פסק"
+
+#~ msgid "Battery"
+#~ msgstr "סוללה"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "‎%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "Invitation"
+#~ msgstr "הזמנה"
+
+#~ msgid "Call"
+#~ msgstr "התקשרות"
+
+#~ msgid "File Transfer"
+#~ msgstr "העברת קבצים"
+
+#~ msgid "Chat"
+#~ msgstr "שיחה"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "הזמנה ל־%s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "הוזמנת על ידי %s להצטרף אל %s"
+
+#~ msgid "Decline"
+#~ msgstr "דחייה"
+
+#~ msgid "Accept"
+#~ msgstr "אישור"
+
+#~ msgid "Video call from %s"
+#~ msgstr "שיחת וידאו מאת %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "שיחה מאת %s"
+
+#~ msgid "Answer"
+#~ msgstr "מענה"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "‏%s שולח/ת אליך %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "‏%s רוצה לקבל הרשאות כדי לראות מתי מצבך הוא מקוון"
+
+#~ msgid "Authentication failed"
+#~ msgstr "האימות נכשל"
+
+#~ msgid "Encryption error"
+#~ msgstr "שגיאת הצפנה"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "לא סופק אישור"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "האישור אינו מהימן"
+
+#~ msgid "Certificate expired"
+#~ msgstr "האישור פג"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "האישור לא מופעל"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "שם המארח באישור אינו תואם"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "חוסר התאמה בטביעת האצבע של האישור"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "אישור בחתימה עצמית"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "המצב הוגדר ל'מנותק'"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "האישור אינו תקף"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "ההתחברות נשללה"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "לא ניתן להפעיל את החיבור"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "החיבור אבד"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "חשבון זה כבר מחובר לשרת"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "החיבור הוחלף בחיבור חדש באמצעות אותו המשאב"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "החשבון כבר קיים בשרת"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "השרת עסוק ברגע זה מכדי לטפל בחיבור"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "האישור נשלל"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "האישור משתמש באלגוריתם הצפנה שאינו בטוח או חלש מבחינת הצפנה"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "אורך אישור השרת, או עומק שרשרת אישור השרת, חורגים מהמגבלות שנכפו על ידי "
+#~ "ספריית ההצפנה"
+
+#~ msgid "Internal error"
+#~ msgstr "שגיאה פנימית"
+
+#~ msgid "View account"
+#~ msgstr "צפייה בחשבון"
+
+#~ msgid "Show the message list"
+#~ msgstr "הצגת רשימת הודעות"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "א׳"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "ב׳"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "ג׳"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "ד׳"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "ה"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "ו׳"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "ש׳"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "היומן ריק"
+
+#~ msgid "This week"
+#~ msgstr "השבוע"
+
+#~ msgid "Next week"
+#~ msgstr "בשבוע הבא"
+
+#~ msgid "Removable Devices"
+#~ msgstr "התקנים נשלפים"
+
+#~ msgid "Eject"
+#~ msgstr "שליפה"
+
+#~ msgid "Unmute"
+#~ msgstr "ביטול ההשתקה"
+
+#~ msgid "Mute"
+#~ msgstr "השתקה"
+
+#~ msgid "Open Calendar"
+#~ msgstr "פתיחת היומן"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "הגדרות תאריך ושעה"
+
+#~ msgid "Open"
+#~ msgstr "פתיחה"
+
+#~ msgid "Clear Messages"
+#~ msgstr "פינוי ההודעות"
+
+#~ msgid "Notification Settings"
+#~ msgstr "הגדרות התרעה"
+
+#~ msgid "Tray Menu"
+#~ msgstr "תפריט מגש"
+
+#~ msgid "No Messages"
+#~ msgstr "אין הודעות"
+
+#~ msgid "Message Tray"
+#~ msgstr "אזור דיווח ההודעות"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Captive Portal"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "The maximum accuracy level of location."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Arrangement of buttons on the titlebar"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "יש לבחור את ההרחבה להגדרה באמצעות תיבת הבחירה המשולבת שלהלן."
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:YM"
+
+#~ msgid "unavailable"
+#~ msgstr "לא זמין"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgid "%A, %H\\u2236%M"
+#~ msgstr "%A, %H\\u2236%M"
+
+#~ msgid "%B %d, %H\\u2236%M"
+#~ msgstr "%d ב%B, %H\\u2236%M"
+
+#~ msgid "%B %d %Y, %H\\u2236%M"
+#~ msgstr "%d ב%B %Y, %H\\u2236%M"
+
+#~ msgid "%l\\u2236%M %p"
+#~ msgstr "%l\\u2236%M %p"
+
+#~ msgid "%A, %l\\u2236%M %p"
+#~ msgstr "%A, %l\\u2236%M %p"
+
+#~ msgid "%B %d, %l\\u2236%M %p"
+#~ msgstr "%d ב%B, %l\\u2236%M %p"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b> ב־<b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "ה־<b>%d</b> ב<b>%B</b> ב־<b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "ה־<b>%d</b> ב<b>%B</b> <b>%Y</b> ב־<b>%H:%M</b>"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "List of categories that should be displayed as folders"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "בקשת אישור מאת %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "ההתקן %s מעוניין בצימוד עם מחשב זה"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "ההתקן %s מעוניין לגשת אל השירות '%s'"
+
+#~ msgid "Grant this time only"
+#~ msgstr "הענקת גישה הפעם בלבד"
+
+#~ msgid "Reject"
+#~ msgstr "סירוב"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "אישור צימוד עבור %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "נא לאשר האם מילת הצופן '%06d' תואמת את זו שמופיעה בהתקן."
+
+#~ msgid "Does not match"
+#~ msgstr "אינו תואם"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "בקשת צימוד עבור %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "נא להזין את קוד ה־PIN המוזכר בהתקן."
+
+#~ msgid "OK"
+#~ msgstr "אישור"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "אין משפטי חכמה עבורך להיום, צר לי:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "‏%s, כה אמרה האוראקל"
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Keybinding to toggle the screen recorder"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Keybinding to start/stop the builtin screen recorder."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Framerate used for recording screencasts."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "The gstreamer pipeline used to encode the screencast"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "File extension used for storing the screencast"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+
+#~ msgid "Power"
+#~ msgstr "צריכת חשמל"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "יש ללחוץ על יציאה כדי לסגור יישומים אלה ולצאת מהמערכת."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "מתבצעת יציאה מהמערכת."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "יש ללחוץ על כיבוי כדי לסגור יישומים אלה ולכבות את המערכת."
+
+#~ msgid "Powering off the system."
+#~ msgstr "המערכת נכבית."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "יש ללחוץ על יציאה כדי לסגור יישומים אלה ולהפעיל את המערכת מחדש."
+
+#~ msgid "Restarting the system."
+#~ msgstr "המערכת מופעלת מחדש"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "עצמת שמע, רשת, סוללה"
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "כיבוי המערכת עלול לגרום להם לאיבוד עבודה שלא נשמרה."
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Whether to collect stats about applications usage"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "הגדרות גישה אוניברסלית"
+
+#~ msgid "Visibility"
+#~ msgstr "הצגה"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "הגדרת התקן חדש…"
+
+#~ msgid "Send Files…"
+#~ msgstr "שליחת קבצים…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "הגדרות מקלדת"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "הגדרות עכבר"
+
+#~ msgid "disabled"
+#~ msgstr "מנוטרל"
+
+#~ msgid "cable unplugged"
+#~ msgstr "הכבל מנותק"
+
+#~ msgid "More…"
+#~ msgstr "עוד…"
+
+#~ msgid "Wired"
+#~ msgstr "קווי"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "אתרנט אוטומטי"
+
+#~ msgid "Auto broadband"
+#~ msgstr "פס רחב אוטומטי"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "חיוג אוטומטי"
+
+#~ msgid "Auto %s"
+#~ msgstr "‏%s אוטומטי"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Bluetooth אוטומטי"
+
+#~ msgid "Auto wireless"
+#~ msgstr "אלחוטי אוטומטי"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "רשת אלחוטית"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s נותרו"
+
+#~ msgid "AC Adapter"
+#~ msgstr "מתאם חשמל"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "סוללת מחשב נייד"
+
+#~ msgid "Monitor"
+#~ msgstr "צג"
+
+#~ msgid "Mouse"
+#~ msgstr "עכבר"
+
+#~ msgid "PDA"
+#~ msgstr "מחשב כף יד"
+
+#~ msgid "Cell Phone"
+#~ msgstr "טלפון סלולרי"
+
+#~ msgid "Media Player"
+#~ msgstr "נגן מדיה"
+
+#~ msgid "Tablet"
+#~ msgstr "טבלת שליטה"
+
+#~ msgid "Computer"
+#~ msgstr "מחשב"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "לא ידוע"
+
+#~ msgid "Available"
+#~ msgstr "זמין"
+
+#~ msgid "Busy"
+#~ msgstr "עסוק"
+
+#~ msgid "Invisible"
+#~ msgstr "בלתי נראה"
+
+#~ msgid "Away"
+#~ msgstr "מרוחק"
+
+#~ msgid "Idle"
+#~ msgstr "בהמתנה"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "מצב הצ׳אט שלך יוגדר ל'עסוק'"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "ההתרעות כבויות כעת, לרבות הודעות צ׳אט. המצב המקוון שלך הותאם כדי לבשר "
+#~ "לאחרים שיתכן שהודעותיהם לא יתקבלו באופן מיידי."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "כניסה"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "tray"
+#~ msgstr "מגש מערכת"
+
+#~ msgid "More..."
+#~ msgstr "עוד..."
+
+#~ msgid "United Kingdom"
+#~ msgstr "בריטניה"
+
+#~ msgid "Default"
+#~ msgstr "בררת מחדל"
+
+#~ msgid "Show full name in the user menu"
+#~ msgstr "Show full name in the user menu"
+
+#~ msgid "Whether the users full name is shown in the user menu or not."
+#~ msgstr "Whether the users full name is shown in the user menu or not."
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "יישומים"
+
+#~ msgid "SETTINGS"
+#~ msgstr "הגדרות"
+
+#~ msgid "Subscription request"
+#~ msgstr "בקשת הרשמה"
+
+#~ msgid "<b>%H:%M</b> on Yesterday"
+#~ msgstr "אתמול ב־<b>%H:%M</b>"
+
+#~ msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
+#~ msgstr "<b>%A</b>, ה־<b>%d</b> ב<b>%B</b>, %Y בשעה <b>%H:%M</b>"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "החיבור אל %s נכשל"
+
+#~ msgid "Reconnect"
+#~ msgstr "התחברות מחדש"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "ביצת הפסחא האהובה עליך"
+
+#~ msgid "Browse Files..."
+#~ msgstr "עיון בקבצים..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "שגיאה בעיון בהתקן"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "לא ניתן לעיין בהתקן הנבחר, השגיאה היא '%s'"
+
+#~ msgid "Wireless"
+#~ msgstr "אלחוטי"
+
+#~ msgid "VPN Connections"
+#~ msgstr "חיבורי VPN"
+
+#~ msgid "System Settings"
+#~ msgstr "הגדרות המערכת"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "disabled OpenSearch providers"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "אירע כשל בניתוק '%s'"
+
+#~ msgid "Retry"
+#~ msgstr "ניסיון חוזר"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "מקומות והתקנים"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Passphrase"
+#~ msgstr "מילת צופן"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Show time with seconds"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "If true, display seconds in time."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Show date in clock"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "If true, display date in the clock, in addition to time."
+
+#~ msgctxt "contact"
+#~ msgid "Unknown"
+#~ msgstr "לא ידוע"
+
+#~ msgid "CONTACTS"
+#~ msgstr "אנשי קשר"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "ססמה שגויה, נא לנסות שוב"
+
+#~ msgid "%s is online."
+#~ msgstr "‏%s התחבר/ה."
+
+#~ msgid "%s is offline."
+#~ msgstr "‏%s התנתק/ה."
+
+#~ msgid "%s is away."
+#~ msgstr "‏'%s' מרוחק/ת."
+
+#~ msgid "%s is busy."
+#~ msgstr "‏%s עסוק/ה."
+
+#~ msgid "Hidden"
+#~ msgstr "מוסתר"
+
+#~ msgid "Power Off..."
+#~ msgstr "כיבוי..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "חשבונות מקוונים"
+
+#~ msgid "Log Out..."
+#~ msgstr "ניתוק..."
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "פריטים אחרונים"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should be loaded. disabled-extensions overrides this setting for "
+#~ "extensions that appear in both lists."
+#~ msgstr ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should be loaded. disabled-extensions overrides this setting for "
+#~ "extensions that appear in both lists."
+
+#~ msgid "Show password"
+#~ msgstr "הצגת ססמה"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "‏%s סיים את תהליך ההתחלה"
+
+#~ msgid "Uuids of extensions to disable"
+#~ msgstr "Uuids of extensions to disable"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "כרגע ישנו חיבור בינך ובין רשת הפס הרחב הניידת '%s'"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "כרגע ישנו חיבור בינך ובין הרשת האלחוטית '%s'"
+
+#~ msgid "You're now connected to wired network '%s'"
+#~ msgstr "כרגע ישנו חיבור בינך ובין הרשת הקווית '%s'"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "כרגע ישנו חיבור בינך ובין רשת ה־VPN‏ '%s'"
+
+#~ msgid "calendar:week_start:0"
+#~ msgstr "calendar:week_start:0"
+
+#~ msgid "Localization Settings"
+#~ msgstr "הגדרות אזוריות"
+
+#~ msgid "Shut Down"
+#~ msgstr "כיבוי"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr "יש ללחוץ על יציאה כדי לסגור יישומים אלה ולכבות את המערכת."
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "המערכת תכבה אוטומטית בעוד %d שניות."
+
+#~ msgid "Shutting down the system."
+#~ msgstr "המערכת נכבית."
+
+#~ msgid "Confirm"
+#~ msgstr "אישור"
+
+#~ msgid "Panel"
+#~ msgstr "לוח"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "העדפות"
+
+#~ msgid "Shut Down..."
+#~ msgstr "כיבוי..."
+
+#~ msgid "Clip the crosshairs at the center"
+#~ msgstr "Clip the crosshairs at the center"
+
+#~ msgid "Color of the crosshairs"
+#~ msgstr "Color of the crosshairs"
+
+#~ msgid ""
+#~ "Determines the length of the vertical and horizontal lines that make up "
+#~ "the crosshairs."
+#~ msgstr ""
+#~ "Determines the length of the vertical and horizontal lines that make up "
+#~ "the crosshairs."
+
+#~ msgid ""
+#~ "Determines the position of the magnified mouse image within the magnified "
+#~ "view and how it reacts to system mouse movement. The values are - none: "
+#~ "no mouse tracking; - centered: the mouse image is displayed at the center "
+#~ "of the zoom region (which also represents the point under the system "
+#~ "mouse) and the magnified contents are scrolled as the system mouse moves; "
+#~ "- proportional: the position of the magnified mouse in the zoom region is "
+#~ "proportionally the same as the position of the system mouse on screen; - "
+#~ "push: when the magnified mouse intersects a boundary of the zoom region, "
+#~ "the contents are scrolled into view."
+#~ msgstr ""
+#~ "Determines the position of the magnified mouse image within the magnified "
+#~ "view and how it reacts to system mouse movement. The values are - none: "
+#~ "no mouse tracking; - centered: the mouse image is displayed at the center "
+#~ "of the zoom region (which also represents the point under the system "
+#~ "mouse) and the magnified contents are scrolled as the system mouse moves; "
+#~ "- proportional: the position of the magnified mouse in the zoom region is "
+#~ "proportionally the same as the position of the system mouse on screen; - "
+#~ "push: when the magnified mouse intersects a boundary of the zoom region, "
+#~ "the contents are scrolled into view."
+
+#~ msgid ""
+#~ "Determines the transparency of the crosshairs, from fully opaque to fully "
+#~ "transparent."
+#~ msgstr ""
+#~ "Determines the transparency of the crosshairs, from fully opaque to fully "
+#~ "transparent."
+
+#~ msgid ""
+#~ "Determines whether the crosshairs intersect the magnified mouse sprite, "
+#~ "or are clipped such that the ends of the horizontal and vertical lines "
+#~ "surround the mouse image."
+#~ msgstr ""
+#~ "Determines whether the crosshairs intersect the magnified mouse sprite, "
+#~ "or are clipped such that the ends of the horizontal and vertical lines "
+#~ "surround the mouse image."
+
+#~ msgid "Enable lens mode"
+#~ msgstr "Enable lens mode"
+
+#~ msgid ""
+#~ "Enables/disables display of crosshairs centered on the magnified mouse "
+#~ "sprite."
+#~ msgstr ""
+#~ "Enables/disables display of crosshairs centered on the magnified mouse "
+#~ "sprite."
+
+#~ msgid ""
+#~ "For centered mouse tracking, when the system pointer is at or near the "
+#~ "edge of the screen, the magnified contents continue to scroll such that "
+#~ "the screen edge moves into the magnified view."
+#~ msgstr ""
+#~ "For centered mouse tracking, when the system pointer is at or near the "
+#~ "edge of the screen, the magnified contents continue to scroll such that "
+#~ "the screen edge moves into the magnified view."
+
+#~ msgid "Length of the crosshairs"
+#~ msgstr "Length of the crosshairs"
+
+#~ msgid "Magnification factor"
+#~ msgstr "Magnification factor"
+
+#~ msgid "Mouse Tracking Mode"
+#~ msgstr "Mouse Tracking Mode"
+
+#~ msgid "Opacity of the crosshairs"
+#~ msgstr "Opacity of the crosshairs"
+
+#~ msgid "Scroll magnified contents beyond the edges of the desktop"
+#~ msgstr "Scroll magnified contents beyond the edges of the desktop"
+
+#~ msgid "Show or hide crosshairs"
+#~ msgstr "Show or hide crosshairs"
+
+#~ msgid "Show or hide the magnifier"
+#~ msgstr "Show or hide the magnifier"
+
+#~ msgid "Show or hide the magnifier and all of its zoom regions."
+#~ msgstr "Show or hide the magnifier and all of its zoom regions."
+
+#~ msgid ""
+#~ "The color of the the vertical and horizontal lines that make up the "
+#~ "crosshairs."
+#~ msgstr ""
+#~ "The color of the the vertical and horizontal lines that make up the "
+#~ "crosshairs."
+
+#~ msgid ""
+#~ "The magnified view either fills the entire screen, or occupies the top-"
+#~ "half, bottom-half, left-half, or right-half of the screen."
+#~ msgstr ""
+#~ "The magnified view either fills the entire screen, or occupies the top-"
+#~ "half, bottom-half, left-half, or right-half of the screen."
+
+#~ msgid ""
+#~ "The power of the magnification. A value of 1.0 means no magnification. A "
+#~ "value of 2.0 doubles the size."
+#~ msgstr ""
+#~ "The power of the magnification. A value of 1.0 means no magnification. A "
+#~ "value of 2.0 doubles the size."
+
+#~ msgid "Thickness of the crosshairs"
+#~ msgstr "Thickness of the crosshairs"
+
+#~ msgid ""
+#~ "Whether the magnified view should be centered over the location of the "
+#~ "system mouse and move with it."
+#~ msgstr ""
+#~ "Whether the magnified view should be centered over the location of the "
+#~ "system mouse and move with it."
+
+#~ msgid ""
+#~ "Width of the vertical and horizontal lines that make up the crosshairs."
+#~ msgstr ""
+#~ "Width of the vertical and horizontal lines that make up the crosshairs."
+
+#~ msgid "Bluetooth Agent"
+#~ msgstr "סוכן Bluetooth"
+
+#~ msgid "Search your computer"
+#~ msgstr "חיפוש במחשב שלך"
+
+#~ msgid ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+#~ msgstr "לא ניתן להוסיף מרחבי עבודה כיוון שהם ממלאים את המכסה המרבית."
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "לא ניתן להסיר את מרחב העבודה הראשון."
+
+#~ msgid "Customize the panel clock"
+#~ msgstr "התאמת לוח השעון"
+
+#~ msgid "Custom format of the clock"
+#~ msgstr "Custom format of the clock"
+
+#~ msgid "Hour format"
+#~ msgstr "Hour format"
+
+#~ msgid ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+#~ msgstr ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+
+#~ msgid ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+#~ msgstr ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+
+#~ msgid ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+#~ msgstr ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+
+#~ msgid "Clock Format"
+#~ msgstr "מבנה השעון"
+
+#~ msgid "Clock Preferences"
+#~ msgstr "העדפות השעון"
+
+#~ msgid "Show seco_nds"
+#~ msgstr "הצגת ש_ניות"
+
+#~ msgid "_12 hour format"
+#~ msgstr "מבנה _12 שעות"
+
+#~ msgid "_24 hour format"
+#~ msgstr "מבנה _24 שעות"
+
+#~ msgid "Preferences"
+#~ msgstr "העדפות"
+
+#~ msgid "What's using power..."
+#~ msgstr "מה צורך חשמל..."
+
+#~ msgid "Overview workspace view mode"
+#~ msgstr "Overview workspace view mode"
+
+#~ msgid ""
+#~ "The selected workspace view mode in the overview. Supported values are "
+#~ "\"single\" and \"grid\"."
+#~ msgstr ""
+#~ "The selected workspace view mode in the overview. Supported values are "
+#~ "\"single\" and \"grid\"."
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "יש לגרור פריטים לכאן כדי להוסיף מועדפים"
+
+#~ msgid "Find"
+#~ msgstr "חיפוש"
+
+#~ msgid "ON"
+#~ msgstr "1"
+
+#~ msgid "OFF"
+#~ msgstr "0"
+
+#~ msgid "PLACES"
+#~ msgstr "מקומות"
+
+#~ msgid "SEARCH RESULTS"
+#~ msgstr "תוצאות חיפוש"
+
+#~ msgid "Recent Documents"
+#~ msgstr "מסמכים אחרונים"
+
+#~ msgid "Can't lock screen: %s"
+#~ msgstr "לא ניתן לנעול את המסך: %s"
+
+#~ msgid "Can't temporarily set screensaver to blank screen: %s"
+#~ msgstr "לא ניתן זמנית לקבוע שומר מסך כמסך שחור: %s"
+
+#~ msgid "Can't logout: %s"
+#~ msgstr "לא ניתן להתנתק: %s"
+
+#~ msgid "Sidebar"
+#~ msgstr "סרגל צד"
diff --git a/po/hi.po b/po/hi.po
new file mode 100644
index 0000000..b298ce4
--- /dev/null
+++ b/po/hi.po
@@ -0,0 +1,2381 @@
+# translation of gnome-shell.master.hi.po to Hindi
+# Hindi translation for gnome-shell.
+# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Rajesh <rranjan>, 2011.
+# rajesh <rajeshkajha@yahoo.com>, 2012, 2013, 2014.
+# chandankumar <chandankumar.093047@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell.master.hi\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-09-19 19:40+0000\n"
+"PO-Revision-Date: 2014-09-23 15:37+0630\n"
+"Last-Translator: rajesh <rajesh>\n"
+"Language-Team: Hindi <kde-i18n-doc@kde.org>\n"
+"Language: hi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.5\n"
+"X-DamnedLies-Scope: partial\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "तंत्र"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "संदेश तश्तरी दिखाएँ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "सक्रिय अधिसूचना पर फोकस करें"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "सारांश दिखाएँ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "सभी अनुप्रयोग दिखाएँ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "अनुप्रयोग मेन्यू खोलें"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "गनोम शेल"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "विंडो प्रबंधन और अनुप्रयोग लॉन्चिंग"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "गनोम शेल एक्सटेंशन वरीयताएँ"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "गनोम शेल एक्सटेंशन विन्यस्त करें"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "गनोम शेल (wayland compositor)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Alt-F2 से डेवलपर्स और परीक्षकों के लिए उपयोगी आंतरिक उपकरण को सक्षम करें"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 संवाद का उपयोग करके आंतरिक डिबगिंग और निगरानी उपकरण तक पहुँच पायें."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "एक्सटेंशन का UUIDs सक्रिय किया जाना है"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"गनोम शैल एक्सटेंशन का एक uuid गुण है, इस कुंजी एक्सटेंशन जो लोड किया जाना "
+"चाहिए की सूची "
+"दिखाता हैं. किसी भी विस्तारक जिसे लोड करने के लिए चाहता है को इस सूची में "
+"होने की जरूरत "
+"है. आप भी इस सूची में EnableExtension और DisableExtension डिबस तरीकों के "
+"साथ org."
+"gnome.Shell पर हेरफेर कर सकते हैं."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "विस्तार संस्करण संगतता के वैधीकरण को निष्क्रिय करता है"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"गनोम शेल केवल वही विस्तार लोड करेगा जो मौजूदा कार्यशील संस्करण के समर्थन का "
+"दावा करता है. इस विकल्प का सक्रिय करना इस चेक को निष्क्रिय करेगा और सभी "
+"विस्तार को लोड करने की कोशिश करेगा उन संस्करण से अलग जिसके समर्थन का वे दावा "
+"करते हैं."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "पसंदीदा अनुप्रयोगों के लिए डेस्कटॉप फ़ाइल आईडी की सूची."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"इन पहचानकर्ता के लिए अनुप्रयोगों को पसंदीदा क्षेत्र में प्रदर्शित किया जाएगा."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "एप्प पिकर दृश्य"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "अनुप्रयोग पिकर में मौजूदा चयनित दृश्य की सूची."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "कमांड (Alt-F2) के लिए इतिहास संवाद "
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "ग्लास खोज संवाद के लिए इतिहास"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "हमेशा 'लॉग आउट' मेन्यू मद को उपयोक्ता मेन्यू में दिखाएँ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"यह कुँजी 'लॉग आउट' मेन्यू मद का एकल उपयोक्ता, एकल सत्र स्थिति में स्वचालित "
+"छिपाने को "
+"अधिरोहित करती है."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "गोपित या दूरस्थ फ़ाइलतंत्र के लिए कूटशब्द क्या याद रखने हैं"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"यह शेल किसी कूटशब्द का निवेदन करेगा जब कोई गोपित युक्ति या दूरस्थ फ़ाइलतंत्र "
+"आरोहित किया "
+"जाता है. यदि कूटशब्द को भविष्य के उपयोग के लिए सहेजा जाता है तो 'कूटशब्द याद "
+"रखें' "
+"जाँचपेटी मौजूद रहेगा. यह कुँजी जाँचपेटी की तयशुदा स्थिति को सेट करता है."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "अगर सही है, पंचांग में सप्ताह संख्या दिखाएँ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "अगर सही है, तो पंचांग में ISO सप्ताह तिथि दिखाएँ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "अनुप्रयोग मेनू को खोलने के लिए कीबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "अनुप्रयोग मेनू को खोलने के लिए कीबाइंडिंग."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"अनुप्रयोग दिखाएँ\" दृश्य खोलने के लिए खोलने के लिए कीबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"गतिविधि परिदृश्य के \"अनुप्रयोग दिखाएँ\" दृश्य खोलने के लिए खोलने के लिए "
+"कीबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+#| msgid "Keybinding to open the \"Show Applications\" view"
+msgid "Keybinding to open the overview"
+msgstr "सारांश खोलने के लिए कीबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+#| msgid "Keybinding to open the \"Show Applications\" view"
+msgid "Keybinding to open the Activities Overview."
+msgstr "गतिविधि सारांश खोलने के लिए कीबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "संदेश ट्रे की दृश्यता को टॉगल करने के लिए कीबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "संदेश ट्रे की दृश्यता को टॉगल करने के लिए कीबाइंडिंग."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "सक्रिय अधिसूचना फोकस करने के लिए कीबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "सक्रिय अधिसूचना फोकस करने के लिए कीबाइंडिंग."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"डिबगिंग उद्देश्य के लिए कीबाइंडिंग जो सभी कार्यशील ट्विन्स को रोकता है और "
+"पुनर्बहाल करता है"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "उपयोग करने के लिए कुंजीपटल"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "उपयोग करने के लिए कीबोर्ड के प्रकार"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "मौजूदा कार्यस्थान में स्विचर सीमित करें."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"यदि सही है, मौजूदा कार्यस्थान पर विंडोयुक्त अनुप्रयोग इस स्विचर में दिखेगा. "
+"अन्यथा, सभी अनुप्रयोग शामिल किए जाते हैं."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "अनुप्रयोग चिह्न अवस्था."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"विन्यस्त करें कि कैसे विंडोज स्विचर में दिखाए जाते हैं. मान्य संभावनाएँ हैं "
+"'लघुचित्र केवल' (विंडो "
+"का लघुचित्र दिखाता है), 'app-icon-केवल' (केवल अनुप्रयोग चिह्न दिखाता है) या "
+"'दोनों'."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"यदि सही है, मौजूदा कार्यस्थान से आनेवाला विंडो इस स्विचर में दिखेगा. अन्यथा, "
+"सभी विंडो शामिल किए जाते हैं."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "जनक विंडो में मोडल संवाद संलग्न करें"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"यह कुँजी org.gnome.mutter में कुँजी को अधिरोहित करता है जब गनोम शेल को चला "
+"रहा हो."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "जब स्क्रीन किनारे पर विंडोज़ को छोड़ने बढ़त टाइलिंग सक्षम करें"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "कार्यस्थान गतिशील रूप से प्रबंधित की जाती हैं"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "केवल प्राथमिक मॉनिटर पर कार्यस्थान"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"माउस अवस्था में पॉइंटर के चलने के रूकने तक फोकस परिवर्तन को विलंबित करें"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "कैप्टिव पोर्टल"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s के लिए वरीयता संवाद लोड करने में एक त्रुटि थी:"
+
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extensions"
+msgstr "गनोम शेल एक्सटेंशन"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "रद्द करें"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "अगला"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "ताला खोलें"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "साइन इन"
+
+#: ../js/gdm/loginDialog.js:269
+msgid "Choose Session"
+msgstr "सत्र चुनें"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "सूचीबद्ध नहीं?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(उदाहरण के लिए, उपयोक्ता या %s)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "उपयोक्तानाम: "
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "लॉगिन विंडो"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "प्रमाणीकरण त्रुटि"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(या स्वाइप फिंगर)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "कमांड नहीं मिला"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "कमांड का विश्लेषण नहीं कर सका:"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” का निष्पादन विफल:"
+
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "वेब सत्यापन रिडायरेक्ट"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "बारंबार उपयोग वाला अनुप्रयोग यहाँ प्रकट होगा"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "बारंबार"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "सभी"
+
+#: ../js/ui/appDisplay.js:1790
+msgid "New Window"
+msgstr "नया विंडो"
+
+#: ../js/ui/appDisplay.js:1816 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "पसंदीदा से हटाएँ"
+
+#: ../js/ui/appDisplay.js:1822
+msgid "Add to Favorites"
+msgstr "पसंदीदा में जोड़ें"
+
+#: ../js/ui/appDisplay.js:1831
+msgid "Show Details"
+msgstr "विवरण दिखाएँ"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s को आपकी पसंदीदा में जोड़ा गया है."
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s को आपकी पसंदीदा से हटाया गया है."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "सेटिंग"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "पृष्ठभूमि बदलें…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "पूरा दिन"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "र"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "सो"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "मं"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "बु"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "गु"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "शु"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "श"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "रवि"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "सो"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "मं"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "बु"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "गुरू"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "शु"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "श"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "पिछला माह"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "अगला माह"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "कुछ नियोजित नहीं"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "आज"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "कल"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "इस सप्ताह"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "अगला सप्ताह"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "बाहरी ड्राइव कनेक्टेड है"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "बाहरी ड्राइव डिसकनेक्टेड है"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "हटाने योग्य युक्तियाँ"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s के साथ खोलें"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "बाहर निकालें"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "कूटशब्द:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "फिर टाइप करें:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "कनेक्ट करें"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "कूटशब्द: "
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "कुंजी: "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "पहचान : "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "निजी कुंजी कूटशब्द: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "सेवा: "
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "बेतार संजाल के द्वारा सत्यापन जरूरी"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "कूटशब्द या गोपन कुंजी बेतार संजाल “%s” की पहुँच के लिए जरूरी है."
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "तारयुक्त 802.1X सत्यापन"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "संजाल नाम: "
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "डीएसएल सत्यापन"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "पिन कोड जरूरी"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "पिन कोड मोबाइल ब्रॉडबैंड युक्ति के लिए जरूरी है"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "पिन: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "मोबाइल ब्रॉडबैंज संजाल कूटशब्द"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” में कनेक्ट होने के लिए एक कूटशब्द जरूरी है."
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "सत्यापन आवश्यक"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "प्रशासक"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "सत्यापित करें"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "माफ करें, वह काम नहीं करता है. कृपया फिर कोशिश करें."
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "आमंत्रण"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "कॉल करें"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "फाइल हस्तांतरण"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "गपशप"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "आवाज़ चालू"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "मूक करें"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+msgid "Yesterday, %H∶%M"
+msgstr "कल %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "कल, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s अब %s रूप में जाना जाता है"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%s में आमंत्रण"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s आपको %s में शामिल होने के लिए आमंत्रित कर रहा है"
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "मना करें"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "स्वीकारें"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s से वीडियो कॉल"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s से कॉल"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "जवाब"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s आपको %s भेज रहा है"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s यह देखने के लिए आपकी अनुमति चाहेगा कि आप कब ऑनलाइन हैं"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "संजाल त्रुटि"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "सत्यापन विफल"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "गोपन त्रुटि"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "प्रमाणपत्र नहीं दिया हुआ"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "प्रमाणपत्र गैर भरोसेमंद"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "प्रमाणपत्र समाप्त"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "प्रमाणपत्र निष्क्रिय"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "प्रमाणपत्र मेजबाननाम बेमेल"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "प्रमाणपत्र फिंगरप्रिंट बेमेल"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "प्रमाणपत्र स्वहस्ताक्षरित"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "प्रस्थिति ऑफ़लाइन पर सेट है"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "गोपन उपलब्ध नहीं है"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "प्रमाणपत्र अवैध है"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "कनेक्शन अस्वीकृत किया गया है"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "कनेक्शन स्थापित नहीं किया जा सकता है"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "कनेक्शन गुम हो गया है"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "यह खाता पहले ही सर्वर से कनेक्टेड है"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr ""
+"समान संसाधन के उपयोग से नए कनेक्शन के द्वारा कनेक्शन को प्रतिस्थापित किया गया "
+"है"
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "खाता पहले ही सर्वर पर मौजूद है"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "सर्वर कनेक्शन नियंत्रित करने के लिए अभी काफी व्यस्त है"
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "प्रमाणपत्र वापस लिया गया है"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr "प्रमाणपत्र असुरक्षित साइफर अल्गोरिथम या गोपन की दृष्टि से कमजोर है"
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"सर्वर प्रमाणपत्र की लंबाई या सर्वर प्रमाणपत्र की शृंखला की गहराई कूटलेखन "
+"लाइब्रेरी के "
+"द्वारा लगायी गई सीमा से आगे है"
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "आंतरिक त्रुटि"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s से संबंधित करने में असमर्थ."
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "खाता देखें"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "अज्ञात कारण"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "विंडोज़"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "अनुप्रयोग दिखाएँ"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "डैश"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "पंचांग खोलें"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "घड़ी खोलें"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "तिथि व समय सेटिंग्स"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "लॉग आउट %s"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "लॉग आउट"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s को %d सेकेंड में स्वतः लॉग आउट कर दिया जाएगा."
+msgstr[1] "%s को %d सेकेंडों में स्वतः लॉग आउट कर दिया जाएगा."
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "आपको %d सेकेंड में स्वतः लॉग आउट कर दिया जाएगा."
+msgstr[1] "आपको %d सेकेंडों में स्वतः लॉग आउट कर दिया जाएगा."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "लॉग आउट"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "बंद करें"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "अद्यतन संस्थापित करें और बंद करें"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "यह तंत्र %d सेकेंड में स्वतः बंद हो जाएगा."
+msgstr[1] "यह तंत्र %d सेकेंडों में स्वतः बंद हो जाएगा."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "स्थगित सॉफ्टवेयर अद्यतन संस्थापित करें"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "फिर आरंभ करें"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "बंद करें"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "फिर आरंभ करें"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "यह तंत्र %d सेकेंड में स्वतः फिर आरंभ हो जाएगा."
+msgstr[1] "यह तंत्र %d सेकेंडों में स्वतः फिर आरंभ हो जाएगा."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "अद्यतन संस्थापित करें और फिर आरंभ करें"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+#| msgid "The system will restart automatically in %d second."
+#| msgid_plural "The system will restart automatically in %d seconds."
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"यह तंत्र %d सेकेंड में स्वतः फिर आरंभ हो जाएगा और अद्यतन संस्थापित करेगा."
+msgstr[1] ""
+"यह तंत्र %d सेकेंड में स्वतः फिर आरंभ हो जाएगा और अद्यतन संस्थापित करेगा."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "फिर आरंभ करें और संस्थापित करें"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "संस्थापित करें और बंद करें"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "अद्यतन संस्थापित होने के बाद बंद करें"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "बैटरी ऊर्जा पर चल रहा है: अद्यतन संस्थापन के पहले प्लगिन करें."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "कुछ अनुप्रयोग व्यस्त हैं या बिना सहेजा कार्य है."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "अन्य उपयोक्ता लॉगिन गैं."
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (दूरस्थ)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (कंसोल)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "संस्थापित करें"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "“%s” को extensions.gnome.org से डाउनलोड कर संस्थापित करें?"
+
+#: ../js/ui/keyboard.js:692 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "कुंजीपटल"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "कोई एक्सटेंशन संस्थापित नहीं"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ने कोई त्रुटि नहीं दिया है."
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "त्रुटियाँ छुपाएँ"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "त्रुटियाँ दिखाएँ"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "सक्षम"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "अक्षम"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "त्रुटि"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "समयातीत"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "डाउनलोड कर रहा है"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "श्रोत दिखाएँ"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "वेब पृष्ठ"
+
+#: ../js/ui/messageTray.js:1327
+msgid "Open"
+msgstr "खोलें"
+
+#: ../js/ui/messageTray.js:1334
+msgid "Remove"
+msgstr "हटाएँ"
+
+#: ../js/ui/messageTray.js:1631
+msgid "Notifications"
+msgstr "अधिसूचना"
+
+#: ../js/ui/messageTray.js:1638
+msgid "Clear Messages"
+msgstr "संदेश साफ़ करें"
+
+#: ../js/ui/messageTray.js:1657
+msgid "Notification Settings"
+msgstr "अधिसूचना सेटिंग्स"
+
+#: ../js/ui/messageTray.js:1710
+msgid "Tray Menu"
+msgstr "ट्रे मेनू"
+
+#: ../js/ui/messageTray.js:1934
+msgid "No Messages"
+msgstr "कोई संदेश नहीं"
+
+#: ../js/ui/messageTray.js:1979
+msgid "Message Tray"
+msgstr "संदेश तश्तरी"
+
+#: ../js/ui/messageTray.js:2992
+msgid "System Information"
+msgstr "तंत्र जानकारी"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "अज्ञात"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d नया संदेश"
+msgstr[1] "%d नए संदेश"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "पहले जैसा"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "सारांश"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "खोजने के लिए टाइप करें…"
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "बाहर"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "क्रियाएँ"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "शीर्ष पट्टी"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "कमांड दर्ज करें"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "बंद करें"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "फिर आरंभ कर रहा है..."
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d नयी अधिसूचना"
+msgstr[1] "%d नयी अधिसूचनाएँ"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "तालाबंद"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "गनोम को स्क्रीन को लॉक करने में जरूरत है"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "ताला खोलने में अक्षम"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "लॉक को किसी अनुप्रयोग द्वारा अवरोधित किया गया"
+
+#: ../js/ui/search.js:594
+msgid "Searching…"
+msgstr "खोज रहा है..."
+
+#: ../js/ui/search.js:596
+msgid "No results."
+msgstr "कोई परिणाम नहीं."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "नक़ल करें"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "चिपकाएँ"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "पाठ दिखाएँ"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "पाठ छिपाएँ"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "कूटशब्द:"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "कूटशब्द याद रखें"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "पहुँच"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "छोटा-बड़ा करें"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "स्क्रीन वाचक"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "स्क्रीन कुँजीपटल"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "दृष्टि चेतावनी"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "स्टिकी कुंजी"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "धीमी कुंजी"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "उछलती कुंजियाँ"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "माउस कुंजियाँ"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "अधिक विरोध"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "बड़ा पाठ"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "ब्लूटूथ"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "बन्द करें"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "ब्लूटूथ सेटिंग्स"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d कनेक्टेड युक्ति"
+msgstr[1] "%d कनेक्टेड युक्ति"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+msgid "Not Connected"
+msgstr "मत कनेक्ट करें"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "चमकीलापन"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "कुंजीपट लेआउट दिखाएँ"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "स्थान"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "निष्क्रिय करें"
+
+#: ../js/ui/status/location.js:73
+msgid "Privacy Settings"
+msgstr "गोपनीयता विन्यास"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "उपयोग में"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "सक्रिय करें"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<अज्ञात>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+msgid "Off"
+msgstr "बंद"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "कनेक्टेड"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "अप्रबंधित"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "डिसकनेक्ट कर रहा है"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+msgid "Connecting"
+msgstr "कनेक्शन"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "सत्यापन जरूरी"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "फर्मवेयर अनुपस्थित"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "अनुपलब्ध"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "कनेक्शन असफल"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "बेतार सेटिंग"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "मोबाइल ब्रॉडबैंड सेटिंग"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+msgid "Hardware Disabled"
+msgstr "हार्डवेयर निष्क्रिय"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "बतौर इंटरनेट कनेक्शन उपयोग करें"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "हवाई जहाज मोड चालू है"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "वाई-फ़ाइल निष्क्रिय है जब हवाईजहाज अवस्था चालू है."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "हवाई जहाज मोड बंद करें"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "वाई फाई बंद करें"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "वाई-फ़ाई को चलाने की जरूरत है ताकि संजाल से जोड़ा जा सके."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "वाई फाई चालू करें"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr " वाई फाई संजाल"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "एक नेटवर्क चुनें"
+
+#: ../js/ui/status/network.js:882
+msgid "No Networks"
+msgstr "कोई संजाल नहीं"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "बंद करने के लिए हार्डवेयर स्विच का उपयोग करें"
+
+#: ../js/ui/status/network.js:1173
+msgid "Select Network"
+msgstr "एक नेटवर्क चुनें"
+
+#: ../js/ui/status/network.js:1179
+msgid "Wi-Fi Settings"
+msgstr "वाइ फाइ सेटिंग"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "चालू करें"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "हॉटस्पॉट सक्रिय"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "कनेक्ट कर रहा है..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "सत्यापन जरूरी"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "संबंधन विफल"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "नेटवर्क सेटिंग्स"
+
+#: ../js/ui/status/network.js:1482
+msgid "VPN Settings"
+msgstr "वीपीएन सेटिंग"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "संजाल प्रबंधक"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "संजाल कनेक्शन का सक्रियन असफल"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "बिजली सेटिंग्स"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "पूरी तरह चार्ज्ड"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "अनुमान लगा रहा है…"
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d शेष (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d जबतक पूर्ण (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "बैटरी"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "हवाई जहाज मोड"
+
+#: ../js/ui/status/rfkill.js:85
+msgid "On"
+msgstr "चालू"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "उपयोक्ता बदलें"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "लॉग आउट"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "दिशा लॉक"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "स्थगित"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "बंद करें"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "आवाज़ बदला गया"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "आवाज़"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "माइक्रोफोन"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "बतौर अन्य उपयोक्ता लॉगिन करें"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "विंडो खोलें"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "अनुप्रयोग"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "ढूँढें"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” तैयार है"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "क्या आप इस प्रदर्शन जमावट को रखना चाहते हैं?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "सेटिंग्स में वापस जाएँ"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "परिवर्तन बनाए रखें"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "सेटिंग बदलाव %d सेकेंड में वापस होगा"
+msgstr[1] "सेटिंग बदलाव %d सेकेंड में वापस होगा"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "न्यूनतम करें"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "गैर अधिकतम करें"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "अधिकतम करें"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "हटाएँ"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "आकार बदलें"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "ऑनस्क्रीन शीर्षकपट्टी खिसकाएँ"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "हमेशा ऊपर"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "हमेशा दृश्य कार्यस्थान पर"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "ऊपर कार्यस्थान में ले जाएँ"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "नीचे कार्यस्थान में ले जाएँ"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "एवोल्यूशन पंचांग"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u आउटपुट"
+msgstr[1] "%u आउटपुट"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u इनपुट"
+msgstr[1] "%u इनपुट"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "तंत्र ध्वनि"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "छपाई संस्करण"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "लॉगिन स्क्रीन के लिए प्रयुक्त जीडीएम अवस्था"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "विशेष अवस्था का उपयोग करें, उदाहरण के लिए लॉगिन स्क्रीन के लिए \"gdm\""
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "संभावित अवस्था सूचीबद्ध करें"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” लॉन्च करने में विफल"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "कूटशब्द मेल नहीं खाता है."
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "कूटशब्द खाली नहीं हो सकता है"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "सत्यापन संवाद को उपयोक्ता के द्वारा खारिज़ कर दिया गया"
+
+#~ msgid "Screenshots"
+#~ msgstr "स्क्रीनशॉट"
+
+#~ msgid "Record a screencast"
+#~ msgstr "screencast रिकॉर्ड करें"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "क्या अनुप्रयोगों के उपयोग के बारे में आँकड़े इकट्ठा करना हैं."
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "सबसे अधिक इस्तेमाल किया जाने वाले सक्रिय अनुप्रयोगों (जैसे लांचरों में ) पर शेल सामान्य "
+#~ "रूप से नज़र रखता है. हालांकि इस डेटा को निजी रखा जाएगा, आप गोपनीयता कारणों के लिए "
+#~ "इसे अक्षम कर सकते हैं. कृपया ध्यान दें कि ऐसा करने से पहले से सहेजे गए डेटा को हटाया नहीं "
+#~ "जायेगा."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "श्रेणियों की सूची जिसे बतौर फ़ोल्ड़र दिखाया जाना चाहिए"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "अनुप्रयोग दृश्य में हर श्रेणी नाम को बतौर फ़ोल्डर प्रदर्शित किया जाएगा, मुख्य दृश्य में अंदर "
+#~ "ही दिखाए गए रूप के बजाय."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "स्पष्ट रूप से उपयोगकर्ता द्वारा निर्धारित की गई आंतरिक रूप से पिछले आईएम उपस्थिति का "
+#~ "इस्तेमाल किया किया जाता हैं. यह TpConnectionPresenceType गणन से मान लिया गया "
+#~ "हैं "
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "आंतरिक रूप से उपयोगकर्ता के लिए पिछले सत्र उपस्थिति स्थिति को संग्रहीत करने के लिए "
+#~ "उपयोग किया जाता हैं. यह GsmPresenceStatus गणन से मान लिया गया हैं "
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "स्क्रीन रिकॉर्डर को टॉगल करने के लिए कीबाइंडिंग"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr " निर्मित स्क्रीन रिकॉर्डर को रोकने/शुरू करने के लिए कीबाइंडिंग"
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "screencasts के लिए फ्रेमरेट रिकॉर्डिंग इस्तेमाल किया."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ " गनोम शैल के स्क्रीनकास्ट रिकॉर्डर द्वारा परिणामस्वरूप रिकॉर्ड किये गए स्क्रीनकास्ट का "
+#~ "फ्रेमरेट फ्रेम प्रति सेकंड में."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "Gstreamer पाइपलाइन स्क्रीनकास्ट एनकोड करने के लिए प्रयोग किया जाता है."
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "रिकॉर्डिंग को इनकोड करने के लिए GStreamer पाइपलाइन सेट करें. पाइपलाइन के पास एक "
+#~ "असंबद्ध सिंक पैड है जहाँ रिकॉर्ड की गई वीडियो दर्ज की गई है.यह सामान्य रूप से एक "
+#~ "असंबद्ध स्रोत पैड होगा, इस पैड के उत्पादन को आउटपुट फाइल में लिखा जाएगा. लेकिन पाइप "
+#~ "लाइन भी अपने स्वयं केआउटपुट की देखभाल ले सकते हैं- इस shout2send या इसी तरह के "
+#~ "माध्यम से एक Icecast सर्वर से आउटपुट को भेजने के लिए इस्तेमाल किया जा सकता है. रिक्त "
+#~ "मान को निर्धारित या अनिर्धारित करने के दौरान, डिफ़ॉल्ट पाइपलाइन का इस्तेमाल किया "
+#~ "जाएगा. VP8 कोडेक का उपयोग करके को WEBM रिकॉर्ड कर सकते हैं और यह वर्त्तमान में "
+#~ "'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+#~ "threads=%T ! queue ! webmmux' हैं. %T सिस्टम पर इष्टतम धागा गिनती में एक अनुमान "
+#~ "के लिए एक प्लेसहोल्डर के रूप में प्रयोग किया जाता है."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr " स्क्रीनकास्ट के भंडारण के लिए इस्तेमाल किया जाने वाला फ़ाइल एक्सटेंशन"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "रिकॉर्ड किये गए स्क्रीनकास्ट के लिए फ़ाइल का नाम अद्वितीय वर्तमान दिनांक के आधार पर "
+#~ "फ़ाइलनाम होगा और इस विस्तारक का उपयोग करेगा. एक अलग कंटेनर प्रारूप के साथ "
+#~ "रिकॉर्डिंग करने के दौरान बदला जाना चाहिए."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "शीर्षक-पट्टी में बटनों का विन्यास"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "यह कुँजी org.gnome.desktop.wm.preferences में कुँजी को अधिरोहित करता है जब गनोम "
+#~ "शेल को चला रहा हो."
+
+#~ msgid "Extension"
+#~ msgstr "एक्सटेंशन"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "उपर्युक्त कोंबोबॉक्स के उपयोग को विन्यस्त करने के लिए किसी विस्तार को चुनें."
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "सत्र…"
+
+#~ msgid "Power"
+#~ msgstr "पावर"
+
+#~ msgid "Restart"
+#~ msgstr "फिर आरंभ करें"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d %t से स्क्रीनकास्ट"
+
+#~| msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~| msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "इन अनुप्रयोगों से बाहर निकलने के लिए और तंत्र से लॉग आउट होने के लिए लॉग आउट पर क्लिक "
+#~ "करें."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "तंत्र से लॉगिंग आउट."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "इन अनुप्रयोगों से बाहर निकलने या तंत्र की बिजली बंद करने के लिए बंद करें पर क्लिक करें."
+
+#~ msgid "Powering off the system."
+#~ msgstr "तंत्र बंद किया जा रहा है."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "इन अनुप्रयोगों से बाहर निकलने और तंत्र को फिर चालू करने के लिए फिर आरंभ करें क्लिक करें."
+
+#~ msgid "Restarting the system."
+#~ msgstr "तंत्र फिर आरंभ कर रहा है"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "सार्वभौमिक पहुँच सेटिंग"
+
+#~ msgid "Visibility"
+#~ msgstr "दृश्यता"
+
+#~| msgid "Send Files to Device..."
+#~ msgid "Send Files to Device…"
+#~ msgstr "युक्ति में फ़ाइल भेजें…"
+
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "कोई नयी युक्ति सेटअप करें…"
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "फाइलें भेजें…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "कुंजीपट सेंटिग्स"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "माउस सेटिंग"
+
+#~ msgid "Sound Settings"
+#~ msgstr "ध्वनि सेटिंग्स"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s से सत्यापन आग्रह"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "युक्ति %s सेवा '%s' में पहुँच रखती है"
+
+#~ msgid "Always grant access"
+#~ msgstr "हमेशा पहुँच दें"
+
+#~ msgid "Grant this time only"
+#~ msgstr "केवल इस समय अनुमति दें"
+
+#~ msgid "Reject"
+#~ msgstr "अस्वीकारें"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s के लिए युग्मन पुष्टि"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "युक्ति %s इस कंप्यूटर के साथ जोड़ा बनाना चाहती है"
+
+#~ msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgstr "कृपया पुष्टि करें कि क्या पिन '%06d' युक्ति पर स्थिति किसी के साथ मेल खाता है."
+
+#~ msgid "Matches"
+#~ msgstr "मिलान"
+
+#~ msgid "Does not match"
+#~ msgstr "मेल नहीं खाता है"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s के लिए जोड़ा आग्रह"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "कृपया युक्ति पर वर्णित पिन दर्ज करें."
+
+#~ msgid "OK"
+#~ msgstr "ठीक"
+
+#~| msgid "Region and Language Settings"
+#~ msgid "Region & Language Settings"
+#~ msgstr "क्षेत्र और भाषा सेटिंग्स"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "आयतन, संजाल, बैटरी"
+
+#~ msgid "disabled"
+#~ msgstr "अक्षम"
+
+#~ msgid "cable unplugged"
+#~ msgstr "केबल प्लग अलग किया गया"
+
+#~ msgid "unavailable"
+#~ msgstr "अनुपलब्ध"
+
+#~ msgid "More…"
+#~ msgstr "अधिक…"
+
+#~ msgid "Wired"
+#~ msgstr "तारसहित"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "स्वतः इथरनेट"
+
+#~ msgid "Auto broadband"
+#~ msgstr "स्वचालित ब्रॉडबैंड"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "स्वतः डायलअप"
+
+#~ msgid "Auto %s"
+#~ msgstr "स्वतः %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "स्वतः ब्लूटूथ"
+
+#~ msgid "Auto wireless"
+#~ msgstr "स्वतः बेतार"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "वाई-फाई"
+
+#~ msgid "Networking is disabled"
+#~ msgstr "संजालन निष्क्रिय"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d घंटा शेष"
+#~ msgstr[1] "%d घंटा शेष"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s शेष"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "घंटा"
+#~ msgstr[1] "घंटा"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "मिनट"
+#~ msgstr[1] "मिनट"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d मिनट शेष"
+#~ msgstr[1] "%d मिनट शेष"
+
+#~ msgctxt "percent of battery remaining"
+#~ msgid "%d%%"
+#~ msgstr "%d%%"
+
+#~| msgid "AC adapter"
+#~ msgid "AC Adapter"
+#~ msgstr "एसी एडॉप्टर"
+
+#~| msgid "Laptop battery"
+#~ msgid "Laptop Battery"
+#~ msgstr "लैपटॉप बैटरी"
+
+#~ msgid "Monitor"
+#~ msgstr "मॉनीटर"
+
+#~ msgid "Mouse"
+#~ msgstr "माउस"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~| msgid "Cell phone"
+#~ msgid "Cell Phone"
+#~ msgstr "सेल फोन"
+
+#~| msgid "Media player"
+#~ msgid "Media Player"
+#~ msgstr "मीडिया-प्लेयर"
+
+#~ msgid "Tablet"
+#~ msgstr "टैब्लेट"
+
+#~ msgid "Computer"
+#~ msgstr "कम्प्यूटर"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "अज्ञात"
+
+#~ msgid "Available"
+#~ msgstr "उपलब्ध"
+
+#~ msgid "Busy"
+#~ msgstr "व्यस्त"
+
+#~ msgid "Invisible"
+#~ msgstr "अदृश्य"
+
+#~ msgid "Away"
+#~ msgstr "दूर"
+
+#~ msgid "Idle"
+#~ msgstr "निष्क्रिय"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "आपकी चैट स्थिति व्यस्त नियत की जाएगी"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "चैट संदेश सहित अधिसूचना अब निष्क्रिय है. आपकी ऑनलाइन स्थिति दूसरे को यह जानने के लिए "
+#~ "समायोजित की जाएगी जिसे आप अपने संदेश में नहीं देख सकते हैं."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "बंद करना बिना सहेजे काम के नष्ट होने का कारण बन सकता है."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "माफ़ करें, आपके लिए आज कोई दृष्टि नहीं:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s ओरेकल कहती है"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "साइन इन"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "अनुप्रयोग"
+
+#~ msgid "SETTINGS"
+#~ msgstr "सेटिंग्स"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Subscription request"
+#~ msgstr "सदस्यता आग्रह"
+
+#~ msgid "Connection error"
+#~ msgstr "कनेक्शन त्रुटि"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "<b>%X</b> पर प्रेषित <b>%A</b> पर"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "<b>%A</b> पर प्रेषित, <b>%B %d</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "%s में कनेक्शन असफल"
+
+#~ msgid "Reconnect"
+#~ msgstr "फिर कनेक्ट करें"
+
+#~ msgid "tray"
+#~ msgstr "तश्तरी"
+
+#~ msgid "Browse Files..."
+#~ msgstr "फ़ाइल ब्राउज़ करें..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "युक्ति की ब्राउज़िंग में त्रुटि"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "निवेदित युक्ति को ब्राउज़ नहीं किया जा सकता है, त्रुटि है '%s'"
+
+#~ msgid "More..."
+#~ msgstr "अधिक..."
+
+#~ msgid "Wireless"
+#~ msgstr "बेतार"
+
+#~ msgid "VPN Connections"
+#~ msgstr "वीपीएन कनेक्शन्स"
+
+#~ msgid "System Settings"
+#~ msgstr "तंत्र विन्यास"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "आपकी पसंदीदा ईस्टर एग"
+
+#~ msgid "United Kingdom"
+#~ msgstr "यूनाइटेड किंगडम"
+
+#~ msgid "Default"
+#~ msgstr "तयशुदा"
+
+#, fuzzy
+#~| msgid "If true, display seconds in time."
+#~ msgid "If true, display onscreen keyboard."
+#~ msgstr "अगर सत्य है तो समय में सेकण्ड भी दर्शाएगा."
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "अगर सत्य है तो समय में सेकण्ड भी दर्शाएगा."
+
+#~ msgid "Show date in clock"
+#~ msgstr "घड़ी में तिथि दिखाएँ"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "समय सेकण्ड्स के साथ दिखाएँ"
+
+#~ msgid "CONTACTS"
+#~ msgstr "संपर्क"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "हालिया मद"
+
+#~| msgid "Password:"
+#~ msgid "Show password"
+#~ msgstr "कूटशब्द दिखाएँ"
+
+#~ msgid "Retry"
+#~ msgstr "फिर कोशिश करें"
+
+#~ msgid "Connect to..."
+#~ msgstr "इससे कनेक्ट करें..."
+
+#~| msgid "Connection"
+#~ msgid "Connectivity lost"
+#~ msgstr "कनेक्टिविटी गुम"
+
+#~ msgid "%s is online."
+#~ msgstr "%s ऑनलाइन है."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s ऑफ़लाइन है."
+
+#~ msgid "%s is away."
+#~ msgstr "%s दूर है."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s व्यस्त है."
+
+#~ msgid "Hidden"
+#~ msgstr "छुपा हुआ"
+
+#~ msgid "Power Off..."
+#~ msgstr "पावर ऑफ़..."
+
+#~| msgid "My Account"
+#~ msgid "Online Accounts"
+#~ msgstr "ऑनलाइन खाता"
+
+#~ msgid "Lock Screen"
+#~ msgstr "स्क्रीन पर ताला लगाएँ"
+
+#~ msgid "Log Out..."
+#~ msgstr "लॉग आउट..."
+
+#~ msgid "Home Folder"
+#~ msgstr "घर फ़ोल्डर"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "एक मिनट से कम पहले"
+
+#~ msgid "%d minute ago"
+#~ msgid_plural "%d minutes ago"
+#~ msgstr[0] "%d मिनट पहले"
+#~ msgstr[1] "%d मिनट पहले"
+
+#~ msgid "%d hour ago"
+#~ msgid_plural "%d hours ago"
+#~ msgstr[0] "%d घंटा पहले"
+#~ msgstr[1] "%d घंटा पहले"
+
+#~ msgid "%d day ago"
+#~ msgid_plural "%d days ago"
+#~ msgstr[0] "%d दिन पहले"
+#~ msgstr[1] "%d दिन पहले"
diff --git a/po/hr.po b/po/hr.po
new file mode 100644
index 0000000..38df262
--- /dev/null
+++ b/po/hr.po
@@ -0,0 +1,3368 @@
+# Croatian translation for gnome-shell.
+# Copyright (C) 2016 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-14 22:12+0000\n"
+"PO-Revision-Date: 2022-09-15 00:29+0200\n"
+"Last-Translator: gogo <trebelnik2@gmail.com>\n"
+"Language-Team: Croatian <hr@li.org>\n"
+"Language: hr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 3.1.1\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Pokretači"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktiviraj omiljenu aplikaciju 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktiviraj omiljenu aplikaciju 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktiviraj omiljenu aplikaciju 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktiviraj omiljenu aplikaciju 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktiviraj omiljenu aplikaciju 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktiviraj omiljenu aplikaciju 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktiviraj omiljenu aplikaciju 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktiviraj omiljenu aplikaciju 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktiviraj omiljenu aplikaciju 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Slike zaslona"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Uslikaj zaslon interaktivno"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Uslikaj zaslon"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Uslikaj prozor"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Snimaj zaslon interaktivno"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sustav"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Prikaži popis poruka obavijesti"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokusiraj aktivnu obavijest"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Prikaži pregled"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Prikaži sve aplikacije"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Otvori izbornik aplikacija"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME ljuska"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Pokretanje upravljanja prozorima i aplikacijama"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Omogući unutrašnji alat koristan za razvijatelje i testiratelje koristeći "
+"Alt-F2 dijalog"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Dopusti pristup alat nadziranja i unutrašnjeg otklanjanja grešaka koristeći "
+"Alt-F2 dijalog."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID-ovi proširenja za omogućavanje"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Proširenja GNOME ljuske imaju UUID svojstvo; taj ključ prikazuje proširenja "
+"koja treba učitati. Svako proširenje koje se želi učitati treba biti na tom "
+"popisu. Možete isto tako manipulirati tim popisom sa EnableExtension i "
+"DisableExtension D-Bus načinima na org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID-ovi proširenja za prisilno onemogućavanje"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Proširenja GNOME ljuske imaju UUID svojstvo; taj ključ prikazuje proširenja "
+"koja treba onemogućiti, čak iako je učitano kao dio trenutnog načina rada. "
+"Možete isto tako manipulirati tim popisom sa EnableExtension i "
+"DisableExtension D-Bus načinima na org.gnome.Shell. Ovaj ključ ima prednost "
+"nad “enabled-extensions” postavkom."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Onemogući korisnikova proširenja"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Onemogući sva proširenja koja je korisnik omogućio bez utjecaja na postavku "
+"“omogućeno-proširenje”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Onemogućuje provjeru kompatibilnosti inačice proširenja"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME ljuska će samo učitati proširenja koje podržavaju trenutno pokrenutu "
+"inačicu. Omogućavanje te mogućnosti će onemogućiti tu provjeru i pokušati "
+"učitati sva proširenja bez obzira koju inačicu podržavaju."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Popis ID-ova datoteka radne površine omiljenih aplikacija"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Aplikacije koje odgovaraju tim identifikatorima će biti prikazane u području "
+"omiljenih."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Povijest naredbi (Alt-F2) dijalog"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Povijest dijaloga naprednih kontrola i upravljanja"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Uvijek prikaži ”Odjava” stavku u korisničkom izborniku."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ova tipka zaobilazi automatsko sakrivanje ”Odjava” stavke izbornika u "
+"situaciji jednog-korisnika, jedne-sesije."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Treba li zapamtiti lozinku za montiranje šifriranih ili udaljenih datotečnih "
+"sustava"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Ova ljuska će zatražiti lozinku kada šifrirani uređaj ili udaljeni datotečni "
+"sustav je montiran. Ako se lozinka može spremiti za buduću upotrebu ”Zapamti "
+"lozinku” mogućnost odabira će biti prisutna. Ta mogućnost postavlja zadano "
+"stanje odabira."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Treba li zadani Bluetooth adapter postaviti uređaj s kojim je pridružen"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Ova ljuska će samo prikazati Bluetooth izbornik ako je Bluetooth adapter "
+"uključen, ili kao su dostupni uređaji pridruženi sa zadanim adapterom. Ovo "
+"će biti poništeno ako zadani adapter nije imao nikada pridruženog uređaja."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "Posljednji odabrani nezadani profil energije"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Pojedini sustavi podržavaju više od dva profila energije. Kako bi se dalje "
+"podržavalo prebacivanje između dva profila, ovaj ključ bilježi zadnji "
+"odabrani nezadani profil."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Posljednja inačica dijaloga \"Dobrodošli u GNOME\" je prikazana za"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Ovaj ključ određuje koja inačica dijaloga \"Dobrodošli u GNOME\" je "
+"posljednja prikazana. Prazan izraz predstavlja najstariju moguću inačicu a "
+"najveći broj će prikazati inačicu koja još ne postoji. Taj najviši broj može "
+"se koristiti za onemogućavanje ovog dijaloga."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Raspored odabira aplikacije"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Raspored odabira aplikacije. Svaki unos u nizu je stranica. Stranice su "
+"spremljene po poretku kao što se pojavljuju u GNOME ljusci. Svaka stranica "
+"sadrži “application id” → 'data' par. Trenutno, sljedeće vrijednosti su "
+"spremljene kao 'data': • “position”: položaj ikone aplikacije na stranici"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Prečac tipkovnice za otvaranje izbornika aplikacije"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Prečac tipkovnice za otvaranje izbornika aplikacije."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Prečac tipkovnice za promjenu stanja pogleda"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Prečac tipkovnice za promjenu sesija, odabiratelja prozora i mreže aplikacija"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Prečac tipkovnice za promjenu između mreže aplikacija, odabiratelja prozora "
+"i sesija"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Prečac tipkovnice za otvaranje prikaza \"Prikaži aplikacije\""
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Prečac tipkovnice za otvaranje prikaza \"Prikaži aplikacije\" u pogledu "
+"Aktivnosti."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Prečac tipkovnice za otvaranje pogleda"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Prečac tipkovnice za otvaranje pogleda Aktivnosti."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Prečac tipkovnice za uključivanje/isključivanje vidljivosti popisa obavijesti"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Prečac tipkovnice za uključivanje/isključivanje vidljivosti popisa "
+"obavijesti."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Prečac tipkovnice za fokusiranje aktivne obavijesti"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Prečac tipkovnice za fokusiranje aktivne obavijesti."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Prebaci na aplikaciju 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Prebaci na aplikaciju 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Prebaci na aplikaciju 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Prebaci na aplikaciju 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Prebaci na aplikaciju 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Prebaci na aplikaciju 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Prebaci na aplikaciju 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Prebaci na aplikaciju 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Prebaci na aplikaciju 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Ograniči premještanje na trenutni radni prostor."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ako je odabrano, samo aplikacije koje imaju prozor na trenutnom radnom "
+"prostoru su prikazane na prebacivaču. U suprotnom, sve aplikacije su "
+"uključene."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "Način ikone aplikacije."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Podešava kako su prozori prikazani u prebacivaču. Valjane mogućnosti su "
+"”thumbnail-only” (prikazuje minijaturu prozora), ”app-icon-only” (prikazuje "
+"samo ikonu aplikacije) ili ”both”."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ako je odabrano, samo prozori s trenutnog radnog prostora su prikazani u "
+"prebacivaču, U suprotnom, svi prozori su uključeni."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Lokacije"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Lokacije prikazane u svjetskim satovima"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Automatska lokacija"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Treba li ili ne treba preuzeti trenutnu lokaciju"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Lokacija"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "Lokacija za koju treba prikazati prognozu"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Pričvrsti prozore dijaloga na nadređeni prozor"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ova vrijednost zaobilazi org.gnome.mutter kada je pokrenuta GNOME ljuska."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Omogući rubno popločavanje pri ispuštanju prozora na rubovima zaslona"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Radni prostori su upravljani promjenjivo"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Radni prostori samo na glavnom zaslonu"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Odgodi promjenu fokusa u načinu rada s mišem dok se pokazivač ne prestane "
+"pomicati"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Mrežna prijava"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Nešto je pošlo po krivu"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Nažalost došlo je do problema: postavke za ovo proširenje se ne mogu "
+"prikazati. Preporučljivo je da problem prijavite autoru proširenja."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Tehničke pojedinosti"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Naslovnica"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Posjetite naslovnicu proširenja"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Odustani"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Lozinka"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Odaberi sesiju"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Niste na popisu?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(npr., korisnik ili %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Korisničko ime"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Prozor prijave"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Greška ovjere"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(ili pomakni prst preko čitača)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(ili stavite prst na čitač)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Isključivanje"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "isključi;isključivanje;zaustavi;zaustavljanje"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Ponovno pokreni"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "ponovno pokreni;ponovno pokretanje;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Zaključavanje zaslona"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "zaključavanje zaslona"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Odjava"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "odjava;odjavljivanje"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspenzija"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspendiraj;spavaj"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Zamijeni korisnika"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "zamjena korisnika"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "zaključavanje orjentacije;otključaj orjentaciju;zaslon;zakretanje"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Uslikaj zaslon"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "uslikaj zaslon;snimaj zaslon;isječak;snimaj;snimi"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Zakretanje otključanog zaslona"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Zakretanje zaključanog zaslona"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Naredba nije pronađena"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Nemoguća obrada naredbe:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Izvršavanje '%s' neuspjelo:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Upravo sada"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Prije %d minute"
+msgstr[1] "Prije %d minute"
+msgstr[2] "Prije %d minuta"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Prije %d sata"
+msgstr[1] "Prije %d sata"
+msgstr[2] "Prije %d sati"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Jučer"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Prije %d dana"
+msgstr[1] "Prije %d dana"
+msgstr[2] "Prije %d dana"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Prije %d tjedna"
+msgstr[1] "Prije %d tjedna"
+msgstr[2] "Prije %d tjedana"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Prije %d mjeseca"
+msgstr[1] "Prije %d mjeseca"
+msgstr[2] "Prije %d mjeseci"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Prije %d godine"
+msgstr[1] "Prije %d godine"
+msgstr[2] "Prije %d godina"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Jučer, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d. %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d. %B, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Jučer, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d. %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d. %B %Y, %H∶%M"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Prijava pristupne točke"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Vaše povezivanje s ovom prijavom pristupne točke nije sigurno. Lozinke i "
+"ostale informacije koje ste upisali na ovoj stranici mogu vidjeti ljudi u "
+"vašoj blizini."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Zabrani pristup"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Dopusti pristup"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Neimenovana mapa"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s je dodan na pokretač."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s je uklonjen iz pokretača."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Otvoreni prozori"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Novi prozor"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Prikaži pojedinosti"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Zatvori"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Ukloni iz pokretača"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Dodaj na pokretač"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Pokreni pomoću integrirane grafičke kartice"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Pokreni pomoću diskretne grafičke kartice"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Odaberi zvučni uređaj"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Postavke zvuka"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Slušalice"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Slušalice s mikrofonom"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Promijeni pozadinu…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Postavke prikaza"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Postavke"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "N"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "U"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Č"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Prijašnji mjesec"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Sljedeći mjesec"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Tjedan %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Nema obavijesti"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Ne ometaj"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Obriši"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” ne reagira."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Možda ste odabrali kratko čekanje do nastavka izvođenja ili prisile "
+"aplikacije da se u potpunosti zatvori."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Prisilno zatvori"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Čekaj"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Vanjski uređaj povezan"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Vanjski uređaj odspojen"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Nemoguće otključavanje uređaja"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Instalirana udisks inačica ne podržava PIM postavku"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Otvori s(a) %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternativno se možete povezati pritiskom na “WPS” tipku vašeg usmjernika."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Povezivanje"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Ključ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Lozinka privatnog ključa"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitet"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Usluga"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Potrebna je ovjera"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Lozinka ili ključevi šifriranja su potrebni za pristup bežičnoj mreži “%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Wired 802.1X ovjera"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Naziv mreže"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL ovjera"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN kôd je potreban"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN kôd je potreban za uređaj mobilnog interneta"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Potrebna je lozinka za povezivanje s “%s”."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Mrežni upravitelj"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN lozinka"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Potrebna je ovjera"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Ovjeri"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Nažalost, to ne radi. Pokušajte ponovno."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s je poznat kao %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Prozori"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Prikaži aplikacije"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Pokretač"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-e %b, %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-e %b"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-e %b, %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Danas"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Sutra"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Cijeli dan"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nema događaja"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Dodaj satove iz svijeta…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Svjetski satovi"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Pretraživanje…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Posjetite za opširnije vremenske informacije"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Vremenske informacije su trenutno nedostupne"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Vrijeme"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Odaberi lokaciju vremena…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Odjavi se %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Odjava"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s će se automatski odjaviti za %d sekundu."
+msgstr[1] "%s će se automatski odjaviti za %d sekunde."
+msgstr[2] "%s će se automatski odjaviti za %d sekundi."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Odjavit ćete se automatski za %d sekundu."
+msgstr[1] "Odjavit ćete se automatski za %d sekunde."
+msgstr[2] "Odjavit ćete se automatski za %d sekundi."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Odjava"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Isključivanje"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instaliraj nadopune i isključi"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sustav će se automatski isključiti za %d sekundu."
+msgstr[1] "Sustav će se automatski isključiti za %d sekunde."
+msgstr[2] "Sustav će se automatski isključiti za %d sekundi."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instaliraj dostupne nadopune softvera"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Isključi"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Ponovno pokreni"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instaliraj nadopune i ponovno pokreni"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sustav će se automatski ponovno pokrenuti za %d sekundu."
+msgstr[1] "Sustav će se automatski ponovno pokrenuti za %d sekunde."
+msgstr[2] "Sustav će se automatski ponovno pokrenuti za %d sekundi."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Ponovno pokreni"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Ponovno pokreni i instaliraj nadopune"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sustav će se automatski ponovno pokrenuti i instalirati nadopune za %d "
+"sekundu."
+msgstr[1] ""
+"Sustav će se automatski ponovno pokrenuti i instalirati nadopune za %d "
+"sekunde."
+msgstr[2] ""
+"Sustav će se automatski ponovno pokrenuti i instalirati nadopune za %d "
+"sekundi."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Ponovno pokreni i instaliraj"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instaliraj i isključi"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Isključi nakon što se nadopune instaliraju"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Ponovno pokreni i instaliraj nadogradnju"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s će se instalirati nakon ponovnog pokretanja. Instalacija nadogradnji "
+"može potrajati duže vrijeme: pobrinite se da ste spremili svoj rad i "
+"računalo je priključeno na izvor napajanja."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Slaba energija baterije: računalo priključite na izvor napajanja prije "
+"instalacije nadopuna."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Neke aplikacije su zauzete ili imaju nespremljeni rad"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Drugi korisnici su prijavljeni"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Mogućnosti pokretanja"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (udaljeno)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konzola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instaliraj"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instaliraj proširenje"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Preuzmi i instaliraj “%s” sa extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Nadopuna proširenja je dostupna"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Nadopune proširenja su spremne za instalaciju."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Dopusti sprječavanje prečaca"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Aplikacija %s želi spriječiti prečac"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Aplikacija želi spriječiti prečac"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Možete obnoviti prečac pritiskom na %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Spriječi"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Dopusti"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Spore tipke uključene"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Spore tipke isključene"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Držali ste pritisnutu tipku Shift 8 sekundi. To je prečac za značajku sporih "
+"tipka, koja utječe na način rada vaše tipkovnice."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Pričvrstive tipke uključene"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Pričvrstive tipke isključene"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Upravo ste pritisnuli tipku Shift 5 puta uzastopno. To je prečac za značajku "
+"pričvrstivih tipka, koja utječe na način rada vaše tipkovnice."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Upravo ste pritisnuli dvije tipke odjednom, ili ste pritisnuli tipku Shift 5 "
+"puta uzastopno. To isključuje značajku pričvrstivih tipka, koja utječe na "
+"način rada vaše tipkovnice."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Ostavi"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Uključi"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Isključi"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Ukloni"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Postavke jezika i regije"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nema instaliranih proširenja"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nije dao ni jednu grešku."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Sakrij greške"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Prikaži greške"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Omogućeno"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Onemogućen"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Greška"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Zastarjelo"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Preuzimanje"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Pogledaj izvor"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Web stranica"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Sustav je stavljen u nesiguran način rada"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Aplikacije sada imaju neograničen pristup"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Poništi"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Prijavljeni ste kao administratorski korisnik"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Pokretanje sesije s ovlastima administratorskog korisnika treba se "
+"izbjegavati iz sigurnosnih razloga. Ako je moguće, trebate se prijaviti kao "
+"normalan korisnik."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Zaključavanje zaslona je onemogućeno"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Zaključavanje zaslona zahtijeva GNOME upravitelja zaslona."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informacije sustava"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Nepoznat izvođač"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Nepoznat naslov"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Upišite za pretragu"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Aplikacije"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Pregled"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Novi prečac…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Određeno aplikacijom"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Prikaži zaslonsku pomoć"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Prebaci zaslon"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Pošalji pritisak tipke"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Završeno"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Uredi…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nedodjeljeno"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Pritisni tipku za podešavanje"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Pritisni Esc za zatvaranje"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Pritisni bilo koju tipku za prekidanje"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Aktivnosti"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sustav"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Gornja traka"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Pokreni naredbu"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Pritisni ESC za zatvaranje"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Ponovno pokretanje nije dostupno na Waylandu"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Ponovno pokretanje…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME treba zaključati zaslon"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Nemoguće zaključavanje"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Zaključavanje je blokirala aplikacija"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Odabir"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Područje odabira"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Zaslon"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Odabir zaslona"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Prozor"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Odabir prozora"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Slikanje zaslona / Snimanje zaslona"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Prikaži pokazivač"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Snimanje zaslona"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Snimka zaslona %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Slika zaslona"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Snimka zaslona je snimljena"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Kliknite ovdje kako bi vidjeli snimku."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Prikaži u Datotekama"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Slika zaslona %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Slika zaslona je uslikana"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Možete zalijepiti sliku iz međuspremnika."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Zaslon uslikan"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Pretraživanje…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Nema rezultata."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "Još %d"
+msgstr[1] "Još %d"
+msgstr[2] "Još %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Pretraži"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopiraj"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Zalijepi"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Prikaži tekst"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Sakrij tekst"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Velika slova su uključena."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Skriveni uređaj"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Uređaj Windows sustava"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Koristi datoteke ključa"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Za otključavanje uređaja koji koristi datoteke ključa, koristite <i>%s</i> "
+"pomagalo."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM broj"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Zapamti lozinku"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Otključaj"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Otvori %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM mora biti broj ili prazan."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Nemoguće pokretanje %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nemoguć pronalazak %s aplikacije"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Pristupačnost"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Visok kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Uvećano"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Čitač zaslona"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Zaslonska tipkovnica"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Vizualna upozorenja"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Ljepljive tipke"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Spore tipke"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Odbijanje tipki"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Tipke miša"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Velik tekst"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatsko zakretanje"
+
+#: js/ui/status/bluetooth.js:171
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Svjetlina"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Tamna tema"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Jednostruki klik"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dvostruki klik"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Povlačenje"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Pomoćni klik"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Zadržani klik"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Tipkovnica"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Prikaži raspored tipkovnice"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Dopusti pristup lokaciji"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Aplikacija %s želi pristupiti vašoj lokaciji"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Pristup lokaciji može se promijeniti u svako vrijeme iz postavka privatnosti."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<nepoznato>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Prekini povezivanje s %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Povežite se s %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s pristupna točka"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Noćno osvjetljenje"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Performanse"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Uravnoteženo"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Štednja energije"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Profili energije"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Zaustavi snimanje zaslona"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Zaustavi dijeljenje zaslona"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Zrakoplovni način rada"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Uslikaj sliku zaslona"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Izbornik isključivanja"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspendiraj"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Ponovno pokreni…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Isključivanje…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Odjava…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Zamijeni korisnika…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Zaključavanje zaslona"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Nepoznati Thunderbolt uređaj"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Otkriven je novi uređaj dok ste bili odsutni. Odspojite i ponovno spojite "
+"uređaj kako bi ga mogli koristiti."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Neovlašteni Thunderbolt uređaj"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Novi uređaj je otkriven i mora biti ovjeren od strane administratora."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Greška Thunderbolt odobravanja"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nemoguće odobravanje Thunderbolt uređaja: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Glasnoća zvuka promijenjena"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Glasnoća zvuka"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Zvučni izlaz"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Zvučni ulaz"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Zrcaljenje"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Spoji zaslone"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Samo vanjski"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Samo ugrađeni"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Povuci za otključavanje"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Klikni ili pritisni tipku za otključavanje"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Otključaj prozor"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Prijavi se kao drugi korisnik"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Dobrodošli u GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Ako se želite lakše snaći, pogledajte vodič."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ne hvala"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Pokreni vodič"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” je spreman"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Želite li zadržati ove postavke zaslona?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Vrati postavke"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Zadrži promjene"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Promjene postavka će se vratiti za %d sekundu"
+msgstr[1] "Promjene postavka će se vratiti za %d sekunde"
+msgstr[2] "Promjene postavka će se vratiti za %d sekundi"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Sakrij"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Prikaži u prozoru"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Uvećaj"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Pomakni"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Promjena veličine"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Premjesti naslovnu traku na zaslom"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Uvijek na vrhu"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Uvijek na vidljivom radnom prostoru"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Premjesti na lijevi radni prostor"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Premjesti na desni radni prostor"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Premjesti na gornji radni prostor"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Premjesti na donji radni prostor"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Premjesti na gornji zaslon"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Premjesti na donji zaslon"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Premjesti na lijevi zaslon"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Premjesti na desni zaslon"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Zatvori"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution kalendar"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Prikaži inačicu"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Način koji koristi GDM za zaslon prijave"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Koristi određen način, npr. \"gdm\" za zaslon prijave"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Popis mogućih načina"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Nepoznat"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Neuspješno pokretanje “%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Lozinke se ne podudaraju."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Lozinka ne može biti prazna"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Dijalog ovjere je prekinut od strane korisnika"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Proširenja"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Upravljajte svojim GNOME proširenjima"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME projekt"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Proširenja rukuje nadopunama proširenja, podešavanjem osobitostima "
+"proširenja i uklanjanjem ili onemogućavanjem neželjenih proširenja."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Podesi proširenja GNOME ljuske"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Nema podudaranja"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Ukloni “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Ako uklonite ovo proširenje, morate se vratiti kako bi ga preuzeli i ponovno "
+"omogućili"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Ukloni"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Launchpad Contributions:\n"
+" ACT Hacklab https://launchpad.net/~stjepan-amajk\n"
+" Ante Karamatić https://launchpad.net/~ivoks\n"
+" Dražen Odobašić https://launchpad.net/~dodobas\n"
+" Frane https://launchpad.net/~kob4lt\n"
+" Miro Glavić https://launchpad.net/~klek\n"
+" Miroslav Matejaš https://launchpad.net/~silverspace+amd64\n"
+" Saša Teković https://launchpad.net/~hseagle2015\n"
+" Senko Rasic https://launchpad.net/~senko\n"
+" Tomislav Krznar https://launchpad.net/~tomislav-krznar\n"
+" gogo https://launchpad.net/~trebelnik-stefina"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d proširenje će se nadopuniti pri sljedećoj prijavi."
+msgstr[1] "%d proširenja će se nadopuniti pri sljedećoj prijavi."
+msgstr[2] "%d proširenja će se nadopuniti pri sljedećoj prijavi."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Proširenje je nekompatibilno s trenutnom GNOME inačicom"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Proširenje sadrži grešku"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Proširenje se može nadopuniti"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Web stranica"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Ukloni…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Pomoć"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "O proširenju"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Proširenja mogu uzrokovati probleme s performansama i stabilnošću. "
+"Onemogućite proširenja ako naiđete na probleme u svojem sustavu."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Ručno instalirana"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Kako bi pronašli i dodali proširenja, posjetite <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Ugrađena"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Nema instaliranih proširenja"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Nažalost, nije moguće dobiti popis instaliranih proširenja. Pobrinite se da "
+"ste prijavljeni u GNOME i pokušajte ponovno."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Nadopune proširenja su spremne"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Novo proširenje je uspješno stvoreno u %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Naziv treba biti vrlo kratak (idealno opisni) izraz.\n"
+"Primjeri su: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Naziv"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Opis je objašnjenje namjene proširenja u jednoj rečenici.\n"
+"Primjeri su: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Opis"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID je globalno jedinstveni identifikator vašeg proširenja.\n"
+"Trebao bi biti u formatu adrese e-pošte (clicktofocus@janedoe.primjer.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Odaberite jedan od dostupnih predložaka:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Predložak"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Jedinstveni identifikator novog proširenja"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAZIV"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Korisniku vidljiv naziv novog proširenja"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "OPIS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Kratak opis namjene proširenja"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "PREDLOŽAK"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Predložak za korištenje s novim proširenjem"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Upišite interaktivno informacije proširenja"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Stvori novo proširenje"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Nepoznati argumenti"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, naziv i opis su potrebni"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Neuspjelo povezivanje s GNOME ljuskom\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Proširenje “%s” ne postoji\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Onemogući proširenje"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID nije naveden"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Naveden je više od jednog UUID-a"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Omogući proširenje"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Proširenje “%s” ne postoji\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Prikaži informacije proširenja"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Prebriši postojeće proširenje"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PAKET_PROŠIRENJA"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instaliraj paket proširenja"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nema odabranih paketa proširenja"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Odabrano je više od jednog paketa proširenja"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Prikaži proširenja instalirana od strane korisnika"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Prikaži proširenja instalirana od strane sustava"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Prikaži omogućena proširenja"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Prikaži onemogućena proširenja"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Prikaži proširenja s postavkama"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Prikaži proširenja s nadopunama"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Prikaži pojedinosti proširenja"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Prikaži instalirana proširenja"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "DATOTEKA"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Dodatni izvor za uključiti u paket"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "GSettings shema koja treba biti uključena"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "MAPA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Mapa gdje su prijevodi pronađeni"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMENA"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "gettext domena za korištenje s prijevodima"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Prebriši postojeći paket"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Mapa gdje će se paket stvoriti"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "IZVORNA_MAPA"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Stvori paket proširenja"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Određeno je više od jedne izvorne mape"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Proširenje “%s” ne sadrži osobitosti\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Neuspjelo otvaranje osobitosti za proširenje “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Otvara osobitosti proširenja"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Vrati proširenje na izvorno"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Nemoguće je uklanjanje proširenja sustava\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Nemoguće je ukloniti “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Ukloni proširenje"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ne ispisuj poruke greške"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Putanja"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Izvorni autor"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Inačica"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stanje"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” ne sadrži argumente"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Upotreba:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Prikaži informaciju inačice i izađi."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "NAREDBA"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTI…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Naredbe:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Prikaži inačicu"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Omogući proširenje"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Onemogući proširenje"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Vrati proširenje na izvorno"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Ukloni proširenje"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Prikaži proširenja"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Prikaži informacije proširenja"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Otvori osobitosti proširenja"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Stvori proširenje"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Paket proširenja"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instaliraj paket proširenja"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Koristi “%s” za dobivanje opširnije pomoći.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Obično"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Prazno proširenje"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Dodaj ikonu na gornju traku"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u izlaz"
+msgstr[1] "%u izlaza"
+msgstr[2] "%u izlaza"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ulaz"
+msgstr[1] "%u ulaza"
+msgstr[2] "%u ulaza"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Zvukovi sustava"
+
+#~ msgid "Log Out"
+#~ msgstr "Odjava"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Bluetooth postavke"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d povezan"
+#~ msgstr[1] "%d povezana"
+#~ msgstr[2] "%d povezanih"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth je uključen"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth je isključen"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Lokacija omogućena"
+
+#~ msgid "Disable"
+#~ msgstr "Onemogući"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Postavke privatnost"
+
+#~ msgid "Location In Use"
+#~ msgstr "Lokacija se već koristi"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Lokacija onemogućena"
+
+#~ msgid "Enable"
+#~ msgstr "Omogući"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s isključena"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s povezana"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s neupravljana"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s povezivanje"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s zahtijeva ovjeru"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Nedostaje firmver za %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s nedostupna"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s neuspješno povezivanje"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Postavke žične mreže"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Mobilni internet"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s hardverski onemogućena"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s onemogućeno"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Povežite se na internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Zrakoplovni način rada je omogućen"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Bežična mreža je onemogućena u zrakoplovnom načinu rada."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Isključi zrakoplovni način rada"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Bežična mreža isključena"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Bežična mreža se mora uključiti kako bi se povezala s internetom."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Uključi bežičnu mrežu"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Bežične mreže"
+
+#~ msgid "Select a network"
+#~ msgstr "Odaberi mrežu"
+
+#~ msgid "No Networks"
+#~ msgstr "Nema mreža"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Koristi hardverski prekidač za isključivanje"
+
+#~ msgid "Select Network"
+#~ msgstr "Odaberi mrežu"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Postavke bežične mreže"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s nije povezana"
+
+#~ msgid "connecting…"
+#~ msgstr "povezivanje…"
+
+#~ msgid "authentication required"
+#~ msgstr "potrebna je ovjera"
+
+#~ msgid "connection failed"
+#~ msgstr "neuspješno povezivanje"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN postavke"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN isključen"
+
+#~ msgid "Network Settings"
+#~ msgstr "Mrežne postavke"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s žično povezivanje"
+#~ msgstr[1] "%s žična povezivanja"
+#~ msgstr[2] "%s žičnih povezivanja"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s bežično povezivanje"
+#~ msgstr[1] "%s bežična povezivanja"
+#~ msgstr[2] "%s bežičnih povezivanja"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s modemsko povezivanje"
+#~ msgstr[1] "%s modemska povezivanja"
+#~ msgstr[2] "%s modemskih povezivanja"
+
+#~ msgid "Connection failed"
+#~ msgstr "Neuspješno povezivanje"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Aktiviranje mrežnog povezivanja je neuspjelo"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Noćno osvjetljenje onemogućeno"
+
+#~ msgid "Resume"
+#~ msgstr "Nastavi"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Onemogući do sutra"
+
+#~ msgid "Power Settings"
+#~ msgstr "Postavke energije"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Potpuno napunjeno"
+
+#~ msgid "Not Charging"
+#~ msgstr "Ne puni se"
+
+#~ msgid "Estimating…"
+#~ msgstr "Procjenjivanje…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d preostalo (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d preostalo do napunjenosti (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Zaslon se trenutno dijeli"
+
+#~ msgid "Turn off"
+#~ msgstr "Isključi"
+
+#~ msgid "Lock"
+#~ msgstr "Zaključaj"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Isključivanje / Odjavljivanje"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Ukloni iz omiljenih"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Dodaj u omiljene"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Upozorenje"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Proširenja mogu prouzrokovati probleme u radu sustava, uključujući "
+#~ "probleme s performansama. Ako se pojave problemi sa sustavom, "
+#~ "preporučljivo je onemogućavanje svih proširenja."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Neuspjelo povezivanje s GNOME ljuskom"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Omogući API samoispitivanja"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Omogućuje D-Bus API koji omogućuje samoispitivanje stanja aplikacije "
+#~ "ljuske."
+
+#~ msgid "Minimize"
+#~ msgstr "Smanji"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Prikaži u prozoru"
+
+#~ msgid "App Picker View"
+#~ msgstr "Prikaz odabira aplikacije"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Sadržaj trenutno odabranog prikaza u odabiru aplikacija."
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopiraj grešku"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Najčešće korištene aplikacije će se pojaviti ovdje"
+
+#~ msgid "Frequent"
+#~ msgstr "Najčešće"
+
+#~ msgid "All"
+#~ msgstr "Sve"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d. %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d. %B, %Y"
+
+#~ msgid "Off"
+#~ msgstr "Isključeno"
+
+#~ msgid "On"
+#~ msgstr "Uključeno"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Pregledaj u Softveru"
+
+#~ msgid "Next"
+#~ msgstr "Sljedeće"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Prijava"
+
+#~ msgid "Password: "
+#~ msgstr "Lozinka: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Upišite lozinku…"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nova poruka"
+#~ msgstr[1] "%d nove poruke"
+#~ msgstr[2] "%d novih poruka"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d nova obavijest"
+#~ msgstr[1] "%d nove obavijesti"
+#~ msgstr[2] "%d novih obavijesti"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Zaključavanje orjentacije"
+
+#~ msgid "Rename"
+#~ msgstr "Preimenuj"
+
+#~ msgid "Password:"
+#~ msgstr "Lozinka:"
+
+#~ msgid "Type again:"
+#~ msgstr "Pokušaj ponovno:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Potrebna je ovjera za bežičnu mrežu"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Lozinka mreže mobilnog interneta"
+
+#~ msgid "Account Settings"
+#~ msgstr "Postavke računa"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Zaključavanje orjentacije"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Prečac tipkovnice za pauziranje i nastavljanje svih pokrenutih duplikata, "
+#~ "u svrhu otklanjanja grešaka"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Koju tipkovnicu koristiti"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Vrsta tipkovnice koja se koristi."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Dogodila se greška učitavanja dijaloga osobitosti za %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s cijeli dan."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, zatim %s kasnije."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, zatim %s, praćena s %s kasnije."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Kao da je %s."
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Odjava"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Zamjena korisnika"
+
+#~ msgid "Hide tray"
+#~ msgstr "Sakrij traku stanja"
+
+#~ msgid "Status Icons"
+#~ msgstr "Ikona stanja"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Web ovjera je preusmjerena"
+
+#~ msgid "Events"
+#~ msgstr "Događaji"
+
+#~ msgid "Notifications"
+#~ msgstr "Obavijesti"
+
+#~ msgid "Media"
+#~ msgstr "Medij"
+
+#~ msgid "Not In Use"
+#~ msgstr "Ne koristi se"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
diff --git a/po/hu.po b/po/hu.po
new file mode 100644
index 0000000..f184fda
--- /dev/null
+++ b/po/hu.po
@@ -0,0 +1,2978 @@
+# Hungarian translation for gnome-shell.
+# Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Gabor Kelemen <kelemeng at gnome dot hu>, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016.
+# Lukács Bence <lukacs.bence1 at gmail dot com>, 2012.
+# Balázs Meskó <mesko.balazs at fsf dot hu>, 2014, 2019, 2020, 2021.
+# Balázs Úr <ur.balazs at fsf dot hu>, 2012, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-26 18:17+0000\n"
+"PO-Revision-Date: 2023-03-02 02:18+0100\n"
+"Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n"
+"Language-Team: Hungarian <gnome-hu-list at gnome dot org>\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 22.08.2\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Indítók"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "1. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "2. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "3. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "4. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "5. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "6. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "7. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "8. kedvenc alkalmazás aktiválása"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "9. kedvenc alkalmazás aktiválása"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Képernyőképek"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Képernyőkép készítése interaktívan"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Képernyőkép készítése"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Képernyőkép készítése egy ablakról"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Képernyőfelvétel rögzítése interaktívan"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Rendszer"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Az értesítési lista megjelenítése"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Az aktív értesítés fókuszba"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Áttekintés megjelenítése"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Minden alkalmazás megjelenítése"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Az alkalmazásmenü megnyitása"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Ablakkezelés és alkalmazásindítás"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Fejlesztők és tesztelők számára hasznos belső eszközök engedélyezése az Alt-"
+"F2 ablakból"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Belső hibakereső és megfigyelő eszközök elérésének engedélyezése az Alt-F2 "
+"ablak használatával."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Engedélyezendő kiterjesztések UUID azonosítói"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"A GNOME Shell kiterjesztések rendelkeznek egy UUID tulajdonsággal; ez a "
+"kulcs felsorolja a betöltendő kiterjesztéseket. Minden betöltendő "
+"kiterjesztésnek szerepelnie kell a listában. A listát az org.gnome.Shell "
+"EnableExtension és DisableExtension D-Bus metódusaival is manipulálhatja."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "A kényszerítetten letiltandó kiterjesztések UUID azonosítói"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"A GNOME Shell kiterjesztések rendelkeznek egy UUID tulajdonsággal; ez a "
+"kulcs felsorolja a letiltandó kiterjesztéseket. Ezek akkor is le lesznek "
+"tiltva, ha a jelenlegi mód részeként betöltésre kerültek. A listát az org."
+"gnome.Shell EnableExtension és DisableExtension D-Bus metódusaival is "
+"manipulálhatja. Ez a kulcs elsőbbséget élvez az „enabled-extensions” "
+"beállítással szemben."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Felhasználói kiterjesztések letiltása"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Minden olyan kiterjesztés letiltása, amelyet a felhasználó az „enabled-"
+"extension” beállítást nem érintve engedélyezett."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Letiltja a kiterjesztés-verzió kompatibilitás ellenőrzését"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"A GNOME Shell csak azon kiterjesztéseket tölti be, amelyek állítják, hogy "
+"támogatják az éppen futó verziót. Ezt a lehetőséget bekapcsolva ez az "
+"ellenőrzés kikapcsolható, és az összes kiterjesztés betöltésére kísérletet "
+"tesz, függetlenül attól, hogy melyik verziót támogatják."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "A kedvenc alkalmazások asztalifájl-azonosítóinak listája"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Az itt felsorolt azonosítóknak megfelelő alkalmazások jelennek meg a "
+"kedvencek területen."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "A parancsablak (Alt-F2) előzményei"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "A távcső ablak előzményei"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Mindig jelenjen meg a „Kijelentkezés” menüelem a felhasználói menüben."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ez a kulcs felülbírálja a „Kijelentkezés” menüelem automatikus elrejtését "
+"egyfelhasználós, egyedüli munkamenet esetén."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Megjegyezze-e a jelszót a titkosított vagy távoli fájlrendszerek csatolásához"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"A shell jelszót fog kérni titkosított eszköz vagy távoli fájlrendszer "
+"csatolásakor. Ha a jelszó elmenthető későbbi használatra, akkor megjelenik "
+"egy „Jelszó megjegyzése” jelölőmező. Ez a kulcs a jelölőnégyzet "
+"alapértelmezett állapotát állítja be."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Az utoljára kiválasztott nem alapértelmezett teljesítményprofil"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Egyes rendszerek kettőnél több teljesítményprofilt támogatnak. Annak "
+"érdekében, hogy továbbra is támogassa a két profil közötti váltást, ez a "
+"kulcs rögzíti az utoljára kiválasztott nem alapértelmezett profilt."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"A legutóbbi verzió, amelynél az „Üdvözli a GNOME” párbeszédablak megjelent"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"A kulcs azt határozza meg, hogy melyik verziónál jelent meg utoljára az "
+"„Üdvözli a GNOME” párbeszédablak. Az üres karakterlánc a lehető legrégebbi "
+"verziót jelenti, egy nagy szám pedig egy még meg sem jelent verziót. Ez a "
+"nagy szám gyakorlatilag letilthatja a párbeszédablakot."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Az alkalmazásválasztó elrendezése"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Az alkalmazásválasztó elrendezése. A tömbben minden bejegyzés egy oldal. Az "
+"oldalak olyan sorrendben vannak tárolva, ahogy megjelennek a GNOME Shellben. "
+"Minden egyes oldal tartalmaz egy „alkalmazás-azonosító” → „adat” párt. "
+"Jelenleg a következő értékek vannak tárolva „adatként”: • „pozíció”: az "
+"oldalon lévő alkalmazásikon pozíciója"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Billentyűtársítás az alkalmazásmenü megnyitásához"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Billentyűtársítás az alkalmazásmenü megnyitásához."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Billentyűtársítás az áttekintés állapotai közti váltáshoz"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Billentyűtársítás a munkamenet, az ablakválasztó és az alkalmazásrács közti "
+"váltáshoz"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Billentyűtársítás az alkalmazásrács, az ablakválasztó és a munkamenet közti "
+"váltáshoz"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Billentyűtársítás az „Alkalmazások megjelenítése” nézet megnyitásához"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Billentyűtársítás a Tevékenységek áttekintés „Alkalmazások megjelenítése” "
+"nézetének megnyitásához."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Billentyűtársítás az áttekintés megnyitásához"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Billentyűtársítás a Tevékenységek áttekintés megnyitásához."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Billentyűtársítás az értesítési lista láthatóságának módosításához"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Billentyűtársítás az értesítési lista láthatóságának módosításához."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Billentyűtársítás az aktív értesítés fókuszálásához"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Billentyűtársítás az aktív értesítés fókuszálásához."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Váltás az 1. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Váltás a 2. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Váltás a 3. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Váltás a 4. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Váltás az 5. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Váltás a 6. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Váltás a 7. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Váltás a 8. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Váltás a 9. alkalmazásra"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Váltó korlátozása a jelenlegi munkaterületre."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ha igaz, akkor csak azok az alkalmazások lesznek megjelenítve a váltón, "
+"amelyek ablakokkal rendelkeznek az aktuális munkaterületen. Egyébként minden "
+"alkalmazás fel van véve."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Az alkalmazás ikon mód."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Az ablakok megjelenésének beállítása a váltóban. Lehetséges értékek: "
+"„thumbnail-only” (az ablak bélyegképének megjelenítése), „app-icon-only” (az "
+"alkalmazás ikonjának megjelenítése) vagy „both” (mindkettő)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ha igaz, akkor csak az aktuális munkaterületről származó ablakok lesznek "
+"megjelenítve a váltón. Egyébként minden ablak fel lesz véve."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Helyek"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "A világórában megjelenítendő helyek"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatikus hely"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Lekérje-e a jelenlegi helyet"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Hely"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "A hely, amelyhez meg kell jeleníteni az előrejelzést"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Kizárólagos párbeszédablak csatolása a szülő ablakhoz"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ez a kulcs felülbírálja az org.gnome.mutter helyen lévő kulcsot, amikor a "
+"GNOME Shell fut."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Szélek csempézésének engedélyezése ablakok képernyőszélekre ejtésekor"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Munkaterületek dinamikus kezelése"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Munkaterületek csak az elsődleges monitoron"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Fókuszváltozások késleltetése a mutató mozgásának megállásáig egér módban"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Hálózati bejelentkezés"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Valami elromlott"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Nagyon sajnáljuk, de probléma történt: a kiterjesztés beállításait nem lehet "
+"megjeleníteni. Azt ajánljuk, hogy jelentse a hibát a kiterjesztés szerzőinek."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Technikai részletek"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Honlap"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "A kiterjesztés honlapjának megtekintése"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Mégse"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Jelszó"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Válasszon munkamenetet"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Nincs a listán?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(például: felhasználó vagy %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Felhasználónév"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Bejelentkezési ablak"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Hitelesítési hiba"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(vagy húzza le az ujját az olvasón)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(vagy húzza le az ujját az olvasón)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Kikapcsolás"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "kikapcsolás;leállítás;megállítás"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Újraindítás"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "újraindítás;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Zárolási képernyő"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "képernyő zárolása;képernyő;zárolás"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Kijelentkezés"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "kijelentkezés;kilépés"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Felfüggesztés"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "felfüggesztés;alvás"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Felhasználóváltás"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "felhasználóváltás"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "tájolás zárolása;tájolás feloldása;képernyő;tájolás;forgatás"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Képernyőkép készítése"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "képernyőkép;képernyőfelvétel;metszet;felvétel;rögzítés;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Képernyőforgatás feloldása"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Képernyőforgatás zárolása"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "A parancs nem található"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "A parancs nem dolgozható fel:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "„%s” végrehajtása meghiúsult:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Épp most"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d perce"
+msgstr[1] "%d perce"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d órája"
+msgstr[1] "%d órája"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Tegnap"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d napja"
+msgstr[1] "%d napja"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d hete"
+msgstr[1] "%d hete"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d hónapja"
+msgstr[1] "%d hónapja"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d éve"
+msgstr[1] "%d éve"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Tegnap %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d. %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%Y. %B %-d. %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Tegnap %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d. %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%Y. %B %-d. %H:%M"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Hotspot bejelentkezés"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"A kapcsolata ehhez a hotspot bejelentkezéshez nem biztonságos. Az ezen az "
+"oldalon megadott jelszavakat és más információkat a közelben lévő emberek is "
+"láthatják."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Hozzáférés tiltása"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Hozzáférés megadása"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Névtelen mappa"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "A(z) %s ki lett tűzve a Dashre."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "A(z) %s el lett távolítva a Dashről."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Ablakok megnyitása"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Új ablak"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Részletek megjelenítése"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Kilépés"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Kitűzés megszüntetése"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Kitűzés a Dashre"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Futtatás integrált videokártya használatával"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Futtatás diszkrét videokártya használatával"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Válasszon hangeszközt"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Hangbeállítások"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Fülhallgató"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Fejhallgató"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Háttér megváltoztatása…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Kijelző beállításai"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Beállítások"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "H"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "K"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Sz"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Cs"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Sz"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%Y. %OB"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Előző hónap"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Következő hónap"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V. hét"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Nincsenek értesítések"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Ne zavarjanak"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Törlés"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "„%s” nem válaszol."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Várhat egy kicsit a folytatódására, vagy kikényszerítheti az alkalmazás "
+"teljes kilépését."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Erőltetett kilépés"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Várakozás"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Külső meghajtó csatlakoztatva"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Külső meghajtó leválasztva"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Nem lehet feloldani a kötetet"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "A telepített udisks verzió nem támogatja a PIM beállítást"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Megnyitás ezzel: %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Alternatívaként kapcsolódhat a „WPS” gomb megnyomásával az eszközön."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Kapcsolódás"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Kulcs"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Személyes kulcs jelszava"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Személyazonosság"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Szolgáltatás"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Hitelesítés szükséges"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Jelszavak vagy titkosítási kulcsok szükségesek a(z) „%s” vezeték nélküli "
+"hálózat eléréséhez."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Vezetékes 802.1X hitelesítés"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Hálózat neve"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL hitelesítés"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN kód szükséges"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN kód szükséges a mobil széles sávú eszközhöz"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Jelszó szükséges a kapcsolódáshoz a következőhöz: „%s”."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Hálózatkezelő"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN jelszó"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Hitelesítés szükséges"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Rendszergazda"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Hitelesítés"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Elnézést, ez nem sikerült. Próbálja újra."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s mostantól %s néven ismert"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Ablakok"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Alkalmazások megjelenítése"
+
+# FIXME - valami jobbat
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%Y. %B %-d."
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%Y. %B %-e. %A"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d."
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y. %B %-d."
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Ma"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Holnap"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Egész nap"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m. %d."
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nincsenek események"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Világórák hozzáadása…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Világórák"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Betöltés…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Időjárás-információkért kapcsolódjon az internetre"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Az időjárás-információk jelenleg nem érhetőek el"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Időjárás"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Válasszon időjárási helyet…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s kijelentkeztetése"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Kijelentkezés"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s automatikusan ki fog jelentkezni %d másodperc múlva."
+msgstr[1] "%s automatikusan ki fog jelentkezni %d másodperc múlva."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Automatikusan ki fog jelentkezni %d másodperc múlva."
+msgstr[1] "Automatikusan ki fog jelentkezni %d másodperc múlva."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Kijelentkezés"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Kikapcsolás"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Frissítések telepítése és kikapcsolás"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "A rendszer automatikusan kikapcsol %d másodperc múlva."
+msgstr[1] "A rendszer automatikusan kikapcsol %d másodperc múlva."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Függő szoftverfrissítések telepítése"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Kikapcsolás"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Újraindítás"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Frissítések telepítése és újraindítás"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "A rendszer automatikusan újraindul %d másodperc múlva."
+msgstr[1] "A rendszer automatikusan újraindul %d másodperc múlva."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Újraindítás"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Újraindítás és frissítések telepítése"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"A rendszer %d másodperc múlva automatikusan újraindul és telepíti a "
+"frissítéseket."
+msgstr[1] ""
+"A rendszer %d másodperc múlva automatikusan újraindul és telepíti a "
+"frissítéseket."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Újraindítás és telepítés"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Telepítés és kikapcsolás"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Kikapcsolás a frissítések telepítése után"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Újraindítás és frissítések telepítése"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"A(z) %s %s újraindítás után lesz telepítve. A frissítések telepítése "
+"eltarthat egy ideig: győződjön meg arról, hogy készített-e biztonsági "
+"mentést, valamint a számítógép be van-e dugva."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Alacsony telepfeszültség: a frissítések telepítése előtt dugja be eszközét a "
+"konnektorba."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Egyes alkalmazások elfoglaltak, vagy mentetlen munkát tartalmaznak"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Más felhasználók vannak bejelentkezve"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Indítási beállítások"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (távoli)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konzol)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Telepítés"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Kiterjesztés telepítése"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+"Letölti és telepíti a következőt az extensions.gnome.org webhelyről: „%s”?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Kiterjesztés-frissítések érhetők el"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "A kiterjesztés-frissítések telepítésre készek."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Gyorsbillentyűk megakadályozásának engedélyezése"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "A(z) %s alkalmazás meg akarja gátolni a gyorsbillentyűk használatát"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Egy alkalmazás meg akarja gátolni a gyorsbillentyűk használatát"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "A(z) %s megnyomásával visszaállíthatja a gyorsbillentyűket."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Tiltás"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Engedélyezés"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Lassú billentyűk be"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Lassú billentyűk ki"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Lenyomva tartotta a Shift billentyűt 8 másodpercig. Ez a Lassú billentyűk "
+"szolgáltatás gyorsbillentyűje, ami a billentyűzet működését befolyásolja."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Ragadós billentyűk be"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Ragadós billentyűk ki"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Egymás után ötször nyomta meg a Shift billentyűt. Ez a Ragadós billentyűk "
+"szolgáltatás gyorsbillentyűje, ami a billentyűzet viselkedését befolyásolja."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Egyszerre két billentyűt nyomott meg, vagy a Shift billentyűt egymás után "
+"ötször. Ez kikapcsolja a Ragadós billentyűk szolgáltatást, ami a "
+"billentyűzet viselkedését befolyásolja."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Bekapcsolva hagyás"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Bekapcsolás"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Kikapcsolás"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Kikapcsolva hagyás"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Területi és nyelvi beállítások"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nincsenek kiterjesztések telepítve"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nem adott hibát."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Hibák elrejtése"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Hibák megjelenítése"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Engedélyezve"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Tiltva"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Hiba"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Elavult"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Letöltés"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Forrás megtekintése"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Weblap"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "A rendszer nem biztonságos módba lett állítva"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Az alkalmazásoknak most már korlátlan hozzáférésük van"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Visszavonás"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Bejelentkezés privilegizált felhasználóként"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"A privilegizált felhasználóként futtatott munkameneteket biztonsági okokból "
+"érdemes kerülni. Ha lehetséges, jelentkezzen be normál felhasználóként."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Képernyőzárolás letiltva"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "A képernyőzároláshoz a GNOME kijelzőkezelő szükséges."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Rendszerinformációk"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Ismeretlen előadó"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Ismeretlen cím"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Gépeljen a kereséshez"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Alkalmazások"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Áttekintés"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Új gyorsbillentyű…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Alkalmazás által meghatározott"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Súgó megjelenítése"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Kijelzőváltás"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Billentyűleütés hozzárendelése"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Kész"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Szerkesztés…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nincs"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Válasszon egy beállítandó gombot"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Nyomja meg az Esc gombot a kilépéshez"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Nyomjon meg egy gombot a kilépéshez"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Tevékenységek"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Rendszer"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Felső sáv"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Egy parancs futtatása"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Nyomja meg az Esc gombot a bezáráshoz"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Az újraindítás nem érhető el Wayland alatt"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Újraindítás…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "A GNOME-nak zárolnia kell a képernyőt"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Nem lehet zárolni"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "A zárolást egy alkalmazás blokkolta"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Kijelölés"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Terület kijelölése"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Képernyő"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Képernyő kijelölése"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Ablak"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Ablak kijelölése"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Képernyőkép vagy képernyőfelvétel"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Mutató megjelenítése"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Képernyőfelvételek"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Képernyőfelvétel ekkor: %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Képernyőkép"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Képernyőfelvétel rögzítve"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Kattintson ide a videó megtekintéséhez."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Megjelenítés a fájlokban"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Képernyőkép ekkor: %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Képernyőkép rögzítve"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Beillesztheti a képet a vágólapról."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Képernyőkép elkészült"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Keresés…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Nincs találat."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "még %d további"
+msgstr[1] "még %d további"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Oldalsáv"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Másolás"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Beillesztés"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Szöveg megjelenítése"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Szöveg elrejtése"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "A Caps Lock be van kapcsolva."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Rejtett kötet"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows rendszerkötet"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Kulcsfájlokat használ"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"A kulcsfájlokat használó kötetek feloldásához használja inkább a(z) <i>%s</"
+"i> segédprogramot."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-szám"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Jelszó megjegyzése"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Feloldás"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s megnyitása"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "A PIM-nek számnak vagy üresnek kell lennie."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "A(z) %s nem indítható el"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nem sikerült megtalálni a(z) %s alkalmazást"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Akadálymentesítés"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Nagy kontraszt"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Nagyítás"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Képernyőolvasó"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Képernyő-billentyűzet"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Vizuális figyelmeztetések"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Ragadós billentyűk"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Lassú billentyűk"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Billentyűszűrés"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Egérbillentyűk"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Nagy szöveg"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatikus forgatás"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Fényerő"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Sötét mód"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Egy kattintás"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dupla kattintás"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Húzás"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Másodlagos kattintás"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Rámutatási kattintás"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Billentyűzet"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Billentyűzetkiosztás megjelenítése"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Helyadatok elérésének engedélyezése"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "A(z) %s alkalmazás el szeretné érni a helyadatait"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"A helyadatok elérése bármikor megváltoztatható az adatvédelmi beállításokban."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<ismeretlen>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "%s leválasztása"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Kapcsolódás ehhez: %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s hotspot"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+#| msgid "PIN"
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+#| msgid "Settings"
+msgid "VPN Settings"
+msgstr "VPN beállítások"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Összes hálózat"
+
+#: js/ui/status/network.js:1815
+#| msgid "Window Selection"
+msgid "Wired Connections"
+msgstr "Vezetékes kapcsolatok"
+
+#: js/ui/status/network.js:1816
+#| msgid "Settings"
+msgid "Wired Settings"
+msgstr "Vezetékes beállítások"
+
+#: js/ui/status/network.js:1830
+#| msgid "Bluetooth"
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth alapú internetmegosztók"
+
+#: js/ui/status/network.js:1831
+#| msgid "Bluetooth"
+msgid "Bluetooth Settings"
+msgstr "Bluetooth beállítások"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Mobil kapcsolatok"
+
+#: js/ui/status/network.js:1847
+#| msgid "Sound Settings"
+msgid "Mobile Broadband Settings"
+msgstr "Mobil széles sáv beállítások"
+
+#: js/ui/status/network.js:1959
+#| msgid "Connect to %s"
+msgid "Connection failed"
+msgstr "A kapcsolódás sikertelen"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "A hálózati kapcsolat aktiválása sikertelen"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Éjszakai fény"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Teljesítmény"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Kiegyensúlyozott"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Energiatakarékos"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Teljesítményprofil"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Képernyőfelvétel leállítása"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Képernyőmegosztás leállítása"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Repülőgép mód"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Képernyőkép készítése"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Kikapcsolás menü"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Felfüggesztés"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Újraindítás…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Kikapcsolás…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Kijelentkezés…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Felhasználóváltás…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Zárolási képernyő"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Ismeretlen thunderbolt eszköz"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Új eszköz lett felismerve, amíg távol volt. Válassza le, és csatlakoztassa "
+"újra az eszközt, hogy megkezdhesse a használatát."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Jogosulatlan Thunderbolt eszköz"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Új eszköz lett észlelve, de egy rendszergazdának hitelesítenie szükséges."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt engedélyezési hiba"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nem sikerült a Thunderbolt eszköz engedélyezése: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Hangerő megváltozott"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Hangerő"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Hangkimenet"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Hangbemenet"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Tükrözés"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Kijelzők egyesítése"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Csak a külső"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Csak a beépített"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%B %-d. %A"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Húzza felfelé a feloldáshoz"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Kattintson vagy nyomjon meg egy billentyűt a feloldáshoz"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Ablak feloldása"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Bejelentkezés más felhasználóként"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Üdvözli a GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Ha szeretné kiismerni, hogy mi merre van, nézze meg a bemutatót."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Köszönöm, nem"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Bemutató megtekintése"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "„%s” kész"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Megtartja ezen beállításokat?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Beállítások visszavonása"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Módosítások megtartása"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "A beállítások módosításai %d másodperc múlva visszavonásra kerülnek"
+msgstr[1] "A beállítások módosításai %d másodperc múlva visszavonásra kerülnek"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Elrejtés"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Visszaállítás"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximalizálás"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Áthelyezés"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Átméretezés"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Címsor mozgatása a képernyőre"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Mindig felül"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Mindig a látható munkaterületen"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Áthelyezés a bal oldali munkaterületre"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Áthelyezés a jobb oldali munkaterületre"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Áthelyezés a felső munkaterületre"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Áthelyezés az alsó munkaterületre"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Áthelyezés a felső kijelzőre"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Áthelyezés az alsó kijelzőre"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Áthelyezés a bal oldali kijelzőre"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Áthelyezés a jobb oldali kijelzőre"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Bezárás"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution naptár"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Verzió kiírása"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "A GDM által a bejelentkezési képernyőhöz használt mód"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Használjon egy adott módot, például a „gdm”-et a bejelentkező képernyőn"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Lehetséges módok listázása"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ismeretlen"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "„%s” indítása meghiúsult"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "A jelszavak nem egyeznek."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "A jelszó nem lehet üres"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "A hitelesítési ablakot a felhasználó bezárta"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Kiterjesztések"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Saját GNOME kiterjesztések kezelése"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "A GNOME projekt"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"A GNOME kiterjesztések kezeli a kiterjesztések frissítését, a "
+"kiterjesztésbeállítások konfigurálást és a nem kívánatos kiterjesztések "
+"eltávolítását vagy letiltását."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Shell kiterjesztések beállítása"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Nincs találat"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Eltávolítja ezt: „%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Ha eltávolítja a kiterjesztést, akkor vissza kell térnie a letöltéshez, ha "
+"újra engedélyezni akarja."
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Eltávolítás"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Meskó Balázs <mesko dot balazs at fsf dot hu>\n"
+"Úr Balázs <ur dot balazs at fsf dot hu>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d kiterjesztés lesz frissítve a következő bejelentkezéskor."
+msgstr[1] "%d kiterjesztés lesz frissítve a következő bejelentkezéskor."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "A kiterjesztés nem kompatibilis a jelenlegi GNOME verzióval"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "A kiterjesztésnek hibája volt"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "A kiterjesztés frissíthető"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Weboldal"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Eltávolítás…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Súgó"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "A kiterjesztések névjegye"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"A kiterjesztések teljesítménybeli és stabilitási problémákat okozhatnak. "
+"Tiltsa le a kiterjesztéseket, ha problémákat tapasztal a rendszerével "
+"kapcsolatban."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Kézileg telepített"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"A kiterjesztések megtalálásához és hozzáadásához keresse fel az <a "
+"href=\"https://extensions.gnome.org\">extensions.gnome.org</a> oldalt."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Beépített"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Nincsenek telepített kiterjesztések"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Sajnáljuk, de nem volt lehetséges lekérni a telepített kiterjesztések "
+"listáját. Győződjön meg róla, hogy bejelentkezett a GNOME-ba, és próbálja "
+"meg újra."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "A kiterjesztésfrissítések készen állnak"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Az új kiterjesztés sikeresen létrehozva itt: %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"A névnek egy rövid (ideális esetben kifejező) karakterláncnak kell lennie.\n"
+"Példák: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Név"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"A leírás egy egymondatos magyarázat a kiterjesztés működéséről.\n"
+"Példák: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Leírás"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"A UUID a kiegészítő globálisan egyedi azonosítója.\n"
+"E-mail cím formátumúnak kell lennie (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Válassza ki az elérhető sablonok egyikét:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Sablon"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Az új kiterjesztés egyedi azonosítója"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NÉV"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Az új kiterjesztés felhasználók számára látható neve"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "LEÍRÁS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Rövid leírás arról, hogy a kiterjesztés mit csinál"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "SABLON"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Az új kiterjesztéshez használandó sablon"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Adja meg a kiterjesztés információit interaktív módon"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Új kiterjesztés létrehozása"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Ismeretlen argumentumok"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "A UUID, név és leírás kötelező"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "A(z) „%s” kiterjesztés nem létezik\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Kiterjesztés letiltása"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nincs UUID megadva"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Több mint egy UUID lett megadva"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Kiterjesztés engedélyezése"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Nem sikerült kapcsolódni a GNOME Shellhez\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "A(z) „%s” kiterjesztés nem létezik\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Kiterjesztésinformációk megjelenítése"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Meglévő kiterjesztés felülírása"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "KITERJESZTÉSCSOMAG"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Kiterjesztéscsomag telepítése"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nincs megadva kiterjesztéscsomag"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Több mint egy kiterjesztéscsomag lett megadva"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Felhasználó által telepített kiterjesztések megjelenítése"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Rendszer által telepített kiterjesztések megjelenítése"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Engedélyezett kiterjesztések megjelenítése"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Letiltott kiterjesztések megjelenítése"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Beállításokkal rendelkező kiterjesztések megjelenítése"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Frissítésekkel rendelkező kiterjesztések megjelenítése"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Kiterjesztés részleteinek kiírása"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Telepített kiterjesztések felsorolása"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FÁJL"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "További hozzáadandó források a csomaghoz"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SÉMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Csomaghoz adandó GSettings séma"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "KÖNYVTÁR"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "A könyvtár, amely a fordításokat tartalmazza"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "TARTOMÁNY"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "A fordításokhoz használandó gettext tartomány"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Létező csomag felülírása"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "A könyvtár, ahol a csomagot létre kell hozni"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "FORRÁS_KÖNYVTÁR"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Kiterjesztéscsomag létrehozása"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Több mint egy forráskönyvtár lett megadva"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "A(z) „%s” kiterjesztésnek nincsenek beállításai\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "A(z) „%s” kiterjesztés beállításainak megnyitása sikertelen: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Megnyitja a kiterjesztés beállításait"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Kiterjesztés visszaállítása"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Nem lehet eltávolítani a rendszerkiterjesztéseket\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "A(z) „%s” eltávolítása sikertelen\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Kiterjesztés eltávolítása"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ne írjon ki hibaüzeneteket"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Útvonal"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Eredeti szerző"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Verzió"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Állapot"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "a „version” nem fogad argumentumokat"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Használat:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Verzióinformációk kiírása és kilépés."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "PARANCS"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTUMOK…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Parancsok:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Súgó kiírása"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Kiterjesztés engedélyezése"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Kiterjesztés letiltása"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Kiterjesztés visszaállítása"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Kiterjesztés eltávolítása"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Kiterjesztések felsorolása"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Kiterjesztésinformációk megjelenítése"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Kiterjesztésbeállítások megnyitása"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Kiterjesztés létrehozása"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Kiterjesztés csomagolása"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Kiterjesztéscsomag telepítése"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Részletes súgóhoz használja ezt: „%s”.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Egyszerű"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Egy üres kiterjesztés"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Jelző"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Ikon hozzáadása a felső sávhoz"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u kimenet"
+msgstr[1] "%u kimenet"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u bemenet"
+msgstr[1] "%u bemenet"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Rendszerhangok"
diff --git a/po/ia.po b/po/ia.po
new file mode 100644
index 0000000..7007493
--- /dev/null
+++ b/po/ia.po
@@ -0,0 +1,1950 @@
+# Interlingua translations for gnome-shell package.
+# Copyright (C) 2013 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+# Nik Kalach <nikka@fedoraproject.org>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-04-23 19:57+0000\n"
+"PO-Revision-Date: 2013-04-23 08:00+0400\n"
+"Last-Translator: Nik Kalach <nikka@fedoraproject.org>\n"
+"Language-Team: Interlingua <trans-ia@lists.fedoraproject.org>\n"
+"Language: ia\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:1
+msgid "Screenshots"
+msgstr "Capturas de schermo"
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:2
+msgid "Record a screencast"
+msgstr "Registrar un video de schermo"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "Systema"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "Monstrar le tiratorio de messages"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "Mitter foco al notificationes active"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "Monstrar le panorama"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "Monstrar tote le applicationes"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "Aperir le menu de applicationes"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "Gestion de fenestras e lanceamento de applicationes"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+#: ../js/extensionPrefs/main.js:153
+msgid "GNOME Shell Extension Preferences"
+msgstr "Preferentias pro le extensiones GNOME Shell"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configurar le extensiones GNOME Shell"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Permitter le accesso via Alt-F2 al instrumentos interne que es utile pro "
+"disveloppatores e probatores"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permitter le accesso via le dialog de lanceamento de applicationes (Alt-F2) "
+"al instrumentos interne que es utile pro eliminar defectos de software e "
+"facer le surveliantia"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "Uuids of extensions to enable"
+msgstr "Identificatores UUID del extensiones a activar"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension DBus methods on org.gnome.Shell."
+msgstr ""
+"Le extensiones GNOME Shell ha un proprietate UUID. Iste clave enumera le "
+"extensiones que debe ser cargate. Tote le extensiones que vole ser cargate "
+"debe adjungar se a iste lista. Il pote anque manipular iste lista con le "
+"methodos DBus EnableExtension e DisableExtension de org.gnome.Shell."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Whether to collect stats about applications usage"
+msgstr "Colliger statisticas super le utilisation de applicationes"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+"Normalmente GNOME Shell survelia le applicationes active a fin de presentar "
+"le plus utilisate (per ex. in le lanceatores). Ben que le datos se guarda in "
+"privato, on pote disactivar iste functionalitate per le ration de "
+"confidentialitate. Remarca que iste disactivation non removera le datos jam "
+"registrate."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Lista de identificatores de files desktop pro le applicationes favorite"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Le applicationes que corresponde a iste identificatores essera monstrate in "
+"le area de favoritos."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "List of categories that should be displayed as folders"
+msgstr "Lista de categorias que deberea ser monstrate como dossiers"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid ""
+"Each category name in this list will be represented as folder in the "
+"application view, rather than being displayed inline in the main view."
+msgstr ""
+"Cata nomine de categoria in iste lista sera representate como un dossier in "
+"le vista de applicationes, plus tosto que de esser alineate in le vista "
+"principal."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historia del dialogo de commandos (Alt-F2)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "Historia del dialogo de inspection"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid ""
+"Internally used to store the last IM presence explicitly set by the user. "
+"The value here is from the TpConnectionPresenceType enumeration."
+msgstr ""
+"Illo se utilisa internemente pro registrar le ultime stato de presentia que "
+"es definite explicitemente per le usator in le systema de messages instante. "
+"Le valor hic es del enumeration TpConnectionPresenceType."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"Internally used to store the last session presence status for the user. The "
+"value here is from the GsmPresenceStatus enumeration."
+msgstr ""
+"Illo se utilisa internemente pro registrar le ultime stato de session del "
+"usator. Le valor hic es del enumeration GsmPresenceStatus."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid "Always show the 'Log out' menuitem in the user menu."
+msgstr ""
+"Sempre monstrar le elemento de menu 'Clauder le session' in le menu de "
+"usator."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menuitem in single-"
+"user, single-session situations."
+msgstr ""
+"Iste clave supplanta le celamento automatic del elemento de menu 'Clauder le "
+"session' in situationes con singule usator o singule session."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Si memorar le contrasigno pro montar le systemas de files cifrate o remote"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Le shell demandara un contrasigno pro montar un dispositivo cifrate o un "
+"systema de files remote. Si le contrasigno pote ser retenite pro uso futur "
+"un quadrato de selection 'Memorar le contrasigno' sera monstrate. Iste clave "
+"defini le stato predefinite del quadrato de selection."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Show the week date in the calendar"
+msgstr "Monstrar le numeration de septimanas in le calendario"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "If true, display the ISO week date in the calendar."
+msgstr "Si ver, monstrar le numero de septimana ISO in le calendario."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the application menu"
+msgstr "Association de claves pro aperir le menu de applicationes"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Keybinding to open the application menu."
+msgstr "Association de claves pro aperir le menu de applicationes."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "Association de claves pro aperir le vista \"Monstrar applicationes\""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"Association de claves pro aperir le vista \"Monstrar applicationes\" del "
+"panorama de activitates."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to open the overview"
+msgstr "Association de claves pro aperir le panorama"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to open the Activities Overview."
+msgstr "Association de claves pro aperir le panorama de activitates"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr ""
+"Association de claves pro commutar le visibilitate del tiratorio de messages"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr ""
+"Association de claves pro commutar le visibilitate del tiratorio de messages."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid "Keybinding to focus the active notification"
+msgstr "Association de claves pro mitter foco al notificationes active"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Keybinding to focus the active notification."
+msgstr "Association de claves pro mitter foco al notificationes active."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "Keybinding to toggle the screen recorder"
+msgstr "Association de claves pro commutar le registrator de schermo"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Keybinding to start/stop the builtin screen recorder."
+msgstr "Association de claves pro initiar/arrestar le registration de schermo."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid "Which keyboard to use"
+msgstr "Que claviero usar"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The type of keyboard to use."
+msgstr "Le typo de claviero a usar."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid "Framerate used for recording screencasts."
+msgstr "Rata de quadros usate pro registrar le video de schermo."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+"Le numero de imagines per secunda in le video de schermo registrate per le "
+"registrator incorporate in GNOME Shell."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr "Le catena usante per gstreamer pro codificar video de schermo"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+"threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is "
+"used as a placeholder for a guess at the optimal thread count on the system."
+msgstr ""
+"Le definition del catena usante per GStreamer pro codificar videos. Le "
+"syntaxe es identic al syntaxe usate per gst-launch. Le catena deberea haber "
+"un puncto de egresso disconnectite a ubi le video se scribe. Generalmente "
+"illo va haber un puncto de origine disconnectite; le egresso de iste puncto "
+"se scribera in le file de resultato. Totevia, le catena anque pote custodiar "
+"su proprie egresso - isto pote ser usate pro inviar le resultato a un "
+"servitor icecast via shout2send o simile. Quando le valor non es definite o "
+"vacue, le catena predefinite se usa. Isto es actualmente 'vp8enc "
+"min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! "
+"queue ! webmmux' e registra in WEBM usante le codec VP8. %T designa le "
+"numero de filos optimal pro le systema."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "File extension used for storing the screencast"
+msgstr "Extension de file a usar pro slaveguardar le video de schermo"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+"Le nomine de file del video registrate es unic. Illo se basa super le data "
+"currente e iste extension. Illo deberea esser modificate si le formato de "
+"container usate pro registar le video es differente."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "The application icon mode."
+msgstr "Le modo de icone in le selector de applicationes."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"Le configuration del maniera como le fenestras es monstrate in le selector. "
+"Le possibilitates valide es 'thumbnail-only' (monstrar un miniatura del "
+"fenestra), 'app-icon-only' (monstrar solmente un icone del application) o "
+"'both' (monstrar tote le duo)."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
+msgid "Attach modal dialog to the parent window"
+msgstr "Attaccar le dialogo modal al fenestra parental"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Iste clave supplanta le clave in org.gnome.mutter quando GNOME Shell es "
+"lanceate."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
+msgid "Arrangement of buttons on the titlebar"
+msgstr "Arrangiamento de buttones sur le barra de titulo"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
+msgid ""
+"This key overrides the key in org.gnome.desktop.wm.preferences when running "
+"GNOME Shell."
+msgstr ""
+"Iste clave supplanta le clave in org.gnome.desktop.wm.preferences quando "
+"GNOME Shell es lanceate."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Activar le tegulage de fenestras depositate sur le bordos del schermo"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:49
+msgid "Workspaces are managed dynamically"
+msgstr "Le spatios de travalio se gere dynamicamente"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:50
+msgid "Workspaces only on primary monitor"
+msgstr "Le spatios de travalio es solmente sur le schermo principal"
+
+#: ../js/extensionPrefs/main.js:125
+#, c-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "Il ha un error al cargamento del dialogo de preferentias pro %s:"
+
+#: ../js/extensionPrefs/main.js:165
+msgid "Extension"
+msgstr "Extension"
+
+#: ../js/extensionPrefs/main.js:189
+msgid "Select an extension to configure using the combobox above."
+msgstr "Selige un extension que configurar usante le lista combinate in alto."
+
+#: ../js/gdm/loginDialog.js:405
+msgid "Session…"
+msgstr "Session…"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:630
+msgid "Not listed?"
+msgstr "Esque le nomine non es in le lista?"
+
+#: ../js/gdm/loginDialog.js:787 ../js/ui/components/networkAgent.js:137
+#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:376
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:100
+#: ../js/ui/userMenu.js:938
+msgid "Cancel"
+msgstr "Annullar"
+
+#: ../js/gdm/loginDialog.js:803
+msgctxt "button"
+msgid "Sign In"
+msgstr "Connecter se"
+
+#: ../js/gdm/loginDialog.js:803
+msgid "Next"
+msgstr "Sequente"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/gdm/loginDialog.js:918 ../js/ui/components/networkAgent.js:260
+#: ../js/ui/components/networkAgent.js:278
+msgid "Username: "
+msgstr "Nomine de usator:"
+
+#: ../js/gdm/loginDialog.js:1174
+msgid "Login Window"
+msgstr "Fenestra de connexion"
+
+#. Translators: accessible name of the power menu in the login screen
+#: ../js/gdm/powerMenu.js:36
+msgid "Power"
+msgstr "Energia"
+
+#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
+#: ../js/ui/userMenu.js:816
+msgid "Suspend"
+msgstr "Suspender"
+
+#: ../js/gdm/powerMenu.js:98
+msgid "Restart"
+msgstr "Reinitiar"
+
+#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
+#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
+msgid "Power Off"
+msgstr "Extinguer"
+
+#: ../js/gdm/util.js:249
+msgid "Authentication error"
+msgstr "Error de authentication"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/util.js:366
+msgid "(or swipe finger)"
+msgstr "(o passa le digito trans le lector de impressiones)"
+
+#: ../js/gdm/util.js:391
+#, c-format
+msgid "(e.g., user or %s)"
+msgstr "(per ex., usator o %s)"
+
+#: ../js/misc/util.js:97
+msgid "Command not found"
+msgstr "Commando non trovate"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:130
+msgid "Could not parse command:"
+msgstr "Impossibile de analysar le commando:"
+
+#: ../js/misc/util.js:138
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "Falta al execution de '%s':"
+
+#: ../js/ui/appDisplay.js:351
+msgid "Frequent"
+msgstr "Frequente"
+
+#: ../js/ui/appDisplay.js:358
+msgid "All"
+msgstr "Tote"
+
+#: ../js/ui/appDisplay.js:916
+msgid "New Window"
+msgstr "Nove fenestra"
+
+#: ../js/ui/appDisplay.js:919 ../js/ui/dash.js:284
+msgid "Remove from Favorites"
+msgstr "Remover del favoritos"
+
+#: ../js/ui/appDisplay.js:920
+msgid "Add to Favorites"
+msgstr "Adder al favoritos"
+
+#: ../js/ui/appFavorites.js:87
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "%s ha essite addite al favoritos."
+
+#: ../js/ui/appFavorites.js:121
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "%s ha essite removite del favoritos."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
+msgid "Settings"
+msgstr "Parametros"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "Cambiar le fundo…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:62
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Jornata"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to :
+#: ../js/ui/calendar.js:68
+msgctxt "event list time"
+msgid "%H\\u2236%M"
+msgstr "%H\\u2236%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space
+#: ../js/ui/calendar.js:77
+msgctxt "event list time"
+msgid "%l\\u2236%M\\u2009%p"
+msgstr "%l\\u2236%M\\u2009%p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:108
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:110
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:112
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:114
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:116
+msgctxt "grid thursday"
+msgid "T"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:118
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:120
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:133
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Do"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:135
+msgctxt "list monday"
+msgid "M"
+msgstr "Lu"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:137
+msgctxt "list tuesday"
+msgid "T"
+msgstr "Ma"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:139
+msgctxt "list wednesday"
+msgid "W"
+msgstr "Me"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:141
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Jo"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:143
+msgctxt "list friday"
+msgid "F"
+msgstr "Ve"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:145
+msgctxt "list saturday"
+msgid "S"
+msgstr "Sa"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:720
+msgid "Nothing Scheduled"
+msgstr "Nihil planate"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:736
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, le %d de %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:739
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, le %d de %B %Y"
+
+#: ../js/ui/calendar.js:749
+msgid "Today"
+msgstr "Hodie"
+
+#: ../js/ui/calendar.js:753
+msgid "Tomorrow"
+msgstr "Deman"
+
+#: ../js/ui/calendar.js:764
+msgid "This week"
+msgstr "Iste septimana"
+
+#: ../js/ui/calendar.js:772
+msgid "Next week"
+msgstr "Le septimana proxime"
+
+#: ../js/ui/components/automountManager.js:90
+msgid "External drive connected"
+msgstr "Disco externe connectite"
+
+#: ../js/ui/components/automountManager.js:101
+msgid "External drive disconnected"
+msgstr "Disco externe disconnectite"
+
+#: ../js/ui/components/autorunManager.js:294
+msgid "Removable Devices"
+msgstr "Dispositivo removibile"
+
+#: ../js/ui/components/autorunManager.js:594
+#, c-format
+msgid "Open with %s"
+msgstr "Aperir con %s"
+
+#: ../js/ui/components/autorunManager.js:620
+msgid "Eject"
+msgstr "Ejacular"
+
+#: ../js/ui/components/keyring.js:82 ../js/ui/components/polkitAgent.js:268
+msgid "Password:"
+msgstr "Contrasigno:"
+
+#: ../js/ui/components/keyring.js:101
+msgid "Type again:"
+msgstr "Introduce de novo:"
+
+#: ../js/ui/components/networkAgent.js:132
+msgid "Connect"
+msgstr "Connecter"
+
+#. Cisco LEAP
+#: ../js/ui/components/networkAgent.js:223
+#: ../js/ui/components/networkAgent.js:235
+#: ../js/ui/components/networkAgent.js:262
+#: ../js/ui/components/networkAgent.js:282
+#: ../js/ui/components/networkAgent.js:292
+msgid "Password: "
+msgstr "Contrasigno: "
+
+#. static WEP
+#: ../js/ui/components/networkAgent.js:228
+msgid "Key: "
+msgstr "Clave: "
+
+#: ../js/ui/components/networkAgent.js:266
+msgid "Identity: "
+msgstr "Identitate: "
+
+#: ../js/ui/components/networkAgent.js:268
+msgid "Private key password: "
+msgstr "Contrasigno del clave private: "
+
+#: ../js/ui/components/networkAgent.js:280
+msgid "Service: "
+msgstr "Servicio: "
+
+#: ../js/ui/components/networkAgent.js:309
+msgid "Authentication required by wireless network"
+msgstr "Le authentication es requirite per le rete sin filo"
+
+#: ../js/ui/components/networkAgent.js:310
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"'%s'."
+msgstr ""
+"Contrasignos o claves de ciframento es necessari pro acceder al rete sin "
+"filo '%s'."
+
+#: ../js/ui/components/networkAgent.js:314
+msgid "Wired 802.1X authentication"
+msgstr "Authentication 802.1X pro rete con filo"
+
+#: ../js/ui/components/networkAgent.js:316
+msgid "Network name: "
+msgstr "Nomine de rete: "
+
+#: ../js/ui/components/networkAgent.js:321
+msgid "DSL authentication"
+msgstr "Authentication DSL"
+
+#: ../js/ui/components/networkAgent.js:328
+msgid "PIN code required"
+msgstr "Codice PIN es necessari"
+
+#: ../js/ui/components/networkAgent.js:329
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Un codice PIN es necessari pro le dispositivo mobile"
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:336
+msgid "Mobile broadband network password"
+msgstr "Contrasigno del rete mobile"
+
+#: ../js/ui/components/networkAgent.js:337
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr "Un contrasigno es necessari pro connecter a '%s'."
+
+#: ../js/ui/components/polkitAgent.js:55
+msgid "Authentication Required"
+msgstr "Authentication necessari"
+
+#: ../js/ui/components/polkitAgent.js:93
+msgid "Administrator"
+msgstr "Administrator"
+
+#: ../js/ui/components/polkitAgent.js:165
+msgid "Authenticate"
+msgstr "Authenticar se"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/components/polkitAgent.js:256 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Error de authentication. Tenta de novo."
+
+#. Translators: this is a filename used for screencast recording
+#: ../js/ui/components/recorder.js:47
+#, no-c-format
+msgid "Screencast from %d %t"
+msgstr "Video de schermo %d %t"
+
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/components/telepathyClient.js:238
+msgid "Invitation"
+msgstr "Invitation"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:298
+msgid "Call"
+msgstr "Appello"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:314
+msgid "File Transfer"
+msgstr "Transferentia de files"
+
+#: ../js/ui/components/telepathyClient.js:418
+msgid "Chat"
+msgstr "Discussion"
+
+#: ../js/ui/components/telepathyClient.js:480
+msgid "Unmute"
+msgstr "Facer non silente"
+
+#: ../js/ui/components/telepathyClient.js:480
+msgid "Mute"
+msgstr "Silentiar"
+
+#. Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"
+#: ../js/ui/components/telepathyClient.js:942
+#, no-c-format
+msgid "<b>Yesterday</b>, <b>%H:%M</b>"
+msgstr "<b>Heri</b>, <b>%H:%M</b>"
+
+#. Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30
+#: ../js/ui/components/telepathyClient.js:948
+#, no-c-format
+msgid "<b>%A</b>, <b>%H:%M</b>"
+msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#. Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"
+#: ../js/ui/components/telepathyClient.js:953
+#, no-c-format
+msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+msgstr "<b>%d</b> de <b>%B</b>, <b>%H:%M</b>"
+
+#. Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"
+#: ../js/ui/components/telepathyClient.js:957
+#, no-c-format
+msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+msgstr "<b>%d</b> de <b>%B</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/components/telepathyClient.js:985
+#, c-format
+msgid "%s is now known as %s"
+msgstr "%s es ora cognite como %s"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1088
+#, c-format
+msgid "Invitation to %s"
+msgstr "Invitation a %s"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/components/telepathyClient.js:1096
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr "%s te es inviante a %s"
+
+#: ../js/ui/components/telepathyClient.js:1098
+#: ../js/ui/components/telepathyClient.js:1137
+#: ../js/ui/components/telepathyClient.js:1177
+#: ../js/ui/components/telepathyClient.js:1240
+msgid "Decline"
+msgstr "Declinar"
+
+#: ../js/ui/components/telepathyClient.js:1099
+#: ../js/ui/components/telepathyClient.js:1178
+#: ../js/ui/components/telepathyClient.js:1241
+msgid "Accept"
+msgstr "Acceptar"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1129
+#, c-format
+msgid "Video call from %s"
+msgstr "Appello video de %s"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1132
+#, c-format
+msgid "Call from %s"
+msgstr "Appello de %s"
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/components/telepathyClient.js:1139
+msgid "Answer"
+msgstr "Responder"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/components/telepathyClient.js:1171
+#, c-format
+msgid "%s is sending you %s"
+msgstr "%s te es inviante %s"
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/components/telepathyClient.js:1206
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s sollicita le permission de vider quando tu es in linea"
+
+#: ../js/ui/components/telepathyClient.js:1298
+msgid "Network error"
+msgstr "Error de rete"
+
+#: ../js/ui/components/telepathyClient.js:1300
+msgid "Authentication failed"
+msgstr "Falta de authentication"
+
+#: ../js/ui/components/telepathyClient.js:1302
+msgid "Encryption error"
+msgstr "Error de ciframento"
+
+#: ../js/ui/components/telepathyClient.js:1304
+msgid "Certificate not provided"
+msgstr "Certificato non fornite"
+
+#: ../js/ui/components/telepathyClient.js:1306
+msgid "Certificate untrusted"
+msgstr "Certificato indigne de fide"
+
+#: ../js/ui/components/telepathyClient.js:1308
+msgid "Certificate expired"
+msgstr "Certificato perimite"
+
+#: ../js/ui/components/telepathyClient.js:1310
+msgid "Certificate not activated"
+msgstr "Certificato non activate"
+
+#: ../js/ui/components/telepathyClient.js:1312
+msgid "Certificate hostname mismatch"
+msgstr "Discordantia de nomine de host del certificato"
+
+#: ../js/ui/components/telepathyClient.js:1314
+msgid "Certificate fingerprint mismatch"
+msgstr "Discordantia de impression digital del certificato"
+
+#: ../js/ui/components/telepathyClient.js:1316
+msgid "Certificate self-signed"
+msgstr "Certificato autofirmate"
+
+#: ../js/ui/components/telepathyClient.js:1318
+msgid "Status is set to offline"
+msgstr "Le stato es disconnexe"
+
+#: ../js/ui/components/telepathyClient.js:1320
+msgid "Encryption is not available"
+msgstr "Le ciframento non es disponibile"
+
+#: ../js/ui/components/telepathyClient.js:1322
+msgid "Certificate is invalid"
+msgstr "Le certificato non es valide"
+
+#: ../js/ui/components/telepathyClient.js:1324
+msgid "Connection has been refused"
+msgstr "Le connexion ha essite refusate"
+
+#: ../js/ui/components/telepathyClient.js:1326
+msgid "Connection can't be established"
+msgstr "Le connexion non pote ser establite"
+
+#: ../js/ui/components/telepathyClient.js:1328
+msgid "Connection has been lost"
+msgstr "Le connexion se ha perdite"
+
+#: ../js/ui/components/telepathyClient.js:1330
+msgid "This account is already connected to the server"
+msgstr "Iste conto jam es connectite al servitor"
+
+#: ../js/ui/components/telepathyClient.js:1332
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr ""
+"Le connexion ha essite substituite per un nove connexion usante le mesme "
+"ressource"
+
+#: ../js/ui/components/telepathyClient.js:1334
+msgid "The account already exists on the server"
+msgstr "Le conto jam existe sur le servitor"
+
+#: ../js/ui/components/telepathyClient.js:1336
+msgid "Server is currently too busy to handle the connection"
+msgstr "Actualmente le servitor es troppo occupate pro tractar le connexion"
+
+#: ../js/ui/components/telepathyClient.js:1338
+msgid "Certificate has been revoked"
+msgstr "Le certificato ha essite revocate"
+
+#: ../js/ui/components/telepathyClient.js:1340
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"Le certificato usa un algorithmo de ciframento insecur o es "
+"cryptographicamente debile"
+
+#: ../js/ui/components/telepathyClient.js:1342
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"Le longitude del certificato de servitor o le profunditate del catena de "
+"certificatos de servitor excede le limites imponite per le bibliotheca de "
+"cryptographia"
+
+#: ../js/ui/components/telepathyClient.js:1344
+msgid "Internal error"
+msgstr "Error interne"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1354
+#, c-format
+msgid "Unable to connect to %s"
+msgstr "Impossibile de connecter a %s"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "View account"
+msgstr "Examinar le conto"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "Unknown reason"
+msgstr "Causa incognite"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96
+msgid "Windows"
+msgstr "Fenestras"
+
+#: ../js/ui/dash.js:248 ../js/ui/dash.js:286
+msgid "Show Applications"
+msgstr "Monstrar applicationes"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/dash.js:435
+msgid "Dash"
+msgstr "Pannello"
+
+#: ../js/ui/dateMenu.js:91
+msgid "Open Calendar"
+msgstr "Aperir calendario"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Clocks"
+msgstr "Aperir horologios"
+
+#: ../js/ui/dateMenu.js:105
+msgid "Date & Time Settings"
+msgstr "Parametros de data e hora"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:215
+msgid "%A %B %e, %Y"
+msgstr "%A, %e de %B %Y"
+
+#: ../js/ui/endSessionDialog.js:63
+#, c-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Clauder le session de %s"
+
+#: ../js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Log Out"
+msgstr "Clauder le session"
+
+#: ../js/ui/endSessionDialog.js:65
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr ""
+"Clicca sur 'Clauder le session' pro quitar iste applicationes e sortir del "
+"systema."
+
+#: ../js/ui/endSessionDialog.js:67
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Le session de %s sera terminate automaticamente in %d secunda."
+msgstr[1] "Le session de %s sera terminate automaticamente in %d secundas."
+
+#: ../js/ui/endSessionDialog.js:72
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Le session sera terminate automaticamente in %d secunda."
+msgstr[1] "Le session sera terminate automaticamente in %d secundas."
+
+#: ../js/ui/endSessionDialog.js:76
+msgid "Logging out of the system."
+msgstr "Clausura del session."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Clauder le session"
+
+#: ../js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Power Off"
+msgstr "Extinguer"
+
+#: ../js/ui/endSessionDialog.js:84
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr ""
+"Clicca sur 'Extinguer' pro quitar iste applicationes e extinguer le systema."
+
+#: ../js/ui/endSessionDialog.js:86
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Le systema se extinguera automaticamente in %d secunda."
+msgstr[1] "Le systema se extinguera automaticamente in %d secundas."
+
+#: ../js/ui/endSessionDialog.js:90
+msgid "Powering off the system."
+msgstr "Extinction del systema."
+
+#: ../js/ui/endSessionDialog.js:92 ../js/ui/endSessionDialog.js:109
+msgctxt "button"
+msgid "Restart"
+msgstr "Reinitiar"
+
+#: ../js/ui/endSessionDialog.js:94
+msgctxt "button"
+msgid "Power Off"
+msgstr "Extinguer"
+
+#: ../js/ui/endSessionDialog.js:100
+msgctxt "title"
+msgid "Restart"
+msgstr "Reinitiar"
+
+#: ../js/ui/endSessionDialog.js:101
+msgid "Click Restart to quit these applications and restart the system."
+msgstr ""
+"Clicca sur 'Reinitiar' pro quitar iste applicationes e reinitiar le systema."
+
+#: ../js/ui/endSessionDialog.js:103
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Le systema se reinitiara automaticamente in %d secunda."
+msgstr[1] "Le systema se reinitiara automaticamente in %d secundas."
+
+#: ../js/ui/endSessionDialog.js:107
+msgid "Restarting the system."
+msgstr "Reinitio del systema."
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "Installar"
+
+#: ../js/ui/extensionDownloader.js:204
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr "Discargar e installar '%s' de extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
+#: ../js/ui/status/power.js:211
+msgid "Keyboard"
+msgstr "Claviero"
+
+#: ../js/ui/lookingGlass.js:693
+msgid "No extensions installed"
+msgstr "Necun extensiones installate"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:747
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr "%s non ha emittite alicun errores."
+
+#: ../js/ui/lookingGlass.js:753
+msgid "Hide Errors"
+msgstr "Occultar errores"
+
+#: ../js/ui/lookingGlass.js:757 ../js/ui/lookingGlass.js:817
+msgid "Show Errors"
+msgstr "Monstrar errores"
+
+#: ../js/ui/lookingGlass.js:766
+msgid "Enabled"
+msgstr "Activate"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:769 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "Disactivate"
+
+#: ../js/ui/lookingGlass.js:771
+msgid "Error"
+msgstr "Error"
+
+#: ../js/ui/lookingGlass.js:773
+msgid "Out of date"
+msgstr "Perempte"
+
+#: ../js/ui/lookingGlass.js:775
+msgid "Downloading"
+msgstr "Discargamento"
+
+#: ../js/ui/lookingGlass.js:799
+msgid "View Source"
+msgstr "Monstrar le fonte"
+
+#: ../js/ui/lookingGlass.js:808
+msgid "Web Page"
+msgstr "Pagina web"
+
+#: ../js/ui/messageTray.js:1182
+msgid "Open"
+msgstr "Aperir"
+
+#: ../js/ui/messageTray.js:1189
+msgid "Remove"
+msgstr "Remover"
+
+#: ../js/ui/messageTray.js:1501
+msgid "Clear Messages"
+msgstr "Expunger messages"
+
+#: ../js/ui/messageTray.js:1528
+msgid "Notification Settings"
+msgstr "Parametros de notificationes"
+
+#: ../js/ui/messageTray.js:1710
+msgid "No Messages"
+msgstr "Necun message"
+
+#: ../js/ui/messageTray.js:1783
+msgid "Message Tray"
+msgstr "Tiratorio de messages"
+
+#: ../js/ui/messageTray.js:2801
+msgid "System Information"
+msgstr "Information de systema"
+
+#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:378
+msgctxt "program"
+msgid "Unknown"
+msgstr "Incognite"
+
+#: ../js/ui/overviewControls.js:472 ../js/ui/screenShield.js:149
+#, c-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d nove message"
+msgstr[1] "%d nove messages"
+
+#: ../js/ui/overview.js:82
+msgid "Undo"
+msgstr "Disfacer"
+
+#: ../js/ui/overview.js:127
+msgid "Overview"
+msgstr "Panorama"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/overview.js:260
+msgid "Type to search…"
+msgstr "Scribe pro cercar…"
+
+#: ../js/ui/panel.js:641
+msgid "Quit"
+msgstr "Quitar"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:692
+msgid "Activities"
+msgstr "Activitates"
+
+#: ../js/ui/panel.js:989
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:718
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:74
+msgid "Enter a Command"
+msgstr "Introduce un commando"
+
+#: ../js/ui/runDialog.js:110
+msgid "Close"
+msgstr "Clauder"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: ../js/ui/screenShield.js:86
+msgid "%A, %B %d"
+msgstr "%A, %d de %B"
+
+#: ../js/ui/screenShield.js:151
+#, c-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d nove notification"
+msgstr[1] "%d nove notificationes"
+
+#: ../js/ui/screenShield.js:438 ../js/ui/userMenu.js:807
+msgid "Lock"
+msgstr "Serrar"
+
+#: ../js/ui/screenShield.js:641
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME ha besonio de serrar le schermo"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: ../js/ui/screenShield.js:762 ../js/ui/screenShield.js:1198
+msgid "Unable to lock"
+msgstr "Impossibile de serrar"
+
+#: ../js/ui/screenShield.js:763 ../js/ui/screenShield.js:1199
+msgid "Lock was blocked by an application"
+msgstr "Le serramento ha essite impedite per un application"
+
+#: ../js/ui/searchDisplay.js:453
+msgid "Searching…"
+msgstr "Cerca…"
+
+#: ../js/ui/searchDisplay.js:497
+msgid "No results."
+msgstr "Necun resultato."
+
+#: ../js/ui/shellEntry.js:29
+msgid "Copy"
+msgstr "Copiar"
+
+#: ../js/ui/shellEntry.js:34
+msgid "Paste"
+msgstr "Collar"
+
+#: ../js/ui/shellEntry.js:101
+msgid "Show Text"
+msgstr "Monstrar texto"
+
+#: ../js/ui/shellEntry.js:103
+msgid "Hide Text"
+msgstr "Occultar texto"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "Contrasigno"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "Recordar contrasigno"
+
+#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:114
+msgid "Unlock"
+msgstr "Disserrar"
+
+#: ../js/ui/status/accessibility.js:36
+msgid "Accessibility"
+msgstr "Accessibilitate"
+
+#: ../js/ui/status/accessibility.js:41
+msgid "Zoom"
+msgstr "Aggrandimento"
+
+#: ../js/ui/status/accessibility.js:48
+msgid "Screen Reader"
+msgstr "Lector de schermo"
+
+#: ../js/ui/status/accessibility.js:52
+msgid "Screen Keyboard"
+msgstr "Claviero visual"
+
+#: ../js/ui/status/accessibility.js:56
+msgid "Visual Alerts"
+msgstr "Allerta visual"
+
+#: ../js/ui/status/accessibility.js:59
+msgid "Sticky Keys"
+msgstr "Claves remanente"
+
+#: ../js/ui/status/accessibility.js:62
+msgid "Slow Keys"
+msgstr "Claves lente"
+
+#: ../js/ui/status/accessibility.js:65
+msgid "Bounce Keys"
+msgstr "Claves saltante"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Mouse Keys"
+msgstr "Claves del mus imitate"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Universal Access Settings"
+msgstr "Parametros del accesso universal"
+
+#: ../js/ui/status/accessibility.js:129
+msgid "High Contrast"
+msgstr "Contrasto elevate"
+
+#: ../js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr "Texto grande"
+
+#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
+#: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
+#: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
+#: ../js/ui/status/network.js:826
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: ../js/ui/status/bluetooth.js:45
+msgid "Visibility"
+msgstr "Visibile"
+
+#: ../js/ui/status/bluetooth.js:59
+msgid "Send Files to Device…"
+msgstr "Inviar files al dispositivo…"
+
+#: ../js/ui/status/bluetooth.js:60
+msgid "Set Up a New Device…"
+msgstr "Configurar un nove dispositivo…"
+
+#: ../js/ui/status/bluetooth.js:84
+msgid "Bluetooth Settings"
+msgstr "Parametros Bluetooth"
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:178
+msgid "hardware disabled"
+msgstr "disactivate physicamente"
+
+#: ../js/ui/status/bluetooth.js:197
+msgid "Connection"
+msgstr "Connexion"
+
+#: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:460
+msgid "disconnecting..."
+msgstr "disconnexion…"
+
+#: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:466
+#: ../js/ui/status/network.js:1546
+msgid "connecting..."
+msgstr "connexion…"
+
+#: ../js/ui/status/bluetooth.js:239
+msgid "Send Files…"
+msgstr "Inviar files…"
+
+#: ../js/ui/status/bluetooth.js:246
+msgid "Keyboard Settings"
+msgstr "Parametros de claviero"
+
+#: ../js/ui/status/bluetooth.js:249
+msgid "Mouse Settings"
+msgstr "Parametros de mus"
+
+#: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
+msgid "Sound Settings"
+msgstr "Parametros de sono"
+
+#: ../js/ui/status/bluetooth.js:322
+#, c-format
+msgid "Authorization request from %s"
+msgstr "Requesta de authorisation de %s"
+
+#: ../js/ui/status/bluetooth.js:328
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "Le dispositivo %s vole acceder al servicio '%s'"
+
+#: ../js/ui/status/bluetooth.js:330
+msgid "Always grant access"
+msgstr "Conceder accesso sempre"
+
+#: ../js/ui/status/bluetooth.js:331
+msgid "Grant this time only"
+msgstr "Conceder accesso solmente in iste vice"
+
+#: ../js/ui/status/bluetooth.js:332
+msgid "Reject"
+msgstr "Rejectar"
+
+#. Translators: argument is the device short name
+#: ../js/ui/status/bluetooth.js:359
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "Confirmation del association con %s"
+
+#: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "Le dispositivo %s vole associar se con iste computator"
+
+#: ../js/ui/status/bluetooth.js:366
+#, c-format
+msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+msgstr "Confirma que le PIN '%06d' corresponde a illo sur le dispositivo."
+
+#. Translators: this is the verb, not the noun
+#: ../js/ui/status/bluetooth.js:369
+msgid "Matches"
+msgstr "Corresponde"
+
+#: ../js/ui/status/bluetooth.js:370
+msgid "Does not match"
+msgstr "Non corresponde"
+
+#: ../js/ui/status/bluetooth.js:389
+#, c-format
+msgid "Pairing request for %s"
+msgstr "Requesta del association con %s"
+
+#: ../js/ui/status/bluetooth.js:397
+msgid "Please enter the PIN mentioned on the device."
+msgstr "Introduce le PIN mentionate sur le dispositivo."
+
+#: ../js/ui/status/bluetooth.js:414
+msgid "OK"
+msgstr "Validar"
+
+#: ../js/ui/status/keyboard.js:368
+msgid "Show Keyboard Layout"
+msgstr "Monstrar le disposition de claviero"
+
+#: ../js/ui/status/keyboard.js:373
+msgid "Region & Language Settings"
+msgstr "Parametros de region e de lingua"
+
+#: ../js/ui/status/lockScreenMenu.js:43
+msgid "Volume, network, battery"
+msgstr "Volumine, rete, accumulator"
+
+#: ../js/ui/status/network.js:104
+msgid "<unknown>"
+msgstr "<incognite>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:200
+msgid "disabled"
+msgstr "disactivate"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:458
+msgid "unmanaged"
+msgstr "foras de gestion"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1549
+msgid "authentication required"
+msgstr "necessita authentication"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:479
+msgid "firmware missing"
+msgstr "firmware mancante"
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:486
+msgid "cable unplugged"
+msgstr "cablo disconnectite"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:491
+msgid "unavailable"
+msgstr "non disponibile"
+
+#: ../js/ui/status/network.js:493 ../js/ui/status/network.js:1551
+msgid "connection failed"
+msgstr "falta de connexion"
+
+#: ../js/ui/status/network.js:552 ../js/ui/status/network.js:1435
+#: ../js/ui/status/network.js:1627
+msgid "More…"
+msgstr "Plus…"
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1365
+msgid "Connected (private)"
+msgstr "Connectite (private)"
+
+#: ../js/ui/status/network.js:667
+msgid "Wired"
+msgstr "Via filo"
+
+#: ../js/ui/status/network.js:668
+msgid "Auto Ethernet"
+msgstr "Ethernet automatic"
+
+#: ../js/ui/status/network.js:695
+msgid "Mobile broadband"
+msgstr "Banda large mobile"
+
+#: ../js/ui/status/network.js:728
+msgid "Auto broadband"
+msgstr "Banda large automatic"
+
+#: ../js/ui/status/network.js:731
+msgid "Auto dial-up"
+msgstr "Telephonic automatic"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:861 ../js/ui/status/network.js:1382
+#, c-format
+msgid "Auto %s"
+msgstr "%s automatic"
+
+#: ../js/ui/status/network.js:863
+msgid "Auto bluetooth"
+msgstr "Bluetooth automatic"
+
+#: ../js/ui/status/network.js:1384
+msgid "Auto wireless"
+msgstr "Sin filo automatic"
+
+#: ../js/ui/status/network.js:1729
+msgid "Enable networking"
+msgstr "Activar le rete"
+
+#: ../js/ui/status/network.js:1771
+msgid "Wi-Fi"
+msgstr "Wi-Fi"
+
+#: ../js/ui/status/network.js:1790
+msgid "Network Settings"
+msgstr "Parametros de rete"
+
+#: ../js/ui/status/network.js:1807
+msgid "Network Manager"
+msgstr "Gerente de rete"
+
+#: ../js/ui/status/network.js:1897
+msgid "Connection failed"
+msgstr "Falta de connexion"
+
+#: ../js/ui/status/network.js:1898
+msgid "Activation of network connection failed"
+msgstr "Falta de activation del connexion de rete"
+
+#: ../js/ui/status/network.js:2276
+msgid "Networking is disabled"
+msgstr "Le rete es disactivate"
+
+#: ../js/ui/status/power.js:55
+msgid "Battery"
+msgstr "Accumulator"
+
+#: ../js/ui/status/power.js:81
+msgid "Power Settings"
+msgstr "Preferentias del gestion de energia"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:99
+msgid "Estimating…"
+msgstr "Estimation…"
+
+#: ../js/ui/status/power.js:106
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "%d hora restante"
+msgstr[1] "%d horas restante"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:109
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "%d %s %d %s restante"
+
+#: ../js/ui/status/power.js:111
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "hora"
+msgstr[1] "horas"
+
+#: ../js/ui/status/power.js:111
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "minuta"
+msgstr[1] "minutas"
+
+#: ../js/ui/status/power.js:114
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "%d minuta restante"
+msgstr[1] "%d minutas restante"
+
+#: ../js/ui/status/power.js:117 ../js/ui/status/power.js:191
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d%%"
+
+#: ../js/ui/status/power.js:201
+msgid "AC Adapter"
+msgstr "Adaptor de currente"
+
+#: ../js/ui/status/power.js:203
+msgid "Laptop Battery"
+msgstr "Accumulator del computator"
+
+#: ../js/ui/status/power.js:205
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:207
+msgid "Monitor"
+msgstr "Monitor"
+
+#: ../js/ui/status/power.js:209
+msgid "Mouse"
+msgstr "Mouse"
+
+#: ../js/ui/status/power.js:213
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:215
+msgid "Cell Phone"
+msgstr "Telephono cellular"
+
+#: ../js/ui/status/power.js:217
+msgid "Media Player"
+msgstr "Lector de multimedia"
+
+#: ../js/ui/status/power.js:219
+msgid "Tablet"
+msgstr "Tabletta"
+
+#: ../js/ui/status/power.js:221
+msgid "Computer"
+msgstr "Computator"
+
+#: ../js/ui/status/power.js:223
+msgctxt "device"
+msgid "Unknown"
+msgstr "Incognite"
+
+#: ../js/ui/status/volume.js:124
+msgid "Volume changed"
+msgstr "Volumine modificate"
+
+#. Translators: This is the label for audio volume
+#: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
+msgid "Volume"
+msgstr "Volumine"
+
+#: ../js/ui/status/volume.js:258
+msgid "Microphone"
+msgstr "Microphono"
+
+#: ../js/ui/unlockDialog.js:125
+msgid "Log in as another user"
+msgstr "Initiar session como altere usator"
+
+#: ../js/ui/unlockDialog.js:146
+msgid "Unlock Window"
+msgstr "Fenestra de disserramento"
+
+#: ../js/ui/userMenu.js:193
+msgid "Available"
+msgstr "Disponibile"
+
+#: ../js/ui/userMenu.js:196
+msgid "Busy"
+msgstr "Occupate"
+
+#: ../js/ui/userMenu.js:199
+msgid "Invisible"
+msgstr "Invisibile"
+
+#: ../js/ui/userMenu.js:202
+msgid "Away"
+msgstr "Absente"
+
+#: ../js/ui/userMenu.js:205
+msgid "Idle"
+msgstr "Inactive"
+
+#: ../js/ui/userMenu.js:208
+msgid "Offline"
+msgstr "Disconnexe"
+
+#: ../js/ui/userMenu.js:781
+msgid "Notifications"
+msgstr "Notificationes"
+
+#: ../js/ui/userMenu.js:797
+msgid "Switch User"
+msgstr "Cambiar de usator"
+
+#: ../js/ui/userMenu.js:802
+msgid "Log Out"
+msgstr "Clauder le session"
+
+#: ../js/ui/userMenu.js:822
+msgid "Install Updates & Restart"
+msgstr "Installar actualisationes e reinitiar"
+
+#: ../js/ui/userMenu.js:840
+msgid "Your chat status will be set to busy"
+msgstr "Le stato de chat sera definite como 'occupate'"
+
+#: ../js/ui/userMenu.js:841
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+"Le notificationes es ora disactivate, includente le messages de chat. Le "
+"stato in linea se ha adjustate pro facer saper al alteres que lor messages "
+"pote non esser notate."
+
+#: ../js/ui/userMenu.js:888
+msgid "Other users are logged in."
+msgstr "Altere usatores son connexe."
+
+#: ../js/ui/userMenu.js:893
+msgid "Shutting down might cause them to lose unsaved work."
+msgstr "Le extinction poterea causar le perdita de obras non salveguardate."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: ../js/ui/userMenu.js:921
+#, c-format
+msgid "%s (remote)"
+msgstr "%s (remote)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: ../js/ui/userMenu.js:924
+#, c-format
+msgid "%s (console)"
+msgstr "%s (consola)"
+
+#: ../js/ui/viewSelector.js:100
+msgid "Applications"
+msgstr "Applicationes"
+
+#: ../js/ui/viewSelector.js:104
+msgid "Search"
+msgstr "Cerca"
+
+#: ../js/ui/wanda.js:77
+#, c-format
+msgid ""
+"Sorry, no wisdom for you today:\n"
+"%s"
+msgstr ""
+"Il ha necun sagessa hodie:\n"
+"%s"
+
+#: ../js/ui/wanda.js:81
+#, c-format
+msgid "%s the Oracle says"
+msgstr "%s le oraculo dice"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, c-format
+msgid "'%s' is ready"
+msgstr "'%s' es preste"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Calendario de Evolution"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u egresso"
+msgstr[1] "%u egressos"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ingresso"
+msgstr[1] "%u ingressos"
+
+#: ../src/gvc/gvc-mixer-control.c:2371
+msgid "System Sounds"
+msgstr "Sonos de systema"
+
+#: ../src/main.c:347
+msgid "Print version"
+msgstr "Monstrar le version"
+
+#: ../src/main.c:353
+msgid "Mode used by GDM for login screen"
+msgstr "Modo usate per GDM pro le schermo de accesso"
+
+#: ../src/main.c:359
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "Usar un modo specific, per ex. \"gdm\" pro le schermo de accesso"
+
+#: ../src/main.c:365
+msgid "List possible modes"
+msgstr "Monstrar le lista del modos possibile"
+
+#: ../src/shell-app.c:626
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr "Impossibile de lancear '%s'"
+
+#: ../src/shell-keyring-prompt.c:708
+msgid "Passwords do not match."
+msgstr "Le contrasignos non corresponde."
+
+#: ../src/shell-keyring-prompt.c:716
+msgid "Password cannot be blank"
+msgstr "Le contrasigno non pote esser vacue"
+
+#: ../src/shell-polkit-authentication-agent.c:343
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Le dialogo de authentication era dimittite per le usator"
diff --git a/po/id.po b/po/id.po
new file mode 100644
index 0000000..2acd454
--- /dev/null
+++ b/po/id.po
@@ -0,0 +1,3137 @@
+# Copyright (C) 2010 THE gnome-shell'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Andika Triwidada <andika@gmail.com>, 2010-2014, 2017, 2022.
+# Dirgita <dirgitadevina@yahoo.co.id>, 2011, 2012, 2014.
+# Wibiharto <wibinem@yahoo.com>, 2011.
+# Kukuh Syafaat <kukuhsyafaat@gnome.org>, 2017-2023.
+# Sucipto <sucipto@pm.me>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell main\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 17:56+0000\n"
+"PO-Revision-Date: 2023-02-15 10:44+0700\n"
+"Last-Translator: Kukuh Syafaat <kukuhsyafaat@gnome.org>\n"
+"Language-Team: Indonesian <gnome-l10n-id@googlegroups.com>\n"
+"Language: id\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Peluncur"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktifkan aplikasi favorit 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktifkan aplikasi favorit 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktifkan aplikasi favorit 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktifkan aplikasi favorit 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktifkan aplikasi favorit 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktifkan aplikasi favorit 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktifkan aplikasi favorit 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktifkan aplikasi favorit 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aplikasi Karakter diaktifkan"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Cuplikan Layar"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Ambil cuplikan layar secara interaktif"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Ambil cuplikan layar"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Ambil cuplikan layar dari suatu jendela"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Rekam screencast secara interaktif"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistem"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Tampilkan daftar pemberitahuan"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokuskan pemberitahuan yang aktif"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Tampilkan ikhtisar"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Tampilkan semua aplikasi"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Buka menu aplikasi"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Manajemen jendela dan peluncuran aplikasi"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Mengaktifkan perkakas internal yang berguna bagi pengembang dan penguji "
+"dengan Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Izinkan askes ke perkakas pemantauan dan pengawakutuan internal memakai "
+"dialog Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID ekstensi yang akan difungsikan"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Ekstensi GNOME Shell memiliki properti UUID; kunci ini memuat daftar "
+"ekstensi yang mesti dimuat. Sebarang ekstensi yang ingin dimuat perlu berada "
+"dalam daftar ini. Anda juga dapat memanipulasi daftar ini dengan metoda D-"
+"Bus EnableExtension dan DisableExtension pada org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID ekstensi untuk memaksa penonaktifan"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Ekstensi GNOME Shell memiliki properti UUID; kunci ini memuat daftar "
+"ekstensi yang harus dinonaktifkan, meskipun dimuat sebagai bagian dari mode "
+"saat ini. Anda juga dapat memanipulasi daftar ini dengan metode D-Bus "
+"EnableExtension dan DisableExtension pada org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Nonaktifkan ekstensi pengguna"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Nonaktifkan semua ekstensi pengguna yang telah diaktifkan tanpa mempengaruhi "
+"pengaturan \"ekstensi-diaktifkan\"."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Menonaktifkan validasi kompatibilitas versi ekstensi"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell hanya akan memuat ekstensi yang mengklaim mendukung versi "
+"berjalan saat ini. Memfungsikan opsi ini akan menonaktifkan pemeriksaan ini "
+"dan mencoba memuat semua ekstensi tanpa peduli versi yang mereka klaim "
+"didukung."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Daftar ID berkas desktop untuk aplikasi favorit"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Aplikasi yang terkait dengan identifier-identifier ini akan ditampilkan pada "
+"area favorit."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Riwayat dialog perintah (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Riwayat untuk dialog yang berpenampilan gelas kaca"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Selalu tampilkan butir menu 'Log keluar' dalam menu pengguna."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Kunci ini menimpa penyembunyian otomatis butir menu 'Log keluar' dalam "
+"situasi pengguna-tunggal, sesi-tunggal."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Apakah mengingat kata sandi untuk mengait sistem berkas terenkripsi atau "
+"jarak jauh"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Shell akan meminta suatu kata sandi ketika suatu perangkat terenkripsi atau "
+"sistem berkas jarak jauh dikaitkan. Bila sandi dapat disimpan untuk dipakai "
+"dimasa depan, suatu kotak contreng 'Ingat Sandi' akan tampil. Kunci ini "
+"menata keadaan baku dari kotak contreng."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Profil daya non-bawaan terakhir yang dipilih"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Beberapa sistem mendukung lebih dari dua profil daya. Agar tetap mendukung "
+"pengalihan antara dua profil, kunci ini merekam profil non-bawaan yang "
+"terakhir dipilih."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Versi terakhir dialog \"Selamat Datang di GNOME\" ditampilkan untuk"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Kunci ini menentukan versi mana dialog \"Selamat Datang di GNOME\" terakhir "
+"kali ditampilkan. String kosong mewakili versi tertua yang mungkin, dan "
+"sejumlah besar akan mewakili versi yang belum ada. Jumlah besar ini dapat "
+"digunakan untuk secara efektif menonaktifkan dialog."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Tata letak pemilih aplikasi"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Tata letak pemilih aplikasi. Setiap entri dalam array adalah halaman. "
+"Halaman disimpan dalam urutan yang muncul di GNOME Shell. Setiap halaman "
+"berisi pasangan “application id” → 'data'. Saat ini, nilai berikut disimpan "
+"sebagai 'data': • \"position\": posisi ikon aplikasi di halaman"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Kombinasi tombol untuk membuka menu aplikasi"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Kombinasi tombol untuk membuka menu aplikasi."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Kombinasi tombol untuk beralih antara status ringkasan"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Kombinasi tombol untuk beralih antara sesi, pemilih jendela, dan kisi "
+"aplikasi"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Kombinasi tombol untuk beralih antara kisi aplikasi, pemilih jendela, dan "
+"sesi"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Kombinasi tombol untuk membuka tilikan \"Tampilkan Aplikasi\""
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Kombinasi tombol untuk membuka tilikan Ringkasan Aktivitas \"Tampilkan "
+"Aplikasi\"."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Kombinasi tombol untuk membuka ringkasan"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Kombinasi tombol untuk membuka Ringkasan Aktivitas."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Kombinasi tombol untuk kenampakan daftar pemberitahuan"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Kombinasi tombol untuk kenampakan daftar pemberitahuan."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Kombinasi tombol untuk fokus pada pemberitahuan yang aktif"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Kombinasi tombol untuk fokus pada pemberitahuan yang aktif."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Beralih ke aplikasi 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Beralih ke aplikasi 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Beralih ke aplikasi 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Beralih ke aplikasi 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Beralih ke aplikasi 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Beralih ke aplikasi 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Beralih ke aplikasi 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Beralih ke aplikasi 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Beralih ke aplikasi 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Batasi pengalih ke ruang kerja saat ini."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Bila berisi true, hanya aplikasi yang punya jendela pada ruang kerja saat "
+"ini ditampilkan pada penukar. Bila tidak, semua aplikasi disertakan."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Mode ikon aplikasi."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Mengatur bagaimana jendela ditampilkan dalam penukar. Kemungkinan yang valid "
+"adalah \"thumbnail-only\" (menampilkan gambar mini dari jendela), \"app-icon-"
+"only\" (hanya menampilkan ikon aplikasi), atau \"both\" (keduanya)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Bila berisi true, hanya jendela dari ruang kerja saat ini ditampilkan pada "
+"penukar. Bila tidak, semua jendela disertakan."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Lokasi"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Lokasi untuk ditampilkan di jam dunia"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Lokasi otomatis"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Apakah akan mengambil lokasi saat ini atau tidak"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Lokasi"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Lokasi yang menunjukkan perkiraan"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Mencantolkan dialog modal ke jendela induk"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Kunci ini menimpa kunci dalam org.gnome.mutter ketika menjalankan GNOME "
+"Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Memfungsikan pengubinan tepi ketika menjatuhkan jendela pada tepi layar"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Ruang kerja dikelola secara dinamis"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Ruang kerja hanya pada monitor primer"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Tunda perubahan fokus dalam mode tetikus sampai penunjuk berhenti bergerak"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Log Masuk Jaringan"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Ada yang tidak beres"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Kami sangat menyesal, tetapi ada masalah: pengaturan untuk ekstensi ini "
+"tidak dapat ditampilkan. Kami menyarankan Anda melaporkan masalah ini kepada "
+"penulis ekstensi."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detail Teknis"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Halaman Web"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Kunjungi halaman web ekstensi"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Batal"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Kata Sandi"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Pilih Sesi"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Tak masuk daftar?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(cth., pengguna dari %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nama pengguna"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Jendela Log Masuk"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Galat autentikasi"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(atau usap jari melintasi pembaca)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(atau letakkan jari pada pembaca)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Matikan"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "matikan;halt;berhenti"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Mulai Ulang"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "mulai ulang;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Kunci Layar"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "kunci layar"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Keluar"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "keluar;logout;log out;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspensi"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspensi;tidur"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Ganti Pengguna"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "ganti pengguna"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "kunci orientasi;buka kunci orientasi;layar;rotasi"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Ambil Cuplikan Layar"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "cuplikan layar;screencast;snip;tangkap;rekam"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Buka Kunci Rotasi Layar"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Kunci Rotasi Layar"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Perintah tidak ditemukan"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Tak dapat mengurai perintah:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Eksekusi \"%s\" gagal:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Baru saja"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d menit yang lalu"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d jam yang lalu"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Kemarin"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d hari yang lalu"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d minggu yang lalu"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d bulan yang lalu"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d tahun yang lalu"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Kemarin, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Kemarin, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Log Masuk Area Bersinyal (hotspot)"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Koneksi Anda ke log masuk area bersinyal (hotspot) ini tidak aman. Kata kata "
+"sandi atau informasi lain yang Anda masukkan pada halaman ini dapat dilihat "
+"oleh orang-orang di sekitar."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Tolak Akses"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Beri Akses"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Folder Tanpa Nama"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s telah disematkan ke dash."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s telah di-unpin dari dash."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Buka Jendela"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Jendela Baru"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Tampilkan Rincian"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Keluar"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Unpin"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Sematkan ke Dash"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Luncurkan menggunakan Kartu Grafis Terintegrasi"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Luncurkan menggunakan Kartu Grafis Diskrit"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Pilih Perangkat Audio"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Pengaturan Suara"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Headphone"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Ubah Latar…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Pengaturan Tampilan"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Pengaturan"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "R"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "K"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Bulan sebelumnya"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Bulan selanjutnya"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Minggu %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Tak Ada Pemberitahuan"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Jangan Ganggu"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Bersihkan"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "\"%s\" tidak merespon."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Anda dapat memilih untuk menunggu sebentar untuk melanjutkan atau memaksa "
+"aplikasi keluar."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Tutup Paksa"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Tunggu"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Drive eksternal tersambung"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Drive eksternal terputus"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Tak bisa membuka kunci volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Versi udisks yang dipasang tidak mendukung pengaturan PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Buka dengan %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Sebagai alternatif Anda dapat terhubung dengan menekan tombol \"WPS\" pada "
+"router Anda."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Sambung"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Kunci"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Kata sandi kunci privat"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitas"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Layanan"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Diperlukan autentikasi"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Kata sandi atau kunci enkripsi diperlukan untuk mengakses jaringan nirkabel "
+"\"%s\"."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autentikasi 802.1X kabel"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nama jaringan"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autentikasi DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Perlu kode PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Kode PIN diperlukan untuk perangkat data seluler"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Perlu suatu kata sandi untuk menyambung ke \"%s\"."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Manajer Jaringan"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Kata Sandi VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Diperlukan Autentikasi"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autentikasi"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Maaf, tidak berhasil. Silakan coba lagi."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s sekarang dikenal sebagai %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Jendela"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Tampilkan Aplikasi"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%e %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%e %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%e %B"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Hari Ini"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Besok"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Sepanjang Hari"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Tak Ada Acara"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Tambah jam dunia…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Jam Dunia"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Memuat…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Pergi daring untuk informasi cuaca"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Informasi cuaca saat ini tidak tersedia"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Cuaca"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Pilih lokasi cuaca…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Keluar %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Keluar"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s akan log keluar otomatis dalam %d detik."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Anda akan log keluar otomatis dalam %d detik."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Keluar"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Matikan"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Pasang Pemutakhiran & Matikan"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistem ini akan otomatis dimatikan dalam %d detik."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Pasang pemutakhiran perangkat lunak yang tertunda"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Matikan"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Mulai Ulang"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Pasang Pemutakhiran & Mulai Ulang"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistem ini akan otomatis dimulai ulang dalam %d detik."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Mulai Ulang"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Mulai Ulang & Pasang Pemutakhiran"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistem ini akan otomatis dimulai ulang dan memasang pemutakhiran dalam %d "
+"detik."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Mulai Ulang &amp; Pasang"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Pasang &amp; Matikan"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Matikan daya setelah pemutakhiran dipasang"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Mulai Ulang & Pasang Peningkatan"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s akan dipasang setelah mulai ulang. Instalasi peningkatan bisa makan "
+"waktu lama: pastikan bahwa Anda telah melakukan back up dan komputer "
+"tersambung ke listrik."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Daya baterai rendah: harap tancapkan sebelum memasang pemutakhiran."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Beberapa aplikasi sedang sibuk atau belum disimpan perubahannya"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Pengguna lain sedang log masuk"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opsi Boot"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (jarak jauh)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsol)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Pasang"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Pasang Ekstensi"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Unduh dan pasang \"%s\" dari extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Pemutakhiran Ekstensi Tersedia"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Pemutakhiran ekstensi siap dipasang."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Izinkan menghalangi pintasan"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Aplikasi %s ingin menghalangi pintasan"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Aplikasi ingin menghalangi pintasan"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Anda dapat memulihkan pintasan dengan menekan %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Tolak"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Izinkan"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tombol Lambat Dinyalakan"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tombol Lambat Dimatikan"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Anda hanya menahan tombol Shift selama 8 detik. Ini adalah pintasan untuk "
+"fitur Tombol Lambat, yang mempengaruhi cara kerja papan tik Anda."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Tombol Lengket Dinyalakan"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Tombol Lengket Dimatikan"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Anda hanya menekan tombol Shift 5 kali berturut-turut. Ini adalah pintasan "
+"untuk fitur Tombol Lengket, yang mempengaruhi cara kerja papan tik Anda."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Anda hanya menekan dua tombol sekaligus, atau menekan tombol Shift 5 kali "
+"berturut-turut. Ini mematikan fitur Tombol Lengket, yang mempengaruhi cara "
+"kerja papan tik Anda."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Biarkan Menyala"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Nyalakan"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Matikan"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Biarkan Mati"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Pengaturan Wilayah & Bahasa"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Tak ada ekstensi terpasang"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s tidak menampilkan galat apa pun."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Sembunyikan Galat"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Tampilkan Galat"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Diaktifkan"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Dinonaktifkan"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Galat"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Kadaluarsa"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Mengunduh"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Tilik Sumber"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Halaman Web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Sistem dimasukkan ke dalam mode yang tidak aman"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Aplikasi sekarang memiliki akses tak terbatas"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Batal"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Masuk sebagai pengguna istimewa"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Menjalankan sesi sebagai pengguna istimewa harus dihindari untuk alasan "
+"keamanan. Jika memungkinkan, Anda harus masuk sebagai pengguna biasa."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Kunci Layar dinonaktifkan"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Penguncian Layar memerlukan manajer tampilan GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informasi Sistem"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artis tak dikenal"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Judul tak dikenal"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Ketik untuk mencari"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Aplikasi"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Gambaran"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Pintasan baru…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplikasi yang didefinisikan"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Tampilkan bantuan di layar"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Ganti monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Tetapkan keystroke"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Selesai"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Sunting…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nihil"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Tekan tombol untuk mengkonfigurasi"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Tekan Esc untuk keluar"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Tekan tombol apa saja untuk keluar"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Aktivitas"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistem"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Bar Atas"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Jalankan Perintah"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Tekan Esc untuk menutup"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Memulai ulang tidak tersedia di Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Memulai ulang…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME perlu mengunci layar"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Tak bisa mengunci"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Kunci diblokir oleh suatu aplikasi"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Pilihan"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Pemilihan Area"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Layar"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Pemilihan Layar"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Jendela"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Pemilihan Jendela"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Cuplikan layar / Screencast"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Tampilkan Penunjuk"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Screencast"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Screencast dari %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Cuplikan layar"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Screencast direkam"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Klik di sini untuk melihat video."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Tampilkan pada Berkas"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Cuplikan layar dari %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Cuplikan layar yang diambil"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Anda dapat menempelkan gambar dari papan klip."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Cuplikan layar telah diambil"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Mencari…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Tak ada yang cocok."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d lagi"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Cari"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Salin"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Tempel"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Tampilkan Teks"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Sembunyikan Teks"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock menyala."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volume Tersembunyi"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volume Sistem Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Gunakan Berkas Kunci"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Untuk membuka kunci volume yang menggunakan berkas kunci, gunakan utilitas "
+"<i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Nomor PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Ingat Kata Sandi"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Buka Kunci"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Buka %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM harus berupa angka atau kosong."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Tak bisa memulai %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Tak bisa menemukan aplikasi %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Aksesibilitas"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Kontras Tinggi"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zum"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Pembaca Layar"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Papan Tik di Layar"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Peringatan Visual"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Tombol Lengket"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tombol Lambat"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Tombol Pantul"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Tombol Tetikus"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Teks Besar"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Putar Otomatis"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Kecerahan"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Mode Gelap"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Klik Tunggal"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Klik Ganda"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Seret"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Klik Sekunder"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Klik Menetap"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Papan Ketik"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Tampilkan Tata Letak Papan Tik"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Izinkan akses lokasi"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Aplikasi %s ingin mengakses lokasi Anda"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Akses lokasi dapat diubah setiap saat dari pengaturan privasi."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<tak dikenal>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Memutus %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Sambung ke %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s Hotspot"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "Pengaturan VPN"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi-Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Semua Jaringan"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Koneksi Kabel"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Pengaturan Kabel"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Tether Bluetooth"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Pengaturan Bluetooth"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Koneksi Seluler"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Pengaturan Data Seluler"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Koneksi gagal"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Aktivasi koneksi jaringan gagal"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Cahaya Malam"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Performa"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Seimbang"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Penghemat Daya"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Profil Daya"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Hentikan Screencast"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Hentikan Berbagi Layar"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Mode Pesawat Terbang"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Ambil Cuplikan"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menu Matikan"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspensi"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Mulai ulang…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Matikan…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Keluar…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Beralih Pengguna…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Kunci Layar"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Perangkat Thunderbolt tak dikenal"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Perangkat baru telah terdeteksi saat Anda pergi. Harap lepaskan dan "
+"sambungkan kembali perangkat untuk mulai menggunakannya."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Perangkat Thunderbolt tak dikenal"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Perangkat baru telah terdeteksi dan perlu diautorisasi oleh administrator."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Autorisasi Thunderbolt galat"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Tidak dapat mengautorisasi perangkat Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Volume diubah"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volume"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Keluaran Suara"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Masukan Suara"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Kembar"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Gabung Layar"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Hanya Eksternal"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Hanya Bawaan"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Gesek ke atas untuk membuka kunci"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Klik atau tekan tombol untuk membuka kunci"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Buka Kunci Jendela"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Masuk sebagai pengguna lain"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Selamat datang di GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Jika Anda ingin mempelajarinya, lihat turnya."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Tidak, Terima Kasih"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Ikuti Tur"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” siap"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Simpan pengaturan tampilan ini?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Balikkan Tatanan"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Simpan Perubahan"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Perubahan tatanan akan dikembalikan dalam %d detik"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Sembunyikan"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Pulihkan"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maksimalkan"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Pindah"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Ubah Ukuran"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Pindahkan Bilah Judul Pada Layar"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Selalu di Puncak"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Selalu pada Ruang Kerja yang Tampak"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Pindah ke Ruang Kerja Kiri"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Pindah ke Ruang Kerja Kanan"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Pindah ke Ruang Kerja Atas"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Pindah ke Ruang Kerja Bawah"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Pindah ke Ruang Kerja Atas"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Pindah ke Ruang Kerja Bawah"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Pindahkan ke Monitor Kiri"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Pindahkan ke Monitor Kanan"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Tutup"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution Kalender"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Versi Cetak"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Mode yang dipakai oleh layar log masuk GDM"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Menggunakan mode tertentu, mis. \"gdm\" untuk layar masuk"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Menampilkan mode yang mungkin"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Tak dikenal"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Gagal meluncurkan \"%s\""
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Kata sandi tidak cocok."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Kata sandi tidak boleh kosong"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Dialog autentikasi ditolak oleh pengguna"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Ekstensi"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Kelola Ekstensi GNOME Anda"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Proyek GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Ekstensi GNOME menangani pemutakhiran ekstensi, mengkonfigurasi preferensi "
+"ekstensi, dan menghapus atau menonaktifkan ekstensi yang tidak diinginkan."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfigurasi Ekstensi GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Tidak Ada Kecocokan"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Hapus \"%s\"?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Jika Anda menghapus ekstensi, Anda harus kembali untuk mengunduhnya jika "
+"Anda ingin mengaktifkannya lagi"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Hapus"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Andika Triwidada <andika@gmail.com>, 2010-2014, 2017, 2022.\n"
+"Dirgita <dirgitadevina@yahoo.co.id>, 2011, 2012, 2014.\n"
+"Wibiharto <wibinem@yahoo.com>, 2011.\n"
+"Kukuh Syafaat <kukuhsyafaat@gnome.org>, 2017-2023.\n"
+"Sucipto <sucipto@pm.me>, 2020."
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d ekstensi akan diperbarui pada log masuk berikutnya."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Ekstensi tidak kompatibel dengan versi GNOME saat ini"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Ekstensi mengalami kesalahan"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Ekstensi dapat dimutakhirkan"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Situs Web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Hapus…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Bantuan"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Tentang Ekstensi"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Ekstensi dapat menyebabkan masalah kinerja dan stabilitas. Nonaktifkan "
+"ekstensi jika Anda mengalami masalah dengan sistem Anda."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Dipasang Secara Manual"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Untuk menemukan dan menambahkan ekstensi, kunjungi <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Bawaan"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Tidak Ada Ekstensi Terpasang"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Kami sangat menyesal, tetapi tidak mungkin mendapatkan daftar ekstensi yang "
+"dipasang. Pastikan Anda masuk ke GNOME dan coba lagi."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Pemutakhiran Ekstensi Siap"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Ekstensi baru berhasil dibuat di %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Nama harus berupa string yang sangat pendek (idealnya deskriptif).\n"
+"Contohnya adalah: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nama"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Deskripsi adalah penjelasan satu kalimat tentang apa yang dilakukan ekstensi "
+"Anda.\n"
+"Contohnya adalah: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Deskripsi"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID adalah pengidentifikasi unik secara global untuk ekstensi Anda.\n"
+"Ini harus dalam format alamat surel (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Pilih salah satu templat yang tersedia:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Templat"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Pengidentifikasi unik ekstensi baru"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAMA"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Nama ekstensi baru yang dapat dilihat pengguna"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESKRIPSI"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Deskripsi singkat tentang apa yang dilakukan ekstensi"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "TEMPLAT"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Templat yang akan digunakan untuk ekstensi baru"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Masukkan informasi ekstensi secara interaktif"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Buat ekstensi baru"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argumen tak dikenal"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Diperlukan UUID, nama, dan deskripsi"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Ekstensi \"%s\" tidak ada\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Nonaktifkan ekstensi"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Tidak ada UUID yang diberikan"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Lebih dari satu UUID diberikan"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Aktifkan ekstensi"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Gagal terhubung ke GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Ekstensi \"%s\" tidak ada\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Tampilkan info ekstensi"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Timpa ekstensi yang ada"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "BUNDEL_EKSTENSI"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Pasang bundel ekstensi"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Tidak ada bundel ekstensi yang ditentukan"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Lebih dari satu bundel ekstensi ditentukan"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Tampilkan ekstensi yang dipasang pengguna"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Tampilkan ekstensi yang dipasang sistem"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Tampilkan ekstensi yang diaktifkan"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Tampilkan ekstensi yang dinonaktifkan"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Tampilkan ekstensi dengan preferensi"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Tampilkan ekstensi dengan pemutakhiran"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Cetak detail ekstensi"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Daftar ekstensi yang dipasang"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "BERKAS"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Sumber tambahan untuk disertakan dalam bundel"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SKEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Skema GSettings yang harus dimasukkan"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIREKTORI"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Direktori tempat terjemahan ditemukan"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Domain gettext yang digunakan untuk terjemahan"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Timpa paket yang ada"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Direktori tempat paket harus dibuat"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIREKTORI_SUMBER"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Buat bundel ekstensi"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Lebih dari satu direktori sumber ditentukan"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Ekstensi \"%s\" tidak memiliki preferensi\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Gagal membuka preferensi untuk ekstensi \"%s\": %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Buka preferensi ekstensi"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Atur ulang ekstensi"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Tidak dapat menghapus pemasangan ekstensi sistem\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Gagal menghapus pemasangan \"%s\"\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Hapus ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Jangan cetak pesan kesalahan"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Path"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Penulis asli"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versi"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Kondisi"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "\"version\" tak menerima argumen"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Pemakaian:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Cetak informasi versi dan keluar."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "PERINTAH"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARG...]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Perintah:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Cetak bantuan"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Aktifkan ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Nonaktifkan ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Atur ulang ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Hapus ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Daftar ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Tampilkan info ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Buka preferensi ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Buat ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Ekstensi Paket"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Pasang bundel ekstensi"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Gunakan \"%s\" untuk mendapatkan bantuan terperinci.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Polos"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Ekstensi kosong"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Tambahkan ikon ke bilah atas"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u Keluaran"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u Masukan"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Suara Sistem"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Apakah adapter Bluetooth baku telah menyiapkan perangkat yang terasosiasi "
+#~ "dengannya"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Shell hanya akan menampilkan butir menu Bluetooth bila sebuah adapter "
+#~ "Bluetooth dinyalakan, atau ada perangkat yang telah disiapkan terasosiasi "
+#~ "dengan adapter baku. Ini akan di-reset bila adapter baku pernah terlihat "
+#~ "tidak punya perangkat yang terasosiasi dengannya."
+
+#~ msgid "Log Out"
+#~ msgstr "Keluar"
+
+#~ msgid "%s Off"
+#~ msgstr "%s Mati"
+
+#~ msgid "%s Connected"
+#~ msgstr "%s Tersambung"
+
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s Tak Dikelola"
+
+#~ msgid "%s Connecting"
+#~ msgstr "%s Menyambung"
+
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s Memerlukan Autentikasi"
+
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Firmware Hilang Untuk %s"
+
+#~ msgid "%s Unavailable"
+#~ msgstr "%s Tak tersedia"
+
+#~ msgid "%s Connection Failed"
+#~ msgstr "Koneksi %s Gagal"
+
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s Perangkat Keras Dinonaktifkan"
+
+#~ msgid "%s Disabled"
+#~ msgstr "%s Dinonaktifkan"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Sambungkan ke Internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Mode Pesawat Terbang Menyala"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Wi-Fi dinonaktifkan ketika mode pesawat terbang menyala."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Matikan Mode Pesawat Terbang"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi Mati"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Wi-Fi perlu dinyalakan untuk menyambung ke suatu jaringan."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Nyalakan Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Jaringan Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Pilih jaringan"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Pakai saklar perangkat keras untuk mematikan"
+
+#~ msgid "Select Network"
+#~ msgstr "Pilih Jaringan"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Pengaturan Wi-Fi"
+
+#~ msgid "%s Not Connected"
+#~ msgstr "%s Tak Tersambung"
+
+#~ msgid "unknown"
+#~ msgstr "tidak dikenal"
+
+#~ msgid "activating…"
+#~ msgstr "mengaktifkan…"
+
+#~ msgid "deactivating…"
+#~ msgstr "menonaktifkan…"
+
+#~ msgid "deactivated"
+#~ msgstr "dinonaktifkan"
+
+#~ msgid "connecting…"
+#~ msgstr "menyambung…"
+
+#~ msgid "authentication required"
+#~ msgstr "diperlukan autentikasi"
+
+#~ msgid "connection failed"
+#~ msgstr "koneksi gagal"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN Mati"
+
+#~ msgid "Network Settings"
+#~ msgstr "Pengaturan Jaringan"
+
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Koneksi Wi-Fi"
+
+#~ msgid "Power Settings"
+#~ msgstr "Pengaturan Daya"
+
+#~ msgid "Lock"
+#~ msgstr "Kunci"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Matikan / Log Keluar"
+
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d Tersambung"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth Nyala"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth Mati"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Lokasi Diaktifkan"
+
+#~ msgid "Disable"
+#~ msgstr "Nonaktifkan"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Pengaturan Privasi"
+
+#~ msgid "Location In Use"
+#~ msgstr "Lokasi Sedang Digunakan"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Koneksi Dinonaktifkan"
+
+#~ msgid "Enable"
+#~ msgstr "Fungsikan"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Lampu Malam Dinonaktifkan"
+
+#~ msgid "Resume"
+#~ msgstr "Lanjutkan"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Nonaktifkan Sampai Besok"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Terisi Penuh"
+
+#~ msgid "Not Charging"
+#~ msgstr "Tidak Mengisi Daya"
+
+#~ msgid "Estimating…"
+#~ msgstr "Memperkirakan…"
+
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d:%02d Tersisa (%d%%)"
+
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d:%02d Sampai Penuh (%d%%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Layar Sedang Dibagi"
+
+#~ msgid "Turn off"
+#~ msgstr "Matikan"
diff --git a/po/ie.po b/po/ie.po
new file mode 100644
index 0000000..3b614c4
--- /dev/null
+++ b/po/ie.po
@@ -0,0 +1,3045 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-12-12 06:33+0700\n"
+"PO-Revision-Date: 2022-12-12 06:49+0700\n"
+"Last-Translator: OIS <mistresssilvara@hotmail.com>\n"
+"Language-Team: IE\n"
+"Language: ie\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.12\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Lansatores"
+
+#: data/50-gnome-shell-launchers.xml:10
+#, fuzzy
+msgid "Activate favorite application 1"
+msgstr "Ear al carte %1"
+
+#: data/50-gnome-shell-launchers.xml:14
+#, fuzzy
+msgid "Activate favorite application 2"
+msgstr "Rapid-taste «%1» in li application «%2» por li action «%3»\n"
+
+#: data/50-gnome-shell-launchers.xml:18
+#, fuzzy
+msgid "Activate favorite application 3"
+msgstr "Rapid-taste «%1» in li application «%2» por li action «%3»\n"
+
+#: data/50-gnome-shell-launchers.xml:22
+#, fuzzy
+msgid "Activate favorite application 4"
+msgstr "Parametres del Calendare de Xfce 4 (Orage)"
+
+#: data/50-gnome-shell-launchers.xml:26
+#, fuzzy
+msgid "Activate favorite application 5"
+msgstr "Monstra icones de _applicationes in menú"
+
+#: data/50-gnome-shell-launchers.xml:30
+#, fuzzy
+msgid "Activate favorite application 6"
+msgstr "Monstra icones de _applicationes in menú"
+
+#: data/50-gnome-shell-launchers.xml:34
+#, fuzzy
+msgid "Activate favorite application 7"
+msgstr "Monstra icones de _applicationes in menú"
+
+#: data/50-gnome-shell-launchers.xml:38
+#, fuzzy
+msgid "Activate favorite application 8"
+msgstr "Monstra icones de _applicationes in menú"
+
+#: data/50-gnome-shell-launchers.xml:42
+#, fuzzy
+msgid "Activate favorite application 9"
+msgstr "Monstra icones de _applicationes in menú"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Capturas de ecran"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+#, fuzzy
+msgid "Take a screenshot interactively"
+msgstr "Capter li tot ecran"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Capter li ecran"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Capter un fenestre"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+#, fuzzy
+msgid "Record a screencast interactively"
+msgstr "Serchar por dominias e hostes"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+#, fuzzy
+msgid "Show the notification list"
+msgstr "Monstrar un notification"
+
+#: data/50-gnome-shell-system.xml:12
+#, fuzzy
+msgid "Focus the active notification"
+msgstr "Monstrar li notification sur li activ monitor."
+
+#: data/50-gnome-shell-system.xml:15
+#, fuzzy
+msgid "Show the overview"
+msgstr "Compendie"
+
+#: data/50-gnome-shell-system.xml:18
+#, fuzzy
+msgid "Show all applications"
+msgstr "Monstrar omni applicationes"
+
+#: data/50-gnome-shell-system.xml:21
+#, fuzzy
+msgid "Open the application menu"
+msgstr "Aperter li menú de application"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Shell GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+#, fuzzy
+msgid "Window management and application launching"
+msgstr "Control de fenestres"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:16
+#, fuzzy
+msgid "UUIDs of extensions to enable"
+msgstr "_Extensiones:"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+#, fuzzy
+msgid "UUIDs of extensions to force disabling"
+msgstr "Extensiones"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+#, fuzzy
+msgid "Disable user extensions"
+msgstr "Desactivar omni _extensiones"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+#, fuzzy
+msgid "History for command (Alt-F2) dialog"
+msgstr "_Gardar correspondenties in li diarium"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:132
+#, fuzzy
+msgid "Layout of the app picker"
+msgstr "_Arangeament"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+#, fuzzy
+msgid "Keybinding to open the application menu"
+msgstr "Aperter li menú"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+#, fuzzy
+msgid "Keybinding to open the application menu."
+msgstr "Aperter li menú"
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+#, fuzzy
+msgid "Keybinding to shift between overview states"
+msgstr "Aperter _per..."
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:169
+#, fuzzy
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Monstra icones de _applicationes in menú"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+#, fuzzy
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Monstra icones de _applicationes in menú"
+
+#: data/org.gnome.shell.gschema.xml.in:177
+#, fuzzy
+msgid "Keybinding to open the overview"
+msgstr "Compendie"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:191
+#, fuzzy
+msgid "Keybinding to focus the active notification"
+msgstr "Monstrar li notification sur li activ monitor."
+
+#: data/org.gnome.shell.gschema.xml.in:192
+#, fuzzy
+msgid "Keybinding to focus the active notification."
+msgstr "Monstrar li notification sur li activ monitor."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 1"
+msgstr "Cambiar un application"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 2"
+msgstr "Cambiar un application"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 3"
+msgstr "Ear al playlist 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 4"
+msgstr "Ear al playlist 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 5"
+msgstr "Cambiar un application"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 6"
+msgstr "Activar labor-spacie 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 7"
+msgstr "Activar labor-spacie 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 8"
+msgstr "Activar labor-spacie 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+#, fuzzy
+#| msgid "Show Applications"
+msgid "Switch to application 9"
+msgstr "Activar labor-spacie 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+#, fuzzy
+msgid "Limit switcher to current workspace."
+msgstr "Monstra _nómines de labor-spacies"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:273
+#, fuzzy
+msgid "The application icon mode."
+msgstr "Null icone"
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Localisationes"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+#, fuzzy
+msgid "The locations to show in world clocks"
+msgstr "Monstrar altri localisationes"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+#, fuzzy
+msgid "Automatic location"
+msgstr "Ne automaticmen: "
+
+#: data/org.gnome.shell.gschema.xml.in:305
+#, fuzzy
+msgid "Whether to fetch the current location or not"
+msgstr "Si it es activ, li images va esser obtenet de un lontan localisation"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Localisation"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+#, fuzzy
+msgid "The location for which to show a forecast"
+msgstr "Monstrar li localisation del apuntator pos un presse de taste"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+#, fuzzy
+msgid "Attach modal dialog to the parent window"
+msgstr "Fenestre superior del dialog"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:342
+#, fuzzy
+msgid "Workspaces are managed dynamically"
+msgstr "Dina_mic titul:"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+#, fuzzy
+msgid "Workspaces only on primary monitor"
+msgstr "Primari monitor"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+#, fuzzy
+msgid "Network Login"
+msgstr "Inregistration"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+#, fuzzy
+msgid "Something’s gone wrong"
+msgstr "Un errore evenit"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detallies technic"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Hem-págine"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+#, fuzzy
+msgid "Visit extension homepage"
+msgstr "Visitar li hem-págine del componente"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Anullar"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Contrasigne"
+
+#: js/gdm/loginDialog.js:317
+#, fuzzy
+msgid "Choose Session"
+msgstr "Selecter un session"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Ne es in li liste?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(p.ex. usator o %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nómine de usator"
+
+#: js/gdm/loginDialog.js:1258
+#, fuzzy
+msgid "Login Window"
+msgstr "Fenestre de inicie"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Errore de autentication"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+#, fuzzy
+msgid "(or swipe finger across reader)"
+msgstr "Trena un fingre trans li letor"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+#, fuzzy
+msgid "(or place finger on reader)"
+msgstr "Posi un fingre sur li letor"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Extinter"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Restartar"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+#, fuzzy
+msgid "reboot;restart;"
+msgstr "_Reiniciar"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Serrar li ecran"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+#, fuzzy
+msgid "lock screen"
+msgstr "Serrar li ecran"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Cluder li session"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+#, fuzzy
+msgid "logout;log out;sign off"
+msgstr "Cluder li session sin monstrar li dialog de clusion"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspender"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+#, fuzzy
+#| msgid "Suspend"
+msgid "suspend;sleep"
+msgstr "Suspender"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Cambiar li usator"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+#, fuzzy
+#| msgid "Switch User"
+msgid "switch user"
+msgstr "Cambiar li usator"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+#, fuzzy
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Capter li ecran"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+
+#: js/misc/systemActions.js:242
+#, fuzzy
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Rotation"
+
+#: js/misc/systemActions.js:243
+#, fuzzy
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Serrar li ecran"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Comande ne esset trovat"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+#, fuzzy
+msgid "Could not parse command:"
+msgstr "Ne successat analisar parametres de linea de comandes: %s\n"
+
+#: js/misc/util.js:174
+#, fuzzy, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Execution de «%s» ne successat:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Nu"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "ante %d minute"
+msgstr[1] "ante %d minutes"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "ante %d hor"
+msgstr[1] "ante %d hores"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Yer"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d die"
+msgstr[1] "%d dies"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "ante %d semane"
+msgstr[1] "ante %d semanes"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "ante %d mensu"
+msgstr[1] "ante %d mensus"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "ante %d annu"
+msgstr[1] "ante %d annus"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Yer a %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, fuzzy, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%e %b, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, fuzzy, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d %b %y, %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+#, fuzzy
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Yer, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, fuzzy, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%x, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, fuzzy, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%e %b, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, fuzzy, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-e %b %Y %l:%M %p"
+
+#: js/portalHelper/main.js:55
+#, fuzzy
+msgid "Hotspot Login"
+msgstr "Hotspot"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+#, fuzzy
+msgid "Deny Access"
+msgstr "Refusar:"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+#, fuzzy
+msgid "Grant Access"
+msgstr "Sin accesse"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Fólder sin nómine"
+
+#: js/ui/appFavorites.js:166
+#, fuzzy, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "Li bux «%s» sta removet"
+
+#: js/ui/appFavorites.js:199
+#, fuzzy, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Li aparate «%s» ha esset securmen removet ex li sistema. "
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Apertet fenestres"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nov fenestre"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Monstrar detallies"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Salir"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Defixar"
+
+#: js/ui/appMenu.js:158
+#, fuzzy
+msgid "Pin to Dash"
+msgstr "PIN:"
+
+#: js/ui/appMenu.js:175
+#, fuzzy
+msgid "Launch using Integrated Graphics Card"
+msgstr "Lansar usante li separat grafic carte"
+
+#: js/ui/appMenu.js:176
+#, fuzzy
+msgid "Launch using Discrete Graphics Card"
+msgstr "Lansar usante li separat grafic carte"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Selecter un audioaparate"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Parametres de son"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Escutatores"
+
+#: js/ui/audioDeviceSelection.js:71
+#, fuzzy
+msgid "Headset"
+msgstr "Garnitura"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Microfon"
+
+#: js/ui/backgroundMenu.js:14
+#, fuzzy
+msgid "Change Background…"
+msgstr "Alterar li _funde"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Parametres de monitor"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Parametres"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "So"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Lu"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Ma"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Me"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Jo"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Ve"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Sa"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%B %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Li precedent mensu"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Li sequent mensu"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Semane %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Null notificationes"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Ne disturbar"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Vacuar"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, fuzzy, javascript-format
+msgid "“%s” is not responding."
+msgstr "<tt>%s</tt> ne responde."
+
+#: js/ui/closeDialog.js:41
+#, fuzzy
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Vu posse atender un moment por permisser it continuar, o terminar li "
+"application completmen."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Terminar"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Atender"
+
+#: js/ui/components/automountManager.js:84
+#, fuzzy
+msgid "External drive connected"
+msgstr "Conexet"
+
+#: js/ui/components/automountManager.js:96
+#, fuzzy
+msgid "External drive disconnected"
+msgstr "Extern"
+
+#: js/ui/components/automountManager.js:207
+#, fuzzy
+msgid "Unable to unlock volume"
+msgstr "Ne posset desmonter li volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Aperter per %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Connexer"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Clave"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Contrasigne del clave privat"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitá"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Servicie"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autentication es besonat"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+#, fuzzy
+msgid "Wired 802.1X authentication"
+msgstr "Autentication"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nómine de rete"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autentication DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+#, fuzzy
+msgid "PIN code required"
+msgstr "Intra un code PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, fuzzy, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Un nómine e contrasigne es besonat por li accesse a «%s»"
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Gerente de rete"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Contrasigne de VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Autentication es postulat"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autenticar"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr ""
+
+#: js/ui/components/telepathyClient.js:828
+#, fuzzy, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s %s es disponibil nu"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Fenestres"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Monstrar applicationes"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+#, fuzzy
+msgid "Dash"
+msgstr "Simbol en-dash --"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %-e %b %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%e %b"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Hodie"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Deman"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Li tot die"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d.%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Null evenimentes"
+
+#: js/ui/dateMenu.js:396
+#, fuzzy
+msgid "Add world clocks…"
+msgstr "Horloges"
+
+#: js/ui/dateMenu.js:397
+#, fuzzy
+msgid "World Clocks"
+msgstr "Horloges"
+
+#: js/ui/dateMenu.js:681
+#, fuzzy
+#| msgid "Searching…"
+msgid "Loading…"
+msgstr "Cargante..."
+
+#: js/ui/dateMenu.js:691
+#, fuzzy
+msgid "Go online for weather information"
+msgstr "Te_mpe"
+
+#: js/ui/dateMenu.js:693
+#, fuzzy
+msgid "Weather information is currently unavailable"
+msgstr "Te_mpe"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Tempe"
+
+#: js/ui/dateMenu.js:705
+#, fuzzy
+msgid "Select weather location…"
+msgstr "Tempe"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Cluder li session de %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Cluder li session"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Cluder li session"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Extintion"
+
+#: js/ui/endSessionDialog.js:65
+#, fuzzy
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installar actualisamentes"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+#, fuzzy
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installar actualisamentes"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Extinter"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Restarta"
+
+#: js/ui/endSessionDialog.js:84
+#, fuzzy
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Installar actualisamentes"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Restartar"
+
+#: js/ui/endSessionDialog.js:103
+#, fuzzy
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Installar actualisamentes"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Restartar e installar"
+
+#: js/ui/endSessionDialog.js:115
+#, fuzzy
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Restartar e installar"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:123
+#, fuzzy
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Restartar e _installar"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:299
+#, fuzzy
+msgid "Other users are logged in"
+msgstr ""
+"Altri usatores have sessiones apert, extintion nu va terminar anc ti "
+"sessiones."
+
+#: js/ui/endSessionDialog.js:470
+#, fuzzy
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Inicialisation:"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (lontan)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (console)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installar"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Installar un extension"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+
+#: js/ui/extensionSystem.js:270
+#, fuzzy
+msgid "Extension Updates Available"
+msgstr "Actualisamentes es disponibil"
+
+#: js/ui/extensionSystem.js:271
+#, fuzzy
+msgid "Extension updates are ready to be installed."
+msgstr "Li bux «%s» ha esset installat e es pret a usa"
+
+#: js/ui/inhibitShortcutsDialog.js:75
+#, fuzzy
+msgid "Allow inhibiting shortcuts"
+msgstr "Rapid-tastes"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:79
+#, fuzzy
+msgid "An application wants to inhibit shortcuts"
+msgstr "Modificar parametres de tastatura a rapid-tastes de applicationes"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Refusar"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Permisser"
+
+#: js/ui/kbdA11yDialog.js:32
+#, fuzzy
+msgid "Slow Keys Turned On"
+msgstr "Avise de lent tastes"
+
+#: js/ui/kbdA11yDialog.js:33
+#, fuzzy
+msgid "Slow Keys Turned Off"
+msgstr "Bluetooth desactivat"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:40
+#, fuzzy
+msgid "Sticky Keys Turned On"
+msgstr "Avise de restant tastes"
+
+#: js/ui/kbdA11yDialog.js:41
+#, fuzzy
+msgid "Sticky Keys Turned Off"
+msgstr "Bluetooth desactivat"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55
+#, fuzzy
+msgid "Leave On"
+msgstr "Lassar it"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Activar"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Desactivar"
+
+#: js/ui/kbdA11yDialog.js:65
+#, fuzzy
+msgid "Leave Off"
+msgstr "Lassar it"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Parametres de lingue e region"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Null extensiones installat"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Celar errores"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Monstrar errores"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Permisset"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Despermisset"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Errore"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Obsolet"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Descarga"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Vider li fonte"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Págine Web"
+
+#: js/ui/main.js:286
+#, fuzzy
+msgid "System was put in unsafe mode"
+msgstr "Mode de somnie del sistema:"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr ""
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Defar"
+
+#: js/ui/main.js:334
+#, fuzzy
+msgid "Logged in as a privileged user"
+msgstr "Avise: %d usator have un session ancor apert."
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+
+#: js/ui/main.js:384
+#, fuzzy
+msgid "Screen Lock disabled"
+msgstr "Serrar li ecran"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Information del sistema"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Ínconosset artist"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Ínconosset titul"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Tippa por serchar"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Applicationes"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Compendie"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nov rapid-taste…"
+
+#: js/ui/padOsd.js:154
+#, fuzzy
+#| msgid "Applications"
+msgid "Application defined"
+msgstr "Null marca-págines definit"
+
+#: js/ui/padOsd.js:155
+#, fuzzy
+msgid "Show on-screen help"
+msgstr "Monstrar un baniere de inicie"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Cambiar li monitor"
+
+#: js/ui/padOsd.js:157
+#, fuzzy
+msgid "Assign keystroke"
+msgstr "Assignar"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Finit"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Modificar…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Null"
+
+#: js/ui/padOsd.js:856
+#, fuzzy
+msgid "Press a button to configure"
+msgstr "Presse un buton de mus "
+
+#: js/ui/padOsd.js:857
+#, fuzzy
+msgid "Press Esc to exit"
+msgstr "Esc"
+
+#: js/ui/padOsd.js:860
+#, fuzzy
+msgid "Press any key to exit"
+msgstr "Li taste de captura del ecran:"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Activitás"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+#, fuzzy
+msgid "Top Bar"
+msgstr "\\bar{}"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Lansar un comande"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Tippa Esc por cluder"
+
+#: js/ui/runDialog.js:238
+#, fuzzy
+msgid "Restart is not available on Wayland"
+msgstr "Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Restartante..."
+
+#: js/ui/screenShield.js:235
+#, fuzzy
+msgid "GNOME needs to lock the screen"
+msgstr "Serrar li ecran _ante que somniar"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+#, fuzzy
+msgid "Unable to lock"
+msgstr "S_errar"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+#, fuzzy
+msgid "Lock was blocked by an application"
+msgstr "S_errar"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Selection"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Selection de un area"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Ecran"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Selection de un ecran"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Fenestre"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Selection de fenestres"
+
+#: js/ui/screenshot.js:1239
+#, fuzzy
+msgid "Screenshot / Screencast"
+msgstr "Captura del ecran"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Monstrar li apuntator"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr ""
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, fuzzy, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Restituer %d elementes ex li Paper-corb"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Captura del ecran"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+#, fuzzy
+msgid "Screencast recorded"
+msgstr "Gardar li registrat canzone"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr ""
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Monstrar in Files"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Captura de %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+#, fuzzy
+msgid "Screenshot captured"
+msgstr "Un rectangulari region esset capturat"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr ""
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Ecran ha esset capturat"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Serchante..."
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Null resultates."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d plu"
+msgstr[1] "%d plu"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Serchar"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copiar"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Collar"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Monstrar textu"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Celar textu"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps Lock es activ."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Celat volume"
+
+#: js/ui/shellMountOperation.js:289
+#, fuzzy
+msgid "Windows System Volume"
+msgstr "_Sistema Windows"
+
+#: js/ui/shellMountOperation.js:292
+#, fuzzy
+msgid "Uses Keyfiles"
+msgstr "_Clav-files"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:307
+#, fuzzy
+msgid "PIM Number"
+msgstr "Secun li _númere"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Memorar li contrasigne"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desserrar"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Aperter %s"
+
+#: js/ui/shellMountOperation.js:424
+#, fuzzy
+msgid "The PIM must be a number or empty."
+msgstr "ID deve esser present e unic.\n"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Ne successat startar %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr ""
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accessibilitá"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Alt contraste"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Scalar"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Letor del ecran"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Tastatura visual"
+
+#: js/ui/status/accessibility.js:62
+#, fuzzy
+msgid "Visual Alerts"
+msgstr "Visual"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Restant tastes"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Lent tastes"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Restant tastes"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Tastes del mus"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Grand textu"
+
+#: js/ui/status/autoRotate.js:14
+#, fuzzy
+msgid "Auto Rotate"
+msgstr "Autorotar e -centrar"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Luciditá"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Mode obscur"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Singul clic"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Duplic clic"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Tirar"
+
+#: js/ui/status/dwellClick.js:27
+#, fuzzy
+msgid "Secondary Click"
+msgstr "Secundari"
+
+#: js/ui/status/dwellClick.js:36
+#, fuzzy
+msgid "Dwell Click"
+msgstr "o clicca li icone "
+
+#: js/ui/status/keyboard.js:833
+msgid "Keyboard"
+msgstr "Tastatura"
+
+#: js/ui/status/keyboard.js:850
+msgid "Show Keyboard Layout"
+msgstr "Monstrar li arangeament del tastatura"
+
+#: js/ui/status/location.js:330
+#, fuzzy
+msgid "Allow location access"
+msgstr "Pe_rmisser lontan accesse"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr ""
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<ínconosset>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, fuzzy, javascript-format
+msgid "Disconnect %s"
+msgstr "_Desconexer"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, fuzzy, javascript-format
+msgid "Connect to %s"
+msgstr "Ne successat conexer a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, fuzzy, javascript-format
+msgid "%s Hotspot"
+msgstr "Hotspot"
+
+#: js/ui/status/nightLight.js:20
+#, fuzzy
+msgid "Night Light"
+msgstr "Nocte"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Productivitá"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr ""
+
+#: js/ui/status/powerProfiles.js:30
+#, fuzzy
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Garda-ecran"
+
+#: js/ui/status/powerProfiles.js:70
+#, fuzzy
+msgid "Power Profiles"
+msgstr "_Accender"
+
+#: js/ui/status/remoteAccess.js:74
+#, fuzzy
+msgid "Stop Screencast"
+msgstr " --stop Stoppar li reproduction\n"
+
+#: js/ui/status/remoteAccess.js:144
+#, fuzzy
+msgid "Stop Screen Sharing"
+msgstr "_Stoppar"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Mode de avion"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Capturar li ecran"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menú de extintion"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspender"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Restartar…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Extinter…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Cluder li session…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Cambiar li usator…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Serrar li ecran"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Ínconosset aparate Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:321
+#, fuzzy
+msgid "Unauthorized Thunderbolt device"
+msgstr "aparate"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:328
+#, fuzzy
+msgid "Thunderbolt authorization error"
+msgstr "Autorisation"
+
+#: js/ui/status/thunderbolt.js:329
+#, fuzzy, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Ne posset accessar li aparate «%s»"
+
+#: js/ui/status/volume.js:194
+#, fuzzy
+msgid "Volume changed"
+msgstr "Modificat:"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volúmine"
+
+#: js/ui/status/volume.js:272
+#, fuzzy
+#| msgid "Sound Settings"
+msgid "Sound Output"
+msgstr "Sele_cte un aparate por li exeada de son:"
+
+#: js/ui/status/volume.js:340
+#, fuzzy
+msgid "Sound Input"
+msgstr "Sele_cte un aparate por li intrada de son:"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Duplicar"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+#, fuzzy
+msgid "Join Displays"
+msgstr "Adherer"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Solmen extern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+#, fuzzy
+msgid "Built-in Only"
+msgstr "Standard"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%a, %d %b"
+
+#: js/ui/unlockDialog.js:370
+#, fuzzy
+msgid "Swipe up to unlock"
+msgstr "Trenar a levul"
+
+#: js/ui/unlockDialog.js:371
+#, fuzzy
+msgid "Click or press a key to unlock"
+msgstr "Li taste de captura del ecran:"
+
+#: js/ui/unlockDialog.js:554
+#, fuzzy
+msgid "Unlock Window"
+msgstr "D_esserrar"
+
+#: js/ui/unlockDialog.js:563
+#, fuzzy
+msgid "Log in as another user"
+msgstr "Cluder li session o cambiar li usator"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Benevenit a GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ne, mersí"
+
+#: js/ui/welcomeDialog.js:51
+#, fuzzy
+msgid "Take Tour"
+msgstr "Far un pause"
+
+#: js/ui/windowAttentionHandler.js:20
+#, fuzzy, javascript-format
+msgid "“%s” is ready"
+msgstr "Li bux «%s» ha esset installat e es pret a usa"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+#, fuzzy
+msgid "Keep these display settings?"
+msgstr "_Configurar parametres de monitor..."
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Reverter"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Retener"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Celar"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restituer"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximisar"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Mover"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: js/ui/windowMenu.js:72
+#, fuzzy
+msgid "Move Titlebar Onscreen"
+msgstr "Tastatura sur ecran"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Sempre in avan"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Sempre in li visibil labor-spacie"
+
+#: js/ui/windowMenu.js:110
+#, fuzzy
+msgid "Move to Workspace Left"
+msgstr "Mover al labor-spacie a _levul"
+
+#: js/ui/windowMenu.js:116
+#, fuzzy
+msgid "Move to Workspace Right"
+msgstr "Mover al labor-spacie a dextri"
+
+#: js/ui/windowMenu.js:122
+#, fuzzy
+msgid "Move to Workspace Up"
+msgstr "Mover al labor-spacie ad _up"
+
+#: js/ui/windowMenu.js:128
+#, fuzzy
+msgid "Move to Workspace Down"
+msgstr "Mover al labor-spacie a _bass"
+
+#: js/ui/windowMenu.js:146
+#, fuzzy
+msgid "Move to Monitor Up"
+msgstr "Mover al monitor "
+
+#: js/ui/windowMenu.js:155
+#, fuzzy
+msgid "Move to Monitor Down"
+msgstr "Mover un fenestre al monitor a-bass"
+
+#: js/ui/windowMenu.js:164
+#, fuzzy
+msgid "Move to Monitor Left"
+msgstr "Mover un fenestre al west (levul) monitor"
+
+#: js/ui/windowMenu.js:173
+#, fuzzy
+msgid "Move to Monitor Right"
+msgstr "Mover un fenestre al ost (dextri) monitor"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Clúder"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+#, fuzzy
+msgid "Evolution Calendar"
+msgstr "Un tache in un calendare de Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Printar li version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+
+#: src/main.c:453
+#, fuzzy
+msgid "List possible modes"
+msgstr "Modes"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ínconosset"
+
+#: src/shell-app.c:556
+#, fuzzy, c-format
+msgid "Failed to launch “%s”"
+msgstr "Ne successat lansar «%s»"
+
+#: src/shell-keyring-prompt.c:764
+#, fuzzy
+msgid "Passwords do not match."
+msgstr "Contrasignes"
+
+#: src/shell-keyring-prompt.c:772
+#, fuzzy
+msgid "Password cannot be blank"
+msgstr "_Eraser"
+
+#: src/shell-polkit-authentication-agent.c:344
+#, fuzzy
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Dialog de autentication esset anullat per li usator"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensiones"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#, fuzzy
+msgid "Manage your GNOME Extensions"
+msgstr "Escutar e gerer vor musica"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Li projecte GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+#, fuzzy
+msgid "Configure GNOME Shell Extensions"
+msgstr "Shell GNOME"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Null corespondenties"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Remover «%s»?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Remover"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "OIS <mistresssilvara@hotmail.com>, 2016-22"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+#, fuzzy
+msgid "The extension had an error"
+msgstr "errore de EXTENSION"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+#, fuzzy
+msgid "The extension can be updated"
+msgstr "Extension:"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Website"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Remover…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Auxilie"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Pri Extensiones"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+#, fuzzy
+msgid "Manually Installed"
+msgstr "Continuar usar li driver installat manualmen"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+#, fuzzy
+msgid "Built-In"
+msgstr "Standard"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Null installat extensiones"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+#, fuzzy
+msgid "Extension Updates Ready"
+msgstr "Extension:"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, fuzzy, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "«%s» har esset creat con success"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nómine"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descrition"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:363
+#, fuzzy
+msgid "Choose one of the available templates:\n"
+msgstr "Null claves disponibil. Crea ún!"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Modelle"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NÓMINE"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+#, fuzzy
+msgid "The user-visible name of the new extension"
+msgstr "Li visibil nómine del renominator"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRITION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODELLE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+#, fuzzy
+msgid "The template to use for the new extension"
+msgstr "Usar un modelle quam on composi nov missages"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+#, fuzzy
+msgid "Enter extension information interactively"
+msgstr "Ples intrar li information pri vor equip"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+#, fuzzy
+msgid "Create a new extension"
+msgstr "Crear un nov"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+#, fuzzy
+msgid "Unknown arguments"
+msgstr "[ARGUMENTES...]"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+#, fuzzy
+msgid "UUID, name and description are required"
+msgstr "{description} ({name})"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+#, fuzzy
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Shell GNOME"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, fuzzy, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "%s: %s existiert nicht, muss es jedoch."
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+#, fuzzy
+msgid "Disable an extension"
+msgstr "Depermisser"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+#, fuzzy
+msgid "No UUID given"
+msgstr ""
+"\n"
+"UUID: "
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+#, fuzzy
+msgid "More than one UUID given"
+msgstr "Plu quam ún file es providet"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+#, fuzzy
+msgid "Enable an extension"
+msgstr "Secun _extension"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, fuzzy, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Li file ne existe o es vacui."
+
+#: subprojects/extensions-tool/src/command-info.c:85
+#, fuzzy
+msgid "Show extensions info"
+msgstr "Monstrar panel de information"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+#, fuzzy
+msgid "Overwrite an existing extension"
+msgstr "Superscrir li existent data con zeros"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+#, fuzzy
+msgid "EXTENSION_BUNDLE"
+msgstr "Null files de fonte o files traductibil in li bundle"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+#, fuzzy
+msgid "Install an extension bundle"
+msgstr "Installar un extension"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+#, fuzzy
+msgid "No extension bundle specified"
+msgstr "Ínspecificat"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+#, fuzzy
+msgid "Show user-installed extensions"
+msgstr "Null extensiones installat"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+#, fuzzy
+msgid "Show system-installed extensions"
+msgstr "Null extensiones installat"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+#, fuzzy
+msgid "Show enabled extensions"
+msgstr "Permisset"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+#, fuzzy
+msgid "Show disabled extensions"
+msgstr "Ínactiv"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+#, fuzzy
+msgid "Show extensions with preferences"
+msgstr "Monstrar li dialog «Preferenties del panel»"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+#, fuzzy
+msgid "Show extensions with updates"
+msgstr "Monstrar actualisamentes"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+#, fuzzy
+msgid "Print extension details"
+msgstr "&Detallies"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+#, fuzzy
+msgid "List installed extensions"
+msgstr "Null extensiones installat"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+#, fuzzy
+msgid "Additional source to include in the bundle"
+msgstr "Null files de fonte o files traductibil in li bundle"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORIA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+#, fuzzy
+msgid "The directory where translations are found"
+msgstr "Fólder ne esset trovat"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMINIA"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+#, fuzzy
+msgid "The gettext domain to use for translations"
+msgstr "Gettext retrodat un vacui liste de dominias."
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+#, fuzzy
+msgid "Overwrite an existing pack"
+msgstr "superscrir existent files"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+#, fuzzy
+msgid "SOURCE_DIRECTORY"
+msgstr "directoria"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+#, fuzzy
+msgid "Create an extension bundle"
+msgstr "Null files de fonte o files traductibil in li bundle"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, fuzzy, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "File %s ne have un extension .wav"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, fuzzy, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Ne successat aperter li file «%s»"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+#, fuzzy
+msgid "Opens extension preferences"
+msgstr "Preferenties…"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+#, fuzzy
+msgid "Reset an extension"
+msgstr "Extension:"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+#, fuzzy
+msgid "Cannot uninstall system extensions\n"
+msgstr "T-ci es un applette de sistema. It es impossibil remover it."
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, fuzzy, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "_Desinstallar..."
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+#, fuzzy
+msgid "Uninstall an extension"
+msgstr "_Desinstallar..."
+
+#: subprojects/extensions-tool/src/main.c:72
+#, fuzzy
+msgid "Do not print error messages"
+msgstr "Monstrar errores de auto-conexion"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Rute"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Original autor"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Version"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Statu"
+
+#: subprojects/extensions-tool/src/main.c:294
+#, fuzzy
+msgid "“version” takes no arguments"
+msgstr "[ARGUMENTES...]"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Usage:"
+
+#: subprojects/extensions-tool/src/main.c:299
+#, fuzzy
+msgid "Print version information and exit."
+msgstr "Monstra li information del version, poy salir"
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMANDE"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Comandes:"
+
+#: subprojects/extensions-tool/src/main.c:320
+#, fuzzy
+msgid "Print help"
+msgstr " -h, --help Monstra ti-ci textu e surtir\n"
+
+#: subprojects/extensions-tool/src/main.c:322
+#, fuzzy
+msgid "Enable extension"
+msgstr "Secun _extension"
+
+#: subprojects/extensions-tool/src/main.c:323
+#, fuzzy
+msgid "Disable extension"
+msgstr "Depermisser"
+
+#: subprojects/extensions-tool/src/main.c:324
+#, fuzzy
+msgid "Reset extension"
+msgstr "Extension:"
+
+#: subprojects/extensions-tool/src/main.c:325
+#, fuzzy
+msgid "Uninstall extension"
+msgstr "_Desinstallar..."
+
+#: subprojects/extensions-tool/src/main.c:326
+#, fuzzy
+msgid "List extensions"
+msgstr "_Extensiones:"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+#, fuzzy
+msgid "Show extension info"
+msgstr "Monstrar panel de information"
+
+#: subprojects/extensions-tool/src/main.c:329
+#, fuzzy
+msgid "Open extension preferences"
+msgstr "Aperter _preferenties del funde"
+
+#: subprojects/extensions-tool/src/main.c:330
+#, fuzzy
+msgid "Create extension"
+msgstr "Secun _extension"
+
+#: subprojects/extensions-tool/src/main.c:331
+#, fuzzy
+msgid "Package extension"
+msgstr "Paccage"
+
+#: subprojects/extensions-tool/src/main.c:332
+#, fuzzy
+msgid "Install extension bundle"
+msgstr "Installar un extension"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Simplic"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+#, fuzzy
+msgid "An empty extension"
+msgstr "Secun _extension"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+#, fuzzy
+msgid "Add an icon to the top bar"
+msgstr "Adjunter un icone"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sones de sistema"
diff --git a/po/is.po b/po/is.po
new file mode 100644
index 0000000..6e8b46e
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,3439 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Sveinn í Felli <sv1@fellsnet.is>, 2015, 2016, 2017, 2019, 2021, 2022.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-11-04 14:57+0000\n"
+"PO-Revision-Date: 2022-11-30 09:47+0000\n"
+"Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n"
+"Language-Team: Icelandic\n"
+"Language: is\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 21.12.3\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Forritaræsar"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Virkja eftirlætisforrit 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Virkja eftirlætisforrit 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Virkja eftirlætisforrit 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Virkja eftirlætisforrit 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Virkja eftirlætisforrit 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Virkja eftirlætisforrit 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Virkja eftirlætisforrit 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Virkja eftirlætisforrit 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Virkja eftirlætisforrit 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2070
+msgid "Screenshots"
+msgstr "Skjámyndir"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Taka skjámynd gagnvirkt"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Taka skjámynd"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Taka skjámynd af glugga"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Taka upp skjámyndskeið gagnvirkt"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Kerfið"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Birta tilkynningalistann"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Áhersla á virka tilkynningu"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Birta yfirlit"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Birta öll forrit"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Birta forritavalmynd"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME skel"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gluggastjórnun og forritaræsing"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID-auðkenni forritsviðauka sem á að gera virka"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID-auðkenni viðbóta sem á að þvinga til að gera óvirkar"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Gera forritsviðauka notanda óvirka"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Listi yfir auðkenni skjáborðsskráa fyrir uppáhaldsforrit"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Ferill skipanalínuglugga (Alt+F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Ferill looking-glass skipanalínuglugga (Alt+F)"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Alltaf sýna \"Skrá út\" atriðið í notandavalmyndinni."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Hvort muna eigi lykilorð þegar tengd eru fjartengd eða dulrituð skráakerfi"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Síðast valið en ekki sjálfgefið orkustýringarsnið"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Framsetning forritaveljara"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Lyklasamsetning til að opna forritavalmyndina"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Lyklasamsetning til að opna forritavalmyndina."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Lyklasamsetning til að skipta á milli yfirlitsstöðu"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Lyklasamsetning til að skipta á milli setu, gluggaveljara og forritareita"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Lyklasamsetning til að skipta á milli forritareita, gluggaveljara og setu"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Lyklasamsetning til að opna \"Birta forrit\" sýnina"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Lyklasamsetning til að opna \"Birta forrit\" sýnina í virkniyfirlitinu."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Lyklasamsetning til að opna yfirlitið"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Lyklasamsetning til að opna virkniyfirlitið."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Lyklasamsetning til að víxla sýnileika tilkynningalistans"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Lyklasamsetning til að víxla sýnileika tilkynningalistans."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Lyklasamsetning til að setja virkni á virka tilkynningu"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Lyklasamsetning til að setja virkni á virka tilkynningu."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Skipta í forrit 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Skipta í forrit 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Skipta í forrit 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Skipta í forrit 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Skipta í forrit 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Skipta í forrit 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Skipta í forrit 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Skipta í forrit 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Skipta í forrit 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Takmarka fletti við núverandi vinnusvæði."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ef þetta er satt, verða eingöngu birt forrit sem eru með glugga á núverandi "
+"vinnusvæði í flettinum.Annars eru öll forrit höfð með."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Táknmyndinahamur forrits."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Stillir hvernig gluggar birtast í skiptinum. Möguleg gildi eru 'einungis-"
+"smámynd' (birtir smámynd gluggans), 'einungis-forritstákn' (birtir aðeins "
+"táknmynd forritsins) eða 'bæði'."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ef þetta er satt, verða eingöngu birtir gluggar frá núverandi vinnusvæði í "
+"flettinum.Annars eru allir gluggar hafðir með."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Staðsetningar"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Staðsetningar sem á að birta í heimsklukkum"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Sjálfvirk staðsetning"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Hvort eigi að sækja núverandi staðsetningu eða ekki"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Staðsetning"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Staðsetningin sem á að birta veðurspá fyrir"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Festa kvaðningarglugga við yfirglugga"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Þetta hefur forgang fram yfir lykilinn í kjörstillingum org.gnome.mutter "
+"þegar GNOME-skelin er keyrð."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Virkja flísalögn við jaðra þegar gluggum er sleppt á skjájaðra"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Vinnusvæðum er stýrt eftir þörfum"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Vinnusvæði einungis á aðalskjá"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Fresta breytingum á virkni í músarham þar til bendillinn hefur stöðvast"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Innskráning á neti"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Eitthvað hefur farið úrskeiðis"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Því miður kom upp vandamál: ekki er hægt að birta stillingarnar fyrir þennan "
+"forritsviðauka. Við mælum með því að þú tilkynnir þetta vandamál til höfunda "
+"forritsviðaukans."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Tæknilegar upplýsingar"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Heimasíða"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Heimsækja vefsvæði forritsviðauka"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Hætta við"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Lykilorð"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Veldu setu"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Ekki á listanum?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(t.d., notandi eða %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Notandanafn"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Innskráningargluggi"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Auðkenningarvilla"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(eða strjúka fingri yfir lesara)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(eða setja fingur á lesara)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Slökkva"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "slökkva;hætta;endurræsa;stöðva"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Endurræsa"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "endurræsa;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Læsa skjá"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "læsa skjá"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Skrá út"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "Útskráning;skrá út"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Setja í bið"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "hvíla;svæfa"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Skipta um notanda"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "skipta um notanda"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "læsa stefnu;aflæsa;stefnulás;skjár;snúningur"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Taka skjámynd"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "skjámynd;skjámyndskeið;skjáupptaka;myndataka;upptaka"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Aflæsa stefnu skjás"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Læsa stefnu skjás"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Skipun fannst ekki"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Gat ekki þáttað skipun:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Keyrsla '“%s' mistókst:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Í þessum töluðu orðum"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "fyrir %d mínútu síðan"
+msgstr[1] "fyrir %d mínútu síðan"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "fyrir %d klukkustund síðan"
+msgstr[1] "fyrir %d klukkustundum síðan"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Í gær"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "fyrir %d degi"
+msgstr[1] "fyrir %d dögum síðan"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "fyrir %d viku"
+msgstr[1] "fyrir %d vikum síðan"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "fyrir %d mánuði"
+msgstr[1] "fyrir %d mánuðum"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "fyrir %d ári"
+msgstr[1] "fyrir %d árum"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Í gær klukkan H:∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, klukkan %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d. %B, kl. %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d. %B %Y, kl. %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "í gær, kl. %I:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d. %B, kl. %H∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d. %B %Y, kl. %H∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Innskráning á tengipunkt (hotspot)"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Tenging þín við þennan aðgangsstað er ekki örugg. Aðilar í næsta nágrenni "
+"geta séð lykilorð og aðrar upplýsingar sem þú setur inn á þessari síðu."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Hafna aðgangi"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Veita aðgang"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Nafnlaus mappa"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s var bætt við á stjórnborð."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s var fjarlægt af stjórnborði."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Opna glugga"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nýr gluggi"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Sýna ítarlegri upplýsingar"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Hætta"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Losa"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Festa á stjórnborð"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Ræsa með innbyggðu skjákorti"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Ræsa með stakstæðu skjákorti"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Veldu hljóðtæki"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Hljóðstillingar"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Heyrnartól"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Heyrnartól með hljóðnema"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Hljóðnemi"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Breyta _bakgrunni skjáborðs…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Birtingarstillingar"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Stillingar"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Su"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Má"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Þr"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Mi"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Fi"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Fö"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "La"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Fyrri mánuður"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Næsti mánuður"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Vika %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Engar tilkynningar"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Ekki trufla"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Hreinsa"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” svarar ekki."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Þú getur annað hvort hinkrað augnablik og leyft forritinu að halda áfram eða "
+"þú getur þvingað forritið til að slökkva algjörlega á sér."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Þvinga til að hætta"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Bíða"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Utanáliggjandi drif tengt"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Utanáliggjandi drif aftengt"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Gat ekki aflæst gagnageymslu"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Opna með %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Einnig gætirðu tengst með því að ýta á “WPS”-hnappinn á netbeininum þínum."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Tengjast"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Lykill"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Leyniorð einkalykils"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Auðkenni"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Þjónusta"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Auðkenningar krafist"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Lykilorða eða dulritunarlykla er krafist til að fá aðgang að þráðlausa "
+"netinu “%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Kapaltengd 802.1X auðkenning"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Heiti netkerfis"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL auðkenning"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Krafist er PIN-númers"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+"Þú þarft að slá inn PIN númer til að þess að tengjast við símaþjónustuna"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN-númer"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Það er nauðsynlegt að gefa upp lykilorð til að tengjast við “%s”."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Netstjórnun"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN-lykilorð"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Auðkenningar krafist"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Kerfisstjóri"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Auðkenna"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Æ, þetta virkaði ekki. Endilega reyndu aftur."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s er nú þekkt(ur) sem %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Gluggar"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Sýna forrit"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Stjórnborð"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d. %B, %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e. %B, %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Í dag"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Á morgun"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Allan daginn"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Engir atburðir"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Bæta við heimsklukkum…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Heimsklukkur"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Hleð inn..."
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Fara á netið eftir veðurupplýsingum"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Veðurupplýsingar eru ekki tiltækar í augnablikinu"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Veður"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Veldu staðsetningu fyrir veðurspár…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Skrá út %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Skrá út"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s verður skráður út sjálfkrafa eftir %d sekúndu."
+msgstr[1] "%s verður skráður út sjálfkrafa eftir %d sekúndur."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Þú verður skráður út sjálfkrafa eftir %d sekúndu."
+msgstr[1] "Þú verður skráður út sjálfkrafa eftir %d sekúndur."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Skrá út"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Slökkva"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Setja inn uppfærslur og slökkva"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Kerfið mun slökkva á sér sjálfkrafa eftir %d sekúndu."
+msgstr[1] "Kerfið mun slökkva á sér sjálfkrafa eftir %d sekúndur."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Setja inn tiltækar hugbúnaðaruppfærslur"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Slökkva"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Endurræsa"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Setja inn uppfærslur og endurræsa"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Kerfið mun endurræsast sjálfkrafa eftir %d sekúndu."
+msgstr[1] "Kerfið mun endurræsast sjálfkrafa eftir %d sekúndur."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Endurræsa"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Endurræsa og setja inn uppfærslur"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Kerfið mun endurræsast sjálfkrafa og setja inn uppfærslur eftir %d sekúndu."
+msgstr[1] ""
+"Kerfið mun endurræsast sjálfkrafa og setja inn uppfærslur eftir %d sekúndur."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Endurræsa &amp; setja inn uppfærslur"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Setja inn &amp; slökkva"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Slökkva á vélinni eftir að uppfærslur hafa verið settar inn"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Endurræsa og setja inn uppfærslur"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s verður sett upp eftir endurræsingu. Uppfærsla uppsetningar getur tekið "
+"langan tíma: gakktu úr skugga um að þú hafir tekið öryggisafrit og að tölvan "
+"sé tengd við rafmagn."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Lítil hleðsla rafhlöðu: stingdu í samband við rafmagn áður en uppfærslur eru "
+"settar inn."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Sum forrit eru upptekin eða eru með óvistuð gögn"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Aðrir notendur eru skráðir inn"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Ræsivalkostir"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (fjartengt)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (villuskjár)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Setja inn"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Setja inn forritsviðauka"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Sækja og setja upp '%s' frá extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Uppfærslur forritsviðauka tiltækar"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Uppfærslur fyrir forritsviðauka eru tilbúnar til uppsetningar."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Leyfa hindrun flýtilykla"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Forritið %s vill koma í veg fyrir notkun flýtilykla"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Forrit vill koma í veg fyrir notkun flýtilykla"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Þú getur endurheimt flýtilykla með því að ýta á %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Hafna"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Leyfa"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Kveikt á hægum lyklum"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Slökkt á hægum lyklum"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Þú hefur haldið niðri Shift-lyklinum í 8 sekúndur. Þetta er flýtileiðin "
+"fyrir hæga lykla, sem hefur áhrif á hvernig lyklaborðið þitt virkar."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Kveikt á klístruðum lyklum"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Slökkt á klístruðum lyklum"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Þú hefur ýtt á Shift-lykilinn 5 sinnum í röð. Þetta er flýtileiðin fyrir "
+"klístraða lykla, sem hefur áhrif á hvernig lyklaborðið þitt virkar."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Þú hefur ýtt á tvo lykla í einu eða á Shift-lykilinn 5 sinnum í röð. Þetta "
+"slekkur á klístruðum lyklum, sem hefur áhrif á hvernig lyklaborðið þitt "
+"virkar."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Halda kveiktu"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Kveikja á"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Slökkva"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Halda slökktu"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Lands- & tungumálastillingar"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Engir forritsviðaukar uppsettir"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s gaf ekki frá sér neinar villur."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Fela villur"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Birta villur"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Virkt"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Óvirkt"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Villa"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Útrunnið"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Hala inn"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Skoða frumtexta"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Vefsíða"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Kerfið var sett í óöruggan ham"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Forrit hafa núna óheftan aðgang"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Afturkalla"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Skráð inn sem notandi með auknum heimildum"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Forðast ætti af öryggisástæðum að keyra setu með auknum notandaheimildum. "
+"Skráðu þig inn sem venjulegan notanda, ef mögulegt er."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Skjálæsing er óvirk"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Kerfisupplýsingar"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Óþekktur flytjandi"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Óþekktur titill"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Skrifaðu hér til að leita"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Forrit"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Yfirlit"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nýr flýtilykill..."
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Forrit skilgreint"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Birta upplýsingar á skjá"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Skipta um skjá"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Úthluta lyklaslætti"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Lokið"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Breyta…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Ekkert"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Ýttu á hnapp til að stilla"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Ýttu á ESC til að hætta"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Ýttu á einhvern lykil til að hætta"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Virkni"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Kerfið"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Toppstika"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Keyra skipun"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Ýttu á ESC til að loka"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Endurræsing er ekki tiltæk á Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Endurræsi…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME þarf að læsa skjánum"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Gat ekki læst skjá"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Læsing var hindruð af öðru forriti"
+
+#: js/ui/screenshot.js:1159
+msgid "Selection"
+msgstr "Val"
+
+#: js/ui/screenshot.js:1169
+msgid "Area Selection"
+msgstr "Val svæðis"
+
+#: js/ui/screenshot.js:1174
+msgid "Screen"
+msgstr "Skjár"
+
+#: js/ui/screenshot.js:1184
+msgid "Screen Selection"
+msgstr "Val skjás"
+
+#: js/ui/screenshot.js:1189
+msgid "Window"
+msgstr "Gluggi"
+
+#: js/ui/screenshot.js:1199
+msgid "Window Selection"
+msgstr "Val glugga"
+
+#: js/ui/screenshot.js:1237
+msgid "Screenshot / Screencast"
+msgstr "Skjámynd / Skjámyndskeið"
+
+#: js/ui/screenshot.js:1273
+msgid "Show Pointer"
+msgstr "Sýna bendil"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1864
+msgid "Screencasts"
+msgstr "Skjámyndskeið"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1869
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Skjámyndskeið frá %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1911 js/ui/screenshot.js:2123
+msgid "Screenshot"
+msgstr "Skjámynd"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1917
+msgid "Screencast recorded"
+msgstr "Skjámyndskeið tekið upp"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1919
+msgid "Click here to view the video."
+msgstr "Smelltu hér til að skoða myndskeiðið."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1922 js/ui/screenshot.js:2137
+msgid "Show in Files"
+msgstr "Sýna í skrám"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2083
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Skjámynd frá %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2129
+msgid "Screenshot captured"
+msgstr "Skjámynd tekin"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2131
+msgid "You can paste the image from the clipboard."
+msgstr "Þú getur límt myndina af klippispjaldinu."
+
+#: js/ui/screenshot.js:2184 js/ui/screenshot.js:2349
+msgid "Screenshot taken"
+msgstr "Skjámynd tekin"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Leita..."
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Engar niðurstöður."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d til viðbótar"
+msgstr[1] "%d til viðbótar"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Leita"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Afrita"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Líma"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Sýna texta"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Fela texta"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps Lock er á."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Falinn hljóðstyrkur"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Geymslueining á Windows-stýrikerfi"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Notar lyklaskrár"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Til að aflæsa gagnageymslu sem notar lyklaskrár skaltu nota <i>%s</i> "
+"nytjatólið í staðinn."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-númer"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Muna lykilorð"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Aflæsa"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Opna %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM-númerið verður að vera tala eða tómt."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Ekki tekst að ræsa %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Tókst ekki að finna %s forritið"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Aukið aðgengi"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Mikil birtuskil"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Aðdráttur"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Skjálesari"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Skjályklaborð"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Sjónrænar viðvaranir"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Klístraðir lyklar"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Hægir lyklar"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Lyklaendurvarp"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Músarlyklar"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Stór texti"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr ""
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Birtustig"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Einsmella"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Tvísmella"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Draga"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Aukasmellur"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Tímamörk tvísmellingar"
+
+#: js/ui/status/keyboard.js:833
+msgid "Keyboard"
+msgstr "Lyklaborð"
+
+#: js/ui/status/keyboard.js:850
+msgid "Show Keyboard Layout"
+msgstr "Birta lyklaborðsuppsetningu"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Leyfa aðgang að staðsetningu"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Forritið %s vill fáaðgang að staðsetningunni þinni"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Hægt er að breyta aðgangi að staðsetningu hvenær sem er með því að fara í "
+"stillingaforritið fyrir gagnaleynd."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<óþekkt>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+#| msgid "%s Disconnecting"
+msgid "Disconnect %s"
+msgstr "Aftengja %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+#| msgid "Connect"
+msgid "Connect to %s"
+msgstr "Tengjast við %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+#| msgid "%s Hotspot Active"
+msgid "%s Hotspot"
+msgstr "%s tengipunktur"
+
+#: js/ui/status/nightLight.js:20
+#| msgid "Night Light On"
+msgid "Night Light"
+msgstr "Næturlýsing"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Afköst"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Jafnvægi"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Orkusparnaður"
+
+#: js/ui/status/powerProfiles.js:70
+#| msgctxt "search-result"
+#| msgid "Power off"
+msgid "Power Profiles"
+msgstr "Orkustýringarsnið"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Stöðva skjámyndskeið"
+
+#: js/ui/status/remoteAccess.js:144
+#| msgid "Stop Screencast"
+msgid "Stop Screen Sharing"
+msgstr "Stöðva skjádeilingu"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Flugvélahamur"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Taka skjámynd"
+
+#: js/ui/status/system.js:161
+#| msgctxt "search-result"
+#| msgid "Power Off"
+msgid "Power Off Menu"
+msgstr "Slökkva-valmynd"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Hvíla"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Endurræsa…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Slökkva..."
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Skrá út..."
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Skipta um notanda…"
+
+#: js/ui/status/system.js:235
+#| msgctxt "search-result"
+#| msgid "Lock Screen"
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Læsa skjá"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Óþekkt Thunderbolt-tæki"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Nýtt tæki fannst á meðan þú varst í burtu. Aftengdu og tengdu svo tækið "
+"aftur til að byrja að nota það."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Óauðkennt Thunderbolt-tæki"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Nýtt tæki hefur fundist og þarf það að fá heimild frá kerfisstjóra."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Villa í auðkenningu á Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Gat ekki auðkennt Thunderbolt-tæki: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Hljóðstyrkur breyttist"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Hljóðstyrkur"
+
+#: js/ui/status/volume.js:272
+#| msgid "%u Output"
+#| msgid_plural "%u Outputs"
+msgid "Sound Output"
+msgstr "Hljóðúttak"
+
+#: js/ui/status/volume.js:340
+#| msgid "%u Input"
+#| msgid_plural "%u Inputs"
+msgid "Sound Input"
+msgstr "Hljóðinntak"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Spegla"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Tengja skjái"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Einungis utanáliggjandi"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Einungis innbyggt"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Strjúka upp til að aflæsa"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Smelltu eða ýttu á lykil til að aflæsa"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Aflæsa glugga"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Skrá inn sem annar notandi"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Velkomin í GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nei, takk"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Skoðaðu kynninguna"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s' er tilbúið"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Halda þessum skjástillingum?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Afturkalla stillingar"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Halda breytingum"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Breytingar á stillingum verða afturkallaðar eftir %d sekúndu"
+msgstr[1] "Breytingar á stillingum verða afturkallaðar eftir %d sekúndur"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Fela"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Endurheimta"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Hámarka"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Færa"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Breyta stærð"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Flytja titilslá á skjáinn"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Alltaf efst"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Alltaf á sjáanlegu vinnusvæði"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Flytja á vinnusvæðið til vinstri"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Flytja á vinnusvæðið til hægri"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Færa yfir á vinnusvæði fyrir ofan"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Færa yfir á vinnusvæði fyrir neðan"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Færa á skjá fyrir ofan"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Færa á skjá fyrir neðan"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Færa á skjá til vinstri"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Færa á skjá til hægri"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Loka"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution dagatal"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Prenta útgáfunúmer"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Hamur sem GDM notar fyrir innskráningarglugga"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Nota sérstakan ham, t.d. \"gdm\" fyrir innskráningarskjá"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Sýna alla mögulega hami"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Óþekkt"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Mistókst að ræsa \"“%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Lykilorðin stemma ekki"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Lykilorðið getur ekki verið autt"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Notandi hafnaði auðkenningarglugga"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Forritsviðaukar"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Sýsla með GNOME Shell Extension forritsviðauka"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME verkefnið"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Stilla GNOME Shell Extension forritsviðauka"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Engar samsvaranir"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Fjarlægja „%s“?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Fjarlægja"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Sveinn í Felli, sv1@fellsnet.is"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Þessi forritsviðauki er ósamrýmanlegur við þessa útgáfu af GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Villa fannst í forritsviðaukanum"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Þennan forritsviðauka er hægt að uppfæra"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Vefsvæði"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Fjarlægja…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Hjálp"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Um forritsviðauka"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Uppsett handvirkt"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Til að finna og setja upp forritsviðauka, ættirðu að heimsækja <a href="
+"\"https://extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Innbyggt"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Engir uppsettir forritsviðaukar"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Uppfærslur fyrir forritsviðauka eru tilbúnar"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nafn"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Lýsing"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Sniðmát"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAFN"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "LÝSING"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "SNIÐMÁT"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Búa til nýjan forritviðauka"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Óþekkt viðföng"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, nafn og lýsing eru nauðsynleg"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Ekki tókst að tengjast GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Forritsviðaukinn “%s” er ekki til\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Gera forritsviðauka óvirkan"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Ekkert UUID-auðkenni uppgefið"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Fleiri en eitt UUID-auðkenni uppgefið"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Virkja forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Forritsviðaukinn “%s” er ekki til\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Birta upplýsingar um forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Skrifa yfir fyrirliggjandi forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "FORRITSVIÐAUKAVÖNDULL"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Setja inn forritsviðaukavöndul"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Enginn forritsviðaukavöndull tilgreindur"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Fleiri en einn forritsviðaukavöndull tilgreindur"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Birta forritsviðauka uppsetta af notanda"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Birta forritsviðauka uppsetta af kerfinu"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Birta virka forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Birta óvirka forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Birta forritsviðauka með kjörstillingum"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Birta forritsviðauka með uppfærslum"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Birta nánari upplýsingar um forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Gera lista yfir uppsetta forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "SKRÁ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SKEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "MAPPA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "LÉN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Skrifa yfir fyrirliggjandi pakka"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "UPPRUNAMAPPA"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Búa til forritsviðaukavöndul"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Meira en ein grunnmappa skilgreind"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Forritsviðaukinn “%s” er ekki með kjörstillingar\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Gat ekki opnað kjörstillingar fyrir forritsviðaukann “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Opnar kjörstillingar forritsviðauka"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Frumstilla forritviðauka"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Get ekki fjarlægt forritsviðauka kerfisins\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Mistókst að fjarlægja “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Fjarlægja forritsviðauka"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ekki birta villuskilaboð"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Slóð"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL-slóð"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Upprunalegur höfundur"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Útgáfa"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Staða"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” tekur engin viðföng"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Notkun:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Birta upplýsingar um útgáfu og hætta."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "SKIPUN"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[VIÐFÖNG…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Skipanir:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Birta hjálp"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Virkja forritsviðauka"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Gera forritsviðauka óvirkan"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Frumstilla forritviðauka"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Fjarlægja forritsviðauka"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Gera lista yfir forritsviðauka"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Birta upplýsingar um forritsviðauka"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Opna kjörstillingar forritsviðauka"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Búa til forritviðauka"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Pakka forritsviðauka"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Setja inn forritsviðaukavöndul"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Notaðu “%s” til að fá ítarlega hjálp.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Einfalt"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Tómur forritsviðauki"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Vísir"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Bæta táknmynd við efstu stikuna"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u úttak"
+msgstr[1] "%u úttök"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u inntak"
+msgstr[1] "%u inntök"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Kerfishljóð"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Stillingar Bluetooth"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d tengt"
+#~ msgstr[1] "%d tengt"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth kveikt"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth slökkt"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Staðsetning virk"
+
+#~ msgid "Disable"
+#~ msgstr "Gera óvirkt"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Stillingar á friðhelgi"
+
+#~ msgid "Location In Use"
+#~ msgstr "Staðsetning í notkun"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Staðsetning óvirk"
+
+#~ msgid "Enable"
+#~ msgstr "Virkja"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s slökkt"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s tengt"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s óstýrt"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s tengist"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s krefst auðkenningar"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Grunnhugbúnað (firmware) vantar fyrir %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s er ekki tiltækt"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s tenging mistókst"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Stillingar nets um kapal"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Stillingar ferðabreiðbands"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s vélbúnaður óvirkur"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s óvirkt"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Tengjast internetinu"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Flugvélahamur virkur"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Þráðlaust Wi-Fi net er óvirkt þegar flugvélahamur er virkur."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Slökkva á flugvélaham"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Slökkt á þráðlausu neti"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Kveikja verður á þráðlausu Wi-Fi neti til að tengjast netkerfi."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Kveikja á þráðlausu neti"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Þráðlaus Wi-Fi netkerfi"
+
+#~ msgid "Select a network"
+#~ msgstr "Veldu netkerfi"
+
+#~ msgid "No Networks"
+#~ msgstr "Engin netkerfi"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Nota vélbúnaðarrofa til að slökkva."
+
+#~ msgid "Select Network"
+#~ msgstr "Veldu netkerfi"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Stillingar þráðlauss netkerfis"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s ekki tengdur"
+
+#~ msgid "connecting…"
+#~ msgstr "tengist..."
+
+#~ msgid "authentication required"
+#~ msgstr "auðkenningar krafist"
+
+#~ msgid "connection failed"
+#~ msgstr "tenging mistókst"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN stillingar"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "Slökkt á VPN"
+
+#~ msgid "Network Settings"
+#~ msgstr "Stillingar nets"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s þráðlaus tenging"
+#~ msgstr[1] "%s þráðlausar tengingar"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Wi-Fi tenging"
+#~ msgstr[1] "%s Wi-Fi tengingar"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s mótaldstenging"
+#~ msgstr[1] "%s mótaldstengingar"
+
+#~ msgid "Connection failed"
+#~ msgstr "Tenging mistókst"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Mistókst að virkja nettengingu"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Næturlýsing óvirk"
+
+#~ msgid "Resume"
+#~ msgstr "Halda áfram"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Óvirkt þar til á morgun"
+
+#~ msgid "Power Settings"
+#~ msgstr "Orkustillingar"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Full hleðsla"
+
+#~ msgid "Not Charging"
+#~ msgstr "Ekki í hleðslu"
+
+#~ msgid "Estimating…"
+#~ msgstr "Reikna…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d eftir (%d%%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d þar til fullhlaðið (%d%%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Skjánum er deilt með e-n"
+
+#~ msgid "Turn off"
+#~ msgstr "Slökkva"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Flugvélahamur virkur"
+
+#~ msgid "Lock"
+#~ msgstr "Læsa"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Slökkva / Skrá út"
+
+#~ msgid "Log Out"
+#~ msgstr "Skrá út"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Fjarlægja úr Eftirlæti"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Bæta í Eftirlæti"
+
+#~ msgid "Author"
+#~ msgstr "Höfundur"
+
+#~ msgid "Warning"
+#~ msgstr "Aðvörun"
+
+#, fuzzy
+#~| msgid "Unable to connect to %s"
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Ekki tókst að tengjast %s"
+
+#~ msgid "App Picker View"
+#~ msgstr "Forritavalssýn"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Hvaða lyklaborð á að nota"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Tegund lyklaborðs sem á að nota."
+
+#~| msgid "Show Errors"
+#~ msgid "Copy Error"
+#~ msgstr "Afrita villu"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Skoða í hugbúnaði"
+
+#~ msgid "Next"
+#~ msgstr "Næsta"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Skrá inn"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Oftast notuð forrit munu birtast hér"
+
+#~ msgid "Frequent"
+#~ msgstr "Algengt"
+
+#~ msgid "All"
+#~ msgstr "Allt"
+
+#~| msgctxt "calendar heading"
+#~| msgid "%A, %B %d, %Y"
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %d. %B, %Y"
+
+#~ msgid "Password:"
+#~ msgstr "Lykilorð:"
+
+#~ msgid "Type again:"
+#~ msgstr "Skrifaðu aftur:"
+
+#~ msgid "Password: "
+#~ msgstr "Lykilorð:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Auðkenningar er krafist af þráðlausu neti"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Lykilorð fyrir þráðlaust breiðbandsnet"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d. %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nýtt skilaboð"
+#~ msgstr[1] "%d ný skilaboð"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d ný skilaboð"
+#~ msgstr[1] "%d ný skilaboð"
+
+#~ msgid "Off"
+#~ msgstr "Slökkt"
+
+#~ msgid "On"
+#~ msgstr "Á"
+
+#~ msgid "Account Settings"
+#~ msgstr "Stillingar aðgangs"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Stefnulás"
+
+#~ msgid "Minimize"
+#~ msgstr "Lágmarka"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Minnka glugga"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Villa kom upp við ræsingu kjörstillingaglugga fyrir %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s allan daginn."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, síðan %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, síðan %s, en snýst svo upp í %s síðar."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Með vindkælingu %s."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Skrá út"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Skipta um notanda"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Endurbeining á vefauðkenningu"
+
+#~ msgid "Events"
+#~ msgstr "Atburðir"
+
+#~ msgid "Notifications"
+#~ msgstr "Tilkynningar"
+
+#~ msgid "Hide tray"
+#~ msgstr "Fela kerfisbakka"
+
+#~ msgid "Status Icons"
+#~ msgstr "Táknmyndir fyrir stöðu"
+
+#~ msgid "Media"
+#~ msgstr "Gagnamiðill"
+
+#~ msgid "Not In Use"
+#~ msgstr "Ekki í notkun"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Sýna vikudag í dagatali"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr ""
+#~ "Ef merkt er við þetta sem satt þá mun ISO-vikunúmer vera sýnt í "
+#~ "dagatalinu."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Nota sem internettengingu"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME skel (wayland skjásamsetning)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d tengt tæki"
+#~ msgstr[1] "%d tengd tæki"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Rafhlaða"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Birta skilaboðabakka"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Su"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Má"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Þr"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Mi"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Fi"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Fö"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "La"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Ekkert á dagskrá"
+
+#~ msgid "This week"
+#~ msgstr "Í þessari viku"
+
+#~ msgid "Next week"
+#~ msgstr "Í næstu viku"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Útskiptanleg tæki"
+
+#~ msgid "Eject"
+#~ msgstr "Spýta út"
+
+#~ msgid "Invitation"
+#~ msgstr "Boð"
+
+#~ msgid "Call"
+#~ msgstr "Símtal"
+
+#~ msgid "File Transfer"
+#~ msgstr "Skráaflutningur"
+
+#~ msgid "Chat"
+#~ msgstr "Spjall"
+
+#~ msgid "Unmute"
+#~ msgstr "Kveikja á hljóði"
+
+#~ msgid "Mute"
+#~ msgstr "Þagga niður"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Boð til %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s er að bjóða þér að taka þátt í %s"
+
+#~ msgid "Decline"
+#~ msgstr "Hafna"
+
+#~ msgid "Accept"
+#~ msgstr "Samþykkja"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Myndsímtal frá %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Símtal frá %s"
+
+#~ msgid "Answer"
+#~ msgstr "Svara"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s er að senda þér %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s vill fá heimild til að sjá hvenær þú ert tengd/ur"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Auðkenning mistókst"
+
+#~ msgid "Encryption error"
+#~ msgstr "Dulritunarvilla"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Skilríki fylgir ekki"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Skilríki ekki treyst"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Skilríki er útrunnið"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Skilríki ekki virkjað"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Vélarheiti skilríkis samsvara ekki"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Fingraför skilríkis samsvara ekki"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Skilríki aðeins undirritað af handhafa"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Staða er sett á ótengda"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Skilríkið er ekki gilt"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Tengingu hefur verið hafnað"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Ekki er hægt að koma á tengingu"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Tenging hefur slitnað"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Þessi aðgangur er þegar tengdur miðlaranum"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Aðgangurinn er þegar til á miðlaranum"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr ""
+#~ "Miðlarinn er of upptekinn í augnablikinu til að meðhöndla tenginguna"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Skilríkið hefur veriið afturkallað"
+
+#~ msgid "Internal error"
+#~ msgstr "Innvær villa"
+
+#~ msgid "View account"
+#~ msgstr "Skoða aðgang"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Opna dagatal"
+
+#~ msgid "Open"
+#~ msgstr "Opna"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Hreinsa skilaboð"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Stillingar á tilkynningum"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Valmynd í kerfisbakka"
+
+#~ msgid "Message Tray"
+#~ msgstr "Skilaboðabakki"
diff --git a/po/it.po b/po/it.po
new file mode 100644
index 0000000..5a95368
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,3215 @@
+# Italian translations for gnome-shell package.
+# Copyright (C) 2009, 2010, the gnome-shell copyright holder
+# Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 the Free Software Foundation
+# Copyright (C) 2019, 2020, 2021, 2022 the Free Software Foundation
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Milo Casagrande <milo@milo.name>, 2009, 2010, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022.
+# Luca Ferretti <elle.uca@infinito.it>, 2010, 2011, 2012, 2013, 2014.
+# Gianvito Cavasoli <gianvito@gmx.it>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-05-28 12:08+0000\n"
+"PO-Revision-Date: 2022-06-14 17:30+0200\n"
+"Last-Translator: Milo Casagrande <milo@milo.name>\n"
+"Language-Team: Italiano <gnome-it-list@gnome.org>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.0.1\n"
+"X-DamnedLies-Scope: partial\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Lanciatori"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Attiva l'applicazione preferita 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Attiva l'applicazione preferita 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Attiva l'applicazione preferita 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Attiva l'applicazione preferita 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Attiva l'applicazione preferita 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Attiva l'applicazione preferita 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Attiva l'applicazione preferita 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Attiva l'applicazione preferita 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Attiva l'applicazione preferita 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2069
+msgid "Screenshots"
+msgstr "Schermate"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "Take a screenshot interactively"
+msgstr "Cattura una schermata in modo interattivo"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:247
+msgid "Take a screenshot"
+msgstr "Cattura una schermata"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:243
+msgid "Take a screenshot of a window"
+msgstr "Cattura una schermata di una finestra"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:239
+msgid "Record a screencast interactively"
+msgstr "Registra un breve video dello schermo"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostra l'elenco delle notifiche"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Dà il focus alla notifica attiva"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostra la panoramica"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostra tutte le applicazioni"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Apre il menù applicazioni"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gestione finestre e avvio applicazioni"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Abilita gli strumenti interni utili a sviluppatori e collaudatori attraverso "
+"Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Consente l'accesso agli strumenti interni di debug e monitoraggio "
+"utilizzando la finestra di dialogo Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID delle estensioni da abilitare"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Le estensioni per la GNOME Shell dispongono di una proprietà UUID: questa "
+"chiave elenca le estensioni da caricare. È necessario inserire in questo "
+"elenco ogni estensione che si vuole caricare. È anche possibile modificare "
+"questo elenco con i metodi DBus EnableExtension e DisableExtension su org."
+"gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID delle estensioni da disabilitare in maniera fozata"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Le estensioni per la GNOME Shell dispongono di una proprietà UUID: questa "
+"chiave elenca le estensioni da disabilitare, anche se caricate dalla "
+"modalità attuale. È anche possibile modificare questo elenco con i metodi "
+"DBus EnableExtension e DisableExtension su org.gnome.Shell. Questa chiave ha "
+"precedenza rispetto all'impostazione \"enabled-extensions\"."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Disabilita le estensioni utente"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Disabilita tutte le estensioni abilitate dall'utente senza influire "
+"sull'impostazione \"enabled-extension\"."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+"Disabilita la convalida della compatibilità con le versioni delle estensioni"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell carica solo le estensioni che dichiarano di supportare la "
+"versione di GNOME Shell attualmente in esecuzione. Abilitando questa opzione "
+"verrà disabilitato tale controllo e verrà tentato il caricamento di tutte le "
+"estensioni, senza distinzione sulla versione supportata dichiarata."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Elenco di ID di file desktop per le applicazioni preferite"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Le applicazioni che corrispondono a questi identificatori vengono "
+"visualizzate nell'area dei preferiti."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Cronologia per la finestra di dialogo dei comandi (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Cronologia per la finestra di dialogo \"looking glass\""
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Mostra sempre la voce \"Termina sessione\" nel menù utente."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Questa chiave sovrascrive la scomparsa automatica della voce di menù "
+"\"Termina sessione\" nelle modalità singolo utente e sessione singola."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Indica se ricordare la password per montare file system remoti o cifrati"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"La shell richiederà una password quando un dispositivo cifrato o un file "
+"system remoto viene montato. Se la password può essere salvata, viene "
+"aggiunta la corrispettiva casella di selezione. Questa chiave imposta lo "
+"stato predefinito di tale casella."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Indica se l'adattatore Bluetooth predefinito abbia avuto del dispositivi "
+"associati"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Viene visualizzato un elemento nel menù Bluetooth solamente se un adattatore "
+"Bluetooth è alimentato o se altri dispositivi sono stati associati con "
+"l'adattatore predefinito. Ciò viene reimpostato se l'adattatore predefinito "
+"non risulterà avere dispositivi associati."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"L'ultima versione per la quale la finestra di dialogo \"Benvenuti in GNOME\" "
+"è stata visualizzata"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Questa chiave determina per quale versione è stata visualizzata per l'ultima "
+"volta la finestra di dialogo \"Benvenuti in GNOME\". Una stringa vuota "
+"rappresenta la versione più vecchia possibile e un numero enorme rappresenta "
+"versioni che non esistono ancora. Questo numero enorme può essere utilizzato "
+"per disabilitare la finestra di dialogo."
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid "Layout of the app picker"
+msgstr "Disposizione del selettore app"
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Disposizione del selettore app. Le voci nell'array sono delle pagine, ogni "
+"pagina è archiviata nell'ordine con cui appare nella GNOME Shell. Ogni "
+"pagina contiene coppie \"application id\" → \"data\". Attualmente, i "
+"seguenti valori sono archiviati come \"data\": • \"position\": la posizione "
+"dell'icona applicazione nella pagina"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu"
+msgstr "Associazione di tasti per aprire il menù delle applicazioni"
+
+#: data/org.gnome.shell.gschema.xml.in:150
+msgid "Keybinding to open the application menu."
+msgstr "Associazione di tasti per aprire il menù delle applicazioni."
+
+#: data/org.gnome.shell.gschema.xml.in:156
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between overview states"
+msgstr "Associazione di tasti per spostarsi tra gli stati della panoramica"
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Associazione di tasti per spostarsi tra sessione, selezione finestre e "
+"griglia app"
+
+#: data/org.gnome.shell.gschema.xml.in:164
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Associazione di tasti per spostarsi tra griglia app, selezione finestre e "
+"sessione"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Associazione di tasti per aprire la vista \"Mostra applicazioni\""
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Associazione di tasti per aprire la vista \"Mostra applicazioni\" della "
+"panoramica Attività."
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the overview"
+msgstr "Associazione di tasti per aprire la panoramica"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the Activities Overview."
+msgstr "Associazione di tasti per aprire la panoramica delle attività."
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Associazione di tasti per commutare la visibilità dell'elenco delle notifiche"
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Associazione di tasti per commutare la visibilità dell'elenco delle "
+"notifiche."
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification"
+msgstr "Associazione di tasti per dare il focus alla notifica attiva"
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to focus the active notification."
+msgstr "Associazione di tasti per dare il focus alla notifica attiva."
+
+#: data/org.gnome.shell.gschema.xml.in:199
+msgid "Switch to application 1"
+msgstr "Passa all'applicazione 1"
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid "Switch to application 2"
+msgstr "Passa all'applicazione 2"
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 3"
+msgstr "Passa all'applicazione 3"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 4"
+msgstr "Passa all'applicazione 4"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 5"
+msgstr "Passa all'applicazione 5"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 6"
+msgstr "Passa all'applicazione 6"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 7"
+msgstr "Passa all'applicazione 7"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 8"
+msgstr "Passa all'applicazione 8"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 9"
+msgstr "Passa all'applicazione 9"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Limit switcher to current workspace."
+msgstr "Limita il selettore allo spazio di lavoro in uso."
+
+#: data/org.gnome.shell.gschema.xml.in:257
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Se impostata a VERO, nel selettore sono mostrate solo le applicazioni che "
+"hanno finestre nello spazio di lavoro attualmente in uso. In caso contrario "
+"sono incluse tutte le applicazioni."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid "The application icon mode."
+msgstr "La modalità dell'icona dell'applicazione."
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configura come le finestre vengono mostrate nel selettore. Valori consentiti "
+"sono: \"thumbnail-only\" (mostra una miniatura della finestra), \"app-icon-"
+"only\" (mostra solo l'icona dell'applicazione) oppure \"both\"."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Se impostata a VERO, nel selettore sono mostrate solo le finestre dello "
+"spazio di lavoro attualmente in uso. In caso contrario sono incluse tutte le "
+"finestre."
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "Locations"
+msgstr "Posizioni"
+
+#: data/org.gnome.shell.gschema.xml.in:295
+msgid "The locations to show in world clocks"
+msgstr "Le posizione da mostrare negli orologi mondiali"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Automatic location"
+msgstr "Posizione automatica"
+
+#: data/org.gnome.shell.gschema.xml.in:306
+msgid "Whether to fetch the current location or not"
+msgstr "Indica se trovare la posizione attuale o meno"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "Location"
+msgstr "Posizione"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "The location for which to show a forecast"
+msgstr "La posizione per cui mostrare le previsioni meteorologiche"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+msgid "Attach modal dialog to the parent window"
+msgstr "Collega la finestra modale a quella genitore"
+
+#: data/org.gnome.shell.gschema.xml.in:327
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+#: data/org.gnome.shell.gschema.xml.in:352
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Questa chiave sovrascrive la chiave in org.gnome.mutter quando si esegue "
+"GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Abilita il tiling di bordo quando si trascinano le finestre sui bordi dello "
+"schermo"
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Workspaces are managed dynamically"
+msgstr "Gli spazi di lavoro sono gestiti dinamicamente"
+
+#: data/org.gnome.shell.gschema.xml.in:351
+msgid "Workspaces only on primary monitor"
+msgstr "Gli spazi di lavoro sono solo sul monitor primario"
+
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Ritarda il cambio del focus nella modalità mouse finché il puntatore non si "
+"ferma"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Accesso di rete"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Qualcosa è andato storto"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Si è verificato un problema: non è possibile visualizzare le impostazioni di "
+"questa estensione. È consigliato segnalare il problema agli autori "
+"dell'estensione."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Dettagli tecnici"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Sito web"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Sito web dell'estensione"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:437 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: js/ui/status/network.js:956 subprojects/extensions-app/js/main.js:175
+msgid "Cancel"
+msgstr "Annulla"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Password"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Scegli sessione"
+
+#: js/gdm/loginDialog.js:463
+msgid "Not listed?"
+msgstr "Non elencato?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:931
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(per es. utente o %s)"
+
+#: js/gdm/loginDialog.js:936 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nome utente"
+
+#: js/gdm/loginDialog.js:1257
+msgid "Login Window"
+msgstr "Finestra di accesso"
+
+#: js/gdm/util.js:434
+msgid "Authentication error"
+msgstr "Errore di autenticazione"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:606
+msgid "(or swipe finger across reader)"
+msgstr "(o scorrere il dito sul lettore)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:611
+msgid "(or place finger on reader)"
+msgstr "(o posizionare il dito sul lettore)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Spegni"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "spegni;arresta;ferma;stop;riavvia;"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Riavvia"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "riavvia;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Blocca schermo"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "blocca schermo"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Termina sessione"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "esci;logout;termina sessione"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Sospendi"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "sospendi;riposo"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Cambia utente"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "cambia utente"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "blocca orientazione;sblocca orientazione;schermo;rotazione"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Cattura una schermata"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "schermata;screencast;video;snip;clip;cattura;registra;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Sblocca rotazione schermo"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Blocca rotazione schermo"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Comando non trovato"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Impossibile riconoscere il comando:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Esecuzione di «%s» non riuscita:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Adesso"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minuto fa"
+msgstr[1] "%d minuti fa"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d ora fa"
+msgstr[1] "%d ore fa"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Ieri"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d giorno fa"
+msgstr[1] "%d giorni fa"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d settimana fa"
+msgstr[1] "%d settimane fa"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d mese fa"
+msgstr[1] "%d mesi fa"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d anno fa"
+msgstr[1] "%d anni fa"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ieri, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%e %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%e %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%I∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ieri, %I∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %I∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%e %B, %I∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%e %B %Y, %I∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Accesso hotspot"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"La connessione per l'accesso a questo hotspot non è sicura. Le password e le "
+"altre informazioni inviate possono essere lette da altri."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:406
+msgid "Deny Access"
+msgstr "Nega accesso"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:411
+msgid "Grant Access"
+msgstr "Consenti accesso"
+
+# (ndt) e usare:
+# L'elemento %s è stato rimosso... ?
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s è stato bloccato sulla dash."
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s è stato sbloccato dalla dash."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Finestre aperte"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nuova finestra"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Mostra dettagli"
+
+#: js/ui/appMenu.js:96
+msgid "Quit"
+msgstr "Esci"
+
+#: js/ui/appMenu.js:156 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Sblocca"
+
+#: js/ui/appMenu.js:157
+msgid "Pin to Dash"
+msgstr "Blocca sulla dash"
+
+#: js/ui/appMenu.js:174
+msgid "Launch using Integrated Graphics Card"
+msgstr "Lancia utilizzando scheda grafica integrata"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Discrete Graphics Card"
+msgstr "Lancia utilizzando scheda grafica discreta"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Seleziona dispositivo audio"
+
+#: js/ui/audioDeviceSelection.js:56
+msgid "Sound Settings"
+msgstr "Impostazioni audio"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Cuffie"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Cuffie con microfono"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:271
+msgid "Microphone"
+msgstr "Microfono"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Cambia sfondo…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Impostazioni monitor"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Impostazioni"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "G"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:412
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:472
+msgid "Previous month"
+msgstr "Mese precedente"
+
+#: js/ui/calendar.js:490
+msgid "Next month"
+msgstr "Mese successivo"
+
+#: js/ui/calendar.js:642
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:701
+msgid "Week %V"
+msgstr "Settimana %V"
+
+#: js/ui/calendar.js:880
+msgid "No Notifications"
+msgstr "Nessuna notifica"
+
+#: js/ui/calendar.js:937
+msgid "Do Not Disturb"
+msgstr "Non disturbare"
+
+#: js/ui/calendar.js:958
+msgid "Clear"
+msgstr "Pulisci"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» non risponde."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"È possibile scegliere se attendere, lasciando che l'applicazione continui, "
+"oppure forzare la sua chiusura."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Uscita forzata"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Attendi"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Unità esterna collegata"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Unità esterna scollegata"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Impossibile sbloccare il volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "La versione installata di udisks non supporta l'impostazione PIM"
+
+#: js/ui/components/autorunManager.js:334
+#, javascript-format
+msgid "Open with %s"
+msgstr "Apri con %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"In alternativa è possibile collegarsi premendo il pulsante «WPS» del router."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:244
+#: js/ui/status/network.js:335 js/ui/status/network.js:961
+msgid "Connect"
+msgstr "Connetti"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Chiave"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Password chiave privata"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identità"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Servizio"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:746 js/ui/components/networkAgent.js:767
+msgid "Authentication required"
+msgstr "Richiesta autenticazione"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:747
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"È richiesta una password o una chiave di cifratura per accedere alla rete "
+"wireless «%s»."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:751
+msgid "Wired 802.1X authentication"
+msgstr "Autenticazione via cavo 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nome rete"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:755
+msgid "DSL authentication"
+msgstr "Autenticazione DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:760
+msgid "PIN code required"
+msgstr "Richiesto codice PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:761
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "È necessario il codice PIN per il dispositivo a banda larga mobile"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:752
+#: js/ui/components/networkAgent.js:756 js/ui/components/networkAgent.js:768
+#: js/ui/components/networkAgent.js:772
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "È richiesta una password per connettersi a «%s»."
+
+#: js/ui/components/networkAgent.js:735 js/ui/status/network.js:1751
+msgid "Network Manager"
+msgstr "Gestore reti"
+
+#: js/ui/components/networkAgent.js:771
+msgid "VPN password"
+msgstr "Password VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Richiesta autenticazione"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Amministratore"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autentica"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Errore nell'autenticazione. Provare di nuovo."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s ha cambiato nome in %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:405
+msgid "Windows"
+msgstr "Finestre"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Mostra applicazioni"
+
+# cruscotto?!?!?!?!?!?!?
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-e %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-e %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-e %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Oggi"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Domani"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Giornata"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nessun evento"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Aggiungi orologi mondiali…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Orologi mondiali"
+
+#: js/ui/dateMenu.js:699
+msgid "Loading…"
+msgstr "Caricamento…"
+
+#: js/ui/dateMenu.js:709
+msgid "Go online for weather information"
+msgstr "Collegarsi a Internet per le informazioni meteo"
+
+#: js/ui/dateMenu.js:711
+msgid "Weather information is currently unavailable"
+msgstr "Le informazioni meteo non sono disponibili"
+
+#: js/ui/dateMenu.js:721
+msgid "Weather"
+msgstr "Meteo"
+
+#: js/ui/dateMenu.js:723
+msgid "Select weather location…"
+msgstr "Seleziona una posizione…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Termina sessione di %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Termina sessione"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "La sessione di %s verrà terminata automaticamente tra %d secondo."
+msgstr[1] "La sessione di %s verrà terminata automaticamente tra %d secondi."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "La sessione verrà terminata automaticamente tra %d secondo."
+msgstr[1] "La sessione verrà terminata automaticamente tra %d secondi."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Termina sessione"
+
+#: js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Power Off"
+msgstr "Spegni"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installa aggiornamenti e spegni"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Il sistema verrà spento automaticamente tra %d secondo."
+msgstr[1] "Il sistema verrà spento automaticamente tra %d secondi."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installare gli aggiornamenti software in sospeso"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Spegni"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Riavvia"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Installa aggiornamenti e riavvia"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Il sistema verrà riavviato automaticamente tra %d secondo."
+msgstr[1] "Il sistema verrà riavviato automaticamente tra %d secondi."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Riavvia"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Riavvia e installa aggiornamenti"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Il sistema verrà riavviato automaticamente per installare gli aggiornamenti "
+"tra %d secondo."
+msgstr[1] ""
+"Il sistema verrà riavviato automaticamente per installare gli aggiornamenti "
+"tra %d secondi."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Riavvia e installa"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installa e spegni"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Spegnere dopo aver installato gli aggiornamenti"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Riavvia e installa aggiornamenti"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s verrà installato dopo il riavvio. L'installazione potrebbe richiedere "
+"tempo: assicurarsi di aver eseguito una copia di sicurezza dei dati e che il "
+"computer sia collegato all'alimentazione elettrica."
+
+#: js/ui/endSessionDialog.js:287
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Batteria quasi scarica: collegare alla rete elettrica prima di installare "
+"gli aggiornamenti."
+
+#: js/ui/endSessionDialog.js:296
+msgid "Some applications are busy or have unsaved work"
+msgstr "Alcune applicazioni risultano in esecuzione o con lavoro non salvato"
+
+#: js/ui/endSessionDialog.js:301
+msgid "Other users are logged in"
+msgstr "Altri utenti sono collegati"
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opzioni di avvio"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:688
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remoto)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:691
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (console)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installa"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Installa estensione"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Scaricare e installare «%s» da extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:267
+msgid "Extension Updates Available"
+msgstr "Disponibili aggiornamenti estensioni"
+
+#: js/ui/extensionSystem.js:268
+msgid "Extension updates are ready to be installed."
+msgstr "È possibile installare gli aggiornamenti delle estensioni."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Consenti blocco delle scorciatoie"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "L'applicazione %s vuole bloccare le scorciatoie"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Un'applicazione vuole bloccare le scorciatoie"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Per riabilitare le scorciatoie, premere %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Nega"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Consenti"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tasti lenti attivati"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tasti lenti disattivati"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"È stato premuto il tasto «Maiusc» per otto secondi. Questa è la scorciatoia "
+"per la funzione «Tasti lenti», che determina il modo in cui opera la "
+"tastiera."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Tasti singoli attivati"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Tasti singoli disattivati"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"È stato premuto per cinque volte consecutive il tasto «Maiusc». Questa è la "
+"scorciatoia per la funzione «Tasti singoli», che determina il modo in cui "
+"opera la tastiera."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Sono stati premuti due tasti contemporaneamente o cinque volte consecutive "
+"il tasto «Maiusc». Questo disabilita la funzione «Tasti singoli», che "
+"determina il modo in cui opera la tastiera."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Lascia attivata"
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:1341
+msgid "Turn On"
+msgstr "Accendi"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:160 js/ui/status/network.js:336
+#: js/ui/status/network.js:1341 js/ui/status/network.js:1456
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Spegni"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Lascia disattivata"
+
+#: js/ui/keyboard.js:250
+msgid "Region & Language Settings"
+msgstr "Impostazioni regione e lingua"
+
+#: js/ui/lookingGlass.js:710
+msgid "No extensions installed"
+msgstr "Nessuna estensione installata"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:771
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s non ha emesso alcun errore."
+
+#: js/ui/lookingGlass.js:777
+msgid "Hide Errors"
+msgstr "Nascondi errori"
+
+#: js/ui/lookingGlass.js:781 js/ui/lookingGlass.js:854
+msgid "Show Errors"
+msgstr "Mostra errori"
+
+# (ndt) o abilitata?
+#: js/ui/lookingGlass.js:790
+msgid "Enabled"
+msgstr "Abilitato"
+
+# (ndt) o disabilitata?
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:793 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Disabilitato"
+
+#: js/ui/lookingGlass.js:795
+msgid "Error"
+msgstr "Errore"
+
+#: js/ui/lookingGlass.js:797
+msgid "Out of date"
+msgstr "Non aggiornato"
+
+#: js/ui/lookingGlass.js:799
+msgid "Downloading"
+msgstr "Scaricamento"
+
+#: js/ui/lookingGlass.js:832
+msgid "View Source"
+msgstr "Visualizza sorgente"
+
+#: js/ui/lookingGlass.js:843
+msgid "Web Page"
+msgstr "Pagina web"
+
+#: js/ui/main.js:266
+msgid "System was put in unsafe mode"
+msgstr "Il sistema è stato messo in modalità non sicura"
+
+#: js/ui/main.js:267
+msgid "Applications now have unrestricted access"
+msgstr "Le applicazioni ora hanno accesso illimitato"
+
+#: js/ui/main.js:268 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Annulla"
+
+#: js/ui/main.js:314
+msgid "Logged in as a privileged user"
+msgstr "Accesso eseguito come utente con privilegi"
+
+#: js/ui/main.js:315
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Per motivi di sicurezza, non è consigliato avviare una sessione con un "
+"utente con privilegi. Se possibile, accedere come utente normale."
+
+#: js/ui/main.js:364
+msgid "Screen Lock disabled"
+msgstr "Blocca schermo disabilitato"
+
+#: js/ui/main.js:365
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Il blocco dello schermo richiede il gestore schermi di GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informazione di sistema"
+
+#: js/ui/mpris.js:200
+msgid "Unknown artist"
+msgstr "Artista sconosciuto"
+
+#: js/ui/mpris.js:210
+msgid "Unknown title"
+msgstr "Titolo sconosciuto"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:315
+msgid "Type to search"
+msgstr "Digita per cercare"
+
+#: js/ui/overviewControls.js:393
+msgid "Applications"
+msgstr "Applicazioni"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Panoramica"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nuova scorciatoia…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Definita dall'applicazione"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Mostra aiuto sullo schermo"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Cambia monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Assegna tasti"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Fatto"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Modifica…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nessuno"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Premere un pulsante per configurare"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Premere Esc per uscire"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Premere un tasto per uscire"
+
+#: js/ui/panel.js:241
+msgid "Activities"
+msgstr "Attività"
+
+#: js/ui/panel.js:364
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:480
+msgid "Top Bar"
+msgstr "Barra superiore"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Eseguire un comando"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Premere Esc per chiudere"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Il riavvio non è disponibile su Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Riavvio…"
+
+#: js/ui/screenShield.js:229
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME deve bloccare lo schermo"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:269 js/ui/screenShield.js:644
+msgid "Unable to lock"
+msgstr "Impossibile bloccare"
+
+#: js/ui/screenShield.js:270 js/ui/screenShield.js:645
+msgid "Lock was blocked by an application"
+msgstr "Il blocco è stato impedito da un'applicazione"
+
+#: js/ui/screenshot.js:1149
+msgid "Selection"
+msgstr "Selezione"
+
+#: js/ui/screenshot.js:1159
+msgid "Area Selection"
+msgstr "Selezione area"
+
+#: js/ui/screenshot.js:1164
+msgid "Screen"
+msgstr "Schermo"
+
+#: js/ui/screenshot.js:1174
+msgid "Screen Selection"
+msgstr "Selezione schermo"
+
+#: js/ui/screenshot.js:1179
+msgid "Window"
+msgstr "Finestra"
+
+#: js/ui/screenshot.js:1189
+msgid "Window Selection"
+msgstr "Selezione finestra"
+
+#: js/ui/screenshot.js:1227
+msgid "Screenshot / Screencast"
+msgstr "Schermata / Video"
+
+#: js/ui/screenshot.js:1263
+msgid "Show Pointer"
+msgstr "Mostra puntatore"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1835
+msgid "Screencasts"
+msgstr "Video"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1840
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Video del %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1909 js/ui/screenshot.js:2122
+msgid "Screenshot"
+msgstr "Schermata"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1915
+msgid "Screencast recorded"
+msgstr "Video registrato"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1917
+msgid "Click here to view the video."
+msgstr "Fare clic per visualizzare il video."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2136
+msgid "Show in Files"
+msgstr "Mostra in File"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2082
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Schermata del %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2128
+msgid "Screenshot captured"
+msgstr "Schermata catturata"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2130
+msgid "You can paste the image from the clipboard."
+msgstr "È possibile incollare l'immagine dagli appunti."
+
+#: js/ui/screenshot.js:2183 js/ui/screenshot.js:2348
+msgid "Screenshot taken"
+msgstr "Schermata catturata"
+
+#: js/ui/search.js:815
+msgid "Searching…"
+msgstr "Ricerca…"
+
+#: js/ui/search.js:817
+msgid "No results."
+msgstr "Nessun risultato."
+
+#: js/ui/search.js:948
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d altro"
+msgstr[1] "%d altri"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Cerca"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copia"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Incolla"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Mostra testo"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Nascondi testo"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Blocco maiuscolo attivo"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volume nascosto"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volume di sistema Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Utilizza file di chiave"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Per sbloccare un volume che utilizza file di chiave, usare lo strumento <i>"
+"%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Numero PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Ricorda password"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Sblocca"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Apri %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM deve essere un numero o vuoto."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Impossibile avviare %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Impossibile trovare l'applicazione %s"
+
+# su Android è Rilevabile :P
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accessibilità"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Contrasto elevato"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Ingrandimento"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Lettore schermo"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Tastiera a schermo"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Avvisi visibili"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Tasti singoli"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tasti lenti"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Tasti rimbalzati"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Mouse da tastiera"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Caratteri grandi"
+
+#: js/ui/status/bluetooth.js:54
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:67 js/ui/status/network.js:626
+msgid "Bluetooth Settings"
+msgstr "Impostazioni Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:166
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d collegato"
+msgstr[1] "%d collegati"
+
+#: js/ui/status/bluetooth.js:170
+msgid "Bluetooth On"
+msgstr "Bluetooth acceso"
+
+#: js/ui/status/bluetooth.js:172
+msgid "Bluetooth Off"
+msgstr "Bluetooth spento"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Luminosità"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clic singolo"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Doppio-clic"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Trascina"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clic secondario"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clic automatico"
+
+#: js/ui/status/keyboard.js:827
+msgid "Keyboard"
+msgstr "Tastiera"
+
+#: js/ui/status/keyboard.js:844
+msgid "Show Keyboard Layout"
+msgstr "Mostra disposizione tastiera"
+
+#: js/ui/status/location.js:231 js/ui/status/location.js:255
+msgid "Location Enabled"
+msgstr "Posizione abilitata"
+
+# (ndt) o disabilitata?
+#: js/ui/status/location.js:232 js/ui/status/location.js:256
+msgid "Disable"
+msgstr "Disabilitato"
+
+#: js/ui/status/location.js:234
+msgid "Privacy Settings"
+msgstr "Impostazioni privacy"
+
+#: js/ui/status/location.js:254
+msgid "Location In Use"
+msgstr "Posizione in uso"
+
+#: js/ui/status/location.js:258
+msgid "Location Disabled"
+msgstr "Posizione disabilitata"
+
+#: js/ui/status/location.js:259
+msgid "Enable"
+msgstr "Abilita"
+
+#: js/ui/status/location.js:386
+msgid "Allow location access"
+msgstr "Consenti accesso alla posizione"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:388
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "L'applicazione %s vuole accedere alla propria posizione geografica"
+
+#: js/ui/status/location.js:398
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"L'accesso alla posizione geografica può essere modificato in qualsiasi "
+"momento attraverso le impostazioni della privacy."
+
+#: js/ui/status/network.js:71
+msgid "<unknown>"
+msgstr "<sconosciuto>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:436 js/ui/status/network.js:1370
+#, javascript-format
+msgid "%s Off"
+msgstr "%s spento"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:439
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s collegato"
+
+# FIXME o femminile? boh
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:444
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s non gestito"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:447
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s in disconnessione"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454 js/ui/status/network.js:1362
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s in connessione"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:457
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s richiede autenticazione"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:465
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Firmware mancante per %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:469
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s non disponibile"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:472
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "Connessione non riuscita su %s"
+
+#: js/ui/status/network.js:484
+msgid "Wired Settings"
+msgstr "Impostazioni rete via cavo"
+
+#: js/ui/status/network.js:531
+msgid "Mobile Broadband Settings"
+msgstr "Impostazioni banda larga mobile"
+
+# (ndt) o disabilitata?
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:587 js/ui/status/network.js:1367
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s disabilitato via hardware"
+
+# (ndt) o disabilitata?
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:591
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s disabilitato"
+
+#: js/ui/status/network.js:638
+msgid "Connect to Internet"
+msgstr "Connetti a Internet"
+
+#: js/ui/status/network.js:836
+msgid "Airplane Mode is On"
+msgstr "La modalità aereo è attiva"
+
+#: js/ui/status/network.js:837
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Il Wi-Fi è disabilitato quando la modalità aereo è attiva."
+
+#: js/ui/status/network.js:838
+msgid "Turn Off Airplane Mode"
+msgstr "Disattiva modalità aereo"
+
+#: js/ui/status/network.js:847
+msgid "Wi-Fi is Off"
+msgstr "Il Wi-Fi è spento"
+
+#: js/ui/status/network.js:848
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "È necessario accendere il Wi-Fi per potersi connettere a una rete."
+
+#: js/ui/status/network.js:849
+msgid "Turn On Wi-Fi"
+msgstr "Accendi Wi-Fi"
+
+#: js/ui/status/network.js:877
+msgid "Wi-Fi Networks"
+msgstr "Reti Wi-Fi"
+
+#: js/ui/status/network.js:881
+msgid "Select a network"
+msgstr "Seleziona una rete"
+
+#: js/ui/status/network.js:917
+msgid "No Networks"
+msgstr "Nessuna rete"
+
+#: js/ui/status/network.js:942 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "Usare l'interruttore hardware per spegnere"
+
+#: js/ui/status/network.js:1253
+msgid "Select Network"
+msgstr "Seleziona rete"
+
+#: js/ui/status/network.js:1259
+msgid "Wi-Fi Settings"
+msgstr "Impostazioni Wi-Fi"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1358
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "Hotspot %s attivo"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1373
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s non collegato"
+
+#: js/ui/status/network.js:1473
+msgid "connecting…"
+msgstr "connessione…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1476
+msgid "authentication required"
+msgstr "richiesta autenticazione"
+
+#: js/ui/status/network.js:1478
+msgid "connection failed"
+msgstr "connessione non riuscita"
+
+#: js/ui/status/network.js:1524
+msgid "VPN Settings"
+msgstr "Impostazioni VPN"
+
+#: js/ui/status/network.js:1541
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1551
+msgid "VPN Off"
+msgstr "VPN spenta"
+
+#: js/ui/status/network.js:1612 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Impostazioni rete"
+
+#: js/ui/status/network.js:1640
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s connessione via cavo"
+msgstr[1] "%s connessioni via cavo"
+
+#: js/ui/status/network.js:1644
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s connessione Wi-Fi"
+msgstr[1] "%s connessioni Wi-Fi"
+
+#: js/ui/status/network.js:1648
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s connessione modem"
+msgstr[1] "%s connessioni modem"
+
+#: js/ui/status/network.js:1792
+msgid "Connection failed"
+msgstr "Connessione non riuscita"
+
+#: js/ui/status/network.js:1793
+msgid "Activation of network connection failed"
+msgstr "Attivazione della connessione di rete non riuscita"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Modalità notturna disattivata"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Modalità notturna attivata"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Riprendi"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Disabilita fino a domani"
+
+#: js/ui/status/power.js:51 js/ui/status/powerProfiles.js:57
+msgid "Power Settings"
+msgstr "Impostazioni alimentazione"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr "Carica"
+
+#: js/ui/status/power.js:74
+msgid "Not Charging"
+msgstr "Non in carica"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:77 js/ui/status/power.js:83
+msgid "Estimating…"
+msgstr "Stima in corso…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d rimanente (%d %%)"
+
+# in stile MacOS sarebbe "al pieno" :P
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:97
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d alla carica (%d %%)"
+
+#. The icon label
+#: js/ui/status/power.js:145
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/powerProfiles.js:19
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Prestazioni"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Bilanciate"
+
+#: js/ui/status/powerProfiles.js:21
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Risparmio energia"
+
+#: js/ui/status/remoteAccess.js:43
+msgid "Screen is Being Shared"
+msgstr "Lo schermo è condiviso"
+
+#: js/ui/status/remoteAccess.js:45
+msgid "Turn off"
+msgstr "Spegni"
+
+#: js/ui/status/remoteAccess.js:149
+msgid "Stop Screencast"
+msgstr "Ferma video"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Modalità aereo attivata"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "Blocca"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "Spegni / Termina sessione"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "Sospendi"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "Riavvia…"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "Spegni…"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Termina sessione"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "Cambia utente…"
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Dispositivo Thunderbolt sconosciuto"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"È stato rilevato un nuovo dispositivo: scollegarlo e ricollegarlo per "
+"poterlo utilizzare."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispositivo Thunderbolt non autorizzato"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"È stato rilevato un nuovo dispositivo e l'amministratore di sistema deve "
+"autorizzarlo."
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Errore autenticazione Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Impossibile autorizzare il dispositivo Thunderbolt: %s"
+
+#: js/ui/status/volume.js:159
+msgid "Volume changed"
+msgstr "Volume modificato"
+
+#: js/ui/status/volume.js:221
+msgid "Volume"
+msgstr "Volume"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Duplica"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Unisci schermi"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Solo esterno"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Solo integrato"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Scorrere verso l'alto per sbloccare"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Fare clic o premere un tasto per sbloccare"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Sblocca finestra"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Accedi come altro utente"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Benvenuti in GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Per imparare a usare il sistema, dai un'occhiata al tour."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "No grazie"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Fai il tour"
+
+# (ndt) dovrebbe essere il nome dell'applicazione
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» è pronto"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Mantenere queste impostazioni per il monitor?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Ripristina impostazioni"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Mantieni modifiche"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Le modifiche alle impostazioni saranno ripristinate tra %d secondo"
+msgstr[1] "Le modifiche alle impostazioni saranno ripristinate tra %d secondi"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:553
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Cattura schermata"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Nascondi"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Ripristina"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Massimizza"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Muovi"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Ridimensiona"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Muovi barra del titolo a schermo"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Sempre in primo piano"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Sempre sullo spazio di lavoro visibile"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Sposta su spazio di lavoro a sinistra"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Sposta su spazio di lavoro a destra"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Sposta su spazio di lavoro in alto"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Sposta su spazio di lavoro in basso"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Sposta su schermo in alto"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Sposta su schermo in basso"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Sposta su schermo a sinistra"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Sposta su schermo a destra"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Chiudi"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendario di Evolution"
+
+#: src/main.c:419 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Stampa la versione"
+
+#: src/main.c:425
+msgid "Mode used by GDM for login screen"
+msgstr "Modalità usata da GDM per la schermata d'accesso"
+
+#: src/main.c:431
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Usa una modalità specifica, per es. «gdm» per la schermata d'accesso"
+
+#: src/main.c:437
+msgid "List possible modes"
+msgstr "Elenca le modalità possibili"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Sconosciuto"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Avvio di «%s» non riuscito"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Le password non corrispondono."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "La password non può essere vuota"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "La finestra di dialogo di autenticazione è stata annullata dall'utente"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:210
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Estensioni"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:211
+msgid "Manage your GNOME Extensions"
+msgstr "Gestione delle Estensioni GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "Il progetto GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Estensioni GNOME gestisce l'aggiornamento e la configurazione delle "
+"preferenze delle estensioni e la rimozione o la disabilitazione di "
+"estensioni indesiderate."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configura le estensioni di GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:132
+#: subprojects/extensions-app/js/main.js:143
+msgid "No Matches"
+msgstr "Nessuna corrispondenza"
+
+#: subprojects/extensions-app/js/main.js:171
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Rimuovere «%s»?"
+
+#: subprojects/extensions-app/js/main.js:172
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Una volta rimossa l'estensione sarà necessario scaricarla nuovamente per "
+"abilitarla"
+
+#: subprojects/extensions-app/js/main.js:176
+msgid "Remove"
+msgstr "Rimuovi"
+
+#: subprojects/extensions-app/js/main.js:209
+msgid "translator-credits"
+msgstr "Milo Casagrande <milo@milo.name>"
+
+#: subprojects/extensions-app/js/main.js:337
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d estensione verrà aggiornata al prossimo accesso."
+msgstr[1] "%d estensioni verranno aggiornate al prossimo accesso."
+
+#: subprojects/extensions-app/js/main.js:479
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "L'estensione non è compatibile con la versione attuale di GNOME"
+
+#: subprojects/extensions-app/js/main.js:482
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "L'estensione ha causato un errore"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "L'estensione può essere aggiornata"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Sito web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Rimuovi…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Aiuto"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Informazioni su Estensioni"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Le estensioni possono causare problemi di prestazioni e stabilità. "
+"Disabilitare le estensioni se si riscontrano problemi con il proprio sistema."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Installata manualmente"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Per trovare e installare estensioni, visitare il sito web <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Integrata"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Nessuna estensione installata"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Impossibile recuperare l'elenco delle estensioni installate. Assicurarsi di "
+"aver eseguire l'accesso a GNOME e riprovare."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Disponibili aggiornamenti estensioni"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Termina sessione…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "La nuova estensione è stata creata in %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Il nome dovrebbe essere una stringa descrittiva molto corta.\n"
+"Esempi: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nome"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"La descrizione è una frase singola che spiega lo scopo dell'estensione.\n"
+"Esempi: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descrizione"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"Uno UUID è un identificativo globale univoco per l'estensione.\n"
+"Dovrebbe essere espresso sotto forma di indirizzo email "
+"(clicktofocus@janedoe.example.com)\n"
+"\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Scegliere uno dei modelli disponibili:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Modello"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "L'identificatore univoco della nuova estensione"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Il nome visibile dagli utenti della nuova estensione"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIZIONE"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Una breve descrizione di cosa fa l'estensione"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODELLO"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Il modello da usare per la nuova estensione"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Inserire le informazioni sull'estensione interattivamente"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Crea nuova estensione"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argomenti sconosciuti"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Sono richiesti UUID, nome e descrizione"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Collegamento a GNOME Shell non riuscito\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "L'estensione «%s» non esiste\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Disabilita un'estensione"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nessun UUID fornito"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Fornito più di un UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Abilita un'estensione"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "L'estensione «%s» non esiste\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostra informazioni sull'estensione"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Sovrascrive un'estensione esistente"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "BUNDLE_ESTENSIONE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Installa un bundle di estensioni"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nessun bundle di estensioni specificato"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "È stato specificato più di un bundle di estensioni"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Mostra estensioni installate dall'utente"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostra estensioni di sistema"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostra estensioni abilitate"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostra estensioni disabilitate"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostra estensioni con preferenze"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostra estensioni con aggiornamenti"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Stampa dettagli dell'estensioni"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Elenca estensioni installate"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Sorgenti aggiuntivi da includere nel bundle"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Uno schema GSettings da includere"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "La directory dove trovare le traduzioni"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMINIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Il domino gettext da usare per le traduzioni"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Sovrascrive un pacchetto esistente"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "La directory dove creare il pacchetto"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIRECTORY_SORGENTE"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Crea un bundle di estensioni"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "È stata specificata più di una directory di sorgenti"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "L'estensione «%s» non ha preferenze\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Apertura delle preferenze per l'estensione «%s» non riuscita: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Apre preferenze estensione"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Ripristina un'estensione"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Impossibile rimuovere estensioni di sistema\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Rimozione di «%s» non riuscita\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Rimuove un'estensione"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Non stampa messaggi di errore"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Percorso"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autore"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versione"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stato"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» non accetta alcun argomento"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Uso:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Stampa informazioni sulla versione ed esce"
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMANDO"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARG…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Comandi:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Stampa l'aiuto"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Abilita estensione"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Disabilita estensione"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Ripristina estensione"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Rimuovi estensione"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Elenca estensioni"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Mostra informazioni sull'estensione"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Apri preferenze estensione"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Crea estensione"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Crea pacchetto estensione"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Installa bundle estensioni"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Usare «%s» per maggiori informazioni.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Semplice"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Un'estensione vuota"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicatore"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Aggiunge un'icona alla barra superiore"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u uscita"
+msgstr[1] "%u uscite"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ingresso"
+msgstr[1] "%u ingressi"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Audio di sistema"
diff --git a/po/ja.po b/po/ja.po
new file mode 100644
index 0000000..2d26bc9
--- /dev/null
+++ b/po/ja.po
@@ -0,0 +1,3236 @@
+# Japanese translation of gnome-shell package.
+# Copyright (C) 2010-2017, 2019-2020, 2022 the gnome-shell copyright holder.
+# This file is distributed under the same license as the gnome-shell package.
+# Nishio Futoshi <fut_nis@d3.dion.ne.jp>, 2010.
+# Kiyotaka NISHIBORI <ml.nishibori.kiyotaka@gmail.com>, 2011.
+# Hideki Yamane <henrich@debian.org>, 2011.
+# Takayuki KUSANO <AE5T-KSN@asahi-net.or.jp>, 2011, 2012.
+# Jiro Matsuzawa <jmatsuzawa@gnome.org>, 2011-2017.
+# Takanori MATSUURA <t.matsuu@gmail.com>, 2012.
+# Yoji TOYODA <bsyamato@sea.plala.or.jp>, 2013.
+# Ikuya Awashiro <ikuya@fruitsbasket.info>, 2014.
+# IWAI, Masaharu <iwaim.sub@gmail.com>, 2015.
+# sicklylife <translation@sicklylife.jp>, 2019-2020, 2022.
+# 小山田 純 <oyamadajun@outlook.jp>, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-03-12 09:44+0000\n"
+"PO-Revision-Date: 2022-03-12 19:00+0900\n"
+"Last-Translator: sicklylife <translation@sicklylife.jp>\n"
+"Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "ランチャー"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "お気に入りのアプリケーション 1 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "お気に入りのアプリケーション 2 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "お気に入りのアプリケーション 3 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "お気に入りのアプリケーション 4 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "お気に入りのアプリケーション 5 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "お気に入りのアプリケーション 6 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "お気に入りのアプリケーション 7 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "お気に入りのアプリケーション 8 をアクティブにする"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "お気に入りのアプリケーション 9 をアクティブにする"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2062
+msgid "Screenshots"
+msgstr "スクリーンショット"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "Take a screenshot interactively"
+msgstr "インタラクティブにスクリーンショットを撮る"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:247
+msgid "Take a screenshot"
+msgstr "スクリーンショットを撮る"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:243
+msgid "Take a screenshot of a window"
+msgstr "ウィンドウのスクリーンショットを撮る"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:239
+msgid "Record a screencast interactively"
+msgstr "インタラクティブにスクリーンキャストを撮る"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "システム"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "通知リストを表示する"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "アクティブな通知にフォーカスを当てる"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "アクティビティ画面を表示する"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "すべてのアプリケーションを表示する"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "アプリケーションメニューを開く"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "ウィンドウ管理とアプリケーションの起動"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Alt-F2 から開発・テスト用の内部ツールを利用可能にする"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 ダイアログを利用した内部のデバッグツールとモニターツールへのアクセスを"
+"許可します。"
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "有効にする拡張機能の UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell の拡張機能には UUID プロパティがあり、このキーは、ロードしたい拡"
+"張機能の UUID のリストです。ロードしたい拡張機能はこのリストに含めなければな"
+"りません。このリストは org.gnome.Shell の EnableExtensions や "
+"DisableExtensions といった DBus メソッドでも操作できます。"
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "強制的に無効にする拡張機能の UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+#, fuzzy
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell の拡張機能には UUID プロパティがあり、このキーは、現在のモードの"
+"一部としてロードされる場合でも無効化したい拡張機能の UUID のリストです。この"
+"リストは org.gnome.Shell の EnableExtensions や DisableExtensions といった "
+"DBus メソッドでも操作できます。このキーは“enabled-extensions”の設定より優先さ"
+"れます。"
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "ユーザー拡張機能を無効化"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"ユーザーが有効にした拡張機能をすべて無効にします。“enabled-extension”の設定値"
+"には影響しません。"
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "拡張機能のバージョン互換の検証を無効にする"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"通常は現在起動中の GNOME Shell のバージョンをサポートする拡張機能のみを読み込"
+"みます。この設定を true にした場合、バージョンチェック機能が無効になり、拡張"
+"機能のサポートバージョンに関係なく、すべての拡張機能を読み込みます。"
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "お気に入りのアプリケーションの .desktop ファイル ID の一覧"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "これらの ID で示されるアプリケーションは「お気に入り」に表示されます。"
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "コマンド (Alt-F2) ダイアログの履歴"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "looking glass ダイアログの履歴"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "ユーザーメニューに「ログアウト」を常に表示する"
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"このキーは、単一ユーザー、単一セッション状況で「ログアウト」メニューアイテム"
+"を自動的に非表示にする機能よりも優先します。"
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"マウント対象の暗号化されたファイルシステムやリモートファイルシステムのパス"
+"ワードを記憶する"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"GNOME Shell は、暗号化されたデバイスやリモートファイルシステムのマウント時に"
+"パスワードを要求します。パスワードを保存できる場合は、「パスワードを保存」"
+"チェックボックスが表示されます。このキーは、チェックボックスのデフォルト値と"
+"なります。"
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"デフォルトの Bluetooth アダプターにデバイスが関連付けられているかどうか"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Bluetooth アダプターの電源がオンになっているか、あるいはデフォルトのアダプ"
+"ターに関連付けられたデバイスがある場合にのみ、GNOME Shell は Bluetooth メ"
+"ニューを表示します。デフォルトのアダプターに関連付けられたデバイスがなくなっ"
+"た場合に、この値はリセットされます。"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "“GNOME へようこそ”ダイアログが表示された最新のバージョン"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"このキーは“GNOME へようこそ”ダイアログが最後に表示されたときの GNOME のバー"
+"ジョンを指定します。空の場合は識別可能な最も古いバージョンを、大きすぎる値の"
+"場合はまだ存在しないバージョンを意味します。大きすぎる値を使用することでダイ"
+"アログを意図的に無効にすることができます。"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid "Layout of the app picker"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu"
+msgstr "アプリケーションメニューを開くキーバインド"
+
+#: data/org.gnome.shell.gschema.xml.in:150
+msgid "Keybinding to open the application menu."
+msgstr "アプリケーションメニューを開くキーバインドです。"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between overview states"
+msgstr "アクティビティ画面の状態を切り替えるキーバインド"
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "通常画面、ウィンドウ一覧、アプリ一覧の順に切り替えるキーバインドです"
+
+#: data/org.gnome.shell.gschema.xml.in:164
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "アプリ一覧、ウィンドウ一覧、通常画面の順に切り替えるキーバインドです"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "アプリケーション一覧を開くキーバインド"
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "アプリケーション一覧を開くキーバインドです。"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the overview"
+msgstr "アクティビティ画面を開くキーバインド"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the Activities Overview."
+msgstr "アクティビティ画面を開くキーバインドです。"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "通知リストの表示/非表示を切り替えるキーバインド"
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "通知リストの表示/非表示を切り替えるキーバインドです。"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification"
+msgstr "アクティブな通知にフォーカスを当てるキーバインド"
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to focus the active notification."
+msgstr "アクティブな通知にフォーカスを当てるキーバインドです。"
+
+#: data/org.gnome.shell.gschema.xml.in:199
+msgid "Switch to application 1"
+msgstr "アプリケーション 1 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid "Switch to application 2"
+msgstr "アプリケーション 2 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 3"
+msgstr "アプリケーション 3 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 4"
+msgstr "アプリケーション 4 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 5"
+msgstr "アプリケーション 5 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 6"
+msgstr "アプリケーション 6 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 7"
+msgstr "アプリケーション 7 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 8"
+msgstr "アプリケーション 8 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 9"
+msgstr "アプリケーション 9 に切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Limit switcher to current workspace."
+msgstr "スイッチャーを現在のワークスペース内に制限する"
+
+#: data/org.gnome.shell.gschema.xml.in:257
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"true に設定した場合、現在のワークスペースにウィンドウのあるアプリケーションだ"
+"けをスイッチャーに表示します。false に設定した場合はすべてのアプリケーション"
+"を表示します。"
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid "The application icon mode."
+msgstr "アプリケーションアイコンモード"
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"ウィンドウスイッチャーでのウィンドウの表示方法を設定します。指定可能な値"
+"は、“thumbnail-only”(ウィンドウのサムネイルを表示します)、“app-icon-only”(ア"
+"プリケーションアイコンを表示します)、あるいは“both”です。"
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"true に設定した場合、現在のワークスペースのウィンドウだけをスイッチャーに表示"
+"します。false に設定した場合はすべてのアプリケーションを表示します。"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "Locations"
+msgstr "場所"
+
+#: data/org.gnome.shell.gschema.xml.in:295
+msgid "The locations to show in world clocks"
+msgstr "世界時計に表示される地域です"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Automatic location"
+msgstr "自動位置検出"
+
+#: data/org.gnome.shell.gschema.xml.in:306
+msgid "Whether to fetch the current location or not"
+msgstr "現在の位置を取得するかどうか"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "Location"
+msgstr "場所"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "The location for which to show a forecast"
+msgstr "表示される天気予報の地域です"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+msgid "Attach modal dialog to the parent window"
+msgstr "モーダルダイアログを親ウィンドウに結び付ける"
+
+#: data/org.gnome.shell.gschema.xml.in:327
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+#: data/org.gnome.shell.gschema.xml.in:352
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"GNOME Shell 使用時は、 このキーが、org.gnome.mutter の同じキーよりも優先しま"
+"す。"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "ウィンドウを画面の端に移動させたときにタイル状に配置する"
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Workspaces are managed dynamically"
+msgstr "ワークスペースを動的に管理する"
+
+#: data/org.gnome.shell.gschema.xml.in:351
+msgid "Workspaces only on primary monitor"
+msgstr "プライマリモニターのみワークスペースを切り替える"
+
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "マウスモードにおけるフォーカス遷移をポインターが停止するまで遅延させる"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "ネットワークログイン"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "何か問題が起きています"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"大変申し訳ありませんが、拡張機能の設定を表示できない問題が発生しました。拡張"
+"機能の作者に問題を報告することをお勧めします。"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "技術的な詳細"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "ホームページ"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "拡張機能のホームページを開く"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:437 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: js/ui/status/network.js:956 subprojects/extensions-app/js/main.js:174
+msgid "Cancel"
+msgstr "キャンセル"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "パスワード"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "セッションを選択する"
+
+#: js/gdm/loginDialog.js:463
+msgid "Not listed?"
+msgstr "アカウントが見つかりませんか?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:931
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(たとえば、user あるいは %s)"
+
+#: js/gdm/loginDialog.js:936 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "ユーザー名"
+
+#: js/gdm/loginDialog.js:1257
+msgid "Login Window"
+msgstr "ログインウィンドウ"
+
+#: js/gdm/util.js:434
+msgid "Authentication error"
+msgstr "認証エラー"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:606
+msgid "(or swipe finger across reader)"
+msgstr "(または指紋リーダーで指をスワイプ)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:611
+msgid "(or place finger on reader)"
+msgstr "(または指紋リーダーに指を置く)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "電源オフ"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;電源オフ;パワーオフ;シャットダウン;停止;"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "再起動"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;リブート;リスタート;再起動;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "画面ロック"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen;画面ロック"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "ログアウト"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;ログアウト;サインオフ"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "サスペンド"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;サスペンド;スリープ"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "ユーザーを切り替え"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "switch user;ユーザー切り替え"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "スクリーンショットを撮る"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;スクリーンショット;スクリーンキャスト;"
+"スニップ;キャプチャー;レコーディング;撮影;録画;画面;記録;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "画面の回転をロック解除"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "画面の回転をロック"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "コマンドが見つかりませんでした"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "コマンドを解析できませんでした:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s”の実行に失敗しました:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "たった今"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d 分前"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d 時間前"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "昨日"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d 日前"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d 週間前"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d ヶ月前"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d 年前"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "昨日 %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B%-e日 %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%Y年%B%-e日 %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%p %l:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "昨日 %p %l:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A %p %l:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B%-e日 %p %l:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%Y年%B%-e日 %p %l:%M"
+
+#: js/portalHelper/main.js:53
+msgid "Hotspot Login"
+msgstr "ホットスポットログイン"
+
+#: js/portalHelper/main.js:106
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"このホットスポットログインの接続は安全ではありません。このページであなたが入"
+"力したパスワードなどの情報は、近くにいる人に知られてしまう恐れがあります。"
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:406
+msgid "Deny Access"
+msgstr "拒否"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:411
+msgid "Grant Access"
+msgstr "許可"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s をダッシュボードにピン留めしました。"
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s のピン留めを外しました。"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "開いているウィンドウ"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "新しいウィンドウで開く"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "詳細を表示"
+
+#: js/ui/appMenu.js:96
+msgid "Quit"
+msgstr "終了"
+
+#: js/ui/appMenu.js:156 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "ピン留めを外す"
+
+#: js/ui/appMenu.js:157
+msgid "Pin to Dash"
+msgstr "ダッシュボードにピン留め"
+
+#: js/ui/appMenu.js:174
+msgid "Launch using Integrated Graphics Card"
+msgstr "統合グラフィックカードを使用して起動"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Discrete Graphics Card"
+msgstr "ディスクリートグラフィックカードを使用して起動"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "オーディオデバイスを選択"
+
+#: js/ui/audioDeviceSelection.js:56
+msgid "Sound Settings"
+msgstr "サウンド設定"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "ヘッドフォン"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "ヘッドセット"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:270
+msgid "Microphone"
+msgstr "マイク"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "背景を変更…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "ディスプレイ設定"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "設定"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "日"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "月"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "火"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "水"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "木"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "金"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "土"
+
+# %OB は February、%Ob は Feb のように月名が表示される
+# 日本語環境だと %OB は 「2月」 %Ob は 「 2月」 (半角スペース付き) のように表示される?
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB"
+msgstr "%B"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:412
+msgid "%OB %Y"
+msgstr "%Y年%B"
+
+#: js/ui/calendar.js:472
+msgid "Previous month"
+msgstr "先月"
+
+#: js/ui/calendar.js:490
+msgid "Next month"
+msgstr "来月"
+
+#: js/ui/calendar.js:642
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:701
+msgid "Week %V"
+msgstr "第 %V 週"
+
+#: js/ui/calendar.js:880
+msgid "No Notifications"
+msgstr "通知なし"
+
+#: js/ui/calendar.js:937
+msgid "Do Not Disturb"
+msgstr "通知ポップアップを表示しない"
+
+#: js/ui/calendar.js:958
+msgid "Clear"
+msgstr "消去"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s”の応答がありません。"
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"もうしばらく応答を待ちますか? それともアプリケーションを強制的に終了しますか?"
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "強制終了"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "応答を待つ"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "外付けドライブが接続されました"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "外付けドライブが外されました"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "ボリュームをロック解除できません"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+"インストールされているバージョンの udisks は PIM の設定に対応していません"
+
+#: js/ui/components/autorunManager.js:334
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s で開く"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "または、ルーターの“WPS”ボタンを押して接続する方法もあります。"
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:244
+#: js/ui/status/network.js:335 js/ui/status/network.js:961
+msgid "Connect"
+msgstr "接続"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "キー"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "秘密鍵のパスワード"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identity"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "サービス"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:746 js/ui/components/networkAgent.js:767
+msgid "Authentication required"
+msgstr "認証が必要です"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:747
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"無線ネットワーク“%s”にアクセスするにはパスワードか暗号化キーが必要です。"
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:751
+msgid "Wired 802.1X authentication"
+msgstr "有線 802.1X の認証"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "ネットワーク名"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:755
+msgid "DSL authentication"
+msgstr "DSL 認証"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:760
+msgid "PIN code required"
+msgstr "PIN コードが必要です"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:761
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "モバイルブロードバンドデバイスには PIN コードが必要です"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:752
+#: js/ui/components/networkAgent.js:756 js/ui/components/networkAgent.js:768
+#: js/ui/components/networkAgent.js:772
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s”への接続にはパスワードが必要です。"
+
+#: js/ui/components/networkAgent.js:735 js/ui/status/network.js:1751
+msgid "Network Manager"
+msgstr "ネットワークマネージャー"
+
+#: js/ui/components/networkAgent.js:771
+msgid "VPN password"
+msgstr "VPN パスワード"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "認証が必要です"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "管理者"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "認証"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "申し訳ありません、認証できませんでした。再試行してください。"
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s は %s になりました"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:405
+msgid "Windows"
+msgstr "ウィンドウ"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "アプリケーションを表示する"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "ダッシュボード"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%Y年%-m月%-e日"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%Y年%-m月%-e日 (%a)"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-m月%-e日"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y年%-m月%-e日"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "今日"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "明日"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "終日"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "イベントなし"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "世界時計を追加…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "世界時計"
+
+#: js/ui/dateMenu.js:677
+msgid "Loading…"
+msgstr "読み込み中…"
+
+#: js/ui/dateMenu.js:687
+msgid "Go online for weather information"
+msgstr "気象情報取得のためにネットワークに接続してください"
+
+#: js/ui/dateMenu.js:689
+msgid "Weather information is currently unavailable"
+msgstr "気象情報を取得できません"
+
+#: js/ui/dateMenu.js:699
+msgid "Weather"
+msgstr "天気"
+
+#: js/ui/dateMenu.js:701
+msgid "Select weather location…"
+msgstr "天気の場所を選択…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s のログアウト"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "ログアウト"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s は %d 秒後に自動的にログアウトします。"
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "%d 秒後に自動的にログアウトします。"
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "ログアウト"
+
+#: js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Power Off"
+msgstr "電源オフ"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "アップデートをインストールして電源オフ"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "%d 秒後に自動的にシステムの電源を切ります。"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "保留中のソフトウェアアップデートをインストールする"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "電源オフ"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "再起動"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "アップデートをインストールして再起動"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "%d 秒後にシステムを再起動します。"
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "再起動"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "再起動してアップデートをインストール"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "%d 秒後にシステムを再起動してアップデートをインストールします。"
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "再起動とインストール"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "インストールと電源オフ"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "アップデートをインストールして電源をオフにする"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "再起動してアップグレード"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s は再起動後にインストールされます。アップグレードには時間が掛かることが"
+"あります。重要なデータはバックアップを取っておいてください。また、コンピュー"
+"ター本体は電源につないでください。"
+
+#: js/ui/endSessionDialog.js:287
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"バッテリー低下: アップデートをインストールする前に電源に接続してください。"
+
+#: js/ui/endSessionDialog.js:296
+msgid "Some applications are busy or have unsaved work"
+msgstr "実行中または作業が未保存のアプリケーションがあります"
+
+#: js/ui/endSessionDialog.js:301
+msgid "Other users are logged in"
+msgstr "他のユーザーがログインしています"
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "起動オプション"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:688
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (リモート)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:691
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (コンソール)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "インストール"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "拡張機能をインストール"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.org から“%s”をダウンロードしてインストールしますか?"
+
+#: js/ui/extensionSystem.js:267
+msgid "Extension Updates Available"
+msgstr "拡張機能のアップデートが利用可能です"
+
+#: js/ui/extensionSystem.js:268
+msgid "Extension updates are ready to be installed."
+msgstr "拡張機能のアップデートをインストールする準備ができました。"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr ""
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "%s を押してショートカットを復元できます。"
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "拒否"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "許可"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "スローキーをオンにする"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "スローキーをオフにする"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"[SHIFT] キーを 8 秒間押下しました。これはスローキー機能のショートカットとし"
+"て、キーボード操作に影響を与えるものです。"
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "固定キーをオンにする"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "固定キーをオフにする"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"[SHIFT] キーを連続して 5 回押下しました。これは固定キー機能のショートカットと"
+"して、キーボード操作に影響を与えるものです。"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"同時に 2 つのキーを押下したか、[SHIFT] キーを連続して 5 回押下しました。これ"
+"は固定キー機能を無効にし、キーボード操作に影響を与えるものです。"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "オンのままにする"
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:1341
+msgid "Turn On"
+msgstr "オンにする"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:160 js/ui/status/network.js:336
+#: js/ui/status/network.js:1341 js/ui/status/network.js:1456
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "オフにする"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "オフのままにする"
+
+#: js/ui/keyboard.js:250
+msgid "Region & Language Settings"
+msgstr "地域と言語の設定"
+
+#: js/ui/lookingGlass.js:701
+msgid "No extensions installed"
+msgstr "機能拡張はインストールされていません"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:762
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s は何もエラーを出力していません。"
+
+#: js/ui/lookingGlass.js:768
+msgid "Hide Errors"
+msgstr "エラーを非表示"
+
+#: js/ui/lookingGlass.js:772 js/ui/lookingGlass.js:845
+msgid "Show Errors"
+msgstr "エラーを表示"
+
+#: js/ui/lookingGlass.js:781
+msgid "Enabled"
+msgstr "有効"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:784 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "無効"
+
+#: js/ui/lookingGlass.js:786
+msgid "Error"
+msgstr "エラー"
+
+#: js/ui/lookingGlass.js:788
+msgid "Out of date"
+msgstr "最新ではありません"
+
+#: js/ui/lookingGlass.js:790
+msgid "Downloading"
+msgstr "ダウンロード中"
+
+#: js/ui/lookingGlass.js:823
+msgid "View Source"
+msgstr "ソースを表示"
+
+#: js/ui/lookingGlass.js:834
+msgid "Web Page"
+msgstr "ウェブページ"
+
+#: js/ui/main.js:265
+msgid "System was put in unsafe mode"
+msgstr ""
+
+#: js/ui/main.js:266
+msgid "Applications now have unrestricted access"
+msgstr ""
+
+#: js/ui/main.js:267 js/ui/overview.js:58
+msgid "Undo"
+msgstr "元に戻す"
+
+#: js/ui/main.js:313
+msgid "Logged in as a privileged user"
+msgstr "特権ユーザーでログインしました"
+
+#: js/ui/main.js:314
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"セキュリティ上の観点から、特権ユーザーでセッションを実行することはお勧めでき"
+"ません。可能な限り、一般ユーザーでログインしてください。"
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "システム情報"
+
+#: js/ui/mpris.js:200
+msgid "Unknown artist"
+msgstr "不明なアーティスト"
+
+#: js/ui/mpris.js:210
+msgid "Unknown title"
+msgstr "不明なタイトル"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:315
+msgid "Type to search"
+msgstr "検索ワードを入力"
+
+#: js/ui/overviewControls.js:393
+msgid "Applications"
+msgstr "アプリケーション"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "アクティビティ画面"
+
+#: js/ui/padOsd.js:97
+msgid "New shortcut…"
+msgstr "新しいショートカット…"
+
+#: js/ui/padOsd.js:148
+msgid "Application defined"
+msgstr "アプリケーション定義"
+
+#: js/ui/padOsd.js:149
+msgid "Show on-screen help"
+msgstr "オンスクリーンヘルプを表示"
+
+#: js/ui/padOsd.js:150
+msgid "Switch monitor"
+msgstr "モニターを切り替え"
+
+#: js/ui/padOsd.js:151
+msgid "Assign keystroke"
+msgstr "キーストロークを割り当て"
+
+#: js/ui/padOsd.js:220
+msgid "Done"
+msgstr "完了"
+
+#: js/ui/padOsd.js:737
+msgid "Edit…"
+msgstr "編集…"
+
+#: js/ui/padOsd.js:779 js/ui/padOsd.js:896
+msgid "None"
+msgstr "なし"
+
+#: js/ui/padOsd.js:850
+msgid "Press a button to configure"
+msgstr "ボタンを押して設定してください"
+
+#: js/ui/padOsd.js:851
+msgid "Press Esc to exit"
+msgstr "Esc を押すと終了します"
+
+#: js/ui/padOsd.js:854
+msgid "Press any key to exit"
+msgstr "キーを押すと終了します"
+
+#: js/ui/panel.js:241
+msgid "Activities"
+msgstr "アクティビティ"
+
+#: js/ui/panel.js:364
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "システム"
+
+#: js/ui/panel.js:480
+msgid "Top Bar"
+msgstr "トップバー"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "コマンドを実行"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Esc を押すと閉じます"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Wayland 上では再起動できません"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "再起動中…"
+
+#: js/ui/screenShield.js:229
+msgid "GNOME needs to lock the screen"
+msgstr "画面をロックする必要があります"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:269 js/ui/screenShield.js:644
+msgid "Unable to lock"
+msgstr "ロックできません"
+
+#: js/ui/screenShield.js:270 js/ui/screenShield.js:645
+msgid "Lock was blocked by an application"
+msgstr "アプリケーションによってロックがブロックされました"
+
+#: js/ui/screenshot.js:1148
+msgid "Selection"
+msgstr "選択範囲"
+
+#: js/ui/screenshot.js:1158
+msgid "Area Selection"
+msgstr "領域選択"
+
+#: js/ui/screenshot.js:1163
+msgid "Screen"
+msgstr "画面"
+
+#: js/ui/screenshot.js:1173
+msgid "Screen Selection"
+msgstr "画面選択"
+
+#: js/ui/screenshot.js:1178
+msgid "Window"
+msgstr "ウィンドウ"
+
+#: js/ui/screenshot.js:1188
+msgid "Window Selection"
+msgstr "ウィンドウ選択"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "スクリーンショット / スクリーンキャスト"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "ポインターを表示"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1828
+msgid "Screencasts"
+msgstr "スクリーンキャスト"
+
+# ここは英語のままの方がいいようなので翻訳しません (sicklylife)
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1833
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Screencast from %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1902 js/ui/screenshot.js:2115
+msgid "Screenshot"
+msgstr "スクリーンショット"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1908
+msgid "Screencast recorded"
+msgstr "スクリーンキャストを撮影しました"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1910
+msgid "Click here to view the video."
+msgstr "ここをクリックすると動画を再生します。"
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2129
+msgid "Show in Files"
+msgstr "“ファイル”で表示"
+
+# ここは英語のままの方がいいようなので翻訳しません (sicklylife)
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2075
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Screenshot from %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2121
+msgid "Screenshot captured"
+msgstr "スクリーンショットを撮影しました"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2123
+msgid "You can paste the image from the clipboard."
+msgstr "クリップボードから画像を貼り付けることができます。"
+
+#: js/ui/screenshot.js:2176 js/ui/screenshot.js:2341
+msgid "Screenshot taken"
+msgstr "スクリーンショットを撮影しました"
+
+#: js/ui/search.js:815
+msgid "Searching…"
+msgstr "検索中…"
+
+#: js/ui/search.js:817
+msgid "No results."
+msgstr "一致するものがありません。"
+
+#: js/ui/search.js:948
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "他 %d 件"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "検索"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "コピー"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "貼り付け"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "文字を表示"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "文字を非表示"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps Lock がオンになっています。"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "隠しボリューム"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows システムボリューム"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "キーファイルを使用する"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"キーファイルを使用するボリュームのロック解除には <i>%s</i> ユーティリティを使"
+"用します。"
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM 数値"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "パスワードを保存する"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "ロック解除"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s を開く"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM は数字か空である必要があります。"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s を起動できません"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s アプリケーションが見つかりません"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "アクセシビリティ"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "ハイコントラスト"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "ズーム"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "スクリーンリーダー"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "スクリーンキーボード"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "視覚警告"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "固定キー"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "スローキー"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "バウンスキー"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "マウスキー"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "大きな文字"
+
+#: js/ui/status/bluetooth.js:54
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:67 js/ui/status/network.js:626
+msgid "Bluetooth Settings"
+msgstr "Bluetooth の設定"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:166
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d 台接続"
+
+#: js/ui/status/bluetooth.js:170
+msgid "Bluetooth On"
+msgstr "Bluetooth オン"
+
+#: js/ui/status/bluetooth.js:172
+msgid "Bluetooth Off"
+msgstr "Bluetooth オフ"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "明るさ"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "シングルクリック"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "ダブルクリック"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "ドラッグ"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "セカンダリークリック"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "自動クリック"
+
+#: js/ui/status/keyboard.js:827
+msgid "Keyboard"
+msgstr "キーボード"
+
+#: js/ui/status/keyboard.js:844
+msgid "Show Keyboard Layout"
+msgstr "キーボードレイアウトを表示"
+
+#: js/ui/status/location.js:231 js/ui/status/location.js:255
+msgid "Location Enabled"
+msgstr "位置情報サービス有効"
+
+#: js/ui/status/location.js:232 js/ui/status/location.js:256
+msgid "Disable"
+msgstr "無効"
+
+#: js/ui/status/location.js:234
+msgid "Privacy Settings"
+msgstr "プライバシー設定"
+
+#: js/ui/status/location.js:254
+msgid "Location In Use"
+msgstr "位置情報サービス使用中"
+
+#: js/ui/status/location.js:258
+msgid "Location Disabled"
+msgstr "位置情報サービス無効"
+
+#: js/ui/status/location.js:259
+msgid "Enable"
+msgstr "有効"
+
+#: js/ui/status/location.js:386
+msgid "Allow location access"
+msgstr "位置情報へのアクセスの許可"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:388
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "アプリ %s が位置情報へのアクセスを要求しています"
+
+#: js/ui/status/location.js:398
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"位置情報のアクセス許可設定は、プライバシー設定からいつでも変更できます。"
+
+#: js/ui/status/network.js:71
+msgid "<unknown>"
+msgstr "< 不明 >"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:436 js/ui/status/network.js:1370
+#, javascript-format
+msgid "%s Off"
+msgstr "%s オフ"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:439
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s 接続済み"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:444
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s 管理対象外"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:447
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s 切断中"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454 js/ui/status/network.js:1362
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s 接続中"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:457
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s には認証が必要"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:465
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "%s のファームウェア未検出"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:469
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s 利用不可"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:472
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s 接続失敗"
+
+#: js/ui/status/network.js:484
+msgid "Wired Settings"
+msgstr "有線設定"
+
+#: js/ui/status/network.js:531
+msgid "Mobile Broadband Settings"
+msgstr "モバイルブロードバンド設定"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:587 js/ui/status/network.js:1367
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s ハードウェア無効"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:591
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s 無効"
+
+#: js/ui/status/network.js:638
+msgid "Connect to Internet"
+msgstr "インターネットへ接続"
+
+#: js/ui/status/network.js:836
+msgid "Airplane Mode is On"
+msgstr "機内モードオン"
+
+#: js/ui/status/network.js:837
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "機内モードがオンになっていると Wi-Fi は無効になります。"
+
+#: js/ui/status/network.js:838
+msgid "Turn Off Airplane Mode"
+msgstr "機内モードをオフにする"
+
+#: js/ui/status/network.js:847
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi オフ"
+
+#: js/ui/status/network.js:848
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "ネットワークに接続するには Wi-Fi をオンにする必要があります。"
+
+#: js/ui/status/network.js:849
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi をオンにする"
+
+#: js/ui/status/network.js:877
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi ネットワーク"
+
+#: js/ui/status/network.js:881
+msgid "Select a network"
+msgstr "ネットワークを選択してください"
+
+#: js/ui/status/network.js:917
+msgid "No Networks"
+msgstr "ネットワークなし"
+
+#: js/ui/status/network.js:942 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "オフにハードウェアスイッチを使用する"
+
+#: js/ui/status/network.js:1253
+msgid "Select Network"
+msgstr "ネットワークを選択"
+
+#: js/ui/status/network.js:1259
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi 設定"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1358
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s アクセスポイント使用中"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1373
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s 未接続"
+
+#: js/ui/status/network.js:1473
+msgid "connecting…"
+msgstr "接続中…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1476
+msgid "authentication required"
+msgstr "認証が必要"
+
+#: js/ui/status/network.js:1478
+msgid "connection failed"
+msgstr "接続失敗"
+
+#: js/ui/status/network.js:1524
+msgid "VPN Settings"
+msgstr "VPN 設定"
+
+#: js/ui/status/network.js:1541
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1551
+msgid "VPN Off"
+msgstr "VPN オフ"
+
+#: js/ui/status/network.js:1612 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "ネットワーク設定"
+
+#: js/ui/status/network.js:1640
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "有線接続 %s 件"
+
+#: js/ui/status/network.js:1644
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "Wi-Fi 接続 %s 件"
+
+#: js/ui/status/network.js:1648
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "モデム接続 %s 件"
+
+#: js/ui/status/network.js:1792
+msgid "Connection failed"
+msgstr "接続失敗"
+
+#: js/ui/status/network.js:1793
+msgid "Activation of network connection failed"
+msgstr "ネットワーク接続を有効にできません"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "夜間モード オフ"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "夜間モード オン"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "再開"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "明日まで無効にする"
+
+#: js/ui/status/power.js:51 js/ui/status/powerProfiles.js:57
+msgid "Power Settings"
+msgstr "電源設定"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr "充電完了"
+
+#: js/ui/status/power.js:74
+msgid "Not Charging"
+msgstr "充電していません"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:77 js/ui/status/power.js:83
+msgid "Estimating…"
+msgstr "残量推計中…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "残り %d:%02d (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:97
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "充電完了まで %d:%02d (%d %%)"
+
+#. The icon label
+#: js/ui/status/power.js:145
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/powerProfiles.js:19
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "パフォーマンス"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "バランス"
+
+#: js/ui/status/powerProfiles.js:21
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "省電力"
+
+#: js/ui/status/remoteAccess.js:43
+msgid "Screen is Being Shared"
+msgstr "画面を共有しています"
+
+#: js/ui/status/remoteAccess.js:45
+msgid "Turn off"
+msgstr "オフにする"
+
+#: js/ui/status/remoteAccess.js:149
+msgid "Stop Screencast"
+msgstr "スクリーンキャストを停止"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "機内モードオン"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "ロック"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "電源オフ / ログアウト"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "サスペンド"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "再起動…"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "電源オフ…"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "ログアウト"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "ユーザーを切り替え…"
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "不明な Thunderbolt デバイス"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"離席中に新しいデバイスを検出しました。そのデバイスを使用する場合は一度取り外"
+"し、再接続してください。"
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr "認証していない Thunderbolt デバイス"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "新しいデバイスを検出しました。管理者の認証が必要です。"
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt 認証エラー"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Thunderbolt デバイスを認証できませんでした: %s"
+
+#: js/ui/status/volume.js:158
+msgid "Volume changed"
+msgstr "音量変更しました"
+
+#: js/ui/status/volume.js:220
+msgid "Volume"
+msgstr "音量"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "画面を複製"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "画面を拡張"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "外部のみ"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "組み込みのみ"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "ロック解除"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "別のユーザーでログイン"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "GNOME %s へようこそ"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "“ツアー”アプリで詳しく知ることができます。"
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "いいえ結構です"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "“ツアー”を始める"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s”の準備ができました"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "このディスプレイ設定を保存しますか?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "設定を元に戻す"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "変更を保存"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "%d 秒後に元の設定に戻ります"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:553
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "スクリーンショットを撮る"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "隠す"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "元に戻す"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "最大化"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "移動"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "サイズを変更"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "画面上でタイトルバーを移動する"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "最前面に維持する"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "すべてのワークスペースに表示する"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "左側のワークスペースへ移動する"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "右側のワークスペースへ移動する"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "上側のワークスペースへ移動する"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "下側のワークスペースへ移動する"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "上側のモニターへ移動する"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "下側のモニターへ移動する"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "左側のモニターへ移動する"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "右側のモニターへ移動する"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "閉じる"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution カレンダー"
+
+#: src/main.c:419 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "バージョンを表示する"
+
+#: src/main.c:425
+msgid "Mode used by GDM for login screen"
+msgstr "ログイン画面で GDM が使用するモード"
+
+#: src/main.c:431
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "指定したモードを使用する (例: ログイン画面用の“gdm”)"
+
+#: src/main.c:437
+msgid "List possible modes"
+msgstr "使用可能なモードを一覧表示する"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "不明なプログラム"
+
+#: src/shell-app.c:569
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s”の起動に失敗しました"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "パスワードが一致しません。"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "パスワードを空白にすることはできません。"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "認証ダイアログはユーザーに拒否されました"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:209
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "拡張機能"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:210
+msgid "Manage your GNOME Extensions"
+msgstr "GNOME 拡張機能を管理します"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "The GNOME Project"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"“拡張機能”は GNOME Shell 拡張機能の設定、更新、削除、不要な拡張機能の無効化を"
+"行うことができます。"
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Shell 拡張機能の設定を行います"
+
+#: subprojects/extensions-app/js/main.js:131
+#: subprojects/extensions-app/js/main.js:142
+msgid "No Matches"
+msgstr "一致なし"
+
+#: subprojects/extensions-app/js/main.js:170
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "“%s”を削除しますか?"
+
+#: subprojects/extensions-app/js/main.js:171
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"拡張機能を削除した場合、再度有効にするにはダウンロードし直す必要があります"
+
+#: subprojects/extensions-app/js/main.js:175
+msgid "Remove"
+msgstr "削除"
+
+#: subprojects/extensions-app/js/main.js:208
+msgid "translator-credits"
+msgstr ""
+"小山田 純 <oyamadajun@outlook.jp>\n"
+"Hideki Yamane <henrich@debian.org>\n"
+"Ikuya Awashiro <ikuya@fruitsbasket.info>\n"
+"IWAI, Masaharu <iwaim.sub@gmail.com>\n"
+"Jiro Matsuzawa <jmatsuzawa@gnome.org>\n"
+"Kiyotaka NISHIBORI <ml.nishibori.kiyotaka@gmail.com>\n"
+"Nishio Futoshi <fut_nis@d3.dion.ne.jp>\n"
+"sicklylife <translation@sicklylife.jp>\n"
+"Takanori MATSUURA <t.matsuu@gmail.com>\n"
+"Takayuki KUSANO <AE5T-KSN@asahi-net.or.jp>\n"
+"Yoji TOYODA <bsyamato@sea.plala.or.jp>"
+
+#: subprojects/extensions-app/js/main.js:336
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d 個の拡張機能を次回ログイン時に更新します。"
+
+#: subprojects/extensions-app/js/main.js:478
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "現在の GNOME のバージョンと互換性のない拡張機能です"
+
+#: subprojects/extensions-app/js/main.js:481
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "拡張機能にエラーがありました"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "拡張機能をアップデートできます"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "ウェブサイト"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "削除…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "ヘルプ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "“拡張機能”について"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "手動でインストール"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"拡張機能の検索や追加は <a href=\"https://extensions.gnome.org\">extensions."
+"gnome.org</a> で行うことができます。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "組み込み"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "インストールされた拡張機能はありません"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"大変申し訳ありませんが、インストール済み拡張機能一覧を取得できませんでした。"
+"GNOME にログインしていることを確認した上で再試行してください。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "拡張機能の更新準備完了"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "ログアウト…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "名前"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "説明"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "新しい拡張機能を作成します"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "不明な引数です"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "GNOME Shell への接続に失敗しました\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "“%s”拡張機能は存在しません\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "拡張機能を無効にします"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "拡張機能を有効にします"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "“%s”拡張機能は存在しません\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "拡張機能の情報を表示します"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "既存の拡張機能を上書きする"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "ユーザーがインストールした拡張機能を表示する"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "システムにインストールされた拡張機能を表示する"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "有効化された拡張機能を表示する"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "無効化された拡張機能を表示する"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "設定画面がある拡張機能を表示する"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "アップデートがある拡張機能を表示する"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "拡張機能の詳細を表示する"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "インストールされた拡張機能一覧を表示します"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "ソースディレクトリが複数指定されています"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "“%s”拡張機能には設定がありません\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "拡張機能の設定を開きます"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "拡張機能をリセットします"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "システムの拡張機能はアンインストールできません\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "“%s”のアンインストールに失敗しました\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "拡張機能をアンインストールします"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "パス"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "オリジナルの作者"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "バージョン"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "状態"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version”は引数を取りません"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "使用法:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "バージョン情報を表示して終了します。"
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[引数…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "コマンド:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "ヘルプを表示する"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "拡張機能を有効にする"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "拡張機能を無効にする"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "拡張機能をリセットする"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "拡張機能をアンインストールする"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "拡張機能一覧を表示する"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "拡張機能の情報を表示する"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "拡張機能の設定を開く"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "拡張機能を作成する"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "“%s”を使用すると詳細なヘルプが表示されます。\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "インジケーター"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "トップバーにアイコンを追加します"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "出力数: %u"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "入力数: %u"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "システムのサウンド"
+
+#~ msgid "Unnamed Folder"
+#~ msgstr "名前なしのフォルダー"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "お気に入りから削除"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "お気に入りに追加"
+
+#~ msgid "Screen Lock disabled"
+#~ msgstr "画面ロックが無効です"
+
+#~ msgid "Screen Locking requires the GNOME display manager."
+#~ msgstr "画面ロックには GNOME ディスプレイマネージャーが必要です。"
+
+#~ msgid "%A %B %-d"
+#~ msgstr "%B%-e日 (%a)"
+
+#~ msgid "Swipe up to unlock"
+#~ msgstr "ロック解除は上にスワイプしてください"
+
+#~ msgid "Click or press a key to unlock"
+#~ msgstr "ロック解除はキーを押すかクリックしてください"
+
+#~ msgid "Author"
+#~ msgstr "作者"
+
+#~ msgid "Warning"
+#~ msgstr "警告"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "拡張機能は、システムに何らかの問題 (パフォーマンスへの影響を含む) を引き起"
+#~ "こす場合があります。システムに問題が発生した場合は、すべての拡張機能を無効"
+#~ "にすることをお勧めします。"
+
+#~ msgid "App Picker View"
+#~ msgstr "アプリ一覧画面"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "現在のアプリケーション一覧画面のインデックスです。"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "イントロスペクション API を有効にする"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Shell のアプリケーション状態をイントロスペクトできる D-Bus API を有効にし"
+#~ "ます。"
+
+#~ msgid "Minimize"
+#~ msgstr "最小化"
+
+#~ msgid "Unmaximize"
+#~ msgstr "元のサイズに戻す"
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "GNOME Shell への接続に失敗しました"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "よく使用するアプリケーションがここに表示されます"
+
+#~ msgid "Frequent"
+#~ msgstr "常用"
+
+#~ msgid "All"
+#~ msgstr "すべて"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%B%-e日 (%a)"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%Y年%B%-e日 (%a)"
+
+#~ msgid "Copy Error"
+#~ msgstr "エラーをコピー"
+
+#~ msgid "Off"
+#~ msgstr "オフ"
+
+#~ msgid "On"
+#~ msgstr "オン"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d 件の新しいメッセージ"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d 件の新しい通知"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "無線ネットワークでは認証が要求されます"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "使いたいキーボードの種類"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "使いたいキーボードの種類です。"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "暦週日付を表示"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "ISO 8601 方式の暦週日付を表示します。"
+
+#~ msgid "Not In Use"
+#~ msgstr "未使用"
diff --git a/po/ka.po b/po/ka.po
new file mode 100644
index 0000000..10cfb55
--- /dev/null
+++ b/po/ka.po
@@ -0,0 +1,3076 @@
+# Georgian translation for gnome-shell
+# Copyright (C) 2022, gnome-shell authors
+# This file is distributed under the same license as the gnome-shell package.
+# Ekaterine Papava <papava.e@gtu.ge>, 2023
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 07:18+0000\n"
+"PO-Revision-Date: 2023-02-14 08:28+0100\n"
+"Last-Translator: Ekaterine Papava <papava.e@gtu.ge>\n"
+"Language-Team: Georgian <(nothing)>\n"
+"Language: ka\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 3.2.2\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "გამშვებები"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "#1 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "#2 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "#3 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "#4 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "#5 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "#6 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "#7 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "#8 რჩეული აპლიკაციის აქტივაცია"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "#9 რჩეული აპლიკაციის აქტივაცია"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "ეკრანის ანაბეჭდები"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "ეკრანის ანაბეჭდის ინტერაქტიული გადაღება"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "ეკრანის ანაბეჭდის გადაღება"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "ფანჯრის ანაბეჭდის გადაღება"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "ეკრანიდან ვიდეოს ჩაწერა"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "სისტემა"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "გაფრთხილებების სიის ჩვენება"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "აქტიური გაფრთხილების ჩვენება"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "გადახედვის ჩვენება"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "ყველა აპლიკაციის ჩვენება"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "აპლიკაციების მენიუს გახსნა"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME-ის გარსი"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "ფანჯრების მართვა და აპლიკაციების გაშვება"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "პროგრამისტებისთვის საჭირო ხელსაწყოების გამოჩენა Alt-F2-ში"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "პროგრამისტებისთვის საჭირო ხელსაწყოების Alt-F2-ში გამოჩენის ჩართვა."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "ჩასართავი დამატებების UUID-ები"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME-ის გარსის გაფართოებებს UUID თვისება გააჩნიათ. ეს ცვლადი აღწერს "
+"გაფართოებას, რომელიც უნდა ჩაიტვირთოს. ნებისმიერი გაფართოება, რომელსაც სურს, "
+"რომ ჩაიტვირთოს, ეს სია უნდა ჰქონდეს. ამ სიის ცვლილება შესაძლებელია D-Bus-ის "
+"EnableExtension და DisableExtension მეთოდებით org.gnome.shell-ზე."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "გასათიში დამატებების UUID-ები"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "მომხმარებლის გაფართოებების გათიშვა"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"მომხმარებლის მიერ ჩართული ყველა გაფართოების გამორთვა \"enabled-extensions\" "
+"პარამეტრის შეხების გარეშე."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "გაფართოების ვერსიის თავსებადობის გადამოწმების გამორთვა"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "რჩეული აპლიკაციების სამუშაო მაგიდის ფაილის ID-ების სია"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "ამ იდენტიფიკატორების შესაბამისი აპლიკაციები რჩეულ სიაში გამოჩნდება."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "ისტორია ბრძანების (Alt-F2) ფანჯრისთვის"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "გამადიდებელი ლინზის ფანჯრის ისტორია"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "მომხმარებლის მენიუში \"გასვლის\" ყოველთვის ჩვენება."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "დავიმახსოვრო თუ არა დაშიფრული ან დაშორებული ფაილურ სისტემის პაროლები"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "ბოლოს არჩეული არა-ნაგულისხმები კვების პროფილი"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "ბოლო ვერსია, რომლისთვისაც \"მოგესალმებათ GNOME\" ფანჯარა იყო ნაჩვენები"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "აპლიკაციის ამრჩევის განლაგება"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "კლავიატურის მალსახმობი აპლიკაციის მენიუს გასახსნელად"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "კლავიატურის მალსახმობი აპლიკაციის მენიუს გასახსნელად."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "კლავიატურის მალსახმობი მდგომარეობებს შორის გადასართავად"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"კლავიატურის მალსახმობი სესიებს, ფანჯრებსა და აპლიკაციის ბადეს შორის "
+"გადართავად"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"კლავიატურის მალსახმობი აპლიკაციის ბადეს, ფანჯრებსა და სესიებს შორის "
+"გადასართავად"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "კლავიატურის მალსახმობი \"აპლიკაციების ჩვენების\" ხედის გასახსნელად"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"კლავიატურის მალსახმობი \"აპლიკაციების ჩვენების\" ხედის გასახსნელად "
+"ქმედებების მიმოხილვით."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "კლავიატურის მალსახმობი მიმოხილვის გასახსნელად"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "კლავიატურის მალსახმობი ქმედებების გადასახედად."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "კლავიატურის მალსახმობი გაფრთხილებების სიის ხილვადობის ჩართ/გამორთ"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "კლავიატურის მალსახმობი გაფრთხილებების სიის ხილვადობის ჩართ/გამორთ."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "კლავიატურის მალსახმობი აქტიურ გაფრთხილებაზე გადასართავად"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "კლავიატურის მალსახმობი აქტიურ გაფრთხილებაზე გადასართავად."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "#1 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "#2 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "#3 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "#4 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "#5 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "#6 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "#7 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "#8 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "#9 აპლიკაციაზე გადართვა"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "გადამრთველის მიმდინარე სამუშაო სივრცეზე შეზღუდვა."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "აპლიკაციის ხატულების რეჟიმი."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"გადამრთველში ფანჯრების გარეგნობის მორგება. შესაძლო ვარიანტების \"მხოლოდ "
+"მინიატურები\", \"მხოლოდ აპლიკაციის ხატულები\" ან \"ორივე\"."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "მდებარეობები"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "საათის ჩვენების მდებარეობა"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "ავტომატური მდებარეობა"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "გამოვითხოვო თუ არა მდებარეობა"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "მდებარეობა"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "მდებარეობა ამინდის პროგნოზის საჩვენებლად"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "მოდალური ფანჯრის მშობელ ფანჯარაზე მიმაგრება"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"ეს გასაღები გადაფარავს org.gnome.mutter-ის გასაღებს, როცა GNOME-ის გარსია "
+"გაშვებული."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "სამუშაო სივრცეები დინამიკურად იმართება"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "სამუშაო მაგიდები მხოლოდ აქტიურ ეკრანზე"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "ფოკუსის შეცვლის მაჩვენებლის მოძრაობის შეწყვეტამდე დაყოვნება"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "ქსელური შესვლა"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "რაღაც გაფუჭდა"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"სამწუხარო პრობლემა: ამ გაფართოების პარამეტრების ჩვენება შეუძლებელია. გირჩევთ "
+"მიმართოთ გაფართოების ავტორებს,."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "ტექნიკური დეტალები"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Homepage"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "გაფართოებების ვებგვერდის გახსნა"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "გაუქმება"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "პაროლი"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "აირჩიეთ სესია"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "არაა სიაში?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(მაგ, user ან %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "მომხმარებლის სახელი"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "შესვლის ფანჯარა"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "ავთენტიკაციის შეცდომა"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(ან გადაუსვით თითი წამკითხველს)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(ან მოათავსეთ თითი წამკითხველზე)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "გამორთვა"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "გადატვრთვა"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "ეკრანის დაბლოკვა"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "გასვლა"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "ძილი"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "მომხმარებლის გადართვა"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "მომხმარებლის გადართვა"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "lock orientation;unlock orientation;screen;rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "ეკრანის ანაბეჭდის გადაღება"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "screenshot;screencast;snip;capture;record"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "ეკრანის ტრიალის განბლოკვა"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "ეკრანის ტრიალის დაბლოკვა"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "ბრძანება ვერ ვიპოვე"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "ბრძანების ანალიზის შეცდომა:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "%s-ის გაშვების შეცდომა:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "ახლა"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d წუთის წინ"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d საათის წინ"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "გუშინ"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d დღის წინ"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d კვირის წინ"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d თვის წინ"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d წლის წინ"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "გუშინ, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "გუშინ, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "ჰოტსპოტზე შესვლა"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "წვდომის მინიჭება"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "უარი წვდომაზე"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "უსახელო საქაღალდე"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s მიმაგრებულია."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s ჩამოხსნილია."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "ფანჯრების გახსნა"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "ახალი ფანჯარა"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "დეტალების ჩვენება"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "გასვლა"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "ჩამოხსნა"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "მიმაგრება"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "ჩაშენებული ვიდეობარათით გაშვება"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "დისკრეტული ვიდეობარათით გაშვება"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "აირჩიეთ აუდიომოწყობილობა"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "ხმის მორგება"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "ყურსაცვამები"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "ყურსაცვამები & მიკროფონი"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "მიკროფონი"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "ფონის შეცვლა…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "ეკრანის მორგება"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "მორგება"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "კ"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "მ"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "ტერა"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "სიგ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "ტერა"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "პ"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "კ"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "წინა თვე"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "შემდეგი თვე"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V კვირა"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "შეტყობინებების გარეშე"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "არ შემაწუხო"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "გასუფთავება"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "%s არ პასუხობს."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "ძალით გასვლა"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "მოცდა"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "გარე საცავი მიერთებულია"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "გარე საცავი გათიშულია"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "საცავის განბლოკვის შეცდომა"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Udisks-ის დაყენებულ ვერსიას PIM პარამეტრების მხარდაჭერა არ გააჩნია"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s-ით გახსნა"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "დაკავშირება ასევე შეგიძლიათ რაუტერზე \"WPS\" ღილაკზე დაწოლით."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "დაკავშირება"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "გასაღები"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "პირადი გასაღების პაროლი"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "იდენტიფიკაცია"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "სერვისი"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "საჭროა ავთენტიკაცია"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"უსადენო ქსელთან (%s) წვდომისთვის საჭიროა პაროლები ან დაშიფვრის გასაღებები."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "802.1X ავთენტიკაცია მავთულზე"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "ქსელის სახელი"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL ავთენტიკაცია"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "საჭიროა PIN კოდი"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "მობილური კავშირის მოწყობილობას სჭირდება PIN კოდი"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN კოდი"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "%s-თან დასაკავშირებლად საჭიროა პაროლი."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "ქსელის მმართველი"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN-ის პაროლი"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "საჭროა ავთენტიკაცია"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "ადმინისტრატორი"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "ავთენტიფიკაცია"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "უკაცრავად, არ იმუშავა. კიდევ სცადეთ."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s-ს ახლა ჰქვია %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "ფანჯრები"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "აპლიკაციების ჩვენება"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "პანელი"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "დღეს"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "ხვალ"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "მთელი დღე"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "ღონისძიებების გარეშე"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "მსოფლიო საათების დამატება…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "მსოფლიო საათები"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "იტვირთება…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "ამინდის ინფორმაციისთვის საჭიროა ინტერნეტი"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "ამინდის ინფორმაცია ხელმიუწვდომელია"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "ამინდი"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "აირჩიეთ ამინდის მდებარეობა…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s-დან გასვლა"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "გასვლა"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s გამოვა ავტომატურად, %d წამში."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "ავტომატური გამოსვლა %d წამში."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "გასვლა"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "გამორთვა"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "განახლებების დაყენება და გამორთვა"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "სისტემა გამოირთვება ავტომატურად %d წამში."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "დარჩენილი განახლებების დაყენება"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "გამორთვა"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "გადატვრთვა"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "განახლებების დაყენება და გადატვირთვა"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "სისტემა გადაიტვირთება ავტომატურად %d წამში."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "გადატვრთვა"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "სისტემა გადაიტვირთება და განახლდება"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "სისტემა გადაიტვირთება და განახლდება %d წამში."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "გადატვირთვა &amp; დაყენება"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "განახლებების დაყენება და გამორთვა"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "გამორთვა განახლებების დაყენების შემდეგ"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "გადატვირთვა და განახლება"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s დაყენებული იქნება გადატვირთვის შემდეგ. განახლებების დაყენებას საკმაოდ "
+"დიდი დრო შეიძლება დასჭირდეს. დარწმუნდით, რომ გაქვთ მონაცემების მარქაფი და "
+"კომპიუტერი შეერთებულია ელ-წყაროში."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"ელემენტი თითქმის დამჯდარია. განახლებების დაყენებამდე გთხოვთ შეართოთ კვების "
+"კაბელი."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "ზოგიერთი აპლიკაცია დაკავებულია ან აქვს შეუნახავი ცვლილებები"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "სხვა მომხმარებლებიც არიან შემოსული"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "ჩატვირთვის მორგება"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (დაშორებული)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (კონსოლიდან)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "დაყენება"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "გაფართოების დაყენება"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "გადმოვწერო და დავაყენო %s extensions.gnome.org-დან?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "ხელმისაწვდომია გაფართოების განახლება"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "გაფართოების განახლება მზადაა დასაყენებლად."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "მალსახმობების გათიშვის ნების დართვა"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "აპლიკაცია %s-ს უნდა მალსახმობების გათიშვა"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "აპლიკაციას უნდა მალსახმობების გათიშვა"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "მალსახმობების აღდგენა შეგიძლიათ %s-ზე დაწოლით."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "უარყოფა"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "დაშვება"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "ნელი ღილაკები ჩართულია"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "ნელი ღილაკები გამორთულია"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"თქვენ Shift ღილაკს 8 წამით დააჭირეთ. ეს ნელი კლავიატურის თვისების "
+"მალსახმობია, რომელიც განსაზღვრავს, როგორ მუშაობს თქვენი კლავიატურა."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "წებოვანი ღილაკები ჩართულია"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "წებოვანი ღილაკები გამორთულია"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"თქვენ Shift ღილაკს 5 წამით დააჭირეთ. ეს წებოვანი კლავიატურის თვისების "
+"მალსახმობია, რომელიც განსაზღვრავს, როგორ მუშაობს თქვენი კლავიატურა."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"თქვენ დააჭირეთ ერთდროულად ორ ღილაკს ან ზედიზედ 5-ჯერ დააჭირეთ Shift ღილაკს, "
+"ეს გამორთავს წებოვანი კლავიატურის თვისებას, რომელიც განსაზღვრავს, როგორ "
+"მუშაობს თქვენი კლავიატურა."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "ჩართული დატოვება"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "ჩართვა"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "გამორთვა"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "გამორთული დატოვება"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "ქვეყნისა და ენის მორგება"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "გაფართოებები დაყენებული არაა"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s-მა შეცდომა არ დააბრუნა."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "შეცდომების დამალვა"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "შეცდომების ჩვენება"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "ჩაირთო"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "გამოირთო"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "შეცდომა"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "ვადაგასული"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "გადმოწერა"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "წყაროს ნახვა"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "ვებ გვერდი"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "სისტემა უსაფრთხო რეჟიმზე გადაერთო"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "ახლა აპლიკაციებს განუსაზღვრელი წვდომა აქვთ"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "ბრძანების გაუქმება"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "შეხვედით, როგორც პრივილეგირებული მომხმარებელი"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"სესიის პრივილეგირებული მომხმარებლით გაშვება უსაფრთხოების მიზნით თავიდან უნდა "
+"აირიდოთ. თუ ეს შესაძლებელია, ჩვეულებრივი მომხმარებლით შედით."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "ეკრანის ჩაკეტვა გათიშულია"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "ეკრანის ჩაკეტვას GNOME-ის ეკრანის მმართველი სჭირდება."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "ინფორმაცის სისტემის შესახებ"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "უცნობი შემსრულებელი"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "უცნობი სათაური"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "მოსაძებნად აკრიფეთ"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "აპლიკაციები"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "მიმოხილვა"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "ახალი მალსახმობი…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "აპლიკაციის განსაზღვრულია"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "ეკრანზე ნაჩვენები დახმარება"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "ეკრანის შეცვლა"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "ღილაკების თანამიმდევრობის მინიჭება"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "დასრულდა"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "ჩასწორება…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "არაფერი"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "მოსარგებად დააწექით ღილაკს"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "დასახურად დააწექით ESC-ს"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "გასასვლელად დააჭირეთ ნებისმიერ ღილაკს"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "ქმედებები"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "სისტემა"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "ზედა ზოლი"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "ბრძანების გაშვება"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "დასახურად დააწექით ESC-ს"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Wayland-ში გადატვირთვა მიუწვდომელია"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "გადატვირთვა…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME -ს სჭირდება თქვენი ეკრანის ჩაკეტვა"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "ჩაკეტვის შეცდომა"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "ჩაკეტვა დაბლოკილია აპლიკაციის მიერ"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "მონიშნული"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "მონიშნული მონაკვეთი"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "ეკრანი"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "ეკრანის არჩევანი"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "ფანჯარა"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "ფანჯრის არჩევანი"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "ეკრანის ანაბეჭდი / ჩანაწერი"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "კურსორის ჩვენება"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "ეკრანის ვიდეოჩანაწერი"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "ეკრანის ვიდეოჩანაწერი %d %t-დან.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "ეკრანის ანაბეჭდი"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "ეკრანის ვიდეოჩანაწერი დასრულებულია"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "ვიდეოს სანახავად დააწკაპუნეთ აქ."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "ფაილებში ჩვენება"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "ეკრანის ანაბეჭდი %s-დან"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "ეკრანის ანაბეჭდი დათრეულია"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "შეგიძლიათ მიმოცვლის ბაფერიდან ჩასვათ."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "ეკრანის ანაბეჭდი აღებულია"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "ძებნა…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "შედეგების გარეშე."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "კიდევ %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "ძებნა"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "კოპირება"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "ჩასმა"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "ტექსტის ჩვენება"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "ტექსტის დამალვა"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "CapsLock ჩართულია."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "დამალული საცავი"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows-ის სისტემური საცავი"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "მოხმარებლის გასაღების ფაილები"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"საცავების, რომლებიც იყენებენ გასაღებებს, განსაბლოკად გამოიყენეთ <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-ის ნომერი"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "პაროლის დამასოვრება"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "განბლოკვა"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "გახსენი %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM უნდა იყოს ან რიცხვი, ან არაფერი."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s-ის გაშვების შეცდომა"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "აპლიკაცია %s ვერ ვიპოვე"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "_დამხმარე საშუალებები"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "მაღალი კონტრასტი"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "გადიდება"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "ეკრანის მკითხველი"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "კლავიატურა ეკრანზე"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "ვიზუალური გართხილებები"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "წებოვანი ღილაკები"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "ნელი ღილაკები"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "მხტუნავი ღილაკები"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "თაგუნას ღილაკები"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "დიდი ტექსტი"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "ავტომატური მობრუნება"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "სიკაშკაშე"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "მუქი რეჟიმი"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "ერთი წკაპი"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "ორმაგი წკაპი"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "გადათრევა"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "დამატებით წკაპი"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "დიდხნიანია წკაპი"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "კლავიატურა"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "კლავიატურის განლაგების ჩვენება"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "მდებარეობის პოვნის წვდომა"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "აპლიკაციას %s უნდა თქვენი მდებარეობის გაგება"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"მდებარეობაზე წვდომა შეიძლება შეიცვალოს ნებისმიერ დროს კონფიდენციალურობის "
+"პარამეტრებიდან."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<უცნობი>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "გათიშვა %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "%s-სთან დაკავშირება"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s Hotspot-ი"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "VPN-ის მორგება"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "ყველა ქსელი"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "მავთულიანი კავშირები"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "მავთულით მიერთების მორგება"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "ბლუთუზით მიბმა"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Bluetooth-ის მორგება"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "მობილური კავშირები"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "მობილური კავშირის მორგება"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "დაკავშირება ვერ მოხერხდა"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "ქსელის კავშირის აქტივაციის შეცდომა"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "ღამის სინათლე"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "წარმადობა"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "ბალანსირებული"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "ელემენტის შენახვის რეჟიმი"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "კვების პროფილები"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "ეკრანის ვიდეოჩანაწერის დასრულება"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "ეკრანის გაზიარების შეწყვეტა"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "თვითმფრინავის რეჟიმი"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "ეკრანის ანაბეჭდის გადაღება"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "გამორთვის მენიუ"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "ძილი"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "გადატვირთვა…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "გამორთვა…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "გასვლა…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "მომხმარებლის გადართვა…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "ეკრანის ჩაკეტვა"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Thunderbolt-ის უცნობი მოწყობილობა"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"სანამ გასული ბრძანდებოდით, აღმოჩენილია ახალი მოწყობილობები. გამოსაყენებლად "
+"ჯერ გამოაერთეთ ისინი, შემდეგ კი თავიდან მიაერთეთ."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "უცნობი Thunderbolt მოწყობილობა"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"აღმოჩენილია ახალი მოწყობილობა და ადმინისტრატორის მიერ ავტორიზებული უნდა იყოს."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt მოწყობილობის ავტორიზაციის შეცდომა"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Thunderbolt მოწყობილობასთან ავტორიზაციის შეცდომა: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "საცავი შეიცვალა"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "ტომი"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "ხმის გამოტანა"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "ხმის შეყვანა"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "სარკე"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "ეკრანების შეერთება"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "მხოლოდ გარე"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "მხოლოდ ჩაშენებული"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "გადაუსვით ბლოკის მოსახსნელად"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "ბლოკის მოსახსნელად წკაპი ან ღილაკს დააწექით"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "ფანჯრის განბლოკვა"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "სხვა მომხმარებლით შესვლა"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "მოგესალმებათ GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "თუ გნებავთ, ისწავლოთ, დაიწყეთ ჩვენი ტურით."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "არა, მადლობა"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "ტურის აღება"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "\"%s\" მზადაა"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "დაიტოვებთ ამ პარამეტრებს?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "პარამეტრების დაბრუნება"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "ცვლილებების დატოვება"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "კონფიგურაცია დაბრუნდება %d წამში"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "დამალვა"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "აღდგენა"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "გადიდება"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "გადაადგილება"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "ზომის შეცვლა"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "სათაურის ზოლის ეკრანზე გადატანა"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "ყოველთვის ყველაზე ზემოთ"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "ყოველთვის ხილულ სამუშაო სივრცეში"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "მარცხენა სამუშაო სივრცეში გადატანა"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "მარჯვენა სამუშაო სივრცეში გადატანა"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "ზედა სამუშაო სივრცეში გადატანა"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "ქვედა სამუშაო სივრცეში გადატანა"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "ეკრანის მაღლა ატანა"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "ეკრანის დაბლა ჩამოტანა"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "ეკრანის მარცხნივ გატანა"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "ეკრანის მარჯვნივ გატანა"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "დახურვა"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution-ის კალენდარი"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "მიმდინარე ვერსიის დაბეჭდვა"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "რეჟიმი, რომელსაც GDM შემოსვლის ეკრანისთვის იყენებს"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "შესვლის ეკრანის რეჟიმის მითითება. მაგ: \"gdm\""
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "შესაძლო რეჟიმების სია"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Unknown"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "%s-ის გაშვების შეცდომა"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "პაროლები არ ემთხვევა."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "პაროლები ცარიელი ვერ იქნება"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ავთენტიკაციის ფანჯარა დახურულია მომხმარებლის მიერ"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "გაფართოებები"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "GNOME-ის თქვენი გაფართოებების მართვა"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "პროექტი \"GNOME\""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME-ის გარსის გაფართოებების მორგება"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "დათხვევა არ არსებობს"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "წავშალო %s?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"თუ ამ გაფართოებას წაშლით, მის თავიდან ჩასართავად გადმოწერებზე დაბრუნება "
+"მოგიწევთ"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "წაშლა"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "თარგმანი-შესრულებულია"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d გაფართოება განახლდება შემდეგი შემოსვლისას."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "ეს გაფართოება GNOME-ის მიმდინარე ვერსიასთან შეუთავსებელია"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "გაფართოების შეცდომა"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "გაფართოება შეიძლება განახლდეს"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "ვებგვერდი"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "წაშლა…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "დახმარება"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "გაფართოებების შესახებ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "ხელით დაყენებული"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"გაფართოებების საპოვნელად და დასამატებლად ეწვიეთ ვებგვერდს <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "ჩაშენებული"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "არცერთი გაფართოება დაყენებული არაა"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"სამწუხაროდ დაყენებული გაფართოებების სიის მიღება შეუძლებელია. დარწმუნდით, რომ "
+"შეხვედით GNOME-ში და თავიდან სცადეთ."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "გაფართოების განახლება მზადაა"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "%s-ში ახალი გაფართოება წარმატებით შეიქმნა.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"სახელები ძალიან მოკლე უნდა იყოს (აღწერა).\n"
+"მაგალითები: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "სახელი"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"აღწერა ერთწინადადებიანი აღწერაა, რას აკეთებს ეს გაფართოება.\n"
+"მაგალითად: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "აღწერა"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID გლობალურად-უნიკალური იდენტიფიკატორია თქვენი გაფართოებისთვის.\n"
+"უნდა იყოს ელფოსტის მისამართის ფორმატში (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "აირჩიეთ ერთერთი ხელმისაწვდომის შაბლონი:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "ნიმუში"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "ახალ გაფართოებების UID"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "სახელი"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "ახალი გაფართოების სასიამოვნოდ დასანახი სახელი"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "აღწერა"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "ამ გაფართოების მოკლე აღწერა"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ნიმუში"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "ახალ გაფართოებებთან გამოსაყენებელი შაბლონი"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "გაფართოების ინფორმაციის ინტერაქტიურად შეყვანა"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "ახალი გაფართოების შექმნა"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "უცნობი არგუმენტები"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "საჭიროა UUID, სახელი და აღწერა"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "გაფართოება %s არ არსებობს\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "გაფართოების გამორთვა"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "შეიყვანეთ UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "შეყვანილია ერთზე მეტი UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "გაფართოების ჩართვა"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "GNOME -ის გარსთან დაკავშირების შეცდომა\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "გაფართოება %s არ არსებობს\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "გაფართოების შესახებ ინფორმაციის ჩვენება"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "არსებულ გაფართოების გადაწერა"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "გაფართოების პაკეტი"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "გაფართოების პაკეტის დაყენება"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "გაფართოების პაკეტი მითითებული არაა"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "მითითებულია ერთზე მეტი გაფართოების პაკეტი"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "მომხმარებლის მიერ დაყენებული გაფართოებების ჩვენება"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "სისტემის მიერ დაყენებული გაფართოებების ჩვენება"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "ჩართული გაფართოებების ჩვენება"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "გამორთული გაფართოებების ჩვენება"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "მორგებადი გაფართოებების ჩვენება"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "განახლებადი გაფართოებების ჩვენება"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "გაფართოების შესახებ ინფორმაციის დაბეჭდვა"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "დაყენებული გაფართოებების სია"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ფაილი"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "დამატებით წყაროები ნაკრებში ჩასართავად"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "სქემა"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "GSettings-ის თემა, რომელსაც უნდა შეიცავდეს"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "საქაღალდე"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "საქაღალდე, სადაც თარგმანები მოიპოვება"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "დომენი"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Gettext-ის დომენი თარგმამენისთვის"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "არსებული პაკეტის თავზე გადაწერა"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "საქაღალდე, სადაც პაკეტი შეიქმნება"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "საწყისი_საქაღალდე"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "გაფართოების პაკეტის შექმნა"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "მითითებულია ერთზე მეტი საწყისი საქაღალდე"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "გაფართოებას \"%s\" პარამეტრები არ გააჩნია\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "პარამეტრების გახსნის შეცდომა გაფართოებისთვი \"%s\": %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "გაფართოების მორგების ფანჯარა"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "გაფართოების საწყის მდგომარეობაში დაბრუნება"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "სისტემური გაფართოებების წაშლა შეუძლებელია\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "\"%s\"-ის წაშლის შეცდომა\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "გაფართოების წაშლა"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "არ დაბეჭდო შეცდომები"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "მისამართი"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "საწყისი ავტორი"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "ვერსია"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "მდგომარეობა"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "\"version\"-ს არგუმენტები არ აქვს"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "გამოყენება:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "ვერსიის ინფორმაციის გამოტანა და გასვლა."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "ბრძანება"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[არგუმენტები…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "ბრძანებები:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "დახმარების ჩვენება"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "გაფართოების ჩართვა"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "გაფართოების გამორთვა"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "გაფართოების საწყის მდგომარეობაში დაბრუნება"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "გაფართოების წაშლა"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "გაფართოებების სია"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "გაფართოების შესახებ ინფორმაციის ჩვენება"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "გაფართოების მორგება"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "გაფართოების შექმნა"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "გაფართოების პაკეტად გადაქცევა"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "გაფართოების პაკეტის დაყენება"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "დეტალური დახმარების მისაღებად გამოიყენეთ %s\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "სუფთა"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "ცარიელი გაფართოება"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "ინდიკატორი"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "ზედა ზოლში ხატულის დამატება"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u გამოტანა"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u შეტანა"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "სისტემური ხმები"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "აქვს თუ არა Bluetooth-ის ნაგულისხმევ ადაპტერს ერთი მოწყობილობა მაინც"
+
+#~ msgid "Log Out"
+#~ msgstr "გასვლა"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d მიერთებული"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth ჩართულია"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth გამორთულია"
+
+#~ msgid "Location Enabled"
+#~ msgstr "მდებარეობის პოვნა ჩართულია"
+
+#~ msgid "Disable"
+#~ msgstr "გამორთვა"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "პირადი კონფიგურაცია"
+
+#~ msgid "Location In Use"
+#~ msgstr "მდებარეობის პოვნა გამოიყენება"
+
+#~ msgid "Location Disabled"
+#~ msgstr "მდებარეობის პოვნა გამორთულია"
+
+#~ msgid "Enable"
+#~ msgstr "ჩართვა"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s გამორთული"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s მიერთებული"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s უმართავი"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s ერთდება"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s-ს სჭირდება ავთენტიკაცია"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "პროგრამული ფაილები აკლია %s-თვის"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s ხელმიუწვდომელია"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s კავშირის შეცდომა"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s აპარატურა გათიშულია"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s გათიშულია"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "ინტერნეტთან მიერთება"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "თვითმფრინავის რეჟიმი ჩართულია"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "როცა თვითმფრინავის რეჟიმი ჩართულია, Wi-Fi ითიშება."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "თვითმფრინავის რეჟმის გამორთვა"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi გამორთულია"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "ქსელთან მისაერთებლად საჭიროა ჩართოთ Wi-Fi."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Wi-Fi-ის ჩართვა"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wi-Fi ქსელები"
+
+#~ msgid "Select a network"
+#~ msgstr "აირჩიეთ ქსელი"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "გამოსართავად გამოიყენეთ აპარატურული გადამრთველი"
+
+#~ msgid "Select Network"
+#~ msgstr "აირჩიეთ ქსელი"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wi-Fi-ის მორგება"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s მიერთებული არაა"
+
+#~ msgid "unknown"
+#~ msgstr "უცნობი"
+
+#~ msgid "activating…"
+#~ msgstr "aქტივაცია…"
+
+#~ msgid "deactivating…"
+#~ msgstr "დეაქტივაცია…"
+
+#~ msgid "deactivated"
+#~ msgstr "დეაქტივირებულია"
+
+#~ msgid "connecting…"
+#~ msgstr "დაკავშირება…"
+
+#~ msgid "authentication required"
+#~ msgstr "საჭიროა ავთენტიკაცია"
+
+#~ msgid "connection failed"
+#~ msgstr "შეერთების შეცდომა"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN გამორთულია"
+
+#~ msgid "Network Settings"
+#~ msgstr "ქსელის მორგება"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Wi-Fi კავშირი"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "ღამის სინათლე გამორთულია"
+
+#~ msgid "Resume"
+#~ msgstr "გაგრძელება"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "ხვალამდე გამორთვა"
+
+#~ msgid "Power Settings"
+#~ msgstr "კვების პარამეტრები"
+
+#~ msgid "Fully Charged"
+#~ msgstr "სრულად დატენილი"
+
+#~ msgid "Not Charging"
+#~ msgstr "არ იტენება"
+
+#~ msgid "Estimating…"
+#~ msgstr "დაახლოებით…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d დარჩენილია (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d სრულამდე (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "ეკრანი ზიარდება"
+
+#~ msgid "Turn off"
+#~ msgstr "გამორთვა"
+
+#~ msgid "Lock"
+#~ msgstr "ჩაკეტვა"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "გამორთვა / გასვლა"
diff --git a/po/kab.po b/po/kab.po
new file mode 100644
index 0000000..43f1670
--- /dev/null
+++ b/po/kab.po
@@ -0,0 +1,2948 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2021-10-14 16:29+0000\n"
+"PO-Revision-Date: 2021-10-16 15:32+0100\n"
+"Last-Translator: Slimane Selyan Amiri <selyan.kab@protonmail.com>\n"
+"Language-Team: \n"
+"Language: kab_DZ\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 3.0\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Imsinḍen"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Rmed asnas anurif 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Rmed asnas anurif 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Rmed asnas anurif 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Rmed asnas anurif 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Rmed asnas anurif 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Rmed asnas anurif 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Rmed asnas anurif 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Rmed asnas anurif 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Rmed asnas anurif 9"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Anagraw"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Sken tabdart n telɣut"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Asaḍas ɣef telɣut turmidt"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Sken agzul"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Sken akk isnasen"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Ldi umuɣ n usnas"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Sens iseɣzaf n useqdac"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid "Layout of the app picker"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:150
+msgid "Keybinding to open the application menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:156
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between overview states"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:164
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the overview"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to focus the active notification."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:199
+msgid "Switch to application 1"
+msgstr "Senfel s asnas 1"
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid "Switch to application 2"
+msgstr "Senfel s asnas 2"
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 3"
+msgstr "Senfel s asnas 3"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 4"
+msgstr "Senfel s asnas 4"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 5"
+msgstr "Senfel s asnas 5"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 6"
+msgstr "Senfel s asnas 6"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 7"
+msgstr "Senfel s asnas 7"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 8"
+msgstr "Senfel s asnas 8"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 9"
+msgstr "Senfel s asnas 9"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid "Limit switcher to current workspace."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:241
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:258
+msgid "The application icon mode."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:259
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:268
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:278
+msgid "Locations"
+msgstr "Idγaren"
+
+#: data/org.gnome.shell.gschema.xml.in:279
+msgid "The locations to show in world clocks"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:289
+msgid "Automatic location"
+msgstr "Adeg s wudem awurman"
+
+#: data/org.gnome.shell.gschema.xml.in:290
+msgid "Whether to fetch the current location or not"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:297
+msgid "Location"
+msgstr "Adig"
+
+#: data/org.gnome.shell.gschema.xml.in:298
+msgid "The location for which to show a forecast"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:310
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:311
+#: data/org.gnome.shell.gschema.xml.in:320
+#: data/org.gnome.shell.gschema.xml.in:328
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:319
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:327
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Workspaces only on primary monitor"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Tuqqna ɣer uẓeṭṭa"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:28
+#: subprojects/extensions-app/data/ui/extensions-window.ui:241
+msgid "Something’s gone wrong"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:39
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:64
+msgid "Technical Details"
+msgstr "Talqayt taiknant"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:106
+msgid "Homepage"
+msgstr "Asebter agejdan"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:107
+msgid "Visit extension homepage"
+msgstr "Rzu s asmel n useɣzef"
+
+#: js/gdm/authPrompt.js:141 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:110 js/ui/components/polkitAgent.js:138
+#: js/ui/endSessionDialog.js:438 js/ui/extensionDownloader.js:228
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:978 subprojects/extensions-app/js/main.js:183
+msgid "Cancel"
+msgstr "Sefsex"
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:285 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:225 js/ui/components/networkAgent.js:249
+#: js/ui/components/networkAgent.js:270 js/ui/components/networkAgent.js:290
+#: js/ui/components/networkAgent.js:300 js/ui/components/polkitAgent.js:275
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "Awal ufir"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Fren tiɣimit"
+
+#: js/gdm/loginDialog.js:456
+msgid "Not listed?"
+msgstr "Ur d-yettwabder ara?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:921
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr ""
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:926 js/ui/components/networkAgent.js:245
+#: js/ui/components/networkAgent.js:268 js/ui/components/networkAgent.js:286
+msgid "Username"
+msgstr "Isem n useqdac"
+
+#: js/gdm/loginDialog.js:1279
+msgid "Login Window"
+msgstr "Asfaylu n usesteb"
+
+#: js/gdm/util.js:430
+msgid "Authentication error"
+msgstr "Tuccḍa deg usesteb"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:589
+msgid "(or swipe finger across reader)"
+msgstr ""
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:594
+msgid "(or place finger on reader)"
+msgstr ""
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:82
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Sens"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:85
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:90
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Ales tanekra"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:93
+msgid "reboot;restart;"
+msgstr ""
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:98
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Sekkeṛ agdil"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:101
+msgid "lock screen"
+msgstr "sekkeṛ agdil"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Ffeɣ"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "logout;log out;sign off"
+msgstr ""
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:114
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Ḥbes di leεḍil"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:117
+msgid "suspend;sleep"
+msgstr ""
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:122
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Senfel aseqdac"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:125
+msgid "switch user"
+msgstr "senfel aseqdac"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+
+#: js/misc/systemActions.js:232
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Serreḥ i tuzzya n ugdil"
+
+#: js/misc/systemActions.js:233
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Sekkeṛ tuzzya n ugdil"
+
+#: js/misc/util.js:121
+msgid "Command not found"
+msgstr "Ulac taladna"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:157
+msgid "Could not parse command:"
+msgstr ""
+
+#: js/misc/util.js:165
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr ""
+
+#: js/misc/util.js:182
+msgid "Just now"
+msgstr "Tura kan"
+
+#: js/misc/util.js:184
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d n tsedidt aya"
+msgstr[1] "%d n tsedidin aya"
+
+#: js/misc/util.js:188
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d n usrag aya"
+msgstr[1] "%d n yisragen aya"
+
+#: js/misc/util.js:192 js/ui/dateMenu.js:162
+msgid "Yesterday"
+msgstr "Iḍeli"
+
+#: js/misc/util.js:194
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d n wass aya"
+msgstr[1] "%d n wussan aya"
+
+#: js/misc/util.js:198
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d n imalas aya"
+msgstr[1] "%d n yimalasen"
+
+#: js/misc/util.js:202
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d n waggur aya"
+msgstr[1] "%d n wagguren aya"
+
+#: js/misc/util.js:205
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d n useggas aya"
+msgstr[1] "%d n yiseggasen aya"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:238
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:244
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Iḍelli, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:250
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:256
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:262
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:267
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:273
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Iḍelli, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:279
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:285
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:291
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:49
+msgid "Hotspot Login"
+msgstr ""
+
+#: js/portalHelper/main.js:95
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:417
+msgid "Deny Access"
+msgstr "Agi adduf"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:420
+msgid "Grant Access"
+msgstr "Mudd tasiregt"
+
+#: js/ui/appDisplay.js:1846
+msgid "Unnamed Folder"
+msgstr "Akaram war isem"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s yettwarna s inuraf-inek m."
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s yettwakkes s inuraf-inek m."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Ldi asfaylu"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Asfaylu amaynut"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Sken talqayt"
+
+#: js/ui/appMenu.js:96
+msgid "Quit"
+msgstr "Ffeɣ"
+
+#: js/ui/appMenu.js:163 js/ui/dash.js:245
+msgid "Remove from Favorites"
+msgstr "Kkes seg yinurifen"
+
+#: js/ui/appMenu.js:164
+msgid "Add to Favorites"
+msgstr "Rnu s inurifen"
+
+#: js/ui/appMenu.js:181
+msgid "Launch using Integrated Graphics Card"
+msgstr ""
+
+#: js/ui/appMenu.js:182
+msgid "Launch using Discrete Graphics Card"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Fren ibenk ameslaw"
+
+#: js/ui/audioDeviceSelection.js:56
+msgid "Sound Settings"
+msgstr "Iɣewwaren n yimesli"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Isawaḍen n uqerru"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Kask"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:277
+msgid "Microphone"
+msgstr "Asawaḍ"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Beddel agilal…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Beqqeḍ iɣewwaren"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "Iɣewwaṛen"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:36
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:65
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Sed"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:67
+msgctxt "grid monday"
+msgid "M"
+msgstr "A"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:69
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Am"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:71
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:73
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Am"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:75
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:77
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Sed"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:392
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:461
+msgid "Previous month"
+msgstr "Aggur yezrin"
+
+#: js/ui/calendar.js:476
+msgid "Next month"
+msgstr "Aggur d-itteddun"
+
+#: js/ui/calendar.js:626
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:682
+msgid "Week %V"
+msgstr ""
+
+#: js/ui/calendar.js:896
+msgid "No Notifications"
+msgstr "Ulac tilɣa"
+
+#: js/ui/calendar.js:950
+msgid "Do Not Disturb"
+msgstr ""
+
+#: js/ui/calendar.js:971
+msgid "Clear"
+msgstr "Sfeḍ"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” ur d-yettara ara."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "Ḥettem tuffɣa"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "Rǧu"
+
+#: js/ui/components/automountManager.js:85
+msgid "External drive connected"
+msgstr "Ibenk uffiɣ yettwaqqen"
+
+#: js/ui/components/automountManager.js:97
+msgid "External drive disconnected"
+msgstr "Ibenk uffiɣ yenser"
+
+#: js/ui/components/automountManager.js:206
+msgid "Unable to unlock volume"
+msgstr "Ulamek aserreḥ n ubleɣ"
+
+#: js/ui/components/automountManager.js:207
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:332
+#, javascript-format
+msgid "Open with %s"
+msgstr "Ldi s %s"
+
+#: js/ui/components/networkAgent.js:92
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:104 js/ui/status/network.js:258
+#: js/ui/status/network.js:349 js/ui/status/network.js:981
+msgid "Connect"
+msgstr "Qqen"
+
+#: js/ui/components/networkAgent.js:215
+msgid "Key"
+msgstr "Tasarut"
+
+#: js/ui/components/networkAgent.js:253 js/ui/components/networkAgent.js:276
+msgid "Private key password"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:274
+msgid "Identity"
+msgstr "Tamagit"
+
+#: js/ui/components/networkAgent.js:288
+msgid "Service"
+msgstr "Ameẓlu"
+
+#: js/ui/components/networkAgent.js:317 js/ui/components/networkAgent.js:345
+#: js/ui/components/networkAgent.js:679 js/ui/components/networkAgent.js:700
+msgid "Authentication required"
+msgstr "Illaq usesteb"
+
+#: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:322 js/ui/components/networkAgent.js:684
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:324
+msgid "Network name"
+msgstr "Isem n uzeḍḍa"
+
+#: js/ui/components/networkAgent.js:329 js/ui/components/networkAgent.js:688
+msgid "DSL authentication"
+msgstr "Asesteb DSL"
+
+#: js/ui/components/networkAgent.js:336 js/ui/components/networkAgent.js:693
+msgid "PIN code required"
+msgstr "Tangalt PIN tettwasra"
+
+#: js/ui/components/networkAgent.js:337 js/ui/components/networkAgent.js:694
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:338
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:346 js/ui/components/networkAgent.js:685
+#: js/ui/components/networkAgent.js:689 js/ui/components/networkAgent.js:701
+#: js/ui/components/networkAgent.js:705
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Awal n uɛeddi yettwsra i tuqqna ɣer “%s”."
+
+#: js/ui/components/networkAgent.js:668 js/ui/status/network.js:1789
+msgid "Network Manager"
+msgstr "Amsefrak n uzeḍḍa"
+
+#: js/ui/components/networkAgent.js:704
+msgid "VPN password"
+msgstr "Awal n uɛeddi VPN"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "Illaq usesteb"
+
+#: js/ui/components/polkitAgent.js:79
+msgid "Administrator"
+msgstr "Anedbal"
+
+#: js/ui/components/polkitAgent.js:141
+msgid "Authenticate"
+msgstr "Asesteb"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:252 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Surfaɣ, ayagi ur yeddi ara. Ttxil-k m eɛreḍ tikkelt-nniḍen."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:822
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr ""
+
+#: js/ui/ctrlAltTab.js:21 js/ui/overviewControls.js:404
+msgid "Windows"
+msgstr "Isfuyla"
+
+#: js/ui/dash.js:204 js/ui/dash.js:247
+msgid "Show Applications"
+msgstr "Sken isnasen"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr ""
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:79
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:86
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:151
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:154
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:160
+msgid "Today"
+msgstr "Assa"
+
+#: js/ui/dateMenu.js:164
+msgid "Tomorrow"
+msgstr "Azekka"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:180
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Meṛṛa ass"
+
+#: js/ui/dateMenu.js:231
+msgid "No Events"
+msgstr "Ulac ineḍruyen"
+
+#: js/ui/dateMenu.js:348
+msgid "Add world clocks…"
+msgstr ""
+
+#: js/ui/dateMenu.js:349
+msgid "World Clocks"
+msgstr ""
+
+#: js/ui/dateMenu.js:629
+msgid "Loading…"
+msgstr "Asali…"
+
+#: js/ui/dateMenu.js:639
+msgid "Go online for weather information"
+msgstr ""
+
+#: js/ui/dateMenu.js:641
+msgid "Weather information is currently unavailable"
+msgstr ""
+
+#: js/ui/dateMenu.js:651
+msgid "Weather"
+msgstr "Tignewt"
+
+#: js/ui/dateMenu.js:653
+msgid "Select weather location…"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:39
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:40
+msgctxt "title"
+msgid "Log Out"
+msgstr "Ffeɣ"
+
+#: js/ui/endSessionDialog.js:43
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:49
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "Ffeɣ"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "Sens"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Sbedd ileqman sakin sens aselkim"
+
+#: js/ui/endSessionDialog.js:66
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Anagraw ad yexsi s wudem awurman di %d n tasint."
+msgstr[1] "Anagraw ad yexsi s wudem awurman di %d n tasinin."
+
+#: js/ui/endSessionDialog.js:70 js/ui/endSessionDialog.js:89
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "Sens"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "Ales tanekra"
+
+#: js/ui/endSessionDialog.js:82
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Sbedd ileqman sakin ales asekker n uselkim"
+
+#: js/ui/endSessionDialog.js:85
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Anagraw ad yales tanekra s wudem awurman di %d n tasint."
+msgstr[1] "Anagraw ad yales tanekra s wudem awurman di %d n tasinin."
+
+#: js/ui/endSessionDialog.js:93
+msgctxt "button"
+msgid "Restart"
+msgstr "Ales tanekra"
+
+#: js/ui/endSessionDialog.js:101
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Ales asekker sakin sbedd ileqman"
+
+#: js/ui/endSessionDialog.js:104
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:111 js/ui/endSessionDialog.js:132
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:113
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:114
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:121
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr ""
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:126
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:284
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:293
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:298
+msgid "Other users are logged in"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Iɣewwaṛen n usenker"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:686
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (anmeggag)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:689
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (tadiwent)"
+
+#: js/ui/extensionDownloader.js:232
+msgid "Install"
+msgstr "Sebded"
+
+#: js/ui/extensionDownloader.js:238
+msgid "Install Extension"
+msgstr "Sbed aseɣzef"
+
+#: js/ui/extensionDownloader.js:239
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+
+#: js/ui/extensionSystem.js:252
+msgid "Extension Updates Available"
+msgstr ""
+
+#: js/ui/extensionSystem.js:253
+msgid "Extension updates are ready to be installed."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr ""
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:100
+msgid "Deny"
+msgstr "Aggi"
+
+#: js/ui/inhibitShortcutsDialog.js:107
+msgid "Allow"
+msgstr "Sireg"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:54
+msgid "Leave On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:54 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:1377
+msgid "Turn On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:62 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:166 js/ui/status/network.js:350
+#: js/ui/status/network.js:1377 js/ui/status/network.js:1489
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Sens"
+
+#: js/ui/kbdA11yDialog.js:62
+msgid "Leave Off"
+msgstr ""
+
+#: js/ui/keyboard.js:226
+msgid "Region & Language Settings"
+msgstr "Iɣewwaren n tutlayt akked tmennaḍt"
+
+#: js/ui/lookingGlass.js:676
+msgid "No extensions installed"
+msgstr "Ulac iseɣzaf yettwasbedden"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:734
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: js/ui/lookingGlass.js:740
+msgid "Hide Errors"
+msgstr "Ffer tuccḍiwin"
+
+#: js/ui/lookingGlass.js:744 js/ui/lookingGlass.js:810
+msgid "Show Errors"
+msgstr "Sken tuccḍiwin"
+
+#: js/ui/lookingGlass.js:753
+msgid "Enabled"
+msgstr "Yermed"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:756 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Yensa"
+
+#: js/ui/lookingGlass.js:758
+#: subprojects/extensions-app/data/ui/extension-row.ui:158
+msgid "Error"
+msgstr "Tuccḍa"
+
+#: js/ui/lookingGlass.js:760
+msgid "Out of date"
+msgstr "Yezri"
+
+#: js/ui/lookingGlass.js:762
+msgid "Downloading"
+msgstr "Asider"
+
+#: js/ui/lookingGlass.js:792
+msgid "View Source"
+msgstr "Wali aɣbalu"
+
+#: js/ui/lookingGlass.js:801
+msgid "Web Page"
+msgstr ""
+
+#: js/ui/main.js:290
+msgid "Logged in as a privileged user"
+msgstr ""
+
+#: js/ui/main.js:291
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+
+#: js/ui/main.js:340
+msgid "Screen Lock disabled"
+msgstr "Asekkeṛ n wegdil yensa"
+
+#: js/ui/main.js:341
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Asekkeṛ n wegdil yesra amsefrak n ubeqeḍ n GNOME."
+
+#: js/ui/messageTray.js:1443
+msgid "System Information"
+msgstr "Talɣut n unagraw"
+
+#: js/ui/mpris.js:207
+msgid "Unknown artist"
+msgstr "Anaẓur arussin"
+
+#: js/ui/mpris.js:217
+msgid "Unknown title"
+msgstr "Azwel arussin"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:313
+msgid "Type to search"
+msgstr "Aru iwakken ad tnadiḍ"
+
+#: js/ui/overviewControls.js:392
+msgid "Applications"
+msgstr "Isnasen"
+
+#: js/ui/overview.js:58
+msgid "Undo"
+msgstr "Sefsex"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Agzul"
+
+#: js/ui/padOsd.js:95
+msgid "New shortcut…"
+msgstr "Anegzum amaynut…"
+
+#: js/ui/padOsd.js:142
+msgid "Application defined"
+msgstr ""
+
+#: js/ui/padOsd.js:143
+msgid "Show on-screen help"
+msgstr ""
+
+#: js/ui/padOsd.js:144
+msgid "Switch monitor"
+msgstr ""
+
+#: js/ui/padOsd.js:145
+msgid "Assign keystroke"
+msgstr ""
+
+#: js/ui/padOsd.js:211
+msgid "Done"
+msgstr "Immed"
+
+#: js/ui/padOsd.js:716
+msgid "Edit…"
+msgstr "Ẓreg…"
+
+#: js/ui/padOsd.js:758 js/ui/padOsd.js:875
+msgid "None"
+msgstr "Ulac"
+
+#: js/ui/padOsd.js:829
+msgid "Press a button to configure"
+msgstr "Ssed taqeffalt iwakken ad tsewleḍ"
+
+#: js/ui/padOsd.js:830
+msgid "Press Esc to exit"
+msgstr "Ssed taqeffalt ESC iwakken ad teffɣeḍ"
+
+#: js/ui/padOsd.js:833
+msgid "Press any key to exit"
+msgstr "Ssed taqeffalt menwala iwakken ad teffɣeḍ"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:271
+msgid "Activities"
+msgstr "Irmuden"
+
+#: js/ui/panel.js:556
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Anagraw"
+
+#: js/ui/panel.js:674
+msgid "Top Bar"
+msgstr "Afeggag n ufella"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr ""
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Ssed taqeffalt ESC iwakken ad tmedleḍ"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr ""
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Tulsa n tnekra…"
+
+#: js/ui/screenShield.js:224
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME yesra ad isekkeṛ agdil"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:264 js/ui/screenShield.js:634
+msgid "Unable to lock"
+msgstr "Ulamek asekkeṛ"
+
+#: js/ui/screenShield.js:265 js/ui/screenShield.js:635
+msgid "Lock was blocked by an application"
+msgstr ""
+
+#: js/ui/screenshot.js:155
+msgid "Screenshot taken"
+msgstr "Tuṭṭfa n ugdil tedda"
+
+#: js/ui/search.js:825
+msgid "Searching…"
+msgstr "Anadi…"
+
+#: js/ui/search.js:827
+msgid "No results."
+msgstr "Ulac igmaḍ."
+
+#: js/ui/search.js:953
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Nadi"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Nɣel"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Senteḍ"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Sken aḍris"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ffer aḍris"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "Mekti awal-agi n uɛeddi"
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr "Serreḥ"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "Ldi %s"
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:465
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Ulamek asekker n %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:467
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Ur d-nufi ara asnas %s"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Tuffart"
+
+#: js/ui/status/accessibility.js:48
+msgid "Zoom"
+msgstr "Simɣur"
+
+#: js/ui/status/accessibility.js:55
+msgid "Screen Reader"
+msgstr "Ameɣri n ugdil"
+
+#: js/ui/status/accessibility.js:59
+msgid "Screen Keyboard"
+msgstr "Tasarut unasiw"
+
+#: js/ui/status/accessibility.js:63
+msgid "Visual Alerts"
+msgstr ""
+
+#: js/ui/status/accessibility.js:66
+msgid "Sticky Keys"
+msgstr "Tisura ineṭṭḍen"
+
+#: js/ui/status/accessibility.js:69
+msgid "Slow Keys"
+msgstr "Tisura ẓẓayen"
+
+#: js/ui/status/accessibility.js:72
+msgid "Bounce Keys"
+msgstr "Tisura i d-yettuɣalen"
+
+#: js/ui/status/accessibility.js:75
+msgid "Mouse Keys"
+msgstr "Tiqfallin n umumed"
+
+#: js/ui/status/accessibility.js:134
+msgid "High Contrast"
+msgstr "Amyeẓli afellay"
+
+#: js/ui/status/accessibility.js:176
+msgid "Large Text"
+msgstr "Aḍris ahrawan"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:652
+msgid "Bluetooth Settings"
+msgstr "Iɣewwaṛen n Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:148
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d yettwaqqen"
+msgstr[1] "%d ttwaqqnen"
+
+#: js/ui/status/bluetooth.js:152
+msgid "Bluetooth Off"
+msgstr "Bluetooth yensa"
+
+#: js/ui/status/bluetooth.js:154
+msgid "Bluetooth On"
+msgstr "Bluetooth yermed"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Tifawit"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Asiti asuf"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Asiti uslig"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Zuɣer"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Asiti wis sin"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr ""
+
+#: js/ui/status/keyboard.js:829
+msgid "Keyboard"
+msgstr "Anasiw"
+
+#: js/ui/status/keyboard.js:846
+msgid "Show Keyboard Layout"
+msgstr "Sken taneɣruft n unasiw"
+
+#: js/ui/status/location.js:234 js/ui/status/location.js:267
+msgid "Location Enabled"
+msgstr ""
+
+#: js/ui/status/location.js:235 js/ui/status/location.js:268
+msgid "Disable"
+msgstr "Assensi"
+
+#: js/ui/status/location.js:236
+msgid "Privacy Settings"
+msgstr "Iɣewwaren n tbaḍnit"
+
+#: js/ui/status/location.js:266
+msgid "Location In Use"
+msgstr ""
+
+#: js/ui/status/location.js:270
+msgid "Location Disabled"
+msgstr ""
+
+#: js/ui/status/location.js:271
+msgid "Enable"
+msgstr "Rmed"
+
+#: js/ui/status/location.js:398
+msgid "Allow location access"
+msgstr ""
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:400
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr ""
+
+#: js/ui/status/location.js:410
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+
+#: js/ui/status/network.js:72
+msgid "<unknown>"
+msgstr "Arussin"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:455 js/ui/status/network.js:1406
+#, javascript-format
+msgid "%s Off"
+msgstr "%s yensa"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:458
+#, javascript-format
+msgid "%s Connected"
+msgstr ""
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:463
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:466
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:473 js/ui/status/network.js:1398
+#, javascript-format
+msgid "%s Connecting"
+msgstr ""
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:476
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr ""
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:484
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:488
+#, javascript-format
+msgid "%s Unavailable"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:491
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr ""
+
+#: js/ui/status/network.js:503
+msgid "Wired Settings"
+msgstr ""
+
+#: js/ui/status/network.js:550
+msgid "Mobile Broadband Settings"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:619 js/ui/status/network.js:1403
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:623
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s yensa"
+
+#: js/ui/status/network.js:664
+msgid "Connect to Internet"
+msgstr "Yeqqen ɣer internet"
+
+#: js/ui/status/network.js:873
+msgid "Airplane Mode is On"
+msgstr ""
+
+#: js/ui/status/network.js:874
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr ""
+
+#: js/ui/status/network.js:875
+msgid "Turn Off Airplane Mode"
+msgstr ""
+
+#: js/ui/status/network.js:884
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi yensa"
+
+#: js/ui/status/network.js:885
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr ""
+
+#: js/ui/status/network.js:886
+msgid "Turn On Wi-Fi"
+msgstr "Sermed Wi-Fi"
+
+#: js/ui/status/network.js:911
+msgid "Wi-Fi Networks"
+msgstr "Izeḍwan n Wi-Fi"
+
+#: js/ui/status/network.js:913
+msgid "Select a network"
+msgstr "Fren azeḍḍa"
+
+#: js/ui/status/network.js:945
+msgid "No Networks"
+msgstr "Ulac izeḍwan"
+
+#: js/ui/status/network.js:966 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr ""
+
+#: js/ui/status/network.js:1267
+msgid "Select Network"
+msgstr "Fren azeḍḍa"
+
+#: js/ui/status/network.js:1273
+msgid "Wi-Fi Settings"
+msgstr "Iɣewwaren n Wi-Fi"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1394
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1409
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s ur yeqqin ara"
+
+#: js/ui/status/network.js:1506
+msgid "connecting…"
+msgstr "la yetteqqen…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1509
+msgid "authentication required"
+msgstr "illaq usesteb"
+
+#: js/ui/status/network.js:1511
+msgid "connection failed"
+msgstr "tuqqna ur teddi ara"
+
+#: js/ui/status/network.js:1562
+msgid "VPN Settings"
+msgstr "Iɣewwaren n VPN"
+
+#: js/ui/status/network.js:1579
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1589
+msgid "VPN Off"
+msgstr "VPN yensa"
+
+#: js/ui/status/network.js:1650 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Iɣewwaren n uzeḍḍa"
+
+#: js/ui/status/network.js:1678
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/network.js:1682
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/network.js:1686
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/network.js:1830
+msgid "Connection failed"
+msgstr "Tuqqna ur teddi ara"
+
+#: js/ui/status/network.js:1831
+msgid "Activation of network connection failed"
+msgstr "Azeṭṭa ur yessaweḍ ara ad yeqqen"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr ""
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr ""
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Kemmel"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr ""
+
+#: js/ui/status/power.js:51 js/ui/status/powerProfiles.js:57
+msgid "Power Settings"
+msgstr "Iɣewwaren usečči"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr ""
+
+#: js/ui/status/power.js:74
+msgid "Not Charging"
+msgstr ""
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:77 js/ui/status/power.js:83
+msgid "Estimating…"
+msgstr ""
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr ""
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:97
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr ""
+
+#. The icon label
+#: js/ui/status/power.js:145
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/powerProfiles.js:19
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Atwal"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr ""
+
+#: js/ui/status/powerProfiles.js:21
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr ""
+
+#: js/ui/status/remoteAccess.js:38
+msgid "Screen is Being Shared"
+msgstr ""
+
+#: js/ui/status/remoteAccess.js:40
+msgid "Turn off"
+msgstr "Sens"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr ""
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "Sekkeṛ"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr ""
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "Ḥbes di leεḍil"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "Ales tanekra…"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "Sens…"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Ffeɣ"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "Senfel aseqdac…"
+
+#: js/ui/status/thunderbolt.js:262
+msgid "Thunderbolt"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:160
+msgid "Volume changed"
+msgstr ""
+
+#: js/ui/status/volume.js:222
+msgid "Volume"
+msgstr "Ableɣ"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "Tamrayt"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr ""
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr ""
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr ""
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:371
+msgid "%A %B %-d"
+msgstr ""
+
+#: js/ui/unlockDialog.js:377
+msgid "Swipe up to unlock"
+msgstr ""
+
+#: js/ui/unlockDialog.js:378
+msgid "Click or press a key to unlock"
+msgstr ""
+
+#: js/ui/unlockDialog.js:556
+msgid "Unlock Window"
+msgstr ""
+
+#: js/ui/unlockDialog.js:565
+msgid "Log in as another user"
+msgstr ""
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr ""
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+
+#: js/ui/welcomeDialog.js:45
+msgid "No Thanks"
+msgstr "Ala tanemmirt"
+
+#: js/ui/welcomeDialog.js:50
+msgid "Take Tour"
+msgstr ""
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr ""
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:63
+msgid "Keep these display settings?"
+msgstr ""
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr ""
+
+#: js/ui/windowManager.js:75
+msgid "Keep Changes"
+msgstr ""
+
+#: js/ui/windowManager.js:94
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:550
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Hide"
+msgstr "Ffer"
+
+#: js/ui/windowMenu.js:34
+msgid "Restore"
+msgstr "Err-it"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "Semɣer"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Senkez"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr ""
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr ""
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr ""
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr ""
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr ""
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr ""
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr ""
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr ""
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr ""
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr ""
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr ""
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr ""
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Mdel"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr ""
+
+#: src/main.c:419 subprojects/extensions-tool/src/main.c:317
+msgid "Print version"
+msgstr "Sken lqem"
+
+#: src/main.c:425
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: src/main.c:431
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+
+#: src/main.c:437
+msgid "List possible modes"
+msgstr ""
+
+#: src/shell-app.c:298
+msgctxt "program"
+msgid "Unknown"
+msgstr "Arusin"
+
+#: src/shell-app.c:549
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Ur izmir ara ad yekker “%s”"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "Awalen uffiren mgarraden."
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr ""
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:217
+#: subprojects/extensions-app/data/ui/extensions-window.ui:56
+msgid "Extensions"
+msgstr "Isiɣzaf"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:218
+msgid "Manage your GNOME Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:142
+#: subprojects/extensions-app/js/main.js:152
+msgid "No Matches"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:179
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Kkes \"%s\"?"
+
+#: subprojects/extensions-app/js/main.js:180
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:184
+msgid "Remove"
+msgstr "Kkes"
+
+#: subprojects/extensions-app/js/main.js:216
+msgid "translator-credits"
+msgstr ""
+"Launchpad Contributions:\n"
+" Slimane Selyan AMIRI https://launchpad.net/~slimaneamiri\n"
+" atlantis103 https://launchpad.net/~atlantis103-hotmail"
+
+#: subprojects/extensions-app/js/main.js:344
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/extensions-app/js/main.js:494
+msgid "The extension is incompatible with the current GNOME version"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:497
+msgid "The extension had an error"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:83
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Aglam"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:104
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Lqem"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:131
+msgid "Author"
+msgstr "Auteur"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:185
+msgid "Website"
+msgstr "Asmel Web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:201
+msgid "Remove…"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:7
+msgid "Help"
+msgstr "Tallelt"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:11
+msgid "About Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:34
+msgid "Warning"
+msgstr "Alɣu"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:44
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:150
+msgid "Manually Installed"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:174
+msgid "Built-In"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:217
+msgid "No Installed Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:252
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:288
+msgid "Extension Updates Ready"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:303
+msgid "Log Out…"
+msgstr ""
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Isem"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Talɣa"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "ISEM"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+msgid "Failed to connect to GNOME Shell\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:97
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:102
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "AFAYLU"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "AZENZIƔ"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "AKARAM"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "TAƔULT"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:79
+msgid "Opens extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell"
+msgstr "Tecceḍ tuqqna ɣer GNOME Shell"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Abrid"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Addad"
+
+#: subprojects/extensions-tool/src/main.c:290
+msgid "“version” takes no arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:292
+#: subprojects/extensions-tool/src/main.c:312
+msgid "Usage:"
+msgstr "Aseqdec:"
+
+#: subprojects/extensions-tool/src/main.c:295
+msgid "Print version information and exit."
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:310
+#: subprojects/extensions-tool/src/main.c:313
+msgid "COMMAND"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:313
+msgid "[ARGS…]"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:315
+msgid "Commands:"
+msgstr "Tiludna:"
+
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Print help"
+msgstr "Siggez tallelt"
+
+#: subprojects/extensions-tool/src/main.c:318
+msgid "Enable extension"
+msgstr "Sermed seɣzef"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Disable extension"
+msgstr "Sens aseɣzef"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Reset extension"
+msgstr "Ales awennez n useɣzef"
+
+#: subprojects/extensions-tool/src/main.c:321
+msgid "Uninstall extension"
+msgstr "Kkes aseɣzef"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "List extensions"
+msgstr "Tabdart n yisiɣzaf"
+
+#: subprojects/extensions-tool/src/main.c:323
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Show extension info"
+msgstr "Sken talɣut n useɣzef"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Open extension preferences"
+msgstr "Ldi ismenyifen n useɣzef"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "Create extension"
+msgstr "Snulfu-d aseɣzef"
+
+#: subprojects/extensions-tool/src/main.c:327
+msgid "Package extension"
+msgstr "Asiɣzef n ukemmus"
+
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Install extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:330
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Seqdec “%s” iwakken ad-d-tawiḍ tallelt s telqayt.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Aḥerfi"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Aseɣzef ilem"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Anamal"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Rnu tignit di tama n ufella n ufeggag"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u n tuffɣa"
+msgstr[1] "%u n tuffɣiwin"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u n unekcum"
+msgstr[1] "%u n yinekcumen"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Imeslan n unagraw"
+
+#~ msgid "Minimize"
+#~ msgstr "Semẓi"
diff --git a/po/kk.po b/po/kk.po
new file mode 100644
index 0000000..998c535
--- /dev/null
+++ b/po/kk.po
@@ -0,0 +1,3529 @@
+# Kazakh translation for gnome-shell.
+# Copyright (C) 2015 The gnome-shell authors.
+# This file is distributed under the same license as the gnome-shell package.
+# Baurzhan Muftakhidinov <baurthefirst@gmail.com>, 2012-2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-17 18:27+0000\n"
+"PO-Revision-Date: 2022-09-18 11:38+0600\n"
+"Last-Translator: Baurzhan Muftakhidinov <baurthefirst@gmail.com>\n"
+"Language-Team: Kazakh <kk_KZ@googlegroups.com>\n"
+"Language: kk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 3.1.1\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Жөнелткіштер"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "1-ші таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "2-ші таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "3-ші таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "4-ші таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "5-ші таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "6-шы таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "7-ші таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "8-ші таңдамалы қолданбаны белсендіру"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "9-шы таңдамалы қолданбаны белсендіру"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Скриншоттар"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Скриншотты интерактивті түрде түсіру"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Скриншотты түсіру"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Терезе скриншотын түсіру"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Скринкастты интерактивті түрде жазу"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Жүйе"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Хабарламалар тізімін көрсету"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Белсенді хабарламаға фокусты орнату"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Шолуды көрсету"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Барлық қолданбаларды көрсету"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Қолданбалар мәзірін ашу"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Терезелерді басқару мен қолданбаларды жөнелту"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Alt-F2 ішінен өндірушілер мен сынаушылар үшін пайдалы ішкі саймандарды іске "
+"қосу"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 сұхбат арқылы қолжетерлік болатын ішкі жөндеу мен бақылау "
+"саймандарына қатынауды рұқсат ету."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Іске қосу үшін кеңейтулердің UUID-ры"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell кеңейтулерінде UUID қасиеті бар; бұл кілт жүктелетін кеңейтулер "
+"тізімін сақтайды. Жүйеге кірген кезде жүктелем дейтін кеңейту осы тізімде "
+"болуы тиіс. Бұл тізімді org.gnome.Shell ішіндегі EnableExtension және "
+"DisableExtension DBus тәсілдерімен де өзгертуге болады."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Мәжбүрлі сөндіру үшін кеңейтулер UUID-лары"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell кеңейтулерінде UUID қасиеті бар; бұл кілт сөндіру үшін "
+"кеңейтулер тізімін сақтайды, олар қазіргі режимде іске қосылған болса да. "
+"Сонымен қатар, бұл тізімді org.gnome.Shell EnableExtension және "
+"DisableExtension D-Bus тәсілдері арқылы түзетуге болады. Бұл кілт \"enabled-"
+"extensions\" баптауын үстінен басады."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Пайдаланушы кеңейтулерін сөндіру"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"\"enabled-extension\" баптауын тимей-ақ, пайдаланушы іске қосқан барлық "
+"кеңейтулерді сөндіру."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Кеңейту нұсқасы үйлесімділігінің расталуын сөндіреді"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell тек ағымдағы орындалып жатқан нұсқаны қолдаймын деп хабарлайтын "
+"кеңейтулерді жүктейтін болады. Бұл опцияны іске қосу ол мүмкіндікті "
+"сөндіреді, және кеңейту қолдайтын нұсқасына қарамастан жүктеп көретін болады."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Таңдамалы қолданбалар үшін desktop файлдары ID-нің тізімі"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Осы анықтағыштарға сәйкес келетін қолданбалар таңдамалылар аймағында "
+"көрсетілетін болады."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Командалар сұхбаты (Alt-F2) үшін тарихы"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Іздеу сұхбатының тарихы"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Пайдаланушы мәзірінде \"Жүйеден шығу\" нұсқасын әрқашан да көрсету."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Бұл кілт бір пайдаланушы, бір сессия бар кезінде \"Жүйеден шығу\" "
+"мүмкіндігінің автоматты түрде жасыруын алмастырады."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Шифрленген немесе қашықтағы файлдық жүйелерді тіркеу парольдерін сақтау "
+"керек пе, соны көрсетеді"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Қоршам шифрленген құрылғы немесе қашықтағы файлдық жүйе тіркелген кезде "
+"парольді сұрайды. Егер парольді болашақтағы қолданулар үшін сақтау керек "
+"болса, \"Парольді есте сақтау\" жалаушасын орнатыңыз. Бұл кілт жалаушаның "
+"бастапқы күйін орнатады."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Соңғы таңдалған әдепкі емес эл. қорегі профилі"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Кейбір жүйелер екіден артық эл. корек профильдерін қолдайды. Екі профиль "
+"арасында ауысуды қолдау үшін бұл кілт соңғы таңдалған әдепкі емес профильді "
+"жазады."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "\"GNOME %s ішіне қош келдіңіз\" сұхбаты көрсетілген соңғы нұсқасы"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Бұл кілт \"GNOME %s ішіне қош келдіңіз\" сұхбаты көрсетілген соңғы нұсқасын "
+"сипаттайды. Бос жол мүмкін болған ең ескі нұсқаны, ал үлкен сан әлі жоқ "
+"болып тұрған нұсқаларды сипаттайды. Бұл үлкен санды сұхбатты сөндіру үшін "
+"қолдануға болады."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Қолданбалар таңдаушысының жаймасы"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Қолданбалар таңдаушысының жаймасы. Массивтегі әрбір жазба жеке бет болып "
+"табылады. Беттер GNOME Shell-да көрсетілетін ретімен сақталады. Әр бетте "
+"\"бағдарлама id\" → \"деректер\" жұбы бар. Қазіргі кезде мәндер келесідей "
+"сақталады 'деректер': • \"орын\": беттегі бағдарлама таңбашасының орны"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Қолданбалар мәзірін ашу үшін пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Қолданбалар мәзірін ашу үшін пернелер комбинациясы."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Шолу күйлері арасында ауысу үшін пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Сессия, терезелер таңдаушысы және қолданбалар торы арасында ауысу үшін "
+"пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Қолданбалар торы, терезелер таңдаушысы және сессия арасында ауысу үшін "
+"пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "\"Қолданбаларды көрсету\" көрінісін ашу үшін пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Көрініс ішінен \"Қолданбаларды көрсету\" ашу үшін пернелер комбинациясы."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Шолуды ашу үшін пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Көріністі ашу үшін пернелер комбинациясы."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Хабарламалар тізімінің көрінуін іске қосу/сөндіру үшін пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Хабарламалар тізімінің көрінуін іске қосу/сөндіру үшін пернелер комбинациясы."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Белсенді ескертуге фокусты ауыстыру пернелер комбинациясы"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Белсенді ескертуге фокусты ауыстыру пернелер комбинациясы."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "1-ші қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "2-ші қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "3-ші қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "4-ші қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "5-ші қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "6-шы қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "7-ші қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "8-ші қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "9-шы қолданбаға ауысу"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Ауыстырғышты ағымдағы жұмыс орнымен шектеу."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Таңдалса, ауыстырғышта тек ағымдағы жұмыс орнындағы терезелері бар "
+"қолданбалар көрсетіледі. Болмаса, барлық қолданбалар болады."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Қолданбаның таңбаша режимі."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Терезелер ауыстырғышта қалай көрсетілетінін сипаттайды. Мүмкін мәндері "
+"\"thumbnail-only\" (терезенің кіші көрінісі көрсетіледі), \"app-icon-only"
+"\" (тек қолданба таңбашасы көрсетіледі) немесе \"both\" (екеуі де)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Таңдалса, ауыстырғышта тек ағымдағы жұмыс орнындағы терезелер көрсетіледі. "
+"Болмаса, барлық терезелер болады."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Орналасулар"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Дүниежүзілік сағаттарда көрсетілетін орналасулар"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Автоматты орналасу"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Ағымдағы орналасуды алу керек пе, жоқ па"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Орналасу"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Ауа райы болжамын көрсету үшін орналасу"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Модальды сұхбатты аталық терезесіне жалғау"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Бұл кілт GNOME Shell орындалған кезде org.gnome.mutter ішіндегі кілтті "
+"үстінен жазады."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Терезелерді экран шеттеріне апарған кезде олардың өлшемдерін өзгертуді іске "
+"қосу"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Жұмыс орындары динамикалы түрде басқарылады"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Жұмыс орындар тек біріншілік мониторда"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Тышқан режиміндегі фокусты ауыстыру курсор тоқтағанша дейін кідірту"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Желілік кіру"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Бір нәрсе қате кетті"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Кешіріңіз, мәселе орын алды: бұл кеңейту үшін баптауларды көрсету мүмкін "
+"емес. Бұл мәселені кеңейту авторларына хабарлауды ұсынамыз."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Техникалық ақпараты"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Үй парағы"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Кеңейтудің үй парағын шолу"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Бас тарту"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Пароль"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Сессияны таңдау"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Тізімде жоқсыз ба?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(мыс., пайдаланушы не %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Пайдаланушы аты"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Жүйеге кіру терезесі"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Аутентификация қатесі"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(немесе саусағыңызды оқу құралынан өткізіңіз)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(немесе саусақты оқу құралына орналастырыңыз)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Сөндіру"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;сөндіру;қайта қосу;тоқтату"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Қайта қосу"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;қайта жүктеу;қайта іске қосу;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Экранды блоктау"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "экранды блоктау"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Жүйеден шығу"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;шығу;жүйеден шығу"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Ұйықтату"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "ұйықтату"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Пайдаланушыны ауыстыру"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "пайдаланушыны ауыстыру"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;unlock orientation;screen;rotation;бұрылуды құлыптау;экран "
+"бұрылуы"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Скриншотты түсіру"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;скриншот;скринкаст;жазу;ұстау"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Экранды бұруды құлыптан босату"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Экранды бұруды құлыптау"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Команда табылмады"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Команданы талдау мүмкін емес:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "\"%s\" жөнелту сәтсіз:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Жаңа ғана"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d минут бұрын"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d сағат бұрын"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Кеше"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d күн бұрын"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d апта бұрын"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d ай бұрын"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d жыл бұрын"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Кеше, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Кеше, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %H∶%M"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Қатынау нүктесіне кіру"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Бұл қатынау нүктесіне кіруге байланысыңыз қауіпсіз емес. Бұл парақта "
+"енгізілген парольдер немесе басқа да ақпаратыңызды сол желідегі адамдар көре "
+"алады."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Тыйым салу"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Рұқсат ету"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Атаусыз бума"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s қолданбалар панеліне бекітілді."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s қолданбалар панелінен босатылды."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Ашық терезелер"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Жаңа терезе"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Деректерді көрсету"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Шығу"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Босату"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Қолданбалар панеліне бекіту"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Құрамындағы графикалық картаны пайдаланып жөнелту"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Бөлек графикалық картаны пайдаланып жөнелту"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Аудио құрылғысын таңдаңыз"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Дыбыс баптаулары"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Құлаққап"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Микрофонды құлаққап"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Фонды өзгерту…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Экран баптаулары"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Баптаулар"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Жк"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Дс"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Сс"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ср"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Бс"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Жм"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Сн"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Өткен ай"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Келесі ай"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Апта %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Хабарламалар жоқ"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Мазаламаңыз"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Тазарту"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "\"%s\" жауап бермейді."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Сіз оның жалғастыруын біраз уақыт бойы күте аласыз, немесе қолданбаны "
+"толықтай шығуды мәжбүрлете аласыз."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Мәжбүрлі тоқтату"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Күту"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Сыртқы диск қосылды"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Сыртқы диск алынды"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Томды босату мүмкін емес"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Орнатылған udisks нұсқасы PIM орнатуды қолдамайды"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s көмегімен ашу"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Қосымша түрде, сіз роутердегі \"WPS\" батырмасын басу арқылы байланыса "
+"аласыз."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Байланысу"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Кілт"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Жеке кілт паролі"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Идентификация"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Қызмет"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Аутентификация керек"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"\"%s\" сымсыз желісіне қатынау үшін парольдер не шифрлеу кілттері керек."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Сымды 802.1X аутентификациясы"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Желі атауы"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL аутентификациясы"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN коды керек"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Сымсыз кеңжолақты құрылғы үшін PIN коды керек"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "\"%s\" үшін байланысты орнату үшін пароль керек."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Желілер басқарушысы"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN паролі"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Аутентификация керек"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Әкімші"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Аутентификация"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Кешіріңіз, талап сәтсіз. Қайтадан көріңіз."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s енді %s ретінде белгілі"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Терезелер"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Қолданбаларды көрсету"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Бүгін"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Ертең"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Толық күн"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Оқиғалар жоқ"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Дүниежүзілік сағаттарды қосу…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Дүниежүзілік сағаттар"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Жүктеу…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Ауа райы ақпаратын алу үшін желіге байланысыңыз"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Ауа райы ақпараты ағымдағы уақытта қолжетерсіз"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Ауа райы"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Ауа райы орнын таңдау…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Шығу: %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Шығу"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s жүйеден автоматты түрде %d секундтан кейін шығады."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Сіз жүйеден автоматты түрде %d секундтан кейін шығасыз."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Шығу"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Сөндіру"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Жаңартуларды орнату және сөндіру"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Жүйе %d секундтан кейін автоматты түрде сөндіріледі."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Кезекте тұрған БҚ жаңартуларын орнату"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Сөндіру"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Қайта қосу"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Жаңартуларды орнату және қайта қосу"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Жүйе %d секундтан кейін автоматты түрде қайта қосылады."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Қайта қосу"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Қайта қосу және жаңартуларды орнату"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Жүйе %d секундтан кейін автоматты түрде қайта қосылады және жаңартуларды "
+"орнатады."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Қайта қосу және орнату"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Орнату және сөндіру"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Жаңартулар орнатылғаннан кейін сөндіру"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Қайта қосу және жаңартуларды орнату"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s қайта қосылғаннан кейін орнатылатын болады. Орнатуды жаңарту ұзақ "
+"уақытты алуы мүмкін: деректердің қор көшірмесі бар екеніне және компьютер "
+"эл. желісіне жалғанып тұрғанына көз жеткізіңіз."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Батарея заряд деңгейі аз: жаңартуларды орнату алдында эл. қорегі желісіне "
+"жалғаңыз."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Кейбір қолданбалар бос емес немесе сақталмаған жұмыстары бар"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Басқа пайдаланушылар жүйеге кіріп тұр"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Жүктеу параметрлері"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (қашықтағы)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (консоль)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Орнату"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Кеңейтуді орнату"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.org адресінен \"%s\" жүктеп алып, орнату керек пе?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Кеңейту жаңартулары дайын"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Кеңейту жаңартулары орнатылуға дайын."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Пернелер жарлығын алмастыруды рұқсат ету"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "%s қолданбасы пернелер жарлығын алмастырғысы келеді"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Қолданба пернелер жарлығын алмастырғысы келеді"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Сіз жарлықтарды %s басу арқылы қалпына келтіре аласыз."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Тыйым салу"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Рұқсат ету"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Баяу пернелер іске қосылған"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Баяу пернелер сөндірілген"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Сіз Shift пернесін 8 секунд бойы басулы ұстадыңыз. Бұл Баяу пернелер "
+"мүмкіндігінің жарлығы, ол пернетақта жұмысына әсер етеді."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Жабысқақ пернелер іске қосылған"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Жабысқақ пернелер сөндірілген"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Сіз Shift пернесін қатарынан 5 рет бастыңыз. Бұл Жабысқақ пернелер "
+"мүмкіндігінің жарлығы, ол пернетақта жұмысына әсер етеді."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Сіз екі пернені бірге бастыңыз, немесе Shift пернесін қатарынан 5 рет "
+"бастыңыз. Бұл Жабысқақ пернелер мүмкіндігін сөндіреді, ол пернетақта "
+"жұмысына әсер етеді."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Іске қосулы қалдыру"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Іске қосу"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Сөндіру"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Сөндірулі қалдыру"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Аймақ және тіл баптаулары"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Кеңейтулер орнатылмаған"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ешбір қатені шығармады."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Қателерді жасыру"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Қателерді көрсету"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Іске қосылған"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Сөндірулі"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Қате"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Ескірген"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Жүктелуде"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Бастапқы кодын қарау"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Веб парағы"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Жүйе қауіпсіз емес режимге қойылды"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Қолданбаларда енді шектелмеген рұқсаттар бар"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Болдырмау"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Привилегияланған пайдаланушы ретінде жүйеге кірген"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Привилегияланған пайдаланушы ретінде сессияны орындаудан қауіпсіздік "
+"салдарынан аулақ болу керек. Мүмкін болса, қалыпты пайдаланушы ретінде "
+"жүйеге кіріңіз."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Экранды блоктау сөндірілген"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Экранды блоктау GNOME дисплейлер басқарушысын талап етеді."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Жүйелік ақпарат"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Белгісіз әртіс"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Белгісіз атауы"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Іздеу үшін теріңіз"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Қолданбалар"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Шолу"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Жаңа жарлық…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Тағайындалған қолданбалар"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Экрандағы көмекті көрсету"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Мониторды ауыстыру"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Пернелер жарлығын тағайындау"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Дайын"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Түзету…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Ешнәрсе"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Баптау үшін батырманы басыңыз"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Шығу үшін Esc басыңыз"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Шығу үшін кез-келген батырманы басыңыз"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Көрініс"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Жүйелік"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Үстідегі панель"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Команданы орындау"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Жабу үшін ESC пернесін басыңыз"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Қайта қосу Wayland нұсқасында қолжетерсіз"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Қайта қосу…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME экранды блоктау керек"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Блоктау мүмкін емес"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Блоктауды басқа қолданба болдырмады"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Таңдау"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Аймақты таңдау"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Экран"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Экранды таңдау"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Терезе"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Терезені таңдау"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Скриншот/скринкаст"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Курсорды көрсету"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Скринкасттар"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Скринкаст, уақыты %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Скриншот"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Скринкаст жазылды"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Видеоны қарау үшін осында шертіңіз."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Файлдар қолданбасында көрсету"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Скриншот, уақыты %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Скриншот түсірілді"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Суретті алмасу буферінен кірістіруге болады."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Скриншот түсірілді"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Іздеу…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Нәтижелер жоқ."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "тағы %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Іздеу"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Көшіру"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Кірістіру"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Мәтінді көрсету"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Мәтінді жасыру"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock іске қосылған."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Жасырын том"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows жүйелік томы"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Кілт файлдарды қолданады"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Кілт файлдарын қолданатын томды босату үшін, <i>%s</i> утилитасын қолданыңыз."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM нөмірі"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Парольді есте сақтау"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Босату"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s ашу"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM тек сандардан тұруы, немесе бос болуы тиіс."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s іске қосу мүмкін емес"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s қолданбасы табылмады"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Қол жетерлілігі"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Жоғары контраст"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Үлкейту"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Экраннан оқитын қолданба"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Экрандағы пернетақта"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Бейнелік хабарлау"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Жабысқақ пернелер"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Баяу пернелер"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Қайталанатын пернелер"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Тышқан пернелері"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Үлкен мәтін"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Авто бұру"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Жарықтылығы"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Қараңғы режім"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Дара шерту"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Қос шерту"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Тартып апару"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Екінші шерту"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Пернеде кідіртуден кейінгі сол жақпен шерту"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Пернетақта"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Пернетақта жаймасын көрсету"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Орналасуға қатынауды рұқсат ету"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "%s қолданбасы орналасуыңызға рұқсат сұрап отыр"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Орналасуыңызға қатынауды кез-кезген кезде жекелік баптауларының ішінен "
+"өзгертуге болады."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<белгісіз>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "%s байланысын үзу"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "%s құрылғысымен байланысу"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s қатынау нүктесі"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Түнгі жарық"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Өнімділік"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Баланс"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Қорек үнемдеу"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Эл. қорегі профильдері"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Скринкастты тоқтату"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Экранмен бөлісуді тоқтату"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Ұшақтағы режим"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Скриншотты түсіру"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Сөндіру мәзірі"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Ұйықтату"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Қайта қосу…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Сөндіру…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Жүйеден шығу…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Пайдаланушыны ауыстыру…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Экранды блоктау"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Белгісіз Thunderbolt құрылғысы"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Сіз кетіп қалған уақытта жаңа құрылғы анықталған. Оны қолдануды бастау үшін, "
+"оны ажыратып, қайта салыңыз."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Авторизацияланбаған Thunderbolt құрылғысы"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Жаңа құрылғы анықталды және де оны әкімші авторизациялау керек."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt авторизация қатесі"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Thunderbolt құралғысын авторизациялау мүмкін емес: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Дыбыс өзгертілді"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Дыбыс деңгейі"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Дыбыс шығысы"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Дыбыс кірісі"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Айналы"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Экран­дарды біріктіру"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Тек сыртқы"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Тек құрамындағы"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Босату үшін жоғары өткізіңіз"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Босату үшін шертіңіз немесе пернені басыңыз"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Блоктауды алу терезесі"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Басқа пайдаланушы ретінде жүйеге кіру"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "GNOME %s ішіне қош келдіңіз"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Егер айналаңызды үйренгіңіз келсе, турды шолыңыз."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Жоқ рахмет"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Турды шолу"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "\"%s\" дайын"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Бұл экран баптауларын сақтауды қалайсыз ба?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Баптауларды қалпына келтіру"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Өзгерістерді сақтау"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Өзгертілген баптаулар %d секундтан кейін қалпына келтіріледі"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Жасыру"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Қалпына келтіру"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Жазық қылу"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Жылжыту"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Өлшемін өзгерту"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Атау жолағын экранға жылжыту"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Әрқашан алдында"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Әрқашан көрінетін жұмыс орнында"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Сол жақтағы жұмыс орнына жылжыту"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Оң жақтағы жұмыс орнына жылжыту"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Жоғарыдағы жұмыс орнына жылжыту"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Төмендегі жұмыс орнына жылжыту"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Жоғарыдағы мониторға жылжыту"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Төмендегі мониторға жылжыту"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Сол жақ мониторға жылжыту"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Оң жақ мониторға жылжыту"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Жабу"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution күнтізбесі"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Баспа нұсқасы"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "GDM жүйеге кіру экраны үшін қолданатын режимі"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Жүйеге кіру экраны үшін арнайы режимді, мыс. \"gdm\", қолдану"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Қолжетерлік режимдерді тізіп шығу"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Белгісіз"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "\"%s\" жөнелту сәтсіз"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Парольдер өзара сәйкес емес."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Пароль бос болуы мүмкін емес"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Аутентификация терезесін пайдаланушы тайдырды"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Кеңейтулер"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "GNOME кеңейтулерін басқару"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME жобасы"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME кеңейтулері көмегімен кеңейтулерді жаңарту, баптау және оларды өшіру "
+"немесе сөндіруге болады."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Shell кеңейтулерін баптау"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Сәйкестіктер жоқ"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "\"%s\" өшіру керек пе?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Кеңейтуді өшірсеңіз, оны қайта іске қосу үшін қайта жүктеп алу керек болады"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Өшіру"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Baurzhan Muftakhidinov <baurthefirst@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d кеңейту жүйеге келесі рет кірген кезде жаңартылатын болады."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Бұл кеңейту ағымдағы GNOME нұсқасымен үйлеспейді"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Кеңейтуде қате болды"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Кеңейтуді жаңартуға болады"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Веб сайт"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Өшіру…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Көмек"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Кеңейтулер туралы"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Кеңейтулер өнімділік пен тұрақтылық мәселелерін тудыруы мүмкін. Жүйеңіздегі "
+"ақаулықтарға тап болсаңыз, кеңейтулерді сөндіріңіз."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Қолмен орнатылған"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Жаңа кеңейтулерді тауып, орнату үшін, <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a> шолыңыз."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Құрамындағы"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Кеңейтулер орнатылмаған"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Кешірім өтінеміз, барлық орнатылған кеңейтулер тізімін алу мүмкін болмады. "
+"GNOME ішіне кіргеніңізді тексеріп, қайталап көріңіз."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Кеңейту жаңартулары дайын"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Жаңа кеңейту %s ішінде сәтті жасалды.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Атауы қысқа мәтіндік жол (сипаттайтын) болуы тиіс.\n"
+"Мысалы: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Аты"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Сипаттамасы - бұл сіздің кеңейтуіңіз жасайтын әрекеттің бір сөйлемді "
+"анықтамасы.\n"
+"Мысалы: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Сипаттамасы"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID - бұл кеңейтуңіздің глобалды деңгейдегі бірегей идентификаторы.\n"
+"Ол эл. пошта адресі пішімінде болуы тиіс (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Қол жетімді үлгілердің бірін таңдаңыз:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Үлгі"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Жаңа кеңейтудің бірегей идентификаторы"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "АТАУЫ"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Жаңа кеңейтудің пайдаланушыға көрінетін атауы"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "СИПАТТАМАСЫ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Кеңейтудің қысқаша сипаттамасы"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ҮЛГІ"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Жаңа кеңейту үшін қолданылатын үлгі"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Кеңейту ақпаратын интерактивті түрде енгізу"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Жаңа кеңейтуді жасау"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Белгісіз аргументтер"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, аты және сипаттамасы керек"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "GNOME Shell-ға байланысты орнату сәтсіз аяқталды\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "\"%s\" кеңейтуі жоқ\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Кеңейтуді сөндіру"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID көрсетілмеген"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Бірден көп UUID көрсетілген"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Кеңейтуді іске қосу"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "\"%s\" кеңейтуі жоқ\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Кеңейтулер ақпаратын көрсету"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Бар болып тұрған кеңейтуді үстінен жазу"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "КЕҢЕЙТУ_ДЕСТЕСІ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Кеңейту дестесін орнату"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Кеңейту дестесі көрсетілмеген"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Бірден көп кеңейту дестесі көрсетілген"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Пайдаланушы орнатқан кеңейтулерді көрсету"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Жүйелік деңгейде орнатылған кеңейтулерді көрсету"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Іске қосылған кеңейтулерді көрсету"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Сөндірілген кеңейтулерді көрсету"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Баптаулары бар кеңейтулерді көрсету"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Жаңартулары бар кеңейтулерді көрсету"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Кеңейту ақпаратын басып шығару"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Орнатылған кеңейтулерді тізіп шығару"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ФАЙЛ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Дестеге қосылатын қосымша қайнар көз"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "СҰЛБА"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Құрамына енуі тиіс GSettings сұлбасы"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "БУМА"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Аудармалар орналасқан бума"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ДОМЕН"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Аудармалар үшін қолданылуы тиіс gettext домені"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Бар болып тұрған пакетті үстінен жазу"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Десте жасалуы тиіс бума"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ҚАЙНАР_КӨЗ_БУМАСЫ"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Кеңейту дестесін жасау"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Бірден көп қайнар көз бумасы көрсетілген"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "\"%s\" кеңейтудің баптаулары жоқ\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "\"%s\" кеңейтуі үшін қолданбаларды ашу сәтсіз аяқталды: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Кеңейту баптауларын ашады"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Кеңейтуді қалпына келтіру"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Жүйелік кеңейтулерді өшіру мүмкін емес\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "\"%s\" өшіру сәтсіз аяқталды\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Кеңейтуді өшіру"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Қате туралы хабарламаларды басып шығармау"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Жол"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Бастапқы авторы"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Нұсқасы"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Қалып-күйі"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” аргументтерді қабылдамайды"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Қолданылуы:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Нұсқа ақпаратын басып шығару және шығу."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "КОМАНДА"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[АРГУМЕНТТЕР…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Командалар:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Көмекті басып шығару"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Кеңейтуді іске қосу"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Кеңейтуді сөндіру"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Кеңейтуді қалпына келтіру"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Кеңейтуді өшіру"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Кеңейтулерді тізіп шығару"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Кеңейту ақпаратын көрсету"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Кеңейту параметрлерін ашу"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Кеңейтуді жасау"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Кеңейтуді дестеге салу"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Кеңейту дестесін орнату"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Толық көмекті алу үшін \"%s\" пайдаланыңыз.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Қалыпты"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Бос кеңейту"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Индикатор"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Жоғарғы панельге таңбашаны қосу"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u шығысы"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u кірісі"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Жүйелік дыбыстар"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Үнсіз келісім бойынша Bluetooth адаптерінде байланысқан құрылғылар бар ма"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Қоршам Bluetooth мәзірін тек Bluetooth адаптері іске қосылған болса және "
+#~ "үнсіз келісім бойынша адаптерінде байланысқан құрылғылар бар болса ғана "
+#~ "көрсетеді. Бұл үнсіз келісім адаптерінде байланысқан құрылғылар жоқ болса "
+#~ "тасталады."
+
+#~ msgid "Log Out"
+#~ msgstr "Жүйеден шығу"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Bluetooth баптаулары"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d байланысқан"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth іске қосылған"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth сөндірілген"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Орналасу іске қосулы тұр"
+
+#~ msgid "Disable"
+#~ msgstr "Сөндіру"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Жекелік баптаулары"
+
+#~ msgid "Location In Use"
+#~ msgstr "Орналасу қолданылуда"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Орналасу сөндірулі тұр"
+
+#~ msgid "Enable"
+#~ msgstr "Іске қосу"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s сөндірулі"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s байланысқан"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s басқарылмайтын"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s байланысуда"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s аутентификацияны талап етеді"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s үшін бинарлы кодтары жоқ"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s қолжетерсіз"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s байланысын орнату сәтсіз"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Сымды желі баптаулары"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Сымсыз кеңжолақты желісінің баптаулары"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s құрылғылық түрде сөндірулі тұр"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s сөндірулі тұр"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Интернетке байланысу"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Ұшақтағы режим іске қосылған"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Wi-Fi ұшақтағы режимі кезінде сөндірілген болады."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Ұшақтағы режимді сөндіру"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi сөндірілген"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Желіге байланысты орнату үшін Wi-Fi іске қосылған болуы тиіс."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Wi-Fi іске қосу"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wi-Fi желілері"
+
+#~ msgid "Select a network"
+#~ msgstr "Желіні таңдау"
+
+#~ msgid "No Networks"
+#~ msgstr "Желілер жоқ"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Сөндіру үшін құрылғылық қосқышты қолданыңыз"
+
+#~ msgid "Select Network"
+#~ msgstr "Желіні таңдау"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wi-Fi баптаулары"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s байланыспаған"
+
+#~ msgid "connecting…"
+#~ msgstr "байланысты орнату…"
+
+#~ msgid "authentication required"
+#~ msgstr "аутентификация керек"
+
+#~ msgid "connection failed"
+#~ msgstr "байланысты орнату сәтсіз"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN баптаулары"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN сөндірілген"
+
+#~ msgid "Network Settings"
+#~ msgstr "Желі баптаулары"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s сымды желі байланысы"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Wi-Fi желі байланысы"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s модем желі байланысы"
+
+#~ msgid "Connection failed"
+#~ msgstr "Байланыс орнату сәтсіз"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Желілік байланысты белсендіру сәтсіз"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Түнгі жарық сөндірілген"
+
+#~ msgid "Resume"
+#~ msgstr "Жалғастыру"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Ертеңге дейін сөндіру"
+
+#~ msgid "Power Settings"
+#~ msgstr "Қорек баптаулары"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Толығымен зарядталған"
+
+#~ msgid "Not Charging"
+#~ msgstr "Зарядталуда емес"
+
+#~ msgid "Estimating…"
+#~ msgstr "Есептеу…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d қалды (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d толық зарядқа дейін (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Экранмен бөлісудесіз"
+
+#~ msgid "Turn off"
+#~ msgstr "Сөндіру"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Ұшақтағы режим іске қосулы"
+
+#~ msgid "Lock"
+#~ msgstr "Блоктау"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Сөндіру / Жүйеден шығу"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Интроспекция API іске қосу"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Қоршамның қолданба күйін интроспекциялауды мүмкін қылатын D-Bus API іске "
+#~ "қосу."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Таңдамалылардан өшіру"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Таңдамалыларға қосу"
+
+#~ msgid "Author"
+#~ msgstr "Авторы"
+
+#~ msgid "Warning"
+#~ msgstr "Ескерту"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Кеңейтулер жүйелік мәселелерді, соның ішінде өнімділік мәселелерін "
+#~ "туғызуы мүмкін. Жүйелік мәселелерге тап болсаңыз, барлық кеңейтулерді "
+#~ "сөндіру ұсынылады."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "GNOME Shell-ға байланысты орнату сәтсіз аяқталды"
+
+#~ msgid "Minimize"
+#~ msgstr "Қайыру"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Жазық емес қылу"
+
+#~ msgid "App Picker View"
+#~ msgstr "Қолданбаларды таңдау көрінісі"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Ағымдағы таңдалған көріністің қолданбаларды таңдау режиміндегі индексі."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Жиі қолданылатын қолданбалар осында көрінеді"
+
+#~ msgid "Frequent"
+#~ msgstr "Жиі қолданылатын"
+
+#~ msgid "All"
+#~ msgstr "Барлығы"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %B %-d"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %B %-d, %Y"
+
+#~ msgid "Off"
+#~ msgstr "Сөнд."
+
+#~ msgid "On"
+#~ msgstr "Іске қос."
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Барлық орындалып тұрған анимацияларды тоқтататын не жалғастыратын "
+#~ "пернелер жарлығы (жөндеу үшін)"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Қолданылатын пернетақта"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Қолданылатын пернетақта түрі."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "Copy Error"
+#~ msgstr "Көшіріп алу қатесі"
+
+#~ msgid "Browse in Software"
+#~ msgstr "БҚ қолданбасында шолу"
+
+#~ msgid "Next"
+#~ msgstr "Келесі"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Кіру"
+
+#~ msgid "Password:"
+#~ msgstr "Пароль:"
+
+#~ msgid "Type again:"
+#~ msgstr "Қайтадан енгізіңіз:"
+
+#~ msgid "Password: "
+#~ msgstr "Пароль: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Сымсыз желісі аутентификацияны талап етеді"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Сымсыз кеңжолақты желісінің паролі"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %B %d"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d жаңа хабарлама"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d жаңа ескерту"
+
+#~ msgid "Account Settings"
+#~ msgstr "Тіркелгі баптаулары"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Бағдарды бекіту"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "%s үшін баптаулар сұхбатын жүктеу кезінде қате орын алды:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s күні бойы."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, одан кейін %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, одан кейін %s, одан кейін %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "%s сияқты сезіледі."
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Жүйеден шығу"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Пайдаланушыны ауыстыру"
+
+#~ msgid "Hide tray"
+#~ msgstr "Жүйелік трейді жасыру"
+
+#~ msgid "Status Icons"
+#~ msgstr "Қалып-күй таңбашалары"
+
+#~ msgid "Events"
+#~ msgstr "Оқиғалар"
+
+#~ msgid "Notifications"
+#~ msgstr "Хабарламалар"
+
+#~ msgid "Media"
+#~ msgstr "Медиа"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Веб аутентификацияның қайта бағдарлауы"
+
+#~ msgid "Not In Use"
+#~ msgstr "Қолданылуда емес"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Күнтізбеде апта күнін көрсету"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Ақиқат болса, күнтізбеде ISO апта күнін көрсетеді."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Интернетпен байланыс ретінде қолдану"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s орналасу ақпаратыңызға қатынауды сұрайды."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (wayland үйлестіргіші)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d байланысқан құрылғы"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Батарея"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgid "Invitation"
+#~ msgstr "Шақыру"
+
+#~ msgid "Call"
+#~ msgstr "Қоңырау"
+
+#~ msgid "File Transfer"
+#~ msgstr "Файлдармен алмасу"
+
+#~ msgid "Chat"
+#~ msgstr "Чат"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "%s ішіне шақыру"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s сізді %s ішіне қосылуды шақырады"
+
+#~ msgid "Decline"
+#~ msgstr "Тайдыру"
+
+#~ msgid "Accept"
+#~ msgstr "Қабылдау"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Видео қоңырау: %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Қоңырау: %s"
+
+#~ msgid "Answer"
+#~ msgstr "Жауап"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s сізге %s жіберуде"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s сіз қашан желіде болатыныңызды білу құқығын сұрап тұр"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Аутентификация сәтсіз"
+
+#~ msgid "Encryption error"
+#~ msgstr "Шифрлеу қатесі"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Сертификат ұсынылмады"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Сертификат сенімсіз"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Сертификаттың мерзімі аяқталған"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Сертификат белсендірілмеген"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Сертификаттың хост аты сәйкес емес"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Сертификаттың баспасы сәйкес емес"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Сертификат өздігінен қолтаңбаланған"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Қалып-күй желіде емес етіп орнатылды"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Сертификат дұрыс емес"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Сертификат тайдырылды"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Байланысты орнату мүмкін емес"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Байланыс жоғалтылды"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Бұл тіркелгі серверге байланысқан болып тұр"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Байланыс дәл сол ресурсты қолданып, жаңа байланыспен алмастырылды"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Бұл тіркелгі серверде бар болып тұр"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Сервер бұл байланысты өңдеу үшін тым бос емес"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Сертификат қайта шақырылған"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Сертификат қауіпсіз емес шифрлеу алгоритмін қолдануда немесе "
+#~ "криптографиялы әлсіз"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Сервер сертификатының ұзындығы, не ол сертификат тізбегінің ұзындығы "
+#~ "криптографиялық жинақ ұсына алатын шегінен асып тұр"
+
+#~ msgid "Internal error"
+#~ msgstr "Ішкі қате"
+
+#~ msgid "View account"
+#~ msgstr "Тіркелгіні қарап шығу"
+
+#~ msgid "Show the message list"
+#~ msgstr "Хабарламалар тізімін көрсету"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Жк"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Бс"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Сс"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Ср"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Бс"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Жм"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Сн"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Жоспар бос"
+
+#~ msgid "This week"
+#~ msgstr "Осы аптада"
+
+#~ msgid "Next week"
+#~ msgstr "Келесі аптада"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Ауыстырмалы құрылғылар"
+
+#~ msgid "Eject"
+#~ msgstr "Шығару"
+
+#~ msgid "Unmute"
+#~ msgstr "Дыбысты қайтару"
+
+#~ msgid "Mute"
+#~ msgstr "Дыбысын өшіру"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Күнтізбені ашу"
+
+#~ msgid "Open"
+#~ msgstr "Ашу"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Хабарламаларды тазарту"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Хабарлау баптаулары"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Трей мәзірі"
+
+#~ msgid "Message Tray"
+#~ msgstr "Жүйелік трей"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Captive-портал"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Орналасудың максималды дәлдік деңгейі."
diff --git a/po/km.po b/po/km.po
new file mode 100644
index 0000000..4347655
--- /dev/null
+++ b/po/km.po
@@ -0,0 +1,1740 @@
+# Khmer translation for gnome-shell.
+# Copyright (C) 2014 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Khoem Sokhem <sokhem@open.org.kh>, 2014.
+# Sophea Sok <sophea@open.org.kh>, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-04-22 07:25+0000\n"
+"PO-Revision-Date: 2014-04-03 10:34+0700\n"
+"Last-Translator: Sophea Sok <sophea@open.org.kh>\n"
+"Language-Team: Khmer <>\n"
+"Language: km\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: WordForge 0.8 RC1\n"
+"X-Language: km-KH\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "ប្រព័ន្ធ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "បង្ហាញ​ថាស​សារ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "ផ្ដោត​លើ​ការ​ជូនដំណឹង​សកម្ម"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "បង្ហាញ​ទិដ្ឋភាព​ទូទៅ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "បង្ហាញ​កម្មវិធី​ទាំងអស់"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "បើក​ម៉ឺនុយ​កម្មវិធី"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME សែល"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "ការ​គ្រប់គ្រង​វីនដូ និង​ការ​ចាប់ផ្ដើម​កម្មវិធី"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extension Preferences"
+msgstr "ចំណូលចិត្ត​ផ្នែក​បន្ថែម​របស់ GNOME សែល"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "កំណត់​រចនាសម្ព័ន្ធ​ផ្នែក​បន្ថែម​របស់ GNOME សែល"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME សែល (wayland compositor)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "បើក​ឧបករណ៍​ខាងក្នុង មាន​ប្រយោជន៍​សម្រាប់​អ្នក​អភិវឌ្ឍន៍ និង​អ្នក​សាកល្បង​ចេញពី Alt-F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "អនុញ្ញាត​ឲ្យ​ចូល​ដំណើរការ​ឧបករណ៍​តាមដាន និង​ឧបករណ៍​បំបាត់​កំហុស​ខាងក្នុង​ដោយ​ប្រើ​ប្រអប់ Alt-F2 ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs នៃ​ផ្នែក​បន្ថែម​ដែល​ត្រូវ​បើក"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"ផ្នែក​បន្ថែម​របស់ GNOME សែល​ដែល​មាន​លក្ខណសម្បត្តិ UUID ។ ពាក្យ​គន្លឹះ​នេះ​គឺ​រាយ​ផ្នែក​បន្ថែម​ដែល​គួរ​ត្រូវ​"
+"បាន​ផ្ទុក។ ផ្នែក​បន្ថែម​ណា​ដែល​ត្រូវ​ផ្ទុក គឺ​ត្រូវតែ​នៅ​ក្នុង​បញ្ជី​នេះ។ អ្នក​ក៏​អាច​រៀបចំ​បញ្ជី​នេះ​ដោយ​ប្រើ​"
+"វិធីសាស្ត្រ EnableExtension និង DisableExtension D-Bus បាន​ផង​ដែរ​នៅ​លើ org.gnome."
+"Shell ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "បិទ​សុពលកម្ម​នៃ​ភាព​ឆបគ្នា​របស់​កំណែ​ផ្នែក​បន្ថែម"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME សែល​នឹង​ផ្ទុក​តែ​ផ្នែក​បន្ថែម​ដែល​ប្រកាស​ដើម្បី​គាំទ្រ​កំណែ​កំពុង​ដំណើរការ​បច្ចុប្បន្ន​ប៉ុណ្ណោះ។ ការ​បើក​"
+"ជម្រើស​នេះ គឺ​វា​នឹង​បិទ​ការ​ពិនិត្យមើល​នេះ ហើយ​ព្យាយាម​ផ្ទុក​ផ្នែក​បន្ថែម​ទាំងអស់​ដោយ​មិន​គិត​អំពី​កំណែ​ដែល​ពួកគេ​"
+"ប្រកាស​ដើម្បី​គាំទ្រ​ឡើយ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "បញ្ជី​លេខ​សម្គាល់​ឯកសារ​ផ្ទៃតុ​សម្រាប់​កម្មវិធី​សំណព្វ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "កម្មវិធី​ដែល​ត្រូវគ្នា​ជាមួយ​កម្មវិធី​កំណត់​អត្តសញ្ញាណ​ទាំងនេះ នឹង​ត្រូវ​បាន​បង្ហាញ​នៅ​ក្នុង​ផ្ទៃ​សំណព្វ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "History for command (Alt-F2) dialog"
+msgstr "ប្រវត្តិ​សម្រាប់​ប្រអប់​ពាក្យ​បញ្ជា (Alt-F2)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "History for the looking glass dialog"
+msgstr "ប្រវត្តិ​សម្រាប់​ប្រអប់ looking glass"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "បង្ហាញ​ធាតុ​ម៉ឺនុយ 'ចេញ' ជានិច្ច​នៅ​ក្នុង​ម៉ឺនុយ​អ្នកប្រើ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"ពាក្យ​គន្លឹះ​នេះ​បដិសេធ​ការ​លាក់​ធាតុ​ម៉ឺនុយ 'ចេញ' ស្វ័យប្រវត្តិ​នៅ​ក្នុង​ស្ថានភាព​អ្នកប្រើ​តែមួយ សម័យ​តែមួយ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"ថា​តើ​ត្រូវ​ចងចាំ​ពាក្យសម្ងាត់​សម្រាប់​ការ​ម៉ោន​ប្រព័ន្ធ​ឯកសារ​ពី​ចម្ងាយ ឬ​ឯកសារ​ដែល​បាន​ដាក់​លេខ​កូដ​ដែរ​ឬទេ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"សែល​នឹង​ស្នើ​ពាក្យសម្ងាត់ នៅ​ពេល​ដែល​ឧបករណ៍​ដាក់​លេខ​កូដ ឬ​ប្រព័ន្ធ​ឯកសារ​ពី​ចម្ងាយ​ត្រូវ​បាន​ម៉ោន។ ប្រសិនបើ​"
+"ពាក្យសម្ងាត់​អាច​ត្រូវ​បាន​រក្សាទុក​សម្រាប់​ប្រើ​នៅ​ពេល​អនាគត ប្រអប់​ធីក 'ចងចាំ​ពាក្យសម្ងាត់' នឹង​បង្ហាញ។ "
+"ពាក្យ​គន្លឹះ​នេះ​កំណត់​សភាព​លំនាំដើម​នៃ​ប្រអប់​ធីក។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid "Show the week date in the calendar"
+msgstr "បង្ហាញ​កាលបរិច្ឆេទ​សប្ដាហ៍​នៅ​ក្នុង​ប្រតិទិន"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid "If true, display the ISO week date in the calendar."
+msgstr "បើ​ពិត បង្ហាញ​កាលបរិច្ឆេទ​សប្ដាហ៍ ISO នៅ​ក្នុង​ប្រតិទិន។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Keybinding to open the application menu"
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បើក​ម៉ឺនុយ​កម្មវិធី"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "Keybinding to open the application menu."
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បើក​ម៉ឺនុយ​កម្មវិធី។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បើក​ទិដ្ឋភាព \"កម្មវិធី​បង្ហាញ\""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បើក​ទិដ្ឋភាព \"កម្មវិធី​បង្ហាញ\" របស់​ទិដ្ឋភាព​សកម្មភាព។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the overview"
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បើក​ទិដ្ឋភាព​ទូទៅ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Keybinding to open the Activities Overview."
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បើក​ទិដ្ឋភាព​សកម្មភាព។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បិទ/បើក​លទ្ធភាព​មើល​ឃើញ​របស់​ថាស​សារ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​បិទ/បើក​លទ្ធភាព​មើល​ឃើញ​របស់​ថាស​សារ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to focus the active notification"
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​ផ្ដោត​លើ​ការ​ជូនដំណឹង​សកម្ម"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to focus the active notification."
+msgstr "បង្គំ​គ្រាប់ចុច​ដើម្បី​ផ្ដោត​លើ​ការ​ជូនដំណឹង​សកម្ម។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Which keyboard to use"
+msgstr "ក្ដារចុច​ដែល​ត្រូវ​ប្រើ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "The type of keyboard to use."
+msgstr "ប្រភេទ​ក្ដារចុច​ដែល​ត្រូវ​ប្រើ។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid "The maximum accuracy level of location."
+msgstr "កម្រិត​សុក្រឹតភាព​អតិបរមា​នៃ​ទីតាំង។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid ""
+"Configures the maximum level of location accuracy applications are allowed "
+"to see. Valid options are 'off' (disable location tracking), 'country', "
+"'city', 'neighborhood', 'street', and 'exact' (typically requires GPS "
+"receiver). Please keep in mind that this only controls what GeoClue will "
+"allow applications to see and they can find user's location on their own "
+"using network resources (albeit with street-level accuracy at best)."
+msgstr ""
+"កំណត់​រចនាសម្ព័ន្ធ​កម្រិត​អតិបរមា​របស់​កម្មវិធី​សុក្រឹតភាព​ទីតាំង​ដែល​បាន​អនុញ្ញាត​ឲ្យ​មើល​ឃើញ។ ជម្រើស​ដែល​ត្រឹមត្រូវ​"
+"គឺ 'off' (បិទ​ការ​តាមដាន​ទីតាំង), 'country', 'city', 'neighborhood', 'street', និង "
+"'exact' (ត្រូវ​មានឧបករណ៍​ទទួល GPS) ។ សូម​ចងចាំ​ថា​វា​គ្រប់គ្រង​អ្វី​ដែល GeoClue នឹង​អនុញ្ញាត​ឲ្យ​កម្មវិធី​"
+"មើល​ឃើញ ហើយ​ពួកគេ​អាច​រក​ឃើញ​ទីតាំង​របស់​អ្នកប្រើ​ដោយ​ប្រើ​ធនធាន​បណ្ដាញ​ផ្ទាល់ខ្លួន (សូម្បីតែ​សុក្រឹតភាព​កម្រិត​"
+"ផ្លូវ​ក៏​ល្អ​ដែរ)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The application icon mode."
+msgstr "របៀប​រូបតំណាង​កម្មវិធី។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"កំណត់​រចនាសម្ព័ន្ធ​អំពី​របៀប​បង្ហាញ​បង្អួច​នៅ​ក្នុង​កម្មវិធី​ប្ដូរ។ តម្លៃ​ត្រឹមត្រូវ​ដែល​អាច​ប្រើ​បាន​គឺ 'thumbnail-"
+"only' (បង្ហាញ​រូបភាព​តូច​របស់​បង្អួច), 'app-icon-only' (បង្ហាញ​តែ​រូបតំណាង​កម្មវិធី​ប៉ុណ្ណោះ) ឬ "
+"'both'។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid "Attach modal dialog to the parent window"
+msgstr "ភ្ជាប់​ប្រអប់​បែបបទ​ទៅ​បង្អួច​មេ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "ពាក្យ​គន្លឹះ​នេះ​បដិសេធ​ពាក្យ​គន្លឹះ​នៅ​ក្នុង org.gnome.mutter ពេល​ដំណើរការ GNOME សែល។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid "Arrangement of buttons on the titlebar"
+msgstr "ការ​រៀបចំ​ប៊ូតុង​នៅ​លើ​របារ​ចំណងជើង"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"This key overrides the key in org.gnome.desktop.wm.preferences when running "
+"GNOME Shell."
+msgstr ""
+"ពាក្យ​គន្លឹះ​នេះ​បដិសេធ​ពាក្យ​គន្លឹះ​នៅ​ក្នុង org.gnome.desktop.wm.preferences ពេល​ដំណើរការ "
+"GNOME សែល។"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "បើក​គម្រប​គែម​ពេល​ទម្លាក់​បង្អួច​នៅ​លើ​គែម​អេក្រង់"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid "Workspaces are managed dynamically"
+msgstr "តំបន់​ធ្វើការ​ត្រូវ​បាន​គ្រប់គ្រង​ជា​ថាមវន្ត"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Workspaces only on primary monitor"
+msgstr "តំបន់​ធ្វើការ​មានតែ​នៅ​លើ​ម៉ូនីទ័រ​មេ​ប៉ុណ្ណោះ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "ប្ដូរ​ការ​ពន្យារពេល​ផ្ដោត​នៅ​ក្នុង​របៀប​កណ្ដុរ​រហូតដល់​ព្រួញ​កណ្ដុរ​លែង​ផ្លាស់ទី"
+
+#: ../js/extensionPrefs/main.js:127
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "មាន​កំហុស​ពេល​ផ្ទុក​ប្រអប់​ចំណូលចិត្ត​សម្រាប់ %s ៖"
+
+#: ../js/extensionPrefs/main.js:167
+msgid "Extension"
+msgstr "ផ្នែក​បន្ថែម"
+
+#: ../js/extensionPrefs/main.js:191
+msgid "Select an extension to configure using the combobox above."
+msgstr "ជ្រើស​ផ្នែក​បន្ថែម​ដើម្បី​កំណត់​រចនាសម្ព័ន្ធ​ដោយ​ប្រើ​​ប្រអប់​បន្សំ​ខាង​លើ។"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:136
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:429
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:878
+msgid "Cancel"
+msgstr "បោះបង់"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "បន្ទាប់"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "ដោះ​សោ"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "ចូល"
+
+#: ../js/gdm/loginDialog.js:271
+msgid "Choose Session"
+msgstr "ជ្រើស​សម័យ"
+
+#: ../js/gdm/loginDialog.js:431
+msgid "Not listed?"
+msgstr "មិន​បាន​រាយ?"
+
+#: ../js/gdm/loginDialog.js:616
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ឧ., អ្នកប្រើ ឬ %s)"
+
+#: ../js/gdm/loginDialog.js:621 ../js/ui/components/networkAgent.js:262
+#: ../js/ui/components/networkAgent.js:280
+msgid "Username: "
+msgstr "ឈ្មោះ​អ្នកប្រើ៖"
+
+#: ../js/gdm/loginDialog.js:924
+msgid "Login Window"
+msgstr "បង្អួច​ចូល"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "កំហុស​ការ​ផ្ទៀងផ្ទាត់"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(ឬ​អូស​ម្រាមដៃ)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "រក​មិន​ឃើញ​ពាក្យ​បញ្ជា"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "មិន​អាច​ញែក​ពាក្យ​បញ្ជា៖"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "ការ​ប្រតិបត្តិ “%s” បាន​បរាជ័យ៖"
+
+#: ../js/ui/appDisplay.js:636
+msgid "Frequently used applications will appear here"
+msgstr "កម្មវិធី​ដែល​បាន​ប្រើ​ញឹកញាប់​នឹង​បង្ហាញ​នៅ​ទីនេះ"
+
+#: ../js/ui/appDisplay.js:747
+msgid "Frequent"
+msgstr "ញឹកញាប់"
+
+#: ../js/ui/appDisplay.js:754
+msgid "All"
+msgstr "ទាំងអស់"
+
+#: ../js/ui/appDisplay.js:1567
+msgid "New Window"
+msgstr "បង្អួច​ថ្មី"
+
+#: ../js/ui/appDisplay.js:1589 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "យកចេញ​ពី​សំណព្វ"
+
+#: ../js/ui/appDisplay.js:1595
+msgid "Add to Favorites"
+msgstr "បន្ថែម​ទៅ​សំណព្វ"
+
+#: ../js/ui/appFavorites.js:87
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "បាន​បន្ថែម %s ទៅកាន់​សំណព្វ​របស់​អ្នក។"
+
+#: ../js/ui/appFavorites.js:121
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "បាន​យក %s ចេញពី​សំណព្វ​របស់​អ្នក។"
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:810
+#: ../js/ui/status/system.js:334
+msgid "Settings"
+msgstr "ការ​កំណត់"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "ប្ដូរ​ផ្ទៃ​ខាងក្រោយ..."
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:64
+msgctxt "event list time"
+msgid "All Day"
+msgstr "ពេញ​មួយ​ថ្ងៃ"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:70
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:79
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:110
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:112
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:114
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:116
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:118
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:120
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:122
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:135
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Su"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:137
+msgctxt "list monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:139
+msgctxt "list tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:141
+msgctxt "list wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:143
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Th"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:145
+msgctxt "list friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:147
+msgctxt "list saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:390
+msgid "calendar:MY"
+msgstr "calendar:MY"
+
+#: ../js/ui/calendar.js:450
+msgid "Previous month"
+msgstr "ខែ​មុន"
+
+#: ../js/ui/calendar.js:460
+msgid "Next month"
+msgstr "ខែ​បន្ទាប់"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:772
+msgid "Nothing Scheduled"
+msgstr "គ្មាន​កាលវិភាគ"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:790
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:793
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:804
+msgid "Today"
+msgstr "ថ្ងៃនេះ"
+
+#: ../js/ui/calendar.js:808
+msgid "Tomorrow"
+msgstr "ថ្ងៃស្អែក"
+
+#: ../js/ui/calendar.js:819
+msgid "This week"
+msgstr "សប្ដាហ៍​នេះ"
+
+#: ../js/ui/calendar.js:827
+msgid "Next week"
+msgstr "សប្ដាហ៍​បន្ទាប់"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "បាន​ភ្ជាប់​ដ្រាយ​ខាងក្រៅ"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "បាន​ផ្ដាច់​ដ្រាយ​ខាងក្រៅ"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "ឧបករណ៍​ចល័ត"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "បើក​ជាមួយ %s"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "ច្រានចេញ"
+
+#: ../js/ui/components/keyring.js:91 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "ពាក្យសម្ងាត់៖"
+
+#: ../js/ui/components/keyring.js:110
+msgid "Type again:"
+msgstr "វាយ​ម្ដងទៀត៖"
+
+#: ../js/ui/components/networkAgent.js:131 ../js/ui/status/network.js:240
+#: ../js/ui/status/network.js:322 ../js/ui/status/network.js:881
+msgid "Connect"
+msgstr "តភ្ជាប់"
+
+#: ../js/ui/components/networkAgent.js:224
+#: ../js/ui/components/networkAgent.js:236
+#: ../js/ui/components/networkAgent.js:264
+#: ../js/ui/components/networkAgent.js:284
+#: ../js/ui/components/networkAgent.js:294
+msgid "Password: "
+msgstr "ពាក្យសម្ងាត់៖"
+
+#: ../js/ui/components/networkAgent.js:229
+msgid "Key: "
+msgstr "សោ៖"
+
+#: ../js/ui/components/networkAgent.js:268
+msgid "Identity: "
+msgstr "អត្តសញ្ញាណ៖"
+
+#: ../js/ui/components/networkAgent.js:270
+msgid "Private key password: "
+msgstr "ពាក្យសម្ងាត់​សោ​ឯកជន៖"
+
+#: ../js/ui/components/networkAgent.js:282
+msgid "Service: "
+msgstr "សេវាកម្ម៖"
+
+#: ../js/ui/components/networkAgent.js:311
+msgid "Authentication required by wireless network"
+msgstr "បាន​ទាមទារ​ការ​ផ្ទៀងផ្ទាត់​តាម​បណ្ដាញ​ឥតខ្សែ"
+
+#: ../js/ui/components/networkAgent.js:312
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "បាន​ទាមទារ​ពាក្យសម្ងាត់ ឬ​សោ​លេខ​កូដ​ដើម្បី​ចូល​ដំណើរការ​បណ្ដាញ​ឥត​ខ្សែ “%s” ។"
+
+#: ../js/ui/components/networkAgent.js:316
+msgid "Wired 802.1X authentication"
+msgstr "ការ​ផ្ទៀង​ផ្ទាត់​ខ្សែ 802.1X"
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Network name: "
+msgstr "ឈ្មោះ​បណ្ដាញ៖"
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "DSL authentication"
+msgstr "ការ​ផ្ទៀងផ្ទាត់ DSL"
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "PIN code required"
+msgstr "បាន​ទាមទារ​លេខ​កូដ PIN"
+
+#: ../js/ui/components/networkAgent.js:331
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "បាន​ទាមទារ​លេខ​កូដ PIN សម្រាប់​ឧបករណ៍​រលក​អាកាស​ធំ​ចល័ត"
+
+#: ../js/ui/components/networkAgent.js:332
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "Mobile broadband network password"
+msgstr "ពាក្យសម្ងាត់​បណ្ដាញ​រលក​អាកាស​ធំ​ចល័ត"
+
+#: ../js/ui/components/networkAgent.js:339
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "បាន​ទាមទារ​ពាក្យសម្ងាត់​ដើម្បី​តភ្ជាប់​ទៅ “%s” ។"
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "បាន​ទាមទារ​ការ​ផ្ទៀងផ្ទាត់"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "អ្នក​គ្រប់គ្រង"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "ផ្ទៀងផ្ទាត់"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "សុំទោស​ដែល​មិន​ដំណើរការ។ សូម​ព្យាយាម​ម្ដងទៀត។"
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "ការ​អញ្ជើញ"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "ហៅ"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "ផ្ទេរ​ឯកសារ"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "ជជែក"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "បើក​សំឡេង"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "បិទ​សំឡេង"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:958
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:965
+msgid "Yesterday, %H∶%M"
+msgstr "ម្សិលមិញ %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:972
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:979
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:985
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:994
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1001
+msgid "Yesterday, %l∶%M %p"
+msgstr "ម្សិលមិញ %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1008
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1015
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1021
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1054
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s បាន​ស្គាល់​ជា %s"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1158
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "ការ​អញ្ជើញ​មកកាន់ %s"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1166
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s អញ្ជើញ​អ្នក​ឲ្យ​ចូលរួម %s"
+
+#: ../js/ui/components/telepathyClient.js:1168
+#: ../js/ui/components/telepathyClient.js:1203
+#: ../js/ui/components/telepathyClient.js:1237
+#: ../js/ui/components/telepathyClient.js:1295
+msgid "Decline"
+msgstr "បដិសេធ"
+
+#: ../js/ui/components/telepathyClient.js:1174
+#: ../js/ui/components/telepathyClient.js:1243
+#: ../js/ui/components/telepathyClient.js:1300
+msgid "Accept"
+msgstr "ព្រម​ទទួល"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1193
+#, javascript-format
+msgid "Video call from %s"
+msgstr "ហៅ​ជា​វីដេអូ​ពី %s"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1196
+#, javascript-format
+msgid "Call from %s"
+msgstr "ហៅ​ពី %s"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1210
+msgid "Answer"
+msgstr "ឆ្លើយ"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1231
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s កំពុង​ផ្ញើ %s ឲ្យ​អ្នក"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1260
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s ចង់​បាន​សិទ្ធិ​ដើម្បី​មើល ពេល​អ្នក​នៅ​ក្រៅ​បណ្ដាញ"
+
+#: ../js/ui/components/telepathyClient.js:1346
+msgid "Network error"
+msgstr "កំហុស​បណ្ដាញ"
+
+#: ../js/ui/components/telepathyClient.js:1348
+msgid "Authentication failed"
+msgstr "ការ​ផ្ទៀងផ្ទាត់​បាន​បរាជ័យ"
+
+#: ../js/ui/components/telepathyClient.js:1350
+msgid "Encryption error"
+msgstr "កំហុស​ការ​ដាក់​លេខ​កូដ"
+
+#: ../js/ui/components/telepathyClient.js:1352
+msgid "Certificate not provided"
+msgstr "មិន​បាន​ផ្ដល់​វិញ្ញាបនបត្រ"
+
+#: ../js/ui/components/telepathyClient.js:1354
+msgid "Certificate untrusted"
+msgstr "វិញ្ញាបនបត្រ​មិន​ទុកចិត្ត"
+
+#: ../js/ui/components/telepathyClient.js:1356
+msgid "Certificate expired"
+msgstr "វិញ្ញាបនបត្រ​ផុត​កំណត់"
+
+#: ../js/ui/components/telepathyClient.js:1358
+msgid "Certificate not activated"
+msgstr "វិញ្ញាបនបត្រ​មិន​បាន​ធ្វើឲ្យ​សកម្ម"
+
+#: ../js/ui/components/telepathyClient.js:1360
+msgid "Certificate hostname mismatch"
+msgstr "វិញ្ញាបនបត្រ​ឈ្មោះ​ម៉ាស៊ីន​មិន​ផ្គូផ្គង"
+
+#: ../js/ui/components/telepathyClient.js:1362
+msgid "Certificate fingerprint mismatch"
+msgstr "វិញ្ញាបនបត្រ​ស្នាម​ម្រាមដៃ​មិន​ផ្គូផ្គង"
+
+#: ../js/ui/components/telepathyClient.js:1364
+msgid "Certificate self-signed"
+msgstr "វិញ្ញាបនបត្រ​ចុះហត្ថលេខា​ខ្លួន​ឯង"
+
+#: ../js/ui/components/telepathyClient.js:1366
+msgid "Status is set to offline"
+msgstr "បាន​កំណត់​ស្ថានភាព​ទៅ​ក្រៅ​បណ្ដាញ"
+
+#: ../js/ui/components/telepathyClient.js:1368
+msgid "Encryption is not available"
+msgstr "មិន​មាន​ការ​ដាក់​លេខ​កូដ"
+
+#: ../js/ui/components/telepathyClient.js:1370
+msgid "Certificate is invalid"
+msgstr "វិញ្ញាបនបត្រ​មិន​ត្រឹមត្រូវ"
+
+#: ../js/ui/components/telepathyClient.js:1372
+msgid "Connection has been refused"
+msgstr "បាន​បដិសេធ​ការ​តភ្ជាប់"
+
+#: ../js/ui/components/telepathyClient.js:1374
+msgid "Connection can't be established"
+msgstr "មិន​អាច​បង្កើត​ការ​តភ្ជាប់"
+
+#: ../js/ui/components/telepathyClient.js:1376
+msgid "Connection has been lost"
+msgstr "ការ​តភ្ជាប់​បាន​ដាច់"
+
+#: ../js/ui/components/telepathyClient.js:1378
+msgid "This account is already connected to the server"
+msgstr "គណនី​នេះ​បាន​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​មេ​រួចហើយ"
+
+#: ../js/ui/components/telepathyClient.js:1380
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "បាន​ជំនួស​ការ​តភ្ជាប់​ថ្មី​ដោយ​ប្រើ​ធនធាន​ដូចគ្នា"
+
+#: ../js/ui/components/telepathyClient.js:1382
+msgid "The account already exists on the server"
+msgstr "គណនី​មាន​នៅ​លើ​ម៉ាស៊ីន​មេ​រួចហើយ"
+
+#: ../js/ui/components/telepathyClient.js:1384
+msgid "Server is currently too busy to handle the connection"
+msgstr "ម៉ាស៊ីន​មេ​កំពុង​រវល់​ក្នុង​ការ​តភ្ជាប់"
+
+#: ../js/ui/components/telepathyClient.js:1386
+msgid "Certificate has been revoked"
+msgstr "បាន​ដកហូត​វិញ្ញាបនបត្រ"
+
+#: ../js/ui/components/telepathyClient.js:1388
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr "វិញ្ញាបនបត្រ​ប្រើ​ក្បួន​ដោះស្រាយ​ការ​សរសេរ​សម្ងាត់​ដែល​គ្មាន​សុវត្ថិភាព ឬ​ការ​ដាក់​លេខ​កូដ​ខ្សោយ"
+
+#: ../js/ui/components/telepathyClient.js:1390
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"ប្រវែង​វិញ្ញាបនបត្រ​ម៉ាស៊ីន​មេ ឬ​ជម្រៅ​ច្រវាក់​វិញ្ញាបនបត្រ​ម៉ាស៊ីន​មេ​បាន​លើស​ដែន​កំណត់​ដែល​បាន​បង្ខំ​ដោយ​បណ្ណាល័យ​"
+"ការ​ដាក់​លេខ​កូដ"
+
+#: ../js/ui/components/telepathyClient.js:1392
+msgid "Internal error"
+msgstr "កំហុស​ខាងក្នុង"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1402
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "មិន​អាច​តភ្ជាប់​ទៅ %s"
+
+#: ../js/ui/components/telepathyClient.js:1407
+msgid "View account"
+msgstr "មើល​គណនី"
+
+#: ../js/ui/components/telepathyClient.js:1444
+msgid "Unknown reason"
+msgstr "មិន​ស្គាល់​មូលហេតុ"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96
+msgid "Windows"
+msgstr "វីនដូ"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "បង្ហាញ​កម្មវិធី"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "ដាច់ៗ"
+
+#: ../js/ui/dateMenu.js:91
+msgid "Open Calendar"
+msgstr "បើក​ប្រតិទិន"
+
+#: ../js/ui/dateMenu.js:95
+msgid "Open Clocks"
+msgstr "បើក​នាឡិកា"
+
+#: ../js/ui/dateMenu.js:102
+msgid "Date & Time Settings"
+msgstr "ការ​កំណត់​កាលបរិច្ឆេទ និង​ពេលវេលា"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:192
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:66
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "ចេញ %s"
+
+#: ../js/ui/endSessionDialog.js:67
+msgctxt "title"
+msgid "Log Out"
+msgstr "ចេញ"
+
+#: ../js/ui/endSessionDialog.js:69
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s នឹង​ចេញ​ដោយ​ស្វ័យប្រវត្តិ​ក្នុង​រយៈពេល %d វិនាទី។"
+
+#: ../js/ui/endSessionDialog.js:74
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "អ្នក​នឹង​ចេញ​ដោយ​ស្វ័យប្រវត្តិ​ក្នុង​រយៈពេល %d វិនាទី។"
+
+#: ../js/ui/endSessionDialog.js:80
+msgctxt "button"
+msgid "Log Out"
+msgstr "ចេញ"
+
+#: ../js/ui/endSessionDialog.js:86
+msgctxt "title"
+msgid "Power Off"
+msgstr "បិទ"
+
+#: ../js/ui/endSessionDialog.js:87
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "ដំឡើង​បច្ចុប្បន្នភាព រួច​បិទ"
+
+#: ../js/ui/endSessionDialog.js:89
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "ប្រព័ន្ធ​នឹង​បិទ​ដោយ​ស្វ័យប្រវត្តិ​ក្នុង​រយៈពេល %d វិនាទី។"
+
+#: ../js/ui/endSessionDialog.js:93
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "ដំឡើង​បច្ចុប្បន្នភាព​កម្មវិធី​មិនទាន់​សម្រេច"
+
+#: ../js/ui/endSessionDialog.js:96 ../js/ui/endSessionDialog.js:113
+msgctxt "button"
+msgid "Restart"
+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
+
+#: ../js/ui/endSessionDialog.js:98
+msgctxt "button"
+msgid "Power Off"
+msgstr "បិទ"
+
+#: ../js/ui/endSessionDialog.js:105
+msgctxt "title"
+msgid "Restart"
+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
+
+#: ../js/ui/endSessionDialog.js:107
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "ប្រព័ន្ធ​នឹង​ចាប់ផ្ដើម​ឡើងវិញ​ដោយ​ស្វ័យប្រវត្តិ​ក្នុង​រយៈពេល %d វិនាទី។"
+
+#: ../js/ui/endSessionDialog.js:121
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "ចាប់ផ្ដើម​ឡើងវិញ រួច​ដំឡើង​បច្ចុប្បន្នភាព"
+
+#: ../js/ui/endSessionDialog.js:123
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "ប្រព័ន្ធ​នឹង​ចាប់ផ្ដើម​ឡើងវិញ រួច​ដំឡើង​បច្ចុប្បន្នភាព​ដោយ​ស្វ័យប្រវត្តិ​ក្នុង​រយៈពេល %d វិនាទី។"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "ចាប់ផ្ដើម​ឡើងវិញ &amp; ដំឡើង"
+
+#: ../js/ui/endSessionDialog.js:130
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ដំឡើង &amp; បិទ"
+
+#: ../js/ui/endSessionDialog.js:131
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "បិទ​បន្ទាប់ពី​បាន​ដំឡើង​បច្ចុប្បន្នភាព"
+
+#: ../js/ui/endSessionDialog.js:315
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "កំពុង​ដំណើរការ​ដោយសារ​ថាមពល​ថ្ម៖ សូម​ដោត​ខ្សែ​មុន​នឹង​ដំឡើង​បច្ចុប្បន្នភាព។"
+
+#: ../js/ui/endSessionDialog.js:332
+msgid "Some applications are busy or have unsaved work."
+msgstr "កម្មវិធី​មួយ​ចំនួន​កំពុង​រវល់ ឬ​មាន​កិច្ចការ​មិនទាន់​រក្សាទុក។"
+
+#: ../js/ui/endSessionDialog.js:339
+msgid "Other users are logged in."
+msgstr "អ្នកប្រើ​ផ្សេង​បាន​ចូល។"
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:619
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (ពី​ចម្ងាយ)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:622
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (កុងសូល)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "ដំឡើង"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "ទាញ​យក និង​ដំឡើង “%s” ពី extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:645 ../js/ui/status/keyboard.js:335
+msgid "Keyboard"
+msgstr "ក្ដារចុច"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "មិន​បាន​ដំឡើង​ផ្នែក​បន្ថែម"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s មិន​បាន​បញ្ចេញ​កំហុស។"
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "លាក់​កំហុស"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "បង្ហាញ​កំហុស"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:59
+#: ../js/ui/status/location.js:167
+msgid "Enabled"
+msgstr "បាន​បើក"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:164
+#: ../js/ui/status/network.js:555 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "បាន​បិទ"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "កំហុស"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "ហួស​សម័យ"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "ការ​ទាញ​យក"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "មើល​ប្រភព"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "ទំព័រ​បណ្ដាញ"
+
+#: ../js/ui/messageTray.js:1326
+msgid "Open"
+msgstr "បើក"
+
+#: ../js/ui/messageTray.js:1333
+msgid "Remove"
+msgstr "យកចេញ"
+
+#: ../js/ui/messageTray.js:1630
+msgid "Notifications"
+msgstr "ការ​ជូនដំណឹង"
+
+#: ../js/ui/messageTray.js:1637
+msgid "Clear Messages"
+msgstr "សម្អាត​សារ"
+
+#: ../js/ui/messageTray.js:1656
+msgid "Notification Settings"
+msgstr "ការ​កំណត់​ការ​ជូនដំណឺង"
+
+#: ../js/ui/messageTray.js:1709
+msgid "Tray Menu"
+msgstr "ម៉ឺនុយ​ថាស"
+
+#: ../js/ui/messageTray.js:1926
+msgid "No Messages"
+msgstr "មិន​មាន​សារ"
+
+#: ../js/ui/messageTray.js:1964
+msgid "Message Tray"
+msgstr "ថាស​សារ"
+
+#: ../js/ui/messageTray.js:2967
+msgid "System Information"
+msgstr "ព័ត៌មាន​ប្រព័ន្ធ"
+
+#: ../js/ui/notificationDaemon.js:516 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "មិន​ស្គាល់"
+
+#: ../js/ui/overviewControls.js:483 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "សារ​ថ្មី %d"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "មិនធ្វើវិញ"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "ទិដ្ឋភាព​ទូទៅ"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:250
+msgid "Type to search…"
+msgstr "វាយ​បញ្ចូល​ដើម្បី​ស្វែងរក..."
+
+#: ../js/ui/panel.js:516
+msgid "Quit"
+msgstr "បោះបង់"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:568
+msgid "Activities"
+msgstr "សកម្មភាព"
+
+#: ../js/ui/panel.js:915
+msgid "Top Bar"
+msgstr "របារ​កំពូល"
+
+#: ../js/ui/popupMenu.js:271
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:74
+msgid "Enter a Command"
+msgstr "បញ្ចូល​ពាក្យ​បញ្ជា"
+
+#: ../js/ui/runDialog.js:114
+msgid "Close"
+msgstr "បិទ"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "ការ​ជូនដំណឺង​ថ្មី %d"
+
+#: ../js/ui/screenShield.js:474 ../js/ui/status/system.js:342
+msgid "Lock"
+msgstr "ចាក់សោ"
+
+#: ../js/ui/screenShield.js:708
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME ត្រូវការ​ចាក់សោ​អេក្រង់"
+
+#: ../js/ui/screenShield.js:835 ../js/ui/screenShield.js:1312
+msgid "Unable to lock"
+msgstr "មិន​អាច​ចាក់សោ"
+
+#: ../js/ui/screenShield.js:836 ../js/ui/screenShield.js:1313
+msgid "Lock was blocked by an application"
+msgstr "បាន​ទប់ស្កាត់​ការ​ចាក់សោ​ដោយ​កម្មវិធី"
+
+#: ../js/ui/search.js:606
+msgid "Searching…"
+msgstr "កំពុង​ស្វែងរក..."
+
+#: ../js/ui/search.js:652
+msgid "No results."
+msgstr "មិន​មាន​លទ្ធផល។"
+
+#: ../js/ui/shellEntry.js:27
+msgid "Copy"
+msgstr "ចម្លង"
+
+#: ../js/ui/shellEntry.js:32
+msgid "Paste"
+msgstr "បិទភ្ជាប់"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Show Text"
+msgstr "បង្ហាញ​អត្ថបទ"
+
+#: ../js/ui/shellEntry.js:101
+msgid "Hide Text"
+msgstr "លាក់​អត្ថបទ"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "ពាក្យសម្ងាត់"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "ចងចាំ​ពាក្យសម្ងាត់"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "ងាយស្រួល"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "ពង្រីក"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "កម្មវិធី​អាន​អេក្រង់"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "ក្ដារចុច​អេក្រង់"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "ការ​ជូនដំណឹង​និម្មិត"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "គ្រាប់ចុចស្អិត"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "គ្រាប់ចុច​យឺត"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "គ្រាប់ចុច​លោត"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "ប៊ូតុង​កណ្ដុរ"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "កម្រិត​ពណ៌​ខ្ពស់"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "អត្ថបទ​ធំ"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "ប៊្លូធូស"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:151
+#: ../js/ui/status/network.js:323 ../js/ui/status/network.js:1234
+#: ../js/ui/status/network.js:1345 ../js/ui/status/rfkill.js:85
+#: ../js/ui/status/rfkill.js:105
+msgid "Turn Off"
+msgstr "បិទ"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "ការ​កំណត់​ប៊្លូធូស"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "បាន​តភ្ជាប់​ឧបករណ៍ %d"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1262
+msgid "Not Connected"
+msgstr "មិន​បាន​តភ្ជាប់"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "ពន្លឺ"
+
+#: ../js/ui/status/keyboard.js:403
+msgid "Show Keyboard Layout"
+msgstr "បង្ហាញ​ប្លង់​ក្ដារចុច"
+
+#: ../js/ui/status/location.js:53
+msgid "Location"
+msgstr "ទីតាំង"
+
+#: ../js/ui/status/location.js:60 ../js/ui/status/location.js:168
+msgid "Disable"
+msgstr "បិទ"
+
+#: ../js/ui/status/location.js:165
+msgid "Enable"
+msgstr "បើក"
+
+#: ../js/ui/status/location.js:167
+msgid "In Use"
+msgstr "កំពុង​ប្រើ"
+
+#: ../js/ui/status/network.js:74
+msgid "<unknown>"
+msgstr "<មិន​ស្គាល់>"
+
+#: ../js/ui/status/network.js:420 ../js/ui/status/network.js:1260
+#: ../js/ui/status/network.js:1464
+msgid "Off"
+msgstr "បិទ"
+
+#: ../js/ui/status/network.js:422
+msgid "Connected"
+msgstr "បាន​តភ្ជាប់"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:426
+#| msgid "unmanaged"
+msgid "Unmanaged"
+msgstr "មិន​បាន​គ្រប់គ្រង"
+
+#: ../js/ui/status/network.js:428
+#| msgid "disconnecting..."
+msgid "Disconnecting"
+msgstr "​ផ្ដាច់"
+
+#: ../js/ui/status/network.js:434 ../js/ui/status/network.js:1254
+msgid "Connecting"
+msgstr "ការ​តភ្ជាប់"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:437
+#| msgid "authentication required"
+msgid "Authentication required"
+msgstr "ទាមទារ​ការ​ផ្ទៀងផ្ទាត់"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:445
+#| msgid "firmware missing"
+msgid "Firmware missing"
+msgstr "បាត់​កម្មវិធី​បង្កប់"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:449
+#| msgid "unavailable"
+msgid "Unavailable"
+msgstr "មិន​មាន"
+
+#: ../js/ui/status/network.js:451 ../js/ui/status/network.js:1646
+msgid "Connection failed"
+msgstr "ការ​តភ្ជាប់​បាន​បរាជ័យ"
+
+#: ../js/ui/status/network.js:467
+msgid "Wired Settings"
+msgstr "ការ​កំណត់​ខ្សែ"
+
+#: ../js/ui/status/network.js:509 ../js/ui/status/network.js:587
+msgid "Mobile Broadband Settings"
+msgstr "ការ​កំណត់​រលក​អាកាស​ធំ​ចល័ត"
+
+#: ../js/ui/status/network.js:551 ../js/ui/status/network.js:1258
+msgid "Hardware Disabled"
+msgstr "បាន​បិទ​ផ្នែក​រឹង"
+
+#: ../js/ui/status/network.js:595
+msgid "Use as Internet connection"
+msgstr "ប្រើ​ជា​ការ​តភ្ជាប់​អ៊ីនធឺណិត"
+
+#: ../js/ui/status/network.js:776
+msgid "Airplane Mode is On"
+msgstr "បាន​របៀប​ជិះ​យន្តហោះ"
+
+#: ../js/ui/status/network.js:777
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "វ៉ាយហ្វាយ​ត្រូវ​បាន​បិទ ពេល​បើក​របៀប​ជិះ​យន្តហោះ។"
+
+#: ../js/ui/status/network.js:778
+msgid "Turn Off Airplane Mode"
+msgstr "បិទ​របៀប​ជិះ​យន្តហោះ"
+
+#: ../js/ui/status/network.js:787
+msgid "Wi-Fi is Off"
+msgstr "បាន​បិទ​វ៉ាយហ្វាយ"
+
+#: ../js/ui/status/network.js:788
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "ចាំបាច់​ត្រូវ​បើក​វ៉ាយហ្វាយ​ដើម្បី​តភ្ជាប់​បណ្ដាញ។"
+
+#: ../js/ui/status/network.js:789
+msgid "Turn On Wi-Fi"
+msgstr "បើក​វ៉ាយហ្វាយ"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi Networks"
+msgstr "បណ្ដាញ​វ៉ាយហ្វាយ"
+
+#: ../js/ui/status/network.js:816
+msgid "Select a network"
+msgstr "ជ្រើស​បណ្ដាញ"
+
+#: ../js/ui/status/network.js:845
+msgid "No Networks"
+msgstr "គ្មាន​បណ្ដាញ"
+
+#: ../js/ui/status/network.js:866 ../js/ui/status/rfkill.js:103
+msgid "Use hardware switch to turn off"
+msgstr "ប្រើ​កុងតាក់​ផ្នែក​រឹង​ដើម្បី​បិទ"
+
+#: ../js/ui/status/network.js:1136
+msgid "Select Network"
+msgstr "ជ្រើស​បណ្ដាញ"
+
+#: ../js/ui/status/network.js:1142
+msgid "Wi-Fi Settings"
+msgstr "ការ​កំណត់​វ៉ាយហ្វាយ"
+
+#: ../js/ui/status/network.js:1234
+msgid "Turn On"
+msgstr "បើក"
+
+#: ../js/ui/status/network.js:1251
+msgid "Hotspot Active"
+msgstr "ហតស្ប៉ត​សកម្ម"
+
+#: ../js/ui/status/network.js:1362
+msgid "connecting..."
+msgstr "កំពុង​តភ្ជាប់"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1365
+msgid "authentication required"
+msgstr "ទាមទារ​ការ​ផ្ទៀងផ្ទាត់"
+
+#: ../js/ui/status/network.js:1367
+msgid "connection failed"
+msgstr "ការ​តភ្ជាប់​បាន​បរាជ័យ"
+
+#: ../js/ui/status/network.js:1433 ../js/ui/status/rfkill.js:88
+msgid "Network Settings"
+msgstr "ការ​កំណត់​បណ្ដាញ"
+
+#: ../js/ui/status/network.js:1435
+msgid "VPN Settings"
+msgstr "ការ​កំណត់​វីភីអិន"
+
+#: ../js/ui/status/network.js:1454
+msgid "VPN"
+msgstr "វីភីអិន"
+
+#: ../js/ui/status/network.js:1607
+msgid "Network Manager"
+msgstr "កម្មវិធី​គ្រប់គ្រង​បណ្ដាញ"
+
+#: ../js/ui/status/network.js:1647
+msgid "Activation of network connection failed"
+msgstr "ការ​ធ្វើឲ្យ​បណ្ដាញ​សកម្ម បាន​បរាជ័យ"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "ការ​កំណត់ថាមពល"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "បាន​បញ្ចូល​ពេញ"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "កំពុង​ប៉ាន់ស្មាន..."
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d នៅសល់ (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d រហូត​ពេញ (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "យូភីអេស"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "ថ្ម"
+
+#: ../js/ui/status/rfkill.js:82
+msgid "Airplane Mode"
+msgstr "របៀប​ជិះ​យន្តហោះ"
+
+#: ../js/ui/status/rfkill.js:84
+msgid "On"
+msgstr "បើក"
+
+#: ../js/ui/status/system.js:314
+msgid "Switch User"
+msgstr "ប្ដូរ​អ្នកប្រើ"
+
+#: ../js/ui/status/system.js:319
+msgid "Log Out"
+msgstr "ចេញ"
+
+#: ../js/ui/status/system.js:338
+msgid "Orientation Lock"
+msgstr "ចាក់សោ​ទិស"
+
+#: ../js/ui/status/system.js:346
+msgid "Suspend"
+msgstr "ផ្អាក"
+
+#: ../js/ui/status/system.js:349
+msgid "Power Off"
+msgstr "បិទ"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "ប្ដូរ​កម្រិត​សំឡេង"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "កម្រិត​សំឡេង"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "មីក្រូហ្វូន"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "ចូល​ជា​អ្នកប្រើ​ផ្សេង"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "ដោះ​សោ​វីនដូ"
+
+#: ../js/ui/viewSelector.js:100
+msgid "Applications"
+msgstr "កម្មវិធី"
+
+#: ../js/ui/viewSelector.js:104
+msgid "Search"
+msgstr "ស្វែងរក"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” គឺ​រួចរាល់​ហើយ"
+
+#: ../js/ui/windowManager.js:56
+msgid "Do you want to keep these display settings?"
+msgstr "អ្នក​ចង់​រក្សា​ការ​កំណត់​បង្ហាញ​ទាំងនេះ​ដែរ​ឬទេ?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:75
+msgid "Revert Settings"
+msgstr "ត្រឡប់​ការ​កំណត់​ទៅ​ដើម"
+
+#: ../js/ui/windowManager.js:79
+msgid "Keep Changes"
+msgstr "រក្សា​ការ​ផ្លាស់ប្ដូរ"
+
+#: ../js/ui/windowManager.js:98
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "ការ​ផ្លាស់ប្ដូរ​ការ​កំណត់​នឹង​ត្រឡប់​ទៅ​ដើម​ក្នុង​រយៈពេល %d វិនាទី"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "ប្រតិទិន​ការ​វិវត្ត"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "ចេញ %u"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "ចូល %u"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "សំឡេង​ប្រព័ន្ធ"
+
+#: ../src/main.c:349
+msgid "Print version"
+msgstr "បោះពុម្ព​កំណែ"
+
+#: ../src/main.c:355
+msgid "Mode used by GDM for login screen"
+msgstr "របៀប​ដែល​បាន​ប្រើ​ដោយ GDM សម្រាប់​អេក្រង់​ចូល"
+
+#: ../src/main.c:361
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "ប្រើ​រៀប​ជាក់លាក់, ឧ. \"gdm\" សម្រាប់​អេក្រង់​ចូល"
+
+#: ../src/main.c:367
+msgid "List possible modes"
+msgstr "រាយ​របៀប​ដែល​អាច​ធ្វើ​បាន"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "បាន​បរាជ័យ​ក្នុង​ការ​ចាប់ផ្ដើម “%s”"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "ពាក្យសម្ងាត់​មិន​ផ្គូរផ្គង។"
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "ពាក្យសម្ងាត់​មិន​អាចទទេ"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "បាន​បដិសេធ​ប្រអប់​ផ្ទៀងផ្ទាត់​ដោយ​អ្នកប្រើ"
diff --git a/po/kn.po b/po/kn.po
new file mode 100644
index 0000000..6aed880
--- /dev/null
+++ b/po/kn.po
@@ -0,0 +1,2220 @@
+# Kannada translation for gnome-shell.
+# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Shankar Prasad <svenkate@redhat.com>, 2011, 2012, 2013, 2014.
+# Shankar <svenkate@redhat.com>, 2013. #zanata.
+# Shankar <svenkate@redhat.com>, 2014. #zanata.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-09-21 19:28+0000\n"
+"PO-Revision-Date: 2014-09-22 12:15+0530\n"
+"Last-Translator: Shankar Prasad <svenkate AT redhat Dot com>\n"
+"Language-Team: Kannada <kde-i18n-doc@kde.org>\n"
+"Language: kn\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "ವ್ಯವಸ್ಥೆ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "ಸಂದೇಶ ಟ್ರೇ ಅನ್ನು ತೋರಿಸು"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "ಸಕ್ರಿಯ ಸೂಚನೆಯತ್ತ ಗಮನಹರಿಸು"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "ಅವಲೋಕನವನ್ನು ತೋರಿಸು"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "ಎಲ್ಲಾ ಅನ್ವಯಗಳನ್ನು ತೋರಿಸು"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "ಅನ್ವಯಗಳ ಮೆನುವನ್ನು ತೆರೆ"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME ಶೆಲ್‌"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "ಕಿಟಕಿ ನಿರ್ವಹಣೆ ಹಾಗು ಅನ್ವಯವನ್ನು ಆರಂಭಿಸುವಿಕೆ"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME ಶೆಲ್ ಎಕ್ಸ‍ಟೆನ್ಶನ್‍ ಆದ್ಯತೆಗಳು"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME ಶೆಲ್ ಎಕ್ಸ‍ಟೆನ್ಶನ್ಸ್ ಅನ್ನು ಸಂರಚಿಸಿ"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME ಶೆಲ್‌ (ವೇಲ್ಯಾಂಡ್‌ ) ಸಂಯೋಜಕ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Alt-F2 ಇಂದ ವಿಕಸನೆಗಾರರಿಗೆ ಹಾಗು ಪರೀಕ್ಷಕರಿಗೆ ಉಪಯುಕ್ತವಾಗುವ ಆಂತರಿಕ ಉಪಕರಣಗಳನ್ನು "
+"ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 ಸಂವಾದಚೌಕವನ್ನು ಬಳಸಿಕೊಂಡು ಆಂತರಿಕ ದೋಷ ನಿವಾರಣೆ ಹಾಗು ಮೇಲ್ವಿಚಾರಣೆ "
+"ಉಪಕರಣಗಳನ್ನು "
+"ನಿಲುಕಿಸಿಕೊಳ್ಳಲು ಅನುವುಮಾಡಿಕೊಡುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "ಸಕ್ರಿಯಗೊಳಿಸಬೇಕಿರುವ ವಿಸ್ತರಣೆಗಳ UUIDಗಳು"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME ಶೆಲ್ ವಿಸ್ತರಣೆಗಳು ಒಂದು UUID ಗುಣವನ್ನು ಹೊಂದಿರುತ್ತವೆ; ಈ ಕೀಲಿ ಪಟ್ಟಿಯು ಲೋಡ್ "
+"ಮಾಡಬೇಕಿರುವ ವಿಸ್ತರಣೆಗಳ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸುತ್ತದೆ. ಯಾವುದೆ ವಿಸ್ತರಣೆಗಳನ್ನು ಲೋಡ್‌ "
+"ಮಾಡಬೇಕಿದ್ದಲ್ಲಿ ಅದು ಈ ಪಟ್ಟಿಯಲ್ಲಿ ಇರುವುದು ಅತ್ಯಗತ್ಯ. org.gnome.Shell ನಲ್ಲಿ "
+"EnableExtension ಮತ್ತು DisableExtension D-Bus ವಿಧಾನಗಳನ್ನು ಬಳಸಿಕೊಂಡೂ ಸಹ ಈ "
+"ಪಟ್ಟಿಯನ್ನು ನಿರ್ವಹಿಸಬಹುದು."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "ವಿಸ್ತರಣೆ ಆವೃತ್ತಿಯ ಹೊಂದಾಣಿಕೆಯ ಮೌಲ್ಯಮಾಪನವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME ಶೆಲ್‌ ಪ್ರಸಕ್ತ ಚಲಾಯಿಸಲಾಗುತ್ತಿರುವ ಆವೃತ್ತಿಯನ್ನು ಬೆಂಬಲಿಸಲು ಕೇವಲ "
+"ವಿಸ್ತರಣೆಗಳನ್ನು ಮಾತ್ರ "
+"ಲೋಡ್ ಮಾಡುತ್ತದೆ. ಈ ಆಯ್ಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುವುದರಿಂದ ಈ ಪರೀಕ್ಷೆಯನ್ನು "
+"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ "
+"ಮತ್ತು ಬೆಂಬಲವಿರಲಿ ಇಲ್ಲದೆ ಇರಲಿ, ಲಭ್ಯವಿರುವ ಎಲ್ಲಾ ವಿಸ್ತರಣೆಗಳನ್ನು ಲೋಡ್ ಮಾಡಲು "
+"ಪ್ರಯತ್ನಿಸುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "ಮೆಚ್ಚಿನ ಅನ್ವಯಗಳಿಗಾಗಿನ ಗಣಕತೆರೆ ಕಡತ IDಗಳ ಪಟ್ಟಿ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"ಈ ಐಡೆಂಟಿಫಯರುಗಳಿಗೆ ಅನುಗುಣವಾದ ಅನ್ವಯಗಳನ್ನು ಮೆಚ್ಚಿನವುಗಳ ಜಾಗದಲ್ಲಿ ತೋರಿಸಲಾಗುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "ಆಪ್ ಆಯ್ಕೆಗಾರ ನೋಟ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "ಅನ್ವಯ ಆಯ್ಕೆಗಾರದಲ್ಲಿನ ಪ್ರಸಕ್ತ ಆಯ್ಕೆ ಮಾಡಿದ ನೋಟದ ಸೂಚಿ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "ಆದೇಶದ ಇತಿಹಾಸ (Alt-F2) ಸಂವಾದಚೌಕ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "ನೋಡುವ ಗಾಜಿನ ಸಂವಾದ ಚೌಕಕ್ಕಾಗಿನ ಇತಿಹಾಸ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "ಯಾವಾಗಲೂ 'ನಿರ್ಗಮಿಸು' ಮೆನು ಅಂಶವನ್ನು ಬಳಕೆದಾರ ಮೆನುವಿನಲ್ಲಿ ತೋರಿಸು."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"ಈ ಕೀಲಿಯು ಏಕ-ಬಳಕೆದಾರ, ಏಕ-ಅಧಿವೇಶನ ಸಂದರ್ಭಗಳಲ್ಲಿ 'ನಿರ್ಗಮಿಸು' ಮೆನು ಅಂಶದ "
+"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಅಡಗಿಸಲ್ಪಡುವ ಆಯ್ಕೆಯನ್ನು ಅತಿಕ್ರಮಿಸುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"ಗೂಢಲಿಪೀಕರಿಸಲಾದ ಅಥವ ದೂರದ ಕಡತವ್ಯವಸ್ಥೆಗಳನ್ನು ಏರಿಸುವ ಸಲುವಾಗಿನ ಗುಪ್ತಪದಗಳನ್ನು "
+"ನೆನಪಿಟ್ಟುಕೊಳ್ಳಬೇಕೆ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"ಗೂಢಲಿಪೀಕರಿಸಲಾದ ಸಾಧನ ಅಥವ ದೂರದ ಕಡತವ್ಯವಸ್ಥೆಗಳನ್ನು ಏರಿಸುವ ಸಲುವಾಗಿನ ಗುಪ್ತಪದಗಳನ್ನು "
+"ಶೆಲ್ "
+"ನೆನಪಿಟ್ಟುಕೊಳ್ಳುತ್ತದೆ. ಗುಪ್ತಪದವನ್ನು ಭವಿಷ್ಯದ ಬಳಕೆಗಾಗಿ ಉಳಿಸಬಹುದಾದಲ್ಲಿ "
+"'ಗುಪ್ತಪದವನ್ನು "
+"ನೆನಪಿಟ್ಟುಕೊ' ಎಂಬ ಗುರುತು ಚೌಕವು ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತದೆ. ಈ ಕೀಲಿಯು ಗುರುತುಚೌಕದ "
+"ಪೂರ್ವನಿಯೋಜಿತ "
+"ಸ್ಥಿತಿಯನ್ನು ಹೊಂದಿಸುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "ಕ್ಯಾಲೆಂಡರಿನಲ್ಲಿ ವಾರದ ದಿನವನ್ನು ತೋರಿಸು"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "true ಆದಲ್ಲಿ, ಕ್ಯಾಲೆಂಡರಿನಲ್ಲಿ ISO ವಾರದ ದಿನವನ್ನು ತೋರಿಸುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "ಅನ್ವಯಗಳ ಮೆನುವನ್ನು ತೆರೆಯಬೇಕಿರುವ ಕೀಲಿಬೈಂಡಿಂಗ್"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "ಅನ್ವಯಗಳ ಮೆನುವನ್ನು ತೆರೆಯಬೇಕಿರುವ ಕೀಲಿಬೈಂಡಿಂಗ್."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"ಅನ್ವಯಗಳನ್ನು ತೋರಿಸು\" ನೋಟವನ್ನು ತೆರೆಯಬೇಕಿರುವ ಕೀಲಿಬೈಂಡಿಂಗ್"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"ಚಟುವಟಿಕೆ ಅವಲೋಕನದ ನೋಟದಲ್ಲಿನ \"ಅನ್ವಯಗಳನ್ನು ತೋರಿಸು\" ಅನ್ನು ತೆರೆಯಬೇಕಿರುವ "
+"ಕೀಲಿಬೈಂಡಿಂಗ್."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the overview"
+msgstr "ಅವಲೋಕನವನ್ನು ತೆರೆಯಬೇಕಿರುವ ಕೀಲಿಬೈಂಡಿಂಗ್"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the Activities Overview."
+msgstr "ಚಟುವಟಿಕೆಗಳ ಅವಲೋಕನವನ್ನು ತೆರೆಯಬೇಕಿರುವ ಕೀಲಿಬೈಂಡಿಂಗ್."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "ಸಂದೇಶ ಟ್ರೇಯ ಗೋಚರಿಕೆಯನ್ನು ಹೊರಳಿಸಲು ಕೀಬೈಂಡಿಂಗ್"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "ಸಂದೇಶ ಟ್ರೇಯ ಗೋಚರಿಕೆಯನ್ನು ಹೊರಳಿಸಲು ಕೀಬೈಂಡಿಂಗ್."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "ಸಕ್ರಿಯ ಸೂಚನೆಯತ್ತ ಗಮನ ಹರಿಸಲು ಕೀಲಿಬೈಂಡಿಂಗ್"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "ಸಕ್ರಿಯ ಸೂಚನೆಯತ್ತ ಗಮನ ಹರಿಸಲು ಕೀಲಿಬೈಂಡಿಂಗ್."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"ಡೀಬಗ್‌ ಉದ್ದೇಶಗಳಿಗಾಗಿ ಚಾಲನೆಯಲ್ಲಿರುವ ಎಲ್ಲಾ ಟ್ವೀನ್‌ಗಳನ್ನು ವಿರಮಿಸುತ್ತದೆ ಮತ್ತು "
+"ಮರಳಿ "
+"ಆರಂಭಿಸುತ್ತದೆ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "ಯಾವ ಕೀಲಿಮಣೆಯನ್ನು ಬಳಸಬೇಕಿದೆ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "ಬಳಸಬೇಕಿರುವ ಕೀಲಿಮಣೆಯ ಬಗೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "ಸ್ವಿಚರ್ ಅನ್ನು ಪ್ರಸಕ್ತ ಕಾರ್ಯಸ್ಥಳಕ್ಕೆ ಮಿತಿಗೊಳಿಸು."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"ಟ್ರೂ ಆದಲ್ಲಿ, ಪ್ರಸಕ್ತ ಕಾರ್ಯಸ್ಥಳದಲ್ಲಿ ಕಿಟಕಿಗಳನ್ನು ಹೊಂದಿರುವ ಅನ್ವಯಗಳನ್ನು ಮಾತ್ರ "
+"ಸ್ವಿಚರ್‌ನಲ್ಲಿ "
+"ತೋರಿಸಲಾಗುತ್ತದೆ. ಇಲ್ಲದೆ ಹೋದಲ್ಲಿ, ಎಲ್ಲಾ ಅನ್ವಯಗಳನ್ನು ಸೇರಿಸಲಾಗುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "ಅನ್ವಯ ಚಿಹ್ನೆ ಕ್ರಮ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"ಸ್ವಿಚರಿನಲ್ಲಿ ಕಿಟಕಿಗಳನ್ನು ಹೇಗೆ ತೋರಿಸಲಾಗುತ್ತದೆ ಎನ್ನುವುದನ್ನು ಸಂರಚಿಸುತ್ತದೆ. "
+"ಮಾನ್ಯವಾದ "
+"ಸಾಧ್ಯತೆಗಳೆಂದರೆ 'ತಂಬ್‌ನೈಲ್-ಮಾತ್ರ' (ಕಿಟಕಿಯ ಒಂದು ತಂಬ್‌ನೈಲ್ ಅನ್ನು ತೋರಿಸುತ್ತದೆ), "
+"'app-icon-"
+"ಮಾತ್ರ' (ಅನ್ವಯ ಚಿಹ್ನೆಯನ್ನು ಮಾತ್ರ ತೋರಿಸುತ್ತದೆ) ಅಥವ 'ಎರಡೂ'."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"ಟ್ರೂ ಆದಲ್ಲಿ, ಪ್ರಸಕ್ತ ಕಾರ್ಯಸ್ಥಳದಲ್ಲಿ ಕಿಟಕಿಗಳನ್ನು ಮಾತ್ರ ಸ್ವಿಚರ್‌ನಲ್ಲಿ "
+"ತೋರಿಸಲಾಗುತ್ತದೆ. ಇಲ್ಲದೆ "
+"ಹೋದಲ್ಲಿ, ಎಲ್ಲಾ ಕಿಟಕಿಗಳನ್ನು ಸೇರಿಸಲಾಗುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "ಮೂಲ ಕಿಟಕಿಗೆ ಮೋಡಲ್ ಸಂವಾದವನ್ನು ಸೇರಿಸು"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"ಈ ಕೀಲಿಯು GNOME ಶೆಲ್‌ ಅನ್ನು ಚಲಾಯಿಸುವಾಗ org.gnome.mutter ನಲ್ಲಿನ ಕೀಲಿಯನ್ನು "
+"ಅತಿಕ್ರಮಿಸುತ್ತದೆ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "ಕಿಟಕಿಗಳನ್ನು ತೆರೆಯ ಅಂಚಿನಲ್ಲಿ ಬೀಳಿಸುವಾಗ ಅಂಚಿನ ಟೈಲಿಂಗ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸು"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "ಕಾರ್ಯಸ್ಥಳಗಳನ್ನು ಕ್ರಿಯಾತ್ಮಕವಾಗಿ ನಿರ್ವಹಿಸಲಾಗುತ್ತದೆ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "ಪ್ರಾಥಮಿಕ ತೆರೆಯ ಮೇಲಿರುವ ಕಾರ್ಯಕ್ಷೇತ್ರಗಳು ಮಾತ್ರ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"ತೆರೆಸೂಚಕವು ಚಲಿಸುವುದನ್ನು ನಿಲ್ಲಿಸುವವರೆಗೆ ಮೌಸ್‌ನ ಗಮನದಲ್ಲಿನ ಬದಲಾವಣೆಗಳನ್ನು "
+"ವಿಳಂಬವಾಗಿಸು"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "ಕ್ಯಾಪ್ಟೀವ್ ಪೋರ್ಟಲ್"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s ಗಾಗಿ ಆದ್ಯತೆಗಳ ಸಂವಾದವನ್ನು ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ದೋಷ ಉಂಟಾಗಿದೆ:"
+
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extensions"
+msgstr "GNOME ಶೆಲ್ ಎಕ್ಸ‍ಟೆನ್ಶನ್ಸ್"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "ರದ್ದು ಮಾಡು"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "ಮುಂದಿನ"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "ಅನ್‌ಲಾಕ್ ಮಾಡು"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "ಸೈನ್ ಇನ್"
+
+#: ../js/gdm/loginDialog.js:269
+msgid "Choose Session"
+msgstr "ಅಧಿವೇಶನವನ್ನು ಆರಿಸಿ"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "ಪಟ್ಟಿಯಲ್ಲಿಲ್ಲವೆ?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ಉದಾ., ಬಳಕೆದಾರ ಅಥವ %s)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "ಬಳಕೆದಾರ ಹೆಸರು: "
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "ಪ್ರವೇಶದ ಕಿಟಕಿ"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "ದೃಢೀಕರಣ ದೋಷ"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(ಅಥವ ನಿಮ್ಮ ಬೆರಳನ್ನು ಉಜ್ಜಿ)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "ಆದೇಶವು ಕಂಡು ಬಂದಿಲ್ಲ"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "ಆದೇಶವನ್ನು ಪಾರ್ಸ್ ಮಾಡಲಾಗಿಲ್ಲ:"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” ಅನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ:"
+
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "ಜಾಲ ದೃಢೀಕರಣದ ಮರುನಿರ್ದೇಶನ"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "ಪದೆ ಪದೆ ಬಳಸಲಾಗುವ ಅನ್ವಯಗಳು ಇಲ್ಲಿ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "ಪುನರಾವರ್ತನೆ"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "ಎಲ್ಲಾ"
+
+#: ../js/ui/appDisplay.js:1790
+msgid "New Window"
+msgstr "ಹೊಸ ಕಿಟಕಿ"
+
+#: ../js/ui/appDisplay.js:1816 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "ಎಲ್ಲಾ ಅಚ್ಚುಮೆಚ್ಚಿನವುಗಳನ್ನು ತೆಗೆದುಹಾಕು"
+
+#: ../js/ui/appDisplay.js:1822
+msgid "Add to Favorites"
+msgstr "ಅಚ್ಚುಮೆಚ್ಚಿನವುಗಳಿಗೆ ಸೇರಿಸು"
+
+#: ../js/ui/appDisplay.js:1831
+msgid "Show Details"
+msgstr "ವಿವರಗಳನ್ನು ತೋರಿಸು"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s ಅನ್ನು ನಿಮ್ಮ ಅಚ್ಚುಮೆಚ್ಚಿನವುಗಳನ್ನು ಸೇರಿಸಲಾಗಿದೆ."
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s ಅನ್ನು ನಿಮ್ಮ ಅಚ್ಚುಮೆಚ್ಚಿನವುಗಳಿಂದ ತೆಗೆದುಹಾಕಲಾಗಿದೆ."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "ಹಿನ್ನೆಲೆಯನ್ನು ಬದಲಾಯಿಸು…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "ಎಲ್ಲಾ ದಿನ"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ಭಾ"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "ಸೋ"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "ಮಂ"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "ಬು"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "ಗು"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "ಶು"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ಶ"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "ಭಾ"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "ಸೋ"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "ಮಂ"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "ಬು"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "ಗು"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "ಶು"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "ಶ"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "ಹಿಂದಿನ ತಿಂಗಳು"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "ಮುಂದಿನ ತಿಂಗಳು"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "ಯಾವುದೂ ಅನುಸೂಚಿತಗೊಂಡಿಲ್ಲ"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "ಇಂದು"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "ನಾಳೆ"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "ಈ ವಾರ"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "ಮುಂದಿನ ವಾರ"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "ಬಾಹ್ಯ ಡ್ರೈವ್‌ನಿಂದ ಸಂಪರ್ಕ ಕಡಿದಿದೆ"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "ಬಾಹ್ಯ ಡ್ರೈವ್‌ನಿಂದ ಸಂಪರ್ಕ ಕಡಿದಿದೆ"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "ತೆಗೆದುಹಾಕಬಹುದಾದ ಸಾಧನಗಳು"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ನೊಂದಿಗೆ ತೆರೆ"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "ಹೊರತಳ್ಳು"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "ಗುಪ್ತಪದ:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "ಇನ್ನೊಮ್ಮೆ ನಮೂದಿಸಿ:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "ಸಂಪರ್ಕ ಜೋಡಿಸು"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "ಗುಪ್ತಪದ: "
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "ಕೀಲಿ: "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "ಗುರುತು: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "ಖಾಸಗಿ ಕೀಲಿ ಗುಪ್ತಪದ: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "ಸೇವೆ: "
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "ವೈರ್ಲೆಸ್‌ ಜಾಲಬಂಧಕ್ಕಾಗಿ ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"ವೈರ್ಲೆಸ್‌ ಜಾಲಬಂಧ “%s” ಅನ್ನು ನಿಲುಕಿಸಿಕೊಳ್ಳಲು ಗುಪ್ತಪದಗಳು ಅಥವ ಗೂಢಲಿಪೀಕರಣ ಕೀಲಿಗಳ "
+"ಅಗತ್ಯವಿದೆ."
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "ವೈರ್ಡ್ 802.1X ದೃಢೀಕರಣ"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "ಜಾಲಬಂಧದ ಹೆಸರು: "
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "DSL ದೃಢೀಕರಣ"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "PIN ಕೋಡ್ ಅಗತ್ಯವಿದೆ"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "ಮೊಬೈಲ್ ಬ್ರಾಡ್‌ಬ್ಯಾಂಡ್‌ ಸಾಧನಕ್ಕಾಗಿ PIN ಕೋಡ್‌ ಅಗತ್ಯವಿದೆ"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "ಮೊಬೈಲ್ ಬ್ರಾಡ್‌ಬ್ಯಾಂಡ್ ಜಾಲಬಂಧ ಗುಪ್ತಪದ"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” ನೊಂದಿಗೆ ಸಂಪರ್ಕಸಾಧಿಸಲು ಗುಪ್ತಪದದ ಅಗತ್ಯವಿದೆ."
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "ವ್ಯವಸ್ಥಾಪಕ"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "ದೃಢೀಕರಿಸು"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "ಕ್ಷಮಿಸು, ಅದು ಕೆಲಸ ಮಾಡಲಿಲ್ಲ. ದಯವಿಟ್ಟು ಇನ್ನೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ."
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "ಆಹ್ವಾನ"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "ಕರೆ"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "ಕಡತದ ವರ್ಗಾವಣೆ"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "ಹರಟೆ"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "ಮಾತುಬರಿಸು"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "ಮೂಕಗೊಳಿಸು"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+msgid "Yesterday, %H∶%M"
+msgstr "ನಿನ್ನೆ, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "ನಿನ್ನೆ, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s ಈಗ %s ಆಗಿದ್ದಾರೆ"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%s ಗಾಗಿನ ಆಹ್ವಾನ"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s ನಿಮ್ಮನ್ನು %s ಗೆ ಸೇರುವಂತೆ ಆಹ್ವಾನಿಸುತ್ತಿದ್ದಾರೆ"
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "ತಿರಸ್ಕರಿಸು"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "ಅಂಗೀಕರಿಸು"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s ಇಂದ ವೀಡಿಯೊ ಕರೆ ಬಂದಿದೆ"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s ಇಂದ ಕರೆ ಬಂದಿದೆ"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "ಉತ್ತರ"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s ರವರು ನಿಮಗೆ %s ಅನ್ನು ಕಳುಹಿಸುತ್ತಿದ್ದಾರೆ"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr ""
+"ನೀವು ಯಾವಾಗ ಆನ್‌ಲೈನ್‌ನಲ್ಲಿ ಇರುತ್ತೀರಿ ಎಂದು ತಿಳಿಯಲು %s ರವರು ನಿಮ್ಮ ಅನುಮತಿಯನ್ನು "
+"ಕೋರಿದ್ದಾರೆ"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "ಜಾಲಬಂಧದ ದೋಷ"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "ದೃಢೀಕರಣವು ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "ಗೂಢಲಿಪೀಕರಣ ದೋಷ"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "ಪ್ರಮಾಣಪತ್ರವನ್ನು ಒದಗಿಸಲಾಗಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "ಪ್ರಮಾಣಪತ್ರವನ್ನು ನಂಬಲಾಗಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "ಪ್ರಮಾಣಪತ್ರದ ಕಾಲಾವಧಿ ತೀರಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "ಪ್ರಮಾಣಪತ್ರವು ಸಕ್ರಿಯಗೊಂಡಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "ಪ್ರಮಾಣಪತ್ರದ ಅತಿಥೇಯದ ಹೆಸರು ತಾಳೆಯಾಗುತ್ತಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "ಪ್ರಮಾಣಪತ್ರದ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ತಾಳೆಯಾಗುತ್ತಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "ಪ್ರಮಾಣಪತ್ರವು ಸ್ವತಃ ಸೈನ್ ಮಾಡಲ್ಪಟ್ಟಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "ಸ್ಥಿತಿಯನ್ನು ಆಫ್‌ಲೈನ್‌ ಎಂದು ಸೂಚಿಸಿದ್ದಾರೆ."
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "ಗೂಢಲಿಪೀಕರಣ ಲಭ್ಯವಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾಗಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "ಸಂಪರ್ಕವನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "ಸಂಪರ್ಕವನ್ನು ಸಾಧಿಸಲಾಗಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "ಸಂಪರ್ಕವು ಕಡಿದು ಹೋಗಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "ಈ ಖಾತೆಯು ಈಗಾಗಲೆ ಪೂರೈಕೆಗಣಕದೊಂದಿಗೆ ಸಂಪರ್ಕ ಜೋಡಿಸಲಾಗಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr ""
+"ಒಂದು ಹೊಸ ಸಂಪರ್ಕದಿಂದ ಈ ಸಂಪರ್ಕವನ್ನು ಇದೇ ಸಂಪನ್ಮೂಲವನ್ನು ಬಳಸಿಕೊಂಡು ಬದಲಾಯಿಸಲಾಗಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "ಖಾತೆಯು ಈಗಾಗಲೆ ಪೂರೈಕೆಗಣಕದಲ್ಲಿ ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "ಪೂರೈಕೆಗಣಕಕ್ಕೆ ಬಿಡುವಿಲ್ಲದ ಕಾರಣ ಸಂಪರ್ಕವನ್ನು ನಿಭಾಯಿಸಲಾಗುತ್ತಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "ಪ್ರಮಾಣಪತ್ರವನ್ನು ಹಿಂದಕ್ಕೆ ತೆಗೆದುಕೊಳ್ಳಲಾಗಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"ಪ್ರಮಾಣಪತ್ರವು ಅಸುರಕ್ಷಿತವಾದ ಸಿಫರ್ ಅಲ್ಗಾರಿತಮ್ ಅನ್ನು ಬಳಸುತ್ತದೆ ಅಥವ ದುರ್ಬಲ "
+"ಗೂಢಲಿಪೀಕರಣವನ್ನು "
+"ಹೊಂದಿದೆ"
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"ಪೂರೈಕೆಗಣಕ ಪ್ರಮಾಣಪತ್ರದ ಉದ್ದ ಅಥವ ಪೂರೈಕೆಗಣಕ ಪ್ರಮಾಣಪತ್ರ ಸರಣಿಯ ಆಳವು ಕ್ರಿಪ್ಟೋಗ್ರಫಿ "
+"ಲೈಬ್ರರಿಯಿಂದ ನಿಗದಿ ಪಡಿಸಲಾದ ಮಿತಿಗಳನ್ನು ಮೀರಿದೆ."
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "ಆಂತರಿಕ ತಪ್ಪು"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s ನೊಂದಿಗೆ ಸಂಪರ್ಕ ಸಾಧಿಸಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "ಖಾತೆಯನ್ನು ನೋಡು"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "ಅಜ್ಞಾತ ಕಾರಣ"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "ವಿಂಡೋಸ್"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "ಅನ್ವಯಗಳನ್ನು ತೋರಿಸು"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "ಡ್ಯಾಶ್"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "ಕ್ಯಾಲೆಂಡರನ್ನು ತೆರೆ"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "ಗಡಿಯಾರವನ್ನು ತೆರೆ"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "ದಿನಾಂಕ ಹಾಗು ಸಮಯದ ಸಿದ್ಧತೆಗಳು"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s ಇಂದ ನಿರ್ಗಮಿಸು"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "ನಿರ್ಗಮಿಸು"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s ರವರು %d ಸೆಕೆಂಡಿನಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿರ್ಗಮಿಸುತ್ತಾರೆ."
+msgstr[1] "%s ರವರು %d ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿರ್ಗಮಿಸಲ್ಪಡುತ್ತಾರೆ."
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "ನೀವು %d ಸೆಕೆಂಡಿನಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿರ್ಗಮಿಸಲ್ಪಡುತ್ತೀರಿ."
+msgstr[1] "ನೀವು %d ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿರ್ಗಮಿಸಲ್ಪಡುತ್ತೀರಿ."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "ನಿರ್ಗಮಿಸು"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "ಸ್ಥಗಿತಗೊಳಿಸು"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಅನುಸ್ಥಾಪಿಸುತ್ತದೆ ಮತ್ತು ಮರಳಿಸ್ಥಾಪಿಸುತ್ತದೆ"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "ವ್ಯವಸ್ಥೆಯು %d ಸೆಕೆಂಡಿನಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸ್ಥಗಿತಗೊಳ್ಳುತ್ತದೆ."
+msgstr[1] "ವ್ಯವಸ್ಥೆಯು %d ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸ್ಥಗಿತಗೊಳ್ಳುತ್ತದೆ."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "ಬಾಕಿ ಇರುವ ತಂತ್ರಾಂಶ ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಅನುಸ್ಥಾಪಿಸು"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "ಮರಳಿ ಆರಂಭಿಸು"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "ಸ್ಥಗಿತಗೊಳಿಸು"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "ಮರಳಿ ಆರಂಭಿಸು"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "ವ್ಯವಸ್ಥೆಯು %d ಸೆಕೆಂಡಿನಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರಳಿ ಆರಂಭಗೊಳ್ಳುತ್ತದೆ."
+msgstr[1] "ವ್ಯವಸ್ಥೆಯು %d ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರಳಿ ಆರಂಭಗೊಳ್ಳುತ್ತದೆ."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "ಮರಳಿ ಆರಂಭಿಸು ಮತ್ತು ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಅನುಸ್ಥಾಪಿಸು"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"%d ಸೆಕೆಂಡಿನಲ್ಲಿ ವ್ಯವಸ್ಥೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರಳಿ ಆರಂಭಗೊಳ್ಳುತ್ತದೆ ಮತ್ತು "
+"ಅಪ್‌ಡೇಟ್‌ಗಳು "
+"ಅನುಸ್ಥಾಪನೆಗೊಳ್ಳುತ್ತದೆ."
+msgstr[1] ""
+"%d ಸೆಕೆಂಡುಗಳಲ್ಲಿ ವ್ಯವಸ್ಥೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರಳಿ ಆರಂಭಗೊಳ್ಳುತ್ತದೆ ಮತ್ತು "
+"ಅಪ್‌ಡೇಟ್‌ಗಳು "
+"ಅನುಸ್ಥಾಪನೆಗೊಳ್ಳುತ್ತವೆ."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "ಮರಳಿ ಆರಂಭಿಸು ಮತ್ತು ಅನುಸ್ಥಾಪಿಸು"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ಅನುಸ್ಥಾಪಿಸು ಮತ್ತು ಆಫ್ ಮಾಡು"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಅನುಸ್ಥಾಪಿಸಿದ ನಂತರ ಪವರ್ ಆಫ್ ಮಾಡು"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"ಬ್ಯಾಟರಿಯ ವಿದ್ಯುಚ್ಛಕ್ತಿಯಲ್ಲಿ ಕೆಲಸ ಮಾಡುತ್ತಿದೆ: ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಅನುಸ್ಥಾಪಿಸುವ "
+"ಮೊದಲು ಪ್ಲಗ್ ಇನ್ "
+"ಮಾಡು."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr ""
+"ಕೆಲವು ಅನ್ವಯಗಳು ಕಾರ್ಯನಿರತವಾಗಿರುತ್ತವೆ ಅಥವ ಉಳಿಸದೆ ಇರುವ ಕೆಲಸವನ್ನು ಹೊಂದಿರಬಹುದು."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "ಇನ್ನೊಬ್ಬ ಬಳಕೆದಾರರು ಒಳಗೆ ಪ್ರವೇಶಿಸಿದ್ದಾರೆ."
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (ದೂರದ)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (ಕನ್ಸೋಲ್)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "ಅನುಸ್ಥಾಪಿಸು"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "“%s” ಅನ್ನು extensions.gnome.org ಇಂದ ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ ಅನುಸ್ಥಾಪಿಸಬೇಕೆ?"
+
+#: ../js/ui/keyboard.js:692 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "ಕೀಲಿಮಣೆ"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "ಯಾವುದು ವಿಸ್ತರಣೆಗಳನ್ನು ಅನುಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ಯಾವುದೆ ದೋಷಗಳನ್ನು ತೋರಿಸಿಲ್ಲ."
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "ದೋಷಗಳನ್ನು ಅಡಗಿಸು"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "ದೋಷಗಳನ್ನು ತೋರಿಸು"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "ದೋಷ"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "ಹಳೆಯದಾಗಿದೆ"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "ಇಳಿಸಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "ಆಕರವನ್ನು ನೋಡಿ"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "ಜಾಲ ಪುಟ"
+
+#: ../js/ui/messageTray.js:1327
+msgid "Open"
+msgstr "ತೆರೆ"
+
+#: ../js/ui/messageTray.js:1334
+msgid "Remove"
+msgstr "ತೆಗೆದು ಹಾಕು"
+
+#: ../js/ui/messageTray.js:1631
+msgid "Notifications"
+msgstr "ಸೂಚನೆಗಳು"
+
+#: ../js/ui/messageTray.js:1638
+msgid "Clear Messages"
+msgstr "ಸಂದೇಶಗಳನ್ನು ಅಳಿಸು"
+
+#: ../js/ui/messageTray.js:1657
+msgid "Notification Settings"
+msgstr "ಸೂಚನೆಯ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/messageTray.js:1710
+msgid "Tray Menu"
+msgstr "ಟ್ರೇ ಮೆನು"
+
+#: ../js/ui/messageTray.js:1934
+msgid "No Messages"
+msgstr "ಯಾವುದೆ ಸಂದೇಶವಿಲ್ಲ"
+
+#: ../js/ui/messageTray.js:1979
+msgid "Message Tray"
+msgstr "ಸಂದೇಶ ಟ್ರೇ"
+
+#: ../js/ui/messageTray.js:2992
+msgid "System Information"
+msgstr "ವ್ಯವಸ್ಥೆಯ ಮಾಹಿತಿ"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "ಗೊತ್ತಿಲ್ಲದ"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d ಹೊಸ ಸಂದೇಶ"
+msgstr[1] "%d ಹೊಸ ಸಂದೇಶಗಳು"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "ರದ್ದುಗೊಳಿಸು"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "ಅವಲೋಕನ"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "ಹುಡುಕಲು ನಮೂದಿಸು…"
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "ನಿರ್ಗಮಿಸು"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "ಚಟುವಟಿಕೆಗಳು"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "ಮೇಲಿನ ಪಟ್ಟಿ"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "ಒಂದು ಆದೇಶವನ್ನು ನಮೂದಿಸಿ"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "ಮುಚ್ಚು"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "ಮರಳಿ ಆರಂಭಿಸಲಾಗುತ್ತಿದೆ…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d ಹೊಸ ಸೂಚನೆ"
+msgstr[1] "%d ಹೊಸ ಸೂಚನೆಗಳು"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "ಲಾಕ್ ಮಾಡು"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "ತೆರೆಯನ್ನು ಲಾಕ್ ಮಾಡುವಂತೆ GNOME ಬಯಸುತ್ತದೆ"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "ಲಾಕ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "ಲಾಕ್ ಮಾಡದಂತೆ ಒಂದು ಅನ್ವಯವು ತಡೆದಿದೆ"
+
+#: ../js/ui/search.js:594
+msgid "Searching…"
+msgstr "ಹುಡುಕಲಾಗುತ್ತಿದೆ…"
+
+#: ../js/ui/search.js:596
+msgid "No results."
+msgstr "ಯಾವುದೆ ಫಲಿತಾಂಶಗಳಿಲ್ಲ."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "ಪ್ರತಿಮಾಡು"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "ಅಂಟಿಸು"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "ಪಠ್ಯವನ್ನು ತೋರಿಸು"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "ಪಠ್ಯವನ್ನು ಅಡಗಿಸು"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "ಗುಪ್ತಪದ"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "ಈ ಗುಪ್ತಪದವನ್ನು ನೆನಪಿನಲ್ಲಿಡು"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "ನಿಲುಕಣೆ"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "ಗಾತ್ರ ಬದಲಾವಣೆ"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "ತೆರೆ ಓದುಗ"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "ತೆರೆ ಕೀಲಿಮಣೆ"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "ದೃಶ್ಯರೂಪದ ಎಚ್ಚರಿಕೆಗಳು"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "ಸ್ಟಿಕಿ ಕೀಲಿಗಳು"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "ನಿಧಾನ ಕೀಲಿಗಳು"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "ಪುಟಿಯುವ ಕೀಲಿಗಳು"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "ಮೌಸ್‌ ಕೀಲಿಗಳು"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "ಅತಿ ಹೆಚ್ಚು ವೈದೃಶ್ಯ"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "ದೊಡ್ಡ ಪಠ್ಯ"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "ಬ್ಲೂಟೂತ್"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "ಆಫ್ ಮಾಡು"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "ಬ್ಲೂಟೂತ್ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d ಸಂಪರ್ಕಿತಗೊಂಡಿರುವ ಸಾಧನ"
+msgstr[1] "%d ಸಂಪರ್ಕಿತಗೊಂಡಿರುವ ಸಾಧನಗಳು"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+msgid "Not Connected"
+msgstr "ಸಂಪರ್ಕ ಜೋಡಿಸಲಾಗಿಲ್ಲ"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "ಪ್ರಕಾಶ"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "ಕೀಲಿಮಣೆ ವಿನ್ಯಾಸವನ್ನು ತೋರಿಸು"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "ಸ್ಥಳ"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "ನಿಷ್ಕ್ರಿಯಗೊಳಿಸು"
+
+#: ../js/ui/status/location.js:73
+#| msgid "Power Settings"
+msgid "Privacy Settings"
+msgstr "ಗೌಪ್ಯತಾ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "ಬಳಕೆಯಲ್ಲಿದೆ"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "ಸಕ್ರಿಯಗೊಳಿಸು"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<ಗೊತ್ತಿರದ>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+msgid "Off"
+msgstr "ಆಫ್‌"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "ಸಂಪರ್ಕಿತಗೊಂಡಿದೆ"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "ನಿರ್ವಹಿಸಲಾಗದಿರುವ"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "ಸಂಪರ್ಕ ಕಡಿದುಹಾಕಲಾಗುತ್ತಿದೆ"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+msgid "Connecting"
+msgstr "ಸಂಪರ್ಕಿತಗೊಳ್ಳುತ್ತಿದೆ"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "ಫರ್ಮ್‌ವೇರ್ ಕಾಣಿಸುತ್ತಿಲ್ಲ"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "ಅಲಭ್ಯ"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "ಸಂಪರ್ಕವು ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "ವೈರ್ಡ್ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "ಮೊಬೈಲ್ ಬ್ರಾಡ್‌ಬ್ಯಾಂಡ್ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+msgid "Hardware Disabled"
+msgstr "ಯಂತ್ರಾಂಶ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "ಜಾಲಬಂಧ ಸಂಪರ್ಕವಾಗಿ ಬಳಸು"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "ಏರ್‌ಪ್ಲೇನ್ ಸ್ಥಿತಿಯು ಸಕ್ರಿಯವಾಗಿದೆ"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr ""
+"ಏರ್‌ಪ್ಲೇನ್ ಸ್ಥಿತಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ ವೈ-ಫೈ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗುತ್ತದೆ."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "ಏರ್‌ಪ್ಲೇನ್ ಸ್ಥಿತಿಯನ್ನು ಆಫ್ ಮಾಡು"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "ವೈ-ಫೈ ನಿಷ್ಕ್ರಿಯವಾಗಿದೆ"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "ಜಾಲಬಂಧಕ್ಕೆ ಸಂಪರ್ಕಿತಗೊಳ್ಳುವ ಸಲುವಾಗಿ ವೈ-ಫೈ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕು."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "ವೈ-ಫೈ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸು"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "ವೈ-ಫೈ ಜಾಲಬಂಧಗಳು"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "ಒಂದು ಜಾಲಬಂಧವನ್ನು ಆರಿಸಿ"
+
+#: ../js/ui/status/network.js:882
+msgid "No Networks"
+msgstr "ಯಾವುದೆ ಜಾಲಬಂಧಗಳಿಲ್ಲ"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "ಆಫ್ ಮಾಡಲು ಯಂತ್ರಾಂಶದ ಸ್ವಿಚ್ ಅನ್ನು ಬಳಸಿ"
+
+#: ../js/ui/status/network.js:1173
+msgid "Select Network"
+msgstr "ಜಾಲಬಂಧವನ್ನು ಆರಿಸಿ"
+
+#: ../js/ui/status/network.js:1179
+msgid "Wi-Fi Settings"
+msgstr "ವೈ-ಫೈ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "ಸಕ್ರಿಯಗೊಳಿಸು"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "ಹಾಟ್‌ಸ್ಪಾಟ್‌ ಸಕ್ರಿಯ"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "ಸಂಪರ್ಕ ಹೊಂದಲಾಗುತ್ತಿದೆ..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "ಸಂಪರ್ಕ ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "ಜಾಲಬಂಧ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/network.js:1482
+msgid "VPN Settings"
+msgstr "VPN ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "ನೆಟ್‌ವರ್ಕ್ ಮ್ಯಾನೇಜರ್"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "ಜಾಲಬಂಧ ಸಂಪರ್ಕವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "ವಿದ್ಯುಚ್ಛಕ್ತಿ ಸಿದ್ಧತೆಗಳು"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "ಸಂಪೂರ್ಣ ಚಾರ್ಜ್ ಆಗಿದೆ"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "ಊಹಿಸಲಾಗುತ್ತಿದೆ…"
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d ಬಾಕಿ ಇರುವುದು (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d ಪೂರ್ಣಗೊಳ್ಳುವ ವರೆಗೆ (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "ಬ್ಯಾಟರಿ"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "ಏರ್‌ಪ್ಲೇನ್‌ ಸ್ಥಿತಿ"
+
+#: ../js/ui/status/rfkill.js:85
+msgid "On"
+msgstr "ಸಕ್ರಿಯ"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "ಬಳಕೆದಾರನನ್ನು ಬದಲಿಸು"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "ನಿರ್ಗಮಿಸು"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "ಅಭಿಮುಖದ (ಓರಿಯೆಂಟೇಶನ್) ಲಾಕ್"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "ಅಮಾನತ್ತಿನಲ್ಲಿಡು"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "ಸ್ಥಗಿತಗೊಳಿಸು"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "ಧ್ವನಿ ಪ್ರಮಾಣವನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "ಧ್ವನಿ ಪ್ರಮಾಣ"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "ಮೈಕ್ರೊಫೋನ್"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "ಇನ್ನೊಬ್ಬ ಬಳಕೆದಾರನಾಗಿ ಪ್ರವೇಶಿಸಿ"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "ಕಿಟಕಿಯನ್ನು ಅನ್‌ಲಾಕ್ ಮಾಡು"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "ಅನ್ವಯಗಳು"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "ಹುಡುಕು"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” ಸಿದ್ಧಗೊಂಡಿದೆ"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "ನೀವು ಈ ಸಿದ್ಧತೆಗಳನ್ನು ಉಳಿಸಿಕೊಳ್ಳಲು ಬಯಸುವಿರಾ?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "ಸಿದ್ಧತೆಗಳನ್ನು ಹಿಮ್ಮರಳಿಸು"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸು"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "ಸಿದ್ಧತೆಗಳ ಬದಲಾವಣೆಗಳು %d ಸೆಕೆಂಡಿನಲ್ಲಿ ಹಿಮ್ಮರಳುತ್ತವೆ"
+msgstr[1] "ಸಿದ್ಧತೆಗಳ ಬದಲಾವಣೆಗಳು %d ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಹಿಮ್ಮರಳುತ್ತವೆ"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "ಕುಗ್ಗಿಸು"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "ಹಿಂದಿನ ಸ್ಥಿತಿಗೆ ಮರಳಿಸು"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "ಹಿಗ್ಗಿಸು"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "ಸ್ಥಳಾಂತರಿಸು"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "ಮರುಗಾತ್ರಿಸು"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "ಶೀರ್ಷಿಕೆಪಟ್ಟಿಯನ್ನು ತೆರೆಯ ಮೇಲೆ ಸ್ಥಳಾಂತರಿಸು"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "ಯಾವಾಗಲೂ ಮೇಲ್ಭಾಗದಲ್ಲಿ"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "ಯಾವಾಗಲೂ ಗೋಚರಿಸುವ ಕಾರ್ಯಸ್ಥಳದಲ್ಲಿ"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "ಮೇಲಿನ ಕಾರ್ಯಸ್ಥಳಕ್ಕೆ ಸ್ಥಳಾಂತರಿಸು"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "ಕೆಳಗಿನ ಕಾರ್ಯಸ್ಥಳಕ್ಕೆ ಸ್ಥಳಾಂತರಿಸು"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution ಕ್ಯಾಲೆಂಡರ್"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u ಔಟ್‌ಪುಟ್"
+msgstr[1] "%u ಔಟ್‌ಪುಟ್‌ಗಳು"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ಇನ್‌ಪುಟ್"
+msgstr[1] "%u ಇನ್‌ಪುಟ್‌ಗಳು"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "ವ್ಯವಸ್ಥೆಯ ಧ್ವನಿಗಳು"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "ಮುದ್ರಿಸಬಹುದಾದ ಆವೃತ್ತಿ"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "ಪ್ರವೇಶದ ತೆರೆಗಾಗಿ GDM ಬಳಸಿದ ಪ್ರಕಾರ"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "ನಿಶ್ಚಿತ ಸ್ಥಿತಿಯನ್ನು ಬಳಸು, ಉದಾ. ಪ್ರವೇಶ ತೆರೆಗಾಗಿ \"gdm\""
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "ಸಾಧ್ಯವಿರುವ ಸ್ಥಿತಿಗಳನ್ನು ಪಟ್ಟಿ ಮಾಡು"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” ಅನ್ನು ಆರಂಭಿಸುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "ಗುಪ್ತಪದಗಳು ತಾಳೆಯಾಗುವುದಿಲ್ಲ."
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "ಗುಪ್ತಪದವು ಖಾಲಿ ಇರುವಂತಿಲ್ಲ"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ದೃಢೀಕರಣದ ಸಂವಾದವನ್ನು ಬಳಕೆದಾರರಿಂದ ತಿರಸ್ಕರಿಸಲಾಗಿದೆ"
+
+#~ msgid "Screenshots"
+#~ msgstr "ತೆರೆಚಿತ್ರಗಳು"
+
+#~ msgid "Record a screencast"
+#~ msgstr "ಒಂದು ಸ್ಕ್ರೀನ್‌ಕ್ಯಾಸ್ಟನ್ನು ರೆಕಾರ್ಡು ಮಾಡಿ"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "ಅನ್ವಯದ ಬಳಕೆಯ ಬಗೆಗಿನ ಅಂಕಿಅಂಶಗಳನ್ನು ಸಂಗ್ರಹಿಸಬೇಕೆ"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "ಶೆಲ್‌ ಸಾಮಾನ್ಯವಾಗಿ ಹೆಚ್ಚಾಗಿ ಬಳಸಲಾದ (ಉದಾ. ಆರಂಭಕಗಳಲ್ಲಿ) ಅನ್ವಯಗಳನ್ನು ತೋರಿಸಲು "
+#~ "ಸಕ್ರಿಯವಾದವುಗಳನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ನಡೆಸುತ್ತದೆ. ಈ ದತ್ತಾಂಶವನ್ನು ಗೌಪ್ಯವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ, "
+#~ "ಹಾಗೂ ಗೌಪ್ಯತಾ ಕಾರಣಗಳಿದ್ದಲ್ಲಿ ನೀವು ಅದನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಬಹುದು. ಹಾಗೆ ಮಾಡುವುದರಿಂದ "
+#~ "ಉಳಿಸಲಾದ ಯಾವದೆ ದತ್ತಾಂಶವು ತೆಗೆದುಹಾಕಲ್ಪಡುವುದಿಲ್ಲ ಎನ್ನುವುದನ್ನು ನೆನಪಿಡಿ."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "ಕಡತಕೋಶಗಳಾಗಿ ತೋರಿಸಬೇಕಿರುವ ವರ್ಗಗಳ ಪಟ್ಟಿ"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "ಈ ಪಟ್ಟಿಯಲ್ಲಿನ ಪ್ರತಿಯೊಂದು ವರ್ಗ ಹೆಸರನ್ನೂ ಸಹ ಮುಖ್ಯ ನೋಟದಲ್ಲಿ ಸಾಲಿನ ಒಳಗೆ ತೋರಿಸುವ "
+#~ "ಬದಲಿಗೆ, ಅನ್ವಯ ನೋಟದಲ್ಲಿ ಕಡತಕೋಶವಾಗಿ ತೋರಿಸಲಾಗುತ್ತದೆ."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "ಆಂತರಿಕವಾಗಿ ಬಳಕೆದಾರರಿಂದ ಸ್ಪಷ್ಟವಾಗಿ ಹೊಂದಿಸಲಾದ ಕೊನೆಯ IM ಅನ್ನು ಶೇಖರಿರಿಸಲು "
+#~ "ಬಳಸಲಾಗುತ್ತದೆ. ಇಲ್ಲಿರುವ ಮೌಲ್ಯವು TpConnectionPresenceType ಎನ್ಯೂಮರೇಶನ್‌ನಿಂದ "
+#~ "ಬಂದಿರುತ್ತದೆ."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "ಆಂತರಿಕವಾಗಿ ಬಳಕೆದಾರರಿಂದ ಸ್ಪಷ್ಟವಾಗಿ ಹೊಂದಿಸಲಾದ ಕೊನೆಯ ಅಧಿವೇಶನವನ್ನು ಶೇಖರಿರಿಸಲು "
+#~ "ಬಳಸಲಾಗುತ್ತದೆ. ಇಲ್ಲಿರುವ ಮೌಲ್ಯವು GsmPresenceStatus ಎನ್ಯೂಮರೇಶನ್‌ನಿಂದ ಬಂದಿರುತ್ತದೆ."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "ತೆರೆ ರೆಕಾರ್ಡರ್ ಅನ್ನು ಹೊರಳಿಸಲು ಕೀಬೈಂಡಿಂಗ್"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "ಒಳನಿರ್ಮಿತ ತೆರೆ ರೆಕಾರ್ಡರ್ ಅನ್ನು ಆರಂಭಿಸಲು/ನಿಲ್ಲಿಸಲು ಕೀಬೈಂಡಿಂಗ್."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "ಸ್ಕ್ರೀನ್‌ಕ್ಯಾಸ್ಟುಗಳನ್ನು ರೆಕಾರ್ಡು ಮಾಡಲು ಬಳಸಲಾಗುವ ಫ್ರೇಮ್‌ದರಗಳು."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "ಪ್ರತಿ ಸೆಕೆಂಡಿನ ಫ್ರೇಮ್‌ಗಳಲ್ಲಿನ GNOME ಶೆಲ್‌ನ ಸ್ಕ್ರೀನ್‌ಕ್ಯಾಸ್ಟ್‍ ರೆಕಾರ್ಡರಿನಿಂದ ರೆಕಾರ್ಡು "
+#~ "ಮಾಡಲಾದ ಸ್ಕ್ರೀನ್‌ಕ್ಯಾಸ್ಟಿನ ಫ್ರೇಮ್‌ದರ. "
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "ಸ್ಕ್ರೀನ್‌ಕ್ಯಾಸ್ಟ್‍ ಅನ್ನು ಎನ್ಕೋಡ್ ಮಾಡಲು ಬಳಸಲಾಗುವ gstreamer ಪೈಪ್‌ಲೈನ್"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "ರೆಕಾರ್ಡಿಂಗ್‌ಗಳನ್ನು ಎನ್ಕೋಡ್ ಮಾಡಲು ಬಳಸಲಾದ GStreamer ಪೈಪ್‌ಲೈನ್‌ ಅನ್ನು ಹೊಂದಿಸುತ್ತದೆ. ಇದು "
+#~ "gst-launch ನಲ್ಲಿ ಬಳಸಲಾದ ಸಿಂಟ್ಯಾಕ್ಸನ್ನು ಅನುಸರಿಸುತ್ತದೆ. ಪೈಪ್‌ಲೈನ್ ವಿಡಿಯೊವನ್ನು "
+#~ "ರೆಕಾರ್ಡು ಮಾಡಲು ಒಂದು ನಿಯೋಜಿಸದೇ ಇರುವ ಸಿಂಕ್‌ಪ್ಯಾಡ್‌ಗೆ ಸಂಪರ್ಕಿತಗೊಂಡಿರಬೇಕು. ಇದು "
+#~ "ಸಾಮಾನ್ಯವಾಗಿ ಸಂಪರ್ಕಿತಗೊಂಡಿರದೆ ಇರುವ ಪೈಪ್‌ಲೈನ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ; ಆ ಪ್ಯಾಡ್‌ನಿಂದ ಔಟ್‌ಪುಟ್ "
+#~ "ಅನ್ನು ಔಟ್‌ಪುಟ್ ಕಡತಕ್ಕೆ ಬರೆಯಲಾಗುತ್ತದೆ. ಆದರೆ ಪೈಪ್‌ಲೈನ್ ತನ್ನದೆ ಆದ ಔಟ್‌ಪುಟ್ ಅನ್ನು "
+#~ "ನೋಡಿಕೊಳ್ಳಬಲ್ಲದು - shout2send ಅಥವ ಅದೇ ರೀತಿಯದರ ಮೂಲಕ icecast ಪೂರೈಕೆಗಣಕಕ್ಕೆ "
+#~ "ಔಟ್‌ಪುಟ್ ಅನ್ನು ಕಳುಹಿಸಲು ಇದನ್ನು ಬಳಸಬಹುದಾಗಿರುತ್ತದೆ. ಇದರ ಹೊಂದಿಕೆಯನ್ನು "
+#~ "ರದ್ದುಗೊಳಿಸಿದಲ್ಲಿ ಅಥವ ಒಂದು ಖಾಲಿ ಮೌಲ್ಯಕ್ಕೆ ಹೊಂದಿಸಿದಲ್ಲಿ, ಪೂರ್ವನಿಯೋಜಿತ ಪೈಪ್‌ಲೈನ್ ಅನ್ನು "
+#~ "ಬಳಸಲಾಗುತ್ತದೆ. ಇದು ಪ್ರಸ್ತುತ 'vp8enc min_quantizer=13 max_quantizer=13 cpu-"
+#~ "used=5 deadline=1000000 threads=%T ! queue ! webmmux' ಆಗಿರುತ್ತದೆ ಮತ್ತು VP8 "
+#~ "ಕೋಡೆಕ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು WEBM ಅನ್ನು ರೆಕಾರ್ಡು ಮಾಡುತ್ತದೆ. %T ಎನ್ನು ವ್ಯವಸ್ತೆಯಲ್ಲಿನ "
+#~ "ಸೂಕ್ತವಾದ ಎಳೆಯ ಎಣಿಕೆಯನ್ನು ಊಹಿಸಲು ಒಂದು ಪ್ಲೇಸ್‌ಹೋಲ್ಡರ್ ಆಗಿ ಬಳಸಲಾಗುತ್ತದೆ."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "ಸ್ಕ್ರೀನ್‌ಕ್ಯಾಸ್ಟುಗಳನ್ನು ಶೇಖರಿಸಿಡಲು ಬಳಸಲಾಗುವ ಕಡತ ವಿಸ್ತರಣೆಗಳು"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "ರೆಕಾರ್ಡು ಮಾಡಲಾದ ಸ್ಕ್ರೀನ್‌ಕ್ಯಾಸ್ಟುಗಳಿಗಾಗಿನ ಕಡತದ ಹೆಸರು ಪ್ರಸಕ್ತ ದಿನಾಂಕದ ಆಧರಿತವಾದ ಒಂದು "
+#~ "ವಿಶೇಷವಾದ ಕಡತದ ಹೆಸರಾಗಿರುತ್ತದೆ, ಹಾಗು ಈ ವಿಸ್ತರಣೆಯನ್ನು ಬಳಸುತ್ತದೆ. ಬೇರೊಂದು ಕಂಟೈನರ್ "
+#~ "ಶೈಲಿಗೆ ರೆಕಾರ್ಡ್ ಮಾಡುವಾಗ ಇದನ್ನು ಬದಲಾಯಿಸಬೇಕಾಗುತ್ತದೆ."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "ಶೀರ್ಷಿಕೆಪಟ್ಟಿಯಲ್ಲಿ ಗುಂಡಿಗಳ ಜೋಡಣೆ"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "ಈ ಕೀಲಿಯು GNOME ಶೆಲ್‌ ಅನ್ನು ಚಲಾಯಿಸುವಾಗ org.gnome.desktop.wm.preferences ನಲ್ಲಿನ "
+#~ "ಕೀಲಿಯನ್ನು ಅತಿಕ್ರಮಿಸುತ್ತದೆ."
+
+#~ msgid "Extension"
+#~ msgstr "ವಿಸ್ತರಣೆ"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "ಮೇಲಿನ ಸಂಯೋಜನಾಚೌಕವನ್ನು ಬಳಸಿಕೊಂಡು ಸಂರಚಿಸಬೇಕಿರುವ ಒಂದು ವಿಸ್ತರಣೆಯನ್ನು ಆರಿಸಿ."
+
+#~ msgid "Session"
+#~ msgstr "ಅಧಿವೇಶನ"
+
+#~ msgid "Power"
+#~ msgstr "ವಿದ್ಯುಚ್ಛಕ್ತಿ"
+
+#~ msgid "Restart"
+#~ msgstr "ಮರಳಿ ಆರಂಭಿಸು"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d %t ಇಂದ ತೆರೆಚಲನಚಿತ್ರ"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "ಈ ಅನ್ವಯದಿಂದ ನಿರ್ಗಮಿಸಿ ನಂತರ ವ್ಯವಸ್ಥೆಯಿಂದ ನಿರ್ಗಮಿಸಲು ನಿರ್ಗಮಿಸು ಅನ್ನು ಒತ್ತಿ."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "ವ್ಯವಸ್ಥೆಯಿಂದ ನಿರ್ಗಮಿಸಲಾಗುತ್ತಿದೆ."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "ಈ ಅನ್ವಯದಿಂದ ನಿರ್ಗಮಿಸಿ ನಂತರ ವ್ಯವಸ್ಥೆಯಿಂದ ನಿರ್ಗಮಿಸಲು ಸ್ಥಗಿತಗೊಳಿಸು ಅನ್ನು ಒತ್ತಿ."
+
+#~ msgid "Powering off the system."
+#~ msgstr "ವ್ಯವಸ್ಥೆಯನ್ನು ಸ್ಥಗಿತಗೊಳಿಸಲಾಗುತ್ತಿದೆ."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "ಈ ಅನ್ವಯದಿಂದ ನಿರ್ಗಮಿಸಿ ಗಣಕವನ್ನು ಮರಳಿ ಅರಂಭಿಸಲು ಮರಳಿ ಆರಂಭಿಸು ಅನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ."
+
+#~ msgid "Restarting the system."
+#~ msgstr "ನಿಮ್ಮ ವ್ಯವಸ್ಥೆಯನ್ನು ಮರಳಿ ಆರಂಭಿಸಲಾಗುತ್ತಿದೆ."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "ಜಾಗತಿಕ ನಿಲುಕಣೆ ಸಿದ್ಧತೆಗಳು"
+
+#~ msgid "Visibility"
+#~ msgstr "ಗೋಚರಿಕೆ"
+
+#~ msgid "Send Files to Device…"
+#~ msgstr "ಸಾಧನಕ್ಕೆ ಕಡತಗಳನ್ನು ಕಳುಹಿಸಿ…"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "ಒಂದು ಹೊಸ ಸಾಧನವನ್ನು ಸಿದ್ಧಗೊಳಿಸು…"
+
+#~ msgid "Send Files…"
+#~ msgstr "ಕಡತಗಳನ್ನು ಕಳುಹಿಸು…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "ಕೀಲಿಮಣೆ ಸಿದ್ಧತೆಗಳು"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "ಮೌಸ್‌ನ ಸಿದ್ಧತೆಗಳು"
+
+#~ msgid "Sound Settings"
+#~ msgstr "ಧ್ವನಿಯ ಸಿದ್ಧತೆಗಳು"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s ಇಂದ ದೃಢೀಕರಣ ದೋಷ"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "%s ಎಂಬ ಸಾಧನಕ್ಕಾಗಿ '%s' ಎಂಬ ಸೇವೆಯನ್ನು ನಿಲುಕಿಸಿಕೊಳ್ಳಲು ಬಯಸುತ್ತಿದೆ"
+
+#~ msgid "Always grant access"
+#~ msgstr "ಯಾವಾಗಲೂ ಅನುಮತಿಸು"
+
+#~ msgid "Grant this time only"
+#~ msgstr "ಈ ಬಾರಿ ಮಾತ್ರ ಅನುಮತಿಸು"
+
+#~ msgid "Reject"
+#~ msgstr "ತಿರಸ್ಕರಿಸು"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s ಗಾಗಿ ಪೇರಿಂಗ್ ಖಚಿತಪಡಿಕೆ"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "%s ಎನ್ನುವ ಸಾಧನದೊಂದಿಗೆ ಸಂಪರ್ಕ ಜೋಡಿಸಲು ಬಯಸುತ್ತಿದೆ"
+
+#~ msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "'%06d' ಎಂಬ PIN ಸಾಧನದಲ್ಲಿರುವುದಕ್ಕೆ ತಾಳೆಯಾಗುತ್ತದೆಯೆ ಎಂದು ದಯವಿಟ್ಟು ಖಚಿತಪಡಿಸಿ."
+
+#~ msgid "Matches"
+#~ msgstr "ಹೊಂದಾಣಿಕೆಗಳು"
+
+#~ msgid "Does not match"
+#~ msgstr "ತಾಳೆಯಾಗುತ್ತಿಲ್ಲ"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s ಗಾಗಿ ಪೇರಿಂಗ್ ಮನವಿ"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "ದಯವಿಟ್ಟು ಸಾಧನದಲ್ಲಿ ಸೂಚಿಸಿರುವ PIN ಅನ್ನು ನಮೂದಿಸಿ."
+
+#~ msgid "OK"
+#~ msgstr "ಸರಿ"
+
+#~ msgid "Region & Language Settings"
+#~ msgstr "ಪ್ರದೇಶ ಮತ್ತು ಭಾಷೆ ಸಿದ್ಧತೆಗಳು"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "ಧ್ವನಿಪ್ರಮಾಣ, ಜಾಲಬಂಧ, ಬ್ಯಾಟರಿ"
+
+#~ msgid "disabled"
+#~ msgstr "ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"
+
+#~ msgid "cable unplugged"
+#~ msgstr "ಕೇಬಲ್‌ ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"
+
+#~ msgid "More…"
+#~ msgstr "ಇನ್ನಷ್ಟು …"
+
+#~ msgid "Wired"
+#~ msgstr "ತಂತಿಯುಕ್ತ"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "ಸ್ವಯಂ-ಎತರ್ನೆಟ್"
+
+#~ msgid "Auto broadband"
+#~ msgstr "ಸ್ವಯಂ-ಬ್ರಾಡ್‌ಬ್ಯಾಂಡ್"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "ಸ್ವಯಂ ಡಯಲ್-ಅಪ್"
+
+#~ msgid "Auto %s"
+#~ msgstr "ಸ್ವಯಂ %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "ಸ್ವಯಂ ಬ್ಲೂಟೂತ್"
+
+#~ msgid "Auto wireless"
+#~ msgstr "ಸ್ವಯಂ ತಂತಿರಹಿತ"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "ವೈ-ಫೈ"
+
+#~ msgid "Networking is disabled"
+#~ msgstr "ಜಾಲಬಂಧವು ವಿಫಲಗೊಂಡಿದೆ"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d ಗಂಟೆ ಬಾಕಿ ಇದೆ"
+#~ msgstr[1] "%d ಗಂಟೆಗಳು ಬಾಕಿ ಇವೆ"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s ಬಾಕಿ ಇದೆ"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ಗಂಟೆ"
+#~ msgstr[1] "ಗಂಟೆಗಳು"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "ನಿಮಿಷ"
+#~ msgstr[1] "ನಿಮಿಷಗಳು"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d ನಿಮಿಷ ಬಾಕಿ ಇದೆ"
+#~ msgstr[1] "%d ನಿಮಿಷಗಳು ಬಾಕಿ ಇವೆ"
+
+#~ msgctxt "percent of battery remaining"
+#~ msgid "%d%%"
+#~ msgstr "%d%%"
+
+#~ msgid "AC Adapter"
+#~ msgstr "AC ಅಡಾಪ್ಟರ್"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "ಲ್ಯಾಪ್‌ಟಾಪ್ ಬ್ಯಾಟರಿ"
+
+#~ msgid "Monitor"
+#~ msgstr "ತೆರೆ"
+
+#~ msgid "Mouse"
+#~ msgstr "ಮೌಸ್"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "ಸೆಲ್ ಫೋನ್"
+
+#~ msgid "Media Player"
+#~ msgstr "ಮಾಧ್ಯಮ ಚಾಲಕ"
+
+#~ msgid "Tablet"
+#~ msgstr "ಟ್ಯಾಬ್ಲೆಟ್"
+
+#~ msgid "Computer"
+#~ msgstr "ಗಣಕ"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "ಗೊತ್ತಿಲ್ಲದ"
+
+#~ msgid "Available"
+#~ msgstr "ಲಭ್ಯ"
+
+#~ msgid "Busy"
+#~ msgstr "ಕಾರ್ಯನಿರತ"
+
+#~ msgid "Invisible"
+#~ msgstr "ಅಗೋಚರ"
+
+#~ msgid "Away"
+#~ msgstr "ಹೊರಗೆ ಹೋಗಿದ್ದೇನೆ"
+
+#~ msgid "Idle"
+#~ msgstr "ಜಡ"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "ನಿಮ್ಮ ಹರಟೆಯ ಸ್ಥಿತಿಯನ್ನು ಕಾರ್ಯನಿರತರಾಗಿದ್ದಾರೆ ಎಂದು ಬದಲಾಯಿಸಲಾಗಿದೆ."
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "ಸಂಭಾಷಣೆಯ ಸಂದೇಶಗಳೂ ಸಹ ಸೇರಿದಂತೆ ಸೂಚನೆಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿರುತ್ತದೆ. ನೀವು "
+#~ "ಬೇರೆಯವರಿಂದ ಬಂದ ಸಂದೇಶಗಳನ್ನು ನೀವು ನೋಡಿಲ್ಲ ಎಂದು ಅವರಿಗೆ ಗೊತ್ತಾಗುವಂತೆ ನಿಮ್ಮ ಆನ್‌ಲೈನ್ "
+#~ "ಸ್ಥಿತಿಯನ್ನು ಹೊಂದಿಸಲಾಗಿರುತ್ತದೆ."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "ಸ್ಥಗಿತಗೊಳಿಸಿದಲ್ಲಿ ಉಳಿಸದೆ ಇರುವ ಕೆಲಸವು ಇಲ್ಲವಾಗಲು ಕಾರಣವಾಗಬಹುದು."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "ಕ್ಷಮಿಸಿ, ನಿಮಗಾಗಿ ಯಾವುದೆ ವಿವೇಕವಾಣಿ ಇಲ್ಲ :\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s Oracle ಹೇಳುವಂತೆ"
diff --git a/po/ko.po b/po/ko.po
new file mode 100644
index 0000000..c27c4c8
--- /dev/null
+++ b/po/ko.po
@@ -0,0 +1,3115 @@
+# Korean message translation for gnome-shell
+#
+# This file is distributed under the same license as the gnome-shell package.
+# Young-Ho Cha <ganadist@gmail.com>, 2009.
+# Seong-ho Cho <darkcircle.0426@gmail.com>, 2012.
+# Changwoo Ryu <cwryu@debian.org>, 2011-2022.
+#
+#
+# 주의 사항:
+# - 이 프로그램의 이름인 shell은 "셸"이라고 쓰고, "쉘"이라고 하지 않음.
+# - 용어:
+# - Airplane Mode: 비행기 모드
+# - Dash: 대시보드
+# - instance messanger 또는 IM은 메신저라고 번역
+# - VPN: 가상사설망
+# - pairing (bluetooth 관련): 페어링
+# - screencast: 스크린캐스트
+# - Wayland: 웨일랜드로 음역
+# - Night Light: 야간 모드 (gnome-control-center와 일치)
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-08-12 03:40+0000\n"
+"PO-Revision-Date: 2022-09-03 22:50+0900\n"
+"Last-Translator: Changwoo Ryu <cwryu@debian.org>\n"
+"Language-Team: GNOME Korea <gnome-kr@googlegroups.com>\n"
+"Language: ko\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "실행"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "즐겨 찾는 앱 1 활성화"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "즐겨 찾는 앱 2 활성화"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "즐겨 찾는 앱 3 활성화"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "즐겨 찾는 앱 4 활성화"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "즐겨 찾는 앱 5 활성화"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "즐겨 찾는 앱 6 활성화"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "즐겨 찾는 앱 7 활성화"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "즐겨 찾는 앱 8 활성화"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "즐겨 찾는 앱 9 활성화"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2070
+msgid "Screenshots"
+msgstr "스크린샷"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "대화식으로 스크린샷 찍기"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "스크린샷 찍기"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "창의 스크린샷 찍기"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "대화식으로 스크린캐스트 녹화"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "시스템"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "알림 목록 표시"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "현재 알림에 포커스"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "요약 보기"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "모든 앱 표시"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "앱 메뉴 열기"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "그놈 셸"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "창 관리 및 앱 실행"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Alt-F2에서 개발 및 테스트용 내부 기능 사용 가능"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "Alt-F2 대화 상자에서 내부 디버깅 및 감시 기능에 접근을 허용합니다."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "사용할 확장의 UUID 목록"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"그놈 셸 확장에는 UUID 속성이 있습니다. 이 키는 읽어들일 확장의 UUID를 나열합"
+"니다. 읽어들이려는 확장은 모두 이 목록에 들어 있어야 합니다. EnableExtension "
+"및 DisableExtension D-버스 메소드를 이용해 이 목록을 조작할 수도 있습니다."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "사용하지 않도록 강제한 확장의 UUID 목록"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"그놈 셸 확장에는 UUID 속성이 있습니다. 이 키는 사용하지 않을 확장의 UUID를 나"
+"열합니다. 현재 모드의 일부로 해당 확장을 읽어들이더라도 사용하지 않습니다. "
+"org.gnome.Shell의 EnableExtension 및 DisableExtension D-버스 메소드를 이용해 "
+"이 목록을 조작할 수도 있습니다. 이 키가 “enabled-extensions” 설정보다 우선합"
+"니다."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "사용자 확장 기능 사용하지 않기"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"“enabled-extension” 설정에 영향을 미치지 않고, 사용자가 켠 확장 기능을 모두 "
+"사용하지 않습니다."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "확장의 버전 호환성 검사 하지 않기"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"그놈 셸에서 현재 실행 중인 버전을 지원하는 확장 기능만 읽어들입니다. 이 옵션"
+"을 사용하면 이 버전 검사를 하지 않고, 지원한다고 지정된 버전에 관계없이 모든 "
+"확장 기능을 읽어들입니다."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "즐겨찾기 앱의 데스크톱 파일 ID 목록"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "여기 들어 있는 ID에 해당하는 앱은 즐겨찾기 영역에 표시됩니다."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "명령어 대화 상자에(Alt-F2) 기록 기능"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "돋보기 대화 상자에 기록 기능"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "사용자 메뉴에 “로그아웃” 메뉴 항목을 항상 표시합니다."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"이 키를 설정하면, 단일 사용자 및 단일 세션에서 “로그아웃” 메뉴 항목 자동 감추"
+"기 기능을 사용하지 않습니다."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"암호화 볼륨을 마운트하거나 원격 파일 시스템을 마운트할 때 암호를 저장할지 여"
+"부"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"암호화된 저장 장치나 원격 파일 시스템을 마운트하는 경우 암호를 입력해야 합니"
+"다. 암호를 저장할 수 있으면 “암호 저장” 확인란이 나타납니다. 이 키에서는 이 "
+"확인란의 기본 상태를 지정합니다."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "기본 블루투스 어댑터가 여기 연결할 장치를 준비했는지 여부"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"그놈 셸에서는 블루투스 어댑터에 전원이 들어오거나, 기본 어댑터에 연결 설정한 "
+"장치가 있을 경우엠나 경우에만 블루투스 메뉴 항목을 표시합니다. 기본 어댑터에 "
+"연결된 장치가 하나도 없는 상황이 발견되면 이 값을 초기화합니다."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "최근 선택한 (기본값이 아닌) 전력 프로필"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr "일부 시스템은 여러 가지의 전력 프로필을 지원합니다. 두 개의 프로필 사이를 토글하는 기능을 지원하려고, 이 키는 최근 선택한 기본값이 아닌 전력 프로필을 저장합니다."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "“환영합니다 — 그놈” 대화 상자가 표시된 최근 버전"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"이 키는 “환영합니다 — 그놈” 대화 상자가 최근 표시된 버전을 지정합니다. 빈 문"
+"자열이면 가능한 가장 오래된 버전을 뜻하고, 매우 큰 번호는 아직 없는 버전을 뜻"
+"합니다. 매우 큰 번호를 써서 이 대화상자를 표시하지 않는 방법으로 사용할 수 있"
+"습니다."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "앱 고르기의 배치"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"앱 고르기의 배치. 배열의 각 항목은 페이지입니다. 페이지는 그놈 셸에 나"
+"타나는 순서로 저장됩니다. 각 페이지에는 “application id” → 'data'의 쌍이 하"
+"나 들어 있습니다. 현재 다음 값이 'data'로 저장됩니다: • “position”: 페이지에"
+"서 앱 아이콘의 위치"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "앱 메뉴를 여는 키 바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "앱 메뉴를 여는 키 바인딩."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "현재 활동 요약 상태를 전환하는 키 바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "세션, 창 고르기, 앱 그리드 사이에서 전환하는 키바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "앱 그리드, 창 고르기, 세션 사이에서 전환하는 키바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "“앱 보기” 뷰를 여는 키 바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "현재 활동 요약의 “앱 보기” 뷰를 여는 키 바인딩."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "현재 활동 요약을 여는 키 바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "현재 활동 요약을 여는 키 바인딩."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "알림 목록 보이기를 토글하는 키 바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "알림 목록 보이기를 토글하는 키 바인딩."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "현재 알림에 포커스하는 키 바인딩"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "현재 알림에 포커스하는 키 바인딩."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "앱 1로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "앱 2로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "앱 3로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "앱 4로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "앱 5로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "앱 6으로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "앱 7로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "앱 8로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "앱 9로 전환"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "앱 전환을 현재 작업 공간에만 한정."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"참이면, 현재 작업 공간에 창이 있는 앱만 앱 전환 창에 표시합니다. "
+"참이 아니면 모든 앱을 표시합니다."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "앱 아이콘 모드."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"창 전환에서 창이 어떻게 표시될지 설정합니다. 가능한 값은: “thumbnail-only”(창"
+"의 섬네일만 표시), “app-icon-only”(앱 아이콘만 표시), “both”(모두)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"참이면, 현재 작업 공간에 창이 있는 창만 창 전환 창에 표시합니다. 거짓이면 모"
+"든 창을 표시합니다."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "위치 목록"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "세계 시계에서 표시할 위치 목록"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "자동 위치"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "현재 위치를 가져올지 여부"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "위치"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "날씨 표시에 사용할 위치"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "상위 창에 모달 대화창 붙이기"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "그놈 셸을 실행할 때 org.gnome.mutter의 키 대신 사용됩니다."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "화면 가장자리에 창을 놓을 때 가장자리에 맞춥니다"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "작업 공간을 동적으로 관리"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "주 모니터에서만 작업 공간 사용"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "마우스 포인터가 움직이지 않을 때까지 포커스 전환을 미루기"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "네트워크 로그인"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "무언가 잘못되었습니다"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"죄송하지만 문제가 발생했습니다. 이 확장의 설정을 표시할 수 없습니다. 확장 개"
+"발자에게 이 문제를 알려주시기를 추천합니다."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "기술적 정보"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "홈페이지"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "확장 홈페이지 보기"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "취소"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "암호"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "세션 선택"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "목록에 없습니까?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(예를 들어, 사용자 또는 %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "사용자 이름"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "로그인 창"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "인증 오류"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(또는 지문을 센서에 문지르십시오)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(또는 손가락을 센서에 대십시오)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "컴퓨터 끄기"
+
+# 주의: 키워드 번역
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;전원;끄기;셧다운;halt;stop;중지"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "다시 시작"
+
+# 주의: 키워드 번역
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;재시작;다시;리부팅"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "화면 잠그기"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "화면 잠그기"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "로그아웃"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;로그아웃;log out;로그 아웃;sign off;나가기"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "절전"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;절전;슬립;서스펜드"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "사용자 바꾸기"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "사용자 바꾸기"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;방향 잠금;unlock orientation;방향 잠금 해제;screen;화면;"
+"rotation;회전"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "스크린샷 찍기"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;스크린샷;screencast;스크린캐스트;snip;스닙;capture;캡쳐;record;녹"
+"화"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "화면 회전 잠금 해제"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "화면 회전 잠금"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "명령이 없습니다"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "명령어를 파싱할 수 없습니다:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” 실행이 실패했습니다:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "방금"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d분 전"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d시간 전"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "어제"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d일 전"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d주 전"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d달 전"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d년 전"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "어제, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d일, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%Y년 %B %-d일, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%p %l∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "어제, %p %l∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %p %l∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d일, %p %l∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%Y년 %B %-d일, %p %l∶%M"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "핫스팟 로그인"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"이 핫스팟 로그인에 대한 연결은 안전하지 않습니다. 암호나 기타 이 페이지에서 "
+"입력한 정보를 주변 사람이 볼 수 있습니다."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "접근 거부"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "접근 허용"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "이름 없는 폴더"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s을(를) 대시보드에 고정했습니다."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s을(를) 대시보드에서 고정 해제했습니다."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "열린 창"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "새 창"
+
+# 주의: "Show ..." 형태지만 보이기로 번역하지 않는다
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "자세히 보기"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "끝내기"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "고정 해제"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "대시보드에 고정"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "내장 그래픽 카드를 사용해 시작"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "외장 그래픽 카드를 사용해 시작"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "오디오 장치 선택"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "소리 설정"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "헤드폰"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "헤드셋"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "마이크"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "배경 바꾸기…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "디스플레이 설정"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "설정"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "일"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "월"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "화"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "수"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "목"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "금"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "토"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:412
+msgid "%OB %Y"
+msgstr "%Y년 %OB"
+
+#: js/ui/calendar.js:473
+msgid "Previous month"
+msgstr "이전 달"
+
+#: js/ui/calendar.js:491
+msgid "Next month"
+msgstr "다음 달"
+
+#: js/ui/calendar.js:642
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:701
+msgid "Week %V"
+msgstr "주 %V"
+
+#: js/ui/calendar.js:880
+msgid "No Notifications"
+msgstr "알림 없음"
+
+#: js/ui/calendar.js:937
+msgid "Do Not Disturb"
+msgstr "방해 금지"
+
+#: js/ui/calendar.js:958
+msgid "Clear"
+msgstr "지우기"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” 앱이 응답하지 않습니다."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"좀 더 기다려서 계속 할 수도 있고, 강제로 앱을 끝낼 수도 있습니다."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "강제로 끝내기"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "기다리기"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "외부 드라이브 연결"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "외부 드라이브 연결 끊김"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "볼륨을 잠글 수 없습니다"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "설치한 버전의 udisks는 PIM 설정을 지원하지 않습니다"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s 앱으로 열기"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "다른 방법으로 라우터의 “WPS” 단추를 눌러서 연결할 수 있습니다."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "연결"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "키"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "개인 키 암호"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "신원"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "서비스"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "인증 필요"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "“%s” 무선 네트워크에 연결하려면 암호 또는 암호화 키가 필요합니다."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "유선 802.1X 인증"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "네트워크 이름"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "인증이 필요합니다"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN 코드가 필요합니다"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "휴대전화 네트워크 장치를 사용하려면 PIN 코드가 필요합니다"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s”에 연결하려면 암호가 필요합니다."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "네트워크 관리"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN 암호"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "인증이 필요합니다"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "관리자"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "인증"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "죄송합니다. 동작하지 않았습니다. 다시 시도하십시오."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s의 대화명이 이제 %s입니다"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "창"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "앱 표시"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "대시보드"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%Y년 %B %-d일"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%Y년 %B %e일 %A"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d일"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y년 %B %-d일"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "오늘"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "내일"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "종일"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "행사 없음"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "세계 시계 추가…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "세계 시계"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "읽어들이는 중…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "날씨 정보를 온라인으로 가져옵니다"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "날씨 정보를 현재 사용할 수 없습니다"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "날씨"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "날씨 위치를 선택하십시오…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s 로그아웃"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "로그아웃"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s 사용자가 %d초 뒤에 자동으로 로그아웃합니다."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "%d초 뒤에 자동으로 로그아웃합니다."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "로그아웃"
+
+#: js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Power Off"
+msgstr "컴퓨터 끄기"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "업데이트 설치 및 컴퓨터 끄기"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "시스템이 %d초 뒤에 자동으로 꺼집니다."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "대기 중인 소프트웨어 업데이트 설치"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "컴퓨터 끄기"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "다시 시작"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "업데이트 설치 및 컴퓨터 다시 시작"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "시스템이 %d초 뒤에 자동으로 다시 시작합니다."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "다시 시작"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "다시 시작 및 업데이트 설치"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "시스템이 %d초 뒤에 자동으로 다시 시작하고 업데이트를 설치합니다."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "다시 시작 및 설치"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "설치 및 컴퓨터 끄기"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "업데이트를 설치한 다음 컴퓨터를 끕니다"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "다시 시작 및 업그레이드 설치"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"다시 시작한 뒤에 %s %s 버전을 설치합니다. 업그레이드 설치가 오래 걸릴 수도 있"
+"습니다: 백업을 하고 컴퓨터에 전원을 연결하십시오."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "배터리가 부족합니다: 업데이트를 설치하려면 전원을 연결하십시오."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "일부 앱이 동작 중이거나 저장하지 않은 작업이 남아 있습니다"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "다른 사용자가 로그인 중입니다"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "부팅 옵션"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (원격)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (콘솔)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "설치"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "확장 설치"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+"extensions.gnome.org 사이트에서 “%s” 확장을 다운로드해 설치하시겠습니까?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "확장 업데이트가 있습니다"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "확장 업데이트 설치할 준비가 되었습니다."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "바로 가기 금지 허용"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "%s 앱이 바로 가기를 금지하려 합니다"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "앱이 바로 가기를 금지하려 합니다"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "%s 키를 누르면 바로 가기를 복구할 수 있습니다."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "거부"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "허용"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "느린 키 켜짐"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "느린 키 꺼짐"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Shift 키를 8초 동안 누르고 있었습니다. 느린 키 기능의 바로 가기이고, 이렇게 "
+"하면 키보드 동작이 달라집니다."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "고정 키 켜짐"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "고정 키 꺼짐"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Shift 키를 5번 연속으로 눌렀습니다. 고정 키 기능의 바로 가기이고, 이렇게 하"
+"면 키보드 동작이 달라집니다."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"두 키를 동시에 누르거나, Shift 키를 5번 연속으로 눌렀습니다. 고정 키 기능을 "
+"끄는 바로 가기이고, 이렇게 하면 키보드 동작이 달라집니다."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "계속 유지"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "켜기"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "끄기"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "그만두고 끄기"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "지역 및 언어 설정"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "확장을 설치하지 않았습니다"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s에서 발생한 에러가 없습니다."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "오류 숨기기"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "오류 보이기"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "사용"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "사용 않음"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "오류"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "오래 된 버전"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "다운로드 중"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "소스 보기"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "웹페이지"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "시스템이 위험 모드에 들어가 있습니다"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "현재 앱에서 제한적인 접근만 할 수 있습니다"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "실행 취소"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "관리자 권한으로 로그인"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"관리자 권한으로 세션을 시작하는 일은 보안 이유로 피해야 합니다. 할 수 있으면 "
+"일반 유저로 로그인해야 합니다."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "화면 잠금 사용하지 않음"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "화면 잠금은 그놈 디스플레이 관리자가 필요합니다."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "시스템 정보"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "알 수 없는 음악가"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "알 수 없는 제목"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "검색하려면 입력하십시오"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "앱"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "개요"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "새 바로 가기…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "정의한 앱"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "화면 도움말 표시"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "모니터 바꾸기"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "키 누르기 지정"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "마침"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "편집…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "없음"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "설정하려면 단추를 누르십시오"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "나가려면 Esc를 누르십시오"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "나가려면 아무 키나 누르십시오"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "현재 활동"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "시스템"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "맨 위 표시줄"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "명령 실행"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "닫으려면 ESC를 누르십시오"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "웨일랜드에서는 다시 시작 기능을 사용할 수 없습니다"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "다시 시작하는 중…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "그놈에서 화면을 잠궈야 합니다"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "잠글 수 없습니다"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "앱이 잠금을 막았습니다"
+
+#: js/ui/screenshot.js:1149
+msgid "Selection"
+msgstr "선택"
+
+#: js/ui/screenshot.js:1159
+msgid "Area Selection"
+msgstr "영역 선택"
+
+#: js/ui/screenshot.js:1164
+msgid "Screen"
+msgstr "화면"
+
+#: js/ui/screenshot.js:1174
+msgid "Screen Selection"
+msgstr "화면 선택"
+
+#: js/ui/screenshot.js:1179
+msgid "Window"
+msgstr "창"
+
+#: js/ui/screenshot.js:1189
+msgid "Window Selection"
+msgstr "창 선택"
+
+#: js/ui/screenshot.js:1227
+msgid "Screenshot / Screencast"
+msgstr "스크린샷 / 스크린캐스트"
+
+#: js/ui/screenshot.js:1263
+msgid "Show Pointer"
+msgstr "포인터 보이기"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1840
+msgid "Screencasts"
+msgstr "스크린캐스트"
+
+# 주의: 파일 이름
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1845
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "스크린캐스트 %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1911 js/ui/screenshot.js:2123
+msgid "Screenshot"
+msgstr "스크린샷"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1917
+msgid "Screencast recorded"
+msgstr "스크린캐스트 녹화함"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1919
+msgid "Click here to view the video."
+msgstr "여기를 눌러 비디오를 보십시오."
+
+# 주의: "Show ..." 형태지만 보이기로 번역하지 않는다
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1922 js/ui/screenshot.js:2137
+msgid "Show in Files"
+msgstr "파일에서 보기"
+
+# 주의: 파일 이름
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2083
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "스크린샷 %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2129
+msgid "Screenshot captured"
+msgstr "스크린샷 찍음"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2131
+msgid "You can paste the image from the clipboard."
+msgstr "클립보드에서 이미지를 붙여넣을 수 있습니다."
+
+#: js/ui/screenshot.js:2184 js/ui/screenshot.js:2349
+msgid "Screenshot taken"
+msgstr "찍은 스크린샷"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "검색하는 중…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "결과가 없습니다."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d개 더"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "검색"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "복사"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "붙여넣기"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "텍스트 보이기"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "텍스트 숨기기"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock이 켜져 있습니다."
+
+# 오디오 볼륨
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "숨겨진 볼륨"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "윈도우 시스템 볼륨"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "keyfiles 사용"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"keyfiles를 사용하는 볼륨의 잠금을 해제하려면, 대신 <i>%s</i> 유틸리티를 사용"
+"하십시오."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM 번호"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "암호 저장"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "잠금 풀기"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s 앱 열기"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM은 숫자거나 비어 있어야 합니다."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s 앱을 시작할 수 없습니다"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s 앱을 찾을 수 없습니다"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "접근성"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "고대비"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "크기 조정"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "화면 읽기"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "화면 키보드"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "화면 알림"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "고정 키"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "느린 키"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "탄력 키"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "마우스 키"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "큰 글자"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "자동 회전"
+
+#: js/ui/status/bluetooth.js:160
+msgid "Bluetooth"
+msgstr "블루투스"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "밝기"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "다크 모드"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "한번 누르기"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "두번 누르기"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "끌기"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "보조 누르기"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "머무르면 누르기"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "키보드"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "키보드 배치 표시"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "위치 접근 허용"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "%s 앱이 현재 위치에 접근하려고 합니다"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "위치 정보 접근은 언제든지 사생활 보호 설정에서 바꿀 수 있습니다."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<알 수 없음>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "%s 연결"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "%s 연결 해제"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s 핫스팟"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "야간 모드"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "성능"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "균형"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "전기 절약"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "컴퓨터 프로필"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "스크린캐스트 중지"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "화면 공유 중지"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "비행기 모드"
+
+#: js/ui/status/system.js:89
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/system.js:140
+msgid "Power Off Menu"
+msgstr "컴퓨터 끄기 메뉴"
+
+#: js/ui/status/system.js:148
+msgid "Suspend"
+msgstr "절전"
+
+#: js/ui/status/system.js:153
+msgid "Restart…"
+msgstr "다시 시작…"
+
+#: js/ui/status/system.js:158
+msgid "Power Off…"
+msgstr "컴퓨터 끄기…"
+
+#: js/ui/status/system.js:165
+msgid "Log Out"
+msgstr "로그아웃"
+
+#: js/ui/status/system.js:170
+msgid "Switch User…"
+msgstr "사용자 바꾸기…"
+
+#: js/ui/status/system.js:216
+msgid "Lock Screen"
+msgstr "화면 잠그기"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "썬더볼트"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "알 수 없는 썬더볼트 장치"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"자리에 없는 동안 새 장치를 검색했습니다. 이 장치를 사용하려면 장치 연결을 끊"
+"었다가 다시 연결하십시오."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "권한 없는 썬더볼트 장치"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "새 장치가 발견되었고 관리자가 허가해야 합니다."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "썬더볼트 권한 오류"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "썬더볼트 장치에 권한을 부여할 수 없습니다: %s"
+
+# 오디오 볼륨
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "볼륨 바꿈"
+
+# 오디오 볼륨
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "볼륨"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "소리 출력"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "소리 입력"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "반복"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "디스플레이 연결"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "외부만 사용"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "내장만 사용"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%B %-d %A"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "위로 밀어 잠금 해제"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "잠금을 해제하려면 마우스 단추 또는 키를 누르십시오"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "창 잠금 해제"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "다른 사용자로 로그인"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "환영합니다 — 그놈 %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "사용 방법을 배우고 싶으시면, 둘러보기를 확인해 보십시오."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "괜찮습니다"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "둘러보기"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” 앱이 준비되었습니다"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "이 디스플레이 설정을 유지하시겠습니까?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "설정 되돌리기"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "바뀐 사항 유지"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "바뀐 설정을 %d초 후에 되돌립니다"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "스크린샷 찍기"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "숨기기"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "복원"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "최대화"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "옮기기"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "크기 조정"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "제목 표시줄을 화면에 옮기기"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "항상 위"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "항상 보이는 작업 공간에"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "왼쪽 작업 공간으로 옮기기"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "오른쪽 작업 공간으로 옮기기"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "위 작업 공간으로 옮기기"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "아래 작업 공간으로 옮기기"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "위 모니터로 옮기기"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "아래 모니터로 옮기기"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "왼쪽 모니터로 옮기기"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "오른쪽 모니터로 옮기기"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "닫기"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "에볼루션 달력"
+
+# 커맨드라인 옵션 설명
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "버전을 표시합니다"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "GDM에서 로그인 화면에 사용할 모드"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "특정 모드 사용. 예를 들어 로그인 화면에 대해 “gdm”"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "가능한 모드 목록 표시"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "알 수 없음"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” 실행에 실패했습니다"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "암호가 일치하지 않습니다."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "빈 암호를 쓸 수 없습니다"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "인증 대화 창을 사용자가 닫았습니다"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "확장"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "그놈 확장을 관리합니다"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "그놈 프로젝트"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"그놈 확장 관리자에서는 확장 기능의 업데이트, 확장 기능의 설정, 원하지 않는 확"
+"장 기능 제거 또는 사용 중지를 처리합니다."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "그놈 셸 확장을 설정합니다"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "일치 항목이 없습니다"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "“%s” 확장을 제거하시겠습니까?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr "확장을 제거한 다음 다시 사용하려면 다시 다운로드해야 합니다"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "제거"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"차영호 <ganadist@gmail.com>\n"
+"조성호 <darkcircle.0426@gmail.com>\n"
+"류창우 <cwryu@debian.org>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "다음 로그인에 확장 %d개를 업데이트할 예정입니다."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "이 확장 기능은 현재 그놈 버전과 호환되지 않습니다"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "확장 기능에 오류가 있습니다"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "확장을 업데이트할 수 있습니다"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "홈페이지"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "제거…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "도움말"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "확장 정보"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"확장 기능은 성능 저하 또는 안정성 문제를 일으킬 수 있습니다. 시스템에 문제를 "
+"발견하면 확장을 사용 중지하십시오."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "수동 설치"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"확장을 찾고 추가하려면, <a href=\"https://extensions.gnome.org\">extensions."
+"gnome.org</a> 사이트를 방문하십시오."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "내장"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "설치한 확장 없음"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"대단히 죄송합니다. 설치한 확장 기능의 목록을 가져올 수 없습니다. 그놈 데스크"
+"톱에 로그인했는지 확인하시고 다시 시도하십시오."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "확장 업데이트가 준비되었습니다"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "로그아웃…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "새 확장을 %s 안에 성공적으로 만들었습니다.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"이름은 아주 짧은 (무슨 기능인지 설명이 되면 좋은) 문자열이어야 합니다.\n"
+"예: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "이름"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"설명은 이 확장 기능이 무슨 일을 하는지 한 문장으로 설명합니다.\n"
+"예: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "설명"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID는 이 확장을 나타내는 전셰계적으로 유일한 ID 값입니다.\n"
+"전자메일 주소의 형식이어야 합니다 (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "다음 사용 가능한 서식 중 하나를 고르십시오:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "서식"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "새 확장 기능의 유일한 ID"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "<이름>"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "사용자에게 표시할 새 확장 기능 이름"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "<설명>"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "확장이 하는 일에 대한 짧은 설명"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "<서식>"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "새 확장 기능에 사용할 서식"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "확장 기능 정보 대화식으로 입력하기"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "새 확장 만들기"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "알 수 없는 인자"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, 이름, 설명이 필요합니다"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "그놈 셸 연결에 실패했습니다\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "“%s” 확장 기능이 없습니다\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "확장 기능 사용하지 않기"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID가 주어지지 않았습니다"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "여러 개의 UUID가 주어졌습니다"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "확장 기능 사용하기"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "“%s” 확장 기능이 없습니다\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "확장을 설치하지 않았습니다"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "기존 확장 기능 덮어쓰기"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "<확장_번들>"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "확장 번들 설치"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "확장 번들을 지정하지 않았습니다"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "여러 개의 확장 번들을 지정했습니다"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "사용자 설치 확장 기능 표시"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "시스템 설치 확장 기능 표시"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "사용하는 확장 기능 표시"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "사용하지 않는 확장 기능 표시"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "설정이 있는 확장 기능 표시"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "업데이트가 있는 확장 기능 표시"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "확장 기능 자세히 표시"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "설치한 확장 기능 목록 보기"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "<파일>"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "번들에 포함할 추가 소스 파일"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "<스키마>"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "포함할 GSettings 스키마"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "<디렉터리>"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "번역이 들어 있는 디렉터리"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "<도메인>"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "번역에 사용할 gettext 도메인"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "기존의 묶음을 덮어씁니다"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "만들어진 묶음이 들어갈 디렉터리"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "<소스_디렉터리>"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "확장 번들을 만듭니다"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "여러 개의 소스 디렉터리를 지정했습니다"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "“%s” 확장에 설정이 없습니다\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "확장 “%s”에 대한 기본 설정을 여는데 실패했습니다: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "확장 기능 설정을 엽니다"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "확장 기능 초기화"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "시스템 확장 기능은 설치를 제거할 수 없습니다\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "“%s” 설치 제거에 실패했습니다\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "확장 기능 설치 제거"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "오류 메시지를 인쇄하지 않습니다"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "경로"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "원 작성자"
+
+# 커맨드라인 옵션 설명
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "버전"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "상태"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” 옵션은 인수를 받지 않습니다"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "사용법:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "버전 정보를 표시하고 끝납니다."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "<명령>"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[인자…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "명령어:"
+
+# 커맨드라인 옵션 설명
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "도움말을 표시합니다"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "확장 기능 사용"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "확장 기능 사용하지 않기"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "확장 기능 초기화"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "확장 기능 설치 제거"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "확장 기능 목록 보기"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "확장 기능 정보 보기"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "확장 기능 설정 열기"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "확장 기능 만들기"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "확장 기능 패키지 만들기"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "확장 번들 설치"
+
+# “gnome-extensions help COMMAND”
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "자세한 도움말을 보려면 “%s” 명령을 사용하십시오.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "있는 그대로"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "빈 확장 기능"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "알림 표시"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "아이콘을 맨 위 표시줄에 추가합니다"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u개 출력"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u개 입력"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "시스템 소리"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "블루투스 설정"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d개 연결"
+
+# 블루투스의 현재 상태가 켜졌음
+#~ msgid "Bluetooth On"
+#~ msgstr "블루투스 켜짐"
+
+# 블루투스의 현재 상태가 꺼졌음
+#~ msgid "Bluetooth Off"
+#~ msgstr "블루투스 꺼짐"
+
+#~ msgid "Location Enabled"
+#~ msgstr "위치 사용"
+
+#~ msgid "Disable"
+#~ msgstr "사용 않기"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "사생활 보호 설정"
+
+#~ msgid "Location In Use"
+#~ msgstr "위치 사용 중"
+
+#~ msgid "Location Disabled"
+#~ msgstr "위치 사용 않음"
+
+#~ msgid "Enable"
+#~ msgstr "사용"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s 꺼짐"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s 연결됨"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s 관리되지 않음"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s 연결하는 중"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s 인증 필요"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s에 필요한 펌웨어 없음"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s 사용 불가"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s 연결이 실패했습니다"
+
+#~ msgid "Wired Settings"
+#~ msgstr "유선 네트워크 설정"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "휴대전화 네트워크 설정"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s 하드웨어 사용 않음"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s 사용 않음"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "인터넷에 연결"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "비행기 모드 켜짐"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "비행기 모드에서는 와이파이를 사용하지 않습니다."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "비행기 모드 끄기"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "와이파이 꺼짐"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "네트워크에 연결하려면 와이파이를 켜야 합니다."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "와이파이 켜기"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "와이파이 네트워크"
+
+#~ msgid "Select a network"
+#~ msgstr "네트워크를 선택하십시오"
+
+#~ msgid "No Networks"
+#~ msgstr "네트워크 없음"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "끄려면 하드웨어 스위치를 사용하십시오"
+
+#~ msgid "Select Network"
+#~ msgstr "네트워크 선택"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "와이파이 설정"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s 연결되지 않음"
+
+#~ msgid "connecting…"
+#~ msgstr "연결하는 중…"
+
+#~ msgid "authentication required"
+#~ msgstr "인증 필요"
+
+#~ msgid "connection failed"
+#~ msgstr "연결 실패"
+
+#~ msgid "VPN Settings"
+#~ msgstr "가상사설망 설정"
+
+#~ msgid "VPN"
+#~ msgstr "가상사설망"
+
+#~ msgid "VPN Off"
+#~ msgstr "가상사설망 꺼짐"
+
+#~ msgid "Network Settings"
+#~ msgstr "네트워크 설정"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s 유선 연결"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s 와이파이 연결"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s 모뎀 연결"
+
+#~ msgid "Connection failed"
+#~ msgstr "연결이 실패했습니다"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "네트워크 연결이 실패했습니다"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "야간 모드 사용하지 않음"
+
+#~ msgid "Resume"
+#~ msgstr "계속 하기"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "내일까지 사용하지 않기"
+
+#~ msgid "Power Settings"
+#~ msgstr "전원 설정"
+
+#~ msgid "Fully Charged"
+#~ msgstr "완전 충전"
+
+#~ msgid "Not Charging"
+#~ msgstr "충전 중 아님"
+
+#~ msgid "Estimating…"
+#~ msgstr "예상치 계산 중…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d 남음(%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "충전까지 %d:%02d(%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "화면을 공유하는 중입니다"
+
+#~ msgid "Turn off"
+#~ msgstr "끄기"
+
+#~ msgid "Lock"
+#~ msgstr "잠그기"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "컴퓨터 끄기 / 로그아웃"
diff --git a/po/ku.po b/po/ku.po
new file mode 100644
index 0000000..fd03263
--- /dev/null
+++ b/po/ku.po
@@ -0,0 +1,1575 @@
+# Kurdish translations for l package.
+# Copyright (C) 2011 THE l'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the l package.
+# Erdal <reimar.heider@gmail.com>, 2011.
+# Erdal Ronahî <erdal dot ronahi at gmail dot com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: l 10n-gnome\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2011-10-17 08:32+0000\n"
+"PO-Revision-Date: 2011-10-17 13:36+0200\n"
+"Last-Translator: Erdal Ronahî <erdal dot ronahi at gmail dot com>\n"
+"Language-Team: Kurdish Team http://pckurd.net\n"
+"Language: ku\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.6.1\n"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr ""
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:1
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:2
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:3
+msgid "File extension used for storing the screencast"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:4
+msgid "Framerate used for recording screencasts."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:5
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. disabled-extensions overrides this setting for extensions "
+"that appear in both lists."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:6
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:7
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:8
+msgid "If true, display date in the clock, in addition to time."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:9
+msgid "If true, display seconds in time."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:10
+msgid "If true, display the ISO week date in the calendar."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:11
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:13
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
+"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
+"at the optimal thread count on the system."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:14
+msgid "Show date in clock"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:15
+msgid "Show the week date in the calendar"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:16
+msgid "Show time with seconds"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:17
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:18
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:19
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:20
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:21
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:22
+msgid "The type of keyboard to use."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:23
+msgid "Uuids of extensions to enable"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:24
+msgid "Whether to collect stats about applications usage"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:25
+msgid "Which keyboard to use"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:26
+msgid "disabled OpenSearch providers"
+msgstr ""
+
+#: ../js/gdm/loginDialog.js:617
+msgid "Session..."
+msgstr "Danişîn..."
+
+#: ../js/gdm/loginDialog.js:788
+msgctxt "title"
+msgid "Sign In"
+msgstr "Têkeve"
+
+#. translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/loginDialog.js:833
+msgid "(or swipe finger)"
+msgstr ""
+
+#: ../js/gdm/loginDialog.js:851
+msgid "Not listed?"
+msgstr "Di listê de tune?"
+
+#: ../js/gdm/loginDialog.js:1019 ../js/ui/endSessionDialog.js:426
+#: ../js/ui/extensionSystem.js:477 ../js/ui/networkAgent.js:165
+#: ../js/ui/polkitAuthenticationAgent.js:171 ../js/ui/status/bluetooth.js:480
+msgid "Cancel"
+msgstr "Betal"
+
+#: ../js/gdm/loginDialog.js:1024
+msgctxt "button"
+msgid "Sign In"
+msgstr "Têkeve"
+
+#: ../js/gdm/loginDialog.js:1373
+msgid "Login Window"
+msgstr "Paceya têketinê"
+
+#: ../js/gdm/powerMenu.js:116 ../js/ui/userMenu.js:549
+#: ../js/ui/userMenu.js:551 ../js/ui/userMenu.js:620
+msgid "Suspend"
+msgstr "Xew"
+
+#: ../js/gdm/powerMenu.js:121 ../js/ui/endSessionDialog.js:89
+#: ../js/ui/endSessionDialog.js:97 ../js/ui/endSessionDialog.js:106
+msgid "Restart"
+msgstr "Dîsdestpêkirin"
+
+#: ../js/gdm/powerMenu.js:126 ../js/ui/endSessionDialog.js:80
+#: ../js/ui/endSessionDialog.js:91
+msgid "Power Off"
+msgstr "Girtin"
+
+#: ../js/misc/util.js:92
+msgid "Command not found"
+msgstr "Ferman nehate dîtin"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:119
+msgid "Could not parse command:"
+msgstr ""
+
+#: ../js/misc/util.js:127
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr ""
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:255
+msgid "All"
+msgstr "Hemû"
+
+#: ../js/ui/appDisplay.js:319
+msgid "APPLICATIONS"
+msgstr "SEPAN"
+
+#: ../js/ui/appDisplay.js:377
+msgid "SETTINGS"
+msgstr "MÎHENG"
+
+#: ../js/ui/appDisplay.js:684
+msgid "New Window"
+msgstr "Paceya nû"
+
+#: ../js/ui/appDisplay.js:687
+msgid "Remove from Favorites"
+msgstr "Ji bijareyan rake"
+
+#: ../js/ui/appDisplay.js:688
+msgid "Add to Favorites"
+msgstr "Li bijareyan zêde bike"
+
+#: ../js/ui/appFavorites.js:89
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "%s li bijareyên te de zêde bû."
+
+#: ../js/ui/appFavorites.js:120
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "%s li bijareyên te de hate rakirin."
+
+#: ../js/ui/autorunManager.js:280
+msgid "Removable Devices"
+msgstr ""
+
+#: ../js/ui/autorunManager.js:590
+#, c-format
+msgid "Open with %s"
+msgstr "Bi %s veke"
+
+#: ../js/ui/autorunManager.js:616
+msgid "Eject"
+msgstr "Avêtin"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:63
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Hemû roj"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:68
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:75
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:115
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Y"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:117
+msgctxt "grid monday"
+msgid "M"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:119
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:121
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ç"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:123
+msgctxt "grid thursday"
+msgid "T"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:125
+msgctxt "grid friday"
+msgid "F"
+msgstr "Î"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:127
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Ş"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:140
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Yş"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:142
+msgctxt "list monday"
+msgid "M"
+msgstr "D"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:144
+msgctxt "list tuesday"
+msgid "T"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:146
+msgctxt "list wednesday"
+msgid "W"
+msgstr "Ç"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:148
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Pş"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:150
+msgctxt "list friday"
+msgid "F"
+msgstr "Î"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:152
+msgctxt "list saturday"
+msgid "S"
+msgstr "Ş"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:687
+msgid "Nothing Scheduled"
+msgstr ""
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:703
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr ""
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:706
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr ""
+
+#: ../js/ui/calendar.js:716
+msgid "Today"
+msgstr ""
+
+#: ../js/ui/calendar.js:720
+msgid "Tomorrow"
+msgstr ""
+
+#: ../js/ui/calendar.js:729
+msgid "This week"
+msgstr ""
+
+#: ../js/ui/calendar.js:737
+msgid "Next week"
+msgstr ""
+
+#: ../js/ui/contactDisplay.js:65 ../js/ui/notificationDaemon.js:444
+#: ../js/ui/status/power.js:223 ../src/shell-app.c:353
+msgid "Unknown"
+msgstr ""
+
+#: ../js/ui/contactDisplay.js:86 ../js/ui/userMenu.js:139
+msgid "Available"
+msgstr ""
+
+#: ../js/ui/contactDisplay.js:91 ../js/ui/userMenu.js:148
+msgid "Away"
+msgstr ""
+
+#: ../js/ui/contactDisplay.js:95 ../js/ui/userMenu.js:142
+msgid "Busy"
+msgstr ""
+
+#: ../js/ui/contactDisplay.js:99
+msgid "Offline"
+msgstr ""
+
+#: ../js/ui/contactDisplay.js:146
+msgid "CONTACTS"
+msgstr "TEKILÎ"
+
+#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1204
+msgid "Remove"
+msgstr "Rakirin"
+
+#: ../js/ui/dateMenu.js:99
+msgid "Date and Time Settings"
+msgstr ""
+
+#: ../js/ui/dateMenu.js:125
+msgid "Open Calendar"
+msgstr ""
+
+#. Translators: This is the time format with date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:183
+msgid "%a %b %e, %R:%S"
+msgstr ""
+
+#: ../js/ui/dateMenu.js:184
+msgid "%a %b %e, %R"
+msgstr ""
+
+#. Translators: This is the time format without date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:188
+msgid "%a %R:%S"
+msgstr ""
+
+#: ../js/ui/dateMenu.js:189
+msgid "%a %R"
+msgstr ""
+
+#. Translators: This is a time format with date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:196
+msgid "%a %b %e, %l:%M:%S %p"
+msgstr ""
+
+#: ../js/ui/dateMenu.js:197
+msgid "%a %b %e, %l:%M %p"
+msgstr ""
+
+#. Translators: This is a time format without date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:201
+msgid "%a %l:%M:%S %p"
+msgstr ""
+
+#: ../js/ui/dateMenu.js:202
+msgid "%a %l:%M %p"
+msgstr ""
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:213
+msgid "%A %B %e, %Y"
+msgstr ""
+
+#: ../js/ui/docDisplay.js:16
+msgid "RECENT ITEMS"
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:60
+#, c-format
+msgid "Log Out %s"
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:61 ../js/ui/endSessionDialog.js:75
+msgid "Log Out"
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:62
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:64
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:69
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:73
+msgid "Logging out of the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:81
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:83
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:87
+msgid "Powering off the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:98
+msgid "Click Restart to quit these applications and restart the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:100
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:104
+msgid "Restarting the system."
+msgstr ""
+
+#: ../js/ui/extensionSystem.js:481
+msgid "Install"
+msgstr ""
+
+#: ../js/ui/extensionSystem.js:485
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr ""
+
+#: ../js/ui/keyboard.js:309
+msgid "tray"
+msgstr ""
+
+#: ../js/ui/keyboard.js:531 ../js/ui/status/power.js:211
+msgid "Keyboard"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:645
+msgid "No extensions installed"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:691
+msgid "Enabled"
+msgstr ""
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:693 ../src/gvc/gvc-mixer-control.c:1093
+msgid "Disabled"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:695
+msgid "Error"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:697
+msgid "Out of date"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:699
+msgid "Downloading"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:720
+msgid "View Source"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:726
+msgid "Web Page"
+msgstr ""
+
+#: ../js/ui/messageTray.js:1197
+msgid "Open"
+msgstr ""
+
+#: ../js/ui/messageTray.js:2406
+msgid "System Information"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:145
+msgid "Show password"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:160
+msgid "Connect"
+msgstr ""
+
+#. Cisco LEAP
+#: ../js/ui/networkAgent.js:255 ../js/ui/networkAgent.js:267
+#: ../js/ui/networkAgent.js:294 ../js/ui/networkAgent.js:314
+#: ../js/ui/networkAgent.js:324
+msgid "Password: "
+msgstr ""
+
+#. static WEP
+#: ../js/ui/networkAgent.js:260
+msgid "Key: "
+msgstr ""
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/ui/networkAgent.js:292 ../js/ui/networkAgent.js:310
+msgid "Username: "
+msgstr ""
+
+#: ../js/ui/networkAgent.js:298
+msgid "Identity: "
+msgstr ""
+
+#: ../js/ui/networkAgent.js:300
+msgid "Private key password: "
+msgstr ""
+
+#: ../js/ui/networkAgent.js:312
+msgid "Service: "
+msgstr ""
+
+#: ../js/ui/networkAgent.js:341
+msgid "Authentication required by wireless network"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:342
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"'%s'."
+msgstr ""
+
+#: ../js/ui/networkAgent.js:346
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:348
+msgid "Network name: "
+msgstr ""
+
+#: ../js/ui/networkAgent.js:353
+msgid "DSL authentication"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:360
+msgid "PIN code required"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:361
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:362
+msgid "PIN: "
+msgstr ""
+
+#: ../js/ui/networkAgent.js:368
+msgid "Mobile broadband network password"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:369
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr ""
+
+#: ../js/ui/overview.js:91
+msgid "Undo"
+msgstr ""
+
+#: ../js/ui/overview.js:205
+msgid "Windows"
+msgstr ""
+
+#: ../js/ui/overview.js:208
+msgid "Applications"
+msgstr ""
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:230
+msgid "Dash"
+msgstr ""
+
+#. TODO - _quit() doesn't really work on apps in state STARTING yet
+#: ../js/ui/panel.js:539
+#, c-format
+msgid "Quit %s"
+msgstr ""
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:575
+msgid "Activities"
+msgstr ""
+
+#: ../js/ui/panel.js:967
+msgid "Top Bar"
+msgstr ""
+
+#: ../js/ui/placeDisplay.js:120
+#, c-format
+msgid "Failed to unmount '%s'"
+msgstr ""
+
+#: ../js/ui/placeDisplay.js:123
+msgid "Retry"
+msgstr ""
+
+#: ../js/ui/placeDisplay.js:163
+msgid "Connect to..."
+msgstr ""
+
+#: ../js/ui/placeDisplay.js:375
+msgid "PLACES & DEVICES"
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:72
+msgid "Authentication Required"
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:106
+msgid "Administrator"
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:175
+msgid "Authenticate"
+msgstr ""
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/polkitAuthenticationAgent.js:256
+msgid "Sorry, that didn't work. Please try again."
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:268
+msgid "Password:"
+msgstr ""
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:731
+msgid "toggle-switch-us"
+msgstr ""
+
+#: ../js/ui/runDialog.js:208
+msgid "Please enter a command:"
+msgstr ""
+
+#: ../js/ui/searchDisplay.js:340
+msgid "Searching..."
+msgstr ""
+
+#: ../js/ui/searchDisplay.js:363
+msgid "No matching results."
+msgstr ""
+
+#: ../js/ui/shellMountOperation.js:285
+msgid "Wrong password, please try again"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:60
+msgid "Zoom"
+msgstr ""
+
+#. let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
+#. 'screen-reader-enabled');
+#. this.menu.addMenuItem(screenReader);
+#: ../js/ui/status/accessibility.js:71
+msgid "Screen Keyboard"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Visual Alerts"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Sticky Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Slow Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Bounce Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:87
+msgid "Mouse Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:91
+msgid "Universal Access Settings"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:141
+msgid "High Contrast"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:39 ../js/ui/status/bluetooth.js:261
+#: ../js/ui/status/bluetooth.js:347 ../js/ui/status/bluetooth.js:381
+#: ../js/ui/status/bluetooth.js:421 ../js/ui/status/bluetooth.js:454
+msgid "Bluetooth"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:52
+msgid "Visibility"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:66
+msgid "Send Files to Device..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:67
+msgid "Set up a New Device..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:91
+msgid "Bluetooth Settings"
+msgstr ""
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:111
+msgid "hardware disabled"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:208
+msgid "Connection"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:217 ../js/ui/status/network.js:486
+msgid "disconnecting..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:230 ../js/ui/status/network.js:492
+msgid "connecting..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:248
+msgid "Send Files..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:253
+msgid "Browse Files..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:262
+msgid "Error browsing device"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:263
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:271
+msgid "Keyboard Settings"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:274
+msgid "Mouse Settings"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:279 ../js/ui/status/volume.js:62
+msgid "Sound Settings"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:382
+#, c-format
+msgid "Authorization request from %s"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:388
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:390
+msgid "Always grant access"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:391
+msgid "Grant this time only"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:392 ../js/ui/telepathyClient.js:1204
+msgid "Reject"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:422
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:428 ../js/ui/status/bluetooth.js:462
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:429
+#, c-format
+msgid "Please confirm whether the PIN '%s' matches the one on the device."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:431
+msgid "Matches"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:432
+msgid "Does not match"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:455
+#, c-format
+msgid "Pairing request for %s"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:463
+msgid "Please enter the PIN mentioned on the device."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:479
+msgid "OK"
+msgstr ""
+
+#: ../js/ui/status/keyboard.js:73
+msgid "Show Keyboard Layout"
+msgstr ""
+
+#: ../js/ui/status/keyboard.js:78
+msgid "Region and Language Settings"
+msgstr ""
+
+#: ../js/ui/status/network.js:97
+msgid "<unknown>"
+msgstr ""
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:285
+msgid "disabled"
+msgstr ""
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:484
+msgid "unmanaged"
+msgstr ""
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:495
+msgid "authentication required"
+msgstr ""
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:505
+msgid "firmware missing"
+msgstr ""
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:512
+msgid "cable unplugged"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:517
+msgid "unavailable"
+msgstr ""
+
+#: ../js/ui/status/network.js:519
+msgid "connection failed"
+msgstr ""
+
+#: ../js/ui/status/network.js:575 ../js/ui/status/network.js:1523
+msgid "More..."
+msgstr ""
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:611 ../js/ui/status/network.js:1458
+msgid "Connected (private)"
+msgstr ""
+
+#: ../js/ui/status/network.js:689
+msgid "Auto Ethernet"
+msgstr ""
+
+#: ../js/ui/status/network.js:753
+msgid "Auto broadband"
+msgstr ""
+
+#: ../js/ui/status/network.js:756
+msgid "Auto dial-up"
+msgstr ""
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:878 ../js/ui/status/network.js:1470
+#, c-format
+msgid "Auto %s"
+msgstr ""
+
+#: ../js/ui/status/network.js:880
+msgid "Auto bluetooth"
+msgstr ""
+
+#: ../js/ui/status/network.js:1472
+msgid "Auto wireless"
+msgstr ""
+
+#: ../js/ui/status/network.js:1566
+msgid "Enable networking"
+msgstr ""
+
+#: ../js/ui/status/network.js:1578
+msgid "Wired"
+msgstr ""
+
+#: ../js/ui/status/network.js:1589
+msgid "Wireless"
+msgstr ""
+
+#: ../js/ui/status/network.js:1599
+msgid "Mobile broadband"
+msgstr ""
+
+#: ../js/ui/status/network.js:1609
+msgid "VPN Connections"
+msgstr ""
+
+#: ../js/ui/status/network.js:1620
+msgid "Network Settings"
+msgstr ""
+
+#: ../js/ui/status/network.js:1757
+msgid "Connection failed"
+msgstr ""
+
+#: ../js/ui/status/network.js:1758
+msgid "Activation of network connection failed"
+msgstr ""
+
+#: ../js/ui/status/network.js:2008
+msgid "Networking is disabled"
+msgstr ""
+
+#: ../js/ui/status/network.js:2133
+msgid "Network Manager"
+msgstr ""
+
+#: ../js/ui/status/power.js:82
+msgid "Power Settings"
+msgstr ""
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:103
+msgid "Estimating..."
+msgstr ""
+
+#: ../js/ui/status/power.js:110
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] ""
+msgstr[1] ""
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:113
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr ""
+
+#: ../js/ui/status/power.js:115
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/status/power.js:115
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/status/power.js:118
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/status/power.js:121 ../js/ui/status/power.js:194
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr ""
+
+#: ../js/ui/status/power.js:201
+msgid "AC adapter"
+msgstr ""
+
+#: ../js/ui/status/power.js:203
+msgid "Laptop battery"
+msgstr ""
+
+#: ../js/ui/status/power.js:205
+msgid "UPS"
+msgstr ""
+
+#: ../js/ui/status/power.js:207
+msgid "Monitor"
+msgstr ""
+
+#: ../js/ui/status/power.js:209
+msgid "Mouse"
+msgstr ""
+
+#: ../js/ui/status/power.js:213
+msgid "PDA"
+msgstr ""
+
+#: ../js/ui/status/power.js:215
+msgid "Cell phone"
+msgstr ""
+
+#: ../js/ui/status/power.js:217
+msgid "Media player"
+msgstr ""
+
+#: ../js/ui/status/power.js:219
+msgid "Tablet"
+msgstr ""
+
+#: ../js/ui/status/power.js:221
+msgid "Computer"
+msgstr ""
+
+#: ../js/ui/status/volume.js:42
+msgid "Volume"
+msgstr ""
+
+#: ../js/ui/status/volume.js:54
+msgid "Microphone"
+msgstr ""
+
+#. We got the TpContact
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/telepathyClient.js:259
+msgid "Invitation"
+msgstr ""
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:327
+msgid "Call"
+msgstr ""
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:357
+msgid "File Transfer"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:438
+msgid "Subscription request"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:474
+msgid "Connection error"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:741
+#, c-format
+msgid "%s is online."
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:746
+#, c-format
+msgid "%s is offline."
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:749
+#, c-format
+msgid "%s is away."
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:752
+#, c-format
+msgid "%s is busy."
+msgstr ""
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/telepathyClient.js:986
+#, no-c-format
+msgid "Sent at <b>%X</b> on <b>%A</b>"
+msgstr ""
+
+#. Translators: this is a time format in the style of "Wednesday, May 25",
+#. shown when you get a chat message in the same year.
+#: ../js/ui/telepathyClient.js:992
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+msgstr ""
+
+#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
+#. shown when you get a chat message in a different year.
+#: ../js/ui/telepathyClient.js:997
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+msgstr ""
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/telepathyClient.js:1039
+#, c-format
+msgid "%s is now known as %s"
+msgstr ""
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1148
+#, c-format
+msgid "Invitation to %s"
+msgstr ""
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/telepathyClient.js:1156
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1158 ../js/ui/telepathyClient.js:1248
+#: ../js/ui/telepathyClient.js:1352
+msgid "Decline"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1159 ../js/ui/telepathyClient.js:1249
+#: ../js/ui/telepathyClient.js:1353
+msgid "Accept"
+msgstr ""
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1192
+#, c-format
+msgid "Video call from %s"
+msgstr ""
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1195
+#, c-format
+msgid "Call from %s"
+msgstr ""
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/telepathyClient.js:1206
+msgid "Answer"
+msgstr ""
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/telepathyClient.js:1242
+#, c-format
+msgid "%s is sending you %s"
+msgstr ""
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/telepathyClient.js:1317
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1415
+msgid "Network error"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1417
+msgid "Authentication failed"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1419
+msgid "Encryption error"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1421
+msgid "Certificate not provided"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1423
+msgid "Certificate untrusted"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1425
+msgid "Certificate expired"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1427
+msgid "Certificate not activated"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1429
+msgid "Certificate hostname mismatch"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1431
+msgid "Certificate fingerprint mismatch"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1433
+msgid "Certificate self-signed"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1435
+msgid "Status is set to offline"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1437
+msgid "Encryption is not available"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1439
+msgid "Certificate is invalid"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1441
+msgid "Connection has been refused"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1443
+msgid "Connection can't be established"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1445
+msgid "Connection has been lost"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1447
+msgid "This resource is already connected to the server"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1449
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1451
+msgid "The account already exists on the server"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1453
+msgid "Server is currently too busy to handle the connection"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1455
+msgid "Certificate has been revoked"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1457
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1459
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1468
+#, c-format
+msgid "Connection to %s failed"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1477
+msgid "Reconnect"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1478
+msgid "Edit account"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1524
+msgid "Unknown reason"
+msgstr ""
+
+#: ../js/ui/userMenu.js:145
+msgid "Hidden"
+msgstr ""
+
+#: ../js/ui/userMenu.js:151
+msgid "Idle"
+msgstr ""
+
+#: ../js/ui/userMenu.js:154
+msgid "Unavailable"
+msgstr ""
+
+#: ../js/ui/userMenu.js:547 ../js/ui/userMenu.js:551 ../js/ui/userMenu.js:621
+msgid "Power Off..."
+msgstr ""
+
+#: ../js/ui/userMenu.js:583
+msgid "Notifications"
+msgstr ""
+
+#: ../js/ui/userMenu.js:591
+msgid "Online Accounts"
+msgstr ""
+
+#: ../js/ui/userMenu.js:595
+msgid "System Settings"
+msgstr ""
+
+#: ../js/ui/userMenu.js:602
+msgid "Lock Screen"
+msgstr ""
+
+#: ../js/ui/userMenu.js:607
+msgid "Switch User"
+msgstr ""
+
+#: ../js/ui/userMenu.js:612
+msgid "Log Out..."
+msgstr ""
+
+#: ../js/ui/userMenu.js:640
+msgid "Your chat status will be set to busy"
+msgstr ""
+
+#: ../js/ui/userMenu.js:641
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/viewSelector.js:120
+msgid "Type to search..."
+msgstr ""
+
+#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
+msgid "Search"
+msgstr ""
+
+#: ../js/ui/windowAttentionHandler.js:35
+#, c-format
+msgid "'%s' is ready"
+msgstr ""
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1100
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1110
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../src/gvc/gvc-mixer-control.c:1408
+msgid "System Sounds"
+msgstr ""
+
+#: ../src/main.c:480
+msgid "Print version"
+msgstr ""
+
+#: ../src/main.c:486
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: ../src/shell-app.c:579
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr ""
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr ""
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr ""
+
+#: ../src/shell-polkit-authentication-agent.c:334
+msgid "Authentication dialog was dismissed by the user"
+msgstr ""
+
+#: ../src/shell-util.c:100
+msgid "Home Folder"
+msgstr "Peldanka mal"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:115
+msgid "File System"
+msgstr "Pergala pelan"
+
+#. Translators: the first string is the name of a gvfs
+#. * method, and the second string is a path. For
+#. * example, "Trash: some-directory". It means that the
+#. * directory called "some-directory" is in the trash.
+#.
+#: ../src/shell-util.c:311
+#, c-format
+msgid "%1$s: %2$s"
+msgstr "%1$s: %2$s"
diff --git a/po/ky.po b/po/ky.po
new file mode 100644
index 0000000..5592a82
--- /dev/null
+++ b/po/ky.po
@@ -0,0 +1,1753 @@
+# Kirghiz translation for gnome-shell.
+# Copyright (C) 2012 The gnome-shell authors.
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Chynggyz Jumaliev <translatorky@lavabit.com>, 2012.
+# Timur Zhamakeev <ztimur@gmail.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell-3-6\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-09-22 18:40+0000\n"
+"PO-Revision-Date: 2012-09-24 14:29+0600\n"
+"Last-Translator: Timur Zhamakeev <ztimur@gmail.com>\n"
+"Language-Team: Kirghiz <gnome-i18n@gnome.org>\n"
+"Language: ky\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Generator: Lokalize 1.4\n"
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:1
+msgid "Screenshots"
+msgstr ""
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:2
+msgid "Record a screencast"
+msgstr ""
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+#| msgid "File System"
+msgid "System"
+msgstr "Файлдар системасы"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "Кабар сабын көрсөтүү"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "Терезелерди башкаруу жана тиркемелерди иштетүү"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+#: ../js/extensionPrefs/main.js:152
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME Shell кеңейтмелеринин параметрлери"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Shell кеңейтмелерин ырастоо"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "Uuids of extensions to enable"
+msgstr "Күйгүзүлгөн кеңейтмелердин uuid'ри"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension DBus methods on org.gnome.Shell."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Whether to collect stats about applications usage"
+msgstr "Тиркемелерди колдонуу жөнүндө статистиканы чогултуш керекпи"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "History for command (Alt-F2) dialog"
+msgstr "(Alt-F2) диалогунун команда тарыхы"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "History for the looking glass dialog"
+msgstr "Тунук диалогтун көрүү тарыхы"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid ""
+"Internally used to store the last IM presence explicitly set by the user. "
+"The value here is from the TpConnectionPresenceType enumeration."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid ""
+"Internally used to store the last session presence status for the user. The "
+"value here is from the GsmPresenceStatus enumeration."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Show the week date in the calendar"
+msgstr "Жылнаамадан жуманын датасын көрсөтүү"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid "If true, display the ISO week date in the calendar."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid "Keybinding to open the application menu"
+msgstr "Тиркеме менюсун ачуу үчүн клавиштик айкалыш"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid "Keybinding to open the application menu."
+msgstr "Тиркеме менюсун ачуу үчүн клавиштик айкалыш."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+#, fuzzy
+#| msgid "Keybinding to toggle the screen recorder"
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "Экран тарткычты которуучу клавиштик айкалыш"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+#, fuzzy
+#| msgid "Keybinding to toggle the screen recorder"
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "Экран тарткычты которуучу клавиштик айкалыш"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to toggle the screen recorder"
+msgstr "Экран тарткычты которуучу клавиштик айкалыш"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to start/stop the builtin screen recorder."
+msgstr "Ички экран тарткычты жүргүзүүчү/токтотуучу клавиштик айкалыш."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Which keyboard to use"
+msgstr "Кайсы клавиатураны колдонуу"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "The type of keyboard to use."
+msgstr "Колдонгон клавиатуранын түрү."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Framerate used for recording screencasts."
+msgstr "Скринкасттарды жазыш үчүн кадр жыштыгынын алмашуусу."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+"threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is "
+"used as a placeholder for a guess at the optimal thread count on the system."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "File extension used for storing the screencast"
+msgstr "Скринкасттарды сактоо үчүн колдонулган файлдын кеңейтмеси"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+
+#: ../js/extensionPrefs/main.js:124
+#, c-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr ""
+
+#: ../js/extensionPrefs/main.js:164
+#, fuzzy
+#| msgid "<b>Extension</b>"
+msgid "Extension"
+msgstr "<b>Кеңейтме</b>"
+
+#: ../js/extensionPrefs/main.js:188
+msgid "Select an extension to configure using the combobox above."
+msgstr ""
+
+#: ../js/gdm/loginDialog.js:526
+msgid "Session..."
+msgstr "Сеанс…"
+
+#: ../js/gdm/loginDialog.js:674
+msgctxt "title"
+msgid "Sign In"
+msgstr "Кирүү"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:741
+msgid "Not listed?"
+msgstr "Тизмеде жокпу?"
+
+#: ../js/gdm/loginDialog.js:894 ../js/ui/components/networkAgent.js:137
+#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:373
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:396
+#: ../js/ui/status/bluetooth.js:427 ../js/ui/unlockDialog.js:166
+msgid "Cancel"
+msgstr "Айнуу"
+
+#: ../js/gdm/loginDialog.js:899
+msgctxt "button"
+msgid "Sign In"
+msgstr "Кирүү"
+
+#: ../js/gdm/loginDialog.js:1238
+msgid "Login Window"
+msgstr "Кирүү терезеси"
+
+#: ../js/gdm/powerMenu.js:88 ../js/ui/userMenu.js:658 ../js/ui/userMenu.js:662
+#: ../js/ui/userMenu.js:773
+msgid "Suspend"
+msgstr "Күтүүчү режим"
+
+#: ../js/gdm/powerMenu.js:93
+msgid "Restart"
+msgstr "Кайта жүргүзүү"
+
+#: ../js/gdm/powerMenu.js:98 ../js/ui/userMenu.js:660 ../js/ui/userMenu.js:662
+#: ../js/ui/userMenu.js:772
+msgid "Power Off"
+msgstr "Өчүрүү"
+
+#: ../js/gdm/util.js:148
+#, fuzzy
+#| msgid "Authentication Required"
+msgid "Authentication error"
+msgstr "Аутентификация керек"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/util.js:265
+msgid "(or swipe finger)"
+msgstr "(же эсептеген орнотмодон бармагыңызды өткөрүңүз)"
+
+#: ../js/gdm/util.js:290
+#, c-format
+msgid "(e.g., user or %s)"
+msgstr ""
+
+#: ../js/misc/util.js:92
+msgid "Command not found"
+msgstr "Команда табылбады"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:125
+msgid "Could not parse command:"
+msgstr "Команданы талдоого мүмкүн эмес:"
+
+#: ../js/misc/util.js:133
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr ""
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:252
+msgid "All"
+msgstr "Баары"
+
+#: ../js/ui/appDisplay.js:310
+msgid "APPLICATIONS"
+msgstr "ТИРКЕМЕЛЕР"
+
+#: ../js/ui/appDisplay.js:370
+msgid "SETTINGS"
+msgstr "ЫРАСТООЛОР"
+
+#: ../js/ui/appDisplay.js:675
+msgid "New Window"
+msgstr "Жаңы терезе"
+
+#: ../js/ui/appDisplay.js:678 ../js/ui/dash.js:271
+msgid "Remove from Favorites"
+msgstr "Тандалмадан өчүрүү"
+
+#: ../js/ui/appDisplay.js:679
+msgid "Add to Favorites"
+msgstr "Тандалмага кошуу"
+
+#: ../js/ui/appFavorites.js:87
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "Cиздин тандалмаларыңызга %s кошулду."
+
+#: ../js/ui/appFavorites.js:118
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "Cиздин тандалмаларыңыздан %s алынды."
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:62
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Бүт күн"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:74
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:114
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Жк"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:116
+msgctxt "grid monday"
+msgid "M"
+msgstr "Дш"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:118
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Ше"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:120
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ша"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:122
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Бш"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:124
+msgctxt "grid friday"
+msgid "F"
+msgstr "Жм"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:126
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Иш"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:139
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Жк"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:141
+msgctxt "list monday"
+msgid "M"
+msgstr "Дш"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:143
+msgctxt "list tuesday"
+msgid "T"
+msgstr "Ше"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:145
+msgctxt "list wednesday"
+msgid "W"
+msgstr "Ша"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:147
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Бш"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:149
+msgctxt "list friday"
+msgid "F"
+msgstr "Жм"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:151
+msgctxt "list saturday"
+msgid "S"
+msgstr "Иш"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:699
+msgid "Nothing Scheduled"
+msgstr "Окуялар жок"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:715
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%B айынын %e %a"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:718
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%Y-ж. %B айынын %e %a"
+
+#: ../js/ui/calendar.js:728
+msgid "Today"
+msgstr "Бүгүн"
+
+#: ../js/ui/calendar.js:732
+msgid "Tomorrow"
+msgstr "Эртең"
+
+#: ../js/ui/calendar.js:743
+msgid "This week"
+msgstr "Ушул жума"
+
+#: ../js/ui/calendar.js:751
+msgid "Next week"
+msgstr "Кийинки жума"
+
+#: ../js/ui/components/autorunManager.js:278
+msgid "Removable Devices"
+msgstr "Алынуучу орнотмолор"
+
+#: ../js/ui/components/autorunManager.js:575
+#, c-format
+msgid "Open with %s"
+msgstr "%s менен ачуу"
+
+#: ../js/ui/components/autorunManager.js:601
+msgid "Eject"
+msgstr "Чыгаруу"
+
+#: ../js/ui/components/keyring.js:86 ../js/ui/components/polkitAgent.js:260
+msgid "Password:"
+msgstr "Сырсөз:"
+
+#: ../js/ui/components/keyring.js:105
+msgid "Type again:"
+msgstr "Дагы киргизиңиз:"
+
+#: ../js/ui/components/networkAgent.js:132
+msgid "Connect"
+msgstr "Туташуу"
+
+#. Cisco LEAP
+#: ../js/ui/components/networkAgent.js:223
+#: ../js/ui/components/networkAgent.js:235
+#: ../js/ui/components/networkAgent.js:262
+#: ../js/ui/components/networkAgent.js:282
+#: ../js/ui/components/networkAgent.js:292
+msgid "Password: "
+msgstr "Сырсөз: "
+
+#. static WEP
+#: ../js/ui/components/networkAgent.js:228
+msgid "Key: "
+msgstr "Ачкыч: "
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/ui/components/networkAgent.js:260
+#: ../js/ui/components/networkAgent.js:278
+msgid "Username: "
+msgstr "Колдонуучу аты: "
+
+#: ../js/ui/components/networkAgent.js:266
+msgid "Identity: "
+msgstr "Окшоштук: "
+
+#: ../js/ui/components/networkAgent.js:268
+msgid "Private key password: "
+msgstr "Өздүк ачкычтын сырсөзү: "
+
+#: ../js/ui/components/networkAgent.js:280
+msgid "Service: "
+msgstr "Кызмат: "
+
+#: ../js/ui/components/networkAgent.js:309
+msgid "Authentication required by wireless network"
+msgstr "Зымсыз тармагына аутентификация керек"
+
+#: ../js/ui/components/networkAgent.js:310
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"'%s'."
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:314
+msgid "Wired 802.1X authentication"
+msgstr "Wired 802.1X аутентификация"
+
+#: ../js/ui/components/networkAgent.js:316
+msgid "Network name: "
+msgstr "Тармак аты: "
+
+#: ../js/ui/components/networkAgent.js:321
+msgid "DSL authentication"
+msgstr "DSL аутентификация"
+
+#: ../js/ui/components/networkAgent.js:328
+msgid "PIN code required"
+msgstr "PIN-код керек"
+
+#: ../js/ui/components/networkAgent.js:329
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:336
+msgid "Mobile broadband network password"
+msgstr "Мобилдүү жазы тилкелик тармак үчүн сырсөз"
+
+#: ../js/ui/components/networkAgent.js:337
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr "«%s» жерине туташыш үчүн сырсөз керек."
+
+#: ../js/ui/components/polkitAgent.js:55
+msgid "Authentication Required"
+msgstr "Аутентификация керек"
+
+#: ../js/ui/components/polkitAgent.js:93
+msgid "Administrator"
+msgstr "Администратор"
+
+#: ../js/ui/components/polkitAgent.js:166
+msgid "Authenticate"
+msgstr "Аныктоо"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/components/polkitAgent.js:248 ../js/ui/shellMountOperation.js:381
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Кечириңиз, бул иштеген жок. Дагы аракет кылып көрүңүз."
+
+#. Translators: this is a filename used for screencast recording
+#: ../js/ui/components/recorder.js:44
+#, no-c-format
+msgid "Screencast from %d %t"
+msgstr "Скринкаст %d %t"
+
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/components/telepathyClient.js:237
+msgid "Invitation"
+msgstr "Чакыруу"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:297
+msgid "Call"
+msgstr "Чакыруу"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:313
+msgid "File Transfer"
+msgstr "Файл берүү"
+
+#: ../js/ui/components/telepathyClient.js:394
+msgid "Subscription request"
+msgstr "Жазылыш сурамы"
+
+#: ../js/ui/components/telepathyClient.js:430
+msgid "Connection error"
+msgstr "Туташуу катасы"
+
+#: ../js/ui/components/telepathyClient.js:491
+msgid "Unmute"
+msgstr "Катуулукту калыбына келтирүү"
+
+#: ../js/ui/components/telepathyClient.js:491
+msgid "Mute"
+msgstr "Үндү басаңдатуу"
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/components/telepathyClient.js:948
+#, no-c-format
+msgid "Sent at <b>%X</b> on <b>%A</b>"
+msgstr ""
+
+#. Translators: this is a time format in the style of "Wednesday, May 25",
+#. shown when you get a chat message in the same year.
+#: ../js/ui/components/telepathyClient.js:954
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+msgstr ""
+
+#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
+#. shown when you get a chat message in a different year.
+#: ../js/ui/components/telepathyClient.js:959
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+msgstr ""
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/components/telepathyClient.js:988
+#, c-format
+msgid "%s is now known as %s"
+msgstr "%s эми %s болуп калды"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1088
+#, c-format
+msgid "Invitation to %s"
+msgstr "%s бөлүмүнө чакыруу"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/components/telepathyClient.js:1096
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr "%s сизди %s бөлүмүнө чакырып жатат"
+
+#: ../js/ui/components/telepathyClient.js:1098
+#: ../js/ui/components/telepathyClient.js:1177
+#: ../js/ui/components/telepathyClient.js:1240
+msgid "Decline"
+msgstr "Кабыл албоо"
+
+#: ../js/ui/components/telepathyClient.js:1099
+#: ../js/ui/components/telepathyClient.js:1178
+#: ../js/ui/components/telepathyClient.js:1241
+msgid "Accept"
+msgstr "Кабыл алуу"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1129
+#, c-format
+msgid "Video call from %s"
+msgstr "Сизди видеодон %s чыкырып жатат"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1132
+#, c-format
+msgid "Call from %s"
+msgstr "Сизди %s чакырып жатат"
+
+#: ../js/ui/components/telepathyClient.js:1137
+#: ../js/ui/status/bluetooth.js:346
+msgid "Reject"
+msgstr "Кабыл албоо"
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/components/telepathyClient.js:1139
+msgid "Answer"
+msgstr "Жооп берүү"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/components/telepathyClient.js:1171
+#, c-format
+msgid "%s is sending you %s"
+msgstr "%s сизге %s жиберип жатат"
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/components/telepathyClient.js:1206
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr ""
+
+#: ../js/ui/components/telepathyClient.js:1298
+msgid "Network error"
+msgstr "Тармак катасы"
+
+#: ../js/ui/components/telepathyClient.js:1300
+msgid "Authentication failed"
+msgstr "Аутентификация оңунан чыккан жок"
+
+#: ../js/ui/components/telepathyClient.js:1302
+msgid "Encryption error"
+msgstr "Шифрлоо катасы"
+
+#: ../js/ui/components/telepathyClient.js:1304
+msgid "Certificate not provided"
+msgstr "Сертификат берилбейт"
+
+#: ../js/ui/components/telepathyClient.js:1306
+msgid "Certificate untrusted"
+msgstr "Ишеничсиз сертификат"
+
+#: ../js/ui/components/telepathyClient.js:1308
+msgid "Certificate expired"
+msgstr "Сертификаттын күч мөөнөтү өткөн"
+
+#: ../js/ui/components/telepathyClient.js:1310
+msgid "Certificate not activated"
+msgstr "Сертификат активацияланган жок"
+
+#: ../js/ui/components/telepathyClient.js:1312
+msgid "Certificate hostname mismatch"
+msgstr "Сертификаттын түйүн аты дал келген жок"
+
+#: ../js/ui/components/telepathyClient.js:1314
+msgid "Certificate fingerprint mismatch"
+msgstr "Сертификаттын бармак тагы дал келген жок"
+
+#: ../js/ui/components/telepathyClient.js:1316
+msgid "Certificate self-signed"
+msgstr "Өз жазылган сертификат"
+
+#: ../js/ui/components/telepathyClient.js:1318
+msgid "Status is set to offline"
+msgstr "Офлайн статусуна коюлду"
+
+#: ../js/ui/components/telepathyClient.js:1320
+msgid "Encryption is not available"
+msgstr "Шифрлоо жеткиликсиз"
+
+#: ../js/ui/components/telepathyClient.js:1322
+msgid "Certificate is invalid"
+msgstr "Жарабас сертификат"
+
+#: ../js/ui/components/telepathyClient.js:1324
+msgid "Connection has been refused"
+msgstr "Туташтырууга кабыл албады"
+
+#: ../js/ui/components/telepathyClient.js:1326
+msgid "Connection can't be established"
+msgstr "Туташтырууну коюуга мүмкүн эмес"
+
+#: ../js/ui/components/telepathyClient.js:1328
+msgid "Connection has been lost"
+msgstr "Туташтыруу жоготулду"
+
+#: ../js/ui/components/telepathyClient.js:1330
+msgid "This account is already connected to the server"
+msgstr "Бул эсептик жазылыш эчак эле серверге туташытырлган"
+
+#: ../js/ui/components/telepathyClient.js:1332
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr ""
+
+#: ../js/ui/components/telepathyClient.js:1334
+msgid "The account already exists on the server"
+msgstr "Эсептик жазылыш эчак эле серверде бар"
+
+#: ../js/ui/components/telepathyClient.js:1336
+msgid "Server is currently too busy to handle the connection"
+msgstr "Учурда туташтырууну иштетип чыгаруу үчүн сервердин күчү жетпей жатат"
+
+#: ../js/ui/components/telepathyClient.js:1338
+msgid "Certificate has been revoked"
+msgstr "Сертификат жокко чыгарылды"
+
+#: ../js/ui/components/telepathyClient.js:1340
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+
+#: ../js/ui/components/telepathyClient.js:1342
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+
+#: ../js/ui/components/telepathyClient.js:1344
+msgid "Internal error"
+msgstr "Ички ката"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1354
+#, c-format
+msgid "Connection to %s failed"
+msgstr "%s дарегине туташтыруу оңунан чыккан жок"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Reconnect"
+msgstr "Кайта туташуу"
+
+#: ../js/ui/components/telepathyClient.js:1364
+msgid "Edit account"
+msgstr "Эсептик жазылышты оңдоо"
+
+#: ../js/ui/components/telepathyClient.js:1409
+msgid "Unknown reason"
+msgstr "Белгисиз себеп"
+
+#: ../js/ui/dash.js:245 ../js/ui/dash.js:273
+#, fuzzy
+#| msgid "Applications"
+msgid "Show Applications"
+msgstr "Тиркемелер"
+
+#: ../js/ui/dateMenu.js:86
+msgid "Date and Time Settings"
+msgstr "Дата жана убакытты ырастоо"
+
+#: ../js/ui/dateMenu.js:109
+msgid "Open Calendar"
+msgstr "Жылнааманы ачуу"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:175
+msgid "%A %B %e, %Y"
+msgstr "%Y-ж. %B айынын %e %a"
+
+#: ../js/ui/endSessionDialog.js:61
+#, c-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s колдонуучусунун сеансын бүтүрүү"
+
+#: ../js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Log Out"
+msgstr "Системадан чыгуу"
+
+#: ../js/ui/endSessionDialog.js:63
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:65
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+"%s колдонуучусунун сеансы %d секундадан кийин автоматтуу түрдө бүтүрүлөт."
+
+#: ../js/ui/endSessionDialog.js:70
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "%d секундадан кийин автоматтуу түрдө сеанс бүтөт."
+
+#: ../js/ui/endSessionDialog.js:74
+msgid "Logging out of the system."
+msgstr "Системадан чыгуу."
+
+#: ../js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Log Out"
+msgstr "Системадан чыгуу"
+
+#: ../js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Power Off"
+msgstr "Өчүрүү"
+
+#: ../js/ui/endSessionDialog.js:82
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:84
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "%d секундадан кийин автоматтуу түрдө система өчүрүлөт."
+
+#: ../js/ui/endSessionDialog.js:88
+msgid "Powering off the system."
+msgstr "Системаны өчүрүү."
+
+#: ../js/ui/endSessionDialog.js:90 ../js/ui/endSessionDialog.js:107
+msgctxt "button"
+msgid "Restart"
+msgstr "Кайта жүргүзүү"
+
+#: ../js/ui/endSessionDialog.js:92
+msgctxt "button"
+msgid "Power Off"
+msgstr "Өчүрүү"
+
+#: ../js/ui/endSessionDialog.js:98
+msgctxt "title"
+msgid "Restart"
+msgstr "Кайта жүргүзүү"
+
+#: ../js/ui/endSessionDialog.js:99
+msgid "Click Restart to quit these applications and restart the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:101
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "%d секундадан кийин автоматтуу түрдө система кайта жүргүзүлөт."
+
+#: ../js/ui/endSessionDialog.js:105
+msgid "Restarting the system."
+msgstr "Системаны кайта жүргүзүү."
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "Орнотуу"
+
+#: ../js/ui/extensionDownloader.js:204
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr "extensions.gnome.org сайтынан «%s» кеңейтмесин жүктөп орнотосузбу?"
+
+#: ../js/ui/keyboard.js:327
+msgid "tray"
+msgstr "жайма"
+
+#: ../js/ui/keyboard.js:561 ../js/ui/status/keyboard.js:195
+#: ../js/ui/status/power.js:205
+msgid "Keyboard"
+msgstr "Клавиатура"
+
+#: ../js/ui/lookingGlass.js:691
+msgid "No extensions installed"
+msgstr "Кеңейтмелер орнотулган жок"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:745
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr "%s каталар жөнүндө эч нерсе маалымдаган жок."
+
+#: ../js/ui/lookingGlass.js:751
+msgid "Hide Errors"
+msgstr "Каталарды катуу"
+
+#: ../js/ui/lookingGlass.js:755 ../js/ui/lookingGlass.js:815
+msgid "Show Errors"
+msgstr "Каталарды көрсөтүү"
+
+#: ../js/ui/lookingGlass.js:764
+msgid "Enabled"
+msgstr "Күйгүлгөн"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:767 ../src/gvc/gvc-mixer-control.c:1082
+msgid "Disabled"
+msgstr "Өчүрүлгөн"
+
+#: ../js/ui/lookingGlass.js:769
+msgid "Error"
+msgstr "Ката"
+
+#: ../js/ui/lookingGlass.js:771
+msgid "Out of date"
+msgstr "Эскирген"
+
+#: ../js/ui/lookingGlass.js:773
+msgid "Downloading"
+msgstr "Жүктөө"
+
+#: ../js/ui/lookingGlass.js:797
+msgid "View Source"
+msgstr "Кодду көрсөтүү"
+
+#: ../js/ui/lookingGlass.js:806
+msgid "Web Page"
+msgstr "Веб-барак"
+
+#: ../js/ui/messageTray.js:1080
+msgid "Open"
+msgstr "Ачуу"
+
+#: ../js/ui/messageTray.js:1087
+msgid "Remove"
+msgstr "Өчүрүү"
+
+#: ../js/ui/messageTray.js:2055
+msgid "Message Tray"
+msgstr ""
+
+#: ../js/ui/messageTray.js:2511
+msgid "System Information"
+msgstr "Системалык информация"
+
+#: ../js/ui/notificationDaemon.js:506 ../src/shell-app.c:373
+msgctxt "program"
+msgid "Unknown"
+msgstr "Белгисиз"
+
+#: ../js/ui/overview.js:82
+msgid "Undo"
+msgstr "Жокко чыгаруу"
+
+#: ../js/ui/overview.js:127
+msgid "Overview"
+msgstr "Сереп"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/overview.js:201
+msgid "Type to search..."
+msgstr "Табуу…"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:222
+msgid "Dash"
+msgstr "Аспаптык панель"
+
+#: ../js/ui/panel.js:567
+msgid "Quit"
+msgstr "Жабуу"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:599
+msgid "Activities"
+msgstr "Сереп"
+
+#: ../js/ui/panel.js:965
+msgid "Top Bar"
+msgstr "Үстүңкү панель"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:731
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:205
+msgid "Please enter a command:"
+msgstr "Команданы киргизиңиз:"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: ../js/ui/screenShield.js:79
+#, fuzzy
+#| msgctxt "calendar heading"
+#| msgid "%A, %B %d"
+msgid "%A, %B %d"
+msgstr "%B айынын %e %a"
+
+#: ../js/ui/screenShield.js:144
+#, c-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] ""
+
+#: ../js/ui/screenShield.js:146
+#, fuzzy, c-format
+#| msgid "Notifications"
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "Эскертүүлөр"
+
+#: ../js/ui/searchDisplay.js:275
+msgid "Searching..."
+msgstr "Издөө…"
+
+#: ../js/ui/searchDisplay.js:323
+#, fuzzy
+#| msgid "No matching results."
+msgid "No results."
+msgstr "Дал келген натыйжалар жок."
+
+#: ../js/ui/shellEntry.js:26
+msgid "Copy"
+msgstr "Копиялоо"
+
+#: ../js/ui/shellEntry.js:31
+msgid "Paste"
+msgstr "Коюу"
+
+#: ../js/ui/shellEntry.js:102
+msgid "Show Text"
+msgstr "Текстти көрсөтүү"
+
+#: ../js/ui/shellEntry.js:104
+msgid "Hide Text"
+msgstr "Текстти катуу"
+
+#: ../js/ui/shellMountOperation.js:368
+#, fuzzy
+#| msgid "Password:"
+msgid "Password"
+msgstr "Сырсөз:"
+
+#: ../js/ui/shellMountOperation.js:389
+#, fuzzy
+#| msgid "Remember Passphrase"
+msgid "Remember Password"
+msgstr "Сырсүйлөмдү эске сактоо"
+
+#: ../js/ui/shellMountOperation.js:400 ../js/ui/unlockDialog.js:169
+msgid "Unlock"
+msgstr "Ачуу"
+
+#: ../js/ui/status/accessibility.js:39
+msgid "Accessibility"
+msgstr "Жеткиликтүүлүк"
+
+#: ../js/ui/status/accessibility.js:44
+msgid "Zoom"
+msgstr "Масштабтоо"
+
+#: ../js/ui/status/accessibility.js:51
+msgid "Screen Reader"
+msgstr "Экран окугуч"
+
+#: ../js/ui/status/accessibility.js:55
+msgid "Screen Keyboard"
+msgstr "Экрандык клавиатура"
+
+#: ../js/ui/status/accessibility.js:59
+msgid "Visual Alerts"
+msgstr "Көрүнөө эскертүүлөр"
+
+#: ../js/ui/status/accessibility.js:62
+msgid "Sticky Keys"
+msgstr "Жабышма клавиштер"
+
+#: ../js/ui/status/accessibility.js:65
+msgid "Slow Keys"
+msgstr "Жай клавиштер"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Bounce Keys"
+msgstr "Секирген клавиштер"
+
+#: ../js/ui/status/accessibility.js:71
+msgid "Mouse Keys"
+msgstr "Чычкан клавиштери"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Universal Access Settings"
+msgstr ""
+"Атайы мүмкүнчүлүктөрдү\n"
+"ырастоо"
+
+#: ../js/ui/status/accessibility.js:109
+msgid "High Contrast"
+msgstr "Карама-каршы тема"
+
+#: ../js/ui/status/accessibility.js:146
+msgid "Large Text"
+msgstr "Ири текст"
+
+#: ../js/ui/status/bluetooth.js:27 ../js/ui/status/bluetooth.js:31
+#: ../js/ui/status/bluetooth.js:251 ../js/ui/status/bluetooth.js:304
+#: ../js/ui/status/bluetooth.js:335 ../js/ui/status/bluetooth.js:371
+#: ../js/ui/status/bluetooth.js:400 ../js/ui/status/network.js:867
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: ../js/ui/status/bluetooth.js:44
+msgid "Visibility"
+msgstr "Көрүнүү"
+
+#: ../js/ui/status/bluetooth.js:58
+msgid "Send Files to Device..."
+msgstr "Орнотмого файлдарды жөнөтүү…"
+
+#: ../js/ui/status/bluetooth.js:59
+msgid "Set up a New Device..."
+msgstr "Жаңы орнотмону ырастоо…"
+
+#: ../js/ui/status/bluetooth.js:83
+msgid "Bluetooth Settings"
+msgstr "Bluetooth'ту ырастоо"
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:103 ../js/ui/status/network.js:208
+msgid "hardware disabled"
+msgstr "орнотмо өчүрүлдү"
+
+#: ../js/ui/status/bluetooth.js:196
+msgid "Connection"
+msgstr "Туташуу"
+
+#: ../js/ui/status/bluetooth.js:207 ../js/ui/status/network.js:458
+msgid "disconnecting..."
+msgstr "өчүрүлүү…"
+
+#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:464
+#: ../js/ui/status/network.js:934
+msgid "connecting..."
+msgstr "туташуу…"
+
+#: ../js/ui/status/bluetooth.js:238
+msgid "Send Files..."
+msgstr "Файлдарды жөнөтүү…"
+
+#: ../js/ui/status/bluetooth.js:243
+msgid "Browse Files..."
+msgstr "Файлдарды кароо…"
+
+#: ../js/ui/status/bluetooth.js:252
+msgid "Error browsing device"
+msgstr "Орнотмону көрүү катасы"
+
+#: ../js/ui/status/bluetooth.js:253
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:261
+msgid "Keyboard Settings"
+msgstr "Клавиатураны ырастоо"
+
+#: ../js/ui/status/bluetooth.js:264
+msgid "Mouse Settings"
+msgstr "Чычканды ырастоо"
+
+#: ../js/ui/status/bluetooth.js:269 ../js/ui/status/volume.js:236
+msgid "Sound Settings"
+msgstr "Үндү ырастоо"
+
+#: ../js/ui/status/bluetooth.js:336
+#, c-format
+msgid "Authorization request from %s"
+msgstr "%s авториязацияны сурап жатат"
+
+#: ../js/ui/status/bluetooth.js:342
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "%s орнотмосу «%s» кызматына кирүү аракетин кылып жатат"
+
+#: ../js/ui/status/bluetooth.js:344
+msgid "Always grant access"
+msgstr "Дайыма кирүүгө мүмкүндүк берүү"
+
+#: ../js/ui/status/bluetooth.js:345
+msgid "Grant this time only"
+msgstr "Бир гана жолу берүү"
+
+#: ../js/ui/status/bluetooth.js:372
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "%s менен байланыштыруу аныктоосу"
+
+#: ../js/ui/status/bluetooth.js:378 ../js/ui/status/bluetooth.js:408
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "%s орнотмосу ушу компьютер менен байланышайын деп жатат"
+
+#: ../js/ui/status/bluetooth.js:379
+#, c-format
+msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:381
+msgid "Matches"
+msgstr "Дал келди"
+
+#: ../js/ui/status/bluetooth.js:382
+msgid "Does not match"
+msgstr "Дал келбеди"
+
+#: ../js/ui/status/bluetooth.js:401
+#, c-format
+msgid "Pairing request for %s"
+msgstr "%s менен байланыштырууга суроо"
+
+#: ../js/ui/status/bluetooth.js:409
+msgid "Please enter the PIN mentioned on the device."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:426
+msgid "OK"
+msgstr "OK"
+
+#: ../js/ui/status/keyboard.js:228
+msgid "Show Keyboard Layout"
+msgstr "Клавиатура жайылматүрүн көрсөтүү"
+
+#: ../js/ui/status/keyboard.js:233
+msgid "Region and Language Settings"
+msgstr "Региондук жана тилдик ырастоолор"
+
+#: ../js/ui/status/lockScreenMenu.js:18
+msgid "Volume, network, battery"
+msgstr ""
+
+#: ../js/ui/status/network.js:94
+msgid "<unknown>"
+msgstr "<белгисиз>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:230
+msgid "disabled"
+msgstr "өчүрүлгөн"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:456
+msgid "unmanaged"
+msgstr "башкарылган жок"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:467 ../js/ui/status/network.js:937
+msgid "authentication required"
+msgstr "аутентификация керек"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:477
+msgid "firmware missing"
+msgstr "тигилме жок"
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:484
+msgid "cable unplugged"
+msgstr "кабель кошулган жок"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:489
+msgid "unavailable"
+msgstr "жеткиликтүү эмес"
+
+#: ../js/ui/status/network.js:491 ../js/ui/status/network.js:939
+msgid "connection failed"
+msgstr "туташтыруу оңунан чыккан жок"
+
+#: ../js/ui/status/network.js:552 ../js/ui/status/network.js:1529
+msgid "More..."
+msgstr "Дагы…"
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1459
+msgid "Connected (private)"
+msgstr "Туташтырылды (жеке)"
+
+#: ../js/ui/status/network.js:663
+msgid "Auto Ethernet"
+msgstr "Автоматтуу зымдык"
+
+#: ../js/ui/status/network.js:721
+msgid "Auto broadband"
+msgstr "Автоматтуу жазы тилкелик"
+
+#: ../js/ui/status/network.js:724
+msgid "Auto dial-up"
+msgstr "Автоматтуу коммутациялык"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:853 ../js/ui/status/network.js:1476
+#, c-format
+msgid "Auto %s"
+msgstr "Автоматтуу %s"
+
+#: ../js/ui/status/network.js:855
+msgid "Auto bluetooth"
+msgstr "Автоматтуу bluetooth"
+
+#: ../js/ui/status/network.js:1478
+msgid "Auto wireless"
+msgstr "Автоматтуу зымсыз"
+
+#: ../js/ui/status/network.js:1575
+msgid "Enable networking"
+msgstr "Тармакты күйгүзүү"
+
+#: ../js/ui/status/network.js:1597
+msgid "Wired"
+msgstr "Зымдык"
+
+#: ../js/ui/status/network.js:1608
+msgid "Wireless"
+msgstr "Зымсыз"
+
+#: ../js/ui/status/network.js:1618
+msgid "Mobile broadband"
+msgstr "Мобильдүү жазы тилкелик"
+
+#: ../js/ui/status/network.js:1628
+msgid "VPN Connections"
+msgstr "VPN туташтыруусу"
+
+#: ../js/ui/status/network.js:1635
+msgid "Network Settings"
+msgstr "Тармакты ырастоо"
+
+#: ../js/ui/status/network.js:1679
+msgid "Network Manager"
+msgstr "Тармак диспетчери"
+
+#: ../js/ui/status/network.js:1769
+msgid "Connection failed"
+msgstr "Туташтыруу оңунан чыккан жок"
+
+#: ../js/ui/status/network.js:1770
+msgid "Activation of network connection failed"
+msgstr "Тармактык туташтыруунун активациясы оңунан чыккан жок"
+
+#: ../js/ui/status/network.js:2065
+msgid "Networking is disabled"
+msgstr "Тармак өчүрүлгөн"
+
+#: ../js/ui/status/power.js:55
+msgid "Battery"
+msgstr "Батарея"
+
+#: ../js/ui/status/power.js:72
+msgid "Power Settings"
+msgstr "Азыктанууну ырастоо"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:94
+msgid "Estimating..."
+msgstr "Эсептелип жатат…"
+
+#: ../js/ui/status/power.js:101
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "%d саат калды"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:104
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "%d %s %d %s калды"
+
+#: ../js/ui/status/power.js:106
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "саат"
+
+#: ../js/ui/status/power.js:106
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "мүнөт"
+
+#: ../js/ui/status/power.js:109
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "%d мүнөт калды"
+
+#: ../js/ui/status/power.js:112 ../js/ui/status/power.js:186
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d%%"
+
+#: ../js/ui/status/power.js:195
+msgid "AC adapter"
+msgstr "Алмашма ток адаптери"
+
+#: ../js/ui/status/power.js:197
+msgid "Laptop battery"
+msgstr "Ноутбук батареясы"
+
+#: ../js/ui/status/power.js:199
+msgid "UPS"
+msgstr "ҮЖБ"
+
+#: ../js/ui/status/power.js:201
+msgid "Monitor"
+msgstr "Монитор"
+
+#: ../js/ui/status/power.js:203
+msgid "Mouse"
+msgstr "Чычкан"
+
+#: ../js/ui/status/power.js:207
+msgid "PDA"
+msgstr "ЧПК"
+
+#: ../js/ui/status/power.js:209
+msgid "Cell phone"
+msgstr "Мобильдик телефон"
+
+#: ../js/ui/status/power.js:211
+msgid "Media player"
+msgstr "Медиаплеер"
+
+#: ../js/ui/status/power.js:213
+msgid "Tablet"
+msgstr "Планшет"
+
+#: ../js/ui/status/power.js:215
+msgid "Computer"
+msgstr "Компьютер"
+
+#: ../js/ui/status/power.js:217
+msgctxt "device"
+msgid "Unknown"
+msgstr "Белгисиз"
+
+#. Translators: This is the label for audio volume
+#: ../js/ui/status/volume.js:47 ../js/ui/status/volume.js:223
+msgid "Volume"
+msgstr "Катуулук"
+
+#: ../js/ui/status/volume.js:59
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: ../js/ui/unlockDialog.js:176
+#, fuzzy
+#| msgid "Login as another user"
+msgid "Log in as another user"
+msgstr "Башка колдонуучу катары кирүү"
+
+#: ../js/ui/userMenu.js:175
+msgid "Available"
+msgstr "Жеткиликтүү"
+
+#: ../js/ui/userMenu.js:178
+msgid "Busy"
+msgstr "Бош эмес"
+
+#: ../js/ui/userMenu.js:181
+msgid "Invisible"
+msgstr "Көрүнбөс"
+
+#: ../js/ui/userMenu.js:184
+msgid "Away"
+msgstr "Кетти"
+
+#: ../js/ui/userMenu.js:187
+msgid "Idle"
+msgstr "Аракет кылган жок"
+
+#: ../js/ui/userMenu.js:190
+msgid "Unavailable"
+msgstr "Жеткиликсиз"
+
+#: ../js/ui/userMenu.js:613 ../js/ui/userMenu.js:754
+msgid "Switch User"
+msgstr "Колдонуучуну которуу"
+
+#: ../js/ui/userMenu.js:614
+msgid "Switch Session"
+msgstr "Сеансты которуу"
+
+#: ../js/ui/userMenu.js:738
+msgid "Notifications"
+msgstr "Эскертүүлөр"
+
+#: ../js/ui/userMenu.js:746
+msgid "System Settings"
+msgstr "Системалык параметрлер"
+
+#: ../js/ui/userMenu.js:759
+msgid "Log Out"
+msgstr "Системадан чыгуу"
+
+#: ../js/ui/userMenu.js:764
+msgid "Lock"
+msgstr "Камалоо"
+
+#: ../js/ui/userMenu.js:779
+msgid "Install Updates & Restart"
+msgstr "Жаңыртмаларды орнотуп жана кайта жүргүзүү"
+
+#: ../js/ui/userMenu.js:797
+msgid "Your chat status will be set to busy"
+msgstr "Чат статусу «бош эмес» дегенине коюлат"
+
+#: ../js/ui/userMenu.js:798
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+
+#: ../js/ui/viewSelector.js:85
+msgid "Windows"
+msgstr "Терезелер"
+
+#: ../js/ui/viewSelector.js:89
+msgid "Applications"
+msgstr "Тиркемелер"
+
+#: ../js/ui/viewSelector.js:93
+msgid "Search"
+msgstr "Издөө"
+
+#: ../js/ui/wanda.js:119
+#, c-format
+msgid ""
+"Sorry, no wisdom for you today:\n"
+"%s"
+msgstr ""
+"Кечириңиз, бүгүнкүгө эч кандай кеңештер жок:\n"
+"%s"
+
+#: ../js/ui/wanda.js:123
+#, c-format
+msgid "%s the Oracle says"
+msgstr "«%s» деп оракул айтып жатат"
+
+#: ../js/ui/wanda.js:164
+msgid "Your favorite Easter Egg"
+msgstr "Сиз жакшы көргөн «пасха жумуртасы»"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, c-format
+msgid "'%s' is ready"
+msgstr "«%s» терезеси даяр"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution жылнаамасы"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1089
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u чыгыш"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1099
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u кириш"
+
+#: ../src/gvc/gvc-mixer-control.c:1397
+msgid "System Sounds"
+msgstr "Системалык үндөр"
+
+#: ../src/main.c:330
+msgid "Print version"
+msgstr "Версиянын номерин көрсөтүү"
+
+#: ../src/main.c:336
+msgid "Mode used by GDM for login screen"
+msgstr "Кирүү экранына GDM менен колдонулуучу режим"
+
+#: ../src/main.c:342
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "Кирүү экраны үчүн белгилүү бир режимди колдонуу, мисалы «gdm»"
+
+#: ../src/main.c:348
+msgid "List possible modes"
+msgstr "Мүмкүн болгон режимдеринин тизмеси"
+
+#: ../src/shell-app.c:621
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr ""
+
+#: ../src/shell-keyring-prompt.c:708
+msgid "Passwords do not match."
+msgstr "Сырсөздөр дал келген жок."
+
+#: ../src/shell-keyring-prompt.c:716
+msgid "Password cannot be blank"
+msgstr "Сырсөз бош боло албайт"
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr "Улуу Британия"
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr "Жарыяланбас"
+
+#: ../src/shell-polkit-authentication-agent.c:343
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Аутентификация диалогун колдонуучу кабыл албады"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "өчүрүлгөн OpenSearch провайдерлери"
+
+#~ msgid "Retry"
+#~ msgstr "Кайталоо"
+
+#~ msgid "Connect to..."
+#~ msgstr "Туташуу…"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ОРУНДАР ЖАНА ОРНОТМОЛОР"
+
+#~ msgid "Passphrase"
+#~ msgstr "Сырсүйлөм"
+
+#~ msgid "Home"
+#~ msgstr "Үйдүк папка"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
diff --git a/po/lt.po b/po/lt.po
new file mode 100644
index 0000000..f9848ab
--- /dev/null
+++ b/po/lt.po
@@ -0,0 +1,3612 @@
+# Lithuanian translation for gnome-shell.
+# Copyright © 2010-2012 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+# Žygimantas Beručka <zygis@gnome.org>, 2010, 2011, 2012.
+# Algimantas Margevičius <gymka@mail.ru>, 2011.
+# Mantas Kriaučiūnas <mantas@akl.lt>, 2012, 2013.
+# Aurimas Černius <aurisc4@gmail.com>, 2013-2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-18 13:54+0000\n"
+"PO-Revision-Date: 2023-02-19 21:34+0200\n"
+"Last-Translator: Aurimas Černius <aurisc4@gmail.com>\n"
+"Language-Team: Lietuvių <gnome-lt@lists.akl.lt>\n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"(n%100<10 || n%100>=20) ? 1 : 2)\n"
+"X-Generator: Gtranslator 42.0\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Paleidikliai"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Paleisti mėgiamą programą 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Paleisti mėgiamą programą 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Paleisti mėgiamą programą 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Paleisti mėgiamą programą 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Paleisti mėgiamą programą 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Paleisti mėgiamą programą 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Paleisti mėgiamą programą 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Paleisti mėgiamą programą 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Paleisti mėgiamą programą 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Ekrano nuotraukos"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Padaryti ekrano nuotrauką interaktyviai"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Padaryti ekrano nuotrauką"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Padaryti lango nuotrauką"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Interaktyviai įrašyti ekrano vaizdą"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Rodyti pranešimų sąrašą"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokusuoti aktyvų pranešimą"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Rodyti apžvalgą"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Rodyti visas programas"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Atverti programų meniu"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell aplinka"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Langų tvarkymas ir programų paleidimas"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Įjungti vidinius, Alt-F2 klavišų pagalba pasiekiamus įrankius, naudingus "
+"programuotojams ir bandytojams"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Suteikia prieigą prie vidinio derinimo ir stebėjimo įrankių, naudojant Alt-"
+"F2 dialogą."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Įjungtų plėtinių UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell plėtiniai turi UUID savybę; šiame rakte išvardinti plėtiniai, "
+"kurie turėtų būti įkelti. Bet kuris plėtinys, kuris nori būti įkeltas, turi "
+"būti šiame sąraše. Šį sąrašą taip pat galima keisti naudojant org.gnome."
+"Shell D-Bus metodus EnableExtension ir DisableExtension."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Privestinai išjungtų plėtinių UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell plėtiniai turi UUID savybę; šiame rakte išvardinti plėtiniai, "
+"kurie turėtų būti išjungti net jei yra įkelti kaip dalis dabartinės "
+"veiksenos.Šį sąrašą taip pat galima keisti naudojant org.gnome.Shell D-Bus "
+"metodus EnableExtension ir DisableExtension. Šiam raktui teikiama pirmenybė "
+"prieš „enabled-extensions“ parametrą."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Išjungti naudotojo plėtinius"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Išjungti visus naudotojo įjungtus plėtinius nepakeičiant „enabled-extension“ "
+"nustatymo."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Išjungia plėtinio versijos suderinamumo tikrinimą"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell leidžia įkelti tik šiuo metu veikiančios versijos plėtinius. Šio "
+"parametro įjungimas išjungs šį tikrinimą ir bandys įkelti visus plėtinius "
+"nepaisant jų nurodomos palaikomos versijos."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Mėgstamų programų darbalaukio failų ID sąrašas"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programos, atitinkančios šiuos identifikatorius, bus rodomos mėgstamų "
+"srityje."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Komandų (Alt-F2) dialogo retrospektyva"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Didinamojo stiklo dialogo retrospektyva"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Visada rodyti naudotojo meniu punktą „Atsijungti“."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Šis raktas perrašo automatinį meniu punkto „Atsijungti“ slėpimą vieno "
+"naudotojo, vieno seanso situacijose."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Ar atsiminti slaptažodį prijungiant šifruotas ar nutolusias failų sistemas"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Apvalkalas prašys slaptažodžio, kai prijungiamas šifruotas įrenginys ar "
+"nutolusi failų sistema. Jei slaptažodis gali būti įrašytas ateičiai, "
+"žymimasis langelis „Įsiminti slaptažodį“ bus rodomas. Šis raktas nustato "
+"numatytąją žymimojo langelio būseną."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Paskutinis pasirinktas ne numatytasis energijos profilis"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Kai kurios sistemos palaiko daugiau nei du profilius. Siekiant palaikyti "
+"dviejų profilių keitimą, šiame rakte įrašomas paskutinis pasirinktas ne "
+"numatytasis profilis."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"Paskutinė versija, kuriai buvo rodytas dialogas „Sveiki pradėję naudoti "
+"GNOME“"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Šis raktas nusako, kuriai versijai paskutinį kartą buvo rodytas dialogas "
+"„Sveiki pradėję naudoti GNOME“. Tuščia eilutė reiškia seniausią galimą "
+"versiją, o milžiniški skaičiai reiškia versijas, kurių dar nėra. Todėl "
+"milžiniškus skaičius galima naudoti šio dialogo išjungimui."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Programų pasirinkimo išdėstymas"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Programų pasirinkimo išdėstymas. Kiekvienas įrašas masyve yra puslapis. "
+"Puslapiai yra įrašomi tokia tvarka, kokia jie matomi GNOME apvalkale. "
+"Kiekviename puslapyje yra pora „programos id“ → „duomenys“. Šiuo metu tokios "
+"vertės yra įrašomos kaip „duomenys“: • “position”: programos piktogramos "
+"padėtis puslapyje"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Klavišų susiejimas, kuriuo atveriamas programų meniu"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Klavišų susiejimas, kuriuo atveriamas programų meniu."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Klavišų susiejimas apžvalgos būsenų keitimui"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Klavišų susiejimas perėjimui tarp seanso, langų pasirinkimo ir programų "
+"tinklelio"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Klavišų susiejimas perėjimui tarp programų tinklelio, langų pasirinkimo bei "
+"seanso."
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Klavišų susiejimas, kuriuo atveriamas rodinys „rodyti programas“"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Klavišų susiejimas, kuriuo atveriamas rodinys „rodyti programas“ veiklų "
+"apžvalgoje."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Klavišų susiejimas, kuriuo atveriama apžvalga"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Klavišų susiejimas, kuriuo atveriama veiklų apžvalga."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Klavišų susiejimas pranešimų juostos matomumui perjungti"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Klavišų susiejimas pranešimų juostos matomumui perjungti."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Klavišų susiejimas, kuriuo fokusuojamas aktyvus pranešimas"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Klavišų susiejimas, kuriuo fokusuojamas aktyvus pranešimas."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Pereiti į programą 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Pereiti į programą 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Pereiti į programą 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Pereiti į programą 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Pereiti į programą 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Pereiti į programą 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Pereiti į programą 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Pereiti į programą 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Pereiti į programą 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Apriboti perjungėją dabartine darbo sritimi."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Jei teigiama, perjungimo lange bus rodomos tik programos, turinčios langų "
+"dabartinėje darbo srityje. Priešingu atveju įtraukiamos visos programos."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Programos piktogramos veiksena."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Nustato, kaip langai yra rodomi perjungimo lange. Galimi pasirinkimai yra "
+"„thumbnail-only“ (rodo lango miniatiūrą), „app-icon-only“ (rodo tik "
+"programos piktogramą) arba „both“ (abu)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Jei teigiama, perjungimo lange rodomi tik langai iš dabartinės darbo "
+"srities. Priešingu atveju įtraukiami visi langai."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Vietos"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Vietos, kurias rodyti pasaulio laikrodžiuose"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatinė vieta"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Ar gauti dabartinę vietą"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Vieta"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Vieta, kuriai rodyti prognozę"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Prikabinti modalinį dialogą prie tėvinio lango"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "Šis raktas perrašo org.gnome.mutter raktą, kai vykdoma GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Įjungti kraštų išplėtimą, kai langai numetami ekrano kraštuose"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Darbo sritys yra tvarkomos dinamiškai"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Darbo sritys tik pagrindiniame monitoriuje"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Atidėti fokuso pakeitimus pelei iki žymiklis nustos judėti"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Tinklo prisijungimas"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Atsitiko kažkas negero"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Atsiprašome, bet kilo problema: šio plėtinio nustatymų parodyti nepavyksta. "
+"Rekomenduojame pranešti apie problemą plėtinio autoriams."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Techninė informacija"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Namų tinklalapis"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Aplankyti plėtinio namų tinklalapį"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Atsisakyti"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Slaptažodis"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Pasirinkite seansą"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Nėra sąraše?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(pvz., naudotojas arba %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Naudotojo vardas"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Prisijungimo langas"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Tapatybės patvirtinimo klaida"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(arba perbraukite pirštu per skaitytuvą)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(arba padėkite pirštą ant skaitytuvo)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Išjungti"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "išjungti;perleisti;perkrauti;sustabdyti"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Perleisti"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "perleisti;paleisti iš naujo;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Užrakinti ekraną"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "užrakinti ekraną"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Atsijungti"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "atsijungti;išeiti;"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Pristabdyti"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "pristabdyti;užmigdyti"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Keisti naudotoją"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "keisti naudotoją"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "orientacijos užrakinimas;orientacijos atrakinimas;ekranas;pasukimas"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Padaryti ekrano nuotrauką"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "ekrano nuotrauka;ekrano įrašas;stebėjimas;fotografavimas;įrašymas;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Atrakinti ekrano orientaciją"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Užrakinti ekrano orientaciją"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Komanda nerasta"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Nepavyko perskaityti komandos:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Nepavyko įvykdyti „%s“:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Ką tik"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Prieš %d minutę"
+msgstr[1] "Prieš %d minutes"
+msgstr[2] "Prieš %d minučių"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Prieš %d valandą"
+msgstr[1] "Prieš %d valandas"
+msgstr[2] "Prieš %d valandų"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Vakar"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Prieš %d dieną"
+msgstr[1] "Prieš %d dienas"
+msgstr[2] "Prieš %d dienų"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Prieš %d savaitę"
+msgstr[1] "Prieš %d savaites"
+msgstr[2] "Prieš %d savaičių"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Prieš %d mėnesį"
+msgstr[1] "Prieš %d mėnesius"
+msgstr[2] "Prieš %d mėnesių"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Prieš %d metus"
+msgstr[1] "Prieš %d metus"
+msgstr[2] "Prieš %d metų"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Vakar, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%Y %m %-d, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Vakar, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%Y %m %-d, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Prisijungimas prie prieigos taško"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Jūsų prisijungimas prie šio prieigos taško yra nesaugus. Slaptažodžius bei "
+"kitą šiame puslapyje jūsų įvedamą informaciją gali matyti kiti asmenys."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Atmesti prieigą"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Suteikti prieigą"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Nepavadintas aplankas"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s pridėta prie jūsų mėgstamų."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s pašalinta iš jūsų mėgstamų."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Atverti langai"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Naujas langas"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Rodyti detalią informaciją"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Užverti"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Atsegti"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Prisegti juostoje"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Paleisti naudojant integruotą grafikos kortą"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Paleisti naudojant konkrečią grafikos kortą"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Pasirinkite garso įrenginį"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Garso nustatymai"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Ausinės"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Ausinės su mikrofonu"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofonas"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Keisti foną…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Ekrano nustatymai"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Nustatymai"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "A"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "K"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Š"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Ankstesnis mėnuo"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Kitas mėnuo"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Savaitė %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Nėra pranešimų"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Netrukdyti"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Išvalyti"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "„%s“ neatsiliepia."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Galite dar palaukti ir duoti programai laiko arba galite priverstinai "
+"išjungti programą."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Priverstinai išjungti"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Laukti"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Prijungta išorinė laikmena"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Atjungta išorinė laikmena"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Nepavyksta atrakinti garso"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Įdiegta udisks versija nepalaiko PIM nustatymo"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Atverti su %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Taip pat galite prisijungti paspausdami „WPS“ mygtuką savo maršrutizatoriuje."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Prisijungti"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Raktas"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Privataus rakto slaptažodis"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Tapatybė"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Tarnyba"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Reikia patvirtinti tapatybę"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Slaptažodžiai arba šifravimo raktai yra būtini priėjimui prie belaidžio "
+"tinklo „%s“."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Laidinis 802.1X tapatybės patvirtinimas"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Tinklo vardas"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL tapatybės patvirtinimas"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Reikalingas PIN kodas"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Reikalingas PIN kodas mobiliajam plačiajuosčiam įrenginiui"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Būtinas slaptažodis norint prisijungti prie „%s“."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Tinklo tvarkymas"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN slaptažodis"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Reikia patvirtinti tapatybę"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administratorius"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Patvirtinti tapatybę"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Atsiprašome, tai nesuveikė. Bandykite dar kartą."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s nuo šiol vadinasi %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Langai"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Paleisti programas"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Paleidimo sritis"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%Y %B %-d."
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %Y m. %B %d d."
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y %B %-d"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Šiandien"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Rytoj"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Visa diena"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m-%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nėra įvykių"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Pridėti pasaulio laikrodžius…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Pasaulio laikrodžiai"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Įkeliama…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Prisijunkite prie tinklo orų informacijai gauti"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Orų informacija šiuo metu yra neprieinama"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Orai"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Pasirinkite orų vietą…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Atjungti naudotoją %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Atsijungti"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s bus automatiškai atjungta (-s) už %d sekundės."
+msgstr[1] "%s bus automatiškai atjungta (-s) už %d sekundžių."
+msgstr[2] "%s bus automatiškai atjungta (-s) už %d sekundžių."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Jūs būsite automatiškai atjungti už %d sekundės."
+msgstr[1] "Jūs būsite automatiškai atjungti už %d sekundžių."
+msgstr[2] "Jūs būsite automatiškai atjungti už %d sekundžių."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Atsijungti"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Išjungti"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Įdiegti atnaujinimus ir išjungti"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistema automatiškai išsijungs po %d sekundės."
+msgstr[1] "Sistema automatiškai išsijungs po %d sekundžių."
+msgstr[2] "Sistema automatiškai išsijungs po %d sekundžių."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Įdiegti programinės įrangos atnaujinimus"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Išjungti"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Paleisti iš naujo"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Įdiegti atnaujinimus ir perleisti"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistema bus paleista iš naujo po %d sekundės."
+msgstr[1] "Sistema bus paleista iš naujo po %d sekundžių."
+msgstr[2] "Sistema bus paleista iš naujo po %d sekundžių."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Paleisti iš naujo"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Perleisti ir įdiegti atnaujinimus"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistema bus paleista iš naujo ir įdiegs atnaujinimus po %d sekundės."
+msgstr[1] ""
+"Sistema bus paleista iš naujo ir įdiegs atnaujinimus po %d sekundžių."
+msgstr[2] ""
+"Sistema bus paleista iš naujo ir įdiegs atnaujinimus po %d sekundžių."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Perleisti ir įdiegti"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Įdiegti ir išjungti"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Išjungti baigus diegti atnaujinimus"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Perleisti ir įdiegti atnaujinimą"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s bus įdiegta po perleidimo. Sistemos atnaujinimas gali ilgai užtrukti: "
+"įsitikinkite, kad pasidarėte atsarginę duomenų kopiją, ir kad kompiuteris "
+"yra įjungtas į elektros tinklą."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Senka baterijos energija: įjunkite į elektros tinklą prieš įdiegdami "
+"atnaujinimus."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Kai kurios programos dirba arba turi neįrašyto darbo."
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Yra prisijungusių kitų naudotojų"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Įkėlimo parametrai"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (nutolęs)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (komandų eilutė)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Įdiegti"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Išdiegti plėtinį"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Parsiųsti ir įdiegti „%s“ iš extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Yra plėtinių atnaujinimų"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Plėtinių atnaujinimai paruošti diegimui."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Leisti talpinti trumpinius"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Programa %s nori talpinti trumpinius"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Programa nori talpinti trumpinius"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Galite atstatyti trumpinius paspaudę %s"
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Neleisti"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Leisti"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Lėtieji klavišai įjungti"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Lėtieji klavišai išjungti"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Ką tik palaikėte nuspaudę 8 sekundes Lyg2 klavišą. Tai yra lėtųjų klavišų "
+"trumpinus, kuris keičia klaviatūros veikseną."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Kibieji klavišai įjungti"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Kibieji klavišai išjungti"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Ką tik paspaudėte Lyg2 klavišą 5 kartus iš eilės. Tai yra kibiųjų klavišų "
+"trumpinys, kuris pakeičia klaviatūros veikseną."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Ką tik vienu metu paspaudėte du klavišus arba paspaudėte Lyg2 klavišą 5 "
+"kartus iš eilės. Tai įjungia kibiuosius klavišus ir pakeičia klaviatūros "
+"veikseną."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Palikti įjungtą"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Įjungti"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Išjungti"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Palikti išjungtą"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Regiono ir kalbos nustatymai"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nėra įdiegtų plėtinių"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nepranešė apie jokias klaidas."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Slėpti klaidas"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Rodyti klaidas"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Įjungta"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Išjungta"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Klaida"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Pasenęs"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Atsiunčiama"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Žiūrėti šaltinį"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Tinklalapis"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Sistema perjungta į nesaugią veikseną"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Programos dabar turi neribotą prieigą"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Atšaukti"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Prisijungta privilegijuotu naudotoju"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Saugumo sumetimais turėtų būti vengiama vykdyti seansus privilegijuotais "
+"naudotojais. Jei įmanoma, turėtumėt visada prisjungti normaliu naudotoju."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Ekrano užraktas išjungtas"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Ekrano užrakinimas reikalaujas GNOME vaizduoklio valdyklės."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Sistemos informacija"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Nežinomas atlikėjas"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Nežinomas pavadinimas"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Rašykite, ko ieškote"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Programos"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Apžvalga"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Naujas trumpinys…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Programos nustatytas"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Rodyti pagalbą ekrane"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Keisti monitorių"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Priskirti klavišų kombinaciją"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Atlikta"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Keisti…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nėra"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Spauskite mygtuką konfigūravimui"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Spauskit Esc išėjimui"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Išėjimui spauskite bet kurį klavišą"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Apžvalga"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Viršutinė juosta"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Vykdyti komandą"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Spauskit ESC užvėrimui"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Perleisti Wayland aplinkoje negalima"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Perleidžiama…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME aplinkai reikia užrakinti ekraną"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Nepavyksta užrakinti"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Programa užblokavo užrakinimą"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Žymėjimas"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Srities žymėjimas"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Ekranas"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Ekrano žymėjimas"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Langas"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Lango žymėjimas"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Ekrano nuotrauka / įrašas"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Rodyti žymeklį"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Ekrano įrašai"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Ekrano įrašas iš %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Ekrano nuotrauka"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Ekrano įrašas įrašytas"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Spauskite čia įrašui žiūrėti."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Rodyti Failuose"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Ekrano nuotrauka iš %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Ekrano nuotrauka padaryta"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Galite įdėti paveikslėlį iš iškarpinės."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Ekrano nuotrauka padaryta"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Ieškoma…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Nerasta atitikmenų."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "dar %d"
+msgstr[1] "dar %d"
+msgstr[2] "dar %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Ieškoti"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopijuoti"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Įdėti"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Rodyti tekstą"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Slėpti tekstą"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Įjungtos didžiosios raidės."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Paslėptas garsumas"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows sisteminis tomas"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Naudoja raktų failus"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Norint atrakinti tomą, kuris naudoja raktų failus, naudokite <i>%s</i> "
+"įrankį."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM numeris"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Atsiminti slaptažodį"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Atrakinti"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Atverti %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM turi būti numeris arba tuščias."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Nepavyksta paleisti %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nepavyksta rasti programos %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Universali prieiga"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Didelis kontrastas"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Priartinimas"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Ekrano skaityklė"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Klaviatūra ekrane"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Vaizdo įspėjimai"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Kibieji klavišai"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Lėtieji klavišai"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Pasikartojantieji klavišai"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Pelės mygtukai"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Didelis tekstas"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatiškai pasukti"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Ryškumas"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Tamsi veiksena"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Vienas paspaudimas"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dvigubas paspaudimas"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Tempimas"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Antrinis paspaudimas"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Uždelstas paspaudimas"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Klaviatūra"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Rodyti klaviatūros išdėstymą"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Leisti vietos informacijos prieigą"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Programa %s nori prieigos prie jūsų būvimo vietos"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Prieigą prie vietos bet kada galima pakeisti privatumo nustatymuose."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<nežinoma>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Atsijungti nuo %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Prisijungti prie %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Prieigos taškas %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "VPN nustatymai"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Visi tinklai"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Laidiniai ryšiai"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Laidinio ryšio nustatymai"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth ryšiai"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Bluetooth nustatymai"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Mobilieji ryšiai"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Mobiliojo plačiajuosčio tinklo nustatymai"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Nepavyko prisijungti"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Tinklo ryšio nepavyko aktyvuoti"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Naktinis apšvietimas"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Našus"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Balansuotas"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Taupantis energiją"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Energijos profiliai"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Stabdyti ekrano įrašą"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Stabdyti dalinimąsi ekranu"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Skrydžio veiksena"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Padaryti ekrano nuotrauką"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Išjungimo meniu"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Pristabdyti"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Perleisti…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Išjungti…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Atsijungti…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Keisti naudotoją…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Ekrano užraktas"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Nežinoma Thunderbolt įrenginys"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Jums nesant aptiktas naujas įrenginys. Atjunkite ir vėl prijunkite įrenginį, "
+"jei norite jį naudoti."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Neleidžiamas Thunderbolt įrenginys"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Buvo aptiktas naujas įrenginys, kurį turi leisti administratorius."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt autorizacijos klaida"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nepavyko autorizuoti Thunderbolt įrenginio: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Garsumas pakeistas"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Garsumas"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Garso išvestis"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Garso įvestis"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Veidrodis"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Sujungti vaizduoklius"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Tik išorinis"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Tik vidinis"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Atrakinkite perbraukdami"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Atrakinkite mygtuko ar klavišo paspaudimu"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Atrakinimo langas"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Prisijungti kitu naudotoju"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Sveiki pradėję naudoti GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Jei norite sužinoti, kaip naudotis, išbandykite turą."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ne, ačiū"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Turas"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "„%s“ yra pasirengusi"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Ar norite įrašyti šiuos vaizduoklio nustatymus?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Grąžinti nustatymus"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Įrašyti pakeitimus"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Pakeitimai bus grąžinti po %d sekundės"
+msgstr[1] "Pakeitimai bus grąžinti po %d sekundžių"
+msgstr[2] "Pakeitimai bus grąžinti po %d sekundžių"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Slėpti"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Atstatyti"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Išdidinti"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Perkelti"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Keisti dydį"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Perkelti pavadinimo juostą į ekraną"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Visada viršuje"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Visada matomoje darbo srityje"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Perkelti į kairiau esančią darbo sritį"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Perkelti į dešiniau esančią darbo sritį"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Perkelti į aukščiau esančią darbo sritį"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Perkelti į žemiau esančią darbo sritį"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Perkelti į aukščiau esantį monitorių"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Perkelti į žemiau esantį monitorių"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Perkelti į kairiau esantį monitorių"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Perkelti į dešiniau esantį monitorių"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Užverti"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution kalendorius"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Išvesti versijos numerį"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Veiksena, naudojama GDM prisijungimo ekrane"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Naudoti konkrečią veikseną, pvz., „gdm“ prisijungimo ekranui"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Išvardinti galimas veiksenas"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Nežinoma"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Nepavyko paleisti „%s“"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Slaptažodžiai nesutampa."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Slaptažodis negali būti tuščias"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Naudotojas užvėrė tapatybės patvirtinimo dialogą"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Plėtiniai"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Tvarkyti GNOME plėtinius"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME projektas"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Plėtiniai atnaujina plėtinius, konfigūruoja jų nustatymus bei šalina "
+"arba išjungia nepageidaujamus plėtinius."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfigūruoti GNOME Shell plėtinius"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Nėra atitikmenų"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Pašalinti „%s“?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Jei pašalinsite plėtinį, reikės grįžti prie jo atsisiuntimo, jei norėsite jį "
+"vėl įjungti"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Pašalinti"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Išvertė:\n"
+"Aurimas Černius <aurisc4@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d plėtinys bus atnaujintas kitą kartą prisijungus."
+msgstr[1] "%d plėtiniai bus atnaujintas kitą kartą prisijungus."
+msgstr[2] "%d plėtinių bus atnaujintas kitą kartą prisijungus."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Plėtinys yra nesuderinamas su dabartine GNOME versija"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Plėtinyje kilo klaida"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Plėtiniai gali būti atnaujinti"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Tinklalapis"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Pašalinti…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Žinynas"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Apie plėtinius"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Plėtiniai gali sukelti našumo ir stabilumo problemų. Išjunkite plėtinius, "
+"jei turite problemų su sistema."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Įdiegtas rankomis"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Plėtinius galima rasti ir įdiegti per apsilankius <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Vidinis"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Nėra įdiegtų plėtinių"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Atsiprašome, bet nepavyko gauti įdiegtų plėtinių sąrašo. Įsitikinkite, kad "
+"esate prisijungęs prie GNOME ir bandykite vėl."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Plėtinių atnaujinimai paruošti"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Naujas plėtinys sėkmingai sukurtas kataloge %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Pavadinimas turi būti trumpa (geriausia apibūdinant) simbolių eilutė.\n"
+"Pavyzdžiai: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Pavadinimas"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Aprašymas yra vieno sakinio paaiškinimas, ką daro plėtinys.\n"
+"Pavyzdžiai: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Aprašymas"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID yra visuotinai unikalus identifikatorius jūsų plėtiniui.\n"
+"Jis turėtų būti el. pašto adreso formato (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Pasirinkite vieną iš šablonų:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Šablonas"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Unikalus identifikatorius naujam plėtiniui"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "PAVADINIMAS"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Naudotojui matomas plėtinio pavadinimas"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "APRAŠYMAS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Trumpas aprašymas, ką daro plėtinys"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ŠABLONAS"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Šablonas naujam plėtiniui"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Įveskite plėtinio informaciją interaktyviai"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Sukurti naują plėtinį"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Nežinomi argumentai"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, pavadinimas ir aprašymas yra būtini"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Plėtinio „%s“ nėra\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Išjungti plėtinį"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nenurodytas UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Pateiktas daugiau nei vienas UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Išjungti plėtinį"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Nepavyko prisijungti prie GNOME apvalkalo\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Plėtinio „%s“ nėra\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Rodyti plėtinio informaciją"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Perrašyti esamą plėtinį"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PLĖTINIO_RYŠULYS"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Įdiegti plėtinio ryšulį"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nenurodytas plėtinio ryšulys"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Nurodytas daugiau nei vienas plėtinio ryšulys"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Rodyti naudotojo įdiegtus plėtinius"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Rodyti sistemoje įdiegtus plėtinius"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Rodyti įjungtus plėtinius"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Rodyti išjungtus plėtinius"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Rodyti plėtinius su nuostatomis"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Rodyti plėtinius su atnaujinimais"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Atspausdinti plėtinio informaciją"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Išvardinti įdiegtus plėtinius"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FAILAS"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Papildomas šaltinis įdėjimui į ryšulį"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "GSettings schema, kuri turi būti įtraukta"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "KATALOGAS"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Katalogas, kuriame yra vertimai"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMENAS"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "gettext domenas, naudojamas vertimams"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Perrašyti esamą paketą"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Katalogas, kuriame turi būti sukurtas paketas"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "KODO_KATALOGAS"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Sukurti plėtinio ryšulį"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Nurodytas daugiau nei vienas kodo aplankas"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Plėtinys „%s“ neturi nustatymų\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Nepavyko atverti nuostatų plėtinio „%s“: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Atveria plėtinio nuostatas"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Atstatyti plėtinį"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Negalima pašalinti sisteminių plėtinių\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Nepavyko pašalinti „%s“\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Išdiegti plėtinį"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Nespausdinti klaidų pranešimų"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Kelias"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Pradinis autorius"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versija"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Būsena"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "„version“ nepriima argumentų"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Naudojimas:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Atspausdinti versijos informaciją ir išeiti."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "KOMANDA"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARG…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Komandos:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Atspausdinti žinyną"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Įjungti ]plėtinį"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Išjungti plėtinį"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Atstatyti plėtinį"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Išdiegti plėtinį"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Išvardinti plėtinius"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Rodyti plėtinio informaciją"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Atverti plėtinio nuostatas"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Sukurti plėtinį"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Supakuoti plėtinį"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Įdiegti plėtinio ryšulį"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Detalesnį žinyną gausite panaudoję „%s“.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Grynasis"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Tuščias plėtinys"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikatorius"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Pridėti piktogramą prie viršutinės juostos"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u išvestis"
+msgstr[1] "%u išvestys"
+msgstr[2] "%u išvesčių"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u įvestis"
+msgstr[1] "%u įvestys"
+msgstr[2] "%u įvesčių"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Sistemos garsai"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Ar numatytasis Bluetooth adapteris buvo nusistatęs su juo susietus "
+#~ "įrenginius"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Apvalkalas rodys Bluetooth meniu elementą tik tuo atveju, jei Bluetooth "
+#~ "adapteris yra pajungtas arba jei buvo nustatyti įrenginiai, susieti su "
+#~ "numatytuoju adapteriu. Tai bus atstatyta, jeigu kada nors bus pastebėta, "
+#~ "kad numatytasis adapteris neturi su juo susietų įrenginių."
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d prijungtas"
+#~ msgstr[1] "%d prijungti"
+#~ msgstr[2] "%d prijungta"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth įjungtas"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth išjungtas"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Vietos nustatymas įjungtas"
+
+#~ msgid "Disable"
+#~ msgstr "Išjungti"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Privatumo nustatymai"
+
+#~ msgid "Location In Use"
+#~ msgstr "Vietos nustatymas naudojamas"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Vietos nustatymas išjungtas"
+
+#~ msgid "Enable"
+#~ msgstr "Įjungti"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s išjungtas"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "Prisijungta prie %s"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s netvarkomas"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "Jungiamasi prie %s"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s reikalauja patvirtinti tapatybę"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s trūksta integruotos programinės įrangos"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s nepasiekiamas"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Nepavyko prisijungti prie %s"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s aparatinė įranga išjungta"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s išjungtas"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Prisijungti prie interneto"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Skrydžio veiksena įjungta"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Belaidis ryšys yra išjungta skrydžio veiksenoje."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Išjungti skrydžio veikseną"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Belaidžio ryšys išjungtas"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Norint prisijungti prie tinklo reikia įjungti belaidį ryšį."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Įjungti belaidį ryšį"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wi-Fi tinklai"
+
+#~ msgid "Select a network"
+#~ msgstr "Pasirinkite tinklą"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Išjungimui naudoti aparatinį jungiklį"
+
+#~ msgid "Select Network"
+#~ msgstr "Pasirinkite tinklą"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Belaidžio ryšio nustatymai"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "Neprisijungta prie %s"
+
+#~| msgid "<unknown>"
+#~ msgid "unknown"
+#~ msgstr "nežinoma"
+
+#~| msgid "Estimating…"
+#~ msgid "activating…"
+#~ msgstr "aktyvinama…"
+
+#~| msgid "Estimating…"
+#~ msgid "deactivating…"
+#~ msgstr "deaktyvinama…"
+
+#~ msgid "deactivated"
+#~ msgstr "deaktyvinta"
+
+#~ msgid "connecting…"
+#~ msgstr "jungiamasi…"
+
+#~ msgid "authentication required"
+#~ msgstr "reikia patvirtinti tapatybę"
+
+#~ msgid "connection failed"
+#~ msgstr "nepavyko prisijungti"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN išjungtas"
+
+#~ msgid "Network Settings"
+#~ msgstr "Tinklo nustatymai"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s belaidis ryšys"
+#~ msgstr[1] "%s belaidžiai ryšiai"
+#~ msgstr[2] "%s belaidžių ryšių"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Naktinis apšvietimas išjungtas"
+
+#~ msgid "Resume"
+#~ msgstr "Tęsti"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Išjungti iki rytojaus"
+
+#~ msgid "Power Settings"
+#~ msgstr "Energijos valdymo nustatymai"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Pilnai įkrauta"
+
+#~ msgid "Not Charging"
+#~ msgstr "Nekraunama"
+
+#~ msgid "Estimating…"
+#~ msgstr "Įvertinama…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "Liko %d∶%02d (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d iki pilno (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Ekranas yra bendrinamas"
+
+#~ msgid "Turn off"
+#~ msgstr "Išjungti"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Skrydžio veiksena įjungta"
+
+#~ msgid "Lock"
+#~ msgstr "Užrakinti"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Išjungti / Atsijungti"
+
+#~ msgid "Log Out"
+#~ msgstr "Atsijungti"
+
+#~| msgid "Show on-screen help"
+#~ msgid "Show screenshot UI"
+#~ msgstr "Rodyti ekrano nuotraukos sąsają"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Pašalinti iš mėgstamų"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Pridėti prie mėgstamų"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Ekrano vaizdas įrašomas"
+
+#~ msgid "Stop"
+#~ msgstr "Stabdyti"
+
+#~ msgid "Author"
+#~ msgstr "Autorius"
+
+#~ msgid "Warning"
+#~ msgstr "Įspėjimas"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Plėtiniai gali sukelti sistemai problemų, įskaitant našumo problemas. Jei "
+#~ "su tokiomis susidursite, rekomenduojama išjungti visus plėtinius."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Įjungti nagrinėjimo sąsają"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Įjungia D-Bus sąsają, kuri leidžia nagrinėti apvalkalo programos būseną."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Nepavyko prisijungti prie GNOME apvalkalo"
+
+#~ msgid "Minimize"
+#~ msgstr "Sumažinti"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Grąžinti iš išdidinimo"
+
+#~ msgid "App Picker View"
+#~ msgstr "Programos pasirinkimo vaizdas"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Šiuo metu pasirinkto vaizdo programų vaizde indeksas."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Čia bus matomos dažnai naudojamos programos"
+
+#~ msgid "Frequent"
+#~ msgstr "Dažnai naudojamos"
+
+#~ msgid "All"
+#~ msgstr "Visos"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %B %-d."
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %Y %B %-d."
+
+#~ msgid "Off"
+#~ msgstr "Išjungta"
+
+#~ msgid "On"
+#~ msgstr "Įjungta"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopijuoti klaidą"
+
+#~ msgid "Next"
+#~ msgstr "Kitas"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Prisijungti"
+
+#~ msgid "Password: "
+#~ msgstr "Slaptažodis: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Įveskite slaptažodį…"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %B %d d."
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d naujas pranešimas"
+#~ msgstr[1] "%d nauji pranešimai"
+#~ msgstr[2] "%d naujų pranešimų"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d naujas pranešimas"
+#~ msgstr[1] "%d nauji pranešimai"
+#~ msgstr[2] "%d naujų pranešimų"
+
+#~ msgid "Logout…"
+#~ msgstr "Atsijungti…"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Naršyti Programinėje įrangoje"
+
+#~| msgid "Username: "
+#~ msgid "Rename"
+#~ msgstr "Pervadinti"
+
+#~ msgid "Type again:"
+#~ msgstr "Įveskite dar kartą:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Belaidžiam tinklui reikia patvirtinti tapatybę"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Mobiliojo plačiajuosčio tinklo slaptažodis"
+
+#~ msgid "Account Settings"
+#~ msgstr "Paskyros nustatymai"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Padėties užrakinimas"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Klavišų susiejimas, kuris sustabdo ir pratęsia visus veikiančius "
+#~ "elementus, derinimui"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Kurią klaviatūrą naudoti"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Naudotinas klaviatūros tipas."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "%s all day."
+#~ msgstr "%s visą dieną."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, o vėliau %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, tuomet %s, o po to vėliau %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Jaučiama kaip %s."
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Kilo klaida įkeliant %s nuostatų dialogą:"
+
+#~| msgid "Log Out"
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Atsijungti"
+
+#~| msgid "Switch User"
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Keisti naudotoją"
+
+#~ msgid "Hide tray"
+#~ msgstr "Slėpti dėklą"
+
+#~ msgid "Status Icons"
+#~ msgstr "Būsenos piktogramos"
+
+#~ msgid "Events"
+#~ msgstr "Įvykiai"
+
+#~ msgid "Notifications"
+#~ msgstr "Pranešimai"
+
+#~ msgid "Media"
+#~ msgstr "Medija"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Reikia patvirtinti tapatybę internete"
+
+#~ msgid "Not In Use"
+#~ msgstr "Naudojamas"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Rodyti savaitės dienas kalendoriuje"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Jeigu reikšmė teigiama, kalendoriuje rodyti ISO savaičių datą."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Naudoti kaip interneto ryšį"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s prašo prieigos prie jūsų buvimo vietos."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (wayland kompozitorius)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d prijungtas įrenginys"
+#~ msgstr[1] "%d prijungti įrenginiai"
+#~ msgstr[2] "%d prijungtų įrenginių"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Baterija"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l:%M"
+
+#~ msgid "Invitation"
+#~ msgstr "Kvietimas"
+
+#~ msgid "Call"
+#~ msgstr "Skambutis"
+
+#~ msgid "File Transfer"
+#~ msgstr "Failo persiuntimas"
+
+#~ msgid "Chat"
+#~ msgstr "Pokalbiai"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Kvietimas į %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s jus kviečia prisijungti prie %s"
+
+#~ msgid "Decline"
+#~ msgstr "Atmesti"
+
+#~ msgid "Accept"
+#~ msgstr "Priimti"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Vaizdo skambutis nuo %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Skambutis nuo %s"
+
+#~ msgid "Answer"
+#~ msgstr "Atsiliepti"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s jums siunčia %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s pageidauja matyti, kai esate prisijungę prie interneto"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Nepavyko patvirtinti tapatybės"
+
+#~ msgid "Encryption error"
+#~ msgstr "Šifravimo klaida"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Liudijimas nepateiktas"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Liudijimas nepatikimas"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Liudijimo galiojimas pasibaigęs"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Liudijimas neaktyvuotas"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Liudijimo serverio vardo nesutapimas"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Liudijimo piršto atspaudo nesutapimas"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Liudijimas pačių pasirašytas"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Nustatyta atsijungimo būsena"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Liudijimas netinkamas"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Ryšys atmestas"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Nepavyko užmegzti ryšio"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Ryšys nutrūko"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Ši paskyra jau prijungta prie serverio"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Ryšys pakeistas nauju ryšiu naudojant tą patį išteklių"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Tokia paskyra serveryje jau yra"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Šiuo metu serveris per daug užimtas šiai užklausai apdoroti"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Liudijimas atšauktas"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Liudijimui naudojamas nesaugus šifravimo algoritmas arba jis "
+#~ "kriptografiškai silpnas"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Serverio liudijimo ilgis arba liudijimų eilės dydis viršija "
+#~ "kriptografijos bibliotekos apribojimus"
+
+#~ msgid "Internal error"
+#~ msgstr "Vidinė klaida"
+
+#~ msgid "View account"
+#~ msgstr "Rodyti paskyrą"
+
+#~| msgid "Show the message tray"
+#~ msgid "Show the message list"
+#~ msgstr "Rodyti pranešimų sąrašą"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Pagavimo portalas"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Sk"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Pr"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "An"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Tr"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Kt"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Pn"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Št"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Nieko nesuplanuota"
+
+#~ msgid "This week"
+#~ msgstr "Šią savaitę"
+
+#~ msgid "Next week"
+#~ msgstr "Kitą savaitę"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Išimami įrenginiai"
+
+#~ msgid "Eject"
+#~ msgstr "Išimti"
+
+#~ msgid "Unmute"
+#~ msgstr "Įjungti garsą"
+
+#~ msgid "Mute"
+#~ msgstr "Nutildyti"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Atverti kalendorių"
+
+#~ msgid "Open"
+#~ msgstr "Atverti"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Išvalyti pranešimus"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Pranešimų nustatymai"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Dėklo meniu"
+
+#~ msgid "Message Tray"
+#~ msgstr "Pranešimų juosta"
diff --git a/po/lv.po b/po/lv.po
new file mode 100644
index 0000000..d8270d1
--- /dev/null
+++ b/po/lv.po
@@ -0,0 +1,3324 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+#
+# Rudolfs <rudolfs.mazurs@gmail.com>, 2011.
+# Rūdofls Mazurs <rudolfs.mazurs@gmail.com>, 2011, 2012, 2013, 2014.
+# Pēteris Krišjānis <pecisk@gmail.com>, 2011.
+# Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022.
+# Vika Leimane <vika.leimane@gmail.com>, 2020.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-08-12 03:40+0000\n"
+"PO-Revision-Date: 2022-09-03 12:21+0300\n"
+"Last-Translator: Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>\n"
+"Language-Team: Latvian <lata-l10n@googlegroups.com>\n"
+"Language: lv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 :"
+" 2);\n"
+"X-Generator: Lokalize 21.12.3\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Palaidēji"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktivizēt iecienīto lietotni 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktivizēt iecienīto lietotni 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktivizēt iecienīto lietotni 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktivizēt iecienīto lietotni 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktivizēt iecienīto lietotni 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktivizēt iecienīto lietotni 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktivizēt iecienīto lietotni 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktivizēt iecienīto lietotni 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktivizēt iecienīto lietotni 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2070
+msgid "Screenshots"
+msgstr "Ekrānattēli"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Interaktīvi uzņemt ekrānattēlu"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Uzņemt ekrānattēlu"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Uzņemt loga ekrānattēlu"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Interaktīvi ierakstīt ekrānierakstu"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistēma"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Rādīt paziņojumu sarakstu"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokusēt aktīvo paziņojumu"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Rādīt pārskatu"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Rādīt visas lietotnes"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Atvērt lietotnes izvēlni"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME čaula"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Logu pārvaldība un lietotņu palaišana"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Aktivēt iekšējos rīkus, kas pieejami no Alt-F2; noder izstrādātājiem un "
+"testētājiem"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Ļauj piekļūt iekšējiem atkļūdošanas un pārraudzības rīkiem, izmantojot Alt-"
+"F2 dialoglodziņu."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID paplašinājumiem, kurus aktivēt"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell paplašinājumiem ir UUID īpašība; šī atslēga uzskaita "
+"paplašinājumus, kurus vajadzētu ielādēt. Katram paplašinājumam, kas grib "
+"tikt ielādēts, jābūt šajā sarakstā. Šo sarakstu var mainīt arī ar "
+"EnableExtension un DisableExtension D-Bus metodēm uz org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID paplašinājumiem, kurus forsēti izslēgt"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell paplašinājumiem ir UUID īpašība; šī atslēga uzskaita "
+"paplašinājumus, kurus vajadzētu izslēgt, pat ja ielādēti. Šo sarakstu var "
+"mainīt arī ar EnableExtension un DisableExtension D-Bus metodēm uz org.gnome."
+"Shell. Šai atslēga ņem virsroku pār “enabled-extensions” iestatījumu."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Izslēgt lietotāju paplašinājumus"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Izslēgt visus paplašinājumus, ko ir ieslēdzis lietotājs, neietekmējot "
+"iestatījumu “enabled-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Deaktivē paplašinājumu versijas savietojamības pārbaudīšanu"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME čaula ielādēs tikai tos paplašinājumus, kuri apgalvos, ka atbalsta "
+"pašlaik palaisto versiju. Šīs opcijas aktivēšana deaktivēs šo pārbaudi un "
+"mēģinās ielādēt visus paplašinājumus, neatkarīgi no tā, kādu čaulas versiju "
+"tie atbalsta."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Uzskaitīt iecienīto lietotņu darbvirsmas datņu ID"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Lietotnes, kas atbilst šiem identifikatoriem, tiks rādītas izlases laukā."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Komandu (Alt-F2) dialoglodziņa vēsture"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Looking glass dialoglodziņa vēsture"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Lietotāja izvēlnē vienmēr rādīt izvēlnes vienumu “Izrakstīties”."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Šī atslēga pārraksta automātisko “Izrakstīties” izvēlnes vienuma slēpšanu "
+"situācijās, kad ir tikai viens lietotājs vai viena sesija."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "Vai atcerēties paroli šifrētu vai attālinātu datņu sistēmu montēšanai"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Čaula prasīs paroli, kad tiks montēta šifrēta ierīce vai attālināta datņu "
+"sistēma. Ja paroli var saglabāt turpmākai lietošanai, būs redzama izvēles "
+"rūtiņa “Atcerēties paroli”. Šī atslēga iestata šīs izvēles rūtiņas "
+"noklusējuma stāvokli."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Vai noklusējuma Bluetooth adapterim ir iestatītas ar to asociētās ierīces"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Čaula rādīs Bluetooth izvēlnes vienumu tikai tad, ja Bluetooth adapteris ir "
+"ieslēgts vai ja ir iestatītas ierīces, kas ir saistītas ar noklusējuma "
+"adapteri. Tas tiks atiestatīts, ja noklusējuma adapteris jebkad tiks redzēts "
+"bez saistītajām ierīcēm."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "Pēdējais izvēlētais ne-noklusējuma jaudas profils"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Dažas sistēmas atbalsta vairāk kā divus jaudas profilus. Lai atbalstītu"
+" pārslēgšanos starp diviem profiliem, šī atslēga ieraksta pēdējo izvēlēto"
+" ne-noklusējuma profilu."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"Pēdējā versija dialoglodziņam “Laipni lūgti GNOME” tika rādīts versijai"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Šī atslēga nosaka, kura versija tika parādīta dialoglodziņam “Laipni lūgti "
+"GNOME”. Tukša virkne apzīmē vecāko iespējamo versiju un milzīgs skaitlis "
+"apzīmē versiju, kas vēl neeksistē. Milzīgo skaitli var izmantot, lai "
+"praktiski izslēgtu šo dialoglodziņu."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Lietotņu izvēlētāja izkārtojums"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Lietotņu izvēlētāja izkārtojums. Katrs ieraksts masīvā ir lapa. Lapas tiek "
+"glabātas tādā secībā, kā tās parādās GNOME čaulā. Katra lapa satur "
+"“lietotnes id” → “dati” pāri. Šobrīd lietotņu ikonu vieta lapā tiek "
+"saglabāta kā “dati”: • “pozīcija”"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Taustiņu sasaiste, kas atver lietotnes izvēlni"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Taustiņu sasaiste, kas atver lietotnes izvēlni."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Taustiņu sasaiste, lai pārslēgtos starp pārskata stāvokļiem"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Taustiņu sasaiste, lai pārslēgtos starp sesiju, logu izvēlētāju un lietotņu "
+"režģi"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Taustiņu sasaiste, lai pārslēgtos starp lietotņu režģi, logu izvēlētāju un "
+"sesiju"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Taustiņu sasaiste, kas atver “Rādīt lietotnes” skatu"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Taustiņu sasaiste, kas atver “Rādīt lietotnes” aktivitāšu pārskata skatu."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Taustiņu sasaiste, kas atver pārskatu"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Taustiņu sasaiste, kas atver aktivitāšu pārskatu."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Taustiņu sasaiste, kas pārslēdz paziņojumu saraksta redzamību"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Taustiņu sasaiste, kas pārslēdz paziņojumu saraksta redzamību."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Taustiņu sasaiste, kas fokusē aktīvo paziņojumu"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Taustiņu sasaiste, kas fokusē aktīvo paziņojumu."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Pārslēgties uz lietotni 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Pārslēgties uz lietotni 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Pārslēgties uz lietotni 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Pārslēgties uz lietotni 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Pārslēgties uz lietotni 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Pārslēgties uz lietotni 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Pārslēgties uz lietotni 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Pārslēgties uz lietotni 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Pārslēgties uz lietotni 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Ierobežot pārslēdzēju uz pašreizējo darbvietu."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ja patiess, pārslēdzējā tiek rādītas tikai lietotnes, kas ir pašreizējā "
+"darbvietā. Citādi tiek iekļautas visas lietotnes."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "Lietotnes ikonas režīms."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Konfigurē, kā pārslēdzējā tiek parādīts logs. Derīgās iespējas ir “thumbnail-"
+"only” (rāda loga sīktēlu), “app-icon-only” (rāda tikai lietotnes ikonu) vai "
+"“both” (abi)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ja patiess, pārslēdzējā tiek rādītas tikai lietotnes no pašreizējās "
+"darbvietas. Citādi tiek iekļautas visas lietotnes."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Vietas"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Vietas, kuras rādīt pasaules pulksteņos"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Automātiska atrašanās vieta"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Vai noteikt pašreizējo atrašanās vietu"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Vieta"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "Vieta, kurai rādīt laika prognozi"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Pievienot modālo dialoglodziņu vecāka logam"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "Šī atslēga pārraksta org.gnome.mutter atslēgu, darbinot GNOME čaulu."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Aktivēt logu sānisko izklāšanu, kad to nomet uz ekrāna malas"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Darbvietas tiek pārvaldītas dinamiski"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Darbvietas tikai uz galvenā monitora"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Peles režīmā aizkavēt fokusa izmaiņas, līdz rādītājs pārstāj kustēties"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Tīkla ierakstīšanās"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Kaut kas nogāja greizi"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Mums ir ļoti žēl, bet gadījās problēma: nevar parādīt šī paplašinājuma "
+"iestatījumus. Mēs iesakām jums ziņot par šo problēmu paplašinājuma autoriem."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Tehniskā informācija"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Mājaslapa"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Apmeklēt paplašinājum mājaslapu"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Atcelt"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Parole"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Izvēlieties sesiju"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Nav sarakstā?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(piemēram, lietotājs vai %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Lietotājvārds"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Ierakstīšanās logs"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Autentifikācijas kļūda"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(vai velciet pirkstu pāri lasītājam)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(vai novietojiet pirkstu uz lasītāja)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Izslēgt"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "izslēgt;beidzēt;apturēt"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Pārstartēt"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "pārstartēt;restartēt;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bloķēt ekrānu"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "bloķēt ekrānu"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Izrakstīties"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "izrakstīties;atteikties;izsaiņoties;logautoties"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Iesnaudināt"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "iesnaudināt;suspendot;"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Mainīt lietotāju"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "mainīt lietotāju"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "noslēgt orientāciju;atslēgt orientāciju;ekrāns;rotācija;pagriešana"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Uzņemt ekrānattēlu"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "ekrānattēls;ekrānieraksts;izgriezums;uzņēmums;ieraksts"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Atslēgt ekrāna orientāciju"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Noslēgt ekrāna orientāciju"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Komanda nav atrasta"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Neizdevās apstrādāt komandu:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” izpilde neizdevās:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Tikko"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Pirms %d minūtes"
+msgstr[1] "Pirms %d minūtēm"
+msgstr[2] "Pirms %d minūtēm"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Pirms %d stundas"
+msgstr[1] "Pirms %d stundām"
+msgstr[2] "Pirms %d stundām"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Vakar"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Pirms %d dienas"
+msgstr[1] "Pirms %d dienām"
+msgstr[2] "Pirms %d dienām"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Pirms %d nedēļas"
+msgstr[1] "Pirms %d nedēļām"
+msgstr[2] "Pirms %d nedēļām"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Pirms %d mēneša"
+msgstr[1] "Pirms %d mēnešiem"
+msgstr[2] "Pirms %d mēnešiem"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Pirms %d gada"
+msgstr[1] "Pirms %d gadiem"
+msgstr[2] "Pirms %d gadiem"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H.%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Vakar, %H.%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H.%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d. %B, %H.%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d. %B, %Y., %H.%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l.%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Vakar, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l.%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d. %B, %l.%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d. %B, %Y., %l.%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Tīklāja ierakstīšanās"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Jūsu savienojums ar šo tīklāju nav drošs. Paroles un cita informācija, ko "
+"jūs ievadāt šajā lapā var būt redzama tuvumā esošiem cilvēkiem."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Liegt pieeju"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Piešķirt pieeju"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Nenosaukta mape"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s ir piesprausts panelim."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s ir atsprausts no paneļa."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Atvērt logus"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Jauns logs"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Rādīt sīkāku informāciju"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Iziet"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Atspraust"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Piespraust pie paneļa"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Palaist, izmantojot integrēto grafikas karti"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Palaist, izmantojot diskrēto grafikas karti"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Izvēlieties audio ierīci"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Skaņas Iestatījumi"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Austiņas"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Austiņas ar mikrofonu"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Mikrofons"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Mainīt fonu…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Displeja iestatījumi"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Iestatījumi"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Sv"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Pr"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Ot"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Tr"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ce"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Pk"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Se"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:412
+msgid "%OB %Y"
+msgstr "%OB %Y."
+
+#: js/ui/calendar.js:473
+msgid "Previous month"
+msgstr "Iepriekšējais mēnesis"
+
+#: js/ui/calendar.js:491
+msgid "Next month"
+msgstr "Nākamais mēnesis"
+
+#: js/ui/calendar.js:642
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:701
+msgid "Week %V"
+msgstr "Nedēļa %V"
+
+#: js/ui/calendar.js:880
+msgid "No Notifications"
+msgstr "Nav paziņojumu"
+
+#: js/ui/calendar.js:937
+msgid "Do Not Disturb"
+msgstr "Netraucēt"
+
+#: js/ui/calendar.js:958
+msgid "Clear"
+msgstr "Attīrīt"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” nereaģē."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Var uzgaidīt neilgu brīdi, līdz lietotne atgūstas, vai arī aizvērt to "
+"piespiedu kārtā."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Piespiedu apturēšana"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Gaidīt"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Pievienots ārējs dzinis"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Atvienots ārējs dzinis"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Nevar atbloķēt sējumu"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Instalētā udisks versija neatbalsta PIM iestatījumu"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Atvērt ar %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Varat arī savienoties, uz sava maršrutētāja spiežot pogu “WPS”."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Savienot"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Atslēga"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Privātās atslēgas parole"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitāte"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Serviss"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Nepieciešama autentifikācija"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Lai piekļūtu bezvadu tīklam “%s”, ir nepieciešamas paroles vai šifrēšanas "
+"atslēgas."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Vadu 802.1X autentifikācija"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Tīkla nosaukums"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL autentifikācija"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Nepieciešams PIN kods"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Mobilā platjoslas tīkla ierīcei ir vajadzīgs PIN kods"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Lai savienotos ar “%s”, ir nepieciešama parole."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Tīkla pārvaldnieks"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN parole"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Nepieciešama autentifikācija"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrators"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autentificēt"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Diemžēl tas nenostrādāja. Lūdzu, mēģiniet vēlreiz."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s tagad zināms kā %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Logi"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Rādīt lietotnes"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Panelis"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e. %B, %Y."
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d. %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y. gada %-d. %B"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Šodien"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Rīt"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Visu dienu"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d.%m."
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nav notikumu"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Pievienot pasaules pulksteņus…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Pasaules pulksteņi"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Ielādē…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Laika ziņas skatīt tiešsaistē"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Laika ziņas pašlaik nav pieejamas"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Laikapstākļi"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Izvēlieties laikapstākļu vietu…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Izrakstīt %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Izrakstīties"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s tiks automātiski izrakstīts no sesijas pēc %d sekundes."
+msgstr[1] "%s tiks automātiski izrakstīts no sesijas pēc %d sekundēm."
+msgstr[2] "%s tiks automātiski izrakstīts no sesijas pēc %d sekundēm."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Jūs tiksiet automātiski izrakstīts no sesijas pēc %d sekundes."
+msgstr[1] "Jūs tiksiet automātiski izrakstīts no sesijas pēc %d sekundēm."
+msgstr[2] "Jūs tiksiet automātiski izrakstīts no sesijas pēc %d sekundēm."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Izrakstīties"
+
+#: js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Power Off"
+msgstr "Izslēgt"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalēt atjauninājumus un izslēgt"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistēma tiks izslēgta automātiski pēc %d sekundes."
+msgstr[1] "Sistēma tiks izslēgta automātiski pēc %d sekundēm."
+msgstr[2] "Sistēma tiks izslēgta automātiski pēc %d sekundēm."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalēt sagatavotos programmatūras atjauninājumus"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Izslēgt"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Pārstartēt"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalēt atjauninājumus un pārstartēt"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistēma tiks automātiski pārstartēta pēc %d sekundes."
+msgstr[1] "Sistēma tiks automātiski pārstartēta pēc %d sekundēm."
+msgstr[2] "Sistēma tiks automātiski pārstartēta pēc %d sekundēm."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Pārstartēt"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Pārstartēt un instalēt atjauninājumus"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistēma tiks automātiski pārstartēta un instalēs atjauninājumus pēc %d "
+"sekundes."
+msgstr[1] ""
+"Sistēma tiks automātiski pārstartēta un instalēs atjauninājumus pēc %d "
+"sekundēm."
+msgstr[2] ""
+"Sistēma tiks automātiski pārstartēta un instalēs atjauninājumus pēc %d "
+"sekundēm."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Pārstartēt un instalēt"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalēt un izslēgt"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Izslēgt pēc atjauninājumu instalēšanas"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Pārstartēt un instalēt uzlabojumus"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Pēc pārstartēšanas tiks instalēts %s %s. Uzlabošana var aizņemt daudz laika: "
+"pārliecinieties, ka esat izveidojši rezerves kopijas un ka dators ir "
+"pievienots strāvai."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Maz baterijas enerģijas. Lūdzu, pieslēdziet elektrībai, pirms instalējat "
+"atjauninājumus."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Dažas lietotnes ir aizņemtas vai ir nesaglabāts darbs"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Citi lietotāji ir ierakstījušies"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Startēšanas opcijas"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (attālināts)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsole)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instalēt"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalēt paplašinājumu"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Lejupielādēt un instalēt “%s” no extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Ir pieejami atjauninājumi paplašinājumiem"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Paplašinājumu atjauninājumi ir gatavi instalēšanai."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Atļaut nomācošās saīsnes"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Lietotne %s vēlas nomākt saīsnes"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Lietotne vēlas nomākt saīsnes"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Varat atjaunot saīsnes, nospiežot %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Liegt"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Atļaut"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Lēnie taustiņi ieslēgti"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Lēnie taustiņi izslēgti"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Jūs nupat noturējāt Shift taustiņu piespiestu vairāk kā 8 sekundes. Tā ir "
+"tastatūras lēno taustiņu funkcijas saīsne, kas ietekmē to, kā darbojas jūsu "
+"tastatūra."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Lipīgie taustiņi ieslēgti"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Lipīgie taustiņi izslēgti"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Jūs tikko piespiedāt Shift taustiņu 5 reizes pēc kārtas. Tā ir tastatūras "
+"Lipīgo taustiņu funkcijas saīsne, kas ietekmē to kā darbojas jūsu tastatūra."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Jūs tikko vienlaicīgi piespiedāt divus taustiņus vai Shift taustiņu 5 reizes "
+"pēc kārtas. Tas atslēdz tastatūras lipīgo taustiņu funkciju, kas ietekmē to "
+"kā darbojas jūsu tastatūra."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Atstāt ieslēgtu"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Ieslēgt"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Izslēgt"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Atstāt izslēgtu"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Vietas un valodas iestatījumi"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nav instalētu paplašinājumu"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nav izdevis nevienu kļūdu."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Slēpt kļūdas"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Rādīt kļūdas"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Aktivēta"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Deaktivēta"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Kļūda"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Nav aktuāls"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Lejupielādē"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Skatīt avotu"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Tīmekļa lapa"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Sistēma ir ielikta nedrošajā režīmā"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Lietotnēm tagad ir neierobežota piekļuve"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Atsaukt"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Ierakstījies kā priviliģēts lietotājs"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Drošības apsvērumu dēļ nav ieteicams darbināt sesiju ar priviliģētu "
+"lietotāju. Ja iespējams, jums vajadzētu ierakstīties kā parastam lietotājam."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Ekrāna bloķēšana ir izslēgta"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Ekrāna bloķēšanai vajag GNOME darbvirsmas pārvaldnieku."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Sistēmas informācija"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Nezināms izpildītājs"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Nezināms nosaukums"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Rakstiet, lai meklētu"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Lietotnes"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Pārskats"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Jauna saīsne…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Definētā lietotne"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Rādīt palīdzību uz ekrāna"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Pārslēgt monitoru"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Piešķirt taustiņsitienu"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Gatavs"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Rediģēt…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nekas"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Spiediet pogu, lai konfigurētu"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Spiediet ESC, lai izietu"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Spiediet jebkuru taustiņu, lai izietu"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Aktivitātes"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistēma"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Augšējā josla"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Izpildīt komandu"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Spiediet ESC, lai aizvērtu"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Uz Wayland pārstartēšana nav pieejama"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Pārstartē…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME vajag bloķēt ekrānu"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Nevar bloķēt"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Lietotne neļāva bloķēt"
+
+#: js/ui/screenshot.js:1149
+msgid "Selection"
+msgstr "Atlase"
+
+#: js/ui/screenshot.js:1159
+msgid "Area Selection"
+msgstr "Laukuma atlase"
+
+#: js/ui/screenshot.js:1164
+msgid "Screen"
+msgstr "Ekrāns"
+
+#: js/ui/screenshot.js:1174
+msgid "Screen Selection"
+msgstr "Ekrāna atlase"
+
+#: js/ui/screenshot.js:1179
+msgid "Window"
+msgstr "Logs"
+
+#: js/ui/screenshot.js:1189
+msgid "Window Selection"
+msgstr "Loga atlase"
+
+#: js/ui/screenshot.js:1227
+msgid "Screenshot / Screencast"
+msgstr "Ekrānattēls / ekrānieraksts"
+
+#: js/ui/screenshot.js:1263
+msgid "Show Pointer"
+msgstr "Rādīt rādītāju"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1840
+msgid "Screencasts"
+msgstr "Ekrānieraksti"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1845
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Ekrānieraksts %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1911 js/ui/screenshot.js:2123
+msgid "Screenshot"
+msgstr "Ekrānattēls"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1917
+msgid "Screencast recorded"
+msgstr "Ekrānieraksts pabeigs"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1919
+msgid "Click here to view the video."
+msgstr "Spiediet šeit, lai skatītu video."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1922 js/ui/screenshot.js:2137
+msgid "Show in Files"
+msgstr "Rādīt Datņu lietotnē"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2083
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Ekrānattēls no %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2129
+msgid "Screenshot captured"
+msgstr "Ekrānattēls uzņemts"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2131
+msgid "You can paste the image from the clipboard."
+msgstr "Jūs varat ielīmēt attēlu no starpliktuves."
+
+#: js/ui/screenshot.js:2184 js/ui/screenshot.js:2349
+msgid "Screenshot taken"
+msgstr "Ekrānattēls uzņemts"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Meklē…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Nav rezultātu."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d vairāk"
+msgstr[1] "%d vairāk"
+msgstr[2] "%d vairāk"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Meklēt"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopēt"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Ielīmēt"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Rādīt tekstu"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Slēpt tekstu"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Ieslēgts Caps Lock."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Slēpts sējums"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows sistēmas sējums"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Izmanto atslēgdatnes"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Lai atbloķētu sējumu, kas izmanto atslēgdatnes, izmantojiet <i>%s</i> "
+"utilītprogrammu."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM numurs"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Atcerēties paroli"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Atbloķēt"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Atvērt %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM ir jābūt skaitlim vai ari tukšam laukam."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Nevar palaist %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nevarēja atrast %s lietotni"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Pieejamība"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Augsts kontrasts"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Palielinājums"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Ekrāna lasītājs"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Ekrāna tastatūra"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Vizuālie brīdinājumi"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Lipīgie taustiņi"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Lēnie taustiņi"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Atlecošie taustiņi"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Peles taustiņi"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Liels teksts"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automātiskā pagriešana"
+
+#: js/ui/status/bluetooth.js:160
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Gaišums"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Tumšais režīms"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Viens klikšķis"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dubultklikšķis"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Vilkt"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Sekundārais klikšķis"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Apstājas klikšķis"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Tastatūra"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Rādīt tastatūras izkārtojumu"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Atļaut piekļuvi atrašanās vietai"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Lietotne %s vēlas pieeju jūsu atrašanās vietai"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Pieeju atrašanās vietai var mainīt jebkurā brīdī privātuma iestatījumos."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<nezināms>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+#| msgid "%s Disconnecting"
+msgid "Disconnect %s"
+msgstr "Atvienot %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+#| msgid "Connect"
+msgid "Connect to %s"
+msgstr "Savienoties ar %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+#| msgid "%s Hotspot Active"
+msgid "%s Hotspot"
+msgstr "%s tīklājs"
+
+#: js/ui/status/nightLight.js:20
+#| msgid "Night Light On"
+msgid "Night Light"
+msgstr "Nakts gaisma"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Veiktspēja"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Līdzsvarota"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Taupīga"
+
+#: js/ui/status/powerProfiles.js:68
+#| msgctxt "search-result"
+#| msgid "Power Off"
+msgid "Power Profiles"
+msgstr "Jaudas profili"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Apturēt ekrānierakstu"
+
+#: js/ui/status/remoteAccess.js:144
+#| msgid "Stop Screencast"
+msgid "Stop Screen Sharing"
+msgstr "Apturēt ekrāna koplietošanu"
+
+#: js/ui/status/rfkill.js:96
+#| msgid "Airplane Mode On"
+msgid "Airplane Mode"
+msgstr "Lidmašīnas režīms"
+
+#: js/ui/status/system.js:89
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/system.js:140
+#| msgctxt "search-result"
+#| msgid "Power Off"
+msgid "Power Off Menu"
+msgstr "Izslēgšana izvēlne"
+
+#: js/ui/status/system.js:148
+msgid "Suspend"
+msgstr "Iesnaudināt"
+
+#: js/ui/status/system.js:153
+msgid "Restart…"
+msgstr "Pārstartēt…"
+
+#: js/ui/status/system.js:158
+msgid "Power Off…"
+msgstr "Izslēgt…"
+
+#: js/ui/status/system.js:165
+msgid "Log Out"
+msgstr "Izrakstīties"
+
+#: js/ui/status/system.js:170
+msgid "Switch User…"
+msgstr "Mainīt lietotāju…"
+
+#: js/ui/status/system.js:216
+#| msgctxt "search-result"
+#| msgid "Lock Screen"
+msgid "Lock Screen"
+msgstr "Bloķēt ekrānu"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Nezināma Thunderbolt ierīce"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Kamēr nebijāt klāt, tika atklāta jauna ierīce. Lūdzu, atvienojiet un no "
+"jauna pievienojiet to ierīci, lai sāktu to izmantot."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Nepilnvarota Thunderbolt ierīce"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Ir atklāta jauna ierīce un to administratoram tā ir jāpilnvaro."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt pilnvarošanas kļūda"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nevarēja autorizēt Thunderbolt ierīci: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Skaļums ir mainīts"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Skaļums"
+
+#: js/ui/status/volume.js:269
+#| msgid "%u Output"
+#| msgid_plural "%u Outputs"
+msgid "Sound Output"
+msgstr "Skaņas izvade"
+
+#: js/ui/status/volume.js:337
+#| msgid "%u Input"
+#| msgid_plural "%u Inputs"
+msgid "Sound Input"
+msgstr "Skaņas ievade"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Dublēt"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Savienot displejus"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Tikai ārējais"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Tikai iebūvētais"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Velciet augšup, lai atbloķētu"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Klikšķiniet vai nospiediet kādu taustiņu, lai atbloķētu"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Atbloķēšanas logs"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Ierakstīties kā citam lietotājam"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Laipni lūgti GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Ja vēlaties uzzināt, kas kur atrodas, paņemiet ceļvedi."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nē, paldies"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Ņemt ceļvedi"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” ir gatavs"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Paturēt šos displeja iestatījumus?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Atjaunot iestatījumus"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Paturēt izmaiņas"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Izmaiņas tiks atgrieztas pēc %d sekundes"
+msgstr[1] "Izmaiņas tiks atgrieztas pēc %d sekundēm"
+msgstr[2] "Izmaiņas tiks atgrieztas pēc %d sekundēm"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Uzņemt ekrānattēlu"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Slēpt"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Atjaunot"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maksimizēt"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Pārvietot"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Mainīt izmēru"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Pārvietot virsraksta joslu uz ekrāna"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Vienmēr virspusē"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Vienmēr redzamajā darbvietā"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Pārvietot uz darbvietu pa kreisi"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Pārvietot uz darbvietu pa labi"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Pārvietot uz augstāku darbvietu"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Pārvietot uz zemāku darbvietu"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Pārvietot uz augstāku monitoru"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Pārvietot uz zemāku monitoru"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Pārvietot uz monitoru pa kreisi"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Pārvietot uz monitoru pa labi"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Aizvērt"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution kalendārs"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Drukāt versiju"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Režīms, ko izmantot GDM ierakstīšanās ekrānam"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Izmantot specifisku metodi, piemēram, “gdm” ierakstīšanās ekrānam"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Uzskaitīt iespējamos režīmus"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Nezināma"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Neizdevās palaist “%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Paroles nesakrīt."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Parole nevar būt tukša"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Lietotājs noraidīja autentifikācijas dialoglodziņu"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Paplašinājumi"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Pārvaldiet savus GNOME paplašinājumus"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "\"GNOME projekts\""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"“GNOME paplašinājumi” nodrošina paplašinājumu atjaunināšanu un "
+"konfigurēšanu, kā arī nevajadzīgo paplašinājumu izslēgšanu vai izņemšanu."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfigurēt GNOME Shell paplašinājumus"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Nekas neatbilst"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Izņemt “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Ja izņemsiet paplašinājumu, tad to uzlikt varēs tikai pēc tā lejupielādēšanas"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Izņemt"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>\n"
+"Vika Leimane <vika.leimane@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Nākamajā ierakstīšanās reizē tiks atjaunināts %d paplašinājums."
+msgstr[1] "Nākamajā ierakstīšanās reizē tiks atjaunināts %d paplašinājumi."
+msgstr[2] "Nākamajā ierakstīšanās reizē tiks atjaunināts %d paplašinājumu."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Paplašinājums neatbalsta šo GNOME versiju"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Paplašinājumam gadījās kļūda"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Paplašinājumu var atjaunināt"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Tīmekļa vietne"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Izņemt…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Palīdzība"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Par paplašinājumiem"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Paplašinājumi var radīt problēmas ar veiktspēju un stabilitāti. Ja jūsu "
+"sistēmā rodas problēmas, izslēdziet paplašinājumus."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Pašrocīgi instalētie"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Lai atrastu un pievienotu paplašinājumus, apmeklējiet <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Iebūvētie"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Nav instalētu paplašinājumu"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Diemžēl nevar saņemt sarakstu ar instalētajiem paplašinājumiem. "
+"Pārliecinieties, ka esat ierakstījušies GNOME sesijā un mēģiniet vēlreiz."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Paplašinājumu atjauninājumi ir gatavi"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Izrakstīties…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Jaunais paplašinājumus tika veiksmīgi izveidots mapē %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Nosaukumam ir jābūt ļoti īsam (vēlams aprakstošam).\n"
+"Piemēri: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nosaukums"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Apraksts ir viena teikuma garš teksts, kas izskaidro, ko dara "
+"paplašinājums.\n"
+"Piemēri: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Apraksts"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID ir globāli unikāls identifikators paplašinājumam\n"
+"Tas var būt e-pasta adreses formā (annavanna@extensions.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Izvēlieties vienu no pieejamajām veidnēm\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Veidne"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Jaunā paplašinājuma unikālais identifikators"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOSAUKUMS"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Lietotājam redzamais jaunā paplašinājums nosaukums"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "APRAKSTS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Īss teksts, kas apraksta, ko dara paplašinājumus"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "VEIDNE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Lietotājam redzamais jaunā paplašinājuma nosaukums"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Interaktīvi ievadiet informāciju par paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Izveidot jaunu paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Nezināmi argumenti"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Nepieciešams UUID, nosaukums un apraksts"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Neizdevās savienoties ar GNOME čaulu\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Paplašinājums \"%s\" neeksistē\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Izslēgt paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nav norādīts UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Ir norādīts vairāk kā viens UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Ieslēgt paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Paplašinājums \"%s\" neeksistē\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Rādīt informāciju par paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Pārrakstīt esošo paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PAPLAŠINĀJUMA_SAINIS"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instalēt paplašinājuma saini"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nav norādīt paplašinājuma sainis"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Ir norādīts vairāk kā viens paplašinājuma sainis"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Rādīt lietotāja instalētus paplašinājumus"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Rādīt sistēmas instalētus paplašinājumus"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Rādīt ieslēgtos paplašinājumus"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Rādīt izslēgtos paplašinājumus"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Rādīt paplašinājumus ar iestatījumiem"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Rādīt paplašinājumus ar atjauninājumiem"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Izdrukāt informāciju par paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Rādīt instalētos paplašinājumus"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "DATNE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Papildu avots, ko iekļaut šajā sainī"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SHĒMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "GSettings shēma, kas ir jāiekļauj"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIREKTORIJA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Direktorija, kurā ir tulkojumi"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMĒNS"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Tulkošanai izmantojamais gettext domēns"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Pārrakstīt esošo paku"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Direktorija, kurā jāizveido paka"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "AVOTA_DIREKTORIJA"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Izveidot paplašinājuma saini"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Ir norādīta vairāk kā viena avota direktorija"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Paplašinājumam “%s” nav iestatījumi\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Neizdevās atvērt paplašinājuma “%s” iestatījumus: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Atver paplašinājuma iestatījumus"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Pārstatīt paplašinājumu"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Nevar atinstalēt sistēmas paplašinājumus\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Neizdevās atinstalēt “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Atinstalēt paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Nedrukāt kļūdu paziņojumus"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Ceļš"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Sākotnējais autors"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versija"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stāvoklis"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” nepieņem argumentus"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Lietošana:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Parādīt informāciju par versiju un iziet."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "KOMANDA"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARG…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Komandas:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Drukāt palīdzību"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Ieslēgt paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Izslēgt paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Pārstatīt paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Atinstalēt paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Uzskaitīt paplašinājumus"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Rādīt informāciju par paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Atvērt paplašinājuma iestatījumus"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Izveidot paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Pakot paplašinājumu"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instalēt paplašinājuma saini"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Lietojiet “%s”, lai iegūtu detalizētu palīdzību.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Vienkāršs"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Tukšs paplašinājums"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikators"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Pievienot ikonu augšējā joslā"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u izvadkanāls"
+msgstr[1] "%u izvadkanāli"
+msgstr[2] "%u izvadkanāli"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ievadkanāls"
+msgstr[1] "%u ievadkanāli"
+msgstr[2] "%u ievadkanāli"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sistēmas skaņas"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Bluetooth iestatījumi"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d savienots"
+#~ msgstr[1] "%d savienoti"
+#~ msgstr[2] "%d savienotu"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth ieslēgts"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth izslēgts"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Atrašanās vieta ieslēgta"
+
+#~ msgid "Disable"
+#~ msgstr "Izslēgt"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Privātuma iestatījumi"
+
+#~ msgid "Location In Use"
+#~ msgstr "Atrašanās vieta tiek izmantota"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Atrašanās vieta ir izslēgta"
+
+#~ msgid "Enable"
+#~ msgstr "Ieslēgt"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s ir izslēgta"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s ir savienots"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s nav pārvaldīts"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s savienojas"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s pieprasa autentificēšanos"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s trūkst aparātprogrammatūras"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s nav pieejams"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s neizdevās savienoties"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Vadu tīkla iestatījumi"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Mobilās platjoslas iestatījumi"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s ir izslēgts aparatūrā"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s ir izslēgts"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Savienoties ar internetu"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Lidmašīnas režīms ir ieslēgts"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Wi-Fi tiek izslēgts, kad ir ieslēgts lidmašīnas režīms."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Izslēgt lidmašīnas režīmu"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi ir izslēgts"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Lai savienotos ar tīklu, jāieslēdz Wi-Fi."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Ieslēgt Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Bezvadu tīkli"
+
+#~ msgid "Select a network"
+#~ msgstr "Izvēlieties tīklu"
+
+#~ msgid "No Networks"
+#~ msgstr "Nav tīklu"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Lai izslēgtu, izmantot aparatūras slēdzi"
+
+#~ msgid "Select Network"
+#~ msgstr "Izvēlieties tīklu"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wi-Fi iestatījumi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s nav savienots"
+
+#~ msgid "connecting…"
+#~ msgstr "savienojas…"
+
+#~ msgid "authentication required"
+#~ msgstr "nepieciešama autentifikācija"
+
+#~ msgid "connection failed"
+#~ msgstr "savienojums neizdevās"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN iestatījumi"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN ir izslēgts"
+
+#~ msgid "Network Settings"
+#~ msgstr "Tīkla iestatījumi"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s vadu savienojums"
+#~ msgstr[1] "%s vadu savienojumi"
+#~ msgstr[2] "%s vadu savienojumu"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Wi-Fi savienojums"
+#~ msgstr[1] "%s Wi-Fi savienojumi"
+#~ msgstr[2] "%s Wi-Fi savienojumu"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s modema savienojums"
+#~ msgstr[1] "%s modema savienojumi"
+#~ msgstr[2] "%s modema savienojumu"
+
+#~ msgid "Connection failed"
+#~ msgstr "Neizdevās savienoties"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Neizdevās tīkla savienojuma aktivēšana"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Nakts gaisma ir izslēgta"
+
+#~ msgid "Resume"
+#~ msgstr "Turpināt"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Izslēgt līdz rītdienai"
+
+#~ msgid "Power Settings"
+#~ msgstr "Barošanas iestatījumi"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Pilnībā uzlādēta"
+
+#~ msgid "Not Charging"
+#~ msgstr "Nelādējas"
+
+#~ msgid "Estimating…"
+#~ msgstr "Novērtē…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "Atlicis %d.%02d (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "Līdz pilnai %d.%02d (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Ekrāns tiek koplietots"
+
+#~ msgid "Turn off"
+#~ msgstr "Izslēgt"
+
+#~ msgid "Lock"
+#~ msgstr "Bloķēt"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Izslēgt / izrakstīties"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Izņemt no izlases"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Pievienot izlasei"
+
+#~ msgid "Author"
+#~ msgstr "Autors"
+
+#~ msgid "Warning"
+#~ msgstr "Brīdinājums"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Paplašinājumi var radīt problēmas, piemēram, ar veiktspēju. Ja jūsu "
+#~ "sistēmā rodas problēmas, vēlams izslēgt visus paplašinājumus."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Neizdevās savienoties ar GNOME čaulu"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Ieslēgt introspekcijas API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr "Ieslēdz D-Bus API, kas ļauj izpētīt lietotnes stāvokli."
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizēt"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Atjaunot"
+
+#~ msgid "App Picker View"
+#~ msgstr "Lietotņu izvēles skats"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Pašlaik izvēlētā skata rādītājs lietotņu izvēlē."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Šeit parādīsies biežāk izmantotās lietotnes"
+
+#~ msgid "Frequent"
+#~ msgstr "Biežāk izmantotās"
+
+#~ msgid "All"
+#~ msgstr "Visas"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d. %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d. %B, %Y."
+
+#~ msgid "Off"
+#~ msgstr "Izslēgts"
+
+#~ msgid "On"
+#~ msgstr "Ieslēgts"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopēšanas kļūda"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Pārlūkot “Programmatūrā”"
+
+#~ msgid "Next"
+#~ msgstr "Nākamais"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Ierakstīties"
+
+#~ msgid "Password:"
+#~ msgstr "Parole:"
+
+#~ msgid "Type again:"
+#~ msgstr "Ierakstiet vēlreiz:"
+
+#~ msgid "Password: "
+#~ msgstr "Parole: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Bezvadu tīkls pieprasa autentifikāciju"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Mobilās platjoslas tīkla parole"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d. %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d jauns ziņojums"
+#~ msgstr[1] "%d jauni ziņojumi"
+#~ msgstr[2] "%d jaunu ziņojumu"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d jauns paziņojums"
+#~ msgstr[1] "%d jauni paziņojumi"
+#~ msgstr[2] "%d jaunu paziņojumu"
+
+#~ msgid "Account Settings"
+#~ msgstr "Konta iestatījumi"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Orientācijas slēdzis"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Taustiņu sasaiste, kas pauzē un turpina visus tvīnus (tweens) "
+#~ "atkļūdošanas nolūkiem"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Kuru tastatūru izmantot"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Izmantojamās tastatūras tips."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Ielādējot %s iestatījumu dialogu, gadījās kļūda:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s visu dienu."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, pēc tam %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, pēc tam %s, vēl pēc tam %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Komforta temperatūra %s."
diff --git a/po/meson.build b/po/meson.build
new file mode 100644
index 0000000..e9b77d7
--- /dev/null
+++ b/po/meson.build
@@ -0,0 +1 @@
+i18n.gettext(meson.project_name(), preset: 'glib')
diff --git a/po/mjw.po b/po/mjw.po
new file mode 100644
index 0000000..708656d
--- /dev/null
+++ b/po/mjw.po
@@ -0,0 +1,2758 @@
+# Karbi translation for gnome-shell.
+# Copyright (C) 2019 nautilus-shell's The Gnome Foundation.
+# This file is distributed under the same license as the gnome-shell package.
+# Jor Teron <jor.teron@gmail.com>, 2019-20.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2020-02-22 15:36+0000\n"
+"PO-Revision-Date: 2020-02-22 21:44+0530\n"
+"Last-Translator: Jor Teron <jor.teron@gmail.com>\n"
+"Language-Team: Karbi <karbi.translation@gmail.com>\n"
+"Language: mjw\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator:Gedit \n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "System"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Notification list ke paklang"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Non-het notification ke paklang"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Kado-kawe"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Kado-kawe applications ke-paklang"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Application ason-amung kangpu"
+
+#: data/org.gnome.Extensions.desktop.in.in:4 js/extensionPrefs/main.js:218
+#: js/extensionPrefs/ui/extensions-window.ui:61
+msgid "Extensions"
+msgstr "Extensions"
+
+#: data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr ""
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "App Picker View"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:69
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:74
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:78
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "“Log out” menu user menu along do vek nangji."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:87
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:97
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:106
+msgid "Enable introspection API"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:107
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:119
+msgid "Keybinding to open the application menu"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:120
+msgid "Keybinding to open the application menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:126
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:127
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid "Keybinding to open the overview"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:135
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:141
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to focus the active notification"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to focus the active notification."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:155
+msgid "Switch to application 1"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Switch to application 2"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Switch to application 3"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:167
+msgid "Switch to application 4"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid "Switch to application 5"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:175
+msgid "Switch to application 6"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Switch to application 7"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:183
+msgid "Switch to application 8"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Switch to application 9"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:196
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Limit switcher to current workspace."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:197
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "The application icon mode."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Locations"
+msgstr "Dim"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "The locations to show in world clocks"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:245
+msgid "Automatic location"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Whether to fetch the current location or not"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:253
+msgid "Location"
+msgstr "Dim"
+
+#: data/org.gnome.shell.gschema.xml.in:254
+msgid "The location for which to show a forecast"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:267
+#: data/org.gnome.shell.gschema.xml.in:276
+#: data/org.gnome.shell.gschema.xml.in:284
+#: data/org.gnome.shell.gschema.xml.in:292
+#: data/org.gnome.shell.gschema.xml.in:300
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:291
+msgid "Workspaces only on primary monitor"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:299
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Network Login"
+
+#: js/extensionPrefs/main.js:140
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr ""
+
+#: js/extensionPrefs/main.js:141
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: js/extensionPrefs/main.js:144 js/ui/audioDeviceSelection.js:57
+#: js/ui/components/networkAgent.js:107 js/ui/components/polkitAgent.js:139
+#: js/ui/endSessionDialog.js:374 js/ui/extensionDownloader.js:165
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:913
+msgid "Cancel"
+msgstr "Kethan"
+
+#: js/extensionPrefs/main.js:145
+msgid "Remove"
+msgstr ""
+
+#: js/extensionPrefs/main.js:217
+msgid "translator-credits"
+msgstr ""
+
+#: js/extensionPrefs/main.js:219
+msgid "Manage your GNOME Extensions"
+msgstr ""
+
+#: js/extensionPrefs/main.js:261 js/extensionPrefs/ui/extensions-window.ui:222
+msgid "Something’s gone wrong"
+msgstr "Kopine chokche det"
+
+#: js/extensionPrefs/main.js:268
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/extensionPrefs/main.js:275
+msgid "Technical Details"
+msgstr ""
+
+#: js/extensionPrefs/main.js:310
+msgid "Copy Error"
+msgstr ""
+
+#: js/extensionPrefs/main.js:337
+msgid "Homepage"
+msgstr "Homepage"
+
+#: js/extensionPrefs/main.js:338
+msgid "Visit extension homepage"
+msgstr "Extensions homepage lang rei tha"
+
+#: js/extensionPrefs/main.js:449
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/extensionPrefs/ui/extension-row.ui:100
+#: subprojects/extensions-tool/src/command-create.c:211
+#: subprojects/extensions-tool/src/main.c:173
+msgid "Description"
+msgstr ""
+
+#: js/extensionPrefs/ui/extension-row.ui:123
+#: subprojects/extensions-tool/src/main.c:185
+msgid "Version"
+msgstr ""
+
+#: js/extensionPrefs/ui/extension-row.ui:151
+msgid "Author"
+msgstr ""
+
+#: js/extensionPrefs/ui/extension-row.ui:175
+msgid "Website"
+msgstr ""
+
+#: js/extensionPrefs/ui/extension-row.ui:192
+msgid "Remove…"
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:8
+msgid "Help"
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:12
+msgid "About Extensions"
+msgstr "Extensions Aputhak"
+
+#: js/extensionPrefs/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:35
+msgid "Warning"
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:46
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:133
+msgid "Manually Installed"
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:157
+msgid "Built-In"
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:198
+msgid "No Installed Extensions"
+msgstr "Extension thapthe lang"
+
+#: js/extensionPrefs/ui/extensions-window.ui:234
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: js/extensionPrefs/ui/extensions-window.ui:287
+msgid "Log Out…"
+msgstr "Hong ke-tet…"
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:236 js/ui/components/networkAgent.js:202
+#: js/ui/components/networkAgent.js:218 js/ui/components/networkAgent.js:242
+#: js/ui/components/networkAgent.js:263 js/ui/components/networkAgent.js:283
+#: js/ui/components/networkAgent.js:293 js/ui/components/polkitAgent.js:277
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "Password"
+
+#: js/gdm/loginDialog.js:318
+msgid "Choose Session"
+msgstr "Session ingvai nangji"
+
+#: js/gdm/loginDialog.js:457
+msgid "Not listed?"
+msgstr "List long awe?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:912
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(e.g., user mate %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:917 js/ui/components/networkAgent.js:238
+#: js/ui/components/networkAgent.js:261 js/ui/components/networkAgent.js:279
+msgid "Username"
+msgstr "Username"
+
+#: js/gdm/loginDialog.js:1253
+msgid "Login Window"
+msgstr "Login Window"
+
+#: js/gdm/util.js:338
+msgid "Authentication error"
+msgstr "Hovang chokche"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr ""
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:89
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Power Off"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:92
+msgid "power off;shutdown;reboot;restart;halt;stop"
+msgstr "power off;shutdown;reboot;restart;halt;stop"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:97
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Screen kanghap"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:100
+msgid "lock screen"
+msgstr "Screen kanghap"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:105
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Log Out"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:108
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:113
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspend"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:116
+msgid "suspend;sleep"
+msgstr "suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:121
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Monit chelar"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:124
+msgid "switch user"
+msgstr "monit chelar"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:131
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "lock orientation;unlock orientation;screen;rotation"
+
+#: js/misc/systemActions.js:251
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Unlock Screen Rotation"
+
+#: js/misc/systemActions.js:252
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Lock Screen Rotation"
+
+#: js/misc/util.js:120
+msgid "Command not found"
+msgstr "Command longle"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:156
+msgid "Could not parse command:"
+msgstr "Command chokche:"
+
+#: js/misc/util.js:164
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr ""
+
+#: js/misc/util.js:181
+msgid "Just now"
+msgstr "Non het"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minute aphrang"
+msgstr[1] "%d minute aphrang"
+
+#: js/misc/util.js:187
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Porphai %d aphrang"
+msgstr[1] "Porphai %d aphrang"
+
+#: js/misc/util.js:191
+msgid "Yesterday"
+msgstr "Tumi"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Ni %d aphrang"
+msgstr[1] "Ni %d aphrang"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Rui %d aphrang"
+msgstr[1] "Rui %d aphrang"
+
+#: js/misc/util.js:201
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Cheklo %d aphrang"
+msgstr[1] "Cheklo %d aphrang"
+
+#: js/misc/util.js:204
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Ningkan %d aphrang"
+msgstr[1] "Ningkan %d aphrang"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:237
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:243
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Tumi, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:249
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:255
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:261
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d %B, %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:266
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:272
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Tumi, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:278
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:284
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:290
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d %B, %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:41
+msgid "Hotspot Login"
+msgstr "Hotspot Login"
+
+#: js/portalHelper/main.js:87
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:374
+msgid "Deny Access"
+msgstr "Hovang Longle"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:377
+msgid "Grant Access"
+msgstr "Hovang Long"
+
+#: js/ui/appDisplay.js:906
+msgid "Unnamed Folder"
+msgstr ""
+
+#: js/ui/appDisplay.js:929
+msgid "Frequently used applications will appear here"
+msgstr "ke lang ver applications la dak klang ik po"
+
+#: js/ui/appDisplay.js:1064
+msgid "Frequent"
+msgstr ""
+
+#: js/ui/appDisplay.js:1071
+msgid "All"
+msgstr "Kado-kawe"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:2450 js/ui/panel.js:75
+msgid "Open Windows"
+msgstr "Windows kangpu"
+
+#: js/ui/appDisplay.js:2470 js/ui/panel.js:82
+msgid "New Window"
+msgstr "Windows kimi"
+
+#: js/ui/appDisplay.js:2481
+msgid "Launch using Dedicated Graphics Card"
+msgstr "Dedicated Graphics Card pen kangpu"
+
+#: js/ui/appDisplay.js:2509 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "Favourites long pen musi noi"
+
+#: js/ui/appDisplay.js:2515
+msgid "Add to Favorites"
+msgstr "Favorites long thap noi"
+
+#: js/ui/appDisplay.js:2525 js/ui/panel.js:93
+msgid "Show Details"
+msgstr "Details ke-lang"
+
+#: js/ui/appFavorites.js:152
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s nangli favourites along thap ed lo."
+
+#: js/ui/appFavorites.js:185
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s nangli favourites pen musi ed lo."
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Audio Device ingvai tha"
+
+#: js/ui/audioDeviceSelection.js:54
+msgid "Sound Settings"
+msgstr "Ase amokha"
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headphones"
+msgstr "Headphones"
+
+#: js/ui/audioDeviceSelection.js:66
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:269
+msgid "Microphone"
+msgstr "Microphone"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Arjan kelar…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Display ason-amung"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "Settings"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:41
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:70
+msgctxt "grid sunday"
+msgid "S"
+msgstr "B"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:72
+msgctxt "grid monday"
+msgid "M"
+msgstr "U"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:74
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:76
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:78
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:80
+msgctxt "grid friday"
+msgid "F"
+msgstr "B"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:82
+msgctxt "grid saturday"
+msgid "S"
+msgstr "B"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:371
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:381
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:440
+msgid "Previous month"
+msgstr "Chu cheklo"
+
+#: js/ui/calendar.js:455
+msgid "Next month"
+msgstr "Dunthu cheklo"
+
+#: js/ui/calendar.js:605
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:661
+msgid "Week %V"
+msgstr "Rui %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:730
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Losoni"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/calendar.js:867
+msgctxt "calendar heading"
+msgid "%A, %B %-d"
+msgstr "%A, %d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/calendar.js:870
+msgctxt "calendar heading"
+msgid "%A, %B %-d, %Y"
+msgstr "%A, %d %B, %Y"
+
+#: js/ui/calendar.js:1096
+msgid "No Notifications"
+msgstr "Notifications Awe"
+
+#: js/ui/calendar.js:1099
+msgid "No Events"
+msgstr "Events awe"
+
+#: js/ui/calendar.js:1153
+msgid "Do Not Disturb"
+msgstr ""
+
+#: js/ui/calendar.js:1167
+msgid "Clear"
+msgstr "Paprei"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” dam pame me."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Nangli inghong chet tame, lason dam bom cheme det tahai kali te application "
+"bonchek noipame phan."
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "Jakong pen ingchir noi"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "Inghong tha"
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr "External drive chepho lo"
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr "External drive chepho phe"
+
+#: js/ui/components/automountManager.js:208
+msgid "Unable to unlock volume"
+msgstr ""
+
+#: js/ui/components/automountManager.js:209
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:333
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s pen kangpu"
+
+#: js/ui/components/networkAgent.js:89
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:101 js/ui/status/network.js:223
+#: js/ui/status/network.js:314 js/ui/status/network.js:916
+msgid "Connect"
+msgstr "Pa-chepho"
+
+#: js/ui/components/networkAgent.js:208
+msgid "Key"
+msgstr "Key"
+
+#: js/ui/components/networkAgent.js:246 js/ui/components/networkAgent.js:269
+msgid "Private key password"
+msgstr "Private key password"
+
+#: js/ui/components/networkAgent.js:267
+msgid "Identity"
+msgstr "Identity"
+
+#: js/ui/components/networkAgent.js:281
+msgid "Service"
+msgstr "Service"
+
+#: js/ui/components/networkAgent.js:310 js/ui/components/networkAgent.js:338
+#: js/ui/components/networkAgent.js:685 js/ui/components/networkAgent.js:706
+msgid "Authentication required"
+msgstr "Hovang nangji"
+
+#: js/ui/components/networkAgent.js:311 js/ui/components/networkAgent.js:686
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:315 js/ui/components/networkAgent.js:690
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:317
+msgid "Network name"
+msgstr "Network amen"
+
+#: js/ui/components/networkAgent.js:322 js/ui/components/networkAgent.js:694
+msgid "DSL authentication"
+msgstr "DSL hovang"
+
+#: js/ui/components/networkAgent.js:329 js/ui/components/networkAgent.js:699
+msgid "PIN code required"
+msgstr "PIN code nangji"
+
+#: js/ui/components/networkAgent.js:330 js/ui/components/networkAgent.js:700
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN code nangji sepalar (mobile) hormu phan"
+
+#: js/ui/components/networkAgent.js:331
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:339 js/ui/components/networkAgent.js:691
+#: js/ui/components/networkAgent.js:695 js/ui/components/networkAgent.js:707
+#: js/ui/components/networkAgent.js:711
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” pen chepho ji phan password nangji"
+
+#: js/ui/components/networkAgent.js:674 js/ui/status/network.js:1691
+msgid "Network Manager"
+msgstr "Network Manager"
+
+#: js/ui/components/networkAgent.js:710
+msgid "VPN password"
+msgstr "VPN Password"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "Hovang nangji"
+
+#: js/ui/components/polkitAgent.js:80
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:142
+msgid "Authenticate"
+msgstr ""
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:254 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Diya tha, laso pot damde det. Ephong vi thu ik tha."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:787
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s phan nonke %s si pu"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:177
+msgid "Windows"
+msgstr "Windows"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr "Applications paklang"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr "Adim"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:75
+msgid "%B %-d %Y"
+msgstr "%e %B, %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:82
+msgid "%A %B %e %Y"
+msgstr "%e %B, %Y, %A"
+
+#: js/ui/dateMenu.js:161
+msgid "Add world clocks…"
+msgstr ""
+
+#: js/ui/dateMenu.js:162
+msgid "World Clocks"
+msgstr ""
+
+#: js/ui/dateMenu.js:276
+msgid "Weather"
+msgstr "Botor"
+
+#: js/ui/dateMenu.js:391
+msgid "Select a location…"
+msgstr "Adim chongvai nangji…"
+
+#: js/ui/dateMenu.js:404
+msgid "Loading…"
+msgstr "Wang bom-lo…"
+
+#: js/ui/dateMenu.js:414
+msgid "Go online for weather information"
+msgstr "Online pen botor birta kelang"
+
+#: js/ui/dateMenu.js:416
+msgid "Weather information is currently unavailable"
+msgstr "Botor birta non ko awe"
+
+#: js/ui/endSessionDialog.js:37
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s phan hong ke-wan"
+
+#: js/ui/endSessionDialog.js:38
+msgctxt "title"
+msgid "Log Out"
+msgstr "Hong wang"
+
+#: js/ui/endSessionDialog.js:40
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s phan hong pa-tet po non %d second lote."
+msgstr[1] "%s phan hong pa-tet po non %d seconds lote."
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Nangli phan hong pa-tet po non %d second lote."
+msgstr[1] "Nangli phan hong pa-tet po non %d seconds lote."
+
+#: js/ui/endSessionDialog.js:51
+msgctxt "button"
+msgid "Log Out"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "title"
+msgid "Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:57
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Updates thapdet aphi Power Off noi"
+
+#: js/ui/endSessionDialog.js:59
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Computer amethang chebon po non %d second lote."
+msgstr[1] "Computer amethang chebon po non %d seconds lote."
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Pending software updates thap tha"
+
+#: js/ui/endSessionDialog.js:66 js/ui/endSessionDialog.js:82
+msgctxt "button"
+msgid "Restart"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:68
+msgctxt "button"
+msgid "Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "title"
+msgid "Restart"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:76
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Computer amethang restart po non %d second lote."
+msgstr[1] "Computer amethang restart po non %d seconds lote."
+
+#: js/ui/endSessionDialog.js:89
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "restart si Updates thap noi"
+
+#: js/ui/endSessionDialog.js:91
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:97 js/ui/endSessionDialog.js:116
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Restart si Install noi"
+
+#: js/ui/endSessionDialog.js:98
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Thap det si Power Off noi"
+
+#: js/ui/endSessionDialog.js:99
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Updates det bon-chek noi"
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Restart aphi Upgrade thap noi"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:111
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:259
+msgid "Running on battery power: Please plug in before installing updates."
+msgstr "Battery pensi dambom: choningri updates de aprang charge ik noi."
+
+#: js/ui/endSessionDialog.js:268
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:273
+msgid "Other users are logged in"
+msgstr "Kaprek monit logged dolang."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:588
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remote)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:591
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (console)"
+
+#: js/ui/extensionDownloader.js:169
+msgid "Install"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:175
+msgid "Install Extension"
+msgstr "Extension kethap"
+
+#: js/ui/extensionDownloader.js:176
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Download si “%s” thap noi extensions.gnome.org long pen?"
+
+#: js/ui/extensionSystem.js:228
+msgid "Extension Updates Available"
+msgstr "Extensions Updates dolo"
+
+#: js/ui/extensionSystem.js:229
+msgid "Extension updates are ready to be installed."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr ""
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:98
+msgid "Deny"
+msgstr "Longle"
+
+#: js/ui/inhibitShortcutsDialog.js:105
+msgid "Allow"
+msgstr "Long"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:42
+msgid "Sticky Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:44
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:46
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:135
+#: js/ui/status/network.js:1288
+msgid "Turn On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:63 js/ui/status/bluetooth.js:135
+#: js/ui/status/network.js:131 js/ui/status/network.js:315
+#: js/ui/status/network.js:1288 js/ui/status/network.js:1400
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:108
+msgid "Turn Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:63
+msgid "Leave Off"
+msgstr ""
+
+#: js/ui/keyboard.js:207
+msgid "Region & Language Settings"
+msgstr ""
+
+#: js/ui/lookingGlass.js:665
+msgid "No extensions installed"
+msgstr ""
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:720
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: js/ui/lookingGlass.js:726
+msgid "Hide Errors"
+msgstr "Errors matu"
+
+#: js/ui/lookingGlass.js:730 js/ui/lookingGlass.js:795
+msgid "Show Errors"
+msgstr "Errors paklang"
+
+#: js/ui/lookingGlass.js:739
+msgid "Enabled"
+msgstr ""
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:742 subprojects/gvc/gvc-mixer-control.c:1892
+msgid "Disabled"
+msgstr ""
+
+#: js/ui/lookingGlass.js:744
+msgid "Error"
+msgstr "Chokche"
+
+#: js/ui/lookingGlass.js:746
+msgid "Out of date"
+msgstr "Arni chokche"
+
+#: js/ui/lookingGlass.js:748
+msgid "Downloading"
+msgstr "San bomlo"
+
+#: js/ui/lookingGlass.js:777
+msgid "View Source"
+msgstr ""
+
+#: js/ui/lookingGlass.js:786
+msgid "Web Page"
+msgstr "Web aloh"
+
+#: js/ui/main.js:269
+msgid "Logged in as a privileged user"
+msgstr ""
+
+#: js/ui/main.js:270
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+
+#: js/ui/main.js:276
+msgid "Screen Lock disabled"
+msgstr ""
+
+#: js/ui/main.js:277
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+
+#: js/ui/messageTray.js:1554
+msgid "System Information"
+msgstr ""
+
+#: js/ui/mpris.js:199
+msgid "Unknown artist"
+msgstr ""
+
+#: js/ui/mpris.js:209
+msgid "Unknown title"
+msgstr "Amen chinine"
+
+#: js/ui/overview.js:73
+msgid "Undo"
+msgstr "Undo"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:86
+msgid "Overview"
+msgstr "Sai"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:107
+msgid "Type to search"
+msgstr "Kiri phan ketok"
+
+#: js/ui/padOsd.js:95
+msgid "New shortcut…"
+msgstr "Shortcut kimi…"
+
+#: js/ui/padOsd.js:142
+msgid "Application defined"
+msgstr ""
+
+#: js/ui/padOsd.js:143
+msgid "Show on-screen help"
+msgstr ""
+
+#: js/ui/padOsd.js:144
+msgid "Switch monitor"
+msgstr "Monitor kelar"
+
+#: js/ui/padOsd.js:145
+msgid "Assign keystroke"
+msgstr ""
+
+#: js/ui/padOsd.js:211
+msgid "Done"
+msgstr "Tanglo"
+
+#: js/ui/padOsd.js:745
+msgid "Edit…"
+msgstr "Edit…"
+
+#: js/ui/padOsd.js:787 js/ui/padOsd.js:910
+msgid "None"
+msgstr "Angse"
+
+#: js/ui/padOsd.js:863
+msgid "Press a button to configure"
+msgstr ""
+
+#: js/ui/padOsd.js:864
+msgid "Press Esc to exit"
+msgstr ""
+
+#: js/ui/padOsd.js:867
+msgid "Press any key to exit"
+msgstr ""
+
+#: js/ui/panel.js:109
+msgid "Quit"
+msgstr ""
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:434
+msgid "Activities"
+msgstr "Sai (WiP)"
+
+#: js/ui/panel.js:707
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr ""
+
+#: js/ui/panel.js:820
+msgid "Top Bar"
+msgstr ""
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Atoi (command) keber"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr ""
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr ""
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr ""
+
+#: js/ui/screenShield.js:203
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME screen lock nangji"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:244 js/ui/screenShield.js:598
+msgid "Unable to lock"
+msgstr "Lock un-eh"
+
+#: js/ui/screenShield.js:245 js/ui/screenShield.js:599
+msgid "Lock was blocked by an application"
+msgstr ""
+
+#: js/ui/search.js:694
+msgid "Searching…"
+msgstr "Ri bomlo…"
+
+#: js/ui/search.js:696
+msgid "No results."
+msgstr "Satlang klang kle."
+
+#: js/ui/search.js:822
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d dolang"
+msgstr[1] "Son %d dolang"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copy"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Paste"
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr "Amek paklang"
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr "Amek matu"
+
+#: js/ui/shellEntry.js:162
+msgid "Caps lock is on."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr "Keyfiles pen"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr "PIM Number"
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "Password ning chibi nangji"
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr "Ingpu-ji"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "%s Kangpu"
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr "PIM ke lakha mate angse nangji"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:469
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s pangcheng un-eh"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:471
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s application longle"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Accessibility"
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr "Pathe-bi"
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr ""
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr ""
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr ""
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:136
+msgid "High Contrast"
+msgstr ""
+
+#: js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr "Kethe amek"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:591
+msgid "Bluetooth Settings"
+msgstr "Bluwtooth amokha"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:129
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d Chepho"
+msgstr[1] "Son %d Chepho"
+
+#: js/ui/status/bluetooth.js:131
+msgid "Off"
+msgstr ""
+
+#: js/ui/status/bluetooth.js:133
+msgid "On"
+msgstr ""
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Atur"
+
+#: js/ui/status/dwellClick.js:13
+msgid "Single Click"
+msgstr "Ephong Click"
+
+#: js/ui/status/dwellClick.js:18
+msgid "Double Click"
+msgstr "Phongni Click"
+
+#: js/ui/status/dwellClick.js:23
+msgid "Drag"
+msgstr "Kesan"
+
+#: js/ui/status/dwellClick.js:28
+msgid "Secondary Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:37
+msgid "Dwell Click"
+msgstr ""
+
+#: js/ui/status/keyboard.js:826
+msgid "Keyboard"
+msgstr "Keyboard"
+
+#: js/ui/status/keyboard.js:848
+msgid "Show Keyboard Layout"
+msgstr "Keyboard Layout paklang"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:174
+msgid "Location Enabled"
+msgstr ""
+
+#: js/ui/status/location.js:66 js/ui/status/location.js:175
+msgid "Disable"
+msgstr ""
+
+#: js/ui/status/location.js:67
+msgid "Privacy Settings"
+msgstr "Privacy amokha"
+
+#: js/ui/status/location.js:173
+msgid "Location In Use"
+msgstr ""
+
+#: js/ui/status/location.js:177
+msgid "Location Disabled"
+msgstr ""
+
+#: js/ui/status/location.js:178
+msgid "Enable"
+msgstr ""
+
+#: js/ui/status/location.js:355
+msgid "Allow location access"
+msgstr ""
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:357
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "%s app aphan nangli kedo adim pachini ji?"
+
+#: js/ui/status/location.js:367
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+
+#: js/ui/status/network.js:66
+msgid "<unknown>"
+msgstr "<puthekthe>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:420 js/ui/status/network.js:1317
+#, javascript-format
+msgid "%s Off"
+msgstr "%s Bon-chek"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:423
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s Chepho"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:428
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:431
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s chethan bomlo"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:438 js/ui/status/network.js:1309
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s chepho bomlo"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:441
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s hovang nangji"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:449
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Firmware awe %s aphan"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:453
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s nonko awe"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:456
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s Chepho un-eh det"
+
+#: js/ui/status/network.js:468
+msgid "Wired Settings"
+msgstr "Wired amokha"
+
+#: js/ui/status/network.js:511
+msgid "Mobile Broadband Settings"
+msgstr "Mobile Broadband amokha"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:558 js/ui/status/network.js:1314
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:562
+#, javascript-format
+msgid "%s Disabled"
+msgstr ""
+
+#: js/ui/status/network.js:603
+msgid "Connect to Internet"
+msgstr "Internet pen chepho"
+
+#: js/ui/status/network.js:808
+msgid "Airplane Mode is On"
+msgstr "Airplane Mode dambom"
+
+#: js/ui/status/network.js:809
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr ""
+
+#: js/ui/status/network.js:810
+msgid "Turn Off Airplane Mode"
+msgstr "Airplane Mode bon-noi"
+
+#: js/ui/status/network.js:819
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi pamek chok si do"
+
+#: js/ui/status/network.js:820
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr ""
+
+#: js/ui/status/network.js:821
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi ingpu noi"
+
+#: js/ui/status/network.js:846
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi Networks"
+
+#: js/ui/status/network.js:848
+msgid "Select a network"
+msgstr "Network ingvai nangji"
+
+#: js/ui/status/network.js:880
+msgid "No Networks"
+msgstr "Networks awe"
+
+#: js/ui/status/network.js:901 js/ui/status/rfkill.js:106
+msgid "Use hardware switch to turn off"
+msgstr ""
+
+#: js/ui/status/network.js:1178
+msgid "Select Network"
+msgstr "Network chongvai nangji"
+
+#: js/ui/status/network.js:1184
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi amokha"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1305
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s Hotspot reng-dok"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1320
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s pen chepho-phe"
+
+#: js/ui/status/network.js:1417
+msgid "connecting…"
+msgstr "chepho bomlo…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1420
+msgid "authentication required"
+msgstr "Hovang nangji"
+
+#: js/ui/status/network.js:1422
+msgid "connection failed"
+msgstr "chepho un-eh det"
+
+#: js/ui/status/network.js:1473
+msgid "VPN Settings"
+msgstr "VPN amokha"
+
+#: js/ui/status/network.js:1490
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1500
+msgid "VPN Off"
+msgstr "VPN bon-chek"
+
+#: js/ui/status/network.js:1561 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Network ason-amung"
+
+#: js/ui/status/network.js:1590
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s Wired chepho"
+msgstr[1] "Son %s Wired chepho"
+
+#: js/ui/status/network.js:1594
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s Wi-Fi chepho"
+msgstr[1] "Son %s Wi-Fi chepho"
+
+#: js/ui/status/network.js:1598
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s Modem chepho"
+msgstr[1] "Son %s Modem chepho"
+
+#: js/ui/status/network.js:1732
+msgid "Connection failed"
+msgstr "Chepho un-eh det"
+
+#: js/ui/status/network.js:1733
+msgid "Activation of network connection failed"
+msgstr ""
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Night Light bon-chok"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Night Light dam-bom"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Ingthun thu-non"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Pinap arni an ke-pamep"
+
+#: js/ui/status/power.js:47
+msgid "Power Settings"
+msgstr "Power amokha"
+
+#: js/ui/status/power.js:63
+msgid "Fully Charged"
+msgstr "Charge pleng-lo"
+
+#: js/ui/status/power.js:69
+msgid "Not Charging"
+msgstr "Che Charge che"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:72 js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "Lakha bomlo…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d Dolang (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d Plengji aphan (%d %%)"
+
+#: js/ui/status/power.js:139 js/ui/status/power.js:141
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:44
+msgid "Screen is Being Shared"
+msgstr "Screen kaprek pen chepi bom"
+
+#: js/ui/status/remoteAccess.js:46
+msgid "Turn off"
+msgstr "Bon-noi"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Aeroplane Mode dambom"
+
+#: js/ui/status/system.js:103
+msgid "Lock"
+msgstr ""
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "Pamep / Ketet"
+
+#: js/ui/status/system.js:119
+msgid "Log Out"
+msgstr "Hong ke-tet"
+
+#: js/ui/status/system.js:131
+msgid "Switch User…"
+msgstr "Monit chelar…"
+
+#: js/ui/status/system.js:145
+msgid "Suspend"
+msgstr "Suspend"
+
+#: js/ui/status/system.js:157
+msgid "Power Off…"
+msgstr "Pamep…"
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:325
+msgid "Unknown Thunderbolt device"
+msgstr "Pangthek thekthe Thunderbold hormu"
+
+#: js/ui/status/thunderbolt.js:326
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Nangli bang awe hut akimi hormu theklong. Choningri pen hek-jok si thon-thu "
+"noi keklang ke-ot ji phan"
+
+#: js/ui/status/thunderbolt.js:329
+msgid "Unauthorized Thunderbolt device"
+msgstr "Hovang kawe Thunderbold hormu"
+
+#: js/ui/status/thunderbolt.js:330
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Kimi hormu ke thon phan administrators hovang nangji."
+
+#: js/ui/status/thunderbolt.js:336
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:337
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:150
+msgid "Volume changed"
+msgstr "Ase lar det"
+
+#: js/ui/status/volume.js:221
+msgid "Volume"
+msgstr "Aseh"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "Mirror"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "Displays pa-chepho"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "Hong pen anchot"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "Kedun bang anchot"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:370
+msgid "%A %B %-d"
+msgstr "%A %d %B"
+
+#: js/ui/unlockDialog.js:376
+msgid "Swipe up to unlock"
+msgstr ""
+
+#: js/ui/unlockDialog.js:377
+msgid "Click or press a key to unlock"
+msgstr ""
+
+#: js/ui/unlockDialog.js:550
+msgid "Unlock Window"
+msgstr "Window ingpu noi"
+
+#: js/ui/viewSelector.js:181
+msgid "Applications"
+msgstr "Applications"
+
+#: js/ui/viewSelector.js:185
+msgid "Search"
+msgstr "Kiri"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” tang lo"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:55
+msgid "Keep these display settings?"
+msgstr "Laso display settings pado nang?"
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:64
+msgid "Revert Settings"
+msgstr "Aphrang amokha si nangji"
+
+#: js/ui/windowManager.js:67
+msgid "Keep Changes"
+msgstr "Ke-lar chok lo"
+
+#: js/ui/windowManager.js:85
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Settings aphrang ateng chelar thu po %d second lote"
+msgstr[1] "Settings aphrang ateng chelar thu po %d seconds lote"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr "Pabi"
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr "Pathe-ri"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "Pathe"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Te-rek"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "Pathe-bi"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr ""
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "Angsong dover nangji"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr ""
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr "Ar-vi Workspace long pon noi"
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr "Ar-eh Workspace long pon noi"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "Athak Workspace long pon noi"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "Aber Workspace long pon noi"
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr "Athak Monitor long pon noi"
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr "Aber Monitor long pon noi"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "Ar-vi Monitor long pon noi"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "Ar-eh Monitor long pon noi"
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Kanghap"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution Calendar"
+
+#: src/main.c:460 subprojects/extensions-tool/src/main.c:249
+msgid "Print version"
+msgstr ""
+
+#: src/main.c:466
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: src/main.c:472
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+
+#: src/main.c:478
+msgid "List possible modes"
+msgstr ""
+
+#: src/shell-app.c:279
+msgctxt "program"
+msgid "Unknown"
+msgstr "Puthekthe"
+
+#: src/shell-app.c:530
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” ingpu un-eh"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "Passwords homan meh"
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "Password angse bi un-eh"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr ""
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:125
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:194
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:197
+#: subprojects/extensions-tool/src/main.c:170
+msgid "Name"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:208
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:222
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:244
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:247
+msgid "NAME"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:248
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:250
+msgid "DESCRIPTION"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:252
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:255
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:263
+msgid "Create a new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:280
+#: subprojects/extensions-tool/src/command-list.c:168
+msgid "Unknown arguments"
+msgstr "Athe chinine"
+
+#: subprojects/extensions-tool/src/command-create.c:289
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:57
+msgid "Disable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:74
+#: subprojects/extensions-tool/src/command-enable.c:74
+#: subprojects/extensions-tool/src/command-info.c:96
+#: subprojects/extensions-tool/src/command-prefs.c:107
+#: subprojects/extensions-tool/src/command-reset.c:74
+#: subprojects/extensions-tool/src/command-uninstall.c:89
+msgid "No UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:79
+#: subprojects/extensions-tool/src/command-enable.c:79
+#: subprojects/extensions-tool/src/command-info.c:101
+#: subprojects/extensions-tool/src/command-prefs.c:112
+#: subprojects/extensions-tool/src/command-reset.c:79
+#: subprojects/extensions-tool/src/command-uninstall.c:94
+msgid "More than one UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-enable.c:57
+msgid "Enable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:79
+msgid "Show extensions info"
+msgstr "Extensions info paklang"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:201
+msgid "No extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:207
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:125
+msgid "Show user-installed extensions"
+msgstr "User kethap tang Extensions paklang"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show system-installed extensions"
+msgstr "System long Extensions paklang"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show enabled extensions"
+msgstr "Dambom Extensions paklang"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show disabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show extensions with preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with updates"
+msgstr "Extensions updates do an paklang"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Print extension details"
+msgstr "Extensions details print"
+
+#: subprojects/extensions-tool/src/command-list.c:151
+msgid "List installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:500
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:90
+msgid "Opens extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-reset.c:57
+msgid "Reset an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:72
+msgid "Uninstall an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:176
+msgid "Path"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:179
+msgid "URL"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:182
+msgid "Original author"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:188
+msgid "State"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:222
+msgid "“version” takes no arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:224
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Usage:"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:227
+msgid "Print version information and exit."
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:242
+#: subprojects/extensions-tool/src/main.c:245
+msgid "COMMAND"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:245
+msgid "[ARGS…]"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "Commands:"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:248
+msgid "Print help"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Enable extension"
+msgstr "Extension kangpu"
+
+#: subprojects/extensions-tool/src/main.c:251
+msgid "Disable extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:252
+msgid "Reset extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Uninstall extension"
+msgstr "Shell Extension kehek"
+
+#: subprojects/extensions-tool/src/main.c:254
+msgid "List extensions"
+msgstr "Extensions paklang thai"
+
+#: subprojects/extensions-tool/src/main.c:255
+#: subprojects/extensions-tool/src/main.c:256
+msgid "Show extension info"
+msgstr "Extensions info paklang"
+
+#: subprojects/extensions-tool/src/main.c:257
+msgid "Open extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:258
+msgid "Create extension"
+msgstr "Extension selam"
+
+#: subprojects/extensions-tool/src/main.c:259
+msgid "Package extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:260
+msgid "Install extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:262
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr ""
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1899
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u pen aseh ke-tet"
+msgstr[1] "%u pen aseh ke-tet"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1909
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u pen ase kethap"
+msgstr[1] "%u pen ase kethap"
+
+#: subprojects/gvc/gvc-mixer-control.c:2766
+msgid "System Sounds"
+msgstr "System Aseh"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Software long kelang"
+
+#~ msgid "Next"
+#~ msgstr "Dunthu"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Sign In"
+
+#~ msgid "Password:"
+#~ msgstr "Password:"
+
+#~ msgid "Type again:"
+#~ msgstr "Sonthot tok thu nanglang:"
+
+#~ msgid "Password: "
+#~ msgstr "Password: "
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d birta kimi"
+#~ msgstr[1] "Son %d birta kimi"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d notification kimi"
+#~ msgstr[1] "Son %d notification kimi"
+
+#~ msgid "Log in as another user"
+#~ msgstr "Kaprek amen pen Login ji"
+
+#~ msgid "Account Settings"
+#~ msgstr "Account amokha"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Tewar longle"
diff --git a/po/mk.po b/po/mk.po
new file mode 100644
index 0000000..2f95cdd
--- /dev/null
+++ b/po/mk.po
@@ -0,0 +1,1640 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 3.4\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2011-12-08 16:50+0000\n"
+"PO-Revision-Date: 2011-12-08 22:37+0100\n"
+"Last-Translator: Jovan N\n"
+"Language-Team: mk_MK <jovan@lugola.net>\n"
+"Language: mk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural= n==1 || n%10==1 ? 0 : 1;\n"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "Менаџмент на прозорци и пуштање на апликации"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:1
+msgid "Allows access to internal debugging and monitoring tools using the Alt-F2 dialog."
+msgstr "Овозможува пристап до внатрешни алатки за дебагирање и мониторирање со користење на дијалогот на Alt-F2."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:2
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Овозможи ги внатрешните алатки кои се корисни за програмерите и тестерите преку Alt-F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:3
+msgid "File extension used for storing the screencast"
+msgstr "Наставка на датотека која се користи за зачувување на снимката од екран"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:4
+msgid "Framerate used for recording screencasts."
+msgstr "Рата на рамки која се користи при снимање на снимки од екран."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:5
+msgid "GNOME Shell extensions have a uuid property; this key lists extensions which should be loaded. Any extension that wants to be loaded needs to be in this list. You can also manipulate this list with the EnableExtension and DisableExtension DBus methods on org.gnome.Shell."
+msgstr "Додатоците за Gnome Shell имаат uuid својство; овој клуч ги листа додатоците кои ќе се вчитаат. Секој додаток кој треба да биде вчитан мора да стои во оваа листа. Можете да манипулирате со оваа листа со DBus методите EnableExtension и DisableExtension на org.gnome.Shell."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:6
+msgid "History for command (Alt-F2) dialog"
+msgstr "Историја на дијалогот за команди (Alt-F2)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:7
+msgid "History for the looking glass dialog"
+msgstr "Историја на стаклениот дијалог"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:8
+msgid "If true, display date in the clock, in addition to time."
+msgstr "Ако е точно, прикажи го датумот на часовникот, покрај времето."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:9
+msgid "If true, display seconds in time."
+msgstr "Ако е точно, прикажи ги секундите во часовникот."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:10
+msgid "If true, display the ISO week date in the calendar."
+msgstr "Ако е точно, прикажи го ISO датумот во календарот."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:11
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Листа на ID на desktop датотеки за омилените апликации"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:13
+#, no-c-format
+msgid "Sets the GStreamer pipeline used to encode recordings. It follows the syntax used for gst-launch. The pipeline should have an unconnected sink pad where the recorded video is recorded. It will normally have a unconnected source pad; output from that pad will be written into the output file. However the pipeline can also take care of its own output - this might be used to send the output to an icecast server via shout2send or similar. When unset or set to an empty value, the default pipeline will be used. This is currently 'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is used as a placeholder for a guess at the optimal thread count on the system."
+msgstr "Го поставува GStreamer цевководот за енкодирање на снимки. Ја следи синтаксата која се користи за gst-launch. Цевководот треба да има неповрзан одвод каде се зачувува сниманото видео. Вообичаено тој има неповрзан извор; излезните податоци ќе бидат запишани во излезната датотека. Сепак, цевководот може да се грижи за својот излез - тоа најчесто може да се користи за испраќање на излезните податоци на icecast сервер преку shout2send или слично. Кога не е поставен или пак е поставен на празна вредност, се користи стандардниот цевковод. Оваа вредност моментално е „videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux“ и снима во WEBM со користење на кодекот VP8. %T се користи за определување на оптималниот број на нишки за системот."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:14
+msgid "Show date in clock"
+msgstr "Прикажи го датумот во часовникот"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:15
+msgid "Show the week date in the calendar"
+msgstr "Покажи ја неделата во календарот"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:16
+msgid "Show time with seconds"
+msgstr "Покажи го времето со секунди"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:17
+msgid "The applications corresponding to these identifiers will be displayed in the favorites area."
+msgstr "Апликациите на кои се однесуваат овие идентификатори ќе бидат прикажани во површината за омилени."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:18
+msgid "The filename for recorded screencasts will be a unique filename based on the current date, and use this extension. It should be changed when recording to a different container format."
+msgstr "Името на датотеката за зачуваните снимки од екранот ќе бидее уникатно според тековниот датум и ќе ја има оваа наставка. Таа треба да се промени кога снимањето се прави со различен формат на контејнерот. "
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:19
+msgid "The framerate of the resulting screencast recordered by GNOME Shell's screencast recorder in frames-per-second."
+msgstr "Ратата на рамки, во рамки/секунда, на добиената снимка на екранот снимана од алатката за правење снимки од екранот на GNOME Shell."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:20
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr "Цевководот на gstreamer кој се користи за енкодирање на снимката од екран"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:21
+msgid "The shell normally monitors active applications in order to present the most used ones (e.g. in launchers). While this data will be kept private, you may want to disable this for privacy reasons. Please note that doing so won't remove already saved data."
+msgstr "Школката обично ги набљудува активните апликации за да прикаже кои се најкористени (пр. во пуштачите). Иако овие податоци се се чуваат како приватни, може да го оневозможите ова за поголема приватност. Имајте во предвид дека со тоа нема да се отстранат тековно сниманите податоци."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:22
+msgid "The type of keyboard to use."
+msgstr "Типот на тастатура кој се користи."
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:23
+msgid "Uuids of extensions to enable"
+msgstr "Uuid на екстензиите кои ќе се овозможат"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:24
+msgid "Whether to collect stats about applications usage"
+msgstr "Дали да се собира статистика за користењето на апликациите"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:25
+msgid "Which keyboard to use"
+msgstr "Која тастатура да се користи"
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:26
+msgid "disabled OpenSearch providers"
+msgstr "оневозможени OpenSearch провајдери"
+
+#: ../js/gdm/loginDialog.js:623
+msgid "Session..."
+msgstr "Сесија..."
+
+#: ../js/gdm/loginDialog.js:785
+msgctxt "title"
+msgid "Sign In"
+msgstr "Најави се"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/loginDialog.js:830
+msgid "(or swipe finger)"
+msgstr "(или поминете со прстот)"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:851
+msgid "Not listed?"
+msgstr "Не сте во листата?"
+
+#: ../js/gdm/loginDialog.js:1019
+#: ../js/ui/endSessionDialog.js:418
+#: ../js/ui/extensionSystem.js:516
+#: ../js/ui/networkAgent.js:145
+#: ../js/ui/polkitAuthenticationAgent.js:175
+#: ../js/ui/status/bluetooth.js:462
+msgid "Cancel"
+msgstr "Откажи"
+
+#: ../js/gdm/loginDialog.js:1024
+msgctxt "button"
+msgid "Sign In"
+msgstr "Најави се"
+
+#: ../js/gdm/loginDialog.js:1376
+msgid "Login Window"
+msgstr "Најавен прозорец"
+
+#: ../js/gdm/powerMenu.js:113
+#: ../js/ui/userMenu.js:578
+#: ../js/ui/userMenu.js:580
+#: ../js/ui/userMenu.js:649
+msgid "Suspend"
+msgstr "Суспендирај"
+
+#: ../js/gdm/powerMenu.js:118
+msgid "Restart"
+msgstr "Рестартирај се"
+
+#: ../js/gdm/powerMenu.js:123
+msgid "Power Off"
+msgstr "Исклучи се"
+
+#: ../js/misc/util.js:92
+msgid "Command not found"
+msgstr "Командата не е пронајдена"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:119
+msgid "Could not parse command:"
+msgstr "Не можев да ја испарсирам командата:"
+
+#: ../js/misc/util.js:127
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "Извршувањето на „%s“ не успеа:"
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:251
+msgid "All"
+msgstr "Сите"
+
+#: ../js/ui/appDisplay.js:310
+msgid "APPLICATIONS"
+msgstr "АПЛИКАЦИИ"
+
+#: ../js/ui/appDisplay.js:366
+msgid "SETTINGS"
+msgstr "ПОСТАВУВАЊА"
+
+#: ../js/ui/appDisplay.js:664
+msgid "New Window"
+msgstr "Нов прозорец"
+
+#: ../js/ui/appDisplay.js:667
+msgid "Remove from Favorites"
+msgstr "Отстрани од омилените"
+
+#: ../js/ui/appDisplay.js:668
+msgid "Add to Favorites"
+msgstr "Додај во омилени"
+
+#: ../js/ui/appFavorites.js:87
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "%s е веќе постои во омилените."
+
+#: ../js/ui/appFavorites.js:118
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "%s е отстранет од листата на омилени."
+
+#: ../js/ui/autorunManager.js:265
+msgid "Removable Devices"
+msgstr "Отстранливи уреди"
+
+#: ../js/ui/autorunManager.js:560
+#, c-format
+msgid "Open with %s"
+msgstr "Отвори со %s"
+
+#: ../js/ui/autorunManager.js:586
+msgid "Eject"
+msgstr "Извади"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:62
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Цел ден"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:74
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:114
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Н"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:116
+msgctxt "grid monday"
+msgid "M"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:118
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "В"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:120
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:122
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ч"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:124
+msgctxt "grid friday"
+msgid "F"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:126
+msgctxt "grid saturday"
+msgid "S"
+msgstr "С"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:139
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Нед"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:141
+msgctxt "list monday"
+msgid "M"
+msgstr "Пон"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:143
+msgctxt "list tuesday"
+msgid "T"
+msgstr "Вто"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:145
+msgctxt "list wednesday"
+msgid "W"
+msgstr "Сре"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:147
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Чет"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:149
+msgctxt "list friday"
+msgid "F"
+msgstr "Пет"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:151
+msgctxt "list saturday"
+msgid "S"
+msgstr "Саб"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:681
+msgid "Nothing Scheduled"
+msgstr "Нема ништо закажано"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:697
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:700
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:710
+msgid "Today"
+msgstr "Денес"
+
+#: ../js/ui/calendar.js:714
+msgid "Tomorrow"
+msgstr "Утре"
+
+#: ../js/ui/calendar.js:723
+msgid "This week"
+msgstr "Оваа недела"
+
+#: ../js/ui/calendar.js:731
+msgid "Next week"
+msgstr "Следната недела"
+
+#: ../js/ui/contactDisplay.js:63
+#: ../js/ui/notificationDaemon.js:466
+#: ../js/ui/status/power.js:215
+#: ../src/shell-app.c:350
+msgid "Unknown"
+msgstr "Непознато"
+
+#: ../js/ui/contactDisplay.js:84
+#: ../js/ui/userMenu.js:127
+msgid "Available"
+msgstr "Достапен"
+
+#: ../js/ui/contactDisplay.js:89
+#: ../js/ui/userMenu.js:136
+msgid "Away"
+msgstr "Отсутен"
+
+#: ../js/ui/contactDisplay.js:93
+#: ../js/ui/userMenu.js:130
+msgid "Busy"
+msgstr "Зафатен"
+
+#: ../js/ui/contactDisplay.js:97
+msgid "Offline"
+msgstr "Офлајн"
+
+#: ../js/ui/contactDisplay.js:141
+msgid "CONTACTS"
+msgstr "КОНТАКТИ"
+
+#: ../js/ui/dash.js:169
+#: ../js/ui/messageTray.js:1204
+msgid "Remove"
+msgstr "Отстрани"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Date and Time Settings"
+msgstr "Поставувања за датум и време"
+
+#: ../js/ui/dateMenu.js:122
+msgid "Open Calendar"
+msgstr "Отвори го календарот"
+
+#. Translators: This is the time format with date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:180
+msgid "%a %b %e, %R:%S"
+msgstr "%a %b %e, %R:%S"
+
+#: ../js/ui/dateMenu.js:181
+msgid "%a %b %e, %R"
+msgstr "%a %b %e, %R"
+
+#. Translators: This is the time format without date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:185
+msgid "%a %R:%S"
+msgstr "%a %R:%S"
+
+#: ../js/ui/dateMenu.js:186
+msgid "%a %R"
+msgstr "%a %R"
+
+#. Translators: This is a time format with date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:193
+msgid "%a %b %e, %l:%M:%S %p"
+msgstr "%a %b %e, %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:194
+msgid "%a %b %e, %l:%M %p"
+msgstr "%a %b %e, %l:%M %p"
+
+#. Translators: This is a time format without date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:198
+msgid "%a %l:%M:%S %p"
+msgstr "%a %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:199
+msgid "%a %l:%M %p"
+msgstr "%a %l:%M %p"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:210
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/docDisplay.js:13
+msgid "RECENT ITEMS"
+msgstr "СКОРЕШНИ ПРЕДМЕТИ"
+
+#: ../js/ui/endSessionDialog.js:61
+#, c-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Одјави го „%s“"
+
+#: ../js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Log Out"
+msgstr "Одјави се"
+
+#: ../js/ui/endSessionDialog.js:63
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr "Кликнете „Одјави се“ за да се исклучат овие апликации и за одјавување од системот."
+
+#: ../js/ui/endSessionDialog.js:65
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s ќе биде автоматски одјавен од системот за %d секунда."
+msgstr[1] "%s ќе биде автоматски одјавен од системот за %d секунди."
+
+#: ../js/ui/endSessionDialog.js:70
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Ќе бидете автоматски одјавен од системот за %d секунда."
+msgstr[1] "Ќе бидете автоматски одјавен од системот за %d секунди."
+
+#: ../js/ui/endSessionDialog.js:74
+msgid "Logging out of the system."
+msgstr "Одјавување од системот."
+
+#: ../js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Log Out"
+msgstr "Одјави се"
+
+#: ../js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Power Off"
+msgstr "Исклучи се"
+
+#: ../js/ui/endSessionDialog.js:82
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr "Кликнете „Исклучи се“ за да ги исклучите овие апликации и да го исклучите системот."
+
+#: ../js/ui/endSessionDialog.js:84
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Системот ќе се исклучи автоматски за %d секунда."
+msgstr[1] "Системот ќе се исклучи автоматски за %d секунди."
+
+#: ../js/ui/endSessionDialog.js:88
+msgid "Powering off the system."
+msgstr "Го исклучувам системот."
+
+#: ../js/ui/endSessionDialog.js:90
+#: ../js/ui/endSessionDialog.js:107
+msgctxt "button"
+msgid "Restart"
+msgstr "Рестартирај се"
+
+#: ../js/ui/endSessionDialog.js:92
+msgctxt "button"
+msgid "Power Off"
+msgstr "Исклучи се"
+
+#: ../js/ui/endSessionDialog.js:98
+msgctxt "title"
+msgid "Restart"
+msgstr "Рестартирај се"
+
+#: ../js/ui/endSessionDialog.js:99
+msgid "Click Restart to quit these applications and restart the system."
+msgstr "Кликнете „Рестартирај се“ за да ги исклучите овие апликации и да го рестартирате системот."
+
+#: ../js/ui/endSessionDialog.js:101
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Системот ќе се рестартира автоматски за %d секунда."
+msgstr[1] "Системот ќе се рестартира автоматски за %d секунди."
+
+#: ../js/ui/endSessionDialog.js:105
+msgid "Restarting the system."
+msgstr "Го рестартирам системот."
+
+#: ../js/ui/extensionSystem.js:520
+msgid "Install"
+msgstr "Инсталирај"
+
+#: ../js/ui/extensionSystem.js:524
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr "Да го преземам и инсталирам „%s“ од extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:322
+msgid "tray"
+msgstr "место за известување"
+
+#: ../js/ui/keyboard.js:539
+#: ../js/ui/status/power.js:203
+msgid "Keyboard"
+msgstr "Тастатура"
+
+#: ../js/ui/lookingGlass.js:724
+msgid "No extensions installed"
+msgstr "Не е инсталиран додаток"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:779
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr "%s не испрати никакви грешки."
+
+#: ../js/ui/lookingGlass.js:785
+msgid "Hide Errors"
+msgstr "Скриј ги грешките"
+
+#: ../js/ui/lookingGlass.js:789
+#: ../js/ui/lookingGlass.js:840
+msgid "Show Errors"
+msgstr "Прикажи ги грешките"
+
+#: ../js/ui/lookingGlass.js:798
+msgid "Enabled"
+msgstr "Овозможено"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:801
+#: ../src/gvc/gvc-mixer-control.c:1093
+msgid "Disabled"
+msgstr "Оневозможено"
+
+#: ../js/ui/lookingGlass.js:803
+msgid "Error"
+msgstr "Грешка"
+
+#: ../js/ui/lookingGlass.js:805
+msgid "Out of date"
+msgstr "Застарено"
+
+#: ../js/ui/lookingGlass.js:807
+msgid "Downloading"
+msgstr "Преземам"
+
+#: ../js/ui/lookingGlass.js:828
+msgid "View Source"
+msgstr "Погледни го изворот"
+
+#: ../js/ui/lookingGlass.js:834
+msgid "Web Page"
+msgstr "Веб страница"
+
+#: ../js/ui/messageTray.js:1197
+msgid "Open"
+msgstr "Отвори"
+
+#: ../js/ui/messageTray.js:1214
+msgid "Unmute"
+msgstr "Пушти го гласот"
+
+#: ../js/ui/messageTray.js:1214
+msgid "Mute"
+msgstr "Исклучи го гласот"
+
+#: ../js/ui/messageTray.js:2444
+msgid "System Information"
+msgstr "Информации за системот"
+
+#: ../js/ui/networkAgent.js:140
+msgid "Connect"
+msgstr "Поврзи се"
+
+#. Cisco LEAP
+#: ../js/ui/networkAgent.js:235
+#: ../js/ui/networkAgent.js:247
+#: ../js/ui/networkAgent.js:274
+#: ../js/ui/networkAgent.js:294
+#: ../js/ui/networkAgent.js:304
+msgid "Password: "
+msgstr "Лозинка:"
+
+#. static WEP
+#: ../js/ui/networkAgent.js:240
+msgid "Key: "
+msgstr "Клуч:"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/ui/networkAgent.js:272
+#: ../js/ui/networkAgent.js:290
+msgid "Username: "
+msgstr "Корисничко име:"
+
+#: ../js/ui/networkAgent.js:278
+msgid "Identity: "
+msgstr "Идентитет:"
+
+#: ../js/ui/networkAgent.js:280
+msgid "Private key password: "
+msgstr "Лозинка на приватниот клуч:"
+
+#: ../js/ui/networkAgent.js:292
+msgid "Service: "
+msgstr "Сервис:"
+
+#: ../js/ui/networkAgent.js:321
+msgid "Authentication required by wireless network"
+msgstr "Потребна е автентикација од безжичната мрежа"
+
+#: ../js/ui/networkAgent.js:322
+#, c-format
+msgid "Passwords or encryption keys are required to access the wireless network '%s'."
+msgstr "Потребни се лозинки или клучеви за енкрипција за пристап на безжичната мрежа „%s“."
+
+#: ../js/ui/networkAgent.js:326
+msgid "Wired 802.1X authentication"
+msgstr "Жичена 802.1X автентикација"
+
+#: ../js/ui/networkAgent.js:328
+msgid "Network name: "
+msgstr "Име на мрежа:"
+
+#: ../js/ui/networkAgent.js:333
+msgid "DSL authentication"
+msgstr "DSL автентикација"
+
+#: ../js/ui/networkAgent.js:340
+msgid "PIN code required"
+msgstr "Потребен е PIN код"
+
+#: ../js/ui/networkAgent.js:341
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Потребен е PIN кодот за мобилниот уред"
+
+#: ../js/ui/networkAgent.js:342
+msgid "PIN: "
+msgstr "PIN:"
+
+#: ../js/ui/networkAgent.js:348
+msgid "Mobile broadband network password"
+msgstr "Лозинка за мобилна широкопојасна мрежа "
+
+#: ../js/ui/networkAgent.js:349
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr "Потребна е лозинка за поврзување со „%s“."
+
+#: ../js/ui/overview.js:89
+msgid "Undo"
+msgstr "Врати"
+
+#: ../js/ui/overview.js:198
+msgid "Windows"
+msgstr "Прозорци"
+
+#: ../js/ui/overview.js:201
+msgid "Applications"
+msgstr "Апликации"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:223
+msgid "Dash"
+msgstr "Лента"
+
+#. TODO - _quit() doesn't really work on apps in state STARTING yet
+#: ../js/ui/panel.js:532
+#, c-format
+msgid "Quit %s"
+msgstr "Исклучи го %s"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:565
+msgid "Activities"
+msgstr "Активности"
+
+#: ../js/ui/panel.js:953
+msgid "Top Bar"
+msgstr "Горна лента"
+
+#: ../js/ui/placeDisplay.js:115
+#, c-format
+msgid "Failed to unmount '%s'"
+msgstr "Не успеав да го одмонтирам „%s“"
+
+#: ../js/ui/placeDisplay.js:118
+msgid "Retry"
+msgstr "Обиди се повторно"
+
+#: ../js/ui/placeDisplay.js:156
+msgid "Connect to..."
+msgstr "Поврзи се со..."
+
+#: ../js/ui/placeDisplay.js:364
+msgid "PLACES & DEVICES"
+msgstr "МЕСТА И УРЕДИ"
+
+#: ../js/ui/polkitAuthenticationAgent.js:71
+msgid "Authentication Required"
+msgstr "Потребна е автентикација"
+
+#: ../js/ui/polkitAuthenticationAgent.js:109
+msgid "Administrator"
+msgstr "Администратор"
+
+#: ../js/ui/polkitAuthenticationAgent.js:179
+msgid "Authenticate"
+msgstr "Автентицирај се"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/polkitAuthenticationAgent.js:260
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Жалам, но тоа не успеа. Пробајте пак."
+
+#: ../js/ui/polkitAuthenticationAgent.js:272
+msgid "Password:"
+msgstr "Лозинка:"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:717
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:205
+msgid "Please enter a command:"
+msgstr "Ве молам, внесете команда:"
+
+#: ../js/ui/searchDisplay.js:333
+msgid "Searching..."
+msgstr "Барам..."
+
+#: ../js/ui/searchDisplay.js:356
+msgid "No matching results."
+msgstr "Нема резултати."
+
+#: ../js/ui/shellEntry.js:26
+msgid "Copy"
+msgstr "Копирај"
+
+#: ../js/ui/shellEntry.js:31
+msgid "Paste"
+msgstr "Вметни"
+
+#: ../js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Прикажи текст"
+
+#: ../js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Скриј го текстот"
+
+#: ../js/ui/shellMountOperation.js:269
+msgid "Wrong password, please try again"
+msgstr "Грешна лозинка, пробајте пак"
+
+#: ../js/ui/status/accessibility.js:52
+msgid "Zoom"
+msgstr "Зумирај"
+
+#. let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
+#. 'screen-reader-enabled');
+#. this.menu.addMenuItem(screenReader);
+#: ../js/ui/status/accessibility.js:63
+msgid "Screen Keyboard"
+msgstr "Екранска тастатура"
+
+#: ../js/ui/status/accessibility.js:67
+msgid "Visual Alerts"
+msgstr "Визуелни аларми"
+
+#: ../js/ui/status/accessibility.js:70
+msgid "Sticky Keys"
+msgstr "Лепливи копчиња"
+
+#: ../js/ui/status/accessibility.js:73
+msgid "Slow Keys"
+msgstr "Бавни копчиња"
+
+#: ../js/ui/status/accessibility.js:76
+msgid "Bounce Keys"
+msgstr "Враќачки копчиња"
+
+#: ../js/ui/status/accessibility.js:79
+msgid "Mouse Keys"
+msgstr "Копчиња на глушец"
+
+#: ../js/ui/status/accessibility.js:83
+msgid "Universal Access Settings"
+msgstr "Поставувања за универзален пристап"
+
+#: ../js/ui/status/accessibility.js:117
+msgid "High Contrast"
+msgstr "Висок контраст"
+
+#: ../js/ui/status/accessibility.js:154
+msgid "Large Text"
+msgstr "Голем текст"
+
+#: ../js/ui/status/bluetooth.js:35
+#: ../js/ui/status/bluetooth.js:258
+#: ../js/ui/status/bluetooth.js:341
+#: ../js/ui/status/bluetooth.js:371
+#: ../js/ui/status/bluetooth.js:407
+#: ../js/ui/status/bluetooth.js:436
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: ../js/ui/status/bluetooth.js:48
+msgid "Visibility"
+msgstr "Видливост"
+
+#: ../js/ui/status/bluetooth.js:62
+msgid "Send Files to Device..."
+msgstr "Испрати датотеки до уред..."
+
+#: ../js/ui/status/bluetooth.js:63
+msgid "Set up a New Device..."
+msgstr "Постави нов уред..."
+
+#: ../js/ui/status/bluetooth.js:87
+msgid "Bluetooth Settings"
+msgstr "Поставувања за bluetooth"
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:107
+#: ../js/ui/status/network.js:255
+msgid "hardware disabled"
+msgstr "уредот е оневозможен"
+
+#: ../js/ui/status/bluetooth.js:203
+msgid "Connection"
+msgstr "Врска"
+
+#: ../js/ui/status/bluetooth.js:214
+#: ../js/ui/status/network.js:477
+msgid "disconnecting..."
+msgstr "ја прекинувам врската..."
+
+#: ../js/ui/status/bluetooth.js:227
+#: ../js/ui/status/network.js:483
+msgid "connecting..."
+msgstr "се поврзувам..."
+
+#: ../js/ui/status/bluetooth.js:245
+msgid "Send Files..."
+msgstr "Испраќам датотеки..."
+
+#: ../js/ui/status/bluetooth.js:250
+msgid "Browse Files..."
+msgstr "Прелистај низ датотеките..."
+
+#: ../js/ui/status/bluetooth.js:259
+msgid "Error browsing device"
+msgstr "Грешка при прелистувањето низ уредот"
+
+#: ../js/ui/status/bluetooth.js:260
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr "Низ побараниот уред не може да се прелистува, грешката е „%s“"
+
+#: ../js/ui/status/bluetooth.js:268
+msgid "Keyboard Settings"
+msgstr "Поставувања за тастатура"
+
+#: ../js/ui/status/bluetooth.js:271
+msgid "Mouse Settings"
+msgstr "Поставувања за глушец"
+
+#: ../js/ui/status/bluetooth.js:276
+#: ../js/ui/status/volume.js:58
+msgid "Sound Settings"
+msgstr "Поставувања за звук"
+
+#: ../js/ui/status/bluetooth.js:372
+#, c-format
+msgid "Authorization request from %s"
+msgstr "Барање за авторизација од %s"
+
+#: ../js/ui/status/bluetooth.js:378
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "Уредот %s бара пристап до сервисот „%s“"
+
+#: ../js/ui/status/bluetooth.js:380
+msgid "Always grant access"
+msgstr "Секогаш доделувај му пристап"
+
+#: ../js/ui/status/bluetooth.js:381
+msgid "Grant this time only"
+msgstr "Додели му само овој пат"
+
+#: ../js/ui/status/bluetooth.js:382
+#: ../js/ui/telepathyClient.js:1181
+msgid "Reject"
+msgstr "Одбиј"
+
+#: ../js/ui/status/bluetooth.js:408
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "Потврда за спарување за %s"
+
+#: ../js/ui/status/bluetooth.js:414
+#: ../js/ui/status/bluetooth.js:444
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "Уредот %s сака да се спари со компјутерот"
+
+#: ../js/ui/status/bluetooth.js:415
+#, c-format
+msgid "Please confirm whether the PIN '%s' matches the one on the device."
+msgstr "Потврдете дека PIN-от „%s“ одговара со тој на уредот."
+
+#: ../js/ui/status/bluetooth.js:417
+msgid "Matches"
+msgstr "Одговара"
+
+#: ../js/ui/status/bluetooth.js:418
+msgid "Does not match"
+msgstr "Не одговара"
+
+#: ../js/ui/status/bluetooth.js:437
+#, c-format
+msgid "Pairing request for %s"
+msgstr "Барање за спарување со %s"
+
+#: ../js/ui/status/bluetooth.js:445
+msgid "Please enter the PIN mentioned on the device."
+msgstr "Внесете го PIN-от кој се појавува на уредот."
+
+#: ../js/ui/status/bluetooth.js:461
+msgid "OK"
+msgstr "Во ред"
+
+#: ../js/ui/status/keyboard.js:68
+msgid "Show Keyboard Layout"
+msgstr "Прикажи го распоредот на тастатурата"
+
+#: ../js/ui/status/keyboard.js:73
+msgid "Region and Language Settings"
+msgstr "Поставувања за регион и јазик"
+
+#: ../js/ui/status/network.js:96
+msgid "<unknown>"
+msgstr "<непознато>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:277
+msgid "disabled"
+msgstr "оневозможена"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:475
+msgid "unmanaged"
+msgstr "неменаџирана"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:486
+msgid "authentication required"
+msgstr "потребна е автентикација"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:496
+msgid "firmware missing"
+msgstr "недостасува firmware"
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:503
+msgid "cable unplugged"
+msgstr "кабелот е откачен"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:508
+msgid "unavailable"
+msgstr "нема пристап"
+
+#: ../js/ui/status/network.js:510
+msgid "connection failed"
+msgstr "не успеа врската"
+
+#: ../js/ui/status/network.js:566
+#: ../js/ui/status/network.js:1508
+msgid "More..."
+msgstr "Повеќе..."
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:602
+#: ../js/ui/status/network.js:1443
+msgid "Connected (private)"
+msgstr "Поврзан (приватен)"
+
+#: ../js/ui/status/network.js:677
+msgid "Auto Ethernet"
+msgstr "Автоматски ethernet"
+
+#: ../js/ui/status/network.js:738
+msgid "Auto broadband"
+msgstr "Автоматски широкопојасен"
+
+#: ../js/ui/status/network.js:741
+msgid "Auto dial-up"
+msgstr "Автоматски dial-up"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:860
+#: ../js/ui/status/network.js:1455
+#, c-format
+msgid "Auto %s"
+msgstr "Aвтоматски %s"
+
+#: ../js/ui/status/network.js:862
+msgid "Auto bluetooth"
+msgstr "Автоматски bluetooth"
+
+#: ../js/ui/status/network.js:1457
+msgid "Auto wireless"
+msgstr "Автоматски безжично"
+
+#: ../js/ui/status/network.js:1551
+msgid "Enable networking"
+msgstr "Овозможи мрежа"
+
+#: ../js/ui/status/network.js:1563
+msgid "Wired"
+msgstr "Жичена"
+
+#: ../js/ui/status/network.js:1574
+msgid "Wireless"
+msgstr "Безжична"
+
+#: ../js/ui/status/network.js:1584
+msgid "Mobile broadband"
+msgstr "Мобилна широкопојасна мрежа"
+
+#: ../js/ui/status/network.js:1594
+msgid "VPN Connections"
+msgstr "VPN врски"
+
+#: ../js/ui/status/network.js:1605
+msgid "Network Settings"
+msgstr "Поставувања за мрежата"
+
+#: ../js/ui/status/network.js:1742
+msgid "Connection failed"
+msgstr "Врската не успеа"
+
+#: ../js/ui/status/network.js:1743
+msgid "Activation of network connection failed"
+msgstr "Активирањето на мрежата не успеа"
+
+#: ../js/ui/status/network.js:1993
+msgid "Networking is disabled"
+msgstr "Мрежата е оневозможена"
+
+#: ../js/ui/status/network.js:2117
+msgid "Network Manager"
+msgstr "Менаџер на мрежи"
+
+#: ../js/ui/status/power.js:77
+msgid "Power Settings"
+msgstr "Поставувања за енергијата"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:98
+msgid "Estimating..."
+msgstr "Проценувам..."
+
+#: ../js/ui/status/power.js:105
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "преостанува уште %d час"
+msgstr[1] "преостануваат уште %d часа"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:108
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "преостануваат уште %d %s %d %s"
+
+#: ../js/ui/status/power.js:110
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "час"
+msgstr[1] "часа"
+
+#: ../js/ui/status/power.js:110
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "минута"
+msgstr[1] "минути"
+
+#: ../js/ui/status/power.js:113
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "преостанува уште %d минута"
+msgstr[1] "преостануваат уште %d минути"
+
+#: ../js/ui/status/power.js:116
+#: ../js/ui/status/power.js:186
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d%%"
+
+#: ../js/ui/status/power.js:193
+msgid "AC adapter"
+msgstr "Адаптер"
+
+#: ../js/ui/status/power.js:195
+msgid "Laptop battery"
+msgstr "Батерија од лаптоп"
+
+#: ../js/ui/status/power.js:197
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:199
+msgid "Monitor"
+msgstr "Монитор"
+
+#: ../js/ui/status/power.js:201
+msgid "Mouse"
+msgstr "Глушец"
+
+#: ../js/ui/status/power.js:205
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:207
+msgid "Cell phone"
+msgstr "Мобилен телефон"
+
+#: ../js/ui/status/power.js:209
+msgid "Media player"
+msgstr "Плеер"
+
+#: ../js/ui/status/power.js:211
+msgid "Tablet"
+msgstr "Таблет"
+
+#: ../js/ui/status/power.js:213
+msgid "Computer"
+msgstr "Компјутер"
+
+#: ../js/ui/status/volume.js:38
+msgid "Volume"
+msgstr "Гласност"
+
+#: ../js/ui/status/volume.js:50
+msgid "Microphone"
+msgstr "Микрофон"
+
+#. We got the TpContact
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/telepathyClient.js:256
+msgid "Invitation"
+msgstr "Покана"
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:324
+msgid "Call"
+msgstr "Повик"
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:354
+msgid "File Transfer"
+msgstr "Пренос на датотеки"
+
+#: ../js/ui/telepathyClient.js:435
+msgid "Subscription request"
+msgstr "Барање за претплата"
+
+#: ../js/ui/telepathyClient.js:471
+msgid "Connection error"
+msgstr "Грешка во поврзувањето"
+
+#: ../js/ui/telepathyClient.js:735
+#, c-format
+msgid "%s is online."
+msgstr "%s е онлајн."
+
+#: ../js/ui/telepathyClient.js:740
+#, c-format
+msgid "%s is offline."
+msgstr "%s е офлајн."
+
+#: ../js/ui/telepathyClient.js:743
+#, c-format
+msgid "%s is away."
+msgstr "%s е отсутен/на."
+
+#: ../js/ui/telepathyClient.js:746
+#, c-format
+msgid "%s is busy."
+msgstr "%s е зафатен(а)."
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/telepathyClient.js:977
+#, no-c-format
+msgid "Sent at <b>%X</b> on <b>%A</b>"
+msgstr "Испратено на <b>%X</b> на <b>%A</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25",
+#. shown when you get a chat message in the same year.
+#: ../js/ui/telepathyClient.js:983
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+msgstr "Испратено на <b>%A</b>, <b>%d %B</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
+#. shown when you get a chat message in a different year.
+#: ../js/ui/telepathyClient.js:988
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+msgstr "Испратено на <b>%A</b>, <b>%B %d</b>, %Y"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/telepathyClient.js:1030
+#, c-format
+msgid "%s is now known as %s"
+msgstr "%s е сега познат(а) како %s"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1132
+#, c-format
+msgid "Invitation to %s"
+msgstr "Покана до %s"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/telepathyClient.js:1140
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr "%s Ве поканува да се придружите во %s"
+
+#: ../js/ui/telepathyClient.js:1142
+#: ../js/ui/telepathyClient.js:1222
+#: ../js/ui/telepathyClient.js:1320
+msgid "Decline"
+msgstr "Одбиј"
+
+#: ../js/ui/telepathyClient.js:1143
+#: ../js/ui/telepathyClient.js:1223
+#: ../js/ui/telepathyClient.js:1321
+msgid "Accept"
+msgstr "Прифати"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1173
+#, c-format
+msgid "Video call from %s"
+msgstr "Видео повик од %s"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1176
+#, c-format
+msgid "Call from %s"
+msgstr "Повик од %s"
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/telepathyClient.js:1183
+msgid "Answer"
+msgstr "Одговори"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/telepathyClient.js:1216
+#, c-format
+msgid "%s is sending you %s"
+msgstr "%s Ви праќа %s"
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/telepathyClient.js:1285
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s бара дозвола да гледа кога сте онлајн"
+
+#: ../js/ui/telepathyClient.js:1378
+msgid "Network error"
+msgstr "Грешка во мрежата"
+
+#: ../js/ui/telepathyClient.js:1380
+msgid "Authentication failed"
+msgstr "Автентикацијата не успеа"
+
+#: ../js/ui/telepathyClient.js:1382
+msgid "Encryption error"
+msgstr "Грешка во енкрипцијата"
+
+#: ../js/ui/telepathyClient.js:1384
+msgid "Certificate not provided"
+msgstr "Не е обезбеден сертификат"
+
+#: ../js/ui/telepathyClient.js:1386
+msgid "Certificate untrusted"
+msgstr "Сертификатот не е доверлив"
+
+#: ../js/ui/telepathyClient.js:1388
+msgid "Certificate expired"
+msgstr "Сертификатот е истечен"
+
+#: ../js/ui/telepathyClient.js:1390
+msgid "Certificate not activated"
+msgstr "Сертификатот не е активиран"
+
+#: ../js/ui/telepathyClient.js:1392
+msgid "Certificate hostname mismatch"
+msgstr "Името на хостот на сертификатот не одговара"
+
+#: ../js/ui/telepathyClient.js:1394
+msgid "Certificate fingerprint mismatch"
+msgstr "Отпечатокот на сертификатот не одговара"
+
+#: ../js/ui/telepathyClient.js:1396
+msgid "Certificate self-signed"
+msgstr "Сертификатот е самопотпишан"
+
+#: ../js/ui/telepathyClient.js:1398
+msgid "Status is set to offline"
+msgstr "Статусот е поставен на „офлајн“"
+
+#: ../js/ui/telepathyClient.js:1400
+msgid "Encryption is not available"
+msgstr "Енкрипцијата не е достапна"
+
+#: ../js/ui/telepathyClient.js:1402
+msgid "Certificate is invalid"
+msgstr "Сертификатот не е валиден"
+
+#: ../js/ui/telepathyClient.js:1404
+msgid "Connection has been refused"
+msgstr "Врската беше одбиена"
+
+#: ../js/ui/telepathyClient.js:1406
+msgid "Connection can't be established"
+msgstr "Врската не може да се воспостави"
+
+#: ../js/ui/telepathyClient.js:1408
+msgid "Connection has been lost"
+msgstr "Врската е загубена"
+
+#: ../js/ui/telepathyClient.js:1410
+msgid "This resource is already connected to the server"
+msgstr "Ресурсот е веќе поврзан на серверот"
+
+#: ../js/ui/telepathyClient.js:1412
+msgid "Connection has been replaced by a new connection using the same resource"
+msgstr "Врската е заменета со нова врска со користење на истиот ресурс"
+
+#: ../js/ui/telepathyClient.js:1414
+msgid "The account already exists on the server"
+msgstr "Сметката веќе постои на серверот"
+
+#: ../js/ui/telepathyClient.js:1416
+msgid "Server is currently too busy to handle the connection"
+msgstr "Серверот е моментално презафатен за да се справи со врската"
+
+#: ../js/ui/telepathyClient.js:1418
+msgid "Certificate has been revoked"
+msgstr "Сертификатот е отповикан"
+
+#: ../js/ui/telepathyClient.js:1420
+msgid "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr "Сертификатот користи небезбеден алгоритам за шифрирање или е криптографски слаб"
+
+#: ../js/ui/telepathyClient.js:1422
+msgid "The length of the server certificate, or the depth of the server certificate chain, exceed the limits imposed by the cryptography library"
+msgstr "Должината на сертификатот од серверот или длабочината на веригата од сертификати ги надминува границите на библиотеката за криптографија"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1432
+#, c-format
+msgid "Connection to %s failed"
+msgstr "Врската со %s не успеа"
+
+#: ../js/ui/telepathyClient.js:1441
+msgid "Reconnect"
+msgstr "Поврзи се пак"
+
+#: ../js/ui/telepathyClient.js:1442
+msgid "Edit account"
+msgstr "Уреди ја сметката"
+
+#: ../js/ui/telepathyClient.js:1488
+msgid "Unknown reason"
+msgstr "Непозната причина"
+
+#: ../js/ui/userMenu.js:133
+msgid "Hidden"
+msgstr "Скриен"
+
+#: ../js/ui/userMenu.js:139
+msgid "Idle"
+msgstr "Неактивен"
+
+#: ../js/ui/userMenu.js:142
+msgid "Unavailable"
+msgstr "Недостапен"
+
+#: ../js/ui/userMenu.js:576
+#: ../js/ui/userMenu.js:580
+#: ../js/ui/userMenu.js:650
+msgid "Power Off..."
+msgstr "Исклучи се..."
+
+#: ../js/ui/userMenu.js:612
+msgid "Notifications"
+msgstr "Известувања"
+
+#: ../js/ui/userMenu.js:620
+msgid "Online Accounts"
+msgstr "Онлајн сметки"
+
+#: ../js/ui/userMenu.js:624
+msgid "System Settings"
+msgstr "Поставувања за системот"
+
+#: ../js/ui/userMenu.js:631
+msgid "Lock Screen"
+msgstr "Заклучи го екранот"
+
+#: ../js/ui/userMenu.js:636
+msgid "Switch User"
+msgstr "Смени корисник"
+
+#: ../js/ui/userMenu.js:641
+msgid "Log Out..."
+msgstr "Одјави се..."
+
+#: ../js/ui/userMenu.js:669
+msgid "Your chat status will be set to busy"
+msgstr "Вашиот статус на разговорите ќе биде поставен на „зафатен“"
+
+#: ../js/ui/userMenu.js:670
+msgid "Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."
+msgstr "Известувањата сега се оневозможени, вклучувајќи ги и тие на разговорите. Вашиот онлајн статус е прилагоден за да им се каже на другите дека Вие нема да ги видите нивните пораки."
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/viewSelector.js:113
+msgid "Type to search..."
+msgstr "Внесете текст за пребарување..."
+
+#: ../js/ui/viewSelector.js:131
+#: ../src/shell-util.c:244
+msgid "Search"
+msgstr "Пребарај"
+
+#: ../js/ui/windowAttentionHandler.js:33
+#, c-format
+msgid "'%s' is ready"
+msgstr "„%s“ е подготвен"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1100
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u излез"
+msgstr[1] "%u излези"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1110
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u излез"
+msgstr[1] "%u излези"
+
+#: ../src/gvc/gvc-mixer-control.c:1408
+msgid "System Sounds"
+msgstr "Системски звуци"
+
+#: ../src/main.c:508
+msgid "Print version"
+msgstr "Печатена верзија"
+
+#: ../src/main.c:514
+msgid "Mode used by GDM for login screen"
+msgstr "Модулот кој се користи од GDM за најавниот екран"
+
+#: ../src/shell-app.c:567
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr "Не успеав да го пуштам „%s“"
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr "Обединетото Кралство"
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr "Стандардно"
+
+#: ../src/shell-polkit-authentication-agent.c:334
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Дијалогот за автентикација беше исклучен од корисникот"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:89
+msgid "Home"
+msgstr "Дома"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:98
+msgid "File System"
+msgstr "Датотечен систем"
+
+#. Translators: the first string is the name of a gvfs
+#. * method, and the second string is a path. For
+#. * example, "Trash: some-directory". It means that the
+#. * directory called "some-directory" is in the trash.
+#.
+#: ../src/shell-util.c:294
+#, c-format
+msgid "%1$s: %2$s"
+msgstr "%1$s: %2$s"
+
diff --git a/po/ml.po b/po/ml.po
new file mode 100644
index 0000000..15dec42
--- /dev/null
+++ b/po/ml.po
@@ -0,0 +1,2852 @@
+# Malayalam translation for gnome-shell.
+# Copyright (C) 2010 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Anish A <aneesh.nl@gmail.com>, 2010, 2011, 2013.
+# Mohammed Sadiq <sadiqpkp@gmail.com>, 2012.
+# Praveen Arimbrathodiyil <pravi.a@gmail.com>, 2012.
+# Ani Peter <apeter@redhat.com>, 2012, 2013.
+# Ranjith Siji <ranjith.sajeev@gmail.com>, 2018.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2018-10-10 19:07+0000\n"
+"PO-Revision-Date: 2018-10-26 13:50+0530\n"
+"Last-Translator: Ranjith Siji <ranjith.sajeev@gmail.com>\n"
+"Language-Team: American English <kde-i18n-doc@kde.org>\n"
+"Language: ml\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "സിസ്റ്റം"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "അറിയിപ്പുകള്‍ കാണിക്കുക"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "സജീവമായ അറിയിപ്പിനെ കേന്ദ്രീകരിക്കുക"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "പൊതുവായ അവലോകനം കാണിക്കുക"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "എല്ലാ പ്രയോഗങ്ങളും കാണിയ്ക്കുക"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "പ്രയോഗത്തിന്റെ മെനു തുറക്കുക"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4 js/extensionPrefs/main.js:142
+msgid "Shell Extensions"
+msgstr "ഷെല്‍ എക്സ്റ്റെന്‍ഷനുകള്‍"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "ഗ്നോം ഷെല്‍ എക്സ്റ്റെന്‍ഷനുകള്‍ ക്രമീകരിയ്ക്കുക"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "ഗ്നോം ഷെല്‍"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "വിന്‍ഡോ കൈകാര്യം ചെയ്യലും പ്രയോഗം ലഭ്യമാക്കലും"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Alt-F2-ല്‍ ഡവലപ്പര്‍മാര്‍ക്കും ടെസ്റ്റേര്‍സിനും പ്രയോജനകരമായ ആന്തരിക പ്രയോഗങ്ങള്‍ പ്രവര്‍ത്തന സജ്ജമാക്കുന്നു"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid "Allows access to internal debugging and monitoring tools using the Alt-F2 dialog."
+msgstr "Alt-F2 ഡയലോഗ് ഉപയോഗിച്ച് ആന്തരിക ഡീബഗ്ഗിലേക്കും നീരീക്ഷണ പ്രയോഗങ്ങളിലേക്കും പ്രവേശനം അനുവദിയ്ക്കുക."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "പ്രവര്‍ത്തന സജ്ജമാക്കുന്നതിനുള്ള എക്സ്റ്റെന്‍ഷനുകളുടെ യുയുഐഡികള്‍"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which should be loaded. Any extension "
+"that wants to be loaded needs to be in this list. You can also manipulate this list with the EnableExtension "
+"and DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"ഗ്നോം ഷെല്‍ എക്സ്റ്റെന്‍ഷനുകള്‍ക്ക് ഒരു യുയുഐഡി വിശേഷതയുണ്ട്; ലഭ്യമാക്കേണ്ട എക്സ്റ്റെന്‍ഷനുകള്‍ ഈ കീ പട്ടികപ്പെടുത്തുന്നു. ലഭ്യമാക്കേണ്ട ഏതു് എക്സ്റ്റെന്‍"
+"ഷനും ഈ പട്ടികയിലുണ്ടാവണം. org.gnome.Shell-ല്‍ നിങ്ങള്‍ക്കു് EnableExtension, DisableExtension എന്നീ ഡീബസ് രീതികളിലൂടെ ഈ "
+"പട്ടിക കൈകാര്യം ചെയ്യുവാനും സാധിയ്ക്കുന്നു."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disable user extensions"
+msgstr "ഉപയോക്തൃ എക്സ്റ്റന്‍ഷനുകള്‍ പ്രവര്‍ത്തനരഹിതമാക്കുക"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid "Disable all extensions the user has enabled without affecting the “enabled-extension” setting."
+msgstr "“enabled-extension” എന്ന ക്രമീകരണത്തിനെ ബാധിക്കാതെ യൂസര്‍ പ്രവര്‍ത്തിപ്പിച്ച എല്ലാ എക്സ്റ്റന്‍ഷനുകളും പ്രവര്‍ത്തനരഹിതമാക്കുക."
+
+#: data/org.gnome.shell.gschema.xml.in:34
+msgid "Disables the validation of extension version compatibility"
+msgstr "എക്സ്റ്റന്‍ഷന്റെ വെര്‍ഷന്‍ പൊരുത്ത സാധുത പരിശോധന പ്രവര്‍ത്തനരഹിതമാക്കുക"
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current running version. Enabling this "
+"option will disable this check and try to load all extensions regardless of the versions they claim to "
+"support."
+msgstr ""
+"ഇപ്പോള്‍ പ്രവര്‍ത്തിച്ചുകൊണ്ടിരിക്കുന്ന വെര്‍ഷനെ പിന്‍തുണയ്ക്കുന്ന എക്സ്റ്റന്‍ഷനുകളെമാത്രമേ ഗ്നോം ഷെല്‍ പ്രവര്‍ത്തിപ്പിക്കുകയുള്ളു. ഈ ഓപ്ഷന്‍ ഓണാക്കിയാല്‍ ഈ "
+"പരിശോധന ഇല്ലാതാവുകയും എല്ലാ എക്സ്റ്റന്‍ഷനുകളെയും പ്രവര്‍ത്തിപ്പിക്കാന്‍ ശ്രമിക്കുകയും ചെയ്യും, അതില്‍ വെര്‍ഷന്‍ പിന്‍തുണ പരിഗണിക്കുകയില്ല."
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "List of desktop file IDs for favorite applications"
+msgstr "ഇഷ്ടമുള്ള പ്രയോഗങ്ങള്‍ക്കുള്ള പണിയിട ഫയല്‍ ഐഡികളുടെ പട്ടിക"
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid "The applications corresponding to these identifiers will be displayed in the favorites area."
+msgstr "ഈ ഐഡന്റിഫയറുകള്‍ക്കുള്ള പ്രയോഗങ്ങള്‍ ഉചിതമായ സ്ഥലങ്ങളില്‍ കാണിയ്ക്കുന്നു."
+
+#: data/org.gnome.shell.gschema.xml.in:51
+msgid "App Picker View"
+msgstr "ആപ്പ് പിക്കര്‍ കാഴ്ച"
+
+#: data/org.gnome.shell.gschema.xml.in:52
+msgid "Index of the currently selected view in the application picker."
+msgstr "ആപ്ലിക്കേഷന്‍ പിക്കറില്‍ ഇപ്പോള്‍ തിരഞ്ഞെടുത്ത കാഴ്ചരീതിയുടെ ഉള്ളടക്കം."
+
+#: data/org.gnome.shell.gschema.xml.in:58
+msgid "History for command (Alt-F2) dialog"
+msgstr "കമാന്‍ഡ് (Alt-F2) ഡയലോഗിന്റെ നാള്‍വഴി"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "History for the looking glass dialog"
+msgstr "തെരച്ചില്‍ ഡയലോഗിനുള്ള നാള്‍വഴി"
+
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "ഉപയോക്തൃ മെനുവില്‍ 'പുറത്തിറങ്ങുക' കാണിക്കുക."
+
+#: data/org.gnome.shell.gschema.xml.in:68
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-user, single-session situations."
+msgstr "സിംഗിള്‍ യൂസര്‍, സിംഗിള്‍ സെഷനില്‍ 'ലോഗൌട്ട്' മെനുവസ്തു തനിയെ അദൃശ്യമാകുന്നത് ഈ കീ മറികടക്കുന്നു."
+
+#: data/org.gnome.shell.gschema.xml.in:75
+msgid "Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "എന്‍ക്രിപ്റ്റ് ചെയ്തതോ വിദൂരമോ ആയ ഫയല്‍സിസ്റ്റങ്ങള്‍ മൌണ്ട് ചെയ്യുമ്പോഴുള്ള രഹസ്യവാക്ക് ഓര്‍മ്മിക്കണോ എന്ന്"
+
+#: data/org.gnome.shell.gschema.xml.in:76
+msgid ""
+"The shell will request a password when an encrypted device or a remote filesystem is mounted. If the "
+"password can be saved for future use a “Remember Password” checkbox will be present. This key sets the "
+"default state of the checkbox."
+msgstr ""
+"ഒരു എന്‍ക്രിപ്റ്റ് ചെയ്ത ഡിവൈസ് അല്ലെങ്കില്‍ വിദൂര ഫയല്‍സിസ്റ്റം മൌണ്ട് ചെയ്യുമ്പോള്‍ ഷെല്‍ ഒരു രഹസ്യവാക്ക് ആവശ്യപ്പെടുന്നു. രഹസ്യവാക്ക് സൂക്ഷിയ്ക്കുവാന്‍ "
+"സാധ്യമെങ്കില്‍, 'രഹസ്യവാക്ക് ഓര്‍ത്തു്വയ്ക്കുക' ചെക്ക്ബോക്സ് കാണാം. ഈ കീ ചെക്ക്ബോക്സിന്റെ സ്വതവേയുള്ള അവസ്ഥ സജ്ജമാക്കുന്നു."
+
+#: data/org.gnome.shell.gschema.xml.in:85
+msgid "Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "സ്വതേയുള്ള ബ്ലൂടൂത്ത് അഡാപ്റ്റര്‍ അതുമായി ബന്ധമുള്ള ഉപകരണങ്ങളുമായി സെറ്റപ്പ് ചെയ്തുവോ"
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is powered, or if there were devices "
+"set up associated with the default adapter. This will be reset if the default adapter is ever seen not to "
+"have devices associated to it."
+msgstr ""
+"ബ്ലൂടൂത്ത് അഡാപ്റ്ററിന് വൈദ്യുതി ലഭിച്ചാല്‍ മാത്രമേ ഷെല്‍ ബ്ലൂടൂത്ത് മെനു പ്രദര്‍ശിപ്പിക്കുകയുള്ളു, അല്ലെങ്കില്‍ സ്വതേയുള്ള ബ്ലൂടൂത്ത് അഡാപ്റ്ററുമായി "
+"ഉപകരണങ്ങള്‍ ബന്ധപ്പെടുത്തിയിരിക്കണം. ഇത് സ്വതേയുള്ള ബ്ലൂടൂത്ത് അഡാപ്റ്റര്‍ കാണുകയും അതുമായി ഉപകരണങ്ങള്‍ ബന്ധപ്പെടുത്തിയിട്ടില്ലെങ്കിലും റീസെറ്റ് "
+"ചെയ്യും."
+
+#: data/org.gnome.shell.gschema.xml.in:101
+msgid "Keybinding to open the application menu"
+msgstr "പ്രയോഗത്തിന്റെ മെനു തുറക്കുന്നതിനുള്ള കീക്കൂട്ടം"
+
+#: data/org.gnome.shell.gschema.xml.in:102
+msgid "Keybinding to open the application menu."
+msgstr "പ്രയോഗത്തിന്റെ മെനു തുറക്കുന്നതിനുള്ള കീക്കൂട്ടം."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "\"പ്രയോഗങ്ങള്‍ കാണിയ്ക്കുക\" എന്ന കാഴ്ച തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "പ്രവര്‍ത്തികളുടെ അവലോകനത്തിന്റെ \"പ്രയോഗങ്ങള്‍ കാണിയ്ക്കുക\" എന്ന കാഴ്ച തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്."
+
+#: data/org.gnome.shell.gschema.xml.in:116
+msgid "Keybinding to open the overview"
+msgstr "പൊതുവായ അവലോകനം തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്"
+
+#: data/org.gnome.shell.gschema.xml.in:117
+msgid "Keybinding to open the Activities Overview."
+msgstr "പ്രയോഗങ്ങളുടെ പൊതുവായ അവലോകനം എന്ന കാഴ്ച തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്."
+
+#: data/org.gnome.shell.gschema.xml.in:123
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "അറിയിപ്പ് പട്ടികയുടെ ദൃശ്യത ടൊഗ്ഗിള്‍ ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
+
+#: data/org.gnome.shell.gschema.xml.in:124
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "അറിയിപ്പ് പട്ടികയുടെ ദൃശ്യത ടൊഗ്ഗിള്‍ ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം."
+
+#: data/org.gnome.shell.gschema.xml.in:130
+msgid "Keybinding to focus the active notification"
+msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്‍ഡിങ്"
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to focus the active notification."
+msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്‍ഡിങ്."
+
+#: data/org.gnome.shell.gschema.xml.in:137
+msgid "Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr "ഡീബഗ്ഗിംഗ് ആവശ്യങ്ങള്‍ക്കായി പ്രവര്‍ത്തിച്ചുകൊണ്ടിരിക്കുന്ന എല്ലാ ട്വീനുകളെയും നിറുത്തുകയോ തുടരുകയോ ചെയ്യാനുള്ള കീബന്ധനങ്ങള്‍"
+
+#: data/org.gnome.shell.gschema.xml.in:146
+msgid "Which keyboard to use"
+msgstr "ഏതു് കീബോര്‍ഡ് ഉപയോഗിയ്ക്കണം"
+
+#: data/org.gnome.shell.gschema.xml.in:147
+msgid "The type of keyboard to use."
+msgstr "ഏതു് തരം കീബോര്‍ഡ് ഉപയോഗിയ്ക്കണം."
+
+#: data/org.gnome.shell.gschema.xml.in:158 data/org.gnome.shell.gschema.xml.in:185
+msgid "Limit switcher to current workspace."
+msgstr "സ്വിച്ചറിനെ ഇപ്പോഴുള്ള പ്രവര്‍ത്തനസ്ഥലത്തുമാത്രമായി തടയുക."
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid ""
+"If true, only applications that have windows on the current workspace are shown in the switcher. Otherwise, "
+"all applications are included."
+msgstr ""
+"വില true എന്നാണെങ്കില്‍, ഇപ്പോഴുള്ള പ്രവര്‍ത്തനയിടത്തില്‍ ജാലകങ്ങളുള്ള ആപ്ലിക്കേഷനുകള്‍ മാത്രം സ്വിച്ചറില്‍ കാണിക്കുക. അല്ലെങ്കില്‍ എല്ലാ "
+"ആപ്ലിക്കേഷനുകളും കാണിക്കും."
+
+#: data/org.gnome.shell.gschema.xml.in:176
+msgid "The application icon mode."
+msgstr "പ്രയോഗത്തിന്റെ ഐക്കണ്‍ മോഡ്."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities are “thumbnail-only” (shows a "
+"thumbnail of the window), “app-icon-only” (shows only the application icon) or “both”."
+msgstr ""
+"സ്വിച്ചറില്‍ ജാലകങ്ങള്‍ എങ്ങനെ കാണിയ്ക്കുന്നു എന്നു് ക്രമീകരിയ്ക്കുന്നു. ശരിയായ സാധ്യതകള്‍: 'thumbnail-only' (ജാലകത്തിന്റെ പ്രതിരൂപം "
+"കാണിയ്ക്കുന്നു), 'app-icon-only' (പ്രയോഗത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു) അല്ലെങ്കില്‍ 'both'."
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. Otherwise, all windows are "
+"included."
+msgstr ""
+"വില true എന്നാണെങ്കില്‍, ഇപ്പോഴുള്ള പ്രവര്‍ത്തനയിടത്തിലുള്ള ജാലകങ്ങള്‍ മാത്രം സ്വിച്ചറില്‍ കാണിക്കുക. അല്ലെങ്കില്‍ എല്ലാ ജാലകങ്ങളും കാണിക്കും."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Attach modal dialog to the parent window"
+msgstr "പേരന്റ് ജാലകത്തിലേക്കു് ഡയലോഗ് ചേര്‍ക്കുക"
+
+#: data/org.gnome.shell.gschema.xml.in:199 data/org.gnome.shell.gschema.xml.in:208
+#: data/org.gnome.shell.gschema.xml.in:216 data/org.gnome.shell.gschema.xml.in:224
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "ഗ്നോം ഷെല്‍ പ്രവര്‍ത്തിയ്ക്കുമ്പോള്‍ org.gnome.mutter-ലുള്ള കീ ഈ കീ തിരുത്തിയെഴുതുന്നു."
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "സ്ക്രീന്‍ കോണുകളില്‍ ജാലകങ്ങള്‍ എത്തിയ്ക്കുമ്പോള്‍ കോണ്‍ ചരിയ്ക്കുന്നതിനായി പ്രവര്‍ത്തന സജ്ജമാക്കുക"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Workspaces are managed dynamically"
+msgstr "പണിയിടങ്ങള്‍ ഡയനാമിക്കായി കൈകാര്യം ചെയ്തിരിക്കുന്നു"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Workspaces only on primary monitor"
+msgstr "പ്രധാന മോണിറ്ററില്‍ മാത്രം പണിയിടങ്ങള്‍"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "പോയന്ററിന്റെ ചലനം അവസാനിക്കുന്നതുവരെ മൗസ് മോഡില്‍ ഫോക്കസ് മാറുന്നത് തടയുക"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "നെറ്റ്​വര്‍ക്ക് ലോഗിന്‍"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:112
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s നുള്ള മുന്‍ഗണനാ ഡയലോഗ് ലഭ്യമാക്കുന്നതില്‍ പിശക്:"
+
+#: js/gdm/authPrompt.js:147 js/ui/audioDeviceSelection.js:62 js/ui/components/networkAgent.js:117
+#: js/ui/components/polkitAgent.js:153 js/ui/endSessionDialog.js:442 js/ui/extensionDownloader.js:197
+#: js/ui/shellMountOperation.js:345 js/ui/status/network.js:905
+msgid "Cancel"
+msgstr "റദ്ദാക്കുക"
+
+#: js/gdm/authPrompt.js:166 js/gdm/authPrompt.js:209 js/gdm/authPrompt.js:441
+msgid "Next"
+msgstr "അടുത്തത്"
+
+#: js/gdm/authPrompt.js:205 js/ui/shellMountOperation.js:349 js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "പൂട്ട് തുറക്കുക"
+
+#: js/gdm/authPrompt.js:207
+msgctxt "button"
+msgid "Sign In"
+msgstr "അകത്തുകയറുക"
+
+#: js/gdm/loginDialog.js:319
+msgid "Choose Session"
+msgstr "സെഷന്‍ തെരഞ്ഞെടുക്കുക"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "ലഭ്യമല്ലേ?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:891
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(e.g., ഉപയോക്താവു് അല്ലെങ്കില്‍ %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:896 js/ui/components/networkAgent.js:243 js/ui/components/networkAgent.js:263
+#: js/ui/components/networkAgent.js:281
+msgid "Username: "
+msgstr "ഉപയോക്തൃ നാമം: "
+
+#: js/gdm/loginDialog.js:1234
+msgid "Login Window"
+msgstr "പ്രവേശന ജാലകം"
+
+#: js/gdm/util.js:345
+msgid "Authentication error"
+msgstr "തിരിച്ചറിയല്‍ പിശക്"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:485
+msgid "(or swipe finger)"
+msgstr "(അല്ലെങ്കില്‍ ഫിംഗര്‍ സ്വൈപ്പ് ചെയ്യുക)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:97
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "നിറുത്തുക"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:100
+msgid "power off;shutdown;reboot;restart"
+msgstr "power off;shutdown;reboot;restart"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:104
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "സ്ക്രീൻ പൂട്ടുക"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:107
+msgid "lock screen"
+msgstr "സ്ക്രീന്‍ പൂട്ടുക"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:111
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "പുറത്തിറങ്ങുക"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:114
+msgid "logout;sign off"
+msgstr "logout;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:118
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "താത്കാലികമായി നിര്‍ത്തുക"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:121
+msgid "suspend;sleep"
+msgstr "suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:125
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "ഉപയോക്താവിനെ മാറ്റുക"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:128
+msgid "switch user"
+msgstr "ഉപയോക്താവിനെ മാറ്റുക"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:132
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr "വിന്യാസം ബന്ധിക്കുക"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:135
+msgid "lock orientation;screen;rotation"
+msgstr "lock orientation;screen;rotation"
+
+#: js/misc/util.js:122
+msgid "Command not found"
+msgstr "ആജ്ഞ കണ്ടില്ല"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:155
+msgid "Could not parse command:"
+msgstr "ആജ്ഞ പ്രാവര്‍ത്തികമാക്കാന്‍ സാധ്യമായില്ല:"
+
+#: js/misc/util.js:163
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "\"%s\" നടപ്പിലാക്കുന്നതില്‍ പരാജയപ്പെട്ടു :"
+
+#: js/misc/util.js:180
+msgid "Just now"
+msgstr "ഇപ്പോള്‍ മാത്രം"
+
+#: js/misc/util.js:182
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d മിനിറ്റ് മുന്‍പ്"
+msgstr[1] "%d മിനിറ്റുകള്‍ക്ക് മുന്‍പ്"
+
+#: js/misc/util.js:185
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d മണിക്കൂര്‍ മുന്‍പ്"
+msgstr[1] "%d മണിക്കൂറുകള്‍ മുന്‍പ്"
+
+#: js/misc/util.js:188
+msgid "Yesterday"
+msgstr "ഇന്നലെ"
+
+#: js/misc/util.js:190
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d ദിവസം മുന്‍പ്"
+msgstr[1] "%d ദിവസങ്ങള്‍ക്ക് മുന്‍പ്"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d ആഴ്ച മുന്‍പ്"
+msgstr[1] "%d ആഴ്ചകള്‍ക്ക് മുന്‍പ്"
+
+#: js/misc/util.js:196
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d മാസം മുമ്പ്"
+msgstr[1] "%d മാസങ്ങള്‍ക്ക് മുമ്പ്"
+
+#: js/misc/util.js:198
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d വര്‍ഷം മുമ്പ്"
+msgstr[1] "%d വര്‍ഷങ്ങള്‍ക്കു മുമ്പ്"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:228
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:234
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "ഇന്നലെ, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:240
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:246
+#, no-c-format
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:252
+#, no-c-format
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:257
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:263
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "ഇന്നലെ, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:269
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:275
+#, no-c-format
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:281
+#, no-c-format
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:49
+msgid "Hotspot Login"
+msgstr "ഹോട്സ്പോട്ട് ലോഗിന്‍"
+
+#: js/portalHelper/main.js:95
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other information you enter on this page "
+"can be viewed by people nearby."
+msgstr "ഈ ഹോട്സ്പോട്ടിലേക്ക് ലോഗിന്‍ ചെയ്യുന്നത് സുരക്ഷിതമല്ല. ഈ പേജില്‍ നിങ്ങള്‍ ചേര്‍ക്കുന്ന രഹസ്യവാക്ക് ചുറ്റുമുള്ളവര്‍ക്ക് കാണാം."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:46 js/ui/status/location.js:376
+msgid "Deny Access"
+msgstr "അനുമതി റദ്ദാക്കുക"
+
+#: js/ui/accessDialog.js:47 js/ui/status/location.js:379
+msgid "Grant Access"
+msgstr "അനുമതി നല്‍കുക"
+
+#: js/ui/appDisplay.js:790
+msgid "Frequently used applications will appear here"
+msgstr "ഇടയ്ക്കിടയ്ക്ക് ഉപയോഗിക്കുന്ന പ്രയോഗങ്ങള്‍ ഇവിടെ പ്രത്യക്ഷമാകും"
+
+#: js/ui/appDisplay.js:911
+msgid "Frequent"
+msgstr "ഇടയ്ക്കിടെ"
+
+#: js/ui/appDisplay.js:918
+msgid "All"
+msgstr "എല്ലാം"
+
+#: js/ui/appDisplay.js:1887
+msgid "New Window"
+msgstr "പുതിയ വിന്‍ഡോ"
+
+#: js/ui/appDisplay.js:1901
+msgid "Launch using Dedicated Graphics Card"
+msgstr "ഇതിനായുള്ള ഗ്രാഫിക്ക്സ് കാര്‍ഡ് ഉപയോഗിച്ച് ലോഞ്ച് ചെയ്യുക"
+
+#: js/ui/appDisplay.js:1928 js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "ഇഷ്ടപ്പെട്ടവയില്‍ നിന്നും നീക്കം ചെയ്യുക"
+
+#: js/ui/appDisplay.js:1934
+msgid "Add to Favorites"
+msgstr "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ക്കുക"
+
+#: js/ui/appDisplay.js:1944
+msgid "Show Details"
+msgstr "വിവരങ്ങള്‍ കാണിക്കുക"
+
+#: js/ui/appFavorites.js:140
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s നിങ്ങള്‍ക്കിഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തിട്ടുണ്ട്."
+
+#: js/ui/appFavorites.js:174
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s നിങ്ങള്‍ക്കിഷ്ടപ്പെട്ടവയില്‍ നിന്നും നീക്കം ചെയ്തിട്ടുണ്ട്."
+
+#: js/ui/audioDeviceSelection.js:50
+msgid "Select Audio Device"
+msgstr "ഡിവൈസിലേക്കു് ഫയലുകള്‍ അയയ്ക്കുക"
+
+#: js/ui/audioDeviceSelection.js:60
+msgid "Sound Settings"
+msgstr "ശബ്ദത്തിന്റെ ക്രമീകരണങ്ങള്‍"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "ഹെഡ്ഫോണ്‍സ്"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "ഹെഡ്സെറ്റ്"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:255
+msgid "Microphone"
+msgstr "മൈക്രോഫോണ്‍"
+
+#: js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "പശ്ചാത്തലം മാറ്റുക…"
+
+#: js/ui/backgroundMenu.js:21 js/ui/status/nightLight.js:47
+msgid "Display Settings"
+msgstr "ദൃശ്യക്രമീകരണങ്ങള്‍"
+
+#: js/ui/backgroundMenu.js:22
+msgid "Settings"
+msgstr "സജ്ജീകരണങ്ങള്‍"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:44
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "[calender-no-work]06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:73
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ഞാ"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:75
+msgctxt "grid monday"
+msgid "M"
+msgstr "തി"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:77
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "ചൊ"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:79
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "ബു"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:81
+msgctxt "grid thursday"
+msgid "T"
+msgstr "വ്യാ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:83
+msgctxt "grid friday"
+msgid "F"
+msgstr "വെ"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:85
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ശ"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:380
+msgid "%OB"
+msgstr "OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:390
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:447
+msgid "Previous month"
+msgstr "കഴിഞ്ഞ മാസം"
+
+#: js/ui/calendar.js:457
+msgid "Next month"
+msgstr "അടുത്ത മാസം"
+
+#: js/ui/calendar.js:610
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "[date day number format]%d"
+
+#: js/ui/calendar.js:665
+msgid "Week %V"
+msgstr "ആഴ്ച %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:734
+msgctxt "event list time"
+msgid "All Day"
+msgstr "ദിനം മുഴുവനും"
+
+#: js/ui/calendar.js:869
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: js/ui/calendar.js:873
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: js/ui/calendar.js:1103
+msgid "No Notifications"
+msgstr "അറിയിപ്പുകളില്ല"
+
+#: js/ui/calendar.js:1106
+msgid "No Events"
+msgstr "ഇവന്റുകളില്ല"
+
+#: js/ui/calendar.js:1134
+msgid "Clear All"
+msgstr "എല്ലാം മായ്ക്കുക"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:50
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "\"%s\" പ്രതികരിക്കുന്നില്ല."
+
+#: js/ui/closeDialog.js:51
+msgid "You may choose to wait a short while for it to continue or force the application to quit entirely."
+msgstr ""
+"പ്രയോഗം പ്രവര്‍ത്തിച്ചു തുടങ്ങാന്‍ കുറച്ചു സമയം കൂടി കാത്തു നില്‍ക്കുകയോ അല്ലെങ്കില്‍ പ്രവര്‍ത്തനം നിര്‍ത്താന്‍ ഫോഴ്സ് ക്വിറ്റ് ചെയ്യുകയോ ചെയ്യാം."
+
+#: js/ui/closeDialog.js:67
+msgid "Force Quit"
+msgstr "ഫോഴ്സ് ക്വിറ്റ്"
+
+#: js/ui/closeDialog.js:70
+msgid "Wait"
+msgstr "കാത്തുനില്‍ക്കുക"
+
+#: js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "പുറമേയുള്ള ഡ്രൈവ് ബന്ധിപ്പിച്ചു"
+
+#: js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "പുറമേയുള്ള ഡ്രൈവ് വിച്ഛേദിച്ചു"
+
+#: js/ui/components/autorunManager.js:348
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ഉപയോഗിച്ചു തുറക്കുക"
+
+#: js/ui/components/keyring.js:107 js/ui/components/polkitAgent.js:297
+msgid "Password:"
+msgstr "രഹസ്യവാക്ക്:"
+
+#: js/ui/components/keyring.js:140
+msgid "Type again:"
+msgstr "വീണ്ടും അമര്‍ത്തുക:"
+
+#: js/ui/components/networkAgent.js:112 js/ui/status/network.js:229 js/ui/status/network.js:320
+#: js/ui/status/network.js:908
+msgid "Connect"
+msgstr "കണക്ട് ചെയ്യുക"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:211 js/ui/components/networkAgent.js:223
+#: js/ui/components/networkAgent.js:246 js/ui/components/networkAgent.js:265
+#: js/ui/components/networkAgent.js:285 js/ui/components/networkAgent.js:295
+msgid "Password: "
+msgstr "നിശബ്ദമാക്കുക: "
+
+#. static WEP
+#: js/ui/components/networkAgent.js:216
+msgid "Key: "
+msgstr "കീ: "
+
+#: js/ui/components/networkAgent.js:249 js/ui/components/networkAgent.js:271
+msgid "Private key password: "
+msgstr "സ്വകാര്യ കീ രഹസ്യവാക്ക്: "
+
+#: js/ui/components/networkAgent.js:269
+msgid "Identity: "
+msgstr "ഐഡന്റിറ്റി: "
+
+#: js/ui/components/networkAgent.js:283
+msgid "Service: "
+msgstr "സേവനം: "
+
+#: js/ui/components/networkAgent.js:312 js/ui/components/networkAgent.js:688
+msgid "Authentication required by wireless network"
+msgstr "വയര്‍ലെസ്സ് ശൃംഖല നിങ്ങളെ തിരിച്ചറിയണം"
+
+#: js/ui/components/networkAgent.js:313 js/ui/components/networkAgent.js:689
+#, javascript-format
+msgid "Passwords or encryption keys are required to access the wireless network “%s”."
+msgstr "വയര്‍ലെസ് നെറ്റ്‌വര്‍ക്ക് \"%s\" ലേക്ക് പ്രവേശിക്കുന്നതിനായി രഹസ്യവാക്കുകള്‍ അല്ലെങ്കില്‍ എന്‍ക്രിപ്ഷന്‍ കീകള്‍ ആവശ്യമുണ്ട്."
+
+#: js/ui/components/networkAgent.js:317 js/ui/components/networkAgent.js:692
+msgid "Wired 802.1X authentication"
+msgstr "വയര്‍ഡ് 802.1X ആധികാരികത"
+
+#: js/ui/components/networkAgent.js:319
+msgid "Network name: "
+msgstr "നെറ്റ്​വര്‍ക്ക് പേര്: "
+
+#: js/ui/components/networkAgent.js:324 js/ui/components/networkAgent.js:696
+msgid "DSL authentication"
+msgstr "DSL ആധികാരികത ഉറപ്പാക്കല്‍"
+
+#: js/ui/components/networkAgent.js:331 js/ui/components/networkAgent.js:702
+msgid "PIN code required"
+msgstr "PIN കോട് ആവശ്യമാണ്"
+
+#: js/ui/components/networkAgent.js:332 js/ui/components/networkAgent.js:703
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "മൊബൈല്‍ ബ്രോഡ്ബാന്‍ഡ് ഉപകരണത്തിന് പിന്‍ കോഡ് ആവശ്യമുണ്ടു്"
+
+#: js/ui/components/networkAgent.js:333
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:340 js/ui/components/networkAgent.js:709
+msgid "Mobile broadband network password"
+msgstr "മൊബൈല്‍ ബ്രോഡ്ബാന്‍ഡ് നെറ്റ്‌വര്‍ക്കിനുള്ള രഹസ്യവാക്ക്"
+
+#: js/ui/components/networkAgent.js:341 js/ui/components/networkAgent.js:693
+#: js/ui/components/networkAgent.js:697 js/ui/components/networkAgent.js:710
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "\"%s\" ലേക്ക് കണക്റ്റ് ചെയ്യുന്നതിന് ഒരു രഹസ്യവാക്ക് ആവശ്യമുണ്ട്."
+
+#: js/ui/components/networkAgent.js:677 js/ui/status/network.js:1690
+msgid "Network Manager"
+msgstr "നെറ്റ്‌വര്‍ക്ക് മാനേജര്‍"
+
+#: js/ui/components/polkitAgent.js:48
+msgid "Authentication Required"
+msgstr "തിരിച്ചറിയണം"
+
+#: js/ui/components/polkitAgent.js:76
+msgid "Administrator"
+msgstr "അഡ്മിനിസ്ട്രേറ്റര്‍"
+
+#: js/ui/components/polkitAgent.js:156
+msgid "Authenticate"
+msgstr "തിരിച്ചറിയുക"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:283 js/ui/shellMountOperation.js:329
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "ക്ഷമിക്കണം, അതു ശരിയല്ല. ദയവായി വീണ്ടും ശ്രമിക്കുക."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:795
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s ഇപ്പോള്‍ %s ആയിട്ടാണ് അറിയപ്പെടുന്നത്"
+
+#: js/ui/ctrlAltTab.js:29 js/ui/viewSelector.js:186
+msgid "Windows"
+msgstr "ജാലകങ്ങള്‍"
+
+#: js/ui/dash.js:246 js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "പ്രയോഗങ്ങള്‍ കാണിയ്ക്കുക"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:444
+msgid "Dash"
+msgstr "ഡാഷ്"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: js/ui/dateMenu.js:74
+msgid "%B %e %Y"
+msgstr "%B %e %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:81
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#: js/ui/dateMenu.js:145
+msgid "Add world clocks…"
+msgstr "ലോക ഘടികാരം കൂട്ടിച്ചേര്‍ക്കുക…"
+
+#: js/ui/dateMenu.js:146
+msgid "World Clocks"
+msgstr "ലോക ഘടികാരം"
+
+#: js/ui/dateMenu.js:227
+msgid "Weather"
+msgstr "കാലാവസ്ഥ"
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:291
+#, javascript-format
+msgid "%s all day."
+msgstr "%s എല്ലാ ദിവസവും."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:297
+#, javascript-format
+msgid "%s, then %s later."
+msgstr "%s, എന്നിട്ട് %s ഒടുവില്‍."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:303
+#, javascript-format
+msgid "%s, then %s, followed by %s later."
+msgstr "%s, ഉടനെ %s, തുടര്‍ന്ന് %s ഒടുവില്‍."
+
+#: js/ui/dateMenu.js:314
+msgid "Select a location…"
+msgstr "ഒരു പ്രദേശം സെലക്റ്റ് ചെയ്യുക…"
+
+#: js/ui/dateMenu.js:317
+msgid "Loading…"
+msgstr "ലോഡിങ്ങ്…"
+
+#. Translators: %s is a temperature with unit, e.g. "23℃"
+#: js/ui/dateMenu.js:323
+#, javascript-format
+msgid "Feels like %s."
+msgstr "%s പോലെ തോന്നുന്നു."
+
+#: js/ui/dateMenu.js:326
+msgid "Go online for weather information"
+msgstr "കാലാവസ്ഥ വിവരങ്ങള്‍ ലഭിക്കാന്‍ ഓണ്‍ലൈന്‍ ആകുക"
+
+#: js/ui/dateMenu.js:328
+msgid "Weather information is currently unavailable"
+msgstr "കാലാവസ്ഥാ വിവരം നിലവില്‍ ലഭ്യമല്ല"
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s ല്‍ നിന്നു പുറത്ത് കടക്കുക"
+
+#: js/ui/endSessionDialog.js:52
+msgctxt "title"
+msgid "Log Out"
+msgstr "പുറത്ത് കടക്കുക"
+
+#: js/ui/endSessionDialog.js:54
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s %d നിമിഷത്തിനുള്ളില്‍ സ്വയമേ പുറത്ത് കടക്കുന്നതാണു്."
+msgstr[1] "%s %d നിമിഷങ്ങള്‍ക്കുള്ളില്‍ സ്വയമേ പുറത്ത് കടക്കുന്നതാണു്."
+
+#: js/ui/endSessionDialog.js:59
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "നിങ്ങള്‍ %d നിമിഷത്തിനുള്ളില്‍ സ്വയമേ പുറത്ത് കടക്കുന്നതാണു്."
+msgstr[1] "നിങ്ങള്‍ %d നിമിഷങ്ങള്‍ക്കുള്ളില്‍ സ്വയമേ പുറത്ത് കടക്കുന്നതാണു്."
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "button"
+msgid "Log Out"
+msgstr "പുറത്ത് കടക്കുക"
+
+#: js/ui/endSessionDialog.js:71
+msgctxt "title"
+msgid "Power Off"
+msgstr "നിര്‍ത്തുക"
+
+#: js/ui/endSessionDialog.js:72
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റോള്‍ ചെയ്ത് പവര്‍ ഓഫ് ആക്കുക"
+
+#: js/ui/endSessionDialog.js:74
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "സിസ്റ്റം %d നിമിഷത്തിനുള്ളില്‍ സ്വയമേ ഓഫ് ചെയ്യപ്പെടുന്നതാണ്."
+msgstr[1] "സിസ്റ്റം %d നിമിഷങ്ങള്‍ക്കുള്ളില്‍ സ്വയമേ ഓഫ് ചെയ്യപ്പെടുന്നതാണ്."
+
+#: js/ui/endSessionDialog.js:78
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "ബാക്കി ഉള്ള സോഫ്റ്റ്വെയര്‍ പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യുക"
+
+#: js/ui/endSessionDialog.js:81 js/ui/endSessionDialog.js:98
+msgctxt "button"
+msgid "Restart"
+msgstr "പുനരാരംഭിക്കൂ"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "button"
+msgid "Power Off"
+msgstr "നിര്‍ത്തുക"
+
+#: js/ui/endSessionDialog.js:90
+msgctxt "title"
+msgid "Restart"
+msgstr "പുനരാരംഭിക്കുക"
+
+#: js/ui/endSessionDialog.js:92
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "സിസ്റ്റം %d നിമിഷത്തിനുള്ളില്‍ സ്വയമേ വീണ്ടും ആരംഭിയ്ക്കുന്നു."
+msgstr[1] "സിസ്റ്റം %d നിമിഷങ്ങള്‍ക്കുള്ളില്‍ സ്വയമേ വീണ്ടും ആരംഭിയ്ക്കുന്നു."
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "പുനരാരംഭിക്കവേ പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യുക"
+
+#: js/ui/endSessionDialog.js:108
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural "The system will automatically restart and install updates in %d seconds."
+msgstr[0] "സിസ്റ്റം %d നിമിഷത്തിനുള്ളില്‍ സ്വയമേ പുനരാരംഭിച്ച് പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യും."
+msgstr[1] "സിസ്റ്റം %d നിമിഷങ്ങള്‍ക്കുള്ളില്‍ സ്വയമേ പുനരാരംഭിച്ച് പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യും."
+
+#: js/ui/endSessionDialog.js:114 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "പുനരാരംഭിക്കുക &amp; ഇന്‍സ്റ്റാള്‍ ചെയ്യുക"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ഇന്‍സ്റ്റാള്‍ ചെയ്യുക & പവര്‍ ഓഫ് ചെയ്യുക"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റാളായ ശേഷം പവര്‍ ഓഫ് ചെയ്യുക"
+
+#: js/ui/endSessionDialog.js:124
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "പുനരാരംഭിക്കവേ പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യുക"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:129
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you have "
+"backed up and that the computer is plugged in."
+msgstr ""
+"%s %s റീസ്റ്റാര്‍ട്ടിനു ശേഷം ഇന്‍സ്റ്റാളാകും. ഈ ഇന്‍സ്റ്റാളേഷന്‍ ഒരുപാട് സമയം എടുത്തേക്കാം: നിങ്ങള്‍ ആവശ്യമുള്ള ബാക്ക് അപ്പ് എടുത്തു എന്നും "
+"കമ്പ്യൂട്ടര്‍ പ്ലഗ്ഡ് ഇന്‍ ആണെന്നും ഉറപ്പാക്കുക."
+
+#: js/ui/endSessionDialog.js:321
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "ബാറ്ററി ശേഷിയിലാണ് പ്രവര്‍ത്തിക്കുന്നത്. പരിഷ്കരണങ്ങള്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യുന്നതിനു മുന്നേ പ്ലഗ് ഇന്‍ ചെയ്യുക."
+
+#: js/ui/endSessionDialog.js:338
+msgid "Some applications are busy or have unsaved work."
+msgstr "ചില പ്രയോഗങ്ങള്‍ തിരക്കിലാണ് അല്ലെങ്കില്‍ സംരക്ഷിക്കാത്ത വര്‍ക്ക് ഉണ്ട്."
+
+#: js/ui/endSessionDialog.js:345
+msgid "Other users are logged in."
+msgstr "വേറേ ഉപയോക്താക്കള്‍ ലോഗിന്‍ ചെയ്തിട്ടുണ്ട്."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:626
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (വിദൂരം)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:629
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (കണ്‍സോള്‍)"
+
+#: js/ui/extensionDownloader.js:201
+msgid "Install"
+msgstr "ഇന്‍സ്റ്റോള്‍"
+
+#: js/ui/extensionDownloader.js:206
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "\"%s\" എന്നത് extenstions.gnome.org ല്‍ നിന്നും ഡൌണ്‍ലോഡ് ചെയ്ത് ഇന്‍സ്റ്റാള്‍ ചെയ്യണമോ ?"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:59
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr "%s കുറുക്കുവഴികള്‍ തടയാന്‍ ആഗ്രഹിക്കുന്നു"
+
+#: js/ui/inhibitShortcutsDialog.js:60
+msgid "Application wants to inhibit shortcuts"
+msgstr "പ്രയോഗം കുറുക്കുവഴികള്‍ തടയാന്‍ ആഗ്രഹിക്കുന്നു"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:69
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "കുറുക്കുവഴികള്‍ പഴയപടിയാക്കാന്‍ %s അമര്‍ത്താം."
+
+#: js/ui/inhibitShortcutsDialog.js:74
+msgid "Deny"
+msgstr "നിഷേധിക്കുക"
+
+#: js/ui/inhibitShortcutsDialog.js:80
+msgid "Allow"
+msgstr "അനുവദിക്കുക"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned On"
+msgstr "സ്ലോ കീകള്‍ പ്രവര്‍ത്തിപ്പിക്കുക"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid "Slow Keys Turned Off"
+msgstr "സ്ലോ കീകള്‍ പ്രവര്‍ത്തനരഹിതമാക്കുക"
+
+#: js/ui/kbdA11yDialog.js:35
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the Slow Keys feature, which "
+"affects the way your keyboard works."
+msgstr ""
+"താങ്കള്‍ ഷിഫ്റ്റ് കീ 8 സെക്കന്റ് അമര്‍ത്തിപ്പിടിച്ചു. ഇത് സ്ലോ കീകള്‍ക്കുള്ള കീകുറുക്കുവഴിയാണ്, ഇത് കീബോര്‍ഡ് പ്രവര്‍ത്തിക്കുന്നവിധത്തിനെ ബാധിക്കും."
+
+#: js/ui/kbdA11yDialog.js:42
+msgid "Sticky Keys Turned On"
+msgstr "സ്റ്റിക്കി കീകള്‍ പ്രവര്‍ത്തിപ്പിക്കുക"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid "Sticky Keys Turned Off"
+msgstr "സ്റ്റിക്കി കീകള്‍ പ്രവര്‍ത്തനരഹിതമാക്കുക"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for the Sticky Keys feature, which "
+"affects the way your keyboard works."
+msgstr ""
+"താങ്കള്‍ ഷിഫ്റ്റ് കീ 5 പ്രാവശ്യം തുടര്‍ച്ചയായി അമര്‍ത്തി. ഇത് സ്റ്റിക്കി കീകള്‍ക്കുള്ള കീകുറുക്കുവഴിയാണ്, ഇത് കീബോര്‍ഡ് പ്രവര്‍ത്തിക്കുന്നവിധത്തിനെ "
+"ബാധിക്കും."
+
+#: js/ui/kbdA11yDialog.js:47
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a row. This turns off the Sticky Keys "
+"feature, which affects the way your keyboard works."
+msgstr ""
+"താങ്കള്‍ രണ്ട് കീകള്‍ ഒരുമിച്ചോ, ഷിഫ്റ്റ് കീ 5പ്രാവശ്യം തുടര്‍ച്ചയായോ അമര്‍ത്തി. ഇത് സ്റ്റിക്കി കീ ഓഫാക്കാനുള്ള കീകുറുക്കുവഴിയാണ്, ഇത് കീബോര്‍ഡ് "
+"പ്രവര്‍ത്തിക്കുന്നവിധത്തിനെ ബാധിക്കും."
+
+#: js/ui/kbdA11yDialog.js:59
+msgid "Leave On"
+msgstr "ലീവ് ഓണ്‍"
+
+#: js/ui/kbdA11yDialog.js:59 js/ui/status/bluetooth.js:138 js/ui/status/network.js:1280
+msgid "Turn On"
+msgstr "ഓണ്‍ ചെയ്യുക"
+
+#: js/ui/kbdA11yDialog.js:67 js/ui/status/bluetooth.js:138 js/ui/status/network.js:138
+#: js/ui/status/network.js:321 js/ui/status/network.js:1280 js/ui/status/network.js:1395
+#: js/ui/status/nightLight.js:43 js/ui/status/rfkill.js:85 js/ui/status/rfkill.js:112
+msgid "Turn Off"
+msgstr "ഓഫ് ചെയ്യുക"
+
+#: js/ui/kbdA11yDialog.js:67
+msgid "Leave Off"
+msgstr "ലീവ് ഓഫ്"
+
+#: js/ui/keyboard.js:198
+msgid "Region & Language Settings"
+msgstr "സ്ഥലം, ഭാഷ സജ്ജീകരണങ്ങള്‍"
+
+#: js/ui/lookingGlass.js:644
+msgid "No extensions installed"
+msgstr "ഒരു എക്സ്റ്റെന്‍ഷനും ഇന്‍സ്റ്റോള്‍ ചെയ്തിട്ടില്ല"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:698
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ഒരു പിശകും കാണിച്ചുട്ടില്ല."
+
+#: js/ui/lookingGlass.js:704
+msgid "Hide Errors"
+msgstr "പിശകുകള്‍ മറച്ചുവെക്കുക"
+
+#: js/ui/lookingGlass.js:708 js/ui/lookingGlass.js:768
+msgid "Show Errors"
+msgstr "പിശകുകള്‍ കാണിക്കുക"
+
+#: js/ui/lookingGlass.js:717
+msgid "Enabled"
+msgstr "പ്രവര്‍ത്തനക്ഷമമാക്കി"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:720 subprojects/gvc/gvc-mixer-control.c:1866
+msgid "Disabled"
+msgstr "പ്രവര്‍ത്തനരഹിതമാക്കി"
+
+#: js/ui/lookingGlass.js:722
+msgid "Error"
+msgstr "പിശക്"
+
+#: js/ui/lookingGlass.js:724
+msgid "Out of date"
+msgstr "തിയതി കഴിഞ്ഞത്"
+
+#: js/ui/lookingGlass.js:726
+msgid "Downloading"
+msgstr "ഡൗണ്‍ലോട് ചെയ്യുന്നു"
+
+#: js/ui/lookingGlass.js:750
+msgid "View Source"
+msgstr "സ്രോതസ്സ് കാണിക്കുക"
+
+#: js/ui/lookingGlass.js:759
+msgid "Web Page"
+msgstr "വെബ് താള്‍"
+
+#: js/ui/messageTray.js:1495
+msgid "System Information"
+msgstr "സിസ്റ്റത്തെക്കുറിച്ചുള്ള വിവരം"
+
+#: js/ui/mpris.js:185
+msgid "Unknown artist"
+msgstr "അജ്ഞാതനായ ആര്‍ട്ടിസ്റ്റ്"
+
+#: js/ui/mpris.js:186
+msgid "Unknown title"
+msgstr "അജ്ഞാതമായ ടൈറ്റില്‍"
+
+#: js/ui/osdWindow.js:32 js/ui/status/volume.js:204
+msgid "Volume"
+msgstr "ഒച്ച"
+
+#: js/ui/overview.js:83
+msgid "Undo"
+msgstr "വേണ്ട"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:112
+msgid "Overview"
+msgstr "അവലോകനം"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:239
+msgid "Type to search…"
+msgstr "തിരയുന്നതിനായി ഇവിടെ ടൈപ്പ് ചെയ്യുക…"
+
+#: js/ui/padOsd.js:107
+msgid "New shortcut…"
+msgstr "പുതിയ കുറുക്കുവഴി…"
+
+#: js/ui/padOsd.js:158
+msgid "Application defined"
+msgstr "പ്രയോഗങ്ങള്‍ നിര്‍വചിച്ചിരിക്കുന്നു"
+
+#: js/ui/padOsd.js:159
+msgid "Show on-screen help"
+msgstr "on-screen സഹായം ദൃശ്യമാക്കുക"
+
+#: js/ui/padOsd.js:160
+msgid "Switch monitor"
+msgstr "മോണിറ്റര്‍ മാറ്റുക"
+
+#: js/ui/padOsd.js:161
+msgid "Assign keystroke"
+msgstr "കീസ്ട്രോക്ക് അസൈന്‍ ചെയ്യുക"
+
+#: js/ui/padOsd.js:228
+msgid "Done"
+msgstr "തീര്‍ന്നു"
+
+#: js/ui/padOsd.js:744
+msgid "Edit…"
+msgstr "തിരുത്തുക…"
+
+#: js/ui/padOsd.js:786 js/ui/padOsd.js:891
+msgid "None"
+msgstr "ഒന്നുമില്ല"
+
+#: js/ui/padOsd.js:845
+msgid "Press a button to configure"
+msgstr "കോണ്‍ഫിഗര്‍ ചെയ്യാന്‍ ഒരു ബട്ടണ്‍ അമര്‍ത്തുക"
+
+#: js/ui/padOsd.js:846
+msgid "Press Esc to exit"
+msgstr "പുറത്തുകടക്കാന്‍ Esc അമര്‍ത്തുക"
+
+#: js/ui/padOsd.js:849
+msgid "Press any key to exit"
+msgstr "പുറത്തു കടക്കാന്‍ ഏതെങ്കിലും കീ അമര്‍ത്തുക"
+
+#: js/ui/panel.js:359
+msgid "Quit"
+msgstr "നിര്‍ത്തുക"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:415
+msgid "Activities"
+msgstr "പ്രവര്‍ത്തനങ്ങള്‍"
+
+#: js/ui/panel.js:696
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "സിസ്റ്റം"
+
+#: js/ui/panel.js:820
+msgid "Top Bar"
+msgstr "മുകളിലുള്ള ബാര്‍"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:300
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: js/ui/runDialog.js:74
+msgid "Enter a Command"
+msgstr "ഒരു കമാന്‍ഡ് നല്‍കുക"
+
+#: js/ui/runDialog.js:114 js/ui/windowMenu.js:174
+msgid "Close"
+msgstr "അടക്കുക"
+
+#: js/ui/runDialog.js:276
+msgid "Restart is not available on Wayland"
+msgstr "വേയ്ലാന്‍ഡില്‍ പുനരാരംഭം എന്ന ഓപ്ഷന്‍ ലഭ്യമല്ല"
+
+#: js/ui/runDialog.js:281
+msgid "Restarting…"
+msgstr "പുനരാരംഭിക്കുന്നു…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: js/ui/screenShield.js:146
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d പുതിയ സന്ദേശം"
+msgstr[1] "%d പുതിയ സന്ദേശങ്ങള്‍"
+
+#: js/ui/screenShield.js:148
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d പുതിയ അറിയിപ്പു്"
+msgstr[1] "%d പുതിയ അറിയിപ്പുകള്‍"
+
+#: js/ui/screenShield.js:451 js/ui/status/system.js:294
+msgid "Lock"
+msgstr "പൂട്ടുക"
+
+#: js/ui/screenShield.js:715
+msgid "GNOME needs to lock the screen"
+msgstr "ഗ്നോമിന് സ്ക്രീന്‍ പൂട്ടണം"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:836 js/ui/screenShield.js:1309
+msgid "Unable to lock"
+msgstr "പൂട്ടുവാന്‍ സാധ്യമല്ല"
+
+#: js/ui/screenShield.js:837 js/ui/screenShield.js:1310
+msgid "Lock was blocked by an application"
+msgstr "പൂട്ടുന്ന സംവിധാനം ഒരു പ്രയോഗം തടസ്സപ്പെടുത്തിയിരിയ്ക്കുന്നു"
+
+#: js/ui/search.js:660
+msgid "Searching…"
+msgstr "തിരയുന്നു…"
+
+#: js/ui/search.js:662
+msgid "No results."
+msgstr "ഫലങ്ങളില്ല."
+
+#: js/ui/search.js:786
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d കൂടുതല്‍"
+msgstr[1] "%d കൂടുതല്‍"
+
+#: js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "പകര്‍ത്തുക"
+
+#: js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "ഒട്ടിയ്ക്കുക"
+
+#: js/ui/shellEntry.js:96
+msgid "Show Text"
+msgstr "പദാവലി കാണിക്കുക"
+
+#: js/ui/shellEntry.js:98
+msgid "Hide Text"
+msgstr "പദാവലി മറക്കുക"
+
+#: js/ui/shellMountOperation.js:316
+msgid "Password"
+msgstr "രഹസ്യവാക്ക്"
+
+#: js/ui/shellMountOperation.js:337
+msgid "Remember Password"
+msgstr "രഹസ്യവാക്ക് ഓര്‍ത്തു് വയ്ക്കുക"
+
+#: js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "ആക്സസിബിളിറ്റി"
+
+#: js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "വലുതാക്കുക"
+
+#: js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "സ്ക്രീന്‍ റീഡര്‍"
+
+#: js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "സ്ക്രീന്‍ കീബോര്‍ഡ്"
+
+#: js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "വിഷ്വല്‍ അറിയിപ്പുകള്‍"
+
+#: js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "സ്റ്റിക്കി കീകള്‍"
+
+#: js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "സ്ലോ കീകള്‍"
+
+#: js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "ബൌണ്‍സ് കീകള്‍"
+
+#: js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "മൌസ് കീകള്‍"
+
+#: js/ui/status/accessibility.js:165
+msgid "High Contrast"
+msgstr "ഹൈ കോണ്ട്രാസ്റ്റ്"
+
+#: js/ui/status/accessibility.js:200
+msgid "Large Text"
+msgstr "വലുപ്പത്തിലുളള ടെകസ്റ്റിന്റെ ശൈലി"
+
+#: js/ui/status/bluetooth.js:43
+msgid "Bluetooth"
+msgstr "ബ്ളുടൂത്ത്"
+
+#: js/ui/status/bluetooth.js:52 js/ui/status/network.js:600
+msgid "Bluetooth Settings"
+msgstr "ബ്ളുടൂത്ത് ക്രമീകരണങ്ങള്‍"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:132
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d കണക്റ്റായി"
+msgstr[1] "%d കണക്റ്റായി"
+
+#: js/ui/status/bluetooth.js:134
+msgid "Off"
+msgstr "ഓഫ്​"
+
+#: js/ui/status/bluetooth.js:136
+msgid "On"
+msgstr "ഓണ്‍"
+
+#: js/ui/status/brightness.js:41
+msgid "Brightness"
+msgstr "തെളിച്ചം"
+
+#: js/ui/status/keyboard.js:781
+msgid "Keyboard"
+msgstr "കീബോര്‍ഡ്"
+
+#: js/ui/status/keyboard.js:804
+msgid "Show Keyboard Layout"
+msgstr "കീബോര്‍ഡ് വിന്യാസം കാണിയ്ക്കുക"
+
+#: js/ui/status/location.js:72 js/ui/status/location.js:180
+msgid "Location Enabled"
+msgstr "ലൊക്കേഷന്‍ ഇനേബിള്‍ ചെയ്തു"
+
+#: js/ui/status/location.js:73 js/ui/status/location.js:181
+msgid "Disable"
+msgstr "പ്രവര്‍ത്തനരഹിതമാക്കി"
+
+#: js/ui/status/location.js:74
+msgid "Privacy Settings"
+msgstr "സ്വകാര്യതാ ക്രമീകരണങ്ങള്‍"
+
+#: js/ui/status/location.js:179
+msgid "Location In Use"
+msgstr "ലൊക്കേഷന്‍ ഉപയോഗത്തിലാണ്"
+
+#: js/ui/status/location.js:183
+msgid "Location Disabled"
+msgstr "ലൊക്കേഷന്‍ ഡിസേബിള്‍ ചെയ്തു"
+
+#: js/ui/status/location.js:184
+msgid "Enable"
+msgstr "പ്രവര്‍ത്തനക്ഷമമാക്കി"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:369
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "%s ന് ലൊക്കേഷനിലേക്ക് അനുമതി നല്‍കണോ ?"
+
+#: js/ui/status/location.js:370
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "ലൊക്കേഷന്‍ അനുമതി എപ്പോള്‍ വേണമെങ്കിലും പ്രൈവസി ക്രമീകരണങ്ങളില്‍ നിന്ന് മാറ്റാവുന്നതാണ്."
+
+#: js/ui/status/network.js:74
+msgid "<unknown>"
+msgstr "<അജ്ഞാതം>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:427 js/ui/status/network.js:1309
+#, javascript-format
+msgid "%s Off"
+msgstr "%s ഓഫ്"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:430
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s കണക്റ്റ് ചെയ്തു"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:435
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s കൈകാര്യം ചെയ്തിട്ടില്ലാത്തത്"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:438
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s വിച്ഛേദിക്കുന്നു"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:445 js/ui/status/network.js:1301
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s കണക്റ്റ് ചെയ്യുന്നു"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:448
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s തിരിച്ചറിയല്‍ ആവശ്യപ്പെടുന്നു"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:456
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "%s നുള്ള ഫേംവെയര്‍ ഇപ്പോള്‍ ലഭ്യമല്ല"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:460
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s ലഭ്യമല്ല"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:463
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s കണക്ഷന്‍ പരാജയപ്പെട്ടു"
+
+#: js/ui/status/network.js:479
+msgid "Wired Settings"
+msgstr "വയേഡ് സെറ്റിങ്സ്"
+
+#: js/ui/status/network.js:521
+msgid "Mobile Broadband Settings"
+msgstr "മൊബൈല്‍ ബ്രോഡ്ബാന്‍ഡ് ക്രമീകരണങ്ങള്‍"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:564 js/ui/status/network.js:1306
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s ഹാര്‍ഡ്‌വെയര്‍ പ്രവര്‍ത്തന രഹിതം"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:568
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s പ്രവര്‍ത്തനരഹിതമാക്കി"
+
+#: js/ui/status/network.js:608
+msgid "Connect to Internet"
+msgstr "ഇന്റര്‍നെറ്റുമായി കണക്റ്റ് ചെയ്യുക"
+
+#: js/ui/status/network.js:802
+msgid "Airplane Mode is On"
+msgstr "എയര്‍പ്ലെയിന്‍ മോഡ് ഓണ്‍ ആണ്"
+
+#: js/ui/status/network.js:803
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "എയര്‍പ്ലെയിന്‍ മോഡ് ഓണ്‍ ആകവേ വൈ-ഫൈ പ്രവര്‍ത്തനരഹിതമാകും."
+
+#: js/ui/status/network.js:804
+msgid "Turn Off Airplane Mode"
+msgstr "എര്‍പ്ലെയിന്‍ മോഡ് ഓഫ് ചെയ്യുക"
+
+#: js/ui/status/network.js:813
+msgid "Wi-Fi is Off"
+msgstr "വൈ-ഫൈ ഓഫ് ആണ്"
+
+#: js/ui/status/network.js:814
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "നെറ്റ്വര്‍ക്കിലേക്ക് കണക്റ്റ് ചെയ്യാന്‍ വൈഫൈ ഓണ്‍ ചെയ്യുക."
+
+#: js/ui/status/network.js:815
+msgid "Turn On Wi-Fi"
+msgstr "വൈഫൈ ഓണ്‍ ചെയ്യുക"
+
+#: js/ui/status/network.js:840
+msgid "Wi-Fi Networks"
+msgstr "വൈഫൈ നെറ്റ്വര്‍ക്കുകള്‍"
+
+#: js/ui/status/network.js:842
+msgid "Select a network"
+msgstr "നെറ്റ്വര്‍ക്ക് തിരഞ്ഞെടുക്കുക"
+
+#: js/ui/status/network.js:872
+msgid "No Networks"
+msgstr "നെറ്റ്വര്‍ക്കുകളില്ല"
+
+#: js/ui/status/network.js:893 js/ui/status/rfkill.js:110
+msgid "Use hardware switch to turn off"
+msgstr "നിര്‍ത്തുവാന്‍ ഹാര്‍ഡ്വെയര്‍ സ്വിച്ച് ഉപയോഗിക്കുക"
+
+#: js/ui/status/network.js:1172
+msgid "Select Network"
+msgstr "നെറ്റ്വര്‍ക്ക് തിരഞ്ഞെടുക്കുക"
+
+#: js/ui/status/network.js:1178
+msgid "Wi-Fi Settings"
+msgstr "വൈഫൈ സജ്ജീകരണങ്ങള്‍"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1297
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s ഹോട്സ്പോട്ട് ആക്റ്റീവ് ആയി"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1312
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s കണക്റ്റഡ് അല്ല"
+
+#: js/ui/status/network.js:1412
+msgid "connecting…"
+msgstr "ബന്ധിപ്പിക്കുന്നു…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1415
+msgid "authentication required"
+msgstr "ആധികാരികത ഉറപ്പ് വരുത്തല്‍ ആവശ്യമുണ്ട്"
+
+#: js/ui/status/network.js:1417
+msgid "connection failed"
+msgstr "ബന്ധം പരാജയപ്പെട്ടു"
+
+#: js/ui/status/network.js:1471
+msgid "VPN Settings"
+msgstr "വിപിഎന്‍ സജ്ജീകരണങ്ങള്‍"
+
+#: js/ui/status/network.js:1484
+msgid "VPN"
+msgstr "വി പി എന്‍"
+
+#: js/ui/status/network.js:1494
+msgid "VPN Off"
+msgstr "വിപിഎന്‍ ഓഫ്"
+
+#: js/ui/status/network.js:1558 js/ui/status/rfkill.js:88
+msgid "Network Settings"
+msgstr "നെറ്റ്‌വര്‍ക്ക് സജ്ജീകരണങ്ങള്‍"
+
+#: js/ui/status/network.js:1587
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s വയേഡ് കണക്ഷന്‍"
+msgstr[1] "%s വയേഡ് കണക്ഷനുകള്‍"
+
+#: js/ui/status/network.js:1591
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s വൈഫൈ കണക്ഷന്‍"
+msgstr[1] "%s വൈഫൈ കണക്ഷനുകള്‍"
+
+#: js/ui/status/network.js:1595
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s മോഡം കണക്ഷന്‍"
+msgstr[1] "%s മോഡം കണക്ഷനുകള്‍"
+
+#: js/ui/status/network.js:1727
+msgid "Connection failed"
+msgstr "കണക്ഷന്‍ പരാജയപ്പെട്ടു"
+
+#: js/ui/status/network.js:1728
+msgid "Activation of network connection failed"
+msgstr "നെറ്റ്‌വര്‍ക്ക് കണക്ഷന്‍ സജീവമാക്കല്‍ പരാജയപ്പെട്ടു"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light Disabled"
+msgstr "നൈറ്റ് ലൈറ്റ് പ്രവര്‍ത്തന രഹിതം"
+
+#: js/ui/status/nightLight.js:65
+msgid "Night Light On"
+msgstr "നൈറ്റ് ലൈറ്റ് ഓണ്‍"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "വീണ്ടും തുടങ്ങുക"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "നാളെ വരെ പ്രവര്‍ത്തനരഹിതമാക്കുക"
+
+#: js/ui/status/power.js:52
+msgid "Power Settings"
+msgstr "ഊര്‍ജ്ജ ക്രമീകരണങ്ങള്‍"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr "ചാര്‍ജ് നിറഞ്ഞിരിക്കുന്നു"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:75 js/ui/status/power.js:81
+msgid "Estimating…"
+msgstr "കണക്കുകൂട്ടുന്നു…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:89
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d ബാക്കിയുണ്ട് (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:94
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d നിറയുന്നതുവരെ (%d %%)"
+
+#: js/ui/status/power.js:122 js/ui/status/power.js:124
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:46
+msgid "Screen is Being Shared"
+msgstr "സ്ക്രീന്‍ പങ്കുവയ്ക്കപ്പെടുന്നു"
+
+#: js/ui/status/remoteAccess.js:48
+msgid "Turn off"
+msgstr "ഓഫാക്കുക"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:83
+msgid "Airplane Mode On"
+msgstr "വിമാനം മോഡ് ഓണ്‍ ചെയ്യുക"
+
+#: js/ui/status/system.js:227
+msgid "Switch User"
+msgstr "ഉപയോക്താവിനെ മാറ്റുക"
+
+#: js/ui/status/system.js:239
+msgid "Log Out"
+msgstr "പുറത്ത് കടക്കുക"
+
+#: js/ui/status/system.js:251
+msgid "Account Settings"
+msgstr "അക്കൌണ്ട് ക്രമീകരണങ്ങള്‍"
+
+#: js/ui/status/system.js:279
+msgid "Orientation Lock"
+msgstr "വിന്യാസ ബന്ധനം"
+
+#: js/ui/status/system.js:305
+msgid "Suspend"
+msgstr "താത്കാലികമായി നിര്‍ത്തുക"
+
+#: js/ui/status/system.js:315
+msgid "Power Off"
+msgstr "നിര്‍ത്തുക"
+
+#: js/ui/status/thunderbolt.js:268
+msgid "Thunderbolt"
+msgstr "തണ്ടര്‍ബോള്‍ട്ട്"
+
+#. we are done
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "അറിയില്ലാത്ത തണ്ടര്‍ബോള്‍ട്ട് ഉപകരണം"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and reconnect the device to start using "
+"it."
+msgstr ""
+"താങ്കള്‍ സ്ഥലത്തില്ലാത്തപ്പോള്‍ പുതിയ ഉപകരണം ബന്ധിപ്പിച്ചിരിക്കുന്നു. ഉപകരണം ഉപയോഗിക്കുന്നതിന് വിച്ഛേദിക്കുകയും പുനര്‍ബന്ധിപ്പിക്കുയും ചെയ്യുക."
+
+#: js/ui/status/thunderbolt.js:330
+msgid "Thunderbolt authorization error"
+msgstr "തണ്ടര്‍ബോള്‍ട്ട് സാധുതാ പിഴവ്"
+
+#: js/ui/status/thunderbolt.js:331
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "തണ്ടര്‍ബോള്‍ട്ട് ഉപകരണത്തിന് സാധുതനല്‍കാന്‍ കഴിഞ്ഞില്ല: %s"
+
+#: js/ui/status/volume.js:135
+msgid "Volume changed"
+msgstr "ഒച്ച മാറ്റി"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:21
+msgid "Mirror"
+msgstr "മിറര്‍"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:26
+msgid "Join Displays"
+msgstr "ഡിസ്പ്ലേകള്‍ ചേര്‍ക്കുക"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:31
+msgid "External Only"
+msgstr "പുറമേ മാത്രം"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:36
+msgid "Built-in Only"
+msgstr "ഉള്ളിലേത് മാത്രം"
+
+#: js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "മറ്റൊരു ഉപയോക്താവായി പ്രവേശിയ്ക്കുക"
+
+#: js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "ജാലകത്തിന്റെ പൂട്ടു തുറക്കുക"
+
+#: js/ui/viewSelector.js:190
+msgid "Applications"
+msgstr "പ്രയോഗങ്ങള്‍"
+
+#: js/ui/viewSelector.js:194
+msgid "Search"
+msgstr "തിരയല്‍"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "\"%s\" തയ്യാറാണ്"
+
+#: js/ui/windowManager.js:63
+msgid "Do you want to keep these display settings?"
+msgstr "ഈ ദൃശ്യക്രമീകരണം സൂക്ഷിക്കണോ ?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:75
+msgid "Revert Settings"
+msgstr "ക്രമീകരണങ്ങള്‍ തിരിച്ചാക്കുക"
+
+#: js/ui/windowManager.js:78
+msgid "Keep Changes"
+msgstr "മാറ്റങ്ങള്‍ സംരക്ഷിക്കുക"
+
+#: js/ui/windowManager.js:96
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "ക്രമീകരണങ്ങള്‍ %d സെക്കന്‍ഡില്‍ തിരിച്ചാകും"
+msgstr[1] "ക്രമീകരണങ്ങള്‍ %d സെക്കന്‍ഡുകളില്‍ തിരിച്ചാകും"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:681
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "ചെറുതാക്കുക"
+
+#: js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "പരമാവധി ആക്കാതിരിക്കുക"
+
+#: js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "പരമാവധി ആക്കുക"
+
+#: js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "നീക്കുക"
+
+#: js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "വലിപ്പം മാറ്റുക"
+
+#: js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "ടൈറ്റില്‍ ബാര്‍ സ്ക്രീനില്‍ നീക്കുക"
+
+#: js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "എപ്പോഴും മുകളില്‍"
+
+#: js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "എപ്പോഴും ദൃശ്യമായ പണിയിടത്തില്‍ ഉണ്ടാകുക"
+
+#: js/ui/windowMenu.js:103
+msgid "Move to Workspace Left"
+msgstr "പണിയിടം ഇടത്തേക്ക് നീക്കുക"
+
+#: js/ui/windowMenu.js:109
+msgid "Move to Workspace Right"
+msgstr "പണിയിടം വലത്തേക്ക് നീക്കുക"
+
+#: js/ui/windowMenu.js:115
+msgid "Move to Workspace Up"
+msgstr "പണിയിടം മുകളിലേക്ക് നീക്കുക"
+
+#: js/ui/windowMenu.js:121
+msgid "Move to Workspace Down"
+msgstr "പണിയിടം താഴേക്ക് നീക്കുക"
+
+#: js/ui/windowMenu.js:139
+msgid "Move to Monitor Up"
+msgstr "മോണിറ്ററിന്റെ മുകള്‍ഭാഗത്തേക്ക് നീക്കുക"
+
+#: js/ui/windowMenu.js:148
+msgid "Move to Monitor Down"
+msgstr "മോണിറ്ററിന്റെ താഴ്ഭാഗത്തേക്ക് നീക്കുക"
+
+#: js/ui/windowMenu.js:157
+msgid "Move to Monitor Left"
+msgstr "മോണിറ്ററിന്റെ ഇടത്തേക്ക് നീക്കുക"
+
+#: js/ui/windowMenu.js:166
+msgid "Move to Monitor Right"
+msgstr "മോണിറ്ററിന്റെ വലത്തേക്ക് നീക്കുക"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "ഇവല്യൂഷന്‍ കലണ്ടര്‍"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: src/main.c:410
+msgid "Print version"
+msgstr "പ്രിന്റ് ചെയ്യുവാന്‍ സാധിയ്ക്കുന്ന പതിപ്പു്"
+
+#: src/main.c:416
+msgid "Mode used by GDM for login screen"
+msgstr "പ്രവേശന സ്ക്രീനില്‍ ജിഡിഎം ഉപയോഗിയ്ക്കുന്ന മോഡ്"
+
+#: src/main.c:422
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "ഒരു പ്രത്യേക മോഡ് ഉപയോഗിയ്ക്കുക, ഉദാ. പ്രവേശന സ്ക്രീനിന് gdm"
+
+#: src/main.c:428
+msgid "List possible modes"
+msgstr "സാധ്യമായ മോഡുകള്‍ ലഭ്യമാക്കുക"
+
+#: src/shell-app.c:272
+msgctxt "program"
+msgid "Unknown"
+msgstr "അജ്ഞാതം"
+
+#: src/shell-app.c:523
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "\"%s\" ലഭ്യമാക്കുന്നതില്‍ പരാജയം"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "രഹസ്യവാക്കുകള്‍ സമാനമല്ല."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "രഹസ്യവാക്ക് കാലിയാകുവാന്‍ പാടില്ല"
+
+#: src/shell-polkit-authentication-agent.c:353
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ഉപയോക്താവു് ആധികാരികത ഉറപ്പാക്കല്‍ ഡയലോഗ് നിര്‍ത്തുന്നു"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1873
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u ഔട്ട്പുട്ട്"
+msgstr[1] "%u ഔട്ട്പുട്ടുകള്‍"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1883
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ഇന്‍പുട്ട്"
+msgstr[1] "%u ഇന്‍പുട്ടുകള്‍"
+
+#: subprojects/gvc/gvc-mixer-control.c:2738
+msgid "System Sounds"
+msgstr "സിസ്റ്റം ശബ്ദങ്ങള്‍"
+
+#~ msgctxt "search-result"
+#~ msgid "Power off"
+#~ msgstr "നിര്‍ത്തുക"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "പുറത്ത് കടക്കുക"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "ഉപയോക്താവിനെ മാറ്റുക"
+
+#~ msgid "Screenshots"
+#~ msgstr "സ്ക്രീന്‍ഷോട്ടുകള്‍"
+
+#~ msgid "Record a screencast"
+#~ msgstr "ഒരു സ്ക്രീന്‍കാസ്റ്റ് റിക്കോര്‍ഡ് ചെയ്യുക"
+
+#~ msgid "Show the message tray"
+#~ msgstr "സന്ദേശത്തിന്റെ ട്രേ കാണിയ്ക്കുക"
+
+#~ msgid "GNOME Shell Extension Preferences"
+#~ msgstr "ഗ്നോം ഷെല്‍ എക്സ്റ്റെന്‍ഷന്‍ മുന്‍ഗണനകള്‍"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "പ്രയോഗങ്ങളുടെ ഉപയോഗത്തെപ്പറ്റിയുള്ള സ്ഥിതിവിവരക്കണക്കുകള്‍ ശേഖരിയ്ക്കണമോ എന്നു്"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the most used ones (e.g. in "
+#~ "launchers). While this data will be kept private, you may want to disable this for privacy reasons. "
+#~ "Please note that doing so won't remove already saved data."
+#~ msgstr ""
+#~ "ഏറ്റവും കൂടുതല്‍ തവണ ഉപയോഗിയ്ക്കുന്ന പ്രയോഗങ്ങള്‍ ലഭ്യമാക്കുന്നതിനായി ഷെല്‍ സാധാരണയായി സജീവമായ പ്രയോഗങ്ങളെ നിരീക്ഷിയ്ക്കുന്നു. "
+#~ "(ഉദാഹരണത്തിനു്, ലോഞ്ചേര്‍സ്). ഈ ഡേറ്റാ സ്വകാര്യമായി സൂക്ഷിയ്ക്കുന്നെങ്കിലും, ചില കാരണങ്ങളാല്‍ ഇതു് പ്രവര്‍ത്തന രഹിതമാക്കേണ്ടതുണ്ടു്. ഇങ്ങനെ "
+#~ "ചെയ്യുന്നതു് നിങ്ങള്‍ സൂക്ഷിച്ച ഡേറ്റയെ ബാധിയ്ക്കുന്നതല്ല."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "ഫോള്‍ഡറുകളായി കാണിയ്ക്കേണ്ട വിഭാഗങ്ങളുടെ പട്ടിക"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the application view, rather than being "
+#~ "displayed inline in the main view."
+#~ msgstr ""
+#~ "ഈ പട്ടികയിലുള്ള ഓരോ വിഭാഗത്തിന്റെ പേരും, പ്രധാന കാഴ്ചയില്‍ ഓരോ വരിയായി കാണിയ്ക്കുന്നതിനു് പകരം പ്രയോഗങ്ങളുടെ കാഴ്ചയില്‍ ഫോള്‍"
+#~ "ഡറായി കാണിയ്ക്കുന്നു. "
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. The value here is from the "
+#~ "TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "ഉപയോക്താവു് സജ്ജമാക്കിയ അവസാന ഐഎം ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് ഉപയോഗിയ്ക്കുന്നു. മൂല്യം TpConnectionPresenceType "
+#~ "തരത്തിലുള്ളതാകുന്നു."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. The value here is from the "
+#~ "GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "ഉപയോക്താവിനുള്ള അവസാന സെഷന്‍ അവസ്ഥ ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് ഉപയോഗിയ്ക്കുന്നു. മൂല്യം GsmPresenceStatus തരത്തിലുള്ളതാകുന്നു."
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "കലണ്ടറിലുള്ള ആഴ്ച തീയിതി കാണിയ്ക്കുക"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "true എങ്കില്‍, കലണ്ടറില്‍ ഐഎസ്ഒ ആഴ്ച തീയതി കാണിയ്ക്കുക."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "സ്ക്രീന്‍ റിക്കോര്‍ഡര്‍ ടൊഗ്ഗിള്‍ ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "ബിള്‍ട്ടിന്‍ സ്ക്രീന്‍ റിക്കോര്‍ഡര്‍ തുടങ്ങുവാന്‍/നിര്‍ത്തുന്നതിനുള്ള കീക്കൂട്ടം."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "സ്ക്രീന്‍കാസ്റ്റുകള്‍ റിക്കോര്‍ഡ് ചെയ്യുന്നതിനുള്ള ഫ്രെയിം റേറ്റ്."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's screencast recorder in frames-per-"
+#~ "second."
+#~ msgstr ""
+#~ "ഗ്നോം ഷെല്ലിന്റെ സ്ക്രീന്‍കാസ്റ്റ് റിക്കോര്‍ഡര്‍ റീക്കോര്‍ഡ് ചെയ്തിട്ടുള്ള സ്ക്രീന്‍കാസ്റ്റിന്റെ ഫ്രെയിംറേറ്റ്, ഒരു സെക്കന്‍ഡില്‍ ഒരു ഫ്രെയിം."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "സ്ക്രീന്‍കാസ്റ്റ് എന്‍കോഡ് ചെയ്യുന്നതിനുള്ള gstreamer പൈപ്പ്‌ലൈന്‍"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the syntax used for gst-launch. The "
+#~ "pipeline should have an unconnected sink pad where the recorded video is recorded. It will normally have "
+#~ "a unconnected source pad; output from that pad will be written into the output file. However the pipeline "
+#~ "can also take care of its own output - this might be used to send the output to an icecast server via "
+#~ "shout2send or similar. When unset or set to an empty value, the default pipeline will be used. This is "
+#~ "currently 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! "
+#~ "webmmux' and records to WEBM using the VP8 codec. %T is used as a placeholder for a guess at the optimal "
+#~ "thread count on the system."
+#~ msgstr ""
+#~ "റിക്കോര്‍ഡിങുകള്‍ എന്‍കോഡ് ചെയ്യുന്നതിനായി GStreamer പൈപ്പ് ലൈന്‍ ഉപയോഗിയ്ക്കുന്നു. gst-launch-നുള്ള സിന്റാക്സ് ഉപയോഗിയ്ക്കുന്നു. "
+#~ "കാലിയായി സജ്ജമാക്കുമ്പോള്‍ കാലിയാകുന്നു.ഇതു് നിലവില്‍ 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 "
+#~ "deadline=1000000 threads=%T ! queue ! webmmux' ആകുന്നുസ WEBM VP8 കോഡ് ഉപയോഗിച്ചു് റിക്കോര്‍ഡ് ചെയ്യുന്നു."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "സ്ക്രീന്‍കാസ്റ്റ് സൂക്ഷിയ്ക്കുന്നതിനുള്ള ഫയല്‍ എക്സ്റ്റെന്‍ഷന്‍"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on the current date, and use this "
+#~ "extension. It should be changed when recording to a different container format."
+#~ msgstr ""
+#~ "റിക്കോര്‍ഡ് ചെയ്ത സ്ക്രീന്‍കാസ്റ്റുകള്‍ക്കുള്ള ഫയല്‍നാമം നിലവിലുള്ള തീയതി, എക്സ്റ്റെന്‍ഷന്‍ എന്നിവ അനുസരിച്ചാകുന്നു. മറ്റൊരു ശൈലിയിലേക്കു് റിക്കോര്‍"
+#~ "ഡ് ചെയ്യുമ്പോള്‍ ഇതു് മാറ്റണം."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "തലക്കെട്ടിനുള്ള ബാറില്‍ ബട്ടണുകളുടെ ക്രമീകരണം"
+
+#~ msgid "This key overrides the key in org.gnome.desktop.wm.preferences when running GNOME Shell."
+#~ msgstr "ഗ്നോം ഷെല്‍ പ്രവര്‍ത്തിയ്ക്കുമ്പോള്‍ org.gnome.desktop.wm.preferences-ലുള്ള കീ ഈ കീ തിരുത്തിയെഴുതുന്നു."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "മുകളിലുള്ള കോമ്പോ ബോക്സ് ഉപയോഗിച്ചു് ക്രമീകരിയ്ക്കുന്നതിനുള്ളൊരു എക്സ്റ്റെന്‍ഷന്‍ തെര‍ഞ്ഞെടുക്കുക."
+
+#~ msgid "Session…"
+#~ msgstr "പ്രവര്‍ത്തനവേള..."
+
+#~ msgid "Power"
+#~ msgstr "ഓഫ്"
+
+#~ msgid "Restart"
+#~ msgstr "പുനരാരംഭിക്കുക"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "ഞാ"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "തി"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "ചൊ"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "ബു"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "വ്യാ"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "വെ"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "ശ"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "ഒന്നും ക്രമീകരിച്ചിട്ടില്ല"
+
+#~ msgid "Today"
+#~ msgstr "ഇന്നു്"
+
+#~ msgid "Tomorrow"
+#~ msgstr "നാളെ"
+
+#~ msgid "This week"
+#~ msgstr "ഈ ആഴ്ച"
+
+#~ msgid "Next week"
+#~ msgstr "അടുത്ത ആഴ്ച"
+
+#~ msgid "Removable Devices"
+#~ msgstr "നീക്കം ചെയ്യാവുന്ന ഡിവൈസുകള്‍"
+
+#~ msgid "Eject"
+#~ msgstr "പുറന്തള്ളുക"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d-ല്‍ നിന്നുള്ള സ്ക്രീന്‍കാസ്റ്റ്, %t-ല്‍"
+
+#~ msgid "Invitation"
+#~ msgstr "ക്ഷണം"
+
+#~ msgid "Call"
+#~ msgstr "വിളിക്കുക"
+
+#~ msgid "File Transfer"
+#~ msgstr "ഫയല്‍ നീക്കുക"
+
+#~ msgid "Chat"
+#~ msgstr "സല്ലാപം"
+
+#~ msgid "Unmute"
+#~ msgstr "ശബ്ദം നിര്‍ത്തുക"
+
+#~ msgid "Mute"
+#~ msgstr "നിശബ്ദമാക്കുക"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Invitation to %s"
+#~ msgstr "%s ലേക്കുള്ള ക്ഷണം"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s നിങ്ങളെ %sഇലേക്കു ചേരാന്‍ ക്ഷണിക്കുന്നുണ്ട്"
+
+#~ msgid "Decline"
+#~ msgstr "നിരസിക്കുക"
+
+#~ msgid "Accept"
+#~ msgstr "സ്വീകരിക്കുക"
+
+#~ msgid "Video call from %s"
+#~ msgstr "%s-ല്‍ നിന്നുള്ള വീഡിയോ കോള്‍"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s-ല്‍ നിന്നുള്ള കോള്‍"
+
+#~ msgid "Answer"
+#~ msgstr "എടുക്കുക"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s നിങ്ങള്‍ക്കു് %s അയച്ചിരിയ്ക്കുന്നു"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "നിങ്ങള്‍ ഓണ്‍ലൈന്‍ ആകുമ്പോള്‍ കാണുന്നതിനുള്ള അനുമതി %s-നു് ആവശ്യമുണ്ടു്"
+
+#~ msgid "Authentication failed"
+#~ msgstr "തിരിച്ചറിയല്‍ പ്രക്രിയ പരാജയപ്പെട്ടു"
+
+#~ msgid "Encryption error"
+#~ msgstr "എന്‍ക്രിപ്ഷനില്‍ പിശകു്"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "സാക്ഷ്യപത്രം നല്‍കിയില്ല"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "സാക്ഷ്യംപത്രം വിശ്വാസമില്ലാത്തു്"
+
+#~ msgid "Certificate expired"
+#~ msgstr "സാക്ഷ്യപത്രം കാലാവധി തീര്‍ന്നതു്"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "സാക്ഷ്യപത്രം സജീകമാക്കാത്തതു്"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "സാക്ഷ്യപത്രത്തിലെ ഹോസ്റ്റിന്റെ പേരു് പൊരുത്തമില്ല"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "സാക്ഷ്യപത്രത്തിലെ ഒപ്പു് പൊരുത്തമില്ല"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "സാക്ഷ്യപത്രം സ്വയം ഒപ്പിട്ടതാണു്"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "അവസ്ഥ ഓഫ്‌ലൈന്‍ ആയി സജ്ജമാക്കിയിരിയ്ക്കുന്നു"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "സാക്ഷ്യപത്രം തെറ്റാണു്"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "കണക്ഷന്‍ നിഷേധിച്ചിരിയ്ക്കുന്നു"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "കണക്ഷന്‍ സ്ഥാപിയ്ക്കുവാന്‍ സാധ്യമല്ല"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "കണക്ഷന്‍ നഷ്ടമായിരിയ്ക്കുന്നു"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "ഈ അക്കൌണ്ട് നിലവില്‍ സര്‍വറിലേക്കു് കണക്ട് ചെയ്തിരിയ്ക്കുന്നു"
+
+#~ msgid "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "അതേ ശ്രോതസ്സ് ഉപയോഗിച്ചു് ഒരു പുതിയ കണക്ഷന്‍ ഉപയോഗിച്ചു് ഈ കണക്ഷന്‍ മാറ്റിസ്ഥാപിയ്ക്കുന്നു"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "അക്കൌണ്ട് സര്‍വറില്‍ നിലവിലുണ്ടു്"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "കണക്ഷന്‍ കൈകാര്യം ചെയ്യുന്നതിനായി സര്‍വറിനു് സാധ്യമല്ല, തിരക്കിലാണു്"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "സമ്മതപത്രം വീണ്ടും ആവശ്യപ്പെട്ടിരിയ്ക്കുന്നു"
+
+#~ msgid "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "സമ്മതപത്രം സുരക്ഷിതമല്ലാത്തൊരു സിഫര്‍ ആല്‍ഗോരിഥം ഉപയോഗിയ്ക്കുന്നു അല്ലെങ്കില്‍ ഉചിതമല്ല"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server certificate chain, exceed the limits "
+#~ "imposed by the cryptography library"
+#~ msgstr "സര്‍വറിന്റെ സമ്മതപത്രത്തിന്റെ വ്യാപ്തി, അല്ലെങ്കില്‍ സര്‍വര്‍ സമ്മതപത്ര ചെയിന്റെ വ്യാപ്തി, എന്നിവ പരിധിയില്‍ കൂടുന്നു"
+
+#~ msgid "Internal error"
+#~ msgstr "ആന്തരിക പിശക്"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "%s -ലേക്ക് കണക്ട് ചെയ്യുവാന്‍ സാധ്യമല്ല"
+
+#~ msgid "View account"
+#~ msgstr "അക്കൌണ്ട് കാണുക"
+
+#~ msgid "Open Calendar"
+#~ msgstr "കലണ്ടര്‍ തുറക്കുക"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "തീയതി, സമയ ക്രമീകരണങ്ങള്‍"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "ഈ പ്രയോഗങ്ങളില്‍ നിന്നും പുറത്തു് കടക്കുന്നതിനായി പുറത്തു കടക്കുക ക്ലിക്ക് ചെയ്തു് സിസ്റ്റത്തില്‍ നിന്നും പുറത്തു് കടക്കുക."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "സിസ്റ്റത്തില്‍ നിന്നു പുറത്തു കടക്കുന്നു."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "ഈ പ്രയോഗങ്ങളില്‍ നിന്നും പുറത്തു് കടക്കുന്നതിനായി പവര്‍ ഓഫ് ചെയ്യുക ക്ലിക്ക് ചെയ്തു സിസ്റ്റിന്റെ പവര്‍ ഓഫ് ചെയ്യുക."
+
+#~ msgid "Powering off the system."
+#~ msgstr "സിസ്റ്റത്തിന്റെ പവര്‍ ഓഫ് ചെയ്യുന്നു."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "ഈ പ്രയോഗങ്ങള്‍ നിറുത്തി സിസ്റ്റം പുനരാരംഭിക്കുവാന്‍ പുനരാരംഭിക്കൂ അമര്‍ത്തുക"
+
+#~ msgid "Restarting the system."
+#~ msgstr "സിസ്റ്റം പുനരാരംഭിക്കുന്നു."
+
+#~ msgid "Remove"
+#~ msgstr "നീക്കം ചെയ്യുക"
+
+#~ msgid "Clear Messages"
+#~ msgstr "സന്ദേശങ്ങള്‍ വെടിപ്പാക്കുക"
+
+#~ msgid "Notification Settings"
+#~ msgstr "അറിയിപ്പു് ക്രമീകരണങ്ങള്‍"
+
+#~ msgid "No Messages"
+#~ msgstr "സന്ദേശങ്ങളില്ല"
+
+#~ msgid "Message Tray"
+#~ msgstr "സന്ദേശത്തിന്റെ ട്രേ"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "സാര്‍വ്വജനികലഭ്യതാ സജ്ജീകരണങ്ങള്‍"
+
+#~ msgid "Visibility"
+#~ msgstr "കാഴ്ച"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "പുതിയൊരു ഡിവൈസ് സജ്ജമാക്കുക..."
+
+#~ msgid "Send Files…"
+#~ msgstr "ഫയലുകള്‍ അയയ്ക്കുക..."
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "കീബോര്‍ഡ് ക്രമീകരണങ്ങള്‍"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s-ല്‍ നിന്നുള്ള ആധികാരികത ഉറപ്പാക്കല്‍ ആവശ്യം"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "%s ഉപകരണത്തിനു് '%s' സേവനത്തിനുള്ള അനുവാദം ആവശ്യമുണ്ടു്."
+
+#~ msgid "Grant this time only"
+#~ msgstr "ഇത്തവണ മാത്രം "
+
+#~ msgid "Reject"
+#~ msgstr "നിരസിക്കു"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s-നുള്ള ജോടി ഉറപ്പിക്കല്‍"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "%s ഉപകരണത്തിനു് ഈ കമ്പ്യൂട്ടറുമായി ജോടി ചേരണം"
+
+#~ msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgstr "PIN '%06d' ഉപകരണത്തിലുള്ളതുമായി ചേരുന്നു എന്നു് ദയവായി ഉറപ്പാക്കുക."
+
+#~ msgid "Matches"
+#~ msgstr "ചേ‌ര്‍ച്ചകള്‍"
+
+#~ msgid "Does not match"
+#~ msgstr "ചേരുന്നില്ല"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s-നുള്ള ജോടി ആവശ്യപ്പെടുന്നു"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "ഉപകരണത്തില്‍ പറഞ്ഞിരിക്കുന്ന പിന്‍ ദയവായി നല്‍കുക."
+
+#~ msgid "OK"
+#~ msgstr "ശരി"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "വോള്യം, നെറ്റ്‌വര്‍ക്ക്, ബാട്ടറി"
+
+#~ msgid "disabled"
+#~ msgstr "പ്രവര്‍ത്തനരഹിതമാക്കി"
+
+#~ msgid "cable unplugged"
+#~ msgstr "കേബിള്‍ ഊരി"
+
+#~ msgid "unavailable"
+#~ msgstr "ലഭ്യമല്ല"
+
+#~ msgid "More…"
+#~ msgstr "കൂടുതല്‍..."
+
+#~ msgid "Connected (private)"
+#~ msgstr "കണക്ട് ചെയ്തിരിയ്ക്കുന്നു (സ്വകാര്യം)"
+
+#~ msgid "Wired"
+#~ msgstr "വയര്‍ഡ്"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "ഓട്ടോ ഇഥര്‍നെറ്റ്"
+
+#~ msgid "Auto broadband"
+#~ msgstr "ഓട്ടോ ബ്രോഡ്ബാന്‍ഡ്"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "ഓട്ടോ ഡയല്‍-അപ്പ്"
+
+#~ msgid "Auto %s"
+#~ msgstr "ഓട്ടോ %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "ഓട്ടോ ബ്ലൂടൂത്"
+
+#~ msgid "Auto wireless"
+#~ msgstr "ഓട്ടോ വയര്‍ലെസ്സ് "
+
+#~ msgid "Wi-Fi"
+#~ msgstr "വയര്‍ലസ്സ്"
+
+#~ msgid "Battery"
+#~ msgstr "ബാറ്ററി"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d മണിക്കൂര്‍ ബാക്കി"
+#~ msgstr[1] "%d മണിക്കൂറുകള്‍ ബാക്കി"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s ബാക്കിയുണ്ട്"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "മണിക്കൂര്‍"
+#~ msgstr[1] "മണിക്കൂറുകള്‍"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "മിനുട്ട്"
+#~ msgstr[1] "മിനുട്ടുകള്‍"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d മിനുട്ട് ബാക്കി"
+#~ msgstr[1] "%d മിനുട്ടുകള്‍ ബാക്കി"
+
+#~ msgid "AC Adapter"
+#~ msgstr "എസി അഡാപ്റ്റര്‍"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "ലാപ്പ്‌ടോപ്പ് ബാറ്ററി"
+
+#~ msgid "UPS"
+#~ msgstr "യുപിഎസ്"
+
+#~ msgid "Monitor"
+#~ msgstr "മോണിറ്റര്‍"
+
+#~ msgid "Mouse"
+#~ msgstr "മൗസ്"
+
+#~ msgid "PDA"
+#~ msgstr "പിഡിഎ"
+
+#~ msgid "Cell Phone"
+#~ msgstr "സെല്‍ ഫോണ്‍"
+
+#~ msgid "Media Player"
+#~ msgstr "മീഡിയ പ്ലേയര്‍"
+
+#~ msgid "Tablet"
+#~ msgstr "ടാബ്‌ലെറ്റ്"
+
+#~ msgid "Computer"
+#~ msgstr "കമ്പ്യൂട്ടര്‍"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "അജ്ഞാതം"
+
+#~ msgid "Available"
+#~ msgstr "ലഭ്യമാണ്"
+
+#~ msgid "Busy"
+#~ msgstr "തിരക്കിലാണ്"
+
+#~ msgid "Invisible"
+#~ msgstr "അദൃശ്യം"
+
+#~ msgid "Away"
+#~ msgstr "വിദൂരെയാണ്"
+
+#~ msgid "Idle"
+#~ msgstr "പ്രവര്‍ത്തനത്തിലല്ല"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "നിങ്ങളുടെ ചാറ്റ് അവസ്ഥ - തിരക്കില്‍"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online status has been adjusted to let "
+#~ "others know that you might not see their messages."
+#~ msgstr ""
+#~ "ചാറ്റ് സന്ദേശങ്ങള്‍ എന്ന പോലെ അറിയിപ്പുകള്‍ പ്രവര്‍ത്തന രഹിതമാക്കുന്നു. മറ്റുള്ളവരുടെ ചാറ്റ് സന്ദേശങ്ങള്‍ നിങ്ങള്‍ക്കു് കാണുവാന്‍ സാധ്യമല്ല എന്നു് "
+#~ "നിങ്ങളുടെ ഓണ്‍ലൈന്‍ അവസ്ഥയില്‍ വ്യക്തമാക്കുന്നു."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "അടച്ചുപൂട്ടുന്നത് അവരുടെ സൂക്ഷിക്കാത്ത ജോലികള്‍ നഷ്ട്ടപ്പെടുത്താം."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "ക്ഷമിയ്ക്കണം, ഇന്നത്തേക്കു് ഒന്നും ലഭ്യമല്ല:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s എന്ന ഒറക്കിള്‍ പറയുന്നു"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "അകത്തുകയറുക"
+
+#~ msgid "tray"
+#~ msgstr "ട്രേ"
+
+#~ msgid "More..."
+#~ msgstr "കൂടുതല്‍..."
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "പ്രയോഗങ്ങള്‍"
+
+#~ msgid "SETTINGS"
+#~ msgstr "ക്രമീകരണങ്ങള്‍"
+
+#~ msgid "Subscription request"
+#~ msgstr "വരിക്കാരനാകാനുള്ള അപേക്ഷ"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "<b>%X</b>-നു് <b>%A</b>-ല്‍ അയച്ചിരിയ്ക്കുന്നു"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "<b>%A</b>, <b>%B %d</b>, %Y-നു് അയച്ചിരിയ്ക്കുന്നു"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "%s-ലേക്കുള്ള കണക്ഷന്‍ പരാജയപ്പെട്ടു"
+
+#~ msgid "Reconnect"
+#~ msgstr "വീണ്ടും കണക്ട് ചെയ്യുക"
+
+#~ msgid "Browse Files..."
+#~ msgstr "ഫയലുകള്‍ നോക്കുക..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "ബ്രൌസ് ചെയ്യുന്ന ഡിവൈസില്‍ പിശക്"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "ആവശ്യപ്പെട്ട ഡിവൈസ് തെരയുവാന്‍ സാധ്യമല്ല, പിശക് '%s'"
+
+#~ msgid "Wireless"
+#~ msgstr "വയര്‍ലെസ്സ്"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN കണക്ഷനുകള്‍"
+
+#~ msgid "System Settings"
+#~ msgstr "സിസ്റ്റം ക്രമീകരണങ്ങള്‍"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "നിങ്ങള്‍ക്കു് ഇഷ്ടമുള്ള ഈസ്റ്റര്‍ മുട്ട"
+
+#~ msgid "United Kingdom"
+#~ msgstr "യുണൈറ്റഡ് കിങ്ഡം"
+
+#~ msgid "Default"
+#~ msgstr "സഹജം"
+
+#, fuzzy
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "OpenSearch വെബ്സൈറ്റ് ചേര്‍ക്കുക"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "സമയം സെക്കന്റ് ഉള്‍പ്പെടെ കാണിക്കുക"
+
+#, fuzzy
+#~ msgid "If true, display seconds in time."
+#~ msgstr "ശരി (ട്രൂ) എങ്കില്‍, സെക്കന്‍ഡുകളും സമയത്തില് കാണിക്കുക."
+
+#~ msgid "Show date in clock"
+#~ msgstr "ഘടികാരത്തില്‍ തിയതി കാണിക്കുക"
+
+#, fuzzy
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "ശരി (ട്രൂ) എങ്കില്‍,ഘടികാരത്തില്‍ സമയത്തിനു പുറമെ തീയതിയും കാണിക്കുക"
+
+#, fuzzy
+#~ msgid "CONTACTS"
+#~ msgstr "വിലാസങ്ങള്‍"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#, fuzzy
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "%V അണ്‍മൌണ്ടു് ചെയ്യാന്‍ കഴിഞ്ഞില്ല."
+
+#~ msgid "Retry"
+#~ msgstr "വിണ്ടും ശ്രമിക്കുക"
+
+#, fuzzy
+#~ msgid "Connect to..."
+#~ msgstr "സര്‍വറിലേക്കു് കണക്ടു് ചെയ്യുക"
+
+#, fuzzy
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ഹാര്‍ഡ്‍വയര്‍ ഡിവൈസുകള്‍"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "തെറ്റായ രഹസ്യവാക്ക്, വീണ്ടും ശ്രമിക്കുക"
+
+#, fuzzy
+#~ msgid "%s is online."
+#~ msgstr "ബട്ടണ്‍ ഓണ്‍ലൈന്‍ ആണു്"
+
+#, fuzzy
+#~ msgid "%s is offline."
+#~ msgstr "റിപ്പോസിറ്ററി ഓഫ് ലൈന്‍ ആണ്"
+
+#~ msgid "%s is away."
+#~ msgstr "%s ദൂരെയാണ്."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s തിരക്കിലാണ്."
+
+#~ msgid "Hidden"
+#~ msgstr "മറഞ്ഞ"
+
+#~ msgid "Power Off..."
+#~ msgstr "നിര്‍ത്തുക..."
+
+#, fuzzy
+#~ msgid "Online Accounts"
+#~ msgstr "അക്കൌണ്ടുകളുടെ പട്ടിക"
+
+#~ msgid "Log Out..."
+#~ msgstr "പുറത്ത് കടക്കുക..."
+
+#~ msgid "Home"
+#~ msgstr "വീട്"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Custom format of the clock"
+#~ msgstr "ഘടികാരത്തിന്റെ പ്രത്യേക ശൈലി"
+
+#~ msgid "Hour format"
+#~ msgstr "മണിക്കുര്‍ ശൈലി"
+
+#~ msgid "Clock Preferences"
+#~ msgstr "ഘടികാര മുന്‍ഗണനകള്‍"
+
+#~ msgid "Preferences"
+#~ msgstr "മുന്‍ഗണനകള്‍"
+
+#~ msgid "Search your computer"
+#~ msgstr "നിങ്ങളുടെ കമ്പ്യൂട്ടര്‍ തിരയുക"
+
+#~ msgid "Home Folder"
+#~ msgstr "ആസ്ഥാന ഫോള്‍ഡര്‍"
diff --git a/po/mr.po b/po/mr.po
new file mode 100644
index 0000000..a119c09
--- /dev/null
+++ b/po/mr.po
@@ -0,0 +1,2455 @@
+# translation of mr.po to Marathi
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Sandeep Shedmake <sshedmak@redhat.com>, 2011, 2012, 2013, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: mr\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-09-15 07:36+0000\n"
+"PO-Revision-Date: 2014-09-16 10:06+0530\n"
+"Last-Translator: Sandeep Shedmake <sshedmak@redhat.com>\n"
+"Language-Team: Marathi <maajhe-sanganak@freelists.org>\n"
+"Language: mr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "प्रणाली"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "संदेश ट्रे दाखवा"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "सक्रीय सूचनावर फोकस करा"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "पूर्वावलोकन दाखवा"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "सर्व ॲप्लिकेशन्स दाखवा"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "ॲप्लिकेशन मेन्यु उघडा"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME शेल"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "पटल व्यवस्थापन व ॲप्लिकेशन सुरू करणे"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME शेल एक्सटेंशन पसंती"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME शेल एक्सटेंशन्स् संरचीत करा"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME शेल (वेलँड कम्पोजिटर)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Alt-F2 पासून डेव्लपर्स् व चाचणीकर्त्यांसाठी आंतरिक साधणे सुरू करा"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "आंतरिक डिबगिंग व Alt-F2 संवादचा वापर करून निंयत्रणकरीता प्रवेश देतो."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+#| msgid "Uuids of extensions to enable"
+msgid "UUIDs of extensions to enable"
+msgstr "सुरू करण्याजोगी एक्सटेंशन्स्चे UUIDs"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+#| msgid ""
+#| "GNOME Shell extensions have a uuid property; this key lists extensions "
+#| "which should be loaded. Any extension that wants to be loaded needs to be "
+#| "in this list. You can also manipulate this list with the EnableExtension "
+#| "and DisableExtension DBus methods on org.gnome.Shell."
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME शेल एक्सटेंशन्सकडे UUID गुणधर्म असते; हि कि एक्सटेंशन्स दाखवते ज्यास "
+"लोड करणे आवश्यक "
+"आहे. लोड करण्याजोगी कोणत्याहि एक्सटेंशला सूचीमध्ये समाविष्टीत असणे आवश्यक "
+"आहे. org.gnome.Shell वरील "
+"EnableExtension व DisableExtension D-Bus मेथड्ससह या सूचीमध्ये बदल करणे शक्य "
+"आहे."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "एक्सटेंशन आवृत्ती सहत्वताची वैधता बंद करते"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME शेल फक्त एक्सटेंशन्स लोड करेल जे सध्याच्या आवृत्तीला समर्थन पुरवते. या "
+"पर्यायला सुरू केल्याने या तपासाला बंद करेल आणि सर्व एक्सटेंशन्स लोड करायचा "
+"प्रयत्न करेल समर्थनजोगी आवृत्ती असेल किंवा नसेल."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "पसंतीच्या ॲप्लिकेशन्स् करीता डेस्कटॉप फाइल IDs ची सूची"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "या आइडेंटिफायर्स् सह परस्पर ॲप्लिकेशन्स् पसंतीचे कक्षात दाखवले जाईल."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "ॲप पिकर अवलोकन"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "ॲप्लिकेशन पिकरमधील आत्ता पसंत केलेल्या अवलोकनचे इंडेक्स."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "आदेश (Alt-F2) संवादकरीता इतिहास"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "लूकिंग ग्लास संवादकरीता इतिहास"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+#| msgid "Always show the 'Log out' menuitem in the user menu."
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "नेहमी वापरकर्ता मेन्युमध्ये 'बाहेर पडा' मेन्यु घटक दाखवा."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+#| msgid ""
+#| "This key overrides the automatic hiding of the 'Log out' menuitem in "
+#| "single-user, single-session situations."
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"हि कि एकमेव-वापरकर्ता, एक-सत्र घटनांमध्ये 'बाहेर पडा' मेन्युघटकाला स्व छुपे "
+"करणे ओव्हरराइड करतो."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"एनक्रिप्टेड किंवा रिमोट फाइलसिस्टम माउंट करण्यासाठी पासवर्ड लक्षात ठेवायचे "
+"किंवा नाही"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"एनक्रिप्टेड साधन किंवा रिमोट फाइलसिस्टम माउंट केल्यानंतर शेल पासवर्डकरीता "
+"विनंती करेल. "
+"पासवर्डला भविष्यातील वापरकरीता साठवायचे असल्यास 'पासवर्ड लक्षात ठेवा' "
+"चेकबॉक्सचा वापर "
+"करा. हि कि चेकबॉक्सचे पूर्वनिर्धारित स्तर ठरवते."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "दिनदर्शिकामध्ये सप्ताह दिनांक दाखवा"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "खरे असल्यास, दिनदर्शिकमध्ये ISO सप्ताह दिनांक दाखवा."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "ॲप्लिकेशन मेन्यु उघडण्यासाठी किबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "ॲप्लिकेशन मेन्यु उघडण्यासाठी किबाइंडिंग."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"ॲप्लिकेशन्स दाखवा\" दृष्य उघडण्यासाठी किबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr "क्रिया अवलोकनचे \"ॲप्लिकेशन्स दाखवा\" दृष्य उघडण्यासाठी किबाइंडिंग."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+#| msgid "Keybinding to open the \"Show Applications\" view"
+msgid "Keybinding to open the overview"
+msgstr "पूर्वावलोकन उघडण्यासाठी किबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+#| msgid "Keybinding to open the \"Show Applications\" view"
+msgid "Keybinding to open the Activities Overview."
+msgstr "कृती पूर्वावलोकन उघडण्यासाठी किबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "संदेश ट्रेचे अवलोकन बदलण्यासाठी वापरण्याजोगी किबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "संदेश ट्रेचे अवलोकन बदलण्यासाठी वापरण्याजोगी किबाइंडिंग."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "सक्रीय सूचनावर फोकस करण्यासाठी किबाइंडिंग"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "सक्रीय सूचनावर फोकस करण्यासाठी किबाइंडिंग."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"किबाइंडिंग जी थांबते आणि पुन्हा कार्यरत ट्विन्स सक्रीय करते, डिबगिंग कारणास्तव"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "कुठले किबोर्ड वापरायचे"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "वापरण्याजोगी किबोर्डचे प्रकार."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "आत्ताच्या कार्यक्षेत्रकरिता लिमिट स्विचर."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"खरे असल्यास, फक्त आत्ताच्या कार्यक्षेत्रवरील पटलांच्या ॲप्लिकेसन्सना स्वीचर "
+"मध्ये दाखवले जाते. नाहीतर, सर्व ॲप्लिकेशन्स समाविष्ट केले जातील."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "ॲप्लिकेशन चिन्ह मोड."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"स्विचरमध्ये पटल कसे दाखवले जातात ते संरचीत करते. वैध पर्याय 'फक्त थंबनेल' "
+"(पटलाचे थंबनेल "
+"दाखवते), 'फक्त-ॲप-चिन्ह' (फक्त ॲप्लिकेशन चिन्ह दाखवते) किंवा 'दोंही'."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"खरे असल्यास, फक्त आत्ताच्या कार्यक्षेत्रवरील पटलांना स्वीचरमध्ये दाखवले जाते. "
+"नाहीतर, सर्व पटल समाविष्ट केले जातात."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "पॅरेंट पटलाला मोडल संवाद जोडा"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "GNOME शेल चालवतेवेळी हि कि org.gnome.mutter मधील कि खोडून पुनः लिहते."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "स्क्रीन एड्जेसवर पटलांना वगळतेवेळी एड्ज टाइलिंग सुरू करा"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "वर्कस्पेसेस्ना गतियरित्या व्यवस्थापीत केले जाते"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "फक्त प्रारंभिक मॉनिटरवरील वर्कस्पेसेस"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "पाँटरची हालचाल बंद होईपर्यंत माउस मोडमधील फोकस बदलावमध्ये विलंब करा"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "कॅप्टिव्ह पोर्टल"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s करीता पसंती संवाद लोड करतेवेळी त्रुटी आढळली:"
+
+#: ../js/extensionPrefs/main.js:155
+#| msgid "Configure GNOME Shell Extensions"
+msgid "GNOME Shell Extensions"
+msgstr "GNOME शेल एक्सटेंशन्स"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "रद्द करा"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "पुढील"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "कुलूपबंद अशक्य करा"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "प्रवेश करा"
+
+#: ../js/gdm/loginDialog.js:269
+#| msgid "Switch Session"
+msgid "Choose Session"
+msgstr "सत्र पसंत करा"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "सूचीत नाही?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(उ.दा., वापरकर्ता किंवा %s)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "वापरकर्तानाव: "
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "प्रवेश पटल"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "ओळखपटवतेवेळी त्रुटी"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(किंवा बोट फिरवा)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "आदेश आढळले नाही"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "आदेश वाचणे अशक्य:"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+#| msgid "Execution of '%s' failed:"
+msgid "Execution of “%s” failed:"
+msgstr "“%s” चालवणे अपयशी:"
+
+#: ../js/portalHelper/main.js:85
+#| msgid "Authentication Required"
+msgid "Web Authentication Redirect"
+msgstr "वेबवरील ओळख पटवणे पुन्हा निर्देशीत केले"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "नेहमी वापरण्याजोगी ॲप्लिकेशन्स येथे आढळेल"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "वारंवारता"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "सर्व"
+
+#: ../js/ui/appDisplay.js:1789
+msgid "New Window"
+msgstr "नविन पटल"
+
+#: ../js/ui/appDisplay.js:1815 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "पंसतीतून काढून टाका"
+
+#: ../js/ui/appDisplay.js:1821
+msgid "Add to Favorites"
+msgstr "पसंतीमध्ये समाविष्ट करा"
+
+#: ../js/ui/appDisplay.js:1830
+#| msgid "Show Text"
+msgid "Show Details"
+msgstr "तपशील दाखवा"
+
+#: ../js/ui/appFavorites.js:124
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s ला पसंतीमध्ये समाविष्ट केले आहे."
+
+#: ../js/ui/appFavorites.js:158
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s ला पसंतीतून काढून टाकले."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "सेटिंग्ज"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "पार्श्वभूमी बदला…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "सर्व दिवस"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Su"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Th"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "मागील महिना"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "पुढील महिना"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "काहीच वेळपत्रानुरूप नाही"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "आज"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "उद्या"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "हा सप्ताह"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "पुढचा सप्ताह"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "बाहेरिल ड्राइव्ह जोडा"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "बाहेरील ड्राइव्ह खंडीत केले"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "काढूनटाकण्याजोगी साधने"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s सह उघडा"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "बाहेर काढा"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "पासवर्ड:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "पुनः टाइप करा:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "जोडणी करा"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "पासवर्ड: "
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "कि: "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "आइडेंटिटि: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "प्राइव्हेट कि पासवर्ड: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "सर्व्हिस: "
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "वायरलेस नेटवर्कतर्फे आवश्यक ओळखपटवणे"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+#| msgid ""
+#| "Passwords or encryption keys are required to access the wireless network "
+#| "'%s'."
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"वायरलेस नेटवर्क “%s” करिता प्रवेश प्राप्त करण्यासाठी पासवर्डज किंवा "
+"एनक्रिप्शन किज आवश्यक आहे."
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "वायर्ड 802.1X ओळखपटवणे"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "नेटवर्क नाव: "
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "DSL ओळख पटवणे"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "PIN कोड आवश्यक"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "मोबाइल ब्रॉडबँड साधनकरीता PIN कोड आवश्यक आहे"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "मोबाईल ब्रॉडबँड नेटवर्क पासवर्ड"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+#| msgid "A password is required to connect to '%s'."
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” सह जोडणीकरिता पासवर्ड आवश्यक आहे."
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "ओळख पटवणे आवश्यक"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "प्रशासक"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "ओळख पटवा"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "माफ करा, ते कार्य करू शकले नाही. कृपया पुनःप्रयत्न करा."
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "आमंत्रण"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "कॉल"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "फाइल स्थानांतरन"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "गप्पा"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "बंद करणे अशक्य करा"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "बंद करा"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+#| msgid "<b>Yesterday</b>, <b>%H:%M</b>"
+msgid "Yesterday, %H∶%M"
+msgstr "काल, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+#| msgid "%A, %B %d"
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+#| msgctxt "event list time"
+#| msgid "%l:%M %p"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "काल, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+#| msgid "%a %l:%M %p"
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+#| msgid "%a %b %e, %l:%M %p"
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+#| msgid "%a %b %e, %l:%M %p"
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s ला %s म्हणून ओळखले जाते"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%s करीता आमंत्रण"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s तुम्हाला %s सह जोडणीकरीता आमंत्रण देत आहे"
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "नकारा"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "स्वीकारा"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s पासून व्हिडीओ कॉल्स्"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s पासून कॉल"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "उत्तर"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s तुम्हाला %s पाठवत आहे"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "ऑनलाइन असल्यावर %s परवानगी दृष्यास्पद करायची"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "नेटवर्क त्रुटी"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "ओळख पटवणे अपयशी"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "एंक्रिप्शन त्रुटी"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "प्रमाणपत्र पुरवले नाही"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "प्रमाणपत्र अविश्वासर्ह आहे"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "प्रमाणपत्राची वेळसमाप्ति"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "प्रमाणपत्र सुरू केले नाही"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "प्रमाणपत्र यजमाननाव जुळत नाही"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "प्रमाणपत्र फिंग्ररप्रिंट जुळत नाही"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "प्रमाणपत्र स्वयं स्वाक्षरि"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "स्थिती ऑफलाइनकरीता ठरवली आहे"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "एंक्रिप्शन अनुपलब्ध"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "प्रमाणपत्र अवैध आहे"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "जोडणी नकारली गेली"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "जोडणी स्थापीत करणे अशक्य"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "जोडणी खंडीत झाली"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "हे खाते आधिपासूनच सर्व्हरसह जुळले आहे"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "समान स्रोतचा वापर करून जोडणीला नविन जोडणीसह बदलाबदल केले आहे"
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "खाते आधिपासूनच सर्व्हरवर अस्तित्वात आहे"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "जोडणी हाताळण्यासाठी सर्व्हर सध्या खूप व्यस्थ आहे"
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "प्रमाणपत्र रद्द केले"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"प्रमाणपत्र असुरक्षित सिफर अल्गोरिदमचा वापर करते किंवा क्रिप्टोग्राफिकरित्या "
+"खूप कमजोर आहे"
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"सर्व्हर प्रमाणपत्राची लांबी, किंवा सर्व्हर प्रमाणपत्र चैनचे गांभीर्य, "
+"क्रिप्टोग्राफि "
+"लाइब्ररितर्फे लादलेल्या मर्यादापेक्षा जास्त आहे"
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "आंतरिक त्रुटी"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s सह जोडणी अशक्य"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "खाते दृष्य"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "अपरिचीत कारण"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "पटल"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "ॲप्लिकेशन्स् दाखवा"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "डॅश"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "दिनदर्शिका उघडा"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "क्लॉक्स उघडा"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "दिनांक व वेळ सेटिंग्ज"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s पासून बाहेर पडा"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "बाहेर पडा"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s स्वयं, %d सेकंदात बाहेर पडेल."
+msgstr[1] "%s स्वयं, %d सेकंदात बाहेर पडेल."
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "%d सेकंदात तुम्ही स्वयं बाहेर पडाल."
+msgstr[1] "%d सेकंदात तुम्ही स्वयं बाहेर पडाल."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "बाहेर पडा"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "बंद करा"
+
+#: ../js/ui/endSessionDialog.js:85
+#| msgid "Install Updates & Restart"
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "अद्यावत इंस्टॉल करा आणि बंद करा"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "प्रणाली स्वयं %d सेकंदात बंद होईल."
+msgstr[1] "प्रणाली स्वयं %d सेकंदात बंद होईल."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "उर्वरित सॉफ्टवेअर अद्यावत इंस्टॉल करा"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "पुनः सुरू करा"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "बंद करा"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "पुनः सुरू करा"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "प्रणाली स्वयं %d सेकंदात पुनः सुरू होईल."
+msgstr[1] "प्रणाली स्वयं %d सेकंदामध्ये पुनः सुरू होईल"
+
+#: ../js/ui/endSessionDialog.js:119
+#| msgid "Install Updates & Restart"
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "पुन्हा सुरू करा आणि अद्यावत इंस्टॉल करा"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+#| msgid "The system will restart automatically in %d second."
+#| msgid_plural "The system will restart automatically in %d seconds."
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"प्रणाली स्व पुन्हा सुरू होईल आणि %d सेकंदमध्ये अद्यावत इंस्टॉल करेल."
+msgstr[1] ""
+"प्रणाली स्व पुन्हा सुरू होईल आणि %d सेकंदमध्ये अद्यावत इंस्टॉल करेल."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "पुन्हा सुरू करा &amp; इंस्टॉल करा"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "इंस्टॉल &amp; बंद करा"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "अद्यावत इंस्टॉल झाल्यानंतर बंद करा"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "बॅटरी पावरवर सुरू आहे: अद्यावत इंस्टॉल करण्यापूर्वी कृपया प्लग इन करा."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "काही ॲप्लिकेशन्स व्यस्थ असतात किंवा काही न साठवलेली कार्य असतात."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "इतर वापरकर्त्यांनी प्रवेश केला आहे."
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (रिमोट)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (कंसोल)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "प्रतिष्ठापीत करा"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+#| msgid "Download and install '%s' from extensions.gnome.org?"
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.org पासून “%s” डाउनलोड आणि इंस्टॉल करायचे?"
+
+#: ../js/ui/keyboard.js:692 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "कळफलक"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "एक्सटेंशन्स् प्रतिष्ठापीत केले नाही"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ने कोणत्याहि त्रुटी दाखवले नाही."
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "त्रुटी लपवा"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "त्रुटी दाखवा"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "सुरू केले"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "बंद केले"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "त्रुटी"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "जुणे झाले"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "डाऊनलोड करत आहे"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "स्त्रोत पहा"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "वेब पृष्ठ"
+
+#: ../js/ui/messageTray.js:1326
+msgid "Open"
+msgstr "उघडा"
+
+#: ../js/ui/messageTray.js:1333
+msgid "Remove"
+msgstr "काढून टाका"
+
+#: ../js/ui/messageTray.js:1630
+msgid "Notifications"
+msgstr "सूचना"
+
+#: ../js/ui/messageTray.js:1637
+msgid "Clear Messages"
+msgstr "संदेश नष्ट करा"
+
+#: ../js/ui/messageTray.js:1656
+msgid "Notification Settings"
+msgstr "सूचना सेटिंग्ज"
+
+#: ../js/ui/messageTray.js:1709
+msgid "Tray Menu"
+msgstr "ट्रे मेन्यु"
+
+#: ../js/ui/messageTray.js:1926
+msgid "No Messages"
+msgstr "संदेश नाही"
+
+#: ../js/ui/messageTray.js:1968
+msgid "Message Tray"
+msgstr "संदेश ट्रे"
+
+#: ../js/ui/messageTray.js:2971
+msgid "System Information"
+msgstr "प्रणाली माहिती"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "अपरिचीत"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d नवीन संदेश"
+msgstr[1] "%d नवीन संदेश"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "पूर्ववत् करा"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "पूर्वावलोकन"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "शोधण्याकरीता टाइप करा…"
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "बाहेर पडा"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "क्रिया"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "वरची पट्टी"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "आदेश द्या"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "बंद करा"
+
+#: ../js/ui/runDialog.js:277
+#| msgid "Estimating…"
+msgid "Restarting…"
+msgstr "पुन्हा सुरू करत आहे…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d नवीन सूचना"
+msgstr[1] "%d नवीन सूचना"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "कुलूपबंद"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME ने पडदा बंद करायला हवे"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "बंद करणे अशक्य"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "ॲप्लिकेशनतर्फे लॉक रोखले गेले"
+
+#: ../js/ui/search.js:594
+msgid "Searching…"
+msgstr "शोधत आहे..."
+
+#: ../js/ui/search.js:596
+msgid "No results."
+msgstr "परिणाम नाही."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "प्रत बनवा"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "चिकटवा"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "मजकूर दाखवा"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "मजकूर लपवा"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "पासवर्ड"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "पासवर्ड लक्षात ठेवा"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "ॲक्सेसिबिलिटि"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "झूम"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "स्क्रीन रिडर"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "स्क्रीन किबोर्ड"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "दृष्टीविषयक सतर्कता"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "स्टीकी किज्"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "स्लो किज्"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "बाऊंस् किज्"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "माऊस किज्"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "उच्च विरोधाभास"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "मोठे मजकूर"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "ब्ल्यूटूथ"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "बंद करा"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "ब्ल्यूटूथ सेटिंग्ज"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+#| msgid "Connected (private)"
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d जुळलेले साधन"
+msgstr[1] "%d जुळलेल्या साधने"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+#| msgid "Connect"
+msgid "Not Connected"
+msgstr "जुळलेले नाही"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "उजळपणा"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "किबोर्ड लेआऊट दाखवा"
+
+#: ../js/ui/status/location.js:65
+#| msgid "Notifications"
+msgid "Location"
+msgstr "ठिकाण"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+#| msgid "Disabled"
+msgid "Disable"
+msgstr "बंद करा"
+
+#: ../js/ui/status/location.js:73
+#| msgid "Power Settings"
+msgid "Privacy Settings"
+msgstr "गोपणीय सेटिंग्ज"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "वापरात आहे"
+
+#: ../js/ui/status/location.js:180
+#| msgid "Enabled"
+msgid "Enable"
+msgstr "सुरू करा"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<अपरिचीत>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+#| msgid "Offline"
+msgid "Off"
+msgstr "बंद करा"
+
+#: ../js/ui/status/network.js:459
+#| msgid "Connect"
+msgid "Connected"
+msgstr "जोडणी केली"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+#| msgid "unmanaged"
+msgid "Unmanaged"
+msgstr "अव्यवस्थापीत"
+
+#: ../js/ui/status/network.js:465
+#| msgid "disconnecting..."
+msgid "Disconnecting"
+msgstr "खंडीत करत आहे"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+#| msgid "Connection"
+msgid "Connecting"
+msgstr "जोडणी"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+#| msgid "authentication required"
+msgid "Authentication required"
+msgstr "ओळख पटवणे आवश्यक"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+#| msgid "firmware missing"
+msgid "Firmware missing"
+msgstr "फर्मवेअर आढळले नाही"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "अनुपलब्ध"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "जोडणी अपयशी"
+
+#: ../js/ui/status/network.js:504
+#| msgid "Settings"
+msgid "Wired Settings"
+msgstr "वायर्ड सेटिंग्ज"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+#| msgid "Mobile broadband"
+msgid "Mobile Broadband Settings"
+msgstr "मोबाईल ब्रॉडबँड सेटिंग्ज"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+#| msgid "hardware disabled"
+msgid "Hardware Disabled"
+msgstr "हार्डवेअर बंद केले"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "इंटरनेट जोडणी म्हणून वापर करा"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "एयरप्लेन मोड सुरू आहे"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "एयरप्लेन मोड सुरू असतेवेळी वाय-फाय बंद असते."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "एयरप्लेन मोड बंद करा"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "वाय-फाय बंद आहे"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "नेटवर्कशी जोडणी करण्यासाठी वाय-फायला सुरू करणे आवश्यक आहे."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "वाय-फाय सुरू करा"
+
+#: ../js/ui/status/network.js:851
+#| msgid "Network"
+msgid "Wi-Fi Networks"
+msgstr "वाय-फाय नेटवर्क्स"
+
+#: ../js/ui/status/network.js:853
+#| msgid "Enable networking"
+msgid "Select a network"
+msgstr "नेटवर्क पसंत करा"
+
+#: ../js/ui/status/network.js:882
+#| msgid "Network"
+msgid "No Networks"
+msgstr "नेटवर्क नाही"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "बंद करण्यासाठी या हार्डवेअर स्विचचा वापर करा"
+
+#: ../js/ui/status/network.js:1173
+#| msgid "Network"
+msgid "Select Network"
+msgstr "नेटवर्क नीवडा"
+
+#: ../js/ui/status/network.js:1179
+#| msgid "Settings"
+msgid "Wi-Fi Settings"
+msgstr "वाय-फाय सेटिंग्ज"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "सुरू करा"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "हॉटस्पॉट सुरू करा"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "जोडणी करत आहे..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "ओळख पटवणे आवश्यक आहे"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "जोडणी अपयशी"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "जाळं संयोजना"
+
+#: ../js/ui/status/network.js:1482
+#| msgid "Settings"
+msgid "VPN Settings"
+msgstr "VPN सेटिंग्ज"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "नेटवर्क मॅनेजर"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "नेटवर्क जोडणी सुरू करणे अपयशी"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "पावर सेटिंग्ज"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "संपूर्णतया चार्ज झाले"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "अंदाज घेत आहे…"
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d उर्वरित (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d पूर्ण होईपर्यंत (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "बॅटरि"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "एयरप्लेन मोड"
+
+#: ../js/ui/status/rfkill.js:85
+#| msgid "Open"
+msgid "On"
+msgstr "सुरू करा"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "उपयोक्ता बदला"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "बाहेर पडा"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "निर्देशन कुलूपबंद"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "निलंबित"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "बंद करा"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "ध्वनी बदलले"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "आवाज"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "माइक्रोफोन"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "इतर वापरकर्ता म्हणून प्रवेश करा"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "पटल उघडा"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "ॲप्लिकेशन्स्"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "शोधा"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+#| msgid "'%s' is ready"
+msgid "“%s” is ready"
+msgstr "“%s” सज्ज आहे"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "तुम्हाला या डिस्पले सेटिंग्ज जपवून ठेवायचे?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+#| msgid "Power Settings"
+msgid "Revert Settings"
+msgstr "सेटिंग्जना मूळस्थितीत आणा"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "बदल जपवून ठेवा"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "सेटिंग्ज बदल %d सेकंदनंतर मूळस्थितीत येतील"
+msgstr[1] "सेटिंग्ज बदल %d सेकंदनंतर मूळस्थितीत येतील"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "छोटे करा"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "छोटे अशक्य करा"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "मोठे करा"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "हलवा"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "पुन्हा आकार द्या"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "शीर्षकपट्टीला ऑनस्क्रीनरित्या हलवा"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "नेहमी शीर्षवरील"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "नेहमी दृष्यास्पद कार्यक्षेत्रवर आढळते"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "वर्कस्पेस अपकडे जा"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "वर्कस्पेस डाउनकडे जा"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "एव्हल्युशन दिनदर्शिका"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u आऊटपुट"
+msgstr[1] "%u आऊटपुट"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u इंपुट"
+msgstr[1] "%u इंपुट"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "प्रणाली आवाज"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "छपाई आवृत्ती"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "प्रवेश पडद्याकरीता GDM तर्फे वापरण्याजोगी मोड"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "निर्देशीत मोडचा वापर करा, उ.दा. प्रवेश स्क्रीनकरीता \"gdm\""
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "संभाव्य मोड्स्ची सूची दाखवा"
+
+#: ../src/shell-app.c:666
+#, c-format
+#| msgid "Failed to launch '%s'"
+msgid "Failed to launch “%s”"
+msgstr "“%s” सुरू करण्यास अपयशी"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "पासवर्डस् जुळत नाही."
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "पासवर्ड रिकामे असणे अशक्य"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ओळख पटवा संवाद वापरकर्त्याद्वारे वगळले"
+
+#~ msgid "Screenshots"
+#~ msgstr "स्क्रीनशॉटस्"
+
+#~ msgid "Record a screencast"
+#~ msgstr "स्क्रीनकास्ट रेकॉर्ड करा"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "ॲप्लिकेशन वापरविषयी स्थिती गोळा करायची"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). हा डाटा व्यक्तिगत असेपर्यंत, यांस गोपणीय "
+#~ "कारणास्तव you may want to disable this for privacy reasons. कृपया लक्षात ठेवा "
+#~ "असे केल्यास आधिपासूनच साठवलेला डाटा काढून टाकणे अशक्य होईल."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "फोल्डर्स नुरूप दाखवण्याजोगी विभागांची सूची"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "या सूचीतील प्रत्येक विभागाचे नाव ॲप्लिकेशन दृष्यमध्ये फोल्डर म्हणून प्रस्तुत केले जाईल, मुख्य "
+#~ "दृष्यमध्ये इनलाइन दाखवण्याऐवजी."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "वापरकर्तातर्फे ठरवलेले शेवटचे IM हाजेरी साठवण्याकरीता आंतरिकपणे वापरले जाते. येथील मूल्य "
+#~ "TpConnectionPresenceType एन्युमरेशनपासूनचे आहे."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "वापरकर्तातर्फे ठरवलेले शेवटचे सत्र हाजेरी साठवण्याकरीता आंतरिकपणे वापरले जाते. येथील मूल्य "
+#~ "GsmPresenceStatus एन्युमरेशनपासूनचे आहे."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "स्क्रीन रेकॉर्डरमधील बदलसाठी किबाइंडिंग"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "बिल्टइन स्क्रीन रेकॉर्डर सुरू किंवा थांबवण्यासाठी किबाइंडिंग."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "स्क्रिनकास्ट्स् रेकॉर्ड करण्यासाठी वापरलेले फ्रेमरेट."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "फ्रेम्स्-दर-सेकंदमध्ये GNOME शेलच्या सक्रीनकास्ट रेकॉर्डरद्वारे रेकॉर्ड केलेल्या परिणामक "
+#~ "सक्रीनकास्टचा फ्रेमरेट."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "स्क्रीनकास्ट एंकोड करण्यासाठी वापरलेले gstreamer पाइपलाइन"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "रेकॉर्डिंग्स् एंकोड करण्यासाठी GStreamer पाइपलाइन ठरवतो. gst-launch सुरू करण्यासाठी "
+#~ "मांडणीचा वापर करतो. पाइपलाइनमध्ये जोडणी अशक्य सिंक पॅड असायला हवे जेथे रेकॉर्डेड "
+#~ "व्हिडीओ रेकॉर्ड केले जाते. सहसा जोडणी अशक्य स्रोत पॅड असते; पॅडपासूनचे आऊटपुट, आऊटपुट "
+#~ "फाइलमध्ये लिहले जाते. तरी पाइपलाइन स्वतःच्या आऊटपुटची काळजी घेतो - याचा वापर "
+#~ "shout2send किंवा समानतर्फे icecast सर्व्हरकरीता आऊटपुट पाठवण्याकरीता केला जातो. "
+#~ "रिकामे मूल्यकरीता सेट अशक्य किंवा शक्य केल्यावर, मूळ पाइपलाइनचा वापर केला जातो. हे "
+#~ "सध्या 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 "
+#~ "deadline=1000000 threads=%T ! queue ! webmmux' आहे व VP8 कोडेकचा वापर करून "
+#~ "WEBM करीता रेकॉर्डिंग करतो. %T चा वापर प्रणालीवरील कमाल थ्रेड गणणा ओळखण्यासाठी "
+#~ "प्लेसहोल्डर म्हणून केला जातो."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "स्क्रिनकास्ट साठवण्याकरीता वापरलेले फाइल एक्सटेंशन"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "सध्याच्या दिनांकवर आधारित रेकॉर्डेड स्क्रिनकास्ट्स्करीता फाइलचेना एकमेव फाइलनाव असणार, "
+#~ "व या एक्सटेंशनचा वापर करा. वेगळ्या कंटेनर स्वरूपकरीता रेकॉर्डिंग करतेवेळी बदलणे आवश्यक आहे."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "शीर्षकपट्टीवर बटनांची मांडणी"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "GNOME शेल चालवतेवेळी हि कि org.gnome.desktop.wm.preferences मधील कि खोडून पुनः "
+#~ "लिहते."
+
+#~ msgid "Extension"
+#~ msgstr "एक्सटेंशन"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "वरील कॉम्बोबॉक्सचा वापर करून संरचनाकरीता एक्सटेंशनचा वापर करा."
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "सत्र…"
+
+#~ msgid "Power"
+#~ msgstr "पावर"
+
+#~ msgid "Restart"
+#~ msgstr "पुनः सुरू करा"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d %t पासून स्क्रीनकास्ट"
+
+#~| msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~| msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "ॲप्लिकेशन्स् मधून बाहेर पडण्यासाठी बाहेर पडा क्लिक करा व प्रणालीतून बाहेर पडा."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "प्रणालीतून बाहेर पडत आहे."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "ॲप्लिकेशन्स् बंद करण्यासाठी बंद करा क्लिक करा व प्रणाली बंद करा."
+
+#~ msgid "Powering off the system."
+#~ msgstr "प्रणाली बंद करत आहे."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "ॲप्लिकेशन्स् बंद करण्यासाठी पुनः सुरू करा क्लिक करा व प्रणाली पुनः सुरू करा."
+
+#~ msgid "Restarting the system."
+#~ msgstr "प्रणाली पुनःसुरू करत आहे."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "जागतीक प्रवेश संयोजना"
+
+#~ msgid "Visibility"
+#~ msgstr "दर्शनियता"
+
+#~| msgid "Send Files to Device..."
+#~ msgid "Send Files to Device…"
+#~ msgstr "साधनाकडे फाइल्स पाठवा…"
+
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "नवीन साधनची मांडणी करा…"
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "फाइल्स पाठवा..."
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "कळफलक सेटिंग्ज"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "माऊस सेटिंग्ज"
+
+#~ msgid "Sound Settings"
+#~ msgstr "आवाज सेटिंग्ज"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s पासून ओळख पटवण्याची विनंती"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "साधन %s ला सर्व्हिस '%s' करीता प्रवेश पाहिजे"
+
+#~ msgid "Always grant access"
+#~ msgstr "नेहमी प्रवेश द्या"
+
+#~ msgid "Grant this time only"
+#~ msgstr "फक्त याचवेळी मान्य करा"
+
+#~ msgid "Reject"
+#~ msgstr "नकारा"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s करीता जोड पुष्टी करा"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "साधन %s ला या संगणकाशी जोडणी करायची"
+
+#~ msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgstr "कृपया PIN '%06d' साधनावरील पिनशी जुळते, याची खात्री करा."
+
+#~ msgid "Matches"
+#~ msgstr "जोड"
+
+#~ msgid "Does not match"
+#~ msgstr "जुळत नाही"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s करीता जोड विनंती"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "कृपया साधनावर निर्देशीत PIN द्या."
+
+#~ msgid "OK"
+#~ msgstr "ठीक आहे"
+
+#~| msgid "Region and Language Settings"
+#~ msgid "Region & Language Settings"
+#~ msgstr "क्षेत्र व भाषांचे सेटिंग्ज"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "वॉल्यूम, नेटवर्क, बॅटरि"
+
+#~ msgid "disabled"
+#~ msgstr "बंद केले"
+
+#~ msgid "cable unplugged"
+#~ msgstr "केबल जोडले नाही"
+
+#~ msgid "unavailable"
+#~ msgstr "अनुपलब्ध"
+
+#~ msgid "More…"
+#~ msgstr "आणखी…"
+
+#~ msgid "Wired"
+#~ msgstr "वायर्ड्"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "स्वयं इथरनेट"
+
+#~ msgid "Auto broadband"
+#~ msgstr "स्वयं ब्रॉडबँड"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "स्वयं डायल-अप"
+
+#~ msgid "Auto %s"
+#~ msgstr "स्वयं %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "स्वयं ब्ल्यूटूथ"
+
+#~ msgid "Auto wireless"
+#~ msgstr "स्वयं वायरलेस्"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "वाय-फाय"
+
+#~ msgid "Networking is disabled"
+#~ msgstr "नेटवर्किंग बंद आहे"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d तास उर्वरित"
+#~ msgstr[1] "%d तास उर्वरित"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s उर्वरित"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "तास"
+#~ msgstr[1] "तास"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "मिनिट"
+#~ msgstr[1] "मिनिट"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d मिनीट उर्वरित"
+#~ msgstr[1] "%d मिनीटे उर्वरित"
+
+#~ msgctxt "percent of battery remaining"
+#~ msgid "%d%%"
+#~ msgstr "%d%%"
+
+#~| msgid "AC adapter"
+#~ msgid "AC Adapter"
+#~ msgstr "AC अडॅप्टर"
+
+#~| msgid "Laptop battery"
+#~ msgid "Laptop Battery"
+#~ msgstr "लॅपटॉप बॅटरि"
+
+#~ msgid "Monitor"
+#~ msgstr "मॉनीटर"
+
+#~ msgid "Mouse"
+#~ msgstr "माउस"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~| msgid "Cell phone"
+#~ msgid "Cell Phone"
+#~ msgstr "सेल फोन"
+
+#~| msgid "Media player"
+#~ msgid "Media Player"
+#~ msgstr "मिडीया वादक"
+
+#~ msgid "Tablet"
+#~ msgstr "टॅबलेट"
+
+#~ msgid "Computer"
+#~ msgstr "संगणक"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "अपरिचीत"
+
+#~ msgid "Available"
+#~ msgstr "उपलब्ध"
+
+#~ msgid "Busy"
+#~ msgstr "व्यस्त"
+
+#~ msgid "Invisible"
+#~ msgstr "अदृष्य"
+
+#~ msgid "Away"
+#~ msgstr "दूर आहे"
+
+#~ msgid "Idle"
+#~ msgstr "रिकामे"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "तुमची चॅट स्थिती व्यस्थकरीता ठरवले जाईल"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "सूचना आता बंद केले आहे, चॅट संदेश समाविष्टीत. इतरांना तुमचे संदेश दिसणार नाही हे "
+#~ "कळवण्यासाठी ऑनलाइन स्थिती सुस्थीत केली आहे."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "बंद केल्याने न साठवलेले कार्य गमवण्याची शक्यता आहे."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "माफ करा, चातुर्या आढळले नाही:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s ऑरॅकल म्हणतो"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "प्रवेश करा"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APPLICATIONS"
+
+#~ msgid "SETTINGS"
+#~ msgstr "SETTINGS"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Subscription request"
+#~ msgstr "सबस्क्रिप्शन विनंती"
+
+#~ msgid "Connection error"
+#~ msgstr "जोडणी त्रुटी"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "<b>%X</b> वेळी, <b>%A</b> ला पाठवले"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "<b>%A</b>, <b>%B %d</b>, %Y वेळी पाठवले"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "%s सह जोडणी अपयशी"
+
+#~ msgid "Reconnect"
+#~ msgstr "पुनःजोडणी करा"
+
+#~ msgid "tray"
+#~ msgstr "ट्रे"
+
+#~ msgid "Browse Files..."
+#~ msgstr "फाइल्स् तपासा..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "साधनाची तपासणी करतेवेळी त्रुटी"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "विनंती केलेल्या साधनाची तपासणी अशक्य, त्रुटी '%s' आहे"
+
+#~ msgid "More..."
+#~ msgstr "अधिक..."
+
+#~ msgid "Wireless"
+#~ msgstr "वायरलेस्"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN जोडणी"
+
+#~ msgid "System Settings"
+#~ msgstr "प्रणाली मांडणी"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "तुमच्या पसंतीचा इस्टर एग"
+
+#~ msgid "United Kingdom"
+#~ msgstr "यूनाइटेड किंगडम"
+
+#~ msgid "Default"
+#~ msgstr "मुलभूत"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "'%s' माऊंट अशक्य करण्यास अपयशी"
+
+#~ msgid "Retry"
+#~ msgstr "पुनःप्रयत्न करा"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "PLACES & DEVICES"
+
+#~ msgid "Home"
+#~ msgstr "होम"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "OpenSearch प्रोव्हाइडर्स् बंद केले"
+
+#~ msgid "Connect to..."
+#~ msgstr "यासह जोडणी करा..."
+
+#~ msgid "Passphrase"
+#~ msgstr "पासफ्रेज"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "सेकंदात वेळ दाखवा"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "खरे असल्यास, वेळेत सेकंद दाखवा."
+
+#~ msgid "Show date in clock"
+#~ msgstr "घड्याळात दिनांक दाखवा"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "खरे असल्यास,वेळेबरोबर तारीख पण घड्याळात दाखवा."
+
+#~ msgid "CONTACTS"
+#~ msgstr "संपर्क"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "चुकिचा पासवर्ड, कृपया पुनः प्रयत्न करा"
+
+#~ msgid "%s is online."
+#~ msgstr "%s ऑनलाइन आहे."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s ऑफलाइन आहे."
+
+#~ msgid "%s is away."
+#~ msgstr "%s दूर आहे."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s व्यग्र आहे."
+
+#~ msgid "Hidden"
+#~ msgstr "लवपलेले"
+
+#~ msgid "Power Off..."
+#~ msgstr "बंद करा..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "ऑनलाइन खाते"
+
+#~ msgid "Lock Screen"
+#~ msgstr "पडदा कुलूपबंद करा"
+
+#~ msgid "Log Out..."
+#~ msgstr "बाहेर पडा..."
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should not be loaded."
+#~ msgstr ""
+#~ "GNOME शेल एक्सटेंशन्स्मध्ये uuid गुणधर्म आढळते; या किमध्ये एक्सटेंशन्स् आहेत ज्यांना लोड "
+#~ "करायची आवश्यकता नाही."
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "RECENT ITEMS"
+
+#~ msgid "Localization Settings"
+#~ msgstr "भाषांतरन सेटिंग्ज"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "तुम्ही आत्ता मोबाईल ब्रॉडबँड जोडणी '%s' शी जुळले आहात"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "तुम्ही आत्ता वायरलेस नेटवर्क '%s' शी जुळले आहात"
+
+#~ msgid "You're now connected to wired network '%s'"
+#~ msgstr "तुम्ही आत्ता वायर्ड् नेटवर्क '%s' शी जुळले आहात"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "तुम्ही आत्ता VPN नेटवर्क '%s' शी जुळले आहात"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s ने सुरवात पूर्ण केले"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "एका मिनीटापूर्वी"
+
+#~ msgid "%d minute ago"
+#~ msgid_plural "%d minutes ago"
+#~ msgstr[0] "%d मिनीट पूर्वी"
+#~ msgstr[1] "%d मिनीट पूर्वी"
+
+#~ msgid "%d hour ago"
+#~ msgid_plural "%d hours ago"
+#~ msgstr[0] "%d तास पूर्वी"
+#~ msgstr[1] "%d तास पूर्वी"
+
+#~ msgid "%d day ago"
+#~ msgid_plural "%d days ago"
+#~ msgstr[0] "%d दिवस पूर्वी"
+#~ msgstr[1] "%d दिवस पूर्वी"
+
+#~ msgid "%d week ago"
+#~ msgid_plural "%d weeks ago"
+#~ msgstr[0] "%d आठवडा पूर्वी"
+#~ msgstr[1] "%d आठवडा पूर्वी"
+
+#~ msgid "Home Folder"
+#~ msgstr "होम फोल्डर"
diff --git a/po/ms.po b/po/ms.po
new file mode 100644
index 0000000..10ee7c6
--- /dev/null
+++ b/po/ms.po
@@ -0,0 +1,3068 @@
+# Malay translations for gnome-shell package.
+# Copyright (C) 2011 Listed translators
+# This file is distributed under the same license as the gnome-shell package.
+# Andi Rady Kurniawan <niariot87@gmail.com>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master ms\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2020-01-25 01:15+0000\n"
+"PO-Revision-Date: 2020-01-27 03:20+0800\n"
+"Last-Translator: abuyop <abuyop@gmail.com>\n"
+"Language-Team: Pasukan Terjemahan GNOME Malaysia\n"
+"Language: ms\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Poedit 2.0.6\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistem"
+
+#: data/50-gnome-shell-system.xml:9
+#| msgid "%d new notification"
+#| msgid_plural "%d new notifications"
+msgid "Show the notification list"
+msgstr "Tunjuk senarai pemberitahuan"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokus pada pemberitahuan aktif"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Tunjuk selayang pandang"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Tunjuk semua aplikasi"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Buka menu aplikasi"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:209
+#| msgid "GNOME Shell Extensions"
+msgid "Shell Extensions"
+msgstr "Sambungan-Sambungan Shell"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfigur Sambungan-Sambungan Shell GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Shell GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Pengurusan tetingkap dan pelancaran aplikasi"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Benarkan alatan dalaman yang berguna untuk para pembangun dan penguji "
+"melalui Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Benarkan capaian pada alatan penyahpepijatan dan pemantauan dalaman melalui "
+"dialog Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Sambungan UUID untuk dibenarkan"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Sambungan-sambungan GNOME Shell mempunyai satu sifat UUID; kunci ini "
+"menyenaraikan sambungan-sambungan yang patut dimuatkan. Sebarang sambungan "
+"yang hendak dimuatkan perlu berada di dalam senarai ini. Anda juga boleh "
+"memanipulasi senarai ini dengan kaedah D-Bus EnableExtension dan "
+"DisableExtension yang terdapat di org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+#| msgid "UUIDs of extensions to enable"
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID sambungan yang memaksa pelumpuhan"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+#| msgid ""
+#| "GNOME Shell extensions have a UUID property; this key lists extensions "
+#| "which should be loaded. Any extension that wants to be loaded needs to be "
+#| "in this list. You can also manipulate this list with the EnableExtension "
+#| "and DisableExtension D-Bus methods on org.gnome.Shell."
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Sambungan Shell GNOME mempunyai sifat UUID; kunci ini menyenaraikan "
+"sambungan-sambungan yang patut dilumpuhkan, walaupun jika ia telah dimuatkan "
+"sebagai sebahagian daripada mod semasa. Anda boleh memanipulasikan senarai "
+"ini dengan kaedah D-Bus EnableExtension dan DisableExtension D-Bus yang "
+"terdapat di dalam org.gnome.Shell. Kunci ini mengikut keutamaan di dalam "
+"tetapan \"enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Lumpuhkan sambungan-sambungan pengguna"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Lumpuhkan semua sambungan yang dibenarkan oleh pengguna tanpa mempengaruhi "
+"tetapan \"enabled-extension\"."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Lumpuhkan pengesahan keserasian versi sambungan"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell hanya akan memuatkan sambungan-sambungan yang menyokong versi "
+"semasa. Dengan membenarkan pilihan ini, kotak tanda dinyahtanda dan semua "
+"sambungan akan dimuatkan tanpa mengira sama ada versi yang menyokong atau "
+"sebaliknya."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Senarai ID fail desktop untuk aplikasi kegemaran"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Aplikasi-aplikasi bagi setiap pengenal pasti ini akan dipaparkan di kawasan "
+"kegemaran."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "App Picker View"
+msgstr "Pandangan Pemilih Apl"
+
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "Index of the currently selected view in the application picker."
+msgstr "Indeks bagi pandangan terpilih semasa di dalam pemilih aplikasi."
+
+#: data/org.gnome.shell.gschema.xml.in:69
+msgid "History for command (Alt-F2) dialog"
+msgstr "Sejarah untuk dialog perintah (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:74
+msgid "History for the looking glass dialog"
+msgstr "Sejarah untuk dialog looking glass"
+
+#: data/org.gnome.shell.gschema.xml.in:78
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Sentiasa tunjuk item menu \"Daftar keluar\" di dalam menu pengguna."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Kunci ini membatalkan penyembunyian automatik bagi item menu \"Daftar keluar"
+"\" dalam situasi pengguna-tunggal, sesi-tunggal."
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Sama ada hendak mengingati kata laluan untuk melekap tersulit atau sistem "
+"fail jauh"
+
+#: data/org.gnome.shell.gschema.xml.in:87
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Shell akan meminta satu kata laluan ketika peranti tersulit atau sistem fail "
+"jauh dilekapkan. Sekiranya kata laluan boleh disimpan untuk kegunaan akan "
+"datang gunakan kotak tanda \"Ingat Kata Laluan\" akan hadir. Kunci ini "
+"menetapkan keadaan lalai bagi kotak tanda."
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Sama ada penyesuai Bluetooth lalai telah pasangkan peranti-peranti yang "
+"berkaitan dengannya"
+
+#: data/org.gnome.shell.gschema.xml.in:97
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Shell hanya menunjukkan item menu Bluetooth sekiranya penyesuai Bluetooth "
+"telah dihidupkan, atau jika terdapat peranti-peranti yang dipasang berkaitan "
+"dengan penyesuai lalai. Ia akan ditetapkan semula jika penyesuai lalai tidak "
+"mempunyai peranti-peranti yang berkaitan dengannya."
+
+#: data/org.gnome.shell.gschema.xml.in:106
+msgid "Enable introspection API"
+msgstr "Benarkan API introspection"
+
+#: data/org.gnome.shell.gschema.xml.in:107
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+"Benarkan satu API D-Bus yang membolehkan kaji semua keadaan aplikasi shell."
+
+#: data/org.gnome.shell.gschema.xml.in:119
+msgid "Keybinding to open the application menu"
+msgstr "Pengikatan kekunci untuk membuka menu aplikasi"
+
+#: data/org.gnome.shell.gschema.xml.in:120
+msgid "Keybinding to open the application menu."
+msgstr "Pengikatan kekunci untuk membuka menu aplikasi."
+
+#: data/org.gnome.shell.gschema.xml.in:126
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Pengikatan kekunci untuk membuka pandangan \"Tunjuk Aplikasi\""
+
+#: data/org.gnome.shell.gschema.xml.in:127
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Pengikatan kekunci untuk membuka pandangan \"Tunjuk Aplikasi-Aplikasi\" bagi "
+"Selayang Pandang Aktiviti."
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid "Keybinding to open the overview"
+msgstr "Pengikatan kekunci untuk membuka selayang pandang"
+
+#: data/org.gnome.shell.gschema.xml.in:135
+msgid "Keybinding to open the Activities Overview."
+msgstr "Pengikatan kekunci untuk membuka Selayang Pandang Aktiviti-Aktiviti."
+
+#: data/org.gnome.shell.gschema.xml.in:141
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Pengikatan untuk togol ketampakan senarai pemberitahuan"
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Pengikatan untuk togol ketampakan senarai pemberitahuan."
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to focus the active notification"
+msgstr "Pengikatan kekunci untuk fokuskan pemberitahuan aktif"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to focus the active notification."
+msgstr "Pengikatan kekunci untuk fokuskan pemberitahuan aktif."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#| msgid "Show Applications"
+msgid "Switch to application 1"
+msgstr "Beralih ke aplikasi 1"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+#| msgid "Show Applications"
+msgid "Switch to application 2"
+msgstr "Beralih ke aplikasi 2"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+#| msgid "Show Applications"
+msgid "Switch to application 3"
+msgstr "Beralih ke aplikasi 3"
+
+#: data/org.gnome.shell.gschema.xml.in:167
+#| msgid "Show Applications"
+msgid "Switch to application 4"
+msgstr "Beralih ke aplikasi 4"
+
+#: data/org.gnome.shell.gschema.xml.in:171
+#| msgid "Show Applications"
+msgid "Switch to application 5"
+msgstr "Beralih ke aplikasi 5"
+
+#: data/org.gnome.shell.gschema.xml.in:175
+#| msgid "Show Applications"
+msgid "Switch to application 6"
+msgstr "Beralih ke aplikasi 6"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+#| msgid "Show Applications"
+msgid "Switch to application 7"
+msgstr "Beralih ke aplikasi 7"
+
+#: data/org.gnome.shell.gschema.xml.in:183
+#| msgid "Show Applications"
+msgid "Switch to application 8"
+msgstr "Beralih ke aplikasi 8"
+
+#: data/org.gnome.shell.gschema.xml.in:187
+#| msgid "Show Applications"
+msgid "Switch to application 9"
+msgstr "Beralih ke aplikasi 9"
+
+#: data/org.gnome.shell.gschema.xml.in:196
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Limit switcher to current workspace."
+msgstr "Hadkan penukar ke ruang kerja semasa."
+
+#: data/org.gnome.shell.gschema.xml.in:197
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Jika benar, hanya aplikasi-aplikasi yang ada tetingkap pada ruang kerja "
+"semasa ditunjukkan dalam penukar. Jika sebaliknya, semua aplikasi terlibat."
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "The application icon mode."
+msgstr "Mod ikon aplikasi."
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Konfigur bagaimanakah tetingkap-tetingkap ditunjukkan dalam penukar. "
+"Kemungkinan yang sah adalah \"lakaran-kenit-sahaja\" (tunjuk satu lakaran "
+"kenit bagi tetingkap), \"ikon-apl-sahaja\" (hanya tunjuk ikon aplikasi) atau "
+"\"kedua-duanya\"."
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Jika benar, hanya tetingkap-tetingkap pada ruang kerja semasa ditunjukkan "
+"dalam penukar. Jika sebaliknya, semua tetingkap terlibat."
+
+#: data/org.gnome.shell.gschema.xml.in:234
+#| msgid "Location"
+msgid "Locations"
+msgstr "Lokasi"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "The locations to show in world clocks"
+msgstr "Lokasi yang dipaparkan dalam jam dunia"
+
+#: data/org.gnome.shell.gschema.xml.in:245
+msgid "Automatic location"
+msgstr "Lokasi automatik"
+
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Whether to fetch the current location or not"
+msgstr "Sama ada hendak dapatkan lokasi semasa atau sebaliknya"
+
+#: data/org.gnome.shell.gschema.xml.in:253
+msgid "Location"
+msgstr "Lokasi"
+
+#: data/org.gnome.shell.gschema.xml.in:254
+msgid "The location for which to show a forecast"
+msgstr "Lokasi yang dapat menunjukkan ramalan"
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid "Attach modal dialog to the parent window"
+msgstr "Lampir dialog modal kepada tetingkap induk"
+
+#: data/org.gnome.shell.gschema.xml.in:267
+#: data/org.gnome.shell.gschema.xml.in:276
+#: data/org.gnome.shell.gschema.xml.in:284
+#: data/org.gnome.shell.gschema.xml.in:292
+#: data/org.gnome.shell.gschema.xml.in:300
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Kunci ini membatalkan kunci dalam org.gnome.mutter ketika menjalankan GNOME "
+"Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Benarkan penjubinan pinggir ketika melepaskan tetingkap-tetingkap di atas "
+"pinggir skrin"
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Workspaces are managed dynamically"
+msgstr "Ruang-ruang kerja diurus secara dinamik"
+
+#: data/org.gnome.shell.gschema.xml.in:291
+msgid "Workspaces only on primary monitor"
+msgstr "Ruang-ruang kerja hanya berada pada monitor utama"
+
+#: data/org.gnome.shell.gschema.xml.in:299
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Fokus lengah berubah dalam mod tetikus sehinggalah penuding berhenti bergerak"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+#| msgid "Network error"
+msgid "Network Login"
+msgstr "Daftar Masuk Rangkaian"
+
+#: js/extensionPrefs/main.js:102 js/extensionPrefs/main.js:525
+msgid "Something’s gone wrong"
+msgstr "Adalah masalah telah berlaku"
+
+#: js/extensionPrefs/main.js:109
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Mohon maaf, adalah masalah telah berlaku: tetapan bagi sambungan ini tidak "
+"dapat dipaparkan. Kami menyarankan anda laporkan masalah ini kepada para "
+"pengarang sambungan."
+
+#: js/extensionPrefs/main.js:116
+msgid "Technical Details"
+msgstr "Perincian Teknikal"
+
+#: js/extensionPrefs/main.js:151
+#| msgid "Show Errors"
+msgid "Copy Error"
+msgstr "Salin Ralat"
+
+#: js/extensionPrefs/main.js:178
+msgid "Homepage"
+msgstr "Laman Sesawang"
+
+#: js/extensionPrefs/main.js:179
+#| msgid "UUIDs of extensions to enable"
+msgid "Visit extension homepage"
+msgstr "Lawati laman sesawang sambungan"
+
+#: js/extensionPrefs/main.js:467
+#| msgid "No extensions installed"
+msgid "No Extensions Installed"
+msgstr "Tiada Sambungan Dipasang"
+
+#: js/extensionPrefs/main.js:477
+msgid ""
+"Extensions can be installed through Software or <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+msgstr ""
+"Sambungan-sambungan boleh dipasang melalui Perisian atau <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: js/extensionPrefs/main.js:492
+msgid "Browse in Software"
+msgstr "Layar dalam Perisian"
+
+#: js/extensionPrefs/main.js:532
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Mohon maaf, berkemungkinan boleh dapatkan senarai sambungan terpasang. "
+"Pastikan anda telah mendaftar masuk ke dalam GNOME dan cuba sekali lagi."
+
+#: js/gdm/authPrompt.js:174 js/ui/audioDeviceSelection.js:57
+#: js/ui/components/networkAgent.js:129 js/ui/components/polkitAgent.js:138
+#: js/ui/endSessionDialog.js:374 js/ui/extensionDownloader.js:190
+#: js/ui/shellMountOperation.js:399 js/ui/shellMountOperation.js:409
+#: js/ui/status/network.js:910
+msgid "Cancel"
+msgstr "Batal"
+
+#: js/gdm/authPrompt.js:188 js/gdm/authPrompt.js:241 js/gdm/authPrompt.js:476
+msgid "Next"
+msgstr "Berikutnya"
+
+#: js/gdm/authPrompt.js:237 js/ui/shellMountOperation.js:403
+#: js/ui/unlockDialog.js:44
+msgid "Unlock"
+msgstr "Nyahkunci"
+
+#: js/gdm/authPrompt.js:239
+msgctxt "button"
+msgid "Sign In"
+msgstr "Daftar Masuk"
+
+#: js/gdm/loginDialog.js:319
+msgid "Choose Session"
+msgstr "Pilih Sesi"
+
+#: js/gdm/loginDialog.js:461
+msgid "Not listed?"
+msgstr "Tidak tersenarai?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:895
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(cth., pengguna atau %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:899 js/ui/components/networkAgent.js:256
+#: js/ui/components/networkAgent.js:279 js/ui/components/networkAgent.js:297
+msgid "Username: "
+msgstr "Nama Pengguna: "
+
+#: js/gdm/loginDialog.js:1230
+msgid "Login Window"
+msgstr "Tetingkap Daftar Masuk"
+
+#: js/gdm/util.js:338
+msgid "Authentication error"
+msgstr "Ralat pengesahihan"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr "(atau leret jari)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:89
+#| msgid "Power Off"
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Tutup"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:92
+msgid "power off;shutdown;reboot;restart;halt;stop"
+msgstr "tutup;matikan;but semula;mula semula;tegun;henti"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:97
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Skrin Kunci"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:100
+msgid "lock screen"
+msgstr "kunci skrin"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:105
+#| msgid "Log Out"
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Daftar Keluar"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:108
+msgid "logout;log out;sign off"
+msgstr "daftarkeluar;daftar keluar;keluar"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:113
+#| msgid "Suspend"
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Tangguh"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:116
+#| msgid "Suspend"
+msgid "suspend;sleep"
+msgstr "tangguh;tidur"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:121
+#| msgid "Switch User"
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Tukar Pengguna"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:124
+#| msgid "Switch User"
+msgid "switch user"
+msgstr "tukar pengguna"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:129
+#| msgid "Location"
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr "Kunci Orientasi"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;screen;rotation"
+msgstr "kunci orientasi;skrin;putaran"
+
+#: js/misc/util.js:116
+msgid "Command not found"
+msgstr "Perintah tidak ditemui"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:152
+msgid "Could not parse command:"
+msgstr "Tidak dapat menghurai perintah:"
+
+#: js/misc/util.js:160
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Perlaksanaan “%s” telah gagal:"
+
+#: js/misc/util.js:177
+msgid "Just now"
+msgstr "Baru berlaku"
+
+#: js/misc/util.js:179
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minit yang lalu"
+msgstr[1] "%d minit yang lalu"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d jam yang lalu"
+msgstr[1] "%d jam yang lalu"
+
+#: js/misc/util.js:187
+msgid "Yesterday"
+msgstr "Semalam"
+
+#: js/misc/util.js:189
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d hari yang lalu"
+msgstr[1] "%d hari yang lalu"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d minggu yang lalu"
+msgstr[1] "%d minggu yang lalu"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d bulan yang lalu"
+msgstr[1] "%d bulan yang lalu"
+
+#: js/misc/util.js:200
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d tahun yang lalu"
+msgstr[1] "%d tahun yang lalu"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:233
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:239
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Semalam, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:245
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:251
+#, no-c-format
+#| msgid "%B %d, %l∶%M %p"
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:257
+#, no-c-format
+#| msgid "%B %d %Y, %l∶%M %p"
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:262
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:268
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Semalam, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:274
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:280
+#, no-c-format
+#| msgid "%B %d, %l∶%M %p"
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:286
+#, no-c-format
+#| msgid "%B %d %Y, %l∶%M %p"
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:40
+msgid "Hotspot Login"
+msgstr "Daftar Masuk Kawasan Khas"
+
+#: js/portalHelper/main.js:86
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Sambungan anda dengan daftar masuk kawasan khas ini adalah tidak selamat. "
+"Kata laluan atau lain-lain maklumat yang anda telah masukkan di dalam "
+"halaman ini boleh dilihat oleh individu berhampiran."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:366
+msgid "Deny Access"
+msgstr "Nafi Capaian"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:369
+msgid "Grant Access"
+msgstr "Beri Capaian"
+
+#: js/ui/appDisplay.js:904
+msgid "Unnamed Folder"
+msgstr "Folder Tiada Nama"
+
+#: js/ui/appDisplay.js:927
+msgid "Frequently used applications will appear here"
+msgstr "Aplikasi kerap digunakan akan muncul di sini"
+
+#: js/ui/appDisplay.js:1062
+msgid "Frequent"
+msgstr "Kerap"
+
+#: js/ui/appDisplay.js:1069
+msgid "All"
+msgstr "Semua"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:2452 js/ui/panel.js:75
+#| msgid "Windows"
+msgid "Open Windows"
+msgstr "Buka Tetingkap"
+
+#: js/ui/appDisplay.js:2472 js/ui/panel.js:82
+msgid "New Window"
+msgstr "Tetingkap Baharu"
+
+#: js/ui/appDisplay.js:2483
+msgid "Launch using Dedicated Graphics Card"
+msgstr "Lancar menggunakan Kad Grafik Khusus"
+
+#: js/ui/appDisplay.js:2511 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "Buang daripada Kegemaran"
+
+#: js/ui/appDisplay.js:2517
+msgid "Add to Favorites"
+msgstr "Tambah ke Kegemaran"
+
+#: js/ui/appDisplay.js:2527 js/ui/panel.js:93
+msgid "Show Details"
+msgstr "Tunjuk Perincian"
+
+#: js/ui/appFavorites.js:152
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s telah ditambah ke kegemaran anda."
+
+#: js/ui/appFavorites.js:185
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s telah dibuang daripada kegemaran anda."
+
+#: js/ui/audioDeviceSelection.js:40
+msgid "Select Audio Device"
+msgstr "Pilih Peranti Audio"
+
+#: js/ui/audioDeviceSelection.js:54
+#| msgid "Settings"
+msgid "Sound Settings"
+msgstr "Tetapan Bunyi"
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headphones"
+msgstr "Fon Kepala"
+
+#: js/ui/audioDeviceSelection.js:66
+msgid "Headset"
+msgstr "Set Kepala"
+
+#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:269
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Ubah Latar Belakang…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+#| msgid "Privacy Settings"
+msgid "Display Settings"
+msgstr "Tetapan Paparan"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "Tetapan"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:41
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:70
+msgctxt "grid sunday"
+msgid "S"
+msgstr "A"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:72
+msgctxt "grid monday"
+msgid "M"
+msgstr "I"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:74
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:76
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "R"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:78
+msgctxt "grid thursday"
+msgid "T"
+msgstr "K"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:80
+msgctxt "grid friday"
+msgid "F"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:82
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:371
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:381
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:440
+msgid "Previous month"
+msgstr "Bulan terdahulu"
+
+#: js/ui/calendar.js:455
+msgid "Next month"
+msgstr "Bulan berikutnya"
+
+#: js/ui/calendar.js:605
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:661
+msgid "Week %V"
+msgstr "Minggu %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:730
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Sepanjang Hari"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/calendar.js:867
+#| msgctxt "calendar heading"
+#| msgid "%A, %B %d"
+msgctxt "calendar heading"
+msgid "%A, %B %-d"
+msgstr "%A, %B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/calendar.js:870
+#| msgctxt "calendar heading"
+#| msgid "%A, %B %d, %Y"
+msgctxt "calendar heading"
+msgid "%A, %B %-d, %Y"
+msgstr "%A, %B %-d, %Y"
+
+#: js/ui/calendar.js:1096
+#| msgid "Notifications"
+msgid "No Notifications"
+msgstr "Tiada Pemberitahuan"
+
+#: js/ui/calendar.js:1099
+msgid "No Events"
+msgstr "Tiada Peristiwa"
+
+#: js/ui/calendar.js:1153
+msgid "Do Not Disturb"
+msgstr "Jangan Ganggu"
+
+#: js/ui/calendar.js:1167
+msgid "Clear"
+msgstr "Kosongkan"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+#| msgid "“%s” is ready"
+msgid "“%s” is not responding."
+msgstr "“%s” tidak memberi respons."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Anda boleh tunggu sebentar untuk teruskan atau paksa aplikasi ditutup "
+"sepenuhnya."
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "Paksa Keluar"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "Tunggu"
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr "Pemacu luar bersambung"
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr "Pemacu luar ditanggalkan"
+
+#: js/ui/components/automountManager.js:208
+msgid "Unable to unlock volume"
+msgstr "Gagal menyahkunci volum"
+
+#: js/ui/components/automountManager.js:209
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Versi udisks terpasang tidak menyokong tetapan PIM"
+
+#: js/ui/components/autorunManager.js:333
+#, javascript-format
+msgid "Open with %s"
+msgstr "Buka dengan %s"
+
+#: js/ui/components/keyring.js:70 js/ui/components/polkitAgent.js:278
+msgid "Password:"
+msgstr "Katalaluan:"
+
+#: js/ui/components/keyring.js:104
+msgid "Type again:"
+msgstr "Taip sekali lagi:"
+
+#: js/ui/components/networkAgent.js:115
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Selain itu anda boleh sambung dengan menekan butang \"WPS\" yang ada pada "
+"penghala anda."
+
+#: js/ui/components/networkAgent.js:123 js/ui/status/network.js:223
+#: js/ui/status/network.js:314 js/ui/status/network.js:913
+msgid "Connect"
+msgstr "Sambung"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:224 js/ui/components/networkAgent.js:236
+#: js/ui/components/networkAgent.js:260 js/ui/components/networkAgent.js:281
+#: js/ui/components/networkAgent.js:301 js/ui/components/networkAgent.js:311
+msgid "Password: "
+msgstr "Kata laluan: "
+
+#. static WEP
+#: js/ui/components/networkAgent.js:229
+msgid "Key: "
+msgstr "Kunci: "
+
+#: js/ui/components/networkAgent.js:264 js/ui/components/networkAgent.js:287
+msgid "Private key password: "
+msgstr "Kata laluan kunci persendirian: "
+
+#: js/ui/components/networkAgent.js:285
+msgid "Identity: "
+msgstr "Identiti: "
+
+#: js/ui/components/networkAgent.js:299
+msgid "Service: "
+msgstr "Perkhidmatan: "
+
+#: js/ui/components/networkAgent.js:328 js/ui/components/networkAgent.js:703
+msgid "Authentication required by wireless network"
+msgstr "Pengesahihan diperlukan untuk rangkaian tanpa wayar"
+
+#: js/ui/components/networkAgent.js:329 js/ui/components/networkAgent.js:704
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Kata laluan atau kunci penyulitan diperlukan untuk mencapai rangkaian tanpa "
+"wayar “%s”."
+
+#: js/ui/components/networkAgent.js:333 js/ui/components/networkAgent.js:708
+msgid "Wired 802.1X authentication"
+msgstr "Pengesahan berwayar 802.1X"
+
+#: js/ui/components/networkAgent.js:335
+msgid "Network name: "
+msgstr "Nama rangkaian: "
+
+#: js/ui/components/networkAgent.js:340 js/ui/components/networkAgent.js:712
+msgid "DSL authentication"
+msgstr "Pengesahan DSL"
+
+#: js/ui/components/networkAgent.js:347 js/ui/components/networkAgent.js:717
+msgid "PIN code required"
+msgstr "Kod PIN diperlukan"
+
+#: js/ui/components/networkAgent.js:348 js/ui/components/networkAgent.js:718
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Kod PIN diperlukan untuk peranti jalur lebar mudah alih"
+
+#: js/ui/components/networkAgent.js:349
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:356 js/ui/components/networkAgent.js:724
+msgid "Mobile broadband network password"
+msgstr "Kata laluan rangkaian jalur lebar mudah alih"
+
+#: js/ui/components/networkAgent.js:357 js/ui/components/networkAgent.js:709
+#: js/ui/components/networkAgent.js:713 js/ui/components/networkAgent.js:725
+#: js/ui/components/networkAgent.js:729
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Kata laluan diperlukan untuk bersambung dengan “%s”."
+
+#: js/ui/components/networkAgent.js:692 js/ui/status/network.js:1688
+msgid "Network Manager"
+msgstr "Pengurus Rangkaian"
+
+#: js/ui/components/networkAgent.js:728
+#| msgid "Password"
+msgid "VPN password"
+msgstr "Kata laluan VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Pengesahihan Diperlukan"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Pentadbir"
+
+#: js/ui/components/polkitAgent.js:141
+msgid "Authenticate"
+msgstr "Sahihkan"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:259 js/ui/shellMountOperation.js:383
+#| msgid "Sorry, that didn't work. Please try again."
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Maaf, nampaknya tidak berjaya. Cuba sekali lagi."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:787
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s kini dikenali sebagai %s"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:177
+msgid "Windows"
+msgstr "Tetingkap"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr "Tunjukkan Aplikasi"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr "Pemuka"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:75
+#| msgctxt "calendar heading"
+#| msgid "%A, %B %d, %Y"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:82
+#| msgid "%A %B %e, %Y"
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#: js/ui/dateMenu.js:161
+msgid "Add world clocks…"
+msgstr "Tambah jam dunia…"
+
+#: js/ui/dateMenu.js:162
+msgid "World Clocks"
+msgstr "Jam Dunia"
+
+#: js/ui/dateMenu.js:276
+msgid "Weather"
+msgstr "Cuaca"
+
+#: js/ui/dateMenu.js:391
+#| msgid "Select a network"
+msgid "Select a location…"
+msgstr "Pilih satu lokasi…"
+
+#: js/ui/dateMenu.js:404
+#| msgid "Searching…"
+msgid "Loading…"
+msgstr "Memuatkan…"
+
+#: js/ui/dateMenu.js:414
+msgid "Go online for weather information"
+msgstr "Pergi dalam talian untuk dapatkan maklumat cuaca"
+
+#: js/ui/dateMenu.js:416
+#| msgid "Encryption is not available"
+msgid "Weather information is currently unavailable"
+msgstr "Maklumat cuaca buat masa ini tidak tersedia"
+
+#: js/ui/endSessionDialog.js:37
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Daftar Keluar %s"
+
+#: js/ui/endSessionDialog.js:38
+msgctxt "title"
+msgid "Log Out"
+msgstr "Daftar Keluar"
+
+#: js/ui/endSessionDialog.js:40
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s akan mendaftar keluar secara automatik dalam tempoh %d saat."
+msgstr[1] "%s akan mendaftar keluar secara automatik dalam tempoh %d saat."
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Anda akan mendaftar keluar secara automatik dalam tempoh %d saat."
+msgstr[1] "Anda akan mendaftar keluar secara automatik dalam tempoh %d saat."
+
+#: js/ui/endSessionDialog.js:51
+msgctxt "button"
+msgid "Log Out"
+msgstr "Daftar Keluar"
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "title"
+msgid "Power Off"
+msgstr "Tutup"
+
+#: js/ui/endSessionDialog.js:57
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Pasang Kemas Kini & Matikan"
+
+#: js/ui/endSessionDialog.js:59
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistem akan dimatikan secara automatik dalam tempoh %d saat."
+msgstr[1] "Sistem akan dimatikan secara automatik dalam tempoh %d saat."
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Pasang kemas kini-kemas kini perisian tertangguh"
+
+#: js/ui/endSessionDialog.js:66 js/ui/endSessionDialog.js:82
+msgctxt "button"
+msgid "Restart"
+msgstr "Mula Semula"
+
+#: js/ui/endSessionDialog.js:68
+msgctxt "button"
+msgid "Power Off"
+msgstr "Tutup"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "title"
+msgid "Restart"
+msgstr "Mula Semula"
+
+#: js/ui/endSessionDialog.js:76
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistem akan dimulakan semula secara automatik dalam tempoh %d saat."
+msgstr[1] "Sistem akan dimulakan semula secara automatik dalam tempoh %d saat."
+
+#: js/ui/endSessionDialog.js:89
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Mulas Semula & Pasang Kemas Kini"
+
+#: js/ui/endSessionDialog.js:91
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistem akan dimulakan semula dan memasang kemas kini secara automatik dalam "
+"tempoh %d saat."
+msgstr[1] ""
+"Sistem akan dimulakan semula dan memasang kemas kini secara automatik dalam "
+"tempoh %d saat."
+
+#: js/ui/endSessionDialog.js:97 js/ui/endSessionDialog.js:116
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Mula Semula &amp; Pasang"
+
+#: js/ui/endSessionDialog.js:98
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Pasang &amp; Matikan"
+
+#: js/ui/endSessionDialog.js:99
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Matikan selepas kemas kini-kemas kini telah dipasang"
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Mula Semula & Pasang Penataran"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:111
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s%s akan dipasang selepas mula semula. Pemasangan penataran mengambil masa "
+"yang agak lama: pastikan anda telah menyandar dan komputer anda menggunakan "
+"kuasa AC."
+
+#: js/ui/endSessionDialog.js:259
+msgid "Running on battery power: Please plug in before installing updates."
+msgstr ""
+"Berjalan dengan kuasa bateri. Sila palamkan sebelum memasang kemas kini."
+
+#: js/ui/endSessionDialog.js:268
+msgid "Some applications are busy or have unsaved work"
+msgstr "Sesetengah aplikasi masih sibuk atau ada kerja belum disimpan"
+
+#: js/ui/endSessionDialog.js:273
+msgid "Other users are logged in"
+msgstr "Pengguna lain masih mendaftar masuk"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:588
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (jauh)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:591
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsol)"
+
+#: js/ui/extensionDownloader.js:194
+msgid "Install"
+msgstr "Pasang"
+
+#: js/ui/extensionDownloader.js:200
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Muat turun dan pasang “%s” dari extensions.gnome.org?"
+
+#: js/ui/inhibitShortcutsDialog.js:80
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr "%s mahu menyekat pintasan-pintasan"
+
+#: js/ui/inhibitShortcutsDialog.js:81
+msgid "Application wants to inhibit shortcuts"
+msgstr "Aplikasi mahu menyekat pintasan-pintasan"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:89
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Anda boleh memulihkan pintasan-pintasan dengan menekan %s."
+
+#: js/ui/inhibitShortcutsDialog.js:95
+msgid "Deny"
+msgstr "Nafi"
+
+#: js/ui/inhibitShortcutsDialog.js:102
+msgid "Allow"
+msgstr "Benarkan"
+
+#: js/ui/kbdA11yDialog.js:32
+#| msgid "Slow Keys"
+msgid "Slow Keys Turned On"
+msgstr "Kekunci Lambat Hidup"
+
+#: js/ui/kbdA11yDialog.js:33
+#| msgid "Slow Keys"
+msgid "Slow Keys Turned Off"
+msgstr "Kekunci Lambat Mati"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Anda baru sahaja menahan kekunci Shift selama 8 saat. Tindakan ini adalah "
+"pintasan untuk fitur Kekunci-Kekunci Lambat, yang mungkin mempengaruhi "
+"fungsi papan kekunci anda."
+
+#: js/ui/kbdA11yDialog.js:41
+#| msgid "Sticky Keys"
+msgid "Sticky Keys Turned On"
+msgstr "Kekunci Lekat Hidup"
+
+#: js/ui/kbdA11yDialog.js:42
+#| msgid "Sticky Keys"
+msgid "Sticky Keys Turned Off"
+msgstr "Kekunci Lekat Mati"
+
+#: js/ui/kbdA11yDialog.js:44
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Anda baru sahaja menekan kekunci Shift sebanyak 5 kali berturutan. Tindakan "
+"ini adalah pintasan untuk fitur Kekunci-Kekunci Lekat, yang mungkin "
+"mempengaruhi fungsi papan kekunci anda."
+
+#: js/ui/kbdA11yDialog.js:46
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Anda baru sahaja menekan dua kekunci sekaligus, atau menekan kekunci Shift "
+"sebanyak 5 kali berturutan. Tindakan ini mematikan fitur Kekunci-Kekunci "
+"Lekat, yang mungkin mempengaruhi fungsi papan kekunci anda."
+
+#: js/ui/kbdA11yDialog.js:57
+msgid "Leave On"
+msgstr "Biarkan"
+
+#: js/ui/kbdA11yDialog.js:57 js/ui/status/bluetooth.js:135
+#: js/ui/status/network.js:1285
+msgid "Turn On"
+msgstr "Hidup"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:135
+#: js/ui/status/network.js:131 js/ui/status/network.js:315
+#: js/ui/status/network.js:1285 js/ui/status/network.js:1397
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:108
+msgid "Turn Off"
+msgstr "Mati"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Tinggalkan"
+
+#: js/ui/keyboard.js:207
+#| msgid "Date & Time Settings"
+msgid "Region & Language Settings"
+msgstr "Tetapan-Tetapan Wilayah & Bahasa"
+
+#: js/ui/lookingGlass.js:659
+msgid "No extensions installed"
+msgstr "Tiada sambungan dipasang"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:714
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s tidak menghasilkan apa-apa ralat."
+
+#: js/ui/lookingGlass.js:720
+msgid "Hide Errors"
+msgstr "Sembunyikan Ralat"
+
+#: js/ui/lookingGlass.js:724 js/ui/lookingGlass.js:789
+msgid "Show Errors"
+msgstr "Tunjukkan Ralat"
+
+#: js/ui/lookingGlass.js:733
+msgid "Enabled"
+msgstr "Dibolehkan"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:736 subprojects/gvc/gvc-mixer-control.c:1892
+msgid "Disabled"
+msgstr "Dilumpuhkan"
+
+#: js/ui/lookingGlass.js:738
+msgid "Error"
+msgstr "Ralat"
+
+#: js/ui/lookingGlass.js:740
+msgid "Out of date"
+msgstr "Lapuk"
+
+#: js/ui/lookingGlass.js:742
+msgid "Downloading"
+msgstr "Memuat turun"
+
+#: js/ui/lookingGlass.js:771
+msgid "View Source"
+msgstr "Lihat Sumber"
+
+#: js/ui/lookingGlass.js:780
+msgid "Web Page"
+msgstr "Halaman Sesawang"
+
+#: js/ui/main.js:267
+msgid "Logged in as a privileged user"
+msgstr "Mendaftar masuk sebagai seorang pengguna istimewa"
+
+#: js/ui/main.js:268
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Menjalankan satu sesi sebagai seorang pengguna istimewa perlu dihindari atas "
+"sebab keselamatan. Jika boleh, anda patut mendaftar masuk sebagai seorang "
+"pengguna biasa."
+
+#: js/ui/main.js:274
+msgid "Screen Lock disabled"
+msgstr "Kunci Skrin dilumpuhkan"
+
+#: js/ui/main.js:275
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Penguncian skrin memerlukan pengurus paparan GNOME."
+
+#: js/ui/messageTray.js:1552
+msgid "System Information"
+msgstr "Maklumat Sistem"
+
+#: js/ui/mpris.js:199
+#| msgid "Unknown reason"
+msgid "Unknown artist"
+msgstr "Artis tidak diketahui"
+
+#: js/ui/mpris.js:209
+#| msgctxt "program"
+#| msgid "Unknown"
+msgid "Unknown title"
+msgstr "Tajuk tidak diketahui"
+
+#: js/ui/overview.js:73
+msgid "Undo"
+msgstr "Buat asal"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:86
+msgid "Overview"
+msgstr "Selayang Pandang"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:107
+msgid "Type to search…"
+msgstr "Taip untuk gelintar…"
+
+#: js/ui/padOsd.js:95
+msgid "New shortcut…"
+msgstr "Pintasan baharu…"
+
+#: js/ui/padOsd.js:142
+#| msgid "Applications"
+msgid "Application defined"
+msgstr "Aplikasi ditakrifkan"
+
+#: js/ui/padOsd.js:143
+msgid "Show on-screen help"
+msgstr "Tunjuk bantuan atas-skrin"
+
+#: js/ui/padOsd.js:144
+#| msgid "Switch User"
+msgid "Switch monitor"
+msgstr "Tukar monitor"
+
+#: js/ui/padOsd.js:145
+msgid "Assign keystroke"
+msgstr "Umpuk ketukan kekunci"
+
+#: js/ui/padOsd.js:211
+msgid "Done"
+msgstr "Selesai"
+
+#: js/ui/padOsd.js:747
+msgid "Edit…"
+msgstr "Sunting…"
+
+#: js/ui/padOsd.js:789 js/ui/padOsd.js:912
+msgid "None"
+msgstr "Tiada"
+
+#: js/ui/padOsd.js:865
+msgid "Press a button to configure"
+msgstr "Tekan satu butang untuk dikonfigurkan"
+
+#: js/ui/padOsd.js:866
+msgid "Press Esc to exit"
+msgstr "Tekan Esc untuk keluar"
+
+#: js/ui/padOsd.js:869
+msgid "Press any key to exit"
+msgstr "Tekan mana-mana kekunci untuk keluar"
+
+#: js/ui/panel.js:109
+msgid "Quit"
+msgstr "Keluar"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:434
+msgid "Activities"
+msgstr "Aktiviti-Aktiviti"
+
+#: js/ui/panel.js:707
+#| msgid "System"
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistem"
+
+#: js/ui/panel.js:820
+msgid "Top Bar"
+msgstr "Palang Atas"
+
+#: js/ui/runDialog.js:58
+msgid "Enter a Command"
+msgstr "Masukkan satu Perintah"
+
+#: js/ui/runDialog.js:97 js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Tutup"
+
+#: js/ui/runDialog.js:259
+#| msgid "Encryption is not available"
+msgid "Restart is not available on Wayland"
+msgstr "Mula Semula tidak tersedia dalam Wayland"
+
+#: js/ui/runDialog.js:264
+msgid "Restarting…"
+msgstr "Memulakan semula…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:82
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: js/ui/screenShield.js:139
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d mesej baharu"
+msgstr[1] "%d mesej baharu"
+
+#: js/ui/screenShield.js:141
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d pemberitahuan baharu"
+msgstr[1] "%d pemberitahuan baharu"
+
+#: js/ui/screenShield.js:454 js/ui/status/system.js:93
+msgid "Lock"
+msgstr "Kunci"
+
+#: js/ui/screenShield.js:717
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME perlu mengunci skrin ini"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:830 js/ui/screenShield.js:1301
+msgid "Unable to lock"
+msgstr "Gagal dikuncikan"
+
+#: js/ui/screenShield.js:831 js/ui/screenShield.js:1302
+msgid "Lock was blocked by an application"
+msgstr "Kunci disekat oleh suatu aplikasi"
+
+#: js/ui/search.js:694
+msgid "Searching…"
+msgstr "Menggelintar…"
+
+#: js/ui/search.js:696
+msgid "No results."
+msgstr "Tiada padanan."
+
+#: js/ui/search.js:822
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d lagi"
+msgstr[1] "%d lagi"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Salin"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Tampal"
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr "Tunjuk Teks"
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr "Sembunyikan Teks"
+
+#: js/ui/shellEntry.js:162
+msgid "Caps lock is on."
+msgstr "Caps lock hidup."
+
+#: js/ui/shellMountOperation.js:305
+#| msgid "Volume"
+msgid "Hidden Volume"
+msgstr "Volum Tersembunyi"
+
+#: js/ui/shellMountOperation.js:308
+msgid "Windows System Volume"
+msgstr "Volum Sistem Tetingkap"
+
+#: js/ui/shellMountOperation.js:311
+#| msgid "Mouse Keys"
+msgid "Uses Keyfiles"
+msgstr "Guna Fail Kunci"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:317
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Untuk menyahkunci volum yang menggunakan fail kunci, gunakan utiliti <i>%s</"
+"i> sebagai ganti."
+
+#: js/ui/shellMountOperation.js:324
+msgid "PIM Number"
+msgstr "Nombor PIM"
+
+#: js/ui/shellMountOperation.js:343
+msgid "The PIM must be a number or empty."
+msgstr "PIM mestilah nombor atau dikosongkan."
+
+#: js/ui/shellMountOperation.js:354
+msgid "Password"
+msgstr "Kata laluan"
+
+#: js/ui/shellMountOperation.js:390
+msgid "Remember Password"
+msgstr "Ingat Kata Laluan"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:414
+#, javascript-format
+#| msgid "Open with %s"
+msgid "Open %s"
+msgstr "Buka %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:486
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Gagal memulakan %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:488
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Tidak dapat mencari aplikasi %s"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Kebolehcapaian"
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr "Zum"
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr "Pembaca Skrin"
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr "Papan Kekunci Skrin"
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr "Amaran Visual"
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr "Kekunci Sticky"
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr "Kekunci Lembab"
+
+# Kekunci untuk OKU yang tangan bergetar.
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr "Kekunci Lantun"
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr "Kekunci Tetikus"
+
+# contrast boleh juga diterjemah "beza jelas", tapi "senjang" (/sénjang/) dipilih kerana lebih pendek.
+#: js/ui/status/accessibility.js:136
+msgid "High Contrast"
+msgstr "Beza Jelas Tinggi"
+
+#: js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr "Teks Besar"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:590
+msgid "Bluetooth Settings"
+msgstr "Tetapan Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:129
+#, javascript-format
+#| msgid "Connected"
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d Bersambung"
+msgstr[1] "%d Bersambung"
+
+#: js/ui/status/bluetooth.js:131
+msgid "Off"
+msgstr "Mati"
+
+#: js/ui/status/bluetooth.js:133
+msgid "On"
+msgstr "Hidup"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Kecerahan"
+
+#: js/ui/status/dwellClick.js:13
+msgid "Single Click"
+msgstr "Seklik"
+
+#: js/ui/status/dwellClick.js:18
+msgid "Double Click"
+msgstr "Dwi-Klik"
+
+#: js/ui/status/dwellClick.js:23
+msgid "Drag"
+msgstr "Seret"
+
+#: js/ui/status/dwellClick.js:28
+msgid "Secondary Click"
+msgstr "Klik Sekunder"
+
+#: js/ui/status/dwellClick.js:37
+msgid "Dwell Click"
+msgstr "Klik Inap"
+
+#: js/ui/status/keyboard.js:825
+msgid "Keyboard"
+msgstr "Papan kekunci"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Tunjuk Susun Atur Papan Kekunci"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:174
+#| msgid "Location"
+msgid "Location Enabled"
+msgstr "Lokasi Dibenarkan"
+
+#: js/ui/status/location.js:66 js/ui/status/location.js:175
+msgid "Disable"
+msgstr "Lumpuhkan"
+
+#: js/ui/status/location.js:67
+msgid "Privacy Settings"
+msgstr "Tetapan Kerahsiaan"
+
+#: js/ui/status/location.js:173
+#| msgid "Location"
+msgid "Location In Use"
+msgstr "Lokasi Digunakan"
+
+#: js/ui/status/location.js:177
+#| msgid "Connection failed"
+msgid "Location Disabled"
+msgstr "Lokasi Dilumpuhkan"
+
+#: js/ui/status/location.js:178
+msgid "Enable"
+msgstr "Benarkan"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:355
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "Berikan %s capaian ke lokasi anda?"
+
+#: js/ui/status/location.js:361
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Capaian lokasi boleh diubah pada bila-bila masa melalui tetapan kerahsiaan."
+
+#: js/ui/status/network.js:66
+msgid "<unknown>"
+msgstr "<tidak diketahui>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:420 js/ui/status/network.js:1314
+#, javascript-format
+msgid "%s Off"
+msgstr "%s Mati"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:423
+#, javascript-format
+#| msgid "Connected"
+msgid "%s Connected"
+msgstr "%s Bersambung"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:428
+#, javascript-format
+#| msgid "Unmanaged"
+msgid "%s Unmanaged"
+msgstr "%s Tidak Diurus"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:431
+#, javascript-format
+#| msgid "Disconnecting"
+msgid "%s Disconnecting"
+msgstr "%s Terputus"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:438 js/ui/status/network.js:1306
+#, javascript-format
+#| msgid "Connecting"
+msgid "%s Connecting"
+msgstr "%s Menyambung"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:441
+#, javascript-format
+#| msgid "Wired 802.1X authentication"
+msgid "%s Requires Authentication"
+msgstr "%s Memerlukan Pengesahihan"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:449
+#, javascript-format
+#| msgid "Firmware missing"
+msgid "Firmware Missing For %s"
+msgstr "Perisian Tegar Untuk %s Hilang"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:453
+#, javascript-format
+#| msgid "Unavailable"
+msgid "%s Unavailable"
+msgstr "%s Tidak Tersedia"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:456
+#, javascript-format
+#| msgid "Connection failed"
+msgid "%s Connection Failed"
+msgstr "%s Sambungan Mengalami Kegagalan"
+
+#: js/ui/status/network.js:468
+msgid "Wired Settings"
+msgstr "Tetapan Berwayar"
+
+#: js/ui/status/network.js:511
+msgid "Mobile Broadband Settings"
+msgstr "Tetapan Jalur Lebar Mudah Alih"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:558 js/ui/status/network.js:1311
+#, javascript-format
+#| msgid "Hardware Disabled"
+msgid "%s Hardware Disabled"
+msgstr "%s Perkakasan Dilumpuhkan"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:562
+#, javascript-format
+#| msgid "Disabled"
+msgid "%s Disabled"
+msgstr "%s Dilumpuhkan"
+
+#: js/ui/status/network.js:602
+msgid "Connect to Internet"
+msgstr "Sambung ke Internet"
+
+#: js/ui/status/network.js:805
+msgid "Airplane Mode is On"
+msgstr "Mod Kapal Terbang Hidup"
+
+#: js/ui/status/network.js:806
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi dilumpuhkan ketika mod kapal terbang hidup."
+
+#: js/ui/status/network.js:807
+msgid "Turn Off Airplane Mode"
+msgstr "Matikan Mod Kapal Terbang"
+
+#: js/ui/status/network.js:816
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi Mati"
+
+#: js/ui/status/network.js:817
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Wi-Fi perlu dihidupkan supaya dapat bersambung dengan rangkaian."
+
+#: js/ui/status/network.js:818
+msgid "Turn On Wi-Fi"
+msgstr "Hidupkan Wi-Fi"
+
+#: js/ui/status/network.js:843
+msgid "Wi-Fi Networks"
+msgstr "Rangkaian-Rangkaian Wi-Fi"
+
+#: js/ui/status/network.js:845
+msgid "Select a network"
+msgstr "Pilih satu rangkaian"
+
+#: js/ui/status/network.js:877
+msgid "No Networks"
+msgstr "Tiada Rangkaian"
+
+#: js/ui/status/network.js:898 js/ui/status/rfkill.js:106
+msgid "Use hardware switch to turn off"
+msgstr "Guna suis perkakasan untuk dimatikan"
+
+#: js/ui/status/network.js:1175
+msgid "Select Network"
+msgstr "Pilih Rangkaian"
+
+#: js/ui/status/network.js:1181
+msgid "Wi-Fi Settings"
+msgstr "Tetapan Wi-Fi"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1302
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "Kawasan Khas %s Aktif"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1317
+#, javascript-format
+#| msgid "Not Connected"
+msgid "%s Not Connected"
+msgstr "%s Tidak Bersambung"
+
+#: js/ui/status/network.js:1414
+#| msgid "Connecting"
+msgid "connecting…"
+msgstr "menyambung…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1417
+msgid "authentication required"
+msgstr "pengesahihan diperlukan"
+
+#: js/ui/status/network.js:1419
+msgid "connection failed"
+msgstr "sambungan gagal"
+
+#: js/ui/status/network.js:1470
+msgid "VPN Settings"
+msgstr "Tetapan VPN"
+
+#: js/ui/status/network.js:1487
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1497
+#| msgid "Power Off"
+msgid "VPN Off"
+msgstr "VPN Mati"
+
+#: js/ui/status/network.js:1558 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Tetapan-tetapan Rangkaian"
+
+#: js/ui/status/network.js:1587
+#, javascript-format
+#| msgid "Connecting"
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s Sambungan Berwayar"
+msgstr[1] "%s Sambungan Berwayar"
+
+#: js/ui/status/network.js:1591
+#, javascript-format
+#| msgid "Connecting"
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s Sambungan Wi-Fi"
+msgstr[1] "%s Sambungan Wi-Fi"
+
+#: js/ui/status/network.js:1595
+#, javascript-format
+#| msgid "Connecting"
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s Sambungan Modem"
+msgstr[1] "%s Sambungan Modem"
+
+#: js/ui/status/network.js:1729
+msgid "Connection failed"
+msgstr "Gagal menyambung"
+
+#: js/ui/status/network.js:1730
+msgid "Activation of network connection failed"
+msgstr "Pengaktifan sambungan rangkaian gagal"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Cahaya Malam Dilumpuhkan"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Cahaya Malam Hidup"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Sambung Semula"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Dilumpuhkan Sehingga Hari Esok"
+
+#: js/ui/status/power.js:47
+msgid "Power Settings"
+msgstr "Tetapan Kuasa"
+
+#: js/ui/status/power.js:63
+msgid "Fully Charged"
+msgstr "Sepenuhnya Dicas"
+
+#: js/ui/status/power.js:69
+msgid "Not Charging"
+msgstr "Tidak Dicas"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:72 js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "Menganggarkan…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d Berbaki (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d Sehingga Penuh (%d %%)"
+
+#: js/ui/status/power.js:139 js/ui/status/power.js:141
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:44
+msgid "Screen is Being Shared"
+msgstr "Skrin telah Dikongsikan"
+
+#: js/ui/status/remoteAccess.js:46
+msgid "Turn off"
+msgstr "Matikan"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Mod Kapal Terbang hidup"
+
+#: js/ui/status/system.js:57
+msgid "Lock Screen Rotation"
+msgstr "Kunci Putaran Skrin"
+
+#: js/ui/status/system.js:106
+#| msgid "Power Off"
+msgid "Power Off / Log Out"
+msgstr "Matikan / Daftar Keluar"
+
+#: js/ui/status/system.js:109
+msgid "Log Out"
+msgstr "Daftar Keluar"
+
+#: js/ui/status/system.js:121
+#| msgid "Switch User"
+msgid "Switch User…"
+msgstr "Tukar Pengguna…"
+
+#: js/ui/status/system.js:135
+msgid "Suspend"
+msgstr "Tangguh"
+
+#: js/ui/status/system.js:147
+#| msgid "Power Off"
+msgid "Power Off…"
+msgstr "Matikan…"
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Peranti Thunderbolt tidak diketahui"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Peranti baharu telah dikesan ketika anda tiada. Sila putuskan dan sambung "
+"semula peranti tersebut untuk mula menggunakannya."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr "Peranti Thunderbolt tidak diizinkan"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Peranti baharu telah dikesan dan perlu mendapat keizinan oleh pentadbir."
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Ralat keizinan Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Tidak dapat izinkan peranti Thunderbolt: %s"
+
+#: js/ui/status/volume.js:150
+msgid "Volume changed"
+msgstr "Tetapan bunyi diubah"
+
+#: js/ui/status/volume.js:221
+msgid "Volume"
+msgstr "Volum"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+#| msgid "Error"
+msgid "Mirror"
+msgstr "Cermin"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "Paparan Tergabung"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "Luaran Sahaja"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "Terbina-dalam Sahaja"
+
+#: js/ui/unlockDialog.js:52
+msgid "Log in as another user"
+msgstr "Daftar masuk sebagai pengguna lain"
+
+#: js/ui/unlockDialog.js:67
+msgid "Unlock Window"
+msgstr "Buka Tetingkap"
+
+#: js/ui/viewSelector.js:181
+msgid "Applications"
+msgstr "Aplikasi"
+
+#: js/ui/viewSelector.js:185
+msgid "Search"
+msgstr "Gelintar"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” telah sedia"
+
+#: js/ui/windowManager.js:54
+msgid "Do you want to keep these display settings?"
+msgstr "Anda mahu kekalkan tetapan-tetapan paparan ini?"
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:68
+msgid "Revert Settings"
+msgstr "Kembali Ke Tetapan Asal"
+
+#: js/ui/windowManager.js:71
+msgid "Keep Changes"
+msgstr "Kekalkan Perubahan"
+
+#: js/ui/windowManager.js:89
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Perubahan tetapan akan dikembalikan dalam tempoh %d saat"
+msgstr[1] "Perubahan tetapan akan dikembalikan dalam tempoh %d saat"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:546
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr "Minimum"
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr "Nyahmaksimum"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "Maksimum"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Alih"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "Saiz Semula"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr "Alih Palang Tajuk Atas-Skrin"
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "Sentiasa di Atas"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr "Sentiasa pada Ruang Kerja Tampak"
+
+#: js/ui/windowMenu.js:96
+#| msgid "Move to Workspace Up"
+msgid "Move to Workspace Left"
+msgstr "Alih ke Ruang Kerja Kiri"
+
+#: js/ui/windowMenu.js:102
+#| msgid "Move to Workspace Up"
+msgid "Move to Workspace Right"
+msgstr "Alih ke Ruang Kerja Kanan"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "Pindah ke Ruangkerja Atas"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "Pindah ke Ruangkerja Bawah"
+
+#: js/ui/windowMenu.js:132
+#| msgid "Move to Workspace Up"
+msgid "Move to Monitor Up"
+msgstr "Alih ke Monitor Atas"
+
+#: js/ui/windowMenu.js:141
+#| msgid "Move to Workspace Down"
+msgid "Move to Monitor Down"
+msgstr "Alih ke Monitor Bawah"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "Alih ke Monitor Kiri"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "Alih ke Monitor Kanan"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Kalendar Evolution"
+
+#: src/main.c:460 subprojects/extensions-tool/src/main.c:249
+msgid "Print version"
+msgstr "Cetak versi"
+
+#: src/main.c:466
+msgid "Mode used by GDM for login screen"
+msgstr "Mod yang digunakan oleh GDM untuk skrin daftar masuk"
+
+#: src/main.c:472
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Guna satu mod khusus, cth. \"gdm\" untuk skrin daftar masuk"
+
+#: src/main.c:478
+msgid "List possible modes"
+msgstr "Senarai mod yang mungkin"
+
+#: src/shell-app.c:279
+msgctxt "program"
+msgid "Unknown"
+msgstr "Tidak diketahui"
+
+#: src/shell-app.c:530
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Gagal melancarkan “%s”"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "Kata laluan tidak sepadan."
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "Kata laluan tidak boleh kosong"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Dialog pengesahihan telah digugurkan oleh pengguna"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:125
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Sambungan baharu berjaya dicipta dalam %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:194
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Nama mestilah rentetan yang sangat pendek (jelas).\n"
+"Contohnya: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:197
+#: subprojects/extensions-tool/src/main.c:170
+msgid "Name"
+msgstr "Nama"
+
+#: subprojects/extensions-tool/src/command-create.c:208
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Keterangan ialah penjelasan satu ayat berkenaan sambungan tersebut.\n"
+"Contohnya: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:211
+#: subprojects/extensions-tool/src/main.c:173
+msgid "Description"
+msgstr "Keterangan"
+
+#: subprojects/extensions-tool/src/command-create.c:222
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID merupakan pengecam unik-sejagat untuk sambungan anda.\n"
+"Ia seharusnya dalam format alamat emel (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:244
+msgid "The unique identifier of the new extension"
+msgstr "Pengecam unik bagi sambungan baharu"
+
+#: subprojects/extensions-tool/src/command-create.c:247
+msgid "NAME"
+msgstr "NAMA"
+
+#: subprojects/extensions-tool/src/command-create.c:248
+msgid "The user-visible name of the new extension"
+msgstr "Nama tampak-oleh-pengguna bagi sambungan baharu"
+
+#: subprojects/extensions-tool/src/command-create.c:250
+msgid "DESCRIPTION"
+msgstr "DESCRIPTION"
+
+#: subprojects/extensions-tool/src/command-create.c:252
+msgid "A short description of what the extension does"
+msgstr "Satu keterangan pendek apakah fungsi sambungan tersebut"
+
+#: subprojects/extensions-tool/src/command-create.c:255
+msgid "Enter extension information interactively"
+msgstr "Masukkan maklumat sambungan secara interaktif"
+
+#: subprojects/extensions-tool/src/command-create.c:263
+msgid "Create a new extension"
+msgstr "Cipta satu sambungan baharu"
+
+#: subprojects/extensions-tool/src/command-create.c:280
+#: subprojects/extensions-tool/src/command-list.c:158
+#| msgid "Unknown reason"
+msgid "Unknown arguments"
+msgstr "Argumen tidak diketahui"
+
+#: subprojects/extensions-tool/src/command-create.c:289
+msgid "UUID, name and description are required"
+msgstr "UUID, nama dan keterangan diperlukan"
+
+#: subprojects/extensions-tool/src/command-disable.c:57
+msgid "Disable an extension"
+msgstr "Lumpuhkan satu sambungan"
+
+#: subprojects/extensions-tool/src/command-disable.c:74
+#: subprojects/extensions-tool/src/command-enable.c:74
+#: subprojects/extensions-tool/src/command-info.c:96
+#: subprojects/extensions-tool/src/command-prefs.c:107
+#: subprojects/extensions-tool/src/command-reset.c:74
+#: subprojects/extensions-tool/src/command-uninstall.c:89
+msgid "No UUID given"
+msgstr "Tiada UUID diberi"
+
+#: subprojects/extensions-tool/src/command-disable.c:79
+#: subprojects/extensions-tool/src/command-enable.c:79
+#: subprojects/extensions-tool/src/command-info.c:101
+#: subprojects/extensions-tool/src/command-prefs.c:112
+#: subprojects/extensions-tool/src/command-reset.c:79
+#: subprojects/extensions-tool/src/command-uninstall.c:94
+msgid "More than one UUID given"
+msgstr "Lebih daripada satu UUID diberi"
+
+#: subprojects/extensions-tool/src/command-enable.c:57
+msgid "Enable an extension"
+msgstr "Benarkan satu sambungan"
+
+#: subprojects/extensions-tool/src/command-info.c:79
+#| msgid "No extensions installed"
+msgid "Show extensions info"
+msgstr "Tunjuk maklumat sambungan"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Tulis-ganti satu sambungan sedia ada"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "BERKAS_SAMBUNGAN"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+#| msgid "UUIDs of extensions to enable"
+msgid "Install an extension bundle"
+msgstr "Pasang satu berkas sambungan"
+
+#: subprojects/extensions-tool/src/command-install.c:201
+#| msgid "No extensions installed"
+msgid "No extension bundle specified"
+msgstr "Tiada berkas sambungan dinyatakan"
+
+#: subprojects/extensions-tool/src/command-install.c:207
+msgid "More than one extension bundle specified"
+msgstr "Lebih daripada satu berkas sambungan dinyatakan"
+
+#: subprojects/extensions-tool/src/command-list.c:118
+msgid "Show user-installed extensions"
+msgstr "Tunjuk sambungan dipasang-pengguna"
+
+#: subprojects/extensions-tool/src/command-list.c:121
+msgid "Show system-installed extensions"
+msgstr "Tunjuk sambungan dipasang-sistem"
+
+#: subprojects/extensions-tool/src/command-list.c:124
+#| msgid "GNOME Shell Extensions"
+msgid "Show enabled extensions"
+msgstr "Tunjuk sambungan dibenarkan"
+
+#: subprojects/extensions-tool/src/command-list.c:127
+msgid "Show disabled extensions"
+msgstr "Tunjuk sambungan dilumpuhkan"
+
+#: subprojects/extensions-tool/src/command-list.c:130
+msgid "Show extensions with preferences"
+msgstr "Tunjuk sambungan dengan keutamaan"
+
+#: subprojects/extensions-tool/src/command-list.c:133
+#| msgid "Print version"
+msgid "Print extension details"
+msgstr "Cetak perincian sambungan"
+
+#: subprojects/extensions-tool/src/command-list.c:141
+msgid "List installed extensions"
+msgstr "Senarai sambungan terpasang"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FAIL"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Sumber tambahan yang disertakan di dalam berkas"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SKEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Skema GSetting yang patut disertakan"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIREKTORI"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Direktori yang mana fail-fail terjemahan diletakkan"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Domain gettext yang digunakan untuk terjemahan"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Tulis-ganti pek sedia ada"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Direktori yang mana pek patut dicipta"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIREKTORI_SUMBER"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Cipta satu berkas sambungan"
+
+#: subprojects/extensions-tool/src/command-pack.c:500
+msgid "More than one source directory specified"
+msgstr "Lebih daripada satu direktori sumber dinyatakan"
+
+#: subprojects/extensions-tool/src/command-prefs.c:90
+msgid "Opens extension preferences"
+msgstr "Buka keutamaan sambungan"
+
+#: subprojects/extensions-tool/src/command-reset.c:57
+msgid "Reset an extension"
+msgstr "Tetap semula satu sambungan"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:72
+msgid "Uninstall an extension"
+msgstr "Nyahpasang satu sambungan"
+
+#: subprojects/extensions-tool/src/main.c:176
+msgid "Path"
+msgstr "Laluan"
+
+#: subprojects/extensions-tool/src/main.c:179
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:182
+msgid "Original author"
+msgstr "Pengarang asal"
+
+#: subprojects/extensions-tool/src/main.c:185
+#| msgid "Print version"
+msgid "Version"
+msgstr "Versi"
+
+#: subprojects/extensions-tool/src/main.c:188
+msgid "State"
+msgstr "Negeri"
+
+#: subprojects/extensions-tool/src/main.c:222
+msgid "“version” takes no arguments"
+msgstr "\"version\" tidak mengambil argumen"
+
+#: subprojects/extensions-tool/src/main.c:224
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Usage:"
+msgstr "Penggunaan:"
+
+#: subprojects/extensions-tool/src/main.c:227
+msgid "Print version information and exit."
+msgstr "Cetak maklumat versi kemudian keluar."
+
+#: subprojects/extensions-tool/src/main.c:242
+#: subprojects/extensions-tool/src/main.c:245
+msgid "COMMAND"
+msgstr "PERINTAH"
+
+#: subprojects/extensions-tool/src/main.c:245
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "Commands:"
+msgstr "Perintah:"
+
+#: subprojects/extensions-tool/src/main.c:248
+#| msgid "Print version"
+msgid "Print help"
+msgstr "Cetak bantuan"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Enable extension"
+msgstr "Benarkan sambungan"
+
+#: subprojects/extensions-tool/src/main.c:251
+msgid "Disable extension"
+msgstr "Lumpuhkan sambungan"
+
+#: subprojects/extensions-tool/src/main.c:252
+msgid "Reset extension"
+msgstr "Tetap semula sambungan"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Uninstall extension"
+msgstr "Nyahpasang sambungan"
+
+#: subprojects/extensions-tool/src/main.c:254
+msgid "List extensions"
+msgstr "Senarai sambungan"
+
+#: subprojects/extensions-tool/src/main.c:255
+#: subprojects/extensions-tool/src/main.c:256
+#| msgid "No extensions installed"
+msgid "Show extension info"
+msgstr "Tunjuk maklumat sambungan"
+
+#: subprojects/extensions-tool/src/main.c:257
+msgid "Open extension preferences"
+msgstr "Buka keutamaan sambungan"
+
+#: subprojects/extensions-tool/src/main.c:258
+msgid "Create extension"
+msgstr "Cipta sambungan"
+
+#: subprojects/extensions-tool/src/main.c:259
+msgid "Package extension"
+msgstr "Pakej sambungan"
+
+#: subprojects/extensions-tool/src/main.c:260
+#| msgid "UUIDs of extensions to enable"
+msgid "Install extension bundle"
+msgstr "Pasang berkas sambungan"
+
+#: subprojects/extensions-tool/src/main.c:262
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Guna \"%s\" untuk dapatkan bantuan terperinci.\n"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1899
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u Output"
+msgstr[1] "%u Output"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1909
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u Input"
+msgstr[1] "%u Input"
+
+#: subprojects/gvc/gvc-mixer-control.c:2766
+msgid "System Sounds"
+msgstr "Bunyi Sistem"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Tunjukkan talam mesej"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Tunjuk minggu pada kalendar"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Jika benar, papar tarikh minggu ISO pada kalendar."
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Pilih papan kekunci"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Jenis papan kekunci untuk digunakan."
+
+#~| msgid "Authentication Required"
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Lencongan Pengesahan Web"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "A"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "I"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Se"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "R"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "K"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "J"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "S"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Jadual Kosong"
+
+#~ msgid "Today"
+#~ msgstr "Hari ini"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Esok"
+
+#~ msgid "This week"
+#~ msgstr "Minggu ini"
+
+#~ msgid "Next week"
+#~ msgstr "Minggu depan"
+
+#~| msgid "Reject"
+#~ msgid "Eject"
+#~ msgstr "Lentingkan"
+
+#~ msgid "Invitation"
+#~ msgstr "Jemputan"
+
+#~ msgid "Call"
+#~ msgstr "Panggilan"
+
+#~ msgid "File Transfer"
+#~ msgstr "Pemindahan Fail"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Jemputan untuk %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s menjemput anda untuk menyertai %s"
+
+#~ msgid "Decline"
+#~ msgstr "Tolak"
+
+#~ msgid "Accept"
+#~ msgstr "Terima"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Panggilan video dari %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Panggilan dari %s"
+
+#~ msgid "Answer"
+#~ msgstr "Jawapan"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s menghantarkan %s kepada anda"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s mahukan kebenaran untuk melihat bila anda akan ada di talian"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Pengesahan gagal"
+
+#~ msgid "Encryption error"
+#~ msgstr "Ralat penyulitan"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Perakuan tidak diberikan"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Perakuan tidak dipercayai"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Perakuan habis tempoh"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Perakuan tidak diaktifkan"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Nama hos perakuan tak padan"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Fingerprint perakuan tak padan"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Perakuan didaftar sendiri"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Status ditetapkan kepada luar talian"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Perakuan tak sah"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Sambungan telah ditolak"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Sambungan tidak dapat dibuat"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Sambungan telah terputus"
+
+#~| msgid "This resource is already connected to the server"
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Akaun ini telah pun disambungkan ke pelayan"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "Sambungan telah digantikan dengan sambungan baru dengan sumber yang sama"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Akaun berkenaan telah pun wujud pada pelayan"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Pelayan sedang terlalu sibuk untuk mengendalikan sambungan"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Perakuan telah dibatalkan"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Perakuan menggunakan algoritma sifer yang tak selamat atau rapuh dari "
+#~ "segi kriptografi"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Panjang perakuan pelayan, atau kedalaman rantai perakuan pelayan, "
+#~ "melebihi had yang dikenakan oleh pustaka kriptografi"
+
+#~| msgid "Connection error"
+#~ msgid "Internal error"
+#~ msgstr "Ralat dalaman"
+
+#~| msgid "Edit account"
+#~ msgid "View account"
+#~ msgstr "Tunjukkan akaun"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Buka Kalendar"
+
+#~ msgid "Open"
+#~ msgstr "Buka"
+
+#~ msgid "Remove"
+#~ msgstr "Buang"
+
+#~| msgid "Notifications"
+#~ msgid "Notification Settings"
+#~ msgstr "Tetapan Makluman"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~| msgid "Connected (private)"
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d Peranti Tersambung"
+#~ msgstr[1] "%d Peranti Tersambung"
+
+#~| msgid "authentication required"
+#~ msgid "Authentication required"
+#~ msgstr "Pengesahan diperlukan"
+
+#~ msgid "connecting..."
+#~ msgstr "menyambung..."
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
diff --git a/po/nb.po b/po/nb.po
new file mode 100644
index 0000000..3dc3e4b
--- /dev/null
+++ b/po/nb.po
@@ -0,0 +1,2977 @@
+# Norwegian bokmål translation of gnome-shell.
+# Copyright (C) 2009 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Åka Sikrom <a4@hush.com>, 2014-2015.
+# Kjartan Maraas <kmaraas@gnome.org>, 2009-2021.
+# Torstein Adolf Winterseth <kvikende@fsfe.org>, 2010.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell 3.40.x\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2021-03-06 17:28+0000\n"
+"PO-Revision-Date: 2021-03-13 17:51+0100\n"
+"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
+"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
+"Language: nb\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "System"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Vis varslingsliste"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokuser på aktiv varsling"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Vis oversikt"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Vis alle programmer"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Åpne programmenyen"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Vindushåndtering og oppstart av programmer"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Slå på interne verktøy for utviklere og testere fra Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Tillat tilgang til interne feilsøkings- og overvåkingsverktøy ved å bruke "
+"Alt-F2-dialogen."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID-er for utvidelser som skal slås på"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell-utvidelser har en UUID-egenskap. Denne nøkkelen viser utvidelser "
+"som skal lastes inn. Alle utvidelser som ønsker å bli lastet inn må være med "
+"på denne lista. Du kan også manipulere lista med DBus-metodene "
+"EnablExtension og DisableExtension på org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID-er for utvidelser som skal tvinges til å slås av"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell-utvidelser har en UUID-egenskap. Denne nøkkelen viser utvidelser "
+"som skal slås av selv om de lastes som en del av aktiv modus. Du kan også "
+"manipulere lista med DBus-metodene EnablExtension og DisableExtension på org."
+"gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Slå av brukerutvideleser"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Slå av alle utvidelser bruker har aktivert uten å påvirke innstillingen for "
+"«enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+"Slår av gyldighetssjekk av hvorvidt programversjoner passer med utvidelser"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell laster bare inn utvidelser som hevder å støtte kjørende versjon. "
+"Hvis du slår på dette valget, slår du av denne sjekken, og alle utvidelser "
+"lastes inn uavhengig av hvilke versjoner de hevder å støtte."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Liste av skrivebordsfil-id-er for favorittprogrammer"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programmene som passer til disse identifikatorene vises i favorittområdet."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historikk for kommandodialog (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historikk for forstørrelsesglass-dialogen"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Alltid vis menyoppføringen «Logg ut» i brukermenyen."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Denne nøkkelen overstyrer automatisk skjuling av menyoppføringen «Logg ut» i "
+"situasjonen én bruker, én sesjon."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "Om passord til eksterne eller krypterte filsystemer skal huskes."
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Skallet spør etter passord når du monterer en kryptert enhet eller et "
+"eksternt filsystem. Hvis passordet kan lagres for fremtidig bruk, vises en "
+"avkryssingsboks med teksten «Husk passord». Denne nøkkelen bestemmer "
+"standardverdien for avkryssingsboksen."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Hvorvidt forvalgt Bluetooth-enhet hadde ferdigoppsatte enheter assosiert med "
+"seg"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Enable introspection API"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:110
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:141
+msgid "Layout of the app picker"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to open the application menu"
+msgstr "Tastaturbinding som åpner programmenyen"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu."
+msgstr "Tastaturbinding som åpner programmenyen."
+
+#: data/org.gnome.shell.gschema.xml.in:164
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid "Keybinding to shift between overview states"
+msgstr "Tastaturbinding som bytter mellom oversiktstilstander"
+
+#: data/org.gnome.shell.gschema.xml.in:165
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Tastaturbinding som åpner visningen «Vis programmer»"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Tastaturbinding som åpner visningen «Vis programmer» i aktivitetsoversikten."
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to open the overview"
+msgstr "Tastaturbinding som åpner oversikten"
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the Activities Overview."
+msgstr "Tastaturbinding som åpner aktivitetsoversikten."
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Tastaturbinding som slår av/på visning av varslingsliste"
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Tastaturbinding som slår av/på visning av varslingsliste."
+
+#: data/org.gnome.shell.gschema.xml.in:200
+msgid "Keybinding to focus the active notification"
+msgstr "Tastaturbinding som fokuserer aktiv varsling"
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification."
+msgstr "Tastaturbinding som fokuserer aktiv varsling."
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 1"
+msgstr "Bytt til program 1"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 2"
+msgstr "Bytt til program 2"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 3"
+msgstr "Bytt til program 3"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 4"
+msgstr "Bytt til program 4"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 5"
+msgstr "Bytt til program 5"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 6"
+msgstr "Bytt til program 6"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 7"
+msgstr "Bytt til program 7"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "Switch to application 8"
+msgstr "Bytt til program 8"
+
+#: data/org.gnome.shell.gschema.xml.in:239
+msgid "Switch to application 9"
+msgstr "Bytt til program 9"
+
+#: data/org.gnome.shell.gschema.xml.in:248
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Limit switcher to current workspace."
+msgstr "Begrens programveksling til gjeldende arbeidsområde."
+
+#: data/org.gnome.shell.gschema.xml.in:249
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Hvis verdien er sann («true»), er det bare programmer med vinduer på "
+"gjeldende arbeidsområder som vises i programveksleren. I motsatt fall kan du "
+"veksle mellom programmer på tvers av arbeidsområder."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid "The application icon mode."
+msgstr "Ikonmodus for programmet."
+
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Bestemmer hvordan vinduene vises i programveksleren. Gyldige "
+"valgalternativer er «thumbnail-only» (viser miniatyrbilde av vinduet), «app-"
+"icon-only» (viser bare programikonet) og «both» (viser begge forannevnte)."
+
+#: data/org.gnome.shell.gschema.xml.in:276
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Hvis verdien er sann («true»), er det bare vinduer på gjeldende "
+"arbeidsområder som vises i programveksleren. I motsatt fall vises vinduer "
+"fra alle arbeidsområder."
+
+#: data/org.gnome.shell.gschema.xml.in:286
+msgid "Locations"
+msgstr "Steder"
+
+#: data/org.gnome.shell.gschema.xml.in:287
+msgid "The locations to show in world clocks"
+msgstr "Steder som skal vises i verdensklokken"
+
+#: data/org.gnome.shell.gschema.xml.in:297
+msgid "Automatic location"
+msgstr "Automatisk stedsplassering"
+
+#: data/org.gnome.shell.gschema.xml.in:298
+msgid "Whether to fetch the current location or not"
+msgstr "Hvorvidt nåværende sted skal hentes eller ikke"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Location"
+msgstr "Sted"
+
+#: data/org.gnome.shell.gschema.xml.in:306
+msgid "The location for which to show a forecast"
+msgstr "Sted for å vise værvarsel"
+
+#: data/org.gnome.shell.gschema.xml.in:318
+msgid "Attach modal dialog to the parent window"
+msgstr "Fest modal dialog til opphavsvinduet"
+
+#: data/org.gnome.shell.gschema.xml.in:319
+#: data/org.gnome.shell.gschema.xml.in:328
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Denne nøkkelen overstyrer nøkkelen i org.gnome.mutter når GNOME Shell kjøres."
+
+#: data/org.gnome.shell.gschema.xml.in:327
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Del opp skjermkantene i fliser når brukeren drar og slipper vinduer på dem"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Workspaces are managed dynamically"
+msgstr "Arbeidsområder håndteres dynamisk"
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Workspaces only on primary monitor"
+msgstr "Arbeidsområder vises kun på hovedskjerm"
+
+#: data/org.gnome.shell.gschema.xml.in:351
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Utsett fokusendringer i musmodus til peker slutter å bevege seg"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Nettverkspålogging"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:28
+#: subprojects/extensions-app/data/ui/extensions-window.ui:241
+msgid "Something’s gone wrong"
+msgstr "Noe gikk galt"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:39
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Beklager, men det har oppstått et problem. Innstillingene for denne "
+"utvidelsen kan ikke vises. Vi anbefaler at du rapporterer dette til "
+"utviklerene av denne utvidelsen."
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:64
+msgid "Technical Details"
+msgstr "Tekniske detaljer"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:106
+msgid "Homepage"
+msgstr "Hjemmeside"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:107
+msgid "Visit extension homepage"
+msgstr "Gå til hjemmesiden for utvidelsen"
+
+#: js/gdm/authPrompt.js:141 js/ui/audioDeviceSelection.js:58
+#: js/ui/components/networkAgent.js:111 js/ui/components/polkitAgent.js:138
+#: js/ui/endSessionDialog.js:438 js/ui/extensionDownloader.js:183
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:941 subprojects/extensions-app/js/main.js:183
+msgid "Cancel"
+msgstr "Avbryt"
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:244 js/ui/components/networkAgent.js:210
+#: js/ui/components/networkAgent.js:226 js/ui/components/networkAgent.js:250
+#: js/ui/components/networkAgent.js:271 js/ui/components/networkAgent.js:291
+#: js/ui/components/networkAgent.js:301 js/ui/components/polkitAgent.js:275
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "Passord"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Velg økt"
+
+#: js/gdm/loginDialog.js:456
+msgid "Not listed?"
+msgstr "Ikke listet?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:918
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(f.eks. bruker eller %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:923 js/ui/components/networkAgent.js:246
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:287
+msgid "Username"
+msgstr "Brukernavn"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Innloggingsvindu"
+
+#: js/gdm/util.js:430
+msgid "Authentication error"
+msgstr "Autentiseringsfeil"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:589
+msgid "(or swipe finger across reader)"
+msgstr "(eller dra fingeren over leseren)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:594
+msgid "(or place finger on reader)"
+msgstr "(plasser fingeren på leseren)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:82
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Slå av"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:85
+msgid "power off;shutdown;halt;stop"
+msgstr "slå av;steng ned;omstart;stopp"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:90
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Start på nytt"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:93
+msgid "reboot;restart;"
+msgstr "omstart;start på nytt;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:98
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Lås skjerm"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:101
+msgid "lock screen"
+msgstr "lås skjerm"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Logg ut"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "logout;log out;sign off"
+msgstr "logg ut;logg av;"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:114
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Hvilemodus"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:117
+msgid "suspend;sleep"
+msgstr "hvilemodus;sov;"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:122
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Bytt bruker"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:125
+msgid "switch user"
+msgstr "bytt bruker"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "lås orientering;lås opp orientering;skjerm;rotasjon;"
+
+#: js/misc/systemActions.js:232
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Lås opp skjermorientering"
+
+#: js/misc/systemActions.js:233
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Lås skjermorientering"
+
+#: js/misc/util.js:120
+msgid "Command not found"
+msgstr "Kommando ikke funnet"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:156
+msgid "Could not parse command:"
+msgstr "Klarte ikke å lese kommando:"
+
+#: js/misc/util.js:164
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Kjøring av «%s» mislyktes:"
+
+#: js/misc/util.js:181
+msgid "Just now"
+msgstr "Akkurat nå"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minutt siden"
+msgstr[1] "%d minutter siden"
+
+#: js/misc/util.js:187
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d time siden"
+msgstr[1] "%d timer siden"
+
+#: js/misc/util.js:191 js/ui/dateMenu.js:162
+msgid "Yesterday"
+msgstr "I går"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dag siden"
+msgstr[1] "%d dager siden"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d uke siden"
+msgstr[1] "%d uker siden"
+
+#: js/misc/util.js:201
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d måned siden"
+msgstr[1] "%d måneder siden"
+
+#: js/misc/util.js:204
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d år siden"
+msgstr[1] "%d år siden"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:237
+msgid "%H∶%M"
+msgstr "%H.%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:243
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "I går, %H.%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:249
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H.%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:255
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H.%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:261
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H.%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:266
+msgid "%l∶%M %p"
+msgstr "%H.%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:272
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "I går, %H.%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:278
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %H.%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:284
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %H.%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:290
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %H.%M"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:42
+msgid "Hotspot Login"
+msgstr "Pålogging for trådløst aksesspunkt"
+
+#: js/portalHelper/main.js:88
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Påloggingen din til dette trådløse aksesspunktet er ikke sikker. Passord "
+"eller annen informasjon du oppgir på denne siden kan ses av andre i nærheten."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:369
+msgid "Deny Access"
+msgstr "Nekt tilgang"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:372
+msgid "Grant Access"
+msgstr "Gi tilgang"
+
+#: js/ui/appDisplay.js:1815
+msgid "Unnamed Folder"
+msgstr "Mappe uten navn"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:3392 js/ui/panel.js:33
+msgid "Open Windows"
+msgstr "Åpne vinduer"
+
+#: js/ui/appDisplay.js:3411 js/ui/panel.js:40
+msgid "New Window"
+msgstr "Nytt vindu"
+
+#: js/ui/appDisplay.js:3427
+msgid "Launch using Integrated Graphics Card"
+msgstr "Start med integrert skjermkort"
+
+#: js/ui/appDisplay.js:3428
+msgid "Launch using Discrete Graphics Card"
+msgstr "Start med dedikert skjermkort"
+
+#: js/ui/appDisplay.js:3457 js/ui/dash.js:245
+msgid "Remove from Favorites"
+msgstr "Fjern fra favoritter"
+
+#: js/ui/appDisplay.js:3463
+msgid "Add to Favorites"
+msgstr "Legg til i favoritter"
+
+#: js/ui/appDisplay.js:3473 js/ui/panel.js:51
+msgid "Show Details"
+msgstr "Vis detaljer"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s ble lagt til i favoritter."
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s ble fjernet fra favoritter."
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Velg lydenhet"
+
+#: js/ui/audioDeviceSelection.js:55
+msgid "Sound Settings"
+msgstr "Innstillinger for lyd"
+
+#: js/ui/audioDeviceSelection.js:65
+msgid "Headphones"
+msgstr "Hodetelefoner"
+
+#: js/ui/audioDeviceSelection.js:67
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:69 js/ui/status/volume.js:277
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Bytt bakgrunn …"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Innstillinger for skjerm"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "Innstillinger"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:36
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:65
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:67
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:69
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:71
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "O"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:73
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:75
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:77
+msgctxt "grid saturday"
+msgid "S"
+msgstr "L"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:392
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:461
+msgid "Previous month"
+msgstr "Forrige måned"
+
+#: js/ui/calendar.js:476
+msgid "Next month"
+msgstr "Neste måned"
+
+#: js/ui/calendar.js:626
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:682
+msgid "Week %V"
+msgstr "Uke %V"
+
+#: js/ui/calendar.js:896
+msgid "No Notifications"
+msgstr "Ingen varslinger"
+
+#: js/ui/calendar.js:950
+msgid "Do Not Disturb"
+msgstr "Ikke forstyrr"
+
+#: js/ui/calendar.js:969
+msgid "Clear"
+msgstr "Fjern"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» svarer ikke."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Du kan vente en liten stund for å se om det fortsetter eller tvinge "
+"programmet til å avsluttes."
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "Tvunget avslutting"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "Vent"
+
+#: js/ui/components/automountManager.js:85
+msgid "External drive connected"
+msgstr "Ekstern stasjon koblet til"
+
+#: js/ui/components/automountManager.js:97
+msgid "External drive disconnected"
+msgstr "Ekstern stasjon koblet fra"
+
+#: js/ui/components/automountManager.js:206
+msgid "Unable to unlock volume"
+msgstr "Kan ikke låse opp volum"
+
+#: js/ui/components/automountManager.js:207
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Installert versjon av udisks støtter ikke PIM-innstillingene"
+
+#: js/ui/components/autorunManager.js:332
+#, javascript-format
+msgid "Open with %s"
+msgstr "Åpne med %s"
+
+#: js/ui/components/networkAgent.js:93
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternativt kan du koble til ved å trykke på «WPS»-knappen på ruteren din."
+
+#: js/ui/components/networkAgent.js:105 js/ui/status/network.js:253
+#: js/ui/status/network.js:344 js/ui/status/network.js:944
+msgid "Connect"
+msgstr "Koble til"
+
+#: js/ui/components/networkAgent.js:216
+msgid "Key"
+msgstr "Nøkkel"
+
+#: js/ui/components/networkAgent.js:254 js/ui/components/networkAgent.js:277
+msgid "Private key password"
+msgstr "Passord for privat nøkkel"
+
+#: js/ui/components/networkAgent.js:275
+msgid "Identity"
+msgstr "Identitet"
+
+#: js/ui/components/networkAgent.js:289
+msgid "Service"
+msgstr "Tjeneste"
+
+#: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:346
+#: js/ui/components/networkAgent.js:679 js/ui/components/networkAgent.js:700
+msgid "Authentication required"
+msgstr "Autentisering kreves"
+
+#: js/ui/components/networkAgent.js:319 js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Passord eller krypteringsnøkler kreves for å koble til trådløst nettverk "
+"«%s»."
+
+#: js/ui/components/networkAgent.js:323 js/ui/components/networkAgent.js:684
+msgid "Wired 802.1X authentication"
+msgstr "802.1X autentisering for trådbundet nettverk"
+
+#: js/ui/components/networkAgent.js:325
+msgid "Network name"
+msgstr "Navn på nettverk"
+
+#: js/ui/components/networkAgent.js:330 js/ui/components/networkAgent.js:688
+msgid "DSL authentication"
+msgstr "DSL-autentisering"
+
+#: js/ui/components/networkAgent.js:337 js/ui/components/networkAgent.js:693
+msgid "PIN code required"
+msgstr "PIN-kode kreves"
+
+#: js/ui/components/networkAgent.js:338 js/ui/components/networkAgent.js:694
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN-kode kreves for mobil bredbåndsenhet"
+
+#: js/ui/components/networkAgent.js:339
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:347 js/ui/components/networkAgent.js:685
+#: js/ui/components/networkAgent.js:689 js/ui/components/networkAgent.js:701
+#: js/ui/components/networkAgent.js:705
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Du må oppgi et passord for å koble til «%s»."
+
+#: js/ui/components/networkAgent.js:668 js/ui/status/network.js:1718
+msgid "Network Manager"
+msgstr "Nettverkshåndtering"
+
+#: js/ui/components/networkAgent.js:704
+msgid "VPN password"
+msgstr "Passord for VPN"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "Autentisering kreves"
+
+#: js/ui/components/polkitAgent.js:79
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:141
+msgid "Authenticate"
+msgstr "Autentiser"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:252 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Beklager, det virket ikke. Prøv igjen."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:822
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s er nå kjent som %s"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/overviewControls.js:377
+msgid "Windows"
+msgstr "Vinduer"
+
+#: js/ui/dash.js:204 js/ui/dash.js:247
+msgid "Show Applications"
+msgstr "Vis programmer"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr "Favoritter"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:79
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:86
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:151
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:154
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:160
+msgid "Today"
+msgstr "I dag"
+
+#: js/ui/dateMenu.js:164
+msgid "Tomorrow"
+msgstr "I morgen"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:180
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Hele dagen"
+
+#: js/ui/dateMenu.js:231
+msgid "No Events"
+msgstr "Ingen hendelser"
+
+#: js/ui/dateMenu.js:348
+msgid "Add world clocks…"
+msgstr "Legg til verdensklokker …"
+
+#: js/ui/dateMenu.js:349
+msgid "World Clocks"
+msgstr "Verdensklokker"
+
+#: js/ui/dateMenu.js:629
+msgid "Loading…"
+msgstr "Laster …"
+
+#: js/ui/dateMenu.js:639
+msgid "Go online for weather information"
+msgstr "Gå på nettet for informasjon om været"
+
+#: js/ui/dateMenu.js:641
+msgid "Weather information is currently unavailable"
+msgstr "Værinformasjon er for tiden utilgjengelig"
+
+#: js/ui/dateMenu.js:651
+msgid "Weather"
+msgstr "Vær"
+
+#: js/ui/dateMenu.js:653
+msgid "Select weather location…"
+msgstr "Velg en lokasjon for vær …"
+
+#: js/ui/endSessionDialog.js:39
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Logg ut %s"
+
+#: js/ui/endSessionDialog.js:40
+msgctxt "title"
+msgid "Log Out"
+msgstr "Logg ut"
+
+#: js/ui/endSessionDialog.js:43
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s logges ut automatisk om %d sekund."
+msgstr[1] "%s logges ut automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:49
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Du blir logget ut automatisk om %d sekund."
+msgstr[1] "Du blir logget ut automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "Logg ut"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "Slå av"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installer oppdateringer og slå av"
+
+#: js/ui/endSessionDialog.js:66
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Systemet slås av automatisk om %d sekund."
+msgstr[1] "Systemet slås av automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:70 js/ui/endSessionDialog.js:89
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installer utestående programvareoppdateringer"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "Slå av"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "Start på nytt"
+
+#: js/ui/endSessionDialog.js:82
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Installer oppdateringer og start på nytt"
+
+#: js/ui/endSessionDialog.js:85
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Systemet starter på nytt automatisk om %d sekund."
+msgstr[1] "Systemet starter på nytt automatisk om %d sekunder."
+
+#: js/ui/endSessionDialog.js:93
+msgctxt "button"
+msgid "Restart"
+msgstr "Start på nytt"
+
+#: js/ui/endSessionDialog.js:101
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Start på nytt og installer oppdateringer"
+
+#: js/ui/endSessionDialog.js:104
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Systemet starter på nytt og installere oppdateringer automatisk om %d sekund."
+msgstr[1] ""
+"Systemet starter på nytt og installere oppdateringer automatisk om %d "
+"sekunder."
+
+#: js/ui/endSessionDialog.js:111 js/ui/endSessionDialog.js:132
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Start på nytt og installer"
+
+#: js/ui/endSessionDialog.js:113
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installer og slå av"
+
+#: js/ui/endSessionDialog.js:114
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Slå av etter at oppdateringer er installert"
+
+#: js/ui/endSessionDialog.js:121
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Start på nytt og installer oppgradering"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:126
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s vil bli installert etter omstart. Installering av oppgraderinger kan "
+"ta lang tid: sjekk at du har en sikkerhetskopi og at datamaskinen er koblet "
+"til strøm."
+
+#: js/ui/endSessionDialog.js:284
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Lite batteristrøm: vennligst koble til strøm før oppdateringer installeres."
+
+#: js/ui/endSessionDialog.js:293
+msgid "Some applications are busy or have unsaved work"
+msgstr "Noen programmer er opptatt eller har arbeid som ikke er lagret"
+
+#: js/ui/endSessionDialog.js:298
+msgid "Other users are logged in"
+msgstr "Andre brukere er logget inn."
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Alternativer for oppstart"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:686
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (ekstern)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:689
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsoll)"
+
+#: js/ui/extensionDownloader.js:187
+msgid "Install"
+msgstr "Installer"
+
+#: js/ui/extensionDownloader.js:193
+msgid "Install Extension"
+msgstr "Installer utvideleser"
+
+#: js/ui/extensionDownloader.js:194
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Vil du laste ned og installere «%s» fra extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:253
+msgid "Extension Updates Available"
+msgstr "Oppdateringer for utvidelser tilgjengelig"
+
+#: js/ui/extensionSystem.js:254
+msgid "Extension updates are ready to be installed."
+msgstr "Oppdateringer for utvidelser er klar for installasjon."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Tillat hindring av snarveier"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Programmet %s ønsker å hindre snarveier"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Programmet ønsker å hindre snarveier"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Du kan gjenopprette snarveier ved å trykke %s."
+
+#: js/ui/inhibitShortcutsDialog.js:100
+msgid "Deny"
+msgstr "Nekt"
+
+#: js/ui/inhibitShortcutsDialog.js:107
+msgid "Allow"
+msgstr "Tillat"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Trege taster slått på"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Trege taster slått av"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Du holdt nede Shift-tasten i 8 sekunder. Dette er snarveien for trege taster "
+"funksjonen som påvirker hvordan tastaturet virker."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Klebrige taster er slått på"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Klebrige taster er slått av"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Du trykket på Shift tasten fem ganger på rad. Dette er snarveien for å "
+"aktivere klebrige taster og påvirker hvordan tastaturet virker."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Du trykket på to taster samtidig eller trykket ned Shift tasten fem ganger "
+"på rad. Dette slår av funksjonen klebrige taster og påvirker måten "
+"tastaturet virker."
+
+#: js/ui/kbdA11yDialog.js:54
+msgid "Leave On"
+msgstr "La den være på"
+
+#: js/ui/kbdA11yDialog.js:54 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:1316
+msgid "Turn On"
+msgstr "Slå på"
+
+#: js/ui/kbdA11yDialog.js:62 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:161 js/ui/status/network.js:345
+#: js/ui/status/network.js:1316 js/ui/status/network.js:1428
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Slå av"
+
+#: js/ui/kbdA11yDialog.js:62
+msgid "Leave Off"
+msgstr "La den være av"
+
+#: js/ui/keyboard.js:227
+msgid "Region & Language Settings"
+msgstr "Innstillinger for region og språk"
+
+#: js/ui/lookingGlass.js:676
+msgid "No extensions installed"
+msgstr "Ingen utvidelser installert"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:734
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s har ikke avgitt noen feil."
+
+#: js/ui/lookingGlass.js:740
+msgid "Hide Errors"
+msgstr "Skjul feil"
+
+#: js/ui/lookingGlass.js:744 js/ui/lookingGlass.js:810
+msgid "Show Errors"
+msgstr "Vis feil"
+
+#: js/ui/lookingGlass.js:753
+msgid "Enabled"
+msgstr "Slått på"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:756 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Slått av"
+
+#: js/ui/lookingGlass.js:758
+#: subprojects/extensions-app/data/ui/extension-row.ui:158
+msgid "Error"
+msgstr "Feil"
+
+#: js/ui/lookingGlass.js:760
+msgid "Out of date"
+msgstr "Utdatert"
+
+#: js/ui/lookingGlass.js:762
+msgid "Downloading"
+msgstr "Laster ned"
+
+#: js/ui/lookingGlass.js:792
+msgid "View Source"
+msgstr "Vis kildekode"
+
+#: js/ui/lookingGlass.js:801
+msgid "Web Page"
+msgstr "Nettside"
+
+#: js/ui/main.js:294
+msgid "Logged in as a privileged user"
+msgstr "Logget inn som en priviligert bruker"
+
+#: js/ui/main.js:295
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Du bør unngå å kjøre en økt som en priviligert bruker av sikkerhetshensyn. "
+"Du bør logge inn som en vanlig bruker hvis mulig."
+
+#: js/ui/main.js:345
+msgid "Screen Lock disabled"
+msgstr "Skjermlås er deaktivert"
+
+#: js/ui/main.js:346
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Skjermlåsing krever GNOME skjermhåndterer."
+
+#: js/ui/messageTray.js:1452
+msgid "System Information"
+msgstr "Systeminformasjon"
+
+#: js/ui/mpris.js:207
+msgid "Unknown artist"
+msgstr "Ukjent artist"
+
+#: js/ui/mpris.js:217
+msgid "Unknown title"
+msgstr "Ukjent tittel"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:289
+msgid "Type to search"
+msgstr "Skriv for å søke"
+
+#: js/ui/overviewControls.js:365
+msgid "Applications"
+msgstr "Programmer"
+
+#: js/ui/overview.js:69
+msgid "Undo"
+msgstr "Angre"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:82
+msgid "Overview"
+msgstr "Oversikt"
+
+#: js/ui/padOsd.js:96
+msgid "New shortcut…"
+msgstr "Ny snarvei …"
+
+#: js/ui/padOsd.js:143
+msgid "Application defined"
+msgstr "Definert program"
+
+#: js/ui/padOsd.js:144
+msgid "Show on-screen help"
+msgstr "Vis hjelp på skjermen"
+
+#: js/ui/padOsd.js:145
+msgid "Switch monitor"
+msgstr "Bytt skjerm"
+
+#: js/ui/padOsd.js:146
+msgid "Assign keystroke"
+msgstr "Tildel tastatursnarvei"
+
+#: js/ui/padOsd.js:212
+msgid "Done"
+msgstr "Ferdig"
+
+#: js/ui/padOsd.js:718
+msgid "Edit…"
+msgstr "Rediger …"
+
+#: js/ui/padOsd.js:760 js/ui/padOsd.js:877
+msgid "None"
+msgstr "Ingen"
+
+#: js/ui/padOsd.js:831
+msgid "Press a button to configure"
+msgstr "Trykk en knapp for å konfigurere"
+
+#: js/ui/padOsd.js:832
+msgid "Press Esc to exit"
+msgstr "Trykk Esc for å avslutte"
+
+#: js/ui/padOsd.js:835
+msgid "Press any key to exit"
+msgstr "Trykk en tast for å avslutte"
+
+#: js/ui/panel.js:65
+msgid "Quit"
+msgstr "Avslutt"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:391
+msgid "Activities"
+msgstr "Aktiviteter"
+
+#: js/ui/panel.js:662
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "System"
+
+#: js/ui/panel.js:774
+msgid "Top Bar"
+msgstr "Topp-panel"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Skriv inn en kommando"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Trykk Esc for å lukke"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Omstart er ikke tilgjengelig i Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Starter på nytt …"
+
+#: js/ui/screenShield.js:211
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME må låse skjermen"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:252 js/ui/screenShield.js:620
+msgid "Unable to lock"
+msgstr "Klarte ikke å låse"
+
+#: js/ui/screenShield.js:253 js/ui/screenShield.js:621
+msgid "Lock was blocked by an application"
+msgstr "Låsing ble stoppet av et program"
+
+#: js/ui/screenshot.js:141
+msgid "Screenshot taken"
+msgstr "Skjermdump ble tatt"
+
+#: js/ui/search.js:824
+msgid "Searching…"
+msgstr "Søker …"
+
+#: js/ui/search.js:826
+msgid "No results."
+msgstr "Ingen resultater."
+
+#: js/ui/search.js:952
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d flere"
+msgstr[1] "%d flere"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Søk"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopier"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Lim inn"
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr "Vis tekst"
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr "Skjul tekst"
+
+#: js/ui/shellEntry.js:162
+msgid "Caps lock is on."
+msgstr "Caps Lock er på."
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr "Skjult volum"
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr "Volum for vindusystem"
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr "Bruker nøkkelfiler"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"For å låse opp et volum som bruker nøkkelfiler kan du bruke <i>%s</i> "
+"verktøyet i stedet."
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr "PIM-nummer"
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "Husk passord"
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr "Lås opp"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "Åpne %s"
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr "PIM må være et nummer eller tomt."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:465
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Kan ikke starte %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:467
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Fant ikke programmet %s"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Tilgjengelighet"
+
+#: js/ui/status/accessibility.js:48
+msgid "Zoom"
+msgstr "Zoom"
+
+#: js/ui/status/accessibility.js:55
+msgid "Screen Reader"
+msgstr "Skjermleser"
+
+#: js/ui/status/accessibility.js:59
+msgid "Screen Keyboard"
+msgstr "Tastatur på skjermen"
+
+#: js/ui/status/accessibility.js:63
+msgid "Visual Alerts"
+msgstr "Synlig varsling"
+
+#: js/ui/status/accessibility.js:66
+msgid "Sticky Keys"
+msgstr "Klebrige taster"
+
+#: js/ui/status/accessibility.js:69
+msgid "Slow Keys"
+msgstr "Trege taster"
+
+#: js/ui/status/accessibility.js:72
+msgid "Bounce Keys"
+msgstr "Spretne taster"
+
+#: js/ui/status/accessibility.js:75
+msgid "Mouse Keys"
+msgstr "Mustaster"
+
+#: js/ui/status/accessibility.js:134
+msgid "High Contrast"
+msgstr "Høy kontrast"
+
+#: js/ui/status/accessibility.js:176
+msgid "Large Text"
+msgstr "Stor tekst"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:620
+msgid "Bluetooth Settings"
+msgstr "Bluetooth-innstillinger"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:148
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d koblet til"
+msgstr[1] "%d koblet til"
+
+#: js/ui/status/bluetooth.js:152
+msgid "Bluetooth Off"
+msgstr "Bluetooth av"
+
+#: js/ui/status/bluetooth.js:154
+msgid "Bluetooth On"
+msgstr "Bluetooth på"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Lysstyrke"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Enkeltklikk"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dobbeltklikk"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Dra"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Sekundærklikk"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Venteklikk"
+
+#: js/ui/status/keyboard.js:825
+msgid "Keyboard"
+msgstr "Tastatur"
+
+#: js/ui/status/keyboard.js:842
+msgid "Show Keyboard Layout"
+msgstr "Vis tastaturutforming"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:174
+msgid "Location Enabled"
+msgstr "Plassering slått på"
+
+#: js/ui/status/location.js:66 js/ui/status/location.js:175
+msgid "Disable"
+msgstr "Slå av"
+
+#: js/ui/status/location.js:67
+msgid "Privacy Settings"
+msgstr "Innstillinger for personvern"
+
+#: js/ui/status/location.js:173
+msgid "Location In Use"
+msgstr "Plassering i bruk"
+
+#: js/ui/status/location.js:177
+msgid "Location Disabled"
+msgstr "Plassering slått av"
+
+#: js/ui/status/location.js:178
+msgid "Enable"
+msgstr "Slå på"
+
+#: js/ui/status/location.js:350
+msgid "Allow location access"
+msgstr "Gi tilgang til stedsinformasjon"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:352
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Programmet %s ønsker tilgang til din plassering?"
+
+#: js/ui/status/location.js:362
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Tilgang til plassering kan endres når som helst fra innstillinger for "
+"personvern."
+
+#: js/ui/status/network.js:72
+msgid "<unknown>"
+msgstr "<ukjent>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:450 js/ui/status/network.js:1345
+#, javascript-format
+msgid "%s Off"
+msgstr "%s av"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:453
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s koblet til"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:458
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s håndteres ikke"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:461
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s kobler fra"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:468 js/ui/status/network.js:1337
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s kobler til"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:471
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s krever autentisering"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:479
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Fastvare mangler for %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:483
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s er ikke tilgjengelig"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:486
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s tilkobling mislyktes"
+
+#: js/ui/status/network.js:498
+msgid "Wired Settings"
+msgstr "Innstillinger for trådbundet nettverk"
+
+#: js/ui/status/network.js:541
+msgid "Mobile Broadband Settings"
+msgstr "Innstillinger for mobilt bredbånd"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:587 js/ui/status/network.js:1342
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s maskinvare slått av"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:591
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s slått av"
+
+#: js/ui/status/network.js:632
+msgid "Connect to Internet"
+msgstr "Koble til internett"
+
+#: js/ui/status/network.js:836
+msgid "Airplane Mode is On"
+msgstr "Flymodus er slått på"
+
+#: js/ui/status/network.js:837
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Trådløst blir slått av når flymodus slås på."
+
+#: js/ui/status/network.js:838
+msgid "Turn Off Airplane Mode"
+msgstr "Slå av flymodus"
+
+#: js/ui/status/network.js:847
+msgid "Wi-Fi is Off"
+msgstr "Trådløs er av"
+
+#: js/ui/status/network.js:848
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Trådløs må slås på for å koble til et nettverk."
+
+#: js/ui/status/network.js:849
+msgid "Turn On Wi-Fi"
+msgstr "Slå på trådløs"
+
+#: js/ui/status/network.js:874
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi nettverk"
+
+#: js/ui/status/network.js:876
+msgid "Select a network"
+msgstr "Velg et nettverk"
+
+#: js/ui/status/network.js:908
+msgid "No Networks"
+msgstr "Ingen nettverk"
+
+#: js/ui/status/network.js:929 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "Bruk maskinvarebryter til å slå av"
+
+#: js/ui/status/network.js:1206
+msgid "Select Network"
+msgstr "Velg nettverk"
+
+#: js/ui/status/network.js:1212
+msgid "Wi-Fi Settings"
+msgstr "Innstillinger"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1333
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s aksesspunkt aktivt"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1348
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s ikke koblet til"
+
+#: js/ui/status/network.js:1445
+msgid "connecting…"
+msgstr "kobler til …"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1448
+msgid "authentication required"
+msgstr "autentisering kreves"
+
+#: js/ui/status/network.js:1450
+msgid "connection failed"
+msgstr "tilkobling mislyktes"
+
+#: js/ui/status/network.js:1501
+msgid "VPN Settings"
+msgstr "Innstillinger for VPN"
+
+#: js/ui/status/network.js:1518
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1528
+msgid "VPN Off"
+msgstr "VPN av"
+
+#: js/ui/status/network.js:1589 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Innstillinger for nettverk"
+
+#: js/ui/status/network.js:1617
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s kablet tilkobling"
+msgstr[1] "%s kablede tilkoblinger"
+
+#: js/ui/status/network.js:1621
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s trådløs tilkobling"
+msgstr[1] "%s trådløse tilkoblinger"
+
+#: js/ui/status/network.js:1625
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s tilkobling med modem"
+msgstr[1] "%s tilkoblinger med modem"
+
+#: js/ui/status/network.js:1759
+msgid "Connection failed"
+msgstr "Tilkobling mislyktes"
+
+#: js/ui/status/network.js:1760
+msgid "Activation of network connection failed"
+msgstr "Aktivering av nettverkstilkobling mislyktes"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Nattlys slått av"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Nattlys slått på"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Gjenoppta"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Slå av til i morgen"
+
+#: js/ui/status/power.js:47
+msgid "Power Settings"
+msgstr "Innstillinger for strøm"
+
+#: js/ui/status/power.js:63
+msgid "Fully Charged"
+msgstr "Fullt oppladet"
+
+#: js/ui/status/power.js:69
+msgid "Not Charging"
+msgstr "Lader ikke"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:72 js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "Estimerer …"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d:%02d gjenstår (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d:%02d til batteriet er fullt (%d %%)"
+
+#: js/ui/status/power.js:139 js/ui/status/power.js:141
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:38
+msgid "Screen is Being Shared"
+msgstr "Skjermen er delt"
+
+#: js/ui/status/remoteAccess.js:40
+msgid "Turn off"
+msgstr "Slå av"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Flymodus er slått på"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "Lås"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "Slå av / logg ut"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "Hvilemodus"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "Start på nytt …"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "Slå av …"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Logg ut"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "Bytt bruker …"
+
+#: js/ui/status/thunderbolt.js:262
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Ukjent Thunderbolt-enhet"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr "En ny enhet er oppdaget mens du var borte. Vennligst koble fra og til enheten for å starte å bruke den."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr "Uautorisert Thunderbolt-enhet"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "En ny enhet er oppdaget og trenger autorisering av en administrator."
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Feil ved autorisering av Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Kunne ikke autorisere Thunderbolt-enhet: %s"
+
+#: js/ui/status/volume.js:160
+msgid "Volume changed"
+msgstr "Volum endret"
+
+#: js/ui/status/volume.js:222
+msgid "Volume"
+msgstr "Volum"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "Speil"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "Slå sammen skjermer"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "Kun ekstern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "Kun innebygget"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:371
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:377
+msgid "Swipe up to unlock"
+msgstr "Sveip for å låse opp"
+
+#: js/ui/unlockDialog.js:378
+msgid "Click or press a key to unlock"
+msgstr "Klikk eller trykk en tast for å låse opp"
+
+#: js/ui/unlockDialog.js:555
+msgid "Unlock Window"
+msgstr "Lås opp vindu"
+
+#: js/ui/unlockDialog.js:564
+msgid "Log in as another user"
+msgstr "Logg inn som en annen bruker"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Velkommen til GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Hvis du vil lære mer bør du sjekke ut omvisningen."
+
+#: js/ui/welcomeDialog.js:45
+msgid "No Thanks"
+msgstr "Nei takk"
+
+#: js/ui/welcomeDialog.js:50
+msgid "Take Tour"
+msgstr "Ta omvisningen"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» er klar"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:63
+msgid "Keep these display settings?"
+msgstr "Beholde disse skjerminnstillingene?"
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Forkast innstillinger"
+
+#: js/ui/windowManager.js:75
+msgid "Keep Changes"
+msgstr "Behold endringer"
+
+#: js/ui/windowManager.js:94
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Endringer i innstillingene forkastes om %d sekund"
+msgstr[1] "Endringer i innstillingene forkastes om %d sekunder"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:550
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr "Minimer"
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr "De-maksimer"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "Maksimer"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Flytt"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "Endre størrelse"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr "Flytt tittellinja på skjermen"
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "Alltid øverst"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr "Alltid på synlig arbeidsområde"
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr "Flytt til arbeidsområdet til venstre"
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr "Flytt til arbeidsområdet til høyre"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "Flytt til arbeidsområdet over"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "Flytt til arbeidsområdet under"
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr "Flytt til skjermen over"
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr "Flytt til skjermen under"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "Flytt til skjermen til venstre"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "Flytt til skjermen til høyre"
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Lukk"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution kalender"
+
+#: src/main.c:415 subprojects/extensions-tool/src/main.c:317
+msgid "Print version"
+msgstr "Vis versjon"
+
+#: src/main.c:421
+msgid "Mode used by GDM for login screen"
+msgstr "Modus som brukes av GDM for innloggingsskjermen"
+
+#: src/main.c:427
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Bruk spesifikt modus, f.eks. «gdm» for innloggingsskjerm"
+
+#: src/main.c:433
+msgid "List possible modes"
+msgstr "Vis mulige modi"
+
+#: src/shell-app.c:268
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ukjent"
+
+#: src/shell-app.c:519
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Klarte ikke å starte «%s»"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "Passordene er ikke like."
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "Passordet kan ikke være tomt"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Autentiseringsdialogen ble lukket av brukeren"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:217
+#: subprojects/extensions-app/data/ui/extensions-window.ui:56
+msgid "Extensions"
+msgstr "Utvidelser"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:218
+msgid "Manage your GNOME Extensions"
+msgstr "Håndter utvidelser for GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "GNOME prosjektet"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr "GNOME utvidelser håndterer oppdatering av utvidelser, konfigurasjon av utvidelsenes brukervalg og fjerning eller deaktivering av uønskede utvidelser."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Sett opp utvidelser for GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:142
+#: subprojects/extensions-app/js/main.js:152
+msgid "No Matches"
+msgstr "Ingen treff"
+
+#: subprojects/extensions-app/js/main.js:179
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Fjerne «%s»?"
+
+#: subprojects/extensions-app/js/main.js:180
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Hvis du fjerner utvidelsen må du laste den ned på nytt hvis du ønsker å "
+"aktivere den igjen."
+
+#: subprojects/extensions-app/js/main.js:184
+msgid "Remove"
+msgstr "Fjern"
+
+#: subprojects/extensions-app/js/main.js:216
+msgid "translator-credits"
+msgstr "Kjartan Maraas <kmaraas@gnome.org>"
+
+#: subprojects/extensions-app/js/main.js:344
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d utvidelse vil bli oppdatert ved neste pålogging."
+msgstr[1] "%d utvidelser vil bli oppdatert ved neste pålogging."
+
+#: subprojects/extensions-app/js/main.js:494
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Utvidelsen er ikke kompatibel med denne versjonen av GNOME"
+
+#: subprojects/extensions-app/js/main.js:497
+msgid "The extension had an error"
+msgstr "Utvidelsen hadde en feil"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:83
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Beskrivelse"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:104
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versjon"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:131
+msgid "Author"
+msgstr "Utviklet av"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:185
+msgid "Website"
+msgstr "Nettsted"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:201
+msgid "Remove…"
+msgstr "Fjern …"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:7
+msgid "Help"
+msgstr "Hjelp"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:11
+msgid "About Extensions"
+msgstr "Om utvidelser"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Du kan finne og legge til utvidelser på <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:34
+msgid "Warning"
+msgstr "Advarsel"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:44
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr "Utvidelser kan forårsake problemer med systemet, inkludert ytelsesproblemer. Hvis du møter på problemer med systemet anbefales det å deaktivere alle utvidelser."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:150
+msgid "Manually Installed"
+msgstr "Manuelt installert"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:174
+msgid "Built-In"
+msgstr "Innebygget"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:217
+msgid "No Installed Extensions"
+msgstr "Ingen installerte utvideleser"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:252
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Beklager men det var ikke mulig å hente listen over installerte utvidelser. "
+"Sjekk at du har logget inn i GNOME og prøv igjen."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:288
+msgid "Extension Updates Ready"
+msgstr "Oppdateringer for utvidelser er klare"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:303
+msgid "Log Out…"
+msgstr "Logg ut …"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:223
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Den nye utvidelsen ble opprettet i %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Navnet bør være meget en kort (ideellt beskrivende) streng.\n"
+"For eksempel: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Navn"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Beskrivelse er en setning som forklarer hva utvidelsen din gjør.\n"
+"For eksempel: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID er en globalt unik identifikator for din utvidelse.\n"
+"Dette bør være i format av en e-postadresse (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Velg en av de tilgjengelige malene:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Mal"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Unik identifikator for den nye utvidelsen"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAVN"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Brukersynlig navn på den nye utvidelsen"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "BESKRIVELSE"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "En kort beskrivelse av hva utvidelsen gjør"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MAL"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Mal som skal brukes for den nye utvidelsen"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Oppgi informasjon om utvidelsen interaktivt"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Lag en ny utvidelse"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Ukjente argumenter"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, navn og beskrivelse må oppgis"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Klarte ikke å koble til GNOME skallet\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Utvidelse «%s» eksisterer ikke\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Slå av en utvidelse"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:97
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Ingen UUID oppgitt"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:102
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Mer enn en UUID ble oppgitt"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Slå på en utvidelse"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Utvidelse «%s» eksisterer ikke\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Vis informasjon om utvidelse"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Overskriv en eksisterende utvidelse"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "UTVIDELSES_PAKKE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Installer en utvidelsespakke"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Ingen utvidelsespakke oppgitt"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Mer enn en utvidelsespakke oppgitt"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Vis utvidelser installert av bruker"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Vis utvidelser installert av systemet"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Vis utvidelser som er aktivert"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Vis utvidelser som er deaktivert"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Vis utvidelser med brukervalg"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Vis utvidelser med oppdateringer"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Vis detaljer om utvidelse"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Vis installerte utvideleser"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FIL"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Ekstra kilde som skal inkluderes i pakken"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Et GSettings-schema som skal inkluderes"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "KATALOG"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Katalogen hvor oversettelsene finnes"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMENE"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Gettext-domene som skal brukes for oversettelser"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Overskriv en eksisterende pakke"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Katalogen hvor pakken skal lages"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "KILDEKATALOG"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Lag en utvidelsespakke"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Mer enn en kildekatalog oppgitt"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Utvidelse «%s» har ikke brukervalg\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:79
+msgid "Opens extension preferences"
+msgstr "Åpner brukervalg for utvidelser"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Nullstill en utvidelse"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Kan ikke avinstallere systemutvidelser\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Klarte ikke å avinstallere «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Avinstaller en utvidelese"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ikke vis feilmelding"
+
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell"
+msgstr "Klarte ikke å koble til GNOME skallet"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Sti"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Opprinnelig utvikler"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Tilstand"
+
+#: subprojects/extensions-tool/src/main.c:290
+msgid "“version” takes no arguments"
+msgstr "«version» tar ingen argumenter"
+
+#: subprojects/extensions-tool/src/main.c:292
+#: subprojects/extensions-tool/src/main.c:312
+msgid "Usage:"
+msgstr "Bruk:"
+
+#: subprojects/extensions-tool/src/main.c:295
+msgid "Print version information and exit."
+msgstr "Skriv versjonsinformasjon og avslutt."
+
+#: subprojects/extensions-tool/src/main.c:310
+#: subprojects/extensions-tool/src/main.c:313
+msgid "COMMAND"
+msgstr "KOMMANDO"
+
+#: subprojects/extensions-tool/src/main.c:313
+msgid "[ARGS…]"
+msgstr "[ARG …]"
+
+#: subprojects/extensions-tool/src/main.c:315
+msgid "Commands:"
+msgstr "Kommandoer:"
+
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Print help"
+msgstr "Vis hjelp"
+
+#: subprojects/extensions-tool/src/main.c:318
+msgid "Enable extension"
+msgstr "Aktiver utvidelese"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Disable extension"
+msgstr "Deaktiver utvidelese"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Reset extension"
+msgstr "Nullstill utvidelese"
+
+#: subprojects/extensions-tool/src/main.c:321
+msgid "Uninstall extension"
+msgstr "Avinstaller utvidelse"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "List extensions"
+msgstr "Vis utvidelser"
+
+#: subprojects/extensions-tool/src/main.c:323
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Show extension info"
+msgstr "Vis informasjon om utvidelse"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Open extension preferences"
+msgstr "Åpne brukervalg for utvidelse"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "Create extension"
+msgstr "Lag utvidelse"
+
+#: subprojects/extensions-tool/src/main.c:327
+msgid "Package extension"
+msgstr "Pakk utvidelse"
+
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Install extension bundle"
+msgstr "Installer utvidelsespakke"
+
+#: subprojects/extensions-tool/src/main.c:330
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Bruk «%s» for å få detaljert hjelp.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Enkel"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Tom utvidelese"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Legg til et ikon på topplinjen"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u utgang"
+msgstr[1] "%u utganger"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u inngang"
+msgstr[1] "%u innganger"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Systemlyder"
diff --git a/po/ne.po b/po/ne.po
new file mode 100644
index 0000000..85702ae
--- /dev/null
+++ b/po/ne.po
@@ -0,0 +1,3407 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Nepali Translation Gnome 3.26 dev\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-17 18:27+0000\n"
+"PO-Revision-Date: 2022-09-19 10:55+0545\n"
+"Last-Translator: Pawan Chitrakar <chautari@gmail.com>\n"
+"Language-Team: Nepali Translation Team <chautari@gmail.com>\n"
+"Language: ne\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.0.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "सुरुआत"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "मनपर्ने अनुप्रयोग १ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "मनपर्ने अनुप्रयोग २ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "मनपर्ने अनुप्रयोग ३ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "मनपर्ने अनुप्रयोग ४ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "मनपर्ने अनुप्रयोग ५ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "मनपर्ने अनुप्रयोग ६ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "मनपर्ने अनुप्रयोग ७ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "मनपर्ने अनुप्रयोग ८ सक्रिय पार्नुहोस्"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "मनपर्ने अनुप्रयोग ९ सक्रिय पार्नुहोस्"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "स्क्रिनसट"
+
+#: data/50-gnome-shell-screenshots.xml:9 data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "अन्तरक्रियात्मक रूपमा स्क्रिनसट लिनुहोस्"
+
+#: data/50-gnome-shell-screenshots.xml:12 data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "स्क्रीनको तस्बिर लीने"
+
+#: data/50-gnome-shell-screenshots.xml:15 data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "विन्डोको तस्बिर लीने"
+
+#: data/50-gnome-shell-screenshots.xml:18 data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "अन्तरक्रियात्मक रूपमा स्क्रिनकास्ट रेकर्ड गर्नुहोस्"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "प्रणाली"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "सूचना सुची देखाउनुहोस्"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "सक्रिय सुचना फोकस"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "अधिलेखन हेर्नुहोस्"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "सबै अनुप्रयोगहरू देखाउनुहोस्"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "अनुप्रयोग मेनु खोल्नुहोस्"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "जिनोम शेल"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "सञ्झ्याल प्रबन्ध र अनुप्रयोग सुरुआत "
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Alt-F2 बाट विकासकर्ता र परीक्षकका लागि उपयोगी आन्तरिक उपकरण सक्षम पार्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid "Allows access to internal debugging and monitoring tools using the Alt-F2 dialog."
+msgstr "Alt-F2 संवाद प्रयोग गरेर आन्तरिक त्रुटि सच्याउने र अनुगमन गर्ने उपकरणमा पहुँच अनुमति दिन्छ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "सक्षम पार्नका लागि विस्तारको UUIDs"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which should be loaded. "
+"Any extension that wants to be loaded needs to be in this list. You can also manipulate this list "
+"with the EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"जिनोम शेल विस्तारसँग UUID गुण छ; यो कुञ्जीले लोड गर्नुपर्ने विस्तारहरू सूचीबद्ध गर्दछ । लोड हुन चाहने कुनै पनि विस्तार "
+"यो सूचीमा हुनु आवश्यक छ । तपाईँले org.gnome.Shell मा EnableExtension र DisableExtension D-Bus विधिहरूसँग "
+"पनि यो सूची मिलाउन सक्नुहुन्छ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "अक्षम पार्नका लागि विस्तारको UUIDs"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which should be disabled, "
+"even if loaded as part of the current mode. You can also manipulate this list with the "
+"EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell. This key takes precedence "
+"over the “enabled-extensions” setting."
+msgstr ""
+"जिनोम शेल विस्तारसँग UUID गुण छ; यो कुञ्जीले हालको मोडको भागको रूपमा लोड गरिएको भएता पनि, अक्षम गरिनुपर्ने "
+"विस्तारहरू सूचीबद्ध गर्दछ । तपाईँले org.gnome.Shell मा EnableExtension र DisableExtension D-Bus विधिहरूसँग "
+"पनि यो सूची मिलाउन सक्नुहुन्छ । यो कुञ्जीले \"सक्षम-विस्तार\" सेटिङ भन्दा प्राथमिकता लिन्छ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "सेल विस्तारहरू असक्षम पार्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-extension” setting."
+msgstr "\"सक्षम-विस्तार\" सेटिङलाई प्रभाव नपारिकन प्रयोगकर्ताले सक्षम पारेको सबै विस्तार अक्षम पार्नुहोस् ।"
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "विस्तार संस्करण अनुकूलताको वैधता अक्षम पार्दछ"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current running version. Enabling "
+"this option will disable this check and try to load all extensions regardless of the versions "
+"they claim to support."
+msgstr ""
+"जिनोम शेलले हालको चलिरहेको संस्करणलाई समर्थन गर्ने दावा विस्तार मात्र लोड गर्नेछ । यो विकल्प सक्षम पार्दा यो जाँच "
+"अक्षम पारिनेछ र तिनीहरूले समर्थन गरेको दावी गरे तापनि सबै विस्तारहरू लोड गर्ने प्रयास गर्नुहोस् ।"
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "अनुकूल अनुप्रयोगका लागि डेस्कटप फाइल IDको सूची"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid "The applications corresponding to these identifiers will be displayed in the favorites area."
+msgstr "यी पहिचायकसँग सङ्गत अनुप्रयोगहरू मनपर्ने क्षेत्रमा प्रदर्शित हुनेछन् ।"
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "आदेश (Alt-F2) संवादका लागि इतिहास"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "देखिने सिसा संवादका लागि इतिहास"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "प्रयोगकर्ता मेनुमा जहिले पनि \"लगआउट\" मेनु वस्तु देखाउनुहोस् ।"
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-user, single-session "
+"situations."
+msgstr "यो कुञ्जीले एकल-प्रयोगकर्ता, एकल सत्र परिस्थितिहरूमा \"लग आउट\" मेनु वस्तुको स्वचालित लुकेर अधिरोहण गर्दछ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid "Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "गुप्तिकरण गरिएको वा टाढाको फाइल प्रणाली माउन्ट गर्नका लागि पासवर्ड सम्झनुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote filesystem is mounted. If "
+"the password can be saved for future use a “Remember Password” checkbox will be present. This key "
+"sets the default state of the checkbox."
+msgstr ""
+"गुप्तिकृत यन्त्र वा टाढाको फाइल प्रणाली माउन्ट हुँदा शेलले पासवर्ड अनुरोध गर्नेछ । यदि भविष्यका लागि पासवर्ड बचत "
+"गर्न सकिन्छ भने \"पासवर्ड सम्झनुहोस्\" जाँच बाकस उपस्थित हुनेछ । यस कुञ्जीले जाँच बाकसको पूर्वनिर्धारित स्थिति सेट "
+"गर्दछ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "अन्तिम चयन गरिएको गैर-पूर्वनिर्धारित पावर प्रोफाइल"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support toggling between two "
+"profiles, this key records the last selected non-default profile."
+msgstr ""
+"केही प्रणालीहरूले दुई भन्दा बढी पावर प्रोफाइलहरू समर्थन गर्दछ। अझै पनि दुई प्रोफाइलहरू बीच टगलिङ समर्थन गर्न, यो "
+"कुञ्जीले अन्तिम चयन गरिएको गैर-पूर्वनिर्धारित प्रोफाइल रेकर्ड गर्दछ।"
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "\"जिनोममा स्वागत छ\" संवादको अन्तिम संस्करण यसका लागि देखाइएको थियो"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last shown. An empty "
+"string represents the oldest possible version, and a huge number will represent versions that do "
+"not exist yet. This huge number can be used to effectively disable the dialog."
+msgstr ""
+"यस कुञ्जीले पछिल्लो पटक देखाइएको \"जिनोममा स्वागत छ\" संवाद कुन संस्करणका लागि निर्धारण गर्दछ । एउटा खाली "
+"स्ट्रिङले सबैभन्दा पुरानो सम्भावित संस्करणलाई प्रतिनिधित्व गर्दछ, र ठूलो सङ्ख्याले संस्करणहरूको प्रतिनिधित्व गर्दछ जुन "
+"अहिलेसम्म अवस्थित छैन । यो ठूलो सङ्ख्यालाई प्रभावकारी तरिकाले संवाद अक्षम पार्न प्रयोग गर्न सकिन्छ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "अनुप्रयोग पिकरको सजावट"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are stored in the order they "
+"appear in GNOME Shell. Each page contains an “application id” → 'data' pair. Currently, the "
+"following values are stored as 'data': • “position”: the position of the application icon in the "
+"page"
+msgstr ""
+"अनुप्रयोग पिकरको सजावट । एरेमा प्रत्येक प्रविष्टि एउटा पृष्ठ हो । पृष्ठहरू जिनोम शेलमा देखिने क्रममा भण्डारण गरिएका "
+"छन् । प्रत्येक पृष्ठले 'डाटा' जोडी→ \"अनुप्रयोग आईडी\" समाविष्ट गर्दछ । हाल, निम्न मानहरू 'डाटा' को रूपमा "
+"भण्डारण गरिएका छन्: • \"स्थिति\": पृष्ठमा अनुप्रयोग प्रतिमाको स्थिति"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "मेनुबारलाई सक्रिय गर्न किबाइन्डिङ्ग"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "मेनुबारलाई सक्रिय गर्न किबाइन्डिङ्ग।"
+
+#: data/org.gnome.shell.gschema.xml.in:155 data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "सदृष्य स्थिति बीचमा शिफ्ट गर्न कुञ्जी बाइन्डिङ"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "सत्र, सञ्झ्याल पिकर र अनुप्रयोग ग्रिड बीचमा शिफ्ट गर्न कुञ्जी बाइन्डिङ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "अनुप्रयोग ग्रिड, सञ्झ्याल पिकर र सत्र बीचमा शिफ्ट गर्न कुञ्जी बाइन्डिङ"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "\"अनुप्रयोग देखाउनुहोस्\" दृश्य खोल्नका लागि कुञ्जी बाइन्डिङ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "क्रियाकलाप अवलोकनको \"अनुप्रयोग देखाउनुहोस्\" सदृश्य खोल्नका लागि कुञ्जी बाइन्डिङ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "सदृश्य खुल्नका लागि कुञ्जी बाइन्डिङ"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "क्रियाकलापको सदृश्य खोल्नका लागि कुञ्जी बाइन्डिङ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "सूचना सूचीको दृश्यात्मकता टगल गर्ने कुञ्जी बाइन्डिङ"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "सूचना सूचीको दृश्यात्मकता टगल गर्ने कुञ्जी बाइन्डिङ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "सक्रिय सूचना केन्द्रित गर्न कुञ्जी बाइन्डिङ"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "सक्रिय सूचना केन्द्रित गर्न कुञ्जी बाइन्डिङ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "अनुप्रयोग १ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "अनुप्रयोग २ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "अनुप्रयोग ३ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "अनुप्रयोग ४ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "अनुप्रयोग ५ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "अनुप्रयोग ६ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "अनुप्रयोग ७ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "अनुप्रयोग ८ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "अनुप्रयोग ९ स्विच गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:255 data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "हालको कार्यस्थानमा स्विचर सिमित गर्नुहोस् ।"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are shown in the switcher. "
+"Otherwise, all applications are included."
+msgstr ""
+"ठीक भएमा, हालको कार्यस्थानमा सञ्झ्याल भएका अनुप्रयोगहरू मात्र स्विचकर्तामा देखिन्छन् । अन्यथा, सबै अनुप्रयोगहरू "
+"समावेश गरिएका छन् ।"
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "अनुप्रयोग प्रतिमा मात्रै."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities are “thumbnail-"
+"only” (shows a thumbnail of the window), “app-icon-only” (shows only the application icon) or "
+"“both”."
+msgstr ""
+"स्विचरमा सञ्झ्याल देखाउने तरिका कन्फिगर गर्दछ । वैध सम्भावनाहरू \"थम्बनेल मात्र\" (सञ्झ्यालको थम्बनेल देखाउँदछ), "
+"\"अनुप्रयोग प्रतिमा-मात्र\" (अनुप्रयोग प्रतिमा मात्र देखाउँदछ) वा \"दुबै\" हुन्।"
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. Otherwise, all "
+"windows are included."
+msgstr ""
+"ठीक भएमा, हालको कार्यस्थानबाट सञ्झ्याल मात्र स्विचकर्तामा देखाइन्छ । अन्यथा, सबै सञ्झ्यालहरू समावेश गरिएका छन् ।"
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "स्थानहरू"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "विश्व घडिमा प्रदर्शन हुने स्थानहरु"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "स्वत: स्थान"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "हालको स्थान ल्याउने वा नल्याउने"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "स्थान"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "मौसम पूर्वानुमान देखाउनका लागि स्थान"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "प्रमूल सञ्झ्यालमा मोडल संवाद संलग्न गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:326 data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343 data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid "This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "यो कुञ्जीले जिनोम शेल चलाउदा org.gnome.mutter मा कुञ्जी अधिरोहण गर्दछ ।"
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "स्क्रिन किनाराहरूमा सञ्झ्याल छोड्दा किनारा टायल सक्षम गर्नुहोस्"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "कार्यस्थान गतिशील रूपमा व्यवस्थापन गरिन्छ"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "प्राथमिक मोनिटरमा मात्र कार्यस्थान"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "सूचक ले चालन नरोकेसम्म माउस मोडमा फोकस परिवर्तन विलम्ब गर्नुहोस्"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "सञ्जाल लगईन"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "केही गलत भयो"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this extension can’t be displayed. "
+"We recommend that you report the issue to the extension authors."
+msgstr ""
+"हामीलाई धेरै दुःख लाग्छ, तर त्यहाँ एउटा समस्या भएको छ: यो विस्तारका लागि सेटिङ हरू प्रदर्शन गर्न सकिँदैन। हामी "
+"तपाईँलाई यो विषय विस्तार लेखकलाई प्रतिवेदन गर्न सुझाव दिन्छौं ।"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "प्राविधिक विवरण"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "गृहपृष्ठ"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "विस्तार गृहपृष्ठ हेर्नुहोस्"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61 js/ui/components/networkAgent.js:109
+#: js/ui/components/polkitAgent.js:141 js/ui/endSessionDialog.js:440
+#: js/ui/extensionDownloader.js:223 js/ui/shellMountOperation.js:377
+#: js/ui/shellMountOperation.js:387 subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "_रद्द"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "पासवर्ड"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "सेसन छान।नुहोस्"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "सूचीमा नभएको ?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(जस्तै, प्रयोगकर्ता वा %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "प्रयोगकर्ता नाम"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "सञ्झ्याल लगइन गर्नुहोस्"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "प्रमाणीकरण त्रुति"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(वा पढ्नेमा औंला स्वाइप गर्नुहोस्)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(वा पढ्नेमा औँला राख्नुहोस्)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "पावर बन्द"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "पावर बन्द;बन्द गर्नुहोस्;रोक्ने;रोक्नुहोस्"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "पुन: सुरु गर्नुहोस्"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "पुन: बुट;फेरि सुरु गर्नुहोस्;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "पर्दा ताल्चा लगाउनुहोस्"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "पर्दा ताला लगाउ"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "लगआउट"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "लगआउट;लग आउट;साइन अफ"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "निश्क्रिय गर्ने"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "निलम्बन गर्नुहोस्;निदाउनुहोस्"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "स्विच प्रयोगकर्ता"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "स्विच प्रयोगकर्ता"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "अभिमुखिकरण ताल्चा लगाउनुहोस्;अभिमुखीकरण अनलक गर्नुहोस्;पर्दा;परिक्रमण"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "स्क्रिनसट लिनुहोस्"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "स्क्रिनसट;स्क्रिनकास्ट;स्निप;क्याप्चर;रेकर्ड"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "पर्दा परिक्रमण ताल्चा बाट हटाउनुहोस्"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "पर्दा परिक्रमण ताल्चा लगाउनुहोस्"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "आदेश फेला परेन ।"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "आदेश पद वर्णन गर्न सकेन:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "\"%s\" कार्यान्वयन असफल:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "अहिले"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d मिनेट पहिला"
+msgstr[1] "%d मिनेट पहिला"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d घण्टा पहिला"
+msgstr[1] "%d घण्टा पहिला"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "हिजो"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d दिन पहिला"
+msgstr[1] "%d दिन पहिला"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d हप्ता पहिला"
+msgstr[1] "%d हप्ता अगाडि"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d महिना पहिला"
+msgstr[1] "%d महिना अघि"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d वर्ष अगाडि"
+msgstr[1] "%d वर्ष अघि"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "हिजो %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "हिजो %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "हटस्पट लगईन"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other information you enter on "
+"this page can be viewed by people nearby."
+msgstr ""
+"यो हटस्पट लगइनमा तपाईँको जडान सुरक्षित छैन । यो पृष्ठमा तपाईँले प्रविष्ट गरेको पासवर्ड वा अन्य जानकारी नजिकका "
+"मानिसहरूले हेर्न सक्छन् ।"
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "अस्वीकार गर्नुहोस्"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "अनुमति दिनुहोस्"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "बेनामी फोलडर"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s ड्यासमा पिन गरिएको छ ।"
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s ड्यासबाट अनपिन गरिएको छ ।"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "सञ्झ्याल खोल्नुहोस्"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "नयाँ सञ्झ्याल"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "अरू धेरै विवरण देखाउनुहोस्"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "अन्त्य गर्नुहोस्"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "अनपिन"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "ड्यासमा पिन गर्नुहोस्"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "एकीकृत ग्राफिक्स कार्ड प्रयोग गरेर सुरु गर्नुहोस्"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "डिस्क्रेट ग्राफिक्स कार्ड प्रयोग गरेर सुरु गर्नुहोस्"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "ध्वनी यन्त्र छन्नुहोस्"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "ध्वोनिका सेटिङ्स"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "हेडफोनहरू"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "हेडसेट"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "माईक्रोफोन"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "पृष्ठभूमि परिवर्तन गर्नुहोस्…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "अनुकूलता देखाउ"
+
+#: js/ui/backgroundMenu.js:17 subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "सेटिङ"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "०६"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "पहिलो महिना"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "अर्को महिना"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V हप्ता"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "सूचनाहरू छैन"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "डिस्टर्ब नगर्नुहोस्"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "खाली गर्नुहोस्"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "\"%s\" ले प्रतिक्रिया दिइरहेको छैन ।"
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the application to quit entirely."
+msgstr ""
+"तपाईँले अनुप्रयोगलाई जारी राख्न केही समय प्रतिक्षा गर्नुहोस् रोज्न वा यसलाई पूर्ण रूपमा अन्त्य गर्न दवाब दिन सक्नुहुन्छ "
+"।"
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "_अन्त्य गर्न दवाब दिनुहोस्"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "_प्रतिक्षा गर्नुहोस्"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "बाहिरि ड्राइभ जडान भयो"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "बाहिरि ड्राइभ विच्छेद भयो"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "भोल्युम अनलक गर्न असक्षम"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "स्थापना गरिएको udisks संस्करणले PIM सेटिङलाई समर्थन गर्दैन"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s सँग खोल्नुहोस्"
+
+#: js/ui/components/networkAgent.js:91
+msgid "Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "वैकल्पिक रूपमा तपाईँले आफ्नो राउटरमा \"WPS\" बटन थिचेर जडान गर्न सक्नुहुन्छ ।"
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "जडान गर्नुहोस्"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "कुञ्जी"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "निजी कुञ्जी पासवर्ड"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "परिचय"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "सेवा"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "प्रमाणीकरण आवश्यक"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid "Passwords or encryption keys are required to access the wireless network “%s”."
+msgstr "तार रहित सञ्जाल \"%s\" पहुँच गर्न पासवर्ड वा गुप्तिकरण कुञ्जी आवश्यक हुन्छ ।"
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Wired 802.1X प्रमाणीकरण"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "सञ्जाल नाम"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL प्रमाणीकरण"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "पिन कोड आवश्यक"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "मोबाइल ब्रडब्यान्ड यन्त्रका लागि PIN कोड आवश्यक छ"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” जडान गर्न पासवर्ड चाहिने छ ।"
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "सञ्जाल प्रबन्धक"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "भीपीएन पासवर्ड"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "प्रमाणीकरण आवश्यक"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "प्रशासक"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "प्रमाणीकरण"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "त्यो कार्य भएन : पुन प्रयास गर्नुस।."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s अब %s भएको छ"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "सञ्झ्यालहरू"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "अनुप्रयोग देखाउनुहोस्"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "ड्यास"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "आज"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "भोलि"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "दिनभर"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "घटनाहरू छैन"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "विश्व घडि थप्नुहोस्…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "विश्व घडि"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "लोड गर्दैछ…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "मौसम जानकारिको लागि अनलाइन जानुहोस्"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "मौसम जानकारि अहिले उपलब्ध छैन"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "मौसम"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "मौसम स्थान चयन गर्नुहोस्…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "लगआउट %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "लगआउट"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s स्वचालित रूपमा %d सेकेन्डमा लगआउट हुनेछ ।"
+msgstr[1] "%s स्वचालित रूपमा %d सेकेन्डमा लगआउट हुनेछ ।"
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "तपाईँ स्वचालित रूपमा %d सेकेन्डमा लगआउट हुनुहुनेछ ।"
+msgstr[1] "तपाईँ स्वचालित रूपमा %d सेकेन्डमा लगआउट हुनुहुनेछ ।"
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "लगआउट"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "पावर बन्द"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "अपडेट स्थापना पछि पावर बन्द गर्ने"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "%d सेकेन्डमा प्रणाली स्वचालित रूपमा बन्द हुनेछ ।"
+msgstr[1] "%d सेकेन्डमा प्रणाली स्वचालित रूपमा बन्द हुनेछ ।"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "बाकि अनुप्रयोग स्थापना गर्नुहोस्"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "पावर बन्द"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "फेरि सुरू गर्नुहोस्"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "अद्यावधिक स्थापना र पुन शुरु गर्नुहोस्"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "प्रणाली %d सेकेन्डमा स्वचालित रूपमा पुन: सुरुआत हुनेछ ।"
+msgstr[1] "प्रणाली %d सेकेन्डमा स्वचालित रूपमा पुन: सुरुआत हुनेछ ।"
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "फेरि सुरू गर्नुहोस्"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "पुन शुरु र अद्यावधिक स्थापना गर्नुहोस्"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural "The system will automatically restart and install updates in %d seconds."
+msgstr[0] "प्रणालीले स्वचालित रूपमा %d सेकेन्डमा अद्यावधिकहरू पुन: सुरुआत र स्थापना गर्नेछ ।"
+msgstr[1] "प्रणालीले स्वचालित रूपमा %d सेकेन्डमा अद्यावधिकहरू पुन: सुरुआत र स्थापना गर्नेछ ।"
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "पुन शुरु र स्थापना गर्नुहोस्"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "स्थापना पछि पावर बन्द गर्ने"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "अपडेट स्थापना पछि पावर बन्द गर्ने"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "पुन शुरु रस्तरवृद्धि गर्नुहोस्"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you "
+"have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s पुन: सुरुआत पछि स्थापना हुनेछ । स्तरवृद्धि स्थापना गर्न धेरै समय लाग्न सक्छ: तपाईँले जगेडा गर्नुभएको छ र कम्प्युटर "
+"प्लगइन गरिएको छ भन्ने निश्चित गर्नुहोस् ।"
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "न्यून ब्याट्री पावर: कृपया अद्यावधिक हरू स्थापना गर्नु भन्दा पहिले पावर जडान गर्नुहोस् ।"
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "केही अनुप्रयोग व्यस्त छन् वा बचत नगरिएका कार्य छन्"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "अन्य प्रयोगकर्ता लगईन छ"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "बुट विकल्पहरू"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s टाढा"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (कन्सोल)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "स्थापना गर्नुहोस्"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "विस्तार स्थापना गर्नुहोस्"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.orgबाट \"%s\" डाउनलोड र स्थापना गर्नुहुन्छ ?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "विस्तार अद्यावधिक उपलब्ध छ"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "विस्तार अद्यावधिकहरू स्थापना गर्न तयार छन् ।"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "अवरोध सर्टकट अनुमति दिनुहोस्"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "अनुप्रयोग %s ले सर्टकटहरू निषेध गर्न चाहन्छ"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "एउटा अनुप्रयोगले सर्टकट हरू निषेध गर्न चाहन्छ"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "तपाईँले %s थिचेर सर्टकटहरू पूर्वावस्थामा ल्याउन सक्नुहुन्छ ।"
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "अस्वीकार गर्नुहोस्"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "अनुमति दिनुहोस्"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "ढिलो कुञ्जी सुचारु छ"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "ढिलो कुञ्जी बन्द छ"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the Slow Keys feature, "
+"which affects the way your keyboard works."
+msgstr ""
+"तपाईँले भर्खरै ८ सेकेन्डका लागि शिफ्ट कुञ्जी तल सार्नु भएको छ । यो सुस्त कुञ्जी विशेषताका लागि सर्टकट हो, जसले "
+"तपाईँको कुञ्जीपाटीले कार्य गर्ने तरिकालाई असर गर्दछ ।"
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "टाँसिने कुञ्जि सुचारू छ"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "टाँसिने कुञ्जिहरु बन्द छ"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for the Sticky Keys "
+"feature, which affects the way your keyboard works."
+msgstr ""
+"तपाईँले पङ्क्तिमा ५ पटक Shift कुञ्जी थिच्नुहोस् । यो स्टिकी कुञ्जी सुविधाको लागि सर्टकट हो, जसले तपाईँको "
+"कुञ्जीपाटीले काम गर्ने तरिकालाई असर गर्दछ ।"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a row. This turns off the "
+"Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"तपाईँले एकैपटकमा दुईवटा कुञ्जी थिच्नुहुन्छ, वा पङ्क्तिमा Shift कुञ्जी ५ पटक थिच्नुहुन्छ । यसले टाँसिने कुञ्जी विशेषता बन्द "
+"गर्दछ, जसले तपाईँको कुञ्जीपाटीले कार्य गर्ने तरिकालाई असर गर्दछ ।"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "छोड्नुहोस्"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "सुरु गर्नुहोस"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "बन्द गर्नुहोस्"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "छोड्नुहोस्"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "क्षेत्र र भाषा सेटिङ"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "विस्तार स्थापना गरिएको छैन"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ले कुनै त्रुटिहरू दिएको छैन ।"
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "त्रुटि लुकाउनुहोस्"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "त्रुटि देखाउनुहोस्"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "सक्षम पारिएको"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "अक्षम पारिएको"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "त्रुटि:"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "मिति भन्दा बाहिर छ"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "डाउनलोड हुँदैछ"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "स्रोत हेर्नुहोस्"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "वेब पृष्ठ"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "प्रणालीलाई असुरक्षित शैलीमा राखियो"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "अनुप्रयोगहरू अब असीमित पहुँच छ"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "पूर्वस्थितिमा फर्काउनुहोस्"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "विशेषाधिकार भएको प्रयोगकर्ताको रूपमा लगइन गरियो"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security reasons. If possible, you "
+"should log in as a normal user."
+msgstr ""
+"विशेषाधिकार पाएको प्रयोगकर्ताको रूपमा सत्र चलाउन सुरक्षाका कारणले रोक्नुपर्छ । यदि सम्भव भएमा, तपाईँले सामान्य "
+"प्रयोगकर्ताको रूपमा लगइन गर्नुपर्दछ ।."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "पर्दामा ताल्चा अक्षम गरिएको छ"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "पर्दा ताल्चा लगाउनेलाई जिनोम प्रदर्शन प्रबन्धक को आवश्यकता पर्दछ ।."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "प्रणाली जानकारी"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "अज्ञात कलाकार"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "अज्ञात शीर्षक"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "खोजी गर्न टाईप गर्नुहोस्"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "अनुप्रयोग"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "अधिलेखन गर्नुहोस्"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "नयाँ शर्टकर्ट…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "अनुप्रयोग परिभाषित छ"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "अन-स्क्रिन सहयोग हेर्नुहोस्"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "निगरानि बदल्नुहोस्"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "कुञ्जी स्ट्रोक मानाङ्कन गर्नुहोस्"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "गरियो"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "सम्पादन…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "None"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "कन्फिगर गर्न बटन थिच्नुहोस्"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "निस्कन ESC थिच्नुहोस्"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "निस्कनको लागि कुनै कुञ्जी थिच्नुहोस्"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "गतिविधिहरू"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "प्रणाली"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "माथिल्लो पट्टी"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "एउटा आदेश चलाउनुहोस्"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "बन्द गर्न ESC थिच्नुहोस्"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "वेल्यान्डमा पुन: सुरुआत गर्न सकिँदैन"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "पुन: सुरुआत गर्दै…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "प्रयोगकर्तालाई उसको पर्दामा ताल्चा लगाउन पर्‌यो"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "ताल्चा मार्न असक्षम"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "अनुप्रयोगद्वारा ताल्चा रोक्का छ"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "चयन"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "क्षेत्र चयन"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "पर्दा"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "पर्दा चयन"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "सन्झ्याल"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "सञ्झ्याल चयन"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "स्क्रिनसट / स्क्रिनकास्ट"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "सूचक देखाउनुहोस्"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "स्क्रिनकास्ट"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "%d %t.webm बाट स्क्रिनकास्ट"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "स्क्रिनसट"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "स्क्रिनकास्ट रेकर्ड गरियो"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "भिडियो हेर्न यहाँ क्लिक गर्नुहोस् ।"
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "फाइलना देखाउनुहोस्"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "%s बाट स्क्रिनसट"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "स्क्रिनसट क्याप्चर गरियो"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "तपाईँले क्लिपबोर्डबाट छवि टाँस्न सक्नुहुन्छ ।"
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "स्क्रिनको तस्बिर लिइयो"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "खोजी गर्दै…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "नतिजा छैन ।"
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d अरू"
+msgstr[1] "%d अरूहरु"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "खोजी गर्नुहोस्"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "प्रतिलिपि बनाउनुहोस्"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "टाँस्नुहोस्"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "पाठ देखाऊ"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "प्रविष्टि पाठ लुकाउनुहोस्"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "क्यप्स लक अन छ"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "लुकेको भोल्युम"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "सञ्झ्याल प्रणाली भोल्युम"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "कुञ्जी फाइल प्रयोग गर्नुहोस्"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid "To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "कुञ्जीफाइल प्रयोग गर्ने भोल्युम अनलक गर्न, सट्टामा <i>%s</i> युटिलिटी प्रयोग गर्नुहोस् ।"
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM नम्बर"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "पासवर्ड सम्झनुहोस्"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "ताल्चा खोल्नुहोस्"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s खोल्नुहोस्"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM सङ्ख्या वा खाली हुनुपर्दछ ।"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "सुरु गर्न अक्षम: “%s”"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s अनुप्रयोग फेला पार्न सकेन"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "पहुँचता"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "उच्च व्यतिरेक"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "जूम गर्नुहोस्"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "दृष्टि वाचक"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "कुञ्जिपाटी "
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "दृश्यात्मक सावधानि"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "टाँसिने कुञ्जि"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "सुस्त कुञ्जीहरू"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "बाउन्स कुञ्जीहरू"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "माउस कुञ्जीहरू"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "ठूलो पाठ शैली"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "स्वतः घुमाउनुहोस्"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "ब्लुटुठ"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "चम्लिकोपना"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "गाढा शैली"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "एकपटकमात्र क्लिक"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "डबल क्लिक"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "घिसार्नु"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "दोस्रो क्लिक"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "ड्वेल क्लिक गर्नुहोस्"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "कुञ्जीपाटी"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "किबोर्ड लेआउट देखाउनुहोस्"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "स्थान पहुँच अनुमति दिनुहोस्"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "अनुप्रयोग %s ले तपाईँको स्थान पहुँच गर्न चाहन्छ"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "गोपनीयता सेटिङबाट कुनै पनि समयमा स्थान पहुँच परिवर्तन गर्न सकिन्छ ।"
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<अज्ञात>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "%s विच्छेदन गर्नुहोस्"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "%s मा जडान गर्नुहोस्"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s हटस्पट"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "रातको उज्यालो"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "कार्यसम्पादन"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "संतुलित पावर"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "पावर सेभर"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "पावर प्रोफाइल"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "स्क्रिनकास्ट रोक्नुहोस्"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "प्रदर्शनपर्दा साझेदारी रोक्नुहोस्"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "हवाइजहाज मोड"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "स्क्रिनसट लिनुहोस्"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "पावर बन्द मेनु"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "निश्क्रिय गर्ने"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "पु:न सुरु गर्ने…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "पावर बन्द…"
+
+#: js/ui/status/system.js:186 subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "लगआउट…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "प्रयोगकर्ता स्विच गर्नुहोस्…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "पर्दा ताल्चा लगाउनुहोस्"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "थन्डरबोल्ट"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "अज्ञात थन्डरबोल्ट यन्त्र"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and reconnect the device to "
+"start using it."
+msgstr ""
+"तपाईँ टाढा हुँदा नयाँ यन्त्र पत्ता लागेको छ । कृपया यसलाई प्रयोग गर्न सुरु गर्न यन्त्र विच्छेदन गर्नुहोस् र पुन: जडान "
+"गर्नुहोस् ।"
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "अनधिकृत थन्डरबोल्ट यन्त्र"
+
+#: js/ui/status/thunderbolt.js:322
+msgid "New device has been detected and needs to be authorized by an administrator."
+msgstr "नयाँ यन्त्र पत्ता लागेको छ र प्रशासकद्वारा प्रमाणीकरण गरिनु आवश्यक छ ।"
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "थन्डरबोल्ट प्रमाणीकरण त्रुटि"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "थन्डरबोल्ट यन्त्र अधिकार दिन सकेन: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "भोल्युम परिवर्तन गरियो"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "भोल्युम"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "ध्वनि निर्गत"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "ध्वनि आगत"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "दर्पण"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "प्रदर्शन जडान गर्नुहोस्"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "बाह्य मात्र"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "अवस्थित मात्र"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "ताल्चा खोल्नका लागि स्वाइप गर्नुहोस्"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "ताल्चा खोल्न कुञ्जी थिच्नुहोस् वा क्लिक गर्नुहोस्"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "ताल्चा हटाउनुहोस्"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "अर्को प्रयोगकर्ताको रूपमा लगइन गर्नुहोस्"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "जिनोम %s मा स्वागत छ"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "यदि तपाईं आफैं सिक्न चाहनुहुन्छ भने भ्रमण हेर्नुहोस्।"
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "होइन धन्यवाद"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "भ्रमण गर्नुहोस्"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” तयार छ"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "यी प्रदर्शन सेटिङहरू राख्नचाहनुहुन्छ?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "सेटिङ्ग उल्टाउनुहोस्"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "परिवर्तनहरू राख्नुहोस्"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "%d सेकेन्डमा सेटिङ परिवर्तन पुर्वास्थामा फर्किन्छ"
+msgstr[1] "%d सेकेन्डमा सेटिङ परिवर्तन पुर्वास्थामा फर्किन्छ"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "लुकाउनुहोस्"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "पूर्वावस्थामा ल्याउनुहोस्"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "ठूलो बनाउनुहोस्"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "सार्नुहोस्"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "पुन:आकार"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "शीर्षकपट्टीलाई पर्दामा सार्नुहोस्"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "सधै माथि"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "सधैँ दृश्यात्मक कार्यस्थानमा"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "कार्यक्षेत्र बायाँ सार्नुहोस्"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "कार्यक्षेत्र दायाँ सार्नुहोस्"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "कार्यस्थानलाई माथि सार्नुहोस्"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "कार्यस्थानलाई तल सार्नुहोस्"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "माथिको मोनिटरमा सार्नुहोस्"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "तलको मोनिटरमा सार्नुहोस्"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "ट्याब बायाँतिर सार्नुहोस्"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "दायाँ मोनिटरमा सार्नुहोस्"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "बन्द गर्नुहोस्"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "इभोल्युसन पात्रो"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "मुद्रण संस्करण"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "लगइन पर्दाका लागि GDM द्वारा प्रयोग गरिएको शैली"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "निर्दिष्ट मोड प्रयोग गर्नुहोस्, जस्तै लगइन पर्दाका लागि \"gdm\""
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "सम्भाव्य शैली सूची"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "अज्ञात"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "%s सुरु गर्न असफल"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "पासवर्ड मिलेन"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "पासवर्ड खालि हुन सक्दैन"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "प्रमाणीकरण संवाद प्रयोगकर्ताद्वारा खारेज गरिएको थियो"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "विस्तारहरू"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "तपाईँको जिनोम विस्तार व्यवस्थापन गर्नुहोस्"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "जिनोम परियोजना"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension preferences and removing or "
+"disabling unwanted extensions."
+msgstr ""
+"जिनोम विस्तारले विस्तारअद्यावधिक ह्यान्डल गर्दछ, विस्तार प्राथमिकताहरू कन्फिगर गर्दछ र नचाहिएका विस्तारहरू "
+"हटाउँदै वा अक्षम पार्दैछ।"
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "जिनोम सेल विस्तारहरू कन्फिगर"
+
+#: subprojects/extensions-app/js/main.js:130 subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "मिल्दो छैन"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "\"%s\" हटाउनुहुन्छ?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want to enable it again"
+msgstr ""
+"यदि तपाईँले विस्तार हटाउनुभयो भने, यदि तपाईँ यसलाई फेरि सक्षम पार्न चाहनुहुन्छ भने यसलाई डाउनलोड गर्न फर्काउनु "
+"आवश्यक छ"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "हटाउनुहोस्"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Pawan Chitrakar <chautari @gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d विस्तार पछिल्लो लगइनमा अद्यावधिक हुनेछ ।"
+msgstr[1] "%d विस्तार पछिल्लो लगइनमा अद्यावधिक हुनेछ ।"
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "हालको जिनोम संस्करणसँग विस्तार मिल्दो छैन"
+
+#: subprojects/extensions-app/js/main.js:484 subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "विस्तारमा एउटा त्रुटि थियो"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "विस्तार अद्यावधिक गर्न सकिन्छ"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "वेबसाइट"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "हटाउनुहोस्…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "मद्दत गर्नुहोस्"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "विस्तारहरू बारेमा"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if you encounter "
+"problems with your system."
+msgstr ""
+"एक्सटेन्सनले प्रदर्शन र स्थिरता समस्याहरू निम्त्याउन सक्छ। यदि तपाईँले आफ्नो प्रणालीमा समस्या हरू सामना गर्नुभयो भने "
+"एक्सटेन्सनहरू अक्षम पार्नुहोस् ।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "म्यानुअल तरिकाले स्थापना गरियो"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org\">extensions.gnome.org</"
+"a>."
+msgstr ""
+"विस्तारहरू फेला पार्न र थप गर्न <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a> "
+"हेर्नुहोस्।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "अवस्थित"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "विस्तार स्थापना गरिएको छैन"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed extensions. Make sure you "
+"are logged into GNOME and try again."
+msgstr ""
+"हामी धेरै दुःखी छौं, तर स्थापना गरिएको विस्तारको सूची प्राप्त गर्न सम्भव थिएन । तपाईँ जिनोममा लग इन भएको "
+"निश्चित गर्नुहोस् र फेरि प्रयास गर्नुहोस् ।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "विस्तार अद्यावधिक तयार छ"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "%s मा नयाँ विस्तार सफलतापूर्वक सिर्जना गरियो ।\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"नाम धेरै छोटो (वर्णनात्मक) स्ट्रिङ हुनुपर्दछ ।\n"
+"उदाहरणहरू: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302 subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "नाम"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"वर्णन तपाईँको विस्तारले के गर्छ भन्ने एकल-वाक्य व्याख्या हो।\n"
+"उदाहरणहरू: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322 subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "वर्णन"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe.example.com)\n"
+msgstr ""
+"UUID तपाईँको विस्तारका लागि विश्वव्यापी रूपमा अद्वितीय पहिचायक हो ।\n"
+"यो एउटा इमेल ठेगानाको ढाँचामा हुनुपर्छ (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "एउटा उपलब्ध टेम्प्लेट रोज्नुहोस्:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "टेम्प्लेट"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "नयाँ विस्तारको अद्वितिय पहिचायक"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "नयाँ विस्तारको प्रयोगकर्ता-दृश्यात्मक नाम"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "वर्णन"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "विस्तारले के गर्छ भन्ने छोटो वर्णन"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "टेम्प्लेट"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "नयाँ विस्तार प्रयोग गर्नका लागि टेम्प्लेट"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "विस्तार सूचना अन्तर्क्रियात्मक रूपमा प्रविष्ट गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "एउटा नयाँ विस्तार सिर्जना गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "अज्ञात तर्कहरू"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, नाम र वर्णन आवश्यक छ"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64 subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "जिनोम शेल जडान गर्न असफल\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "\"%s\" विस्तार अवस्थित छैन\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "एउटा विस्तार अक्षम पार्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID दिइएको छैन"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "एक भन्दा बढी UUID दिएको छ"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "एउटा विस्तार सक्षम पार्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-info.c:59 subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "\"%s\" विस्तार अवस्थित छैन\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "विस्तार जानकारि देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "एउटा अवस्थित विस्तार अधिलेखन गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "विस्तार बन्डल स्थापना गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "विस्तार बन्डल निर्दिष्ट गरिएको छैन"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "एक भन्दा बढी विस्तार बन्डल निर्दिष्ट गरियो"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "प्रयोगकर्ता-स्थापना विस्तार देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "प्रणाली स्थापना विस्तार देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "सक्षम पारिएको विस्तार देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "अक्षम पारिएको विस्तार देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "प्राथमिकता भएको विस्तार देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "अद्यावधिक भएको विस्तारहरू देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "मुद्रण बिस्तार विवरण"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "स्थापित विस्तार सूची"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "बन्डलमा समावेश गर्न थप स्रोत"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "स्किमा"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "समावेश गरिनुपर्ने एउटा जी-सेटिङ स्किमा"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "निर्देशिका"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "निर्देशिका जहाँ अनुवादहरू फेला पर्यो"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "डोमेन"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "अनुवादका लागि प्रयोग हुने gettext डोमेन"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "एउटा अवस्थित प्याक अधिलेखन गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "प्याक सिर्जना गर्नुपर्ने निर्दैशिका"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "स्रोत निर्देशिका"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "एउटा विस्तार बन्डल सिर्जना गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "एक भन्दा बढी स्रोत निर्देशिका निर्दिष्ट गरियो"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "विस्तार \"%s\" सँग प्राथमिकता छैन\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "एक्सटेन्सन \"%s\" का लागि प्रिफहरू खोल्न असफल भयो: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "विस्तार प्राथमिकताहरू खोल्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "एउटा बिस्तार रिसेट गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "प्रणाली विस्तार स्थापनाबाट हटाउन सकिँदैन\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "\"%s\" स्थापनाबाट हटाउन असफल\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "एउटा विस्तार स्थापनाबाट हटाउनुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "त्रुटि सन्देश मुद्रण नगर्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "पाथ"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "यू आर एल"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "वास्तविक लेखक"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "संस्करण"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "राज्य"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "\"संस्करण\" ले कुनै तर्कहरू लिदैन"
+
+#: subprojects/extensions-tool/src/main.c:296 subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "प्रयोग:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "संस्करण सूचना छाप्नुहोस् र बाहिर निस्कनुहोस्।"
+
+#: subprojects/extensions-tool/src/main.c:314 subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMMAND"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "गलत तर्क"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "आदेशहरू:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "मद्दत मुद्रण गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "विस्तार सक्षम पार्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "विस्तार अक्षम पार्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "बिस्तार रिसेट गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "विस्तार स्थापनाबाट हटाउनुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "विस्तार सूची"
+
+#: subprojects/extensions-tool/src/main.c:327 subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "विस्तार जानकारि देखाउनुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "विस्तार प्राथमिकताहरू खोल्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "विस्तार सिर्जना गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "प्याकेज बिस्तार"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "एउटा विस्तार बन्डल स्थापना गर्नुहोस्"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "विस्तृत मद्दत प्राप्त गर्न \"%s\" प्रयोग गर्नुहोस् ।\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "सादा"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "एउटा खाली विस्तार"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "सूचक"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "माथिपट्टीमा एउटा प्रतिमा थप्नुहोस्"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u निर्गत"
+msgstr[1] "%u निर्गतहरू"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u आगत"
+msgstr[1] "%u आगत"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "प्रणाली ध्वनिहरू"
+
+#~ msgid "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "पूर्वनिर्धारित ब्लुतुथ एडप्टरले यसमा सम्बन्धित यन्त्र सेटअप गरेको थियो या थिएन ।"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is powered, or if there "
+#~ "were devices set up associated with the default adapter. This will be reset if the default "
+#~ "adapter is ever seen not to have devices associated to it."
+#~ msgstr ""
+#~ "यदि ब्लुतुथ एडप्टर शक्तिमा छ भने वा पूर्वनिर्धारित एडप्टरसँग सम्बन्धित यन्त्र सेट गरिएको भएमा शेलले ब्लुतुथ मेनु वस्तु "
+#~ "मात्र देखाउनेछ । यदि पूर्वनिर्धारित एडप्टरसँग कहिल्यै पनि यन्त्र सम्बन्धित छैन भने यो रिसेट हुनेछ ।"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "इन्ट्रोस्पेक्टAPI सक्षम पार्नुहोस्"
+
+#~ msgid "Enables a D-Bus API that allows to introspect the application state of the shell."
+#~ msgstr "शेलको अनुप्रयोग अवस्था घुसाउन अनुमति दिने एउटा D-Bus API सक्षम बनाउँदछ ।"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "मनपर्नेबाट हताउनुहोस्"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "मनपर्नेमा थप्नुहोस्"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "ब्लुटुठ सेटिङ"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d जडान गरियो"
+#~ msgstr[1] "%d जडान गरियो"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "ब्लुटुठ बन्द गरियो"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "ब्लुटुठ शरु छ"
+
+#~ msgid "Location Enabled"
+#~ msgstr "स्थान सक्षम"
+
+#~ msgid "Disable"
+#~ msgstr "अक्षम पार्नुहोस्"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "गोपनीयता सेटिङहरू"
+
+#~ msgid "Location In Use"
+#~ msgstr "स्थान प्रयोगमा छ"
+
+#~ msgid "Location Disabled"
+#~ msgstr "स्थान अक्षम पारिएको"
+
+#~ msgid "Enable"
+#~ msgstr "सक्षम पार्नुहोस्"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s बन्द छ"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s जडान भएको छ"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s अव्यवस्थित"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s मा जडान गरिदैछ"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s प्रमाणीकरण आवश्यक छ"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s का लागि फर्मवेयर हराइरहेको छ"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s उपलब्ध छैन"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s जडान असफल"
+
+#~ msgid "Wired Settings"
+#~ msgstr "तारजडिट सेटिङ्ग"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "मोबाइल ब्रोडब्यान्ड सेटिङ्ग"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s हार्डवेयरअक्षम"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s अक्षम"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "इन्टरनेट जडान गर्ने"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "एअरप्लेन मोड राख्नु"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "हवाईजहाज मोड चलिरहेको बेला Wi-Fi अक्षम हुन्छ ।"
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "एअरप्लेन मोड हटाऊनु"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi बन्द छ"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "सञ्जालमा जडान गर्नका लागि Wi-Fi खोल्न आवश्यक हुन्छ ।"
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "वाइफाइ खोल्नुहोस्"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "वाई-फाई सञ्जाल"
+
+#~ msgid "Select a network"
+#~ msgstr "सञ्जाल चयन गर्नुहोस"
+
+#~ msgid "No Networks"
+#~ msgstr "सञ्जाल छैन"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "बन्द गर्नका लागि हार्डवेयर स्विच प्रयोग गर्नुहोस्"
+
+#~ msgid "Select Network"
+#~ msgstr "सञ्जाल चयन गर्नुहोस"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "वाई-फाई सेटिङ्ग"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s जडान भएको छैन"
+
+#~ msgid "connecting…"
+#~ msgstr "जडान गर्दै…"
+
+#~ msgid "authentication required"
+#~ msgstr "प्रमाणीकरण आवश्यक"
+
+#~ msgid "connection failed"
+#~ msgstr "जडान असफल"
+
+#~ msgid "VPN Settings"
+#~ msgstr "भीपीएन सेटिङ"
+
+#~ msgid "VPN"
+#~ msgstr "भीपीएन"
+
+#~ msgid "VPN Off"
+#~ msgstr "भीपीएन बन्द गर्नुहोस्"
+
+#~ msgid "Network Settings"
+#~ msgstr "सञ्जाल सेटिङ"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s तार जडान"
+#~ msgstr[1] "%s तारहरू जडान"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s वाई-फाई जडान"
+#~ msgstr[1] "%s वाई-फाईहरू जडान"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s मोडेम जडान"
+#~ msgstr[1] "%s मोडेमहरू जडान"
+
+#~ msgid "Connection failed"
+#~ msgstr "जडान असफल"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "सञ्जालमा जडान असफल भयो"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "रातको उज्यालो अक्षम"
+
+#~ msgid "Resume"
+#~ msgstr "पुन: निरन्तरता दिनुहोस्"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "भोलि सम्म निस्क्रिय"
+
+#~ msgid "Fully Charged"
+#~ msgstr "पुरा चार्ज भयो"
+
+#~ msgid "Not Charging"
+#~ msgstr "चार्ज छैन"
+
+#~ msgid "Estimating…"
+#~ msgstr "आँकलन गर्दै…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d बाँकि (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d पूरा हुन (%d %%) बाँकि"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "पर्दा साझेदार गरिएको"
+
+#~ msgid "Turn off"
+#~ msgstr "बन्द गर्नुहोस्"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "एअरप्लेन मोड राख्नु"
+
+#~ msgid "Lock"
+#~ msgstr "ताल्चा लगाउनुहोस्"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "पावर बन्द / लग आउट गर्नुहोस्"
+
+#~ msgid "Log Out"
+#~ msgstr "लगआउट"
+
+#~ msgid "Minimize"
+#~ msgstr "सानो बनाउनुहोस्"
+
+#~ msgid "Unmaximize"
+#~ msgstr "पूर्वावस्थामा ल्याउनुहोस्"
+
+#~ msgid "Author"
+#~ msgstr "लेखक"
+
+#~ msgid "Warning"
+#~ msgstr "चेतावनी"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If you encounter problems "
+#~ "with your system, it is recommended to disable all extensions."
+#~ msgstr ""
+#~ "विस्तारले कार्यसम्पादन समस्या लगायतका प्रणाली का समस्याहरू उत्पन्न गर्न सक्छ। यदि तपाईँले आफ्नो प्रणालीमा "
+#~ "समस्या सामना गर्नुभयो भने, सबै विस्तारहरू अक्षम पार्न सिफारिस गरिन्छ ।"
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "जिनोम शेल जडान गर्न असफल"
+
+#~ msgid "App Picker View"
+#~ msgstr "अनुप्रयोग पिकर संवाद"
+
+#~ msgid "Copy Error"
+#~ msgstr "प्रतिलिपि बनाउदा त्रुटि"
+
+#, fuzzy
+#~ msgid "Browse in Software"
+#~ msgstr "सफ्टवेयर"
+
+#~ msgid "Next"
+#~ msgstr "पछिल्लो"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "लगइन गर्दै"
+
+#, fuzzy
+#~ msgid "Frequent"
+#~ msgstr ""
+#~ "यो त्रुटिका लागि एउटा निरन्तर कारण भनेको तपाईँको डाटाबेसको भाषाका लागि अनुपयुक्त क्यारेकटर सेट सेटिङ हो । "
+#~ "सम्पादन गर्नुहोस - डाटाबेस - गुणहरू रोजेर सेटिङ जाँच गर्नुहोस ।"
+
+#~ msgid "All"
+#~ msgstr "सबै"
+
+#~| msgctxt "calendar heading"
+#~| msgid "%A, %B %d, %Y"
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %B %-d, %Y"
+
+#~ msgid "Password:"
+#~ msgstr "पासवर्ड:"
+
+#~ msgid "Type again:"
+#~ msgstr "फेरि टाईप गर्नुहोस्"
+
+#~ msgid "Password: "
+#~ msgstr "पासवर्ड: "
+
+#, fuzzy
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "%s प्रमाणिकरण आवश्यक छ ।"
+
+#, fuzzy
+#~ msgid "Mobile broadband network password"
+#~ msgstr "मोबाइल ब्रोडब्यान्ड सेटिङ्ग"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %B %d"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d नयाँ सन्देश"
+#~ msgstr[1] "%d नयाँ सन्देशहरू"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d नयाँ सन्देश"
+#~ msgstr[1] "%d नयाँ सन्देश"
+
+#~ msgid "Off"
+#~ msgstr "बन्द"
+
+#~ msgid "On"
+#~ msgstr "खुला छ"
+
+#~ msgid "Account Settings"
+#~ msgstr "खाता सेटिङ्ग"
+
+#, fuzzy
+#~ msgid "Orientation Lock"
+#~ msgstr "ताल्चा लगाउनुहोस्"
+
+#, fuzzy
+#~ msgid "Which keyboard to use"
+#~ msgstr "'extcmd' को कुन कार्यन्वयन प्रयोग गर्ने"
+
+#, fuzzy
+#~ msgid "The type of keyboard to use."
+#~ msgstr "गतिशील कुञ्जीपाटी सर्टकट प्रयोग गर्नुहोस्"
+
+#, fuzzy
+#~| msgid "Network error"
+#~ msgid "network-workgroup"
+#~ msgstr "एसएमबी (SMB) कार्यसमूह"
+
+#, fuzzy
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "पीएनजी फाइल लोड गर्दा त्रुटि: %s"
+
+#, fuzzy
+#~ msgid "%s all day."
+#~ msgstr "दिनभर"
+
+#, fuzzy
+#~ msgid "%s, then %s later."
+#~ msgstr "%s: पछि फेरी प्रयास गर्नुहोस्\n"
+
+#, fuzzy
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "फाँट नाम `%.*s' विरामचिन्ह द्धारा साथ दिनु पर्छ"
+
+#, fuzzy
+#~ msgid "Feels like %s."
+#~ msgstr "हवस् त, %s । तपाईं के चाहनुहुन्छ..."
+
+#, fuzzy
+#~| msgid "Hide Text"
+#~ msgid "Hide tray"
+#~ msgstr "लुकाउनुहोस्"
+
+#, fuzzy
+#~ msgid "Status Icons"
+#~ msgstr "प्रतिमाहरू"
+
+#, fuzzy
+#~ msgid "toggle-switch-us"
+#~ msgstr "उल्टनू"
+
+#~ msgid "Show the message tray"
+#~ msgstr "सन्देश पूर्वावलोकन देखाउनुहोस्"
+
+#, fuzzy
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "व्यल्यान्ड जिनोम"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "क्यालेन्डरमा हप्ता नम्बर देखाउनुहोस्"
+
+#, fuzzy
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "अस्थायी प्रमाणीकरण असफल"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Su"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "M"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "T"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "W"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Th"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "F"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "S"
+
+#, fuzzy
+#~ msgid "Nothing Scheduled"
+#~ msgstr "केही पनि होइन"
+
+#~ msgid "This week"
+#~ msgstr "यो हप्ता"
+
+#~ msgid "Next week"
+#~ msgstr "गएको हप्ता"
+
+#~ msgid "Removable Devices"
+#~ msgstr "छुट्याउन मिल्ने यन्त्र"
+
+#~ msgid "Eject"
+#~ msgstr "निकाल"
+
+#~ msgid "Invitation"
+#~ msgstr "निमन्त्रना"
+
+#~ msgid "Call"
+#~ msgstr "कल"
+
+#~ msgid "File Transfer"
+#~ msgstr "फाइल स्थानान्तरण"
+
+#~ msgid "Chat"
+#~ msgstr "कुराकानी"
+
+#~ msgid "Unmute"
+#~ msgstr "मौन हटाउनुहोस्"
+
+#~ msgid "Mute"
+#~ msgstr "मौन"
+
+#~ msgid "Invitation to %s"
+#~ msgstr " %sलाई निमन्त्रणा"
+
+#, fuzzy
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "के तपाईँ वार्तालाप जडान गर्न चाहानुहुन्छ ?"
+
+#~ msgid "Decline"
+#~ msgstr "घटाउनुहोस्"
+
+#~ msgid "Accept"
+#~ msgstr "स्विकार गर्नुहोस्"
+
+#, fuzzy
+#~ msgid "Video call from %s"
+#~ msgstr "inetd बाट आह्वान गरिएको"
+
+#, fuzzy
+#~ msgid "Call from %s"
+#~ msgstr "%s बाट विच्छेदन गर्नुहोस्..."
+
+#~ msgid "Answer"
+#~ msgstr "उत्तर"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s ले %s पठाएको छ"
+
+#~ msgid "Authentication failed"
+#~ msgstr "प्रमाणीकरण असफल भयो ।"
+
+#~ msgid "Encryption error"
+#~ msgstr "गुप्तीकरण त्रुति"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "प्रमाणपत्र उपलब्ध छैन"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "प्रमाणपत्र विश्वासिलो भएन"
+
+#~ msgid "Certificate expired"
+#~ msgstr "मिति नाघेको प्रमाणपत्र "
+
+#, fuzzy
+#~ msgid "Certificate not activated"
+#~ msgstr "<Not Part of Certificate>"
+
+#, fuzzy
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "AKID/SKID बेमेल"
+
+#, fuzzy
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "AKID/SKID बेमेल"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "श्रींखलामा आफै-हस्ताक्षर गरिएको प्रमाणपत्र"
+
+#, fuzzy
+#~ msgid "Status is set to offline"
+#~ msgstr "जीकन्फ कुञ्जी \"%s\" लाई अमान्य प्रकारमा सेट गरिएको छ\n"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "अवैध प्रमाणपत्र"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "सञ्जाल जडान विच्छेदन भयो ।"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "जडान स्थापना गर्नसकिएन"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "सञ्जाल जडान विच्छेदन भयो ।"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "खाता पहिले नै सर्भरसंग जडित छ"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "खाता पहिले नै सर्भरमा अवस्थित छ"
+
+#, fuzzy
+#~ msgid "Certificate has been revoked"
+#~ msgstr "<b>यो कुञ्जी खारेज गरिएको छ</b>"
+
+#~ msgid "Internal error"
+#~ msgstr "आन्तरिक त्रुटि"
+
+#~ msgid "View account"
+#~ msgstr "खाता हेर्नुहोस्"
+
+#~ msgid "Open Calendar"
+#~ msgstr "पात्रो खोल्नुहोस्"
+
+#~ msgid "Open"
+#~ msgstr "खोल्नुहोस्"
+
+#~ msgid "Clear Messages"
+#~ msgstr "सूचना मेट्नुहोस्"
+
+#~ msgid "Notification Settings"
+#~ msgstr "सुचना सेटिङ"
+
+#, fuzzy
+#~ msgid "Tray Menu"
+#~ msgstr "टेयरअफ मेनु"
+
+#, fuzzy
+#~ msgid "Message Tray"
+#~ msgstr "ट्रेको अभिमुखीकरण ।"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d जडित यन्त्र"
+#~ msgstr[1] "%d जडित यन्त्रहरू"
+
+#~ msgid "In Use"
+#~ msgstr "प्रयोग मा"
+
+#~ msgid "connecting..."
+#~ msgstr "जडान गर्दै....."
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "बेटरि"
diff --git a/po/nl.po b/po/nl.po
new file mode 100644
index 0000000..5cd0daa
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,3369 @@
+# Dutch translation for gnome-shell
+# This file is distributed under the same license as the gnome-shell package.
+# Aangezien dit een erg zichtbare module is graag eerst contact opnemen met
+# Wouter Bolsterlee alvorens zaken te wijzigen!
+# Message tray - Berichtenoverzicht
+#
+# Sander Dijkhuis <sander.dijkhuis@gmail.com>, 2009–2010.
+# Reinout van Schouwen <reinouts@gnome.org>, 2010, 2013, 2014.
+# Wouter Bolsterlee <wbolster@gnome.org>, 2011–2014.
+# Erwin Poeze <donnut@outlook.com>, 2013.
+# Nathan Follens <nfollens@gnome.org>, 2015-2022.
+# Hannie Dumoleyn <hannie@ubuntu-nl.org>, 2015, 2017, 2018, 2021.
+# Justin van Steijn <jvs@fsfe.org>, 2016, 2018.
+# Philip Goto <philip.goto@gmail.com>, 2018, 2022.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-10-26 10:09+0000\n"
+"PO-Revision-Date: 2022-11-01 23:30+0100\n"
+"Last-Translator: Nathan Follens <nfollens@gnome.org>\n"
+"Language-Team: Dutch <gnome-nl-list@gnome.org>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.1.1\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Starters"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Favoriete toepassing 1 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Favoriete toepassing 2 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Favoriete toepassing 3 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Favoriete toepassing 4 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Favoriete toepassing 5 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Favoriete toepassing 6 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Favoriete toepassing 7 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Favoriete toepassing 8 inschakelen"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Favoriete toepassing 9 inschakelen"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Schermafdrukken"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Interactief een schermafdruk maken"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Schermafdruk maken"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Schermafdruk maken van een venster"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Interactief een schermopname maken"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Systeem"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "De notificatielijst tonen"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Actieve notificatie focus geven"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Het overzicht tonen"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Alle toepassingen tonen"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Toepassingenmenu openen"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Gnome Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Vensterbeheer en toepassingen starten"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Interne hulpprogramma’s inschakelen die nuttig zijn voor ontwikkelaars en "
+"testers via Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Geeft toegang tot interne debugging- en observatieprogramma’s met behulp van "
+"het Alt-F2-dialoogvenster."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs van in te schakelen uitbreidingen"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Gnome Shell-uitbreidingen hebben een UUID-eigenschap; deze instelling somt "
+"de uitbreidingen op die geladen moeten worden. Alle te laden uitbreiden "
+"moeten in deze lijst voorkomen. Deze lijst is ook te manipuleren met de DBus-"
+"methodes ‘EnableExtension’ en ‘DisableExtension’ op org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs van gedwongen uit te schakelen uitbreidingen"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Gnome Shell-uitbreidingen hebben een UUID-eigenschap; deze instelling somt "
+"de uitbreidingen op die uitgeschakeld moeten worden, zelfs indien ze als "
+"onderdeel van de huidige modus geladen zijn. Deze lijst is ook te "
+"manipuleren met de DBus-methodes ‘EnableExtension’ en ‘DisableExtension’ op "
+"org.gnome.Shell. Deze instelling heeft voorrang op de ‘enabled-extensions’-"
+"instelling."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Gebruikersuitbreidingen uitschakelen"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Schakel alle door de gebruiker ingeschakelde uitbreidingen uit, zonder de "
+"instelling ‘enabled-extension’ te wijzigen."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Schakelt de validatie van versiecompatibiliteit voor uitbreidingen uit"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Gnome Shell zal alleen uitbreidingen laden die aangeven de huidige versie te "
+"ondersteunen. Door deze optie in te schakelen wordt deze controle "
+"overgeslagen en worden alle uitbreidingen geladen, ongeacht de versies die "
+"ze zouden moeten ondersteunen."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lijst van bureaubladbestand-id’s voor favoriete toepassingen"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"De toepassingen die aan deze identifiers voldoen worden in het "
+"favorietengebied weergegeven."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Geschiedenis voor het opdrachtvenster (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Geschiedenis voor het ‘looking glass’-venster"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Menu-item ‘afmelden’ altijd tonen in gebruikersmenu."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Deze instelling voorkomt het automatisch verbergen van het menu-item "
+"‘afmelden’ indien slechts een enkele gebruiker met een enkele sessie is "
+"aangemeld."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Of wachtwoorden voor versleutelde bestandssystemen of bestandssystemen op "
+"afstand onthouden moeten worden"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"De shell vraagt om een wachtwoord als een versleuteld bestandssysteem of een "
+"bestandssysteem op afstand aangekoppeld wordt. Als het wachtwoord voor "
+"toekomstig gebruik opgeslagen kan worden, dan zal een ‘Wachtwoord onthouden’-"
+"vinkje getoond worden. Deze instelling bepaalt de standaardwaarde hiervoor."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "De laatst geselecteerde, niet-standaard energiemodus"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Sommige systemen ondersteunen meer dan twee energiemodi. Om tussen twee "
+"profielen omschakelen te blijven ondersteunen, houdt deze sleutel de laatste "
+"niet-standaard energiemodus bij."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"De laatste versie waarvoor het dialoogvenster ‘Welkom bij Gnome’ werd getoond"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Deze sleutelt bepaalt voor welke versie het dialoogvenster ‘Welkom bij "
+"Gnome’ voor het laatst werd getoond. Een lege tekenreeks stelt de oudst "
+"mogelijke versie voor, en een groot getal stelt versies die nog niet bestaan "
+"voor. Dit grote getal kan gebruikt worden om het dialoogvenster in de "
+"praktijk uit te schakelen."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Weergave van de toepassingskiezer"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Weergave van de toepassingskiezer. Elke ingave in de array is een pagina. "
+"Pagina’s worden opgeslagen in de volgorde waarin ze in Gnome Shell "
+"voorkomen. Elke pagina bevat een paar ‘toepassings-ID’ → ‘data’. Momenteel "
+"worden de huidige waarden opgeslagen als ‘data’: • “position”: de positie "
+"van het toepassingspictogram op de pagina"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Sneltoets om het toepassingenmenu te openen"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Sneltoets om het toepassingenmenu te openen."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Sneltoets om te schakelen tussen overzichtsstatussen"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "Sneltoets om te schakelen tussen sessie, vensterkiezer en app-raster"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "Sneltoets om te schakelen tussen app-raster, vensterkiezer en sessie"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Sneltoets voor de toepassingenweergave"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Sneltoets voor de toepassingenweergave in het activiteitenoverzicht."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Sneltoets voor het overzicht"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Sneltoets voor het openen van het Activiteitenoverzicht."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Sneltoets om de notificatielijst te tonen of te verbergen"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Sneltoets om de notificatielijst te tonen of te verbergen."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Sneltoets om de actieve notificatie de focus te geven"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Sneltoets om de actieve notificatie de focus te geven."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Schakelen naar toepassing 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Schakelen naar toepassing 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Schakelen naar toepassing 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Schakelen naar toepassing 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Schakelen naar toepassing 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Schakelen naar toepassing 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Schakelen naar toepassing 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Schakelen naar toepassing 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Schakelen naar toepassing 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Beperk wisselaar tot huidig werkblad."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Indien ingeschakeld, worden alleen toepassingen die vensters op het huidige "
+"werkblad hebben getoond in de wisselaar. Anders worden alle toepassingen "
+"meegenomen."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Weergave van toepassingspictogrammen."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Geeft aan hoe vensters in het overzicht getoond worden. Geldige waarden zijn "
+"‘thumbnail-only’ (alleen een miniatuur tonen), ‘app-icon-only’ (alleen het "
+"pictogram van de toepassing tonen) of ‘both’ (beide tonen)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Indien ingeschakeld, worden alleen vensters op het huidige werkblad getoond "
+"in de wisselaar. Anders worden alle vensters meegenomen."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Locaties"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Lijst van de te tonen wereldklokken"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatische locatie"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Of de huidige locatie opgehaald moet worden of niet"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Locatie"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "De locatie waarvoor er een weersvoorspelling wordt weergegeven"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Modale dialoogvensters aan hoofdvenster vastmaken"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "Deze waarde overschrijft de waarde in org.gnome.mutter in Gnome Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Vensters langs de randen positioneren bij verslepen naar de rand van het "
+"scherm"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Werkbladen dynamisch beheren"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Alleen werkbladen op hoofdmonitor"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Focus pas wijzigen nadat de muisaanwijzer is gestopt met bewegen"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Netwerkaanmelding"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Er is iets misgegaan"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Het spijt ons, maar er is een probleem opgetreden: de instellingen voor deze "
+"uitbreiding kunnen niet worden weergegeven. We raden u aan dit probleem te "
+"melden aan de ontwikkelaars van de uitbreiding."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Technische details"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Startpagina"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Website van uitbreiding openen"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Annuleren"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Wachtwoord"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Kies een sessie"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Niet in de lijst?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(bijv. gebruiker of %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Gebruikersnaam"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Aanmeldvenster"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Authenticatie mislukt"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(of veeg met uw vinger over de lezer)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(of plaats uw vinger op de lezer)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Uitschakelen"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;uitschakelen;uitzetten;stoppen"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Herstarten"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr ""
+"reboot;restart;herstarten;heropstarten;opnieuw opstarten;opnieuw starten;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Vergrendelingsscherm"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen;vergrendelingsscherm;vergrendelscherm"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Afmelden"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;afmelden;uitloggen"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Pauzestand"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;pauzestand;slaapmodus"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Gebruiker wisselen"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "gebruiker wisselen"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;unlock orientation;screen;rotation;oriëntatievergrendeling;"
+"scherm;draaiing;rotatie"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Schermafdruk maken"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"schermafdruk;screenshot;schermopname;screencast;knip;snip;opnemen;capture;"
+"record"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Schermoriëntatie ontgrendelen"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Schermoriëntatie vergrendelen"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Opdracht niet gevonden"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Kon opdracht niet parsen:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Uitvoeren van ‘%s’ mislukt:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Zojuist"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minuut geleden"
+msgstr[1] "%d minuten geleden"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d uur geleden"
+msgstr[1] "%d uur geleden"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Gisteren"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dag geleden"
+msgstr[1] "%d dagen geleden"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d week geleden"
+msgstr[1] "%d weken geleden"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d maand geleden"
+msgstr[1] "%d maand geleden"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d jaar geleden"
+msgstr[1] "%d jaar geleden"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Gisteren, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A,%H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Gisteren, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Hotspotaanmelding"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Uw verbinding met deze hotspotaanmelding is niet beveiligd. Wachtwoorden of "
+"andere informatie die u op deze pagina invoert kunnen door anderen in de "
+"buurt gelezen worden."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Toegang weigeren"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Toegang verlenen"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Naamloze map"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s is vastgemaakt aan de dash."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s is losgemaakt van de dash."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Open vensters"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nieuw venster"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Details tonen"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Afsluiten"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Losmaken"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Vastmaken aan dash"
+
+# Dedicated niet vertaald
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Met geïntegreerde grafische kaart opstarten"
+
+# Dedicated niet vertaald
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Met discrete grafische kaart opstarten"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Kies audioapparaat"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Geluidsinstellingen"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Hoofdtelefoon"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Headset"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Microfoon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Achtergrond aanpassen…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Scherminstellingen"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Instellingen"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Z"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Z"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Vorige maand"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Volgende maand"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Week %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Geen notificaties"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Niet storen"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Wissen"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "‘%s’ reageert niet."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"U kan even wachten tot de toepassing weer reageert, of ze dwingen "
+"onmiddellijk af te sluiten."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Afsluiten dwingen"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Wachten"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Verbinding met externe schijf gemaakt"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Verbinding met externe schijf verbroken"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Kan volume niet ontgrendelen"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+"De geïnstalleerde versie van udisks biedt geen ondersteuning voor de PIM-"
+"instelling"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Openen met %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"U kunt ook verbinding maken door op de ‘WPS’-knop op uw router te drukken."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Verbinden"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Sleutel"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Wachtwoord van privésleutel"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identiteit"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Dienst"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Aanmelding vereist"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Voor toegang tot het draadloze netwerk ‘%s’ is een wachtwoord of sleutel "
+"benodigd."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "802.1X-authenticatie (bekabeld)"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Netwerknaam"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL-authenticatie"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN-code vereist"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN-code vereist voor het apparaat voor mobiel breedband"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "Pincode"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Er is een wachtwoord nodig om met ‘%s’ te verbinden."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Netwerk-manager"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN-wachtwoord"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Authenticatie nodig"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Beheerder"
+
+# Vrij vertaald (Wouter Bolsterlee)
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Verifiëren"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Helaas, dat werkte niet. Probeer het opnieuw."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s heet vanaf nu %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Vensters"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Toepassingen tonen"
+
+# Betere vertaling is welkom (Wouter Bolsterlee)
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Zijbalk"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Vandaag"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Morgen"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Gehele dag"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d-%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Geen gebeurtenissen"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Wereldklokken toevoegen…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Wereldklokken"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Laden…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Ga online voor informatie over het weer"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Informatie over het weer is momenteel niet beschikbaar"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Weer"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Selecteer weerlocatie…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s afmelden"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Afmelden"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s wordt automatisch afgemeld over %d seconde."
+msgstr[1] "%s wordt automatisch afgemeld over %d seconden."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "U wordt automatisch afgemeld over %d seconde."
+msgstr[1] "U wordt automatisch afgemeld over %d seconden."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Afmelden"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Uitschakelen"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Updates installeren en uitschakelen"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "De computer wordt automatisch afgesloten over %d seconde."
+msgstr[1] "De computer wordt automatisch afgesloten over %d seconden."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Gereedstaande software-updates installeren"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Uitschakelen"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Opnieuw opstarten"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Updates installeren en herstarten"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "De computer wordt opnieuw opgestart over %d seconde."
+msgstr[1] "De computer wordt opnieuw opgestart over %d seconden."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Opnieuw opstarten"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Opnieuw opstarten en updates installeren"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"De computer zal opnieuw opstarten en updates installeren over %d seconde."
+msgstr[1] ""
+"De computer zal opnieuw opstarten en updates installeren over %d seconden."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Herstarten en installeren"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installeren en uitschakelen"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Uitschakelen na installatie van updates"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Opnieuw opstarten en upgrades installeren"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s zal de herstart worden geïnstalleerd. De installatie van de upgrade "
+"kan lang duren: zorg ervoor dat u een reservekopie heeft gemaakt en dat de "
+"computer is aangesloten op netstroom."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"De batterij is bijna leeg: sluit de adapter aan alvorens de installatie te "
+"starten."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Sommige toepassingen zijn bezig of hebben niet-opgeslagen werk"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Er zijn andere gebruikers aangemeld"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opstartopties"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (op afstand)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (console)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installeren"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Uitbreiding installeren"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "‘%s’ downloaden van extensions.gnome.org en daarna installeren?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Er zijn uitbreidingsupdates beschikbaar"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Uitbreidingsupdates zijn klaar voor installatie."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Blokkeren van sneltoetsen toestaan"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "De toepassing %s wil sneltoetsen blokkeren"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Een toepassing wil sneltoetsen blokkeren"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "U kan sneltoetsen herstellen door op %s te drukken."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Weigeren"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Toestaan"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Trage Toetsen ingeschakeld"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Trage Toetsen uitgeschakeld"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"U heeft zojuist de Shift-toets gedurende 8 seconden ingedrukt gehouden. Dat "
+"is de sneltoets voor de Trage Toetsen-modus, hetgeen invloed heeft op de "
+"manier waarop het toetsenbord functioneert."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Plaktoetsen ingeschakeld"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Plaktoetsen uitgeschakeld"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"U heeft zojuist de Shift-toets 5 keer op een rij ingedrukt. Dat is de "
+"sneltoets voor de Plaktoetsen-modus, hetgeen invloed heeft op de manier "
+"waarop uw toetsenbord functioneert."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"U heeft zojuist twee toetsen tegelijk ingedrukt, of de Shift-toets 5 keer op "
+"een rij ingedrukt. Dat zet de Plaktoetsen-modus uit, hetgeen invloed heeft "
+"op de manier waarop uw toetsenbord functioneert."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Aan laten"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Inschakelen"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Uitschakelen"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Uit laten"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Regio- & taalinstellingen"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Geen uitbreidingen geïnstalleerd"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s heeft geen fouten gemeld."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Fouten verbergen"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Fouten tonen"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Ingeschakeld"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Uitgeschakeld"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Fout"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Gedateerd"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Downloaden"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Broncode weergeven"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Webpagina"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Systeem is in onveilige modus gezet"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Toepassingen hebben nu ongelimiteerde toegang"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Ongedaan maken"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Aangemeld als gebruiker met extra rechten"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Uit beveiligingsoverwegingen is het beter dat u het uitvoeren van een sessie "
+"als gebruiker met extra rechten vermijdt. Meld u, indien mogelijk, aan als "
+"een normale gebruiker."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Schermvergrendeling uitgeschakeld"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Schermvergrendeling vereist de Gnome-displaymanager."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Systeeminformatie"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Onbekende artiest"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Onbekend nummer"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Typ om te zoeken"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Toepassingen"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Overzicht"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nieuwe sneltoets…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Toepassing gedefinieerd"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Hulptekst op scherm tonen"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Van beeldscherm wisselen"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Toetsaanslag toewijzen"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Klaar"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Bewerken…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Geen"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Druk op een toets om te configueren"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Druk op Esc om af te sluiten"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Druk op een willekeurige toets om af te sluiten"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Activiteiten"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Systeem"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Menubalk"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Voer een opdracht uit"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Druk op ESC om te sluiten"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Herstarten is niet beschikbaar op Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Herstarten…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Gnome moet het scherm vergrendelen"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Kon niet vergrendelen"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Vergrendelen is door een toepassing voorkomen"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Selectie"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Gebiedsselectie"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Scherm"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Schermselectie"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Venster"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Vensterselectie"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Schermafdruk / schermopname"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Muisaanwijzer tonen"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Schermopnamen"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Schermopname van %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Schermafdruk"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Schermopname opgenomen"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Klik hier om de video te bekijken."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "In Bestanden tonen"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Schermafdruk van %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Schermafdruk gemaakt"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "U kunt de afbeelding van het klembord plakken."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Schermafdruk gemaakt"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Zoeken…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Geen resultaten."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d meer"
+msgstr[1] "%d meer"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Zoeken"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopiëren"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Plakken"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Tekst tonen"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Tekst verbergen"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock staat aan."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Verborgen volume"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows-systeemvolume"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Gebruikt sleutelbestanden"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Gebruik <i>%s</i> om een volume dat sleutelbestanden gebruikt te "
+"ontgrendelen."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-getal"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Wachtwoord onthouden"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Ontgrendelen"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s openen"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "De PIM moet ofwel een getal ofwel leeg zijn."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Kan ‘%s’ niet starten"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Kon ‘%s’ niet vinden"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Toegankelijkheid"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Hoog contrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zoomen"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Schermlezer"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Schermtoetsenbord"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Visuele alerteringen"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Plaktoetsen"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Trage Toetsen"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Springende toetsen"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Muistoetsen"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Grote tekst"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatisch draaien"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Helderheid"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Donkere modus"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Enkele klik"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dubbelklik"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Slepen"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Tweede klik"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Hangklik"
+
+#: js/ui/status/keyboard.js:833
+msgid "Keyboard"
+msgstr "Toetsenbord"
+
+#: js/ui/status/keyboard.js:850
+msgid "Show Keyboard Layout"
+msgstr "Toetsenbordindeling tonen"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Toegang verlenen tot locatie"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "De toepassing %s wil toegang tot uw locatie"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Locatietoegang kan op elk moment worden gewijzigd in de privacyinstellingen."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<onbekend>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Verbinding verbreken met %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Verbinden met %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s-hotspot"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Nachtlicht"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Prestaties"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Gebalanceerd"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Energie besparen"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Energiemodi"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Schermopname stoppen"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Scherm delen stoppen"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Vliegtuigstand"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Schermafdruk maken"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Uitschakelmenu"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Pauzestand"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Herstarten…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Uitschakelen…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Afmelden…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Gebruiker wisselen…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Scherm vergrendelen"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Onbekend Thunderbolt-apparaat"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Terwijl u weg was is er een nieuw apparaat gedetecteerd. Koppel het apparaat "
+"los en verbind het opnieuw om het te gebruiken."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Ongeautoriseerd Thunderbolt-apparaat"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Er is een nieuw apparaat gedetecteerd, dit moet worden geautoriseerd door "
+"een systeembeheerder."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt-autorisatiefout"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Kon het Thunderbolt-apparaat niet autoriseren: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Volume gewijzigd"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volume"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Geluidsuitvoer"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Geluidsinvoer"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Spiegel"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Schermen samenvoegen"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Enkel extern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Enkel ingebouwd"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Veeg omhoog om te ontgrendelen"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Klik of druk op een toets om te ontgrendelen"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Venster voor ontgrendelen"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Aanmelden als andere gebruiker"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Welkom bij Gnome %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Wilt u hiermee leren omgaan, volg dan de rondleiding."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nee, bedankt"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "De rondleiding volgen"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "‘%s’ is gereed"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Deze scherminstellingen behouden?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Terugzetten"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Behouden"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Instellingswijzigingen worden teruggezet in %d seconde"
+msgstr[1] "Instellingswijzigingen worden teruggezet in %d seconden"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Verbergen"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Herstellen"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximaliseren"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Verplaatsen"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Vergroten/verkleinen"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Titelbalk op scherm plaatsen"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Altijd voorop"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Altijd op zichtbaar werkblad"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Naar werkblad links"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Naar werkblad rechts"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Naar werkblad hierboven"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Naar werkblad hieronder"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Naar monitor hierboven"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Naar monitor hieronder"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Naar monitor links"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Naar monitor rechts"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Sluiten"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution-agenda"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Versie weergeven"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "De modus die door GDM voor het aanmeldscherm gebruikt wordt"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Specifieke modus gebruiken, bijv. ‘gdm’ voor het aanmeldscherm"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Mogelijke modi tonen"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Onbekend"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Kon ‘%s’ niet starten"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "De wachtwoorden komen niet overeen."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Het wachtwoord mag niet leeg blijven"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Authenticatievenster is door de gebruiker afgesloten"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Uitbreidingen"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Beheer uw Gnome-uitbreidingen"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Het Gnome-project"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Gnome Uitbreidingen staat in voor het bijwerken van uitbreidingen, hun "
+"voorkeuren te configureren of ongewenste uitbreidingen te verwijderen of uit "
+"te schakelen."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Gnome Shell-uitbreidingen configureren"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Geen overeenkomsten"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "‘%s’ verwijderen?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Als u een uitbreiding verwijdert, zult u deze opnieuw moeten downloaden om "
+"ze opnieuw te gebruiken"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Verwijderen"
+
+# Met het nieuwe over-venster ziet de lege regel tussen vertalers en meer info er vreemd uit, mij lijkt het beter om deze weg te laten.
+# - Philip
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Nathan Follens <nfollens@gnome.org>\n"
+"Philip Goto <philip.goto@gmail.com>\n"
+"Meer info over Gnome-NL http://nl.gnome.org"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d uitbreiding wordt bijgewerkt bij de volgende aanmelding."
+msgstr[1] "%d uitbreidingen worden bijgewerkt bij de volgende aanmelding."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "De uitbreiding is incompatibel met de huidige Gnome-versie"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "De uitbreiding heeft een fout ondervonden"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "De uitbreiding kan worden bijgewerkt"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Website"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Verwijderen…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Hulp"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Over uitbreidingen"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Uitbreidingen kunnen prestatie- en stabiliteitsproblemen veroorzaken. "
+"Schakel uitbreidingen uit als u problemen ondervindt met uw systeem."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Handmatig geïnstalleerd"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Bezoek <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a> om "
+"uitbreidingen te zoeken en installeren."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Ingebouwd"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Geen uitbreidingen geïnstalleerd"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Sorry, we konden de lijst met geïnstalleerde uitbreidingen niet verkrijgen. "
+"Zorg dat u bij Gnome aangemeld bent, en probeer het opnieuw."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Uitbreidingsupdates zijn klaar"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "De nieuwe uitbreiding is aangemaakt in %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"De naam moet een zeer korte (liefst beschrijvende) tekenreeks zijn.\n"
+"Bijvoorbeeld: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Naam"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"De beschrijving is een uitleg in één zin van wat de uitbreiding doet.\n"
+"Bijvoorbeeld: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Beschrijving"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"De UUID is een unieke identificatie voor uw uitbreiding.\n"
+"Dit moet in het formaat van een e-mailadres zijn. (focusklik@jansmits."
+"voorbeeld.nl)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Kies een van de beschikbare sjablonen:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Sjabloon"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "De unieke identificatie van de nieuwe uitbreiding"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAAM"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "De voor de gebruikers zichtbare naam van de nieuwe uitbreiding"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "BESCHRIJVING"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Een korte beschrijving van wat de uitbreiding doet"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "SJABLOON"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Het te gebruiken sjabloon voor de nieuwe uitbreiding"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Voer uitbreidingsinformatie interactief in"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Maak een nieuwe uitbreiding aan"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Onbekende argumenten"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, naam en beschrijving zijn vereist"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Verbinden met Gnome Shell mislukt\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Uitbreiding ‘%s’ bestaat niet\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Schakel een uitbreiding uit"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Geen UUID opgegeven"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Meer dan één UUID opgegeven"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Schakel een uitbreiding in"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Uitbreiding ‘%s’ bestaat niet\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Uitbreidingsinfo tonen"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Een bestaande uitbreiding overschrijven"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "UITBREIDINGSBUNDEL"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Installeer een uitbreidingsbundel"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Geen uitbreidingsbundel opgegeven"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Meer dan één uitbreidingsbundel opgegeven"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Gebruikersuitbreidingen tonen"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Systeemuitbreidingen tonen"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Ingeschakelde uitbreidingen tonen"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Uitgeschakelde uitbreidingen tonen"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Uitbreidingen met voorkeuren tonen"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Uitbreidingen met updates tonen"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Details van uitbreiding tonen"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Lijst met geïnstalleerde uitbreidingen"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "BESTAND"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Aanvullende bron om in de bundel in te voegen"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Een GSettings-schema om in te voegen"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "MAP"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "De map waar de vertalingen te vinden zijn"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMEIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Het gettext-domein om te gebruiken voor vertalingen"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Een bestaand pakket overschrijven"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "De map waar het pakket aangemaakt moet worden"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "BRONMAP"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Maak een uitbreidingsbundel aan"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Meer dan één bronmap opgegeven"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Uitbreiding ‘%s’ heeft geen voorkeuren\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Openen van voorkeuren voor uitbreiding ‘%s’ is mislukt: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Opent de uitbreidingsvoorkeuren"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Stel een uitbreiding opnieuw in"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Kan systeemuitbreidingen niet verwijderen\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Kan ‘%s’ niet verwijderen\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Verwijder een uitbreiding"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Foutberichten niet tonen"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Pad"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Oorspronkelijke auteur"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versie"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Status"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "‘version’ verwacht geen argumenten"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Gebruik:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Versie-informatie tonen en afsluiten."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "OPDRACHT"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTEN…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Opdrachten:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Hulp tonen"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Uitbreiding inschakelen"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Uitbreiding uitschakelen"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Uitbreiding opnieuw instellen"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Uitbreiding verwijderen"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Lijst met uitbreidingen"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Uitbreidingsinfo tonen"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Uitbreidingsvoorkeuren openen"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Uitbreiding maken"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Uitbreiding inpakken"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Uitbreidingsbundel installeren"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Gebruik ‘%s’ voor gedetailleerde hulp.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Normaal"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Een lege uitbreiding"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Voeg een pictogram toe aan de bovenste balk"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u uitvoerkanalen"
+msgstr[1] "%u uitvoerkanalen"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u invoerkanaal"
+msgstr[1] "%u invoerkanalen"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Systeemgeluiden"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Of er apparaten met de standaard Bluetooth-adapter geassocieerd waren"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "De shell zal enkel een Bluetooth-menu-item weergeven indien een Bluetooth-"
+#~ "adapter is ingeschakeld, of indien er apparaten ingesteld zijn "
+#~ "geassocieerd met de standaardadapter. Dit zal opnieuw ingesteld worden "
+#~ "indien de standaardadapter geen apparaten met zich geassocieerd blijkt te "
+#~ "hebben."
+
+#~ msgid "Log Out"
+#~ msgstr "Afmelden"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Bluetooth-instellingen"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d verbonden apparaat"
+#~ msgstr[1] "%d verbonden apparaten"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth aan"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth uit"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Locatie ingeschakeld"
+
+#~ msgid "Disable"
+#~ msgstr "Uitschakelen"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Privacyinstellingen"
+
+#~ msgid "Location In Use"
+#~ msgstr "Locatie in gebruik"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Locatie uitgeschakeld"
+
+#~ msgid "Enable"
+#~ msgstr "Inschakelen"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s uit"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s verbonden"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s onbeheerd"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s verbinden"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s vereist authenticatie"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Firmware ontbreekt voor %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s niet beschikbaar"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Verbinding van %s is mislukt"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Instellingen voor bekabeld netwerk"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Instellingen voor mobiel breedband"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s uitgeschakeld in hardware"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s uitgeschakeld"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Met internet verbinden"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Vliegtuigstand is aan"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Wifi is uitgeschakeld in vliegtuigstand."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Vliegtuigstand uitschakelen"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wifi is uitgeschakeld"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Wifi moet ingeschakeld zijn om met een netwerk te kunnen verbinden."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Wifi inschakelen"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wifi-netwerken"
+
+#~ msgid "Select a network"
+#~ msgstr "Selecteer een netwerk"
+
+#~ msgid "No Networks"
+#~ msgstr "Geen netwerken"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Gebruik de hardware-schakelaar om uit te schakelen"
+
+#~ msgid "Select Network"
+#~ msgstr "Netwerk selecteren"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wifi-instellingen"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s niet verbonden"
+
+#~ msgid "connecting…"
+#~ msgstr "verbinden…"
+
+#~ msgid "authentication required"
+#~ msgstr "authenticatie nodig"
+
+#~ msgid "connection failed"
+#~ msgstr "verbinding mislukt"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN-instellingen"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN uit"
+
+#~ msgid "Network Settings"
+#~ msgstr "Netwerkinstellingen"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s bekabelde verbinding"
+#~ msgstr[1] "%s bekabelde verbindingen"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s wifi-verbinding"
+#~ msgstr[1] "%s wifi-verbindingen"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s modemverbinding"
+#~ msgstr[1] "%s modemverbindingen"
+
+#~ msgid "Connection failed"
+#~ msgstr "Verbinding mislukt"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Activeren van netwerkverbinding mislukt"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Nachtlicht uitgeschakeld"
+
+#~ msgid "Resume"
+#~ msgstr "Hervatten"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Uitschakelen tot morgen"
+
+#~ msgid "Power Settings"
+#~ msgstr "Energie-instellingen"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Volledig opgeladen"
+
+#~ msgid "Not Charging"
+#~ msgstr "Niet aan het opladen"
+
+#~ msgid "Estimating…"
+#~ msgstr "Schatten…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d:%02d resterend (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d:%02d tot opgeladen (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Scherm wordt gedeeld"
+
+#~ msgid "Turn off"
+#~ msgstr "Uitschakelen"
+
+#~ msgid "Lock"
+#~ msgstr "Vergrendelen"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Uitschakelen / afmelden"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Introspectie-API inschakelen"
+
+# ? - Nathan
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Schakelt een D-Bus-API in die u de mogelijkheid biedt een introspectie "
+#~ "uit te voeren op de toepassingsstatus van de shell."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Uit favorieten verwijderen"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Aan favorieten toevoegen"
+
+#~ msgid "Author"
+#~ msgstr "Auteur"
+
+#~ msgid "Warning"
+#~ msgstr "Waarschuwing"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Uitbreidingen kunnen problemen veroorzaken, inclusief prestatieproblemen. "
+#~ "Als u een probleem met uw systeem ondervindt, wordt het aanbevolen eerst "
+#~ "alle uitbreidingen uit te schakelen."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Verbinden met Gnome Shell mislukt"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimaliseren"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Herstellen"
+
+#~ msgid "App Picker View"
+#~ msgstr "Toepassingskiezer-weergave"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Index van de momenteel geselecteerde weergave in de toepassingskiezer."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Vaak gebruikte toepassingen verschijnen hier"
+
+#~ msgid "Frequent"
+#~ msgstr "Vaak gebruikt"
+
+#~ msgid "All"
+#~ msgstr "Alles"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A %-d %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A %-d %B %Y"
+
+#~ msgid "Off"
+#~ msgstr "Uit"
+
+#~ msgid "On"
+#~ msgstr "Aan"
+
+#~ msgid "Copy Error"
+#~ msgstr "Fout kopiëren"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Bladeren in Software"
+
+#~ msgid "Next"
+#~ msgstr "Volgende"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Aanmelden"
+
+#~ msgid "Rename"
+#~ msgstr "Hernoemen"
+
+#~ msgid "Password:"
+#~ msgstr "Wachtwoord:"
+
+#~ msgid "Type again:"
+#~ msgstr "Nogmaals:"
+
+#~ msgid "Password: "
+#~ msgstr "Wachtwoord: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Authenticatie nodig voor draadloos netwerk"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Netwerkwachtwoord voor mobiel breedband"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nieuw bericht"
+#~ msgstr[1] "%d nieuwe berichten"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d nieuwe notificatie"
+#~ msgstr[1] "%d nieuwe notificaties"
+
+#~ msgid "Account Settings"
+#~ msgstr "Accountinstellingen"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Oriëntatievergrendeling"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Sneltoets die alle actieve tweens pauzeert en hervat, voor debug-"
+#~ "doeleinden"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Het gebruikte toetsenbord"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Het te gebruiken type toetsenbord."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Fout bij laden van het voorkeurenvenster voor %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "De hele dag %s."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, daarna %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, daarna %s, gevolgd door %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Voelt aan als %s."
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Afmelden"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Gebruiker wisselen"
+
+#~ msgid "Hide tray"
+#~ msgstr "Systeemvak verbergen"
+
+#~ msgid "Status Icons"
+#~ msgstr "Statuspictogrammen"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Webauthenticatie-omleiding"
+
+#~ msgid "Events"
+#~ msgstr "Gebeurtenissen"
+
+#~ msgid "Notifications"
+#~ msgstr "Notificaties"
+
+#~ msgid "Media"
+#~ msgstr "Media"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Not In Use"
+#~ msgstr "Niet in gebruik"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Weeknummers tonen in kalender"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Indien ingeschakeld worden weeknummers in de kalender getoond."
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s vraagt toegang tot uw locatie."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Voor internetverbinding gebruiken"
diff --git a/po/nn.po b/po/nn.po
new file mode 100644
index 0000000..769153c
--- /dev/null
+++ b/po/nn.po
@@ -0,0 +1,1713 @@
+# Norwegian Nynorsk translation for gnome-shell.
+# Copyright (C) 2010 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Torstein Adolf Winterseth <kvikende@fsfe.org>, 2010.
+# Torstein Winterseth <kvikende@fsfe.org>, 2010.
+# Andreas N. <gedemiti@gmail.com>, 2011.
+# Åsmund Skjæveland <aasmunds@ulrik.uio.no>, 2011.
+# Kjartan Maraas <kmaraas@gnome.org>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-07-10 15:17+0200\n"
+"PO-Revision-Date: 2012-07-10 15:21+0200\n"
+"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
+"Language-Team: Norsk (nynorsk) <i18n-nn@lister.ping.uio.no>\n"
+"Language: nn\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Poedit-Language: Norwegian Nynorsk\n"
+"X-Poedit-Country: NORWAY\n"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "Vindaugshandsaming og programstarting"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+#: ../js/extensionPrefs/main.js:153
+msgid "GNOME Shell Extension Preferences"
+msgstr ""
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Slå på interne verktøy for utviklarar og testarar frå Alt + F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Tillèt tilgang til interne feilfiksings- og overvakingsverktøy gjennom «Alt "
+"+ F2»-dialogvindauget."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "Uuids of extensions to enable"
+msgstr "Uuid-ar til utvidingar som skal slåast på"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+#, fuzzy
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension DBus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell-utvidingar har ein uuid-eigenskap. Denne nøkkelen viser ei liste "
+"over utvidingane som skal lastast. disabled-extensions overstyrer denne "
+"innstillinga for utvidingar som finst i begge listene."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Whether to collect stats about applications usage"
+msgstr "Om statistikk om programbruk skal innsamlast"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+"Shell overvakar aktive program for å visa dei mest brukte (t.d. i "
+"oppstartaren). Sjølv om desse data vil haldast private, så kan det henda at "
+"du ønskjer å slå dette av med omsyn til personvern. Legg merke til at det "
+"vil ikkje fjerna data som alt er lagra."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Liste over skrivebordsfil-ID-ar for favorittprogram"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programma som korresponderer til desse identifikatorane vil verta viste i "
+"favorittområdet."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "disabled OpenSearch providers"
+msgstr "OpenSearch-tilbydarar slått av"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historikk for kommandovindauget (Alt + F2)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for the looking glass dialog"
+msgstr "Historikk for forstørringsglasvindauget"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid ""
+"Internally used to store the last IM presence explicitly set by the user. "
+"The value here is from the TpConnectionPresenceType enumeration."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid ""
+"Internally used to store the last session presence status for the user. The "
+"value here is from the GsmPresenceStatus enumeration."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid "Show the week date in the calendar"
+msgstr "Vis vekedag i kalenderen"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid "If true, display the ISO week date in the calendar."
+msgstr "Viss sann, vis ISO-vekedag i kalenderen."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid "Keybinding to open the application menu"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Keybinding to open the application menu."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "Keybinding to toggle the screen recorder"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to start/stop the builtin screen recorder."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Which keyboard to use"
+msgstr "Tastatur som skal brukast"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "The type of keyboard to use."
+msgstr "Type tastatur som skal brukast."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Framerate used for recording screencasts."
+msgstr "Biletrate brukt for filming av skjermvideo."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+"Biletfrekvensen til den ferdige skjermvideoen, teken opp med "
+"skjermvideoopptakaren i GNOME Shell, i bilete per sekund."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr "GStreamer-kanalen brukt til å koda skjermvideoen"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux' and records to WEBM "
+"using the VP8 codec. %T is used as a placeholder for a guess at the optimal "
+"thread count on the system."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "File extension used for storing the screencast"
+msgstr "Filtype brukt for å lagra skjermvideoen"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+"Filnamnet for skjermvideoopptak vil vera eit unikt filnamn basert på "
+"gjeldande dato, og vil bruka dette filetternamnet. Det bør endrast når du "
+"gjer opptak til eit anna oppbevaringsformat."
+
+#: ../js/extensionPrefs/main.js:125
+#, c-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr ""
+
+#: ../js/extensionPrefs/main.js:165
+msgid "<b>Extension</b>"
+msgstr ""
+
+#: ../js/extensionPrefs/main.js:189
+msgid "Select an extension to configure using the combobox above."
+msgstr ""
+
+#: ../js/gdm/loginDialog.js:629
+msgid "Session..."
+msgstr "Økt …"
+
+#: ../js/gdm/loginDialog.js:800
+msgctxt "title"
+msgid "Sign In"
+msgstr "Logg inn"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/loginDialog.js:845
+msgid "(or swipe finger)"
+msgstr "(eller dra finger)"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:866
+msgid "Not listed?"
+msgstr "Ikkje oppført?"
+
+#: ../js/gdm/loginDialog.js:1046 ../js/ui/endSessionDialog.js:410
+#: ../js/ui/extensionDownloader.js:138 ../js/ui/networkAgent.js:153
+#: ../js/ui/polkitAuthenticationAgent.js:176
+#: ../js/ui/shellMountOperation.js:339 ../js/ui/status/bluetooth.js:431
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: ../js/gdm/loginDialog.js:1051
+msgctxt "button"
+msgid "Sign In"
+msgstr "Logg inn"
+
+#: ../js/gdm/loginDialog.js:1411
+msgid "Login Window"
+msgstr "Innloggingsvindauge"
+
+#: ../js/gdm/powerMenu.js:130 ../js/ui/userMenu.js:612
+#: ../js/ui/userMenu.js:616 ../js/ui/userMenu.js:730
+msgid "Suspend"
+msgstr "Kvilemodus"
+
+#: ../js/gdm/powerMenu.js:135
+msgid "Restart"
+msgstr "Start på nytt"
+
+#: ../js/gdm/powerMenu.js:140 ../js/ui/userMenu.js:614
+#: ../js/ui/userMenu.js:616 ../js/ui/userMenu.js:729
+msgid "Power Off"
+msgstr "Slå av"
+
+#: ../js/misc/util.js:95
+msgid "Command not found"
+msgstr "Fann ikkje kommando"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:128
+msgid "Could not parse command:"
+msgstr "Klarte ikkje lesa kommando:"
+
+#: ../js/misc/util.js:136
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "Klarte ikkje køyra «%s»:"
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:252
+msgid "All"
+msgstr "Alle"
+
+#: ../js/ui/appDisplay.js:310
+msgid "APPLICATIONS"
+msgstr "PROGRAM"
+
+#: ../js/ui/appDisplay.js:370
+msgid "SETTINGS"
+msgstr "INNSTILLINGAR"
+
+#: ../js/ui/appDisplay.js:675
+msgid "New Window"
+msgstr "Nytt vindauge"
+
+#: ../js/ui/appDisplay.js:678
+msgid "Remove from Favorites"
+msgstr "Fjern frå favorittar"
+
+#: ../js/ui/appDisplay.js:679
+msgid "Add to Favorites"
+msgstr "Legg til i favorittar"
+
+#: ../js/ui/appFavorites.js:87
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "%s har vorte lagt til i dine favorittar."
+
+#: ../js/ui/appFavorites.js:118
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "%s har vorte fjerna frå dine favorittar."
+
+#: ../js/ui/autorunManager.js:261
+msgid "Removable Devices"
+msgstr "Flyttbare einingar"
+
+#: ../js/ui/autorunManager.js:549
+#, c-format
+msgid "Open with %s"
+msgstr "Opna med %s"
+
+#: ../js/ui/autorunManager.js:575
+msgid "Eject"
+msgstr "Løys ut"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:62
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Heile dagen"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H.%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:74
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l.%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:114
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:116
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:118
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:120
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "O"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:122
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:124
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:126
+msgctxt "grid saturday"
+msgid "S"
+msgstr "L"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:139
+msgctxt "list sunday"
+msgid "Su"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:141
+msgctxt "list monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:143
+msgctxt "list tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:145
+msgctxt "list wednesday"
+msgid "W"
+msgstr "O"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:147
+msgctxt "list thursday"
+msgid "Th"
+msgstr "To"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:149
+msgctxt "list friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:151
+msgctxt "list saturday"
+msgid "S"
+msgstr "L"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:681
+msgid "Nothing Scheduled"
+msgstr "Ingenting er planlagt"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:697
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A %d. %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:700
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A %d. %B %Y"
+
+#: ../js/ui/calendar.js:710
+msgid "Today"
+msgstr "I dag"
+
+#: ../js/ui/calendar.js:714
+msgid "Tomorrow"
+msgstr "I morgon"
+
+#: ../js/ui/calendar.js:723
+msgid "This week"
+msgstr "Denne veka"
+
+#: ../js/ui/calendar.js:731
+msgid "Next week"
+msgstr "Neste veke"
+
+#: ../js/ui/dash.js:239 ../js/ui/messageTray.js:1287
+msgid "Remove"
+msgstr "Fjern"
+
+#: ../js/ui/dateMenu.js:94
+msgid "Date and Time Settings"
+msgstr "Innstillingar for dato og tid"
+
+#: ../js/ui/dateMenu.js:120
+msgid "Open Calendar"
+msgstr "Opna kalender"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:161
+msgid "%A %B %e, %Y"
+msgstr "%a %e. %B %Y"
+
+#: ../js/ui/endSessionDialog.js:60
+#, c-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Logg ut %s"
+
+#: ../js/ui/endSessionDialog.js:61
+msgctxt "title"
+msgid "Log Out"
+msgstr "Logg ut"
+
+#: ../js/ui/endSessionDialog.js:62
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr "Vel «Logg ut» for å avslutta desse programma og logga ut av systemet."
+
+#: ../js/ui/endSessionDialog.js:64
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s vil verta logga ut automatisk om %d sekund."
+msgstr[1] "%s vil verta logga ut automatisk om %d sekund."
+
+#: ../js/ui/endSessionDialog.js:69
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Du vil verta logga ut automatisk om %d sekund."
+msgstr[1] "Du vil verta logga ut automatisk om %d sekund."
+
+#: ../js/ui/endSessionDialog.js:73
+msgid "Logging out of the system."
+msgstr "Loggar ut av systemet."
+
+#: ../js/ui/endSessionDialog.js:75
+msgctxt "button"
+msgid "Log Out"
+msgstr "Logg ut"
+
+#: ../js/ui/endSessionDialog.js:80
+msgctxt "title"
+msgid "Power Off"
+msgstr "Slå av"
+
+#: ../js/ui/endSessionDialog.js:81
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr "Vel «Slå av» for å avslutta desse programma og slå av systemet."
+
+#: ../js/ui/endSessionDialog.js:83
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Systemet vil slå seg av automatisk om %d sekund."
+msgstr[1] "Systemet vil slå seg av automatisk om %d sekund."
+
+#: ../js/ui/endSessionDialog.js:87
+msgid "Powering off the system."
+msgstr "Slår av systemet."
+
+#: ../js/ui/endSessionDialog.js:89 ../js/ui/endSessionDialog.js:106
+msgctxt "button"
+msgid "Restart"
+msgstr "Start på nytt"
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "button"
+msgid "Power Off"
+msgstr "Slå av"
+
+#: ../js/ui/endSessionDialog.js:97
+msgctxt "title"
+msgid "Restart"
+msgstr "Start på nytt"
+
+#: ../js/ui/endSessionDialog.js:98
+msgid "Click Restart to quit these applications and restart the system."
+msgstr ""
+"Vel «Start på nytt» for å avslutta desse programma og starta systemet på "
+"nytt."
+
+#: ../js/ui/endSessionDialog.js:100
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Systemet vil startast på nytt om %d sekund."
+msgstr[1] "Systemet vil startast på nytt om %d sekund."
+
+#: ../js/ui/endSessionDialog.js:104
+msgid "Restarting the system."
+msgstr "Startar systemet på nytt."
+
+#: ../js/ui/extensionDownloader.js:142
+msgid "Install"
+msgstr "Installer"
+
+#: ../js/ui/extensionDownloader.js:146
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr "Lasta ned og installera «%s» frå extensions.gnome.org?"
+
+#: ../js/ui/keyboard.js:327
+msgid "tray"
+msgstr "varslingsområde"
+
+#: ../js/ui/keyboard.js:545 ../js/ui/status/keyboard.js:42
+#: ../js/ui/status/power.js:203
+msgid "Keyboard"
+msgstr "Tastatur"
+
+#: ../js/ui/keyringPrompt.js:85 ../js/ui/polkitAuthenticationAgent.js:273
+msgid "Password:"
+msgstr "Passord:"
+
+#: ../js/ui/keyringPrompt.js:101
+msgid "Type again:"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:696
+msgid "No extensions installed"
+msgstr "Ingen utvidingar er installerte"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:750
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:756
+msgid "Hide Errors"
+msgstr "Gøym feil"
+
+#: ../js/ui/lookingGlass.js:760 ../js/ui/lookingGlass.js:811
+msgid "Show Errors"
+msgstr "Vis feil"
+
+#: ../js/ui/lookingGlass.js:769
+msgid "Enabled"
+msgstr "Påslått"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:772 ../src/gvc/gvc-mixer-control.c:1082
+msgid "Disabled"
+msgstr "Avslått"
+
+#: ../js/ui/lookingGlass.js:774
+msgid "Error"
+msgstr "Feil"
+
+#: ../js/ui/lookingGlass.js:776
+msgid "Out of date"
+msgstr "Utgått på dato"
+
+#: ../js/ui/lookingGlass.js:778
+msgid "Downloading"
+msgstr "Lastar ned"
+
+#: ../js/ui/lookingGlass.js:799
+msgid "View Source"
+msgstr "Vis kjeldekode"
+
+#: ../js/ui/lookingGlass.js:805
+msgid "Web Page"
+msgstr "Nettside"
+
+#. Translators: this is a filename used for screencast recording
+#: ../js/ui/main.js:129
+#, no-c-format
+msgid "Screencast from %d %t"
+msgstr ""
+
+#: ../js/ui/messageTray.js:1280
+msgid "Open"
+msgstr "Opna"
+
+#: ../js/ui/messageTray.js:1297
+msgid "Unmute"
+msgstr "Fjern demping"
+
+#: ../js/ui/messageTray.js:1297
+msgid "Mute"
+msgstr "Demp"
+
+#: ../js/ui/messageTray.js:2575
+msgid "System Information"
+msgstr "Systeminformasjon"
+
+#: ../js/ui/networkAgent.js:148
+msgid "Connect"
+msgstr "Kopla til"
+
+#. Cisco LEAP
+#: ../js/ui/networkAgent.js:243 ../js/ui/networkAgent.js:255
+#: ../js/ui/networkAgent.js:282 ../js/ui/networkAgent.js:302
+#: ../js/ui/networkAgent.js:312
+msgid "Password: "
+msgstr "Passord:"
+
+#. static WEP
+#: ../js/ui/networkAgent.js:248
+msgid "Key: "
+msgstr "Nøkkel:"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/ui/networkAgent.js:280 ../js/ui/networkAgent.js:298
+msgid "Username: "
+msgstr "Brukarnamn:"
+
+#: ../js/ui/networkAgent.js:286
+msgid "Identity: "
+msgstr "Identitet:"
+
+#: ../js/ui/networkAgent.js:288
+msgid "Private key password: "
+msgstr "Passord for privat nøkkel:"
+
+#: ../js/ui/networkAgent.js:300
+msgid "Service: "
+msgstr "Teneste:"
+
+#: ../js/ui/networkAgent.js:329
+msgid "Authentication required by wireless network"
+msgstr "Trådlaust nettverk krev autentisering"
+
+#: ../js/ui/networkAgent.js:330
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"'%s'."
+msgstr ""
+"Passord eller krypteringsnøklar er nødvendig for å kopla til det trådlause "
+"nettverket «%s»."
+
+#: ../js/ui/networkAgent.js:334
+msgid "Wired 802.1X authentication"
+msgstr "Kabla 802.1X-autentisering"
+
+#: ../js/ui/networkAgent.js:336
+msgid "Network name: "
+msgstr "Nettverksnamn:"
+
+#: ../js/ui/networkAgent.js:341
+msgid "DSL authentication"
+msgstr "DSL-autentisering"
+
+#: ../js/ui/networkAgent.js:348
+msgid "PIN code required"
+msgstr "PIN-kode påkravd"
+
+#: ../js/ui/networkAgent.js:349
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Mobilt breibandeining krev PIN-kode"
+
+#: ../js/ui/networkAgent.js:350
+msgid "PIN: "
+msgstr "PIN:"
+
+#: ../js/ui/networkAgent.js:356
+msgid "Mobile broadband network password"
+msgstr "Nettverkspassord for mobilt breiband"
+
+#: ../js/ui/networkAgent.js:357
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr "Passord er nødvendig for å kopla til «%s»."
+
+#: ../js/ui/notificationDaemon.js:486 ../src/shell-app.c:374
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ukjend"
+
+#: ../js/ui/overview.js:88
+msgid "Undo"
+msgstr "Angra"
+
+#: ../js/ui/overview.js:128
+msgid "Overview"
+msgstr "Oversikt"
+
+#: ../js/ui/overview.js:198
+msgid "Windows"
+msgstr "Vindauge"
+
+#: ../js/ui/overview.js:201
+msgid "Applications"
+msgstr "Program"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:226
+msgid "Dash"
+msgstr "Favorittar"
+
+#: ../js/ui/panel.js:564
+msgid "Quit"
+msgstr "Avslutt"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:596
+msgid "Activities"
+msgstr "Aktivitetar"
+
+#: ../js/ui/panel.js:962
+msgid "Top Bar"
+msgstr "Toppanel"
+
+#: ../js/ui/placeDisplay.js:115
+#, c-format
+msgid "Failed to unmount '%s'"
+msgstr "Klarte ikkje avmontera «%s»"
+
+#: ../js/ui/placeDisplay.js:118
+msgid "Retry"
+msgstr "Prøv på nytt"
+
+#: ../js/ui/placeDisplay.js:156
+msgid "Connect to..."
+msgstr "Kopla til …"
+
+#: ../js/ui/placeDisplay.js:367
+msgid "PLACES & DEVICES"
+msgstr "PLASSERING OG EININGAR"
+
+#: ../js/ui/polkitAuthenticationAgent.js:71
+msgid "Authentication Required"
+msgstr "Autentisering påkravd"
+
+#: ../js/ui/polkitAuthenticationAgent.js:109
+msgid "Administrator"
+msgstr "Administrator"
+
+#: ../js/ui/polkitAuthenticationAgent.js:180
+msgid "Authenticate"
+msgstr "Autentiser"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/polkitAuthenticationAgent.js:261
+#: ../js/ui/shellMountOperation.js:324
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Det verka ikkje. Prøv på nytt."
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:728
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:205
+msgid "Please enter a command:"
+msgstr "Skriv inn kommando:"
+
+#: ../js/ui/searchDisplay.js:320
+msgid "Searching..."
+msgstr "Søker …"
+
+#: ../js/ui/searchDisplay.js:373
+msgid "No matching results."
+msgstr "Ingen treff."
+
+#: ../js/ui/shellEntry.js:26
+msgid "Copy"
+msgstr "Kopier"
+
+#: ../js/ui/shellEntry.js:31
+msgid "Paste"
+msgstr "Lim inn"
+
+#: ../js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Vis tekst"
+
+#: ../js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Gøym tekst"
+
+#: ../js/ui/shellMountOperation.js:311
+msgid "Passphrase"
+msgstr ""
+
+#: ../js/ui/shellMountOperation.js:332
+msgid "Remember Passphrase"
+msgstr ""
+
+#: ../js/ui/shellMountOperation.js:343
+#, fuzzy
+msgid "Unlock"
+msgstr "Klokke"
+
+#: ../js/ui/status/accessibility.js:47
+msgid "Accessibility"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:52
+msgid "Zoom"
+msgstr "Forstørring"
+
+#: ../js/ui/status/accessibility.js:59
+msgid "Screen Reader"
+msgstr "Skjermlesar"
+
+#: ../js/ui/status/accessibility.js:63
+msgid "Screen Keyboard"
+msgstr "Skjermtastatur"
+
+#: ../js/ui/status/accessibility.js:67
+msgid "Visual Alerts"
+msgstr "Synleg varsling"
+
+#: ../js/ui/status/accessibility.js:70
+msgid "Sticky Keys"
+msgstr "Faste valtastar"
+
+#: ../js/ui/status/accessibility.js:73
+msgid "Slow Keys"
+msgstr "Treige tastar"
+
+#: ../js/ui/status/accessibility.js:76
+msgid "Bounce Keys"
+msgstr "Sprettetastar"
+
+#: ../js/ui/status/accessibility.js:79
+msgid "Mouse Keys"
+msgstr "Musetastar"
+
+#: ../js/ui/status/accessibility.js:83
+msgid "Universal Access Settings"
+msgstr "Innstillingar for tilgjenge"
+
+#: ../js/ui/status/accessibility.js:117
+msgid "High Contrast"
+msgstr "Høg kontrast"
+
+#: ../js/ui/status/accessibility.js:154
+msgid "Large Text"
+msgstr "Stor tekst"
+
+#: ../js/ui/status/bluetooth.js:31 ../js/ui/status/bluetooth.js:35
+#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/bluetooth.js:308
+#: ../js/ui/status/bluetooth.js:339 ../js/ui/status/bluetooth.js:375
+#: ../js/ui/status/bluetooth.js:404 ../js/ui/status/network.js:844
+msgid "Bluetooth"
+msgstr "Blåtann"
+
+#: ../js/ui/status/bluetooth.js:48
+msgid "Visibility"
+msgstr "Synleg"
+
+#: ../js/ui/status/bluetooth.js:62
+msgid "Send Files to Device..."
+msgstr "Send filer til eining …"
+
+#: ../js/ui/status/bluetooth.js:63
+msgid "Set up a New Device..."
+msgstr "Set opp ei ny eining …"
+
+#: ../js/ui/status/bluetooth.js:87
+msgid "Bluetooth Settings"
+msgstr "Innstillingar for blåtann"
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:107 ../js/ui/status/network.js:211
+msgid "hardware disabled"
+msgstr "eining slått av"
+
+#: ../js/ui/status/bluetooth.js:200
+msgid "Connection"
+msgstr "Tilkopling"
+
+#: ../js/ui/status/bluetooth.js:211 ../js/ui/status/network.js:445
+msgid "disconnecting..."
+msgstr "koplar frå …"
+
+#: ../js/ui/status/bluetooth.js:224 ../js/ui/status/network.js:451
+#: ../js/ui/status/network.js:911
+msgid "connecting..."
+msgstr "koplar til …"
+
+#: ../js/ui/status/bluetooth.js:242
+msgid "Send Files..."
+msgstr "Send filer …"
+
+#: ../js/ui/status/bluetooth.js:247
+msgid "Browse Files..."
+msgstr "Bla gjennom filer …"
+
+#: ../js/ui/status/bluetooth.js:256
+msgid "Error browsing device"
+msgstr "Klarte ikkje lesa eining"
+
+#: ../js/ui/status/bluetooth.js:257
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr "Kan ikkje bla gjennom eininga. Feilen er «%s»"
+
+#: ../js/ui/status/bluetooth.js:265
+msgid "Keyboard Settings"
+msgstr "Innstillingar for tastatur"
+
+#: ../js/ui/status/bluetooth.js:268
+msgid "Mouse Settings"
+msgstr "Innstillingar for mus"
+
+#: ../js/ui/status/bluetooth.js:273 ../js/ui/status/volume.js:59
+msgid "Sound Settings"
+msgstr "Innstillingar for lyd"
+
+#: ../js/ui/status/bluetooth.js:340
+#, c-format
+msgid "Authorization request from %s"
+msgstr "Førespurnad om autorisering frå %s"
+
+#: ../js/ui/status/bluetooth.js:346
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "Eininga %s vil ha tilgang til tenesta «%s»"
+
+#: ../js/ui/status/bluetooth.js:348
+msgid "Always grant access"
+msgstr "Gje tilgang alltid"
+
+#: ../js/ui/status/bluetooth.js:349
+msgid "Grant this time only"
+msgstr "Gje tilgang berre denne gongen"
+
+#: ../js/ui/status/bluetooth.js:350 ../js/ui/telepathyClient.js:1097
+msgid "Reject"
+msgstr "Avvis"
+
+#: ../js/ui/status/bluetooth.js:376
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "Tilkopling for %s stadfesta"
+
+#: ../js/ui/status/bluetooth.js:382 ../js/ui/status/bluetooth.js:412
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "Eininga %s vil kopla seg saman med denne datamaskina"
+
+#: ../js/ui/status/bluetooth.js:383
+#, c-format
+msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+msgstr "Kontroller at PIN-koden «%06d» samsvarer med koden på eininga."
+
+#: ../js/ui/status/bluetooth.js:385
+msgid "Matches"
+msgstr "Samsvarer"
+
+#: ../js/ui/status/bluetooth.js:386
+msgid "Does not match"
+msgstr "Samsvarer ikkje"
+
+#: ../js/ui/status/bluetooth.js:405
+#, c-format
+msgid "Pairing request for %s"
+msgstr "Førespurnad om tilkopling for %s"
+
+#: ../js/ui/status/bluetooth.js:413
+msgid "Please enter the PIN mentioned on the device."
+msgstr "Skriv inn PIN-koden oppgjeve på eininga."
+
+#: ../js/ui/status/bluetooth.js:430
+msgid "OK"
+msgstr "OK"
+
+#: ../js/ui/status/keyboard.js:69
+msgid "Show Keyboard Layout"
+msgstr "Vis tastaturutforming"
+
+#: ../js/ui/status/keyboard.js:71
+msgid "Region and Language Settings"
+msgstr "Innstillingar for region og språk"
+
+#: ../js/ui/status/network.js:96
+msgid "<unknown>"
+msgstr "<ukjend>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:233
+msgid "disabled"
+msgstr "slått av"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:443
+msgid "unmanaged"
+msgstr "ikkje handsama"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:454 ../js/ui/status/network.js:914
+msgid "authentication required"
+msgstr "autentisering påkravd"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:464
+msgid "firmware missing"
+msgstr "fastvare manglar"
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:471
+msgid "cable unplugged"
+msgstr "kabel kopla frå"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:476
+msgid "unavailable"
+msgstr "utilgjengeleg"
+
+#: ../js/ui/status/network.js:478 ../js/ui/status/network.js:916
+msgid "connection failed"
+msgstr "klarte ikkje kopla til"
+
+#: ../js/ui/status/network.js:539 ../js/ui/status/network.js:1537
+msgid "More..."
+msgstr "Meir …"
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:575 ../js/ui/status/network.js:1467
+msgid "Connected (private)"
+msgstr "Tilkopla (privat)"
+
+#: ../js/ui/status/network.js:650
+msgid "Auto Ethernet"
+msgstr "Automatisk Ethernet"
+
+#: ../js/ui/status/network.js:708
+msgid "Auto broadband"
+msgstr "Automatisk breiband"
+
+#: ../js/ui/status/network.js:711
+msgid "Auto dial-up"
+msgstr "Automatisk oppringd"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:830 ../js/ui/status/network.js:1484
+#, c-format
+msgid "Auto %s"
+msgstr "Automatisk %s"
+
+#: ../js/ui/status/network.js:832
+msgid "Auto bluetooth"
+msgstr "Automatisk blåtann"
+
+#: ../js/ui/status/network.js:1486
+msgid "Auto wireless"
+msgstr "Automatisk trådlaus"
+
+#: ../js/ui/status/network.js:1595
+msgid "Enable networking"
+msgstr "Slå på nettverk"
+
+#: ../js/ui/status/network.js:1617
+msgid "Wired"
+msgstr "Kabla"
+
+#: ../js/ui/status/network.js:1628
+msgid "Wireless"
+msgstr "Trådlaus"
+
+#: ../js/ui/status/network.js:1638
+msgid "Mobile broadband"
+msgstr "Mobilt breiband"
+
+#: ../js/ui/status/network.js:1648
+msgid "VPN Connections"
+msgstr "VPN-tilkoplingar"
+
+#: ../js/ui/status/network.js:1655
+msgid "Network Settings"
+msgstr "Innstillingar for nettverk"
+
+#: ../js/ui/status/network.js:1703
+msgid "Network Manager"
+msgstr "Nettverkshandsaming"
+
+#: ../js/ui/status/network.js:1796
+msgid "Connection failed"
+msgstr "Klarte ikkje kopla til"
+
+#: ../js/ui/status/network.js:1797
+msgid "Activation of network connection failed"
+msgstr "Klarte ikkje slå på nettverkstilkopling"
+
+#: ../js/ui/status/network.js:2060
+msgid "Networking is disabled"
+msgstr "Nettverk er slått av"
+
+#: ../js/ui/status/power.js:59
+msgid "Battery"
+msgstr "Batteri"
+
+#: ../js/ui/status/power.js:76
+msgid "Power Settings"
+msgstr "Innstillingar for straum"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:98
+msgid "Estimating..."
+msgstr "Reknar ut …"
+
+#: ../js/ui/status/power.js:105
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "%d time står att"
+msgstr[1] "%d timar står att"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:108
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "%d %s %d %s står att"
+
+#: ../js/ui/status/power.js:110
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "time"
+msgstr[1] "timar"
+
+#: ../js/ui/status/power.js:110
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "minutt"
+msgstr[1] "minutt"
+
+#: ../js/ui/status/power.js:113
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "%d minutt står att"
+msgstr[1] "%d minutt står att"
+
+#: ../js/ui/status/power.js:116 ../js/ui/status/power.js:186
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d %%"
+
+#: ../js/ui/status/power.js:193
+msgid "AC adapter"
+msgstr "Straumadapter"
+
+#: ../js/ui/status/power.js:195
+msgid "Laptop battery"
+msgstr "Batteri"
+
+#: ../js/ui/status/power.js:197
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:199
+msgid "Monitor"
+msgstr "Skjerm"
+
+#: ../js/ui/status/power.js:201
+msgid "Mouse"
+msgstr "Mus"
+
+#: ../js/ui/status/power.js:205
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:207
+msgid "Cell phone"
+msgstr "Mobiltelefon"
+
+#: ../js/ui/status/power.js:209
+msgid "Media player"
+msgstr "Mediaspelar"
+
+#: ../js/ui/status/power.js:211
+msgid "Tablet"
+msgstr "Nettbrett"
+
+#: ../js/ui/status/power.js:213
+msgid "Computer"
+msgstr "Datamaskin"
+
+#: ../js/ui/status/power.js:215
+msgctxt "device"
+msgid "Unknown"
+msgstr "Ukjend"
+
+#. Translators: This is the label for audio volume
+#: ../js/ui/status/volume.js:25 ../js/ui/status/volume.js:39
+msgid "Volume"
+msgstr "Lydstyrke"
+
+#: ../js/ui/status/volume.js:51
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/telepathyClient.js:226
+msgid "Invitation"
+msgstr "Invitasjon"
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:284
+msgid "Call"
+msgstr "Ring"
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:300
+msgid "File Transfer"
+msgstr "Filoverføring"
+
+#: ../js/ui/telepathyClient.js:381
+msgid "Subscription request"
+msgstr "Førespurnad om abonnering"
+
+#: ../js/ui/telepathyClient.js:418
+msgid "Connection error"
+msgstr "Tilkoplingsfeil"
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/telepathyClient.js:908
+#, no-c-format
+msgid "Sent at <b>%X</b> on <b>%A</b>"
+msgstr "Sendt <b>%A</b> <b>%X</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25",
+#. shown when you get a chat message in the same year.
+#: ../js/ui/telepathyClient.js:914
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+msgstr "Sendt <b>%A</b> <b>%d. %B</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
+#. shown when you get a chat message in a different year.
+#: ../js/ui/telepathyClient.js:919
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+msgstr "Sendt <b>%A</b> <b>%d. %B</b> %Y"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/telepathyClient.js:948
+#, c-format
+msgid "%s is now known as %s"
+msgstr "%s er no kjend som %s"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1048
+#, c-format
+msgid "Invitation to %s"
+msgstr "Invitasjon til %s"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/telepathyClient.js:1056
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr "%s inviterer deg til å vera med i %s"
+
+#: ../js/ui/telepathyClient.js:1058 ../js/ui/telepathyClient.js:1137
+#: ../js/ui/telepathyClient.js:1201
+msgid "Decline"
+msgstr "Avslå"
+
+#: ../js/ui/telepathyClient.js:1059 ../js/ui/telepathyClient.js:1138
+#: ../js/ui/telepathyClient.js:1202
+msgid "Accept"
+msgstr "Godta"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1089
+#, c-format
+msgid "Video call from %s"
+msgstr "Videooppringing frå %s"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1092
+#, c-format
+msgid "Call from %s"
+msgstr "Oppringing frå %s"
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/telepathyClient.js:1099
+msgid "Answer"
+msgstr "Svar"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/telepathyClient.js:1131
+#, c-format
+msgid "%s is sending you %s"
+msgstr "%s sender deg %s"
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/telepathyClient.js:1166
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s ønskjer å sjå når du er tilkopla"
+
+#: ../js/ui/telepathyClient.js:1259
+msgid "Network error"
+msgstr "Nettverksfeil"
+
+#: ../js/ui/telepathyClient.js:1261
+msgid "Authentication failed"
+msgstr "Feil under autentisering"
+
+#: ../js/ui/telepathyClient.js:1263
+msgid "Encryption error"
+msgstr "Krypteringsfeil"
+
+#: ../js/ui/telepathyClient.js:1265
+msgid "Certificate not provided"
+msgstr "Sertifikat ikkje oppgjeve"
+
+#: ../js/ui/telepathyClient.js:1267
+msgid "Certificate untrusted"
+msgstr "Sertifikatet er upåliteleg"
+
+#: ../js/ui/telepathyClient.js:1269
+msgid "Certificate expired"
+msgstr "Sertifikatet er utgått"
+
+#: ../js/ui/telepathyClient.js:1271
+msgid "Certificate not activated"
+msgstr "Sertifikatet er ikkje slått på"
+
+#: ../js/ui/telepathyClient.js:1273
+msgid "Certificate hostname mismatch"
+msgstr "Feil vertsnamn for sertifikatet"
+
+#: ../js/ui/telepathyClient.js:1275
+msgid "Certificate fingerprint mismatch"
+msgstr "Fingeravtrykket til sertifikatet er feil"
+
+#: ../js/ui/telepathyClient.js:1277
+msgid "Certificate self-signed"
+msgstr "Sertifikatet er sjølvsignert"
+
+#: ../js/ui/telepathyClient.js:1279
+msgid "Status is set to offline"
+msgstr "Status er sett til fråkopla"
+
+#: ../js/ui/telepathyClient.js:1281
+msgid "Encryption is not available"
+msgstr "Kryptering er ikkje tilgjengeleg"
+
+#: ../js/ui/telepathyClient.js:1283
+msgid "Certificate is invalid"
+msgstr "Sertifikatet er ugyldig"
+
+#: ../js/ui/telepathyClient.js:1285
+msgid "Connection has been refused"
+msgstr "Tilkopling nekta"
+
+#: ../js/ui/telepathyClient.js:1287
+msgid "Connection can't be established"
+msgstr "Kan ikkje kopla til"
+
+#: ../js/ui/telepathyClient.js:1289
+msgid "Connection has been lost"
+msgstr "Mista tilkoplinga"
+
+#: ../js/ui/telepathyClient.js:1291
+msgid "This account is already connected to the server"
+msgstr "Denne kontoen er allereie kopla til tenaren"
+
+#: ../js/ui/telepathyClient.js:1293
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "Tilkoplinga er bytt ut med ei ny tilkopling som bruker same ressurs"
+
+#: ../js/ui/telepathyClient.js:1295
+msgid "The account already exists on the server"
+msgstr "Kontoen finst allereie på tenaren"
+
+#: ../js/ui/telepathyClient.js:1297
+msgid "Server is currently too busy to handle the connection"
+msgstr "Tenaren er for oppteken til å ta i mot tilkoplinga"
+
+#: ../js/ui/telepathyClient.js:1299
+msgid "Certificate has been revoked"
+msgstr "Sertifikatet er trekt tilbake"
+
+#: ../js/ui/telepathyClient.js:1301
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"Sertifikatet brukar ei usikker sifferalgoritme eller er kryptografisk svakt"
+
+#: ../js/ui/telepathyClient.js:1303
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"Lengda på tenarsertifikatet eller djupna på sertifikatkjeda oversteig grensa "
+"som er sett i kryptografibiblioteket"
+
+#: ../js/ui/telepathyClient.js:1305
+msgid "Internal error"
+msgstr "Intern feil"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1315
+#, c-format
+msgid "Connection to %s failed"
+msgstr "Klarte ikkje kopla til %s"
+
+#: ../js/ui/telepathyClient.js:1324
+msgid "Reconnect"
+msgstr "Kopla til på nytt"
+
+#: ../js/ui/telepathyClient.js:1325
+msgid "Edit account"
+msgstr "Endra konto"
+
+#: ../js/ui/telepathyClient.js:1371
+msgid "Unknown reason"
+msgstr "Ukjend årsak"
+
+#: ../js/ui/userMenu.js:130
+msgid "Available"
+msgstr "Tilgjengeleg"
+
+#: ../js/ui/userMenu.js:133
+msgid "Busy"
+msgstr "Oppteken"
+
+#: ../js/ui/userMenu.js:136
+msgid "Invisible"
+msgstr "Usynleg"
+
+#: ../js/ui/userMenu.js:139
+msgid "Away"
+msgstr "Vekke"
+
+#: ../js/ui/userMenu.js:142
+msgid "Idle"
+msgstr "Uverksam"
+
+#: ../js/ui/userMenu.js:145
+msgid "Unavailable"
+msgstr "Utilgjengeleg"
+
+#: ../js/ui/userMenu.js:567 ../js/ui/userMenu.js:706
+msgid "Switch User"
+msgstr "Byt brukar"
+
+#: ../js/ui/userMenu.js:568
+msgid "Switch Session"
+msgstr "Byt økt"
+
+#: ../js/ui/userMenu.js:689
+msgid "Notifications"
+msgstr "Varslingar"
+
+#: ../js/ui/userMenu.js:698
+msgid "System Settings"
+msgstr "Systeminnstillingar"
+
+#: ../js/ui/userMenu.js:711
+msgid "Log Out"
+msgstr "Logg ut"
+
+#: ../js/ui/userMenu.js:716
+msgid "Lock"
+msgstr ""
+
+#: ../js/ui/userMenu.js:724
+msgid "Install Updates & Restart"
+msgstr ""
+
+#: ../js/ui/userMenu.js:749
+msgid "Your chat status will be set to busy"
+msgstr "Nettpratstatusen din vil verta sett til oppteken"
+
+#: ../js/ui/userMenu.js:750
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+"Varslingar er no slått av, òg for nettpratmeldingar. Nettstatusen din er "
+"endra, slik at andre får vite at du kanskje ikkje ser meldingane deira."
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/viewSelector.js:113
+msgid "Type to search..."
+msgstr "Skriv for å søkja …"
+
+#: ../js/ui/viewSelector.js:131 ../src/shell-util.c:253
+msgid "Search"
+msgstr "Søk"
+
+#: ../js/ui/wanda.js:124
+#, c-format
+msgid ""
+"Sorry, no wisdom for you today:\n"
+"%s"
+msgstr ""
+
+#: ../js/ui/wanda.js:128
+#, c-format
+msgid "%s the Oracle says"
+msgstr ""
+
+#: ../js/ui/wanda.js:168
+msgid "Your favorite Easter Egg"
+msgstr ""
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, c-format
+msgid "'%s' is ready"
+msgstr "«%s» er klar"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution kalender"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1089
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u utgang"
+msgstr[1] "%u utgangar"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1099
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u inngang"
+msgstr[1] "%u inngangar"
+
+#: ../src/gvc/gvc-mixer-control.c:1397
+msgid "System Sounds"
+msgstr "Systemlydar"
+
+#: ../src/main.c:327
+msgid "Print version"
+msgstr "Skriv ut versjon"
+
+#: ../src/main.c:333
+msgid "Mode used by GDM for login screen"
+msgstr "Modus brukt av GDM for innloggingskjermen"
+
+#: ../src/main.c:339
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr ""
+
+#: ../src/main.c:345
+msgid "List possible modes"
+msgstr ""
+
+#: ../src/shell-app.c:622
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr "Klarte ikkje starta «%s»"
+
+#: ../src/shell-keyring-prompt.c:708
+msgid "Passwords do not match."
+msgstr "Passorda samsvarer ikkje."
+
+#: ../src/shell-keyring-prompt.c:716
+msgid "Password cannot be blank"
+msgstr ""
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr "Storbritannia"
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr "Standard"
+
+#: ../src/shell-polkit-authentication-agent.c:340
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Autentiseringsvindauget vart lata att av brukaren"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:97
+msgid "Home"
+msgstr "Heim"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:107
+msgid "File System"
+msgstr "Filsystem"
+
+#. Translators: the first string is the name of a gvfs
+#. * method, and the second string is a path. For
+#. * example, "Trash: some-directory". It means that the
+#. * directory called "some-directory" is in the trash.
+#.
+#: ../src/shell-util.c:303
+#, c-format
+msgid "%1$s: %2$s"
+msgstr "%1$s: %2$s"
diff --git a/po/oc.po b/po/oc.po
new file mode 100644
index 0000000..cf6aef4
--- /dev/null
+++ b/po/oc.po
@@ -0,0 +1,4141 @@
+# Occitan translations for gnome-shell package.
+# Copyright (C) 2009-2015 Listed translators
+# This file is distributed under the same license as the gnome-shell package.
+# Cédric Valmary (Tot en Òc) <cvalmary@yahoo.fr>, 2015.
+# Cédric Valmary (totenoc.eu) <cvalmary@yahoo.fr>, 2016, 2018.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master oc\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-14 22:12+0000\n"
+"PO-Revision-Date: 2022-09-16 21:21+0200\n"
+"Last-Translator: Quentin PAGÈS\n"
+"Language-Team: Tot En Òc\n"
+"Language: oc\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 3.1.1\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Aviadors"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Activar l’aplicacion favorita 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Activar l’aplicacion favorita 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Activar l’aplicacion favorita 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Activar l’aplicacion favorita 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Activar l’aplicacion favorita 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Activar l’aplicacion favorita 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Activar l’aplicacion favorita 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Activar l’aplicacion favorita 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Activar l’aplicacion favorita 10"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Capturas d'ecran"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Prendre una captura d’ecran interactiu"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Prendre una captura d'ecran"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Prendre una captura d'ecran d'una fenèstra"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Enregistrar una vidèo d'ecran interactiu"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistèma"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Afichar la lista de las notificacions"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Balhar lo focus a la notificacion activa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Afichar la vista d'ensemble"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Afichar totas las aplicacions"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Dobrir lo menú de l'aplicacion"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gestion de las fenèstras e aviada de las aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Activar las aisinas intèrnas pels desvolopaires e los testaires dempuèi Alt-"
+"F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Autorizar l'accès a las aisinas intèrnas de desbugatge e de susvelhança "
+"gràcia a la bóstia de dialòg d'aviada d'aplicacion (Alt-F2)."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Identificants UUID de las extensions d'activar"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Las extensions GNOME Shell an una proprietat UUID. Aquesta clau enumèra las "
+"extensions que devon èsser cargadas. Tota extension que voldriá èsser "
+"cargada deu èsser aponduda dins aquesta lista. Podètz tanben manipular "
+"aquesta lista amb los metòdes DBus EnableExtension e DisableExtension de org."
+"gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Identificants UUID de las extensions de desactivar de fòrça"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Las extensions GNOME Shell an una proprietat UUID. Aquesta clau lista las "
+"extensions que devon èsser desactivadas, encara que fòrmen part del mòde "
+"actual. Podètz tanben manipular aquesta lista amb los metòdes "
+"EnableExtension e DisableExtension D-Bus methods de org.gnome.Shell. Aquesta "
+"clau subrecarga lo paramètre « enabled-extensions »."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Desactivar las extensions utilizaire"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Desactivar l’ensemble de las extensions activadas per l’utilizaire sens "
+"afectar la proprietat « enabled-extension »."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Desactiva la verificacion de compatibilitat de version"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell carga unicament las extensions qu'anóncia lor gestion de la "
+"version en cors. Activar aquesta opcion desactiva aquesta verificacion e "
+"ensaja de cargar totas las extensions, sens considerar las versions "
+"qu'anóncian prene en carga."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Lista d'identificants de fichièrs desktop per las aplicacions favoritas"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Las aplicacions que correspondon a aqueles identificants son afichadas dins "
+"la zòna dels favorits."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Istoric de la bóstia de dialòg de las comandas (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Istoric de la bóstia de dialòg d'inspeccion"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Totjorn afichar l’element « Se desconnectar » dins lo menú utilizaire."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Aquesta clau aficha l’element « Se desconnectar » del menú utilizaire "
+"normalament amagat automaticament dins lo cas d’una session o d’un "
+"utilizaire unic."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Se remembrar dels senhals pels montatges dels sistèmas de fichièrs chifrats "
+"o distants"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Lo shell demandarà un senhal al montatge d’un periferic chifrat o d’un "
+"sistèma de fichièrs distant. Se lo senhal pòt èsser enregistrat per una "
+"utilizacion ulteriora, una casa de marcar « Se remembrar del senhal » serà "
+"presenta. Aquesta clau determina l’estat per defaut d'aquesta casa de marcar."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "Indica se l’adaptator Bluetooth a de periferics associats"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Lo shell aficharà pas d’element de menú Bluetooth que se un adaptator "
+"Bluetooth es alucat o se i a de periferics associats amb l’adaptator per "
+"defaut. Aquò sera reïnicializat se l’adaptator per defaut se retròba sens "
+"cap de periferic associat."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "Lo darrièr perfil energetic pas per defaut seleccionat"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"D’unes sistèmas prenon en carga mai de dos perfils energetics. Per dire de "
+"téner de prendre en carga la bascula entre dos perfils, aqueste acorchi "
+"memoriza lo darrièra perfil pas per defaut seleccionat"
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"La darrièra version de la fenèstra de « Benvenguda a GNOME » èra mostrada per"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Aquesta clau determina la version de la fenèstra « Benvenguda de GNOME » "
+"darrièrament afichada. Una cadena de tèxt voida indica la version mai "
+"anciana possibla, e un grand nombre representa una version qu'existís pas "
+"encara. Un grand nombre pòt servir a desactivar d’un biais efectiu la "
+"fenèstra."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Disposicion del selector d’aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Disposicion del selector d’aplicacions. Cada entrada del tablèu es una "
+"pagina. Las paginas son gardadas dins l’òrdre qu'apareisson dins Shell de "
+"GNOME. Cada pagina conten un parelh « identificant d’aplicacion » → "
+"« donadas ». Actualament, las valors seguentas son gardadas coma "
+"« donadas » : • « posicion » : la posicion de l’icòna de l’aplicacion sus la "
+"pagina"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Combinason de tòcas per dobrir lo menú de l'aplicacion"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Combinason de tòcas per dobrir lo menú de l'aplicacion."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Combinason de tòcas per alternar los estats de la la vista"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Combinason de tòcas per bascular entre la session, lo selector de fenèstra e "
+"la grasilha d'aplicacions"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Combinason de tòcas per bascular entre la grasilha d'aplicacions, lo "
+"selector de fenèstra e la session"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Combinason de tòcas per dobrir la vista « Afichar las aplicacions »"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Combinason de tòcas per dobrir la vista « Afichar las aplicacions » de la "
+"vista d’ensemble de las activitats."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Combinason de tòcas per dobrir la vista d'ensemble"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Combinason de tòcas per dobrir la vista d'ensemble de las aplicacions."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Combinason de tòcas per inversar la visibilitat de la lista de las "
+"notificacions"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Combinason de tòcas per inversar la visibilitat de la lista de las "
+"notificacions."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Combinason de tòcas per donar lo focus a la notificacion activa"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Combinason de tòcas per donar lo focus a la notificacion activa."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Passar a l'aplicacion 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Passar a l'aplicacion 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Passar a l'aplicacion 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Passar a l'aplicacion 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Passar a l'aplicacion 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Passar a l'aplicacion 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Passar a l'aplicacion 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Passar a l'aplicacion 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Passar a l'aplicacion 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Limita lo selector de fenèstras a l'espaci de trabalh actual."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Se verai, solas las aplicacions qu'an de fenèstras dobèrtas sus l'espaci de "
+"trabalh actual son afichadas dins lo selector. Siquenon, totas las "
+"aplicacions i son inclusas."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "Lo tipe d'icòna de las aplicacions."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configura lo biais que las fenèstras son afichadas dins lo selector. Las "
+"causidas possiblas son « thumbnail-only » (aficha una miniatura de la "
+"fenèstra), « app-icon-only » (aficha unicament l’icòna de l’aplicacion), o "
+"« both » (aficha los dos)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Se verai, solas las fenèstras de l'espaci de trabalh actual son afichadas "
+"dins lo selector. Siquenon, totas las fenèstras i son inclusas."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Emplaçaments"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Lo lòc d’afichar sul relòtge mondial"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Localizacion automatica"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Se cal obtenir automaticament la localizacion o non"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Emplaçament"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "Lo lòc ont mostrar las previsions metèo"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Estacar los dialògs modals a lor fenèstra parenta"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Aquesta clau pren lo pas sus la clau dins org.gnome.mutter quand GNOME Shell "
+"es aviat."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Activar l'empilatge de las fenèstras depausadas suls bòrds de l'ecran"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Los espacis de trabalh son gerits dinamicament"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Los espacis de trabalh son unicament sus l'ecran principal"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Retardar los cambiaments de focus en mòde mirga fins al moment que lo "
+"puntador arrestarà de bolegar"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Identificacion ret"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Quicòm a trucat"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"O planhèm, mas i a un problèmas : los paramètres d’aquesta extensions se "
+"pòdon pas afichar. Vos recomandam de senhalar aqueste problèma als autors de "
+"l’extension."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detalhs tecnics"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Pagina d'acuèlh"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Consultar la pagina de las extensions"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Anullar"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Senhal"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Causir una session"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Absent de la lista ?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(per ex. utilizaire o %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nom d'utilizaire"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Fenèstra de connexion"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Error d'autentificacion"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(o fasètz lisar lo det pel lector)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(o pausatz lo det sul lector)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Atudar"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "atudar;tampar;desbrancar;reaviar;ralucar;reboot"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Reaviar"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;reaviar;reamodar;reaviada;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Verrolhatge de l’ecran"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "verrolhar l'ecran"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Tampar la session"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "tampar;session;desconnectar"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Metre en velha"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "metre en velha;sòm;suspendre"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Cambiar d’utilizaire"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "cambiar d’utilizaire"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "verrolhatge;orientacion;ecran;rotacion"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Prendre una captura d'ecran"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"captura;screenshot;difusion;vidèo;snip;screencast;enregistrar;salvar;"
+"instantanèu;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Desverrolhar l’orientacion"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Verrolhar l’orientacion"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Comanda pas trobada"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Impossible d'analisar la comanda :"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Execucion de « %s » impossibla :"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Ara meteis"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Fa %d minuta"
+msgstr[1] "Fa %d minutas"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Fa %d ora"
+msgstr[1] "Fa %d oras"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Ièr"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Fa %d jorn"
+msgstr[1] "Fa %d jorns"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Fa %d setmana"
+msgstr[1] "Fa %d setmanas"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Fa %d mes"
+msgstr[1] "Fa %d meses"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Fa %d an"
+msgstr[1] "Fa %d ans"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ièr, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%e %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%e %B de %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ièr, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%e %B de %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Identificacion del punt d’accès"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"La connexion a aqueste punt d’accès es pas securizada. Los senhals e las "
+"autras informacions picatz sus aquesta pagina pòdon èsser interceptats per "
+"de personas a l'entorn de vos."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Refusar l'accès"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Acordar l'accès"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Dossièr sens nom"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s es estat apondut al dash."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s es estat despenjat d’al dash."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Fenèstras dobèrtas"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Novèla fenèstra"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Afichar los detalhs"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Quitar"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Despenjar"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Penjar al Dash"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Aviar en utilizant la carta grafica integrada"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Aviar en utilizant la carta grafica dedicada"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Seleccionar lo perfieric àudio"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Paramètres del son"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Casc àudio"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Microcasc"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Microfòn"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Modificar lo rèireplan…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Afichar los paramètres"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Paramètres"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB de %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Mes precedent"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Mes seguent"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Setmana %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Pas cap de notificacion"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Destorbar pas"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Escafar"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "« %s » respond pas."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Podètz siá esperar un pauc per contunhar, siá forçar l’aplicacion a quitar."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Forçar a quitar"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Esperar"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Disc extèrne connectat"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Disc extèrne desconnectat"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Desblocatge del volum impossible"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "La version installada d’udisks pren pas en carga lo paramètre PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Dobrir amb %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Autrament podètz quichatz lo boton « WPS » sul router."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Se connectar"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Clau"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Senhal de la clau privada"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitat"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Servici"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autentificacion necessària"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Cal un senhal o una clau de chiframent per accedir a la ret sens fial « %s »."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autentificacion filara 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nom de la ret"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autentificacion DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Còdi PIN requesit"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Un còdi PIN es necessari pels telefòns mobils de benda larga"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Un senhal es requesit per se connectar a « %s »."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Gestionari de ret"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Senhal del VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Autentificacion necessària"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "S'autentificar"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Fracàs de l’autentificacion. Ensajar tornamai."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s es ara conegut jol nom de %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Fenèstras"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Afichar las aplicacions"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-e %B de %Y"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B de %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%A %d %B"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-e %B de %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Uèi"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Deman"
+
+# luc: Trying alternatives:
+# 123456789|123456789|
+# Toute la jornada bad
+# Journée entière bad
+# Jour entier
+# La jornada
+# Journée
+#
+# All Day
+#
+# lo calendrier (mal) francisé :
+# http://blog.arkezis.com/wp-content/uploads/2011/04/Captura.png
+#
+# See also https://bugzilla.gnome.org/show_bug.cgi?id=658679
+#
+#
+#
+# lo calendrier (mal) francisé :
+# http://blog.arkezis.com/wp-content/uploads/2011/04/Capture.png
+#
+#
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Tota la jornada"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Pas cap d'eveniment"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Apondre de relòtges locals…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Relòtges locals"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Cargament…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Cercar las informacions meteorologicas en linha"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Las informacions meteorologicas son pas disponiblas actualament"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Metèo"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Causissètz lo lòc per la metèo…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Tampar la session de %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Tampar la session"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s serà desconnectat automaticament dins %d segonda."
+msgstr[1] "%s serà desconnectat automaticament dins %d segondas."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Seretz desconnectat automaticament dins %d segonda."
+msgstr[1] "Seretz desconnectat automaticament dins %d segondas."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Tampar la session"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Atudar"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installar las mesas a jorn e atudar"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Aqueste ordenador s'atudarà automaticament dins %d segonda."
+msgstr[1] "Aqueste ordenador s'atudarà automaticament dins %d segondas."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installar las mesas a jorn logicialles en espèra"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Atudar"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Reaviar"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Installar las mesas a jorn e reaviar"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Aqueste ordenador se reaviarà automaticament dins %d segonda."
+msgstr[1] "Aqueste ordenador se reaviarà automaticament dins %d segondas."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Reaviar"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reaviar e installar las mesas a jorn"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Lo sistèma se reaviarà automaticament e installarà las mesas a jorn dins %d "
+"segonda."
+msgstr[1] ""
+"Lo sistèma se reaviarà automaticament e installarà las mesas a jorn dins %d "
+"segondas."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reaviar e installar"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installar e atudar"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Atudar aprèp l'installacion de las mesas a jorn"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reaviar e installar la mesa a nivèl"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s sera installat aprèp la reaviada. L’installacion d’una mesa a nivèl "
+"pòt prene fòrça temps : verificatz que dispausatz de salvaments e que "
+"l’ordenador es brancat sul sector."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Batariá febla : mercés de brancar al sector abans d’installar las mesas a "
+"jorn."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "D'unas aplicacions son ocupadas o an de trabalhs pas salvats"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "D'autres utilizaires son connectats"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opcions d'aviada"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (distant)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consòla)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installar"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Installar l’extension"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Telecargar e installar « %s » a partir de extensions.gnome.org ?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Mesas a jorn extension disponiblas"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Mesas a jorn per l’extension prèstas per installacion."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Permetre de neutralizar los acorchis"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "L’aplicacion %s vòl inibir los acorchis"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Una aplicacion vòl inibir los acorchis"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Per restablir los acorchis, quichatz sus %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Refusar"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Autorizar"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tòcas lentas activadas"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tòcas lentas desactivadas"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Venètz de quichar pendent 8 segondas sus la tòca Maj. Aquò es l'acorchi "
+"d’activacion de las tòcas lentas e afècta lo biais que vòstre clavièr reagís."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Tòcas remanentas activadas"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Tòcas remanentas desactivadas"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Venètz de quichar 5 còps de seguida sus la tòca Maj. Aquò es l'acorchi "
+"d’activacion de las tòcas remanentas e afècta lo biais que vòstre clavièr "
+"reagís."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Venètz de quichar sus doas tòcas en même temps, o 5 còps de seguida sus la "
+"tòca Maj. Aquò desactiva la foncion tòcas remanentas e afècta lo biais que "
+"vòstre clavièr reagís."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Daissar activat"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Activar"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Atudar"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Daissar atudat"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Paramètres de region e de lenga"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Cap d'extension pas installada"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s a pas emés cap d'error."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Amagar las errors"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Afichar las errors"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Activat"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Desactivat"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Error"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Perimit"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Telecargament"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Afichar la font"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Pagina Web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Lo sistèma es estat passat en mòde pas segur"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Las aplicacions auràn ara un accès restrenh"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Anullar"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Connectat coma utilizaire amb privilègis"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Es pas aconselhat d'aviar una session en qualitat d'utilizaire privilegiat "
+"per de rasons de seguretat. Vos caldriá, se es possible, de vos connectar "
+"coma utilizaire normal."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Verrolhatge d’ecran desactivat"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Lo verrolhatge d’ecran requerís lo gestionari d’afichatge GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informacions del sistèma"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artista desconegut"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Títol desconegut"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Picatz per recercar"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Aplicacions"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Vista d'ensemble"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Novèl acorchi…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplicacions definidas"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Afichar l’ajuda a l’ecran"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Cambiar d’ecran"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Associar una tòca"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Acabat"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Edicion…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Pas cap"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Quichatz sus un boton per lo configurar"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Quichatz sus Escap. per quitar"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Quichatz sus una tòca per quitar"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Activitats"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistèma"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Barra superiora"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Executar una comanda"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Quichatz sus Escap. per quitar"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "La reaviada es pas disponibla sus Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Reaviada en cors…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME a besonh de verrolhar l'ecran"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Impossible de verrolhar"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Lo verrolhatge es estat blocat per una aplicacion"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Seleccion"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Seleccion de la zòna"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Ecran"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Seleccion de l’ecran"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Fenèstra"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Seleccion de fenèstra"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Captura d'ecran / vidèo"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Inclure lo puntador"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Capturas vidèo"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Captura vidèo del %d a %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Captura d'ecran"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Captura vidèo salvada"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Clicatz aicí per veire la vidèo."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Mostrar dins Fichièrs"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Captura d'ecran de %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Captura d'ecran efectuada"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Podètz pegar l’imatge del quichapapièrs estant."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Captura d'ecran efectuada"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Recèrca en cors…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Pas cap de resultat."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d mai"
+msgstr[1] "%d mai"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Recèrca"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copiar"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Pegar"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Afichar lo tèxte"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Amagar lo tèxte"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Verr. maj. es activat."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volum amagat"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volum sistèma Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Utiliza de fichièrs de claus"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Per desverrolhar un volum qu’utiliza los fichièrs de clau, emplegatz "
+"l’utilitari <i>%s</i> puslèu."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Numèro PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Se remembrar del senhal"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desverrolhar"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Dobrir %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "Lo numèro PIM deu èsser un nombre o èsser void."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Aviada impossibla de %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Impossible de trobar l’aplicacion %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Accessibilitat"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Contraste elevat"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zoom"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Lector d'ecran"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Clavièr visual"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Alèrtas visualas"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Tòcas remanentas"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tòcas lentas"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Rebombs de tòcas"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Tòcas de la mirga"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Grand tèxte"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Rotacion auto"
+
+#: js/ui/status/bluetooth.js:171
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Luminositat"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Mòde escur"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clic simple"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Clic doble"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Tirar"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clic segondari"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clic per manten"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Clavièr"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Afichar la disposicion del clavièr"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Autorizar l’accès a la localizacion"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "L’aplicacion %s vòl accedir a vòstra localizacion"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Las règlas d’accès a la localizacion pòdon a tot moment èsser modificadas "
+"dins los paramètres de confidencialitat."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<desconegut>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Desconnectar %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Se connectar a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Punt d’accès %s"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Lum nuechenc"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Performança"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Equilibri"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Estalvi d'energia"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Perfils energetics"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Arrestar la difusion"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Arrestar lo partiment d’ecran"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Mòde avion"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Prendre captura d'ecran"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menú d’extincion"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Metre en velha"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Reaviar…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Atudar…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Tampar la session…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Cambiar d’utilizaire…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Verrolhar l'ecran"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Interfàcia Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Periferic Thunderbolt desconegut"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Un novèl periferic es estat detectat pendent vòstra abséncia. Desbrancatz-lo "
+"e rebrancatz-lo abans de començar de l’utilizar."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Periferic Thunderbolt pas autorizat"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Un aparelh novèl es estat detectat e li fa mestièr d’èsser autorizat per un "
+"administrator."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Error d’autorizacion Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Impossible d’autorizar lo periferic Thunderbolt : %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Volum modificat"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Volum"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Sortida son"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Entrada son"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Clonar"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Jónher ecrans"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Pas que l’extèrne"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Pas que l’integrat"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Far lisar ennaut per desverrolhar"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Clicatz o quichatz una tòca per desverrolhar"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Fenèstra de desverrolhatge"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Se connectar en tant qu'un autre utilizaire"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Benvenguda al GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Se volètz vos assabentar, anatz far la visita."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Non, mercé"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Far la visita"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "« %s » es prèst"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Servar aquestes paramètres d’afichatge ?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Restablir los paramètres"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Conservar las modificacions"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Los paramètres seràn restablits dins %d segonda"
+msgstr[1] "Los paramètres seràn restablits dins %d segondas"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Amagar"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restablir"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximizar"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Desplaçar"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Desplaçar la barra de títol sus l'ecran"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Totjorn en naut"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Totjorn sus l'espaci de trabalh visible"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Desplaçar cap a l'espaci de trabalh d'esquèrra"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Desplaçar cap a l'espaci de trabalh a dreita"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Desplaçar cap a l'espaci de trabalh superior"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Desplaçar cap a l'espaci de trabalh inferior"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Desplaçar cap a l'ecran superior"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Desplaçar cap a l'ecran inferior"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Desplaçar cap a l'ecran d'esquèrra"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Desplaçar cap a l'ecran de dreita"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Tampar"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Agenda d'Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Aficha la version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Mòde utilizat per GDM per l'ecran de connexion"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Utilizar un mòde particular, per ex. « gdm » per l’ecran de connexion"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Far la lista dels mòdes possibles"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Desconegut"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Impossible d'aviar « %s »"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Los senhals correspondon pas."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Lo senhal pòt pas èsser void"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "La fenèstra d'autentificacion es estada escartada per l'utilizaire"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensions"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Gerir vòstras extensions GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Lo projècte GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Extensions gerís las mesas a jorn d'extensions, lo parametratge de la "
+"preferéncias d'extensions e la supression o desactivacion de las extensions "
+"pas volguda."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configurar las extensions GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Cap de correspondéncia pas trobada"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Suprimir « %s » ?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Se suprimissètz l'extension, vos calrà la tornar telecargar se la volètz "
+"tornar activar"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Levar"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Cédric Valmary (totenoc.eu), <cvalmary@yahoo.fr>\n"
+"Quentin PAGÈS"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d extension serà mesa a jorn a la connexion venenta."
+msgstr[1] "%d extensions seràn mesas a jorn a la connexion venenta."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Aquesta extension es pas compatibla amb vòstra version de GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "L’extension a rescontrat un problèma"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Se pòt actualizar aquesta extension"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Site Web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Suprimir…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Ajuda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "A prepaus de las extensions"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Las extensions pòdon causar de problèmas de performanças e d’estabilitat. "
+"Desactivatz las extensions se trobatz de problèmas ligats al sistèma."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Installada manualament"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Per trobar e apondre d’extensions, consultatz <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Integrada"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Cap d’extension pas installada"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"O planhèm, es pas possible d’obténer la lista de las extensions installadas. "
+"Verificatz que sètz ben connectat a GNOME e tornatz ensajar."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Mesas a jorn d’extensions prèstas"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "La nòva extension es estada corrèctament creada dins %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Lo nom deu èsser plan cort (l'idèal lo nom seriá descriptiu).\n"
+"Per exemple : %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nom"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"La descripcion son una sola frasa explicativa per dire çò que fa vòstra "
+"extension.\n"
+"Per exemple : %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descripcion"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"L’UUID es un identificant globalament unica per vòstra extension.\n"
+"Son format deu correspondre al format d’una adreça electronica "
+"(clicperfocus@joanmiquel.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Causissètz un dels modèls disponibles :\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Modèl"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "L’identificant unic de la novèla extension"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOM"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Lo nom d’extension visible per l’utilizaire"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPCION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Una descripcion corta de çò que fa l’extension"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODÈL"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Lo modèl d’utilizar per l’extension novèla"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Picatz d’un biais interactiu las informacions de l’extension"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Crear una extension novèla"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argument desconegut"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Los camps UUIS, nom e descripcion son obligatòris"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Error en se connectant a Shell de GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "L’extension « %s » existís pas\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Desactivar una extension"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Cap d’UUID pas indicat"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Mai d’un UUID indicat"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Activar una extension"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "L’extension « %s » existís pas\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostrar las informacions de las extensions"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Remplaçar una extension existenta"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_EMPAQUETADA"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Installar una extension empaquetada"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Cap d'extension empaquetadas especificadas"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Mai d’una extension empaquetada especificada"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Afichar las extensions installadas per l’utilizaire"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostrar las extension installada pel sistèma"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostrar las extensions activas"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostrar las extensions desactivadas"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostrar las extensions amb de preferéncias"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostrar las extensions amb de mesas a jorn"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Mostrar los detalhs de l’extension"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Mostrar la lista de las extensions installadas"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FICHIÈR"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Font addicionala d’inclure al paquet"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ESQUÈMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Un esquèma GSettings deu èsser inclús"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "REPERTÒRI"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Lo repertòris ont son las traduccions"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMENI"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Lo domeni gettext d’utilizaire per las traduccions"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Remplaçar un paquet existent"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Lo repertòri ont lo paquet deu èsser creat"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "REPERTÒRI_FONT"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Crear una extension empaquetada"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Mai d’un repertòri font indicat"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "L’extension « %s » a pas de preferéncias\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Dobertura impossibla de las preferéncias per l'extension « %s » : %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Dobrís las preferéncias de las extensions"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Reïnicializar una extension"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Impossible de desinstallar las extension installada pel sistèma\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Desinstallacion impossibla de « %s »\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Desinstallar una extension"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Afichar pas los messatges d'error"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Camin"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autor original"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Extension"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Estat"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "« version » accèpta pas cap de paramètre"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Usatge :"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Afichar las informacions de version e quitar."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMANDA"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[PARAMS...]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Comandas :"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Afichar l'ajuda"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Activar l’extension"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Desactivar l’extension"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Reïnicializar l’extension"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Desinstallar l’extension"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Listar las extensions"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Afichar las informacions de l’extension"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Dobrir las preferéncias de l’extension"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Crear una extension"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Paquet de l’extension"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Installar l’extension empaquetada"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Utilizatz « %s » per obténer una ajuda detalhada.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Brut"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Una extension voida"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Apondre una icòna a la barra ennaut"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u sortida"
+msgstr[1] "%u sortidas"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entradas"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sons sistèma"
+
+#~ msgid "Log Out"
+#~ msgstr "Tampar la session"
+
+#~ msgid "%s Off"
+#~ msgstr "%s atudat"
+
+#~ msgid "%s Connected"
+#~ msgstr "%s connectat"
+
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s pas gerit"
+
+#~ msgid "%s Connecting"
+#~ msgstr "Connexion de %s en cors"
+
+#~ msgid "%s Requires Authentication"
+#~ msgstr "Autentificacion necessària per %s"
+
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Micrologicial mancant per %s"
+
+#~ msgid "%s Unavailable"
+#~ msgstr "%s pas disponible"
+
+#~ msgid "%s Connection Failed"
+#~ msgstr "Fracàs de connexion a %s"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Paramètres de la connexion filara"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Paramètres del telefòn mobil de benda larga"
+
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Equipament %s desactivat"
+
+#~ msgid "%s Disabled"
+#~ msgstr "%s desactivat"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Paramètres Bluetooth"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Se connectar a Internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Lo mòde avion es activat"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Lo Wi-Fi es desactivat quand lo mòde avion es activat."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Desactivar lo mòde avion"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Lo Wi-fi es desactivat"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Lo Wi-Fi a besonh d'èsser activat per se connectar a una ret."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Activar lo Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Rets Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Causir una ret"
+
+#~ msgid "No Networks"
+#~ msgstr "Cap de ret pas disponibla"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Utilizar l'interruptor material per atudar"
+
+#~ msgid "Select Network"
+#~ msgstr "Seleccionar una ret"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Paramètres del Wifi"
+
+#~ msgid "%s Not Connected"
+#~ msgstr "%s desconnectat"
+
+#~ msgid "unknown"
+#~ msgstr "desconegut"
+
+#~ msgid "activating…"
+#~ msgstr "activacion…"
+
+#~ msgid "deactivating…"
+#~ msgstr "desactivacion…"
+
+#~ msgid "deactivated"
+#~ msgstr "desactivada"
+
+#~ msgid "connecting…"
+#~ msgstr "connexion…"
+
+#~ msgid "authentication required"
+#~ msgstr "autentificacion necessària"
+
+#~ msgid "connection failed"
+#~ msgstr "fracàs de connexion"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Paramètres VPN"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN atudat"
+
+#~ msgid "Network Settings"
+#~ msgstr "Paramètres de la ret"
+
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s connexion filària"
+#~ msgstr[1] "%s connexions filàrias"
+
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s connexion Wi-Fi"
+#~ msgstr[1] "%s connexions Wi-Fi"
+
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s connexion a un modèm"
+#~ msgstr[1] "%s connexions a de modèm"
+
+#~ msgid "Connection failed"
+#~ msgstr "Fracàs de connexion"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "L'activacion de la connexion a la ret a fracassat"
+
+#~ msgid "Power Settings"
+#~ msgstr "Paramètres de gestion de l'energia"
+
+#~ msgid "Lock"
+#~ msgstr "Verrolhar"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Atudar / Se desconnectar"
+
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d connectat"
+#~ msgstr[1] "%d connectats"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth activat"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth desactivat"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Localizacion activada"
+
+#~ msgid "Disable"
+#~ msgstr "Desactivar"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Paramètres de confidencialitat"
+
+#~ msgid "Location In Use"
+#~ msgstr "Localizacion activada"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Localizacion desactivada"
+
+#~ msgid "Enable"
+#~ msgstr "Activar"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Mòde nuèit desactivat"
+
+#~ msgid "Resume"
+#~ msgstr "Reprene"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Desactivar fins a deman"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Carga completa"
+
+#~ msgid "Not Charging"
+#~ msgstr "Pas en carga"
+
+#~ msgid "Estimating…"
+#~ msgstr "Estimacion…"
+
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%dh%02d restant (%d %%)"
+
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%dh%02d abans cargament complet (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Ecran en partatge"
+
+#~ msgid "Turn off"
+#~ msgstr "Atudar"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Lo mòde avion es activat"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "Aficha l'interfàcia de captura d’ecran"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Levar dels favorits"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Apondre als favorits"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Enregistrament de l’ecran en cors"
+
+#~ msgid "Stop"
+#~ msgstr "Arrestar"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Avertiment"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Las extensions pòdon menar a de problèmas sistèma, inclutz per la "
+#~ "performança. Se pensatz qu’es vòstre cas, se recomanda de desactivar "
+#~ "totas las extensions."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Error en se connectant a Shell de GNOME"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Activar l’API d’introspeccion"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Activa l’API D-Bus, que permet d’introspectar l’estat d’aplicacion del "
+#~ "shell."
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizar"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Reduire"
+
+#~ msgid "App Picker View"
+#~ msgstr "Vista del selector d'aplicacions"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Indèx de la vista actualament seleccionada dins lo selector d'aplicacions."
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Combinason de tòcas per arrestar e tampar totas las transicions amb una "
+#~ "tòca de desbugatge"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Lo clavièr utilizat"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Lo tipe de clavièr utilizat."
+
+#~| msgid "Network error"
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr ""
+#~ "Una error s'es produita al moment del cargament de la bóstia de dialòg de "
+#~ "las preferéncias de %s :"
+
+#~ msgid "Next"
+#~ msgstr "Seguent"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Se connectar"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Las aplicacions utilizadas frequentament apareisseràn aicí"
+
+#~ msgid "Frequent"
+#~ msgstr "Utilizadas frequentament"
+
+#~ msgid "All"
+#~ msgstr "Totes"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %d, %Y"
+#~ msgstr "%A %d %B, %Y"
+
+#~ msgid "Password:"
+#~ msgstr "Senhal :"
+
+#~ msgid "Type again:"
+#~ msgstr "Picatz tornamai :"
+
+#~ msgid "Password: "
+#~ msgstr "Senhal : "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "L'autentificacion es requesida per la ret sens fial"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Senhal del telefòn mobil de benda larga"
+
+#~ msgid "%s all day."
+#~ msgstr "%s tota la jornada."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, puèi %s mai tard."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, puèi %s, seguit per %s mai tard."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Temperatura ressentida : %s."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d messatge novèl"
+#~ msgstr[1] "%d messatges novèls"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d notificacion novèla"
+#~ msgstr[1] "%d notificacions novèlas"
+
+#~ msgid "Off"
+#~ msgstr "Desactivat"
+
+#~ msgid "On"
+#~ msgstr "Activat"
+
+#~ msgid "Account Settings"
+#~ msgstr "Paramètres del compte"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Verrolhatge de l'orientacion"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Afichar la numerotacion de las setmanas dins lo calendièr"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Se verai, afichar lo numèro de setmana ISO dins lo calendièr."
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Redireccion de l'autentificacion Web"
+
+#~ msgid "Events"
+#~ msgstr "Eveniments"
+
+#~ msgid "Notifications"
+#~ msgstr "Notificacions"
+
+#~ msgid "Hide tray"
+#~ msgstr "Amagar lo tirador"
+
+#~ msgid "Status Icons"
+#~ msgstr "Icònas d'estat"
+
+#~ msgid "Media"
+#~ msgstr "Mèdias"
+
+#~ msgid "Not In Use"
+#~ msgstr "Pas en cors d'utilizacion"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Utilizar per la connexion Internet"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Afichar lo tirador de messatjariá"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (gestionari de fenèstras composit Wayland)"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should be loaded. Any extension that wants to be loaded needs to be "
+#~ "in this list. You can also manipulate this list with the EnableExtension "
+#~ "and DisableExtension DBus methods on org.gnome.Shell."
+#~ msgstr ""
+#~ "Las extensions GNOME Shell an una proprietat UUID. Aquesta clau enumèra "
+#~ "las extensions que devon èsser cargadas. Tota extension que voldriá èsser "
+#~ "cargada deu èsser aponduda dins aquesta lista. Tanben podètz manipular "
+#~ "aquesta lista amb los metòdes DBus EnableExtension e DisableExtension de "
+#~ "org.gnome.Shell."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr ""
+#~ "Lista de las categorias que deurián èsser afichadas coma de dorsièrs"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Cada nom de categoria dins aquesta lista serà representada coma un "
+#~ "dorsièr dins la vista de las aplicacions, pluslèu qu'alinhada dins la "
+#~ "vista principala."
+
+#~ msgid "Always show the 'Log out' menuitem in the user menu."
+#~ msgstr ""
+#~ "Afichar totjorn l'element « Se desconnectar » dins lo menú utilizaire."
+
+#~ msgid ""
+#~ "This key overrides the automatic hiding of the 'Log out' menuitem in "
+#~ "single-user, single-session situations."
+#~ msgstr ""
+#~ "Aquesta clau aficha l'element « Se desconnectar » del menú utilizaire "
+#~ "normalament amagat automaticament dins lo cas d'una session o d'un "
+#~ "utilizaire unique."
+
+#~ msgid "Keybinding to toggle the visibility of the message tray"
+#~ msgstr ""
+#~ "Combinason de tòcas per bascular la visibilitat del tirador de messatjariá"
+
+#~ msgid "Keybinding to toggle the visibility of the message tray."
+#~ msgstr ""
+#~ "Combinason de tòcas per bascular la visibilitat del tirador de "
+#~ "messatjariá."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "La disposicion dels botons dins la barra de títol"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Aquesta clau pren lo pas sus la clau dins org.gnome.desktop.wm."
+#~ "preferences quand GNOME Shell es aviat."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Seleccionatz una extension de configurar en utilizant la bóstia combinada "
+#~ "çaisús."
+
+#~ msgid "Execution of '%s' failed:"
+#~ msgstr "Execucion de « %s » impossibla :"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l:%M %p"
+
+# luc: Lun. Mar. Mer. Jeu. Ven. | Lu Ma Me Je Ve | L M M J V in a column
+# I checked a bunch of calendars
+# (including the complete collection of Aubade calendars),
+# most of them use either the one-letter either the full text version.
+# Here I set it to the two-letters version because I think it is
+# visually better, more readable and as much understandable than alternatives
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "D"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Lu"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Ma"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Mè"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Jò"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Ve"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Sa"
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Pas res de previst"
+
+#~ msgid "This week"
+#~ msgstr "Aquesta setmana"
+
+#~ msgid "Next week"
+#~ msgstr "La setmana que ven"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Periferics amovibles"
+
+#~ msgid "Eject"
+#~ msgstr "Ejectar"
+
+#~ msgid ""
+#~ "Passwords or encryption keys are required to access the wireless network "
+#~ "'%s'."
+#~ msgstr ""
+#~ "Cal un senhal o una clau de chiframent per accedir a la ret sens fial « "
+#~ "%s »"
+
+#~ msgid "A password is required to connect to '%s'."
+#~ msgstr "Un senhal es requesit per se connectar a « %s »."
+
+#~ msgid "Convidation"
+#~ msgstr "Convit"
+
+#~ msgid "Call"
+#~ msgstr "Apèl"
+
+#~ msgid "File Transfer"
+#~ msgstr "Transferiment de fichièrs"
+
+#~ msgid "Chat"
+#~ msgstr "Discussion"
+
+#~ msgid "Unmute"
+#~ msgstr "Pas mut"
+
+#~ msgid "Mute"
+#~ msgstr "Mut"
+
+#~ msgid "<b>Yesterday</b>, <b>%H:%M</b>"
+#~ msgstr "<b>Ièr</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> <b>%B</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b> <b>%B</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Convidation to %s"
+#~ msgstr "Convit cap a %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s vos convida a rejónher %s"
+
+#~ msgid "Decline"
+#~ msgstr "Refusar"
+
+#~ msgid "Accept"
+#~ msgstr "Acceptar"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Apèl vidèo de %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Apèl que proven de %s"
+
+#~ msgid "Answer"
+#~ msgstr "Respondre"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s vos manda %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s aimariá èsser autorizat a saber quand sètz en linha"
+
+#~ msgid "Authentication failed"
+#~ msgstr "L'autentificacion a fracassat"
+
+#~ msgid "Encryption error"
+#~ msgstr "Error de chiframent"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Certificat pas provesit"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Certificat pas validat"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certificat expirat"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certificat pas activat"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Lo nom de l'òste del certificat correspond pas"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "L'emprenta del certificat correspond pas"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificat autosignat"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "L'estatut es fòra linha"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Certificat invalid"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "La connexion es estada refusada"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "La connexion pòt pas èsser establida"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "La connexion es estada perduda"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Aqueste compte es ja connectat al servidor"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "La connexion es estada remplaçada per una novèla connexion qu'utiliza la "
+#~ "meteissa ressorsa"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Aqueste compte existís ja sul servidor"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Lo servidor es actualament tròp cargat per tractar la connexion"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Lo certificat es estat revocat"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Lo certificat utiliza un algoritme de chiframent dobtós, o es feble "
+#~ "criptograficament"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "La longor del certificat del servidor, o la talha de sa cadena de "
+#~ "certificacion, depassa los limits de la bibliotèca de criptografia"
+
+#~ msgid "Internal error"
+#~ msgstr "Error intèrna"
+
+#~ msgid "View account"
+#~ msgstr "Afichar lo compte"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Dobrir l'agenda"
+
+#~ msgid "Open Clocks"
+#~ msgstr "Dobrir Relòtges"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Paramètres de data e ora"
+
+# luc: FIXME: how to have a capitalized weekday (start of sentence)?
+# %a (abbreviated) %A (full weekday) %^A (full weekday all uppercase)
+# https://bugzilla.gnome.org/show_bug.cgi?id=658686
+#
+# luc: TODO: to test, this possibly explodes on dimanche 25 septembre 2011
+# L M M J V S D
+#~ msgid "%A %B %e, %Y"
+#~ msgstr "%A %e %B %Y"
+
+#~ msgctxt "button"
+#~ msgid "Restart & Install"
+#~ msgstr "Reamodar e installar"
+
+#~ msgid "Download and install '%s' from extensions.gnome.org?"
+#~ msgstr "Telecargar e installar « %s » a partir de extensions.gnome.org ?"
+
+#~ msgid "Open"
+#~ msgstr "Dobrir"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Escafar los messatges"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Paramètres de notificacions"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Menú del tirador de messatjariá"
+
+#~ msgid "No Messages"
+#~ msgstr "Pas cap de messatge"
+
+#~ msgid "Message Tray"
+#~ msgstr "Tirador de messatjariá"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d periferic connectat"
+#~ msgstr[1] "%d periferics connectats"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Demanda d'autorizacion de « %s »"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Lo periferic « %s » se vòl connectar a aqueste ordenador"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Lo periferic %s vòl accedir al servici « %s »"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Acordar l'accès aqueste còp unicament"
+
+#~ msgid "Reject"
+#~ msgstr "Refusar"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Confirmacion de la demanda de ligason per « %s »"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Confirmatz que la clau secreta (Passkey) « %06d » correspond a la del "
+#~ "periferic."
+
+#~ msgid "Does not match"
+#~ msgstr "Correspond pas"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Demanda de ligason per « %s »"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr ""
+#~ "Picatz lo numèro d'identificacion personala (PIN) mencionat sul periferic."
+
+#~ msgid "OK"
+#~ msgstr "Validar"
+
+#~ msgid "unmanaged"
+#~ msgstr "pas gerit"
+
+#~ msgid "disconnecting..."
+#~ msgstr "desconnexion..."
+
+#~ msgid "firmware missing"
+#~ msgstr "micrologicial absent"
+
+#~ msgid "unavailable"
+#~ msgstr "pas disponible"
+
+#~ msgid "Battery"
+#~ msgstr "Batariá"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "O planhèm, pas cap de saviesa per vos uèi :\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "L'Oracle %s declara"
+
+#~ msgid "'%s' is ready"
+#~ msgstr "« %s » es prèst"
+
+#~ msgid "Failed to launch '%s'"
+#~ msgstr "Impossible d'aviar « %s »"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Settings Menu"
+#~ msgstr "Menú dels paramètres"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Collecter des statistiques sus l'utilizacion des applications"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "Normalament GNOME Shell survelha las aplicacions activas per fin de "
+#~ "determinar las mai utilizadas (per ex. pels aviadors). Quitament se "
+#~ "aquestas donadas son gardadas secretas, de motius de confidencialitat vos "
+#~ "pòdon butar a desactivar aquesta foncionalitat. Remarcatz qu'aquesta "
+#~ "desactivacion suprimís pas d'eventualas donadas ja enregistradas."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Utilizat en intèrne per enregistrar la darrièra preséncia de messatjariá "
+#~ "instantanèa IM explicitament definida per l'utilizaire. Aicí la valor es "
+#~ "tirada de l'enumeracion TpConnectionPresenceType."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Utilizat en interne per enregistrar lo darrièr estatut de preséncia de la "
+#~ "session per l'utilizaire. Aici la valor es tirada de l'enumeracion "
+#~ "GsmPresenceStatus."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Combinason de tòcas per bascular l'enregistrador d'ecran"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr ""
+#~ "Combinason de tòcas per aviar/arrestar l'enregistrador d'ecran integrat."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr ""
+#~ "Nombre d'imatges per segonda per l'enregistrament de las animacions "
+#~ "d'ecran."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "Lo nombre d'imatges per segonda de las animacions d'ecran enregistradas "
+#~ "per l'aisina idònia de GNOME Shell."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "Lo pipeline GStreamer utilizat per encodar l'animacion d'ecran"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Definicion del pipeline GStreamer utilizat per encodar los "
+#~ "enregistraments vidèo. La sintaxi es identica a la de gst-launch. Lo "
+#~ "connectador d'entrada (sink pad) del pipeline deuriá èsser non connectat "
+#~ "là où la vidèo es enregistrada. Lo connectador font deuriá normalement "
+#~ "èsser non connectat ; la sortida de ce connectador es escrita dins lo "
+#~ "fichièr de sortida. Pasmens, lo pipeline pòt tanben se cargar de sa "
+#~ "pròpria sortida, per exemple per diriger la sortida cap a un servidor "
+#~ "icecast via shout2send o autre. Se aquesta clau es pas definida o se es "
+#~ "voida, es lo pipeline per defaut qu'es utilizat. Aqueste es actualament « "
+#~ "vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+#~ "threads=%T ! queue ! webmmux' » e l'enregistrament utiliza lo format WEBM "
+#~ "e lo codec VP8. %T es utilizat coma paramètre per una suposicion quant al "
+#~ "nombre optimal de threads d'utilizar sul sistèma."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Extension de fichièr d'utilizar per enregistrar l'animacion d'ecran"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "Lo nom de fichièr de las animacions d'ecran es unic, se basa sus la data, "
+#~ "e utiliza aquesta extension. Deuriá èsser modificada se lo format del "
+#~ "contenidor utilizat per l'enregistrament es diferent."
+
+#~ msgid "Session…"
+#~ msgstr "Session…"
+
+#~ msgid "Power"
+#~ msgstr "Energia"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Clicatz sus « Tampar la session » per quitar aquestas aplicacions e "
+#~ "tampar la session."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Desconnexion del sistèma."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Clicatz sus « Atudar » per quitar aquestas aplicacions e atudar "
+#~ "l'ordenador."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Extinction del sistèma."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Clicatz sus « Reaviar » per quitar aquestas aplicacions e reaviar "
+#~ "l'ordenador."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Reaviada del sistèma."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Paramètres de l'accès universel"
+
+#~ msgid "Visibility"
+#~ msgstr "Visibilitat"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Configurar un novèl periferic…"
+
+#~ msgid "Connection"
+#~ msgstr "Connexion"
+
+#~ msgid "Send Files…"
+#~ msgstr "Mandar de fichièrs…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Paramètres del clavièr"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Paramètres de la mirga"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Volum, ret, batariá"
+
+#~ msgid "disabled"
+#~ msgstr "desactivat"
+
+#~ msgid "cable unplugged"
+#~ msgstr "cable desbrancat"
+
+#~ msgid "More…"
+#~ msgstr "Mai…"
+
+#~ msgid "Wired"
+#~ msgstr "Filara"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Ethernet automatic"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Connexion benda larga automatica"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Connexion per telefòn automatica"
+
+#~ msgid "Auto %s"
+#~ msgstr "Auto %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Bluetooth automatique"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Sens fial automatic"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s restantas"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ora"
+#~ msgstr[1] "oras"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minuta"
+#~ msgstr[1] "minutas"
+
+#~ msgid "AC Adapter"
+#~ msgstr "Adaptador secteur"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Batariá de l'ordenador"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Monitor"
+#~ msgstr "Ecran"
+
+#~ msgid "Mouse"
+#~ msgstr "Mirga"
+
+#~ msgid "PDA"
+#~ msgstr "Assistent personal"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Telefòn portable"
+
+#~ msgid "Media Player"
+#~ msgstr "Lector multimèdia"
+
+#~ msgid "Tablet"
+#~ msgstr "Tauleta"
+
+#~ msgid "Computer"
+#~ msgstr "Ordenador"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Desconegut"
+
+#~ msgid "Available"
+#~ msgstr "Disponible"
+
+#~ msgid "Busy"
+#~ msgstr "Ocupat"
+
+#~ msgid "Invisible"
+#~ msgstr "Invisible"
+
+#~ msgid "Away"
+#~ msgstr "Absent"
+
+#~ msgid "Idle"
+#~ msgstr "Inactiu"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Vòstre estatut per las discussions serà definit a ocupat"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Les notifications son maintenant desactivadas, y compris les messages de "
+#~ "discussion. Votre statut en linha a été modificat per fin que les autres "
+#~ "sachent qu'il se pòt que vous ne voyez pas leurs messages."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "L'extinction pourrait lor far pèrdre lor travaux non enregistrats."
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Connexion"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APLICACIONS"
+
+#~ msgid "SETTINGS"
+#~ msgstr "PARAMÈTRES"
+
+#
+# luc: https://bugzilla.gnome.org/show_bug.cgi?id=658675
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "Subscription request"
+#~ msgstr "Demanda d'abonnement"
+
+#
+# luc: TODO: double check with source code. %A is weekday « Mandat lundi a 8:23»
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Mandat <b>%A</b> a <b>%X</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Mandat lo <b>%A %-d %B %Y</b>"
+
+#~ msgid "Reconnect"
+#~ msgstr "Se reconnectar"
+
+#~ msgid "tray"
+#~ msgstr "tirador de messatjariá"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Percórrer los fichièrs..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "Error en percorrent lo periferic"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "Lo periferic demandat pòt pas èsser percorrut, l'error es « %s »"
+
+#~ msgid "More..."
+#~ msgstr "Mai..."
+
+#~ msgid "Wireless"
+#~ msgstr "Sens fial"
+
+#~ msgid "VPN Connections"
+#~ msgstr "Connexions per VPN"
+
+#~ msgid "System Settings"
+#~ msgstr "Paramètres sistèma"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Vòstre uòu de Pascas favorit"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Reialme Unit"
+
+#~ msgid "Default"
+#~ msgstr "Defaut"
+
+#~ msgid "Show the message list"
+#~ msgstr "Afichar la lista de messatges"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Portal captiu"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Lo nivèl de precision de localizacion maximal"
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Configure lo nivèl maximal de precision de localizacion que las "
+#~ "aplicacions son autorizadas a veire. Las opcions validas son « off "
+#~ "» (desactivar lo seguiment de l'emplaçament), « country » (país), « city "
+#~ "» (vila), « neighborhood » (quartièr), « street » (carrièra) e « exact "
+#~ "» (emplaçament exacte, necessita generalament un receptor GPS). Gardatz "
+#~ "en cap qu'aqueste paramètre contraròtla unicament çò que GeoClue "
+#~ "autorizarà las aplicacions a veire mas que demòran pr'aquò capablas de "
+#~ "determinar l'emplaçament de l'utilizaire d'ela-meteissa en utilizant las "
+#~ "ressorsas ret (amb al pus melhor un nivèl de precision limitat a la "
+#~ "carrièra)."
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Tampar la session"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Cambiar d’utilizaire"
diff --git a/po/or.po b/po/or.po
new file mode 100644
index 0000000..e3e4e08
--- /dev/null
+++ b/po/or.po
@@ -0,0 +1,2368 @@
+# Oriya translation for gnome-shell.
+# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Manoj Kumar Giri <mgiri@redhat.com>, 2011, 2012, 2013, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-09-18 07:41+0000\n"
+"PO-Revision-Date: 2014-09-18 17:43+0530\n"
+"Last-Translator: Manoj Kumar Giri <mgiri@redhat.com>\n"
+"Language-Team: Oriya <oriya-it@googlegroups.com>\n"
+"Language: or\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "ତନ୍ତ୍ର"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "ସନ୍ଦେଶ ପାତ୍ରକୁ ଦର୍ଶାନ୍ତୁ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "ସକ୍ରିୟ ବିଜ୍ଞପ୍ତିକୁ ଲକ୍ଷ୍ୟ କରନ୍ତୁ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "ସମୀକ୍ଷାକୁ ଦର୍ଶାନ୍ତୁ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "ସମସ୍ତ ପ୍ରୟୋଗଗୁଡ଼ିକୁ ଦର୍ଶାନ୍ତୁ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "ପ୍ରୟୋଗ ତାଲିକାକୁ ଖୋଲନ୍ତୁ"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME ସେଲ"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "ୱିଣ୍ଡୋ ପରିଚାଳନା ଏବଂ ପ୍ରୟୋଗ ପ୍ରାରମ୍ଭ"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME ସେଲ ଅନୁଲଗ୍ନ ପସନ୍ଦଗୁଡ଼ିକ"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME ସେଲ ଅନୁଲଗ୍ନଗୁଡ଼ିକ"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME ସେଲ (ୱେଲ୍ୟାଣ୍ଡ ଯୋଜନାକାରୀ)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"ଆଭ୍ୟନ୍ତରୀଣ ସାଧନଗୁଡ଼ିକୁ Alt-F2 ରୁ ବିକାଶକାରୀ ଏବଂ ପରୀକ୍ଷକମାନଙ୍କ ପାଇଁ ସକ୍ରିୟ "
+"କରନ୍ତୁ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"ଆଭ୍ୟନ୍ତରୀଣ ତ୍ରୁଟିନିବାରଣ ଏବଂ ନିରୀକ୍ଷଣ ସାଧନଗୁଡ଼ିକୁ Alt-F2 ସଂଳାପ ବ୍ୟବହାର କରି "
+"ଅନୁମତି ପ୍ରଦାନ କରନ୍ତୁ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "ସକ୍ରିୟ କରିବା ପାଇଁ ଥିବା ଅନୁଲଗ୍ନଗୁଡ଼ିକର UUID ଗୁଡ଼ିକ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME ସେଲ ଅନୁଲଗ୍ନଗୁଡ଼ିକରେ ଗୋଟିଏ UUID ଗୁଣଧର୍ମ ଅଛି; ଏହି କି ଅନୁଲଗ୍ନଗୁଡ଼ିକୁ "
+"ତାଲିକାଭୁକ୍ତ କରିଥାଏ ଯାହାକୁ "
+"ଧାରଣ କରିବା ଉଚିତ। ସକ୍ରିୟ ଅନୁଲଗ୍ନ ଏବଂ ନିଷ୍କ୍ରିୟ-ଅନୁଲଗ୍ନଗୁଡ଼ିକ ସହିତ D-Bus "
+"ପଦ୍ଧତିରେ ଆପଣ ତାଲିକାକୁ "
+"ସଙ୍କଳନ କରିପାରିବେ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "ଅନୁଲଗ୍ନ ସଂସ୍କରଣ ସୁସଙ୍ଗତତାର ବୈଧତାକୁ ନିଷ୍କ୍ରିୟ କରିଥାଏ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME ସେଲ କେବଳ ଅନୁଲଗ୍ନଗୁଡ଼ିକୁ ଧାରଣ କରିବ ଯାହାକି ପ୍ରଚଳିତ ସଂସ୍କରଣକୁ ସହାୟତା ଦେବା "
+"ପାଇଁ ଉଦ୍ଦିଷ୍ଟ।"
+"ଏହି ବିକଳ୍ପକୁ ସକ୍ରିୟ କରିବା ଫଳରେ ତାହା ଏହି ଯାଞ୍ଚକୁ ନିଷ୍କ୍ରିୟ କରିବ ଏବଂ ସହାୟତା "
+"ପାଇଁ ଉଦ୍ଦିଷ୍ଟ "
+"ସଂସ୍କରଣଗୁଡ଼ିକୁ ଅଦେଖା କରି ସମସ୍ତ ଅନୁଲଗ୍ନଗୁଡ଼ିକୁ ଧାରଣ କରିବା ପାଇଁ ଚେଷ୍ଟା କରିବ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "ମନପସନ୍ଦ ପ୍ରୟୋଗଗୁଡ଼ିକ ପାଇଁ ଡେସ୍କଟପ ଫାଇଲ ID ଗୁଡ଼ିକର ତାଲିକା"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"ଏହି ପରିଚାୟକମାନଙ୍କ ସହିତ ସମ୍ପୃକ୍ତ ପ୍ରୟୋଗଗୁଡ଼ିକୁ ମନପସନ୍ଦ ସ୍ଥାନରେ ଦର୍ଶାଯିବ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "ଆପ ଗୋଟାଳୀ ଦୃଶ୍ୟ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "ପ୍ରୟୋଗ ଗୋଟାଳୀରେ ବର୍ତ୍ତମାନ ବଚ୍ଛିତ ଦୃଶ୍ୟର ଅନୁକ୍ରମ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "ନିର୍ଦ୍ଦେଶ (Alt-F2) ସଂଳାପ ପାଇଁ ପୁରୁଣା ତଥ୍ୟ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "କାଚ ସଂଳାପ ଦେଖିବା ପାଇଁ ପୁରୁଣା ତଥ୍ୟ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "ସର୍ବଦା 'ଲଗଆଉଟ' ତାଲିକା ବସ୍ତୁକୁ ବ୍ୟବହାରକାରୀ ତାଲିକାରେ ଦର୍ଶାନ୍ତୁ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"ଏହି ଚାବିଟି ସ୍ୱୟଂଚାଳିତ ଭାବରେ 'ଲଗଆଉଟ' ତାଲିକା ବସ୍ତୁକୁ ଲୁଚାଇବାକୁ ଏକକ-ବ୍ୟବହାରକାରୀ, "
+"ଏକକ-ଅଧିବେଶନ "
+"ପରିସ୍ଥିତିରେ ନବଲିଖନ କରିଥାଏ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"ସଂଗୁପ୍ତ ଅଥବା ସୁଦୂର ଫାଇଲତନ୍ତ୍ରଗୁଡ଼ିକୁ ସ୍ଥାପନ କରିବା ପାଇଁ ପ୍ରବେଶ ସଂକେତକୁ "
+"ମନେରଖିବା ଉଚିତ କି"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"ସେଲ ଏକ ପ୍ରବେଶ ସଂକେତ ମାଗିବ ଯେତେବେଳେ ଏକ ସଂଗୁପ୍ତ ଉପକରଣ ଅଥବା ସୁଦୂର ଫାଇଲତନ୍ତ୍ରକୁ "
+"ସ୍ଥାପନ "
+"କରାଯିବ। ଯଦି ପ୍ରବେଶ ସଂକେତକୁ ଭବିଷ୍ୟତ ପାଇଁ ସଂରକ୍ଷଣ କରାଯାଇପାରିବ ତେବେ'ପ୍ରବେଶ ସଂକେତ "
+"ମନେରଖନ୍ତୁ' "
+"ଯାଞ୍ଚବାକ୍ସ ଥିବା ଉଚିତ। ଏହି ଚାବି ପୂର୍ବନିର୍ଦ୍ଧାରିତ ସ୍ଥିତିକୁ ସେଟ କରିଥାଏ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "କ୍ୟାଲେଣ୍ଡରରେ ସାପ୍ତାହିକ ତାରିଖ ଦର୍ଶାନ୍ତୁ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "ସତ ହେଲେ, କାଲେଣ୍ଡରରେ ISO ସପ୍ତାହ ତାରିଖ ଦେଖାନ୍ତୁ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "ପ୍ରୟୋଗ ମେନୁକୁ ସକ୍ରିୟ କରିବା ପାଇଁ କି ବନ୍ଧନ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "ପ୍ରୟୋଗ ମେନୁକୁ ଖୋଲିବା ପାଇଁ କି ବନ୍ଧନ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"ପ୍ରୟୋଗଗୁଡ଼ିକୁ ଦର୍ଶାନ୍ତୁ\" ଦୃଶ୍ୟକୁ ଖୋଲିବା ପାଇଁ କି ବନ୍ଧନ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"କାର୍ଯ୍ୟକଳାପ ସମୀକ୍ଷାର \"ପ୍ରୟୋଗଗୁଡ଼ିକୁ ଦର୍ଶାନ୍ତୁ\" ଦୃଶ୍ୟକୁ ଖୋଲିବା ପାଇଁ କି ବନ୍ଧନ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the overview"
+msgstr "ସମୀକ୍ଷାକୁ ଖୋଲିବା ପାଇଁ କି ବନ୍ଧନ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the Activities Overview."
+msgstr "କାର୍ଯ୍ୟକଳାପ ସମୀକ୍ଷାକୁ ଖୋଲିବା ପାଇଁ କି ବନ୍ଧନ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "ସନ୍ଦେଶ ଟ୍ରେର ଦୃଶ୍ୟମାନ୍ୟତାକୁ ଆଗପଛ କରିବା ପାଇଁ କି ବନ୍ଧନ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "ସନ୍ଦେଶ ଟ୍ରେର ଦୃଶ୍ୟମାନ୍ୟତାକୁ ଆରମ୍ଭ/ବନ୍ଦ କରିବା ପାଇଁ କି ବନ୍ଧନ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "ସକ୍ରିୟ ବିଜ୍ଞପ୍ତିକୁ ଲକ୍ଷ୍ଯ କରିବା ପାଇଁ କି ବନ୍ଧନ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "ସକ୍ରିୟ ବିଜ୍ଞପ୍ତିକୁ ଲକ୍ଷ୍ଯ କରିବା ପାଇଁ କି ବନ୍ଧନ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"ଚାଲୁଥିବା ସମସ୍ତ ଟ୍ୱୀନଗୁଡ଼ିକୁ ସ୍ଥିର ରଖିବା ଏବଂ ପୁନଃଚାଳନ କରିବା ପାଇଁ ଥିବା କି "
+"ବନ୍ଧନ, ତୃଟି ନିବାରଣ "
+"ଉଦ୍ଦେଶ୍ୟରେ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "କେଉଁ କିବୋର୍ଡକୁ ବ୍ୟବହାର କରିବା ଉଚିତ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "ବ୍ୟବହାର କରିବାକୁ ଥିବା କିବୋର୍ଡ ପ୍ରକାର"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "ପରିବର୍ତ୍ତକକୁ ପ୍ରଚଳିତ କାର୍ଯ୍ୟକ୍ଷେତ୍ର ପାଇଁ ସିମୀତ ରଖନ୍ତୁ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"ଯଦି true, ତେବେ କେବଳ ପ୍ରଚଳିତ କାର୍ଯ୍ୟକ୍ଷେତ୍ରରେ ଥିବା ପ୍ରୟୋଗଗୁଡ଼ିକ ପରିବର୍ତ୍ତକରେ "
+"ଦର୍ଶାଯାଇଥାଏ।"
+"ଅନ୍ୟଥା, ସମସ୍ତ ପ୍ରୟୋଗଗୁଡ଼ିକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରାଯାଇଥାଏ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "ପ୍ରୟୋଗ ଚିତ୍ରସଂକେତ ଧାରା।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"ସ୍ୱୀଚରରେ ୱିଣ୍ଡୋଗୁଡ଼ିକ କିପରି ଦର୍ଶାଯିବ ତାହା ପାଇଁ ବିନ୍ୟାସ କରନ୍ତୁ। ବୈଧ "
+"ସମ୍ଭାବ୍ୟଗୁଡ଼ିକ ହେଉଛି 'କେବଳ-"
+"ଅଙ୍ଗିଳିଚିହ୍ନ' (ୱିଣ୍ଡୋର ଏକ ଅଙ୍ଗୁଳି ଚିହ୍ନକୁ ଦର୍ଶାନ୍ତୁ), 'app-icon-only' (କେବଳ "
+"ପ୍ରୟୋଗ ଚିତ୍ର ସଂକେତ "
+"ଦର୍ଶାଇଥାଏ) ଅଥବା 'ଉଭୟ'।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"ଯଦି true, ତେବେ କେବଳ ପ୍ରଚଳିତ କାର୍ଯ୍ୟକ୍ଷେତ୍ରରୁ ୱିଣ୍ଡୋଗୁଡ଼ିକ ପରିବର୍ତ୍ତକରେ "
+"ଦର୍ଶାଯାଇଥାଏ।ଅନ୍ୟଥା, "
+"ସମସ୍ତ ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରାଯାଇଥାଏ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "ମୂଖ୍ୟ ୱିଣ୍ଡୋରେ ମଡେଲ ସଂଳାପ ସଂଲଗ୍ନ କରନ୍ତୁ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"GNOME ସେଲ ଚାଲିଥିବା ସମୟରେ ଏହି ଚାବିଟି org.gnome.mutter ରେ ଥିବା ଚାବିକୁ ନବଲିଖନ "
+"କରିଥାଏ।"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରଦା ଧାରରେ ରଖିବା ସମୟରେ ଧାର ଟାଇଲକୁ ସକ୍ରିୟ କରନ୍ତୁ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "କାର୍ଯ୍ୟକ୍ଷେତ୍ରଗୁଡ଼ିକ ଅସ୍ଥାୟୀ ଭାବରେ ପରିଚାଳିତ ହୋଇଥାଏ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "କାର୍ଯ୍ୟକ୍ଷେତ୍ର କେବଳ ପ୍ରାଥମିକ ମନିଟରରେ ଥାଏ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"ସୂଚକ ଗତି କରିବା ବନ୍ଦ ନହେଲା ପର୍ଯ୍ୟନ୍ତ ମାଉସ ଧାରାରେ ଲକ୍ଷ୍ଯସ୍ଥଳ ପରିବର୍ତ୍ତନକୁ "
+"ବିଳମ୍ବ କରନ୍ତୁ"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "କେପଟିଭ୍‌ ପୋର୍ଟାଲ"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s ପାଇଁ ପସନ୍ଦ ସଂଳାପ ଧାରଣ କରିବା ସମୟରେ ତ୍ରୁଟି:"
+
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extensions"
+msgstr "GNOME ସେଲ ଅନୁଲଗ୍ନଗୁଡ଼ିକ"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "ବାତିଲ କରନ୍ତୁ"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "ପରବର୍ତ୍ତୀ"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "ଖୋଲନ୍ତୁ"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "ସାଇନ ଇନ"
+
+#: ../js/gdm/loginDialog.js:269
+msgid "Choose Session"
+msgstr "ଅଧିବେଶନ ବାଛନ୍ତୁ"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "ତାଲିକାଭୁକ୍ତ ନୁହଁ କି?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ଯେପରିକି, %s ପାଇଁ ଚାଳକ)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "ଚାଳକ ନାମ: "
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "ଲଗଇନ ୱିଣ୍ଡୋ"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "ବୈଧିକରଣ ତ୍ରୁଟି"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(ଅଥବା ଅଙ୍ଗୁଳି ବୁଲାନ୍ତୁ)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "ନିର୍ଦ୍ଦେଶ ମିଳିଲା ନାହିଁ"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "ନିର୍ଦ୍ଦେଶ ବିଶ୍ଲେଷିତ କରିହେଲା ନାହିଁ:"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” ର ନିଷ୍ପାଦନ ବିଫଳ ହୋଇଛି:"
+
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "ୱେବ ବୈଧିକରଣ ଦିଗ ପରିବର୍ତ୍ତନ"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "ବାରମ୍ବାର ବ୍ୟବହୃତ ପ୍ରୟୋଗଗୁଡ଼ିକ ଏଠାରେ ଦୃଶ୍ୟମାନ ହେବ"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "ବାରମ୍ବାର"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "ସମସ୍ତ"
+
+#: ../js/ui/appDisplay.js:1789
+msgid "New Window"
+msgstr "ନୂତନ ୱିଣ୍ଡୋ"
+
+#: ../js/ui/appDisplay.js:1815 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "ମନପସନ୍ଦଗୁଡ଼ିକ ମଧ୍ଯରୁ କାଢ଼ନ୍ତୁ"
+
+#: ../js/ui/appDisplay.js:1821
+msgid "Add to Favorites"
+msgstr "ମନପସନ୍ଦ ମଧ୍ଯରେ ଯୋଗକରନ୍ତୁ"
+
+#: ../js/ui/appDisplay.js:1830
+msgid "Show Details"
+msgstr "ବିସ୍ତୃତ ବିବରଣୀ ଦର୍ଶାନ୍ତୁ"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s କୁ ଆପଣଙ୍କର ମନପସନ୍ଦରେ ଯୋଗ କରାଯାଇଛି।"
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s କୁ ମନପସନ୍ଦରୁ ବାହାର କରିଦିଆଯାଇଛି।"
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "ବିନ୍ଯାସ"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "ପୃଷ୍ଠଭୂମିକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "ସାରା ଦିନ"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "Su"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "W"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "Th"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "S"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "ପୂର୍ବ ମାସ"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "ପର ମାସ"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "କିଛି ଯୋଜନା କରାହୋଇନାହିଁ"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "ଆଜି"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "ଆସନ୍ତାକାଲି"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "ଏହି ସପ୍ତାହ"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "ପରବର୍ତ୍ତୀ ସପ୍ତାହ"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "ବାହ୍ୟ ଡ୍ରାଇଭ ସଂଯକ୍ତ ଅଛି"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "ବାହ୍ୟ ଡ୍ରାଇଭ ସଂଯକ୍ତ ଅଛି"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "କଢ଼ାଯୋଗ୍ୟ ଉପକରଣଗୁଡ଼ିକ"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ସହିତ ଖୋଲନ୍ତୁ"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "ବାହାର କରନ୍ତୁ"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "ପ୍ରବେଶ ସଙ୍କେତ:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "ପୁଣିଥରେ ଲେଖନ୍ତୁ:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "ସଂଯୋଗ କରନ୍ତୁ"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "ପ୍ରବେଶ ସଙ୍କେତ: "
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "କି: "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "ପରିଚୟ: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "ବ୍ୟକ୍ତିଗତ କି ପ୍ରବେଶ ସଂକେତ: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "ସର୍ଭିସ: "
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "ବେତାର ନେଟୱର୍କ ପାଇଁ ପ୍ରାଧିକରଣ ଆବଶ୍ୟକ"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"ବେତାର ନେଟୱର୍କ “%s” କୁ ଅଭିଗମ୍ୟ କରିବା ପାଇଁ ପ୍ରବେଶ ସଂକେତ କିମ୍ବା ସଂଗୁପ୍ତ କିଗୁଡ଼ିକ "
+"ଆବଶ୍ୟକ।"
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "ତାରଯୁକ୍ତ 802.1X ବୈଧିକରଣ"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "ନେଟୱାର୍କ ନାମ: "
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "DSL ବୈଧିକରଣ"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "PIN ସଂକେତ ଆବଶ୍ୟକ"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "ମୋବାଇଲ ବ୍ରୋଡବ୍ୟାଣ୍ଡ ଉପକରଣ ପାଇଁ PIN ସଂକେତ ଆବଶ୍ୟକ"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "ମୋବାଇଲ ବ୍ରୋଡ଼ବ୍ୟାଣ୍ଡ ନେଟୱର୍କ ପ୍ରବେଶ ସଂକେତ"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” ସହିତ ସଂଯୁକ୍ତ ହେବା ପାଇଁ ଗୋଟିଏ ପ୍ରବେଶ ସଂକେତ ଆବଶ୍ୟକ।"
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "ବୈଧିକରଣ ଆବଶ୍ୟକ"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "ପ୍ରଶାସକ"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "ବୈଧିକୃତ"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "ଦୁଖିତଃ, ତାହା କାର୍ଯ୍ୟକାରୀ ହେଲା ନହିଁ। ଦୟାକରି ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "ଆମନ୍ତ୍ରଣ"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "ଡାକରା"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "ଫାଇଲ ସ୍ଥାନାନ୍ତରଣ"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "ଆଳାପ"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "ଧ୍ୱନି ଚାଲୁ କରନ୍ତୁ"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "ଧ୍ୱନିକୁ ମୁକ କରନ୍ତୁ"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+msgid "Yesterday, %H∶%M"
+msgstr "ଗତକାଲି,%H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "ଗତକାଲି, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s ବର୍ତ୍ତମାନ %s ଭାବରେ ପରିଚିତ"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%s କୁ ଆମନ୍ତ୍ରଣ"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s ଆପଣଙ୍କୁ %s ରେ ଯୋଗଦାନ କରିବା ପାଇଁ ଆମନ୍ତ୍ରଣ କରୁଛନ୍ତି"
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "ମନା କରନ୍ତୁ"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "ଗ୍ରହଣ କରନ୍ତୁ"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s ଠାରୁ ଭିଡିଓ ଡାକରା"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s ଠାରୁ ଡାକରା"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "ଉତ୍ତର"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s ଆପଣଙ୍କୁ %s ପଠାଉଅଛି"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "ଆପଣ କେତେ ବେଳେ ଅନଲାଇନ ଆସୁଛନ୍ତି ଦେଖିବା ପାଇଁ %s କୁ ଅନୁମତି ଆବଶ୍ୟକ କରୁଛି"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "ନେଟୱର୍କ ତ୍ରୁଟି"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "ବୈଧିକରଣ ବିଫଳ ହେଲା"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "ସଂଗୁପ୍ତ ତ୍ରୁଟି"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "ପ୍ରମାଣପତ୍ର ଦିଆଯାଇନାହିଁ"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "ପ୍ରମାଣପତ୍ର ବିଶ୍ୱସ୍ଥ ନୁହଁ"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "ପ୍ରମାଣପତ୍ରର ସମୟ ସମାପ୍ତ"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "ପ୍ରମାଣପତ୍ର ସକ୍ରିୟ କରାଯାଇନାହିଁ"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "ପ୍ରମାଣପତ୍ର ହୋଷ୍ଟ ନାମ ମେଳଖାଉ ନାହିଁ"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "ପ୍ରମାଣପତ୍ର ଅଙ୍ଗୁଳିଚିହ୍ନ ମେଳଖାଉନାହିଁ"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "ପ୍ରମାଣପତ୍ର ନିଜ ଦ୍ୱାରା ହସ୍ତାକ୍ଷର ହୋଇଛି"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "ସ୍ଥିତିକୁ ଅଫଲାଇନ ସେଟ କରାଯାଇଛି"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "ସଂଗୁପ୍ତ ଉପଲବ୍ଧ ନାହିଁ"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "ପ୍ରମାଣପତ୍ରଟି ଅବୈଧ ଅଟେ"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "ସଂଯୋଗକୁ ବାରଣ ହୋଇଛି"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "ସଂଯୋଗ ସ୍ଥାପନ କରିପାରିବେ ନାହିଁ"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "ସଂଯୋଗ ବିଚ୍ଛିନ୍ନ ହୋଇଛି"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "ଏହି ଖାତାଟି ସର୍ଭର ସହିତ ପୂର୍ବରୁ ସଂଯୁକ୍ତ ଅଛି"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr ""
+"ସମାନ ଉତ୍ସ ବ୍ୟବହାର କରି ଏହି ସଂଯୋଗଟି ଏକ ନୂତନ ସଂଯୋଗ ଦ୍ୱାରା ପରିବର୍ତ୍ତିତ ହୋଇଛି"
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "ସର୍ଭରରେ ଏହି ଖାତାଟି ପୂର୍ବରୁ ଅବସ୍ଥିତ ଅଛି"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "ଏହି ସଂଯୋଗକୁ କାର୍ଯ୍ୟକାରୀ କରିବା ପାଇଁ ସର୍ଭର ବର୍ତ୍ତମାନ ବ୍ୟସ୍ତ ଅଛି"
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "ପ୍ରମାଣପତ୍ରଟିର ସମୟ ସମାପ୍ତ ହୋଇଛି"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"ପ୍ରମାଣପତ୍ରଟି ଗୋଟିଏ ଅସୁରକ୍ଷିତ ଆଲଗୋରିଦମ ବ୍ୟବହାର କରିଥାଏ କିମ୍ବା ତାହା "
+"କ୍ରିପ୍ଟୋଗ୍ରାଫି ଅନୁଯାୟୀ ଦୁର୍ବଳ "
+"ଅଟେ"
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"ସର୍ଭର ପ୍ରମାଣପତ୍ରର ଲମ୍ବ, ଅଥବା ସର୍ଭର ପ୍ରମାଣପତ୍ର ସୃଙ୍ଖଳର ଗଭୀରତା, କ୍ରିପ୍ଟୋଗ୍ରାଫି "
+"ଲାଇବ୍ରେରୀ "
+"ଦ୍ୱାରା ନିୟୋଜିତ ସୀମାକୁ ଅତିକ୍ରମ କରିଥାଏ"
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "ଆଭ୍ଯନ୍ତରୀଣ ତ୍ରୁଟି"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s ସହିତ ସଂଯୋଗ ହେବାରେ ଅସମର୍ଥ"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "ଖାତା ଦେଖନ୍ତୁ"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "ଅଜ୍ଞାତ କାରଣ"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "Windows"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "ପ୍ରୟୋଗଗୁଡ଼ିକୁ ଦର୍ଶାନ୍ତୁ"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "ଡ୍ୟାସ"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "କ୍ୟାଲେଣ୍ଡର ଖୋଲନ୍ତୁ"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "ଘଡ଼ିଗୁଡ଼ିକୁ ଖୋଲନ୍ତୁ"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "ତାରିଖ ଏବଂ ସମୟ ସେଟିଙ୍ଗ"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "ଲଗଆଉଟ %s"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "ଲଗ ଆଉଟ"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ଲଗ ଆଉଟ ହେବ।"
+msgstr[1] "%s ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ଲଗ ଆଉଟ ହେବ।"
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "ଆପଣ ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ଲଗ ଆଉଟ ହେବ।"
+msgstr[1] "ଆପଣ ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ଲଗ ଆଉଟ ହେବ।"
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "ଲଗଆଉଟ"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "ଅଦ୍ୟତନଗୁଡ଼ିକୁ ସ୍ଥାପନ କରନ୍ତୁ ଏବଂ ଅଫ୍‌ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "ତନ୍ତ୍ରଟି ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ ହେବ।"
+msgstr[1] "ତନ୍ତ୍ରଟି ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ ହେବ।"
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "ବଳକା ସଫ୍ଟୱେର ଅଦ୍ୟତନଗୁଡ଼ିକୁ ସ୍ଥାପନ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "ପୂନଃପ୍ରାରମ୍ଭ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "ପୂନଃପ୍ରାରମ୍ଭ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "ତନ୍ତ୍ର ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ପୁନଃପ୍ରାରମ୍ଭ ହେବ।"
+msgstr[1] "ତନ୍ତ୍ର ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ପୁନଃପ୍ରାରମ୍ଭ ହେବ।"
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "ପୁନଃଚାଳନ କରନ୍ତୁ ଏବଂ ଅଦ୍ୟତନଗୁଡ଼ିକୁ ସ୍ଥାପନ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"ତନ୍ତ୍ର ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ପୁନଃପ୍ରାରମ୍ଭ ହେବ ଏବଂ ଅଦ୍ୟତନଗୁଡ଼ିକୁ ସ୍ଥାପନ "
+"କରିବ।"
+msgstr[1] ""
+"ତନ୍ତ୍ର ସ୍ୱୟଂଚାଳିତ ଭାବରେ %d ସେକଣ୍ଡରେ ପୁନଃପ୍ରାରମ୍ଭ ହେବ ଏବଂ ଅଦ୍ୟତନଗୁଡ଼ିକୁ ସ୍ଥାପନ "
+"କରିବ।"
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "ପୁନଃଚାଳନ କରନ୍ତୁ ଏବଂ ସ୍ଥାପନ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ସ୍ଥାପନ କରନ୍ତୁ ଏବଂ ଅଫ୍‌ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "ଅଦ୍ୟତନଗୁଡ଼ିକ ସ୍ଥାପିତ ହୋଇସାରିବା ପରେ ଅଫ୍‌ କରନ୍ତୁ"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"ବ୍ୟାଟେରୀରେ ଚାଲୁଅଛି: ଅଦ୍ୟତନଗୁଡ଼ିକୁ ସ୍ଥାପନ କରିବା ପୂର୍ବରୁ ପ୍ଲଗଇନ୍‌ କରନ୍ତୁ।"
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "କିଛି ପ୍ରୟୋଗଗୁଡ଼ିକ ବ୍ୟସ୍ତ ଅଛି କିମ୍ବା ସେଥିରେ କିଛି କାମ ଅସଂରକ୍ଷିତ ଅଛି।"
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "ଅନ୍ୟ ବ୍ୟବହାରକାରୀମାନେ ଲଗଇନ ହୋଇଛନ୍ତି।"
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (ଦୂରବର୍ତ୍ତୀ)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (କୋନସୋଲ)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "ସ୍ଥାପନ କରନ୍ତୁ"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "“%s” କୁ extensions.gnome.org ରୁ ଆହରଣ କରି ସ୍ଥାପନ କରିବେ କି?"
+
+#: ../js/ui/keyboard.js:692 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "କିବୋର୍ଡ"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "କୌଣସି ଅନୁଲଗ୍ନ ସ୍ଥାପିତ ହୋଇନାହିଁ"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s କୌଣସି ତ୍ରୁଟି ଚିହ୍ନଟ କରିନାହିଁ।"
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "ତୃଟିଗୁଡ଼ିକୁ ଲୁଚାନ୍ତୁ"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "ତୃଟି ଗୁଡିକୁ ଦର୍ଶାନ୍ତୁ"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "ସକ୍ରିୟ"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "ନିଷ୍କ୍ରିୟ"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "ତୃଟି"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "ତାରିଖ ବାହାରେ"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "ଆହରଣ କରୁଅଛି"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "ଉତ୍ସକୁ ଦେଖନ୍ତୁ"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "ୱେବ ପୃଷ୍ଠା"
+
+#: ../js/ui/messageTray.js:1327
+msgid "Open"
+msgstr "ଖୋଲନ୍ତୁ"
+
+#: ../js/ui/messageTray.js:1334
+msgid "Remove"
+msgstr "ବାହାର କରନ୍ତୁ"
+
+#: ../js/ui/messageTray.js:1631
+msgid "Notifications"
+msgstr "ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"
+
+#: ../js/ui/messageTray.js:1638
+msgid "Clear Messages"
+msgstr "ସନ୍ଦେଶଗୁଡ଼ିକୁ ସଫାକରନ୍ତୁ"
+
+#: ../js/ui/messageTray.js:1657
+msgid "Notification Settings"
+msgstr "ବିଜ୍ଞପ୍ତି ବିନ୍ୟାସ"
+
+#: ../js/ui/messageTray.js:1710
+msgid "Tray Menu"
+msgstr "ଟ୍ରେ ତାଲିକା"
+
+#: ../js/ui/messageTray.js:1934
+msgid "No Messages"
+msgstr "କୌଣସି ସନ୍ଦେଶ ନାହିଁ"
+
+#: ../js/ui/messageTray.js:1979
+msgid "Message Tray"
+msgstr "ସନ୍ଦେଶ ଟ୍ରେ"
+
+#: ../js/ui/messageTray.js:2992
+msgid "System Information"
+msgstr "ତନ୍ତ୍ର ସୂଚନା"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "ଅଜଣା"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d ଟି ନୂତନ ସନ୍ଦେଶ"
+msgstr[1] "%d ଟି ନୂତନ ସନ୍ଦେଶ"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "ପଦକ୍ଷେପ ବାତିଲ କରନ୍ତୁ"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "ସମୀକ୍ଷା"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "ସନ୍ଧାନ କରିବା ପାଇଁ ଲେଖନ୍ତୁ…"
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "ବିଦାୟ ନିଅନ୍ତୁ"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "କାର୍ଯ୍ୟକ୍ରମଗୁଡ଼ିକ"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "ଉପର ପଟି"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "ଗୋଟିଏ ନିର୍ଦ୍ଦେଶକୁ ଭରଣ କରନ୍ତୁ"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "ବନ୍ଦକରନ୍ତୁ"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "ପୁନଃଚାଳନ କରୁଅଛି…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d ଟି ନୂତନ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"
+msgstr[1] "%d ଟି ନୂତନ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "ଅପରିବର୍ତ୍ତନୀୟ କରନ୍ତୁ"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME ପରଦାକୁ ତାଲା ପକାଇବା ଆବଶ୍ୟକ କରିଥାଏ"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "ତାଲା ପକାଇବାରେ ଅସମର୍ଥ"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "ତାଲାଟି ଏକ ପ୍ରୟୋଗ ଦ୍ୱାରା ଅଟକ ହୋଇଛି"
+
+#: ../js/ui/search.js:594
+msgid "Searching…"
+msgstr "ସନ୍ଧାନ କରୁଅଛି…"
+
+#: ../js/ui/search.js:596
+msgid "No results."
+msgstr "କୌଣସି ଫଳାଫଳ ନାହିଁ।"
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "ନକଲ କରନ୍ତୁ"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "ଲଗାନ୍ତୁ"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "ପାଠ୍ୟ ଦର୍ଶାନ୍ତୁ"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "ପାଠ୍ଯ ଲୁଚାନ୍ତୁ"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "ପ୍ରବେଶ ସଙ୍କେତ"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "ପ୍ରବେଶ ସଂକେତ ମନେରଖନ୍ତୁ"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "ଅଭିଗମ୍ୟତା"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "ଛୋଟବଡ଼ କରନ୍ତୁ"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "ପରଦା ପାଠକ"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "ପରଦା କିବୋର୍ଡ"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "ଦୃଶ୍ଯମାନ ସତର୍କତା"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "ସାମୟିକ କିଗୁଡ଼ିକ"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "ମନ୍ଥର କିଗୁଡ଼ିକ"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "ଡେଉଁଥିବା କିଗୁଡ଼ିକ"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "ମାଉସ କିଗୁଡ଼ିକ"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "ଅଧିକ ସ୍ପଷ୍ଟତା"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "ବୃହତ ପାଠ୍ୟ"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "ବ୍ଲୁଟୁଥ"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "ଅଫ୍‌ କରନ୍ତୁ"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "ବ୍ଲୁଟୁଥ ସଂରଚନାଗୁଡ଼ିକ"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d ଟି ସଂଯୁକ୍ତ ଉପକରଣ"
+msgstr[1] "%d ଟି ସଂଯୁକ୍ତ ଉପକରଣଗୁଡ଼ିକ"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+msgid "Not Connected"
+msgstr "ସଂଯୁକ୍ତ ହୋଇନାହିଁ"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "ଉଜ୍ଜ୍ବଳତା"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "କି-ବୋର୍ଡ ବିନ୍ଯାସ ଦର୍ଶାନ୍ତୁ"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "ଅବସ୍ଥାନ"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "ନିଷ୍କ୍ରିୟ"
+
+#: ../js/ui/status/location.js:73
+#| msgid "Power Settings"
+msgid "Privacy Settings"
+msgstr "ଗୋପନୀୟତା ସେଟିଙ୍ଗ"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "ବ୍ଯବହୃତ ହେଉଅଛି"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "ସକ୍ରିୟ"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<unknown>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+msgid "Off"
+msgstr "ଅଫ"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "ସଂଯୁକ୍ତ"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "ଅପରିଚାଳିତ"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "ସଂଯୋଗ ବିଚ୍ଛିନ୍ନ କରୁଅଛି"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+msgid "Connecting"
+msgstr "ସଂଯୋଗ କରୁଅଛି"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "ବୈଧିକରଣ ଆବଶ୍ଯକ"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "ଫର୍ମୱେର ଅନୁପସ୍ଥିତ ଅଛି"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "ଉପଲବ୍ଧ ନାହିଁ"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "ସଂଯୋଗ ବିଫଳ ହୋଇଛି"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "ତାରମୟ ସଂରଚନା"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "ମୋବାଇଲ ବ୍ରୋଡ଼ବ୍ୟାଣ୍ଡ ସଂରଚନା"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+msgid "Hardware Disabled"
+msgstr "ହାର୍ଡୱେର ନିଷ୍କ୍ରିୟ ଅଛି"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "ଇଣ୍ଟରନେଟ ସଂଯୋଗ ବ୍ୟବହାର କରନ୍ତୁ"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "ବିମାନ ଧାରା ଅନ୍‌ ଅଛି"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "ବିମାନ ଧାରା ଅନ୍‌ ଥିବା ସମୟରେ ୱାଇ-ଫାଇ ନିଷ୍କ୍ରିୟ ଅଛି।"
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "ବିମାନ ଧାରାକୁ ଅଫ୍‌ କରନ୍ତୁ"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi ଅଫ୍‌ ଅଛି"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "ନେଟୱର୍କ ସହିତ ସଂଯୋଗ କରିବା ପାଇଁ ୱାଇ-ଫାଇକୁ ଅନ୍‌ କରିବା ଆବଶ୍ୟକ।"
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi କୁ ଅନ୍‌ କରନ୍ତୁ"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "ୱାଇ-ଫାଇ ନେଟୱର୍କଗୁଡ଼ିକ"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "ଗୋଟିଏ ନେଟୱର୍କ ବାଛନ୍ତୁ"
+
+#: ../js/ui/status/network.js:882
+msgid "No Networks"
+msgstr "କୌଣସି ନେଟୱର୍କ ନାହିଁ"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "ଅଫ୍‌ କରିବାକୁ ଥିବା ବ୍ୟବହାରକାରୀ ହାର୍ଡୱେର ସ୍ୱୀଚ"
+
+#: ../js/ui/status/network.js:1173
+msgid "Select Network"
+msgstr "ନେଟୱର୍କ ବାଛନ୍ତୁ"
+
+#: ../js/ui/status/network.js:1179
+msgid "Wi-Fi Settings"
+msgstr "ୱାଇ-ଫାଇ ସଂରଚନା"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "ଅନ୍‌ କରନ୍ତୁ"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "ସଂଯୋଗ କରୁଅଛି..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "ବୈଧିକରଣ ଆବଶ୍ୟକ"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "ସଂଯୋଗ ବିଫଳ ହୋଇଛି"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "ନେଟୱର୍କ ସଂରଚନା"
+
+#: ../js/ui/status/network.js:1482
+msgid "VPN Settings"
+msgstr "VPN ସଂରଚନା"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "ନେଟୱର୍କ ପରିଚାଳକ"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "ନେଟୱର୍କ ସଂଯୋଗ ବିଫଳ ହୋଇଛି"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "ବିଦ୍ୟୁତ ଶକ୍ତି ବିନ୍ଯାସ"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ ଚାର୍ଜ ହୋଇଛି"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "ହିସାବ କରୁଅଛି ..."
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d ବଳିଅଛି (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d ସମ୍ପୂର୍ଣ୍ଣ ହେବା ପର୍ଯ୍ୟନ୍ତ (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "ବ୍ଯାଟେରୀ"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "ବିମାନ ଧାରା"
+
+#: ../js/ui/status/rfkill.js:85
+msgid "On"
+msgstr "ଅନ୍‌"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "ଚାଳକଙ୍କୁ ବଦଳାନ୍ତୁ"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "ଲଗଆଉଟ କରନ୍ତୁ"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "ଆବର୍ତ୍ତନ ତାଲା"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "ନିଲମ୍ବନ କରନ୍ତୁ"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ କରନ୍ତୁ"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "ଆକାର ପରିବର୍ତ୍ତିତ"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "ଭଲ୍ଯୁମ"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "ମାଇକ୍ରଫୋନ"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "ଅନ୍ୟ ଏକ ଚାଳକ ଭାବରେ ଲଗଇନ କରନ୍ତୁ"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "ୱିଣ୍ଡୋକୁ ଖୋଲନ୍ତୁ"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "ପ୍ରୟୋଗଗୁଡ଼ିକ"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "ସନ୍ଧାନ କରନ୍ତୁ"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” ପ୍ରସ୍ତୁତ ଅଛି"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "ଆପଣ ଏହି ପ୍ରଦର୍ଶନୀ ସଂରଚନାକୁ ରଖିବାକୁ ଚାହୁଁଛନ୍ତି କି?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "ସଂରଚନାକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "ପରିବର୍ତ୍ତନଗୁଡ଼ିକୁ ରଖନ୍ତୁ"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "ସଂରଚନା ପରିବର୍ତ୍ତନଗୁଡ଼ିକ %d ସେକେଣ୍ଡରେ ପ୍ରତ୍ୟାବୃତ ହେବ"
+msgstr[1] "ସଂରଚନା ପରିବର୍ତ୍ତନଗୁଡ଼ିକ %d ସେକେଣ୍ଡରେ ପ୍ରତ୍ୟାବୃତ ହେବ"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "ଛୋଟକରନ୍ତୁ"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "ବଡ଼କରନ୍ତୁ ନାହିଁ"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "ବଡ଼କରନ୍ତୁ"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "ଘୁଞ୍ଚାନ୍ତୁ"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "ଆକାର ବଦଳାନ୍ତୁ"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "ଶୀର୍ଷକ ପଟିକୁ ପରଦା ଉପରେ ଘୁଞ୍ଚାନ୍ତୁ"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "ସର୍ବଦା ଉପରେ"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "ସଦାବେଳେ ଦୃଶ୍ଯମାନ କାର୍ଯ୍ଯକ୍ଷେତ୍ରରେ ଥାଏ"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "ଉପର କାର୍ଯ୍ଯକ୍ଷେତ୍ରକୁ ପଠାନ୍ତୁ"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "ତଳ କାର୍ଯ୍ଯକ୍ଷେତ୍ରକୁ ପଠାନ୍ତୁ"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution କ୍ୟାଲେଣ୍ଡର"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u ଫଳାଫଳ"
+msgstr[1] "%u ଫଳାଫଳଗୁଡ଼ିକ"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ନିବେଶ"
+msgstr[1] "%u ନିବେଶଗୁଡ଼ିକ"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "ତନ୍ତ୍ର ଧ୍ୱନି"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "ମୁଦ୍ରଣ ସଂସ୍କରଣ"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "ଲଗଇନ ପରଦା ପାଇଁ ବ୍ୟବହୃତ GDM"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "ଏକ ନିର୍ଦ୍ଦିଷ୍ଟ ଧାରା ବ୍ୟବହାର କରନ୍ତୁ, ଯେପରିକି ଲଗଇନ ପରଦା ପାଇଁ \"gdm\""
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "ସମ୍ଭାବ୍ୟ ଧାରାଗୁଡ଼ିକୁ ତାଲିକାଭୁକ୍ତ କରନ୍ତୁ"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” କୁ ଆରମ୍ଭ କରିବାରେ ବିଫଳ"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "ପ୍ରବେଶ ସଙ୍କେତ ଦ୍ବୟ ମିଶୁ ନାହିଁ।"
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "ପ୍ରବେଶ ସଙ୍କେତଟି ଖାଲି ହୋଇ ପାରିବ ନାହିଁ"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ବୈଧିକରଣ ସଂଳାପଟି ଚାଳକ ଦ୍ୱାରା ବାତିଲ ହୋଇଛି"
+
+#~ msgid "Screenshots"
+#~ msgstr "ପରଦା ପ୍ରତିଛବିଗୁଡ଼ିକ"
+
+#~ msgid "Record a screencast"
+#~ msgstr "ସ୍କ୍ରିନକାଷ୍ଟକୁ ଲିପିବଦ୍ଧ କରନ୍ତୁ"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "ପ୍ରୟୋଗ ଉପଯୋଗ ବିଷୟରେ ସ୍ଥିତି ସଂଗ୍ରହ କରିବା ଉଚିତ କି"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "ବହୁଳ ଭାବରେ ବ୍ୟବହୃତ ପ୍ରୟୋଗକୁ ଉପସ୍ଥାପନ କରିବା ପାଇଁ ସେଲ ସାଧାରଣତଃ ସକ୍ରିୟ ପ୍ରୟୋଗଗୁଡ଼ିକୁ ନିରୀକ୍ଷଣ "
+#~ "କରିଥାଏ। ଯେତେବେଳେ ଏହି ତଥ୍ୟକୁ ବ୍ୟକ୍ତିଗତ ରଖାଯାଇଥାଏ, ସେତେବେଳେ ଆପଣ ଏହାକୁ ବ୍ୟକ୍ତିଗତ କାରଣ "
+#~ "ହେତୁ ନିଷ୍କ୍ରିୟ କରିପାରିବେ। ଦୟାକରିମନେରଖନ୍ତୁ ଯେ ଏହା କରିବା ଫଳରେ ତାହା ସଂରକ୍ଷିତ ତଥ୍ୟକୁ ବାହାର "
+#~ "କରିନଥାଏ।"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "ବିଭାଗଗୁଡ଼ିକର ତାଲିକା ଯାହାକୁ ଫୋଲଡର୍‌ ଭାବରେ ଦର୍ଶାଯାଇଥାଏ"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "ମୂଖ୍ୟ ଦୃଶ୍ୟରେ ଦର୍ଶାଇବା ପରିବର୍ତ୍ତେ, ଏହି ତାଲିକାରେ ପ୍ରତ୍ୟେକ ବିଭାଗ ନାମକୁ ପ୍ରୟୋଗ ଦୃଶ୍ୟରେ ଫୋଲଡର୍‌ "
+#~ "ଭାବରେ ଉପସ୍ଥାପନା କରାଯାଇଥାଏ।"
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "ଶେଷ IM ଉପସ୍ଥିତିକୁ ସଂରକ୍ଷଣ କରିବା ପାଇଁ ଆଭ୍ୟନ୍ତରୀଣ ବ୍ୟବହାର ପାଇଁ ବ୍ୟବହାରକାରୀ ଦ୍ୱାରା ସେଟ "
+#~ "କରାଯାଇଥାଏ। ଏଠାରେ ଥିବା ମୂଲ୍ଯଟି ହେଉଛି TpConnectionPresenceType enumeration."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "ବ୍ୟବହାରକାରୀ ପାଇଁ ଅନ୍ତିମ ଅଧିବେଶନ ଉପସ୍ଥିତିକୁ ସଂରକ୍ଷଣ କରିବା ପାଇଁ ଆଭ୍ୟନ୍ତରିଣ ଭାବରେ ବ୍ୟବହାର "
+#~ "କରାଯାଇଥାଏ। ଏଠାରେ ଥିବା ମୂଲ୍ଯଟି ହେଉଛି GsmPresenceStatus enumeration."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "ପରଦା ଲେଖକକୁ ଆଗପଛ କରିବା ପାଇଁ କି ବନ୍ଧନ"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "ପୂର୍ବନିର୍ମିତ ପରଦା ଲେଖକକୁ ଆରମ୍ଭ/ବନ୍ଦ କରିବା ପାଇଁ କି ବନ୍ଧନ।"
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "ସ୍କ୍ରିନକାଷ୍ଟଗୁଡ଼ିକୁ ଲିପିବଦ୍ଧ କରିବା ପାଇଁ ବ୍ୟବହୃତ ଫ୍ରେମ ହାର।"
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "GNOME ସେଲ ସ୍କ୍ରିନକାଷ୍ଟ ଲେଖକ ଦ୍ୱାରା ଲିପିବଦ୍ଧ ଫଳାଫଳ ସ୍କ୍ରିନକାଷ୍ଟର ଫ୍ରେମ ହାର ହେଉଛି ସେକଣ୍ଡ "
+#~ "ପ୍ରତି ଫ୍ରେମ ସଂଖ୍ୟା।"
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "ସ୍କ୍ରିନକାଷ୍ଟକୁ ସାଙ୍କେତିକରଣ କରିବା ପାଇଁ ବ୍ୟବହୃତ gstreamer"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "ଲିପିବଦ୍ଧ କରିବା ପାଇଁ ସାଙ୍କେତିକରଣରେ ବ୍ୟବହୃତ GStreamer ପାଇପଲାଇନ। ଏହା gst-launch ପାଇଁ "
+#~ "ବ୍ୟବହୃତ ବାକ୍ୟ ବିନ୍ୟାସକୁ ଅନୁସରଣ କରିଥାଏ। ସେହି ପାଇପଲାଇନରେ ଅସଂଯୁକ୍ତ ସିଙ୍କ ପ୍ୟାଡ଼ ଥାଏ "
+#~ "ଯେଉଁଠିଲିପିବଦ୍ଧ ଭିଡ଼ିଓକୁ ରଖାଯାଇଥାଏ। ଏଥିରେ ସାଧାରଣତଃ ଅସଂଯୁକ୍ତ ଉତ୍ସ ପ୍ୟାଡ଼ ଥାଏ; ସେହି ପ୍ୟାଡ଼ର "
+#~ "ଫଳାଫଳକୁ ଫଳାଫଳ ଫାଇଲରେ ଲେଖାଯାଇଥାଏ। ତଥାପି ସେହି ପାଇପଲାଇନ ତାହାର ଯତ୍ନ ନେଇପାରିବ -ଏହା "
+#~ "ହୁଏତଃଫଳାଫଳକୁ icecast ସର୍ଭରକୁ shout2send କିମ୍ବା ସେହିପରି କିଛି ମାଧ୍ଯମରେପଠାଇବାରେ ବ୍ୟବହାର "
+#~ "କରାଯାଇପାରିବ। ଯେତେବେଳେ ସେଟ କରାଯାଇନଥାଏ କିମ୍ବା ଖାଲି ମୂଲ୍ୟ ସେଟ ହୋଇଥାଏ, ସେତେବେଳେ "
+#~ "ପୂର୍ବନିର୍ଦ୍ଧାରିତ ପାଇପଲାଇନକୁ ବ୍ୟବହାର କରାଯିବ। ଏହା ବର୍ତ୍ତମାନ 'vp8enc min_quantizer=13 "
+#~ "max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! "
+#~ "webmmux' ଏବଂ WEBM ରେ VP8 କୋଡେକ ବ୍ୟବହାର କରି ଲିପିବଦ୍ଧ କରିଥାଏ। %T କୁ ତନ୍ତ୍ରରେ ଏକ ସ୍ଥାନ "
+#~ "ରକ୍ଷକ ଭାବରେ ବ୍ୟବହାର କରାଯାଇଥାଏ।"
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "ସ୍କ୍ରିନକାଷ୍ଟକୁ ସଂରକ୍ଷଣ କରିବା ପାଇଁ ବ୍ୟବହୃତ ଫାଇଲ ଅନୁଲଗ୍ନ"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "ଲିପିବଦ୍ଧ ସ୍କ୍ରିନକାଷ୍ଟ ପାଇଁ ଫାଇଲ ନାମଟି ବର୍ତ୍ତମାନ ତାରିଖ ଅନୁସାରେ ଏକ ଅନନ୍ୟ ଫାଇଲନାମ "
+#~ "ଦିଆଯାଇଥାଏ, ଏବଂ ଏହି ଅନୁଲଗ୍ନକୁ ବ୍ୟବହାର କରାଯାଇଥାଏ। ଏହାକୁ ବିଭିନ୍ନ ଧାରଣକର୍ତ୍ତା ଶୈଳୀରେ "
+#~ "ପରିବର୍ତ୍ତନ କରିବା ଉଚିତ।"
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "ଶୀର୍ଷକ ପଟିରେ ଚାବିର ସଜ୍ଜା"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "GNOME ସେଲ ଚାଲିଥିବା ସମୟରେ ଏହି ଚାବିଟି org.gnome.desktop.wm.preferences ରେ ଥିବା "
+#~ "ଚାବିକୁ ନବଲିଖନ କରିଥାଏ।"
+
+#~ msgid "Extension"
+#~ msgstr "ଅନୁଲଗ୍ନ"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "ଉପରେ ଥିବା କମ୍ବୋବକ୍ସ ବ୍ୟବହାର କରି ବିନ୍ୟାସ କରିବା ପାଇଁ ଏକ ଅନୁଲଗ୍ନ ବାଛନ୍ତୁ।"
+
+#~| msgid "Session..."
+#~ msgid "Session…"
+#~ msgstr "ଅଧିବେଶନ…"
+
+#~| msgid "Power Off"
+#~ msgid "Power"
+#~ msgstr "ବିଦ୍ୟୁତ ଶକ୍ତି"
+
+#~ msgid "Restart"
+#~ msgstr "ପୂନଃପ୍ରାରମ୍ଭ କରନ୍ତୁ"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d %t ରୁ ସ୍କ୍ରିନକାଷ୍ଟ"
+
+#~| msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~| msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "ଏହି ପ୍ରୟୋଗଗୁଡ଼ିକରୁ ପ୍ରସ୍ଥାନ କରିବା ପାଇଁ ଲଗ ଆଉଟ କ୍ଲିକ କରନ୍ତୁ ଏବଂ ତନ୍ତ୍ରରୁ ଲଗ ଆଉଟ କରନ୍ତୁ।"
+
+#~ msgid "Logging out of the system."
+#~ msgstr "ତନ୍ତ୍ରରୁ ଲଗ ଆଉଟ କରୁଅଛି।"
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "ଏହି ପ୍ରୟୋଗଗୁଡ଼ିକରୁ ପ୍ରସ୍ଥାନ କରିବା ପାଇଁ ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ କରନ୍ତୁକୁ କ୍ଲିକ କରନ୍ତୁ ଏବଂ ତନ୍ତ୍ରର ବିଦ୍ୟୁତ "
+#~ "ପ୍ରବାହ ବନ୍ଦ କରନ୍ତୁ।"
+
+#~ msgid "Powering off the system."
+#~ msgstr "ତନ୍ତ୍ରର ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ ହେଉଅଛି।"
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "ଏହି ପ୍ରୟୋଗଗୁଡ଼ିକରୁ ପ୍ରସ୍ଥାନ କରିବା ପାଇଁ ପୁନଃପ୍ରାରମ୍ଭ କରନ୍ତୁକୁ କ୍ଲିକ କରନ୍ତୁ ଏବଂ ତନ୍ତ୍ରକୁ ପୁନଃପ୍ରାରମ୍ଭ "
+#~ "କରନ୍ତୁ।"
+
+#~ msgid "Restarting the system."
+#~ msgstr "ତନ୍ତ୍ରକୁ ପୁନଃଚାଳନ କରୁଅଛି।"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "ସାର୍ବଜନିକ ଅଭିଗମ୍ୟତା ବିନ୍ଯାସ"
+
+#~ msgid "Visibility"
+#~ msgstr "ଦେଖିବା କ୍ଷମତା"
+
+#~| msgid "Send Files to Device..."
+#~ msgid "Send Files to Device…"
+#~ msgstr "ଉପକରଣ ମଧ୍ଯକୁ ଫାଇଲଗୁଡ଼ିକୁ ପଠାନ୍ତୁ…"
+
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "ଏକ ନୂଆ ଉପକରଣ ବ୍ୟବସ୍ଥା କରନ୍ତୁ ..."
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "ଫାଇଲଗୁଡ଼ିକୁ ପଠାନ୍ତୁ…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "କିବୋର୍ଡ ବିନ୍ୟାସଗୁଡ଼ିକ"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "ମାଉସ ସେଟିଙ୍ଗଗୁଡିକ"
+
+#~ msgid "Sound Settings"
+#~ msgstr "ଧ୍ୱନି ସେଟିଙ୍ଗଗୁଡିକ"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s ରୁ ବୈଧିକରଣ ଅନୁରୋଧ"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "ଉପକରଣ %s ସର୍ଭିସ '%s' ପାଇଁ ଅଭିଗମ୍ୟତା ଆଶା କରିଥାଏ।"
+
+#~ msgid "Always grant access"
+#~ msgstr "ସର୍ବଦା ଅନୁମତି ପ୍ରଦାନ କରନ୍ତୁ"
+
+#~ msgid "Grant this time only"
+#~ msgstr "କେବଳ ବର୍ତ୍ତମାନ ପାଇଁ ଅନୁମତି ପ୍ରଦାନ କରନ୍ତୁ"
+
+#~ msgid "Reject"
+#~ msgstr "ଅସ୍ବୀକାର କରନ୍ତୁ"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s ପାଇଁ ଯୁଗଳ ନିଶ୍ଚିତକରଣ"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "ଉପକରଣ %s ଏହି କମ୍ପୁଟର ସହିତ ଯୁଗଳ ହେବାକୁ ଇଚ୍ଛାପ୍ରକାଶ କରୁଅଛି"
+
+#~ msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgstr "ଦୟାକରି ନିଶ୍ଚିତ କରନ୍ତୁ ଯେ PIN '%06d' ଉପକରଣ ସହିତ ମେଳ ଖାଇଥାଏ।"
+
+#~ msgid "Matches"
+#~ msgstr "ମେଳଗୁଡିକ"
+
+#~ msgid "Does not match"
+#~ msgstr "ମେଳ ଖାଉନାହିଁ"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s ପାଇଁ ଯୁଗଳ ଅନୁରୋଧ"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "ଉପକରଣରେ ନିର୍ଦ୍ଦିଷ୍ଟ PIN କୁ ଭରଣ କରନ୍ତୁ।"
+
+#~ msgid "OK"
+#~ msgstr "ଠିକ ଅଛି"
+
+#~| msgid "Region and Language Settings"
+#~ msgid "Region & Language Settings"
+#~ msgstr "ସ୍ଥାନ ଏବଂ ଭାଷା ସେଟିଙ୍ଗଗୁଡିକ"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "ଭଲ୍ୟୁମ, ନେଟୱର୍କ, ବ୍ୟାଟେରୀ"
+
+#~ msgid "disabled"
+#~ msgstr "ନିଷ୍କ୍ରିୟ ହୋଇଛି"
+
+#~ msgid "cable unplugged"
+#~ msgstr "କେବଲ ଲଗାହୋଇ ନାହିଁ"
+
+#~ msgid "unavailable"
+#~ msgstr "ଉପଲବ୍ଧ ନାହିଁ"
+
+#~ msgid "More…"
+#~ msgstr "ଅଧିକ…"
+
+#~ msgid "Wired"
+#~ msgstr "ତାରଯୁକ୍ତ"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "ସ୍ୱୟଂଚାଳିତ ଇଥରନେଟ"
+
+#~ msgid "Auto broadband"
+#~ msgstr "ସ୍ୱୟଂଚାଳିତ ବ୍ରୋଡ଼ବ୍ୟାଣ୍ଡ"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "ସ୍ୱୟଂଚାଳିତ ଡାଏଲ-ଅପ"
+
+#~ msgid "Auto %s"
+#~ msgstr "ସ୍ବୟଂଚାଳିତ %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "ସ୍ବୟଂଚାଳିତ ବ୍ଲୁଟୁଥ"
+
+#~ msgid "Auto wireless"
+#~ msgstr "ସ୍ବୟଂଚାଳିତ ବେତାର"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "Networking is disabled"
+#~ msgstr "ନେଟୱର୍କିଙ୍ଗ ନିଷ୍କ୍ରିୟ ହୋଇଛି"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d ଘଣ୍ଟା ବଳିଛି"
+#~ msgstr[1] "%d ଘଣ୍ଟା ବଳିଛି"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s ବଳିଅଛି"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ଘଣ୍ଟା"
+#~ msgstr[1] "ଘଣ୍ଟା"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "ମିନିଟ"
+#~ msgstr[1] "ମିନିଟ"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d ମିନଟ ବଳିଅଛି"
+#~ msgstr[1] "%d ମିନଟ ବଳିଅଛି"
+
+#~ msgctxt "percent of battery remaining"
+#~ msgid "%d%%"
+#~ msgstr "%d%%"
+
+#~| msgid "AC adapter"
+#~ msgid "AC Adapter"
+#~ msgstr "AC ଏଡପଟର"
+
+#~| msgid "Laptop battery"
+#~ msgid "Laptop Battery"
+#~ msgstr "ଲାପଟପ ବ୍ଯାଟେରୀ"
+
+#~ msgid "Monitor"
+#~ msgstr "ମନିଟର"
+
+#~ msgid "Mouse"
+#~ msgstr "ମାଉସ"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~| msgid "Cell phone"
+#~ msgid "Cell Phone"
+#~ msgstr "ସେଲ ଫୋନ"
+
+#~| msgid "Media player"
+#~ msgid "Media Player"
+#~ msgstr "ମେଡ଼ିଆ ଚାଲକ"
+
+#~ msgid "Tablet"
+#~ msgstr "ଟ୍ୟାବଲେଟ"
+
+#~ msgid "Computer"
+#~ msgstr "କମ୍ପୁଟର"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "ଅଜଣା"
+
+#~ msgid "Available"
+#~ msgstr "ଉପଲବ୍ଧ "
+
+#~ msgid "Busy"
+#~ msgstr "ବ୍ଯସ୍ତ"
+
+#~ msgid "Invisible"
+#~ msgstr "ଅଦୃଶ୍ଯ"
+
+#~ msgid "Away"
+#~ msgstr "ଅନୁପସ୍ଥିତ"
+
+#~ msgid "Idle"
+#~ msgstr "ନିଷ୍କ୍ରିୟ"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "ଆପଣଙ୍କର ଚାଟ ସ୍ଥିତିକୁ ବ୍ୟସ୍ତ ବୋଲି ସେଟ କରାଯିବ"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "ଆଳାପ ସନ୍ଦେଶଗୁଡ଼ିକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରି ବିଜ୍ଞପ୍ତିକୁ ବର୍ତ୍ତମାନ ନିଷ୍କ୍ରିୟ କରାଯାଇଛି। ଆପଣ ଅନ୍ୟମାନଙ୍କର "
+#~ "ସନ୍ଦେଶଗୁଡ଼ିକୁ ଦେଖିପାରୁଛନ୍ତି କି ନାହିଁ ତାହା ଜଣାଇବା ପାଇଁ ଆପଣଙ୍କର ଅନଲାଇନ ସ୍ଥିତିକୁ ସଜଡ଼ାଯାଇଛି।"
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "ବନ୍ଦ କରିବା ଫଳରେ ହୁଏତ ସେମାନେ ତାଙ୍କର ଅସଂରକ୍ଷିତ କାର୍ଯ୍ୟକୁ ହରାଇପାରନ୍ତି।"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "କ୍ଷମା କରିବେ, ଆପଣଙ୍କ ପାଇଁ ଆଜି କୌଣସି ଜ୍ଞାନ ନାହିଁ:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s Oracle କହିଥାଏ"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "ସାଇନ ଇନ"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "ପ୍ରୟୋଗଗୁଡ଼ିକ"
+
+#~ msgid "SETTINGS"
+#~ msgstr "ସେଟିଙ୍ଗଗୁଡିକ"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Subscription request"
+#~ msgstr "ସଦସ୍ୟତା ‍ଅନୁରୋଧ"
+
+#~ msgid "Connection error"
+#~ msgstr "ସଂଯୋଗ ତ୍ରୁଟି"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "<b>%X</b> କୁ <b>%A</b> ଉପରେ ପଠାଯାଇଛି"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "<b>%A</b> ଉପରେ ପଠାଯାଇଛି, <b>%B %d</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "%s ସହିତ ସଂଯୋଗ ବିଫଳ ହୋଇଛି"
+
+#~ msgid "Reconnect"
+#~ msgstr "ପୁନଃସଂଯୋଗ କରନ୍ତୁ"
+
+#~ msgid "tray"
+#~ msgstr "ଟ୍ରେ"
+
+#~ msgid "Browse Files..."
+#~ msgstr "ଫାଇଲଗୁଡ଼ିକୁ ବ୍ରାଉଜ କରନ୍ତୁ..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "ଉପକରଣକୁ ବ୍ରାଉଜ କରିବାରେ ତ୍ରୁଟି"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "ଅନୁରୋଧ କରାଯାଇଥିବା ଉପକରଣକୁ ବ୍ରାଉଜ କରିହେବ ନାହିଁ, ତ୍ରୁଟି ହେଉଛି '%s'"
+
+#~ msgid "More..."
+#~ msgstr "ଅଧିକ..."
+
+#~ msgid "Wireless"
+#~ msgstr "ବେତାର"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN ସଂୟୋଗଗୁଡିକ"
+
+#~ msgid "System Settings"
+#~ msgstr "ତନ୍ତ୍ର ବିନ୍ୟାସ"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "ଆପଣଙ୍କର ମନପସନ୍ଦ ଇଷ୍ଟର ଏଗ"
+
+#~ msgid "United Kingdom"
+#~ msgstr "ଯୁକ୍ତ ରାଜ୍ଯ"
+
+#~ msgid "Default"
+#~ msgstr "ପୂର୍ବନିର୍ଦ୍ଧାରିତ"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "ସତ ଥିଲେ, ଘଣ୍ଟାରେ ସମୟ ସହିତ ତାରିଖ ଦେଖାନ୍ତୁ।"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "ସତ ଥିଲେ, ସମୟରେ ସେକେଣ୍ଡ ଦେଖାନ୍ତୁ।"
+
+#~ msgid "Show date in clock"
+#~ msgstr "ଘଣ୍ଟାରେ ତାରିଖ ଦେଖାନ୍ତୁ"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "ସମୟରେ ସେକେଣ୍ଡ ଦେଖାନ୍ତୁ"
+
+#~ msgid "CONTACTS"
+#~ msgstr "ସମ୍ପର୍କଗୁଡ଼ିକ"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "ସାମ୍ପ୍ରତିକ ବସ୍ତୁଗୁଡ଼ିକ"
+
+#~ msgid "Show password"
+#~ msgstr "ପ୍ରବେଶ ସଂକେତ ଦର୍ଶାନ୍ତୁ"
+
+#~ msgid "Retry"
+#~ msgstr "ପୁଣି ଚେଷ୍ଟାକରନ୍ତୁ"
+
+#~ msgid "Connect to..."
+#~ msgstr "ଏହା ସହିତ ସଂଯୋଗ କରନ୍ତୁ..."
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ସ୍ଥାନ ଏବଂ ଉପକରଣଗୁଡ଼ିକ"
+
+#~ msgid "%s is online."
+#~ msgstr "%s ଅନଲାଇନ ଅଛି।"
+
+#~ msgid "%s is offline."
+#~ msgstr "%s ଅଫଲାଇନ ଅଛି।"
+
+#~ msgid "%s is away."
+#~ msgstr "%s ଅନୁପସ୍ଥିତ ଅଛି।"
+
+#~ msgid "%s is busy."
+#~ msgstr "%s ବ୍ୟସ୍ତ ଅଛି।"
+
+#~ msgid "Hidden"
+#~ msgstr "ଲୁକ୍କାଇତ"
+
+#~ msgid "Power Off..."
+#~ msgstr "ବିଦ୍ୟୁତ ପ୍ରବାହ ବନ୍ଦ କରନ୍ତୁ..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "ଅନଲାଇନ ଖାତାଗୁଡ଼ିକ"
+
+#~ msgid "Lock Screen"
+#~ msgstr "ପରଦାକୁ ତାଲା ଦେଇ ରଖନ୍ତୁ"
+
+#~ msgid "Log Out..."
+#~ msgstr "ଲଗ ଆଉଟ..."
+
+#~ msgid "Home Folder"
+#~ msgstr "ମୂଳ ଫୋଲଡର"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "calendar:week_start:0"
+#~ msgstr "calendar:week_start:0"
diff --git a/po/os.po b/po/os.po
new file mode 100644
index 0000000..fda1faa
--- /dev/null
+++ b/po/os.po
@@ -0,0 +1,2803 @@
+# Ossetian translation for gnome-shell.
+# Copyright (C) 2020 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Soslan Khubulov <soslanx@gmail.com>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2020-04-28 09:12+0000\n"
+"PO-Revision-Date: 2020-05-03 18:52+0300\n"
+"Last-Translator: Soslan Khubulov <soslanx@gmail.com>\n"
+"Language-Team: Ossetian; Ossetic <soslanx@gmail.com>\n"
+"Language: os\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"X-Generator: Gtranslator 3.36.0\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Системӕ"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Хъусинӕгтӕ равдисын"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Активон хъусинагмӕ ахизын"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Афӕлгӕст равдисын"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Ӕфтуантӕ иууылдӕр равдисын"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Ӕфтуанты меню бакӕнын"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Рудзгуытӕй архайын ӕмӕ ӕфтуан иу кӕнын"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Аразджытӕн ӕмӕ тестгӕнджытӕн пайда мидӕггаг мигӕнӕнтӕ Alt-F2-йӕ баиу кӕнын"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 диалогы уылты мидӕггаг дебаггинг ӕмӕ мониторингы мигӕнӕнтӕм уадзы."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Иу кӕнинаг уӕлӕмхасӕнты UUID-тӕ"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell-ы уӕлӕмхасӕнтӕн ис UUID-тӕ; ацы дӕгъӕл ранымайы ӕвгӕнинаг "
+"уӕлӕмхасӕнтӕ. Кӕд искӕцы уӕлӕмхасӕн ӕвгӕнинаг у, уӕд хъуамӕ уа ацы "
+"номхыгъды. Ацы номхыгъд ма дӕ бон у org.gnome.Shell-ы EnableExtension ӕмӕ "
+"DisableExtension D-Bus-ы методтӕй ивын."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Тыххӕй хицӕнгӕнинаг уӕлӕмхасӕнты UUID-тӕ"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell-ы уӕлӕмхасӕнтӕн ис UUID-тӕ; ацы дӕгъӕл ранымайы хицӕнгӕнинаг "
+"уӕлӕмхасӕнтӕ, кӕд ныры режиммӕ гӕсгӕ ӕвгӕд сты, уӕддӕр. Ацы номхыгъд ма дӕ "
+"бон у org.gnome.Shell-ы EnableExtension ӕмӕ DisableExtension D-Bus-ы "
+"методтӕй ивын. Ацы дӕгъӕл-ӕн “enabled-extensions”-ӕй фылдӕр тых ис."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Архайӕджы уӕлӕмхасӕнтӕ ахицӕн кӕнын"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "App Picker View"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:69
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:74
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:78
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:87
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:97
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:106
+msgid "Enable introspection API"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:107
+msgid ""
+"Enables a D-Bus API that allows to introspect the application state of the "
+"shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:119
+msgid "Keybinding to open the application menu"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:120
+msgid "Keybinding to open the application menu."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:126
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:127
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid "Keybinding to open the overview"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:135
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:141
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to focus the active notification"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to focus the active notification."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:155
+msgid "Switch to application 1"
+msgstr "1-аг ӕфтуанмӕ ахизын"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Switch to application 2"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Switch to application 3"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:167
+msgid "Switch to application 4"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid "Switch to application 5"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:175
+msgid "Switch to application 6"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Switch to application 7"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:183
+msgid "Switch to application 8"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Switch to application 9"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:196
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Limit switcher to current workspace."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:197
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "The application icon mode."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Locations"
+msgstr "Бынӕттӕ"
+
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "The locations to show in world clocks"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:245
+msgid "Automatic location"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Whether to fetch the current location or not"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:253
+msgid "Location"
+msgstr "Бынат"
+
+#: data/org.gnome.shell.gschema.xml.in:254
+msgid "The location for which to show a forecast"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid "Attach modal dialog to the parent window"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:267
+#: data/org.gnome.shell.gschema.xml.in:276
+#: data/org.gnome.shell.gschema.xml.in:284
+#: data/org.gnome.shell.gschema.xml.in:292
+#: data/org.gnome.shell.gschema.xml.in:300
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Workspaces are managed dynamically"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:291
+msgid "Workspaces only on primary monitor"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:299
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:36
+#: subprojects/extensions-app/data/ui/extensions-window.ui:224
+msgid "Something’s gone wrong"
+msgstr "Цыдӕр раст нӕ ацыд"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:48
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:82
+msgid "Technical Details"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:165
+msgid "Homepage"
+msgstr ""
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:166
+msgid "Visit extension homepage"
+msgstr ""
+
+#: js/gdm/authPrompt.js:135 js/ui/audioDeviceSelection.js:57
+#: js/ui/components/networkAgent.js:110 js/ui/components/polkitAgent.js:139
+#: js/ui/endSessionDialog.js:369 js/ui/extensionDownloader.js:181
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:916 subprojects/extensions-app/js/main.js:148
+msgid "Cancel"
+msgstr ""
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:237 js/ui/components/networkAgent.js:205
+#: js/ui/components/networkAgent.js:221 js/ui/components/networkAgent.js:245
+#: js/ui/components/networkAgent.js:266 js/ui/components/networkAgent.js:286
+#: js/ui/components/networkAgent.js:296 js/ui/components/polkitAgent.js:277
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr ""
+
+#: js/gdm/loginDialog.js:318
+msgid "Choose Session"
+msgstr ""
+
+#: js/gdm/loginDialog.js:457
+msgid "Not listed?"
+msgstr ""
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:913
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr ""
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:918 js/ui/components/networkAgent.js:241
+#: js/ui/components/networkAgent.js:264 js/ui/components/networkAgent.js:282
+msgid "Username"
+msgstr ""
+
+#: js/gdm/loginDialog.js:1254
+msgid "Login Window"
+msgstr ""
+
+#: js/gdm/util.js:345
+msgid "Authentication error"
+msgstr ""
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:471
+msgid "(or swipe finger)"
+msgstr ""
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:93
+msgctxt "search-result"
+msgid "Power Off"
+msgstr ""
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:96
+msgid "power off;shutdown;reboot;restart;halt;stop"
+msgstr ""
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:101
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr ""
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:104
+msgid "lock screen"
+msgstr ""
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:109
+msgctxt "search-result"
+msgid "Log Out"
+msgstr ""
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:112
+msgid "logout;log out;sign off"
+msgstr ""
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:117
+msgctxt "search-result"
+msgid "Suspend"
+msgstr ""
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:120
+msgid "suspend;sleep"
+msgstr ""
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:125
+msgctxt "search-result"
+msgid "Switch User"
+msgstr ""
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:128
+msgid "switch user"
+msgstr ""
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:135
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+
+#: js/misc/systemActions.js:255
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr ""
+
+#: js/misc/systemActions.js:256
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr ""
+
+#: js/misc/util.js:120
+msgid "Command not found"
+msgstr ""
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:156
+msgid "Could not parse command:"
+msgstr ""
+
+#: js/misc/util.js:164
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr ""
+
+#: js/misc/util.js:181
+msgid "Just now"
+msgstr "Ацы тӕккӕ"
+
+#: js/misc/util.js:183
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:187
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:191
+msgid "Yesterday"
+msgstr "Знон"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:201
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/misc/util.js:204
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:237
+msgid "%H∶%M"
+msgstr ""
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:243
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr ""
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:249
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr ""
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:255
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr ""
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:261
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr ""
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:266
+msgid "%l∶%M %p"
+msgstr ""
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:272
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr ""
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:278
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr ""
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:284
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr ""
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:290
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr ""
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:41
+msgid "Hotspot Login"
+msgstr ""
+
+#: js/portalHelper/main.js:87
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:374
+msgid "Deny Access"
+msgstr ""
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:377
+msgid "Grant Access"
+msgstr ""
+
+#: js/ui/appDisplay.js:950
+msgid "Unnamed Folder"
+msgstr ""
+
+#: js/ui/appDisplay.js:973
+msgid "Frequently used applications will appear here"
+msgstr ""
+
+#: js/ui/appDisplay.js:1108
+msgid "Frequent"
+msgstr "Арӕх"
+
+#: js/ui/appDisplay.js:1115
+msgid "All"
+msgstr "Иууыл"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appDisplay.js:2504 js/ui/panel.js:75
+msgid "Open Windows"
+msgstr "Гом рудзгуытӕ"
+
+#: js/ui/appDisplay.js:2523 js/ui/panel.js:82
+msgid "New Window"
+msgstr "Ног рудзынг"
+
+#: js/ui/appDisplay.js:2534
+msgid "Launch using Dedicated Graphics Card"
+msgstr ""
+
+#: js/ui/appDisplay.js:2562 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "Сӕрмагондтӕй райсын"
+
+#: js/ui/appDisplay.js:2568
+msgid "Add to Favorites"
+msgstr "Сӕрмагондтӕм бахӕссын"
+
+#: js/ui/appDisplay.js:2578 js/ui/panel.js:93
+msgid "Show Details"
+msgstr ""
+
+#: js/ui/appFavorites.js:163
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr ""
+
+#: js/ui/appFavorites.js:196
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:54
+msgid "Sound Settings"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headphones"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:66
+msgid "Headset"
+msgstr ""
+
+#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:270
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr ""
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr ""
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr ""
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:41
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:70
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Х"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:72
+msgctxt "grid monday"
+msgid "M"
+msgstr "К"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:74
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Д"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:76
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ӕ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:78
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ц"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:80
+msgctxt "grid friday"
+msgid "F"
+msgstr "М"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:82
+msgctxt "grid saturday"
+msgid "S"
+msgstr "С"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:397
+msgid "%OB"
+msgstr ""
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:407
+msgid "%OB %Y"
+msgstr ""
+
+#: js/ui/calendar.js:466
+msgid "Previous month"
+msgstr "Ай размӕ мӕй"
+
+#: js/ui/calendar.js:481
+msgid "Next month"
+msgstr "Иннӕ мӕй"
+
+#: js/ui/calendar.js:631
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr ""
+
+#: js/ui/calendar.js:687
+msgid "Week %V"
+msgstr ""
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:762
+msgctxt "event list time"
+msgid "All Day"
+msgstr ""
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/calendar.js:900
+msgctxt "calendar heading"
+msgid "%A, %B %-d"
+msgstr ""
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/calendar.js:903
+msgctxt "calendar heading"
+msgid "%A, %B %-d, %Y"
+msgstr ""
+
+#: js/ui/calendar.js:1133
+msgid "No Notifications"
+msgstr ""
+
+#: js/ui/calendar.js:1136
+msgid "No Events"
+msgstr ""
+
+#: js/ui/calendar.js:1190
+msgid "Do Not Disturb"
+msgstr ""
+
+#: js/ui/calendar.js:1209
+msgid "Clear"
+msgstr ""
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr ""
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr ""
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr ""
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr ""
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr ""
+
+#: js/ui/components/automountManager.js:208
+msgid "Unable to unlock volume"
+msgstr ""
+
+#: js/ui/components/automountManager.js:209
+msgid "The installed udisks version does not support the PIM setting"
+msgstr ""
+
+#: js/ui/components/autorunManager.js:332
+#, javascript-format
+msgid "Open with %s"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:92
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:104 js/ui/status/network.js:227
+#: js/ui/status/network.js:318 js/ui/status/network.js:919
+msgid "Connect"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:211
+msgid "Key"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:249 js/ui/components/networkAgent.js:272
+msgid "Private key password"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:270
+msgid "Identity"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:284
+msgid "Service"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:313 js/ui/components/networkAgent.js:341
+#: js/ui/components/networkAgent.js:680 js/ui/components/networkAgent.js:701
+msgid "Authentication required"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:314 js/ui/components/networkAgent.js:681
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:685
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:320
+msgid "Network name"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:325 js/ui/components/networkAgent.js:689
+msgid "DSL authentication"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:332 js/ui/components/networkAgent.js:694
+msgid "PIN code required"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:333 js/ui/components/networkAgent.js:695
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:334
+msgid "PIN"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:342 js/ui/components/networkAgent.js:686
+#: js/ui/components/networkAgent.js:690 js/ui/components/networkAgent.js:702
+#: js/ui/components/networkAgent.js:706
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:669 js/ui/status/network.js:1694
+msgid "Network Manager"
+msgstr ""
+
+#: js/ui/components/networkAgent.js:705
+msgid "VPN password"
+msgstr ""
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr ""
+
+#: js/ui/components/polkitAgent.js:80
+msgid "Administrator"
+msgstr "Администратор"
+
+#: js/ui/components/polkitAgent.js:142
+msgid "Authenticate"
+msgstr ""
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:254 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr ""
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:822
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr ""
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:177
+msgid "Windows"
+msgstr "Рудзгуытӕ"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr ""
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr ""
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:75
+msgid "%B %-d %Y"
+msgstr ""
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:82
+msgid "%A %B %e %Y"
+msgstr ""
+
+#: js/ui/dateMenu.js:161
+msgid "Add world clocks…"
+msgstr ""
+
+#: js/ui/dateMenu.js:162
+msgid "World Clocks"
+msgstr ""
+
+#: js/ui/dateMenu.js:424
+msgid "Loading…"
+msgstr ""
+
+#: js/ui/dateMenu.js:434
+msgid "Go online for weather information"
+msgstr ""
+
+#: js/ui/dateMenu.js:436
+msgid "Weather information is currently unavailable"
+msgstr ""
+
+#: js/ui/dateMenu.js:446
+msgid "Weather"
+msgstr "Боныгъӕд"
+
+#: js/ui/dateMenu.js:448
+msgid "Select weather location…"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:37
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:38
+msgctxt "title"
+msgid "Log Out"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:40
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:51
+msgctxt "button"
+msgid "Log Out"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "title"
+msgid "Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:57
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:59
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:66 js/ui/endSessionDialog.js:82
+msgctxt "button"
+msgid "Restart"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:68
+msgctxt "button"
+msgid "Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "title"
+msgid "Restart"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:76
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:89
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:91
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/endSessionDialog.js:97 js/ui/endSessionDialog.js:116
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:98
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:99
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr ""
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:111
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:259
+msgid "Running on battery power: Please plug in before installing updates."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:268
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+
+#: js/ui/endSessionDialog.js:273
+msgid "Other users are logged in"
+msgstr ""
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:583
+#, javascript-format
+msgid "%s (remote)"
+msgstr ""
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:586
+#, javascript-format
+msgid "%s (console)"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:185
+msgid "Install"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:191
+msgid "Install Extension"
+msgstr ""
+
+#: js/ui/extensionDownloader.js:192
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+
+#: js/ui/extensionSystem.js:251
+msgid "Extension Updates Available"
+msgstr ""
+
+#: js/ui/extensionSystem.js:252
+msgid "Extension updates are ready to be installed."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr ""
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:98
+msgid "Deny"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:105
+msgid "Allow"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:42
+msgid "Sticky Keys Turned Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:44
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:46
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:1291
+msgid "Turn On"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:63 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:135 js/ui/status/network.js:319
+#: js/ui/status/network.js:1291 js/ui/status/network.js:1403
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:108
+msgid "Turn Off"
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:63
+msgid "Leave Off"
+msgstr ""
+
+#: js/ui/keyboard.js:207
+msgid "Region & Language Settings"
+msgstr ""
+
+#: js/ui/lookingGlass.js:664
+msgid "No extensions installed"
+msgstr ""
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:719
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: js/ui/lookingGlass.js:725
+msgid "Hide Errors"
+msgstr ""
+
+#: js/ui/lookingGlass.js:729 js/ui/lookingGlass.js:794
+msgid "Show Errors"
+msgstr ""
+
+#: js/ui/lookingGlass.js:738
+msgid "Enabled"
+msgstr "Иу"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:741 subprojects/gvc/gvc-mixer-control.c:1892
+msgid "Disabled"
+msgstr "Хицӕн"
+
+#: js/ui/lookingGlass.js:743
+msgid "Error"
+msgstr "Рӕдыд"
+
+#: js/ui/lookingGlass.js:745
+msgid "Out of date"
+msgstr ""
+
+#: js/ui/lookingGlass.js:747
+msgid "Downloading"
+msgstr ""
+
+#: js/ui/lookingGlass.js:776
+msgid "View Source"
+msgstr ""
+
+#: js/ui/lookingGlass.js:785
+msgid "Web Page"
+msgstr ""
+
+#: js/ui/main.js:297
+msgid "Logged in as a privileged user"
+msgstr ""
+
+#: js/ui/main.js:298
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+
+#: js/ui/main.js:337
+msgid "Screen Lock disabled"
+msgstr ""
+
+#: js/ui/main.js:338
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+
+#: js/ui/messageTray.js:1543
+msgid "System Information"
+msgstr ""
+
+#: js/ui/mpris.js:199
+msgid "Unknown artist"
+msgstr ""
+
+#: js/ui/mpris.js:209
+msgid "Unknown title"
+msgstr ""
+
+#: js/ui/overview.js:73
+msgid "Undo"
+msgstr ""
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:86
+msgid "Overview"
+msgstr "Афӕлгӕст"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:107
+msgid "Type to search"
+msgstr ""
+
+#: js/ui/padOsd.js:95
+msgid "New shortcut…"
+msgstr ""
+
+#: js/ui/padOsd.js:142
+msgid "Application defined"
+msgstr ""
+
+#: js/ui/padOsd.js:143
+msgid "Show on-screen help"
+msgstr ""
+
+#: js/ui/padOsd.js:144
+msgid "Switch monitor"
+msgstr ""
+
+#: js/ui/padOsd.js:145
+msgid "Assign keystroke"
+msgstr ""
+
+#: js/ui/padOsd.js:211
+msgid "Done"
+msgstr "Ӕххӕст"
+
+#: js/ui/padOsd.js:745
+msgid "Edit…"
+msgstr "Ивӕн..."
+
+#: js/ui/padOsd.js:787 js/ui/padOsd.js:910
+msgid "None"
+msgstr "Никӕцы"
+
+#: js/ui/padOsd.js:863
+msgid "Press a button to configure"
+msgstr ""
+
+#: js/ui/padOsd.js:864
+msgid "Press Esc to exit"
+msgstr ""
+
+#: js/ui/padOsd.js:867
+msgid "Press any key to exit"
+msgstr ""
+
+#: js/ui/panel.js:107
+msgid "Quit"
+msgstr ""
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:432
+msgid "Activities"
+msgstr "Архӕйдтытӕ"
+
+#: js/ui/panel.js:711
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Системӕ"
+
+#: js/ui/panel.js:824
+msgid "Top Bar"
+msgstr ""
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr ""
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr ""
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr ""
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr ""
+
+#: js/ui/screenShield.js:203
+msgid "GNOME needs to lock the screen"
+msgstr ""
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:244 js/ui/screenShield.js:602
+msgid "Unable to lock"
+msgstr ""
+
+#: js/ui/screenShield.js:245 js/ui/screenShield.js:603
+msgid "Lock was blocked by an application"
+msgstr ""
+
+#: js/ui/search.js:702
+msgid "Searching…"
+msgstr ""
+
+#: js/ui/search.js:704
+msgid "No results."
+msgstr ""
+
+#: js/ui/search.js:830
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr ""
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr ""
+
+#: js/ui/shellEntry.js:73
+msgid "Show Text"
+msgstr ""
+
+#: js/ui/shellEntry.js:75
+msgid "Hide Text"
+msgstr ""
+
+#: js/ui/shellEntry.js:162
+msgid "Caps lock is on."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr ""
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:465
+#, javascript-format
+msgid "Unable to start %s"
+msgstr ""
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:467
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr ""
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr ""
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr ""
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr ""
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr ""
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr ""
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr ""
+
+#: js/ui/status/accessibility.js:136
+msgid "High Contrast"
+msgstr ""
+
+#: js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr ""
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr ""
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:595
+msgid "Bluetooth Settings"
+msgstr ""
+
+#: js/ui/status/bluetooth.js:152
+msgid "Bluetooth Off"
+msgstr ""
+
+#: js/ui/status/bluetooth.js:154
+msgid "Bluetooth On"
+msgstr ""
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:13
+msgid "Single Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:18
+msgid "Double Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:23
+msgid "Drag"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:28
+msgid "Secondary Click"
+msgstr ""
+
+#: js/ui/status/dwellClick.js:37
+msgid "Dwell Click"
+msgstr ""
+
+#: js/ui/status/keyboard.js:826
+msgid "Keyboard"
+msgstr ""
+
+#: js/ui/status/keyboard.js:848
+msgid "Show Keyboard Layout"
+msgstr ""
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:174
+msgid "Location Enabled"
+msgstr ""
+
+#: js/ui/status/location.js:66 js/ui/status/location.js:175
+msgid "Disable"
+msgstr ""
+
+#: js/ui/status/location.js:67
+msgid "Privacy Settings"
+msgstr ""
+
+#: js/ui/status/location.js:173
+msgid "Location In Use"
+msgstr ""
+
+#: js/ui/status/location.js:177
+msgid "Location Disabled"
+msgstr ""
+
+#: js/ui/status/location.js:178
+msgid "Enable"
+msgstr ""
+
+#: js/ui/status/location.js:355
+msgid "Allow location access"
+msgstr ""
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:357
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr ""
+
+#: js/ui/status/location.js:367
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+
+#: js/ui/status/network.js:70
+msgid "<unknown>"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:424 js/ui/status/network.js:1320
+#, javascript-format
+msgid "%s Off"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:427
+#, javascript-format
+msgid "%s Connected"
+msgstr ""
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:432
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:435
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:442 js/ui/status/network.js:1312
+#, javascript-format
+msgid "%s Connecting"
+msgstr ""
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:445
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr ""
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:453
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:457
+#, javascript-format
+msgid "%s Unavailable"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:460
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr ""
+
+#: js/ui/status/network.js:472
+msgid "Wired Settings"
+msgstr ""
+
+#: js/ui/status/network.js:515
+msgid "Mobile Broadband Settings"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:562 js/ui/status/network.js:1317
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr ""
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:566
+#, javascript-format
+msgid "%s Disabled"
+msgstr ""
+
+#: js/ui/status/network.js:607
+msgid "Connect to Internet"
+msgstr ""
+
+#: js/ui/status/network.js:811
+msgid "Airplane Mode is On"
+msgstr ""
+
+#: js/ui/status/network.js:812
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr ""
+
+#: js/ui/status/network.js:813
+msgid "Turn Off Airplane Mode"
+msgstr ""
+
+#: js/ui/status/network.js:822
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi хицӕн у"
+
+#: js/ui/status/network.js:823
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr ""
+
+#: js/ui/status/network.js:824
+msgid "Turn On Wi-Fi"
+msgstr ""
+
+#: js/ui/status/network.js:849
+msgid "Wi-Fi Networks"
+msgstr ""
+
+#: js/ui/status/network.js:851
+msgid "Select a network"
+msgstr ""
+
+#: js/ui/status/network.js:883
+msgid "No Networks"
+msgstr ""
+
+#: js/ui/status/network.js:904 js/ui/status/rfkill.js:106
+msgid "Use hardware switch to turn off"
+msgstr ""
+
+#: js/ui/status/network.js:1181
+msgid "Select Network"
+msgstr ""
+
+#: js/ui/status/network.js:1187
+msgid "Wi-Fi Settings"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1308
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1323
+#, javascript-format
+msgid "%s Not Connected"
+msgstr ""
+
+#: js/ui/status/network.js:1420
+msgid "connecting…"
+msgstr "иу кӕны..."
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1423
+msgid "authentication required"
+msgstr ""
+
+#: js/ui/status/network.js:1425
+msgid "connection failed"
+msgstr ""
+
+#: js/ui/status/network.js:1476
+msgid "VPN Settings"
+msgstr ""
+
+#: js/ui/status/network.js:1493
+msgid "VPN"
+msgstr ""
+
+#: js/ui/status/network.js:1503
+msgid "VPN Off"
+msgstr ""
+
+#: js/ui/status/network.js:1564 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr ""
+
+#: js/ui/status/network.js:1593
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/network.js:1597
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/network.js:1601
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] ""
+msgstr[1] ""
+
+#: js/ui/status/network.js:1735
+msgid "Connection failed"
+msgstr ""
+
+#: js/ui/status/network.js:1736
+msgid "Activation of network connection failed"
+msgstr ""
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr ""
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr ""
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr ""
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr ""
+
+#: js/ui/status/power.js:47
+msgid "Power Settings"
+msgstr ""
+
+#: js/ui/status/power.js:63
+msgid "Fully Charged"
+msgstr ""
+
+#: js/ui/status/power.js:69
+msgid "Not Charging"
+msgstr ""
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:72 js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr ""
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr ""
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr ""
+
+#: js/ui/status/power.js:139 js/ui/status/power.js:141
+#, javascript-format
+msgid "%d %%"
+msgstr ""
+
+#: js/ui/status/remoteAccess.js:43
+msgid "Screen is Being Shared"
+msgstr ""
+
+#: js/ui/status/remoteAccess.js:45
+msgid "Turn off"
+msgstr ""
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr ""
+
+#: js/ui/status/system.js:102
+msgid "Lock"
+msgstr ""
+
+#: js/ui/status/system.js:115
+msgid "Power Off / Log Out"
+msgstr ""
+
+#: js/ui/status/system.js:118
+msgid "Log Out"
+msgstr ""
+
+#: js/ui/status/system.js:130
+msgid "Switch User…"
+msgstr ""
+
+#: js/ui/status/system.js:144
+msgid "Suspend"
+msgstr ""
+
+#: js/ui/status/system.js:156
+msgid "Power Off…"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:325
+msgid "Unknown Thunderbolt device"
+msgstr "Ӕнӕзонгӕ Thunderbolt гарз"
+
+#: js/ui/status/thunderbolt.js:326
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:329
+msgid "Unauthorized Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:330
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:336
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:337
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:151
+msgid "Volume changed"
+msgstr ""
+
+#: js/ui/status/volume.js:222
+msgid "Volume"
+msgstr ""
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "Кӕсӕн"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr ""
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "Ӕрмӕст ӕттагон"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr ""
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:371
+msgid "%A %B %-d"
+msgstr ""
+
+#: js/ui/unlockDialog.js:377
+msgid "Swipe up to unlock"
+msgstr "Уӕлӕмӕ схӕц бакӕнынӕн"
+
+#: js/ui/unlockDialog.js:378
+msgid "Click or press a key to unlock"
+msgstr "Ныкъкъӕрц кӕн мыстӕй кӕнӕ ныххӕц ӕгънӕгыл бакӕнынӕн"
+
+#: js/ui/unlockDialog.js:550
+msgid "Unlock Window"
+msgstr ""
+
+#: js/ui/unlockDialog.js:559
+msgid "Log in as another user"
+msgstr ""
+
+#: js/ui/viewSelector.js:181
+msgid "Applications"
+msgstr "Ӕфтуантӕ"
+
+#: js/ui/viewSelector.js:185
+msgid "Search"
+msgstr "Агурын"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” цӕттӕ у"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:55
+msgid "Keep these display settings?"
+msgstr ""
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:64
+msgid "Revert Settings"
+msgstr ""
+
+#: js/ui/windowManager.js:67
+msgid "Keep Changes"
+msgstr ""
+
+#: js/ui/windowManager.js:86
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:546
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Minimize"
+msgstr ""
+
+#: js/ui/windowMenu.js:34
+msgid "Unmaximize"
+msgstr ""
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr ""
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Хӕссын"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr ""
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr ""
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr ""
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr ""
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr ""
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr ""
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr ""
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr ""
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr ""
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr ""
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr ""
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr ""
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Сӕхгӕнын"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr ""
+
+#: src/main.c:458 subprojects/extensions-tool/src/main.c:317
+msgid "Print version"
+msgstr "Мыхуырӕн"
+
+#: src/main.c:464
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: src/main.c:470
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+
+#: src/main.c:476
+msgid "List possible modes"
+msgstr ""
+
+#: src/shell-app.c:286
+msgctxt "program"
+msgid "Unknown"
+msgstr "Ӕнӕзонгӕ"
+
+#: src/shell-app.c:537
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr ""
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr ""
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr ""
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:182
+#: subprojects/extensions-app/data/ui/extensions-window.ui:61
+msgid "Extensions"
+msgstr "Уӕлӕмхасӕнтӕ"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:183
+msgid "Manage your GNOME Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr ""
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:144
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Схафон “%s”?"
+
+#: subprojects/extensions-app/js/main.js:145
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:149
+msgid "Remove"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:181
+msgid "translator-credits"
+msgstr ""
+
+#: subprojects/extensions-app/js/main.js:313
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] ""
+msgstr[1] ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:100
+#: subprojects/extensions-tool/src/command-create.c:301
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:123
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Фӕлтӕр"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:151
+msgid "Author"
+msgstr "Автор"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:175
+msgid "Website"
+msgstr "Вебсайт"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:192
+msgid "Remove…"
+msgstr "Схафӕн..."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:8
+msgid "Help"
+msgstr "Ӕххуыс"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:12
+msgid "About Extensions"
+msgstr "Уӕлӕмхасӕнты тыххӕй"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:35
+msgid "Warning"
+msgstr "Хъусдаринаг"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:46
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:135
+msgid "Manually Installed"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:159
+msgid "Built-In"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:200
+msgid "No Installed Extensions"
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:236
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:289
+msgid "Log Out…"
+msgstr "Рахизӕн..."
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:202
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:275
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:281
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Ном"
+
+#: subprojects/extensions-tool/src/command-create.c:295
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:315
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:342
+msgid "Choose one of the available templates:\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:356
+msgid "Template"
+msgstr "Хуызӕг"
+
+#: subprojects/extensions-tool/src/command-create.c:411
+msgid "The unique identifier of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:414
+msgid "NAME"
+msgstr "НОМ"
+
+#: subprojects/extensions-tool/src/command-create.c:415
+msgid "The user-visible name of the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:417
+msgid "DESCRIPTION"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:419
+msgid "A short description of what the extension does"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:422
+msgid "TEMPLATE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:423
+msgid "The template to use for the new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:429
+msgid "Enter extension information interactively"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:437
+msgid "Create a new extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:455
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-create.c:480
+msgid "UUID, name and description are required"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+msgid "Failed to connect to GNOME Shell\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:97
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID лӕвӕрд не рцыд"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:102
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ФАЙЛ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ДИРЕКТОРИ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ДОМЕН"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-prefs.c:79
+msgid "Opens extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr ""
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Фӕт"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Фыццаг автор"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Уаг"
+
+#: subprojects/extensions-tool/src/main.c:290
+msgid "“version” takes no arguments"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:292
+#: subprojects/extensions-tool/src/main.c:312
+msgid "Usage:"
+msgstr "Пайда кӕнын:"
+
+#: subprojects/extensions-tool/src/main.c:295
+msgid "Print version information and exit."
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:310
+#: subprojects/extensions-tool/src/main.c:313
+msgid "COMMAND"
+msgstr "КОМАНДӔ"
+
+#: subprojects/extensions-tool/src/main.c:313
+msgid "[ARGS…]"
+msgstr "[АРГУМЕНТТӔ…]"
+
+#: subprojects/extensions-tool/src/main.c:315
+msgid "Commands:"
+msgstr "Командӕтӕ:"
+
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Print help"
+msgstr "Ӕххуыс равдисын"
+
+#: subprojects/extensions-tool/src/main.c:318
+msgid "Enable extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Disable extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Reset extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:321
+msgid "Uninstall extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "List extensions"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:323
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Show extension info"
+msgstr "Уӕлӕмхасӕны тыххӕй"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Open extension preferences"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "Create extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:327
+msgid "Package extension"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Install extension bundle"
+msgstr ""
+
+#: subprojects/extensions-tool/src/main.c:330
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Спайда кӕн “%s”-ӕй цӕмӕй лӕмбынӕг ӕххуыс райсай.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Хуымӕтӕг"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Афтид уӕлӕмхасӕн"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Индикатор"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr ""
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1899
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u рафӕрсӕн"
+msgstr[1] "%u рафӕрсӕны"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1909
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u бафыссӕн"
+msgstr[1] "%u бафыссӕны"
+
+#: subprojects/gvc/gvc-mixer-control.c:2766
+msgid "System Sounds"
+msgstr "Системӕйы унӕртӕ"
diff --git a/po/pa.po b/po/pa.po
new file mode 100644
index 0000000..ca7c15a
--- /dev/null
+++ b/po/pa.po
@@ -0,0 +1,4381 @@
+# Punjabi translation for gnome-shell.
+# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# A S Alam <aalam@users.sf.net>, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-20 09:42+0000\n"
+"PO-Revision-Date: 2022-10-16 17:09-0700\n"
+"Last-Translator: A S Alam <aalam@satluj.org>\n"
+"Language-Team: Punjabi <punjabi-users@list.sf.net>\n"
+"Language: pa\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 22.08.1\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "ਲਾਂਚਰ"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 1 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 2 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 3 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 4 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 5 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 6 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 7 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 8 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ 9 ਐਕਟੀਵੇਟ ਕਰੋ"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+#| msgid "Screenshot taken"
+msgid "Screenshots"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+#| msgid "Enter extension information interactively"
+msgid "Take a screenshot interactively"
+msgstr "ਤਾਲਮੇਲ ਰਾਹੀਂ ਸਕਰੀਨਸ਼ਾਟ ਲਵੋ"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+#| msgid "Record a screencast"
+msgid "Take a screenshot"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ ਲਵੋ"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "ਵਿੰਡੋ ਦਾ ਸਕਰੀਨਸ਼ਾਟ ਲਵੋ"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+#| msgid "Record a screencast"
+msgid "Record a screencast interactively"
+msgstr "ਤਾਲਮੇਲ ਰਾਹੀਂ ਸਕਰੀਨਕਾਸਟ ਰਿਕਾਰਡ ਕਰੋ"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "ਸਿਸਟਮ"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "ਨੋਟੀਫਿਕੇਸ਼ਨ ਸੂਚੀ ਵੇਖਾਓ"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "ਸਰਗਰਮ ਸੂਚਨਾ ਉੱਤੇ ਫੋਕਸ"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "ਸੰਖੇਪ ਵੇਖਾਓ"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "ਸਭ ਐਪਲੀਕੇਸ਼ਨ ਵੇਖੋ"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ ਮੇਨੂ ਖੋਲ੍ਹੋ"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "ਗਨੋਮ ਸ਼ੈਲ"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "ਵਿੰਡੋ ਇੰਤਜ਼ਾਮ ਅਤੇ ਐਪਲੀਕੇਸ਼ਨ ਚਲਾਉਣਾ"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Alt-F2 ਤੋਂ ਡਿਵੈਲਪਰਾਂ ਤੇ ਟੈਸਟਰਾਂ ਲਈ ਫਾਇਦੇਮੰਦ ਅੰਦਰੂਨੀ ਟੂਲ ਚਾਲੂ ਕਰਦਾ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 ਡਾਈਲਾਗ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਅੰਦਰੂਨੀ ਡੀਬੱਗਿਗ ਤੇ ਮਾਨੀਟਰਿੰਗ ਟੂਲ ਵਰਤੋਂ ਕਰਨ ਲਈ"
+" ਸਹਾਇਕ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "ਚਾਲੂ ਕਰਨ ਲਈ ਇਕਟੈਨਸ਼ਨ ਦੀ UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"ਗਨੋਮ ਸ਼ੈਲ ਇਕਸਟੈਨਸ਼ਨ ਲਈ ਇੱਕ UUID ਵਿਸ਼ੇਸ਼ਤਾ ਹੈ; ਇਹ ਕੁੰਜੀ ਇਕਸਟੈਨਸ਼ਨ ਦਰਸਾਉਂਦੀ ਹੈ,"
+" ਜੋ ਲੋਡ ਹੋਣੀ "
+"ਚਾਹੀਦੀ ਹੈ। ਕੋਈ ਵੀ ਇਕਸਟੈਸ਼ਨ, ਜੋ ਲੋਡ ਹੋਣਾ ਚਾਹੁੰਦੀ ਹੈ, ਨੂੰ ਇਹ ਸੂਚੀ ਵਿੱਚ ਹੋਣਾ"
+" ਚਾਹੀਦਾ ਹੈ। ਤੁਸੀਂ ਵੀ "
+"ਇਹ ਸੂਚੀ ਨੂੰ org.gnome.shell ਉੱਤੇ EnableExtension ਅਤੇ DisableExtension D-Bus"
+" ਢੰਗ ਨਾਲ "
+"ਬਦਲ ਸਕਦੇ ਹੋ।"
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "ਧੱਕੇ ਨਾਲ ਅਸਮਰੱਥ ਕਰਨ ਲਈ ਇਕਟੈਨਸ਼ਨਾਂ ਦੀ UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"ਗਨੋਮ ਸ਼ੈਲ ਇਕਸਟੈਨਸ਼ਨ ਲਈ ਇੱਕ UUID ਵਿਸ਼ੇਸ਼ਤਾ ਹੈ; ਇਹ ਕੁੰਜੀ ਇਕਸਟੈਨਸ਼ਨ ਦਰਸਾਉਂਦੀ ਹੈ,"
+" ਜੋ ਸਮਰੱਥ ਹੋਣੀ "
+"ਚਾਹੀਦੀ ਹੈ, ਭਾਵੇੇਂ ਕਿ ਉਹ ਮੌਜੂਦਾ ਮੋਡ ਦੇ ਹਿੱਸੇ ਵਜੋਂ ਲੋਡ ਹੁੰਦੀ ਹੈ। ਤੁਸੀਂ ਇਹ ਸੂਚੀ"
+" ਨੂੰ org.gnome.shell "
+"ਉੱਤੇ EnableExtension ਅਤੇ DisableExtension D-Bus ਢੰਗ ਨਾਲ ਬਦਲ ਸਕਦੇ ਹੋ। ਇਹ ਕੁੰਜੀ"
+" ਦੀ "
+"“enabled-extensions” ਤੋਂ ਵੱਧ ਤਰਜੀਹ ਹੁੰਦੀ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "ਵਰਤੋਂਕਾਰ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਸਮਰੱਥ ਕਰੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"“enabled-extension” ਸੈਟਿੰਗ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕੀਤੇ ਬਿਨਾਂ ਵਰਤੋਂਕਾਰ ਵਲੋਂ ਸਮਰੱਥ ਕੀਤੀਆਂ"
+" ਸਾਰੀਆਂ "
+"ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਸਮਰੱਥ ਕਰੋ।"
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਵਰਜ਼ਨ ਅਨੁਕੂਲਤਾ ਦੀ ਵੈਧਤਾ ਨੂੰ ਬੰਦ ਕਰੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"ਗਨੋਮ ਸ਼ੈਲ ਕੇਵਲ ਉਹਨਾਂ ਹੀ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਲੋਡ ਕਰਦੀ ਹੈ, ਜੋ ਕਿ ਮੌਜੂਦਾ ਚਾਲੂ ਵਰਜ਼ਨ ਨਾਲ"
+" ਸਹਾਇਤਾ ਦਾ "
+"ਦਾਅਵਾ ਕਰਦੀਆਂ ਹਨ। ਇਹ ਚੋਣ ਕਰਨ ਨਾਲ ਇਹ ਜਾਂਚ ਬੰਦ ਹੋ ਜਾਵੇਗੀ ਅਤੇ ਸਭ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ"
+" ਲੋਡ ਕਰਨ ਦੀ "
+"ਕੋਸ਼ਿਸ਼ ਕਰੇਗਾ, ਭਾਵੇਂ ਕਿ ਉਹ ਵਰਜ਼ਨ ਲਈ ਸਹਾਇਤਾ ਬਾਰੇ ਕੁਝ ਵੀ ਦਾਆਵਾ ਕਰਨ।"
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "ਪਸੰਦੀਦਾ ਐਪਲੀਕੇਸ਼ਨ ਲਈ ਡੈਸਕਟਾਪ ਫਾਇਲ ID ਦੀ ਲਿਸਟ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"ਇਹਨਾਂ ਐਂਡਟਟੀਫਾਇਰ ਨਾਲ ਸਬੰਧਿਤ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਪਸੰਦੀਦਾ ਖੇਤਰ 'ਚ ਵੇਖਾਇਆ ਜਾਵੇਗਾ।"
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "ਕਮਾਂਡ (Alt-F2) ਡਾਈਲਾਗ ਲਈ ਅਤੀਤ"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "ਗਲਾਸ ਡਾਈਲਾਗ ਖੋਜ ਲਈ ਅਤੀਤ"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "ਵਰਤੋਂਕਾਰ ਮੇਨੂ ਵਿੱਚ “ਲਾਗ ਆਉਟ “ ਮੇਨੂ ਆਈਟਮ ਹਮੇਸ਼ਾ ਵੇਖਾਉ।"
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"ਇਹ ਕੁੰਜੀ ਇੱਕਲੇ ਵਰਤੋਂਕਾਰ, ਸਿੰਗਲ-ਸ਼ੈਸ਼ਨ ਸਥਿਤੀ ਵਿੱਚ “ਲਾਗ ਆਉਟ“ ਮੇਨੂ-ਆਈਟਮ ਨੂੰ"
+" ਆਪਣੇ-ਆਪ ਓਹਲੇ ਕਰਨ ਨੂੰ "
+"ਅਣਡਿੱਠਾ ਕਰਦੀ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "ਕੀ ਇੰਕ੍ਰਿਪਟ ਜਾਂ ਰਿਮੋਟ ਫਾਇਲ-ਸਿਸਟਮਾਂ ਲਈ ਪਾਸਵਰਡ ਯਾਦ ਰੱਖਣਾ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"ਸੈੱਲ ਨੂੰ ਇਹ ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੋਵੇਗਾ, ਜਦੋਂ ਇੰਕ੍ਰਿਪਟ ਕੀਤਾ ਡਿਵਾਈਸ ਜਾਂ ਰਿਮੋਟ ਫਾਇਲ"
+" ਸਿਸਟਮ ਮਾਊਂਟ ਕੀਤਾ "
+"ਜਾਂਦਾ ਹੈ। ਜੇ ਇਹ ਪਾਸਵਰਡ ਭਵਿੱਖ ਵਿੱਚ ਵਰਤਣ ਸੰਭਾਲਿਆ ਜਾ ਸਕਦਾ ਹੋਇਆ ਤਾਂ “ਪਾਸਵਰਡ ਯਾਦ"
+" ਰੱਖੋ“ ਬਾਕਸ "
+"ਦਿੱਤਾ ਜਾਵੇਗਾ। ਇਹ ਕੁੰਜੀ ਚੋਣ-ਬਾਕਸ ਦੀ ਸਥਿਤੀ ਨੂੰ ਡਿਫਾਲਟ ਲਈ ਸੈੱਟ ਕਰਦੀ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "ਆਖਰੀ ਵਾਰ ਚੁਣਿਆ ਗ਼ੈਰ-ਮੂਲ ਪਾਵਰ ਪਰੋਫਾਇਲ"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"ਕਈ ਸਿਸਟਮ ਦੋ ਤੋਂ ਵੱਧ ਪਾਵਰ ਪਰੋਫਾਇਲਾਂ ਲਈ ਸਹਾਇਕ ਹੁੰਦੇ ਹਨ। ਦੋ ਪਰੋਫਾਇਲਾਂ ਵਿਚਾਲੇ"
+" ਬਦਲਣ ਲਈ ਸਹਾਇਕ ਹੋਣ ਵਾਸਤੇ ਇਹ ਕੁੰਜੀ ਆਖਰੀ ਵਾਰ ਚੁਣੇ ਗ਼ੈਰ-ਮੂਲ ਪਰੋਫਾਇਲਾਂ ਦਾ ਰਿਕਾਰਡ"
+" ਰੱਖਦੀ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "ਇਸ ਲਈ ”ਗਨੋਮ ਵਲੋਂ ਜੀ ਆਇਆਂ ਨੂੰ” ਡਾਈਲਾਗ ਦਾ ਆਖਰੀ ਵਰਜ਼ਨ ਦਿਖਾਇਆ ਗਿਆ ਸੀ"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"ਇਹ ਕੁੰਜੀ ਪਤਾ ਲਗਾਉਂਦੀ ਹੈ ਕਿ ”ਗਨੋਮ ਵਲੋਂ ਜੀ ਆਇਆਂ ਨੂੰ” ਡਾਈਲਾਗ ਦਾ ਕਿਹੜਾ ਵਰਜ਼ਨ ਪਿਛਲੀ"
+" ਵਾਰ "
+"ਦਿਖਾਇਆ ਸੀ। ਖਾਲੀ ਸਤਰ ਪੁਰਾਣਾ ਵਰਜ਼ਨ ਦਿਖਾਉਂਦੀ ਹੈ ਅਤੇ ਬਹੁਤ ਸਾਰੇ ਨੰਬਰ ਉਹ ਵਰਜ਼ਨ"
+" ਦਿਖਾਉਗੇ, ਜੋ ਕਿ "
+"ਹਾਲੇ ਮੌਜੂਦ ਨਹੀਂ ਹਨ। ਇਹ ਵੱਡੀ ਗਿਣਤੀ ਨੂੰ ਡਾਈਲਾਗ ਅਸਮਰੱਥ ਕਰਨ ਲਈ ਵਰਤਿਆ ਜਾ ਸਕਦਾ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "ਐਪ ਚੋਣਕਾਰ ਦਾ ਖਾਕਾ"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"ਐਪ ਚੋਣਕਾਰ ਦਾ ਖਾਕਾ। ਲੜੀ ਵਿੱਚ ਹਰ ਇੰਦਰਾਜ਼ ਇੱਕ ਸਫ਼ਾ ਹੈ। ਸਫ਼ਿਆਂ ਨੂੰ ਉਸ ਲੜੀ ਮੁਤਾਬਕ"
+" ਸੰਭਾਲਿਆ ਜਾਂਦਾ "
+"ਹੈ, ਜਿਵੇਂ ਉਹ ਗਨੋਮ ਸ਼ੈੱਲ ਵਿੱਚ ਦਿਖਾਈ ਦਿੰਦੇ ਹਨ। ਹਰ ਸਫ਼ੇ ਵਿੱਚ ਇੱਕ “application id”"
+" → 'data' "
+"ਜੋੜਾ ਹੈ। ਇਸ ਵੇਲੇ ਮੁੱਲਾਂ ਨੂੰ ਸਫ਼ੇ ਵਿੱਚ ਐਪਲੀਕੇਸ਼ਨ ਦੀ 'data': • “position”: ਵਜੋਂ"
+" ਸੰਭਾਲਿਆ ਜਾਂਦਾ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ ਮੇਨੂ ਖੋਲ੍ਹਣ ਲਈ ਕੀਬਾਈਡਿੰਗ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "ਐਪਲੀਕੇਸ਼ਨ ਮੇਨੂ ਖੋਲ੍ਹਣ ਲਈ ਕੀਬਾਈਡਿੰਗ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "ਸੰਖੇਪ ਸਥਿਤੀਆਂ ਵਿੱਚ ਤਬਦੀਲ ਕਰਨ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "ਸ਼ੈਸ਼ਨ, ਵਿੰਡੋ ਚੋਣਕਾਰ ਅਤੇ ਐਪ ਗਰਿੱਡ ਵਿੱਚ ਤਬਦੀਲ ਕਰਨ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "ਐਪ ਗਰਿੱਡ, ਵਿੰਡੋ ਚੋਣਕਾਰ ਅਤੇ ਸ਼ੈਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕਰਨ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "“ਐਪਲੀਕੇਸ਼ਨ ਵੇਖੋ“ ਮੇਨੂ ਖੋਲ੍ਹਣ ਲਈ ਕੀਬਾਈਡਿੰਗ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "ਸਰਗਰਮੀ ਝਲਕ ਵਿੱਚ “ਐਪਲੀਕੇਸ਼ਨ ਵੇਖੋ“ ਮੇਨੂ ਖੋਲ੍ਹਣ ਲਈ ਕੀਬਾਈਡਿੰਗ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "ਸੰਖੇਪ ਖੋਲ੍ਹਣ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "ਸਰਗਰਮੀ ਸੰਖੇਪ ਖੋਲ੍ਹਣ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "ਨੋਟੀਫਿਕੇਸ਼ਨ ਸੂਚੀ ਦੀ ਦਿੱਖਣ ਨੂੰ ਬਦਲਣ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "ਨੋਟੀਫਿਕੇਸ਼ਨ ਸੂਚੀ ਦੀ ਦਿੱਖਣ ਨੂੰ ਬਦਲਣ ਲਈ ਕੀਬਾਈਡਿੰਗ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "ਸਰਗਰਮ ਸੂਚਨਾ ਉੱਤੇ ਫੋਕਸ ਲਈ ਕੀਬਾਈਡਿੰਗ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "ਸਰਗਰਮ ਸੂਚਨਾ ਉੱਤੇ ਫੋਕਸ ਲਈ ਕੀਬਾਈਡਿੰਗ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 1 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 2 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 3 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 4 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 5 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 6 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 7 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 8 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ 9 ਲਈ ਬਦਲੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "ਸਵਿੱਚਰ ਨੂੰ ਮੌਜੂਦਾ ਵਰਕਸਪੇਸ ਤੱਕ ਸੀਮਿਤ ਕਰੋ।"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"ਜੇ ਠੀਕ ਹੈ ਤਾਂ ਕੇਵਲ ਐਪਲੀਕੇਸ਼ਨ ਜਿਹਨਾਂ ਲਈ ਵਿੰਡੋ ਮੌਜੂਦਾ ਵਰਕਸਪੇਸ ਵਿੱਚ ਹਨ ਨੂੰ ਹੀ"
+" ਸਵਿੱਚਰ ਵਿੱਚ ਵਿਖਾਇਆ "
+"ਜਾਂਦਾ ਹੈ, ਨਹੀਂ ਤਾਂ ਸਭ ਐਪਲੀਕੇਸ਼ਨਾਂ ਸ਼ਾਮਿਲ ਹੁੰਦੀਆਂ ਹਨ।"
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "ਐਪਲੀਕੇਸ਼ਨ ਆਈਕਾਨ ਮੋਡ।"
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"ਸੰਰਚਨਾ ਕਰੋ ਕਿ ਸਵਿੱਚਰ ਵਿੱਚ ਵਿੰਡੋਜ਼ ਕਿਵੇਂ ਵੇਖਾਈਆਂ ਜਾਣ। ਸੰਭਵ ਚੋਣਾਂ ਹਨ ਕੇਵਲ ਥੰਮਨੇਲ"
+" (“thumbnail-"
+"only“) (ਵਿੰਡੋ ਦਾ ਥੰਮਨੇਲ ਵੇਖਾਇਆ ਜਾਵੇਗਾ), ਕੇਵਲ ਐਪਸ ਆਈਕਾਨ (“app-icon-only“)"
+" (ਕੇਵਲ "
+"ਐਪਲੀਕੇਸ਼ਨ ਆਈਕਾਨ ਵੇਖਾਇਆ ਜਾਵੇਗਾ) ਜਾਂ ਦੋਵੇਂ (“both“)"
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"ਜੇ ਠੀਕ ਹੈ ਤਾਂ ਕੇਵਲ ਮੌਜੂਦਾ ਵਰਕਸਪੇਸ ਤੋਂ ਵਿੰਡੋ ਨੂੰ ਹੀ ਸਵਿੱਚਰ ਵਿੱਚ ਵਿਖਾਇਆ ਜਾਂਦਾ"
+" ਹੈ, ਨਹੀਂ ਤਾਂ ਸਭ ਵਿੰਡੋ "
+"ਸ਼ਾਮਿਲ ਹੁੰਦੀਆਂ ਹਨ।"
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "ਟਿਕਾਣੇ"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "ਸੰਸਾਰ ਘੜੀ ਵਿੱਚ ਵੇਖਾਉਣ ਲਈ ਟਿਕਾਣੇ"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "ਆਪਣੇ-ਆਪ ਟਿਕਾਣਾ ਲਵੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "ਕੀ ਮੌਜੂਦਾ ਟਿਕਾਣਾ ਪ੍ਰਾਪਤ ਕਰਨਾ ਹੈ ਜਾਂ ਨਹੀਂ"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "ਟਿਕਾਣਾ"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "ਭਵਿੱਖਬਾਣੀ ਵੇਖਾਏ ਜਾਣ ਵਾਲਾ ਟਿਕਾਣਾ"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "ਮੂਲ ਵਿੰਡੋ ਵਿੱਚ ਮਾਡਲ ਡਾਈਲਾਗ ਜੋੜੋ"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"ਇਹ ਕੁੰਜੀ ਗਨੋਮ ਸ਼ੈੱਲ ਚੱਲਣ ਦੇ ਦੌਰਾਨ org.gnome.mutter ਕੁੰਜੀ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਦੀ ਹੈ।"
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "ਕੋਨਾ ਟਿੱਲ ਨੂੰ ਚਾਲੂ ਕਰੋ, ਜਦੋਂ ਵਿੰਡੋ ਸਕਰੀਨ ਕੋਨਿਆਂ ਤੋਂ ਹਿਲਦੀ ਹੈ"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "ਵਰਕਸਪੇਸ ਚੱਲਦੇ ਰੂਪ ਵਿੱਚ ਰੱਖੇ ਜਾਂਦੇ ਹਨ"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "ਪ੍ਰਾਈਮਰੀ ਮਾਨੀਟਰ ਉੱਤੇ ਕੇਵਲ ਵਰਕਸਪੇਸ"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "ਪੁਆਇੰਟਰ ਦੇ ਹਿਲਣ ਤੋਂ ਰੁਕਣ ਤੱਕ ਮਾਊਸ ਮੋਡ ਵਿੱਚ ਫੋਕਸ ਬਦਲਾਅ ਵਿੱਚ ਦੇਰੀ"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "ਨੈੱਟਵਰਕ ਲਾਗਇਨ"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "ਕੁਝ ਗਲਤ ਵਾਪਰਿਆ"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"ਸਾਨੂੰ ਬਹੁਤ ਅਫ਼ਸੋਸ ਹੈ ਪਰ ਸਮੱਸਿਆ ਆਈ ਹੈ: ਇਸ ਇਕਸਟੈਨਸ਼ਨ ਲਈ ਸੈਟਿੰਗਾਂ ਨੂੰ ਦਿਖਾਇਆ ਨਹੀਂ"
+" ਜਾ ਸਕਦਾ ਹੈ। "
+"ਅਸੀਂ ਤੁਹਾਨੂੰ ਇਕਸਟੈਨਸ਼ਨ ਲੇਖਕਾਂ ਨਾਲ ਇਸ ਮਸਲੇ ਬਾਰੇ ਰਿਪੋਰਟ ਦੇਣ ਦੀ ਸਿਫਾਰਸ਼ ਕਰਦੇ ਹਾਂ।"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "ਤਕਨੀਕੀ ਵੇਰਵੇ"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "ਮੁੱਖ ਸਫ਼ਾ"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "ਇਕਟੈਨਸ਼ਨ ਦੇ ਮੁੱਖ ਸਫ਼ੇ ਨੂੰ ਵੇਖੋ"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "ਰੱਦ ਕਰੋ"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "ਪਾਸਵਰਡ"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "ਸ਼ੈਸ਼ਨ ਚੁਣੋ"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "ਲਿਸਟ ਵਿੱਚ ਨਹੀਂ ਹੈ?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ਜਿਵੇਂ ਕਿ ਵਰਤੋਂਕਾਰ ਜਾਂ %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "ਵਰਤੋਂਂਕਾਰ ਨਾਂ"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "ਲਾਗਇਨ ਵਿੰਡੋ"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "ਪਰਮਾਣਕਿਤਾ ਗਲਤੀ"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(ਜਾਂ ਰੀਡਰ ਉੱਤੇ ਉਂਗਲ ਘਸਾਓ)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(ਜਾਂ ਰੀਡਰ ਉੱਤੇ ਉਂਗਲ ਰੱਖੋ)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "ਬੰਦ ਕਰੋ"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "ਬੰਦ ਕਰੋ;ਪਾਵਰ ਆਫ਼;ਮੁੜ-ਚਾਲੂ ਕਰੋ;ਰੋਕੋ;ਅਟਕਾਓ"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "ਮੁੜ-ਚਾਲੂ ਕਰੋ"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "ਮੁੜ-ਚਾਲੂ;ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "ਸਕਰੀਨ ਲਾਕ ਕਰੋ"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "ਸਕਰੀਨ ਲਾਕ"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "ਲਾਗ ਆਉਟ"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "ਲਾਗਆਉਟ;ਸਾਈਨ ਆਫ਼;ਲਾਗ-ਆਉਟ"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "ਸਸਪੈਂਡ ਕਰੋ"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "ਸਸਪੈਂਡ;ਸਲੀਪ"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "ਵਰਤੋਂਂਕਾਰ ਬਦਲੋ"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "ਵਰਤੋਂਕਾਰ ਬਦਲੋ"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "ਸਥਿਤੀ ਲਾਕ ਕਰੋ;ਸਥਿਤੀ ਅਣ-ਲਾਕ ਕਰੋ;ਸਕਰੀਨ;ਘੁੰਮਾਉਣਾ;ਘੁੰਮਾਓ"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+#| msgid "Record a screencast"
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ ਲਵੋ"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ;ਸਕਰੀਨਕਾਸਟ;ਸਨਿਪ;ਕੈਪਚਰ;ਲਵੋ;ਰਿਕਾਰਡ;ਖਿੱਚੋ"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "ਸਕਰੀਨ ਘੁੰਮਾਉਣਾ ਅਣ-ਲਾਕ ਕਰੋ"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "ਸਕਰੀਨ ਘੁੰਮਾਉਣਾ ਲਾਕ ਹੈ"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "ਕਮਾਂਡ ਨਹੀਂ ਲੱਭੀ"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "ਕਮਾਂਡ ਪਾਰਸ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” ਚਲਾਉਣ ਲਈ ਫੇਲ੍ਹ:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "ਹੁਣੇ ਹੀ"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
+msgstr[1] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d ਘੰਟਾ ਪਹਿਲਾਂ"
+msgstr[1] "%d ਘੰਟੇ ਪਹਿਲਾਂ"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "ਕੱਲ੍ਹ"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d ਦਿਨ ਪਹਿਲਾਂ"
+msgstr[1] "%d ਦਿਨ ਪਹਿਲਾਂ"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d ਹਫ਼ਤਾ ਪਹਿਲਾਂ"
+msgstr[1] "%d ਹਫ਼ਤੇ ਪਹਿਲਾਂ"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d ਮਹੀਨਾ ਪਹਿਲਾਂ"
+msgstr[1] "%d ਮਹੀਨੇ ਪਹਿਲਾਂ"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d ਸਾਲ ਪਹਿਲਾਂ"
+msgstr[1] "%d ਸਾਲ ਪਹਿਲਾਂ"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "ਕੱਲ੍ਹ, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "ਕੱਲ੍ਹ, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "ਹਾਟਸਪਾਟ ਲਾਗਇਨ"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"ਇਸ ਹਾਟਸਪਾਟ ਨਾਲ ਤੁਹਾਡਾ ਕਨੈਕਸ਼ਨ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹੈ। ਇਸ ਸਫ਼ੇ ਉੱਤੇ ਤੁਹਾਡੇ ਵਲੋਂ ਭਰੇ ਗਏ"
+" ਪਾਸਵਰਡ ਜਾਂ "
+"ਦਿੱਤੀ ਹੋਰ ਜਾਣਾਕਰੀ ਨੂੰ ਨੇੜਲੇ ਲੋਕਾਂ ਵਲੋਂ ਵੇਖਿਆ ਜਾ ਸਕਦਾ ਹੈ।"
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "ਪਹੁੰਚ ਉੱਤ ਪਾਬੰਦੀ"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "ਪਹੁੰਚ ਲਈ ਮਨਜ਼ੂਰੀ"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "ਬੇਨਾਮ ਫੋਲਡਰ"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+#| msgid "%s has been added to your favorites."
+msgid "%s has been pinned to the dash."
+msgstr "%s ਨੂੰ ਡੈਸ਼ ਵਿੱਚ ਟੰਗਿਆ ਗਿਆ ਹੈ।"
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+#| msgid "%s has been removed from your favorites."
+msgid "%s has been unpinned from the dash."
+msgstr "%s ਨੂੰ ਡੈਸ਼ ਤੋਂ ਲਾਹਿਆ ਜਾ ਚੁੱਕਾ ਹੈ।"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "ਵਿੰਡੋਆਂ ਖੋਲ੍ਹੋ"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "ਨਵੀਂ ਵਿੰਡੋ"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "ਵੇਰਵੇ ਵੇਖੋ"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "ਬਾਹਰ"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "ਲਾਹੋ"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "ਡੈਸ਼ ਵਿੱਚ ਟੰਗੋ"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "ਐਂਟੀਗਰੇਟਡ ਗਰਾਫ਼ਿਕਸ ਕਾਰਡ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਚਲਾਓ"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "ਵੱਖਰੇ (ਡਿਸਕ੍ਰਿਟ) ਗਰਾਫ਼ਿਕਸ ਕਾਰਡ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਚਲਾਓ"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "ਆਡੀਓ ਡਿਵਾਈਸ ਨੂੰ ਚੁਣੋ"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "ਸਾਊਂਡ ਸੈਟਿੰਗਾਂ"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "ਹੈੱਡਫ਼ੋਨ"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "ਹੈੱਡਸੈੱਟ"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "ਮਾਈਕਰੋਫੋਨ"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "…ਬੈਕਗਰਾਊਂਡ ਬਦਲੋ"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "ਡਿਸਪਲੇਅ ਸੈਟਿੰਗਾਂ"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "ਸੈਟਿੰਗਾਂ"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ਐ"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "ਸੋ"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "ਮੰ"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "ਬੁੱ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "ਵੀ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "ਸ਼ੁੱ"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ਸ਼"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "ਪਿੱਛੇ ਮਹੀਨੇ"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "ਅਗਲੇ ਮਹੀਨੇ"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "ਹਫ਼ਤਾ %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "ਕੋਈ ਨੋਟੀਫਿਕੇਸ਼ਨ"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "ਤੰਗ ਨਾ ਕਰੋ"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "ਸਾਫ਼ ਕਰੋ"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” ਜਵਾਬ ਨਹੀਂ ਦਿੰਦਾ ਹੈ।"
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"ਤੁਸੀਂ ਇਸ ਲਈ ਕੁਝ ਸਮੇਂ ਵਾਸਤੇ ਉਡੀਕ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਕਾਰਜ ਨੂੰ ਧੱਕੇ ਨਾਲ ਬੰਦ ਕਰ ਸਕਦੇ"
+" ਹੋ।"
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "ਧੱਕੇ ਨਾਲ ਬੰਦ ਕਰੋ"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "ਉਡੀਕੋ"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "ਬਾਹਰੀ ਡਰਾਇਵ ਕਨੈਕਟ ਹੋਈ"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "ਬਾਹਰੀ ਡਰਾਇਵ ਡਿਸ-ਕਨੈਕਟ ਹੈ"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "ਵਾਲੀਅਮ ਨੂੰ ਅਣਲਾਕ ਕਰਨ ਲਈ ਅਸਮਰੱਥ"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "ਇੰਸਟਾਲ ਕੀਤਾ udisks ਵਰਜ਼ਨ PIM ਸੈਟਿੰਗ ਲਈ ਸਹਾਇਕ ਨਹੀਂ ਹੈ"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ਨਾਲ ਖੋਲ੍ਹੋ"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"ਬਦਲੇ ਰੂਪ ਵਿੱਚ ਤੁਸੀਂ ਆਪਣੇ ਰਾਊਂਟਰ ਉੱਤੇ \"WPS\" ਬਟਨ ਨੂੰ ਦਬਾ ਕੇ ਕਨੈਕਟ ਕਰ ਸਕਦੇ ਹੋ।"
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "ਕਨੈਕਟ ਕਰੋ"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "ਸਵਿੱਚ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "ਪ੍ਰਾਈਵੇਟ ਕੁੰਜੀ ਪਾਸਵਰਡ"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "ਪਛਾਣ"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "ਸਰਵਿਸ"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "ਬੇਤਾਰ ਨੈੱਟਵਰਕ “%s” ਵਰਤਣ ਲਈ ਪਾਸਵਰਡ ਜਾਂ ਇੰਕ੍ਰਿਪਸ਼ਨ ਕੁੰਜੀਆਂ ਦੀ ਲੋੜ ਹੈ।"
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "ਤਾਰ 802.1X ਪਰਮਾਣਕਿਤਾ"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "ਨੈੱਟਵਰਕ ਨਾਂ"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL ਪਰਮਾਣਕਿਤਾ"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN ਕੋਡ ਲੋੜੀਦਾ ਹੈ"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "ਮੋਬਾਇਲ ਬਰਾਡਬੈਂਡ ਜੰਤਰ ਲਈ ਪਿੰਨ ਕੋਡ ਚਾਹੀਦਾ ਹੈ"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "ਪਿੰਨ"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s” ਨਾਲ ਕੁਨੈਕਟ ਹੋਣ ਲਈ ਪਾਸਵਰਡ ਦੀ ਲੋੜ ਹੈ। "
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "ਨੈੱਟਵਰਕ ਮੈਨੇਜਰ"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN ਪਾਸਵਰਡ"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "ਪਰਸ਼ਾਸ਼ਕ"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "ਪਰਮਾਣਿਤ"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "ਅਫਸੋਸ, ਉਹ ਕੰਮ ਨਹੀਂ ਕਰਦਾ। ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।"
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s ਨੂੰ ਹੁਣ %s ਵਜੋਂ ਜਾਣਿਆ ਜਾਵੇਗਾ"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "ਵਿੰਡੋ"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "ਐਪਲੀਕੇਸ਼ਨਾਂ ਵੇਖੋ"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "ਡੈਸ਼"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "ਅੱਜ"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "ਭਲਕ"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "ਸਭ ਦਿਨ"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "ਕੋਈ ਘਟਨਾ ਨਹੀਂ"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "...ਸੰਸਾਰ ਘੜੀਆਂ ਜੋੜੋ"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "ਸੰਸਾਰ ਘੜੀਆਂ"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "…ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "ਮੌਸਮ ਦੀ ਜਾਣਕਾਰੀ ਲਈ ਆਨਲਾਈਨ ਜਾਓ"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "ਮੌਸਮ ਦੀ ਜਾਣਕਾਰੀ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "ਮੌਸਮ"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "…ਮੌਸਮੀ ਟਿਕਾਣਾ ਚੁਣੋ"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s ਲਾਗਆਉਟ"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "ਲਾਗਆਉਟ"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s ਨੂੰ %d ਸਕਿੰਟ ਵਿੱਚ ਆਪਣੇ-ਆਪ ਹੀ ਲਾਗ ਆਉਟ ਕੀਤਾ ਜਾਵੇਗਾ।"
+msgstr[1] "%s ਨੂੰ %d ਸਕਿੰਟਾਂ ਵਿੱਚ ਆਪਣੇ-ਆਪ ਹੀ ਲਾਗ ਆਉਟ ਕੀਤਾ ਜਾਵੇਗਾ।"
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "ਤੁਹਾਨੂੰ %d ਸਕਿੰਟ ਵਿੱਚ ਆਪਣੇ-ਆਪ ਹੀ ਲਾਗਆਉਟ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ।"
+msgstr[1] "ਤੁਹਾਨੂੰ %d ਸਕਿੰਟਾਂ ਵਿੱਚ ਆਪਣੇ-ਆਪ ਹੀ ਲਾਗਆਉਟ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ।"
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "ਲਾਗਆਉਟ"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "ਬੰਦ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕਰੋ ਤੇ ਬੰਦ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "ਸਿਸਟਮ ਨੂੰ ਆਪਣੇ-ਆਪ ਹੀ %d ਸਕਿੰਟ ਵਿੱਚ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ।"
+msgstr[1] "ਸਿਸਟਮ ਨੂੰ ਆਪਣੇ-ਆਪ ਹੀ %d ਸਕਿੰਟਾਂ ਵਿੱਚ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ।"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "ਬਾਕੀ ਰਹਿੰਦੇ ਸਾਫਟਵੇਅਰ ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "ਬੰਦ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "ਮੁੜ-ਚਾਲੂ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕਰੋ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "ਸਿਸਟਮ ਨੂੰ ਆਪਣੇ-ਆਪ ਹੀ %d ਸਕਿੰਟ ਵਿੱਚ ਮੁੜ-ਚਾਲੂ ਕੀਤਾ ਜਾਵੇਗਾ।"
+msgstr[1] "ਸਿਸਟਮ ਨੂੰ ਆਪਣੇ-ਆਪ ਹੀ %d ਸਕਿੰਟਾਂ ਵਿੱਚ ਮੁੜ-ਚਾਲੂ ਕੀਤਾ ਜਾਵੇਗਾ।"
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "ਮੁੜ-ਚਾਲੂ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "ਮੁੜ-ਚਾਲੂ ਕਰੋ ਤੇ ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"ਸਿਸਟਮ ਨੂੰ ਆਪਣੇ-ਆਪ ਹੀ %d ਸਕਿੰਟ ਵਿੱਚ ਮੁੜ-ਚਾਲੂ ਅਤੇ ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕੀਤੇ ਜਾਣਗੇ।"
+msgstr[1] ""
+"ਸਿਸਟਮ ਨੂੰ ਆਪਣੇ-ਆਪ ਹੀ %d ਸਕਿੰਟਾਂ ਵਿੱਚ ਮੁੜ-ਚਾਲੂ ਅਤੇ ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕੀਤੇ ਜਾਣਗੇ।"
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "ਮੁੜ-ਚਾਲੂ ਕਰੋ ਅਤੇ ਇੰਸਟਾਲ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "ਇੰਸਟਾਲ ਕਰੋ ਅਤੇ ਬੰਦ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕਰਨ ਦੇ ਬਾਅਦ ਬੰਦ ਕਰੋ"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "ਮੁੜ-ਚਾਲੂ ਕਰੋ ਤੇ ਅੱਪਗਰੇਡ ਇੰਸਟਾਲ ਕਰੋ"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"ਮੁੜ-ਚਾਲੂ ਹੋਣ ਦੇ ਬਾਅਦ %s %s ਨੂੰ ਇੰਸਟਾਲ ਕੀਤਾ ਜਾਵੇਗਾ। ਅੱਪਗਰੇਡ ਇੰਸਟਾਲੇਸ਼ਨ ਨੂੰ ਲੰਮਾ"
+" ਸਮਾਂ ਲੱਗ ਸਕਦਾ "
+"ਹੈ: ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਬੈਕਅੱਪ ਲੈ ਲਿਆ ਹੈ ਅਤੇ ਕੰਪਿਊਟਰ ਦਾ ਪਲੱਗ ਲੱਗਾ ਹੈ।"
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "ਬੈਟਰੀ ਊਰਜਾ ਘੱਟ ਹੈ: ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਪਲੱਗ ਲਗਾਉ ਜੀ।"
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "ਕੁਝ ਐਪਲੀਕੇਸ਼ਨ ਰੁੱਝੀਆਂ ਹਨ ਜਾਂ ਕੁਝ ਨਾ-ਸੰਭਾਲਿਆ ਕੰਮ ਪਿਆ ਹੈ"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "ਹੋਰ ਵਰਤੋਂਕਾਰ ਲਾਗਇਨ ਹਨ"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "ਬੂਟ ਚੋਣਾਂ"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (ਰਿਮੋਟ)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (ਕਨਸੋਲ)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "ਇੰਸਟਾਲ ਕਰੋ"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਇੰਸਟਾਲ ਕਰੋ"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "ਕੀ extensions.gnome.org ਤੋਂ “%s” ਡਾਊਨਲੋਡ ਅਤੇ ਇੰਸਟਾਲ ਕਰਨੀ ਹੈ?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅੱਪਡੇਟ ਉਪਲਬਧ ਹਨ"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕਰਨ ਲਈ ਤਿਆਰ ਹਨ।"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "ਸ਼ਾਰਟਕੱਟਾਂ ਲਈ ਰੁਕਾਵਟ ਪਾਉਣ ਲਈ ਸਹਿਮਤੀ ਦਿਓ"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "%s ਐਪਲੀਕੇਸ਼ਨ ਸ਼ਾਰਟਕੱਟਾਂ ਲਈ ਰੁਕਾਵਟ ਚਾਹੁੰਦੀ ਹੈ"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ ਸ਼ਾਰਟਕੱਟਾਂ ਲਈ ਰੁਕਾਵਟ ਚਾਹੁੰਦੀ ਹੈ"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "ਤੁਸੀਂ ਸ਼ਾਰਟਕੱਟਾਂ ਨੂੰ %s ਦਬਾ ਕੇ ਬਹਾਲ ਕਰ ਸਕਦੇ ਹੋ।"
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "ਇਨਕਾਰ"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "ਮਨਜ਼ੂਰ"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "ਹੌਲੀ ਸਵਿੱਚਾਂ ਚਾਲੂ ਕੀਤੀਆਂ"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "ਹੌਲੀ ਸਵਿੱਚਾਂ ਬੰਦ ਕੀਤੀਆਂ"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"ਤੁਸੀ ਸਿਫਟ (Shift) ਸਵਿੱਚ ਨੂੰ ਸਿਰਫ 8 ਸਕਿੰਟਾਂ ਲਈ ਦਬਾਉਣਾ ਹੈ। ਇਹ ਹੌਲੀ-ਸਵਿੱਚ ਫੀਚਰ"
+" ਦਾ ਸ਼ਾਰਟਕੱਟ "
+"ਹੈ, ਜੋ ਕਿ ਤੁਹਾਡੇ ਕੀ-ਬੋਰਡ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਢੰਗ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕਰੇਗਾ।"
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "ਸਟਿੱਕੀ ਸਵਿੱਚਾਂ ਚਾਲੂ ਕੀਤੀਆਂ"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "ਸਟਿੱਕੀ ਸਵਿੱਚਾਂ ਬੰਦ ਕੀਤੀਆਂ"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"ਤੁਸੀ ਸਿਫਟ (Shift) ਸਵਿੱਚ ਨੂੰ ਸਿਰਫ 5 ਵਾਰ ਦਬਾਉਣਾ ਹੈ। ਇਹ ਸਟਿੱਕੀ-ਸਵਿੱਚ ਫੀਚਰ ਦਾ"
+" ਸ਼ਾਰਟਕੱਟ ਹੈ, "
+"ਜੋ ਕਿ ਤੁਹਾਡੇ ਕੀ-ਬੋਰਡ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਢੰਗ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕਰੇਗਾ।"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"ਤੁਸੀ ਲਗਾਤਾਰ ਦੋ ਕੀ ਇੱਕ ਵਾਰ ਦਬਾ ਸਕਦੇ ਹੋ ਜਾਂ ਸਿਫਟ (Shift) ਸਵਿੱਚ ਨੂੰ 5 ਵਾਰ ਇਹ"
+" ਸਟਿੱਕੀ ਸਵਿੱਚ "
+"ਫੀਚਰ ਨੂੰ ਚਾਲੂ ਕਰ ਸਕਦੇ ਹੋ, ਜੋ ਕਿ ਤੁਹਾਡਾ ਕੀ-ਬੋਰਡ ਤੇ ਕੰਮ ਕਰਨ ਦੇ ਢੰਗ ਨੂੰ ਤਬਦੀਲ"
+" ਕਰਦਾ ਹੈ।"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "ਚਾਲੂ ਛੱਡੋ"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "ਚਾਲੂ ਕਰੋ"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "ਬੰਦ ਕਰੋ"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "ਬੰਦ ਰਹਿਣ ਦਿਓ"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "ਖੇਤਰ ਅਤੇ ਭਾਸ਼ਾ ਸੈਟਿੰਗਾਂ"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "ਕੋਈ ਇਕਸਟੈਨਸ਼ਨ ਇੰਸਟਾਲ ਨਹੀਂ ਹੈ"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ਨੇ ਕੋਈ ਗਲਤੀ ਨਹੀਂ ਦਿੱਤੀ।"
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "ਗਲਤੀਆਂ ਓਹਲੇ ਕਰੋ"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "ਗਲਤੀਆਂ ਵੇਖਾਓ"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "ਚਾਲੂ ਹੈ"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "ਬੰਦ ਹੈ"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "ਗਲਤੀ"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "ਪੁਰਾਣਾ"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "ਡਾਊਨਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "ਸਰੋਤ ਵੇਖੋ"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "ਵੈੱਬ ਪੇਜ਼"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "ਸਿਸਟਮ ਨੂੰ ਗੈਰ-ਸੁਰੱਖਿਅਤ ਢੰਗ ਵਿੱਚ ਰੱਖਿਆ ਗਿਆ ਹੈ"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "ਐਪਲੀਕੇਸ਼ਨਾਂ ਕੋਲ ਹੁਣ ਬਿਨਾਂ ਰੋਕ-ਟੋਕ ਪਹੁੰਚ ਹੈ"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "ਵਾਪਸ"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "ਅਧਿਕਾਰ ਪ੍ਰਾਪਤ ਵਰਤੋਂਕਾਰ ਵਜੋਂ ਲਾਗਇਨ ਕੀਤਾ"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"ਅਧਿਕਾਰ ਪ੍ਰਾਪਤ ਵਰਤੋਂ ਵਜੋਂ ਸ਼ੈਸ਼ਨ ਚਲਾਉਣਾ ਸੁਰੱਖਿਆ ਕਾਰਨਾਂ ਕਾਰਕੇ ਬਚਣਾ ਚਾਹੀਦਾ ਹੈ। ਜੇ"
+" ਸੰਭਵ ਹੋਵੇ ਤਾਂ "
+"ਤੁਹਾਨੂੰ ਸਧਾਰਨ ਵਰਤੋਂਕਾਰ ਵਜੋਂ ਲਾਗਇਨ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ।"
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "ਸਕਰੀਨ ਲਾਕ ਅਸਮਰੱਥ ਹੈ"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "ਸਕਰੀਨ ਲਾਕ ਕਰਨ ਲਈ ਗਨੋਮ ਡਿਸਪਲੇਅ ਮੈਨੇਜਰ ਚਾਹੀਦਾ ਹੈ।"
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "ਸਿਸਟਮ ਜਾਣਕਾਰੀ"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "ਅਣਪਛਾਤਾ ਕਲਾਕਾਰ"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "ਅਣਪਛਾਤਾ ਟਾਈਟਲ"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "ਲੱਭਣ ਲਈ ਲਿਖੋ"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "ਐਪਲੀਕੇਸ਼ਨ"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "ਸੰਖੇਪ"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "...ਨਵਾਂ ਸ਼ਾਰਟਕੱਟ"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "ਨਿਰਧਾਰਿਤ ਕੀਤੀ ਐਪਲੀਕੇਸ਼ਨ"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "ਆਨ-ਸਕਰੀਨ ਮਦਦ ਵੇਖਾਓ"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "ਮਾਨੀਟਰ ਨੂੰ ਬਦਲੋ"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "ਕੀ-ਸਟਰੋਕ ਦਿਓ"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "ਮੁਕੰਮਲ"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "…ਸੋਧੋ"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "ਕੋਈ ਨਹੀਂ"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "ਸੰਰਚਨਾ ਲਈ ਬਟਨ ਨੂੰ ਦੱਬੋ"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "ਬਾਹਰ ਜਾਣ ਲਈ ਕੋਈ ਵੀ Esc ਦੱਬੋ"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "ਬਾਹਰ ਜਾਣ ਲਈ ਕੋਈ ਵੀ ਸਵਿੱਚ ਦੱਬੋ"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "ਸਰਗਰਮੀਆਂ"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "ਸਿਸਟਮ"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "ਉੱਤਲੀ ਪੱਟੀ"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "ਕਮਾਂਡ ਚਲਾਓ"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "ਬੰਦ ਕਰਨ ਲਈ Esc ਦੱਬੋ"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "ਵੇਅਲੈਂਡ ਉੱਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ ਉਪਲਬਧ ਨਹੀਂ"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "...ਮੁੜ-ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "ਗਨੋਮ ਨੂੰ ਸਕਰੀਨ ਲਾਕ ਕਰਨ ਦੀ ਲੋੜ ਹੈ"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "ਅਣਲਾਕ ਕਰਨ ਲਈ ਅਸਮਰੱਥ"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "ਲਾਕ ਨੂੰ ਐਪਲੀਕੇਸ਼ਨ ਵਲੋਂ ਪਾਬੰਦੀ ਲਗਾਈ"
+
+#: js/ui/screenshot.js:1147
+#| msgid "evolution"
+msgid "Selection"
+msgstr "ਚੋਣ"
+
+#: js/ui/screenshot.js:1157
+#| msgid "Clear section"
+msgid "Area Selection"
+msgstr "ਚੋਣ ਖੇਤਰ"
+
+#: js/ui/screenshot.js:1162
+#| msgctxt "search-result"
+#| msgid "Lock Screen"
+msgid "Screen"
+msgstr "ਸਕਰੀਨ"
+
+#: js/ui/screenshot.js:1172
+#| msgid "Screen Reader"
+msgid "Screen Selection"
+msgstr "ਸਕਰੀਨ ਚੋਣ"
+
+#: js/ui/screenshot.js:1177
+#| msgid "Windows"
+msgid "Window"
+msgstr "ਵਿੰਡੋ"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "ਵਿੰਡੋ ਚੋਣ"
+
+#: js/ui/screenshot.js:1225
+#| msgid "Screenshot taken"
+msgid "Screenshot / Screencast"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ / ਸਕਰੀਨਕਾਸਟ"
+
+#: js/ui/screenshot.js:1261
+#| msgid "Show Errors"
+msgid "Show Pointer"
+msgstr "ਪੁਆਇੰਟਰ ਵੇਖਾਓ"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+#| msgid "Screencast from %d %t"
+msgid "Screencasts"
+msgstr "ਸਕਰੀਨਕਾਸਟ"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+#| msgid "Screencast from %d %t"
+msgid "Screencast from %d %t.webm"
+msgstr "%d %t.webm ਤੋਂ ਸਕਰੀਨਕਾਸਟ"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+#| msgid "Screenshot taken"
+msgid "Screenshot"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+#| msgid "Screencast from %d %t"
+msgid "Screencast recorded"
+msgstr "ਸਕਰੀਨਕਾਸਟ ਰਿਕਾਰਡ ਕੀਤਾ"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "ਵੀਡੀਓ ਵੇਖਣ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।"
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+#| msgid "Show Details"
+msgid "Show in Files"
+msgstr "ਫ਼ਾਇਲਾਂ ਵਿੱਚ ਵੇਖੋ"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+#| msgid "Screencast from %d %t"
+msgid "Screenshot from %s"
+msgstr "%s ਤੋਂ ਸਕਰੀਨਸ਼ਾਟ"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+#| msgid "Screenshot taken"
+msgid "Screenshot captured"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ ਲਿਆ ਗਿਆ"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "ਤੁਸੀਂ ਕਲਿੱਪਬੋਰਡ ਤੋਂ ਚਿੱਤਰ ਚੇਪ ਸਕਦੇ ਹੋ।"
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ ਲਿਆ"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "…ਖੋਜ ਜਾਰੀ ਹੈ"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "ਕੋਈ ਨਤੀਜਾ ਨਹੀਂ।"
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d ਹੋਰ"
+msgstr[1] "%d ਹੋਰ"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "ਖੋਜ"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "ਕਾਪੀ ਕਰੋ"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "ਚੇਪੋ"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "ਟੈਕਸਟ ਵੇਖੋ"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "ਟੈਕਸਟ ਓਹਲੇ"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "ਕੈਪਸ ਲਾਕ ਚਾਲੂ ਹੈ।"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "ਵਾਲੀਅਮ ਲੁਕਾਓ"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "ਵਿੰਡੋਜ਼ ਸਿਸਟਮ ਵਾਲੀਅਮ"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "ਕੁੰਜੀ-ਫਾਇਲਾਂ ਵਰਤਦਾ ਹੈ"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"ਵਾਲੀਅਮ, ਜੋ ਕਿ keyfiles ਵਰਤਦਾ ਹੈ, ਨੂੰ ਅਣ-ਲਾਕ ਕਰਨ ਲਈ <i>%s</i> ਸਹੂਲਤ ਨੂੰ ਵਰਤੋਂ।"
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM ਨੰਬਰ"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "ਪਾਸਵਰਡ ਯਾਦ ਰੱਖੋ"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "ਅਣ-ਲਾਕ"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s ਨੂੰ ਖੋਲ੍ਹੋ"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM ਨੰਬਰ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ ਜਾਂ ਖਾਲੀ ਨਹੀਂ ਹੈ।"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਅਸਮਰੱਥ"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s ਐਪਲੀਕੇਸ਼ਨ ਲੱਭੀ ਨਹੀਂ ਜਾ ਸਕੀ"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "ਅਸੈੱਸਬਿਲਟੀ"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "ਵੱਧ ਕਨਟਰਾਸਟ"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "ਜ਼ੂਮ"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "ਸਕਰੀਨ ਰੀਡਰ"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "ਸਕਰੀਨ ਕੀਬੋਰਡ"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "ਦਿੱਖ ਚੇਤਾਵਨੀ"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "ਸਟਿੱਕੀ ਸਵਿੱਚਾਂ"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "ਹੌਲੀ ਸਵਿੱਚਾਂ"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "ਬਾਊਂਸ ਸਵਿੱਚਾਂ"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "ਮਾਊਸ ਸਵਿੱਚਾਂ"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "ਵੱਡੇ ਅੱਖਰ"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "ਆਪੇ-ਘੁੰਮਾਓ"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "ਬਲੂਟੁੱਥ"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "ਚਮਕ"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "ਗੂੜ੍ਹਾ ਢੰਗ"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "ਇੱਕ ਵਾਰ ਕਲਿੱਕ"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "ਡਬਲ ਕਲਿੱਕੱ"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "ਖਿੱਚੋ"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "ਸੈਕੰਡਰੀ ਦੇਰੀ"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "ਡੀਵੈੱਲ ਕਲਿੱਕ"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "ਕੀਬੋਰਡ"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "ਕੀਬੋਰਡ ਲੇਆਉਟ ਵੇਖਾਓ"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "ਟਿਕਾਣੇ ਲਈ ਪਹੁੰਚ ਲਈ ਸਹਿਮਤੀ ਦਿਓ"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "ਐਪ %s ਨੂੰ ਆਪਣੇ ਟਿਕਾਣੇ ਲਈ ਪਹੁੰਚ ਚਾਹੁੰਦੀ ਹੈ"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"ਟਿਕਾਣਾ ਪਹੁੰਚ ਨੂੰ ਪਰਦੇਦਾਰੀ ਸੈਟਿੰਗਾਂ ਵਿੱਚੋਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਬਦਲਿਆ ਜਾ ਸਕਦਾ ਹੈ।"
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<ਅਣਪਛਾਤਾ>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+#| msgid "%s Disconnecting"
+msgid "Disconnect %s"
+msgstr "%s ਡਿਸ-ਕਨੈਕਟ ਕਰੋ"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+#| msgid "Connect to..."
+msgid "Connect to %s"
+msgstr "%s ਨਾਲ ਕਨੈਕਟ ਕਰੋ"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+#| msgid "%s Hotspot Active"
+msgid "%s Hotspot"
+msgstr "%s ਹਾਟਸਪਾਟ"
+
+#: js/ui/status/nightLight.js:20
+#| msgid "Night Light On"
+msgid "Night Light"
+msgstr "ਰਾਤ ਲਈ ਰੋਸ਼ਨੀ"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "ਕਾਰਗੁਜ਼ਾਰੀ"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "ਸੰਤੁਲਤ"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "ਪਾਵਰ ਬੱਚਤ"
+
+#: js/ui/status/powerProfiles.js:68
+#| msgctxt "search-result"
+#| msgid "Power off"
+msgid "Power Profiles"
+msgstr "ਪਾਵਰ ਪਰੋਫਾਇਲ"
+
+#: js/ui/status/remoteAccess.js:74
+#| msgid "Record a screencast"
+msgid "Stop Screencast"
+msgstr "ਸਕਰੀਨਕਾਸਟ ਰੋਕੋ"
+
+#: js/ui/status/remoteAccess.js:144
+#| msgctxt "search-result"
+#| msgid "Lock Screen Rotation"
+msgid "Stop Screen Sharing"
+msgstr "ਸਕਰੀਨ ਸਾਂਝਾ ਕਰਨਾ ਰੋਕੋ"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "ਏਅਰਪਲੇਨ ਮੋਡ"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+#| msgid "Screenshot taken"
+msgid "Take Screenshot"
+msgstr "ਸਕਰੀਨਸ਼ਾਟ ਲਵੋ"
+
+#: js/ui/status/system.js:161
+#| msgctxt "search-result"
+#| msgid "Power Off"
+msgid "Power Off Menu"
+msgstr "ਬੰਦ ਕਰਨ ਮੇਨੂ"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "ਸਸਪੈਂਡ"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "...ਮੁੜ-ਚਾਲੂ ਕਰੋ"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "…ਬੰਦ ਕਰੋ"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "ਲਾਗ ਆਉਟ…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "…ਵਰਤੋਂਂਕਾਰ ਬਦਲੋ"
+
+#: js/ui/status/system.js:235
+#| msgctxt "search-result"
+#| msgid "Lock Screen"
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "ਸਕਰੀਨ ਲਾਕ"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "ਥੰਡਰਬੋਲਟ"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "ਅਣਪਛਾਤਾ ਥੰਡਰਬੋਲਟ ਡਿਵਾਈਸ"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"ਜਦੋਂ ਤੁਸੀਂ ਦੂਰ ਸੀ ਤਾਂ ਨਵਾਂ ਡਿਵਾਈਸ ਖੋਜਿਆ ਗਿਆ ਹੈ। ਡਿਵਾਈਸ ਨੂੰ ਵਰਤਣਾ ਸ਼ੁਰੂ ਕਰਨ ਲਈ"
+" ਇਸ ਨੂੰ ਡਿਸ-"
+"ਕਨੈਕਟ ਕਰਕੇ ਮੁੜ ਕਨੈਕਟ ਕਰੋ।"
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "ਨਾ-ਪ੍ਰਮਾਣਿਤ ਕੀਤਾ ਥੰਡਰਬੋਲਟ ਯੰਤਰ"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"ਨਵਾਂ ਡਿਵਾਈਸ ਖੋਜਿਆ ਜਾ ਚੁੱਕਿਆ ਗਿਆ ਹੈ ਅਤੇ ਪਰਸ਼ਾਸ਼ਕ ਵਲੋਂ ਪਰਮਾਣਿਤ ਕਰਨ ਦੀ ਲੋੜ ਹੁੰਦੀ"
+" ਹੈ।"
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "ਥੰਡਰਬੋਲਟ ਪਰਮਾਣਕਿਤਾ ਗ਼ਲਤੀ"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "ਥੰਡਰਬੋਲਟ ਡਿਵਾਈਸ ਨੂੰ ਪਰਮਾਣਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "ਵਾਲੀਅਮ ਬਦਲਿਆ ਗਿਆ"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "ਵਾਲੀਅਮ"
+
+#: js/ui/status/volume.js:269
+#| msgid "%u Output"
+#| msgid_plural "%u Outputs"
+msgid "Sound Output"
+msgstr "ਆਵਾਜ਼ ਆਉਟਪੁੱਟ"
+
+#: js/ui/status/volume.js:337
+#| msgid "%u Input"
+#| msgid_plural "%u Inputs"
+msgid "Sound Input"
+msgstr "ਆਵਾਜ਼ ਇਨਪੁੱਟ"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "ਮਿੱਰਰ"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "ਡਿਸਪਲੇਆਂ ਨੂੰ ਜੋੜੋ"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "ਕੇਵਲ ਬਾਹਰੀ"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "ਕੇਵਲ ਬਿਲਟ-ਇਨ"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "ਅਣ-ਲਾਕ ਕਰਨ ਲਈ ਉੱਤੇ ਵੱਲ ਸਰਕਾਓ"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "ਅਣ-ਲਾਕ ਕਰਨ ਲਈ ਸਵਿੱਚ ਕਲਿੱਕ ਕਰੋ ਜਾਂ ਦਬਾਓ"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "ਵਿੰਡੋ ਨੂੰ ਅਣਲਾਕ ਕਰੋ"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "ਹੋਰ ਵਰਤੋਂਕਾਰ ਵਜੋਂ ਲਾਗਇਨ"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "ਗਨੋਮ %s ਲਈ ਜੀ ਆਇਆਂ ਨੂੰ"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "ਜੇ ਤੁਸੀਂ ਕੁਝ ਜਾਣਕਾਰੀ ਹਾਸਲ ਕਰਨੀ ਚਾਹੁੰਦੇ ਹੋ ਤਾਂ ਟੂਰ ਲਵੋ।"
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "ਨਹੀਂ ਜੀ ਧੰਨਵਾਦ"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "ਟੂਰ ਲਵੋ"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” ਤਿਆਰ ਹੈ"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "ਇਹ ਡਿਸਪਲੇਅ ਸੈਟਿੰਗਾਂ ਰੱਖਣੀਆਂ ਚਾਹੁੰਦੇ ਹੋ?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "ਸੈਟਿੰਗਾਂ ਵਾਪਿਸ ਲਵੋ"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "ਬਦਲਾਅ ਲਾਗੂ ਰੱਖੋ"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "ਸੈਟਿੰਗਾਂ ਬਦਲਾਅ ਨੂੰ %d ਸਕਿੰਟ ਵਿੱਚ ਵਾਪਿਸ ਕੀਤਾ ਜਾਵੇਗਾ"
+msgstr[1] "ਸੈਟਿੰਗਾਂ ਬਦਲਾਅ ਨੂੰ %d ਸਕਿੰਟਾਂ ਵਿੱਚ ਵਾਪਿਸ ਕੀਤਾ ਜਾਵੇਗਾ"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "ਲੁਕਾਓ"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "ਬਹਾਲ ਕਰੋ"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "ਵੱਧੋ-ਵੱਧ"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "ਮੁੜ-ਆਕਾਰ"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "ਟਾਈਟਲ-ਪੱਟੀ ਆਨ-ਸਕਰੀਨ ਹਿਲਾਓ"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "ਹਮੇਸ਼ਾ ਉੱਤੇ ਰੱਖੋ"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "ਹਮੇਸ਼ਾ ਵਿਖਾਈ ਦਿੰਦੇ ਵਰਕਸਪੇਸ ਉੱਤੇ"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "ਖੱਬੇ ਵਰਕਸਪੇਸ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "ਸੱਜੇ ਵਰਕਸਪੇਸ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "ਉੱਤਲੇ ਵਰਕਸਪੇਸ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "ਹੇਠਲੇ ਵਰਕਸਪੇਸ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "ਉੱਤਲੇ ਮਾਨੀਟਰ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "ਹੇਠਲੇ ਮਾਨੀਟਰ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "ਖੱਬੇ ਮਾਨੀਟਰ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "ਸੱਜੇ ਮਾਨੀਟਰ ਵਿੱਚ ਭੇਜੋ"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "ਬੰਦ ਕਰੋ"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "ਈਵੇਲੂਸ਼ਨ ਕੈਲੰਡਰ"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "ਵਰਜ਼ਨ ਛਾਪੋ"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "ਲਾਗਇਨ ਸਕਰੀਨ ਲਈ GDM ਵਲੋਂ ਵਰਤਿਆ ਮੋਡ"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "ਲਾਗਇਨ ਸਕਰੀਨ ਉੱਤੇ ਖਾਸ ਮੋਡ ਜਿਵੇਂ ਕਿ “gdm“ ਵਰਤੋਂ"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "ਸੰਭਵ ਮੋਡ ਵੇਖਾਓ"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "ਅਣਪਛਾਤਾ"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” ਚਲਾਉਣ ਲਈ ਫੇਲ੍ਹ"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "ਪਾਸਵਰਡ ਮਿਲਦੇ ਨਹੀਂ ਹਨ।"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "ਪਾਸਵਰਡ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ਪਰਮਾਣਕਿਤਾ ਡਾਈਲਾਗ ਵਰਤੋਂਂਕਾਰ ਵਲੋਂ ਰੱਦ ਕੀਤਾ"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "ਗਨੋਮ ਇਕਸਟੈਸ਼ਨਾਂ ਦਾ ਇੰਤਜ਼ਾਮ ਕਰੋ"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "ਗਨੋਮ ਪਰੋਜੈਕਟ"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"ਗਨੋਮ ਇਕਸਟੈਨਸ਼ਨਾਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ, ਇਕਸਟੈਨਸ਼ਨ ਪਸੰਦਾਂ ਦੀ ਸੰਰਚਨਾ ਕਰਨ ਅਤੇ"
+" ਬੇਲੋੜੀਆਂ "
+"ਇਕਸਟੈਨਸ਼ਨਾਂ ਹਟਾਉਣ ਜਾਂ ਅਸਮਰੱਥ ਕਰਨ ਦੇ ਕੰਮ ਆਉਂਦੀਆਂ ਹਨ।"
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "ਗਨੋਮ ਸ਼ੈਲ ਇਕਸਟੈਸ਼ਨ ਸੰਰਚਨਾ"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "ਕੋਈ ਮਿਲਦਾ ਨਹੀਂ ਹੈ"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "“%s” ਨੂੰ ਹਟਾਉਣਾ ਹੈ?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"ਜੇ ਤੁਸੀਂ ਇਕਟੈਨਸ਼ਨ ਹਟਾਈ ਤਾਂ ਜੇ ਤੁਹਾਨੂੰ ਮੁੜ ਇਹ ਸਮਰੱਥ ਕਰਨਾ ਦੀ ਲੋੜ ਹੋਈ ਤਾਂ ਤੁਹਾਨੂੰ"
+" ਇਹ ਦੁਬਾਰਾ ਡਾਊਨਲੋਡ "
+"ਕਰਨ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "ਹਟਾਓ"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"ਅ ਸ ਆਲਮ (A S Alam) ੨੦੦੪-੨੦੨੧\n"
+"Punjabi OpenSource Team (POST) www.satluj.org"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d ਇਕਟੈਨਸ਼ਨ ਨੂੰ ਅਗਲੇ ਲਾਗਇਨ ਉੱਤੇ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾਵੇਗਾ।"
+msgstr[1] "%d ਇਕਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਗਲੇ ਲਾਗਇਨ ਉੱਤੇ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾਵੇਗਾ।"
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਮੌਜੂਦਾ ਗਨੋਮ ਵਰਜ਼ਨ ਨਾਲ ਅਨੁਕੂਲ ਨਹੀਂ ਹੈ"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਲਈ ਗ਼ਲਤੀ ਆਈ"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+#| msgid "Show extensions with updates"
+msgid "The extension can be updated"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅੱਪਡੇਟ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "ਵੈੱਬਸਾਈਟ"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "...ਨੂੰ ਹਟਾਓ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "ਮਦਦ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "ਇਕਸਟੈਨਸ਼ਨਾਂ ਬਾਰੇ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"ਇਕਸਟੈਨਸ਼ਨਾਂ ਕਾਰਗੁਜ਼ਾਰੀ ਅਤੇ ਸਥਿਰਤਾਂ ਮਸਲੇ ਖੜ੍ਹੇ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਜੇ ਤੁਹਾਨੂੰ ਆਪਣੇ"
+" ਸਿਸਟਮ ਨਾਲ ਸਮੱਸਿਆਵਾਂ ਆ ਰਹੀਆਂ ਹੋਣ ਤਾਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਸਮਰੱਥ ਕਰੋ।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "ਖੁਦ ਇੰਸਟਾਲ ਕੀਤੇ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"ਇਕਸਟੈਨਸ਼ਨ ਲੱਭਣ ਅਤੇ ਜੋੜਨ ਲਈ <a href=\"https://extensions.gnome.org\">extensions."
+"gnome.org</a> ਨੂੰ ਵੇਖੋ।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "ਬਿਲਟ-ਇਨ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "ਕੋਈ ਇੰਸਟਾਲ ਕੀਤੀ ਇਕਟੈਨਸ਼ਨ ਨਹੀਂ ਹੈ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"ਸਾਨੂੰ ਬਹੁਤ ਅਫ਼ਸੋਸ ਹੈ, ਪਰ ਇੰਸਟਾਲ ਹੋਈਆਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਦੀ ਸੂਚੀ ਲੈਣਾ ਸੰਭਵ ਨਹੀਂ ਹੈ।"
+" ਯਕੀਨੀ ਬਣਾਓ ਕਿ "
+"ਤੁਸੀਂ ਗਨੋਮ ਵਿੱਚ ਲਾਗਇਨ ਕੀਤਾ ਹੈ ਅਤੇ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅੱਪਡੇਟ ਤਿਆਰ ਹਨ"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "ਨਵੀਂ ਇਕਸਟੈਨਸ਼ਨ %s ਵਿੱਚ ਕਾਮਯਾਬੀ ਨਾਲ ਬਣਾਈ ਗਈ ਸੀ।\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"ਨਾਂ ਬਹੁਤ ਛੋਟੀ ਸਤਰ (ਸਹੀ ਵਰਣਨਾਤਮਿਕ) ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।\n"
+"ਮਿਸਾਲ ਵਜੋਂ: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "ਨਾਂ"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"ਤੁਹਾਡੀ ਇਕਟੈਨਸ਼ਨ ਕੀ ਕਰਦੀ ਹੈ, ਉਸ ਬਾਰੇ ਇੱਕ ਲਾਈਨ ਵਿੱਚ ਜਾਣਕਾਰੀ ਹੈ।\n"
+"ਮਿਸਾਲ ਵਜੋਂ: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "ਵਰਣਨ"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID ਤੁਹਾਡੀ ਇਕਟੈਨਸ਼ਨ ਲਈ ਪੂਰੇ ਸਿਸਟਮ ਲਈ ਵਿਲੱਖਣ ਪਛਾਣਕਰਤਾ ਹੈ।\n"
+"ਇਹ ਈਮੇਲ ਸਿਰਨਾਵੇਂ(clicktofocus@janedoe.example.com) ਦੇ ਰੂਪ ਵਿੱਚ ਹੋਣਾ ਚਾਹੀਦਾ"
+" ਹੈ।\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "ਮੌਜੂਦਾ ਟੈਂਪਲੇਟ ਵਿੱਚੋਂ ਇੱਕ ਚੁਣੋ:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "ਟੈਂਪਲੇਟ"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "ਨਵੀਂ ਇਕਟੈਨਸ਼ਨ ਲਈ ਵਿੱਲਖਣ ਪਛਾਣਕਰਤਾ"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "ਨਾਂ"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "ਨਵੀਂ ਇਕਟੈਨਸ਼ਨ ਦਾ ਵਰਤੋਂਕਾਰ ਨੂੰ ਦਿਸਣ ਵਾਲਾ ਨਾਂ"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "ਵਰਣਨ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "ਛੋਟਾ ਵਰਣਨ ਕਿ ਇਕਸਟੈਨਸ਼ਨ ਕੀ ਕਰਦੀ ਹੈ"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ਟੈਂਪਲੇਟ"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "ਨਵੀਂ ਇਕਟੈਨਸ਼ਨ ਲਈ ਵਰਤਣ ਲਈ ਟੈਂਪਲੇਟ"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "ਇਕਟੈਨਸ਼ਨ ਜਾਣਕਾਰੀ ਤਾਲਮੇਲ ਦਿਓ"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "ਨਵੀਂ ਇਕਸਟੈਨਸ਼ਨ ਬਣਾਓ"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "ਅਣਪਛਾਤਾ ਆਰਗੂਮੈਂਟ"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, ਨਾਂ ਅਤੇ ਵਰਣਨ ਚਾਹੀਦਾ ਹੈ"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "ਗਨੋਮ ਸ਼ੈੱਲ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਲਈ ਅਸਫ਼ਲ ਹੈ\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "ਇਕਟੈਨਸ਼ਨ “%s” ਮੌਜੂਦ ਨਹੀਂ ਹੈ\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅਸਮਰੱਥ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "ਕੋਈ UUID ਨਹੀਂ ਦਿੱਤਾ"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "ਇੱਕ ਤੋਂ ਵੱਧ UUID ਦਿੱਤਾ"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਸਮਰੱਥ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "ਇਕਟੈਨਸ਼ਨ “%s” ਮੌਜੂਦ ਨਹੀਂ ਹੈ\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਜਾਣਕਾਰੀ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "ਮੌਜੂਦਾ ਇਕਟੈਨਸ਼ਨ ਉੱਤੇ ਲਿਖੋ"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "ਇਕਟੈਨਸ਼ਨ ਬੰਡਲ ਇੰਸਟਾਲ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "ਕੋਈ ਇਕਟੈਨਸ਼ਨ ਬੰਡਲ ਨਹੀਂ ਦਿੱਤਾ"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "ਇੱਕ ਤੋਂ ਵੱਧ ਇਕਟੈਨਸ਼ਨ ਬੰਡਲ ਦਿੱਤਾ ਗਿਆ"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "ਵਰਤੋਂਕਾਰ ਵਲੋਂ ਇੰਸਟਾਲ ਕੀਤੀਆਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "ਸਿਸਟਮ ਰਾਹੀਂ ਇੰਸਟਾਲ ਕੀਤੀਆਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "ਸਮਰੱਥ ਕੀਤੀਆਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "ਅਸਮਰੱਥ ਕੀਤੀਆਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "ਪਸੰਦਾਂ ਨਾਲ ਇਕਸਟੈਨਸ਼ਨਾਂ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "ਇਕਸਟੈਨਸ਼ਨਾਂ ਨਾਲ ਇਕਸਟੈਨਸ਼ਨਾਂ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਵੇਰਵੇ ਛਾਪੋ"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "ਇੰਸਟਾਲ ਕੀਤੀਆਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਦੀ ਸੂਚੀ"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ਫਾਇਲ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "ਬੰਡਲ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਵਧੀਕ ਸਰੋਤ"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ਸਕੀਮਾ"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "GSettings ਸਕੀਮ, ਜੋ ਸ਼ਾਮਲ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ਡਾਇਰੈਕਟਰੀ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "ਡਾਇਰੈਕਟਰੀ, ਜਿੱਥੇ ਉਲੱਥੇ ਲੱਭੇ ਜਾਂਦੇ ਹਨ"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ਡੋਮੇਨ"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "ਉਲੱਥੇ ਲਈ ਵਰਤਣ ਲਈ gettext ਡੋਮੇਨ"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "ਮੌਜੂਦਾ ਪੈਕ ਉੱਤੇ ਲਿਖੋ"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "ਡਾਇਰੈਕਟਰੀ, ਜਿੱਥੇ ਪੈਕ ਬਣਾਏ ਜਾਣੇ ਚਾਹੀਦੇ ਹਨ"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ਸਰੋਤ_ਡਾਇਰੈਕਟਰੀ"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "ਇਕਟੈਨਸ਼ਨ ਬੰਡਲ ਬਣਾਓ"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "ਇੱਕ ਤੋਂ ਵੱਧ ਸਰੋਤ ਡਾਇਰੈਕਟਰੀ ਦਿੱਤੀ ਗਈ ਸੀ"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "ਇਕਟੈਨਸ਼ਨ “%s” ਲਈ ਪਸੰਦਾਂ ਨਹੀਂ ਹਨ\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ “%s” ਲਈ ਪਸੰਦ ਖੋਲ੍ਹਣ ਲਈ ਅਸਫ਼ਲ ਹੈ: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਪਸੰਦਾਂ ਨੂੰ ਖੋਲ੍ਹੋ"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਨੂੰ ਮੁੜ-ਸੈੱਟ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "ਸਿਸਟਮ ਇਕਟੈਨਸ਼ਨਾਂ ਅਣ-ਇੰਸਟਾਲ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕਦੀਆਂ\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "“%s” ਅਣ-ਇੰਸਟਾਲ ਕਰਨ ਲਈ ਅਸਫ਼ਲ ਹੈ\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅਣ-ਇੰਸਟਾਲ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "ਗ਼ਲਤੀ ਸੁਨੇਹੇ ਪਰਿੰਟ ਨਾ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "ਮਾਰਗ"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "ਅਸਲੀ ਲੇਖਕ"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "ਵਰਜ਼ਨ"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "ਹਾਲਤ"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” ਲਈ ਕੋਈ ਆਰਗੂਮੈਂਟ ਨਹੀਂ ਹੁੰਦਾ"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "ਵਰਤੋ:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "ਵਰਜ਼ਨ ਜਾਣਕਾਰੀ ਛਾਪੋ ਅਤੇ ਬੰਦ ਕਰੋ।"
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "ਕਮਾਂਡ"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "ਕਮਾਂਡਾਂ:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "ਮਦਦ ਛਾਪੋ"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਸਮਰੱਥ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅਸਮਰੱਥ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਨੂੰ ਮੁੜ-ਸੈੱਟ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਅਣ-ਇੰਸਟਾਲ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "ਇਕਸਟੈਨਸ਼ਨਾਂ ਦੀ ਸੂਚੀ"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਜਾਣਕਾਰੀ ਵੇਖਾਓ"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਪਸੰਦਾਂ ਨੂੰ ਖੋਲ੍ਹੋ"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਬਣਾਓ"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "ਇਕਸਟੈਨਸ਼ਨ ਪੈਕੇਜ ਬਣਾਓ"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "ਇਕਟੈਨਸ਼ਨ ਬੰਡਲ ਇੰਸਟਾਲ ਕਰੋ"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "ਵੇਰਵੇ ਸਮੇਤ ਮਦਦ ਲੈਣ ਲਈ “%s“ ਵਰਤੋ।\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "ਤਲ"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "ਖਾਲੀ ਇਕਟੈਨਸ਼ਨ"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "ਸੰਕੇਤਕ"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "ਸਿਖਰ ਪੱਟੀ ਲਈ ਆਈਕਾਨ ਜੋੜੋ"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u ਆਉਟਪੁੱਟ"
+msgstr[1] "%u ਆਉਟਪੁੱਟ"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ਇੰਪੁੱਟ"
+msgstr[1] "%u ਇੰਪੁੱਟ"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "ਸਿਸਟਮ ਸਾਊਂਡ"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "ਕੀ ਡਿਫਾਲਟ ਬਲੂਟੁੱਥ ਅਡੈਪਟਰ ਇਸ ਨਾਲ ਸਬੰਧਿਤ ਡਿਵਾਈਸਾਂ ਲਈ ਸੈੱਟ ਅੱਪ ਕੀਤਾ ਹੈ"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "ਸ਼ੈੱਲ ਬਲੂਟੁੱਥ ਮੇਨੂ ਆਈਟਮ ਨੂੰ ਕੇਵਲ ਤਾਂ ਹੀ ਦਿਖਾਏਗੀ, ਜੇ ਬਲੂਟੁੱਥ ਅਡੈਪਟਰ ਚਾਲੂ ਹੈ ਜਾਂ ਡਿਫਾਲਟ ਅਡੈਪਟਰ "
+#~ "ਨਾਲ ਸੰਬੰਧਿਤ ਡਿਵਾਈਸ ਸੈਟਅੱਪ ਮੌਜੂਦ ਹੈ। ਇਸ ਨੂੰ ਮੁੜ-ਸੈੱਟ ਕੀਤਾ ਜਾਵੇਗਾ, ਜੇ ਡਿਫਾਲਟ ਅਡੈਪਟਰ ਨੂੰ ਇਸ "
+#~ "ਨਾਲ ਸੰਬੰਧਿਤ ਕੀਤਾ ਡਿਵਾਈਸ ਨਾ ਮਿਲਿਆ।"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "ਮਨਪਸੰਦਾਂ 'ਚੋਂ ਹਟਾਓ"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "ਮਨਪਸੰਦ 'ਚ ਜੋੜੋ"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "ਬਲੂਟੁੱਥ ਸੈਟਿੰਗਾਂ"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d ਕਨੈਕਟ ਹੈ"
+#~ msgstr[1] "%d ਕਨੈਕਟ ਹੈ"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "ਬਲੂਟੁੱਥ ਬੰਦ ਕਰੋ"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "ਬਲੂਟੁੱਥ ਚਾਲੂ ਕਰੋ"
+
+#~ msgid "Location Enabled"
+#~ msgstr "ਟਿਕਾਣਾ ਸਮਰੱਥ ਹੈ"
+
+#~ msgid "Disable"
+#~ msgstr "ਬੰਦ"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "ਪਰਦੇਦਾਰੀ ਸੈਟਿੰਗਾਂ"
+
+#~ msgid "Location In Use"
+#~ msgstr "ਟਿਕਾਣਾ ਵਰਤੋਂ ਵਿੱਚ ਹੈ"
+
+#~ msgid "Location Disabled"
+#~ msgstr "ਟਿਕਾਣਾ ਅਸਮਰੱਥ ਹੈ"
+
+#~ msgid "Enable"
+#~ msgstr "ਚਾਲੂ"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s ਬੰਦ ਹੈ"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s ਕਨੈਕਟ ਹੈ"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s ਬਿਨ-ਇੰਤਜ਼ਾਮ"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s ਲਈ ਫਿਰਮਵੇਅਰ ਮੌਜੂਦ ਨਹੀਂ ਹੈ"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s ਨਾ-ਮੌਜੂਦ"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s ਕਨੈਕਸ਼ਨ ਫੇਲ੍ਹ ਹੈ"
+
+#~ msgid "Wired Settings"
+#~ msgstr "ਤਾਰ ਸੈਟਿੰਗਾਂ"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "ਮੋਬਾਇਲ ਬਰਾਡਬੈਂਡ ਸੈਟਿੰਗਾਂ"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s ਹਾਰਡਵੇਅਰ ਅਸਮਰੱਥ ਹੈ"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s ਅਸਮਰੱਥ ਹੈ"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "ਏਅਰਪਲੇਨ ਮੋਡ ਚਾਲੂ ਹੈ"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "ਜਦੋਂ ਏਅਰ-ਪਲੇਨ ਮੋਡ ਚਾਲੂ ਹੁੰਦਾ ਹੈ ਤਾਂ ਵਾਈ-ਫਾਈ ਬੰਦ ਹੁੰਦਾ ਹੈ"
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "ਏਅਰਪਲੇਨ ਮੋਡ ਬੰਦ ਕਰੋ"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "ਵਾਈ-ਫਾਈ ਬੰਦ ਹੈ"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "ਨੈੱਟਵਰਕ ਨਾਲ ਕੁਨੈਕਟ ਕਰਨ ਲਈ ਵਾਈ-ਫਾਈ ਨੂੰ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ"
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "ਵਾਈ-ਫਾਈ ਚਾਲੂ"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ"
+
+#~ msgid "Select a network"
+#~ msgstr "ਨੈੱਟਵਰਕ ਚੁਣੋ"
+
+#~ msgid "No Networks"
+#~ msgstr "ਕੋਈ ਨੈੱਟਵਰਕ ਨਹੀਂ"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "ਬੰਦ ਕਰਨ ਲਈ ਹਾਰਡਵੇਅਰ ਸਵਿੱਚ ਵਰਤੋਂ"
+
+#~ msgid "Select Network"
+#~ msgstr "ਨੈੱਟਵਰਕ ਚੁਣੋ"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "ਵਾਈ-ਫਾਈ ਸੈਟਿੰਗਾਂ"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s ਕੁਨੈਕਟ ਨਹੀਂ ਹੈ"
+
+#~ msgid "connecting…"
+#~ msgstr "…ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"
+
+#~ msgid "authentication required"
+#~ msgstr "ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ"
+
+#~ msgid "connection failed"
+#~ msgstr "ਕਨੈਕਸ਼ਨ ਫੇਲ੍ਹ ਹੈ"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN ਸੈਟਿੰਗਾਂ"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN ਬੰਦ ਹੈ"
+
+#~ msgid "Network Settings"
+#~ msgstr "ਨੈੱਟਵਰਕ ਸੈਟਿੰਗਾਂ"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s ਤਾਰ ਵਾਲਾ ਕਨੈਕਸ਼ਨ"
+#~ msgstr[1] "%s ਤਾਰ ਵਾਲੇ ਕਨੈਕਸ਼ਨ"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s Wi-Fi ਕਨੈਕਸ਼ਨ"
+#~ msgstr[1] "%s Wi-Fi ਕਨੈਕਸ਼ਨ"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s ਮਾਡਮ ਕਨੈਕਸ਼ਨ"
+#~ msgstr[1] "%s ਮਾਡਮ ਕਨੈਕਸ਼ਨ"
+
+#~ msgid "Connection failed"
+#~ msgstr "ਕਨੈਕਸ਼ਨ ਫੇਲ੍ਹ ਹੈ"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੀ ਐਕਟੀਵੇਸ਼ਨ ਫੇਲ੍ਹ ਹੋਈ"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "ਰਾਤ ਦੀ ਰੌਸ਼ਨੀ ਅਸਮਰੱਥ ਹੈ"
+
+#~ msgid "Resume"
+#~ msgstr "ਮੁੜ-ਪ੍ਰਾਪਤ ਕਰੋ"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "ਭਲਕੇ ਤੱਕ ਅਸਮਰੱਥ ਕਰੋ"
+
+#~ msgid "Power Settings"
+#~ msgstr "ਪਾਵਰ ਸੈਟਿੰਗਾਂ"
+
+#~ msgid "Fully Charged"
+#~ msgstr "ਪੂਰੀ ਚਾਰਜ"
+
+#~ msgid "Not Charging"
+#~ msgstr "ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਹੀ"
+
+#~ msgid "Estimating…"
+#~ msgstr "…ਅੰਦਾਜ਼ਾ ਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d ਬਾਕੀ (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d ਪੂਰਾ ਹੋਣ ਲਈ (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "ਸਕਰੀਨ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"
+
+#~ msgid "Turn off"
+#~ msgstr "ਬੰਦ ਕਰੋ"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "ਏਅਰਪਲੇਨ ਮੋਡ ਚਾਲੂ ਹੈ"
+
+#~ msgid "Lock"
+#~ msgstr "ਲਾਕ"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "ਬੰਦ ਕਰੋ / ਲਾਗ ਆਉਟ"
+
+#~ msgid "Log Out"
+#~ msgstr "ਲਾਗ ਆਉਟ"
+
+#~ msgid "Author"
+#~ msgstr "ਲੇਖਕ"
+
+#~ msgid "Warning"
+#~ msgstr "ਚੇਤਾਵਨੀ"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "ਇਕਸਟੈਨਸ਼ਨਾਂ ਸਿਸਟਮ ਪੱਧਰ ਦੇ ਮਸਲੇ ਖੜ੍ਹੇ ਕਰ ਸਕਦੀਆਂ ਹਨ, ਜਿਸ ਵਿੱਚ ਕਾਰਗੁਜ਼ਾਰੀ ਸਮੱਸਿਆਵਾਂ ਹਨ। ਜੇ "
+#~ "ਤੁਹਾਨੂੰ ਤੁਹਾਡੇ ਸਿਸਟਮ ਨਾਲ ਸਮੱਸਿਆਵਾਂ ਆ ਰਹੀਆਂ ਹੋਣ ਤਾਂ ਸਾਰੀਆਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਸਮਰੱਥ ਕਰਨ ਦੀ "
+#~ "ਸਿਫਾਰਸ਼ ਕੀਤੀ ਜਾਂਦੀ ਹੈ।"
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "ਗਨੋਮ ਸ਼ੈੱਲ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਲਈ ਫੇਲ੍ਹ ਹੈ"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "ਧਿਆਨ ਦਿਵਾਉ API ਸਮਰੱਥ ਕਰੋ"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "ਡੀ-ਬੱਸ API ਸਮਰੱਥ ਕਰਦੀ ਹੈ, ਜੋ ਕਿ ਸ਼ੈੱਲ ਦੀ ਐਪਲੀਕੇਸ਼ਨ ਹਾਲਤ ਉੱਤੇ ਧਿਆਨ ਦੇਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"
+
+#~ msgid "Minimize"
+#~ msgstr "ਘੱਟੋ-ਘੱਟ"
+
+#~ msgid "Unmaximize"
+#~ msgstr "ਅਣ-ਵੱਧੋ-ਵੱਧ"
+
+#~ msgid "App Picker View"
+#~ msgstr "ਐਪ ਚੁਣਨ ਝਲਕ"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "ਐਪਲੀਕੇਸ਼ਨ ਚੋਣਕਾਰ ਵਿੱਚ ਮੌਜੂਦਾ ਚੁਣੀ ਝਲਕ ਦਾ ਇੰਡੈਕਸ।"
+
+#~ msgid "Copy Error"
+#~ msgstr "ਗਲਤੀ ਕਾਪੀ ਕਰੋ"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "ਆਮ ਵਰਤੀਆਂ ਐਪਲੀਕੇਸ਼ਨ ਇੱਥੇ ਵਿਖਾਈ ਦੇਣਗੀਆਂ"
+
+#~ msgid "Frequent"
+#~ msgstr "ਅਕਸਰ"
+
+#~ msgid "All"
+#~ msgstr "ਸਭ"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d %B %Y"
+
+#~ msgid "Off"
+#~ msgstr "ਬੰਦ"
+
+#~ msgid "On"
+#~ msgstr "ਚਾਲੂ"
+
+#~ msgid "Browse in Software"
+#~ msgstr "ਸਾਫਟਵੇਅਰ ਵਿੱਚ ਵੇਖੋ"
+
+#~ msgid "Next"
+#~ msgstr "ਅੱਗੇ"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "ਸਾਇਨ ਇਨ"
+
+#~ msgid "Password:"
+#~ msgstr "ਪਾਸਵਰਡ:"
+
+#~ msgid "Type again:"
+#~ msgstr "ਮੁੜ-ਕੋਸ਼ਿਸ਼:"
+
+#~ msgid "Password: "
+#~ msgstr "ਪਾਸਵਰਡ: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "ਬੇਤਾਰ ਨੈੱਟਵਰਕ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "ਮੋਬਾਇਲ ਬਰਾਡਬੈਂਡ ਨੈੱਟਵਰਕ ਪਾਸਵਰਡ"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d ਨਵਾਂ ਸੁਨੇਹਾ"
+#~ msgstr[1] "%d ਨਵੇਂ ਸੁਨੇਹੇ"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d ਨਵਾਂ ਸੂਚਨਾ"
+#~ msgstr[1] "%d ਨਵੀਆਂ ਸੂਚਨਾਵਾਂ"
+
+#~ msgid "Account Settings"
+#~ msgstr "ਖਾਤਾ ਸੈਟਿੰਗਾਂ"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "ਸਥਿਤੀ ਲਾਕ"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr "ਕੀਬਾਈਡਿੰਗ, ਜੋ ਕਿ ਡੀਬੱਗ ਕਰਨ ਵਾਸਤੇ ਸਭ ਚੱਲ ਰਹੀਆਂ ਟਵੀਨਸ ਨੂੰ ਰੌਕਦੀ ਅਤੇ ਚਲਾਉਂਦੀ ਹੈ"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "ਕਿਹੜਾ ਕੀਬੋਰਡ ਵਰਤਣਾ ਹੈ"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "ਵਰਤਣ ਲਈ ਕੀਬੋਰਡ ਦੀ ਕਿਸਮ ਹੈ।"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "%s ਲਈ ਪਸੰਦ ਡਾਈਲਾਗ ਨੂੰ ਲੋਡ ਕਰਨ ਦੌਰਾਨ ਸਮੱਸਿਆ:"
+
+#~ msgid "%s all day."
+#~ msgstr "ਸਾਰਾ ਦਿਨ %s।"
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, ਤਦ ਬਾਅਦ ਵਿੱਚ %s।"
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, ਤਦ %s, ਉਸ ਤੋਂ ਬਾਅਦ %s।"
+
+#~ msgid "Feels like %s."
+#~ msgstr "%s ਵਾਂਗੂੰ ਮਹਿਸੂਸ ਹੁੰਦਾ ਹੈ।"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~| msgid "Log Out"
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "ਲਾਗ ਆਉਟ"
+
+#~| msgid "Switch User"
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "ਵਰਤੋਂਕਾਰ ਨੂੰ ਬਦਲੋ"
+
+#~ msgid "Hide tray"
+#~ msgstr "ਟਰੇ ਓਹਲੇ ਕਰੋ"
+
+#~ msgid "Status Icons"
+#~ msgstr "ਹਾਲਤ ਆਈਕਾਨ"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "ਵੈੱਬ ਪਰਮਾਣਕਿਤਾ ਰੀ-ਡਾਇਰੈਕਟ"
+
+#~ msgid "Events"
+#~ msgstr "ਘਟਨਾਵਾਂ"
+
+#~ msgid "Notifications"
+#~ msgstr "ਨੋਟੀਫਿਕੇਸ਼ਨ"
+
+#~ msgid "Media"
+#~ msgstr "ਮੀਡੀਆ"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Not In Use"
+#~ msgstr "ਵਰਤੋਂ ਵਿੱਚ ਨਹੀਂ"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "ਕੈਲੰਡਰ ਵਿੱਚ ਹਫ਼ਤਾ ਮਿਤੀ ਵੇਖੋ"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "ਜੇ ਚੋਣ ਕੀਤੀ ਤਾਂ ਕੈਲੰਡਰ ਵਿੱਚ ISO ਹਫਤਾ ਮਿਤੀ ਵੇਖਾਈ ਜਾਵੇਗੀ।"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s ਵਲੋਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਲਈ ਬੇਨਤੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ।"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "ਇੰਟਰਨੈੱਟ ਕੁਨੈਕਸ਼ਨ ਵਜੋਂ ਵਰਤੋਂ"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "ਗਨੋਮ ਸ਼ੈਲ (ਵੇਲੈਂਡ ਨਿਰਮਾਤਾ)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d ਕੁਨੈਕਟ ਹੋਇਆ ਜੰਤਰ"
+#~ msgstr[1] "%d ਕੁਨੈਕਟ ਹੋਏ ਜੰਤਰ"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "ਬੈਟਰੀ"
+
+#~ msgid "Show the message tray"
+#~ msgstr "ਸੁਨੇਹਾ ਟਰੇ ਵੇਖਾਓ"
+
+#~ msgid "Captive Portal"
+#~ msgstr "ਕੈਪਟਿਵ ਪੋਰਟਲ"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "ਐ"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "ਸੋ"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "ਮੰ"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "ਬੁੱ"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "ਵੀ"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "ਸ਼ੁੱ"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "ਸ਼"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "ਕੋਈ ਵੀ ਸੈਡਿਊਲ ਨਹੀਂ ਹੈ"
+
+#~ msgid "This week"
+#~ msgstr "ਇਹ ਹਫ਼ਤਾ"
+
+#~ msgid "Next week"
+#~ msgstr "ਹਫ਼ਤਾ ਅੱਗੇ"
+
+#~ msgid "Removable Devices"
+#~ msgstr "ਹਟਾਉਣਯੋਗ ਜੰਤਰ"
+
+#~ msgid "Eject"
+#~ msgstr "ਬਾਹਰ ਕੱਢੋ"
+
+#~ msgid "Invitation"
+#~ msgstr "ਸੱਦਾ"
+
+#~ msgid "Call"
+#~ msgstr "ਕਾਲ ਕਰੋ"
+
+#~ msgid "File Transfer"
+#~ msgstr "ਫਾਇਲ ਟਰਾਂਸਫਰ"
+
+#~ msgid "Chat"
+#~ msgstr "ਗੱਲਬਾਤ"
+
+#~ msgid "Unmute"
+#~ msgstr "ਸੁਣਾਓ"
+
+#~ msgid "Mute"
+#~ msgstr "ਚੁੱਪ"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "%s ਲਈ ਸੱਦਾ"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s ਤੁਹਾਨੂੰ %s ਜੁਆਇੰਨ ਕਰਨ ਲਈ ਸੱਦ ਰਿਹਾ ਹੈ"
+
+#~ msgid "Decline"
+#~ msgstr "ਇਨਕਾਰ"
+
+#~ msgid "Accept"
+#~ msgstr "ਮਨਜ਼ੂਰ"
+
+#~ msgid "Video call from %s"
+#~ msgstr "%s ਵਲੋਂ ਵਿਡੀਓ ਕਾਲ"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s ਵਲੋਂ ਕਾਲ"
+
+#~ msgid "Answer"
+#~ msgstr "ਜਵਾਬ ਦਿਓ"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s ਤੁਹਾਨੂੰ %s ਭੇਜ ਰਿਹਾ/ਰਹੀ ਹੈ"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s ਤੁਹਾਨੂੰ ਵੇਖਣ ਦਾ ਅਧਿਕਾਰ ਚਾਹੁੰਦਾ/ਚਾਹੁੰਦੀ ਹੈ, ਜਦੋਂ ਵੀ ਤੁਸੀਂ ਆਨਲਾਈਨ ਹੋਵੋ"
+
+#~ msgid "Authentication failed"
+#~ msgstr "ਪਰਮਾਣਕਿਤਾ ਫੇਲ੍ਹ ਹੋਈ"
+
+#~ msgid "Encryption error"
+#~ msgstr "ਇੰਕ੍ਰਿਪਸ਼ਨ ਗਲਤੀ"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਨਹੀਂ ਦਿੱਤਾ"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਬੇਭਰੋਸੇਯੋਗ"
+
+#~ msgid "Certificate expired"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਦੀ ਮਿਆਦ ਪੁੱਗੀ"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਸਰਗਰਮ ਨਹੀਂ ਹੈ"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਹੋਸਟ-ਨਾਂ ਨਹੀਂ ਮਿਲਦਾ"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਫਿੰਗਰ-ਪਰਿੰਟ ਮਿਲਦਾ ਨਹੀਂ"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਖੁਦ-ਦਸਤਖਤੀ ਹੈ"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "ਹਾਲਤ ਆਫਲਾਈਨ ਸੈੱਟ ਕੀਤੀ ਗਈ ਹੈ"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਗਲਤ ਹੈ"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "ਕੁਨੈਕਸ਼ਨ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ ਗਿਆ"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "ਕੁਨੈਕਸ਼ਨ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "ਕੁਨੈਕਸ਼ਨ ਖਤਮ ਹੋ ਗਿਆ"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "ਇਹ ਅਕਾਊਂਟ ਪਹਿਲਾਂ ਹੀ ਸਰਵਰ ਨਾਲ ਕੁਨੈਕਟ ਹੈ"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "ਉਸੇ ਸਰੋਤ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਕੁਨੈਕਸ਼ਨ ਨੂੰ ਨਵੇਂ ਕੁਨੈਕਸ਼ਨ ਨਾਲ ਬਦਲ ਦਿੱਤਾ ਗਿਆ ਹੈ"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "ਅਕਾਊਂਟ ਸਰਵਰ ਉੱਤੇ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "ਸਰਵਰ ਇਸ ਵੇਲੇ ਐਨਾ ਰੁੱਝਿਆ ਹੋਇਆ ਹੈ ਕਿ ਕੁਨੈਕਸ਼ਨ ਸੰਭਾਲ ਨਹੀਂ ਸਕਦਾ"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਮਨਸੂਖ ਕੀਤਾ ਜਾ ਚੁੱਕਾ ਹੈ"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "ਸਰਟੀਫਿਕੇਟ ਅਸੁਰੱਖਿਅਤ ਸੀਫ਼ਰ ਐਲਗੋਰਿਥਮ ਵਰਤਦਾ ਹੈ ਜਾਂ ਕਮਜ਼ੋਰ ਕ੍ਰਿਪਟੋਗਰਾਫਿਕਲ ਵਰਤਦਾ ਹੈ"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "ਸਰਵਰ ਸਰਟੀਫਿਕੇਟ ਦੀ ਲੰਬਾਈ ਜਾਂ ਸਰਵਰ ਸਰਟੀਫਿਕੇਟ ਚੇਨ ਦੀ ਡੂੰਘਾਈ ਕ੍ਰਿਪਟੂਗਰਾਫ਼ੀ ਲਾਇਬਰੇਰੀ ਵਲੋਂ "
+#~ "ਰੱਖੀ ਗਈ ਲਿਮਟ ਤੋਂ ਵੱਧ ਗਈ ਹੈ।"
+
+#~ msgid "Internal error"
+#~ msgstr "ਅੰਦਰੂਨੀ ਗਲਤੀ"
+
+#~ msgid "View account"
+#~ msgstr "ਅਕਾਊਂਟ ਵੇਖੋ"
+
+#~ msgid "Open Calendar"
+#~ msgstr "ਕੈਲੰਡਰ ਖੋਲ੍ਹੋ"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "ਮਿਤੀ ਤੇ ਸਮਾਂ ਸੈਟਿੰਗ"
+
+#~ msgid "Open"
+#~ msgstr "ਖੋਲ੍ਹੋ"
+
+#~ msgid "Clear Messages"
+#~ msgstr "ਸੁਨੇਹੇ ਸਾਫ਼ ਕਰੋ"
+
+#~ msgid "Notification Settings"
+#~ msgstr "ਸੂਚਨਾ ਸੈਟਿੰਗ"
+
+#~ msgid "Tray Menu"
+#~ msgstr "ਟਰੇ ਮੇਨੂ"
+
+#~ msgid "No Messages"
+#~ msgstr "ਕੋਈ ਸੁਨੇਹਾ ਨਹੀਂ"
+
+#~ msgid "Message Tray"
+#~ msgstr "ਸੁਨੇਹਾ ਟਰੇ"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "ਟਿਕਾਣੇ ਦੀ ਵੱਧ ਤੋਂ ਵੱਧ ਸ਼ੁੱਧਤਾ ਦਾ ਪੱਧਰ।"
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "ਟਿਕਾਣੇ ਦੀ ਵੱਧ ਤੋਂ ਵੱਧ ਸ਼ੁੱਧਤਾ ਦਰਸਾਉਣ ਦੀ ਸੰਰਚਨਾ ਕਰੋ, ਜੋ ਕਿ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਵੇਖਣ ਦੀ ਮਨਜ਼ੂਰੀ ਹੈ। "
+#~ "ਢੁੱਕਵੀਆਂ ਚੋਣਾਂ ਵਿੱਚ ਹਨ 'ਬੰਦ' (ਟਿਕਾਣਾ ਸਾਂਝਾ ਕਰਨਾ ਬੰਦ), 'ਦੇਸ਼', 'ਸ਼ਹਿਰ', 'ਆਂਢ-ਗੁਆਂਢ', "
+#~ "'ਗਲੀ' ਅਤੇ 'ਠੀਕ' (ਅਕਸਰ ਜੀਪੀਐਸ ਰਸੀਵਰ ਚਾਹੀਦਾ ਹੈ)। ਕਿਰਪਾ ਕਰਕੇ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਇਹ "
+#~ "ਕੇਵਲ GeoClue ਰਾਹੀਂ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਵਿਖਾਏ ਜਾਣ ਵਾਲੇ ਨੂੰ ਕੰਟਰੋਲ ਕਰਦਾ ਹੈ ਅਟੇ ਉਹ ਆਪਣੇ ਨੈੱਟਵਰਕ "
+#~ "ਸਰੋਤਾਂ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਯੂਜ਼ਰ ਦਾ ਟਿਕਾਣਾ ਲੱਭ ਸਕਦੇ ਹਨ (ਹਾਲਾਂਕਿ ਸ਼ੁੱਧ ਰੂਪ ਵਿੱਚ ਵੱਧ ਤੋਂ ਵੱਧ ਕੇਵਲ ਗਲੀ-"
+#~ "ਪੱਧਰ ਤੱਕ ਹੀ)।"
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "ਟਾਈਟਲ-ਪੱਟੀ ਵਿੱਚ ਬਟਨਾਂ ਦਾ ਪ੍ਰਬੰਧ"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "ਇਹ ਕੁੰਜੀ ਗਨੋਮ ਸ਼ੈੱਲ ਚੱਲਣ ਦੇ ਦੌਰਾਨ org.gnome.desktop.wm.preference ਕੁੰਜੀ ਨੂੰ ਅਣਡਿੱਠਾ "
+#~ "ਕਰਦੀ ਹੈ।"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "ਉੱਤੇ ਦਿੱਤੇ ਕੰਬੋਬਾਕਸ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਇਕਸਟੈਸ਼ਨ ਦੀ ਸੰਰਚਨਾ ਕਰਨ ਲਈ ਚੋਣ ਕਰੋ।"
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "unavailable"
+#~ msgstr "ਨਾ-ਉਪਲੱਬਧ"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "ਕੈਟਾਗਰੀਆਂ ਦੀ ਸੂਚੀ, ਜੋ ਕਿ ਫੋਲਡਰ ਵਜੋਂ ਵੇਖਾਈਆਂ ਜਾਣੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "ਇਹ ਸੂਚੀ ਵਿੱਚ ਹਰੇਕ ਕੈਟਾਗਰੀ ਨਾਂ ਨੂੰ ਐਪਲੀਕੇਸ਼ਨ ਝਲਕ ਵਿੱਚ ਫੋਲਡਰ ਵਜੋਂ ਵੇਖਾਇਆ ਜਾਵੇਗਾ, ਨਾ ਕਿ ਮੁੱਖ "
+#~ "ਝਲਕ ਵਿੱਚ ਲਾਈਨ ਵਿੱਚ ਵੇਖਾਇਆ।"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> <b>%B</b>,<b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b> <b>%B</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "'%s' ਤੋਂ ਪਰਮਾਣਕਿਤਾ ਮੰਗ"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "ਜੰਤਰ %s ਇਸ ਕੰਪਿਊਟਰ ਨਾਲ ਪੇਅਰ ਹੋਣਾ ਚਾਹੁੰਦਾ ਹੈ"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "ਜੰਤਰ %s ਸਰਵਿਸ '%s' ਨੂੰ ਵਰਤਣੀ ਚਾਹੁੰਦਾ ਹੈ।"
+
+#~ msgid "Grant this time only"
+#~ msgstr "ਕੇਵਲ ਇਸ ਸਮੇਂ ਹੀ ਮਨਜ਼ੂਰ"
+
+#~ msgid "Reject"
+#~ msgstr "ਨਾ-ਮਨਜ਼ੂਰ"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s ਲਈ ਪੇਅਰ ਕਰਨ ਦੀ ਪੁਸ਼ਟੀ"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "ਪੁਸ਼ਟੀ ਕਰੋ ਜੀ ਕਿ ਪਾਸ-ਕੀ '%06d' ਜੰਤਰ ਉੱਤੇ ਮੌਜੂਦ ਪਿੰਨ ਨਾਲ ਮਿਲਦਾ ਹੈ।"
+
+#~ msgid "Does not match"
+#~ msgstr "ਮਿਲਦਾ ਨਹੀਂ ਹੈ"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s ਲਈ ਪੇਅਰ ਕਰਨ ਦੀ ਮੰਗ"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "ਜੰਤਰ ਉੱਤੇ ਦਿੱਤਾ ਗਿਆ ਪਿੰਨ ਦਿਉ ਜੀ।"
+
+#~ msgid "OK"
+#~ msgstr "ਠੀਕ ਹੈ"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "ਅਫਸੋਸ, ਅੱਜ ਤੁਹਾਡੇ ਲਈ ਕੋਈ ਨਸੀਹਤ ਨਹੀਂ:\n"
+#~ "%s "
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s ਓਰੇਕਲ ਨੇ ਕਿਹਾ"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Settings Menu"
+#~ msgstr "ਸੈਟਿੰਗ ਮੇਨੂ"
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "ਯੂਜ਼ਰ ਵਲੋਂ ਆਖਰੀ IM ਵਿੱਚ ਮੌਜੂਦਗੀ ਸਥਿਤੀ ਸਟੋਰ ਕਰਨ ਲਈ ਅੰਦਰੂਨੀ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ। "
+#~ "TpConnectionPresenceType ਤੋਂ ਇੱਥੇ ਮੁੱਲ।"
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "ਯੂਜ਼ਰ ਵਲੋਂ ਆਖਰੀ ਸ਼ੈਸ਼ਨ ਵਿੱਚ ਮੌਜੂਦਗੀ ਸਥਿਤੀ ਸਟੋਰ ਕਰਨ ਲਈ ਅੰਦਰੂਨੀ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ। "
+#~ "GsmPresenceStatus ਤੋਂ ਇੱਥੇ ਮੁੱਲ।"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "ਇਹ ਐਪਲੀਕੇਸ਼ਨ ਬੰਦ ਕਰਨ ਤੇ ਸਿਸਟਮ ਨੂੰ ਲਾਗਆਉਟ ਕਰਨ ਲਈ ਲਾਗਆਉਟ ਕਰੋ ਨੂੰ ਕਲਿੱਕ ਕਰੋ।"
+
+#~ msgid "Logging out of the system."
+#~ msgstr "ਸਿਸਟਮ ਲਾਗ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "ਇਹ ਐਪਲੀਕੇਸ਼ਨਾਂ ਬੰਦ ਕਰਨ ਅਤੇ ਸਿਸਟਮ ਨੂੰ ਬੰਦ ਕਰਨ ਲਈ ਬੰਦ ਕਰੋ ਨੂੰ ਕਲਿੱਕ ਕਰੋ।"
+
+#~ msgid "Powering off the system."
+#~ msgstr "ਸਿਸਟਮ ਬੰਦ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "ਇਹ ਐਪਲੀਕੇਸ਼ਨ ਬੰਦ ਕਰਨ ਤੇ ਸਿਸਟਮ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਲਈ ਮੁੜ-ਚਾਲੂ ਕਰੋ ਨੂੰ ਕਲਿੱਕ ਕਰੋ।"
+
+#~ msgid "Restarting the system."
+#~ msgstr "ਸਿਸਟਮ ਮੁੜ-ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "ਬੰਦ ਕਰਨ ਨਾਲ ਉਹਨਾਂ ਵਲੋਂ ਨਾ-ਸੰਭਾਲਿਆ ਕੰਮ ਖਰਾਬ ਹੋ ਸਕਦਾ ਹੈ।"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "ਐਪਲੀਕੇਸ਼ਨ ਵਰਤੋਂ ਬਾਰੇ ਅੰਕੜੇ ਇੱਕਠੇ ਕਰਨੇ ਹਨ"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "ਸ਼ੈੱਲ ਲਗਾਤਾਰ ਸਰਗਰਮ ਐਪਲੀਕੇਸ਼ਨ ਦੀ ਨਿਗਰਾਨੀ ਕਰਦੀ ਰਹਿੰਦੀ ਹੈ ਤਾਂ ਕਿ ਸਭ ਤੋਂ ਵੱਧ ਵਰਤੀਆਂ "
+#~ "ਐਪਲੀਕੇਸ਼ਨਾਂ ਨੂੰ ਵੇਖਾਇਆ ਜਾ ਸਕੇ (ਜਿਵੇਂ ਕਿ ਲਾਂਚਰ ਵਿੱਚ)। ਹਾਲਾਂਕਿ ਇਹ ਡਾਟਾ ਪ੍ਰਾਈਵੇਟ ਹੀ ਰੱਖਿਆ "
+#~ "ਜਾਵੇਗਾ, ਤਾਂ ਵੀ ਜੇ ਤੁਸੀਂ ਚਾਹੋ ਤਾਂ ਇਸ ਨੂੰ ਸੁਰੱਖਿਆ ਕਾਰਨਾਂ ਕਰਕੇ ਬੰਦ ਕਰ ਸਕਦੇ ਹੋ। ਯਾਦ ਰੱਖੋ ਕਿ ਇੰਝ "
+#~ "ਕਰਨ ਨਾਲ ਪਹਿਲਾਂ ਸੰਭਾਲਿਆ ਗਿਆ ਡਾਟਾ ਹਟਾਇਆ ਨਹੀਂ ਜਾਵੇਗਾ।"
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "ਸਕਰੀਨ ਰਿਕਾਡਰ ਨੂੰ ਬਦਲਣ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "ਬਿਲਟ-ਇਨ ਸਕਰੀਨ ਰਿਕਾਰਡ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ/ਰੋਕਣ ਲਈ ਕੀਬਾਈਡਿੰਗ"
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "ਸਕਰੀਨਕਾਸਟ ਰਿਕਾਰਡ ਕਰਨ ਲਈ ਫਰੇਮਰੇਟ ਹੈ।"
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "ਗਨੋਮ ਸ਼ੈੱਲ ਦੇ ਸਕਰੀਨਕਾਸਟ ਰਿਕਾਰਡਰ ਵਲੋਂ ਰਿਕਾਰਡ ਕਰਕੇ ਬਣਾਈ ਗਈ ਸਕਰੀਨਕਾਸਟ ਦਾ ਫਰੇਮਰੇਟ ਫਰੇਮ "
+#~ "ਪ੍ਰਤੀ ਸਕਿੰਟ 'ਚ ਹੈ।"
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "ਸਕਰੀਨਕਾਸਟ ਇੰਕੋਡ ਕਰਨ ਲਈ ਵਰਤਣ ਵਾਸਤੇ ਜੀਸਟਰੀਮਰ ਪਾਇਪਲਾਇਨ"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "ਰਿਕਾਰਡਿੰਗ ਇੰਕੋਡ ਕਰਨ ਲਈ ਜੀਸਟੀਮਰ ਪਾਇਪਲਾਈਨ ਸੈੱਟ ਕਰੋ। ਇਹ gst-launch ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ "
+#~ "ਸੰਟੈਕਸ ਵਰਤਦਾ ਹੈ। ਪਾਇਪਲਾਈਨ ਲਈ ਸਿੰਕ ਪੈਡ ਲਈ ਕੁਨੈਕਟ ਨਹੀਂ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ, ਜਿੱਥੇ ਰਿਕਾਰਡ ਹੋਣ "
+#~ "ਵਾਲੀ ਵਿਡੀਓ ਰਿਕਾਰਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ। ਇਹ ਆਮ ਤੌਰ ਉੱਤੇ ਨਾ-ਕੁਨੈਕਟ ਕੀਤਾ ਸਰੋਤ ਪੈਡ ਹੋਵੇਗਾ, ਉਸ "
+#~ "ਪੈਡ ਤੋਂ ਆਉਟਪੁੱਟ ਨੂੰ ਆਉਟਪੁੱਟ ਫਾਇਲ ਵਿੱਚ ਲਿਖਿਆ ਜਾਵੇਗਾ। ਪਰ ਪਾਈਪਲਾਈਨ ਖੁਦ ਦੀ ਆਉਟਪੁੱਟ ਦਾ ਧਿਆਨ "
+#~ "ਵੀ ਰੱਖ ਸਕਦੀ ਹੈ - ਇਸ ਨਾਲ ਆਉਟਪੁੱਟ ਨੂੰ icecast ਸਰਵਰ ਉੱਤੇ shout2send ਜਾਂ ਕਿਸੇ ਹੋਰ ਰਾਹੀਂ "
+#~ "ਵੀ ਭੇਜਿਆ ਜਾ ਸਕਦਾ ਹੈ। ਜਦੋਂ ਅਣ-ਸੈੱਟ ਕੀਤਾ ਜਾਂ ਖਾਲੀ ਮੁੱਲ ਨਾਲ ਸੈੱਟ ਕੀਤਾ ਤਾਂ, ਡਿਫਾਲਟ "
+#~ "ਪਾਈਪਲਾਈਨ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ। ਇਸ ਇਸ ਵੇਲੇ 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' ਹੈ ਅਤੇ VP8 codec "
+#~ "ਦੀ ਵਰਤੋਂ ਕਰਕੇ WEBM ਨਾਲ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। %T ਸਿਸਟਮ ਉੱਤੇ ਢੁੱਕਵੀ ਥਰਿੱਡ ਗਿਣਤੀ ਦਾ "
+#~ "ਅੰਦਾਜ਼ਾ ਲਗਾਉਣ ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "ਸਕਰੀਨਕਾਸਟ ਸੰਭਾਲਣ ਲਈ ਵਰਤਣ ਵਾਸਤੇ ਫਾਇਲ ਇਕਟੈਨਸ਼ਨ"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "ਰਿਕਾਰਡ ਕੀਤੇ ਸਕਰੀਨਕਾਸਟ ਲਈ ਫਾਇਲ ਨਾਂ ਮੌਜੂਦਾ ਮਿਤੀ ਦੇ ਮੁਤਾਬਕ ਵਿਲੱਖਣ ਫਾਇਲ ਨਾਂ ਹੋਵੇਗਾ ਅਤੇ "
+#~ "ਇਹ ਇਕਸਟੈਨਸ਼ਨ ਵਰਤੀ ਜਾਵੇਗੀ। ਇਸ ਨੂੰ ਬਦਲਿਆ ਜਾਵੇਗਾ, ਜਦੋਂ ਵੱਖਰੇ ਕੰਨਟੇਨਰ ਫਾਰਮੈਟ ਵਿੱਚ ਰਿਕਾਰਡ "
+#~ "ਕੀਤਾ ਜਾਵੇਗਾ।"
+
+#~ msgid "Power"
+#~ msgstr "ਪਾਵਰ"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "ਯੂਨੀਵਰਸਲ ਅਸੈੱਸ ਸੈਟਿੰਗ"
+
+#~ msgid "Visibility"
+#~ msgstr "ਦਿੱਖ"
+
+#~| msgid "Set Up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "…ਨਵਾਂ ਜੰਤਰ ਸੈਟਅੱਪ ਕਰੋ"
+
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "…ਫਾਇਲਾਂ ਭੇਜੋ"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "ਕੀਬੋਰਡ ਸੈਟਿੰਗ"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "ਮਾਊਸ ਸੈਟਿੰਗ"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "ਵਾਲੀਅਮ, ਨੈੱਟਵਰਕ, ਬੈਟਰੀ"
+
+#~ msgid "disabled"
+#~ msgstr "ਬੰਦ ਹੈ"
+
+#~ msgid "cable unplugged"
+#~ msgstr "ਕੇਬਲ ਕੱਢੀ ਹੋਈ ਹੈ"
+
+#~ msgid "More…"
+#~ msgstr "…ਹੋਰ"
+
+#~ msgid "Wired"
+#~ msgstr "ਤਾਰ"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "ਆਟੋ ਈਥਰਨੈੱਟ"
+
+#~ msgid "Auto broadband"
+#~ msgstr "ਆਟੋ ਬਰਾਡਬੈਂਡ"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "ਆਟੋ ਡਾਇਲ-ਅੱਪ"
+
+#~ msgid "Auto %s"
+#~ msgstr "ਆਟੋ %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "ਆਟੋ ਬਲਿਊਟੁੱਥ"
+
+#~ msgid "Auto wireless"
+#~ msgstr "ਆਟੋ ਬੇਤਾਰ"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "ਵਾਈ-ਫਾਈ"
+
+#~ msgid "%d hour remaining"
+#~ msgid_plural "%d hours remaining"
+#~ msgstr[0] "%d ਘੰਟਾ ਬਾਕੀ"
+#~ msgstr[1] "%d ਘੰਟੇ ਬਾਕੀ"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s ਬਾਕੀ"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "ਘੰਟਾ"
+#~ msgstr[1] "ਘੰਟੇ"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "ਮਿੰਟ"
+#~ msgstr[1] "ਮਿੰਟ"
+
+#~ msgid "%d minute remaining"
+#~ msgid_plural "%d minutes remaining"
+#~ msgstr[0] "%d ਮਿੰਟ ਬਾਕੀ"
+#~ msgstr[1] "%d ਮਿੰਟ ਬਾਕੀ"
+
+#~ msgid "AC Adapter"
+#~ msgstr "AC ਐਡਪਟਰ"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "ਲੈਪਟਾਪ ਬੈਟਰੀ"
+
+#~ msgid "Monitor"
+#~ msgstr "ਮਾਨੀਟਰ"
+
+#~ msgid "Mouse"
+#~ msgstr "ਮਾਊਸ"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "ਸੈੱਲ ਫੋਨ"
+
+#~ msgid "Media Player"
+#~ msgstr "ਮੀਡਿਆ ਪਲੇਅਰ"
+
+#~ msgid "Tablet"
+#~ msgstr "ਟੇਬਲੇਟ"
+
+#~ msgid "Computer"
+#~ msgstr "ਕੰਪਿਊਟਰ"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "ਅਣਜਾਣ"
+
+#~ msgid "Available"
+#~ msgstr "ਉਪਲੱਬਧ"
+
+#~ msgid "Busy"
+#~ msgstr "ਰੁਝਿਆ"
+
+#~ msgid "Invisible"
+#~ msgstr "ਅਦਿੱਖ"
+
+#~ msgid "Away"
+#~ msgstr "ਦੂਰ"
+
+#~ msgid "Idle"
+#~ msgstr "ਵੇਹਲਾ"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "ਤੁਹਾਡੀ ਚੈਟ ਹਾਲਤ ਰੁੱਝੇ ਵਜੋਂ ਸੈੱਟ ਕੀਤੀ ਜਾਵੇਗੀ"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "ਨੋਟੀਫਿਕੇਸ਼ਨ ਹੁਣ ਬੰਦ ਕੀਤੇ ਹੋਏ ਹਨ, ਜਿਸ ਵਿੱਚ ਗੱਲਬਾਤ (ਚੈਟ) ਸੁਨੇਹੇ ਵੀ ਹਨ। ਤੁਹਾਡੇ ਆਨਲਾਈਨ ਹਾਲਤ "
+#~ "ਨੂੰ ਵੀ ਅਡਜੱਸਟ ਕੀਤਾ ਜਾਵੇਗਾ ਤਾਂ ਕਿ ਹੋਰਾਂ ਨੂੰ ਇਹ ਪਤਾ ਲੱਗ ਸਕੇ ਕਿ ਤੁਸੀਂ ਉਹਨਾਂ ਦੇ ਸੁਨੇਹੇ ਸ਼ਾਇਦ ਨਾ "
+#~ "ਵੇਖੋਗੇ।"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "ਸਾਇਨ ਇਨ"
+
+#~ msgid "tray"
+#~ msgstr "ਟਰੇ"
+
+#~ msgid "More..."
+#~ msgstr "ਹੋਰ..."
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "United Kingdom"
+#~ msgstr "ਬਰਤਾਨੀਆ"
+
+#~ msgid "Default"
+#~ msgstr "ਡਿਫਾਲਟ"
+
+#~ msgid "Show full name in the user menu"
+#~ msgstr "ਯੂਜ਼ਰ ਮੇਨੂ ਵਿੱਚ ਪੂਰਾ ਨਾਂ ਵੇਖਾਓ"
+
+#~ msgid "Whether the users full name is shown in the user menu or not."
+#~ msgstr "ਕੀ ਯੂਜ਼ਰ ਮੇਨੂ ਵਿੱਚ ਪੂਰਾ ਯੂਜ਼ਰ ਨਾਂ ਵੇਖਾਉਣਾ ਹੈ ਜਾਂ ਨਹੀਂ।"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "ਐਪਲੀਕੇਸ਼ਨ"
+
+#~ msgid "SETTINGS"
+#~ msgstr "ਸੈਟਿੰਗ"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "ਤੁਹਾਡਾ ਪਸੰਦੀਦਾ ਈਸਟਰ ਅੰਡਾ"
+
+#~ msgid "Subscription request"
+#~ msgstr "ਮੈਂਬਰੀ ਦੀ ਮੰਗ"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "<b>%2$A</b> ਨੂੰ <b>%1$X</b> ਵਜੇ ਭੇਜਿਆ"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "<b>%A</b> <b>%B %d</b>, %Y ਨੂੰ ਭੇਜਿਆ"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "%s ਲਈ ਕੁਨੈਕਸ਼ਨ ਫੇਲ੍ਹ ਹੈ"
+
+#~ msgid "Reconnect"
+#~ msgstr "ਮੁੜ-ਕੁਨੈਕਟ ਕਰੋ"
+
+#~ msgid "Browse Files..."
+#~ msgstr "...ਫਾਇਲਾਂ ਦੀ ਝਲਕ"
+
+#~ msgid "Error browsing device"
+#~ msgstr "ਜੰਤਰ ਬਰਾਊਜ਼ ਕਰਨ ਲਈ ਗਲਤੀ"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "ਮੰਗ ਕੀਤੇ ਗਏ ਜੰਤਰ ਨੂੰ ਬਰਾਊਜ਼ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ, ਗਲਤੀ ਸੀ '%s'"
+
+#~ msgid "Wireless"
+#~ msgstr "ਬੇਤਾਰ"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN ਕੁਨੈਕਸ਼ਨ"
+
+#~ msgid "System Settings"
+#~ msgstr "ਸਿਸਟਮ ਸੈਟਿੰਗ"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "ਓਪਨਸਰਚ ਉਪਲੱਬਧ ਕਰਵਾਉਣ ਵਾਲੇ ਬੰਦ ਹਨ"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "'%s' ਅਣ-ਮਾਊਂਟ ਕਰਨ ਲਈ ਫੇਲ੍ਹ"
+
+#~ msgid "Retry"
+#~ msgstr "ਮੁੜ-ਕੋਸ਼ਿਸ਼"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ਥਾਵਾਂ ਅਤੇ ਜੰਤਰ"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Passphrase"
+#~ msgstr "ਵਾਕ"
+
+#~| msgid "Unknown"
+#~ msgctxt "contact"
+#~ msgid "Unknown"
+#~ msgstr "ਅਣਜਾਣ"
+
+#~ msgid "CONTACTS"
+#~ msgstr "ਸੰਪਰਕ"
+
+#~ msgid "%s is online."
+#~ msgstr "%s ਆਨਲਾਈਨ ਹੈ।"
+
+#~ msgid "%s is offline."
+#~ msgstr "%s ਆਫਲਾਈਨ ਹੈ।"
+
+#~ msgid "%s is away."
+#~ msgstr "%s ਦੂਰ ਹੈ।"
+
+#~ msgid "%s is busy."
+#~ msgstr "%s ਰੁੱਝਿਆ/ਰੁੱਝੀ ਹੈ।"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "ਸਮਾਂ ਵਿੱਚ ਸਕਿੰਟ ਵੇਖੋ"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "ਜੇ ਸੱਚ ਹੈ ਤਾਂ ਸਮਾਂ ਵਿੱਚ ਸਕਿੰਟ ਵੀ ਵੇਖਾਏ ਜਾਣਗੇ।"
+
+#~ msgid "Show date in clock"
+#~ msgstr "ਘੜੀ ਵਿੱਚ ਮਿਤੀ ਵੇਖੋ"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "ਜੇ ਸੱਚ ਹੈ ਤਾਂ, ਘੜੀ ਵਿਚ ਸਮਾਂ ਨਾਲ ਮਿਤੀ ਵੀ ਵੇਖਾਈ ਜਾਵੇਗੀ।"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a, %e %b %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e %b, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "ਗਲਤ ਪਾਸਵਰਡ; ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ"
+
+#~ msgid "Hidden"
+#~ msgstr "ਲੁਕਵਾਂ"
+
+#~ msgid "Power Off..."
+#~ msgstr "...ਬੰਦ ਕਰੋ"
+
+#~ msgid "Online Accounts"
+#~ msgstr "ਆਨਲਾਈਨ ਅਕਾਊਂਟ"
+
+#~ msgid "Log Out..."
+#~ msgstr "...ਲਾਗਆਉਟ"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should be loaded. disabled-extensions overrides this setting for "
+#~ "extensions that appear in both lists."
+#~ msgstr ""
+#~ "ਗਨੋਮ ਸ਼ੈਲ ਇਕਸਟੈਨਸ਼ਨ ਲਈ ਇੱਕ uuid ਵਿਸ਼ੇਸ਼ਤਾ ਹੈ; ਇਹ ਕੁੰਜੀ ਇਕਸਟੈਨਸ਼ਨ ਦਰਸਾਉਂਦੀ ਹੈ, ਜੋ ਲੋਡ ਹਨ। "
+#~ "disabled-extensions ਇਹ ਸੈਟਿੰਗ ਉਹਨਾਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਦੀ ਹੈ, ਜੋ ਦੋਵੇਂ ਲਿਸਟਾਂ ਵਿੱਚ "
+#~ "ਹੁੰਦੀਆਂ ਹਨ।"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "ਤਾਜ਼ਾ ਆਈਟਮਾਂ"
+
+#~ msgid "Show password"
+#~ msgstr "ਪਾਸਵਰਡ ਵੇਖਾਓ"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s ਸ਼ੁਰੂ ਹੋਣਾ ਖਤਮ ਹੋਇਆ"
+
+#~ msgid "If true, display onscreen keyboard."
+#~ msgstr "ਜੇ ਚੋਣ ਕੀਤੀ ਤਾਂ ਆਨਸਕਰੀਨ ਕੀਬੋਰਡ ਵੇਖਾਇਆ ਜਾਵੇਗਾ।"
+
+#~ msgid "Connectivity lost"
+#~ msgstr "ਕੁਨੈਕਸ਼ਨ ਟੁੱਟ ਗਿਆ"
+
+#~| msgid "You're no longer connected to the network"
+#~ msgid "You are no longer connected to the network"
+#~ msgstr "ਹੁਣ ਤੁਸੀਂ ਨੈੱਟਵਰਕ ਨਾਲ ਕੁਨਕੈਟ ਨਹੀਂ ਰਹੇ ਹੋ"
+
+#~ msgid "calendar:week_start:0"
+#~ msgstr "calendar:week_start:1"
+
+#~ msgid "Uuids of extensions to disable"
+#~ msgstr "ਇਕਟੈਨਸ਼ਨ ਦੀ Uuids ਬੰਦ ਹੈ"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "ਹੁਣ ਤੁਸੀਂ ਮੋਬਾਇਲ ਬਰਾਡਬੈਂਡ ਨੈੱਟਵਰਕ '%s' ਨਾਲ ਕੁਨੈਕਟ ਹੋ ਗਏ ਹੋ"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "ਹੁਣ ਤੁਸੀਂ ਬੇਤਾਰ ਨੈੱਟਵਰਕ '%s' ਨਾਲ ਕੁਨੈਕਟ ਹੋ ਗਏ ਹੋ"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "ਤੁਸੀਂ ਹੁਣ VPN ਨੈੱਟਵਰਕ '%s' ਨਾਲ ਕੁਨੈਕਟ ਹੋ ਚੁੱਕੇ ਹੋ"
+
+#~ msgid "Localization Settings"
+#~ msgstr "ਲੋਕਲਾਈਜ਼ੇਸ਼ਨ ਸੈਟਿੰਗ"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਚਿਰ ਪਹਿਲਾਂ"
+
+#~ msgid "Shut Down"
+#~ msgstr "ਬੰਦ ਕਰੋ"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr "ਇਹ ਐਪਲੀਕੇਸ਼ਨ ਬੰਦ ਕਰਕੇ ਸਿਸਟਮ ਨੂੰ ਬੰਦ ਕਰਨ ਲਈ ਬੰਦ ਕਰੋ ਨੂੰ ਕਲਿੱਕ ਕਰੋ।"
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "ਸਿਸਟਮ ਨੂੰ ਆਟੋਮੈਟਿਕ ਹੀ %d ਸਕਿੰਟਾਂ ਵਿੱਚ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ।"
+
+#~ msgid "Shutting down the system."
+#~ msgstr "ਸਿਸਟਮ ਬੰਦ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
+
+#~ msgid "Confirm"
+#~ msgstr "ਪੁਸ਼ਟੀ"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "ਪਸੰਦ"
+
+#~ msgid "Clip the crosshairs at the center"
+#~ msgstr "ਸੈਂਟਰ ਉੱਤੇ ਕਰਾਂਸਹੇਅਰ ਕਲਿੱਪ ਕਰੋ"
+
+#~ msgid "Color of the crosshairs"
+#~ msgstr "ਕਰਾਂਸਹੇਅਰ ਦਾ ਰੰਗ"
+
+#~ msgid ""
+#~ "Determines the length of the vertical and horizontal lines that make up "
+#~ "the crosshairs."
+#~ msgstr "ਵਰਟੀਕਲ ਤੇ ਹਰੀਜੱਟਲ ਲਾਈਨਾਂ ਦੀ ਲੰਬਾਈ ਦੱਸੋ, ਜੋ ਕਿ ਕਰਾਂਸਹੇਅਰ ਬਣਾਉਂਦੀਆਂ ਹਨ।"
+
+#~ msgid ""
+#~ "Determines the transparency of the crosshairs, from fully opaque to fully "
+#~ "transparent."
+#~ msgstr "ਕਰਾਂਸਹੇਅਰ ਦੀ ਟਰਾਂਸਪਰੇਸੀ ਤਹਿ ਕਰੋ, ਪੂਰੀ ਤਰ੍ਹਾਂ ਧੁੰਦਲੇ ਤੋਂ ਪਾਰਦਰਸ਼ੀ ਹੀ।"
+
+#~ msgid "Enable lens mode"
+#~ msgstr "ਲੈਨਜ਼ ਮੋਡ ਚਾਲੂ"
+
+#~ msgid ""
+#~ "Enables/disables display of crosshairs centered on the magnified mouse "
+#~ "sprite."
+#~ msgstr "ਵੱਡਦਰਸ਼ੀ ਮਾਊਸ ਸਪਰਿਟ ਉੱਤੇ ਸੈਂਟਰ ਕੀਤੇ ਕਰਾਂਸਹੇਅਰ ਵੇਖਾਉਣਾ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕਰੋ।"
+
+#~ msgid ""
+#~ "For centered mouse tracking, when the system pointer is at or near the "
+#~ "edge of the screen, the magnified contents continue to scroll such that "
+#~ "the screen edge moves into the magnified view."
+#~ msgstr ""
+#~ "ਸੈਂਟਰਡ ਮਾਊਸ ਟਰੈਕ ਕਰਨ ਲਈ, ਜਦੋਂ ਸਿਸਟਮ ਪੁਆਇੰਟਰ ਸਕਰੀਨ ਦੇ ਕਿਸੇ ਕੋਨੇ ਕੋਲ ਜਾਂਦਾ ਹੈ ਤਾਂ ਵੱਡੇ ਰੂਪ "
+#~ "ਵਿੱਚ ਵੇਖਾਈ ਜਾਂਦੀ ਸਮੱਗਰੀ ਇੰਝ ਸਕਰੋਲ ਕੀਤੀ ਜਾਂਦੀ ਹੈ ਤਾਂ ਕਿ ਸਕਰੀਨ ਕੋਨੇ ਵੱਡਦਰਸ਼ ਝਲਕ ਦੇ ਰੂਪ ਵਿੱਚ "
+#~ "ਵੇਖਾਈ ਜਾਂਦੇ ਰਹਿੰਦੇ ਹਨ।"
+
+#~ msgid "Length of the crosshairs"
+#~ msgstr "ਕਰਾਂਸਹੇਅਰ ਦੀ ਲੰਬਾਈ"
+
+#~ msgid "Magnification factor"
+#~ msgstr "ਵੱਡਦਰਸ਼ੀ ਗੁਣਾਂਕ"
+
+#~ msgid "Mouse Tracking Mode"
+#~ msgstr "ਮਾਊਸ ਟਰੈਕਿੰਗ ਮੋਡ"
+
+#~ msgid "Opacity of the crosshairs"
+#~ msgstr "ਕਰਾਂਸਹੇਅਰ ਦਾ ਧੁੰਦਲਾਪਨ"
+
+#~ msgid "Scroll magnified contents beyond the edges of the desktop"
+#~ msgstr "ਡੈਸਕਟਾਪ ਦੇ ਕੋਨਿਆਂ ਤੋਂ ਪਾਰ ਵੱਡਦਰਸ਼ੀ ਸਮੱਗਰੀ ਸਕਰੋਲ ਕਰੋ"
+
+#~ msgid "Show or hide crosshairs"
+#~ msgstr "ਕਰਾਂਸਹੇਅਰ ਵੇਖੋ ਜਾਂ ਓਹਲੇ ਕਰੋ"
+
+#~ msgid "Show or hide the magnifier"
+#~ msgstr "ਵੱਡਦਰਸ਼ੀ ਵੇਖੋ ਜਾਂ ਓਹਲੇ ਕਰੋ"
+
+#~ msgid "Show or hide the magnifier and all of its zoom regions."
+#~ msgstr "ਵੱਡਦਰਸ਼ੀ ਵੇਖੋ ਜਾਂ ਓਹਲੇ ਕਰੋ ਤੇ ਇਸ ਦੇ ਸਭ ਖੇਤਰਾਂ ਨੂੰ ਜ਼ੂਮ ਕਰੋ।"
+
+#~ msgid ""
+#~ "The color of the the vertical and horizontal lines that make up the "
+#~ "crosshairs."
+#~ msgstr "ਵਰਟੀਕਲ ਤੇ ਹਰੀਜੱਟਲ ਲਾਈਨਾਂ ਦਾ ਰੰਗ, ਜੋ ਕਿ ਕਰਾਂਸਹੇਅਰ ਬਣਾਉਂਦੀਆਂ ਹਨ"
+
+#~ msgid ""
+#~ "The magnified view either fills the entire screen, or occupies the top-"
+#~ "half, bottom-half, left-half, or right-half of the screen."
+#~ msgstr ""
+#~ "ਵੱਡਦਰਸ਼ੀ ਝਲਕ ਪੂਰੀ ਸਕਰੀਨ ਨੂੰ ਭਰ ਸਕਦਾ ਹੈ ਜਾਂ ਅੱਧਾ-ਉੱਤੇ, ਅੱਧਾ ਹੇਠਾਂ, ਅੱਧਾ ਖੱਬੇ ਜਾਂ ਅੱਧਾ-ਸੱਜੇ ਭਾਗ "
+#~ "ਨੂੰ ਭਰ ਸਕਦਾ ਹੈ।"
+
+#~ msgid ""
+#~ "The power of the magnification. A value of 1.0 means no magnification. A "
+#~ "value of 2.0 doubles the size."
+#~ msgstr ""
+#~ "ਵੱਡਦਰਸ਼ੀ ਦੀ ਤਾਕਤ ਹੈ। ੧.੦ ਦਾ ਮਤਲਬ ਹੈ ਕਿ ਕੋਈ ਵੀ ਨਹੀਂ। ੨.੦ ਦਾ ਮਤਲਬ ਹੈ ਆਕਾਰ ਦਾ ਦੋ ਗੁਣਾ।"
+
+#~ msgid "Thickness of the crosshairs"
+#~ msgstr "ਕਰਾਂਸਹੇਅਰ ਦੀ ਮੋਟਾਈ"
+
+#~ msgid ""
+#~ "Whether the magnified view should be centered over the location of the "
+#~ "system mouse and move with it."
+#~ msgstr "ਕੀ ਵੱਡਦਰਸ਼ੀ ਝਲਕ ਦੀ ਸਥਿਤੀ ਸਿਸਟਮ ਮਾਊਂਸ ਦੁਆਲੇ ਕੇਂਦਰਤ ਰਹੇ ਅਤੇ ਉਸ ਨਾਲ ਹੀ ਹਿੱਲੇ।"
+
+#~ msgid ""
+#~ "Width of the vertical and horizontal lines that make up the crosshairs."
+#~ msgstr "ਵਰਟੀਕਲ ਤੇ ਹਰੀਜੱਟਲ ਲਾਈਨਾਂ ਦੀ ਚੌੜਾਈ, ਜੋ ਕਿ ਕਰਾਂਸਹੇਅਰ ਬਣਾਉਂਦੀਆਂ ਹਨ"
+
+#~ msgid "Shut Down..."
+#~ msgstr "ਬੰਦ ਕਰੋ..."
+
+#~ msgid "Bluetooth Agent"
+#~ msgstr "ਬਲਿਊਟੁੱਥ ਏਜੰਟ"
+
+#~ msgid "Search your computer"
+#~ msgstr "ਆਪਣੇ ਕੰਪਿਊਟਰ ਉੱਤੇ ਲੱਭੋ"
+
+#~ msgid ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+#~ msgstr ""
+#~ "ਨਵਾਂ ਵਰਕਸਪੇਸ ਜੋੜਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ, ਕਿਉਂਕਿ ਵਰਕਸਪੇਸਾਂ ਦੀ ਵੱਧੋ-ਵੱਧ ਗਿਣਤੀ ਪੂਰੀ ਹੋ ਚੁੱਕੀ ਹੈ।"
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "ਪਹਿਲਾਂ ਵਰਕਸਪੇਸ ਨਹੀਂ ਹਟਾਇਆ ਜਾ ਸਕਦਾ।"
+
+#~ msgid "Customize the panel clock"
+#~ msgstr "ਪੈਨਲ ਘੜੀ ਕਸਟਮਾਈਜ਼ ਕਰੋ"
+
+#~ msgid "Custom format of the clock"
+#~ msgstr "ਘੜੀ ਲਈ ਪਸੰਦੀਦਾ ਫਾਰਮੈਟ"
+
+#~ msgid "Hour format"
+#~ msgstr "ਘੰਟਾ ਫਾਰਮੈਟ"
+
+#~ msgid ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+#~ msgstr "ਜੇ ਚੋਣ ਕੀਤੀ ਤਾਂ ਸਮੇਂ ਵਿੱਚ \"12-hour\" ਜਾਂ \" 24-hour\" ਸਕਿੰਟ ਵੇਖਾਏ ਜਾਣਗੇ।"
+
+#~ msgid ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+#~ msgstr ""
+#~ "ਇਹ ਕੁੰਜੀ ਪੈਨਲ ਘੜੀ ਲਈ ਵਰਤਣ ਵਾਸਤੇ ਫਾਰਮੈਟ ਦਿੰਦੀ ਹੈ, ਜਦੋਂ ਕਿ ਫਾਰਮੈਟ ਕੁੰਜੀ \"ਕਸਟਮ\" ਸੈੱਟ "
+#~ "ਕੀਤੀ ਹੋਵੇ। ਤੁਸੀਂ ਖਾਸ ਫਾਰਮੈਟ ਦੇਣ ਲਈ ਹਦਾਇਤਾਂ ਵਾਸਤੇ strftime () ਨੂੰ ਵਰਤ ਸਕਦੇ ਹੋ। ਹੋਰ "
+#~ "ਜਾਣਕਾਰੀ ਲਈ strftime () ਦਸਤਾਵੇਜ਼ ਵੇਖੋ।"
+
+#~ msgid ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+#~ msgstr ""
+#~ "ਇਹ ਕੁੰਜੀ ਤਹਿ ਕਰਦੀ ਹੈ ਕਿ ਕਿਹੜਾ ਪੈਨਲ ਘੜੀ ਵਿੱਚ ਘੰਟਾ ਫਾਰਮੈਟ ਵਰਤਿਆ ਜਾਵੇ। ਸੰਭਵ ਮੁੱਲ ਹਨ "
+#~ "\"12-hour\"(੧੨-ਘੰਟੇ), \"24-hour\" (੨੪-ਘੰਟੇ), \"unix\" (ਯੂਨੈਕਸ) ਅਤੇ \"custom"
+#~ "\" (ਪਸੰਦੀਦਾ)। ਜੇ \"ਯੂਨੈਕਸ\" ਸੈੱਟ ਕੀਤਾ ਗਿਆ ਤਾਂ ਘੜੀ ਨੂੰ ਈਪੋਚ (੦੧-ਜਨਵਰੀ-੧੯੭੦) ਤੋਂ ਵੇਖਾਇਆ\n"
+#~ "ਜਾਵੇਗਾ। ਜੇ \"ਪਸੰਦੀਦਾ\" ਲਈ ਸੈੱਟ ਕੀਤਾ ਗਿਆ ਤਾਂ, ਘੜੀ ਨੂੰ custom_format ਕੁੰਜੀ ਵਿੱਚ ਦਿੱਤੇ "
+#~ "ਫਾਰਮੈਟ ਮੁਤਾਬਕ ਵੇਖਾਇਆ ਜਾਵੇਗਾ। ਯਾਦ ਰੱਖੋ ਕਿ ਜੇ \"ਯੂਨੈਕਸ\" ਜਾਂ \"ਪਸੰਦੀਦਾ\" ਸੈੱਟ ਕੀਤਾ ਤਾਂ "
+#~ "ਅੱਪਗਰੇਡ ਮਿਤੀ ਵੇਖਾਓ ਤੇ ਸਕਿੰਟ ਵੇਖਾਓ ਨੂੰ ਅਣਡਿੱਠਾ ਕੀਤਾ ਜਾਵੇਗਾ।"
+
+#~ msgid "Clock Preferences"
+#~ msgstr "ਘੜੀ ਪਸੰਦ"
+
+#~ msgid "Show seco_nds"
+#~ msgstr "ਸਕਿੰਟ ਵੇਖੋ(_n)"
+
+#~ msgid "_12 hour format"
+#~ msgstr "_੧੨ ਘੰਟੇ ਫਾਰਮੈਟ"
+
+#~ msgid "_24 hour format"
+#~ msgstr "_੨੪ ਘੰਟੇ ਫਾਰਮੈਟ"
+
+#~ msgid "Preferences"
+#~ msgstr "ਮੇਰੀ ਪਸੰਦ"
+
+#~ msgid "Overview workspace view mode"
+#~ msgstr "ਵਰਕਸਪੇਸ ਝਲਕ ਮੋਡ ਦੀ ਝਲਕ"
+
+#~ msgid ""
+#~ "The selected workspace view mode in the overview. Supported values are "
+#~ "\"single\" and \"grid\"."
+#~ msgstr ""
+#~ "ਸੰਖੇਪ ਵਿੱਚ ਚੁਣੇ ਵਰਕਸਪੇਸ ਝਲਕ ਮੋਡ ਹੈ। ਸਹਾਇਕ ਮੁੱਲ ਹਨ \"single\"(ਇੱਕਲਾ) ਜਾਂ \"grid"
+#~ "\" (ਗਰਿੱਡ)।"
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "ਪਸੰਦ ਵਿੱਚ ਜੋੜਨ ਲਈ ਇੱਥੇ ਸੁੱਟੋ"
+
+#~ msgid "Find"
+#~ msgstr "ਖੋਜ"
+
+#~| msgid "Preferences"
+#~ msgid "System Preferences..."
+#~ msgstr "ਸਿਸਟਮ ਪਸੰਦ..."
+
+#~ msgid "Sidebar"
+#~ msgstr "ਬਾਹੀ"
+
+#~ msgid "Recent Documents"
+#~ msgstr "ਤਾਜ਼ਾ ਡੌਕੂਮੈਂਟ"
+
+#~ msgid "(see all)"
+#~ msgstr "(ਸਭ ਵੇਖੋ)"
+
+#~ msgid "PLACES"
+#~ msgstr "ਥਾਵਾਂ"
+
+#~ msgid "SEARCH RESULTS"
+#~ msgstr "ਖੋਜ ਨਤੀਜੇ"
+
+#~ msgid "Can't lock screen: %s"
+#~ msgstr "ਸਕਰੀਨ ਲਾਕ ਨਹੀਂ ਹੋ ਸਕਦੀ: %s"
+
+#~ msgid "Can't temporarily set screensaver to blank screen: %s"
+#~ msgstr "ਸਕਰੀਨ ਬੰਦ ਕਰਨ ਲਈ ਆਰਜ਼ੀ ਰੂਪ ਵਿੱਚ ਸਕਰੀਨ-ਸੇਵਰ ਨਹੀਂ ਸੈੱਟ ਕੀਤਾ ਜਾ ਸਕਦਾ: %s"
+
+#~ msgid "Can't logout: %s"
+#~ msgstr "ਲਾਗਆਉਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ: %s"
+
+#~ msgid "Browse"
+#~ msgstr "ਝਲਕ"
diff --git a/po/pl.po b/po/pl.po
new file mode 100644
index 0000000..ca2754b
--- /dev/null
+++ b/po/pl.po
@@ -0,0 +1,2993 @@
+# Polish translation for gnome-shell.
+# Copyright © 2009-2023 the gnome-shell authors.
+# This file is distributed under the same license as the gnome-shell package.
+# Piotr Drąg <piotrdrag@gmail.com>, 2009-2023.
+# Tomasz Dominikowski <dominikowski@gmail.com>, 2009.
+# Wojciech Szczęsny <wszczesny@aviary.pl>, 2013.
+# Aviary.pl <community-poland@mozilla.org>, 2009-2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 05:16+0000\n"
+"PO-Revision-Date: 2023-02-18 16:18+0100\n"
+"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
+"Language-Team: Polish <community-poland@mozilla.org>\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Uruchamianie"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktywacja 1. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktywacja 2. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktywacja 3. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktywacja 4. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktywacja 5. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktywacja 6. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktywacja 7. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktywacja 8. ulubionego programu"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktywacja 9. ulubionego programu"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Zrzuty ekranu"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Interaktywne wykonanie zrzutu ekranu"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Wykonanie zrzutu ekranu"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Wykonanie zrzutu ekranu okna"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Interaktywne nagranie ekranu"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "System"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Wyświetlenie listy powiadomień"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Aktywacja bieżącego powiadomienia"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Wyświetlenie ekranu podglądu"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Wyświetlenie wszystkich programów"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Otwarcie menu programu"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Powłoka środowiska GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Zarządzanie oknami i uruchamianiem programów"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Włącza wewnętrzne narzędzia przydatne programistom i testerom w oknie "
+"dialogowym Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Umożliwia dostęp do wewnętrznych narzędzi debugowania i monitorowania, "
+"używając okna dialogowego Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Lista UUID rozszerzeń do włączenia"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Rozszerzenia powłoki GNOME mają własność UUID; ten klucz zawiera "
+"rozszerzenia, które mają zostać wczytane. Każde rozszerzenie, które ma "
+"zostać wczytane, musi znajdować się na tej liście. Można także manipulować "
+"tą listą za pomocą metod EnableExtension i DisableExtension usługi D-Bus na "
+"org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Lista UUID rozszerzeń do wymuszenia wyłączenia"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Rozszerzenia powłoki GNOME mają własność UUID; ten klucz zawiera "
+"rozszerzenia, które mają zostać wyłączone, nawet jeśli zostały wczytane jako "
+"część obecnego trybu. Każde rozszerzenie, które ma zostać wczytane, musi "
+"znajdować się na tej liście. Można także manipulować tą listą za pomocą "
+"metod EnableExtension i DisableExtension usługi D-Bus na org.gnome.Shell. "
+"Ten klucz jest przetwarzany przed ustawieniem „enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Wyłączenie rozszerzeń użytkownika"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Wyłączenie wszystkich rozszerzeń włączonych przez użytkownika bez zmiany "
+"ustawienia „enabled-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Wyłącza sprawdzanie zgodności wersji rozszerzeń"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Powłoka GNOME będzie wczytywała tylko rozszerzenia, które obsługują obecnie "
+"uruchomioną wersję. Włączenie tej opcji wyłączy sprawdzanie zgodności "
+"i spowoduje próbę wczytania wszystkich rozszerzeń, niezależnie od wersji, "
+"jakie obsługują."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lista identyfikatorów plików .desktop ulubionych programów"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programy odpowiadające tym identyfikatorom będą wyświetlane w obszarze "
+"ulubionych."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historia okna dialogowego poleceń (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historia okna dialogowego „Looking Glass”"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Wyświetlanie elementu menu „Wyloguj się” w menu użytkownika."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ten klucz zastępuje automatyczne ukrywanie elementu menu „Wyloguj się” "
+"w sytuacji, gdy istnieje tylko jeden użytkownik i jedna sesja."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Określa, czy pamiętać hasła do montowania zaszyfrowanych lub zdalnych "
+"systemów plików"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Powłoka poprosi o hasło podczas montowania zaszyfrowanego urządzenia lub "
+"zdalnego systemu plików. Jeśli hasło może zostać zapisane do późniejszego "
+"użycia, to obecne będzie pole wyboru „Zapamiętanie hasła”. Ten klucz ustawia "
+"domyślną wartość tego pola."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Ostatnio wybrany niedomyślny profil zasilania"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Niektóre komputery obsługują więcej niż dwa profile zasilania. Aby nadal "
+"obsługiwać przełączanie między dwoma profilami, ten klucz zachowuje ostatnio "
+"wybrany niedomyślny profil."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Ostatnia wersja, dla której wyświetlono okno „Witamy w GNOME”"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Ten klucz określa, dla której wersji ostatnio wyświetlono okno „Witamy "
+"w GNOME”. Pusty ciąg oznacza najstarszą możliwą wersję, a wielka liczba "
+"będzie oznaczać wersje, które jeszcze nie istnieją. Ta wielka liczba może "
+"być używana do wyłączenia tego okna."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Układ ekranu wyboru programu"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Układ ekranu wyboru programu. Każdy wpis w macierzy to strona. Strony są "
+"przechowywane w kolejności, w jakiej są wyświetlane w powłoce GNOME. Każda "
+"strona zawiera parę „identyfikator programu” → „data”. Obecnie następujące "
+"wartości są przechowywane jako „data”: • „position”: położenie ikony "
+"programu na stronie"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Skrót do otwarcia menu programu"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Skrót do otwarcia menu programu."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Skrót do przełączenia między stanami ekranu podglądu"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Skrót do przełączenia między sesją, ekranem wyboru okna i siatką programów"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Skrót do przełączenia między siatką programów, ekranem wyboru okna i sesją"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Skrót do otwarcia widoku „Wyświetl programy”"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Skrót do otwarcia widoku „Wyświetl programy” ekranu podglądu."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Skrót do otwarcia ekranu podglądu"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Skrót do otwarcia ekranu podglądu."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Skrót do przełączenia widoczności listy powiadomień"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Skrót do przełączenia widoczności listy powiadomień."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Skrót do aktywacji bieżącego powiadomienia"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Skrót do aktywacji bieżącego powiadomienia."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Przełączenie na 1. program"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Przełączenie na 2. program"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Przełączenie na 3. program"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Przełączenie na 4. program"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Przełączenie na 5. program"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Przełączenie na 6. program"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Przełączenie na 7. program"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Przełączenie na 8. program"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Przełączenie na 9. program"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Ograniczenie przełącznika do bieżącego obszaru roboczego."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Jeśli wynosi wartość „true”, to tylko programy mające okna na bieżącym "
+"obszarze roboczym są wyświetlane w przełączniku. W przeciwnym przypadku "
+"wszystkie programy są załączone."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Tryb ikon programów."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Konfiguruje, jak wyświetlać okna w przełączniku. Prawidłowe wartości to "
+"„thumbnail-only” (wyświetla miniaturę okna), „app-icon-only” (wyświetla "
+"tylko ikonę programu) lub „both” (wyświetla oba)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Jeśli wynosi wartość „true”, to tylko okna z bieżącego obszaru roboczego są "
+"wyświetlane w przełączniku. W przeciwnym przypadku wszystkie okna są "
+"załączone."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Położenia"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Położenia wyświetlane w zegarze światowym"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatyczne położenie"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Czy ustalać obecne położenie"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Położenie"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Położenie, dla którego wyświetlać prognozę pogody"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Dołączanie modalnych okien dialogowych do okien nadrzędnych"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ten klucz zastępuje klucz w „org.gnome.mutter”, kiedy uruchomiona jest "
+"powłoka GNOME."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Włączenie kafelkowania przy krawędziach podczas przenoszenia okien do "
+"krawędzi ekranu"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Dynamiczne zarządzanie obszarami roboczymi"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Obszary robocze tylko na pierwszym monitorze"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Opóźnienie zmiany aktywności w trybie myszy do momentu, w którym kursor się "
+"zatrzymuje"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Logowanie do sieci"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Coś się nie powiodło"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Wystąpił problem: nie można wyświetlić ustawień tego rozszerzenia. Zalecamy "
+"zgłoszenie problemu autorom rozszerzenia."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Informacje techniczne"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Strona WWW"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Otwiera stronę WWW rozszerzenia"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Anuluj"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Hasło"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Wybór sesji"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Inny użytkownik?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(np. użytkownik lub %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nazwa użytkownika"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Okno logowania"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Błąd uwierzytelniania"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(lub przeciągnięcie palca przez czytnik)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(lub umieszczenie palca na czytniku)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Wyłącz komputer"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+"wyłącz komputer;wyłączanie komputera;zakończ;power off;shutdown;halt;stop;"
+"zatrzymaj"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Uruchom ponownie"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "uruchom ponownie;restartuj;zrestartuj;reboot;restart;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Zablokuj ekran"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr ""
+"zablokuj ekran;zablokowanie ekranu;blokowanie ekranu;blokada ekranu;lock "
+"screen"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Wyloguj się"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "wyloguj się;wylogowanie;logout;log out;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Uśpij"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "uśpij;uśpienie;wstrzymaj;wstrzymanie;suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Przełącz użytkownika"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "przełącz użytkownika;przełączanie użytkowników;switch user"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"zablokuj orientację;zablokowanie orientacji;blokowanie orientacji;blokada "
+"orientacji;lock orientation;odblokuj orientację;odblokowanie orientacji;"
+"unlock orientation;ekran;monitor;wyświetlacz;screen;obrót;obracanie;rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Wykonaj zrzut ekranu"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"zrzut ekranu;screenshot;skrin;nagranie ekranu;screencast;nagrywanie;snip;"
+"capture;record"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Odblokuj orientację ekranu"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Zablokuj orientację ekranu"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Nie odnaleziono polecenia"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Nie można przetworzyć polecenia:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Wykonanie polecenia „%s” się nie powiodło:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Przed chwilą"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minuta temu"
+msgstr[1] "%d minuty temu"
+msgstr[2] "%d minut temu"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d godzina temu"
+msgstr[1] "%d godziny temu"
+msgstr[2] "%d godzin temu"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Wczoraj"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dzień temu"
+msgstr[1] "%d dni temu"
+msgstr[2] "%d dni temu"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d tydzień temu"
+msgstr[1] "%d tygodnie temu"
+msgstr[2] "%d tygodni temu"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d miesiąc temu"
+msgstr[1] "%d miesiące temu"
+msgstr[2] "%d miesięcy temu"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d rok temu"
+msgstr[1] "%d lata temu"
+msgstr[2] "%d lat temu"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Wczoraj o %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%-l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Wczoraj o %-l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %-l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %-l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %-l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Logowanie do hotspotu"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Połączenie z tym hotspotem nie jest bezpieczne. Hasła i inne informacje "
+"wpisywane na tej stronie mogą być widoczne dla osób w pobliżu."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Odmów dostępu"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Udziel dostępu"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Katalog bez nazwy"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "Przypięto program „%s” do ulubionych."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Odpięto program „%s” z ulubionych."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Otwarte okna"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nowe okno"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Wyświetl szczegóły"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Zakończ"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Odepnij"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Przypnij do ulubionych"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Uruchom za pomocą zintegrowanej karty graficznej"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Uruchom za pomocą dedykowanej karty graficznej"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Wybór urządzenia dźwiękowego"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Ustawienia dźwięku"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Słuchawki"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Słuchawki z mikrofonem"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Zmień tło…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Ustawienia ekranu"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Ustawienia"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "N"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "W"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ś"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "C"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Poprzedni miesiąc"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Następny miesiąc"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V. tydzień"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Brak powiadomień"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Nie przeszkadzać"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Wyczyść"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "Okno „%s” nie odpowiada."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "Można poczekać chwilę dłużej lub wymusić zakończenie programu."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Zakończ"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Czekaj"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Podłączono dysk zewnętrzny"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Odłączono dysk zewnętrzny"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Nie można odblokować woluminu"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Zainstalowana wersja usługi udisks nie obsługuje ustawienia PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Otwórz za pomocą „%s”"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Można także połączyć naciskając przycisk „WPS” na routerze."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Połącz"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Klucz"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Hasło klucza prywatnego"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Tożsamość"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Usługa"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Wymagane jest uwierzytelnienie"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Do uzyskania dostępu do sieci bezprzewodowej „%s” wymagane jest hasło lub "
+"klucze szyfrowania."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Uwierzytelnianie przewodowe 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nazwa sieci"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Uwierzytelnienie DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Wymagany jest kod PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Do korzystania z urządzenia komórkowego wymagane jest podanie kodu PIN"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "Kod PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Do połączenia z siecią „%s” wymagane jest hasło."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Menedżer sieci"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Hasło sieci VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Wymagane jest uwierzytelnienie"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Uwierzytelnij"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "To nie zadziałało. Proszę spróbować ponownie."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Użytkownik „%s” jest teraz znany jako „%s”"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Okna"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Wyświetl programy"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Ulubione"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %-d %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Dzisiaj"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Jutro"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Cały dzień"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%-d.%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Brak wydarzeń"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Dodaj zegar światowy…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Zegar światowy"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Wczytywanie…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Prognoza pogody wymaga połączenia z Internetem"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Prognoza pogody jest obecnie niedostępna"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Pogoda"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Wybierz położenie pogody…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Wylogowanie użytkownika %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Wylogowanie"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Użytkownik %s zostanie wylogowany za %d sekundę."
+msgstr[1] "Użytkownik %s zostanie wylogowany za %d sekundy."
+msgstr[2] "Użytkownik %s zostanie wylogowany za %d sekund."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Wylogowanie nastąpi za %d sekundę."
+msgstr[1] "Wylogowanie nastąpi za %d sekundy."
+msgstr[2] "Wylogowanie nastąpi za %d sekund."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Wyloguj się"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Wyłączenie komputera"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalacja aktualizacji i wyłączenie komputera"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Wyłączenie komputera nastąpi za %d sekundę."
+msgstr[1] "Wyłączenie komputera nastąpi za %d sekundy."
+msgstr[2] "Wyłączenie komputera nastąpi za %d sekund."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalacja oczekujących aktualizacji oprogramowania"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Wyłącz komputer"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Ponowne uruchomienie"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalacja aktualizacji i ponowne uruchomienie"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Ponowne uruchomienie komputera nastąpi za %d sekundę."
+msgstr[1] "Ponowne uruchomienie komputera nastąpi za %d sekundy."
+msgstr[2] "Ponowne uruchomienie komputera nastąpi za %d sekund."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Uruchom ponownie"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Ponowne uruchomienie i instalacja aktualizacji"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Ponowne uruchomienie komputera i instalacja aktualizacji nastąpi za "
+"%d sekundę."
+msgstr[1] ""
+"Ponowne uruchomienie komputera i instalacja aktualizacji nastąpi za "
+"%d sekundy."
+msgstr[2] ""
+"Ponowne uruchomienie komputera i instalacja aktualizacji nastąpi za "
+"%d sekund."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Uruchom ponownie i zainstaluj"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Zainstaluj i wyłącz komputer"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Wyłączenie komputera po zainstalowaniu aktualizacji"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Ponowne uruchomienie i instalacja aktualizacji"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"System %s %s zostanie zainstalowany po ponownym uruchomieniu. Instalacja "
+"aktualizacji może zająć dużo czasu. Proszę się upewnić, że wykonano kopię "
+"zapasową danych i podłączono komputer do prądu."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Niski poziom naładowania akumulatora: proszę podłączyć komputer do prądu "
+"przed instalowaniem aktualizacji."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Niektóre programy są używane lub mają niezapisane dane"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Inni użytkownicy są zalogowani"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opcje uruchamiania"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (zdalnie)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Zainstaluj"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalacja rozszerzenia"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr ""
+"Pobrać i zainstalować rozszerzenie „%s” z witryny extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Dostępne są aktualizacje rozszerzeń"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Aktualizacje rozszerzeń są gotowe do zainstalowania."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Zezwolenie na wyłączenie skrótów"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Program „%s” prosi o wyłączenie skrótów"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Program prosi o wyłączenie skrótów"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Można je włączyć naciskając klawisze %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Odmów"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Zezwól"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Włączono powolne klawisze"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Wyłączono powolne klawisze"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Przytrzymano klawisz Shift przez osiem sekund. Jest to skrót klawiszowy dla "
+"funkcji powolnych klawiszy, która wpływa na sposób pracy klawiatury."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Włączono trwałe klawisze"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Wyłączono trwałe klawisze"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Przyciśnięto klawisz Shift pięć razy z rzędu. Jest to skrót klawiszowy dla "
+"funkcji trwałych klawiszy, która wpływa na sposób pracy klawiatury."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Przyciśnięto dwa klawisze naraz lub przyciśnięto klawisz Shift pięć razy "
+"z rzędu. Wyłącza to funkcję trwałych klawiszy, która wpływa na sposób pracy "
+"klawiatury."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Pozostaw włączone"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Włącz"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Wyłącz"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Pozostaw wyłączone"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Ustawienia regionu i języka"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nie zainstalowano rozszerzeń"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "Rozszerzenie „%s” nie wysłało żadnych błędów."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Ukryj błędy"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Wyświetl błędy"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Włączone"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Wyłączone"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Błąd"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Nieaktualne"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Pobieranie"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Wyświetl źródło"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Strona WWW"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Komputer został przełączony w tryb niezabezpieczony"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Programy mają teraz nieograniczony dostęp"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Cofnij"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Zalogowano jako uprawniony użytkownik"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Należy unikać działania jako uprawniony użytkownik z powodów bezpieczeństwa. "
+"Jeśli to możliwe, należy zalogować się jako zwykły użytkownik."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Blokada ekranu jest wyłączona"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Blokowanie ekranu wymaga menedżera wyświetlania GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informacje systemowe"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Nieznany wykonawca"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Nieznany tytuł"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Wyszukiwanie"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Programy"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Podgląd"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nowy skrót…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Określone przez program"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Ekran pomocy"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Przełączenie monitora"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Przydzielenie klawiszy"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Gotowe"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Modyfikuj…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Brak"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Naciśnięcie przycisku skonfiguruje"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Klawisz Esc zakończy"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Naciśnięcie dowolnego przycisku zakończy"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Podgląd"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Menu systemowe"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Górny pasek"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Proszę wprowadzić polecenie:"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Klawisz Esc zamknie"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Ponowne uruchamianie jest niedostępne w technologii Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Ponowne uruchamianie…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Środowisko GNOME musi zablokować ekran"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Nie można zablokować"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Blokowanie zostało zablokowane przez program"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Zaznaczenie"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Wybór obszaru"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Ekran"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Wybór ekranu"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Okno"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Wybór okna"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Zrzut/nagranie ekranu"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Widoczny kursor"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Nagrania ekranu"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Nagranie ekranu z %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Zrzut ekranu"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Nagrano ekran"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Kliknięcie tutaj wyświetli film."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Wyświetl w Menedżerze plików"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Zrzut ekranu z %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Wykonano zrzut ekranu"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Można wkleić obraz ze schowka."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Wykonano zrzut ekranu"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Wyszukiwanie…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Brak wyników."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d więcej"
+msgstr[1] "%d więcej"
+msgstr[2] "%d więcej"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Wyszukiwanie"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Skopiuj"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Wklej"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Wyświetl tekst"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ukryj tekst"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Klawisz Caps Lock jest włączony."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Ukryty wolumin"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Systemowy wolumin Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Używa plików kluczy"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Do odblokowania woluminu używającego plików kluczy należy użyć narzędzia <i>"
+"%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Numer PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Zapamiętanie hasła"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Odblokuj"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Otwórz program %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM musi być numerem lub być pusty."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Nie można uruchomić programu %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nie można odnaleźć programu %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Ułatwienia dostępu"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Wysoki kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Powiększanie"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Czytnik ekranowy"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Klawiatura ekranowa"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Alarmy wizualne"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Trwałe klawisze"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Powolne klawisze"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Odskakujące klawisze"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Klawisze myszy"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Duży tekst"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatyczne obracanie"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Jasność"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Tryb ciemny"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Pojedyncze kliknięcie"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Podwójne kliknięcie"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Przeciągnięcie"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Kliknięcie pomocnicze"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Kliknięcie spoczynkowe"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Klawiatura"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Wyświetl układ klawiatury"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Zezwolenie na dostęp do położenia"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Program „%s” prosi o dostęp do położenia użytkownika"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"W każdej chwili można zmienić dostęp do położenia w ustawieniach prywatności."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<nieznane>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Rozłącz urządzenie %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Połącz z urządzeniem %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Hotspot %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "Ustawienia sieci VPN"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi-Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Wszystkie sieci"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Połączenia przewodowe"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Ustawienia sieci przewodowej"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Połączenia Bluetooth"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Ustawienia Bluetooth"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Połączenia komórkowe"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Ustawienia sieci komórkowej"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Połączenie się nie powiodło"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Włączenie połączenia sieciowego się nie powiodło"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Nocne światło"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Wydajność"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Zrównoważone zasilanie"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Oszczędzanie energii"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Profile zasilania"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Zatrzymaj nagrywanie ekranu"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Zatrzymaj udostępnianie ekranu"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Tryb samolotowy"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d%%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Wykonaj zrzut ekranu"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menu wyłączania komputera"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Uśpij"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Uruchom ponownie…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Wyłącz komputer…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Wyloguj się…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Przełącz użytkownika…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Zablokuj ekran"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Nieznane urządzenie Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Podczas nieobecności wykryto nowe urządzenie. Proszę je rozłączyć "
+"i podłączyć ponownie, aby zacząć je używać."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Nieupoważnione urządzenie Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Wykryto nowe urządzenie, które musi zostać upoważnione przez administratora."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Błąd upoważnienia Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nie można upoważnić urządzenia Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Zmieniono głośność"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Głośność"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Wyjście dźwięku"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Wejście dźwięku"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Ten sam obraz"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Połączone ekrany"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Tylko zewnętrzny"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Tylko wbudowany"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Przeciągnięcie w górę odblokuje"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Kliknięcie lub naciśnięcie klawisza odblokuje"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Okno odblokowania"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Zaloguj jako inny użytkownik"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Witamy w GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+"Przewodnik po środowisku pomoże zacząć pracę i poznać niezbędne funkcje."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nie, dziękuję"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Rozpocznij przewodnik"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Okno „%s” jest gotowe"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Zachować te ustawienia?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Przywróć ustawienia"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Zachowaj zmiany"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Zmienione ustawienia zostaną przywrócone za %d sekundę"
+msgstr[1] "Zmienione ustawienia zostaną przywrócone za %d sekundy"
+msgstr[2] "Zmienione ustawienia zostaną przywrócone za %d sekund"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d×%d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Ukryj"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Przywróć"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Zmaksymalizuj"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Przenieś"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Zmień rozmiar"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Przenieś pasek tytułowy na ekran"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Zawsze na wierzchu"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Zawsze na widocznym obszarze roboczym"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Przenieś do lewego obszaru"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Przenieś do prawego obszaru"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Przenieś do górnego obszaru"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Przenieś do dolnego obszaru"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Przenieś na górny monitor"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Przenieś na dolny monitor"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Przenieś na lewy monitor"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Przenieś na prawy monitor"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Zamknij"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Kalendarz programu Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Wyświetla wersję"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Tryb używany przez GDM dla ekranu logowania"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Używa podanego trybu, np. „gdm” dla ekranu logowania"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Wyświetla listę możliwych trybów"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Nieznany"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Uruchomienie „%s” się nie powiodło"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Hasła się nie zgadzają."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Hasło nie może być puste"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Okno uwierzytelniania zostało odrzucone przez użytkownika"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Rozszerzenia"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Zarządzanie rozszerzeniami GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Projekt GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Ten program obsługuje aktualizowanie, konfigurowanie preferencji oraz "
+"usuwanie i wyłączanie niechcianych rozszerzeń."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfiguracja rozszerzeń powłoki GNOME"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Brak wyników"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Usunąć rozszerzenie „%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Po usunięciu rozszerzenia jego ponowne włączenie będzie wymagało pobrania od "
+"nowa"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Usuń"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Piotr Drąg <piotrdrag@gmail.com>, 2009-2022\n"
+"Tomasz Dominikowski <dominikowski@gmail.com>, 2009\n"
+"Wojciech Szczęsny <wszczesny@aviary.pl>, 2013\n"
+"Aviary.pl <community-poland@mozilla.org>, 2009-2022"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d rozszerzenie zostanie zaktualizowane po następnym zalogowaniu."
+msgstr[1] "%d rozszerzenia zostaną zaktualizowane po następnym zalogowaniu."
+msgstr[2] "%d rozszerzeń zostanie zaktualizowanych po następnym zalogowaniu."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Rozszerzenie jest niezgodne z obecną wersją środowiska GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Rozszerzenie ma błąd"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Można zaktualizować rozszerzenie"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Witryna"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Usuń…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Pomoc"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "O programie"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Rozszerzenia mogą powodować problemy z wydajnością i stabilnością. "
+"W przypadku występowania problemów z komputerem należy je wyłączyć."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Ręcznie zainstalowane"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Witryna <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a> "
+"umożliwia wyszukiwanie i dodawanie rozszerzeń."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Wbudowane"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Brak zainstalowanych rozszerzeń"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Nie można wyświetlić listy zainstalowanych rozszerzeń. Proszę się upewnić, "
+"że zalogowano w środowisku GNOME i spróbować ponownie."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Aktualizacje rozszerzeń są gotowe"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Pomyślnie utworzono nowe rozszerzenie w %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Nazwa jest być bardzo krótkim (najlepiej opisowym) napisem.\n"
+"Przykłady: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nazwa"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Opis jest jednozdaniowym wyjaśnieniem działania rozszerzenia.\n"
+"Przykłady: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Opis"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID to globalnie unikalny identyfikator rozszerzenia.\n"
+"Musi być w formacie adresu e-mail (kliknięcie-aktywuje@alicja.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Proszę wybrać jeden z dostępnych szablonów:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Szablon"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Unikalny identyfikator nowego rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAZWA"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Widoczna dla użytkownika nazwa nowego rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "OPIS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Krótki opis działania rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "SZABLON"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Szablon do użycia dla nowego rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Interaktywnie wprowadza informacje o rozszerzeniu"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Tworzy nowe rozszerzenie"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Nieznane parametry"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, nazwa i opis są wymagane"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Rozszerzenie „%s” nie istnieje\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Wyłącza rozszerzenie"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nie podano UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Podano więcej niż jeden UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Włącza rozszerzenie"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Połączenie z powłoką GNOME się nie powiodło\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Rozszerzenie „%s” nie istnieje\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Wyświetla informacje o rozszerzeniach"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Zastępuje istniejące rozszerzenie"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PAKIET_ROZSZERZENIA"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instaluje pakiet rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nie podano pakietu rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Podano więcej niż jeden pakiet rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Wyświetla rozszerzenia zainstalowane przez użytkownika"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Wyświetla rozszerzenia zainstalowane w systemie"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Wyświetla włączone rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Wyświetla wyłączone rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Wyświetla rozszerzenia z preferencjami"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Wyświetla rozszerzenia z aktualizacjami"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Wyświetla informacje o rozszerzeniu"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Wyświetla listę zainstalowanych rozszerzeń"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "PLIK"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Dodatkowe źródło do dołączenia do pakietu"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMAT"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Schemat GSettings, który ma zostać dołączony"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "KATALOG"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Katalog z tłumaczeniami"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMENA"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Domena gettext używana dla tłumaczeń"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Zastępuje istniejący pakiet"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Katalog, w którym utworzyć pakiet"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "KATALOG_ŹRÓDŁOWY"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Tworzy pakiet rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Podano więcej niż jeden katalog źródłowy"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Rozszerzenie „%s” nie ma preferencji\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Otwarcie preferencji rozszerzenia „%s” się nie powiodło: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Otwiera preferencje rozszerzenia"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Przywraca rozszerzenie"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Nie można odinstalować rozszerzeń systemowych\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Odinstalowanie „%s” się nie powiodło\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Odinstalowuje rozszerzenie"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Bez wyświetlania komunikatów o błędach"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Ścieżka"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "Adres URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Oryginalny autor"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Wersja"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stan"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "Opcja „version” nie przyjmuje parametrów"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Użycie:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Wyświetla informacje o wersji i kończy działanie."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "POLECENIE"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[PARAMETRY…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Polecenia:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Wyświetla pomoc"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Włącza rozszerzenie"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Wyłącza rozszerzenie"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Przywraca rozszerzenie"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Odinstalowuje rozszerzenie"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Wyświetla listę rozszerzeń"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Wyświetla informacje o rozszerzeniu"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Otwiera preferencje rozszerzenia"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Tworzy rozszerzenie"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Tworzy pakiet z rozszerzeniem"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instaluje pakiet rozszerzenia"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Polecenie „%s” wyświetli szczegółową pomoc.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Zwykłe"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Puste rozszerzenie"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Wskaźnik"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Dodaje ikonę do górnego paska"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u wyjście"
+msgstr[1] "%u wyjścia"
+msgstr[2] "%u wyjść"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u wejście"
+msgstr[1] "%u wejścia"
+msgstr[2] "%u wejść"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Dźwięki systemowe"
diff --git a/po/pt.po b/po/pt.po
new file mode 100644
index 0000000..a28fd47
--- /dev/null
+++ b/po/pt.po
@@ -0,0 +1,3776 @@
+# Portuguese translation for gnome-shell.
+# Copyright © 2010-2023 gnome-shell.
+# This file is distributed under the same license as the gnome-shell package.
+# Duarte Loreto <happyguy_pt@hotmail.com>, 2010, 2011, 2012, 2013, 2014.
+# Rui Gouveia <rui.gouveia@gmail.com>, 2011.
+# António Lima <amrlima@gmail.com>, 2013.
+# Tiago Santos <tiagofsantos81@sapo.pt>, 2014 - 2016.
+# Bruno Ramalhete <bram.512@gmail.com>, 2015.
+# Pedro Albuquerque <palbuquerque73@gmail.com>, 2014, 2015.
+# Sérgio Cardeira <cardeira dot sergio at gmail dot com>, 2016.
+# Juliano de Souza Camargo <julianosc@protonmail.com>, 2020.
+# José Vieira <jvieira33@sapo.pt>, 2020-2021.
+# Hugo Carvalho <hugokarvalho@hotmail.com>, 2020, 2021, 2022, 2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 3.14\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 07:30+0000\n"
+"PO-Revision-Date: 2023-02-14 10:42+0000\n"
+"Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
+"Language-Team: Português <https://l10n.gnome.org/teams/pt/>\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-Project-Style: gnome\n"
+"X-Language: pt_PT\n"
+"X-Source-Language: C\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Lançadores"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Ativar a aplicação favorita 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Ativar a aplicação favorita 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Ativar a aplicação favorita 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Ativar a aplicação favorita 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Ativar a aplicação favorita 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Ativar a aplicação favorita 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Ativar a aplicação favorita 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Ativar a aplicação favorita 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Ativar a aplicação favorita 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Capturas de ecrã"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Tirar uma captura de ecrã de forma interativa"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Tirar uma captura de ecrã"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Tirar uma captura de ecrã de uma janela"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Gravar o ecrã de forma interativa"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostrar a lista de notificação"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Foco na notificação ativa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostrar a vista geral"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostrar todas as aplicações"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Abrir o menu de aplicações"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Interface GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gestão de janelas e iniciação de aplicações"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Usar Alt-F2 para ativar ferramentas internas úteis para programadores e "
+"testadores"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permite o acesso a ferramentas internas de depuração e monitorização usando "
+"o diálogo aberto com Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs das extensões a ativar"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"As extensões da interface GNOME têm uma propriedade UUID; esta chave lista "
+"extensões que deverão ser carregadas. Qualquer extensão que deseje ser "
+"carregada tem de estar nesta lista. Pode também manipular esta lista com os "
+"métodos D-Bus EnableExtension e DisableExtension de org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs das extensões a forçar desativação"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"As extensões da interface GNOME têm uma propriedade UUID; esta chave lista "
+"as extensões que deverão ser desativadas, mesmo que carregadas como parte do "
+"modo atual. Pode também manipular esta lista com os métodos D-Bus "
+"EnableExtension e DisableExtension em org.gnome.Shell. Esta chave tem "
+"precedência sobre a definição \"extensões-ativadas\"."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Desativar extensões de utilizador"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Desativar todas as extensões que o utilizador ativou, sem afetar a definição "
+"\"extensões-ativadas\"."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Desativa a validação de compatibilidade de versão da extensão"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"A interface GNOME só vai carregar extensões que indiquem suportar a versão "
+"atual. Ativar esta opção irá desativar esta verificação e tentará carregar "
+"todas as extensões, independentemente das versões que elas indiquem suportar."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Lista de IDs de ficheiros .desktop (de ambiente de trabalho) das aplicações "
+"favoritas"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"As aplicações correspondentes a estes identificadores serão mostradas na "
+"área de favoritas."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Histórico do diálogo de comando (aberto com Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Histórico do diálogo de pesquisa"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Mostrar sempre 'Sair' no menu de utilizador."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Esta chave ignora a ocultação automática do 'Sair' em situações de "
+"utilizador-único, sessão-única."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Recordar ou não a palavra-passe para montar sistemas de ficheiros "
+"encriptados ou remotos"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"A interface pede uma palavra-passe quando um dispositivo encriptado ou "
+"sistema de ficheiros remoto é montado. Se a palavra-passe puder ser guardada "
+"para utilização futura, estará disponível uma caixa de seleção 'Recordar "
+"palavra-passe'. Esta chave define o estado predefinido dessa caixa de "
+"seleção."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "O último perfil de energia não-padrão selecionado"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Alguns sistemas suportam mais de dois perfis de energia. A fim de continuar "
+"a suportar a alternância entre dois perfis, esta chave regista o último "
+"perfil não-padrão selecionado."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "A última versão da janela \"Bem-vindo ao GNOME\" foi mostrada para"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Esta chave determina para qual versão a janela \"Bem-vindo ao GNOME\" foi "
+"mostrada pela última vez. Uma linha vazia representa a versão mais antiga "
+"possível, e um número enorme representará versões que ainda não existem. "
+"Este número enorme pode ser usado para efetivamente desativar a janela."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Esquema do seletor de aplicações"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Esquema do seletor de aplicações. Cada entrada na matriz é uma página. As "
+"páginas são armazenadas na ordem em que aparecem na Interface GNOME. Cada "
+"página contém um par “id da aplicação” → “dados”. Atualmente, os seguintes "
+"valores são armazenados como “dados”: • “posição”: a posição do ícone da "
+"aplicação na página"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Tecla de atalho para abrir o menu de aplicações"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Tecla de atalho para abrir o menu de aplicações."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Tecla de atalho para mudar de estado de vista geral"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Tecla de atalho para mudar de sessão, seletor de janela e grelha de aplicação"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Tecla de atalho para mudar a grelha de aplicação, seletor de janela e sessão"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Tecla de atalho para abrir a vista \"Mostrar aplicações\""
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Tecla de atalho para abrir a vista \"Mostrar aplicações\" da Vista geral de "
+"atividades."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Tecla de atalho para abrir a vista geral"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Tecla de atalho para abrir a Vista geral de atividades."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Tecla de atalho para mostrar/ocultar a lista de notificação"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Tecla de atalho para alternar a visibilidade da lista de notificação."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Tecla de atalho para focar a notificação ativa"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Tecla de atalho para focar a notificação ativa."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Mudar para a aplicação 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Mudar para a aplicação 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Mudar para a aplicação 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Mudar para a aplicação 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Mudar para a aplicação 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Mudar para a aplicação 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Mudar para a aplicação 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Mudar para a aplicação 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Mudar para a aplicação 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Limitar a comutação à área de trabalho atual."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Se verdadeiro, só as aplicações com janelas na área de trabalho atual são "
+"mostradas para troca. Senão, são incluídas todas as aplicações com janelas "
+"abertas."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "O modo do ícone da aplicação."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configura a exibição das janelas. As opções válidas são \"apenas-"
+"miniatura\" (mostra uma miniatura da janela), \"apenas-ícone-"
+"aplicação\" (mostra só o ícone da aplicação) ou \"ambas\"."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Se verdadeiro, só janelas da área de trabalho atual são apresentadas para "
+"troca. Senão, são incluídas todas as aplicações com janelas abertas."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Localizações"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "As localizações a mostrar nos relógios mundiais"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Localização automática"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Se deve ser obtida a localização atual ou não"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Localização"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "A localização para a qual deve ser mostrada uma previsão"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Anexar diálogo modal à janela principal"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Esta chave ignora a chave em org.gnome.mutter ao executar a interface GNOME."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Ativar prender nas margens ao largar janelas junto às margens do ecrã"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "As áreas de trabalho são geridas dinamicamente"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Áreas de trabalho só no monitor principal"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Atrasar alterações de foco no modo de rato até que o ponteiro pare de se "
+"mover"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Acesso pela rede"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Alguma coisa correu mal"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Houve um problema: não é possível mostrar as configurações desta extensão. "
+"Recomendamos que relate o problema aos autores da extensão."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Informação técnica"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Sítio web"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visitar o sítio web da extensão"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Palavra-passe"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Escolher sessão"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Não está listado?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(por ex., utilizador ou %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Utilizador"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Janela de acesso"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Erro de autenticação"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(ou passe o dedo pelo leitor)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(ou coloque o dedo no leitor)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Desligar"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "desligar;encerrar;parar"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reiniciar;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bloquear ecrã"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "bloquear ecrã"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Sair"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "terminar sessão;sair"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspender"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspender"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Mudar de utilizador"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "mudar de utilizador"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "bloquear orientação;desbloquear orientação;ecrã;rotação"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Capturar ecrã"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "screenshot;screencast;capturar;gravar;ecrã;cortar"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Desbloquear rotação de ecrã"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Bloquear rotação de ecrã"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Comando não encontrado"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Impossível analisar o comando:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Falha ao executar “%s”:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Agora mesmo"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "há %d minuto atrás"
+msgstr[1] "há %d minutos atrás"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "há %d hora atrás"
+msgstr[1] "há %d horas atrás"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Ontem"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "há %d dia atrás"
+msgstr[1] "há %d dias atrás"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "há %d semana atrás"
+msgstr[1] "há %d semanas atrás"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "há %d mês atrás"
+msgstr[1] "há %d meses atrás"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "há %d ano atrás"
+msgstr[1] "há %d anos atrás"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ontem às %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d de %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d de %B de %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ontem às %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d de %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d de %B de %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Acesso por ponto wi-fi público"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"A ligação a este ponto de acesso não é segura. As palavras-passe e outras "
+"informações que inserir nesta página podem ser vistas por pessoas próximas."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Negar acesso"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Conceder acesso"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Pasta sem nome"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s foi afixado ao Painel."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s foi desafixado do Painel."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Janelas abertas"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nova janela"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Mostrar detalhes"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Sair"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Desafixar"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Afixar ao Painel"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Iniciar usando a placa de vídeo integrada"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Iniciar usando a placa de vídeo dedicada"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Selecione o dispositivo de áudio"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Definições de som"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Auscultadores"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Auscultadores com microfone"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Microfone"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Alterar o fundo…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Definições de ecrã"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Definições"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Q"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Q"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB de %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Mês anterior"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Mês seguinte"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Semana %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Sem notificações"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Não incomodar"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Limpar"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” não está a responder."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Pode escolher entre esperar que a aplicação retome a execução ou forçar a "
+"aplicação a encerrar totalmente."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Forçar encerrar"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Esperar"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Dispositivo externo ligado"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Dispositivo externo desligado"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Impossível desbloquear o volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "A versão do udisks instalada não suporta a configuração do PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Abrir com %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternativamente, pode ligar premindo o botão “WPS” no encaminhador (router)."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Ligar"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Chave"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Palavra-passe da chave privada"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identidade"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Serviço"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autenticação necessária"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"São necessárias palavras-passe ou chaves de encriptação para aceder à rede "
+"sem fios “%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autenticação para 802.1X por cabo"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nome da rede"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autenticação DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Necessário código PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "É necessário o código PIN para o dispositivo de banda larga móvel"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "É necessária uma palavra-passe para se ligar a “%s”."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Gestor de rede"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Palavra-passe da VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Necessária autenticação"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrador"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autenticar"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Não funcionou. Tente novamente."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s é agora conhecido como %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Janelas"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Mostrar aplicações"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Traço"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d de %B de %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e de %B de %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d de %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d de %B de %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Hoje"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Amanhã"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Dia completo"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Sem eventos"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Adicionar relógios do mundo…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Relógios do mundo"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "A carregar…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Procurar informação meteorológica na rede"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Presentemente a informação meteorológica está indisponível"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Meteorologia"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Selecione uma localização meteorológica…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Sair de %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Sair"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "A sessão de %s terminará automaticamente dentro de %d segundo."
+msgstr[1] "A sessão de %s terminará automaticamente dentro de %d segundos."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "A sua sessão terminará automaticamente dentro de %d segundo."
+msgstr[1] "A sua sessão terminará automaticamente dentro de %d segundos."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Sair"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Desligar"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalar atualizações e desligar"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "O sistema desligará automaticamente dentro de %d segundo."
+msgstr[1] "O sistema desligará automaticamente dentro de %d segundos."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalar atualizações de software pendentes"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Desligar"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalar atualizações e Reiniciar"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "O sistema reiniciará automaticamente dentro de %d segundo."
+msgstr[1] "O sistema reiniciará automaticamente dentro de %d segundos."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reiniciar e instalar atualizações"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"O sistema reiniciará automaticamente e instalará atualizações dentro de %d "
+"segundo."
+msgstr[1] ""
+"O sistema reiniciará automaticamente e instalará atualizações dentro de %d "
+"segundos."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reiniciar &amp; Instalar"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalar &amp; Desligar"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Desligar depois das atualizações serem instaladas"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reiniciar e instalar novas versões"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s será instalado após reiniciar. Atualizar a instalação pode demorar "
+"muito tempo: assegure-se de que criou cópias de segurança e de que o "
+"computador está ligado à corrente."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Bateria descarregada: ligar à corrente antes de instalar atualizações."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Algumas aplicações estão ocupadas ou têm trabalho não guardado"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Há outros utilizadores em sessão"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opções de arranque"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remoto)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instalar"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalar extensão"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Transferir e instalar \"%s\" a partir de extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Há atualizações de extensões disponíveis"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "As atualizações de extensões estão prontas para serem instaladas."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Permitir inibição de atalhos"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "A aplicação %s pretende inibir atalhos"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Uma aplicação pretende inibir atalhos"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Pode restaurar os atalhos premindo %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Negar"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Permitir"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Teclas lentas ligadas"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Teclas lentas desligadas"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Manteve a tecla Shift pressionada durante 8 segundos. Este é o atalho para "
+"ativar a funcionalidade Teclas lentas, que afeta a forma como o teclado "
+"opera."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Teclas aderentes ligadas"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Teclas aderentes desligadas"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Premiu a tecla Shift 5 vezes seguidas. Este é o atalho para a funcionalidade "
+"Teclas aderentes, que afeta a forma como o teclado opera."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Premiu duas teclas de uma vez ou premiu a tecla Shift 5 vezes seguidas. Isto "
+"desliga a funcionalidade Teclas aderentes, que afeta a forma como o teclado "
+"opera."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Deixar ativado"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Ligar"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Desligar"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Deixar desativado"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Definições de Região e Idioma"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nenhuma extensão instalada"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s não emitiu qualquer erro."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Ocultar erros"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Mostrar erros"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Ativo"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Inativo"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Erro"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Data inválida"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "A transferir"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Ver fonte"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Página Web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "O sistema foi colocado em modo inseguro"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "As aplicações têm agora acesso sem restrições"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Desfazer"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Em sessão como utilizador privilegiado"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Executar uma sessão como utilizador privilegiado deve ser evitado por "
+"motivos de segurança. Se possível, aceder como utilizador normal."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Bloqueio de ecrã desligado"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "O bloqueio de ecrã requer o gestor de exibição do GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informação do sistema"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artista desconhecido"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Titulo desconhecido"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Escreva para pesquisar"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Aplicações"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Vista geral"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Novo atalho…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplicação definida"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Mostrar ajuda no ecrã"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Mudar de monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Atribuir atalho"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Concluído"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Editar…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nenhum"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Premir um botão para configurar"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Premir Esc para sair"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Premir qualquer tecla para sair"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Atividades"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Executar um comando"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Premir Esc para fechar"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "O reinício não está disponível no Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "A reiniciar…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "O GNOME precisa de bloquear o ecrã"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Impossível bloquear"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Uma aplicação impediu o bloqueio"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Seleção"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Seleção da área"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Ecrã"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Seleção do ecrã"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Janela"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Seleção de janelas"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Captura de ecrã / Gravação de ecrã"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Mostrar cursor"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Gravações de ecrã"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Gravação de ecrã a partir de %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Captura de ecrã"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Gravação de ecrã efetuada"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Clicar aqui para ver o vídeo."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Mostrar em Ficheiros"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Captura de ecrã de %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Captura de ecrã efetuada"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Pode colar a imagem a partir da área de transferência."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Captura de ecrã tirada"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "A procurar…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Nenhum resultado."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d mais"
+msgstr[1] "%d mais"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Procurar"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copiar"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Colar"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Mostrar texto"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ocultar texto"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Maiúsculas ligadas."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volume oculto"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volume de sistema Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Usa ficheiros de chaves"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Para desbloquear um volume que utilize ficheiros de chave, use o utilitário "
+"<i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Número PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Recordar a palavra-passe"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desbloquear"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Abrir %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "O PIM deve ser um número ou espaço em branco."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Incapaz de iniciar %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Não foi possível localizar a aplicação %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Acessibilidade"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Alto contraste"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Ampliação"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Leitor de ecrã"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Teclado no ecrã"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Alertas visuais"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Teclas aderentes"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Teclas lentas"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Teclas tolerantes"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Teclas de rato"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Texto grande"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Rotação automática"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Brilho"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Modo escuro"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clique único"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Clique duplo"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Arrastar"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clique secundário"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clique permanente"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Teclado"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Mostrar a disposição de teclado"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Permitir acesso a localização"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "A aplicação %s pretende usar a localização"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"O acesso à localização pode ser alterado a qualquer altura a partir das "
+"definições de privacidade."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<desconhecido>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Desligar %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Ligar a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Ponto de acesso %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "Definições de VPN"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Todas as redes"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Ligações por cabo de rede"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Definições de ligação por cabo"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Tethers Bluetooth"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Definições do Bluetooth"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Ligações móveis"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Definições da banda larga móvel"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Falha na ligação"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Falha na ativação da ligação à rede"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Luz noturna"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Desempenho"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Equilibrado"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Poupança de energia"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Perfis de energia"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Parar a gravação de ecrã"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Parar a partilha de ecrã"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Modo de avião"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d%%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Capturar ecrã"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menu de Desligar"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspender"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Reiniciar…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Desligar…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Terminar sessão…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Mudar de utilizador…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Bloquear ecrã"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Dispositivo Thunderbolt desconhecido"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Um novo dispositivo foi detetado enquanto estava ausente. Desligue e volte a "
+"ligar o dispositivo para começar a usá-lo."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispositivo Thunderbolt não autorizado"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Um novo dispositivo foi detetado e precisa ser autorizado por um "
+"administrador."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Erro de autorização do Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Não foi possível autorizar o dispositivo Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Volume alterado"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volume"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Saída de som"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Entrada de som"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Espelho (mirror)"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Juntar ecrãs"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Apenas externo"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Apenas interno"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %d de %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Deslizar para desbloquear"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Clicar ou premir uma tecla para desbloquear"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Desbloquear janela"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Iniciar sessão como outro utilizador"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Bem-vindo ao GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Se quiser familiarizar-se, percorra a visita guiada."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Dispensar"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Faça uma visita guiada"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” está pronto"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Manter estas definições de ecrã?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Reverter definições"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Manter alterações"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "As alterações às definições serão revertidas em %d segundo"
+msgstr[1] "As alterações às definições serão revertidas em %d segundos"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Ocultar"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restaurar"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximizar"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Mover"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Mover barra de título no ecrã"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Sempre no topo"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Sempre na área de trabalho visível"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Mover para a área de trabalho à esquerda"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Mover para a área de trabalho à direita"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Mover para a área de trabalho acima"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Mover para a área de trabalho abaixo"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Mover para o monitor acima"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Mover para o monitor abaixo"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Mover para o monitor à esquerda"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Mover para o monitor à direita"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Fechar"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendário do Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Mostrar versão"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Modo utilizado pelo GDM para o ecrã de início de sessão"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Usar um modo específico, por ex. \"gdm\" para o ecrã de início de sessão"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Listar modos possíveis"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Desconhecida"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Falha ao iniciar “%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "As palavras-passe não coincidem."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "A palavra-passe não pode ser espaço vazio"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "O diálogo de autenticação foi fechado pelo utilizador"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensões"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Gerir as extensões do GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Projeto GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"O Extensões do GNOME lida com a atualização da extensões, configuração das "
+"preferências de extensões e remoção ou desativação de extensões já não "
+"desejadas."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configurar o Extensões à interface GNOME"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Sem correpondências"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Remover “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Se remover a extensão, tem que voltar a descarregá-la para a ativar novamente"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Remover"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Duarte Loreto <happyguy_pt@hotmail.com>\n"
+"Rui Gouveia <rui.gouveia@gmail.com>\n"
+"António Lima <amrlima@gmail.com>\n"
+"Tiago Santos <tiagofsantos81@sapo.pt>\n"
+"Bruno Ramalhete <bram.512@gmail.com>\n"
+"Pedro Albuquerque <palbuquerque73@gmail.com>\n"
+"Sérgio Cardeira <cardeira.sergio@gmail.com>\n"
+"José Vieira <jvieira33@sapo.pt>\n"
+"Juliano de Souza Camargo <julianosc@pm.me>\n"
+"Hugo Carvalho <hugokarvalho@hotmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d extensão será atualizada na próxima sessão."
+msgstr[1] "%d extensões serão atualizadas na próxima sessão."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "A extensão é incompatível com a versão atual do GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "A extensão apresentou um erro"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "A extensão pode ser atualizada"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Sítio web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Remover…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Ajuda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Acerca do Extensões"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"As extensões podem causar problemas de desempenho e estabilidade. Desativar "
+"as extensões se encontrar problemas com o sistema."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Instalada manualmente"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Para encontrar e adicionar extensões, visite <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Interna"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Não há extensões instaladas"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Não foi possível obter a lista de extensões instaladas. Certifique-se de que "
+"está numa sessão do GNOME e tente novamente."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Atualizações de extensões prontas"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "A nova extensão foi criada com sucesso em %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"O nome deve ser uma linha muito curta (idealmente descritiva).\n"
+"Exemplos: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nome"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"A descrição é uma explicação numa só frase do que a extensão faz.\n"
+"Exemplos: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descrição"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"O UUID é um identificador global único para a extensão.\n"
+"Deve estar no formato de endereço de correio eletrónico "
+"(cliqueparafoco@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Escolher um dos modelos disponíveis:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Modelo"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "O identificador único para a nova extensão"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "O nome da nova extensão visível para o utilizador"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIÇÃO"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Uma descrição curta do que a extensão faz"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODELO"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "O modelo a usar para a nova extensão"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Introduzir as informações da extensão interativamente"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Criar uma nova extensão"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argumentos desconhecidos"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "São necessários o UUID, o nome e a descrição"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "A extensão “%s” não existe\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Desativar uma extensão"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nenhum UUID fornecido"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Fornecido mais do que um UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Ativar uma extensão"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Ligar à Interface GNOME falhou\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "A extensão “%s” não existe\n"
+
+# ver o contexto
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostrar a informação das extensões"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Sobrescrever uma extensão existente"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PACOTE_DE_EXTENSÕES"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instalar um pacote de extensões"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nenhum pacote de extensões especificado"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Especificado mais do que um pacote de extensões"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Mostrar extensões instaladas pelo utilizador"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostrar extensões instaladas pelo sistema"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostrar extensões ativadas"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostrar extensões desativadas"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostrar extensões com preferências"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostrar extensões com atualizações"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Mostrar informação da extensão"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Listar extensões instaladas"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FICHEIRO"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Origem adicional a incluir no pacote"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ESQUEMA"
+
+# GSettings é GNOME Settings?
+# ver se o nome foi traduzido
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "O esquema de GSettings que deve ser incluído"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRETÓRIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "O diretório onde estão as traduções"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMÍNIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "O domínio gettext a usar nas traduções"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Sobrescrever um pacote existente"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Diretório no qual o pacote deve ser criado"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIRETÓRIO_ORIGEM"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Criar um pacote de extensões"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Especificado mais do que um diretório origem"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "A extensão “%s” não tem preferências\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Falha ao abrir prefs para a extensão \"%s\": %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Abre as preferências da extensão"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Repor uma extensão"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Não é possível desinstalar as extensões do sistema\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "A desinstalação de “%s” falhou\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Desinstalar uma extensão"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Não mostrar mensagens de erro"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Caminho"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autor original"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versão"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Estado"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” não aceita argumentos"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Utilização:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Mostrar a versão e sair."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMANDO"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Comandos:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Mostrar ajuda"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Ativar extensão"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Desativar extensão"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Restabelecer extensão"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Desinstalar extensão"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Listar extensões"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Mostrar a informação da extensão"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Mostrar as preferências das extensões"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Criar extensão"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Empacotar extensão"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instalar pacote da extensão"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Usar “%s” para obter ajuda detalhada.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Em branco"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Uma extensão vazia"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicador"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Adicionar um ícone à barra superior"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u saída"
+msgstr[1] "%u saídas"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entradas"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Sons de sistema"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Se o adaptador padrão Bluetooth tinha dispositivos configurados associados"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "A interface só vai mostrar um item de menu de Bluetooth se estiver ligado "
+#~ "algum adaptador de Bluetooth ou se houver dispositivos configurados "
+#~ "associados ao adaptador predefinido. Isto será revertido se o adaptador "
+#~ "predefinido deixar de ter dispositivos associados."
+
+#~ msgid "Log Out"
+#~ msgstr "Sair"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s desligado"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s ligado"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s não geridos"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s a ligar"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s requer autenticação"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Firmware em falta para %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s indisponível"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Falha na ligação %s"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Equipamento %s desativado"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s desativado"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Ligar à Internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Modo avião está ligado"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "O Wi-Fi é desativado quando o modo de avião é ligado."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Desligar modo avião"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi está desligado"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "O Wi-Fi tem de ser ligado para se poder ligar a uma rede."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Ligar o Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Redes Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Selecionar uma rede"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Usar o interruptor físico para desligar"
+
+#~ msgid "Select Network"
+#~ msgstr "Selecionar rede"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Definições de Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s não ligado"
+
+#~ msgid "unknown"
+#~ msgstr "desconhecido"
+
+#~ msgid "activating…"
+#~ msgstr "a ativar…"
+
+#~ msgid "deactivating…"
+#~ msgstr "a desativar…"
+
+#~ msgid "deactivated"
+#~ msgstr "desativado"
+
+#~ msgid "connecting…"
+#~ msgstr "a ligar…"
+
+#~ msgid "authentication required"
+#~ msgstr "autenticação necessária"
+
+#~ msgid "connection failed"
+#~ msgstr "falha ao ligar"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN desligada"
+
+#~ msgid "Network Settings"
+#~ msgstr "Definições de rede"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s ligação Wi-Fi"
+#~ msgstr[1] "%s ligações Wi-Fi"
+
+#~ msgid "Power Settings"
+#~ msgstr "Definições de energia"
+
+#~ msgid "Lock"
+#~ msgstr "Bloquear"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Desligar / Sair"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d ligado"
+#~ msgstr[1] "%d ligados"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth ligado"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth desligado"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Luz noturna desligada"
+
+#~ msgid "Resume"
+#~ msgstr "Retomar"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Desligar até amanhã"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Totalmente carregado"
+
+#~ msgid "Not Charging"
+#~ msgstr "Não está a carregar"
+
+#~ msgid "Estimating…"
+#~ msgstr "A estimar…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d restantes (%d%%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d até estar cheio (%d%%)"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Localização ativada"
+
+#~ msgid "Disable"
+#~ msgstr "Desativar"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Definições de privacidade"
+
+#~ msgid "Location In Use"
+#~ msgstr "Localização em uso"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Localização desativada"
+
+#~ msgid "Enable"
+#~ msgstr "Ativar"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "O ecrã está a ser partilhado"
+
+#~ msgid "Turn off"
+#~ msgstr "Desligar"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "Mostrar a interface da captura de ecrã"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Gravação de ecrã em curso"
+
+#~ msgid "Stop"
+#~ msgstr "Parar"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Aviso"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "As extensões podem causar problemas no sistema, incluindo problemas de "
+#~ "desempenho. Se o sistema apresentar problemas, é recomendável desativar "
+#~ "todas as extensões."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Remover dos favoritos"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Adicionar aos favoritos"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Ativar API de introspecção"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Ativa uma API de D-Bus que permite a introspeção do estado de aplicação "
+#~ "da \"Shell\"."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Ligar à Interface GNOME falhou"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizar"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Desmaximizar"
+
+#~ msgid "App Picker View"
+#~ msgstr "Vista de escolha de aplicações"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Índice da vista atualmente selecionada na escolha de aplicações."
+
+#~| msgid "Unable to connect to %s"
+#~ msgid "Failed to connect to GNOME Shell \n"
+#~ msgstr "Ligar à Interface GNOME falhou \n"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Atalho de teclado que pausa e retoma todos os tweens em execução, para "
+#~ "depuração de erros"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Que teclado utilizar"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "O tipo de teclado a utilizar."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Ocorreu um erro ao ler o diálogo de preferências de %s:"
+
+#~ msgid "Next"
+#~ msgstr "Seguinte"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Iniciar sessão"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Autenticação Web redirecionada"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "As aplicações frequentemente utilizadas aparecem aqui"
+
+#~ msgid "Frequent"
+#~ msgstr "Frequentes"
+
+#~ msgid "All"
+#~ msgstr "Todas"
+
+#~ msgid "Events"
+#~ msgstr "Eventos"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %d, %Y"
+#~ msgstr "%A, %B %d, %Y"
+
+#~ msgid "Notifications"
+#~ msgstr "Notificações"
+
+#~ msgid "Password:"
+#~ msgstr "Senha:"
+
+#~ msgid "Type again:"
+#~ msgstr "Insira novamente:"
+
+#~ msgid "Password: "
+#~ msgstr "Senha: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "A rede sem fios requer autenticação"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Senha da rede de banda larga móvel"
+
+#~ msgid "Hide tray"
+#~ msgstr "Ocultar tabuleiro"
+
+#~ msgid "Status Icons"
+#~ msgstr "Ícones de estado"
+
+#~ msgid "Media"
+#~ msgstr "Média"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nova mensagem"
+#~ msgstr[1] "%d novas mensagens"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d nova notificação"
+#~ msgstr[1] "%d novas notificações"
+
+#~ msgid "Off"
+#~ msgstr "Desligado"
+
+#~ msgid "Not In Use"
+#~ msgstr "Não em uso"
+
+#~ msgid "Account Settings"
+#~ msgstr "Definições de conta"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Orientação de bloqueio"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Mostrar o número da semana no calendário"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Se verdadeiro, mostra o número ISO da semana no calendário."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Utilizar como ligação à Internet"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s está a pedir acesso à sua localização."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Interface GNOME (compositor wayland)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d Dispositivo ligado"
+#~ msgstr[1] "%d Dispositivos ligados"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Bateria"
+
+#~ msgid "On"
+#~ msgstr "Ligado"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Apresentar a zona de notificações"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Dom"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Seg"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Ter"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Qua"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Qui"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Sex"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Sáb"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Nada Agendado"
+
+#~ msgid "This week"
+#~ msgstr "Esta semana"
+
+#~ msgid "Next week"
+#~ msgstr "Próxima semana"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Dispositivos Removíveis"
+
+#~ msgid "Eject"
+#~ msgstr "Ejetar"
+
+#~ msgid "Invitation"
+#~ msgstr "Convite"
+
+#~ msgid "Call"
+#~ msgstr "Chamada"
+
+#~ msgid "File Transfer"
+#~ msgstr "Transferência de Ficheiro"
+
+#~ msgid "Chat"
+#~ msgstr "Diálogo"
+
+#~ msgid "Unmute"
+#~ msgstr "Ativar Som"
+
+#~ msgid "Mute"
+#~ msgstr "Silenciar"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Convite para %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s está a convidá-lo para se juntar a %s"
+
+#~ msgid "Decline"
+#~ msgstr "Recusar"
+
+#~ msgid "Accept"
+#~ msgstr "Aceitar"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Chamada vídeo de %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Chamada de %s"
+
+#~ msgid "Answer"
+#~ msgstr "Atender"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s está a enviar-lhe %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s deseja a sua autorização para ver quando está ligado"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Falha na autenticação"
+
+#~ msgid "Encryption error"
+#~ msgstr "Erro de encriptação"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Não foi disponibilizado o certificado"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Certificado não é de confiança"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certificado expirou"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certificado não está ativo"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Nome de máquina incorreto no certificado"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Impressão digital incorreta no certificado"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificado assinado pelo próprio"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Estado definido como desligado"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Certificado inválido"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "A ligação foi recusada"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Incapaz de estabelecer a ligação"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "A ligação foi perdida"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Esta conta já se encontra ligada ao servidor"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "A ligação foi substituída por uma nova ligação utilizando o mesmo recurso"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "A conta já existe no servidor"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "O servidor está de momento demasiado ocupado para gerir a ligação"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Certificado foi revogado"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Certificado tem um algoritmo de cifra inseguro ou é criptologicamente "
+#~ "fraco"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "O comprimento do certificado do servidor ou a profundidade da hierarquia "
+#~ "do certificado do servidor excede os limites impostos pela biblioteca "
+#~ "criptográfica"
+
+#~ msgid "Internal error"
+#~ msgstr "Erro interno"
+
+#~ msgid "View account"
+#~ msgstr "Visualizar a conta"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Abrir o Calendário"
+
+#~ msgid "Open"
+#~ msgstr "Abrir"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Limpar Mensagens"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Definições de Notificação"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Menu de Bandeja"
+
+#~ msgid "No Messages"
+#~ msgstr "Nenhuma Mensagem"
+
+#~ msgid "Message Tray"
+#~ msgstr "Zona de Notificações"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Portal Cativo"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "O nível máximo de precisão de localização."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Configura o nível máximo de precisão de localização que é permitido às "
+#~ "aplicações ver. Opções válidas são 'off' (desativa o rastreamento de "
+#~ "posição), 'country' (país), 'city' (cidade), 'neighborhood' (bairro), "
+#~ "'street' (rua) e 'exact' (exata) (por norma requer um receptor GPS). Note "
+#~ "que isto apenas controla o que o GeoClue irá permitir às aplicações ver e "
+#~ "elas poderão encontrar a localização do utilizador por si, utilizando "
+#~ "recursos de rede (com uma precisão máxima de nível de rua)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Organização dos botões na barra de título"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Esta chave sobrepõe a chave em org.gnome.desktop.wm.preferences ao "
+#~ "executar o GNOME Shell."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Selecione uma extensão a configurar utilizando a caixa de seleção acima."
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendário:MY"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Lista de categorias que deveriam ser apresentadas como pastas"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Cada nome de categoria nesta lista será representada como uma pasta na "
+#~ "vista de aplicações, em vez de ser apresentada embutira na vista "
+#~ "principal."
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> de <b>%B</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b> de <b>%B</b> de <b>%Y</b>, <b>%H:%M</b>"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Pedido de autorização de %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "O dispositivo %s deseja emparelhar com este computador"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "O dispositivo %s deseja aceder ao serviço '%s'"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Conceder apenas desta vez"
+
+#~ msgid "Reject"
+#~ msgstr "Rejeitar"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Confirmação de emparelhamento para %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "Por favor, confirme se a chave '%06d' corresponde à do dispositivo."
+
+#~ msgid "Does not match"
+#~ msgstr "Não coincide"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Pedido de emparelhamento de %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Introduza o PIN indicado no dispositivo."
+
+#~ msgid "OK"
+#~ msgstr "OK"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Hoje não há nenhuma pérola de sabedoria:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s, o Oráculo, diz"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Se recolher ou não estatísticas sobre a utilização das aplicações"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "O interface normalmente monitoriza aplicações ativas de forma a "
+#~ "apresentar as mais utilizadas (por ex: nos iniciadores). Apesar destes "
+#~ "dados permanecerem privados, poderá desejar desativar a funcionalidade "
+#~ "por questões de privacidade. Note que ao fazê-lo não irá remover os dados "
+#~ "até à data gravados."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Utilizado internamente para armazenar a última presença de Mensagens "
+#~ "Instantâneas explicitamente definida pelo utilizador. Este valor consta "
+#~ "da enumeração TpConnectionPresenceType."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Utilizado internamente para armazenar o último estado de presença de "
+#~ "sessão do utilizador. Este valor consta da enumeração GsmPresenceStatus."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Atalho de teclado para alternar o gravador de ecrã"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Atalho de teclado para alternar o gravador de ecrã."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Taxa de imagens utilizada para a gravação das transmissões de ecrã."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "A taxa de imagens da transmissão de ecrã resultante gravada através do "
+#~ "gravador de transmissões de ecrã do Interface GNOME, em imagens por "
+#~ "segundo."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "O canal gstreamer utilizado para codificar a transmissão de ecrã"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Define o canal GStreamer utilizado para codificar as gravações. Segue a "
+#~ "sintaxe utilizada para o gst-launch. O canal deverá ter um ponto de saída "
+#~ "desligado onde o vídeo gravado é gravado. Terá normalmente um ponto de "
+#~ "entrada desligado; o resultado desse ponto será escrito no ficheiro de "
+#~ "saída. No entanto, o canal poderá tratar da sua própria saída - tal "
+#~ "poderá ser utilizado para enviar o resultado para um servidor icecast "
+#~ "através do shout2send ou semelhante. Quando não definido ou definido com "
+#~ "um valor vazio, será utilizado o canal por omissão. Este é atualmente "
+#~ "'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+#~ "threads=%T ! queue ! webmmux' e grava para WEBM utilizando o codec VP8. "
+#~ "%T é utilizado como uma variável para se tentar inferir o número ótimo de "
+#~ "threads no sistema."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Extensão de ficheiro utilizado para armazenar a transmissão de ecrã"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "O nome do ficheiro da transmissão de ecrã gravada será um nome único "
+#~ "baseado na data atual e utilizará esta extensão. Deverá ser alterada "
+#~ "quando se gravar para um formato de conteúdo diferente."
diff --git a/po/pt_BR.po b/po/pt_BR.po
new file mode 100644
index 0000000..f1ce210
--- /dev/null
+++ b/po/pt_BR.po
@@ -0,0 +1,3643 @@
+# Portuguese translations for gnome-shell package.
+# Copyright (C) 2023 THE gnome-shell'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Og Maciel <ogmaciel@gnome.org>, 2009.
+# Rodrigo Flores <mail@rodrigoflores.org>, 2009.
+# Felipe Borges <felipe10borges@gmail.com>, 2010.
+# Henrique P. Machado <hpmachado@gnome.org>, 2010, 2011.
+# Jonh Wendell <wendell@bani.com.br>, 2010.
+# Rodrigo Padula <contato@rodrigopadula.com>, 2011.
+# Gabriel F. Vilar <cogumm@gmail.com>, 2011.
+# Adorilson Bezerra <adorilson@gmail.com>, 2011, 2014.
+# Djavan Fagundes <djavan@comum.org>, 2012.
+# Juan Diego Martins da Costa Cruz <juan.martins@ifrn.edu.br>, 2013.
+# Fábio Nogueira <fnogueira@gnome.org>, 2014.
+# Georges Basile Stavracas Neto <georges.stavracas@gmail.com>, 2014.
+# Felipe Braga <fbobraga@gmail.com>, 2015.
+# Artur de Aquino Morais <artur.morais93@outlook.com>, 2016.
+# Ricardo Silva Veloso <ricvelozo@gmail.com>, 2019.
+# Bruno Lopes <brunolopesdsilv@gmail.com>, 2018, 2021.
+# Enrico Nicoletto <hiko@duck.com>, 2013-2019, 2021-2022.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2013-2022.
+# Matheus Barbosa <mdpb.matheus@gmail.com>, 2022.
+# Leônidas Araújo <leorusvellt@hotmail.com>, 2022-2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-04-25 08:25+0000\n"
+"PO-Revision-Date: 2023-04-12 10:58-0300\n"
+"Last-Translator: Leônidas Araújo <leorusvellt@hotmail.com>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"X-Generator: Gtranslator 42.0\n"
+"X-Project-Style: gnome\n"
+"X-DL-Team: pt_BR\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: Translating\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Lançadores"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Ativar aplicativo favorito 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Ativar aplicativo favorito 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Ativar aplicativo favorito 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Ativar aplicativo favorito 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Ativar aplicativo favorito 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Ativar aplicativo favorito 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Ativar aplicativo favorito 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Ativar aplicativo favorito 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Ativar aplicativo favorito 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2076
+msgid "Screenshots"
+msgstr "Capturas de tela"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Fazer uma captura de tela interativamente"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Fazer uma captura de tela"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Fazer uma captura de tela de uma janela"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Fazer uma gravação de tela interativamente"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistema"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Mostrar a lista de notificação"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Focar na notificação ativa"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Mostrar o panorama"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Mostrar todos os aplicativos"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Abrir o menu do aplicativo"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Gerenciamento de janelas e lançador de aplicativos"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Habilitar ferramentas internas úteis para desenvolvedores e testadores a "
+"partir do Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permite acesso a ferramentas internas de depuração e monitoramento usando o "
+"diálogo Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUIDs das extensões para habilitar"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"As extensões do GNOME Shell possuem uma propriedade UUID; esta chave lista "
+"as extensões que devem ser carregadas. Qualquer extensão que quiser ser "
+"carregada precisa estar nesta lista. Você também pode manipular esta lista "
+"com os métodos do DBus EnableExtension e DisableExtensions em org.gnome."
+"Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUIDs das extensões para forçar a desabilitação"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"As extensões do GNOME Shell possuem uma propriedade UUID; esta chave lista "
+"as extensões que devem ser desabilitadas, mesmo se carregadas como parte do "
+"modo atual. Você também pode manipular esta lista com os métodos do DBus "
+"EnableExtension e DisableExtensions em org.gnome.Shell. Essa chave leva "
+"precedência sobre a configuração “enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Desabilitar extensões do usuário"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Desabilita todas as extensões que o usuário habilitou sem afetar a definição "
+"de “enabled-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Desabilita a validação de compatibilidade da versão da extensão"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"O GNOME Shell somente carregará extensões que declaram possuir suporte a "
+"versão atual em execução. Ao habilitar esta opção, esta verificação será "
+"desabilitada e haverá tentativas de carregar todas as extensões independente "
+"das versões que estas declaram suportar."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Lista dos IDs de arquivo de área de trabalho para os aplicativos favoritos"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Os aplicativos correspondentes a estes identificadores serão exibidos na "
+"área de favoritos."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Histórico do diálogo de comandos (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Histórico do diálogo do shell looking glass"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Sempre mostrar o item de menu “Encerrar sessão” no menu de usuário."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Essa chave sobrescreve a ocultação automática do item de menu “Encerrar "
+"sessão” quando houver somente um usuário, em situações de somente uma sessão."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Se lembra ou não as senhas para montar sistemas de arquivos criptografados "
+"ou remotos"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"O shell irá pedir uma senha quando um dispositivo criptografado ou um "
+"sistema de arquivo remoto for montado. Caso a senha possa ser salva para uso "
+"posterior, a caixa de seleção “Lembrar senha” estará presente. Esta chave "
+"ajusta o estado padrão da caixa de seleção."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "O último perfil de energia não padrão selecionado"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Alguns sistemas suportam mais de dois perfis de energia. Para ainda oferecer "
+"suporte à alternância entre dois perfis, essa chave registra o último perfil "
+"não-padrão selecionado."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"A última versão para qual a caixa de diálogo “Bem-vindo ao GNOME” foi exibida"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Esta chave determina para qual versão a caixa de diálogo “Bem-vindo ao "
+"GNOME” foi exibida pela última vez. Uma string vazia representa a versão "
+"mais antiga possível, e um grande número representará versões que ainda não "
+"existem. Este grande número pode ser usado para desativar efetivamente o "
+"diálogo."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Layout do seletor de aplicativo"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Layout do seletor de aplicativo. Cada entrada na matriz é uma página. As "
+"páginas são armazenadas na ordem em que aparecem no GNOME Shell. Cada página "
+"contém um par “id do aplicativo” → “dados”. Atualmente, os seguintes valores "
+"são armazenados como “dados”: • “posição”: a posição do ícone do aplicativo "
+"na página"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Atalho de teclado para abrir um menu de aplicativo"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Atalho de teclado para abrir um menu de aplicativo."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Atalho de teclado para alternar entre estados de panorama"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Atalho de teclado para alternar entre sessão, seletor de janelas e a grade "
+"de aplicativos"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Atalho de teclado para alternar entre grade de aplicativos, seletor de "
+"janelas e a sessão"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Atalho de teclado para abrir a visualização “Mostrar aplicativos”"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Atalho de teclado para abrir a visualização “Mostrar aplicativos” do "
+"panorama de atividades."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Atalho de teclado para abrir o panorama"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Atalho de teclado para abrir o panorama de atividades."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Atalho de teclado para alternar a visibilidade da lista de notificação"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Atalho de teclado para alternar a visibilidade da lista de notificação."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Atalho de teclado para ativar a notificação ativa"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Atalho de teclado para ativar a notificação ativa."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Alternar para o aplicativo 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Alternar para o aplicativo 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Alternar para o aplicativo 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Alternar para o aplicativo 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Alternar para o aplicativo 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Alternar para o aplicativo 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Alternar para o aplicativo 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Alternar para o aplicativo 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Alternar para o aplicativo 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Limitar o alternador ao espaço de trabalho atual."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Se verdadeiro, o alternador mostrará somente os aplicativos que possuem "
+"janelas no espaço de trabalho atual. Caso contrário, todos os aplicativos "
+"serão incluídos."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "O modo ícone do aplicativo."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configura como as janelas são mostradas no alternador. As possibilidades "
+"válidas são “thumbnail-only” (mostra uma miniatura da janela), “app-icon-"
+"only” (mostra apenas o ícone do aplicativo) ou “both”."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Se verdadeiro, o alternador mostrará somente as janelas do espaço de "
+"trabalho atual. Caso contrário, todos as janelas serão incluídas."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Localizações"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "As localizações para mostrar nos relógios mundiais"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Localização automática"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Se deve-se obter a localização atual ou não"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Localização"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "A localização para a qual deve-se mostrar uma previsão do tempo"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Anexar diálogo modal à janela pai"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Esta chave sobrescreve a chave em org.gnome.mutter ao executar o GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Habilitar contorno ladrilhado ao arrastar janelas sobre as bordas da tela"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Espaços de trabalho são gerenciados dinamicamente"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Espaços de trabalho apenas no monitor primário"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Atrasar foco altera o modo do mouse até o ponteiro parar de mover"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Sessão de Rede"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Algo deu errado"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Lamentamos, mas houve um problema: as configurações dessa extensão não podem "
+"ser exibidas. Recomendamos que você relate o problema aos autores da "
+"extensão."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Detalhes técnicos"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Site"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Visita a página web da extensão"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Senha"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Escolher sessão"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Não está listado?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ex.: usuário ou %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Nome de usuário"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Janela de sessão"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Erro de autenticação"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(ou deslize o dedo pelo leitor)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(ou coloque o dedo no leitor)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Desligar"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "desligar;desligamento;reinicialização;reiniciar"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reiniciar;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Bloquear tela"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "tela de bloqueio"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Encerrar sessão"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "encerrar sessão;desconectar;sair"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspender"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspender;dormir"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Alternar usuário"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "alternar usuário;trocar de usuário"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "bloqueio de orientação;desbloqueio de orientação;tela;rotação"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Captura de tela"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"captura de tela;capturar tela;screencast;recorte;recortar;captura;capturar;"
+"gravação;gravar"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Desbloquear rotação de tela"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Bloquear rotação de tela"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Comando não encontrado"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Não foi possível analisar comando:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "A execução de “%s” falhou:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Agora mesmo"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minuto atrás"
+msgstr[1] "%d minutos atrás"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d hora atrás"
+msgstr[1] "%d horas atrás"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Ontem"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dia atrás"
+msgstr[1] "%d dias atrás"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d semana atrás"
+msgstr[1] "%d semanas atrás"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d mês atrás"
+msgstr[1] "%d meses atrás"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d ano atrás"
+msgstr[1] "%d anos atrás"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ontem, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d de %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d de %B de %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ontem, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d de %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d de %B de %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Autenticação de ponto de acesso"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Seu conexão com essa autenticação de ponto de acesso não é segura. Senhas e "
+"outras informações que você inserir nesta página podem ser vistas por "
+"pessoas próximas."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Negar acesso"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Conceder acesso"
+
+#: js/ui/appDisplay.js:1732
+msgid "Unnamed Folder"
+msgstr "Pasta sem nome"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s foi adicionado aos seus favoritos."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s foi removido dos seus favoritos."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Janelas abertas"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nova janela"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Mostrar detalhes"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Sair"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:250
+msgid "Unpin"
+msgstr "Desafixar"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Fixar nos favoritos"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Iniciar usando placa de vídeo integrada"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Iniciar usando placa de vídeo dedicada"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Selecione o dispositivo de áudio"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Configurações de som"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Fones de ouvido"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Fone de ouvido com microfone"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Microfone"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Alterar plano de fundo…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Configurações de exibição"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Configurações"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Q"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Q"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB de %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Mês anterior"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Próximo mês"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Semana %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Nenhuma notificação"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Não perturbe"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Limpar"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” não está respondendo."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Você pode escolher esperar por um breve tempo para que continue ou force o "
+"aplicativo a sair totalmente."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Forçar sair"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Esperar"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Unidade externa conectada"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Unidade externa desconectada"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Não foi possível desbloquear o volume"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "A versão do udisks instalado não possui suporte à configuração do PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Abrir com %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternativamente, você pode conectar pressionando o botão “WPS” em seu "
+"roteador."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Conectar"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Chave"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Senha da chave privada"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identidade"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Serviço"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autenticação necessária"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Senhas ou chaves criptografadas são necessárias para acessar a rede sem fio "
+"“%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Autenticação 802.1X cabeada"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nome da rede"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Autenticação DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Código PIN requisitado"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "O código PIN é necessário para o dispositivo móvel de banda larga"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Uma senha é necessária para se conectar a “%s”."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1960
+msgid "Network Manager"
+msgstr "Gerenciador de rede"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Senha de VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Autenticação necessária"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administrador"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autenticação"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Desculpe, isto não funcionou. Por favor, tente novamente."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s agora é conhecido como %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Janelas"
+
+#: js/ui/dash.js:206 js/ui/dash.js:252
+msgid "Show Applications"
+msgstr "Mostrar aplicativos"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:399
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d de %B de %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e de %B de %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d de %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d de %B de %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Hoje"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Amanhã"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Dia todo"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Nenhum evento"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Adicionar relógios mundiais…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Relógios mundiais"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Carregando…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Conecte-se à internet para obter as informações meteorológicas"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "No momento as informações meteorológicas não estão disponíveis"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Meteorologia"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Selecione uma localização meteorológica…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Encerrar sessão de %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Encerrar sessão"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s encerrará a sessão automaticamente em %d segundo."
+msgstr[1] "%s encerrará a sessão automaticamente em %d segundos."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Sua sessão será encerrada automaticamente em %d segundo."
+msgstr[1] "Sua sessão será encerrada automaticamente em %d segundos."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Encerrar sessão"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Desligar"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalar atualizações & desligar"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "O sistema será desligado automaticamente em %d segundo."
+msgstr[1] "O sistema será desligado automaticamente em %d segundos."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalar atualizações de software pendentes"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Desligar"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalar atualizações & reiniciar"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "O sistema irá reiniciar automaticamente em %d segundo."
+msgstr[1] "O sistema irá reiniciar automaticamente em %d segundos."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reiniciar & instalar atualizações"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"O sistema irá reiniciar e instalar atualizações automaticamente em %d "
+"segundo."
+msgstr[1] ""
+"O sistema irá reiniciar e instalar atualizações automaticamente em %d "
+"segundos."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reiniciar &amp; instalar"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalar &amp; desligar"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Desligar após atualizações serem instaladas"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reiniciar & instalar atualizações"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s será instalado após a reinicialização. A instalação da atualização "
+"pode levar um longo tempo: certifique-se de que fez cópia de segurança (back "
+"up) e que o computador esteja ligado na tomada."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Bateria fraca: conecte na tomada antes de instalar atualizações."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Alguns aplicativos estão ocupados ou possuem trabalhos não salvos"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Outros usuários estão com sessão aberta"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opções de inicialização"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (remoto)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (console)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Instalar"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Instalar extensão"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Baixar e instalar “%s” de extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Atualizações de extensões disponíveis"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Atualizações de extensões estão prontas para serem instaladas."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Permitir inibir atalhos"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "O aplicativo %s deseja inibir atalhos"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Um aplicativo deseja inibir atalhos"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Você pode restaurar os atalhos pressionando %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Negar"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Permitir"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Teclas lentas ligadas"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Teclas lentas desligadas"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Você acabou de manter a tecla Shift pressionada durante 8 segundos. Este é o "
+"atalho para o recurso de teclas lentas, que afeta a forma como o seu teclado "
+"opera."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Teclas de aderência ligadas"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Teclas de aderência desligadas"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Você acabou de pressionar a tecla Shift por 5 vezes de uma vez. Este é o "
+"atalho para o recurso de teclas de aderência, que afeta a forma como o seu "
+"teclado opera."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Você acabou de pressionar duas teclas de uma vez ou pressionou de uma vez 5 "
+"vezes a tecla Shift. Isto desliga o recurso de teclas de aderência, que "
+"afeta a forma como o seu teclado opera."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Deixar ativado"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Ligar"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Desligar"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Deixar desativado"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Configurações regionais e de idioma"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Nenhuma extensão instalada"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s não emitiu erros."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Ocultar erros"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Exibir erros"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Habilitado"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Desabilitado"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Erro"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Expirado"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Baixando"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Ver fonte"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Página web"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "O sistema foi colocado em modo inseguro"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Os aplicativos têm agora acesso irrestrito"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Desfazer"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Sessão aberta como um usuário privilegiado"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Usar uma sessão como um usuário privilegiado deve ser evitado por motivos de "
+"segurança. Se possível, você deve abrir uma sessão como um usuário normal."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Bloqueio de tela desabilitado"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "O bloqueio de tela requer o gerenciador de exibição do GNOME."
+
+#: js/ui/messageTray.js:1417
+msgid "System Information"
+msgstr "Informações do sistema"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Artista desconhecido"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Título desconhecido"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Digite para pesquisar"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Aplicativos"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Panorama"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Novo atalho…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Aplicativo definido"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Mostrar ajuda na tela"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Trocar monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Atribuir atalho"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Concluído"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Editar…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Nenhum"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Pressione um botão para configurar"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Pressione Esc para sair"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Pressione qualquer tecla para sair"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Atividades"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistema"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Barra superior"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Executar um comando"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Pressione Esc para fechar"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Reinício não está disponível no Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Reiniciando…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME precisa bloquear a tela"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Não foi possível bloquear"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "O bloqueio foi impedido por um aplicativo"
+
+#: js/ui/screenshot.js:1165
+msgid "Selection"
+msgstr "Seleção"
+
+#: js/ui/screenshot.js:1175
+msgid "Area Selection"
+msgstr "Seleção de área"
+
+#: js/ui/screenshot.js:1180
+msgid "Screen"
+msgstr "Tela"
+
+#: js/ui/screenshot.js:1190
+msgid "Screen Selection"
+msgstr "Seleção de tela"
+
+#: js/ui/screenshot.js:1195
+msgid "Window"
+msgstr "Janela"
+
+#: js/ui/screenshot.js:1205
+msgid "Window Selection"
+msgstr "Seleção de janela"
+
+#: js/ui/screenshot.js:1243
+msgid "Screenshot / Screencast"
+msgstr "Captura de tela / Gravação de tela"
+
+#: js/ui/screenshot.js:1279
+msgid "Show Pointer"
+msgstr "Mostrar cursor"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1870
+msgid "Screencasts"
+msgstr "Gravações de tela"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1875
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Gravação de tela de %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1917 js/ui/screenshot.js:2129
+msgid "Screenshot"
+msgstr "Captura de tela"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1923
+msgid "Screencast recorded"
+msgstr "Gravação de tela registrada"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1925
+msgid "Click here to view the video."
+msgstr "Clique aqui para ver o vídeo."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1928 js/ui/screenshot.js:2143
+msgid "Show in Files"
+msgstr "Mostrar no Arquivos"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2089
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Captura de tela de %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2135
+msgid "Screenshot captured"
+msgstr "Captura de tela obtida"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2137
+msgid "You can paste the image from the clipboard."
+msgstr "Você pode colar a imagem a partir da área de transferência."
+
+#: js/ui/screenshot.js:2190 js/ui/screenshot.js:2355
+msgid "Screenshot taken"
+msgstr "Captura de tela obtida"
+
+#: js/ui/search.js:809
+msgid "Searching…"
+msgstr "Pesquisando…"
+
+#: js/ui/search.js:811
+msgid "No results."
+msgstr "Nenhum resultado."
+
+#: js/ui/search.js:942
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d mais"
+msgstr[1] "%d mais"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Pesquisar"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copiar"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Colar"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Mostrar texto"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ocultar texto"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock está ligado."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Volume oculto"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Volume de sistema Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Usa arquivos de chave"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Para desbloquear um volume que usa arquivos de chave, use o utilitário "
+"<i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Insira um número PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Lembrar senha"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Desbloquear"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Abrir o %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "O PIM deve ser um número ou vazio."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Não foi possível iniciar o %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Não foi possível localizar o aplicativo %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Acessibilidade"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Alto contraste"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Ampliador"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Leitor de tela"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Teclado de tela"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Alertas visuais"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Teclas de aderência"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Teclas lentas"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Teclas de repercussão"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Teclas do mouse"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Texto grande"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Rotação automática"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Brilho"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Modo escuro"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clique único"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Clique duplo"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Arrastar"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clique secundário"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clique de permanência"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Teclado"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Exibir disposição de teclado"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Permitir acesso a localização"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "O aplicativo %s deseja acesso a sua localização"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Acesso a localização pode ser alterado a qualquer momento nas configurações "
+"de privacidade."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<desconhecido>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Desconectar %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Conectar a %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1113
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Ponto de acesso %s"
+
+#: js/ui/status/network.js:1472 js/ui/status/network.js:1488
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1473
+msgid "VPN Settings"
+msgstr "Configurações de VPN"
+
+#: js/ui/status/network.js:1722
+msgid "Wi–Fi"
+msgstr "Wi-Fi"
+
+#: js/ui/status/network.js:1724
+msgid "All Networks"
+msgstr "Todas as redes"
+
+#: js/ui/status/network.js:1821
+msgid "Wired Connections"
+msgstr "Conexões cabeadas"
+
+#: js/ui/status/network.js:1822
+msgid "Wired Settings"
+msgstr "Configurações da rede cabeada"
+
+#: js/ui/status/network.js:1836
+msgid "Bluetooth Tethers"
+msgstr "Pontos de acesso via Bluetooth"
+
+#: js/ui/status/network.js:1837
+msgid "Bluetooth Settings"
+msgstr "Configurações de Bluetooth"
+
+#: js/ui/status/network.js:1851
+msgid "Mobile Connections"
+msgstr "Conexões móveis"
+
+#: js/ui/status/network.js:1853
+msgid "Mobile Broadband Settings"
+msgstr "Configurações de banda larga móvel"
+
+#: js/ui/status/network.js:1965
+msgid "Connection failed"
+msgstr "Falha de conexão"
+
+#: js/ui/status/network.js:1966
+msgid "Activation of network connection failed"
+msgstr "Falha ao ativar a conexão da rede"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Luz noturna"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Desempenho"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Balanceado"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Economia de energia"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Perfis de energia"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Parar gravação de tela"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Parar o compartilhamento de tela"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Modo avião"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Fazer captura de tela"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Menu de desligamento"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Suspender"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Reiniciar…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Desligar…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Encerrar sessão…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Alternar usuário…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Bloquear tela"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Dispositivo Thunderbolt desconhecido"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Um novo dispositivo foi detectado enquanto você estava ausente. Por "
+"gentileza, desconecte e reconecte o dispositivo para começar a usá-lo."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispositivo Thunderbolt não autorizado"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Um novo dispositivo foi detectado e precisa ser autorizado por um "
+"administrador."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Erro de autorização de thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Não foi possível autorizar o dispositivo Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Volume alterado"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volume"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Saída de som"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Entrada de som"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Espelhar"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Juntar telas"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Externa apenas"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Interna apenas"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d de %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Deslize para desbloquear"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Clique ou pressione uma tecla para desbloquear"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Desbloquear janela"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Iniciar sessão como outro usuário"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Bem-vindo ao GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Se você quiser se familiarizar, confira o tour."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Não, obrigado"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Fazer tour"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” está pronto"
+
+# Título de janela de confirmação; Se grande demais, pode ser exibida com "..."
+# Vide: https://bugzilla.gnome.org/show_bug.cgi?id=786331
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Manter essas configurações da tela?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Reverter configurações"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Manter alterações"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Alterações nas configurações serão revertidas em %d segundo"
+msgstr[1] "Alterações nas configurações serão revertidas em %d segundos"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Ocultar"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Restaurar"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximizar"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Mover"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Mover barra de título na tela"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Sempre no topo"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Sempre na área de trabalho visível"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Mover para a área de trabalho esquerda"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Mover para a área de trabalho direita"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Mover para a área de trabalho acima"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Mover para a área de trabalho abaixo"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Mover para o monitor acima"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Mover para o monitor abaixo"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Mover para o monitor à esquerda"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Mover para o monitor à direita"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Fechar"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Agenda do Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Exibe a versão"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Modo usado pelo GDM para a tela de início de sessão"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Utilizar um modo específico, ex.: “gdm” para a tela de início de sessão"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Listar modos possíveis"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Desconhecido"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Erro ao lançar “%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "As senhas não coincidem."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "A senha não pode estar em branco"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "O diálogo de autenticação foi descartado pelo usuário"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Extensões"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Gerenciar suas extensões do GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "O Projeto GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Extensões lida com a atualização da extensões, configuração das "
+"preferências de extensões e remoção ou desabilitação de extensões "
+"indesejadas."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configure extensões do GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Nenhuma correspondência"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Remover “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Se você remover a extensão, você precisa voltar a baixá-la se você quiser "
+"habilitá-la novamente"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Remover"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Og Maciel <ogmaciel@gnome.org>\n"
+"Rodrigo Flores <mail@rodrigoflores.org>\n"
+"Felipe Borges <felipe10borges@gmail.com>\n"
+"Henrique P. Machado <hpmachado@gnome.org>\n"
+"Jonh Wendell <wendell@bani.com.br>\n"
+"Rodrigo Padula <contato@rodrigopadula.com>\n"
+"Gabriel F. Vilar <cogumm@gmail.com>\n"
+"Adorilson Bezerra <adorilson@gmail.com>\n"
+"Djavan Fagundes <djavan@comum.org>\n"
+"Juan Diego Martins da Costa Cruz <juan.martins@ifrn.edu.br>\n"
+"Fábio Nogueira <fnogueira@gnome.org>\n"
+"Georges Basile Stavracas Neto <georges.stavracas@gmail.com>\n"
+"Felipe Braga <fbobraga@gmail.com>\n"
+"Artur de Aquino Morais <artur.morais93@outlook.com>\n"
+"Ricardo Silva Veloso <ricvelozo@gmail.com>\n"
+"Bruno Lopes <brunolopesdsilv@gmail.com>\n"
+"Enrico Nicoletto <hiko@duck.com>\n"
+"Rafael Fontenelle <rafaelff@gnome.org>\n"
+"Matheus Barbosa <mdpb.matheus@gmail.com>\n"
+"Leônidas Araújo <leorusvellt@hotmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d extensão será atualizada na próxima sessão."
+msgstr[1] "%d extensões serão atualizadas na próxima sessão."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "A extensão é incompatível com a versão atual do GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "A extensão apresentou um erro"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "A extensão pode ser atualizada"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Site"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Remover…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Ajuda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Sobre as Extensões"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Extensões podem causar problemas de desempenho e estabilidade. Desabilite "
+"extensões se você encontrar problemas em seu sistema."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Instalada manualmente"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Para encontrar e adicionar extensões, visite <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Interna"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Nenhuma extensão instalada"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Sentimos muito, mas não foi possível obter a lista de extensões instaladas. "
+"Certifique-se de estar em uma sessão do GNOME e tente novamente."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Atualizações de extensões prontas"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "A nova extensão foi criada com sucesso em %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"O nome deve ser uma string muito curta (idealmente descritiva.\n"
+"Exemplos são: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nome"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Descrição é uma explicação uma só frase do que sua extensão faz.\n"
+"Exemplos são: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descrição"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID é um identificador global único para sua extensão.\n"
+"Deve estar no formato de um endereço de e-mail (cliqueparafoco@janedoe."
+"example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Escolha um dos modelos disponíveis:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Modelo"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "O identificador único para a nova extensão"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NOME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "O nome visível do usuário da nova extensão"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIÇÃO"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Uma descrição curta do que a extensão faz"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MODELO"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "O modelo para usar para a nova extensão"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Insira as informações da extensão interativamente"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Cria uma nova extensão"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argumentos desconhecidos"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, nome e descrição são necessários"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "A extensão “%s” não existe\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Desabilita uma extensão"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nenhuma UUID dado"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Mais de um UUID fornecido"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Habilita uma extensão"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Falha ao conectar ao GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "A extensão “%s” não existe\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Mostra informações das extensões"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Sobrescreve uma extensão existente"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PACOTE_EXTENSÃO"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instala um pacote de extensão"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nenhum pacote de extensão especificado"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Mais de um pacote de extensão especificado"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Mostra extensões instaladas pelo usuário"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Mostra extensões instaladas pelo sistema"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Mostra extensões habilitadas"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Mostra extensões desabilitadas"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Mostra extensões com preferências"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Mostra extensões com atualizações"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Mostra detalhes da extensão"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Lista extensões instaladas"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ARQUIVO"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Fonte adicional para incluir no pacote"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ESQUEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "O esquema de GSettings que deve ser incluído"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRETÓRIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "O diretório no qual traduções são encontradas"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMÍNIO"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "O domínio gettext para usar para as traduções"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Sobrescreve um pacote existente"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "O diretório no qual o pacote deve ser criado"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIRETÓRIO_FONTE"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Cria um pacote de extensão"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Mais de um diretório fonte especificado"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "A extensão “%s” não tem preferências\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Falha ao abrir as preferências para a extensão “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Abre as preferências da extensão"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Redefine uma extensão"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Não é possível desinstalar as extensões do sistema\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Falha ao desinstalar “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Desinstala uma extensão"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Não exibir mensagens de erro"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Caminho"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autor original"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versão"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Estado"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” não aceita argumentos"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Uso:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Exibe as informações da versão e sai."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "COMANDO"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGS…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Comandos:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Exibe essa ajuda"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Habilita extensão"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Desabilita extensão"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Redefine extensão"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Desinstala extensão"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Lista extensões"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Mostra informações de extensão"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Abre preferências de extensão"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Cria extensão"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Empacota extensão"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Instala pacote de extensão"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Use “%s” para obter ajuda detalhada.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Em branco"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Uma extensão vazia"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicador"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Adiciona um ícone à barra superior"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u saída"
+msgstr[1] "%u saídas"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u entrada"
+msgstr[1] "%u entradas"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Sons do sistema"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Se o adaptador Bluetooth padrão configurou, ou não, dispositivos "
+#~ "associados a ele"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "O shell irá mostrar apenas um item de menu Bluetooth se um adaptador "
+#~ "Bluetooth estiver ligado ou se existirem dispositivos configurados "
+#~ "associados ao adaptador padrão. Isto será redefinido caso o adaptador "
+#~ "padrão sempre seja visto sem possuir dispositivos associados a ele."
+
+#~ msgid "Log Out"
+#~ msgstr "Encerrar sessão"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s desligada"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "Conectado à %s"
+
+# Não gerenciável para transmitir a ideia que o Networkmanager não consegue gerenciar o dispositivo --Enrico
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s não é gerenciável"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "Conectando à %s"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s requer autenticação"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Firmware em falta para %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s está indisponível"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Falha na conexão de %s"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Hardware de %s desabilitado"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s está desabilitado"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Conectar à Internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Modo avião ligado"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "O Wi-Fi é desabilitado quando o modo avião está ligado."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Desligar modo avião"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi desligado"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "O Wi-Fi precisa ser ligado a fim de conectar-se a uma rede."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Ligar Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Redes Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Selecione uma rede"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Usar alternador de hardware para desligar"
+
+#~ msgid "Select Network"
+#~ msgstr "Selecione a rede"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Configurações de Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s não está conectado"
+
+#~| msgid "<unknown>"
+#~ msgid "unknown"
+#~ msgstr "desconhecido"
+
+#~| msgid "Estimating…"
+#~ msgid "activating…"
+#~ msgstr "ativando..."
+
+#~| msgid "Estimating…"
+#~ msgid "deactivating…"
+#~ msgstr "desativando..."
+
+#~ msgid "deactivated"
+#~ msgstr "desativado"
+
+#~ msgid "connecting…"
+#~ msgstr "conectando…"
+
+#~ msgid "authentication required"
+#~ msgstr "autenticação necessária"
+
+#~ msgid "connection failed"
+#~ msgstr "conexão falhou"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN desligada"
+
+#~ msgid "Network Settings"
+#~ msgstr "Configurações de rede"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s conexão Wi-Fi"
+#~ msgstr[1] "%s conexões Wi-Fi"
+
+#~ msgid "Power Settings"
+#~ msgstr "Configurações de energia"
+
+#~ msgid "Lock"
+#~ msgstr "Bloquear"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Desligar / encerrar sessão"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d conectado"
+#~ msgstr[1] "%d conectados"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth ligado"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth desligado"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Localização habilitada"
+
+#~ msgid "Disable"
+#~ msgstr "Desabilitar"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Configurações de privacidade"
+
+#~ msgid "Location In Use"
+#~ msgstr "Localização em uso"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Localização desabilitada"
+
+#~ msgid "Enable"
+#~ msgstr "Habilitar"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Luz noturna desabilitada"
+
+#~ msgid "Resume"
+#~ msgstr "Continuar"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Desabilitar até amanhã"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Carga completa"
+
+#~ msgid "Not Charging"
+#~ msgstr "Não está carregando"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d:%02d restante(s) (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d:%02d até completar (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "A tela está sendo compartilhada"
+
+#~ msgid "Turn off"
+#~ msgstr "Desativar"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "Mostrar UI da captura de tela"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Gravação de tela em progresso"
+
+#~ msgid "Stop"
+#~ msgstr "Parar"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Aviso"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Extensões podem causar problemas no sistema, incluindo problemas de "
+#~ "desempenho. Se você encontrar problemas com o seu sistema, é recomendável "
+#~ "desativar todas as extensões."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Remover dos favoritos"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Adicionar aos favoritos"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Habilitar API de introspecção"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Habilita uma API de D-Bus que permite introspectar o estado do aplicativo "
+#~ "do shell."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Falha ao conectar ao GNOME Shell"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizar"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Restaurar"
+
+#~ msgid "App Picker View"
+#~ msgstr "Visualização do seletor de aplicativos"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Índice da visualização atualmente selecionada no seletor de aplicativos."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Aplicativos usados frequentemente vão aparecer aqui"
+
+#~ msgid "Frequent"
+#~ msgstr "Frequente"
+
+#~ msgid "All"
+#~ msgstr "Todos"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d de %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d de %B de %Y"
+
+#~ msgid "Off"
+#~ msgstr "Off"
+
+#~ msgid "On"
+#~ msgstr "On"
+
+#~ msgid "Copy Error"
+#~ msgstr "Copiar erro"
+
+#~| msgid "Username: "
+#~ msgid "Username…"
+#~ msgstr "Nome de usuário…"
+
+#~ msgid "Password: "
+#~ msgstr "Senha: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Lembrar senha…"
+
+#~ msgid "Next"
+#~ msgstr "Próximo"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Entrar"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d de %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nova mensagem"
+#~ msgstr[1] "%d novas mensagens"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d nova notificação"
+#~ msgstr[1] "%d novas notificações"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Navegar no Software"
+
+#~ msgid "Password:"
+#~ msgstr "Senha:"
+
+#~ msgid "Type again:"
+#~ msgstr "Digite novamente:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Autenticação requisitada pela rede sem fio"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Senha da rede de banda larga móvel"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Bloquear orientação"
+
+#~ msgid "Rename"
+#~ msgstr "Renomear"
+
+#~ msgid "Account Settings"
+#~ msgstr "Configurações de conta"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Bloqueio de orientação"
+
+# Tween pode significar uma contração de Between ou se referir a um termo "in-between" usado em animação gráfica. -- Enrico
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Atalho de teclado que pausa e continua todos os intermediários em "
+#~ "execução, a fim de depuração"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Qual teclado usar"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "O tipo do teclado para usar."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Ocorreu um erro ao carregar o dialogo de preferências para %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s por todo o dia."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, depois %s mais tarde."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, depois %s, seguido de %s mais tarde."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Sensação térmica de %s."
+
+#~| msgid "Log Out"
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Encerrar sessão"
+
+#~| msgid "Switch User"
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Alternar usuário"
+
+#~ msgid "Hide tray"
+#~ msgstr "Esconder bandeja"
+
+#~ msgid "Status Icons"
+#~ msgstr "Ícones de status"
+
+#~ msgid "Events"
+#~ msgstr "Eventos"
+
+#~ msgid "Notifications"
+#~ msgstr "Notificações"
+
+#~ msgid "Media"
+#~ msgstr "Mídia"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Redirecionamento para autenticação web"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+# Livre no sentido de disponível, ou seja, não estar em uso --Enrico
+#~ msgid "Not In Use"
+#~ msgstr "Livre"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Mostrar o número da semana na agenda"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Se verdadeiro, exibe o número da semana na agenda."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Usar como conexão de Internet"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s está solicitando acesso à sua localização."
+
+# Traduções do Xfce e da Microsoft usam compositor --Enrico
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (compositor wayland)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d dispositivo conectado"
+#~ msgstr[1] "%d dispositivos conectados"
+
+# UPS significa uninterruptible power supply, mas no Brasil vejo usando mais
+# "No-break", que é a mesma coisa, então optei pela segunda. -- Rafael Fontenelle
+#~ msgid "UPS"
+#~ msgstr "No-break"
+
+#~ msgid "Battery"
+#~ msgstr "Bateria"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgid "Invitation"
+#~ msgstr "Convite"
+
+#~ msgid "Call"
+#~ msgstr "Chamada"
+
+#~ msgid "File Transfer"
+#~ msgstr "Transferência de arquivo"
+
+#~ msgid "Chat"
+#~ msgstr "Conversar"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Convite para %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s está convidando você para participar de %s"
+
+#~ msgid "Decline"
+#~ msgstr "Recusar"
+
+#~ msgid "Accept"
+#~ msgstr "Aceitar"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Chamada de vídeo para %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Chamada de %s"
+
+#~ msgid "Answer"
+#~ msgstr "Atender"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s está enviando %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s quer permissão para vê-lo quando conectado"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Falha de autenticação"
+
+#~ msgid "Encryption error"
+#~ msgstr "Erro de criptografia"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Certificado não fornecido"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Certificado não confiável"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certificado expirado"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certificado não ativado"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Máquina do certificado não confere"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Impressão digital do certificado não confere"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificado auto-assinado"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "O status está definido como desconectado."
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "O certificado é inválido"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "A conexão foi recusada"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "A conexão não pode ser estabelecida"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Conexão perdida"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Esta conta já está conectada ao servidor"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "A conexão foi substituída por uma nova conexão usando o mesmo recurso"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "A conta já existe no servidor"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "O servidor está atualmente muito ocupado para controlar a conexão"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "O certificado foi revogado"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "O certificado usa um algoritmo de cifragem inseguro ou é "
+#~ "criptograficamente fraco"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "O comprimento do certificado do servidor, ou a profundidade da cadeia do "
+#~ "certificado excedeu os limites impostos pela biblioteca de criptografia"
+
+#~ msgid "Internal error"
+#~ msgstr "Erro interno"
+
+#~ msgid "View account"
+#~ msgstr "Visualizar conta"
+
+#~ msgid "Show the message list"
+#~ msgstr "Mostra a lista de mensagem"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Dom"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Seg"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Ter"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Qua"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Qui"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Sex"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Sab"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Nada agendado"
+
+#~ msgid "This week"
+#~ msgstr "Esta semana"
+
+#~ msgid "Next week"
+#~ msgstr "Próxima semana"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Dispositivos removíveis"
+
+#~ msgid "Eject"
+#~ msgstr "Ejetar"
+
+#~ msgid "Unmute"
+#~ msgstr "Ativar áudio"
+
+#~ msgid "Mute"
+#~ msgstr "Sem áudio"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Abrir agenda"
diff --git a/po/ro.po b/po/ro.po
new file mode 100644
index 0000000..5162dc7
--- /dev/null
+++ b/po/ro.po
@@ -0,0 +1,3867 @@
+# This file is distributed under the same license as the gnome-shell package.
+# Lucian Adrian Grijincu <lucian.grijincu@gmail.com>, 2009, 2010, 2011.
+# Daniel Șerbănescu <cyber19rider@gmail.com>, 2010, 2011.
+# laurion <me_lawr@yahoo.com>, 2010.
+# Ion Laurențiu Cristian <me_lawr@yahoo.com>, 2010.
+# Răpițeanu Viorel-Cătălin <rapiteanu.catalin@gmail.com>, 2013.
+# Viorel-Cătălin Răpițeanu <rapiteanu.catalin@gmail.com>, 2014.
+# Daniel Șerbănescu <daniel [at] serbanescu [dot] dk>, 2015, 2018.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2021-09-03 21:44+0000\n"
+"PO-Revision-Date: 2021-09-05 11:47+0200\n"
+"Last-Translator: Florentina Mușat <florentina.musat.28@gmail.com>\n"
+"Language-Team: Gnome Romanian Translation Team <gnomero-list@lists."
+"sourceforge.net>\n"
+"Language: ro\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==0 || (n!=1 && n%100>=1 && n"
+"%100<=19) ? 1 : 2);\n"
+"20)) ? 1: 2);\n"
+"X-Generator: Poedit 3.0\n"
+"X-Project-Style: gnome\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Lansatoare"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Activează aplicația preferată 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Activează aplicația preferată 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Activează aplicația preferată 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Activează aplicația preferată 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Activează aplicația preferată 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Activează aplicația preferată 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Activează aplicația preferată 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Activează aplicația preferată 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Activează aplicația preferată 9"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistem"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Arată lista de notificare"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Focalizează notificarea activă"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Arată prezentarea generală"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Arată toate aplicațiile"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Arată meniul aplicației"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Vizualizatorul activităților GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Administrare de ferestre și lansare de aplicații"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Activează uneltele interne utile dezvoltatorilor și testerilor din Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Permite accesul la unelte interne de depanare și monitorizare folosind "
+"dialogul Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Uuid-urile extensiilor de activat"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Extensiile pentru Vizualizatorul activităților GNOME au o proprietate uuid; "
+"această cheie listează extensiile care ar trebui încărcate. Orice extensie "
+"care trebuie încărcată trebuie să fie în listă. Puteți de asemenea să "
+"manipulați această listă cu metodele EnableExtension și DisableExtension pe "
+"org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID-urile extensiilor pentru a forța dezactivarea"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Extensiile pentru Shell GNOME au o proprietate UUID; această cheie listează "
+"extensiile care ar trebui dezactivate, chiar dacă sunt încărcate ca parte a "
+"modului curent. Puteți de asemenea să manipulați această listă cu metodele D-"
+"Bus EnableExtension și DisableExtension pe org.gnome.Shell. Această cheie "
+"are prioritate peste configurarea „enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Dezactivează extensiile utilizatorului"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Dezactivează toate extensiile pe care utilizatorul le-a activat fără să "
+"afecteze configurarea „enable-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Dezactivează validarea compatibilității versiunii extensiei"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Vizualizatorul activităților GNOME va încărca doar extensiile care pretind "
+"să fie compatibile cu versiunea actuală. Activarea acestei opțiuni va duce "
+"la dezactivarea verificării și la încercarea de a încărca toate extensiile "
+"fără a ține cont de versiunile cu care acestea pretind că sunt compatibile."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+"Lista de identificatori ale fișierelor desktop pentru aplicațiile favorite"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Aplicațiile corespunzătoare acestor identificatori vor fi afișate în zona "
+"favoritelor."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Istoricul dialogului de comenzi (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Istoric pentru lupa de căutare"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr ""
+"Arată întotdeauna elementul „Ieșire din sesiune” în meniul utilizatorului."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Această cheie suprascrie ascunderea automată a elementului „Ieșire din "
+"sesiune” în situațiile cu un singur utilizator, o singură sesiune."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Dacă se va reține parola pentru montarea sistemelor de fișiere criptate sau "
+"la distanță"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Vizualizatorul activităților va cere o parolă când este montat un dispozitiv "
+"criptat sau un sistem de fișiere la distanță. Dacă parola poate fi salvată "
+"pentru o utilizare viitoare, o căsuță intitulată „Reține parola” va fi "
+"afișată. Această cheie determină starea implicită a căsuței."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "Dacă adaptorul implicit Bluetooth are dispozitive asociate lui"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Vizualizatorul va arăta un element de meniu Bluetooth numai dacă adaptorul "
+"Bluetooth este pornit, sau dacă există dispozitive configurate asociate cu "
+"adaptorul implicit. Acesta va fi restabilit dacă adaptorul este perceput a "
+"nu avea dispozitive asociate lui."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"Ultima versiune pentru care a fost afișat dialogul „Bine ați venit la GNOME”"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Această cheie determină pentru care versiune a fost arătat ultima dată "
+"dialogul „Bine ați venit la GNOME”. Un șir gol reprezintă cea mai veche "
+"versiune posibilă, și un număr gigantic va reprezenta versiuni care nu "
+"există încă. Numărul gigantic poate fi folosit pentru a dezactiva efectiv "
+"dialogul."
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid "Layout of the app picker"
+msgstr "Aspectul selectorului de aplicații"
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Aspectul selectorului de aplicații. Fiecare intrare din vector este o "
+"pagină. Paginile sunt stocate în ordinea în care apar în GNOME Shell. "
+"Fiecare pagină conține o pereche de „id aplicație” → „date”. Momentan, "
+"următoarele valori sunt stocate ca „data”: • „poziție”: poziția iconiței "
+"aplicației în pagină"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu"
+msgstr "Combinație de taste pentru deschiderea meniului aplicației"
+
+#: data/org.gnome.shell.gschema.xml.in:150
+msgid "Keybinding to open the application menu."
+msgstr "Combinație de taste pentru deschiderea meniului aplicației."
+
+#: data/org.gnome.shell.gschema.xml.in:156
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between overview states"
+msgstr ""
+"Combinație de taste pentru comutarea între stările prezentării generale"
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Combinație de taste pentru a comuta între sesiune, selectorul de ferestre și "
+"grila de aplicații"
+
+#: data/org.gnome.shell.gschema.xml.in:164
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Combinație de taste pentru a comuta între grila de aplicații, selectorul de "
+"ferestre și sesiunea"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view"
+msgstr ""
+"Combinație de taste pentru deschiderea modului de afișare „Arată aplicațiile”"
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Combinație de taste pentru deschiderea modului de afișare „Arată "
+"aplicațiile” a prezentării generale a activităților."
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the overview"
+msgstr "Combinație de taste pentru deschiderea prezentării generale"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the Activities Overview."
+msgstr ""
+"Combinație de taste pentru deschiderea prezentării generale a activităților."
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr ""
+"Combinație de taste pentru comutarea vizibilității listei de notificare"
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr ""
+"Combinație de taste pentru comutarea vizibilității listei de notificare."
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification"
+msgstr "Combinație de taste pentru focalizarea notificării active"
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to focus the active notification."
+msgstr "Combinație de taste pentru focalizarea notificării active."
+
+#: data/org.gnome.shell.gschema.xml.in:199
+msgid "Switch to application 1"
+msgstr "Comută la aplicația 1"
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid "Switch to application 2"
+msgstr "Comută la aplicația 2"
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 3"
+msgstr "Comută la aplicația 3"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 4"
+msgstr "Comută la aplicația 4"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 5"
+msgstr "Comută la aplicația 5"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 6"
+msgstr "Comută la aplicația 6"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 7"
+msgstr "Comută la aplicația 7"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 8"
+msgstr "Comută la aplicația 8"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 9"
+msgstr "Comută la aplicația 9"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid "Limit switcher to current workspace."
+msgstr "Limitează comutatorul la spațiul de lucru curent."
+
+#: data/org.gnome.shell.gschema.xml.in:241
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Dacă este activat, doar aplicațiile care au ferestre în spațiul de lucru "
+"curent sunt arătate în comutator. Altfel, toate aplicațiile sunt incluse."
+
+#: data/org.gnome.shell.gschema.xml.in:258
+msgid "The application icon mode."
+msgstr "Miniatură și pictograma aplicației."
+
+#: data/org.gnome.shell.gschema.xml.in:259
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Configurează modul în care ferestrele sunt arătate în comutator. "
+"Posibilități valide sunt „mod miniatură” (arată o miniatură a ferestrei) "
+"„mod iconiță” (arată doar iconița aplicației) sau „ambele”."
+
+#: data/org.gnome.shell.gschema.xml.in:268
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Dacă este activat, doar ferestrele din spațiul de lucru curent sunt arătate "
+"în comutator. Altfel, toate ferestrele sunt incluse."
+
+#: data/org.gnome.shell.gschema.xml.in:278
+msgid "Locations"
+msgstr "Locații"
+
+#: data/org.gnome.shell.gschema.xml.in:279
+msgid "The locations to show in world clocks"
+msgstr "Locațiile de arătat în ceasuri globale"
+
+#: data/org.gnome.shell.gschema.xml.in:289
+msgid "Automatic location"
+msgstr "Locație automată"
+
+#: data/org.gnome.shell.gschema.xml.in:290
+msgid "Whether to fetch the current location or not"
+msgstr "Dacă să se obțină locația curentă"
+
+#: data/org.gnome.shell.gschema.xml.in:297
+msgid "Location"
+msgstr "Locație"
+
+#: data/org.gnome.shell.gschema.xml.in:298
+msgid "The location for which to show a forecast"
+msgstr "Locația pentru care să se arate o prognoză"
+
+#: data/org.gnome.shell.gschema.xml.in:310
+msgid "Attach modal dialog to the parent window"
+msgstr "Atașează dialogul modal la fereastra părinte"
+
+#: data/org.gnome.shell.gschema.xml.in:311
+#: data/org.gnome.shell.gschema.xml.in:320
+#: data/org.gnome.shell.gschema.xml.in:328
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Această cheie suprascrie cheia corespondentă din org.gnome.mutter când "
+"Vizualizatorul activităților GNOME rulează."
+
+#: data/org.gnome.shell.gschema.xml.in:319
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Activează mozaic lateral la plasarea ferestrelor pe marginile ecranului"
+
+#: data/org.gnome.shell.gschema.xml.in:327
+msgid "Workspaces are managed dynamically"
+msgstr "Spațiile de lucru sunt gestionate în mod dinamic"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Workspaces only on primary monitor"
+msgstr "Spații de lucru doar pe monitorul principal"
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Întârzie schimbările de focalizare în maus până când cursorul încetează să "
+"se miște"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Logare prin rețea"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:28
+#: subprojects/extensions-app/data/ui/extensions-window.ui:241
+msgid "Something’s gone wrong"
+msgstr "Ceva nu a funcționat cum trebuie"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:39
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Ne pare foarte rău, dar a avut loc o problemă: configurările pentru această "
+"extensie nu pot fi afișate. Vă recomandăm să raportați problema autorilor "
+"extensiei."
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:64
+msgid "Technical Details"
+msgstr "Detalii tehnice"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:106
+msgid "Homepage"
+msgstr "Pagină principală"
+
+#: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:107
+msgid "Visit extension homepage"
+msgstr "Vizitează pagina principală a extensiei"
+
+#: js/gdm/authPrompt.js:141 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:110 js/ui/components/polkitAgent.js:138
+#: js/ui/endSessionDialog.js:438 js/ui/extensionDownloader.js:228
+#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
+#: js/ui/status/network.js:978 subprojects/extensions-app/js/main.js:183
+msgid "Cancel"
+msgstr "Anulează"
+
+#. Cisco LEAP
+#: js/gdm/authPrompt.js:285 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:225 js/ui/components/networkAgent.js:249
+#: js/ui/components/networkAgent.js:270 js/ui/components/networkAgent.js:290
+#: js/ui/components/networkAgent.js:300 js/ui/components/polkitAgent.js:275
+#: js/ui/shellMountOperation.js:326
+msgid "Password"
+msgstr "Parolă"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Alegeți o sesiune"
+
+#: js/gdm/loginDialog.js:456
+msgid "Not listed?"
+msgstr "Nelistat?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:921
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(de exemplu, utilizator sau %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:926 js/ui/components/networkAgent.js:245
+#: js/ui/components/networkAgent.js:268 js/ui/components/networkAgent.js:286
+msgid "Username"
+msgstr "Nume de utilizator"
+
+#: js/gdm/loginDialog.js:1279
+msgid "Login Window"
+msgstr "Fereastră de autentificare"
+
+#: js/gdm/util.js:430
+msgid "Authentication error"
+msgstr "Eroare de autentificare"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:589
+msgid "(or swipe finger across reader)"
+msgstr "(sau treceți cu degetul peste cititor)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:594
+msgid "(or place finger on reader)"
+msgstr "(sau plasați degetul pe cititor)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:82
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Oprire"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:85
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;oprește;închide;blochează"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:90
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Repornire"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:93
+msgid "reboot;restart;"
+msgstr "reboot;restart;repornește;restartează;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:98
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Blochează ecranul"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:101
+msgid "lock screen"
+msgstr "lock screen;blochează ecranul"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Ieșire din sesiune"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "logout;log out;sign off"
+msgstr "logout;sign off;ieșire din sesiune;deautentificare"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:114
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Suspendare"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:117
+msgid "suspend;sleep"
+msgstr "suspend;sleep;suspendare"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:122
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Comută utilizatorul"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:125
+msgid "switch user"
+msgstr "switch user;comută utilizatorul"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:132
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;screen;rotation;blocare orientare;blocare de orientare;"
+"ecran;rotație"
+
+#: js/misc/systemActions.js:232
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Deblochează rotația ecranului"
+
+#: js/misc/systemActions.js:233
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Rotație de ecran blocat"
+
+#: js/misc/util.js:121
+msgid "Command not found"
+msgstr "Comanda nu a fost găsită"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:157
+msgid "Could not parse command:"
+msgstr "Nu s-a putut analiza comanda:"
+
+#: js/misc/util.js:165
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Execuția comenzii „%s” a eșuat:"
+
+#: js/misc/util.js:182
+msgid "Just now"
+msgstr "Acum"
+
+#: js/misc/util.js:184
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minut în urmă"
+msgstr[1] "%d minute în urmă"
+msgstr[2] "%d de minute în urmă"
+
+#: js/misc/util.js:188
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d oră în urmă"
+msgstr[1] "%d ore în urmă"
+msgstr[2] "%d de ore în urmă"
+
+#: js/misc/util.js:192 js/ui/dateMenu.js:162
+msgid "Yesterday"
+msgstr "Ieri"
+
+#: js/misc/util.js:194
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d zi în urmă"
+msgstr[1] "%d zile în urmă"
+msgstr[2] "%d de zile în urmă"
+
+#: js/misc/util.js:198
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d săptămână în urmă"
+msgstr[1] "%d săptămâni în urmă"
+msgstr[2] "%d de săptămâni în urmă"
+
+#: js/misc/util.js:202
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d lună în urmă"
+msgstr[1] "%d luni în urmă"
+msgstr[2] "%d de luni în urmă"
+
+#: js/misc/util.js:205
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d an în urmă"
+msgstr[1] "%d ani în urmă"
+msgstr[2] "%d de ani în urmă"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:238
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:244
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Ieri, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:250
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:256
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%B %-d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:262
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %-d %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:267
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:273
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Ieri, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:279
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:285
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %-d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:291
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %-d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:49
+msgid "Hotspot Login"
+msgstr "Logare hotspot"
+
+#: js/portalHelper/main.js:95
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Conexiunea dumneavoastră la acest hotspot nu este sigură. Parolele sau alte "
+"informății pe care le-ați introdus pe această pagină pot fi văzute de "
+"oamenii din jur."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:417
+msgid "Deny Access"
+msgstr "Refuză accesul"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:420
+msgid "Grant Access"
+msgstr "Permite accesul"
+
+#: js/ui/appDisplay.js:1846
+msgid "Unnamed Folder"
+msgstr "Dosar nedenumit"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s a fost adăugat la favorite."
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s a fost eliminat de la favorite."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Ferestre deschise"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Fereastră nouă"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Arată detaliile"
+
+#: js/ui/appMenu.js:96
+msgid "Quit"
+msgstr "Ieșire"
+
+#: js/ui/appMenu.js:163 js/ui/dash.js:245
+msgid "Remove from Favorites"
+msgstr "Elimină din favorite"
+
+#: js/ui/appMenu.js:164
+msgid "Add to Favorites"
+msgstr "Adaugă la Favorite"
+
+#: js/ui/appMenu.js:181
+msgid "Launch using Integrated Graphics Card"
+msgstr "Lansează folosind placa grafică integrată"
+
+#: js/ui/appMenu.js:182
+msgid "Launch using Discrete Graphics Card"
+msgstr "Lansează folosind placa grafică discretă"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Selectați dispozitivul audio"
+
+#: js/ui/audioDeviceSelection.js:56
+msgid "Sound Settings"
+msgstr "Configurări de sunet"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Căști auriculare"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Căști cu microfon"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:277
+msgid "Microphone"
+msgstr "Microfon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Schimbă fundalul…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Configurări afișaj"
+
+#: js/ui/backgroundMenu.js:17
+msgid "Settings"
+msgstr "Configurări"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:36
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:65
+msgctxt "grid sunday"
+msgid "S"
+msgstr "D"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:67
+msgctxt "grid monday"
+msgid "M"
+msgstr "L"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:69
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:71
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:73
+msgctxt "grid thursday"
+msgid "T"
+msgstr "J"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:75
+msgctxt "grid friday"
+msgid "F"
+msgstr "V"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:77
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:392
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:461
+msgid "Previous month"
+msgstr "Luna precedentă"
+
+#: js/ui/calendar.js:476
+msgid "Next month"
+msgstr "Luna viitoare"
+
+#: js/ui/calendar.js:626
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:682
+msgid "Week %V"
+msgstr "Săptămâna %V"
+
+#: js/ui/calendar.js:896
+msgid "No Notifications"
+msgstr "Nu există notificări"
+
+#: js/ui/calendar.js:950
+msgid "Do Not Disturb"
+msgstr "Nu deranja"
+
+#: js/ui/calendar.js:971
+msgid "Clear"
+msgstr "Curăță"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "„%s” nu răspunde."
+
+#: js/ui/closeDialog.js:43
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Puteți alege să așteptați o scurtă vreme pentru a continua sau să forțați "
+"aplicația să se închidă complet."
+
+#: js/ui/closeDialog.js:70
+msgid "Force Quit"
+msgstr "Forțează închiderea"
+
+#: js/ui/closeDialog.js:73
+msgid "Wait"
+msgstr "Așteaptă"
+
+#: js/ui/components/automountManager.js:85
+msgid "External drive connected"
+msgstr "A fost conectat un disc extern"
+
+#: js/ui/components/automountManager.js:97
+msgid "External drive disconnected"
+msgstr "A fost deconectat un disc extern"
+
+#: js/ui/components/automountManager.js:206
+msgid "Unable to unlock volume"
+msgstr "Nu s-a putut debloca volumul"
+
+#: js/ui/components/automountManager.js:207
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Versiunea instalată udisks nu suportă configurarea PIM"
+
+#: js/ui/components/autorunManager.js:332
+#, javascript-format
+msgid "Open with %s"
+msgstr "Deschide cu %s"
+
+#: js/ui/components/networkAgent.js:92
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"În mod alternativ vă puteți conecta apăsând butonul „WPS” pe routerul "
+"dumneavoastră."
+
+#: js/ui/components/networkAgent.js:104 js/ui/status/network.js:258
+#: js/ui/status/network.js:349 js/ui/status/network.js:981
+msgid "Connect"
+msgstr "Conectare"
+
+#: js/ui/components/networkAgent.js:215
+msgid "Key"
+msgstr "Tastă"
+
+#: js/ui/components/networkAgent.js:253 js/ui/components/networkAgent.js:276
+msgid "Private key password"
+msgstr "Parolă pentru cheia privată"
+
+#: js/ui/components/networkAgent.js:274
+msgid "Identity"
+msgstr "Identitate"
+
+#: js/ui/components/networkAgent.js:288
+msgid "Service"
+msgstr "Serviciu"
+
+#: js/ui/components/networkAgent.js:317 js/ui/components/networkAgent.js:345
+#: js/ui/components/networkAgent.js:679 js/ui/components/networkAgent.js:700
+msgid "Authentication required"
+msgstr "Autentificarea este necesară"
+
+#: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Sunt necesare parole sau chei de criptare pentru a accesa rețeaua fără fir "
+"„%s”."
+
+#: js/ui/components/networkAgent.js:322 js/ui/components/networkAgent.js:684
+msgid "Wired 802.1X authentication"
+msgstr "Autentificare 802.1X prin cablu"
+
+#: js/ui/components/networkAgent.js:324
+msgid "Network name"
+msgstr "Nume de rețea"
+
+#: js/ui/components/networkAgent.js:329 js/ui/components/networkAgent.js:688
+msgid "DSL authentication"
+msgstr "Autentificare DSL"
+
+#: js/ui/components/networkAgent.js:336 js/ui/components/networkAgent.js:693
+msgid "PIN code required"
+msgstr "Este necesar codul PIN"
+
+#: js/ui/components/networkAgent.js:337 js/ui/components/networkAgent.js:694
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Este necesar codul PIN pentru dispozitivul mobil de bandă largă"
+
+#: js/ui/components/networkAgent.js:338
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:346 js/ui/components/networkAgent.js:685
+#: js/ui/components/networkAgent.js:689 js/ui/components/networkAgent.js:701
+#: js/ui/components/networkAgent.js:705
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Este necesară o parolă pentru conectarea la „%s”."
+
+#: js/ui/components/networkAgent.js:668 js/ui/status/network.js:1789
+msgid "Network Manager"
+msgstr "Gestionar de rețea"
+
+#: js/ui/components/networkAgent.js:704
+msgid "VPN password"
+msgstr "Parolă VPN"
+
+#: js/ui/components/polkitAgent.js:39
+msgid "Authentication Required"
+msgstr "Necesită autentificare"
+
+#: js/ui/components/polkitAgent.js:79
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:141
+msgid "Authenticate"
+msgstr "Autentificare"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:252 js/ui/shellMountOperation.js:402
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Nu a funcționat. Încercați din nou."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:822
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s este acum cunoscut ca %s"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/overviewControls.js:404
+msgid "Windows"
+msgstr "Ferestre"
+
+#: js/ui/dash.js:204 js/ui/dash.js:247
+msgid "Show Applications"
+msgstr "Arată aplicațiile"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:394
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:79
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:86
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:151
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:154
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:160
+msgid "Today"
+msgstr "Astăzi"
+
+#: js/ui/dateMenu.js:164
+msgid "Tomorrow"
+msgstr "Mâine"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:180
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Toată ziua"
+
+#: js/ui/dateMenu.js:231
+msgid "No Events"
+msgstr "Nu există evenimente"
+
+#: js/ui/dateMenu.js:348
+msgid "Add world clocks…"
+msgstr "Adaugă un fus orar…"
+
+#: js/ui/dateMenu.js:349
+msgid "World Clocks"
+msgstr "Fusuri orare"
+
+#: js/ui/dateMenu.js:629
+msgid "Loading…"
+msgstr "Se încarcă…"
+
+#: js/ui/dateMenu.js:639
+msgid "Go online for weather information"
+msgstr "Conectați-vă la internet pentru informații despre vreme"
+
+#: js/ui/dateMenu.js:641
+msgid "Weather information is currently unavailable"
+msgstr "Informațiile despre vreme nu sunt disponibile momentan"
+
+#: js/ui/dateMenu.js:651
+msgid "Weather"
+msgstr "Vreme"
+
+#: js/ui/dateMenu.js:653
+msgid "Select weather location…"
+msgstr "Selectează locația vremii…"
+
+#: js/ui/endSessionDialog.js:39
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Ieșire din sesiune pentru %s"
+
+#: js/ui/endSessionDialog.js:40
+msgctxt "title"
+msgid "Log Out"
+msgstr "Ieșire din sesiune"
+
+#: js/ui/endSessionDialog.js:43
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s va fi scos din sesiune automat în %d secundă."
+msgstr[1] "%s va fi scos din sesiune automat în %d secunde."
+msgstr[2] "%s va fi scos din sesiune automat în %d de secunde."
+
+#: js/ui/endSessionDialog.js:49
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Veți fi scos din sesiune automat în %d secundă."
+msgstr[1] "Veți fi scos din sesiune automat în %d secunde."
+msgstr[2] "Veți fi scos din sesiune automat în %d de secunde."
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "Ieșire din sesiune"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "Oprire"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instalarea actualizărilor și repornirea"
+
+#: js/ui/endSessionDialog.js:66
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistemul se va opri automat în %d secundă."
+msgstr[1] "Sistemul se va opri automat în %d secunde."
+msgstr[2] "Sistemul se va opri automat în %d de secunde."
+
+#: js/ui/endSessionDialog.js:70 js/ui/endSessionDialog.js:89
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instalează actualizările în așteptare"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "Oprire"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "Repornire"
+
+#: js/ui/endSessionDialog.js:82
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Instalează actualizările și repornește"
+
+#: js/ui/endSessionDialog.js:85
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistemul se va reporni automat în %d secundă."
+msgstr[1] "Sistemul se va reporni automat în %d secunde."
+msgstr[2] "Sistemul se va reporni automat în %d de secunde."
+
+#: js/ui/endSessionDialog.js:93
+msgctxt "button"
+msgid "Restart"
+msgstr "Repornire"
+
+#: js/ui/endSessionDialog.js:101
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Repornire și instalare actualizări"
+
+#: js/ui/endSessionDialog.js:104
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "Sistemul se va reporni automat în %d secundă."
+msgstr[1] "Sistemul se va reporni automat în %d secunde."
+msgstr[2] "Sistemul se va reporni automat în %d de secunde."
+
+#: js/ui/endSessionDialog.js:111 js/ui/endSessionDialog.js:132
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Repornește și instalează"
+
+#: js/ui/endSessionDialog.js:113
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instalează și oprește"
+
+#: js/ui/endSessionDialog.js:114
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Oprește după ce actualizările au fost instalate"
+
+#: js/ui/endSessionDialog.js:121
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Repornire și instalare înnoiri"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:126
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s va fi instalată după repornire. Instalarea înnoirii poate dura mult "
+"timp: asigurați-vă că ați făcut o copie de siguranță și că calculatorul este "
+"conectat la rețeaua electrică."
+
+#: js/ui/endSessionDialog.js:284
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Putere de baterie scăzută: conectați înainte de a instala actualizările."
+
+#: js/ui/endSessionDialog.js:293
+msgid "Some applications are busy or have unsaved work"
+msgstr "Unele aplicații sunt ocupate sau au muncă nesalvată"
+
+#: js/ui/endSessionDialog.js:298
+msgid "Other users are logged in"
+msgstr "Alți utilizatori sunt autentificați"
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Opțiuni de inițializare"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:686
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (la distanță)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:689
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (consolă)"
+
+#: js/ui/extensionDownloader.js:232
+msgid "Install"
+msgstr "Instalează"
+
+#: js/ui/extensionDownloader.js:238
+msgid "Install Extension"
+msgstr "Instalează extensia"
+
+#: js/ui/extensionDownloader.js:239
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Descărcați și instalați „%s” de la extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:252
+msgid "Extension Updates Available"
+msgstr "Actualizări de extensie disponibile"
+
+#: js/ui/extensionSystem.js:253
+msgid "Extension updates are ready to be installed."
+msgstr "Actualizările de extensie sunt gata de instalat."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Permite inhibarea scurtăturilor"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Aplicația %s dorește să inhibe scurtăturile"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "O aplicație vrea să inhibe scurtăturile"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Puteți restaura scurtăturile apăsând %s."
+
+#: js/ui/inhibitShortcutsDialog.js:100
+msgid "Deny"
+msgstr "Refuză"
+
+#: js/ui/inhibitShortcutsDialog.js:107
+msgid "Allow"
+msgstr "Permite"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Taste încete pornite"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Taste încete oprite"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Tocmai ați ținut apăsată tasta Shift pentru 8 secunde. Aceasta este "
+"scurtătra pentru funcționalitatea Taste încete, care afectează modul în care "
+"tastatura funcționează."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Taste persistente pornite"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Taste persistente oprite"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Tocmai ați apăsat tasta Shift de 5 ori la rând. Aceasta este scurtătura "
+"pentru funcționalitatea Taste persistente, care afectează modul în care "
+"tastatura funcționează."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Tocmai ați apăsat două taste simultan, sau apăsat tasta Shift de 5 ori la "
+"rând. Aceasta oprește funcționalitatea Taste persistente, care afectează "
+"modul în care tastatura funcționează."
+
+#: js/ui/kbdA11yDialog.js:54
+msgid "Leave On"
+msgstr "Lasă pornit"
+
+#: js/ui/kbdA11yDialog.js:54 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:1377
+msgid "Turn On"
+msgstr "Pornește"
+
+#: js/ui/kbdA11yDialog.js:62 js/ui/status/bluetooth.js:156
+#: js/ui/status/network.js:166 js/ui/status/network.js:350
+#: js/ui/status/network.js:1377 js/ui/status/network.js:1489
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Oprește"
+
+#: js/ui/kbdA11yDialog.js:62
+msgid "Leave Off"
+msgstr "Lasă oprit"
+
+#: js/ui/keyboard.js:226
+msgid "Region & Language Settings"
+msgstr "Configurări de regiune și limbă"
+
+#: js/ui/lookingGlass.js:676
+msgid "No extensions installed"
+msgstr "Nicio extensie instalată"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:734
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nu a emis nicio eroare."
+
+#: js/ui/lookingGlass.js:740
+msgid "Hide Errors"
+msgstr "Ascunde erorile"
+
+#: js/ui/lookingGlass.js:744 js/ui/lookingGlass.js:810
+msgid "Show Errors"
+msgstr "Afișează erorile"
+
+#: js/ui/lookingGlass.js:753
+msgid "Enabled"
+msgstr "Activat"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:756 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Dezactivat"
+
+#: js/ui/lookingGlass.js:758
+#: subprojects/extensions-app/data/ui/extension-row.ui:158
+msgid "Error"
+msgstr "Eroare"
+
+#: js/ui/lookingGlass.js:760
+msgid "Out of date"
+msgstr "Învechit"
+
+#: js/ui/lookingGlass.js:762
+msgid "Downloading"
+msgstr "Se descarcă"
+
+#: js/ui/lookingGlass.js:792
+msgid "View Source"
+msgstr "Vizualizează sursa"
+
+#: js/ui/lookingGlass.js:801
+msgid "Web Page"
+msgstr "Pagină Web"
+
+#: js/ui/main.js:290
+msgid "Logged in as a privileged user"
+msgstr "Autentificat ca utilizator privilegiat"
+
+#: js/ui/main.js:291
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Rularea unei extensii ca un utilizator privilegia ar trebui să fie evitată "
+"pentru motive de securitate. Dacă este posibil, ar trebui să vă "
+"autentificați ca un utilizator normal."
+
+#: js/ui/main.js:340
+msgid "Screen Lock disabled"
+msgstr "Blocarea ecranului este dezactivată"
+
+#: js/ui/main.js:341
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Blocarea ecranului necesită administratorul de afișaj GNOME."
+
+#: js/ui/messageTray.js:1443
+msgid "System Information"
+msgstr "Informații despre sistem"
+
+#: js/ui/mpris.js:207
+msgid "Unknown artist"
+msgstr "Artist necunoscut"
+
+#: js/ui/mpris.js:217
+msgid "Unknown title"
+msgstr "Titlu necunoscut"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:313
+msgid "Type to search"
+msgstr "Tastați pentru a căuta"
+
+#: js/ui/overviewControls.js:392
+msgid "Applications"
+msgstr "Aplicații"
+
+#: js/ui/overview.js:58
+msgid "Undo"
+msgstr "Anulează"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Prezentare generală"
+
+#: js/ui/padOsd.js:95
+msgid "New shortcut…"
+msgstr "Scurtătură nouă…"
+
+#: js/ui/padOsd.js:142
+msgid "Application defined"
+msgstr "Aplicație definită"
+
+#: js/ui/padOsd.js:143
+msgid "Show on-screen help"
+msgstr "Arată ajutorul pe ecran"
+
+#: js/ui/padOsd.js:144
+msgid "Switch monitor"
+msgstr "Comută monitorul"
+
+#: js/ui/padOsd.js:145
+msgid "Assign keystroke"
+msgstr "Alochează tasta apăsată"
+
+#: js/ui/padOsd.js:211
+msgid "Done"
+msgstr "Gata"
+
+#: js/ui/padOsd.js:716
+msgid "Edit…"
+msgstr "Editează…"
+
+#: js/ui/padOsd.js:758 js/ui/padOsd.js:875
+msgid "None"
+msgstr "Nespecificat"
+
+#: js/ui/padOsd.js:829
+msgid "Press a button to configure"
+msgstr "Apăsați un buton de configurat"
+
+#: js/ui/padOsd.js:830
+msgid "Press Esc to exit"
+msgstr "Apăsați Esc pentru a ieși"
+
+#: js/ui/padOsd.js:833
+msgid "Press any key to exit"
+msgstr "Apăsați orice tastă pentru a ieși"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:271
+msgid "Activities"
+msgstr "Activități"
+
+#: js/ui/panel.js:556
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistem"
+
+#: js/ui/panel.js:674
+msgid "Top Bar"
+msgstr "Bara de sus"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Rulează o comandă"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Apăsați ESC pentru a închide"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Repornirea nu este disponibilă în Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Se repornește…"
+
+#: js/ui/screenShield.js:224
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME trebuie să blocheze ecranul"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:264 js/ui/screenShield.js:634
+msgid "Unable to lock"
+msgstr "Blocare nereușită"
+
+#: js/ui/screenShield.js:265 js/ui/screenShield.js:635
+msgid "Lock was blocked by an application"
+msgstr "Blocarea a fost împiedicată de o aplicație"
+
+#: js/ui/screenshot.js:155
+msgid "Screenshot taken"
+msgstr "Captură de ecran efectuată"
+
+#: js/ui/search.js:825
+msgid "Searching…"
+msgstr "Se caută…"
+
+#: js/ui/search.js:827
+msgid "No results."
+msgstr "Niciun rezultat."
+
+#: js/ui/search.js:953
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "încă %d"
+msgstr[1] "încă %d"
+msgstr[2] "încă %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Caută"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Copiere"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Lipire"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Afișează text"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ascunde text"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock este activat."
+
+#: js/ui/shellMountOperation.js:285
+msgid "Hidden Volume"
+msgstr "Volum ascuns"
+
+#: js/ui/shellMountOperation.js:288
+msgid "Windows System Volume"
+msgstr "Volum de sistem Windows"
+
+#: js/ui/shellMountOperation.js:291
+msgid "Uses Keyfiles"
+msgstr "Utilizează fișiere cheie"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:298
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Pentru a debloca un volum care utilizează fișiere cheie, utilizați în schimb "
+"utilitatea <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:306
+msgid "PIM Number"
+msgstr "Număr PIM"
+
+#: js/ui/shellMountOperation.js:365
+msgid "Remember Password"
+msgstr "Memorează parola"
+
+#: js/ui/shellMountOperation.js:380
+msgid "Unlock"
+msgstr "Deblochează"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:391
+#, javascript-format
+msgid "Open %s"
+msgstr "Deschide %s"
+
+#: js/ui/shellMountOperation.js:423
+msgid "The PIM must be a number or empty."
+msgstr "PIM-ul trebuie să fie un număr sau gol."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:465
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Nu s-a putut porni %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:467
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nu s-a putut găsi aplicația %s"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Accesibilitate"
+
+#: js/ui/status/accessibility.js:48
+msgid "Zoom"
+msgstr "Zoom"
+
+#: js/ui/status/accessibility.js:55
+msgid "Screen Reader"
+msgstr "Cititor de ecran"
+
+#: js/ui/status/accessibility.js:59
+msgid "Screen Keyboard"
+msgstr "Tastatură virtuală"
+
+#: js/ui/status/accessibility.js:63
+msgid "Visual Alerts"
+msgstr "Alerte vizuale"
+
+#: js/ui/status/accessibility.js:66
+msgid "Sticky Keys"
+msgstr "Taste persistente"
+
+#: js/ui/status/accessibility.js:69
+msgid "Slow Keys"
+msgstr "Taste încete"
+
+#: js/ui/status/accessibility.js:72
+msgid "Bounce Keys"
+msgstr "Taste fără repetiție"
+
+#: js/ui/status/accessibility.js:75
+msgid "Mouse Keys"
+msgstr "Butoanele mausului"
+
+#: js/ui/status/accessibility.js:134
+msgid "High Contrast"
+msgstr "Contrast ridicat"
+
+#: js/ui/status/accessibility.js:176
+msgid "Large Text"
+msgstr "Text mare"
+
+#: js/ui/status/bluetooth.js:40
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:49 js/ui/status/network.js:652
+msgid "Bluetooth Settings"
+msgstr "Configurări Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:148
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d conectată"
+msgstr[1] "%d conectate"
+msgstr[2] "%d conectate"
+
+#: js/ui/status/bluetooth.js:152
+msgid "Bluetooth Off"
+msgstr "Bluetooth oprit"
+
+#: js/ui/status/bluetooth.js:154
+msgid "Bluetooth On"
+msgstr "Bluetooth pornit"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Luminozitate"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Clic simplu"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Clic dublu"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Trage"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Clic secundar"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Clic întârziat"
+
+#: js/ui/status/keyboard.js:829
+msgid "Keyboard"
+msgstr "Tastatură"
+
+#: js/ui/status/keyboard.js:846
+msgid "Show Keyboard Layout"
+msgstr "Afișează aranjamentul tastaturii"
+
+#: js/ui/status/location.js:234 js/ui/status/location.js:267
+msgid "Location Enabled"
+msgstr "Locația activată"
+
+#: js/ui/status/location.js:235 js/ui/status/location.js:268
+msgid "Disable"
+msgstr "Dezactivează"
+
+#: js/ui/status/location.js:236
+msgid "Privacy Settings"
+msgstr "Configurări de confidențialitate"
+
+#: js/ui/status/location.js:266
+msgid "Location In Use"
+msgstr "Locație utilizată"
+
+#: js/ui/status/location.js:270
+msgid "Location Disabled"
+msgstr "Locația dezactivată"
+
+#: js/ui/status/location.js:271
+msgid "Enable"
+msgstr "Activează"
+
+#: js/ui/status/location.js:398
+msgid "Allow location access"
+msgstr "Permite accesul la locație"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:400
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Aplicația %s dorește să vă acceseze locația"
+
+#: js/ui/status/location.js:410
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Accesul la locație poate fi schimbat oricând din configurările de "
+"confidențialitate."
+
+#: js/ui/status/network.js:72
+msgid "<unknown>"
+msgstr "<necunoscut>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:455 js/ui/status/network.js:1406
+#, javascript-format
+msgid "%s Off"
+msgstr "%s oprit"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:458
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s conectat"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:463
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s este negestionat"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:466
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "Se deconectează %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:473 js/ui/status/network.js:1398
+#, javascript-format
+msgid "%s Connecting"
+msgstr "Se conectează %s"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:476
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s necesită autentificare"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:484
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Firmware lipsă pentru %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:488
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s indisponibil"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:491
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "Conectarea la %s a eșuat"
+
+#: js/ui/status/network.js:503
+msgid "Wired Settings"
+msgstr "Configurări pentru conexiune cu fir"
+
+#: js/ui/status/network.js:550
+msgid "Mobile Broadband Settings"
+msgstr "Configurări de rețea de bandă largă mobilă"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:619 js/ui/status/network.js:1403
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "Hardware %s dezactivat"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:623
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s dezactivat"
+
+#: js/ui/status/network.js:664
+msgid "Connect to Internet"
+msgstr "Conectează la Internet"
+
+#: js/ui/status/network.js:873
+msgid "Airplane Mode is On"
+msgstr "Modul avion este pornit"
+
+#: js/ui/status/network.js:874
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi este dezactivat când modul avion este pornit."
+
+#: js/ui/status/network.js:875
+msgid "Turn Off Airplane Mode"
+msgstr "Oprește modul avion"
+
+#: js/ui/status/network.js:884
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi este oprit"
+
+#: js/ui/status/network.js:885
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Wi-Fi trebuie pornit pentru a se putea face conexiunea la o rețea."
+
+#: js/ui/status/network.js:886
+msgid "Turn On Wi-Fi"
+msgstr "Pornește Wi-Fi"
+
+#: js/ui/status/network.js:911
+msgid "Wi-Fi Networks"
+msgstr "Rețele Wi-Fi"
+
+#: js/ui/status/network.js:913
+msgid "Select a network"
+msgstr "Selectați o rețea"
+
+#: js/ui/status/network.js:945
+msgid "No Networks"
+msgstr "Nicio rețea"
+
+#: js/ui/status/network.js:966 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "Folosește un întrerupător hardware pentru a opri"
+
+#: js/ui/status/network.js:1267
+msgid "Select Network"
+msgstr "Selectați rețeaua"
+
+#: js/ui/status/network.js:1273
+msgid "Wi-Fi Settings"
+msgstr "Configurări Wi-Fi"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1394
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "Hotspot %s activ"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1409
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s nu este conectat"
+
+#: js/ui/status/network.js:1506
+msgid "connecting…"
+msgstr "se conectează…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1509
+msgid "authentication required"
+msgstr "necesită autentificare"
+
+#: js/ui/status/network.js:1511
+msgid "connection failed"
+msgstr "conexiune eșuată"
+
+#: js/ui/status/network.js:1562
+msgid "VPN Settings"
+msgstr "Configurări VPN"
+
+#: js/ui/status/network.js:1579
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1589
+msgid "VPN Off"
+msgstr "VPN oprit"
+
+#: js/ui/status/network.js:1650 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Configurări de rețea"
+
+#: js/ui/status/network.js:1678
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s conexiune cu fir"
+msgstr[1] "%s conexiuni cu fir"
+msgstr[2] "%s de conexiuni cu fir"
+
+#: js/ui/status/network.js:1682
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s conexiune fără fir"
+msgstr[1] "%s conexiuni fără fir"
+msgstr[2] "%s de conexiuni fără fir"
+
+#: js/ui/status/network.js:1686
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s conexiune cu modem"
+msgstr[1] "%s conexiuni cu modem"
+msgstr[2] "%s de conexiuni cu modem"
+
+#: js/ui/status/network.js:1830
+msgid "Connection failed"
+msgstr "Conexiune eșuată"
+
+#: js/ui/status/network.js:1831
+msgid "Activation of network connection failed"
+msgstr "Activarea conexiunii de rețea a eșuat"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Lumina de noapte este dezactivată"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Lumina de noapte este activată"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Reia"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Dezactivează până mâine"
+
+#: js/ui/status/power.js:51 js/ui/status/powerProfiles.js:57
+msgid "Power Settings"
+msgstr "Configurări consum"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr "Complet încărcată"
+
+#: js/ui/status/power.js:74
+msgid "Not Charging"
+msgstr "Nu încarcă"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:77 js/ui/status/power.js:83
+msgid "Estimating…"
+msgstr "Se estimează…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d rămase (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:97
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d până este plină (%d%%)"
+
+#. The icon label
+#: js/ui/status/power.js:145
+#, javascript-format
+msgid "%d %%"
+msgstr "%d%%"
+
+#: js/ui/status/powerProfiles.js:19
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Performanță"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Echilibrată"
+
+#: js/ui/status/powerProfiles.js:21
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Economizor de energie"
+
+#: js/ui/status/remoteAccess.js:38
+msgid "Screen is Being Shared"
+msgstr "Ecranul este partajat"
+
+#: js/ui/status/remoteAccess.js:40
+msgid "Turn off"
+msgstr "Oprește"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Modul avion este pornit"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "Blocare"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "Oprire / Deautentificare"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "Suspendare"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "Repornește…"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "Oprire…"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Ieșire din sesiune"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "Comută utilizatorul…"
+
+#: js/ui/status/thunderbolt.js:262
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Dispozitiv Thunderbolt necunoscut"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Un nou dispozitiv a fost detectat în timp ce erați plecat. Deconectați și "
+"reconectați dispozitivul pentru a începe utilizarea lui."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr "Dispozitiv Thunderbolt neautorizat"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Un nou dispozitiv a fost detectat și trebuie autorizat de un administrator."
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Eroare de autorizare Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nu s-a putut autoriza dispozitivul Thunderbolt: %s"
+
+#: js/ui/status/volume.js:160
+msgid "Volume changed"
+msgstr "Volumul a fost schimbat"
+
+#: js/ui/status/volume.js:222
+msgid "Volume"
+msgstr "Volum"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:17
+msgid "Mirror"
+msgstr "Oglindește"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:22
+msgid "Join Displays"
+msgstr "Unește afișajele"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:27
+msgid "External Only"
+msgstr "Numai extern"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:32
+msgid "Built-in Only"
+msgstr "Numai încorporat"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:371
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:377
+msgid "Swipe up to unlock"
+msgstr "Glisați în sus pentru a debloca"
+
+#: js/ui/unlockDialog.js:378
+msgid "Click or press a key to unlock"
+msgstr "Apăsați clic sau o tastă pentru a debloca"
+
+#: js/ui/unlockDialog.js:556
+msgid "Unlock Window"
+msgstr "Deblochează fereastră"
+
+#: js/ui/unlockDialog.js:565
+msgid "Log in as another user"
+msgstr "Intră în sesiune ca utilizator diferit"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Bine ați venit la GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Dacă doriți să aflați despre împrejurimi, consultați turul Gnome."
+
+#: js/ui/welcomeDialog.js:45
+msgid "No Thanks"
+msgstr "Nu, merci"
+
+#: js/ui/welcomeDialog.js:50
+msgid "Take Tour"
+msgstr "Începe turul"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "„%s” este gata"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:63
+msgid "Keep these display settings?"
+msgstr "Doriți să păstrați aceste configurări de afișaj?"
+
+#. Translators: this and the following message should be limited in length,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Restaurează configurările"
+
+#: js/ui/windowManager.js:75
+msgid "Keep Changes"
+msgstr "Păstrați modificările"
+
+#: js/ui/windowManager.js:94
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Schimbările vor fi restaurare în %d secundă"
+msgstr[1] "Schimbările vor fi restaurare în %d secunde"
+msgstr[2] "Schimbările vor fi restaurare în %d de secunde"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:550
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:27
+msgid "Hide"
+msgstr "Ascunde"
+
+#: js/ui/windowMenu.js:34
+msgid "Restore"
+msgstr "Restaurează"
+
+#: js/ui/windowMenu.js:38
+msgid "Maximize"
+msgstr "Maximizează"
+
+#: js/ui/windowMenu.js:45
+msgid "Move"
+msgstr "Mută"
+
+#: js/ui/windowMenu.js:51
+msgid "Resize"
+msgstr "Redimensionează"
+
+#: js/ui/windowMenu.js:58
+msgid "Move Titlebar Onscreen"
+msgstr "Mută bara de titlu pe ecran"
+
+#: js/ui/windowMenu.js:63
+msgid "Always on Top"
+msgstr "Întotdeauna deasupra"
+
+#: js/ui/windowMenu.js:82
+msgid "Always on Visible Workspace"
+msgstr "Întotdeauna pe spațiul de lucru vizibil"
+
+#: js/ui/windowMenu.js:96
+msgid "Move to Workspace Left"
+msgstr "Mută pe spațiul de lucru din stânga"
+
+#: js/ui/windowMenu.js:102
+msgid "Move to Workspace Right"
+msgstr "Mută pe spațiul de lucru din dreapta"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Up"
+msgstr "Mută fereastra pe spațiul de lucru de mai sus"
+
+#: js/ui/windowMenu.js:114
+msgid "Move to Workspace Down"
+msgstr "Mută fereastra pe spațiul de lucru de mai jos"
+
+#: js/ui/windowMenu.js:132
+msgid "Move to Monitor Up"
+msgstr "Mută fereastra cu un monitor mai sus"
+
+#: js/ui/windowMenu.js:141
+msgid "Move to Monitor Down"
+msgstr "Mută fereastra cu un monitor mai jos"
+
+#: js/ui/windowMenu.js:150
+msgid "Move to Monitor Left"
+msgstr "Mută fereastra cu un monitor spre stânga"
+
+#: js/ui/windowMenu.js:159
+msgid "Move to Monitor Right"
+msgstr "Mută fereastra cu un monitor spre dreapta"
+
+#: js/ui/windowMenu.js:167
+msgid "Close"
+msgstr "Închide"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Calendar Evolution"
+
+#: src/main.c:419 subprojects/extensions-tool/src/main.c:317
+msgid "Print version"
+msgstr "Tipărește versiunea"
+
+#: src/main.c:425
+msgid "Mode used by GDM for login screen"
+msgstr "Mod folosit de GDM pentru meniul de autentificare"
+
+#: src/main.c:431
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Utilizează un mod specific, de exemplu „gdm” pentru ecranul de logare"
+
+#: src/main.c:437
+msgid "List possible modes"
+msgstr "Enumeră câmpurile care pot fi afișate"
+
+#: src/shell-app.c:298
+msgctxt "program"
+msgid "Unknown"
+msgstr "Necunoscut"
+
+#: src/shell-app.c:549
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Nu s-a putut lansa „%s”"
+
+#: src/shell-keyring-prompt.c:731
+msgid "Passwords do not match."
+msgstr "Parolele nu se potrivesc."
+
+#: src/shell-keyring-prompt.c:739
+msgid "Password cannot be blank"
+msgstr "Parola nu poate fi goală"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Fereastra de autentificare a fost închisă de către utilizator"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:217
+#: subprojects/extensions-app/data/ui/extensions-window.ui:56
+msgid "Extensions"
+msgstr "Extensii"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:218
+msgid "Manage your GNOME Extensions"
+msgstr "Administrați Exensiile GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "Proiectul GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Exensii GNOME gestionează actualizarea extensiilor, configurarea "
+"preferințelor extensiei și eliminarea sau dezactivarea extensiilor nedorite."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Configurarea extensiilor pentru Vizualizatorul activităților GNOME"
+
+#: subprojects/extensions-app/js/main.js:142
+#: subprojects/extensions-app/js/main.js:152
+msgid "No Matches"
+msgstr "Nu există potriviri"
+
+#: subprojects/extensions-app/js/main.js:179
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Se elimină „%s”?"
+
+#: subprojects/extensions-app/js/main.js:180
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Dacă eliminați extensia, trebuie să vă întoarceți să o descărcați dacă "
+"doriți să o activați din nou"
+
+#: subprojects/extensions-app/js/main.js:184
+msgid "Remove"
+msgstr "Elimină"
+
+#: subprojects/extensions-app/js/main.js:216
+msgid "translator-credits"
+msgstr ""
+"Florentina Mușat <florentina [dot] musat [dot] 28 [at] gmail [dot] com>, "
+"2020-2021\n"
+"Daniel Șerbănescu <daniel [at] serbanescu [dot] dk>, 2021"
+
+#: subprojects/extensions-app/js/main.js:344
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d extensie va fi actualizată la următoarea autentificare."
+msgstr[1] "%d extensii vor fi actualizate la următoarea autentificare."
+msgstr[2] "%d de extensii vor fi actualizate la următoarea autentificare."
+
+#: subprojects/extensions-app/js/main.js:494
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Extensia nu este compatibilă cu versiunea GNOME curentă"
+
+#: subprojects/extensions-app/js/main.js:497
+msgid "The extension had an error"
+msgstr "Extensia a avut o eroare"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:83
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Descriere"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:104
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Versiune"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:131
+msgid "Author"
+msgstr "Autor"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:185
+msgid "Website"
+msgstr "Pagină web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:201
+msgid "Remove…"
+msgstr "Elimină…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:7
+msgid "Help"
+msgstr "Ajutor"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:11
+msgid "About Extensions"
+msgstr "Despre extensii"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:27
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Pentru a găsi și adăuga extensii, vizitați <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:34
+msgid "Warning"
+msgstr "Avertisment"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:44
+msgid ""
+"Extensions can cause system issues, including performance problems. If you "
+"encounter problems with your system, it is recommended to disable all "
+"extensions."
+msgstr ""
+"Extensiile pot cauza probleme de sistem, inclusiv probleme de performanță. "
+"Dacă întâlniți probleme cu sistemul, este recomandat să dezactivați toate "
+"extensiile."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:150
+msgid "Manually Installed"
+msgstr "Instalat manual"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:174
+msgid "Built-In"
+msgstr "Încorporat"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:217
+msgid "No Installed Extensions"
+msgstr "Nu sunt extensii instalate"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:252
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Nu a fost posibil să se obțină lista extensiilor instalate. Asigurați-vă că "
+"sunteți autentificat la GNOME și încercați din nou."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:288
+msgid "Extension Updates Ready"
+msgstr "Actualizările de extensie sunt gata"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:303
+msgid "Log Out…"
+msgstr "Deautentificare…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Extensia nouă a fost creată cu succes la %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Numele ar trebui să fie un șir foarte scurt (ideal descriptiv).\n"
+"Exemple: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Nume"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Descrierea este o explicație într-o singură propoziție despre ce face "
+"extensia.\n"
+"Exemple: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID-ul este un identificator global unic pentru extensie.\n"
+"Aceasta ar trebui să fie în formatul unei adrese de email "
+"(clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Alege unul din șabloanele existente:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Șablon"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Identificatorul unic al noii extensii"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NUME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Numele vizibil de utilizator pentru noua extensie"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIERE"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "O scurtă descriere despre ce face extensia"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ȘABLON"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Șablonul de utilizat pentru extensia nouă"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Introdu informațiile extensiei interactiv"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Creează o nouă extensie"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Argumente necunoscute"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID-ul, numele și descrierea sunt necesare"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Nu s-a putut conecta la Shell GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Extensia „%s” nu există\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Dezactivează o extensie"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:97
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Niciun UUID dat"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:102
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Mai mult de un UUID dat"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Activează o extensie"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Extensia „%s” nu există\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Arată informații despre extensii"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Suprascrie o extensie existentă"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PACHET_EXTENSIE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Instalează un pachet de extensii"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Niciun pachet de extensii specificat"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Mai mult de un pachet de extensii specificat"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Arată extensiile instalate de utilizator"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Arată extensiile instalate de sistem"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Arată extensiile activate"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Arată extensiile dezactivate"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Arată extensiile cu preferințe"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Arată extensiile cu actualizări"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Tipărește detaliile extensiei"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Listează extensiile instalate"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FIȘIER"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Sursă adițională de inclus în pachet"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMĂ"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "O schemă GSettings care ar trebui inclusă"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTOR"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Directorul unde se găsesc traducerile"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMENIU"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Domeniu gettext de folosit pentru traduceri"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Suprascrie un pachet existent"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Directorul unde pachetul ar trebui creat"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "DIRECTOR_SURSĂ"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Creează un pachet de extensii"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Mai mult de un director sursă specificat"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Extensia „%s” nu are preferințe\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:79
+msgid "Opens extension preferences"
+msgstr "Deschide preferințele extensiei"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Restabilește o extensie"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Nu se pot dezinstala extensiile de sistem\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Nu s-a putut dezinstala „%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Dezinstalează o extensie"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Nu tipări mesajele eroare"
+
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell"
+msgstr "Nu s-a putut conecta la Shell GNOME"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Cale"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Autor original"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stare"
+
+#: subprojects/extensions-tool/src/main.c:290
+msgid "“version” takes no arguments"
+msgstr "„version” nu primește argumente"
+
+#: subprojects/extensions-tool/src/main.c:292
+#: subprojects/extensions-tool/src/main.c:312
+msgid "Usage:"
+msgstr "Utilizare:"
+
+#: subprojects/extensions-tool/src/main.c:295
+msgid "Print version information and exit."
+msgstr "Tipărește informațiile versiunii și ieși."
+
+#: subprojects/extensions-tool/src/main.c:310
+#: subprojects/extensions-tool/src/main.c:313
+msgid "COMMAND"
+msgstr "COMANDĂ"
+
+#: subprojects/extensions-tool/src/main.c:313
+msgid "[ARGS…]"
+msgstr "[ARGUMENTE…]"
+
+#: subprojects/extensions-tool/src/main.c:315
+msgid "Commands:"
+msgstr "Comenzi:"
+
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Print help"
+msgstr "Tipărește ajutorul"
+
+#: subprojects/extensions-tool/src/main.c:318
+msgid "Enable extension"
+msgstr "Activează extensia"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Disable extension"
+msgstr "Dezactivează extensia"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Reset extension"
+msgstr "Restabilește extensia"
+
+#: subprojects/extensions-tool/src/main.c:321
+msgid "Uninstall extension"
+msgstr "Dezinstalează extensia"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "List extensions"
+msgstr "Listează extensiile"
+
+#: subprojects/extensions-tool/src/main.c:323
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Show extension info"
+msgstr "Arată informații despre extensie"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Open extension preferences"
+msgstr "Deschide preferințele extensiei"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "Create extension"
+msgstr "Creează extensie"
+
+#: subprojects/extensions-tool/src/main.c:327
+msgid "Package extension"
+msgstr "Împachetează extensia"
+
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Install extension bundle"
+msgstr "Instalează pachetul de extensii"
+
+#: subprojects/extensions-tool/src/main.c:330
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Utilizați „%s” pentru a obține ajutor detaliat.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Simplu"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "O extensie goală"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indicator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Adaugă o iconiță la bara de sus"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u rezultat"
+msgstr[1] "%u rezultate"
+msgstr[2] "%u de rezultate"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u intrare"
+msgstr[1] "%u intrări"
+msgstr[2] "%u de intrări"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Sunete de sistem"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Activează API-ul de introspecție"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Activează un API D-Bus care permite introspecția stării aplicației shell."
+
+#~ msgid "Minimize"
+#~ msgstr "Minimizează"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Demaximizează"
+
+#~ msgid "App Picker View"
+#~ msgstr "Mod de afișare selector de aplicații"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Indicele modului de afișare selectat în selectorul de aplicații."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Aplicațiile utilizate frecvent vor fi afișate aici"
+
+#~ msgid "Frequent"
+#~ msgstr "Frecvente"
+
+#~ msgid "All"
+#~ msgstr "Toate"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %B %-d"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %B %-d, %Y"
+
+#~ msgid "Copy Error"
+#~ msgstr "Eroare la copiere"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Navighați în Aplicații"
+
+#~ msgid "Next"
+#~ msgstr "Următorul"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Autentificare"
+
+#~ msgid "Rename"
+#~ msgstr "Redenumește"
+
+#~ msgid "Password:"
+#~ msgstr "Parolă:"
+
+#~ msgid "Type again:"
+#~ msgstr "Tastați din nou:"
+
+#~ msgid "Password: "
+#~ msgstr "Parolă: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Autentificare cerută de către rețeaua fără fir"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Parolă pentru rețeaua mobilă de bandă largă"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %B %d"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d mesaj nou"
+#~ msgstr[1] "%d mesaje noi"
+#~ msgstr[2] "%d de mesaje noi"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d notificare"
+#~ msgstr[1] "%d notificări"
+#~ msgstr[2] "%d de notificări"
+
+#~ msgid "Off"
+#~ msgstr "Oprit"
+
+#~ msgid "On"
+#~ msgstr "Pornit"
+
+#~ msgid "Account Settings"
+#~ msgstr "Configurări de cont"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Blocare de orientare"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Combinație de taste care întrerupe și reia animațiile în execuțue, pentru "
+#~ "depanare"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Ce tastatură să fie folosită"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Tipul de tastatură de folosit."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~ msgid "evolution"
+#~ msgstr "evolution"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr ""
+#~ "S-a produs o eroare la încărcarea ferestrei cu preferințe pentru %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s toată ziua."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, apoi %s mai târziu."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, apoi %s, urmat de %s mai târziu."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Percepută ca %s."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Vizualizatorul activităților GNOME (compozitor wayland)"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Afișează data săptămânii în calendar"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Dacă este adevărat, afișează săptămâna ISO în calendar."
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Redirecționează autentificarea web"
+
+#~ msgid "Clear section"
+#~ msgstr "Curăță secțiunea"
+
+#~ msgid "Events"
+#~ msgstr "Evenimente"
+
+#~ msgid "Notifications"
+#~ msgstr "Notificări"
+
+#~ msgid "Hide tray"
+#~ msgstr "Ascunde zona de notificare"
+
+#~ msgid "Status Icons"
+#~ msgstr "Iconițe de stare"
+
+#~ msgid "Not In Use"
+#~ msgstr "Neutilizată"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Folosește pe post de conexiunea la internet"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "D"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "L"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Ma"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Mi"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "J"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "V"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "S"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Nimic programat"
+
+#~ msgid "This week"
+#~ msgstr "Săptămâna aceasta"
+
+#~ msgid "Next week"
+#~ msgstr "Săptămâna viitoare"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Dispozitive care pot fi eliminate"
+
+#~ msgid "Eject"
+#~ msgstr "Scoate"
+
+#~ msgid "Invitation"
+#~ msgstr "Invitație"
+
+#~ msgid "Call"
+#~ msgstr "Apel"
+
+#~ msgid "File Transfer"
+#~ msgstr "Transfer de fișiere"
+
+#~ msgid "Unmute"
+#~ msgstr "Activează sunetul"
+
+#~ msgid "Mute"
+#~ msgstr "Mut"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Invitație către %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s vă invită să vă alăturați %s"
+
+#~ msgid "Decline"
+#~ msgstr "Refuză"
+
+#~ msgid "Accept"
+#~ msgstr "Acceptă"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Apel video de la %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Apel de la %s"
+
+#~ msgid "Answer"
+#~ msgstr "Răspunde"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s vă trimite %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s ar dori permisiunea să știe când sunteți online"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Autentificare eșuată"
+
+#~ msgid "Encryption error"
+#~ msgstr "Eroare de criptare"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Certificat nefurnizat"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Certificat nesigur"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certificat expirat"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Certificat neactivat"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Numele de gazdă al certificatului nu se potrivește"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Amprenta certificatului nu se potrivește"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Certificat subsemnat"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Starea este setată pe deconectat"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Certificatul este invalid"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Conexiunea a fost refuzată"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Conexiunea nu poate fi stabilită"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Conexiunea a fost pierdută"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Aceast cont este deja conectat la server"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr ""
+#~ "Conexiunea a fost înlocuită de o nouă conexiune folosind aceeași resursă"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Acest cont există deja pe server"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Serverul este acum prea încărcat să accepte conexiunea"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Certificatul a fost revocat"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Certificatul folosește un algoritm de cifrare nesigur sau este slab "
+#~ "criptografic"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Lungimea certificatului serverului sau adâncimea lanțului de certificare "
+#~ "a serverului depășește limita impusă de către biblioteca criptografică"
+
+#~ msgid "Internal error"
+#~ msgstr "Eroare internă"
+
+#, fuzzy
+#~ msgid "View account"
+#~ msgstr "Editează contul"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Deschide calendarul"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Setări pentru dată și oră"
+
+#~ msgid "Open"
+#~ msgstr "Deschide"
+
+#, fuzzy
+#~ msgid "Notification Settings"
+#~ msgstr "Notificări"
+
+#~ msgid "No Messages"
+#~ msgstr "Nici un mesaj"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d dispozitiv conectat"
+#~ msgstr[1] "%d dispozitive conectate"
+#~ msgstr[2] "%d de dispozitive conectate"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Baterie"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Mod avion"
+
+#~ msgid "unavailable"
+#~ msgstr "indisponibil"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "Trimis pe <b>%A</b>. <b>%d %B</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "Trimis pe <b>%A</b>. <b>%d %B</b>"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Cerere de autorizare de la %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Dispozitivul %s vrea să se asocieze cu acest calculator"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Dispozitivul %s dorește acces la serviciul „%s”"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Permite accesul doar de această dată"
+
+#~ msgid "Reject"
+#~ msgstr "Respingere"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Confirmarea asocierii pentru %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Confirmați dacă cheia de acces „%06d” se potrivește cu cea de pe "
+#~ "dispozitiv."
+
+#~ msgid "Does not match"
+#~ msgstr "Nu se potrivește"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Cererea de asociere pentru %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Introduceți PIN-ul menționat pe dispozitiv."
+
+#~ msgid "OK"
+#~ msgstr "OK"
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Frecvența de cadre utilizată pentru înregistrările de ecran."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "Frecvența de cadre a capturii de ecran realizată de către înregistratorul "
+#~ "de ecran al GNOME Shell, în cadre pe secundă."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr ""
+#~ "Linia de asamblare gstreamer folosită pentru a coda captura de ecran"
+
+#, fuzzy
+#~| msgid ""
+#~| "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~| "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~| "pad where the recorded video is recorded. It will normally have a "
+#~| "unconnected source pad; output from that pad will be written into the "
+#~| "output file. However the pipeline can also take care of its own output - "
+#~| "this might be used to send the output to an icecast server via "
+#~| "shout2send or similar. When unset or set to an empty value, the default "
+#~| "pipeline will be used. This is currently 'videorate ! vp8enc quality=10 "
+#~| "speed=2 threads=%T ! queue ! webmmux' and records to WEBM using the VP8 "
+#~| "codec. %T is used as a placeholder for a guess at the optimal thread "
+#~| "count on the system."
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Definește linia de asamblare GStreamer utilizată pentru a coda "
+#~ "înregistrările. Folosește aceeași sintaxă ca la gst-launch. Linia de "
+#~ "asamblare ar trebui să aibă un sink pad neconectat unde va fi înregistrat "
+#~ "ecranul. În mod normal va avea un source pad neconectat și o ieșire care "
+#~ "va fi scrisă în fișierul de ieșire. Cu toate acestea, linia de asamblare "
+#~ "poate să aibă grijă de ieșirea proprie - aceasta ar putea fi utilizată "
+#~ "pentru a trimite ieșirea la un server icecast, shout2send sau un alt "
+#~ "server similar. Când este dezactivată, sau are o valoare vidă, se "
+#~ "utilizează linia de asamblare implicită. Aceasta este acum definită ca "
+#~ "„videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux” și "
+#~ "înregistrează în WEBM folosind codecul VP8. %T este folosit ca înlocuitor "
+#~ "pentru o valoare estimativă a numărului optim de fire de execuție pe "
+#~ "acest sistem."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr ""
+#~ "Extensia de fișier utilizată pentru stocarea înregistrărilor de ecran"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "Numele de fișier ale înregistrărilor de ecran vor fi unice, bazate pe "
+#~ "data curentă și vor folosi această extensie. Ar trebui să fie modificată "
+#~ "când se înregistrează într-un format de container diferit."
+
+#, fuzzy
+#~| msgid "Power Off"
+#~ msgid "Power"
+#~ msgstr "Oprire"
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Apăsați „Ieșire din sesiune” pentru a închide aceste aplicații și a ieși "
+#~ "din sistem."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Deconectare din sistem."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr ""
+#~ "Apăsați „Oprire” pentru a închide aceste aplicații și a opri sistemul."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Se oprește sistemul."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Apăsați „Repornire” pentru a închide aceste aplicații și a reporni "
+#~ "sistemul."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Se repornește sistemul."
+
+#~ msgid "Visibility"
+#~ msgstr "Vizibilitate"
+
+#, fuzzy
+#~| msgid "Set up a New Device..."
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Configurează un dispozitiv nou..."
+
+#, fuzzy
+#~| msgid "Send Files..."
+#~ msgid "Send Files…"
+#~ msgstr "Trimite fișiere..."
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Volum, rețea, baterie"
+
+#~ msgid "disabled"
+#~ msgstr "dezactivat"
+
+#~ msgid "Wired"
+#~ msgstr "Cu fir"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Dacă este adevărat, afișează data în ceas, lângă oră."
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Dacă este adevărat, afișează secundele lângă oră."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Afișează data în ceas"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Afișează ora cu secunde"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "În mod normal shell-ul monitorizează aplicațiile active pentru a le "
+#~ "prezenta cele mai folosite (de ex: în lansatoare de aplicații). Deși "
+#~ "aceste date vor fi păstrate private este posibil să vreți să dezactivați "
+#~ "această funcționalitate din motive de intimitate. Dezactivarea nu va "
+#~ "șterge datele deja salvate."
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Dacă să fie colectate detalii despre utilizarea aplicațiilor"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "furnizori OpenSearch dezactivați"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Autentificare"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "APLICAȚII"
+
+#~ msgid "SETTINGS"
+#~ msgstr "SETĂRI"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Available"
+#~ msgstr "Disponibil"
+
+#~ msgid "Away"
+#~ msgstr "Plecat"
+
+#~ msgid "Busy"
+#~ msgstr "Ocupat"
+
+#~ msgid "CONTACTS"
+#~ msgstr "CONTACTE"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %e %b, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %e %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e %b, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "ELEMENTE RECENTE"
+
+#~ msgid "tray"
+#~ msgstr "zonă de notificare"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Nu s-a putut demonta „%s”"
+
+#~ msgid "Retry"
+#~ msgstr "Reîncearcă"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "LOCURI & DISPOZITIVE"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Parolă greșită, încercați din nou"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Setări acces universal"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Explorează fișiere..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "Eroare la explorarea dispozitivului"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "Dispozitivul solicitat nu poate fi explorat, eroarea este „%s”"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Setări tastatură"
+
+#~ msgid "cable unplugged"
+#~ msgstr "cablu deconectat"
+
+#~ msgid "More..."
+#~ msgstr "Mai multe..."
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Ethernet automat"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Rețea de bandă largă automată"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Dial-up automat"
+
+#~ msgid "Auto %s"
+#~ msgstr "%s automat"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Bluetooth automat"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Rețea fără fir automată"
+
+#~ msgid "Wireless"
+#~ msgstr "Fără fir"
+
+#~ msgid "VPN Connections"
+#~ msgstr "Conexiuni VPN"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s rămase"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "oră"
+#~ msgstr[1] "ore"
+#~ msgstr[2] "de ore"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "minut"
+#~ msgstr[1] "minute"
+#~ msgstr[2] "de minute"
+
+#~ msgid "AC adapter"
+#~ msgstr "Adaptor AC"
+
+#~ msgid "Laptop battery"
+#~ msgstr "Baterie laptop"
+
+#~ msgid "Monitor"
+#~ msgstr "Monitor"
+
+#~ msgid "Mouse"
+#~ msgstr "Maus"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Media player"
+#~ msgstr "Player Media"
+
+#~ msgid "Tablet"
+#~ msgstr "Tabletă"
+
+#~ msgid "Computer"
+#~ msgstr "Calculator"
+
+#~ msgid "Subscription request"
+#~ msgstr "Cerere de abonare"
+
+#~ msgid "%s is online."
+#~ msgstr "%s este conectat."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s este deconectat."
+
+#~ msgid "%s is away."
+#~ msgstr "%s este plecat."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s este ocupat."
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Trimis la <b>%X</b> pe <b>%A</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Trimis pe <b>%A</b>, <b>%d %B</b> %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Conexiunea la %s a eșuat"
+
+#~ msgid "Reconnect"
+#~ msgstr "Reconectează"
+
+#~ msgid "Hidden"
+#~ msgstr "Ascuns"
+
+#~ msgid "Idle"
+#~ msgstr "Inactiv"
+
+#~ msgid "Power Off..."
+#~ msgstr "Oprire..."
+
+#~ msgid "Online Accounts"
+#~ msgstr "Conturi online"
+
+#~ msgid "System Settings"
+#~ msgstr "Setări de sistem"
+
+#~ msgid "Log Out..."
+#~ msgstr "Ieșire din sesiune..."
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Starea de chat va fi trecută pe ocupat"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Notificările sunt acum dezactivate, incluzând mesajele de chat. Starea a "
+#~ "fost ajustată pentru ca alții să știe că este posibil să nu le vedeți "
+#~ "mesajele."
+
+#~ msgid "United Kingdom"
+#~ msgstr "Regatul Unit"
+
+#~ msgid "Default"
+#~ msgstr "Implicit"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
diff --git a/po/ru.po b/po/ru.po
new file mode 100644
index 0000000..621b6cd
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,3272 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+#
+#
+# Andrey Korzinev <fellrond@gmail.com>, 2009.
+# Sergey V. Kovylov <serejka@gmail.com>, 2009.
+# Marina Zhurakhinskaya <marinaz@redhat.com>, 2009, 2010.
+# Sergey Davidoff <shnatsel@gmail.com>, 2010.
+# Yuri Kozlov <yuray@komyakino.ru>, 2011, 2012.
+# JDronova <juliette.tux@gmail.com>, 2013.
+# Valery Kirichenko <valera5505@gmail.com>, 2014.
+# Yuri Myasoedov <ymyasoedov@yandex.ru>, 2012, 2013, 2014.
+# Ivan Komaritsyn <vantu5z@mail.ru>, 2015.
+# Stas Solovey <whats_up@tut.by>, 2011, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020.
+# Konstantin Nezhbert <zhbert@yandex.ru>, 2021.
+# Ivan Molodetskikh <yalterz@gmail.com>, 2020-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-05 17:40+0000\n"
+"PO-Revision-Date: 2022-12-05 12:16+0300\n"
+"Last-Translator: Aleksandr Melman <Alexmelman88@gmail.com>\n"
+"Language-Team: Russian <gnome-cyr@gnome.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-DL-Team: ru\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: None\n"
+
+# Запуск приложений, как другой вариант
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Ярлыки"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Переключиться на избранное приложение 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Переключиться на избранное приложение 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Переключиться на избранное приложение 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Переключиться на избранное приложение 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Переключиться на избранное приложение 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Переключиться на избранное приложение 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Переключиться на избранное приложение 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Переключиться на избранное приложение 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Переключиться на избранное приложение 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "Снимки экрана"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:244
+msgid "Take a screenshot interactively"
+msgstr "Сделать снимок экрана интерактивно"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid "Take a screenshot"
+msgstr "Сделать снимок экрана"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:252
+msgid "Take a screenshot of a window"
+msgstr "Сделать снимок окна"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:248
+msgid "Record a screencast interactively"
+msgstr "Сделать запись экрана интерактивно"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Система"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Показать список уведомлений"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Передать фокус активному уведомлению"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Показать обзор"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Показать все приложения"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Открыть меню приложений"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Управление окнами и запуск приложений"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Включить внутренние инструменты из Alt-F2 для разработчиков и тестеров"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Разрешает доступ к внутренней отладке и инструментам наблюдения, используя "
+"диалог Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID включённых расширений"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"У расширений GNOME Shell есть свойство UUID; в этом ключе перечислены "
+"расширения, которые должны быть загружены. Этим списком также можно "
+"управлять через методы DBus EnableExtension и DisableExtension сервиса org."
+"gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID расширений для принудительного отключения"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"У расширений GNOME Shell есть свойство UUID; в этом ключе перечислены "
+"расширения, которые должны быть отключены, если загружены как часть текущего "
+"режима. Этим списком также можно управлять через методы DBus EnableExtension "
+"и DisableExtension сервиса org.gnome.Shell. Этот ключ имеет приоритет над "
+"настройкой “enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Выключить пользовательские расширения"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Отключите все расширения, которые пользователь активировал, не затрагивая "
+"настройку «enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Отключает проверку совместимости версий расширений"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell будет загружать только те расширения, у которых заявлена "
+"поддержка текущей версии. Включение этой опции отключит эту проверку и "
+"попробует загрузить все расширения вне зависимости от версии, которую они "
+"поддерживают."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Список идентификаторов desktop-файлов для избранных приложений"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Приложения, соответствующие этих идентификаторам, будут показаны в области "
+"избранных приложений."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "История команд диалогового окна (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "История просмотра прозрачного диалогового окна"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Всегда показывать в пользовательском меню пункт «Выйти»."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Этот ключ переопределяет автоматическое скрытие пункта «Выйти» при "
+"использовании системы с одной учётной записью пользователя."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Запоминать ли пароль для подключения зашифрованных или удалённых файловых "
+"систем"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Gnome Shell запросит пароль при подключении зашифрованного устройства или "
+"удаленной файловой системы. Если пароль можно сохранить для последующего "
+"использования, будет установлен флажок «Запомнить пароль». Этот ключ "
+"устанавливает состояние по умолчанию для флажка."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "Связан ли адаптер Bluetooth по умолчанию с каким-либо устройством"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Пункт меню Bluetooth будет отображаться только в случае включённого адаптера "
+"Bluetooth, или если имеются установленные устройства связанные с адаптером "
+"по умолчанию. Параметр будет сброшен, если адаптер по умолчанию не имеет "
+"связанных с ним устройств."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last selected non-default power profile"
+msgstr "Последний выбранный профиль питания не по умолчанию"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Некоторые системы поддерживают более двух профилей питания. Чтобы сохранить "
+"поддержку переключения между двумя профилями, этот ключ записывает последний "
+"выбранный не по умолчанию профиль."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "В последней версии диалог “Добро пожаловать в GNOME” был показан для"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Этот ключ определяет, для какой версии в последний раз был показан диалог "
+"“Добро пожаловать в GNOME\". Пустая строка представляет самую старую "
+"возможную версию, а большое число будет представлять версии, которые еще не "
+"существуют. Это большое число может быть использовано для эффективного "
+"отключения диалога."
+
+#: data/org.gnome.shell.gschema.xml.in:142
+msgid "Layout of the app picker"
+msgstr "Макет меню выбора приложений"
+
+#: data/org.gnome.shell.gschema.xml.in:143
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Макет меню выбора приложений. Каждая запись в массиве представляет собой "
+"страницу. Страницы хранятся в том порядке, в котором они отображаются в "
+"оболочке GNOME. Каждая страница содержит пару “application id” → \"data\". В "
+"настоящее время в качестве “data” хранятся следующие значения: • "
+"\"position\" - положение значка приложения на странице"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+msgid "Keybinding to open the application menu"
+msgstr "Комбинация клавиш для открытия меню приложения"
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid "Keybinding to open the application menu."
+msgstr "Комбинация клавиш для открытия меню приложения."
+
+#: data/org.gnome.shell.gschema.xml.in:165
+#: data/org.gnome.shell.gschema.xml.in:172
+msgid "Keybinding to shift between overview states"
+msgstr "Сочетание клавиш для переключения между состояниями обзора"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Сочетание клавиш для переключения между сеансом, меню выбора окон и сеткой "
+"приложений"
+
+#: data/org.gnome.shell.gschema.xml.in:173
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Сочетание клавиш для переключения между сеткой приложений, меню выбора окон "
+"и сеансом"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Комбинация клавиш для перехода в режим просмотра приложений"
+
+#: data/org.gnome.shell.gschema.xml.in:180
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Комбинация клавиш для перехода в режим просмотра приложений в меню «Обзор»."
+
+#: data/org.gnome.shell.gschema.xml.in:187
+msgid "Keybinding to open the overview"
+msgstr "Комбинация клавиш для перехода в режим обзора"
+
+#: data/org.gnome.shell.gschema.xml.in:188
+msgid "Keybinding to open the Activities Overview."
+msgstr "Комбинация клавиш для перехода в режим обзора."
+
+#: data/org.gnome.shell.gschema.xml.in:194
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Комбинация клавиш для отображения или скрытия списка уведомлений"
+
+#: data/org.gnome.shell.gschema.xml.in:195
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Комбинация клавиш для отображения или скрытия списка уведомлений."
+
+#: data/org.gnome.shell.gschema.xml.in:201
+msgid "Keybinding to focus the active notification"
+msgstr "Комбинация клавиш для перевода фокуса на текущее уведомление"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Keybinding to focus the active notification."
+msgstr "Комбинация клавиш для перевода фокуса на текущее уведомление."
+
+#: data/org.gnome.shell.gschema.xml.in:208
+msgid "Switch to application 1"
+msgstr "Переключиться на приложение 1"
+
+#: data/org.gnome.shell.gschema.xml.in:212
+msgid "Switch to application 2"
+msgstr "Переключиться на приложение 2"
+
+#: data/org.gnome.shell.gschema.xml.in:216
+msgid "Switch to application 3"
+msgstr "Переключиться на приложение 3"
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "Switch to application 4"
+msgstr "Переключиться на приложение 4"
+
+#: data/org.gnome.shell.gschema.xml.in:224
+msgid "Switch to application 5"
+msgstr "Переключиться на приложение 5"
+
+#: data/org.gnome.shell.gschema.xml.in:228
+msgid "Switch to application 6"
+msgstr "Переключиться на приложение 6"
+
+#: data/org.gnome.shell.gschema.xml.in:232
+msgid "Switch to application 7"
+msgstr "Переключиться на приложение 7"
+
+#: data/org.gnome.shell.gschema.xml.in:236
+msgid "Switch to application 8"
+msgstr "Переключиться на приложение 8"
+
+#: data/org.gnome.shell.gschema.xml.in:240
+msgid "Switch to application 9"
+msgstr "Переключиться на приложение 9"
+
+#: data/org.gnome.shell.gschema.xml.in:265
+#: data/org.gnome.shell.gschema.xml.in:292
+msgid "Limit switcher to current workspace."
+msgstr "Ограничить переключатель текущим рабочим столом."
+
+#: data/org.gnome.shell.gschema.xml.in:266
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Если выбрано, то в переключателе будут показываться только те приложения, "
+"чьи окна находятся на текущем рабочем столе. Иначе все приложения будут "
+"включены."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "The application icon mode."
+msgstr "Режим значка приложения."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Управляет способом отображения окон в переключателе. Доступные значения: "
+"«thumbnail-only» (показывать миниатюру окна), «app-icon-only» (показывать "
+"только значок приложения), «both» (показывать миниатюру и значок)."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Если выбрано, то в переключателе будут показываться только окна из текущего "
+"рабочего стола. Иначе все окна будут включены."
+
+#: data/org.gnome.shell.gschema.xml.in:303
+msgid "Locations"
+msgstr "Местоположения"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "The locations to show in world clocks"
+msgstr "Местоположения для отображения в мировых часах"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "Automatic location"
+msgstr "Автоматическое местоположение"
+
+#: data/org.gnome.shell.gschema.xml.in:315
+msgid "Whether to fetch the current location or not"
+msgstr "Определяет получать местоположение или нет"
+
+#: data/org.gnome.shell.gschema.xml.in:322
+msgid "Location"
+msgstr "Местоположение"
+
+#: data/org.gnome.shell.gschema.xml.in:323
+msgid "The location for which to show a forecast"
+msgstr "Местоположение для которого отображается прогноз погоды"
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Attach modal dialog to the parent window"
+msgstr "Прикреплять модальное диалоговое окно к родительскому окну"
+
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:345
+#: data/org.gnome.shell.gschema.xml.in:353
+#: data/org.gnome.shell.gschema.xml.in:361
+#: data/org.gnome.shell.gschema.xml.in:369
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Этот ключ переопределяет ключ в org.gnome.mutter при запуске GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:344
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Включить автоматическое изменение размеров окон при перемещении окон к краям "
+"экрана"
+
+#: data/org.gnome.shell.gschema.xml.in:352
+msgid "Workspaces are managed dynamically"
+msgstr "Рабочие столы управляются динамически"
+
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid "Workspaces only on primary monitor"
+msgstr "Рабочие столы только на основном мониторе"
+
+#: data/org.gnome.shell.gschema.xml.in:368
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Задержка изменения фокуса в режиме мыши после остановки указателя"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Сетевая авторизация"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "О, нет! Что-то пошло не так"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Мы очень сожалеем, но возникла проблема: настройки этого расширения не могут "
+"быть отображены. Мы рекомендуем сообщить об этой проблеме авторам расширения."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Технические подробности"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Домашняя страница"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Посетить домашнюю страницу расширения"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Отмена"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Пароль"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Выбрать сеанс"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Нет в списке?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(например, пользователь или %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Имя пользователя"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Окно входа в систему"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Ошибка проверки подлинности"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(или проведите пальцем по считывающему устройству)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(или поместите палец на считывающее устройство)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Выключить"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "выключение;завершение;остановка;стоп"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Перезапустить"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "перезагрузить;перезапустить;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Заблокировать экран"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "заблокировать экран"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Выйти"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;выйти"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Режим ожидания"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "ждущий;спящий режим"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Сменить пользователя"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "переключить;сменить пользователя"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "заблокировать ориентацию;экран;поворот;вращение"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Сделать снимок экрана"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "screenshot;screencast;snip;capture;record;скриншот;захват;запись"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Разблокировать ориентацию экрана"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Заблокировать ориентацию экрана"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Команда не найдена"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Не удалось разобрать команду:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Не удалось выполнить «%s»:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Прямо сейчас"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d минута назад"
+msgstr[1] "%d минуты назад"
+msgstr[2] "%d минут назад"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d час назад"
+msgstr[1] "%d часа назад"
+msgstr[2] "%d часов назад"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Вчера"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d день назад"
+msgstr[1] "%d дня назад"
+msgstr[2] "%d дней назад"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d неделя назад"
+msgstr[1] "%d недели назад"
+msgstr[2] "%d недель назад"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d месяц назад"
+msgstr[1] "%d месяца назад"
+msgstr[2] "%d месяцев назад"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d год назад"
+msgstr[1] "%d года назад"
+msgstr[2] "%d лет назад"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Вчера, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%-l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Вчера, %-l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %-l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+# fix даты
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Авторизация в точке доступа"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Соединение с этой точкой доступа не безопасно. Пароли или другая информация, "
+"которую вы вводите на этой странице, могут просматриваться пользователями, "
+"находящимися рядом."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Запретить доступ"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Предоставить доступ"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Безымянная папка"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "Приложение %s было прикреплено к панели приложений."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Приложение %s было откреплено от панели приложений."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Открытые окна"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Новое окно"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Показать подробности"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Завершить"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Открепить"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Прикрепить к панели приложений"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Запустить, используя встроенную видеокарту"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Запустить, используя дискретную видеокарту"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Выберите аудиоустройство"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Настройки звука"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Наушники"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Гарнитура"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Изменить фон…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Настройки дисплея"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Настройки"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Вс"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "Пн"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "Вт"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ср"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Чт"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "Пт"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Сб"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Предыдущий месяц"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Следующий месяц"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V неделя"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Уведомлений нет"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Не беспокоить"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Очистить"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» не отвечает."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Вы можете подождать отклика приложения некоторое время, или принудительно "
+"завершить его работу."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Принудительно завершить"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Подождать"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Внешний диск подключён"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Внешний диск отключён"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Не удалось разблокировать том"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Установленная версия udisks не поддерживает настройку PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Открыть с помощью %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"В качестве альтернативы вы можете подключиться, нажав кнопку «WPS» на вашем "
+"маршрутизаторе."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Подключиться"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Ключ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Пароль личного ключа"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Идентичность"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Служба"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Требуется подтверждение подлинности"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Для доступа к беспроводной сети «%s» требуется пароль или ключ шифрования."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Проводная аутентификация 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Название сети"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Аутентификация DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Требуется PIN-код"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Для устройства мобильной связи требуется PIN-код"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Для подключения к «%s» требуется пароль."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Диспетчер сети"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Пароль VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Требуется подтверждение подлинности"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Администратор"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Подтвердить"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Не удалось подтвердить подлинность. Попробуйте снова."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Контакт %s теперь известен как %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:414
+msgid "Windows"
+msgstr "Окна"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Показать приложения"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Панель приложений"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %-d %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Сегодня"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Завтра"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Весь день"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Событий нет"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Добавить мировые часы…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Мировые часы"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Загрузка…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Подключите интернет для получения информации о погоде"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Информация о погоде сейчас недоступна"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Погода"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Выберите ваше местоположение…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Выход пользователя %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Выйти из системы"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s выйдет из системы автоматически через %d секунду."
+msgstr[1] "%s выйдет из системы автоматически через %d секунды."
+msgstr[2] "%s выйдет из системы автоматически через %d секунд."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Вы автоматически выйдете из системы через %d секунду."
+msgstr[1] "Вы автоматически выйдете из системы через %d секунды."
+msgstr[2] "Вы автоматически выйдете из системы через %d секунд."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Выйти"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Выключение"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Установить обновления и выключить"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Система будет автоматически выключена через %d секунду."
+msgstr[1] "Система будет автоматически выключена через %d секунды."
+msgstr[2] "Система будет автоматически выключена через %d секунд."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Установить отложенные обновления ПО"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Выключить"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Перезапуск"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Установить обновления и перезапустить"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Система будет автоматически перезапущена через %d секунду."
+msgstr[1] "Система будет автоматически перезапущена через %d секунды."
+msgstr[2] "Система будет автоматически перезапущена через %d секунд."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Перезапустить"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Перезапуск и установка обновлений"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Система автоматически перезапустится и установит обновления через %d секунду."
+msgstr[1] ""
+"Система автоматически перезапустится и установит обновления через %d секунды."
+msgstr[2] ""
+"Система автоматически перезапустится и установит обновления через %d секунд."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Перезапустить и установить"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Установить и выключить"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Выключить после установки обновлений"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Перезапуск и установка обновлений"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s будет установлено после перезапуска. Установка обновлений может занять "
+"продолжительное время: убедитесь, что у вас есть резервная копия и что "
+"компьютер подключен к сети."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Низкий заряд батареи: перед установкой обновлений подключите зарядное "
+"устройство."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Некоторые приложения заняты или результаты их работы не сохранены"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Есть открытые сеансы других пользователей"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Опции загрузки"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (удалённый)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (консоль)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Установить"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Установить расширение"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Загрузить и установить расширение «%s» из extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Доступны обновления расширений"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Обновления расширений готовы к установке."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Разрешить замену комбинации клавиш"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Приложение %s пытается заменить комбинации клавиш"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Приложение пытается заменить комбинации клавиш"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Можете восстановить комбинации клавиш, нажав %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Отказать"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Разрешить"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "«Медленные» клавиши включены"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "«Медленные» клавиши выключены"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Вы только что удержали клавишу Shift нажатой 8 секунд. Это комбинация для "
+"включения функции «медленные» клавиши, которая влияет на способ работы с "
+"клавиатурой."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "«Залипающие» клавиши включены"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "«Залипающие» клавиши выключены"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Вы только что нажали клавишу Shift 5 раз подряд. Это комбинация для "
+"включения функции «залипающие» клавиши, которая влияет на способ работы "
+"клавиатуры."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Вы только что нажали две клавиши одновременно или нажали клавишу Shift 5 раз "
+"подряд. Это привело к включению функции «залипающие клавиши», которая влияет "
+"на режим работы клавиатуры."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Оставить включёнными"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Включить"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Выключить"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Оставить выключенными"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Настройки языка и региона"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Расширения не установлены"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s не сообщал о каких-либо ошибках."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Скрыть ошибки"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Показать ошибки"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Включено"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Выключено"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Ошибка"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Устарело"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Загрузка"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Показать код"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Веб-страница"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Система была переведена в небезопасный режим"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Приложения теперь имеют неограниченный доступ"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Отменить"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Авторизован как привилегированный пользователь"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"В целях соблюдения безопасности, не следует запускать сессию от имени "
+"привилегированного пользователя. Если это возможно, вам следует войти от "
+"имени обычного пользователя."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Блокировка экрана отключена"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Для блокировки экрана требуется менеджер дисплеев GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Системная информация"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Неизвестный исполнитель"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Неизвестное название"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:324
+msgid "Type to search"
+msgstr "Введите для поиска"
+
+#: js/ui/overviewControls.js:402
+msgid "Applications"
+msgstr "Приложения"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Обзор"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Создать комбинацию клавиш…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Определено приложением"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Показывать экранную справку"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Переключить монитор"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Назначение клавишь"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Готово"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Правка…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Нет"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Нажмите кнопку для настройки"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Нажмите Esc для выхода"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Нажмите любую клавишу для выхода"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Обзор"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Система"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Верхняя панель"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Выполнить команду"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Нажмите Esc чтобы закрыть"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Перезапуск не доступен при использовании Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Перезапуск…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME необходимо заблокировать экран"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Не удалось заблокировать"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Блокировке помешало приложение"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "Область"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "Выбор области"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "Экран"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "Выбор экрана"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "Окно"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "Выбор окна"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Снимок экрана / Запись экрана"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Показать курсор мыши"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "Записи экрана"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Запись экрана от %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "Снимок экрана"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "Запись экрана сделана"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "Нажмите здесь, чтобы посмотреть видео."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "Показать в Файлах"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Снимок экрана от %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "Снимок экрана сделан"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "Вы можете вставить изображение из буфера обмена."
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "Сделан снимок экрана"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Поиск…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Ничего не найдено."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d ещё"
+msgstr[1] "%d ещё"
+msgstr[2] "%d ещё"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Поиск"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Копировать"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Вставить"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Показать текст"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Скрыть текст"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Включён Caps Lock."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Скрытый том"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Системный том Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Использует файлы-ключи"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Чтобы разблокировать том, использующий файлы-ключи, используйте вместо этого "
+"утилиту <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Номер PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Запомнить пароль"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Разблокировать"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Открыть %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM должен содержать цифры или быть пустым."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Не удалось запустить %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Не удалось найти приложение %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Специальные возможности"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Высокая контрастность"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Масштабирование"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Экранный диктор"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Экранная клавиатура"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Визуальные предупреждения"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Залипающие клавиши"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Медленные клавиши"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Отскакивающие клавиши"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Клавиши мыши"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Крупный текст"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Автоповорот"
+
+#: js/ui/status/bluetooth.js:171
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Яркость"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Темный режим"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Одиночное нажатие левой кнопки мыши"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Двойное нажатие левой кнопки мыши"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Перетаскивание"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Нажатие правой кнопки мыши"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Нажатие левой кнопки мыши после задержки над кнопкой"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "Клавиатура"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "Показать раскладку клавиатуры"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Разрешить доступ к местоположению"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Приложение %s запрашивает доступ к вашему местоположению"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Доступ к местоположению можно изменить в любое время в настройках "
+"конфиденциальности."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<неизвестно>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Отключить %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Подключиться к %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Точка доступа %s"
+
+#: js/ui/status/network.js:1464
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1465
+msgid "VPN Settings"
+msgstr "Настройки VPN"
+
+#: js/ui/status/network.js:1708
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1710
+msgid "All Networks"
+msgstr "Все сети"
+
+#: js/ui/status/network.js:1807
+msgid "Wired Connections"
+msgstr "Проводные соединения"
+
+#: js/ui/status/network.js:1808
+msgid "Wired Settings"
+msgstr "Настройки соединения"
+
+#: js/ui/status/network.js:1822
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth соединения"
+
+#: js/ui/status/network.js:1823
+msgid "Bluetooth Settings"
+msgstr "Настройки Bluetooth"
+
+#: js/ui/status/network.js:1837
+msgid "Mobile Connections"
+msgstr "Мобильные соединения"
+
+#: js/ui/status/network.js:1839
+msgid "Mobile Broadband Settings"
+msgstr "Настройки мобильной связи"
+
+#: js/ui/status/network.js:1951
+msgid "Connection failed"
+msgstr "Сбой подключения"
+
+#: js/ui/status/network.js:1952
+msgid "Activation of network connection failed"
+msgstr "Не удалось активировать сетевое подключение"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Ночной свет"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Производительный"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Сбалансированный"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Энергосберегающий"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "Профили электропитания"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Остановить запись экрана"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Остановить совместный доступ к экрану"
+
+# Нужно максимально коротко, иначе не влазит в меню gnome shell
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Авиарежим"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Сделать снимок экрана"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Меню выключения"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Режим ожидания"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Перезапуск…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Выключение…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Выйти…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Сменить пользователя…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Заблокировать экран"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Неизвестное устройство Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Новое устройство было обнаружено во время вашего отсутствия. Отключите и "
+"снова подключите устройство, Чтобы начать использовать устройство, отключите "
+"и снова подключите его."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Неавторизованное устройство Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Новое устройство было обнаружено и должно быть авторизовано администратором."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Ошибка авторизации Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Не удалось авторизовать устройство Thunderbolt: %s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "Громкость изменена"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "Громкость"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "Звуковой выход"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "Звуковой вход"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Зеркальное отображение"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Объединить дисплеи"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Только внешний"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Только встроенный"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Проведите вверх, чтобы разблокировать"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Нажмите клавишу или кнопку мыши, чтобы разблокировать"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Разблокировать окно"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Войти от имени другого пользователя"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Добро пожаловать в GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Для знакомства с возможностями пройдите ознакомительный тур."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Нет, спасибо"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Просмотреть обзор"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Окно «%s» ожидает"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Сохранить эти настройки дисплея?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Вернуть настройки"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Сохранить изменения"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Изменения настроек будут отменены через %d секунду"
+msgstr[1] "Изменения настроек будут отменены через %d секунды"
+msgstr[2] "Изменения настроек будут отменены через %d секунд"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Скрыть"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Восстановить"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Развернуть"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Переместить"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Изменить размер"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Переместить панель заголовка на экран"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Всегда сверху"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Всегда на видимом рабочем столе"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Переместить на рабочий стол влево"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Переместить на рабочий стол вправо"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Переместить на рабочий стол вверх"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Переместить на рабочий стол вниз"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Переместить на экран вверх"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Переместить на экран вниз"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Переместить на экран влево"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Переместить на экран вправо"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Закрыть"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Календарь Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Показать номер версии"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Режим, используемый GDM для окна входа в систему"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr ""
+"Использовать для экрана входа в систему определённый режим, например «gdm»"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Список возможных режимов"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Неизвестное приложение"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Не удалось запустить «%s»"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Пароли не совпадают."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Пароль не может быть пустым"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Аутентификация отклонена пользователем"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Расширения"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Управление расширениями GNOME Shell"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "The GNOME Project"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Приложение «Расширения GNOME» занимается обновлением расширений, настройкой "
+"параметров расширений и удалением или отключением ненужных расширений."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Настроить расширения GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Нет совпадений"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Удалить «%s»?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"После удаления расширения, если потребуется использовать его снова, нужно "
+"будет загрузить и установить его заново"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Удалить"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Stas Solovey <whats_up@tut.by>, 2011-2020\n"
+"Ivan Molodetskikh <yalterz@gmail.com>, 2020\n"
+"Alexey Rubtsov <rushills@gmail.com>, 2021\n"
+"Aleksandr Melman <alexmelman88@gmail.com>, 2022"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d расширение будет обновлено при следующем входе в систему."
+msgstr[1] "%d расширения будет обновлено при следующем входе в систему."
+msgstr[2] "%d расширений будет обновлено при следующем входе в систему."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Расширение несовместимо с текущей версией GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Расширение содержит ошибку"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Расширение может быть обновлено"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Веб-страница"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Удалить…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Справка"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "О приложении"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Расширения могут вызывать проблемы с производительностью и стабильностью. "
+"Отключите расширения, если вы столкнулись с проблемами в системе."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Установлены вручную"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Чтобы найти и добавить расширения, посетите <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Встроенные"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Нет установленных расширений"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Нам очень жаль, но список установленных расширений получить не удалось. "
+"Убедитесь, что вы вошли в GNOME и попробуйте снова."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Доступны обновления расширений"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Новое расширение было успешно создано в %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Имя должно быть очень короткой (в идеале наглядной) строкой.\n"
+"Например: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Имя"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Описание — это одно предложение, описывающее работу вашего расширения.\n"
+"Например: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Описание"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID — это глобально уникальный идентификатор для вашего расширения.\n"
+"Он должен быть в формате адреса электронной почты (clicktofocus@janedoe."
+"example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Выберите один из доступных шаблонов:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Шаблон"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Уникальный идентификатор нового расширения"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "ИМЯ"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Имя расширения, видимое пользователю"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "ОПИСАНИЕ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Краткое описание того, что делает расширение"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ШАБЛОН"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Шаблон, используемый для нового расширения"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Интерактивный ввод информации о расширении"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Создать новое расширение"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Неизвестные аргументы"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Необходимо указать UUID, имя и описание"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Невозможно подключиться к GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Расширение «%s» не найдено\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Выключить расширение"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "UUID не указан"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Указано более одного UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Включить расширение"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Расширение «%s» не найдено\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Показать сведения о расширении"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Перезаписать существующие расширение"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "ПАКЕТ_РАСШИРЕНИЯ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Установить пакет расширения"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Не указан пакет расширения"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Указано более одного пакета расширения"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Показать пользовательские расширения"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Показать системные расширения"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Показать включенные расширения"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Показать отключенные расширения"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Показать расширения имеющие настройки"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Показать расширения с обновлениями"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Вывести сведения о расширении"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Показать установленные расширения"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ФАЙЛ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Дополнительный исходный файл для включения в пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "СХЕМА"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Схема GSettings для включения в пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "КАТАЛОГ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Директория с переводами"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ДОМЕН"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Домен gettext для переводов"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Переписать существующий пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Директория, где должен быть создан пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ИСХОДНЫЙ_КАТАЛОГ"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Создать пакет расширения"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Указано больше одной исходной директории"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Расширение «%s» не имеет настроек\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Не удалось открыть настройки для расширения \"%s\": %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Открыть параметры расширения"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Сбросить расширение"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Не удалось удалить системные расширения\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Не удалось удалить «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Удалить расширение"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Не показывать сообщения об ошибках"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Путь"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Первый автор"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Версия"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Состояние"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» не принимает аргументов"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Использование:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Показать информацию о версии и выйти."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "КОМАНДА"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[АРГУМЕНТЫ…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Команды:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Показать справку"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Включить расширение"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Выключить расширение"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Сбросить расширение"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Удалить расширение"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Показать расширения"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Показать сведения о расширении"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Открыть параметры расширения"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Создать расширение"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Создать пакет расширения"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Установить пакет расширения"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Используйте «%s» для получения подробной справки.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Пустое"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Пустое расширение"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Индикатор"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Добавить значок на верхнюю панель"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u выход"
+msgstr[1] "%u выхода"
+msgstr[2] "%u выходов"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u вход"
+msgstr[1] "%u входа"
+msgstr[2] "%u входов"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Системные звуки"
+
+#~ msgid "Log Out"
+#~ msgstr "Выйти"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Подключиться к Интернету"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Включён авиарежим"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Когда включён режим авиаперелёта, адаптер Wi-Fi выключен."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Выключить режим авиаперелёта"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi выключен"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Для подключения к сети, адаптер Wi-Fi должен быть включён."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Включить Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Сети Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Выберите сеть"
+
+#~ msgid "No Networks"
+#~ msgstr "Сети отсутствуют"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr ""
+#~ "Отключается\n"
+#~ "механическим\n"
+#~ "переключателем"
+
+#~ msgid "Select Network"
+#~ msgstr "Выбрать сеть"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Настройки Wi-Fi"
+
+#~ msgid "VPN Off"
+#~ msgstr ""
+#~ "Соединение VPN\n"
+#~ "выключено"
+
+#~ msgid "Network Settings"
+#~ msgstr "Настройки сети"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s соединение Wi-Fi"
+#~ msgstr[1] "%s соединения Wi-Fi"
+#~ msgstr[2] "%s соединений Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s проводное соединение"
+#~ msgstr[1] "%s проводных соединения"
+#~ msgstr[2] "%s проводных соединений"
+
+#, javascript-format
+#~ msgid "%s Bluetooth Connection"
+#~ msgid_plural "%s Bluetooth Connections"
+#~ msgstr[0] "%s Bluetooth соединение"
+#~ msgstr[1] "%s Bluetooth соединения"
+#~ msgstr[2] "%s Bluetooth соединений"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s модемное соединение"
+#~ msgstr[1] "%s модемных соединения"
+#~ msgstr[2] "%s модемных соединений"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "выключено"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "подключено"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "не управляется"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "подключается"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "требует аутентификацию"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr ""
+#~ "%s соединение:\n"
+#~ "отсутствует прошивка"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "недоступно"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "не удалось подключить"
+
+# заменил "оборудование" потому что не влазит в панель
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr ""
+#~ "%s соединение:\n"
+#~ "аппаратура отключена"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr ""
+#~ "%s соединение\n"
+#~ "отключено"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr ""
+#~ "%s сеть\n"
+#~ "не подключена"
+
+#~ msgid "unknown"
+#~ msgstr "неизвестно"
+
+#~ msgid "activating…"
+#~ msgstr "активация…"
+
+#~ msgid "deactivating…"
+#~ msgstr "деактивация…"
+
+#~ msgid "deactivated"
+#~ msgstr "деактивировано"
+
+#~ msgid "connecting…"
+#~ msgstr "подключение…"
+
+#~ msgid "authentication required"
+#~ msgstr "требуется аутентификация"
+
+#~ msgid "connection failed"
+#~ msgstr "сбой подключения"
+
+#~ msgid "Power Settings"
+#~ msgstr "Настройки электропитания"
+
+#~ msgid "Lock"
+#~ msgstr "Заблокировать"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Выключить / Выйти"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "Подключено %d"
+#~ msgstr[1] "Подключено %d"
+#~ msgstr[2] "Подключено %d"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth включён"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth выключен"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Ночной свет отключен"
+
+#~ msgid "Resume"
+#~ msgstr "Продолжить"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Отключить до завтра"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Полный заряд"
+
+#~ msgid "Not Charging"
+#~ msgstr "Не заряжается"
+
+#~ msgid "Estimating…"
+#~ msgstr "Подсчитывается…"
+
+# нужно в две строки, иначе не влезает в выпадающую панель
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr ""
+#~ "Осталось %02d ч. %d мин.\n"
+#~ "Разряжается (%d %%)"
+
+# нужно в две строки, иначе не влезает в выпадающую панель
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr ""
+#~ "До полной %02d ч. %d мин.\n"
+#~ "Заряжается (%d %%)"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Геолокация включена"
+
+#~ msgid "Disable"
+#~ msgstr "Выключить"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Настройки конфиденциальности"
+
+#~ msgid "Location In Use"
+#~ msgstr "Геолокация используется"
+
+# заменил "определение местоположения" на "геолокация" потому что не влазит в панель
+#~ msgid "Location Disabled"
+#~ msgstr "Геолокация выключена"
+
+#~ msgid "Enable"
+#~ msgstr "Включить"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Экран общедоступен"
+
+#~ msgid "Turn off"
+#~ msgstr "Выключить"
diff --git a/po/si.po b/po/si.po
new file mode 100644
index 0000000..194f8ec
--- /dev/null
+++ b/po/si.po
@@ -0,0 +1,1667 @@
+# Sinhala translation for gnome-shell.
+# Copyright (C) 2012 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Danishka Navin <danishka@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-02-01 05:57+0000\n"
+"PO-Revision-Date: 2012-02-01 15:44+0530\n"
+"Last-Translator: Danishka Navin <danishka@gmail.com>\n"
+"Language-Team: Sinhala <info@hanthana.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"Language: si\n"
+"X-Generator: Lokalize 1.2\n"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME ශෙල්"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:1
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:2
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:3
+msgid "File extension used for storing the screencast"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:4
+msgid "Framerate used for recording screencasts."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:5
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension DBus methods on org.gnome.Shell."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:6
+msgid "History for command (Alt-F2) dialog"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:7
+msgid "History for the looking glass dialog"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:8
+msgid "If true, display date in the clock, in addition to time."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:9
+msgid "If true, display seconds in time."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:10
+msgid "If true, display the ISO week date in the calendar."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:11
+msgid "List of desktop file IDs for favorite applications"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:13
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux' and records to WEBM "
+"using the VP8 codec. %T is used as a placeholder for a guess at the optimal "
+"thread count on the system."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:14
+msgid "Show date in clock"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:15
+msgid "Show the week date in the calendar"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:16
+msgid "Show time with seconds"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:17
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:18
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:19
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:20
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:21
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:22
+msgid "The type of keyboard to use."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:23
+msgid "Uuids of extensions to enable"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:24
+msgid "Whether to collect stats about applications usage"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:25
+msgid "Which keyboard to use"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.h:26
+msgid "disabled OpenSearch providers"
+msgstr ""
+
+#: ../js/gdm/loginDialog.js:624
+msgid "Session..."
+msgstr "සැසිය..."
+
+#: ../js/gdm/loginDialog.js:786
+msgctxt "title"
+msgid "Sign In"
+msgstr ""
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/loginDialog.js:831
+msgid "(or swipe finger)"
+msgstr ""
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:852
+msgid "Not listed?"
+msgstr ""
+
+#: ../js/gdm/loginDialog.js:1020 ../js/ui/endSessionDialog.js:419
+#: ../js/ui/extensionSystem.js:516 ../js/ui/networkAgent.js:145
+#: ../js/ui/polkitAuthenticationAgent.js:175 ../js/ui/status/bluetooth.js:462
+msgid "Cancel"
+msgstr "අහෝසි කරන්න"
+
+#: ../js/gdm/loginDialog.js:1025
+msgctxt "button"
+msgid "Sign In"
+msgstr ""
+
+#: ../js/gdm/loginDialog.js:1377
+msgid "Login Window"
+msgstr ""
+
+#: ../js/gdm/powerMenu.js:113 ../js/ui/userMenu.js:581
+#: ../js/ui/userMenu.js:583 ../js/ui/userMenu.js:652
+msgid "Suspend"
+msgstr ""
+
+#: ../js/gdm/powerMenu.js:118
+msgid "Restart"
+msgstr "යළි අරඹන්න"
+
+#: ../js/gdm/powerMenu.js:123
+msgid "Power Off"
+msgstr "වසා දමන්න"
+
+#: ../js/misc/util.js:92
+msgid "Command not found"
+msgstr ""
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:119
+msgid "Could not parse command:"
+msgstr ""
+
+#: ../js/misc/util.js:127
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr ""
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:251
+msgid "All"
+msgstr "සියල්ල"
+
+#: ../js/ui/appDisplay.js:310
+msgid "APPLICATIONS"
+msgstr "යෙදුම්"
+
+#: ../js/ui/appDisplay.js:366
+msgid "SETTINGS"
+msgstr "සැකසුම්"
+
+#: ../js/ui/appDisplay.js:666
+msgid "New Window"
+msgstr "නව කවුළුව"
+
+#: ../js/ui/appDisplay.js:669
+msgid "Remove from Favorites"
+msgstr ""
+
+#: ../js/ui/appDisplay.js:670
+msgid "Add to Favorites"
+msgstr ""
+
+#: ../js/ui/appFavorites.js:87
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr ""
+
+#: ../js/ui/appFavorites.js:118
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr ""
+
+#: ../js/ui/autorunManager.js:265
+msgid "Removable Devices"
+msgstr "ඉවත් කළ හැකි මෙවලම්"
+
+#: ../js/ui/autorunManager.js:560
+#, c-format
+msgid "Open with %s"
+msgstr "%s සමඟ විවෘත කරන්න"
+
+#: ../js/ui/autorunManager.js:586
+msgid "Eject"
+msgstr "ඉවත් කරන්න"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:62
+msgctxt "event list time"
+msgid "All Day"
+msgstr "දවස පුරා"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:74
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:114
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ඉ"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:116
+msgctxt "grid monday"
+msgid "M"
+msgstr "ස"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:118
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "අ"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:120
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "බ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:122
+msgctxt "grid thursday"
+msgid "T"
+msgstr "බ්‍ර"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:124
+msgctxt "grid friday"
+msgid "F"
+msgstr "සි"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:126
+msgctxt "grid saturday"
+msgid "S"
+msgstr "සෙ"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:139
+msgctxt "list sunday"
+msgid "Su"
+msgstr "ඉ"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:141
+msgctxt "list monday"
+msgid "M"
+msgstr "ස"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:143
+msgctxt "list tuesday"
+msgid "T"
+msgstr "අ"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:145
+msgctxt "list wednesday"
+msgid "W"
+msgstr "බ"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:147
+msgctxt "list thursday"
+msgid "Th"
+msgstr "බ්‍ර"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:149
+msgctxt "list friday"
+msgid "F"
+msgstr "සි"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:151
+msgctxt "list saturday"
+msgid "S"
+msgstr "සෙ"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:681
+msgid "Nothing Scheduled"
+msgstr "කිසිවක් සැළමුක් කර නැත"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:697
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:700
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:710
+msgid "Today"
+msgstr "අද"
+
+#: ../js/ui/calendar.js:714
+msgid "Tomorrow"
+msgstr "හෙට"
+
+#: ../js/ui/calendar.js:723
+msgid "This week"
+msgstr "මේ සතියෙ"
+
+#: ../js/ui/calendar.js:731
+msgid "Next week"
+msgstr "ලබන සතියෙ"
+
+#: ../js/ui/contactDisplay.js:63 ../js/ui/notificationDaemon.js:486
+#: ../js/ui/status/power.js:215 ../src/shell-app.c:372
+msgid "Unknown"
+msgstr "නොදන්නා"
+
+#: ../js/ui/contactDisplay.js:84 ../js/ui/userMenu.js:127
+msgid "Available"
+msgstr "සිටි"
+
+#: ../js/ui/contactDisplay.js:89 ../js/ui/userMenu.js:136
+msgid "Away"
+msgstr "පිටතට ගොස්"
+
+#: ../js/ui/contactDisplay.js:93 ../js/ui/userMenu.js:130
+msgid "Busy"
+msgstr "කාර්‍යය බහුලයි"
+
+#: ../js/ui/contactDisplay.js:97
+msgid "Offline"
+msgstr "නොබැඳිව"
+
+#: ../js/ui/contactDisplay.js:148
+msgid "CONTACTS"
+msgstr "සබධතා"
+
+#: ../js/ui/dash.js:229 ../js/ui/messageTray.js:1204
+msgid "Remove"
+msgstr "ඉවත් කරන්න"
+
+#: ../js/ui/dateMenu.js:97
+msgid "Date and Time Settings"
+msgstr "දින සහ වේලා සැකසුම්"
+
+#: ../js/ui/dateMenu.js:123
+msgid "Open Calendar"
+msgstr "දිනදර්ශනය විවෘත කරන්න"
+
+#. Translators: This is the time format with date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:181
+msgid "%a %b %e, %R:%S"
+msgstr "%a %b %e, %R:%S"
+
+#: ../js/ui/dateMenu.js:182
+msgid "%a %b %e, %R"
+msgstr "%a %b %e, %R"
+
+#. Translators: This is the time format without date used
+#. in 24-hour mode.
+#: ../js/ui/dateMenu.js:186
+msgid "%a %R:%S"
+msgstr "%a %R:%S"
+
+#: ../js/ui/dateMenu.js:187
+msgid "%a %R"
+msgstr "%a %R"
+
+#. Translators: This is a time format with date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:194
+msgid "%a %b %e, %l:%M:%S %p"
+msgstr "%a %b %e, %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:195
+msgid "%a %b %e, %l:%M %p"
+msgstr "%a %b %e, %l:%M %p"
+
+#. Translators: This is a time format without date used
+#. for AM/PM.
+#: ../js/ui/dateMenu.js:199
+msgid "%a %l:%M:%S %p"
+msgstr "%a %l:%M:%S %p"
+
+#: ../js/ui/dateMenu.js:200
+msgid "%a %l:%M %p"
+msgstr "%a %l:%M %p"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:211
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/docDisplay.js:13
+msgid "RECENT ITEMS"
+msgstr "මැතකදි භාවිත කළ අයිතම"
+
+#: ../js/ui/endSessionDialog.js:61
+#, c-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "ඉවත් වන්න %s"
+
+#: ../js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Log Out"
+msgstr "පිටවීම"
+
+#: ../js/ui/endSessionDialog.js:63
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:65
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:70
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:74
+msgid "Logging out of the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Log Out"
+msgstr "ඉවත්වන්න"
+
+#: ../js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Power Off"
+msgstr "වසාදමන්න"
+
+#: ../js/ui/endSessionDialog.js:82
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:84
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:88
+msgid "Powering off the system."
+msgstr "පද්ධතිය වසමින්"
+
+#: ../js/ui/endSessionDialog.js:90 ../js/ui/endSessionDialog.js:107
+msgctxt "button"
+msgid "Restart"
+msgstr "යළි අරඹන්න"
+
+#: ../js/ui/endSessionDialog.js:92
+msgctxt "button"
+msgid "Power Off"
+msgstr "වසන්න"
+
+#: ../js/ui/endSessionDialog.js:98
+msgctxt "title"
+msgid "Restart"
+msgstr "යළි අරඹන්න"
+
+#: ../js/ui/endSessionDialog.js:99
+msgid "Click Restart to quit these applications and restart the system."
+msgstr ""
+
+#: ../js/ui/endSessionDialog.js:101
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../js/ui/endSessionDialog.js:105
+msgid "Restarting the system."
+msgstr "පද්ධතිය යළි අරඹන්න"
+
+#: ../js/ui/extensionSystem.js:520
+msgid "Install"
+msgstr "ස්ථාපනය"
+
+#: ../js/ui/extensionSystem.js:524
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr ""
+
+#: ../js/ui/keyboard.js:322
+msgid "tray"
+msgstr "තැටිය"
+
+#: ../js/ui/keyboard.js:539 ../js/ui/status/power.js:203
+msgid "Keyboard"
+msgstr "යතුරුපුවරුව"
+
+#: ../js/ui/lookingGlass.js:724
+msgid "No extensions installed"
+msgstr ""
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:779
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:785
+msgid "Hide Errors"
+msgstr "දෝෂ සඟවන්න"
+
+#: ../js/ui/lookingGlass.js:789 ../js/ui/lookingGlass.js:840
+msgid "Show Errors"
+msgstr "දෝෂ පෙන්වන්න"
+
+#: ../js/ui/lookingGlass.js:798
+msgid "Enabled"
+msgstr "සක්‍රීයයි"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:801 ../src/gvc/gvc-mixer-control.c:1093
+msgid "Disabled"
+msgstr "අක්‍රීයයි"
+
+#: ../js/ui/lookingGlass.js:803
+msgid "Error"
+msgstr "දෝෂය"
+
+#: ../js/ui/lookingGlass.js:805
+msgid "Out of date"
+msgstr "කල් ඉකුත් වූ"
+
+#: ../js/ui/lookingGlass.js:807
+msgid "Downloading"
+msgstr "බාගනිමින්"
+
+#: ../js/ui/lookingGlass.js:828
+msgid "View Source"
+msgstr "මූලය පෙන්වන්න"
+
+#: ../js/ui/lookingGlass.js:834
+msgid "Web Page"
+msgstr "වෙබ් පිටුව"
+
+#: ../js/ui/messageTray.js:1197
+msgid "Open"
+msgstr "විවෘත කරන්න"
+
+#: ../js/ui/messageTray.js:1214
+msgid "Unmute"
+msgstr "නිහඬ නොකරන්න"
+
+#: ../js/ui/messageTray.js:1214
+msgid "Mute"
+msgstr "නිහඬ"
+
+#: ../js/ui/messageTray.js:2446
+msgid "System Information"
+msgstr "පද්ධති තොරතුරු"
+
+#: ../js/ui/networkAgent.js:140
+msgid "Connect"
+msgstr "සම්බන්ධවන්න"
+
+#. Cisco LEAP
+#: ../js/ui/networkAgent.js:235 ../js/ui/networkAgent.js:247
+#: ../js/ui/networkAgent.js:274 ../js/ui/networkAgent.js:294
+#: ../js/ui/networkAgent.js:304
+msgid "Password: "
+msgstr "මුරපදය: "
+
+#. static WEP
+#: ../js/ui/networkAgent.js:240
+msgid "Key: "
+msgstr "යතුර: "
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/ui/networkAgent.js:272 ../js/ui/networkAgent.js:290
+msgid "Username: "
+msgstr "පරිශීලක නාමය: "
+
+#: ../js/ui/networkAgent.js:278
+msgid "Identity: "
+msgstr ""
+
+#: ../js/ui/networkAgent.js:280
+msgid "Private key password: "
+msgstr "පුද්ගලික මුරපදය:"
+
+#: ../js/ui/networkAgent.js:292
+msgid "Service: "
+msgstr "සේවාව:"
+
+#: ../js/ui/networkAgent.js:321
+msgid "Authentication required by wireless network"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:322
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network '%"
+"s'."
+msgstr ""
+
+#: ../js/ui/networkAgent.js:326
+msgid "Wired 802.1X authentication"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:328
+msgid "Network name: "
+msgstr "ජාල නාමය:"
+
+#: ../js/ui/networkAgent.js:333
+msgid "DSL authentication"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:340
+msgid "PIN code required"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:341
+msgid "PIN code is needed for the mobile broadband device"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:342
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/networkAgent.js:348
+msgid "Mobile broadband network password"
+msgstr ""
+
+#: ../js/ui/networkAgent.js:349
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr ""
+
+#: ../js/ui/overview.js:90
+msgid "Undo"
+msgstr ""
+
+#: ../js/ui/overview.js:199
+msgid "Windows"
+msgstr "කවුළු"
+
+#: ../js/ui/overview.js:202
+msgid "Applications"
+msgstr "යෙදුම්"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:226
+msgid "Dash"
+msgstr "ඩැෂ්"
+
+#: ../js/ui/panel.js:572
+msgid "Quit"
+msgstr "ඉවත්වීම"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:603
+msgid "Activities"
+msgstr "ක්‍රියාකාරකම්"
+
+#: ../js/ui/panel.js:990
+msgid "Top Bar"
+msgstr "ඉහළ තීරුව"
+
+#: ../js/ui/placeDisplay.js:115
+#, c-format
+msgid "Failed to unmount '%s'"
+msgstr ""
+
+#: ../js/ui/placeDisplay.js:118
+msgid "Retry"
+msgstr "යළි-උත්සාහය"
+
+#: ../js/ui/placeDisplay.js:156
+msgid "Connect to..."
+msgstr ""
+
+#: ../js/ui/placeDisplay.js:367
+msgid "PLACES & DEVICES"
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:71
+msgid "Authentication Required"
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:109
+msgid "Administrator"
+msgstr "පරිපාලක"
+
+#: ../js/ui/polkitAuthenticationAgent.js:179
+msgid "Authenticate"
+msgstr ""
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/polkitAuthenticationAgent.js:260
+msgid "Sorry, that didn't work. Please try again."
+msgstr ""
+
+#: ../js/ui/polkitAuthenticationAgent.js:272
+msgid "Password:"
+msgstr "මුරපදය:"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:720
+msgid "toggle-switch-us"
+msgstr ""
+
+#: ../js/ui/runDialog.js:205
+msgid "Please enter a command:"
+msgstr ""
+
+#: ../js/ui/searchDisplay.js:333
+msgid "Searching..."
+msgstr "සොයමින්..."
+
+#: ../js/ui/searchDisplay.js:356
+msgid "No matching results."
+msgstr ""
+
+#: ../js/ui/shellEntry.js:26
+msgid "Copy"
+msgstr "පිටපත් කරන්න"
+
+#: ../js/ui/shellEntry.js:31
+msgid "Paste"
+msgstr "අලවන්න"
+
+#: ../js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "පෙළ පෙන්වන්න"
+
+#: ../js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "පෙළ සඟවන්න"
+
+#: ../js/ui/shellMountOperation.js:271
+msgid "Wrong password, please try again"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:47
+msgid "Accessibility"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:52
+msgid "Zoom"
+msgstr "විශාලණය"
+
+#. let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
+#. 'screen-reader-enabled');
+#. this.menu.addMenuItem(screenReader);
+#: ../js/ui/status/accessibility.js:63
+msgid "Screen Keyboard"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:67
+msgid "Visual Alerts"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:70
+msgid "Sticky Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:73
+msgid "Slow Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:76
+msgid "Bounce Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:79
+msgid "Mouse Keys"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:83
+msgid "Universal Access Settings"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:117
+msgid "High Contrast"
+msgstr ""
+
+#: ../js/ui/status/accessibility.js:154
+msgid "Large Text"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:31 ../js/ui/status/bluetooth.js:35
+#: ../js/ui/status/bluetooth.js:258 ../js/ui/status/bluetooth.js:341
+#: ../js/ui/status/bluetooth.js:371 ../js/ui/status/bluetooth.js:407
+#: ../js/ui/status/bluetooth.js:436 ../js/ui/status/network.js:893
+msgid "Bluetooth"
+msgstr "බ්ලුටූත්"
+
+#: ../js/ui/status/bluetooth.js:48
+msgid "Visibility"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:62
+msgid "Send Files to Device..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:63
+msgid "Set up a New Device..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:87
+msgid "Bluetooth Settings"
+msgstr ""
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:107 ../js/ui/status/network.js:256
+msgid "hardware disabled"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:203
+msgid "Connection"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:214 ../js/ui/status/network.js:491
+msgid "disconnecting..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:227 ../js/ui/status/network.js:497
+msgid "connecting..."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:245
+msgid "Send Files..."
+msgstr "ගොනු යවන්න..."
+
+#: ../js/ui/status/bluetooth.js:250
+msgid "Browse Files..."
+msgstr "ගොනු ගවේෂණය"
+
+#: ../js/ui/status/bluetooth.js:259
+msgid "Error browsing device"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:260
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:268
+msgid "Keyboard Settings"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:271
+msgid "Mouse Settings"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:276 ../js/ui/status/volume.js:58
+msgid "Sound Settings"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:372
+#, c-format
+msgid "Authorization request from %s"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:378
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:380
+msgid "Always grant access"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:381
+msgid "Grant this time only"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:382 ../js/ui/telepathyClient.js:1098
+msgid "Reject"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:408
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:414 ../js/ui/status/bluetooth.js:444
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:415
+#, c-format
+msgid "Please confirm whether the PIN '%s' matches the one on the device."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:417
+msgid "Matches"
+msgstr "ගැළපීම්"
+
+#: ../js/ui/status/bluetooth.js:418
+msgid "Does not match"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:437
+#, c-format
+msgid "Pairing request for %s"
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:445
+msgid "Please enter the PIN mentioned on the device."
+msgstr ""
+
+#: ../js/ui/status/bluetooth.js:461
+msgid "OK"
+msgstr "හරි"
+
+#: ../js/ui/status/keyboard.js:68
+msgid "Show Keyboard Layout"
+msgstr ""
+
+#: ../js/ui/status/keyboard.js:73
+msgid "Region and Language Settings"
+msgstr ""
+
+#: ../js/ui/status/network.js:96
+msgid "<unknown>"
+msgstr "<නොදන්නා>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:278
+msgid "disabled"
+msgstr "අක්‍රීයයි"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:489
+msgid "unmanaged"
+msgstr ""
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:500
+msgid "authentication required"
+msgstr ""
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:510
+msgid "firmware missing"
+msgstr ""
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:517
+msgid "cable unplugged"
+msgstr "කේබලය ගැලවී ඇත"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:522
+msgid "unavailable"
+msgstr "භාවිතයට නැත"
+
+#: ../js/ui/status/network.js:524
+msgid "connection failed"
+msgstr ""
+
+#: ../js/ui/status/network.js:585 ../js/ui/status/network.js:1505
+msgid "More..."
+msgstr "තවත්..."
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:621 ../js/ui/status/network.js:1440
+msgid "Connected (private)"
+msgstr ""
+
+#: ../js/ui/status/network.js:696
+msgid "Auto Ethernet"
+msgstr "ස්වයංක්‍රීය ඉදර්නෙට්"
+
+#: ../js/ui/status/network.js:757
+msgid "Auto broadband"
+msgstr "ස්වයංක්‍රීය අඳිවේගිජාලය"
+
+#: ../js/ui/status/network.js:760
+msgid "Auto dial-up"
+msgstr "ස්වයංක්‍රීය ඩයල්-අප්"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:879 ../js/ui/status/network.js:1452
+#, c-format
+msgid "Auto %s"
+msgstr "ස්වයංක්‍රීය %s"
+
+#: ../js/ui/status/network.js:881
+msgid "Auto bluetooth"
+msgstr "ස්වයංක්‍රීය බ්ලුටූත්"
+
+#: ../js/ui/status/network.js:1454
+msgid "Auto wireless"
+msgstr "ස්වයංක්‍රීය රැහැන් රහිත"
+
+#: ../js/ui/status/network.js:1541
+msgid "Network"
+msgstr "ජාලය"
+
+#: ../js/ui/status/network.js:1548
+msgid "Enable networking"
+msgstr "ජාලය සක්‍රීය කරන්න"
+
+#: ../js/ui/status/network.js:1560
+msgid "Wired"
+msgstr "රැහැන් සහිත"
+
+#: ../js/ui/status/network.js:1571
+msgid "Wireless"
+msgstr "රැහැන් රහිත"
+
+#: ../js/ui/status/network.js:1581
+msgid "Mobile broadband"
+msgstr ""
+
+#: ../js/ui/status/network.js:1591
+msgid "VPN Connections"
+msgstr "VPN Connections"
+
+#: ../js/ui/status/network.js:1602
+msgid "Network Settings"
+msgstr "ජාල සැකසුම්"
+
+#: ../js/ui/status/network.js:1739
+msgid "Connection failed"
+msgstr ""
+
+#: ../js/ui/status/network.js:1740
+msgid "Activation of network connection failed"
+msgstr ""
+
+#: ../js/ui/status/network.js:1993
+msgid "Networking is disabled"
+msgstr "ජාලය අක්‍රීයයි"
+
+#: ../js/ui/status/network.js:2117
+msgid "Network Manager"
+msgstr "ජාල කළමණාකරු"
+
+#: ../js/ui/status/power.js:59
+msgid "Battery"
+msgstr "බැටරිය"
+
+#: ../js/ui/status/power.js:76
+msgid "Power Settings"
+msgstr "බැටරි සැකසුම්"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:98
+msgid "Estimating..."
+msgstr "ගණනය කරමින්..."
+
+#: ../js/ui/status/power.js:105
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "පැය %d ක් ඉතිරියි"
+msgstr[1] "පැය %d ක් ඉතිරියි"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:108
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "%d %s %d %s ක් ඉතිරිව ඇත"
+
+#: ../js/ui/status/power.js:110
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "පැයක්"
+msgstr[1] "පැය"
+
+#: ../js/ui/status/power.js:110
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "මිනිත්තුව"
+msgstr[1] "මිනිත්තු"
+
+#: ../js/ui/status/power.js:113
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "මිනිත්තු %d ක් ඉතිරියි"
+msgstr[1] "මිනිත්තු %d ක් ඉතිරියි"
+
+#: ../js/ui/status/power.js:116 ../js/ui/status/power.js:186
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d%%"
+
+#: ../js/ui/status/power.js:193
+msgid "AC adapter"
+msgstr "AC ඇප්ටරය"
+
+#: ../js/ui/status/power.js:195
+msgid "Laptop battery"
+msgstr "ලැප්ටොප් බැටරිය"
+
+#: ../js/ui/status/power.js:197
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:199
+msgid "Monitor"
+msgstr "මොනිටරය"
+
+#: ../js/ui/status/power.js:201
+msgid "Mouse"
+msgstr "මවුසය"
+
+#: ../js/ui/status/power.js:205
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:207
+msgid "Cell phone"
+msgstr ""
+
+#: ../js/ui/status/power.js:209
+msgid "Media player"
+msgstr ""
+
+#: ../js/ui/status/power.js:211
+msgid "Tablet"
+msgstr ""
+
+#: ../js/ui/status/power.js:213
+msgid "Computer"
+msgstr ""
+
+#: ../js/ui/status/volume.js:25 ../js/ui/status/volume.js:38
+msgid "Volume"
+msgstr "හඬ"
+
+#: ../js/ui/status/volume.js:50
+msgid "Microphone"
+msgstr ""
+
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/telepathyClient.js:225
+msgid "Invitation"
+msgstr ""
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:278
+msgid "Call"
+msgstr ""
+
+#. We got the TpContact
+#: ../js/ui/telepathyClient.js:294
+msgid "File Transfer"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:376
+msgid "Subscription request"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:412
+msgid "Connection error"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:670
+#, c-format
+msgid "%s is online."
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:674
+#, c-format
+msgid "%s is offline."
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:678
+#, c-format
+msgid "%s is away."
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:681
+#, c-format
+msgid "%s is busy."
+msgstr ""
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/telepathyClient.js:894
+#, no-c-format
+msgid "Sent at <b>%X</b> on <b>%A</b>"
+msgstr ""
+
+#. Translators: this is a time format in the style of "Wednesday, May 25",
+#. shown when you get a chat message in the same year.
+#: ../js/ui/telepathyClient.js:900
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+msgstr ""
+
+#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
+#. shown when you get a chat message in a different year.
+#: ../js/ui/telepathyClient.js:905
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+msgstr ""
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/telepathyClient.js:947
+#, c-format
+msgid "%s is now known as %s"
+msgstr ""
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1049
+#, c-format
+msgid "Invitation to %s"
+msgstr ""
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/telepathyClient.js:1057
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1059 ../js/ui/telepathyClient.js:1138
+#: ../js/ui/telepathyClient.js:1236
+msgid "Decline"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1060 ../js/ui/telepathyClient.js:1139
+#: ../js/ui/telepathyClient.js:1237
+msgid "Accept"
+msgstr ""
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1090
+#, c-format
+msgid "Video call from %s"
+msgstr ""
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/telepathyClient.js:1093
+#, c-format
+msgid "Call from %s"
+msgstr ""
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/telepathyClient.js:1100
+msgid "Answer"
+msgstr ""
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/telepathyClient.js:1132
+#, c-format
+msgid "%s is sending you %s"
+msgstr ""
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/telepathyClient.js:1201
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1294
+msgid "Network error"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1296
+msgid "Authentication failed"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1298
+msgid "Encryption error"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1300
+msgid "Certificate not provided"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1302
+msgid "Certificate untrusted"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1304
+msgid "Certificate expired"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1306
+msgid "Certificate not activated"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1308
+msgid "Certificate hostname mismatch"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1310
+msgid "Certificate fingerprint mismatch"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1312
+msgid "Certificate self-signed"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1314
+msgid "Status is set to offline"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1316
+msgid "Encryption is not available"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1318
+msgid "Certificate is invalid"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1320
+msgid "Connection has been refused"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1322
+msgid "Connection can't be established"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1324
+msgid "Connection has been lost"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1326
+msgid "This resource is already connected to the server"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1328
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1330
+msgid "The account already exists on the server"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1332
+msgid "Server is currently too busy to handle the connection"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1334
+msgid "Certificate has been revoked"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1336
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1338
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/telepathyClient.js:1348
+#, c-format
+msgid "Connection to %s failed"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1357
+msgid "Reconnect"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1358
+msgid "Edit account"
+msgstr ""
+
+#: ../js/ui/telepathyClient.js:1404
+msgid "Unknown reason"
+msgstr ""
+
+#: ../js/ui/userMenu.js:133
+msgid "Hidden"
+msgstr ""
+
+#: ../js/ui/userMenu.js:139
+msgid "Idle"
+msgstr ""
+
+#: ../js/ui/userMenu.js:142
+msgid "Unavailable"
+msgstr ""
+
+#: ../js/ui/userMenu.js:579 ../js/ui/userMenu.js:583 ../js/ui/userMenu.js:653
+msgid "Power Off..."
+msgstr ""
+
+#: ../js/ui/userMenu.js:615
+msgid "Notifications"
+msgstr "දැණුම් දීම්"
+
+#: ../js/ui/userMenu.js:623
+msgid "Online Accounts"
+msgstr "සබැඳි ගිණුම්"
+
+#: ../js/ui/userMenu.js:627
+msgid "System Settings"
+msgstr "පද්ධති සැකසුම්"
+
+#: ../js/ui/userMenu.js:634
+msgid "Lock Screen"
+msgstr "තිරය අඟුලු දමන්න"
+
+#: ../js/ui/userMenu.js:639
+msgid "Switch User"
+msgstr "පරිශීලක සීරුමාරුව"
+
+#: ../js/ui/userMenu.js:644
+msgid "Log Out..."
+msgstr "පිටවීම..."
+
+#: ../js/ui/userMenu.js:672
+msgid "Your chat status will be set to busy"
+msgstr ""
+
+#: ../js/ui/userMenu.js:673
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/viewSelector.js:113
+msgid "Type to search..."
+msgstr ""
+
+#: ../js/ui/viewSelector.js:131 ../src/shell-util.c:244
+msgid "Search"
+msgstr "සෙවුම"
+
+#: ../js/ui/wanda.js:124
+#, c-format
+msgid ""
+"Sorry, no wisdom for you today:\n"
+"%s"
+msgstr ""
+
+#: ../js/ui/wanda.js:128
+#, c-format
+msgid "%s the Oracle says"
+msgstr ""
+
+#: ../js/ui/wanda.js:168
+msgid "Your favorite Easter Egg"
+msgstr ""
+
+#: ../js/ui/windowAttentionHandler.js:33
+#, c-format
+msgid "'%s' is ready"
+msgstr ""
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1100
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u ප්‍රථිදානය"
+msgstr[1] "%u ප්‍රථිදාන"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1110
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ප්‍රදානය"
+msgstr[1] "%u ප්‍රදාන"
+
+#: ../src/gvc/gvc-mixer-control.c:1408
+msgid "System Sounds"
+msgstr "පද්ධති හඬ"
+
+#: ../src/main.c:262
+msgid "Print version"
+msgstr "මුද්‍රණ වෙළුම"
+
+#: ../src/main.c:268
+msgid "Mode used by GDM for login screen"
+msgstr ""
+
+#: ../src/shell-app.c:617
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr ""
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr "එක්සත් රාජධානිය"
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr "පෙරනිමිය"
+
+#: ../src/shell-polkit-authentication-agent.c:332
+msgid "Authentication dialog was dismissed by the user"
+msgstr ""
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:89
+msgid "Home"
+msgstr "නිවස"
+
+#. Translators: this is the same string as the one found in
+#. * nautilus
+#: ../src/shell-util.c:98
+msgid "File System"
+msgstr "ගොනු පද්ධතිය"
+
+#. Translators: the first string is the name of a gvfs
+#. * method, and the second string is a path. For
+#. * example, "Trash: some-directory". It means that the
+#. * directory called "some-directory" is in the trash.
+#.
+#: ../src/shell-util.c:294
+#, c-format
+msgid "%1$s: %2$s"
+msgstr "%1$s: %2$s"
+
diff --git a/po/sk.po b/po/sk.po
new file mode 100644
index 0000000..60fb55e
--- /dev/null
+++ b/po/sk.po
@@ -0,0 +1,3717 @@
+# Slovak translation for gnome-shell.
+# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Peter Mráz <etkinator@gmail.com>, 2011.
+# Michal Štrba <faiface2202@gmail.com>, 2011.
+# Andrej Shadura <andrew@shadura.me>, 2019.
+# Matej Mrenica <matejm98mthw@gmail.com>, 2021.
+# Dušan Kazik <prescott66@gmail.com>, 2012-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-11-19 16:31+0000\n"
+"PO-Revision-Date: 2022-11-22 23:07+0100\n"
+"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
+"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
+"Language: sk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
+"X-Generator: Poedit 3.2.1\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Project-Style: gnome\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Spúšťače"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktivovať obľúbenú aplikáciu č. 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktivovať obľúbenú aplikáciu č. 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktivovať obľúbenú aplikáciu č. 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktivovať obľúbenú aplikáciu č. 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktivovať obľúbenú aplikáciu č. 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktivovať obľúbenú aplikáciu č. 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktivovať obľúbenú aplikáciu č. 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktivovať obľúbenú aplikáciu č. 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktivovať obľúbenú aplikáciu č. 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Snímky obrazovky"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Interaktívne zachytávanie snímkov obrazovky"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Zachytenie snímky obrazovky"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Zachytenie snímky obrazovky okna"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Interaktívny záznam obrazovky"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Systém"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Zobraziť zoznam s oznámeniami"
+
+# nazov klavesovej skratky
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Zamerať aktívne oznámenie"
+
+# nazov klavesovej skratky
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Zobraziť prehľad"
+
+# nazov klavesovej skratky
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Zobraziť všetky aplikácie"
+
+# nazov klavesovej skratky
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Otvoriť ponuku aplikácií"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Shell prostredia GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Správa okien a spúšťanie aplikácií"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Povoliť vnútorné nástroje užitočné pre vývojárov a testerov z dialógového "
+"okna Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Umožňuje prístup k vnútorným ladiacim a sledovacím nástrojom pomocou "
+"dialógového okna Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Vlastnosť UUID rozšírení určených na povolenie"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Rozšírenia pre Shell prostredia GNOME majú vlastnosť UUID; tento kľúč "
+"obsahuje zoznam rozšírení, ktoré by mali byť načítané. Každé rozšírenie, "
+"ktoré je potrebné načítať, musí byť v tomto zozname. Tento zoznam môžete "
+"upraviť aj ručne pomocou metód DBus EnableExtension a DisableExtension v org."
+"gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Vlastnosť UUID rozšírení určených na nútené vypnutie"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Rozšírenia pre prostredie GNOME Shell majú vlastnosť UUID; tento kľúč "
+"obsahuje zoznam rozšírení, ktoré by mali byť zakázané, aj keď sú načítané "
+"ako súčasť aktuálneho režimu. Tento zoznam môžete upraviť aj ručne pomocou "
+"metód D-Bus EnableExtension a DisableExtension v org.gnome.Shell. Tento kľúč "
+"má prednosť pred nastavením „enabled-extensions“."
+
+# summary
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Zakázať rozšírenia používateľa"
+
+# description
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Zakáže všetky rozšírenia povolené požívateľom bez vplyvu na nastavenie "
+"„enabled-extension“."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Zakáže kontrolu kompatibility verzií rozšírení"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Shell prostredia GNOME načíta len rozšírenia, ktoré deklarujú podporu pre "
+"práve spustenú verziu prostredia. Povolením tejto voľby zakážete túto "
+"kontrolu a umožníte načítanie všetkých rozšírení bez ohľadu na to, pre ktorú "
+"verziu prostredia deklarujú podporu."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Zoznam identifikátorov súborov plochy pre obľúbené aplikácie"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Aplikácie zodpovedajúce týmto identifikátorom budú zobrazené medzi "
+"obľúbenými aplikáciami."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "História dialógového okna príkazov (Alt-F2)"
+
+# * https://live.gnome.org/GnomeShell/LookingGlass
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "História dialógového okna integrovaného odlaďovača"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Vždy zobraziť položku „Odhlásiť sa“ v ponuke používateľa."
+
+# description
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Tento kľúč preváži automatické skrývanie položky „Odhlásiť sa“ v situáciách "
+"s jedným používateľom alebo jednou reláciou."
+
+# summary
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Určiť, či bude zapamätané heslo pre pripojenie zašifrovaných alebo "
+"prenosných súborových systémov"
+
+# description
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Shell bude po pripojení zašifrovaného alebo prenosného súborového systému "
+"požadovať heslo. Ak bude možné toto heslo uložiť pre neskoršie použitie, "
+"zobrazí sa zaškrtávacie pole „Zapamätať heslo“. Tento kľúč nastaví "
+"predvolený stav zaškrtávacieho poľa."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Naposledy vybraný profil napájania iný ako predvolený"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Niektoré systémy podporujú viac ako dva profily napájania. Kvôli "
+"pretrvávajúcej podpore prepínania medzi dvoma profilmi, tento kľúč "
+"zaznamenáva naposledy vybraný profil, ktorý je odlišný od predvoleného."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"Posledná verzia, pre ktorú sa zobrazilo dialógové okno „Vitajte v GNOME“"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Tento klúč určuje, pre ktorú verziu sa naposledy zobrazilo dialógové okno "
+"„Vitajte v GNOME“. Prázdny reťazec predstavuje najstaršiu možnú verziu a "
+"veľké číslo bude predstavovať verzie, ktoré zatiaľ neexistujú. Toto veľké "
+"číslo je možné použiť na efektívne deaktivovanie dialógového okna."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Rozloženie nástroja na výber aplikácie"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Rozloženie nástroja na výbe raplikácie. Každý záznam v poli je stránkou. "
+"Stránky sú uložené v poradí, v akom sú viditeľné v Shelli prostredia GNOME. "
+"Každá stránka obsahuje pár záznamov „application id“ → „údaje“. Momentálne "
+"sú nasledovné hodnoty uložené ako „údaje“: • “„pozícia“: pozícia ikony "
+"aplikácie na stránke."
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Klávesová skratka na otvorenie ponuky aplikácií"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Klávesová skratka na otvorenie ponuky aplikácií."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Kombinácia kláves na prepínanie medzi stavmi prehľadu"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Klávesová skratka na prepínanie medzi sedením, výberom okna a mriežkou "
+"aplikácií"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Klávesová skratka slúži na prepínanie medzi mriežkou aplikácií, výberom okna "
+"a sedením"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Klávesová skratka na otvorenie pohľadu „Zobraziť aplikácie“"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Klávesová skratka na otvorenie pohľadu „Zobraziť aplikácie“ v prehľade "
+"aktivít."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Klávesová skratka na otvorenie prehľadu"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Klávesová skratka na otvorenie prehľadu aktivít."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Klávesová skratka na prepnutie viditeľnosti zoznamu s oznámeniami"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Klávesová skratka na prepnutie viditeľnosti zoznamu s oznámeniami."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Klávesová skratka na zameranie aktívnych oznámení"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Klávesová skratka, s ktorou sa zamerá na aktívne oznámenia."
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Prepnúť na aplikáciu č. 1"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Prepnúť na aplikáciu č. 2"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Prepnúť na aplikáciu č. 3"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Prepnúť na aplikáciu č. 4"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Prepnúť na aplikáciu č. 5"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Prepnúť na aplikáciu č. 6"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Prepnúť na aplikáciu č. 7"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Prepnúť na aplikáciu č. 8"
+
+# tooltip
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Prepnúť na aplikáciu č. 9"
+
+# summary
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Obmedziť prepínač na aktuálny pracovný priestor."
+
+# desc
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ak je true, iba aplikácie, ktoré majú okná na aktuálnom pracovnom priestore "
+"budú zobrazené v prepínači. Inak budú zahrnuté všetky aplikácie."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Režim ikonizácie aplikácií."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Konfiguruje, ako sa majú zobraziť okná v prepínači. Platné možnosti sú "
+"„thumbnail-only“ (zobrazí miniatúru okna), „app-icon-only“ (zobrazí iba "
+"ikonu aplikácie) alebo „both“ (zobrazí oboje)."
+
+# desc
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ak je true, iba okná z aktuálneho pracovného priestoru budú zobrazené v "
+"prepínači. Inak budú zahrnuté všetky okná."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Umiestnenia"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Zobrazené umiestnenia v svetových časoch"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatické umiestnenie"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Určuje, či sa má získať aktuálne umiestnenie alebo nie"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Umiestnenie"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Umiestnenie, pre ktoré zobraziť predpoveď"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Pripojiť modálne dialógové okno k rodičovskému oknu"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Tento kľúč preváži kľúč v org.gnome.mutter po spustení Shellu prostredia "
+"GNOME."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Povoliť natiahnutie k okraju pri pustení okien na okrajoch obrazovky"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Pracovné priestory sú spravované dynamicky"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Pracovné priestory sú iba na primárnom monitore"
+
+# summary
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Oneskoriť pohyb zamerania v režime myši, až kým sa ukazovateľ nezastaví"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Sieťové prihlásenie"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Niečo sa pokazilo"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Ospravedlňujeme sa, ale vyskytol sa problém: Nastavenia tohoto rozšírenia sa "
+"nedajú zobraziť. Odporúčame vám nahlásiť problém autorom rozšírenia."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Technické podrobnosti"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Domovská stránka"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Navštíviť domovskú stránku rozšírenia"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Zrušiť"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Heslo"
+
+# button
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Vybrať reláciu"
+
+# https://bugzilla.gnome.org/show_bug.cgi?id=659972
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Nie ste v zozname?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(napr., používateľ alebo %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Používateľské meno"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Prihlasovacie okno"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Chyba pri overovaní totožnosti"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(alebo prejdite prstom po čítačke)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(alebo priložte prst na čítačku)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Vypnúť"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "vypnúť;vypnutie;zastaviť;zastavenie"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Reštartovať"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reštartovať;reštart;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Uzamknúť obrazovku"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "uzamknúť;zámok;obrazovku;obrazovky"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Odhlásiť sa"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "odhlásiť;odhlásenie"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Uspať"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "uspať;režim;spánku"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Prepnúť používateľa"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "prepnúť;prepnutie;používateľa"
+
+# action button
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "uzamknúť orientáciu;odomknúť orientáciu;obrazovka;otočenie"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Zachytiť snímku obrazovky"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "snímka;obrazovky;záznam;nahrávka;výrez"
+
+# action button
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Odomknúť otočenie obrazovky"
+
+# action button
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Uzamknúť otočenie obrazovky"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Príkaz nebol nájdený"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Nepodarilo sa analyzovať príkaz:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Spustenie „%s“ zlyhalo:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Práve teraz"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Pred %d minútami"
+msgstr[1] "Pred %d minútou"
+msgstr[2] "Pred %d minútami"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Pred %d hodinami"
+msgstr[1] "Pred %d hodinou"
+msgstr[2] "Pred %d hodinami"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Včera"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Pred %d dňami"
+msgstr[1] "Pred %d dňom"
+msgstr[2] "Pred %d dňami"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Pred %d týždňami"
+msgstr[1] "Pred %d týždňom"
+msgstr[2] "Pred %d týždňami"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Pred %d mesiacmi"
+msgstr[1] "Pred %d mesiacom"
+msgstr[2] "Pred %d mesiacmi"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Pred %d rokmi"
+msgstr[1] "Pred %d rokom"
+msgstr[2] "Pred %d rokmi"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Včera o %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H:%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d. %B, %H:%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d. %B %Y, %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Včera, %l:%M %p"
+
+# DK: je tento format casu spravne? neviem zistit co ktore premenne znamenaju.
+# ma sa v preklade zmenit "%e" na "%d" ?
+# v ostatnych retazcoch je pouzite %e, tak to bude asi OK
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l:%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d. %B, %l:%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d. %B %Y, %l:%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Prihlásenie do aktívneho bodu"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Vaše pripojenie s prihlásením k tomuto aktívnemu bodu nie je bezpečné. Heslá "
+"alebo iné informácie, ktoré ste zadali na tejto stránke môžu byť zobrazené "
+"ľuďmi vo vašom okolí."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Odmietnuť prístup"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Udeliť prístup"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "Nepomenovaný priečinok"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "Aplikácia %s bola pripnutá k doku."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Aplikácii %s bolo zrušené pripnutie k doku."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Otvorené okná"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nové okno"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Zobraziť podrobnosti"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Ukončiť"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Zrušiť pripnutie"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Pripnúť k doku"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Spustiť pomocou integrovanej grafickej karty"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Spustiť pomocou diskrétnej grafickej karty"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Výber audio zariadenia"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Nastavenia zvuku"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Slúchadlá"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Náhlavná súprava"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofón"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Zmeniť pozadie…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Nastavenia displeja"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Nastavenia"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+# MČ: nemali by tu byť tiež jednopísmenové? Občas som videl v kolendároch aj také použiť a ďalej sú dvojpísmenové.
+# PK: v grid to skratene bude, inac v liste to moze byt 2 pismenove, kedze aj v
+# originale dali Su
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "N"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "U"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Š"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Predchádzajúci mesiac"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Nasledujúci mesiac"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V. týždeň"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Žiadne oznámenia"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Nevyrušovať"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Vymazať"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "Aplikácia „%s“ neodpovedá."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Môžete chvíľu počkať na pokračovanie aplikácie, alebo vynútiť úplné "
+"ukončenie aplikácie."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Vynútiť ukončenie"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Počkať"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Externá jednotka bola pripojená"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Externá jednotka bola odpojená"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Nepodarilo sa odomknúť zväzok"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Nainštalovaná verzia programu udisks nepodporuje nastavenie PIM"
+
+# DK: doplnil som slovo "programu", aby to znelo prirodzenejsie. priklad:po pripojeni USB kluca bolo zobrazene "Otvoriť pomocou Súbory"
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Otvoriť pomocou aplikácie %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Pripojiť sa môžete taktiež stlačením tlačidla „WPS“ na vašom smerovači."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Pripojiť"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Kľúč"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Heslo k súkromnému kľúču"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identita"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Služba"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Požaduje sa overenie totožnosti"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Na prístup do bezdrôtovej siete „%s“ sú vyžadované heslá alebo šifrovacie "
+"kľúče."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Overenie totožnosti k drôtovej sieti 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Názov siete"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Overenie totožnosti k DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Požaduje sa kód PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Pre zariadenie mobilnej širokopásmovej siete je potrebný kód PIN"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Na pripojenie k „%s“ sa požaduje heslo."
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "Správca siete"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Heslo siete VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Požaduje sa overenie totožnosti"
+
+# PŠ: ináč toto je riadna hlúposť, keďže sa pýta heslo používateľa "root"
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Správca"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Overiť totožnosť"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Prepáčte, ale nezabralo to. Skúste to, prosím, znova."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Kontakt %s odteraz vystupuje ako %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Okná"
+
+# tooltip
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Zobrazí aplikácie"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dok"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-e. %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %-e. %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-e. %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-e. %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Dnes"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Zajtra"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Celý deň"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d.%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Žiadne udalosti"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Pridať svetové časy…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Svetové časy"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Načítava sa…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Aby sa mohli zobraziť informácie o počasí, musíte sa pripojiť"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Informácie o počasí nie sú momentálne dostupné"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Počasie"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Vybrať umiestnenie pre počasie…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Odhlásenie používateľa %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Odhlásenie"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Používateľ %s bude automaticky odhlásený o %d sekúnd."
+msgstr[1] "Používateľ %s bude automaticky odhlásený o %d sekundu."
+msgstr[2] "Používateľ %s bude automaticky odhlásený o %d sekundy."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Budete automaticky odhlásený o %d sekúnd."
+msgstr[1] "Budete automaticky odhlásený o %d sekundu."
+msgstr[2] "Budete automaticky odhlásený o %d sekundy."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Odhlásiť sa"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Vypnutie"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Inštalácia aktualizácií a vypnutie"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Systém sa automaticky vypne o %d sekúnd."
+msgstr[1] "Systém sa automaticky vypne o %d sekundu."
+msgstr[2] "Systém sa automaticky vypne o %d sekundy."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Nainštalovať čakajúce aktualizácie softvéru"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Vypnúť"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Reštart"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Inštalácia aktualizácií a reštart"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Systém sa automaticky reštartuje o %d sekúnd."
+msgstr[1] "Systém sa automaticky reštartuje o %d sekundu."
+msgstr[2] "Systém sa automaticky reštartuje o %d sekundy."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Reštartovať"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Reštart a inštalácia aktualizácií"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Systém sa automaticky reštartuje a nainštaluje aktualizácie o %d sekúnd."
+msgstr[1] ""
+"Systém sa automaticky reštartuje a nainštaluje aktualizácie o %d sekundu."
+msgstr[2] ""
+"Systém sa automaticky reštartuje a nainštaluje aktualizácie o %d sekundy."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Reštartovať a inštalovať"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Nainštalovať a vypnúť"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Vypnúť po inštalácii aktualizácií"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Reštart a inštalácia inovácie"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Operačný systém %s %s bude nainštalovaný po reštarte. Inštalácia inovácie "
+"môže trvať dlhšiu dobu: uistite sa, že máte vytvorenú zálohu, a že počítač "
+"je pripojený k zdroju napájania."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Batéria je vybitá: pred inštaláciou aktualizácií pripojte napájací zdroj."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Niektoré aplikácie sú zaneprázdnené, alebo obsahujú neuloženú prácu"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Sú prihlásení iní používatelia"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Voľby zavádzania"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (vzdialená relácia)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konzola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Inštalovať"
+
+# dialog title
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Inštalácia rozšírenia"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Prevziať a nainštalovať rozšírenie „%s“ z extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Sú dostupné aktualizácie rozšírení"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Aktualizácie rozšírení sú pripravené na inštaláciu."
+
+# dialog title
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Umožnenie potlačenia skratiek"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Aplikácia %s chce potlačiť skratky"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Aplikácia chce potlačiť skratky"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Skratky môžete obnoviť stlačením %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Odmietnuť"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Povoliť"
+
+# z gnome control center
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Pomalé klávesy zapnuté"
+
+# z gnome control center
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Pomalé klávesy vypnuté"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Práve ste podržali stlačenú klávesu Shift po dobu 8 sekúnd. Je to skratka "
+"funkcie Pomalé klávesy, ktorá mení spôsob fungovania vašej klávesnice."
+
+# z gnome control center
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Lepkavé klávesy zapnuté"
+
+# z gnome control center
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Lepkavé klávesy vypnuté"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Práve ste stlačili klávesu Shift 5krát za sebou. je to skratka funkcie "
+"Lepkavé klávesy, ktorá mení spôsob fungovania vašej klávesnice."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Práve ste stlačili dve klávesy naraz, alebo ste stlačili klávesu Shift 5krát "
+"za sebou. Tým sa vypne funkcia Lepkavé klávesy, ktorá mení spôsob fungovania "
+"vašej klávesnice."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Ponechať zapnuté"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Zapnúť"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Vypnúť"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Ponechať vypnuté"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Nastavenia oblasti a jazyka"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Žiadne nainštalované rozšírenia"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nevyslal žiadnu chybu."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Skryť chyby"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Zobraziť chyby"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Povolené"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Zakázané"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Chyba"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Neaktuálne"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Sťahuje sa"
+
+# PK: ide tu o zdrojovy kod?
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Zobraziť zdroj"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Webová stránka"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Systém prešiel do nezabezpečeného režimu"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Aplikácie teraz majú neobmedzený prístup"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Vrátiť"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Prihlásený ako privilegovaný používateľ"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Spusteniu relácie pod privilegovaným používateľom by sa malo z "
+"bezpečnostných dôvodov zabrániť. Ak je to možné, mali by ste sa prihlásiť "
+"ako normálny používateľ."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Uzamknutie obrazovky je zakázané"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Uzamykanie obrazovky vyžaduje správcu displejov prostredia GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Informácie o systéme"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Neznámy interpret"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Neznámy názov"
+
+# hint_text
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Zadajte text na vyhľadanie"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Aplikácie"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Prehľad"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nová skratka…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Definované aplikáciou"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Zobraziť pomocníka na obrazovke"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Prepnúť monitor"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Priradiť klávesu"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Dokončiť"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Upraviť…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Žiadne"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Stlačte kláves na spustenie konfigurácie"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Stlačte kláves Esc na skončenie"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Stlačte akýkoľvek kláves na skončenie"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Aktivity"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Systém"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Horná lišta"
+
+# dialog title
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Spustenie príkazu"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Stlačte kláves ESC na zavretie"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Reštart nie je dostupný s protokolom Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Reštartuje sa…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Prostredie GNOME vyžaduje uzamknutie obrazovky"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Nepodarilo sa uzamknúť obrazovku"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Uzamknutie bolo zablokované aplikáciou"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Výber"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Výber oblasti"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Obrazovka"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Výber obrazovky"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Okno"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Výber okna"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Snímka / záznam obrazovky"
+
+# tooltip
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Zobrazí ukazovateľ"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Záznamy obrazovky"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Záznam obrazovky zo dňa %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Snímka obrazovky"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Záznam obrazovky zaznamenaný"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Kliknutím sem zobrazíte video."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Zobraziť v aplikácii Súbory"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Snímka obrazovky zo dňa %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Snímka obrazovky zachytená"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Môžete vložiť obrázok zo schránky."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Snímka obrazovky bola zachytená"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Hľadá sa…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Žiadne výsledky."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d ďalších"
+msgstr[1] "%d ďalší"
+msgstr[2] "%d ďalšie"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Hľadať"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopírovať"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Prilepiť"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Zobraziť text"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Skryť text"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Je zapnutá funkcia Caps Lock."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Skrytý zväzok"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Zväzok so systémom Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Využíva kľúčové súbory"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "Na odomknutie zväzku s kľúčovými súbormi použite nástroj <i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Číslo PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Zapamätať heslo"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Odomknúť"
+
+# DK: doplnil som slovo "programu", aby to znelo prirodzenejsie. priklad:po pripojeni USB kluca bolo zobrazene "Otvoriť pomocou Súbory"
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Otvoriť %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM musí byť číslo alebo prázdne."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Nepodarilo sa spustiť %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Nepodarilo sa nájsť aplikáciu %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Zjednodušenie ovládania"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Vysoký kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Lupa"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Čítačka obrazovky"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Klávesnica na obrazovke"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Vizuálne varovania"
+
+# z gnome control center
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Lepkavé klávesy"
+
+# z gnome control center
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Pomalé klávesy"
+
+# z gnome control center
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Poskakujúce klávesy"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Myš klávesmi"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Veľký text"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automaticky otáčať"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Jas"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Tmavý režim"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Jednoduché kliknutie"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dvojité kliknutie"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Ťahanie"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Druhotné kliknutie"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Kliknutie zotrvaním"
+
+#: js/ui/status/keyboard.js:833
+msgid "Keyboard"
+msgstr "Klávesnica"
+
+# menu item
+#: js/ui/status/keyboard.js:850
+msgid "Show Keyboard Layout"
+msgstr "Zobraziť rozloženie klávesnice"
+
+# dialog title
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Umožnenie prístupu k umiestneniu"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Aplikácia %s chce získať prístup k vášmu umiestneniu"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Prístup k umiestneniu sa dá kedykoľvek zmeniť v nastaveniach súkromia."
+
+# zariadenie
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<neznáme>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Odpojiť zariadenie %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Pripojiť k zariadeniu %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Aktívny bod %s"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Nočné osvetlenie"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Výkon"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Vyvážený"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Šetrič energie"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Profily napájania"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "Zastaviť záznam obrazovky"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "Zastaviť sprístupnenie obrazovky"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Režim v lietadle"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Zachytiť snímku obrazovky"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Ponuka vypnutia"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Uspať"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Reštartovať…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Vypnúť…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Odhlásiť sa…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Prepnúť používateľa…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Uzamknúť obrazovku"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Neznáme zariadenie Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Počas vašej neprítomnosti bolo zistené nové zariadenie. Prosím, odpojte a "
+"znovu pripojte zariadenie, aby ste ho mohli začať používať."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Zariadenie Thunderbolt s neoverenou totožnosťou"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Bolo zistené nové zariadenie a vyžaduje overenie totožnosti od správcu."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Chyba pri overovaní totožnosti zariadenia Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Nepodarilo sa overiť totožnosť zariadenia Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Hlasitosť bola zmenená"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Hlasitosť"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Zvukový výstup"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Zvukový vstup"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Zrkadliť"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Zlúčiť displeje"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Iba externý"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Iba vstavaný"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %e. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Odomknete potiahnutím nahor"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Odomknete kliknutím alebo stlačením klávesy"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Odomykacie okno"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Prihlásiť ako iný používateľ"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Vitajte v prostredí GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Ak sa chcete zorientovať, vyskúšajte prehliadku."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nie ďakujem"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Zúčastniť sa prehliadky"
+
+# %s je totiž titulok okna
+# informacna bublina
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Aplikácia „%s“ je pripravená"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Ponechať nastavenia displeja?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Vrátiť nastavenia"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Uchovať zmeny"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Zmeny nastavení budú vrátené o %d sekúnd"
+msgstr[1] "Zmeny nastavení budú vrátené o %d sekundu"
+msgstr[2] "Zmeny nastavení budú vrátené o %d sekundy"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Skryť"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Obnoviť"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximalizovať"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Presunúť"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Zmeniť veľkosť"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Presunúť titulok okna na obrazovku"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Vždy navrchu"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Vždy vo viditeľnom pracovnom priestore"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Presunúť do ľavého pracovného priestoru"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Presunúť do pravého pracovného priestoru"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Presunúť do vyššieho pracovného priestoru"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Presunúť do nižšieho pracovného priestoru"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Presunúť na monitor vyššie"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Presunúť na monitor nižšie"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Presunúť na monitor vľavo"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Presunúť na monitor vpravo"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Zavrieť"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Kalendár Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Verzia pre tlač"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Režim používaný správcom GDM pre prihlasovaciu obrazovku"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Použitie zvláštneho režimu, napr. „gdm“ pre prihlasovaciu obrazovku"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Zoznam možných režimov"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Neznámy"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Zlyhalo spustenie „%s“"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Heslá sa nezhodujú."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Heslo nemôže byť prázdne"
+
+# rovnaký reťazec ako v PolicyKit-gnome
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Dialógové okno overenia totožnosti bolo zatvorené používateľom"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Rozšírenia"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Spravuje vaše rozšírenia prostredia GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Projekt GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Rozšírenia prostredia GNOME spravujú aktualizácie rozšírení, konfiguráciu "
+"predvolieb rozšírení a odstraňovanie alebo zakázanie nechcených rozšírení."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Nastavenie rozšírení pre Shell prostredia GNOME"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Žiadne zhody"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Odstrániť rozšírenie „%s“?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Ak odstránite rozšírenie, budete ho musieť pre jeho opätovné povolenie znovu "
+"prevziať."
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Odstrániť"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Dušan Kazik <prescott66@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d rozšírení bude aktualizovaných po najbližšom prihlásení."
+msgstr[1] "%d rozšírenie bude aktualizované po najbližšom prihlásení."
+msgstr[2] "%d rozšírenia budú aktualizované po najbližšom prihlásení."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Rozšírenie nie je kompatibilné s aktuálnou verziou prostredia GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Rozšírenie narazilo na chybu"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Rozšírenie môže byť aktualizované"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Web"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Odstrániť…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Pomocník"
+
+# summary
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "O aplikácii Rozšírenia"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Rozšírenia môžu spôsobiť problémy s výkonom a stabilitou. Ak narazíte na "
+"problémy s vaším systémom, zakážte rozšírenia."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Ručne nainštalované"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Ak chcete nájsť a pridať rozšírenia, navštívte adresu <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Vstavané"
+
+# dialog title
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Žiadne nainštalované rozšírenia"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Ospravedlňujeme sa, ale nebolo možné získať zoznam nainštalovaných "
+"rozšírení. Uistite sa, že ste prihlásený do prostredia GNOME a skúste to "
+"znovu."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Sú pripravené aktualizácie rozšírení"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Nové rozšírenie bolo úspešne vytvorené v %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Názov by mal byť veľmi krátky (ideálne popisný) reťazec.\n"
+"Príklady: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Názov"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Popis je jedna veta, popisujúca, čo robí Vaše rozšírenie\n"
+"Príklady: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Popis"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID je globálne unikátny identifikátor pre Vaše rozšírenie.\n"
+"Má by vo formáte emailovej adresy (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Zvoľte jednu z dostupných šablón:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Šablóna"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Unikátny identifikátor nového rozšírenia"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NÁZOV"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Názov nového rozšírenia viditeľný používateľovi"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "POPIS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Stručný popis toho, čo robí rozšírenie"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ŠABLÓNA"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Šablóna použitá pre nové rozšírenie"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Umožní interaktívne zadávanie informácií o rozšírení"
+
+# summary
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Vytvoriť nové rozšírenie"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Neznáme parametre"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Vlastnosť UUID, názov a popis sú povinné"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Zlyhalo pripojenie k Shellu prostredia GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Rozšírenie „%s“ neexistuje\n"
+
+# summary
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Zakázať rozšírenie"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Nebol zadaný žiadny identifikátor UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Bol zadaný viac ako jeden identifikátor UUID"
+
+# summary
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Povoliť rozšírenie"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Rozšírenie „%s“ neexistuje\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Zobrazí informácie o rozšíreniach"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Prepísať existujúce rozšírenie"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "BALÍK_ROZŠÍRENÍ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Nainštaluje balík rozšírení"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Nie je určený žiadny balík rozšírení"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Je určený viac ako jeden balík rozšírení"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Zobrazí rozšírenia nainštalované používateľom"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Zobrazí systémom nainštalované rozšírenia"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Zobrazí zapnuté rozšírenia"
+
+# summary
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Zobrazí vypnuté rozšírenia"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Zobrazí rozšírenia s predvoľbami"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Zobrazí rozšírenia s aktualizáciami"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Vypíše podrobnosti o rozšírení"
+
+# summary
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Zoznam nainštalovaných rozšírení"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "SÚBOR"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Dodatočný zdroj, ktorý má byť zahrnutý v balíku"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHÉMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Schéma GSettings, ktorá by mala byť zahrnutá"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ADRESÁR"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Adresár, v ktorom sa nachádzajú preklady"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMÉNA"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Doména gettext použitá pre preklady"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Prepíše existujúci balík"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Adresár, kde by mal byť vytvorený balík"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ZDROJOVÝ_ADRESÁR"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Vytvorí balík rozšírení"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Bol určený viac ako jeden zdrojový adresár"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Rozšírenie „%s“ neobsahuje predvoľby\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Zlyhalo otvorenie predvolieb rozšírenia „%s“: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Otvorí predvoľby rozšírenia"
+
+# summary
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Obnoví rozšírenie"
+
+# summary
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Nedajú sa odinštalovať systémové rozšírenia\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Zlyhalo odinštalovanie rozšírenia „%s“\n"
+
+# summary
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Odinštalovať rozšírenie"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Nevypíše chybové správy"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Cesta"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Pôvodný autor"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Verzia"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stav"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "Voľba „version“ neprijíma žiadne parametre"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Použitie:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Vypíše informácie o verzii a skončí."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "PRÍKAZ"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[PARAMETRE…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Príkazy:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Zobrazí pomocníka"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Povoliť rozšírenie"
+
+# summary
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Zakázať rozšírenie"
+
+# summary
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Reštartovať rozšírenie"
+
+# summary
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Odinštalovať rozšírenie"
+
+# summary
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Zoznam rozšírení"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Zobrazí informácie o rozšírení"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Otvorí predvoľby rozšírenia"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Vytvoriť rozšírenie"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Balík rozšírení"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Nainštaluje balík rozšírení"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Na získanie podrobnejšieho pomocníka, použite príkaz „%s“.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Prázdne"
+
+# summary
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Prázdne rozšírenie"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikátor"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Pridá ikonu do hornej lišty"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u výstup"
+msgstr[1] "%u výstupy"
+msgstr[2] "%u výstupov"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u vstup"
+msgstr[1] "%u vstupy"
+msgstr[2] "%u vstupov"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Systémové zvuky"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Či má predvolený Bluetooth adaptér nastaviť jemu priradené zariadenia"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Shell zobrazí položku Bluetooth v ponuke iba vtedy, ak je zapnutý adaptér "
+#~ "Bluetooth, alebo ak boli zariadenia nastavené a priradené predvolenému "
+#~ "adaptéru. Táto voľba bude vynulovaná, ak predvolený adaptér nemal nikdy "
+#~ "priradené zariadenia."
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "Nastavenia Bluetooth"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d pripojených"
+#~ msgstr[1] "%d pripojený"
+#~ msgstr[2] "%d pripojené"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth zapnutý"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth vypnutý"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Umiestnenie je povolené"
+
+#~ msgid "Disable"
+#~ msgstr "Zakázať"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Nastavenia súkromia"
+
+#~ msgid "Location In Use"
+#~ msgstr "Používa sa umiestnenie"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Umiestnenie je zakázané"
+
+#~ msgid "Enable"
+#~ msgstr "Povoliť"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s vypnuté"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s pripojené"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s nespravované"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "Pripája sa %s"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s vyžaduje overenie totožnosti"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Chýba firmvér pre %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s nedostupné"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Pripojenie %s zlyhalo"
+
+#~ msgid "Wired Settings"
+#~ msgstr "Nastavenia drôtovej siete"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "Nastavenia mobilnej širokopásmovej siete"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Hardvér %s zakázaný"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s zakázané"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Pripojiť k internetu"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Režim v lietadle je zapnutý"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Sieť Wi-Fi je zakázaná, keď je zapnutý režim v lietadle."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Vypnúť režim v lietadle"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Sieť Wi-Fi je vypnutá"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Na pripojenie k sieti je potrebné zapnúť sieť Wi-Fi."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Zapnúť Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Siete Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Vyberte sieť"
+
+#~ msgid "No Networks"
+#~ msgstr "Žiadne siete"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Na vypnutie použite hardvérový prepínač"
+
+# item menu
+#~ msgid "Select Network"
+#~ msgstr "Vybrať sieť"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Nastavenia siete Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s nepripojené"
+
+#~ msgid "connecting…"
+#~ msgstr "pripája sa…"
+
+#~ msgid "authentication required"
+#~ msgstr "požaduje sa overenie totožnosti"
+
+#~ msgid "connection failed"
+#~ msgstr "pripojenie zlyhalo"
+
+#~ msgid "VPN Settings"
+#~ msgstr "Nastavenia VPN"
+
+#~ msgid "VPN"
+#~ msgstr "VPN"
+
+#~ msgid "VPN Off"
+#~ msgstr "Pripojenie VPN vypnuté"
+
+#~ msgid "Network Settings"
+#~ msgstr "Nastavenia siete"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s drôtové pripojenie"
+#~ msgstr[1] "%s drôtové pripojenia"
+#~ msgstr[2] "%s drôtových pripojení"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s pripojenie k sieti Wi-Fi"
+#~ msgstr[1] "%s pripojenia k sieti Wi-Fi"
+#~ msgstr[2] "%s pripojení k sieti Wi-Fi"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s pripojenie modemom"
+#~ msgstr[1] "%s pripojenia modemom"
+#~ msgstr[2] "%s pripojení modemom"
+
+#~ msgid "Connection failed"
+#~ msgstr "Pripojenie zlyhalo"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "Aktivácia pripojenia k sieti zlyhala"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Nočné osvetlenie zakázané"
+
+#~ msgid "Resume"
+#~ msgstr "Pokračovať"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Zakázať do zajtra"
+
+#~ msgid "Power Settings"
+#~ msgstr "Nastavenia napájania"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Plne nabité"
+
+#~ msgid "Not Charging"
+#~ msgstr "Nenabíja sa"
+
+#~ msgid "Estimating…"
+#~ msgstr "Odhaduje sa…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d:%02d zostáva (%d%%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d:%02d do plného nabitia (%d%%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Prebieha zdieľanie obrazovky"
+
+#~ msgid "Turn off"
+#~ msgstr "Vypnúť"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Režim v lietadle zapnutý"
+
+#~ msgid "Lock"
+#~ msgstr "Uzamknúť"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Vypnúť / Odhlásiť sa"
+
+#~ msgid "Log Out"
+#~ msgstr "Odhlásiť sa"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Odstrániť z obľúbených"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Pridať do obľúbených"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Warning"
+#~ msgstr "Upozornenie"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Rozšírenia môžu spôsobiť problémy so systémom, vrátane problémov s "
+#~ "výkonom. Ak sa stretnete s problémami s vaším systémom, odporúča sa "
+#~ "zakázať všetky rozšírenia."
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Povoliť sebapozorovacie API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Povolí D-Bus API, ktoré umožní sebapozorovanie stavu aplikácie prostredia "
+#~ "shell."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Zlyhalo pripojenie k Shellu prostredia GNOME"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimalizovať"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Zrušiť maximalizáciu"
+
+# summary
+#~ msgid "App Picker View"
+#~ msgstr "Zobrazenie výberu aplikácií"
+
+# desc
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Číslo aktuálne vybraného zobrazenia vo výbere aplikácií."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Tu sa objavia často používané aplikácie"
+
+#~ msgid "Frequent"
+#~ msgstr "Často používané"
+
+#~ msgid "All"
+#~ msgstr "Všetky"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %e. %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %e. %B %Y"
+
+# DK: pripojenie, zariadenie
+#~ msgid "Off"
+#~ msgstr "Vypnuté"
+
+# režim v lietadle
+#~ msgid "On"
+#~ msgstr "Zapnutý"
+
+#~ msgid "Copy Error"
+#~ msgstr "Skopírovať chybu"
+
+#~| msgid "Shell Extensions"
+#~ msgid "org.gnome.Extensions"
+#~ msgstr "org.gnome.Extensions"
+
+#~ msgid "Logout…"
+#~ msgstr "Odhlásiť sa…"
+
+#~ msgid "Next"
+#~ msgstr "Ďalej"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Prihlásiť sa"
+
+#~ msgid "Password: "
+#~ msgstr "Heslo: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Zadajte heslo…"
+
+# DK: je tento format casu spravne? neviem zistit co ktore premenne znamenaju.
+# ma sa v preklade zmenit "%e" na "%d" ?
+# v ostatnych retazcoch je pouzite %e, tak to bude asi OK
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %e. %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nová správa"
+#~ msgstr[1] "%d nové správy"
+#~ msgstr[2] "%d nových správ"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d nové oznámenie"
+#~ msgstr[1] "%d nové oznámenia"
+#~ msgstr[2] "%d nových oznámení"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Prehliadať v aplikácii Softvér"
+
+#~ msgid "Password:"
+#~ msgstr "Heslo:"
+
+#~ msgid "Type again:"
+#~ msgstr "Zadajte znovu:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Bezdrôtová sieť vyžaduje overenie totožnosti"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Heslo k mobilnej širokopásmovej sieti"
+
+# action button
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Uzamknúť orientáciu"
+
+#~ msgid "Rename"
+#~ msgstr "Premenovať"
+
+#~ msgid "Account Settings"
+#~ msgstr "Nastavenia účtu"
+
+# action button
+#~ msgid "Orientation Lock"
+#~ msgstr "Uzamknutie orientácie"
+
+# summary
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Klávesová skratka, ktorá pozastaví a znovu spustí všetky animácie(tween), "
+#~ "pre ladiace účely"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Ktorú klávesnicu používať"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Typ klávesnice, ktorá sa má používať."
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Vyskytla sa chyba pri načítavaní dialógového okna nastavení pre %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s celý deň."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, potom bude %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, potom bude %s a následne neskôr bude %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Pocitová teplota %s."
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Odhlásiť sa"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Prepnúť používateľa"
+
+#~ msgid "Hide tray"
+#~ msgstr "Skryť lištu"
+
+#~ msgid "Status Icons"
+#~ msgstr "Stavová ikona"
+
+#~ msgid "Events"
+#~ msgstr "Udalosti"
+
+#~ msgid "Notifications"
+#~ msgstr "Oznámenia"
+
+#~ msgid "Media"
+#~ msgstr "Médiá"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+# PM: Nie som si istý
+# TRANSLATORS: this is the title of the wifi captive portal login
+# window, until we know the title of the actual login page
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Presmerovanie webového overenia totožnosti"
+
+#~ msgid "Not In Use"
+#~ msgstr "Nepoužíva sa"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Zobraziť čísla týždňov v kalendári"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr ""
+#~ "Ak je true, zobrazí v kalendári poradie dní v týždni podľa štandardu ISO."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Použiť na pripojenie k internetu"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "Aplikácia %s požaduje prístup k vášmu umiestneniu."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Shell prostredia GNOME (kompozitor pre wayland)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d pripojené zariadenie"
+#~ msgstr[1] "%d pripojené zariadenia"
+#~ msgstr[2] "%d pripojených zariadení"
+
+#~ msgid "UPS"
+#~ msgstr "Záložný zdroj UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Batéria"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgid "Invitation"
+#~ msgstr "Pozvánka"
+
+#~ msgid "Call"
+#~ msgstr "Hovor"
+
+#~ msgid "File Transfer"
+#~ msgstr "Prenos súborov"
+
+#~ msgid "Chat"
+#~ msgstr "Rozhovor"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Pozvánka do %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "Kontakt %s vás pozýva aby ste sa pridali do %s"
+
+#~ msgid "Decline"
+#~ msgstr "Odmietnuť"
+
+#~ msgid "Accept"
+#~ msgstr "Prijať"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Videohovor od kontaktu %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Hovor od kontaktu %s"
+
+#~ msgid "Answer"
+#~ msgstr "Prijať hovor"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "Kontakt %s vám posiela %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "Kontakt %s by chcel získať oprávnenie vidieť, kedy ste pripojený"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Overenie totožnosti zlyhalo"
+
+#~ msgid "Encryption error"
+#~ msgstr "Chyba šifrovania"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Neposkytnutý certifikát"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Nedôveryhodný certifikát"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Certifikát s ukončenou platnosťou"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Neaktivovaný certifikát"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Certifikát s nesúhlasným názvom hostiteľa"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Certifikát s nesúhlasným odtlačkom"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Sebou podpísaný certifikát"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Stav je nastavený na odhlásený"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Certifikát je neplatný"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Pripojenie bolo odmietnuté"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Nedá sa nadviazať spojenie"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Spojenie sa stratilo"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Tento účet je už pripojený k serveru"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Pripojenie bolo nahradené novým, ktoré používa rovnaký zdroj"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Účet na serveri už existuje"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Server je momentálne príliš zaneprázdnený na zvládnutie pripojenia"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Certifikát bol zrušený"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Šifrovací algoritmus používaný certifikátom nie je bezpečný alebo je "
+#~ "kryptograficky slabý"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Dĺžka certifikátu servera, alebo hĺbka reťazca certifikátu servera "
+#~ "presahuje limit stanovený kryptografickou knižnicou."
+
+#~ msgid "Internal error"
+#~ msgstr "Vnútorná chyba"
+
+#~ msgid "View account"
+#~ msgstr "Zobraziť účet"
+
+# nazov klavesovej skratky
+#~ msgid "Show the message list"
+#~ msgstr "Zobraziť zoznam správ"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Ne"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Po"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Ut"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "St"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Št"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Pi"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "So"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Žiadne naplánované udalosti"
+
+#~ msgid "This week"
+#~ msgstr "Tento týždeň"
+
+#~ msgid "Next week"
+#~ msgstr "Ďalší týždeň"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Vymeniteľné zariadenia"
+
+#~ msgid "Eject"
+#~ msgstr "Vysunúť"
+
+#~ msgid "Unmute"
+#~ msgstr "Obnoviť zvuk"
+
+#~ msgid "Mute"
+#~ msgstr "Stlmiť"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Otvoriť kalendár"
+
+#~ msgid "Open"
+#~ msgstr "Otvoriť"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Vymazať správy"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Nastavenia oznámení"
+
+# MČ" zd sa mi to čudné. tuším som videl preklady „oznamovacia oblasť“, čo mi viac sedí. Toto je trošku akoby zavádzajúce, ale možno to s okolím ozrejmuje výsledok. Možno by som tam ponechal len „ponuka“ ak akolie jednoznačne naznačuje, že ide o „lištu správ“
+#~ msgid "Tray Menu"
+#~ msgstr "Ponuka v lište"
+
+# DK: zvazoval som pouzit "Panel správ"
+# neviem co bude vhodnejsie ako preklad "tray"
+#~ msgid "Message Tray"
+#~ msgstr "Lišta správ"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Prihlasovací portál"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Maximálna úroveň presnosti určenia polohy."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Konfiguruje maximálnu úroveň presnosti určenia polohy, ktorú aplikácie "
+#~ "môžu získať. Povolené sú voľby 'off' (zakáže sledovanie polohy), "
+#~ "'country' (krajina), 'city' (mesto), 'neighborhood' (okolie), "
+#~ "'street' (ulica), 'exact' (presne, pri čom je štandardne požadované "
+#~ "použitie prijímača GPS). Uvedomte si, že toto nastavenie ovplyvňuje "
+#~ "povolenia GeoClue a aplikácie môžu tiež získať údaje o polohe použitím "
+#~ "sieťových prostriedkov (aj keď prinajlepšom s presnosťou na ulicu)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Usporiadanie tlačidiel na titulku okna"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Tento kľúč preváži kľúč v org.gnome.desktop.wm.preferences po spustení "
+#~ "Shellu prostredia GNOME."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Použitím ponuky vyberte rozšírenie na nastavenie"
diff --git a/po/sl.po b/po/sl.po
new file mode 100644
index 0000000..103b83c
--- /dev/null
+++ b/po/sl.po
@@ -0,0 +1,2993 @@
+# Slovenian translations for gnome-shell.
+# Copyright (C) 2006 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Matej Urbančič <mateju@src.gnome.org>, 2009–2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 05:16+0000\n"
+"PO-Revision-Date: 2023-02-15 23:29+0100\n"
+"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
+"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
+"Language: sl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
+"n%100==4 ? 3 : 0);\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"X-Generator: Poedit 3.2.2\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Zaganjalniki"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Omogoči priljubljeni program 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Omogoči priljubljeni program 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Omogoči priljubljeni program 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Omogoči priljubljeni program 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Omogoči priljubljeni program 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Omogoči priljubljeni program 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Omogoči priljubljeni program 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Omogoči priljubljeni program 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Omogoči priljubljeni program 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Zaslonske slike"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Zajemi zaslonsko sliko interaktivno"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Zajemi zaslonsko sliko"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Zajemi zaslonsko sliko"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Posnemi zaslonski posnetek interaktivno"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistem"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Pokaži seznam obvestil"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Prikaz dejavnega obvestila"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Pokaži pregled"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Pokaži vse programe"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Odpri meni programov"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Lupina GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Upravljanje oken in zaganjanje programov"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Omogoči dostop do orodij razvijalcev in preizkuševalcev programske opreme "
+"prek vnosnega polja Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Dovoli dostop do razhroščevanja in drugih orodij nadzora prek vnosnega polja "
+"Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Določila razširitev UUID, ki bodo omogočene"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Razširitve lupine GNOME imajo nastavljeno določilo UUID; ključ določa seznam "
+"razširitev, ki naj bodo naložene ob zagonu. Upravljanje seznama je mogoče "
+"tudi prek vodila D-BUs na org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Določila razširitev UUID, ki naj bodo onemogočene"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Razširitve lupine GNOME imajo nastavljeno določilo UUID; ključ določa seznam "
+"razširitev, ki naj bodo onemogočene, neglede na to ali so del trenutnega "
+"načina razširitev. Upravljanje seznama je mogoče tudi prek vodila D-BUs na "
+"org.gnome.Shell z možnostjo Omogoči/Onemogoči razširitev. Ta nastavitev ima "
+"prednost pred nastavitvijo »omogočene razširitve«."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Onemogoči uporabniške razširitve"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Onemogoči vse razširitve, ki jih je uporabnik omogoči brez vpliva na "
+"nastavitev »omogočena razširitev«."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Onemogoči overjanje skladnosti različice razširitve"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Lupina GNOME naloži le razširitve, ki so skladne z nameščeno različico. "
+"Izbrana možnost onemogoči preverjanje, zato so lahko naložene tudi "
+"razširitve, katerih skladnost ni potrjena."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Seznam določil ID namiznih datotek priljubljenih programov"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programi, ki ustrezajo določilom, bodo prikazani v polju priljubljenih "
+"programov."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Zgodovina pogovornega okna ukazov (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Zgodovina za pogovorno okno povečevala"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Vedno pokaži možnost »Odjave« v uporabniškem meniju."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Izbira prepiše možnost samodejnega skrivanja gumba za »Odjavo« na sistemskem "
+"meniju pri eno-uporabniškem in eno-sejnem zagonu."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Ali naj se geslo za priklapljanje šifriranih in oddaljenih datotečnih "
+"sistemov shrani"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Za priklop oddaljenega datotečnega sistema ali šifrirane naprave bo po "
+"izbiri podana zahteva za vnos gesla. Na pogovornem oknu bo prikazana tudi "
+"možnost »Shrani geslo«. Ta možnost določa privzeto stanje izbirnega polja."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Nazadnje izbran neprivzet profil varčevanja"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Nekateri sistemi podpirajo več kot en profil varčevanja. Za podporo "
+"preklapljanja med dvema profiloma, nastavitev beleži nazadnje izbran "
+"neprivzet profil."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"Zadnja različica namizja, ki za katero je bilo prikazano pogovorno okno "
+"»Dobrodošlice GNOME«"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Nastavitev določa, za katero različico namizja je bilo pogovorno okno "
+"»Dobrodošlice GNOME« nazadnje prikazano. Prazen niz določa najstarejšo "
+"različico in velike številke različice, ki še ne obstajajo. S temi velikimi "
+"vrednostmi je mogoče preprečiti prikaz tega okna."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Pogled izbirnika programov"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Pogled izbirnika programov. Vsak vnos v polju je stran. Strani so "
+"razvrščene, kot se pojavijo v lupini GNOME. Vsaka stran vsebuje par podatkov "
+"»ID programa« → »podatki«. Trenutno so kot podatki shranjeni:: • »položaj«: "
+"položaj ikone programa na strani"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Tipkovna bližnjica, ki odpre meni programov"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Tipkovna bližnjica, ki odpre meni programov."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Tipkovna bližnjica za preklop med stanji pregleda"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Tipkovna bližnjica za preklop med sejo, izbirniki oken in programsko mrežo"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Tipkovna bližnjica za preklop med programsko mrežo, izbirniki oken in sejo"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Tipkovna bližnjica, ki odpre pogled »Pokaži programe«"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Tipkovna bližnjica, ki odpre pogled »Pokaži programe« v pregledu dejavnosti."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Tipkovna bližnjica, ki odpre pogled pregleda"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Tipkovna bližnjica, ki odpre pogled pregleda dejavnosti."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Tipkovna bližnjica za preklop vidnosti seznama obvestil"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Tipkovna bližnjica za preklop vidnosti seznama obvestil."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Tipkovna bližnjica za prikaz dejavnega obvestila"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Tipkovna bližnjica za prikaz dejavnega obvestila."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Preklopi na program 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Preklopi na program 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Preklopi na program 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Preklopi na program 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Preklopi na program 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Preklopi na program 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Preklopi na program 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Preklopi na program 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Preklopi na program 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Omeji preklopnik na trenutno delovno površino."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Izbrana možnost določa, da bodo v trenutnem pogledu prikazana le okna, ki so "
+"v izbrani delovni površini v preklopniku. V nasprotnem primeru so prikazana "
+"vsa okna."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Ikonski način programa."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Določa, kako naj bodo prikazana okna v preklopniku. Veljavne možnosti so »le "
+"sličice»« (pokaže le sličice oken), »le ikona« (pokaže ikono programa) in pa "
+"možnost »oboje«."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Izbrana možnost določa, da bodo v trenutnem pogledu prikazana le okna v "
+"preklopniku. V nasprotnem primeru so prikazana vsa okna."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Mesta"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Mesta, za katere naj bodo prikazani svetovni časi"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Samodejno določanje mesta"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Ali naj program pridobi trenutno mesto ali ne"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Trenutno mesto"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Mesto, za katero naj bo pokazana napoved"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Pripni modalno pogovorno okno na glavno okno"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ključ prepiše vrednost v org.gnome.mutter med zaganjanjem lupine GNOME."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Omogoči utripanje črte med spuščanjem okna na robove zaslona"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Delovne površine se obravnavajo dinamično"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Delovne površine so le na osnovnem zaslonu"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Zamik žarišča se v načinu miške spreminja, dokler se kazalnik ne neha "
+"premikati"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Omrežna prijava"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Prišlo je do nepričakovane napake"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Prišlo je do napake: nastavitev za to razširitev ni mogoče prikazati. "
+"Priporočljivo je poslati poročilo o hrošču razvijalcem programa."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Tehnične podrobnosti"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Spletna stran"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Odpri spletno stran razširitev"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Prekliči"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Geslo"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Izbor seje"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Ali vašega uporabniškega imena ni na seznamu?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(na primer, uporabnika ali %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Uporabniško ime"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Prijavno okno"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Napaka overitve"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(ali pa povlecite prst prek bralnika)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(ali pa povlecite prst po bralniku)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Izklopi"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "izklop;ugasni;ponovni zagon;reboot;zaustavi;restart;stop;shutdown"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Ponovno zaženi"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "izklop;ugasni;ponovni zagon;reboot;zaustavi;restart;stop;shutdown;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Zakleni zaslon"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "zaklep zaslona"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Odjava"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "odjava;izpis;izklop;logout"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "V pripravljenost"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "v pripravljenost;v mirovanje"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Preklop uporabnika"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "preklop uporabnika"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"zaklep usmerjenosti;zaslon;rotacija;obrat;obračanje;vrtenje;zavrti;rotacija"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Zajemi zaslonsko sliko"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"zaslon;zaslonska slika;zaslonski posnetek;slika;posnetek;zajem;zapis;"
+"screenshot;screen;monitor"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Vrtenje odklepanja zaslona"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Vrtenje zaklepanja zaslona"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Ukaz ni mogoče najti"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Ukaza ni mogoče razčleniti:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Izvedba »%s« je spodletela:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Ravnokar"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Pred %d minutami"
+msgstr[1] "Pred %d minuto"
+msgstr[2] "Pred %d minutama"
+msgstr[3] "Pred %d minutami"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Pred %d urami"
+msgstr[1] "Pred %d uro"
+msgstr[2] "Pred %d urama"
+msgstr[3] "Pred %d urami"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Včeraj"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Pred %d dnevi"
+msgstr[1] "Pred %d dnevom"
+msgstr[2] "Pred %d dnevoma"
+msgstr[3] "Pred %d dnevi"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Pred %d tedni"
+msgstr[1] "Pred %d tednom"
+msgstr[2] "Pred %d tednoma"
+msgstr[3] "Pred %d tedni"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Pred %d meseci"
+msgstr[1] "Pred %d mesecem"
+msgstr[2] "Pred %d mesecema"
+msgstr[3] "Pred %d meseci"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Pred %d leti"
+msgstr[1] "Pred %d letom"
+msgstr[2] "Pred %d letoma"
+msgstr[3] "Pred %d leti"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Včeraj, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%d. %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Včeraj, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%d. %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%d. %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Prijava prek vroče točke"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Povezava prek vroče točke ni varna. Gesla in druge podrobnosti, ki bodo "
+"vpisane na spletne strani, bodo lahko vidne tretjim osebam."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Zavrni dostop"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Dovoli dostop"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Neimenovana mapa"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "Program »%s« je dodan med priljubljene."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "Program »%s« je odstranjen iz priljubljenih."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Odpri okna"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Novo okno"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Pokaži podrobnosti"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Končaj"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Odpni"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Pripni med priljubljene"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Zaženi z uporabo vgrajene grafične kartice"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Zaženi z uporabo določene grafične kartice"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Izbor zvokovne naprave"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Nastavitve zvoka"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Slušalke"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Slušalka"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Spremeni ozadje …"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Nastavitve zaslona"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Nastavitve"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "N"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Č"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Predhodni mesec"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Naslednji mesec"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Teden %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Ni obvestil"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Ne moti"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Počisti"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "Program »%s« se ne odziva."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Lahko počakate, če se program morda začne spet odzivati, lahko pa vsilite "
+"končanje delovanja."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Vsili končanje"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Počakaj"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Zunanji pogon je priklopljen"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Zunanji pogon je odklopljen"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Ni mogoče odkleniti nosilca"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Nameščena različica udisks ne podpira nastavitev PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Odpri s programom %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Vzpostavitev povezave je mogoča tudi z gumbom »WPS« na usmerjevalniku."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Poveži"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Ključ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Geslo zasebnega ključa"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Istovetnost"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Storitev"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Zahtevana je overitev"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Za povezavo v brezžično omrežje »%s« je zahtevano geslo oziroma šifrirni "
+"ključ."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Žična overitev 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Naziv omrežja"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Overitev DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Zahtevana koda PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Za napravo mobilnega širokopasovnega dostopa je zahtevana koda PIN."
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "Koda PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Za povezavo z omrežjem »%s« je zahtevano geslo."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Upravljalnik omrežij"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Geslo VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Zahtevana je overitev"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Skrbnik"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Overi"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Overitev je spodletela.. Poskusite znova."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s je sedaj znan kot v %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Okna"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Pokaži programe"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Pregledna plošča"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%d. %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%B %-d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%B %-d %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Danes"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Jutri"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Celodnevno"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d.%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Ni dogodkov"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Dodaj svetovni čas …"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Svetovni časi"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Poteka nalaganje …"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Preglej splet za podrobnosti o vremenu."
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Podatki o vremenu trenutno niso na voljo."
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Vreme"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Izbor kraja za vremensko napoved …"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Odjava %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Odjava"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Uporabnik %s bo samodejno odjavljen čez %d sekund."
+msgstr[1] "Uporabnik %s bo samodejno odjavljen čez %d sekundo."
+msgstr[2] "Uporabnik %s bo samodejno odjavljen čez %d sekundi."
+msgstr[3] "Uporabnik %s bo samodejno odjavljen čez %d sekunde."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Uporabnik bo samodejno odjavljen čez %d sekund."
+msgstr[1] "Uporabnik bo samodejno odjavljen čez %d sekundo."
+msgstr[2] "Uporabnik bo samodejno odjavljen čez %d sekundi."
+msgstr[3] "Uporabnik bo samodejno odjavljen čez %d sekunde."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Odjavi"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Izklop sistema"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Namesti posodobitve in ugasni računalnik"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistem se bo samodejno izklopil čez %d sekund."
+msgstr[1] "Sistem se bo samodejno izklopil čez %d sekundo."
+msgstr[2] "Sistem se bo samodejno izklopil čez %d sekundi."
+msgstr[3] "Sistem se bo samodejno izklopil čez %d sekunde."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Namesti pripravljene posodobitve programov"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Izklopi"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Ponoven zagon"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Namestitev posodobitev in ponoven zagon računalnika"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistem se bo samodejno ponovno zagnal čez %d sekund."
+msgstr[1] "Sistem se bo samodejno ponovno zagnal čez %d sekundo."
+msgstr[2] "Sistem se bo samodejno ponovno zagnal čez %d sekundi."
+msgstr[3] "Sistem se bo samodejno ponovno zagnal čez %d sekunde."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Ponovno zaženi"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Namesti posodobitve in ponovno zaženi"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistem bo samodejno namestil pakete in se nato ponovno zagnal čez %d sekund."
+msgstr[1] ""
+"Sistem bo samodejno namestil pakete in se nato ponovno zagnal %d sekundo."
+msgstr[2] ""
+"Sistem bo samodejno namestil pakete in se nato ponovno zagnal %d sekundi."
+msgstr[3] ""
+"Sistem bo samodejno namestil pakete in se nato ponovno zagnal %d sekunde."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Ponovno zaženi in namesti"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Namesti in izklopi napravo"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Izklopi napravo po uspešnem nameščanju posodobitev"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Ponovno zaženi in namesti posodobitev"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Po ponovnem zagonu bo nameščen paket %s %s. Posodobitev namestitve je lahko "
+"dolgotrajna; preverite, ali je narejena varnostna kopija podatkov in ali je "
+"računalnik priklopljen na električno omrežje."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Naprava deluje na bateriji: pred nameščanjem posodobitev jo je priporočljivo "
+"priključiti v omrežje."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr ""
+"Nekateri programi se ne odzivajo, ali pa so odprte neshranjene datoteke."
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "V sistem so prijavljeni tudi drugi uporabniki."
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Možnosti zagona"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (oddaljeno)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konzola)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Namesti"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Namesti razširitev"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Ali želite prejeti in namestiti »%s« prek extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Na voljo so posodobitve razširitev"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Posodobitve razširitev so pripravljene na namestitev."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Dovoli onemogočanje tipkovnih bližnjic"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Program %s poskuša onemogočiti tipkovne bližnjice"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Program poskuša onemogočiti tipkovne bližnjice"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Tipkovne bližnjice je mogoče obnoviti s pritiskom na gumb %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Zavrni"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Dovoli"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Počasne tipke so omogočene"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Počasne tipke so onemogočene"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Tipka Shift je bila pritisnjena več kot 8 sekund. To je tipkovna bližnjica "
+"za zagon možnosti Počasnih tipk, ki vpliva na način delovanja tipkovnice."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Lepljive tipke so omogočene"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Lepljive tipke so onemogočene"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Tipka Shift je bila pritisnjena 5-krat zapored. To je tipkovna bližnjica za "
+"zagon možnosti Lepljivih tipk, ki vpliva na način delovanja tipkovnice."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Tipka Shift je bila pritisnjena 5-krat zapored ali pa sta bili sočasno "
+"pritisnjeni dve tipki. To je tipkovna bližnjica za preklic možnosti "
+"Lepljivih tipk, ki vpliva na način delovanja tipkovnice."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Pusti omogočeno"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Omogoči"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Onemogoči"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Pusti onemogočeno"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Nastavitve območja in jezika"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Ni nameščenih razširitev"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ni javil napak."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Skrij napake"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Pokaži napake"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Omogočeno"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Onemogočeno"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Napaka"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Zastarelo"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Prejemanje"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Poglej vir"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Spletna stran"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Sistem je v ne-varnem načinu"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Programi imajo sedaj neomejen dostop"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Razveljavi"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Zaznana je prijava s skrbniškimi dovoljenji"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Zagona seje s skrbniškim računom se je treba iz varnostnih razlogov "
+"izogibati. Priporočljivo je uporabljati običajen uporabniški račun."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Zaklep zaslona je onemogočen"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr ""
+"Zaklepanje zaslona zahteva namestitev upravljalnika zaslona za okolje GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Podrobnosti sistema"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Neznan izvajalec"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Neznan naslov"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Poišči …"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Programi"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Pregled"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nova bližnjica …"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Program je določen"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Pokaži zaslonsko pomoč"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Nadzornik preklopa"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Dodeli tipkovno bližnjico"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Končano"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Uredi …"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Brez"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Pritisnite gumb za nastavitev"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Pritisnite tipko Esc za končanje"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Pritisnite katerokoli tipko za končanje"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Dejavnosti"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistem"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Vrhnja vrstica"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Zaženi ukaz"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Pritisnite tipko Esc za končanje"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Na sistemu Wayland je na voljo ponovni zagon"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Poteka ponovno zaganjanje …"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Zakleniti je treba zaslon"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Zaklep ni mogoč"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Zaklep je preprečil program"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Izbor"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Izbor polja"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Zaslon"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Izbor zaslona"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Okno"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Izbor okna"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Zaslonski slika / posnetek"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Pokaži kazalnik"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Zaslonski posnetek"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Zaslonski posnetek %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Zaslonska slika"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Zaslonski posnetek je zajet"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Kliknite za ogled posnetka."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Pokaži v mapi"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Zaslonska slika %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Zaslonska slika je zajeta"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Prilepiti je mogoče sliko iz odložišča."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Zaslonska slika je zajeta"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Iskanje …"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Ni najdenih zadetkov."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "še %d"
+msgstr[1] "še %d"
+msgstr[2] "še %d"
+msgstr[3] "še %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Poišči"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopiraj"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Prilepi"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Pokaži besedilo"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Skrij besedilo"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Vključena je tipka zaklepa velikosti črk."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Skrit razdelek"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Sistemska glasnost"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Uporablja nastavitvene datoteke"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Za odklepanje pogona, ki uporablja nastavitvene datoteke, uporabite orodje "
+"<i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Koda PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Zapomni si geslo"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Odkleni"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Odpri %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "Vrednost PIM mora biti številka ali prazno polje."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Ni mogoče začeti »%s«"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Ni mogoče najti programa %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Dostopnost"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Visok kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Približanje"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Zaslonski bralnik"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Zaslonska tipkovnica"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Vidna opozorila"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Lepljive tipke"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Počasne tipke"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Odskočne tipke"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Miškine tipke"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Veliko besedilo"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Samodejno zavrti"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Svetlost"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Temna tema"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Enojni klik"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dvojni klik"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Poteg"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Drugotni klik"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Zadržan klik"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Tipkovnica"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Pokaži razporeditev tipkovnice"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Odobri dostop do trenutnega mesta"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Program %s zahteva dostop do podatkov trenutnega mesta"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Dostop do trenutnega mesta je mogoče spremeniti kadarkoli med nastavitvami "
+"zasebnosti."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<neznano>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Prekini povezavo %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Poveži z mestom %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Vroča točka %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "Nastavitve VPN"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Brezžična omrežja"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Vsa omrežja"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Žične povezave"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Nastavitve žičnega omrežja"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Povezave Bluetooth"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Nastavitve za Bluetooth"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Mobilne povezave"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Nastavitve mobilnega širokopasovnega dostopa"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Povezovanje je spodletelo"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Omogočanje omrežne povezave je spodletelo."
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Nočna osvetlitev"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Zmogljivo"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Uravnoteženo"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Varčno"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Profil varčevanja"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Zaustavi zaslonski posnetek"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Prekini souporabo zaslona"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Način letala"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Zajemi zaslonsko sliko"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Meni izklopa"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "V pripravljenost …"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Ponovno zaženi …"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Izklopi …"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Odjava …"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Preklopi uporabnika …"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Zakleni zaslon"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Neznana naprava Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Med nedejavnostjo je bila zaznana nova. Odklopite napravo in jo znova "
+"priklopite za uporabo."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Neznana naprava Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Zaznana je nova naprava, ki jo mora overiti skrbnik sistema."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Napaka overitve naprave Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Naprave Thunderbolt ni mogoče overiti: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Glasnost je spremenjena"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Glasnost"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Odvod zvoka"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Dovod zvoka"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Zrcaljenje zaslona"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Združi zaslone"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Le zunanje"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Le vgrajeno"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %B %-d"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Povlecite navzgor za odklepanje"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Kliknite, ali pa pritisnite poljubno tipko za prikaz prijavnega okna"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Odkleni okno"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Prijavi z drugim uporabniškim imenom"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Dobrodošli v GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Če se hočete najprej na hitro razgledati po namizju, sledite vodiču."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ne, hvala"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Vodič GNOME"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "Storitev »%s« je pripravljena"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Ali želite ohraniti te nastavitve zaslona?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Povrni nastavitve"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Ohrani spremembe"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Spremembe nastavitev bodo povrnjene v %d sekundah."
+msgstr[1] "Spremembe nastavitev bodo povrnjene v %d sekundi."
+msgstr[2] "Spremembe nastavitev bodo povrnjene v %d sekundah."
+msgstr[3] "Spremembe nastavitev bodo povrnjene v %d sekundah."
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Skrij"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Obnovi"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Razpni"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Premakni"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Spremeni velikost"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Premakni nazivno vrstico na zaslon"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Vedno na vrhu"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Vedno na dejavni delovni površini"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Premakni na delovno površino na levi"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Premakni na delovno površino na desni"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Premakni na zgornjo delovno površino"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Premakni na spodnjo delovno površino"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Premakni na zaslon zgoraj"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Premakni na zaslon spodaj"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Premakni na zaslon levo"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Premakni na zaslon desno"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Zapri"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Koledar Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Izpiši različico"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Način, uporabljen v upravljalniku GDM kot prijavni zaslon"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Uporabi poseben način, na primer »gdm« za prijavni zaslon"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Seznam mogočih načinov"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Neznano"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Zaganjanje »%s« je spodletelo"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Gesli nista skladni!"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Geslo ne more biti prazna vrednost"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Uporabnik je preklical pogovorno okno overitve"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Razširitve lupine"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Upravljanje z razširitvami Lupine GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Projekt GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Program Razširitve GNOME omogoča nameščanje in posodabljanje razširitev, "
+"prilagajanje nastavitev in upravljanje njihovega delovanja."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Nastavitve razširitev lupine GNOME"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Ni zadetkov"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Ali želite odstraniti »%s«?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Če razširitev odstranite, jo je treba pred ponovno namestitvijo najprej "
+"prejeti iz omrežja."
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Odstrani"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr "Matej Urbančič <mateju@src.gnome.org>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Ob naslednji prijavi bo posodobljenih %d razširitev."
+msgstr[1] "Ob naslednji prijavi bo posodobljena %d razširitev."
+msgstr[2] "Ob naslednji prijavi bosta posodobljeni %d razširitvi."
+msgstr[3] "Ob naslednji prijavi bodo posodobljene %d razširitve."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Razširitev ni skladna s trenutno nameščeno različico okolja GNOME."
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Razširitev javlja napake"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Za razširitev je na voljo posodobitev"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Spletna stran"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Odstrani …"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Pomoč"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "O programu razširitev"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Razširitve lahko vplivajo na delovanje in stabilnost programa. V primeru "
+"težav, je priporočljivo razširitve onemogočiti."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Ročno nameščeno"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Za iskanje in dodajanje razširitev obiščite spletne strani <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Vgrajeno"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Ni nameščenih razširitev"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Kaže, da ni mogoče pridobiti seznama nameščenih razširitev. Prepričajte se, "
+"da ste prijavljeni v namizje GNOME in poskusite znova."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Posodobitev razširitve je na voljo"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Nova razširitev je uspešno ustvarjena v mapi %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Ime naj bo kratek in (najbolje opisen) niz.\n"
+"Primer:: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Ime"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Opis je enostavčna predstavitev, kaj razširitev omogoča.\n"
+"Na primer: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Opis"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"Določilo UUID je splošno-enoznačno določilo razširitve.\n"
+"Zapisano mora biti v obliki elektronskega naslova (na primer: "
+"clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Izberite eno izmed razpoložljivih predlog:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Predloga"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Enoznačno določilo nove razširitve"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "IME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Prikazano ime nove razširitve"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "OPIS"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Kratek opis funkcije razširitve"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "PREDLOGA"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Predloga za uporabo z novo razširitvijo"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Vnos podrobnosti razširitve"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Ustvari novo razširitev"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Neznani argumenti"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Zahtevani so določilo UUID, ime in opis"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Razširitev »%s« ne obstaja.\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Onemogoči razširitev"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Ni podanega UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Podano je več kot eno določilo UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Onemogoči razširitev"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Povezava z Lupino GNOME je spodletela\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Razširitev »%s« ne obstaja.\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Pokaži podrobnosti razširitev"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Prepiši obstoječo razširitev"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "PAKET_RAZŠIRITVE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Namesti paket razširitve"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Ni navedenega paketa razširitev"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Določen je več kot en paket razširitve"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Pokaži uporabniško nameščene razširitve"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Pokaži sistemsko nameščene razširitve lupine"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Pokaži omogočene razširitve"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Pokaži onemogočene razširitve"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Pokaži razširitve z lastnostmi"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Pokaži razširitve s posodobitvami"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Natisni podrobnosti razširitve"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Izpiše seznam nameščenih razširitev"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "DATOTEKA"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Dodatni viri, ki morajo biti vključeni v paket"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "Shema"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Shema GSettings, ki naj bo vključena v paket"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "MAPA"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Mapa, kjer bodo shranjeni prevodi"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMENA"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Domena prevajanja, ki jo uporablja gettext"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Prepiši obstoječ paket"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Mapa, v kateri bo ustvarjen paket"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "Izvorna mapa"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Ustvari paket razširitve"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Določena je več kot ena izvorna mapa"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Razširitev »%s« nima posebnih nastavitev\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Odpiranje lastnosti razširitve »%s« je spodletelo: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Odpre možnosti razširitve"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Ponastavi razširitev"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Sistemskih razširitev ni mogoče odstraniti\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Odstranjevanje namestitve »%s« je spodletelo\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Odstrani razširitev"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Ne izpisuj sporočil o napakah"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Pot"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "Naslov URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Izvorni avtor"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Različica"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Stanje"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "»različica« ne prevzema argumentov"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Uporaba:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Izpiši podatke o različici in končaj."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "UKAZ"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENTI ...]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Ukazi:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Izpiši pomoč"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Omogoči razširitev"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Onemogoči razširitev"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Ponastavi razširitev"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Odstrani razširitev"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Izpiši seznam razširitev"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Pokaži podatke razširitve"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Odpri možnosti razširitve"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Ustvari razširitev"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Razširitev paketa"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Namesti paket razširitve"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Z ukazom »%s« se izpiše podrobna pomoč.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Običajno"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Prazna razširitev"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Kazalnik"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Dodaj ikono v vrhnjo orodno vrstico"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u odvodov naprave"
+msgstr[1] "%u odvod naprave"
+msgstr[2] "%u odvoda naprave"
+msgstr[3] "%u odvodi naprave"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u dovodov naprave"
+msgstr[1] "%u dovod naprave"
+msgstr[2] "%u dovoda naprave"
+msgstr[3] "%u dovodi naprave"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Sistemski zvoki"
diff --git a/po/sr.po b/po/sr.po
new file mode 100644
index 0000000..9d32844
--- /dev/null
+++ b/po/sr.po
@@ -0,0 +1,3730 @@
+# Serbian translation for gnome-shell.
+# Courtesy of Prevod.org team (http://prevod.org/) -- 2010—2023.
+# Copyright © 2010 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Translators:
+# Милош Поповић <gpopac@gmail.com>, 2010—2011.
+# Борисав Живановић <borisavzivanovic@gmail.com>, 2017—2018.
+# Марко М. Костић <marko.m.kostic@gmail.com>, 2016-2020.
+# Мирослав Николић <miroslavnikolic@rocketmail.com>, 2011–2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 05:16+0000\n"
+"PO-Revision-Date: 2023-02-26 19:11+0100\n"
+"Last-Translator: Мирослав Николић <miroslavnikolic@rocketmail.com>\n"
+"Language-Team: Serbian <gnome-sr@googlegroups.org>\n"
+"Language: sr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : "
+"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2\n"
+"X-Project-Style: gnome\n"
+"X-Poedit-Bookmarks: -1,167,-1,-1,-1,-1,-1,-1,-1,-1\n"
+"X-Generator: Gtranslator 41.0\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Покретачи"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Покрени омиљени програм 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Покрени омиљени програм 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Покрени омиљени програм 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Покрени омиљени програм 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Покрени омиљени програм 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Покрени омиљени програм 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Покрени омиљени програм 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Покрени омиљени програм 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Покрени омиљени програм 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Снимци екрана"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Направите интерактивни снимак екрана"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Направи снимак екрана"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Направи снимак прозора"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Интерактивно сними видео екрана"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Систем"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Прикажите списак обавештења"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Поставите у жижу активно обавештење"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Прикажи преглед"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Прикажи све програме"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Отворите изборник програма"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Гномова шкољка"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Управник прозорима и покретач програма"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Укључује алате корисне програмерима и тестерима из „Alt-F2“ прозорчета"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Дозвољава приступ унутрашњем отклањању грешака и алатима за праћење "
+"коришћењем „Alt-F2“ прозорчета."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "ЈУИБ-ови проширења за укључивање"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Проширења Гномове шкољке имају ЈУИБ вредност; овај кључ исписује проширења "
+"која треба учитавати. Свако проширење које жели бити учитано мора бити на "
+"списку. Такође можете да управљате овим списком користећи начине Д-"
+"магистрале „Укључи проширење“ и „Искључи проширење“ на „org.gnome.Shell“."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Насилно онемогућавање за следеће ЈУИБ-ове проширења"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Проширења Гномове шкољке имају ЈУИБ вредност; овај кључ исписује проширења "
+"која треба онемогућити, чак иако су учитани као део тренутног режима. Можете "
+"мењати овај списак методама д-сабирнице под називима EnableExtension и "
+"DisableExtension, на org.gnome.Shell. Овај кључ има предност у односу на "
+"подешавање \"enabled-extensions\"."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Искључује корисничка проширења"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Искључује сва проширења која је укључио корисник без деловања на подешавање "
+"„enabled-extension“."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Искључује потврђивање сагласности издања проширења"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Гномова шкољка ће учитати само проширења која тврде да подржавају текуће "
+"покренуто издање. Укључивање ове опције ће искључити ову проверу и покушати "
+"да учита проширења без обзира на издања која тврде да подржавају."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Списак ИБ датотека радне површине са омиљеним програмима"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Програми који одговарају овим одредницама се приказују унутар области "
+"омиљених програмима."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Историјат прозорчета (Alt-F2) наредби"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Историјат прозорчета огледала"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Увек приказује ставку изборника „Одјави ме“ у изборнику корисника."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Овај кључ превазилази самостално скривање ставке изборника „Одјави ме“ у "
+"случају једног корисника, једне сесије."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Да ли ће бити запамћена лозинка за качење шифрованих или удаљених система "
+"датотека"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Шкољка ће затражити лозинку када се прикачи шифровани уређај или удаљени "
+"систем датотека. Ако лозинка може бити сачувана за даљњу употребу биће "
+"присутно поље за избор „Запамти лозинку“. Овај кључ подешава основно стање "
+"поља за избор."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Последње изабрани неподразумевани профил напајања"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Неки системи подржавају више од два профила напајања. Да бисмо подржали "
+"промену између два профила, овај кључ чува последње изабрани неподразумевани "
+"профил."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Последње издање прозорчета „Добро дошли у Гном“ које је приказано"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Овај кључ приказује издање прозорчета „Добро дошли у Гном“ које је "
+"приказано. Празна ниска представља најстарију могућу верзију а велики број "
+"представља издања која још не постоје. Овај велики број се може искористити "
+"за ефективно онемогућавање прозорчета."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Распоред бирача програма"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Распоред бирача програма. Свака ставка у низу је страница. Странице се "
+"чувају онако како се појављују у Гномовој шкољки. Свака страница садржи пар "
+"“application id” → 'data'. Тренутно, следеће вредности се чувају као 'data': "
+"• “position”: положај иконице програма на страници"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Пречица за отварање изборника програма"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Пречица за отварање изборника програма."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Пречица на тастатури за промену између стања прегледа"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Пречица на тастатури за промену између сесије, бирача прозора и мреже "
+"програма"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Пречица на тастатури за промену између мреже програма, бирача прозора и "
+"сесије"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Пречица за отварање прегледа „Прикажи програме“"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Пречица за отварање прегледа „Прикажи програме“ у прегледу активности."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Пречица за отварање прегледа"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Пречица за отварање прегледа активности."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Пречица за окидање видљивости списка обавештења"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Пречица за окидање видљивости списка обавештења."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Пречица за постављање у жижу радног обавештења"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Пречица за постављање у жижу радног обавештења."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Пребаци се на програм 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Пребаци се на програм 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Пребаци се на програм 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Пребаци се на програм 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Пребаци се на програм 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Пребаци се на програм 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Пребаци се на програм 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Пребаци се на програм 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Пребаци се на програм 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Ограничава пребацивача на текући радни простор."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ако је изабрано, само програми који имају прозоре на текућем радном простору "
+"се приказују у пребацивачу. У супротном, сви програми су укључени."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Режим иконице програма."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Подешава начин приказивања прозора у пребацивачу. Исправне могућности су "
+"„thumbnail-only“ (приказује сличицу прозора), „app-icon-only“ (приказује "
+"само иконицу програма) или „both“ (оба)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ако је изабрано, само прозори са текућег радног простора се приказују у "
+"пребацивачу. У супротном, сви прозори су укључени."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Локације"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Места која треба приказивати у светским сатовима"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Аутоматска локација"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Да ли треба добавити тренутну локацију"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Локација"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Место за које треба приказати временску прогнозу"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Прикачиње прозорче родитељском прозору"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Овај кључ превазилази кључ у „org.gnome.mutter“ када покреће Гномову шкољку."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Укључује поплочавање ивице приликом отпуштања прозора на ивицама екрана"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Радним просторима се управља динамички"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Радни простори само на примарном монитору"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Застој првог плана се мења у режиму миша док се показивач не заустави"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Мрежна пријава"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Нешто је пошло наопако"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Веома нам је жао али се догодио проблем, не можемо приказати подешавања за "
+"ово проширење. Предлажемо вам да пријавите овај проблем творцима проширења."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Техничке појединости"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Матична страна"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Посети матичну страну проширењ"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Откажи"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Лозинка"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Изабери сесију"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Није на списку?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(нпр., корисник или %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Корисник"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Прозор за пријављивање"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Грешка потврђивања идентитета"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(или превуците прст преко читача)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(или поставите прст на читач)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Искључи"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+"power off;shutdown;искључи;угаси;гашење;isključi;ugasi;gašenje;iskljuci;"
+"gasenje;halt;stop;халт;стоп"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Поново покрени"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;рибут;рестарт;поновно покретање;рестартовање;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Закључај екран"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "закључај екран"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Одјави ме"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr ""
+"изађи;одјави се;одлогуј се;izađi;odjavi se;odloguj se;izadji;logout;log out;"
+"sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Обустави"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr ""
+"suspend;sleep;обустави;спавај;суспендуј;obustavi;spavaj;suspenduj;suspend;"
+"sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Промени корисника"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "switch user;промени корисника;promeni korisnika;switch user"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;screen;rotation;закључај окретање екрана;закључај "
+"оријентацију;zaključaj okretanje ekrana;zaključaj orijentaciju;zakljucaj "
+"okretanje ekrana;екран;ekran;окретање;okretanje;lock orientation;screen;"
+"rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Направи снимак екрана"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;снимак екрана;видео екрана;снип;"
+"snimak ekrana;video ekrana"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Откључај окретање екрана"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Закључај окретање екрана"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Наредба није нађена"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Не могу да обрадим наредбу:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Није успело извршавање „%s“:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Управо сада"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Пре %d минут"
+msgstr[1] "Пре %d минута"
+msgstr[2] "Пре %d минута"
+msgstr[3] "Пре један минут"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Пре %d сат"
+msgstr[1] "Пре %d сата"
+msgstr[2] "Пре %d сати"
+msgstr[3] "Пре један сат"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Јуче"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Пре %d дан"
+msgstr[1] "Пре %d дана"
+msgstr[2] "Пре %d дана"
+msgstr[3] "Пре један дан"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Пре %d недељу"
+msgstr[1] "Пре %d недеље"
+msgstr[2] "Пре %d недеља"
+msgstr[3] "Пре недељу дана"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Пре %d месец"
+msgstr[1] "Пре %d месеца"
+msgstr[2] "Пре %d месеци"
+msgstr[3] "Пре месец дана"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Пре %d годину"
+msgstr[1] "Пре %d године"
+msgstr[2] "Пре %d година"
+msgstr[3] "Пре годину дана"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Јуче, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d. %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d. %B %Y., %H:%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Јуче, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d. %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d .%B %Y., %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Пријава на врућу тачку"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Ваша веза са овом пријавом вруће тачке није безбедна. Људи у близини могу "
+"видети лозинке или друге податке које будете унели на овој страници."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Забрани приступ"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Дозволи приступ"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Неименована фасцикла"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "„%s“ је додат на док."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "„%s“ је откачен са дока."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Отвори прозоре"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Нови прозор"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Прикажи детаље"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Изађи"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Откачи"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Закачи на док"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Покрени са уграђеном графичком картицом"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Покрени са наменском графичком картицом"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Изаберите звучни уређај"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Подешавања звука"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Слушалице"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Слушалице са микрофоном"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Измени позадину…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Подешавања приказа"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Подешавања"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Н"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "У"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ч"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "С"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Претходни месец"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Следећи месец"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Седмица %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Без обавештења"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Не узнемиравај"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Очисти"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "„%s“ не одговара."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Можете изабрати да сачекате који тренутак да настави или да приморате "
+"програм да се читав затвори."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Приморај излаз"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Сачекај"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Спољни уређај је прикључен"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Спољни уређај је искључен"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Не могу да откључам волумен"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Инсталирано издање udisks-a не подржава ЛИЧ (PIM) подешавање"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Отвори програмом %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "Или можете повезати притиском на дугме „WPS“, на вашем рутеру."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Повежи се"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Кључ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Лозинка приватног кључа"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Идентитет"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Услуга"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Потребно је потврђивање идентитета"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Потребне су лозинке или кључеви шифровања за приступ бежичној мрежи „%s“."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Потврђивање идентитета за жичану 802.1X везу"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Назив мреже"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Потврђивање идентитета ДСЛ-а"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Потребан је ПИН кôд"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Потребан је ПИН код за повезивање мобилног широкопојасног уређаја"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "ПИН"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Потребна је лозинка за повезивање на „%s“."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Управник мреже"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "ВПН лозинка"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Потребна је пријава"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Администратор"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Потврди идентитет"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Погрешили сте! Покушајте поново."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "„%s“ је сада познат као „%s“"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Прозори"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Прикажи програме"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Док"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d. %B %Y."
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A, %e. %B %Y."
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d. %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d. %B %Y."
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Данас"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Сутра"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Цео дан"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d.%m."
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Без догађаја"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Светски сатови…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Светски сатови"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Учитавам…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Повежите се на мрежу за податке о времену"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Подаци о временској прогнози тренутно нису доступни"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Временска прогноза"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Изаберите место временске прогнозе…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Одјави корисника „%s“"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Одјави ме"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s ће бити одјављен за %d секунду."
+msgstr[1] "%s ће бити одјављен за %d секунде."
+msgstr[2] "%s ће бити одјављен за %d секунди."
+msgstr[3] "%s ће бити одјављен за %d секунду."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Бићете одјављени за %d секунду."
+msgstr[1] "Бићете одјављени за %d секунде."
+msgstr[2] "Бићете одјављени за %d секунди."
+msgstr[3] "Бићете одјављени за %d секунду."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Одјави"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Искључи"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Инсталирај ажурирања и искључи"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Рачунар ће се искључити за %d секунду."
+msgstr[1] "Рачунар ће се искључити за %d секунде."
+msgstr[2] "Рачунар ће се искључити за %d секунди."
+msgstr[3] "Рачунар ће се искључити за %d секунду."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Инсталирај ажурирања софтвера на чекању"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Искључи"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Поново покрени"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Инсталирај ажурирања и поново покрени"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Систем ће се поново покренути за %d секунду."
+msgstr[1] "Систем ће се поново покренути за %d секунде."
+msgstr[2] "Систем ће се поново покренути за %d секунди."
+msgstr[3] "Систем ће се поново покренути за %d секунду."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Поново покрени"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Поново покрени и инсталирај ажурирања"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Систем ће се сам поново покренути и инсталирати ажурирања за %d секунду."
+msgstr[1] ""
+"Систем ће се сам поново покренути и инсталирати ажурирања за %d секунде."
+msgstr[2] ""
+"Систем ће се сам поново покренути и инсталирати ажурирања за %d секунди."
+msgstr[3] ""
+"Систем ће се сам поново покренути и инсталирати ажурирања за %d секунду."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Поново покрени и инсталирај"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Инсталирај и искључи"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Искључи након инсталирања ажурирања"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Поново покрени и инсталирај надоградњу"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s ће бит инсталиран/а након поновног покретања. Инсталација надоградње "
+"понекад може да потраје: проверите да ли сте направили резерву ваших важних "
+"података и да ли је рачунар прикључен на мрежно напајање."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Батерија је скоро празна, прикључите мрежно напајање пре инсталирања "
+"ажурирања."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Неки програми су заузети или имају несачувани рад"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Други корисници су пријављени"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Опције подизања"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (удаљено)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (љуска)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Инсталирај"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Инсталирај проширење"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Да преузмем и да инсталирам „%s“ са „extensions.gnome.org“-а?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Доступна су ажурирања проширења"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Ажурирања проширења су доступна за инсталирање."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Дозволи спречавања пречица"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Програм „%s“ жели да спречи пречице"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Програм жели да спречи пречице"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Можете повратити пречице притиском на „%s“."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Забрани"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Дозволи"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Спори тастери су укључени"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Спори тастери су искључени"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Управо сте тастер Shift држали притиснутим 8 секунди. То укључује Споре "
+"тастере, што утиче на рад тастатуре."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Лепљиви тастери су укључени"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Лепљиви тастери су искључени"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Управо сте притиснули тастер Shift 5 пута заредом. То је пречица за Лепљиве "
+"тастере, што утиче на рад тастатуре."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Управо сте притиснули два тастера истовремено, или сте притиснули тастер "
+"Shift 5 пута заредом. То укључује Лепљиве тастере, што утиче на рад "
+"тастатуре."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Остави укључено"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Укључи"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Искључи"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Остави искључено"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Подешавања региона и језика"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Нису инсталирана проширења"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s није објавио никакве грешке."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Сакриј грешке"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Прикажите грешке"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Укључено"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Искључено"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Грешка"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Изван датума"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Преузимам"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Прикажи код"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Веб страница"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Систем је постављен у небезбедан режим"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Програми сада имају неограничен приступ"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Опозови"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Пријављен као повлашћен корисник"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Покретање сесије под повлашћеним корисничким налогом треба избегавати из "
+"безбедносних разлога. Ако је то могуће, пријавите се као обичан корисник."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Закључавање екрана онемогућено"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Потребан је Гномов управник екрана за могућност закључавања екрана."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Подаци о систему"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Непознат извођач"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Непознат наслов"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Упишите жељено"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Програми"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Преглед"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Нова пречица…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Дефинисани програм"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Прикажи помоћ на екрану"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Промени монитор"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Додели тастер"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Готово"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Уреди…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Ништа"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Притисните дугме да подесите"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Притисните „Есц“ да изађете"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Притисните неки тастер да изађете"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Активности"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Систем"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Горња трака"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Покрени наредбу"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Притисни Esc за излазак"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Поновно покретање није доступно на Вејланду"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Поновно покрећем…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "Гном мора да закључа екран"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Не могу да закључам"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Неки програм је блокирао закључавање"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Избор"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Избор области"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Екран"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Избор екрана"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Прозор"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Избор прозора"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Снимак екрана / видео снимак"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Покажи показивач"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Видео снимци"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Видео снимак направљен дана %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Снимак екрана"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Видео снимак направљен"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Кликните овде за преглед видеа."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Прикажи у Датотекама"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Снимак екрана направљен дана %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Снимак екрана направљен"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Можете убацити слику из оставе."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Направљен је снимак екрана"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Тражим…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Нема одговарајућих резултата."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "Још %d"
+msgstr[1] "Још %d"
+msgstr[2] "Још %d"
+msgstr[3] "Још један"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Тражи"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Умножи"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Убаци"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Прикажи текст"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Сакриј текст"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Тастер за велика слова је притиснут."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Скривени волумен"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Виндоус системски волумен"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Користи кључ-датотеке"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Да бисте откључали волумен који користи кључ-датотеке, користите алатку "
+"<i>%s</i>."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "ЛИЧ (PIM) број"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Запамти лозинку"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Откључај"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Отвори програмом %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "ЛИЧ (PIM) мора бити број или празно."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Не могу да покренем „%s“"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Нисам могао наћи програм %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Приступачност"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Велики контраст"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Зумирај"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Читач екрана"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Тастатура на екрану"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Визуелна упозорења"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Лепљиви тастери"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Спори тастери"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Одскочни тастери"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Тастери миша"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Велики текст"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Самостално ротирај"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Блутут"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Осветљеност"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Тамни режим"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Један клик"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Двоструки клик"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Повлачење"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Секундарни клик"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Дужи клик"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Тастатура"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Покажи распоред тастатуре"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Дозволи приступ месту"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Програм %s жели да приступи вашем месту"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Приступ месту можете да измените у било које време у подешавањима "
+"приватности."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<непознато>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Откачи %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Повежи %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s хотспот"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "ВПН"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "ВПН подешавања"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Бежична"
+
+#: js/ui/status/network.js:1718
+#| msgid "No Networks"
+msgid "All Networks"
+msgstr "Све мреже"
+
+#: js/ui/status/network.js:1815
+#| msgid "%s Wired Connection"
+#| msgid_plural "%s Wired Connections"
+msgid "Wired Connections"
+msgstr "Жичане везе"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Подешавања жичане везе"
+
+#: js/ui/status/network.js:1830
+#| msgid "Bluetooth Settings"
+msgid "Bluetooth Tethers"
+msgstr "Досег Блутута"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Подешавања Блутута"
+
+#: js/ui/status/network.js:1845
+#| msgid "%s Modem Connection"
+#| msgid_plural "%s Modem Connections"
+msgid "Mobile Connections"
+msgstr "Мобилне везе"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Подешавања мобилне широкопојасне везе"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Повезивање није успело"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Активирање мрежне везе није успело"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Ноћно светло"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Најбрже"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Уравнотежено"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Чување батерије"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Профили напајања"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Заустави снимање"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Заустави дељење екрана"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Режим у авиону"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Направи снимак екрана"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Мени гашења"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Обустави"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Поново покрени…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Искључи…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Одјава…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Промени корисника…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Закључај екран"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Тандерболт"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Непознат Тандерболт уређај"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Нови уређај је уочен док нисте били ту. Поново повежите уређај да бисте "
+"почели да га користите."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Неовлашћени Тандерболт уређај"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"Уочен је нови уређај и потребно је да администратор овласти његову употребу."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Грешка у овлашћивању Тандерболта"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Не могу да овластим Тандерболт уређај: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Промена јачине звука"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Јачина звука"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Звучни излаз"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Звучни улаз"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Пресликај"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Споји екране"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Само спољни"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Само уграђени"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d. %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Превуци за откључавање"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Кликни или притисни тастер за откључавање"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Откључај прозор"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Пријавите се као други корисник"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Добро дошли у Гном %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Ако желите да сазнате више о употреби, погледајте обилазак."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Не, хвала"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Започни обилазак"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "„%s“ је спреман"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Задржати подешавања екрана?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Врати подешавања"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Задржи измене"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Измене подешавања ће бити враћене за %d секунду"
+msgstr[1] "Измене подешавања ће бити враћене за %d секунде"
+msgstr[2] "Измене подешавања ће бити враћене за %d секунди"
+msgstr[3] "Измене подешавања ће бити враћене за %d секунду"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Сакриј"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Поврати"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Увећај"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Премести"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Промени величину"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Премести траку наслова на екран"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Држи изнад осталих"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Прикажи на свим радним просторима"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Премести на леви радни простор"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Премести на десни радни простор"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Премести на радни простор горе"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Премести на радни простор доле"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Премести на горњи радни простор"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Премести на доњи радни простор"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Премести екран улево"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Премести екран удесно"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Затвори"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Календар Еволуције"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Исписује издање"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Који режим користи ГДМ за екран пријављивања"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Користи нарочит режим, нпр. „gdm“ за екран пријављивања"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Исписује могуће режиме"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Непознат"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Нисам успео да покренем „%s“"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Лозинке се не подударају."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Лозинка не може бити празна"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Корисник је одбацио прозорче за потврђивање идентитета"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Проширења"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Подесите проширења Гнома"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Гном пројекат"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Гномова проширења руководе ажурирањем проширења, подешавањем поставки "
+"проширења и уклањањем или онемогућавањем проширења."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Подесите проширења Гномове шкољке"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Нема подударања"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Уклонити „%s“?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Уколико уклоните проширење, мораћете га поново преузети да бисте га поново "
+"омогућили"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Уклони"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Мирослав Николић <miroslavnikolic@rocketmail.com>\n"
+"Милош Поповић <gpopac@gmail.com>\n"
+"Борисав Живановић <borisavzivanovic@gmail.com>\n"
+"Марко М. Костић <marko.m.kostic@gmail.com>\n"
+"https://гном.срб — превод Гнома на српски језик."
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d проширење биће ажурирано током следећег пријављивања."
+msgstr[1] "%d проширења биће ажурирана током следећег пријављивања."
+msgstr[2] "%d проширења биће ажурирана током следећег пријављивања."
+msgstr[3] "Једно проширење биће ажурирано током следећег пријављивања."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Ово проширење није подударно са тренутним издањем Гнома"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Проширење садржи грешку"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Постоји ажурирање проширења"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Веб страница"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Уклони…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Помоћ"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "О Проширењима"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Проширења могу утицати на делотворност и стабилност. Онемогућите проширења "
+"уколико имате проблема са вашим системом."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Ручно инсталирана"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Да бисте пронашли и додали проширења, посетите страницу <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Уграђена"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Неинсталирана проширења"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Нажалост, није било могуће добавити списак инсталираних проширења. Проверите "
+"да ли сте пријављени у Гном и пробајте поново."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Спремна су ажурирања проширења"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Ново проширење је успешно направљено у путањи %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Назив треба бити кратка (по могућуству описна) ниска.\n"
+"На пример: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Назив"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Опис објашњава, у једној реченици, шта ваше проширење ради.\n"
+"На пример: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Опис"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"ЈУИБ је општи и јединствени идентификатор вашег проширења.\n"
+"Ово треба бити у облику адресе е-поште (klikzafokus@petarperic.primer.rs)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Изаберите један од доступних шаблона:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Шаблон"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Јединствени идентификатор новог проширења"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "НАЗИВ"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Назив новог проширења видљив кориснику"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "ОПИС"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Кратак опис онога што проширење ради"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ШАБЛОН"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Шаблон који треба користити за ново проширење"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Интерактивно унесите податке о проширењу"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Направи ново проширење"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Непознати аргументи"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "Ставке ЈУИБ, назив и опис су обавезне"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Проширење „%s“ не постоји\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Онемогући проширење"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Ниједан ЈУИБ није наведен"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Наведено је више од једног ЈУИБ-а"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Омогући проширење"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Нисам успео да се повежем на Гномову шкољку\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Проширење „%s“ не постоји\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Прикажи податке о проширењима"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Препиши постојеће проширење"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "ЗАПАКОВАНО_ПРОШИРЕЊЕ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Инсталирај пакет проширења"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Ниједан пакет проширења није наведен"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Наведено је више од једног запакованог проширења"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Прикажи корисничка проширења"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Прикажи системска проширења"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Прикажи омогућена проширења"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Прикажи онемогућена проширења"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Прикажи проширења са поставкама"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Прикажи проширења са ажурирањима"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Прикажи појединости проширења"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Излистај инсталирана проширења"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ДАТОТЕКА"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Додатан извор који треба укључити у пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ШЕМА"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Шема г-подешавања коју треба упаковати"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "ДИРЕКТОРИЈУМ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Директоријум са преводима"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ДОМЕН"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Геттекст домен који треба користити за преводе"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Препиши постојећи пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Фасцикла где треба направити пакет"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "ИЗВОРНИ_ДИРЕКТОРИЈУМ"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Направи пакет са проширењем"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Наведено је више изворних директоријума"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Проширење „%s“ нема своје поставке\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Нисам успео да отворим поставке за проширење „%s“: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Отвара поставке проширења"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Поново поставља проширење"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Није могуће деинсталирати системска проширења\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Нисам успео да деинсталирам „%s“\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Деинсталира проширење"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Не приказуј поруке са грешкама"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Путања"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "Адреса"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Изворни творац"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Издање"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Стaњe"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "\"version\" не узима аргументе"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Начин коришћења:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Исписује податке о издању и излази."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "НАРЕДБА"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[АРГУМЕНТИ…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Наредбе:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Прикажи помоћ"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Омогући проширење"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Онемогући проширење"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Поново постави проширење"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Деинсталирај проширење"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Излистај проширење"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Прикажи податке о проширењу"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Отвори поставке проширења"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Направи проширење"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Запакуј проширење"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Инсталирај пакет проширења"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Користи „%s“ за детаљнију помоћ.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Обично"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Празно проширење"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Показатељ"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Додај иконицу у горњу траку"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u излаз"
+msgstr[1] "%u излаза"
+msgstr[2] "%u излаза"
+msgstr[3] "%u излаз"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u улаз"
+msgstr[1] "%u улаза"
+msgstr[2] "%u улаза"
+msgstr[3] "%u улаз"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Системски звуци"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "Да ли је основни адаптер блутута подесио њему придружене уређаје"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Шкољка ће приказати ставку изборника блутута само ако је адаптер блутута "
+#~ "упаљен, или ако има подешених уређаја придружених основном адаптеру. Ово "
+#~ "ће се вратити на почетно стање ако се установи да основни адаптер нема "
+#~ "њему придружених уређаја."
+
+#~ msgid "Log Out"
+#~ msgstr "Одјави ме"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d повезан"
+#~ msgstr[1] "%d повезана"
+#~ msgstr[2] "%d повезано"
+#~ msgstr[3] "%d повезан"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Блутут укљ."
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Блутут иск."
+
+#~ msgid "Location Enabled"
+#~ msgstr "Локација је омогућена"
+
+#~ msgid "Disable"
+#~ msgstr "Искључи"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Подешавања приватности"
+
+#~ msgid "Location In Use"
+#~ msgstr "Користи локацију"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Не користи локацију"
+
+#~ msgid "Enable"
+#~ msgstr "Укључи"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s искључено"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s повезано"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s неуправљано"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s повезивање у току"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s захтева пријаву"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Недостаје фирмвер за %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s недоступно"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s повезивање неуспешно"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s уређај искључен"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s искључено"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Повежи се на Интернет"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Авионски режим рада је укључен"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Бежична веза је искључена када је укључен авионски режим рада."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Искључи авионски режим рада"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Бежична веза је искључена"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Бежична веза треба бити укључена да бисте се повезали на мрежу."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Укључи бежичну везу"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Бежичне мреже"
+
+#~ msgid "Select a network"
+#~ msgstr "Изаберите мрежу"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Користи физички прекидач за искључивање"
+
+#~ msgid "Select Network"
+#~ msgstr "Изабери мрежу"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Подешавања бежичне везе"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s неповезано"
+
+#~ msgid "connecting…"
+#~ msgstr "повезујем се…"
+
+#~ msgid "authentication required"
+#~ msgstr "потребна је пријава"
+
+#~ msgid "connection failed"
+#~ msgstr "повезивање није успело"
+
+#~ msgid "VPN Off"
+#~ msgstr "Искључи ВПН"
+
+#~ msgid "Network Settings"
+#~ msgstr "Подешавања мреже"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s бежична веза"
+#~ msgstr[1] "%s бежичне везе"
+#~ msgstr[2] "%s бежичних веза"
+#~ msgstr[3] "%s бежична веза"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Ноћно светло не ради"
+
+#~ msgid "Resume"
+#~ msgstr "Настави"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Искључи до сутра"
+
+#~ msgid "Power Settings"
+#~ msgstr "Подешавања напајања"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Потпуно пуна"
+
+#~ msgid "Not Charging"
+#~ msgstr "Не пуни се"
+
+#~ msgid "Estimating…"
+#~ msgstr "Прорачунавам…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "До празне: %d:%02d (%d %%)"
+
+# Кратак превод да би се видело време и проценат напуњености у системском менију Гномове шкољке.
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "До пуне: %d:%02d (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Екран се дели"
+
+#~ msgid "Turn off"
+#~ msgstr "Искључи"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Авио режим укључен"
+
+#~ msgid "Lock"
+#~ msgstr "Закључај"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Искључивање или одјава"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Уклони из омиљених"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Додај у омиљене"
+
+#~ msgid "Author"
+#~ msgstr "Творац"
+
+#~ msgid "Warning"
+#~ msgstr "Упозорење"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Проширења могу утицати на стабилност система, укључујући и на "
+#~ "делотворност. Уколико приметите проблеме у раду, препоручујемо да "
+#~ "онемогућите сва проширења."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Нисам успео да се повежем на Гномову шкољку"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Омогући АПИ за интроспекцију"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Омогућава АПИ Д-сабирнице који дозвољава интроспекцију програмског стања "
+#~ "шкољке."
+
+#~ msgid "Minimize"
+#~ msgstr "Умањи"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Поништи увећање"
+
+#~ msgid "App Picker View"
+#~ msgstr "Преглед избирача програма"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Попис тренутно изабраног прегледа у избирачу програма."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Често коришћени програми ће се појавити овде"
+
+#~ msgid "Frequent"
+#~ msgstr "Често"
+
+#~ msgid "All"
+#~ msgstr "Све"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d. %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d. %B %Y."
+
+#~ msgid "Off"
+#~ msgstr "Искљ."
+
+#~ msgid "On"
+#~ msgstr "Укључено"
+
+#~ msgid "Copy Error"
+#~ msgstr "Грешка при копирању"
+
+#~ msgid "Username…"
+#~ msgstr "Корисник…"
+
+#~ msgid "Password: "
+#~ msgstr "Лозинка: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Унеси лозинку…"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Прегледај у Програмима"
+
+#~ msgid "Next"
+#~ msgstr "Даље"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Пријави ме"
+
+#~ msgid "Rename"
+#~ msgstr "Преименуј"
+
+#~ msgid "Type again:"
+#~ msgstr "Упишите поново:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Бежична мрежа захтева потврђивање идентитета"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Лозинка мобилне широкопојасне мреже"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d. %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d нова порука"
+#~ msgstr[1] "%d нове поруке"
+#~ msgstr[2] "%d нових порука"
+#~ msgstr[3] "%d нова порука"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d ново обавештење"
+#~ msgstr[1] "%d нова обавештења"
+#~ msgstr[2] "%d нових обавештења"
+#~ msgstr[3] "%d ново обавештење"
+
+#~ msgid "Account Settings"
+#~ msgstr "Подешавања налога"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Катанац окренутости"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Свеза тастера која паузира и наставља све покренуте близанце, за сврхе "
+#~ "прочишћавања"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Која ће тастатура бити коришћена"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Врста тастатуре за употребу."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Дошло је до грешке при учитавању прозорчета поставки за „%s“:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s целог дана."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, затим %s касније."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, затим %s, а касније %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Осећа се као %s."
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Одјави ме"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Промени корисника"
+
+#~ msgid "Hide tray"
+#~ msgstr "Сакриј касету"
+
+#~ msgid "Status Icons"
+#~ msgstr "Иконице стања"
+
+#~ msgid "Events"
+#~ msgstr "Догађаји"
+
+#~ msgid "Notifications"
+#~ msgstr "Обавештења"
+
+#~ msgid "Media"
+#~ msgstr "Медији"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Преусмеравање потврђивања идентитета вебом"
+
+#~ msgid "Not In Use"
+#~ msgstr "Није у употреби"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Приказује дан у недељи у календару"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Ако је изабрано, приказује ИСО дан у недељи у календару."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Користи као Интернет везу"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "„%s“ захтева приступ вашем месту."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Гномова шкољка (вајланд састављач)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d прикључени уређај"
+#~ msgstr[1] "%d прикључена уређаја"
+#~ msgstr[2] "%d прикључених уређаја"
+#~ msgstr[3] "%d прикључени уређај"
+
+#~ msgid "UPS"
+#~ msgstr "Резервно напајање"
+
+#~ msgid "Battery"
+#~ msgstr "Батерија"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Прикажите фиоку порука"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Портал стопке"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Нед"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Пон"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Уто"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Сре"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Чет"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Пет"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Суб"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Ништа планирано"
+
+#~ msgid "This week"
+#~ msgstr "Ове недеље"
+
+#~ msgid "Next week"
+#~ msgstr "Следеће недеље"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Уклоњиви уређаји"
+
+#~ msgid "Eject"
+#~ msgstr "Избаци"
+
+#~ msgid "Invitation"
+#~ msgstr "Позивница"
+
+#~ msgid "Call"
+#~ msgstr "Позив"
+
+#~ msgid "File Transfer"
+#~ msgstr "Пренос датотека"
+
+#~ msgid "Chat"
+#~ msgstr "Ћаскање"
+
+#~ msgid "Unmute"
+#~ msgstr "Укључи тон"
+
+#~ msgid "Mute"
+#~ msgstr "Утишај"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Позивница за „%s“"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s вас позива да се придружите „%s“"
+
+#~ msgid "Decline"
+#~ msgstr "Одбиј"
+
+#~ msgid "Accept"
+#~ msgstr "Прихвати"
+
+#~ msgid "Video call from %s"
+#~ msgstr "%s вам је упутио видео позив"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s вас зове"
+
+#~ msgid "Answer"
+#~ msgstr "Одговори"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s вам шаље „%s“"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s тражи одобрење да види када сте на вези"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Потврђивање идентитета није успело"
+
+#~ msgid "Encryption error"
+#~ msgstr "Грешка шифровања"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Уверење није прослеђено"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Уверење није поверљиво"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Уверење је истекло"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Уверење није активирано"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Назив домаћина уверења се не поклапа"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Отисак уверења се не поклапа"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Уверење је самопотписано"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Стање је постављено на „неповезан“"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Уверење није исправно"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Веза је одбијена"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Веза не може бити успостављена"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Веза је изгубљена"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Овај налог је већ повезан са сервером"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Веза је замењена новом везом користећи исти ресурс"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Овај налог већ постоји на серверу"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Сервер је тренутно превише заузет да би руковао везом"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Уверење је опозвано"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Уверење користи несигуран алгоритам шифровања или је криптографски слабо"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Дужина уверења сервера, или дубина ланца уверења сервера, премашује "
+#~ "границе које је задала библиотека шифровања"
+
+#~ msgid "Internal error"
+#~ msgstr "Унутрашња грешка"
+
+#~ msgid "View account"
+#~ msgstr "Види налог"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Отвори календар"
+
+#~ msgid "Open"
+#~ msgstr "Отвори"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Очисти поруке"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Поставке обавештења"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Изборник фиоке"
+
+#~ msgid "No Messages"
+#~ msgstr "Нема порука"
+
+#~ msgid "Message Tray"
+#~ msgstr "Фиока порука"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Највиши ниво тачности места."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Подешава највећи ниво тачности места коју програми могу да користе. "
+#~ "Исправне могућности су „off“ (искључује праћење места), "
+#~ "„country“ (држава), „city“ (град), „neighborhood“ (комшилук), "
+#~ "„street“ (улица), и „exact“ (тачно — обично захтева ГПС пријемник). "
+#~ "Имајте на уму да ће ово одлучити шта ће „Гео индиција“ дозволити "
+#~ "програмима само да виде и да могу пронаћи корисниково место на своју руку "
+#~ "користећи изворишта мреже (мада са тачношћу на нивоу улице у најбољем "
+#~ "случају)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Распоред дугмића на траци наслова"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Овај кључ превазилази кључ у „org.gnome.desktop.wm.preferences“ када "
+#~ "покреће Гномову шкољку."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Изаберите проширење за подешавање користећи прозорче за избор."
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Списак категорија које треба да буду приказане као фасцикле"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Назив сваке категорије на списку ће бити представљен као фасцикла у "
+#~ "прегледу програма, уместо да буде приказан непосредно у главном прегледу."
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, у <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b>. <b>%B</b> у <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b>. <b>%B</b> <b>%Y</b>. у <b>%H:%M</b>"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Захтев за овлашћење са „%s“"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Уређај „%s“ жели да се упари са овим рачунаром"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Уређај „%s“ жели приступ услузи „%s“"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Одобри само овај пут"
+
+#~ msgid "Reject"
+#~ msgstr "Одбиј"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Упарујем потврду за „%s“"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Молим потврдите да ли се пропусни кључ „%06d“ подудара са оним на уређају."
+
+#~ msgid "Does not match"
+#~ msgstr "Не подудара се"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Захтев за упаривање за „%s“"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Молим унесите ПИН назначен на уређају."
+
+#~ msgid "OK"
+#~ msgstr "У реду"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Извините, данас за вас нема мудрости:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "Пророк је рекао %s"
diff --git a/po/sr@latin.po b/po/sr@latin.po
new file mode 100644
index 0000000..3efe097
--- /dev/null
+++ b/po/sr@latin.po
@@ -0,0 +1,2641 @@
+# Serbian translation for gnome-shell.
+# Courtesy of Prevod.org team (http://prevod.org/) -- 2010—2017.
+# Copyright (C) 2010 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Translators:
+# Miloš Popović <gpopac@gmail.com>, 2010—2011.
+# Miroslav Nikolić <miroslavnikolic@rocketmail.com>, 2011—2017.
+# Marko M. Kostić <marko.m.kostic@gmail.com>, 2016.
+# Borisav Živanović <borisavzivanovic@gmail.com>, 2017—2018.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2018-02-19 12:41+0000\n"
+"PO-Revision-Date: 2018-02-19 18:00+0100\n"
+"Last-Translator: Borisav Živanović <borisavzivanovic@gmail.com>\n"
+"Language-Team: srpski <gnome-sr@googlegroups.org>\n"
+"Language: sr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : n"
+"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Project-Style: gnome\n"
+"X-Poedit-Bookmarks: -1,167,-1,-1,-1,-1,-1,-1,-1,-1\n"
+"X-Generator: Poedit 2.0.4\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistem"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Prikažite spisak obaveštenja"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Postavite u žižu aktivno obaveštenje"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Prikaži pregled"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Prikaži sve programe"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Otvorite izbornik programa"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:152
+msgid "Shell Extensions"
+msgstr "Proširenja školjke"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "Podesite proširenja Gnomove školjke"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Gnomova školjka"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Upravnik prozorima i pokretač programa"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Uključuje alate korisne programerima i testerima iz „Alt-F2“ prozorčeta"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Dozvoljava pristup unutrašnjem otklanjanju grešaka i alatima za praćenje "
+"korišćenjem „Alt-F2“ prozorčeta."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "JUIB-ovi proširenja za uključivanje"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Proširenja Gnomove školjke imaju JUIB vrednost; ovaj ključ ispisuje proširenja "
+"koja treba učitavati. Svako proširenje koje želi biti učitano mora biti na "
+"spisku. Takođe možete da upravljate ovim spiskom koristeći načine D-"
+"magistrale „Uključi proširenje“ i „Isključi proširenje“ na „org.gnome.Shell“."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disable user extensions"
+msgstr "Isključuje korisnička proširenja"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Isključuje sva proširenja koja je uključio korisnik bez delovanja na podešavanje "
+"„enabled-extension“."
+
+#: data/org.gnome.shell.gschema.xml.in:34
+msgid "Disables the validation of extension version compatibility"
+msgstr "Isključuje potvrđivanje saglasnosti izdanja proširenja"
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Gnomova školjka će učitati samo proširenja koja tvrde da podržavaju tekuće "
+"pokrenuto izdanje. Uključivanje ove opcije će isključiti ovu proveru i pokušati "
+"da učita proširenja bez obzira na izdanja koja tvrde da podržavaju."
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Spisak IB datoteka radne površine sa omiljenim programima"
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programi koji odgovaraju ovim odrednicama se prikazuju unutar oblasti "
+"omiljenih programima."
+
+#: data/org.gnome.shell.gschema.xml.in:51
+msgid "App Picker View"
+msgstr "Pregled izbirača programa"
+
+#: data/org.gnome.shell.gschema.xml.in:52
+msgid "Index of the currently selected view in the application picker."
+msgstr "Popis trenutno izabranog pregleda u izbiraču programa."
+
+#: data/org.gnome.shell.gschema.xml.in:58
+msgid "History for command (Alt-F2) dialog"
+msgstr "Istorijat prozorčeta (Alt-F2) naredbi"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "History for the looking glass dialog"
+msgstr "Istorijat prozorčeta ogledala"
+
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Uvek prikazuje stavku izbornika „Odjavi me“ u izborniku korisnika."
+
+#: data/org.gnome.shell.gschema.xml.in:68
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ovaj ključ prevazilazi samostalno skrivanje stavke izbornika „Odjavi me“ u "
+"slučaju jednog korisnika, jedne sesije."
+
+#: data/org.gnome.shell.gschema.xml.in:75
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Da li će biti zapamćena lozinka za kačenje šifrovanih ili udaljenih sistema "
+"datoteka"
+
+#: data/org.gnome.shell.gschema.xml.in:76
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Školjka će zatražiti lozinku kada se prikači šifrovani uređaj ili udaljeni "
+"sistem datoteka. Ako lozinka može biti sačuvana za daljnju upotrebu biće "
+"prisutno polje za izbor „Zapamti lozinku“. Ovaj ključ podešava osnovno stanje "
+"polja za izbor."
+
+#: data/org.gnome.shell.gschema.xml.in:85
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "Da li je osnovni adapter blututa podesio njemu pridružene uređaje"
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Školjka će prikazati stavku izbornika blututa samo ako je adapter blututa "
+"upaljen, ili ako ima podešenih uređaja pridruženih osnovnom adapteru. Ovo će "
+"se vratiti na početno stanje ako se ustanovi da osnovni adapter nema njemu "
+"pridruženih uređaja."
+
+#: data/org.gnome.shell.gschema.xml.in:101
+msgid "Keybinding to open the application menu"
+msgstr "Prečica za otvaranje izbornika programa"
+
+#: data/org.gnome.shell.gschema.xml.in:102
+msgid "Keybinding to open the application menu."
+msgstr "Prečica za otvaranje izbornika programa."
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Prečica za otvaranje pregleda „Prikaži programe“"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Prečica za otvaranje pregleda „Prikaži programe“ u pregledu aktivnosti."
+
+#: data/org.gnome.shell.gschema.xml.in:116
+msgid "Keybinding to open the overview"
+msgstr "Prečica za otvaranje pregleda"
+
+#: data/org.gnome.shell.gschema.xml.in:117
+msgid "Keybinding to open the Activities Overview."
+msgstr "Prečica za otvaranje pregleda aktivnosti."
+
+#: data/org.gnome.shell.gschema.xml.in:123
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Prečica za okidanje vidljivosti spiska obaveštenja"
+
+#: data/org.gnome.shell.gschema.xml.in:124
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Prečica za okidanje vidljivosti spiska obaveštenja."
+
+#: data/org.gnome.shell.gschema.xml.in:130
+msgid "Keybinding to focus the active notification"
+msgstr "Prečica za postavljanje u žižu radnog obaveštenja"
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to focus the active notification."
+msgstr "Prečica za postavljanje u žižu radnog obaveštenja."
+
+#: data/org.gnome.shell.gschema.xml.in:137
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"Sveza tastera koja pauzira i nastavlja sve pokrenute blizance, za svrhe "
+"pročišćavanja"
+
+#: data/org.gnome.shell.gschema.xml.in:146
+msgid "Which keyboard to use"
+msgstr "Koja će tastatura biti korišćena"
+
+#: data/org.gnome.shell.gschema.xml.in:147
+msgid "The type of keyboard to use."
+msgstr "Vrsta tastature za upotrebu."
+
+#: data/org.gnome.shell.gschema.xml.in:158
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Limit switcher to current workspace."
+msgstr "Ograničava prebacivača na tekući radni prostor."
+
+#: data/org.gnome.shell.gschema.xml.in:159
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Ako je izabrano, samo programi koji imaju prozore na tekućem radnom prostoru "
+"se prikazuju u prebacivaču. U suprotnom, svi programi su uključeni."
+
+#: data/org.gnome.shell.gschema.xml.in:176
+msgid "The application icon mode."
+msgstr "Režim ikonice programa."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Podešava način prikazivanja prozora u prebacivaču. Ispravne mogućnosti su "
+"„thumbnail-only“ (prikazuje sličicu prozora), „app-icon-only“ (prikazuje "
+"samo ikonicu programa) ili „both“ (oba)."
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Ako je izabrano, samo prozori sa tekućeg radnog prostora se prikazuju u "
+"prebacivaču. U suprotnom, svi prozori su uključeni."
+
+#: data/org.gnome.shell.gschema.xml.in:197
+msgid "Attach modal dialog to the parent window"
+msgstr "Prikačinje prozorče roditeljskom prozoru"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+#: data/org.gnome.shell.gschema.xml.in:207
+#: data/org.gnome.shell.gschema.xml.in:215
+#: data/org.gnome.shell.gschema.xml.in:223
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Ovaj ključ prevazilazi ključ u „org.gnome.mutter“ kada pokreće Gnomovu školjku."
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Uključuje popločavanje ivice prilikom otpuštanja prozora na ivicama ekrana"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Workspaces are managed dynamically"
+msgstr "Radnim prostorima se upravlja dinamički"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Workspaces only on primary monitor"
+msgstr "Radni prostori samo na primarnom monitoru"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Zastoj prvog plana se menja u režimu miša dok se pokazivač ne zaustavi"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Mrežna prijava"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:120
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "Došlo je do greške pri učitavanju prozorčeta postavki za „%s“:"
+
+#: js/gdm/authPrompt.js:149 js/ui/audioDeviceSelection.js:71
+#: js/ui/components/networkAgent.js:117 js/ui/components/polkitAgent.js:148
+#: js/ui/endSessionDialog.js:482 js/ui/extensionDownloader.js:197
+#: js/ui/shellMountOperation.js:344 js/ui/status/network.js:925
+msgid "Cancel"
+msgstr "Otkaži"
+
+#: js/gdm/authPrompt.js:171 js/gdm/authPrompt.js:218 js/gdm/authPrompt.js:450
+msgid "Next"
+msgstr "Dalje"
+
+#: js/gdm/authPrompt.js:214 js/ui/shellMountOperation.js:348
+#: js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "Otključaj"
+
+#: js/gdm/authPrompt.js:216
+msgctxt "button"
+msgid "Sign In"
+msgstr "Prijavi me"
+
+#: js/gdm/loginDialog.js:308
+msgid "Choose Session"
+msgstr "Izaberi sesiju"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:458
+msgid "Not listed?"
+msgstr "Nije na spisku?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:888
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(npr., korisnik ili %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:893 js/ui/components/networkAgent.js:243
+#: js/ui/components/networkAgent.js:261
+msgid "Username: "
+msgstr "Korisnik: "
+
+#: js/gdm/loginDialog.js:1236
+msgid "Login Window"
+msgstr "Prozor za prijavljivanje"
+
+#: js/gdm/util.js:346
+msgid "Authentication error"
+msgstr "Greška potvrđivanja identiteta"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:478
+msgid "(or swipe finger)"
+msgstr "(ili prevucite prst)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:99
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Isključi"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:102
+msgid "power off;shutdown;reboot;restart"
+msgstr ""
+"power off;shutdown;isključi;ugasi;gašenje;isključi;ugasi;gašenje;iskljuci;"
+"gasenje;reboot;restart;ponovo;pokreni;ponovo;pokreni;restart"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:106
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Zaključaj ekran"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:109
+msgid "lock screen"
+msgstr "zaključaj ekran"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:113
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Odjavi me"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:116
+msgid "logout;sign off"
+msgstr "izađi;odjavi se;odloguj se;izađi;odjavi se;odloguj se;izadji;"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:120
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Obustavi"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:123
+msgid "suspend;sleep"
+msgstr "suspend;sleep;obustavi;spavaj;suspenduj;obustavi;spavaj;suspenduj;"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:127
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Promeni korisnika"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:130
+msgid "switch user"
+msgstr "switch user;promeni korisnika;promeni korisnika"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:134
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr "Zaključaj okretanje ekrana"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:137
+msgid "lock orientation;screen;rotation"
+msgstr ""
+"lock orientation;screen;rotation;zaključaj okretanje ekrana;zaključaj "
+"orijentaciju;zaključaj okretanje ekrana;zaključaj orijentaciju;zakljucaj "
+"okretanje ekrana;ekran;ekran;okretanje;okretanje"
+
+#: js/misc/util.js:122
+msgid "Command not found"
+msgstr "Naredba nije nađena"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:155
+msgid "Could not parse command:"
+msgstr "Ne mogu da obradim naredbu:"
+
+#: js/misc/util.js:163
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Nije uspelo izvršavanje „%s“:"
+
+#: js/misc/util.js:180
+msgid "Just now"
+msgstr "Upravo sada"
+
+#: js/misc/util.js:182
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "Pre %d minut"
+msgstr[1] "Pre %d minuta"
+msgstr[2] "Pre %d minuta"
+msgstr[3] "Pre jedan minut"
+
+#: js/misc/util.js:185
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "Pre %d sat"
+msgstr[1] "Pre %d sata"
+msgstr[2] "Pre %d sati"
+msgstr[3] "Pre jedan sat"
+
+#: js/misc/util.js:188
+msgid "Yesterday"
+msgstr "Juče"
+
+#: js/misc/util.js:190
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "Pre %d dan"
+msgstr[1] "Pre %d dana"
+msgstr[2] "Pre %d dana"
+msgstr[3] "Pre jedan dan"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "Pre %d nedelju"
+msgstr[1] "Pre %d nedelje"
+msgstr[2] "Pre %d nedelja"
+msgstr[3] "Pre nedelju dana"
+
+#: js/misc/util.js:196
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "Pre %d mesec"
+msgstr[1] "Pre %d meseca"
+msgstr[2] "Pre %d meseci"
+msgstr[3] "Pre mesec dana"
+
+#: js/misc/util.js:198
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "Pre %d godinu"
+msgstr[1] "Pre %d godine"
+msgstr[2] "Pre %d godina"
+msgstr[3] "Pre godinu dana"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:228
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:234
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Juče, %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:240
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:246
+#, no-c-format
+msgid "%B %d, %H∶%M"
+msgstr "%d. %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:252
+#, no-c-format
+msgid "%B %d %Y, %H∶%M"
+msgstr "%d. %B %Y., %H:%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:257
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:263
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Juče, %l:%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:269
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:275
+#, no-c-format
+msgid "%B %d, %l∶%M %p"
+msgstr "%d. %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:281
+#, no-c-format
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%d .%B %Y., %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:66
+msgid "Hotspot Login"
+msgstr "Prijava na vruću tačku"
+
+#: js/portalHelper/main.js:112
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Vaša veza sa ovom prijavom vruće tačke nije bezbedna. Ljudi u blizini mogu "
+"videti lozinke ili druge podatke koje budete uneli na ovoj stranici."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:63 js/ui/status/location.js:395
+msgid "Deny Access"
+msgstr "Zabrani pristup"
+
+#: js/ui/accessDialog.js:64 js/ui/status/location.js:398
+msgid "Grant Access"
+msgstr "Dozvoli pristup"
+
+#: js/ui/appDisplay.js:809
+msgid "Frequently used applications will appear here"
+msgstr "Često korišćeni programi će se pojaviti ovde"
+
+#: js/ui/appDisplay.js:930
+msgid "Frequent"
+msgstr "Često"
+
+#: js/ui/appDisplay.js:937
+msgid "All"
+msgstr "Sve"
+
+#: js/ui/appDisplay.js:1919
+msgid "New Window"
+msgstr "Novi prozor"
+
+#: js/ui/appDisplay.js:1933
+msgid "Launch using Dedicated Graphics Card"
+msgstr "Pokreni sa namenjenom grafičkom karticom"
+
+#: js/ui/appDisplay.js:1960 js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "Ukloni iz omiljenih"
+
+#: js/ui/appDisplay.js:1966
+msgid "Add to Favorites"
+msgstr "Dodaj u omiljene"
+
+#: js/ui/appDisplay.js:1976
+msgid "Show Details"
+msgstr "Prikaži detalje"
+
+#: js/ui/appFavorites.js:141
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "„%s“ je dodat među omiljene."
+
+#: js/ui/appFavorites.js:175
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "„%s“ je uklonjen iz omiljenih."
+
+#: js/ui/audioDeviceSelection.js:59
+msgid "Select Audio Device"
+msgstr "Izaberite zvučni uređaj"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Sound Settings"
+msgstr "Podešavanja zvuka"
+
+#: js/ui/audioDeviceSelection.js:78
+msgid "Headphones"
+msgstr "Slušalice"
+
+#: js/ui/audioDeviceSelection.js:80
+msgid "Headset"
+msgstr "Slušalice sa mikrofonom"
+
+#: js/ui/audioDeviceSelection.js:82 js/ui/status/volume.js:221
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "Izmeni pozadinu…"
+
+#: js/ui/backgroundMenu.js:21 js/ui/status/nightLight.js:51
+msgid "Display Settings"
+msgstr "Podešavanja prikaza"
+
+#: js/ui/backgroundMenu.js:22 js/ui/status/system.js:265
+msgid "Settings"
+msgstr "Podešavanja"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:44
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:73
+msgctxt "grid sunday"
+msgid "S"
+msgstr "N"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:75
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:77
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "U"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:79
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:81
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Č"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:83
+msgctxt "grid friday"
+msgid "F"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:85
+msgctxt "grid saturday"
+msgid "S"
+msgstr "S"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:382
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:392
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:449
+msgid "Previous month"
+msgstr "Prethodni mesec"
+
+#: js/ui/calendar.js:459
+msgid "Next month"
+msgstr "Sledeći mesec"
+
+#: js/ui/calendar.js:612
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:667
+msgid "Week %V"
+msgstr "Sedmica %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:736
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Ceo dan"
+
+#: js/ui/calendar.js:869
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %d. %B"
+
+#: js/ui/calendar.js:873
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %d. %B %Y."
+
+#: js/ui/calendar.js:1093
+msgid "No Notifications"
+msgstr "Bez obaveštenja"
+
+#: js/ui/calendar.js:1096
+msgid "No Events"
+msgstr "Bez događaja"
+
+#: js/ui/calendar.js:1124
+msgid "Clear All"
+msgstr "Očisti sve"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:44
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "„%s“ ne odgovara."
+
+#: js/ui/closeDialog.js:45
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Možete izabrati da sačekate koji trenutak da nastavi ili da primorate "
+"program da se čitav zatvori."
+
+#: js/ui/closeDialog.js:61
+msgid "Force Quit"
+msgstr "Primoraj izlaz"
+
+#: js/ui/closeDialog.js:64
+msgid "Wait"
+msgstr "Sačekaj"
+
+#: js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "Spoljni uređaj je priključen"
+
+#: js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "Spoljni uređaj je isključen"
+
+#: js/ui/components/autorunManager.js:358
+#, javascript-format
+msgid "Open with %s"
+msgstr "Otvori programom %s"
+
+#: js/ui/components/keyring.js:107 js/ui/components/polkitAgent.js:284
+msgid "Password:"
+msgstr "Lozinka:"
+
+#: js/ui/components/keyring.js:140
+msgid "Type again:"
+msgstr "Upišite ponovo:"
+
+#: js/ui/components/networkAgent.js:112 js/ui/status/network.js:245
+#: js/ui/status/network.js:338 js/ui/status/network.js:928
+msgid "Connect"
+msgstr "Poveži se"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:205 js/ui/components/networkAgent.js:217
+#: js/ui/components/networkAgent.js:245 js/ui/components/networkAgent.js:265
+#: js/ui/components/networkAgent.js:275
+msgid "Password: "
+msgstr "Lozinka: "
+
+#. static WEP
+#: js/ui/components/networkAgent.js:210
+msgid "Key: "
+msgstr "Ključ: "
+
+#: js/ui/components/networkAgent.js:249
+msgid "Identity: "
+msgstr "Identitet: "
+
+#: js/ui/components/networkAgent.js:251
+msgid "Private key password: "
+msgstr "Lozinka privatnog ključa: "
+
+#: js/ui/components/networkAgent.js:263
+msgid "Service: "
+msgstr "Usluga: "
+
+#: js/ui/components/networkAgent.js:292 js/ui/components/networkAgent.js:659
+msgid "Authentication required by wireless network"
+msgstr "Bežična mreža zahteva potvrđivanje identiteta"
+
+#: js/ui/components/networkAgent.js:293 js/ui/components/networkAgent.js:660
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Potrebne su lozinke ili ključevi šifrovanja za pristup bežičnoj mreži „%s“."
+
+#: js/ui/components/networkAgent.js:297 js/ui/components/networkAgent.js:663
+msgid "Wired 802.1X authentication"
+msgstr "Potvrđivanje identiteta za žičanu 802.1X vezu"
+
+#: js/ui/components/networkAgent.js:299
+msgid "Network name: "
+msgstr "Naziv mreže: "
+
+#: js/ui/components/networkAgent.js:304 js/ui/components/networkAgent.js:667
+msgid "DSL authentication"
+msgstr "Potvrđivanje identiteta DSL-a"
+
+#: js/ui/components/networkAgent.js:311 js/ui/components/networkAgent.js:673
+msgid "PIN code required"
+msgstr "Potreban je PIN kôd"
+
+#: js/ui/components/networkAgent.js:312 js/ui/components/networkAgent.js:674
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Potreban je PIN kod za povezivanje mobilnog širokopojasnog uređaja"
+
+#: js/ui/components/networkAgent.js:313
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:320 js/ui/components/networkAgent.js:680
+msgid "Mobile broadband network password"
+msgstr "Lozinka mobilne širokopojasne mreže"
+
+#: js/ui/components/networkAgent.js:321 js/ui/components/networkAgent.js:664
+#: js/ui/components/networkAgent.js:668 js/ui/components/networkAgent.js:681
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Potrebna je lozinka za povezivanje na „%s“."
+
+#: js/ui/components/networkAgent.js:648 js/ui/status/network.js:1699
+msgid "Network Manager"
+msgstr "Upravnik mreže"
+
+#: js/ui/components/polkitAgent.js:43
+msgid "Authentication Required"
+msgstr "Potrebna je prijava"
+
+#: js/ui/components/polkitAgent.js:71
+msgid "Administrator"
+msgstr "Administrator"
+
+#: js/ui/components/polkitAgent.js:151
+msgid "Authenticate"
+msgstr "Potvrdi identitet"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:270 js/ui/shellMountOperation.js:328
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Pogrešili ste! Pokušajte ponovo."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:799
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "„%s“ je sada poznat kao „%s“"
+
+#: js/ui/ctrlAltTab.js:29 js/ui/viewSelector.js:186
+msgid "Windows"
+msgstr "Prozori"
+
+#: js/ui/dash.js:246 js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "Prikaži programe"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:445
+msgid "Dash"
+msgstr "Polet"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: js/ui/dateMenu.js:76
+msgid "%B %e %Y"
+msgstr "%e. %B %Y."
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%A %B %e %Y"
+msgstr "%A, %e. %B %Y."
+
+#: js/ui/dateMenu.js:148
+msgid "Add world clocks…"
+msgstr "Svetski satovi…"
+
+#: js/ui/dateMenu.js:149
+msgid "World Clocks"
+msgstr "Svetski satovi"
+
+#: js/ui/dateMenu.js:228
+msgid "Weather"
+msgstr "Vremenska prognoza"
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:292
+#, javascript-format
+msgid "%s all day."
+msgstr "%s celog dana."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:298
+#, javascript-format
+msgid "%s, then %s later."
+msgstr "%s, zatim %s kasnije."
+
+#. Translators: %s is a weather condition like "Clear sky"; see
+#. libgweather for the possible condition strings. If at all
+#. possible, the sentence should match the grammatical case etc. of
+#. the inserted conditions.
+#: js/ui/dateMenu.js:304
+#, javascript-format
+msgid "%s, then %s, followed by %s later."
+msgstr "%s, zatim %s, a kasnije %s."
+
+#: js/ui/dateMenu.js:315
+msgid "Select a location…"
+msgstr "Izaberite mesto…"
+
+#: js/ui/dateMenu.js:318
+msgid "Loading…"
+msgstr "Učitavam…"
+
+#. Translators: %s is a temperature with unit, e.g. "23℃"
+#: js/ui/dateMenu.js:324
+#, javascript-format
+msgid "Feels like %s."
+msgstr "Oseća se kao %s."
+
+#: js/ui/dateMenu.js:327
+msgid "Go online for weather information"
+msgstr "Idite na mrežu za podatke o vremenskoj prognozi."
+
+#: js/ui/dateMenu.js:329
+msgid "Weather information is currently unavailable"
+msgstr "Podaci o vremenskoj prognozi trenutno nisu dostupni."
+
+#: js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Odjavi korisnika „%s“"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "Odjavi me"
+
+#: js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s će biti odjavljen za %d sekundu."
+msgstr[1] "%s će biti odjavljen za %d sekunde."
+msgstr[2] "%s će biti odjavljen za %d sekundi."
+msgstr[3] "%s će biti odjavljen za %d sekundu."
+
+#: js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Bićete odjavljeni za %d sekundu."
+msgstr[1] "Bićete odjavljeni za %d sekunde."
+msgstr[2] "Bićete odjavljeni za %d sekundi."
+msgstr[3] "Bićete odjavljeni za %d sekundu."
+
+#: js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Odjavi"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "Isključi"
+
+#: js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Instaliraj osveženja i isključi"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Računar će se isključiti za %d sekundu."
+msgstr[1] "Računar će se isključiti za %d sekunde."
+msgstr[2] "Računar će se isključiti za %d sekundi."
+msgstr[3] "Računar će se isključiti za %d sekundu."
+
+#: js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Instaliraj osveženja softvera na čekanju"
+
+#: js/ui/endSessionDialog.js:94 js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "Ponovo pokreni"
+
+#: js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "Isključi"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "Ponovo pokreni"
+
+#: js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistem će se ponovo pokrenuti za %d sekundu."
+msgstr[1] "Sistem će se ponovo pokrenuti za %d sekunde."
+msgstr[2] "Sistem će se ponovo pokrenuti za %d sekundi."
+msgstr[3] "Sistem će se ponovo pokrenuti za %d sekundu."
+
+#: js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Ponovo pokreni i instaliraj ažuriranja"
+
+#: js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistem će se sam ponovo pokrenuti i instalirati ažuriranja za %d sekundu."
+msgstr[1] ""
+"Sistem će se sam ponovo pokrenuti i instalirati ažuriranja za %d sekunde."
+msgstr[2] ""
+"Sistem će se sam ponovo pokrenuti i instalirati ažuriranja za %d sekundi."
+msgstr[3] ""
+"Sistem će se sam ponovo pokrenuti i instalirati ažuriranja za %d sekundu."
+
+#: js/ui/endSessionDialog.js:127 js/ui/endSessionDialog.js:147
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Ponovo pokreni i instaliraj"
+
+#: js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Instaliraj i isključi"
+
+#: js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Isključi nakon instaliranja osveženja"
+
+#: js/ui/endSessionDialog.js:137
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Ponovo pokreni i instaliraj nadogradnju"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:142
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s će bit instaliran/a nakon ponovnog pokretanja. Instalacija nadogradnje "
+"ponekad može da potraje: proverite da li ste napravili rezervu vaših važnih "
+"podataka i da li je računar priključen na mrežno napajanje."
+
+#: js/ui/endSessionDialog.js:361
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"Napajate se sa baterije: priključite mrežno napajanje pre instaliranja osveženja."
+
+#: js/ui/endSessionDialog.js:378
+msgid "Some applications are busy or have unsaved work."
+msgstr "Neki programi su zauzeti ili imaju nesačuvani rad."
+
+#: js/ui/endSessionDialog.js:385
+msgid "Other users are logged in."
+msgstr "Drugi korisnici su prijavljeni."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:670
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (udaljeno)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:673
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (ljuska)"
+
+#: js/ui/extensionDownloader.js:201
+msgid "Install"
+msgstr "Instaliraj"
+
+#: js/ui/extensionDownloader.js:206
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Da preuzmem i da instaliram „%s“ sa „extensions.gnome.org“-a?"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:59
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr "„%s“ želi da spreči prečice"
+
+#: js/ui/inhibitShortcutsDialog.js:60
+msgid "Application wants to inhibit shortcuts"
+msgstr "Program želi da spreči prečice"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:69
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Možete povratiti prečice pritiskom na „%s“."
+
+#: js/ui/inhibitShortcutsDialog.js:74
+msgid "Deny"
+msgstr "Zabrani"
+
+#: js/ui/inhibitShortcutsDialog.js:80
+msgid "Allow"
+msgstr "Dozvoli"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned On"
+msgstr "Spori tasteri su uključeni"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid "Slow Keys Turned Off"
+msgstr "Spori tasteri su isključeni"
+
+#: js/ui/kbdA11yDialog.js:35
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Upravo ste taster Shift držali pritisnutim 8 sekundi. To uključuje Spore "
+"tastere, što utiče na rad tastature."
+
+#: js/ui/kbdA11yDialog.js:42
+msgid "Sticky Keys Turned On"
+msgstr "Lepljivi tasteri su uključeni"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid "Sticky Keys Turned Off"
+msgstr "Lepljivi tasteri su isključeni"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Upravo ste pritisnuli taster Shift 5 puta zaredom. To je prečica za Lepljive "
+"tastere, što utiče na rad tastature."
+
+#: js/ui/kbdA11yDialog.js:47
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Upravo ste pritisnuli dva tastera istovremeno, ili ste pritisnuli taster "
+"Shift 5 puta zaredom. To uključuje Lepljive tastere, što utiče na rad "
+"tastature."
+
+#: js/ui/kbdA11yDialog.js:59
+msgid "Leave On"
+msgstr "Ostavi uključeno"
+
+#: js/ui/kbdA11yDialog.js:59 js/ui/status/bluetooth.js:143
+#: js/ui/status/network.js:1287
+msgid "Turn On"
+msgstr "Uključi"
+
+#: js/ui/kbdA11yDialog.js:67 js/ui/status/bluetooth.js:143
+#: js/ui/status/network.js:154 js/ui/status/network.js:339
+#: js/ui/status/network.js:1287 js/ui/status/network.js:1402
+#: js/ui/status/nightLight.js:47 js/ui/status/rfkill.js:90
+#: js/ui/status/rfkill.js:117
+msgid "Turn Off"
+msgstr "Isključi"
+
+#: js/ui/kbdA11yDialog.js:67
+msgid "Leave Off"
+msgstr "Ostavi isključeno"
+
+#: js/ui/keyboard.js:198
+msgid "Region & Language Settings"
+msgstr "Podešavanja regiona i jezika"
+
+#: js/ui/lookingGlass.js:642
+msgid "No extensions installed"
+msgstr "Nisu instalirana proširenja"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:696
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s nije objavio nikakve greške."
+
+#: js/ui/lookingGlass.js:702
+msgid "Hide Errors"
+msgstr "Sakrij greške"
+
+#: js/ui/lookingGlass.js:706 js/ui/lookingGlass.js:766
+msgid "Show Errors"
+msgstr "Prikažite greške"
+
+#: js/ui/lookingGlass.js:715
+msgid "Enabled"
+msgstr "Uključeno"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:718 subprojects/gvc/gvc-mixer-control.c:1866
+msgid "Disabled"
+msgstr "Isključeno"
+
+#: js/ui/lookingGlass.js:720
+msgid "Error"
+msgstr "Greška"
+
+#: js/ui/lookingGlass.js:722
+msgid "Out of date"
+msgstr "Izvan datuma"
+
+#: js/ui/lookingGlass.js:724
+msgid "Downloading"
+msgstr "Preuzimam"
+
+#: js/ui/lookingGlass.js:748
+msgid "View Source"
+msgstr "Prikaži kod"
+
+#: js/ui/lookingGlass.js:757
+msgid "Web Page"
+msgstr "Veb stranica"
+
+#: js/ui/messageTray.js:1494
+msgid "System Information"
+msgstr "Podaci o sistemu"
+
+#: js/ui/mpris.js:211
+msgid "Unknown artist"
+msgstr "Nepoznat izvođač"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Nepoznat naslov"
+
+#: js/ui/overview.js:84
+msgid "Undo"
+msgstr "Opozovi"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:113
+msgid "Overview"
+msgstr "Pregled"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:240
+msgid "Type to search…"
+msgstr "Upišite tekst za pretragu…"
+
+#: js/ui/padOsd.js:103
+msgid "New shortcut…"
+msgstr "Nova prečica…"
+
+#: js/ui/padOsd.js:152
+msgid "Application defined"
+msgstr "Definisani program"
+
+#: js/ui/padOsd.js:153
+msgid "Show on-screen help"
+msgstr "Prikaži pomoć na ekranu"
+
+#: js/ui/padOsd.js:154
+msgid "Switch monitor"
+msgstr "Promeni monitor"
+
+#: js/ui/padOsd.js:155
+msgid "Assign keystroke"
+msgstr "Dodeli taster"
+
+#: js/ui/padOsd.js:220
+msgid "Done"
+msgstr "Gotovo"
+
+#: js/ui/padOsd.js:734
+msgid "Edit…"
+msgstr "Uredi…"
+
+#: js/ui/padOsd.js:774 js/ui/padOsd.js:879
+msgid "None"
+msgstr "Ništa"
+
+#: js/ui/padOsd.js:833
+msgid "Press a button to configure"
+msgstr "Pritisnite dugme da podesite"
+
+#: js/ui/padOsd.js:834
+msgid "Press Esc to exit"
+msgstr "Pritisnite „Esc“ da izađete"
+
+#: js/ui/padOsd.js:837
+msgid "Press any key to exit"
+msgstr "Pritisnite neki taster da izađete"
+
+#: js/ui/panel.js:358
+msgid "Quit"
+msgstr "Izađi"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:414
+msgid "Activities"
+msgstr "Aktivnosti"
+
+#: js/ui/panel.js:695
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistem"
+
+#: js/ui/panel.js:814
+msgid "Top Bar"
+msgstr "Gornja traka"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:291
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: js/ui/runDialog.js:71
+msgid "Enter a Command"
+msgstr "Unesite naredbu"
+
+#: js/ui/runDialog.js:111 js/ui/windowMenu.js:160
+msgid "Close"
+msgstr "Zatvori"
+
+#: js/ui/runDialog.js:277
+msgid "Restart is not available on Wayland"
+msgstr "Ponovno pokretanje nije dostupno na Vejlandu"
+
+#: js/ui/runDialog.js:282
+msgid "Restarting…"
+msgstr "Ponovno pokrećem…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %d. %B"
+
+#: js/ui/screenShield.js:147
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d nova poruka"
+msgstr[1] "%d nove poruke"
+msgstr[2] "%d novih poruka"
+msgstr[3] "%d nova poruka"
+
+#: js/ui/screenShield.js:149
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d novo obaveštenje"
+msgstr[1] "%d nova obaveštenja"
+msgstr[2] "%d novih obaveštenja"
+msgstr[3] "%d novo obaveštenje"
+
+#: js/ui/screenShield.js:452 js/ui/status/system.js:284
+msgid "Lock"
+msgstr "Zaključaj"
+
+#: js/ui/screenShield.js:715
+msgid "GNOME needs to lock the screen"
+msgstr "Gnom mora da zaključa ekran"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:836 js/ui/screenShield.js:1311
+msgid "Unable to lock"
+msgstr "Ne mogu da zaključam"
+
+#: js/ui/screenShield.js:837 js/ui/screenShield.js:1312
+msgid "Lock was blocked by an application"
+msgstr "Neki program je blokirao zaključavanje"
+
+#: js/ui/search.js:651
+msgid "Searching…"
+msgstr "Tražim…"
+
+#: js/ui/search.js:653
+msgid "No results."
+msgstr "Nema odgovarajućih rezultata."
+
+#: js/ui/search.js:777
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "Još %d"
+msgstr[1] "Još %d"
+msgstr[2] "Još %d"
+msgstr[3] "Još jedan"
+
+#: js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "Umnoži"
+
+#: js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "Ubaci"
+
+#: js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "Prikaži tekst"
+
+#: js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "Sakrij tekst"
+
+#: js/ui/shellMountOperation.js:315
+msgid "Password"
+msgstr "Lozinka"
+
+#: js/ui/shellMountOperation.js:336
+msgid "Remember Password"
+msgstr "Zapamti lozinku"
+
+#: js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "Pristupačnost"
+
+#: js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "Zumiraj"
+
+#: js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "Čitač ekrana"
+
+#: js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "Tastatura na ekranu"
+
+#: js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "Vizuelna upozorenja"
+
+#: js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "Lepljivi tasteri"
+
+#: js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "Spori tasteri"
+
+#: js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "Odskočni tasteri"
+
+#: js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "Tasteri miša"
+
+#: js/ui/status/accessibility.js:167
+msgid "High Contrast"
+msgstr "Veliki kontrast"
+
+#: js/ui/status/accessibility.js:202
+msgid "Large Text"
+msgstr "Veliki tekst"
+
+#: js/ui/status/bluetooth.js:48
+msgid "Bluetooth"
+msgstr "Blutut"
+
+#: js/ui/status/bluetooth.js:57 js/ui/status/network.js:616
+msgid "Bluetooth Settings"
+msgstr "Podešavanja Blututa"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:137
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d povezan"
+msgstr[1] "%d povezana"
+msgstr[2] "%d povezano"
+msgstr[3] "%d povezan"
+
+#: js/ui/status/bluetooth.js:139
+msgid "Off"
+msgstr "Isklj."
+
+#: js/ui/status/bluetooth.js:141
+msgid "On"
+msgstr "Uključeno"
+
+#: js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "Osvetljenost"
+
+#: js/ui/status/keyboard.js:783
+msgid "Keyboard"
+msgstr "Tastatura"
+
+#: js/ui/status/keyboard.js:806
+msgid "Show Keyboard Layout"
+msgstr "Pokaži raspored tastature"
+
+#: js/ui/status/location.js:89 js/ui/status/location.js:197
+msgid "Location Enabled"
+msgstr "Lokacija je omogućena"
+
+#: js/ui/status/location.js:90 js/ui/status/location.js:198
+msgid "Disable"
+msgstr "Isključi"
+
+#: js/ui/status/location.js:91
+msgid "Privacy Settings"
+msgstr "Podešavanja privatnosti"
+
+#: js/ui/status/location.js:196
+msgid "Location In Use"
+msgstr "Koristi lokaciju"
+
+#: js/ui/status/location.js:200
+msgid "Location Disabled"
+msgstr "Ne koristi lokaciju"
+
+#: js/ui/status/location.js:201
+msgid "Enable"
+msgstr "Uključi"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:388
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "Da li da „%s“ dam pristup vašem mestu?"
+
+#: js/ui/status/location.js:389
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Pristup mestu možete da izmenite u bilo koje vreme u podešavanjima "
+"privatnosti."
+
+#: js/ui/status/network.js:90
+msgid "<unknown>"
+msgstr "<nepoznato>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:443 js/ui/status/network.js:1316
+#, javascript-format
+msgid "%s Off"
+msgstr "%s isključeno"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:446
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s povezano"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:451
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s neupravljano"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s prekidanje veze u toku"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:461 js/ui/status/network.js:1308
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s povezivanje u toku"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:464
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s zahteva prijavu"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:472
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Nedostaje firmver za %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:476
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s nedostupno"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:479
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s povezivanje neuspešno"
+
+#: js/ui/status/network.js:495
+msgid "Wired Settings"
+msgstr "Podešavanja žičane veze"
+
+#: js/ui/status/network.js:537
+msgid "Mobile Broadband Settings"
+msgstr "Podešavanja mobilne širokopojasne veze"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:580 js/ui/status/network.js:1313
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s uređaj isključen"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:584
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s isključeno"
+
+#: js/ui/status/network.js:624
+msgid "Connect to Internet"
+msgstr "Poveži se na Internet"
+
+#: js/ui/status/network.js:822
+msgid "Airplane Mode is On"
+msgstr "Avionski režim rada je uključen"
+
+#: js/ui/status/network.js:823
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Bežična veza je isključena kada je uključen avionski režim rada."
+
+#: js/ui/status/network.js:824
+msgid "Turn Off Airplane Mode"
+msgstr "Isključi avionski režim rada"
+
+#: js/ui/status/network.js:833
+msgid "Wi-Fi is Off"
+msgstr "Bežična veza je isključena"
+
+#: js/ui/status/network.js:834
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Bežična veza treba biti uključena da biste se povezali na mrežu."
+
+#: js/ui/status/network.js:835
+msgid "Turn On Wi-Fi"
+msgstr "Uključi bežičnu vezu"
+
+#: js/ui/status/network.js:860
+msgid "Wi-Fi Networks"
+msgstr "Bežične mreže"
+
+#: js/ui/status/network.js:862
+msgid "Select a network"
+msgstr "Izaberite mrežu"
+
+#: js/ui/status/network.js:892
+msgid "No Networks"
+msgstr "Nema mreža"
+
+#: js/ui/status/network.js:913 js/ui/status/rfkill.js:115
+msgid "Use hardware switch to turn off"
+msgstr "Koristi fizički prekidač za isključivanje"
+
+#: js/ui/status/network.js:1179
+msgid "Select Network"
+msgstr "Izaberi mrežu"
+
+#: js/ui/status/network.js:1185
+msgid "Wi-Fi Settings"
+msgstr "Podešavanja bežične veze"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1304
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s hotspot uključen"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1319
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s nepovezano"
+
+#: js/ui/status/network.js:1419
+msgid "connecting…"
+msgstr "povezujem se…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1422
+msgid "authentication required"
+msgstr "potrebna je prijava"
+
+#: js/ui/status/network.js:1424
+msgid "connection failed"
+msgstr "povezivanje nije uspelo"
+
+#: js/ui/status/network.js:1478
+msgid "VPN Settings"
+msgstr "VPN podešavanja"
+
+#: js/ui/status/network.js:1491
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1501
+msgid "VPN Off"
+msgstr "Isključi VPN"
+
+#: js/ui/status/network.js:1565 js/ui/status/rfkill.js:93
+msgid "Network Settings"
+msgstr "Podešavanja mreže"
+
+#: js/ui/status/network.js:1596
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s žičana veza"
+msgstr[1] "%s žičane veze"
+msgstr[2] "%s žičanih veza"
+msgstr[3] "%s žičana veza"
+
+#: js/ui/status/network.js:1600
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s bežična veza"
+msgstr[1] "%s bežične veze"
+msgstr[2] "%s bežičnih veza"
+msgstr[3] "%s bežična veza"
+
+#: js/ui/status/network.js:1604
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s modemska veza"
+msgstr[1] "%s modemske veze"
+msgstr[2] "%s modemskih veza"
+msgstr[3] "%s modemska veza"
+
+#: js/ui/status/network.js:1738
+msgid "Connection failed"
+msgstr "Povezivanje nije uspelo"
+
+#: js/ui/status/network.js:1739
+msgid "Activation of network connection failed"
+msgstr "Aktiviranje mrežne veze nije uspelo"
+
+#: js/ui/status/nightLight.js:68
+msgid "Night Light Disabled"
+msgstr "Noćno svetlo ne radi"
+
+#: js/ui/status/nightLight.js:69
+msgid "Night Light On"
+msgstr "Noćno svetlo radi"
+
+#: js/ui/status/nightLight.js:70
+msgid "Resume"
+msgstr "Nastavi"
+
+#: js/ui/status/nightLight.js:71
+msgid "Disable Until Tomorrow"
+msgstr "Isključi do sutra"
+
+#: js/ui/status/power.js:61
+msgid "Power Settings"
+msgstr "Podešavanja napajanja"
+
+#: js/ui/status/power.js:77
+msgid "Fully Charged"
+msgstr "Potpuno puna"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:84 js/ui/status/power.js:90
+msgid "Estimating…"
+msgstr "Proračunavam…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:98
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "Do prazne: %d:%02d (%d %%)"
+
+# Kratak prevod da bi se videlo vreme i procenat napunjenosti u sistemskom meniju Gnomove školjke.
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:103
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "Do pune: %d:%02d (%d %%)"
+
+#: js/ui/status/power.js:131 js/ui/status/power.js:133
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:88
+msgid "Airplane Mode On"
+msgstr "Avio režim uključen"
+
+#: js/ui/status/system.js:228
+msgid "Switch User"
+msgstr "Promeni korisnika"
+
+#: js/ui/status/system.js:240
+msgid "Log Out"
+msgstr "Odjavi me"
+
+#: js/ui/status/system.js:252
+msgid "Account Settings"
+msgstr "Podešavanja naloga"
+
+#: js/ui/status/system.js:269
+msgid "Orientation Lock"
+msgstr "Katanac okrenutosti"
+
+#: js/ui/status/system.js:295
+msgid "Suspend"
+msgstr "Obustavi"
+
+#: js/ui/status/system.js:305
+msgid "Power Off"
+msgstr "Isključi"
+
+#: js/ui/status/thunderbolt.js:272
+msgid "Thunderbolt"
+msgstr "Tanderbolt"
+
+#. we are done
+#: js/ui/status/thunderbolt.js:330
+msgid "Unknown Thunderbolt device"
+msgstr "Nepoznat Tanderbold uređaj"
+
+#: js/ui/status/thunderbolt.js:331
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Novi uređaj je pronađen dok niste bili tu. Isključite uređaj i ponovo ga "
+"povežite da biste počeli da ga koristite."
+
+#: js/ui/status/thunderbolt.js:336
+msgid "Thunderbolt authorization error"
+msgstr "Greška u ovlašćivanju Tanderbolta"
+
+#: js/ui/status/thunderbolt.js:337
+#, javascript-format
+msgid "Could not authorize the thunderbolt device: %s"
+msgstr "Ne mogu da ovlastim tanderbolt uređaj: %s"
+
+#: js/ui/status/volume.js:128
+msgid "Volume changed"
+msgstr "Promena jačine zvuka"
+
+#: js/ui/status/volume.js:170
+msgid "Volume"
+msgstr "Jačina zvuka"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:21
+msgid "Mirror"
+msgstr "Preslikaj"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:26
+msgid "Join Displays"
+msgstr "Spoji ekrane"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:31
+msgid "External Only"
+msgstr "Samo spoljni"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:36
+msgid "Built-in Only"
+msgstr "Samo ugrađeni"
+
+#: js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "Prijavite se kao drugi korisnik"
+
+#: js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "Otključaj prozor"
+
+#: js/ui/viewSelector.js:190
+msgid "Applications"
+msgstr "Programi"
+
+#: js/ui/viewSelector.js:194
+msgid "Search"
+msgstr "Traži"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "„%s“ je spreman"
+
+#: js/ui/windowManager.js:72
+msgid "Do you want to keep these display settings?"
+msgstr "Da li želite da zadržite ova podešavanja ekrana?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "Vrati podešavanja"
+
+#: js/ui/windowManager.js:87
+msgid "Keep Changes"
+msgstr "Zadrži izmene"
+
+#: js/ui/windowManager.js:105
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Izmene podešavanja će biti vraćene za %d sekundu"
+msgstr[1] "Izmene podešavanja će biti vraćene za %d sekunde"
+msgstr[2] "Izmene podešavanja će biti vraćene za %d sekundi"
+msgstr[3] "Izmene podešavanja će biti vraćene za %d sekundu"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:660
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "Umanji"
+
+#: js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "Poništi uvećanje"
+
+#: js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "Uvećaj"
+
+#: js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "Premesti"
+
+#: js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "Promeni veličinu"
+
+#: js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "Premesti traku naslova na ekran"
+
+#: js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "Drži iznad ostalih"
+
+#: js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "Prikaži na svim radnim prostorima"
+
+#: js/ui/windowMenu.js:103
+msgid "Move to Workspace Left"
+msgstr "Premesti na levi radni prostor"
+
+#: js/ui/windowMenu.js:108
+msgid "Move to Workspace Right"
+msgstr "Premesti na desni radni prostor"
+
+#: js/ui/windowMenu.js:113
+msgid "Move to Workspace Up"
+msgstr "Premesti na radni prostor gore"
+
+#: js/ui/windowMenu.js:118
+msgid "Move to Workspace Down"
+msgstr "Premesti na radni prostor dole"
+
+#: js/ui/windowMenu.js:134
+msgid "Move to Monitor Up"
+msgstr "Premesti na gornji radni prostor"
+
+#: js/ui/windowMenu.js:140
+msgid "Move to Monitor Down"
+msgstr "Premesti na donji radni prostor"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Left"
+msgstr "Premesti ekran ulevo"
+
+#: js/ui/windowMenu.js:152
+msgid "Move to Monitor Right"
+msgstr "Premesti ekran udesno"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Kalendar Evolucije"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: src/main.c:432
+msgid "Print version"
+msgstr "Ispisuje izdanje"
+
+#: src/main.c:438
+msgid "Mode used by GDM for login screen"
+msgstr "Koji režim koristi GDM za ekran prijavljivanja"
+
+#: src/main.c:444
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Koristi naročit režim, npr. „gdm“ za ekran prijavljivanja"
+
+#: src/main.c:450
+msgid "List possible modes"
+msgstr "Ispisuje moguće režime"
+
+#: src/shell-app.c:270
+msgctxt "program"
+msgid "Unknown"
+msgstr "Nepoznat"
+
+#: src/shell-app.c:511
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Nisam uspeo da pokrenem „%s“"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "Lozinke se ne podudaraju."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "Lozinka ne može biti prazna"
+
+#: src/shell-polkit-authentication-agent.c:353
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Korisnik je odbacio prozorče za potvrđivanje identiteta"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1873
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u izlaz"
+msgstr[1] "%u izlaza"
+msgstr[2] "%u izlaza"
+msgstr[3] "%u izlaz"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1883
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ulaz"
+msgstr[1] "%u ulaza"
+msgstr[2] "%u ulaza"
+msgstr[3] "%u ulaz"
+
+#: subprojects/gvc/gvc-mixer-control.c:2738
+msgid "System Sounds"
+msgstr "Sistemski zvuci"
+
+#~ msgctxt "search-result"
+#~ msgid "Power off"
+#~ msgstr "Isključi"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Odjavi me"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Promeni korisnika"
+
+#~ msgid "Hide tray"
+#~ msgstr "Sakrij kasetu"
+
+#~ msgid "Status Icons"
+#~ msgstr "Ikonice stanja"
+
+#~ msgid "Events"
+#~ msgstr "Događaji"
+
+#~ msgid "Notifications"
+#~ msgstr "Obaveštenja"
+
+#~ msgid "Clear section"
+#~ msgstr "Očisti odeljak"
+
+#~ msgid "Media"
+#~ msgstr "Mediji"
+
+#~ msgid "GNOME Shell Extension Preferences"
+#~ msgstr "Postavke proširenja Gnomove školjke"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Preusmeravanje potvrđivanja identiteta vebom"
+
+#~ msgid "Not In Use"
+#~ msgstr "Nije u upotrebi"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Prikazuje dan u nedelji u kalendaru"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Ako je izabrano, prikazuje ISO dan u nedelji u kalendaru."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Koristi kao Internet vezu"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "„%s“ zahteva pristup vašem mestu."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Gnomova školjka (vajland sastavljač)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d priključeni uređaj"
+#~ msgstr[1] "%d priključena uređaja"
+#~ msgstr[2] "%d priključenih uređaja"
+#~ msgstr[3] "%d priključeni uređaj"
+
+#~ msgid "Authentication required"
+#~ msgstr "Potrebno je potvrđivanje identiteta"
+
+#~ msgid "UPS"
+#~ msgstr "Rezervno napajanje"
+
+#~ msgid "Battery"
+#~ msgstr "Baterija"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Režim u avionu"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Prikažite fioku poruka"
+
+#~ msgid "Captive Portal"
+#~ msgstr "Portal stopke"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Ned"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Pon"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "Uto"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Sre"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Čet"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Pet"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Sub"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Ništa planirano"
+
+#~ msgid "Today"
+#~ msgstr "Danas"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Sutra"
+
+#~ msgid "This week"
+#~ msgstr "Ove nedelje"
+
+#~ msgid "Next week"
+#~ msgstr "Sledeće nedelje"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Uklonjivi uređaji"
+
+#~ msgid "Eject"
+#~ msgstr "Izbaci"
+
+#~ msgid "Invitation"
+#~ msgstr "Pozivnica"
+
+#~ msgid "Call"
+#~ msgstr "Poziv"
+
+#~ msgid "File Transfer"
+#~ msgstr "Prenos datoteka"
+
+#~ msgid "Chat"
+#~ msgstr "Ćaskanje"
+
+#~ msgid "Unmute"
+#~ msgstr "Uključi ton"
+
+#~ msgid "Mute"
+#~ msgstr "Utišaj"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Pozivnica za „%s“"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s vas poziva da se pridružite „%s“"
+
+#~ msgid "Decline"
+#~ msgstr "Odbij"
+
+#~ msgid "Accept"
+#~ msgstr "Prihvati"
+
+#~ msgid "Video call from %s"
+#~ msgstr "%s vam je uputio video poziv"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s vas zove"
+
+#~ msgid "Answer"
+#~ msgstr "Odgovori"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s vam šalje „%s“"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s traži odobrenje da vidi kada ste na vezi"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Potvrđivanje identiteta nije uspelo"
+
+#~ msgid "Encryption error"
+#~ msgstr "Greška šifrovanja"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Uverenje nije prosleđeno"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Uverenje nije poverljivo"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Uverenje je isteklo"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Uverenje nije aktivirano"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Naziv domaćina uverenja se ne poklapa"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Otisak uverenja se ne poklapa"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Uverenje je samopotpisano"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Stanje je postavljeno na „nepovezan“"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Uverenje nije ispravno"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Veza je odbijena"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Veza ne može biti uspostavljena"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Veza je izgubljena"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Ovaj nalog je već povezan sa serverom"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Veza je zamenjena novom vezom koristeći isti resurs"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Ovaj nalog već postoji na serveru"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Server je trenutno previše zauzet da bi rukovao vezom"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Uverenje je opozvano"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Uverenje koristi nesiguran algoritam šifrovanja ili je kriptografski slabo"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Dužina uverenja servera, ili dubina lanca uverenja servera, premašuje "
+#~ "granice koje je zadala biblioteka šifrovanja"
+
+#~ msgid "Internal error"
+#~ msgstr "Unutrašnja greška"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "Ne mogu da se povežem sa „%s“"
+
+#~ msgid "View account"
+#~ msgstr "Vidi nalog"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Otvori kalendar"
+
+#~ msgid "Open"
+#~ msgstr "Otvori"
+
+#~ msgid "Remove"
+#~ msgstr "Ukloni"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Očisti poruke"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Postavke obaveštenja"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Izbornik fioke"
+
+#~ msgid "No Messages"
+#~ msgstr "Nema poruka"
+
+#~ msgid "Message Tray"
+#~ msgstr "Fioka poruka"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Najviši nivo tačnosti mesta."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "Podešava najveći nivo tačnosti mesta koju programi mogu da koriste. "
+#~ "Ispravne mogućnosti su „off“ (isključuje praćenje mesta), "
+#~ "„country“ (država), „city“ (grad), „neighborhood“ (komšiluk), "
+#~ "„street“ (ulica), i „exact“ (tačno — obično zahteva GPS prijemnik). "
+#~ "Imajte na umu da će ovo odlučiti šta će „Geo indicija“ dozvoliti "
+#~ "programima samo da vide i da mogu pronaći korisnikovo mesto na svoju ruku "
+#~ "koristeći izvorišta mreže (mada sa tačnošću na nivou ulice u najboljem "
+#~ "slučaju)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Raspored dugmića na traci naslova"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Ovaj ključ prevazilazi ključ u „org.gnome.desktop.wm.preferences“ kada "
+#~ "pokreće Gnomovu školjku."
+
+#~ msgid "Extension"
+#~ msgstr "Proširenje"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Izaberite proširenje za podešavanje koristeći prozorče za izbor."
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Spisak kategorija koje treba da budu prikazane kao fascikle"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Naziv svake kategorije na spisku će biti predstavljen kao fascikla u "
+#~ "pregledu programa, umesto da bude prikazan neposredno u glavnom pregledu."
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, u <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b>. <b>%B</b> u <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%d</b>. <b>%B</b> <b>%Y</b>. u <b>%H:%M</b>"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Zahtev za ovlašćenje sa „%s“"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Uređaj „%s“ želi da se upari sa ovim računarom"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Uređaj „%s“ želi pristup usluzi „%s“"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Odobri samo ovaj put"
+
+#~ msgid "Reject"
+#~ msgstr "Odbij"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Uparujem potvrdu za „%s“"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Molim potvrdite da li se propusni ključ „%06d“ podudara sa onim na uređaju."
+
+#~ msgid "Matches"
+#~ msgstr "Podudara se"
+
+#~ msgid "Does not match"
+#~ msgstr "Ne podudara se"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Zahtev za uparivanje za „%s“"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Molim unesite PIN naznačen na uređaju."
+
+#~ msgid "OK"
+#~ msgstr "U redu"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Izvinite, danas za vas nema mudrosti:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "Prorok je rekao %s"
diff --git a/po/sv.po b/po/sv.po
new file mode 100644
index 0000000..efdbaa9
--- /dev/null
+++ b/po/sv.po
@@ -0,0 +1,3397 @@
+# Swedish translation for gnome-shell.
+# Copyright © 2009-2023 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+# Daniel Nylander <po@danielnylander.se>, 2009, 2010, 2011, 2012.
+# Marcus Lundblad <ml@update.uu.se>, 2014.
+# Åke Engelbrektson <eson57@gmail.com>, 2014.
+# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023.
+# Sebastian Rasmussen <sebras@gmail.com>, 2015, 2016.
+# Luna Jernberg <droidbittin@gmail.com>, 2021, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 00:30+0000\n"
+"PO-Revision-Date: 2023-02-14 06:13+0100\n"
+"Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.2.2\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Programstartare"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Aktivera favoritprogram 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Aktivera favoritprogram 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Aktivera favoritprogram 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Aktivera favoritprogram 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Aktivera favoritprogram 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Aktivera favoritprogram 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Aktivera favoritprogram 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Aktivera favoritprogram 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Aktivera favoritprogram 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Skärmbilder"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Ta en skärmbild interaktivt"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Ta en skärmbild"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Ta en skärmbild av ett fönster"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Spela in skärmen interaktivt"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "System"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Visa aviseringslistan"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Fokusera den aktiva aviseringen"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Visa översiktsvyn"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Visa alla program"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Öppna programmenyn"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME-skal"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Fönsterhantering och uppstart av program"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Aktivera interna verktyg användbara för utvecklare och testare från Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Tillåter åtkomst till interna verktyg för felsökning och övervakning med "
+"dialogen Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "UUID:n för tillägg att aktivera"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Tillägg för GNOME-skal har en UUID-egenskap; denna nyckel listar tillägg som "
+"ska läsas in. De tillägg som vill bli inlästa måste finnas i denna lista. Du "
+"kan även ändra denna lista med D-Bus-metoderna EnableExtension och "
+"DisableExtension på org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "UUID:n för tillägg att tvinga att inaktiveras"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Tillägg för GNOME-skal har en UUID-egenskap; denna nyckel listar tillägg som "
+"ska inaktiveras, även om de lästs in som del av det aktuella läget. Du kan "
+"även ändra denna lista med D-Bus-metoderna EnableExtension och "
+"DisableExtension på org.gnome.Shell. Denna nyckel har företräde över "
+"inställningen ”enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Inaktivera användartillägg"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Inaktivera alla tillägg som användaren har aktiverat, utan att påverka "
+"inställningen ”enabled-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Slår av valideringen av versionskompatibilitet för tillägg"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME-skalet kommer endast att läsa in tillägg som säger sig stödja den "
+"nuvarande körande versionen. Att slå på denna inställning medför att den "
+"kontrollen slås av och alla tillägg kommer att försöka läsas in, oavsett "
+"vilken version de säger sig stödja."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Lista över skrivbordsfil-id för favoritprogram"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Programmen som motsvarar dessa identifierare kommer att visas i "
+"favoritområdet."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Historik för kommandodialog (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Historik för spegeldialogen"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Visa alltid menyalternativet ”Logga ut” i användarmenyn."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Denna nyckel åsidosätter det automatiska gömmandet av menyalternativet "
+"”Logga ut” i situationer med endast en användare och en session."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Huruvida lösenord för krypterade enheter och fjärrenheter skall memoreras"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Skalet kommer att fråga efter ett lösenord när en krypterad enhet eller en "
+"fjärrenhet monteras. Om lösenordet kan sparas för framtida bruk kommer en "
+"”Kom ihåg lösenord”-kryssruta att visas. Denna nyckel sätter standardvärdet "
+"för kryssrutan."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Den senast valda icke-standardströmprofilen"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Vissa system stöder mer än två strömprofiler. För att fortfarande stödja "
+"växling mellan två profiler registrerar denna nyckel den senast valda icke-"
+"standardprofilen."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Den senaste versionen som ”Välkommen till GNOME”-dialogen visades för"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Denna nyckel avgör för vilken version som ”Välkommen till GNOME”-dialogen "
+"senast visades. En tom sträng motsvarar den äldsta möjliga versionen, och "
+"ett enormt tal kommer motsvara versioner som inte finns ännu. Detta enorma "
+"tal kan användas för att inaktivera dialogrutan."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Layout för programväljaren"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Layout för programväljaren. Varje post i vektorn är en sida. Sidor lagras i "
+"ordningen de dyker upp i GNOME-skalet. Varje sida innehåller ett par "
+"”program-ID” → ”data”. För närvarande lagras följande värde som ”data”: • "
+"”position”: positionen för programikonen på sidan"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Snabbtangent för att öppna programmenyn"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Snabbtangent för att öppna programmenyn."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Snabbtangent för att växla mellan översiktstillstånd"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Snabbtangent för att växla mellan session, fönsterväljare och programrutnät"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Snabbtangent för att växla mellan programrutnät, fönsterväljare och session"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Snabbtangent för att öppna ”Visa program”-vyn"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Snabbtangent för att öppna ”Visa program”-vyn i översiktsvyn."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Snabbtangent för att öppna översiktsvyn"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Snabbtangent för att öppna översiktsvyn."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Snabbtangent för att växla synligheten för aviseringslistan"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Snabbtangent för att växla synligheten för aviseringslistan."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Snabbtangent för att fokusera på den aktiva aviseringen"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Snabbtangent för att fokusera på den aktiva aviseringen."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Växla till program 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Växla till program 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Växla till program 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Växla till program 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Växla till program 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Växla till program 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Växla till program 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Växla till program 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Växla till program 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Begränsa väljare till aktuell arbetsyta."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Om satt till true (sant), visas endast program som har fönster på den "
+"aktuella arbetsytan i väljaren. I annat fall inkluderas alla program."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Programikonsläget."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Ställer in hur fönster visas i väljaren. Giltiga alternativ är ”thumbnail-"
+"only” (visar en miniatyrbild av fönstret), ”app-icon-only” (visar endast "
+"programikonen) eller ”both” (visar båda)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Om satt till \"true\", visas endast fönster från den aktuella arbetsytan i "
+"väljaren. I annat fall inkluderas alla fönster."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Platser"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Platserna att visa i världsklockor"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Automatisk plats"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Huruvida den aktuella platsen ska hämtas eller ej"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Plats"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Platsen för vilken en väderprognos ska visas"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Fäst modal dialog till föräldrafönstret"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Denna nyckel åsidosätter nyckeln i org.gnome.mutter när GNOME-skalet körs."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Slå på kantdockning när fönster släpps på skärmkanter"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Arbetsytor hanteras dynamiskt"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Arbetsytor endast på primär skärm"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Fördröj fokusändringar i musläge tills pekare slutar röra sig"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Nätverksinloggning"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Något har gått fel"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Vi är ledsna, men ett problem har uppstått: inställningarna för detta "
+"tillägg kan inte visas. Vi rekommenderar att du rapporterar problemet till "
+"tilläggets skapare."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Tekniska detaljer"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Webbsida"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Besök webbsida för tillägg"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Lösenord"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Välj session"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Inte listad?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(till exempel användare eller %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Användarnamn"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Inloggningsfönster"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Autentiseringsfel"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(eller dra fingret längs läsaren)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(eller placera fingret på läsaren)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Stäng av"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "stäng av;stäng ner;stäng;halt;stopp"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Starta om"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "starta om;omstart;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Lås skärm"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lås skärm"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Logga ut"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "utloggning;logga ut"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Vänteläge"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "vänteläge;vila"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Växla användare"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "växla användare"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lås orientering;lås riktning;lås upp orientering;lås upp riktning;skärm;"
+"rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Ta en skärmbild"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr "skärmbild;skärmdump;skärminspelning;klipp;fångst;inspelning"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Lås upp skärmrotation"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Lås skärmrotation"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Kommandot hittades inte"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Kunde inte tolka kommando:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Körning av ”%s” misslyckades:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Just nu"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d minut sedan"
+msgstr[1] "%d minuter sedan"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d timme sedan"
+msgstr[1] "%d timmar sedan"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "I går"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d dag sedan"
+msgstr[1] "%d dagar sedan"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d vecka sedan"
+msgstr[1] "%d veckor sedan"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d månad sedan"
+msgstr[1] "%d månader sedan"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d år sedan"
+msgstr[1] "%d år sedan"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "I går, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "I går, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %I∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Surfzonsinloggning"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Din anslutning till denna surfzonsinloggning är inte säker. Lösenord eller "
+"annan information som du matar in på denna sida kan visas av andra personer "
+"i närheten."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Neka åtkomst"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Bevilja åtkomst"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Namnlös mapp"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s har fästs på snabbstartspanelen."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s har lösgjorts från snabbstartspanelen."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Öppna fönster"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Nytt fönster"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Visa detaljer"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Avsluta"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Lösgör"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Fäst till snabbstartspanel"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Kör med integrerat grafikkort"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Kör med diskret grafikkort"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Välj ljudenhet"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Ljudinställningar"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Hörlurar"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Mikrofonlur"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Ändra bakgrund…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Skärminställningar"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Inställningar"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "M"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "O"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "T"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "F"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "L"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Föregående månad"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Nästa månad"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Vecka %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Inga aviseringar"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Stör ej"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Töm"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "”%s” svarar inte."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Du kan välja att vänta en kort stund på att det ska fortsätta eller tvinga "
+"programmet att helt avslutas."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Tvinga avslut"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Vänta"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Extern disk ansluten"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Extern disk frånkopplad"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Kunde inte låsa upp volym"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Den installerade udisks-versionen stöder inte PIM-inställningen"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Öppna med %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Alternativt kan du ansluta genom att trycka på ”WPS”-knappen på din router."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Anslut"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Nyckel"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Lösenord för privat nyckel"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Identitet"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Tjänst"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Autentisering krävs"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Lösenord eller krypteringsnycklar krävs för att komma åt det trådlösa "
+"nätverket ”%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Trådbunden 802.1X-autentisering"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Nätverksnamn"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL-autentisering"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN-kod krävs"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "PIN-koden krävs för den mobila bredbandsenheten"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Ett lösenord krävs för att ansluta till ”%s”."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Nätverkshanterare"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN-lösenord"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Autentisering krävs"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Administratör"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Autentisera"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Tyvärr, det fungerade inte. Försök igen."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s är nu känd som %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Fönster"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Visa program"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Snabbstartspanel"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "I dag"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "I morgon"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Hela dagen"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%-d/%-m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Inga händelser"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Lägg till världsklockor…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Världsklockor"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Läser in…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Anslut till nätet för väderinformation"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Väderinformation är för närvarande inte tillgänglig"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Väder"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Välj väderplats…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Logga ut %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Logga ut"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s kommer att loggas ut automatiskt om %d sekund."
+msgstr[1] "%s kommer att loggas ut automatiskt om %d sekunder."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Du kommer att loggas ut automatiskt om %d sekund."
+msgstr[1] "Du kommer att loggas ut automatiskt om %d sekunder."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Logga ut"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Stäng av"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Installera uppdateringar & Stäng av"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Systemet kommer att stängas av automatiskt om %d sekund."
+msgstr[1] "Systemet kommer att stängas av automatiskt om %d sekunder."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Installera väntande programvaruuppdateringar"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Stäng av"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Starta om"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Installera uppdateringar & starta om"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Systemet kommer att startas om automatiskt om %d sekund."
+msgstr[1] "Systemet kommer att startas om automatiskt om %d sekunder."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Starta om"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Starta om och installera uppdateringar"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Systemet kommer att startas om automatiskt och installera uppdateringar om "
+"%d sekund."
+msgstr[1] ""
+"Systemet kommer att startas om automatiskt och installera uppdateringar om "
+"%d sekunder."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Starta om &amp; installera"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Installera &amp; stäng av"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Stäng av efter att uppdateringarna har installerats"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Starta om och installera uppgradering"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s kommer att installeras efter omstart. En uppgraderingsinstallation kan "
+"ta lång tid: försäkra dig om att du gjort säkerhetskopior och att datorn är "
+"ikopplad."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Lågt batteri: anslut till en fast strömkälla innan uppdateringar installeras."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Några program är upptagna eller innehåller icke sparat arbete"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Andra användare är inloggade"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Uppstartsalternativ"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (fjärransluten)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (konsol)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Installera"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Installera tillägg"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Hämta och installera ”%s” från extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Uppdateringar för tillägg finns tillgängliga"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Uppdateringar för tillägg är redo att installeras."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Tillåt förhindrande av kortkommandon"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Programmet %s vill förhindra kortkommandon"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Ett program vill förhindra kortkommandon"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Du kan återställa kortkommandon genom att trycka %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Neka"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Tillåt"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Tröga tangenter aktiverade"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Tröga tangenter inaktiverade"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Du höll precis ned skifttangenten under 8 sekunder. Detta är kortkommandot "
+"för funktionen tröga tangenter, som påverkar hur ditt tangentbord fungerar."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Klistriga tangenter aktiverade"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Klistriga tangenter inaktiverade"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Du tryckte precis ned skifttangenten 5 gånger i följd. Detta är "
+"kortkommandot för funktionen klistriga tangenter, som påverkar hur ditt "
+"tangentbord fungerar."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Du tryckte precis ned två tangenter samtidigt, eller tryckte ned "
+"skifttangenten 5 gånger i följd. Detta stänger av funktionen klistriga "
+"tangenter vilket påverkar hur ditt tangentbord fungerar."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Lämna påslagen"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Slå på"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Stäng av"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Lämna avstängd"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Re­gion- & språkinställningar"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Inga tillägg installerade"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s har inte skickat ut några fel."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Dölj fel"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Visa fel"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Aktiverad"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Inaktiverad"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Fel"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Utanför datumintervallet"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Hämtar"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Visa källa"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Webbsida"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Systemet försattes i osäkert läge"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Program har nu obegränsad åtkomst"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Ångra"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Inloggad som en privilegierad användare"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Att köra en session som en privilegierad användare bör undvikas av "
+"säkerhetsskäl. Om möjligt bör du logga in som en normal användare."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Skärmlås inaktiverat"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Skärmlås kräver GNOME:s displayhanterare."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Systeminformation"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Okänd artist"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Okänd titel"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Skriv för att söka"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Program"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Översikt"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Nytt kortkommando…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Programdefinierad"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Visa hjälp på skärmen"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Växla skärm"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Tilldela tangenttryckning"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Färdig"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Redigera…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Ingen"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Tryck på en knapp för att konfigurera"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Tryck Esc för att avsluta"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Tryck på valfri tangent för att avsluta"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Aktiviteter"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "System"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Systemrad"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Kör ett kommando"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Tryck Esc för att stänga"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Omstart är inte tillgänglig i Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Startar om…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME behöver låsa skärmen"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Kunde inte låsa"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Låsning hindrades av ett program"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Markering"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Områdesmarkering"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Skärm"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Skärmval"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Fönster"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Fönsterval"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Skärmbild / skärminspelning"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Visa pekare"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Skärminspelningar"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Skärminspelning från %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Skärmbild"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Skärminspelning inspelad"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Klicka här för att se videon."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Visa i Filer"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Skärmbild från %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Skärmbild tagen"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Du kan klistra in bilden från urklipp."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Skärmbild tagen"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Söker…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Inga sökträffar."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d till"
+msgstr[1] "%d till"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Sök"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopiera"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Klistra in"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Visa text"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Dölj text"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock är på."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Dold volym"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows-systemvolym"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Använder nyckelfiler"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"För att låsa upp en volym som använder nyckelfiler, använd verktyget <i>%s</"
+"i> i stället."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM-nummer"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Kom ihåg lösenord"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Lås upp"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Öppna %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM måste vara ett tal eller tomt."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Kunde inte starta %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Kunde inte hitta programmet %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Hjälpmedel"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Hög kontrast"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Zooma"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Skärmläsare"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Skärmtangentbord"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Visuella larm"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Klistriga tangenter"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Tröga tangenter"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Studsande tangenter"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Mustangenter"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Stor text"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Automatisk rotation"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Ljusstyrka"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Mörkt läge"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Enkelklick"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Dubbelklick"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Dragning"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Sekundärklick"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Uppehållsklick"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Tangentbord"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Visa tangentbordslayout"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Tillåt platsåtkomst"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Programmet %s vill ha tillgång till din plats"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Platsåtkomst kan ändras när som helst från sekretessinställningarna."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<okänd>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Koppla från %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Anslut till %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Surfzon för %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "VPN-inställningar"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Alla nätverk"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Trådbundna anslutningar"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Trådbundna inställningar"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth-delningar"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Inställningar för Bluetooth"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Mobilanslutningar"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Inställningar för mobilt bredband"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Anslutningen misslyckades"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Aktivering av nätverksanslutning misslyckades"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Nattbelysning"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Prestanda"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Balanserad"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Strömsparare"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Strömprofiler"
+
+# https://gitlab.gnome.org/GNOME/gnome-shell/-/commit/6d0c2ae69772981537613ab5bd7bd706e0445855 This is specifically for stopping the screenshot UI screencasts for now.
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Stoppa skärminspelning"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Stoppa skärmdelning"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Flygplansläge"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Ta skärmbild"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Avstängningsmeny"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Vänteläge"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Starta om…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Stäng av…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Logga ut…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Växla användare…"
+
+# Accessible för activateLockScreen
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Lås skärm"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Okänd Thunderbolt-enhet"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"En ny enhet har upptäckts medan du var borta. Koppla från och anslut enheten "
+"igen för att börja använda den."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Ej auktoriserad Thunderbolt-enhet"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+"En ny enhet har upptäckts och behöver auktoriseras av en administratör."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt-auktoriseringsfel"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Kunde inte auktorisera Thunderbolt-enheten: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Volymen ändrades"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Volym"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Ljudutgång"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Ljudingång"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Spegla"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Slå ihop skärmar"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Extern skärm"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Inbyggd skärm"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Svep uppåt för att låsa upp"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Klicka eller tryck på en tangent för att låsa upp"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Lås upp fönster"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Logga in som en annan användare"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Välkommen till GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Om du vill lära dig grunderna, ta en titt på rundturen."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Nej tack"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Ta rundturen"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "”%s” är redo"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Behåll dessa inställningar?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Återställ inställningar"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Behåll ändringar"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Inställningarna kommer återställas om %d sekund"
+msgstr[1] "Inställningarna kommer återställas om %d sekunder"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Dölj"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Återställ"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Maximera"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Flytta"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Ändra storlek"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Flytta namnlist på skärm"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Alltid överst"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Alltid på synlig arbetsyta"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Flytta till arbetsyta åt vänster"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Flytta till arbetsyta åt höger"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Flytta till arbetsyta ovan"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Flytta till arbetsyta nedan"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Flytta till skärm uppåt"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Flytta till skärm nedåt"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Flytta till skärm åt vänster"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Flytta till skärm åt höger"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Stäng"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution-kalender"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Skriv ut version"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Läge som används av GDM för inloggningsskärmen"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Använd ett specifikt läge, t.ex. ”gdm” för inloggningsskärm"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Lista möjliga lägen"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Okänt"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Misslyckades med att starta ”%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Lösenorden stämmer inte överens."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Lösenordet får inte vara blankt"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Autentiseringsdialogen stängdes av användaren"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Tillägg"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Hantera dina GNOME-tillägg"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME-projektet"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Tillägg hanterar uppdatering av tillägg, konfiguration av "
+"inställningar för tillägg samt att ta bort eller inaktivera oönskade tillägg."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Konfigurera tillägg för GNOME-skal"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Inga träffar"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Ta bort ”%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Om du tar bort tillägget kommer du att behöva återvända för att hämta det om "
+"du vill aktivera det igen"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Ta bort"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
+"Luna Jernberg <droidbittin@gmail.com>\n"
+"\n"
+"Skicka synpunkter på översättningen till\n"
+"<tp-sv@listor.tp-sv.se>."
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d inlägg kommer att uppdateras vid nästa inloggning."
+msgstr[1] "%d inlägg kommer att uppdateras vid nästa inloggning."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Tillägget är inkompatibelt med den aktuella GNOME-versionen"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Tillägget hade ett fel"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Tillägget kan uppdateras"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Webbplats"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Ta bort…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Hjälp"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Om tillägg"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Tillägg kan orsaka prestanda- och stabilitetsproblem. Inaktivera tillägg om "
+"du stöter på problem med ditt system."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Manuellt installerade"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"För att hitta och lägga till tillägg, besök <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Inbyggda"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Inga installerade tillägg"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Vi är ledsna, men det var inte möjligt att erhålla listan över installerade "
+"tillägg. Säkerställ att du är inloggad i GNOME och försök igen."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Uppdateringar för tillägg är redo"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Det nya tillägget skapades i %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Namn bör vara en väldigt kort (helst beskrivande) sträng.\n"
+"Exempel: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Namn"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Beskrivning är en ensam mening som förklarar vad ditt tillägg gör.\n"
+"Exempel: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Beskrivning"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID är en globalt unik identifierare för ditt tillägg.\n"
+"Detta ska vara i formatet av en e-postadress\n"
+"(clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Välj en av de tillgängliga mallarna:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Mall"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Det nya tilläggets unika identifierare"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAMN"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Det användarsynliga namnet på det nya tillägget"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "BESKRIVNING"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "En kort beskrivning av vad tillägget gör"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MALL"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Mallen att använda för det nya tillägget"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Ange tilläggsinformation interaktivt"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Skapa ett nytt tillägg"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Okända argument"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, namn och beskrivning krävs"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Tillägget ”%s” finns inte\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Inaktivera ett tillägg"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Inget UUID angivet"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Mer än ett UUID angivet"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Aktivera ett tillägg"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Misslyckades med att ansluta till GNOME-skalet\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Tillägget ”%s” finns inte\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Visa info för tillägg"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Skriv över ett befintligt tillägg"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "TILLÄGGSBUNT"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Installera en tilläggsbunt"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Ingen tilläggsbunt angiven"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Mer än en tilläggsbunt angiven"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Visa användarinstallerade tillägg"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Visa systeminstallerade tillägg"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Visa aktiverade tillägg"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Visa inaktiverade tillägg"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Visa tillägg med inställningar"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Visa tillägg med uppdateringar"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Skriv ut detaljer för tillägg"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Lista installerade tillägg"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FIL"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Ytterligare källa att inkludera i bunten"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Ett GSettings-schema som ska inkluderas"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "KATALOG"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Katalogen där översättningar hittas"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMÄN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Gettext-domänen att använda för översättningar"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Skriv över ett befintligt paket"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Katalogen där paketet ska skapas"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "KÄLLKATALOG"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Skapa en tilläggsbunt"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Mer än en källkatalog angiven"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Tillägget ”%s” har inga inställningar\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Misslyckades med att öppna inställningar för tillägget ”%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Öppnar inställningar för tillägg"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Återställ ett tillägg"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Det går inte att avinstallera systemtillägg\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Misslyckades med att avinstallera ”%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Avinstallera ett tillägg"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Skriv inte ut felmeddelanden"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Sökväg"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Ursprunglig upphovsman"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Version"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Tillstånd"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "”version” tar inga argument"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Användning:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Skriv ut versionsinformation och avsluta."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "KOMMANDO"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGUMENT…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Kommandon:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Skriv ut hjälp"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Aktivera tillägg"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Inaktivera tillägg"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Återställ tillägg"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Avinstallera tillägg"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Lista tillägg"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Visa info för tillägg"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Öppna inställningar för tillägg"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Skapa tillägg"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Paketera tillägg"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Installera tilläggsbunt"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Använd ”%s” för att få detaljerad hjälp.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Enkel"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Ett tomt tillägg"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Indikator"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Lägg till en ikon i systemraden"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u utgång"
+msgstr[1] "%u utgångar"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ingång"
+msgstr[1] "%u ingångar"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Systemljud"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Huruvida standardadaptern för Bluetooth har enheter associerade med sig"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Skalet kommer bara att visa ett Bluetooth-menyalternativ om en Bluetooth-"
+#~ "adapter är strömsatt, eller om enheter ställts in som associerats med "
+#~ "standardadaptern. Detta kommer att återställas om standardadaptern "
+#~ "någonsin ses utan att ha enheter associerade med sig."
+
+#~ msgid "Log Out"
+#~ msgstr "Logga ut"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Anslut till internet"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Flygplansläge är på"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Trådlöst nätverk är avslaget när flygplansläge är på."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Slå av flygplansläge"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Trådlöst nätverk är av"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr ""
+#~ "Trådlöst nätverk behöver slås på för att kunna ansluta till ett nätverk."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Slå på trådlöst nätverk"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Trådlösa nätverk"
+
+#~ msgid "Select a network"
+#~ msgstr "Välj ett nätverk"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Använd hårdvarubrytare för att slå av"
+
+#~ msgid "Select Network"
+#~ msgstr "Välj nätverk"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Inställningar för trådlösa nätverk"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN avslaget"
+
+#~ msgid "Network Settings"
+#~ msgstr "Nätverksinställningar"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s trådlös anslutning"
+#~ msgstr[1] "%s trådlösa anslutningar"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s avslagen"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s ansluten"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s ohanterad"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s ansluter"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s kräver autentisering"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Fast programvara saknas för %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s ej tillgänglig"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "Anslutning till %s misslyckades"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "Hårdvara för %s inaktiverad"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s inaktiverad"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s ej ansluten"
+
+#~ msgid "unknown"
+#~ msgstr "okänd"
+
+#~ msgid "activating…"
+#~ msgstr "aktiverar…"
+
+#~ msgid "connecting…"
+#~ msgstr "ansluter…"
+
+#~ msgid "authentication required"
+#~ msgstr "autentisering krävs"
+
+#~ msgid "connection failed"
+#~ msgstr "anslutningen misslyckades"
+
+#~ msgid "Power Settings"
+#~ msgstr "Ströminställningar"
+
+#~ msgid "Lock"
+#~ msgstr "Lås"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Stäng av / Logga ut"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d ansluten"
+#~ msgstr[1] "%d anslutna"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth på"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth av"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Plats aktiverad"
+
+#~ msgid "Disable"
+#~ msgstr "Inaktivera"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Sekretessinställningar"
+
+#~ msgid "Location In Use"
+#~ msgstr "Plats används"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Plats inaktiverad"
+
+#~ msgid "Enable"
+#~ msgstr "Aktivera"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Nattbelysning inaktiverad"
+
+#~ msgid "Resume"
+#~ msgstr "Återuppta"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Inaktivera till imorgon"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Fulladdad"
+
+#~ msgid "Not Charging"
+#~ msgstr "Laddar inte"
+
+#~ msgid "Estimating…"
+#~ msgstr "Beräknar…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "%d∶%02d återstår (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "%d∶%02d tills fulladdad (%d %%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Skärmen delas"
+
+#~ msgid "Turn off"
+#~ msgstr "Stäng av"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "Flygplansläge påslaget"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Skärminspelning pågår"
+
+#~ msgid "Stop"
+#~ msgstr "Stoppa"
+
+#~ msgid "Author"
+#~ msgstr "Upphovsman"
+
+#~ msgid "Warning"
+#~ msgstr "Varning"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Tillägg kan orsaka systemproblem, även prestandaproblem. Om du stöter på "
+#~ "problem med ditt system så rekommenderas du att inaktivera alla tillägg."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Ta bort från favoriter"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Lägg till som favorit"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Aktivera introspektions-API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Aktiverar ett D-Bus-API som tillåter introspektion av programtillståndet "
+#~ "för skalet."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Misslyckades med att ansluta till GNOME-skalet"
+
+#~ msgid "Minimize"
+#~ msgstr "Minimera"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Avmaximera"
+
+#~ msgid "App Picker View"
+#~ msgstr "Programväljarvy"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Index för den valda vyn i applikationsväljaren."
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Ofta använda program kommer visas här"
+
+#~ msgid "Frequent"
+#~ msgstr "Ofta använda"
+
+#~ msgid "All"
+#~ msgstr "Alla"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%A, %-d %B"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d %B, %Y"
+
+#~ msgid "Off"
+#~ msgstr "Frånkopplad"
+
+#~ msgid "On"
+#~ msgstr "På"
+
+#~ msgid "Copy Error"
+#~ msgstr "Kopiera fel"
+
+#~ msgid "Username…"
+#~ msgstr "Användarnamn…"
+
+#~ msgid "Password: "
+#~ msgstr "Lösenord: "
+
+#~ msgid "Enter Password…"
+#~ msgstr "Ange lösenord…"
+
+#~ msgid "Next"
+#~ msgstr "Nästa"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Logga in"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d nytt meddelande"
+#~ msgstr[1] "%d nya meddelanden"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d ny avisering"
+#~ msgstr[1] "%d nya aviseringar"
+
+#~ msgid "org.gnome.Extensions"
+#~ msgstr "org.gnome.Extensions"
+
+#~ msgid "Logout…"
+#~ msgstr "Logga ut…"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Bläddra i Programvara"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "Skärmrotation"
+
+#~ msgid "Password:"
+#~ msgstr "Lösenord:"
+
+#~ msgid "Type again:"
+#~ msgstr "Skriv igen:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Autentisering krävs av trådlöst nätverk"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Lösenord för mobilt bredbandsnätverk"
+
+#~ msgid "Rename"
+#~ msgstr "Byt namn"
+
+#~ msgid "Account Settings"
+#~ msgstr "Kontoinställningar"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Skärmrotation"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Snabbtangent som pausar och återupptar alla aktiva animeringar (tweens), "
+#~ "i felsökningssyfte"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Vilket tangentbord att använda"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Typen av tangentbord att använda."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Det uppstod ett fel vid inläsning av inställningsdialogen för %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s hela dagen."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, sedan %s senare."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, sedan %s, följt av %s senare."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Känns som %s."
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Logga ut"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Växla användare"
+
+#~ msgid "Hide tray"
+#~ msgstr "Dölj aktivitetsfält"
+
+#~ msgid "Status Icons"
+#~ msgstr "Statusikoner"
+
+#~ msgid "Events"
+#~ msgstr "Händelser"
+
+#~ msgid "Notifications"
+#~ msgstr "Aviseringar"
+
+#~ msgid "Media"
+#~ msgstr "Media"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Omdirigering för webbautentisering"
+
+#~ msgid "Not In Use"
+#~ msgstr "Används inte"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Visa veckonummer i kalendern"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Om satt till \"true\", visa veckonummer enligt ISO i kalendern."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Använd som internetanslutning"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s begär åtkomst till din plats."
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME-skal (wayland-compositor)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d ansluten enhet"
+#~ msgstr[1] "%d anslutna enheter"
+
+#~ msgid "UPS"
+#~ msgstr "Avbrottsfri kraftförsörjning (UPS)"
+
+#~ msgid "Battery"
+#~ msgstr "Batteri"
diff --git a/po/ta.po b/po/ta.po
new file mode 100644
index 0000000..2071b00
--- /dev/null
+++ b/po/ta.po
@@ -0,0 +1,2087 @@
+# Tamil translation for gnome-shell.
+# Copyright (C) 2010 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Dr.T.Vasudevan <agnihot3@gmail.com>, 2010, 2011, 2012.
+# Shantha kumar <shkumar@redhat.com>, 2013, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-09-13 16:02+0000\n"
+"PO-Revision-Date: 2014-09-13 21:39+0630\n"
+"Last-Translator: Shantha kumar <shkumar@redhat.com>\n"
+"Language-Team: American English <kde-i18n-doc@kde.org>\n"
+"Language: ta\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.5\n"
+"X-Poedit-Language: தமிழ்\n"
+"X-Poedit-Country: இந்தியா\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "கணினி"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "அறிவிப்பு பலகத்தை காட்டுக"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "இயங்கு செய்தி அறிவிப்பை குவி"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "கண்ணோட்டத்தை காட்டு"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "எல்லா செயலிகளையும் காட்டு"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "செயலிகள் பட்டியை திறக்க விசைபிணைப்பு"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "க்னோம் ஷெல்"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "சாளர மேலாண்மை மற்றும் பயன்பாடு துவக்கம்"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "க்னோம் ஷெல் நீட்சி விருப்பங்கள்"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "க்னோம் ஷெல் நீட்சிகளை வடிவமை"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME ஷெல் (வேலேன்ட் கம்ப்போசிட்டர்)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"உருவாக்குவோர் மற்றூம் சோதிப்போருக்கு பயன்படுமாறு Alt-F2 வழியாக உள்ளமை "
+"கருவிகளை "
+"செயலாக்கு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"உள்ளமை வழி நீக்கம் மற்றும் Alt-F2 உரையாடல் மூலம் கருவிகள் கண்காணிப்பு "
+"ஆகியவற்றை அணுக "
+"உதவும்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "செயல்படுத்த வேண்டிய நீட்சிகளின் UUIDகள்"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"க்னோம் ஷெல் நீட்சிகளுக்கு ஒரு யூயூஐடி பண்பு உண்டு. இந்த விசை எந்த நீட்சிகள் "
+"ஏற்றப்பட வேண்டும் "
+"என பட்டியலிடுகிறது. ஏற்றப்பட வேண்டிய எந்த நீட்சியும் இந்த பட்டியலில் இருந்தே "
+"ஆகவேண்டும். "
+"org.gnome.Shell. இல் உள்ள EnableExtension மற்றும் DisableExtension DBus "
+"முறைகளில் "
+"இந்த பட்டியலை நீங்கள் திருத்தலாம்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "நீட்சிப் பதிப்பு இணக்கத்தன்மை செல்லுபடியாக்கத்தை முடக்கும்"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME ஷெல் நடப்பு இயக்கப் பதிப்பை ஆதரிப்பதாகக் கூறும் நீட்சிகளை மட்டுமே "
+"ஏற்றும். இந்த "
+"விருப்பத்தைச் செயல்படுத்தினால் இந்த சோதனை முடக்கப்படும், பிறகு நீட்சிகள் "
+"எந்தப் பதிப்புகளை "
+"ஆதரிப்பதாகக் கூறுகின்றன என்பதைப் பொருட்படுத்தாமல் எல்லா நீட்சிகளையும் ஏற்ற "
+"முயற்சி "
+"செய்யப்படும்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "விருப்ப பயன்பாடுகளுக்கு மேல்மேசை கோப்பு அடையாளங்கள்"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"இந்த அடையாளங் காட்டிகளுக்கு பொருத்தமான பயன்பாடுகள் விருப்ப இடத்தில் "
+"காட்டப்படும்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "ஆப் பிக்கர் காட்சி"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr ""
+"பயன்பாட்டுத் தேர்வியில் தற்போது தேர்ந்தெடுக்கப்பட்டுள்ள காட்சியின் அட்டவணை."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "(Alt-F2) கட்டளை உரையாடல் க்கு வரலாறு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "லுக்கிங் க்ளாஸ் உரையாடலுக்கு வரலாறு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "எப்போதும் பயனர் மெனுவில் 'வெளியேறு' மெனு உருப்படியைக் காண்பி."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"இந்த விசையானது ஒற்றை பயனர், ஒற்றை அமர்வு சூழ்நிலைகளில் 'வெளியேறு' மெனு "
+"உருப்படி "
+"தானாக மறைதலை கட்டுப்படுத்தி இயங்கும்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"குறியாக்கம் செய்யப்பட்ட அல்லது தொலைநிலை கோப்புமுறைமைகளை மவுன்ட் செய்வதற்கான "
+"கடவுச்சொல்லை "
+"நினைவில் வைத்திருக்க வேண்டுமா"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"ஒரு குறியாக்கம் செய்யப்பட்ட சாதனம் அல்லது தொலைநிலை கோப்புமுறைமையை மவுன்ட் "
+"செய்யும் போது "
+"ஷெல்லானது ஒரு கடவுச்சொல்லைக் கேட்கும். எதிர்காலப் பயன்பாட்டுக்கு கடவுச்சொல் "
+"சேமிக்கப்பட "
+"முடியும் எனில், 'கடவுச்சொல்லை நினைவில் வைத்திரு' என ஒரு தேர்வுப் பெட்டி "
+"இடம்பெறும். இந்த "
+"விசையே இந்த தேர்வுப் பெட்டியின் முன்னிருப்பு நிலையை அமைக்கிறது."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "நாட்காட்டியில் வார நாளை காட்டவும்"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "உண்மையானால் நாட்காட்டியில் ஐஎஸ்ஓ வார எண்களை காட்டவும்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "பயன்பாட்டு பட்டியை திறக்க விசைபிணைப்பு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "பயன்பாட்டு பட்டியை திறக்க விசைபிணைப்பு."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr " \"செயலிகளைக்காட்டு\" பார்வையை திறக்க விசைபிணைப்பு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"செயல்பாடுகள் மேல்பார்வையின் \"செயலிகளைக்காட்டு\" பார்வையை திறக்க விசைபிணைப்பு."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the overview"
+msgstr "மேற்பார்வையை திறக்கும் விசைபிணைப்பு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the Activities Overview."
+msgstr "செயலிகளின் மேற்பார்வையை திறக்கும் விசைபிணைப்பு."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "அறிவிப்பு பலகத்தின் காட்சியை நிலை மாற்ற விசைபிணைப்பு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "அறிவிப்பு பலகத்தின் காட்சியை நிலை மாற்ற விசைபிணைப்பு."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "இயங்கு செய்தி அறிவிப்பை குவிக்க விசைபிணைப்பு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "இயங்கு செய்தி அறிவிப்பை குவிக்க விசைபிணைப்பு."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"இயக்கத்திலிருக்கும் ட்வீன்கள் அனைத்தையும் இடைநிறுத்தி மீண்டும் துவக்கும் "
+"விசைப்பிணைப்பாக்கம், வழுநீக்கத் தேவைகளுக்கானது."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "எந்த விசைப்பலகையை பயன்படுத்த வேண்டும்?"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "பயன்படுத்த வேண்டிய விசைப்பலகை வகை"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "மாற்றியை நடப்பு பணியிடத்திற்கு மட்டும் எனக் கட்டுப்படுத்து."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"ஆம் என அமைக்கப்பட்டால், நடப்புப் பணியிடத்தில் சாளரங்களைக் கொண்டுள்ள "
+"பயன்பாடுகள் மட்டுமே மாற்றியில் காண்பிக்கப்படும். இல்லாவிட்டால் அனைத்துப் "
+"பயன்பாடுகளும் இருக்கும்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "பயன்பாடு படவுரு பயன்முறை."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"ஸ்விட்ச்சரில் சாளரங்கள் எப்படிக் காண்பிக்கப்படும் என்பதை அமைவாக்கம் "
+"செய்கிறது. 'சிறுதோற்றம்-"
+"மட்டும்' (சாளரத்தின் சிறுதோற்றத்தைக் காண்பிக்கும்), "
+"'பயன்பாட்டு-படவுரு-மட்டும்' (பயன்பாட்டின் "
+"படவுருவை மட்டும் காண்பிக்கும்) அல்லது 'இரன்டும்' ஆகிய சாத்தியங்கள் உள்ளன."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"ஆம் என அமைக்கப்பட்டால், நடப்புப் பணியிடத்தில் உள்ள சாளரங்கள் மட்டுமே "
+"மாற்றியில் காண்பிக்கப்படும். இல்லாவிட்டால் அனைத்து சாளரங்களும் இருக்கும்."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "தாய் சாளரத்துக்கு மாதிரி உரையாடலை இணை"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"இந்த விசை org.gnome.mutter இல் உள்ள விசையை க்னோம் ஷெல்லை இயக்கும்போது "
+"வன்மீறல் செய்கிறது."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "திரை விளிம்புகளில் சாளரங்களை இடும்போது விளிம்பு சாய்தலை செயலாக்கு"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "பணிக்களங்கள் இயங்கு நிலையில் மேலாளப்படுகின்றன"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "பணிக்களங்கள் முதன்மை திரையில் மட்டும் "
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"சொடுக்கி பயன்முறையில் சுட்டி நகர்வது நிற்கும் வரை கவனப் பகுதி மாறுவதைத் "
+"தாமதிக்கவும்"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "கட்டுப்படுத்தப்பட்ட வலைவாசல்"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s க்கு விருப்பங்கள் உரையாடலை ஏற்றுகையில் பிழை நேர்ந்தது:"
+
+#: ../js/extensionPrefs/main.js:155
+#| msgid "Configure GNOME Shell Extensions"
+msgid "GNOME Shell Extensions"
+msgstr "GNOME ஷெல் நீட்சிகள்"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "ரத்துசெய்க"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "அடுத்தது"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "பூட்டை திற"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "உள்நுழை"
+
+#: ../js/gdm/loginDialog.js:269
+msgid "Choose Session"
+msgstr "அமர்வை தேர்ந்தெடு"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "பட்டியலில் இல்லை?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(எ.கா., பயனர் அல்லது %s)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "பயனர்பெயர்: "
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "உள்புகு சாளரம்"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "உறுதிப்படுத்தல் பிழை"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(அல்லது விரல் தீய்ப்பு)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "கட்டளை காணப்படவில்லை"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "கட்டளையை அலகிட முடியவில்லை"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+#| msgid "Execution of '%s' failed:"
+msgid "Execution of “%s” failed:"
+msgstr "“%s” செயல்படுத்தல் தோல்வியடைந்தது:"
+
+#: ../js/portalHelper/main.js:85
+#| msgid "Authentication Required"
+msgid "Web Authentication Redirect"
+msgstr "இணைய அங்கீகரிப்பு திருப்பிவிடல்"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "அதிகம் பயன்படுத்தப்படும் பயன்பாடுகள் இங்கு காண்பிக்கப்படும்"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "அடிக்கடி"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "எல்லா"
+
+#: ../js/ui/appDisplay.js:1789
+msgid "New Window"
+msgstr "புதிய சாளரம்"
+
+#: ../js/ui/appDisplay.js:1815 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "விருப்பத்தில் இருந்து நீக்கு"
+
+#: ../js/ui/appDisplay.js:1821
+msgid "Add to Favorites"
+msgstr "விருப்பங்களுக்கு சேர்"
+
+#: ../js/ui/appDisplay.js:1830
+#| msgid "Show Text"
+msgid "Show Details"
+msgstr "விவரங்களைக் காட்டு"
+
+#: ../js/ui/appFavorites.js:124
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s உங்கள் விருப்பங்களில் சேர்க்கப்பட்டது"
+
+#: ../js/ui/appFavorites.js:158
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s உங்கள் விருப்பங்களில் இருந்து நீக்கப்பட்டது"
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "அமைப்புகள்"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "பின்புலத்தை மாற்று…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "முழு நாள்"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ஞா"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "தி"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "செ"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "பு"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "வி"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "வெ"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ச"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "ஞா"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "தி"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "செ"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "பு"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "வியா"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "வெ"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "ச"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "முந்தைய மாதம்"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "அடுத்த மாதம்"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "திட்டம் எதுவுமில்லை"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "இன்று"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "நாளை"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "இந்த வாரம்"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "அடுத்த வாரம்"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "வெளிப்புற இயக்கி இணைக்கப்பட்டது"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "வெளிப்புற இயக்கி இணைப்பு நீக்கப்பட்டது"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "நீக்கப்படக்கூடிய சாதனங்கள் "
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ஆல் திற"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "வெளியேற்று"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "கடவுச்சொல்:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "மீண்டும் உள்ளிடவும்:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "இணை"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "கடவுச்சொல்:"
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "விசை: "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "அடையாளம்: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "அந்தரங்க முதன்மை கடவுச்சொல்: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "சேவை:"
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "கம்பியில்லா வலைப்பின்னலுக்கு உறுதிப்படுத்துதல் தேவை"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+#| msgid ""
+#| "Passwords or encryption keys are required to access the wireless network "
+#| "'%s'."
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"வயர்லெஸ் பிணையம் \"%s\" ஐ அணுக கடவுச்சொற்கள் அல்லது மறைகுறியாகக் விசைகள் "
+"தேவைப்படுகிறது."
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "கம்பியுள்ள 802.1X அங்கீகாரம்"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "வலைப்பின்னல் பெயர்:"
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "டிஎஸ்எல் உறுதிப்படுத்துதல் "
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "பின் கோட் தேவைப்படுகிறது"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "அலை அகலப்பட்டை சாதனத்துக்கு பின் கோட் தேவை"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "அலை அகலப்பட்டைக்கு வலைப்பின்னல் கடவுச்சொல்"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+#| msgid "A password is required to connect to '%s'."
+msgid "A password is required to connect to “%s”."
+msgstr "\"%s\" உடன் இணைப்பதற்கு கடவுச்சொல் தேவைப்படுகிறது."
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "உறுதிப்படுத்தல் தேவை"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "நிர்வாகி"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "உறுதிப்படுத்து"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "மன்னிக்க, அது வேலை செய்யவில்லை. மீண்டு முயற்சி செய்க."
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "அழைப்பு"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "அழை"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "கோப்பு பறிமாற்றம் "
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "அரட்டை"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "ஒலி நிறுத்தம் நீக்கு"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "ஒலி நிறுத்தம்"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+msgid "Yesterday, %H∶%M"
+msgstr "நேற்று, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "நேற்று, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s இப்போது %s ஆனார்"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%s க்கு அழைப்பு"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s உங்களை %s உடன் சேர அழைக்கிறார்."
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "நிராகரி"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "ஏற்றுக்கொள்"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s இடமிருந்து விடியோ அழைப்பு"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s இடமிருந்து அழைப்பு"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "பதிலளி"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s உங்களுக்கு %s அனுப்புகிறார்"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s நீங்கள் இணைப்பில் இருக்கும் போது காண அனுமதி வேண்டுகிறார். "
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "வலையமைப்பு பிழை"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "உறுதி செய்தல் தோல்வி"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "மறையாக்க பிழை"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "சான்றிதழ் தரப்படவில்லை"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "சான்றிதழில் நம்பக தன்மை இல்லை"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "சான்றிதழ் காலாவதியானது"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "சான்றிதழ் செயல்படுத்தப்படவில்லை"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "சான்றிதழ் புரவலன் பெயர் ஒத்திசையவில்லை"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "சான்றிதழ் அடையாளம் ஒத்திசையவில்லை"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "சான்றிதழ் தானே கையெழுத்திட்டது"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "நிலை இப்போது இணைப்பு விலகி "
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "குறீயீட்டுமுறை கிடைப்பில் இல்லை"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "சான்றிதழ் செல்லுபடியாகாது"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "இணைப்பு மறுக்கப்பட்டது"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "இணைக்க முடியவில்லை"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "இணைப்பு அற்றுப்போயிற்று"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "இந்த கணக்கு ஏற்கனவே சேவையகத்துக்கு இணைக்கப்பட்டது"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "இணைப்பு அதே முலத்துடன் புதிய இணைப்பால் மாற்றப்பட்டது."
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "இந்த கணக்கு ஏற்கனவே சேவையகத்தில் உள்ளது"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "இணைப்பை கையாள சேவையகம் இப்போது மிகவும் வேலை பளுவில் உள்ளது."
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "சான்றிதழ் ரத்து செய்யப்பட்டது"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"சான்றிதழ் பாதுகாப்பில்லாத சைபர் அல்கோரிதத்தை பயன்படுத்துகிறது. இது "
+"மறையாக்கத்தில் "
+"பலகீனமானது."
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"சேவயகத்தின் சான்றிதழின் நீளம் அல்லது சங்கிலியின் ஆழம் மறையாக்க நூலகம் "
+"வரயறுத்த மதிப்பை "
+"தாண்டியது."
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "உள்ளார்ந்த பிழை"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s க்கு இணைக்க முடியவில்லை"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "கணக்கை காட்டவும்"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "தெரியாத காரணம்"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "சாளரங்கள்"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "பயன்பாடுகளை காட்டு"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "டேஷ்போர்ட்"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "நாள்காட்டியை திற"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "கடிகாரங்களைத் திற"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "தேதி நேரம் அமைப்புகள்"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s யிலிருந்து வெளியேறு"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "வெளியேறு"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s தானியங்கியாக %d வினாடியில் வெளியேறும்"
+msgstr[1] "%s தானியங்கியாக %d வினாடிகளில் வெளியேறும்"
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "நீங்கள் தானியங்கியாக %d வினாடியில் வெளியேற்றப்படுவீர்கள் "
+msgstr[1] "நீங்கள் தானியங்கியாக %d வினாடிகளில் வெளியேற்றப்படுவீர்கள் "
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "வெளியேறு"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "மின்சக்தி நிறுத்தம்"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "புதுப்பிப்புகளை நிறுவு & இயக்கம் நிறுத்து"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "கணினி தானியங்கியாக %d வினாடியில் மின் நிறுத்தப்படும்"
+msgstr[1] "கணினி தானியங்கியாக %d வினாடிகளில் மின் நிறுத்தப்படும்"
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "நிலுவையிலுள்ள மென்பொருள் புதுப்பிப்புகளை நிறுவு"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "மறு துவக்கம்"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "மின்சக்தி நிறுத்தம்"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "மறு துவக்கம்"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "கணினி தானியங்கியாக %d வினாடியில் மீள் துவக்கம் செய்யப்படும்."
+msgstr[1] "கணினி தானியங்கியாக %d வினாடிகளில் மீள் துவக்கம் செய்யப்படும்."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "மறுதுவக்கு & புதுப்பிப்புகளை நிறுவு"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "%d வினாடியில் கணினி தானாக மறுதுவக்கமாகி புதுப்பிப்புகளை நிறுவும்."
+msgstr[1] "%d வினாடிகளில் கணினி தானாக மறுதுவக்கமாகி புதுப்பிப்புகளை நிறுவும்."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "மறுதுவக்கு &amp; நிறுவு"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "நிறுவு &amp; இயக்கம் நிறுத்து"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "புதுப்பிப்புகள் நிறுவப்பட்ட பிறகு இயக்கம் நிறுத்து"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"மின்கல சக்தியில் இயங்குகிறது: புதுப்பிப்புகளை நிறுவும் முன்பு மின் இணைப்பு "
+"கொடுக்கவும்."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr ""
+"சில பயன்பாடுகள் பணிமிகுதியாக உள்ளன அல்லது சேமிக்காத வேலையைக் கொண்டுள்ளன."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "மற்ற பயனர்கள் புகுபதிவு செய்துள்ளனர்."
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (தொலைநிலை)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (கன்சோல்)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "நிறுவு"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.org இல் இருந்து '%s' ஐ தரவிறக்கி நிறுவவா?"
+
+#: ../js/ui/keyboard.js:692 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "விசைப்பலகை"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "நீட்சிகள் ஏதும் நிறுவப்படவில்லை"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s எந்த பிழையையும் வெளியிடவில்லை"
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "பிழைகளை மறை"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "பிழைகளை காட்டுக"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "செயலாக்கப்பட்டது"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "செயல்நீக்கப்பட்டது"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "பிழை"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "காலாவதியானது"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "பதிவிறக்குகிறது"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "மூலத்தை பார்க்க"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "இணைய பக்கம்"
+
+#: ../js/ui/messageTray.js:1326
+msgid "Open"
+msgstr "திற"
+
+#: ../js/ui/messageTray.js:1333
+msgid "Remove"
+msgstr "நீக்கு"
+
+#: ../js/ui/messageTray.js:1630
+msgid "Notifications"
+msgstr "அறிவிப்புகள்"
+
+#: ../js/ui/messageTray.js:1637
+msgid "Clear Messages"
+msgstr "செய்திகளை அழி"
+
+#: ../js/ui/messageTray.js:1656
+msgid "Notification Settings"
+msgstr "அறிவிப்பு அமைப்புகள்"
+
+#: ../js/ui/messageTray.js:1709
+msgid "Tray Menu"
+msgstr "தட்டில் பட்டி"
+
+#: ../js/ui/messageTray.js:1926
+msgid "No Messages"
+msgstr "செய்திகள் இல்லை"
+
+#: ../js/ui/messageTray.js:1968
+msgid "Message Tray"
+msgstr "அறிவிப்பு பலகம்"
+
+#: ../js/ui/messageTray.js:2971
+msgid "System Information"
+msgstr "கணினி தகவல்கள்"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "தெரியாத"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d புதிய செய்தி"
+msgstr[1] "%d புதிய செய்திகள்"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "மறை"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "மேலோட்டம்"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "தேட தட்டச்சு செய்யவும்..."
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "வெளியேறு"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "செயல்பாடுகள்"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "மேல் பட்டை"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "ஒரு கட்டளையை உள்ளிடுக"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "மூடுக"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "மறுதுவக்குகிறது…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d புதிய அறிவிப்பு"
+msgstr[1] "%d புதிய அறிவிப்புகள்"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "பூட்டு"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME திரையைப் பூட்ட வேண்டுகிறது"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "பூட்ட முடியவில்லை"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "ஒரு பயன்பாடு பூட்டுதலை முடக்கியுள்ளது"
+
+#: ../js/ui/search.js:594
+msgid "Searching…"
+msgstr "தேடுகிறது..."
+
+#: ../js/ui/search.js:596
+msgid "No results."
+msgstr "விடைகள் இல்லை."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "நகலெடு"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "ஒட்டு"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "உரையை காட்டுக"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "உரையை மறை"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "கடவுச்சொல்"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "கடவுச்சொல்லை நினைவு கொள்ளவும்"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "அணுகல்"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "பெரிதாக்கு"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "திரைபடிப்பான்"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "திரை விசைப்பலகை"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "காட்சி எச்சரிக்கைகள்"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "ஒட்டு விசைகள்"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "மெது விசைகள்"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "எதிரொலிப்பு விசைகள்"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "சொடுக்கி விசைகள்"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "அதிக முறண்"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "பெரிய உரை"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "ப்ளூடூத்"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "முடக்கு"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "ப்ளூடூத் அமைப்புகள்"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "இணைக்கப்பட்ட சாதனம் %d"
+msgstr[1] "இணைக்கப்பட்ட சாதனங்கள் %d"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+msgid "Not Connected"
+msgstr "இணைக்கப்படவில்லை"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "வெளிச்சம்"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "விசைப்பலகை இடவமைவை காட்டுக"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "இருப்பிடம்"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "முடக்கு"
+
+#: ../js/ui/status/location.js:73
+msgid "Privacy Settings"
+msgstr "தனியுரிமை அமைவுகள்"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "பயனில் உள்ளது"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "செயல்படுத்து"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<தெரியாத>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+msgid "Off"
+msgstr "முடக்கு"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "இணைக்கப்பட்டது"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "நிர்வகிக்கப்படாதது"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "துண்டிக்கப்படுகிறது"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+msgid "Connecting"
+msgstr "இணைக்கிறது"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "அங்கீகரிப்பு தேவை"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "சாதன நிரல் இல்லை"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "கிடைக்கவில்லை"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "இணைப்பு தோல்வியுற்றது"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "வயர்டு அமைவுகள்"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "மொபைல் பிராட்பேண்டு அமைவுகள்"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+msgid "Hardware Disabled"
+msgstr "வன்பொருள் முடக்கப்பட்டது"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "இணைய இணைப்பாகப் பயன்படுத்து"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "விமான பயன்முறை இயக்கத்தில்"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "விமானப் பயன்முறை இயக்கத்தில் இருக்கும் போது Wi-Fi முடக்கப்படும்."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "விமான பயன்முறையை அணைக்கவும்"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi அணைக்கப்பட்டுள்ளது"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "பிணையத்திற்கு இணைக்க வேண்டுமானால் Wi-Fi ஐ இயக்க வேண்டும்."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fi ஐ இயக்கு"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "கம்பியில்லா பிணையங்கள்"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "ஒரு பிணையத்தை தெரிவுச்செய்"
+
+#: ../js/ui/status/network.js:882
+msgid "No Networks"
+msgstr "பிணையங்கள் இல்லை"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "அணைக்க, வன்பொருள் பொத்தானைப் பயன்படுத்தவும்"
+
+#: ../js/ui/status/network.js:1173
+msgid "Select Network"
+msgstr "பிணையத்தை தெரிவுச்செய்"
+
+#: ../js/ui/status/network.js:1179
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi அமைவுகள்"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "செயற்படுத்து"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "ஹாட்ஸ்பாட் செயலில் உள்ளது"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "இணைக்கிறது..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "உறுதிப்படுத்துதல் தேவை"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "இணைப்பு தோல்வி அடைந்தது"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "வலைப்பின்னல் அமைப்புகள்"
+
+#: ../js/ui/status/network.js:1482
+msgid "VPN Settings"
+msgstr "VPN அமைவுகள்"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "விபிஎன் - VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "வலைப்பின்னல் மேலாளர்"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "வலைப்பின்னல் இணைப்பு செயலாக்கம் தோல்வி அடைந்தது"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "மின்சக்தி அமைப்புகள்"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "முழுமையாக மின்னூட்டப்பட்டது"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "கணிக்கிறது…"
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d மீதமுள்ளது (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d முழுமையாகும் வரை (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "மின்கலம்"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "விமான பயன்முறை"
+
+#: ../js/ui/status/rfkill.js:85
+msgid "On"
+msgstr "மீது\t"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "பயனர் மாற்று"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "வெளியேறு"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "சார்பு பூட்டு"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "இடைநிறுத்தம்."
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "மின்சக்தி நிறுத்தம்"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "ஒலியளவு மாற்றப்பட்டது"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "ஒலியளவு"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "மைக்ரோஃபோன்"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "வேறு பயனராக உள்நுழைக"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "சாளர பூட்டு திறக்கவும்"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "பயன்பாடுகள்"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "தேடு"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” தயாராக உள்ளது"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "இந்தக் காட்சி அமைவுகளை வைத்திருக்க வேண்டுமா?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "அமைவுகளை மீட்டமை"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "மாற்றங்களைச் செயல்படுத்து"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "அமைவுகளின் மாற்றங்கள் %d வினாடியில் மீட்டமைக்கப்படும்"
+msgstr[1] "அமைவுகளின் மாற்றங்கள் %d வினாடிகளில் மீட்டமைக்கப்படும்"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "சிறிதாக்கு"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "பெரிதாக்க வேண்டாம்"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "பெரிதாக்கு"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "நகர்த்து"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "மறுஅளவிடு"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "தலைப்புப்பட்டையை திரைக்கு நகர்த்தவும்"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "எப்போதும் மேலே"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "எப்போதும் புலனாகும் வேலையிடத்தில்"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "மேலே உள்ள வேலையிடத்திற்கு நகர்த்தவும்"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "கீழே உள்ள வேலையிடத்திற்கு நகர்த்தவும்"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "எவல்யூஷன் நாள்காட்டி"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u வெளிப்பாடு"
+msgstr[1] "%u வெளிப்பாடுகள்"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u உள்ளீடு"
+msgstr[1] "%u உள்ளீடுகள்"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "கணினி ஒலிகள்"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "அச்சுப் பதிப்பு"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "ஜிடிஎம் உள்நுழைவு திரைக்கு பயன்படுத்தும் பாங்கு"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "உள்நுழைவு திரைக்கு குறிப்பிட்ட பாங்கை, எ.கா. \"gdm\" பயன்படுத்துக"
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "இயன்ற பாங்குகளின் பட்டியல்"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "\"%s\" ஐத் தொடங்குதல் தோல்வியடைந்தது"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "கடவுச்சொற்கள் பொருந்தவில்லை"
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "கடவுச்சொல் வெறுமையாக இருக்க முடியாது"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "பயனர் உறுதிப்படுத்தல் உரையாடலை வெளியனுப்பினார்."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "கோப்புறைகளாகக் காண்பிக்க வேண்டிய பகுப்புகளின் பட்டியல்"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "இந்தப் பட்டியலில் காண்பிக்கப்படும் ஒவ்வொரு பகுப்பும், பிரதானப் பார்வையில் உள்ளே "
+#~ "காண்பிக்கப்படுவதற்கு மாறாக பயன்பாட்டுப் பார்வையில் கோப்புறையாகக் காண்பிக்கப்படும்."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "பயனர் குறிப்பாக அமைத்த கடைசி ஐஎம் இருப்பு நிலையை சேமிக்க உள்ளே பயனாகும். இங்குள்ள "
+#~ "மதிப்பு TpConnectionPresenceType கணக்கிடுதலில் இருந்து."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "பயனருக்கு கடைசி அமர்வின் இருப்பு நிலையை சேமிக்க உள்ளே பயனாகும். இங்குள்ள மதிப்பு "
+#~ "GsmPresenceStatus கணக்கிடுதலில் இருந்து."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "தலைப்புப்பட்டியில் பொத்தான்களின் அமைப்பு"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "இந்த விசை org.gnome.desktop.wm.preferences ஐ க்னோம் ஷெல்லை இயக்கும்போது வன்மீறல் "
+#~ "செய்கிறது."
+
+#~ msgid "Extension"
+#~ msgstr "நீட்சி"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "மேலுள்ள கூட்டுப்பெட்டியை பயன்படுத்தி வடிவமைக்க ஒரு நீட்சியை தேர்ந்தெடுக்கவும்"
+
+#~| msgid "Session…"
+#~ msgid "Session"
+#~ msgstr "அமர்வு"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "இந்த பயன்பாடுகளை மூடி கணினியில் இருந்து வெளியேற வெளியேறு ஐ சொடுக்கவும்"
+
+#~ msgid "Logging out of the system."
+#~ msgstr "கணினியில் இருந்து வெளியேறுகிறது"
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "இந்த பயன்பாடுகளை மூடி கணினி மின் நிறுத்தம் செய்ய மின் நிறுத்தம் ஐ சொடுக்கவும்"
+
+#~ msgid "Powering off the system."
+#~ msgstr "கணினி மின் நிறுத்தப்படுகிறது"
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "இந்த பயன்பாடுகளை மூடி கணினி மீள் துவக்கம் செய்ய செய்ய மீள் துவக்கம் ஐ சொடுக்கவும்"
+
+#~ msgid "Restarting the system."
+#~ msgstr "கணினி மறு துவக்கம் செய்யப்படுகிறது"
+
+#~| msgid "Settings"
+#~ msgid "Settings Menu"
+#~ msgstr "பட்டி அமைப்புகள்"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%s இடமிருந்து உறுதிப்படுத்தல் வேண்டுதல்"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "சாதனம் %s இந்த கணினியுடன் இணைக்க விரும்புகிறது"
+
+#~ msgid "Allow"
+#~ msgstr "அனுமதி"
+
+#~ msgid "Deny"
+#~ msgstr "மறுப்பு"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "சாதனம் %s ' சேவை %s' க்கு அணுகல் வேண்டுகிறது"
+
+#~ msgid "Always grant access"
+#~ msgstr "எப்போதும் அணுகலை அளி"
+
+#~ msgid "Grant this time only"
+#~ msgstr "இம்முறை மட்டும் அணுகலை அளி"
+
+#~ msgid "Reject"
+#~ msgstr "மறு"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s க்கு இணை சேர்த்த உறுதிப்படுத்தல்"
+
+#~| msgid ""
+#~| "Please confirm whether the PIN '%06d' matches the one on the device."
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "கடவு எண்ணானது '%06d' சாதனத்தில் உள்ள ஒன்றுடன் பொருந்துகிறதா என உறுதி செய்யவும்."
+
+#~ msgid "Matches"
+#~ msgstr "பொருந்துகிறது"
+
+#~ msgid "Does not match"
+#~ msgstr "பொருந்தவில்லை"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s க்கு இணைக்க வேண்டுதல்"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "சாதனத்தின் சொல்லும் பின் ஐ உள்ளீடு செய்க"
+
+#~ msgid "OK"
+#~ msgstr "சரி"
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "இயக்க நிறுத்தம் செய்தால், அவர்களது சேமிக்கப்படாத பணிகள் இழக்கப்படலாம்."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "மன்னிக்க இன்றூ உங்களுக்கு பொன்மொழி இல்லை:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s அசரீரி சொல்லுகிறது"
+
+#~ msgid "Screenshots"
+#~ msgstr "திரைவெட்டுகள்"
+
+#~ msgid "Record a screencast"
+#~ msgstr "ஒரு திரைவெட்டை பதிவெடு."
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "நிரல்களின் பயன்பாடு குறித்த புள்ளிவிவரம் சேகரிக்க வேண்டுமா"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "ஷெல் சாதாரணமாக செயலிலுள்ள நிரல்களை மேற்பார்வை இடுகிறது. அதனால் அடிக்கடி பயனாகும் "
+#~ "நிரல்கள் (எ-டு: துவக்கிகள்) முன் வைக்கப்படும். இந்த தரவு அந்தரங்கமாக வைக்கப்பட்டாலும் "
+#~ "நீங்கள் இதை நீக்க விரும்பலாம். அப்படிச் செய்வது முன்னே சேகரித்த தரவை நீக்காஅது என் "
+#~ "அறியவும்."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "உள்ளமை திரை பதிவியை நிலை மாற்ற விசைபிணைப்பு"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "உள்ளமை திரை பதிவியை துவக்க, நிறுத்த விசைபிணைப்பு"
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "திரைநிகழ்வை பதிவு செய்ய சட்டவிகிதம் ."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "க்னோம்ஷெல் இன் ஸ்க்ரீன்காஸ்ட் பதிவரில் ஸ்க்ரீன்காட் ஐ பதிகையில் வினாடிக்கு சட்டங்களின் விகிதம்."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "ஸ்க்ரீன்காஸ்டை குறியாக்க பயனாகும் ஜிஸ்ட்ரீமர் குழாய்."
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "பதிவுகளை குறியாக்க ஜிஸ்ட்ரீமர் குழாயை அமைக்கிறது. ஜிஎஸ்டி லான்ஸ் க்கு பயன்படும் அதே "
+#~ "இலக்கணத்தை பின் பற்றும். குழாய்க்கு இணைப்பில்லா பதிவுக்குழி இருத்தல் வேண்டும். இதில் "
+#~ "விடியோ பதிவாகும். சாதாரணமாக இதில் இணைக்காத ஒரு மூல பதிவேடு இருக்கும்; இதன் "
+#~ "வெளியீடு வெளியீட்டு கோப்பாக பதிவாகும். எனினும் குழாய் தன் வெளியீட்டை தானே "
+#~ "கவனித்துக்கொள்ள இயலும். இது வெளியீட்டை ஐஸ்காஸ்ட் அல்லது ஷௌட்2சென்ட் போன்றவற்றுக்கு "
+#~ "நேரடியாக அனுப்ப இயலும். அமைப்பை அன்செட் செய்தாலல்லது காலி மதிப்புக்கு அமைத்தாலும் "
+#~ "முன்னிருப்பு குழாய் பயன்படுத்தப்படும். நடப்பில் இது 'vp8enc min_quantizer=13 "
+#~ "max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! "
+#~ "webmmux' ஆகும்; இதுVP8 கோடக் ஐ பயன்படுத்தி WEBM க்கு பதிவுசெய்யும். கணினியில் "
+#~ "பொருத்தமான இழை கணக்குக்கு ஊகம் செய்வதற்கு %T இடப்பிடிப்பானாக வேலை செய்யும்."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "திரைவெட்டை சேமிக்க கோப்பு பின்னொட்டு."
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "ஸ்க்ரீன்காஸ்ட் இல் பதிவாவனக்கான கோப்புப்பெயர் தனித்தன்மை வாய்ந்தது. இது நடப்பு தேதியை "
+#~ "அடிப்படையாக கொண்டது; இந்த பின்னொட்டை பயன்படுத்தும். பதிவதை வேறு கொள்கலத்தின் ஒழுங்கில் "
+#~ "மாற்றுகையில் இதையும் மாற்ற வேண்டும்."
+
+#~ msgid "Power"
+#~ msgstr "மின்சக்தி"
+
+#~ msgid "Restart"
+#~ msgstr "மறு துவக்கம்"
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "%d %t இலிருந்து ஸ்க்ரீண்காஸ்ட்"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "உலகளாவிய அணுகல் அமைப்பு"
+
+#~ msgid "Visibility"
+#~ msgstr "காணல்"
diff --git a/po/te.po b/po/te.po
new file mode 100644
index 0000000..2b032cf
--- /dev/null
+++ b/po/te.po
@@ -0,0 +1,1965 @@
+# Telugu translation for gnome-shell.
+# Copyright (C) 2011, 2012 Swecha telugu translations team <localization@swecha.net>
+# This file is distributed under the same license as the gnome-shell package.
+# Krishnababu Krothapalli <kkrothap@redhat.com>, 2011, 2012, 2013, 2014.
+# Hari Krishna <hari@swecha.net>, 2011.
+# Sasi Bhushan Boddepalli <sasi@swecha.net>, 2012.
+# Praveen Illa <mail2ipn@gmail.com>, 2011, 2012, 2013, 2014.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell gnome-3-0\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2014-09-23 07:38+0000\n"
+"PO-Revision-Date: 2014-09-23 15:55+0530\n"
+"Last-Translator: Krishnababu Krothapalli <kkrothap@redhat.com>\n"
+"Language-Team: American English <kde-i18n-doc@kde.org>\n"
+"Language: te\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.5\n"
+"X-Project-Style: gnome\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "వ్యవస్థ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "సందేశ పళ్ళెం చూపించు"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "క్రియాశీల గమనింపుపై దృష్టివుంచు"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "అవలోకనాన్ని చూపించు"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "అన్ని అనువర్తనాలను చూపించు"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "అనువర్తనం మెనూ తెరువు"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "గ్నోమ్ షెల్"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "కిటికీ నిర్వాహణ మరియు అనువర్తనము ప్రారంభించుట"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "గ్నోమ్ షెల్ పొడిగింతల ప్రాధాన్యతలు"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "గ్నోమ్ షెల్ పొడిగింతలను స్వరూపించు"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "గ్నోమ్ షెల్ (వేల్యాండ్ సృష్టి)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"అభివృద్ధికారులకు మరియు పరీక్షకులకు ఉపయోగపడే సాధనాలను Alt-F2 నుండి చేతనపరుచు"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 డైలాగుని వాడి అంతర్గత దోషశుద్ది మరియు సాధనాలను పర్యవేక్షించుటకు "
+"సౌలభ్యతను అనుమతిస్తుంది."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "చేతనించాల్సిన పొడిగింతలు యొక్క UUIDs"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"గ్నోమ్ షెల్ పొడిగింతలు ఒక UUID లక్షణాన్ని కలిగివున్నాయి; ఏ పొడిగింతలు "
+"లోడుచేయదగినవి కాదో ఈ కీ జాబితాచేయును. "
+"ఏ పొడిగింత లోడవ్వాలో అది ఈ జాబితా నందు ఉండాలి. అంతేకాకుండా మీరు ఈ జాబితాను "
+"పాడిగింతను చేతనపరుచు లేదా "
+"పొడిగింతను అచేతనపరుచు డిబస్ పద్ధతులలో గ్నోమ్ షెల్ నందు మార్చవచ్చును."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "పొడిగింత రూపాంతర అనుగుణ్యత యొక్క చెల్లుబాటును అచేతనిస్తుంది"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"ప్రస్తుతం నడుస్తున్న రూపాంతరం తోడ్పాటుందని పేర్కొంటేనే గ్నోమ్ షెల్ "
+"పొడిగింతలను లోడు చేస్తుంది. ఈ "
+"ఐచ్ఛికాన్ని చేతనం చేస్తే ఈ తనిఖీని అచేనించి, తోడ్పాటు ఉన్నాదా లేదా అనే "
+"విషయంతో సంబంధం లేకుండా అన్ని "
+"పొడిగింతలను నింపుటకు ప్రయత్నిస్తుంది"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "ప్రియమైన అనువర్తనాల కోసం డెస్క్‌టాప్‌ దస్త్ర ఐడీల యొక్క జాబితా"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"ఈ గుర్తింపకాలకు అనుగుణమైన అనువర్తనాలు ప్రియమైన ప్రదేశములో ప్రదర్శించబడతాయి."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "అనువర్తన ఎంపికరి వీక్షణం"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "అనువర్తన ఎంపికరిలో ప్రస్తుతం ఎంపికచేసిన వీక్షణ సూచిక"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "ఆదేశ (Alt-F2) సంవాదం కోసం చరిత్ర"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "చూస్తున్న గ్లాసు సంవాదం కొరకు చరిత్ర"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "వాడుకరి మెనూ నందు ఎల్లప్పుడూ 'నిష్క్రమించు' మెనూ అంశాన్ని చూపించు."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"ఏక-వాడుకరి, ఏక-ఘట్టం పరిస్థితులలో 'నిష్క్రమించు' మెనూ అంశము స్వయంచాలకంగా "
+"దాగుటను ఈ మీట భర్తీ "
+"చేస్తుంది."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"ఎన్క్రిప్టెడ్ లేదా రిమోట్ దస్త్రవ్యవస్థల మౌంటు చేయుటకు సంకేతపదాలను "
+"గుర్తుంచుకోవాలా"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"ఎన్క్రిప్టెడ్ పరికరం లేదా రిమోట్ దస్త్రవ్యవస్థ మౌంటవునప్పుడు షెల్ సంకేతపదం "
+"కొరకు అభ్యర్ధించును. భవిష్య "
+"వినియోగం కొరకు సంకేతపదం దాయగలిగితే 'సంకేతపదం గుర్తుంచు' చెక్‌బాక్స్ వస్తుంది. "
+"ఈ కీ చెక్‌బాక్స్ యొక్క "
+"అప్రమేయ స్థితిని అమర్చును."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Show the week date in the calendar"
+msgstr "క్యాలెండరులో వారపు తేదీని చూపించు"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "If true, display the ISO week date in the calendar."
+msgstr "ఒకవేళ నిజమైతే, క్యాలెండరులో ISO వారము తేదీని ప్రదర్శిస్తుంది."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to open the application menu"
+msgstr "అనువర్తనం మెనూ తెరవడానికి కీ బైండింగ్ "
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to open the application menu."
+msgstr "అనువర్తనం మెనూ తెరవడానికి కీ బైండింగ్ "
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"అనువర్తనములు చూపు\" దర్శనంను తెరువుటకు కీబందనం"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"కార్యకలాపాల అవలోకనం యొక్క \"అనువర్తనములు చూపించు\" వీక్షణం తెరువుటకు కీ బందనం."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the overview"
+msgstr "అవలోకనాన్ని తెరుచుటకు కీబంధనం"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the Activities Overview."
+msgstr "కార్యకలాపాల అవలోకనాన్ని తెరుచుటకు కీబంధనం"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "సందేశ పళ్ళెం యొక్క దృగ్గోచరతను మార్చుటకు అడ్డదారి"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "సందేశ పళ్ళెం యొక్క దృగ్గోచరతను మార్చుటకు అడ్డదారి"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "క్రియాశీల ప్రకటన ఫోకస్‌కు కీబందనం"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "క్రియాశీల ప్రకటన ఫోకస్‌కు కీబందనం."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"దోషవిశ్లేషణ కోసం, కీబైండింగు నడుస్తున్న అన్ని ట్వీన్లను తిరిగి కొనసాగిస్తుంది "
+"మరియు నిలిపివేస్తుంది."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Which keyboard to use"
+msgstr "ఏ కీబోర్డ్ ఉపయోగించాలి"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "The type of keyboard to use."
+msgstr "ఉపయోగించుటకు కీబోర్డ్ రకము."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "Limit switcher to current workspace."
+msgstr "మార్పకాన్ని ప్రస్తుత కార్యక్షేత్రానికి పరిమితించు"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"ఒకవేళ నిజమైతే, ప్రస్తుత కార్యక్షేత్రంలో ఉన్న అనువర్తనాల కిటికీలు మాత్రమే "
+"మార్పకంలో చూపించబడతాయి. లేదంటే, "
+"అన్ని అనువర్తనాలు ఉంచబడతాయి."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The application icon mode."
+msgstr "అనువర్తనం ప్రతిమ రీతి."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"స్విచర్ నందు కిటికీలు ఎలా చూపునో ఆకృతీకరించును. చెల్లునటువంటి సాధ్యాలు "
+"'thumbnail-only' (విండో "
+"థంబ్‌నెయిల్ చూపును), 'app-icon-only' (అనువర్తన ప్రతిమ మాత్రమే చూపును) లేదా "
+"'both'."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"ఒకవేళ నిజమైతే, ప్రస్తుత కార్యక్షేత్రంలో ఉన్న కిటికీలు మాత్రమే మార్పకంలో "
+"చూపించబడతాయి. లేదంటే, అన్ని కిటికీలు "
+"ఉంచబడతాయి."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "Attach modal dialog to the parent window"
+msgstr "పేరెంట్ కిటికీకు మోడల్ డైలాగ్ జోడించు"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"గ్నోమ్ షెల్ నందు నడుచునప్పుడు ఈ కీ org.gnome.mutter నందలి కీను వోవర్‌రైడ్ "
+"చేయును."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "తెర అంచులనందు విండోలను విడువునప్పుడు ఎడ్జ్ టైటిలింగ్ చేతనం చేయి"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Workspaces are managed dynamically"
+msgstr "కార్యక్షేత్రాలు గతికంగా నిర్వహించబడును"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid "Workspaces only on primary monitor"
+msgstr "ప్రాధమిక మానిటర్ పైని కార్యక్షేత్రాలు మాత్రమే"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "మౌసు రీతిలో కేంద్రీకరణ మార్పులను పాయింటరు కదలిక ఆగే వరకూ ఆలస్యం చేయి"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+msgid "Captive Portal"
+msgstr "కాప్టివ్ పోర్టల్"
+
+#: ../js/extensionPrefs/main.js:123
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s కొరకు ప్రాధాన్యతల సంభాషణను నింపుటలో అక్కడ ఒక దోషం ఉన్నది:"
+
+#: ../js/extensionPrefs/main.js:155
+msgid "GNOME Shell Extensions"
+msgstr "గ్నోమ్ షెల్ పొడిగింతలు"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:143
+#: ../js/ui/components/polkitAgent.js:166 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:915
+msgid "Cancel"
+msgstr "రద్దుచేయి"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:217
+msgid "Next"
+msgstr "తరువాత"
+
+#: ../js/gdm/authPrompt.js:213 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "తాళంతీయి"
+
+#: ../js/gdm/authPrompt.js:215
+msgctxt "button"
+msgid "Sign In"
+msgstr "ప్రవేశించండి"
+
+#: ../js/gdm/loginDialog.js:269
+msgid "Choose Session"
+msgstr "ఘట్టాన్ని ఎంచుకోండి"
+
+#: ../js/gdm/loginDialog.js:429
+msgid "Not listed?"
+msgstr "జాబితా చేయలేదా?"
+
+#: ../js/gdm/loginDialog.js:614
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ఉదా., వాడుకరి లేదా %s)"
+
+#: ../js/gdm/loginDialog.js:619 ../js/ui/components/networkAgent.js:269
+#: ../js/ui/components/networkAgent.js:287
+msgid "Username: "
+msgstr "వాడుకరిపేరు: "
+
+#: ../js/gdm/loginDialog.js:922
+msgid "Login Window"
+msgstr "ప్రవేశ కిటికీ"
+
+#: ../js/gdm/util.js:323
+msgid "Authentication error"
+msgstr "ధృవీకరణ దోషం"
+
+#: ../js/gdm/util.js:453
+msgid "(or swipe finger)"
+msgstr "(లేదా వేలుతో స్వైప్‌చేయి)"
+
+#: ../js/misc/util.js:115
+msgid "Command not found"
+msgstr "ఆదేశము కనపడలేదు"
+
+#: ../js/misc/util.js:148
+msgid "Could not parse command:"
+msgstr "ఆదేశము పదనిరూపణ వీలుకాదు:"
+
+#: ../js/misc/util.js:156
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” అమలు విఫలమైంది:"
+
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "జాల ధృవీకరణ దారిమార్పు"
+
+#: ../js/ui/appDisplay.js:772
+msgid "Frequently used applications will appear here"
+msgstr "తరచూ వాడే అనువర్తనాలు ఇక్కడ కనిపిస్తాయి"
+
+#: ../js/ui/appDisplay.js:883
+msgid "Frequent"
+msgstr "తరచు"
+
+#: ../js/ui/appDisplay.js:890
+msgid "All"
+msgstr "అన్ని"
+
+#: ../js/ui/appDisplay.js:1790
+msgid "New Window"
+msgstr "కొత్త కిటికీ"
+
+#: ../js/ui/appDisplay.js:1816 ../js/ui/dash.js:285
+msgid "Remove from Favorites"
+msgstr "ఇష్టాంశాల నుండి తొలగించు"
+
+#: ../js/ui/appDisplay.js:1822
+msgid "Add to Favorites"
+msgstr "ఇష్టాంశాలకు జతచేయి"
+
+#: ../js/ui/appDisplay.js:1831
+msgid "Show Details"
+msgstr "వివరాలను చూపించు"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s మీ ఇష్టాంశాలకు జతచేయబడింది."
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s మీ ఇష్టాంశాల నుండి తీసివేయబడింది."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/panel.js:813
+#: ../js/ui/status/system.js:337
+msgid "Settings"
+msgstr "అమరికలు"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "నేపథ్యాన్ని మార్చు..."
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#. */
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "All Day"
+msgstr "రోజు మొత్తం"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to : */
+#: ../js/ui/calendar.js:73
+msgctxt "event list time"
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space */
+#: ../js/ui/calendar.js:82
+msgctxt "event list time"
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#. */
+#: ../js/ui/calendar.js:113
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ఆ"
+
+#. Translators: Calendar grid abbreviation for Monday */
+#: ../js/ui/calendar.js:115
+msgctxt "grid monday"
+msgid "M"
+msgstr "సో"
+
+#. Translators: Calendar grid abbreviation for Tuesday */
+#: ../js/ui/calendar.js:117
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "మ"
+
+#. Translators: Calendar grid abbreviation for Wednesday */
+#: ../js/ui/calendar.js:119
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "బు"
+
+#. Translators: Calendar grid abbreviation for Thursday */
+#: ../js/ui/calendar.js:121
+msgctxt "grid thursday"
+msgid "T"
+msgstr "గు"
+
+#. Translators: Calendar grid abbreviation for Friday */
+#: ../js/ui/calendar.js:123
+msgctxt "grid friday"
+msgid "F"
+msgstr "శు"
+
+#. Translators: Calendar grid abbreviation for Saturday */
+#: ../js/ui/calendar.js:125
+msgctxt "grid saturday"
+msgid "S"
+msgstr "శ"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#. */
+#: ../js/ui/calendar.js:138
+msgctxt "list sunday"
+msgid "Su"
+msgstr "ఆది"
+
+#. Translators: Event list abbreviation for Monday */
+#: ../js/ui/calendar.js:140
+msgctxt "list monday"
+msgid "M"
+msgstr "సోమ"
+
+#. Translators: Event list abbreviation for Tuesday */
+#: ../js/ui/calendar.js:142
+msgctxt "list tuesday"
+msgid "T"
+msgstr "మం"
+
+#. Translators: Event list abbreviation for Wednesday */
+#: ../js/ui/calendar.js:144
+msgctxt "list wednesday"
+msgid "W"
+msgstr "బుధ"
+
+#. Translators: Event list abbreviation for Thursday */
+#: ../js/ui/calendar.js:146
+msgctxt "list thursday"
+msgid "Th"
+msgstr "గురు"
+
+#. Translators: Event list abbreviation for Friday */
+#: ../js/ui/calendar.js:148
+msgctxt "list friday"
+msgid "F"
+msgstr "శుక్ర"
+
+#. Translators: Event list abbreviation for Saturday */
+#: ../js/ui/calendar.js:150
+msgctxt "list saturday"
+msgid "S"
+msgstr "శని"
+
+#: ../js/ui/calendar.js:453
+msgid "Previous month"
+msgstr "మునుపటి నెల"
+
+#: ../js/ui/calendar.js:463
+msgid "Next month"
+msgstr "తదుపరి నెల"
+
+#. Translators: Text to show if there are no events */
+#: ../js/ui/calendar.js:781
+msgid "Nothing Scheduled"
+msgstr "ఏ ప్రణాళిక లేదు"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year */
+#: ../js/ui/calendar.js:799
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year */
+#: ../js/ui/calendar.js:802
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: ../js/ui/calendar.js:813
+msgid "Today"
+msgstr "నేడు"
+
+#: ../js/ui/calendar.js:817
+msgid "Tomorrow"
+msgstr "రేపు"
+
+#: ../js/ui/calendar.js:828
+msgid "This week"
+msgstr "ఈ వారం"
+
+#: ../js/ui/calendar.js:836
+msgid "Next week"
+msgstr "తరువాత వారం"
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "బాహ్య డ్రైవు అనుసంధానమైంది"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "బాహ్య డ్రైవ్ నిరనుసంధానించబడింది"
+
+#: ../js/ui/components/autorunManager.js:296
+msgid "Removable Devices"
+msgstr "తీసివేయదగు పరికరాలు"
+
+#: ../js/ui/components/autorunManager.js:596
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s‍తో తెరువు"
+
+#: ../js/ui/components/autorunManager.js:622
+msgid "Eject"
+msgstr "బయటకునెట్టు"
+
+#: ../js/ui/components/keyring.js:94 ../js/ui/components/polkitAgent.js:285
+msgid "Password:"
+msgstr "సంకేతపదం:"
+
+#: ../js/ui/components/keyring.js:120
+msgid "Type again:"
+msgstr "మళ్ళీ టంకించండి:"
+
+#: ../js/ui/components/networkAgent.js:138 ../js/ui/status/network.js:277
+#: ../js/ui/status/network.js:359 ../js/ui/status/network.js:918
+msgid "Connect"
+msgstr "అనుసంధానించు"
+
+#: ../js/ui/components/networkAgent.js:231
+#: ../js/ui/components/networkAgent.js:243
+#: ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:291
+#: ../js/ui/components/networkAgent.js:301
+msgid "Password: "
+msgstr "సంకేతపదం:"
+
+#: ../js/ui/components/networkAgent.js:236
+msgid "Key: "
+msgstr "మీట: "
+
+#: ../js/ui/components/networkAgent.js:275
+msgid "Identity: "
+msgstr "గుర్తింపు: "
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Private key password: "
+msgstr "వ్యక్తిగత కీ సంకేతపదం: "
+
+#: ../js/ui/components/networkAgent.js:289
+msgid "Service: "
+msgstr "సేవ: "
+
+#: ../js/ui/components/networkAgent.js:318
+msgid "Authentication required by wireless network"
+msgstr "వైర్‌లెస్ నెట్‌వర్క్ చేత ధృవీకరణ అవసరమైంది"
+
+#: ../js/ui/components/networkAgent.js:319
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"నిస్తంత్రి నెట్‌వర్కు “%s” ప్రాప్యించుటకు సంకేతపదాలు లేదా ఎన్క్రిప్షన్ మీటలు "
+"అవసరం."
+
+#: ../js/ui/components/networkAgent.js:323
+msgid "Wired 802.1X authentication"
+msgstr "వైర్డ్ 802.1X ధృవీకరణ"
+
+#: ../js/ui/components/networkAgent.js:325
+msgid "Network name: "
+msgstr "నెట్‍‌వర్క్ పేరు: "
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "DSL authentication"
+msgstr "DSL ధృవీకరణ"
+
+#: ../js/ui/components/networkAgent.js:337
+msgid "PIN code required"
+msgstr "PIN కోడ్ అవసరమైంది"
+
+#: ../js/ui/components/networkAgent.js:338
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "మొబైల్ బ్రాడ్‌బాండ్ పరికరం కొరకు PIN కోడ్ అవసరమైంది"
+
+#: ../js/ui/components/networkAgent.js:339
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:345
+msgid "Mobile broadband network password"
+msgstr "మొబైల్ బ్రాడ్‌బాండ్ నెట్వర్క్ సంకేతపదం"
+
+#: ../js/ui/components/networkAgent.js:346
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s”కు అనుసంధానమగుటకు సంకేతపదం అవసరం."
+
+#: ../js/ui/components/polkitAgent.js:54
+msgid "Authentication Required"
+msgstr "ధృవీకరణ అవసరం"
+
+#: ../js/ui/components/polkitAgent.js:96
+msgid "Administrator"
+msgstr "నిర్వాహకుడు"
+
+#: ../js/ui/components/polkitAgent.js:175
+msgid "Authenticate"
+msgstr "ధృవీకరించు"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance. */
+#: ../js/ui/components/polkitAgent.js:271 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "క్షమించండి, అది పనిచేయలేదు. దయచేసి మరలా ప్రయత్నించండి."
+
+#: ../js/ui/components/telepathyClient.js:240
+msgid "Invitation"
+msgstr "ఆహ్వానం"
+
+#: ../js/ui/components/telepathyClient.js:300
+msgid "Call"
+msgstr "కాల్"
+
+#: ../js/ui/components/telepathyClient.js:316
+msgid "File Transfer"
+msgstr "దస్త్ర బదిలీ"
+
+#: ../js/ui/components/telepathyClient.js:420
+msgid "Chat"
+msgstr "సంభాషణ"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Unmute"
+msgstr "శబ్దం"
+
+#: ../js/ui/components/telepathyClient.js:483
+msgid "Mute"
+msgstr "నిశబ్దం"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:953
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:960
+msgid "Yesterday, %H∶%M"
+msgstr "నిన్న, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30" */
+#: ../js/ui/components/telepathyClient.js:967
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30" */
+#: ../js/ui/components/telepathyClient.js:974
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30" */
+#: ../js/ui/components/telepathyClient.js:980
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 24h format */
+#: ../js/ui/components/telepathyClient.js:986
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:993
+msgid "Yesterday, %l∶%M %p"
+msgstr "నిన్న, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1000
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm" */
+#: ../js/ui/components/telepathyClient.js:1007
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"*/
+#: ../js/ui/components/telepathyClient.js:1013
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name. */
+#: ../js/ui/components/telepathyClient.js:1045
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s ఇప్పుడు %s గా తెలుసు"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1149
+#, javascript-format
+msgid "Invitation to %s"
+msgstr "%sకు ఆహ్వానం"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example. */
+#: ../js/ui/components/telepathyClient.js:1157
+#, javascript-format
+msgid "%s is inviting you to join %s"
+msgstr "%s మిమ్ములను %s పై చేరుటకు ఆహ్వానిస్తోంది"
+
+#: ../js/ui/components/telepathyClient.js:1159
+#: ../js/ui/components/telepathyClient.js:1194
+#: ../js/ui/components/telepathyClient.js:1228
+#: ../js/ui/components/telepathyClient.js:1286
+msgid "Decline"
+msgstr "తిరస్కరించు"
+
+#: ../js/ui/components/telepathyClient.js:1165
+#: ../js/ui/components/telepathyClient.js:1234
+#: ../js/ui/components/telepathyClient.js:1291
+msgid "Accept"
+msgstr "ఆమోదించు"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1184
+#, javascript-format
+msgid "Video call from %s"
+msgstr "%s నుండి వీడియో కాల్"
+
+#. translators: argument is a contact name like Alice for example. */
+#: ../js/ui/components/telepathyClient.js:1187
+#, javascript-format
+msgid "Call from %s"
+msgstr "%s నుండి కాల్"
+
+#. translators: this is a button label (verb), not a noun */
+#: ../js/ui/components/telepathyClient.js:1201
+msgid "Answer"
+msgstr "సమాధానమివ్వండి"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#. */
+#: ../js/ui/components/telepathyClient.js:1222
+#, javascript-format
+msgid "%s is sending you %s"
+msgstr "%s మీకు %s పంపుచున్నది"
+
+#. To translators: The parameter is the contact's alias */
+#: ../js/ui/components/telepathyClient.js:1251
+#, javascript-format
+msgid "%s would like permission to see when you are online"
+msgstr "మీరు ఆన్‌లైన్‌లో వున్నప్పుడు చూడుటకు %s అనుమతి కోరుతున్నారు"
+
+#: ../js/ui/components/telepathyClient.js:1337
+msgid "Network error"
+msgstr "నెట్‍‌వర్క్ దోషం"
+
+#: ../js/ui/components/telepathyClient.js:1339
+msgid "Authentication failed"
+msgstr "ధృవీకరణ వైఫల్యం"
+
+#: ../js/ui/components/telepathyClient.js:1341
+msgid "Encryption error"
+msgstr "ఎన్క్రిప్షన్ దోషం"
+
+#: ../js/ui/components/telepathyClient.js:1343
+msgid "Certificate not provided"
+msgstr "ధృవీకరణపత్రం అందించబడలేదు"
+
+#: ../js/ui/components/telepathyClient.js:1345
+msgid "Certificate untrusted"
+msgstr "ధృవీకరణపత్రం నమ్మలేనిది"
+
+#: ../js/ui/components/telepathyClient.js:1347
+msgid "Certificate expired"
+msgstr "ధృవీకరణపత్రం గడువుతీరెను"
+
+#: ../js/ui/components/telepathyClient.js:1349
+msgid "Certificate not activated"
+msgstr "ధృవీకరణపత్రం క్రియాశీలపరచబడిలేదు"
+
+#: ../js/ui/components/telepathyClient.js:1351
+msgid "Certificate hostname mismatch"
+msgstr "ధృవీకరణపత్రం హోస్టుపేరు సరిపోలలేదు"
+
+#: ../js/ui/components/telepathyClient.js:1353
+msgid "Certificate fingerprint mismatch"
+msgstr "ధృవీకరణపత్రం వ్రేలిముద్ర సరిపోలలేదు"
+
+#: ../js/ui/components/telepathyClient.js:1355
+msgid "Certificate self-signed"
+msgstr "ధృవీకరణపత్రం స్వతహాగా-సంతకంచేయబడెను"
+
+#: ../js/ui/components/telepathyClient.js:1357
+msgid "Status is set to offline"
+msgstr "స్థితి ఆఫ్‌లైన్‌కు అమర్చబడెను"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "Encryption is not available"
+msgstr "ఎన్క్రిప్షన్ అందుబాటులో లేదు"
+
+#: ../js/ui/components/telepathyClient.js:1361
+msgid "Certificate is invalid"
+msgstr "ధృవీకరణపత్రం చెల్లనిది"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Connection has been refused"
+msgstr "అనుసంధానము తిరస్కరించబడెను"
+
+#: ../js/ui/components/telepathyClient.js:1365
+msgid "Connection can't be established"
+msgstr "అనుసంధానం ఏర్పరచలేము"
+
+#: ../js/ui/components/telepathyClient.js:1367
+msgid "Connection has been lost"
+msgstr "అనుసంధానము కోల్పోయాము"
+
+#: ../js/ui/components/telepathyClient.js:1369
+msgid "This account is already connected to the server"
+msgstr "ఈ ఖాతా ఇదివరకే సేవకానికి అనుసంధానమైంది"
+
+#: ../js/ui/components/telepathyClient.js:1371
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "అనుసంధానం అదే వనరు ఉపయోగించి కొత్త అనుసంధానంచే పునఃస్థాపించబడెను"
+
+#: ../js/ui/components/telepathyClient.js:1373
+msgid "The account already exists on the server"
+msgstr "ఖాతా ఇప్పటికే సేవికపైన ఉన్నది"
+
+#: ../js/ui/components/telepathyClient.js:1375
+msgid "Server is currently too busy to handle the connection"
+msgstr "అనుసంధానంను సంభాలించుటకు సేవిక మరీ వత్తిడిలో వుంది"
+
+#: ../js/ui/components/telepathyClient.js:1377
+msgid "Certificate has been revoked"
+msgstr "ధృవీకరణపత్రం కొట్టివేయబడింది"
+
+#: ../js/ui/components/telepathyClient.js:1379
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr ""
+"ధృవీకరణపత్రం సురక్షితం కాని సైఫర్ ఆల్గార్దెమ్ వుపయోగించుచున్నది లేదా "
+"క్రిప్టోగ్రఫీ పరంగా బలహీనంగా వుంది"
+
+#: ../js/ui/components/telepathyClient.js:1381
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"సేవిక ధృవీకరణపత్రం యొక్క పొడవు, లేదా సేవిక ధృవీకరణపత్రం చైన్ యొక్క లోతు, "
+"క్రిప్టోగ్రఫీ లైబ్రరీ చేత "
+"నిర్దేశితమైన పరిమితులను మించును"
+
+#: ../js/ui/components/telepathyClient.js:1383
+msgid "Internal error"
+msgstr "అంతర్గత దోషం"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example. */
+#: ../js/ui/components/telepathyClient.js:1393
+#, javascript-format
+msgid "Unable to connect to %s"
+msgstr "%s కు అనుసంధానం కాలేకపోయింది"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "View account"
+msgstr "ఖాతాను చూడండి"
+
+#: ../js/ui/components/telepathyClient.js:1435
+msgid "Unknown reason"
+msgstr "తెలియని కారణం"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:154
+msgid "Windows"
+msgstr "కిటికీలు"
+
+#: ../js/ui/dash.js:249 ../js/ui/dash.js:287
+msgid "Show Applications"
+msgstr "అనువర్తనాలను చూపించు"
+
+#: ../js/ui/dash.js:445
+msgid "Dash"
+msgstr "గీటు"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Calendar"
+msgstr "క్యాలెండరు తెరువు"
+
+#: ../js/ui/dateMenu.js:100
+msgid "Open Clocks"
+msgstr "గడియారాలు తెరువు"
+
+#: ../js/ui/dateMenu.js:107
+msgid "Date & Time Settings"
+msgstr "తేదీ మరియు సమయం అమరికలు"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#. */
+#: ../js/ui/dateMenu.js:204
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e, %Y"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s నిష్క్రమించు"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "నిష్క్రమించు"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s స్వయంచాలకంగా %d సెకనులో నిష్క్రమించును."
+msgstr[1] "%s స్వయంచాలకంగా %d సెకనులలో నిష్క్రమించును."
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "మీరు %d సెకనులో స్వయంచాలకంగా నిష్క్రమిస్తారు."
+msgstr[1] "మీరు %d సెకనులలో స్వయంచాలకంగా నిష్క్రమిస్తారు."
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "నిష్క్రమించు"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "విద్యుత్ ఆపు"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "నవీకరణలను స్థాపించి, విద్యుత్ ఆపు"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "%d సెకన్లలో వ్యవస్థ స్వయంచాలకంగా ఆపివేయబడుతుంది."
+msgstr[1] "%d సెకన్లలో వ్యవస్థ స్వయంచాలకంగా ఆపివేయబడుతుంది."
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "పెండింగులో ఉన్న సాఫ్ట్‌వేర్ నవీకరణలను స్థాపించు"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "పునఃప్రారంభించు"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "విద్యుత్ ఆపు"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "పునఃప్రారంభించు"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "%d సెకనులో వ్యవస్థ స్వయంచాలకంగా పునఃప్రారంభించబడుతుంది."
+msgstr[1] "%d సెకనులలో వ్యవస్థ స్వయంచాలకంగా పునఃప్రారంభించబడుతుంది."
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "నవీకరణలను స్థాపించి, పునఃప్రారంభించు"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"%d క్షణాలలో నవీకరణలను స్థాపించి, వ్యవస్థ స్వయంచాలకంగా పునఃప్రారంభించబడుతుంది."
+msgstr[1] ""
+"%d క్షణాలలో నవీకరణలను స్థాపించి, వ్యవస్థ స్వయంచాలకంగా పునఃప్రారంభించబడుతుంది."
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "పునఃప్రారంభించి &amp; స్థాపించు"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "స్థాపించి &amp; విద్యుత్ ఆపు"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "నవీకరణలు స్థాపించిన తరువాత విద్యుత్ ఆపు"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"బ్యాటరీ శక్తితో నడుస్తూంది: దయచేసి నవీకరణలు స్థాపించే ముందు విద్యుత్ "
+"అనుసంధానించండి."
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "కొన్ని అనువర్తనాలు బిజీగా ఉన్నాయి లేదా భద్రపరుచని పని ఉండిపోయింది."
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "వేరే వాడుకరులు లాగిన్ అయివున్నారు."
+
+#. Translators: Remote here refers to a remote session, like a ssh login */
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (రిమోట్)"
+
+#. Translators: Console here refers to a tty like a VT console */
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (కన్సోల్)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "స్థాపించు"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "extensions.gnome.org నుండి “%s” దింపుకొని, స్థాపించాలా?"
+
+#: ../js/ui/keyboard.js:692 ../js/ui/status/keyboard.js:523
+msgid "Keyboard"
+msgstr "కీబోర్డు"
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "ఏ పొడిగింతలు స్థాపించబడిలేవు"
+
+#. Translators: argument is an extension UUID. */
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ఎటువంటి దోషాలను ఉద్గారించలేదు."
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "దోషాలను దాయి"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "దోషాలను చూపించు"
+
+#: ../js/ui/lookingGlass.js:716 ../js/ui/status/location.js:71
+#: ../js/ui/status/location.js:176
+msgid "Enabled"
+msgstr "చేతనమైనది"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode) */
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../js/ui/status/location.js:179
+#: ../js/ui/status/network.js:592 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "అచేతనమైనది"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "దోషం"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "కాలం చెల్లినది"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "దింపుకుంటున్నది"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "మూలాన్ని చూడు"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "జాల పుట"
+
+#: ../js/ui/messageTray.js:1327
+msgid "Open"
+msgstr "తెరువు"
+
+#: ../js/ui/messageTray.js:1334
+msgid "Remove"
+msgstr "తీసివేయి"
+
+#: ../js/ui/messageTray.js:1631
+msgid "Notifications"
+msgstr "ప్రకటనలు"
+
+#: ../js/ui/messageTray.js:1638
+msgid "Clear Messages"
+msgstr "సందేశాలు తుడిచివేయి"
+
+#: ../js/ui/messageTray.js:1657
+msgid "Notification Settings"
+msgstr "ప్రకటనల అమరికలు"
+
+#: ../js/ui/messageTray.js:1710
+msgid "Tray Menu"
+msgstr "పళ్ళెం జాబితా"
+
+#: ../js/ui/messageTray.js:1934
+msgid "No Messages"
+msgstr "సందేశాలు లేవు"
+
+#: ../js/ui/messageTray.js:1979
+msgid "Message Tray"
+msgstr "సందేశ పళ్ళెం"
+
+#: ../js/ui/messageTray.js:2992
+msgid "System Information"
+msgstr "వ్యవస్థ సమాచారం"
+
+#: ../js/ui/notificationDaemon.js:513 ../src/shell-app.c:425
+msgctxt "program"
+msgid "Unknown"
+msgstr "తెలియదు"
+
+#: ../js/ui/overviewControls.js:482 ../js/ui/screenShield.js:151
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d కొత్త సందేశం"
+msgstr[1] "%d కొత్త సందేశాలు"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "రద్దుచేయి"
+
+#: ../js/ui/overview.js:124
+msgid "Overview"
+msgstr "పర్యావలోకనం"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters. */
+#: ../js/ui/overview.js:246
+msgid "Type to search…"
+msgstr "వెతకడానికి టైపు చేయండి…"
+
+#: ../js/ui/panel.js:515
+msgid "Quit"
+msgstr "నిష్క్రమించు"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview". */
+#: ../js/ui/panel.js:567
+msgid "Activities"
+msgstr "కార్యకలాపాలు"
+
+#: ../js/ui/panel.js:918
+msgid "Top Bar"
+msgstr "పైన పట్టీ"
+
+#: ../js/ui/popupMenu.js:269
+msgid "toggle-switch-us"
+msgstr "టోగిల్-స్విచ్-అజ్"
+
+#: ../js/ui/runDialog.js:70
+msgid "Enter a Command"
+msgstr "ఒక ఆదేశమును ప్రవేశపెట్టండి"
+
+#: ../js/ui/runDialog.js:110 ../js/ui/windowMenu.js:120
+msgid "Close"
+msgstr "మూసివేయి"
+
+#: ../js/ui/runDialog.js:277
+msgid "Restarting…"
+msgstr "పునఃప్రారంభిస్తున్నాము…"
+
+#. Translators: This is a time format for a date in
+#. long format */
+#: ../js/ui/screenShield.js:88
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: ../js/ui/screenShield.js:153
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d కొత్త గమనింపు"
+msgstr[1] "%d కొత్త గమనింపులు"
+
+#: ../js/ui/screenShield.js:472 ../js/ui/status/system.js:345
+msgid "Lock"
+msgstr "తాళంవేయి"
+
+#: ../js/ui/screenShield.js:706
+msgid "GNOME needs to lock the screen"
+msgstr "గ్నోమ్ తెరకు తాళం వేయవలసివుంది"
+
+#: ../js/ui/screenShield.js:833 ../js/ui/screenShield.js:1304
+msgid "Unable to lock"
+msgstr "తాళం వేయలేకపోతూంది"
+
+#: ../js/ui/screenShield.js:834 ../js/ui/screenShield.js:1305
+msgid "Lock was blocked by an application"
+msgstr "తాళం ఒక అనువర్తనం చేత నిరోధించబడింది"
+
+#: ../js/ui/search.js:594
+msgid "Searching…"
+msgstr "వెతుకుతోంది..."
+
+#: ../js/ui/search.js:596
+msgid "No results."
+msgstr "ఏ ఫలితాలు లేవు."
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "నకలించు"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "అతికించు"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "పాఠం చూపించు"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "పాఠ్యం దాయి"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "సంకేతపదం"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "సంకేతపదాన్ని గుర్తుంచుకొను"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "ప్రాపనీయత"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "జూమ్"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "తెర చదువకం"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "తెర కీబోర్డు"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "దృశ్యమాన అప్రమత్తతలు"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "స్టికీ కీలు"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "నెమ్మది కీలు"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "బౌన్స్ కీలు"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "మౌస్ కీలు"
+
+#: ../js/ui/status/accessibility.js:144
+msgid "High Contrast"
+msgstr "అధిక వ్యత్యాసం"
+
+#: ../js/ui/status/accessibility.js:193
+msgid "Large Text"
+msgstr "అతిపెద్ద పాఠ్యము"
+
+#: ../js/ui/status/bluetooth.js:49
+msgid "Bluetooth"
+msgstr "బ్లూటూత్"
+
+#: ../js/ui/status/bluetooth.js:51 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:360 ../js/ui/status/network.js:1281
+#: ../js/ui/status/network.js:1392 ../js/ui/status/rfkill.js:86
+#: ../js/ui/status/rfkill.js:114
+msgid "Turn Off"
+msgstr "చేతనించు"
+
+#: ../js/ui/status/bluetooth.js:54
+msgid "Bluetooth Settings"
+msgstr "బ్లూటూత్ అమరికలు"
+
+#: ../js/ui/status/bluetooth.js:104
+#, javascript-format
+msgid "%d Connected Device"
+msgid_plural "%d Connected Devices"
+msgstr[0] "%d అనుసంధానించబడిన పరికరం"
+msgstr[1] "%d అనుసంధానించబడిన పరికరాలు"
+
+#: ../js/ui/status/bluetooth.js:106 ../js/ui/status/network.js:1309
+msgid "Not Connected"
+msgstr "అనుసంధానించబడలేదు"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "ప్రకాశత"
+
+#: ../js/ui/status/keyboard.js:547
+msgid "Show Keyboard Layout"
+msgstr "కీబోర్డు నమూనాను చూపించు"
+
+#: ../js/ui/status/location.js:65
+msgid "Location"
+msgstr "స్థానం"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:177
+msgid "Disable"
+msgstr "అచేతనించు"
+
+#: ../js/ui/status/location.js:73
+#| msgid "Power Settings"
+msgid "Privacy Settings"
+msgstr "గోప్యతా అమరికలు"
+
+#: ../js/ui/status/location.js:176
+msgid "In Use"
+msgstr "వాడుకలో వుంది"
+
+#: ../js/ui/status/location.js:180
+msgid "Enable"
+msgstr "చేతనించు"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<తెలియదు>"
+
+#: ../js/ui/status/network.js:457 ../js/ui/status/network.js:1307
+#: ../js/ui/status/network.js:1511
+msgid "Off"
+msgstr "అచేతనం"
+
+#: ../js/ui/status/network.js:459
+msgid "Connected"
+msgstr "అనుసంధానమైంది"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu) */
+#: ../js/ui/status/network.js:463
+msgid "Unmanaged"
+msgstr "అస్థవ్యస్థం"
+
+#: ../js/ui/status/network.js:465
+msgid "Disconnecting"
+msgstr "అననుసంధానిస్తున్నాము"
+
+#: ../js/ui/status/network.js:471 ../js/ui/status/network.js:1301
+msgid "Connecting"
+msgstr "అనుసంధానిస్తూంది"
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:474
+msgid "Authentication required"
+msgstr "ధృవీకరణ అవసరం"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing */
+#: ../js/ui/status/network.js:482
+msgid "Firmware missing"
+msgstr "ఫిర్మ్​వేర్ తప్పిపోయినది"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage */
+#: ../js/ui/status/network.js:486
+msgid "Unavailable"
+msgstr "అందుబాటులోలేదు"
+
+#: ../js/ui/status/network.js:488 ../js/ui/status/network.js:1695
+msgid "Connection failed"
+msgstr "అనుసంధానం విఫలమైంది"
+
+#: ../js/ui/status/network.js:504
+msgid "Wired Settings"
+msgstr "తంత్రి అమరికలు"
+
+#: ../js/ui/status/network.js:546 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "మొబైల్ బ్రాడ్‌బాండ్ అమరికలు"
+
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+msgid "Hardware Disabled"
+msgstr "హార్డువేర్ అచేతనించబడింది"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "అంతర్జాల అనుసంధానం వలె ఉపయోగించు"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "విమాన రీతి చేతనం"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "విమాన రీతి ప్రారంభించబడినప్పుడు వై-ఫై అచేతనించబడుతుంది."
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "విమాన రీతిని ఆపివేయి"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "వై-ఫై ఆపబడింది"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "నెట్‌వర్కుకు అనుసంధానం కావాలంటే వై-ఫై చేతనమైవుండాలి."
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "వై-ఫై ప్రారంభించు"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "వై-ఫై నెట్‍‌వర్కులు"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "ఒక నెట్‌వర్కును ఎంచుకోండి"
+
+#: ../js/ui/status/network.js:882
+msgid "No Networks"
+msgstr "ఏ నెట్‍‌వర్కులు లేవు"
+
+#: ../js/ui/status/network.js:903 ../js/ui/status/rfkill.js:112
+msgid "Use hardware switch to turn off"
+msgstr "ఆపివేయుటకు హార్డ్‌వేర్ మీటను వాడండి"
+
+#: ../js/ui/status/network.js:1173
+msgid "Select Network"
+msgstr "నెట్‍‌వర్క్ ఎంచుకోండి"
+
+#: ../js/ui/status/network.js:1179
+msgid "Wi-Fi Settings"
+msgstr "వై-ఫై అమరికలు"
+
+#: ../js/ui/status/network.js:1281
+msgid "Turn On"
+msgstr "అచేతనించు"
+
+#: ../js/ui/status/network.js:1298
+msgid "Hotspot Active"
+msgstr "హాట్‌స్పాట్ క్రియాశీలం"
+
+#: ../js/ui/status/network.js:1409
+msgid "connecting..."
+msgstr "అనుసంధానిస్తున్నది..."
+
+#. Translators: this is for network connections that require some kind of key or password */
+#: ../js/ui/status/network.js:1412
+msgid "authentication required"
+msgstr "ప్రమాణీకరణ అవసరం"
+
+#: ../js/ui/status/network.js:1414
+msgid "connection failed"
+msgstr "అనుసంధానం విఫలమైంది"
+
+#: ../js/ui/status/network.js:1480 ../js/ui/status/rfkill.js:89
+msgid "Network Settings"
+msgstr "నెట్‌వర్క్ అమరికలు"
+
+#: ../js/ui/status/network.js:1482
+msgid "VPN Settings"
+msgstr "VPN అమరికలు"
+
+#: ../js/ui/status/network.js:1501
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1656
+msgid "Network Manager"
+msgstr "నెట్‍‌వర్క్ నిర్వాహకము"
+
+#: ../js/ui/status/network.js:1696
+msgid "Activation of network connection failed"
+msgstr "నెట్‌వర్క్అనుసంధానం క్రియాశీలపరచుట విఫలమైంది"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "విద్యుత్ అమరికలు"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "పూర్తిగా చార్జయింది"
+
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "అంచనావేస్తున్నది…"
+
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "%d∶%02d మిగిలివున్నది (%d%%)"
+
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "%d∶%02d పూర్తయ్యేవరకు (%d%%)"
+
+#: ../js/ui/status/power.js:119
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:121
+msgid "Battery"
+msgstr "బ్యాటరీ"
+
+#: ../js/ui/status/rfkill.js:83
+msgid "Airplane Mode"
+msgstr "విమాన రీతి"
+
+#: ../js/ui/status/rfkill.js:85
+msgid "On"
+msgstr "చేతనం"
+
+#: ../js/ui/status/system.js:317
+msgid "Switch User"
+msgstr "వాడుకరిని మార్చు"
+
+#: ../js/ui/status/system.js:322
+msgid "Log Out"
+msgstr "నిష్క్రమించు"
+
+#: ../js/ui/status/system.js:341
+msgid "Orientation Lock"
+msgstr "దిశ తాళం"
+
+#: ../js/ui/status/system.js:349
+msgid "Suspend"
+msgstr "తాత్కాలికంగా నిలిపివేయి"
+
+#: ../js/ui/status/system.js:352
+msgid "Power Off"
+msgstr "విద్యుత్ ఆపు"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "ధ్వనిస్థాయి మార్చబడింది"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "ధ్వనిస్థాయి"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "మైక్రోఫోను"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "వేరొక వాడుకరి వలె ప్రవేశించండి"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "కిటికీ తాళంతీయి"
+
+#: ../js/ui/viewSelector.js:158
+msgid "Applications"
+msgstr "అనువర్తనాలు"
+
+#: ../js/ui/viewSelector.js:162
+msgid "Search"
+msgstr "వెతుకు"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” సిద్ధం"
+
+#: ../js/ui/windowManager.js:65
+msgid "Do you want to keep these display settings?"
+msgstr "ఈ ప్రదర్శనా అమరికలను ఉంచుకోవాలనుకుంటున్నారా?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#. */
+#: ../js/ui/windowManager.js:84
+msgid "Revert Settings"
+msgstr "అమరికలను తిరితిప్పు"
+
+#: ../js/ui/windowManager.js:88
+msgid "Keep Changes"
+msgstr "మార్పులను ఉంచు"
+
+#: ../js/ui/windowManager.js:107
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "%d క్షణంలో అమరికలు తిరితిప్పబడతాయి"
+msgstr[1] "%d క్షణాలలో అమరికలు తిరితిప్పబడతాయి"
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "చిన్నగించు"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "కాస్త చిన్నదిగాచేయి"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "పెద్దగించు"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "తరలించు"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "పరిమాణం మార్చు"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "శీర్షికపట్టీ పైతెరను తరలించు"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "ఎల్లప్ఫుడు పైనే"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "ఎల్లప్పుడూ కనిపిస్తున్న కార్యక్షేత్రం పైనే (_A)"
+
+#: ../js/ui/windowMenu.js:106
+msgid "Move to Workspace Up"
+msgstr "పనిచేస్తున్న చోటునుండీ పైకి వెళ్ళు"
+
+#: ../js/ui/windowMenu.js:111
+msgid "Move to Workspace Down"
+msgstr "పనిచేస్తున్న చోటునుండీ కిందకు వెళ్ళు"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "ఎవల్యూషన్ క్యాలెండరు"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u అవుట్‌పుట్"
+msgstr[1] "%u అవుట్‌పుట్‌లు"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u ఇన్‌పుట్"
+msgstr[1] "%u ఇన్‌పుట్‌లు"
+
+#: ../src/gvc/gvc-mixer-control.c:2373
+msgid "System Sounds"
+msgstr "వ్యవస్థ శబ్దములు"
+
+#: ../src/main.c:373
+msgid "Print version"
+msgstr "ముద్రిత రూపాంతరం"
+
+#: ../src/main.c:379
+msgid "Mode used by GDM for login screen"
+msgstr "ప్రవేశ తెర కొరకు GDM చే ఉపయోగించబడిన రీతి"
+
+#: ../src/main.c:385
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "ఒక నిర్దిష్ట రీతిని వాడు, ఉదా. ప్రవేశ తెర కొరకు \"gdm\""
+
+#: ../src/main.c:391
+msgid "List possible modes"
+msgstr "సాధ్యమగు రీతులను జాబితాగా చేయి"
+
+#: ../src/shell-app.c:666
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” ప్రారంభించుటలో విఫలమైంది"
+
+#: ../src/shell-keyring-prompt.c:714
+msgid "Passwords do not match."
+msgstr "సంకేతపదాలు సరిపోలడంలేదు."
+
+#: ../src/shell-keyring-prompt.c:722
+msgid "Password cannot be blank"
+msgstr "సంకేతపదము ఖాళీగా ఉండకూడదు"
+
+#: ../src/shell-polkit-authentication-agent.c:346
+msgid "Authentication dialog was dismissed by the user"
+msgstr "ప్రమాణీకరణ సంవాదం వాడుకరిచే రద్దుచేయబడింది"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "స్థానం యొక్క గరిష్ఠ ఖచ్ఛితత్వ స్థాయి."
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "గరిష్ట స్థాయిలో స్థానం ఖచ్చితత్వ అనువర్తనాలు చూడటానికి అనుమతించుటను స్వరూపిస్తుంది. చెల్లుబాటు "
+#~ "అయ్యే ఐచ్ఛికాలు 'ఆపివేయి' (స్థానం జాడ అచేతనం), 'దేశం', 'పట్టణం', 'పొరుగు', 'వీధి', 'మరియు "
+#~ "'ఖచ్చితం' (సాధారణంగా GPS రిసీవర్ అవసరం) ఉంటాయి. ఇది కేవలం జియోక్లూ అనువర్తనాలకు ఏమైతే చూడటానికి "
+#~ "అనుమతినిస్తుందో వాటిని మాత్రమే నియంత్రిస్తుంది మరియు అవి వాడుకరి స్థానాన్ని వాటి స్వంతంగా నెట్‌వర్కు "
+#~ "వనరులను ఉపయోగించి కనుక్కోగలవని గమనించగలరు (ఆల్బెట్‌తో వీధి స్థాయి కచ్చితత్వం ఉత్తమం)"
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "శీర్షికపట్టీ పై బటన్స్ యొక్క పేర్పు"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "గ్నోమ్ షెల్ నందు నడుచునప్పుడు ఈ కీ org.gnome.desktop.wm.preferences నందలి కీను "
+#~ "వోవర్‌రైడ్ చేయును."
+
+#~ msgid "Extension"
+#~ msgstr "పొడిగింత"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "పైన ఉన్న కోంబోపెట్టెను వాడి స్వరూపించుటకు అనువర్తనాన్ని ఎంచుకోండి."
+
+#~ msgid "calendar:MY"
+#~ msgstr "క్యాలెండరు:నా"
+
+#~ msgid "Screenshots"
+#~ msgstr "తెరపట్టులు"
+
+#~ msgid "Record a screencast"
+#~ msgstr "తెరప్రసారాన్ని రికార్డించు"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "అనువర్తనాలు వాడుక గురించిన గణాంకాలను సేకరించాలా వద్దా"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "ఎక్కువగా వాడిన వాటిని సమర్పించడానికి షెల్ సాధారణంగా క్రియాశీల అనువర్తనాలను పర్యవేక్షిస్తుంది (ఉదా. "
+#~ "ప్రారంభకాలలో). అపుడు ఈ డేటా గోప్యంగా ఉంచబడుతుంది, గోప్యతా కారణాల వలన మీరు దీనిని "
+#~ "అచేతనపరచాలనుకోవచ్చు. దయచేసి గుర్తుంచుకోండి ఇలా చేయడం వలన ఇదివరకే దాచబడిన డేటా తీసివేయబడదు."
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "సంచయాల వలె ప్రదర్శించబడవలసిన వర్గముల జాబితా"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "ఈ జాబితానందలి ప్రతి వర్గపు పేరు ముఖ్య దర్శనం నందు యిన్‌లైన్‌గా ప్రదర్శించబడుట కన్నా, అనువర్తన "
+#~ "దర్శనం నందు ఫోల్డర్ వలె చూపబడును."
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "అంతర్గతంగా స్పష్టంగా వినియోగదారుని ద్వారా సెట్ చివరి IM ఉనికిని నిల్వ చేయడానికి ఉపయోగించారు. ఇక్కడ "
+#~ "విలువ Tp కనెక్షన్ ఉనికి పద్ధతి వయనము నుండి ఉంది."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "అంతర్గతంగా యూజర్ కోసం చివరి సెషన్ ఉనికిని స్థితి నిల్వ చేయడానికి ఉపయోగించారు. ఇక్కడ విలువ GSM ఉనికి "
+#~ "స్థితి వయనము నుండి ఉంది."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "తెర రికార్డరుని మార్చుటకు అడ్డదారి"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "అప్రమేయ తెర రికార్డరుని ప్రారంభించుటకు/ఆపడానికి అడ్డదారి."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "ఫ్రేమ్‌రేట్ అను దానిని తెరప్రసారాలను రికార్డు చేయుటకు వాడతారు."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "ఫలితముగా వచ్చు తెర ప్రసారము యొక్క ఫ్రేమ్ రేటు గ్నోమ్ షెల్ తెరప్రసార రికార్డర్ ఫ్రేమ్ పర్ సెకనులలో "
+#~ "రికార్డుచేయబడుతుంది."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "తెరప్రసారాన్ని ఎన్‌కోడ్ చేయడానికి జి స్ట్రీమర్ పైప్‌లైన్ వాడబడుతుంది"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "రికార్డింగులను ఎన్‌కోడ్ చేయడానికి జిస్ట్రీమర్ పైప్‌లైన్‌ను అమర్చును. ఇది gst-launch కొరకు "
+#~ "వ్యాక్యనిర్మాణాన్ని అనుసరిస్తుంది. వీడియో ఎక్కడైతే రికార్డు అవుతుందో అక్కడ ఒక అనుసంధానం కాని సింక్ "
+#~ "ప్యాడ్‌ని కలిగివుండాలి. ఇది సాధారణంగా ఒక అనుసంధానము కాని మూలము ప్యాడ్ అయివుంటుంది; ఈ ప్యాడ్ నుండి "
+#~ "వచ్చు అవుట్‌పుట్ అవుట్‌పుట్ దస్త్రంలోనికి వ్రాయబడుతుంది. ఏదిఏమైనప్పటికీ పైప్ లైన్ కూడా దాని అవుట్‌పుట్ "
+#~ "గురించి భద్రత వహిస్తుంది - ఇది అవుట్‌పుట్‌ని ఐస్‌కాస్ట్ సేవకానికి shout2send లేదా ఇటువంటి వాటి ద్వారా "
+#~ "పంపుటకు వాడబడుతుంది. అమర్చకపోయినా లేక ఒక ఖాళీ విలువకి అమర్చినా, అప్రమేయ పైప్‌లైన్ వాడబడుతుంది, "
+#~ "ఇది ప్రస్తుతం 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 "
+#~ "deadline=1000000 threads=%T ! queue ! webmmux' మరియు VP8 కొడెక్ వాడి WEBMకి "
+#~ "రికార్డుచేస్తుంది. %T అనేది వ్యవస్థ పై ఒక ప్లేస్‌హోల్డర్ వలె గ్వెస్ కొరకు ఆప్టిమల్ త్రెడ్ కౌంట్ "
+#~ "వాడబడుతుంది."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "దస్త్ర పొడిగింతను తెరప్రసారాన్ని భద్రపరుచుటకు వాడతారు"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "రికార్డుచేయబడిన తెరప్రసారాలకు దస్త్రము పేరు ప్రస్తుత తేదీ పై ఆధారపడి దస్త్రం పేరు ఒకే విధముగా "
+#~ "ఉంటుంది, మరియు ఈ పొడిగింతను వాడండి. ఇది వేరే ఇతర ఆకృతిలో రికార్డు చేస్తున్నపుడు ఇది మార్చబడాలి."
diff --git a/po/tg.po b/po/tg.po
new file mode 100644
index 0000000..41c1d3b
--- /dev/null
+++ b/po/tg.po
@@ -0,0 +1,2803 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Victor Ibragimov <victor.ibragimov@gmail.com>, 2013, 2014, 2015, 2019
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Tajik Gnome\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2019-03-02 10:57+0000\n"
+"PO-Revision-Date: 2019-03-13 21:44+0500\n"
+"Last-Translator: Victor Ibragimov <victor.ibragimov@gmail.com>\n"
+"Language-Team: \n"
+"Language: tg\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Низом"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Намоиш додани рӯйхати огоҳиҳо"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Марказонидани огоҳии фаъол"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Намоиш додани ҷамъбаст"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Намоиш додани ҳамаи барномаҳо"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Кушодани феҳристи барномаҳо"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+#: js/extensionPrefs/main.js:216
+#| msgid "GNOME Shell Extensions"
+msgid "Shell Extensions"
+msgstr "Васеъшавиҳои восит"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "Танзимоти васеъшавиҳои восити GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Восити GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Идоракунии равзанаҳо ва оғозкунии барномаҳо"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "Фаъол кардани абзорҳои дохилие, ки барои таҳиягарон ва озмунгарон дастрас мебошанд (ба воситаи Alt-F2)"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid "Allows access to internal debugging and monitoring tools using the Alt-F2 dialog."
+msgstr "Ҳамеша пайдо кардани дастрасӣ ба абзорҳои дохилии ислоҳи хатоҳо ва идоракунӣ тавассути равзанаи гуфтугӯи Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Рамзҳои UUID барои пасвандҳои фаъолшаванда"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid "GNOME Shell extensions have a UUID property; this key lists extensions which should be loaded. Any extension that wants to be loaded needs to be in this list. You can also manipulate this list with the EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr "Пасвандҳои GNOME Shell хусусияти UUID доранд; ин калид пасвандҳоеро, ки бояд бор карда шаванд, номбар мекунад. Ҳар як пасванде, ки бояд бор карда шавад, бояд дар ин рӯйхат бошад. Шумо инчунин метавонед ин рӯйхатро тавассути усулҳои DisableExtension D-Bus ва EnableExtension дар org.gnome.Shell таҳрир кунед."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disable user extensions"
+msgstr "Ғайрифаъол кардани васеъшавиҳои корбар"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid "Disable all extensions the user has enabled without affecting the “enabled-extension” setting."
+msgstr "Ҳамаи васеъшавиҳое, ки корбар фаъол кард, бе расонидани таъсир ба танзими “васеъшавиии-фаъол” ғайрифаъол карда мешаванд."
+
+#: data/org.gnome.shell.gschema.xml.in:34
+msgid "Disables the validation of extension version compatibility"
+msgstr "Тафтиши пасванди мувофиқати версияро ғайрифаъол мекунад"
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid "GNOME Shell will only load extensions that claim to support the current running version. Enabling this option will disable this check and try to load all extensions regardless of the versions they claim to support."
+msgstr "Восити GNOME танҳо пасвандҳоеро, ки версияи ҷориро дастгирӣ мекунанд, бор мекунад. Фаъолсозии ин имкон, байракчаи ҷориро ғайрифаъол мекунад ва ҳамаи пасвандҳоро новобаста аз версияҳои дастгиришаванда бор мекунад."
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Рӯйхати рамзҳои ID-и файлҳои мизи корӣ барои барномаҳои дӯстдошта"
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid "The applications corresponding to these identifiers will be displayed in the favorites area."
+msgstr "Барномаҳое, ки ба ин идентификаторҳо мувофиқ мебошанд, дар ноҳияи баргузидаҳо намоиш дода мешаванд."
+
+#: data/org.gnome.shell.gschema.xml.in:51
+msgid "App Picker View"
+msgstr "Намуди интихобкунандаи барнома"
+
+#: data/org.gnome.shell.gschema.xml.in:52
+msgid "Index of the currently selected view in the application picker."
+msgstr "Индекси намуди интихобшудаи ҷорӣ дар интихобкунандаи барнома."
+
+#: data/org.gnome.shell.gschema.xml.in:58
+msgid "History for command (Alt-F2) dialog"
+msgstr "Таърихи равзанаи гуфтугӯи фармон (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:63
+msgid "History for the looking glass dialog"
+msgstr "Таърих барои равзанаи гуфтугӯи оина"
+
+#: data/org.gnome.shell.gschema.xml.in:67
+#| msgid "Always show the 'Log out' menu item in the user menu."
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Ҳамеша намоиш додани объекти менюи \"Баромадан\" дар менюи корбар."
+
+#: data/org.gnome.shell.gschema.xml.in:68
+#| msgid "This key overrides the automatic hiding of the 'Log out' menu item in single-user, single-session situations."
+msgid "This key overrides the automatic hiding of the “Log out” menu item in single-user, single-session situations."
+msgstr "Ин калид пинҳонкунии худкори объекти менюи \"Баромадан\"-ро дар вазъиятҳои корбари ягона ва ҷаласаи ягона идора мекунад."
+
+#: data/org.gnome.shell.gschema.xml.in:75
+msgid "Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "Дар ёд доштан ё дар ёд надоштани парол барои васл кардани системаҳои файлии рамздор ё дурдаст"
+
+#: data/org.gnome.shell.gschema.xml.in:76
+#| msgid "The shell will request a password when an encrypted device or a remote filesystem is mounted. If the password can be saved for future use a 'Remember Password' checkbox will be present. This key sets the default state of the checkbox."
+msgid "The shell will request a password when an encrypted device or a remote filesystem is mounted. If the password can be saved for future use a “Remember Password” checkbox will be present. This key sets the default state of the checkbox."
+msgstr "Вақте ки дастгоҳ ё системаи файлии рамздор васл карда мешавад, восит паролро мепурсад. Агар парол барои истифодаи оянда захира шавад, байрақчаи \"Дар ёд доштани парол\" мавҷуд мешавад. Ин калид ҳолати пешфарзи байрақчаро таъин мекунад."
+
+#: data/org.gnome.shell.gschema.xml.in:85
+msgid "Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:86
+msgid "The shell will only show a Bluetooth menu item if a Bluetooth adapter is powered, or if there were devices set up associated with the default adapter. This will be reset if the default adapter is ever seen not to have devices associated to it."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:95
+msgid "Enable introspection API"
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:96
+msgid "Enables a D-Bus API that allows to introspect the application state of the shell."
+msgstr ""
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Keybinding to open the application menu"
+msgstr "Тугмабандӣ барои кушодани менюи барнома"
+
+#: data/org.gnome.shell.gschema.xml.in:110
+msgid "Keybinding to open the application menu."
+msgstr "Тугмабандӣ барои кушодани менюи барнома."
+
+#: data/org.gnome.shell.gschema.xml.in:116
+#| msgid "Keybinding to open the \"Show Applications\" view"
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Тугмабандӣ барои кушодани намуди \"Намоиш додани барномаҳо\""
+
+#: data/org.gnome.shell.gschema.xml.in:117
+#| msgid "Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgid "Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Тугмабандӣ барои кушодани намуди \"Намоиш додани барномаҳо\"-и Хулосаи фаъолият."
+
+#: data/org.gnome.shell.gschema.xml.in:124
+msgid "Keybinding to open the overview"
+msgstr "Тугмабандӣ барои кушодани хулоса"
+
+#: data/org.gnome.shell.gschema.xml.in:125
+msgid "Keybinding to open the Activities Overview."
+msgstr "Тугмабандӣ барои кушодани хулосаи фаъолият."
+
+#: data/org.gnome.shell.gschema.xml.in:131
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Тугмабандӣ барои иваз кардани намоёнии рӯйхати огоҳиҳо"
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Тугмабандӣ барои иваз кардани намоёнии рӯйхати огоҳиҳо."
+
+#: data/org.gnome.shell.gschema.xml.in:138
+msgid "Keybinding to focus the active notification"
+msgstr "Тугмабандӣ барои марказонидани огоҳии фаъол"
+
+#: data/org.gnome.shell.gschema.xml.in:139
+msgid "Keybinding to focus the active notification."
+msgstr "Тугмабандӣ барои марказонидани огоҳии фаъол."
+
+#: data/org.gnome.shell.gschema.xml.in:145
+msgid "Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr "Тугмабандие, ки ҳамаи твинҳои фаъол барои мақсадҳои ислоҳи хатоҳо таваққуф ва оғоз мекунад"
+
+#: data/org.gnome.shell.gschema.xml.in:150
+#| msgid "Show Applications"
+msgid "Switch to application 1"
+msgstr "Гузариш ба барномаи 1"
+
+#: data/org.gnome.shell.gschema.xml.in:154
+#| msgid "Show Applications"
+msgid "Switch to application 2"
+msgstr "Гузариш ба барномаи 2"
+
+#: data/org.gnome.shell.gschema.xml.in:158
+#| msgid "Show Applications"
+msgid "Switch to application 3"
+msgstr "Гузариш ба барномаи 3"
+
+#: data/org.gnome.shell.gschema.xml.in:162
+#| msgid "Show Applications"
+msgid "Switch to application 4"
+msgstr "Гузариш ба барномаи 4"
+
+#: data/org.gnome.shell.gschema.xml.in:166
+#| msgid "Show Applications"
+msgid "Switch to application 5"
+msgstr "Гузариш ба барномаи 5"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+#| msgid "Show Applications"
+msgid "Switch to application 6"
+msgstr "Гузариш ба барномаи 6"
+
+#: data/org.gnome.shell.gschema.xml.in:174
+#| msgid "Show Applications"
+msgid "Switch to application 7"
+msgstr "Гузариш ба барномаи 7"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+#| msgid "Show Applications"
+msgid "Switch to application 8"
+msgstr "Гузариш ба барномаи 8"
+
+#: data/org.gnome.shell.gschema.xml.in:182
+#| msgid "Show Applications"
+msgid "Switch to application 9"
+msgstr "Гузариш ба барномаи 9"
+
+#: data/org.gnome.shell.gschema.xml.in:190
+msgid "Which keyboard to use"
+msgstr "Клавиатураро интихоб намоед"
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "The type of keyboard to use."
+msgstr "Навъи клавиатурае, ки истифода мешавад."
+
+#: data/org.gnome.shell.gschema.xml.in:202
+#: data/org.gnome.shell.gschema.xml.in:229
+msgid "Limit switcher to current workspace."
+msgstr "Ивазкунандаи ҳудуд ба фазои кории ҷорӣ."
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid "If true, only applications that have windows on the current workspace are shown in the switcher. Otherwise, all applications are included."
+msgstr "Агар фаъол бошад, танҳо барномаҳое, ки равзанаро дар фазои кории ҷорӣ дар бар мегирад, дар ивазкунанда намоиш дода мешаванд. Ба таври дигар ҳамаи барномаҳо илова карда мешаванд."
+
+#: data/org.gnome.shell.gschema.xml.in:220
+msgid "The application icon mode."
+msgstr "Ҳолати нишонаи барнома."
+
+#: data/org.gnome.shell.gschema.xml.in:221
+#| msgid "Configures how the windows are shown in the switcher. Valid possibilities are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only' (shows only the application icon) or 'both'."
+msgid "Configures how the windows are shown in the switcher. Valid possibilities are “thumbnail-only” (shows a thumbnail of the window), “app-icon-only” (shows only the application icon) or “both”."
+msgstr "Тарзи намоиш додани равзанаҳоро дар интихобкунанда конфигуратсия мекунад. Имкониятҳои боэътибор \"танҳо пешнамоишҳои кучак\" (пешнамоиши кучаки равзанаро намоиш медиҳад), \"танҳо нишонаи барнома\" (танҳо нишонаи барномаро намоиш медиҳад) ё \"ҳар ду\"."
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "If true, only windows from the current workspace are shown in the switcher. Otherwise, all windows are included."
+msgstr "Агар фаъол бошад, танҳо равзанаҳо аз фазои кории ҷорӣ дар ивазкунанда намоиш дода мешаванд. Ба таври дигар ҳамаи равзанаҳо илова карда мешаванд."
+
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Attach modal dialog to the parent window"
+msgstr "Замима кардани равзанаи гуфтугӯи модалӣ ба равзанаи асосӣ"
+
+#: data/org.gnome.shell.gschema.xml.in:243
+#: data/org.gnome.shell.gschema.xml.in:252
+#: data/org.gnome.shell.gschema.xml.in:260
+#: data/org.gnome.shell.gschema.xml.in:268
+#: data/org.gnome.shell.gschema.xml.in:276
+msgid "This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "Ин калид ҳангоми иҷроиши GNOME Shell калиди дар org.gnome.mutter ҷойгирбударо бекор мекунад."
+
+#: data/org.gnome.shell.gschema.xml.in:251
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Фаъол кардани лавҳабандии канорӣ ҳангоми гузоштани равзанаҳо ба канорҳои экран"
+
+#: data/org.gnome.shell.gschema.xml.in:259
+msgid "Workspaces are managed dynamically"
+msgstr "Фазоҳои корӣ ба таври серамалӣ идора мешаванд"
+
+#: data/org.gnome.shell.gschema.xml.in:267
+msgid "Workspaces only on primary monitor"
+msgstr "Танҳо фазоҳои корӣ дар монитори асосӣ"
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "То вақте ки курсор намеистад тағйироти фокусро дар ҳолати истифодаи муш ба таъхир гузоред"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Воридшавии шабакавӣ"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:116
+msgid "Something’s gone wrong"
+msgstr "Чизе нодуруст ба миён омад"
+
+#: js/extensionPrefs/main.js:123
+msgid "We’re very sorry, but there’s been a problem: the settings for this extension can’t be displayed. We recommend that you report the issue to the extension authors."
+msgstr "Мутаассифона, хатое ба вуҷуд омад: танзимот барои ин васеъшавӣ нишон дода намешавад. Тавсия дода мешавад, ки шумо дар бораи хато ба муаллифони васеъшавӣ гузориш диҳед."
+
+#: js/extensionPrefs/main.js:130
+#| msgid "Show Details"
+msgid "Technical Details"
+msgstr "Намоиш додани тафсилот"
+
+#: js/extensionPrefs/main.js:165
+#| msgid "Show Errors"
+msgid "Copy Error"
+msgstr "Намоиш додани хатоҳо"
+
+#: js/extensionPrefs/main.js:185
+msgid "Homepage"
+msgstr "Саҳифаи асосӣ"
+
+#: js/extensionPrefs/main.js:186
+#| msgid "UUIDs of extensions to enable"
+msgid "Visit extension homepage"
+msgstr "Рамзҳои UUID барои пасвандҳои фаъолшаванда"
+
+#: js/extensionPrefs/main.js:449
+#| msgid "No extensions installed"
+msgid "No Extensions Installed"
+msgstr "Ягон васеъшавӣ насб нашудааст"
+
+#: js/extensionPrefs/main.js:459
+msgid "Extensions can be installed through Software or <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
+msgstr "Васеъшавиҳо ба воситаи низоми нармафзор ё <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a> ҷорӣ карда мешаванд."
+
+#: js/extensionPrefs/main.js:474
+msgid "Browse in Software"
+msgstr "Намоиш дар низоми нармафзор"
+
+#: js/gdm/authPrompt.js:140 js/ui/audioDeviceSelection.js:55
+#: js/ui/components/networkAgent.js:117 js/ui/components/polkitAgent.js:136
+#: js/ui/endSessionDialog.js:430 js/ui/extensionDownloader.js:188
+#: js/ui/shellMountOperation.js:325 js/ui/status/network.js:888
+msgid "Cancel"
+msgstr "Бекор кардан"
+
+#: js/gdm/authPrompt.js:159 js/gdm/authPrompt.js:202 js/gdm/authPrompt.js:434
+msgid "Next"
+msgstr "Навбатӣ"
+
+#: js/gdm/authPrompt.js:198 js/ui/shellMountOperation.js:329
+#: js/ui/unlockDialog.js:41
+msgid "Unlock"
+msgstr "Кушодан"
+
+#: js/gdm/authPrompt.js:200
+msgctxt "button"
+msgid "Sign In"
+msgstr "Ворид шудан"
+
+#: js/gdm/loginDialog.js:302
+msgid "Choose Session"
+msgstr "Интихоби ҷаласа"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:446
+msgid "Not listed?"
+msgstr "Корбар дар ин рӯйхат вуҷуд надорад?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:884
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(масалан, корбар ё %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:889 js/ui/components/networkAgent.js:243
+#: js/ui/components/networkAgent.js:263 js/ui/components/networkAgent.js:281
+msgid "Username: "
+msgstr "Номи корбар: "
+
+#: js/gdm/loginDialog.js:1227
+msgid "Login Window"
+msgstr "Равзанаи воридшавӣ"
+
+#: js/gdm/util.js:337
+msgid "Authentication error"
+msgstr "Хатои санҷиши ҳаққоният"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:477
+msgid "(or swipe finger)"
+msgstr "(ё бо ангут ламс кунед)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:88
+#| msgid "Power Off"
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Хомӯш кардани компютер"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:91
+msgid "power off;shutdown;reboot;restart"
+msgstr ""
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:95
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Қулф кардани экран"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:98
+msgid "lock screen"
+msgstr ""
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:102
+#| msgid "Log Out"
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Баромад аз мизи корӣ"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:105
+msgid "logout;sign off"
+msgstr ""
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:109
+#| msgid "Suspend"
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Таваққуф кардани низом"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:112
+#| msgid "Suspend"
+msgid "suspend;sleep"
+msgstr "таваққуф;хоб"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:116
+#| msgid "Switch User"
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Иваз кардани корбар"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:119
+#| msgid "Switch User"
+msgid "switch user"
+msgstr "ивази корбар"
+
+#. Translators: The name of the lock orientation action in search
+#: js/misc/systemActions.js:123
+#| msgid "Orientation Lock"
+msgctxt "search-result"
+msgid "Lock Orientation"
+msgstr "Қулфи самти экран"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:126
+msgid "lock orientation;screen;rotation"
+msgstr ""
+
+#: js/misc/util.js:117
+msgid "Command not found"
+msgstr "Фармон ёфт нашудааст"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:150
+msgid "Could not parse command:"
+msgstr "Фармон иҷро нашудааст:"
+
+#: js/misc/util.js:158
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Иҷрокунии “%s” қатъ шудааст:"
+
+#: js/misc/util.js:175
+msgid "Just now"
+msgstr "Ҳоли ҳозир"
+
+#: js/misc/util.js:177
+#, javascript-format
+#| msgid "%d minute remaining"
+#| msgid_plural "%d minutes remaining"
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d дақиқа пеш"
+msgstr[1] "%d дақиқа пеш"
+
+#: js/misc/util.js:180
+#, javascript-format
+#| msgid "%d hour remaining"
+#| msgid_plural "%d hours remaining"
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d соат пеш"
+msgstr[1] "%d соат пеш"
+
+#: js/misc/util.js:183
+#| msgid "Yesterday, %H∶%M"
+msgid "Yesterday"
+msgstr "Дирӯз"
+
+#: js/misc/util.js:185
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d рӯз пеш"
+msgstr[1] "%d рӯз пеш"
+
+#: js/misc/util.js:188
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d ҳафта пеш"
+msgstr[1] "%d ҳафта пеш"
+
+#: js/misc/util.js:191
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d моҳ пеш"
+msgstr[1] "%d моҳ пеш"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d сол пеш"
+msgstr[1] "%d сол пеш"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:223
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:229
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Дирӯз, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:235
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:241
+#, no-c-format
+#| msgid "%B %d, %H∶%M"
+msgid "%B %-d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:247
+#, no-c-format
+#| msgid "%B %d %Y, %H∶%M"
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:252
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:258
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Дирӯз, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:264
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:270
+#, no-c-format
+#| msgid "%B %d, %l∶%M %p"
+msgid "%B %-d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:276
+#, no-c-format
+#| msgid "%B %d %Y, %l∶%M %p"
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login window
+#: js/portalHelper/main.js:40
+#| msgid "Hotspot Active"
+msgid "Hotspot Login"
+msgstr "Воридшавӣ ба нуқтаи пайваст"
+
+#: js/portalHelper/main.js:86
+msgid "Your connection to this hotspot login is not secure. Passwords or other information you enter on this page can be viewed by people nearby."
+msgstr "Пайвасти воридшавии шумо ба нуқтаи пайвасти ҷорӣ бехатар намебошад. Ниҳонвожаҳо ё маълумоти дигаре, ки дар ин саҳифа ворид карда мешавад, метавонанд бо одамони дар гидру атроф ошкор карда шаванд."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:37 js/ui/status/location.js:360
+msgid "Deny Access"
+msgstr "Манъ кардани дастрасӣ"
+
+#: js/ui/accessDialog.js:38 js/ui/status/location.js:363
+#| msgid "Always grant access"
+msgid "Grant Access"
+msgstr "Иҷозат додани дастрасӣ"
+
+#: js/ui/appDisplay.js:660
+msgid "Frequently used applications will appear here"
+msgstr "Барномаҳои бештар истифодашуда ин ҷо намоиш дода мешаванд"
+
+#: js/ui/appDisplay.js:775
+msgid "Frequent"
+msgstr "Роиҷ"
+
+#: js/ui/appDisplay.js:782
+msgid "All"
+msgstr "Ҳама"
+
+#: js/ui/appDisplay.js:1737 js/ui/panel.js:83
+msgid "New Window"
+msgstr "Равзанаи нав"
+
+#: js/ui/appDisplay.js:1751
+msgid "Launch using Dedicated Graphics Card"
+msgstr ""
+
+#: js/ui/appDisplay.js:1778 js/ui/dash.js:239
+msgid "Remove from Favorites"
+msgstr "Тоза кардан аз Баргузидаҳо"
+
+#: js/ui/appDisplay.js:1784
+msgid "Add to Favorites"
+msgstr "Илова кардан ба Баргузидаҳо"
+
+#: js/ui/appDisplay.js:1794 js/ui/panel.js:94
+msgid "Show Details"
+msgstr "Намоиш додани тафсилот"
+
+#: js/ui/appFavorites.js:141
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s ба баргузидаҳои шумо илова шудааст."
+
+#: js/ui/appFavorites.js:175
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s аз баргузидаҳои шумо тоза шудааст."
+
+#: js/ui/audioDeviceSelection.js:42
+#| msgid "Send Files to Device…"
+msgid "Select Audio Device"
+msgstr "Интихоби дастгоҳи аудиоӣ"
+
+#: js/ui/audioDeviceSelection.js:53
+msgid "Sound Settings"
+msgstr "Танзимоти садо"
+
+#: js/ui/audioDeviceSelection.js:62
+msgid "Headphones"
+msgstr "Гӯшмонакҳо"
+
+#: js/ui/audioDeviceSelection.js:64
+msgid "Headset"
+msgstr "Гӯшмонак бо микрофон"
+
+#: js/ui/audioDeviceSelection.js:66 js/ui/status/volume.js:247
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: js/ui/backgroundMenu.js:13
+msgid "Change Background…"
+msgstr "Тағйир додани пазсамина…"
+
+#: js/ui/backgroundMenu.js:15 js/ui/status/nightLight.js:43
+msgid "Display Settings"
+msgstr "Танзимоти дисплей"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Settings"
+msgstr "Танзимот"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:40
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:69
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Ш"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:71
+msgctxt "grid monday"
+msgid "M"
+msgstr "Д"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:73
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:75
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ч"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:77
+msgctxt "grid thursday"
+msgid "T"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:79
+msgctxt "grid friday"
+msgid "F"
+msgstr "Ҷ"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:81
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Ш"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:332
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:342
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:399
+msgid "Previous month"
+msgstr "Моҳи қаблӣ"
+
+#: js/ui/calendar.js:410
+msgid "Next month"
+msgstr "Моҳи навбатӣ"
+
+#: js/ui/calendar.js:564
+#, no-javascript-format
+#| msgctxt "percent of battery remaining"
+#| msgid "%d%%"
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:619
+msgid "Week %V"
+msgstr "Ҳафтаи %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:687
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Тамоми рӯз"
+
+#: js/ui/calendar.js:819
+#| msgctxt "calendar heading"
+#| msgid "%A, %B %d"
+msgctxt "calendar heading"
+msgid "%A, %B %-d"
+msgstr "%A, %B %d"
+
+#: js/ui/calendar.js:823
+#| msgctxt "calendar heading"
+#| msgid "%A, %B %d, %Y"
+msgctxt "calendar heading"
+msgid "%A, %B %-d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: js/ui/calendar.js:1046
+msgid "No Notifications"
+msgstr "Ягон огоҳӣ нест"
+
+#: js/ui/calendar.js:1049
+msgid "No Events"
+msgstr "Ягон рӯйдод нест"
+
+#: js/ui/calendar.js:1075
+msgid "Clear"
+msgstr "Пок кардан"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:42
+#, javascript-format
+#| msgid "“%s” is ready"
+msgid "“%s” is not responding."
+msgstr "“%s” ҷавоб намедиҳад."
+
+#: js/ui/closeDialog.js:43
+msgid "You may choose to wait a short while for it to continue or force the application to quit entirely."
+msgstr ""
+
+#: js/ui/closeDialog.js:59
+msgid "Force Quit"
+msgstr "Маҷбуран пӯшида шавад"
+
+#: js/ui/closeDialog.js:62
+msgid "Wait"
+msgstr "Интизор шавед"
+
+#: js/ui/components/automountManager.js:86
+msgid "External drive connected"
+msgstr "Диски берунӣ васл шудааст"
+
+#: js/ui/components/automountManager.js:98
+msgid "External drive disconnected"
+msgstr "Диски берунӣ ҷудо шудааст"
+
+#: js/ui/components/autorunManager.js:334
+#, javascript-format
+msgid "Open with %s"
+msgstr "Кушодан бо %s"
+
+#: js/ui/components/keyring.js:77 js/ui/components/polkitAgent.js:255
+msgid "Password:"
+msgstr "Ниҳонвожа:"
+
+#: js/ui/components/keyring.js:108
+msgid "Type again:"
+msgstr "Аз нав ворид кунед:"
+
+#: js/ui/components/networkAgent.js:102
+msgid "Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+
+#: js/ui/components/networkAgent.js:112 js/ui/status/network.js:219
+#: js/ui/status/network.js:309 js/ui/status/network.js:891
+msgid "Connect"
+msgstr "Пайваст шудан"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:211 js/ui/components/networkAgent.js:223
+#: js/ui/components/networkAgent.js:246 js/ui/components/networkAgent.js:265
+#: js/ui/components/networkAgent.js:285 js/ui/components/networkAgent.js:295
+msgid "Password: "
+msgstr "Ниҳонвожа: "
+
+#. static WEP
+#: js/ui/components/networkAgent.js:216
+msgid "Key: "
+msgstr "Калид: "
+
+#: js/ui/components/networkAgent.js:249 js/ui/components/networkAgent.js:271
+msgid "Private key password: "
+msgstr "Ниҳонвожаи калиди шахсӣ: "
+
+#: js/ui/components/networkAgent.js:269
+msgid "Identity: "
+msgstr "Шахсият: "
+
+#: js/ui/components/networkAgent.js:283
+msgid "Service: "
+msgstr "Хидмат: "
+
+#: js/ui/components/networkAgent.js:312 js/ui/components/networkAgent.js:685
+msgid "Authentication required by wireless network"
+msgstr "Шабакаи бесим санҷиши ҳаққониятро талаб мекунад"
+
+#: js/ui/components/networkAgent.js:313 js/ui/components/networkAgent.js:686
+#, javascript-format
+msgid "Passwords or encryption keys are required to access the wireless network “%s”."
+msgstr "Барои пайдо кардани дастрасӣ ба шабакаи бесими “%s” паролҳо ё калидҳои рамзгузорӣ лозиманд."
+
+#: js/ui/components/networkAgent.js:317 js/ui/components/networkAgent.js:689
+msgid "Wired 802.1X authentication"
+msgstr "Санҷиши ҳаққонияти 802.1X-и симдор"
+
+#: js/ui/components/networkAgent.js:319
+msgid "Network name: "
+msgstr "Номи шабака: "
+
+#: js/ui/components/networkAgent.js:324 js/ui/components/networkAgent.js:693
+msgid "DSL authentication"
+msgstr "Санҷиши ҳаққонияти DSL"
+
+#: js/ui/components/networkAgent.js:331 js/ui/components/networkAgent.js:699
+msgid "PIN code required"
+msgstr "Рамзи PIN ҳатмист"
+
+#: js/ui/components/networkAgent.js:332 js/ui/components/networkAgent.js:700
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Барои дастгоҳи паҳннавори мобилӣ рамзи PIN лозим аст"
+
+#: js/ui/components/networkAgent.js:333
+msgid "PIN: "
+msgstr "PIN: "
+
+#: js/ui/components/networkAgent.js:340 js/ui/components/networkAgent.js:706
+msgid "Mobile broadband network password"
+msgstr "Пароли шабакаи паҳннавори мобилӣ"
+
+#: js/ui/components/networkAgent.js:341 js/ui/components/networkAgent.js:690
+#: js/ui/components/networkAgent.js:694 js/ui/components/networkAgent.js:707
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Барои пайваст шудан ба “%s” парол лозим аст."
+
+#: js/ui/components/networkAgent.js:674 js/ui/status/network.js:1665
+msgid "Network Manager"
+msgstr "Мудири шабака"
+
+#: js/ui/components/polkitAgent.js:34
+msgid "Authentication Required"
+msgstr "Санҷиши ҳаққоният лозим аст"
+
+#: js/ui/components/polkitAgent.js:62
+msgid "Administrator"
+msgstr "Маъмур"
+
+#: js/ui/components/polkitAgent.js:139
+msgid "Authenticate"
+msgstr "Санҷиши ҳаққоният"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:241 js/ui/shellMountOperation.js:309
+#| msgid "Sorry, that didn't work. Please try again."
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Мутаассифона, ин амал иҷро нашуд. Лутфан, амалро такрор кунед."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:778
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "Акнун %s ба номи %s дониста мешавад"
+
+#: js/ui/ctrlAltTab.js:21 js/ui/viewSelector.js:169
+msgid "Windows"
+msgstr "Равзанаҳо"
+
+#: js/ui/dash.js:200 js/ui/dash.js:241
+msgid "Show Applications"
+msgstr "Намоиш додани барномаҳо"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:390
+msgid "Dash"
+msgstr "Рах"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:62
+#| msgid "%B %e %Y"
+msgid "%B %-d %Y"
+msgstr "%B %e %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:69
+msgid "%A %B %e %Y"
+msgstr "%A %B %e %Y"
+
+#: js/ui/dateMenu.js:131
+msgid "Add world clocks…"
+msgstr "Илова кардани соатҳои ҷаҳон…"
+
+#: js/ui/dateMenu.js:132
+msgid "World Clocks"
+msgstr "Соати ҷаҳон"
+
+#: js/ui/dateMenu.js:222
+msgid "Weather"
+msgstr "Обу ҳаво"
+
+#: js/ui/dateMenu.js:305
+#| msgid "Select a network"
+msgid "Select a location…"
+msgstr "Интихоби ҷойгиршавӣ…"
+
+#: js/ui/dateMenu.js:313
+#| msgid "Searching…"
+msgid "Loading…"
+msgstr "Бор шуда истодааст…"
+
+#: js/ui/dateMenu.js:323
+msgid "Go online for weather information"
+msgstr "Барои донистани вазъи обу ҳавоӣ ба онлайн равед"
+
+#: js/ui/dateMenu.js:325
+#| msgid "Encryption is not available"
+msgid "Weather information is currently unavailable"
+msgstr "Рамзгузорӣ дастрас нест"
+
+#: js/ui/endSessionDialog.js:42
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Баромадан аз %s"
+
+#: js/ui/endSessionDialog.js:43
+msgctxt "title"
+msgid "Log Out"
+msgstr "Баромад аз мизи корӣ"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s аз система баъд аз %d сония ба таври худкор мебарояд."
+msgstr[1] "%s аз система баъд аз %d сония ба таври худкор мебарояд."
+
+#: js/ui/endSessionDialog.js:50
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Шумо аз система баъд аз %d сония ба таври худкор мебароед."
+msgstr[1] "Шумо аз система баъд аз %d сония ба таври худкор мебароед."
+
+#: js/ui/endSessionDialog.js:56
+msgctxt "button"
+msgid "Log Out"
+msgstr "Баромад аз мизи корӣ"
+
+#: js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Power Off"
+msgstr "Хомӯш кардани компютер"
+
+#: js/ui/endSessionDialog.js:63
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Насб кардани навсозиҳо ва хомӯш кардани компютер"
+
+#: js/ui/endSessionDialog.js:65
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Система ба таври худкор баъд аз %d сония корро анҷом медиҳад."
+msgstr[1] "Система ба таври худкор баъд аз %d сония корро анҷом медиҳад."
+
+#: js/ui/endSessionDialog.js:69
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Насб кардани навсозиҳои нармафзори мунтазир"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:89
+msgctxt "button"
+msgid "Restart"
+msgstr "Аз нав оғоз кардани компютер"
+
+#: js/ui/endSessionDialog.js:74
+msgctxt "button"
+msgid "Power Off"
+msgstr "Хомӯш кардани компютер"
+
+#: js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Restart"
+msgstr "Аз нав оғоз кардани компютер"
+
+#: js/ui/endSessionDialog.js:83
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Система ба таври худкор баъд аз %d сония бозоғозӣ мешавад."
+msgstr[1] "Система ба таври худкор баъд аз %d сония бозоғозӣ мешавад."
+
+#: js/ui/endSessionDialog.js:97
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Бозоғозӣ кардан ва насб кардани навсозиҳо"
+
+#: js/ui/endSessionDialog.js:99
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural "The system will automatically restart and install updates in %d seconds."
+msgstr[0] "Низом баъд аз %d сония ба таври худкор бозоғозӣ мешавад ва навсозиҳоро насб мекунад."
+msgstr[1] "Низом баъд аз %d сония ба таври худкор бозоғозӣ мешавад ва навсозиҳоро насб мекунад."
+
+#: js/ui/endSessionDialog.js:105 js/ui/endSessionDialog.js:125
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Бозоғозӣ ва насб кардан"
+
+#: js/ui/endSessionDialog.js:106
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Насб кардан ва хомӯш кардани компютер"
+
+#: js/ui/endSessionDialog.js:107
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Хомӯш кардани компютер баъд аз насб кардани навсозиҳо"
+
+#: js/ui/endSessionDialog.js:115
+#| msgctxt "title"
+#| msgid "Restart & Install Updates"
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Бозоғозӣ кардан ва насб кардани навсозиҳо"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:120
+#, javascript-format
+msgid "%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+
+#: js/ui/endSessionDialog.js:309
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "Аз батарея кор мекунад: лутфан, еш аз насбкунии навсозиҳо компютерро ба манбаъи барқ васл кунед."
+
+#: js/ui/endSessionDialog.js:326
+msgid "Some applications are busy or have unsaved work."
+msgstr "Баъзе барномаҳо банданд ё дорои корҳои захиранашуда мебошанд."
+
+#: js/ui/endSessionDialog.js:333
+msgid "Other users are logged in."
+msgstr "Корбарони дигар ворид шудаанд."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:614
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (дурдаст)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:617
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (консол)"
+
+#: js/ui/extensionDownloader.js:192
+msgid "Install"
+msgstr "Насб кардан"
+
+#: js/ui/extensionDownloader.js:197
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "“%s”-ро аз extensions.gnome.org боргирӣ ва насб мекунед?"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:50
+#, javascript-format
+msgid "%s wants to inhibit shortcuts"
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:51
+msgid "Application wants to inhibit shortcuts"
+msgstr ""
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:60
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr ""
+
+#: js/ui/inhibitShortcutsDialog.js:65
+msgid "Deny"
+msgstr "Манъ кардан"
+
+#: js/ui/inhibitShortcutsDialog.js:71
+msgid "Allow"
+msgstr "Иҷозат додан"
+
+#: js/ui/kbdA11yDialog.js:31
+#| msgid "Slow Keys"
+msgid "Slow Keys Turned On"
+msgstr "Тугмаҳои суст"
+
+#: js/ui/kbdA11yDialog.js:32
+#| msgid "Slow Keys"
+msgid "Slow Keys Turned Off"
+msgstr "Тугмаҳои суст"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "You just held down the Shift key for 8 seconds. This is the shortcut for the Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:40
+#| msgid "Sticky Keys"
+msgid "Sticky Keys Turned On"
+msgstr "Тугмаҳои часпак"
+
+#: js/ui/kbdA11yDialog.js:41
+#| msgid "Sticky Keys"
+msgid "Sticky Keys Turned Off"
+msgstr "Тугмаҳои часпак"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid "You just pressed the Shift key 5 times in a row. This is the shortcut for the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:45
+msgid "You just pressed two keys at once, or pressed the Shift key 5 times in a row. This turns off the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+
+#: js/ui/kbdA11yDialog.js:57
+msgid "Leave On"
+msgstr "Ҳамеша фаъол"
+
+#: js/ui/kbdA11yDialog.js:57 js/ui/status/bluetooth.js:133
+#: js/ui/status/network.js:1264
+msgid "Turn On"
+msgstr "Фаъол кардан"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:133
+#: js/ui/status/network.js:128 js/ui/status/network.js:310
+#: js/ui/status/network.js:1264 js/ui/status/network.js:1376
+#: js/ui/status/nightLight.js:39 js/ui/status/rfkill.js:79
+#: js/ui/status/rfkill.js:106
+msgid "Turn Off"
+msgstr "Ғайрифаъол кардан"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Ҳамеша ғайрифаъол"
+
+#: js/ui/keyboard.js:203
+msgid "Region & Language Settings"
+msgstr "Танзимоти забон ва минтақа"
+
+#: js/ui/lookingGlass.js:615
+msgid "No extensions installed"
+msgstr "Ягон пасванд насб нашудааст"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:669
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s ягон хато надорад."
+
+#: js/ui/lookingGlass.js:675
+msgid "Hide Errors"
+msgstr "Пинҳон кардани хатоҳо"
+
+#: js/ui/lookingGlass.js:679 js/ui/lookingGlass.js:739
+msgid "Show Errors"
+msgstr "Намоиш додани хатоҳо"
+
+#: js/ui/lookingGlass.js:688
+msgid "Enabled"
+msgstr "Фаъол"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:691 subprojects/gvc/gvc-mixer-control.c:1864
+msgid "Disabled"
+msgstr "Ғайрифаъол"
+
+#: js/ui/lookingGlass.js:693
+msgid "Error"
+msgstr "Хато"
+
+#: js/ui/lookingGlass.js:695
+msgid "Out of date"
+msgstr "Аз мӯҳлат гузашта"
+
+#: js/ui/lookingGlass.js:697
+msgid "Downloading"
+msgstr "Боргирӣ рафта истодааст"
+
+#: js/ui/lookingGlass.js:721
+msgid "View Source"
+msgstr "Намоиш додани манбаъ"
+
+#: js/ui/lookingGlass.js:730
+msgid "Web Page"
+msgstr "Саҳифаи веб"
+
+#: js/ui/messageTray.js:1474
+msgid "System Information"
+msgstr "Маълумоти система"
+
+#: js/ui/mpris.js:177
+#| msgid "Unknown reason"
+msgid "Unknown artist"
+msgstr "Ҳофизи номаълум"
+
+#: js/ui/mpris.js:178
+#| msgctxt "program"
+#| msgid "Unknown"
+msgid "Unknown title"
+msgstr "Унвони номаълум"
+
+#: js/ui/osdWindow.js:22 js/ui/status/volume.js:199
+msgid "Volume"
+msgstr "Баландии садо"
+
+#: js/ui/overview.js:73
+msgid "Undo"
+msgstr "Ботил сохтан"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:100
+msgid "Overview"
+msgstr "Ҷамъбаст"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:226
+msgid "Type to search…"
+msgstr "Барои ҷустуҷӯ чизеро чоп намоед…"
+
+#: js/ui/padOsd.js:92
+msgid "New shortcut…"
+msgstr "Миёнбури нав…"
+
+#: js/ui/padOsd.js:141
+#| msgid "Applications"
+msgid "Application defined"
+msgstr "Барномаи муайяншуда"
+
+#: js/ui/padOsd.js:142
+msgid "Show on-screen help"
+msgstr "Намоиш додани кумаки экранӣ"
+
+#: js/ui/padOsd.js:143
+#| msgid "Switch User"
+msgid "Switch monitor"
+msgstr "Иваз кардани монитор"
+
+#: js/ui/padOsd.js:144
+msgid "Assign keystroke"
+msgstr ""
+
+#: js/ui/padOsd.js:209
+msgid "Done"
+msgstr "Тайёр"
+
+#: js/ui/padOsd.js:721
+msgid "Edit…"
+msgstr "Таҳрир кардан…"
+
+#: js/ui/padOsd.js:763 js/ui/padOsd.js:868
+msgid "None"
+msgstr "Ҳеҷ"
+
+#: js/ui/padOsd.js:822
+msgid "Press a button to configure"
+msgstr "Барои идома тугмаеро пахш намоед"
+
+#: js/ui/padOsd.js:823
+msgid "Press Esc to exit"
+msgstr "Барои баромад тугмаи Esc-ро пахш намоед"
+
+#: js/ui/padOsd.js:826
+msgid "Press any key to exit"
+msgstr "Барои баромад тугмаи дилхоҳро пахш намоед"
+
+#: js/ui/panel.js:108
+msgid "Quit"
+msgstr "Баромад"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:466
+msgid "Activities"
+msgstr "Фаъолиятҳо"
+
+#: js/ui/panel.js:741
+#| msgid "System"
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Низом"
+
+#: js/ui/panel.js:861
+msgid "Top Bar"
+msgstr "Лавҳаии болоӣ"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:285
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: js/ui/runDialog.js:64
+msgid "Enter a Command"
+msgstr "Фармонеро ворид кунед"
+
+#: js/ui/runDialog.js:104 js/ui/windowMenu.js:166
+msgid "Close"
+msgstr "Пӯшидан"
+
+#: js/ui/runDialog.js:266
+#| msgid "Encryption is not available"
+msgid "Restart is not available on Wayland"
+msgstr "Рамзгузорӣ дастрас нест"
+
+#: js/ui/runDialog.js:271
+msgid "Restarting…"
+msgstr "Дар ҳоли бозоғозӣ…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:77
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: js/ui/screenShield.js:133
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d паёми нав"
+msgstr[1] "%d паёми нав"
+
+#: js/ui/screenShield.js:135
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d огоҳии нав"
+msgstr[1] "%d огоҳии нав"
+
+#: js/ui/screenShield.js:449 js/ui/status/system.js:270
+msgid "Lock"
+msgstr "Қулф кардан"
+
+#: js/ui/screenShield.js:713
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME бояд экранро қулф кунад"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:834 js/ui/screenShield.js:1307
+msgid "Unable to lock"
+msgstr "Қулф карда намешавад"
+
+#: js/ui/screenShield.js:835 js/ui/screenShield.js:1308
+msgid "Lock was blocked by an application"
+msgstr "Қулф аз тарави барнома баста шудааст"
+
+#: js/ui/search.js:635
+msgid "Searching…"
+msgstr "Дар ҳоли ҷустуҷӯ…"
+
+#: js/ui/search.js:637
+msgid "No results."
+msgstr "Ягон натиҷа нест."
+
+#: js/ui/search.js:761
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d дигар"
+msgstr[1] "%d дигар"
+
+#: js/ui/shellEntry.js:19
+msgid "Copy"
+msgstr "Нусха бардоштан"
+
+#: js/ui/shellEntry.js:24
+msgid "Paste"
+msgstr "Гузоштан"
+
+#: js/ui/shellEntry.js:90
+msgid "Show Text"
+msgstr "Намоиш додани матн"
+
+#: js/ui/shellEntry.js:92
+msgid "Hide Text"
+msgstr "Пинҳон кардани матн"
+
+#: js/ui/shellMountOperation.js:296
+msgid "Password"
+msgstr "Парол"
+
+#: js/ui/shellMountOperation.js:317
+msgid "Remember Password"
+msgstr "Ба ёд гирифтани парол"
+
+#: js/ui/status/accessibility.js:35
+msgid "Accessibility"
+msgstr "Қобилияти дастрасӣ"
+
+#: js/ui/status/accessibility.js:50
+msgid "Zoom"
+msgstr "Танзими андоза"
+
+#: js/ui/status/accessibility.js:57
+msgid "Screen Reader"
+msgstr "Хонандаи экранӣ"
+
+#: js/ui/status/accessibility.js:61
+msgid "Screen Keyboard"
+msgstr "Клавиатураи экранӣ"
+
+#: js/ui/status/accessibility.js:65
+msgid "Visual Alerts"
+msgstr "Огоҳиҳои намоён"
+
+#: js/ui/status/accessibility.js:68
+msgid "Sticky Keys"
+msgstr "Тугмаҳои часпак"
+
+#: js/ui/status/accessibility.js:71
+msgid "Slow Keys"
+msgstr "Тугмаҳои суст"
+
+#: js/ui/status/accessibility.js:74
+msgid "Bounce Keys"
+msgstr "Тугмаҳои ҷастухез"
+
+#: js/ui/status/accessibility.js:77
+msgid "Mouse Keys"
+msgstr "Тугмаҳои муш"
+
+#: js/ui/status/accessibility.js:151
+msgid "High Contrast"
+msgstr "Контрасти баланд"
+
+#: js/ui/status/accessibility.js:182
+msgid "Large Text"
+msgstr "Матни бузург"
+
+#: js/ui/status/bluetooth.js:38
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:47 js/ui/status/network.js:585
+msgid "Bluetooth Settings"
+msgstr "Танзимоти Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:127
+#, javascript-format
+#| msgid "Connected"
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d пайваст шуд"
+msgstr[1] "%d пайваст шуд"
+
+#: js/ui/status/bluetooth.js:129
+msgid "Off"
+msgstr "Хомӯш"
+
+#: js/ui/status/bluetooth.js:131
+msgid "On"
+msgstr "Фаъол"
+
+#: js/ui/status/brightness.js:36
+msgid "Brightness"
+msgstr "Дурахшонӣ"
+
+#: js/ui/status/keyboard.js:812
+msgid "Keyboard"
+msgstr "Клавиатура"
+
+#: js/ui/status/keyboard.js:834
+msgid "Show Keyboard Layout"
+msgstr "Намоиш додани тарҳбандии клавиатура"
+
+#: js/ui/status/location.js:64 js/ui/status/location.js:172
+#| msgid "Location"
+msgid "Location Enabled"
+msgstr "Ҷойгиршавӣ"
+
+#: js/ui/status/location.js:65 js/ui/status/location.js:173
+msgid "Disable"
+msgstr "Ғайрифаъол кардан"
+
+#: js/ui/status/location.js:66
+msgid "Privacy Settings"
+msgstr "Танзимоти махфият"
+
+#: js/ui/status/location.js:171
+#| msgid "Location"
+msgid "Location In Use"
+msgstr "Ҷойгиршавӣ"
+
+#: js/ui/status/location.js:175
+#| msgid "Connection failed"
+msgid "Location Disabled"
+msgstr "Пайваст қатъ шудааст"
+
+#: js/ui/status/location.js:176
+msgid "Enable"
+msgstr "Фаъол кардан"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:353
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr ""
+
+#: js/ui/status/location.js:354
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+
+#: js/ui/status/network.js:66
+msgid "<unknown>"
+msgstr "<unknown>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:416 js/ui/status/network.js:1293
+#, javascript-format
+msgid "%s Off"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:419
+#, javascript-format
+#| msgid "Connected"
+msgid "%s Connected"
+msgstr "%s пайваст шуд"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:424
+#, javascript-format
+#| msgid "Unmanaged"
+msgid "%s Unmanaged"
+msgstr "%s беназорат"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:427
+#, javascript-format
+#| msgid "Disconnecting"
+msgid "%s Disconnecting"
+msgstr "Пайвасти %s қатъ шуда истодааст"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:434 js/ui/status/network.js:1285
+#, javascript-format
+#| msgid "Connecting"
+msgid "%s Connecting"
+msgstr "%s пайваст шуда истодааст"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:437
+#, javascript-format
+#| msgid "Wired 802.1X authentication"
+msgid "%s Requires Authentication"
+msgstr "%s санҷиши ҳаққониятро талаб мекунад"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:445
+#, javascript-format
+#| msgid "Firmware missing"
+msgid "Firmware Missing For %s"
+msgstr "Нармафзори дарунсохт барои %s вуҷуд надорад"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:449
+#, javascript-format
+#| msgid "Unavailable"
+msgid "%s Unavailable"
+msgstr "%s дастнорас аст"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:452
+#, javascript-format
+#| msgid "Connection failed"
+msgid "%s Connection Failed"
+msgstr "Пайвасти %s қатъ шуд"
+
+#: js/ui/status/network.js:464
+msgid "Wired Settings"
+msgstr "Танзимоти шабакаи симдор"
+
+#: js/ui/status/network.js:506
+msgid "Mobile Broadband Settings"
+msgstr "Танзимоти паҳннавори мобилӣ"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:553 js/ui/status/network.js:1290
+#, javascript-format
+#| msgid "Hardware Disabled"
+msgid "%s Hardware Disabled"
+msgstr "Сахтафзори %s ғайрифаъол шуд"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:557
+#, javascript-format
+#| msgid "Disabled"
+msgid "%s Disabled"
+msgstr "%s ғайрифаъол аст"
+
+#: js/ui/status/network.js:597
+msgid "Connect to Internet"
+msgstr "Пайваст шудан ба Интернет"
+
+#: js/ui/status/network.js:786
+msgid "Airplane Mode is On"
+msgstr "Ҳолати ҳавопаймо фаъол аст"
+
+#: js/ui/status/network.js:787
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Ҳангоми фаъол будани ҳолати ҳавопаймо Wi-Fi ғайрифаъол аст."
+
+#: js/ui/status/network.js:788
+msgid "Turn Off Airplane Mode"
+msgstr "Ҳолати ҳавопаймо хомӯш аст"
+
+#: js/ui/status/network.js:797
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi хомӯш аст"
+
+#: js/ui/status/network.js:798
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Барои пайваст шудан ба шабака, Wi-Fi бояд фаъол карда шавад."
+
+#: js/ui/status/network.js:799
+msgid "Turn On Wi-Fi"
+msgstr "Фаъол кардани Wi-Fi"
+
+#: js/ui/status/network.js:824
+msgid "Wi-Fi Networks"
+msgstr "Шабакаҳои Wi-Fi"
+
+#: js/ui/status/network.js:826
+msgid "Select a network"
+msgstr "Интихоб кардани шабака"
+
+#: js/ui/status/network.js:855
+msgid "No Networks"
+msgstr "Ягон шабака нест"
+
+#: js/ui/status/network.js:876 js/ui/status/rfkill.js:104
+msgid "Use hardware switch to turn off"
+msgstr "Истифодаи калиди сахтафзорӣ барои фаъолсозӣ ё хомӯшкунӣ"
+
+#: js/ui/status/network.js:1152
+msgid "Select Network"
+msgstr "Интихоби шабака"
+
+#: js/ui/status/network.js:1158
+msgid "Wi-Fi Settings"
+msgstr "Танзимоти Wi-Fi"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1281
+#, javascript-format
+#| msgid "Hotspot Active"
+msgid "%s Hotspot Active"
+msgstr "Нуқтаи пайвасти %s ваъол аст"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1296
+#, javascript-format
+#| msgid "Not Connected"
+msgid "%s Not Connected"
+msgstr "%s пайваст нашудааст"
+
+#: js/ui/status/network.js:1393
+#| msgid "Connecting"
+msgid "connecting…"
+msgstr "пайваст шуда истодааст…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1396
+msgid "authentication required"
+msgstr "санҷиши ҳаққоният лозим аст"
+
+#: js/ui/status/network.js:1398
+msgid "connection failed"
+msgstr "пайваст қатъ шудааст"
+
+#: js/ui/status/network.js:1448
+msgid "VPN Settings"
+msgstr "Танзимоти VPN"
+
+#: js/ui/status/network.js:1465
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1475
+#| msgid "Power Off"
+msgid "VPN Off"
+msgstr "Хомӯш кардани VPN"
+
+#: js/ui/status/network.js:1536 js/ui/status/rfkill.js:82
+msgid "Network Settings"
+msgstr "Танзимоти шабака"
+
+#: js/ui/status/network.js:1565
+#, javascript-format
+#| msgid "Use as Internet connection"
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s пайвасти симдор"
+msgstr[1] "%s пайвасти симдор"
+
+#: js/ui/status/network.js:1569
+#, javascript-format
+#| msgid "Connecting"
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s пайвасти Wi-Fi"
+msgstr[1] "%s пайвасти Wi-Fi"
+
+#: js/ui/status/network.js:1573
+#, javascript-format
+#| msgid "Connecting"
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s пайвасти модем"
+msgstr[1] "%s пайвасти модем"
+
+#: js/ui/status/network.js:1702
+msgid "Connection failed"
+msgstr "Пайваст қатъ шудааст"
+
+#: js/ui/status/network.js:1703
+msgid "Activation of network connection failed"
+msgstr "Фаъолсозии пайвасти шабака қатъ шуд"
+
+#: js/ui/status/nightLight.js:60
+#| msgid "Networking is disabled"
+msgid "Night Light Disabled"
+msgstr "Шабака ғайрифаъол аст"
+
+#: js/ui/status/nightLight.js:61
+msgid "Night Light On"
+msgstr ""
+
+#: js/ui/status/nightLight.js:62
+msgid "Resume"
+msgstr ""
+
+#: js/ui/status/nightLight.js:63
+msgid "Disable Until Tomorrow"
+msgstr ""
+
+#: js/ui/status/power.js:45
+msgid "Power Settings"
+msgstr "Танзимоти барқ"
+
+#: js/ui/status/power.js:61
+msgid "Fully Charged"
+msgstr "Пуррагӣ заряд гирифтааст"
+
+#: js/ui/status/power.js:67
+msgid "Not Charging"
+msgstr ""
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:70 js/ui/status/power.js:76
+msgid "Estimating…"
+msgstr "Ҳисоб шуда истодааст…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:84
+#, javascript-format
+#| msgid "%d∶%02d Remaining (%d%%)"
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d боқӣ мондааст (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:89
+#, javascript-format
+#| msgid "%d∶%02d Until Full (%d%%)"
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "%d∶%02d то пур шудан (%d%%)"
+
+#: js/ui/status/power.js:117 js/ui/status/power.js:119
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/remoteAccess.js:42
+msgid "Screen is Being Shared"
+msgstr ""
+
+#: js/ui/status/remoteAccess.js:44
+#| msgid "Turn Off"
+msgid "Turn off"
+msgstr "Хомӯш кардан"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:77
+#| msgid "Airplane Mode is On"
+msgid "Airplane Mode On"
+msgstr "Ҳолати ҳавопаймо фаъол аст"
+
+#: js/ui/status/system.js:203
+msgid "Switch User"
+msgstr "Иваз кардани корбар"
+
+#: js/ui/status/system.js:215
+msgid "Log Out"
+msgstr "Баромад аз мизи корӣ"
+
+#: js/ui/status/system.js:227
+#| msgid "Sound Settings"
+msgid "Account Settings"
+msgstr "Танзимоти ҳисоб"
+
+#: js/ui/status/system.js:255
+msgid "Orientation Lock"
+msgstr "Қулфи самти экран"
+
+#: js/ui/status/system.js:281
+msgid "Suspend"
+msgstr "Таваққуф кардан"
+
+#: js/ui/status/system.js:291
+msgid "Power Off"
+msgstr "Хомӯш кардани компютер"
+
+#: js/ui/status/thunderbolt.js:261
+msgid "Thunderbolt"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:322
+msgid "Unknown Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:323
+msgid "New device has been detected while you were away. Please disconnect and reconnect the device to start using it."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:326
+msgid "Unauthorized Thunderbolt device"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:327
+msgid "New device has been detected and needs to be authorized by an administrator."
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:333
+msgid "Thunderbolt authorization error"
+msgstr ""
+
+#: js/ui/status/thunderbolt.js:334
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr ""
+
+#: js/ui/status/volume.js:133
+msgid "Volume changed"
+msgstr "Ҳаҷм тағйир ёфт"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:16
+#| msgid "Error"
+msgid "Mirror"
+msgstr "Оина"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:21
+msgid "Join Displays"
+msgstr ""
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:26
+msgid "External Only"
+msgstr ""
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:31
+msgid "Built-in Only"
+msgstr ""
+
+#: js/ui/unlockDialog.js:49
+msgid "Log in as another user"
+msgstr "Ворид шудан бо корбари дигар"
+
+#: js/ui/unlockDialog.js:66
+msgid "Unlock Window"
+msgstr "Кушодани равзана"
+
+#: js/ui/viewSelector.js:173
+msgid "Applications"
+msgstr "Барномаҳо"
+
+#: js/ui/viewSelector.js:177
+msgid "Search"
+msgstr "Ҷустуҷӯ"
+
+#: js/ui/windowAttentionHandler.js:19
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” омода аст"
+
+#: js/ui/windowManager.js:53
+msgid "Do you want to keep these display settings?"
+msgstr "Шумо мехоҳед, ки ин танзимоти дисплейро нигоҳ доред?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:65
+msgid "Revert Settings"
+msgstr "Барқарор кардани тағйирот"
+
+#: js/ui/windowManager.js:68
+msgid "Keep Changes"
+msgstr "Нигоҳ доштани тағйирот"
+
+#: js/ui/windowManager.js:86
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Тағйироти танзимот баъд аз %d сония барқарор мешаванд"
+msgstr[1] "Тағйироти танзимот баъд аз %d сония барқарор мешаванд"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:662
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:26
+msgid "Minimize"
+msgstr "Ҳадди ақал сохтан"
+
+#: js/ui/windowMenu.js:33
+msgid "Unmaximize"
+msgstr "Бекор кардани ҳадди аксар"
+
+#: js/ui/windowMenu.js:37
+msgid "Maximize"
+msgstr "Ҳадди аксар сохтан"
+
+#: js/ui/windowMenu.js:44
+msgid "Move"
+msgstr "Интиқол додан"
+
+#: js/ui/windowMenu.js:50
+msgid "Resize"
+msgstr "Тағйир додани андоза"
+
+#: js/ui/windowMenu.js:57
+msgid "Move Titlebar Onscreen"
+msgstr "Интиқоли номи навор дар экран"
+
+#: js/ui/windowMenu.js:62
+msgid "Always on Top"
+msgstr "Ҳамеша дар боло"
+
+#: js/ui/windowMenu.js:81
+msgid "Always on Visible Workspace"
+msgstr "Ҳамеша дар фазои кории намоён"
+
+#: js/ui/windowMenu.js:95
+msgid "Move to Workspace Left"
+msgstr "Интиқол додан ба фазои кории чап"
+
+#: js/ui/windowMenu.js:101
+msgid "Move to Workspace Right"
+msgstr "Интиқол додан ба фазои кории рост"
+
+#: js/ui/windowMenu.js:107
+msgid "Move to Workspace Up"
+msgstr "Интиқол додан ба фазои кории боло"
+
+#: js/ui/windowMenu.js:113
+msgid "Move to Workspace Down"
+msgstr "Интиқол додан ба фазои кории поён"
+
+#: js/ui/windowMenu.js:131
+msgid "Move to Monitor Up"
+msgstr "Интиқол ба канори болои монитор"
+
+#: js/ui/windowMenu.js:140
+msgid "Move to Monitor Down"
+msgstr "Интиқол ба канори поёни монитор"
+
+#: js/ui/windowMenu.js:149
+msgid "Move to Monitor Left"
+msgstr "Интиқол ба канори чапи монитор"
+
+#: js/ui/windowMenu.js:158
+msgid "Move to Monitor Right"
+msgstr "Интиқол ба канори рости монитор"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Тақвими инкишоф"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#: src/main.c:408
+msgid "Print version"
+msgstr "Версияи чоп"
+
+#: src/main.c:414
+msgid "Mode used by GDM for login screen"
+msgstr "Усуле, ки бо GDM барои экрани воридшавӣ истифода мешавад"
+
+#: src/main.c:420
+#| msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Истифода бурдани ҳолати мушаххас, масалан \"gdm\" барои экрани воридшавӣ"
+
+#: src/main.c:426
+msgid "List possible modes"
+msgstr "Рӯйхати ҳолатҳои имконпазир"
+
+#: src/shell-app.c:260
+msgctxt "program"
+msgid "Unknown"
+msgstr "Номаълум"
+
+#: src/shell-app.c:511
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Оғози “%s” қатъ шуд"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "Паролҳо мувофиқат намекунанд."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "Парол бояд холӣ набошад"
+
+#: src/shell-polkit-authentication-agent.c:348
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Равзанаи гуфтугӯи санҷиши ҳакконият бо корбар бекор карда шуд"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1871
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u барориш"
+msgstr[1] "%u барориш"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1881
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u вуруд"
+msgstr[1] "%u вуруд"
+
+#: subprojects/gvc/gvc-mixer-control.c:2736
+msgid "System Sounds"
+msgstr "Садоҳои низом"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Ҳангоми боркунии равзанаи гуфтугӯи хусусиятҳо барои %s хатогие ба вуҷуд омадааст:"
+
+#~ msgid "Hide tray"
+#~ msgstr "Пинҳон кардани лавҳа"
+
+#~ msgid "Status Icons"
+#~ msgstr "Нишонаҳои вазъият"
+
+#~ msgid "GNOME Shell Extension Preferences"
+#~ msgstr "Хусусиятҳои пасвандҳои восити GNOME"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Восити GNOME (созандаи wayland)"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Намоиш додани ҳафта дар тақвим"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Агар фаъол бошад, санаи ҳафтаи ISO дар тақвим намоиш дода мешавад."
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Бозфиристодани санҷиши ҳаққонияти веб"
+
+#~ msgid "Clear section"
+#~ msgstr "Пок кардани қисмат"
+
+#~ msgid "Events"
+#~ msgstr "Рӯйдодҳо"
+
+#~ msgid "Notifications"
+#~ msgstr "Огоҳиҳо"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d дастгоҳи пайвастшуда"
+#~ msgstr[1] "%d дастгоҳи пайвастшуда"
+
+#~ msgid "In Use"
+#~ msgstr "Дар истифода"
+
+#~ msgid "Authentication required"
+#~ msgstr "Санҷиши ҳаққоният лозим аст"
+
+#~ msgid "connecting..."
+#~ msgstr "пайвастшавӣ..."
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Батарея"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Ҳолати ҳавопаймо"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Намоиш додани қуттии паёмҳо"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "Сатҳи аксари саҳеҳии ҷойгиршавӣ."
+
+#~ msgid "Configures the maximum level of location accuracy applications are allowed to see. Valid options are 'off' (disable location tracking), 'country', 'city', 'neighborhood', 'street', and 'exact' (typically requires GPS receiver). Please keep in mind that this only controls what GeoClue will allow applications to see and they can find user's location on their own using network resources (albeit with street-level accuracy at best)."
+#~ msgstr "Сатҳи баландтарини саҳеҳии ҷойгиршавиро барои барномаҳо танзим мекунад. Имконоти боэътибор: 'хомӯш' (пайгирии ҷойгиршавии ғайрифаъол), 'кишвар', 'шаҳр', 'ҳамсоягӣ', 'кӯча' ва 'дақиқ' (қабулкунандаи GPS лозим аст). Лутфан, қайд кнед, ки ин танҳо GeoClue-ро идора мекунад ва ба барномаҳо иҷозат медиҳад, то ки тавонанд ҷойгиршавии корбаронро тавассути манбаъи шабакавии онҳо пайдо кунанд (ақаллан ҷойгиршавии саҳеҳии кӯча)."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Мураттабсозии тугмаҳо дар сарлавҳаи равзана"
+
+#~ msgid "This key overrides the key in org.gnome.desktop.wm.preferences when running GNOME Shell."
+#~ msgstr "Ин калид ҳангоми иҷроиши GNOME Shell калиди дар org.gnome.desktop.wm.preferences ҷойгирбударо бекор мекунад."
+
+#~ msgid "Extension"
+#~ msgstr "Пасванд"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Пасвандеро интихоб кунед, то ин ки бо ҷаъбаи мураккаби боло онро конфигуратсия кунед."
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Яш"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Д"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "С"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Ч"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "Пш"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Ҷ"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Ш"
+
+#~ msgid "calendar:MY"
+#~ msgstr "тақвим:MY"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Ягон чиз ба нақша нагирифтааст"
+
+#~ msgid "Today"
+#~ msgstr "Имрӯз"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Фардо"
+
+#~ msgid "This week"
+#~ msgstr "Ҳафтаи ҷорӣ"
+
+#~ msgid "Next week"
+#~ msgstr "Ҳафтаи навбатӣ"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Дастгоҳҳои ҷудошаванда"
+
+#~ msgid "Eject"
+#~ msgstr "Баровардан"
+
+#~ msgid "Invitation"
+#~ msgstr "Даъватнома"
+
+#~ msgid "Call"
+#~ msgstr "Занг задан"
+
+#~ msgid "File Transfer"
+#~ msgstr "Интиқолдиҳии файл"
+
+#~ msgid "Chat"
+#~ msgstr "Чат"
+
+#~ msgid "Unmute"
+#~ msgstr "Садоро фаъол кардан"
+
+#~ msgid "Mute"
+#~ msgstr "Бесадо кардан"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Даъватнома барои to %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s шуморо барои ҳамроҳ шудан ба %s даъват мекунад"
+
+#~ msgid "Decline"
+#~ msgstr "Рад кардан"
+
+#~ msgid "Accept"
+#~ msgstr "Қабул кардан"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Занги видеоӣ аз %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Занг аз %s"
+
+#~ msgid "Answer"
+#~ msgstr "Ҷавоб"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s ба шумо %s-ро мефиристад"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s аз шумо иҷозат мепурсад, ки тавонад шуморо дар онлайн бинад"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Санҷиши ҳаққоният қатъ шудааст."
+
+#~ msgid "Encryption error"
+#~ msgstr "Хатои рамзгузорӣ"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Гувоҳинома таъмин нашудааст"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Гувоҳиномаи беэътибор"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Гувоҳинома аз мӯҳлаташ гузашт"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Гувоҳинома фаъол нашудааст"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Номувофиқати гувоҳиномаи номи мизоҷон"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Номувофиқати гувоҳиномаи нақши ангуштон"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Гувоҳиномаи худимзошуда"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Вазъият ба \"офлайн\" гузаштааст"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Гувоҳинома нодуруст аст"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Пайваст рад карда шудааст"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Пайвастшавӣ имконнопазир аст"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Пайваст гум шудааст"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Ин ҳисоб аллакай ба сервер пайваст шудааст"
+
+#~ msgid "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Пайваст тавассути сарчашмаи якхела бо пайвасти нав ҷойиваз карда шуд"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Ҳисоб дар сервер аллакай мавҷуд аст"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Барои коркарди ин пайваст сервер дар айни ҳол хеле машғул мебошад"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Гувоҳинома лағв карда шуд"
+
+#~ msgid "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "Гувоҳинома алгоритми хатарноки рамзро истифода мебарад, ё ки рамзгузории суст дорад"
+
+#~ msgid "The length of the server certificate, or the depth of the server certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr "Дарозии гувоҳиномаи сервер, ё ки чуқурии занҷири гувоҳиномаи сервер, аз маҳдудиятҳое, ки бо китобхонаи рамзгузорӣ таъин шудаанд, дарозтар мебошад"
+
+#~ msgid "Internal error"
+#~ msgstr "Хатои дохилӣ"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "Пайвастшавӣ ба %s имконнопазир аст"
+
+#~ msgid "View account"
+#~ msgstr "Намоиш додани ҳисоб"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Кушодани тақвим"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Танзимоти сана ва вақт"
+
+#~ msgid "Open"
+#~ msgstr "Кушодани файл"
+
+#~ msgid "Remove"
+#~ msgstr "Тоза кардан"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Пок кардани паёмҳо"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Танзимоти огоҳиҳо"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Менюи қуттӣ"
+
+#~ msgid "No Messages"
+#~ msgstr "Ягон паём нест"
+
+#~ msgid "Message Tray"
+#~ msgstr "Қуттии паёмҳо"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Рӯйхати категорияҳо, ки бояд ҳамчун ҷузвдонҳо намоиш дода шаванд"
+
+#~ msgid "Each category name in this list will be represented as folder in the application view, rather than being displayed inline in the main view."
+#~ msgstr "Ҳар як номи категория, ки дар ин рӯйхат мавҷуд аст, дар намуди барномаҳо ҳамчун ҷузвдон ифода мешавад, ба ҷойи намоишдиҳии шомил дар намуди асосӣ."
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Дархости санҷиши ҳаққоният аз %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Дастгоҳи %s мехоҳад, ки бо ин компютер ҷуфт кунад"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Дастгоҳи %s дастрасиро ба хидмати \"%s\" дархост мекунад"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Танҳо дар ин маротиба иҷозат додан"
+
+#~ msgid "Reject"
+#~ msgstr "Рад кардан"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Тасдиқи ҷуфтсозӣ барои %s"
+
+#~ msgid "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "Лутфан, тасдиқ кунед, ки калиди хусусии '%06d' ба калиди дар дастгоҳ мавҷудбуда мувофиқат мекунад."
+
+#~ msgid "Matches"
+#~ msgstr "Мутобиқаткунанда"
+
+#~ msgid "Does not match"
+#~ msgstr "Мувофиқат намекунад"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Дархости ҷуфтсозӣ барои %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Лутфан, рамзи PIN-ро, ки дар дастгоҳ гуфта шудааст, ворид кунед."
+
+#~ msgid "OK"
+#~ msgstr "OK"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Мутаассифода, барои шумо имрӯз ягон хирадмандӣ нест:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "Паёми паёмовар: %s"
+
+#~ msgid "Settings Menu"
+#~ msgstr "Менюи танзимот"
+
+#~ msgid "Session"
+#~ msgstr "Ҷаласа"
+
+#~ msgid "Internally used to store the last IM presence explicitly set by the user. The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr "Ба таври дохилӣ барои нигоҳ доштани мавҷудияти охирини IM, ки бо корбар ба таври бевосита таъин шудааст, истифода мешавад. Дар ин ҷо қимат аз шумориши TpConnectionPresenceType ба вуҷуд меояд."
+
+#~ msgid "Internally used to store the last session presence status for the user. The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr "Ба таври дохилӣ барои нигоҳ доштани вазъияти мавҷудияти ҷаласаи охирин барои корбар истифода мешавад. Дар ин ҷо қимат аз шумориши GsmPresenceStatus ба вуҷуд меояд."
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr "Барои баромадан аз ин барномаҳо ва баромадан аз система, \"Баромадан\"-ро зер кунед."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Баромад аз система."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "Барои баромадан аз ин барномаҳо ва анҷом додани кори система, \"Анҷоми кор\"-ро зер кунед."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Хомӯш кардани система."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr "Барои баромадан аз ин барномаҳо ва бозоғозидани система, \"Бозоғозидан\"-ро зер кунед."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Бозоғозии система."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Анҷоми кор метавонад сабаби гум шудани кори захиранашуда гардад."
+
+#~ msgid "Screenshots"
+#~ msgstr "Суратҳои экран"
+
+#~ msgid "Record a screencast"
+#~ msgstr "Сабти рӯйдодҳои экран"
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Тугма барои иваз кардани сабткунандаи экран"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Тугма барои оғоз/қатъ кардани сабткунандаи экрани дарунсохт."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Миқдори кадрҳо дар сония, ки барои сабти рӯйдодҳои экран истифода мешавад."
+
+#~ msgid "The framerate of the resulting screencast recordered by GNOME Shell's screencast recorder in frames-per-second."
+#~ msgstr "Миқдори кадрҳо дар як сония барои сабти рӯйдодҳои экран, ки бо сабткунандаи рӯйдодҳои экрани GNOME Shell сабт шудааст, дар кадрҳо дар як сония."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "Лӯламаҷрои gstreamer, ки барои рамзӣ кардани рӯйдодҳои экран истифода мешавад"
+
+#~ msgid "Sets the GStreamer pipeline used to encode recordings. It follows the syntax used for gst-launch. The pipeline should have an unconnected sink pad where the recorded video is recorded. It will normally have a unconnected source pad; output from that pad will be written into the output file. However the pipeline can also take care of its own output - this might be used to send the output to an icecast server via shout2send or similar. When unset or set to an empty value, the default pipeline will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is used as a placeholder for a guess at the optimal thread count on the system."
+#~ msgstr "Лӯламаҷрои GStreamer-ро, ки барои рамзӣ кардани сабтҳо истифода мешаванд, таъин мекунад. Он наҳвро, ки барои gst-launch истифода мешавад, риоя мекунад. Лӯламаҷро бояд қабати қабулкунандаи пайвастнашуда дошта бошад, ки дар он видеои сабтшаванда сабт мешавад. Одатан он дорои қабати пайвастнашудаи аслӣ мешавад; натиҷаи он қабат ба файли натиҷаҳо навишта мешавад. Аммо лӯламаҷро метавонад инчунин натиҷаи худро истифода барад - ин метавонад барои фиристодани натиҷа ба сервери icecast тавассути shout2send ё монанд истифода шавад. Агар ин таъин шуда набошад, ё ки ба қимати холӣ таъин шуда бошад, лӯламаҷрои пешфарз истифода мешавад. Дар айни ҳол ин чунин мебошад: \"vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux\", ва тавассути кодеки VP8 ба формати WEBM сабт мекунад. %T ҳамчун пуркунандаи фазо барои ҳисоби тақрибӣ дар миқдори беҳтарини риштаҳо дар система истифода мешавад."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Пасванди файл, ки барои нигоҳ доштани рӯйдодҳои экран истифода мешавад"
+
+#~ msgid "The filename for recorded screencasts will be a unique filename based on the current date, and use this extension. It should be changed when recording to a different container format."
+#~ msgstr "Номи файл барои рӯйдодҳои экрани сабтшуда номи ягонаи файл мебошад, ки бо санаи ҷорӣ асос меёбад ва ин пасвандро истифода мебарад. Вақте ки ба формати дигари дарбаргиранда сабт рафта истода бошад, он бояд тағйир дода шавад."
+
+#~ msgid "Screencast from %d %t"
+#~ msgstr "Рӯйдодҳои экран аз %d %t"
+
+#~ msgid "Power"
+#~ msgstr "Барқ"
+
+#~ msgid "Restart"
+#~ msgstr "Бозоғозидан"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Ҳаҷм, шабака, батарея"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "disabled"
+#~ msgstr "ғайрифаъол"
+
+#~ msgid "More…"
+#~ msgstr "Бештар..."
+
+#~ msgid "Connected (private)"
+#~ msgstr "Пайваст шудааст (шахсӣ)"
+
+#~ msgid "Wired"
+#~ msgstr "Симдор"
+
+#~ msgid "Visibility"
+#~ msgstr "Қобилияти намоиш"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Насб кардани дастгоҳи нав…"
+
+#~ msgid "Send Files…"
+#~ msgstr "Фиристодани файлҳо…"
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Танзимоти дастрасии универсалӣ"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Танзимоти клавиатура"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Танзимоти муш"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s боқӣ мондааст"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "соат"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "дақиқа"
+
+#~ msgid "AC Adapter"
+#~ msgstr "Адаптери AC"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Батареяи лэптоп"
+
+#~ msgid "Monitor"
+#~ msgstr "Монитор"
+
+#~ msgid "Mouse"
+#~ msgstr "Муш"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Телефони мобилӣ"
+
+#~ msgid "Media Player"
+#~ msgstr "Плеери мултимедиа"
+
+#~ msgid "Tablet"
+#~ msgstr "Планшет"
+
+#~ msgid "Computer"
+#~ msgstr "Компютер"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Номаълум"
+
+#~ msgid "Available"
+#~ msgstr "Дастрас"
+
+#~ msgid "Busy"
+#~ msgstr "Машғул"
+
+#~ msgid "Invisible"
+#~ msgstr "Ноаён"
+
+#~ msgid "Away"
+#~ msgstr "Ғоиб"
+
+#~ msgid "Idle"
+#~ msgstr "Ғайрифаъол"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Вазъияти чати шумо ба \"машғул\" тағйир дода мешавад"
+
+#~ msgid "Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."
+#~ msgstr "Акнун огоҳиҳо ғайрифаъол мебошанд, аз он ҷумла паёмҳои чат. Вазъияти онлайни шумо ба тавре танзим карда шуд, ки дигарон фаҳманд, ки эҳтимолан шумо паёмҳои онҳоро намебинед."
+
+#~ msgid "cable unplugged"
+#~ msgstr "сим ҷудо шудааст"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Ҷамъ кардан ё ҷамъ накардани омор дар бораи истифодабарии барномаҳо"
+
+#~ msgid "The shell normally monitors active applications in order to present the most used ones (e.g. in launchers). While this data will be kept private, you may want to disable this for privacy reasons. Please note that doing so won't remove already saved data."
+#~ msgstr "Одатан ин восит барномаҳои фаъолро нигоҳдорӣ мекунад, то ин ки барномаҳои аз ҳама зиёд истифодашаванда (масалан оғозкунандагон) ҳамеша мавҷуд бошанд. Гарчанде ки ин иттилоот махфӣ нигоҳ дошта мешавад, шумо метавонед интихоб кунед, ки инро барои сабабҳои махфият ғайрифаъол кунед. Лутфан, ба қайд гиред, ки чунин амал иттилооти аллакай захирашударо тоза намекунад."
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Ethernet-и худкор"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Паҳннавори худкор"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Шуморагирии худкор"
+
+#~ msgid "Auto %s"
+#~ msgstr "Худкор %s"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Bluetooth-и худкор"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Бесими худкор"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Ворид шудан"
+
+#~ msgid "More..."
+#~ msgstr "Бештар..."
+
+#~ msgid "United Kingdom"
+#~ msgstr "Британияи Кабир"
+
+#~ msgid "Default"
+#~ msgstr "Пешфарз"
diff --git a/po/th.po b/po/th.po
new file mode 100644
index 0000000..f4d3c9e
--- /dev/null
+++ b/po/th.po
@@ -0,0 +1,1941 @@
+# Thai translation for gnome-shell.
+# Copyright (C) 2010-2012 Free Software Foundation, Inc.
+# This file is distributed under the same license as the gnome-shell package.
+# Sira Nokyoongtong <gumaraa@gmail.com>, 2010, 2011.
+# Theppitak Karoonboonyanan <thep@linux.thai.net>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-10-04 06:59+0000\n"
+"PO-Revision-Date: 2012-10-09 18:31+0700\n"
+"Last-Translator: Theppitak Karoonboonyanan <thep@linux.thai.net>\n"
+"Language-Team: Thai <thai-l10n@googlegroups.com>\n"
+"Language: th\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:1
+msgid "Screenshots"
+msgstr "จับภาพหน้าจอ"
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:2
+msgid "Record a screencast"
+msgstr "อัดภาพถ่ายทอดหน้าจอ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "ระบบ"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "แสดงถาดข้อความ"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "เชลล์ GNOME"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "การจัดการหน้าต่างและการเรียกใช้โปรแกรม"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+#: ../js/extensionPrefs/main.js:152
+msgid "GNOME Shell Extension Preferences"
+msgstr "ปรับแต่งส่วนขยายของเชลล์ GNOME"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "ตั้งค่าส่วนขยายต่างๆ ของเชลล์ GNOME"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "เปิดใช้เครื่องมือภายในที่เป็นประโยชน์ต่อนักพัฒนาและนักทดสอบ ผ่านปุ่ม Alt-F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "เปิดให้เข้าถึงเครื่องมือภายในสำหรับดีบั๊กและเฝ้าสังเกต ซึ่งเรียกผ่านกล่องโต้ตอบ Alt-F2"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "Uuids of extensions to enable"
+msgstr "UUID ของส่วนขยายที่จะเปิดใช้งาน"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension DBus methods on org.gnome.Shell."
+msgstr ""
+"ส่วนขยายต่างๆ ของเชลล์ GNOME จะมี UUID ประจำตัว คีย์นี้จะเก็บรายชื่อของส่วนขยายที่จะโหลดใช้ "
+"ส่วนขยายใดๆ ที่ต้องการให้โหลดจะต้องอยู่ในรายชื่อนี้ คุณยังสามารถจัดการรายชื่อนี้ผ่านเมธอด "
+"EnableExtension และ DisableExtension ของ DBus ที่หัวข้อ org.gnome.Shell ได้"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Whether to collect stats about applications usage"
+msgstr "กำหนดว่าจะเก็บสถิติการใช้งานโปรแกรมต่างๆ หรือไม่"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr ""
+"โดยปกติ เชลล์ GNOME จะเฝ้าสังเกตการเรียกใช้งานโปรแกรมต่างๆ เพื่อนำเสนอโปรแกรมที่ใช้บ่อย "
+"(เช่น ในตัวเรียกโปรแกรม) แม้ข้อมูลนี้จะถูกเก็บไว้ไม่มีการเผยแพร่อยู่แล้ว "
+"แต่คุณอาจต้องการปิดการทำงานส่วนนี้ด้วยเหตุผลเรื่องความเป็นส่วนตัว "
+"แต่อย่าลืมว่าการปิดดังกล่าวไม่ได้เป็นการลบข้อมูลที่บันทึกไว้ก่อนหน้านี้"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "รายชื่อของแฟ้มเดสก์ท็อปสำหรับโปรแกรมโปรด"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "โปรแกรมที่แทนด้วยแฟ้มเดสก์ท็อปเหล่านี้จะถูกแสดงในบริเวณโปรแกรมโปรด"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "History for command (Alt-F2) dialog"
+msgstr "ประวัติคำสั่งของกล่องโต้ตอบเรียกโปรแกรม (Alt-F2)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "History for the looking glass dialog"
+msgstr "ประวัติคำสั่งของกล่องโต้ตอบ looking glass"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid ""
+"Internally used to store the last IM presence explicitly set by the user. "
+"The value here is from the TpConnectionPresenceType enumeration."
+msgstr ""
+"ใช้เป็นการภายในเพื่อเก็บสถานะอยู่/ไม่อยู่ล่าสุดของ IM ที่ผู้ใช้เปลี่ยนเองโดยตรง ค่านี้เป็นค่าจาก "
+"enumeration ชนิด TpConnectionPresenceType"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid ""
+"Internally used to store the last session presence status for the user. The "
+"value here is from the GsmPresenceStatus enumeration."
+msgstr ""
+"ใช้เป็นการภายในเพื่อเก็บสถานะอยู่/ไม่อยู่ของผู้ใช้ในวาระล่าสุด ค่านี้เป็นค่าจาก enumeration ชนิด "
+"GsmPresenceStatus"
+
+# See http://en.wikipedia.org/wiki/ISO_week_date
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "Show the week date in the calendar"
+msgstr "แสดงวันที่แบบสัปดาห์ในปฏิทิน"
+
+# See http://en.wikipedia.org/wiki/ISO_week_date
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid "If true, display the ISO week date in the calendar."
+msgstr "ถ้าเลือก จะแสดงวันที่แบบสัปดาห์ของ ISO ในปฏิทิน"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid "Keybinding to open the application menu"
+msgstr "ปุ่มลัดสำหรับเปิดเมนูโปรแกรม"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid "Keybinding to open the application menu."
+msgstr "ปุ่มลัดสำหรับเปิดเมนูโปรแกรม"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "ปุ่มลัดสำหรับสลับการซ่อน/แสดงของถาดข้อความ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "ปุ่มลัดสำหรับสลับการซ่อน/แสดงของถาดข้อความ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Keybinding to toggle the screen recorder"
+msgstr "ปุ่มลัดสำหรับสลับเครื่องมืออัดภาพถ่ายทอดหน้าจอ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Keybinding to start/stop the builtin screen recorder."
+msgstr "ปุ่มลัดสำหรับเริ่ม/หยุดเครื่องมืออัดภาพถ่ายทอดหน้าจอที่มีมากับเชลล์"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Which keyboard to use"
+msgstr "ใช้แป้นพิมพ์ชนิดไหน"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "The type of keyboard to use."
+msgstr "ชนิดของแป้นพิมพ์ที่ใช้"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Framerate used for recording screencasts."
+msgstr "อัตราเฟรมที่จะใช้อัดภาพถ่ายทอดหน้าจอ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr ""
+"อัตราเฟรมของภาพถ่ายทอดหน้าจอที่จะอัดด้วยเครื่องมืออัดภาพถ่ายทอดหน้าจอของเชลล์ GNOME "
+"ในหน่วยเฟรมต่อวินาที"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr "ไปป์ไลน์ของ GStreamer ที่จะใช้ลงรหัสภาพถ่ายทอดหน้าจอ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+"threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is "
+"used as a placeholder for a guess at the optimal thread count on the system."
+msgstr ""
+"กำหนดไปป์ไลน์ของ GStreamer ที่จะใช้ในการลงรหัสวีดิทัศน์ที่อัด โดยกำหนดในรูปแบบที่ gst-launch "
+"ใช้ ไปป์ไลน์นี้ควรจะมี sink pad ปลายเปิดซึ่งจะรับสัญญาณวีดิทัศน์ที่อัด และโดยปกติก็จะมี source "
+"pad ปลายเปิดด้วย ซึ่งจะใช้ปล่อยข้อมูลที่จะเขียนลงแฟ้มปลายทาง อย่างไรก็ดี "
+"ไปป์ไลน์สามารถจัดการข้อมูลออกของตัวเองได้ด้วย ซึ่งอาจจะใช้ในการส่งข้อมูลออกไปยังเซิร์ฟเวอร์ "
+"icecast ผ่าน shout2send หรืออะไรทำนองนี้ ถ้าค่านี้ไม่ได้กำหนดไว้ หรือกำหนดเป็นค่าว่างเปล่า "
+"ก็จะใช้ไปป์ไลน์ปริยาย ซึ่งขณะนี้ืคือ 'vp8enc min_quantizer=13 max_quantizer=13 cpu-"
+"used=5 deadline=1000000 threads=%T ! queue ! webmmux' และจะอัดวีดิทัศน์เป็น WEBM "
+"โดยใช้ตัวอ่าน/ลงรหัส VP8 ตัวแปร %T จะถูกแทนที่ด้วยค่าคาดเดาของจำนวนเธรดที่เหมาะสมของระบบ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "File extension used for storing the screencast"
+msgstr "นามสกุลแฟ้มที่จะใช้เก็บภาพถ่ายทอดหน้าจอ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr ""
+"ชื่อแฟ้มของภาพถ่ายทอดหน้าจอที่อัดจะเป็นชื่อที่ไม่ซ้ำ โดยสร้างจากวันเวลาปัจจุบัน แล้วตามด้วยนามสกุลนี้ "
+"ซึ่งควรเปลี่ยนให้ตรงตามฟอร์แมตที่อัด"
+
+#: ../js/extensionPrefs/main.js:124
+#, c-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "เกิดข้อผิดพลาดขณะโหลดกล่องโต้ตอบปรับแต่งสำหรับ %s:"
+
+#: ../js/extensionPrefs/main.js:164
+msgid "Extension"
+msgstr "ส่วนขยาย"
+
+#: ../js/extensionPrefs/main.js:188
+msgid "Select an extension to configure using the combobox above."
+msgstr "เลือกส่วนขยายที่จะตั้งค่าโดยใช้กล่องคอมโบด้านบน"
+
+#: ../js/gdm/loginDialog.js:528
+msgid "Session..."
+msgstr "วาระ..."
+
+#: ../js/gdm/loginDialog.js:676
+msgctxt "title"
+msgid "Sign In"
+msgstr "เข้าระบบ"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:743
+msgid "Not listed?"
+msgstr "ไม่มีชื่อของคุณงั้นหรือ?"
+
+#: ../js/gdm/loginDialog.js:896 ../js/ui/components/networkAgent.js:137
+#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:373
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:396
+#: ../js/ui/status/bluetooth.js:427 ../js/ui/unlockDialog.js:166
+msgid "Cancel"
+msgstr "ยกเลิก"
+
+#: ../js/gdm/loginDialog.js:901
+msgctxt "button"
+msgid "Sign In"
+msgstr "เข้าระบบ"
+
+#: ../js/gdm/loginDialog.js:1240
+msgid "Login Window"
+msgstr "หน้าต่างเข้าระบบ"
+
+#. Translators: accessible name of the power menu in the login screen
+#: ../js/gdm/powerMenu.js:35
+msgid "Power"
+msgstr "เปิด/ปิด"
+
+#: ../js/gdm/powerMenu.js:89 ../js/ui/userMenu.js:660 ../js/ui/userMenu.js:664
+#: ../js/ui/userMenu.js:775
+msgid "Suspend"
+msgstr "พักเครื่อง"
+
+#: ../js/gdm/powerMenu.js:94
+msgid "Restart"
+msgstr "เปิดเครื่องใหม่"
+
+#: ../js/gdm/powerMenu.js:99 ../js/ui/userMenu.js:662 ../js/ui/userMenu.js:664
+#: ../js/ui/userMenu.js:774
+msgid "Power Off"
+msgstr "ปิดเครื่อง"
+
+#: ../js/gdm/util.js:148
+msgid "Authentication error"
+msgstr "ยืนยันตัวบุคคลผิดพลาด"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/util.js:265
+msgid "(or swipe finger)"
+msgstr "(หรือลูบจอ)"
+
+#: ../js/gdm/util.js:290
+#, c-format
+msgid "(e.g., user or %s)"
+msgstr "(เช่น ผู้ใช้ หรือ %s)"
+
+#: ../js/misc/util.js:92
+msgid "Command not found"
+msgstr "ไม่พบคำสั่ง"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:125
+msgid "Could not parse command:"
+msgstr "ไม่สามารถแจงคำสั่ง:"
+
+#: ../js/misc/util.js:133
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "คำสั่ง '%s' ทำงานล้มเหลว:"
+
+#. Translators: Filter to display all applications
+#: ../js/ui/appDisplay.js:252
+msgid "All"
+msgstr "ทั้งหมด"
+
+#: ../js/ui/appDisplay.js:310
+msgid "APPLICATIONS"
+msgstr "โปรแกรม"
+
+#: ../js/ui/appDisplay.js:370
+msgid "SETTINGS"
+msgstr "ค่าตั้ง"
+
+#: ../js/ui/appDisplay.js:675
+msgid "New Window"
+msgstr "หน้าต่างใหม่"
+
+#: ../js/ui/appDisplay.js:678 ../js/ui/dash.js:271
+msgid "Remove from Favorites"
+msgstr "ลบออกจากรายการโปรด"
+
+#: ../js/ui/appDisplay.js:679
+msgid "Add to Favorites"
+msgstr "เพิ่มเข้าในรายการโปรด"
+
+#: ../js/ui/appFavorites.js:87
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "%s ถูกเพิ่มเข้าในรายการโปรดของคุณแล้ว"
+
+#: ../js/ui/appFavorites.js:118
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "%s ถูกลบออกจากรายการโปรดของคุณแล้ว"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:62
+msgctxt "event list time"
+msgid "All Day"
+msgstr "ทั้งวัน"
+
+#. Translators: Shown in calendar event list, if 24h format
+#: ../js/ui/calendar.js:67
+msgctxt "event list time"
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#. Transators: Shown in calendar event list, if 12h format
+#: ../js/ui/calendar.js:74
+msgctxt "event list time"
+msgid "%l:%M %p"
+msgstr "%l:%M %p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:114
+msgctxt "grid sunday"
+msgid "S"
+msgstr "อา"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:116
+msgctxt "grid monday"
+msgid "M"
+msgstr "จ"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:118
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "อ"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:120
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "พ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:122
+msgctxt "grid thursday"
+msgid "T"
+msgstr "พฤ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:124
+msgctxt "grid friday"
+msgid "F"
+msgstr "ศ"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:126
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ส"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:139
+msgctxt "list sunday"
+msgid "Su"
+msgstr "อา."
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:141
+msgctxt "list monday"
+msgid "M"
+msgstr "จ."
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:143
+msgctxt "list tuesday"
+msgid "T"
+msgstr "อ."
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:145
+msgctxt "list wednesday"
+msgid "W"
+msgstr "พ."
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:147
+msgctxt "list thursday"
+msgid "Th"
+msgstr "พฤ."
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:149
+msgctxt "list friday"
+msgid "F"
+msgstr "ศ."
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:151
+msgctxt "list saturday"
+msgid "S"
+msgstr "ส."
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:699
+msgid "Nothing Scheduled"
+msgstr "ไม่มีกำหนดการ"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:715
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A %d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:718
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A %d %B %Ey"
+
+#: ../js/ui/calendar.js:728
+msgid "Today"
+msgstr "วันนี้"
+
+#: ../js/ui/calendar.js:732
+msgid "Tomorrow"
+msgstr "พรุ่งนี้"
+
+#: ../js/ui/calendar.js:743
+msgid "This week"
+msgstr "สัปดาห์นี้"
+
+#: ../js/ui/calendar.js:751
+msgid "Next week"
+msgstr "สัปดาห์หน้า"
+
+#: ../js/ui/components/autorunManager.js:297
+msgid "Removable Devices"
+msgstr "อุปกรณ์ถอดเสียบ"
+
+#: ../js/ui/components/autorunManager.js:594
+#, c-format
+msgid "Open with %s"
+msgstr "เปิดด้วย %s"
+
+#: ../js/ui/components/autorunManager.js:620
+msgid "Eject"
+msgstr "เอาสื่อออก"
+
+#: ../js/ui/components/keyring.js:86 ../js/ui/components/polkitAgent.js:260
+msgid "Password:"
+msgstr "รหัสผ่าน:"
+
+#: ../js/ui/components/keyring.js:105
+msgid "Type again:"
+msgstr "ป้อนซ้ำอีกครั้ง:"
+
+#: ../js/ui/components/networkAgent.js:132
+msgid "Connect"
+msgstr "เชื่อมต่อ"
+
+#. Cisco LEAP
+#: ../js/ui/components/networkAgent.js:223
+#: ../js/ui/components/networkAgent.js:235
+#: ../js/ui/components/networkAgent.js:262
+#: ../js/ui/components/networkAgent.js:282
+#: ../js/ui/components/networkAgent.js:292
+msgid "Password: "
+msgstr "รหัสผ่าน: "
+
+#. static WEP
+#: ../js/ui/components/networkAgent.js:228
+msgid "Key: "
+msgstr "กุญแจ: "
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/ui/components/networkAgent.js:260
+#: ../js/ui/components/networkAgent.js:278
+msgid "Username: "
+msgstr "ชื่อผู้ใช้: "
+
+#: ../js/ui/components/networkAgent.js:266
+msgid "Identity: "
+msgstr "ข้อมูลบุคคล: "
+
+#: ../js/ui/components/networkAgent.js:268
+msgid "Private key password: "
+msgstr "รหัสผ่านกุญแจส่วนตัว: "
+
+#: ../js/ui/components/networkAgent.js:280
+msgid "Service: "
+msgstr "บริการ: "
+
+#: ../js/ui/components/networkAgent.js:309
+msgid "Authentication required by wireless network"
+msgstr "เครือข่ายไร้สายต้องการการยืนยันตัวบุคคล"
+
+#: ../js/ui/components/networkAgent.js:310
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"'%s'."
+msgstr "ต้องป้อนรหัสผ่านหรือกุญแจสำหรับเข้ารหัสลับเพื่อจะเข้าใช้เครือข่ายไร้สาย '%s'"
+
+#: ../js/ui/components/networkAgent.js:314
+msgid "Wired 802.1X authentication"
+msgstr "การยืนยันตัวบุคคลกับเครือข่ายมีสาย 802.1X"
+
+#: ../js/ui/components/networkAgent.js:316
+msgid "Network name: "
+msgstr "ชื่อเครือข่าย: "
+
+#: ../js/ui/components/networkAgent.js:321
+msgid "DSL authentication"
+msgstr "การยืนยันตัวบุคคลกับ DSL"
+
+#: ../js/ui/components/networkAgent.js:328
+msgid "PIN code required"
+msgstr "ต้องการรหัส PIN"
+
+#: ../js/ui/components/networkAgent.js:329
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "ต้องการรหัส PIN ของอุปกรณ์บรอดแบนด์มือถือ"
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:336
+msgid "Mobile broadband network password"
+msgstr "รหัสผ่านของเครือข่ายบรอดแบนด์มือถือ"
+
+#: ../js/ui/components/networkAgent.js:337
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr "ต้องการรหัสผ่านในการเชื่อมต่อกับ '%s'"
+
+#: ../js/ui/components/polkitAgent.js:55
+msgid "Authentication Required"
+msgstr "จำเป็นต้องยืนยันตัวบุคคล"
+
+#: ../js/ui/components/polkitAgent.js:93
+msgid "Administrator"
+msgstr "ผู้ดูแลระบบ"
+
+#: ../js/ui/components/polkitAgent.js:166
+msgid "Authenticate"
+msgstr "ยืนยันตัวบุคคล"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/components/polkitAgent.js:248 ../js/ui/shellMountOperation.js:381
+msgid "Sorry, that didn't work. Please try again."
+msgstr "ขออภัย ยังไม่ผ่าน โปรดลองใหม่"
+
+#. Translators: this is a filename used for screencast recording
+#: ../js/ui/components/recorder.js:44
+#, no-c-format
+msgid "Screencast from %d %t"
+msgstr "Screencast from %d %t"
+
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/components/telepathyClient.js:237
+msgid "Invitation"
+msgstr "คำเชิญ"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:297
+msgid "Call"
+msgstr "สายเรียก"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:313
+msgid "File Transfer"
+msgstr "การถ่ายโอนแฟ้ม"
+
+#: ../js/ui/components/telepathyClient.js:394
+msgid "Subscription request"
+msgstr "การขอสมัคร"
+
+#: ../js/ui/components/telepathyClient.js:430
+msgid "Connection error"
+msgstr "ข้อผิดพลาดในการเชื่อมต่อ"
+
+#: ../js/ui/components/telepathyClient.js:491
+msgid "Unmute"
+msgstr "เลิกเงียบ"
+
+#: ../js/ui/components/telepathyClient.js:491
+msgid "Mute"
+msgstr "เงียบ"
+
+#. Translators: this is a time format string followed by a date.
+#. If applicable, replace %X with a strftime format valid for your
+#. locale, without seconds.
+#: ../js/ui/components/telepathyClient.js:948
+#, no-c-format
+msgid "Sent at <b>%X</b> on <b>%A</b>"
+msgstr "ส่งเมื่อ<b>วัน%A</b> เวลา <b>%H:%M น.</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25",
+#. shown when you get a chat message in the same year.
+#: ../js/ui/components/telepathyClient.js:954
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>"
+msgstr "ส่งเมื่อ<b>วัน%Aที่ %d %B</b>"
+
+#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
+#. shown when you get a chat message in a different year.
+#: ../js/ui/components/telepathyClient.js:959
+#, no-c-format
+msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+msgstr "ส่งเมื่อ<b>วัน%Aที่ %d %B %Ey</b>"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/components/telepathyClient.js:988
+#, c-format
+msgid "%s is now known as %s"
+msgstr "%s เปลี่ยนชื่อเป็น %s"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1088
+#, c-format
+msgid "Invitation to %s"
+msgstr "คำเชิญให้เข้าห้อง %s"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/components/telepathyClient.js:1096
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr "%s เชิญคุณเข้าร่วมสนทนาที่ห้อง %s"
+
+#: ../js/ui/components/telepathyClient.js:1098
+#: ../js/ui/components/telepathyClient.js:1177
+#: ../js/ui/components/telepathyClient.js:1240
+msgid "Decline"
+msgstr "ปฏิเสธ"
+
+#: ../js/ui/components/telepathyClient.js:1099
+#: ../js/ui/components/telepathyClient.js:1178
+#: ../js/ui/components/telepathyClient.js:1241
+msgid "Accept"
+msgstr "ตอบรับ"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1129
+#, c-format
+msgid "Video call from %s"
+msgstr "สายเรียกแบบวีดิทัศน์จาก %s"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1132
+#, c-format
+msgid "Call from %s"
+msgstr "สายเรียกจาก %s"
+
+#: ../js/ui/components/telepathyClient.js:1137
+#: ../js/ui/status/bluetooth.js:346
+msgid "Reject"
+msgstr "ปฏิเสธ"
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/components/telepathyClient.js:1139
+msgid "Answer"
+msgstr "ตอบรับ"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/components/telepathyClient.js:1171
+#, c-format
+msgid "%s is sending you %s"
+msgstr "%s กำลังส่ง %s ให้คุณ"
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/components/telepathyClient.js:1206
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s ขออนุญาตติดต่อกับคุณแบบออนไลน์"
+
+#: ../js/ui/components/telepathyClient.js:1298
+msgid "Network error"
+msgstr "ข้อผิดพลาดเกี่ยวกับเครือข่าย"
+
+#: ../js/ui/components/telepathyClient.js:1300
+msgid "Authentication failed"
+msgstr "การยืนยันตัวบุคคลไม่ผ่าน"
+
+#: ../js/ui/components/telepathyClient.js:1302
+msgid "Encryption error"
+msgstr "ข้อผิดพลาดเกี่ยวกับการเข้ารหัสลับ"
+
+#: ../js/ui/components/telepathyClient.js:1304
+msgid "Certificate not provided"
+msgstr "ไม่มีใบรับรอง"
+
+#: ../js/ui/components/telepathyClient.js:1306
+msgid "Certificate untrusted"
+msgstr "ไม่เชื่อถือใบรับรอง"
+
+#: ../js/ui/components/telepathyClient.js:1308
+msgid "Certificate expired"
+msgstr "ใบรับรองหมดอายุแล้ว"
+
+#: ../js/ui/components/telepathyClient.js:1310
+msgid "Certificate not activated"
+msgstr "ใบรับรองยังไม่มีการเริ่มใช้"
+
+#: ../js/ui/components/telepathyClient.js:1312
+msgid "Certificate hostname mismatch"
+msgstr "ชื่อโฮสต์ในใบรับรองไม่ตรงกัน"
+
+#: ../js/ui/components/telepathyClient.js:1314
+msgid "Certificate fingerprint mismatch"
+msgstr "ลายนิ้วมือในใบรับรองไม่ตรงกัน"
+
+#: ../js/ui/components/telepathyClient.js:1316
+msgid "Certificate self-signed"
+msgstr "ใบรับรองเซ็นรับรองตัวเอง"
+
+#: ../js/ui/components/telepathyClient.js:1318
+msgid "Status is set to offline"
+msgstr "สถานะเปลี่ยนเป็นออฟไลน์"
+
+#: ../js/ui/components/telepathyClient.js:1320
+msgid "Encryption is not available"
+msgstr "ไม่มีการเข้ารหัสลับให้ใช้งาน"
+
+#: ../js/ui/components/telepathyClient.js:1322
+msgid "Certificate is invalid"
+msgstr "ใบรับรองไม่ถูกต้อง"
+
+#: ../js/ui/components/telepathyClient.js:1324
+msgid "Connection has been refused"
+msgstr "การเชื่อมต่อถูกปฏิเสธ"
+
+#: ../js/ui/components/telepathyClient.js:1326
+msgid "Connection can't be established"
+msgstr "เชื่อมต่อไม่สำเร็จ"
+
+#: ../js/ui/components/telepathyClient.js:1328
+msgid "Connection has been lost"
+msgstr "การเชื่อมต่อถูกตัดขาด"
+
+#: ../js/ui/components/telepathyClient.js:1330
+msgid "This account is already connected to the server"
+msgstr "บัญชีนี้ได้เชื่อมต่อกับเซิร์ฟเวอร์อยู่ก่อนแล้ว"
+
+#: ../js/ui/components/telepathyClient.js:1332
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "การเชื่อมต่อถูกแทนที่ด้วยการเชื่อมต่อใหม่โดยใช้ทรัพยากรเดียวกัน"
+
+#: ../js/ui/components/telepathyClient.js:1334
+msgid "The account already exists on the server"
+msgstr "บัญชีนี้มีอยู่แล้วที่เซิร์ฟเวอร์"
+
+#: ../js/ui/components/telepathyClient.js:1336
+msgid "Server is currently too busy to handle the connection"
+msgstr "เซิร์ฟเวอร์ไม่ว่างพอที่จะจัดการการเชื่อมต่อ"
+
+#: ../js/ui/components/telepathyClient.js:1338
+msgid "Certificate has been revoked"
+msgstr "ใบรับรองถูกเพิกถอนไปแล้ว"
+
+#: ../js/ui/components/telepathyClient.js:1340
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr "ใบรับรองใช้อัลกอริทึมการเข้ารหัสลับที่ไม่ปลอดภัยหรืออ่อนเกินไป"
+
+#: ../js/ui/components/telepathyClient.js:1342
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr ""
+"ความยาวของใบรับรอง หรือความลึกของห่วงโซ่ใบรับรองของเซิร์ฟเวอร์ "
+"มีขนาดเกินขีดจำกัดที่ไลบรารีระบบเข้ารหัสลับกำหนดไว้"
+
+#: ../js/ui/components/telepathyClient.js:1344
+msgid "Internal error"
+msgstr "ข้อผิดพลาดภายใน"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1354
+#, c-format
+msgid "Connection to %s failed"
+msgstr "เชื่อมต่อไปยัง %s ไม่สำเร็จ"
+
+#: ../js/ui/components/telepathyClient.js:1363
+msgid "Reconnect"
+msgstr "เชื่อมต่อใหม่"
+
+#: ../js/ui/components/telepathyClient.js:1364
+msgid "Edit account"
+msgstr "แก้ไขบัญชี"
+
+#: ../js/ui/components/telepathyClient.js:1409
+msgid "Unknown reason"
+msgstr "ไม่ทราบสาเหตุ"
+
+#: ../js/ui/dash.js:245 ../js/ui/dash.js:273
+msgid "Show Applications"
+msgstr "แสดงโปรแกรมต่างๆ"
+
+#: ../js/ui/dateMenu.js:86
+msgid "Date and Time Settings"
+msgstr "ตั้งค่าวันที่และเวลา"
+
+#: ../js/ui/dateMenu.js:109
+msgid "Open Calendar"
+msgstr "เปิดปฏิทิน"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:175
+msgid "%A %B %e, %Y"
+msgstr "%A %d %B %Ey"
+
+#: ../js/ui/endSessionDialog.js:61
+#, c-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s ออกจากระบบ"
+
+#: ../js/ui/endSessionDialog.js:62
+msgctxt "title"
+msgid "Log Out"
+msgstr "ออกจากระบบ"
+
+#: ../js/ui/endSessionDialog.js:63
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr "คลิก \"ออกจากระบบ\" เพื่อปิดโปรแกรมเหล่านี้ และออกจากระบบ"
+
+#: ../js/ui/endSessionDialog.js:65
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s จะออกจากระบบโดยอัตโนมัติใน %d วินาที"
+
+#: ../js/ui/endSessionDialog.js:70
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "คุณจะออกจากระบบโดยอัตโนมัติใน %d วินาที"
+
+#: ../js/ui/endSessionDialog.js:74
+msgid "Logging out of the system."
+msgstr "กำลังออกจากระบบ"
+
+#: ../js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Log Out"
+msgstr "ออกจากระบบ"
+
+#: ../js/ui/endSessionDialog.js:81
+msgctxt "title"
+msgid "Power Off"
+msgstr "ปิดเครื่อง"
+
+#: ../js/ui/endSessionDialog.js:82
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr "คลิก \"ปิดเครื่อง\" เพื่อออกจากโปรแกรมเหล่านี้ และปิดเครื่อง"
+
+#: ../js/ui/endSessionDialog.js:84
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "ระบบจะปิดโดยอัตโนมัติใน %d วินาที"
+
+#: ../js/ui/endSessionDialog.js:88
+msgid "Powering off the system."
+msgstr "กำลังปิดระบบ"
+
+#: ../js/ui/endSessionDialog.js:90 ../js/ui/endSessionDialog.js:107
+msgctxt "button"
+msgid "Restart"
+msgstr "เปิดเครื่องใหม่"
+
+#: ../js/ui/endSessionDialog.js:92
+msgctxt "button"
+msgid "Power Off"
+msgstr "ปิดเครื่อง"
+
+#: ../js/ui/endSessionDialog.js:98
+msgctxt "title"
+msgid "Restart"
+msgstr "เปิดเครื่องใหม่"
+
+#: ../js/ui/endSessionDialog.js:99
+msgid "Click Restart to quit these applications and restart the system."
+msgstr "คลิก \"เปิดเครื่องใหม่\" เพื่อออกจากโปรแกรมเหล่านี้ และเปิดเครื่องใหม่"
+
+#: ../js/ui/endSessionDialog.js:101
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "ระบบจะเปิดใหม่โดยอัตโนมัติใน %d วินาที"
+
+#: ../js/ui/endSessionDialog.js:105
+msgid "Restarting the system."
+msgstr "กำลังเปิดเครื่องใหม่"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "ติดตั้ง"
+
+#: ../js/ui/extensionDownloader.js:204
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr "จะดาวน์โหลดและติดตั้ง '%s' จาก extensions.gnome.org หรือไม่?"
+
+#: ../js/ui/keyboard.js:337
+msgid "tray"
+msgstr "ถาด"
+
+#: ../js/ui/keyboard.js:584 ../js/ui/status/keyboard.js:195
+#: ../js/ui/status/power.js:205
+msgid "Keyboard"
+msgstr "แป้นพิมพ์"
+
+#: ../js/ui/lookingGlass.js:691
+msgid "No extensions installed"
+msgstr "ไม่มีส่วนขยายติดตั้งอยู่"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:745
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr "%s ไม่ได้ให้ข้อผิดพลาดใดออกมา"
+
+#: ../js/ui/lookingGlass.js:751
+msgid "Hide Errors"
+msgstr "ซ่อนข้อผิดพลาด"
+
+#: ../js/ui/lookingGlass.js:755 ../js/ui/lookingGlass.js:815
+msgid "Show Errors"
+msgstr "แสดงข้อผิดพลาด"
+
+#: ../js/ui/lookingGlass.js:764
+msgid "Enabled"
+msgstr "เปิดใช้งาน"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:767 ../src/gvc/gvc-mixer-control.c:1082
+msgid "Disabled"
+msgstr "ปิดใช้"
+
+#: ../js/ui/lookingGlass.js:769
+msgid "Error"
+msgstr "ผิดพลาด"
+
+#: ../js/ui/lookingGlass.js:771
+msgid "Out of date"
+msgstr "ตกรุ่น"
+
+#: ../js/ui/lookingGlass.js:773
+msgid "Downloading"
+msgstr "กำลังดาวน์โหลด"
+
+#: ../js/ui/lookingGlass.js:797
+msgid "View Source"
+msgstr "ดูซอร์ส"
+
+#: ../js/ui/lookingGlass.js:806
+msgid "Web Page"
+msgstr "หน้าเว็บ"
+
+#: ../js/ui/messageTray.js:1081
+msgid "Open"
+msgstr "เปิด"
+
+#: ../js/ui/messageTray.js:1088
+msgid "Remove"
+msgstr "ลบ"
+
+#: ../js/ui/messageTray.js:1533
+msgid "Message Tray"
+msgstr "ถาดข้อความ"
+
+#: ../js/ui/messageTray.js:2544
+msgid "System Information"
+msgstr "ข้อมูลระบบ"
+
+#: ../js/ui/notificationDaemon.js:506 ../src/shell-app.c:373
+msgctxt "program"
+msgid "Unknown"
+msgstr "ไม่ทราบชื่อ"
+
+#: ../js/ui/overview.js:82
+msgid "Undo"
+msgstr "เรียกคืน"
+
+#: ../js/ui/overview.js:127
+msgid "Overview"
+msgstr "ภาพรวม"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/overview.js:201
+msgid "Type to search..."
+msgstr "ป้อนเพื่อค้นหา..."
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/overview.js:222
+msgid "Dash"
+msgstr "แผงปุ่ม"
+
+#: ../js/ui/panel.js:567
+msgid "Quit"
+msgstr "ออก"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:599
+msgid "Activities"
+msgstr "กิจกรรม"
+
+#: ../js/ui/panel.js:965
+msgid "Top Bar"
+msgstr "แถบด้านบน"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:731
+msgid "toggle-switch-us"
+msgstr "toggle-switch-intl"
+
+#: ../js/ui/runDialog.js:205
+msgid "Please enter a command:"
+msgstr "โปรดป้อนคำสั่ง:"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: ../js/ui/screenShield.js:79
+msgid "%A, %B %d"
+msgstr "%A %d %B"
+
+#: ../js/ui/screenShield.js:144
+#, c-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "ข้อความใหม่ %d ข้อความ"
+
+#: ../js/ui/screenShield.js:146
+#, c-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "รายการแจ้งเหตุ %d รายการ"
+
+#: ../js/ui/searchDisplay.js:275
+msgid "Searching..."
+msgstr "กำลังค้นหา..."
+
+#: ../js/ui/searchDisplay.js:323
+msgid "No results."
+msgstr "ไม่พบรายการ"
+
+#: ../js/ui/shellEntry.js:26
+msgid "Copy"
+msgstr "คัดลอก"
+
+#: ../js/ui/shellEntry.js:31
+msgid "Paste"
+msgstr "แปะ"
+
+#: ../js/ui/shellEntry.js:102
+msgid "Show Text"
+msgstr "แสดงข้อความ"
+
+#: ../js/ui/shellEntry.js:104
+msgid "Hide Text"
+msgstr "ซ่อนข้อความ"
+
+#: ../js/ui/shellMountOperation.js:368
+msgid "Password"
+msgstr "รหัสผ่าน"
+
+#: ../js/ui/shellMountOperation.js:389
+msgid "Remember Password"
+msgstr "จำรหัสผ่าน"
+
+#: ../js/ui/shellMountOperation.js:400 ../js/ui/unlockDialog.js:169
+msgid "Unlock"
+msgstr "ปลดล็อค"
+
+#: ../js/ui/status/accessibility.js:39
+msgid "Accessibility"
+msgstr "สิ่งอำนวยความสะดวก"
+
+#: ../js/ui/status/accessibility.js:44
+msgid "Zoom"
+msgstr "ซูม"
+
+#: ../js/ui/status/accessibility.js:51
+msgid "Screen Reader"
+msgstr "โปรแกรมอ่านหน้าจอ"
+
+#: ../js/ui/status/accessibility.js:55
+msgid "Screen Keyboard"
+msgstr "แป้นพิมพ์บนจอ"
+
+#: ../js/ui/status/accessibility.js:59
+msgid "Visual Alerts"
+msgstr "แจ้งเหตุด้วยภาพ"
+
+#: ../js/ui/status/accessibility.js:62
+msgid "Sticky Keys"
+msgstr "ค้างปุ่มกด"
+
+#: ../js/ui/status/accessibility.js:65
+msgid "Slow Keys"
+msgstr "พิมพ์แบบช้า"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Bounce Keys"
+msgstr "ป้องกันการกดแป้นรัว"
+
+#: ../js/ui/status/accessibility.js:71
+msgid "Mouse Keys"
+msgstr "บังคับเมาส์ด้วยแป้น"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Universal Access Settings"
+msgstr "ตั้งค่าการเข้าถึงหลากหลาย"
+
+#: ../js/ui/status/accessibility.js:109
+msgid "High Contrast"
+msgstr "สีตัดกัน"
+
+#: ../js/ui/status/accessibility.js:146
+msgid "Large Text"
+msgstr "อักษรขนาดใหญ่"
+
+#: ../js/ui/status/bluetooth.js:27 ../js/ui/status/bluetooth.js:31
+#: ../js/ui/status/bluetooth.js:251 ../js/ui/status/bluetooth.js:304
+#: ../js/ui/status/bluetooth.js:335 ../js/ui/status/bluetooth.js:371
+#: ../js/ui/status/bluetooth.js:400 ../js/ui/status/network.js:867
+msgid "Bluetooth"
+msgstr "บลูทูท"
+
+#: ../js/ui/status/bluetooth.js:44
+msgid "Visibility"
+msgstr "ความปรากฏเห็น"
+
+#: ../js/ui/status/bluetooth.js:58
+msgid "Send Files to Device..."
+msgstr "ส่งแฟ้มไปยังอุปกรณ์..."
+
+#: ../js/ui/status/bluetooth.js:59
+msgid "Set up a New Device..."
+msgstr "ตั้งค่าอุปกรณ์ชิ้นใหม่..."
+
+#: ../js/ui/status/bluetooth.js:83
+msgid "Bluetooth Settings"
+msgstr "ตั้งค่าบลูทูท"
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:103 ../js/ui/status/network.js:208
+msgid "hardware disabled"
+msgstr "ฮาร์ดแวร์ปิดการใช้งานอยู่"
+
+#: ../js/ui/status/bluetooth.js:196
+msgid "Connection"
+msgstr "การเชื่อมต่อ"
+
+#: ../js/ui/status/bluetooth.js:207 ../js/ui/status/network.js:458
+msgid "disconnecting..."
+msgstr "กำลังตัดการเชื่อมต่อ..."
+
+#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:464
+#: ../js/ui/status/network.js:934
+msgid "connecting..."
+msgstr "กำลังเชื่อมต่อ..."
+
+#: ../js/ui/status/bluetooth.js:238
+msgid "Send Files..."
+msgstr "ส่งแฟ้ม..."
+
+#: ../js/ui/status/bluetooth.js:243
+msgid "Browse Files..."
+msgstr "เรียกดูแฟ้ม..."
+
+#: ../js/ui/status/bluetooth.js:252
+msgid "Error browsing device"
+msgstr "เกิดข้อผิดพลาดขณะเรียกดูแฟ้มในอุปกรณ์"
+
+#: ../js/ui/status/bluetooth.js:253
+#, c-format
+msgid "The requested device cannot be browsed, error is '%s'"
+msgstr "อุปกรณ์ที่ร้องขอไม่สามารถเรียกดูแฟ้มได้ ข้อผิดพลาดคือ '%s'"
+
+#: ../js/ui/status/bluetooth.js:261
+msgid "Keyboard Settings"
+msgstr "ตั้งค่าแป้นพิมพ์"
+
+#: ../js/ui/status/bluetooth.js:264
+msgid "Mouse Settings"
+msgstr "ตั้งค่าเมาส์"
+
+#: ../js/ui/status/bluetooth.js:269 ../js/ui/status/volume.js:234
+msgid "Sound Settings"
+msgstr "ตั้งค่าเสียง"
+
+#: ../js/ui/status/bluetooth.js:336
+#, c-format
+msgid "Authorization request from %s"
+msgstr "มีการขออนุญาตจาก %s"
+
+#: ../js/ui/status/bluetooth.js:342
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "อุปกรณ์ %s ต้องการเข้าถึงบริการ '%s'"
+
+#: ../js/ui/status/bluetooth.js:344
+msgid "Always grant access"
+msgstr "อนุญาตให้เข้าใช้เสมอ"
+
+#: ../js/ui/status/bluetooth.js:345
+msgid "Grant this time only"
+msgstr "อนุญาตครั้งนี้เท่านั้น"
+
+#: ../js/ui/status/bluetooth.js:372
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "การยืนยันการจับคู่สำหรับ %s"
+
+#: ../js/ui/status/bluetooth.js:378 ../js/ui/status/bluetooth.js:408
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "อุปกรณ์ %s ต้องการจับคู่กับคอมพิวเตอร์เครื่องนี้"
+
+#: ../js/ui/status/bluetooth.js:379
+#, c-format
+msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+msgstr "กรุณายืนยันว่ารหัส PIN '%06d' ตรงกันกับของอุปกรณ์ดังกล่าวหรือไม่"
+
+#: ../js/ui/status/bluetooth.js:381
+msgid "Matches"
+msgstr "ตรงกัน"
+
+#: ../js/ui/status/bluetooth.js:382
+msgid "Does not match"
+msgstr "ไม่ตรงกัน"
+
+#: ../js/ui/status/bluetooth.js:401
+#, c-format
+msgid "Pairing request for %s"
+msgstr "คำร้องขอจับคู่สำหรับ %s"
+
+#: ../js/ui/status/bluetooth.js:409
+msgid "Please enter the PIN mentioned on the device."
+msgstr "กรุณาป้อนรหัส PIN ที่ระบุบนอุปกรณ์ดังกล่าว"
+
+#: ../js/ui/status/bluetooth.js:426
+msgid "OK"
+msgstr "ตกลง"
+
+#: ../js/ui/status/keyboard.js:228
+msgid "Show Keyboard Layout"
+msgstr "แสดงผังแป้นพิมพ์"
+
+#: ../js/ui/status/keyboard.js:233
+msgid "Region and Language Settings"
+msgstr "ตั้งค่าท้องที่และภาษา"
+
+#: ../js/ui/status/lockScreenMenu.js:18
+msgid "Volume, network, battery"
+msgstr "ความดังเสียง, เครือข่าย, แบตเตอรี่"
+
+#: ../js/ui/status/network.js:94
+msgid "<unknown>"
+msgstr "<ไม่ทราบ>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:230
+msgid "disabled"
+msgstr "ปิดใช้"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:456
+msgid "unmanaged"
+msgstr "ไม่ถูกจัดการ"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:467 ../js/ui/status/network.js:937
+msgid "authentication required"
+msgstr "ต้องยืนยันตัวบุคคล"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:477
+msgid "firmware missing"
+msgstr "ขาดเฟิร์มแวร์"
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:484
+msgid "cable unplugged"
+msgstr "ไม่ได้เสียบสายเคเบิล"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:489
+msgid "unavailable"
+msgstr "ไม่สามารถใช้งานได้"
+
+#: ../js/ui/status/network.js:491 ../js/ui/status/network.js:939
+msgid "connection failed"
+msgstr "เชื่อมต่อไม่สำเร็จ"
+
+#: ../js/ui/status/network.js:552 ../js/ui/status/network.js:1529
+msgid "More..."
+msgstr "เพิ่มเติม..."
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1459
+msgid "Connected (private)"
+msgstr "เชื่อมต่อแล้ว (ส่วนตัว)"
+
+#: ../js/ui/status/network.js:663
+msgid "Auto Ethernet"
+msgstr "อีเทอร์เน็ตอัตโนมัติ"
+
+#: ../js/ui/status/network.js:721
+msgid "Auto broadband"
+msgstr "บรอดแบนด์อัตโนมัติ"
+
+#: ../js/ui/status/network.js:724
+msgid "Auto dial-up"
+msgstr "หมุนโทรศัพท์อัตโนมัติ"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:853 ../js/ui/status/network.js:1476
+#, c-format
+msgid "Auto %s"
+msgstr "%s อัตโนมัติ"
+
+#: ../js/ui/status/network.js:855
+msgid "Auto bluetooth"
+msgstr "บลูทูทอัตโนมัติ"
+
+#: ../js/ui/status/network.js:1478
+msgid "Auto wireless"
+msgstr "ไร้สายอัตโนมัติ"
+
+#: ../js/ui/status/network.js:1575
+msgid "Enable networking"
+msgstr "เปิดใช้งานเครือข่าย"
+
+#: ../js/ui/status/network.js:1597
+msgid "Wired"
+msgstr "ใช้สาย"
+
+#: ../js/ui/status/network.js:1608
+msgid "Wireless"
+msgstr "ไร้สาย"
+
+#: ../js/ui/status/network.js:1618
+msgid "Mobile broadband"
+msgstr "บรอดแบนด์มือถือ"
+
+#: ../js/ui/status/network.js:1628
+msgid "VPN Connections"
+msgstr "การเชื่อมต่อ VPN"
+
+#: ../js/ui/status/network.js:1635
+msgid "Network Settings"
+msgstr "ตั้งค่าเครือข่าย"
+
+#: ../js/ui/status/network.js:1679
+msgid "Network Manager"
+msgstr "โปรแกรมจัดการเครือข่าย"
+
+#: ../js/ui/status/network.js:1769
+msgid "Connection failed"
+msgstr "เชื่อมต่อไม่สำเร็จ"
+
+#: ../js/ui/status/network.js:1770
+msgid "Activation of network connection failed"
+msgstr "เปิดใช้ช่องเชื่อมต่อไม่สำเร็จ"
+
+#: ../js/ui/status/network.js:2065
+msgid "Networking is disabled"
+msgstr "การใช้เครือข่ายถูกปิด"
+
+#: ../js/ui/status/power.js:55
+msgid "Battery"
+msgstr "แบตเตอรี่"
+
+#: ../js/ui/status/power.js:72
+msgid "Power Settings"
+msgstr "ตั้งค่าพลังงาน"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:94
+msgid "Estimating..."
+msgstr "กำลังประเมิน..."
+
+#: ../js/ui/status/power.js:101
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "เหลืออีก %d ชั่วโมง"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:104
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "เหลืออีก %d %s %d %s"
+
+#: ../js/ui/status/power.js:106
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "ชั่วโมง"
+
+#: ../js/ui/status/power.js:106
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "นาที"
+
+#: ../js/ui/status/power.js:109
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "เหลืออีก %d นาที"
+
+#: ../js/ui/status/power.js:112 ../js/ui/status/power.js:186
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d%%"
+
+#: ../js/ui/status/power.js:195
+msgid "AC adapter"
+msgstr "ที่แปลงไฟ AC"
+
+#: ../js/ui/status/power.js:197
+msgid "Laptop battery"
+msgstr "แบตเตอรี่แล็ปท็อป"
+
+#: ../js/ui/status/power.js:199
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:201
+msgid "Monitor"
+msgstr "จอภาพ"
+
+#: ../js/ui/status/power.js:203
+msgid "Mouse"
+msgstr "เมาส์"
+
+#: ../js/ui/status/power.js:207
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:209
+msgid "Cell phone"
+msgstr "โทรศัพท์มือถือ"
+
+#: ../js/ui/status/power.js:211
+msgid "Media player"
+msgstr "เครื่องเล่นสื่อ"
+
+#: ../js/ui/status/power.js:213
+msgid "Tablet"
+msgstr "แท็บเบล็ต"
+
+#: ../js/ui/status/power.js:215
+msgid "Computer"
+msgstr "คอมพิวเตอร์"
+
+#: ../js/ui/status/power.js:217
+msgctxt "device"
+msgid "Unknown"
+msgstr "ไม่ทราบ"
+
+#. Translators: This is the label for audio volume
+#: ../js/ui/status/volume.js:47 ../js/ui/status/volume.js:221
+msgid "Volume"
+msgstr "ความดังเสียง"
+
+#: ../js/ui/status/volume.js:59
+msgid "Microphone"
+msgstr "ไมโครโฟน"
+
+#: ../js/ui/unlockDialog.js:176
+msgid "Log in as another user"
+msgstr "เข้าระบบในนามผู้ใช้อื่น"
+
+#: ../js/ui/userMenu.js:180
+msgid "Available"
+msgstr "อยู่"
+
+#: ../js/ui/userMenu.js:183
+msgid "Busy"
+msgstr "ไม่ว่าง"
+
+#: ../js/ui/userMenu.js:186
+msgid "Invisible"
+msgstr "ซ่อนตัว"
+
+#: ../js/ui/userMenu.js:189
+msgid "Away"
+msgstr "ไม่อยู่"
+
+#: ../js/ui/userMenu.js:192
+msgid "Idle"
+msgstr "ไม่มีกิจกรรม"
+
+#: ../js/ui/userMenu.js:195
+msgid "Unavailable"
+msgstr "ออฟไลน์"
+
+#: ../js/ui/userMenu.js:740
+msgid "Notifications"
+msgstr "การแจ้งเหตุ"
+
+#: ../js/ui/userMenu.js:748
+msgid "System Settings"
+msgstr "ตั้งค่าระบบ"
+
+#: ../js/ui/userMenu.js:756
+msgid "Switch User"
+msgstr "สลับผู้ใช้"
+
+#: ../js/ui/userMenu.js:761
+msgid "Log Out"
+msgstr "ออกจากระบบ"
+
+#: ../js/ui/userMenu.js:766
+msgid "Lock"
+msgstr "ล็อค"
+
+#: ../js/ui/userMenu.js:781
+msgid "Install Updates & Restart"
+msgstr "ติดตั้งรายการปรับรุ่น & เปิดเครื่องใหม่"
+
+#: ../js/ui/userMenu.js:799
+msgid "Your chat status will be set to busy"
+msgstr "จะกำหนดสถานะการสนทนาของคุณเป็นไม่ว่าง"
+
+#: ../js/ui/userMenu.js:800
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+"จะปิดการแจ้งเหตุ รวมถึงข้อความสนทนาด้วย "
+"และได้ปรับสถานะออนไลน์ของคุณเพื่อให้คนอื่นทราบว่าคุณอาจไม่เห็นข้อความจากพวกเขาแล้ว"
+
+#: ../js/ui/viewSelector.js:85
+msgid "Windows"
+msgstr "หน้าต่าง"
+
+#: ../js/ui/viewSelector.js:89
+msgid "Applications"
+msgstr "โปรแกรม"
+
+#: ../js/ui/viewSelector.js:93
+msgid "Search"
+msgstr "ค้นหา"
+
+#: ../js/ui/wanda.js:117
+#, c-format
+msgid ""
+"Sorry, no wisdom for you today:\n"
+"%s"
+msgstr ""
+"เสียใจด้วย วันนี้ไม่มีคำคมสำหรับคุณ:\n"
+"%s"
+
+#: ../js/ui/wanda.js:121
+#, c-format
+msgid "%s the Oracle says"
+msgstr "หมอดู %s กล่าว"
+
+#: ../js/ui/wanda.js:162
+msgid "Your favorite Easter Egg"
+msgstr "ไข่อีสเตอร์สุดโปรดของคุณ"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, c-format
+msgid "'%s' is ready"
+msgstr "'%s' พร้อมแล้ว"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "ปฏิทิน Evolution"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1089
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "ช่องเสียงออก %u ช่อง"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1099
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "ช่องเสียงเข้า %u ช่อง"
+
+#: ../src/gvc/gvc-mixer-control.c:1397
+msgid "System Sounds"
+msgstr "เสียงของระบบ"
+
+#: ../src/main.c:330
+msgid "Print version"
+msgstr "แสดงเลขรุ่น"
+
+#: ../src/main.c:336
+msgid "Mode used by GDM for login screen"
+msgstr "โหมดสำหรับให้ GDM เรียกใช้ในหน้าจอเข้าระบบ"
+
+#: ../src/main.c:342
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "ใช้โหมดเฉพาะ เช่น \"gdm\" สำหรับหน้าจอเข้าระบบ"
+
+#: ../src/main.c:348
+msgid "List possible modes"
+msgstr "แสดงโหมดทั้งหมดที่เป็นไปได้"
+
+#: ../src/shell-app.c:621
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr "เรียก '%s' ไม่สำเร็จ"
+
+#: ../src/shell-keyring-prompt.c:708
+msgid "Passwords do not match."
+msgstr "รหัสผ่านไม่ตรงกัน"
+
+#: ../src/shell-keyring-prompt.c:716
+msgid "Password cannot be blank"
+msgstr "รหัสผ่านว่างเปล่าไม่ได้"
+
+#: ../src/shell-mobile-providers.c:80
+msgid "United Kingdom"
+msgstr "สหราชอาณาจักร"
+
+#: ../src/shell-mobile-providers.c:526
+msgid "Default"
+msgstr "ปริยาย"
+
+#: ../src/shell-polkit-authentication-agent.c:343
+msgid "Authentication dialog was dismissed by the user"
+msgstr "กล่องโต้ตอบยืนยันตัวบุคคลถูกผู้ใช้ปิดทิ้ง"
+
+#~ msgid "Switch Session"
+#~ msgstr "สลับวาระ"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "เลิกเมานท์ '%s' ไม่สำเร็จ"
+
+#~ msgid "Retry"
+#~ msgstr "ลองใหม่"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ที่หลักๆ และอุปกรณ์"
+
+#~ msgid "Home"
+#~ msgstr "บ้าน"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should not be loaded."
+#~ msgstr ""
+#~ "ส่วนขยายต่างๆ ของเชลล์ GNOME จะมี uuid ประจำตัว "
+#~ "ค่านี้จะเก็บรายชื่อของส่วนขยายที่จะไม่เรียกใช้"
+
+#~ msgid ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display date in "
+#~ "the clock, in addition to time."
+#~ msgstr ""
+#~ "ถ้าเลือก และรูปแบบเวลาเป็น \"12-hour\" หรือ \"24-hour\" "
+#~ "ก็จะแสดงวันที่ในนาฬิกาควบคู่กับเวลาด้วย"
+
+#~ msgid "Show date in clock"
+#~ msgstr "แสดงวันที่ในนาฬิกา"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "แสดงเวลาพร้อมวินาที"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %d %b, %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %d %b, %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %d %b, %l:%M:%S %p"
+
+#~ msgid "%a %b %e, %l:%M %p"
+#~ msgstr "%a %d %b, %l:%M %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "%a %l:%M %p"
+#~ msgstr "%a %l:%M %p"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "รายการล่าสุด"
+
+#~ msgid "Connect to..."
+#~ msgstr "เชื่อมต่อไปยัง..."
+
+#~ msgid "Power Off..."
+#~ msgstr "ปิดเครื่อง..."
+
+#~ msgid "Lock Screen"
+#~ msgstr "ล็อคหน้าจอ"
+
+#~ msgid "Log Out..."
+#~ msgstr "ออกจากระบบ..."
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "คุณเชื่อมต่อกับเครือข่ายไร้สาย '%s' แล้ว"
+
+#~ msgid "You're now connected to wired network '%s'"
+#~ msgstr "คุณเชื่อมต่อกับเครือข่ายใช้สาย '%s' แล้ว"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "คุณเชื่อมต่อกับเครือข่าย VPN '%s' แล้ว"
+
+#~ msgid "%s is online."
+#~ msgstr "%s ออนไลน์"
+
+#~ msgid "%s is away."
+#~ msgstr "%s ไม่อยู่"
+
+#~ msgid "%s is busy."
+#~ msgstr "%s ไม่ว่าง"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "%s เปิดเสร็จแล้ว"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "ไม่ถึงหนึ่งนาทีก่อน"
+
+#~ msgid "%d minute ago"
+#~ msgid_plural "%d minutes ago"
+#~ msgstr[0] "%d นาทีก่อน"
+
+#~ msgid "%d hour ago"
+#~ msgid_plural "%d hours ago"
+#~ msgstr[0] "%d ชั่วโมงก่อน"
+
+#~ msgid "%d day ago"
+#~ msgid_plural "%d days ago"
+#~ msgstr[0] "%d วันก่อน"
+
+#~ msgid "%d week ago"
+#~ msgid_plural "%d weeks ago"
+#~ msgstr[0] "%d สัปดาห์ก่อน"
+
+#~ msgid "Home Folder"
+#~ msgstr "โฟลเดอร์บ้าน"
+
+#~ msgid "Customize the panel clock"
+#~ msgstr "ปรับแต่งนาฬิกาบนพาเนล"
+
+#~ msgid "Custom format of the clock"
+#~ msgstr "รูปแบบนาฬิกากำหนดเอง"
+
+#~ msgid "Hour format"
+#~ msgstr "รูปแบบชั่วโมง"
+
+#~ msgid ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+#~ msgstr ""
+#~ "ถ้าเลือก และรูปแบบเวลาเป็น \"12-hour\" หรือ \"24-hour\" ก็จะแสดงวินาทีในเวลาด้วย"
+
+#~ msgid ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+#~ msgstr ""
+#~ "ค่านี้กำหนดรูปแบบของเวลาในแอพเพล็ตนาฬิกาเมื่อกำหนดรูปแบบเป็น \"custom\" "
+#~ "คุณสามารถใช้รหัสการแปลงของ strftime() เพื่อระบุรูปแบบที่ต้องการได้ "
+#~ "ดูข้อมูลเพิ่มเติมได้จากคู่มือของ strftime()"
+
+#~ msgid ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+#~ msgstr ""
+#~ "ค่านี้กำหนดรูปแบบชั่วโมงที่จะใช้ในแอพเพล็ตนาฬิกา ค่าที่เป็นไปได้คือ\"12-hour\", \"24-hour"
+#~ "\", \"unix\" และ \"custom\" ถ้ากำหนดเป็น \"unix\" "
+#~ "นาฬิกาจะแสดงเวลาเป็นวินาทีนับจาก 1970-01-01 ถ้ากำหนดเป็น \"custom\" "
+#~ "นาฬิกาจะแสดงตามรูปแบบที่กำหนดใน custom_format สังเกตว่าถ้ากำหนดเป็น \"unix\" หรือ "
+#~ "\"custom\" ค่า show_date และ show_seconds ก็จะไม่มีผล"
+
+#~ msgid "Enable lens mode"
+#~ msgstr "เปิดใช้โหมดแว่นขยาย"
+
+#~ msgid ""
+#~ "The magnified view either fills the entire screen, or occupies the top-"
+#~ "half, bottom-half, left-half, or right-half of the screen."
+#~ msgstr ""
+#~ "ช่องแสดงภาพขยายอาจจะใหญ่เต็มจอภาพ (full-screen) หรืออาจจะกินที่แค่ครึ่งบน (top-"
+#~ "half) ครึ่งล่าง (bottom-half) ครึ่งซ้าย (left-half) หรือครึ่งขวา (right-half)"
+
+#~ msgid ""
+#~ "Whether the magnified view should be centered over the location of the "
+#~ "system mouse and move with it."
+#~ msgstr "กำหนดว่าจะให้ภาพขยายหน้าจอวางตำแหน่งกึ่งกลางที่เมาส์และเคลื่อนย้ายไปตามเมาส์หรือไม่"
+
+#~ msgid "Clock Format"
+#~ msgstr "รูปแบบนาฬิกา"
+
+#~ msgid "Clock Preferences"
+#~ msgstr "ปรับแต่งค่านาฬิกา"
+
+#~ msgid "Panel Display"
+#~ msgstr "การแสดงบนพาเนล"
+
+#~ msgid "Show seco_nds"
+#~ msgstr "แสดง_วินาที"
+
+#~ msgid "_12 hour format"
+#~ msgstr "แ_บบ AM/PM"
+
+#~ msgid "_24 hour format"
+#~ msgstr "แบบ _24 ขั่วโมง"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "ปรับแต่ง"
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "ลากมาที่นี่เพื่อเพิ่มเป็นรายการโปรด"
+
+#~ msgid "Find"
+#~ msgstr "หา"
+
+#~ msgid "Preferences"
+#~ msgstr "ปรับแต่ง"
+
+#~ msgid "Shut Down..."
+#~ msgstr "ปิดเครื่อง..."
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "ไม่สามารถลบพื้นที่ทำงานแรกได้"
diff --git a/po/tr.po b/po/tr.po
new file mode 100644
index 0000000..e526b6e
--- /dev/null
+++ b/po/tr.po
@@ -0,0 +1,2972 @@
+# Turkish translation of gnome-shell
+# Copyright (C) 2009, 2011 the Free Software Foundation, Inc.
+# Copyright (C) 2012-2023 gnome-shell'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+#
+# Baris Cicek <baris@teamforce.name.tr>, 2009.
+# ercin <ercin@linux.org.tr>, 2011.
+# Osman Karagöz <osmank3@gmail.com>, 2013.
+# Bayram Güçlü <byrmgcl@yandex.com.tr>, 2014.
+# Gökhan Gurbetoğlu <ggurbet@gmail.com>, 2014.
+# Muhammet Kara <muhammetk@gmail.com>, 2011-2017.
+# Furkan Ahmet Kara <furkanahmetkara.fk@gmail.com>, 2017, 2018.
+# Sabri Ünal <libreajans@gmail.com>, 2014, 2019, 2022, 2023.
+# Emin Tufan Çetin <etcetin@gmail.com>, 2017-2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 00:30+0000\n"
+"PO-Revision-Date: 2023-02-14 04:52+0300\n"
+"Last-Translator: Sabri Ünal <libreajans@gmail.com>\n"
+"Language-Team: Turkish <gnome-turk@gnome.org>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 3.1.1\n"
+"X-Project-Style: gnome\n"
+"X-DL-Team: tr\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: Translating\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Başlatıcılar"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Gözde uygulama 1ʼi aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Gözde uygulama 2ʼyi aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Gözde uygulama 3ʼü aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Gözde uygulama 4ʼü aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Gözde uygulama 5ʼi aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Gözde uygulama 6ʼyı aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Gözde uygulama 7ʼyi aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Gözde uygulama 8ʼi aktifleştir"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Gözde uygulama 9ʼu aktifleştir"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Ekran Görüntüleri"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Etkileşimli olarak ekran görüntüsü al"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Ekran görüntüsü al"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Pencerenin ekran görüntüsünü al"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Etkileşimli olarak ekranı kaydet"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Sistem"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Bildirim listesini göster"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Etkin bildirime odaklan"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Genel görünümü göster"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Tüm uygulamaları göster"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Uygulama menüsünü aç"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Kabuğu"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Pencere yönetimi ve uygulama başlatma"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Geliştirici ve sınayıcılar için Alt-F2’den ulaşılan araçları etkinleştir"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 kutucuğu ile tümleşik hata ayıklama ve izleme uygulamalarına erişim "
+"sağlar."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Etkinleştirilecek uzantıların UUID’si"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Kabuğu uzantılarında UUID adında özellik bulunur; bu anahtar, "
+"yüklenmesi gereken uzantıları listeler. Yüklenmesi istenen her tür uzantının "
+"bu listede olması gerekir. Ayrıca bu listeyi, org.gnome.Shell üzerindeki "
+"EnableExtension ve DisableExtension DBus yöntemleri ile de "
+"değiştirebilirsiniz."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Devre dışı bırakmaya zorlanacak uzantıların UUID’leri"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Kabuğu uzantılarında UUID adında özellik bulunur; bu anahtar, geçerli "
+"kipin parçası olarak yüklenmiş olsa bile devre dışı bırakılması gereken "
+"uzantıları listeler. Bu listeyi, org.gnome.Shell üzerindeki EnableExtension "
+"ve DisableExtension D-Bus yöntemleriyle de değiştirebilirsiniz. Bu anahtar "
+"“enabled-extensions” ayarına göre önceliklidir."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Kullanıcı uzantılarını devre dışı bırak"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Kullanıcının etkinleştirdiği tüm uzantıları “enabled-extension” ayarına etki "
+"etmeden devre dışı bırak."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Uzantının sürüm uyumu doğrulamasını devre dışı bırak"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Kabuğu yalnızca şu andaki çalışan sürümü desteklediklerini ileri süren "
+"uzantıları yükleyecektir. Bu seçeneği etkinleştirmek bu denetimi devre dışı "
+"bırakacak ve desteklendiği ileri sürülen sürümleri dikkate almaksızın, tüm "
+"uygulamaları yüklemeye çalışacaktır."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Sık kullanılan uygulamalar için masa üstü dosyalarının ID listesi"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Bu tanımlayıcıya sahip uygulamalar sık kullanılanlar bölümünde gösterilecek."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Komut kutucuğu (Alt-F2) geçmişi"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Ayna iletişim penceresinin geçmişi"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Kullanıcı menüsünde “Oturumu Kapat” menü ögesini daima göster."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Bu ayar tek kullanıcı oturum açtığında “Oturumu Kapat” menü ögesinin "
+"gizlenmesini önler."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Şifreli ya da uzak dosya sistemlerini bağlamak için parolanın anımsanması"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Kabuk, şifreli aygıt ya da uzak dosya sistemi bağlandığında parola "
+"isteyecek. Eğer parola gelecekte kullanılmak üzere kaydedilebiliyorsa, "
+"“Parolayı Anımsa” onay kutusu sunulacak. Bu anahtar, onay kutusunun "
+"öntanımlı durumunu belirler."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "En son seçilen öntanımlı olmayan güç profili"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"Bazı sistemler ikiden çok güç profilini destekler. İki profil arasında "
+"geçişi destekleyebilmek için bu anahtar, en son seçilen öntanımlı olmayan "
+"profili kaydeder."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr ""
+"“GNOME’a Hoş Geldiniz” iletişim penceresinin en son hangi sürüm için "
+"gösterildiği"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Bu anahtar, “GNOME’a Hoş Geldiniz” iletişim penceresinin en son hangi sürüm "
+"için gösterildiğini belirler. Boş dizge, en eski olası sürüme karşılık gelir "
+"ve büyük sayı henüz var olmayan sürümlere karşılık gelir. Bu büyük sayı, "
+"iletişim penceresini devre dışı bırakmada da kullanılabilir."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Uygulama seçicinin düzeni"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Uygulama seçicinin düzeni. Dizideki her girdi bir sayfadır. Sayfalar GNOME "
+"Kabuğu’nda göründüğü sırayla depolanır. Her sayfa bir “application id” → "
+"'data' çifti içerir. Şimdilik şu değerler 'data' olarak depolanır: • "
+"“position”: sayfadaki uygulama simgesinin konumu"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Uygulama menüsünü açmak için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Uygulama menüsünü açmak için klavye kısayolu."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Genel görünüm durumları arası geçiş için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Oturum, pencere seçici ve uygulama ızgarası arası geçiş için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Uygulama ızgarası, pencere seçici ve oturum arası geçiş için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "“Uygulamaları Göster” görünümünü açmak için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr ""
+"Etkinlikler Genel Görünümünün “Uygulamaları Göster” görünümünü açmak için "
+"klavye kısayolu."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Genel görünümü açmak için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Etkinlikler Genel Görünümünü açmak için klavye kısayolu."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Bildirim listesinin görünürlüğünü değiştirmek için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Bildirim listesinin görünürlüğünü değiştirmek için klavye kısayolu."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Etkin bildirime odaklanmak için klavye kısayolu"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Etkin bildirime odaklanmak için klavye kısayolu."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Uygulama 1ʼe geç"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Uygulama 2ʼye geç"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Uygulama 3ʼe geç"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Uygulama 4ʼe geç"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Uygulama 5ʼe geç"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Uygulama 6ʼya geç"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Uygulama 7ʼye geç"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Uygulama 8ʼe geç"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Uygulama 9ʼa geç"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Geçiş menüsünü geçerli çalışma alanıyla sınırla."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Eğer bu seçenek etkinse, yalnızca geçerli çalışma alanında penceresi olan "
+"uygulamalar geçiş menüsünde gösterilir. Aksi halde, tüm uygulamalar "
+"görünecektir."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Uygulama simge kipi."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Pencerelerin değiştiricideki gösterim biçimini yapılandırır. Uygun "
+"olasılıklar; “thumbnail-only” (pencerenin küçük resmini gösterir), “app-icon-"
+"only” (yalnızca uygulama simgesini gösterir) ya da “both” (her ikisi) "
+"değerleridir."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Eğer bu seçenek etkinse, yalnızca geçerli çalışma alanındaki pencereler "
+"geçiş menüsünde gösterilir. Aksi halde, tüm pencereler görünecektir."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Konumlar"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Dünya saatlerinde gösterilecek konumlar"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Kendiliğinden konumlama"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Geçerli konumun getirilip getirilmeyeceğini belirler"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Konum"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Hava durumunun gösterileceği konum"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Yardımcı iletişim penceresini üst pencereye iliştir"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Bu anahtar, GNOME Shell çalışırken org.gnome.mutter içindeki anahtarı "
+"geçersiz kılar."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Pencereler ekran kenarlarında bırakıldığında kenar döşemeyi etkinleştir"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Çalışma alanları dinamik olarak yönetilir"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Çalışma alanları yalnızca birincil ekranda"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Fare kipinde odak değişikliklerini işaretçi hareketi durana kadar beklet"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Ağ Girişi"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Bir şeyler ters gitti"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Çok üzgünüz ancak sorun oluştu: bu uzantı için ayarlar görüntülenemiyor. "
+"Sorunu uzantı yazarlarına bildirmenizi öneririz."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Teknik Ayrıntılar"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Ana Sayfa"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Uzantı ana sayfasını ziyaret et"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "İptal"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Parola"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Oturum Seç"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Listede yok mu?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(örneğin, kullanıcı veya %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Kullanıcı Adı"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Oturum Açma Penceresi"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Kimlik doğrulama hatası"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(ya da parmağı okuyucuda kaydır)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(ya da parmağı okuyucuya yerleştir)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Bilgisayarı Kapat"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "gücü kapat;bilgisayarı kapat;duraklat;durdur"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Yeniden Başlat"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "yeniden önyükle;yeniden başlat;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Ekranı Kilitle"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "ekranı kilitle"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Oturumu Kapat"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "çıkış;oturumu kapat;çıkış yap"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Bilgisayarı askıya al"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "beklet;uyku;duraklat;askıya al"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Kullanıcı Değiştir"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "kullanıcı değiştir"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "yönelimi kilitle;yönelim kilidini kaldır;ekran;döndürme"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Ekran Görüntüsü Al"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"ekran görüntüsü;screenshot;ekran kaydı;ekran yayını;ekran video kaydı;"
+"screencast;kırp;kes;yakala;kaydet;kayıt;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Ekran Döndürme Kilidini Kaldır"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Ekran Döndürmeyi Kilitle"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Komut bulunamadı"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Komut işlenemedi:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "“%s” çalıştırılması başarısız:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Şimdi"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d dakika önce"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d saat önce"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Dün"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d gün önce"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d hafta önce"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d ay önce"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d yıl önce"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Dün, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Dün, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Erişim Noktası Girişi"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Bu erişim noktası girişine olan bağlantınız güvenli değil. Bu sayfaya "
+"girdiğiniz parolalar veya diğer bilgiler yakındaki insanlarca "
+"görüntülenebilir."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Erişimi Reddet"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Erişime İzin Ver"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Adsız Klasör"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s, konsola iliştirildi."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s, konsoldan çıkarıldı."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Açık Pencereler"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Yeni Pencere"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Ayrıntıları Göster"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Kapat"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Çıkar"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Konsola İliştir"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Tümleşik Ekran Kartıyla Başlat"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Ayrık Ekran Kartıyla Başlat"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Ses Aygıtı Seç"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Ses Ayarları"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Kulaklıklar"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Kulaklıklı Mikrofon"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Mikrofon"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Arka Planı Değiştir…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Görüntü Ayarları"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Ayarlar"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "S"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ç"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "P"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "C"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "C"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Önceki ay"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Gelecek ay"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "%V. Hafta"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Bildirim Yok"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Rahatsız Etme"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Temizle"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” yanıtlamıyor."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Sürmesini bekleyebilir ya da uygulamayı tümüyle kapanmaya zorlayabilirsiniz."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Kapanmaya Zorla"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Bekle"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Dış sürücü bağlandı"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Dış sürücü ayrıldı"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Bölümün kilidi açılamadı"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Kurulu udisks sürümü PIM ayarını desteklemiyor"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s ile Aç"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Diğer seçenek olarak, yönlendiricinizde “WPS” düğmesini kullanarak "
+"bağlayabilirsiniz."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "Bağlan"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Anahtar"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Özel anahtar parolası"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Kimlik"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Hizmet"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Kimlik doğrulama gerekir"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"“%s” kablosuz ağına erişmek için parola veya şifreleme anahtarları gerekiyor."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Kablolu 802.1X kimlik doğrulaması"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Ağ adı"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL kimlik doğrulama"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "PIN kodu gerekli"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Mobil geniş bant aygıtı için PIN kodu gerekli"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "“%s”e bağlanmak için parola gerekli."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Ağ Yöneticisi"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN parolası"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Kimlik Doğrulaması Gerekli"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Yönetici"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Doğrula"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Üzgünüm ama işe yaramadı. Lütfen yeniden deneyin."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s, şimdi %s olarak biliniyor"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Pencereler"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Uygulamaları Göster"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Konsol"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%e %B %Y %A"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Bugün"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Yarın"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Tüm Gün"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Olay Yok"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Dünya saatlerini ekle…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Dünya Saatleri"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Yükleniyor…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Hava durumu bilgisi için çevrim içi olun"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Hava durumu bilgisi şu anda kullanılabilir değil"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Hava Durumu"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Hava durumu konumu seç…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s Oturumunu Kapat"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Oturumu Kapat"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s oturumu %d saniyede kendiliğinden kapatılacak."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "%d saniyede oturumunuz kendiliğinden kapatılacak."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Çıkış"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Bilgisayarı Kapat"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Güncellemeleri Kur ve Kapat"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Sistem %d saniyede kendiliğinden kapanacak."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Bekleyen yazılım güncellemelerini kur"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Kapat"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Bilgisayarı Yeniden Başlat"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Güncellemeleri Kur ve Yeniden Başlat"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Sistem %d saniyede kendiliğinden yeniden başlayacak."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Yeniden Başlat"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Yeniden Başlat ve Güncellemeleri Kur"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Sistem %d saniyede kendiliğinden yeniden başlayacak ve güncellemeleri "
+"kuracak."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Yeniden Başlat ve Kur"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Kur ve Kapat"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Güncellemeler kurulduktan sonra kapat"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Yeniden Başlat ve Yükseltmeyi Kur"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Bilgisayarınız yeniden başladıktan sonra %s %s kurulacak. Yükseltme kurulumu "
+"uzun sürebilir: verilerinizi yedeklediğinizden ve bilgisayarınızın prize "
+"takılı olduğundan emin olun."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "Düşük pil gücü: lütfen güncellemeleri kurmadan önce fişi takın."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Bazı uygulamalar meşgul ya da kaydedilmemiş verisi var"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Diğer kullanıcılar oturum açmış"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Önyükleme Seçenekleri"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (uzak)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (uçbirim)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Kur"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Uzantı Kur"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "“%s” uzantısı extensions.gnome.org üstünden indirilip kurulsun mu?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Uzantı Güncellemeleri Var"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Uzantı güncellemeleri kuruluma hazır."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Kısayolları baskılamaya izin ver"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "%s uygulaması kısayolları baskılamak istiyor"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Bir uygulama, kısayolları baskılamak istiyor"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "%s kısayoluyla, kısayolları geri yükleyebilirsiniz."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Reddet"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "İzin Ver"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Yavaş Tuşlar Açık"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Yavaş Tuşlar Kapalı"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Shift tuşuna 8 saniye boyunca bastınız. Bu, klavyenizin çalışma yolunu "
+"etkileyen Yavaş Tuşlar özelliğinin kısayoludur."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Yapışkan Tuşlar Açık"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Yapışkan Tuşlar Kapalı"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Shift tuşuna art arda 5 kez bastınız. Bu, klavyenizin çalışma yolunu "
+"etkileyen Yapışkan Tuşlar özelliğinin kısayoludur."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"İki tuşa aynı anda veya Shift tuşuna art arda 5 kez bastınız. Bu, "
+"klavyenizin çalışma yolunu etkileyen Yapışkan Tuşlar özelliğini kapatır."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Açık Bırak"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Aç"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Kapat"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Kapalı Bırak"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Bölge ve Dil Ayarları"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Kurulu uzantı yok"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s, herhangi bir hata vermedi."
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Hataları Gizle"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Hataları Göster"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Etkin"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Devre dışı"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Hata"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Güncel değil"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "İndiriliyor"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Kaynağı Görüntüle"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Web Sayfası"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Sistem güvensiz kipe sokuldu"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Şimdi uygulamalar yetkisiz erişime sahip"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Geri Al"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Yetkili kullanıcı olarak oturum açılmış"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Oturumu öncelikli kullanıcı olarak çalıştırmaktan güvenlik nedeniyle "
+"kaçınılmalıdır. Eğer yapabiliyorsanız sıradan kullanıcı olarak girmelisiniz."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Ekran Kilidi devre dışı"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Ekran Kilitleme, GNOME ekran yöneticisi gerektirir."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Sistem Bilgisi"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Bilinmeyen sanatçı"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Bilinmeyen başlık"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Yazarak ara"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Uygulamalar"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Genel Görünüm"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Yeni kısayol…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Uygulama tanımlı"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Ekranda yardımı göster"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Monitör değiştir"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Tuş vuruşu ata"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Bitti"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Düzenle…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Yok"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Yapılandırmak için düğmeye bas"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Çıkmak için Esc’ye bas"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Çıkmak için herhangi bir tuşa bas"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Etkinlikler"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Sistem"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Tepe Çubuğu"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Komut Çalıştır"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Çıkmak için ESC’ye bas"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Wayland’de yeniden başlatma kullanılabilir değil"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Yeniden başlatılıyor…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME’un ekranı kilitlemesi gerekiyor"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Kilitlenemedi"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Kilitleme bir uygulamaca engellendi"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Seçim"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Alan Seçimi"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Ekran"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Ekran Seçimi"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Pencere"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Pencere Seçimi"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Ekran Görüntüsü / Ekran Kaydı"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "İmleci Göster"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Ekran Kaydı"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Ekran Kaydı - %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Ekran Görüntüsü"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Ekran kaydı yakalandı"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Videoyu görmek için buraya tıkla."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Dosyalar’da Göster"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Ekran Görüntüsü - %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Ekran görüntüsü yakalandı"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Resmi panodan yapıştırabilirsiniz."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Ekran görüntüsü alındı"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Aranıyor…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Sonuç yok."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d tane daha"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Ara"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Kopyala"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Yapıştır"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Metni Göster"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Metni Gizle"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock açık."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Gizli Bölüm"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows Sistem Bölümü"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Anahtar Dosyaları Kullanır"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Anahtar dosyaları kullanan bölümün kilidini açmak için <i>%s</i> aracını "
+"kullanın."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM Numarası"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Parolayı Anımsa"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Kilit Aç"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "%s’i Aç"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM bir sayı veya boşluk olmalı."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "%s başlatılamadı"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "%s uygulaması bulunamadı"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Erişilebilirlik"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Yüksek Karşıtlık"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Yakınlaştırma"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Ekran Okuyucu"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Ekran Klavyesi"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Görsel Uyarılar"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Yapışkan Tuşlar"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Yavaş Tuşlar"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Zıplayan Tuşlar"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Fare Tuşları"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Büyük Yazı"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Kendiliğinden Döndür"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Parlaklık"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Karanlık Kip"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Tek Tık"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Çift Tık"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Sürükle"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "İkincil Tık"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Durağan Tık"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Klavye"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Klavye Düzenini Göster"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Konum erişimine izin ver"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "%s uygulaması konumunuza erişmek istiyor"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Konum erişimi, gizlilik ayarlarından her zaman değiştirilebilir."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<bilinmeyen>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "%s Bağlantısını Kes"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "Bağlan: %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s Erişim Noktası"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "VPN Ayarları"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Kablosuz"
+
+#: js/ui/status/network.js:1718
+msgid "All Networks"
+msgstr "Tüm Ağlar"
+
+#: js/ui/status/network.js:1815
+msgid "Wired Connections"
+msgstr "Kablolu Bağlantılar"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Kablolu Ayarları"
+
+#: js/ui/status/network.js:1830
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth İnternet"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Bluetooth Ayarları"
+
+#: js/ui/status/network.js:1845
+msgid "Mobile Connections"
+msgstr "Mobil Bağlantılar"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "Mobil Geniş Bant Ayarları"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Bağlantı başarısız"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Ağ bağlantısının etkinleşimi başarısız"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Gece Işığı"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Başarım"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Dengeli"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Güç Tutumu"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Güç Profilleri"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Ekran Kaydını Durdur"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Ekran Paylaşımını Durdur"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Uçak Kipi"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Ekran Görüntüsü Al"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Bilgisayarı Kapat Menüsü"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Askıya Al"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Yeniden Başlat…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Gücü Kapat…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Oturumu Kapat…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Kullanıcı Değiştir…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Ekranı Kilitle"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Bilinmeyen Thunderbolt aygıtı"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Siz uzaktayken yeni aygıt saptandı. Kullanmaya başlamadan önce aygıtın "
+"bağlantısını kesin ve yeniden bağlayın."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Yetkisiz Thunderbolt aygıtı"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Yeni aygıt saptandı ve yöneticice yetkilendirilmelidir."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt yetkilendirme hatası"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Thunderbolt aygıtı yetkilendirilemedi: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Bölüm değişti"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Bölüm"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Ses Çıktısı"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Ses Girdisi"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Yinele"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Birleştir"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Yalnızca Dış"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Yalnızca İç"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Kilidi açmak için yukarı kaydır"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Kilidi açmak için tıklayın veya tuşa basın"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Kilit Açma Penceresi"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Başka kullanıcı olarak oturum aç"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "GNOME %s’a Hoş Geldiniz"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Eğer yolu öğrenmek istiyorsanız, gezintiye çıkın."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Hayır, Sağ Ol"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Gezintiye Çık"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” hazır"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Bu görüntü ayarları saklansın mı?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Ayarları Eskisine Döndür"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Değişiklikleri Sakla"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Ayarlardaki değişiklikler %d saniyede eskisine döndürülecek"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Gizle"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Geri Yükle"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "En üst düzeye çıkar"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Taşı"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Yeniden Boyutlandır"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Başlık Çubuğunu Ekranda Taşı"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Her Zaman Üstte"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Her Zaman Görünür Çalışma Alanında"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Soldaki Çalışma Alanına Taşı"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Sağdaki Çalışma Alanına Taşı"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Üstteki Çalışma Alanına Taşı"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Alttaki Çalışma Alanına Taşı"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Üstteki Monitöre Taşı"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Alttaki Monitöre Taşı"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Soldaki Monitöre Taşı"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Sağdaki Monitöre Taşı"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Kapat"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution Takvim"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Sürümü yazdır"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Oturum açma ekranında GDM tarafından kullanılan kip"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Oturum açma ekranı için -“gdm” gibi- özel kip kullan"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Olası kipleri listele"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Bilinmeyen"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "“%s” başlatılamadı"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Parolalar eşleşmiyor."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Parola boş bırakılamaz"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Kimlik doğrulama penceresi kullanıcıca kapatıldı"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Uzantılar"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "GNOME Uzantılarını Yönet"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME Projesi"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"GNOME Uzantılar; uzantıları güncellemeyi, uzantı tercihlerini yapılandırmayı "
+"ve istenmeyen uzantıları kaldırmayı veya devre dışı bırakmanızı sağlar."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Kabuğu Uzantılarını Ayarla"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Eşleşme Yok"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "“%s” kaldırılsın mı?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Uzantıyı kaldırırsanız, yeniden etkinleştirmek istediğinizde yeniden "
+"indirmeniz gerekir"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Sil"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Emin Tufan Çetin <etcetin@gmail.com>\n"
+"Muhammet Kara <muhammetk@gmail.com>\n"
+"Sabri Ünal <libreajans@gmail.com>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d uzantı sonraki girişte güncellenecek."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Uzantı, geçerli GNOME sürümüyle uyumsuz"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Uzantıda hata oluştu"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Uzantı güncellenebilir"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Web Sitesi"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Kaldır…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Yardım"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Uzantılar Hakkında"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Uzantılar başarım ve kararlılık sorunlarına neden olabilir. Sisteminizde "
+"sorun yaşarsanız uzantıları devre dışı bırakın."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Elle Kurulmuş"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Uzantı bulmak ve eklemek için <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a> adresine göz atın."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Dahili"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Yüklü Uzantı Yok"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Maalesef, yüklü uzantıların listesi alınamadı. GNOME'da oturum açtığınızdan "
+"emin olun ve tekrar deneyin."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Uzantı Güncellemeleri Hazır"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Yeni uzantı %s içinde oluşturuldu.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Ad çok kısa (ideal olarak açıklayıcı) dizge olmalıdır.\n"
+"Örnekler: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Ad"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Açıklama, uzantınızın ne yaptığı anlatan tek tümcelik tanımlamadır.\n"
+"Örnekler: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Açıklama"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID, uzantınız için genel ve benzersiz tanımlayıcıdır.\n"
+"E-posta biçiminde olmalıdır (tiklayarakodakla@birisi.ornek.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Uygun şablonlardan birini seçin:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Şablon"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Yeni uzantının eşsiz tanımlayıcısı"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "AD"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Yeni uzantının kullanıcıca görülebilir adı"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "AÇIKLAMA"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Uzantının ne yaptığıyla ilgili kısa açıklama"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ŞABLON"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Yeni uzantı için kullanılacak şablon"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Uzantı bilgisini etkileşimli olarak gir"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Yeni uzantı oluştur"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Bilinmeyen argüman"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, ad ve açıklama gereklidir"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "“%s” uzantısı yok\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Uzantı devre dışı bırak"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Verilen UUID yok"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Birden ÇOK UUID verildi"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Uzantı etkinleştir"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "GNOME Kabuğu’na bağlanılamadı\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "“%s” uzantısı yok\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Uzantı bilgisini göster"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Var olan uzantının üzerine yaz"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "UZANTI_DEMETİ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Uzantı demeti kur"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Uzantı demeti belirtilmedi"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Birden çok uzantı demeti belirtildi"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Kullanıcı uzantılarını göster"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Sistem uzantılarını göster"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Etkinleştirilmiş uzantıları göster"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Devre dışı bırakılmış uzantıları göster"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Tercihleri olan uzantıları göster"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Güncellemeleri olan uzantıları göster"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Uzantı ayrıntılarını yazdır"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Kurulu uzantıları listele"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "DOSYA"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Demete eklenecek ek kaynak"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "ŞEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "İçerilmesi gereken GSettings şeması"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DİZİN"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Çevirilerin bulunduğu dizin"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ALAN_ADI"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Çeviriler için kullanılacak gettext alan adı"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Var olan paketin üstüne yaz"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Paketin oluşturacağı dizin"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "KAYNAK_DİZİN"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Yeni uzantı demeti oluştur"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Birden çok kaynak dizin belirtildi"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "“%s” uzantısının tercihleri yok\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "“%s” eklentisi için seçenekler açılamadı: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "GNOME Kabuğu Uzantı Tercihleri"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Uzantıyı sıfırla"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Sistem uzantıları kaldırılamaz\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "“%s” kaldırılamadı\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Uzantı kaldır"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Hata iletilerini yazdırma"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Yol"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Özgün yazar"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Sürüm"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Durum"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” hiçbir argüman almaz"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Kullanım:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Sürüm bilgisini yazdır ve çık."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "KOMUT"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ARGÜMANLAR…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Komutlar:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Yardımı yazdır"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Uzantıyı etkinleştir"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Uzantıyı devre dışı bırak"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Uzantıyı sıfırla"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Uzantıyı kaldır"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Uzantıları listele"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Uzantı bilgisini göster"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "GNOME Kabuğu Uzantı Tercihleri"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Uzantı oluştur"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Uzantıyı paketle"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Uzantı demetini kur"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Ayrıntılı yardım almak için “%s” kullanın.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Düz"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Boş uzantı"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Belirteç"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Üst çubuğa simge ekle"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u Çıktı"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u Girdi"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Sistem Sesleri"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr ""
+#~ "Öntanımlı Bluetooth bağdaştırıcının ayarlanmış ilişkili aygıtlara sahip "
+#~ "olması"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Kabuk, yalnızca gücü açık Bluetooth bağdaştırıcı varsa ya da öntanımlı "
+#~ "bağdaştırıcı ile ilişkili olarak ayarlanmış aygıtlar varsa Bluetooth menü "
+#~ "ögesi gösterecektir. Eğer öntanımlı bağdaştırıcıyla ilişkili aygıtların "
+#~ "olmadığı görülecek olursa bu durum sıfırlanacaktır."
diff --git a/po/ug.po b/po/ug.po
new file mode 100644
index 0000000..2964054
--- /dev/null
+++ b/po/ug.po
@@ -0,0 +1,2208 @@
+# Uyghur translation for gnome-shell.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Gheyret Kenji<gheyret@yahoo.com>,2010.
+# Sahran <sahran.ug@gmail.com>, 2010.
+# Zeper <zeper@msn.com>, 2010.
+# Bakhtiyar<bakhtiyar108@gmail.com>, 2011
+# Oghlan Temkin <temkin119@gmail.com>,2011
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-03-04 08:38+0000\n"
+"PO-Revision-Date: 2013-03-07 21:01+0900\n"
+"Last-Translator: Gheyret Kenji <gheyret@gmail.com>\n"
+"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\n"
+"Language: ug\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:1
+msgid "Screenshots"
+msgstr "ئېكران كۆرۈنۈشى"
+
+#: ../data/50-gnome-shell-screenshot.xml.in.h:2
+msgid "Record a screencast"
+msgstr "ئېكراننى سۈرەتكە ئېلىش"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "سىستېما"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+msgid "Show the message tray"
+msgstr "ئۇچۇر قوندىقىنى كۆرسىتىش"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "ئاكتىپ ئۇقتۇرۇشقا فوكۇسلىنىش"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show all applications"
+msgstr "بارلىق پروگراممىنى كۆرسىتىش"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Open the application menu"
+msgstr "پروگرامما تىزىملىكىنى كۆرسىتىش"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "گىنوم Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "كۆزنەك باشقۇرۇش ۋە پروگرامما ئىجرا قىلىش پروگراممىسى"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+#: ../js/extensionPrefs/main.js:153
+msgid "GNOME Shell Extension Preferences"
+msgstr "گىنوم Shell كېڭەيتمىسىنىڭ مايىللىقى"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "گىنوم Shell كېڭەيتمىسىنى سەپلەش"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "ئىچكى قورال قوزغىتىلسا ئىجادكارلار ۋە سىنىغۇچىلارنىڭ Alt-F2 ئارقىلىق كىرىشىگە قۇلايلىق"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "ئىچكى سازلاش ۋە كۆزىتىش قورالىنى زىيارەت قىلىشتا Alt-F2 ئىشلىتىشكە ئىجازەت."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "Uuids of extensions to enable"
+msgstr "ئىناۋەتلىك قىلىدىغان كېڭەيتىلمىنىڭ Uuids سى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a uuid property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension DBus methods on org.gnome.Shell."
+msgstr "گىنوم Shell كېڭەيتىلمىسىنىڭ uuid خاسلىقى بولىدۇ. بۇ ئاچقۇچتا ئىناۋەتلىك قىلىنىدىغان كېڭەيتىلمىلەرنىڭ تىزىمى بار. ئوقۇلىدىغان كېڭەيتىلمىلەر چوقۇم مۇشۇ تىزىمدا بولۇشى كېرەك. بۇ تىزىمنى org.gnome.Shell دىكى DBus نىڭ EnableExtension ۋە DisableExtension دېگەن فۇنكسىيىلىرى(methods) ئارقىلىق باشقۇرغىلى بولىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Whether to collect stats about applications usage"
+msgstr "پروگراممىنىڭ ئىشلىتىلىشى ھەققىدىكى ستاتىستىكىنى توپلامدۇ يوق"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"The shell normally monitors active applications in order to present the most "
+"used ones (e.g. in launchers). While this data will be kept private, you may "
+"want to disable this for privacy reasons. Please note that doing so won't "
+"remove already saved data."
+msgstr "چاپان (shell) ئادەتتىكى ئەھۋالدا كۆپ ئىشلىتىلىدىغان ئاكتىپ پروگراممىلار(مەسىلەن، ئىجرا قىلىنىۋاتقان)نى كۆزىتىدۇ. گەرچە بۇ سانلىق مەلۇماتلار مەخپىي ساقلانسىمۇ، شەخسىي سىر سەۋەبىدىن بۇنى چەكلىشىڭىز مۇمكىن. دىققەت بۇنداق قىلغاندا ئاللىبۇرۇن ساقلانغان سانلىق مەلۇماتلار چىقىرىۋېتىلمەيدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "ئامراق پروگراممىلارنىڭ ئۈستەلئۈستى ھۆججەت ID تىزىمى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "مۇناسىپ پروگرامما بەلگىسى يىغقۇچ رايونىدا كۆرسىتىلىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "List of categories that should be displayed as folders"
+msgstr "قىسقۇچ سۈپىتىدە كۆرسىتىلىدىغان كاتېگورىيەلەرنىڭ تىزىمى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid ""
+"Each category name in this list will be represented as folder in the "
+"application view, rather than being displayed inline in the main view."
+msgstr "پروگرامما كۆرۈنۈشىدە ھەر بىر كاتېگورىيە ئاتى قىسقۇچ سۈپىتىدە ئىپادىلىنىدۇ، ئاساسىي كۆرۈنۈشتە كۆرسىتىلمەيدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "بۇيرۇق (Alt-F2) سۆزلەشكۈنىڭ تارىخى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
+msgid "History for the looking glass dialog"
+msgstr "looking glass سۆزلەشكۈنىڭ تارىخى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid ""
+"Internally used to store the last IM presence explicitly set by the user. "
+"The value here is from the TpConnectionPresenceType enumeration."
+msgstr "ئىشلەتكۈچى تەرىپىدىن روشەن بەلگىلەنگەن ئەڭ يېڭى IM ھالىتى ساقلاش ئۈچۈن ئىچكى قىسىمدا ئىشلىتىلىدۇ. بۇ TpConnectionPresenceType نىڭ قىممىتى بولىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid ""
+"Internally used to store the last session presence status for the user. The "
+"value here is from the GsmPresenceStatus enumeration."
+msgstr "ئىشلەتكۈچىنىڭ ئەڭ يېڭى ھالىتى ساقلاش ئۈچۈن ئىچكى قىسىمدا ئىشلىتىلىدۇ. بۇ GsmPresenceStatus نىڭ قىممىتى بولىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid "Always show the 'Log out' menuitem in the user menu."
+msgstr "ئىشلەتكۈچى تىزىملىكىدە ھەمىشە ‹چىق› تىزىملىكىنى كۆرسىتىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menuitem in single-"
+"user, single-session situations."
+msgstr "مەزكۇر ئاچقۇچ يەككە ئىشلەتكۈچى، يەككە ئەڭگىمە ھالىتىدە ‹چىق› دېگەن تىزىملىكنى قاپلىۋېتىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "شىفىرلانغان ياكى يىراقتىكى ھۆججەت سىستېمىسىنى ئېگەرلەشتە ئىمنى ئەستە تۇتسۇنمۇ"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr "شىفىرلانغان ياكى يىراقتىكى ھۆججەت سىستېمىسىنى ئېگەرلەيدىغان ۋاقىتتا مەزكۇر shell ئىم تەلەپ قىلىدۇ. ئەگەر ‹ئىمنى ئەستە تۇت› دېگىنى تاللانسا، كېيىنلىك ئۈچۈن ئىمنى ساقلاپ قويىدۇ. بۇ ئاچقۇچ checkbox نىڭ كۆڭۈلدىكى ھالىتىنى بەلگىلەيدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid "Show the week date in the calendar"
+msgstr "يىلنامىدە ھەپتىنى كۆرسەتسۇن"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "If true, display the ISO week date in the calendar."
+msgstr "ئەگەر راست(true) بولسا يىلنامىدىكى ISO ھەپتە چېسلانى كۆرسىتىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "Keybinding to open the application menu"
+msgstr "پروگرامما تىزىملىكىنى ئاچىدىغان كۇنۇپكا بىرىكمىسى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Keybinding to open the application menu."
+msgstr "پروگرامما تىزىملىكىنى ئاچىدىغان كۇنۇپكا بىرىكمىسى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "«پروگراممىلارنى كۆرسەت» كۆرۈنۈشىنى ئاچىدىغان كۇنۇپكا بىرىكمىسى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr "پائالىيەتلەر كۆرۈنۈشىدە «پروگراممىلارنى كۆرسەت» كۆرۈنۈشىنى ئاچىدىغان كۇنۇپكا بىرىكمىسى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid "Keybinding to toggle the visibility of the message tray"
+msgstr "ئۇچۇر قوندىقىنى كۆرسىتىش-كۆرسەتمەسلىكنى ئالماشتۇرىدىغان كۇنۇپكا بىرىكمىسى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to toggle the visibility of the message tray."
+msgstr "ئۇچۇر قوندىقىنى كۆرسىتىش-كۆرسەتمەسلىكنى ئالماشتۇرىدىغان كۇنۇپكا بىرىكمىسى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to focus the active notification"
+msgstr "ئاكتىپ ئۇقتۇرۇشنى كۆرسىتىدىغان كۇنۇپكا بىرىكمىسى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+msgid "Keybinding to focus the active notification."
+msgstr "ئاكتىپ ئۇقتۇرۇشنى كۆرسىتىدىغان كۇنۇپكا بىرىكمىسى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+msgid "Keybinding to toggle the screen recorder"
+msgstr "ئېكراننى سۈرەتكە ئالغۇنى باشلايدىغان/توختىتىدىغان كۇنۇپكا بىرىكمىسى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Keybinding to start/stop the builtin screen recorder."
+msgstr "سىڭدۈرۈۋېتىلگەن ئېكراننى سۈرەتكە ئالغۇنى باشلايدىغان/توختىتىدىغان كۇنۇپكا بىرىكمىسى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "Which keyboard to use"
+msgstr "ئىشلەتمەكچى بولغان كۇنۇپكا تاختىسى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid "The type of keyboard to use."
+msgstr "ئىشلەتمەكچى بولغان كۇنۇپكا تاختىسى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid "Framerate used for recording screencasts."
+msgstr "ئېكران كۆرۈنۈشى (screencasts) خاتىرىلەشتە ئىشلىتىلىدىغان كاندۇك تېزلىكى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid ""
+"The framerate of the resulting screencast recordered by GNOME Shell's "
+"screencast recorder in frames-per-second."
+msgstr "گىنوم Shell ئېكران خاتىرىلىگۈچ ھەر سېكۇنتتا خاتىرىلەيدىغان ئېكران كۆرۈنۈشى كاندۇك سۈرىتى(ھەر سېكۇنتتىكى كاندۇك سانى)."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid "The gstreamer pipeline used to encode the screencast"
+msgstr "ئېكران كۆرۈنۈشىنى كودلاشتا ئىشلىتىلىدىغان gstreamer ئاقما لىنىيىسى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+#, no-c-format
+msgid ""
+"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
+"used for gst-launch. The pipeline should have an unconnected sink pad where "
+"the recorded video is recorded. It will normally have a unconnected source "
+"pad; output from that pad will be written into the output file. However the "
+"pipeline can also take care of its own output - this might be used to send "
+"the output to an icecast server via shout2send or similar. When unset or set "
+"to an empty value, the default pipeline will be used. This is currently "
+"'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+"threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is "
+"used as a placeholder for a guess at the optimal thread count on the system."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid "File extension used for storing the screencast"
+msgstr "ئېكران كۆرۈنۈشى (screencasts) ساقلاشتا ئىشلىتىلىدىغان ھۆججەتنىڭ كېڭەيتىلگەن ئاتى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid ""
+"The filename for recorded screencasts will be a unique filename based on the "
+"current date, and use this extension. It should be changed when recording to "
+"a different container format."
+msgstr "خاتىرىلەنگەن ئېكراننىڭ ھۆججەت ئاتى نۆۋەتتىكى چېسلا ئاساسىدا بىردىنبىر بولۇپ بۇ كېڭەيتىلگەن ئاتىنى ئىشلىتىدۇ. ئۇ ئۆزگەرسە ئوخشاش بولمىغان قاچا فورماتىدا خاتىرىلەيدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "The application icon mode."
+msgstr "پروگرامما سىنبەلگە ھالىتى."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr "ئالماشتۇرغۇچتا پروگراممىلارنىڭ قانداق كۆرسىتىلىدىغانلىقىنى سەپلەيدۇ. قىممەتلەر: ‹thumbnail-only›(پەقەت كۆزنەكنىڭ كىچىك سۈرىتىنىلا كۆرسىتىدۇ)،‹app-icon-only›(پروگراممىنىڭ سىنبەلگىسىنىلا كۆرسىتىدۇ) ۋە ‹ئىككىلىسى›"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Attach modal dialog to the parent window"
+msgstr "ئاتا كۆزنەككە modal سۆزلەشكۈنى قات"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "گىنوم Shell ئىجرا بولۇۋاتقاندا مەزكۇر ئاچقۇچ org.gnome.mutter نىڭ ئىچىدىكى ئاچقۇچنى قاپلىۋېتىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
+msgid "Arrangement of buttons on the titlebar"
+msgstr "ماۋزۇ بالدىقىدىكى توپچىلارنىڭ ئورۇنلاشتۇرۇلۇشى"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
+msgid ""
+"This key overrides the key in org.gnome.desktop.wm.preferences when running "
+"GNOME Shell."
+msgstr "گىنوم Shell ئىجرا بولۇۋاتقاندا مەزكۇر ئاچقۇچ org.gnome.desktop.wm.preferences نىڭ ئىچىدىكى ئاچقۇچنى قاپلىۋېتىدۇ."
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
+msgid "Workspaces are managed dynamically"
+msgstr "خىزمەت رايونلىرى جانلىق باشقۇرۇلسۇن"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
+msgid "Workspaces only on primary monitor"
+msgstr "ئاساسىي كۆزەتكۈچتىكى خىزمەت رايونلىرىلا"
+
+#: ../js/extensionPrefs/main.js:125
+#, c-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s نىڭ خاسلىق سۆزلەشكۈسىنى ئوقۇشتا خاتالىق كۆرۈلدى:"
+
+#: ../js/extensionPrefs/main.js:165
+msgid "Extension"
+msgstr "كېڭەيتىلمە"
+
+#: ../js/extensionPrefs/main.js:189
+msgid "Select an extension to configure using the combobox above."
+msgstr "يۇقىرىدىكى combobox دىن سەپلەيدىغان كېڭەيتىلمىنى تاللاڭ."
+
+#: ../js/gdm/loginDialog.js:405
+msgid "Session…"
+msgstr "ئەڭگىمە…"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:629
+msgid "Not listed?"
+msgstr "تىزىمدا يوقمۇ؟"
+
+#: ../js/gdm/loginDialog.js:783 ../js/ui/components/networkAgent.js:137
+#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
+#: ../js/ui/userMenu.js:934
+msgid "Cancel"
+msgstr "ئەمەلدىن قالدۇر"
+
+#: ../js/gdm/loginDialog.js:799
+msgctxt "button"
+msgid "Sign In"
+msgstr "كىر"
+
+#: ../js/gdm/loginDialog.js:799
+msgid "Next"
+msgstr "كېيىنكى"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/gdm/loginDialog.js:904 ../js/ui/components/networkAgent.js:260
+#: ../js/ui/components/networkAgent.js:278
+msgid "Username: "
+msgstr "ئىشلەتكۈچى ئاتى: "
+
+#: ../js/gdm/loginDialog.js:1157
+msgid "Login Window"
+msgstr "كىرىش كۆزنىكى"
+
+#. Translators: accessible name of the power menu in the login screen
+#: ../js/gdm/powerMenu.js:36
+msgid "Power"
+msgstr "توك مەنبە"
+
+#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:695 ../js/ui/userMenu.js:699
+#: ../js/ui/userMenu.js:815
+msgid "Suspend"
+msgstr "توڭلات"
+
+#: ../js/gdm/powerMenu.js:98
+msgid "Restart"
+msgstr "قايتا قوزغات"
+
+#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:697
+#: ../js/ui/userMenu.js:699 ../js/ui/userMenu.js:814 ../js/ui/userMenu.js:938
+msgid "Power Off"
+msgstr "توكنى ئۈز"
+
+#: ../js/gdm/util.js:182
+msgid "Authentication error"
+msgstr "كىملىك دەلىللەش خاتالىقى"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/util.js:299
+msgid "(or swipe finger)"
+msgstr "(ياكى بارمىقىڭىزنى سىيرىڭ)"
+
+#: ../js/gdm/util.js:324
+#, c-format
+msgid "(e.g., user or %s)"
+msgstr "(مەسىلەن، ئىشلەتكۈچى ياكى %s)"
+
+#: ../js/misc/util.js:94
+msgid "Command not found"
+msgstr "بۇيرۇق تېپىلمىدى"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:127
+msgid "Could not parse command:"
+msgstr "بۇيرۇقنى تەھلىل قىلالمىدى:"
+
+#: ../js/misc/util.js:135
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "‹%s› ئىجرا قىلىش مەغلۇپ بولدى:"
+
+#: ../js/ui/appDisplay.js:348
+msgid "Frequent"
+msgstr "دائىم"
+
+#: ../js/ui/appDisplay.js:355
+msgid "All"
+msgstr "ھەممىسى"
+
+#: ../js/ui/appDisplay.js:913
+msgid "New Window"
+msgstr "يېڭى كۆزنەك"
+
+#: ../js/ui/appDisplay.js:916 ../js/ui/dash.js:284
+msgid "Remove from Favorites"
+msgstr "يىغقۇچتىن چىقىرىۋەت"
+
+#: ../js/ui/appDisplay.js:917
+msgid "Add to Favorites"
+msgstr "يىغقۇچقا قوش"
+
+#: ../js/ui/appFavorites.js:87
+#, c-format
+msgid "%s has been added to your favorites."
+msgstr "‏%s يىغقۇچىڭىزغا قوشۇلدى."
+
+#: ../js/ui/appFavorites.js:121
+#, c-format
+msgid "%s has been removed from your favorites."
+msgstr "‏%s يىغقۇچىڭىزدىن چىقىرىۋېتىلىدۇ."
+
+#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:788
+msgid "Settings"
+msgstr "تەڭشەكلەر"
+
+#: ../js/ui/backgroundMenu.js:21
+msgid "Change Background…"
+msgstr "تەگلىكنى ئۆزگەرتىش…"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:62
+msgctxt "event list time"
+msgid "All Day"
+msgstr "كۈن بويى"
+
+#. Translators: Shown in calendar event list, if 24h format,
+#. \u2236 is a ratio character, similar to :
+#: ../js/ui/calendar.js:68
+msgctxt "event list time"
+msgid "%H\\u2236%M"
+msgstr "%H\\u2236%M"
+
+#. Transators: Shown in calendar event list, if 12h format,
+#. \u2236 is a ratio character, similar to : and \u2009 is
+#. a thin space
+#: ../js/ui/calendar.js:77
+msgctxt "event list time"
+msgid "%l\\u2236%M\\u2009%p"
+msgstr "%l\\u2236%M\\u2009%p"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:108
+msgctxt "grid sunday"
+msgid "S"
+msgstr "ي"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:110
+msgctxt "grid monday"
+msgid "M"
+msgstr "د"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:112
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "س"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:114
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "چ"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:116
+msgctxt "grid thursday"
+msgid "T"
+msgstr "پ"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:118
+msgctxt "grid friday"
+msgid "F"
+msgstr "ج"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:120
+msgctxt "grid saturday"
+msgid "S"
+msgstr "ش"
+
+#. Translators: Event list abbreviation for Sunday.
+#. *
+#. * NOTE: These list abbreviations are normally not shown together
+#. * so they need to be unique (e.g. Tuesday and Thursday cannot
+#. * both be 'T').
+#.
+#: ../js/ui/calendar.js:133
+msgctxt "list sunday"
+msgid "Su"
+msgstr "ي"
+
+#. Translators: Event list abbreviation for Monday
+#: ../js/ui/calendar.js:135
+msgctxt "list monday"
+msgid "M"
+msgstr "د"
+
+#. Translators: Event list abbreviation for Tuesday
+#: ../js/ui/calendar.js:137
+msgctxt "list tuesday"
+msgid "T"
+msgstr "س"
+
+#. Translators: Event list abbreviation for Wednesday
+#: ../js/ui/calendar.js:139
+msgctxt "list wednesday"
+msgid "W"
+msgstr "چ"
+
+#. Translators: Event list abbreviation for Thursday
+#: ../js/ui/calendar.js:141
+msgctxt "list thursday"
+msgid "Th"
+msgstr "پ"
+
+#. Translators: Event list abbreviation for Friday
+#: ../js/ui/calendar.js:143
+msgctxt "list friday"
+msgid "F"
+msgstr "ج"
+
+#. Translators: Event list abbreviation for Saturday
+#: ../js/ui/calendar.js:145
+msgctxt "list saturday"
+msgid "S"
+msgstr "ش"
+
+#. Translators: Text to show if there are no events
+#: ../js/ui/calendar.js:692
+msgid "Nothing Scheduled"
+msgstr "ھېچنېمە پىلانلانمىدى"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: ../js/ui/calendar.js:708
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A، %B %d"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: ../js/ui/calendar.js:711
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A، %B %d، %Y"
+
+#: ../js/ui/calendar.js:721
+msgid "Today"
+msgstr "بۈگۈن"
+
+#: ../js/ui/calendar.js:725
+msgid "Tomorrow"
+msgstr "ئەتە"
+
+#: ../js/ui/calendar.js:736
+msgid "This week"
+msgstr "بۇ ھەپتە"
+
+#: ../js/ui/calendar.js:744
+msgid "Next week"
+msgstr "كېيىنكى ھەپتە"
+
+#: ../js/ui/components/automountManager.js:90
+msgid "External drive connected"
+msgstr "سىرتقى دىسكا ئۇلاندى."
+
+#: ../js/ui/components/automountManager.js:101
+msgid "External drive disconnected"
+msgstr "سىرتقى دىسكا ئۈزۈلدى."
+
+#: ../js/ui/components/autorunManager.js:294
+msgid "Removable Devices"
+msgstr "چىقىرىۋېتىشكە بولىدىغان ئۈسكۈنىلەر"
+
+#: ../js/ui/components/autorunManager.js:593
+#, c-format
+msgid "Open with %s"
+msgstr "%s دا ئاچ"
+
+#: ../js/ui/components/autorunManager.js:619
+msgid "Eject"
+msgstr "چىقار"
+
+#: ../js/ui/components/keyring.js:82 ../js/ui/components/polkitAgent.js:268
+msgid "Password:"
+msgstr "ئىم:"
+
+#: ../js/ui/components/keyring.js:101
+msgid "Type again:"
+msgstr "قايتا كىرگۈزۈڭ:"
+
+#: ../js/ui/components/networkAgent.js:132
+msgid "Connect"
+msgstr "باغلان"
+
+#. Cisco LEAP
+#: ../js/ui/components/networkAgent.js:223
+#: ../js/ui/components/networkAgent.js:235
+#: ../js/ui/components/networkAgent.js:262
+#: ../js/ui/components/networkAgent.js:282
+#: ../js/ui/components/networkAgent.js:292
+msgid "Password: "
+msgstr "ئىم: "
+
+#. static WEP
+#: ../js/ui/components/networkAgent.js:228
+msgid "Key: "
+msgstr "شىفىرلىق ئاچقۇچ: "
+
+#: ../js/ui/components/networkAgent.js:266
+msgid "Identity: "
+msgstr "كىملىك: "
+
+#: ../js/ui/components/networkAgent.js:268
+msgid "Private key password: "
+msgstr "شەخسىي ئاچقۇچ ئىمى: "
+
+#: ../js/ui/components/networkAgent.js:280
+msgid "Service: "
+msgstr "مۇلازىمەت: "
+
+#: ../js/ui/components/networkAgent.js:309
+msgid "Authentication required by wireless network"
+msgstr "سىمسىز تور كىملىك تەكشۈرۈشكە ئېھتىياجلىق"
+
+#: ../js/ui/components/networkAgent.js:310
+#, c-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"'%s'."
+msgstr "سىمسىز تور ‹%s› نى زىيارەت قىلماق ئۈچۈن ئىم ياكى شىفىرلاش ئاچقۇچلىرى كېرەكلىك."
+
+#: ../js/ui/components/networkAgent.js:314
+msgid "Wired 802.1X authentication"
+msgstr "سىملىق 802.1X گۇۋاھنامە"
+
+#: ../js/ui/components/networkAgent.js:316
+msgid "Network name: "
+msgstr "تور ئاتى: "
+
+#: ../js/ui/components/networkAgent.js:321
+msgid "DSL authentication"
+msgstr "DSL دەلىللەش"
+
+#: ../js/ui/components/networkAgent.js:328
+msgid "PIN code required"
+msgstr "PIN كودى ئېھتىياجلىق"
+
+#: ../js/ui/components/networkAgent.js:329
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "كۆچمە كەڭ بەلۋاغ ئۈسكۈنىسى ئۈچۈن PIN كودى كېرەك"
+
+#: ../js/ui/components/networkAgent.js:330
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:336
+msgid "Mobile broadband network password"
+msgstr "كۆچمە كەڭ بەلۋاغ تور ئىمى"
+
+#: ../js/ui/components/networkAgent.js:337
+#, c-format
+msgid "A password is required to connect to '%s'."
+msgstr "‹%s› كە باغلىنىش ئۈچۈن بىر ئىمغا ئېھتىياجلىق."
+
+#: ../js/ui/components/polkitAgent.js:55
+msgid "Authentication Required"
+msgstr "كىملىك دەلىللەش زۆرۈر"
+
+#: ../js/ui/components/polkitAgent.js:93
+msgid "Administrator"
+msgstr "باشقۇرغۇچى"
+
+#: ../js/ui/components/polkitAgent.js:165
+msgid "Authenticate"
+msgstr "كىملىك دەلىللەش"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/components/polkitAgent.js:256 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "كەچۈرۈڭ، خىزمەت قىلالمايدۇ. قايتا سىناڭ."
+
+#. Translators: this is a filename used for screencast recording
+#: ../js/ui/components/recorder.js:48
+#, no-c-format
+msgid "Screencast from %d %t"
+msgstr "بۇ %d %t نىڭ ئېكران كۆرۈنۈشى"
+
+#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
+#. system-users for now as Empathy does.
+#: ../js/ui/components/telepathyClient.js:238
+msgid "Invitation"
+msgstr "تەكلىپ"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:298
+msgid "Call"
+msgstr "چاقىر"
+
+#. We got the TpContact
+#: ../js/ui/components/telepathyClient.js:314
+msgid "File Transfer"
+msgstr "ھۆججەت يوللاش"
+
+#: ../js/ui/components/telepathyClient.js:418
+msgid "Chat"
+msgstr "سۆھبەت"
+
+#: ../js/ui/components/telepathyClient.js:480
+msgid "Unmute"
+msgstr "ئاۋازلىق"
+
+#: ../js/ui/components/telepathyClient.js:480
+msgid "Mute"
+msgstr "ئۈنسىز"
+
+#. Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"
+#: ../js/ui/components/telepathyClient.js:942
+#, no-c-format
+msgid "<b>Yesterday</b>, <b>%H:%M</b>"
+msgstr "<b>تۈنۈگۈن</b>، <b>%H:%M</b>"
+
+#. Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30
+#: ../js/ui/components/telepathyClient.js:948
+#, no-c-format
+msgid "<b>%A</b>, <b>%H:%M</b>"
+msgstr "<b>%A</b>، <b>%H:%M</b>"
+
+#. Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"
+#: ../js/ui/components/telepathyClient.js:953
+#, no-c-format
+msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+msgstr "<b>%B</b> <b>%d</b>، <b>%H:%M</b>"
+
+#. Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"
+#: ../js/ui/components/telepathyClient.js:957
+#, no-c-format
+msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>، <b>%H:%M</b> "
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/components/telepathyClient.js:985
+#, c-format
+msgid "%s is now known as %s"
+msgstr "%s نى ھازىر %s دەپ قارايدۇ"
+
+#. translators: argument is a room name like
+#. * room@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1088
+#, c-format
+msgid "Invitation to %s"
+msgstr "%s غا تەكلىپ"
+
+#. translators: first argument is the name of a contact and the second
+#. * one the name of a room. "Alice is inviting you to join room@jabber.org
+#. * for example.
+#: ../js/ui/components/telepathyClient.js:1096
+#, c-format
+msgid "%s is inviting you to join %s"
+msgstr "%s سىزنى %s غا قاتنىشىشنى تەكلىپ قىلىۋاتىدۇ"
+
+#: ../js/ui/components/telepathyClient.js:1098
+#: ../js/ui/components/telepathyClient.js:1137
+#: ../js/ui/components/telepathyClient.js:1177
+#: ../js/ui/components/telepathyClient.js:1240
+msgid "Decline"
+msgstr "قوشۇلما"
+
+#: ../js/ui/components/telepathyClient.js:1099
+#: ../js/ui/components/telepathyClient.js:1178
+#: ../js/ui/components/telepathyClient.js:1241
+msgid "Accept"
+msgstr "قوشۇل"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1129
+#, c-format
+msgid "Video call from %s"
+msgstr "%s دىن كەلگەن سىن چاقىرىقى"
+
+#. translators: argument is a contact name like Alice for example.
+#: ../js/ui/components/telepathyClient.js:1132
+#, c-format
+msgid "Call from %s"
+msgstr "«%s» دىن كەلگەن تېلېفون"
+
+#. translators: this is a button label (verb), not a noun
+#: ../js/ui/components/telepathyClient.js:1139
+msgid "Answer"
+msgstr "جاۋاب"
+
+#. To translators: The first parameter is
+#. * the contact's alias and the second one is the
+#. * file name. The string will be something
+#. * like: "Alice is sending you test.ogg"
+#.
+#: ../js/ui/components/telepathyClient.js:1171
+#, c-format
+msgid "%s is sending you %s"
+msgstr "%s سىزگە %s نى ئەۋەتتى"
+
+#. To translators: The parameter is the contact's alias
+#: ../js/ui/components/telepathyClient.js:1206
+#, c-format
+msgid "%s would like permission to see when you are online"
+msgstr "%s سىزنى توردا بار چېغىڭىزدا كۆرۈشكە ئىجازەت سوراۋاتىدۇ"
+
+#: ../js/ui/components/telepathyClient.js:1298
+msgid "Network error"
+msgstr "تور خاتالىقى"
+
+#: ../js/ui/components/telepathyClient.js:1300
+msgid "Authentication failed"
+msgstr "كىملىك دەلىللەش مەغلۇپ بولدى"
+
+#: ../js/ui/components/telepathyClient.js:1302
+msgid "Encryption error"
+msgstr "شىفىرلاش خاتالىقى"
+
+#: ../js/ui/components/telepathyClient.js:1304
+msgid "Certificate not provided"
+msgstr "گۇۋاھنامە تەمىنلەنمىگەن"
+
+#: ../js/ui/components/telepathyClient.js:1306
+msgid "Certificate untrusted"
+msgstr "گۇۋاھنامە ئىشەنچسىز"
+
+#: ../js/ui/components/telepathyClient.js:1308
+msgid "Certificate expired"
+msgstr "گۇۋاھنامىنىڭ ۋاقتى ئوتكەن"
+
+#: ../js/ui/components/telepathyClient.js:1310
+msgid "Certificate not activated"
+msgstr "گۇۋاھنامە ئاكتىپ ئەمەس"
+
+#: ../js/ui/components/telepathyClient.js:1312
+msgid "Certificate hostname mismatch"
+msgstr "گۇۋاھنامە كومپيۇتېرىنىڭ ئاتى ماسلاشمىدى"
+
+#: ../js/ui/components/telepathyClient.js:1314
+msgid "Certificate fingerprint mismatch"
+msgstr "گۇۋاھنامىنىڭ بارماق ئىزى ماسلاشمىدى"
+
+#: ../js/ui/components/telepathyClient.js:1316
+msgid "Certificate self-signed"
+msgstr "گۇۋاھنامىنىڭ ئۆزىنىڭ ئىمزاسى"
+
+#: ../js/ui/components/telepathyClient.js:1318
+msgid "Status is set to offline"
+msgstr "ھالەت توردا يوق قىلىپ بەلگىلەندى"
+
+#: ../js/ui/components/telepathyClient.js:1320
+msgid "Encryption is not available"
+msgstr "شىفىرلاشنى ئىشلەتكىلى بولمايدۇ"
+
+#: ../js/ui/components/telepathyClient.js:1322
+msgid "Certificate is invalid"
+msgstr "گۇۋاھنامە ئىناۋەتسىز"
+
+#: ../js/ui/components/telepathyClient.js:1324
+msgid "Connection has been refused"
+msgstr "باغلىنىش رەت قىلىندى"
+
+#: ../js/ui/components/telepathyClient.js:1326
+msgid "Connection can't be established"
+msgstr "باغلانغىلى بولمىدى"
+
+#: ../js/ui/components/telepathyClient.js:1328
+msgid "Connection has been lost"
+msgstr "باغلىنىش ئۈزۈلدى"
+
+#: ../js/ui/components/telepathyClient.js:1330
+msgid "This account is already connected to the server"
+msgstr "بۇ ھېسابات ئاللىقاچان مۇلازىمېتىرغا باغلانغان"
+
+#: ../js/ui/components/telepathyClient.js:1332
+msgid ""
+"Connection has been replaced by a new connection using the same resource"
+msgstr "ئوخشاش مەنبە ئىشلەتكەن باغلىنىش ئەسلىدىكىنىڭ ئورنىنى ئىگىلىدى"
+
+#: ../js/ui/components/telepathyClient.js:1334
+msgid "The account already exists on the server"
+msgstr "مۇلازىمېتىردا بۇ ھېسابات ئاللىقاچان بار"
+
+#: ../js/ui/components/telepathyClient.js:1336
+msgid "Server is currently too busy to handle the connection"
+msgstr "مۇلازىمېتىر بەكلا ئالدىراش بولغاچقا بۇ باغلىنىشنى بىر تەرەپ قىلالمايدۇ"
+
+#: ../js/ui/components/telepathyClient.js:1338
+msgid "Certificate has been revoked"
+msgstr "گۇۋاھنامە ئىناۋەتسىز قىلىنغان"
+
+#: ../js/ui/components/telepathyClient.js:1340
+msgid ""
+"Certificate uses an insecure cipher algorithm or is cryptographically weak"
+msgstr "گۇۋاھنامە خەتەرلىك شىفىرلاش ئالگورىزىمىنى ئىشلەتكەن ياكى شىفىرلىنىشى ئاجىز"
+
+#: ../js/ui/components/telepathyClient.js:1342
+msgid ""
+"The length of the server certificate, or the depth of the server certificate "
+"chain, exceed the limits imposed by the cryptography library"
+msgstr "مۇلازىمېتىر گۇۋاھنامىسىنىڭ ئۇزۇنلۇقى ياكى مۇلازىمېتىر گۇۋاھنامىسىنىڭ زەنجىرىنىڭ چوڭقۇرلۇقى شىفىرلاش فۇنكسىيە ئامبىرىدا بەلگىلەنگەن ئۇزۇنلۇقتىن ئېشىپ كەتتى"
+
+#: ../js/ui/components/telepathyClient.js:1344
+msgid "Internal error"
+msgstr "ئىچكى خاتالىق"
+
+#. translators: argument is the account name, like
+#. * name@jabber.org for example.
+#: ../js/ui/components/telepathyClient.js:1354
+#, c-format
+msgid "Unable to connect to %s"
+msgstr "%s غا باغلانغىلى بولمىدى"
+
+#: ../js/ui/components/telepathyClient.js:1359
+msgid "View account"
+msgstr "ھېساباتنى كۆرۈش"
+
+#: ../js/ui/components/telepathyClient.js:1398
+msgid "Unknown reason"
+msgstr "نامەلۇم سەۋەب"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96
+msgid "Windows"
+msgstr "كۆزنەكلەر"
+
+#: ../js/ui/dash.js:248 ../js/ui/dash.js:286
+msgid "Show Applications"
+msgstr "پروگراممىلارنى كۆرسىتىدۇ"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/dash.js:435
+msgid "Dash"
+msgstr "سىزىقچە"
+
+#: ../js/ui/dateMenu.js:91
+msgid "Open Calendar"
+msgstr "يىلنامىلەرنى ئېچىش"
+
+#: ../js/ui/dateMenu.js:96
+msgid "Open Clocks"
+msgstr "سائەتلەرنى ئېچىش"
+
+#: ../js/ui/dateMenu.js:105
+msgid "Date & Time Settings"
+msgstr "چېسلا ۋە ۋاقىت تەڭشىكى"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:205
+msgid "%A %B %e, %Y"
+msgstr "%A %B %e، %Y"
+
+#: ../js/ui/endSessionDialog.js:63
+#, c-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s دىن چىقىش"
+
+#: ../js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Log Out"
+msgstr "تىزىمدىن چىقىش"
+
+#: ../js/ui/endSessionDialog.js:65
+msgid "Click Log Out to quit these applications and log out of the system."
+msgstr "تىزىمدىن چىقىش (Log Out) نى بېسىپ پروگراممىدىن چېكىنىش ۋە سىستېما تىزىمىدىن چىقىش."
+
+#: ../js/ui/endSessionDialog.js:67
+#, c-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "ئىشلەتكۈچى %s يەنە %d سېكۇنتتىن كېيىن ئاپتوماتىك چىقىپ كېتىدۇ."
+
+#: ../js/ui/endSessionDialog.js:72
+#, c-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "سىز يەنە %d سېكۇنتتىن كېيىن ئاپتوماتىك چىقىپ كېتىسىز."
+
+#: ../js/ui/endSessionDialog.js:76
+msgid "Logging out of the system."
+msgstr "سىستېما تىزىمدىن چىقىۋاتىدۇ"
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "تىزىمدىن چىقىش"
+
+#: ../js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Power Off"
+msgstr "توكنى ئۈز"
+
+#: ../js/ui/endSessionDialog.js:84
+msgid "Click Power Off to quit these applications and power off the system."
+msgstr "تۈكنى ئۈز چېكىلسە قوللىنىشچان پروگراممىلاردىن چېكىنىپ سىستېمىنى تاقايدۇ."
+
+#: ../js/ui/endSessionDialog.js:86
+#, c-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "سىستېمىنىڭ توكى يەنە %d سېكۇنتتىن كېيىن ئاپتوماتىك ئۈزۈلىدۇ."
+
+#: ../js/ui/endSessionDialog.js:90
+msgid "Powering off the system."
+msgstr "سىستېمىنى تاقاۋاتىدۇ."
+
+#: ../js/ui/endSessionDialog.js:92 ../js/ui/endSessionDialog.js:109
+msgctxt "button"
+msgid "Restart"
+msgstr "قايتا قوزغات"
+
+#: ../js/ui/endSessionDialog.js:94
+msgctxt "button"
+msgid "Power Off"
+msgstr "توكنى ئۈز"
+
+#: ../js/ui/endSessionDialog.js:100
+msgctxt "title"
+msgid "Restart"
+msgstr "قايتا قوزغات"
+
+#: ../js/ui/endSessionDialog.js:101
+msgid "Click Restart to quit these applications and restart the system."
+msgstr "قايتا قوزغات (restart) چېكىلسە پروگراممىدىن چېكىنىپ ۋە سىستېمىنى قايتا قوزغىتىدۇ."
+
+#: ../js/ui/endSessionDialog.js:103
+#, c-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "سىستېما يەنە %d سېكۇنتتىن كېيىن ئاپتوماتىك قايتا قوزغىلىدۇ."
+
+#: ../js/ui/endSessionDialog.js:107
+msgid "Restarting the system."
+msgstr "سىستېما قايتا قوزغىلىۋاتىدۇ."
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "ئورنات"
+
+#: ../js/ui/extensionDownloader.js:204
+#, c-format
+msgid "Download and install '%s' from extensions.gnome.org?"
+msgstr "مەنبە extensions.gnome.org دىن ‹%s› نى چۈشۈرۈپ ئورنىتامسىز؟"
+
+#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
+#: ../js/ui/status/power.js:211
+msgid "Keyboard"
+msgstr "ھەرپتاختا"
+
+#: ../js/ui/lookingGlass.js:693
+msgid "No extensions installed"
+msgstr "ھېچقانداق كېڭەيتىلمە ئورنىتىلمىغان"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:747
+#, c-format
+msgid "%s has not emitted any errors."
+msgstr "%s نىڭدا ھېچقانداق خاتالىق يۈز بەرمىدى."
+
+#: ../js/ui/lookingGlass.js:753
+msgid "Hide Errors"
+msgstr "خاتالىقلارنى يوشۇر"
+
+#: ../js/ui/lookingGlass.js:757 ../js/ui/lookingGlass.js:817
+msgid "Show Errors"
+msgstr "خاتالىقلارنى كۆرسەت"
+
+#: ../js/ui/lookingGlass.js:766
+msgid "Enabled"
+msgstr "ئىناۋەتلىك قىلىنغان"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:769 ../src/gvc/gvc-mixer-control.c:1830
+msgid "Disabled"
+msgstr "ئىناۋەتسىز قىلىنغان"
+
+#: ../js/ui/lookingGlass.js:771
+msgid "Error"
+msgstr "خاتالىق"
+
+#: ../js/ui/lookingGlass.js:773
+msgid "Out of date"
+msgstr "ۋاقتى ئۆتۈپ كەتكەن"
+
+#: ../js/ui/lookingGlass.js:775
+msgid "Downloading"
+msgstr "چۈشۈرۈۋاتىدۇ"
+
+#: ../js/ui/lookingGlass.js:799
+msgid "View Source"
+msgstr "مەنبەنى كۆرسەت"
+
+#: ../js/ui/lookingGlass.js:808
+msgid "Web Page"
+msgstr "توربەت"
+
+#: ../js/ui/messageTray.js:1182
+msgid "Open"
+msgstr "ئاچ"
+
+#: ../js/ui/messageTray.js:1189
+msgid "Remove"
+msgstr "چىقىرىۋەت"
+
+#: ../js/ui/messageTray.js:1501
+msgid "Clear Messages"
+msgstr "ئۇچۇرنى تازىلا"
+
+#: ../js/ui/messageTray.js:1528
+msgid "Notification Settings"
+msgstr "ئۇقتۇرۇش تەڭشىكى"
+
+#: ../js/ui/messageTray.js:1707
+msgid "No Messages"
+msgstr "ئۇچۇر يوق"
+
+#: ../js/ui/messageTray.js:1787
+msgid "Message Tray"
+msgstr "ئۇچۇر قوندىقى"
+
+#: ../js/ui/messageTray.js:2864
+msgid "System Information"
+msgstr "سىستېما ئۇچۇرى"
+
+#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:374
+msgctxt "program"
+msgid "Unknown"
+msgstr "نامەلۇم"
+
+#: ../js/ui/overviewControls.js:460 ../js/ui/screenShield.js:153
+#, c-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d دانە ئۇچۇر"
+
+#: ../js/ui/overview.js:82
+msgid "Undo"
+msgstr "يېنىۋال"
+
+#: ../js/ui/overview.js:129
+msgid "Overview"
+msgstr "قىسقىچە بايان"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/overview.js:284
+msgid "Type to search…"
+msgstr "ئىزدەيدىغاننى كىرگۈزۈڭ.…"
+
+#: ../js/ui/panel.js:613
+msgid "Quit"
+msgstr "ئاخىرلاشتۇر"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:642
+msgid "Activities"
+msgstr "پائالىيەتلەر"
+
+#: ../js/ui/panel.js:983
+msgid "Top Bar"
+msgstr "ئۈستى بالداق"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:727
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:205
+msgid "Enter a Command"
+msgstr "بۇيرۇق كىرگۈزۈڭ"
+
+#: ../js/ui/runDialog.js:241
+msgid "Close"
+msgstr "ياپ"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: ../js/ui/screenShield.js:90
+msgid "%A, %B %d"
+msgstr "%A، %B %d"
+
+#: ../js/ui/screenShield.js:155
+#, c-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d دانە ئۇقتۇرۇش"
+
+#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:806
+msgid "Lock"
+msgstr "قۇلۇپلا"
+
+#: ../js/ui/screenShield.js:639
+msgid "GNOME needs to lock the screen"
+msgstr "گىنوم ئېكراننى قۇلۇپلىشى زۆرۈر"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
+msgid "Unable to lock"
+msgstr "قۇلۇپلىغىلى بولمىدى"
+
+#: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1170
+msgid "Lock was blocked by an application"
+msgstr "بىر پروگرامما قۇلۇپلاشنى توسۇپ قويغان"
+
+#: ../js/ui/searchDisplay.js:431
+msgid "Searching…"
+msgstr "ئىزدەۋاتىدۇ…"
+
+#: ../js/ui/searchDisplay.js:475
+msgid "No results."
+msgstr "نەتىجە يوق."
+
+#: ../js/ui/shellEntry.js:29
+msgid "Copy"
+msgstr "كۆچۈر"
+
+#: ../js/ui/shellEntry.js:34
+msgid "Paste"
+msgstr "چاپلا"
+
+#: ../js/ui/shellEntry.js:105
+msgid "Show Text"
+msgstr "تېكىست كۆرسەت"
+
+#: ../js/ui/shellEntry.js:107
+msgid "Hide Text"
+msgstr "تېكىستنى يوشۇر"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "ئىم"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "ئىمنى ئەستە تۇتۇش"
+
+#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:140
+msgid "Unlock"
+msgstr "قۇلۇپسىزلا"
+
+#: ../js/ui/status/accessibility.js:36
+msgid "Accessibility"
+msgstr "ياردەم ئىقتىدارى"
+
+#: ../js/ui/status/accessibility.js:41
+msgid "Zoom"
+msgstr "كېڭەيت-تارايت"
+
+#: ../js/ui/status/accessibility.js:48
+msgid "Screen Reader"
+msgstr "ئېكران ئوقۇغۇچ"
+
+#: ../js/ui/status/accessibility.js:52
+msgid "Screen Keyboard"
+msgstr "ئېكران ھەرپتاختىسى"
+
+#: ../js/ui/status/accessibility.js:56
+msgid "Visual Alerts"
+msgstr "كۆرۈنمە ئاگاھلاندۇرۇش"
+
+#: ../js/ui/status/accessibility.js:59
+msgid "Sticky Keys"
+msgstr "يېپىشقاق كۇنۇپكىلار"
+
+#: ../js/ui/status/accessibility.js:62
+msgid "Slow Keys"
+msgstr "ئاستا كۇنۇپكىلار"
+
+#: ../js/ui/status/accessibility.js:65
+msgid "Bounce Keys"
+msgstr "سەكرىمە كۇنۇپكىلار"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Mouse Keys"
+msgstr "چاشقىنەك كۇنۇپكىلىرى"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Universal Access Settings"
+msgstr "ئۇنىۋېرسال زىيارەت تەڭشىكى"
+
+#: ../js/ui/status/accessibility.js:129
+msgid "High Contrast"
+msgstr "يۇقىرى ئاق-قارىلىقى"
+
+#: ../js/ui/status/accessibility.js:178
+msgid "Large Text"
+msgstr "چوڭ تېكىست"
+
+#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
+#: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
+#: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
+#: ../js/ui/status/network.js:826
+msgid "Bluetooth"
+msgstr "كۆكچىش"
+
+#: ../js/ui/status/bluetooth.js:45
+msgid "Visibility"
+msgstr "كۆرۈشچانلىقى"
+
+#: ../js/ui/status/bluetooth.js:59
+msgid "Send Files to Device…"
+msgstr "ھۆججەتلەرنى ئۈسكۈنىگە ئەۋەتىش…"
+
+#: ../js/ui/status/bluetooth.js:60
+msgid "Set Up a New Device…"
+msgstr "يېڭى ئۈسكۈنە ئورنىتىش…"
+
+#: ../js/ui/status/bluetooth.js:84
+msgid "Bluetooth Settings"
+msgstr "كۆكچىش تەڭشىكى"
+
+#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
+#: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:178
+msgid "hardware disabled"
+msgstr "قاتتىق دېتال ئىناۋەتسىز قىلىنغان"
+
+#: ../js/ui/status/bluetooth.js:197
+msgid "Connection"
+msgstr "باغلىنىش"
+
+#: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:460
+msgid "disconnecting..."
+msgstr "ئۈزۈۋاتىدۇ…"
+
+#: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:466
+#: ../js/ui/status/network.js:1546
+msgid "connecting..."
+msgstr "باغلىنىۋاتىدۇ…"
+
+#: ../js/ui/status/bluetooth.js:239
+msgid "Send Files…"
+msgstr "ھۆججەت يوللاش…"
+
+#: ../js/ui/status/bluetooth.js:246
+msgid "Keyboard Settings"
+msgstr "ھەرپتاختا تەڭشەكلىرى‏"
+
+#: ../js/ui/status/bluetooth.js:249
+msgid "Mouse Settings"
+msgstr "چاشقىنەك تەڭشەكلىرى"
+
+#: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
+msgid "Sound Settings"
+msgstr "ئاۋاز تەڭشەكلىرى‏"
+
+#: ../js/ui/status/bluetooth.js:322
+#, c-format
+msgid "Authorization request from %s"
+msgstr "‹%s› تىن كەلگەن دەلىللەش ئىلتىماسى"
+
+#: ../js/ui/status/bluetooth.js:328
+#, c-format
+msgid "Device %s wants access to the service '%s'"
+msgstr "ئۈسكۈنە %s مۇلازىمەت ‹%s› نى زىيارەت قىلماقچى"
+
+#: ../js/ui/status/bluetooth.js:330
+msgid "Always grant access"
+msgstr "ھەمىشە زىيارەتكە رۇخسەت"
+
+#: ../js/ui/status/bluetooth.js:331
+msgid "Grant this time only"
+msgstr "بۇ قېتىمغىلا رۇخسەت"
+
+#: ../js/ui/status/bluetooth.js:332
+msgid "Reject"
+msgstr "رەت قىل"
+
+#. Translators: argument is the device short name
+#: ../js/ui/status/bluetooth.js:359
+#, c-format
+msgid "Pairing confirmation for %s"
+msgstr "‏%s نىڭ جەزملىشىنى جۈپلەشتۈرۈۋاتىدۇ"
+
+#: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
+#, c-format
+msgid "Device %s wants to pair with this computer"
+msgstr "ئۈسكۈنە‹%s› كومپيۇتېر بىلەن جۈپلەنمەكچى"
+
+#: ../js/ui/status/bluetooth.js:366
+#, c-format
+msgid "Please confirm whether the PIN '%06d' matches the one on the device."
+msgstr "ئۈسكۈنە ئۈستىدىكى بىلەن PIN ‹%06d› ماسلىشامدۇ جەزملەڭ."
+
+#. Translators: this is the verb, not the noun
+#: ../js/ui/status/bluetooth.js:369
+msgid "Matches"
+msgstr "ماسلاشتى"
+
+#: ../js/ui/status/bluetooth.js:370
+msgid "Does not match"
+msgstr "ماسلاشمىدى"
+
+#: ../js/ui/status/bluetooth.js:389
+#, c-format
+msgid "Pairing request for %s"
+msgstr "‏%s دىن كەلگەن جۈپلەش ئىلتىماسى"
+
+#: ../js/ui/status/bluetooth.js:397
+msgid "Please enter the PIN mentioned on the device."
+msgstr "ئۈسكۈنىسىدە يوللىغان PIN نومۇرىنى كىرگۈزۈڭ."
+
+#: ../js/ui/status/bluetooth.js:414
+msgid "OK"
+msgstr "تامام"
+
+#: ../js/ui/status/keyboard.js:368
+msgid "Show Keyboard Layout"
+msgstr "ھەرپ تاختا ئورۇنلاشتۇرۇشىنى كۆرسىتىش"
+
+#: ../js/ui/status/keyboard.js:373
+msgid "Region & Language Settings"
+msgstr "رايون ۋە تىل تەڭشىكى"
+
+#: ../js/ui/status/lockScreenMenu.js:43
+msgid "Volume, network, battery"
+msgstr "ئاۋاز مىقدارى، تور، توكدان"
+
+#: ../js/ui/status/network.js:104
+msgid "<unknown>"
+msgstr "<نامەلۇم>"
+
+#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
+#: ../js/ui/status/network.js:200
+msgid "disabled"
+msgstr "چەكلەنگەن"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu)
+#: ../js/ui/status/network.js:458
+msgid "unmanaged"
+msgstr "باشقۇرۇلمىغان"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1549
+msgid "authentication required"
+msgstr "كىملىك دەلىللەش زۆرۈر"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing
+#: ../js/ui/status/network.js:479
+msgid "firmware missing"
+msgstr "مۇقىم دېتال كەم"
+
+#. Translators: this is for wired network devices that are physically disconnected
+#: ../js/ui/status/network.js:486
+msgid "cable unplugged"
+msgstr "كابېل چېتىلمىدى"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage
+#: ../js/ui/status/network.js:491
+msgid "unavailable"
+msgstr "ئىشلەتكىلى بولمايدۇ"
+
+#: ../js/ui/status/network.js:493 ../js/ui/status/network.js:1551
+msgid "connection failed"
+msgstr "باغلىنىش مەغلۇپ بولدى"
+
+#: ../js/ui/status/network.js:552 ../js/ui/status/network.js:1435
+#: ../js/ui/status/network.js:1627
+msgid "More…"
+msgstr "تېخىمۇ كۆپ…"
+
+#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
+#. and we cannot access its settings (including the name)
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1365
+msgid "Connected (private)"
+msgstr "باغلاندى (شەخسىي)"
+
+#: ../js/ui/status/network.js:667
+msgid "Wired"
+msgstr "سىملىق"
+
+#: ../js/ui/status/network.js:668
+msgid "Auto Ethernet"
+msgstr "ئاپتوماتىك Ethernet"
+
+#: ../js/ui/status/network.js:695
+msgid "Mobile broadband"
+msgstr "كۆچمە كەڭ بەلۋاغ"
+
+#: ../js/ui/status/network.js:728
+msgid "Auto broadband"
+msgstr "ئاپتوماتىك كۆچمە كەڭ بەلۋاغ"
+
+#: ../js/ui/status/network.js:731
+msgid "Auto dial-up"
+msgstr "ئاپتوماتىك نومۇر بۇرا"
+
+#. TRANSLATORS: this the automatic wireless connection name (including the network name)
+#: ../js/ui/status/network.js:861 ../js/ui/status/network.js:1382
+#, c-format
+msgid "Auto %s"
+msgstr "ئاپتوماتىك %s"
+
+#: ../js/ui/status/network.js:863
+msgid "Auto bluetooth"
+msgstr "ئاپتوماتىك كۆكچىش"
+
+#: ../js/ui/status/network.js:1384
+msgid "Auto wireless"
+msgstr "ئاپتوماتىك سىمسىز تور"
+
+#: ../js/ui/status/network.js:1729
+msgid "Enable networking"
+msgstr "تور ئۇلاشنى قوزغات"
+
+#: ../js/ui/status/network.js:1771
+msgid "Wi-Fi"
+msgstr "Wi-Fi"
+
+#: ../js/ui/status/network.js:1790
+msgid "Network Settings"
+msgstr "تور تەڭشەكلىرى"
+
+#: ../js/ui/status/network.js:1807
+msgid "Network Manager"
+msgstr "تور باشقۇرغۇچ"
+
+#: ../js/ui/status/network.js:1897
+msgid "Connection failed"
+msgstr "باغلىنىش مەغلۇپ بولدى"
+
+#: ../js/ui/status/network.js:1898
+msgid "Activation of network connection failed"
+msgstr "تور باغلىنىشىنى ئاكتىپلاش مەغلۇپ بولدى"
+
+#: ../js/ui/status/network.js:2276
+msgid "Networking is disabled"
+msgstr "تورغا باغلىنىش چەكلەنگەن"
+
+#: ../js/ui/status/power.js:55
+msgid "Battery"
+msgstr "توكدان"
+
+#: ../js/ui/status/power.js:81
+msgid "Power Settings"
+msgstr "توك مەنبە تەڭشىكى"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:99
+msgid "Estimating…"
+msgstr "مۆلچەرلەۋاتىدۇ…"
+
+#: ../js/ui/status/power.js:106
+#, c-format
+msgid "%d hour remaining"
+msgid_plural "%d hours remaining"
+msgstr[0] "%d سائەت قالدى"
+
+#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
+#: ../js/ui/status/power.js:109
+#, c-format
+msgid "%d %s %d %s remaining"
+msgstr "%d %s %d %s قالدى"
+
+#: ../js/ui/status/power.js:111
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "سائەت"
+
+#: ../js/ui/status/power.js:111
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "مىنۇت"
+
+#: ../js/ui/status/power.js:114
+#, c-format
+msgid "%d minute remaining"
+msgid_plural "%d minutes remaining"
+msgstr[0] "%d مىنۇت قالدى"
+
+#: ../js/ui/status/power.js:117 ../js/ui/status/power.js:191
+#, c-format
+msgctxt "percent of battery remaining"
+msgid "%d%%"
+msgstr "%d%%"
+
+#: ../js/ui/status/power.js:201
+msgid "AC Adapter"
+msgstr "ئۆزگىرىشچان توك ماسلاشتۇرغۇچ"
+
+#: ../js/ui/status/power.js:203
+msgid "Laptop Battery"
+msgstr "يان كومپيۇتېر توكدانى"
+
+#: ../js/ui/status/power.js:205
+msgid "UPS"
+msgstr "UPS"
+
+#: ../js/ui/status/power.js:207
+msgid "Monitor"
+msgstr "ئېكران"
+
+#: ../js/ui/status/power.js:209
+msgid "Mouse"
+msgstr "چاشقىنەك"
+
+#: ../js/ui/status/power.js:213
+msgid "PDA"
+msgstr "PDA"
+
+#: ../js/ui/status/power.js:215
+msgid "Cell Phone"
+msgstr "يانفون"
+
+#: ../js/ui/status/power.js:217
+msgid "Media Player"
+msgstr "ۋاسىتە قويغۇ"
+
+#: ../js/ui/status/power.js:219
+msgid "Tablet"
+msgstr "سەزگۈر تاختا"
+
+#: ../js/ui/status/power.js:221
+msgid "Computer"
+msgstr "كومپيۇتېر"
+
+#: ../js/ui/status/power.js:223
+msgctxt "device"
+msgid "Unknown"
+msgstr "نامەلۇم"
+
+#: ../js/ui/status/volume.js:124
+msgid "Volume changed"
+msgstr "ئاۋاز مىقدارى ئۆزگەردى"
+
+#. Translators: This is the label for audio volume
+#: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
+msgid "Volume"
+msgstr "ئاۋاز مىقدارى"
+
+#: ../js/ui/status/volume.js:258
+msgid "Microphone"
+msgstr "مىكروفون"
+
+#: ../js/ui/unlockDialog.js:151
+msgid "Log in as another user"
+msgstr "باشقا ئىشلەتكۈچى سۈپىتىدە كىر"
+
+#: ../js/ui/unlockDialog.js:177
+msgid "Unlock Window"
+msgstr "كۆزنەكنى قۇلۇپلا"
+
+#: ../js/ui/userMenu.js:193
+msgid "Available"
+msgstr "ئىشلەتكىلى بولىدۇ"
+
+#: ../js/ui/userMenu.js:196
+msgid "Busy"
+msgstr "ئالدىراش"
+
+#: ../js/ui/userMenu.js:199
+msgid "Invisible"
+msgstr "يوشۇرۇن"
+
+#: ../js/ui/userMenu.js:202
+msgid "Away"
+msgstr "يوق"
+
+#: ../js/ui/userMenu.js:205
+msgid "Idle"
+msgstr "بىكار"
+
+#: ../js/ui/userMenu.js:208
+msgid "Offline"
+msgstr "توردا يوق"
+
+#: ../js/ui/userMenu.js:780
+msgid "Notifications"
+msgstr "ئۇقتۇرۇشلار"
+
+#: ../js/ui/userMenu.js:796
+msgid "Switch User"
+msgstr "ئىشلەتكۈچى ئالماشتۇرۇش"
+
+#: ../js/ui/userMenu.js:801
+msgid "Log Out"
+msgstr "تىزىمدىن چىقىش"
+
+#: ../js/ui/userMenu.js:821
+msgid "Install Updates & Restart"
+msgstr "يېڭىلانمىلارنى ئورنىتىپ قايتا قوزغات"
+
+#: ../js/ui/userMenu.js:839
+msgid "Your chat status will be set to busy"
+msgstr "سىزنىڭ سۆھبەت ھالىتىڭىز ئالدىراش قىلىنىدۇ"
+
+#: ../js/ui/userMenu.js:840
+msgid ""
+"Notifications are now disabled, including chat messages. Your online status "
+"has been adjusted to let others know that you might not see their messages."
+msgstr ""
+
+#: ../js/ui/userMenu.js:886
+msgid "Other users are logged in."
+msgstr "باشقا ئىشلەتكۈچىلەر كىردى:"
+
+#: ../js/ui/userMenu.js:891
+msgid "Shutting down might cause them to lose unsaved work."
+msgstr "تاقالسا ساقلانمىغان خىزمەتلەر يوقىلىشى مۇمكىن."
+
+#: ../js/ui/userMenu.js:918
+#, c-format
+msgid "%s (remote)"
+msgstr "%s (يىراق)"
+
+#: ../js/ui/userMenu.js:920
+#, c-format
+msgid "%s (console)"
+msgstr "%s (تىزگىن كۆزنەك)"
+
+#: ../js/ui/viewSelector.js:100
+msgid "Applications"
+msgstr "پروگراممىلار"
+
+#: ../js/ui/viewSelector.js:104
+msgid "Search"
+msgstr "ئىزدە"
+
+#: ../js/ui/wanda.js:92
+#, c-format
+msgid ""
+"Sorry, no wisdom for you today:\n"
+"%s"
+msgstr "كەچۈرۈڭ، سىزگە بۈگۈن ھېكمەتلىك سۆز يوق:\n"
+"%s"
+
+#: ../js/ui/wanda.js:96
+#, c-format
+msgid "%s the Oracle says"
+msgstr "خەۋەرچى مۇنداق دەيدۇ %s"
+
+#: ../js/ui/windowAttentionHandler.js:19
+#, c-format
+msgid "'%s' is ready"
+msgstr "‹%s› تەييار"
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution يىلنامىسى"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1837
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u چىقىرىلما"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1847
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u كىرگۈزۈلمە"
+
+#: ../src/gvc/gvc-mixer-control.c:2371
+msgid "System Sounds"
+msgstr "سىستېما ئاۋازى"
+
+#: ../src/main.c:347
+msgid "Print version"
+msgstr "نەشرىنى باس"
+
+#: ../src/main.c:353
+msgid "Mode used by GDM for login screen"
+msgstr "كىرىش ئېكرانىدا GDM ئىشلىتىدىغان ئۇسۇل"
+
+#: ../src/main.c:359
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "كۆرسىتىلگەن ئۇسۇلنى ئىشلەتسۇن، مەسىلەن: كىرىش ئېكرانى ئۈچۈن «gdm» دېگەندەك"
+
+#: ../src/main.c:365
+msgid "List possible modes"
+msgstr "بارلىق ئۇسۇللارنى تىزىپ كۆرسەت"
+
+#: ../src/shell-app.c:622
+#, c-format
+msgid "Failed to launch '%s'"
+msgstr "‹%s› نى قوزغىتالمىدى"
+
+#: ../src/shell-keyring-prompt.c:708
+msgid "Passwords do not match."
+msgstr "ئىم ماس كەلمىدى."
+
+#: ../src/shell-keyring-prompt.c:716
+msgid "Password cannot be blank"
+msgstr "ئىم قۇرۇق بولسا بولمايدۇ"
+
+#: ../src/shell-polkit-authentication-agent.c:343
+msgid "Authentication dialog was dismissed by the user"
+msgstr "كىملىك دەلىللەش سۆزلەشكۈنى ئىشلەتكۈچى تاقىدى"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "كىر"
+
+#~ msgid "tray"
+#~ msgstr "قونداق، پەغەز"
+
+#~ msgid "Clear"
+#~ msgstr "تازىلا"
+
+#~ msgid "More..."
+#~ msgstr "تېخىمۇ كۆپ…"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l:%M %p"
+#~ msgstr "%l:%M %p"
+
+#~ msgid "United Kingdom"
+#~ msgstr "ئەنگلىيە پادىشاھلىقى"
+
+#~ msgid "Default"
+#~ msgstr "كۆڭۈلدىكى"
+
+#~ msgid "Show full name in the user menu"
+#~ msgstr "ئىشلەتكۈچى تىزىملىكىدە تولۇق ئاتىنى كۆرسەتسۇن"
+
+#~ msgid "Whether the users full name is shown in the user menu or not."
+#~ msgstr "ئىشلەتكۈچى تىزىملىكىدە تولۇق ئاتىنى كۆرسىتەمدۇ يوق بەلگىلەيدۇ."
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should not be loaded."
+#~ msgstr ""
+#~ "GNOME Shell كېڭەيتىلمىسىنىڭ uuid خاسلىقى بار؛ بۇ كۇنۇپكا يۈكلەنمەيدىغان "
+#~ "كېڭەيتىلمىلەر تىزىمىنى كۆرسىتىدۇ."
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "true بولسا سائەتتە ۋاقىت كۆرسەتكەندىن تاشقىرى چېسلا كۆرسىتىدۇ."
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "true بولسا ۋاقىتتا سېكۇنتمۇ كۆرۈنىدۇ."
+
+#~ msgid "Show date in clock"
+#~ msgstr "سائەت ئىچىدە چېسلا كۆرسەت"
+
+#~ msgid "Show time with seconds"
+#~ msgstr "ۋاقىت ئىچىدە سېكۇنتنى بىللە كۆرسەت"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "OpenSearch نىڭ تەمىنلىگۈچىلىرى چەكلەنگەن"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "پروگراممىلار"
+
+#~ msgid "SETTINGS"
+#~ msgstr "تەڭشەكلەر"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %b %e، %R:%S"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %b %e، %R"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %b %e، %l:%M:%S %p"
+
+#~ msgid "%a %b %e, %l:%M %p"
+#~ msgstr "%a %b %e، %l:%M %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "%a %l:%M %p"
+#~ msgstr "%a %l:%M"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "يېقىنقى تۈرلەر"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "«%s» نى ئېگەرسىزلەش مەغلۇپ بولدى"
+
+#~ msgid "Retry"
+#~ msgstr "قايتا سىنا"
+
+#~ msgid "Connect to..."
+#~ msgstr "باغلىنىش…"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ئورۇن ۋە ئۈسكۈنىلەر"
+
+#~ msgid "Power Off..."
+#~ msgstr "توكنى ئۈز…"
+
+#~ msgid "System Settings"
+#~ msgstr "سىستېما تەڭشىكى"
+
+#~ msgid "Lock Screen"
+#~ msgstr "ئېكراننى قۇلۇپلاش"
+
+#~ msgid "Log Out..."
+#~ msgstr "تىزىمدىن چىقىش…"
+
+#~ msgid "Browse Files..."
+#~ msgstr "ھۆججەتكە كۆز يۈگۈرت..."
+
+#~ msgid "Error browsing device"
+#~ msgstr "ئۈسكىنىگە كۆز يۈگۈرتكەندە خاتالىق كۆرۈلدى"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "ئىلتىماس قىلغان ئۈسكىنىگە كۆز يۈگۈرتەلمىدى، خاتالىق '%s'"
+
+#~ msgid "Localization Settings"
+#~ msgstr "يەرلىكلەشتۈرۈش تەڭشىكى"
+
+#~ msgid "Wireless"
+#~ msgstr "سىمسىز"
+
+#~ msgid "VPN Connections"
+#~ msgstr "VPN باغلىنىشلىرى"
+
+#~ msgid "You're now connected to mobile broadband connection '%s'"
+#~ msgstr "ھازىر كۆچمە كەڭ بەلۋاغ '%s' غا باغلاندىڭىز"
+
+#~ msgid "You're now connected to wireless network '%s'"
+#~ msgstr "ھازىر سىمسىز تور '%s' غا باغلاندىڭىز"
+
+#~ msgid "You're now connected to wired network '%s'"
+#~ msgstr "ھازىر سىملىق تور '%s' غا باغلاندىڭىز"
+
+#~ msgid "You're now connected to VPN network '%s'"
+#~ msgstr "ھازىر VPN تور '%s' غا باغلاندىڭىز"
+
+#~ msgid "%s is online."
+#~ msgstr "‏%s توردا."
+
+#~ msgid "%s is away."
+#~ msgstr "‏%s يوق."
+
+#~ msgid "%s is busy."
+#~ msgstr "‏%s ئالدىراش."
+
+#~ msgid "Sent at %X on %A"
+#~ msgstr "سائەت %X %A غا ئەۋەتتى"
+
+#~ msgid "%s has finished starting"
+#~ msgstr "‏%s باشلاشنى تاماملىدى"
+
+#~ msgid "Less than a minute ago"
+#~ msgstr "بىر مىنۇتتىنمۇ ئىلگىرى"
+
+#~ msgid "%d minute ago"
+#~ msgid_plural "%d minutes ago"
+#~ msgstr[0] "%d مىنۇت ئىلگىرى"
+
+#~ msgid "%d hour ago"
+#~ msgid_plural "%d hours ago"
+#~ msgstr[0] "%d سائەت ئىلگىرى"
+
+#~ msgid "%d day ago"
+#~ msgid_plural "%d days ago"
+#~ msgstr[0] "%d كۈن ئىلگىرى"
+
+#~ msgid "%d week ago"
+#~ msgid_plural "%d weeks ago"
+#~ msgstr[0] "%d ھەپتە ئىلگىرى"
+
+#~ msgid "Home Folder"
+#~ msgstr "ماكان مۇندەرىجە"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "PREFERENCES"
+#~ msgstr "مايىللىق"
+
+#~ msgid "Shut Down"
+#~ msgstr "تاقا"
+
+#~ msgid "Click Shut Down to quit these applications and shut down the system."
+#~ msgstr "تاقاش (Shut Down) نى بېسىپ پروگراممىدىن چېكىنىش ۋە سىستېمىنى تاقاش."
+
+#~ msgid "The system will shut down automatically in %d seconds."
+#~ msgstr "بۇ سىستېما %d سېكۇنتتىن كېيىن ئۆزلۈكىدىن تاقىلىدۇ."
+
+#~ msgid "Shutting down the system."
+#~ msgstr "سىستېما تاقىلىۋاتىدۇ."
+
+#~ msgid "Confirm"
+#~ msgstr "جەزملە"
+
+#~ msgid "Shut Down..."
+#~ msgstr "تاقا…"
+
+#~ msgid "Search your computer"
+#~ msgstr "كومپيۇتېر ئىزدەش"
+
+#~ msgid "Customize the panel clock"
+#~ msgstr "تاختا سائەتنى ئۆزلەشتۈرىدۇ"
+
+#~ msgid "Custom format of the clock"
+#~ msgstr "سائەتنىڭ ئىختىيارىي فورماتى"
+
+#~ msgid "Hour format"
+#~ msgstr "سائەت فورماتى"
+
+#~ msgid ""
+#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
+#~ "in time."
+#~ msgstr ""
+#~ "ئەگەر راست (true) بولسا سائەتتە ۋاقىت فورماتىنى «12 سائەت» ياكى «24 "
+#~ "سائەت» كۆرسەتكەندىن سىرت سېكۇنتنىمۇ كۆرسىتىدۇ."
+
+#~ msgid ""
+#~ "This key specifies the format used by the panel clock when the format key "
+#~ "is set to \"custom\". You can use conversion specifiers understood by "
+#~ "strftime() to obtain a specific format. See the strftime() manual for "
+#~ "more information."
+#~ msgstr ""
+#~ "format (فورمات) كۇنۇپكىسى \"custom\" (ئىختىيارى) قىلىپ تەڭشەلسە بۇ "
+#~ "كۇنۇپكا تاختا سائەت ئىشلىتىدىغان فورماتنى بەلگىلەيدۇ. سىز strftime()نىڭ "
+#~ "فورمات بەلگىسىنى ئىشلىتىپ بەلگىلەنگەن فورماتقا ئېرىشەلەيسىز. تەپسىلاتىنى "
+#~ "strftime() نىڭ قوللانمىسىدىن كۆرۈڭ."
+
+#~ msgid ""
+#~ "This key specifies the hour format used by the panel clock. Possible "
+#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
+#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
+#~ "1970-01-01. If set to \"custom\", the clock will display time according "
+#~ "to the format specified in the custom_format key. Note that if set to "
+#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
+#~ "ignored."
+#~ msgstr ""
+#~ "بۇ كۇنۇپكا تاختا سائەت ئىشلەتكەن سائەت فورماتىنى بەلگىلىگەن. ئىشلەتكىلى "
+#~ "بولىدىغان قىممىتى \"12-hour\" يەنى (12 سائەت)، \"24-hour\" يەنى (24 "
+#~ "سائەت)، \"unix\" ۋە \"custom\" (ئىختىيارى). ئەگەر \"unix\" قىلىپ "
+#~ "تەڭشەلسە سائەت ئۆزلۈكىدىن يېڭى ئېرا (يەنى، 1970-01-01) دىن ئۆتكەن "
+#~ "سېكۇنتنى ئاساس قىلىدۇ. ئەگەر \"custom\" قىلىپ تەڭشەلسە سائەت "
+#~ "custom_format كۇنۇپكا قىممىتىگە ئاساسەن ۋاقىتنى كۆرسىتىدۇ. ئەگەر \"unix"
+#~ "\" ياكى \"custom\" قىلىپ تەڭشەلسە show_date ۋە show_seconds قىممىتىگە "
+#~ "پەرۋا قىلمايدۇ"
+
+#~ msgid "Clip the crosshairs at the center"
+#~ msgstr "نىشانلىغۇچنى ئوتتۇرىغا توغرىلا"
+
+#~ msgid "Color of the crosshairs"
+#~ msgstr "نىشانلىغۇچنىڭ رەڭگى"
+
+#~ msgid ""
+#~ "Determines the length of the vertical and horizontal lines that make up "
+#~ "the crosshairs."
+#~ msgstr ""
+#~ "نىشانلىغۇچنى ھاسىل قىلىدىغان توغرا ۋە بوي سىزىقنىڭ ئۇزۇنلۇقى بەلگىلىنىدۇ."
+
+#~ msgid ""
+#~ "Determines the transparency of the crosshairs, from fully opaque to fully "
+#~ "transparent."
+#~ msgstr "نىشانلىغۇچنىڭ سۈزۈكلۈكى تولۇق سۈزۈكتىن تولۇق تۇتۇققىچە بەلگىلىنىدۇ."
+
+#~ msgid "Enable lens mode"
+#~ msgstr "لېنزا ھالىتىنى قوزغات"
+
+#~ msgid "Length of the crosshairs"
+#~ msgstr "نىشانلىغۇچنىڭ ئېگىزلىكى"
+
+#~ msgid "Magnification factor"
+#~ msgstr "چوڭايتىش-كىچىكلىتىش نىسبىتى"
+
+#~ msgid "Mouse Tracking Mode"
+#~ msgstr "چاشقىنەك ئىزلاش ھالىتى"
+
+#~ msgid "Opacity of the crosshairs"
+#~ msgstr "نىشانلىغۇچنىڭ سۈزۈكلۈكى"
+
+#~ msgid "Scroll magnified contents beyond the edges of the desktop"
+#~ msgstr "دومىلىما چوڭايتقۇچ ئۈستەل ئۈستى گىرۋەك مەزمۇنىدىن ھالقىپ كەتتى"
+
+#~ msgid "Show or hide crosshairs"
+#~ msgstr "نىشانلىغۇچنى كۆرسەت ياكى يوشۇر"
+
+#~ msgid "Show or hide the magnifier"
+#~ msgstr "لوپا ئەينەكنى كۆرسەت ياكى يوشۇر"
+
+#~ msgid "Show or hide the magnifier and all of its zoom regions."
+#~ msgstr ""
+#~ "لوپا ئەينەك ۋە ئۇنىڭ ھەممە چوڭايتىش دائىرىسىنى كۆرسەت ياكى يوشۇرىدۇ."
+
+#~ msgid ""
+#~ "The color of the the vertical and horizontal lines that make up the "
+#~ "crosshairs."
+#~ msgstr "نىشانلىغۇچنى ھاسىل قىلىدىغان توغرا ۋە بوي سىزىقنىڭ رەڭگى."
+
+#~ msgid ""
+#~ "The power of the magnification. A value of 1.0 means no magnification. A "
+#~ "value of 2.0 doubles the size."
+#~ msgstr ""
+#~ "چوڭايتىش كۈچى. 1.0 چوڭايتمايدۇ، 2.0 چوڭلۇقىنى ھەسسىلەيدۇ دېگەن مەنىدە."
+
+#~ msgid "Thickness of the crosshairs"
+#~ msgstr "نىشانلىغۇچنىڭ قېلىنلىقى"
+
+#~ msgid ""
+#~ "Width of the vertical and horizontal lines that make up the crosshairs."
+#~ msgstr "نىشانلىغۇچنى ھاسىل قىلىدىغان توغرا ۋە بوي سىزىقنىڭ كەڭلىكى."
+
+#~ msgid "Clock Format"
+#~ msgstr "سائەت فورماتى"
+
+#~ msgid "Clock Preferences"
+#~ msgstr "سائەت مايىللىقى"
+
+#~ msgid "Panel Display"
+#~ msgstr "تاختا كۆرسىتىش"
+
+#~ msgid "Show seco_nds"
+#~ msgstr "سېكۇنتنى كۆرسەت(_N)"
+
+#~ msgid "_12 hour format"
+#~ msgstr "_12 سائەت فورماتى"
+
+#~ msgid "_24 hour format"
+#~ msgstr "_24 سائەت فورماتى"
+
+#~ msgid "Preferences"
+#~ msgstr "مايىللىق"
+
+#~ msgid ""
+#~ "Can't add a new workspace because maximum workspaces limit has been "
+#~ "reached."
+#~ msgstr ""
+#~ "يېڭى خىزمەت رايونى قوشالمايدۇ چۈنكى ئەڭ كۆپ خىزمەت رايون چېكىگە يەتتى."
+
+#~ msgid "Can't remove the first workspace."
+#~ msgstr "بىرىنچى خىزمەت رايونىنى چىقىرىۋەتكىلى بولمايدۇ."
+
+#~ msgid "Overview workspace view mode"
+#~ msgstr "خىزمەت رايون كۆرۈنۈش ھالىتى ھەققىدە قىسقىچە بايان"
+
+#~ msgid ""
+#~ "The selected workspace view mode in the overview. Supported values are "
+#~ "\"single\" and \"grid\"."
+#~ msgstr ""
+#~ "قىسقىچە باياندىكى تاللانغان خىزمەت رايونىنىڭ كۆرۈنۈش ھالىتى. ئىشلىتىشكە "
+#~ "بولىدىغان قىممەتلەر «يەككە» ۋە «سېتكا»"
+
+#~ msgid "Drag here to add favorites"
+#~ msgstr "بۇ جايغا سۆرەپ يىغقۇچقا قوش"
+
+#~ msgid "Find"
+#~ msgstr "ئىزدە"
diff --git a/po/uk.po b/po/uk.po
new file mode 100644
index 0000000..b04ac6d
--- /dev/null
+++ b/po/uk.po
@@ -0,0 +1,3277 @@
+# Ukrainian translation for gnome-shell.
+# Copyright (C) 2010 Free Software Foundation
+# This file is distributed under the same license as the gnome-shell package.
+#
+#
+#
+# Maxim V. Dziumanenko <dziumanenko@gmail.com>, 2010.
+# Daniel Korostil <ted.korostiled@gmail.com>, 2013, 2014, 2015, 2016, 2017.
+# Danylo Korostil <ted.korostiled@gmail.com>, 2020.
+# Yuri Chornoivan <yurchor@ukr.net>, 2020, 2021, 2022, 2023.
+# Olexander Movchan <olexander-movchan@ukr.net>, 2022.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-02-14 07:30+0000\n"
+"PO-Revision-Date: 2023-02-14 19:55+0200\n"
+"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
+"Language-Team: Ukrainian <none>\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n"
+"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2\n"
+"X-Generator: Lokalize 20.12.0\n"
+"X-Project-Style: gnome\n"
+"X-DL-Team: uk\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: Translating\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Пускачі"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Активувати улюблену програму 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Активувати улюблену програму 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Активувати улюблену програму 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Активувати улюблену програму 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Активувати улюблену програму 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Активувати улюблену програму 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Активувати улюблену програму 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Активувати улюблену програму 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Активувати улюблену програму 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "Знімки екрана"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "Зробити знімок екрана інтерактивно"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "Зробити знімок"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "Зробити знімок вікна"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "Записати з екрана інтерактивно"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Система"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Показувати перелік сповіщень"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Перейти до активних сповіщень"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Показати огляд"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Показати всі програми"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Відкрити меню програм"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Керування вікнами та запуск програм"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Увімкнути внутрішні засоби, які будуть корисними для розробників і "
+"випробування при натисненні Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Надає доступ до внутрішніх засобів зневаджування та спостереження через Alt-"
+"F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Увімкнути розширення UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Розширення GNOME Shell мають властивості «UUID»; цей ключ перелічує "
+"розширення, які потрібно завантажувати. Вимкнення розширень ігноруватиме ці "
+"налаштування для розширень, які з'являються в обох переліках. Також можете "
+"орудувати цим переліком через способи EnableExtension і DisableExtension "
+"DBus DisableExtension DBus на org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Коди UUID розширень, які примусово вимкнено"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Розширення GNOME Shell мають властивості «UUID»; цей ключ перелічує "
+"розширення, які потрібно вимкнути, навіть якщо вони завантажені в поточному "
+"сеансі. Також можете комбінувати цей список через методи D-Bus "
+"«EnableExtension» і «DisableExtension» у org.gnome.Shell. Це ключ переважає "
+"над параметром «enabled-extensions»."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Вимкнути розширення користувача"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Вимкнути всі розширення, які користувач увімкнув сам, без використання "
+"параметра «enabled-extension»."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Вимикає перевірку сумісності версії розширення"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell завантажуватиме тільки розширення, які вимагають поточну версію. "
+"Увімкнення цього параметра вимкне цю перевірку і спробує завантажувати всі "
+"розширення, навіть попри те, що версії можуть бути несумісні."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Перелічити ідентифікатори файла стільниці для улюблених програм"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Програми, які відповідають цьому ідентифікатору, буде показано в ділянці "
+"улюбленого."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Історія для команд (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Історія для пошуку дзеркальних діалогів"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Завжди показувати пункт меню «Вийти» у меню користувача."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Цей ключ перевизначає автоматичне приховування пункту меню «Вийти» за умови, "
+"що користувач або сеанс тільки один."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Чи запам'ятовувати пароль для монтування шифрованих або віддалених файлових "
+"систем"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Середовище вимагатиме пароль, коли змонтовано зашифрований пристрій або "
+"віддалену файлову систему. Якщо пароль можна зберегти для подальшого вжитку, "
+"то з'явиться параметр «Запам'ятати пароль». Цей ключ вказує типовий стан "
+"цього параметра."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "Останній вибраний нетиповий профіль живлення"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"У деяких системах передбачено понад два профілі живлення. Для уможливлення "
+"перемикання між двома профілями у цьому ключі записано останній нетиповий "
+"профіль."
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Останню версію вікна «Вітаємо у GNOME» було показано для"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Цей ключ визначає, для якої версії було востаннє показано вікно «Вітаємо у "
+"GNOME». Порожній рядок відповідає найстарішій доступній версії, а дуже "
+"велике число — версіям, яких іще не існує. Таким великим числом можна "
+"скористатися для того, щоб вимкнути показ вікна."
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "Компонування засобу вибору програм"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Компонування засобу вибору програм. Кожен запис у цьому масиві є сторінкою. "
+"Сторінки зберігаються у порядку, у якому їх буде показано у GNOME Shell. "
+"Кожна сторінка містить пану записів “ідентифікатор програми” → 'дані'. У "
+"поточній версії як 'дані' може бути збережено такі значення: • “позиція”: "
+"позиція піктограми програми на сторінці"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "Комбінація клавіш, щоб відкрити меню програм"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "Комбінація клавіш, щоб відкрити меню програм."
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "Комбінація клавіш для перемикання між станами огляду"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr ""
+"Комбінація клавіш для переходу між сеансом, засобом вибору вікна та таблицею "
+"програм"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Комбінація клавіш для переходу між таблицею програм, засобом вибору вікна та "
+"сеансом"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Комбінація клавіш, щоб «Показати програми»"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Комбінація клавіш, щоб «Показати програми» в огляді діяльності."
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "Комбінація клавіш, щоб відкрити огляд"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "Комбінація клавіш, щоб відкрити огляд діяльності."
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Комбінація клавіш, щоб змінювати видимість переліку сповіщень"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Комбінація клавіш, щоб змінювати видимість переліку сповіщень."
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "Комбінація клавіш, щоб перейти до активних сповіщень"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "Комбінація клавіш, щоб перейти до активних сповіщень."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "Перемкнутися на програму 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "Перемкнутися на програму 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "Перемкнутися на програму 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "Перемкнутися на програму 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "Перемкнутися на програму 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "Перемкнутися на програму 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "Перемкнутися на програму 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "Перемкнутися на програму 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "Перемкнутися на програму 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "Обмежити перемикач на поточний робочий простір."
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Якщо це вказано, то тільки програми з поточного робочого простору будуть "
+"показані в перемикачі. Інакше — програми з усіх просторів."
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "Режим піктограм для програм."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Налаштовує яким чином вікна показуються в перемикачі. Допустимі значення: "
+"«thumbnail-only» (показує мініатюру вікна), «app-icon-only» (показує тільки "
+"піктограми програм) або «both» (обидва)."
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Якщо це вказано, то тільки вікна з поточного робочого простору будуть "
+"показані в перемикачі. Інакше — вікна з усіх просторів."
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "Місцевості"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "Місцевість для світового годинника"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "Автоматичне визначення місцевості"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "Чи одержувати поточну локацію"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "Місцевість"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "Місцевість для прогнозу погоди"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "Приєднати модальне вікно до батьківського вікна"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Цей ключ перевизначає ключ у org.gnome.mutter, коли запущено GNOME Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Увімкнути розбиття країв, коли кладуться вікна на краї екрана"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "Робочі простори організовуються динамічно"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "Робочий простір лише на основному моніторі"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Затримувати зміни фокусу миші, поки вказівник не перестане рухатись"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Мережевий вхід"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Трапилась халепа"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"На жаль, виникла помилка: неможливо показати параметри для розширення. "
+"Радимо, повідомити про ваду автора розширення."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Технічні подробиці"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Сторінка"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Відвідати сторінку розширення"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "Скасувати"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Пароль"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Виберіть сеанс"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "Немає в переліку?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(наприклад, користувач або %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Користувач"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "Вікно входу"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "Помилка автентифікації"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(або проведіть пальцем на зчитувачі)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(або розташуйте палець на зчитувачі)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Вимкнути"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "вимкнути;перезавантажити;зупинити;power off;shutdown;halt;stop"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Перезапустити"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;перезавантаження;перезапуск;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Заблокувати екран"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "заблокувати екран;lock screen"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Вийти"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "вийти;завершити роботу;logout;log out;sign off"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Призупинити"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "призупинити;сон;suspend;sleep"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Змінити користувача"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "змінити користувача;перемкнути;switch user"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"заблокувати орієнтацію;розблокувати орієнтацію;екран;обертання;lock "
+"orientation;unlock orientation;screen;rotation"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Зробити знімок екрана"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;знімок;вікна;вікно;скрінкаст;запис "
+"з екрана;захоплення;запис;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Розблокувати обертання екрана"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Заблокувати обертання екрана"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Команди не знайдено"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Неможливо розібрати команду:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Не вдалось виконати «%s»:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Щойно"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d хвилина тому"
+msgstr[1] "%d хвилини тому"
+msgstr[2] "%d хвилин тому"
+msgstr[3] "%d хвилина тому"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d година тому"
+msgstr[1] "%d години тому"
+msgstr[2] "%d годин тому"
+msgstr[3] "%d година тому"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Учора"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d день тому"
+msgstr[1] "%d дні тому"
+msgstr[2] "%d днів тому"
+msgstr[3] "%d день тому"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d тиждень тому"
+msgstr[1] "%d тижні тому"
+msgstr[2] "%d тижнів тому"
+msgstr[3] "%d тиждень тому"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d місяць тому"
+msgstr[1] "%d місяці тому"
+msgstr[2] "%d місяців тому"
+msgstr[3] "%d місяць тому"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d рік тому"
+msgstr[1] "%d роки тому"
+msgstr[2] "%d років тому"
+msgstr[3] "%d рік тому"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H:%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Учора, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Учора, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "Вхід в точку"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"З'єднання з точкою — небезпечне. Пароль та інша інформація, яку ви вводите "
+"на цій сторінці, може бути перехоплено людьми поруч."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "Заборонити доступ"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "Надати доступ"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "Неназвана тека"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s було пришпилено до панелі приладів."
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s було відшпилено від панелі приладів."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Відкрити вікна"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Нове вікно"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Показати подробиці"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "Вийти"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Відшпилити"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "Пришпилити до панелі приладів"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "Запустити через інтегровану графічну плату"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "Запустити через дискретну графічну плату"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Вибрати звуковий пристрій"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "Параметри звуку"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Навушники"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Гарнітура"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "Мікрофон"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Змінити тло…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "Параметри екрана"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Параметри"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Н"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "В"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "Ч"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "С"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "Попередній місяць"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "Наступний місяць"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "Тиждень %V"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "Немає сповіщень"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "Не турбувати"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "Очистити"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "«%s» не відповідає."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Можете трошки зачекати відновлення активності або примусово закрити програму."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Примусово закрити"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Зачекати"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Зовнішній пристрій під'єднано"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Зовнішній пристрій від'єднано"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Неможливо розблокувати том"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Наявна версія udisks не підтримує параметри PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "Відкрити через %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Також можете під'єднатися, натиснувши кнопку «WPS» на вашому маршрутизаторі."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "З'єднатись"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Ключ"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Пароль до закритого ключа"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Профіль"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Служба"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "Необхідна автентифікація"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Потрібно паролі або зашифровані ключі для доступу до радіомережі «%s»."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "Дротова автентифікація 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Назва мережі"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "Автентифікація DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "Потрібен код PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Потрібен код PIN для мобільних широкосмугових пристроїв"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "Пін-код"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Пароль потрібен для з'єднання з «%s»."
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1954
+msgid "Network Manager"
+msgstr "Керування мережею"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "Пароль VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Необхідна автентифікація"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Адміністратор"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Автентифікувати"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Вибачте, це не спрацювало. Спробуйте ще."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s тепер відомий як %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "Вікна"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Показати програми"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Панель приладів"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %-d %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Сьогодні"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Завтра"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Увесь день"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d.%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Немає подій"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Додати світові годинники…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Світові годинники"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Завантаження…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Увійти в мережу за прогнозом погоди"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Наразі інформації про погоду недоступна"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Погода"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Виберіть місцевість для погоди…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Завершити сеанс %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Завершити сеанс"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s вийде автоматично через %d секунду."
+msgstr[1] "%s вийде автоматично через %d секунди."
+msgstr[2] "%s вийде автоматично через %d секунд."
+msgstr[3] "%s вийде автоматично через %d секунду."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Вихід автоматично через %d секунду."
+msgstr[1] "Вихід автоматично через %d секунди."
+msgstr[2] "Вихід автоматично через %d секунд."
+msgstr[3] "Вихід автоматично через %d секунду."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Вийти"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "Вимкнути"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Установити оновлення і вимкнути"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Система автоматично вимкнеться через %d секунду."
+msgstr[1] "Система автоматично вимкнеться через %d секунди."
+msgstr[2] "Система автоматично вимкнеться через %d секунд."
+msgstr[3] "Система автоматично вимкнеться через %d секунду."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Установити оновлення, які в черзі"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Вимкнути"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Перезапустити"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Установити оновлення і перезапустити"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Система автоматично перезапуститься через %d секунду."
+msgstr[1] "Система автоматично перезапуститься через %d секунди."
+msgstr[2] "Система автоматично перезапуститься через %d секунд."
+msgstr[3] "Система автоматично перезапуститься через %d секунду."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Перезапустити"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Перезапустити і встановити оновлення"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid ""
+"The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Система автоматично перезапуститься та встановить оновлення через %d секунду."
+msgstr[1] ""
+"Система автоматично перезапуститься та встановить оновлення через %d секунди."
+msgstr[2] ""
+"Система автоматично перезапуститься та встановить оновлення через %d секунд."
+msgstr[3] ""
+"Система автоматично перезапуститься та встановить оновлення через %d секунду."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Перезапустити та встановити"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Установити та вимкнути"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Вимкнути після встановлення оновлень"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Перезапустити і встановити оновлення"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s буде встановлено після перезапуску. Оновлення може тривати довго: "
+"переконайтесь, що ви зробили резервні копії та комп'ютер під'єднано до "
+"живлення."
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Низький рівень заряду батареї: під'єднайте до живлення перед установленням "
+"оновлень."
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "Деякі програми зайняті або мають незбережені дані"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "Є інші користувачі з активним сеансом"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Параметри завантаження"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (віддалено)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (консоль)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Встановити"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Встановити розширення"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Звантажити і встановити «%s» з extensions.gnome.org?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "Випущено оновлення розширень"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "Приготовано до встановлення оновлення розширень."
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "Дозволити успадкування скорочень"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Програма %s бажає успадкувати клавіатурні скорочення"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "Програма бажає успадкувати скорочення"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Можна відновити скорочення, натиснувши %s."
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "Заборонити"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "Дозволити"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Повільні клавіші ввімкнено"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Повільні клавіші вимкнено"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Ви затиснули клавішу «Shift» на 8 секунд. Це скорочення для повільних "
+"клавіш, які впливають на роботу вашої клавіатури."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Липкі клавіші ввімкнено"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Липкі клавіші вимкнено"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Ви натиснули клавішу «Shift» 5 раз підряд. Це скорочення для липких клавіш, "
+"які впливають на роботу вашої клавіатури."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Ви натиснули дві клавіші одночасно або клавішу «Shift» 5 раз підряд. Це "
+"вимикає липкі клавіші, які впливають на роботу вашої клавіатури."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Залишити"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "Увімкнути"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "Вимкнути"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Покинути"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "Параметри регіону та мови"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "Не встановлено розширення"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s не випустила жодної помилки. "
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "Сховати помилки"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "Показати помилки"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "Увімкнено"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "Вимкнено"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "Помилка"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "Застаріло"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "Звантаження"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "Переглянути джерело"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "Веб-сторінка"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "Систему переведено у небезпечний режим"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "Тепер програми мають необмежений доступ до даних"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Повернути"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "Увійшов як наділений користувач"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"З міркувань безпеки слід уникати сеансів з наділеними правами. Користуйтесь "
+"звичайним сеансом."
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "Блокування екрана вимкнено"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "Блокування екрана потребує керування входом GNOME."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Інформація про систему"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "Невідомий виконавець"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "Невідома назва"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "Введіть текст для пошуку"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "Програми"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Огляд"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "Нове скорочення…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "Програму визначено"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "Показати екранну довідку"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "Перемкнути монітор"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "Призначити клавішу"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "Зроблено"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "Редагувати…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "Немає"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "Натисніть кнопку, щоб налаштувати"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "Натисніть клавішу «Esc», щоб вийти"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "Натисніть будь-яку клавішу, щоб вийти"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "Діяльність"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Система"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "Верхня панель"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Виконати команду"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Натисніть клавішу «Esc», щоб закрити"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Перезапускання недоступне в сеансі Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Перезапускання…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME потребує заблокувати екран"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "Неможливо заблокувати"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "Блокування заборонено програмою"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "Вибір"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "Вибір ділянки"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "Екран"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "Вибір екрана"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "Вікно"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "Вибір вікна"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "Знімок / Запис відео з екрана"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "Показувати вказівник"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "Трансляції з екрана"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Запис з %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "Знімок екрана"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "Записано відео з екрана"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "Натисніть тут, щоб переглянути відео."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "Показувати у «Файлах»"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Знімок екрана з %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "Знімок зроблено"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "Ви можете вставити зображення з буфера обміну даними."
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "Знімок зроблено"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "Пошуки…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "Безрезультатно."
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "ще %d"
+msgstr[1] "ще %d"
+msgstr[2] "ще %d"
+msgstr[3] "ще %d"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Пошук"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Копіювати"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Вставити"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Показати текст"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Сховати текст"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps lock активовано."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Прихований том"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Том системи Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Використовує зашифровані файли"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "Використайте <i>%s</i>, щоб розблокувати том."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Номер PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Запам'ятати пароль"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Розблокувати"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Відкрити %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM має бути числом або порожній."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Неможливо запустити %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Неможливо знайти програму %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Доступність"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Висока контрастність"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Масштаб"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Читання з екрану"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Екранна клавіатура"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Візуальні тривоги"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Липкі клавіші"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Повільні клавіші"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Пружні клавіші"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Клавіші миші"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Більший текст"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "Автоматичне обертання"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "Яскравість"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "Темний режим"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Одинарний клац"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Подвійний клац"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Перетягування"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Правий клац"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Затриманий клац"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "Клавіатура"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "Показати розкладку клавіатури"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "Дозволити доступ до даних перебування"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Програмі %s потрібен доступ до даних щодо місця вашого перебування"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Доступ до місця перебування можна змінити в будь-яку мить у параметрах "
+"конфіденційності."
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<невідомо>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "Від'єднатися від %s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "З'єднатися з %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "Точка доступу %s"
+
+#: js/ui/status/network.js:1466 js/ui/status/network.js:1482
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1467
+msgid "VPN Settings"
+msgstr "Параметри VPN"
+
+#: js/ui/status/network.js:1716
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1718
+#| msgid "No Networks"
+msgid "All Networks"
+msgstr "Всі мережі"
+
+#: js/ui/status/network.js:1815
+#| msgid "%s Wired Connection"
+#| msgid_plural "%s Wired Connections"
+msgid "Wired Connections"
+msgstr "Дротові з'єднання"
+
+#: js/ui/status/network.js:1816
+msgid "Wired Settings"
+msgstr "Параметри мережі"
+
+#: js/ui/status/network.js:1830
+#| msgid "Bluetooth Settings"
+msgid "Bluetooth Tethers"
+msgstr "Bluetooth-прив'язки"
+
+#: js/ui/status/network.js:1831
+msgid "Bluetooth Settings"
+msgstr "Параметри Bluetooth"
+
+#: js/ui/status/network.js:1845
+#| msgid "%s Modem Connection"
+#| msgid_plural "%s Modem Connections"
+msgid "Mobile Connections"
+msgstr "Мобільні з'єднання"
+
+#: js/ui/status/network.js:1847
+msgid "Mobile Broadband Settings"
+msgstr "параметри мобільної радіомережі"
+
+#: js/ui/status/network.js:1959
+msgid "Connection failed"
+msgstr "Не вдалось з'єднатись"
+
+#: js/ui/status/network.js:1960
+msgid "Activation of network connection failed"
+msgstr "Не вдалось увімкнути мережеве з'єднання"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "Нічне світло"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Швидкодія"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Баланс"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Заощадження"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "Профілі живлення"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "Припинити трансляцію з екрана"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "Припинити спільне використання екрана"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "Режим польоту"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Зробити знімок"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "Меню вимикання"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "Призупинити"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "Перезапуск…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "Вимкнути…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Вийти…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "Змінити користувача…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "Заблокувати екран"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "Невідомий пристрій Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Виявлено новий пристрій протягом вашої відсутності. Будь ласка, "
+"перепід'єднайте пристрій для його використання."
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "Незавірений пристрій Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Виявлено новий пристрій, який потребує схвалення від адміністратора."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Помилка завірення Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Неможливо завірити пристрій Thunderbolt: %s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "Гучність змінено"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "Гучність"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "Виведення звуку"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "Введення звуку"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Віддзеркалення"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Приєднання екранів"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Тільки зовнішній"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Тільки вбудований"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%A, %-d %B"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "Проведіть вгору для розблокування"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "Клацніть або натисніть клавішу для розблокування"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Розблокувати вікно"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Увійти як інший користувач"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Вітаємо у GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr ""
+"Якщо хочете дізнатися більше про середовище, ознайомтеся із нашим туром."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Ні, дякую"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Здійснити тур"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "«%s» готовий"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Зберегти ці параметри екрана?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Повернути параметри"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Зберегти зміни"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Зміни параметрів буде повернуто через %d секунду"
+msgstr[1] "Зміни параметрів буде повернуто через %d секунди"
+msgstr[2] "Зміни параметрів буде повернуто через %d секунд"
+msgstr[3] "Зміни параметрів буде повернуто через %d секунду"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Приховати"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Відновити"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Розгорнути"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Перемістити"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Змінити розмір"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Перемістити заголовок на екран"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Завжди зверху"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Завжди на видимому робочому просторі"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Перемістити у робочий простір ліворуч"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Перемістити у робочий простір праворуч"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Перемістити у робочий простір зверху"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Перемістити у робочу область знизу"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Перемістити до монітору зверху"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Перемістити до монітору знизу"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Перемістити до монітору ліворуч"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Перемістити до монітору праворуч"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Закрити"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Календар Evolution"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Показати версію"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "Використовуваний режим GDM для екрану входу"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Використовувати вказаний режим, приклад, «gdm» для вікна входу"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "Перелік можливих режимів"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Невідомо"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Не вдалося запустити «%s»"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Пароль не збігається."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Пароль не може бути порожнім"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Вікно автентифікації відхилено користувачем"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Розширення"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "Керуйте вашими розширеннями GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "Проєкт GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"«Розширення» GNOME обробляють оновлення розширень, налаштовування параметрів "
+"розширень, а також вилучення та вимикання небажаних розширень."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Налаштувати розширення GNOME Shell"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "Немає відповідників"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Вилучити «%s»?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Якщо ви вилучите розширення, вам доведеться отримати його код знову, якщо ви "
+"захочете його знову увімкнути"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "Вилучити"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Максим Дзюманенко <dziumanenko@gmail.com>\n"
+"Данило Коростіль <ted.korostiled@gmail.com>\n"
+"Юрій Чорноіван <yurchor@ukr.net>"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "Під час наступного входу до системи буде оновлено %d розширення."
+msgstr[1] "Під час наступного входу до системи буде оновлено %d розширення."
+msgstr[2] "Під час наступного входу до системи буде оновлено %d розширень."
+msgstr[3] "Під час наступного входу до системи буде оновлено одне розширення."
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Розширення є несумісним із поточною версією GNOME"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "У розширенні сталася помилка"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Розширення може бути оновлено"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Сайт"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Вилучити…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Довідка"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Про розширення"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Розширення можуть спричиняти проблеми зі швидкодією і стабільністю. Вимкніть "
+"розширення, якщо у вас виникнуть проблеми із роботою системи."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Встановлені вручну"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"Щоб знайти і додати потрібні вам розширення, відвідайте <a href=\"https://"
+"extensions.gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Вбудовані"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Розширення, які не встановлено"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"На жаль, неможливо одержати перелік установлених розширень. Переконайтесь, "
+"що ви увійшли в GNOME і спробуйте ще раз."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Готові оновлення розширень"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Нове розширення успішно створено в %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Назва повинна бути дуже короткою (бажано максимально описовою).\n"
+"Наприклад: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Назва"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Опис — пояснення одним реченням того, що розширення робить.\n"
+"Наприклад: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Опис"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID — глобальний унікальний код вашого розширення.\n"
+"Він зберігається у форматі електронної адреси (clicktofocus@janedoe.example."
+"com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Виберіть один із доступних шаблонів:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Шаблон"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Унікальний код розширення"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "НАЗВА"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Загальна назва розширення"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "ОПИС"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Короткий опис того, що розширення робить"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "ШАБЛОН"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Шаблон, яким слід скористатися для нового розширення"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Ввести інформацію про розширення покроково"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Створити розширення"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Невідомі параметри"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, назва і опис — обов'язкові"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Розширення «%s» не існує\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "Вимкнути розширення"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Не надано UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Надано понад один UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "Увімкнути розширення"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Не вдалося з'єднатися із GNOME Shell\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Розширення «%s» не існує\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Показати відомості про розширення"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Перезаписати наявне розширення"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "КОМПЛЕКТ_РОЗШИРЕНЬ"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Встановити комплект розширень"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Не вказано жодного комплекту розширень"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Вказано більше за один комплект розширень"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Показати користувацькі розширення"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Показати системні розширення"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Показати увімкнені розширення"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Показати вимкнені розширення"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Показати розширення з параметрами"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Показувати оновлення розширень"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "Показати відомості про розширення"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Перелічити встановлені розширення"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "ФАЙЛ"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Додаткове джерело для комплекту"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "СХЕМА"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Схема GSettings"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "КАТАЛОГ"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Каталог, де зберігаються переклади"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "ПЕРЕКЛАД"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Спосіб як працювати з перекладами"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Перезаписати наявний пакунок"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Каталог, де створюється пакунок"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "КАТАЛОГ_ДЖЕРЕЛА"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Створити комплект розширень"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Вказано понад один каталог джерела"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "У розширення «%s» немає налаштувань\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Не вдалося відкрити налаштування для розширення «%s»: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Відкриває параметри розширення"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Перезапустити розширення"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Не можна вилучати загальносистемні розширення\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Не вдалося вилучити «%s»\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Вилучити розширення"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Не виводити повідомлення про помилки"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Шлях"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "Посилання"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Засновник"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Версія"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Стан"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "«version» не потребує параметрів"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Використання:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Вивести версію та вийти."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "Команда"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ПАРАМЕТРИ…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Команди:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "Вивести версію"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Увімкнути розширення"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Вимкнути розширення"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Перезапустити розширення"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Вилучити розширення"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Перелічити розширення"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Показати відомості про розширення"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Відкрити параметри розширення"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Створити розширення"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Запакувати розширення"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Встановити комплект розширень"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Натисніть «%s», щоб показати докладну довідку.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Просте"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Порожнє розширення"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Індикатор"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Додає піктограму на верхню панель"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u вивід"
+msgstr[1] "%u виводи"
+msgstr[2] "%u виводів"
+msgstr[3] "%u вивід"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u вхід"
+msgstr[1] "%u входи"
+msgstr[2] "%u входів"
+msgstr[3] "%u вхід"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "Системні звуки"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "Чи типовий адаптер Bluetooth зв'язаний з якимось пристроєм"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Оболонка покаже меню Bluetooth лише у випадку, коли адаптер Bluetooth "
+#~ "увімкнено або пристрій зв'язано з типовим адаптером. Якщо типовий адаптер "
+#~ "ніколи не був зв'язаний з жодним пристроєм, то його буде скинуто."
+
+#~ msgid "Log Out"
+#~ msgstr "Вийти"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "Під'єднатись до інтернету"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "Режим «у літаку» ввімкнено"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "Wi-Fi вимкнено, коли режим «у літаку» ввімкнено."
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "Вимкнути режим «у літаку»"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi вимкнено"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "Wi-Fi потрібно ввімкнути, щоб з'єднатись з мережею."
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "Увімкнути Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Мережі Wi-Fi"
+
+#~ msgid "Select a network"
+#~ msgstr "Вибрати мережу"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "Натисніть апаратну кнопку, щоб вимкнути"
+
+#~ msgid "Select Network"
+#~ msgstr "Виберіть мережу"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Параметри Wi-Fi"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN вимкнено"
+
+#~ msgid "Network Settings"
+#~ msgstr "Налаштування мережі"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s з'єднання через Wi-Fi"
+#~ msgstr[1] "%s з'єднання через Wi-Fi"
+#~ msgstr[2] "%s з'єднань через Wi-Fi"
+#~ msgstr[3] "%s з'єднання через Wi-Fi"
+
+#, javascript-format
+#~| msgid "%s Wired Connection"
+#~| msgid_plural "%s Wired Connections"
+#~ msgid "%s Bluetooth Connection"
+#~ msgid_plural "%s Bluetooth Connections"
+#~ msgstr[0] "%s з'єднання Bluetooth"
+#~ msgstr[1] "%s з'єднання Bluetooth"
+#~ msgstr[2] "%s з'єднань Bluetooth"
+#~ msgstr[3] "%s з'єднання Bluetooth"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s вимкнено"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s під'єднано"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s нескеровано"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s під'єднується"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s вимагає автентифікації"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "Бракує мікропрограми для %s"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s недоступний"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s під'єднано невдало"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s вимкнено апаратно"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s вимкнено"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s не під'єднано"
+
+#~ msgid "unknown"
+#~ msgstr "невідомий"
+
+#~ msgid "activating…"
+#~ msgstr "вмикаємо…"
+
+#~ msgid "deactivating…"
+#~ msgstr "вимикаємо…"
+
+#~ msgid "deactivated"
+#~ msgstr "вимкнено"
+
+#~ msgid "connecting…"
+#~ msgstr "з'єднання…"
+
+#~ msgid "authentication required"
+#~ msgstr "необхідна автентифікація"
+
+#~ msgid "connection failed"
+#~ msgstr "не вдалось з'єднатись"
+
+#~ msgid "Power Settings"
+#~ msgstr "Енергетичні параметри"
+
+#~ msgid "Lock"
+#~ msgstr "Заблокувати"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "Вимкнути / Вийти"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d з'єднаний"
+#~ msgstr[1] "%d з'єднаних"
+#~ msgstr[2] "%d з'єднаних"
+#~ msgstr[3] "%d з'єднаний"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "Bluetooth увімкнено"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "Bluetooth вимкнено"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "Нічне світло вимкнено"
+
+#~ msgid "Resume"
+#~ msgstr "Повторити"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "Вимкнути до завтра"
+
+#~ msgid "Fully Charged"
+#~ msgstr "Повністю заряджено"
+
+#~ msgid "Not Charging"
+#~ msgstr "Не заряджається"
+
+#~ msgid "Estimating…"
+#~ msgstr "Оцінювання…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "Залишилось %d∶%02d (%d %%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "Зарядиться через %d∶%02d (%d %%)"
+
+#~ msgid "Location Enabled"
+#~ msgstr "Місцевість залучено"
+
+#~ msgid "Disable"
+#~ msgstr "Вимкнути"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "Параметри конфіденційності"
+
+#~ msgid "Location In Use"
+#~ msgstr "Місцевість використовується"
+
+#~ msgid "Location Disabled"
+#~ msgstr "Місцевість вимкнено"
+
+#~ msgid "Enable"
+#~ msgstr "Увімкнути"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "Екран у спільному доступі"
+
+#~ msgid "Turn off"
+#~ msgstr "Вимкнути"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "Показати інтерфейс знімків екрана"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "Виконуємо запис з екрана"
+
+#~ msgid "Stop"
+#~ msgstr "Припинити"
+
+#~ msgid "Author"
+#~ msgstr "Автор"
+
+#~ msgid "Warning"
+#~ msgstr "Попередження"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Робота розширень може призвести до проблем у системі, зокрема проблем із "
+#~ "швидкодією. Якщо ви зіткнулися із проблемами у системі, рекомендуємо "
+#~ "вимкнути усі розширення."
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Вилучити з улюбленого"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Додати до улюбленого"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Увімкнути API для самоаналізу"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr "Вмикає D-Bus API для самоаналізу стану оболонки."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Не вдалося з'єднатися із GNOME Shell"
+
+#~ msgid "Minimize"
+#~ msgstr "Згорнути"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Відновити розгорнуте"
+
+#~ msgid "App Picker View"
+#~ msgstr "Огляд вибору програми"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "Індекс поточного розміщення вибору в огляді списку програм."
diff --git a/po/uz@cyrillic.po b/po/uz@cyrillic.po
new file mode 100644
index 0000000..b4b5db7
--- /dev/null
+++ b/po/uz@cyrillic.po
@@ -0,0 +1,2064 @@
+# Uzbek (Cyrillic) translation for gnome-shell.
+# Copyright (C) 2014 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Bahodir Mansurov <6ahodir@gmail.com>, 2014.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2016-09-07 09:41+0000\n"
+"PO-Revision-Date: 2016-09-25 09:14-0400\n"
+"Last-Translator: Bahodir Mansurov <6ahodir@gmail.com>\n"
+"Language-Team: Uzbek (Cyrillic) <>\n"
+"Language: uz@cyrillic\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-DamnedLies-Scope: partial\n"
+"X-Generator: Poedit 1.8.9\n"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Система"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Хабарнома рўйхатини кўрсатиш"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Фаол хабарномага диққат қилиш"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Обзорни кўрсатиш"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Барча дастурларни кўрсатиш"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Дастур менюсини очиш"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:4
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME Shell кенгайтма мосламалари"
+
+#: data/gnome-shell-extension-prefs.desktop.in.in:5
+msgid "Configure GNOME Shell Extensions"
+msgstr "GNOME Shell кенгайтмаларини созлаш"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Ойналарни бошқариш ва дастурларни очиш"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Alt-F2дан тузувчилар ва текширувчилар учун фойдали ички асбобларни ёқиш"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Alt-F2 диалог ойнаси орқали ички созлаш ва кузатиш асбобларига рухсат бериш"
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Ёқиладиган кенгайтма UUIDлари"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell кенгайтмаларининг UUID хоссаси бор; бу калит юкланиши керак "
+"бўлган кенгайтмаларни рўйхатда кўрсатади. Юкланишни истайдиган кенгайтма "
+"ушбу рўйхатда бўлиши керак. Сиз бу рўйхатни org.gnome.Shellдаги D-Busнинг "
+"EnableExtension ва DisableExtension функциялари орақли ҳам бошқаришингиз "
+"мумкин."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "Disables the validation of extension version compatibility"
+msgstr "Кенгайтма версияси мослигини текширишни ўчириб қўяди"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell ушбу ишлаётган версияни қўллашини билдирган кенгайтмаларнигина "
+"юклайди. Ушбу танловни белгиласангиз бу текширув ўчириб қўйилади ва, қайси "
+"версияни қўллашидан қатъий назар, барча кенгайтмалар юкланади."
+
+#: data/org.gnome.shell.gschema.xml.in:35
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Танланган дастурлар учун иш столи файл IDлари рўйхати"
+
+#: data/org.gnome.shell.gschema.xml.in:36
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Ушбу идентификаторга мос келувчи дастурлар танланганлар майдонида "
+"кўрсатилади."
+
+#: data/org.gnome.shell.gschema.xml.in:43
+msgid "App Picker View"
+msgstr "Дастур танлагич кўриниши"
+
+#: data/org.gnome.shell.gschema.xml.in:44
+msgid "Index of the currently selected view in the application picker."
+msgstr "Дастур танлагичдаги белгиланган кўриниш индекси."
+
+#: data/org.gnome.shell.gschema.xml.in:50
+msgid "History for command (Alt-F2) dialog"
+msgstr "(Alt-F2) диалог ойначаси буйруқлар тарихи"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid "History for the looking glass dialog"
+msgstr "Шаффоф диалог ойнаси кўришлар тарихи"
+
+#: data/org.gnome.shell.gschema.xml.in:59
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "Фойдаланувчи менюсида \"Сеансни якунлаш\" менюсини ҳар доим кўрсатиш."
+
+#: data/org.gnome.shell.gschema.xml.in:60
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Ушбу калит битта фойдаланувчили системада \"Сеансни якунлаш\" менюсини "
+"автоматик яширишни белгилаб беради."
+
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Шифрланган ёки масофавий файл системаларини улаганда махфий сўзни эслаб "
+"қолиш ёки йўқлиги"
+
+#: data/org.gnome.shell.gschema.xml.in:68
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"GNOME Shell шифрланган ёки масофавий файл системаларини улаганда махфий "
+"сўзни сўрайди. Агар махфий сўз келажакда ишлатиш учун сақлаб бўлинса "
+"\"Махфий сўзни эслаб қолиш\" белгилаш катакчаси кўрсатилади. Ушбу калит ўша "
+"белгилаш катакчасининг сукут ҳолатини ўрнатади."
+
+#: data/org.gnome.shell.gschema.xml.in:77
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr "Bluetooth адаптери сукут бўйича бирор ускунага уланганми"
+
+#: data/org.gnome.shell.gschema.xml.in:78
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Bluetooth адаптери ёниқ ёки унга ускуналар боғлаб қўйилган бўлса Bluetooth "
+"менюси кўринади. Агар сукут бўйича адаптерга уланган ускуналар бўлмаса, ушбу "
+"параметр ташлаб юборилади."
+
+#: data/org.gnome.shell.gschema.xml.in:93
+msgid "Keybinding to open the application menu"
+msgstr "Дастур менюсини очиш учун клавишалар бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:94
+msgid "Keybinding to open the application menu."
+msgstr "Дастур менюсини очиш учун клавишалар бирикмаси."
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "\"Дастурларни кўрсатиш\" кўринишини очиш учун клавишалар бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:101
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr ""
+"Фаолиятлар обзорида \"Дастурларни кўрсатиш\"кўриниши учун клавишалар "
+"бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:108
+msgid "Keybinding to open the overview"
+msgstr "Обзорни очиш учун клавишалар бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:109
+msgid "Keybinding to open the Activities Overview."
+msgstr "Фаолиятлар обзорини очиш учун клавишалар бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:115
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Хабарнома рўйхати очиб-ёпиш учун клавишалар бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:116
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Хабарнома рўйхати очиб-ёпиш учун клавишалар бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:122
+msgid "Keybinding to focus the active notification"
+msgstr "Фаол хабарномага диққат қилиш учун клавишалар бирикмаси"
+
+#: data/org.gnome.shell.gschema.xml.in:123
+msgid "Keybinding to focus the active notification."
+msgstr "Фаол хабарномага диққат қилиш учун клавишалар бирикмаси."
+
+#: data/org.gnome.shell.gschema.xml.in:129
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr ""
+"Барча ишлаб турган анимацияларни тўхтатиб туриш ёки давом эттириш учун "
+"клавишалар бирикмаси (созлаш мақсадиди)"
+
+#: data/org.gnome.shell.gschema.xml.in:138
+msgid "Which keyboard to use"
+msgstr "Қайси клавиатурани ишлатиш"
+
+#: data/org.gnome.shell.gschema.xml.in:139
+msgid "The type of keyboard to use."
+msgstr "Ишлатиладиган клавиатура тури."
+
+#: data/org.gnome.shell.gschema.xml.in:150
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Limit switcher to current workspace."
+msgstr "Переключателни фақат шу иш жойи билан чегаралаш."
+
+#: data/org.gnome.shell.gschema.xml.in:151
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Агар белгиланса, ушбу иш жойида ойнаси бор барча дастурлар ўзгартирувчи "
+"тугмачада кўринади. Акс ҳолда, барча дастурлар ҳисобга олинади."
+
+#: data/org.gnome.shell.gschema.xml.in:168
+msgid "The application icon mode."
+msgstr "Дастур нишончаси режими."
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"Ўзгартирувчи тугмачада ойналар қандай кўринишини мослайди. Мавжуд қийматлар: "
+"\"thumbnail-only\" (ойнанинг миниатурасини кўрсатади), \"app-icon-only"
+"\" (фақтгини дастур нишончасини кўрсатади)."
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Агар белгиланса, фақатгина ушбу иш жойидаги ойналар переключателда "
+"кўрсатилади. Акс ҳолда,барча ойналар ҳисобга олинади."
+
+#: data/org.gnome.shell.gschema.xml.in:189
+msgid "Attach modal dialog to the parent window"
+msgstr "Модал диалог ойнани бош ойнасига маҳкамлаш"
+
+#: data/org.gnome.shell.gschema.xml.in:190
+#: data/org.gnome.shell.gschema.xml.in:199
+#: data/org.gnome.shell.gschema.xml.in:207
+#: data/org.gnome.shell.gschema.xml.in:215
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"GNOME Shell ишга тушганида ушбу калит org.gnome.mutterдаги калитни бошқадан "
+"белгилайди."
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr ""
+"Ойналар экран четларига олиб борилганда уларнинг ўлчамларини автоматик "
+"равишда ўзгартириш"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Workspaces are managed dynamically"
+msgstr "Иш жойлари фаол равишда бошқарилади"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Workspaces only on primary monitor"
+msgstr "Иш жойлари фақат асосий мониторда"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr ""
+"Кўрсаткич юришдан тўхтамагунча сичқонча режимида диққатни ўзгратирмаслик"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Тармоққа кириш"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:9
+msgid "network-workgroup"
+msgstr "network-workgroup"
+
+#: js/extensionPrefs/main.js:117
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "%s учун параметрлар диалог ойнасини юклашда хатолик содир бўлди:"
+
+#: js/extensionPrefs/main.js:149
+msgid "GNOME Shell Extensions"
+msgstr "GNOME Shell кенгайтмалари"
+
+#: js/gdm/authPrompt.js:147 js/ui/audioDeviceSelection.js:71
+#: js/ui/components/networkAgent.js:145 js/ui/components/polkitAgent.js:179
+#: js/ui/endSessionDialog.js:483 js/ui/extensionDownloader.js:195
+#: js/ui/shellMountOperation.js:399 js/ui/status/network.js:916
+msgid "Cancel"
+msgstr "Бекор қилиш"
+
+#: js/gdm/authPrompt.js:169 js/gdm/authPrompt.js:216 js/gdm/authPrompt.js:448
+msgid "Next"
+msgstr "Кейинги"
+
+#: js/gdm/authPrompt.js:212 js/ui/shellMountOperation.js:403
+#: js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "Қулфни ечиш"
+
+#: js/gdm/authPrompt.js:214
+msgctxt "button"
+msgid "Sign In"
+msgstr "Кириш"
+
+#: js/gdm/loginDialog.js:285
+msgid "Choose Session"
+msgstr "Сеансни танлаш"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: js/gdm/loginDialog.js:435
+msgid "Not listed?"
+msgstr "Рўйхатда йўқми?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:854
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(мас. фойдаланувчи ёки %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: js/gdm/loginDialog.js:859 js/ui/components/networkAgent.js:271
+#: js/ui/components/networkAgent.js:289
+msgid "Username: "
+msgstr "Фойдаланувчи:"
+
+#: js/gdm/loginDialog.js:1196
+msgid "Login Window"
+msgstr "Кириш Ойнаси"
+
+#: js/gdm/util.js:341
+msgid "Authentication error"
+msgstr "Хақиқийлигини текширишда хатолик"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr "(ёки ушбу мослама юзасида бармоғингизни юритинг)"
+
+#: js/misc/util.js:119
+msgid "Command not found"
+msgstr "Буйруқ топилмади"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:152
+msgid "Could not parse command:"
+msgstr "Буйруқни таҳлил қилиб бўлмади:"
+
+#: js/misc/util.js:160
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "\"%s\"ни бажариб бўлмади:"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:191
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:197
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "Кеча, %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:203
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A, %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:209
+#, no-c-format
+msgid "%B %d, %H∶%M"
+msgstr "%B %d, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:215
+#, no-c-format
+msgid "%B %d %Y, %H∶%M"
+msgstr "%B %d %Y, %H∶%M"
+
+#. Translators: Time in 12h format
+#: js/misc/util.js:220
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:226
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "Кеча, %l∶%M %p"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:232
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A, %l∶%M %p"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:238
+#, no-c-format
+msgid "%B %d, %l∶%M %p"
+msgstr "%B %d, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:244
+#, no-c-format
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%B %d %Y, %l∶%M %p"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login
+#. * window, until we know the title of the actual login page
+#: js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "Ҳақиқийлигини текшириш учун қайта жўнатиш"
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:62 js/ui/status/location.js:426
+msgid "Deny Access"
+msgstr "Рухсат бермаслик"
+
+#: js/ui/accessDialog.js:63 js/ui/status/location.js:429
+msgid "Grant Access"
+msgstr "Рухсат бериш"
+
+#: js/ui/appDisplay.js:794
+msgid "Frequently used applications will appear here"
+msgstr "Тез-тез ишлатиладиган дастурлар шу ерда пайдо бўлади"
+
+#: js/ui/appDisplay.js:914
+msgid "Frequent"
+msgstr "Тез-тез ишлатиладиган"
+
+#: js/ui/appDisplay.js:921
+msgid "All"
+msgstr "Барча"
+
+#: js/ui/appDisplay.js:1853
+msgid "New Window"
+msgstr "Янги ойна"
+
+#: js/ui/appDisplay.js:1881 js/ui/dash.js:289
+msgid "Remove from Favorites"
+msgstr "Ёқтирганлар рўйхатидан ўчириш"
+
+#: js/ui/appDisplay.js:1887
+msgid "Add to Favorites"
+msgstr "Ёқтирганлар рўйхатига қўшиш"
+
+#: js/ui/appDisplay.js:1897
+msgid "Show Details"
+msgstr "Тафсилотларни кўрсатиш"
+
+#: js/ui/appFavorites.js:134
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "\"%s\" танланганлар рўйхатига қўшилди."
+
+#: js/ui/appFavorites.js:168
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "\"%s\" танланганлар рўйхатидан олиб ташланди."
+
+#: js/ui/audioDeviceSelection.js:59
+msgid "Select Audio Device"
+msgstr "Аудио ускунани танлаш"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Sound Settings"
+msgstr "Товуш мосламалари"
+
+#: js/ui/audioDeviceSelection.js:78
+msgid "Headphones"
+msgstr "Наушниклар"
+
+#: js/ui/audioDeviceSelection.js:80
+msgid "Headset"
+msgstr "Наушник"
+
+#: js/ui/audioDeviceSelection.js:82 js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "Микрофон"
+
+#: js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "Фонни ўзгартириш..."
+
+#: js/ui/backgroundMenu.js:21
+msgid "Display Settings"
+msgstr "Дисплей мосламалари"
+
+#: js/ui/backgroundMenu.js:22 js/ui/status/system.js:374
+msgid "Settings"
+msgstr "Мосламалар"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:47
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:76
+msgctxt "grid sunday"
+msgid "S"
+msgstr "Я"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:78
+msgctxt "grid monday"
+msgid "M"
+msgstr "Д"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:80
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "С"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:82
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "Ч"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:84
+msgctxt "grid thursday"
+msgid "T"
+msgstr "П"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:86
+msgctxt "grid friday"
+msgid "F"
+msgstr "Ж"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:88
+msgctxt "grid saturday"
+msgid "S"
+msgstr "Ш"
+
+#: js/ui/calendar.js:442
+msgid "Previous month"
+msgstr "Ўтган ой"
+
+#: js/ui/calendar.js:452
+msgid "Next month"
+msgstr "Келаси ой"
+
+#: js/ui/calendar.js:605
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:660
+msgid "Week %V"
+msgstr "Ҳафта %V"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/calendar.js:721
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Куни бўйи"
+
+#: js/ui/calendar.js:836
+msgid "Events"
+msgstr "Тадбирлар"
+
+#: js/ui/calendar.js:845
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: js/ui/calendar.js:849
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%A, %B %d, %Y"
+
+#: js/ui/calendar.js:931
+msgid "Notifications"
+msgstr "Хабарнома"
+
+#: js/ui/calendar.js:1082
+msgid "No Notifications"
+msgstr "Хабарлар йўқ"
+
+#: js/ui/calendar.js:1085
+msgid "No Events"
+msgstr "Тадбир йўқ"
+
+#: js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "Ташқи диск уланган"
+
+#: js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "Ташқи диск узилган"
+
+#: js/ui/components/autorunManager.js:355
+#, javascript-format
+msgid "Open with %s"
+msgstr "%s билан очиш"
+
+#: js/ui/components/keyring.js:120 js/ui/components/polkitAgent.js:315
+msgid "Password:"
+msgstr "Махфий сўз:"
+
+#: js/ui/components/keyring.js:153
+msgid "Type again:"
+msgstr "Яна ёзинг:"
+
+#: js/ui/components/networkAgent.js:140 js/ui/status/network.js:269
+#: js/ui/status/network.js:352 js/ui/status/network.js:919
+msgid "Connect"
+msgstr "Уланиш"
+
+#. Cisco LEAP
+#: js/ui/components/networkAgent.js:233 js/ui/components/networkAgent.js:245
+#: js/ui/components/networkAgent.js:273 js/ui/components/networkAgent.js:293
+#: js/ui/components/networkAgent.js:303
+msgid "Password: "
+msgstr "Махфий сўз:"
+
+#. static WEP
+#: js/ui/components/networkAgent.js:238
+msgid "Key: "
+msgstr "Калит:"
+
+#: js/ui/components/networkAgent.js:277
+msgid "Identity: "
+msgstr "Шахсият:"
+
+#: js/ui/components/networkAgent.js:279
+msgid "Private key password: "
+msgstr "Шахсий калитнинг махфий сўзи:"
+
+#: js/ui/components/networkAgent.js:291
+msgid "Service: "
+msgstr "Хизмат:"
+
+#: js/ui/components/networkAgent.js:320 js/ui/components/networkAgent.js:658
+msgid "Authentication required by wireless network"
+msgstr "Симсиз тармоқ ҳақиқийликни текширишни талаб қиляпти"
+
+#: js/ui/components/networkAgent.js:321 js/ui/components/networkAgent.js:659
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr ""
+"Симсиз \"%s\" тармоққа уланиш учун махфий сўзлар ёки шифрлаш калитлари талаб "
+"қилинади."
+
+#: js/ui/components/networkAgent.js:325 js/ui/components/networkAgent.js:662
+msgid "Wired 802.1X authentication"
+msgstr "Симли 802.1X алоқанинг ҳақиқийлигини текшириш"
+
+#: js/ui/components/networkAgent.js:327
+msgid "Network name: "
+msgstr "Тармоқ номи:"
+
+#: js/ui/components/networkAgent.js:332 js/ui/components/networkAgent.js:666
+msgid "DSL authentication"
+msgstr "DSL ҳақиқийлигини текшириш"
+
+#: js/ui/components/networkAgent.js:339 js/ui/components/networkAgent.js:672
+msgid "PIN code required"
+msgstr "PIN код талаб қилинади"
+
+#: js/ui/components/networkAgent.js:340 js/ui/components/networkAgent.js:673
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Катта йўлли мобил алоқа ускунаси уччун PIN код керак бўлади"
+
+#: js/ui/components/networkAgent.js:341
+msgid "PIN: "
+msgstr "PIN:"
+
+#: js/ui/components/networkAgent.js:348 js/ui/components/networkAgent.js:679
+msgid "Mobile broadband network password"
+msgstr "Кенг йўлли мобил алоқа учун махфий сўз"
+
+#: js/ui/components/networkAgent.js:349 js/ui/components/networkAgent.js:663
+#: js/ui/components/networkAgent.js:667 js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "\"%s\"га уланиш учун махфий сўз талаб қилинади."
+
+#: js/ui/components/networkAgent.js:647 js/ui/status/network.js:1658
+msgid "Network Manager"
+msgstr "Тармоқ бошқарувчиси"
+
+#: js/ui/components/polkitAgent.js:60
+msgid "Authentication Required"
+msgstr "Ҳақиқийлигини текшириш талаб қилинади"
+
+#: js/ui/components/polkitAgent.js:102
+msgid "Administrator"
+msgstr "Маъмур"
+
+#: js/ui/components/polkitAgent.js:182
+msgid "Authenticate"
+msgstr "Ҳақиқийлигини текшириш"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:301 js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "Узр, ишламади. Яна ҳаракат қилиб кўринг."
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: js/ui/components/telepathyClient.js:760
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s энди %s сифатида маълум"
+
+#: js/ui/ctrlAltTab.js:29 js/ui/viewSelector.js:155
+msgid "Windows"
+msgstr "Ойналар"
+
+#: js/ui/dash.js:250 js/ui/dash.js:291
+msgid "Show Applications"
+msgstr "Дастурларни кўрсатиш"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:449
+msgid "Dash"
+msgstr "Дастурлар панели"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: js/ui/dateMenu.js:73
+msgid "%B %e %Y"
+msgstr "%B %e %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:80
+msgid "%A %B %e %Y"
+msgstr "%A, %B %e, %Y"
+
+#: js/ui/dateMenu.js:160
+msgid "Add world clocks…"
+msgstr "Дунё соатларини қўшиш..."
+
+#: js/ui/dateMenu.js:161
+msgid "World Clocks"
+msgstr "Дунё соатлари"
+
+#: js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "%s фойдаланувчи сеансини якунлаш"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "Сеансни якунлаш"
+
+#: js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%sнинг сеанси %d сониядан сўнг автоматик равишда тугатилади."
+
+#: js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Сизнинг сеансинги %d сониядан сўнг автоматик равишда тугатилади."
+
+#: js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "Сеансни якунлаш"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "Ўчириш"
+
+#: js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Янгиликларни ўрнатиш ва компьютерни ўчириш"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Система %d сониядан сўнг автоматик равишда ўчади."
+
+#: js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Навбатдаги дастурий таъминот янгиликларини ўрнатиш"
+
+#: js/ui/endSessionDialog.js:94 js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "Ўчириб ёқиш"
+
+#: js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "Ўчириш"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "Ўчириб ёқиш"
+
+#: js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Система %d сониядан сўнг автоматик равишда ўчиб ёнади."
+
+#: js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Ўчириб ёқиш ва янгиликларни ўрнатиш"
+
+#: js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Система %d сониядан сўнг автоматик равишда ўчиб ёнади ва янгиликларни "
+"ўрнатади."
+
+#: js/ui/endSessionDialog.js:127 js/ui/endSessionDialog.js:147
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Ўчириб ёқиш ва ўрнатиш"
+
+#: js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Ўрнатиш ва компьютерни ўчириш"
+
+#: js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Янгиликлар ўрнатилгандан кейин компьютерни ўчириш"
+
+#: js/ui/endSessionDialog.js:137
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Ўчириб ёқиш ва янгиликларни ўрнатиш"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:142
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"Система қайта ишга тушганидан сўнг %s %s ўрнатилади. Янгилаш кўп вақт талаб "
+"қилиши мумкин: захира нусхаси борлиги ва компьютер тармоққа уланганлигига "
+"ишонч ҳосил қилинг."
+
+#: js/ui/endSessionDialog.js:361
+msgid "Running on battery power: please plug in before installing updates."
+msgstr ""
+"Компьютер батарея қувватида ишлаяпти: илтимос янгиликларни ўрнатишдан олдин "
+"заряд ускунасини уланг."
+
+#: js/ui/endSessionDialog.js:378
+msgid "Some applications are busy or have unsaved work."
+msgstr "Баъзи дастурлар банд ёки уларнинг сақланмаган ишлари бор."
+
+#: js/ui/endSessionDialog.js:385
+msgid "Other users are logged in."
+msgstr "Системада бошқа фойдаланувчиларнинг фаол сеанслари мавжуд."
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:671
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (масофавий)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:674
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (терминал)"
+
+#: js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "Ўрнатиш"
+
+#: js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "\"%s\" ни extensions.gnome.org сайтидан юклаб олиб ўрнатасизми?"
+
+#: js/ui/keyboard.js:742 js/ui/status/keyboard.js:782
+msgid "Keyboard"
+msgstr "Клавиатура"
+
+#. translators: 'Hide' is a verb
+#: js/ui/legacyTray.js:65
+msgid "Hide tray"
+msgstr "Лаълини яшириш"
+
+#: js/ui/legacyTray.js:106
+msgid "Status Icons"
+msgstr "Ҳолат нишончалари"
+
+#: js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "Бирорта кенгайтма ўрнатилмаган"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s бирор хато чиқармади."
+
+#: js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "Хатоларни яшириш"
+
+#: js/ui/lookingGlass.js:707 js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "Хатоларни кўрсатиш"
+
+#: js/ui/lookingGlass.js:716
+msgid "Enabled"
+msgstr "Ёниқ"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:719 src/gvc/gvc-mixer-control.c:1866
+msgid "Disabled"
+msgstr "Ўчиқ"
+
+#: js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "Хато"
+
+#: js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "Эски"
+
+#: js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "Юкланяпти"
+
+#: js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "Манбани кўриш"
+
+#: js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "Веб саҳифа"
+
+#: js/ui/messageList.js:543
+msgid "Clear section"
+msgstr "Бўлимни тозалаш"
+
+#: js/ui/messageTray.js:1486
+msgid "System Information"
+msgstr "Система ҳақида ахборот"
+
+#: js/ui/mpris.js:194
+msgid "Unknown artist"
+msgstr "Номаълум артист"
+
+#: js/ui/mpris.js:195
+msgid "Unknown title"
+msgstr "Номаълум сарлавҳа"
+
+#: js/ui/mpris.js:217
+msgid "Media"
+msgstr "Ахборот воситалари"
+
+#: js/ui/overview.js:84
+msgid "Undo"
+msgstr "Ўз ҳолига қайтариш"
+
+#: js/ui/overview.js:113
+msgid "Overview"
+msgstr "Тепадан кўриниш"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overview.js:240
+msgid "Type to search…"
+msgstr "Қидириш учун ёзинг..."
+
+#: js/ui/panel.js:358
+msgid "Quit"
+msgstr "Чиқиш"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: js/ui/panel.js:414
+msgid "Activities"
+msgstr "Фаолият"
+
+#: js/ui/panel.js:695
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Система"
+
+#: js/ui/panel.js:807
+msgid "Top Bar"
+msgstr "Тепа панели"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: js/ui/popupMenu.js:289
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: js/ui/runDialog.js:71
+msgid "Enter a Command"
+msgstr "Буйруқни киритинг"
+
+#: js/ui/runDialog.js:111 js/ui/windowMenu.js:162
+msgid "Close"
+msgstr "Ёпиш"
+
+#: js/ui/runDialog.js:282
+msgid "Restarting…"
+msgstr "Ўчириб ёқиляпти..."
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/screenShield.js:85
+msgid "%A, %B %d"
+msgstr "%A, %B %d"
+
+#: js/ui/screenShield.js:144
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%dта янги хат"
+
+#: js/ui/screenShield.js:146
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%dта янги хабарномалар"
+
+#: js/ui/screenShield.js:449 js/ui/status/system.js:382
+msgid "Lock"
+msgstr "Қулфлаш"
+
+#: js/ui/screenShield.js:704
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME экранни қулфлаши керак"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:825 js/ui/screenShield.js:1291
+msgid "Unable to lock"
+msgstr "Қулфлаб бўлмади"
+
+#: js/ui/screenShield.js:826 js/ui/screenShield.js:1292
+msgid "Lock was blocked by an application"
+msgstr "Қайсидир дастур томонидан қулфлаш бекор қилинди"
+
+#: js/ui/search.js:617
+msgid "Searching…"
+msgstr "Қидириляпти..."
+
+#: js/ui/search.js:619
+msgid "No results."
+msgstr "Натижа йўқ."
+
+#: js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "Кўчириш"
+
+#: js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "Қўйиш"
+
+#: js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "Матнни кўрсатиш"
+
+#: js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "Матнни яшириш"
+
+#: js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "Махфий сўз"
+
+#: js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "Махфий сўзни эслаб қолиш"
+
+#: js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "Махсус имкониятлар"
+
+#: js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "Миқиёс"
+
+#: js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "Экран ўқувчи"
+
+#: js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "Экран клавиатураси"
+
+#: js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "Кўргазмали огоҳлантиришлар"
+
+#: js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "Ёпишқоқ клавишалар"
+
+#: js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "Секин клавишалар"
+
+#: js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "Сакраб турувчи клавиша"
+
+#: js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "Сичқонча тугмалари"
+
+#: js/ui/status/accessibility.js:167
+msgid "High Contrast"
+msgstr "Юқори контрастли"
+
+#: js/ui/status/accessibility.js:202
+msgid "Large Text"
+msgstr "Катта матн"
+
+#: js/ui/status/bluetooth.js:47
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:56 js/ui/status/network.js:624
+msgid "Bluetooth Settings"
+msgstr "Bluetooth мосламалари"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:136
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%dта уланган"
+
+#: js/ui/status/bluetooth.js:138
+msgid "Off"
+msgstr "Ўчган"
+
+#: js/ui/status/bluetooth.js:140
+msgid "Not In Use"
+msgstr "Ишлатилмаяпти"
+
+#: js/ui/status/bluetooth.js:142 js/ui/status/network.js:1279
+msgid "Turn On"
+msgstr "Ёқиш"
+
+#: js/ui/status/bluetooth.js:142 js/ui/status/network.js:178
+#: js/ui/status/network.js:353 js/ui/status/network.js:1279
+#: js/ui/status/network.js:1394 js/ui/status/rfkill.js:90
+#: js/ui/status/rfkill.js:117
+msgid "Turn Off"
+msgstr "Ўчириш"
+
+#: js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "Ёруғлик"
+
+#: js/ui/status/keyboard.js:805
+msgid "Show Keyboard Layout"
+msgstr "Клавиатура жойлашувини кўрсатиш"
+
+#: js/ui/status/location.js:107 js/ui/status/location.js:215
+msgid "Location Enabled"
+msgstr "Манзил ёқилган"
+
+#: js/ui/status/location.js:108 js/ui/status/location.js:216
+msgid "Disable"
+msgstr "Ўчириш"
+
+#: js/ui/status/location.js:109
+msgid "Privacy Settings"
+msgstr "Шахсий маълумот мосламалари"
+
+#: js/ui/status/location.js:214
+msgid "Location In Use"
+msgstr "Манзил ишлатиляпти"
+
+#: js/ui/status/location.js:218
+msgid "Location Disabled"
+msgstr "Манзил ўчирилган"
+
+#: js/ui/status/location.js:219
+msgid "Enable"
+msgstr "Ёқиш"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:435
+#, javascript-format
+msgid "Give %s access to your location?"
+msgstr "%s манзилингизни билишига рухсат берасизми?"
+
+#: js/ui/status/location.js:437
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "Манзилга рухсатни махфийлик мосламаларида ўзгартириш мумкин"
+
+#: js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<номаълум>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:451 js/ui/status/network.js:1308
+#, javascript-format
+msgid "%s Off"
+msgstr "%s ўчган"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s уланган"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:459
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s бошқарувда эмас"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:462
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s узиляпти"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:469 js/ui/status/network.js:1300
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s уланяпти"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:472
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s аутентификацияни талаб қилади"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:480
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "%sнинг чоки йўқ"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:484
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s мавжуд эмас"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:487
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s алоқа қила олмади"
+
+#: js/ui/status/network.js:503
+msgid "Wired Settings"
+msgstr "Симли алоқа мосламалари"
+
+#: js/ui/status/network.js:545
+msgid "Mobile Broadband Settings"
+msgstr "Кенг йўлли мобил алоқа мосламалари"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:588 js/ui/status/network.js:1305
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s ускунаси ўчирилган"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:592
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s ўчиқ"
+
+#: js/ui/status/network.js:632
+msgid "Connect to Internet"
+msgstr "Интернетга уланиш"
+
+#: js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "Самолёт режими ёнган"
+
+#: js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Самолёт режими ёниқ бўлганда Wi-Fi ўчган бўлади."
+
+#: js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "Самолёт режимини ўчириш"
+
+#: js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi ўчган"
+
+#: js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Тармоққа уланиш учун Wi-Fi ёнган бўлиши керак."
+
+#: js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "Wi-Fiни ёқиш"
+
+#: js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi тармоқлар"
+
+#: js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "Тармоқни танлаш"
+
+#: js/ui/status/network.js:883
+msgid "No Networks"
+msgstr "Бирорта тармоқ мавжуд эмас"
+
+#: js/ui/status/network.js:904 js/ui/status/rfkill.js:115
+msgid "Use hardware switch to turn off"
+msgstr "Ўчириш учун ускуна тугмасини ишлатиш"
+
+#: js/ui/status/network.js:1171
+msgid "Select Network"
+msgstr "Тармоқни танлаш"
+
+#: js/ui/status/network.js:1177
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi Мосламалари"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1296
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "%s уланиш нуқтаси фаол"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1311
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s уланмаган"
+
+#: js/ui/status/network.js:1411
+msgid "connecting..."
+msgstr "уланяпти..."
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1414
+msgid "authentication required"
+msgstr "ҳақиқийлигини аниқлаш талаб қилинади"
+
+#: js/ui/status/network.js:1416
+msgid "connection failed"
+msgstr "алоқа қилиб бўлмади"
+
+#: js/ui/status/network.js:1482 js/ui/status/rfkill.js:93
+msgid "Network Settings"
+msgstr "Тармоқ мосламалари"
+
+#: js/ui/status/network.js:1484
+msgid "VPN Settings"
+msgstr "VPN мосламалари"
+
+#: js/ui/status/network.js:1503
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1513
+msgid "VPN Off"
+msgstr "VPN Ўчган"
+
+#: js/ui/status/network.js:1697
+msgid "Connection failed"
+msgstr "Алоқа қилиб бўлмади"
+
+#: js/ui/status/network.js:1698
+msgid "Activation of network connection failed"
+msgstr "Тармоқ алоқасини фаоллаштириб бўлмади"
+
+#: js/ui/status/power.js:61
+msgid "Power Settings"
+msgstr "Қувват мосламалари"
+
+#: js/ui/status/power.js:77
+msgid "Fully Charged"
+msgstr "Тўлиқ зарядланди"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:84 js/ui/status/power.js:90
+msgid "Estimating…"
+msgstr "Баҳоланяпти..."
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:98
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "%d∶%02d қолди (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:103
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "Тўлишига %d∶%02d қолди (%d%%)"
+
+#: js/ui/status/power.js:131 js/ui/status/power.js:133
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:88
+msgid "Airplane Mode On"
+msgstr "Самолёт режими ёнган"
+
+#: js/ui/status/system.js:351
+msgid "Switch User"
+msgstr "Фойдаланувчини алмаштириш"
+
+#: js/ui/status/system.js:356
+msgid "Log Out"
+msgstr "Сеансни якунлаш"
+
+#: js/ui/status/system.js:361
+msgid "Account Settings"
+msgstr "Профил созламалари"
+
+#: js/ui/status/system.js:378
+msgid "Orientation Lock"
+msgstr "Ориентацияни қулфлаш"
+
+#: js/ui/status/system.js:386
+msgid "Suspend"
+msgstr "Тўхтатиб қўймоқ"
+
+#: js/ui/status/system.js:389
+msgid "Power Off"
+msgstr "Ўчган"
+
+#: js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "Овоз баландлиги ўзгарди"
+
+#: js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "Товуш"
+
+#: js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "Бошқа фойдаланувчи сифатида кириш"
+
+#: js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "Ойнани қулфини очиш"
+
+#: js/ui/viewSelector.js:159
+msgid "Applications"
+msgstr "Дастурлар"
+
+#: js/ui/viewSelector.js:163
+msgid "Search"
+msgstr "Қидириш"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "\"%s\" тайёр"
+
+#: js/ui/windowManager.js:63
+msgid "Do you want to keep these display settings?"
+msgstr "Ушбу экран мосламаларини сақламоқчимисиз?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: js/ui/windowManager.js:82
+msgid "Revert Settings"
+msgstr "Мосламаларни ўз ҳолига қайтариш"
+
+#: js/ui/windowManager.js:85
+msgid "Keep Changes"
+msgstr "Ўзгаришларни сақлаш"
+
+#: js/ui/windowManager.js:103
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Мослама ўзгаришлари %d сониядан кейин ўз ҳолига қайтади"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:658
+#, javascript-format
+msgid "%d x %d"
+msgstr "%d x %d"
+
+#: js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "Йиғиш"
+
+#: js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "Олдинги ўлчамига қайтариш"
+
+#: js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "Катталаштирмоқ"
+
+#: js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "Кўчириш"
+
+#: js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "Ўлчамини ўзгартириш"
+
+#: js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "Сарлавҳа панелини экранга кўчириш"
+
+#: js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "Доим тепада"
+
+#: js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "Доим кўриниб турган иш жойига"
+
+#: js/ui/windowMenu.js:105
+msgid "Move to Workspace Left"
+msgstr "Чапдаги иш жойига кўчириш"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Right"
+msgstr "Ўнгдаги иш жойига кўчириш"
+
+#: js/ui/windowMenu.js:115
+msgid "Move to Workspace Up"
+msgstr "Тепадаги иш жойига кўчириш"
+
+#: js/ui/windowMenu.js:120
+msgid "Move to Workspace Down"
+msgstr "Пастдаги иш жойига кўчириш"
+
+#: js/ui/windowMenu.js:136
+msgid "Move to Monitor Up"
+msgstr "Тепадаги мониторга кўчириш"
+
+#: js/ui/windowMenu.js:142
+msgid "Move to Monitor Down"
+msgstr "Пастдаги мониторга кўчириш"
+
+#: js/ui/windowMenu.js:148
+msgid "Move to Monitor Left"
+msgstr "Чапдаги мониторга кўчириш"
+
+#: js/ui/windowMenu.js:154
+msgid "Move to Monitor Right"
+msgstr "Ўнгдаги мониторга кўчириш"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution тақвими"
+
+#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+#: src/calendar-server/evolution-calendar.desktop.in:6
+msgid "evolution"
+msgstr "evolution"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: src/gvc/gvc-mixer-control.c:1873
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%uта товуш чиқиш йўллари"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: src/gvc/gvc-mixer-control.c:1883
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%uта товуш кириш йўллари"
+
+#: src/gvc/gvc-mixer-control.c:2738
+msgid "System Sounds"
+msgstr "Система товушлари"
+
+#: src/main.c:381
+msgid "Print version"
+msgstr "Босма учун версия"
+
+#: src/main.c:387
+msgid "Mode used by GDM for login screen"
+msgstr "Сеансга кириш ойнаси учун GDM ишлатадиган режим"
+
+#: src/main.c:393
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "Аниқ бир режимни, масалан кириш экрани учун \"gdm\"ни, ишлатиш"
+
+#: src/main.c:399
+msgid "List possible modes"
+msgstr "Мавжуд режимлар рўйхати"
+
+#: src/shell-app.c:270
+msgctxt "program"
+msgid "Unknown"
+msgstr "Номаълум"
+
+#: src/shell-app.c:511
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "\"%s\"ни ишга тушириб бўлмади"
+
+#: src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "Махфий сўзлар мос келмади."
+
+#: src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "Махфий сўз бўш бўлмаслиги керак"
+
+#: src/shell-polkit-authentication-agent.c:353
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Ҳақиқийлигини аниқловчи диалог ойна фойдаланувчи томонидан ёпилди"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Хабарлар панелини кўрсатиш"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (wayland улагичи)"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Тақвимда ҳафта тартибини кўрсатиш"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Агар белгиланса, тақвимда ISO ҳафта тартибини кўрсатилади."
+
+#~ msgid "Captive Portal"
+#~ msgstr "Captive Portal"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "Як"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "Д"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "С"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "Ч"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "П"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "Ж"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "Ш"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Ҳеч нарса режалаштирилмаган"
+
+#~ msgid "Today"
+#~ msgstr "Бугун"
+
+#~ msgid "Tomorrow"
+#~ msgstr "Эртага"
+
+#~ msgid "This week"
+#~ msgstr "Шу ҳафта"
+
+#~ msgid "Next week"
+#~ msgstr "Келаси ҳафта"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Олинадиган ускуналар"
+
+#~ msgid "Eject"
+#~ msgstr "Чиқариш"
+
+#~ msgid "Invitation"
+#~ msgstr "Таклифнома"
+
+#~ msgid "Call"
+#~ msgstr "Телефон қилиш"
+
+#~ msgid "File Transfer"
+#~ msgstr "Файлни кўчириш"
+
+#~ msgid "Chat"
+#~ msgstr "Чатлашиш"
+
+#~ msgid "Unmute"
+#~ msgstr "Овозини чиқариш"
+
+#~ msgid "Mute"
+#~ msgstr "Овозини ўчириш"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "%sга таклифнома"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s сизни %sга қўшилишга таклиф қиляпти"
+
+#~ msgid "Decline"
+#~ msgstr "Рад этмоқ"
+
+#~ msgid "Accept"
+#~ msgstr "Қабул қилмоқ"
+
+#~ msgid "Video call from %s"
+#~ msgstr "%sдан видео қўнғироқ"
+
+#~ msgid "Call from %s"
+#~ msgstr " %sдан қўнғироқ"
+
+#~ msgid "Answer"
+#~ msgstr "Жавоб"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s сизга %sни жўнатяпти"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s синзни онлайн бўлганингизда кўриш учун рухсат сўраяпти"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Ҳақиқийлигини текшириб бўлмади"
+
+#~ msgid "Encryption error"
+#~ msgstr "Шифрлашда хатолик"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Гувоҳнома берилмаган"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Гувоҳнома ишонарсиз"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Гувоҳнома эскирган"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Гувоҳнома фаоллаштирилмаган"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Гувоҳнома тугунининг номи мос келмади"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Гувоҳнома тамғаси мос келмади"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Ўзи имзоланган гувоҳнома"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Статусингиз офлайн қилинди"
+
+#~ msgid "Encryption is not available"
+#~ msgstr "Шифрлаш мавжуд эмас"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Хато гувоҳнома"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Алоқа рад жавобини олди"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Алоқа қилиб бўлмади"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Алоқа йўқотилди"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Бу ҳисоб аллақачон серверга уланган"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Алоқа олдинги манбани ишлатган ҳолда янгиси билан алмаштирилди"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Бу ҳисоб аллақачон серверда бор"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Сервер жуда бандлиги туфайли алоқани ушлаб туролмайди"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Гувоҳнома бекор қилинган"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr ""
+#~ "Гувоҳнома хавфсиз бўлмаган шифрлаш алгоритмини ишлатади ёки у "
+#~ "криптографик кучсиз"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Сервер гувоҳномасининг узунлиги ёки сервер гувоҳнома занжирининг "
+#~ "чуқурлиги криптография кутубхонаси қўйган чегарадан ошиб кетган"
+
+#~ msgid "Internal error"
+#~ msgstr "Ички хато"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "%sга уланиб бўлмади"
+
+#~ msgid "View account"
+#~ msgstr "Ҳисобни кўриш"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Calendarни очиш"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Сана ҳамда Вақт Мосламалари"
+
+#~ msgid "Open"
+#~ msgstr "Очиш"
+
+#~ msgid "Remove"
+#~ msgstr "Олиб ташлаш"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Хатларни тозалаш"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Хабарнома мосламалари"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Панел менюси"
+
+#~ msgid "No Messages"
+#~ msgstr "Бирорта хат йўқ"
+
+#~ msgid "Message Tray"
+#~ msgstr "Хатлар панели"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%dта уланган ускуналар"
+
+#~ msgid "Authentication required"
+#~ msgstr "ҳақиқийлигини аниқлаш талаб қилинади"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Интернет алоқаси сифатида ишлатиш"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Батарея"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Самолёт режими"
+
+#~ msgid "On"
+#~ msgstr "Ёнган"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr ""
+#~ "Тепадаги очилувчи рўйхатдан фойдаланиб созламоқчи бўлган кенгайтмангизни "
+#~ "танланг."
+
+#~ msgid "calendar:MY"
+#~ msgstr "Тақвим:MY"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "%sдан ҳақиқийлигини текширишга ижозат"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "%s ускунаси ушбу компьютер билан жуфтлашмоқчи"
+
+#~ msgid "Allow"
+#~ msgstr "Рухсат бериш"
+
+#~ msgid "Deny"
+#~ msgstr "Рад этиш"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "\"%s\" ускуна \"%s\" хизматдан фойдаланмоқчи"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Фақат шу сафар рухсат бериш"
+
+#~ msgid "Reject"
+#~ msgstr "Рад этиш"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s учун жуфтлаш тасдиғи"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "Илтимос \"%06d\" калити ускунадагига мос келишини тасдиқланг."
+
+#~ msgid "Matches"
+#~ msgstr "Мос келади"
+
+#~ msgid "Does not match"
+#~ msgstr "Мос келмайди"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s учун жуфтлашга ижозат"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Илтимос ускунадаги PINни киритинг."
+
+#~ msgid "OK"
+#~ msgstr "Хўп"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Узр, бугун сиз учун донолик йўқ:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "\"%s\" дейди пайғамбар"
diff --git a/po/vi.po b/po/vi.po
new file mode 100644
index 0000000..91d19b5
--- /dev/null
+++ b/po/vi.po
@@ -0,0 +1,4033 @@
+# Vietnamese translation for gnome-shell.
+# Bản dịch tiếng Việt dành cho gnome-shell.
+# Copyright (C) 2016 GNOME i18n Project for Vietnamese.
+# This file is distributed under the same license as the gnome-shell package.
+# Nguyễn Thái Ngọc Duy <pclouds@gmail.com>, 2010-2013.
+# Ngô Chin <ndtrung4419@gmail.com>, 2011.
+# Trần Ngọc Quân <vnwildman@gmail.com>, 2013-2019, 2021-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell main\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-04-03 07:03+0000\n"
+"PO-Revision-Date: 2022-04-03 14:45+0700\n"
+"Last-Translator: Trần Ngọc Quân <vnwildman@gmail.com>\n"
+"Language-Team: Vietnamese <gnome-vi-list@gnome.org>\n"
+"Language: vi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Gtranslator 3.38.0\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "Chạy phần mềm"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "Kích hoạt ứng dụng ưa thích 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "Kích hoạt ứng dụng ưa thích 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "Kích hoạt ứng dụng ưa thích 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "Kích hoạt ứng dụng ưa thích 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "Kích hoạt ứng dụng ưa thích 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "Kích hoạt ứng dụng ưa thích 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "Kích hoạt ứng dụng ưa thích 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "Kích hoạt ứng dụng ưa thích 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "Kích hoạt ứng dụng ưa thích 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2062
+msgid "Screenshots"
+msgstr "Chụp màn hình"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:235
+msgid "Take a screenshot interactively"
+msgstr "Chụp màn hình tương tác"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:247
+msgid "Take a screenshot"
+msgstr "Chụp màn hình"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:243
+msgid "Take a screenshot of a window"
+msgstr "Chụp hình cửa sổ"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:239
+msgid "Record a screencast interactively"
+msgstr "Quay phim màn hình tương tác"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "Hệ thống"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "Hiển thị danh sách thông báo"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "Tập trung vào thông báo hoạt động"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "Hiển thị tổng quan"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "Hiện mọi ứng dụng"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "Mở trình đơn ứng dụng"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "Hệ vỏ GNOME"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "Quản lý cửa sổ và chạy ứng dụng"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr ""
+"Bật công cụ nội bộ, hữu dụng với người phát triển và kiểm thử, qua Alt-F2"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr ""
+"Cho phép truy cập công cụ theo dõi và tìm lỗi nội bộ qua hộp thoại Alt-F2."
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "Mã số thiết bị của phần mở rộng cần bật"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"Phần mở rộng Hệ vỏ GNOME có thuộc tính mã số thiết bị; khóa này liệt kê danh "
+"sách phần mở rộng nên nạp. Bất kỳ phần mở rộng nào muốn nạp phải nằm trong "
+"danh sách này. Bạn có thể thao tác trên danh sách này với phương thức DBus "
+"EnableExtension và DisableExtension trên org.gnome.Shell."
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "Mã số thiết bị của phần mở rộng buộc tắt đi"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"Phần mở rộng Hệ vỏ GNOME có thuộc tính mã số thiết bị UUID; khóa này liệt kê "
+"danh sách phần mở rộng nên bị tắt, ngay cả khi được tải như là một bộ phận "
+"của chế độ hiện tại. Bạn có thể thao tác trên danh sách này với phương thức "
+"DBus EnableExtension và DisableExtension trên org.gnome.Shell. Khóa này nhận "
+"ưu tiên hơn cài đặt “enabled-extensions”."
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "Tắt phần mở rộng người dùng"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"Tắt tất cả các phần mở rộng người dùng đã bật mà không ảnh hưởng đến cài đặt "
+"“enabled-extension”."
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "Tắt kiểm tra tương thích phiên bản của phần mở rộng"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"Hệ vỏ GNOME sẽ chỉ tải những phần mở rộng cái mà yêu cầu phải hỗ trợ phiên "
+"bản hiện đang chạy. Bật tùy chọn này sẽ tắt việc kiểm tra và thử tải tất cả "
+"những phần mở rộng mà không cần quan tâm đến việc nó hỗ trợ phiên bản nào."
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "Danh sách ID tập tin desktop của ứng dụng yêu thích"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr ""
+"Ứng dụng tương ứng với những định danh này sẽ được hiển thị ở vùng ưa thích."
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "Hộp thoại lịch sử lệnh (Alt-F2)"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Lịch sử hộp thoại kính tìm kiếm"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "Luôn hiện mục “Đăng xuất” trên trình đơn người dùng."
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"Khóa này sẽ đè lên việc tự động ẩn trình đơn “Đăng xuất” ở chế độ đơn-người-"
+"dùng, đơn-phiên-làm-việc."
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr ""
+"Hoặc là ghi nhớ mật khẩu dành cho việc gắn hệ thống tập tin đã mã hóa hoặc "
+"hệ thống tập tin trên máy chủ"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"Hệ vỏ sẽ yêu cầu mật khẩu khi có thư mục được mã hóa hay hệ thống tập tin "
+"máy mạng được gắn. Nếu mật khẩu có thể ghi lại để dùng trong lần sau, hộp "
+"dấu kiểm “Nhớ mật khẩu” sẽ xuất hiện. Khóa này đặt trạng thái mặc định cho "
+"hộp dấu kiểm."
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+"Có nên để bộ điều hợp Bluetooth mặc định phải cài đặt thiết bị liên kết với "
+"nó hay không"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+"Hệ vỏ sẽ chỉ hiển thị mục tin trình đơn Bluetooth nếu nó được bật, hoặc là "
+"có các thiết bị được cài đặt liên kết với bộ tiếp hợp mặc định. Cái này sẽ "
+"được đặt lại nếu bộ tiếp hợp mặc định không còn được liên kết với nó nữa."
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "Phiên bản cuối hộp thoại “Chào mừng GNOME” sẽ được hiển thị"
+
+#: data/org.gnome.shell.gschema.xml.in:100
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"Khóa này xác định phiên bản nào hộp thoại \"Chào mừng bạn đến với GNOME\" "
+"được hiển thị lần cuối. Một chuỗi trống đại diện cho phiên bản cũ nhất có "
+"thể và một số cực lớn sẽ đại diện cho các phiên bản chưa tồn tại. Con số "
+"khổng lồ này có thể được sử dụng để vô hiệu hóa hộp thoại một cách hiệu quả."
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid "Layout of the app picker"
+msgstr "Bố cục của bộ chọn ứng dụng"
+
+#: data/org.gnome.shell.gschema.xml.in:134
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"Bố cục của bộ chọn ứng dụng. Mỗi mục trong mảng là một trang. Các trang được "
+"lưu trữ theo thứ tự chúng xuất hiện trong Hệ vỏ GNOME. Mỗi trang chứa một "
+"cặp “id ứng dụng” → 'dữ liệu'. Hiện tại, các giá trị sau được lưu trữ dưới "
+"dạng 'dữ liệu': • “position”: vị trí của biểu tượng ứng dụng trong trang"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu"
+msgstr "Phím mở trình đơn ứng dụng"
+
+#: data/org.gnome.shell.gschema.xml.in:150
+msgid "Keybinding to open the application menu."
+msgstr "Phím mở trình đơn ứng dụng."
+
+#: data/org.gnome.shell.gschema.xml.in:156
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between overview states"
+msgstr "Tổ hợp phím để dịch chuyển giữa các trạng thái tổng quan"
+
+#: data/org.gnome.shell.gschema.xml.in:157
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "Tổ hợp phím để chuyển đổi giữa phiên, bộ chọn cửa sổ và lưới ứng dụng"
+
+#: data/org.gnome.shell.gschema.xml.in:164
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr ""
+"Tổ hợp phím để chuyển đổi giữa lưới ứng dụng, bộ chọn cửa sổ và phiên làm "
+"việc"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "Phím hiện “Ứng dụng”"
+
+#: data/org.gnome.shell.gschema.xml.in:171
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "Phím hiện “Ứng dụng” trong “Tổng quan hoạt động”."
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the overview"
+msgstr "Phím mở tổng quan"
+
+#: data/org.gnome.shell.gschema.xml.in:179
+msgid "Keybinding to open the Activities Overview."
+msgstr "Phím mở tổng quan hoạt động."
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "Phím để bật/tắt hiển thị danh sách thông báo"
+
+#: data/org.gnome.shell.gschema.xml.in:186
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "Phím để bật/tắt hiển thị danh sách thông báo."
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification"
+msgstr "Phím để tập trung vào thông báo hoạt động"
+
+#: data/org.gnome.shell.gschema.xml.in:193
+msgid "Keybinding to focus the active notification."
+msgstr "Phím để tập trung vào thông báo hoạt động."
+
+#: data/org.gnome.shell.gschema.xml.in:199
+msgid "Switch to application 1"
+msgstr "Chuyển đến ứng dụng 1"
+
+#: data/org.gnome.shell.gschema.xml.in:203
+msgid "Switch to application 2"
+msgstr "Chuyển đến ứng dụng 2"
+
+#: data/org.gnome.shell.gschema.xml.in:207
+msgid "Switch to application 3"
+msgstr "Chuyển đến ứng dụng 3"
+
+#: data/org.gnome.shell.gschema.xml.in:211
+msgid "Switch to application 4"
+msgstr "Chuyển đến ứng dụng 4"
+
+#: data/org.gnome.shell.gschema.xml.in:215
+msgid "Switch to application 5"
+msgstr "Chuyển đến ứng dụng 5"
+
+#: data/org.gnome.shell.gschema.xml.in:219
+msgid "Switch to application 6"
+msgstr "Chuyển đến ứng dụng 6"
+
+#: data/org.gnome.shell.gschema.xml.in:223
+msgid "Switch to application 7"
+msgstr "Chuyển đến ứng dụng 7"
+
+#: data/org.gnome.shell.gschema.xml.in:227
+msgid "Switch to application 8"
+msgstr "Chuyển đến ứng dụng 8"
+
+#: data/org.gnome.shell.gschema.xml.in:231
+msgid "Switch to application 9"
+msgstr "Chuyển đến ứng dụng 9"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid "Limit switcher to current workspace."
+msgstr "Giới hạn bộ chuyển đổi cho không gian làm việc hiện tại."
+
+#: data/org.gnome.shell.gschema.xml.in:257
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"Nếu đặt, chỉ những ứng dụng mà nó có cửa sổ nằm ở không gian làm việc hiện "
+"tại là hiện ra trên bộ chuyển. Nếu không, tất cả các ứng dụng sẽ được bao "
+"gồm."
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid "The application icon mode."
+msgstr "Chế độ biểu tượng ứng dụng."
+
+#: data/org.gnome.shell.gschema.xml.in:275
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"Cấu hình về cách hiển thị các cửa sổ ở bảng chuyển đổi. Các kiểu có thể là "
+"“thumbnail-only” (hiển thị ảnh thu nhỏ của cửa sổ), “app-icon-only” (chỉ "
+"hiển thị biểu tượng của ứng dụng) hoặc “both” (cả hai)."
+
+#: data/org.gnome.shell.gschema.xml.in:284
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"Nếu được đặt, chỉ những cửa sổ từ không gian làm việc hiện hành được hiện "
+"thị ở bộ chuyển. Nếu không, tất cả các cửa sổ sẽ được gộp vào."
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "Locations"
+msgstr "Vị trí"
+
+#: data/org.gnome.shell.gschema.xml.in:295
+msgid "The locations to show in world clocks"
+msgstr "Các vị trí để hiển thị trong đồng hồ thế giới"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Automatic location"
+msgstr "Vị trí tự động"
+
+#: data/org.gnome.shell.gschema.xml.in:306
+msgid "Whether to fetch the current location or not"
+msgstr "Có nên lấy về vị trí hiện tại hay không"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "Location"
+msgstr "Vị trí"
+
+#: data/org.gnome.shell.gschema.xml.in:314
+msgid "The location for which to show a forecast"
+msgstr "Vị trí hiển thị dự báo"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+msgid "Attach modal dialog to the parent window"
+msgstr "Gắn hộp thoại dạng modal vào cửa sổ mẹ"
+
+#: data/org.gnome.shell.gschema.xml.in:327
+#: data/org.gnome.shell.gschema.xml.in:336
+#: data/org.gnome.shell.gschema.xml.in:344
+#: data/org.gnome.shell.gschema.xml.in:352
+#: data/org.gnome.shell.gschema.xml.in:360
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr ""
+"Khóa này sẽ đè lên khóa có trong org.gnome.mutter khi chạy hệ vỏ GNOME."
+
+#: data/org.gnome.shell.gschema.xml.in:335
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "Bật xếp lớp ở cạnh khi thả cửa sổ vào cạnh màn hình"
+
+#: data/org.gnome.shell.gschema.xml.in:343
+msgid "Workspaces are managed dynamically"
+msgstr "Quản lý động vùng làm việc"
+
+#: data/org.gnome.shell.gschema.xml.in:351
+msgid "Workspaces only on primary monitor"
+msgstr "Vùng làm việc chỉ ở trên màn hình chính"
+
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "Khoảng trễ chờ cho con chuột ngừng di chuyển"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "Đăng nhập mạng"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "Có cái gì đó bị hỏng rồi"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"Chúng tôi rất lấy làm tiếc, nhưng ở đây đã bị trục trặc: các cài đặt cho "
+"phần mở rộng này không thể được hiển thị. Chúng tôi khuyến nghị bạn báo cáo "
+"lỗi này đến tác gỉa của phần mở rộng."
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "Chi tiết kỹ thuật"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "Trang chủ"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "Viếng thăm trang chủ của phần mở rộng"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:437 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: js/ui/status/network.js:956 subprojects/extensions-app/js/main.js:174
+msgid "Cancel"
+msgstr "Thôi"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "Mật khẩu"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "Chọn phiên làm việc"
+
+#: js/gdm/loginDialog.js:463
+msgid "Not listed?"
+msgstr "Không có trong danh sách?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:931
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(ví dụ: tài khoản hoặc %s)"
+
+#: js/gdm/loginDialog.js:936 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "Tài khoản"
+
+#: js/gdm/loginDialog.js:1257
+msgid "Login Window"
+msgstr "Cửa sổ đăng nhập"
+
+#: js/gdm/util.js:434
+msgid "Authentication error"
+msgstr "Lỗi xác thực"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:606
+msgid "(or swipe finger across reader)"
+msgstr "(hoặc quẹt ngón tay xuyên suốt bộ đọc)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:611
+msgid "(or place finger on reader)"
+msgstr "(hoặc đặt ngón tay trên bộ đọc)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "Tắt máy"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr ""
+"power off;shutdown;halt;stop;tắt máy;tat may;tắt nguồn;tat nguon;khởi động "
+"lại;khoi dong lai"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "Khởi động lại"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;khởi động lại;khoi dong lai;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "Khóa màn hình"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "khóa màn hình"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "Đăng xuất"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;sign off;đăng xuất;dang xuat"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "Ngủ đông"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;tạm dừng;tam dung;ngủ;ngu;ngủ đông;ngu dong"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "Chuyển người dùng"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "chuyển đổi người dùng"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;screen;rotation;khóa hướng;khoa huong;màn hinh;man hinh;"
+"xoay;quay"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "Chụp màn hình"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;chụp màn hình;chup man hinh;ghi "
+"hình;ghi hinh;chụp;chup"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "Mở khóa hướng màn hình"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "Khóa hướng màn hình"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "Không tìm thấy lệnh"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "Không thể phân tích lệnh:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "Gặp lỗi khi thực thi “%s”:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "Vừa xong"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d phút trước"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d giờ trước"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "Hôm qua"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d ngày trước"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d tuần trước"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d tháng trước"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d năm trước"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "%H:%M, hôm qua"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%H∶%M, %A"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-d %B, %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%-d %B %Y, %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%l∶%M %p"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "%l∶%M %p, hôm qua"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%l∶%M %p, %A"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-d %B, %l∶%M %p"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%-d %B %Y, %l∶%M %p"
+
+#: js/portalHelper/main.js:53
+msgid "Hotspot Login"
+msgstr "Đăng nhập điểm truy cập"
+
+#: js/portalHelper/main.js:106
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"Kết nối của bạn đến điểm truy cập này không an toàn. Mật khẩu hay các thông "
+"tin bạn nhập vào trang này có thể bị xem bởi những người gần bạn."
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:406
+msgid "Deny Access"
+msgstr "Từ chối truy cập"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:411
+msgid "Grant Access"
+msgstr "Cấp quyền truy cập"
+
+#: js/ui/appFavorites.js:164
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s đã được gắn vào bảng neo ứng dụng."
+
+#: js/ui/appFavorites.js:197
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s đã bỏ khỏi bảng neo ứng dụng."
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "Mở Cửa sổ"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "Cửa sổ mới"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "Hiện chi tiết"
+
+#: js/ui/appMenu.js:96
+msgid "Quit"
+msgstr "Thoát"
+
+#: js/ui/appMenu.js:156 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "Tháo ra"
+
+#: js/ui/appMenu.js:157
+msgid "Pin to Dash"
+msgstr "Gắn vào bảng neo"
+
+#: js/ui/appMenu.js:174
+msgid "Launch using Integrated Graphics Card"
+msgstr "Khởi chạy sử dụng Card đồ họa tích hợp"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Discrete Graphics Card"
+msgstr "Khởi chạy sử dụng Card đồ rời"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "Chọn thiết bị âm thanh"
+
+#: js/ui/audioDeviceSelection.js:56
+msgid "Sound Settings"
+msgstr "Cài đặt âm thanh"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "Tai nghe"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "Bộ tai nghe + micrô"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:270
+msgid "Microphone"
+msgstr "Micrô"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "Đổi ảnh nền…"
+
+#: js/ui/backgroundMenu.js:16 js/ui/status/nightLight.js:45
+msgid "Display Settings"
+msgstr "Cài đặt hiển thị"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "Cài đặt"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "C"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "2"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "3"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "4"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "5"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "6"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "7"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:402
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:412
+msgid "%OB %Y"
+msgstr "%OB %Y"
+
+#: js/ui/calendar.js:472
+msgid "Previous month"
+msgstr "Tháng trước"
+
+#: js/ui/calendar.js:490
+msgid "Next month"
+msgstr "Tháng tới"
+
+#: js/ui/calendar.js:642
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:701
+msgid "Week %V"
+msgstr "Tuần %V"
+
+#: js/ui/calendar.js:880
+msgid "No Notifications"
+msgstr "Không có thông báo nào"
+
+#: js/ui/calendar.js:937
+msgid "Do Not Disturb"
+msgstr "Xin đừng quấy rầy"
+
+#: js/ui/calendar.js:958
+msgid "Clear"
+msgstr "Xóa"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s” không trả lời."
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr ""
+"Bạn có thể chọn chờ một lúc cho nó tiếp tục hoặc buộc chấm dứt hoàn toàn ứng "
+"dụng."
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "Buộc thoát"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "Chờ"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "Ổ ngoài đã kết nối"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "Ổ ngoài đã ngắt kết nối"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "Không thể mở khóa phân vùng"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "Phiên bản udisks đã cài không hỗ trợ cài đặt PIM"
+
+#: js/ui/components/autorunManager.js:334
+#, javascript-format
+msgid "Open with %s"
+msgstr "Mở bằng %s"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr ""
+"Cách khác là bạn có thể kết nối bằng cách bấm vào nút “WPS” trên bộ định "
+"tuyến của bạn."
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:244
+#: js/ui/status/network.js:335 js/ui/status/network.js:961
+msgid "Connect"
+msgstr "Kết nối"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "Khóa"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "Mật khẩu khóa riêng"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "Định danh"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "Dịch vụ"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:746 js/ui/components/networkAgent.js:767
+msgid "Authentication required"
+msgstr "Cần xác thực"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:747
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "Cần mật mã hoặc khóa mã để truy cập mạng không dây “%s”."
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:751
+msgid "Wired 802.1X authentication"
+msgstr "Xác thực Wired 802.1X"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "Tên mạng"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:755
+msgid "DSL authentication"
+msgstr "Xác thực DSL"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:760
+msgid "PIN code required"
+msgstr "Cần mã PIN"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:761
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "Thiết bị di động băng thông rộng cần mã PIN"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:752
+#: js/ui/components/networkAgent.js:756 js/ui/components/networkAgent.js:768
+#: js/ui/components/networkAgent.js:772
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "Cần mật khẩu để kết nối đến “%s”."
+
+#: js/ui/components/networkAgent.js:735 js/ui/status/network.js:1751
+msgid "Network Manager"
+msgstr "Trình quản lý mạng"
+
+#: js/ui/components/networkAgent.js:771
+msgid "VPN password"
+msgstr "Mật khẩu VPN"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "Cần xác thực"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "Quản trị"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "Xác thực"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "Rất tiếc, không được. Vui lòng thử lại."
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s bây giờ đổi thành %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:405
+msgid "Windows"
+msgstr "Cửa sổ"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "Hiện ứng dụng"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Neo ứng dụng"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%A %e %B %Y"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-d %B"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%-d %B %Y"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "Hôm nay"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "Ngày mai"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "Cả ngày"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%d/%m"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "Không có sự kiện"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "Thêm đồng hồ thế giới…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "Đồng hồ thế giới"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "Đang tải…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "Lên mạng để biết thông tin thời tiết"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "Thông tin thời tiết hiện không sẵn có"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "Thời tiết"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "Chọn vị trí xem thời tiết…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "Đăng xuất %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "Đăng xuất"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "Sẽ tự động đăng xuất %s sau %d giây."
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "Bạn sẽ tự động đăng xuất sau %d giây."
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "Đăng xuất"
+
+#: js/ui/endSessionDialog.js:64
+msgctxt "title"
+msgid "Power Off"
+msgstr "Tắt máy"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "Cài đặt bản cập nhật & tắt máy"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "Hệ thống sẽ tự động tắt sau %d giây."
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "Cài đặt các cập nhật phần mềm còn chưa xử lý"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "Tắt máy"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "Khởi động lại máy tính"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "Cài đặt bản cập nhật & Khởi động lại"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "Hệ thống sẽ tự động khởi động lại sau %d giây."
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "Khởi động lại"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "Khởi động lại & Cài đặt bản cập nhật"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] ""
+"Hệ thống sẽ tự động khởi động lại và cài đặt các cập nhật sau %d giây nữa."
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "Khởi động lại &amp; Cài"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "Cài &amp; Tắt máy"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "Tắt máy sau khi các cập nhật được cài đặt"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "Khởi động lại & Cài đặt nâng cấp"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"%s %s sẽ được cài đặt sau khi khởi động lại. Cài đặt nâng cấp có thể mất "
+"nhiều thời gian: hãy chắn chắn rằng bạn đã sao lưu dự phòng và máy tính được "
+"cắm điện."
+
+#: js/ui/endSessionDialog.js:287
+msgid "Low battery power: please plug in before installing updates."
+msgstr ""
+"Nguồn pin yếu: vui lòng cắm dây sạc trước khi thực hiện việc cài đặt cập "
+"nhật."
+
+#: js/ui/endSessionDialog.js:296
+msgid "Some applications are busy or have unsaved work"
+msgstr "Một số ứng dụng đang bận hoặc có dữ liệu chưa được lưu lại"
+
+#: js/ui/endSessionDialog.js:301
+msgid "Other users are logged in"
+msgstr "Người dùng khác đang đăng nhập"
+
+#: js/ui/endSessionDialog.js:467
+msgctxt "button"
+msgid "Boot Options"
+msgstr "Tùy chọn khởi động"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:688
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (máy chủ)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:691
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (bàn điều khiển)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "Cài đặt"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "Cài Phần mở rộng"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "Tải và cài đặt “%s” từ extensions.gnome.org chứ?"
+
+#: js/ui/extensionSystem.js:267
+msgid "Extension Updates Available"
+msgstr "Các cập nhật phần mở rộng đang sẵn có"
+
+#: js/ui/extensionSystem.js:268
+msgid "Extension updates are ready to be installed."
+msgstr "Các cập nhật phần mở rộng sẵn đã được cài đặt rồi."
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "Cho phép ngăn cản phím tắt"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "Ứng dụng %s muốn ngăn cản phím tắt"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "Một ứng dụng muốn ngăn cản phím tắt"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "Bạn có thể khôi phục lại các phím tắt bằng cách bấm %s."
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "Từ chối"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "Cho phép"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "Phím chậm đã bật"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "Phím chậm đã tắt"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Bạn vừa mới ấn giữ phím Shift trong vòng 8 giây. Đây là phím tắt của tính "
+"năng Phím Chậm, cái mà ảnh hưởng đến hoạt động của bàn phím của bạn."
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "Phím dính đã bật"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "Phím dính đã tắt"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"Bạn vừa mới ấn phím Shift 5 lần liên tiếp. Đây là phím tắt của tính năng "
+"Phím Dính, mà nó ảnh hưởng đến hoạt động của bàn phím của bạn."
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"Bạn vừa mới ấn hai phím đồng thời, hoặc ấn phím Shift 5 lần liên tiếp. Phím "
+"tắt này tắt tính năng Phím Dính, mà nó ảnh hưởng đến hoạt động của bàn phím "
+"của bạn."
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "Để bật"
+
+#: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:1341
+msgid "Turn On"
+msgstr "Bật"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/bluetooth.js:174
+#: js/ui/status/network.js:160 js/ui/status/network.js:336
+#: js/ui/status/network.js:1341 js/ui/status/network.js:1456
+#: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81
+#: js/ui/status/rfkill.js:110
+msgid "Turn Off"
+msgstr "Tắt"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "Để tắt"
+
+#: js/ui/keyboard.js:250
+msgid "Region & Language Settings"
+msgstr "Cài đặt Lãnh thổ & Ngôn ngữ"
+
+#: js/ui/lookingGlass.js:701
+msgid "No extensions installed"
+msgstr "Chưa cài phần mở rộng nào"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:762
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s phát sinh bất kỳ lỗi gì."
+
+#: js/ui/lookingGlass.js:768
+msgid "Hide Errors"
+msgstr "Ẩn lỗi"
+
+#: js/ui/lookingGlass.js:772 js/ui/lookingGlass.js:845
+msgid "Show Errors"
+msgstr "Hiện lỗi"
+
+#: js/ui/lookingGlass.js:781
+msgid "Enabled"
+msgstr "Bật"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:784 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "Tắt"
+
+#: js/ui/lookingGlass.js:786
+msgid "Error"
+msgstr "Lỗi"
+
+#: js/ui/lookingGlass.js:788
+msgid "Out of date"
+msgstr "Đã lạc hậu"
+
+#: js/ui/lookingGlass.js:790
+msgid "Downloading"
+msgstr "Đang tải về"
+
+#: js/ui/lookingGlass.js:823
+msgid "View Source"
+msgstr "Xem mã nguồn"
+
+#: js/ui/lookingGlass.js:834
+msgid "Web Page"
+msgstr "Trang thông tin điện tử"
+
+#: js/ui/main.js:266
+msgid "System was put in unsafe mode"
+msgstr "Hệ thống được đặt vào chế độ không an toàn"
+
+#: js/ui/main.js:267
+msgid "Applications now have unrestricted access"
+msgstr "Ứng dụng giờ có quyền truy cập không hạn chế"
+
+#: js/ui/main.js:268 js/ui/overview.js:58
+msgid "Undo"
+msgstr "Hoàn lại"
+
+#: js/ui/main.js:314
+msgid "Logged in as a privileged user"
+msgstr "Đăng nhập dưới danh nghĩa người dùng đặc quyền"
+
+#: js/ui/main.js:315
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"Chạy một phiên với tư cách là người dùng đặc quyền nên tránh vì lý do bảo "
+"mật. Nếu có thể, bạn nên đăng nhập như một người dùng bình thường."
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "Thông tin hệ thống"
+
+#: js/ui/mpris.js:200
+msgid "Unknown artist"
+msgstr "Chưa biết nghệ sĩ"
+
+#: js/ui/mpris.js:210
+msgid "Unknown title"
+msgstr "Chưa biết tiêu đề"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:315
+msgid "Type to search"
+msgstr "Gõ từ muốn tìm"
+
+#: js/ui/overviewControls.js:393
+msgid "Applications"
+msgstr "Ứng dụng"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "Tổng quan"
+
+#: js/ui/padOsd.js:97
+msgid "New shortcut…"
+msgstr "Phím tắt mới…"
+
+#: js/ui/padOsd.js:148
+msgid "Application defined"
+msgstr "Ứng dụng đã định nghĩa"
+
+#: js/ui/padOsd.js:149
+msgid "Show on-screen help"
+msgstr "Hiển thị trợ giúp trên-màn-hình"
+
+#: js/ui/padOsd.js:150
+msgid "Switch monitor"
+msgstr "Chuyển màn hình"
+
+#: js/ui/padOsd.js:151
+msgid "Assign keystroke"
+msgstr "Gán phím tắt"
+
+#: js/ui/padOsd.js:220
+msgid "Done"
+msgstr "Xong"
+
+#: js/ui/padOsd.js:737
+msgid "Edit…"
+msgstr "Sửa…"
+
+#: js/ui/padOsd.js:779 js/ui/padOsd.js:896
+msgid "None"
+msgstr "Không"
+
+#: js/ui/padOsd.js:850
+msgid "Press a button to configure"
+msgstr "Nhấn vào một nút để cấu hình"
+
+#: js/ui/padOsd.js:851
+msgid "Press Esc to exit"
+msgstr "Nhấn phím Esc để thoát"
+
+#: js/ui/padOsd.js:854
+msgid "Press any key to exit"
+msgstr "Hãy nhấn phím bất kỳ để thoát"
+
+#: js/ui/panel.js:241
+msgid "Activities"
+msgstr "Hoạt động"
+
+#: js/ui/panel.js:364
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "Hệ thống"
+
+#: js/ui/panel.js:480
+msgid "Top Bar"
+msgstr "Thanh đỉnh"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "Chạy một lệnh"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "Nhấn phím Esc để thoát"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Không hỗ trợ khởi động lại trên Wayland"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "Đang khởi động lại…"
+
+#: js/ui/screenShield.js:229
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME cần khóa màn hình"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:269 js/ui/screenShield.js:644
+msgid "Unable to lock"
+msgstr "Không thể khóa"
+
+#: js/ui/screenShield.js:270 js/ui/screenShield.js:645
+msgid "Lock was blocked by an application"
+msgstr "Việc khóa bị ngăn cản bởi một ứng dụng"
+
+#: js/ui/screenshot.js:1148
+msgid "Selection"
+msgstr "Chọn"
+
+#: js/ui/screenshot.js:1158
+msgid "Area Selection"
+msgstr "Chọn theo vùng"
+
+#: js/ui/screenshot.js:1163
+msgid "Screen"
+msgstr "Màn hình"
+
+#: js/ui/screenshot.js:1173
+msgid "Screen Selection"
+msgstr "Chọn màn hình"
+
+#: js/ui/screenshot.js:1178
+msgid "Window"
+msgstr "Cửa sổ"
+
+#: js/ui/screenshot.js:1188
+msgid "Window Selection"
+msgstr "Chọn cửa sổ"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "Chụp màn hình/ Quay phim màn hình"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "Hiện con trỏ"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1828
+msgid "Screencasts"
+msgstr "Quay phim màn hình"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1833
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "Quay phim màn hình từ %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1902 js/ui/screenshot.js:2115
+msgid "Screenshot"
+msgstr "Chụp màn hình"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1908
+msgid "Screencast recorded"
+msgstr "Màn hình đã được quay lại"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1910
+msgid "Click here to view the video."
+msgstr "Bấn vào đây để xem phim."
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2129
+msgid "Show in Files"
+msgstr "Hiển thị trong \"Tập tin\""
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2075
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "Chụp màn hình từ %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2121
+msgid "Screenshot captured"
+msgstr "Đã chụp màn hình"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2123
+msgid "You can paste the image from the clipboard."
+msgstr "Bạn có thể dán ảnh từ clipboard."
+
+#: js/ui/screenshot.js:2176 js/ui/screenshot.js:2341
+msgid "Screenshot taken"
+msgstr "Chụp màn hình"
+
+#: js/ui/search.js:815
+msgid "Searching…"
+msgstr "Đang tìm…"
+
+#: js/ui/search.js:817
+msgid "No results."
+msgstr "Không tìm thấy kết quả nào."
+
+#: js/ui/search.js:948
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "%d nữa"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "Tìm"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "Chép"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "Dán"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "Hiện chữ"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "Ẩn chữ"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Phím Caps lock đang bật."
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "Phân vùng Ẩn"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Phân vùng hệ thống Windows"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "Dùng tập tin khóa"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr ""
+"Để mở khóa một phân vùng mà sử dụng tập tin khóa, dùng tiện ích <i>%s</i> để "
+"thay thế."
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "Số PIM"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "Nhớ mật khẩu"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "Mở khóa"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "Mở %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM phải là dạng con số hoặc để trống."
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "Không thể khởi chạy %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "Không thể tìm thấy ứng dụng %s"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "Khả năng truy cập"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "Tương phản cao"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "Phóng to"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "Trình đọc màn hình"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "Bàn phím màn hình"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "Cảnh báo trực quan"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "Phím dính"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "Phím chậm"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "Phím dội"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "Phím chuột"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "Chữ lớn"
+
+#: js/ui/status/bluetooth.js:54
+msgid "Bluetooth"
+msgstr "Bluetooth"
+
+#: js/ui/status/bluetooth.js:67 js/ui/status/network.js:626
+msgid "Bluetooth Settings"
+msgstr "Cài đặt Bluetooth"
+
+#. Translators: this is the number of connected bluetooth devices
+#: js/ui/status/bluetooth.js:166
+#, javascript-format
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "%d Đã kết nối"
+
+#: js/ui/status/bluetooth.js:170
+msgid "Bluetooth On"
+msgstr "Bluetooth bật"
+
+#: js/ui/status/bluetooth.js:172
+msgid "Bluetooth Off"
+msgstr "Bluetooth tắt"
+
+#: js/ui/status/brightness.js:39
+msgid "Brightness"
+msgstr "Độ sáng"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "Nhấp đơn"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "Nhấn đúp"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "Kéo"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "Nhấn phụ"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "Nhấn giữ"
+
+#: js/ui/status/keyboard.js:827
+msgid "Keyboard"
+msgstr "Bàn phím"
+
+#: js/ui/status/keyboard.js:844
+msgid "Show Keyboard Layout"
+msgstr "Hiện bố cục của bàn phím"
+
+#: js/ui/status/location.js:231 js/ui/status/location.js:255
+msgid "Location Enabled"
+msgstr "Vị trí được bật"
+
+#: js/ui/status/location.js:232 js/ui/status/location.js:256
+msgid "Disable"
+msgstr "Tắt"
+
+#: js/ui/status/location.js:234
+msgid "Privacy Settings"
+msgstr "Cài đặt chính sách riêng tư"
+
+#: js/ui/status/location.js:254
+msgid "Location In Use"
+msgstr "Vị trí đang dùng"
+
+#: js/ui/status/location.js:258
+msgid "Location Disabled"
+msgstr "Vị trí bị tắt"
+
+#: js/ui/status/location.js:259
+msgid "Enable"
+msgstr "Bật"
+
+#: js/ui/status/location.js:386
+msgid "Allow location access"
+msgstr "Cho phép truy cập vị trí"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:388
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "Ứng dụng %s muốn truy cập để biết vị trí của bạn"
+
+#: js/ui/status/location.js:398
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr ""
+"Việc cho truy cập để biết vị trí có thể được thay đổi vào bất kỳ lúc nào từ "
+"các cài đặt riêng tư."
+
+#: js/ui/status/network.js:71
+msgid "<unknown>"
+msgstr "<không rõ>"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:436 js/ui/status/network.js:1370
+#, javascript-format
+msgid "%s Off"
+msgstr "%s Tắt"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:439
+#, javascript-format
+msgid "%s Connected"
+msgstr "%s Đã kết nối"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: js/ui/status/network.js:444
+#, javascript-format
+msgid "%s Unmanaged"
+msgstr "%s Chưa được quản lý"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:447
+#, javascript-format
+msgid "%s Disconnecting"
+msgstr "%s Đang ngắt kết nối"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:454 js/ui/status/network.js:1362
+#, javascript-format
+msgid "%s Connecting"
+msgstr "%s Đang kết nối"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: js/ui/status/network.js:457
+#, javascript-format
+msgid "%s Requires Authentication"
+msgstr "%s Cần xác thực"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: js/ui/status/network.js:465
+#, javascript-format
+msgid "Firmware Missing For %s"
+msgstr "Thiếu firmware cho %s"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: js/ui/status/network.js:469
+#, javascript-format
+msgid "%s Unavailable"
+msgstr "%s Không sẵn sàng"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:472
+#, javascript-format
+msgid "%s Connection Failed"
+msgstr "%s Gặp lỗi khi kết nối"
+
+#: js/ui/status/network.js:484
+msgid "Wired Settings"
+msgstr "Cài đặt có dây"
+
+#: js/ui/status/network.js:531
+msgid "Mobile Broadband Settings"
+msgstr "Cài đặt mạng không giây băng thông rộng"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:587 js/ui/status/network.js:1367
+#, javascript-format
+msgid "%s Hardware Disabled"
+msgstr "%s Phần cứng bị tắt"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: js/ui/status/network.js:591
+#, javascript-format
+msgid "%s Disabled"
+msgstr "%s Bị tắt"
+
+#: js/ui/status/network.js:638
+msgid "Connect to Internet"
+msgstr "Kết nối vào Internet"
+
+#: js/ui/status/network.js:836
+msgid "Airplane Mode is On"
+msgstr "Chế độ máy bay đang được bật"
+
+#: js/ui/status/network.js:837
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "Wi-Fi bị tắt khi ở chế độ máy bay."
+
+#: js/ui/status/network.js:838
+msgid "Turn Off Airplane Mode"
+msgstr "Tắt chế độ máy bay"
+
+#: js/ui/status/network.js:847
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi bị tắt"
+
+#: js/ui/status/network.js:848
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "Wi-Fi cần được bật để có thể kết nối mạng."
+
+#: js/ui/status/network.js:849
+msgid "Turn On Wi-Fi"
+msgstr "Bật Wi-Fi"
+
+#: js/ui/status/network.js:877
+msgid "Wi-Fi Networks"
+msgstr "Mạng Wi-Fi"
+
+#: js/ui/status/network.js:881
+msgid "Select a network"
+msgstr "Chọn một mạng"
+
+#: js/ui/status/network.js:917
+msgid "No Networks"
+msgstr "Không có mạng"
+
+#: js/ui/status/network.js:942 js/ui/status/rfkill.js:108
+msgid "Use hardware switch to turn off"
+msgstr "Dùng nút tắt trên máy tính để tắt"
+
+#: js/ui/status/network.js:1253
+msgid "Select Network"
+msgstr "Chọn mạng"
+
+#: js/ui/status/network.js:1259
+msgid "Wi-Fi Settings"
+msgstr "Cài đặt Wi-Fi"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1358
+#, javascript-format
+msgid "%s Hotspot Active"
+msgstr "Điểm truy cập %s đang hoạt động"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1373
+#, javascript-format
+msgid "%s Not Connected"
+msgstr "%s Chưa kết nối"
+
+#: js/ui/status/network.js:1473
+msgid "connecting…"
+msgstr "đang kết nối…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: js/ui/status/network.js:1476
+msgid "authentication required"
+msgstr "cần xác thực"
+
+#: js/ui/status/network.js:1478
+msgid "connection failed"
+msgstr "gặp lỗi khi kết nối"
+
+#: js/ui/status/network.js:1524
+msgid "VPN Settings"
+msgstr "Cài đặt VPN"
+
+#: js/ui/status/network.js:1541
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1551
+msgid "VPN Off"
+msgstr "VPN Tắt"
+
+#: js/ui/status/network.js:1612 js/ui/status/rfkill.js:84
+msgid "Network Settings"
+msgstr "Cài đặt mạng"
+
+#: js/ui/status/network.js:1640
+#, javascript-format
+msgid "%s Wired Connection"
+msgid_plural "%s Wired Connections"
+msgstr[0] "%s kết nối có dây"
+
+#: js/ui/status/network.js:1644
+#, javascript-format
+msgid "%s Wi-Fi Connection"
+msgid_plural "%s Wi-Fi Connections"
+msgstr[0] "%s kết nối Wi-Fi"
+
+#: js/ui/status/network.js:1648
+#, javascript-format
+msgid "%s Modem Connection"
+msgid_plural "%s Modem Connections"
+msgstr[0] "%s kết nối modem"
+
+#: js/ui/status/network.js:1792
+msgid "Connection failed"
+msgstr "Lỗi kết nối"
+
+#: js/ui/status/network.js:1793
+msgid "Activation of network connection failed"
+msgstr "Lỗi kích hoạt kết nối mạng"
+
+#: js/ui/status/nightLight.js:63
+msgid "Night Light Disabled"
+msgstr "Ánh sang đêm bị tắt"
+
+#: js/ui/status/nightLight.js:64
+msgid "Night Light On"
+msgstr "Ánh sáng đêm được bật"
+
+#: js/ui/status/nightLight.js:66
+msgid "Resume"
+msgstr "Tiếp tục lại"
+
+#: js/ui/status/nightLight.js:67
+msgid "Disable Until Tomorrow"
+msgstr "Tắt cho đến ngày mai"
+
+#: js/ui/status/power.js:51 js/ui/status/powerProfiles.js:57
+msgid "Power Settings"
+msgstr "Cài đặt nguồn điện"
+
+#: js/ui/status/power.js:68
+msgid "Fully Charged"
+msgstr "Đã sạc đầy"
+
+#: js/ui/status/power.js:74
+msgid "Not Charging"
+msgstr "Không sạc"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: js/ui/status/power.js:77 js/ui/status/power.js:83
+msgid "Estimating…"
+msgstr "Ước tính…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Remaining (%d %%)"
+msgstr "Còn %d∶%02d (%d %%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: js/ui/status/power.js:97
+#, javascript-format
+msgid "%d∶%02d Until Full (%d %%)"
+msgstr "Cho đến lúc đầy cần %d∶%02d (%d %%)"
+
+#. The icon label
+#: js/ui/status/power.js:145
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#: js/ui/status/powerProfiles.js:19
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "Hiệu suất"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "Cân bằng"
+
+#: js/ui/status/powerProfiles.js:21
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "Tiết kiệm năng lượng"
+
+#: js/ui/status/remoteAccess.js:43
+msgid "Screen is Being Shared"
+msgstr "Màn hình đang được chia sẻ"
+
+#: js/ui/status/remoteAccess.js:45
+msgid "Turn off"
+msgstr "Tắt"
+
+#: js/ui/status/remoteAccess.js:149
+msgid "Stop Screencast"
+msgstr "Dừng quay phim màn hình"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: js/ui/status/rfkill.js:79
+msgid "Airplane Mode On"
+msgstr "Chế độ máy bay đang được bật"
+
+#: js/ui/status/system.js:104
+msgid "Lock"
+msgstr "Khóa"
+
+#: js/ui/status/system.js:116
+msgid "Power Off / Log Out"
+msgstr "Tắt máy / Đăng xuất"
+
+#: js/ui/status/system.js:119
+msgid "Suspend"
+msgstr "Tạm dừng"
+
+#: js/ui/status/system.js:130
+msgid "Restart…"
+msgstr "Khởi động lại…"
+
+#: js/ui/status/system.js:141
+msgid "Power Off…"
+msgstr "Tắt máy…"
+
+#: js/ui/status/system.js:154
+msgid "Log Out"
+msgstr "Đăng xuất"
+
+#: js/ui/status/system.js:165
+msgid "Switch User…"
+msgstr "Chuyển người dùng…"
+
+#: js/ui/status/thunderbolt.js:263
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:324
+msgid "Unknown Thunderbolt device"
+msgstr "Thiết bị Thunderbolt chưa biết"
+
+#: js/ui/status/thunderbolt.js:325
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr ""
+"Thiết bi mới đã được tìm thấy trong khi bạn rời đi. Vui lòng ngắt kết nối và "
+"kết nối lại thiết bị để bắt đầu dùng nó."
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Unauthorized Thunderbolt device"
+msgstr "Thiết bị Thunderbolt không đủ thẩm quyền"
+
+#: js/ui/status/thunderbolt.js:329
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "Đã dò tìm thấy thiết bị mới và cần được cho phép bởi người quản trị."
+
+#: js/ui/status/thunderbolt.js:335
+msgid "Thunderbolt authorization error"
+msgstr "Lỗi xác thực Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:336
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "Không thể cho phép thiết bị Thunderbolt: %s"
+
+#: js/ui/status/volume.js:158
+msgid "Volume changed"
+msgstr "Âm lượng đã thay đổi"
+
+#: js/ui/status/volume.js:220
+msgid "Volume"
+msgstr "Âm lượng"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "Giống nhau"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "Ghép màn hình"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "Chỉ bên ngoài"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "Chỉ tích-hợp"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "Bỏ khóa cửa sổ"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "Đăng nhập người dùng khác"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "Chúc mừng bạn dùng GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "Nếu bạn muốn tìm hiểu tổng thể, hãy xem phần thăm quan."
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "Không, cảm ơn"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "Tham quan"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s” đã sẵn sàng"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "Bạn có muốn giữ những cài đặt màn hình hiển thị này không?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "Phục hồi lại các cài đặt"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "Giữ các thay đổi"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "Các thay đổi có thể được hoàn lại trong vòng %d giây nữa"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:553
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "Chụp màn hình"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "Ẩn"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "Khôi phục"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "Lớn nhất"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "Di chuyển"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "Đổi cỡ"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "Di chuyển thanh tiêu đề trên màn hình"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "Luôn ở trên cùng"
+
+# Không gian làm việc nào đang được kích hoạt thì ở trên đó. Khi chuyển không gian làm việc, nó sẽ chuyển theo.
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "Luôn trên không gian làm việc nhìn thấy"
+
+# Chuyển cửa sổ sang không gian làm việc hay màn hình khác
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "Chuyển sang không gian làm việc Trái"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "Chuyển sang không gian làm việc Phải"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "Chuyển sang không gian làm việc Trên"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "Chuyển sang không gian làm việc Dưới"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "Chuyển sang màn hình Trên"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "Chuyển sang màn hình Dưới"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "Chuyển sang màn hình Trái"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "Chuyển sang màn hình Phải"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "Đóng"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Lịch Evolution"
+
+#: src/main.c:419 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "Hiển thị phiên bản"
+
+#: src/main.c:425
+msgid "Mode used by GDM for login screen"
+msgstr "Chế độ được dùng bởi GDM cho màn hình đăng nhập"
+
+#: src/main.c:431
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "Dùng chế độ đặc biệt, ví dụ “gdm” cho màn hình đăng nhập"
+
+#: src/main.c:437
+msgid "List possible modes"
+msgstr "Danh sách chế độ có thể"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "Chưa biết"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "Gặp lỗi khi khởi chạy “%s”"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "Mật khẩu không khớp."
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "Mật khẩu không được để trống"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "Hộp thoại xác thực bị người dùng bỏ qua"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:209
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "Phần mở rộng"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+#: subprojects/extensions-app/js/main.js:210
+msgid "Manage your GNOME Extensions"
+msgstr "Quản lý phần mở rộng Hệ vỏ GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+msgid "The GNOME Project"
+msgstr "Dự án GNOME"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"Phần mở rộng GNOME xử lý việc cập nhật tiện ích mở rộng, cấu hình tùy chọn "
+"tiện ích mở rộng và xóa hoặc vô hiệu hóa các tiện ích mở rộng không mong "
+"muốn."
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "Cấu hình phần mở rộng Hệ vỏ GNOME"
+
+#: subprojects/extensions-app/js/main.js:131
+#: subprojects/extensions-app/js/main.js:142
+msgid "No Matches"
+msgstr "Không khớp"
+
+#: subprojects/extensions-app/js/main.js:170
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "Xóa “%s”?"
+
+#: subprojects/extensions-app/js/main.js:171
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr ""
+"Nếu bạn gỡ bỏ phần mở rộng, bạn cần quay lại để tải về nếu muốn dùng lại "
+"tiện ích đó"
+
+#: subprojects/extensions-app/js/main.js:175
+msgid "Remove"
+msgstr "Xóa bỏ"
+
+#: subprojects/extensions-app/js/main.js:208
+msgid "translator-credits"
+msgstr "Nhóm Việt hóa GNOME <gnome-vi-list@gnome.org>"
+
+#: subprojects/extensions-app/js/main.js:336
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d phần mở rộng sẽ được cập nhật vào lần đăng nhập tới."
+
+#: subprojects/extensions-app/js/main.js:478
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "Phần mở rộng không tương thích với phiên bản GNOME hiện tại"
+
+#: subprojects/extensions-app/js/main.js:481
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "Phần mở rộng có lỗi"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "Hiển thị các phần mở rộng có thể cập nhật"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "Trang thông tin điện tử"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "Xóa bỏ…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "Trợ giúp"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "Giới thiệu Phần mở rộng"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"Phần mở rộng có thể là nguyên nhân dẫn đến các vấn đề về hiệu năng và ổn "
+"định. Tắt các phần mở rộng nếu hệ thống của bạn gặp trục trặc."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "Đã được cài đặt thủ công"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
+"\">extensions.gnome.org</a>."
+msgstr ""
+"Để tìm và thêm các thành phần mở rộng, truy cập <a href=\"https://extensions."
+"gnome.org\">extensions.gnome.org</a>."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "Tích-hợp"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "Không có phần mở rộng nào"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"Chúng tôi rất tiếc, nhưng không thể có được danh sách các tiện ích mở rộng "
+"đã cài đặt. Hãy chắc chắn rằng bạn đã đăng nhập vào GNOME và thử lại."
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "Cập nhật tiện ích mở rộng đã sẵn sàng"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "Đăng xuất…"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "Phần mở rộng mới đã được tạo thành công %s.\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"Tên phải là một chuỗi rất ngắn (mô tả lý tưởng).\n"
+"Ví dụ như: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "Tên"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"Mô tả là một lời giải thích một câu về những gì phần mở rộng của bạn làm.\n"
+"Ví dụ như: %s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "Mô tả"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID là mã định danh duy nhất trên toàn cầu cho tiện ích mở rộng của bạn.\n"
+"Cái này phải ở định dạng của một địa chỉ thư điện tử (clicktofocus@janedoe."
+"example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "Chọn một trong các mẫu có sẵn:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "Mẫu"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "Mã định danh duy nhất của tiện ích mở rộng mới"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "TÊN"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "Tên hiển thị của người dùng của tiện ích mở rộng mới"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "MÔ_TẢ"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "Mô tả ngắn về những gì tiện ích mở rộng làm"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "MẪU"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "Mẫu để sử dụng cho tạo tiện ích mở rộng mới"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "Nhập thông tin tiện ích mở rộng bằng tương tác"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "Tạo phần mở rộng mới"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "Chưa biết tham số"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID, tên và mô tả là bắt buộc"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "Gặp lỗi khi kết nối đến hệ vỏ GNOME\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "Phần mở rộng “%s” không sẵn có\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "Tắt phần một phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "Chưa cho UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "Đưa ra nhiều hơn một UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "Bật một phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "Phần mở rộng “%s” không tồn tại\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "Hiển thị thông tin các phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "Ghi đè lên phần mở rộng có sẵn"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "Cài đặt một mớ phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "Chưa chỉ định mớ phần mở rộng nào"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "Nhiều gói tiện ích mở rộng được chỉ định"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "Hiển thị các Phần mở rộng do người dùng cài"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "Hiển thị các Phần mở rộng do hệ thống cài"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "Hiển thị các Phần mở rộng được bật"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "Hiển thị các phần mở rộng bị tắt"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "Hiển thị các phần mở rộng cùng với cài đặt"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "Hiển thị các phần mở rộng với các cập nhật"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "In chi tiết phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "Liệt kê các phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "TẬP_TIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "Nguồn bổ sung để bao gồm vào gói"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "LƯỢC_ĐỒ"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "Nên bao gồm một lược đồ GSettings"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "THƯ_MỤC"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "Thư mục nơi tìm thấy bản dịch"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "MIỀN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "Miền gettext để sử dụng cho bản dịch"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "Ghi đè gói sẵn có"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "Thư mục nơi mà gói nên được tạo ở đó"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "THƯ_MỤC_NGUỒN"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "Tạo một mớ phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "Đã cho nhiều hơn một thư mục nguồn"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "Phần mở rộng “%s” không có phần cài đặt\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "Gặp lỗi khi mở prefs cho phần mở rộng “%s”: %s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "Mở cài đặt phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "Đặt lại một phần mở rộng"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "Không thể gỡ bỏ các mở rộng hệ thống\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "Gặp lỗi khi gỡ bỏ “%s”\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "Gỡ bỏ một phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "Đừng in thông tin lỗi"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "Đường dẫn"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "Tác giả đầu tiên"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "Phiên bản"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "Tình trạng"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version” không nhận đối số"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "Cách dùng:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "Hiển thị thông tin phiên bản rồi thoát."
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "CÂU_LỆNH"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[ĐSỐ…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "Lệnh:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "In trợ giúp"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "Bật phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "Tắt phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "Đặt lại phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "Gỡ bỏ phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "Liệt kê các phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "Hiển thị thông tin phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "Cá nhân hóa phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "Tạo phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "Đóng gói phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "Cài đặt cả mớ phần mở rộng"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "Gõ lệnh “%s” để biết thêm chi tiết.\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "Thường"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "Một phần mở rộng trống rỗng"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "Chỉ thị"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "Thêm một biểu tượng vào thanh đỉnh"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u đầu ra"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u đầu vào"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "Âm thanh hệ thống"
+
+#~ msgid "Unnamed Folder"
+#~ msgstr "Thư mục không tên"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "Bỏ khỏi danh sách ưa thích"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "Đánh dấu ưa thích"
+
+#~ msgid "Screen Lock disabled"
+#~ msgstr "Khóa màn hình bị tắt"
+
+#~ msgid "Screen Locking requires the GNOME display manager."
+#~ msgstr "Khóa màn hình yêu cầu bộ quản lý màn hình GNOME."
+
+#~ msgid "%A %B %-d"
+#~ msgstr "%A, %-d %B"
+
+#~ msgid "Swipe up to unlock"
+#~ msgstr "Quẹt để mở khóa"
+
+#~ msgid "Click or press a key to unlock"
+#~ msgstr "Bấm chuột hay nhấn một phím để mở khóa"
+
+#~ msgid "Author"
+#~ msgstr "Tác giả"
+
+#~ msgid "Warning"
+#~ msgstr "Cảnh báo"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "Tiện ích mở rộng có thể gây ra sự cố hệ thống, bao gồm các vấn đề về hiệu "
+#~ "suất. Nếu bạn gặp sự cố với hệ thống của mình, bạn nên tắt tất cả các "
+#~ "tiện ích mở rộng."
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "Gặp lỗi khi kết nối đến hệ vỏ GNOME"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "Bật introspection API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr ""
+#~ "Bật một D-Bus API cái mà cho phép tự xem xét tình trạng ứng dụng của hệ "
+#~ "vỏ."
+
+#~ msgid "Minimize"
+#~ msgstr "Thu nhỏ"
+
+#~ msgid "Unmaximize"
+#~ msgstr "Thôi phóng lớn"
+
+#~ msgid "App Picker View"
+#~ msgstr "Bộ trình bày sàng lọc ứng dụng"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr ""
+#~ "Mục lục của trình bày được chọn hiện tại trong bộ sàng lọc ứng dụng."
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr ""
+#~ "Phím ràng buộc cái mà dừng hay tiếp tục chạy, dành cho mục đích gỡ lỗi"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "Bàn phím được dùng"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "Kiểu bàn phím được dùng."
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "Copy Error"
+#~ msgstr "Sao chép lỗi"
+
+#~ msgid "Browse in Software"
+#~ msgstr "Duyệt trong “Phần mềm”"
+
+#~ msgid "Next"
+#~ msgstr "Kế tiếp"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "Đăng nhập"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "Những ứng dụng hay dùng sẽ xuất hiện ở đây"
+
+#~ msgid "Frequent"
+#~ msgstr "Hay dùng"
+
+#~ msgid "All"
+#~ msgstr "Tất cả"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%A, %-d %B, %Y"
+
+#~ msgid "Password:"
+#~ msgstr "Mật khẩu:"
+
+#~ msgid "Type again:"
+#~ msgstr "Nhập lại:"
+
+#~ msgid "Password: "
+#~ msgstr "Mật khẩu: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "Mạng không dây cần xác thực"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "Mật khẩu mạng băng thông rộng"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-intl"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%A, %d %B"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d tin nhắn mới"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d thông báo mới"
+
+#~ msgid "Off"
+#~ msgstr "Tắt"
+
+#~ msgid "On"
+#~ msgstr "Bật"
+
+#~ msgid "Account Settings"
+#~ msgstr "Cài đặt tài khoản"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "Khóa hướng"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "Có lỗi khi đang tải hộp thoại tùy thích cho %s:"
+
+#~ msgid "%s all day."
+#~ msgstr "%s cả ngày."
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s, sau đó %s."
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s, sau đó %s, tiếp theo là %s."
+
+#~ msgid "Feels like %s."
+#~ msgstr "Nhiệt độ %s."
+
+#~ msgctxt "search-result"
+#~ msgid "Power off"
+#~ msgstr "Tắt máy"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "Đăng xuất"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "Chuyển người dùng"
+
+#~ msgid "Hide tray"
+#~ msgstr "Ẩn khay"
+
+#~ msgid "Status Icons"
+#~ msgstr "Biểu tượng trạng thái"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "Chuyển hướng xác thực Web"
+
+#~ msgid "Events"
+#~ msgstr "Sự kiện"
+
+#~ msgid "Notifications"
+#~ msgstr "Thông báo"
+
+#~ msgid "Media"
+#~ msgstr "Đa phương tiện"
+
+#~ msgid "Not In Use"
+#~ msgstr "Không dùng"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "Hiện tuần trên lịch"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "Nếu được đặt, hiện tuần trong lịch theo chuẩn ISO."
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "Dùng làm kết nối internet"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "Hệ vỏ GNOME (wayland compositor)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d thiết bị đã kết nối"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "Pin"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "Chế độ máy bay"
+
+#~ msgid "Show the message tray"
+#~ msgstr "Hiện khay thông báo"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%l∶%M %p"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "CN"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "T2"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "T3"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "T4"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "T5"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "T6"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "T7"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "Không có lịch hẹn"
+
+#~ msgid "This week"
+#~ msgstr "Tuần này"
+
+#~ msgid "Next week"
+#~ msgstr "Tuần tới"
+
+#~ msgid "Removable Devices"
+#~ msgstr "Thiết bị di động"
+
+#~ msgid "Eject"
+#~ msgstr "Đẩy ra"
+
+#~ msgid "Invitation"
+#~ msgstr "Mời"
+
+#~ msgid "Call"
+#~ msgstr "Gọi"
+
+#~ msgid "File Transfer"
+#~ msgstr "Truyền tập tin"
+
+#~ msgid "Chat"
+#~ msgstr "Chat"
+
+#~ msgid "Unmute"
+#~ msgstr "Ngừng câm"
+
+#~ msgid "Mute"
+#~ msgstr "Câm"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "Mời vào phòng %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s đang mời bạn vào phòng %s"
+
+#~ msgid "Decline"
+#~ msgstr "Từ chối"
+
+#~ msgid "Accept"
+#~ msgstr "Nhận lời"
+
+#~ msgid "Video call from %s"
+#~ msgstr "Cuộc gọi có hình từ %s"
+
+#~ msgid "Call from %s"
+#~ msgstr "Cuộc gọi từ %s"
+
+#~ msgid "Answer"
+#~ msgstr "Trả lời"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s đang gửi bạn %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s muốn được phép được thông báo khi bạn trực tuyến"
+
+#~ msgid "Authentication failed"
+#~ msgstr "Lỗi xác thực"
+
+#~ msgid "Encryption error"
+#~ msgstr "Lỗi mã hóa"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "Chưa cung cấp chứng nhận"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "Chứng nhận không đáng tin"
+
+#~ msgid "Certificate expired"
+#~ msgstr "Chứng nhận hết hạn"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "Chứng nhận chưa kích hoạt"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "Tên máy của chứng nhận không khớp"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "Dấu vân tay chứng nhận không khớp"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "Chứng nhận tự ký"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "Đã đặt trạng thái là ngoại tuyến"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "Chứng nhận không hợp lệ"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "Kết nối bị từ chối"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "Không thể tạo kết nối"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "Mất kết nối"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "Tài khoản này đã kết nối đến máy chủ"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "Kết nối bị thay thế bằng một kết nối mới cùng tài nguyên"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "Tài khoản đã có trên máy chủ"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "Máy chủ đang bận không thể xử lý kết nối"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "Chứng nhận bị thu hồi"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "Chứng nhận dùng thuật toán mật mã yếu hoặc không an toàn"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "Độ dài của chứng nhận máy chủ, hoặc độ sâu của chuỗi chứng nhận, vượt quá "
+#~ "giới hạn của thư viện mật mã"
+
+#~ msgid "Internal error"
+#~ msgstr "Lỗi nội bộ"
+
+#~ msgid "View account"
+#~ msgstr "Xem tài khoản"
+
+#~ msgid "Open Calendar"
+#~ msgstr "Mở lịch"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "Cài đặt ngày giờ"
+
+#~ msgid "Open"
+#~ msgstr "Mở"
+
+#~ msgid "Clear Messages"
+#~ msgstr "Xóa thông báo"
+
+#~ msgid "Notification Settings"
+#~ msgstr "Cài đặt thông báo"
+
+#~ msgid "Tray Menu"
+#~ msgstr "Trình đơn khay"
+
+#~ msgid "No Messages"
+#~ msgstr "Không có thông báo"
+
+#~ msgid "Message Tray"
+#~ msgstr "Khay thông báo"
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "Liệt kê các các-ta-lốc mà nó có thể hiển thị như một thư mục"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "Mỗi tên các-ta-lốc trong danh sách này sẽ được xuất hiện như là một thư "
+#~ "mục trong trình bày ứng dụng, thay vì hiển thị cùng dòng trong trình bày "
+#~ "chính. "
+
+#~ msgid ""
+#~ "Internally used to store the last IM presence explicitly set by the user. "
+#~ "The value here is from the TpConnectionPresenceType enumeration."
+#~ msgstr ""
+#~ "Dùng để lưu nội bộ sự hiện diện IM lần cuối do người dùng đặt. Giá trị ở "
+#~ "đây từ enumeration TpConnectionPresenceType."
+
+#~ msgid ""
+#~ "Internally used to store the last session presence status for the user. "
+#~ "The value here is from the GsmPresenceStatus enumeration."
+#~ msgstr ""
+#~ "Dùng để lưu nội bộ sự hiện diện IM lần cuối do người dùng đặt. Giá trị ở "
+#~ "đây từ enumeration GsmPresenceStatus."
+
+#~ msgid "Keybinding to toggle the screen recorder"
+#~ msgstr "Phím để bật/tắt trình quay phim màn hình"
+
+#~ msgid "Keybinding to start/stop the builtin screen recorder."
+#~ msgstr "Phím để quay/dừng việc quay phim màn hình."
+
+#~ msgid "Framerate used for recording screencasts."
+#~ msgstr "Tốc độ khung hình sẽ dùng khi ghi screencast."
+
+#~ msgid ""
+#~ "The framerate of the resulting screencast recordered by GNOME Shell's "
+#~ "screencast recorder in frames-per-second."
+#~ msgstr ""
+#~ "Tốc độ khung hình của screencast được ghi bởi bộ thu Hệ vỏ GNOME tính "
+#~ "theo số khung mỗi giây."
+
+#~ msgid "The gstreamer pipeline used to encode the screencast"
+#~ msgstr "Ống dẫn gstreamer dùng để thu screencast"
+
+#~ msgid ""
+#~ "Sets the GStreamer pipeline used to encode recordings. It follows the "
+#~ "syntax used for gst-launch. The pipeline should have an unconnected sink "
+#~ "pad where the recorded video is recorded. It will normally have a "
+#~ "unconnected source pad; output from that pad will be written into the "
+#~ "output file. However the pipeline can also take care of its own output - "
+#~ "this might be used to send the output to an icecast server via shout2send "
+#~ "or similar. When unset or set to an empty value, the default pipeline "
+#~ "will be used. This is currently 'vp8enc min_quantizer=13 max_quantizer=13 "
+#~ "cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' and records to "
+#~ "WEBM using the VP8 codec. %T is used as a placeholder for a guess at the "
+#~ "optimal thread count on the system."
+#~ msgstr ""
+#~ "Đặt ống dẫn GStreamer dùng để thu hình. Tuân theo cú pháp gst-launch. Ống "
+#~ "dẫn nên có pad sink không kết nối, nơi bắt đầu thu. Bình thường cũng nên "
+#~ "có source pad không kết nối; đầu ra từ pad sẽ được ghi vào tập tin kết "
+#~ "quả. Tuy nhiên ống dẫn có thể tự lưu đầu ra riêng - có thể hữu dụng để "
+#~ "gửi kết quả đến máy chủ icecast thông qua shout2send hoặc tương tự. Nếu "
+#~ "bỏ chọn hoặc đặt giá trị rỗng, ống dẫn mặc định sẽ được dùng, hiện thời "
+#~ "là “vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 "
+#~ "threads=%T ! queue ! webmmux” và lưu ở dạng WEBM dùng VP8 codec. %T được "
+#~ "dùng để thế chỗ cho số thread dự đoán tối ưu cho hệ thống."
+
+#~ msgid "File extension used for storing the screencast"
+#~ msgstr "Phần mở rộng dùng để lưu screencast"
+
+#~ msgid ""
+#~ "The filename for recorded screencasts will be a unique filename based on "
+#~ "the current date, and use this extension. It should be changed when "
+#~ "recording to a different container format."
+#~ msgstr ""
+#~ "Tên tập tin cho screencast được lưu sẽ là tên độc nhất dự trên ngày hiện "
+#~ "thời, và dùng phần mở rộng này. Khi lưu ở định dạng khác nhau thì tên này "
+#~ "cũng nên khác nhau."
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "Sắp đặt các nút trên thanh tiêu đề"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "Khóa này đè lên khóa có trong org.gnome.desktop.wm.preferences khi chạy "
+#~ "hệ vỏ GNOME."
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "Chọn phần mở rộng để cấu hình dùng hộp chọn."
+
+#~ msgid "Power"
+#~ msgstr "Nguồn"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%d</b> <b>%B</b>, <b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%H:%M</b>, <b>%d</b> <b>%B</b> <b>%Y</b> "
+
+#~ msgid "Click Log Out to quit these applications and log out of the system."
+#~ msgstr ""
+#~ "Nhấn Đăng xuất để thoát những ứng dụng này và đăng xuất khỏi hệ thống."
+
+#~ msgid "Logging out of the system."
+#~ msgstr "Đang xuất khỏi hệ thống."
+
+#~ msgid "Click Power Off to quit these applications and power off the system."
+#~ msgstr "Nhấn Tắt máy để thoát những ứng dụng này và tắt máy."
+
+#~ msgid "Powering off the system."
+#~ msgstr "Tắt máy."
+
+#~ msgid "Click Restart to quit these applications and restart the system."
+#~ msgstr ""
+#~ "Bấm vào \"Khởi động lại\" để thoát khỏi những ứng dụng này và khởi động "
+#~ "lại hệ thống."
+
+#~ msgid "Restarting the system."
+#~ msgstr "Khởi động lại hệ thống."
+
+#~ msgid "Universal Access Settings"
+#~ msgstr "Cài đặt hỗ trợ truy cập"
+
+#~ msgid "Visibility"
+#~ msgstr "Tầm nhìn"
+
+#~ msgid "Set Up a New Device…"
+#~ msgstr "Cài đặt thiết bị mới…"
+
+#~ msgid "Send Files…"
+#~ msgstr "Gửi tập tin…"
+
+#~ msgid "Keyboard Settings"
+#~ msgstr "Cài đặt bàn phím"
+
+#~ msgid "Mouse Settings"
+#~ msgstr "Cài đặt chuột"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "Yêu cầu cấp quyền từ %s"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "Thiết bị %s muốn kết nối với máy tính này"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "Thiết bị %s muốn truy cập dịch vụ “%s”"
+
+#~ msgid "Grant this time only"
+#~ msgstr "Chỉ cho phép lần này"
+
+#~ msgid "Reject"
+#~ msgstr "Từ chối"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "Xác nhận kết nối cho %s"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr ""
+#~ "Vui lòng xác nhận Passkey “%06d” nào tương ứng với nó trên thiết bị."
+
+#~ msgid "Does not match"
+#~ msgstr "Không khớp"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "Yêu cầu kết nối cho %s"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "Vui lòng nhập số PIN ghi trên thiết bị."
+
+#~ msgid "OK"
+#~ msgstr "OK"
+
+#~ msgid "Volume, network, battery"
+#~ msgstr "Đĩa, mạng, pin"
+
+#~ msgid "Wi-Fi"
+#~ msgstr "Wi-Fi"
+
+#~ msgid "disabled"
+#~ msgstr "tắt"
+
+#~ msgid "unavailable"
+#~ msgstr "không có mặt"
+
+#~ msgid "More…"
+#~ msgstr "Nữa…"
+
+#~ msgid "Wired"
+#~ msgstr "Mạng dây"
+
+#~ msgid "%d %s %d %s remaining"
+#~ msgstr "%d %s %d %s còn lại"
+
+#~ msgid "hour"
+#~ msgid_plural "hours"
+#~ msgstr[0] "giờ"
+
+#~ msgid "minute"
+#~ msgid_plural "minutes"
+#~ msgstr[0] "phút"
+
+#~ msgid "AC Adapter"
+#~ msgstr "Nguồn AC"
+
+#~ msgid "Laptop Battery"
+#~ msgstr "Pin Máy tính xách tay"
+
+#~ msgid "Monitor"
+#~ msgstr "Màn hình"
+
+#~ msgid "Mouse"
+#~ msgstr "Chuột"
+
+#~ msgid "PDA"
+#~ msgstr "PDA"
+
+#~ msgid "Cell Phone"
+#~ msgstr "Điện thoại di động"
+
+#~ msgid "Media Player"
+#~ msgstr "Máy nghe nhạc"
+
+#~ msgid "Tablet"
+#~ msgstr "Bảng"
+
+#~ msgid "Computer"
+#~ msgstr "Máy tính"
+
+#~ msgctxt "device"
+#~ msgid "Unknown"
+#~ msgstr "Không biết"
+
+#~ msgid "Available"
+#~ msgstr "Có mặt"
+
+#~ msgid "Busy"
+#~ msgstr "Bận"
+
+#~ msgid "Invisible"
+#~ msgstr "Giấu mặt"
+
+#~ msgid "Away"
+#~ msgstr "Vắng mặt"
+
+#~ msgid "Idle"
+#~ msgstr "Nhàn rỗi"
+
+#~ msgid "Your chat status will be set to busy"
+#~ msgstr "Trạng thái nhắn tin của bạn sẽ được đặt là bận"
+
+#~ msgid ""
+#~ "Notifications are now disabled, including chat messages. Your online "
+#~ "status has been adjusted to let others know that you might not see their "
+#~ "messages."
+#~ msgstr ""
+#~ "Thông báo đã bị tắt, bao gồm tin nhắn. Trạng thái trực tuyến của bạn đã "
+#~ "được điều chỉnh để mọi người biết bạn sẽ không xem tin nhắn họ gửi đến."
+
+#~ msgid "Shutting down might cause them to lose unsaved work."
+#~ msgstr "Tắt sẽ làm mất những kết quả chưa được lưu."
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Rất tiếc, hôm nay không có lời thông thái nào:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "Nhà tiên tri %s nói"
+
+#~ msgid "Whether to collect stats about applications usage"
+#~ msgstr "Có thu thập thống kê sử dụng ứng dụng không"
+
+#~ msgid ""
+#~ "The shell normally monitors active applications in order to present the "
+#~ "most used ones (e.g. in launchers). While this data will be kept private, "
+#~ "you may want to disable this for privacy reasons. Please note that doing "
+#~ "so won't remove already saved data."
+#~ msgstr ""
+#~ "GNOME Shell bình thường sẽ theo dõi các ứng dụng tích cực để thể hiện phù "
+#~ "hợp (ví dụ, bộ phóng). Mặc dù thông tin này là riêng tư, bạn vẫn có thể "
+#~ "tắt vì lý do cá nhân. Chú ý là làm vậy sẽ không xóa bỏ những dữ liệu đã "
+#~ "có."
+
+#~ msgid "cable unplugged"
+#~ msgstr "cáp bị tháo"
+
+#~ msgid "Auto Ethernet"
+#~ msgstr "Ethernet tự động"
+
+#~ msgid "Auto broadband"
+#~ msgstr "Băng thông rộng tự động"
+
+#~ msgid "Auto dial-up"
+#~ msgstr "Quay số tự động"
+
+#~ msgid "Auto %s"
+#~ msgstr "%s tự động"
+
+#~ msgid "Auto bluetooth"
+#~ msgstr "Bluetooth tự động"
+
+#~ msgid "Auto wireless"
+#~ msgstr "Mạng không dây tự động"
+
+#~ msgctxt "title"
+#~ msgid "Sign In"
+#~ msgstr "Đăng nhập"
+
+#~ msgid "tray"
+#~ msgstr "khay"
+
+#~ msgid "More..."
+#~ msgstr "Nữa…"
+
+#~ msgid "disabled OpenSearch providers"
+#~ msgstr "Phần cung cấp OpenSearch bị tắt"
+
+#~ msgid "APPLICATIONS"
+#~ msgstr "ỨNG DỤNG"
+
+#~ msgid "SETTINGS"
+#~ msgstr "CÀI ĐẶT"
+
+#~ msgctxt "event list time"
+#~ msgid "%H:%M"
+#~ msgstr "%H:%M"
+
+#~ msgid "Failed to unmount '%s'"
+#~ msgstr "Lỗi bỏ gắn kết “%s”"
+
+#~ msgid "Retry"
+#~ msgstr "Thử lại"
+
+#~ msgid "PLACES & DEVICES"
+#~ msgstr "ĐỊA ĐIỂM & THIẾT BỊ"
+
+#~ msgid "Browse Files..."
+#~ msgstr "Duyệt tập tin…"
+
+#~ msgid "Error browsing device"
+#~ msgstr "Lỗi duyệt tập tin"
+
+#~ msgid "The requested device cannot be browsed, error is '%s'"
+#~ msgstr "Không thể duyệt thiết bị yêu cầu , lỗi là “%s”"
+
+#~ msgid "Wireless"
+#~ msgstr "Mạng không dây"
+
+#~ msgid "VPN Connections"
+#~ msgstr "Kết nối VPN"
+
+#~ msgid "Subscription request"
+#~ msgstr "Yêu cầu đăng ký"
+
+#~ msgid "Sent at <b>%X</b> on <b>%A</b>"
+#~ msgstr "Đã gửi <b>%X</b> lúc <b>%A</b>"
+
+#~ msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
+#~ msgstr "Đã gửi <b>%A</b>, <b>%d %B</b>, %Y"
+
+#~ msgid "Connection to %s failed"
+#~ msgstr "Kết nối %s thất bại"
+
+#~ msgid "Reconnect"
+#~ msgstr "Tái kết nối"
+
+#~ msgid "System Settings"
+#~ msgstr "Cài đặt hệ thống"
+
+#~ msgid "Your favorite Easter Egg"
+#~ msgstr "Trứng phục sinh yêu thích của bạn"
+
+#~ msgid "United Kingdom"
+#~ msgstr "Vương quốc Anh"
+
+#~ msgid "Default"
+#~ msgstr "Mặc định"
+
+#~ msgid "%1$s: %2$s"
+#~ msgstr "%1$s: %2$s"
+
+#~ msgid "Connect to..."
+#~ msgstr "Kết nối đến…"
+
+#~ msgid "Passphrase"
+#~ msgstr "Mật khẩu"
+
+#~ msgid "%s is online."
+#~ msgstr "%s đang trực tuyến."
+
+#~ msgid "%s is offline."
+#~ msgstr "%s đã ngoại tuyến."
+
+#~ msgid "%s is away."
+#~ msgstr "%s đi vắng."
+
+#~ msgid "%s is busy."
+#~ msgstr "%s bận."
+
+#~ msgid "Show time with seconds"
+#~ msgstr "Hiện giây"
+
+#~ msgid "If true, display seconds in time."
+#~ msgstr "Nếu đúng (true), hiện giây."
+
+#~ msgid "Show date in clock"
+#~ msgstr "Hiện ngày trên đồng hồ"
+
+#~ msgid "If true, display date in the clock, in addition to time."
+#~ msgstr "Nếu đúng (true), hiện ngày trên đồng hồ, bên cạnh giờ."
+
+#~ msgid "CONTACTS"
+#~ msgstr "LIÊN LẠC"
+
+#~ msgid "%a %b %e, %R:%S"
+#~ msgstr "%a %R:%S, %e %b"
+
+#~ msgid "%a %b %e, %R"
+#~ msgstr "%a %R, %e %b"
+
+#~ msgid "%a %R:%S"
+#~ msgstr "%a %R:%S"
+
+#~ msgid "%a %R"
+#~ msgstr "%a %R"
+
+#~ msgid "%a %b %e, %l:%M:%S %p"
+#~ msgstr "%a %e %b, %l:%M:%S %p"
+
+#~ msgid "%a %l:%M:%S %p"
+#~ msgstr "%a %l:%M:%S %p"
+
+#~ msgid "Wrong password, please try again"
+#~ msgstr "Nhập sai mật khẩu, xin hãy thử lại"
+
+#~ msgid "Hidden"
+#~ msgstr "Ẩn"
+
+#~ msgid "Power Off..."
+#~ msgstr "Tắt máy…"
+
+#~ msgid "Online Accounts"
+#~ msgstr "Tài khoản trực tuyến"
+
+#~ msgid "Log Out..."
+#~ msgstr "Đăng xuất…"
+
+#~ msgid "RECENT ITEMS"
+#~ msgstr "MỤC GẦN ĐÂY"
+
+#~ msgid ""
+#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
+#~ "which should be loaded. disabled-extensions overrides this setting for "
+#~ "extensions that appear in both lists."
+#~ msgstr ""
+#~ "Phần mở rộng GNOME Shell có thuộc tính uuid. Khóa này liệt kê danh sách "
+#~ "phần mở rộng cần phải nạp. disabled-extensions ghi đè vào khóa này đối "
+#~ "với những phần mở rộng có mặt ở cả hai danh sách."
+
+#~ msgid "Show password"
+#~ msgstr "Hiện mật khẩu"
diff --git a/po/zh_CN.po b/po/zh_CN.po
new file mode 100644
index 0000000..9cf7d6a
--- /dev/null
+++ b/po/zh_CN.po
@@ -0,0 +1,3346 @@
+# Chinese (China) translation for gnome-shell.
+# Copyright (C) 2010-2021 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# jiero <lililjlj@gmail.com>, 2010.
+# Jessica Ban <bancage@gmail.com>, 2010.
+# zhang ping <zhangping159@gmail.com>, 2010.
+# 指冷玉笙寒 (dhyang) <dhyang555@gmail.com>, 2010
+# Ray Wang <raywang@gnome.org>, 2009, 2011.
+# 李炜 <lw124124@gmail.com>, 2011.
+# chiachen <luojiazhen@gmail.com>, 2011.
+# StarBrilliant <m13253@hotmail.com>, 2011
+# YunQiang Su <wzssyqa@gmail.com>, 2010, 2011, 2012.
+# Wylmer Wang <wantinghard@gmail.com>, 2013, 2014.
+# hui <www.eternalhui@gmail.com>, 2013, 2014.
+# mosquito <sensor.wen@gmail.com>, 2014.
+# Tong Hui <tonghuix@gmail.com>, 2013, 2014, 2015.
+# Aron Xu <aronxu@gnome.org>, 2010, 2011, 2012, 2013, 2015, 2016.
+# liushuyu <liushuyu_011@126.com>, 2015, 2016.
+# Mingcong Bai <jeffbai@aosc.xyz>, 2015, 2016, 2017.
+# Dingzhong Chen <wsxy162@gmail.com>, 2018-2021.
+# Liu Tao <lyuutau@outlook.com>, 2021.
+# lumingzh <lumingzh@qq.com>, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2023-04-05 09:08+0000\n"
+"PO-Revision-Date: 2023-04-10 15:40-0400\n"
+"Last-Translator: Boyuan Yang <073plan@gmail.com>\n"
+"Language-Team: Chinese - China <i18n-zh@googlegroups.com>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-DL-Team: zh_CN\n"
+"X-DL-Module: gnome-shell\n"
+"X-DL-Branch: main\n"
+"X-DL-Domain: po\n"
+"X-DL-State: Translating\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "启动器"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "激活收藏夹应用程序 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "激活收藏夹应用程序 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "激活收藏夹应用程序 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "激活收藏夹应用程序 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "激活收藏夹应用程序 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "激活收藏夹应用程序 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "激活收藏夹应用程序 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "激活收藏夹应用程序 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "激活收藏夹应用程序 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2072
+msgid "Screenshots"
+msgstr "截图"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "交互式截图"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "截图"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "对窗口进行截图"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "交互式录屏"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "系统"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "显示通知列表"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "聚焦到活动通知"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "显示概览"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "显示全部应用程序"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "打开应用程序菜单"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "窗口管理与应用程序启动"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "启用从 Alt-F2 调用对开发者和测试者有用的内部工具"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "允许使用 Alt-F2 对话框访问内部调试和监视工具。"
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "要启用的扩展的 UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell 扩展有一个 UUID 属性,此键列出了应该加载的扩展。任何需要加载的扩"
+"展都需出现在此列表中。您还可以通过 DBus 的方式对 org.gnome.Shell 里的 "
+"EnableExtension 和 DisableExtension 的值进行修改,以修改此列表。"
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "要强制禁用的扩展的 UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"GNOME Shell 扩展有一个 UUID 属性,此键列出了应该禁用的扩展,不管它们是否在当"
+"前模式中已加载。您还可以通过 DBus 的方式对 org.gnome.Shell 里的 "
+"EnableExtension 和 DisableExtension 的值进行修改,以修改此列表。此键优先"
+"于“enabled-extensions”设置。"
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "禁用用户扩展"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr "禁用所有用户启用的扩展而不影响“enabled-extension”键值的设置。"
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "禁用检查扩展版本兼容性"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME 只会加载声明支持当前运行版本的扩展。启用该选项会禁止此检查,并尝试加载"
+"所有扩展,无视它们声明支持的版本。"
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "收藏夹中应用程序桌面文件的 ID 列表"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "符合这些标示的应用程序将显示在收藏区中。"
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "命令对话框 (Alt-F2) 的历史记录"
+
+# http://live.gnome.org/GnomeShell/LookingGlass
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Looking Glass 控制台历史"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "总在用户菜单中显示“注销”选项。"
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"单用户、单会话情形下,“注销”菜单项会自动隐藏,使用这个键会覆盖这种行为。"
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "是否记住挂载加密文件系统或远程文件系统时所使用的密码"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"挂载加密设备或远程文件系统时 Shell 将要求输入密码。如果该密码可以记住并在今后"
+"使用,则会有“记住密码”复选框出现。这个键设置了该复选框的默认状态。"
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "上次选择的非默认性能配置文件"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"一些系统支持多于两种性能配置文件。为了仍然支持在两种配置文件间切换,该设置键"
+"记录上次选择的非默认配置文件。"
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "上一次“欢迎使用 GNOME”对话框显示时对应的版本"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"本键位确定上一次“欢迎使用 GNOME”对话框显示时对应的版本。如字符串为空,则默认"
+"对应最旧的版本;一个非常大的数字指代尚未存在的版本。使用大数字可以等效于禁用"
+"该对话框。"
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "应用选择器的布局"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"应用选择器的布局。列表中的每个条目都是一个页面。页面按在 GNOME Shell 中显示的"
+"顺序存储。每个页面都包含一个“应用程序 ID”→“数据”对。目前,以下值存储为“数"
+"据”:•“位置\":应用程序图标在页面中的位置"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "用于打开应用程序菜单的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "用于打开应用程序菜单的快捷键。"
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "用于在概览状态之间切换的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "用于在会话、窗口拾取器和应用网格之间切换的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "用于在应用网格、窗口拾取器和会话之间切换的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "用于打开“显示应用程序”视图的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "用于打开活动概览中的“显示应用程序”视图的快捷键。"
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "用于打开概览的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "用于打开活动概览的快捷键。"
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "用来切换通知列表是否可见的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "用来切换通知列表是否可见的快捷键。"
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "用于聚焦到活动通知的快捷键"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "用于聚焦到活动通知的快捷键。"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "切换到应用程序 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "切换到应用程序 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "切换到应用程序 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "切换到应用程序 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "切换到应用程序 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "切换到应用程序 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "切换到应用程序 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "切换到应用程序 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "切换到应用程序 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "仅在当前工作区切换。"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr "如果设为 true,则切换器只显示当前工作区中的应用。否则,显示所有应用。"
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "应用程序图标模式。"
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"配置窗口如何显示于切换器中。有效的值有“thumbnail-only”“app-icon-"
+"only”和“both”。"
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr "如果设为 true,切换器只显示当前工作区的窗口。否则,包括所有窗口。"
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "位置"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "要在世界时钟中显示的位置"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "自动定位"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "是否获取当前的位置"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "位置"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "要显示天气预报的位置"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "将模态对话框附到其父窗口上"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "当 GNOME Shell 运行时,此键会覆盖 org.gnome.mutter 中的键。"
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "启用拖拽窗口到屏幕边缘时边缘平铺的功能"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "动态地管理工作区"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "仅在主显示器上显示工作区"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "将鼠标模式下焦点的更改推迟到指针停止移动之后"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "网络登录"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "出问题了"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"非常抱歉,但是出现了问题:此扩展的设置无法显示。我们建议您将问题报告给扩展的"
+"作者。"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "技术细节"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "主页"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "访问扩展主页"
+
+#: js/gdm/authPrompt.js:146 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "取消"
+
+#: js/gdm/authPrompt.js:312 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "密码"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "选择会话"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "未列出?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(如 user 或 %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "用户名"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "登录窗口"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "认证出错"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:604
+msgid "(or swipe finger across reader)"
+msgstr "(或将手指划过指纹读取器)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:609
+msgid "(or place finger on reader)"
+msgstr "(或将手指置于指纹读取器上)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "关机"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;关机;断电;挂起;停止"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "重启"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;重启;重新启动;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "锁屏"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen;锁屏"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "注销"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;登出;注销;退出"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "挂起"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;挂起;睡眠"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "切换用户"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "switch user;切换用户"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr "lock orientation;screen;rotation;锁定方向;屏幕;旋转"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "截图"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;截图;截屏;屏幕截图;屏幕录制;录屏;录"
+"制;剪辑;捕获;记录;"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "解锁屏幕旋转"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "锁定屏幕旋转"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "命令未找到"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "不能解析命令:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "执行“%s”失败:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "刚刚"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d 分钟前"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d 小时前"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "昨天"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d 天前"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d 周前"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d 月前"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d 年前"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "昨天 %H∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-m 月 %-d 日,%H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%Y 年 %-m 月 %-d 日,%H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%p %-l∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "昨天%p %-l∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A%p %-l∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-m 月 %-d 日,%p %-l∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%Y 年 %-m 月 %-d 日,%p %-l∶%M"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "热点登录"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr "您到热点登录的连接不安全。您在此页面输入的密码或其他信息可被旁人查看。"
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "拒绝访问"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "允许访问"
+
+#: js/ui/appDisplay.js:1731
+msgid "Unnamed Folder"
+msgstr "未命名文件夹"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "%s 已固定到快捷栏。"
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "%s 已从快捷栏移除。"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "打开的窗口"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "新建窗口"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "显示细节"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "退出"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "取消固定"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "固定到快捷栏"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "使用集成显卡启动"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "使用独立显卡启动"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "选择声音设备"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "声音设置"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "耳机"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "耳麦"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:322
+msgid "Microphone"
+msgstr "麦克风"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "更换壁纸…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "显示设置"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "设置"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "日"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "一"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "二"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "三"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "四"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "五"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "六"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%Y %OB"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "上个月"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "下个月"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "第 %V 周"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "无通知"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "请勿打扰"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "清除"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "“%s”无响应。"
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "您可以尝试等待其响应或强制退出此应用程序。"
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "强制退出"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "等待"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "外部驱动器已连接"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "外部驱动器已断开"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "无法解锁卷"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "安装的 udisks 版本不支持 PIM 设置"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "使用 %s 打开"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "此外,您也可以通过按下您路由器的“WPS”按钮来连接。"
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "连接"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "密钥"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "私钥密码"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "身份"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "服务"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "需要认证"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "访问无线网络“%s”需要密码或密钥。"
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "有线 802.1X 认证"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "网络名称"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL 认证"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "需要 PIN 码"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "移动宽带设备需要 PIN 码"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "连接到“%s”需要密码。"
+
+#: js/ui/components/networkAgent.js:736 js/ui/status/network.js:1960
+msgid "Network Manager"
+msgstr "网络管理器"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN 密码"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "需要认证"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "管理员"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "认证"
+
+# 不一定是密码错误。
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "抱歉,认证失败。请重试。"
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s 现在叫做 %s"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "窗口"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "显示应用程序"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%Y 年 %-m 月 %-d 日"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%Y 年 %-m 月 %-d 日 %A"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-m 月 %-d 日"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y 年 %-m 月 %-d 日"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "今天"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "明天"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "全天"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%-m 月 %-d 日"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "无事件"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "添加世界时钟…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "世界时钟"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "正在载入…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "通过互联网查看天气信息"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "天气信息目前不可用"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "天气"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "选择天气地点…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "注销 %s"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "注销"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s 将在 %d 秒后自动注销。"
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "您将在 %d 秒后自动注销。"
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "注销"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "关机"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "安装更新并关机"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "系统将在 %d 秒后自动关机。"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "安装挂起的软件更新"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "关机"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "重启"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "安装更新并重启"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "系统将在 %d 秒后自动重启。"
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "重启"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "重启并安装更新"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "系统将在 %d 秒后自动重启并安装更新。"
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "重启并安装"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "安装并关机"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "安装更新后关机"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "重启并安装升级"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"重启后将安装 %s %s。升级安装可能需要较长时间:请确保已经进行了备份并且已经插"
+"入了电源。"
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "电池电量低:请在安装更新前插入电源。"
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "有些应用程序正忙或有未保存的工作"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "有其他用户登录"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "启动选项"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s(远程)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s(控制台)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "安装"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "安装扩展"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "从 extensions.gnome.org 下载并安装“%s”?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "扩展更新可用"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "扩展更新已做好安装准备。"
+
+#: js/ui/inhibitShortcutsDialog.js:75
+msgid "Allow inhibiting shortcuts"
+msgstr "允许禁用快捷键"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:78
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "应用程序 %s 希望禁用快捷键"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "An application wants to inhibit shortcuts"
+msgstr "某应用程序希望禁用快捷键"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:86
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "按 %s 以恢复快捷键。"
+
+#: js/ui/inhibitShortcutsDialog.js:97
+msgid "Deny"
+msgstr "拒绝"
+
+#: js/ui/inhibitShortcutsDialog.js:106
+msgid "Allow"
+msgstr "允许"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "慢速键已开启"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "慢速键已关闭"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"您刚刚按住了 Shift 键 8 秒钟。这是启用慢速键特性的快捷方式,这会影响您键盘的"
+"使用。"
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "粘滞键已开启"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "粘滞键已关闭"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"您刚刚按了 Shift 键五次。这是启用粘滞键特性的快捷方式,这会影响您键盘的使用。"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"您刚刚一次按了两个键或者按了 Shift 键五次。这将会关闭粘滞键特性,这会影响您使"
+"用键盘的方式。"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "保持开启"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "开启"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "关闭"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "保持关闭"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "区域与语言设置"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "未安装扩展"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s 没有传出任何错误信息。"
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "隐藏错误"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "显示错误"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "已启用"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1908
+msgid "Disabled"
+msgstr "已禁用"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "出错"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "过时"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "下载中"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "查看源"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "网页"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "系统已置于不安全模式"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "应用程序现在已不受访问限制"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "撤消"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "以特权用户身份登录"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"出于安全原因,应避免以特权用户身份运行会话。如有可能,你应该以正常用户身份登"
+"录。"
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "屏幕锁定已禁用"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "屏幕锁定需要 GNOME 显示管理器。"
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "系统信息"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "未知艺人"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "未知标题"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "输入以搜索"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "应用程序"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "概览"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "新建快捷键…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "应用程序定义"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "显示屏幕帮助"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "切换显示器"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "分配按键"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "完成"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "编辑…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "无"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "按下按键以配置"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "按 Esc 以退出"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "按任意键退出"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "活动"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "系统"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "顶栏"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "运行一个命令"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "按 Esc 以关闭"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "Wayland 下重启不可用"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "正在重启…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME 需要锁定屏幕"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "无法锁定"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "一个应用程序阻止了锁定"
+
+#: js/ui/screenshot.js:1161
+msgid "Selection"
+msgstr "选区"
+
+#: js/ui/screenshot.js:1171
+msgid "Area Selection"
+msgstr "选择区域"
+
+#: js/ui/screenshot.js:1176
+msgid "Screen"
+msgstr "屏幕"
+
+#: js/ui/screenshot.js:1186
+msgid "Screen Selection"
+msgstr "选择屏幕"
+
+#: js/ui/screenshot.js:1191
+msgid "Window"
+msgstr "窗口"
+
+#: js/ui/screenshot.js:1201
+msgid "Window Selection"
+msgstr "选择窗口"
+
+#: js/ui/screenshot.js:1239
+msgid "Screenshot / Screencast"
+msgstr "截图/录屏"
+
+#: js/ui/screenshot.js:1275
+msgid "Show Pointer"
+msgstr "显示指针"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1866
+msgid "Screencasts"
+msgstr "录屏"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1871
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "录屏 %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1913 js/ui/screenshot.js:2125
+msgid "Screenshot"
+msgstr "截图"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1919
+msgid "Screencast recorded"
+msgstr "屏幕已录制"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1921
+msgid "Click here to view the video."
+msgstr "单击这里查看该视频。"
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1924 js/ui/screenshot.js:2139
+msgid "Show in Files"
+msgstr "显示文件"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2085
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "截图 %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2131
+msgid "Screenshot captured"
+msgstr "已获取截图"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2133
+msgid "You can paste the image from the clipboard."
+msgstr "您可以从剪贴板粘贴该图像。"
+
+#: js/ui/screenshot.js:2186 js/ui/screenshot.js:2351
+msgid "Screenshot taken"
+msgstr "已获取屏幕截图"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "正在搜索…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "无结果。"
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "还有另外 %d 项"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "搜索"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "复制"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "粘贴"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "显示文本"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "隐藏文本"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "大写锁定已开启。"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "隐藏卷"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows 系统卷"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "使用密钥文件"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "要解锁使用密钥文件的卷,请改用 <i>%s</i> 实用工具。"
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM 号码"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "记住密码"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "解锁"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "打开 %s"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM 必须是数字或留空。"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "无法启动 %s"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "找不到 %s 应用程序"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "辅助功能"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "高对比度"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "缩放"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "屏幕阅读器"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "屏幕键盘"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "视觉响铃"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "粘滞键"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "慢速键"
+
+# win10 使用这个翻译,在谷歌搜索上分别搜索回弹键和筛选键,回弹键的结果比较多。
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "回弹键"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "鼠标按键"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "大号文本"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "自动旋转"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "蓝牙"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "亮度"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "暗色模式"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "单击"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "双击"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "拖动"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "副键点击"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "悬停点击"
+
+#: js/ui/status/keyboard.js:842
+msgid "Keyboard"
+msgstr "键盘"
+
+#: js/ui/status/keyboard.js:859
+msgid "Show Keyboard Layout"
+msgstr "显示键盘布局"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "允许获取位置信息"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "应用 %s 想要获取您的位置信息"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "位置访问权限可以随时在隐私设置里更改。"
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<未知>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "断开与 %s 的连接"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "连接至 %s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1113
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s 热点"
+
+#: js/ui/status/network.js:1472 js/ui/status/network.js:1488
+msgid "VPN"
+msgstr "VPN"
+
+#: js/ui/status/network.js:1473
+msgid "VPN Settings"
+msgstr "VPN 设置"
+
+#: js/ui/status/network.js:1722
+msgid "Wi–Fi"
+msgstr "Wi–Fi"
+
+#: js/ui/status/network.js:1724
+msgid "All Networks"
+msgstr "所有网络"
+
+#: js/ui/status/network.js:1821
+msgid "Wired Connections"
+msgstr "有线连接"
+
+#: js/ui/status/network.js:1822
+msgid "Wired Settings"
+msgstr "有线设置"
+
+#: js/ui/status/network.js:1836
+msgid "Bluetooth Tethers"
+msgstr "蓝牙网络共享"
+
+#: js/ui/status/network.js:1837
+msgid "Bluetooth Settings"
+msgstr "蓝牙设置"
+
+#: js/ui/status/network.js:1851
+msgid "Mobile Connections"
+msgstr "移动连接"
+
+#: js/ui/status/network.js:1853
+msgid "Mobile Broadband Settings"
+msgstr "移动宽带设置"
+
+#: js/ui/status/network.js:1965
+msgid "Connection failed"
+msgstr "连接失败"
+
+#: js/ui/status/network.js:1966
+msgid "Activation of network connection failed"
+msgstr "网络连接激活失败"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "夜灯"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "性能"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "平衡"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "节电"
+
+#: js/ui/status/powerProfiles.js:70
+msgid "Power Profiles"
+msgstr "性能配置文件"
+
+#: js/ui/status/remoteAccess.js:72
+msgid "Stop Screencast"
+msgstr "停止屏幕录制"
+
+#: js/ui/status/remoteAccess.js:142
+msgid "Stop Screen Sharing"
+msgstr "停止屏幕共享"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "飞行模式"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "截图"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "关机菜单"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "挂起"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "重启…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "关机…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "注销…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "切换用户…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "锁屏"
+
+# 这是英特尔的注册商标名,暂无官方译名。
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "雷电接口"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "未知的雷电接口设备"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr "您不在的时候检测到了新设备。请断开设备连接后重连来开始使用它。"
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "未授权的雷电接口设备"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "检测到新设备,需要管理员授权。"
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "雷电接口授权错误"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "无法授权雷电接口设备:%s"
+
+#: js/ui/status/volume.js:194
+msgid "Volume changed"
+msgstr "音量已变更"
+
+#: js/ui/status/volume.js:256
+msgid "Volume"
+msgstr "音量"
+
+#: js/ui/status/volume.js:272
+msgid "Sound Output"
+msgstr "声音输出"
+
+#: js/ui/status/volume.js:340
+msgid "Sound Input"
+msgstr "声音输入"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "镜像"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "拼接显示器"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "只使用外部"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "只使用内置"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%-m 月 %-d 日 %A"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "向上滑动解锁"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "单击或按键解锁"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "解锁窗口"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "以另一个用户身份登录"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "欢迎使用 GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "如果您想了解基本用法,请跟随我们的导览。"
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "不用了,谢谢"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "进行导览"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "“%s”已就绪"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "保留这些显示设置吗?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "还原设置"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "保留更改"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "设置更改将在 %d 秒后还原"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "隐藏"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "还原"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "最大化"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "移动"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "改变大小"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "在屏幕中移动标题栏"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "置顶"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "总在可见工作区"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "移至左工作区"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "移至右工作区"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "移至上工作区"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "移至下工作区"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "移到上边显示器"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "移到下边显示器"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "移到左边显示器"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "移至右边显示器"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "关闭"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "Evolution 日历"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "打印版本"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "GDM 用于登录屏幕的模式"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "使用指定模式,如“gdm”用于登录屏幕"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "列出可用的模式"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "未知"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "启动“%s”失败"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "密码不匹配。"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "密码不能为空"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "认证对话框已被用户取消"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "扩展"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "管理您的 GNOME 扩展"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME 项目"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr "GNOME 扩展程序可用于升级扩展,配置扩展首选项和移除、禁用不想要的扩展。"
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "配置 GNOME Shell 扩展"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "没有匹配"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "移除 “%s”?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr "如果您移除该扩展,想要再次启用则需重新下载"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "移除"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"eternalhui <www.eternalhui@gmail.com>, 2013\n"
+"tuhaihe <1132321739qq@gmail.com>, 2013, 2020\n"
+"Wylmer Wang <wantinghard@gmail.com>, 2013\n"
+"YunQiang Su <wzssyqa@gmail.com>, 2014\n"
+"Sphinx Jiang <yishanj13@gmail.com>, 2014\n"
+"Tong Hui <tonghuix@gmail.com>, 2014\n"
+"Mingye Wang <arthur2e5@gmail.com>, 2015, 2016\n"
+"Mingcong Bai <jeffbai@aosc.xyz>, 2015, 2018\n"
+"Bin Li <binli@gnome.org>, 2016\n"
+"Michael Chen <searchends@gnu.hk>, 2016\n"
+"Mandy Wang <wangmychn@gmail.com>, 2016\n"
+"Dingzhong Chen <wsxy162@gmail.com>, 2015, 2016, 2017, 2019\n"
+"Weiyi Xu <xuweiyi0923@gmail.com>, 2020\n"
+"lumingzh <lumingzh@qq.com>, 2022"
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d 个扩展将在下次登录时更新。"
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "该扩展与当前 GNOME 版本不兼容"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "该扩展有错误"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "该扩展可以更新"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "网站"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "移除…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "帮助"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "关于扩展"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr "扩展可能会引起性能和稳定性问题。如果您遇到系统问题请尝试禁用扩展。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "已手动安装"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"如需查找或添加扩展,请访问 <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a> 网站。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "内置"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "没有已安装的扩展"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"非常抱歉,我们无法获取已安装扩展的列表。请确保您已登录进 GNOME 并再试一次。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "扩展更新已就绪"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "新扩展已成功创建在目录 %s。\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"名称应该为很短(最好是描述性的)的字符串。\n"
+"示例:%s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "名称"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"描述是对你的扩展的单句话解释。\n"
+"示例:%s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "描述"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID 是你的扩展全局唯一的标识符。\n"
+"这应该是电子邮件地址的格式(clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "选择一个可用模板:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "模板"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "新扩展的唯一标识符"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "名称"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "新扩展的用户可见名称"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "描述"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "扩展功能的简短描述"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "模板"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "新扩展使用的模板"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "以交互方式输入扩展信息"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "创建新扩展"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "未知参数"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID、名称和描述都是必需的"
+
+#: subprojects/extensions-tool/src/command-disable.c:62
+#: subprojects/extensions-tool/src/command-enable.c:62
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "扩展“%s”不存在\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:98
+msgid "Disable an extension"
+msgstr "禁用扩展"
+
+#: subprojects/extensions-tool/src/command-disable.c:116
+#: subprojects/extensions-tool/src/command-enable.c:116
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "未给定 UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:121
+#: subprojects/extensions-tool/src/command-enable.c:121
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "给定了超过一个的 UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:98
+msgid "Enable an extension"
+msgstr "启用扩展"
+
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "连接 GNOME Shell 失败\n"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "扩展“%s”不存在\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "显示扩展信息"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "覆盖已有的扩展"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "扩展包"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "安装扩展包"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "未指定扩展包"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "指定了超过一个的扩展包"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "显示用户安装的扩展"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "显示系统安装的扩展"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "显示启用的扩展"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "显示禁用的扩展"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "显示有设置选项的扩展"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "显示有更新的扩展"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "打印扩展详情"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "列出已安装的扩展"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "文件"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "扩展包要包含的额外文件源"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "架构"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "应该包含的 GSettings 架构"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "目录"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "查找翻译的目录"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "域"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "翻译使用的 gettext 域"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "覆盖已有的打包"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "包应该被创建到的目录"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "源目录"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "创建扩展包"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "指定了超过一个的源目录"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "扩展 “%s” 没有首选项\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "为扩展“%s”打开首选项失败:%s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "打开扩展首选项"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "重置扩展"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "无法卸载系统扩展\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "卸载“%s”失败\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "卸载扩展"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "不要打印错误信息"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "路径"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "URL"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "原作者"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "版本"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "状态"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "“version”不接受参数"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "用法:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "打印版本信息并退出。"
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "命令"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[参数…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "命令:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "打印帮助"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "启用扩展"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "禁用扩展"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "重置扩展"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "卸载扩展"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "列出扩展"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "显示扩展信息"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "打开扩展首选项"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "创建扩展"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "打包扩展"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "安装扩展包"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "使用“%s”以获取详细帮助。\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "普通扩展"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "一个空扩展"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "指示器"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "添加图标到顶栏"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1915
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u 个输出"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1925
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u 个输入"
+
+#: subprojects/gvc/gvc-mixer-control.c:2876
+msgid "System Sounds"
+msgstr "系统声音"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "默认的蓝牙适配器是否有已配对的设备"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "Shell 只会在有蓝牙适配器有供电时或有设备与默认适配器关联时显示蓝牙菜单。若"
+#~ "默认适配器不再和设备关联,蓝牙菜单将不再因此出现。"
+
+#~ msgid "Log Out"
+#~ msgstr "注销"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "连接到互联网"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "飞行模式已开启"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "启用飞行模式时 Wi-Fi 会关闭。"
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "关闭飞行模式"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi 已关闭"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "连接网络需要开启 Wi-Fi。"
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "开启 Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wi-Fi 网络"
+
+#~ msgid "Select a network"
+#~ msgstr "选择网络"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "使用硬件开关关闭"
+
+#~ msgid "Select Network"
+#~ msgstr "选择网络"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wi-Fi 设置"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN 已关闭"
+
+#~ msgid "Network Settings"
+#~ msgstr "网络设置"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s 个 Wi-Fi 连接"
+
+#, javascript-format
+#~ msgid "%s Bluetooth Connection"
+#~ msgid_plural "%s Bluetooth Connections"
+#~ msgstr[0] "%s 个蓝牙连接"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d 台设备已连接"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "蓝牙开启"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "蓝牙关闭"
+
+#~ msgid "Location Enabled"
+#~ msgstr "定位服务已启用"
+
+#~ msgid "Disable"
+#~ msgstr "禁用"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "隐私设置"
+
+#~ msgid "Location In Use"
+#~ msgstr "定位服务使用中"
+
+#~ msgid "Location Disabled"
+#~ msgstr "定位服务已禁用"
+
+#~ msgid "Enable"
+#~ msgstr "启用"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s 已关闭"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s 已连接"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s 未托管"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "%s 正在连接"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s 需要认证"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s 的固件缺失"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s 不可用"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s 连接失败"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s 硬件开关关闭"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s 已禁用"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s 未连接"
+
+#~ msgid "unknown"
+#~ msgstr "未知"
+
+#~ msgid "activating…"
+#~ msgstr "正在激活…"
+
+#~ msgid "deactivating…"
+#~ msgstr "正在停用…"
+
+#~ msgid "deactivated"
+#~ msgstr "已停用"
+
+#~ msgid "connecting…"
+#~ msgstr "正在连接…"
+
+#~ msgid "authentication required"
+#~ msgstr "需要认证"
+
+#~ msgid "connection failed"
+#~ msgstr "连接失败"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "夜间模式已禁用"
+
+#~ msgid "Resume"
+#~ msgstr "继续使用"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "明天前禁用"
+
+#~ msgid "Power Settings"
+#~ msgstr "电源设置"
+
+#~ msgid "Fully Charged"
+#~ msgstr "已充满电"
+
+#~ msgid "Not Charging"
+#~ msgstr "未在充电"
+
+#~ msgid "Estimating…"
+#~ msgstr "正在估计…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "剩余 %d∶%02d (%d%%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "距充满还剩 %d∶%02d (%d%%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "正在共享屏幕"
+
+#~ msgid "Turn off"
+#~ msgstr "关闭"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "飞行模式已开启"
+
+#~ msgid "Lock"
+#~ msgstr "锁定"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "关机 / 注销"
+
+#~ msgid "Show screenshot UI"
+#~ msgstr "显示截图界面"
+
+#~ msgid "Screen Recording in Progress"
+#~ msgstr "正在进行屏幕录制"
+
+#~ msgid "Stop"
+#~ msgstr "停止"
+
+#~ msgid "Author"
+#~ msgstr "作者"
+
+#~ msgid "Warning"
+#~ msgstr "警告"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "扩展可引起系统问题,包括性能问题。如果您遇到了系统的问题,推荐禁用全部扩"
+#~ "展。"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "从收藏夹中移除"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "添加到收藏夹"
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "连接 GNOME Shell 失败"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "启用 introspection API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr "启用一个允许审视(introspect)shell 应用程序状态的 D-Bus API。"
+
+#~ msgid "Minimize"
+#~ msgstr "最小化"
+
+#~ msgid "Unmaximize"
+#~ msgstr "取消最大化"
+
+#~ msgid "App Picker View"
+#~ msgstr "应用选择器视图"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "应用程序选择器中当前选中视图的索引。"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%-m月%-d日 %A"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%Y年%-m月%-d日 %A"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "常用的应用程序会出现在这里"
+
+#~ msgid "Frequent"
+#~ msgstr "常用"
+
+#~ msgid "All"
+#~ msgstr "全部"
+
+#~ msgid "Copy Error"
+#~ msgstr "复制错误"
+
+#~ msgid "Browse in Software"
+#~ msgstr "在“软件”中浏览"
+
+#~ msgid "Next"
+#~ msgstr "下一步"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "登录"
+
+#~ msgid "Rename"
+#~ msgstr "重命名"
+
+#~ msgid "Password:"
+#~ msgstr "密码:"
+
+#~ msgid "Type again:"
+#~ msgstr "再输一次:"
+
+#~ msgid "Password: "
+#~ msgstr "密码:"
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "无线网络要求身份认证"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "移动宽带网络密码"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%-m月%-d日 %A"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d 条新消息"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d 条新通知"
+
+#~ msgid "Off"
+#~ msgstr "关"
+
+#~ msgid "On"
+#~ msgstr "开"
+
+#~ msgid "Account Settings"
+#~ msgstr "帐号设置"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "方向锁定"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr "暂停和恢复所有执行中动画的快捷键,用于调试"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "使用哪种键盘"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "要使用的键盘类型。"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "载入 %s 的首选项对话框时出错:"
+
+#~ msgid "%s all day."
+#~ msgstr "全天%s。"
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s转%s。"
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s转%s,随后转%s。"
+
+#~ msgid "Feels like %s."
+#~ msgstr "体感温度 %s。"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "注销"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "切换用户"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "网页认证重定向"
+
+#~ msgid "Events"
+#~ msgstr "事件"
+
+#~ msgid "Notifications"
+#~ msgstr "提示"
+
+#~ msgid "Hide tray"
+#~ msgstr "隐藏托盘"
+
+#~ msgid "Status Icons"
+#~ msgstr "状态图标"
+
+#~ msgid "Media"
+#~ msgstr "媒体"
+
+#~ msgid "Not In Use"
+#~ msgstr "未在使用"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "在日历中显示星期"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "如果为 true,在日历中显示 ISO 周日期。"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "使用互联网连接"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s 申请访问您的位置信息。"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (wayland 合成器)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "已连接 %d 台设备"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "电池"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%p %-l:%M"
diff --git a/po/zh_HK.po b/po/zh_HK.po
new file mode 100644
index 0000000..b7273ae
--- /dev/null
+++ b/po/zh_HK.po
@@ -0,0 +1,2070 @@
+# Chinese (Hong Kong) translation for gnome-shell.
+# Copyright (C) 2010 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>, 2010.
+# Wei-Lun Chao <chaoweilun@gmail.com>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell 3.3.90\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2015-11-03 22:14+0000\n"
+"PO-Revision-Date: 2014-08-19 19:17+0800\n"
+"Last-Translator: Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>\n"
+"Language-Team: Chinese (Hong Kong) <community@linuxhall.org>\n"
+"Language: zh_HK\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 1.6.5\n"
+
+#: ../data/50-gnome-shell-system.xml.in.h:1
+msgid "System"
+msgstr "系統"
+
+#: ../data/50-gnome-shell-system.xml.in.h:2
+#, fuzzy
+#| msgid "%d new notification"
+#| msgid_plural "%d new notifications"
+msgid "Show the notification list"
+msgstr "%d 個新通知"
+
+#: ../data/50-gnome-shell-system.xml.in.h:3
+msgid "Focus the active notification"
+msgstr "聚焦到使用中的通知"
+
+#: ../data/50-gnome-shell-system.xml.in.h:4
+msgid "Show the overview"
+msgstr "顯示概覽"
+
+#: ../data/50-gnome-shell-system.xml.in.h:5
+msgid "Show all applications"
+msgstr "顯示所有的應用程式"
+
+#: ../data/50-gnome-shell-system.xml.in.h:6
+msgid "Open the application menu"
+msgstr "開啟應用程式選單"
+
+#: ../data/gnome-shell.desktop.in.in.h:1
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: ../data/gnome-shell.desktop.in.in.h:2
+#: ../data/gnome-shell-wayland.desktop.in.in.h:2
+msgid "Window management and application launching"
+msgstr "視窗管理與應用程式的執行"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:1
+msgid "GNOME Shell Extension Preferences"
+msgstr "GNOME Shell 擴充功能偏好設定"
+
+#: ../data/gnome-shell-extension-prefs.desktop.in.in.h:2
+msgid "Configure GNOME Shell Extensions"
+msgstr "設定 GNOME Shell 擴充功能"
+
+#: ../data/gnome-shell-wayland.desktop.in.in.h:1
+msgid "GNOME Shell (wayland compositor)"
+msgstr "GNOME Shell (wayland 組合器)"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "啟用讓開發者與測試者能以 Alt-F2 使用好用的內部工具"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "允許使用 Alt-F2 對話盒存取內部除錯和監控工具。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
+msgid "UUIDs of extensions to enable"
+msgstr "要啟用的擴充功能 UUID"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"GNOME Shell 擴充功能有 UUID 屬性;這個設定鍵列出了應載入的擴充功能。任何要載"
+"入的擴充功能都要出現在這個清單中。你也可以用 org.gnome.Shell 中的 "
+"EnableExtension 和 DisableExtension D-Bus 方法來操作這個清單。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
+msgid "Disables the validation of extension version compatibility"
+msgstr "停用擴充功能版本兼容性的驗證"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"GNOME Shell 只會載入宣稱支援目前運行中版本的擴充功能。啟用此選項會停用這項檢"
+"查,並嘗試載入所有的擴充功能,而不去管它們所宣稱支援的版本為何。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
+msgid "List of desktop file IDs for favorite applications"
+msgstr "喜好的應用程式桌面檔案 ID 清單"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "對應這些辨別碼的應用程式會顯示在喜好區域。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
+msgid "App Picker View"
+msgstr "程式挑選器檢視"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
+msgid "Index of the currently selected view in the application picker."
+msgstr "應用程式挑選器中目前選取檢視的索引。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
+msgid "History for command (Alt-F2) dialog"
+msgstr "指令 (Alt-F2) 對話盒歷史紀錄"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://live.gnome.org/GnomeShell/LookingGlass
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
+msgid "History for the looking glass dialog"
+msgstr "Looking glass 對話盒歷史紀錄"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
+msgid "Always show the 'Log out' menu item in the user menu."
+msgstr "永遠在使用者選單顯示「登出」選單項目。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
+msgid ""
+"This key overrides the automatic hiding of the 'Log out' menu item in single-"
+"user, single-session situations."
+msgstr ""
+"這個設定鍵會覆蓋在單一使用者、單一作業階段情況下對「登出」選單項目的隱藏。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "是否記住已掛載的加密或遠端檔案系統的密碼"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"'Remember Password' checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"當掛載加密裝置或遠端檔案系統時會要求密碼。如果密碼可以被儲存以供未來使用時會"
+"顯示「記住密碼」。這個設定鍵設定了核取方塊的預設狀態。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
+msgid ""
+"Whether the default Bluetooth adapter had set up devices associated to it"
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
+msgid ""
+"The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+"powered, or if there were devices set up associated with the default "
+"adapter. This will be reset if the default adapter is ever seen not to have "
+"devices associated to it."
+msgstr ""
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
+msgid "Show the week date in the calendar"
+msgstr "在日曆中顯示週數"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
+msgid "If true, display the ISO week date in the calendar."
+msgstr "如果設為 true,在日曆中顯示 ISO 週數。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
+msgid "Keybinding to open the application menu"
+msgstr "開啟應用程式選單的組合鍵"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
+msgid "Keybinding to open the application menu."
+msgstr "開啟應用程式選單的組合鍵。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
+msgid "Keybinding to open the \"Show Applications\" view"
+msgstr "開啟「顯示應用程式」檢視的組合鍵"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
+msgid ""
+"Keybinding to open the \"Show Applications\" view of the Activities Overview."
+msgstr "開啟活動概覽「顯示應用程式」檢視的組合鍵"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
+msgid "Keybinding to open the overview"
+msgstr "開啟概覽的組合鍵"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
+msgid "Keybinding to open the Activities Overview."
+msgstr "開啟「活動概覽」的組合鍵。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
+#, fuzzy
+#| msgid "Keybinding to toggle the visibility of the message tray"
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "切換訊息匣顯示的組合鍵"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
+#, fuzzy
+#| msgid "Keybinding to toggle the visibility of the message tray."
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "切換訊息匣顯示的組合鍵。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
+msgid "Keybinding to focus the active notification"
+msgstr "聚焦到使用中通知的組合鍵"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
+msgid "Keybinding to focus the active notification."
+msgstr "聚焦到使用中通知的組合鍵。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
+msgid ""
+"Keybinding that pauses and resumes all running tweens, for debugging purposes"
+msgstr "暫停與恢復執行中重複項的組合鍵,用於除錯用途"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
+msgid "Which keyboard to use"
+msgstr "要使用哪種鍵盤"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
+msgid "The type of keyboard to use."
+msgstr "要使用的鍵盤類型。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
+msgid "Limit switcher to current workspace."
+msgstr "限制切換器使用目前的工作區"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"如果設定為「true」,切換器將僅顯示目前工作區中的應用程式視窗。否則,所有的應"
+"用程式都會包含在內。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
+msgid "The application icon mode."
+msgstr "應用程式圖示模式。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
+"only' (shows only the application icon) or 'both'."
+msgstr ""
+"設定視窗在切換器中如何顯示。有效的數值為「thumbnail-only」(顯示視窗縮圖)、"
+"「app-icon-only」(只顯示應用程式圖示) 或「both」。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"如果設定為「true」,切換器將僅顯示目前工作區中的視窗。否則,所有的視窗都會包"
+"含在內。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
+msgid "Attach modal dialog to the parent window"
+msgstr "在上層視窗附加強制對話盒"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "這個設定鍵會在執行 GNOME Shell 時覆蓋 org.gnome.mutter 中的設定鍵。"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "在螢幕邊緣放下視窗時啟用邊緣拼貼"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
+msgid "Workspaces are managed dynamically"
+msgstr "工作區以動態方式管理"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
+msgid "Workspaces only on primary monitor"
+msgstr "工作區只在主要顯示器"
+
+#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "在滑鼠模式中延遲焦點更改直到指標停止移動"
+
+#: ../data/org.gnome.Shell.PortalHelper.desktop.in.h:1
+#, fuzzy
+#| msgid "Network error"
+msgid "Network Login"
+msgstr "網絡錯誤"
+
+#: ../js/extensionPrefs/main.js:122
+#, javascript-format
+msgid "There was an error loading the preferences dialog for %s:"
+msgstr "載入 %s 的偏好設定對話盒時發生錯誤:"
+
+#: ../js/extensionPrefs/main.js:154
+msgid "GNOME Shell Extensions"
+msgstr "GNOME Shell 擴充功能"
+
+#: ../js/gdm/authPrompt.js:147 ../js/ui/components/networkAgent.js:145
+#: ../js/ui/components/polkitAgent.js:179 ../js/ui/endSessionDialog.js:452
+#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
+#: ../js/ui/status/network.js:916
+msgid "Cancel"
+msgstr "取消"
+
+#: ../js/gdm/authPrompt.js:169 ../js/gdm/authPrompt.js:215
+#: ../js/gdm/authPrompt.js:447
+msgid "Next"
+msgstr "下一個"
+
+#: ../js/gdm/authPrompt.js:211 ../js/ui/shellMountOperation.js:403
+#: ../js/ui/unlockDialog.js:59
+msgid "Unlock"
+msgstr "解鎖"
+
+#: ../js/gdm/authPrompt.js:213
+msgctxt "button"
+msgid "Sign In"
+msgstr "登入"
+
+#: ../js/gdm/loginDialog.js:281
+msgid "Choose Session"
+msgstr "選擇作業階段"
+
+#. translators: this message is shown below the user list on the
+#. login screen. It can be activated to reveal an entry for
+#. manually entering the username.
+#: ../js/gdm/loginDialog.js:431
+msgid "Not listed?"
+msgstr "沒有列出來?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: ../js/gdm/loginDialog.js:850
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(例如: user 或 %s)"
+
+#. TTLS and PEAP are actually much more complicated, but this complication
+#. is not visible here since we only care about phase2 authentication
+#. (and don't even care of which one)
+#: ../js/gdm/loginDialog.js:855 ../js/ui/components/networkAgent.js:271
+#: ../js/ui/components/networkAgent.js:289
+msgid "Username: "
+msgstr "使用者名稱:"
+
+#: ../js/gdm/loginDialog.js:1180
+msgid "Login Window"
+msgstr "登入視窗"
+
+#: ../js/gdm/util.js:341
+msgid "Authentication error"
+msgstr "核對錯誤"
+
+#. We don't show fingerprint messages directly since it's
+#. not the main auth service. Instead we use the messages
+#. as a cue to display our own message.
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger instead
+#: ../js/gdm/util.js:473
+msgid "(or swipe finger)"
+msgstr "(或是滑過手指)"
+
+#: ../js/misc/util.js:119
+msgid "Command not found"
+msgstr "找不到指令"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: ../js/misc/util.js:152
+msgid "Could not parse command:"
+msgstr "無法分析指令:"
+
+#: ../js/misc/util.js:160
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "執行「%s」失敗:"
+
+#. Translators: Time in 24h format
+#: ../js/misc/util.js:191
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: ../js/misc/util.js:197
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "昨天 %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: ../js/misc/util.js:203
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: ../js/misc/util.js:209
+#, no-c-format
+msgid "%B %d, %H∶%M"
+msgstr "%m月%d日 %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: ../js/misc/util.js:215
+#, no-c-format
+msgid "%B %d %Y, %H∶%M"
+msgstr "%Y年%m月%d日 %H:%M"
+
+#. Translators: Time in 12h format
+#: ../js/misc/util.js:220
+msgid "%l∶%M %p"
+msgstr "%p %l∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: ../js/misc/util.js:226
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "昨天 %p %l∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: ../js/misc/util.js:232
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A%p %l∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: ../js/misc/util.js:238
+#, no-c-format
+msgid "%B %d, %l∶%M %p"
+msgstr "%m月%d日%p %l∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: ../js/misc/util.js:244
+#, no-c-format
+msgid "%B %d %Y, %l∶%M %p"
+msgstr "%Y年%m月%d日%p %l∶%M"
+
+#. TRANSLATORS: this is the title of the wifi captive portal login
+#. * window, until we know the title of the actual login page
+#: ../js/portalHelper/main.js:85
+msgid "Web Authentication Redirect"
+msgstr "網頁核對重新導向"
+
+#: ../js/ui/appDisplay.js:794
+msgid "Frequently used applications will appear here"
+msgstr "經常使用的應用程式會出現在這裏"
+
+#: ../js/ui/appDisplay.js:914
+msgid "Frequent"
+msgstr "常用"
+
+#: ../js/ui/appDisplay.js:921
+msgid "All"
+msgstr "全部"
+
+#: ../js/ui/appDisplay.js:1853
+msgid "New Window"
+msgstr "新視窗"
+
+#: ../js/ui/appDisplay.js:1881 ../js/ui/dash.js:289
+msgid "Remove from Favorites"
+msgstr "自喜好中移除"
+
+#: ../js/ui/appDisplay.js:1887
+msgid "Add to Favorites"
+msgstr "加入喜好"
+
+#: ../js/ui/appDisplay.js:1897
+msgid "Show Details"
+msgstr "顯示詳細資訊"
+
+#: ../js/ui/appFavorites.js:132
+#, javascript-format
+msgid "%s has been added to your favorites."
+msgstr "%s 已加入你的喜好中。"
+
+#: ../js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been removed from your favorites."
+msgstr "%s 已經從你的喜好中移除。"
+
+#: ../js/ui/backgroundMenu.js:19
+msgid "Change Background…"
+msgstr "改變背景…"
+
+#: ../js/ui/backgroundMenu.js:21
+#, fuzzy
+#| msgid "Wired Settings"
+msgid "Display Settings"
+msgstr "有線設定值"
+
+#: ../js/ui/backgroundMenu.js:22 ../js/ui/status/system.js:366
+msgid "Settings"
+msgstr "設定值"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: ../js/ui/calendar.js:55
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr ""
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: ../js/ui/calendar.js:84
+msgctxt "grid sunday"
+msgid "S"
+msgstr "日"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: ../js/ui/calendar.js:86
+msgctxt "grid monday"
+msgid "M"
+msgstr "一"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: ../js/ui/calendar.js:88
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "二"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: ../js/ui/calendar.js:90
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "三"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: ../js/ui/calendar.js:92
+msgctxt "grid thursday"
+msgid "T"
+msgstr "四"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: ../js/ui/calendar.js:94
+msgctxt "grid friday"
+msgid "F"
+msgstr "五"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: ../js/ui/calendar.js:96
+msgctxt "grid saturday"
+msgid "S"
+msgstr "六"
+
+#: ../js/ui/calendar.js:566
+msgid "Previous month"
+msgstr "上個月"
+
+#: ../js/ui/calendar.js:576
+msgid "Next month"
+msgstr "下個月"
+
+#: ../js/ui/calendar.js:728
+#, javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr ""
+
+#: ../js/ui/calendar.js:783
+msgid "Week %V"
+msgstr ""
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: ../js/ui/calendar.js:1188
+msgctxt "event list time"
+msgid "All Day"
+msgstr "整天"
+
+#: ../js/ui/calendar.js:1295
+msgid "Clear section"
+msgstr ""
+
+#: ../js/ui/calendar.js:1522
+msgid "Events"
+msgstr ""
+
+#: ../js/ui/calendar.js:1531
+msgctxt "calendar heading"
+msgid "%A, %B %d"
+msgstr "%m月%d日%A"
+
+#: ../js/ui/calendar.js:1535
+msgctxt "calendar heading"
+msgid "%A, %B %d, %Y"
+msgstr "%Y年%m月%d日%A"
+
+#: ../js/ui/calendar.js:1620
+msgid "Notifications"
+msgstr "通知"
+
+#: ../js/ui/calendar.js:1771
+#, fuzzy
+#| msgid "Notifications"
+msgid "No Notifications"
+msgstr "通知"
+
+#: ../js/ui/calendar.js:1774
+msgid "No Events"
+msgstr ""
+
+#: ../js/ui/components/automountManager.js:91
+msgid "External drive connected"
+msgstr "外部裝置已連接"
+
+#: ../js/ui/components/automountManager.js:102
+msgid "External drive disconnected"
+msgstr "外部裝置已拔除"
+
+#: ../js/ui/components/autorunManager.js:351
+#, javascript-format
+msgid "Open with %s"
+msgstr "用 %s 開啟"
+
+#: ../js/ui/components/keyring.js:120 ../js/ui/components/polkitAgent.js:315
+msgid "Password:"
+msgstr "密碼: "
+
+#: ../js/ui/components/keyring.js:153
+msgid "Type again:"
+msgstr "再輸入一次:"
+
+#: ../js/ui/components/networkAgent.js:140 ../js/ui/status/network.js:269
+#: ../js/ui/status/network.js:352 ../js/ui/status/network.js:919
+msgid "Connect"
+msgstr "連線"
+
+#. Cisco LEAP
+#: ../js/ui/components/networkAgent.js:233
+#: ../js/ui/components/networkAgent.js:245
+#: ../js/ui/components/networkAgent.js:273
+#: ../js/ui/components/networkAgent.js:293
+#: ../js/ui/components/networkAgent.js:303
+msgid "Password: "
+msgstr "密碼: "
+
+#. static WEP
+#: ../js/ui/components/networkAgent.js:238
+msgid "Key: "
+msgstr "密碼匙:"
+
+#: ../js/ui/components/networkAgent.js:277
+msgid "Identity: "
+msgstr "識別:"
+
+#: ../js/ui/components/networkAgent.js:279
+msgid "Private key password: "
+msgstr "私密密碼匙密碼:"
+
+#: ../js/ui/components/networkAgent.js:291
+msgid "Service: "
+msgstr "服務:"
+
+#: ../js/ui/components/networkAgent.js:320
+#: ../js/ui/components/networkAgent.js:658
+msgid "Authentication required by wireless network"
+msgstr "無線網絡所需要的核對"
+
+#: ../js/ui/components/networkAgent.js:321
+#: ../js/ui/components/networkAgent.js:659
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "需要密碼或是加密密碼匙來存取無線網絡「%s」。"
+
+#: ../js/ui/components/networkAgent.js:325
+#: ../js/ui/components/networkAgent.js:662
+msgid "Wired 802.1X authentication"
+msgstr "有線網絡 802.1X 核對"
+
+#: ../js/ui/components/networkAgent.js:327
+msgid "Network name: "
+msgstr "網絡名稱:"
+
+#: ../js/ui/components/networkAgent.js:332
+#: ../js/ui/components/networkAgent.js:666
+msgid "DSL authentication"
+msgstr "DSL 核對"
+
+#: ../js/ui/components/networkAgent.js:339
+#: ../js/ui/components/networkAgent.js:672
+msgid "PIN code required"
+msgstr "需要 PIN 碼"
+
+#: ../js/ui/components/networkAgent.js:340
+#: ../js/ui/components/networkAgent.js:673
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "這個流動寬頻裝置需要 PIN 碼"
+
+#: ../js/ui/components/networkAgent.js:341
+msgid "PIN: "
+msgstr "PIN: "
+
+#: ../js/ui/components/networkAgent.js:348
+#: ../js/ui/components/networkAgent.js:679
+msgid "Mobile broadband network password"
+msgstr "流動寬頻網絡密碼"
+
+#: ../js/ui/components/networkAgent.js:349
+#: ../js/ui/components/networkAgent.js:663
+#: ../js/ui/components/networkAgent.js:667
+#: ../js/ui/components/networkAgent.js:680
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "連線至「%s」需要密碼。"
+
+#: ../js/ui/components/networkAgent.js:647 ../js/ui/status/network.js:1658
+msgid "Network Manager"
+msgstr "網絡管理員"
+
+#: ../js/ui/components/polkitAgent.js:60
+msgid "Authentication Required"
+msgstr "要求核對"
+
+#: ../js/ui/components/polkitAgent.js:102
+msgid "Administrator"
+msgstr "系統管理員"
+
+#: ../js/ui/components/polkitAgent.js:182
+msgid "Authenticate"
+msgstr "核對"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: ../js/ui/components/polkitAgent.js:301 ../js/ui/shellMountOperation.js:383
+msgid "Sorry, that didn't work. Please try again."
+msgstr "抱歉,那樣沒有作用。請再試一次。"
+
+#. Translators: this is the other person changing their old IM name to their new
+#. IM name.
+#: ../js/ui/components/telepathyClient.js:759
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "%s 現在被稱為 %s"
+
+#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:155
+msgid "Windows"
+msgstr "視窗"
+
+#: ../js/ui/dash.js:250 ../js/ui/dash.js:291
+msgid "Show Applications"
+msgstr "顯示應用程式"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: ../js/ui/dash.js:449
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
+#.
+#: ../js/ui/dateMenu.js:73
+#, fuzzy
+#| msgid "%A %B %e, %Y"
+msgid "%B %e %Y"
+msgstr "%Y年%m月%e日%A"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: ../js/ui/dateMenu.js:80
+#, fuzzy
+#| msgid "%A %B %e, %Y"
+msgid "%A %B %e %Y"
+msgstr "%Y年%m月%e日%A"
+
+#: ../js/ui/dateMenu.js:160
+msgid "Add world clocks…"
+msgstr ""
+
+#: ../js/ui/dateMenu.js:161
+#, fuzzy
+#| msgid "Open Clocks"
+msgid "World Clocks"
+msgstr "開啟時鐘"
+
+#: ../js/ui/endSessionDialog.js:64
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "登出 %s"
+
+#: ../js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Log Out"
+msgstr "登出"
+
+#: ../js/ui/endSessionDialog.js:67
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "%s 會在 %d 秒後自動登出。"
+
+#: ../js/ui/endSessionDialog.js:72
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "你會在 %d 秒後自動登出。"
+
+#: ../js/ui/endSessionDialog.js:78
+msgctxt "button"
+msgid "Log Out"
+msgstr "登出"
+
+#: ../js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Power Off"
+msgstr "關閉電源"
+
+#: ../js/ui/endSessionDialog.js:85
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "安裝更新並關機"
+
+#: ../js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "系統會在 %d 秒後關閉電源。"
+
+#: ../js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "安裝擱置的軟件更新"
+
+#: ../js/ui/endSessionDialog.js:94 ../js/ui/endSessionDialog.js:111
+msgctxt "button"
+msgid "Restart"
+msgstr "重新啟動"
+
+#: ../js/ui/endSessionDialog.js:96
+msgctxt "button"
+msgid "Power Off"
+msgstr "關閉電源"
+
+#: ../js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart"
+msgstr "重新啟動"
+
+#: ../js/ui/endSessionDialog.js:105
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "系統會在 %d 秒後自動重新啟動。"
+
+#: ../js/ui/endSessionDialog.js:119
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "重新啟動並安裝更新"
+
+#: ../js/ui/endSessionDialog.js:121
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "系統會在 %d 秒後自動重新啟動。"
+
+#: ../js/ui/endSessionDialog.js:127
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "重新啟動並安裝"
+
+#: ../js/ui/endSessionDialog.js:128
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "安裝並關機"
+
+#: ../js/ui/endSessionDialog.js:129
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "在安裝完更新之後關機"
+
+#: ../js/ui/endSessionDialog.js:338
+msgid "Running on battery power: please plug in before installing updates."
+msgstr "正使用電池電源執行:請在安裝更新之前插入電源線。"
+
+#: ../js/ui/endSessionDialog.js:355
+msgid "Some applications are busy or have unsaved work."
+msgstr "部分應用程式忙碌中或有未儲存的工作。"
+
+#: ../js/ui/endSessionDialog.js:362
+msgid "Other users are logged in."
+msgstr "其他使用者已登入。"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: ../js/ui/endSessionDialog.js:640
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s (遠端)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: ../js/ui/endSessionDialog.js:643
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s (主控臺)"
+
+#: ../js/ui/extensionDownloader.js:199
+msgid "Install"
+msgstr "安裝"
+
+#: ../js/ui/extensionDownloader.js:204
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "是否從 extensions.gnome.org 下載並安裝「%s」?"
+
+#: ../js/ui/keyboard.js:741 ../js/ui/status/keyboard.js:713
+msgid "Keyboard"
+msgstr "鍵盤"
+
+#. translators: 'Hide' is a verb
+#: ../js/ui/legacyTray.js:66
+#, fuzzy
+#| msgid "Hide Text"
+msgid "Hide tray"
+msgstr "隱藏文字"
+
+#: ../js/ui/legacyTray.js:107
+msgid "Status Icons"
+msgstr ""
+
+#: ../js/ui/lookingGlass.js:643
+msgid "No extensions installed"
+msgstr "沒有安裝擴充功能"
+
+#. Translators: argument is an extension UUID.
+#: ../js/ui/lookingGlass.js:697
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s 沒有發出任何錯誤。"
+
+#: ../js/ui/lookingGlass.js:703
+msgid "Hide Errors"
+msgstr "隱藏錯誤"
+
+#: ../js/ui/lookingGlass.js:707 ../js/ui/lookingGlass.js:767
+msgid "Show Errors"
+msgstr "顯示錯誤"
+
+#: ../js/ui/lookingGlass.js:716
+msgid "Enabled"
+msgstr "已啟用"
+
+#. translators:
+#. * The device has been disabled
+#: ../js/ui/lookingGlass.js:719 ../src/gvc/gvc-mixer-control.c:1828
+msgid "Disabled"
+msgstr "已停用"
+
+#: ../js/ui/lookingGlass.js:721
+msgid "Error"
+msgstr "錯誤"
+
+#: ../js/ui/lookingGlass.js:723
+msgid "Out of date"
+msgstr "過期"
+
+#: ../js/ui/lookingGlass.js:725
+msgid "Downloading"
+msgstr "下載中"
+
+#: ../js/ui/lookingGlass.js:749
+msgid "View Source"
+msgstr "檢示來源"
+
+#: ../js/ui/lookingGlass.js:758
+msgid "Web Page"
+msgstr "網頁"
+
+#: ../js/ui/messageTray.js:1486
+msgid "System Information"
+msgstr "系統資訊"
+
+#: ../js/ui/overview.js:84
+msgid "Undo"
+msgstr "復原"
+
+#: ../js/ui/overview.js:117
+msgid "Overview"
+msgstr "概覽"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: ../js/ui/overview.js:244
+msgid "Type to search…"
+msgstr "輸入以搜尋…"
+
+#: ../js/ui/panel.js:358
+msgid "Quit"
+msgstr "結束"
+
+#. Translators: If there is no suitable word for "Activities"
+#. in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:414
+msgid "Activities"
+msgstr "概覽 "
+
+#: ../js/ui/panel.js:695
+#, fuzzy
+#| msgid "System"
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "系統"
+
+#: ../js/ui/panel.js:807
+msgid "Top Bar"
+msgstr "頂端列"
+
+#. Translators: this MUST be either "toggle-switch-us"
+#. (for toggle switches containing the English words
+#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
+#. switches containing "◯" and "|"). Other values will
+#. simply result in invisible toggle switches.
+#: ../js/ui/popupMenu.js:289
+msgid "toggle-switch-us"
+msgstr "toggle-switch-us"
+
+#: ../js/ui/runDialog.js:71
+msgid "Enter a Command"
+msgstr "請輸入指令"
+
+#: ../js/ui/runDialog.js:111 ../js/ui/windowMenu.js:162
+msgid "Close"
+msgstr "關閉"
+
+#: ../js/ui/runDialog.js:282
+msgid "Restarting…"
+msgstr "重新啟動…"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: ../js/ui/screenShield.js:85
+msgid "%A, %B %d"
+msgstr "%m月%d日%A"
+
+#: ../js/ui/screenShield.js:144
+#, javascript-format
+msgid "%d new message"
+msgid_plural "%d new messages"
+msgstr[0] "%d 個新訊息"
+
+#: ../js/ui/screenShield.js:146
+#, javascript-format
+msgid "%d new notification"
+msgid_plural "%d new notifications"
+msgstr[0] "%d 個新通知"
+
+#: ../js/ui/screenShield.js:432 ../js/ui/status/system.js:374
+msgid "Lock"
+msgstr "鎖定"
+
+#: ../js/ui/screenShield.js:684
+msgid "GNOME needs to lock the screen"
+msgstr "GNOME 需要鎖定螢幕"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell him to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: ../js/ui/screenShield.js:805 ../js/ui/screenShield.js:1271
+msgid "Unable to lock"
+msgstr "無法鎖定"
+
+#: ../js/ui/screenShield.js:806 ../js/ui/screenShield.js:1272
+msgid "Lock was blocked by an application"
+msgstr "鎖定被應用程式阻擋"
+
+#: ../js/ui/search.js:617
+msgid "Searching…"
+msgstr "搜尋…"
+
+#: ../js/ui/search.js:619
+msgid "No results."
+msgstr "沒有結果。"
+
+#: ../js/ui/shellEntry.js:25
+msgid "Copy"
+msgstr "複製"
+
+#: ../js/ui/shellEntry.js:30
+msgid "Paste"
+msgstr "貼上"
+
+#: ../js/ui/shellEntry.js:97
+msgid "Show Text"
+msgstr "顯示文字"
+
+#: ../js/ui/shellEntry.js:99
+msgid "Hide Text"
+msgstr "隱藏文字"
+
+#: ../js/ui/shellMountOperation.js:370
+msgid "Password"
+msgstr "密碼"
+
+#: ../js/ui/shellMountOperation.js:391
+msgid "Remember Password"
+msgstr "記住密碼"
+
+#: ../js/ui/status/accessibility.js:42
+msgid "Accessibility"
+msgstr "無障礙功能"
+
+#: ../js/ui/status/accessibility.js:57
+msgid "Zoom"
+msgstr "縮放"
+
+#: ../js/ui/status/accessibility.js:64
+msgid "Screen Reader"
+msgstr "螢幕閱讀器"
+
+#: ../js/ui/status/accessibility.js:68
+msgid "Screen Keyboard"
+msgstr "螢幕鍵盤"
+
+#: ../js/ui/status/accessibility.js:72
+msgid "Visual Alerts"
+msgstr "視覺警示"
+
+#: ../js/ui/status/accessibility.js:75
+msgid "Sticky Keys"
+msgstr "黏性特殊鍵"
+
+#: ../js/ui/status/accessibility.js:78
+msgid "Slow Keys"
+msgstr "遲緩按鍵"
+
+#: ../js/ui/status/accessibility.js:81
+msgid "Bounce Keys"
+msgstr "反彈鍵"
+
+#: ../js/ui/status/accessibility.js:84
+msgid "Mouse Keys"
+msgstr "滑鼠按鍵"
+
+#: ../js/ui/status/accessibility.js:167
+msgid "High Contrast"
+msgstr "高反差"
+
+#: ../js/ui/status/accessibility.js:202
+msgid "Large Text"
+msgstr "大型文字"
+
+#: ../js/ui/status/bluetooth.js:50
+msgid "Bluetooth"
+msgstr "藍牙"
+
+#: ../js/ui/status/bluetooth.js:59
+msgid "Bluetooth Settings"
+msgstr "藍牙設定值"
+
+#. Translators: this is the number of connected bluetooth devices
+#: ../js/ui/status/bluetooth.js:139
+#, fuzzy, javascript-format
+#| msgid "Connected"
+msgid "%d Connected"
+msgid_plural "%d Connected"
+msgstr[0] "已連線"
+
+#: ../js/ui/status/bluetooth.js:141
+msgid "Off"
+msgstr "關閉"
+
+#: ../js/ui/status/bluetooth.js:143
+#, fuzzy
+#| msgid "In Use"
+msgid "Not In Use"
+msgstr "使用中"
+
+#: ../js/ui/status/bluetooth.js:145 ../js/ui/status/network.js:1279
+msgid "Turn On"
+msgstr "開啟"
+
+#: ../js/ui/status/bluetooth.js:145 ../js/ui/status/network.js:178
+#: ../js/ui/status/network.js:353 ../js/ui/status/network.js:1279
+#: ../js/ui/status/network.js:1394 ../js/ui/status/rfkill.js:90
+#: ../js/ui/status/rfkill.js:117
+msgid "Turn Off"
+msgstr "關閉"
+
+#: ../js/ui/status/brightness.js:44
+msgid "Brightness"
+msgstr "亮度"
+
+#: ../js/ui/status/keyboard.js:736
+msgid "Show Keyboard Layout"
+msgstr "顯示鍵盤配置"
+
+#: ../js/ui/status/location.js:71 ../js/ui/status/location.js:177
+#, fuzzy
+#| msgid "Location"
+msgid "Location Enabled"
+msgstr "位置"
+
+#: ../js/ui/status/location.js:72 ../js/ui/status/location.js:178
+msgid "Disable"
+msgstr "停用"
+
+#: ../js/ui/status/location.js:73
+#, fuzzy
+#| msgid "Power Settings"
+msgid "Privacy Settings"
+msgstr "電源設定值"
+
+#: ../js/ui/status/location.js:176
+#, fuzzy
+#| msgid "Location"
+msgid "Location In Use"
+msgstr "位置"
+
+#: ../js/ui/status/location.js:180
+#, fuzzy
+#| msgid "Connection failed"
+msgid "Location Disabled"
+msgstr "連線失敗"
+
+#: ../js/ui/status/location.js:181
+msgid "Enable"
+msgstr "啟用"
+
+#: ../js/ui/status/network.js:101
+msgid "<unknown>"
+msgstr "<不明>"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:451 ../js/ui/status/network.js:1308
+#, javascript-format
+msgid "%s Off"
+msgstr ""
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:454
+#, fuzzy, javascript-format
+#| msgid "Connected"
+msgid "%s Connected"
+msgstr "已連線"
+
+#. Translators: this is for network devices that are physically present but are not
+#. under NetworkManager's control (and thus cannot be used in the menu);
+#. %s is a network identifier
+#: ../js/ui/status/network.js:459
+#, fuzzy, javascript-format
+#| msgid "Unmanaged"
+msgid "%s Unmanaged"
+msgstr "未管理"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:462
+#, fuzzy, javascript-format
+#| msgid "Disconnecting"
+msgid "%s Disconnecting"
+msgstr "斷線"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1300
+#, fuzzy, javascript-format
+#| msgid "Connecting"
+msgid "%s Connecting"
+msgstr "連線中"
+
+#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
+#: ../js/ui/status/network.js:472
+#, fuzzy, javascript-format
+#| msgid "Wired 802.1X authentication"
+msgid "%s Requires Authentication"
+msgstr "有線網絡 802.1X 核對"
+
+#. Translators: this is for devices that require some kind of firmware or kernel
+#. module, which is missing; %s is a network identifier
+#: ../js/ui/status/network.js:480
+#, fuzzy, javascript-format
+#| msgid "Firmware missing"
+msgid "Firmware Missing For %s"
+msgstr "缺少韌體"
+
+#. Translators: this is for a network device that cannot be activated (for example it
+#. is disabled by rfkill, or it has no coverage; %s is a network identifier
+#: ../js/ui/status/network.js:484
+#, fuzzy, javascript-format
+#| msgid "Unavailable"
+msgid "%s Unavailable"
+msgstr "無法使用"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:487
+#, fuzzy, javascript-format
+#| msgid "Connection failed"
+msgid "%s Connection Failed"
+msgstr "連線失敗"
+
+#: ../js/ui/status/network.js:503
+msgid "Wired Settings"
+msgstr "有線設定值"
+
+#: ../js/ui/status/network.js:545 ../js/ui/status/network.js:624
+msgid "Mobile Broadband Settings"
+msgstr "流動寬頻設定值"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:588 ../js/ui/status/network.js:1305
+#, fuzzy, javascript-format
+#| msgid "Hardware Disabled"
+msgid "%s Hardware Disabled"
+msgstr "硬件已停用"
+
+#. Translators: this is for a network device that cannot be activated
+#. because it's disabled by rfkill (airplane mode); %s is a network identifier
+#: ../js/ui/status/network.js:592
+#, fuzzy, javascript-format
+#| msgid "Disabled"
+msgid "%s Disabled"
+msgstr "已停用"
+
+#: ../js/ui/status/network.js:632
+msgid "Use as Internet connection"
+msgstr "作為互聯網連線"
+
+#: ../js/ui/status/network.js:813
+msgid "Airplane Mode is On"
+msgstr "飛安模式開啟"
+
+#: ../js/ui/status/network.js:814
+msgid "Wi-Fi is disabled when airplane mode is on."
+msgstr "開啟飛安模式時會停用 Wi-Fi 。"
+
+#: ../js/ui/status/network.js:815
+msgid "Turn Off Airplane Mode"
+msgstr "關閉飛安模式"
+
+#: ../js/ui/status/network.js:824
+msgid "Wi-Fi is Off"
+msgstr "Wi-Fi 關閉"
+
+#: ../js/ui/status/network.js:825
+msgid "Wi-Fi needs to be turned on in order to connect to a network."
+msgstr "需要開啟 Wi-Fi 才能連接網絡。"
+
+#: ../js/ui/status/network.js:826
+msgid "Turn On Wi-Fi"
+msgstr "開啟 Wi-Fi"
+
+#: ../js/ui/status/network.js:851
+msgid "Wi-Fi Networks"
+msgstr "Wi-Fi 網絡"
+
+#: ../js/ui/status/network.js:853
+msgid "Select a network"
+msgstr "選擇一個網絡"
+
+#: ../js/ui/status/network.js:883
+msgid "No Networks"
+msgstr "沒有網絡"
+
+#: ../js/ui/status/network.js:904 ../js/ui/status/rfkill.js:115
+msgid "Use hardware switch to turn off"
+msgstr "使用硬件開關來關閉"
+
+#: ../js/ui/status/network.js:1171
+msgid "Select Network"
+msgstr "選擇網絡"
+
+#: ../js/ui/status/network.js:1177
+msgid "Wi-Fi Settings"
+msgstr "Wi-Fi 設定值"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:1296
+#, fuzzy, javascript-format
+#| msgid "Hotspot Active"
+msgid "%s Hotspot Active"
+msgstr "熱點活動中"
+
+#. Translators: %s is a network identifier
+#: ../js/ui/status/network.js:1311
+#, fuzzy, javascript-format
+#| msgid "Not Connected"
+msgid "%s Not Connected"
+msgstr "未連線"
+
+#: ../js/ui/status/network.js:1411
+msgid "connecting..."
+msgstr "連線中…"
+
+#. Translators: this is for network connections that require some kind of key or password
+#: ../js/ui/status/network.js:1414
+msgid "authentication required"
+msgstr "要求核對"
+
+#: ../js/ui/status/network.js:1416
+msgid "connection failed"
+msgstr "連線失敗"
+
+#: ../js/ui/status/network.js:1482 ../js/ui/status/rfkill.js:93
+msgid "Network Settings"
+msgstr "網絡設定值"
+
+#: ../js/ui/status/network.js:1484
+msgid "VPN Settings"
+msgstr "VPN 設定值"
+
+#: ../js/ui/status/network.js:1503
+msgid "VPN"
+msgstr "VPN"
+
+#: ../js/ui/status/network.js:1513
+#, fuzzy
+#| msgid "Power Off"
+msgid "VPN Off"
+msgstr "關閉電源"
+
+#: ../js/ui/status/network.js:1697
+msgid "Connection failed"
+msgstr "連線失敗"
+
+#: ../js/ui/status/network.js:1698
+msgid "Activation of network connection failed"
+msgstr "啟動網絡連線失敗"
+
+#: ../js/ui/status/power.js:49
+msgid "Power Settings"
+msgstr "電源設定值"
+
+#: ../js/ui/status/power.js:65
+msgid "Fully Charged"
+msgstr "已完全充飽"
+
+#. 0 is reported when UPower does not have enough data
+#. to estimate battery life
+#: ../js/ui/status/power.js:72 ../js/ui/status/power.js:78
+msgid "Estimating…"
+msgstr "評估中…"
+
+#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
+#: ../js/ui/status/power.js:86
+#, javascript-format
+msgid "%d∶%02d Remaining (%d%%)"
+msgstr "剩餘時間 %d∶%02d (%d%%)"
+
+#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
+#: ../js/ui/status/power.js:91
+#, javascript-format
+msgid "%d∶%02d Until Full (%d%%)"
+msgstr "直到充滿還需 %d∶%02d (%d%%)"
+
+#. The menu only appears when airplane mode is on, so just
+#. statically build it as if it was on, rather than dynamically
+#. changing the menu contents.
+#: ../js/ui/status/rfkill.js:88
+#, fuzzy
+#| msgid "Airplane Mode is On"
+msgid "Airplane Mode On"
+msgstr "飛安模式開啟"
+
+#: ../js/ui/status/system.js:343
+msgid "Switch User"
+msgstr "切換使用者"
+
+#: ../js/ui/status/system.js:348
+msgid "Log Out"
+msgstr "登出"
+
+#: ../js/ui/status/system.js:353
+#, fuzzy
+#| msgid "Power Settings"
+msgid "Account Settings"
+msgstr "電源設定值"
+
+#: ../js/ui/status/system.js:370
+msgid "Orientation Lock"
+msgstr "方向鎖"
+
+#: ../js/ui/status/system.js:378
+msgid "Suspend"
+msgstr "暫停"
+
+#: ../js/ui/status/system.js:381
+msgid "Power Off"
+msgstr "關閉電源"
+
+#: ../js/ui/status/volume.js:127
+msgid "Volume changed"
+msgstr "音量已更改"
+
+#: ../js/ui/status/volume.js:162
+msgid "Volume"
+msgstr "音量"
+
+#: ../js/ui/status/volume.js:213
+msgid "Microphone"
+msgstr "麥克風"
+
+#: ../js/ui/unlockDialog.js:67
+msgid "Log in as another user"
+msgstr "以另一個使用者身分登入"
+
+#: ../js/ui/unlockDialog.js:84
+msgid "Unlock Window"
+msgstr "解鎖視窗"
+
+#: ../js/ui/viewSelector.js:159
+msgid "Applications"
+msgstr "應用程式"
+
+#: ../js/ui/viewSelector.js:163
+msgid "Search"
+msgstr "搜尋"
+
+#: ../js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "「%s」已就緒"
+
+#: ../js/ui/windowManager.js:63
+msgid "Do you want to keep these display settings?"
+msgstr "你想要保留這些顯示器設定值嗎?"
+
+#. Translators: this and the following message should be limited in lenght,
+#. to avoid ellipsizing the labels.
+#.
+#: ../js/ui/windowManager.js:82
+msgid "Revert Settings"
+msgstr "還原設定值"
+
+#: ../js/ui/windowManager.js:85
+msgid "Keep Changes"
+msgstr "保留更改"
+
+#: ../js/ui/windowManager.js:103
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "設定值的更改會在 %d 秒內還原"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: ../js/ui/windowManager.js:658
+#, javascript-format
+msgid "%d x %d"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:34
+msgid "Minimize"
+msgstr "最小化"
+
+#: ../js/ui/windowMenu.js:41
+msgid "Unmaximize"
+msgstr "取消最大化"
+
+#: ../js/ui/windowMenu.js:45
+msgid "Maximize"
+msgstr "最大化"
+
+#: ../js/ui/windowMenu.js:52
+msgid "Move"
+msgstr "移動"
+
+#: ../js/ui/windowMenu.js:58
+msgid "Resize"
+msgstr "重設大小"
+
+#: ../js/ui/windowMenu.js:65
+msgid "Move Titlebar Onscreen"
+msgstr "移動螢幕標題列"
+
+#: ../js/ui/windowMenu.js:70
+msgid "Always on Top"
+msgstr "永遠在最上層"
+
+#: ../js/ui/windowMenu.js:89
+msgid "Always on Visible Workspace"
+msgstr "永遠在顯示的工作區"
+
+#: ../js/ui/windowMenu.js:105
+#, fuzzy
+#| msgid "Move to Workspace Up"
+msgid "Move to Workspace Left"
+msgstr "將工作區上移"
+
+#: ../js/ui/windowMenu.js:110
+#, fuzzy
+#| msgid "Move to Workspace Up"
+msgid "Move to Workspace Right"
+msgstr "將工作區上移"
+
+#: ../js/ui/windowMenu.js:115
+msgid "Move to Workspace Up"
+msgstr "將工作區上移"
+
+#: ../js/ui/windowMenu.js:120
+msgid "Move to Workspace Down"
+msgstr "將工作區下移"
+
+#: ../js/ui/windowMenu.js:136
+#, fuzzy
+#| msgid "Move to Workspace Up"
+msgid "Move to Monitor Up"
+msgstr "將工作區上移"
+
+#: ../js/ui/windowMenu.js:142
+#, fuzzy
+#| msgid "Move to Workspace Down"
+msgid "Move to Monitor Down"
+msgstr "將工作區下移"
+
+#: ../js/ui/windowMenu.js:148
+msgid "Move to Monitor Left"
+msgstr ""
+
+#: ../js/ui/windowMenu.js:154
+msgid "Move to Monitor Right"
+msgstr ""
+
+#: ../src/calendar-server/evolution-calendar.desktop.in.in.h:1
+msgid "Evolution Calendar"
+msgstr "Evolution 行事曆"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1835
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u 輸出"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: ../src/gvc/gvc-mixer-control.c:1845
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u 輸入"
+
+#: ../src/gvc/gvc-mixer-control.c:2371
+msgid "System Sounds"
+msgstr "系統音效"
+
+#: ../src/main.c:381
+msgid "Print version"
+msgstr "顯示版本"
+
+#: ../src/main.c:387
+msgid "Mode used by GDM for login screen"
+msgstr "GDM 在登入畫面使用的模式"
+
+#: ../src/main.c:393
+msgid "Use a specific mode, e.g. \"gdm\" for login screen"
+msgstr "使用指定的模式,例如「gdm」為登入畫面"
+
+#: ../src/main.c:399
+msgid "List possible modes"
+msgstr "列出可能的模式"
+
+#: ../src/shell-app.c:246
+msgctxt "program"
+msgid "Unknown"
+msgstr "不明"
+
+#: ../src/shell-app.c:487
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "無法啟動「%s」"
+
+#: ../src/shell-keyring-prompt.c:730
+msgid "Passwords do not match."
+msgstr "密碼不相符。"
+
+#: ../src/shell-keyring-prompt.c:738
+msgid "Password cannot be blank"
+msgstr "密碼不能為空白"
+
+#: ../src/shell-polkit-authentication-agent.c:353
+msgid "Authentication dialog was dismissed by the user"
+msgstr "核對對話盒被使用者取消了"
+
+#~ msgid "Show the message tray"
+#~ msgstr "顯示資訊匣"
+
+#~ msgid "Captive Portal"
+#~ msgstr "網頁入口管制"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%p %l∶%M"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "日"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "一"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "二"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "三"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "四"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "五"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "六"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "沒有預訂行程"
+
+#~ msgid "Today"
+#~ msgstr "今天"
+
+#~ msgid "Tomorrow"
+#~ msgstr "明天"
+
+#~ msgid "This week"
+#~ msgstr "本週"
+
+#~ msgid "Next week"
+#~ msgstr "下週"
+
+#~ msgid "Removable Devices"
+#~ msgstr "可移除式裝置"
+
+#~ msgid "Eject"
+#~ msgstr "退出"
+
+#~ msgid "Invitation"
+#~ msgstr "邀請"
+
+#~ msgid "Call"
+#~ msgstr "通話"
+
+#~ msgid "File Transfer"
+#~ msgstr "檔案傳輸程式"
+
+#~ msgid "Chat"
+#~ msgstr "聊天"
+
+#~ msgid "Unmute"
+#~ msgstr "取消靜音"
+
+#~ msgid "Mute"
+#~ msgstr "靜音"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "邀請加入 %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s 正在邀請你加入 %s"
+
+#~ msgid "Decline"
+#~ msgstr "拒絕"
+
+#~ msgid "Accept"
+#~ msgstr "接受"
+
+#~ msgid "Video call from %s"
+#~ msgstr "來自 %s 的視像電話"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s 來電"
+
+#~ msgid "Answer"
+#~ msgstr "接聽"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s 正傳送給你 %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s 想要得到查看你上線狀態的許可"
+
+#~ msgid "Authentication failed"
+#~ msgstr "核對失敗"
+
+#~ msgid "Encryption error"
+#~ msgstr "加密發生錯誤"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "沒有提供證書"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "證書不被信任"
+
+#~ msgid "Certificate expired"
+#~ msgstr "證書已逾期"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "證書尚未使用"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "證書主機名稱不符"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "證書數碼指紋不符"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "證書為自我簽署"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "狀態設為離線"
+
+#~ msgid "Encryption is not available"
+#~ msgstr "加密無法使用"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "證書無效"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "連線遭到拒絕"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "無法建立連線"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "已失去連線"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "這個帳號已連接至該伺服器"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "連線已被使用相同資源的新連線取代"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "該帳號已經存在於伺服器上"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "伺服器目前過於忙碌以致於無法處理該連線"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "證書已被撤銷"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "證書使用不安全的密碼演算法,或是密碼處理上較弱"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "伺服器證書的長度,或是伺服器證書鏈的深度超過密碼函式庫所能處理的範圍"
+
+#~ msgid "Internal error"
+#~ msgstr "內部的錯誤"
+
+#~ msgid "Unable to connect to %s"
+#~ msgstr "無法連線到 %s"
+
+#~ msgid "View account"
+#~ msgstr "檢視帳號"
+
+#~ msgid "Unknown reason"
+#~ msgstr "不明原因"
+
+#~ msgid "Open Calendar"
+#~ msgstr "開啟行事曆"
+
+#~ msgid "Date & Time Settings"
+#~ msgstr "日期與時刻設定值"
+
+#~ msgid "Open"
+#~ msgstr "開啟"
+
+#~ msgid "Remove"
+#~ msgstr "移除"
+
+#~ msgid "Clear Messages"
+#~ msgstr "清除訊息"
+
+#~ msgid "Notification Settings"
+#~ msgstr "通知設定值"
+
+#~ msgid "Tray Menu"
+#~ msgstr "系統匣選單"
+
+#~ msgid "No Messages"
+#~ msgstr "沒有訊息"
+
+#~ msgid "Message Tray"
+#~ msgstr "訊息匣"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d 已連線的裝置"
+
+#~ msgid "Authentication required"
+#~ msgstr "要求核對"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "電池"
+
+#~ msgid "Airplane Mode"
+#~ msgstr "飛安模式"
+
+#~ msgid "On"
+#~ msgstr "開啟"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "地理位置的最高精確等級。"
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "設定應用程式可查看的地理位置最高精確等級。有效的選項為「off」(停用地理位置"
+#~ "追蹤)、「country」(國家)、「city」(縣市)、「neiborhood」(鄰里)、「street」"
+#~ "(街路)、「exact」(一般需要 GPS 接收器)。請務必記住這只能控制 GeoClue 所允"
+#~ "許應用程式查看的等級,而程式可自行使用本身的網路資源尋找使用者的所在位置 "
+#~ "(雖然精確度最高只能到達街路等級)。"
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "標題列上的按鈕排列"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "這個設定鍵會在執行 GNOME Shell 時覆蓋 org.gnome.desktop.wm.preferences 中"
+#~ "的設定鍵。"
+
+#~ msgid "Extension"
+#~ msgstr "擴充功能"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "使用上面的組合方塊選擇要設定的擴充功能。"
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>,<b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%m月</b><b>%d日</b>,<b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%Y年</b><b>%m月</b><b>%d日</b>,<b>%H:%M</b> "
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "要顯示為資料夾的分類清單"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "這個清單中的每個分類名稱都會在應用程式檢視中以資料夾表示,而不是在主要檢視"
+#~ "中直接顯示。"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "來自「%s」的核對要求"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "裝置 %s 想要和這個電腦配對"
+
+#~ msgid "Allow"
+#~ msgstr "允許"
+
+#~ msgid "Deny"
+#~ msgstr "禁止"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "裝置 %s 想要存取服務「%s」"
+
+#~ msgid "Always grant access"
+#~ msgstr "永遠准許存取"
+
+#~ msgid "Grant this time only"
+#~ msgstr "只有這次准許"
+
+#~ msgid "Reject"
+#~ msgstr "拒絕"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s 的配對確認"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "請確認通行碼「%06d」是否和裝置上的相符。"
+
+#~ msgid "Matches"
+#~ msgstr "相符"
+
+#~ msgid "Does not match"
+#~ msgstr "不相符"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s 的配對請求"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "請輸入裝置所提及的 PIN。"
+
+#~ msgid "OK"
+#~ msgstr "確定"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "抱歉,今天可能有些問題:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s Oracle 說:"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
diff --git a/po/zh_TW.po b/po/zh_TW.po
new file mode 100644
index 0000000..cbadece
--- /dev/null
+++ b/po/zh_TW.po
@@ -0,0 +1,3623 @@
+# Chinese (Taiwan) translation for gnome-shell.
+# Copyright (C) 2010 gnome-shell's COPYRIGHT HOLDER
+# This file is distributed under the same license as the gnome-shell package.
+# Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>, 2010.
+# Wei-Lun Chao <chaoweilun@gmail.com>, 2010.
+# Freddy Cheng <freddy4212@gmail.com>, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell 3.3.90\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
+"POT-Creation-Date: 2022-09-17 18:27+0000\n"
+"PO-Revision-Date: 2022-10-01 15:55+0800\n"
+"Last-Translator: Cheng-Chia Tseng <pswo10680@gmail.com>\n"
+"Language-Team: Chinese - Taiwan <chinese-l10n@googlegroups.com>\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 3.1.1\n"
+
+#: data/50-gnome-shell-launchers.xml:6
+msgid "Launchers"
+msgstr "啟動應用程式"
+
+#: data/50-gnome-shell-launchers.xml:10
+msgid "Activate favorite application 1"
+msgstr "啟動喜好應用程式 1"
+
+#: data/50-gnome-shell-launchers.xml:14
+msgid "Activate favorite application 2"
+msgstr "啟動喜好應用程式 2"
+
+#: data/50-gnome-shell-launchers.xml:18
+msgid "Activate favorite application 3"
+msgstr "啟動喜好應用程式 3"
+
+#: data/50-gnome-shell-launchers.xml:22
+msgid "Activate favorite application 4"
+msgstr "啟動喜好應用程式 4"
+
+#: data/50-gnome-shell-launchers.xml:26
+msgid "Activate favorite application 5"
+msgstr "啟動喜好應用程式 5"
+
+#: data/50-gnome-shell-launchers.xml:30
+msgid "Activate favorite application 6"
+msgstr "啟動喜好應用程式 6"
+
+#: data/50-gnome-shell-launchers.xml:34
+msgid "Activate favorite application 7"
+msgstr "啟動喜好應用程式 7"
+
+#: data/50-gnome-shell-launchers.xml:38
+msgid "Activate favorite application 8"
+msgstr "啟動喜好應用程式 8"
+
+#: data/50-gnome-shell-launchers.xml:42
+msgid "Activate favorite application 9"
+msgstr "啟動喜好應用程式 9"
+
+#. Translators: name of the folder under ~/Pictures for screenshots.
+#: data/50-gnome-shell-screenshots.xml:6 js/ui/screenshot.js:2079
+msgid "Screenshots"
+msgstr "螢幕快照"
+
+#: data/50-gnome-shell-screenshots.xml:9
+#: data/org.gnome.shell.gschema.xml.in:234
+msgid "Take a screenshot interactively"
+msgstr "啟動螢幕快照工具"
+
+#: data/50-gnome-shell-screenshots.xml:12
+#: data/org.gnome.shell.gschema.xml.in:246
+msgid "Take a screenshot"
+msgstr "擷取螢幕快照"
+
+#: data/50-gnome-shell-screenshots.xml:15
+#: data/org.gnome.shell.gschema.xml.in:242
+msgid "Take a screenshot of a window"
+msgstr "擷取視窗快照"
+
+#: data/50-gnome-shell-screenshots.xml:18
+#: data/org.gnome.shell.gschema.xml.in:238
+msgid "Record a screencast interactively"
+msgstr "啟動螢幕錄影工具"
+
+#: data/50-gnome-shell-system.xml:6
+msgid "System"
+msgstr "系統"
+
+#: data/50-gnome-shell-system.xml:9
+msgid "Show the notification list"
+msgstr "顯示通知列表"
+
+#: data/50-gnome-shell-system.xml:12
+msgid "Focus the active notification"
+msgstr "聚焦彈出的通知"
+
+#: data/50-gnome-shell-system.xml:15
+msgid "Show the overview"
+msgstr "顯示概覽畫面"
+
+#: data/50-gnome-shell-system.xml:18
+msgid "Show all applications"
+msgstr "顯示所有應用程式"
+
+#: data/50-gnome-shell-system.xml:21
+msgid "Open the application menu"
+msgstr "開啟應用程式選單"
+
+#: data/org.gnome.Shell.desktop.in.in:4
+msgid "GNOME Shell"
+msgstr "GNOME Shell"
+
+#: data/org.gnome.Shell.desktop.in.in:5
+msgid "Window management and application launching"
+msgstr "兼具視窗管理器與應用程式啟動器的使用者介面"
+
+#: data/org.gnome.shell.gschema.xml.in:6
+msgid "Enable internal tools useful for developers and testers from Alt-F2"
+msgstr "啟用後便能透過 Alt-F2 來開啟專為「開發者」與「測試者」所打造的內部工具"
+
+#: data/org.gnome.shell.gschema.xml.in:9
+msgid ""
+"Allows access to internal debugging and monitoring tools using the Alt-F2 "
+"dialog."
+msgstr "允許透過 Alt-F2 來存取內部除錯工具與監控工具。"
+
+#: data/org.gnome.shell.gschema.xml.in:16
+msgid "UUIDs of extensions to enable"
+msgstr "要啟用之擴充套件的 UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:17
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be loaded. Any extension that wants to be loaded needs to be in this "
+"list. You can also manipulate this list with the EnableExtension and "
+"DisableExtension D-Bus methods on org.gnome.Shell."
+msgstr ""
+"《GNOME Shell》擴充套件均擁有 UUID 屬性;該設定鍵會將所有應載入的擴充套件列出"
+"來。您也可以透過 org.gnome.Shell 中的 EnableExtension 和 DisableExtension 的 "
+"D-Bus 方法來控制該列表。"
+
+#: data/org.gnome.shell.gschema.xml.in:26
+msgid "UUIDs of extensions to force disabling"
+msgstr "強制停用之擴充套件的 UUID"
+
+#: data/org.gnome.shell.gschema.xml.in:27
+msgid ""
+"GNOME Shell extensions have a UUID property; this key lists extensions which "
+"should be disabled, even if loaded as part of the current mode. You can also "
+"manipulate this list with the EnableExtension and DisableExtension D-Bus "
+"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
+"extensions” setting."
+msgstr ""
+"《GNOME Shell》擴充套件均擁有 UUID 屬性;該設定鍵會將所有應停用的擴充套件列出"
+"來,即使它是做為目前模式的一部分載入的。您也可以透過 org.gnome.Shell 中的 "
+"EnableExtension 和 DisableExtension 的 D-Bus 方法來控制該列表。該設定鍵較"
+"「enabled-extensions」更為優先。"
+
+#: data/org.gnome.shell.gschema.xml.in:37
+msgid "Disable user extensions"
+msgstr "停用所有的使用者擴充套件"
+
+#: data/org.gnome.shell.gschema.xml.in:38
+msgid ""
+"Disable all extensions the user has enabled without affecting the “enabled-"
+"extension” setting."
+msgstr ""
+"停用所有由使用者所啟用之擴充套件,但不會影響「enabled-extension」的值。"
+
+#: data/org.gnome.shell.gschema.xml.in:45
+msgid "Disables the validation of extension version compatibility"
+msgstr "停用擴充套件的版本相容性驗證"
+
+#: data/org.gnome.shell.gschema.xml.in:46
+msgid ""
+"GNOME Shell will only load extensions that claim to support the current "
+"running version. Enabling this option will disable this check and try to "
+"load all extensions regardless of the versions they claim to support."
+msgstr ""
+"《GNOME Shell》僅會載入宣稱支援目前運作版本的擴充套件。啟用該設定鍵後會停用這"
+"項檢查,並嘗試載入所有擴充套件而不去管它們所宣稱支援的版本為何。"
+
+#: data/org.gnome.shell.gschema.xml.in:54
+msgid "List of desktop file IDs for favorite applications"
+msgstr "喜好應用程式之 Desktop 檔的 ID 列表"
+
+#: data/org.gnome.shell.gschema.xml.in:55
+msgid ""
+"The applications corresponding to these identifiers will be displayed in the "
+"favorites area."
+msgstr "辨識碼所對應的應用程式會顯示於「喜好」中。"
+
+#: data/org.gnome.shell.gschema.xml.in:62
+msgid "History for command (Alt-F2) dialog"
+msgstr "命令 (Alt-F2) 的歷史紀錄"
+
+#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
+#: data/org.gnome.shell.gschema.xml.in:67
+msgid "History for the looking glass dialog"
+msgstr "Looking glass 對話框的歷史紀錄"
+
+#: data/org.gnome.shell.gschema.xml.in:71
+msgid "Always show the “Log out” menu item in the user menu."
+msgstr "永遠在使用者選單顯示「登出」項目。"
+
+#: data/org.gnome.shell.gschema.xml.in:72
+msgid ""
+"This key overrides the automatic hiding of the “Log out” menu item in single-"
+"user, single-session situations."
+msgstr ""
+"該設定鍵會覆寫選單在「單一使用者、單一作業階段」情況下對「登出」項目的隱藏。"
+
+#: data/org.gnome.shell.gschema.xml.in:79
+msgid ""
+"Whether to remember password for mounting encrypted or remote filesystems"
+msgstr "是否要記住已掛載之加密或遠端檔案系統的密碼"
+
+#: data/org.gnome.shell.gschema.xml.in:80
+msgid ""
+"The shell will request a password when an encrypted device or a remote "
+"filesystem is mounted. If the password can be saved for future use a "
+"“Remember Password” checkbox will be present. This key sets the default "
+"state of the checkbox."
+msgstr ""
+"當掛載加密裝置或遠端檔案系統時會要求密碼。若密碼可儲存供未來使用時,會顯示"
+"「記住密碼」勾選框;這個設定鍵用來設定勾選框的預設狀態。"
+
+#: data/org.gnome.shell.gschema.xml.in:89
+msgid "The last selected non-default power profile"
+msgstr "此前所使用的非預設電源設定檔"
+
+#: data/org.gnome.shell.gschema.xml.in:90
+msgid ""
+"Some systems support more than two power profiles. In order to still support "
+"toggling between two profiles, this key records the last selected non-"
+"default profile."
+msgstr ""
+"某些系統支援兩種以上的電源設定檔(省電與平衡之外的模式)。為了做到在兩種電源"
+"模式間切換,本設定鍵會紀錄此前所使用之非預設的電源設定檔為何。"
+
+#: data/org.gnome.shell.gschema.xml.in:98
+msgid "The last version the “Welcome to GNOME” dialog was shown for"
+msgstr "「歡迎使用 GNOME」對話框顯示的最新版本"
+
+#: data/org.gnome.shell.gschema.xml.in:99
+msgid ""
+"This key determines for which version the “Welcome to GNOME” dialog was last "
+"shown. An empty string represents the oldest possible version, and a huge "
+"number will represent versions that do not exist yet. This huge number can "
+"be used to effectively disable the dialog."
+msgstr ""
+"此設定鍵為「歡迎使用 GNOME」所顯示的最新版本。空白字串代表可能為舊版本,或是"
+"由過大的數字代表尚未存在的版本。過大的數字可用於停用對話框。"
+
+#: data/org.gnome.shell.gschema.xml.in:132
+msgid "Layout of the app picker"
+msgstr "應用程式列表的排列順序"
+
+#: data/org.gnome.shell.gschema.xml.in:133
+msgid ""
+"Layout of the app picker. Each entry in the array is a page. Pages are "
+"stored in the order they appear in GNOME Shell. Each page contains an "
+"“application id” → 'data' pair. Currently, the following values are stored "
+"as 'data': • “position”: the position of the application icon in the page"
+msgstr ""
+"應用程式列表的排列順序。陣列中的每個元素為一頁,並會以指定順序順序出現在"
+"《GNOME Shell》中。每一頁包含 'application id' → 'data' pair' 鍵值對。下列值"
+"以 'data': • “position” 方式儲存:應用程式圖示位於頁面上的位置。"
+
+#: data/org.gnome.shell.gschema.xml.in:148
+msgid "Keybinding to open the application menu"
+msgstr "開啟應用程式選單的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:149
+msgid "Keybinding to open the application menu."
+msgstr "開啟應用程式選單的組合鍵。"
+
+#: data/org.gnome.shell.gschema.xml.in:155
+#: data/org.gnome.shell.gschema.xml.in:162
+msgid "Keybinding to shift between overview states"
+msgstr "在兩種概覽畫面之間切換的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:156
+msgid "Keybinding to shift between session, window picker and app grid"
+msgstr "在作業階段、視窗概覽與應用程式列表之間切換的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:163
+msgid "Keybinding to shift between app grid, window picker and session"
+msgstr "在應用程式列表、視窗概覽與作業階段之間切換的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:169
+msgid "Keybinding to open the “Show Applications” view"
+msgstr "開啟「顯示應用程式」的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:170
+msgid ""
+"Keybinding to open the “Show Applications” view of the Activities Overview."
+msgstr "開啟概覽中「顯示應用程式」的組合鍵。"
+
+#: data/org.gnome.shell.gschema.xml.in:177
+msgid "Keybinding to open the overview"
+msgstr "開啟概覽畫面的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:178
+msgid "Keybinding to open the Activities Overview."
+msgstr "用於開啟概覽畫面的組合鍵。"
+
+#: data/org.gnome.shell.gschema.xml.in:184
+msgid "Keybinding to toggle the visibility of the notification list"
+msgstr "切換訊息列表顯示的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:185
+msgid "Keybinding to toggle the visibility of the notification list."
+msgstr "用於切換訊息列表顯示的組合鍵。"
+
+#: data/org.gnome.shell.gschema.xml.in:191
+msgid "Keybinding to focus the active notification"
+msgstr "聚焦至彈出通知的組合鍵"
+
+#: data/org.gnome.shell.gschema.xml.in:192
+msgid "Keybinding to focus the active notification."
+msgstr "用於聚焦至彈出通知的組合鍵。"
+
+#: data/org.gnome.shell.gschema.xml.in:198
+msgid "Switch to application 1"
+msgstr "切換至應用程式 1"
+
+#: data/org.gnome.shell.gschema.xml.in:202
+msgid "Switch to application 2"
+msgstr "切換至應用程式 2"
+
+#: data/org.gnome.shell.gschema.xml.in:206
+msgid "Switch to application 3"
+msgstr "切換至應用程式 3"
+
+#: data/org.gnome.shell.gschema.xml.in:210
+msgid "Switch to application 4"
+msgstr "切換至應用程式 4"
+
+#: data/org.gnome.shell.gschema.xml.in:214
+msgid "Switch to application 5"
+msgstr "切換至應用程式 5"
+
+#: data/org.gnome.shell.gschema.xml.in:218
+msgid "Switch to application 6"
+msgstr "切換至應用程式 6"
+
+#: data/org.gnome.shell.gschema.xml.in:222
+msgid "Switch to application 7"
+msgstr "切換至應用程式 7"
+
+#: data/org.gnome.shell.gschema.xml.in:226
+msgid "Switch to application 8"
+msgstr "切換至應用程式 8"
+
+#: data/org.gnome.shell.gschema.xml.in:230
+msgid "Switch to application 9"
+msgstr "切換至應用程式 9"
+
+#: data/org.gnome.shell.gschema.xml.in:255
+#: data/org.gnome.shell.gschema.xml.in:282
+msgid "Limit switcher to current workspace."
+msgstr "限制視窗切換器使用目前的工作區"
+
+#: data/org.gnome.shell.gschema.xml.in:256
+msgid ""
+"If true, only applications that have windows on the current workspace are "
+"shown in the switcher. Otherwise, all applications are included."
+msgstr ""
+"若設為「true」,切換器將僅顯示目前工作區中的應用程式視窗,否則所有的應用程式"
+"都會包含在內。"
+
+#: data/org.gnome.shell.gschema.xml.in:273
+msgid "The application icon mode."
+msgstr "應用程式圖示模式"
+
+#: data/org.gnome.shell.gschema.xml.in:274
+msgid ""
+"Configures how the windows are shown in the switcher. Valid possibilities "
+"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
+"only” (shows only the application icon) or “both”."
+msgstr ""
+"設定視窗在視窗切換器中如何顯示。有效的數值為「thumbnail-only」(顯示視窗縮"
+"圖)、「app-icon-only」(只顯示應用程式圖示)或「both」。"
+
+#: data/org.gnome.shell.gschema.xml.in:283
+msgid ""
+"If true, only windows from the current workspace are shown in the switcher. "
+"Otherwise, all windows are included."
+msgstr ""
+"若設為「true」,切換器將僅顯示目前工作區中的視窗,否則所有的視窗都會包含在"
+"內。"
+
+#: data/org.gnome.shell.gschema.xml.in:293
+msgid "Locations"
+msgstr "位置"
+
+#: data/org.gnome.shell.gschema.xml.in:294
+msgid "The locations to show in world clocks"
+msgstr "在世界時鐘顯示的位置。"
+
+#: data/org.gnome.shell.gschema.xml.in:304
+msgid "Automatic location"
+msgstr "自動位置"
+
+#: data/org.gnome.shell.gschema.xml.in:305
+msgid "Whether to fetch the current location or not"
+msgstr "是否取得目前所在地。"
+
+#: data/org.gnome.shell.gschema.xml.in:312
+msgid "Location"
+msgstr "位置"
+
+#: data/org.gnome.shell.gschema.xml.in:313
+msgid "The location for which to show a forecast"
+msgstr "要顯示哪個區域的天氣預報。"
+
+#: data/org.gnome.shell.gschema.xml.in:325
+msgid "Attach modal dialog to the parent window"
+msgstr "將模態對話框附加至上層視窗"
+
+#: data/org.gnome.shell.gschema.xml.in:326
+#: data/org.gnome.shell.gschema.xml.in:335
+#: data/org.gnome.shell.gschema.xml.in:343
+#: data/org.gnome.shell.gschema.xml.in:351
+#: data/org.gnome.shell.gschema.xml.in:359
+msgid ""
+"This key overrides the key in org.gnome.mutter when running GNOME Shell."
+msgstr "該設定鍵會在《GNOME Shell》執行時覆寫 org.gnome.mutter 中的設定鍵。"
+
+#: data/org.gnome.shell.gschema.xml.in:334
+msgid "Enable edge tiling when dropping windows on screen edges"
+msgstr "在螢幕邊緣放下視窗時啟用邊緣拼貼"
+
+#: data/org.gnome.shell.gschema.xml.in:342
+msgid "Workspaces are managed dynamically"
+msgstr "以動態方式管理工作區"
+
+#: data/org.gnome.shell.gschema.xml.in:350
+msgid "Workspaces only on primary monitor"
+msgstr "只在主螢幕上使用工作區"
+
+#: data/org.gnome.shell.gschema.xml.in:358
+msgid "Delay focus changes in mouse mode until the pointer stops moving"
+msgstr "在滑鼠模式中延遲焦點更改直到滑鼠指標停止移動"
+
+#: data/org.gnome.Shell.PortalHelper.desktop.in.in:3
+msgid "Network Login"
+msgstr "網路登入"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:15
+#: subprojects/extensions-app/data/ui/extensions-window.ui:144
+msgid "Something’s gone wrong"
+msgstr "有些地方出錯了"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:23
+msgid ""
+"We’re very sorry, but there’s been a problem: the settings for this "
+"extension can’t be displayed. We recommend that you report the issue to the "
+"extension authors."
+msgstr ""
+"我們非常抱歉發生這個問題:無法顯示該擴充套件的設定畫面;建議將此問題回報給擴"
+"充套件的作者。"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:48
+msgid "Technical Details"
+msgstr "技術細節"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:90
+msgid "Homepage"
+msgstr "首頁"
+
+#: js/dbusServices/extensions/ui/extension-error-page.ui:91
+msgid "Visit extension homepage"
+msgstr "造訪擴充套件首頁"
+
+#: js/gdm/authPrompt.js:144 js/ui/audioDeviceSelection.js:61
+#: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:141
+#: js/ui/endSessionDialog.js:440 js/ui/extensionDownloader.js:223
+#: js/ui/shellMountOperation.js:377 js/ui/shellMountOperation.js:387
+#: subprojects/extensions-app/js/main.js:173
+msgid "Cancel"
+msgstr "取消"
+
+#: js/gdm/authPrompt.js:307 js/ui/components/networkAgent.js:209
+#: js/ui/components/networkAgent.js:229 js/ui/components/networkAgent.js:261
+#: js/ui/components/networkAgent.js:294 js/ui/components/networkAgent.js:333
+#: js/ui/components/networkAgent.js:346 js/ui/components/polkitAgent.js:283
+#: js/ui/shellMountOperation.js:327
+msgid "Password"
+msgstr "密碼"
+
+#: js/gdm/loginDialog.js:317
+msgid "Choose Session"
+msgstr "選擇作業階段"
+
+#: js/gdm/loginDialog.js:462
+msgid "Not listed?"
+msgstr "沒有列出來?"
+
+#. Translators: this message is shown below the username entry field
+#. to clue the user in on how to login to the local network realm
+#: js/gdm/loginDialog.js:930
+#, javascript-format
+msgid "(e.g., user or %s)"
+msgstr "(例如: user 或 %s)"
+
+#: js/gdm/loginDialog.js:935 js/ui/components/networkAgent.js:253
+#: js/ui/components/networkAgent.js:288 js/ui/components/networkAgent.js:322
+msgid "Username"
+msgstr "使用者名稱"
+
+#: js/gdm/loginDialog.js:1258
+msgid "Login Window"
+msgstr "登入視窗"
+
+#: js/gdm/util.js:431
+msgid "Authentication error"
+msgstr "身分核對錯誤"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can swipe their finger on the fingerprint reader
+#: js/gdm/util.js:603
+msgid "(or swipe finger across reader)"
+msgstr "(或是將手指掃過指紋辨識器)"
+
+#. Translators: this message is shown below the password entry field
+#. to indicate the user can place their finger on the fingerprint reader instead
+#: js/gdm/util.js:608
+msgid "(or place finger on reader)"
+msgstr "(或是將手指置於指紋辨識器上)"
+
+#. Translators: The name of the power-off action in search
+#: js/misc/systemActions.js:84
+msgctxt "search-result"
+msgid "Power Off"
+msgstr "關閉電源"
+
+#. Translators: A list of keywords that match the power-off action, separated by semicolons
+#: js/misc/systemActions.js:87
+msgid "power off;shutdown;halt;stop"
+msgstr "power off;shutdown;halt;stop;關機;關閉電源;中斷;停止"
+
+#. Translators: The name of the restart action in search
+#: js/misc/systemActions.js:92
+msgctxt "search-result"
+msgid "Restart"
+msgstr "重新啟動"
+
+#. Translators: A list of keywords that match the restart action, separated by semicolons
+#: js/misc/systemActions.js:95
+msgid "reboot;restart;"
+msgstr "reboot;restart;重新開機;重新啟動;"
+
+#. Translators: The name of the lock screen action in search
+#: js/misc/systemActions.js:100
+msgctxt "search-result"
+msgid "Lock Screen"
+msgstr "鎖定螢幕"
+
+#. Translators: A list of keywords that match the lock screen action, separated by semicolons
+#: js/misc/systemActions.js:103
+msgid "lock screen"
+msgstr "lock screen;螢幕鎖定"
+
+#. Translators: The name of the logout action in search
+#: js/misc/systemActions.js:108
+msgctxt "search-result"
+msgid "Log Out"
+msgstr "登出"
+
+#. Translators: A list of keywords that match the logout action, separated by semicolons
+#: js/misc/systemActions.js:111
+msgid "logout;log out;sign off"
+msgstr "logout;log out;sign off;登出"
+
+#. Translators: The name of the suspend action in search
+#: js/misc/systemActions.js:116
+msgctxt "search-result"
+msgid "Suspend"
+msgstr "暫停"
+
+#. Translators: A list of keywords that match the suspend action, separated by semicolons
+#: js/misc/systemActions.js:119
+msgid "suspend;sleep"
+msgstr "suspend;sleep;暫停;睡眠"
+
+#. Translators: The name of the switch user action in search
+#: js/misc/systemActions.js:124
+msgctxt "search-result"
+msgid "Switch User"
+msgstr "切換使用者"
+
+#. Translators: A list of keywords that match the switch user action, separated by semicolons
+#: js/misc/systemActions.js:127
+msgid "switch user"
+msgstr "switch user;切換使用者"
+
+#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
+#: js/misc/systemActions.js:134
+msgid "lock orientation;unlock orientation;screen;rotation"
+msgstr ""
+"lock orientation;unlock orientation;screen;rotation;鎖定方向;解鎖方向;方向鎖;"
+"螢幕;旋轉"
+
+#. Translators: The name of the screenshot UI action in search
+#: js/misc/systemActions.js:139
+msgctxt "search-result"
+msgid "Take a Screenshot"
+msgstr "擷取螢幕快照"
+
+#. Translators: A list of keywords that match the screenshot UI action, separated by semicolons
+#: js/misc/systemActions.js:142
+msgid "screenshot;screencast;snip;capture;record"
+msgstr ""
+"screenshot;screencast;snip;capture;record;螢幕快照;擷圖;截圖;螢幕錄影;抓圖"
+
+#: js/misc/systemActions.js:242
+msgctxt "search-result"
+msgid "Unlock Screen Rotation"
+msgstr "解鎖螢幕旋轉"
+
+#: js/misc/systemActions.js:243
+msgctxt "search-result"
+msgid "Lock Screen Rotation"
+msgstr "鎖定螢幕旋轉"
+
+#: js/misc/util.js:129
+msgid "Command not found"
+msgstr "找不到命令"
+
+#. Replace "Error invoking GLib.shell_parse_argv: " with
+#. something nicer
+#: js/misc/util.js:166
+msgid "Could not parse command:"
+msgstr "無法分析命令:"
+
+#: js/misc/util.js:174
+#, javascript-format
+msgid "Execution of “%s” failed:"
+msgstr "無法執行「%s」:"
+
+#: js/misc/util.js:191
+msgid "Just now"
+msgstr "剛剛"
+
+#: js/misc/util.js:193
+#, javascript-format
+msgid "%d minute ago"
+msgid_plural "%d minutes ago"
+msgstr[0] "%d 分鐘前"
+
+#: js/misc/util.js:197
+#, javascript-format
+msgid "%d hour ago"
+msgid_plural "%d hours ago"
+msgstr[0] "%d 小時前"
+
+#: js/misc/util.js:201 js/ui/dateMenu.js:167
+msgid "Yesterday"
+msgstr "昨天"
+
+#: js/misc/util.js:203
+#, javascript-format
+msgid "%d day ago"
+msgid_plural "%d days ago"
+msgstr[0] "%d 天前"
+
+#: js/misc/util.js:207
+#, javascript-format
+msgid "%d week ago"
+msgid_plural "%d weeks ago"
+msgstr[0] "%d 週前"
+
+#: js/misc/util.js:211
+#, javascript-format
+msgid "%d month ago"
+msgid_plural "%d months ago"
+msgstr[0] "%d 個月前"
+
+#: js/misc/util.js:214
+#, javascript-format
+msgid "%d year ago"
+msgid_plural "%d years ago"
+msgstr[0] "%d 年前"
+
+#. Translators: Time in 24h format
+#: js/misc/util.js:247
+msgid "%H∶%M"
+msgstr "%H∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 24h format. i.e. "Yesterday, 14:30"
+#: js/misc/util.js:253
+#, no-c-format
+msgid "Yesterday, %H∶%M"
+msgstr "昨天 %H:%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 24h format. i.e. "Monday, 14:30"
+#: js/misc/util.js:259
+#, no-c-format
+msgid "%A, %H∶%M"
+msgstr "%A %H∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 24h format.
+#. i.e. "May 25, 14:30"
+#: js/misc/util.js:265
+#, no-c-format
+msgid "%B %-d, %H∶%M"
+msgstr "%-m月%-d日 %H∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 24h format.
+#. i.e. "May 25 2012, 14:30"
+#: js/misc/util.js:271
+#, no-c-format
+msgid "%B %-d %Y, %H∶%M"
+msgstr "%Y年%-m月%-d日 %H∶%M"
+
+#. Show only the time if date is on today
+#. eslint-disable-line no-lonely-if
+#. Translators: Time in 12h format
+#: js/misc/util.js:276
+msgid "%l∶%M %p"
+msgstr "%p %I∶%M"
+
+#. Translators: this is the word "Yesterday" followed by a
+#. time string in 12h format. i.e. "Yesterday, 2:30 pm"
+#: js/misc/util.js:282
+#, no-c-format
+msgid "Yesterday, %l∶%M %p"
+msgstr "昨天 %p %I∶%M"
+
+#. Translators: this is the week day name followed by a time
+#. string in 12h format. i.e. "Monday, 2:30 pm"
+#: js/misc/util.js:288
+#, no-c-format
+msgid "%A, %l∶%M %p"
+msgstr "%A %p %I∶%M"
+
+#. Translators: this is the month name and day number
+#. followed by a time string in 12h format.
+#. i.e. "May 25, 2:30 pm"
+#: js/misc/util.js:294
+#, no-c-format
+msgid "%B %-d, %l∶%M %p"
+msgstr "%-m月%-d日 %p %I∶%M"
+
+#. Translators: this is the month name, day number, year
+#. number followed by a time string in 12h format.
+#. i.e. "May 25 2012, 2:30 pm"
+#: js/misc/util.js:300
+#, no-c-format
+msgid "%B %-d %Y, %l∶%M %p"
+msgstr "%Y年%-m月%-d日 %p %I∶%M"
+
+#: js/portalHelper/main.js:55
+msgid "Hotspot Login"
+msgstr "登入熱點"
+
+#: js/portalHelper/main.js:108
+msgid ""
+"Your connection to this hotspot login is not secure. Passwords or other "
+"information you enter on this page can be viewed by people nearby."
+msgstr ""
+"您欲連接的熱點並未提供安全的登入環境,在此頁面上所輸入的密碼等資訊均有可能被"
+"他人看見。"
+
+#. No support for non-modal system dialogs, so ignore the option
+#. let modal = options['modal'] || true;
+#: js/ui/accessDialog.js:39 js/ui/status/location.js:350
+msgid "Deny Access"
+msgstr "禁止存取"
+
+#: js/ui/accessDialog.js:40 js/ui/status/location.js:355
+msgid "Grant Access"
+msgstr "允許存取"
+
+#: js/ui/appDisplay.js:1728
+msgid "Unnamed Folder"
+msgstr "未命名資料夾"
+
+#: js/ui/appFavorites.js:166
+#, javascript-format
+msgid "%s has been pinned to the dash."
+msgstr "《%s》已釘選至 Dash。"
+
+#: js/ui/appFavorites.js:199
+#, javascript-format
+msgid "%s has been unpinned from the dash."
+msgstr "《%s》已從 Dash 中取消釘選。"
+
+#. Translators: This is the heading of a list of open windows
+#: js/ui/appMenu.js:46
+msgid "Open Windows"
+msgstr "開啟的視窗"
+
+#: js/ui/appMenu.js:54
+msgid "New Window"
+msgstr "新視窗"
+
+#: js/ui/appMenu.js:81
+msgid "Show Details"
+msgstr "顯示詳細資訊"
+
+#: js/ui/appMenu.js:97
+msgid "Quit"
+msgstr "退出"
+
+#: js/ui/appMenu.js:157 js/ui/dash.js:249
+msgid "Unpin"
+msgstr "取消釘選"
+
+#: js/ui/appMenu.js:158
+msgid "Pin to Dash"
+msgstr "釘選至 Dash"
+
+#: js/ui/appMenu.js:175
+msgid "Launch using Integrated Graphics Card"
+msgstr "使用整合顯示卡啟動"
+
+#: js/ui/appMenu.js:176
+msgid "Launch using Discrete Graphics Card"
+msgstr "使用獨立顯示卡啟動"
+
+#: js/ui/audioDeviceSelection.js:41
+msgid "Select Audio Device"
+msgstr "選擇音訊裝置"
+
+#: js/ui/audioDeviceSelection.js:56 js/ui/status/volume.js:63
+msgid "Sound Settings"
+msgstr "音訊設定值"
+
+#: js/ui/audioDeviceSelection.js:69
+msgid "Headphones"
+msgstr "頭戴式耳機"
+
+#: js/ui/audioDeviceSelection.js:71
+msgid "Headset"
+msgstr "耳麥"
+
+#: js/ui/audioDeviceSelection.js:73 js/ui/status/volume.js:319
+msgid "Microphone"
+msgstr "麥克風"
+
+#: js/ui/backgroundMenu.js:14
+msgid "Change Background…"
+msgstr "更改背景…"
+
+#: js/ui/backgroundMenu.js:16
+msgid "Display Settings"
+msgstr "顯示器設定值"
+
+#: js/ui/backgroundMenu.js:17
+#: subprojects/extensions-app/data/ui/extension-row.ui:122
+msgid "Settings"
+msgstr "設定值"
+
+#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
+#: js/ui/calendar.js:35
+msgctxt "calendar-no-work"
+msgid "06"
+msgstr "06"
+
+#. Translators: Calendar grid abbreviation for Sunday.
+#. *
+#. * NOTE: These grid abbreviations are always shown together
+#. * and in order, e.g. "S M T W T F S".
+#.
+#: js/ui/calendar.js:61
+msgctxt "grid sunday"
+msgid "S"
+msgstr "日"
+
+#. Translators: Calendar grid abbreviation for Monday
+#: js/ui/calendar.js:63
+msgctxt "grid monday"
+msgid "M"
+msgstr "一"
+
+#. Translators: Calendar grid abbreviation for Tuesday
+#: js/ui/calendar.js:65
+msgctxt "grid tuesday"
+msgid "T"
+msgstr "二"
+
+#. Translators: Calendar grid abbreviation for Wednesday
+#: js/ui/calendar.js:67
+msgctxt "grid wednesday"
+msgid "W"
+msgstr "三"
+
+#. Translators: Calendar grid abbreviation for Thursday
+#: js/ui/calendar.js:69
+msgctxt "grid thursday"
+msgid "T"
+msgstr "四"
+
+#. Translators: Calendar grid abbreviation for Friday
+#: js/ui/calendar.js:71
+msgctxt "grid friday"
+msgid "F"
+msgstr "五"
+
+#. Translators: Calendar grid abbreviation for Saturday
+#: js/ui/calendar.js:73
+msgctxt "grid saturday"
+msgid "S"
+msgstr "六"
+
+#. *
+#. * Translators: The header displaying just the month name
+#. * standalone, when this is a month of the current year.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not change it.
+#.
+#: js/ui/calendar.js:414
+msgid "%OB"
+msgstr "%OB"
+
+#. *
+#. * Translators: The header displaying the month name and the year
+#. * number, when this is a month of a different year. You can
+#. * reorder the format specifiers or add other modifications
+#. * according to the requirements of your language.
+#. * "%OB" is the new format specifier introduced in glibc 2.27,
+#. * in most cases you should not use the old "%B" here unless you
+#. * absolutely know what you are doing.
+#.
+#: js/ui/calendar.js:424
+msgid "%OB %Y"
+msgstr "%Y年%OB"
+
+#: js/ui/calendar.js:485
+msgid "Previous month"
+msgstr "上個月"
+
+#: js/ui/calendar.js:503
+msgid "Next month"
+msgstr "下個月"
+
+#: js/ui/calendar.js:654
+#, no-javascript-format
+msgctxt "date day number format"
+msgid "%d"
+msgstr "%d"
+
+#: js/ui/calendar.js:713
+msgid "Week %V"
+msgstr "第 %V 週"
+
+#: js/ui/calendar.js:892
+msgid "No Notifications"
+msgstr "沒有通知"
+
+#: js/ui/calendar.js:949
+msgid "Do Not Disturb"
+msgstr "不要打擾"
+
+#: js/ui/calendar.js:970
+msgid "Clear"
+msgstr "清除"
+
+#. Translators: %s is an application name
+#: js/ui/closeDialog.js:40
+#, javascript-format
+msgid "“%s” is not responding."
+msgstr "《%s》沒有回應。"
+
+#: js/ui/closeDialog.js:41
+msgid ""
+"You may choose to wait a short while for it to continue or force the "
+"application to quit entirely."
+msgstr "您可以選擇等待讓它繼續執行,或強制它立即退出。"
+
+#: js/ui/closeDialog.js:69
+msgid "Force Quit"
+msgstr "強制退出"
+
+#: js/ui/closeDialog.js:74
+msgid "Wait"
+msgstr "等待"
+
+#: js/ui/components/automountManager.js:84
+msgid "External drive connected"
+msgstr "外部裝置已連接"
+
+#: js/ui/components/automountManager.js:96
+msgid "External drive disconnected"
+msgstr "外部裝置已拔除"
+
+#: js/ui/components/automountManager.js:207
+msgid "Unable to unlock volume"
+msgstr "無法解鎖磁碟區"
+
+#: js/ui/components/automountManager.js:208
+msgid "The installed udisks version does not support the PIM setting"
+msgstr "系統所安裝的《udisks》版本無法設定 PIM"
+
+#: js/ui/components/autorunManager.js:316
+#, javascript-format
+msgid "Open with %s"
+msgstr "使用《%s》開啟"
+
+#: js/ui/components/networkAgent.js:91
+msgid ""
+"Alternatively you can connect by pushing the “WPS” button on your router."
+msgstr "您也可以按下路由器的「WPS」按鈕來連接。"
+
+#: js/ui/components/networkAgent.js:103 js/ui/status/network.js:436
+msgid "Connect"
+msgstr "連接"
+
+#: js/ui/components/networkAgent.js:218
+msgid "Key"
+msgstr "金鑰"
+
+#: js/ui/components/networkAgent.js:269 js/ui/components/networkAgent.js:308
+msgid "Private key password"
+msgstr "私密金鑰密碼"
+
+#: js/ui/components/networkAgent.js:302
+msgid "Identity"
+msgstr "身份識別"
+
+#: js/ui/components/networkAgent.js:328
+msgid "Service"
+msgstr "服務"
+
+#: js/ui/components/networkAgent.js:366 js/ui/components/networkAgent.js:402
+#: js/ui/components/networkAgent.js:747 js/ui/components/networkAgent.js:768
+msgid "Authentication required"
+msgstr "需要核對身分"
+
+#: js/ui/components/networkAgent.js:367 js/ui/components/networkAgent.js:748
+#, javascript-format
+msgid ""
+"Passwords or encryption keys are required to access the wireless network "
+"“%s”."
+msgstr "需要密碼或加密金鑰才能存取無線網路「%s」。"
+
+#: js/ui/components/networkAgent.js:371 js/ui/components/networkAgent.js:752
+msgid "Wired 802.1X authentication"
+msgstr "有線 802.1X 驗證"
+
+#: js/ui/components/networkAgent.js:374
+msgid "Network name"
+msgstr "網路名稱"
+
+#: js/ui/components/networkAgent.js:382 js/ui/components/networkAgent.js:756
+msgid "DSL authentication"
+msgstr "DSL 驗證"
+
+#: js/ui/components/networkAgent.js:389 js/ui/components/networkAgent.js:761
+msgid "PIN code required"
+msgstr "需要 PIN 碼"
+
+#: js/ui/components/networkAgent.js:390 js/ui/components/networkAgent.js:762
+msgid "PIN code is needed for the mobile broadband device"
+msgstr "行動寬頻裝置要求輸入 PIN 碼"
+
+#: js/ui/components/networkAgent.js:392
+msgid "PIN"
+msgstr "PIN 碼"
+
+#: js/ui/components/networkAgent.js:403 js/ui/components/networkAgent.js:753
+#: js/ui/components/networkAgent.js:757 js/ui/components/networkAgent.js:769
+#: js/ui/components/networkAgent.js:773
+#, javascript-format
+msgid "A password is required to connect to “%s”."
+msgstr "需要密碼才能連接「%s」。"
+
+#: js/ui/components/networkAgent.js:736
+msgid "Network Manager"
+msgstr "網路管理員"
+
+#: js/ui/components/networkAgent.js:772
+msgid "VPN password"
+msgstr "VPN 密碼"
+
+#: js/ui/components/polkitAgent.js:41
+msgid "Authentication Required"
+msgstr "需要核對身分"
+
+#: js/ui/components/polkitAgent.js:81
+msgid "Administrator"
+msgstr "管理員"
+
+#: js/ui/components/polkitAgent.js:146
+msgid "Authenticate"
+msgstr "驗證"
+
+#. Translators: "that didn't work" refers to the fact that the
+#. * requested authentication was not gained; this can happen
+#. * because of an authentication error (like invalid password),
+#. * for instance.
+#: js/ui/components/polkitAgent.js:260 js/ui/shellMountOperation.js:403
+msgid "Sorry, that didn’t work. Please try again."
+msgstr "抱歉,那沒有作用。請再試一次。"
+
+#: js/ui/components/telepathyClient.js:828
+#, javascript-format
+msgid "%s is now known as %s"
+msgstr "《%s》現為《%s》"
+
+#: js/ui/ctrlAltTab.js:22 js/ui/overviewControls.js:417
+msgid "Windows"
+msgstr "視窗"
+
+#: js/ui/dash.js:205 js/ui/dash.js:251
+msgid "Show Applications"
+msgstr "顯示應用程式"
+
+#. Translators: this is the name of the dock/favorites area on
+#. the left of the overview
+#: js/ui/dash.js:398
+msgid "Dash"
+msgstr "Dash"
+
+#. Translators: This is the date format to use when the calendar popup is
+#. * shown - it is shown just below the time in the top bar (e.g.,
+#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
+#. * "February 17 2015".
+#.
+#: js/ui/dateMenu.js:83
+msgid "%B %-d %Y"
+msgstr "%Y年%-m月%-d日"
+
+#. Translators: This is the accessible name of the date button shown
+#. * below the time in the shell; it should combine the weekday and the
+#. * date, e.g. "Tuesday February 17 2015".
+#.
+#: js/ui/dateMenu.js:90
+msgid "%A %B %e %Y"
+msgstr "%Y年%-m月%-d日 %A"
+
+#. Translators: Shown on calendar heading when selected day occurs on current year
+#: js/ui/dateMenu.js:156
+msgctxt "calendar heading"
+msgid "%B %-d"
+msgstr "%-m月%-d日"
+
+#. Translators: Shown on calendar heading when selected day occurs on different year
+#: js/ui/dateMenu.js:159
+msgctxt "calendar heading"
+msgid "%B %-d %Y"
+msgstr "%Y年%-m月%-d日"
+
+#: js/ui/dateMenu.js:165
+msgid "Today"
+msgstr "今天"
+
+#: js/ui/dateMenu.js:169
+msgid "Tomorrow"
+msgstr "明天"
+
+#. Translators: Shown in calendar event list for all day events
+#. * Keep it short, best if you can use less then 10 characters
+#.
+#: js/ui/dateMenu.js:200
+msgctxt "event list time"
+msgid "All Day"
+msgstr "全天"
+
+#. Translators: Shown in calendar event list as the start/end of events
+#. * that only show day and month
+#.
+#: js/ui/dateMenu.js:222
+msgid "%m/%d"
+msgstr "%m/%d"
+
+#: js/ui/dateMenu.js:273
+msgid "No Events"
+msgstr "沒有行程"
+
+#: js/ui/dateMenu.js:396
+msgid "Add world clocks…"
+msgstr "加入世界時鐘…"
+
+#: js/ui/dateMenu.js:397
+msgid "World Clocks"
+msgstr "世界時鐘"
+
+#: js/ui/dateMenu.js:681
+msgid "Loading…"
+msgstr "正在載入…"
+
+#: js/ui/dateMenu.js:691
+msgid "Go online for weather information"
+msgstr "連接網路以取得天氣資訊"
+
+#: js/ui/dateMenu.js:693
+msgid "Weather information is currently unavailable"
+msgstr "目前無法使用天氣資訊"
+
+#: js/ui/dateMenu.js:703
+msgid "Weather"
+msgstr "天氣"
+
+#: js/ui/dateMenu.js:705
+msgid "Select weather location…"
+msgstr "選擇天氣位置…"
+
+#: js/ui/endSessionDialog.js:41
+#, javascript-format
+msgctxt "title"
+msgid "Log Out %s"
+msgstr "登出「%s」"
+
+#: js/ui/endSessionDialog.js:42
+msgctxt "title"
+msgid "Log Out"
+msgstr "登出"
+
+#: js/ui/endSessionDialog.js:45
+#, javascript-format
+msgid "%s will be logged out automatically in %d second."
+msgid_plural "%s will be logged out automatically in %d seconds."
+msgstr[0] "「%s」會在 %d 秒後自動登出。"
+
+#: js/ui/endSessionDialog.js:51
+#, javascript-format
+msgid "You will be logged out automatically in %d second."
+msgid_plural "You will be logged out automatically in %d seconds."
+msgstr[0] "您會在 %d 秒後自動登出。"
+
+#: js/ui/endSessionDialog.js:58
+msgctxt "button"
+msgid "Log Out"
+msgstr "登出"
+
+#: js/ui/endSessionDialog.js:64 js/ui/status/system.js:167
+msgctxt "title"
+msgid "Power Off"
+msgstr "關閉電源"
+
+#: js/ui/endSessionDialog.js:65
+msgctxt "title"
+msgid "Install Updates & Power Off"
+msgstr "安裝更新並關閉電源"
+
+#: js/ui/endSessionDialog.js:68
+#, javascript-format
+msgid "The system will power off automatically in %d second."
+msgid_plural "The system will power off automatically in %d seconds."
+msgstr[0] "系統會在 %d 秒後關閉電源。"
+
+#: js/ui/endSessionDialog.js:72 js/ui/endSessionDialog.js:91
+msgctxt "checkbox"
+msgid "Install pending software updates"
+msgstr "安裝擱置的軟體更新"
+
+#: js/ui/endSessionDialog.js:76
+msgctxt "button"
+msgid "Power Off"
+msgstr "關閉電源"
+
+#: js/ui/endSessionDialog.js:83
+msgctxt "title"
+msgid "Restart"
+msgstr "重新啟動"
+
+#: js/ui/endSessionDialog.js:84
+msgctxt "title"
+msgid "Install Updates & Restart"
+msgstr "安裝更新並重新啟動"
+
+#: js/ui/endSessionDialog.js:87
+#, javascript-format
+msgid "The system will restart automatically in %d second."
+msgid_plural "The system will restart automatically in %d seconds."
+msgstr[0] "系統會在 %d 秒後重新啟動。"
+
+#: js/ui/endSessionDialog.js:95
+msgctxt "button"
+msgid "Restart"
+msgstr "重新啟動"
+
+#: js/ui/endSessionDialog.js:103
+msgctxt "title"
+msgid "Restart & Install Updates"
+msgstr "重新啟動並安裝更新"
+
+#: js/ui/endSessionDialog.js:106
+#, javascript-format
+msgid "The system will automatically restart and install updates in %d second."
+msgid_plural ""
+"The system will automatically restart and install updates in %d seconds."
+msgstr[0] "系統會在 %d 秒後重新啟動並安裝更新。"
+
+#: js/ui/endSessionDialog.js:113 js/ui/endSessionDialog.js:134
+msgctxt "button"
+msgid "Restart &amp; Install"
+msgstr "重新啟動並安裝"
+
+#: js/ui/endSessionDialog.js:115
+msgctxt "button"
+msgid "Install &amp; Power Off"
+msgstr "安裝並關機"
+
+#: js/ui/endSessionDialog.js:116
+msgctxt "checkbox"
+msgid "Power off after updates are installed"
+msgstr "安裝完更新後關機"
+
+#: js/ui/endSessionDialog.js:123
+msgctxt "title"
+msgid "Restart & Install Upgrade"
+msgstr "重新啟動並升級系統"
+
+#. Translators: This is the text displayed for system upgrades in the
+#. shut down dialog. First %s gets replaced with the distro name and
+#. second %s with the distro version to upgrade to
+#: js/ui/endSessionDialog.js:128
+#, javascript-format
+msgid ""
+"%s %s will be installed after restart. Upgrade installation can take a long "
+"time: ensure that you have backed up and that the computer is plugged in."
+msgstr ""
+"重新啟動後便會開始升級 %s %s。升級過程會花上一段時間:請確保已將電腦完成備份"
+"並接上交流電源。"
+
+#: js/ui/endSessionDialog.js:285
+msgid "Low battery power: please plug in before installing updates."
+msgstr "低電量:請於安裝更新前接上交流電源。"
+
+#: js/ui/endSessionDialog.js:294
+msgid "Some applications are busy or have unsaved work"
+msgstr "仍有應用程式處於忙碌狀態或有工作尚未儲存"
+
+#: js/ui/endSessionDialog.js:299
+msgid "Other users are logged in"
+msgstr "已有其他登入的使用者"
+
+#: js/ui/endSessionDialog.js:470
+msgctxt "button"
+msgid "Boot Options"
+msgstr "開機選項"
+
+#. Translators: Remote here refers to a remote session, like a ssh login
+#: js/ui/endSessionDialog.js:675
+#, javascript-format
+msgid "%s (remote)"
+msgstr "%s(遠端)"
+
+#. Translators: Console here refers to a tty like a VT console
+#: js/ui/endSessionDialog.js:678
+#, javascript-format
+msgid "%s (console)"
+msgstr "%s(主控臺)"
+
+#: js/ui/extensionDownloader.js:227
+msgid "Install"
+msgstr "安裝"
+
+#: js/ui/extensionDownloader.js:233
+msgid "Install Extension"
+msgstr "安裝擴充套件"
+
+#: js/ui/extensionDownloader.js:234
+#, javascript-format
+msgid "Download and install “%s” from extensions.gnome.org?"
+msgstr "是否自 extensions.gnome.org 下載並安裝「%s」?"
+
+#: js/ui/extensionSystem.js:270
+msgid "Extension Updates Available"
+msgstr "擴充套件已有更新"
+
+#: js/ui/extensionSystem.js:271
+msgid "Extension updates are ready to be installed."
+msgstr "擴充套件更新已準備好安裝。"
+
+#: js/ui/inhibitShortcutsDialog.js:79
+msgid "Allow inhibiting shortcuts"
+msgstr "暫時停用快捷鍵"
+
+#. Translators: %s is an application name like "Settings"
+#: js/ui/inhibitShortcutsDialog.js:82
+#, javascript-format
+msgid "The application %s wants to inhibit shortcuts"
+msgstr "應用程式《%s》請求暫時停用快捷鍵"
+
+#: js/ui/inhibitShortcutsDialog.js:83
+msgid "An application wants to inhibit shortcuts"
+msgstr "應用程式請求暫時停用快捷鍵"
+
+#. Translators: %s is a keyboard shortcut like "Super+x"
+#: js/ui/inhibitShortcutsDialog.js:90
+#, javascript-format
+msgid "You can restore shortcuts by pressing %s."
+msgstr "您可以按下 %s 鍵來還原快捷鍵。"
+
+#: js/ui/inhibitShortcutsDialog.js:101
+msgid "Deny"
+msgstr "禁止"
+
+#: js/ui/inhibitShortcutsDialog.js:110
+msgid "Allow"
+msgstr "允許"
+
+#: js/ui/kbdA11yDialog.js:32
+msgid "Slow Keys Turned On"
+msgstr "遲緩按鍵已開啟"
+
+#: js/ui/kbdA11yDialog.js:33
+msgid "Slow Keys Turned Off"
+msgstr "遲緩按鍵已關閉"
+
+#: js/ui/kbdA11yDialog.js:34
+msgid ""
+"You just held down the Shift key for 8 seconds. This is the shortcut for the "
+"Slow Keys feature, which affects the way your keyboard works."
+msgstr ""
+"您已按住 Shift 鍵長達 8 秒。這是「遲緩按鍵」功能的快捷鍵,會影響您的鍵盤操"
+"作。"
+
+#: js/ui/kbdA11yDialog.js:40
+msgid "Sticky Keys Turned On"
+msgstr "相黏按鍵已開啟"
+
+#: js/ui/kbdA11yDialog.js:41
+msgid "Sticky Keys Turned Off"
+msgstr "相黏按鍵已關閉"
+
+#: js/ui/kbdA11yDialog.js:43
+msgid ""
+"You just pressed the Shift key 5 times in a row. This is the shortcut for "
+"the Sticky Keys feature, which affects the way your keyboard works."
+msgstr ""
+"您已連按 Shift 鍵多達 5 次。這是「相黏按鍵」功能的快捷鍵,會影響您的鍵盤操"
+"作。"
+
+#: js/ui/kbdA11yDialog.js:45
+msgid ""
+"You just pressed two keys at once, or pressed the Shift key 5 times in a "
+"row. This turns off the Sticky Keys feature, which affects the way your "
+"keyboard works."
+msgstr ""
+"您已同時按下 2 個按鍵,或是連按 Shift 鍵多達 5 次。這麽做會關閉「相黏按鍵」功"
+"能,並會影響您的鍵盤操作。"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Leave On"
+msgstr "保持開啟"
+
+#: js/ui/kbdA11yDialog.js:55
+msgid "Turn On"
+msgstr "開啟"
+
+#: js/ui/kbdA11yDialog.js:65 js/ui/status/network.js:447
+msgid "Turn Off"
+msgstr "關閉"
+
+#: js/ui/kbdA11yDialog.js:65
+msgid "Leave Off"
+msgstr "保持關閉"
+
+#: js/ui/keyboard.js:219
+msgid "Region & Language Settings"
+msgstr "地區和語言設定"
+
+#: js/ui/lookingGlass.js:713
+msgid "No extensions installed"
+msgstr "沒有已安裝的擴充套件"
+
+#. Translators: argument is an extension UUID.
+#: js/ui/lookingGlass.js:774
+#, javascript-format
+msgid "%s has not emitted any errors."
+msgstr "%s 沒有發出任何錯誤。"
+
+#: js/ui/lookingGlass.js:780
+msgid "Hide Errors"
+msgstr "隱藏錯誤"
+
+#: js/ui/lookingGlass.js:784 js/ui/lookingGlass.js:857
+msgid "Show Errors"
+msgstr "顯示錯誤"
+
+#: js/ui/lookingGlass.js:793
+msgid "Enabled"
+msgstr "已啟用"
+
+#. translators:
+#. * The device has been disabled
+#: js/ui/lookingGlass.js:796 subprojects/gvc/gvc-mixer-control.c:1900
+msgid "Disabled"
+msgstr "已停用"
+
+#: js/ui/lookingGlass.js:798
+msgid "Error"
+msgstr "錯誤"
+
+#: js/ui/lookingGlass.js:800
+msgid "Out of date"
+msgstr "過時的"
+
+#: js/ui/lookingGlass.js:802
+msgid "Downloading"
+msgstr "正在下載"
+
+#: js/ui/lookingGlass.js:835
+msgid "View Source"
+msgstr "檢示來源"
+
+#: js/ui/lookingGlass.js:846
+msgid "Web Page"
+msgstr "網頁"
+
+#: js/ui/main.js:286
+msgid "System was put in unsafe mode"
+msgstr "系統曾處於不安全模式"
+
+#: js/ui/main.js:287
+msgid "Applications now have unrestricted access"
+msgstr "應用程式可以不受限制的存取"
+
+#: js/ui/main.js:288 js/ui/overview.js:58
+msgid "Undo"
+msgstr "還原"
+
+#: js/ui/main.js:334
+msgid "Logged in as a privileged user"
+msgstr "以超級使用者身份登入"
+
+#: js/ui/main.js:335
+msgid ""
+"Running a session as a privileged user should be avoided for security "
+"reasons. If possible, you should log in as a normal user."
+msgstr ""
+"基於安全考量,應避免以超級使用者身份執行作業階段;允許的情況下請以一般使用者"
+"身份登入。"
+
+#: js/ui/main.js:384
+msgid "Screen Lock disabled"
+msgstr "已停用鎖定螢幕"
+
+#: js/ui/main.js:385
+msgid "Screen Locking requires the GNOME display manager."
+msgstr "鎖定螢幕需使用《GNOME 顯示管理器》。"
+
+#: js/ui/messageTray.js:1418
+msgid "System Information"
+msgstr "系統資訊"
+
+#: js/ui/mpris.js:202
+msgid "Unknown artist"
+msgstr "不明演出者"
+
+#: js/ui/mpris.js:212
+msgid "Unknown title"
+msgstr "不明歌曲"
+
+#. Translators: this is the text displayed
+#. in the search entry when no search is
+#. active; it should not exceed ~30
+#. characters.
+#: js/ui/overviewControls.js:327
+msgid "Type to search"
+msgstr "輸入以搜尋"
+
+#: js/ui/overviewControls.js:405
+msgid "Applications"
+msgstr "應用程式"
+
+#. Translators: This is the main view to select
+#. activities. See also note for "Activities" string.
+#: js/ui/overview.js:71
+msgid "Overview"
+msgstr "概覽"
+
+#: js/ui/padOsd.js:100
+msgid "New shortcut…"
+msgstr "輸入新快捷鍵…"
+
+#: js/ui/padOsd.js:154
+msgid "Application defined"
+msgstr "指定的應用程式"
+
+#: js/ui/padOsd.js:155
+msgid "Show on-screen help"
+msgstr "在螢幕上顯示按鍵功能"
+
+#: js/ui/padOsd.js:156
+msgid "Switch monitor"
+msgstr "切換顯示器"
+
+#: js/ui/padOsd.js:157
+msgid "Assign keystroke"
+msgstr "指定的鍵盤按鍵"
+
+#: js/ui/padOsd.js:226
+msgid "Done"
+msgstr "完成"
+
+#: js/ui/padOsd.js:743
+msgid "Edit…"
+msgstr "編輯…"
+
+#: js/ui/padOsd.js:785 js/ui/padOsd.js:902
+msgid "None"
+msgstr "無"
+
+#: js/ui/padOsd.js:856
+msgid "Press a button to configure"
+msgstr "按下個別按鍵進行設定"
+
+#: js/ui/padOsd.js:857
+msgid "Press Esc to exit"
+msgstr "按下 Esc 鍵離開"
+
+#: js/ui/padOsd.js:860
+msgid "Press any key to exit"
+msgstr "按下任意鍵離開"
+
+#: js/ui/panel.js:244
+msgid "Activities"
+msgstr "概覽"
+
+#: js/ui/panel.js:338
+msgctxt "System menu in the top bar"
+msgid "System"
+msgstr "系統"
+
+#: js/ui/panel.js:457
+msgid "Top Bar"
+msgstr "頂端列"
+
+#: js/ui/runDialog.js:58
+msgid "Run a Command"
+msgstr "執行命令"
+
+#: js/ui/runDialog.js:73
+msgid "Press ESC to close"
+msgstr "按下 ESC 鍵關閉"
+
+#: js/ui/runDialog.js:238
+msgid "Restart is not available on Wayland"
+msgstr "使用《Wayland》時無法重新啟動"
+
+#: js/ui/runDialog.js:243
+msgid "Restarting…"
+msgstr "重新啟動…"
+
+#: js/ui/screenShield.js:235
+msgid "GNOME needs to lock the screen"
+msgstr "《GNOME》需將螢幕鎖定"
+
+#. We could not become modal, so we can't activate the
+#. screenshield. The user is probably very upset at this
+#. point, but any application using global grabs is broken
+#. Just tell them to stop using this app
+#.
+#. XXX: another option is to kick the user into the gdm login
+#. screen, where we're not affected by grabs
+#: js/ui/screenShield.js:275 js/ui/screenShield.js:652
+msgid "Unable to lock"
+msgstr "無法鎖定"
+
+#: js/ui/screenShield.js:276 js/ui/screenShield.js:653
+msgid "Lock was blocked by an application"
+msgstr "鎖定的動作遭應用程式阻擋"
+
+#: js/ui/screenshot.js:1147
+msgid "Selection"
+msgstr "選取"
+
+#: js/ui/screenshot.js:1157
+msgid "Area Selection"
+msgstr "擷取所選的範圍"
+
+#: js/ui/screenshot.js:1162
+msgid "Screen"
+msgstr "螢幕"
+
+#: js/ui/screenshot.js:1172
+msgid "Screen Selection"
+msgstr "擷取整個螢幕"
+
+#: js/ui/screenshot.js:1177
+msgid "Window"
+msgstr "視窗"
+
+#: js/ui/screenshot.js:1187
+msgid "Window Selection"
+msgstr "擷取視窗"
+
+#: js/ui/screenshot.js:1225
+msgid "Screenshot / Screencast"
+msgstr "螢幕快照/螢幕錄影"
+
+#: js/ui/screenshot.js:1261
+msgid "Show Pointer"
+msgstr "顯示滑鼠指標"
+
+#. Translators: this is the folder where recorded
+#. screencasts are stored.
+#: js/ui/screenshot.js:1849
+msgid "Screencasts"
+msgstr "螢幕錄影"
+
+#. Translators: this is a filename used for screencast
+#. * recording, where "%d" and "%t" date and time, e.g.
+#. * "Screencast from 07-17-2013 10:00:46 PM.webm"
+#: js/ui/screenshot.js:1854
+#, no-c-format
+msgid "Screencast from %d %t.webm"
+msgstr "螢幕錄影 %d %t.webm"
+
+#. Translators: notification source name.
+#: js/ui/screenshot.js:1920 js/ui/screenshot.js:2132
+msgid "Screenshot"
+msgstr "螢幕快照"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:1926
+msgid "Screencast recorded"
+msgstr "已錄製螢幕錄影"
+
+#. Translators: notification body when a screencast was recorded.
+#: js/ui/screenshot.js:1928
+msgid "Click here to view the video."
+msgstr "點擊此處檢視影片。"
+
+#. Translators: button on the screencast notification.
+#. Translators: button on the screenshot notification.
+#: js/ui/screenshot.js:1931 js/ui/screenshot.js:2146
+msgid "Show in Files"
+msgstr "使用《檔案》開啟"
+
+#. Translators: this is the name of the file that the screenshot is
+#. saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+#: js/ui/screenshot.js:2092
+#, javascript-format
+msgid "Screenshot from %s"
+msgstr "螢幕快照 %s"
+
+#. Translators: notification title.
+#: js/ui/screenshot.js:2138
+msgid "Screenshot captured"
+msgstr "已擷取螢幕快照"
+
+#. Translators: notification body when a screenshot was captured.
+#: js/ui/screenshot.js:2140
+msgid "You can paste the image from the clipboard."
+msgstr "您可以自剪貼簿中貼上螢幕快照。"
+
+#: js/ui/screenshot.js:2193 js/ui/screenshot.js:2358
+msgid "Screenshot taken"
+msgstr "已擷取螢幕快照"
+
+#: js/ui/search.js:804
+msgid "Searching…"
+msgstr "正在搜尋…"
+
+#: js/ui/search.js:806
+msgid "No results."
+msgstr "沒有結果。"
+
+#: js/ui/search.js:937
+#, javascript-format
+msgid "%d more"
+msgid_plural "%d more"
+msgstr[0] "其他 %d 項"
+
+#: js/ui/searchController.js:87
+msgid "Search"
+msgstr "搜尋"
+
+#: js/ui/shellEntry.js:20
+msgid "Copy"
+msgstr "複製"
+
+#: js/ui/shellEntry.js:25
+msgid "Paste"
+msgstr "貼上"
+
+#: js/ui/shellEntry.js:77
+msgid "Show Text"
+msgstr "顯示文字"
+
+#: js/ui/shellEntry.js:79
+msgid "Hide Text"
+msgstr "隱藏文字"
+
+#: js/ui/shellEntry.js:166
+msgid "Caps lock is on."
+msgstr "Caps Lock 已開啟。"
+
+#: js/ui/shellMountOperation.js:286
+msgid "Hidden Volume"
+msgstr "隱藏的儲存區"
+
+#: js/ui/shellMountOperation.js:289
+msgid "Windows System Volume"
+msgstr "Windows 系統儲存區"
+
+#: js/ui/shellMountOperation.js:292
+msgid "Uses Keyfiles"
+msgstr "使用密鑰文件"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:299
+#, javascript-format
+msgid ""
+"To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead."
+msgstr "要解鎖使用密鑰文件的儲存區,請改用《%s》工具。"
+
+#: js/ui/shellMountOperation.js:307
+msgid "PIM Number"
+msgstr "PIM 碼"
+
+#: js/ui/shellMountOperation.js:366
+msgid "Remember Password"
+msgstr "記住密碼"
+
+#: js/ui/shellMountOperation.js:381
+msgid "Unlock"
+msgstr "解鎖"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:392
+#, javascript-format
+msgid "Open %s"
+msgstr "開啟《%s》"
+
+#: js/ui/shellMountOperation.js:424
+msgid "The PIM must be a number or empty."
+msgstr "PIM 必須是數字或為空白。"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:466
+#, javascript-format
+msgid "Unable to start %s"
+msgstr "無法啟動《%s》"
+
+#. Translators: %s is the Disks application
+#: js/ui/shellMountOperation.js:468
+#, javascript-format
+msgid "Couldn’t find the %s application"
+msgstr "找不到應用程式《%s》"
+
+#: js/ui/status/accessibility.js:34
+msgid "Accessibility"
+msgstr "無障礙功能"
+
+#: js/ui/status/accessibility.js:44
+msgid "High Contrast"
+msgstr "高反差"
+
+#: js/ui/status/accessibility.js:47
+msgid "Zoom"
+msgstr "縮放"
+
+#: js/ui/status/accessibility.js:54
+msgid "Screen Reader"
+msgstr "螢幕閱讀器"
+
+#: js/ui/status/accessibility.js:58
+msgid "Screen Keyboard"
+msgstr "螢幕鍵盤"
+
+#: js/ui/status/accessibility.js:62
+msgid "Visual Alerts"
+msgstr "鈴響視覺化"
+
+#: js/ui/status/accessibility.js:65
+msgid "Sticky Keys"
+msgstr "相黏按鍵"
+
+#: js/ui/status/accessibility.js:68
+msgid "Slow Keys"
+msgstr "遲緩按鍵"
+
+#: js/ui/status/accessibility.js:71
+msgid "Bounce Keys"
+msgstr "反彈按鍵"
+
+#: js/ui/status/accessibility.js:74
+msgid "Mouse Keys"
+msgstr "滑鼠鍵"
+
+#: js/ui/status/accessibility.js:131
+msgid "Large Text"
+msgstr "大型文字"
+
+#: js/ui/status/autoRotate.js:14
+msgid "Auto Rotate"
+msgstr "自動旋轉"
+
+#: js/ui/status/bluetooth.js:151
+msgid "Bluetooth"
+msgstr "藍牙"
+
+#: js/ui/status/brightness.js:34
+msgid "Brightness"
+msgstr "亮度"
+
+#: js/ui/status/darkMode.js:11
+msgid "Dark Mode"
+msgstr "深色模式"
+
+#: js/ui/status/dwellClick.js:12
+msgid "Single Click"
+msgstr "點一下"
+
+#: js/ui/status/dwellClick.js:17
+msgid "Double Click"
+msgstr "點兩下"
+
+#: js/ui/status/dwellClick.js:22
+msgid "Drag"
+msgstr "拖動"
+
+#: js/ui/status/dwellClick.js:27
+msgid "Secondary Click"
+msgstr "次要按鍵"
+
+#: js/ui/status/dwellClick.js:36
+msgid "Dwell Click"
+msgstr "替代點擊"
+
+#: js/ui/status/keyboard.js:830
+msgid "Keyboard"
+msgstr "鍵盤"
+
+#: js/ui/status/keyboard.js:847
+msgid "Show Keyboard Layout"
+msgstr "顯示鍵盤配置"
+
+#: js/ui/status/location.js:330
+msgid "Allow location access"
+msgstr "允許取用位置資訊"
+
+#. Translators: %s is an application name
+#: js/ui/status/location.js:332
+#, javascript-format
+msgid "The app %s wants to access your location"
+msgstr "《%s》欲取用您的位置資訊"
+
+#: js/ui/status/location.js:342
+msgid "Location access can be changed at any time from the privacy settings."
+msgstr "位置資訊取用權可隨時從隱私設定中更改。"
+
+#: js/ui/status/network.js:53
+msgid "<unknown>"
+msgstr "<不明>"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:356
+#, javascript-format
+msgid "Disconnect %s"
+msgstr "中斷連接%s"
+
+#. Translators: %s is a device name like "MyPhone"
+#: js/ui/status/network.js:358
+#, javascript-format
+msgid "Connect to %s"
+msgstr "連接%s"
+
+#. Translators: %s is a network identifier
+#: js/ui/status/network.js:1107
+#, javascript-format
+msgid "%s Hotspot"
+msgstr "%s熱點"
+
+#: js/ui/status/nightLight.js:20
+msgid "Night Light"
+msgstr "夜光模式"
+
+#: js/ui/status/powerProfiles.js:20
+msgctxt "Power profile"
+msgid "Performance"
+msgstr "效能模式"
+
+#: js/ui/status/powerProfiles.js:25
+msgctxt "Power profile"
+msgid "Balanced"
+msgstr "平衡模式"
+
+#: js/ui/status/powerProfiles.js:30
+msgctxt "Power profile"
+msgid "Power Saver"
+msgstr "省電模式"
+
+#: js/ui/status/powerProfiles.js:68
+msgid "Power Profiles"
+msgstr "電源設定檔"
+
+#: js/ui/status/remoteAccess.js:74
+msgid "Stop Screencast"
+msgstr "停止螢幕錄影"
+
+#: js/ui/status/remoteAccess.js:144
+msgid "Stop Screen Sharing"
+msgstr "停止螢幕分享"
+
+#: js/ui/status/rfkill.js:96
+msgid "Airplane Mode"
+msgstr "飛安模式"
+
+#: js/ui/status/system.js:90
+#, javascript-format
+msgid "%d %%"
+msgstr "%d %%"
+
+#. Translators: entry in the window right click menu.
+#: js/ui/status/system.js:105 js/ui/windowMenu.js:29
+msgid "Take Screenshot"
+msgstr "擷取視窗快照"
+
+#: js/ui/status/system.js:161
+msgid "Power Off Menu"
+msgstr "關閉電源選單"
+
+#: js/ui/status/system.js:169
+msgid "Suspend"
+msgstr "暫停"
+
+#: js/ui/status/system.js:174
+msgid "Restart…"
+msgstr "重新啟動…"
+
+#: js/ui/status/system.js:179
+msgid "Power Off…"
+msgstr "關閉電源…"
+
+#: js/ui/status/system.js:186
+#: subprojects/extensions-app/data/ui/extensions-window.ui:190
+msgid "Log Out…"
+msgstr "登出…"
+
+#: js/ui/status/system.js:191
+msgid "Switch User…"
+msgstr "切換使用者…"
+
+#: js/ui/status/system.js:235
+msgctxt "action"
+msgid "Lock Screen"
+msgstr "鎖定螢幕"
+
+#: js/ui/status/thunderbolt.js:256
+msgid "Thunderbolt"
+msgstr "Thunderbolt"
+
+#: js/ui/status/thunderbolt.js:317
+msgid "Unknown Thunderbolt device"
+msgstr "不明的 Thunderbolt 裝置"
+
+#: js/ui/status/thunderbolt.js:318
+msgid ""
+"New device has been detected while you were away. Please disconnect and "
+"reconnect the device to start using it."
+msgstr "您離開後有偵測到新裝置。請中斷並重新連接該裝置便能開始使用。"
+
+#: js/ui/status/thunderbolt.js:321
+msgid "Unauthorized Thunderbolt device"
+msgstr "未經授權的 Thunderbolt 裝置"
+
+#: js/ui/status/thunderbolt.js:322
+msgid ""
+"New device has been detected and needs to be authorized by an administrator."
+msgstr "偵測到新裝置,需由系統管理員授權。"
+
+#: js/ui/status/thunderbolt.js:328
+msgid "Thunderbolt authorization error"
+msgstr "Thunderbolt 授權錯誤"
+
+#: js/ui/status/thunderbolt.js:329
+#, javascript-format
+msgid "Could not authorize the Thunderbolt device: %s"
+msgstr "無法授權該 Thunderbolt 裝置:%s"
+
+#: js/ui/status/volume.js:191
+msgid "Volume changed"
+msgstr "音量已更改"
+
+#: js/ui/status/volume.js:253
+msgid "Volume"
+msgstr "音量"
+
+#: js/ui/status/volume.js:269
+msgid "Sound Output"
+msgstr "音訊輸出"
+
+#: js/ui/status/volume.js:337
+msgid "Sound Input"
+msgstr "音訊輸入"
+
+#. Translators: this is for display mirroring i.e. cloning.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:20
+msgid "Mirror"
+msgstr "鏡像顯示"
+
+#. Translators: this is for the desktop spanning displays.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:29
+msgid "Join Displays"
+msgstr "銜接顯示"
+
+#. Translators: this is for using only an external display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:39
+msgid "External Only"
+msgstr "僅外接"
+
+#. Translators: this is for using only the laptop display.
+#. * Try to keep it under around 15 characters.
+#.
+#: js/ui/switchMonitor.js:47
+msgid "Built-in Only"
+msgstr "僅內建"
+
+#. Translators: This is a time format for a date in
+#. long format
+#: js/ui/unlockDialog.js:364
+msgid "%A %B %-d"
+msgstr "%-m月%-d日 %A"
+
+#: js/ui/unlockDialog.js:370
+msgid "Swipe up to unlock"
+msgstr "向上滑動解鎖"
+
+#: js/ui/unlockDialog.js:371
+msgid "Click or press a key to unlock"
+msgstr "按下滑鼠按鍵或是鍵盤任意鍵解鎖"
+
+#: js/ui/unlockDialog.js:554
+msgid "Unlock Window"
+msgstr "解鎖視窗"
+
+#: js/ui/unlockDialog.js:563
+msgid "Log in as another user"
+msgstr "以其他使用者身份登入"
+
+#: js/ui/welcomeDialog.js:36
+#, javascript-format
+msgid "Welcome to GNOME %s"
+msgstr "歡迎使用 GNOME %s"
+
+#: js/ui/welcomeDialog.js:37
+msgid "If you want to learn your way around, check out the tour."
+msgstr "您要不要四處看一下?讓我們帶領你走一趟。"
+
+#: js/ui/welcomeDialog.js:46
+msgid "No Thanks"
+msgstr "謝謝不用"
+
+#: js/ui/welcomeDialog.js:51
+msgid "Take Tour"
+msgstr "踏上旅程"
+
+#: js/ui/windowAttentionHandler.js:20
+#, javascript-format
+msgid "“%s” is ready"
+msgstr "「%s」視窗已就緒"
+
+#. Translators: This string should be shorter than 30 characters
+#: js/ui/windowManager.js:62
+msgid "Keep these display settings?"
+msgstr "要保留顯示器設定值嗎?"
+
+#: js/ui/windowManager.js:72
+msgid "Revert Settings"
+msgstr "還原"
+
+#: js/ui/windowManager.js:77
+msgid "Keep Changes"
+msgstr "保留"
+
+#: js/ui/windowManager.js:97
+#, javascript-format
+msgid "Settings changes will revert in %d second"
+msgid_plural "Settings changes will revert in %d seconds"
+msgstr[0] "更改的設定值會在 %d 秒內還原"
+
+#. Translators: This represents the size of a window. The first number is
+#. * the width of the window and the second is the height.
+#: js/ui/windowManager.js:544
+#, javascript-format
+msgid "%d × %d"
+msgstr "%d × %d"
+
+#: js/ui/windowMenu.js:41
+msgid "Hide"
+msgstr "隱藏"
+
+#: js/ui/windowMenu.js:48
+msgid "Restore"
+msgstr "還原"
+
+#: js/ui/windowMenu.js:52
+msgid "Maximize"
+msgstr "最大化"
+
+#: js/ui/windowMenu.js:59
+msgid "Move"
+msgstr "移動"
+
+#: js/ui/windowMenu.js:65
+msgid "Resize"
+msgstr "重設大小"
+
+#: js/ui/windowMenu.js:72
+msgid "Move Titlebar Onscreen"
+msgstr "移動螢幕標題列"
+
+#: js/ui/windowMenu.js:77
+msgid "Always on Top"
+msgstr "永遠置頂顯示"
+
+#: js/ui/windowMenu.js:96
+msgid "Always on Visible Workspace"
+msgstr "永遠顯示在目前的工作區"
+
+#: js/ui/windowMenu.js:110
+msgid "Move to Workspace Left"
+msgstr "移至左方工作區"
+
+#: js/ui/windowMenu.js:116
+msgid "Move to Workspace Right"
+msgstr "移至右方工作區"
+
+#: js/ui/windowMenu.js:122
+msgid "Move to Workspace Up"
+msgstr "移至上方工作區"
+
+#: js/ui/windowMenu.js:128
+msgid "Move to Workspace Down"
+msgstr "移至下方工作區"
+
+#: js/ui/windowMenu.js:146
+msgid "Move to Monitor Up"
+msgstr "移至上方顯示器"
+
+#: js/ui/windowMenu.js:155
+msgid "Move to Monitor Down"
+msgstr "移至下方顯示器"
+
+#: js/ui/windowMenu.js:164
+msgid "Move to Monitor Left"
+msgstr "移至左方顯示器"
+
+#: js/ui/windowMenu.js:173
+msgid "Move to Monitor Right"
+msgstr "移至右方顯示器"
+
+#: js/ui/windowMenu.js:181
+msgid "Close"
+msgstr "關閉視窗"
+
+#: src/calendar-server/evolution-calendar.desktop.in:3
+msgid "Evolution Calendar"
+msgstr "《Evolution》行事曆"
+
+#: src/main.c:435 subprojects/extensions-tool/src/main.c:321
+msgid "Print version"
+msgstr "輸出版本資訊"
+
+#: src/main.c:441
+msgid "Mode used by GDM for login screen"
+msgstr "《GDM》於登入畫面中所使用的模式"
+
+#: src/main.c:447
+msgid "Use a specific mode, e.g. “gdm” for login screen"
+msgstr "使用指定的模式,例如「gdm」為登入畫面"
+
+#: src/main.c:453
+msgid "List possible modes"
+msgstr "列出可能的模式"
+
+#: src/shell-app.c:305
+msgctxt "program"
+msgid "Unknown"
+msgstr "不明"
+
+#: src/shell-app.c:556
+#, c-format
+msgid "Failed to launch “%s”"
+msgstr "無法啟動《%s》"
+
+#: src/shell-keyring-prompt.c:764
+msgid "Passwords do not match."
+msgstr "密碼不相符。"
+
+#: src/shell-keyring-prompt.c:772
+msgid "Password cannot be blank"
+msgstr "密碼不能為空白"
+
+#: src/shell-polkit-authentication-agent.c:344
+msgid "Authentication dialog was dismissed by the user"
+msgstr "身分核對對話框遭使用者關閉"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4
+#: subprojects/extensions-app/js/main.js:212
+#: subprojects/extensions-app/data/ui/extensions-window.ui:18
+#: subprojects/extensions-app/data/ui/extensions-window.ui:83
+msgid "Extensions"
+msgstr "擴充套件"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6
+msgid "Manage your GNOME Extensions"
+msgstr "管理《GNOME Shell》的擴充套件"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:17
+#: subprojects/extensions-app/js/main.js:216
+msgid "The GNOME Project"
+msgstr "GNOME 專案"
+
+#: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:36
+msgid ""
+"GNOME Extensions handles updating extensions, configuring extension "
+"preferences and removing or disabling unwanted extensions."
+msgstr ""
+"《GNOME 擴充套件》能管理擴充套件更新、調整擴充套件偏好設定、移除或停用不想用"
+"的擴充套件等。"
+
+#: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7
+msgid "Configure GNOME Shell Extensions"
+msgstr "設定《GNOME Shell》的擴充套件"
+
+#: subprojects/extensions-app/js/main.js:130
+#: subprojects/extensions-app/js/main.js:141
+msgid "No Matches"
+msgstr "沒有相符的項目"
+
+#: subprojects/extensions-app/js/main.js:169
+#, javascript-format
+msgid "Remove “%s”?"
+msgstr "移除「%s」?"
+
+#: subprojects/extensions-app/js/main.js:170
+msgid ""
+"If you remove the extension, you need to return to download it if you want "
+"to enable it again"
+msgstr "若將該擴充套件移除,欲再次啟用必需重新下載"
+
+#: subprojects/extensions-app/js/main.js:174
+msgid "Remove"
+msgstr "移除"
+
+#: subprojects/extensions-app/js/main.js:211
+msgid "translator-credits"
+msgstr ""
+"Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>, 2010.\n"
+"Wei-Lun Chao <chaoweilun@gmail.com>, 2010.\n"
+"Cheng-Chia Tseng <pswo10680@gmail.com>, 2020-2022.\n"
+"Freddy Cheng <freddy4212@gmail.com>, 2022."
+
+#: subprojects/extensions-app/js/main.js:339
+#, javascript-format
+msgid "%d extension will be updated on next login."
+msgid_plural "%d extensions will be updated on next login."
+msgstr[0] "%d 個擴充套件會在下次登入時更新。"
+
+#: subprojects/extensions-app/js/main.js:481
+msgid "The extension is incompatible with the current GNOME version"
+msgstr "該擴充套件不相容於目前的 GNOME 版本"
+
+#: subprojects/extensions-app/js/main.js:484
+#: subprojects/extensions-app/data/ui/extension-row.ui:52
+msgid "The extension had an error"
+msgstr "該擴充套件存在錯誤"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:62
+msgid "The extension can be updated"
+msgstr "該擴充套件已可更新"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:112
+msgid "Website"
+msgstr "網站"
+
+#: subprojects/extensions-app/data/ui/extension-row.ui:134
+msgid "Remove…"
+msgstr "移除…"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:6
+msgid "Help"
+msgstr "求助"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:10
+msgid "About Extensions"
+msgstr "關於擴充套件"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:84
+msgid ""
+"Extensions can cause performance and stability issues. Disable extensions if "
+"you encounter problems with your system."
+msgstr ""
+"擴充套件可能導致效能與穩定性問題。若您的系統遭遇問題,則請停用擴充套件。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:98
+msgid "Manually Installed"
+msgstr "手動安裝的擴充套件"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:99
+#: subprojects/extensions-app/data/ui/extensions-window.ui:134
+msgid ""
+"To find and add extensions, visit <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>."
+msgstr ""
+"要尋找並加入擴充套件,可以造訪 <a href=\"https://extensions.gnome."
+"org\">extensions.gnome.org</a>。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:112
+msgid "Built-In"
+msgstr "內建的擴充套件"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:133
+msgid "No Installed Extensions"
+msgstr "沒有已安裝的擴充套件"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:145
+msgid ""
+"We’re very sorry, but it was not possible to get the list of installed "
+"extensions. Make sure you are logged into GNOME and try again."
+msgstr ""
+"非常抱歉,我們無法取得已安裝的擴充套件列表。請確認您已登入 GNOME 之後並重試看"
+"看。"
+
+#: subprojects/extensions-app/data/ui/extensions-window.ui:175
+msgid "Extension Updates Ready"
+msgstr "擴充套件更新已準備就緒"
+
+#. Translators: a file path to an extension directory
+#: subprojects/extensions-tool/src/command-create.c:222
+#, c-format
+msgid "The new extension was successfully created in %s.\n"
+msgstr "成功在「%s」建立新擴充套件。\n"
+
+#: subprojects/extensions-tool/src/command-create.c:296
+#, c-format
+msgid ""
+"Name should be a very short (ideally descriptive) string.\n"
+"Examples are: %s"
+msgstr ""
+"名稱應該要是非常短(理想上是描述性的)的字串。\n"
+"範例:%s"
+
+#: subprojects/extensions-tool/src/command-create.c:302
+#: subprojects/extensions-tool/src/main.c:238
+msgid "Name"
+msgstr "名稱"
+
+#: subprojects/extensions-tool/src/command-create.c:316
+#, c-format
+msgid ""
+"Description is a single-sentence explanation of what your extension does.\n"
+"Examples are: %s"
+msgstr ""
+"描述是用於解釋您擴充套件作用的一句話。\n"
+"範例:%s"
+
+#: subprojects/extensions-tool/src/command-create.c:322
+#: subprojects/extensions-tool/src/main.c:241
+msgid "Description"
+msgstr "描述"
+
+#: subprojects/extensions-tool/src/command-create.c:336
+msgid ""
+"UUID is a globally-unique identifier for your extension.\n"
+"This should be in the format of an email address (clicktofocus@janedoe."
+"example.com)\n"
+msgstr ""
+"UUID 是您擴充套件的全域唯一識別碼。\n"
+"格式應該要像電子信箱位址 (clicktofocus@janedoe.example.com)\n"
+
+#: subprojects/extensions-tool/src/command-create.c:363
+msgid "Choose one of the available templates:\n"
+msgstr "選擇其中一種可用的模板:\n"
+
+#: subprojects/extensions-tool/src/command-create.c:377
+msgid "Template"
+msgstr "模板"
+
+#: subprojects/extensions-tool/src/command-create.c:432
+msgid "The unique identifier of the new extension"
+msgstr "新擴充套件的唯一識別碼"
+
+#: subprojects/extensions-tool/src/command-create.c:435
+msgid "NAME"
+msgstr "NAME"
+
+#: subprojects/extensions-tool/src/command-create.c:436
+msgid "The user-visible name of the new extension"
+msgstr "使用者看得到的新擴充套件名稱"
+
+#: subprojects/extensions-tool/src/command-create.c:438
+msgid "DESCRIPTION"
+msgstr "DESCRIPTION"
+
+#: subprojects/extensions-tool/src/command-create.c:440
+msgid "A short description of what the extension does"
+msgstr "簡短描述該擴充套件的作用"
+
+#: subprojects/extensions-tool/src/command-create.c:443
+msgid "TEMPLATE"
+msgstr "TEMPLATE"
+
+#: subprojects/extensions-tool/src/command-create.c:444
+msgid "The template to use for the new extension"
+msgstr "新擴充套件所要使用的模板"
+
+#: subprojects/extensions-tool/src/command-create.c:450
+msgid "Enter extension information interactively"
+msgstr "互動式輸入擴充套件資訊"
+
+#: subprojects/extensions-tool/src/command-create.c:458
+msgid "Create a new extension"
+msgstr "建立新的擴充套件"
+
+#: subprojects/extensions-tool/src/command-create.c:476
+#: subprojects/extensions-tool/src/command-list.c:172
+msgid "Unknown arguments"
+msgstr "未知參數"
+
+#: subprojects/extensions-tool/src/command-create.c:501
+msgid "UUID, name and description are required"
+msgstr "UUID、名稱及描述皆為必填"
+
+#: subprojects/extensions-tool/src/command-disable.c:46
+#: subprojects/extensions-tool/src/command-enable.c:46
+#: subprojects/extensions-tool/src/command-info.c:50
+#: subprojects/extensions-tool/src/command-list.c:64
+#: subprojects/extensions-tool/src/main.c:146
+msgid "Failed to connect to GNOME Shell\n"
+msgstr "無法連接至《GNOME Shell》\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:53
+#: subprojects/extensions-tool/src/command-enable.c:53
+#, c-format
+msgid "Extension “%s” does not exist\n"
+msgstr "「%s」擴充套件不存在\n"
+
+#: subprojects/extensions-tool/src/command-disable.c:101
+msgid "Disable an extension"
+msgstr "停用擴充套件"
+
+#: subprojects/extensions-tool/src/command-disable.c:119
+#: subprojects/extensions-tool/src/command-enable.c:119
+#: subprojects/extensions-tool/src/command-info.c:103
+#: subprojects/extensions-tool/src/command-prefs.c:105
+#: subprojects/extensions-tool/src/command-reset.c:76
+#: subprojects/extensions-tool/src/command-uninstall.c:104
+msgid "No UUID given"
+msgstr "未提供 UUID"
+
+#: subprojects/extensions-tool/src/command-disable.c:124
+#: subprojects/extensions-tool/src/command-enable.c:124
+#: subprojects/extensions-tool/src/command-info.c:108
+#: subprojects/extensions-tool/src/command-prefs.c:110
+#: subprojects/extensions-tool/src/command-reset.c:81
+#: subprojects/extensions-tool/src/command-uninstall.c:109
+msgid "More than one UUID given"
+msgstr "提供多於一個 UUID"
+
+#: subprojects/extensions-tool/src/command-enable.c:101
+msgid "Enable an extension"
+msgstr "啟用擴充套件"
+
+#: subprojects/extensions-tool/src/command-info.c:59
+#: subprojects/extensions-tool/src/main.c:155
+#, c-format
+msgid "Extension “%s” doesn't exist\n"
+msgstr "「%s」擴充套件不存在\n"
+
+#: subprojects/extensions-tool/src/command-info.c:85
+msgid "Show extensions info"
+msgstr "顯示擴充套件資訊"
+
+#: subprojects/extensions-tool/src/command-install.c:173
+msgid "Overwrite an existing extension"
+msgstr "覆蓋既存的擴充套件"
+
+#: subprojects/extensions-tool/src/command-install.c:175
+msgid "EXTENSION_BUNDLE"
+msgstr "EXTENSION_BUNDLE"
+
+#: subprojects/extensions-tool/src/command-install.c:184
+msgid "Install an extension bundle"
+msgstr "安裝擴充套件組合"
+
+#: subprojects/extensions-tool/src/command-install.c:202
+msgid "No extension bundle specified"
+msgstr "未指定擴充套件組合"
+
+#: subprojects/extensions-tool/src/command-install.c:208
+msgid "More than one extension bundle specified"
+msgstr "指定多於一個擴充套件組合"
+
+#: subprojects/extensions-tool/src/command-list.c:128
+msgid "Show user-installed extensions"
+msgstr "顯示使用者安裝的擴充套件"
+
+#: subprojects/extensions-tool/src/command-list.c:131
+msgid "Show system-installed extensions"
+msgstr "顯示系統安裝的擴充套件"
+
+#: subprojects/extensions-tool/src/command-list.c:134
+msgid "Show enabled extensions"
+msgstr "顯示啟用的擴充套件"
+
+#: subprojects/extensions-tool/src/command-list.c:137
+msgid "Show disabled extensions"
+msgstr "顯示停用的擴充套件"
+
+#: subprojects/extensions-tool/src/command-list.c:140
+msgid "Show extensions with preferences"
+msgstr "顯示具有偏好設定的擴充套件"
+
+#: subprojects/extensions-tool/src/command-list.c:143
+msgid "Show extensions with updates"
+msgstr "顯示有更新的擴充套件"
+
+#: subprojects/extensions-tool/src/command-list.c:146
+msgid "Print extension details"
+msgstr "輸出擴充套件的詳細資訊"
+
+#: subprojects/extensions-tool/src/command-list.c:154
+msgid "List installed extensions"
+msgstr "列出已安裝的擴充套件"
+
+#: subprojects/extensions-tool/src/command-pack.c:450
+msgid "FILE"
+msgstr "FILE"
+
+#: subprojects/extensions-tool/src/command-pack.c:451
+msgid "Additional source to include in the bundle"
+msgstr "要加入至擴充套件組合的其他來源"
+
+#: subprojects/extensions-tool/src/command-pack.c:454
+msgid "SCHEMA"
+msgstr "SCHEMA"
+
+#: subprojects/extensions-tool/src/command-pack.c:455
+msgid "A GSettings schema that should be included"
+msgstr "應包含的 GSettings 基模"
+
+#: subprojects/extensions-tool/src/command-pack.c:457
+#: subprojects/extensions-tool/src/command-pack.c:468
+msgid "DIRECTORY"
+msgstr "DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:459
+msgid "The directory where translations are found"
+msgstr "含有翻譯檔目錄"
+
+#: subprojects/extensions-tool/src/command-pack.c:461
+msgid "DOMAIN"
+msgstr "DOMAIN"
+
+#: subprojects/extensions-tool/src/command-pack.c:463
+msgid "The gettext domain to use for translations"
+msgstr "用於翻譯的 gettext 域名"
+
+#: subprojects/extensions-tool/src/command-pack.c:466
+msgid "Overwrite an existing pack"
+msgstr "覆蓋既存的套件"
+
+#: subprojects/extensions-tool/src/command-pack.c:470
+msgid "The directory where the pack should be created"
+msgstr "應建立套件的目錄"
+
+#: subprojects/extensions-tool/src/command-pack.c:472
+msgid "SOURCE_DIRECTORY"
+msgstr "SOURCE_DIRECTORY"
+
+#: subprojects/extensions-tool/src/command-pack.c:481
+msgid "Create an extension bundle"
+msgstr "建立擴充套件組合"
+
+#: subprojects/extensions-tool/src/command-pack.c:501
+msgid "More than one source directory specified"
+msgstr "指定多於一個來源目錄"
+
+#: subprojects/extensions-tool/src/command-prefs.c:47
+#, c-format
+msgid "Extension “%s” doesn't have preferences\n"
+msgstr "「%s」擴充套件沒有偏好設定\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:62
+#, c-format
+msgid "Failed to open prefs for extension “%s”: %s\n"
+msgstr "無法開啟「%s」擴充套件的偏好設定:%s\n"
+
+#: subprojects/extensions-tool/src/command-prefs.c:87
+msgid "Opens extension preferences"
+msgstr "開啟擴充套件的偏好設定"
+
+#: subprojects/extensions-tool/src/command-reset.c:58
+msgid "Reset an extension"
+msgstr "重設擴充套件"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:49
+msgid "Cannot uninstall system extensions\n"
+msgstr "無法解除安裝系統的擴充套件\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:64
+#, c-format
+msgid "Failed to uninstall “%s”\n"
+msgstr "無法解除安裝「%s」\n"
+
+#: subprojects/extensions-tool/src/command-uninstall.c:86
+msgid "Uninstall an extension"
+msgstr "解除安裝擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:72
+msgid "Do not print error messages"
+msgstr "不要輸出錯誤訊息"
+
+#: subprojects/extensions-tool/src/main.c:244
+msgid "Path"
+msgstr "路徑"
+
+#: subprojects/extensions-tool/src/main.c:247
+msgid "URL"
+msgstr "網址"
+
+#: subprojects/extensions-tool/src/main.c:250
+msgid "Original author"
+msgstr "原始作者"
+
+#: subprojects/extensions-tool/src/main.c:253
+msgid "Version"
+msgstr "版本"
+
+#: subprojects/extensions-tool/src/main.c:256
+msgid "State"
+msgstr "狀態"
+
+#: subprojects/extensions-tool/src/main.c:294
+msgid "“version” takes no arguments"
+msgstr "「版本」沒有參數"
+
+#: subprojects/extensions-tool/src/main.c:296
+#: subprojects/extensions-tool/src/main.c:316
+msgid "Usage:"
+msgstr "用法:"
+
+#: subprojects/extensions-tool/src/main.c:299
+msgid "Print version information and exit."
+msgstr "輸出版本資訊並離開。"
+
+#: subprojects/extensions-tool/src/main.c:314
+#: subprojects/extensions-tool/src/main.c:317
+msgid "COMMAND"
+msgstr "命令"
+
+#: subprojects/extensions-tool/src/main.c:317
+msgid "[ARGS…]"
+msgstr "[參數…]"
+
+#: subprojects/extensions-tool/src/main.c:319
+msgid "Commands:"
+msgstr "命令:"
+
+#: subprojects/extensions-tool/src/main.c:320
+msgid "Print help"
+msgstr "輸出說明"
+
+#: subprojects/extensions-tool/src/main.c:322
+msgid "Enable extension"
+msgstr "啟用擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:323
+msgid "Disable extension"
+msgstr "停用擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:324
+msgid "Reset extension"
+msgstr "重設擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:325
+msgid "Uninstall extension"
+msgstr "解除安裝擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:326
+msgid "List extensions"
+msgstr "列出擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:327
+#: subprojects/extensions-tool/src/main.c:328
+msgid "Show extension info"
+msgstr "顯示擴充套件資訊"
+
+#: subprojects/extensions-tool/src/main.c:329
+msgid "Open extension preferences"
+msgstr "開啟擴充套件的偏好設定"
+
+#: subprojects/extensions-tool/src/main.c:330
+msgid "Create extension"
+msgstr "建立擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:331
+msgid "Package extension"
+msgstr "打包擴充套件"
+
+#: subprojects/extensions-tool/src/main.c:332
+msgid "Install extension bundle"
+msgstr "安裝擴充套件組合"
+
+#: subprojects/extensions-tool/src/main.c:334
+#, c-format
+msgid "Use “%s” to get detailed help.\n"
+msgstr "使用「%s」以取得詳細說明。\n"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:4
+msgid "Plain"
+msgstr "空白"
+
+#: subprojects/extensions-tool/src/templates/00-plain.desktop.in:5
+msgid "An empty extension"
+msgstr "空白的擴充套件"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:4
+msgid "Indicator"
+msgstr "指示器"
+
+#: subprojects/extensions-tool/src/templates/indicator.desktop.in:5
+msgid "Add an icon to the top bar"
+msgstr "在頂端列加入一個圖示"
+
+#. translators:
+#. * The number of sound outputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1907
+#, c-format
+msgid "%u Output"
+msgid_plural "%u Outputs"
+msgstr[0] "%u 個輸出"
+
+#. translators:
+#. * The number of sound inputs on a particular device
+#: subprojects/gvc/gvc-mixer-control.c:1917
+#, c-format
+msgid "%u Input"
+msgid_plural "%u Inputs"
+msgstr[0] "%u 個輸入"
+
+#: subprojects/gvc/gvc-mixer-control.c:2867
+msgid "System Sounds"
+msgstr "系統音效"
+
+#~ msgid ""
+#~ "Whether the default Bluetooth adapter had set up devices associated to it"
+#~ msgstr "預設的藍牙配接器是否已經與裝置完成配對"
+
+#~ msgid ""
+#~ "The shell will only show a Bluetooth menu item if a Bluetooth adapter is "
+#~ "powered, or if there were devices set up associated with the default "
+#~ "adapter. This will be reset if the default adapter is ever seen not to "
+#~ "have devices associated to it."
+#~ msgstr ""
+#~ "《GNOME Shell》只會在藍牙配接器啟動或在有裝置已與預設配接器配對時,才會顯"
+#~ "示選單的藍牙項目。若預設的配接器尚未與裝置配對則會重設此選項。"
+
+#~ msgid "Log Out"
+#~ msgstr "登出"
+
+#, javascript-format
+#~ msgid "%s Off"
+#~ msgstr "%s已關閉"
+
+#, javascript-format
+#~ msgid "%s Connected"
+#~ msgstr "%s已連接"
+
+#, javascript-format
+#~ msgid "%s Unmanaged"
+#~ msgstr "%s不受管理"
+
+#, javascript-format
+#~ msgid "%s Disconnecting"
+#~ msgstr "正在中斷%s連接"
+
+#, javascript-format
+#~ msgid "%s Connecting"
+#~ msgstr "正在連接%s"
+
+#, javascript-format
+#~ msgid "%s Requires Authentication"
+#~ msgstr "%s要求核對身分"
+
+#, javascript-format
+#~ msgid "Firmware Missing For %s"
+#~ msgstr "%s缺少韌體"
+
+#, javascript-format
+#~ msgid "%s Unavailable"
+#~ msgstr "%s無法使用"
+
+#, javascript-format
+#~ msgid "%s Connection Failed"
+#~ msgstr "%s無法連接"
+
+#~ msgid "Wired Settings"
+#~ msgstr "有線網路設定值"
+
+#~ msgid "Mobile Broadband Settings"
+#~ msgstr "行動寬頻設定值"
+
+#, javascript-format
+#~ msgid "%s Hardware Disabled"
+#~ msgstr "%s硬體已停用"
+
+#, javascript-format
+#~ msgid "%s Disabled"
+#~ msgstr "%s已停用"
+
+#~ msgid "Bluetooth Settings"
+#~ msgstr "藍牙設定值"
+
+#~ msgid "Connect to Internet"
+#~ msgstr "連接網際網路"
+
+#~ msgid "Airplane Mode is On"
+#~ msgstr "飛安模式已開啟"
+
+#~ msgid "Wi-Fi is disabled when airplane mode is on."
+#~ msgstr "飛安模式開啟時會停用 Wi-Fi。"
+
+#~ msgid "Turn Off Airplane Mode"
+#~ msgstr "關閉飛安模式"
+
+#~ msgid "Wi-Fi is Off"
+#~ msgstr "Wi-Fi 已關閉"
+
+#~ msgid "Wi-Fi needs to be turned on in order to connect to a network."
+#~ msgstr "需將 Wi-Fi 開啟才能連接網路。"
+
+#~ msgid "Turn On Wi-Fi"
+#~ msgstr "開啟 Wi-Fi"
+
+#~ msgid "Wi-Fi Networks"
+#~ msgstr "Wi-Fi 網路"
+
+#~ msgid "Select a network"
+#~ msgstr "選擇一個網路"
+
+#~ msgid "No Networks"
+#~ msgstr "沒有網路"
+
+#~ msgid "Use hardware switch to turn off"
+#~ msgstr "使用硬體開關來關閉"
+
+#~ msgid "Select Network"
+#~ msgstr "選擇網路"
+
+#~ msgid "Wi-Fi Settings"
+#~ msgstr "Wi-Fi 設定值"
+
+#, javascript-format
+#~ msgid "%s Not Connected"
+#~ msgstr "%s未連接"
+
+#~ msgid "unknown"
+#~ msgstr "不明"
+
+#~ msgid "activating…"
+#~ msgstr "正在啟動…"
+
+#~ msgid "deactivating…"
+#~ msgstr "正在停用…"
+
+#~ msgid "deactivated"
+#~ msgstr "已停用"
+
+#~ msgid "connecting…"
+#~ msgstr "正在連接…"
+
+#~ msgid "authentication required"
+#~ msgstr "要求核對身分"
+
+#~ msgid "connection failed"
+#~ msgstr "連接失敗"
+
+#~ msgid "VPN Settings"
+#~ msgstr "VPN 設定值"
+
+#~ msgid "VPN"
+#~ msgstr "VPN 網路"
+
+#~ msgid "VPN Off"
+#~ msgstr "VPN 關閉"
+
+#~ msgid "Network Settings"
+#~ msgstr "網路設定值"
+
+#, javascript-format
+#~ msgid "%s Wired Connection"
+#~ msgid_plural "%s Wired Connections"
+#~ msgstr[0] "%s 個有線網路連接"
+
+#, javascript-format
+#~ msgid "%s Wi-Fi Connection"
+#~ msgid_plural "%s Wi-Fi Connections"
+#~ msgstr[0] "%s 個 Wi-Fi 連接"
+
+#, javascript-format
+#~ msgid "%s Modem Connection"
+#~ msgid_plural "%s Modem Connections"
+#~ msgstr[0] "%s 個數據機連接"
+
+#~ msgid "Connection failed"
+#~ msgstr "連接失敗"
+
+#~ msgid "Activation of network connection failed"
+#~ msgstr "啟動網路連接失敗"
+
+#~ msgid "Power Settings"
+#~ msgstr "電源設定值"
+
+#~ msgid "Lock"
+#~ msgstr "鎖定"
+
+#~ msgid "Power Off / Log Out"
+#~ msgstr "關閉電源/登出"
+
+#, javascript-format
+#~ msgid "%d Connected"
+#~ msgid_plural "%d Connected"
+#~ msgstr[0] "%d 個裝置已連接"
+
+#~ msgid "Bluetooth On"
+#~ msgstr "藍牙已開啟"
+
+#~ msgid "Bluetooth Off"
+#~ msgstr "藍牙已關閉"
+
+#~ msgid "Location Enabled"
+#~ msgstr "定位已啟用"
+
+#~ msgid "Disable"
+#~ msgstr "停用"
+
+#~ msgid "Privacy Settings"
+#~ msgstr "隱私設定值"
+
+#~ msgid "Location In Use"
+#~ msgstr "位置資訊使用中"
+
+#~ msgid "Location Disabled"
+#~ msgstr "定位服務已停用"
+
+#~ msgid "Enable"
+#~ msgstr "啟用"
+
+#~ msgid "Night Light Disabled"
+#~ msgstr "夜光模式已停用"
+
+#~ msgid "Resume"
+#~ msgstr "繼續"
+
+#~ msgid "Disable Until Tomorrow"
+#~ msgstr "停用至明天"
+
+#~ msgid "Fully Charged"
+#~ msgstr "已充飽"
+
+#~ msgid "Not Charging"
+#~ msgstr "未充電"
+
+#~ msgid "Estimating…"
+#~ msgstr "正在評估…"
+
+#, javascript-format
+#~ msgid "%d∶%02d Remaining (%d %%)"
+#~ msgstr "還能使用 %d∶%02d (%d%%)"
+
+#, javascript-format
+#~ msgid "%d∶%02d Until Full (%d %%)"
+#~ msgstr "充飽仍需 %d∶%02d (%d%%)"
+
+#~ msgid "Screen is Being Shared"
+#~ msgstr "正在分享螢幕"
+
+#~ msgid "Turn off"
+#~ msgstr "關閉"
+
+#~ msgid "Airplane Mode On"
+#~ msgstr "飛安模式已開啟"
+
+#~ msgid "Enable introspection API"
+#~ msgstr "啟用審視 API"
+
+#~ msgid ""
+#~ "Enables a D-Bus API that allows to introspect the application state of "
+#~ "the shell."
+#~ msgstr "啟用允許審視 Shell 應用程式狀態的 D-Bus API。"
+
+#~ msgid "Remove from Favorites"
+#~ msgstr "自喜好中移除"
+
+#~ msgid "Add to Favorites"
+#~ msgstr "加入喜好"
+
+#~ msgid "Minimize"
+#~ msgstr "最小化"
+
+#~ msgid "Unmaximize"
+#~ msgstr "取消最大化"
+
+#~ msgid "Author"
+#~ msgstr "作者"
+
+#~ msgid "Warning"
+#~ msgstr "警告"
+
+#~ msgid ""
+#~ "Extensions can cause system issues, including performance problems. If "
+#~ "you encounter problems with your system, it is recommended to disable all "
+#~ "extensions."
+#~ msgstr ""
+#~ "擴充套件可能會造成系統問題,包含效能問題。如果您遇到系統問題,建議您先停用"
+#~ "所有的擴充套件。"
+
+#~ msgid "Failed to connect to GNOME Shell"
+#~ msgstr "無法連接至 GNOME Shell"
+
+#~ msgid "App Picker View"
+#~ msgstr "程式挑選器檢視"
+
+#~ msgid "Index of the currently selected view in the application picker."
+#~ msgstr "應用程式挑選器中目前選取檢視的索引。"
+
+#~ msgid "Frequently used applications will appear here"
+#~ msgstr "經常使用的應用程式會出現在這裡"
+
+#~ msgid "Frequent"
+#~ msgstr "常用"
+
+#~ msgid "All"
+#~ msgstr "全部"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d"
+#~ msgstr "%b%-d日%A"
+
+#~ msgctxt "calendar heading"
+#~ msgid "%A, %B %-d, %Y"
+#~ msgstr "%Y年%b%-d日%A"
+
+#~ msgid "Off"
+#~ msgstr "關閉"
+
+#~ msgid "On"
+#~ msgstr "開啟"
+
+#~ msgid "Copy Error"
+#~ msgstr "複製錯誤"
+
+#~ msgid "Browse in Software"
+#~ msgstr "在《軟體》中瀏覽"
+
+#~ msgid "Next"
+#~ msgstr "下一個"
+
+#~ msgctxt "button"
+#~ msgid "Sign In"
+#~ msgstr "登入"
+
+#~ msgctxt "search-result"
+#~ msgid "Lock Orientation"
+#~ msgstr "鎖定方向"
+
+#~ msgid "Rename"
+#~ msgstr "重新命名"
+
+#~ msgid "Password:"
+#~ msgstr "密碼: "
+
+#~ msgid "Type again:"
+#~ msgstr "再輸入一次:"
+
+#~ msgid "Password: "
+#~ msgstr "密碼: "
+
+#~ msgid "Authentication required by wireless network"
+#~ msgstr "無線網路所需要的核對"
+
+#~ msgid "Mobile broadband network password"
+#~ msgstr "行動寬頻網路密碼"
+
+#~ msgid "%A, %B %d"
+#~ msgstr "%b%d日%A"
+
+#~ msgid "%d new message"
+#~ msgid_plural "%d new messages"
+#~ msgstr[0] "%d 個新訊息"
+
+#~ msgid "%d new notification"
+#~ msgid_plural "%d new notifications"
+#~ msgstr[0] "%d 個新通知"
+
+#~ msgid ""
+#~ "Keybinding that pauses and resumes all running tweens, for debugging "
+#~ "purposes"
+#~ msgstr "暫停與恢復執行中重複項的組合鍵,用於除錯用途"
+
+#~ msgid "Which keyboard to use"
+#~ msgstr "要使用哪種鍵盤"
+
+#~ msgid "The type of keyboard to use."
+#~ msgstr "要使用的鍵盤類型。"
+
+#~ msgid "toggle-switch-us"
+#~ msgstr "toggle-switch-us"
+
+#~ msgid "Account Settings"
+#~ msgstr "帳號設定值"
+
+#~ msgid "Orientation Lock"
+#~ msgstr "方向鎖"
+
+#~ msgid "network-workgroup"
+#~ msgstr "network-workgroup"
+
+#~ msgid "There was an error loading the preferences dialog for %s:"
+#~ msgstr "載入 %s 的偏好設定對話盒時發生錯誤:"
+
+#~ msgid "%s all day."
+#~ msgstr "全天%s。"
+
+#~ msgid "%s, then %s later."
+#~ msgstr "%s,較晚%s。"
+
+#~ msgid "%s, then %s, followed by %s later."
+#~ msgstr "%s,然後%s,接著較晚%s。"
+
+#~ msgid "Feels like %s."
+#~ msgstr "體感溫度 %s。"
+
+#~ msgctxt "search-result"
+#~ msgid "Log out"
+#~ msgstr "登出"
+
+#~ msgctxt "search-result"
+#~ msgid "Switch user"
+#~ msgstr "切換使用者"
+
+#~ msgid "Hide tray"
+#~ msgstr "隱藏系統匣"
+
+#~ msgid "Status Icons"
+#~ msgstr "狀態圖示"
+
+#~ msgid "Events"
+#~ msgstr "行程"
+
+#~ msgid "Notifications"
+#~ msgstr "通知"
+
+#~ msgid "Media"
+#~ msgstr "媒體"
+
+#~ msgid "Web Authentication Redirect"
+#~ msgstr "網頁核對重新導向"
+
+#~ msgid "connecting..."
+#~ msgstr "連線中…"
+
+#~ msgid "%d x %d"
+#~ msgstr "%d x %d"
+
+#~ msgid "Not In Use"
+#~ msgstr "未被使用"
+
+#~ msgid "Show the week date in the calendar"
+#~ msgstr "在日曆中顯示週數"
+
+#~ msgid "If true, display the ISO week date in the calendar."
+#~ msgstr "如果設為 true,在日曆中顯示 ISO 週數。"
+
+#~ msgid "Use as Internet connection"
+#~ msgstr "作為網際網路連線"
+
+#~ msgid "%s is requesting access to your location."
+#~ msgstr "%s 要求存取您的位置。"
+
+#~ msgid "GNOME Shell (wayland compositor)"
+#~ msgstr "GNOME Shell (wayland 組合器)"
+
+#~ msgid "%d Connected Device"
+#~ msgid_plural "%d Connected Devices"
+#~ msgstr[0] "%d 已連線的裝置"
+
+#~ msgid "Not Connected"
+#~ msgstr "未連線"
+
+#~ msgid "In Use"
+#~ msgstr "使用中"
+
+#~ msgid "Connected"
+#~ msgstr "已連線"
+
+#~ msgid "Unmanaged"
+#~ msgstr "未管理"
+
+#~ msgid "Firmware missing"
+#~ msgstr "缺少韌體"
+
+#~ msgid "Unavailable"
+#~ msgstr "無法使用"
+
+#~ msgid "Hardware Disabled"
+#~ msgstr "硬體已停用"
+
+#~ msgid "UPS"
+#~ msgstr "UPS"
+
+#~ msgid "Battery"
+#~ msgstr "電池"
+
+#~ msgid "Show the message list"
+#~ msgstr "顯示訊息清單"
+
+#~ msgctxt "event list time"
+#~ msgid "%H∶%M"
+#~ msgstr "%H∶%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l∶%M %p"
+#~ msgstr "%p %l∶%M"
+
+#~ msgid "Invitation"
+#~ msgstr "邀請"
+
+#~ msgid "Call"
+#~ msgstr "通話"
+
+#~ msgid "File Transfer"
+#~ msgstr "檔案傳輸程式"
+
+#~ msgid "Chat"
+#~ msgstr "聊天"
+
+#~ msgid "Invitation to %s"
+#~ msgstr "邀請加入 %s"
+
+#~ msgid "%s is inviting you to join %s"
+#~ msgstr "%s 正在邀請您加入 %s"
+
+#~ msgid "Decline"
+#~ msgstr "拒絕"
+
+#~ msgid "Accept"
+#~ msgstr "接受"
+
+#~ msgid "Video call from %s"
+#~ msgstr "來自 %s 的視訊電話"
+
+#~ msgid "Call from %s"
+#~ msgstr "%s 來電"
+
+#~ msgid "Answer"
+#~ msgstr "接聽"
+
+#~ msgid "%s is sending you %s"
+#~ msgstr "%s 正傳送給您 %s"
+
+#~ msgid "%s would like permission to see when you are online"
+#~ msgstr "%s 想要得到查看您上線狀態的許可"
+
+#~ msgid "Authentication failed"
+#~ msgstr "核對失敗"
+
+#~ msgid "Encryption error"
+#~ msgstr "加密發生錯誤"
+
+#~ msgid "Certificate not provided"
+#~ msgstr "沒有提供憑證"
+
+#~ msgid "Certificate untrusted"
+#~ msgstr "憑證不被信任"
+
+#~ msgid "Certificate expired"
+#~ msgstr "憑證已逾期"
+
+#~ msgid "Certificate not activated"
+#~ msgstr "憑證尚未使用"
+
+#~ msgid "Certificate hostname mismatch"
+#~ msgstr "憑證主機名稱不符"
+
+#~ msgid "Certificate fingerprint mismatch"
+#~ msgstr "憑證數位指紋不符"
+
+#~ msgid "Certificate self-signed"
+#~ msgstr "憑證為自我簽署"
+
+#~ msgid "Status is set to offline"
+#~ msgstr "狀態設為離線"
+
+#~ msgid "Certificate is invalid"
+#~ msgstr "憑證無效"
+
+#~ msgid "Connection has been refused"
+#~ msgstr "連線遭到拒絕"
+
+#~ msgid "Connection can't be established"
+#~ msgstr "無法建立連線"
+
+#~ msgid "Connection has been lost"
+#~ msgstr "已失去連線"
+
+#~ msgid "This account is already connected to the server"
+#~ msgstr "這個帳號已連接至該伺服器"
+
+#~ msgid ""
+#~ "Connection has been replaced by a new connection using the same resource"
+#~ msgstr "連線已被使用相同資源的新連線取代"
+
+#~ msgid "The account already exists on the server"
+#~ msgstr "該帳號已經存在於伺服器上"
+
+#~ msgid "Server is currently too busy to handle the connection"
+#~ msgstr "伺服器目前過於忙碌以致於無法處理該連線"
+
+#~ msgid "Certificate has been revoked"
+#~ msgstr "憑證已被撤銷"
+
+#~ msgid ""
+#~ "Certificate uses an insecure cipher algorithm or is cryptographically weak"
+#~ msgstr "憑證使用不安全的密碼演算法,或是密碼處理上較弱"
+
+#~ msgid ""
+#~ "The length of the server certificate, or the depth of the server "
+#~ "certificate chain, exceed the limits imposed by the cryptography library"
+#~ msgstr ""
+#~ "伺服器憑證的長度,或是伺服器憑證鏈的深度超過密碼函式庫所能處理的範圍"
+
+#~ msgid "Internal error"
+#~ msgstr "內部的錯誤"
+
+#~ msgid "View account"
+#~ msgstr "檢視帳號"
+
+#~ msgctxt "list sunday"
+#~ msgid "Su"
+#~ msgstr "日"
+
+#~ msgctxt "list monday"
+#~ msgid "M"
+#~ msgstr "一"
+
+#~ msgctxt "list tuesday"
+#~ msgid "T"
+#~ msgstr "二"
+
+#~ msgctxt "list wednesday"
+#~ msgid "W"
+#~ msgstr "三"
+
+#~ msgctxt "list thursday"
+#~ msgid "Th"
+#~ msgstr "四"
+
+#~ msgctxt "list friday"
+#~ msgid "F"
+#~ msgstr "五"
+
+#~ msgctxt "list saturday"
+#~ msgid "S"
+#~ msgstr "六"
+
+#~ msgid "Nothing Scheduled"
+#~ msgstr "沒有預訂行程"
+
+#~ msgid "This week"
+#~ msgstr "本週"
+
+#~ msgid "Next week"
+#~ msgstr "下週"
+
+#~ msgid "Removable Devices"
+#~ msgstr "可移除式裝置"
+
+#~ msgid "Eject"
+#~ msgstr "退出"
+
+#~ msgid "Unmute"
+#~ msgstr "取消靜音"
+
+#~ msgid "Mute"
+#~ msgstr "靜音"
+
+#~ msgid "Open Calendar"
+#~ msgstr "開啟行事曆"
+
+#~ msgid "Open"
+#~ msgstr "開啟"
+
+#~ msgid "Clear Messages"
+#~ msgstr "清除訊息"
+
+#~ msgid "Notification Settings"
+#~ msgstr "通知設定值"
+
+#~ msgid "Tray Menu"
+#~ msgstr "系統匣選單"
+
+#~ msgid "No Messages"
+#~ msgstr "沒有訊息"
+
+#~ msgid "Message Tray"
+#~ msgstr "訊息匣"
+
+#~ msgid "Captive Portal"
+#~ msgstr "網頁入口管制"
+
+#~ msgid "The maximum accuracy level of location."
+#~ msgstr "地理位置的最高精確等級。"
+
+#~ msgid ""
+#~ "Configures the maximum level of location accuracy applications are "
+#~ "allowed to see. Valid options are 'off' (disable location tracking), "
+#~ "'country', 'city', 'neighborhood', 'street', and 'exact' (typically "
+#~ "requires GPS receiver). Please keep in mind that this only controls what "
+#~ "GeoClue will allow applications to see and they can find user's location "
+#~ "on their own using network resources (albeit with street-level accuracy "
+#~ "at best)."
+#~ msgstr ""
+#~ "設定應用程式可查看的地理位置最高精確等級。有效的選項為「off」(停用地理位置"
+#~ "追蹤)、「country」(國家)、「city」(縣市)、「neiborhood」(鄰里)、「street」"
+#~ "(街路)、「exact」(一般需要 GPS 接收器)。請務必記住這只能控制 GeoClue 所允"
+#~ "許應用程式查看的等級,而程式可自行使用本身的網路資源尋找使用者的所在位置 "
+#~ "(雖然精確度最高只能到達街路等級)。"
+
+#~ msgid "Arrangement of buttons on the titlebar"
+#~ msgstr "標題列上的按鈕排列"
+
+#~ msgid ""
+#~ "This key overrides the key in org.gnome.desktop.wm.preferences when "
+#~ "running GNOME Shell."
+#~ msgstr ""
+#~ "這個設定鍵會在執行 GNOME Shell 時覆蓋 org.gnome.desktop.wm.preferences 中"
+#~ "的設定鍵。"
+
+#~ msgid "Select an extension to configure using the combobox above."
+#~ msgstr "使用上面的組合方塊選擇要設定的擴充功能。"
+
+#~ msgid "calendar:MY"
+#~ msgstr "calendar:MY"
+
+#~ msgid "<b>%A</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%A</b>,<b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
+#~ msgstr "<b>%m月</b><b>%d日</b>,<b>%H:%M</b>"
+
+#~ msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
+#~ msgstr "<b>%Y年</b><b>%m月</b><b>%d日</b>,<b>%H:%M</b> "
+
+#~ msgid "List of categories that should be displayed as folders"
+#~ msgstr "要顯示為資料夾的分類清單"
+
+#~ msgid ""
+#~ "Each category name in this list will be represented as folder in the "
+#~ "application view, rather than being displayed inline in the main view."
+#~ msgstr ""
+#~ "這個清單中的每個分類名稱都會在應用程式檢視中以資料夾表示,而不是在主要檢視"
+#~ "中直接顯示。"
+
+#~ msgid "Authorization request from %s"
+#~ msgstr "來自「%s」的核對要求"
+
+#~ msgid "Device %s wants to pair with this computer"
+#~ msgstr "裝置 %s 想要和這個電腦配對"
+
+#~ msgid "Device %s wants access to the service '%s'"
+#~ msgstr "裝置 %s 想要存取服務「%s」"
+
+#~ msgid "Grant this time only"
+#~ msgstr "只有這次准許"
+
+#~ msgid "Reject"
+#~ msgstr "拒絕"
+
+#~ msgid "Pairing confirmation for %s"
+#~ msgstr "%s 的配對確認"
+
+#~ msgid ""
+#~ "Please confirm whether the Passkey '%06d' matches the one on the device."
+#~ msgstr "請確認通行碼「%06d」是否和裝置上的相符。"
+
+#~ msgid "Does not match"
+#~ msgstr "不相符"
+
+#~ msgid "Pairing request for %s"
+#~ msgstr "%s 的配對請求"
+
+#~ msgid "Please enter the PIN mentioned on the device."
+#~ msgstr "請輸入裝置所提及的 PIN。"
+
+#~ msgid "OK"
+#~ msgstr "確定"
+
+#~ msgid ""
+#~ "Sorry, no wisdom for you today:\n"
+#~ "%s"
+#~ msgstr ""
+#~ "抱歉,今天可能有些問題:\n"
+#~ "%s"
+
+#~ msgid "%s the Oracle says"
+#~ msgstr "%s Oracle 說:"
+
+#~ msgctxt "event list time"
+#~ msgid "%H\\u2236%M"
+#~ msgstr "%H\\u2236%M"
+
+#~ msgctxt "event list time"
+#~ msgid "%l\\u2236%M\\u2009%p"
+#~ msgstr "%l\\u2236%M\\u2009%p"
diff --git a/src/calendar-server/README b/src/calendar-server/README
new file mode 100644
index 0000000..ad9b5e3
--- /dev/null
+++ b/src/calendar-server/README
@@ -0,0 +1 @@
+Please keep in sync with gnome-panel.
diff --git a/src/calendar-server/calendar-debug.h b/src/calendar-server/calendar-debug.h
new file mode 100644
index 0000000..39befd7
--- /dev/null
+++ b/src/calendar-server/calendar-debug.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <mark@skynet.ie>
+ */
+
+#ifndef __CALENDAR_DEBUG_H__
+#define __CALENDAR_DEBUG_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#ifdef CALENDAR_ENABLE_DEBUG
+
+#include <stdio.h>
+
+#ifdef G_HAVE_ISO_VARARGS
+# define dprintf(...) fprintf (stderr, __VA_ARGS__);
+#elif defined(G_HAVE_GNUC_VARARGS)
+# define dprintf(args...) fprintf (stderr, args);
+#endif
+
+#else /* if !defined (CALENDAR_DEBUG) */
+
+#ifdef G_HAVE_ISO_VARARGS
+# define dprintf(...)
+#elif defined(G_HAVE_GNUC_VARARGS)
+# define dprintf(args...)
+#endif
+
+#endif /* CALENDAR_ENABLE_DEBUG */
+
+G_END_DECLS
+
+#endif /* __CALENDAR_DEBUG_H__ */
diff --git a/src/calendar-server/calendar-sources.c b/src/calendar-server/calendar-sources.c
new file mode 100644
index 0000000..9c25f4e
--- /dev/null
+++ b/src/calendar-server/calendar-sources.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <mark@skynet.ie>
+ * William Jon McCann <mccann@jhu.edu>
+ * Martin Grimme <martin@pycage.de>
+ * Christian Kellner <gicmo@xatom.net>
+ */
+
+#include <config.h>
+
+#include "calendar-sources.h"
+
+#include <libintl.h>
+#include <string.h>
+#define HANDLE_LIBICAL_MEMORY
+#define EDS_DISABLE_DEPRECATED
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#include <libecal/libecal.h>
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+#undef CALENDAR_ENABLE_DEBUG
+#include "calendar-debug.h"
+
+typedef struct _ClientData ClientData;
+typedef struct _CalendarSourceData CalendarSourceData;
+
+struct _ClientData
+{
+ ECalClient *client;
+ gulong backend_died_id;
+};
+
+typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
+
+struct _CalendarSources
+{
+ GObject parent;
+
+ ESourceRegistryWatcher *registry_watcher;
+ gulong filter_id;
+ gulong appeared_id;
+ gulong disappeared_id;
+
+ GMutex clients_lock;
+ GHashTable *clients; /* ESource -> ClientData */
+};
+
+G_DEFINE_TYPE (CalendarSources, calendar_sources, G_TYPE_OBJECT)
+
+enum
+{
+ CLIENT_APPEARED,
+ CLIENT_DISAPPEARED,
+ LAST_SIGNAL
+};
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void
+calendar_sources_client_connected_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (source_object);
+ ESource *source = user_data;
+ EClient *client;
+ g_autoptr (GError) error = NULL;
+
+ /* The calendar_sources_connect_client_sync() already stored the 'client'
+ * into the sources->clients */
+ client = calendar_sources_connect_client_finish (sources, result, &error);
+ if (error)
+ {
+ g_warning ("Could not load source '%s': %s",
+ e_source_get_uid (source),
+ error->message);
+ }
+ else
+ {
+ g_signal_emit (sources, signals[CLIENT_APPEARED], 0, client, NULL);
+ }
+
+ g_clear_object (&client);
+ g_clear_object (&source);
+}
+
+static gboolean
+registry_watcher_filter_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
+{
+ return e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) &&
+ e_source_selectable_get_selected (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
+}
+
+static void
+registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
+{
+ ECalClientSourceType source_type;
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+ source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
+ else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
+ source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
+ else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+ source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
+ else
+ g_return_if_reached ();
+
+ calendar_sources_connect_client (sources, source, source_type, 30, NULL, calendar_sources_client_connected_cb, g_object_ref (source));
+}
+
+static void
+registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
+{
+ gboolean emit;
+
+ g_mutex_lock (&sources->clients_lock);
+
+ emit = g_hash_table_remove (sources->clients, source);
+
+ g_mutex_unlock (&sources->clients_lock);
+
+ if (emit)
+ g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL);
+}
+
+static void
+client_data_free (ClientData *data)
+{
+ g_signal_handler_disconnect (data->client, data->backend_died_id);
+ g_object_unref (data->client);
+ g_free (data);
+}
+
+static void
+calendar_sources_constructed (GObject *object)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (object);
+ ESourceRegistry *registry = NULL;
+ GError *error = NULL;
+
+ G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object);
+
+ registry = e_source_registry_new_sync (NULL, &error);
+ if (error != NULL)
+ {
+ /* Any error is fatal, but we don't want to crash gnome-shell-calendar-server
+ because of e-d-s problems. So just exit here.
+ */
+ g_warning ("Failed to start evolution-source-registry: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_return_if_fail (registry != NULL);
+
+ sources->registry_watcher = e_source_registry_watcher_new (registry, NULL);
+
+ g_clear_object (&registry);
+
+ sources->clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
+ (GEqualFunc) e_source_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) client_data_free);
+ sources->filter_id = g_signal_connect (sources->registry_watcher,
+ "filter",
+ G_CALLBACK (registry_watcher_filter_cb),
+ sources);
+ sources->appeared_id = g_signal_connect (sources->registry_watcher,
+ "appeared",
+ G_CALLBACK (registry_watcher_source_appeared_cb),
+ sources);
+ sources->disappeared_id = g_signal_connect (sources->registry_watcher,
+ "disappeared",
+ G_CALLBACK (registry_watcher_source_disappeared_cb),
+ sources);
+
+ e_source_registry_watcher_reclaim (sources->registry_watcher);
+}
+
+static void
+calendar_sources_finalize (GObject *object)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (object);
+
+ g_clear_pointer (&sources->clients, g_hash_table_destroy);
+
+ if (sources->registry_watcher)
+ {
+ g_signal_handler_disconnect (sources->registry_watcher,
+ sources->filter_id);
+ g_signal_handler_disconnect (sources->registry_watcher,
+ sources->appeared_id);
+ g_signal_handler_disconnect (sources->registry_watcher,
+ sources->disappeared_id);
+ g_clear_object (&sources->registry_watcher);
+ }
+
+ g_mutex_clear (&sources->clients_lock);
+
+ G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object);
+}
+
+static void
+calendar_sources_class_init (CalendarSourcesClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->constructed = calendar_sources_constructed;
+ gobject_class->finalize = calendar_sources_finalize;
+
+ signals [CLIENT_APPEARED] =
+ g_signal_new ("client-appeared",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ E_TYPE_CAL_CLIENT);
+
+ signals [CLIENT_DISAPPEARED] =
+ g_signal_new ("client-disappeared",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING); /* ESource::uid of the disappeared client */
+}
+
+static void
+calendar_sources_init (CalendarSources *sources)
+{
+ g_mutex_init (&sources->clients_lock);
+}
+
+CalendarSources *
+calendar_sources_get (void)
+{
+ static CalendarSources *calendar_sources_singleton = NULL;
+ gpointer singleton_location = &calendar_sources_singleton;
+
+ if (calendar_sources_singleton)
+ return g_object_ref (calendar_sources_singleton);
+
+ calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
+ g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
+ singleton_location);
+
+ return calendar_sources_singleton;
+}
+
+ESourceRegistry *
+calendar_sources_get_registry (CalendarSources *sources)
+{
+ return e_source_registry_watcher_get_registry (sources->registry_watcher);
+}
+
+static void
+gather_event_clients_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSList **plist = user_data;
+ ClientData *cd = value;
+
+ if (cd)
+ *plist = g_slist_prepend (*plist, g_object_ref (cd->client));
+}
+
+GSList *
+calendar_sources_ref_clients (CalendarSources *sources)
+{
+ GSList *list = NULL;
+
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
+
+ g_mutex_lock (&sources->clients_lock);
+ g_hash_table_foreach (sources->clients, gather_event_clients_cb, &list);
+ g_mutex_unlock (&sources->clients_lock);
+
+ return list;
+}
+
+gboolean
+calendar_sources_has_clients (CalendarSources *sources)
+{
+ GHashTableIter iter;
+ gpointer value;
+ gboolean has = FALSE;
+
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
+
+ g_mutex_lock (&sources->clients_lock);
+
+ g_hash_table_iter_init (&iter, sources->clients);
+ while (!has && g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ ClientData *cd = value;
+
+ has = cd != NULL;
+ }
+
+ g_mutex_unlock (&sources->clients_lock);
+
+ return has;
+}
+
+static void
+backend_died_cb (EClient *client,
+ CalendarSources *sources)
+{
+ ESource *source;
+ const char *display_name;
+
+ source = e_client_get_source (client);
+ display_name = e_source_get_display_name (source);
+ g_warning ("The calendar backend for '%s' has crashed.", display_name);
+ g_mutex_lock (&sources->clients_lock);
+ g_hash_table_remove (sources->clients, source);
+ g_mutex_unlock (&sources->clients_lock);
+}
+
+static EClient *
+calendar_sources_connect_client_sync (CalendarSources *sources,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EClient *client = NULL;
+ ClientData *client_data;
+
+ g_mutex_lock (&sources->clients_lock);
+ client_data = g_hash_table_lookup (sources->clients, source);
+ if (client_data)
+ client = E_CLIENT (g_object_ref (client_data->client));
+ g_mutex_unlock (&sources->clients_lock);
+
+ if (client)
+ return client;
+
+ client = e_cal_client_connect_sync (source, source_type, wait_for_connected_seconds, cancellable, error);
+ if (!client)
+ return NULL;
+
+ g_mutex_lock (&sources->clients_lock);
+ client_data = g_hash_table_lookup (sources->clients, source);
+ if (client_data)
+ {
+ g_clear_object (&client);
+ client = E_CLIENT (g_object_ref (client_data->client));
+ }
+ else
+ {
+ client_data = g_new0 (ClientData, 1);
+ client_data->client = E_CAL_CLIENT (g_object_ref (client));
+ client_data->backend_died_id = g_signal_connect (client,
+ "backend-died",
+ G_CALLBACK (backend_died_cb),
+ sources);
+
+ g_hash_table_insert (sources->clients, g_object_ref (source), client_data);
+ }
+ g_mutex_unlock (&sources->clients_lock);
+
+ return client;
+}
+
+typedef struct _AsyncContext {
+ ESource *source;
+ ECalClientSourceType source_type;
+ guint32 wait_for_connected_seconds;
+} AsyncContext;
+
+static void
+async_context_free (gpointer ptr)
+{
+ AsyncContext *ctx = ptr;
+
+ if (ctx)
+ {
+ g_clear_object (&ctx->source);
+ g_free (ctx);
+ }
+}
+
+static void
+calendar_sources_connect_client_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ CalendarSources *sources = source_object;
+ AsyncContext *ctx = task_data;
+ EClient *client;
+ GError *local_error = NULL;
+
+ client = calendar_sources_connect_client_sync (sources, ctx->source, ctx->source_type,
+ ctx->wait_for_connected_seconds, cancellable, &local_error);
+ if (!client)
+ {
+ if (local_error)
+ g_task_return_error (task, local_error);
+ else
+ g_task_return_pointer (task, NULL, NULL);
+ } else {
+ g_task_return_pointer (task, client, g_object_unref);
+ }
+}
+
+void
+calendar_sources_connect_client (CalendarSources *sources,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ AsyncContext *ctx;
+ g_autoptr (GTask) task = NULL;
+
+ ctx = g_new0 (AsyncContext, 1);
+ ctx->source = g_object_ref (source);
+ ctx->source_type = source_type;
+ ctx->wait_for_connected_seconds = wait_for_connected_seconds;
+
+ task = g_task_new (sources, cancellable, callback, user_data);
+ g_task_set_source_tag (task, calendar_sources_connect_client);
+ g_task_set_task_data (task, ctx, async_context_free);
+
+ g_task_run_in_thread (task, calendar_sources_connect_client_thread);
+}
+
+EClient *
+calendar_sources_connect_client_finish (CalendarSources *sources,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, sources), NULL);
+ g_return_val_if_fail (g_async_result_is_tagged (result, calendar_sources_connect_client), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+
+void
+print_debug (const gchar *format,
+ ...)
+{
+ g_autofree char *s = NULL;
+ g_autofree char *timestamp = NULL;
+ va_list ap;
+ g_autoptr (GDateTime) now = NULL;
+ static size_t once_init_value = 0;
+ static gboolean show_debug = FALSE;
+ static guint pid = 0;
+
+ if (g_once_init_enter (&once_init_value))
+ {
+ show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL);
+ pid = getpid ();
+ g_once_init_leave (&once_init_value, 1);
+ }
+
+ if (!show_debug)
+ goto out;
+
+ now = g_date_time_new_now_local ();
+ timestamp = g_date_time_format (now, "%H:%M:%S");
+
+ va_start (ap, format);
+ s = g_strdup_vprintf (format, ap);
+ va_end (ap);
+
+ g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n",
+ pid, timestamp, g_date_time_get_microsecond (now), s);
+ out:
+ ;
+}
diff --git a/src/calendar-server/calendar-sources.h b/src/calendar-server/calendar-sources.h
new file mode 100644
index 0000000..1ffc8ad
--- /dev/null
+++ b/src/calendar-server/calendar-sources.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <mark@skynet.ie>
+ * William Jon McCann <mccann@jhu.edu>
+ * Martin Grimme <martin@pycage.de>
+ * Christian Kellner <gicmo@xatom.net>
+ */
+
+#ifndef __CALENDAR_SOURCES_H__
+#define __CALENDAR_SOURCES_H__
+
+#include <glib-object.h>
+
+#define EDS_DISABLE_DEPRECATED
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#include <libedataserver/libedataserver.h>
+#include <libecal/libecal.h>
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+G_BEGIN_DECLS
+
+#define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ())
+G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources,
+ CALENDAR, SOURCES, GObject)
+
+CalendarSources *calendar_sources_get (void);
+ESourceRegistry *calendar_sources_get_registry (CalendarSources *sources);
+GSList *calendar_sources_ref_clients (CalendarSources *sources);
+gboolean calendar_sources_has_clients (CalendarSources *sources);
+
+void calendar_sources_connect_client (CalendarSources *sources,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+EClient *calendar_sources_connect_client_finish
+ (CalendarSources *sources,
+ GAsyncResult *result,
+ GError **error);
+
+/* Set the environment variable CALENDAR_SERVER_DEBUG to show debug */
+void print_debug (const gchar *str,
+ ...) G_GNUC_PRINTF (1, 2);
+
+G_END_DECLS
+
+#endif /* __CALENDAR_SOURCES_H__ */
diff --git a/src/calendar-server/evolution-calendar.desktop.in b/src/calendar-server/evolution-calendar.desktop.in
new file mode 100644
index 0000000..1e34997
--- /dev/null
+++ b/src/calendar-server/evolution-calendar.desktop.in
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=Evolution Calendar
+Exec=evolution -c calendar
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=evolution
+NoDisplay=true
+Type=Application
+StartupNotify=true
diff --git a/src/calendar-server/gnome-shell-calendar-server.c b/src/calendar-server/gnome-shell-calendar-server.c
new file mode 100644
index 0000000..4cd28d1
--- /dev/null
+++ b/src/calendar-server/gnome-shell-calendar-server.c
@@ -0,0 +1,1131 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ *
+ * Based on code from gnome-panel's clock-applet, file calendar-client.c, with Authors:
+ *
+ * Mark McLoughlin <mark@skynet.ie>
+ * William Jon McCann <mccann@jhu.edu>
+ * Martin Grimme <martin@pycage.de>
+ * Christian Kellner <gicmo@xatom.net>
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gio/gio.h>
+
+#define HANDLE_LIBICAL_MEMORY
+#define EDS_DISABLE_DEPRECATED
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#include <libecal/libecal.h>
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+#include "calendar-sources.h"
+
+#define BUS_NAME "org.gnome.Shell.CalendarServer"
+
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.gnome.Shell.CalendarServer'>"
+ " <method name='SetTimeRange'>"
+ " <arg type='x' name='since' direction='in'/>"
+ " <arg type='x' name='until' direction='in'/>"
+ " <arg type='b' name='force_reload' direction='in'/>"
+ " </method>"
+ " <signal name='EventsAddedOrUpdated'>"
+ " <arg type='a(ssxxa{sv})' name='events' direction='out'/>"
+ " </signal>"
+ " <signal name='EventsRemoved'>"
+ " <arg type='as' name='ids' direction='out'/>"
+ " </signal>"
+ " <signal name='ClientDisappeared'>"
+ " <arg type='s' name='source_uid' direction='out'/>"
+ " </signal>"
+ " <property name='Since' type='x' access='read'/>"
+ " <property name='Until' type='x' access='read'/>"
+ " <property name='HasCalendars' type='b' access='read'/>"
+ " </interface>"
+ "</node>";
+static GDBusNodeInfo *introspection_data = NULL;
+
+struct _App;
+typedef struct _App App;
+
+static gboolean opt_replace = FALSE;
+static GOptionEntry opt_entries[] = {
+ {"replace", 0, 0, G_OPTION_ARG_NONE, &opt_replace, "Replace existing daemon", NULL},
+ {NULL }
+};
+static App *_global_app = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* While the UID is usually enough to identify an event,
+ * only the triple of (source,UID,RID) is fully unambiguous;
+ * neither may contain '\n', so we can safely use it to
+ * create a unique ID from the triple
+ */
+static gchar *
+create_event_id (const gchar *source_uid,
+ const gchar *comp_uid,
+ const gchar *comp_rid)
+{
+ return g_strconcat (
+ source_uid ? source_uid : "",
+ "\n",
+ comp_uid ? comp_uid : "",
+ "\n",
+ comp_rid ? comp_rid : "",
+ NULL);
+}
+
+typedef struct
+{
+ ECalClient *client;
+ GSList **pappointments; /* CalendarAppointment * */
+} CollectAppointmentsData;
+
+typedef struct
+{
+ gchar *id;
+ gchar *summary;
+ time_t start_time;
+ time_t end_time;
+} CalendarAppointment;
+
+static gboolean
+get_time_from_property (ECalClient *cal,
+ ICalComponent *icomp,
+ ICalPropertyKind prop_kind,
+ ICalTime * (* get_prop_func) (ICalProperty *prop),
+ ICalTimezone *default_zone,
+ ICalTime **out_itt,
+ ICalTimezone **out_timezone)
+{
+ ICalProperty *prop;
+ ICalTime *itt;
+ ICalTimezone *timezone = NULL;
+
+ prop = i_cal_component_get_first_property (icomp, prop_kind);
+ if (!prop)
+ return FALSE;
+
+ itt = get_prop_func (prop);
+
+ if (i_cal_time_is_utc (itt))
+ timezone = i_cal_timezone_get_utc_timezone ();
+ else
+ {
+ ICalParameter *param;
+
+ param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER);
+ if (param && !e_cal_client_get_timezone_sync (cal, i_cal_parameter_get_tzid (param), &timezone, NULL, NULL))
+ print_debug ("Failed to get timezone '%s'\n", i_cal_parameter_get_tzid (param));
+
+ g_clear_object (&param);
+ }
+
+ if (timezone == NULL)
+ timezone = default_zone;
+
+ i_cal_time_set_timezone (itt, timezone);
+
+ g_clear_object (&prop);
+
+ *out_itt = itt;
+ *out_timezone = timezone;
+
+ return TRUE;
+}
+
+static inline time_t
+get_ical_start_time (ECalClient *cal,
+ ICalComponent *icomp,
+ ICalTimezone *default_zone)
+{
+ ICalTime *itt;
+ ICalTimezone *timezone;
+ time_t retval;
+
+ if (!get_time_from_property (cal,
+ icomp,
+ I_CAL_DTSTART_PROPERTY,
+ i_cal_property_get_dtstart,
+ default_zone,
+ &itt,
+ &timezone))
+ {
+ return 0;
+ }
+
+ retval = i_cal_time_as_timet_with_zone (itt, timezone);
+
+ g_clear_object (&itt);
+
+ return retval;
+}
+
+static inline time_t
+get_ical_end_time (ECalClient *cal,
+ ICalComponent *icomp,
+ ICalTimezone *default_zone)
+{
+ ICalTime *itt;
+ ICalTimezone *timezone;
+ time_t retval;
+
+ if (!get_time_from_property (cal,
+ icomp,
+ I_CAL_DTEND_PROPERTY,
+ i_cal_property_get_dtend,
+ default_zone,
+ &itt,
+ &timezone))
+ {
+ if (!get_time_from_property (cal,
+ icomp,
+ I_CAL_DTSTART_PROPERTY,
+ i_cal_property_get_dtstart,
+ default_zone,
+ &itt,
+ &timezone))
+ {
+ return 0;
+ }
+
+ if (i_cal_time_is_date (itt))
+ i_cal_time_adjust (itt, 1, 0, 0, 0);
+ }
+
+ retval = i_cal_time_as_timet_with_zone (itt, timezone);
+
+ g_clear_object (&itt);
+
+ return retval;
+}
+
+static CalendarAppointment *
+calendar_appointment_new (ECalClient *cal,
+ ECalComponent *comp)
+{
+ CalendarAppointment *appt;
+ ICalTimezone *default_zone;
+ ICalComponent *ical;
+ ECalComponentId *id;
+
+ default_zone = e_cal_client_get_default_timezone (cal);
+ ical = e_cal_component_get_icalcomponent (comp);
+ id = e_cal_component_get_id (comp);
+
+ appt = g_new0 (CalendarAppointment, 1);
+
+ appt->id = create_event_id (e_source_get_uid (e_client_get_source (E_CLIENT (cal))),
+ id ? e_cal_component_id_get_uid (id) : NULL,
+ id ? e_cal_component_id_get_rid (id) : NULL);
+ appt->summary = g_strdup (i_cal_component_get_summary (ical));
+ appt->start_time = get_ical_start_time (cal, ical, default_zone);
+ appt->end_time = get_ical_end_time (cal, ical, default_zone);
+
+ e_cal_component_id_free (id);
+
+ return appt;
+}
+
+static void
+calendar_appointment_free (gpointer ptr)
+{
+ CalendarAppointment *appt = ptr;
+
+ if (appt)
+ {
+ g_free (appt->id);
+ g_free (appt->summary);
+ g_free (appt);
+ }
+}
+
+static time_t
+timet_from_ical_time (ICalTime *time,
+ ICalTimezone *default_zone)
+{
+ ICalTimezone *timezone = NULL;
+
+ timezone = i_cal_time_get_timezone (time);
+ if (timezone == NULL)
+ timezone = default_zone;
+ return i_cal_time_as_timet_with_zone (time, timezone);
+}
+
+static gboolean
+generate_instances_cb (ICalComponent *icomp,
+ ICalTime *instance_start,
+ ICalTime *instance_end,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CollectAppointmentsData *data = user_data;
+ CalendarAppointment *appointment;
+ ECalComponent *comp;
+ ICalTimezone *default_zone;
+
+ default_zone = e_cal_client_get_default_timezone (data->client);
+ comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
+
+ appointment = calendar_appointment_new (data->client, comp);
+ appointment->start_time = timet_from_ical_time (instance_start, default_zone);
+ appointment->end_time = timet_from_ical_time (instance_end, default_zone);
+
+ *(data->pappointments) = g_slist_prepend (*(data->pappointments), appointment);
+
+ g_clear_object (&comp);
+
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+struct _App
+{
+ GDBusConnection *connection;
+
+ time_t since;
+ time_t until;
+
+ ICalTimezone *zone;
+
+ CalendarSources *sources;
+ gulong client_appeared_signal_id;
+ gulong client_disappeared_signal_id;
+
+ gchar *timezone_location;
+
+ GSList *notify_appointments; /* CalendarAppointment *, for EventsAdded */
+ GSList *notify_ids; /* gchar *, for EventsRemoved */
+
+ GSList *live_views;
+};
+
+static void
+app_update_timezone (App *app)
+{
+ g_autofree char *location = NULL;
+
+ location = e_cal_system_timezone_get_location ();
+ if (g_strcmp0 (location, app->timezone_location) != 0)
+ {
+ if (location == NULL)
+ app->zone = i_cal_timezone_get_utc_timezone ();
+ else
+ app->zone = i_cal_timezone_get_builtin_timezone (location);
+ g_free (app->timezone_location);
+ app->timezone_location = g_steal_pointer (&location);
+ print_debug ("Using timezone %s", app->timezone_location);
+ }
+}
+
+static void
+app_notify_events_added (App *app)
+{
+ GVariantBuilder builder, extras_builder;
+ GSList *events, *link;
+
+ events = g_slist_reverse (app->notify_appointments);
+ app->notify_appointments = NULL;
+
+ print_debug ("Emitting EventsAddedOrUpdated with %d events", g_slist_length (events));
+
+ if (!events)
+ return;
+
+ /* The a{sv} is used as an escape hatch in case we want to provide more
+ * information in the future without breaking ABI
+ */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssxxa{sv})"));
+ for (link = events; link; link = g_slist_next (link))
+ {
+ CalendarAppointment *appt = link->data;
+ time_t start_time = appt->start_time;
+ time_t end_time = appt->end_time;
+
+ if ((start_time >= app->since &&
+ start_time < app->until) ||
+ (start_time <= app->since &&
+ (end_time - 1) > app->since))
+ {
+ g_variant_builder_init (&extras_builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder,
+ "(ssxxa{sv})",
+ appt->id,
+ appt->summary != NULL ? appt->summary : "",
+ (gint64) start_time,
+ (gint64) end_time,
+ &extras_builder);
+ }
+ }
+
+ g_dbus_connection_emit_signal (app->connection,
+ NULL, /* destination_bus_name */
+ "/org/gnome/Shell/CalendarServer",
+ "org.gnome.Shell.CalendarServer",
+ "EventsAddedOrUpdated",
+ g_variant_new ("(a(ssxxa{sv}))", &builder),
+ NULL);
+
+ g_variant_builder_clear (&builder);
+
+ g_slist_free_full (events, calendar_appointment_free);
+}
+
+static void
+app_notify_events_removed (App *app)
+{
+ GVariantBuilder builder;
+ GSList *ids, *link;
+
+ ids = app->notify_ids;
+ app->notify_ids = NULL;
+
+ print_debug ("Emitting EventsRemoved with %d ids", g_slist_length (ids));
+
+ if (!ids)
+ return;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+ for (link = ids; link; link = g_slist_next (link))
+ {
+ const gchar *id = link->data;
+
+ g_variant_builder_add (&builder, "s", id);
+ }
+
+ g_dbus_connection_emit_signal (app->connection,
+ NULL, /* destination_bus_name */
+ "/org/gnome/Shell/CalendarServer",
+ "org.gnome.Shell.CalendarServer",
+ "EventsRemoved",
+ g_variant_new ("(as)", &builder),
+ NULL);
+ g_variant_builder_clear (&builder);
+
+ g_slist_free_full (ids, g_free);
+
+ return;
+}
+
+static void
+app_process_added_modified_objects (App *app,
+ ECalClientView *view,
+ GSList *objects) /* ICalComponent * */
+{
+ ECalClient *cal_client;
+ g_autoptr(GHashTable) covered_uids = NULL;
+ GSList *link;
+ gboolean expand_recurrences;
+
+ cal_client = e_cal_client_view_ref_client (view);
+ covered_uids = g_hash_table_new (g_str_hash, g_str_equal);
+ expand_recurrences = e_cal_client_get_source_type (cal_client) == E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
+
+ for (link = objects; link; link = g_slist_next (link))
+ {
+ ECalComponent *comp;
+ ICalComponent *icomp = link->data;
+ const gchar *uid;
+ gboolean fallback = FALSE;
+
+ if (!icomp)
+ continue;
+
+ uid = i_cal_component_get_uid (icomp);
+ if (!uid || g_hash_table_contains (covered_uids, uid))
+ continue;
+
+ g_hash_table_add (covered_uids, (gpointer) uid);
+
+ if (expand_recurrences &&
+ !e_cal_util_component_is_instance (icomp) &&
+ e_cal_util_component_has_recurrences (icomp))
+ {
+ CollectAppointmentsData data;
+
+ data.client = cal_client;
+ data.pappointments = &app->notify_appointments;
+
+ e_cal_client_generate_instances_for_object_sync (cal_client, icomp, app->since, app->until, NULL,
+ generate_instances_cb, &data);
+ }
+ else if (expand_recurrences &&
+ e_cal_util_component_is_instance (icomp))
+ {
+ ICalComponent *main_comp = NULL;
+
+ /* Always pass whole series of the recurring events, because
+ * the calendar removes events with the same UID first. */
+ if (e_cal_client_get_object_sync (cal_client, uid, NULL, &main_comp, NULL, NULL))
+ {
+ CollectAppointmentsData data;
+
+ data.client = cal_client;
+ data.pappointments = &app->notify_appointments;
+
+ e_cal_client_generate_instances_for_object_sync (cal_client, main_comp, app->since, app->until, NULL,
+ generate_instances_cb, &data);
+
+ g_clear_object (&main_comp);
+ }
+ else
+ {
+ fallback = TRUE;
+ }
+ }
+ else
+ {
+ fallback = TRUE;
+ }
+
+ if (fallback)
+ {
+ comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
+ if (!comp)
+ continue;
+
+ app->notify_appointments = g_slist_prepend (app->notify_appointments,
+ calendar_appointment_new (cal_client, comp));
+ g_object_unref (comp);
+ }
+ }
+
+ g_clear_object (&cal_client);
+
+ if (app->notify_appointments)
+ app_notify_events_added (app);
+}
+
+static void
+on_objects_added (ECalClientView *view,
+ GSList *objects,
+ gpointer user_data)
+{
+ App *app = user_data;
+ ECalClient *client;
+
+ client = e_cal_client_view_ref_client (view);
+ print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (objects), e_source_get_uid (e_client_get_source (E_CLIENT (client))));
+ g_clear_object (&client);
+
+ app_process_added_modified_objects (app, view, objects);
+}
+
+static void
+on_objects_modified (ECalClientView *view,
+ GSList *objects,
+ gpointer user_data)
+{
+ App *app = user_data;
+ ECalClient *client;
+
+ client = e_cal_client_view_ref_client (view);
+ print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (objects), e_source_get_uid (e_client_get_source (E_CLIENT (client))));
+ g_clear_object (&client);
+
+ app_process_added_modified_objects (app, view, objects);
+}
+
+static void
+on_objects_removed (ECalClientView *view,
+ GSList *uids,
+ gpointer user_data)
+{
+ App *app = user_data;
+ ECalClient *client;
+ GSList *link;
+ const gchar *source_uid;
+
+ client = e_cal_client_view_ref_client (view);
+ source_uid = e_source_get_uid (e_client_get_source (E_CLIENT (client)));
+
+ print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (uids), source_uid);
+
+ for (link = uids; link; link = g_slist_next (link))
+ {
+ ECalComponentId *id = link->data;
+
+ if (!id)
+ continue;
+
+ app->notify_ids = g_slist_prepend (app->notify_ids,
+ create_event_id (source_uid,
+ e_cal_component_id_get_uid (id),
+ e_cal_component_id_get_rid (id)));
+ }
+
+ g_clear_object (&client);
+
+ if (app->notify_ids)
+ app_notify_events_removed (app);
+}
+
+static gboolean
+app_has_calendars (App *app)
+{
+ return app->live_views != NULL;
+}
+
+static ECalClientView *
+app_start_view (App *app,
+ ECalClient *cal_client)
+{
+ g_autofree char *since_iso8601 = NULL;
+ g_autofree char *until_iso8601 = NULL;
+ g_autofree char *query = NULL;
+ const gchar *tz_location;
+ ECalClientView *view = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (app->since <= 0 || app->since >= app->until)
+ return NULL;
+
+ if (!app->since || !app->until)
+ {
+ print_debug ("Skipping load of events, no time interval set yet");
+ return NULL;
+ }
+
+ /* timezone could have changed */
+ app_update_timezone (app);
+
+ since_iso8601 = isodate_from_time_t (app->since);
+ until_iso8601 = isodate_from_time_t (app->until);
+ tz_location = i_cal_timezone_get_location (app->zone);
+
+ print_debug ("Loading events since %s until %s for calendar '%s'",
+ since_iso8601,
+ until_iso8601,
+ e_source_get_uid (e_client_get_source (E_CLIENT (cal_client))));
+
+ query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") "
+ "(make-time \"%s\") \"%s\"",
+ since_iso8601,
+ until_iso8601,
+ tz_location);
+
+ e_cal_client_set_default_timezone (cal_client, app->zone);
+
+ if (!e_cal_client_get_view_sync (cal_client, query, &view, NULL /* cancellable */, &error))
+ {
+ g_warning ("Error setting up live-query '%s' on calendar: %s\n", query, error ? error->message : "Unknown error");
+ view = NULL;
+ }
+ else
+ {
+ g_signal_connect (view,
+ "objects-added",
+ G_CALLBACK (on_objects_added),
+ app);
+ g_signal_connect (view,
+ "objects-modified",
+ G_CALLBACK (on_objects_modified),
+ app);
+ g_signal_connect (view,
+ "objects-removed",
+ G_CALLBACK (on_objects_removed),
+ app);
+ e_cal_client_view_start (view, NULL);
+ }
+
+ return view;
+}
+
+static void
+app_stop_view (App *app,
+ ECalClientView *view)
+{
+ e_cal_client_view_stop (view, NULL);
+
+ g_signal_handlers_disconnect_by_func (view, on_objects_added, app);
+ g_signal_handlers_disconnect_by_func (view, on_objects_modified, app);
+ g_signal_handlers_disconnect_by_func (view, on_objects_removed, app);
+}
+
+static void
+app_notify_has_calendars (App *app)
+{
+ GVariantBuilder dict_builder;
+
+ g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&dict_builder, "{sv}", "HasCalendars",
+ g_variant_new_boolean (app_has_calendars (app)));
+
+ g_dbus_connection_emit_signal (app->connection,
+ NULL,
+ "/org/gnome/Shell/CalendarServer",
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ g_variant_new ("(sa{sv}as)",
+ "org.gnome.Shell.CalendarServer",
+ &dict_builder,
+ NULL),
+ NULL);
+ g_variant_builder_clear (&dict_builder);
+}
+
+static void
+app_update_views (App *app)
+{
+ GSList *link, *clients;
+ gboolean had_views, has_views;
+
+ had_views = app->live_views != NULL;
+
+ for (link = app->live_views; link; link = g_slist_next (link))
+ {
+ app_stop_view (app, link->data);
+ }
+
+ g_slist_free_full (app->live_views, g_object_unref);
+ app->live_views = NULL;
+
+ clients = calendar_sources_ref_clients (app->sources);
+
+ for (link = clients; link; link = g_slist_next (link))
+ {
+ ECalClient *cal_client = link->data;
+ ECalClientView *view;
+
+ if (!cal_client)
+ continue;
+
+ view = app_start_view (app, cal_client);
+ if (view)
+ app->live_views = g_slist_prepend (app->live_views, view);
+ }
+
+ has_views = app->live_views != NULL;
+
+ if (has_views != had_views)
+ app_notify_has_calendars (app);
+
+ g_slist_free_full (clients, g_object_unref);
+}
+
+static void
+on_client_appeared_cb (CalendarSources *sources,
+ ECalClient *client,
+ gpointer user_data)
+{
+ App *app = user_data;
+ ECalClientView *view;
+ GSList *link;
+ const gchar *source_uid;
+
+ source_uid = e_source_get_uid (e_client_get_source (E_CLIENT (client)));
+
+ print_debug ("Client appeared '%s'", source_uid);
+
+ for (link = app->live_views; link; link = g_slist_next (link))
+ {
+ ECalClientView *view = link->data;
+ ECalClient *cal_client;
+ ESource *source;
+
+ cal_client = e_cal_client_view_ref_client (view);
+ source = e_client_get_source (E_CLIENT (cal_client));
+
+ if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0)
+ {
+ g_clear_object (&cal_client);
+ return;
+ }
+
+ g_clear_object (&cal_client);
+ }
+
+ view = app_start_view (app, client);
+
+ if (view)
+ {
+ app->live_views = g_slist_prepend (app->live_views, view);
+
+ /* It's the first view, notify that it has calendars now */
+ if (!g_slist_next (app->live_views))
+ app_notify_has_calendars (app);
+ }
+}
+
+static void
+on_client_disappeared_cb (CalendarSources *sources,
+ const gchar *source_uid,
+ gpointer user_data)
+{
+ App *app = user_data;
+ GSList *link;
+
+ print_debug ("Client disappeared '%s'", source_uid);
+
+ for (link = app->live_views; link; link = g_slist_next (link))
+ {
+ ECalClientView *view = link->data;
+ ECalClient *cal_client;
+ ESource *source;
+
+ cal_client = e_cal_client_view_ref_client (view);
+ source = e_client_get_source (E_CLIENT (cal_client));
+
+ if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0)
+ {
+ g_clear_object (&cal_client);
+ app_stop_view (app, view);
+ app->live_views = g_slist_remove (app->live_views, view);
+ g_object_unref (view);
+
+ print_debug ("Emitting ClientDisappeared for '%s'", source_uid);
+
+ g_dbus_connection_emit_signal (app->connection,
+ NULL, /* destination_bus_name */
+ "/org/gnome/Shell/CalendarServer",
+ "org.gnome.Shell.CalendarServer",
+ "ClientDisappeared",
+ g_variant_new ("(s)", source_uid),
+ NULL);
+
+ /* It was the last view, notify that it doesn't have calendars now */
+ if (!app->live_views)
+ app_notify_has_calendars (app);
+
+ break;
+ }
+
+ g_clear_object (&cal_client);
+ }
+}
+
+static App *
+app_new (GDBusConnection *connection)
+{
+ App *app;
+
+ app = g_new0 (App, 1);
+ app->connection = g_object_ref (connection);
+ app->sources = calendar_sources_get ();
+ app->client_appeared_signal_id = g_signal_connect (app->sources,
+ "client-appeared",
+ G_CALLBACK (on_client_appeared_cb),
+ app);
+ app->client_disappeared_signal_id = g_signal_connect (app->sources,
+ "client-disappeared",
+ G_CALLBACK (on_client_disappeared_cb),
+ app);
+
+ app_update_timezone (app);
+
+ return app;
+}
+
+static void
+app_free (App *app)
+{
+ GSList *ll;
+
+ for (ll = app->live_views; ll != NULL; ll = g_slist_next (ll))
+ {
+ ECalClientView *view = E_CAL_CLIENT_VIEW (ll->data);
+
+ app_stop_view (app, view);
+ }
+
+ g_signal_handler_disconnect (app->sources,
+ app->client_appeared_signal_id);
+ g_signal_handler_disconnect (app->sources,
+ app->client_disappeared_signal_id);
+
+ g_free (app->timezone_location);
+
+ g_slist_free_full (app->live_views, g_object_unref);
+ g_slist_free_full (app->notify_appointments, calendar_appointment_free);
+ g_slist_free_full (app->notify_ids, g_free);
+
+ g_object_unref (app->connection);
+ g_object_unref (app->sources);
+
+ g_free (app);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ App *app = user_data;
+
+ if (g_strcmp0 (method_name, "SetTimeRange") == 0)
+ {
+ gint64 since;
+ gint64 until;
+ gboolean force_reload = FALSE;
+ gboolean window_changed = FALSE;
+
+ g_variant_get (parameters,
+ "(xxb)",
+ &since,
+ &until,
+ &force_reload);
+
+ if (until < since)
+ {
+ g_dbus_method_invocation_return_dbus_error (invocation,
+ "org.gnome.Shell.CalendarServer.Error.Failed",
+ "until cannot be before since");
+ goto out;
+ }
+
+ print_debug ("Handling SetTimeRange (since=%" G_GINT64_FORMAT ", until=%" G_GINT64_FORMAT ", force_reload=%s)",
+ since,
+ until,
+ force_reload ? "true" : "false");
+
+ if (app->until != until || app->since != since)
+ {
+ GVariantBuilder *builder;
+ GVariantBuilder *invalidated_builder;
+
+ app->until = until;
+ app->since = since;
+ window_changed = TRUE;
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+ invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+ g_variant_builder_add (builder, "{sv}",
+ "Until", g_variant_new_int64 (app->until));
+ g_variant_builder_add (builder, "{sv}",
+ "Since", g_variant_new_int64 (app->since));
+ g_dbus_connection_emit_signal (app->connection,
+ NULL, /* destination_bus_name */
+ "/org/gnome/Shell/CalendarServer",
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ g_variant_new ("(sa{sv}as)",
+ "org.gnome.Shell.CalendarServer",
+ builder,
+ invalidated_builder),
+ NULL); /* GError** */
+
+ g_variant_builder_unref (builder);
+ g_variant_builder_unref (invalidated_builder);
+ }
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ if (window_changed || force_reload)
+ app_update_views (app);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ out:
+ ;
+}
+
+static GVariant *
+handle_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ App *app = user_data;
+ GVariant *ret;
+
+ ret = NULL;
+ if (g_strcmp0 (property_name, "Since") == 0)
+ {
+ ret = g_variant_new_int64 (app->since);
+ }
+ else if (g_strcmp0 (property_name, "Until") == 0)
+ {
+ ret = g_variant_new_int64 (app->until);
+ }
+ else if (g_strcmp0 (property_name, "HasCalendars") == 0)
+ {
+ ret = g_variant_new_boolean (app_has_calendars (app));
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+ return ret;
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+ handle_method_call,
+ handle_get_property,
+ NULL /* handle_set_property */
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GMainLoop *main_loop = user_data;
+ guint registration_id;
+ g_autoptr (GError) error = NULL;
+
+ _global_app = app_new (connection);
+
+ registration_id = g_dbus_connection_register_object (connection,
+ "/org/gnome/Shell/CalendarServer",
+ introspection_data->interfaces[0],
+ &interface_vtable,
+ _global_app,
+ NULL, /* user_data_free_func */
+ &error);
+ if (registration_id == 0)
+ {
+ g_printerr ("Error exporting object: %s (%s %d)\n",
+ error->message,
+ g_quark_to_string (error->domain),
+ error->code);
+ g_main_loop_quit (main_loop);
+ return;
+ }
+
+ print_debug ("Connected to the session bus");
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GMainLoop *main_loop = user_data;
+
+ g_print ("gnome-shell-calendar-server[%d]: Lost (or failed to acquire) the name " BUS_NAME " - exiting\n",
+ (gint) getpid ());
+ g_main_loop_quit (main_loop);
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ print_debug ("Acquired the name " BUS_NAME);
+}
+
+static gboolean
+stdin_channel_io_func (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ GMainLoop *main_loop = data;
+
+ if (condition & G_IO_HUP)
+ {
+ g_debug ("gnome-shell-calendar-server[%d]: Got HUP on stdin - exiting\n",
+ (gint) getpid ());
+ g_main_loop_quit (main_loop);
+ }
+ else
+ {
+ g_warning ("Unhandled condition %d on GIOChannel for stdin", condition);
+ }
+ return FALSE; /* remove source */
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_autoptr (GError) error = NULL;
+ GOptionContext *opt_context;
+ GMainLoop *main_loop;
+ gint ret;
+ guint name_owner_id;
+ GIOChannel *stdin_channel;
+
+ ret = 1;
+ opt_context = NULL;
+ name_owner_id = 0;
+ stdin_channel = NULL;
+
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (introspection_data != NULL);
+
+ opt_context = g_option_context_new ("gnome-shell calendar server");
+ g_option_context_add_main_entries (opt_context, opt_entries, NULL);
+ if (!g_option_context_parse (opt_context, &argc, &argv, &error))
+ {
+ g_printerr ("Error parsing options: %s\n", error->message);
+ goto out;
+ }
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ stdin_channel = g_io_channel_unix_new (STDIN_FILENO);
+ g_io_add_watch_full (stdin_channel,
+ G_PRIORITY_DEFAULT,
+ G_IO_HUP,
+ stdin_channel_io_func,
+ g_main_loop_ref (main_loop),
+ (GDestroyNotify) g_main_loop_unref);
+
+ name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ BUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+ (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ g_main_loop_ref (main_loop),
+ (GDestroyNotify) g_main_loop_unref);
+
+ g_main_loop_run (main_loop);
+
+ g_main_loop_unref (main_loop);
+
+ ret = 0;
+
+ out:
+ if (stdin_channel != NULL)
+ g_io_channel_unref (stdin_channel);
+ if (_global_app != NULL)
+ app_free (_global_app);
+ if (name_owner_id != 0)
+ g_bus_unown_name (name_owner_id);
+ if (opt_context != NULL)
+ g_option_context_free (opt_context);
+
+ return ret;
+}
diff --git a/src/calendar-server/meson.build b/src/calendar-server/meson.build
new file mode 100644
index 0000000..8b4ef41
--- /dev/null
+++ b/src/calendar-server/meson.build
@@ -0,0 +1,37 @@
+calendar_sources = [
+ 'gnome-shell-calendar-server.c',
+ 'calendar-debug.h',
+ 'calendar-sources.c',
+ 'calendar-sources.h'
+]
+
+calendar_server = executable('gnome-shell-calendar-server', calendar_sources,
+ dependencies: [ecal_dep, eds_dep, gio_dep],
+ include_directories: include_directories('..', '../..'),
+ c_args: [
+ '-DPREFIX="@0@"'.format(prefix),
+ '-DLIBDIR="@0@"'.format(libdir),
+ '-DDATADIR="@0@"'.format(datadir),
+ '-DG_LOG_DOMAIN="ShellCalendarServer"'
+ ],
+ install_dir: libexecdir,
+ install: true
+)
+
+service_file = 'org.gnome.Shell.CalendarServer.service'
+
+configure_file(
+ input: service_file + '.in',
+ output: service_file,
+ configuration: service_data,
+ install_dir: servicedir
+)
+
+i18n.merge_file(
+ input: 'evolution-calendar.desktop.in',
+ output: 'evolution-calendar.desktop',
+ po_dir: po_dir,
+ install: true,
+ install_dir: desktopdir,
+ type: 'desktop'
+)
diff --git a/src/calendar-server/org.gnome.Shell.CalendarServer.service.in b/src/calendar-server/org.gnome.Shell.CalendarServer.service.in
new file mode 100644
index 0000000..5addce6
--- /dev/null
+++ b/src/calendar-server/org.gnome.Shell.CalendarServer.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Shell.CalendarServer
+Exec=@libexecdir@/gnome-shell-calendar-server
diff --git a/src/data-to-c.pl b/src/data-to-c.pl
new file mode 100755
index 0000000..69f7436
--- /dev/null
+++ b/src/data-to-c.pl
@@ -0,0 +1,37 @@
+#!/usr/bin/env perl
+
+# Copyright © 2011 Red Hat, Inc
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the licence, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Kalev Lember <kalevlember@gmail.com>
+
+
+if (@ARGV != 2) {
+ die "Usage: data-to-c.pl <filename> <variable>\n";
+}
+
+$file = $ARGV[0];
+
+open (FILE, $file) || die "Cannot open $file: $!\n";
+
+printf ("const char %s[] = \"", $ARGV[1]);
+while (my $line = <FILE>) {
+ foreach my $c (split //, $line) {
+ printf ("\\x%02x", ord ($c));
+ }
+}
+print "\";\n";
+
+close (FILE);
diff --git a/src/gnome-shell-extension-prefs b/src/gnome-shell-extension-prefs
new file mode 100755
index 0000000..303b196
--- /dev/null
+++ b/src/gnome-shell-extension-prefs
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+openPrefs() {
+ if [ "$(which gnome-extensions)" ]
+ then
+ gnome-extensions prefs $1
+ else
+ gdbus call --session \
+ --dest=org.gnome.Shell.Extensions \
+ --object-path=/org/gnome/Shell/Extensions \
+ --method=org.gnome.Shell.Extensions.OpenExtensionPrefs $1 '' '{}'
+ fi
+}
+
+cat >&2 <<EOT
+gnome-shell-extension-prefs is deprecated
+
+Install https://flathub.org/apps/details/org.gnome.Extensions for extension
+management, or use the gnome-extensions command line tool.
+
+Extensions can use the ExtensionUtils.openPrefs() method.
+EOT
+
+UUID=$1
+
+if [ "$UUID" ]
+then
+ openPrefs $UUID
+else
+ gapplication launch org.gnome.Extensions
+fi
diff --git a/src/gnome-shell-extension-tool.in b/src/gnome-shell-extension-tool.in
new file mode 100755
index 0000000..fb3d0d8
--- /dev/null
+++ b/src/gnome-shell-extension-tool.in
@@ -0,0 +1,59 @@
+#!@PYTHON@
+# -*- mode: Python; indent-tabs-mode: nil; -*-
+
+import subprocess
+import sys
+import optparse
+
+def extension_command(args):
+ print("gnome-shell-extension-tool is deprecated, use gnome-extensions instead",
+ file=sys.stderr)
+ subprocess.run(["@bindir@/gnome-extensions"] + args)
+
+def create_extension():
+ extension_command(["create", "--interactive"])
+
+def enable_extension(uuid):
+ extension_command(["enable", uuid])
+
+def disable_extension(uuid):
+ extension_command(["disable", uuid])
+
+def reload_extension(uuid):
+ print("Reloading extensions does not work correctly and is no longer supported",
+ file=sys.stderr)
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option("-d", "--disable-extension", dest="disable",
+ help="Disable a GNOME Shell extension")
+ parser.add_option("-e", "--enable-extension", dest="enable",
+ help="Enable a GNOME Shell extension")
+ parser.add_option("-c", "--create-extension", dest="create", action="store_true",
+ help="Create a new GNOME Shell extension")
+ parser.add_option("-r", "--reload-extension", dest="reload",
+ help="Reload a GNOME Shell extension")
+ options, args = parser.parse_args()
+
+ if args:
+ parser.print_usage()
+ sys.exit(1)
+
+ if options.disable:
+ disable_extension(options.disable)
+
+ elif options.enable:
+ enable_extension(options.enable)
+
+ elif options.create:
+ create_extension()
+
+ elif options.reload:
+ reload_extension(options.reload)
+
+ else:
+ parser.print_usage()
+ sys.exit(1)
+
+if __name__ == "__main__":
+ main()
diff --git a/src/gnome-shell-perf-tool.in b/src/gnome-shell-perf-tool.in
new file mode 100755
index 0000000..a1b5d59
--- /dev/null
+++ b/src/gnome-shell-perf-tool.in
@@ -0,0 +1,326 @@
+#!@PYTHON@
+# -*- mode: Python; indent-tabs-mode: nil; -*-
+
+import datetime
+from gi.repository import GLib, GObject, Gio
+try:
+ import json
+except ImportError:
+ import simplejson as json
+import optparse
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import base64
+from configparser import RawConfigParser
+import hashlib
+import hmac
+from http import client
+from urllib import parse
+
+def show_version(option, opt_str, value, parser):
+ print("GNOME Shell Performance Test @VERSION@")
+ sys.exit()
+
+def start_shell(perf_output=None):
+ # Set up environment
+ env = dict(os.environ)
+ env['SHELL_PERF_MODULE'] = options.perf
+
+ filters = ['Gnome-shell-perf-helper'] + options.extra_filter
+ env['MUTTER_WM_CLASS_FILTER'] = ','.join(filters)
+
+ if perf_output is not None:
+ env['SHELL_PERF_OUTPUT'] = perf_output
+
+ # A fixed background image
+ env['SHELL_BACKGROUND_IMAGE'] = '@pkgdatadir@/perf-background.xml'
+
+ self_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+ args = []
+ args.append(os.path.join(self_dir, 'gnome-shell'))
+
+ if options.replace:
+ args.append('--replace')
+
+ if options.wayland or options.nested:
+ args.append('--wayland')
+ if options.nested:
+ args.append('--nested')
+ else:
+ args.append('--display-server')
+ elif options.x11:
+ args.append('--x11')
+
+ return subprocess.Popen(args, env=env)
+
+def run_shell(perf_output=None):
+ # we do no additional supervision of gnome-shell,
+ # beyond that of wait
+ # in particular, we don't kill the shell upon
+ # receiving a KeyboardInterrupt, as we expect to be
+ # in the same process group
+ shell = start_shell(perf_output=perf_output)
+ shell.wait()
+ return shell.returncode == 0
+
+def restore_shell():
+ pid = os.fork()
+ if (pid == 0):
+ os.execlp("gnome-shell", "gnome-shell", "--replace")
+ else:
+ sys.exit(0)
+
+def upload_performance_report(report_text):
+ try:
+ config_home = os.environ['XDG_CONFIG_HOME']
+ except KeyError:
+ config_home = None
+
+ if not config_home:
+ config_home = os.path.expanduser("~/.config")
+
+ config_file = os.path.join(config_home, "gnome-shell/perf.ini")
+
+ try:
+ config = RawConfigParser()
+ f = open(config_file)
+ config.readfp(f)
+ f.close()
+
+ base_url = config.get('upload', 'url')
+ system_name = config.get('upload', 'name')
+ secret_key = config.get('upload', 'key')
+ except Exception as e:
+ print("Can't read upload configuration from %s: %s" % (config_file, str(e)))
+ sys.exit(1)
+
+ # Determine host, port and upload URL from provided data, we're
+ # a bit extra-careful about normalization since the URL is part
+ # of the signature.
+
+ split = parse.urlsplit(base_url)
+ scheme = split[0].lower()
+ netloc = split[1]
+ base_path = split[2]
+
+ m = re.match(r'^(.*?)(?::(\d+))?$', netloc)
+ if m.group(2):
+ host, port = m.group(1), int(m.group(2))
+ else:
+ host, port = m.group(1), None
+
+ if scheme != "http":
+ print("'%s' is not a HTTP URL" % base_url)
+ sys.exit(1)
+
+ if port is None:
+ port = 80
+
+ if base_path.endswith('/'):
+ base_path = base_path[:-1]
+
+ if port == 80:
+ normalized_base = "%s://%s%s" % (scheme, host, base_path)
+ else:
+ normalized_base = "%s://%s:%d%s" % (scheme, host, port, base_path)
+
+ upload_url = normalized_base + '/system/%s/upload' % system_name
+ upload_path = parse.urlsplit(upload_url)[2] # path portion
+
+ # Create signature based on upload URL and the report data
+
+ signature_data = 'POST&' + upload_url + "&&"
+ h = hmac.new(secret_key, digestmod=hashlib.sha1)
+ h.update(signature_data)
+ h.update(report_text)
+ signature = parse.quote(base64.b64encode(h.digest()), "~")
+
+ headers = {
+ 'User-Agent': 'gnome-shell-performance-tool/@VERSION@',
+ 'Content-Type': 'application/json',
+ 'X-Shell-Signature': 'HMAC-SHA1 ' + signature
+ };
+
+ connection = client.HTTPConnection(host, port)
+ connection.request('POST', upload_path, report_text, headers)
+ response = connection.getresponse()
+
+ if response.status == 200:
+ print("Performance report upload succeeded")
+ else:
+ print("Performance report upload failed with status %d" % response.status)
+ print(response.read())
+
+def gnome_hwtest_log(*args):
+ command = ['gnome-hwtest-log', '-t', 'gnome-shell-perf-tool']
+ command.extend(args)
+ subprocess.check_call(command)
+
+def run_performance_test():
+ iters = options.perf_iters
+ if options.perf_warmup:
+ iters += 1
+
+ logs = []
+ metric_summaries = {}
+
+ for i in range(0, iters):
+ # We create an empty temporary file that the shell will overwrite
+ # with the contents.
+ handle, output_file = tempfile.mkstemp(".json", "gnome-shell-perf.")
+ os.close(handle)
+
+ # Run the performance test and collect the output as JSON
+ normal_exit = False
+ try:
+ normal_exit = run_shell(perf_output=output_file)
+ except:
+ raise
+ finally:
+ if not normal_exit:
+ os.remove(output_file)
+
+ if not normal_exit:
+ return False
+
+ try:
+ f = open(output_file)
+ output = json.load(f)
+ f.close()
+ except:
+ raise
+ finally:
+ os.remove(output_file)
+
+ # Grab the event definitions and monitor layout the first time around
+ if i == 0:
+ events = output['events']
+ monitors = output['monitors']
+
+ if options.perf_warmup and i == 0:
+ continue
+
+ for metric in output['metrics']:
+ name = metric['name']
+ if not name in metric_summaries:
+ summary = {}
+ summary['description'] = metric['description']
+ summary['units'] = metric['units']
+ summary['values'] = []
+ metric_summaries[name] = summary
+ else:
+ summary = metric_summaries[name]
+
+ summary['values'].append(metric['value'])
+
+ logs.append(output['log'])
+
+ if options.perf_output or options.perf_upload:
+ # Write a complete report, formatted as JSON. The Javascript/C code that
+ # generates the individual reports we are summarizing here is very careful
+ # to format them nicely, but we just dump out a compressed no-whitespace
+ # version here for simplicity. Using json.dump(indent=0) doesn't real
+ # improve the readability of the output much.
+ report = {
+ 'date': datetime.datetime.utcnow().isoformat() + 'Z',
+ 'events': events,
+ 'monitors': monitors,
+ 'metrics': metric_summaries,
+ 'logs': logs
+ }
+
+ # Add the Git revision if available
+ self_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+ if os.path.exists(os.path.join(self_dir, 'gnome-shell-jhbuild.in')):
+ top_dir = os.path.dirname(self_dir)
+ git_dir = os.path.join(top_dir, '.git')
+ if os.path.exists(git_dir):
+ env = dict(os.environ)
+ env['GIT_DIR'] = git_dir
+ revision = subprocess.Popen(['git', 'rev-parse', 'HEAD'],
+ env=env,
+ stdout=subprocess.PIPE).communicate()[0].strip()
+ report['revision'] = revision
+
+ if options.perf_output:
+ f = open(options.perf_output, 'w')
+ json.dump(report, f)
+ f.close()
+
+ if options.perf_upload:
+ upload_performance_report(json.dumps(report))
+ elif options.hwtest:
+ # Log to systemd journal
+ for metric in sorted(metric_summaries.keys()):
+ summary = metric_summaries[metric]
+ gnome_hwtest_log('--metric=' + metric + '=' + str(summary['values'][0]) + summary['units'],
+ '--metric-description=' + summary['description'])
+ gnome_hwtest_log('--finished')
+ else:
+ # Write a human readable summary
+ print('------------------------------------------------------------')
+ for metric in sorted(metric_summaries.keys()):
+ summary = metric_summaries[metric]
+ print("#", summary['description'])
+ print(metric, ", ".join((str(x) for x in summary['values'])))
+ print('------------------------------------------------------------')
+
+ return True
+
+# Main program
+
+parser = optparse.OptionParser()
+parser.add_option("", "--perf", metavar="PERF_MODULE",
+ help="Specify the name of a performance module to run")
+parser.add_option("", "--perf-iters", type="int", metavar="ITERS",
+ help="Numbers of iterations of performance module to run",
+ default=1)
+parser.add_option("", "--perf-warmup", action="store_true",
+ help="Run a dry run before performance tests")
+parser.add_option("", "--perf-output", metavar="OUTPUT_FILE",
+ help="Output file to write performance report")
+parser.add_option("", "--perf-upload", action="store_true",
+ help="Upload performance report to server")
+parser.add_option("", "--extra-filter", action="append",
+ help="add an extra window class that should be allowed")
+parser.add_option("", "--hwtest", action="store_true",
+ help="Log results appropriately for GNOME Hardware Testing")
+parser.add_option("", "--version", action="callback", callback=show_version,
+ help="Display version and exit")
+
+parser.add_option("-r", "--replace", action="store_true",
+ help="Replace the running window manager")
+parser.add_option("-w", "--wayland", action="store_true",
+ help="Run as a Wayland compositor")
+parser.add_option("-n", "--nested", action="store_true",
+ help="Run as a Wayland nested compositor")
+parser.add_option("-x", "--x11", action="store_true",
+ help="Run as an X11 compositor")
+
+options, args = parser.parse_args()
+
+if options.perf == None:
+ if options.hwtest:
+ options.perf = 'hwtest'
+ else:
+ options.perf = 'core'
+
+if options.extra_filter is None:
+ options.extra_filter = []
+
+if options.perf == 'hwtest':
+ options.extra_filter.append('Gedit')
+
+if args:
+ parser.print_usage()
+ sys.exit(1)
+
+normal_exit = run_performance_test()
+if normal_exit:
+ if not options.hwtest:
+ restore_shell()
+else:
+ sys.exit(1)
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
new file mode 100644
index 0000000..5364f04
--- /dev/null
+++ b/src/gnome-shell-plugin.c
@@ -0,0 +1,394 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (c) 2008 Red Hat, Inc.
+ * Copyright (c) 2008 Intel Corp.
+ *
+ * Based on plugin skeleton by:
+ * Author: Tomas Frydrych <tf@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * GnomeShellPlugin is the entry point for for GNOME Shell into and out of
+ * Mutter. By registering itself into Mutter using
+ * meta_plugin_manager_set_plugin_type(), Mutter will call the vfuncs of the
+ * plugin at the appropriate time.
+ *
+ * The functions in in GnomeShellPlugin are all just stubs, which just call the
+ * similar methods in GnomeShellWm.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <clutter/clutter.h>
+#include <gjs/gjs.h>
+#include <meta/display.h>
+#include <meta/meta-plugin.h>
+#include <meta/meta-x11-display.h>
+#include <meta/util.h>
+
+#include "shell-global-private.h"
+#include "shell-perf-log.h"
+#include "shell-wm-private.h"
+
+#define GNOME_TYPE_SHELL_PLUGIN (gnome_shell_plugin_get_type ())
+G_DECLARE_FINAL_TYPE (GnomeShellPlugin, gnome_shell_plugin,
+ GNOME, SHELL_PLUGIN,
+ MetaPlugin)
+
+struct _GnomeShellPlugin
+{
+ MetaPlugin parent;
+
+ int glx_error_base;
+ int glx_event_base;
+ guint have_swap_event : 1;
+ CoglContext *cogl_context;
+
+ ShellGlobal *global;
+};
+
+G_DEFINE_TYPE (GnomeShellPlugin, gnome_shell_plugin, META_TYPE_PLUGIN)
+
+static gboolean
+gnome_shell_plugin_has_swap_event (GnomeShellPlugin *shell_plugin)
+{
+ CoglDisplay *cogl_display =
+ cogl_context_get_display (shell_plugin->cogl_context);
+ CoglRenderer *renderer = cogl_display_get_renderer (cogl_display);
+ const char * (* query_extensions_string) (Display *dpy, int screen);
+ Bool (* query_extension) (Display *dpy, int *error, int *event);
+ MetaDisplay *display = meta_plugin_get_display (META_PLUGIN (shell_plugin));
+ MetaX11Display *x11_display = meta_display_get_x11_display (display);
+ Display *xdisplay;
+ int screen_number;
+ const char *glx_extensions;
+
+ /* We will only get swap events if Cogl is using GLX */
+ if (cogl_renderer_get_winsys_id (renderer) != COGL_WINSYS_ID_GLX)
+ return FALSE;
+
+ xdisplay = meta_x11_display_get_xdisplay (x11_display);
+
+ query_extensions_string =
+ (void *) cogl_get_proc_address ("glXQueryExtensionsString");
+ query_extension =
+ (void *) cogl_get_proc_address ("glXQueryExtension");
+
+ query_extension (xdisplay,
+ &shell_plugin->glx_error_base,
+ &shell_plugin->glx_event_base);
+
+ screen_number = XDefaultScreen (xdisplay);
+ glx_extensions = query_extensions_string (xdisplay, screen_number);
+
+ return strstr (glx_extensions, "GLX_INTEL_swap_event") != NULL;
+}
+
+static void
+gnome_shell_plugin_start (MetaPlugin *plugin)
+{
+ GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
+ GError *error = NULL;
+ uint8_t status;
+ GjsContext *gjs_context;
+ ClutterBackend *backend;
+
+ backend = clutter_get_default_backend ();
+ shell_plugin->cogl_context = clutter_backend_get_cogl_context (backend);
+
+ shell_plugin->have_swap_event =
+ gnome_shell_plugin_has_swap_event (shell_plugin);
+
+ shell_perf_log_define_event (shell_perf_log_get_default (),
+ "glx.swapComplete",
+ "GL buffer swap complete event received (with timestamp of completion)",
+ "x");
+
+ shell_plugin->global = shell_global_get ();
+ _shell_global_set_plugin (shell_plugin->global, META_PLUGIN (shell_plugin));
+
+ gjs_context = _shell_global_get_gjs_context (shell_plugin->global);
+
+ if (!gjs_context_eval_module_file (gjs_context,
+ "resource:///org/gnome/shell/ui/init.js",
+ &status,
+ &error))
+ {
+ g_message ("Execution of main.js threw exception: %s", error->message);
+ g_error_free (error);
+ /* We just exit() here, since in a development environment you'll get the
+ * error in your shell output, and it's way better than a busted WM,
+ * which typically manifests as a white screen.
+ *
+ * In production, we shouldn't crash =) But if we do, we should get
+ * restarted by the session infrastructure, which is likely going
+ * to be better than some undefined state.
+ *
+ * If there was a generic "hook into bug-buddy for non-C crashes"
+ * infrastructure, here would be the place to put it.
+ */
+ g_object_unref (gjs_context);
+ exit (1);
+ }
+}
+
+static ShellWM *
+get_shell_wm (void)
+{
+ ShellWM *wm;
+
+ g_object_get (shell_global_get (),
+ "window-manager", &wm,
+ NULL);
+ /* drop extra ref added by g_object_get */
+ g_object_unref (wm);
+
+ return wm;
+}
+
+static void
+gnome_shell_plugin_minimize (MetaPlugin *plugin,
+ MetaWindowActor *actor)
+{
+ _shell_wm_minimize (get_shell_wm (),
+ actor);
+
+}
+
+static void
+gnome_shell_plugin_unminimize (MetaPlugin *plugin,
+ MetaWindowActor *actor)
+{
+ _shell_wm_unminimize (get_shell_wm (),
+ actor);
+
+}
+
+static void
+gnome_shell_plugin_size_changed (MetaPlugin *plugin,
+ MetaWindowActor *actor)
+{
+ _shell_wm_size_changed (get_shell_wm (), actor);
+}
+
+static void
+gnome_shell_plugin_size_change (MetaPlugin *plugin,
+ MetaWindowActor *actor,
+ MetaSizeChange which_change,
+ MetaRectangle *old_frame_rect,
+ MetaRectangle *old_buffer_rect)
+{
+ _shell_wm_size_change (get_shell_wm (), actor, which_change, old_frame_rect, old_buffer_rect);
+}
+
+static void
+gnome_shell_plugin_map (MetaPlugin *plugin,
+ MetaWindowActor *actor)
+{
+ _shell_wm_map (get_shell_wm (),
+ actor);
+}
+
+static void
+gnome_shell_plugin_destroy (MetaPlugin *plugin,
+ MetaWindowActor *actor)
+{
+ _shell_wm_destroy (get_shell_wm (),
+ actor);
+}
+
+static void
+gnome_shell_plugin_switch_workspace (MetaPlugin *plugin,
+ gint from,
+ gint to,
+ MetaMotionDirection direction)
+{
+ _shell_wm_switch_workspace (get_shell_wm(), from, to, direction);
+}
+
+static void
+gnome_shell_plugin_kill_window_effects (MetaPlugin *plugin,
+ MetaWindowActor *actor)
+{
+ _shell_wm_kill_window_effects (get_shell_wm(), actor);
+}
+
+static void
+gnome_shell_plugin_kill_switch_workspace (MetaPlugin *plugin)
+{
+ _shell_wm_kill_switch_workspace (get_shell_wm());
+}
+
+static void
+gnome_shell_plugin_show_tile_preview (MetaPlugin *plugin,
+ MetaWindow *window,
+ MetaRectangle *tile_rect,
+ int tile_monitor)
+{
+ _shell_wm_show_tile_preview (get_shell_wm (), window, tile_rect, tile_monitor);
+}
+
+static void
+gnome_shell_plugin_hide_tile_preview (MetaPlugin *plugin)
+{
+ _shell_wm_hide_tile_preview (get_shell_wm ());
+}
+
+static void
+gnome_shell_plugin_show_window_menu (MetaPlugin *plugin,
+ MetaWindow *window,
+ MetaWindowMenuType menu,
+ int x,
+ int y)
+{
+ _shell_wm_show_window_menu (get_shell_wm (), window, menu, x, y);
+}
+
+static void
+gnome_shell_plugin_show_window_menu_for_rect (MetaPlugin *plugin,
+ MetaWindow *window,
+ MetaWindowMenuType menu,
+ MetaRectangle *rect)
+{
+ _shell_wm_show_window_menu_for_rect (get_shell_wm (), window, menu, rect);
+}
+
+static gboolean
+gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
+ XEvent *xev)
+{
+#ifdef GLX_INTEL_swap_event
+ GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
+
+ if (shell_plugin->have_swap_event &&
+ xev->type == (shell_plugin->glx_event_base + GLX_BufferSwapComplete))
+ {
+ GLXBufferSwapComplete *swap_complete_event;
+ swap_complete_event = (GLXBufferSwapComplete *)xev;
+
+ /* Buggy early versions of the INTEL_swap_event implementation in Mesa
+ * can send this with a ust of 0. Simplify life for consumers
+ * by ignoring such events */
+ if (swap_complete_event->ust != 0)
+ {
+ gboolean frame_timestamps;
+ g_object_get (shell_plugin->global,
+ "frame-timestamps", &frame_timestamps,
+ NULL);
+
+ if (frame_timestamps)
+ shell_perf_log_event_x (shell_perf_log_get_default (),
+ "glx.swapComplete",
+ swap_complete_event->ust);
+ }
+ }
+#endif
+
+ return FALSE;
+}
+
+static gboolean
+gnome_shell_plugin_keybinding_filter (MetaPlugin *plugin,
+ MetaKeyBinding *binding)
+{
+ return _shell_wm_filter_keybinding (get_shell_wm (), binding);
+}
+
+static void
+gnome_shell_plugin_confirm_display_change (MetaPlugin *plugin)
+{
+ _shell_wm_confirm_display_change (get_shell_wm ());
+}
+
+static const MetaPluginInfo *
+gnome_shell_plugin_plugin_info (MetaPlugin *plugin)
+{
+ static const MetaPluginInfo info = {
+ .name = "GNOME Shell",
+ .version = "0.1",
+ .author = "Various",
+ .license = "GPLv2+",
+ .description = "Provides GNOME Shell core functionality"
+ };
+
+ return &info;
+}
+
+static MetaCloseDialog *
+gnome_shell_plugin_create_close_dialog (MetaPlugin *plugin,
+ MetaWindow *window)
+{
+ return _shell_wm_create_close_dialog (get_shell_wm (), window);
+}
+
+static MetaInhibitShortcutsDialog *
+gnome_shell_plugin_create_inhibit_shortcuts_dialog (MetaPlugin *plugin,
+ MetaWindow *window)
+{
+ return _shell_wm_create_inhibit_shortcuts_dialog (get_shell_wm (), window);
+}
+
+static void
+gnome_shell_plugin_locate_pointer (MetaPlugin *plugin)
+{
+ GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
+ _shell_global_locate_pointer (shell_plugin->global);
+}
+
+static void
+gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
+{
+ MetaPluginClass *plugin_class = META_PLUGIN_CLASS (klass);
+
+ plugin_class->start = gnome_shell_plugin_start;
+ plugin_class->map = gnome_shell_plugin_map;
+ plugin_class->minimize = gnome_shell_plugin_minimize;
+ plugin_class->unminimize = gnome_shell_plugin_unminimize;
+ plugin_class->size_changed = gnome_shell_plugin_size_changed;
+ plugin_class->size_change = gnome_shell_plugin_size_change;
+ plugin_class->destroy = gnome_shell_plugin_destroy;
+
+ plugin_class->switch_workspace = gnome_shell_plugin_switch_workspace;
+
+ plugin_class->kill_window_effects = gnome_shell_plugin_kill_window_effects;
+ plugin_class->kill_switch_workspace = gnome_shell_plugin_kill_switch_workspace;
+
+ plugin_class->show_tile_preview = gnome_shell_plugin_show_tile_preview;
+ plugin_class->hide_tile_preview = gnome_shell_plugin_hide_tile_preview;
+ plugin_class->show_window_menu = gnome_shell_plugin_show_window_menu;
+ plugin_class->show_window_menu_for_rect = gnome_shell_plugin_show_window_menu_for_rect;
+
+ plugin_class->xevent_filter = gnome_shell_plugin_xevent_filter;
+ plugin_class->keybinding_filter = gnome_shell_plugin_keybinding_filter;
+
+ plugin_class->confirm_display_change = gnome_shell_plugin_confirm_display_change;
+
+ plugin_class->plugin_info = gnome_shell_plugin_plugin_info;
+
+ plugin_class->create_close_dialog = gnome_shell_plugin_create_close_dialog;
+ plugin_class->create_inhibit_shortcuts_dialog = gnome_shell_plugin_create_inhibit_shortcuts_dialog;
+
+ plugin_class->locate_pointer = gnome_shell_plugin_locate_pointer;
+}
+
+static void
+gnome_shell_plugin_init (GnomeShellPlugin *shell_plugin)
+{
+}
diff --git a/src/gnome-shell-portal-helper.c b/src/gnome-shell-portal-helper.c
new file mode 100644
index 0000000..a0bebb2
--- /dev/null
+++ b/src/gnome-shell-portal-helper.c
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <gjs/gjs.h>
+#include <glib/gi18n.h>
+
+int
+main (int argc, char *argv[])
+{
+ const char *search_path[] = { "resource:///org/gnome/shell", NULL };
+ GError *error = NULL;
+ GjsContext *context;
+ int status;
+
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ context = g_object_new (GJS_TYPE_CONTEXT,
+ "search-path", search_path,
+ NULL);
+
+ if (!gjs_context_define_string_array(context, "ARGV",
+ argc, (const char**)argv,
+ &error))
+ {
+ g_message("Failed to define ARGV: %s", error->message);
+ g_error_free (error);
+ g_object_unref (context);
+
+ return 1;
+ }
+
+
+ if (!gjs_context_eval (context,
+ "const Main = imports.portalHelper.main; Main.main(ARGV);",
+ -1,
+ "<main>",
+ &status,
+ &error))
+ {
+ g_message ("Execution of main.js threw exception: %s", error->message);
+ g_error_free (error);
+ g_object_unref (context);
+
+ return status;
+ }
+
+ g_object_unref (context);
+ return 0;
+}
diff --git a/src/gtkactionmuxer.c b/src/gtkactionmuxer.c
new file mode 100644
index 0000000..7e3e86e
--- /dev/null
+++ b/src/gtkactionmuxer.c
@@ -0,0 +1,945 @@
+/*
+ * Copyright © 2011 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "config.h"
+
+#include "gtkactionmuxer.h"
+
+#include "gtkactionobservable.h"
+#include "gtkactionobserver.h"
+
+#include <clutter/clutter.h>
+
+#include <string.h>
+
+/**
+ * SECTION:gtkactionmuxer
+ * @short_description: Aggregate and monitor several action groups
+ *
+ * #GtkActionMuxer is a #GActionGroup and #GtkActionObservable that is
+ * capable of containing other #GActionGroup instances.
+ *
+ * The typical use is aggregating all of the actions applicable to a
+ * particular context into a single action group, with namespacing.
+ *
+ * Consider the case of two action groups -- one containing actions
+ * applicable to an entire application (such as 'quit') and one
+ * containing actions applicable to a particular window in the
+ * application (such as 'fullscreen').
+ *
+ * In this case, each of these action groups could be added to a
+ * #GtkActionMuxer with the prefixes "app" and "win", respectively. This
+ * would expose the actions as "app.quit" and "win.fullscreen" on the
+ * #GActionGroup interface presented by the #GtkActionMuxer.
+ *
+ * Activations and state change requests on the #GtkActionMuxer are wired
+ * through to the underlying action group in the expected way.
+ *
+ * This class is typically only used at the site of "consumption" of
+ * actions (eg: when displaying a menu that contains many actions on
+ * different objects).
+ */
+
+static void gtk_action_muxer_group_iface_init (GActionGroupInterface *iface);
+static void gtk_action_muxer_observable_iface_init (GtkActionObservableInterface *iface);
+
+typedef GObjectClass GtkActionMuxerClass;
+
+struct _GtkActionMuxer
+{
+ GObject parent_instance;
+
+ GHashTable *observed_actions;
+ GHashTable *groups;
+ GHashTable *primary_accels;
+ GtkActionMuxer *parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GtkActionMuxer, gtk_action_muxer, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_action_muxer_group_iface_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTION_OBSERVABLE, gtk_action_muxer_observable_iface_init))
+
+enum
+{
+ PROP_0,
+ PROP_PARENT,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+guint accel_signal;
+
+typedef struct
+{
+ GtkActionMuxer *muxer;
+ GSList *watchers;
+ gchar *fullname;
+} Action;
+
+typedef struct
+{
+ GtkActionMuxer *muxer;
+ GActionGroup *group;
+ gchar *prefix;
+ gulong handler_ids[4];
+} Group;
+
+static void
+gtk_action_muxer_append_group_actions (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ const gchar *prefix = key;
+ Group *group = value;
+ GArray *actions = user_data;
+ gchar **group_actions;
+ gchar **action;
+
+ group_actions = g_action_group_list_actions (group->group);
+ for (action = group_actions; *action; action++)
+ {
+ gchar *fullname;
+
+ fullname = g_strconcat (prefix, ".", *action, NULL);
+ g_array_append_val (actions, fullname);
+ }
+
+ g_strfreev (group_actions);
+}
+
+static gchar **
+gtk_action_muxer_list_actions (GActionGroup *action_group)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
+ GArray *actions;
+
+ actions = g_array_new (TRUE, FALSE, sizeof (gchar *));
+
+ for ( ; muxer != NULL; muxer = muxer->parent)
+ {
+ g_hash_table_foreach (muxer->groups,
+ gtk_action_muxer_append_group_actions,
+ actions);
+ }
+
+ return (gchar **)(void *) g_array_free (actions, FALSE);
+}
+
+static Group *
+gtk_action_muxer_find_group (GtkActionMuxer *muxer,
+ const gchar *full_name,
+ const gchar **action_name)
+{
+ const gchar *dot;
+ gchar *prefix;
+ Group *group;
+
+ dot = strchr (full_name, '.');
+
+ if (!dot)
+ return NULL;
+
+ prefix = g_strndup (full_name, dot - full_name);
+ group = g_hash_table_lookup (muxer->groups, prefix);
+ g_free (prefix);
+
+ if (action_name)
+ *action_name = dot + 1;
+
+ return group;
+}
+
+static void
+gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer,
+ const gchar *action_name,
+ gboolean enabled)
+{
+ Action *action;
+ GSList *node;
+
+ action = g_hash_table_lookup (muxer->observed_actions, action_name);
+ for (node = action ? action->watchers : NULL; node; node = node->next)
+ gtk_action_observer_action_enabled_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, enabled);
+ g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), action_name, enabled);
+}
+
+static void
+gtk_action_muxer_group_action_enabled_changed (GActionGroup *action_group,
+ const gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ Group *group = user_data;
+ gchar *fullname;
+
+ fullname = g_strconcat (group->prefix, ".", action_name, NULL);
+ gtk_action_muxer_action_enabled_changed (group->muxer, fullname, enabled);
+
+ g_free (fullname);
+}
+
+static void
+gtk_action_muxer_parent_action_enabled_changed (GActionGroup *action_group,
+ const gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ GtkActionMuxer *muxer = user_data;
+
+ gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled);
+}
+
+static void
+gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer,
+ const gchar *action_name,
+ GVariant *state)
+{
+ Action *action;
+ GSList *node;
+
+ action = g_hash_table_lookup (muxer->observed_actions, action_name);
+ for (node = action ? action->watchers : NULL; node; node = node->next)
+ gtk_action_observer_action_state_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, state);
+ g_action_group_action_state_changed (G_ACTION_GROUP (muxer), action_name, state);
+}
+
+static void
+gtk_action_muxer_group_action_state_changed (GActionGroup *action_group,
+ const gchar *action_name,
+ GVariant *state,
+ gpointer user_data)
+{
+ Group *group = user_data;
+ gchar *fullname;
+
+ fullname = g_strconcat (group->prefix, ".", action_name, NULL);
+ gtk_action_muxer_action_state_changed (group->muxer, fullname, state);
+
+ g_free (fullname);
+}
+
+static void
+gtk_action_muxer_parent_action_state_changed (GActionGroup *action_group,
+ const gchar *action_name,
+ GVariant *state,
+ gpointer user_data)
+{
+ GtkActionMuxer *muxer = user_data;
+
+ gtk_action_muxer_action_state_changed (muxer, action_name, state);
+}
+
+static void
+gtk_action_muxer_action_added (GtkActionMuxer *muxer,
+ const gchar *action_name,
+ GActionGroup *original_group,
+ const gchar *orignal_action_name)
+{
+ const GVariantType *parameter_type;
+ gboolean enabled;
+ GVariant *state;
+ Action *action;
+
+ action = g_hash_table_lookup (muxer->observed_actions, action_name);
+
+ if (action && action->watchers &&
+ g_action_group_query_action (original_group, orignal_action_name,
+ &enabled, &parameter_type, NULL, NULL, &state))
+ {
+ GSList *node;
+
+ for (node = action->watchers; node; node = node->next)
+ gtk_action_observer_action_added (node->data,
+ GTK_ACTION_OBSERVABLE (muxer),
+ action_name, parameter_type, enabled, state);
+
+ if (state)
+ g_variant_unref (state);
+ }
+
+ g_action_group_action_added (G_ACTION_GROUP (muxer), action_name);
+}
+
+static void
+gtk_action_muxer_action_added_to_group (GActionGroup *action_group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ Group *group = user_data;
+ gchar *fullname;
+
+ fullname = g_strconcat (group->prefix, ".", action_name, NULL);
+ gtk_action_muxer_action_added (group->muxer, fullname, action_group, action_name);
+
+ g_free (fullname);
+}
+
+static void
+gtk_action_muxer_action_added_to_parent (GActionGroup *action_group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ GtkActionMuxer *muxer = user_data;
+
+ gtk_action_muxer_action_added (muxer, action_name, action_group, action_name);
+}
+
+static void
+gtk_action_muxer_action_removed (GtkActionMuxer *muxer,
+ const gchar *action_name)
+{
+ Action *action;
+ GSList *node;
+
+ action = g_hash_table_lookup (muxer->observed_actions, action_name);
+ for (node = action ? action->watchers : NULL; node; node = node->next)
+ gtk_action_observer_action_removed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name);
+ g_action_group_action_removed (G_ACTION_GROUP (muxer), action_name);
+}
+
+static void
+gtk_action_muxer_action_removed_from_group (GActionGroup *action_group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ Group *group = user_data;
+ gchar *fullname;
+
+ fullname = g_strconcat (group->prefix, ".", action_name, NULL);
+ gtk_action_muxer_action_removed (group->muxer, fullname);
+
+ g_free (fullname);
+}
+
+static void
+gtk_action_muxer_action_removed_from_parent (GActionGroup *action_group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ GtkActionMuxer *muxer = user_data;
+
+ gtk_action_muxer_action_removed (muxer, action_name);
+}
+
+static void
+gtk_action_muxer_primary_accel_changed (GtkActionMuxer *muxer,
+ const gchar *action_name,
+ const gchar *action_and_target)
+{
+ Action *action;
+ GSList *node;
+
+ if (!action_name)
+ action_name = strrchr (action_and_target, '|') + 1;
+
+ action = g_hash_table_lookup (muxer->observed_actions, action_name);
+ for (node = action ? action->watchers : NULL; node; node = node->next)
+ gtk_action_observer_primary_accel_changed (node->data, GTK_ACTION_OBSERVABLE (muxer),
+ action_name, action_and_target);
+ g_signal_emit (muxer, accel_signal, 0, action_name, action_and_target);
+}
+
+static void
+gtk_action_muxer_parent_primary_accel_changed (GtkActionMuxer *parent,
+ const gchar *action_name,
+ const gchar *action_and_target,
+ gpointer user_data)
+{
+ GtkActionMuxer *muxer = user_data;
+
+ /* If it's in our table then don't let the parent one filter through */
+ if (muxer->primary_accels && g_hash_table_lookup (muxer->primary_accels, action_and_target))
+ return;
+
+ gtk_action_muxer_primary_accel_changed (muxer, action_name, action_and_target);
+}
+
+static gboolean
+gtk_action_muxer_query_action (GActionGroup *action_group,
+ const gchar *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
+ Group *group;
+ const gchar *unprefixed_name;
+
+ group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
+
+ if (group)
+ return g_action_group_query_action (group->group, unprefixed_name, enabled,
+ parameter_type, state_type, state_hint, state);
+
+ if (muxer->parent)
+ return g_action_group_query_action (G_ACTION_GROUP (muxer->parent), action_name,
+ enabled, parameter_type,
+ state_type, state_hint, state);
+
+ return FALSE;
+}
+
+static GVariant *
+get_platform_data (void)
+{
+ gchar time[32];
+ GVariantBuilder *builder;
+ GVariant *result;
+
+ g_snprintf (time, 32, "_TIME%d", clutter_get_current_event_time ());
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+
+ g_variant_builder_add (builder, "{sv}", "desktop-startup-id",
+ g_variant_new_string (time));
+
+ result = g_variant_builder_end (builder);
+ g_variant_builder_unref (builder);
+
+ return result;
+}
+
+static void
+gtk_action_muxer_activate_action (GActionGroup *action_group,
+ const gchar *action_name,
+ GVariant *parameter)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
+ Group *group;
+ const gchar *unprefixed_name;
+
+ group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
+
+ if (group)
+ {
+ if (G_IS_REMOTE_ACTION_GROUP (group->group))
+ g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group->group),
+ unprefixed_name, parameter,
+ get_platform_data ());
+ else
+ g_action_group_activate_action (group->group, unprefixed_name, parameter);
+ }
+ else if (muxer->parent)
+ g_action_group_activate_action (G_ACTION_GROUP (muxer->parent), action_name, parameter);
+}
+
+static void
+gtk_action_muxer_change_action_state (GActionGroup *action_group,
+ const gchar *action_name,
+ GVariant *state)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
+ Group *group;
+ const gchar *unprefixed_name;
+
+ group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
+
+ if (group)
+ {
+ if (G_IS_REMOTE_ACTION_GROUP (group->group))
+ g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group->group),
+ unprefixed_name,
+ state,
+ get_platform_data ());
+ else
+ g_action_group_change_action_state (group->group, unprefixed_name, state);
+ }
+ else if (muxer->parent)
+ g_action_group_change_action_state (G_ACTION_GROUP (muxer->parent), action_name, state);
+}
+
+static void
+gtk_action_muxer_unregister_internal (Action *action,
+ gpointer observer)
+{
+ GtkActionMuxer *muxer = action->muxer;
+ GSList **ptr;
+
+ for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next)
+ if ((*ptr)->data == observer)
+ {
+ *ptr = g_slist_remove (*ptr, observer);
+
+ if (action->watchers == NULL)
+ g_hash_table_remove (muxer->observed_actions, action->fullname);
+
+ break;
+ }
+}
+
+static void
+gtk_action_muxer_weak_notify (gpointer data,
+ GObject *where_the_object_was)
+{
+ Action *action = data;
+
+ gtk_action_muxer_unregister_internal (action, where_the_object_was);
+}
+
+static void
+gtk_action_muxer_register_observer (GtkActionObservable *observable,
+ const gchar *name,
+ GtkActionObserver *observer)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable);
+ Action *action;
+
+ action = g_hash_table_lookup (muxer->observed_actions, name);
+
+ if (action == NULL)
+ {
+ action = g_new (Action, 1);
+ action->muxer = muxer;
+ action->fullname = g_strdup (name);
+ action->watchers = NULL;
+
+ g_hash_table_insert (muxer->observed_actions, action->fullname, action);
+ }
+
+ action->watchers = g_slist_prepend (action->watchers, observer);
+ g_object_weak_ref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action);
+}
+
+static void
+gtk_action_muxer_unregister_observer (GtkActionObservable *observable,
+ const gchar *name,
+ GtkActionObserver *observer)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable);
+ Action *action;
+
+ action = g_hash_table_lookup (muxer->observed_actions, name);
+ g_object_weak_unref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action);
+ gtk_action_muxer_unregister_internal (action, observer);
+}
+
+static void
+gtk_action_muxer_free_group (gpointer data)
+{
+ Group *group = data;
+ gint i;
+
+ /* 'for loop' or 'four loop'? */
+ for (i = 0; i < 4; i++)
+ g_clear_signal_handler (&group->handler_ids[i], group->group);
+
+ g_object_unref (group->group);
+ g_free (group->prefix);
+
+ g_free (group);
+}
+
+static void
+gtk_action_muxer_free_action (gpointer data)
+{
+ Action *action = data;
+ GSList *it;
+
+ for (it = action->watchers; it; it = it->next)
+ g_object_weak_unref (G_OBJECT (it->data), gtk_action_muxer_weak_notify, action);
+
+ g_slist_free (action->watchers);
+ g_free (action->fullname);
+
+ g_free (action);
+}
+
+static void
+gtk_action_muxer_finalize (GObject *object)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
+
+ g_assert_cmpint (g_hash_table_size (muxer->observed_actions), ==, 0);
+ g_hash_table_unref (muxer->observed_actions);
+ g_hash_table_unref (muxer->groups);
+
+ G_OBJECT_CLASS (gtk_action_muxer_parent_class)
+ ->finalize (object);
+}
+
+static void
+gtk_action_muxer_dispose (GObject *object)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
+
+ if (muxer->parent)
+ {
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_primary_accel_changed, muxer);
+
+ g_clear_object (&muxer->parent);
+ }
+
+ g_hash_table_remove_all (muxer->observed_actions);
+
+ G_OBJECT_CLASS (gtk_action_muxer_parent_class)
+ ->dispose (object);
+}
+
+static void
+gtk_action_muxer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
+
+ switch (property_id)
+ {
+ case PROP_PARENT:
+ g_value_set_object (value, gtk_action_muxer_get_parent (muxer));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+gtk_action_muxer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
+
+ switch (property_id)
+ {
+ case PROP_PARENT:
+ gtk_action_muxer_set_parent (muxer, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+gtk_action_muxer_init (GtkActionMuxer *muxer)
+{
+ muxer->observed_actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_action);
+ muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_group);
+}
+
+static void
+gtk_action_muxer_observable_iface_init (GtkActionObservableInterface *iface)
+{
+ iface->register_observer = gtk_action_muxer_register_observer;
+ iface->unregister_observer = gtk_action_muxer_unregister_observer;
+}
+
+static void
+gtk_action_muxer_group_iface_init (GActionGroupInterface *iface)
+{
+ iface->list_actions = gtk_action_muxer_list_actions;
+ iface->query_action = gtk_action_muxer_query_action;
+ iface->activate_action = gtk_action_muxer_activate_action;
+ iface->change_action_state = gtk_action_muxer_change_action_state;
+}
+
+static void
+gtk_action_muxer_class_init (GObjectClass *class)
+{
+ class->get_property = gtk_action_muxer_get_property;
+ class->set_property = gtk_action_muxer_set_property;
+ class->finalize = gtk_action_muxer_finalize;
+ class->dispose = gtk_action_muxer_dispose;
+
+ accel_signal = g_signal_new ("primary-accel-changed", GTK_TYPE_ACTION_MUXER, G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+
+ properties[PROP_PARENT] = g_param_spec_object ("parent", "Parent",
+ "The parent muxer",
+ GTK_TYPE_ACTION_MUXER,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (class, NUM_PROPERTIES, properties);
+}
+
+/**
+ * gtk_action_muxer_insert:
+ * @muxer: a #GtkActionMuxer
+ * @prefix: the prefix string for the action group
+ * @action_group: a #GActionGroup
+ *
+ * Adds the actions in @action_group to the list of actions provided by
+ * @muxer. @prefix is prefixed to each action name, such that for each
+ * action <varname>x</varname> in @action_group, there is an equivalent
+ * action @prefix<literal>.</literal><varname>x</varname> in @muxer.
+ *
+ * For example, if @prefix is "<literal>app</literal>" and @action_group
+ * contains an action called "<literal>quit</literal>", then @muxer will
+ * now contain an action called "<literal>app.quit</literal>".
+ *
+ * If any #GtkActionObservers are registered for actions in the group,
+ * "action_added" notifications will be emitted, as appropriate.
+ *
+ * @prefix must not contain a dot ('.').
+ */
+void
+gtk_action_muxer_insert (GtkActionMuxer *muxer,
+ const gchar *prefix,
+ GActionGroup *action_group)
+{
+ gchar **actions;
+ Group *group;
+ gint i;
+
+ /* TODO: diff instead of ripout and replace */
+ gtk_action_muxer_remove (muxer, prefix);
+
+ group = g_new (Group, 1);
+ group->muxer = muxer;
+ group->group = g_object_ref (action_group);
+ group->prefix = g_strdup (prefix);
+
+ g_hash_table_insert (muxer->groups, group->prefix, group);
+
+ actions = g_action_group_list_actions (group->group);
+ for (i = 0; actions[i]; i++)
+ gtk_action_muxer_action_added_to_group (group->group, actions[i], group);
+ g_strfreev (actions);
+
+ group->handler_ids[0] = g_signal_connect (group->group, "action-added",
+ G_CALLBACK (gtk_action_muxer_action_added_to_group), group);
+ group->handler_ids[1] = g_signal_connect (group->group, "action-removed",
+ G_CALLBACK (gtk_action_muxer_action_removed_from_group), group);
+ group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed",
+ G_CALLBACK (gtk_action_muxer_group_action_enabled_changed), group);
+ group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed",
+ G_CALLBACK (gtk_action_muxer_group_action_state_changed), group);
+}
+
+/**
+ * gtk_action_muxer_remove:
+ * @muxer: a #GtkActionMuxer
+ * @prefix: the prefix of the action group to remove
+ *
+ * Removes a #GActionGroup from the #GtkActionMuxer.
+ *
+ * If any #GtkActionObservers are registered for actions in the group,
+ * "action_removed" notifications will be emitted, as appropriate.
+ */
+void
+gtk_action_muxer_remove (GtkActionMuxer *muxer,
+ const gchar *prefix)
+{
+ Group *group;
+
+ group = g_hash_table_lookup (muxer->groups, prefix);
+
+ if (group != NULL)
+ {
+ gchar **actions;
+ gint i;
+
+ g_hash_table_steal (muxer->groups, prefix);
+
+ actions = g_action_group_list_actions (group->group);
+ for (i = 0; actions[i]; i++)
+ gtk_action_muxer_action_removed_from_group (group->group, actions[i], group);
+ g_strfreev (actions);
+
+ gtk_action_muxer_free_group (group);
+ }
+}
+
+/**
+ * gtk_action_muxer_new:
+ *
+ * Creates a new #GtkActionMuxer.
+ */
+GtkActionMuxer *
+gtk_action_muxer_new (void)
+{
+ return g_object_new (GTK_TYPE_ACTION_MUXER, NULL);
+}
+
+/**
+ * gtk_action_muxer_get_parent:
+ * @muxer: a #GtkActionMuxer
+ *
+ * Returns: (transfer none): the parent of @muxer, or NULL.
+ */
+GtkActionMuxer *
+gtk_action_muxer_get_parent (GtkActionMuxer *muxer)
+{
+ g_return_val_if_fail (GTK_IS_ACTION_MUXER (muxer), NULL);
+
+ return muxer->parent;
+}
+
+static void
+emit_changed_accels (GtkActionMuxer *muxer,
+ GtkActionMuxer *parent)
+{
+ while (parent)
+ {
+ if (parent->primary_accels)
+ {
+ GHashTableIter iter;
+ gpointer key;
+
+ g_hash_table_iter_init (&iter, parent->primary_accels);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ gtk_action_muxer_primary_accel_changed (muxer, NULL, key);
+ }
+
+ parent = parent->parent;
+ }
+}
+
+/**
+ * gtk_action_muxer_set_parent:
+ * @muxer: a #GtkActionMuxer
+ * @parent: (nullable): the new parent #GtkActionMuxer
+ *
+ * Sets the parent of @muxer to @parent.
+ */
+void
+gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
+ GtkActionMuxer *parent)
+{
+ g_return_if_fail (GTK_IS_ACTION_MUXER (muxer));
+ g_return_if_fail (parent == NULL || GTK_IS_ACTION_MUXER (parent));
+
+ if (muxer->parent == parent)
+ return;
+
+ if (muxer->parent != NULL)
+ {
+ gchar **actions;
+ gchar **it;
+
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent));
+ for (it = actions; *it; it++)
+ gtk_action_muxer_action_removed (muxer, *it);
+ g_strfreev (actions);
+
+ emit_changed_accels (muxer, muxer->parent);
+
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_primary_accel_changed, muxer);
+
+ g_object_unref (muxer->parent);
+ }
+
+ muxer->parent = parent;
+
+ if (muxer->parent != NULL)
+ {
+ gchar **actions;
+ gchar **it;
+
+ g_object_ref (muxer->parent);
+
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent));
+ for (it = actions; *it; it++)
+ gtk_action_muxer_action_added (muxer, *it, G_ACTION_GROUP (muxer->parent), *it);
+ g_strfreev (actions);
+
+ emit_changed_accels (muxer, muxer->parent);
+
+ g_signal_connect (muxer->parent, "action-added",
+ G_CALLBACK (gtk_action_muxer_action_added_to_parent), muxer);
+ g_signal_connect (muxer->parent, "action-removed",
+ G_CALLBACK (gtk_action_muxer_action_removed_from_parent), muxer);
+ g_signal_connect (muxer->parent, "action-enabled-changed",
+ G_CALLBACK (gtk_action_muxer_parent_action_enabled_changed), muxer);
+ g_signal_connect (muxer->parent, "action-state-changed",
+ G_CALLBACK (gtk_action_muxer_parent_action_state_changed), muxer);
+ g_signal_connect (muxer->parent, "primary-accel-changed",
+ G_CALLBACK (gtk_action_muxer_parent_primary_accel_changed), muxer);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (muxer), properties[PROP_PARENT]);
+}
+
+void
+gtk_action_muxer_set_primary_accel (GtkActionMuxer *muxer,
+ const gchar *action_and_target,
+ const gchar *primary_accel)
+{
+ if (!muxer->primary_accels)
+ muxer->primary_accels = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ if (primary_accel)
+ g_hash_table_insert (muxer->primary_accels, g_strdup (action_and_target), g_strdup (primary_accel));
+ else
+ g_hash_table_remove (muxer->primary_accels, action_and_target);
+
+ gtk_action_muxer_primary_accel_changed (muxer, NULL, action_and_target);
+}
+
+const gchar *
+gtk_action_muxer_get_primary_accel (GtkActionMuxer *muxer,
+ const gchar *action_and_target)
+{
+ if (muxer->primary_accels)
+ {
+ const gchar *primary_accel;
+
+ primary_accel = g_hash_table_lookup (muxer->primary_accels, action_and_target);
+
+ if (primary_accel)
+ return primary_accel;
+ }
+
+ if (!muxer->parent)
+ return NULL;
+
+ return gtk_action_muxer_get_primary_accel (muxer->parent, action_and_target);
+}
+
+gchar *
+gtk_print_action_and_target (const gchar *action_namespace,
+ const gchar *action_name,
+ GVariant *target)
+{
+ GString *result;
+
+ g_return_val_if_fail (strchr (action_name, '|') == NULL, NULL);
+ g_return_val_if_fail (action_namespace == NULL || strchr (action_namespace, '|') == NULL, NULL);
+
+ result = g_string_new (NULL);
+
+ if (target)
+ g_variant_print_string (target, result, TRUE);
+ g_string_append_c (result, '|');
+
+ if (action_namespace)
+ {
+ g_string_append (result, action_namespace);
+ g_string_append_c (result, '.');
+ }
+
+ g_string_append (result, action_name);
+
+ return g_string_free (result, FALSE);
+}
diff --git a/src/gtkactionmuxer.h b/src/gtkactionmuxer.h
new file mode 100644
index 0000000..d71abf4
--- /dev/null
+++ b/src/gtkactionmuxer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2011 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __GTK_ACTION_MUXER_H__
+#define __GTK_ACTION_MUXER_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ACTION_MUXER (gtk_action_muxer_get_type ())
+#define GTK_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ GTK_TYPE_ACTION_MUXER, GtkActionMuxer))
+#define GTK_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ GTK_TYPE_ACTION_MUXER))
+
+typedef struct _GtkActionMuxer GtkActionMuxer;
+
+GType gtk_action_muxer_get_type (void);
+GtkActionMuxer * gtk_action_muxer_new (void);
+
+void gtk_action_muxer_insert (GtkActionMuxer *muxer,
+ const gchar *prefix,
+ GActionGroup *action_group);
+
+void gtk_action_muxer_remove (GtkActionMuxer *muxer,
+ const gchar *prefix);
+
+GtkActionMuxer * gtk_action_muxer_get_parent (GtkActionMuxer *muxer);
+
+void gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
+ GtkActionMuxer *parent);
+
+void gtk_action_muxer_set_primary_accel (GtkActionMuxer *muxer,
+ const gchar *action_and_target,
+ const gchar *primary_accel);
+
+const gchar * gtk_action_muxer_get_primary_accel (GtkActionMuxer *muxer,
+ const gchar *action_and_target);
+
+/* No better place for this... */
+gchar * gtk_print_action_and_target (const gchar *action_namespace,
+ const gchar *action_name,
+ GVariant *target);
+
+G_END_DECLS
+
+#endif /* __GTK_ACTION_MUXER_H__ */
diff --git a/src/gtkactionobservable.c b/src/gtkactionobservable.c
new file mode 100644
index 0000000..ab90df2
--- /dev/null
+++ b/src/gtkactionobservable.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2011 Canonical Limited
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "config.h"
+
+#include "gtkactionobservable.h"
+
+G_DEFINE_INTERFACE (GtkActionObservable, gtk_action_observable, G_TYPE_OBJECT)
+
+/*
+ * SECTION:gtkactionobserable
+ * @short_description: an interface implemented by objects that report
+ * changes to actions
+ */
+
+void
+gtk_action_observable_default_init (GtkActionObservableInterface *iface)
+{
+}
+
+/**
+ * gtk_action_observable_register_observer:
+ * @observable: a #GtkActionObservable
+ * @action_name: the name of the action
+ * @observer: the #GtkActionObserver to which the events will be reported
+ *
+ * Registers @observer as being interested in changes to @action_name on
+ * @observable.
+ */
+void
+gtk_action_observable_register_observer (GtkActionObservable *observable,
+ const gchar *action_name,
+ GtkActionObserver *observer)
+{
+ g_return_if_fail (GTK_IS_ACTION_OBSERVABLE (observable));
+
+ GTK_ACTION_OBSERVABLE_GET_IFACE (observable)
+ ->register_observer (observable, action_name, observer);
+}
+
+/**
+ * gtk_action_observable_unregister_observer:
+ * @observable: a #GtkActionObservable
+ * @action_name: the name of the action
+ * @observer: the #GtkActionObserver to which the events will be reported
+ *
+ * Removes the registration of @observer as being interested in changes
+ * to @action_name on @observable.
+ *
+ * If the observer was registered multiple times, it must be
+ * unregistered an equal number of times.
+ */
+void
+gtk_action_observable_unregister_observer (GtkActionObservable *observable,
+ const gchar *action_name,
+ GtkActionObserver *observer)
+{
+ g_return_if_fail (GTK_IS_ACTION_OBSERVABLE (observable));
+
+ GTK_ACTION_OBSERVABLE_GET_IFACE (observable)
+ ->unregister_observer (observable, action_name, observer);
+}
diff --git a/src/gtkactionobservable.h b/src/gtkactionobservable.h
new file mode 100644
index 0000000..aa1514b
--- /dev/null
+++ b/src/gtkactionobservable.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2011 Canonical Limited
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __GTK_ACTION_OBSERVABLE_H__
+#define __GTK_ACTION_OBSERVABLE_H__
+
+#include "gtkactionobserver.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ACTION_OBSERVABLE (gtk_action_observable_get_type ())
+#define GTK_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ GTK_TYPE_ACTION_OBSERVABLE, GtkActionObservable))
+#define GTK_IS_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ GTK_TYPE_ACTION_OBSERVABLE))
+#define GTK_ACTION_OBSERVABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
+ GTK_TYPE_ACTION_OBSERVABLE, \
+ GtkActionObservableInterface))
+
+typedef struct _GtkActionObservableInterface GtkActionObservableInterface;
+
+struct _GtkActionObservableInterface
+{
+ GTypeInterface g_iface;
+
+ void (* register_observer) (GtkActionObservable *observable,
+ const gchar *action_name,
+ GtkActionObserver *observer);
+ void (* unregister_observer) (GtkActionObservable *observable,
+ const gchar *action_name,
+ GtkActionObserver *observer);
+};
+
+GType gtk_action_observable_get_type (void);
+void gtk_action_observable_register_observer (GtkActionObservable *observable,
+ const gchar *action_name,
+ GtkActionObserver *observer);
+void gtk_action_observable_unregister_observer (GtkActionObservable *observable,
+ const gchar *action_name,
+ GtkActionObserver *observer);
+
+G_END_DECLS
+
+#endif /* __GTK_ACTION_OBSERVABLE_H__ */
diff --git a/src/gtkactionobserver.c b/src/gtkactionobserver.c
new file mode 100644
index 0000000..3287106
--- /dev/null
+++ b/src/gtkactionobserver.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright © 2011 Canonical Limited
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "config.h"
+
+#include "gtkactionobserver.h"
+
+G_DEFINE_INTERFACE (GtkActionObserver, gtk_action_observer, G_TYPE_OBJECT)
+
+/**
+ * SECTION:gtkactionobserver
+ * @short_description: an interface implemented by objects that are
+ * interested in monitoring actions for changes
+ *
+ * GtkActionObserver is a simple interface allowing objects that wish to
+ * be notified of changes to actions to be notified of those changes.
+ *
+ * It is also possible to monitor changes to action groups using
+ * #GObject signals, but there are a number of reasons that this
+ * approach could become problematic:
+ *
+ * - there are four separate signals that must be manually connected
+ * and disconnected
+ *
+ * - when a large number of different observers wish to monitor a
+ * (usually disjoint) set of actions within the same action group,
+ * there is only one way to avoid having all notifications delivered
+ * to all observers: signal detail. In order to use signal detail,
+ * each action name must be quarked, which is not always practical.
+ *
+ * - even if quarking is acceptable, #GObject signal details are
+ * implemented by scanning a linked list, so there is no real
+ * decrease in complexity
+ */
+
+void
+gtk_action_observer_default_init (GtkActionObserverInterface *class)
+{
+}
+
+/**
+ * gtk_action_observer_action_added:
+ * @observer: a #GtkActionObserver
+ * @observable: the source of the event
+ * @action_name: the name of the action
+ * @enabled: %TRUE if the action is now enabled
+ * @parameter_type: the parameter type for action invocations, or %NULL
+ * if no parameter is required
+ * @state: the current state of the action, or %NULL if the action is
+ * stateless
+ *
+ * This function is called when an action that the observer is
+ * registered to receive events for is added.
+ *
+ * This function should only be called by objects with which the
+ * observer has explicitly registered itself to receive events.
+ */
+void
+gtk_action_observer_action_added (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ const GVariantType *parameter_type,
+ gboolean enabled,
+ GVariant *state)
+{
+ g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer));
+
+ GTK_ACTION_OBSERVER_GET_IFACE (observer)
+ ->action_added (observer, observable, action_name, parameter_type, enabled, state);
+}
+
+/**
+ * gtk_action_observer_action_enabled_changed:
+ * @observer: a #GtkActionObserver
+ * @observable: the source of the event
+ * @action_name: the name of the action
+ * @enabled: %TRUE if the action is now enabled
+ *
+ * This function is called when an action that the observer is
+ * registered to receive events for becomes enabled or disabled.
+ *
+ * This function should only be called by objects with which the
+ * observer has explicitly registered itself to receive events.
+ */
+void
+gtk_action_observer_action_enabled_changed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ gboolean enabled)
+{
+ g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer));
+
+ GTK_ACTION_OBSERVER_GET_IFACE (observer)
+ ->action_enabled_changed (observer, observable, action_name, enabled);
+}
+
+/**
+ * gtk_action_observer_action_state_changed:
+ * @observer: a #GtkActionObserver
+ * @observable: the source of the event
+ * @action_name: the name of the action
+ * @state: the new state of the action
+ *
+ * This function is called when an action that the observer is
+ * registered to receive events for changes to its state.
+ *
+ * This function should only be called by objects with which the
+ * observer has explicitly registered itself to receive events.
+ */
+void
+gtk_action_observer_action_state_changed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ GVariant *state)
+{
+ g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer));
+
+ GTK_ACTION_OBSERVER_GET_IFACE (observer)
+ ->action_state_changed (observer, observable, action_name, state);
+}
+
+/**
+ * gtk_action_observer_action_removed:
+ * @observer: a #GtkActionObserver
+ * @observable: the source of the event
+ * @action_name: the name of the action
+ *
+ * This function is called when an action that the observer is
+ * registered to receive events for is removed.
+ *
+ * This function should only be called by objects with which the
+ * observer has explicitly registered itself to receive events.
+ */
+void
+gtk_action_observer_action_removed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name)
+{
+ g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer));
+
+ GTK_ACTION_OBSERVER_GET_IFACE (observer)
+ ->action_removed (observer, observable, action_name);
+}
+
+/**
+ * gtk_action_observer_primary_accel_changed:
+ * @observer: a #GtkActionObserver
+ * @observable: the source of the event
+ * @action_name: the name of the action
+ * @action_and_target: detailed action of the changed accel, in "action and target" format
+ *
+ * This function is called when an action that the observer is
+ * registered to receive events for has one of its accelerators changed.
+ *
+ * Accelerator changes are reported for all targets associated with the
+ * action. The @action_and_target string should be used to check if the
+ * reported target is the one that the observer is interested in.
+ */
+void
+gtk_action_observer_primary_accel_changed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ const gchar *action_and_target)
+{
+ GtkActionObserverInterface *iface;
+
+ g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer));
+
+ iface = GTK_ACTION_OBSERVER_GET_IFACE (observer);
+
+ if (iface->primary_accel_changed)
+ iface->primary_accel_changed (observer, observable, action_name, action_and_target);
+}
diff --git a/src/gtkactionobserver.h b/src/gtkactionobserver.h
new file mode 100644
index 0000000..a4e9659
--- /dev/null
+++ b/src/gtkactionobserver.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2011 Canonical Limited
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __GTK_ACTION_OBSERVER_H__
+#define __GTK_ACTION_OBSERVER_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ACTION_OBSERVER (gtk_action_observer_get_type ())
+#define GTK_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ GTK_TYPE_ACTION_OBSERVER, GtkActionObserver))
+#define GTK_IS_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ GTK_TYPE_ACTION_OBSERVER))
+#define GTK_ACTION_OBSERVER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
+ GTK_TYPE_ACTION_OBSERVER, GtkActionObserverInterface))
+
+typedef struct _GtkActionObserverInterface GtkActionObserverInterface;
+typedef struct _GtkActionObservable GtkActionObservable;
+typedef struct _GtkActionObserver GtkActionObserver;
+
+struct _GtkActionObserverInterface
+{
+ GTypeInterface g_iface;
+
+ void (* action_added) (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ const GVariantType *parameter_type,
+ gboolean enabled,
+ GVariant *state);
+ void (* action_enabled_changed) (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ gboolean enabled);
+ void (* action_state_changed) (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ GVariant *state);
+ void (* action_removed) (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name);
+ void (* primary_accel_changed) (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ const gchar *action_and_target);
+};
+
+GType gtk_action_observer_get_type (void);
+void gtk_action_observer_action_added (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ const GVariantType *parameter_type,
+ gboolean enabled,
+ GVariant *state);
+void gtk_action_observer_action_enabled_changed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ gboolean enabled);
+void gtk_action_observer_action_state_changed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ GVariant *state);
+void gtk_action_observer_action_removed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name);
+void gtk_action_observer_primary_accel_changed (GtkActionObserver *observer,
+ GtkActionObservable *observable,
+ const gchar *action_name,
+ const gchar *action_and_target);
+
+G_END_DECLS
+
+#endif /* __GTK_ACTION_OBSERVER_H__ */
diff --git a/src/hotplug-sniffer/hotplug-mimetypes.h b/src/hotplug-sniffer/hotplug-mimetypes.h
new file mode 100644
index 0000000..b034020
--- /dev/null
+++ b/src/hotplug-sniffer/hotplug-mimetypes.h
@@ -0,0 +1,141 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#ifndef __HOTPLUG_MIMETYPES_H__
+#define __HOTPLUG_MIMETYPES_H__
+
+#include <glib.h>
+
+G_GNUC_UNUSED static const gchar *docs_mimetypes[] = {
+ "application/vnd.oasis.opendocument.text",
+ "application/vnd.oasis.opendocument.presentation",
+ "application/vnd.oasis.opendocument.spreadsheet",
+ "application/msword",
+ "application/vnd.ms-excel",
+ "application/vnd.ms-powerpoint",
+ "application/rtf",
+ "application/pdf",
+ "application/x-bzpdf",
+ "application/x-gzpdf",
+ "application/x-xzpdf",
+ "application/postscript",
+ "application/x-bzpostscript",
+ "application/x-gzpostscript",
+ "image/x-eps",
+ "image/x-bzeps",
+ "image/x-gzeps",
+ "application/x-dvi",
+ "application/x-bzdvi",
+ "application/x-gzdvi",
+ "image/vnd.djvu",
+ "application/x-cbr",
+ "application/x-cbz",
+ "application/x-cb7",
+ "application/x-cbt",
+ NULL
+};
+
+G_GNUC_UNUSED static const gchar *video_mimetypes[] = {
+ "application/mxf",
+ "application/ogg",
+ "application/ram",
+ "application/sdp",
+ "application/vnd.ms-wpl",
+ "application/vnd.rn-realmedia",
+ "application/x-extension-m4a",
+ "application/x-extension-mp4",
+ "application/x-flash-video",
+ "application/x-matroska",
+ "application/x-netshow-channel",
+ "application/x-ogg",
+ "application/x-quicktimeplayer",
+ "application/x-shorten",
+ "image/vnd.rn-realpix",
+ "image/x-pict",
+ "misc/ultravox",
+ "text/x-google-video-pointer",
+ "video/3gpp",
+ "video/dv",
+ "video/fli",
+ "video/flv",
+ "video/mp2t",
+ "video/mp4",
+ "video/mp4v-es",
+ "video/mpeg",
+ "video/msvideo",
+ "video/ogg",
+ "video/quicktime",
+ "video/vivo",
+ "video/vnd.divx",
+ "video/vnd.rn-realvideo",
+ "video/vnd.vivo",
+ "video/webm",
+ "video/x-anim",
+ "video/x-avi",
+ "video/x-flc",
+ "video/x-fli",
+ "video/x-flic",
+ "video/x-flv",
+ "video/x-m4v",
+ "video/x-matroska",
+ "video/x-mpeg",
+ "video/x-ms-asf",
+ "video/x-ms-asx",
+ "video/x-msvideo",
+ "video/x-ms-wm",
+ "video/x-ms-wmv",
+ "video/x-ms-wmx",
+ "video/x-ms-wvx",
+ "video/x-nsv",
+ "video/x-ogm+ogg",
+ "video/x-theora+ogg",
+ "video/x-totem-stream",
+ NULL
+};
+
+G_GNUC_UNUSED static const gchar *audio_mimetypes[] = {
+ "audio/3gpp",
+ "audio/ac3",
+ "audio/AMR",
+ "audio/AMR-WB",
+ "audio/basic",
+ "audio/flac",
+ "audio/midi",
+ "audio/mp2",
+ "audio/mp4",
+ "audio/mpeg",
+ "audio/ogg",
+ "audio/prs.sid",
+ "audio/vnd.rn-realaudio",
+ "audio/x-aiff",
+ "audio/x-ape",
+ "audio/x-flac",
+ "audio/x-gsm",
+ "audio/x-it",
+ "audio/x-m4a",
+ "audio/x-matroska",
+ "audio/x-mod",
+ "audio/x-mp3",
+ "audio/x-mpeg",
+ "audio/x-ms-asf",
+ "audio/x-ms-asx",
+ "audio/x-ms-wax",
+ "audio/x-ms-wma",
+ "audio/x-musepack",
+ "audio/x-pn-aiff",
+ "audio/x-pn-au",
+ "audio/x-pn-wav",
+ "audio/x-pn-windows-acm",
+ "audio/x-realaudio",
+ "audio/x-real-audio",
+ "audio/x-sbc",
+ "audio/x-speex",
+ "audio/x-tta",
+ "audio/x-wav",
+ "audio/x-wavpack",
+ "audio/x-vorbis",
+ "audio/x-vorbis+ogg",
+ "audio/x-xm",
+ NULL
+};
+
+#endif /* __HOTPLUG_MIMETYPES_H__ */
diff --git a/src/hotplug-sniffer/hotplug-sniffer.c b/src/hotplug-sniffer/hotplug-sniffer.c
new file mode 100644
index 0000000..4b70ec7
--- /dev/null
+++ b/src/hotplug-sniffer/hotplug-sniffer.c
@@ -0,0 +1,298 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: David Zeuthen <davidz@redhat.com>
+ * Cosimo Cecchi <cosimoc@redhat.com>
+ *
+ */
+
+#include "shell-mime-sniffer.h"
+#include "hotplug-mimetypes.h"
+
+/* Set the environment variable HOTPLUG_SNIFFER_DEBUG to show debug */
+static void print_debug (const gchar *str, ...);
+
+#define BUS_NAME "org.gnome.Shell.HotplugSniffer"
+#define AUTOQUIT_TIMEOUT 5
+
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.gnome.Shell.HotplugSniffer'>"
+ " <method name='SniffURI'>"
+ " <arg type='s' name='uri' direction='in'/>"
+ " <arg type='as' name='content_types' direction='out'/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+
+static GDBusNodeInfo *introspection_data = NULL;
+static GMainLoop *loop = NULL;
+static guint autoquit_id = 0;
+
+static gboolean
+autoquit_timeout_cb (gpointer _unused)
+{
+ print_debug ("Timeout reached, quitting...");
+
+ autoquit_id = 0;
+ g_main_loop_quit (loop);
+
+ return FALSE;
+}
+
+static void
+ensure_autoquit_off (void)
+{
+ if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL)
+ return;
+
+ g_clear_handle_id (&autoquit_id, g_source_remove);
+}
+
+static void
+ensure_autoquit_on (void)
+{
+ if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL)
+ return;
+
+ autoquit_id =
+ g_timeout_add_seconds (AUTOQUIT_TIMEOUT,
+ autoquit_timeout_cb, NULL);
+ g_source_set_name_by_id (autoquit_id, "[gnome-shell] autoquit_timeout_cb");
+}
+
+typedef struct {
+ GVariant *parameters;
+ GDBusMethodInvocation *invocation;
+} InvocationData;
+
+static InvocationData *
+invocation_data_new (GVariant *params,
+ GDBusMethodInvocation *invocation)
+{
+ InvocationData *ret;
+
+ ret = g_new0 (InvocationData, 1);
+ ret->parameters = g_variant_ref (params);
+ ret->invocation = g_object_ref (invocation);
+
+ return ret;
+}
+
+static void
+invocation_data_free (InvocationData *data)
+{
+ g_variant_unref (data->parameters);
+ g_clear_object (&data->invocation);
+
+ g_free (data);
+}
+
+static void
+sniff_async_ready_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ InvocationData *data = user_data;
+ gchar **types;
+ GError *error = NULL;
+
+ types = shell_mime_sniffer_sniff_finish (SHELL_MIME_SNIFFER (source),
+ res, &error);
+
+ if (error != NULL)
+ {
+ g_dbus_method_invocation_return_gerror (data->invocation, error);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_dbus_method_invocation_return_value (data->invocation,
+ g_variant_new ("(^as)", types));
+ g_strfreev (types);
+
+ out:
+ invocation_data_free (data);
+ ensure_autoquit_on ();
+}
+
+static void
+handle_sniff_uri (InvocationData *data)
+{
+ ShellMimeSniffer *sniffer;
+ const gchar *uri;
+ GFile *file;
+
+ ensure_autoquit_off ();
+
+ g_variant_get (data->parameters,
+ "(&s)", &uri,
+ NULL);
+ file = g_file_new_for_uri (uri);
+
+ print_debug ("Initiating sniff for uri %s", uri);
+
+ sniffer = shell_mime_sniffer_new (file);
+ shell_mime_sniffer_sniff_async (sniffer,
+ sniff_async_ready_cb,
+ data);
+
+ g_object_unref (sniffer);
+ g_object_unref (file);
+}
+
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ InvocationData *data;
+
+ data = invocation_data_new (parameters, invocation);
+
+ if (g_strcmp0 (method_name, "SniffURI") == 0)
+ handle_sniff_uri (data);
+ else
+ g_assert_not_reached ();
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+ handle_method_call,
+ NULL, /* get_property */
+ NULL, /* set_property */
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ print_debug ("Connected to the session bus: %s", name);
+
+ g_dbus_connection_register_object (connection,
+ "/org/gnome/Shell/HotplugSniffer",
+ introspection_data->interfaces[0],
+ &interface_vtable,
+ NULL,
+ NULL,
+ &error);
+
+ if (error != NULL)
+ {
+ g_printerr ("Error exporting object on the session bus: %s",
+ error->message);
+ g_error_free (error);
+
+ _exit(1);
+ }
+
+ print_debug ("Object exported on the session bus");
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ print_debug ("Lost bus name: %s, exiting", name);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ print_debug ("Acquired bus name: %s", name);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ guint name_owner_id;
+
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (introspection_data != NULL);
+
+ ensure_autoquit_on ();
+ loop = g_main_loop_new (NULL, FALSE);
+
+ name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ BUS_NAME, 0,
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ NULL,
+ NULL);
+
+ g_main_loop_run (loop);
+
+ if (name_owner_id != 0)
+ g_bus_unown_name (name_owner_id);
+
+ if (loop != NULL)
+ g_main_loop_unref (loop);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void __attribute__((format(printf, 1, 0)))
+print_debug (const gchar *format, ...)
+{
+ g_autofree char *s = NULL;
+ g_autofree char *timestamp = NULL;
+ va_list ap;
+ g_autoptr (GDateTime) now = NULL;
+ static size_t once_init_value = 0;
+ static gboolean show_debug = FALSE;
+ static guint pid = 0;
+
+ if (g_once_init_enter (&once_init_value))
+ {
+ show_debug = (g_getenv ("HOTPLUG_SNIFFER_DEBUG") != NULL);
+ pid = getpid ();
+ g_once_init_leave (&once_init_value, 1);
+ }
+
+ if (!show_debug)
+ goto out;
+
+ now = g_date_time_new_now_local ();
+ timestamp = g_date_time_format (now, "%H:%M:%S");
+
+ va_start (ap, format);
+ s = g_strdup_vprintf (format, ap);
+ va_end (ap);
+
+ g_print ("gnome-shell-hotplug-sniffer[%d]: %s.%03d: %s\n",
+ pid, timestamp, g_date_time_get_microsecond (now), s);
+ out:
+ ;
+}
+
diff --git a/src/hotplug-sniffer/meson.build b/src/hotplug-sniffer/meson.build
new file mode 100644
index 0000000..4a777e5
--- /dev/null
+++ b/src/hotplug-sniffer/meson.build
@@ -0,0 +1,22 @@
+hotplug_sources = [
+ 'hotplug-mimetypes.h',
+ 'shell-mime-sniffer.h',
+ 'shell-mime-sniffer.c',
+ 'hotplug-sniffer.c'
+]
+
+executable('gnome-shell-hotplug-sniffer', hotplug_sources,
+ dependencies: [gio_dep, gdk_pixbuf_dep],
+ include_directories: include_directories('../..'),
+ install_dir: libexecdir,
+ install: true
+)
+
+service_file = 'org.gnome.Shell.HotplugSniffer.service'
+
+configure_file(
+ input: service_file + '.in',
+ output: service_file,
+ configuration: service_data,
+ install_dir: servicedir
+)
diff --git a/src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in b/src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in
new file mode 100644
index 0000000..b14cea9
--- /dev/null
+++ b/src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Shell.HotplugSniffer
+Exec=@libexecdir@/gnome-shell-hotplug-sniffer
diff --git a/src/hotplug-sniffer/shell-mime-sniffer.c b/src/hotplug-sniffer/shell-mime-sniffer.c
new file mode 100644
index 0000000..7a1c1fe
--- /dev/null
+++ b/src/hotplug-sniffer/shell-mime-sniffer.c
@@ -0,0 +1,590 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Cosimo Cecchi <cosimoc@redhat.com>
+ *
+ * The code for crawling the directory hierarchy is based on
+ * nautilus/libnautilus-private/nautilus-directory-async.c, with
+ * the following copyright and author:
+ *
+ * Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+ * Author: Darin Adler <darin@bentspoon.com>
+ *
+ */
+
+#include "shell-mime-sniffer.h"
+#include "hotplug-mimetypes.h"
+
+#include <glib/gi18n.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define LOADER_ATTRS \
+ G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
+ G_FILE_ATTRIBUTE_STANDARD_NAME "," \
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+
+#define WATCHDOG_TIMEOUT 1500
+#define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
+#define HIGH_SCORE_RATIO 0.10
+
+enum {
+ PROP_FILE = 1,
+ NUM_PROPERTIES
+};
+
+static GHashTable *image_type_table = NULL;
+static GHashTable *audio_type_table = NULL;
+static GHashTable *video_type_table = NULL;
+static GHashTable *docs_type_table = NULL;
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+typedef struct {
+ ShellMimeSniffer *self;
+
+ GFile *file;
+ GFileEnumerator *enumerator;
+ GList *deep_count_subdirectories;
+
+ gint audio_count;
+ gint image_count;
+ gint document_count;
+ gint video_count;
+
+ gint total_items;
+} DeepCountState;
+
+typedef struct _ShellMimeSnifferPrivate ShellMimeSnifferPrivate;
+
+struct _ShellMimeSniffer
+{
+ GObject parent_instance;
+
+ ShellMimeSnifferPrivate *priv;
+};
+
+struct _ShellMimeSnifferPrivate {
+ GFile *file;
+
+ GCancellable *cancellable;
+ guint watchdog_id;
+
+ GTask *task;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellMimeSniffer, shell_mime_sniffer, G_TYPE_OBJECT);
+
+static void deep_count_load (DeepCountState *state,
+ GFile *file);
+
+static void
+init_mimetypes (void)
+{
+ static gsize once_init = 0;
+
+ if (g_once_init_enter (&once_init))
+ {
+ GSList *formats, *l;
+ GdkPixbufFormat *format;
+ gchar **types;
+ gint idx;
+
+ image_type_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ video_type_table = g_hash_table_new (g_str_hash, g_str_equal);
+ audio_type_table = g_hash_table_new (g_str_hash, g_str_equal);
+ docs_type_table = g_hash_table_new (g_str_hash, g_str_equal);
+
+ formats = gdk_pixbuf_get_formats ();
+
+ for (l = formats; l != NULL; l = l->next)
+ {
+ format = l->data;
+ types = gdk_pixbuf_format_get_mime_types (format);
+
+ for (idx = 0; types[idx] != NULL; idx++)
+ g_hash_table_insert (image_type_table, g_strdup (types[idx]), GINT_TO_POINTER (1));
+
+ g_strfreev (types);
+ }
+
+ g_slist_free (formats);
+
+ for (idx = 0; audio_mimetypes[idx] != NULL; idx++)
+ g_hash_table_insert (audio_type_table, (gpointer) audio_mimetypes[idx], GINT_TO_POINTER (1));
+
+ for (idx = 0; video_mimetypes[idx] != NULL; idx++)
+ g_hash_table_insert (video_type_table, (gpointer) video_mimetypes[idx], GINT_TO_POINTER (1));
+
+ for (idx = 0; docs_mimetypes[idx] != NULL; idx++)
+ g_hash_table_insert (docs_type_table, (gpointer) docs_mimetypes[idx], GINT_TO_POINTER (1));
+
+ g_once_init_leave (&once_init, 1);
+ }
+}
+
+static void
+add_content_type_to_cache (DeepCountState *state,
+ const gchar *content_type)
+{
+ gboolean matched = TRUE;
+
+ if (g_hash_table_lookup (image_type_table, content_type))
+ state->image_count++;
+ else if (g_hash_table_lookup (video_type_table, content_type))
+ state->video_count++;
+ else if (g_hash_table_lookup (docs_type_table, content_type))
+ state->document_count++;
+ else if (g_hash_table_lookup (audio_type_table, content_type))
+ state->audio_count++;
+ else
+ matched = FALSE;
+
+ if (matched)
+ state->total_items++;
+}
+
+typedef struct {
+ const gchar *type;
+ gdouble ratio;
+} SniffedResult;
+
+static gint
+results_cmp_func (gconstpointer a,
+ gconstpointer b)
+{
+ const SniffedResult *sniffed_a = a;
+ const SniffedResult *sniffed_b = b;
+
+ if (sniffed_a->ratio < sniffed_b->ratio)
+ return 1;
+
+ if (sniffed_a->ratio > sniffed_b->ratio)
+ return -1;
+
+ return 0;
+}
+
+static void
+prepare_async_result (DeepCountState *state)
+{
+ ShellMimeSniffer *self = state->self;
+ GArray *results;
+ GPtrArray *sniffed_mime;
+ SniffedResult result;
+ char **mimes;
+
+ sniffed_mime = g_ptr_array_new ();
+ results = g_array_new (TRUE, TRUE, sizeof (SniffedResult));
+
+ if (state->total_items == 0)
+ goto out;
+
+ result.type = "x-content/video";
+ result.ratio = (gdouble) state->video_count / (gdouble) state->total_items;
+ g_array_append_val (results, result);
+
+ result.type = "x-content/audio";
+ result.ratio = (gdouble) state->audio_count / (gdouble) state->total_items;
+ g_array_append_val (results, result);
+
+ result.type = "x-content/pictures";
+ result.ratio = (gdouble) state->image_count / (gdouble) state->total_items;
+ g_array_append_val (results, result);
+
+ result.type = "x-content/documents";
+ result.ratio = (gdouble) state->document_count / (gdouble) state->total_items;
+ g_array_append_val (results, result);
+
+ g_array_sort (results, results_cmp_func);
+
+ result = g_array_index (results, SniffedResult, 0);
+ g_ptr_array_add (sniffed_mime, g_strdup (result.type));
+
+ /* if other types score high in ratio, add them, up to three */
+ result = g_array_index (results, SniffedResult, 1);
+ if (result.ratio < HIGH_SCORE_RATIO)
+ goto out;
+ g_ptr_array_add (sniffed_mime, g_strdup (result.type));
+
+ result = g_array_index (results, SniffedResult, 2);
+ if (result.ratio < HIGH_SCORE_RATIO)
+ goto out;
+ g_ptr_array_add (sniffed_mime, g_strdup (result.type));
+
+ out:
+ g_ptr_array_add (sniffed_mime, NULL);
+ mimes = (gchar **) g_ptr_array_free (sniffed_mime, FALSE);
+
+ g_array_free (results, TRUE);
+ g_task_return_pointer (self->priv->task, mimes, (GDestroyNotify)g_strfreev);
+}
+
+/* adapted from nautilus/libnautilus-private/nautilus-directory-async.c */
+static void
+deep_count_one (DeepCountState *state,
+ GFileInfo *info)
+{
+ GFile *subdir;
+ const char *content_type;
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ {
+ /* record the fact that we have to descend into this directory */
+ subdir = g_file_get_child (state->file, g_file_info_get_name (info));
+ state->deep_count_subdirectories =
+ g_list_append (state->deep_count_subdirectories, subdir);
+ }
+ else
+ {
+ content_type = g_file_info_get_content_type (info);
+
+ if (content_type)
+ add_content_type_to_cache (state, content_type);
+ }
+}
+
+static void
+deep_count_finish (DeepCountState *state)
+{
+ prepare_async_result (state);
+
+ if (state->enumerator)
+ {
+ if (!g_file_enumerator_is_closed (state->enumerator))
+ g_file_enumerator_close_async (state->enumerator,
+ 0, NULL, NULL, NULL);
+
+ g_object_unref (state->enumerator);
+ }
+
+ g_cancellable_reset (state->self->priv->cancellable);
+ g_clear_object (&state->file);
+
+ g_list_free_full (state->deep_count_subdirectories, g_object_unref);
+
+ g_free (state);
+}
+
+static void
+deep_count_next_dir (DeepCountState *state)
+{
+ GFile *new_file;
+
+ g_clear_object (&state->file);
+
+ if (state->deep_count_subdirectories != NULL)
+ {
+ /* Work on a new directory. */
+ new_file = state->deep_count_subdirectories->data;
+ state->deep_count_subdirectories =
+ g_list_remove (state->deep_count_subdirectories, new_file);
+
+ deep_count_load (state, new_file);
+ g_object_unref (new_file);
+ }
+ else
+ {
+ deep_count_finish (state);
+ }
+}
+
+static void
+deep_count_more_files_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DeepCountState *state;
+ GList *files, *l;
+ GFileInfo *info;
+
+ state = user_data;
+
+ if (g_cancellable_is_cancelled (state->self->priv->cancellable))
+ {
+ deep_count_finish (state);
+ return;
+ }
+
+ files = g_file_enumerator_next_files_finish (state->enumerator,
+ res, NULL);
+
+ for (l = files; l != NULL; l = l->next)
+ {
+ info = l->data;
+ deep_count_one (state, info);
+ g_object_unref (info);
+ }
+
+ if (files == NULL)
+ {
+ g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL);
+ g_object_unref (state->enumerator);
+ state->enumerator = NULL;
+
+ deep_count_next_dir (state);
+ }
+ else
+ {
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_LOW,
+ state->self->priv->cancellable,
+ deep_count_more_files_callback,
+ state);
+ }
+
+ g_list_free (files);
+}
+
+static void
+deep_count_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DeepCountState *state;
+ GFileEnumerator *enumerator;
+
+ state = user_data;
+
+ if (g_cancellable_is_cancelled (state->self->priv->cancellable))
+ {
+ deep_count_finish (state);
+ return;
+ }
+
+ enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
+ res, NULL);
+
+ if (enumerator == NULL)
+ {
+ deep_count_next_dir (state);
+ }
+ else
+ {
+ state->enumerator = enumerator;
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_LOW,
+ state->self->priv->cancellable,
+ deep_count_more_files_callback,
+ state);
+ }
+}
+
+static void
+deep_count_load (DeepCountState *state,
+ GFile *file)
+{
+ state->file = g_object_ref (file);
+
+ g_file_enumerate_children_async (state->file,
+ LOADER_ATTRS,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
+ G_PRIORITY_LOW, /* prio */
+ state->self->priv->cancellable,
+ deep_count_callback,
+ state);
+}
+
+static void
+deep_count_start (ShellMimeSniffer *self)
+{
+ DeepCountState *state;
+
+ state = g_new0 (DeepCountState, 1);
+ state->self = self;
+
+ deep_count_load (state, self->priv->file);
+}
+
+static void
+query_info_async_ready_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GFileInfo *info;
+ GError *error = NULL;
+ ShellMimeSniffer *self = user_data;
+
+ info = g_file_query_info_finish (G_FILE (source),
+ res, &error);
+
+ if (error != NULL)
+ {
+ g_task_return_error (self->priv->task, error);
+
+ return;
+ }
+
+ if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
+ {
+ g_task_return_new_error (self->priv->task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_DIRECTORY,
+ "Not a directory");
+
+ return;
+ }
+
+ deep_count_start (self);
+}
+
+static gboolean
+watchdog_timeout_reached_cb (gpointer user_data)
+{
+ ShellMimeSniffer *self = user_data;
+
+ self->priv->watchdog_id = 0;
+ g_cancellable_cancel (self->priv->cancellable);
+
+ return FALSE;
+}
+
+static void
+start_loading_file (ShellMimeSniffer *self)
+{
+ g_file_query_info_async (self->priv->file,
+ LOADER_ATTRS,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ self->priv->cancellable,
+ query_info_async_ready_cb,
+ self);
+}
+
+static void
+shell_mime_sniffer_set_file (ShellMimeSniffer *self,
+ GFile *file)
+{
+ g_clear_object (&self->priv->file);
+ self->priv->file = g_object_ref (file);
+}
+
+static void
+shell_mime_sniffer_dispose (GObject *object)
+{
+ ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
+
+ g_clear_object (&self->priv->file);
+ g_clear_object (&self->priv->cancellable);
+ g_clear_object (&self->priv->task);
+
+ g_clear_handle_id (&self->priv->watchdog_id, g_source_remove);
+
+ G_OBJECT_CLASS (shell_mime_sniffer_parent_class)->dispose (object);
+}
+
+static void
+shell_mime_sniffer_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
+
+ switch (prop_id) {
+ case PROP_FILE:
+ g_value_set_object (value, self->priv->file);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_mime_sniffer_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
+
+ switch (prop_id) {
+ case PROP_FILE:
+ shell_mime_sniffer_set_file (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_mime_sniffer_class_init (ShellMimeSnifferClass *klass)
+{
+ GObjectClass *oclass;
+
+ oclass = G_OBJECT_CLASS (klass);
+ oclass->dispose = shell_mime_sniffer_dispose;
+ oclass->get_property = shell_mime_sniffer_get_property;
+ oclass->set_property = shell_mime_sniffer_set_property;
+
+ properties[PROP_FILE] =
+ g_param_spec_object ("file",
+ "File",
+ "The loaded file",
+ G_TYPE_FILE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static void
+shell_mime_sniffer_init (ShellMimeSniffer *self)
+{
+ self->priv = shell_mime_sniffer_get_instance_private (self);
+ init_mimetypes ();
+}
+
+ShellMimeSniffer *
+shell_mime_sniffer_new (GFile *file)
+{
+ return g_object_new (SHELL_TYPE_MIME_SNIFFER,
+ "file", file,
+ NULL);
+}
+
+void
+shell_mime_sniffer_sniff_async (ShellMimeSniffer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_assert (self->priv->watchdog_id == 0);
+ g_assert (self->priv->task == NULL);
+
+ self->priv->cancellable = g_cancellable_new ();
+ self->priv->task = g_task_new (self, self->priv->cancellable,
+ callback, user_data);
+
+ self->priv->watchdog_id =
+ g_timeout_add (WATCHDOG_TIMEOUT,
+ watchdog_timeout_reached_cb, self);
+ g_source_set_name_by_id (self->priv->watchdog_id, "[gnome-shell] watchdog_timeout_reached_cb");
+
+ start_loading_file (self);
+}
+
+gchar **
+shell_mime_sniffer_sniff_finish (ShellMimeSniffer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (self->priv->task, error);
+}
diff --git a/src/hotplug-sniffer/shell-mime-sniffer.h b/src/hotplug-sniffer/shell-mime-sniffer.h
new file mode 100644
index 0000000..3936eef
--- /dev/null
+++ b/src/hotplug-sniffer/shell-mime-sniffer.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Cosimo Cecchi <cosimoc@redhat.com>
+ *
+ */
+
+#ifndef __SHELL_MIME_SNIFFER_H__
+#define __SHELL_MIME_SNIFFER_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_MIME_SNIFFER (shell_mime_sniffer_get_type ())
+G_DECLARE_FINAL_TYPE (ShellMimeSniffer, shell_mime_sniffer,
+ SHELL, MIME_SNIFFER, GObject)
+
+ShellMimeSniffer *shell_mime_sniffer_new (GFile *file);
+
+void shell_mime_sniffer_sniff_async (ShellMimeSniffer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gchar ** shell_mime_sniffer_sniff_finish (ShellMimeSniffer *self,
+ GAsyncResult *res,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __SHELL_MIME_SNIFFER_H__ */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..2311a74
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,599 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#if defined (HAVE_MALLINFO) || defined (HAVE_MALLINFO2)
+#include <malloc.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <cogl-pango/cogl-pango.h>
+#include <clutter/clutter.h>
+#include <gtk/gtk.h>
+#include <glib-unix.h>
+#include <glib/gi18n-lib.h>
+#include <girepository.h>
+#include <meta/meta-context.h>
+#include <meta/meta-plugin.h>
+#include <meta/prefs.h>
+#include <atk-bridge.h>
+
+#include "shell-global.h"
+#include "shell-global-private.h"
+#include "shell-perf-log.h"
+#include "st.h"
+
+extern GType gnome_shell_plugin_get_type (void);
+
+#define SHELL_DBUS_SERVICE "org.gnome.Shell"
+
+#define WM_NAME "GNOME Shell"
+#define GNOME_WM_KEYBINDINGS "Mutter,GNOME Shell"
+
+static gboolean is_gdm_mode = FALSE;
+static char *session_mode = NULL;
+static int caught_signal = 0;
+
+#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
+#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
+
+enum {
+ SHELL_DEBUG_BACKTRACE_WARNINGS = 1,
+ SHELL_DEBUG_BACKTRACE_SEGFAULTS = 2,
+};
+static int _shell_debug;
+static gboolean _tracked_signals[NSIG] = { 0 };
+
+static void
+shell_dbus_acquire_name (GDBusProxy *bus,
+ guint32 request_name_flags,
+ guint32 *request_name_result,
+ const gchar *name,
+ gboolean fatal)
+{
+ GError *error = NULL;
+ GVariant *request_name_variant;
+
+ if (!(request_name_variant = g_dbus_proxy_call_sync (bus,
+ "RequestName",
+ g_variant_new ("(su)", name, request_name_flags),
+ 0, /* call flags */
+ -1, /* timeout */
+ NULL, /* cancellable */
+ &error)))
+ {
+ g_printerr ("failed to acquire %s: %s\n", name, error->message);
+ g_clear_error (&error);
+ if (!fatal)
+ return;
+ exit (1);
+ }
+ g_variant_get (request_name_variant, "(u)", request_name_result);
+ g_variant_unref (request_name_variant);
+}
+
+static void
+shell_dbus_init (gboolean replace)
+{
+ GDBusConnection *session;
+ GDBusProxy *bus;
+ GError *error = NULL;
+ guint32 request_name_flags;
+ guint32 request_name_result;
+
+ session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+ if (error) {
+ g_printerr ("Failed to connect to session bus: %s", error->message);
+ exit (1);
+ }
+
+ bus = g_dbus_proxy_new_sync (session,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, /* interface info */
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ NULL, /* cancellable */
+ &error);
+
+ if (!bus)
+ {
+ g_printerr ("Failed to get a session bus proxy: %s", error->message);
+ exit (1);
+ }
+
+ request_name_flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
+ if (replace)
+ request_name_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
+
+ shell_dbus_acquire_name (bus,
+ request_name_flags,
+ &request_name_result,
+ SHELL_DBUS_SERVICE, TRUE);
+ if (!(request_name_result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
+ || request_name_result == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER))
+ {
+ g_printerr (SHELL_DBUS_SERVICE " already exists on bus and --replace not specified\n");
+ exit (1);
+ }
+
+ g_object_unref (bus);
+ g_object_unref (session);
+}
+
+static void
+shell_introspection_init (void)
+{
+
+ g_irepository_prepend_search_path (MUTTER_TYPELIB_DIR);
+ g_irepository_prepend_search_path (GNOME_SHELL_PKGLIBDIR);
+
+ /* We need to explicitly add the directories where the private libraries are
+ * installed to the GIR's library path, so that they can be found at runtime
+ * when linking using DT_RUNPATH (instead of DT_RPATH), which is the default
+ * for some linkers (e.g. gold) and in some distros (e.g. Debian).
+ */
+ g_irepository_prepend_library_path (MUTTER_TYPELIB_DIR);
+ g_irepository_prepend_library_path (GNOME_SHELL_PKGLIBDIR);
+}
+
+static void
+shell_fonts_init (void)
+{
+ CoglPangoFontMap *fontmap;
+
+ /* Disable text mipmapping; it causes problems on pre-GEM Intel
+ * drivers and we should just be rendering text at the right
+ * size rather than scaling it. If we do effects where we dynamically
+ * zoom labels, then we might want to reconsider.
+ */
+ fontmap = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
+ cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE);
+}
+
+static void
+shell_profiler_init (void)
+{
+ ShellGlobal *global;
+ GjsProfiler *profiler;
+ GjsContext *context;
+ const char *enabled;
+ const char *fd_str;
+ int fd = -1;
+
+ /* Sysprof uses the "GJS_TRACE_FD=N" environment variable to connect GJS
+ * profiler data to the combined Sysprof capture. Since we are in control of
+ * the GjsContext, we need to proxy this FD across to the GJS profiler.
+ */
+
+ fd_str = g_getenv ("GJS_TRACE_FD");
+ enabled = g_getenv ("GJS_ENABLE_PROFILER");
+ if (fd_str == NULL || enabled == NULL)
+ return;
+
+ global = shell_global_get ();
+ g_return_if_fail (global);
+
+ context = _shell_global_get_gjs_context (global);
+ g_return_if_fail (context);
+
+ profiler = gjs_context_get_profiler (context);
+ g_return_if_fail (profiler);
+
+ if (fd_str)
+ {
+ fd = atoi (fd_str);
+
+ if (fd > 2)
+ {
+ gjs_profiler_set_fd (profiler, fd);
+ gjs_profiler_start (profiler);
+ }
+ }
+}
+
+static void
+shell_profiler_shutdown (void)
+{
+ ShellGlobal *global;
+ GjsProfiler *profiler;
+ GjsContext *context;
+
+ global = shell_global_get ();
+ context = _shell_global_get_gjs_context (global);
+ profiler = gjs_context_get_profiler (context);
+
+ if (profiler)
+ gjs_profiler_stop (profiler);
+}
+
+static void
+malloc_statistics_callback (ShellPerfLog *perf_log,
+ gpointer data)
+{
+#if defined (HAVE_MALLINFO) || defined (HAVE_MALLINFO2)
+#ifdef HAVE_MALLINFO2
+ struct mallinfo2 info = mallinfo2 ();
+#else
+ struct mallinfo info = mallinfo ();
+#endif
+
+ shell_perf_log_update_statistic_i (perf_log,
+ "malloc.arenaSize",
+ info.arena);
+ shell_perf_log_update_statistic_i (perf_log,
+ "malloc.mmapSize",
+ info.hblkhd);
+ shell_perf_log_update_statistic_i (perf_log,
+ "malloc.usedSize",
+ info.uordblks);
+#endif /* defined (HAVE_MALLINFO) || defined (HAVE_MALLINFO2) */
+}
+
+static void
+shell_perf_log_init (void)
+{
+ ShellPerfLog *perf_log = shell_perf_log_get_default ();
+
+ /* For probably historical reasons, mallinfo() defines the returned values,
+ * even those in bytes as int, not size_t. We're determined not to use
+ * more than 2G of malloc'ed memory, so are OK with that.
+ */
+ shell_perf_log_define_statistic (perf_log,
+ "malloc.arenaSize",
+ "Amount of memory allocated by malloc() with brk(), in bytes",
+ "i");
+ shell_perf_log_define_statistic (perf_log,
+ "malloc.mmapSize",
+ "Amount of memory allocated by malloc() with mmap(), in bytes",
+ "i");
+ shell_perf_log_define_statistic (perf_log,
+ "malloc.usedSize",
+ "Amount of malloc'ed memory currently in use",
+ "i");
+
+ shell_perf_log_add_statistics_callback (perf_log,
+ malloc_statistics_callback,
+ NULL, NULL);
+}
+
+static void
+shell_a11y_init (void)
+{
+ cally_accessibility_init ();
+
+ if (clutter_get_accessibility_enabled () == FALSE)
+ {
+ g_warning ("Accessibility: clutter has no accessibility enabled"
+ " skipping the atk-bridge load");
+ }
+ else
+ {
+ atk_bridge_adaptor_init (NULL, NULL);
+ }
+}
+
+static void
+shell_init_debug (const char *debug_env)
+{
+ static const GDebugKey keys[] = {
+ { "backtrace-warnings", SHELL_DEBUG_BACKTRACE_WARNINGS },
+ { "backtrace-segfaults", SHELL_DEBUG_BACKTRACE_SEGFAULTS },
+ };
+
+ _shell_debug = g_parse_debug_string (debug_env, keys,
+ G_N_ELEMENTS (keys));
+}
+
+static GLogWriterOutput
+default_log_writer (GLogLevelFlags log_level,
+ const GLogField *fields,
+ gsize n_fields,
+ gpointer user_data)
+{
+ GLogWriterOutput output;
+ int i;
+
+ output = g_log_writer_default (log_level, fields, n_fields, user_data);
+
+ if ((_shell_debug & SHELL_DEBUG_BACKTRACE_WARNINGS) &&
+ ((log_level & G_LOG_LEVEL_CRITICAL) ||
+ (log_level & G_LOG_LEVEL_WARNING)))
+ {
+ const char *log_domain = NULL;
+
+ for (i = 0; i < n_fields; i++)
+ {
+ if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0)
+ {
+ log_domain = fields[i].value;
+ break;
+ }
+ }
+
+ /* Filter out Gjs logs, those already have the stack */
+ if (g_strcmp0 (log_domain, "Gjs") != 0)
+ gjs_dumpstack ();
+ }
+
+ return output;
+}
+
+static GLogWriterOutput
+shut_up (GLogLevelFlags log_level,
+ const GLogField *fields,
+ gsize n_fields,
+ gpointer user_data)
+{
+ return (GLogWriterOutput) {0};
+}
+
+static void
+dump_gjs_stack_alarm_sigaction (int signo)
+{
+ g_log_set_writer_func (g_log_writer_default, NULL, NULL);
+ g_warning ("Failed to dump Javascript stack, got stuck");
+ g_log_set_writer_func (default_log_writer, NULL, NULL);
+
+ raise (caught_signal);
+}
+
+static void
+dump_gjs_stack_on_signal_handler (int signo)
+{
+ struct sigaction sa = { .sa_handler = dump_gjs_stack_alarm_sigaction };
+ gsize i;
+
+ /* Ignore all the signals starting this point, a part the one we'll raise
+ * (which is implicitly ignored here through SA_RESETHAND), this is needed
+ * not to get this handler being called by other signals that we were
+ * tracking and that might be emitted by code called starting from now.
+ */
+ for (i = 0; i < G_N_ELEMENTS (_tracked_signals); ++i)
+ {
+ if (_tracked_signals[i] && i != signo)
+ signal (i, SIG_IGN);
+ }
+
+ /* Waiting at least 5 seconds for the dumpstack, if it fails, we raise the error */
+ caught_signal = signo;
+ sigemptyset (&sa.sa_mask);
+ sigaction (SIGALRM, &sa, NULL);
+
+ alarm (5);
+ gjs_dumpstack ();
+ alarm (0);
+
+ raise (signo);
+}
+
+static void
+dump_gjs_stack_on_signal (int signo)
+{
+ struct sigaction sa = {
+ .sa_flags = SA_RESETHAND | SA_NODEFER,
+ .sa_handler = dump_gjs_stack_on_signal_handler,
+ };
+
+ sigemptyset (&sa.sa_mask);
+
+ sigaction (signo, &sa, NULL);
+ _tracked_signals[signo] = TRUE;
+}
+
+static gboolean
+list_modes (const char *option_name,
+ const char *value,
+ gpointer data,
+ GError **error)
+{
+ ShellGlobal *global;
+ GjsContext *context;
+ const char *script;
+ int status;
+
+ /* Many of our imports require global to be set, so rather than
+ * tayloring our imports carefully here to avoid that dependency,
+ * we just set it.
+ * ShellGlobal has some GTK+ dependencies, so initialize GTK+; we
+ * don't really care if it fails though (e.g. when running from a tty),
+ * so we mute all warnings */
+ g_log_set_writer_func (shut_up, NULL, NULL);
+ gtk_init_check (NULL, NULL);
+
+ _shell_global_init (NULL);
+ global = shell_global_get ();
+ context = _shell_global_get_gjs_context (global);
+
+ shell_introspection_init ();
+
+ script = "imports.ui.environment.init();"
+ "imports.ui.sessionMode.listModes();";
+ if (!gjs_context_eval (context, script, -1, "<main>", &status, NULL))
+ g_message ("Retrieving list of available modes failed.");
+
+ g_object_unref (context);
+ exit (status);
+}
+
+static gboolean
+print_version (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ g_print ("GNOME Shell %s\n", VERSION);
+ exit (0);
+}
+
+GOptionEntry gnome_shell_options[] = {
+ {
+ "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ print_version,
+ N_("Print version"),
+ NULL
+ },
+ {
+ "gdm-mode", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
+ &is_gdm_mode,
+ N_("Mode used by GDM for login screen"),
+ NULL
+ },
+ {
+ "mode", 0, 0, G_OPTION_ARG_STRING,
+ &session_mode,
+ N_("Use a specific mode, e.g. “gdm” for login screen"),
+ "MODE"
+ },
+ {
+ "list-modes", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ list_modes,
+ N_("List possible modes"),
+ NULL
+ },
+ { NULL }
+};
+
+static gboolean
+on_sigterm (gpointer user_data)
+{
+ MetaContext *context = META_CONTEXT (user_data);
+
+ meta_context_terminate (context);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+init_signal_handlers (MetaContext *context)
+{
+ struct sigaction act = { 0 };
+ sigset_t empty_mask;
+
+ sigemptyset (&empty_mask);
+ act.sa_handler = SIG_IGN;
+ act.sa_mask = empty_mask;
+ act.sa_flags = 0;
+ if (sigaction (SIGPIPE, &act, NULL) < 0)
+ g_warning ("Failed to register SIGPIPE handler: %s", g_strerror (errno));
+#ifdef SIGXFSZ
+ if (sigaction (SIGXFSZ, &act, NULL) < 0)
+ g_warning ("Failed to register SIGXFSZ handler: %s", g_strerror (errno));
+#endif
+
+ g_unix_signal_add (SIGTERM, on_sigterm, context);
+}
+
+static void
+change_to_home_directory (void)
+{
+ const char *home_dir;
+
+ home_dir = g_get_home_dir ();
+ if (!home_dir)
+ return;
+
+ if (chdir (home_dir) < 0)
+ g_warning ("Could not change to home directory %s", home_dir);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_autoptr (MetaContext) context = NULL;
+ GError *error = NULL;
+ int ecode = EXIT_SUCCESS;
+
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ context = meta_create_context (WM_NAME);
+ meta_context_add_option_entries (context, gnome_shell_options,
+ GETTEXT_PACKAGE);
+ meta_context_add_option_group (context, g_irepository_get_option_group ());
+
+ session_mode = (char *) g_getenv ("GNOME_SHELL_SESSION_MODE");
+
+ if (!meta_context_configure (context, &argc, &argv, &error))
+ {
+ g_printerr ("Failed to configure: %s", error->message);
+ return EXIT_FAILURE;
+ }
+
+ meta_context_set_plugin_gtype (context, gnome_shell_plugin_get_type ());
+ meta_context_set_gnome_wm_keybindings (context, GNOME_WM_KEYBINDINGS);
+
+ init_signal_handlers (context);
+ change_to_home_directory ();
+
+ if (!meta_context_setup (context, &error))
+ {
+ g_printerr ("Failed to setup: %s", error->message);
+ return EXIT_FAILURE;
+ }
+
+ /* FIXME: Add gjs API to set this stuff and don't depend on the
+ * environment. These propagate to child processes.
+ */
+ g_setenv ("GJS_DEBUG_OUTPUT", "stderr", TRUE);
+ g_setenv ("GJS_DEBUG_TOPICS", "JS ERROR;JS LOG", TRUE);
+
+ shell_init_debug (g_getenv ("SHELL_DEBUG"));
+
+ shell_dbus_init (meta_context_is_replacing (context));
+ shell_a11y_init ();
+ shell_perf_log_init ();
+ shell_introspection_init ();
+ shell_fonts_init ();
+
+ g_log_set_writer_func (default_log_writer, NULL, NULL);
+
+ /* Initialize the global object */
+ if (session_mode == NULL)
+ session_mode = is_gdm_mode ? (char *)"gdm" : (char *)"user";
+
+ _shell_global_init ("session-mode", session_mode, NULL);
+
+ dump_gjs_stack_on_signal (SIGABRT);
+ dump_gjs_stack_on_signal (SIGFPE);
+ dump_gjs_stack_on_signal (SIGIOT);
+ dump_gjs_stack_on_signal (SIGTRAP);
+
+ if ((_shell_debug & SHELL_DEBUG_BACKTRACE_SEGFAULTS))
+ {
+ dump_gjs_stack_on_signal (SIGBUS);
+ dump_gjs_stack_on_signal (SIGSEGV);
+ }
+
+ shell_profiler_init ();
+
+ if (meta_context_get_compositor_type (context) == META_COMPOSITOR_TYPE_WAYLAND)
+ meta_context_raise_rlimit_nofile (context, NULL);
+
+ if (!meta_context_start (context, &error))
+ {
+ g_printerr ("GNOME Shell failed to start: %s", error->message);
+ return EXIT_FAILURE;
+ }
+
+ if (!meta_context_run_main_loop (context, &error))
+ {
+ g_printerr ("GNOME Shell terminated with an error: %s", error->message);
+ ecode = EXIT_FAILURE;
+ }
+
+ meta_context_destroy (g_steal_pointer (&context));
+
+ shell_profiler_shutdown ();
+
+#if 0
+ g_debug ("Doing final cleanup");
+ _shell_global_destroy_gjs_context (shell_global_get ());
+ g_object_unref (shell_global_get ());
+#endif
+
+ return ecode;
+}
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..fc7f8bf
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,276 @@
+service_data = configuration_data()
+service_data.set('libexecdir', libexecdir)
+
+subdir('calendar-server')
+subdir('hotplug-sniffer')
+subdir('st')
+subdir('tray')
+
+script_data = configuration_data()
+script_data.set('bindir', bindir)
+script_data.set('datadir', datadir)
+script_data.set('libdir', libdir)
+script_data.set('libexecdir', libexecdir)
+script_data.set('pkgdatadir', pkgdatadir)
+script_data.set('pkglibdir', pkglibdir)
+script_data.set('PYTHON', python.full_path())
+script_data.set('VERSION', meson.project_version())
+
+script_tools = ['gnome-shell-perf-tool']
+
+if get_option('extensions_tool')
+ script_tools += 'gnome-shell-extension-tool'
+endif
+
+foreach tool : script_tools
+ configure_file(
+ input: tool + '.in',
+ output: tool,
+ configuration: script_data,
+ install_dir: bindir
+ )
+endforeach
+
+install_data('gnome-shell-extension-prefs',
+ install_dir: bindir
+)
+
+gnome_shell_cflags = [
+ '-DCLUTTER_ENABLE_EXPERIMENTAL_API',
+ '-DCOGL_ENABLE_EXPERIMENTAL_API',
+ '-DVERSION="@0@"'.format(meson.project_version()),
+ '-DLOCALEDIR="@0@"'.format(localedir),
+ '-DDATADIR="@0@"'.format(datadir),
+ '-DGNOME_SHELL_LIBEXECDIR="@0@"'.format(libexecdir),
+ '-DGNOME_SHELL_DATADIR="@0@"'.format(pkgdatadir),
+ '-DGNOME_SHELL_PKGLIBDIR="@0@"'.format(pkglibdir)
+]
+
+install_rpath = ':'.join([mutter_typelibdir, pkglibdir])
+
+gnome_shell_deps = [
+ gio_unix_dep,
+ libxml_dep,
+ gtk_dep,
+ atk_bridge_dep,
+ gjs_dep,
+ gdk_x11_dep,
+ clutter_dep,
+ cogl_pango_dep,
+ startup_dep,
+ gi_dep,
+ polkit_dep,
+ gcr_dep,
+ libsystemd_dep
+]
+
+gnome_shell_deps += nm_deps
+
+tools_cflags = '-DLOCALEDIR="@0@"'.format(localedir)
+tools_deps = [gio_dep, gjs_dep]
+
+libshell_menu_sources = [
+ 'gtkactionmuxer.h',
+ 'gtkactionmuxer.c',
+ 'gtkactionobservable.h',
+ 'gtkactionobservable.c',
+ 'gtkactionobserver.h',
+ 'gtkactionobserver.c'
+]
+
+libshell_menu = library('gnome-shell-menu',
+ sources: libshell_menu_sources,
+ dependencies: [gio_dep, clutter_dep],
+ include_directories: conf_inc,
+ build_rpath: mutter_typelibdir,
+ install_rpath: mutter_typelibdir,
+ install_dir: pkglibdir,
+ install: true
+)
+
+libshell_menu_dep = declare_dependency(link_with: libshell_menu)
+
+libshell_public_headers = [
+ 'shell-app.h',
+ 'shell-app-system.h',
+ 'shell-app-usage.h',
+ 'shell-blur-effect.h',
+ 'shell-embedded-window.h',
+ 'shell-glsl-effect.h',
+ 'shell-gtk-embed.h',
+ 'shell-global.h',
+ 'shell-invert-lightness-effect.h',
+ 'shell-action-modes.h',
+ 'shell-mount-operation.h',
+ 'shell-perf-log.h',
+ 'shell-screenshot.h',
+ 'shell-square-bin.h',
+ 'shell-stack.h',
+ 'shell-tray-icon.h',
+ 'shell-tray-manager.h',
+ 'shell-util.h',
+ 'shell-window-preview.h',
+ 'shell-window-preview-layout.h',
+ 'shell-window-tracker.h',
+ 'shell-wm.h',
+ 'shell-workspace-background.h'
+]
+
+if have_networkmanager
+ libshell_public_headers += 'shell-network-agent.h'
+endif
+
+libshell_private_headers = [
+ 'shell-app-private.h',
+ 'shell-app-cache-private.h',
+ 'shell-app-system-private.h',
+ 'shell-global-private.h',
+ 'shell-window-tracker-private.h',
+ 'shell-wm-private.h'
+]
+
+libshell_sources = [
+ 'gnome-shell-plugin.c',
+ 'shell-app.c',
+ 'shell-app-system.c',
+ 'shell-app-usage.c',
+ 'shell-blur-effect.c',
+ 'shell-embedded-window.c',
+ 'shell-embedded-window-private.h',
+ 'shell-global.c',
+ 'shell-glsl-effect.c',
+ 'shell-gtk-embed.c',
+ 'shell-invert-lightness-effect.c',
+ 'shell-keyring-prompt.c',
+ 'shell-keyring-prompt.h',
+ 'shell-mount-operation.c',
+ 'shell-perf-log.c',
+ 'shell-polkit-authentication-agent.c',
+ 'shell-polkit-authentication-agent.h',
+ 'shell-screenshot.c',
+ 'shell-secure-text-buffer.c',
+ 'shell-secure-text-buffer.h',
+ 'shell-square-bin.c',
+ 'shell-stack.c',
+ 'shell-tray-icon.c',
+ 'shell-tray-manager.c',
+ 'shell-util.c',
+ 'shell-window-preview.c',
+ 'shell-window-preview-layout.c',
+ 'shell-window-tracker.c',
+ 'shell-wm.c',
+ 'shell-workspace-background.c'
+]
+
+if have_networkmanager
+ libshell_sources += 'shell-network-agent.c'
+endif
+
+libshell_private_sources = [
+ 'shell-app-cache.c',
+]
+
+libshell_enums = gnome.mkenums_simple('shell-enum-types',
+ sources: libshell_public_headers
+)
+
+libshell_gir_sources = [
+ libshell_enums,
+ libshell_public_headers,
+ libshell_sources
+]
+
+libshell_no_gir_sources = [
+ js_resources,
+ libshell_private_headers,
+ libshell_private_sources
+]
+
+dbus_generated = gnome.gdbus_codegen('org-gtk-application',
+ 'org.gtk.Application.xml',
+ namespace: 'Shell'
+)
+
+dbus_generated += gnome.gdbus_codegen('switcheroo-control',
+ '../data/dbus-interfaces/net.hadess.SwitcherooControl.xml',
+ namespace: 'Shell'
+)
+
+libshell_no_gir_sources += dbus_generated
+
+libshell = library('gnome-shell',
+ sources: libshell_gir_sources + libshell_no_gir_sources,
+ dependencies: gnome_shell_deps + [libshell_menu_dep, libst_dep, mutter_dep, gnome_desktop_dep, m_dep],
+ include_directories: [conf_inc, st_inc, include_directories('tray')],
+ c_args: gnome_shell_cflags,
+ link_with: [libtray],
+ build_rpath: mutter_typelibdir,
+ install_rpath: install_rpath,
+ install_dir: pkglibdir,
+ install: true
+)
+
+libshell_dep = declare_dependency(link_with: libshell)
+
+libshell_gir_includes = [
+ 'Clutter-@0@'.format(mutter_api_version),
+ 'Meta-@0@'.format(mutter_api_version),
+ 'Gcr-4',
+ 'PolkitAgent-1.0'
+]
+
+if have_networkmanager
+ libshell_gir_includes += ['NM-1.0']
+endif
+
+libshell_gir_includes += [
+ libgvc_gir[0],
+ libst_gir[0]
+]
+
+gnome.generate_gir(libshell,
+ sources: libshell_gir_sources,
+ nsversion: '0.1',
+ namespace: 'Shell',
+ includes: libshell_gir_includes,
+ extra_args: ['--quiet'],
+ install_dir_gir: pkgdatadir,
+ install_dir_typelib: pkglibdir,
+ install: true
+)
+
+executable('gnome-shell', 'main.c',
+ c_args: gnome_shell_cflags + [
+ '-DMUTTER_TYPELIB_DIR="@0@"'.format(mutter_typelibdir)
+ ],
+ dependencies: gnome_shell_deps + [libshell_dep, libst_dep, mutter_dep],
+ include_directories: [conf_inc, st_inc, include_directories('tray')],
+ build_rpath: mutter_typelibdir,
+ install_rpath: install_rpath,
+ install: true
+)
+
+if have_networkmanager
+ executable('gnome-shell-portal-helper',
+ 'gnome-shell-portal-helper.c', portal_resources,
+ c_args: tools_cflags,
+ dependencies: tools_deps,
+ include_directories: [conf_inc],
+ install_dir: libexecdir,
+ install: true
+ )
+endif
+
+executable('gnome-shell-perf-helper', 'shell-perf-helper.c',
+ dependencies: [gtk_dep, gio_dep, m_dep],
+ include_directories: [conf_inc],
+ install_dir: libexecdir,
+ install: true
+)
+
+executable('run-js-test', 'run-js-test.c',
+ dependencies: [mutter_dep, gio_dep, gi_dep, gjs_dep],
+ include_directories: [conf_inc],
+ link_with: libshell,
+ build_rpath: mutter_typelibdir,
+)
diff --git a/src/org.gtk.Application.xml b/src/org.gtk.Application.xml
new file mode 100644
index 0000000..161aa1d
--- /dev/null
+++ b/src/org.gtk.Application.xml
@@ -0,0 +1,19 @@
+<node>
+ <interface name='org.gtk.Application'>
+ <method name='Activate'>
+ <arg type='a{sv}' name='platform_data' direction='in'/>
+ </method>
+ <method name='Open'>
+ <arg type='as' name='uris' direction='in'/>
+ <arg type='s' name='hint' direction='in'/>
+ <arg type='a{sv}' name='platform_data' direction='in'/>
+ </method>
+ <method name='CommandLine'>
+ <arg type='o' name='path' direction='in'/>
+ <arg type='aay' name='arguments' direction='in'/>
+ <arg type='a{sv}' name='platform_data' direction='in'/>
+ <arg type='i' name='exit_status' direction='out'/>
+ </method>
+ <property name='Busy' type='b' access='read'/>
+ </interface>
+</node>
diff --git a/src/run-js-test.c b/src/run-js-test.c
new file mode 100644
index 0000000..ba5e875
--- /dev/null
+++ b/src/run-js-test.c
@@ -0,0 +1,118 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Based on gjs/console.c from GJS
+ *
+ * Copyright (c) 2008 litl, LLC
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <girepository.h>
+#include <gjs/gjs.h>
+
+#include "shell-global.h"
+#include "shell-global-private.h"
+
+static char *command = NULL;
+
+static GOptionEntry entries[] = {
+ { "command", 'c', 0, G_OPTION_ARG_STRING, &command, "Program passed in as a string", "COMMAND" },
+ { NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ ShellGlobal *global;
+ GjsContext *js_context;
+ char *script;
+ const char *filename;
+ char *title;
+ gsize len;
+ int code;
+
+ context = g_option_context_new (NULL);
+
+ /* pass unknown through to the JS script */
+ g_option_context_set_ignore_unknown_options (context, TRUE);
+
+ g_option_context_add_main_entries (context, entries, NULL);
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ g_error ("option parsing failed: %s", error->message);
+
+ setlocale (LC_ALL, "");
+
+ _shell_global_init (NULL);
+ global = shell_global_get ();
+ js_context = _shell_global_get_gjs_context (global);
+
+ /* prepare command line arguments */
+ if (!gjs_context_define_string_array (js_context, "ARGV",
+ argc - 2, (const char**)argv + 2,
+ &error)) {
+ g_printerr ("Failed to defined ARGV: %s", error->message);
+ exit (1);
+ }
+
+ if (command != NULL) {
+ script = command;
+ len = strlen (script);
+ filename = "<command line>";
+ } else if (argc <= 1) {
+ script = g_strdup ("const Console = imports.console; Console.interact();");
+ len = strlen (script);
+ filename = "<stdin>";
+ } else /*if (argc >= 2)*/ {
+ error = NULL;
+ if (!g_file_get_contents (argv[1], &script, &len, &error)) {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+ filename = argv[1];
+ }
+
+ title = g_filename_display_basename (filename);
+ g_set_prgname (title);
+ g_free (title);
+
+ /* evaluate the script */
+ error = NULL;
+ if (!gjs_context_eval (js_context, script, len,
+ filename, &code, &error)) {
+ g_free (script);
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+
+ gjs_context_gc (js_context);
+ gjs_context_gc (js_context);
+
+ g_object_unref (js_context);
+ g_free (script);
+ exit (code);
+}
diff --git a/src/shell-action-modes.h b/src/shell-action-modes.h
new file mode 100644
index 0000000..edbdd16
--- /dev/null
+++ b/src/shell-action-modes.h
@@ -0,0 +1,35 @@
+/**
+ * ShellActionMode:
+ * @SHELL_ACTION_MODE_NONE: block action
+ * @SHELL_ACTION_MODE_NORMAL: allow action when in window mode,
+ * e.g. when the focus is in an application window
+ * @SHELL_ACTION_MODE_OVERVIEW: allow action while the overview
+ * is active
+ * @SHELL_ACTION_MODE_LOCK_SCREEN: allow action when the screen
+ * is locked, e.g. when the screen shield is shown
+ * @SHELL_ACTION_MODE_UNLOCK_SCREEN: allow action in the unlock
+ * dialog
+ * @SHELL_ACTION_MODE_LOGIN_SCREEN: allow action in the login screen
+ * @SHELL_ACTION_MODE_SYSTEM_MODAL: allow action when a system modal
+ * dialog (e.g. authentication or session dialogs) is open
+ * @SHELL_ACTION_MODE_LOOKING_GLASS: allow action in looking glass
+ * @SHELL_ACTION_MODE_POPUP: allow action while a shell menu is open
+ * @SHELL_ACTION_MODE_ALL: always allow action
+ *
+ * Controls in which GNOME Shell states an action (like keybindings and gestures)
+ * should be handled.
+*/
+typedef enum {
+ SHELL_ACTION_MODE_NONE = 0,
+ SHELL_ACTION_MODE_NORMAL = 1 << 0,
+ SHELL_ACTION_MODE_OVERVIEW = 1 << 1,
+ SHELL_ACTION_MODE_LOCK_SCREEN = 1 << 2,
+ SHELL_ACTION_MODE_UNLOCK_SCREEN = 1 << 3,
+ SHELL_ACTION_MODE_LOGIN_SCREEN = 1 << 4,
+ SHELL_ACTION_MODE_SYSTEM_MODAL = 1 << 5,
+ SHELL_ACTION_MODE_LOOKING_GLASS = 1 << 6,
+ SHELL_ACTION_MODE_POPUP = 1 << 7,
+
+ SHELL_ACTION_MODE_ALL = ~0,
+} ShellActionMode;
+
diff --git a/src/shell-app-cache-private.h b/src/shell-app-cache-private.h
new file mode 100644
index 0000000..b73094a
--- /dev/null
+++ b/src/shell-app-cache-private.h
@@ -0,0 +1,19 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_APP_CACHE_PRIVATE_H__
+#define __SHELL_APP_CACHE_PRIVATE_H__
+
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+
+#define SHELL_TYPE_APP_CACHE (shell_app_cache_get_type())
+
+G_DECLARE_FINAL_TYPE (ShellAppCache, shell_app_cache, SHELL, APP_CACHE, GObject)
+
+ShellAppCache *shell_app_cache_get_default (void);
+GList *shell_app_cache_get_all (ShellAppCache *cache);
+GDesktopAppInfo *shell_app_cache_get_info (ShellAppCache *cache,
+ const char *id);
+char *shell_app_cache_translate_folder (ShellAppCache *cache,
+ const char *name);
+
+#endif /* __SHELL_APP_CACHE_PRIVATE_H__ */
diff --git a/src/shell-app-cache.c b/src/shell-app-cache.c
new file mode 100644
index 0000000..44fc8b0
--- /dev/null
+++ b/src/shell-app-cache.c
@@ -0,0 +1,404 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include "shell-app-cache-private.h"
+
+/**
+ * SECTION:shell-app-cache
+ * @title: ShellAppCache
+ * @short_description: application information cache
+ *
+ * The #ShellAppCache is responsible for caching information about #GAppInfo
+ * to ensure that the compositor thread never needs to perform disk reads to
+ * access them. All of the work is done off-thread. When the new data has
+ * been loaded, a #ShellAppCache::changed signal is emitted.
+ *
+ * Additionally, the #ShellAppCache caches information about translations for
+ * directories. This allows translation provided in [Desktop Entry] GKeyFiles
+ * to be available when building StLabel and other elements without performing
+ * costly disk reads.
+ *
+ * Various monitors are used to keep this information up to date while the
+ * Shell is running.
+ */
+
+#define DEFAULT_TIMEOUT_SECONDS 5
+
+struct _ShellAppCache
+{
+ GObject parent_instance;
+
+ GAppInfoMonitor *monitor;
+ GPtrArray *dir_monitors;
+ GHashTable *folders;
+ GCancellable *cancellable;
+ GList *app_infos;
+
+ guint queued_update;
+};
+
+typedef struct
+{
+ GList *app_infos;
+ GHashTable *folders;
+} CacheState;
+
+G_DEFINE_TYPE (ShellAppCache, shell_app_cache, G_TYPE_OBJECT)
+
+enum {
+ CHANGED,
+ N_SIGNALS
+};
+
+static guint signals [N_SIGNALS];
+
+static void
+cache_state_free (CacheState *state)
+{
+ g_clear_pointer (&state->folders, g_hash_table_unref);
+ g_list_free_full (state->app_infos, g_object_unref);
+ g_free (state);
+}
+
+static CacheState *
+cache_state_new (void)
+{
+ CacheState *state;
+
+ state = g_new0 (CacheState, 1);
+ state->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ return g_steal_pointer (&state);
+}
+
+/**
+ * shell_app_cache_get_default:
+ *
+ * Gets the default #ShellAppCache.
+ *
+ * Returns: (transfer none): a #ShellAppCache
+ */
+ShellAppCache *
+shell_app_cache_get_default (void)
+{
+ static ShellAppCache *instance;
+
+ if (instance == NULL)
+ {
+ instance = g_object_new (SHELL_TYPE_APP_CACHE, NULL);
+ g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance);
+ }
+
+ return instance;
+}
+
+static void
+load_folder (GHashTable *folders,
+ const char *path)
+{
+ g_autoptr(GDir) dir = NULL;
+ const char *name;
+
+ g_assert (folders != NULL);
+ g_assert (path != NULL);
+
+ dir = g_dir_open (path, 0, NULL);
+ if (dir == NULL)
+ return;
+
+ while ((name = g_dir_read_name (dir)))
+ {
+ g_autofree gchar *filename = NULL;
+ g_autoptr(GKeyFile) keyfile = NULL;
+
+ /* First added wins */
+ if (g_hash_table_contains (folders, name))
+ continue;
+
+ filename = g_build_filename (path, name, NULL);
+ keyfile = g_key_file_new ();
+
+ if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL))
+ {
+ gchar *translated;
+
+ translated = g_key_file_get_locale_string (keyfile,
+ "Desktop Entry", "Name",
+ NULL, NULL);
+
+ if (translated != NULL)
+ g_hash_table_insert (folders, g_strdup (name), translated);
+ }
+ }
+}
+
+static void
+load_folders (GHashTable *folders)
+{
+ const char * const *dirs;
+ g_autofree gchar *userdir = NULL;
+ guint i;
+
+ g_assert (folders != NULL);
+
+ userdir = g_build_filename (g_get_user_data_dir (), "desktop-directories", NULL);
+ load_folder (folders, userdir);
+
+ dirs = g_get_system_data_dirs ();
+ for (i = 0; dirs[i] != NULL; i++)
+ {
+ g_autofree gchar *sysdir = g_build_filename (dirs[i], "desktop-directories", NULL);
+ load_folder (folders, sysdir);
+ }
+}
+
+static void
+shell_app_cache_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ CacheState *state;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SHELL_IS_APP_CACHE (source_object));
+
+ state = cache_state_new ();
+ state->app_infos = g_app_info_get_all ();
+ load_folders (state->folders);
+
+ g_task_return_pointer (task, state, (GDestroyNotify) cache_state_free);
+}
+
+static void
+apply_update_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ShellAppCache *cache = (ShellAppCache *)object;
+ g_autoptr(GError) error = NULL;
+ CacheState *state;
+
+ g_assert (SHELL_IS_APP_CACHE (cache));
+ g_assert (G_IS_TASK (result));
+ g_assert (user_data == NULL);
+
+ state = g_task_propagate_pointer (G_TASK (result), &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ g_list_free_full (cache->app_infos, g_object_unref);
+ cache->app_infos = g_steal_pointer (&state->app_infos);
+
+ g_clear_pointer (&cache->folders, g_hash_table_unref);
+ cache->folders = g_steal_pointer (&state->folders);
+
+ g_signal_emit (cache, signals[CHANGED], 0);
+
+ cache_state_free (state);
+}
+
+static gboolean
+shell_app_cache_do_update (gpointer user_data)
+{
+ ShellAppCache *cache = user_data;
+ g_autoptr(GTask) task = NULL;
+
+ cache->queued_update = 0;
+
+ /* Reset the cancellable state so we don't race with
+ * two updates coming back overlapped and applying the
+ * information in the wrong order.
+ */
+ g_cancellable_cancel (cache->cancellable);
+ g_clear_object (&cache->cancellable);
+ cache->cancellable = g_cancellable_new ();
+
+ task = g_task_new (cache, cache->cancellable, apply_update_cb, NULL);
+ g_task_set_source_tag (task, shell_app_cache_do_update);
+ g_task_run_in_thread (task, shell_app_cache_worker);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+shell_app_cache_queue_update (ShellAppCache *self)
+{
+ g_assert (SHELL_IS_APP_CACHE (self));
+
+ if (self->queued_update != 0)
+ g_source_remove (self->queued_update);
+
+ self->queued_update = g_timeout_add_seconds (DEFAULT_TIMEOUT_SECONDS,
+ shell_app_cache_do_update,
+ self);
+}
+
+static void
+monitor_desktop_directories_for_data_dir (ShellAppCache *self,
+ const gchar *directory)
+{
+ g_autofree gchar *subdir = NULL;
+ g_autoptr(GFile) file = NULL;
+ g_autoptr(GFileMonitor) monitor = NULL;
+
+ g_assert (SHELL_IS_APP_CACHE (self));
+
+ if (directory == NULL)
+ return;
+
+ subdir = g_build_filename (directory, "desktop-directories", NULL);
+ file = g_file_new_for_path (subdir);
+ monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+
+ if (monitor != NULL)
+ {
+ g_file_monitor_set_rate_limit (monitor, DEFAULT_TIMEOUT_SECONDS * 1000);
+ g_signal_connect_object (monitor,
+ "changed",
+ G_CALLBACK (shell_app_cache_queue_update),
+ self,
+ G_CONNECT_SWAPPED);
+ g_ptr_array_add (self->dir_monitors, g_steal_pointer (&monitor));
+ }
+}
+
+static void
+shell_app_cache_finalize (GObject *object)
+{
+ ShellAppCache *self = (ShellAppCache *)object;
+
+ g_clear_object (&self->monitor);
+
+ if (self->queued_update)
+ {
+ g_source_remove (self->queued_update);
+ self->queued_update = 0;
+ }
+
+ g_clear_pointer (&self->dir_monitors, g_ptr_array_unref);
+ g_clear_pointer (&self->folders, g_hash_table_unref);
+ g_list_free_full (self->app_infos, g_object_unref);
+
+ G_OBJECT_CLASS (shell_app_cache_parent_class)->finalize (object);
+}
+
+static void
+shell_app_cache_class_init (ShellAppCacheClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = shell_app_cache_finalize;
+
+ /**
+ * ShellAppCache::changed:
+ *
+ * The "changed" signal is emitted when the cache has updated
+ * information about installed applications.
+ */
+ signals [CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+shell_app_cache_init (ShellAppCache *self)
+{
+ const gchar * const *sysdirs;
+ guint i;
+
+ /* Monitor directories for translation changes */
+ self->dir_monitors = g_ptr_array_new_with_free_func (g_object_unref);
+ monitor_desktop_directories_for_data_dir (self, g_get_user_data_dir ());
+ sysdirs = g_get_system_data_dirs ();
+ for (i = 0; sysdirs[i] != NULL; i++)
+ monitor_desktop_directories_for_data_dir (self, sysdirs[i]);
+
+ /* Load translated directory names immediately */
+ self->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ load_folders (self->folders);
+
+ /* Setup AppMonitor to track changes */
+ self->monitor = g_app_info_monitor_get ();
+ g_signal_connect_object (self->monitor,
+ "changed",
+ G_CALLBACK (shell_app_cache_queue_update),
+ self,
+ G_CONNECT_SWAPPED);
+ self->app_infos = g_app_info_get_all ();
+}
+
+/**
+ * shell_app_cache_get_all:
+ * @cache: (nullable): a #ShellAppCache or %NULL
+ *
+ * Like g_app_info_get_all() but always returns a
+ * cached set of application info so the caller can be
+ * sure that I/O will not happen on the current thread.
+ *
+ * Returns: (transfer none) (element-type GAppInfo):
+ * a #GList of references to #GAppInfo.
+ */
+GList *
+shell_app_cache_get_all (ShellAppCache *cache)
+{
+ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL);
+
+ return cache->app_infos;
+}
+
+/**
+ * shell_app_cache_get_info:
+ * @cache: (nullable): a #ShellAppCache or %NULL
+ * @id: the application id
+ *
+ * A replacement for g_desktop_app_info_new() that will lookup the
+ * information from the cache instead of (re)loading from disk.
+ *
+ * Returns: (nullable) (transfer none): a #GDesktopAppInfo or %NULL
+ */
+GDesktopAppInfo *
+shell_app_cache_get_info (ShellAppCache *cache,
+ const char *id)
+{
+ const GList *iter;
+
+ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL);
+
+ for (iter = cache->app_infos; iter != NULL; iter = iter->next)
+ {
+ GAppInfo *info = iter->data;
+
+ if (g_strcmp0 (id, g_app_info_get_id (info)) == 0)
+ return G_DESKTOP_APP_INFO (info);
+ }
+
+ return NULL;
+}
+
+/**
+ * shell_app_cache_translate_folder:
+ * @cache: (nullable): a #ShellAppCache or %NULL
+ * @name: the folder name
+ *
+ * Gets the translated folder name for @name if any exists.
+ *
+ * Returns: (nullable): the translated string or %NULL if there is no
+ * translation.
+ */
+char *
+shell_app_cache_translate_folder (ShellAppCache *cache,
+ const char *name)
+{
+ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL);
+
+ if (name == NULL)
+ return NULL;
+
+ return g_strdup (g_hash_table_lookup (cache->folders, name));
+}
diff --git a/src/shell-app-private.h b/src/shell-app-private.h
new file mode 100644
index 0000000..b1786b3
--- /dev/null
+++ b/src/shell-app-private.h
@@ -0,0 +1,24 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_APP_PRIVATE_H__
+#define __SHELL_APP_PRIVATE_H__
+
+#include "shell-app.h"
+#include "shell-app-system.h"
+
+G_BEGIN_DECLS
+
+ShellApp* _shell_app_new_for_window (MetaWindow *window);
+
+ShellApp* _shell_app_new (GDesktopAppInfo *info);
+
+void _shell_app_set_app_info (ShellApp *app, GDesktopAppInfo *info);
+
+void _shell_app_handle_startup_sequence (ShellApp *app, MetaStartupSequence *sequence);
+
+void _shell_app_add_window (ShellApp *app, MetaWindow *window);
+
+void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
+
+G_END_DECLS
+
+#endif /* __SHELL_APP_PRIVATE_H__ */
diff --git a/src/shell-app-system-private.h b/src/shell-app-system-private.h
new file mode 100644
index 0000000..975d563
--- /dev/null
+++ b/src/shell-app-system-private.h
@@ -0,0 +1,9 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_APP_SYSTEM_PRIVATE_H__
+#define __SHELL_APP_SYSTEM_PRIVATE_H__
+
+#include "shell-app-system.h"
+
+void _shell_app_system_notify_app_state_changed (ShellAppSystem *self, ShellApp *app);
+
+#endif
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
new file mode 100644
index 0000000..2899544
--- /dev/null
+++ b/src/shell-app-system.c
@@ -0,0 +1,586 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include "shell-app-system.h"
+#include "shell-app-usage.h"
+#include <string.h>
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#include "shell-app-cache-private.h"
+#include "shell-app-private.h"
+#include "shell-window-tracker-private.h"
+#include "shell-app-system-private.h"
+#include "shell-global.h"
+#include "shell-util.h"
+#include "st.h"
+
+/* Rescan for at most RESCAN_TIMEOUT_MS * MAX_RESCAN_RETRIES. That
+ * should be plenty of time for even a slow spinning drive to update
+ * the icon cache.
+ */
+#define RESCAN_TIMEOUT_MS 2500
+#define MAX_RESCAN_RETRIES 6
+
+/* Vendor prefixes are something that can be preprended to a .desktop
+ * file name. Undo this.
+ */
+static const char*const vendor_prefixes[] = { "gnome-",
+ "fedora-",
+ "mozilla-",
+ "debian-",
+ NULL };
+
+enum {
+ PROP_0,
+
+};
+
+enum {
+ APP_STATE_CHANGED,
+ INSTALLED_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct _ShellAppSystemPrivate ShellAppSystemPrivate;
+
+struct _ShellAppSystem
+{
+ GObject parent;
+
+ ShellAppSystemPrivate *priv;
+};
+
+struct _ShellAppSystemPrivate {
+ GHashTable *running_apps;
+ GHashTable *id_to_app;
+ GHashTable *startup_wm_class_to_id;
+ GList *installed_apps;
+
+ guint rescan_icons_timeout_id;
+ guint n_rescan_retries;
+};
+
+static void shell_app_system_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
+
+static void shell_app_system_class_init(ShellAppSystemClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *)klass;
+
+ gobject_class->finalize = shell_app_system_finalize;
+
+ signals[APP_STATE_CHANGED] = g_signal_new ("app-state-changed",
+ SHELL_TYPE_APP_SYSTEM,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ SHELL_TYPE_APP);
+ signals[INSTALLED_CHANGED] =
+ g_signal_new ("installed-changed",
+ SHELL_TYPE_APP_SYSTEM,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+/*
+ * Check whether @wm_class matches @id exactly when ignoring the .desktop suffix
+ */
+static gboolean
+startup_wm_class_is_exact_match (const char *id,
+ const char *wm_class)
+{
+ size_t wm_class_len;
+
+ if (!g_str_has_prefix (id, wm_class))
+ return FALSE;
+
+ wm_class_len = strlen (wm_class);
+ if (id[wm_class_len] == '\0')
+ return TRUE;
+
+ return g_str_equal (id + wm_class_len, ".desktop");
+}
+
+static void
+scan_startup_wm_class_to_id (ShellAppSystem *self)
+{
+ ShellAppSystemPrivate *priv = self->priv;
+ g_autoptr(GPtrArray) no_show_ids = NULL;
+ const GList *l;
+ GList *all;
+
+ g_hash_table_remove_all (priv->startup_wm_class_to_id);
+
+ all = shell_app_cache_get_all (shell_app_cache_get_default ());
+ no_show_ids = g_ptr_array_new ();
+
+ for (l = all; l != NULL; l = l->next)
+ {
+ GAppInfo *info = l->data;
+ const char *startup_wm_class, *id, *old_id;
+ gboolean should_show;
+
+ id = g_app_info_get_id (info);
+ startup_wm_class = g_desktop_app_info_get_startup_wm_class (G_DESKTOP_APP_INFO (info));
+
+ if (startup_wm_class == NULL)
+ continue;
+
+ should_show = g_app_info_should_show (info);
+ if (!should_show)
+ g_ptr_array_add (no_show_ids, (char *) id);
+
+ /* In case multiple .desktop files set the same StartupWMClass, prefer
+ * the one where ID and StartupWMClass match */
+ old_id = g_hash_table_lookup (priv->startup_wm_class_to_id, startup_wm_class);
+
+ if (old_id && startup_wm_class_is_exact_match (id, startup_wm_class))
+ old_id = NULL;
+
+ /* Give priority to the desktop files that should be shown */
+ if (old_id && should_show &&
+ g_ptr_array_find_with_equal_func (no_show_ids, old_id, g_str_equal, NULL))
+ old_id = NULL;
+
+ if (!old_id)
+ g_hash_table_insert (priv->startup_wm_class_to_id,
+ g_strdup (startup_wm_class), g_strdup (id));
+ }
+}
+
+static gboolean
+app_is_stale (ShellApp *app)
+{
+ GDesktopAppInfo *info, *old;
+ GAppInfo *old_info, *new_info;
+ gboolean is_unchanged;
+
+ if (shell_app_is_window_backed (app))
+ return FALSE;
+
+ info = shell_app_cache_get_info (shell_app_cache_get_default (),
+ shell_app_get_id (app));
+ if (!info)
+ return TRUE;
+
+ old = shell_app_get_app_info (app);
+ old_info = G_APP_INFO (old);
+ new_info = G_APP_INFO (info);
+
+ is_unchanged =
+ g_app_info_should_show (old_info) == g_app_info_should_show (new_info) &&
+ strcmp (g_desktop_app_info_get_filename (old),
+ g_desktop_app_info_get_filename (info)) == 0 &&
+ g_strcmp0 (g_app_info_get_executable (old_info),
+ g_app_info_get_executable (new_info)) == 0 &&
+ g_strcmp0 (g_app_info_get_commandline (old_info),
+ g_app_info_get_commandline (new_info)) == 0 &&
+ strcmp (g_app_info_get_name (old_info),
+ g_app_info_get_name (new_info)) == 0 &&
+ g_strcmp0 (g_app_info_get_description (old_info),
+ g_app_info_get_description (new_info)) == 0 &&
+ strcmp (g_app_info_get_display_name (old_info),
+ g_app_info_get_display_name (new_info)) == 0 &&
+ g_icon_equal (g_app_info_get_icon (old_info),
+ g_app_info_get_icon (new_info));
+
+ return !is_unchanged;
+}
+
+static gboolean
+stale_app_remove_func (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ return app_is_stale (value);
+}
+
+static void
+collect_stale_windows (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ ShellApp *app = key;
+ GDesktopAppInfo *info;
+ GPtrArray *windows = user_data;
+
+ info = shell_app_cache_get_info (shell_app_cache_get_default (),
+ shell_app_get_id (app));
+
+ /* No info either means that the app became stale, or that it is
+ * window-backed. Re-tracking the app's windows allows us to reflect
+ * changes in either direction, i.e. from stale app to window-backed,
+ * or from window-backed to app-backed (if the app was launched right
+ * between installing the app and updating the app cache).
+ */
+ if (info == NULL)
+ {
+ GSList *l;
+
+ for (l = shell_app_get_windows (app); l; l = l->next)
+ g_ptr_array_add (windows, l->data);
+ }
+}
+
+static void
+retrack_window (gpointer data,
+ gpointer user_data)
+{
+ GObject *window = data;
+
+ /* Make ShellWindowTracker retrack the window */
+ g_object_notify (window, "wm-class");
+}
+
+static gboolean
+rescan_icon_theme_cb (gpointer user_data)
+{
+ ShellAppSystemPrivate *priv;
+ ShellAppSystem *self;
+ StTextureCache *texture_cache;
+ gboolean rescanned;
+
+ self = (ShellAppSystem *) user_data;
+ priv = self->priv;
+
+ texture_cache = st_texture_cache_get_default ();
+ rescanned = st_texture_cache_rescan_icon_theme (texture_cache);
+
+ priv->n_rescan_retries++;
+
+ if (rescanned || priv->n_rescan_retries >= MAX_RESCAN_RETRIES)
+ {
+ priv->n_rescan_retries = 0;
+ priv->rescan_icons_timeout_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+rescan_icon_theme (ShellAppSystem *self)
+{
+ ShellAppSystemPrivate *priv = self->priv;
+
+ priv->n_rescan_retries = 0;
+
+ if (priv->rescan_icons_timeout_id > 0)
+ return;
+
+ priv->rescan_icons_timeout_id = g_timeout_add (RESCAN_TIMEOUT_MS,
+ rescan_icon_theme_cb,
+ self);
+}
+
+static void
+installed_changed (ShellAppCache *cache,
+ ShellAppSystem *self)
+{
+ GPtrArray *windows = g_ptr_array_new ();
+
+ rescan_icon_theme (self);
+ scan_startup_wm_class_to_id (self);
+
+ g_hash_table_foreach_remove (self->priv->id_to_app, stale_app_remove_func, NULL);
+ g_hash_table_foreach (self->priv->running_apps, collect_stale_windows, windows);
+
+ g_ptr_array_foreach (windows, retrack_window, NULL);
+ g_ptr_array_free (windows, TRUE);
+
+ g_signal_emit (self, signals[INSTALLED_CHANGED], 0, NULL);
+}
+
+static void
+shell_app_system_init (ShellAppSystem *self)
+{
+ ShellAppSystemPrivate *priv;
+ ShellAppCache *cache;
+
+ self->priv = priv = shell_app_system_get_instance_private (self);
+
+ priv->running_apps = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) g_object_unref, NULL);
+ priv->id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ (GDestroyNotify)g_object_unref);
+
+ priv->startup_wm_class_to_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ cache = shell_app_cache_get_default ();
+ g_signal_connect (cache, "changed", G_CALLBACK (installed_changed), self);
+ installed_changed (cache, self);
+}
+
+static void
+shell_app_system_finalize (GObject *object)
+{
+ ShellAppSystem *self = SHELL_APP_SYSTEM (object);
+ ShellAppSystemPrivate *priv = self->priv;
+
+ g_hash_table_destroy (priv->running_apps);
+ g_hash_table_destroy (priv->id_to_app);
+ g_hash_table_destroy (priv->startup_wm_class_to_id);
+ g_list_free_full (priv->installed_apps, g_object_unref);
+ g_clear_handle_id (&priv->rescan_icons_timeout_id, g_source_remove);
+
+ G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
+}
+
+/**
+ * shell_app_system_get_default:
+ *
+ * Return Value: (transfer none): The global #ShellAppSystem singleton
+ */
+ShellAppSystem *
+shell_app_system_get_default (void)
+{
+ static ShellAppSystem *instance = NULL;
+
+ if (instance == NULL)
+ instance = g_object_new (SHELL_TYPE_APP_SYSTEM, NULL);
+
+ return instance;
+}
+
+/**
+ * shell_app_system_lookup_app:
+ *
+ * Find a #ShellApp corresponding to an id.
+ *
+ * Return value: (transfer none): The #ShellApp for id, or %NULL if none
+ */
+ShellApp *
+shell_app_system_lookup_app (ShellAppSystem *self,
+ const char *id)
+{
+ ShellAppSystemPrivate *priv = self->priv;
+ ShellApp *app;
+ GDesktopAppInfo *info;
+
+ app = g_hash_table_lookup (priv->id_to_app, id);
+ if (app)
+ return app;
+
+ info = shell_app_cache_get_info (shell_app_cache_get_default (), id);
+ if (!info)
+ return NULL;
+
+ app = _shell_app_new (info);
+ g_hash_table_insert (priv->id_to_app, (char *) shell_app_get_id (app), app);
+ return app;
+}
+
+/**
+ * shell_app_system_lookup_heuristic_basename:
+ * @system: a #ShellAppSystem
+ * @id: Probable application identifier
+ *
+ * Find a valid application corresponding to a given
+ * heuristically determined application identifier
+ * string, or %NULL if none.
+ *
+ * Returns: (transfer none): A #ShellApp for @name
+ */
+ShellApp *
+shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
+ const char *name)
+{
+ ShellApp *result;
+ const char *const *prefix;
+
+ result = shell_app_system_lookup_app (system, name);
+ if (result != NULL)
+ return result;
+
+ for (prefix = vendor_prefixes; *prefix != NULL; prefix++)
+ {
+ char *tmpid = g_strconcat (*prefix, name, NULL);
+ result = shell_app_system_lookup_app (system, tmpid);
+ g_free (tmpid);
+ if (result != NULL)
+ return result;
+ }
+
+ return NULL;
+}
+
+/**
+ * shell_app_system_lookup_desktop_wmclass:
+ * @system: a #ShellAppSystem
+ * @wmclass: (nullable): A WM_CLASS value
+ *
+ * Find a valid application whose .desktop file, without the extension
+ * and properly canonicalized, matches @wmclass.
+ *
+ * Returns: (transfer none): A #ShellApp for @wmclass
+ */
+ShellApp *
+shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system,
+ const char *wmclass)
+{
+ char *canonicalized;
+ char *desktop_file;
+ ShellApp *app;
+
+ if (wmclass == NULL)
+ return NULL;
+
+ /* First try without changing the case (this handles
+ org.example.Foo.Bar.desktop applications)
+
+ Note that is slightly wrong in that Gtk+ would set
+ the WM_CLASS to Org.example.Foo.Bar, but it also
+ sets the instance part to org.example.Foo.Bar, so we're ok
+ */
+ desktop_file = g_strconcat (wmclass, ".desktop", NULL);
+ app = shell_app_system_lookup_heuristic_basename (system, desktop_file);
+ g_free (desktop_file);
+
+ if (app)
+ return app;
+
+ canonicalized = g_ascii_strdown (wmclass, -1);
+
+ /* This handles "Fedora Eclipse", probably others.
+ * Note g_strdelimit is modify-in-place. */
+ g_strdelimit (canonicalized, " ", '-');
+
+ desktop_file = g_strconcat (canonicalized, ".desktop", NULL);
+
+ app = shell_app_system_lookup_heuristic_basename (system, desktop_file);
+
+ g_free (canonicalized);
+ g_free (desktop_file);
+
+ return app;
+}
+
+/**
+ * shell_app_system_lookup_startup_wmclass:
+ * @system: a #ShellAppSystem
+ * @wmclass: (nullable): A WM_CLASS value
+ *
+ * Find a valid application whose .desktop file contains a
+ * StartupWMClass entry matching @wmclass.
+ *
+ * Returns: (transfer none): A #ShellApp for @wmclass
+ */
+ShellApp *
+shell_app_system_lookup_startup_wmclass (ShellAppSystem *system,
+ const char *wmclass)
+{
+ const char *id;
+
+ if (wmclass == NULL)
+ return NULL;
+
+ id = g_hash_table_lookup (system->priv->startup_wm_class_to_id, wmclass);
+ if (id == NULL)
+ return NULL;
+
+ return shell_app_system_lookup_app (system, id);
+}
+
+void
+_shell_app_system_notify_app_state_changed (ShellAppSystem *self,
+ ShellApp *app)
+{
+ ShellAppState state = shell_app_get_state (app);
+
+ switch (state)
+ {
+ case SHELL_APP_STATE_RUNNING:
+ g_hash_table_insert (self->priv->running_apps, g_object_ref (app), NULL);
+ break;
+ case SHELL_APP_STATE_STARTING:
+ break;
+ case SHELL_APP_STATE_STOPPED:
+ g_hash_table_remove (self->priv->running_apps, app);
+ break;
+ default:
+ g_warn_if_reached();
+ break;
+ }
+ g_signal_emit (self, signals[APP_STATE_CHANGED], 0, app);
+}
+
+/**
+ * shell_app_system_get_running:
+ * @self: A #ShellAppSystem
+ *
+ * Returns the set of applications which currently have at least one
+ * open window. The returned list will be sorted by shell_app_compare().
+ *
+ * Returns: (element-type ShellApp) (transfer container): Active applications
+ */
+GSList *
+shell_app_system_get_running (ShellAppSystem *self)
+{
+ gpointer key, value;
+ GSList *ret;
+ GHashTableIter iter;
+
+ g_hash_table_iter_init (&iter, self->priv->running_apps);
+
+ ret = NULL;
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ ShellApp *app = key;
+
+ ret = g_slist_prepend (ret, app);
+ }
+
+ ret = g_slist_sort (ret, (GCompareFunc)shell_app_compare);
+
+ return ret;
+}
+
+/**
+ * shell_app_system_search:
+ * @search_string: the search string to use
+ *
+ * Wrapper around g_desktop_app_info_search() that replaces results that
+ * don't validate as UTF-8 with the empty string.
+ *
+ * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a
+ * list of strvs. Free each item with g_strfreev() and free the outer
+ * list with g_free().
+ */
+char ***
+shell_app_system_search (const char *search_string)
+{
+ char ***results = g_desktop_app_info_search (search_string);
+ char ***groups, **ids;
+
+ for (groups = results; *groups; groups++)
+ for (ids = *groups; *ids; ids++)
+ if (!g_utf8_validate (*ids, -1, NULL))
+ **ids = '\0';
+
+ return results;
+}
+
+/**
+ * shell_app_system_get_installed:
+ * @self: the #ShellAppSystem
+ *
+ * Returns all installed apps, as a list of #GAppInfo
+ *
+ * Returns: (transfer none) (element-type GAppInfo): a list of #GAppInfo
+ * describing all known applications. This memory is owned by the
+ * #ShellAppSystem and should not be freed.
+ **/
+GList *
+shell_app_system_get_installed (ShellAppSystem *self)
+{
+ return shell_app_cache_get_all (shell_app_cache_get_default ());
+}
diff --git a/src/shell-app-system.h b/src/shell-app-system.h
new file mode 100644
index 0000000..8719dbc
--- /dev/null
+++ b/src/shell-app-system.h
@@ -0,0 +1,32 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_APP_SYSTEM_H__
+#define __SHELL_APP_SYSTEM_H__
+
+#include <gio/gio.h>
+#include <clutter/clutter.h>
+#include <meta/window.h>
+
+#include "shell-app.h"
+
+#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
+G_DECLARE_FINAL_TYPE (ShellAppSystem, shell_app_system,
+ SHELL, APP_SYSTEM, GObject)
+
+ShellAppSystem *shell_app_system_get_default (void);
+
+ShellApp *shell_app_system_lookup_app (ShellAppSystem *system,
+ const char *id);
+ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
+ const char *id);
+
+ShellApp *shell_app_system_lookup_startup_wmclass (ShellAppSystem *system,
+ const char *wmclass);
+ShellApp *shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system,
+ const char *wmclass);
+
+GSList *shell_app_system_get_running (ShellAppSystem *self);
+char ***shell_app_system_search (const char *search_string);
+
+GList *shell_app_system_get_installed (ShellAppSystem *self);
+
+#endif /* __SHELL_APP_SYSTEM_H__ */
diff --git a/src/shell-app-usage.c b/src/shell-app-usage.c
new file mode 100644
index 0000000..0dfb209
--- /dev/null
+++ b/src/shell-app-usage.c
@@ -0,0 +1,774 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <meta/display.h>
+#include <meta/group.h>
+#include <meta/window.h>
+
+#include "shell-app-usage.h"
+#include "shell-window-tracker.h"
+#include "shell-global.h"
+
+/* This file includes modified code from
+ * desktop-data-engine/engine-dbus/hippo-application-monitor.c
+ * in the functions collecting application usage data.
+ * Written by Owen Taylor, originally licensed under LGPL 2.1.
+ * Copyright Red Hat, Inc. 2006-2008
+ */
+
+/**
+ * SECTION:shell-app-usage
+ * @short_description: Track application usage/state data
+ *
+ * This class maintains some usage and state statistics for
+ * applications by keeping track of the approximate time an application's
+ * windows are focused, as well as the last workspace it was seen on.
+ * This time tracking is implemented by watching for focus notifications,
+ * and computing a time delta between them. Also we watch the
+ * GNOME Session "StatusChanged" signal which by default is emitted after 5
+ * minutes to signify idle.
+ */
+
+#define PRIVACY_SCHEMA "org.gnome.desktop.privacy"
+#define ENABLE_MONITORING_KEY "remember-app-usage"
+
+#define FOCUS_TIME_MIN_SECONDS 7 /* Need 7 continuous seconds of focus */
+
+#define USAGE_CLEAN_DAYS 7 /* If after 7 days we haven't seen an app, purge it */
+
+/* Data is saved to file SHELL_CONFIG_DIR/DATA_FILENAME */
+#define DATA_FILENAME "application_state"
+
+#define IDLE_TIME_TRANSITION_SECONDS 30 /* If we transition to idle, only count
+ * this many seconds of usage */
+
+/* The ranking algorithm we use is: every time an app score reaches SCORE_MAX,
+ * divide all scores by 2. Scores are raised by 1 unit every SAVE_APPS_TIMEOUT
+ * seconds. This mechanism allows the list to update relatively fast when
+ * a new app is used intensively.
+ * To keep the list clean, and avoid being Big Brother, apps that have not been
+ * seen for a week and whose score is below SCORE_MIN are removed.
+ */
+
+/* How often we save internally app data, in seconds */
+#define SAVE_APPS_TIMEOUT_SECONDS (5 * 60)
+
+/* With this value, an app goes from bottom to top of the
+ * usage list in 50 hours of use */
+#define SCORE_MAX (3600 * 50 / FOCUS_TIME_MIN_SECONDS)
+
+/* If an app's score in lower than this and the app has not been used in a week,
+ * remove it */
+#define SCORE_MIN (SCORE_MAX >> 3)
+
+/* http://www.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Presence */
+#define GNOME_SESSION_STATUS_IDLE 3
+
+typedef struct UsageData UsageData;
+
+struct _ShellAppUsage
+{
+ GObject parent;
+
+ GFile *configfile;
+ GDBusProxy *session_proxy;
+ GSettings *privacy_settings;
+ guint idle_focus_change_id;
+ guint save_id;
+ gboolean currently_idle;
+ gboolean enable_monitoring;
+
+ long watch_start_time;
+ ShellApp *watched_app;
+
+ /* <char *appid, UsageData *usage> */
+ GHashTable *app_usages;
+};
+
+G_DEFINE_TYPE (ShellAppUsage, shell_app_usage, G_TYPE_OBJECT);
+
+/* Represents an application record for a given context */
+struct UsageData
+{
+ gdouble score; /* Based on the number of times we'e seen the app and normalized */
+ long last_seen; /* Used to clear old apps we've only seen a few times */
+};
+
+static void shell_app_usage_finalize (GObject *object);
+
+static void on_session_status_changed (GDBusProxy *proxy, guint status, ShellAppUsage *self);
+static void on_focus_app_changed (ShellWindowTracker *tracker, GParamSpec *spec, ShellAppUsage *self);
+static void ensure_queued_save (ShellAppUsage *self);
+
+static gboolean idle_save_application_usage (gpointer data);
+
+static void restore_from_file (ShellAppUsage *self);
+
+static void update_enable_monitoring (ShellAppUsage *self);
+
+static void on_enable_monitoring_key_changed (GSettings *settings,
+ const gchar *key,
+ ShellAppUsage *self);
+
+static long
+get_time (void)
+{
+ return g_get_real_time () / G_TIME_SPAN_SECOND;
+}
+
+static void
+shell_app_usage_class_init (ShellAppUsageClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = shell_app_usage_finalize;
+}
+
+static UsageData *
+get_usage_for_app (ShellAppUsage *self,
+ ShellApp *app)
+{
+ UsageData *usage;
+ const char *appid = shell_app_get_id (app);
+
+ usage = g_hash_table_lookup (self->app_usages, appid);
+ if (usage)
+ return usage;
+
+ usage = g_new0 (UsageData, 1);
+ g_hash_table_insert (self->app_usages, g_strdup (appid), usage);
+
+ return usage;
+}
+
+/* Limit the score to a certain level so that most used apps can change */
+static void
+normalize_usage (ShellAppUsage *self)
+{
+ GHashTableIter iter;
+ UsageData *usage;
+
+ g_hash_table_iter_init (&iter, self->app_usages);
+
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &usage))
+ usage->score /= 2;
+}
+
+static void
+increment_usage_for_app_at_time (ShellAppUsage *self,
+ ShellApp *app,
+ long time)
+{
+ UsageData *usage;
+ guint elapsed;
+ guint usage_count;
+
+ usage = get_usage_for_app (self, app);
+
+ usage->last_seen = time;
+
+ elapsed = time - self->watch_start_time;
+ usage_count = elapsed / FOCUS_TIME_MIN_SECONDS;
+ if (usage_count > 0)
+ {
+ usage->score += usage_count;
+ if (usage->score > SCORE_MAX)
+ normalize_usage (self);
+ ensure_queued_save (self);
+ }
+}
+
+static void
+increment_usage_for_app (ShellAppUsage *self,
+ ShellApp *app)
+{
+ long curtime = get_time ();
+ increment_usage_for_app_at_time (self, app, curtime);
+}
+
+static void
+on_app_state_changed (ShellAppSystem *app_system,
+ ShellApp *app,
+ gpointer user_data)
+{
+ ShellAppUsage *self = SHELL_APP_USAGE (user_data);
+ UsageData *usage;
+ gboolean running;
+
+ if (shell_app_is_window_backed (app))
+ return;
+
+ usage = get_usage_for_app (self, app);
+
+ running = shell_app_get_state (app) == SHELL_APP_STATE_RUNNING;
+
+ if (running)
+ usage->last_seen = get_time ();
+}
+
+static void
+on_focus_app_changed (ShellWindowTracker *tracker,
+ GParamSpec *spec,
+ ShellAppUsage *self)
+{
+ if (self->watched_app != NULL)
+ increment_usage_for_app (self, self->watched_app);
+
+ if (self->watched_app)
+ g_object_unref (self->watched_app);
+
+ g_object_get (tracker, "focus-app", &(self->watched_app), NULL);
+ self->watch_start_time = get_time ();
+}
+
+static void
+on_session_status_changed (GDBusProxy *proxy,
+ guint status,
+ ShellAppUsage *self)
+{
+ gboolean idle;
+
+ idle = (status >= GNOME_SESSION_STATUS_IDLE);
+ if (self->currently_idle == idle)
+ return;
+
+ self->currently_idle = idle;
+ if (idle)
+ {
+ long end_time;
+
+ /* The GNOME Session signal we watch is 5 minutes, but that's a long
+ * time for this purpose. Instead, just add a base 30 seconds.
+ */
+ if (self->watched_app)
+ {
+ end_time = self->watch_start_time + IDLE_TIME_TRANSITION_SECONDS;
+ increment_usage_for_app_at_time (self, self->watched_app, end_time);
+ }
+ }
+ else
+ {
+ /* Transitioning to !idle, reset the start time */
+ self->watch_start_time = get_time ();
+ }
+}
+
+static void
+session_proxy_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data)
+{
+ if (g_str_equal (signal_name, "StatusChanged"))
+ {
+ guint status;
+ g_variant_get (parameters, "(u)", &status);
+ on_session_status_changed (proxy, status, SHELL_APP_USAGE (user_data));
+ }
+}
+
+static void
+shell_app_usage_init (ShellAppUsage *self)
+{
+ ShellGlobal *global;
+ char *shell_userdata_dir, *path;
+ GDBusConnection *session_bus;
+ ShellWindowTracker *tracker;
+ ShellAppSystem *app_system;
+
+ global = shell_global_get ();
+
+ self->app_usages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ tracker = shell_window_tracker_get_default ();
+ g_signal_connect (tracker, "notify::focus-app", G_CALLBACK (on_focus_app_changed), self);
+
+ app_system = shell_app_system_get_default ();
+ g_signal_connect (app_system, "app-state-changed", G_CALLBACK (on_app_state_changed), self);
+
+ session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ self->session_proxy = g_dbus_proxy_new_sync (session_bus,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, /* interface info */
+ "org.gnome.SessionManager",
+ "/org/gnome/SessionManager/Presence",
+ "org.gnome.SessionManager",
+ NULL, /* cancellable */
+ NULL /* error */);
+ g_signal_connect (self->session_proxy, "g-signal", G_CALLBACK (session_proxy_signal), self);
+ g_object_unref (session_bus);
+
+ self->currently_idle = FALSE;
+ self->enable_monitoring = FALSE;
+
+ g_object_get (global, "userdatadir", &shell_userdata_dir, NULL),
+ path = g_build_filename (shell_userdata_dir, DATA_FILENAME, NULL);
+ g_free (shell_userdata_dir);
+ self->configfile = g_file_new_for_path (path);
+ g_free (path);
+ restore_from_file (self);
+
+ self->privacy_settings = g_settings_new(PRIVACY_SCHEMA);
+ g_signal_connect (self->privacy_settings,
+ "changed::" ENABLE_MONITORING_KEY,
+ G_CALLBACK (on_enable_monitoring_key_changed),
+ self);
+ update_enable_monitoring (self);
+}
+
+static void
+shell_app_usage_finalize (GObject *object)
+{
+ ShellAppUsage *self = SHELL_APP_USAGE (object);
+
+ g_clear_handle_id (&self->save_id, g_source_remove);
+
+ g_object_unref (self->privacy_settings);
+
+ g_object_unref (self->configfile);
+
+ g_object_unref (self->session_proxy);
+
+ G_OBJECT_CLASS (shell_app_usage_parent_class)->finalize(object);
+}
+
+static int
+sort_apps_by_usage (gconstpointer a,
+ gconstpointer b,
+ gpointer datap)
+{
+ ShellAppUsage *self = datap;
+ ShellApp *app_a, *app_b;
+ UsageData *usage_a, *usage_b;
+
+ app_a = (ShellApp*)a;
+ app_b = (ShellApp*)b;
+
+ usage_a = g_hash_table_lookup (self->app_usages, shell_app_get_id (app_a));
+ usage_b = g_hash_table_lookup (self->app_usages, shell_app_get_id (app_b));
+
+ return usage_b->score - usage_a->score;
+}
+
+/**
+ * shell_app_usage_get_most_used:
+ * @usage: the usage instance to request
+ *
+ * Returns: (element-type ShellApp) (transfer full): List of applications
+ */
+GSList *
+shell_app_usage_get_most_used (ShellAppUsage *self)
+{
+ GSList *apps;
+ char *appid;
+ ShellAppSystem *appsys;
+ GHashTableIter iter;
+
+ appsys = shell_app_system_get_default ();
+
+ g_hash_table_iter_init (&iter, self->app_usages);
+ apps = NULL;
+ while (g_hash_table_iter_next (&iter, (gpointer *) &appid, NULL))
+ {
+ ShellApp *app;
+
+ app = shell_app_system_lookup_app (appsys, appid);
+ if (!app)
+ continue;
+
+ apps = g_slist_prepend (apps, g_object_ref (app));
+ }
+
+ apps = g_slist_sort_with_data (apps, sort_apps_by_usage, self);
+
+ return apps;
+}
+
+
+/**
+ * shell_app_usage_compare:
+ * @self: the usage instance to request
+ * @id_a: ID of first app
+ * @id_b: ID of second app
+ *
+ * Compare @id_a and @id_b based on frequency of use.
+ *
+ * Returns: -1 if @id_a ranks higher than @id_b, 1 if @id_b ranks higher
+ * than @id_a, and 0 if both rank equally.
+ */
+int
+shell_app_usage_compare (ShellAppUsage *self,
+ const char *id_a,
+ const char *id_b)
+{
+ UsageData *usage_a, *usage_b;
+
+ usage_a = g_hash_table_lookup (self->app_usages, id_a);
+ usage_b = g_hash_table_lookup (self->app_usages, id_b);
+
+ if (usage_a == NULL && usage_b == NULL)
+ return 0;
+ else if (usage_a == NULL)
+ return 1;
+ else if (usage_b == NULL)
+ return -1;
+
+ return usage_b->score - usage_a->score;
+}
+
+static void
+ensure_queued_save (ShellAppUsage *self)
+{
+ if (self->save_id != 0)
+ return;
+ self->save_id = g_timeout_add_seconds (SAVE_APPS_TIMEOUT_SECONDS, idle_save_application_usage, self);
+ g_source_set_name_by_id (self->save_id, "[gnome-shell] idle_save_application_usage");
+}
+
+/* Clean up apps we see rarely.
+ * The logic behind this is that if an app was seen less than SCORE_MIN times
+ * and not seen for a week, it can probably be forgotten about.
+ * This should much reduce the size of the list and avoid 'pollution'. */
+static gboolean
+idle_clean_usage (ShellAppUsage *self)
+{
+ GHashTableIter iter;
+ UsageData *usage;
+ long current_time;
+ long week_ago;
+
+ current_time = get_time ();
+ week_ago = current_time - (7 * 24 * 60 * 60);
+
+ g_hash_table_iter_init (&iter, self->app_usages);
+
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &usage))
+ {
+ if ((usage->score < SCORE_MIN) &&
+ (usage->last_seen < week_ago))
+ g_hash_table_iter_remove (&iter);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+write_escaped (GDataOutputStream *stream,
+ const char *str,
+ GError **error)
+{
+ gboolean ret;
+ char *quoted = g_markup_escape_text (str, -1);
+ ret = g_data_output_stream_put_string (stream, quoted, NULL, error);
+ g_free (quoted);
+ return ret;
+}
+
+static gboolean
+write_attribute_string (GDataOutputStream *stream,
+ const char *elt_name,
+ const char *str,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *elt;
+
+ elt = g_strdup_printf (" %s=\"", elt_name);
+ ret = g_data_output_stream_put_string (stream, elt, NULL, error);
+ g_free (elt);
+ if (!ret)
+ goto out;
+
+ ret = write_escaped (stream, str, error);
+ if (!ret)
+ goto out;
+
+ ret = g_data_output_stream_put_string (stream, "\"", NULL, error);
+
+out:
+ return ret;
+}
+
+static gboolean
+write_attribute_uint (GDataOutputStream *stream,
+ const char *elt_name,
+ guint value,
+ GError **error)
+{
+ gboolean ret;
+ char *buf;
+
+ buf = g_strdup_printf ("%u", value);
+ ret = write_attribute_string (stream, elt_name, buf, error);
+ g_free (buf);
+
+ return ret;
+}
+
+static gboolean
+write_attribute_double (GDataOutputStream *stream,
+ const char *elt_name,
+ double value,
+ GError **error)
+{
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+ gboolean ret;
+
+ g_ascii_dtostr (buf, sizeof (buf), value);
+ ret = write_attribute_string (stream, elt_name, buf, error);
+
+ return ret;
+}
+
+/* Save app data lists to file */
+static gboolean
+idle_save_application_usage (gpointer data)
+{
+ ShellAppUsage *self = SHELL_APP_USAGE (data);
+ char *id;
+ GHashTableIter iter;
+ UsageData *usage;
+ GFileOutputStream *output;
+ GOutputStream *buffered_output;
+ GDataOutputStream *data_output;
+ GError *error = NULL;
+
+ self->save_id = 0;
+
+ /* Parent directory is already created by shell-global */
+ output = g_file_replace (self->configfile, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
+ if (!output)
+ {
+ g_debug ("Could not save applications usage data: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ buffered_output = g_buffered_output_stream_new (G_OUTPUT_STREAM (output));
+ g_object_unref (output);
+ data_output = g_data_output_stream_new (G_OUTPUT_STREAM (buffered_output));
+ g_object_unref (buffered_output);
+
+ if (!g_data_output_stream_put_string (data_output, "<?xml version=\"1.0\"?>\n<application-state>\n", NULL, &error))
+ goto out;
+ if (!g_data_output_stream_put_string (data_output, " <context id=\"\">\n", NULL, &error))
+ goto out;
+
+ g_hash_table_iter_init (&iter, self->app_usages);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &usage))
+ {
+ ShellApp *app;
+
+ app = shell_app_system_lookup_app (shell_app_system_get_default(), id);
+
+ if (!app)
+ continue;
+
+ if (!g_data_output_stream_put_string (data_output, " <application", NULL, &error))
+ goto out;
+ if (!write_attribute_string (data_output, "id", id, &error))
+ goto out;
+ if (!write_attribute_double (data_output, "score", usage->score, &error))
+ goto out;
+ if (!write_attribute_uint (data_output, "last-seen", usage->last_seen, &error))
+ goto out;
+ if (!g_data_output_stream_put_string (data_output, "/>\n", NULL, &error))
+ goto out;
+ }
+ if (!g_data_output_stream_put_string (data_output, " </context>\n", NULL, &error))
+ goto out;
+ if (!g_data_output_stream_put_string (data_output, "</application-state>\n", NULL, &error))
+ goto out;
+
+out:
+ if (!error)
+ g_output_stream_close_async (G_OUTPUT_STREAM (data_output), 0, NULL, NULL, NULL);
+ g_object_unref (data_output);
+ if (error)
+ {
+ g_debug ("Could not save applications usage data: %s", error->message);
+ g_error_free (error);
+ }
+ return FALSE;
+}
+
+static void
+shell_app_usage_start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ShellAppUsage *self = user_data;
+
+ if (strcmp (element_name, "application-state") == 0)
+ {
+ }
+ else if (strcmp (element_name, "context") == 0)
+ {
+ }
+ else if (strcmp (element_name, "application") == 0)
+ {
+ const char **attribute;
+ const char **value;
+ UsageData *usage;
+ char *appid = NULL;
+
+ for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
+ {
+ if (strcmp (*attribute, "id") == 0)
+ {
+ appid = g_strdup (*value);
+ break;
+ }
+ }
+
+ if (!appid)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ "Missing attribute id on <%s> element",
+ element_name);
+ return;
+ }
+
+ usage = g_new0 (UsageData, 1);
+ g_hash_table_insert (self->app_usages, appid, usage);
+
+ for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
+ {
+ if (strcmp (*attribute, "score") == 0)
+ {
+ usage->score = g_ascii_strtod (*value, NULL);
+ }
+ else if (strcmp (*attribute, "last-seen") == 0)
+ {
+ usage->last_seen = (guint) g_ascii_strtoull (*value, NULL, 10);
+ }
+ }
+ }
+ else
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ "Unknown element <%s>",
+ element_name);
+ }
+}
+
+static GMarkupParser app_state_parse_funcs =
+{
+ shell_app_usage_start_element_handler,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Load data about apps usage from file */
+static void
+restore_from_file (ShellAppUsage *self)
+{
+ GFileInputStream *input;
+ GMarkupParseContext *parse_context;
+ GError *error = NULL;
+ char buf[1024];
+
+ input = g_file_read (self->configfile, NULL, &error);
+ if (error)
+ {
+ if (error->code != G_IO_ERROR_NOT_FOUND)
+ g_warning ("Could not load applications usage data: %s", error->message);
+
+ g_error_free (error);
+ return;
+ }
+
+ parse_context = g_markup_parse_context_new (&app_state_parse_funcs, 0, self, NULL);
+
+ while (TRUE)
+ {
+ gssize count = g_input_stream_read ((GInputStream*) input, buf, sizeof(buf), NULL, &error);
+ if (count <= 0)
+ goto out;
+ if (!g_markup_parse_context_parse (parse_context, buf, count, &error))
+ goto out;
+ }
+
+out:
+ g_markup_parse_context_free (parse_context);
+ g_input_stream_close ((GInputStream*)input, NULL, NULL);
+ g_object_unref (input);
+
+ idle_clean_usage (self);
+
+ if (error)
+ {
+ g_warning ("Could not load applications usage data: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+/* Enable or disable the timers, depending on the value of ENABLE_MONITORING_KEY
+ * and taking care of the previous state. If selfing is disabled, we still
+ * report apps usage based on (possibly) saved data, but don't collect data.
+ */
+static void
+update_enable_monitoring (ShellAppUsage *self)
+{
+ gboolean enable;
+
+ enable = g_settings_get_boolean (self->privacy_settings,
+ ENABLE_MONITORING_KEY);
+
+ /* Be sure not to start the timers if they were already set */
+ if (enable && !self->enable_monitoring)
+ {
+ on_focus_app_changed (shell_window_tracker_get_default (), NULL, self);
+ }
+ /* ...and don't try to stop them if they were not running */
+ else if (!enable && self->enable_monitoring)
+ {
+ if (self->watched_app)
+ g_object_unref (self->watched_app);
+ self->watched_app = NULL;
+ g_clear_handle_id (&self->save_id, g_source_remove);
+ }
+
+ self->enable_monitoring = enable;
+}
+
+/* Called when the ENABLE_MONITORING_KEY boolean has changed */
+static void
+on_enable_monitoring_key_changed (GSettings *settings,
+ const gchar *key,
+ ShellAppUsage *self)
+{
+ update_enable_monitoring (self);
+}
+
+/**
+ * shell_app_usage_get_default:
+ *
+ * Return Value: (transfer none): The global #ShellAppUsage instance
+ */
+ShellAppUsage *
+shell_app_usage_get_default (void)
+{
+ static ShellAppUsage *instance;
+
+ if (instance == NULL)
+ instance = g_object_new (SHELL_TYPE_APP_USAGE, NULL);
+
+ return instance;
+}
diff --git a/src/shell-app-usage.h b/src/shell-app-usage.h
new file mode 100644
index 0000000..4b0e169
--- /dev/null
+++ b/src/shell-app-usage.h
@@ -0,0 +1,23 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_APP_USAGE_H__
+#define __SHELL_APP_USAGE_H__
+
+#include "shell-app.h"
+#include "shell-window-tracker.h"
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_APP_USAGE (shell_app_usage_get_type ())
+G_DECLARE_FINAL_TYPE (ShellAppUsage, shell_app_usage,
+ SHELL, APP_USAGE, GObject)
+
+ShellAppUsage* shell_app_usage_get_default(void);
+
+GSList *shell_app_usage_get_most_used (ShellAppUsage *usage);
+int shell_app_usage_compare (ShellAppUsage *self,
+ const char *id_a,
+ const char *id_b);
+
+G_END_DECLS
+
+#endif /* __SHELL_APP_USAGE_H__ */
diff --git a/src/shell-app.c b/src/shell-app.c
new file mode 100644
index 0000000..5c38a9c
--- /dev/null
+++ b/src/shell-app.c
@@ -0,0 +1,1756 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <meta/display.h>
+#include <meta/meta-context.h>
+#include <meta/meta-workspace-manager.h>
+#include <meta/meta-x11-display.h>
+
+#include "shell-app-private.h"
+#include "shell-enum-types.h"
+#include "shell-global.h"
+#include "shell-util.h"
+#include "shell-app-system-private.h"
+#include "shell-window-tracker-private.h"
+#include "st.h"
+#include "gtkactionmuxer.h"
+#include "org-gtk-application.h"
+#include "switcheroo-control.h"
+
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-journal.h>
+#include <errno.h>
+#include <unistd.h>
+#endif
+
+/* This is mainly a memory usage optimization - the user is going to
+ * be running far fewer of the applications at one time than they have
+ * installed. But it also just helps keep the code more logically
+ * separated.
+ */
+typedef struct {
+ guint refcount;
+
+ /* Signal connection to dirty window sort list on workspace changes */
+ gulong workspace_switch_id;
+
+ GSList *windows;
+
+ guint interesting_windows;
+
+ /* Whether or not we need to resort the windows; this is done on demand */
+ guint window_sort_stale : 1;
+
+ /* See GApplication documentation */
+ GtkActionMuxer *muxer;
+ char *unique_bus_name;
+ GDBusConnection *session;
+
+ /* GDBus Proxy for getting application busy state */
+ ShellOrgGtkApplication *application_proxy;
+ GCancellable *cancellable;
+
+} ShellAppRunningState;
+
+/**
+ * SECTION:shell-app
+ * @short_description: Object representing an application
+ *
+ * This object wraps a #GDesktopAppInfo, providing methods and signals
+ * primarily useful for running applications.
+ */
+struct _ShellApp
+{
+ GObject parent;
+
+ int started_on_workspace;
+
+ ShellAppState state;
+
+ GDesktopAppInfo *info; /* If NULL, this app is backed by one or more
+ * MetaWindow. For purposes of app title
+ * etc., we use the first window added,
+ * because it's most likely to be what we
+ * want (e.g. it will be of TYPE_NORMAL from
+ * the way shell-window-tracker.c works).
+ */
+ GIcon *fallback_icon;
+ MetaWindow *fallback_icon_window;
+
+ ShellAppRunningState *running_state;
+
+ char *window_id_string;
+ char *name_collation_key;
+};
+
+enum {
+ PROP_0,
+
+ PROP_STATE,
+ PROP_BUSY,
+ PROP_ID,
+ PROP_ACTION_GROUP,
+ PROP_ICON,
+ PROP_APP_INFO,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum {
+ WINDOWS_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint shell_app_signals[LAST_SIGNAL] = { 0 };
+
+static void create_running_state (ShellApp *app);
+static void unref_running_state (ShellAppRunningState *state);
+
+G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT)
+
+static void
+shell_app_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellApp *app = SHELL_APP (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_STATE:
+ g_value_set_enum (value, app->state);
+ break;
+ case PROP_BUSY:
+ g_value_set_boolean (value, shell_app_get_busy (app));
+ break;
+ case PROP_ID:
+ g_value_set_string (value, shell_app_get_id (app));
+ break;
+ case PROP_ICON:
+ g_value_set_object (value, shell_app_get_icon (app));
+ break;
+ case PROP_ACTION_GROUP:
+ if (app->running_state)
+ g_value_set_object (value, app->running_state->muxer);
+ break;
+ case PROP_APP_INFO:
+ if (app->info)
+ g_value_set_object (value, app->info);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_app_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellApp *app = SHELL_APP (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_APP_INFO:
+ _shell_app_set_app_info (app, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+const char *
+shell_app_get_id (ShellApp *app)
+{
+ if (app->info)
+ return g_app_info_get_id (G_APP_INFO (app->info));
+ return app->window_id_string;
+}
+
+static MetaWindow *
+window_backed_app_get_window (ShellApp *app)
+{
+ g_assert (app->info == NULL);
+ if (app->running_state)
+ {
+ g_assert (app->running_state->windows);
+ return app->running_state->windows->data;
+ }
+ else
+ return NULL;
+}
+
+static GIcon *
+x11_window_create_fallback_gicon (MetaWindow *window)
+{
+ StTextureCache *texture_cache;
+ cairo_surface_t *surface;
+
+ g_object_get (window, "icon", &surface, NULL);
+
+ texture_cache = st_texture_cache_get_default ();
+ return st_texture_cache_load_cairo_surface_to_gicon (texture_cache, surface);
+}
+
+static void
+on_window_icon_changed (GObject *object,
+ const GParamSpec *pspec,
+ gpointer user_data)
+{
+ MetaWindow *window = META_WINDOW (object);
+ ShellApp *app = user_data;
+
+ g_clear_object (&app->fallback_icon);
+ app->fallback_icon = x11_window_create_fallback_gicon (window);
+
+ if (!app->fallback_icon)
+ app->fallback_icon = g_themed_icon_new ("application-x-executable");
+
+ g_object_notify_by_pspec (G_OBJECT (app), props[PROP_ICON]);
+}
+
+/**
+ * shell_app_get_icon:
+ *
+ * Look up the icon for this application
+ *
+ * Return value: (transfer none): A #GIcon
+ */
+GIcon *
+shell_app_get_icon (ShellApp *app)
+{
+ MetaWindow *window = NULL;
+
+ g_return_val_if_fail (SHELL_IS_APP (app), NULL);
+
+ if (app->info)
+ return g_app_info_get_icon (G_APP_INFO (app->info));
+
+ if (app->fallback_icon)
+ return app->fallback_icon;
+
+ /* During a state transition from running to not-running for
+ * window-backend apps, it's possible we get a request for the icon.
+ * Avoid asserting here and just return a fallback icon
+ */
+ if (app->running_state != NULL)
+ window = window_backed_app_get_window (app);
+
+ if (window &&
+ meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_X11)
+ {
+ app->fallback_icon_window = window;
+ app->fallback_icon = x11_window_create_fallback_gicon (window);
+ g_signal_connect (G_OBJECT (window),
+ "notify::icon", G_CALLBACK (on_window_icon_changed), app);
+ }
+ else
+ {
+ app->fallback_icon = g_themed_icon_new ("application-x-executable");
+ }
+
+ return app->fallback_icon;
+}
+
+/**
+ * shell_app_create_icon_texture:
+ *
+ * Look up the icon for this application, and create a #ClutterActor
+ * for it at the given size.
+ *
+ * Return value: (transfer none): A floating #ClutterActor
+ */
+ClutterActor *
+shell_app_create_icon_texture (ShellApp *app,
+ int size)
+{
+ ClutterActor *ret;
+
+ ret = st_icon_new ();
+ st_icon_set_icon_size (ST_ICON (ret), size);
+ st_icon_set_fallback_icon_name (ST_ICON (ret), "application-x-executable");
+
+ g_object_bind_property (app, "icon", ret, "gicon", G_BINDING_SYNC_CREATE);
+
+ if (shell_app_is_window_backed (app))
+ st_widget_add_style_class_name (ST_WIDGET (ret), "fallback-app-icon");
+
+ return ret;
+}
+
+const char *
+shell_app_get_name (ShellApp *app)
+{
+ if (app->info)
+ return g_app_info_get_name (G_APP_INFO (app->info));
+ else
+ {
+ MetaWindow *window = window_backed_app_get_window (app);
+ const char *name = NULL;
+
+ if (window)
+ name = meta_window_get_wm_class (window);
+ if (!name)
+ name = C_("program", "Unknown");
+ return name;
+ }
+}
+
+const char *
+shell_app_get_description (ShellApp *app)
+{
+ if (app->info)
+ return g_app_info_get_description (G_APP_INFO (app->info));
+ else
+ return NULL;
+}
+
+/**
+ * shell_app_is_window_backed:
+ *
+ * A window backed application is one which represents just an open
+ * window, i.e. there's no .desktop file association, so we don't know
+ * how to launch it again.
+ */
+gboolean
+shell_app_is_window_backed (ShellApp *app)
+{
+ return app->info == NULL;
+}
+
+typedef struct {
+ MetaWorkspace *workspace;
+ GSList **transients;
+} CollectTransientsData;
+
+static gboolean
+collect_transients_on_workspace (MetaWindow *window,
+ gpointer datap)
+{
+ CollectTransientsData *data = datap;
+
+ if (data->workspace && meta_window_get_workspace (window) != data->workspace)
+ return TRUE;
+
+ *data->transients = g_slist_prepend (*data->transients, window);
+ return TRUE;
+}
+
+/* The basic idea here is that when we're targeting a window,
+ * if it has transients we want to pick the most recent one
+ * the user interacted with.
+ * This function makes raising GEdit with the file chooser
+ * open work correctly.
+ */
+static MetaWindow *
+find_most_recent_transient_on_same_workspace (MetaDisplay *display,
+ MetaWindow *reference)
+{
+ GSList *transients, *transients_sorted, *iter;
+ MetaWindow *result;
+ CollectTransientsData data;
+
+ transients = NULL;
+ data.workspace = meta_window_get_workspace (reference);
+ data.transients = &transients;
+
+ meta_window_foreach_transient (reference, collect_transients_on_workspace, &data);
+
+ transients_sorted = meta_display_sort_windows_by_stacking (display, transients);
+ /* Reverse this so we're top-to-bottom (yes, we should probably change the order
+ * returned from the sort_windows_by_stacking function)
+ */
+ transients_sorted = g_slist_reverse (transients_sorted);
+ g_slist_free (transients);
+ transients = NULL;
+
+ result = NULL;
+ for (iter = transients_sorted; iter; iter = iter->next)
+ {
+ MetaWindow *window = iter->data;
+ MetaWindowType wintype = meta_window_get_window_type (window);
+
+ /* Don't want to focus UTILITY types, like the Gimp toolbars */
+ if (wintype == META_WINDOW_NORMAL ||
+ wintype == META_WINDOW_DIALOG)
+ {
+ result = window;
+ break;
+ }
+ }
+ g_slist_free (transients_sorted);
+ return result;
+}
+
+static MetaWorkspace *
+get_active_workspace (void)
+{
+ ShellGlobal *global = shell_global_get ();
+ MetaDisplay *display = shell_global_get_display (global);
+ MetaWorkspaceManager *workspace_manager =
+ meta_display_get_workspace_manager (display);
+
+ return meta_workspace_manager_get_active_workspace (workspace_manager);
+}
+
+/**
+ * shell_app_activate_window:
+ * @app: a #ShellApp
+ * @window: (nullable): Window to be focused
+ * @timestamp: Event timestamp
+ *
+ * Bring all windows for the given app to the foreground,
+ * but ensure that @window is on top. If @window is %NULL,
+ * the window with the most recent user time for the app
+ * will be used.
+ *
+ * This function has no effect if @app is not currently running.
+ */
+void
+shell_app_activate_window (ShellApp *app,
+ MetaWindow *window,
+ guint32 timestamp)
+{
+ g_autoptr (GSList) windows = NULL;
+
+ if (shell_app_get_state (app) != SHELL_APP_STATE_RUNNING)
+ return;
+
+ windows = shell_app_get_windows (app);
+ if (window == NULL && windows)
+ window = windows->data;
+
+ if (!g_slist_find (windows, window))
+ return;
+ else
+ {
+ GSList *windows_reversed, *iter;
+ ShellGlobal *global = shell_global_get ();
+ MetaDisplay *display = shell_global_get_display (global);
+ MetaWorkspace *active = get_active_workspace ();
+ MetaWorkspace *workspace = meta_window_get_workspace (window);
+ guint32 last_user_timestamp = meta_display_get_last_user_time (display);
+ MetaWindow *most_recent_transient;
+
+ if (meta_display_xserver_time_is_before (display, timestamp, last_user_timestamp))
+ {
+ meta_window_set_demands_attention (window);
+ return;
+ }
+
+ /* Now raise all the other windows for the app that are on
+ * the same workspace, in reverse order to preserve the stacking.
+ */
+ windows_reversed = g_slist_copy (windows);
+ windows_reversed = g_slist_reverse (windows_reversed);
+ for (iter = windows_reversed; iter; iter = iter->next)
+ {
+ MetaWindow *other_window = iter->data;
+
+ if (other_window != window && meta_window_get_workspace (other_window) == workspace)
+ meta_window_raise (other_window);
+ }
+ g_slist_free (windows_reversed);
+
+ /* If we have a transient that the user's interacted with more recently than
+ * the window, pick that.
+ */
+ most_recent_transient = find_most_recent_transient_on_same_workspace (display, window);
+ if (most_recent_transient
+ && meta_display_xserver_time_is_before (display,
+ meta_window_get_user_time (window),
+ meta_window_get_user_time (most_recent_transient)))
+ window = most_recent_transient;
+
+
+ if (active != workspace)
+ meta_workspace_activate_with_focus (workspace, window, timestamp);
+ else
+ meta_window_activate (window, timestamp);
+ }
+}
+
+
+void
+shell_app_update_window_actions (ShellApp *app, MetaWindow *window)
+{
+ const char *object_path;
+
+ object_path = meta_window_get_gtk_window_object_path (window);
+ if (object_path != NULL)
+ {
+ GActionGroup *actions;
+
+ actions = g_object_get_data (G_OBJECT (window), "actions");
+ if (actions == NULL)
+ {
+ actions = G_ACTION_GROUP (g_dbus_action_group_get (app->running_state->session,
+ meta_window_get_gtk_unique_bus_name (window),
+ object_path));
+ g_object_set_data_full (G_OBJECT (window), "actions", actions, g_object_unref);
+ }
+
+ g_assert (app->running_state->muxer);
+ gtk_action_muxer_insert (app->running_state->muxer, "win", actions);
+ g_object_notify_by_pspec (G_OBJECT (app), props[PROP_ACTION_GROUP]);
+ }
+}
+
+/**
+ * shell_app_activate:
+ * @app: a #ShellApp
+ *
+ * Like shell_app_activate_full(), but using the default workspace and
+ * event timestamp.
+ */
+void
+shell_app_activate (ShellApp *app)
+{
+ return shell_app_activate_full (app, -1, 0);
+}
+
+/**
+ * shell_app_activate_full:
+ * @app: a #ShellApp
+ * @workspace: launch on this workspace, or -1 for default. Ignored if
+ * activating an existing window
+ * @timestamp: Event timestamp
+ *
+ * Perform an appropriate default action for operating on this application,
+ * dependent on its current state. For example, if the application is not
+ * currently running, launch it. If it is running, activate the most
+ * recently used NORMAL window (or if that window has a transient, the most
+ * recently used transient for that window).
+ */
+void
+shell_app_activate_full (ShellApp *app,
+ int workspace,
+ guint32 timestamp)
+{
+ ShellGlobal *global;
+
+ global = shell_global_get ();
+
+ if (timestamp == 0)
+ timestamp = shell_global_get_current_time (global);
+
+ switch (app->state)
+ {
+ case SHELL_APP_STATE_STOPPED:
+ {
+ GError *error = NULL;
+ if (!shell_app_launch (app, timestamp, workspace, SHELL_APP_LAUNCH_GPU_APP_PREF, &error))
+ {
+ char *msg;
+ msg = g_strdup_printf (_("Failed to launch “%s”"), shell_app_get_name (app));
+ shell_global_notify_error (global,
+ msg,
+ error->message);
+ g_free (msg);
+ g_clear_error (&error);
+ }
+ }
+ break;
+ case SHELL_APP_STATE_STARTING:
+ break;
+ case SHELL_APP_STATE_RUNNING:
+ shell_app_activate_window (app, NULL, timestamp);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+/**
+ * shell_app_open_new_window:
+ * @app: a #ShellApp
+ * @workspace: open on this workspace, or -1 for default
+ *
+ * Request that the application create a new window.
+ */
+void
+shell_app_open_new_window (ShellApp *app,
+ int workspace)
+{
+ GActionGroup *group = NULL;
+ const char * const *actions;
+
+ g_return_if_fail (app->info != NULL);
+
+ /* First check whether the application provides a "new-window" desktop
+ * action - it is a safe bet that it will open a new window, and activating
+ * it will trigger startup notification if necessary
+ */
+ actions = g_desktop_app_info_list_actions (G_DESKTOP_APP_INFO (app->info));
+
+ if (g_strv_contains (actions, "new-window"))
+ {
+ shell_app_launch_action (app, "new-window", 0, workspace);
+ return;
+ }
+
+ /* Next, check whether the app exports an explicit "new-window" action
+ * that we can activate on the bus - the muxer will add startup notification
+ * information to the platform data, so this should work just as well as
+ * desktop actions.
+ */
+ group = app->running_state ? G_ACTION_GROUP (app->running_state->muxer)
+ : NULL;
+
+ if (group &&
+ g_action_group_has_action (group, "app.new-window") &&
+ g_action_group_get_action_parameter_type (group, "app.new-window") == NULL)
+ {
+ g_action_group_activate_action (group, "app.new-window", NULL);
+
+ return;
+ }
+
+ /* Lastly, just always launch the application again, even if we know
+ * it was already running. For most applications this
+ * should have the effect of creating a new window, whether that's
+ * a second process (in the case of Calculator) or IPC to existing
+ * instance (Firefox). There are a few less-sensical cases such
+ * as say Pidgin.
+ */
+ shell_app_launch (app, 0, workspace, SHELL_APP_LAUNCH_GPU_APP_PREF, NULL);
+}
+
+/**
+ * shell_app_can_open_new_window:
+ * @app: a #ShellApp
+ *
+ * Returns %TRUE if the app supports opening a new window through
+ * shell_app_open_new_window() (ie, if calling that function will
+ * result in actually opening a new window and not something else,
+ * like presenting the most recently active one)
+ */
+gboolean
+shell_app_can_open_new_window (ShellApp *app)
+{
+ ShellAppRunningState *state;
+ MetaWindow *window;
+ GDesktopAppInfo *desktop_info;
+ const char * const *desktop_actions;
+
+ /* Apps that are stopped can always open new windows, because
+ * activating them would open the first one; if they are starting,
+ * we cannot tell whether they can open additional windows until
+ * they are running */
+ if (app->state != SHELL_APP_STATE_RUNNING)
+ return app->state == SHELL_APP_STATE_STOPPED;
+
+ state = app->running_state;
+
+ /* If the app has an explicit new-window action, then it can
+ (or it should be able to) ...
+ */
+ if (g_action_group_has_action (G_ACTION_GROUP (state->muxer), "app.new-window"))
+ return TRUE;
+
+ /* If the app doesn't have a desktop file, then nothing is possible */
+ if (!app->info)
+ return FALSE;
+
+ desktop_info = G_DESKTOP_APP_INFO (app->info);
+
+ /* If the app is explicitly telling us via its desktop file, then we know
+ * for sure
+ */
+ if (g_desktop_app_info_has_key (desktop_info, "SingleMainWindow"))
+ return !g_desktop_app_info_get_boolean (desktop_info,
+ "SingleMainWindow");
+
+ /* GNOME-specific key, for backwards compatibility with apps that haven't
+ * started using the XDG "SingleMainWindow" key yet
+ */
+ if (g_desktop_app_info_has_key (desktop_info, "X-GNOME-SingleWindow"))
+ return !g_desktop_app_info_get_boolean (desktop_info,
+ "X-GNOME-SingleWindow");
+
+ /* If it has a new-window desktop action, it should be able to */
+ desktop_actions = g_desktop_app_info_list_actions (desktop_info);
+ if (desktop_actions && g_strv_contains (desktop_actions, "new-window"))
+ return TRUE;
+
+ /* If this is a unique GtkApplication, and we don't have a new-window, then
+ probably we can't
+
+ We don't consider non-unique GtkApplications here to handle cases like
+ evince, which don't export a new-window action because each window is in
+ a different process. In any case, in a non-unique GtkApplication each
+ Activate() knows nothing about the other instances, so it will show a
+ new window.
+ */
+
+ window = state->windows->data;
+
+ if (state->unique_bus_name != NULL &&
+ meta_window_get_gtk_application_object_path (window) != NULL)
+ {
+ if (meta_window_get_gtk_application_id (window) != NULL)
+ return FALSE;
+ else
+ return TRUE;
+ }
+
+ /* In all other cases, we don't have a reliable source of information
+ or a decent heuristic, so we err on the compatibility side and say
+ yes.
+ */
+ return TRUE;
+}
+
+/**
+ * shell_app_get_state:
+ * @app: a #ShellApp
+ *
+ * Returns: State of the application
+ */
+ShellAppState
+shell_app_get_state (ShellApp *app)
+{
+ return app->state;
+}
+
+typedef struct {
+ ShellApp *app;
+ MetaWorkspace *active_workspace;
+} CompareWindowsData;
+
+static int
+shell_app_compare_windows (gconstpointer a,
+ gconstpointer b,
+ gpointer datap)
+{
+ MetaWindow *win_a = (gpointer)a;
+ MetaWindow *win_b = (gpointer)b;
+ CompareWindowsData *data = datap;
+ gboolean ws_a, ws_b;
+ gboolean vis_a, vis_b;
+
+ ws_a = meta_window_get_workspace (win_a) == data->active_workspace;
+ ws_b = meta_window_get_workspace (win_b) == data->active_workspace;
+
+ if (ws_a && !ws_b)
+ return -1;
+ else if (!ws_a && ws_b)
+ return 1;
+
+ vis_a = meta_window_showing_on_its_workspace (win_a);
+ vis_b = meta_window_showing_on_its_workspace (win_b);
+
+ if (vis_a && !vis_b)
+ return -1;
+ else if (!vis_a && vis_b)
+ return 1;
+
+ return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a);
+}
+
+/**
+ * shell_app_get_windows:
+ * @app:
+ *
+ * Get the windows which are associated with this application. The
+ * returned list will be sorted first by whether they're on the
+ * active workspace, then by whether they're visible, and finally
+ * by the time the user last interacted with them.
+ *
+ * Returns: (transfer container) (element-type MetaWindow): List of windows
+ */
+GSList *
+shell_app_get_windows (ShellApp *app)
+{
+ GSList *windows = NULL;
+ GSList *l;
+
+ if (app->running_state == NULL)
+ return NULL;
+
+ if (app->running_state->window_sort_stale)
+ {
+ CompareWindowsData data;
+ data.app = app;
+ data.active_workspace = get_active_workspace ();
+ app->running_state->windows = g_slist_sort_with_data (app->running_state->windows, shell_app_compare_windows, &data);
+ app->running_state->window_sort_stale = FALSE;
+ }
+
+ for (l = app->running_state->windows; l; l = l->next)
+ if (!meta_window_is_override_redirect (META_WINDOW (l->data)))
+ windows = g_slist_prepend (windows, l->data);
+
+ return g_slist_reverse (windows);
+}
+
+guint
+shell_app_get_n_windows (ShellApp *app)
+{
+ if (app->running_state == NULL)
+ return 0;
+ return g_slist_length (app->running_state->windows);
+}
+
+gboolean
+shell_app_is_on_workspace (ShellApp *app,
+ MetaWorkspace *workspace)
+{
+ GSList *iter;
+
+ if (shell_app_get_state (app) == SHELL_APP_STATE_STARTING)
+ {
+ if (app->started_on_workspace == -1 ||
+ meta_workspace_index (workspace) == app->started_on_workspace)
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ if (app->running_state == NULL)
+ return FALSE;
+
+ for (iter = app->running_state->windows; iter; iter = iter->next)
+ {
+ if (meta_window_get_workspace (iter->data) == workspace)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+shell_app_get_last_user_time (ShellApp *app)
+{
+ GSList *iter;
+ guint32 last_user_time;
+
+ last_user_time = 0;
+
+ if (app->running_state != NULL)
+ {
+ for (iter = app->running_state->windows; iter; iter = iter->next)
+ last_user_time = MAX (last_user_time, meta_window_get_user_time (iter->data));
+ }
+
+ return (int)last_user_time;
+}
+
+static gboolean
+shell_app_is_minimized (ShellApp *app)
+{
+ GSList *iter;
+
+ if (app->running_state == NULL)
+ return FALSE;
+
+ for (iter = app->running_state->windows; iter; iter = iter->next)
+ {
+ if (meta_window_showing_on_its_workspace (iter->data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * shell_app_compare:
+ * @app:
+ * @other: A #ShellApp
+ *
+ * Compare one #ShellApp instance to another, in the following way:
+ * - Running applications sort before not-running applications.
+ * - If one of them has non-minimized windows and the other does not,
+ * the one with visible windows is first.
+ * - Finally, the application which the user interacted with most recently
+ * compares earlier.
+ */
+int
+shell_app_compare (ShellApp *app,
+ ShellApp *other)
+{
+ gboolean min_app, min_other;
+
+ if (app->state != other->state)
+ {
+ if (app->state == SHELL_APP_STATE_RUNNING)
+ return -1;
+ return 1;
+ }
+
+ min_app = shell_app_is_minimized (app);
+ min_other = shell_app_is_minimized (other);
+
+ if (min_app != min_other)
+ {
+ if (min_other)
+ return -1;
+ return 1;
+ }
+
+ if (app->state == SHELL_APP_STATE_RUNNING)
+ {
+ if (app->running_state->windows && !other->running_state->windows)
+ return -1;
+ else if (!app->running_state->windows && other->running_state->windows)
+ return 1;
+
+ return shell_app_get_last_user_time (other) - shell_app_get_last_user_time (app);
+ }
+
+ return 0;
+}
+
+ShellApp *
+_shell_app_new_for_window (MetaWindow *window)
+{
+ ShellApp *app;
+
+ app = g_object_new (SHELL_TYPE_APP, NULL);
+
+ app->window_id_string = g_strdup_printf ("window:%d", meta_window_get_stable_sequence (window));
+
+ _shell_app_add_window (app, window);
+
+ return app;
+}
+
+ShellApp *
+_shell_app_new (GDesktopAppInfo *info)
+{
+ ShellApp *app;
+
+ app = g_object_new (SHELL_TYPE_APP,
+ "app-info", info,
+ NULL);
+
+ return app;
+}
+
+void
+_shell_app_set_app_info (ShellApp *app,
+ GDesktopAppInfo *info)
+{
+ g_set_object (&app->info, info);
+
+ g_clear_pointer (&app->name_collation_key, g_free);
+ if (app->info)
+ app->name_collation_key = g_utf8_collate_key (shell_app_get_name (app), -1);
+}
+
+static void
+shell_app_state_transition (ShellApp *app,
+ ShellAppState state)
+{
+ if (app->state == state)
+ return;
+ g_return_if_fail (!(app->state == SHELL_APP_STATE_RUNNING &&
+ state == SHELL_APP_STATE_STARTING));
+ app->state = state;
+
+ _shell_app_system_notify_app_state_changed (shell_app_system_get_default (), app);
+
+ g_object_notify_by_pspec (G_OBJECT (app), props[PROP_STATE]);
+}
+
+static void
+shell_app_on_user_time_changed (MetaWindow *window,
+ GParamSpec *pspec,
+ ShellApp *app)
+{
+ g_assert (app->running_state != NULL);
+
+ /* Ideally we don't want to emit windows-changed if the sort order
+ * isn't actually changing. This check catches most of those.
+ */
+ if (window != app->running_state->windows->data)
+ {
+ app->running_state->window_sort_stale = TRUE;
+ g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
+ }
+}
+
+static void
+shell_app_sync_running_state (ShellApp *app)
+{
+ g_return_if_fail (app->running_state != NULL);
+
+ if (app->state != SHELL_APP_STATE_STARTING)
+ {
+ if (app->running_state->interesting_windows == 0)
+ shell_app_state_transition (app, SHELL_APP_STATE_STOPPED);
+ else
+ shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
+ }
+}
+
+
+static void
+shell_app_on_skip_taskbar_changed (MetaWindow *window,
+ GParamSpec *pspec,
+ ShellApp *app)
+{
+ g_assert (app->running_state != NULL);
+
+ /* we rely on MetaWindow:skip-taskbar only being notified
+ * when it actually changes; when that assumption breaks,
+ * we'll have to track the "interesting" windows themselves
+ */
+ if (meta_window_is_skip_taskbar (window))
+ app->running_state->interesting_windows--;
+ else
+ app->running_state->interesting_windows++;
+
+ shell_app_sync_running_state (app);
+}
+
+static void
+shell_app_on_ws_switch (MetaWorkspaceManager *workspace_manager,
+ int from,
+ int to,
+ MetaMotionDirection direction,
+ gpointer data)
+{
+ ShellApp *app = SHELL_APP (data);
+
+ g_assert (app->running_state != NULL);
+
+ app->running_state->window_sort_stale = TRUE;
+
+ g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
+}
+
+gboolean
+shell_app_get_busy (ShellApp *app)
+{
+ if (app->running_state != NULL &&
+ app->running_state->application_proxy != NULL &&
+ shell_org_gtk_application_get_busy (app->running_state->application_proxy))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+busy_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ ShellApp *app = user_data;
+
+ g_assert (SHELL_IS_APP (app));
+
+ g_object_notify_by_pspec (G_OBJECT (app), props[PROP_BUSY]);
+}
+
+static void
+get_application_proxy (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ShellApp *app = user_data;
+ ShellOrgGtkApplication *proxy;
+ g_autoptr (GError) error = NULL;
+
+ g_assert (SHELL_IS_APP (app));
+
+ proxy = shell_org_gtk_application_proxy_new_finish (result, &error);
+ if (proxy != NULL)
+ {
+ app->running_state->application_proxy = proxy;
+ g_signal_connect (proxy,
+ "notify::busy",
+ G_CALLBACK (busy_changed_cb),
+ app);
+ if (shell_org_gtk_application_get_busy (proxy))
+ g_object_notify_by_pspec (G_OBJECT (app), props[PROP_BUSY]);
+ }
+
+ if (app->running_state != NULL &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_clear_object (&app->running_state->cancellable);
+
+ g_object_unref (app);
+}
+
+static void
+shell_app_ensure_busy_watch (ShellApp *app)
+{
+ ShellAppRunningState *running_state = app->running_state;
+ MetaWindow *window;
+ const gchar *object_path;
+
+ if (running_state->application_proxy != NULL ||
+ running_state->cancellable != NULL)
+ return;
+
+ if (running_state->unique_bus_name == NULL)
+ return;
+
+ window = g_slist_nth_data (running_state->windows, 0);
+ object_path = meta_window_get_gtk_application_object_path (window);
+
+ if (object_path == NULL)
+ return;
+
+ running_state->cancellable = g_cancellable_new();
+ /* Take a reference to app to make sure it isn't finalized before
+ get_application_proxy runs */
+ shell_org_gtk_application_proxy_new (running_state->session,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ running_state->unique_bus_name,
+ object_path,
+ running_state->cancellable,
+ get_application_proxy,
+ g_object_ref (app));
+}
+
+void
+_shell_app_add_window (ShellApp *app,
+ MetaWindow *window)
+{
+ if (app->running_state && g_slist_find (app->running_state->windows, window))
+ return;
+
+ g_object_freeze_notify (G_OBJECT (app));
+
+ if (!app->running_state)
+ create_running_state (app);
+
+ app->running_state->window_sort_stale = TRUE;
+ app->running_state->windows = g_slist_prepend (app->running_state->windows, g_object_ref (window));
+ g_signal_connect_object (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app, 0);
+ g_signal_connect_object (window, "notify::skip-taskbar", G_CALLBACK(shell_app_on_skip_taskbar_changed), app, 0);
+
+ shell_app_update_app_actions (app, window);
+ shell_app_ensure_busy_watch (app);
+
+ if (!meta_window_is_skip_taskbar (window))
+ app->running_state->interesting_windows++;
+ shell_app_sync_running_state (app);
+
+ if (app->started_on_workspace >= 0 && !meta_window_is_on_all_workspaces (window))
+ meta_window_change_workspace_by_index (window, app->started_on_workspace, FALSE);
+ app->started_on_workspace = -1;
+
+ g_object_thaw_notify (G_OBJECT (app));
+
+ g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
+}
+
+void
+_shell_app_remove_window (ShellApp *app,
+ MetaWindow *window)
+{
+ g_assert (app->running_state != NULL);
+
+ if (!g_slist_find (app->running_state->windows, window))
+ return;
+
+ app->running_state->windows = g_slist_remove (app->running_state->windows, window);
+
+ if (!meta_window_is_skip_taskbar (window))
+ app->running_state->interesting_windows--;
+ shell_app_sync_running_state (app);
+
+ if (app->running_state->windows == NULL)
+ g_clear_pointer (&app->running_state, unref_running_state);
+
+ g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_user_time_changed), app);
+ g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_skip_taskbar_changed), app);
+ if (window == app->fallback_icon_window)
+ {
+ g_signal_handlers_disconnect_by_func (window, G_CALLBACK(on_window_icon_changed), app);
+ app->fallback_icon_window = NULL;
+
+ /* Select a new icon from a different window. */
+ g_clear_object (&app->fallback_icon);
+ g_object_notify_by_pspec (G_OBJECT (app), props[PROP_ICON]);
+ }
+
+ g_object_unref (window);
+
+ g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
+}
+
+/**
+ * shell_app_get_pids:
+ * @app: a #ShellApp
+ *
+ * Returns: (transfer container) (element-type int): An unordered list of process identifiers associated with this application.
+ */
+GSList *
+shell_app_get_pids (ShellApp *app)
+{
+ GSList *result;
+ g_autoptr (GSList) windows = NULL;
+ GSList *iter;
+
+ result = NULL;
+ windows = shell_app_get_windows (app);
+ for (iter = windows; iter; iter = iter->next)
+ {
+ MetaWindow *window = iter->data;
+ pid_t pid = meta_window_get_pid (window);
+
+ if (pid < 1)
+ continue;
+
+ /* Note in the (by far) common case, app will only have one pid, so
+ * we'll hit the first element, so don't worry about O(N^2) here.
+ */
+ if (!g_slist_find (result, GINT_TO_POINTER (pid)))
+ result = g_slist_prepend (result, GINT_TO_POINTER (pid));
+ }
+ return result;
+}
+
+void
+_shell_app_handle_startup_sequence (ShellApp *app,
+ MetaStartupSequence *sequence)
+{
+ gboolean starting = !meta_startup_sequence_get_completed (sequence);
+
+ /* The Shell design calls for on application launch, the app title
+ * appears at top, and no X window is focused. So when we get
+ * a startup-notification for this app, transition it to STARTING
+ * if it's currently stopped, set it as our application focus,
+ * but focus the no_focus window.
+ */
+ if (starting && shell_app_get_state (app) == SHELL_APP_STATE_STOPPED)
+ {
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+
+ shell_app_state_transition (app, SHELL_APP_STATE_STARTING);
+ meta_display_unset_input_focus (display,
+ meta_startup_sequence_get_timestamp (sequence));
+ }
+
+ if (starting)
+ app->started_on_workspace = meta_startup_sequence_get_workspace (sequence);
+ else if (app->running_state && app->running_state->windows)
+ shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
+ else /* application have > 1 .desktop file */
+ shell_app_state_transition (app, SHELL_APP_STATE_STOPPED);
+}
+
+/**
+ * shell_app_request_quit:
+ * @app: A #ShellApp
+ *
+ * Initiate an asynchronous request to quit this application.
+ * The application may interact with the user, and the user
+ * might cancel the quit request from the application UI.
+ *
+ * This operation may not be supported for all applications.
+ *
+ * Returns: %TRUE if a quit request is supported for this application
+ */
+gboolean
+shell_app_request_quit (ShellApp *app)
+{
+ GActionGroup *group = NULL;
+ GSList *iter;
+
+ if (shell_app_get_state (app) != SHELL_APP_STATE_RUNNING)
+ return FALSE;
+
+ /* First, check whether the app exports an explicit "quit" action
+ * that we can activate on the bus
+ */
+ group = G_ACTION_GROUP (app->running_state->muxer);
+
+ if (g_action_group_has_action (group, "app.quit") &&
+ g_action_group_get_action_parameter_type (group, "app.quit") == NULL)
+ {
+ g_action_group_activate_action (group, "app.quit", NULL);
+
+ return TRUE;
+ }
+
+ /* Otherwise, fall back to closing all the app's windows */
+ for (iter = app->running_state->windows; iter; iter = iter->next)
+ {
+ MetaWindow *win = iter->data;
+
+ if (!meta_window_can_close (win))
+ continue;
+
+ meta_window_delete (win, shell_global_get_current_time (shell_global_get ()));
+ }
+ return TRUE;
+}
+
+static void
+child_context_setup (gpointer user_data)
+{
+ ShellGlobal *shell_global = user_data;
+ MetaContext *meta_context;
+
+ g_object_get (shell_global, "context", &meta_context, NULL);
+ meta_context_restore_rlimit_nofile (meta_context, NULL);
+}
+
+#if !defined(HAVE_GIO_DESKTOP_LAUNCH_URIS_WITH_FDS) && defined(HAVE_SYSTEMD)
+/* This sets up the launched application to log to the journal
+ * using its own identifier, instead of just "gnome-session".
+ */
+static void
+app_child_setup (gpointer user_data)
+{
+ const char *appid = user_data;
+ int res;
+ int journalfd = sd_journal_stream_fd (appid, LOG_INFO, FALSE);
+ ShellGlobal *shell_global = shell_global_get ();
+
+ if (journalfd >= 0)
+ {
+ do
+ res = dup2 (journalfd, 1);
+ while (G_UNLIKELY (res == -1 && errno == EINTR));
+ do
+ res = dup2 (journalfd, 2);
+ while (G_UNLIKELY (res == -1 && errno == EINTR));
+ (void) close (journalfd);
+ }
+
+ child_context_setup (shell_global);
+}
+#endif
+
+static void
+wait_pid (GDesktopAppInfo *appinfo,
+ GPid pid,
+ gpointer user_data)
+{
+ g_child_watch_add (pid, (GChildWatchFunc) g_spawn_close_pid, NULL);
+}
+
+static void
+apply_discrete_gpu_env (GAppLaunchContext *context,
+ ShellGlobal *global)
+{
+ GDBusProxy *proxy;
+ GVariant* variant;
+ guint num_children, i;
+
+ proxy = shell_global_get_switcheroo_control (global);
+ if (!proxy)
+ {
+ g_warning ("Could not apply discrete GPU environment, switcheroo-control not available");
+ return;
+ }
+
+ variant = shell_net_hadess_switcheroo_control_get_gpus (SHELL_NET_HADESS_SWITCHEROO_CONTROL (proxy));
+ if (!variant)
+ {
+ g_warning ("Could not apply discrete GPU environment, no GPUs in list");
+ return;
+ }
+
+ num_children = g_variant_n_children (variant);
+ for (i = 0; i < num_children; i++)
+ {
+ g_autoptr(GVariant) gpu = NULL;
+ g_autoptr(GVariant) env = NULL;
+ g_autoptr(GVariant) default_variant = NULL;
+ g_autofree const char **env_s = NULL;
+ guint j;
+
+ gpu = g_variant_get_child_value (variant, i);
+ if (!gpu ||
+ !g_variant_is_of_type (gpu, G_VARIANT_TYPE ("a{s*}")))
+ continue;
+
+ /* Skip over the default GPU */
+ default_variant = g_variant_lookup_value (gpu, "Default", NULL);
+ if (!default_variant || g_variant_get_boolean (default_variant))
+ continue;
+
+ env = g_variant_lookup_value (gpu, "Environment", NULL);
+ if (!env)
+ continue;
+
+ env_s = g_variant_get_strv (env, NULL);
+ for (j = 0; env_s[j] != NULL; j = j + 2)
+ g_app_launch_context_setenv (context, env_s[j], env_s[j+1]);
+ return;
+ }
+
+ g_debug ("Could not find discrete GPU in switcheroo-control, not applying environment");
+}
+
+/**
+ * shell_app_launch:
+ * @timestamp: Event timestamp, or 0 for current event timestamp
+ * @workspace: Start on this workspace, or -1 for default
+ * @gpu_pref: the GPU to prefer launching on
+ * @error: A #GError
+ */
+gboolean
+shell_app_launch (ShellApp *app,
+ guint timestamp,
+ int workspace,
+ ShellAppLaunchGpu gpu_pref,
+ GError **error)
+{
+ ShellGlobal *global;
+ GAppLaunchContext *context;
+ gboolean ret;
+ GSpawnFlags flags;
+ gboolean discrete_gpu = FALSE;
+ ShellGlobal *shell_global = shell_global_get ();
+
+ if (app->info == NULL)
+ {
+ MetaWindow *window = window_backed_app_get_window (app);
+ /* We don't use an error return if there no longer any windows, because the
+ * user attempting to activate a stale window backed app isn't something
+ * we would expect the caller to meaningfully handle or display an error
+ * message to the user.
+ */
+ if (window)
+ meta_window_activate (window, timestamp);
+ return TRUE;
+ }
+
+ global = shell_global_get ();
+ context = shell_global_create_app_launch_context (global, timestamp, workspace);
+ if (gpu_pref == SHELL_APP_LAUNCH_GPU_APP_PREF)
+ discrete_gpu = g_desktop_app_info_get_boolean (app->info, "PrefersNonDefaultGPU");
+ else
+ discrete_gpu = (gpu_pref == SHELL_APP_LAUNCH_GPU_DISCRETE);
+
+ if (discrete_gpu)
+ apply_discrete_gpu_env (context, global);
+
+ /* Set LEAVE_DESCRIPTORS_OPEN in order to use an optimized gspawn
+ * codepath. The shell's open file descriptors should be marked CLOEXEC
+ * so that they are automatically closed even with this flag set.
+ */
+ flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
+ G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
+
+#ifdef HAVE_GIO_DESKTOP_LAUNCH_URIS_WITH_FDS
+ /* Optimized spawn path, avoiding a child_setup function */
+ {
+ int journalfd = -1;
+
+#ifdef HAVE_SYSTEMD
+ journalfd = sd_journal_stream_fd (shell_app_get_id (app), LOG_INFO, FALSE);
+#endif /* HAVE_SYSTEMD */
+
+ ret = g_desktop_app_info_launch_uris_as_manager_with_fds (app->info, NULL,
+ context,
+ flags,
+ child_context_setup, shell_global,
+ wait_pid, NULL,
+ -1,
+ journalfd,
+ journalfd,
+ error);
+
+ if (journalfd >= 0)
+ (void) close (journalfd);
+ }
+#else /* !HAVE_GIO_DESKTOP_LAUNCH_URIS_WITH_FDS */
+ ret = g_desktop_app_info_launch_uris_as_manager (app->info, NULL,
+ context,
+ flags,
+#ifdef HAVE_SYSTEMD
+ app_child_setup, (gpointer)shell_app_get_id (app),
+#else
+ child_context_setup, shell_global,
+#endif
+ wait_pid, NULL,
+ error);
+#endif /* HAVE_GIO_DESKTOP_LAUNCH_URIS_WITH_FDS */
+ g_object_unref (context);
+
+ return ret;
+}
+
+/**
+ * shell_app_launch_action:
+ * @app: the #ShellApp
+ * @action_name: the name of the action to launch (as obtained by
+ * g_desktop_app_info_list_actions())
+ * @timestamp: Event timestamp, or 0 for current event timestamp
+ * @workspace: Start on this workspace, or -1 for default
+ */
+void
+shell_app_launch_action (ShellApp *app,
+ const char *action_name,
+ guint timestamp,
+ int workspace)
+{
+ ShellGlobal *global;
+ GAppLaunchContext *context;
+
+ global = shell_global_get ();
+ context = shell_global_create_app_launch_context (global, timestamp, workspace);
+
+ g_desktop_app_info_launch_action (G_DESKTOP_APP_INFO (app->info),
+ action_name, context);
+
+ g_object_unref (context);
+}
+
+/**
+ * shell_app_get_app_info:
+ * @app: a #ShellApp
+ *
+ * Returns: (transfer none): The #GDesktopAppInfo for this app, or %NULL if backed by a window
+ */
+GDesktopAppInfo *
+shell_app_get_app_info (ShellApp *app)
+{
+ return app->info;
+}
+
+static void
+create_running_state (ShellApp *app)
+{
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+ MetaWorkspaceManager *workspace_manager =
+ meta_display_get_workspace_manager (display);
+
+ g_assert (app->running_state == NULL);
+
+ app->running_state = g_new0 (ShellAppRunningState, 1);
+ app->running_state->refcount = 1;
+ app->running_state->workspace_switch_id =
+ g_signal_connect (workspace_manager, "workspace-switched",
+ G_CALLBACK (shell_app_on_ws_switch), app);
+
+ app->running_state->session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (app->running_state->session != NULL);
+ app->running_state->muxer = gtk_action_muxer_new ();
+}
+
+void
+shell_app_update_app_actions (ShellApp *app,
+ MetaWindow *window)
+{
+ const gchar *unique_bus_name;
+
+ /* We assume that 'gtk-application-object-path' and
+ * 'gtk-app-menu-object-path' are the same for all windows which
+ * have it set.
+ *
+ * It could be possible, however, that the first window we see
+ * belonging to the app didn't have them set. For this reason, we
+ * take the values from the first window that has them set and ignore
+ * all the rest (until the app is stopped and restarted).
+ */
+
+ unique_bus_name = meta_window_get_gtk_unique_bus_name (window);
+
+ if (g_strcmp0 (app->running_state->unique_bus_name, unique_bus_name) != 0)
+ {
+ const gchar *application_object_path;
+ GDBusActionGroup *actions;
+
+ application_object_path = meta_window_get_gtk_application_object_path (window);
+
+ if (application_object_path == NULL || unique_bus_name == NULL)
+ return;
+
+ g_clear_pointer (&app->running_state->unique_bus_name, g_free);
+ app->running_state->unique_bus_name = g_strdup (unique_bus_name);
+ actions = g_dbus_action_group_get (app->running_state->session, unique_bus_name, application_object_path);
+ gtk_action_muxer_insert (app->running_state->muxer, "app", G_ACTION_GROUP (actions));
+ g_object_unref (actions);
+ }
+}
+
+static void
+unref_running_state (ShellAppRunningState *state)
+{
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+ MetaWorkspaceManager *workspace_manager =
+ meta_display_get_workspace_manager (display);
+
+ g_assert (state->refcount > 0);
+
+ state->refcount--;
+ if (state->refcount > 0)
+ return;
+
+ g_clear_signal_handler (&state->workspace_switch_id, workspace_manager);
+
+ g_clear_object (&state->application_proxy);
+
+ if (state->cancellable != NULL)
+ {
+ g_cancellable_cancel (state->cancellable);
+ g_clear_object (&state->cancellable);
+ }
+
+ g_clear_object (&state->muxer);
+ g_clear_object (&state->session);
+ g_clear_pointer (&state->unique_bus_name, g_free);
+
+ g_free (state);
+}
+
+/**
+ * shell_app_compare_by_name:
+ * @app: One app
+ * @other: The other app
+ *
+ * Order two applications by name.
+ *
+ * Returns: -1, 0, or 1; suitable for use as a comparison function
+ * for e.g. g_slist_sort()
+ */
+int
+shell_app_compare_by_name (ShellApp *app, ShellApp *other)
+{
+ return strcmp (app->name_collation_key, other->name_collation_key);
+}
+
+static void
+shell_app_init (ShellApp *self)
+{
+ self->state = SHELL_APP_STATE_STOPPED;
+ self->started_on_workspace = -1;
+}
+
+static void
+shell_app_dispose (GObject *object)
+{
+ ShellApp *app = SHELL_APP (object);
+
+ g_clear_object (&app->info);
+ g_clear_object (&app->fallback_icon);
+
+ while (app->running_state)
+ _shell_app_remove_window (app, app->running_state->windows->data);
+
+ /* We should have been transitioned when we removed all of our windows */
+ g_assert (app->state == SHELL_APP_STATE_STOPPED);
+ g_assert (app->running_state == NULL);
+
+ G_OBJECT_CLASS(shell_app_parent_class)->dispose (object);
+}
+
+static void
+shell_app_finalize (GObject *object)
+{
+ ShellApp *app = SHELL_APP (object);
+
+ g_free (app->window_id_string);
+
+ g_free (app->name_collation_key);
+
+ G_OBJECT_CLASS(shell_app_parent_class)->finalize (object);
+}
+
+static void
+shell_app_class_init(ShellAppClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = shell_app_get_property;
+ gobject_class->set_property = shell_app_set_property;
+ gobject_class->dispose = shell_app_dispose;
+ gobject_class->finalize = shell_app_finalize;
+
+ shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
+ SHELL_TYPE_APP,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * ShellApp:state:
+ *
+ * The high-level state of the application, effectively whether it's
+ * running or not, or transitioning between those states.
+ */
+ props[PROP_STATE] =
+ g_param_spec_enum ("state",
+ "State",
+ "Application state",
+ SHELL_TYPE_APP_STATE,
+ SHELL_APP_STATE_STOPPED,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellApp:busy:
+ *
+ * Whether the application has marked itself as busy.
+ */
+ props[PROP_BUSY] =
+ g_param_spec_boolean ("busy",
+ "Busy",
+ "Busy state",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellApp:id:
+ *
+ * The id of this application (a desktop filename, or a special string
+ * like window:0xabcd1234)
+ */
+ props[PROP_ID] =
+ g_param_spec_string ("id",
+ "Application id",
+ "The desktop file id of this ShellApp",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellApp:icon:
+ *
+ * The #GIcon representing this ShellApp
+ */
+ props[PROP_ICON] =
+ g_param_spec_object ("icon",
+ "GIcon",
+ "The GIcon representing this app",
+ G_TYPE_ICON,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellApp:action-group:
+ *
+ * The #GDBusActionGroup associated with this ShellApp, if any. See the
+ * documentation of #GApplication and #GActionGroup for details.
+ */
+ props[PROP_ACTION_GROUP] =
+ g_param_spec_object ("action-group",
+ "Application Action Group",
+ "The action group exported by the remote application",
+ G_TYPE_ACTION_GROUP,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellApp:app-info:
+ *
+ * The #GDesktopAppInfo associated with this ShellApp, if any.
+ */
+ props[PROP_APP_INFO] =
+ g_param_spec_object ("app-info",
+ "DesktopAppInfo",
+ "The DesktopAppInfo associated with this app",
+ G_TYPE_DESKTOP_APP_INFO,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+}
diff --git a/src/shell-app.h b/src/shell-app.h
new file mode 100644
index 0000000..bf6f45e
--- /dev/null
+++ b/src/shell-app.h
@@ -0,0 +1,83 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_APP_H__
+#define __SHELL_APP_H__
+
+#include <clutter/clutter.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <meta/window.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_APP (shell_app_get_type ())
+G_DECLARE_FINAL_TYPE (ShellApp, shell_app, SHELL, APP, GObject)
+
+typedef enum {
+ SHELL_APP_STATE_STOPPED,
+ SHELL_APP_STATE_STARTING,
+ SHELL_APP_STATE_RUNNING
+} ShellAppState;
+
+typedef enum {
+ SHELL_APP_LAUNCH_GPU_APP_PREF = 0,
+ SHELL_APP_LAUNCH_GPU_DISCRETE,
+ SHELL_APP_LAUNCH_GPU_DEFAULT
+} ShellAppLaunchGpu;
+
+const char *shell_app_get_id (ShellApp *app);
+
+GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
+
+ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size);
+GIcon *shell_app_get_icon (ShellApp *app);
+const char *shell_app_get_name (ShellApp *app);
+const char *shell_app_get_description (ShellApp *app);
+gboolean shell_app_is_window_backed (ShellApp *app);
+
+void shell_app_activate_window (ShellApp *app, MetaWindow *window, guint32 timestamp);
+
+void shell_app_activate (ShellApp *app);
+
+void shell_app_activate_full (ShellApp *app,
+ int workspace,
+ guint32 timestamp);
+
+void shell_app_open_new_window (ShellApp *app,
+ int workspace);
+gboolean shell_app_can_open_new_window (ShellApp *app);
+
+ShellAppState shell_app_get_state (ShellApp *app);
+
+gboolean shell_app_request_quit (ShellApp *app);
+
+guint shell_app_get_n_windows (ShellApp *app);
+
+GSList *shell_app_get_windows (ShellApp *app);
+
+GSList *shell_app_get_pids (ShellApp *app);
+
+gboolean shell_app_is_on_workspace (ShellApp *app, MetaWorkspace *workspace);
+
+gboolean shell_app_launch (ShellApp *app,
+ guint timestamp,
+ int workspace,
+ ShellAppLaunchGpu gpu_pref,
+ GError **error);
+
+void shell_app_launch_action (ShellApp *app,
+ const char *action_name,
+ guint timestamp,
+ int workspace);
+
+int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
+
+int shell_app_compare (ShellApp *app, ShellApp *other);
+
+void shell_app_update_window_actions (ShellApp *app, MetaWindow *window);
+void shell_app_update_app_actions (ShellApp *app, MetaWindow *window);
+
+gboolean shell_app_get_busy (ShellApp *app);
+
+G_END_DECLS
+
+#endif /* __SHELL_APP_H__ */
diff --git a/src/shell-blur-effect.c b/src/shell-blur-effect.c
new file mode 100644
index 0000000..0d4bb45
--- /dev/null
+++ b/src/shell-blur-effect.c
@@ -0,0 +1,907 @@
+/* shell-blur-effect.c
+ *
+ * Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "shell-blur-effect.h"
+
+#include "shell-enum-types.h"
+
+/**
+ * SECTION:shell-blur-effect
+ * @short_description: Blur effect for actors
+ *
+ * #ShellBlurEffect is a blur implementation based on Clutter. It also has
+ * an optional brightness property.
+ *
+ * # Modes
+ *
+ * #ShellBlurEffect can work in @SHELL_BLUR_MODE_BACKGROUND and @SHELL_BLUR_MODE_ACTOR
+ * modes. The actor mode blurs the actor itself, and all of its children. The
+ * background mode blurs the pixels beneath the actor, but not the actor itself.
+ *
+ * @SHELL_BLUR_MODE_BACKGROUND can be computationally expensive, since the contents
+ * beneath the actor cannot be cached, so beware of the performance implications
+ * of using this blur mode.
+ */
+
+static const gchar *brightness_glsl_declarations =
+"uniform float brightness; \n";
+
+static const gchar *brightness_glsl =
+" cogl_color_out.rgb *= brightness; \n";
+
+#define MIN_DOWNSCALE_SIZE 256.f
+#define MAX_SIGMA 6.f
+
+typedef enum
+{
+ ACTOR_PAINTED = 1 << 0,
+ BLUR_APPLIED = 1 << 1,
+} CacheFlags;
+
+typedef struct
+{
+ CoglFramebuffer *framebuffer;
+ CoglPipeline *pipeline;
+ CoglTexture *texture;
+} FramebufferData;
+
+struct _ShellBlurEffect
+{
+ ClutterEffect parent_instance;
+
+ ClutterActor *actor;
+
+ unsigned int tex_width;
+ unsigned int tex_height;
+
+ /* The cached contents */
+ FramebufferData actor_fb;
+ CacheFlags cache_flags;
+
+ FramebufferData background_fb;
+ FramebufferData brightness_fb;
+ int brightness_uniform;
+
+ ShellBlurMode mode;
+ float downscale_factor;
+ float brightness;
+ int sigma;
+};
+
+G_DEFINE_TYPE (ShellBlurEffect, shell_blur_effect, CLUTTER_TYPE_EFFECT)
+
+enum {
+ PROP_0,
+ PROP_SIGMA,
+ PROP_BRIGHTNESS,
+ PROP_MODE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS] = { NULL, };
+
+static CoglPipeline*
+create_base_pipeline (void)
+{
+ static CoglPipeline *base_pipeline = NULL;
+
+ if (G_UNLIKELY (base_pipeline == NULL))
+ {
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ base_pipeline = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_layer_null_texture (base_pipeline, 0);
+ cogl_pipeline_set_layer_filters (base_pipeline,
+ 0,
+ COGL_PIPELINE_FILTER_LINEAR,
+ COGL_PIPELINE_FILTER_LINEAR);
+ cogl_pipeline_set_layer_wrap_mode (base_pipeline,
+ 0,
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+ }
+
+ return cogl_pipeline_copy (base_pipeline);
+}
+
+static CoglPipeline*
+create_brightness_pipeline (void)
+{
+ static CoglPipeline *brightness_pipeline = NULL;
+
+ if (G_UNLIKELY (brightness_pipeline == NULL))
+ {
+ CoglSnippet *snippet;
+
+ brightness_pipeline = create_base_pipeline ();
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ brightness_glsl_declarations,
+ brightness_glsl);
+ cogl_pipeline_add_snippet (brightness_pipeline, snippet);
+ cogl_object_unref (snippet);
+ }
+
+ return cogl_pipeline_copy (brightness_pipeline);
+}
+
+
+static void
+update_brightness (ShellBlurEffect *self,
+ uint8_t paint_opacity)
+{
+ cogl_pipeline_set_color4ub (self->brightness_fb.pipeline,
+ paint_opacity,
+ paint_opacity,
+ paint_opacity,
+ paint_opacity);
+
+ if (self->brightness_uniform > -1)
+ {
+ cogl_pipeline_set_uniform_1f (self->brightness_fb.pipeline,
+ self->brightness_uniform,
+ self->brightness);
+ }
+}
+
+static void
+setup_projection_matrix (CoglFramebuffer *framebuffer,
+ float width,
+ float height)
+{
+ graphene_matrix_t projection;
+
+ graphene_matrix_init_translate (&projection,
+ &GRAPHENE_POINT3D_INIT (-width / 2.0,
+ -height / 2.0,
+ 0.f));
+ graphene_matrix_scale (&projection, 2.0 / width, -2.0 / height, 1.f);
+
+ cogl_framebuffer_set_projection_matrix (framebuffer, &projection);
+}
+
+static gboolean
+update_fbo (FramebufferData *data,
+ unsigned int width,
+ unsigned int height,
+ float downscale_factor)
+{
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ g_clear_pointer (&data->texture, cogl_object_unref);
+ g_clear_object (&data->framebuffer);
+
+ float new_width = floorf (width / downscale_factor);
+ float new_height = floorf (height / downscale_factor);
+
+ data->texture = cogl_texture_2d_new_with_size (ctx, new_width, new_height);
+ if (!data->texture)
+ return FALSE;
+
+ cogl_pipeline_set_layer_texture (data->pipeline, 0, data->texture);
+
+ data->framebuffer =
+ COGL_FRAMEBUFFER (cogl_offscreen_new_with_texture (data->texture));
+ if (!data->framebuffer)
+ {
+ g_warning ("%s: Unable to create an Offscreen buffer", G_STRLOC);
+ return FALSE;
+ }
+
+ setup_projection_matrix (data->framebuffer, new_width, new_height);
+
+ return TRUE;
+}
+
+static gboolean
+update_actor_fbo (ShellBlurEffect *self,
+ unsigned int width,
+ unsigned int height,
+ float downscale_factor)
+{
+ if (self->tex_width == width &&
+ self->tex_height == height &&
+ self->downscale_factor == downscale_factor &&
+ self->actor_fb.framebuffer)
+ {
+ return TRUE;
+ }
+
+ self->cache_flags &= ~ACTOR_PAINTED;
+
+ return update_fbo (&self->actor_fb, width, height, downscale_factor);
+}
+
+static gboolean
+update_brightness_fbo (ShellBlurEffect *self,
+ unsigned int width,
+ unsigned int height,
+ float downscale_factor)
+{
+ if (self->tex_width == width &&
+ self->tex_height == height &&
+ self->downscale_factor == downscale_factor &&
+ self->brightness_fb.framebuffer)
+ {
+ return TRUE;
+ }
+
+ return update_fbo (&self->brightness_fb,
+ width, height,
+ downscale_factor);
+}
+
+static gboolean
+update_background_fbo (ShellBlurEffect *self,
+ unsigned int width,
+ unsigned int height)
+{
+ if (self->tex_width == width &&
+ self->tex_height == height &&
+ self->background_fb.framebuffer)
+ {
+ return TRUE;
+ }
+
+ return update_fbo (&self->background_fb, width, height, 1.0);
+}
+
+static void
+clear_framebuffer_data (FramebufferData *fb_data)
+{
+ g_clear_pointer (&fb_data->texture, cogl_object_unref);
+ g_clear_object (&fb_data->framebuffer);
+}
+
+static float
+calculate_downscale_factor (float width,
+ float height,
+ float sigma)
+{
+ float downscale_factor = 1.0;
+ float scaled_width = width;
+ float scaled_height = height;
+ float scaled_sigma = sigma;
+
+ /* This is the algorithm used by Firefox; keep downscaling until either the
+ * blur radius is lower than the threshold, or the downscaled texture is too
+ * small.
+ */
+ while (scaled_sigma > MAX_SIGMA &&
+ scaled_width > MIN_DOWNSCALE_SIZE &&
+ scaled_height > MIN_DOWNSCALE_SIZE)
+ {
+ downscale_factor *= 2.f;
+
+ scaled_width = width / downscale_factor;
+ scaled_height = height / downscale_factor;
+ scaled_sigma = sigma / downscale_factor;
+ }
+
+ return downscale_factor;
+}
+
+static void
+shell_blur_effect_set_actor (ClutterActorMeta *meta,
+ ClutterActor *actor)
+{
+ ShellBlurEffect *self = SHELL_BLUR_EFFECT (meta);
+ ClutterActorMetaClass *meta_class;
+
+ meta_class = CLUTTER_ACTOR_META_CLASS (shell_blur_effect_parent_class);
+ meta_class->set_actor (meta, actor);
+
+ /* clear out the previous state */
+ clear_framebuffer_data (&self->actor_fb);
+ clear_framebuffer_data (&self->background_fb);
+ clear_framebuffer_data (&self->brightness_fb);
+
+ /* we keep a back pointer here, to avoid going through the ActorMeta */
+ self->actor = clutter_actor_meta_get_actor (meta);
+}
+
+static void
+update_actor_box (ShellBlurEffect *self,
+ ClutterPaintContext *paint_context,
+ ClutterActorBox *source_actor_box)
+{
+ ClutterStageView *stage_view;
+ float box_scale_factor = 1.0f;
+ float origin_x, origin_y;
+ float width, height;
+
+ switch (self->mode)
+ {
+ case SHELL_BLUR_MODE_ACTOR:
+ clutter_actor_get_allocation_box (self->actor, source_actor_box);
+ break;
+
+ case SHELL_BLUR_MODE_BACKGROUND:
+ stage_view = clutter_paint_context_get_stage_view (paint_context);
+
+ clutter_actor_get_transformed_position (self->actor, &origin_x, &origin_y);
+ clutter_actor_get_transformed_size (self->actor, &width, &height);
+
+ if (stage_view)
+ {
+ cairo_rectangle_int_t stage_view_layout;
+
+ box_scale_factor = clutter_stage_view_get_scale (stage_view);
+ clutter_stage_view_get_layout (stage_view, &stage_view_layout);
+
+ origin_x -= stage_view_layout.x;
+ origin_y -= stage_view_layout.y;
+ }
+ else
+ {
+ /* If we're drawing off stage, just assume scale = 1, this won't work
+ * with stage-view scaling though.
+ */
+ }
+
+ clutter_actor_box_set_origin (source_actor_box, origin_x, origin_y);
+ clutter_actor_box_set_size (source_actor_box, width, height);
+
+ clutter_actor_box_scale (source_actor_box, box_scale_factor);
+ break;
+ }
+
+ clutter_actor_box_clamp_to_pixel (source_actor_box);
+}
+
+static void
+add_blurred_pipeline (ShellBlurEffect *self,
+ ClutterPaintNode *node,
+ uint8_t paint_opacity)
+{
+ g_autoptr (ClutterPaintNode) pipeline_node = NULL;
+ float width, height;
+
+ /* Use the untransformed actor size here, since the framebuffer itself already
+ * has the actor transform matrix applied.
+ */
+ clutter_actor_get_size (self->actor, &width, &height);
+
+ update_brightness (self, paint_opacity);
+
+ pipeline_node = clutter_pipeline_node_new (self->brightness_fb.pipeline);
+ clutter_paint_node_set_static_name (pipeline_node, "ShellBlurEffect (final)");
+ clutter_paint_node_add_child (node, pipeline_node);
+
+ clutter_paint_node_add_rectangle (pipeline_node,
+ &(ClutterActorBox) {
+ 0.f, 0.f,
+ width,
+ height,
+ });
+}
+
+static ClutterPaintNode *
+create_blur_nodes (ShellBlurEffect *self,
+ ClutterPaintNode *node,
+ uint8_t paint_opacity)
+{
+ g_autoptr (ClutterPaintNode) brightness_node = NULL;
+ g_autoptr (ClutterPaintNode) blur_node = NULL;
+ float width;
+ float height;
+
+ clutter_actor_get_size (self->actor, &width, &height);
+
+ update_brightness (self, paint_opacity);
+ brightness_node = clutter_layer_node_new_to_framebuffer (self->brightness_fb.framebuffer,
+ self->brightness_fb.pipeline);
+ clutter_paint_node_set_static_name (brightness_node, "ShellBlurEffect (brightness)");
+ clutter_paint_node_add_child (node, brightness_node);
+ clutter_paint_node_add_rectangle (brightness_node,
+ &(ClutterActorBox) {
+ 0.f, 0.f,
+ width, height,
+ });
+
+ blur_node = clutter_blur_node_new (self->tex_width / self->downscale_factor,
+ self->tex_height / self->downscale_factor,
+ self->sigma / self->downscale_factor);
+ clutter_paint_node_set_static_name (blur_node, "ShellBlurEffect (blur)");
+ clutter_paint_node_add_child (brightness_node, blur_node);
+ clutter_paint_node_add_rectangle (blur_node,
+ &(ClutterActorBox) {
+ 0.f, 0.f,
+ cogl_texture_get_width (self->brightness_fb.texture),
+ cogl_texture_get_height (self->brightness_fb.texture),
+ });
+
+ self->cache_flags |= BLUR_APPLIED;
+
+ return g_steal_pointer (&blur_node);
+}
+
+static void
+paint_background (ShellBlurEffect *self,
+ ClutterPaintNode *node,
+ ClutterPaintContext *paint_context,
+ ClutterActorBox *source_actor_box)
+{
+ g_autoptr (ClutterPaintNode) background_node = NULL;
+ g_autoptr (ClutterPaintNode) blit_node = NULL;
+ CoglFramebuffer *src;
+ float transformed_x;
+ float transformed_y;
+ float transformed_width;
+ float transformed_height;
+
+ clutter_actor_box_get_origin (source_actor_box,
+ &transformed_x,
+ &transformed_y);
+ clutter_actor_box_get_size (source_actor_box,
+ &transformed_width,
+ &transformed_height);
+
+ /* Background layer node */
+ background_node =
+ clutter_layer_node_new_to_framebuffer (self->background_fb.framebuffer,
+ self->background_fb.pipeline);
+ clutter_paint_node_set_static_name (background_node, "ShellBlurEffect (background)");
+ clutter_paint_node_add_child (node, background_node);
+ clutter_paint_node_add_rectangle (background_node,
+ &(ClutterActorBox) {
+ 0.f, 0.f,
+ self->tex_width / self->downscale_factor,
+ self->tex_height / self->downscale_factor,
+ });
+
+ /* Blit node */
+ src = clutter_paint_context_get_framebuffer (paint_context);
+ blit_node = clutter_blit_node_new (src);
+ clutter_paint_node_set_static_name (blit_node, "ShellBlurEffect (blit)");
+ clutter_paint_node_add_child (background_node, blit_node);
+ clutter_blit_node_add_blit_rectangle (CLUTTER_BLIT_NODE (blit_node),
+ transformed_x,
+ transformed_y,
+ 0, 0,
+ transformed_width,
+ transformed_height);
+}
+
+static gboolean
+update_framebuffers (ShellBlurEffect *self,
+ ClutterPaintContext *paint_context,
+ ClutterActorBox *source_actor_box)
+{
+ gboolean updated = FALSE;
+ float downscale_factor;
+ float height = -1;
+ float width = -1;
+
+ clutter_actor_box_get_size (source_actor_box, &width, &height);
+
+ downscale_factor = calculate_downscale_factor (width, height, self->sigma);
+
+ updated = update_actor_fbo (self, width, height, downscale_factor) &&
+ update_brightness_fbo (self, width, height, downscale_factor);
+
+ if (self->mode == SHELL_BLUR_MODE_BACKGROUND)
+ updated = updated && update_background_fbo (self, width, height);
+
+ self->tex_width = width;
+ self->tex_height = height;
+ self->downscale_factor = downscale_factor;
+
+ return updated;
+}
+
+static void
+add_actor_node (ShellBlurEffect *self,
+ ClutterPaintNode *node,
+ int opacity)
+{
+ g_autoptr (ClutterPaintNode) actor_node = NULL;
+
+ actor_node = clutter_actor_node_new (self->actor, opacity);
+ clutter_paint_node_add_child (node, actor_node);
+}
+
+static void
+paint_actor_offscreen (ShellBlurEffect *self,
+ ClutterPaintNode *node,
+ ClutterEffectPaintFlags flags)
+{
+ gboolean actor_dirty;
+
+ actor_dirty = (flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY) != 0;
+
+ /* The actor offscreen framebuffer is updated already */
+ if (actor_dirty || !(self->cache_flags & ACTOR_PAINTED))
+ {
+ g_autoptr (ClutterPaintNode) transform_node = NULL;
+ g_autoptr (ClutterPaintNode) layer_node = NULL;
+ graphene_matrix_t transform;
+
+ /* Layer node */
+ layer_node = clutter_layer_node_new_to_framebuffer (self->actor_fb.framebuffer,
+ self->actor_fb.pipeline);
+ clutter_paint_node_set_static_name (layer_node, "ShellBlurEffect (actor offscreen)");
+ clutter_paint_node_add_child (node, layer_node);
+ clutter_paint_node_add_rectangle (layer_node,
+ &(ClutterActorBox) {
+ 0.f, 0.f,
+ self->tex_width / self->downscale_factor,
+ self->tex_height / self->downscale_factor,
+ });
+
+ /* Transform node */
+ graphene_matrix_init_scale (&transform,
+ 1.f / self->downscale_factor,
+ 1.f / self->downscale_factor,
+ 1.f);
+ transform_node = clutter_transform_node_new (&transform);
+ clutter_paint_node_set_static_name (transform_node, "ShellBlurEffect (downscale)");
+ clutter_paint_node_add_child (layer_node, transform_node);
+
+ /* Actor node */
+ add_actor_node (self, transform_node, 255);
+
+ self->cache_flags |= ACTOR_PAINTED;
+ }
+ else
+ {
+ g_autoptr (ClutterPaintNode) pipeline_node = NULL;
+
+ pipeline_node = clutter_pipeline_node_new (self->actor_fb.pipeline);
+ clutter_paint_node_set_static_name (pipeline_node,
+ "ShellBlurEffect (actor texture)");
+ clutter_paint_node_add_child (node, pipeline_node);
+ clutter_paint_node_add_rectangle (pipeline_node,
+ &(ClutterActorBox) {
+ 0.f, 0.f,
+ self->tex_width / self->downscale_factor,
+ self->tex_height / self->downscale_factor,
+ });
+ }
+}
+
+static gboolean
+needs_repaint (ShellBlurEffect *self,
+ ClutterEffectPaintFlags flags)
+{
+ gboolean actor_cached;
+ gboolean blur_cached;
+ gboolean actor_dirty;
+
+ actor_dirty = (flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY) != 0;
+ blur_cached = (self->cache_flags & BLUR_APPLIED) != 0;
+ actor_cached = (self->cache_flags & ACTOR_PAINTED) != 0;
+
+ switch (self->mode)
+ {
+ case SHELL_BLUR_MODE_ACTOR:
+ return actor_dirty || !blur_cached || !actor_cached;
+
+ case SHELL_BLUR_MODE_BACKGROUND:
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+static void
+shell_blur_effect_paint_node (ClutterEffect *effect,
+ ClutterPaintNode *node,
+ ClutterPaintContext *paint_context,
+ ClutterEffectPaintFlags flags)
+{
+ ShellBlurEffect *self = SHELL_BLUR_EFFECT (effect);
+ uint8_t paint_opacity;
+
+ g_assert (self->actor != NULL);
+
+ if (self->sigma > 0)
+ {
+ g_autoptr (ClutterPaintNode) blur_node = NULL;
+
+ switch (self->mode)
+ {
+ case SHELL_BLUR_MODE_ACTOR:
+ paint_opacity = clutter_actor_get_paint_opacity (self->actor);
+ break;
+
+ case SHELL_BLUR_MODE_BACKGROUND:
+ paint_opacity = 255;
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ if (needs_repaint (self, flags))
+ {
+ ClutterActorBox source_actor_box;
+
+ update_actor_box (self, paint_context, &source_actor_box);
+
+ /* Failing to create or update the offscreen framebuffers prevents
+ * the entire effect to be applied.
+ */
+ if (!update_framebuffers (self, paint_context, &source_actor_box))
+ goto fail;
+
+ blur_node = create_blur_nodes (self, node, paint_opacity);
+
+ switch (self->mode)
+ {
+ case SHELL_BLUR_MODE_ACTOR:
+ paint_actor_offscreen (self, blur_node, flags);
+ break;
+
+ case SHELL_BLUR_MODE_BACKGROUND:
+ paint_background (self, blur_node, paint_context, &source_actor_box);
+ break;
+ }
+ }
+ else
+ {
+ /* Use the cached pipeline if no repaint is needed */
+ add_blurred_pipeline (self, node, paint_opacity);
+ }
+
+ /* Background blur needs to paint the actor after painting the blurred
+ * background.
+ */
+ switch (self->mode)
+ {
+ case SHELL_BLUR_MODE_ACTOR:
+ break;
+
+ case SHELL_BLUR_MODE_BACKGROUND:
+ add_actor_node (self, node, -1);
+ break;
+ }
+
+ return;
+ }
+
+fail:
+ /* When no blur is applied, or the offscreen framebuffers
+ * couldn't be created, fallback to simply painting the actor.
+ */
+ add_actor_node (self, node, -1);
+}
+
+static void
+shell_blur_effect_finalize (GObject *object)
+{
+ ShellBlurEffect *self = (ShellBlurEffect *)object;
+
+ clear_framebuffer_data (&self->actor_fb);
+ clear_framebuffer_data (&self->background_fb);
+ clear_framebuffer_data (&self->brightness_fb);
+
+ g_clear_pointer (&self->actor_fb.pipeline, cogl_object_unref);
+ g_clear_pointer (&self->background_fb.pipeline, cogl_object_unref);
+ g_clear_pointer (&self->brightness_fb.pipeline, cogl_object_unref);
+
+ G_OBJECT_CLASS (shell_blur_effect_parent_class)->finalize (object);
+}
+
+static void
+shell_blur_effect_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellBlurEffect *self = SHELL_BLUR_EFFECT (object);
+
+ switch (prop_id)
+ {
+ case PROP_SIGMA:
+ g_value_set_int (value, self->sigma);
+ break;
+
+ case PROP_BRIGHTNESS:
+ g_value_set_float (value, self->brightness);
+ break;
+
+ case PROP_MODE:
+ g_value_set_enum (value, self->mode);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+shell_blur_effect_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellBlurEffect *self = SHELL_BLUR_EFFECT (object);
+
+ switch (prop_id)
+ {
+ case PROP_SIGMA:
+ shell_blur_effect_set_sigma (self, g_value_get_int (value));
+ break;
+
+ case PROP_BRIGHTNESS:
+ shell_blur_effect_set_brightness (self, g_value_get_float (value));
+ break;
+
+ case PROP_MODE:
+ shell_blur_effect_set_mode (self, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+shell_blur_effect_class_init (ShellBlurEffectClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
+ ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
+
+ object_class->finalize = shell_blur_effect_finalize;
+ object_class->get_property = shell_blur_effect_get_property;
+ object_class->set_property = shell_blur_effect_set_property;
+
+ meta_class->set_actor = shell_blur_effect_set_actor;
+
+ effect_class->paint_node = shell_blur_effect_paint_node;
+
+ properties[PROP_SIGMA] =
+ g_param_spec_int ("sigma",
+ "Sigma",
+ "Sigma",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ properties[PROP_BRIGHTNESS] =
+ g_param_spec_float ("brightness",
+ "Brightness",
+ "Brightness",
+ 0.f, 1.f, 1.f,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ properties[PROP_MODE] =
+ g_param_spec_enum ("mode",
+ "Blur mode",
+ "Blur mode",
+ SHELL_TYPE_BLUR_MODE,
+ SHELL_BLUR_MODE_ACTOR,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+shell_blur_effect_init (ShellBlurEffect *self)
+{
+ self->mode = SHELL_BLUR_MODE_ACTOR;
+ self->sigma = 0;
+ self->brightness = 1.f;
+
+ self->actor_fb.pipeline = create_base_pipeline ();
+ self->background_fb.pipeline = create_base_pipeline ();
+ self->brightness_fb.pipeline = create_brightness_pipeline ();
+ self->brightness_uniform =
+ cogl_pipeline_get_uniform_location (self->brightness_fb.pipeline, "brightness");
+}
+
+ShellBlurEffect *
+shell_blur_effect_new (void)
+{
+ return g_object_new (SHELL_TYPE_BLUR_EFFECT, NULL);
+}
+
+int
+shell_blur_effect_get_sigma (ShellBlurEffect *self)
+{
+ g_return_val_if_fail (SHELL_IS_BLUR_EFFECT (self), -1);
+
+ return self->sigma;
+}
+
+void
+shell_blur_effect_set_sigma (ShellBlurEffect *self,
+ int sigma)
+{
+ g_return_if_fail (SHELL_IS_BLUR_EFFECT (self));
+
+ if (self->sigma == sigma)
+ return;
+
+ self->sigma = sigma;
+ self->cache_flags &= ~BLUR_APPLIED;
+
+ if (self->actor)
+ clutter_effect_queue_repaint (CLUTTER_EFFECT (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIGMA]);
+}
+
+float
+shell_blur_effect_get_brightness (ShellBlurEffect *self)
+{
+ g_return_val_if_fail (SHELL_IS_BLUR_EFFECT (self), FALSE);
+
+ return self->brightness;
+}
+
+void
+shell_blur_effect_set_brightness (ShellBlurEffect *self,
+ float brightness)
+{
+ g_return_if_fail (SHELL_IS_BLUR_EFFECT (self));
+
+ if (self->brightness == brightness)
+ return;
+
+ self->brightness = brightness;
+ self->cache_flags &= ~BLUR_APPLIED;
+
+ if (self->actor)
+ clutter_effect_queue_repaint (CLUTTER_EFFECT (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BRIGHTNESS]);
+}
+
+ShellBlurMode
+shell_blur_effect_get_mode (ShellBlurEffect *self)
+{
+ g_return_val_if_fail (SHELL_IS_BLUR_EFFECT (self), -1);
+
+ return self->mode;
+}
+
+void
+shell_blur_effect_set_mode (ShellBlurEffect *self,
+ ShellBlurMode mode)
+{
+ g_return_if_fail (SHELL_IS_BLUR_EFFECT (self));
+
+ if (self->mode == mode)
+ return;
+
+ self->mode = mode;
+ self->cache_flags &= ~BLUR_APPLIED;
+
+ switch (mode)
+ {
+ case SHELL_BLUR_MODE_ACTOR:
+ clear_framebuffer_data (&self->background_fb);
+ break;
+
+ case SHELL_BLUR_MODE_BACKGROUND:
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ if (self->actor)
+ clutter_effect_queue_repaint (CLUTTER_EFFECT (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODE]);
+}
diff --git a/src/shell-blur-effect.h b/src/shell-blur-effect.h
new file mode 100644
index 0000000..a7486cc
--- /dev/null
+++ b/src/shell-blur-effect.h
@@ -0,0 +1,57 @@
+/* shell-blur-effect.h
+ *
+ * Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+/**
+ * ShellBlurMode:
+ * @SHELL_BLUR_MODE_ACTOR: blur the actor contents, and its children
+ * @SHELL_BLUR_MODE_BACKGROUND: blur what's beneath the actor
+ *
+ * The mode of blurring of the effect.
+ */
+typedef enum
+{
+ SHELL_BLUR_MODE_ACTOR,
+ SHELL_BLUR_MODE_BACKGROUND,
+} ShellBlurMode;
+
+#define SHELL_TYPE_BLUR_EFFECT (shell_blur_effect_get_type())
+G_DECLARE_FINAL_TYPE (ShellBlurEffect, shell_blur_effect, SHELL, BLUR_EFFECT, ClutterEffect)
+
+ShellBlurEffect *shell_blur_effect_new (void);
+
+int shell_blur_effect_get_sigma (ShellBlurEffect *self);
+void shell_blur_effect_set_sigma (ShellBlurEffect *self,
+ int sigma);
+
+float shell_blur_effect_get_brightness (ShellBlurEffect *self);
+void shell_blur_effect_set_brightness (ShellBlurEffect *self,
+ float brightness);
+
+ShellBlurMode shell_blur_effect_get_mode (ShellBlurEffect *self);
+void shell_blur_effect_set_mode (ShellBlurEffect *self,
+ ShellBlurMode mode);
+
+G_END_DECLS
diff --git a/src/shell-embedded-window-private.h b/src/shell-embedded-window-private.h
new file mode 100644
index 0000000..5714af9
--- /dev/null
+++ b/src/shell-embedded-window-private.h
@@ -0,0 +1,20 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_EMBEDDED_WINDOW_PRIVATE_H__
+#define __SHELL_EMBEDDED_WINDOW_PRIVATE_H__
+
+#include "shell-embedded-window.h"
+#include "shell-gtk-embed.h"
+
+void _shell_embedded_window_set_actor (ShellEmbeddedWindow *window,
+ ShellGtkEmbed *embed);
+
+void _shell_embedded_window_allocate (ShellEmbeddedWindow *window,
+ int x,
+ int y,
+ int width,
+ int height);
+
+void _shell_embedded_window_map (ShellEmbeddedWindow *window);
+void _shell_embedded_window_unmap (ShellEmbeddedWindow *window);
+
+#endif /* __SHELL_EMBEDDED_WINDOW_PRIVATE_H__ */
diff --git a/src/shell-embedded-window.c b/src/shell-embedded-window.c
new file mode 100644
index 0000000..8fd6112
--- /dev/null
+++ b/src/shell-embedded-window.c
@@ -0,0 +1,247 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <gdk/gdkx.h>
+
+#include "shell-embedded-window-private.h"
+
+/* This type is a subclass of GtkWindow that ties the window to a
+ * ShellGtkEmbed; the resizing logic is bound to the clutter logic.
+ *
+ * The typical usage we might expect is
+ *
+ * - ShellEmbeddedWindow is created and filled with content
+ * - ShellEmbeddedWindow is shown with gtk_widget_show_all()
+ * - ShellGtkEmbed is created for the ShellEmbeddedWindow
+ * - actor is added to a stage
+ *
+ * The way it works is that the GtkWindow is mapped if and only if both:
+ *
+ * - gtk_widget_visible (window) [widget has been shown]
+ * - Actor is mapped [actor and all parents visible, actor in stage]
+ */
+
+enum {
+ PROP_0
+};
+
+typedef struct _ShellEmbeddedWindowPrivate ShellEmbeddedWindowPrivate;
+
+struct _ShellEmbeddedWindowPrivate {
+ ShellGtkEmbed *actor;
+
+ GdkRectangle position;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellEmbeddedWindow,
+ shell_embedded_window,
+ GTK_TYPE_WINDOW);
+
+/*
+ * The normal gtk_window_show() starts all of the complicated asynchronous
+ * window resizing code running; we don't want or need any of that.
+ * Bypassing the normal code does mean that the extra geometry management
+ * available on GtkWindow: gridding, maximum sizes, etc, is ignored; we
+ * don't really want that anyways - we just want a way of embedding a
+ * GtkWidget into a Clutter stage.
+ */
+static void
+shell_embedded_window_show (GtkWidget *widget)
+{
+ ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
+ ShellEmbeddedWindowPrivate *priv;
+ GtkWidgetClass *widget_class;
+
+ priv = shell_embedded_window_get_instance_private (window);
+
+ /* Skip GtkWindow, but run the default GtkWidget handling which
+ * marks the widget visible */
+ widget_class = g_type_class_peek (GTK_TYPE_WIDGET);
+ widget_class->show (widget);
+
+ if (priv->actor)
+ {
+ /* Size is 0x0 if the GtkWindow is not shown */
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->actor));
+
+ if (clutter_actor_is_realized (CLUTTER_ACTOR (priv->actor)))
+ gtk_widget_map (widget);
+ }
+}
+
+static void
+shell_embedded_window_hide (GtkWidget *widget)
+{
+ ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
+ ShellEmbeddedWindowPrivate *priv;
+
+ priv = shell_embedded_window_get_instance_private (window);
+
+ if (priv->actor)
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->actor));
+
+ GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget);
+}
+
+static gboolean
+shell_embedded_window_configure_event (GtkWidget *widget,
+ GdkEventConfigure *event)
+{
+ /* Normally a configure event coming back from X triggers the
+ * resizing logic inside GtkWindow; we just ignore them
+ * since we are handling the resizing logic separately.
+ */
+ return FALSE;
+}
+
+static void
+shell_embedded_window_check_resize (GtkContainer *container)
+{
+ ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (container);
+ ShellEmbeddedWindowPrivate *priv;
+
+ priv = shell_embedded_window_get_instance_private (window);
+
+ /* Check resize is called when a resize is queued on something
+ * inside the GtkWindow; we need to make sure that in response
+ * to this gtk_widget_size_request() and then
+ * gtk_widget_size_allocate() are called; we defer to the Clutter
+ * logic and assume it will do the right thing.
+ */
+ if (priv->actor)
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->actor));
+}
+
+static GObject *
+shell_embedded_window_constructor (GType gtype,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObject *object;
+ GObjectClass *parent_class;
+
+ parent_class = G_OBJECT_CLASS (shell_embedded_window_parent_class);
+ object = parent_class->constructor (gtype, n_properties, properties);
+
+ /* Setting the resize mode to immediate means that calling queue_resize()
+ * on a widget within the window will immediately call check_resize()
+ * to be called, instead of having it queued to an idle. From our perspective,
+ * this is ideal since we just are going to queue a resize to Clutter's
+ * idle resize anyways.
+ */
+ g_object_set (object,
+ "app-paintable", TRUE,
+ "resize-mode", GTK_RESIZE_IMMEDIATE,
+ "type", GTK_WINDOW_POPUP,
+ NULL);
+
+ return object;
+}
+
+static void
+shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+ object_class->constructor = shell_embedded_window_constructor;
+
+ widget_class->show = shell_embedded_window_show;
+ widget_class->hide = shell_embedded_window_hide;
+ widget_class->configure_event = shell_embedded_window_configure_event;
+
+ container_class->check_resize = shell_embedded_window_check_resize;
+}
+
+static void
+shell_embedded_window_init (ShellEmbeddedWindow *window)
+{
+}
+
+/*
+ * Private routines called by ShellGtkEmbed
+ */
+
+void
+_shell_embedded_window_set_actor (ShellEmbeddedWindow *window,
+ ShellGtkEmbed *actor)
+
+{
+ ShellEmbeddedWindowPrivate *priv;
+
+ g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
+
+ priv = shell_embedded_window_get_instance_private (window);
+ priv->actor = actor;
+
+ if (actor &&
+ clutter_actor_is_mapped (CLUTTER_ACTOR (actor)) &&
+ gtk_widget_get_visible (GTK_WIDGET (window)))
+ gtk_widget_map (GTK_WIDGET (window));
+}
+
+void
+_shell_embedded_window_allocate (ShellEmbeddedWindow *window,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ ShellEmbeddedWindowPrivate *priv;
+ GtkAllocation allocation;
+
+ g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
+
+ priv = shell_embedded_window_get_instance_private (window);
+
+ if (priv->position.x == x &&
+ priv->position.y == y &&
+ priv->position.width == width &&
+ priv->position.height == height)
+ return;
+
+ priv->position.x = x;
+ priv->position.y = y;
+ priv->position.width = width;
+ priv->position.height = height;
+
+ if (gtk_widget_get_realized (GTK_WIDGET (window)))
+ gdk_window_move_resize (gtk_widget_get_window (GTK_WIDGET (window)),
+ x, y, width, height);
+
+ allocation.x = 0;
+ allocation.y = 0;
+ allocation.width = width;
+ allocation.height = height;
+
+ gtk_widget_size_allocate (GTK_WIDGET (window), &allocation);
+}
+
+void
+_shell_embedded_window_map (ShellEmbeddedWindow *window)
+{
+ g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
+
+ if (gtk_widget_get_visible (GTK_WIDGET (window)))
+ gtk_widget_map (GTK_WIDGET (window));
+}
+
+void
+_shell_embedded_window_unmap (ShellEmbeddedWindow *window)
+{
+ g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
+
+ gtk_widget_unmap (GTK_WIDGET (window));
+}
+
+/*
+ * Public API
+ */
+GtkWidget *
+shell_embedded_window_new (void)
+{
+ return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
+ NULL);
+}
diff --git a/src/shell-embedded-window.h b/src/shell-embedded-window.h
new file mode 100644
index 0000000..835165b
--- /dev/null
+++ b/src/shell-embedded-window.h
@@ -0,0 +1,19 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_EMBEDDED_WINDOW_H__
+#define __SHELL_EMBEDDED_WINDOW_H__
+
+#include <gtk/gtk.h>
+#include <clutter/clutter.h>
+
+#define SHELL_TYPE_EMBEDDED_WINDOW (shell_embedded_window_get_type ())
+G_DECLARE_DERIVABLE_TYPE (ShellEmbeddedWindow, shell_embedded_window,
+ SHELL, EMBEDDED_WINDOW, GtkWindow)
+
+struct _ShellEmbeddedWindowClass
+{
+ GtkWindowClass parent_class;
+};
+
+GtkWidget *shell_embedded_window_new (void);
+
+#endif /* __SHELL_EMBEDDED_WINDOW_H__ */
diff --git a/src/shell-global-private.h b/src/shell-global-private.h
new file mode 100644
index 0000000..9969691
--- /dev/null
+++ b/src/shell-global-private.h
@@ -0,0 +1,23 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_GLOBAL_PRIVATE_H__
+#define __SHELL_GLOBAL_PRIVATE_H__
+
+#include "shell-global.h"
+
+#include <gjs/gjs.h>
+
+void _shell_global_init (const char *first_property_name,
+ ...);
+void _shell_global_set_plugin (ShellGlobal *global,
+ MetaPlugin *plugin);
+
+void _shell_global_destroy_gjs_context (ShellGlobal *global);
+
+GjsContext *_shell_global_get_gjs_context (ShellGlobal *global);
+
+gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
+ XEvent *xev);
+
+void _shell_global_locate_pointer (ShellGlobal *global);
+
+#endif /* __SHELL_GLOBAL_PRIVATE_H__ */
diff --git a/src/shell-global.c b/src/shell-global.c
new file mode 100644
index 0000000..0ccdb10
--- /dev/null
+++ b/src/shell-global.c
@@ -0,0 +1,1860 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#include <locale.h>
+
+#include <X11/extensions/Xfixes.h>
+#include <gdk/gdkx.h>
+#include <gio/gio.h>
+#include <girepository.h>
+#include <meta/meta-backend.h>
+#include <meta/meta-context.h>
+#include <meta/display.h>
+#include <meta/util.h>
+#include <meta/meta-shaped-texture.h>
+#include <meta/meta-cursor-tracker.h>
+#include <meta/meta-settings.h>
+#include <meta/meta-workspace-manager.h>
+#include <meta/meta-x11-display.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-systemd.h>
+
+#if defined __OpenBSD__ || defined __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+
+#include "shell-enum-types.h"
+#include "shell-global-private.h"
+#include "shell-perf-log.h"
+#include "shell-window-tracker.h"
+#include "shell-wm.h"
+#include "shell-util.h"
+#include "st.h"
+#include "switcheroo-control.h"
+
+static ShellGlobal *the_object = NULL;
+
+struct _ShellGlobal {
+ GObject parent;
+
+ ClutterStage *stage;
+
+ MetaBackend *backend;
+ MetaContext *meta_context;
+ MetaDisplay *meta_display;
+ MetaWorkspaceManager *workspace_manager;
+ Display *xdisplay;
+
+ char *session_mode;
+
+ XserverRegion input_region;
+
+ GjsContext *js_context;
+ MetaPlugin *plugin;
+ ShellWM *wm;
+ GSettings *settings;
+ const char *datadir;
+ char *imagedir;
+ char *userdatadir;
+ GFile *userdatadir_path;
+ GFile *runtime_state_path;
+
+ StFocusManager *focus_manager;
+
+ guint work_count;
+ GSList *leisure_closures;
+ guint leisure_function_id;
+
+ GHashTable *save_ops;
+
+ gboolean frame_timestamps;
+ gboolean frame_finish_timestamp;
+
+ GDBusProxy *switcheroo_control;
+ GCancellable *switcheroo_cancellable;
+};
+
+enum {
+ PROP_0,
+
+ PROP_SESSION_MODE,
+ PROP_BACKEND,
+ PROP_CONTEXT,
+ PROP_DISPLAY,
+ PROP_WORKSPACE_MANAGER,
+ PROP_SCREEN_WIDTH,
+ PROP_SCREEN_HEIGHT,
+ PROP_STAGE,
+ PROP_WINDOW_GROUP,
+ PROP_TOP_WINDOW_GROUP,
+ PROP_WINDOW_MANAGER,
+ PROP_SETTINGS,
+ PROP_DATADIR,
+ PROP_IMAGEDIR,
+ PROP_USERDATADIR,
+ PROP_FOCUS_MANAGER,
+ PROP_FRAME_TIMESTAMPS,
+ PROP_FRAME_FINISH_TIMESTAMP,
+ PROP_SWITCHEROO_CONTROL,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+/* Signals */
+enum
+{
+ NOTIFY_ERROR,
+ LOCATE_POINTER,
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE(ShellGlobal, shell_global, G_TYPE_OBJECT);
+
+static guint shell_global_signals [LAST_SIGNAL] = { 0 };
+
+static void
+got_switcheroo_control_gpus_property_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ ShellGlobal *global;
+ GError *error = NULL;
+ GVariant *gpus;
+
+ gpus = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ res, &error);
+ if (!gpus)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_debug ("Could not get GPUs property from switcheroo-control: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ global = user_data;
+ g_dbus_proxy_set_cached_property (global->switcheroo_control, "GPUs", gpus);
+ g_object_notify_by_pspec (G_OBJECT (global), props[PROP_SWITCHEROO_CONTROL]);
+}
+
+static void
+switcheroo_control_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ ShellGlobal *global;
+ GError *error = NULL;
+ ShellNetHadessSwitcherooControl *control;
+ g_auto(GStrv) cached_props = NULL;
+
+ control = shell_net_hadess_switcheroo_control_proxy_new_for_bus_finish (res, &error);
+ if (!control)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_debug ("Could not get switcheroo-control GDBusProxy: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ global = user_data;
+ global->switcheroo_control = G_DBUS_PROXY (control);
+ g_debug ("Got switcheroo-control proxy successfully");
+
+ cached_props = g_dbus_proxy_get_cached_property_names (global->switcheroo_control);
+ if (cached_props != NULL && g_strv_contains ((const gchar * const *) cached_props, "GPUs"))
+ {
+ g_object_notify_by_pspec (G_OBJECT (global), props[PROP_SWITCHEROO_CONTROL]);
+ return;
+ }
+ /* Delay property notification until we have all the properties gathered */
+
+ g_dbus_connection_call (g_dbus_proxy_get_connection (global->switcheroo_control),
+ g_dbus_proxy_get_name (global->switcheroo_control),
+ g_dbus_proxy_get_object_path (global->switcheroo_control),
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ g_dbus_proxy_get_interface_name (global->switcheroo_control),
+ "GPUs"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ global->switcheroo_cancellable,
+ got_switcheroo_control_gpus_property_cb,
+ user_data);
+}
+
+static void
+shell_global_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellGlobal *global = SHELL_GLOBAL (object);
+
+ switch (prop_id)
+ {
+ case PROP_SESSION_MODE:
+ g_clear_pointer (&global->session_mode, g_free);
+ global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
+ break;
+ case PROP_FRAME_TIMESTAMPS:
+ {
+ gboolean enable = g_value_get_boolean (value);
+
+ if (global->frame_timestamps != enable)
+ {
+ global->frame_timestamps = enable;
+ g_object_notify_by_pspec (object, props[PROP_FRAME_TIMESTAMPS]);
+ }
+ }
+ break;
+ case PROP_FRAME_FINISH_TIMESTAMP:
+ {
+ gboolean enable = g_value_get_boolean (value);
+
+ if (global->frame_finish_timestamp != enable)
+ {
+ global->frame_finish_timestamp = enable;
+ g_object_notify_by_pspec (object, props[PROP_FRAME_FINISH_TIMESTAMP]);
+ }
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_global_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellGlobal *global = SHELL_GLOBAL (object);
+
+ switch (prop_id)
+ {
+ case PROP_SESSION_MODE:
+ g_value_set_string (value, shell_global_get_session_mode (global));
+ break;
+ case PROP_BACKEND:
+ g_value_set_object (value, global->backend);
+ break;
+ case PROP_CONTEXT:
+ g_value_set_object (value, global->meta_context);
+ break;
+ case PROP_DISPLAY:
+ g_value_set_object (value, global->meta_display);
+ break;
+ case PROP_WORKSPACE_MANAGER:
+ g_value_set_object (value, global->workspace_manager);
+ break;
+ case PROP_SCREEN_WIDTH:
+ {
+ int width, height;
+
+ meta_display_get_size (global->meta_display, &width, &height);
+ g_value_set_int (value, width);
+ }
+ break;
+ case PROP_SCREEN_HEIGHT:
+ {
+ int width, height;
+
+ meta_display_get_size (global->meta_display, &width, &height);
+ g_value_set_int (value, height);
+ }
+ break;
+ case PROP_STAGE:
+ g_value_set_object (value, global->stage);
+ break;
+ case PROP_WINDOW_GROUP:
+ g_value_set_object (value, meta_get_window_group_for_display (global->meta_display));
+ break;
+ case PROP_TOP_WINDOW_GROUP:
+ g_value_set_object (value, meta_get_top_window_group_for_display (global->meta_display));
+ break;
+ case PROP_WINDOW_MANAGER:
+ g_value_set_object (value, global->wm);
+ break;
+ case PROP_SETTINGS:
+ g_value_set_object (value, global->settings);
+ break;
+ case PROP_DATADIR:
+ g_value_set_string (value, global->datadir);
+ break;
+ case PROP_IMAGEDIR:
+ g_value_set_string (value, global->imagedir);
+ break;
+ case PROP_USERDATADIR:
+ g_value_set_string (value, global->userdatadir);
+ break;
+ case PROP_FOCUS_MANAGER:
+ g_value_set_object (value, global->focus_manager);
+ break;
+ case PROP_FRAME_TIMESTAMPS:
+ g_value_set_boolean (value, global->frame_timestamps);
+ break;
+ case PROP_FRAME_FINISH_TIMESTAMP:
+ g_value_set_boolean (value, global->frame_finish_timestamp);
+ break;
+ case PROP_SWITCHEROO_CONTROL:
+ g_value_set_object (value, global->switcheroo_control);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+switcheroo_appeared_cb (GDBusConnection *connection,
+ const char *name,
+ const char *name_owner,
+ gpointer user_data)
+{
+ ShellGlobal *global = user_data;
+
+ g_debug ("switcheroo-control appeared");
+ shell_net_hadess_switcheroo_control_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "net.hadess.SwitcherooControl",
+ "/net/hadess/SwitcherooControl",
+ global->switcheroo_cancellable,
+ switcheroo_control_ready_cb,
+ global);
+}
+
+static void
+switcheroo_vanished_cb (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ ShellGlobal *global = user_data;
+
+ g_debug ("switcheroo-control vanished");
+ g_clear_object (&global->switcheroo_control);
+ g_object_notify_by_pspec (G_OBJECT (global), props[PROP_SWITCHEROO_CONTROL]);
+}
+
+static void
+shell_global_init (ShellGlobal *global)
+{
+ const char *datadir = g_getenv ("GNOME_SHELL_DATADIR");
+ const char *shell_js = g_getenv("GNOME_SHELL_JS");
+ char *imagedir, **search_path;
+ char *path;
+ const char *byteorder_string;
+
+ if (!datadir)
+ datadir = GNOME_SHELL_DATADIR;
+ global->datadir = datadir;
+
+ /* We make sure imagedir ends with a '/', since the JS won't have
+ * access to g_build_filename() and so will end up just
+ * concatenating global.imagedir to a filename.
+ */
+ imagedir = g_build_filename (datadir, "images/", NULL);
+ if (g_file_test (imagedir, G_FILE_TEST_IS_DIR))
+ global->imagedir = imagedir;
+ else
+ {
+ g_free (imagedir);
+ global->imagedir = g_strdup_printf ("%s/", datadir);
+ }
+
+ /* Ensure config dir exists for later use */
+ global->userdatadir = g_build_filename (g_get_user_data_dir (), "gnome-shell", NULL);
+ g_mkdir_with_parents (global->userdatadir, 0700);
+ global->userdatadir_path = g_file_new_for_path (global->userdatadir);
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ byteorder_string = "LE";
+#else
+ byteorder_string = "BE";
+#endif
+
+ /* And the runtime state */
+ path = g_strdup_printf ("%s/gnome-shell/runtime-state-%s.%s",
+ g_get_user_runtime_dir (),
+ byteorder_string,
+ XDisplayName (NULL));
+ (void) g_mkdir_with_parents (path, 0700);
+ global->runtime_state_path = g_file_new_for_path (path);
+ g_free (path);
+
+ global->settings = g_settings_new ("org.gnome.shell");
+
+ if (shell_js)
+ {
+ int i, j;
+ search_path = g_strsplit (shell_js, ":", -1);
+
+ /* The naive g_strsplit above will split 'resource:///foo/bar' into 'resource',
+ * '///foo/bar'. Combine these back together by looking for a literal 'resource'
+ * in the array. */
+ for (i = 0, j = 0; search_path[i];)
+ {
+ char *out;
+
+ if (strcmp (search_path[i], "resource") == 0 && search_path[i + 1] != NULL)
+ {
+ out = g_strconcat (search_path[i], ":", search_path[i + 1], NULL);
+ g_free (search_path[i]);
+ g_free (search_path[i + 1]);
+ i += 2;
+ }
+ else
+ {
+ out = search_path[i];
+ i += 1;
+ }
+
+ search_path[j++] = out;
+ }
+
+ search_path[j] = NULL; /* NULL-terminate the now possibly shorter array */
+ }
+ else
+ {
+ search_path = g_malloc0 (2 * sizeof (char *));
+ search_path[0] = g_strdup ("resource:///org/gnome/shell");
+ }
+
+ global->js_context = g_object_new (GJS_TYPE_CONTEXT,
+ "search-path", search_path,
+ NULL);
+
+ g_strfreev (search_path);
+
+ global->save_ops = g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ g_object_unref, g_object_unref);
+
+ global->switcheroo_cancellable = g_cancellable_new ();
+ g_bus_watch_name (G_BUS_TYPE_SYSTEM,
+ "net.hadess.SwitcherooControl",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ switcheroo_appeared_cb,
+ switcheroo_vanished_cb,
+ global,
+ NULL);
+}
+
+static void
+shell_global_finalize (GObject *object)
+{
+ ShellGlobal *global = SHELL_GLOBAL (object);
+
+ g_clear_object (&global->js_context);
+ g_object_unref (global->settings);
+
+ the_object = NULL;
+
+ g_cancellable_cancel (global->switcheroo_cancellable);
+ g_clear_object (&global->switcheroo_cancellable);
+
+ g_clear_object (&global->userdatadir_path);
+ g_clear_object (&global->runtime_state_path);
+
+ g_free (global->session_mode);
+ g_free (global->imagedir);
+ g_free (global->userdatadir);
+
+ g_hash_table_unref (global->save_ops);
+
+ G_OBJECT_CLASS(shell_global_parent_class)->finalize (object);
+}
+
+static void
+shell_global_class_init (ShellGlobalClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = shell_global_get_property;
+ gobject_class->set_property = shell_global_set_property;
+ gobject_class->finalize = shell_global_finalize;
+
+ shell_global_signals[NOTIFY_ERROR] =
+ g_signal_new ("notify-error",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ shell_global_signals[LOCATE_POINTER] =
+ g_signal_new ("locate-pointer",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ props[PROP_SESSION_MODE] =
+ g_param_spec_string ("session-mode",
+ "Session Mode",
+ "The session mode to use",
+ "user",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_SCREEN_WIDTH] =
+ g_param_spec_int ("screen-width",
+ "Screen Width",
+ "Screen width, in pixels",
+ 0, G_MAXINT, 1,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_SCREEN_HEIGHT] =
+ g_param_spec_int ("screen-height",
+ "Screen Height",
+ "Screen height, in pixels",
+ 0, G_MAXINT, 1,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_BACKEND] =
+ g_param_spec_object ("backend",
+ "Backend",
+ "MetaBackend object",
+ META_TYPE_BACKEND,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_CONTEXT] =
+ g_param_spec_object ("context",
+ "Context",
+ "MetaContext object",
+ META_TYPE_CONTEXT,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_DISPLAY] =
+ g_param_spec_object ("display",
+ "Display",
+ "Metacity display object for the shell",
+ META_TYPE_DISPLAY,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_WORKSPACE_MANAGER] =
+ g_param_spec_object ("workspace-manager",
+ "Workspace manager",
+ "Workspace manager",
+ META_TYPE_WORKSPACE_MANAGER,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_STAGE] =
+ g_param_spec_object ("stage",
+ "Stage",
+ "Stage holding the desktop scene graph",
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_WINDOW_GROUP] =
+ g_param_spec_object ("window-group",
+ "Window Group",
+ "Actor holding window actors",
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_TOP_WINDOW_GROUP] =
+ g_param_spec_object ("top-window-group",
+ "Top Window Group",
+ "Actor holding override-redirect windows",
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_WINDOW_MANAGER] =
+ g_param_spec_object ("window-manager",
+ "Window Manager",
+ "Window management interface",
+ SHELL_TYPE_WM,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_SETTINGS] =
+ g_param_spec_object ("settings",
+ "Settings",
+ "GSettings instance for gnome-shell configuration",
+ G_TYPE_SETTINGS,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_DATADIR] =
+ g_param_spec_string ("datadir",
+ "Data directory",
+ "Directory containing gnome-shell data files",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_IMAGEDIR] =
+ g_param_spec_string ("imagedir",
+ "Image directory",
+ "Directory containing gnome-shell image files",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_USERDATADIR] =
+ g_param_spec_string ("userdatadir",
+ "User data directory",
+ "Directory containing gnome-shell user data",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_FOCUS_MANAGER] =
+ g_param_spec_object ("focus-manager",
+ "Focus manager",
+ "The shell's StFocusManager",
+ ST_TYPE_FOCUS_MANAGER,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ props[PROP_FRAME_TIMESTAMPS] =
+ g_param_spec_boolean ("frame-timestamps",
+ "Frame Timestamps",
+ "Whether to log frame timestamps in the performance log",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_FRAME_FINISH_TIMESTAMP] =
+ g_param_spec_boolean ("frame-finish-timestamp",
+ "Frame Finish Timestamps",
+ "Whether at the end of a frame to call glFinish and log paintCompletedTimestamp",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_SWITCHEROO_CONTROL] =
+ g_param_spec_object ("switcheroo-control",
+ "switcheroo-control",
+ "D-Bus Proxy for switcheroo-control daemon",
+ G_TYPE_DBUS_PROXY,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+}
+
+/*
+ * _shell_global_init: (skip)
+ * @first_property_name: the name of the first property
+ * @...: the value of the first property, followed optionally by more
+ * name/value pairs, followed by %NULL
+ *
+ * Initializes the shell global singleton with the construction-time
+ * properties.
+ *
+ * There are currently no such properties, so @first_property_name should
+ * always be %NULL.
+ *
+ * This call must be called before shell_global_get() and shouldn't be called
+ * more than once.
+ */
+void
+_shell_global_init (const char *first_property_name,
+ ...)
+{
+ va_list argument_list;
+
+ g_return_if_fail (the_object == NULL);
+
+ va_start (argument_list, first_property_name);
+ the_object = SHELL_GLOBAL (g_object_new_valist (SHELL_TYPE_GLOBAL,
+ first_property_name,
+ argument_list));
+ va_end (argument_list);
+
+}
+
+/**
+ * shell_global_get:
+ *
+ * Gets the singleton global object that represents the desktop.
+ *
+ * Return value: (transfer none): the singleton global object
+ */
+ShellGlobal *
+shell_global_get (void)
+{
+ return the_object;
+}
+
+/**
+ * _shell_global_destroy_gjs_context: (skip)
+ * @self: global object
+ *
+ * Destroys the GjsContext held by ShellGlobal, in order to break reference
+ * counting cycles. (The GjsContext holds a reference to ShellGlobal because
+ * it's available as window.global inside JS.)
+ */
+void
+_shell_global_destroy_gjs_context (ShellGlobal *self)
+{
+ g_clear_object (&self->js_context);
+}
+
+static guint32
+get_current_time_maybe_roundtrip (ShellGlobal *global)
+{
+ guint32 time;
+
+ time = shell_global_get_current_time (global);
+ if (time != CurrentTime)
+ return time;
+
+ return meta_display_get_current_time_roundtrip (global->meta_display);
+}
+
+static void
+focus_window_changed (MetaDisplay *display,
+ GParamSpec *param,
+ gpointer user_data)
+{
+ ShellGlobal *global = user_data;
+
+ /* If the stage window became unfocused, drop the key focus
+ * on Clutter's side. */
+ if (!meta_stage_is_focused (global->meta_display))
+ clutter_stage_set_key_focus (global->stage, NULL);
+}
+
+static ClutterActor *
+get_key_focused_actor (ShellGlobal *global)
+{
+ ClutterActor *actor;
+
+ actor = clutter_stage_get_key_focus (global->stage);
+
+ /* If there's no explicit key focus, clutter_stage_get_key_focus()
+ * returns the stage. This is a terrible API. */
+ if (actor == CLUTTER_ACTOR (global->stage))
+ actor = NULL;
+
+ return actor;
+}
+
+static void
+sync_stage_window_focus (ShellGlobal *global)
+{
+ ClutterActor *actor;
+
+ actor = get_key_focused_actor (global);
+
+ /* An actor got key focus and the stage needs to be focused. */
+ if (actor != NULL && !meta_stage_is_focused (global->meta_display))
+ meta_focus_stage_window (global->meta_display,
+ get_current_time_maybe_roundtrip (global));
+
+ /* An actor dropped key focus. Focus the default window. */
+ else if (actor == NULL && meta_stage_is_focused (global->meta_display))
+ meta_display_focus_default_window (global->meta_display,
+ get_current_time_maybe_roundtrip (global));
+}
+
+static void
+focus_actor_changed (ClutterStage *stage,
+ GParamSpec *param,
+ gpointer user_data)
+{
+ ShellGlobal *global = user_data;
+ sync_stage_window_focus (global);
+}
+
+static void
+sync_input_region (ShellGlobal *global)
+{
+ MetaDisplay *display = global->meta_display;
+ MetaX11Display *x11_display = meta_display_get_x11_display (display);
+
+ meta_x11_display_set_stage_input_region (x11_display, global->input_region);
+}
+
+/**
+ * shell_global_set_stage_input_region:
+ * @global: the #ShellGlobal
+ * @rectangles: (element-type Meta.Rectangle): a list of #MetaRectangle
+ * describing the input region.
+ *
+ * Sets the area of the stage that is responsive to mouse clicks when
+ * we don't have a modal or grab.
+ */
+void
+shell_global_set_stage_input_region (ShellGlobal *global,
+ GSList *rectangles)
+{
+ MetaRectangle *rect;
+ XRectangle *rects;
+ int nrects, i;
+ GSList *r;
+
+ g_return_if_fail (SHELL_IS_GLOBAL (global));
+
+ if (meta_is_wayland_compositor ())
+ return;
+
+ nrects = g_slist_length (rectangles);
+ rects = g_new (XRectangle, nrects);
+ for (r = rectangles, i = 0; r; r = r->next, i++)
+ {
+ rect = (MetaRectangle *)r->data;
+ rects[i].x = rect->x;
+ rects[i].y = rect->y;
+ rects[i].width = rect->width;
+ rects[i].height = rect->height;
+ }
+
+ if (global->input_region)
+ XFixesDestroyRegion (global->xdisplay, global->input_region);
+
+ global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects);
+ g_free (rects);
+
+ sync_input_region (global);
+}
+
+/**
+ * shell_global_get_stage:
+ *
+ * Return value: (transfer none): The default #ClutterStage
+ */
+ClutterStage *
+shell_global_get_stage (ShellGlobal *global)
+{
+ return global->stage;
+}
+
+/**
+ * shell_global_get_display:
+ *
+ * Return value: (transfer none): The default #MetaDisplay
+ */
+MetaDisplay *
+shell_global_get_display (ShellGlobal *global)
+{
+ return global->meta_display;
+}
+
+/**
+ * shell_global_get_workspace_manager:
+ *
+ * Return value: (transfer none): The default #MetaWorkspaceManager
+ */
+MetaWorkspaceManager *
+shell_global_get_workspace_manager (ShellGlobal *global)
+{
+ return global->workspace_manager;
+}
+
+/**
+ * shell_global_get_window_actors:
+ *
+ * Gets the list of #MetaWindowActor for the plugin's screen
+ *
+ * Return value: (element-type Meta.WindowActor) (transfer container): the list of windows
+ */
+GList *
+shell_global_get_window_actors (ShellGlobal *global)
+{
+ GList *filtered = NULL;
+ GList *l;
+
+ g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
+
+ for (l = meta_get_window_actors (global->meta_display); l; l = l->next)
+ if (!meta_window_actor_is_destroyed (l->data))
+ filtered = g_list_prepend (filtered, l->data);
+
+ return g_list_reverse (filtered);
+}
+
+static void
+global_stage_notify_width (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ ShellGlobal *global = SHELL_GLOBAL (data);
+
+ g_object_notify_by_pspec (G_OBJECT (global), props[PROP_SCREEN_WIDTH]);
+}
+
+static void
+global_stage_notify_height (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ ShellGlobal *global = SHELL_GLOBAL (data);
+
+ g_object_notify_by_pspec (G_OBJECT (global), props[PROP_SCREEN_HEIGHT]);
+}
+
+static gboolean
+global_stage_before_paint (gpointer data)
+{
+ ShellGlobal *global = SHELL_GLOBAL (data);
+
+ if (global->frame_timestamps)
+ shell_perf_log_event (shell_perf_log_get_default (),
+ "clutter.stagePaintStart");
+
+ return TRUE;
+}
+
+static gboolean
+load_gl_symbol (const char *name,
+ void **func)
+{
+ *func = cogl_get_proc_address (name);
+ if (!*func)
+ {
+ g_warning ("failed to resolve required GL symbol \"%s\"\n", name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+global_stage_after_paint (ClutterStage *stage,
+ ClutterStageView *stage_view,
+ ShellGlobal *global)
+{
+ /* At this point, we've finished all layout and painting, but haven't
+ * actually flushed or swapped */
+
+ if (global->frame_timestamps && global->frame_finish_timestamp)
+ {
+ /* It's interesting to find out when the paint actually finishes
+ * on the GPU. We could wait for this asynchronously with
+ * ARB_timer_query (see https://bugzilla.gnome.org/show_bug.cgi?id=732350
+ * for an implementation of this), but what we actually would
+ * find out then is the latency for drawing a frame, not how much
+ * GPU work was needed, since frames can overlap. Calling glFinish()
+ * is a fairly reliable way to separate out adjacent frames
+ * and measure the amount of GPU work. This is turned on with a
+ * separate property from ::frame-timestamps, since it should not
+ * be turned on if we're trying to actual measure latency or frame
+ * rate.
+ */
+ static void (*finish) (void);
+
+ if (!finish)
+ load_gl_symbol ("glFinish", (void **)&finish);
+
+ cogl_flush ();
+ finish ();
+
+ shell_perf_log_event (shell_perf_log_get_default (),
+ "clutter.paintCompletedTimestamp");
+ }
+}
+
+static gboolean
+global_stage_after_swap (gpointer data)
+{
+ /* Everything is done, we're ready for a new frame */
+
+ ShellGlobal *global = SHELL_GLOBAL (data);
+
+ if (global->frame_timestamps)
+ shell_perf_log_event (shell_perf_log_get_default (),
+ "clutter.stagePaintDone");
+
+ return TRUE;
+}
+
+static void
+update_scaling_factor (ShellGlobal *global,
+ MetaSettings *settings)
+{
+ ClutterStage *stage = CLUTTER_STAGE (global->stage);
+ StThemeContext *context = st_theme_context_get_for_stage (stage);
+ int scaling_factor;
+
+ scaling_factor = meta_settings_get_ui_scaling_factor (settings);
+ g_object_set (context, "scale-factor", scaling_factor, NULL);
+}
+
+static void
+ui_scaling_factor_changed (MetaSettings *settings,
+ ShellGlobal *global)
+{
+ update_scaling_factor (global, settings);
+}
+
+static void
+entry_cursor_func (StEntry *entry,
+ gboolean use_ibeam,
+ gpointer user_data)
+{
+ ShellGlobal *global = user_data;
+
+ meta_display_set_cursor (global->meta_display,
+ use_ibeam ? META_CURSOR_IBEAM : META_CURSOR_DEFAULT);
+}
+
+static void
+on_x11_display_closed (MetaDisplay *display,
+ ShellGlobal *global)
+{
+ g_signal_handlers_disconnect_by_data (global->stage, global);
+}
+
+void
+_shell_global_set_plugin (ShellGlobal *global,
+ MetaPlugin *plugin)
+{
+ MetaDisplay *display;
+ MetaBackend *backend;
+ MetaSettings *settings;
+
+ g_return_if_fail (SHELL_IS_GLOBAL (global));
+ g_return_if_fail (global->plugin == NULL);
+
+ global->backend = meta_get_backend ();
+ global->plugin = plugin;
+ global->wm = shell_wm_new (plugin);
+
+ display = meta_plugin_get_display (plugin);
+ global->meta_display = display;
+ global->meta_context = meta_display_get_context (display);
+ global->workspace_manager = meta_display_get_workspace_manager (display);
+
+ global->stage = CLUTTER_STAGE (meta_get_stage_for_display (display));
+
+ if (!meta_is_wayland_compositor ())
+ {
+ MetaX11Display *x11_display = meta_display_get_x11_display (display);
+ global->xdisplay = meta_x11_display_get_xdisplay (x11_display);
+ }
+
+ st_entry_set_cursor_func (entry_cursor_func, global);
+ st_clipboard_set_selection (meta_display_get_selection (display));
+
+ g_signal_connect (global->stage, "notify::width",
+ G_CALLBACK (global_stage_notify_width), global);
+ g_signal_connect (global->stage, "notify::height",
+ G_CALLBACK (global_stage_notify_height), global);
+
+ clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT,
+ global_stage_before_paint,
+ global, NULL);
+
+ g_signal_connect (global->stage, "after-paint",
+ G_CALLBACK (global_stage_after_paint), global);
+
+ clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
+ global_stage_after_swap,
+ global, NULL);
+
+ shell_perf_log_define_event (shell_perf_log_get_default(),
+ "clutter.stagePaintStart",
+ "Start of stage page repaint",
+ "");
+ shell_perf_log_define_event (shell_perf_log_get_default(),
+ "clutter.paintCompletedTimestamp",
+ "Paint completion on GPU",
+ "");
+ shell_perf_log_define_event (shell_perf_log_get_default(),
+ "clutter.stagePaintDone",
+ "End of frame, possibly including swap time",
+ "");
+
+ g_signal_connect (global->stage, "notify::key-focus",
+ G_CALLBACK (focus_actor_changed), global);
+ g_signal_connect (global->meta_display, "notify::focus-window",
+ G_CALLBACK (focus_window_changed), global);
+
+ if (global->xdisplay)
+ g_signal_connect_object (global->meta_display, "x11-display-closing",
+ G_CALLBACK (on_x11_display_closed), global, 0);
+
+ backend = meta_get_backend ();
+ settings = meta_backend_get_settings (backend);
+ g_signal_connect (settings, "ui-scaling-factor-changed",
+ G_CALLBACK (ui_scaling_factor_changed), global);
+
+ global->focus_manager = st_focus_manager_get_for_stage (global->stage);
+
+ update_scaling_factor (global, settings);
+}
+
+GjsContext *
+_shell_global_get_gjs_context (ShellGlobal *global)
+{
+ return global->js_context;
+}
+
+/* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
+ *
+ * Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering
+ *
+ * http://bugzilla.gnome.org/show_bug.cgi?id=469231
+ * http://bugzilla.gnome.org/show_bug.cgi?id=357585
+ */
+
+static int
+set_cloexec (void *data, gint fd)
+{
+ if (fd >= GPOINTER_TO_INT (data))
+ fcntl (fd, F_SETFD, FD_CLOEXEC);
+
+ return 0;
+}
+
+#ifndef HAVE_FDWALK
+static int
+fdwalk (int (*cb)(void *data, int fd), void *data)
+{
+ gint open_max;
+ gint fd;
+ gint res = 0;
+
+#ifdef HAVE_SYS_RESOURCE_H
+ struct rlimit rl;
+#endif
+
+#ifdef __linux__
+ DIR *d;
+
+ if ((d = opendir("/proc/self/fd"))) {
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ glong l;
+ gchar *e = NULL;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ l = strtol(de->d_name, &e, 10);
+ if (errno != 0 || !e || *e)
+ continue;
+
+ fd = (gint) l;
+
+ if ((glong) fd != l)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ if ((res = cb (data, fd)) != 0)
+ break;
+ }
+
+ closedir(d);
+ return res;
+ }
+
+ /* If /proc is not mounted or not accessible we fall back to the old
+ * rlimit trick */
+
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+ if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
+ open_max = rl.rlim_max;
+ else
+#endif
+ open_max = sysconf (_SC_OPEN_MAX);
+
+ for (fd = 0; fd < open_max; fd++)
+ if ((res = cb (data, fd)) != 0)
+ break;
+
+ return res;
+}
+#endif
+
+static void
+pre_exec_close_fds(void)
+{
+ fdwalk (set_cloexec, GINT_TO_POINTER(3));
+}
+
+/**
+ * shell_global_reexec_self:
+ * @global: A #ShellGlobal
+ *
+ * Restart the current process. Only intended for development purposes.
+ */
+void
+shell_global_reexec_self (ShellGlobal *global)
+{
+ GPtrArray *arr;
+ gsize len;
+ MetaContext *meta_context;
+
+#if defined __linux__ || defined __sun
+ char *buf;
+ char *buf_p;
+ char *buf_end;
+ g_autoptr (GError) error = NULL;
+
+ if (!g_file_get_contents ("/proc/self/cmdline", &buf, &len, &error))
+ {
+ g_warning ("failed to get /proc/self/cmdline: %s", error->message);
+ return;
+ }
+
+ buf_end = buf+len;
+ arr = g_ptr_array_new ();
+ /* The cmdline file is NUL-separated */
+ for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1)
+ g_ptr_array_add (arr, buf_p);
+
+ g_ptr_array_add (arr, NULL);
+#elif defined __OpenBSD__
+ gchar **args, **args_p;
+ gint mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
+
+ if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1)
+ return;
+
+ args = g_malloc0 (len);
+
+ if (sysctl (mib, G_N_ELEMENTS (mib), args, &len, NULL, 0) == -1) {
+ g_warning ("failed to get command line args: %d", errno);
+ g_free (args);
+ return;
+ }
+
+ arr = g_ptr_array_new ();
+ for (args_p = args; *args_p != NULL; args_p++) {
+ g_ptr_array_add (arr, *args_p);
+ }
+
+ g_ptr_array_add (arr, NULL);
+#elif defined __FreeBSD__
+ char *buf;
+ char *buf_p;
+ char *buf_end;
+ gint mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid() };
+
+ if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1)
+ return;
+
+ buf = g_malloc0 (len);
+
+ if (sysctl (mib, G_N_ELEMENTS (mib), buf, &len, NULL, 0) == -1) {
+ g_warning ("failed to get command line args: %d", errno);
+ g_free (buf);
+ return;
+ }
+
+ buf_end = buf+len;
+ arr = g_ptr_array_new ();
+ /* The value returned by sysctl is NUL-separated */
+ for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1)
+ g_ptr_array_add (arr, buf_p);
+
+ g_ptr_array_add (arr, NULL);
+#else
+ return;
+#endif
+
+ /* Close all file descriptors other than stdin/stdout/stderr, otherwise
+ * they will leak and stay open after the exec. In particular, this is
+ * important for file descriptors that represent mapped graphics buffer
+ * objects.
+ */
+ pre_exec_close_fds ();
+
+ g_object_get (global, "context", &meta_context, NULL);
+ meta_context_restore_rlimit_nofile (meta_context, NULL);
+
+ meta_display_close (shell_global_get_display (global),
+ shell_global_get_current_time (global));
+
+ execvp (arr->pdata[0], (char**)arr->pdata);
+ g_warning ("failed to reexec: %s", g_strerror (errno));
+ g_ptr_array_free (arr, TRUE);
+#if defined __linux__ || defined __FreeBSD__
+ g_free (buf);
+#elif defined __OpenBSD__
+ g_free (args);
+#endif
+}
+
+/**
+ * shell_global_notify_error:
+ * @global: a #ShellGlobal
+ * @msg: Error message
+ * @details: Error details
+ *
+ * Show a system error notification. Use this function
+ * when a user-initiated action results in a non-fatal problem
+ * from causes that may not be under system control. For
+ * example, an application crash.
+ */
+void
+shell_global_notify_error (ShellGlobal *global,
+ const char *msg,
+ const char *details)
+{
+ g_signal_emit_by_name (global, "notify-error", msg, details);
+}
+
+/**
+ * shell_global_get_pointer:
+ * @global: the #ShellGlobal
+ * @x: (out): the X coordinate of the pointer, in global coordinates
+ * @y: (out): the Y coordinate of the pointer, in global coordinates
+ * @mods: (out): the current set of modifier keys that are pressed down
+ *
+ * Gets the pointer coordinates and current modifier key state.
+ */
+void
+shell_global_get_pointer (ShellGlobal *global,
+ int *x,
+ int *y,
+ ClutterModifierType *mods)
+{
+ ClutterModifierType raw_mods;
+ MetaCursorTracker *tracker;
+ graphene_point_t point;
+
+ tracker = meta_cursor_tracker_get_for_display (global->meta_display);
+ meta_cursor_tracker_get_pointer (tracker, &point, &raw_mods);
+
+ if (x)
+ *x = point.x;
+ if (y)
+ *y = point.y;
+
+ *mods = raw_mods & CLUTTER_MODIFIER_MASK;
+}
+
+/**
+ * shell_global_get_switcheroo_control:
+ * @global: A #ShellGlobal
+ *
+ * Get the global #GDBusProxy instance for the switcheroo-control
+ * daemon.
+ *
+ * Return value: (transfer none): the #GDBusProxy for the daemon,
+ * or %NULL on error.
+ */
+GDBusProxy *
+shell_global_get_switcheroo_control (ShellGlobal *global)
+{
+ return global->switcheroo_control;
+}
+
+/**
+ * shell_global_get_settings:
+ * @global: A #ShellGlobal
+ *
+ * Get the global GSettings instance.
+ *
+ * Return value: (transfer none): The GSettings object
+ */
+GSettings *
+shell_global_get_settings (ShellGlobal *global)
+{
+ return global->settings;
+}
+
+/**
+ * shell_global_get_current_time:
+ * @global: A #ShellGlobal
+ *
+ * Returns: the current X server time from the current Clutter, Gdk, or X
+ * event. If called from outside an event handler, this may return
+ * %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
+ * out-of-date timestamp.
+ */
+guint32
+shell_global_get_current_time (ShellGlobal *global)
+{
+ guint32 time;
+
+ /* meta_display_get_current_time() will return the correct time
+ when handling an X or Gdk event, but will return CurrentTime
+ from some Clutter event callbacks.
+
+ clutter_get_current_event_time() will return the correct time
+ from a Clutter event callback, but may return CLUTTER_CURRENT_TIME
+ timestamp if called at other times.
+
+ So we try meta_display_get_current_time() first, since we
+ can recognize a "wrong" answer from that, and then fall back
+ to clutter_get_current_event_time().
+ */
+
+ time = meta_display_get_current_time (global->meta_display);
+ if (time != CLUTTER_CURRENT_TIME)
+ return time;
+
+ return clutter_get_current_event_time ();
+}
+
+static void
+shell_global_app_launched_cb (GAppLaunchContext *context,
+ GAppInfo *info,
+ GVariant *platform_data,
+ gpointer user_data)
+{
+ gint32 pid;
+ const gchar *app_name;
+
+ if (!g_variant_lookup (platform_data, "pid", "i", &pid))
+ return;
+
+ /* If pid == 0 the application was launched through D-Bus
+ * activation, therefore it's already in its own unit */
+ if (pid == 0)
+ return;
+
+ app_name = g_app_info_get_id (info);
+ if (app_name == NULL)
+ app_name = g_app_info_get_executable (info);
+
+ /* Start async request; we don't care about the result */
+ gnome_start_systemd_scope (app_name,
+ pid,
+ NULL,
+ NULL,
+ NULL, NULL, NULL);
+}
+
+/**
+ * shell_global_create_app_launch_context:
+ * @global: A #ShellGlobal
+ * @timestamp: the timestamp for the launch (or 0 for current time)
+ * @workspace: a workspace index, or -1 to indicate the current one
+ *
+ * Create a #GAppLaunchContext set up with the correct timestamp, and
+ * targeted to activate on the current workspace.
+ *
+ * Return value: (transfer full): A new #GAppLaunchContext
+ */
+GAppLaunchContext *
+shell_global_create_app_launch_context (ShellGlobal *global,
+ guint32 timestamp,
+ int workspace)
+{
+ MetaWorkspaceManager *workspace_manager = global->workspace_manager;
+ MetaStartupNotification *sn;
+ MetaLaunchContext *context;
+ MetaWorkspace *ws = NULL;
+
+ sn = meta_display_get_startup_notification (global->meta_display);
+ context = meta_startup_notification_create_launcher (sn);
+
+ if (timestamp == 0)
+ timestamp = shell_global_get_current_time (global);
+ meta_launch_context_set_timestamp (context, timestamp);
+
+ if (workspace < 0)
+ ws = meta_workspace_manager_get_active_workspace (workspace_manager);
+ else
+ ws = meta_workspace_manager_get_workspace_by_index (workspace_manager, workspace);
+
+ meta_launch_context_set_workspace (context, ws);
+
+ g_signal_connect (context,
+ "launched",
+ G_CALLBACK (shell_global_app_launched_cb),
+ NULL);
+
+ return (GAppLaunchContext *) context;
+}
+
+typedef struct
+{
+ ShellLeisureFunction func;
+ gpointer user_data;
+ GDestroyNotify notify;
+} LeisureClosure;
+
+static gboolean
+run_leisure_functions (gpointer data)
+{
+ ShellGlobal *global = data;
+ GSList *closures;
+ GSList *iter;
+
+ global->leisure_function_id = 0;
+
+ /* We started more work since we scheduled the idle */
+ if (global->work_count > 0)
+ return FALSE;
+
+ /* No leisure closures, so we are done */
+ if (global->leisure_closures == NULL)
+ return FALSE;
+
+ closures = global->leisure_closures;
+ global->leisure_closures = NULL;
+
+ for (iter = closures; iter; iter = iter->next)
+ {
+ LeisureClosure *closure = closures->data;
+ closure->func (closure->user_data);
+
+ if (closure->notify)
+ closure->notify (closure->user_data);
+
+ g_free (closure);
+ }
+
+ g_slist_free (closures);
+
+ return FALSE;
+}
+
+static void
+schedule_leisure_functions (ShellGlobal *global)
+{
+ /* This is called when we think we are ready to run leisure functions
+ * by our own accounting. We try to handle other types of business
+ * (like ClutterAnimation) by adding a low priority idle function.
+ *
+ * This won't work properly if the mainloop goes idle waiting for
+ * the vertical blanking interval or waiting for work being done
+ * in another thread.
+ */
+ if (!global->leisure_function_id)
+ {
+ global->leisure_function_id = g_idle_add_full (G_PRIORITY_LOW,
+ run_leisure_functions,
+ global, NULL);
+ g_source_set_name_by_id (global->leisure_function_id, "[gnome-shell] run_leisure_functions");
+ }
+}
+
+/**
+ * shell_global_begin_work:
+ * @global: the #ShellGlobal
+ *
+ * Marks that we are currently doing work. This is used to to track
+ * whether we are busy for the purposes of shell_global_run_at_leisure().
+ * A count is kept and shell_global_end_work() must be called exactly
+ * as many times as shell_global_begin_work().
+ */
+void
+shell_global_begin_work (ShellGlobal *global)
+{
+ global->work_count++;
+}
+
+/**
+ * shell_global_end_work:
+ * @global: the #ShellGlobal
+ *
+ * Marks the end of work that we started with shell_global_begin_work().
+ * If no other work is ongoing and functions have been added with
+ * shell_global_run_at_leisure(), they will be run at the next
+ * opportunity.
+ */
+void
+shell_global_end_work (ShellGlobal *global)
+{
+ g_return_if_fail (global->work_count > 0);
+
+ global->work_count--;
+ if (global->work_count == 0)
+ schedule_leisure_functions (global);
+
+}
+
+/**
+ * shell_global_run_at_leisure:
+ * @global: the #ShellGlobal
+ * @func: function to call at leisure
+ * @user_data: data to pass to @func
+ * @notify: function to call to free @user_data
+ *
+ * Schedules a function to be called the next time the shell is idle.
+ * Idle means here no animations, no redrawing, and no ongoing background
+ * work. Since there is currently no way to hook into the Clutter master
+ * clock and know when is running, the implementation here is somewhat
+ * approximation. Animations may be detected as terminating early if they
+ * can be drawn fast enough so that the event loop goes idle between frames.
+ *
+ * The intent of this function is for performance measurement runs
+ * where a number of actions should be run serially and each action is
+ * timed individually. Using this function for other purposes will
+ * interfere with the ability to use it for performance measurement so
+ * should be avoided.
+ */
+void
+shell_global_run_at_leisure (ShellGlobal *global,
+ ShellLeisureFunction func,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ LeisureClosure *closure = g_new (LeisureClosure, 1);
+ closure->func = func;
+ closure->user_data = user_data;
+ closure->notify = notify;
+
+ global->leisure_closures = g_slist_append (global->leisure_closures,
+ closure);
+
+ if (global->work_count == 0)
+ schedule_leisure_functions (global);
+}
+
+const char *
+shell_global_get_session_mode (ShellGlobal *global)
+{
+ g_return_val_if_fail (SHELL_IS_GLOBAL (global), "user");
+
+ return global->session_mode;
+}
+
+static void
+delete_variant_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ShellGlobal *global = user_data;
+ GError *error = NULL;
+
+ if (!g_file_delete_finish (G_FILE (object), result, &error))
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_warning ("Could not delete runtime/persistent state file: %s\n",
+ error->message);
+ }
+
+ g_error_free (error);
+ }
+
+ g_hash_table_remove (global->save_ops, object);
+}
+
+static void
+replace_contents_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GFile *file = source_object;
+ GBytes *bytes = task_data;
+ GError *error = NULL;
+ const gchar *data;
+ gsize len;
+
+ data = g_bytes_get_data (bytes, &len);
+
+ if (!g_file_replace_contents (file, data, len, NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ NULL, cancellable, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+replace_contents_async (GFile *path,
+ GBytes *bytes,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (G_IS_FILE (path));
+ g_assert (bytes != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (path, cancellable, callback, user_data);
+ g_task_set_source_tag (task, replace_contents_async);
+ g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
+ g_task_run_in_thread (task, replace_contents_worker);
+}
+
+static gboolean
+replace_contents_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+replace_variant_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ShellGlobal *global = user_data;
+ GError *error = NULL;
+
+ if (!replace_contents_finish (G_FILE (object), result, &error))
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("Could not replace runtime/persistent state file: %s\n",
+ error->message);
+ }
+
+ g_error_free (error);
+ }
+
+ g_hash_table_remove (global->save_ops, object);
+}
+
+static void
+save_variant (ShellGlobal *global,
+ GFile *dir,
+ const char *property_name,
+ GVariant *variant)
+{
+ GFile *path = g_file_get_child (dir, property_name);
+ GCancellable *cancellable;
+
+ cancellable = g_hash_table_lookup (global->save_ops, path);
+ g_cancellable_cancel (cancellable);
+
+ cancellable = g_cancellable_new ();
+ g_hash_table_insert (global->save_ops, g_object_ref (path), cancellable);
+
+ if (variant == NULL || g_variant_get_data (variant) == NULL)
+ {
+ g_file_delete_async (path, G_PRIORITY_DEFAULT, cancellable,
+ delete_variant_cb, global);
+ }
+ else
+ {
+ g_autoptr(GBytes) bytes = NULL;
+
+ bytes = g_bytes_new_with_free_func (g_variant_get_data (variant),
+ g_variant_get_size (variant),
+ (GDestroyNotify)g_variant_unref,
+ g_variant_ref (variant));
+ /* g_file_replace_contents_async() can potentially fsync() from the
+ * calling thread when completing the asynchronous task. Instead, we
+ * want to force that fsync() to a thread to avoid blocking the
+ * compositor main loop. Using our own replace_contents_async()
+ * simply executes the operation synchronously from a thread.
+ */
+ replace_contents_async (path, bytes, cancellable, replace_variant_cb, global);
+ }
+
+ g_object_unref (path);
+}
+
+static GVariant *
+load_variant (GFile *dir,
+ const char *property_type,
+ const char *property_name)
+{
+ GVariant *res = NULL;
+ GMappedFile *mfile;
+ GFile *path = g_file_get_child (dir, property_name);
+ char *pathstr;
+ GError *local_error = NULL;
+
+ pathstr = g_file_get_path (path);
+ mfile = g_mapped_file_new (pathstr, FALSE, &local_error);
+ if (!mfile)
+ {
+ if (!g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ {
+ g_warning ("Failed to open runtime state: %s", local_error->message);
+ }
+ g_clear_error (&local_error);
+ }
+ else
+ {
+ GBytes *bytes = g_mapped_file_get_bytes (mfile);
+ res = g_variant_new_from_bytes (G_VARIANT_TYPE (property_type), bytes, FALSE);
+ g_bytes_unref (bytes);
+ g_mapped_file_unref (mfile);
+ }
+
+ g_object_unref (path);
+ g_free (pathstr);
+
+ return res;
+}
+
+/**
+ * shell_global_set_runtime_state:
+ * @global: a #ShellGlobal
+ * @property_name: Name of the property
+ * @variant: (nullable): A #GVariant, or %NULL to unset
+ *
+ * Change the value of serialized runtime state.
+ */
+void
+shell_global_set_runtime_state (ShellGlobal *global,
+ const char *property_name,
+ GVariant *variant)
+{
+ save_variant (global, global->runtime_state_path, property_name, variant);
+}
+
+/**
+ * shell_global_get_runtime_state:
+ * @global: a #ShellGlobal
+ * @property_type: Expected data type
+ * @property_name: Name of the property
+ *
+ * The shell maintains "runtime" state which does not persist across
+ * logout or reboot.
+ *
+ * Returns: (transfer floating): The value of a serialized property, or %NULL if none stored
+ */
+GVariant *
+shell_global_get_runtime_state (ShellGlobal *global,
+ const char *property_type,
+ const char *property_name)
+{
+ return load_variant (global->runtime_state_path, property_type, property_name);
+}
+
+/**
+ * shell_global_set_persistent_state:
+ * @global: a #ShellGlobal
+ * @property_name: Name of the property
+ * @variant: (nullable): A #GVariant, or %NULL to unset
+ *
+ * Change the value of serialized persistent state.
+ */
+void
+shell_global_set_persistent_state (ShellGlobal *global,
+ const char *property_name,
+ GVariant *variant)
+{
+ save_variant (global, global->userdatadir_path, property_name, variant);
+}
+
+/**
+ * shell_global_get_persistent_state:
+ * @global: a #ShellGlobal
+ * @property_type: Expected data type
+ * @property_name: Name of the property
+ *
+ * The shell maintains "persistent" state which will persist after
+ * logout or reboot.
+ *
+ * Returns: (transfer none): The value of a serialized property, or %NULL if none stored
+ */
+GVariant *
+shell_global_get_persistent_state (ShellGlobal *global,
+ const char *property_type,
+ const char *property_name)
+{
+ return load_variant (global->userdatadir_path, property_type, property_name);
+}
+
+void
+_shell_global_locate_pointer (ShellGlobal *global)
+{
+ g_signal_emit (global, shell_global_signals[LOCATE_POINTER], 0);
+}
diff --git a/src/shell-global.h b/src/shell-global.h
new file mode 100644
index 0000000..8d8238c
--- /dev/null
+++ b/src/shell-global.h
@@ -0,0 +1,94 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_GLOBAL_H__
+#define __SHELL_GLOBAL_H__
+
+#include <clutter/clutter.h>
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <meta/meta-plugin.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_GLOBAL (shell_global_get_type ())
+G_DECLARE_FINAL_TYPE (ShellGlobal, shell_global, SHELL, GLOBAL, GObject)
+
+ShellGlobal *shell_global_get (void);
+
+ClutterStage *shell_global_get_stage (ShellGlobal *global);
+MetaDisplay *shell_global_get_display (ShellGlobal *global);
+GList *shell_global_get_window_actors (ShellGlobal *global);
+GSettings *shell_global_get_settings (ShellGlobal *global);
+guint32 shell_global_get_current_time (ShellGlobal *global);
+MetaWorkspaceManager *shell_global_get_workspace_manager (ShellGlobal *global);
+
+
+/* Input/event handling */
+void shell_global_set_stage_input_region (ShellGlobal *global,
+ GSList *rectangles);
+
+void shell_global_get_pointer (ShellGlobal *global,
+ int *x,
+ int *y,
+ ClutterModifierType *mods);
+
+typedef struct {
+ guint glibc_uordblks;
+
+ guint js_bytes;
+
+ guint gjs_boxed;
+ guint gjs_gobject;
+ guint gjs_function;
+ guint gjs_closure;
+
+ /* 32 bit to avoid js conversion problems with 64 bit */
+ guint last_gc_seconds_ago;
+} ShellMemoryInfo;
+
+/* Run-at-leisure API */
+void shell_global_begin_work (ShellGlobal *global);
+void shell_global_end_work (ShellGlobal *global);
+
+typedef void (*ShellLeisureFunction) (gpointer data);
+
+void shell_global_run_at_leisure (ShellGlobal *global,
+ ShellLeisureFunction func,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+
+/* Misc utilities / Shell API */
+GDBusProxy *
+ shell_global_get_switcheroo_control (ShellGlobal *global);
+
+GAppLaunchContext *
+ shell_global_create_app_launch_context (ShellGlobal *global,
+ guint32 timestamp,
+ int workspace);
+
+void shell_global_notify_error (ShellGlobal *global,
+ const char *msg,
+ const char *details);
+
+void shell_global_reexec_self (ShellGlobal *global);
+
+const char * shell_global_get_session_mode (ShellGlobal *global);
+
+void shell_global_set_runtime_state (ShellGlobal *global,
+ const char *property_name,
+ GVariant *variant);
+GVariant * shell_global_get_runtime_state (ShellGlobal *global,
+ const char *property_type,
+ const char *property_name);
+
+void shell_global_set_persistent_state (ShellGlobal *global,
+ const char *property_name,
+ GVariant *variant);
+GVariant * shell_global_get_persistent_state (ShellGlobal *global,
+ const char *property_type,
+ const char *property_name);
+
+G_END_DECLS
+
+#endif /* __SHELL_GLOBAL_H__ */
diff --git a/src/shell-glsl-effect.c b/src/shell-glsl-effect.c
new file mode 100644
index 0000000..3051e0a
--- /dev/null
+++ b/src/shell-glsl-effect.c
@@ -0,0 +1,205 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/**
+ * SECTION:shell-glsl-effect
+ * @short_description: An offscreen effect using GLSL
+ *
+ * A #ShellGLSLEffect is a #ClutterOffscreenEffect that allows
+ * running custom GLSL to the vertex and fragment stages of the
+ * graphic pipeline.
+ */
+
+#include "config.h"
+
+#include <cogl/cogl.h>
+#include "shell-glsl-effect.h"
+
+typedef struct _ShellGLSLEffectPrivate ShellGLSLEffectPrivate;
+struct _ShellGLSLEffectPrivate
+{
+ CoglPipeline *pipeline;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellGLSLEffect, shell_glsl_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT);
+
+static CoglPipeline *
+shell_glsl_effect_create_pipeline (ClutterOffscreenEffect *effect,
+ CoglTexture *texture)
+{
+ ShellGLSLEffect *self = SHELL_GLSL_EFFECT (effect);
+ ShellGLSLEffectPrivate *priv = shell_glsl_effect_get_instance_private (self);
+
+ cogl_pipeline_set_layer_texture (priv->pipeline, 0, texture);
+
+ return cogl_object_ref (priv->pipeline);
+}
+
+/**
+ * shell_glsl_effect_add_glsl_snippet:
+ * @effect: a #ShellGLSLEffect
+ * @hook: where to insert the code
+ * @declarations: GLSL declarations
+ * @code: GLSL code
+ * @is_replace: whether Cogl code should be replaced by the custom shader
+ *
+ * Adds a GLSL snippet to the pipeline used for drawing the effect texture.
+ * See #CoglSnippet for details.
+ *
+ * This is only valid inside the a call to the build_pipeline() virtual
+ * function.
+ */
+void
+shell_glsl_effect_add_glsl_snippet (ShellGLSLEffect *effect,
+ ShellSnippetHook hook,
+ const char *declarations,
+ const char *code,
+ gboolean is_replace)
+{
+ ShellGLSLEffectClass *klass = SHELL_GLSL_EFFECT_GET_CLASS (effect);
+ CoglSnippet *snippet;
+
+ g_return_if_fail (klass->base_pipeline != NULL);
+
+ if (is_replace)
+ {
+ snippet = cogl_snippet_new ((CoglSnippetHook)hook, declarations, NULL);
+ cogl_snippet_set_replace (snippet, code);
+ }
+ else
+ {
+ snippet = cogl_snippet_new ((CoglSnippetHook)hook, declarations, code);
+ }
+
+ if (hook == SHELL_SNIPPET_HOOK_VERTEX ||
+ hook == SHELL_SNIPPET_HOOK_FRAGMENT)
+ cogl_pipeline_add_snippet (klass->base_pipeline, snippet);
+ else
+ cogl_pipeline_add_layer_snippet (klass->base_pipeline, 0, snippet);
+
+ cogl_object_unref (snippet);
+}
+
+static void
+shell_glsl_effect_dispose (GObject *gobject)
+{
+ ShellGLSLEffect *self = SHELL_GLSL_EFFECT (gobject);
+ ShellGLSLEffectPrivate *priv;
+
+ priv = shell_glsl_effect_get_instance_private (self);
+
+ g_clear_pointer (&priv->pipeline, cogl_object_unref);
+
+ G_OBJECT_CLASS (shell_glsl_effect_parent_class)->dispose (gobject);
+}
+
+static void
+shell_glsl_effect_init (ShellGLSLEffect *effect)
+{
+}
+
+static void
+shell_glsl_effect_constructed (GObject *object)
+{
+ ShellGLSLEffect *self;
+ ShellGLSLEffectClass *klass;
+ ShellGLSLEffectPrivate *priv;
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ G_OBJECT_CLASS (shell_glsl_effect_parent_class)->constructed (object);
+
+ /* Note that, differently from ClutterBlurEffect, we are calling
+ this inside constructed, not init, so klass points to the most-derived
+ GTypeClass, not ShellGLSLEffectClass.
+ */
+ klass = SHELL_GLSL_EFFECT_GET_CLASS (object);
+ self = SHELL_GLSL_EFFECT (object);
+ priv = shell_glsl_effect_get_instance_private (self);
+
+ if (G_UNLIKELY (klass->base_pipeline == NULL))
+ {
+ klass->base_pipeline = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_blend (klass->base_pipeline, "RGBA = ADD (SRC_COLOR * (SRC_COLOR[A]), DST_COLOR * (1-SRC_COLOR[A]))", NULL);
+
+ if (klass->build_pipeline != NULL)
+ klass->build_pipeline (self);
+ }
+
+ priv->pipeline = cogl_pipeline_copy (klass->base_pipeline);
+
+ cogl_pipeline_set_layer_null_texture (klass->base_pipeline, 0);
+}
+
+static void
+shell_glsl_effect_class_init (ShellGLSLEffectClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterOffscreenEffectClass *offscreen_class;
+
+ offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
+ offscreen_class->create_pipeline = shell_glsl_effect_create_pipeline;
+
+ gobject_class->constructed = shell_glsl_effect_constructed;
+ gobject_class->dispose = shell_glsl_effect_dispose;
+}
+
+/**
+ * shell_glsl_effect_get_uniform_location:
+ * @effect: a #ShellGLSLEffect
+ * @name: the uniform name
+ *
+ * Returns: the location of the uniform named @name, that can be
+ * passed to shell_glsl_effect_set_uniform_float().
+ */
+int
+shell_glsl_effect_get_uniform_location (ShellGLSLEffect *effect,
+ const char *name)
+{
+ ShellGLSLEffectPrivate *priv = shell_glsl_effect_get_instance_private (effect);
+ return cogl_pipeline_get_uniform_location (priv->pipeline, name);
+}
+
+/**
+ * shell_glsl_effect_set_uniform_float:
+ * @effect: a #ShellGLSLEffect
+ * @uniform: the uniform location (as returned by shell_glsl_effect_get_uniform_location())
+ * @n_components: the number of components in the uniform (eg. 3 for a vec3)
+ * @total_count: the total number of floats in @value
+ * @value: (array length=total_count): the array of floats to set @uniform
+ */
+void
+shell_glsl_effect_set_uniform_float (ShellGLSLEffect *effect,
+ int uniform,
+ int n_components,
+ int total_count,
+ const float *value)
+{
+ ShellGLSLEffectPrivate *priv = shell_glsl_effect_get_instance_private (effect);
+ cogl_pipeline_set_uniform_float (priv->pipeline, uniform,
+ n_components, total_count / n_components,
+ value);
+}
+
+/**
+ * shell_glsl_effect_set_uniform_matrix:
+ * @effect: a #ShellGLSLEffect
+ * @uniform: the uniform location (as returned by shell_glsl_effect_get_uniform_location())
+ * @transpose: Whether to transpose the matrix
+ * @dimensions: the number of components in the uniform (eg. 3 for a vec3)
+ * @total_count: the total number of floats in @value
+ * @value: (array length=total_count): the array of floats to set @uniform
+ */
+void
+shell_glsl_effect_set_uniform_matrix (ShellGLSLEffect *effect,
+ int uniform,
+ gboolean transpose,
+ int dimensions,
+ int total_count,
+ const float *value)
+{
+ ShellGLSLEffectPrivate *priv = shell_glsl_effect_get_instance_private (effect);
+ cogl_pipeline_set_uniform_matrix (priv->pipeline, uniform,
+ dimensions,
+ total_count / (dimensions * dimensions),
+ transpose, value);
+}
diff --git a/src/shell-glsl-effect.h b/src/shell-glsl-effect.h
new file mode 100644
index 0000000..3759e71
--- /dev/null
+++ b/src/shell-glsl-effect.h
@@ -0,0 +1,62 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_GLSL_EFFECT_H__
+#define __SHELL_GLSL_EFFECT_H__
+
+#include "st.h"
+#include <gtk/gtk.h>
+
+/**
+ * ShellSnippetHook:
+ * Temporary hack to work around Cogl not exporting CoglSnippetHook in
+ * the 1.0 API. Don't use.
+ */
+typedef enum {
+ /* Per pipeline vertex hooks */
+ SHELL_SNIPPET_HOOK_VERTEX = 0,
+ SHELL_SNIPPET_HOOK_VERTEX_TRANSFORM,
+
+ /* Per pipeline fragment hooks */
+ SHELL_SNIPPET_HOOK_FRAGMENT = 2048,
+
+ /* Per layer vertex hooks */
+ SHELL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM = 4096,
+
+ /* Per layer fragment hooks */
+ SHELL_SNIPPET_HOOK_LAYER_FRAGMENT = 6144,
+ SHELL_SNIPPET_HOOK_TEXTURE_LOOKUP
+} ShellSnippetHook;
+
+#define SHELL_TYPE_GLSL_EFFECT (shell_glsl_effect_get_type ())
+G_DECLARE_DERIVABLE_TYPE (ShellGLSLEffect, shell_glsl_effect,
+ SHELL, GLSL_EFFECT, ClutterOffscreenEffect)
+
+struct _ShellGLSLEffectClass
+{
+ ClutterOffscreenEffectClass parent_class;
+
+ CoglPipeline *base_pipeline;
+
+ void (*build_pipeline) (ShellGLSLEffect *effect);
+};
+
+void shell_glsl_effect_add_glsl_snippet (ShellGLSLEffect *effect,
+ ShellSnippetHook hook,
+ const char *declarations,
+ const char *code,
+ gboolean is_replace);
+
+int shell_glsl_effect_get_uniform_location (ShellGLSLEffect *effect,
+ const char *name);
+void shell_glsl_effect_set_uniform_float (ShellGLSLEffect *effect,
+ int uniform,
+ int n_components,
+ int total_count,
+ const float *value);
+void shell_glsl_effect_set_uniform_matrix (ShellGLSLEffect *effect,
+ int uniform,
+ gboolean transpose,
+ int dimensions,
+ int total_count,
+ const float *value);
+
+#endif /* __SHELL_GLSL_EFFECT_H__ */
diff --git a/src/shell-gtk-embed.c b/src/shell-gtk-embed.c
new file mode 100644
index 0000000..2ad18a1
--- /dev/null
+++ b/src/shell-gtk-embed.c
@@ -0,0 +1,364 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include "shell-embedded-window-private.h"
+#include "shell-global.h"
+#include "shell-util.h"
+
+#include <gdk/gdkx.h>
+#include <meta/display.h>
+#include <meta/window.h>
+
+enum {
+ PROP_0,
+
+ PROP_WINDOW
+};
+
+typedef struct _ShellGtkEmbedPrivate ShellGtkEmbedPrivate;
+
+struct _ShellGtkEmbedPrivate
+{
+ ShellEmbeddedWindow *window;
+
+ ClutterActor *window_actor;
+ gulong window_actor_destroyed_handler;
+
+ gulong window_created_handler;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_TYPE_CLONE);
+
+static void shell_gtk_embed_set_window (ShellGtkEmbed *embed,
+ ShellEmbeddedWindow *window);
+
+static void
+shell_gtk_embed_on_window_destroy (GtkWidget *object,
+ ShellGtkEmbed *embed)
+{
+ shell_gtk_embed_set_window (embed, NULL);
+}
+
+static void
+shell_gtk_embed_remove_window_actor (ShellGtkEmbed *embed)
+{
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+
+ if (priv->window_actor)
+ {
+ g_clear_signal_handler (&priv->window_actor_destroyed_handler,
+ priv->window_actor);
+
+ g_object_unref (priv->window_actor);
+ priv->window_actor = NULL;
+ }
+
+ clutter_clone_set_source (CLUTTER_CLONE (embed), NULL);
+}
+
+static void
+shell_gtk_embed_window_created_cb (MetaDisplay *display,
+ MetaWindow *window,
+ ShellGtkEmbed *embed)
+{
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+ Window xwindow = meta_window_get_xwindow (window);
+ GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (priv->window));
+
+ if (gdk_window && xwindow == gdk_x11_window_get_xid (gdk_window))
+ {
+ ClutterActor *window_actor =
+ CLUTTER_ACTOR (meta_window_get_compositor_private (window));
+ GCallback remove_cb = G_CALLBACK (shell_gtk_embed_remove_window_actor);
+ cairo_region_t *empty_region;
+
+ clutter_clone_set_source (CLUTTER_CLONE (embed), window_actor);
+
+ /* We want to explicitly clear the clone source when the window
+ actor is destroyed because otherwise we might end up keeping
+ it alive after it has been disposed. Otherwise this can cause
+ a crash if there is a paint after mutter notices that the top
+ level window has been destroyed, which causes it to dispose
+ the window, and before the tray manager notices that the
+ window is gone which would otherwise reset the window and
+ unref the clone */
+ priv->window_actor = g_object_ref (window_actor);
+ priv->window_actor_destroyed_handler =
+ g_signal_connect_swapped (window_actor,
+ "destroy",
+ remove_cb,
+ embed);
+
+ /* Hide the original actor otherwise it will appear in the scene
+ as a normal window */
+ clutter_actor_set_opacity (window_actor, 0);
+
+ /* Also make sure it (or any of its children) doesn't block
+ events on wayland */
+ shell_util_set_hidden_from_pick (window_actor, TRUE);
+
+ /* Set an empty input shape on the window so that it can't get
+ any input. This probably isn't the ideal way to achieve this.
+ It would probably be better to force the window to go behind
+ Mutter's guard window, but this is quite difficult to do as
+ Mutter doesn't manage the stacking for override redirect
+ windows and the guard window is repeatedly lowered to the
+ bottom of the stack. */
+ empty_region = cairo_region_create ();
+ gdk_window_input_shape_combine_region (gdk_window,
+ empty_region,
+ 0, 0 /* offset x/y */);
+ cairo_region_destroy (empty_region);
+
+ gdk_window_lower (gdk_window);
+
+ /* Now that we've found the window we don't need to listen for
+ new windows anymore */
+ g_clear_signal_handler (&priv->window_created_handler,
+ display);
+ }
+}
+
+static void
+shell_gtk_embed_on_window_mapped (GtkWidget *object,
+ ShellGtkEmbed *embed)
+{
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+
+ if (priv->window_created_handler == 0 && priv->window_actor == NULL)
+ /* Listen for new windows so we can detect when Mutter has
+ created a MutterWindow for this window */
+ priv->window_created_handler =
+ g_signal_connect (display,
+ "window-created",
+ G_CALLBACK (shell_gtk_embed_window_created_cb),
+ embed);
+}
+
+static void
+shell_gtk_embed_set_window (ShellGtkEmbed *embed,
+ ShellEmbeddedWindow *window)
+{
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+
+ if (priv->window)
+ {
+ g_clear_signal_handler (&priv->window_created_handler, display);
+
+ shell_gtk_embed_remove_window_actor (embed);
+
+ _shell_embedded_window_set_actor (priv->window, NULL);
+
+ g_object_unref (priv->window);
+
+ g_signal_handlers_disconnect_by_func (priv->window,
+ (gpointer)shell_gtk_embed_on_window_destroy,
+ embed);
+
+ g_signal_handlers_disconnect_by_func (priv->window,
+ (gpointer)shell_gtk_embed_on_window_mapped,
+ embed);
+ }
+
+ priv->window = window;
+
+ if (priv->window)
+ {
+ g_object_ref (priv->window);
+
+ _shell_embedded_window_set_actor (priv->window, embed);
+
+ g_signal_connect (priv->window, "destroy",
+ G_CALLBACK (shell_gtk_embed_on_window_destroy), embed);
+
+ g_signal_connect (priv->window, "map",
+ G_CALLBACK (shell_gtk_embed_on_window_mapped), embed);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (embed));
+}
+
+static void
+shell_gtk_embed_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ shell_gtk_embed_set_window (embed, (ShellEmbeddedWindow *)g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_gtk_embed_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ g_value_set_object (value, priv->window);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_gtk_embed_get_preferred_width (ClutterActor *actor,
+ float for_height,
+ float *min_width_p,
+ float *natural_width_p)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+
+ if (priv->window
+ && gtk_widget_get_visible (GTK_WIDGET (priv->window)))
+ {
+ GtkRequisition min_req, natural_req;
+ gtk_widget_get_preferred_size (GTK_WIDGET (priv->window), &min_req, &natural_req);
+
+ *min_width_p = min_req.width;
+ *natural_width_p = natural_req.width;
+ }
+ else
+ *min_width_p = *natural_width_p = 0;
+}
+
+static void
+shell_gtk_embed_get_preferred_height (ClutterActor *actor,
+ float for_width,
+ float *min_height_p,
+ float *natural_height_p)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+
+ if (priv->window
+ && gtk_widget_get_visible (GTK_WIDGET (priv->window)))
+ {
+ GtkRequisition min_req, natural_req;
+ gtk_widget_get_preferred_size (GTK_WIDGET (priv->window), &min_req, &natural_req);
+
+ *min_height_p = min_req.height;
+ *natural_height_p = natural_req.height;
+ }
+ else
+ *min_height_p = *natural_height_p = 0;
+}
+
+static void
+shell_gtk_embed_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+ float wx, wy;
+
+ CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->
+ allocate (actor, box);
+
+ /* Find the actor's new coordinates in terms of the stage (which is
+ * priv->window's parent window.
+ */
+ clutter_actor_get_transformed_position (actor, &wx, &wy);
+
+ _shell_embedded_window_allocate (priv->window,
+ (int)(0.5 + wx), (int)(0.5 + wy),
+ box->x2 - box->x1,
+ box->y2 - box->y1);
+}
+
+static void
+shell_gtk_embed_map (ClutterActor *actor)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+
+ _shell_embedded_window_map (priv->window);
+
+ CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->map (actor);
+}
+
+static void
+shell_gtk_embed_unmap (ClutterActor *actor)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
+ ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
+
+ _shell_embedded_window_unmap (priv->window);
+
+ CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->unmap (actor);
+}
+
+static void
+shell_gtk_embed_dispose (GObject *object)
+{
+ ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
+
+ G_OBJECT_CLASS (shell_gtk_embed_parent_class)->dispose (object);
+
+ shell_gtk_embed_set_window (embed, NULL);
+}
+
+static void
+shell_gtk_embed_class_init (ShellGtkEmbedClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ object_class->get_property = shell_gtk_embed_get_property;
+ object_class->set_property = shell_gtk_embed_set_property;
+ object_class->dispose = shell_gtk_embed_dispose;
+
+ actor_class->get_preferred_width = shell_gtk_embed_get_preferred_width;
+ actor_class->get_preferred_height = shell_gtk_embed_get_preferred_height;
+ actor_class->allocate = shell_gtk_embed_allocate;
+ actor_class->map = shell_gtk_embed_map;
+ actor_class->unmap = shell_gtk_embed_unmap;
+
+ g_object_class_install_property (object_class,
+ PROP_WINDOW,
+ g_param_spec_object ("window",
+ "Window",
+ "ShellEmbeddedWindow to embed",
+ SHELL_TYPE_EMBEDDED_WINDOW,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+shell_gtk_embed_init (ShellGtkEmbed *embed)
+{
+}
+
+/*
+ * Public API
+ */
+ClutterActor *
+shell_gtk_embed_new (ShellEmbeddedWindow *window)
+{
+ g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL);
+
+ return g_object_new (SHELL_TYPE_GTK_EMBED,
+ "window", window,
+ NULL);
+}
diff --git a/src/shell-gtk-embed.h b/src/shell-gtk-embed.h
new file mode 100644
index 0000000..4cfc489
--- /dev/null
+++ b/src/shell-gtk-embed.h
@@ -0,0 +1,20 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_GTK_EMBED_H__
+#define __SHELL_GTK_EMBED_H__
+
+#include <clutter/clutter.h>
+
+#include "shell-embedded-window.h"
+
+#define SHELL_TYPE_GTK_EMBED (shell_gtk_embed_get_type ())
+G_DECLARE_DERIVABLE_TYPE (ShellGtkEmbed, shell_gtk_embed,
+ SHELL, GTK_EMBED, ClutterClone)
+
+struct _ShellGtkEmbedClass
+{
+ ClutterCloneClass parent_class;
+};
+
+ClutterActor *shell_gtk_embed_new (ShellEmbeddedWindow *window);
+
+#endif /* __SHELL_GTK_EMBED_H__ */
diff --git a/src/shell-invert-lightness-effect.c b/src/shell-invert-lightness-effect.c
new file mode 100644
index 0000000..f856ede
--- /dev/null
+++ b/src/shell-invert-lightness-effect.c
@@ -0,0 +1,152 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2010-2012 Inclusive Design Research Centre, OCAD University.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Joseph Scheuhammer <clown@alum.mit.edu>
+ */
+
+/**
+ * SECTION:shell-invert-lightness-effect
+ * @short_description: A colorization effect where lightness is inverted but
+ * color is not.
+ * @see_also: #ClutterEffect, #ClutterOffscreenEffect
+ *
+ * #ShellInvertLightnessEffect is a sub-class of #ClutterEffect that enhances
+ * the appearance of a clutter actor. Specifically it inverts the lightness
+ * of a #ClutterActor (e.g., darker colors become lighter, white becomes black,
+ * and white, black).
+ */
+
+#define SHELL_INVERT_LIGHTNESS_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT, ShellInvertLightnessEffectClass))
+#define SHELL_IS_INVERT_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT))
+#define SHELL_INVERT_LIGHTNESS_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_INVERT_LIGHTNESS_EFFEC, ShellInvertLightnessEffectClass))
+
+#include "shell-invert-lightness-effect.h"
+
+#include <cogl/cogl.h>
+
+struct _ShellInvertLightnessEffect
+{
+ ClutterOffscreenEffect parent_instance;
+
+ CoglPipeline *pipeline;
+};
+
+struct _ShellInvertLightnessEffectClass
+{
+ ClutterOffscreenEffectClass parent_class;
+
+ CoglPipeline *base_pipeline;
+};
+
+/* Lightness inversion in GLSL.
+ */
+static const gchar *invert_lightness_source =
+ "cogl_texel = texture2D (cogl_sampler, cogl_tex_coord.st);\n"
+ "vec3 effect = vec3 (cogl_texel);\n"
+ "\n"
+ "float maxColor = max (cogl_texel.r, max (cogl_texel.g, cogl_texel.b));\n"
+ "float minColor = min (cogl_texel.r, min (cogl_texel.g, cogl_texel.b));\n"
+ "float lightness = (maxColor + minColor) / 2.0;\n"
+ "\n"
+ "float delta = (1.0 - lightness) - lightness;\n"
+ "effect.rgb = (effect.rgb + delta);\n"
+ "\n"
+ "cogl_texel = vec4 (effect, cogl_texel.a);\n";
+
+G_DEFINE_TYPE (ShellInvertLightnessEffect,
+ shell_invert_lightness_effect,
+ CLUTTER_TYPE_OFFSCREEN_EFFECT);
+
+static CoglPipeline *
+shell_glsl_effect_create_pipeline (ClutterOffscreenEffect *effect,
+ CoglTexture *texture)
+{
+ ShellInvertLightnessEffect *self = SHELL_INVERT_LIGHTNESS_EFFECT (effect);
+
+ cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
+
+ return cogl_object_ref (self->pipeline);
+}
+
+static void
+shell_invert_lightness_effect_dispose (GObject *gobject)
+{
+ ShellInvertLightnessEffect *self = SHELL_INVERT_LIGHTNESS_EFFECT (gobject);
+
+ if (self->pipeline != NULL)
+ {
+ cogl_object_unref (self->pipeline);
+ self->pipeline = NULL;
+ }
+
+ G_OBJECT_CLASS (shell_invert_lightness_effect_parent_class)->dispose (gobject);
+}
+
+static void
+shell_invert_lightness_effect_class_init (ShellInvertLightnessEffectClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterOffscreenEffectClass *offscreen_class;
+
+ offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
+ offscreen_class->create_pipeline = shell_glsl_effect_create_pipeline;
+
+ gobject_class->dispose = shell_invert_lightness_effect_dispose;
+}
+
+static void
+shell_invert_lightness_effect_init (ShellInvertLightnessEffect *self)
+{
+ ShellInvertLightnessEffectClass *klass;
+ klass = SHELL_INVERT_LIGHTNESS_EFFECT_GET_CLASS (self);
+
+ if (G_UNLIKELY (klass->base_pipeline == NULL))
+ {
+ CoglSnippet *snippet;
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ klass->base_pipeline = cogl_pipeline_new (ctx);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
+ NULL,
+ NULL);
+ cogl_snippet_set_replace (snippet, invert_lightness_source);
+ cogl_pipeline_add_layer_snippet (klass->base_pipeline, 0, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_pipeline_set_layer_null_texture (klass->base_pipeline, 0);
+ }
+
+ self->pipeline = cogl_pipeline_copy (klass->base_pipeline);
+}
+
+/**
+ * shell_invert_lightness_effect_new:
+ *
+ * Creates a new #ShellInvertLightnessEffect to be used with
+ * clutter_actor_add_effect()
+ *
+ * Return value: (transfer full): the newly created
+ * #ShellInvertLightnessEffect or %NULL. Use g_object_unref() when done.
+ */
+ClutterEffect *
+shell_invert_lightness_effect_new (void)
+{
+ return g_object_new (SHELL_TYPE_INVERT_LIGHTNESS_EFFECT, NULL);
+}
diff --git a/src/shell-invert-lightness-effect.h b/src/shell-invert-lightness-effect.h
new file mode 100644
index 0000000..3d7cf3a
--- /dev/null
+++ b/src/shell-invert-lightness-effect.h
@@ -0,0 +1,41 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright © 2010-2012 Inclusive Design Research Centre, OCAD University.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Joseph Scheuhammer <clown@alum.mit.edu>
+ */
+#ifndef __SHELL_INVERT_LIGHTNESS_EFFECT_H__
+#define __SHELL_INVERT_LIGHTNESS_EFFECT_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_INVERT_LIGHTNESS_EFFECT (shell_invert_lightness_effect_get_type ())
+#define SHELL_INVERT_LIGHTNESS_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT, ShellInvertLightnessEffect))
+#define SHELL_IS_INVERT_LIGHTNESS_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT))
+
+typedef struct _ShellInvertLightnessEffect ShellInvertLightnessEffect;
+typedef struct _ShellInvertLightnessEffectClass ShellInvertLightnessEffectClass;
+
+GType shell_invert_lightness_effect_get_type (void) G_GNUC_CONST;
+
+ClutterEffect *shell_invert_lightness_effect_new (void);
+
+G_END_DECLS
+
+#endif /* __SHELL_INVERT_LIGHTNESS_EFFECT_H__ */
diff --git a/src/shell-keyring-prompt.c b/src/shell-keyring-prompt.c
new file mode 100644
index 0000000..bb03279
--- /dev/null
+++ b/src/shell-keyring-prompt.c
@@ -0,0 +1,832 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2012 Red Hat, Inc.
+ * 2012 Stef Walter <stefw@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Stef Walter <stefw@gnome.org>
+ */
+
+#include "config.h"
+
+#include "shell-keyring-prompt.h"
+#include "shell-secure-text-buffer.h"
+
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr.h>
+
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+typedef struct _ShellPasswordPromptPrivate ShellPasswordPromptPrivate;
+
+typedef enum
+{
+ PROMPTING_NONE,
+ PROMPTING_FOR_CONFIRM,
+ PROMPTING_FOR_PASSWORD
+} PromptingMode;
+
+struct _ShellKeyringPrompt
+{
+ GObject parent;
+
+ gchar *title;
+ gchar *message;
+ gchar *description;
+ gchar *warning;
+ gchar *choice_label;
+ gboolean choice_chosen;
+ gboolean password_new;
+ guint password_strength;
+ gchar *continue_label;
+ gchar *cancel_label;
+
+ GTask *task;
+ ClutterText *password_actor;
+ ClutterText *confirm_actor;
+ PromptingMode mode;
+ gboolean shown;
+};
+
+enum {
+ PROP_0,
+
+ PROP_PASSWORD_VISIBLE,
+ PROP_CONFIRM_VISIBLE,
+ PROP_WARNING_VISIBLE,
+ PROP_CHOICE_VISIBLE,
+ PROP_PASSWORD_ACTOR,
+ PROP_CONFIRM_ACTOR,
+
+ N_PROPS,
+
+ /* GcrPrompt */
+ PROP_TITLE,
+ PROP_MESSAGE,
+ PROP_DESCRIPTION,
+ PROP_WARNING,
+ PROP_CHOICE_LABEL,
+ PROP_CHOICE_CHOSEN,
+ PROP_PASSWORD_NEW,
+ PROP_PASSWORD_STRENGTH,
+ PROP_CALLER_WINDOW,
+ PROP_CONTINUE_LABEL,
+ PROP_CANCEL_LABEL
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+static void shell_keyring_prompt_iface (GcrPromptInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ShellKeyringPrompt, shell_keyring_prompt, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, shell_keyring_prompt_iface);
+);
+
+enum {
+ SIGNAL_SHOW_PASSWORD,
+ SIGNAL_SHOW_CONFIRM,
+ SIGNAL_LAST
+};
+
+static gint signals[SIGNAL_LAST];
+
+static void
+shell_keyring_prompt_init (ShellKeyringPrompt *self)
+{
+
+}
+
+static gchar *
+remove_mnemonics (const GValue *value)
+{
+ const gchar mnemonic = '_';
+ gchar *stripped_label, *temp;
+ const gchar *label;
+
+ g_return_val_if_fail (value != NULL, NULL);
+ g_return_val_if_fail (G_VALUE_HOLDS_STRING (value), NULL);
+
+ label = g_value_get_string (value);
+ if (!label)
+ return NULL;
+
+ /* Stripped label will have the original label length at most */
+ stripped_label = temp = g_new (gchar, strlen(label) + 1);
+ g_assert (stripped_label != NULL);
+
+ while (*label != '\0')
+ {
+ if (*label == mnemonic)
+ label++;
+ *(temp++) = *(label++);
+ }
+ *temp = '\0';
+
+ return stripped_label;
+}
+
+static void
+shell_keyring_prompt_set_property (GObject *obj,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ g_free (self->title);
+ self->title = g_value_dup_string (value);
+ g_object_notify (obj, "title");
+ break;
+ case PROP_MESSAGE:
+ g_free (self->message);
+ self->message = g_value_dup_string (value);
+ g_object_notify (obj, "message");
+ break;
+ case PROP_DESCRIPTION:
+ g_free (self->description);
+ self->description = g_value_dup_string (value);
+ g_object_notify (obj, "description");
+ break;
+ case PROP_WARNING:
+ g_free (self->warning);
+ self->warning = g_value_dup_string (value);
+ if (!self->warning)
+ self->warning = g_strdup ("");
+ g_object_notify (obj, "warning");
+ g_object_notify_by_pspec (obj, props[PROP_WARNING_VISIBLE]);
+ break;
+ case PROP_CHOICE_LABEL:
+ g_free (self->choice_label);
+ self->choice_label = remove_mnemonics (value);
+ if (!self->choice_label)
+ self->choice_label = g_strdup ("");
+ g_object_notify (obj, "choice-label");
+ g_object_notify_by_pspec (obj, props[PROP_CHOICE_VISIBLE]);
+ break;
+ case PROP_CHOICE_CHOSEN:
+ self->choice_chosen = g_value_get_boolean (value);
+ g_object_notify (obj, "choice-chosen");
+ break;
+ case PROP_PASSWORD_NEW:
+ self->password_new = g_value_get_boolean (value);
+ g_object_notify (obj, "password-new");
+ g_object_notify_by_pspec (obj, props[PROP_CONFIRM_VISIBLE]);
+ break;
+ case PROP_CALLER_WINDOW:
+ /* ignored */
+ break;
+ case PROP_CONTINUE_LABEL:
+ g_free (self->continue_label);
+ self->continue_label = remove_mnemonics (value);
+ g_object_notify (obj, "continue-label");
+ break;
+ case PROP_CANCEL_LABEL:
+ g_free (self->cancel_label);
+ self->cancel_label = remove_mnemonics (value);
+ g_object_notify (obj, "cancel-label");
+ break;
+ case PROP_PASSWORD_ACTOR:
+ shell_keyring_prompt_set_password_actor (self, g_value_get_object (value));
+ break;
+ case PROP_CONFIRM_ACTOR:
+ shell_keyring_prompt_set_confirm_actor (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_keyring_prompt_get_property (GObject *obj,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ g_value_set_string (value, self->title ? self->title : "");
+ break;
+ case PROP_MESSAGE:
+ g_value_set_string (value, self->message ? self->message : "");
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, self->description ? self->description : "");
+ break;
+ case PROP_WARNING:
+ g_value_set_string (value, self->warning ? self->warning : "");
+ break;
+ case PROP_CHOICE_LABEL:
+ g_value_set_string (value, self->choice_label ? self->choice_label : "");
+ break;
+ case PROP_CHOICE_CHOSEN:
+ g_value_set_boolean (value, self->choice_chosen);
+ break;
+ case PROP_PASSWORD_NEW:
+ g_value_set_boolean (value, self->password_new);
+ break;
+ case PROP_PASSWORD_STRENGTH:
+ g_value_set_int (value, self->password_strength);
+ break;
+ case PROP_CALLER_WINDOW:
+ g_value_set_string (value, "");
+ break;
+ case PROP_CONTINUE_LABEL:
+ g_value_set_string (value, self->continue_label);
+ break;
+ case PROP_CANCEL_LABEL:
+ g_value_set_string (value, self->cancel_label);
+ break;
+ case PROP_PASSWORD_VISIBLE:
+ g_value_set_boolean (value, self->mode == PROMPTING_FOR_PASSWORD);
+ break;
+ case PROP_CONFIRM_VISIBLE:
+ g_value_set_boolean (value, self->password_new &&
+ self->mode == PROMPTING_FOR_PASSWORD);
+ break;
+ case PROP_WARNING_VISIBLE:
+ g_value_set_boolean (value, self->warning && self->warning[0]);
+ break;
+ case PROP_CHOICE_VISIBLE:
+ g_value_set_boolean (value, self->choice_label && self->choice_label[0]);
+ break;
+ case PROP_PASSWORD_ACTOR:
+ g_value_set_object (value, shell_keyring_prompt_get_password_actor (self));
+ break;
+ case PROP_CONFIRM_ACTOR:
+ g_value_set_object (value, shell_keyring_prompt_get_confirm_actor (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_keyring_prompt_dispose (GObject *obj)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+ if (self->shown)
+ gcr_prompt_close (GCR_PROMPT (self));
+
+ if (self->task)
+ shell_keyring_prompt_cancel (self);
+ g_assert (self->task == NULL);
+
+ shell_keyring_prompt_set_password_actor (self, NULL);
+ shell_keyring_prompt_set_confirm_actor (self, NULL);
+
+ G_OBJECT_CLASS (shell_keyring_prompt_parent_class)->dispose (obj);
+}
+
+static void
+shell_keyring_prompt_finalize (GObject *obj)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+ g_free (self->title);
+ g_free (self->message);
+ g_free (self->description);
+ g_free (self->warning);
+ g_free (self->choice_label);
+ g_free (self->continue_label);
+ g_free (self->cancel_label);
+
+ G_OBJECT_CLASS (shell_keyring_prompt_parent_class)->finalize (obj);
+}
+
+static void
+shell_keyring_prompt_class_init (ShellKeyringPromptClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = shell_keyring_prompt_get_property;
+ gobject_class->set_property = shell_keyring_prompt_set_property;
+ gobject_class->dispose = shell_keyring_prompt_dispose;
+ gobject_class->finalize = shell_keyring_prompt_finalize;
+
+ g_object_class_override_property (gobject_class, PROP_TITLE, "title");
+
+ g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
+
+ g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
+
+ g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
+
+ g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
+
+ g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
+
+ g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
+
+ g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
+
+ g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
+
+ g_object_class_override_property (gobject_class, PROP_CONTINUE_LABEL, "continue-label");
+
+ g_object_class_override_property (gobject_class, PROP_CANCEL_LABEL, "cancel-label");
+
+ /**
+ * ShellKeyringPrompt:password-visible:
+ *
+ * Whether the password entry is visible or not.
+ */
+ props[PROP_PASSWORD_VISIBLE] =
+ g_param_spec_boolean ("password-visible",
+ "Password visible",
+ "Password field is visible",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellKeyringPrompt:confirm-visible:
+ *
+ * Whether the password confirm entry is visible or not.
+ */
+ props[PROP_CONFIRM_VISIBLE] =
+ g_param_spec_boolean ("confirm-visible",
+ "Confirm visible",
+ "Confirm field is visible",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellKeyringPrompt:warning-visible:
+ *
+ * Whether the warning label is visible or not.
+ */
+ props[PROP_WARNING_VISIBLE] =
+ g_param_spec_boolean ("warning-visible",
+ "Warning visible",
+ "Warning is visible",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellKeyringPrompt:choice-visible:
+ *
+ * Whether the choice check box is visible or not.
+ */
+ props[PROP_CHOICE_VISIBLE] =
+ g_param_spec_boolean ("choice-visible",
+ "Choice visible",
+ "Choice is visible",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * ShellKeyringPrompt:password-actor:
+ *
+ * Text field for password
+ */
+ props[PROP_PASSWORD_ACTOR] =
+ g_param_spec_object ("password-actor",
+ "Password actor",
+ "Text field for password",
+ CLUTTER_TYPE_TEXT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * ShellKeyringPrompt:confirm-actor:
+ *
+ * Text field for confirmation password
+ */
+ props[PROP_CONFIRM_ACTOR] =
+ g_param_spec_object ("confirm-actor",
+ "Confirm actor",
+ "Text field for confirming password",
+ CLUTTER_TYPE_TEXT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+
+ signals[SIGNAL_SHOW_PASSWORD] = g_signal_new ("show-password", G_TYPE_FROM_CLASS (klass),
+ 0, 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[SIGNAL_SHOW_CONFIRM] = g_signal_new ("show-confirm", G_TYPE_FROM_CLASS (klass),
+ 0, 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+shell_keyring_prompt_password_async (GcrPrompt *prompt,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (prompt);
+ GObject *obj;
+
+ if (self->task != NULL) {
+ g_warning ("this prompt can only show one prompt at a time");
+ return;
+ }
+
+ self->mode = PROMPTING_FOR_PASSWORD;
+ self->task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_source_tag (self->task, shell_keyring_prompt_password_async);
+
+ obj = G_OBJECT (self);
+ g_object_notify (obj, "password-visible");
+ g_object_notify (obj, "confirm-visible");
+ g_object_notify (obj, "warning-visible");
+ g_object_notify (obj, "choice-visible");
+
+ self->shown = TRUE;
+ g_signal_emit (self, signals[SIGNAL_SHOW_PASSWORD], 0);
+}
+
+static const gchar *
+shell_keyring_prompt_password_finish (GcrPrompt *prompt,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_get_source_object (G_TASK (result)) == prompt, NULL);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_keyring_prompt_password_async), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+shell_keyring_prompt_confirm_async (GcrPrompt *prompt,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (prompt);
+ GObject *obj;
+
+ if (self->task != NULL) {
+ g_warning ("this prompt is already prompting");
+ return;
+ }
+
+ self->mode = PROMPTING_FOR_CONFIRM;
+ self->task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_source_tag (self->task, shell_keyring_prompt_confirm_async);
+
+ obj = G_OBJECT (self);
+ g_object_notify (obj, "password-visible");
+ g_object_notify (obj, "confirm-visible");
+ g_object_notify (obj, "warning-visible");
+ g_object_notify (obj, "choice-visible");
+
+ self->shown = TRUE;
+ g_signal_emit (self, signals[SIGNAL_SHOW_CONFIRM], 0);
+}
+
+static GcrPromptReply
+shell_keyring_prompt_confirm_finish (GcrPrompt *prompt,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = G_TASK (result);
+ gssize res;
+
+ g_return_val_if_fail (g_task_get_source_object (task) == prompt,
+ GCR_PROMPT_REPLY_CANCEL);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_keyring_prompt_confirm_async), GCR_PROMPT_REPLY_CANCEL);
+
+ res = g_task_propagate_int (task, error);
+ return res == -1 ? GCR_PROMPT_REPLY_CANCEL : (GcrPromptReply)res;
+}
+
+static void
+shell_keyring_prompt_close (GcrPrompt *prompt)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (prompt);
+
+ /*
+ * We expect keyring.js to connect to this signal and do the
+ * actual work of closing the prompt.
+ */
+
+ self->shown = FALSE;
+}
+
+static void
+shell_keyring_prompt_iface (GcrPromptInterface *iface)
+{
+ iface->prompt_password_async = shell_keyring_prompt_password_async;
+ iface->prompt_password_finish = shell_keyring_prompt_password_finish;
+ iface->prompt_confirm_async = shell_keyring_prompt_confirm_async;
+ iface->prompt_confirm_finish = shell_keyring_prompt_confirm_finish;
+ iface->prompt_close = shell_keyring_prompt_close;
+}
+
+/**
+ * shell_keyring_prompt_new:
+ *
+ * Create new internal prompt base
+ *
+ * Returns: (transfer full): new internal prompt
+ */
+ShellKeyringPrompt *
+shell_keyring_prompt_new (void)
+{
+ return g_object_new (SHELL_TYPE_KEYRING_PROMPT, NULL);
+}
+
+/**
+ * shell_keyring_prompt_get_password_actor:
+ * @self: the internal prompt
+ *
+ * Get the prompt password text actor
+ *
+ * Returns: (transfer none) (nullable): the password actor
+ */
+ClutterText *
+shell_keyring_prompt_get_password_actor (ShellKeyringPrompt *self)
+{
+ g_return_val_if_fail (SHELL_IS_KEYRING_PROMPT (self), NULL);
+ return self->password_actor;
+}
+
+/**
+ * shell_keyring_prompt_get_confirm_actor:
+ * @self: the internal prompt
+ *
+ * Get the prompt password text actor
+ *
+ * Returns: (transfer none) (nullable): the password actor
+ */
+ClutterText *
+shell_keyring_prompt_get_confirm_actor (ShellKeyringPrompt *self)
+{
+ g_return_val_if_fail (SHELL_IS_KEYRING_PROMPT (self), NULL);
+ return self->confirm_actor;
+}
+
+static guint
+calculate_password_strength (const gchar *password)
+{
+ int upper, lower, digit, misc;
+ gdouble pwstrength;
+ int length, i;
+
+ /*
+ * This code is based on the Master Password dialog in Firefox
+ * (pref-masterpass.js)
+ * Original code triple-licensed under the MPL, GPL, and LGPL
+ * so is license-compatible with this file
+ */
+
+ length = strlen (password);
+
+ /* Always return 0 for empty passwords */
+ if (length == 0)
+ return 0;
+
+ upper = 0;
+ lower = 0;
+ digit = 0;
+ misc = 0;
+
+ for (i = 0; i < length ; i++)
+ {
+ if (g_ascii_isdigit (password[i]))
+ digit++;
+ else if (g_ascii_islower (password[i]))
+ lower++;
+ else if (g_ascii_isupper (password[i]))
+ upper++;
+ else
+ misc++;
+ }
+
+ if (length > 5)
+ length = 5;
+ if (digit > 3)
+ digit = 3;
+ if (upper > 3)
+ upper = 3;
+ if (misc > 3)
+ misc = 3;
+
+ pwstrength = ((length * 1) - 2) +
+ (digit * 1) +
+ (misc * 1.5) +
+ (upper * 1);
+
+ /* Always return 1+ for non-empty passwords */
+ if (pwstrength < 1.0)
+ pwstrength = 1.0;
+ if (pwstrength > 10.0)
+ pwstrength = 10.0;
+
+ return (guint)pwstrength;
+}
+
+static void
+on_password_changed (ClutterText *text,
+ gpointer user_data)
+{
+ ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (user_data);
+ const gchar *password;
+
+ password = clutter_text_get_text (self->password_actor);
+
+ self->password_strength = calculate_password_strength (password);
+ g_object_notify (G_OBJECT (self), "password-strength");
+}
+
+/**
+ * shell_keyring_prompt_set_password_actor:
+ * @self: the internal prompt
+ * @password_actor: (nullable): the password actor
+ *
+ * Set the prompt password text actor
+ */
+void
+shell_keyring_prompt_set_password_actor (ShellKeyringPrompt *self,
+ ClutterText *password_actor)
+{
+ ClutterTextBuffer *buffer;
+
+ g_return_if_fail (SHELL_IS_KEYRING_PROMPT (self));
+ g_return_if_fail (password_actor == NULL || CLUTTER_IS_TEXT (password_actor));
+
+ if (self->password_actor == password_actor)
+ return;
+
+ if (password_actor)
+ {
+ buffer = shell_secure_text_buffer_new ();
+ clutter_text_set_buffer (password_actor, buffer);
+ g_object_unref (buffer);
+
+ g_signal_connect (password_actor, "text-changed", G_CALLBACK (on_password_changed), self);
+ g_object_ref (password_actor);
+ }
+ if (self->password_actor)
+ {
+ g_signal_handlers_disconnect_by_func (self->password_actor, on_password_changed, self);
+ g_object_unref (self->password_actor);
+ }
+
+ self->password_actor = password_actor;
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PASSWORD_ACTOR]);
+}
+
+/**
+ * shell_keyring_prompt_set_confirm_actor:
+ * @self: the internal prompt
+ * @confirm_actor: (nullable): the confirm password actor
+ *
+ * Set the prompt password confirmation text actor
+ */
+void
+shell_keyring_prompt_set_confirm_actor (ShellKeyringPrompt *self,
+ ClutterText *confirm_actor)
+{
+ ClutterTextBuffer *buffer;
+
+ g_return_if_fail (SHELL_IS_KEYRING_PROMPT (self));
+ g_return_if_fail (confirm_actor == NULL || CLUTTER_IS_TEXT (confirm_actor));
+
+ if (self->confirm_actor == confirm_actor)
+ return;
+
+ if (confirm_actor)
+ {
+ buffer = shell_secure_text_buffer_new ();
+ clutter_text_set_buffer (confirm_actor, buffer);
+ g_object_unref (buffer);
+
+ g_object_ref (confirm_actor);
+ }
+ if (self->confirm_actor)
+ g_object_unref (self->confirm_actor);
+ self->confirm_actor = confirm_actor;
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONFIRM_ACTOR]);
+}
+
+/**
+ * shell_keyring_prompt_complete:
+ * @self: the internal prompt
+ *
+ * Called by the implementation when the prompt completes. There are various
+ * checks done. %TRUE is returned if the prompt actually should complete.
+ *
+ * Returns: whether the prompt completed
+ */
+gboolean
+shell_keyring_prompt_complete (ShellKeyringPrompt *self)
+{
+ GTask *res;
+ PromptingMode mode;
+ const gchar *password;
+ const gchar *confirm;
+ const gchar *env;
+
+ g_return_val_if_fail (SHELL_IS_KEYRING_PROMPT (self), FALSE);
+ g_return_val_if_fail (self->mode != PROMPTING_NONE, FALSE);
+ g_return_val_if_fail (self->task != NULL, FALSE);
+
+ password = clutter_text_get_text (self->password_actor);
+
+ if (self->mode == PROMPTING_FOR_PASSWORD)
+ {
+ /* Is it a new password? */
+ if (self->password_new)
+ {
+ confirm = clutter_text_get_text (self->confirm_actor);
+
+ /* Do the passwords match? */
+ if (!g_str_equal (password, confirm))
+ {
+ gcr_prompt_set_warning (GCR_PROMPT (self), _("Passwords do not match."));
+ return FALSE;
+ }
+
+ /* Don't allow blank passwords if in paranoid mode */
+ env = g_getenv ("GNOME_KEYRING_PARANOID");
+ if (env && *env)
+ {
+ gcr_prompt_set_warning (GCR_PROMPT (self), _("Password cannot be blank"));
+ return FALSE;
+ }
+ }
+
+ self->password_strength = calculate_password_strength (password);
+ g_object_notify (G_OBJECT (self), "password-strength");
+ }
+
+ res = self->task;
+ mode = self->mode;
+ self->task = NULL;
+ self->mode = PROMPTING_NONE;
+
+ if (mode == PROMPTING_FOR_CONFIRM)
+ g_task_return_int (res, (gssize)GCR_PROMPT_REPLY_CONTINUE);
+ else
+ g_task_return_pointer (res, (gpointer)password, NULL);
+ g_object_unref (res);
+
+ return TRUE;
+}
+
+/**
+ * shell_keyring_prompt_cancel:
+ * @self: the internal prompt
+ *
+ * Called by implementation when the prompt is cancelled.
+ */
+void
+shell_keyring_prompt_cancel (ShellKeyringPrompt *self)
+{
+ GTask *res;
+ PromptingMode mode;
+
+ g_return_if_fail (SHELL_IS_KEYRING_PROMPT (self));
+
+ /*
+ * If cancelled while not prompting, we should just close the prompt,
+ * the user wants it to go away.
+ */
+ if (self->mode == PROMPTING_NONE)
+ {
+ if (self->shown)
+ gcr_prompt_close (GCR_PROMPT (self));
+ return;
+ }
+
+ g_return_if_fail (self->task != NULL);
+
+ res = self->task;
+ mode = self->mode;
+ self->task = NULL;
+ self->mode = PROMPTING_NONE;
+
+ if (mode == PROMPTING_FOR_CONFIRM)
+ g_task_return_int (res, (gssize) GCR_PROMPT_REPLY_CANCEL);
+ else
+ g_task_return_pointer (res, NULL, NULL);
+ g_object_unref (res);
+}
diff --git a/src/shell-keyring-prompt.h b/src/shell-keyring-prompt.h
new file mode 100644
index 0000000..fcacf4c
--- /dev/null
+++ b/src/shell-keyring-prompt.h
@@ -0,0 +1,57 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* shell-keyring-prompt.c - prompt handler for gnome-keyring-daemon
+
+ Copyright (C) 2011 Stefan Walter
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Stef Walter <stef@thewalter.net>
+*/
+
+#ifndef __SHELL_KEYRING_PROMPT_H__
+#define __SHELL_KEYRING_PROMPT_H__
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ShellKeyringPrompt ShellKeyringPrompt;
+
+#define SHELL_TYPE_KEYRING_PROMPT (shell_keyring_prompt_get_type ())
+G_DECLARE_FINAL_TYPE (ShellKeyringPrompt, shell_keyring_prompt,
+ SHELL, KEYRING_PROMPT, GObject)
+
+ShellKeyringPrompt * shell_keyring_prompt_new (void);
+
+ClutterText * shell_keyring_prompt_get_password_actor (ShellKeyringPrompt *self);
+
+void shell_keyring_prompt_set_password_actor (ShellKeyringPrompt *self,
+ ClutterText *password_actor);
+
+ClutterText * shell_keyring_prompt_get_confirm_actor (ShellKeyringPrompt *self);
+
+void shell_keyring_prompt_set_confirm_actor (ShellKeyringPrompt *self,
+ ClutterText *confirm_actor);
+
+gboolean shell_keyring_prompt_complete (ShellKeyringPrompt *self);
+
+void shell_keyring_prompt_cancel (ShellKeyringPrompt *self);
+
+G_END_DECLS
+
+#endif /* __SHELL_KEYRING_PROMPT_H__ */
diff --git a/src/shell-mount-operation.c b/src/shell-mount-operation.c
new file mode 100644
index 0000000..8d43368
--- /dev/null
+++ b/src/shell-mount-operation.c
@@ -0,0 +1,189 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Cosimo Cecchi <cosimoc@redhat.com>
+ *
+ */
+
+#include "shell-mount-operation.h"
+
+/* This is a dummy class; we would like to be able to subclass the
+ * object from JS but we can't yet; the default GMountOperation impl
+ * automatically calls g_mount_operation_reply(UNHANDLED) after an idle,
+ * in interactive methods. We want to handle the reply ourselves
+ * instead, so we just override the default methods with empty ones,
+ * except for ask-password, as we don't want to handle that.
+ *
+ * Also, we need to workaround the fact that gjs doesn't support type
+ * annotations for signals yet (so we can't effectively forward e.g.
+ * the GPid array to JS).
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=645978
+ */
+
+enum {
+ SHOW_PROCESSES_2,
+ NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0, };
+
+typedef struct _ShellMountOperationPrivate ShellMountOperationPrivate;
+
+struct _ShellMountOperation
+{
+ GMountOperation parent_instance;
+
+ ShellMountOperationPrivate *priv;
+};
+
+struct _ShellMountOperationPrivate {
+ GArray *pids;
+ gchar **choices;
+ gchar *message;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellMountOperation, shell_mount_operation, G_TYPE_MOUNT_OPERATION);
+
+static void
+shell_mount_operation_init (ShellMountOperation *self)
+{
+ self->priv = shell_mount_operation_get_instance_private (self);
+}
+
+static void
+shell_mount_operation_ask_password (GMountOperation *op,
+ const char *message,
+ const char *default_user,
+ const char *default_domain,
+ GAskPasswordFlags flags)
+{
+ /* do nothing */
+}
+
+static void
+shell_mount_operation_ask_question (GMountOperation *op,
+ const char *message,
+ const char *choices[])
+{
+ /* do nothing */
+}
+
+static void
+shell_mount_operation_show_processes (GMountOperation *operation,
+ const gchar *message,
+ GArray *processes,
+ const gchar *choices[])
+{
+ ShellMountOperation *self = SHELL_MOUNT_OPERATION (operation);
+
+ if (self->priv->pids != NULL)
+ {
+ g_array_unref (self->priv->pids);
+ self->priv->pids = NULL;
+ }
+
+ g_free (self->priv->message);
+ g_strfreev (self->priv->choices);
+
+ /* save the parameters */
+ self->priv->pids = g_array_ref (processes);
+ self->priv->choices = g_strdupv ((gchar **) choices);
+ self->priv->message = g_strdup (message);
+
+ g_signal_emit (self, signals[SHOW_PROCESSES_2], 0);
+}
+
+static void
+shell_mount_operation_finalize (GObject *obj)
+{
+ ShellMountOperation *self = SHELL_MOUNT_OPERATION (obj);
+
+ g_strfreev (self->priv->choices);
+ g_free (self->priv->message);
+
+ if (self->priv->pids != NULL)
+ {
+ g_array_unref (self->priv->pids);
+ self->priv->pids = NULL;
+ }
+
+ G_OBJECT_CLASS (shell_mount_operation_parent_class)->finalize (obj);
+}
+
+static void
+shell_mount_operation_class_init (ShellMountOperationClass *klass)
+{
+ GMountOperationClass *mclass;
+ GObjectClass *oclass;
+
+ mclass = G_MOUNT_OPERATION_CLASS (klass);
+ mclass->show_processes = shell_mount_operation_show_processes;
+ mclass->ask_question = shell_mount_operation_ask_question;
+ mclass->ask_password = shell_mount_operation_ask_password;
+
+ oclass = G_OBJECT_CLASS (klass);
+ oclass->finalize = shell_mount_operation_finalize;
+
+ signals[SHOW_PROCESSES_2] =
+ g_signal_new ("show-processes-2",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+GMountOperation *
+shell_mount_operation_new (void)
+{
+ return g_object_new (SHELL_TYPE_MOUNT_OPERATION, NULL);
+}
+
+/**
+ * shell_mount_operation_get_show_processes_pids:
+ * @self: a #ShellMountOperation
+ *
+ * Returns: (transfer full) (element-type GPid): a #GArray
+ */
+GArray *
+shell_mount_operation_get_show_processes_pids (ShellMountOperation *self)
+{
+ return g_array_ref (self->priv->pids);
+}
+
+/**
+ * shell_mount_operation_get_show_processes_choices:
+ * @self: a #ShellMountOperation
+ *
+ * Returns: (transfer full):
+ */
+gchar **
+shell_mount_operation_get_show_processes_choices (ShellMountOperation *self)
+{
+ return g_strdupv (self->priv->choices);
+}
+
+/**
+ * shell_mount_operation_get_show_processes_message:
+ * @self: a #ShellMountOperation
+ *
+ * Returns: (transfer full):
+ */
+gchar *
+shell_mount_operation_get_show_processes_message (ShellMountOperation *self)
+{
+ return g_strdup (self->priv->message);
+}
diff --git a/src/shell-mount-operation.h b/src/shell-mount-operation.h
new file mode 100644
index 0000000..c4019a5
--- /dev/null
+++ b/src/shell-mount-operation.h
@@ -0,0 +1,41 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Cosimo Cecchi <cosimoc@redhat.com>
+ *
+ */
+
+#ifndef __SHELL_MOUNT_OPERATION_H__
+#define __SHELL_MOUNT_OPERATION_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_MOUNT_OPERATION (shell_mount_operation_get_type ())
+G_DECLARE_FINAL_TYPE (ShellMountOperation, shell_mount_operation,
+ SHELL, MOUNT_OPERATION, GMountOperation)
+
+GMountOperation *shell_mount_operation_new (void);
+
+GArray * shell_mount_operation_get_show_processes_pids (ShellMountOperation *self);
+gchar ** shell_mount_operation_get_show_processes_choices (ShellMountOperation *self);
+gchar * shell_mount_operation_get_show_processes_message (ShellMountOperation *self);
+
+G_END_DECLS
+
+#endif /* __SHELL_MOUNT_OPERATION_H__ */
diff --git a/src/shell-network-agent.c b/src/shell-network-agent.c
new file mode 100644
index 0000000..474394f
--- /dev/null
+++ b/src/shell-network-agent.c
@@ -0,0 +1,899 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2011 Red Hat, Inc.
+ * 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
+ * 2017 Lubomir Rintel <lkundrak@v3.sk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include <libsecret/secret.h>
+
+#include "shell-network-agent.h"
+
+enum {
+ SIGNAL_NEW_REQUEST,
+ SIGNAL_CANCEL_REQUEST,
+ SIGNAL_LAST
+};
+
+static gint signals[SIGNAL_LAST];
+
+typedef struct {
+ GCancellable * cancellable;
+ ShellNetworkAgent *self;
+
+ gchar *request_id;
+ NMConnection *connection;
+ gchar *setting_name;
+ gchar **hints;
+ NMSecretAgentGetSecretsFlags flags;
+ NMSecretAgentOldGetSecretsFunc callback;
+ gpointer callback_data;
+
+ GVariantDict *entries;
+ GVariantBuilder builder_vpn;
+} ShellAgentRequest;
+
+struct _ShellNetworkAgentPrivate {
+ /* <gchar *request_id, ShellAgentRequest *request> */
+ GHashTable *requests;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellNetworkAgent, shell_network_agent, NM_TYPE_SECRET_AGENT_OLD)
+
+static const SecretSchema network_agent_schema = {
+ "org.freedesktop.NetworkManager.Connection",
+ SECRET_SCHEMA_DONT_MATCH_NAME,
+ {
+ { SHELL_KEYRING_UUID_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { SHELL_KEYRING_SN_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { SHELL_KEYRING_SK_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { NULL, 0 },
+ }
+};
+
+static void
+shell_agent_request_free (gpointer data)
+{
+ ShellAgentRequest *request = data;
+
+ g_cancellable_cancel (request->cancellable);
+ g_object_unref (request->cancellable);
+ g_object_unref (request->self);
+ g_object_unref (request->connection);
+ g_free (request->setting_name);
+ g_strfreev (request->hints);
+ g_clear_pointer (&request->entries, g_variant_dict_unref);
+ g_variant_builder_clear (&request->builder_vpn);
+
+ g_free (request);
+}
+
+static void
+shell_agent_request_cancel (ShellAgentRequest *request)
+{
+ GError *error;
+ ShellNetworkAgent *self;
+
+ self = request->self;
+
+ error = g_error_new (NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+ "Canceled by NetworkManager");
+ request->callback (NM_SECRET_AGENT_OLD (self), request->connection,
+ NULL, error, request->callback_data);
+
+ g_signal_emit (self, signals[SIGNAL_CANCEL_REQUEST], 0, request->request_id);
+
+ g_hash_table_remove (self->priv->requests, request->request_id);
+ g_error_free (error);
+}
+
+static void
+shell_network_agent_init (ShellNetworkAgent *agent)
+{
+ ShellNetworkAgentPrivate *priv;
+
+ priv = agent->priv = shell_network_agent_get_instance_private (agent);
+ priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, shell_agent_request_free);
+}
+
+static void
+shell_network_agent_finalize (GObject *object)
+{
+ ShellNetworkAgentPrivate *priv = SHELL_NETWORK_AGENT (object)->priv;
+ GError *error;
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+
+ error = g_error_new (NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+ "The secret agent is going away");
+
+ g_hash_table_iter_init (&iter, priv->requests);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ ShellAgentRequest *request = value;
+
+ request->callback (NM_SECRET_AGENT_OLD (object),
+ request->connection,
+ NULL, error,
+ request->callback_data);
+ }
+
+ g_hash_table_destroy (priv->requests);
+ g_error_free (error);
+
+ G_OBJECT_CLASS (shell_network_agent_parent_class)->finalize (object);
+}
+
+static void
+request_secrets_from_ui (ShellAgentRequest *request)
+{
+ g_signal_emit (request->self, signals[SIGNAL_NEW_REQUEST], 0,
+ request->request_id,
+ request->connection,
+ request->setting_name,
+ request->hints,
+ (int)request->flags);
+}
+
+static void
+check_always_ask_cb (NMSetting *setting,
+ const gchar *key,
+ const GValue *value,
+ GParamFlags flags,
+ gpointer user_data)
+{
+ gboolean *always_ask = user_data;
+ NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ if (flags & NM_SETTING_PARAM_SECRET)
+ {
+ if (nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
+ {
+ if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+ *always_ask = TRUE;
+ }
+ }
+}
+
+static gboolean
+has_always_ask (NMSetting *setting)
+{
+ gboolean always_ask = FALSE;
+
+ nm_setting_enumerate_values (setting, check_always_ask_cb, &always_ask);
+ return always_ask;
+}
+
+static gboolean
+is_connection_always_ask (NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ const gchar *ctype;
+ NMSetting *setting;
+
+ /* For the given connection type, check if the secrets for that connection
+ * are always-ask or not.
+ */
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con);
+ ctype = nm_setting_connection_get_connection_type (s_con);
+
+ setting = nm_connection_get_setting_by_name (connection, ctype);
+ g_return_val_if_fail (setting != NULL, FALSE);
+
+ if (has_always_ask (setting))
+ return TRUE;
+
+ /* Try type-specific settings too; be a bit paranoid and only consider
+ * secrets from settings relevant to the connection type.
+ */
+ if (NM_IS_SETTING_WIRELESS (setting))
+ {
+ setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+ if (setting && has_always_ask (setting))
+ return TRUE;
+ setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+ if (setting && has_always_ask (setting))
+ return TRUE;
+ }
+ else if (NM_IS_SETTING_WIRED (setting))
+ {
+ setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
+ if (setting && has_always_ask (setting))
+ return TRUE;
+ setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+ if (setting && has_always_ask (setting))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+get_secrets_keyring_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ShellAgentRequest *closure;
+ ShellNetworkAgent *self;
+ ShellNetworkAgentPrivate *priv;
+ GError *secret_error = NULL;
+ GError *error = NULL;
+ GList *items;
+ GList *l;
+ gboolean secrets_found = FALSE;
+ GVariantBuilder builder_setting, builder_connection;
+ g_autoptr (GVariant) setting = NULL;
+
+ items = secret_service_search_finish (NULL, result, &secret_error);
+
+ if (g_error_matches (secret_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (secret_error);
+ return;
+ }
+
+ closure = user_data;
+ self = closure->self;
+ priv = self->priv;
+
+ if (secret_error != NULL)
+ {
+ g_set_error (&error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_FAILED,
+ "Internal error while retrieving secrets from the keyring (%s)", secret_error->message);
+ g_error_free (secret_error);
+ closure->callback (NM_SECRET_AGENT_OLD (closure->self), closure->connection, NULL, error, closure->callback_data);
+
+ goto out;
+ }
+
+ g_variant_builder_init (&builder_setting, NM_VARIANT_TYPE_SETTING);
+
+ for (l = items; l; l = g_list_next (l))
+ {
+ SecretItem *item = l->data;
+ GHashTable *attributes;
+ GHashTableIter iter;
+ const gchar *name, *attribute;
+ SecretValue *secret = secret_item_get_secret (item);
+
+ /* This can happen if the user denied a request to unlock */
+ if (secret == NULL)
+ continue;
+
+ attributes = secret_item_get_attributes (item);
+ g_hash_table_iter_init (&iter, attributes);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&attribute))
+ {
+ if (g_strcmp0 (name, SHELL_KEYRING_SK_TAG) == 0)
+ {
+ g_variant_builder_add (&builder_setting, "{sv}", attribute,
+ g_variant_new_string (secret_value_get (secret, NULL)));
+
+ secrets_found = TRUE;
+
+ break;
+ }
+ }
+
+ g_hash_table_unref (attributes);
+ secret_value_unref (secret);
+ }
+
+ g_list_free_full (items, g_object_unref);
+ setting = g_variant_ref_sink (g_variant_builder_end (&builder_setting));
+
+ /* All VPN requests get sent to the VPN's auth dialog, since it knows better
+ * than the agent about what secrets are required. Otherwise, if no secrets
+ * were found and interaction is allowed the ask for some secrets, because
+ * NetworkManager will fail the connection if not secrets are returned
+ * instead of asking again with REQUEST_NEW.
+ */
+ if (strcmp(closure->setting_name, NM_SETTING_VPN_SETTING_NAME) == 0 ||
+ (!secrets_found && (closure->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)))
+ {
+ nm_connection_update_secrets (closure->connection, closure->setting_name,
+ setting, NULL);
+
+ closure->entries = g_variant_dict_new (setting);
+ request_secrets_from_ui (closure);
+ return;
+ }
+
+ g_variant_builder_init (&builder_connection, NM_VARIANT_TYPE_CONNECTION);
+ g_variant_builder_add (&builder_connection, "{s@a{sv}}",
+ closure->setting_name, setting);
+
+ closure->callback (NM_SECRET_AGENT_OLD (closure->self), closure->connection,
+ g_variant_builder_end (&builder_connection), NULL,
+ closure->callback_data);
+
+ out:
+ g_hash_table_remove (priv->requests, closure->request_id);
+ g_clear_error (&error);
+}
+
+static void
+shell_network_agent_get_secrets (NMSecretAgentOld *agent,
+ NMConnection *connection,
+ const gchar *connection_path,
+ const gchar *setting_name,
+ const gchar **hints,
+ NMSecretAgentGetSecretsFlags flags,
+ NMSecretAgentOldGetSecretsFunc callback,
+ gpointer callback_data)
+{
+ ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
+ ShellAgentRequest *request;
+ GHashTable *attributes;
+ char *request_id;
+
+ request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
+ if ((request = g_hash_table_lookup (self->priv->requests, request_id)) != NULL)
+ {
+ /* We already have a request pending for this (connection, setting)
+ * Cancel it before starting the new one.
+ * This will also free the request structure and associated resources.
+ */
+ shell_agent_request_cancel (request);
+ }
+
+ request = g_new0 (ShellAgentRequest, 1);
+ request->self = g_object_ref (self);
+ request->cancellable = g_cancellable_new ();
+ request->connection = g_object_ref (connection);
+ request->setting_name = g_strdup (setting_name);
+ request->hints = g_strdupv ((gchar **)hints);
+ request->flags = flags;
+ request->callback = callback;
+ request->callback_data = callback_data;
+
+ request->request_id = request_id;
+ g_hash_table_replace (self->priv->requests, request->request_id, request);
+
+ g_variant_builder_init (&request->builder_vpn, G_VARIANT_TYPE ("a{ss}"));
+
+ if ((flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) ||
+ ((flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)
+ && is_connection_always_ask (request->connection)))
+ {
+ request->entries = g_variant_dict_new (NULL);
+ request_secrets_from_ui (request);
+ return;
+ }
+
+ attributes = secret_attributes_build (&network_agent_schema,
+ SHELL_KEYRING_UUID_TAG, nm_connection_get_uuid (connection),
+ SHELL_KEYRING_SN_TAG, setting_name,
+ NULL);
+
+ secret_service_search (NULL, &network_agent_schema, attributes,
+ SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
+ request->cancellable, get_secrets_keyring_cb, request);
+
+ g_hash_table_unref (attributes);
+}
+
+void
+shell_network_agent_add_vpn_secret (ShellNetworkAgent *self,
+ gchar *request_id,
+ gchar *setting_key,
+ gchar *setting_value)
+{
+ ShellNetworkAgentPrivate *priv;
+ ShellAgentRequest *request;
+
+ g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
+
+ priv = self->priv;
+ request = g_hash_table_lookup (priv->requests, request_id);
+ g_return_if_fail (request != NULL);
+
+ g_variant_builder_add (&request->builder_vpn, "{ss}", setting_key, setting_value);
+}
+
+void
+shell_network_agent_set_password (ShellNetworkAgent *self,
+ gchar *request_id,
+ gchar *setting_key,
+ gchar *setting_value)
+{
+ ShellNetworkAgentPrivate *priv;
+ ShellAgentRequest *request;
+
+ g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
+
+ priv = self->priv;
+ request = g_hash_table_lookup (priv->requests, request_id);
+ g_return_if_fail (request != NULL);
+
+ g_variant_dict_insert (request->entries, setting_key, "s", setting_value);
+}
+
+void
+shell_network_agent_respond (ShellNetworkAgent *self,
+ gchar *request_id,
+ ShellNetworkAgentResponse response)
+{
+ ShellNetworkAgentPrivate *priv;
+ ShellAgentRequest *request;
+ GVariantBuilder builder_connection;
+ GVariant *vpn_secrets, *setting;
+
+ g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
+
+ priv = self->priv;
+ request = g_hash_table_lookup (priv->requests, request_id);
+ g_return_if_fail (request != NULL);
+
+ if (response == SHELL_NETWORK_AGENT_USER_CANCELED)
+ {
+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_USER_CANCELED,
+ "Network dialog was canceled by the user");
+
+ request->callback (NM_SECRET_AGENT_OLD (self), request->connection, NULL, error, request->callback_data);
+ g_error_free (error);
+ g_hash_table_remove (priv->requests, request_id);
+ return;
+ }
+
+ if (response == SHELL_NETWORK_AGENT_INTERNAL_ERROR)
+ {
+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_FAILED,
+ "An internal error occurred while processing the request.");
+
+ request->callback (NM_SECRET_AGENT_OLD (self), request->connection, NULL, error, request->callback_data);
+ g_error_free (error);
+ g_hash_table_remove (priv->requests, request_id);
+ return;
+ }
+
+ /* response == SHELL_NETWORK_AGENT_CONFIRMED */
+
+ /* VPN secrets are stored as a hash of secrets in a single setting */
+ vpn_secrets = g_variant_builder_end (&request->builder_vpn);
+ if (g_variant_n_children (vpn_secrets))
+ g_variant_dict_insert_value (request->entries, NM_SETTING_VPN_SECRETS, vpn_secrets);
+ else
+ g_variant_unref (vpn_secrets);
+
+ setting = g_variant_dict_end (request->entries);
+
+ /* Save any updated secrets */
+ if ((request->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) ||
+ (request->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW))
+ {
+ NMConnection *dup = nm_simple_connection_new_clone (request->connection);
+
+ nm_connection_update_secrets (dup, request->setting_name, setting, NULL);
+ nm_secret_agent_old_save_secrets (NM_SECRET_AGENT_OLD (self), dup, NULL, NULL);
+ g_object_unref (dup);
+ }
+
+ g_variant_builder_init (&builder_connection, NM_VARIANT_TYPE_CONNECTION);
+ g_variant_builder_add (&builder_connection, "{s@a{sv}}",
+ request->setting_name, setting);
+
+ request->callback (NM_SECRET_AGENT_OLD (self), request->connection,
+ g_variant_builder_end (&builder_connection), NULL,
+ request->callback_data);
+
+ g_hash_table_remove (priv->requests, request_id);
+}
+
+static void
+search_vpn_plugin (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ NMVpnPluginInfo *info = NULL;
+ char *service = task_data;
+
+ info = nm_vpn_plugin_info_new_search_file (NULL, service);
+
+ if (info)
+ {
+ g_task_return_pointer (task, info, g_object_unref);
+ }
+ else
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "No plugin for %s", service);
+ }
+}
+
+void
+shell_network_agent_search_vpn_plugin (ShellNetworkAgent *self,
+ const char *service,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
+ g_return_if_fail (service != NULL);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_source_tag (task, shell_network_agent_search_vpn_plugin);
+ g_task_set_task_data (task, g_strdup (service), g_free);
+
+ g_task_run_in_thread (task, search_vpn_plugin);
+}
+
+/**
+ * shell_network_agent_search_vpn_plugin_finish:
+ *
+ * Returns: (nullable) (transfer full): The found plugin or %NULL
+ */
+NMVpnPluginInfo *
+shell_network_agent_search_vpn_plugin_finish (ShellNetworkAgent *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SHELL_IS_NETWORK_AGENT (self), NULL);
+ g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+shell_network_agent_cancel_get_secrets (NMSecretAgentOld *agent,
+ const gchar *connection_path,
+ const gchar *setting_name)
+{
+ ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
+ ShellNetworkAgentPrivate *priv = self->priv;
+ gchar *request_id;
+ ShellAgentRequest *request;
+
+ request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
+ request = g_hash_table_lookup (priv->requests, request_id);
+ g_free (request_id);
+
+ if (!request)
+ {
+ /* We've already sent the result, but the caller cancelled the
+ * operation before receiving that result.
+ */
+ return;
+ }
+
+ shell_agent_request_cancel (request);
+}
+
+/************************* saving of secrets ****************************************/
+
+static GHashTable *
+create_keyring_add_attr_list (NMConnection *connection,
+ const gchar *connection_uuid,
+ const gchar *connection_id,
+ const gchar *setting_name,
+ const gchar *setting_key,
+ gchar **out_display_name)
+{
+ NMSettingConnection *s_con;
+
+ if (connection)
+ {
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ g_return_val_if_fail (s_con != NULL, NULL);
+ connection_uuid = nm_setting_connection_get_uuid (s_con);
+ connection_id = nm_setting_connection_get_id (s_con);
+ }
+
+ g_return_val_if_fail (connection_uuid != NULL, NULL);
+ g_return_val_if_fail (connection_id != NULL, NULL);
+ g_return_val_if_fail (setting_name != NULL, NULL);
+ g_return_val_if_fail (setting_key != NULL, NULL);
+
+ if (out_display_name)
+ {
+ *out_display_name = g_strdup_printf ("Network secret for %s/%s/%s",
+ connection_id,
+ setting_name,
+ setting_key);
+ }
+
+ return secret_attributes_build (&network_agent_schema,
+ SHELL_KEYRING_UUID_TAG, connection_uuid,
+ SHELL_KEYRING_SN_TAG, setting_name,
+ SHELL_KEYRING_SK_TAG, setting_key,
+ NULL);
+}
+
+typedef struct
+{
+ /* Sort of ref count, indicates the number of secrets we still need to save */
+ gint n_secrets;
+
+ NMSecretAgentOld *self;
+ NMConnection *connection;
+ gpointer callback;
+ gpointer callback_data;
+} KeyringRequest;
+
+static void
+keyring_request_free (KeyringRequest *r)
+{
+ g_object_unref (r->self);
+ g_object_unref (r->connection);
+
+ g_free (r);
+}
+
+static void
+save_secret_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ KeyringRequest *call = user_data;
+ NMSecretAgentOldSaveSecretsFunc callback = call->callback;
+
+ call->n_secrets--;
+
+ if (call->n_secrets == 0)
+ {
+ if (callback)
+ callback (call->self, call->connection, NULL, call->callback_data);
+ keyring_request_free (call);
+ }
+}
+
+static void
+save_one_secret (KeyringRequest *r,
+ NMSetting *setting,
+ const gchar *key,
+ const gchar *secret,
+ const gchar *display_name)
+{
+ GHashTable *attrs;
+ gchar *alt_display_name = NULL;
+ const gchar *setting_name;
+ NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ /* Only save agent-owned secrets (not system-owned or always-ask) */
+ nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
+ if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+ return;
+
+ setting_name = nm_setting_get_name (setting);
+ g_assert (setting_name);
+
+ attrs = create_keyring_add_attr_list (r->connection, NULL, NULL,
+ setting_name,
+ key,
+ display_name ? NULL : &alt_display_name);
+ g_assert (attrs);
+ r->n_secrets++;
+ secret_password_storev (&network_agent_schema, attrs, SECRET_COLLECTION_DEFAULT,
+ display_name ? display_name : alt_display_name,
+ secret, NULL, save_secret_cb, r);
+
+ g_hash_table_unref (attrs);
+ g_free (alt_display_name);
+}
+
+static void
+vpn_secret_iter_cb (const gchar *key,
+ const gchar *secret,
+ gpointer user_data)
+{
+ KeyringRequest *r = user_data;
+ NMSetting *setting;
+ const gchar *service_name, *id;
+ gchar *display_name;
+
+ if (secret && strlen (secret))
+ {
+ setting = nm_connection_get_setting (r->connection, NM_TYPE_SETTING_VPN);
+ g_assert (setting);
+ service_name = nm_setting_vpn_get_service_type (NM_SETTING_VPN (setting));
+ g_assert (service_name);
+ id = nm_connection_get_id (r->connection);
+ g_assert (id);
+
+ display_name = g_strdup_printf ("VPN %s secret for %s/%s/" NM_SETTING_VPN_SETTING_NAME,
+ key,
+ id,
+ service_name);
+ save_one_secret (r, setting, key, secret, display_name);
+ g_free (display_name);
+ }
+}
+
+static void
+write_one_secret_to_keyring (NMSetting *setting,
+ const gchar *key,
+ const GValue *value,
+ GParamFlags flags,
+ gpointer user_data)
+{
+ KeyringRequest *r = user_data;
+ const gchar *secret;
+
+ /* Non-secrets obviously don't get saved in the keyring */
+ if (!(flags & NM_SETTING_PARAM_SECRET))
+ return;
+
+ if (NM_IS_SETTING_VPN (setting) && (g_strcmp0 (key, NM_SETTING_VPN_SECRETS) == 0))
+ {
+ /* Process VPN secrets specially since it's a hash of secrets, not just one */
+ nm_setting_vpn_foreach_secret (NM_SETTING_VPN (setting),
+ vpn_secret_iter_cb,
+ r);
+ }
+ else
+ {
+ if (!G_VALUE_HOLDS_STRING (value))
+ return;
+
+ secret = g_value_get_string (value);
+ if (secret && strlen (secret))
+ save_one_secret (r, setting, key, secret, NULL);
+ }
+}
+
+static void
+save_delete_cb (NMSecretAgentOld *agent,
+ NMConnection *connection,
+ GError *error,
+ gpointer user_data)
+{
+ KeyringRequest *r = user_data;
+
+ /* Ignore errors; now save all new secrets */
+ nm_connection_for_each_setting_value (connection, write_one_secret_to_keyring, r);
+
+ /* If no secrets actually got saved there may be nothing to do so
+ * try to complete the request here. If there were secrets to save the
+ * request will get completed when those keyring calls return (at the next
+ * mainloop iteration).
+ */
+ if (r->n_secrets == 0)
+ {
+ if (r->callback)
+ ((NMSecretAgentOldSaveSecretsFunc)r->callback) (agent, connection, NULL, r->callback_data);
+ keyring_request_free (r);
+ }
+}
+
+static void
+shell_network_agent_save_secrets (NMSecretAgentOld *agent,
+ NMConnection *connection,
+ const gchar *connection_path,
+ NMSecretAgentOldSaveSecretsFunc callback,
+ gpointer callback_data)
+{
+ KeyringRequest *r;
+
+ r = g_new (KeyringRequest, 1);
+ r->n_secrets = 0;
+ r->self = g_object_ref (agent);
+ r->connection = g_object_ref (connection);
+ r->callback = callback;
+ r->callback_data = callback_data;
+
+ /* First delete any existing items in the keyring */
+ nm_secret_agent_old_delete_secrets (agent, connection, save_delete_cb, r);
+}
+
+static void
+delete_items_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ KeyringRequest *r = user_data;
+ GError *secret_error = NULL;
+ GError *error = NULL;
+ NMSecretAgentOldDeleteSecretsFunc callback = r->callback;
+
+ secret_password_clear_finish (result, &secret_error);
+ if (secret_error != NULL)
+ {
+ error = g_error_new (NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_FAILED,
+ "The request could not be completed. Keyring result: %s",
+ secret_error->message);
+ g_error_free (secret_error);
+ }
+
+ callback (r->self, r->connection, error, r->callback_data);
+ g_clear_error (&error);
+ keyring_request_free (r);
+}
+
+static void
+shell_network_agent_delete_secrets (NMSecretAgentOld *agent,
+ NMConnection *connection,
+ const gchar *connection_path,
+ NMSecretAgentOldDeleteSecretsFunc callback,
+ gpointer callback_data)
+{
+ KeyringRequest *r;
+ NMSettingConnection *s_con;
+ const gchar *uuid;
+
+ r = g_new (KeyringRequest, 1);
+ r->n_secrets = 0; /* ignored by delete secrets calls */
+ r->self = g_object_ref (agent);
+ r->connection = g_object_ref (connection);
+ r->callback = callback;
+ r->callback_data = callback_data;
+
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con);
+ uuid = nm_setting_connection_get_uuid (s_con);
+ g_assert (uuid);
+
+ secret_password_clear (&network_agent_schema, NULL, delete_items_cb, r,
+ SHELL_KEYRING_UUID_TAG, uuid,
+ NULL);
+}
+
+void
+shell_network_agent_class_init (ShellNetworkAgentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS (klass);
+
+ gobject_class->finalize = shell_network_agent_finalize;
+
+ agent_class->get_secrets = shell_network_agent_get_secrets;
+ agent_class->cancel_get_secrets = shell_network_agent_cancel_get_secrets;
+ agent_class->save_secrets = shell_network_agent_save_secrets;
+ agent_class->delete_secrets = shell_network_agent_delete_secrets;
+
+ signals[SIGNAL_NEW_REQUEST] = g_signal_new ("new-request",
+ G_TYPE_FROM_CLASS (klass),
+ 0, /* flags */
+ 0, /* class offset */
+ NULL, /* accumulator */
+ NULL, /* accu_data */
+ NULL, /* marshaller */
+ G_TYPE_NONE, /* return */
+ 5, /* n_params */
+ G_TYPE_STRING,
+ NM_TYPE_CONNECTION,
+ G_TYPE_STRING,
+ G_TYPE_STRV,
+ G_TYPE_INT);
+
+ signals[SIGNAL_CANCEL_REQUEST] = g_signal_new ("cancel-request",
+ G_TYPE_FROM_CLASS (klass),
+ 0, /* flags */
+ 0, /* class offset */
+ NULL, /* accumulator */
+ NULL, /* accu_data */
+ NULL, /* marshaller */
+ G_TYPE_NONE,
+ 1, /* n_params */
+ G_TYPE_STRING);
+}
diff --git a/src/shell-network-agent.h b/src/shell-network-agent.h
new file mode 100644
index 0000000..0ffde02
--- /dev/null
+++ b/src/shell-network-agent.h
@@ -0,0 +1,73 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_NETWORK_AGENT_H__
+#define __SHELL_NETWORK_AGENT_H__
+
+#include <glib-object.h>
+#include <glib.h>
+#include <NetworkManager.h>
+#include <nm-secret-agent-old.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ SHELL_NETWORK_AGENT_CONFIRMED,
+ SHELL_NETWORK_AGENT_USER_CANCELED,
+ SHELL_NETWORK_AGENT_INTERNAL_ERROR
+} ShellNetworkAgentResponse;
+
+typedef struct _ShellNetworkAgent ShellNetworkAgent;
+typedef struct _ShellNetworkAgentClass ShellNetworkAgentClass;
+typedef struct _ShellNetworkAgentPrivate ShellNetworkAgentPrivate;
+
+#define SHELL_TYPE_NETWORK_AGENT (shell_network_agent_get_type ())
+#define SHELL_NETWORK_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgent))
+#define SHELL_IS_NETWORK_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_NETWORK_AGENT))
+#define SHELL_NETWORK_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgentClass))
+#define SHELL_IS_NETWORK_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_NETWORK_AGENT))
+#define SHELL_NETWORK_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgentClass))
+
+struct _ShellNetworkAgent
+{
+ /*< private >*/
+ NMSecretAgentOld parent_instance;
+
+ ShellNetworkAgentPrivate *priv;
+};
+
+struct _ShellNetworkAgentClass
+{
+ /*< private >*/
+ NMSecretAgentOldClass parent_class;
+};
+
+/* used by SHELL_TYPE_NETWORK_AGENT */
+GType shell_network_agent_get_type (void);
+
+void shell_network_agent_add_vpn_secret (ShellNetworkAgent *self,
+ gchar *request_id,
+ gchar *setting_key,
+ gchar *setting_value);
+void shell_network_agent_set_password (ShellNetworkAgent *self,
+ gchar *request_id,
+ gchar *setting_key,
+ gchar *setting_value);
+void shell_network_agent_respond (ShellNetworkAgent *self,
+ gchar *request_id,
+ ShellNetworkAgentResponse response);
+
+void shell_network_agent_search_vpn_plugin (ShellNetworkAgent *self,
+ const char *service,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NMVpnPluginInfo *shell_network_agent_search_vpn_plugin_finish (ShellNetworkAgent *self,
+ GAsyncResult *result,
+ GError **error);
+
+/* If these are kept in sync with nm-applet, secrets will be shared */
+#define SHELL_KEYRING_UUID_TAG "connection-uuid"
+#define SHELL_KEYRING_SN_TAG "setting-name"
+#define SHELL_KEYRING_SK_TAG "setting-key"
+
+G_END_DECLS
+
+#endif /* __SHELL_NETWORK_AGENT_H__ */
diff --git a/src/shell-perf-helper.c b/src/shell-perf-helper.c
new file mode 100644
index 0000000..a50376e
--- /dev/null
+++ b/src/shell-perf-helper.c
@@ -0,0 +1,376 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* gnome-shell-perf-helper: a program to create windows for performance tests
+ *
+ * Running performance tests with whatever windows a user has open results
+ * in unreliable results, so instead we hide all other windows and talk
+ * to this program over D-Bus to create just the windows we want.
+ */
+
+#include "config.h"
+
+#include <math.h>
+
+#include <gtk/gtk.h>
+
+#define BUS_NAME "org.gnome.Shell.PerfHelper"
+
+static void destroy_windows (void);
+static void finish_wait_windows (void);
+static void check_finish_wait_windows (void);
+
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.gnome.Shell.PerfHelper'>"
+ " <method name='Exit'/>"
+ " <method name='CreateWindow'>"
+ " <arg type='i' name='width' direction='in'/>"
+ " <arg type='i' name='height' direction='in'/>"
+ " <arg type='b' name='alpha' direction='in'/>"
+ " <arg type='b' name='maximized' direction='in'/>"
+ " <arg type='b' name='redraws' direction='in'/>"
+ " </method>"
+ " <method name='WaitWindows'/>"
+ " <method name='DestroyWindows'/>"
+ " </interface>"
+ "</node>";
+
+typedef struct {
+ GtkWidget *window;
+ int width;
+ int height;
+
+ guint alpha : 1;
+ guint maximized : 1;
+ guint redraws : 1;
+ guint mapped : 1;
+ guint exposed : 1;
+ guint pending : 1;
+
+ gint64 start_time;
+ gint64 time;
+} WindowInfo;
+
+static int opt_idle_timeout = 30;
+
+static GOptionEntry opt_entries[] =
+ {
+ { "idle-timeout", 'r', 0, G_OPTION_ARG_INT, &opt_idle_timeout, "Exit after N seconds", "N" },
+ { NULL }
+ };
+
+static guint timeout_id;
+static GList *our_windows;
+static GList *wait_windows_invocations;
+
+static gboolean
+on_timeout (gpointer data)
+{
+ timeout_id = 0;
+
+ destroy_windows ();
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+static void
+establish_timeout (void)
+{
+ g_clear_handle_id (&timeout_id, g_source_remove);
+
+ timeout_id = g_timeout_add (opt_idle_timeout * 1000, on_timeout, NULL);
+ g_source_set_name_by_id (timeout_id, "[gnome-shell] on_timeout");
+}
+
+static void
+destroy_windows (void)
+{
+ GList *l;
+
+ for (l = our_windows; l; l = l->next)
+ {
+ WindowInfo *info = l->data;
+ gtk_widget_destroy (info->window);
+ g_free (info);
+ }
+
+ g_list_free (our_windows);
+ our_windows = NULL;
+
+ check_finish_wait_windows ();
+}
+
+static gboolean
+on_window_map_event (GtkWidget *window,
+ GdkEventAny *event,
+ WindowInfo *info)
+{
+ info->mapped = TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+on_child_draw (GtkWidget *window,
+ cairo_t *cr,
+ WindowInfo *info)
+{
+ cairo_rectangle_int_t allocation;
+ double x_offset, y_offset;
+
+ gtk_widget_get_allocation (window, &allocation);
+
+ /* We draw an arbitrary pattern of red lines near the border of the
+ * window to make it more clear than empty windows if something
+ * is drastrically wrong.
+ */
+
+ cairo_save (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+ if (info->alpha)
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+ else
+ cairo_set_source_rgb (cr, 1, 1, 1);
+
+ cairo_paint (cr);
+ cairo_restore (cr);
+
+ if (info->redraws)
+ {
+ double position = (info->time - info->start_time) / 1000000.;
+ x_offset = 20 * cos (2 * M_PI * position);
+ y_offset = 20 * sin (2 * M_PI * position);
+ }
+ else
+ {
+ x_offset = y_offset = 0;
+ }
+
+ cairo_set_source_rgb (cr, 1, 0, 0);
+ cairo_set_line_width (cr, 10);
+ cairo_move_to (cr, 0, 40 + y_offset);
+ cairo_line_to (cr, allocation.width, 40 + y_offset);
+ cairo_move_to (cr, 0, allocation.height - 40 + y_offset);
+ cairo_line_to (cr, allocation.width, allocation.height - 40 + y_offset);
+ cairo_move_to (cr, 40 + x_offset, 0);
+ cairo_line_to (cr, 40 + x_offset, allocation.height);
+ cairo_move_to (cr, allocation.width - 40 + x_offset, 0);
+ cairo_line_to (cr, allocation.width - 40 + x_offset, allocation.height);
+ cairo_stroke (cr);
+
+ info->exposed = TRUE;
+
+ if (info->exposed && info->mapped && info->pending)
+ {
+ info->pending = FALSE;
+ check_finish_wait_windows ();
+ }
+
+ return FALSE;
+}
+
+static gboolean
+tick_callback (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer user_data)
+{
+ WindowInfo *info = user_data;
+
+ if (info->start_time < 0)
+ info->start_time = info->time = gdk_frame_clock_get_frame_time (frame_clock);
+ else
+ info->time = gdk_frame_clock_get_frame_time (frame_clock);
+
+ gtk_widget_queue_draw (widget);
+
+ return TRUE;
+}
+
+static void
+create_window (int width,
+ int height,
+ gboolean alpha,
+ gboolean maximized,
+ gboolean redraws)
+{
+ WindowInfo *info;
+ GtkWidget *child;
+
+ info = g_new0 (WindowInfo, 1);
+ info->width = width;
+ info->height = height;
+ info->alpha = alpha;
+ info->maximized = maximized;
+ info->redraws = redraws;
+ info->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ if (alpha)
+ gtk_widget_set_visual (info->window, gdk_screen_get_rgba_visual (gdk_screen_get_default ()));
+ if (maximized)
+ gtk_window_maximize (GTK_WINDOW (info->window));
+ info->pending = TRUE;
+ info->start_time = -1;
+
+ child = g_object_new (GTK_TYPE_BOX, "visible", TRUE, "app-paintable", TRUE, NULL);
+ gtk_container_add (GTK_CONTAINER (info->window), child);
+
+ gtk_widget_set_size_request (info->window, width, height);
+ gtk_widget_set_app_paintable (info->window, TRUE);
+ g_signal_connect (info->window, "map-event", G_CALLBACK (on_window_map_event), info);
+ g_signal_connect (child, "draw", G_CALLBACK (on_child_draw), info);
+ gtk_widget_show (info->window);
+
+ if (info->redraws)
+ gtk_widget_add_tick_callback (info->window, tick_callback,
+ info, NULL);
+
+ our_windows = g_list_prepend (our_windows, info);
+}
+
+static void
+finish_wait_windows (void)
+{
+ GList *l;
+
+ for (l = wait_windows_invocations; l; l = l->next)
+ g_dbus_method_invocation_return_value (l->data, NULL);
+
+ g_list_free (wait_windows_invocations);
+ wait_windows_invocations = NULL;
+}
+
+static void
+check_finish_wait_windows (void)
+{
+ GList *l;
+ gboolean have_pending = FALSE;
+
+ for (l = our_windows; l; l = l->next)
+ {
+ WindowInfo *info = l->data;
+ if (info->pending)
+ have_pending = TRUE;
+ }
+
+ if (!have_pending)
+ finish_wait_windows ();
+}
+
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ /* Push off the idle timeout */
+ establish_timeout ();
+
+ if (g_strcmp0 (method_name, "Exit") == 0)
+ {
+ destroy_windows ();
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ g_dbus_connection_flush_sync (connection, NULL, NULL);
+
+ gtk_main_quit ();
+ }
+ else if (g_strcmp0 (method_name, "CreateWindow") == 0)
+ {
+ int width, height;
+ gboolean alpha, maximized, redraws;
+
+ g_variant_get (parameters, "(iibbb)", &width, &height, &alpha, &maximized, &redraws);
+
+ create_window (width, height, alpha, maximized, redraws);
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
+ else if (g_strcmp0 (method_name, "WaitWindows") == 0)
+ {
+ wait_windows_invocations = g_list_prepend (wait_windows_invocations, invocation);
+ check_finish_wait_windows ();
+ }
+ else if (g_strcmp0 (method_name, "DestroyWindows") == 0)
+ {
+ destroy_windows ();
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+ handle_method_call,
+ NULL,
+ NULL
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GDBusNodeInfo *introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+
+ g_dbus_connection_register_object (connection,
+ "/org/gnome/Shell/PerfHelper",
+ introspection_data->interfaces[0],
+ &interface_vtable,
+ NULL, /* user_data */
+ NULL, /* user_data_free_func */
+ NULL); /* GError** */
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ destroy_windows ();
+ gtk_main_quit ();
+}
+
+int
+main (int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+
+ /* Since we depend on this, avoid the possibility of lt-gnome-shell-perf-helper */
+ g_set_prgname ("gnome-shell-perf-helper");
+
+ context = g_option_context_new (" - server to create windows for performance testing");
+ g_option_context_add_main_entries (context, opt_entries, NULL);
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_print ("option parsing failed: %s\n", error->message);
+ return 1;
+ }
+
+ g_bus_own_name (G_BUS_TYPE_SESSION,
+ BUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+ G_BUS_NAME_OWNER_FLAGS_REPLACE,
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ NULL,
+ NULL);
+
+ establish_timeout ();
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/src/shell-perf-log.c b/src/shell-perf-log.c
new file mode 100644
index 0000000..3bd5228
--- /dev/null
+++ b/src/shell-perf-log.c
@@ -0,0 +1,959 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "shell-perf-log.h"
+
+typedef struct _ShellPerfEvent ShellPerfEvent;
+typedef struct _ShellPerfStatistic ShellPerfStatistic;
+typedef struct _ShellPerfStatisticsClosure ShellPerfStatisticsClosure;
+typedef union _ShellPerfStatisticValue ShellPerfStatisticValue;
+typedef struct _ShellPerfBlock ShellPerfBlock;
+
+/**
+ * SECTION:shell-perf-log
+ * @short_description: Event recorder for performance measurement
+ *
+ * ShellPerfLog provides a way for different parts of the code to
+ * record information for subsequent analysis and interactive
+ * exploration. Events exist of a timestamp, an event ID, and
+ * arguments to the event.
+ *
+ * Emphasis is placed on storing recorded events in a compact
+ * fashion so log recording disturbs the execution of the program
+ * as little as possible, however events should not be recorded
+ * at too fine a granularity - an event that is recorded once
+ * per frame or once per user action is appropriate, an event that
+ * occurs many times per frame is not.
+ *
+ * Arguments are identified by a D-Bus style signature; at the moment
+ * only a limited number of event signatures are supported to
+ * simplify the code.
+ */
+struct _ShellPerfLog
+{
+ GObject parent;
+
+ GPtrArray *events;
+ GHashTable *events_by_name;
+ GPtrArray *statistics;
+ GHashTable *statistics_by_name;
+
+ GPtrArray *statistics_closures;
+
+ GQueue *blocks;
+
+ gint64 start_time;
+ gint64 last_time;
+
+ guint statistics_timeout_id;
+
+ guint enabled : 1;
+};
+
+struct _ShellPerfEvent
+{
+ guint16 id;
+ char *name;
+ char *description;
+ char *signature;
+};
+
+union _ShellPerfStatisticValue
+{
+ int i;
+ gint64 x;
+};
+
+struct _ShellPerfStatistic
+{
+ ShellPerfEvent *event;
+
+ ShellPerfStatisticValue current_value;
+ ShellPerfStatisticValue last_value;
+
+ guint initialized : 1;
+ guint recorded : 1;
+};
+
+struct _ShellPerfStatisticsClosure
+{
+ ShellPerfStatisticsCallback callback;
+ gpointer user_data;
+ GDestroyNotify notify;
+};
+
+/* The events in the log are stored in a linked list of fixed size
+ * blocks.
+ *
+ * Note that the power-of-two nature of BLOCK_SIZE here is superficial
+ * since the allocated block has the 'bytes' field and malloc
+ * overhead. The current value is well below the size that will
+ * typically be independently mmapped by the malloc implementation so
+ * it doesn't matter. If we switched to mmapping blocks manually
+ * (perhaps to avoid polluting malloc statistics), we'd want to use a
+ * different value of BLOCK_SIZE.
+ */
+#define BLOCK_SIZE 8192
+
+struct _ShellPerfBlock
+{
+ guint32 bytes;
+ guchar buffer[BLOCK_SIZE];
+};
+
+/* Number of milliseconds between periodic statistics collection when
+ * events are enabled. Statistics collection can also be explicitly
+ * triggered.
+ */
+#define STATISTIC_COLLECTION_INTERVAL_MS 5000
+
+/* Builtin events */
+enum {
+ EVENT_SET_TIME,
+ EVENT_STATISTICS_COLLECTED
+};
+
+G_DEFINE_TYPE(ShellPerfLog, shell_perf_log, G_TYPE_OBJECT);
+
+static gint64
+get_time (void)
+{
+ return g_get_monotonic_time ();
+}
+
+static void
+shell_perf_log_init (ShellPerfLog *perf_log)
+{
+ perf_log->events = g_ptr_array_new ();
+ perf_log->events_by_name = g_hash_table_new (g_str_hash, g_str_equal);
+ perf_log->statistics = g_ptr_array_new ();
+ perf_log->statistics_by_name = g_hash_table_new (g_str_hash, g_str_equal);
+ perf_log->statistics_closures = g_ptr_array_new ();
+ perf_log->blocks = g_queue_new ();
+
+ /* This event is used when timestamp deltas are greater than
+ * fits in a gint32. 0xffffffff microseconds is about 70 minutes, so this
+ * is not going to happen in normal usage. It might happen if performance
+ * logging is enabled some time after starting the shell */
+ shell_perf_log_define_event (perf_log, "perf.setTime", "", "x");
+ g_assert (perf_log->events->len == EVENT_SET_TIME + 1);
+
+ /* The purpose of this event is to allow us to optimize out storing
+ * statistics that haven't changed. We want to mark every time we
+ * collect statistics even if we don't record any individual
+ * statistics so that we can distinguish sudden changes from gradual changes.
+ *
+ * The argument is the number of microseconds that statistics collection
+ * took; we record that since statistics collection could start taking
+ * significant time if we do things like grub around in /proc/
+ */
+ shell_perf_log_define_event (perf_log, "perf.statisticsCollected",
+ "Finished collecting statistics",
+ "x");
+ g_assert (perf_log->events->len == EVENT_STATISTICS_COLLECTED + 1);
+
+ perf_log->start_time = perf_log->last_time = get_time();
+}
+
+static void
+shell_perf_log_class_init (ShellPerfLogClass *class)
+{
+}
+
+/**
+ * shell_perf_log_get_default:
+ *
+ * Gets the global singleton performance log. This is initially disabled
+ * and must be explicitly enabled with shell_perf_log_set_enabled().
+ *
+ * Return value: (transfer none): the global singleton performance log
+ */
+ShellPerfLog *
+shell_perf_log_get_default (void)
+{
+ static ShellPerfLog *perf_log;
+
+ if (perf_log == NULL)
+ perf_log = g_object_new (SHELL_TYPE_PERF_LOG, NULL);
+
+ return perf_log;
+}
+
+static gboolean
+statistics_timeout (gpointer data)
+{
+ ShellPerfLog *perf_log = data;
+
+ shell_perf_log_collect_statistics (perf_log);
+
+ return TRUE;
+}
+
+/**
+ * shell_perf_log_set_enabled:
+ * @perf_log: a #ShellPerfLog
+ * @enabled: whether to record events
+ *
+ * Sets whether events are currently being recorded.
+ */
+void
+shell_perf_log_set_enabled (ShellPerfLog *perf_log,
+ gboolean enabled)
+{
+ enabled = enabled != FALSE;
+
+ if (enabled != perf_log->enabled)
+ {
+ perf_log->enabled = enabled;
+
+ if (enabled)
+ {
+ perf_log->statistics_timeout_id = g_timeout_add (STATISTIC_COLLECTION_INTERVAL_MS,
+ statistics_timeout,
+ perf_log);
+ g_source_set_name_by_id (perf_log->statistics_timeout_id, "[gnome-shell] statistics_timeout");
+ }
+ else
+ {
+ g_clear_handle_id (&perf_log->statistics_timeout_id, g_source_remove);
+ }
+ }
+}
+
+static ShellPerfEvent *
+define_event (ShellPerfLog *perf_log,
+ const char *name,
+ const char *description,
+ const char *signature)
+{
+ ShellPerfEvent *event;
+
+ if (strcmp (signature, "") != 0 &&
+ strcmp (signature, "s") != 0 &&
+ strcmp (signature, "i") != 0 &&
+ strcmp (signature, "x") != 0)
+ {
+ g_warning ("Only supported event signatures are '', 's', 'i', and 'x'\n");
+ return NULL;
+ }
+
+ if (perf_log->events->len == 65536)
+ {
+ g_warning ("Maximum number of events defined\n");
+ return NULL;
+ }
+
+ /* We could do stricter validation, but this will break our JSON dumps */
+ if (strchr (name, '"') != NULL)
+ {
+ g_warning ("Event names can't include '\"'");
+ return NULL;
+ }
+
+ if (g_hash_table_lookup (perf_log->events_by_name, name) != NULL)
+ {
+ g_warning ("Duplicate event event for '%s'\n", name);
+ return NULL;
+ }
+
+ event = g_new (ShellPerfEvent, 1);
+
+ event->id = perf_log->events->len;
+ event->name = g_strdup (name);
+ event->signature = g_strdup (signature);
+ event->description = g_strdup (description);
+
+ g_ptr_array_add (perf_log->events, event);
+ g_hash_table_insert (perf_log->events_by_name, event->name, event);
+
+ return event;
+}
+
+/**
+ * shell_perf_log_define_event:
+ * @perf_log: a #ShellPerfLog
+ * @name: name of the event. This should of the form
+ * '<namespace>.<specific eventf'>, for example
+ * 'clutter.stagePaintDone'.
+ * @description: human readable description of the event.
+ * @signature: signature defining the arguments that event takes.
+ * This is a string of type characters, using the same characters
+ * as D-Bus or GVariant. Only a very limited number of signatures
+ * are supported: , '', 's', 'i', and 'x'. This mean respectively:
+ * no arguments, one string, one 32-bit integer, and one 64-bit
+ * integer.
+ *
+ * Defines a performance event for later recording.
+ */
+void
+shell_perf_log_define_event (ShellPerfLog *perf_log,
+ const char *name,
+ const char *description,
+ const char *signature)
+{
+ define_event (perf_log, name, description, signature);
+}
+
+static ShellPerfEvent *
+lookup_event (ShellPerfLog *perf_log,
+ const char *name,
+ const char *signature)
+{
+ ShellPerfEvent *event = g_hash_table_lookup (perf_log->events_by_name, name);
+
+ if (G_UNLIKELY (event == NULL))
+ {
+ g_warning ("Discarding unknown event '%s'\n", name);
+ return NULL;
+ }
+
+ if (G_UNLIKELY (strcmp (event->signature, signature) != 0))
+ {
+ g_warning ("Event '%s'; defined with signature '%s', used with '%s'\n",
+ name, event->signature, signature);
+ return NULL;
+ }
+
+ return event;
+}
+
+static void
+record_event (ShellPerfLog *perf_log,
+ gint64 event_time,
+ ShellPerfEvent *event,
+ const guchar *bytes,
+ size_t bytes_len)
+{
+ ShellPerfBlock *block;
+ size_t total_bytes;
+ guint32 time_delta;
+ guint32 pos;
+
+ if (!perf_log->enabled)
+ return;
+
+ total_bytes = sizeof (gint32) + sizeof (gint16) + bytes_len;
+ if (G_UNLIKELY (bytes_len > BLOCK_SIZE || total_bytes > BLOCK_SIZE))
+ {
+ g_warning ("Discarding oversize event '%s'\n", event->name);
+ return;
+ }
+
+ if (event_time > perf_log->last_time + G_GINT64_CONSTANT(0xffffffff))
+ {
+ perf_log->last_time = event_time;
+ record_event (perf_log, event_time,
+ lookup_event (perf_log, "perf.setTime", "x"),
+ (const guchar *)&event_time, sizeof(gint64));
+ time_delta = 0;
+ }
+ else if (event_time < perf_log->last_time)
+ time_delta = 0;
+ else
+ time_delta = (guint32)(event_time - perf_log->last_time);
+
+ perf_log->last_time = event_time;
+
+ if (perf_log->blocks->tail == NULL ||
+ total_bytes + ((ShellPerfBlock *)perf_log->blocks->tail->data)->bytes > BLOCK_SIZE)
+ {
+ block = g_new (ShellPerfBlock, 1);
+ block->bytes = 0;
+ g_queue_push_tail (perf_log->blocks, block);
+ }
+ else
+ {
+ block = (ShellPerfBlock *)perf_log->blocks->tail->data;
+ }
+
+ pos = block->bytes;
+
+ memcpy (block->buffer + pos, &time_delta, sizeof (guint32));
+ pos += sizeof (guint32);
+ memcpy (block->buffer + pos, &event->id, sizeof (guint16));
+ pos += sizeof (guint16);
+ memcpy (block->buffer + pos, bytes, bytes_len);
+ pos += bytes_len;
+
+ block->bytes = pos;
+}
+
+/**
+ * shell_perf_log_event:
+ * @perf_log: a #ShellPerfLog
+ * @name: name of the event
+ *
+ * Records a performance event with no arguments.
+ */
+void
+shell_perf_log_event (ShellPerfLog *perf_log,
+ const char *name)
+{
+ ShellPerfEvent *event = lookup_event (perf_log, name, "");
+ if (G_UNLIKELY (event == NULL))
+ return;
+
+ record_event (perf_log, get_time(), event, NULL, 0);
+}
+
+/**
+ * shell_perf_log_event_i:
+ * @perf_log: a #ShellPerfLog
+ * @name: name of the event
+ * @arg: the argument
+ *
+ * Records a performance event with one 32-bit integer argument.
+ */
+void
+shell_perf_log_event_i (ShellPerfLog *perf_log,
+ const char *name,
+ gint32 arg)
+{
+ ShellPerfEvent *event = lookup_event (perf_log, name, "i");
+ if (G_UNLIKELY (event == NULL))
+ return;
+
+ record_event (perf_log, get_time(), event,
+ (const guchar *)&arg, sizeof (arg));
+}
+
+/**
+ * shell_perf_log_event_x:
+ * @perf_log: a #ShellPerfLog
+ * @name: name of the event
+ * @arg: the argument
+ *
+ * Records a performance event with one 64-bit integer argument.
+ */
+void
+shell_perf_log_event_x (ShellPerfLog *perf_log,
+ const char *name,
+ gint64 arg)
+{
+ ShellPerfEvent *event = lookup_event (perf_log, name, "x");
+ if (G_UNLIKELY (event == NULL))
+ return;
+
+ record_event (perf_log, get_time(), event,
+ (const guchar *)&arg, sizeof (arg));
+}
+
+/**
+ * shell_perf_log_event_s:
+ * @perf_log: a #ShellPerfLog
+ * @name: name of the event
+ * @arg: the argument
+ *
+ * Records a performance event with one string argument.
+ */
+void
+shell_perf_log_event_s (ShellPerfLog *perf_log,
+ const char *name,
+ const char *arg)
+{
+ ShellPerfEvent *event = lookup_event (perf_log, name, "s");
+ if (G_UNLIKELY (event == NULL))
+ return;
+
+ record_event (perf_log, get_time(), event,
+ (const guchar *)arg, strlen (arg) + 1);
+}
+
+/**
+ * shell_perf_log_define_statistic:
+ * @name: name of the statistic and of the corresponding event.
+ * This should follow the same guidelines as for shell_perf_log_define_event()
+ * @description: human readable description of the statistic.
+ * @signature: The type of the data stored for statistic. Must
+ * currently be 'i' or 'x'.
+ *
+ * Defines a statistic. A statistic is a numeric value that is stored
+ * by the performance log and recorded periodically or when
+ * shell_perf_log_collect_statistics() is called explicitly.
+ *
+ * Code that defines a statistic should update it by calling
+ * the update function for the particular data type of the statistic,
+ * such as shell_perf_log_update_statistic_i(). This can be done
+ * at any time, but would normally done inside a function registered
+ * with shell_perf_log_add_statistics_callback(). These functions
+ * are called immediately before statistics are recorded.
+ */
+void
+shell_perf_log_define_statistic (ShellPerfLog *perf_log,
+ const char *name,
+ const char *description,
+ const char *signature)
+{
+ ShellPerfEvent *event;
+ ShellPerfStatistic *statistic;
+
+ if (strcmp (signature, "i") != 0 &&
+ strcmp (signature, "x") != 0)
+ {
+ g_warning ("Only supported statistic signatures are 'i' and 'x'\n");
+ return;
+ }
+
+ event = define_event (perf_log, name, description, signature);
+ if (event == NULL)
+ return;
+
+ statistic = g_new (ShellPerfStatistic, 1);
+ statistic->event = event;
+
+ statistic->initialized = FALSE;
+ statistic->recorded = FALSE;
+
+ g_ptr_array_add (perf_log->statistics, statistic);
+ g_hash_table_insert (perf_log->statistics_by_name, event->name, statistic);
+}
+
+static ShellPerfStatistic *
+lookup_statistic (ShellPerfLog *perf_log,
+ const char *name,
+ const char *signature)
+{
+ ShellPerfStatistic *statistic = g_hash_table_lookup (perf_log->statistics_by_name, name);
+
+ if (G_UNLIKELY (statistic == NULL))
+ {
+ g_warning ("Unknown statistic '%s'\n", name);
+ return NULL;
+ }
+
+ if (G_UNLIKELY (strcmp (statistic->event->signature, signature) != 0))
+ {
+ g_warning ("Statistic '%s'; defined with signature '%s', used with '%s'\n",
+ name, statistic->event->signature, signature);
+ return NULL;
+ }
+
+ return statistic;
+}
+
+/**
+ * shell_perf_log_update_statistic_i:
+ * @perf_log: a #ShellPerfLog
+ * @name: name of the statistic
+ * @value: new value for the statistic
+ *
+ * Updates the current value of an 32-bit integer statistic.
+ */
+void
+shell_perf_log_update_statistic_i (ShellPerfLog *perf_log,
+ const char *name,
+ gint32 value)
+{
+ ShellPerfStatistic *statistic;
+
+ statistic = lookup_statistic (perf_log, name, "i");
+ if (G_UNLIKELY (statistic == NULL))
+ return;
+
+ statistic->current_value.i = value;
+ statistic->initialized = TRUE;
+}
+
+/**
+ * shell_perf_log_update_statistic_x:
+ * @perf_log: a #ShellPerfLog
+ * @name: name of the statistic
+ * @value: new value for the statistic
+ *
+ * Updates the current value of an 64-bit integer statistic.
+ */
+void
+shell_perf_log_update_statistic_x (ShellPerfLog *perf_log,
+ const char *name,
+ gint64 value)
+{
+ ShellPerfStatistic *statistic;
+
+ statistic = lookup_statistic (perf_log, name, "x");
+ if (G_UNLIKELY (statistic == NULL))
+ return;
+
+ statistic->current_value.x = value;
+ statistic->initialized = TRUE;
+}
+
+/**
+ * shell_perf_log_add_statistics_callback:
+ * @perf_log: a #ShellPerfLog
+ * @callback: function to call before recording statistics
+ * @user_data: data to pass to @callback
+ * @notify: function to call when @user_data is no longer needed
+ *
+ * Adds a function that will be called before statistics are recorded.
+ * The function would typically compute one or more statistics values
+ * and call a function such as shell_perf_log_update_statistic_i()
+ * to update the value that will be recorded.
+ */
+void
+shell_perf_log_add_statistics_callback (ShellPerfLog *perf_log,
+ ShellPerfStatisticsCallback callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ ShellPerfStatisticsClosure *closure = g_new (ShellPerfStatisticsClosure, 1);
+
+ closure->callback = callback;
+ closure->user_data = user_data;
+ closure->notify = notify;
+
+ g_ptr_array_add (perf_log->statistics_closures, closure);
+}
+
+/**
+ * shell_perf_log_collect_statistics:
+ * @perf_log: a #ShellPerfLog
+ *
+ * Calls all the update functions added with
+ * shell_perf_log_add_statistics_callback() and then records events
+ * for all statistics, followed by a perf.statisticsCollected event.
+ */
+void
+shell_perf_log_collect_statistics (ShellPerfLog *perf_log)
+{
+ gint64 event_time = get_time ();
+ gint64 collection_time;
+ guint i;
+
+ if (!perf_log->enabled)
+ return;
+
+ for (i = 0; i < perf_log->statistics_closures->len; i++)
+ {
+ ShellPerfStatisticsClosure *closure;
+
+ closure = g_ptr_array_index (perf_log->statistics_closures, i);
+ closure->callback (perf_log, closure->user_data);
+ }
+
+ collection_time = get_time() - event_time;
+
+ for (i = 0; i < perf_log->statistics->len; i++)
+ {
+ ShellPerfStatistic *statistic = g_ptr_array_index (perf_log->statistics, i);
+
+ if (!statistic->initialized)
+ continue;
+
+ switch (statistic->event->signature[0])
+ {
+ case 'i':
+ if (!statistic->recorded ||
+ statistic->current_value.i != statistic->last_value.i)
+ {
+ record_event (perf_log, event_time, statistic->event,
+ (const guchar *)&statistic->current_value.i,
+ sizeof (gint32));
+ statistic->last_value.i = statistic->current_value.i;
+ statistic->recorded = TRUE;
+ }
+ break;
+ case 'x':
+ if (!statistic->recorded ||
+ statistic->current_value.x != statistic->last_value.x)
+ {
+ record_event (perf_log, event_time, statistic->event,
+ (const guchar *)&statistic->current_value.x,
+ sizeof (gint64));
+ statistic->last_value.x = statistic->current_value.x;
+ statistic->recorded = TRUE;
+ }
+ break;
+ default:
+ g_warning ("Unsupported signature in event");
+ break;
+ }
+ }
+
+ record_event (perf_log, event_time,
+ g_ptr_array_index (perf_log->events, EVENT_STATISTICS_COLLECTED),
+ (const guchar *)&collection_time, sizeof (gint64));
+}
+
+/**
+ * shell_perf_log_replay:
+ * @perf_log: a #ShellPerfLog
+ * @replay_function: (scope call): function to call for each event in the log
+ * @user_data: data to pass to @replay_function
+ *
+ * Replays the log by calling the given function for each event
+ * in the log.
+ */
+void
+shell_perf_log_replay (ShellPerfLog *perf_log,
+ ShellPerfReplayFunction replay_function,
+ gpointer user_data)
+{
+ gint64 event_time = perf_log->start_time;
+ GList *iter;
+
+ for (iter = perf_log->blocks->head; iter; iter = iter->next)
+ {
+ ShellPerfBlock *block = iter->data;
+ guint32 pos = 0;
+
+ while (pos < block->bytes)
+ {
+ ShellPerfEvent *event;
+ guint16 id;
+ guint32 time_delta;
+ GValue arg = { 0, };
+
+ memcpy (&time_delta, block->buffer + pos, sizeof (guint32));
+ pos += sizeof (guint32);
+ memcpy (&id, block->buffer + pos, sizeof (guint16));
+ pos += sizeof (guint16);
+
+ if (id == EVENT_SET_TIME)
+ {
+ /* Internal, we don't include in the replay */
+ memcpy (&event_time, block->buffer + pos, sizeof (gint64));
+ pos += sizeof (gint64);
+ continue;
+ }
+ else
+ {
+ event_time += time_delta;
+ }
+
+ event = g_ptr_array_index (perf_log->events, id);
+
+ if (strcmp (event->signature, "") == 0)
+ {
+ /* We need to pass something, so pass an empty string */
+ g_value_init (&arg, G_TYPE_STRING);
+ }
+ else if (strcmp (event->signature, "i") == 0)
+ {
+ gint32 l;
+
+ memcpy (&l, block->buffer + pos, sizeof (gint32));
+ pos += sizeof (gint32);
+
+ g_value_init (&arg, G_TYPE_INT);
+ g_value_set_int (&arg, l);
+ }
+ else if (strcmp (event->signature, "x") == 0)
+ {
+ gint64 l;
+
+ memcpy (&l, block->buffer + pos, sizeof (gint64));
+ pos += sizeof (gint64);
+
+ g_value_init (&arg, G_TYPE_INT64);
+ g_value_set_int64 (&arg, l);
+ }
+ else if (strcmp (event->signature, "s") == 0)
+ {
+ g_value_init (&arg, G_TYPE_STRING);
+ g_value_set_string (&arg, (char *)block->buffer + pos);
+ pos += strlen ((char *)(block->buffer + pos)) + 1;
+ }
+
+ replay_function (event_time, event->name, event->signature, &arg, user_data);
+ g_value_unset (&arg);
+ }
+ }
+}
+
+static char *
+escape_quotes (const char *input)
+{
+ GString *result;
+ const char *p;
+
+ if (strchr (input, '"') == NULL)
+ return (char *)input;
+
+ result = g_string_new (NULL);
+ for (p = input; *p; p++)
+ {
+ if (*p == '"')
+ g_string_append (result, "\\\"");
+ else
+ g_string_append_c (result, *p);
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+static gboolean
+write_string (GOutputStream *out,
+ const char *str,
+ GError **error)
+{
+ return g_output_stream_write_all (out, str, strlen (str),
+ NULL, NULL,
+ error);
+}
+
+/**
+ * shell_perf_log_dump_events:
+ * @perf_log: a #ShellPerfLog
+ * @out: output stream into which to write the event definitions
+ * @error: location to store #GError, or %NULL
+ *
+ * Dump the definition of currently defined events and statistics, formatted
+ * as JSON, to the specified output stream. The JSON output is an array,
+ * with each element being a dictionary of the form:
+ *
+ * { name: <name of event>,
+ * description: <description of string,
+ * statistic: true } (only for statistics)
+ *
+ * Return value: %TRUE if the dump succeeded. %FALSE if an IO error occurred
+ */
+gboolean
+shell_perf_log_dump_events (ShellPerfLog *perf_log,
+ GOutputStream *out,
+ GError **error)
+{
+ GString *output;
+ guint i;
+
+ output = g_string_new (NULL);
+ g_string_append (output, "[ ");
+
+ for (i = 0; i < perf_log->events->len; i++)
+ {
+ ShellPerfEvent *event = g_ptr_array_index (perf_log->events, i);
+ char *escaped_description = escape_quotes (event->description);
+ gboolean is_statistic = g_hash_table_lookup (perf_log->statistics_by_name, event->name) != NULL;
+
+ if (i != 0)
+ g_string_append (output, ",\n ");
+
+ g_string_append_printf (output,
+ "{ \"name\": \"%s\",\n"
+ " \"description\": \"%s\"",
+ event->name, escaped_description);
+ if (is_statistic)
+ g_string_append (output, ",\n \"statistic\": true");
+
+ g_string_append (output, " }");
+
+ if (escaped_description != event->description)
+ g_free (escaped_description);
+ }
+
+ g_string_append (output, " ]");
+
+ return write_string (out, g_string_free (output, FALSE), error);
+}
+
+typedef struct {
+ GOutputStream *out;
+ GError *error;
+ gboolean first;
+} ReplayToJsonClosure;
+
+static void
+replay_to_json (gint64 time,
+ const char *name,
+ const char *signature,
+ GValue *arg,
+ gpointer user_data)
+{
+ ReplayToJsonClosure *closure = user_data;
+ g_autofree char *event_str = NULL;
+
+ if (closure->error != NULL)
+ return;
+
+ if (!closure->first)
+ {
+ if (!write_string (closure->out, ",\n ", &closure->error))
+ return;
+ }
+
+ closure->first = FALSE;
+
+ if (strcmp (signature, "") == 0)
+ {
+ event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\"]", time, name);
+ }
+ else if (strcmp (signature, "i") == 0)
+ {
+ event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\", %i]",
+ time,
+ name,
+ g_value_get_int (arg));
+ }
+ else if (strcmp (signature, "x") == 0)
+ {
+ event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\", %"G_GINT64_FORMAT "]",
+ time,
+ name,
+ g_value_get_int64 (arg));
+ }
+ else if (strcmp (signature, "s") == 0)
+ {
+ const char *arg_str = g_value_get_string (arg);
+ char *escaped = escape_quotes (arg_str);
+
+ event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\", \"%s\"]",
+ time,
+ name,
+ g_value_get_string (arg));
+
+ if (escaped != arg_str)
+ g_free (escaped);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ if (!write_string (closure->out, event_str, &closure->error))
+ return;
+}
+
+/**
+ * shell_perf_log_dump_log:
+ * @perf_log: a #ShellPerfLog
+ * @out: output stream into which to write the event log
+ * @error: location to store #GError, or %NULL
+ *
+ * Writes the performance event log, formatted as JSON, to the specified
+ * output stream. For performance reasons, the output stream passed
+ * in should generally be a buffered (or memory) output stream, since
+ * it will be written to in small pieces. The JSON output is an array
+ * with the elements of the array also being arrays, of the form
+ * '[' <time>, <event name> [, <event_arg>... ] ']'.
+ *
+ * Return value: %TRUE if the dump succeeded. %FALSE if an IO error occurred
+ */
+gboolean
+shell_perf_log_dump_log (ShellPerfLog *perf_log,
+ GOutputStream *out,
+ GError **error)
+{
+ ReplayToJsonClosure closure;
+
+ closure.out = out;
+ closure.error = NULL;
+ closure.first = TRUE;
+
+ if (!write_string (out, "[ ", &closure.error))
+ return FALSE;
+
+ shell_perf_log_replay (perf_log, replay_to_json, &closure);
+
+ if (closure.error != NULL)
+ {
+ g_propagate_error (error, closure.error);
+ return FALSE;
+ }
+
+ if (!write_string (out, " ]", &closure.error))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/shell-perf-log.h b/src/shell-perf-log.h
new file mode 100644
index 0000000..45d07a5
--- /dev/null
+++ b/src/shell-perf-log.h
@@ -0,0 +1,75 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_PERF_LOG_H__
+#define __SHELL_PERF_LOG_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_PERF_LOG (shell_perf_log_get_type ())
+G_DECLARE_FINAL_TYPE (ShellPerfLog, shell_perf_log, SHELL, PERF_LOG, GObject)
+
+ShellPerfLog *shell_perf_log_get_default (void);
+
+void shell_perf_log_set_enabled (ShellPerfLog *perf_log,
+ gboolean enabled);
+
+void shell_perf_log_define_event (ShellPerfLog *perf_log,
+ const char *name,
+ const char *description,
+ const char *signature);
+void shell_perf_log_event (ShellPerfLog *perf_log,
+ const char *name);
+void shell_perf_log_event_i (ShellPerfLog *perf_log,
+ const char *name,
+ gint32 arg);
+void shell_perf_log_event_x (ShellPerfLog *perf_log,
+ const char *name,
+ gint64 arg);
+void shell_perf_log_event_s (ShellPerfLog *perf_log,
+ const char *name,
+ const char *arg);
+
+void shell_perf_log_define_statistic (ShellPerfLog *perf_log,
+ const char *name,
+ const char *description,
+ const char *signature);
+
+void shell_perf_log_update_statistic_i (ShellPerfLog *perf_log,
+ const char *name,
+ int value);
+void shell_perf_log_update_statistic_x (ShellPerfLog *perf_log,
+ const char *name,
+ gint64 value);
+
+typedef void (*ShellPerfStatisticsCallback) (ShellPerfLog *perf_log,
+ gpointer data);
+
+void shell_perf_log_add_statistics_callback (ShellPerfLog *perf_log,
+ ShellPerfStatisticsCallback callback,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+void shell_perf_log_collect_statistics (ShellPerfLog *perf_log);
+
+typedef void (*ShellPerfReplayFunction) (gint64 time,
+ const char *name,
+ const char *signature,
+ GValue *arg,
+ gpointer user_data);
+
+void shell_perf_log_replay (ShellPerfLog *perf_log,
+ ShellPerfReplayFunction replay_function,
+ gpointer user_data);
+
+gboolean shell_perf_log_dump_events (ShellPerfLog *perf_log,
+ GOutputStream *out,
+ GError **error);
+gboolean shell_perf_log_dump_log (ShellPerfLog *perf_log,
+ GOutputStream *out,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __SHELL_PERF_LOG_H__ */
diff --git a/src/shell-polkit-authentication-agent.c b/src/shell-polkit-authentication-agent.c
new file mode 100644
index 0000000..6815f01
--- /dev/null
+++ b/src/shell-polkit-authentication-agent.c
@@ -0,0 +1,429 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 20011 Red Hat, Inc.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+
+#include <pwd.h>
+
+#include "shell-polkit-authentication-agent.h"
+
+#include <glib/gi18n.h>
+
+/* uncomment for useful debug output */
+/* #define SHOW_DEBUG */
+
+#ifdef SHOW_DEBUG
+static void
+print_debug (const gchar *format, ...)
+{
+ g_autofree char *s = NULL;
+ g_autofree char *timestamp = NULL;
+ g_autoptr (GDateTime) now = NULL;
+ va_list ap;
+
+ now = g_date_time_new_now_local ();
+ timestamp = g_date_time_format (now, "%H:%M:%S");
+
+ va_start (ap, format);
+ s = g_strdup_vprintf (format, ap);
+ va_end (ap);
+
+ g_print ("ShellPolkitAuthenticationAgent: %s.%03d: %s\n",
+ timestamp, g_date_time_get_microsecond (now), s);
+}
+#else
+static void
+print_debug (const gchar *str, ...)
+{
+}
+#endif
+
+struct _AuthRequest;
+typedef struct _AuthRequest AuthRequest;
+
+struct _ShellPolkitAuthenticationAgent {
+ PolkitAgentListener parent_instance;
+
+ GList *scheduled_requests;
+ AuthRequest *current_request;
+
+ gpointer handle;
+};
+
+/* Signals */
+enum
+{
+ INITIATE_SIGNAL,
+ CANCEL_SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (ShellPolkitAuthenticationAgent, shell_polkit_authentication_agent, POLKIT_AGENT_TYPE_LISTENER);
+
+static void initiate_authentication (PolkitAgentListener *listener,
+ const gchar *action_id,
+ const gchar *message,
+ const gchar *icon_name,
+ PolkitDetails *details,
+ const gchar *cookie,
+ GList *identities,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+static gboolean initiate_authentication_finish (PolkitAgentListener *listener,
+ GAsyncResult *res,
+ GError **error);
+
+void
+shell_polkit_authentication_agent_init (ShellPolkitAuthenticationAgent *agent)
+{
+}
+
+void
+shell_polkit_authentication_agent_register (ShellPolkitAuthenticationAgent *agent,
+ GError **error_out)
+{
+ GError *error = NULL;
+ PolkitSubject *subject;
+
+ subject = polkit_unix_session_new_for_process_sync (getpid (),
+ NULL, /* GCancellable* */
+ &error);
+ if (subject == NULL)
+ {
+ if (error == NULL) /* polkit version 104 and older don't properly set error on failure */
+ error = g_error_new (POLKIT_ERROR, POLKIT_ERROR_FAILED,
+ "PolKit failed to properly get our session");
+ goto out;
+ }
+
+ agent->handle = polkit_agent_listener_register (POLKIT_AGENT_LISTENER (agent),
+ POLKIT_AGENT_REGISTER_FLAGS_NONE,
+ subject,
+ NULL, /* use default object path */
+ NULL, /* GCancellable */
+ &error);
+
+ out:
+ if (error != NULL)
+ g_propagate_error (error_out, error);
+
+ if (subject != NULL)
+ g_object_unref (subject);
+}
+
+static void
+shell_polkit_authentication_agent_finalize (GObject *object)
+{
+ ShellPolkitAuthenticationAgent *agent = SHELL_POLKIT_AUTHENTICATION_AGENT (object);
+ shell_polkit_authentication_agent_unregister (agent);
+ G_OBJECT_CLASS (shell_polkit_authentication_agent_parent_class)->finalize (object);
+}
+
+static void
+shell_polkit_authentication_agent_class_init (ShellPolkitAuthenticationAgentClass *klass)
+{
+ GObjectClass *gobject_class;
+ PolkitAgentListenerClass *listener_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = shell_polkit_authentication_agent_finalize;
+
+ listener_class = POLKIT_AGENT_LISTENER_CLASS (klass);
+ listener_class->initiate_authentication = initiate_authentication;
+ listener_class->initiate_authentication_finish = initiate_authentication_finish;
+
+ signals[INITIATE_SIGNAL] =
+ g_signal_new ("initiate",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, /* class_offset */
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ NULL, /* marshaller */
+ G_TYPE_NONE,
+ 5,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRV);
+
+ signals[CANCEL_SIGNAL] =
+ g_signal_new ("cancel",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, /* class_offset */
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ NULL, /* marshaller */
+ G_TYPE_NONE,
+ 0);
+}
+
+ShellPolkitAuthenticationAgent *
+shell_polkit_authentication_agent_new (void)
+{
+ return SHELL_POLKIT_AUTHENTICATION_AGENT (g_object_new (SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT, NULL));
+}
+
+struct _AuthRequest {
+ /* not holding ref */
+ ShellPolkitAuthenticationAgent *agent;
+ GCancellable *cancellable;
+ gulong handler_id;
+
+ /* copies */
+ gchar *action_id;
+ gchar *message;
+ gchar *icon_name;
+ PolkitDetails *details;
+ gchar *cookie;
+ GList *identities;
+
+ GTask *simple;
+};
+
+static void
+auth_request_free (AuthRequest *request)
+{
+ g_free (request->action_id);
+ g_free (request->message);
+ g_free (request->icon_name);
+ g_object_unref (request->details);
+ g_free (request->cookie);
+ g_list_foreach (request->identities, (GFunc) g_object_unref, NULL);
+ g_list_free (request->identities);
+ g_object_unref (request->simple);
+ g_free (request);
+}
+
+static void
+auth_request_initiate (AuthRequest *request)
+{
+ gchar **user_names;
+ GPtrArray *p;
+ GList *l;
+
+ p = g_ptr_array_new ();
+ for (l = request->identities; l != NULL; l = l->next)
+ {
+ if (POLKIT_IS_UNIX_USER (l->data))
+ {
+ PolkitUnixUser *user = POLKIT_UNIX_USER (l->data);
+ gint uid;
+ gchar buf[4096];
+ struct passwd pwd;
+ struct passwd *ppwd;
+
+ uid = polkit_unix_user_get_uid (user);
+ if (getpwuid_r (uid, &pwd, buf, sizeof (buf), &ppwd) == 0)
+ {
+ if (!g_utf8_validate (pwd.pw_name, -1, NULL))
+ {
+ g_warning ("Invalid UTF-8 in username for uid %d. Skipping", uid);
+ }
+ else
+ {
+ g_ptr_array_add (p, g_strdup (pwd.pw_name));
+ }
+ }
+ else
+ {
+ g_warning ("Error looking up user name for uid %d", uid);
+ }
+ }
+ else
+ {
+ g_warning ("Unsupporting identity of GType %s", g_type_name (G_TYPE_FROM_INSTANCE (l->data)));
+ }
+ }
+ g_ptr_array_add (p, NULL);
+ user_names = (gchar **) g_ptr_array_free (p, FALSE);
+ g_signal_emit (request->agent,
+ signals[INITIATE_SIGNAL],
+ 0, /* detail */
+ request->action_id,
+ request->message,
+ request->icon_name,
+ request->cookie,
+ user_names);
+ g_strfreev (user_names);
+}
+
+static void auth_request_complete (AuthRequest *request,
+ gboolean dismissed);
+
+static gboolean
+handle_cancelled_in_idle (gpointer user_data)
+{
+ AuthRequest *request = user_data;
+
+ print_debug ("CANCELLED %s cookie %s", request->action_id, request->cookie);
+ if (request == request->agent->current_request)
+ {
+ g_signal_emit (request->agent,
+ signals[CANCEL_SIGNAL],
+ 0); /* detail */
+ }
+ else
+ {
+ auth_request_complete (request, FALSE);
+ }
+
+ return FALSE;
+}
+
+static void
+on_request_cancelled (GCancellable *cancellable,
+ gpointer user_data)
+{
+ AuthRequest *request = user_data;
+ guint id;
+
+ /* post-pone to idle to handle GCancellable deadlock in
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=642968
+ */
+ id = g_idle_add (handle_cancelled_in_idle, request);
+ g_source_set_name_by_id (id, "[gnome-shell] handle_cancelled_in_idle");
+}
+
+static void
+auth_request_dismiss (AuthRequest *request)
+{
+ auth_request_complete (request, TRUE);
+}
+
+void
+shell_polkit_authentication_agent_unregister (ShellPolkitAuthenticationAgent *agent)
+{
+ if (agent->scheduled_requests != NULL)
+ {
+ g_list_foreach (agent->scheduled_requests, (GFunc)auth_request_dismiss, NULL);
+ agent->scheduled_requests = NULL;
+ }
+ if (agent->current_request != NULL)
+ auth_request_dismiss (agent->current_request);
+
+ if (agent->handle)
+ {
+ polkit_agent_listener_unregister (agent->handle);
+ agent->handle = NULL;
+ }
+}
+
+static void maybe_process_next_request (ShellPolkitAuthenticationAgent *agent);
+
+static void
+auth_request_complete (AuthRequest *request,
+ gboolean dismissed)
+{
+ ShellPolkitAuthenticationAgent *agent = request->agent;
+ gboolean is_current = agent->current_request == request;
+
+ print_debug ("COMPLETING %s %s cookie %s", is_current ? "CURRENT" : "SCHEDULED",
+ request->action_id, request->cookie);
+
+ if (!is_current)
+ agent->scheduled_requests = g_list_remove (agent->scheduled_requests, request);
+ g_cancellable_disconnect (request->cancellable, request->handler_id);
+
+ if (dismissed)
+ g_task_return_new_error (request->simple,
+ POLKIT_ERROR,
+ POLKIT_ERROR_CANCELLED,
+ _("Authentication dialog was dismissed by the user"));
+ else
+ g_task_return_boolean (request->simple, TRUE);
+
+ auth_request_free (request);
+
+ if (is_current)
+ {
+ agent->current_request = NULL;
+ maybe_process_next_request (agent);
+ }
+}
+
+static void
+maybe_process_next_request (ShellPolkitAuthenticationAgent *agent)
+{
+ print_debug ("MAYBE_PROCESS cur=%p len(scheduled)=%d", agent->current_request, g_list_length (agent->scheduled_requests));
+
+ if (agent->current_request == NULL && agent->scheduled_requests != NULL)
+ {
+ AuthRequest *request;
+
+ request = agent->scheduled_requests->data;
+
+ agent->current_request = request;
+ agent->scheduled_requests = g_list_remove (agent->scheduled_requests, request);
+
+ print_debug ("INITIATING %s cookie %s", request->action_id, request->cookie);
+ auth_request_initiate (request);
+ }
+}
+
+static void
+initiate_authentication (PolkitAgentListener *listener,
+ const gchar *action_id,
+ const gchar *message,
+ const gchar *icon_name,
+ PolkitDetails *details,
+ const gchar *cookie,
+ GList *identities,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellPolkitAuthenticationAgent *agent = SHELL_POLKIT_AUTHENTICATION_AGENT (listener);
+ AuthRequest *request;
+
+ request = g_new0 (AuthRequest, 1);
+ request->agent = agent;
+ request->action_id = g_strdup (action_id);
+ request->message = g_strdup (message);
+ request->icon_name = g_strdup (icon_name);
+ request->details = g_object_ref (details);
+ request->cookie = g_strdup (cookie);
+ request->identities = g_list_copy (identities);
+ g_list_foreach (request->identities, (GFunc) g_object_ref, NULL);
+ request->simple = g_task_new (listener, NULL, callback, user_data);
+ request->cancellable = cancellable;
+ request->handler_id = g_cancellable_connect (request->cancellable,
+ G_CALLBACK (on_request_cancelled),
+ request,
+ NULL); /* GDestroyNotify for request */
+
+ print_debug ("SCHEDULING %s cookie %s", request->action_id, request->cookie);
+ agent->scheduled_requests = g_list_append (agent->scheduled_requests, request);
+
+ maybe_process_next_request (agent);
+}
+
+static gboolean
+initiate_authentication_finish (PolkitAgentListener *listener,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+void
+shell_polkit_authentication_agent_complete (ShellPolkitAuthenticationAgent *agent,
+ gboolean dismissed)
+{
+ g_return_if_fail (SHELL_IS_POLKIT_AUTHENTICATION_AGENT (agent));
+ g_return_if_fail (agent->current_request != NULL);
+
+ auth_request_complete (agent->current_request, dismissed);
+}
diff --git a/src/shell-polkit-authentication-agent.h b/src/shell-polkit-authentication-agent.h
new file mode 100644
index 0000000..4f14749
--- /dev/null
+++ b/src/shell-polkit-authentication-agent.h
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 20011 Red Hat, Inc.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#pragma once
+
+#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
+#include <polkitagent/polkitagent.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifndef HAVE_POLKIT_AUTOCLEANUP
+/* Polkit doesn't have g_autoptr support, thus we have to manually set the autoptr function here */
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAgentListener, g_object_unref)
+#endif
+
+#define SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT (shell_polkit_authentication_agent_get_type())
+
+G_DECLARE_FINAL_TYPE (ShellPolkitAuthenticationAgent, shell_polkit_authentication_agent, SHELL, POLKIT_AUTHENTICATION_AGENT, PolkitAgentListener)
+
+ShellPolkitAuthenticationAgent *shell_polkit_authentication_agent_new (void);
+
+void shell_polkit_authentication_agent_complete (ShellPolkitAuthenticationAgent *agent,
+ gboolean dismissed);
+void shell_polkit_authentication_agent_register (ShellPolkitAuthenticationAgent *agent,
+ GError **error_out);
+void shell_polkit_authentication_agent_unregister (ShellPolkitAuthenticationAgent *agent);
+
+G_END_DECLS
+
diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c
new file mode 100644
index 0000000..f7132ee
--- /dev/null
+++ b/src/shell-screenshot.c
@@ -0,0 +1,1217 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <meta/display.h>
+#include <meta/util.h>
+#include <meta/meta-plugin.h>
+#include <meta/meta-cursor-tracker.h>
+#include <st/st.h>
+
+#include "shell-global.h"
+#include "shell-screenshot.h"
+#include "shell-util.h"
+
+typedef enum _ShellScreenshotFlag
+{
+ SHELL_SCREENSHOT_FLAG_NONE,
+ SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR,
+} ShellScreenshotFlag;
+
+typedef enum _ShellScreenshotMode
+{
+ SHELL_SCREENSHOT_SCREEN,
+ SHELL_SCREENSHOT_WINDOW,
+ SHELL_SCREENSHOT_AREA,
+} ShellScreenshotMode;
+
+enum
+{
+ SCREENSHOT_TAKEN,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+typedef struct _ShellScreenshotPrivate ShellScreenshotPrivate;
+
+struct _ShellScreenshot
+{
+ GObject parent_instance;
+
+ ShellScreenshotPrivate *priv;
+};
+
+struct _ShellScreenshotPrivate
+{
+ ShellGlobal *global;
+
+ GOutputStream *stream;
+ ShellScreenshotFlag flags;
+ ShellScreenshotMode mode;
+
+ GDateTime *datetime;
+
+ cairo_surface_t *image;
+ cairo_rectangle_int_t screenshot_area;
+
+ gboolean include_frame;
+
+ float scale;
+ ClutterContent *cursor_content;
+ graphene_point_t cursor_point;
+ float cursor_scale;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
+
+static void
+shell_screenshot_class_init (ShellScreenshotClass *screenshot_class)
+{
+ signals[SCREENSHOT_TAKEN] =
+ g_signal_new ("screenshot-taken",
+ G_TYPE_FROM_CLASS(screenshot_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ META_TYPE_RECTANGLE);
+}
+
+static void
+shell_screenshot_init (ShellScreenshot *screenshot)
+{
+ screenshot->priv = shell_screenshot_get_instance_private (screenshot);
+ screenshot->priv->global = shell_global_get ();
+}
+
+static void
+on_screenshot_written (GObject *source,
+ GAsyncResult *task,
+ gpointer user_data)
+{
+ ShellScreenshot *screenshot = SHELL_SCREENSHOT (source);
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ GTask *result = user_data;
+
+ g_task_return_boolean (result, g_task_propagate_boolean (G_TASK (task), NULL));
+ g_object_unref (result);
+
+ g_clear_pointer (&priv->image, cairo_surface_destroy);
+ g_clear_object (&priv->stream);
+ g_clear_pointer (&priv->datetime, g_date_time_unref);
+}
+
+static void
+write_screenshot_thread (GTask *result,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ShellScreenshot *screenshot = SHELL_SCREENSHOT (object);
+ ShellScreenshotPrivate *priv;
+ g_autoptr (GOutputStream) stream = NULL;
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+ g_autofree char *creation_time = NULL;
+ GError *error = NULL;
+
+ g_assert (screenshot != NULL);
+
+ priv = screenshot->priv;
+
+ stream = g_object_ref (priv->stream);
+
+ pixbuf = gdk_pixbuf_get_from_surface (priv->image,
+ 0, 0,
+ cairo_image_surface_get_width (priv->image),
+ cairo_image_surface_get_height (priv->image));
+ creation_time = g_date_time_format (priv->datetime, "%c");
+
+ if (!creation_time)
+ creation_time = g_date_time_format (priv->datetime, "%FT%T%z");
+
+ gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, &error,
+ "tEXt::Software", "gnome-screenshot",
+ "tEXt::Creation Time", creation_time,
+ NULL);
+
+ if (error)
+ g_task_return_error (result, error);
+ else
+ g_task_return_boolean (result, TRUE);
+}
+
+static void
+do_grab_screenshot (ShellScreenshot *screenshot,
+ int x,
+ int y,
+ int width,
+ int height,
+ ShellScreenshotFlag flags)
+{
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ ClutterStage *stage = shell_global_get_stage (priv->global);
+ cairo_rectangle_int_t screenshot_rect = { x, y, width, height };
+ int image_width;
+ int image_height;
+ float scale;
+ cairo_surface_t *image;
+ ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
+ g_autoptr (GError) error = NULL;
+
+ clutter_stage_get_capture_final_size (stage, &screenshot_rect,
+ &image_width,
+ &image_height,
+ &scale);
+ image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ image_width, image_height);
+
+ if (flags & SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR)
+ paint_flags |= CLUTTER_PAINT_FLAG_FORCE_CURSORS;
+ else
+ paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
+ if (!clutter_stage_paint_to_buffer (stage, &screenshot_rect, scale,
+ cairo_image_surface_get_data (image),
+ cairo_image_surface_get_stride (image),
+ CLUTTER_CAIRO_FORMAT_ARGB32,
+ paint_flags,
+ &error))
+ {
+ cairo_surface_destroy (image);
+ g_warning ("Failed to take screenshot: %s", error->message);
+ return;
+ }
+
+ priv->image = image;
+
+ priv->datetime = g_date_time_new_now_local ();
+}
+
+static void
+draw_cursor_image (cairo_surface_t *surface,
+ cairo_rectangle_int_t area)
+{
+ CoglTexture *texture;
+ int width, height;
+ int stride;
+ guint8 *data;
+ MetaDisplay *display;
+ MetaCursorTracker *tracker;
+ cairo_surface_t *cursor_surface;
+ cairo_region_t *screenshot_region;
+ cairo_t *cr;
+ int x, y;
+ int xhot, yhot;
+ double xscale, yscale;
+ graphene_point_t point;
+
+ display = shell_global_get_display (shell_global_get ());
+ tracker = meta_cursor_tracker_get_for_display (display);
+ texture = meta_cursor_tracker_get_sprite (tracker);
+
+ if (!texture)
+ return;
+
+ screenshot_region = cairo_region_create_rectangle (&area);
+ meta_cursor_tracker_get_pointer (tracker, &point, NULL);
+ x = point.x;
+ y = point.y;
+
+ if (!cairo_region_contains_point (screenshot_region, point.x, point.y))
+ {
+ cairo_region_destroy (screenshot_region);
+ return;
+ }
+
+ meta_cursor_tracker_get_hot (tracker, &xhot, &yhot);
+ width = cogl_texture_get_width (texture);
+ height = cogl_texture_get_height (texture);
+ stride = 4 * width;
+ data = g_new (guint8, stride * height);
+ cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32, stride, data);
+
+ /* FIXME: cairo-gl? */
+ cursor_surface = cairo_image_surface_create_for_data (data,
+ CAIRO_FORMAT_ARGB32,
+ width, height,
+ stride);
+
+ cairo_surface_get_device_scale (surface, &xscale, &yscale);
+
+ if (xscale != 1.0 || yscale != 1.0)
+ {
+ int monitor;
+ float monitor_scale;
+ MetaRectangle cursor_rect = {
+ .x = x, .y = y, .width = width, .height = height
+ };
+
+ monitor = meta_display_get_monitor_index_for_rect (display, &cursor_rect);
+ monitor_scale = meta_display_get_monitor_scale (display, monitor);
+
+ cairo_surface_set_device_scale (cursor_surface, monitor_scale, monitor_scale);
+ }
+
+ cr = cairo_create (surface);
+ cairo_set_source_surface (cr,
+ cursor_surface,
+ x - xhot - area.x,
+ y - yhot - area.y);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (cursor_surface);
+ cairo_region_destroy (screenshot_region);
+ g_free (data);
+}
+
+static void
+grab_screenshot (ShellScreenshot *screenshot,
+ ShellScreenshotFlag flags,
+ GTask *result)
+{
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ MetaDisplay *display;
+ int width, height;
+ GTask *task;
+
+ display = shell_global_get_display (priv->global);
+ meta_display_get_size (display, &width, &height);
+
+ do_grab_screenshot (screenshot,
+ 0, 0, width, height,
+ flags);
+
+ priv->screenshot_area.x = 0;
+ priv->screenshot_area.y = 0;
+ priv->screenshot_area.width = width;
+ priv->screenshot_area.height = height;
+
+ task = g_task_new (screenshot, NULL, on_screenshot_written, result);
+ g_task_run_in_thread (task, write_screenshot_thread);
+ g_object_unref (task);
+}
+
+static void
+grab_screenshot_content (ShellScreenshot *screenshot,
+ GTask *result)
+{
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ MetaDisplay *display;
+ int width, height;
+ cairo_rectangle_int_t screenshot_rect;
+ ClutterStage *stage;
+ int image_width;
+ int image_height;
+ float scale;
+ g_autoptr (GError) error = NULL;
+ g_autoptr (ClutterContent) content = NULL;
+ g_autoptr (GTask) task = result;
+ MetaCursorTracker *tracker;
+ CoglTexture *cursor_texture;
+ int cursor_hot_x, cursor_hot_y;
+
+ display = shell_global_get_display (priv->global);
+ meta_display_get_size (display, &width, &height);
+ screenshot_rect = (cairo_rectangle_int_t) {
+ .x = 0,
+ .y = 0,
+ .width = width,
+ .height = height,
+ };
+
+ stage = shell_global_get_stage (priv->global);
+
+ clutter_stage_get_capture_final_size (stage, &screenshot_rect,
+ &image_width,
+ &image_height,
+ &scale);
+
+ priv->scale = scale;
+
+ content = clutter_stage_paint_to_content (stage, &screenshot_rect, scale,
+ CLUTTER_PAINT_FLAG_NO_CURSORS,
+ &error);
+ if (!content)
+ {
+ g_task_return_error (result, g_steal_pointer (&error));
+ return;
+ }
+
+ tracker = meta_cursor_tracker_get_for_display (display);
+ cursor_texture = meta_cursor_tracker_get_sprite (tracker);
+
+ // If the cursor is invisible, the texture is NULL.
+ if (cursor_texture)
+ {
+ unsigned int width, height;
+ CoglContext *ctx;
+ CoglPipeline *pipeline;
+ CoglTexture2D *texture;
+ CoglOffscreen *offscreen;
+ ClutterStageView *view;
+
+ // Copy the texture to prevent it from changing shortly after.
+ width = cogl_texture_get_width (cursor_texture);
+ height = cogl_texture_get_height (cursor_texture);
+
+ ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ texture = cogl_texture_2d_new_with_size (ctx, width, height);
+ offscreen = cogl_offscreen_new_with_texture (texture);
+ cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
+ COGL_BUFFER_BIT_COLOR,
+ 0, 0, 0, 0);
+
+ pipeline = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture);
+
+ cogl_framebuffer_draw_textured_rectangle (COGL_FRAMEBUFFER (offscreen),
+ pipeline,
+ -1, 1, 1, -1,
+ 0, 0, 1, 1);
+ cogl_object_unref (pipeline);
+ g_object_unref (offscreen);
+
+ priv->cursor_content =
+ clutter_texture_content_new_from_texture (texture, NULL);
+ cogl_object_unref (texture);
+
+ priv->cursor_scale = meta_cursor_tracker_get_scale (tracker);
+
+ meta_cursor_tracker_get_pointer (tracker, &priv->cursor_point, NULL);
+
+ view = clutter_stage_get_view_at (stage,
+ priv->cursor_point.x,
+ priv->cursor_point.y);
+
+ meta_cursor_tracker_get_hot (tracker, &cursor_hot_x, &cursor_hot_y);
+ priv->cursor_point.x -= cursor_hot_x * priv->cursor_scale;
+ priv->cursor_point.y -= cursor_hot_y * priv->cursor_scale;
+
+ // Align the coordinates to the pixel grid the same way it's done in
+ // MetaCursorRenderer.
+ if (view)
+ {
+ cairo_rectangle_int_t view_layout;
+ float view_scale;
+
+ clutter_stage_view_get_layout (view, &view_layout);
+ view_scale = clutter_stage_view_get_scale (view);
+
+ priv->cursor_point.x -= view_layout.x;
+ priv->cursor_point.y -= view_layout.y;
+
+ priv->cursor_point.x =
+ floorf (priv->cursor_point.x * view_scale) / view_scale;
+ priv->cursor_point.y =
+ floorf (priv->cursor_point.y * view_scale) / view_scale;
+
+ priv->cursor_point.x += view_layout.x;
+ priv->cursor_point.y += view_layout.y;
+ }
+ }
+
+ g_task_return_pointer (result, g_steal_pointer (&content), g_object_unref);
+}
+
+static void
+grab_window_screenshot (ShellScreenshot *screenshot,
+ ShellScreenshotFlag flags,
+ GTask *result)
+{
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ GTask *task;
+ MetaDisplay *display = shell_global_get_display (priv->global);
+ MetaWindow *window = meta_display_get_focus_window (display);
+ ClutterActor *window_actor;
+ gfloat actor_x, actor_y;
+ MetaRectangle rect;
+
+ window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
+ clutter_actor_get_position (window_actor, &actor_x, &actor_y);
+
+ meta_window_get_frame_rect (window, &rect);
+
+ if (!priv->include_frame)
+ meta_window_frame_rect_to_client_rect (window, &rect, &rect);
+
+ priv->screenshot_area = rect;
+
+ priv->image = meta_window_actor_get_image (META_WINDOW_ACTOR (window_actor),
+ NULL);
+
+ if (!priv->image)
+ {
+ g_task_report_new_error (screenshot, on_screenshot_written, result, NULL,
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Capturing window failed");
+ return;
+ }
+
+ priv->datetime = g_date_time_new_now_local ();
+
+ if (flags & SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR)
+ {
+ if (meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_WAYLAND)
+ {
+ float resource_scale;
+ resource_scale = clutter_actor_get_resource_scale (window_actor);
+
+ cairo_surface_set_device_scale (priv->image, resource_scale, resource_scale);
+ }
+
+ draw_cursor_image (priv->image, priv->screenshot_area);
+ }
+
+ g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0, &rect);
+
+ task = g_task_new (screenshot, NULL, on_screenshot_written, result);
+ g_task_run_in_thread (task, write_screenshot_thread);
+ g_object_unref (task);
+}
+
+static gboolean
+finish_screenshot (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ cairo_rectangle_int_t **area,
+ GError **error)
+{
+ ShellScreenshotPrivate *priv = screenshot->priv;
+
+ if (!g_task_propagate_boolean (G_TASK (result), error))
+ return FALSE;
+
+ if (area)
+ *area = &priv->screenshot_area;
+
+ return TRUE;
+}
+
+static void
+on_after_paint (ClutterStage *stage,
+ ClutterStageView *view,
+ GTask *result)
+{
+ ShellScreenshot *screenshot = g_task_get_task_data (result);
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ MetaDisplay *display = shell_global_get_display (priv->global);
+ GTask *task;
+
+ g_signal_handlers_disconnect_by_func (stage, on_after_paint, result);
+
+ if (priv->mode == SHELL_SCREENSHOT_AREA)
+ {
+ do_grab_screenshot (screenshot,
+ priv->screenshot_area.x,
+ priv->screenshot_area.y,
+ priv->screenshot_area.width,
+ priv->screenshot_area.height,
+ priv->flags);
+
+ task = g_task_new (screenshot, NULL, on_screenshot_written, result);
+ g_task_run_in_thread (task, write_screenshot_thread);
+ }
+ else
+ {
+ grab_screenshot (screenshot, priv->flags, result);
+ }
+
+ g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+ (cairo_rectangle_int_t *) &priv->screenshot_area);
+
+ meta_enable_unredirect_for_display (display);
+}
+
+/**
+ * shell_screenshot_screenshot:
+ * @screenshot: the #ShellScreenshot
+ * @include_cursor: Whether to include the cursor or not
+ * @stream: The stream for the screenshot
+ * @callback: (scope async): function to call returning success or failure
+ * of the async grabbing
+ * @user_data: the data to pass to callback function
+ *
+ * Takes a screenshot of the whole screen
+ * in @stream as png image.
+ *
+ */
+void
+shell_screenshot_screenshot (ShellScreenshot *screenshot,
+ gboolean include_cursor,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellScreenshotPrivate *priv;
+ GTask *result;
+ ShellScreenshotFlag flags;
+
+ g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
+ g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+
+ priv = screenshot->priv;
+
+ if (priv->stream != NULL) {
+ if (callback)
+ g_task_report_new_error (screenshot,
+ callback,
+ user_data,
+ shell_screenshot_screenshot,
+ G_IO_ERROR,
+ G_IO_ERROR_PENDING,
+ "Only one screenshot operation at a time "
+ "is permitted");
+ return;
+ }
+
+ result = g_task_new (screenshot, NULL, callback, user_data);
+ g_task_set_source_tag (result, shell_screenshot_screenshot);
+ g_task_set_task_data (result, screenshot, NULL);
+
+ priv->stream = g_object_ref (stream);
+
+ flags = SHELL_SCREENSHOT_FLAG_NONE;
+ if (include_cursor)
+ flags |= SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR;
+
+ if (meta_is_wayland_compositor ())
+ {
+ grab_screenshot (screenshot, flags, result);
+
+ g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+ (cairo_rectangle_int_t *) &priv->screenshot_area);
+ }
+ else
+ {
+ MetaDisplay *display = shell_global_get_display (priv->global);
+ ClutterStage *stage = shell_global_get_stage (priv->global);
+
+ meta_disable_unredirect_for_display (display);
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+ priv->flags = flags;
+ priv->mode = SHELL_SCREENSHOT_SCREEN;
+ g_signal_connect (stage, "after-paint",
+ G_CALLBACK (on_after_paint), result);
+ }
+}
+
+/**
+ * shell_screenshot_screenshot_finish:
+ * @screenshot: the #ShellScreenshot
+ * @result: the #GAsyncResult that was provided to the callback
+ * @area: (out) (transfer none): the area that was grabbed in screen coordinates
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by shell_screenshot_screenshot()
+ * and obtain its result.
+ *
+ * Returns: whether the operation was successful
+ *
+ */
+gboolean
+shell_screenshot_screenshot_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ cairo_rectangle_int_t **area,
+ GError **error)
+{
+ g_return_val_if_fail (SHELL_IS_SCREENSHOT (screenshot), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_screenshot_screenshot),
+ FALSE);
+ return finish_screenshot (screenshot, result, area, error);
+}
+
+static void
+screenshot_stage_to_content_on_after_paint (ClutterStage *stage,
+ ClutterStageView *view,
+ GTask *result)
+{
+ ShellScreenshot *screenshot = g_task_get_task_data (result);
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ MetaDisplay *display = shell_global_get_display (priv->global);
+
+ g_signal_handlers_disconnect_by_func (stage,
+ screenshot_stage_to_content_on_after_paint,
+ result);
+
+ meta_enable_unredirect_for_display (display);
+
+ grab_screenshot_content (screenshot, result);
+}
+
+/**
+ * shell_screenshot_screenshot_stage_to_content:
+ * @screenshot: the #ShellScreenshot
+ * @callback: (scope async): function to call returning success or failure
+ * of the async grabbing
+ * @user_data: the data to pass to callback function
+ *
+ * Takes a screenshot of the whole screen as #ClutterContent.
+ *
+ */
+void
+shell_screenshot_screenshot_stage_to_content (ShellScreenshot *screenshot,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellScreenshotPrivate *priv;
+ GTask *result;
+
+ g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
+
+ result = g_task_new (screenshot, NULL, callback, user_data);
+ g_task_set_source_tag (result, shell_screenshot_screenshot_stage_to_content);
+ g_task_set_task_data (result, screenshot, NULL);
+
+ if (meta_is_wayland_compositor ())
+ {
+ grab_screenshot_content (screenshot, result);
+ }
+ else
+ {
+ priv = screenshot->priv;
+
+ MetaDisplay *display = shell_global_get_display (priv->global);
+ ClutterStage *stage = shell_global_get_stage (priv->global);
+
+ meta_disable_unredirect_for_display (display);
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+ g_signal_connect (stage, "after-paint",
+ G_CALLBACK (screenshot_stage_to_content_on_after_paint),
+ result);
+ }
+}
+
+/**
+ * shell_screenshot_screenshot_stage_to_content_finish:
+ * @screenshot: the #ShellScreenshot
+ * @result: the #GAsyncResult that was provided to the callback
+ * @scale: (out) (optional): location to store the content scale
+ * @cursor_content: (out) (optional): location to store the cursor content
+ * @cursor_point: (out) (optional): location to store the point at which to
+ * draw the cursor content
+ * @cursor_scale: (out) (optional): location to store the cursor scale
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by
+ * shell_screenshot_screenshot_stage_to_content() and obtain its result.
+ *
+ * Returns: (transfer full): the #ClutterContent, or NULL
+ *
+ */
+ClutterContent *
+shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ float *scale,
+ ClutterContent **cursor_content,
+ graphene_point_t *cursor_point,
+ float *cursor_scale,
+ GError **error)
+{
+ ShellScreenshotPrivate *priv = screenshot->priv;
+ ClutterContent *content;
+
+ g_return_val_if_fail (SHELL_IS_SCREENSHOT (screenshot), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_screenshot_screenshot_stage_to_content),
+ FALSE);
+
+ content = g_task_propagate_pointer (G_TASK (result), error);
+ if (!content)
+ return NULL;
+
+ if (scale)
+ *scale = priv->scale;
+
+ if (cursor_content)
+ *cursor_content = g_steal_pointer (&priv->cursor_content);
+ else
+ g_clear_pointer (&priv->cursor_content, g_object_unref);
+
+ if (cursor_point)
+ *cursor_point = priv->cursor_point;
+
+ if (cursor_scale)
+ *cursor_scale = priv->cursor_scale;
+
+ return content;
+}
+
+/**
+ * shell_screenshot_screenshot_area:
+ * @screenshot: the #ShellScreenshot
+ * @x: The X coordinate of the area
+ * @y: The Y coordinate of the area
+ * @width: The width of the area
+ * @height: The height of the area
+ * @stream: The stream for the screenshot
+ * @callback: (scope async): function to call returning success or failure
+ * of the async grabbing
+ * @user_data: the data to pass to callback function
+ *
+ * Takes a screenshot of the passed in area and saves it
+ * in @stream as png image.
+ *
+ */
+void
+shell_screenshot_screenshot_area (ShellScreenshot *screenshot,
+ int x,
+ int y,
+ int width,
+ int height,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellScreenshotPrivate *priv;
+ GTask *result;
+ g_autoptr (GTask) task = NULL;
+
+ g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
+ g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+
+ priv = screenshot->priv;
+
+ if (priv->stream != NULL) {
+ if (callback)
+ g_task_report_new_error (screenshot,
+ callback,
+ NULL,
+ shell_screenshot_screenshot_area,
+ G_IO_ERROR,
+ G_IO_ERROR_PENDING,
+ "Only one screenshot operation at a time "
+ "is permitted");
+ return;
+ }
+
+ result = g_task_new (screenshot, NULL, callback, user_data);
+ g_task_set_source_tag (result, shell_screenshot_screenshot_area);
+ g_task_set_task_data (result, screenshot, NULL);
+
+ priv->stream = g_object_ref (stream);
+ priv->screenshot_area.x = x;
+ priv->screenshot_area.y = y;
+ priv->screenshot_area.width = width;
+ priv->screenshot_area.height = height;
+
+
+ if (meta_is_wayland_compositor ())
+ {
+ do_grab_screenshot (screenshot,
+ priv->screenshot_area.x,
+ priv->screenshot_area.y,
+ priv->screenshot_area.width,
+ priv->screenshot_area.height,
+ SHELL_SCREENSHOT_FLAG_NONE);
+
+ g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+ (cairo_rectangle_int_t *) &priv->screenshot_area);
+
+ task = g_task_new (screenshot, NULL, on_screenshot_written, result);
+ g_task_run_in_thread (task, write_screenshot_thread);
+ }
+ else
+ {
+ MetaDisplay *display = shell_global_get_display (priv->global);
+ ClutterStage *stage = shell_global_get_stage (priv->global);
+
+ meta_disable_unredirect_for_display (display);
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+ priv->flags = SHELL_SCREENSHOT_FLAG_NONE;
+ priv->mode = SHELL_SCREENSHOT_AREA;
+ g_signal_connect (stage, "after-paint",
+ G_CALLBACK (on_after_paint), result);
+ }
+}
+
+/**
+ * shell_screenshot_screenshot_area_finish:
+ * @screenshot: the #ShellScreenshot
+ * @result: the #GAsyncResult that was provided to the callback
+ * @area: (out) (transfer none): the area that was grabbed in screen coordinates
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by shell_screenshot_screenshot_area()
+ * and obtain its result.
+ *
+ * Returns: whether the operation was successful
+ *
+ */
+gboolean
+shell_screenshot_screenshot_area_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ cairo_rectangle_int_t **area,
+ GError **error)
+{
+ g_return_val_if_fail (SHELL_IS_SCREENSHOT (screenshot), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_screenshot_screenshot_area),
+ FALSE);
+ return finish_screenshot (screenshot, result, area, error);
+}
+
+/**
+ * shell_screenshot_screenshot_window:
+ * @screenshot: the #ShellScreenshot
+ * @include_frame: Whether to include the frame or not
+ * @include_cursor: Whether to include the cursor or not
+ * @stream: The stream for the screenshot
+ * @callback: (scope async): function to call returning success or failure
+ * of the async grabbing
+ * @user_data: the data to pass to callback function
+ *
+ * Takes a screenshot of the focused window (optionally omitting the frame)
+ * in @stream as png image.
+ *
+ */
+void
+shell_screenshot_screenshot_window (ShellScreenshot *screenshot,
+ gboolean include_frame,
+ gboolean include_cursor,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellScreenshotPrivate *priv;
+ MetaDisplay *display;
+ MetaWindow *window;
+ GTask *result;
+
+ g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
+ g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+
+ priv = screenshot->priv;
+ display = shell_global_get_display (priv->global);
+ window = meta_display_get_focus_window (display);
+
+ if (priv->stream != NULL || !window) {
+ if (callback)
+ g_task_report_new_error (screenshot,
+ callback,
+ NULL,
+ shell_screenshot_screenshot_window,
+ G_IO_ERROR,
+ G_IO_ERROR_PENDING,
+ "Only one screenshot operation at a time "
+ "is permitted");
+ return;
+ }
+
+ result = g_task_new (screenshot, NULL, callback, user_data);
+ g_task_set_source_tag (result, shell_screenshot_screenshot_window);
+
+ priv->stream = g_object_ref (stream);
+ priv->include_frame = include_frame;
+
+ grab_window_screenshot (screenshot, include_cursor, result);
+}
+
+/**
+ * shell_screenshot_screenshot_window_finish:
+ * @screenshot: the #ShellScreenshot
+ * @result: the #GAsyncResult that was provided to the callback
+ * @area: (out) (transfer none): the area that was grabbed in screen coordinates
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by shell_screenshot_screenshot_window()
+ * and obtain its result.
+ *
+ * Returns: whether the operation was successful
+ *
+ */
+gboolean
+shell_screenshot_screenshot_window_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ cairo_rectangle_int_t **area,
+ GError **error)
+{
+ g_return_val_if_fail (SHELL_IS_SCREENSHOT (screenshot), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_screenshot_screenshot_window),
+ FALSE);
+ return finish_screenshot (screenshot, result, area, error);
+}
+
+/**
+ * shell_screenshot_pick_color:
+ * @screenshot: the #ShellScreenshot
+ * @x: The X coordinate to pick
+ * @y: The Y coordinate to pick
+ * @callback: (scope async): function to call returning success or failure
+ * of the async grabbing
+ *
+ * Picks the pixel at @x, @y and returns its color as #ClutterColor.
+ *
+ */
+void
+shell_screenshot_pick_color (ShellScreenshot *screenshot,
+ int x,
+ int y,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ShellScreenshotPrivate *priv;
+ g_autoptr (GTask) result = NULL;
+
+ g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
+
+ result = g_task_new (screenshot, NULL, callback, user_data);
+ g_task_set_source_tag (result, shell_screenshot_pick_color);
+
+ priv = screenshot->priv;
+
+ priv->screenshot_area.x = x;
+ priv->screenshot_area.y = y;
+ priv->screenshot_area.width = 1;
+ priv->screenshot_area.height = 1;
+
+ do_grab_screenshot (screenshot,
+ priv->screenshot_area.x,
+ priv->screenshot_area.y,
+ 1,
+ 1,
+ SHELL_SCREENSHOT_FLAG_NONE);
+
+ g_task_return_boolean (result, TRUE);
+}
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define INDEX_A 3
+#define INDEX_R 2
+#define INDEX_G 1
+#define INDEX_B 0
+#else
+#define INDEX_A 0
+#define INDEX_R 1
+#define INDEX_G 2
+#define INDEX_B 3
+#endif
+
+/**
+ * shell_screenshot_pick_color_finish:
+ * @screenshot: the #ShellScreenshot
+ * @result: the #GAsyncResult that was provided to the callback
+ * @color: (out caller-allocates): the picked color
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by shell_screenshot_pick_color()
+ * and obtain its result.
+ *
+ * Returns: whether the operation was successful
+ *
+ */
+gboolean
+shell_screenshot_pick_color_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ ClutterColor *color,
+ GError **error)
+{
+ ShellScreenshotPrivate *priv;
+
+ g_return_val_if_fail (SHELL_IS_SCREENSHOT (screenshot), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (color != NULL, FALSE);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_screenshot_pick_color),
+ FALSE);
+
+ if (!g_task_propagate_boolean (G_TASK (result), error))
+ return FALSE;
+
+ priv = screenshot->priv;
+
+ /* protect against mutter changing the format used for stage captures */
+ g_assert (cairo_image_surface_get_format (priv->image) == CAIRO_FORMAT_ARGB32);
+
+ if (color)
+ {
+ uint8_t *data = cairo_image_surface_get_data (priv->image);
+
+ color->alpha = data[INDEX_A];
+ color->red = data[INDEX_R];
+ color->green = data[INDEX_G];
+ color->blue = data[INDEX_B];
+ }
+
+ return TRUE;
+}
+
+#undef INDEX_A
+#undef INDEX_R
+#undef INDEX_G
+#undef INDEX_B
+
+static void
+composite_to_stream_on_png_saved (GObject *pixbuf,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+
+ if (!gdk_pixbuf_save_to_stream_finish (result, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, g_object_ref (pixbuf), g_object_unref);
+
+ g_object_unref (task);
+}
+
+/**
+ * shell_screenshot_composite_to_stream:
+ * @texture: the source texture
+ * @x: x coordinate of the rectangle
+ * @y: y coordinate of the rectangle
+ * @width: width of the rectangle, or -1 to use the full texture
+ * @height: height of the rectangle, or -1 to use the full texture
+ * @scale: scale of the source texture
+ * @cursor: (nullable): the cursor texture
+ * @cursor_x: x coordinate to put the cursor texture at, relative to the full
+ * source texture
+ * @cursor_y: y coordinate to put the cursor texture at, relative to the full
+ * source texture
+ * @cursor_scale: scale of the cursor texture
+ * @stream: the stream to write the PNG image into
+ * @callback: (scope async): function to call returning success or failure
+ * @user_data: the data to pass to callback function
+ *
+ * Composite a rectangle defined by x, y, width, height from the texture to a
+ * pixbuf and write it as a PNG image into the stream.
+ *
+ */
+void
+shell_screenshot_composite_to_stream (CoglTexture *texture,
+ int x,
+ int y,
+ int width,
+ int height,
+ float scale,
+ CoglTexture *cursor,
+ int cursor_x,
+ int cursor_y,
+ float cursor_scale,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CoglContext *ctx;
+ CoglTexture *sub_texture;
+ cairo_surface_t *surface;
+ cairo_surface_t *cursor_surface;
+ cairo_t *cr;
+ g_autoptr (GTask) task = NULL;
+ g_autoptr (GdkPixbuf) pixbuf = NULL;
+ g_autofree char *creation_time = NULL;
+ g_autoptr (GDateTime) date_time = NULL;
+
+ task = g_task_new (NULL, NULL, callback, user_data);
+ g_task_set_source_tag (task, shell_screenshot_composite_to_stream);
+
+ if (width == -1 || height == -1)
+ {
+ x = 0;
+ y = 0;
+ width = cogl_texture_get_width (texture);
+ height = cogl_texture_get_height (texture);
+ }
+
+ ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ sub_texture = cogl_sub_texture_new (ctx, texture, x, y, width, height);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ cogl_texture_get_width (sub_texture),
+ cogl_texture_get_height (sub_texture));
+
+ cogl_texture_get_data (sub_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
+ cairo_image_surface_get_stride (surface),
+ cairo_image_surface_get_data (surface));
+ cairo_surface_mark_dirty (surface);
+
+ cogl_object_unref (sub_texture);
+
+ cairo_surface_set_device_scale (surface, scale, scale);
+
+ if (cursor != NULL)
+ {
+ // Paint the cursor on top.
+ cursor_surface =
+ cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ cogl_texture_get_width (cursor),
+ cogl_texture_get_height (cursor));
+ cogl_texture_get_data (cursor, CLUTTER_CAIRO_FORMAT_ARGB32,
+ cairo_image_surface_get_stride (cursor_surface),
+ cairo_image_surface_get_data (cursor_surface));
+ cairo_surface_mark_dirty (cursor_surface);
+
+ cairo_surface_set_device_scale (cursor_surface,
+ 1 / cursor_scale,
+ 1 / cursor_scale);
+
+ cr = cairo_create (surface);
+ cairo_set_source_surface (cr, cursor_surface,
+ (cursor_x - x) / scale,
+ (cursor_y - y) / scale);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (cursor_surface);
+ }
+
+ // Save to an image.
+ pixbuf = gdk_pixbuf_get_from_surface (surface,
+ 0, 0,
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface));
+ cairo_surface_destroy (surface);
+
+ date_time = g_date_time_new_now_local ();
+ creation_time = g_date_time_format (date_time, "%c");
+
+ if (!creation_time)
+ creation_time = g_date_time_format (date_time, "%FT%T%z");
+
+ gdk_pixbuf_save_to_stream_async (pixbuf, stream, "png", NULL,
+ composite_to_stream_on_png_saved,
+ g_steal_pointer (&task),
+ "tEXt::Software", "gnome-screenshot",
+ "tEXt::Creation Time", creation_time,
+ NULL);
+}
+
+/**
+ * shell_screenshot_composite_to_stream_finish:
+ * @result: the #GAsyncResult that was provided to the callback
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by
+ * shell_screenshot_composite_to_stream () and obtain its result.
+ *
+ * Returns: (transfer full) (nullable): a GdkPixbuf with the final image if the
+ * operation was successful, or NULL on error.
+ *
+ */
+GdkPixbuf *
+shell_screenshot_composite_to_stream_finish (GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_async_result_is_tagged (result,
+ shell_screenshot_composite_to_stream),
+ FALSE);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+ShellScreenshot *
+shell_screenshot_new (void)
+{
+ return g_object_new (SHELL_TYPE_SCREENSHOT, NULL);
+}
diff --git a/src/shell-screenshot.h b/src/shell-screenshot.h
new file mode 100644
index 0000000..441410d
--- /dev/null
+++ b/src/shell-screenshot.h
@@ -0,0 +1,90 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_SCREENSHOT_H__
+#define __SHELL_SCREENSHOT_H__
+
+/**
+ * SECTION:shell-screenshot
+ * @short_description: Grabs screenshots of areas and/or windows
+ *
+ * The #ShellScreenshot object is used to take screenshots of screen
+ * areas or windows and write them out as png files.
+ *
+ */
+#define SHELL_TYPE_SCREENSHOT (shell_screenshot_get_type ())
+G_DECLARE_FINAL_TYPE (ShellScreenshot, shell_screenshot,
+ SHELL, SCREENSHOT, GObject)
+
+ShellScreenshot *shell_screenshot_new (void);
+
+void shell_screenshot_screenshot_area (ShellScreenshot *screenshot,
+ int x,
+ int y,
+ int width,
+ int height,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_screenshot_screenshot_area_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ cairo_rectangle_int_t **area,
+ GError **error);
+
+void shell_screenshot_screenshot_window (ShellScreenshot *screenshot,
+ gboolean include_frame,
+ gboolean include_cursor,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_screenshot_screenshot_window_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ cairo_rectangle_int_t **area,
+ GError **error);
+
+void shell_screenshot_screenshot (ShellScreenshot *screenshot,
+ gboolean include_cursor,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_screenshot_screenshot_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ cairo_rectangle_int_t **area,
+ GError **error);
+
+void shell_screenshot_screenshot_stage_to_content (ShellScreenshot *screenshot,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ClutterContent *shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ float *scale,
+ ClutterContent **cursor_content,
+ graphene_point_t *cursor_point,
+ float *cursor_scale,
+ GError **error);
+
+void shell_screenshot_pick_color (ShellScreenshot *screenshot,
+ int x,
+ int y,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_screenshot_pick_color_finish (ShellScreenshot *screenshot,
+ GAsyncResult *result,
+ ClutterColor *color,
+ GError **error);
+
+void shell_screenshot_composite_to_stream (CoglTexture *texture,
+ int x,
+ int y,
+ int width,
+ int height,
+ float scale,
+ CoglTexture *cursor,
+ int cursor_x,
+ int cursor_y,
+ float cursor_scale,
+ GOutputStream *stream,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GdkPixbuf *shell_screenshot_composite_to_stream_finish (GAsyncResult *result,
+ GError **error);
+
+#endif /* ___SHELL_SCREENSHOT_H__ */
diff --git a/src/shell-secure-text-buffer.c b/src/shell-secure-text-buffer.c
new file mode 100644
index 0000000..8271410
--- /dev/null
+++ b/src/shell-secure-text-buffer.c
@@ -0,0 +1,191 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* shell-secure-text-buffer.c - secure memory clutter text buffer
+
+ Copyright (C) 2009 Stefan Walter
+ Copyright (C) 2012 Red Hat Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ see <http://www.gnu.org/licenses/>.
+
+ Author: Stef Walter <stefw@gnome.org>
+*/
+
+#include "config.h"
+
+#include "shell-secure-text-buffer.h"
+
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr.h>
+
+#include <string.h>
+
+struct _ShellSecureTextBuffer {
+ ClutterTextBuffer parent;
+ gchar *text;
+ gsize text_size;
+ gsize text_bytes;
+ guint text_chars;
+};
+
+/* Initial size of buffer, in bytes */
+#define MIN_SIZE 16
+
+G_DEFINE_TYPE (ShellSecureTextBuffer, shell_secure_text_buffer, CLUTTER_TYPE_TEXT_BUFFER);
+
+static const gchar *
+shell_secure_text_buffer_real_get_text (ClutterTextBuffer *buffer,
+ gsize *n_bytes)
+{
+ ShellSecureTextBuffer *self = SHELL_SECURE_TEXT_BUFFER (buffer);
+ if (n_bytes)
+ *n_bytes = self->text_bytes;
+ if (!self->text)
+ return "";
+ return self->text;
+}
+
+static guint
+shell_secure_text_buffer_real_get_length (ClutterTextBuffer *buffer)
+{
+ ShellSecureTextBuffer *self = SHELL_SECURE_TEXT_BUFFER (buffer);
+ return self->text_chars;
+}
+
+static guint
+shell_secure_text_buffer_real_insert_text (ClutterTextBuffer *buffer,
+ guint position,
+ const gchar *chars,
+ guint n_chars)
+{
+ ShellSecureTextBuffer *self = SHELL_SECURE_TEXT_BUFFER (buffer);
+ gsize n_bytes;
+ gsize at;
+
+ n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars;
+
+ /* Need more memory */
+ if (n_bytes + self->text_bytes + 1 > self->text_size)
+ {
+ /* Calculate our new buffer size */
+ while (n_bytes + self->text_bytes + 1 > self->text_size)
+ {
+ if (self->text_size == 0)
+ {
+ self->text_size = MIN_SIZE;
+ }
+ else
+ {
+ if (2 * self->text_size < CLUTTER_TEXT_BUFFER_MAX_SIZE)
+ {
+ self->text_size *= 2;
+ }
+ else
+ {
+ self->text_size = CLUTTER_TEXT_BUFFER_MAX_SIZE;
+ if (n_bytes > self->text_size - self->text_bytes - 1)
+ {
+ n_bytes = self->text_size - self->text_bytes - 1;
+ n_bytes = g_utf8_find_prev_char (chars, chars + n_bytes + 1) - chars;
+ n_chars = g_utf8_strlen (chars, n_bytes);
+ }
+ break;
+ }
+ }
+ }
+ self->text = gcr_secure_memory_realloc (self->text, self->text_size);
+ }
+
+ /* Actual text insertion */
+ at = g_utf8_offset_to_pointer (self->text, position) - self->text;
+ memmove (self->text + at + n_bytes, self->text + at, self->text_bytes - at);
+ memcpy (self->text + at, chars, n_bytes);
+
+ /* Book keeping */
+ self->text_bytes += n_bytes;
+ self->text_chars += n_chars;
+ self->text[self->text_bytes] = '\0';
+
+ clutter_text_buffer_emit_inserted_text (buffer, position, chars, n_chars);
+ return n_chars;
+}
+
+static guint
+shell_secure_text_buffer_real_delete_text (ClutterTextBuffer *buffer,
+ guint position,
+ guint n_chars)
+{
+ ShellSecureTextBuffer *self = SHELL_SECURE_TEXT_BUFFER (buffer);
+ gsize start, end;
+
+ if (position > self->text_chars)
+ position = self->text_chars;
+ if (position + n_chars > self->text_chars)
+ n_chars = self->text_chars - position;
+
+ if (n_chars > 0)
+ {
+ start = g_utf8_offset_to_pointer (self->text, position) - self->text;
+ end = g_utf8_offset_to_pointer (self->text, position + n_chars) - self->text;
+
+ memmove (self->text + start, self->text + end, self->text_bytes + 1 - end);
+ self->text_chars -= n_chars;
+ self->text_bytes -= (end - start);
+
+ clutter_text_buffer_emit_deleted_text (buffer, position, n_chars);
+ }
+
+ return n_chars;
+}
+
+static void
+shell_secure_text_buffer_init (ShellSecureTextBuffer *self)
+{
+
+}
+
+static void
+shell_secure_text_buffer_finalize (GObject *obj)
+{
+ ShellSecureTextBuffer *self = SHELL_SECURE_TEXT_BUFFER (obj);
+
+ if (self->text)
+ {
+ gcr_secure_memory_strfree (self->text);
+ self->text = NULL;
+ self->text_bytes = self->text_size = 0;
+ self->text_chars = 0;
+ }
+
+ G_OBJECT_CLASS (shell_secure_text_buffer_parent_class)->finalize (obj);
+}
+
+static void
+shell_secure_text_buffer_class_init (ShellSecureTextBufferClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterTextBufferClass *buffer_class = CLUTTER_TEXT_BUFFER_CLASS (klass);
+
+ gobject_class->finalize = shell_secure_text_buffer_finalize;
+
+ buffer_class->get_text = shell_secure_text_buffer_real_get_text;
+ buffer_class->get_length = shell_secure_text_buffer_real_get_length;
+ buffer_class->insert_text = shell_secure_text_buffer_real_insert_text;
+ buffer_class->delete_text = shell_secure_text_buffer_real_delete_text;
+}
+
+ClutterTextBuffer *
+shell_secure_text_buffer_new (void)
+{
+ return g_object_new (SHELL_TYPE_SECURE_TEXT_BUFFER, NULL);
+}
diff --git a/src/shell-secure-text-buffer.h b/src/shell-secure-text-buffer.h
new file mode 100644
index 0000000..6685b72
--- /dev/null
+++ b/src/shell-secure-text-buffer.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* shell-secure-text-buffer.h - secure memory clutter text buffer
+
+ Copyright (C) 2009 Stefan Walter
+ Copyright (C) 2012 Red Hat Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ see <http://www.gnu.org/licenses/>.
+
+ Author: Stef Walter <stefw@gnome.org>
+*/
+
+#ifndef __SHELL_SECURE_TEXT_BUFFER_H__
+#define __SHELL_SECURE_TEXT_BUFFER_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_SECURE_TEXT_BUFFER (shell_secure_text_buffer_get_type ())
+G_DECLARE_FINAL_TYPE (ShellSecureTextBuffer, shell_secure_text_buffer,
+ SHELL, SECURE_TEXT_BUFFER, ClutterTextBuffer)
+
+ClutterTextBuffer * shell_secure_text_buffer_new (void);
+
+G_END_DECLS
+
+#endif /* __SHELL_SECURE_TEXT_BUFFER_H__ */
diff --git a/src/shell-square-bin.c b/src/shell-square-bin.c
new file mode 100644
index 0000000..06587fb
--- /dev/null
+++ b/src/shell-square-bin.c
@@ -0,0 +1,43 @@
+#include "config.h"
+
+#include "shell-square-bin.h"
+
+struct _ShellSquareBin
+{
+ /*< private >*/
+ StBin parent_instance;
+};
+
+G_DEFINE_TYPE (ShellSquareBin, shell_square_bin, ST_TYPE_BIN);
+
+static void
+shell_square_bin_get_preferred_width (ClutterActor *actor,
+ float for_height,
+ float *min_width_p,
+ float *natural_width_p)
+{
+ float min_width, nat_width;
+
+ /* Return the actual height to keep the squared aspect */
+ clutter_actor_get_preferred_height (actor, -1,
+ &min_width, &nat_width);
+
+ if (min_width_p)
+ *min_width_p = min_width;
+
+ if (natural_width_p)
+ *natural_width_p = nat_width;
+}
+
+static void
+shell_square_bin_init (ShellSquareBin *self)
+{
+}
+
+static void
+shell_square_bin_class_init (ShellSquareBinClass *klass)
+{
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ actor_class->get_preferred_width = shell_square_bin_get_preferred_width;
+}
diff --git a/src/shell-square-bin.h b/src/shell-square-bin.h
new file mode 100644
index 0000000..2b7d4b2
--- /dev/null
+++ b/src/shell-square-bin.h
@@ -0,0 +1,13 @@
+#ifndef __SHELL_SQUARE_BIN_H__
+#define __SHELL_SQUARE_BIN_H__
+
+#include <st/st.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_SQUARE_BIN (shell_square_bin_get_type ())
+G_DECLARE_FINAL_TYPE (ShellSquareBin, shell_square_bin, SHELL, SquareBin, StBin)
+
+G_END_DECLS
+
+#endif /* __SHELL_SQUARE_BIN_H__ */
diff --git a/src/shell-stack.c b/src/shell-stack.c
new file mode 100644
index 0000000..79841e0
--- /dev/null
+++ b/src/shell-stack.c
@@ -0,0 +1,196 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/**
+ * SECTION:shell-stack
+ * @short_description: Pure "Z-axis" container class
+ *
+ * A #ShellStack draws its children on top of each other,
+ * aligned to the top left. It will be sized in width/height
+ * according to the largest such dimension of its children, and
+ * all children will be allocated that size. This differs
+ * from #ClutterGroup which allocates its children their natural
+ * size, even if that would overflow the size allocated to the stack.
+ */
+
+#include "config.h"
+
+#include "shell-stack.h"
+
+struct _ShellStack
+{
+ StWidget parent;
+};
+
+G_DEFINE_TYPE (ShellStack, shell_stack, ST_TYPE_WIDGET);
+
+static void
+shell_stack_allocate (ClutterActor *self,
+ const ClutterActorBox *box)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ ClutterActorBox content_box;
+ ClutterActor *child;
+
+ clutter_actor_set_allocation (self, box);
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ for (child = clutter_actor_get_first_child (self);
+ child != NULL;
+ child = clutter_actor_get_next_sibling (child))
+ {
+ ClutterActorBox child_box = content_box;
+ clutter_actor_allocate (child, &child_box);
+ }
+}
+
+static void
+shell_stack_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ gboolean first = TRUE;
+ float min = 0, natural = 0;
+ ClutterActor *child;
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ for (child = clutter_actor_get_first_child (actor);
+ child != NULL;
+ child = clutter_actor_get_next_sibling (child))
+ {
+ float child_min, child_natural;
+
+ clutter_actor_get_preferred_height (child,
+ for_width,
+ &child_min,
+ &child_natural);
+
+ if (first)
+ {
+ first = FALSE;
+ min = child_min;
+ natural = child_natural;
+ }
+ else
+ {
+ if (child_min > min)
+ min = child_min;
+
+ if (child_natural > natural)
+ natural = child_natural;
+ }
+ }
+
+ if (min_height_p)
+ *min_height_p = min;
+
+ if (natural_height_p)
+ *natural_height_p = natural;
+
+ st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
+}
+
+static void
+shell_stack_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ gboolean first = TRUE;
+ float min = 0, natural = 0;
+ ClutterActor *child;
+
+ st_theme_node_adjust_for_height (theme_node, &for_height);
+
+ for (child = clutter_actor_get_first_child (actor);
+ child != NULL;
+ child = clutter_actor_get_next_sibling (child))
+ {
+ float child_min, child_natural;
+
+ clutter_actor_get_preferred_width (child,
+ for_height,
+ &child_min,
+ &child_natural);
+
+ if (first)
+ {
+ first = FALSE;
+ min = child_min;
+ natural = child_natural;
+ }
+ else
+ {
+ if (child_min > min)
+ min = child_min;
+
+ if (child_natural > natural)
+ natural = child_natural;
+ }
+ }
+
+ if (min_width_p)
+ *min_width_p = min;
+
+ if (natural_width_p)
+ *natural_width_p = natural;
+
+ st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
+}
+
+static gboolean
+shell_stack_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction)
+{
+ ClutterActor *top_actor;
+
+ /* If the stack is itself focusable, then focus into or out of
+ * it, as appropriate.
+ */
+ if (st_widget_get_can_focus (widget))
+ {
+ if (from && clutter_actor_contains (CLUTTER_ACTOR (widget), from))
+ return FALSE;
+
+ if (clutter_actor_is_mapped (CLUTTER_ACTOR (widget)))
+ {
+ clutter_actor_grab_key_focus (CLUTTER_ACTOR (widget));
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ top_actor = clutter_actor_get_last_child (CLUTTER_ACTOR (widget));
+ while (top_actor && !clutter_actor_is_visible (top_actor))
+ top_actor = clutter_actor_get_previous_sibling (top_actor);
+ if (ST_IS_WIDGET (top_actor))
+ return st_widget_navigate_focus (ST_WIDGET (top_actor), from, direction, FALSE);
+ else
+ return FALSE;
+}
+
+static void
+shell_stack_class_init (ShellStackClass *klass)
+{
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ actor_class->get_preferred_width = shell_stack_get_preferred_width;
+ actor_class->get_preferred_height = shell_stack_get_preferred_height;
+ actor_class->allocate = shell_stack_allocate;
+
+ widget_class->navigate_focus = shell_stack_navigate_focus;
+}
+
+static void
+shell_stack_init (ShellStack *actor)
+{
+}
diff --git a/src/shell-stack.h b/src/shell-stack.h
new file mode 100644
index 0000000..e54160d
--- /dev/null
+++ b/src/shell-stack.h
@@ -0,0 +1,11 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_STACK_H__
+#define __SHELL_STACK_H__
+
+#include "st.h"
+#include <gtk/gtk.h>
+
+#define SHELL_TYPE_STACK (shell_stack_get_type ())
+G_DECLARE_FINAL_TYPE (ShellStack, shell_stack, SHELL, STACK, StWidget)
+
+#endif /* __SHELL_STACK_H__ */
diff --git a/src/shell-tray-icon.c b/src/shell-tray-icon.c
new file mode 100644
index 0000000..14b2191
--- /dev/null
+++ b/src/shell-tray-icon.c
@@ -0,0 +1,294 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include "shell-tray-icon.h"
+#include "shell-gtk-embed.h"
+#include "tray/na-tray-child.h"
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+#include "st.h"
+
+enum {
+ PROP_0,
+
+ PROP_PID,
+ PROP_TITLE,
+ PROP_WM_CLASS
+};
+
+typedef struct _ShellTrayIconPrivate ShellTrayIconPrivate;
+
+struct _ShellTrayIcon
+{
+ ShellGtkEmbed parent;
+
+ ShellTrayIconPrivate *priv;
+};
+
+struct _ShellTrayIconPrivate
+{
+ NaTrayChild *socket;
+
+ pid_t pid;
+ char *title, *wm_class;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellTrayIcon, shell_tray_icon, SHELL_TYPE_GTK_EMBED);
+
+static void
+shell_tray_icon_finalize (GObject *object)
+{
+ ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
+
+ g_free (icon->priv->title);
+ g_free (icon->priv->wm_class);
+
+ G_OBJECT_CLASS (shell_tray_icon_parent_class)->finalize (object);
+}
+
+static void
+shell_tray_icon_constructed (GObject *object)
+{
+ GdkWindow *icon_app_window;
+ ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
+ ShellEmbeddedWindow *window;
+ GdkDisplay *display;
+ Window plug_xid;
+ Atom _NET_WM_PID, type;
+ int result, format;
+ gulong nitems, bytes_after, *val = NULL;
+
+ /* We do all this now rather than computing it on the fly later,
+ * because the shell may want to see their values from a
+ * tray-icon-removed signal handler, at which point the plug has
+ * already been removed from the socket.
+ */
+
+ g_object_get (object, "window", &window, NULL);
+ g_return_if_fail (window != NULL);
+ icon->priv->socket = NA_TRAY_CHILD (gtk_bin_get_child (GTK_BIN (window)));
+ g_object_unref (window);
+
+ icon->priv->title = na_tray_child_get_title (icon->priv->socket);
+ na_tray_child_get_wm_class (icon->priv->socket, NULL, &icon->priv->wm_class);
+
+ icon_app_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
+ plug_xid = GDK_WINDOW_XID (icon_app_window);
+
+ display = gtk_widget_get_display (GTK_WIDGET (icon->priv->socket));
+ gdk_x11_display_error_trap_push (display);
+ _NET_WM_PID = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PID");
+ result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), plug_xid,
+ _NET_WM_PID, 0, G_MAXLONG, False, XA_CARDINAL,
+ &type, &format, &nitems,
+ &bytes_after, (guchar **)&val);
+ if (!gdk_x11_display_error_trap_pop (display) &&
+ result == Success &&
+ type == XA_CARDINAL &&
+ nitems == 1)
+ icon->priv->pid = *val;
+
+ if (val)
+ XFree (val);
+}
+
+static void
+shell_tray_icon_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
+
+ switch (prop_id)
+ {
+ case PROP_PID:
+ g_value_set_uint (value, icon->priv->pid);
+ break;
+
+ case PROP_TITLE:
+ g_value_set_string (value, icon->priv->title);
+ break;
+
+ case PROP_WM_CLASS:
+ g_value_set_string (value, icon->priv->wm_class);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_tray_icon_class_init (ShellTrayIconClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = shell_tray_icon_get_property;
+ object_class->constructed = shell_tray_icon_constructed;
+ object_class->finalize = shell_tray_icon_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PID,
+ g_param_spec_uint ("pid",
+ "PID",
+ "The PID of the icon's application",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The icon's window title",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_WM_CLASS,
+ g_param_spec_string ("wm-class",
+ "WM Class",
+ "The icon's window WM_CLASS",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+shell_tray_icon_init (ShellTrayIcon *icon)
+{
+ icon->priv = shell_tray_icon_get_instance_private (icon);
+}
+
+/*
+ * Public API
+ */
+ClutterActor *
+shell_tray_icon_new (ShellEmbeddedWindow *window)
+{
+ g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL);
+
+ return g_object_new (SHELL_TYPE_TRAY_ICON,
+ "window", window,
+ NULL);
+}
+
+/**
+ * shell_tray_icon_click:
+ * @icon: a #ShellTrayIcon
+ * @event: the #ClutterEvent triggering the fake click
+ *
+ * Fakes a press and release on @icon. @event must be a
+ * %CLUTTER_BUTTON_RELEASE, %CLUTTER_KEY_PRESS or %CLUTTER_KEY_RELEASE event.
+ * Its relevant details will be passed on to the icon, but its
+ * coordinates will be ignored; the click is
+ * always made on the center of @icon.
+ */
+void
+shell_tray_icon_click (ShellTrayIcon *icon,
+ ClutterEvent *event)
+{
+ XKeyEvent xkevent;
+ XButtonEvent xbevent;
+ XCrossingEvent xcevent;
+ GdkDisplay *display;
+ GdkWindow *remote_window;
+ GdkScreen *screen;
+ int x_root, y_root;
+ Display *xdisplay;
+ Window xwindow, xrootwindow;
+ ClutterEventType event_type = clutter_event_type (event);
+
+ g_return_if_fail (event_type == CLUTTER_BUTTON_RELEASE ||
+ event_type == CLUTTER_KEY_PRESS ||
+ event_type == CLUTTER_KEY_RELEASE);
+
+ remote_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
+ if (remote_window == NULL)
+ {
+ g_warning ("shell tray: plug window is gone");
+ return;
+ }
+ xdisplay = GDK_WINDOW_XDISPLAY (remote_window);
+
+ display = gdk_x11_lookup_xdisplay (xdisplay);
+ gdk_x11_display_error_trap_push (display);
+
+ xwindow = GDK_WINDOW_XID (remote_window);
+ screen = gdk_window_get_screen (remote_window);
+ xrootwindow = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
+ gdk_window_get_origin (remote_window, &x_root, &y_root);
+
+
+ /* First make the icon believe the pointer is inside it */
+ xcevent.type = EnterNotify;
+ xcevent.window = xwindow;
+ xcevent.root = xrootwindow;
+ xcevent.subwindow = None;
+ xcevent.time = clutter_event_get_time (event);
+ xcevent.x = gdk_window_get_width (remote_window) / 2;
+ xcevent.y = gdk_window_get_height (remote_window) / 2;
+ xcevent.x_root = x_root + xcevent.x;
+ xcevent.y_root = y_root + xcevent.y;
+ xcevent.mode = NotifyNormal;
+ xcevent.detail = NotifyNonlinear;
+ xcevent.same_screen = True;
+ XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
+
+ /* Now do the click */
+ if (event_type == CLUTTER_BUTTON_RELEASE)
+ {
+ xbevent.window = xwindow;
+ xbevent.root = xrootwindow;
+ xbevent.subwindow = None;
+ xbevent.time = xcevent.time;
+ xbevent.x = xcevent.x;
+ xbevent.y = xcevent.y;
+ xbevent.x_root = xcevent.x_root;
+ xbevent.y_root = xcevent.y_root;
+ xbevent.state = clutter_event_get_state (event);
+ xbevent.same_screen = True;
+ xbevent.type = ButtonPress;
+ xbevent.button = clutter_event_get_button (event);
+ XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
+
+ xbevent.type = ButtonRelease;
+ XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
+ }
+ else
+ {
+ xkevent.window = xwindow;
+ xkevent.root = xrootwindow;
+ xkevent.subwindow = None;
+ xkevent.time = xcevent.time;
+ xkevent.x = xcevent.x;
+ xkevent.y = xcevent.y;
+ xkevent.x_root = xcevent.x_root;
+ xkevent.y_root = xcevent.y_root;
+ xkevent.state = clutter_event_get_state (event);
+ xkevent.same_screen = True;
+ xkevent.keycode = clutter_event_get_key_code (event);
+
+ xkevent.type = KeyPress;
+ XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xkevent);
+
+ if (event_type == CLUTTER_KEY_RELEASE)
+ {
+ /* If the application takes a grab on KeyPress, we don't
+ * want to send it a KeyRelease. There's no good way of
+ * knowing whether a tray icon will take a grab, so just
+ * assume it does, and don't send the KeyRelease. That might
+ * make the tracking for key events messed up if it doesn't take
+ * a grab, but the tray icon won't get key focus in normal cases,
+ * so let's hope this isn't too damaging...
+ */
+ xkevent.type = KeyRelease;
+ XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xkevent);
+ }
+ }
+
+ /* And move the pointer back out */
+ xcevent.type = LeaveNotify;
+ XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+}
diff --git a/src/shell-tray-icon.h b/src/shell-tray-icon.h
new file mode 100644
index 0000000..f0e1191
--- /dev/null
+++ b/src/shell-tray-icon.h
@@ -0,0 +1,16 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_TRAY_ICON_H__
+#define __SHELL_TRAY_ICON_H__
+
+#include "shell-gtk-embed.h"
+
+#define SHELL_TYPE_TRAY_ICON (shell_tray_icon_get_type ())
+G_DECLARE_FINAL_TYPE (ShellTrayIcon, shell_tray_icon,
+ SHELL, TRAY_ICON, ShellGtkEmbed)
+
+ClutterActor *shell_tray_icon_new (ShellEmbeddedWindow *window);
+
+void shell_tray_icon_click (ShellTrayIcon *icon,
+ ClutterEvent *event);
+
+#endif /* __SHELL_TRAY_ICON_H__ */
diff --git a/src/shell-tray-manager.c b/src/shell-tray-manager.c
new file mode 100644
index 0000000..c8e3259
--- /dev/null
+++ b/src/shell-tray-manager.c
@@ -0,0 +1,374 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <clutter/clutter.h>
+#include <girepository.h>
+#include <gtk/gtk.h>
+#include <meta/display.h>
+
+#include "shell-tray-manager.h"
+#include "na-tray-manager.h"
+
+#include "shell-tray-icon.h"
+#include "shell-embedded-window.h"
+#include "shell-global.h"
+
+typedef struct _ShellTrayManagerPrivate ShellTrayManagerPrivate;
+
+struct _ShellTrayManager
+{
+ GObject parent_instance;
+
+ ShellTrayManagerPrivate *priv;
+};
+
+struct _ShellTrayManagerPrivate {
+ NaTrayManager *na_manager;
+ ClutterColor bg_color;
+
+ GHashTable *icons;
+ StWidget *theme_widget;
+};
+
+typedef struct {
+ ShellTrayManager *manager;
+ GtkWidget *socket;
+ GtkWidget *window;
+ ClutterActor *actor;
+} ShellTrayManagerChild;
+
+enum {
+ PROP_0,
+
+ PROP_BG_COLOR
+};
+
+/* Signals */
+enum
+{
+ TRAY_ICON_ADDED,
+ TRAY_ICON_REMOVED,
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellTrayManager, shell_tray_manager, G_TYPE_OBJECT);
+
+static guint shell_tray_manager_signals [LAST_SIGNAL] = { 0 };
+
+static const ClutterColor default_color = { 0x00, 0x00, 0x00, 0xff };
+
+static void shell_tray_manager_release_resources (ShellTrayManager *manager);
+
+static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
+static void na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
+
+static void
+free_tray_icon (gpointer data)
+{
+ ShellTrayManagerChild *child = data;
+
+ gtk_widget_destroy (child->window);
+ if (child->actor)
+ {
+ g_signal_handlers_disconnect_matched (child->actor, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, child);
+ g_object_unref (child->actor);
+ }
+ g_free (child);
+}
+
+static void
+shell_tray_manager_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_BG_COLOR:
+ {
+ ClutterColor *color = g_value_get_boxed (value);
+ if (color)
+ manager->priv->bg_color = *color;
+ else
+ manager->priv->bg_color = default_color;
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_tray_manager_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_BG_COLOR:
+ g_value_set_boxed (value, &manager->priv->bg_color);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_tray_manager_init (ShellTrayManager *manager)
+{
+ manager->priv = shell_tray_manager_get_instance_private (manager);
+
+ manager->priv->bg_color = default_color;
+}
+
+static void
+shell_tray_manager_finalize (GObject *object)
+{
+ ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
+
+ shell_tray_manager_release_resources (manager);
+
+ G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object);
+}
+
+static void
+shell_tray_manager_class_init (ShellTrayManagerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = shell_tray_manager_finalize;
+ gobject_class->set_property = shell_tray_manager_set_property;
+ gobject_class->get_property = shell_tray_manager_get_property;
+
+ shell_tray_manager_signals[TRAY_ICON_ADDED] =
+ g_signal_new ("tray-icon-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ CLUTTER_TYPE_ACTOR);
+ shell_tray_manager_signals[TRAY_ICON_REMOVED] =
+ g_signal_new ("tray-icon-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ CLUTTER_TYPE_ACTOR);
+
+ /* Lifting the CONSTRUCT_ONLY here isn't hard; you just need to
+ * iterate through the icons, reset the background pixmap, and
+ * call na_tray_child_force_redraw()
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_BG_COLOR,
+ g_param_spec_boxed ("bg-color",
+ "BG Color",
+ "Background color (only if we don't have transparency)",
+ CLUTTER_TYPE_COLOR,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+ShellTrayManager *
+shell_tray_manager_new (void)
+{
+ return g_object_new (SHELL_TYPE_TRAY_MANAGER, NULL);
+}
+
+static void
+shell_tray_manager_ensure_resources (ShellTrayManager *manager)
+{
+ if (manager->priv->na_manager != NULL)
+ return;
+
+ manager->priv->icons = g_hash_table_new_full (NULL, NULL,
+ NULL, free_tray_icon);
+
+ manager->priv->na_manager = na_tray_manager_new ();
+
+ g_signal_connect (manager->priv->na_manager, "tray-icon-added",
+ G_CALLBACK (na_tray_icon_added), manager);
+ g_signal_connect (manager->priv->na_manager, "tray-icon-removed",
+ G_CALLBACK (na_tray_icon_removed), manager);
+}
+
+static void
+shell_tray_manager_release_resources (ShellTrayManager *manager)
+{
+ g_clear_object (&manager->priv->na_manager);
+ g_clear_pointer (&manager->priv->icons, g_hash_table_destroy);
+}
+
+static void
+shell_tray_manager_style_changed (StWidget *theme_widget,
+ gpointer user_data)
+{
+ ShellTrayManager *manager = user_data;
+ StThemeNode *theme_node;
+ StIconColors *icon_colors;
+
+ if (manager->priv->na_manager == NULL)
+ return;
+
+ theme_node = st_widget_get_theme_node (theme_widget);
+ icon_colors = st_theme_node_get_icon_colors (theme_node);
+ na_tray_manager_set_colors (manager->priv->na_manager,
+ &icon_colors->foreground, &icon_colors->warning,
+ &icon_colors->error, &icon_colors->success);
+}
+
+static void
+shell_tray_manager_manage_screen_internal (ShellTrayManager *manager)
+{
+ shell_tray_manager_ensure_resources (manager);
+ na_tray_manager_manage_screen (manager->priv->na_manager);
+}
+
+void
+shell_tray_manager_manage_screen (ShellTrayManager *manager,
+ StWidget *theme_widget)
+{
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+
+ g_set_weak_pointer (&manager->priv->theme_widget, theme_widget);
+
+ if (meta_display_get_x11_display (display) != NULL)
+ shell_tray_manager_manage_screen_internal (manager);
+
+ g_signal_connect_object (display, "x11-display-setup",
+ G_CALLBACK (shell_tray_manager_manage_screen_internal),
+ manager, G_CONNECT_SWAPPED);
+ g_signal_connect_object (display, "x11-display-closing",
+ G_CALLBACK (shell_tray_manager_release_resources),
+ manager, G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (theme_widget, "style-changed",
+ G_CALLBACK (shell_tray_manager_style_changed),
+ manager, 0);
+ shell_tray_manager_style_changed (theme_widget, manager);
+}
+
+void
+shell_tray_manager_unmanage_screen (ShellTrayManager *manager)
+{
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+
+ g_signal_handlers_disconnect_by_data (display, manager);
+
+ if (manager->priv->theme_widget != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (manager->priv->theme_widget,
+ G_CALLBACK (shell_tray_manager_style_changed),
+ manager);
+ }
+ g_set_weak_pointer (&manager->priv->theme_widget, NULL);
+
+ shell_tray_manager_release_resources (manager);
+}
+
+static void
+shell_tray_manager_child_on_realize (GtkWidget *widget,
+ ShellTrayManagerChild *child)
+{
+ /* If the tray child is using an RGBA colormap (and so we have real
+ * transparency), we don't need to worry about the background. If
+ * not, we obey the bg-color property by creating a cairo pattern of
+ * that color and setting it as our background. Then "parent-relative"
+ * background on the socket and the plug within that will cause
+ * the icons contents to appear on top of our background color.
+ */
+ if (!na_tray_child_has_alpha (NA_TRAY_CHILD (child->socket)))
+ {
+ ClutterColor color = child->manager->priv->bg_color;
+ cairo_pattern_t *bg_pattern;
+
+ bg_pattern = cairo_pattern_create_rgb (color.red / 255.,
+ color.green / 255.,
+ color.blue / 255.);
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gdk_window_set_background_pattern (gtk_widget_get_window (widget),
+ bg_pattern);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+ cairo_pattern_destroy (bg_pattern);
+ }
+}
+
+static void
+on_plug_added (GtkSocket *socket,
+ ShellTrayManager *manager)
+{
+ ShellTrayManagerChild *child;
+
+ g_signal_handlers_disconnect_by_func (socket, on_plug_added, manager);
+
+ child = g_hash_table_lookup (manager->priv->icons, socket);
+
+ child->actor = shell_tray_icon_new (SHELL_EMBEDDED_WINDOW (child->window));
+ g_object_ref_sink (child->actor);
+
+ g_signal_emit (manager, shell_tray_manager_signals[TRAY_ICON_ADDED], 0,
+ child->actor);
+}
+
+static void
+na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
+ gpointer user_data)
+{
+ ShellTrayManager *manager = user_data;
+ GtkWidget *win;
+ ShellTrayManagerChild *child;
+
+ win = shell_embedded_window_new ();
+ gtk_container_add (GTK_CONTAINER (win), socket);
+
+ /* The visual of the socket matches that of its contents; make
+ * the window we put it in match that as well */
+ gtk_widget_set_visual (win, gtk_widget_get_visual (socket));
+
+ child = g_new0 (ShellTrayManagerChild, 1);
+ child->manager = manager;
+ child->window = win;
+ child->socket = socket;
+
+ g_signal_connect (win, "realize",
+ G_CALLBACK (shell_tray_manager_child_on_realize), child);
+
+ gtk_widget_show_all (win);
+
+ g_hash_table_insert (manager->priv->icons, socket, child);
+
+ g_signal_connect (socket, "plug-added", G_CALLBACK (on_plug_added), manager);
+}
+
+static void
+na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *socket,
+ gpointer user_data)
+{
+ ShellTrayManager *manager = user_data;
+ ShellTrayManagerChild *child;
+
+ child = g_hash_table_lookup (manager->priv->icons, socket);
+ g_return_if_fail (child != NULL);
+
+ if (child->actor != NULL)
+ {
+ /* Only emit signal if a corresponding tray-icon-added signal was emitted,
+ that is, if embedding did not fail and we got a plug-added
+ */
+ g_signal_emit (manager,
+ shell_tray_manager_signals[TRAY_ICON_REMOVED], 0,
+ child->actor);
+ }
+ g_hash_table_remove (manager->priv->icons, socket);
+}
diff --git a/src/shell-tray-manager.h b/src/shell-tray-manager.h
new file mode 100644
index 0000000..d6279d4
--- /dev/null
+++ b/src/shell-tray-manager.h
@@ -0,0 +1,22 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#ifndef __SHELL_TRAY_MANAGER_H__
+#define __SHELL_TRAY_MANAGER_H__
+
+#include <clutter/clutter.h>
+#include "st.h"
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_TRAY_MANAGER (shell_tray_manager_get_type ())
+G_DECLARE_FINAL_TYPE (ShellTrayManager, shell_tray_manager,
+ SHELL, TRAY_MANAGER, GObject)
+
+ShellTrayManager *shell_tray_manager_new (void);
+void shell_tray_manager_manage_screen (ShellTrayManager *manager,
+ StWidget *theme_widget);
+void shell_tray_manager_unmanage_screen (ShellTrayManager *manager);
+
+G_END_DECLS
+
+#endif /* __SHELL_TRAY_MANAGER_H__ */
diff --git a/src/shell-util.c b/src/shell-util.c
new file mode 100644
index 0000000..aeb81a1
--- /dev/null
+++ b/src/shell-util.c
@@ -0,0 +1,854 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <GL/gl.h>
+#include <cogl/cogl.h>
+
+#include "shell-app-cache-private.h"
+#include "shell-util.h"
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <meta/display.h>
+#include <meta/meta-x11-display.h>
+
+#include <locale.h>
+#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
+#include <langinfo.h>
+#endif
+
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-login.h>
+#else
+/* So we don't need to add ifdef's everywhere */
+#define sd_notify(u, m) do {} while (0)
+#define sd_notifyf(u, m, ...) do {} while (0)
+#endif
+
+static void
+stop_pick (ClutterActor *actor)
+{
+ g_signal_stop_emission_by_name (actor, "pick");
+}
+
+/**
+ * shell_util_set_hidden_from_pick:
+ * @actor: A #ClutterActor
+ * @hidden: Whether @actor should be hidden from pick
+ *
+ * If @hidden is %TRUE, hide @actor from pick even with a mode of
+ * %CLUTTER_PICK_ALL; if @hidden is %FALSE, unhide @actor.
+ */
+void
+shell_util_set_hidden_from_pick (ClutterActor *actor,
+ gboolean hidden)
+{
+ gpointer existing_handler_data;
+
+ existing_handler_data = g_object_get_data (G_OBJECT (actor),
+ "shell-stop-pick");
+ if (hidden)
+ {
+ if (existing_handler_data != NULL)
+ return;
+ g_signal_connect (actor, "pick", G_CALLBACK (stop_pick), NULL);
+ g_object_set_data (G_OBJECT (actor),
+ "shell-stop-pick", GUINT_TO_POINTER (1));
+ }
+ else
+ {
+ if (existing_handler_data == NULL)
+ return;
+ g_signal_handlers_disconnect_by_func (actor, stop_pick, NULL);
+ g_object_set_data (G_OBJECT (actor), "shell-stop-pick", NULL);
+ }
+}
+
+/**
+ * shell_util_get_week_start:
+ *
+ * Gets the first week day for the current locale, expressed as a
+ * number in the range 0..6, representing week days from Sunday to
+ * Saturday.
+ *
+ * Returns: A number representing the first week day for the current
+ * locale
+ */
+/* Copied from gtkcalendar.c */
+int
+shell_util_get_week_start (void)
+{
+ int week_start;
+#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
+ union { unsigned int word; char *string; } langinfo;
+ int week_1stday = 0;
+ int first_weekday = 1;
+ guint week_origin;
+#else
+ char *gtk_week_start;
+#endif
+
+#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
+ langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
+ first_weekday = langinfo.string[0];
+ langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
+ week_origin = langinfo.word;
+ if (week_origin == 19971130) /* Sunday */
+ week_1stday = 0;
+ else if (week_origin == 19971201) /* Monday */
+ week_1stday = 1;
+ else
+ g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
+
+ week_start = (week_1stday + first_weekday - 1) % 7;
+#else
+ /* Use a define to hide the string from xgettext */
+# define GTK_WEEK_START "calendar:week_start:0"
+ gtk_week_start = dgettext ("gtk30", GTK_WEEK_START);
+
+ if (strncmp (gtk_week_start, "calendar:week_start:", 20) == 0)
+ week_start = *(gtk_week_start + 20) - '0';
+ else
+ week_start = -1;
+
+ if (week_start < 0 || week_start > 6)
+ {
+ g_warning ("Whoever translated calendar:week_start:0 for GTK+ "
+ "did so wrongly.\n");
+ return 0;
+ }
+#endif
+
+ return week_start;
+}
+
+/**
+ * shell_util_translate_time_string:
+ * @str: String to translate
+ *
+ * Translate @str according to the locale defined by LC_TIME; unlike
+ * dcgettext(), the translations is still taken from the LC_MESSAGES
+ * catalogue and not the LC_TIME one.
+ *
+ * Returns: the translated string
+ */
+const char *
+shell_util_translate_time_string (const char *str)
+{
+ const char *locale = g_getenv ("LC_TIME");
+ const char *res;
+ char *sep;
+ locale_t old_loc;
+ locale_t loc = (locale_t) 0;
+
+ if (locale)
+ loc = newlocale (LC_MESSAGES_MASK, locale, (locale_t) 0);
+
+ old_loc = uselocale (loc);
+
+ sep = strchr (str, '\004');
+ res = g_dpgettext (NULL, str, sep ? sep - str + 1 : 0);
+
+ uselocale (old_loc);
+
+ if (loc != (locale_t) 0)
+ freelocale (loc);
+
+ return res;
+}
+
+/**
+ * shell_util_regex_escape:
+ * @str: a UTF-8 string to escape
+ *
+ * A wrapper around g_regex_escape_string() that takes its argument as
+ * \0-terminated string rather than a byte-array that confuses gjs.
+ *
+ * Returns: @str with all regex-special characters escaped
+ */
+char *
+shell_util_regex_escape (const char *str)
+{
+ return g_regex_escape_string (str, -1);
+}
+
+/**
+ * shell_write_string_to_stream:
+ * @stream: a #GOutputStream
+ * @str: a UTF-8 string to write to @stream
+ * @error: location to store GError
+ *
+ * Write a string to a GOutputStream as UTF-8. This is a workaround
+ * for not having binary buffers in GJS.
+ *
+ * Return value: %TRUE if write succeeded
+ */
+gboolean
+shell_write_string_to_stream (GOutputStream *stream,
+ const char *str,
+ GError **error)
+{
+ return g_output_stream_write_all (stream, str, strlen (str),
+ NULL, NULL, error);
+}
+
+/**
+ * shell_get_file_contents_utf8_sync:
+ * @path: UTF-8 encoded filename path
+ * @error: a #GError
+ *
+ * Synchronously load the contents of a file as a NUL terminated
+ * string, validating it as UTF-8. Embedded NUL characters count as
+ * invalid content.
+ *
+ * Returns: (transfer full): File contents
+ */
+char *
+shell_get_file_contents_utf8_sync (const char *path,
+ GError **error)
+{
+ char *contents;
+ gsize len;
+ if (!g_file_get_contents (path, &contents, &len, error))
+ return NULL;
+ if (!g_utf8_validate (contents, len, NULL))
+ {
+ g_free (contents);
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "File %s contains invalid UTF-8",
+ path);
+ return NULL;
+ }
+ return contents;
+}
+
+static void
+touch_file (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GFile *file = object;
+ g_autoptr (GFile) parent = NULL;
+ g_autoptr (GFileOutputStream) stream = NULL;
+ GError *error = NULL;
+
+ parent = g_file_get_parent (file);
+ g_file_make_directory_with_parents (parent, cancellable, &error);
+
+ if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+ g_clear_error (&error);
+
+ stream = g_file_create (file, G_FILE_CREATE_NONE, cancellable, &error);
+
+ if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+ g_clear_error (&error);
+
+ if (stream)
+ g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, NULL);
+
+ g_task_return_boolean (task, stream != NULL);
+}
+
+void
+shell_util_touch_file_async (GFile *file,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = NULL;
+
+ g_return_if_fail (G_IS_FILE (file));
+
+ task = g_task_new (file, NULL, callback, user_data);
+ g_task_set_source_tag (task, shell_util_touch_file_async);
+
+ g_task_run_in_thread (task, touch_file);
+}
+
+gboolean
+shell_util_touch_file_finish (GFile *file,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (G_IS_TASK (res), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+/**
+ * shell_util_wifexited:
+ * @status: the status returned by wait() or waitpid()
+ * @exit: (out): the actual exit status of the process
+ *
+ * Implements libc standard WIFEXITED, that cannot be used JS
+ * code.
+ * Returns: TRUE if the process exited normally, FALSE otherwise
+ */
+gboolean
+shell_util_wifexited (int status,
+ int *exit)
+{
+ gboolean ret;
+
+ ret = WIFEXITED(status);
+
+ if (ret)
+ *exit = WEXITSTATUS(status);
+
+ return ret;
+}
+
+/**
+ * shell_util_create_pixbuf_from_data:
+ * @data: (array length=len) (element-type guint8) (transfer full):
+ * @len:
+ * @colorspace:
+ * @has_alpha:
+ * @bits_per_sample:
+ * @width:
+ * @height:
+ * @rowstride:
+ *
+ * Workaround for non-introspectability of gdk_pixbuf_from_data().
+ *
+ * Returns: (transfer full):
+ */
+GdkPixbuf *
+shell_util_create_pixbuf_from_data (const guchar *data,
+ gsize len,
+ GdkColorspace colorspace,
+ gboolean has_alpha,
+ int bits_per_sample,
+ int width,
+ int height,
+ int rowstride)
+{
+ return gdk_pixbuf_new_from_data (data, colorspace, has_alpha,
+ bits_per_sample, width, height, rowstride,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+}
+
+typedef const gchar *(*ShellGLGetString) (GLenum);
+
+cairo_surface_t *
+shell_util_composite_capture_images (ClutterCapture *captures,
+ int n_captures,
+ int x,
+ int y,
+ int target_width,
+ int target_height,
+ float target_scale)
+{
+ int i;
+ cairo_format_t format;
+ cairo_surface_t *image;
+ cairo_t *cr;
+
+ g_assert (n_captures > 0);
+ g_assert (target_scale > 0.0f);
+
+ format = cairo_image_surface_get_format (captures[0].image);
+ image = cairo_image_surface_create (format, target_width, target_height);
+ cairo_surface_set_device_scale (image, target_scale, target_scale);
+
+ cr = cairo_create (image);
+
+ for (i = 0; i < n_captures; i++)
+ {
+ ClutterCapture *capture = &captures[i];
+
+ cairo_save (cr);
+
+ cairo_translate (cr,
+ capture->rect.x - x,
+ capture->rect.y - y);
+ cairo_set_source_surface (cr, capture->image, 0, 0);
+ cairo_paint (cr);
+
+ cairo_restore (cr);
+ }
+ cairo_destroy (cr);
+
+ return image;
+}
+
+#ifndef HAVE_FDWALK
+static int
+fdwalk (int (*cb)(void *data, int fd),
+ void *data)
+{
+ gint open_max;
+ gint fd;
+ gint res = 0;
+
+#ifdef HAVE_SYS_RESOURCE_H
+ struct rlimit rl;
+#endif
+
+#ifdef __linux__
+ DIR *d;
+
+ if ((d = opendir("/proc/self/fd")))
+ {
+ struct dirent *de;
+
+ while ((de = readdir(d)))
+ {
+ glong l;
+ gchar *e = NULL;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ l = strtol(de->d_name, &e, 10);
+ if (errno != 0 || !e || *e)
+ continue;
+
+ fd = (gint) l;
+
+ if ((glong) fd != l)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ if ((res = cb (data, fd)) != 0)
+ break;
+ }
+
+ closedir(d);
+ return res;
+ }
+
+ /* If /proc is not mounted or not accessible we fall back to the old
+ * rlimit trick */
+
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+ if (getrlimit (RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
+ open_max = rl.rlim_max;
+ else
+#endif
+ open_max = sysconf (_SC_OPEN_MAX);
+
+ for (fd = 0; fd < open_max; fd++)
+ if ((res = cb (data, fd)) != 0)
+ break;
+
+ return res;
+}
+#endif
+
+static int
+check_cloexec (void *data,
+ gint fd)
+{
+ int r;
+
+ if (fd < 3)
+ return 0;
+
+ r = fcntl (fd, F_GETFD);
+ if (r < 0)
+ return 0;
+
+ if (!(r & FD_CLOEXEC))
+ g_warning ("fd %d is not CLOEXEC", fd);
+
+ return 0;
+}
+
+/**
+ * shell_util_check_cloexec_fds:
+ *
+ * Walk over all open file descriptors. Check them for the FD_CLOEXEC flag.
+ * If this flag is not set, log the offending file descriptor number.
+ *
+ * It is important that gnome-shell's file descriptors are all marked CLOEXEC,
+ * so that the shell's open file descriptors are not passed to child processes
+ * that we launch.
+ */
+void
+shell_util_check_cloexec_fds (void)
+{
+ fdwalk (check_cloexec, NULL);
+ g_info ("Open fd CLOEXEC check complete");
+}
+
+/**
+ * shell_util_get_uid:
+ *
+ * A wrapper around getuid() so that it can be used from JavaScript. This
+ * function will always succeed.
+ *
+ * Returns: the real user ID of the calling process
+ */
+gint
+shell_util_get_uid (void)
+{
+ return getuid ();
+}
+
+typedef enum {
+ SYSTEMD_CALL_FLAGS_NONE = 0,
+ SYSTEMD_CALL_FLAGS_WATCH_JOB = 1 << 0,
+} SystemdFlags;
+
+#ifdef HAVE_SYSTEMD
+typedef struct {
+ GDBusConnection *connection;
+ gchar *command;
+ SystemdFlags flags;
+
+ GCancellable *cancellable;
+ gulong cancel_id;
+
+ guint job_watch;
+ gchar *job;
+} SystemdCall;
+
+static void
+shell_util_systemd_call_data_free (SystemdCall *data)
+{
+ if (data->job_watch)
+ {
+ g_dbus_connection_signal_unsubscribe (data->connection, data->job_watch);
+ data->job_watch = 0;
+ }
+
+ if (data->cancellable)
+ {
+ g_cancellable_disconnect (data->cancellable, data->cancel_id);
+ g_clear_object (&data->cancellable);
+ data->cancel_id = 0;
+ }
+
+ g_clear_object (&data->connection);
+ g_clear_pointer (&data->job, g_free);
+ g_clear_pointer (&data->command, g_free);
+ g_free (data);
+}
+
+static void
+shell_util_systemd_call_cancelled_cb (GCancellable *cancellable,
+ GTask *task)
+{
+ SystemdCall *data = g_task_get_task_data (task);
+
+ /* Task has returned, but data is not yet free'ed, ignore signal. */
+ if (g_task_get_completed (task))
+ return;
+
+ /* We are still in the DBus call; it will return the error. */
+ if (data->job == NULL)
+ return;
+
+ g_task_return_error_if_cancelled (task);
+ g_object_unref (task);
+}
+
+static void
+on_systemd_call_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr (GVariant) reply = NULL;
+ g_autoptr (GError) error = NULL;
+ GTask *task = G_TASK (user_data);
+ SystemdCall *data;
+
+ reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
+ res, &error);
+
+ data = g_task_get_task_data (task);
+
+ if (error) {
+ g_warning ("Could not issue '%s' systemd call", data->command);
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+
+ return;
+ }
+
+ g_assert (data->job == NULL);
+ g_variant_get (reply, "(o)", &data->job);
+
+ /* we should either wait for the JobRemoved notification, or
+ * notify here */
+ if ((data->flags & SYSTEMD_CALL_FLAGS_WATCH_JOB) == 0)
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+on_systemd_job_removed_cb (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ SystemdCall *data;
+ guint32 id;
+ const char *path, *unit, *result;
+
+ /* Task has returned, but data is not yet free'ed, ignore signal. */
+ if (g_task_get_completed (task))
+ return;
+
+ data = g_task_get_task_data (task);
+
+ /* No job information yet, ignore. */
+ if (data->job == NULL)
+ return;
+
+ g_variant_get (parameters, "(u&o&s&s)", &id, &path, &unit, &result);
+
+ /* Is it the job we are waiting for? */
+ if (g_strcmp0 (path, data->job) != 0)
+ return;
+
+ /* Task has completed; return the result of the job */
+ if (g_strcmp0 (result, "done") == 0)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Systemd job completed with status \"%s\"",
+ result);
+
+ g_object_unref (task);
+}
+#endif /* HAVE_SYSTEMD */
+
+static void
+shell_util_systemd_call (const char *command,
+ GVariant *params,
+ SystemdFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = g_task_new (NULL, cancellable, callback, user_data);
+
+#ifdef HAVE_SYSTEMD
+ g_autoptr (GDBusConnection) connection = NULL;
+ GError *error = NULL;
+ SystemdCall *data;
+ g_autofree char *self_unit = NULL;
+ int res;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+ if (connection == NULL) {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ /* We look up the systemd unit that our own process is running in here.
+ * This way we determine whether the session is managed using systemd.
+ */
+ res = sd_pid_get_user_unit (getpid (), &self_unit);
+
+ if (res == -ENODATA)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Not systemd managed");
+ return;
+ }
+ else if (res < 0)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ g_io_error_from_errno (-res),
+ "Error fetching own systemd unit: %s",
+ g_strerror (-res));
+ return;
+ }
+
+ data = g_new0 (SystemdCall, 1);
+ data->command = g_strdup (command);
+ data->connection = g_object_ref (connection);
+ data->flags = flags;
+
+ if ((data->flags & SYSTEMD_CALL_FLAGS_WATCH_JOB) != 0)
+ {
+ data->job_watch = g_dbus_connection_signal_subscribe (connection,
+ "org.freedesktop.systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "JobRemoved",
+ "/org/freedesktop/systemd1",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_systemd_job_removed_cb,
+ task,
+ NULL);
+ }
+
+ g_task_set_task_data (task,
+ data,
+ (GDestroyNotify) shell_util_systemd_call_data_free);
+
+ if (cancellable)
+ {
+ data->cancellable = g_object_ref (cancellable);
+ data->cancel_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (shell_util_systemd_call_cancelled_cb),
+ task,
+ NULL);
+ }
+
+ g_dbus_connection_call (connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ command,
+ params,
+ G_VARIANT_TYPE ("(o)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, cancellable,
+ on_systemd_call_cb,
+ g_steal_pointer (&task));
+#else /* HAVE_SYSTEMD */
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "systemd not supported by gnome-shell");
+#endif /* !HAVE_SYSTEMD */
+}
+
+void
+shell_util_start_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ shell_util_systemd_call ("StartUnit",
+ g_variant_new ("(ss)", unit, mode),
+ SYSTEMD_CALL_FLAGS_WATCH_JOB,
+ cancellable, callback, user_data);
+}
+
+gboolean
+shell_util_start_systemd_unit_finish (GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+void
+shell_util_stop_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ shell_util_systemd_call ("StopUnit",
+ g_variant_new ("(ss)", unit, mode),
+ SYSTEMD_CALL_FLAGS_WATCH_JOB,
+ cancellable, callback, user_data);
+}
+
+gboolean
+shell_util_stop_systemd_unit_finish (GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+void
+shell_util_systemd_unit_exists (const gchar *unit,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ shell_util_systemd_call ("GetUnit",
+ g_variant_new ("(s)", unit),
+ SYSTEMD_CALL_FLAGS_NONE,
+ cancellable, callback, user_data);
+}
+
+gboolean
+shell_util_systemd_unit_exists_finish (GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+void
+shell_util_sd_notify (void)
+{
+ /* We only use NOTIFY_SOCKET exactly once; unset it so it doesn't remain in
+ * our environment. */
+ sd_notify (1, "READY=1");
+}
+
+/**
+ * shell_util_has_x11_display_extension:
+ * @display: A #MetaDisplay
+ * @extension: An X11 extension
+ *
+ * If the corresponding X11 display provides the passed extension, return %TRUE,
+ * otherwise %FALSE. If there is no X11 display, %FALSE is passed.
+ */
+gboolean
+shell_util_has_x11_display_extension (MetaDisplay *display,
+ const char *extension)
+{
+ MetaX11Display *x11_display;
+ Display *xdisplay;
+ int op, event, error;
+
+ x11_display = meta_display_get_x11_display (display);
+ if (!x11_display)
+ return FALSE;
+
+ xdisplay = meta_x11_display_get_xdisplay (x11_display);
+ return XQueryExtension (xdisplay, extension, &op, &event, &error);
+}
+
+/**
+ * shell_util_get_translated_folder_name:
+ * @name: the untranslated folder name
+ *
+ * Attempts to translate the folder @name using translations provided
+ * by .directory files.
+ *
+ * Returns: (nullable): a translated string or %NULL
+ */
+char *
+shell_util_get_translated_folder_name (const char *name)
+{
+ return shell_app_cache_translate_folder (shell_app_cache_get_default (), name);
+}
diff --git a/src/shell-util.h b/src/shell-util.h
new file mode 100644
index 0000000..7e1f7d8
--- /dev/null
+++ b/src/shell-util.h
@@ -0,0 +1,93 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#ifndef __SHELL_UTIL_H__
+#define __SHELL_UTIL_H__
+
+#include <gio/gio.h>
+#include <clutter/clutter.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <meta/meta-cursor-tracker.h>
+#include <meta/meta-window-actor.h>
+
+G_BEGIN_DECLS
+
+void shell_util_set_hidden_from_pick (ClutterActor *actor,
+ gboolean hidden);
+
+int shell_util_get_week_start (void);
+
+const char *shell_util_translate_time_string (const char *str);
+
+char *shell_util_regex_escape (const char *str);
+
+gboolean shell_write_string_to_stream (GOutputStream *stream,
+ const char *str,
+ GError **error);
+
+char *shell_get_file_contents_utf8_sync (const char *path,
+ GError **error);
+
+void shell_util_touch_file_async (GFile *file,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_util_touch_file_finish (GFile *file,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean shell_util_wifexited (int status,
+ int *exit);
+
+GdkPixbuf *shell_util_create_pixbuf_from_data (const guchar *data,
+ gsize len,
+ GdkColorspace colorspace,
+ gboolean has_alpha,
+ int bits_per_sample,
+ int width,
+ int height,
+ int rowstride);
+
+cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures,
+ int n_captures,
+ int x,
+ int y,
+ int target_width,
+ int target_height,
+ float target_scale);
+
+void shell_util_check_cloexec_fds (void);
+
+void shell_util_start_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_util_start_systemd_unit_finish (GAsyncResult *res,
+ GError **error);
+
+void shell_util_stop_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_util_stop_systemd_unit_finish (GAsyncResult *res,
+ GError **error);
+
+void shell_util_systemd_unit_exists (const gchar *unit,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_util_systemd_unit_exists_finish (GAsyncResult *res,
+ GError **error);
+
+void shell_util_sd_notify (void);
+
+gboolean shell_util_has_x11_display_extension (MetaDisplay *display,
+ const char *extension);
+
+char *shell_util_get_translated_folder_name (const char *name);
+
+gint shell_util_get_uid (void);
+
+G_END_DECLS
+
+#endif /* __SHELL_UTIL_H__ */
diff --git a/src/shell-window-preview-layout.c b/src/shell-window-preview-layout.c
new file mode 100644
index 0000000..fa3cc1f
--- /dev/null
+++ b/src/shell-window-preview-layout.c
@@ -0,0 +1,495 @@
+#include "config.h"
+
+#include <clutter/clutter.h>
+#include <meta/window.h>
+#include "shell-window-preview-layout.h"
+
+typedef struct _ShellWindowPreviewLayoutPrivate ShellWindowPreviewLayoutPrivate;
+struct _ShellWindowPreviewLayoutPrivate
+{
+ ClutterActor *container;
+ GHashTable *windows;
+
+ ClutterActorBox bounding_box;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_BOUNDING_BOX,
+
+ PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST] = { NULL, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellWindowPreviewLayout, shell_window_preview_layout,
+ CLUTTER_TYPE_LAYOUT_MANAGER);
+
+typedef struct _WindowInfo
+{
+ MetaWindow *window;
+ ClutterActor *window_actor;
+
+ gulong size_changed_id;
+ gulong position_changed_id;
+ gulong window_actor_destroy_id;
+ gulong destroy_id;
+} WindowInfo;
+
+static void
+shell_window_preview_layout_get_property (GObject *object,
+ unsigned int property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (object);
+ ShellWindowPreviewLayoutPrivate *priv;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ switch (property_id)
+ {
+ case PROP_BOUNDING_BOX:
+ g_value_set_boxed (value, &priv->bounding_box);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+shell_window_preview_layout_set_container (ClutterLayoutManager *layout,
+ ClutterContainer *container)
+{
+ ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+ ShellWindowPreviewLayoutPrivate *priv;
+ ClutterLayoutManagerClass *parent_class;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ priv->container = CLUTTER_ACTOR (container);
+
+ parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (shell_window_preview_layout_parent_class);
+ parent_class->set_container (layout, container);
+}
+
+static void
+shell_window_preview_layout_get_preferred_width (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ float for_height,
+ float *min_width_p,
+ float *natural_width_p)
+{
+ ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+ ShellWindowPreviewLayoutPrivate *priv;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ if (min_width_p)
+ *min_width_p = 0;
+
+ if (natural_width_p)
+ *natural_width_p = clutter_actor_box_get_width (&priv->bounding_box);
+}
+
+static void
+shell_window_preview_layout_get_preferred_height (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ float for_width,
+ float *min_height_p,
+ float *natural_height_p)
+{
+ ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+ ShellWindowPreviewLayoutPrivate *priv;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ if (min_height_p)
+ *min_height_p = 0;
+
+ if (natural_height_p)
+ *natural_height_p = clutter_actor_box_get_height (&priv->bounding_box);
+}
+
+
+static void
+shell_window_preview_layout_allocate (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ const ClutterActorBox *box)
+{
+ ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+ ShellWindowPreviewLayoutPrivate *priv;
+ float scale_x, scale_y;
+ float bounding_box_width, bounding_box_height;
+ ClutterActorIter iter;
+ ClutterActor *child;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ bounding_box_width = clutter_actor_box_get_width (&priv->bounding_box);
+ bounding_box_height = clutter_actor_box_get_height (&priv->bounding_box);
+
+ if (bounding_box_width == 0)
+ scale_x = 1.f;
+ else
+ scale_x = clutter_actor_box_get_width (box) / bounding_box_width;
+
+ if (bounding_box_height == 0)
+ scale_y = 1.f;
+ else
+ scale_y = clutter_actor_box_get_height (box) / bounding_box_height;
+
+ clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
+ while (clutter_actor_iter_next (&iter, &child))
+ {
+ ClutterActorBox child_box = { 0, };
+ WindowInfo *window_info;
+
+ if (!clutter_actor_is_visible (child))
+ continue;
+
+ window_info = g_hash_table_lookup (priv->windows, child);
+
+ if (window_info)
+ {
+ MetaRectangle buffer_rect;
+ float child_nat_width, child_nat_height;
+
+ meta_window_get_buffer_rect (window_info->window, &buffer_rect);
+
+ clutter_actor_box_set_origin (&child_box,
+ buffer_rect.x - priv->bounding_box.x1,
+ buffer_rect.y - priv->bounding_box.y1);
+
+ clutter_actor_get_preferred_size (child, NULL, NULL,
+ &child_nat_width, &child_nat_height);
+
+ clutter_actor_box_set_size (&child_box, child_nat_width, child_nat_height);
+
+ child_box.x1 *= scale_x;
+ child_box.x2 *= scale_x;
+ child_box.y1 *= scale_y;
+ child_box.y2 *= scale_y;
+
+ clutter_actor_allocate (child, &child_box);
+ }
+ else
+ {
+ float x, y;
+
+ clutter_actor_get_fixed_position (child, &x, &y);
+ clutter_actor_allocate_preferred_size (child, x, y);
+ }
+ }
+}
+
+static void
+on_layout_changed (ShellWindowPreviewLayout *self)
+{
+ ShellWindowPreviewLayoutPrivate *priv;
+ GHashTableIter iter;
+ gpointer value;
+ gboolean first_rect = TRUE;
+ MetaRectangle bounding_rect = { 0, };
+ ClutterActorBox old_bounding_box;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ old_bounding_box =
+ (ClutterActorBox) CLUTTER_ACTOR_BOX_INIT (priv->bounding_box.x1,
+ priv->bounding_box.y1,
+ priv->bounding_box.x2,
+ priv->bounding_box.y2);
+
+ g_hash_table_iter_init (&iter, priv->windows);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ WindowInfo *window_info = value;
+ MetaRectangle frame_rect;
+
+ meta_window_get_frame_rect (window_info->window, &frame_rect);
+
+ if (first_rect)
+ {
+ bounding_rect = frame_rect;
+ first_rect = FALSE;
+ continue;
+ }
+
+ meta_rectangle_union (&frame_rect, &bounding_rect, &bounding_rect);
+ }
+
+ clutter_actor_box_set_origin (&priv->bounding_box,
+ (float) bounding_rect.x,
+ (float) bounding_rect.y);
+ clutter_actor_box_set_size (&priv->bounding_box,
+ (float) bounding_rect.width,
+ (float) bounding_rect.height);
+
+ if (!clutter_actor_box_equal (&priv->bounding_box, &old_bounding_box))
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_BOUNDING_BOX]);
+
+ clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
+}
+
+static void
+on_window_size_position_changed (MetaWindow *window,
+ ShellWindowPreviewLayout *self)
+{
+ on_layout_changed (self);
+}
+
+static void
+on_window_destroyed (ClutterActor *actor)
+{
+ clutter_actor_destroy (actor);
+}
+
+static void
+on_actor_destroyed (ClutterActor *actor,
+ ShellWindowPreviewLayout *self)
+{
+ ShellWindowPreviewLayoutPrivate *priv;
+ WindowInfo *window_info;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ window_info = g_hash_table_lookup (priv->windows, actor);
+ g_assert (window_info != NULL);
+
+ shell_window_preview_layout_remove_window (self, window_info->window);
+}
+
+static void
+shell_window_preview_layout_dispose (GObject *gobject)
+{
+ ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (gobject);
+ ShellWindowPreviewLayoutPrivate *priv;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ g_hash_table_iter_init (&iter, priv->windows);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ ClutterActor *actor = key;
+ WindowInfo *info = value;
+
+ g_clear_signal_handler (&info->size_changed_id, info->window);
+ g_clear_signal_handler (&info->position_changed_id, info->window);
+ g_clear_signal_handler (&info->window_actor_destroy_id, info->window_actor);
+ g_clear_signal_handler (&info->destroy_id, actor);
+
+ clutter_actor_remove_child (priv->container, actor);
+ }
+
+ g_hash_table_remove_all (priv->windows);
+
+ G_OBJECT_CLASS (shell_window_preview_layout_parent_class)->dispose (gobject);
+}
+
+static void
+shell_window_preview_layout_finalize (GObject *gobject)
+{
+ ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (gobject);
+ ShellWindowPreviewLayoutPrivate *priv;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ g_hash_table_destroy (priv->windows);
+
+ G_OBJECT_CLASS (shell_window_preview_layout_parent_class)->finalize (gobject);
+}
+
+static void
+shell_window_preview_layout_init (ShellWindowPreviewLayout *self)
+{
+ ShellWindowPreviewLayoutPrivate *priv;
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ priv->windows = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) g_free);
+}
+
+static void
+shell_window_preview_layout_class_init (ShellWindowPreviewLayoutClass *klass)
+{
+ ClutterLayoutManagerClass *layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ layout_class->get_preferred_width = shell_window_preview_layout_get_preferred_width;
+ layout_class->get_preferred_height = shell_window_preview_layout_get_preferred_height;
+ layout_class->allocate = shell_window_preview_layout_allocate;
+ layout_class->set_container = shell_window_preview_layout_set_container;
+
+ gobject_class->dispose = shell_window_preview_layout_dispose;
+ gobject_class->finalize = shell_window_preview_layout_finalize;
+ gobject_class->get_property = shell_window_preview_layout_get_property;
+
+ /**
+ * ShellWindowPreviewLayout:bounding-box:
+ */
+ obj_props[PROP_BOUNDING_BOX] =
+ g_param_spec_boxed ("bounding-box",
+ "Bounding Box",
+ "Bounding Box",
+ CLUTTER_TYPE_ACTOR_BOX,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
+}
+
+/**
+ * shell_window_preview_layout_add_window:
+ * @self: a #ShellWindowPreviewLayout
+ * @window: the #MetaWindow
+ *
+ * Creates a ClutterActor drawing the texture of @window and adds it
+ * to the container. If @window is already part of the preview, this
+ * function will do nothing.
+ *
+ * Returns: (nullable) (transfer none): The newly created actor drawing @window
+ */
+ClutterActor *
+shell_window_preview_layout_add_window (ShellWindowPreviewLayout *self,
+ MetaWindow *window)
+{
+ ShellWindowPreviewLayoutPrivate *priv;
+ ClutterActor *window_actor, *actor;
+ WindowInfo *window_info;
+ GHashTableIter iter;
+ gpointer value;
+
+ g_return_val_if_fail (SHELL_IS_WINDOW_PREVIEW_LAYOUT (self), NULL);
+ g_return_val_if_fail (META_IS_WINDOW (window), NULL);
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ g_hash_table_iter_init (&iter, priv->windows);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ WindowInfo *info = value;
+
+ if (info->window == window)
+ return NULL;
+ }
+
+ window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
+ actor = clutter_clone_new (window_actor);
+
+ window_info = g_new0 (WindowInfo, 1);
+
+ window_info->window = window;
+ window_info->window_actor = window_actor;
+ window_info->size_changed_id =
+ g_signal_connect (window, "size-changed",
+ G_CALLBACK (on_window_size_position_changed), self);
+ window_info->position_changed_id =
+ g_signal_connect (window, "position-changed",
+ G_CALLBACK (on_window_size_position_changed), self);
+ window_info->window_actor_destroy_id =
+ g_signal_connect_swapped (window_actor, "destroy",
+ G_CALLBACK (on_window_destroyed), actor);
+ window_info->destroy_id =
+ g_signal_connect (actor, "destroy",
+ G_CALLBACK (on_actor_destroyed), self);
+
+ g_hash_table_insert (priv->windows, actor, window_info);
+
+ clutter_actor_add_child (priv->container, actor);
+
+ on_layout_changed (self);
+
+ return actor;
+}
+
+/**
+ * shell_window_preview_layout_remove_window:
+ * @self: a #ShellWindowPreviewLayout
+ * @window: the #MetaWindow
+ *
+ * Removes a MetaWindow @window from the preview which has been added
+ * previously using shell_window_preview_layout_add_window().
+ * If @window is not part of preview, this function will do nothing.
+ */
+void
+shell_window_preview_layout_remove_window (ShellWindowPreviewLayout *self,
+ MetaWindow *window)
+{
+ ShellWindowPreviewLayoutPrivate *priv;
+ ClutterActor *actor;
+ WindowInfo *window_info = NULL;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_if_fail (SHELL_IS_WINDOW_PREVIEW_LAYOUT (self));
+ g_return_if_fail (META_IS_WINDOW (window));
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ g_hash_table_iter_init (&iter, priv->windows);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ WindowInfo *info = value;
+
+ if (info->window == window)
+ {
+ actor = CLUTTER_ACTOR (key);
+ window_info = info;
+ break;
+ }
+ }
+
+ if (window_info == NULL)
+ return;
+
+ g_clear_signal_handler (&window_info->size_changed_id, window);
+ g_clear_signal_handler (&window_info->position_changed_id, window);
+ g_clear_signal_handler (&window_info->window_actor_destroy_id, window_info->window_actor);
+ g_clear_signal_handler (&window_info->destroy_id, actor);
+
+ g_hash_table_remove (priv->windows, actor);
+
+ clutter_actor_remove_child (priv->container, actor);
+
+ on_layout_changed (self);
+}
+
+/**
+ * shell_window_preview_layout_get_windows:
+ * @self: a #ShellWindowPreviewLayout
+ *
+ * Gets an array of all MetaWindows that were added to the layout
+ * using shell_window_preview_layout_add_window(), ordered by the
+ * insertion order.
+ *
+ * Returns: (transfer container) (element-type Meta.Window): The list of windows
+ */
+GList *
+shell_window_preview_layout_get_windows (ShellWindowPreviewLayout *self)
+{
+ ShellWindowPreviewLayoutPrivate *priv;
+ GList *windows = NULL;
+ GHashTableIter iter;
+ gpointer value;
+
+ g_return_val_if_fail (SHELL_IS_WINDOW_PREVIEW_LAYOUT (self), NULL);
+
+ priv = shell_window_preview_layout_get_instance_private (self);
+
+ g_hash_table_iter_init (&iter, priv->windows);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ WindowInfo *window_info = value;
+
+ windows = g_list_prepend (windows, window_info->window);
+ }
+
+ return windows;
+}
diff --git a/src/shell-window-preview-layout.h b/src/shell-window-preview-layout.h
new file mode 100644
index 0000000..9376b0d
--- /dev/null
+++ b/src/shell-window-preview-layout.h
@@ -0,0 +1,33 @@
+#ifndef __SHELL_WINDOW_PREVIEW_LAYOUT_H__
+#define __SHELL_WINDOW_PREVIEW_LAYOUT_H__
+
+G_BEGIN_DECLS
+
+#include <clutter/clutter.h>
+
+#define SHELL_TYPE_WINDOW_PREVIEW_LAYOUT (shell_window_preview_layout_get_type ())
+G_DECLARE_FINAL_TYPE (ShellWindowPreviewLayout, shell_window_preview_layout,
+ SHELL, WINDOW_PREVIEW_LAYOUT, ClutterLayoutManager)
+
+typedef struct _ShellWindowPreviewLayout ShellWindowPreviewLayout;
+typedef struct _ShellWindowPreviewLayoutPrivate ShellWindowPreviewLayoutPrivate;
+
+struct _ShellWindowPreviewLayout
+{
+ /*< private >*/
+ ClutterLayoutManager parent;
+
+ ShellWindowPreviewLayoutPrivate *priv;
+};
+
+ClutterActor * shell_window_preview_layout_add_window (ShellWindowPreviewLayout *self,
+ MetaWindow *window);
+
+void shell_window_preview_layout_remove_window (ShellWindowPreviewLayout *self,
+ MetaWindow *window);
+
+GList * shell_window_preview_layout_get_windows (ShellWindowPreviewLayout *self);
+
+G_END_DECLS
+
+#endif /* __SHELL_WINDOW_PREVIEW_LAYOUT_H__ */
diff --git a/src/shell-window-preview.c b/src/shell-window-preview.c
new file mode 100644
index 0000000..47a11c5
--- /dev/null
+++ b/src/shell-window-preview.c
@@ -0,0 +1,177 @@
+#include "config.h"
+
+#include "shell-window-preview.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_WINDOW_CONTAINER,
+
+ PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST] = { NULL, };
+
+struct _ShellWindowPreview
+{
+ /*< private >*/
+ StWidget parent_instance;
+
+ ClutterActor *window_container;
+};
+
+G_DEFINE_TYPE (ShellWindowPreview, shell_window_preview, ST_TYPE_WIDGET);
+
+static void
+shell_window_preview_get_property (GObject *gobject,
+ unsigned int property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (gobject);
+
+ switch (property_id)
+ {
+ case PROP_WINDOW_CONTAINER:
+ g_value_set_object (value, self->window_container);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+ }
+}
+
+static void
+shell_window_preview_set_property (GObject *gobject,
+ unsigned int property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (gobject);
+
+ switch (property_id)
+ {
+ case PROP_WINDOW_CONTAINER:
+ if (g_set_object (&self->window_container, g_value_get_object (value)))
+ g_object_notify_by_pspec (gobject, obj_props[PROP_WINDOW_CONTAINER]);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+ }
+}
+
+static void
+shell_window_preview_get_preferred_width (ClutterActor *actor,
+ float for_height,
+ float *min_width_p,
+ float *natural_width_p)
+{
+ ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (actor);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ float min_width, nat_width;
+
+ st_theme_node_adjust_for_height (theme_node, &for_height);
+
+ clutter_actor_get_preferred_width (self->window_container, for_height,
+ &min_width, &nat_width);
+
+ st_theme_node_adjust_preferred_width (theme_node, &min_width, &nat_width);
+
+ if (min_width_p)
+ *min_width_p = min_width;
+
+ if (natural_width_p)
+ *natural_width_p = nat_width;
+}
+
+static void
+shell_window_preview_get_preferred_height (ClutterActor *actor,
+ float for_width,
+ float *min_height_p,
+ float *natural_height_p)
+{
+ ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (actor);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ float min_height, nat_height;
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ clutter_actor_get_preferred_height (self->window_container, for_width,
+ &min_height, &nat_height);
+
+ st_theme_node_adjust_preferred_height (theme_node, &min_height, &nat_height);
+
+ if (min_height_p)
+ *min_height_p = min_height;
+
+ if (natural_height_p)
+ *natural_height_p = nat_height;
+}
+
+static void
+shell_window_preview_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ ClutterActorBox content_box;
+ float x, y, max_width, max_height;
+ ClutterActorIter iter;
+ ClutterActor *child;
+
+ clutter_actor_set_allocation (actor, box);
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ clutter_actor_box_get_origin (&content_box, &x, &y);
+ clutter_actor_box_get_size (&content_box, &max_width, &max_height);
+
+ clutter_actor_iter_init (&iter, actor);
+ while (clutter_actor_iter_next (&iter, &child))
+ clutter_actor_allocate_available_size (child, x, y, max_width, max_height);
+}
+
+static void
+shell_window_preview_dispose (GObject *gobject)
+{
+ ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (gobject);
+
+ g_clear_object (&self->window_container);
+
+ G_OBJECT_CLASS (shell_window_preview_parent_class)->dispose (gobject);
+}
+
+static void
+shell_window_preview_init (ShellWindowPreview *self)
+{
+}
+
+static void
+shell_window_preview_class_init (ShellWindowPreviewClass *klass)
+{
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ actor_class->get_preferred_width = shell_window_preview_get_preferred_width;
+ actor_class->get_preferred_height = shell_window_preview_get_preferred_height;
+ actor_class->allocate = shell_window_preview_allocate;
+
+ gobject_class->dispose = shell_window_preview_dispose;
+ gobject_class->get_property = shell_window_preview_get_property;
+ gobject_class->set_property = shell_window_preview_set_property;
+
+ /**
+ * ShellWindowPreview:window-container:
+ */
+ obj_props[PROP_WINDOW_CONTAINER] =
+ g_param_spec_object ("window-container",
+ "window-container",
+ "window-container",
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
+}
diff --git a/src/shell-window-preview.h b/src/shell-window-preview.h
new file mode 100644
index 0000000..e503800
--- /dev/null
+++ b/src/shell-window-preview.h
@@ -0,0 +1,14 @@
+#ifndef __SHELL_WINDOW_PREVIEW_H__
+#define __SHELL_WINDOW_PREVIEW_H__
+
+#include <st/st.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_WINDOW_PREVIEW (shell_window_preview_get_type ())
+G_DECLARE_FINAL_TYPE (ShellWindowPreview, shell_window_preview,
+ SHELL, WINDOW_PREVIEW, StWidget)
+
+G_END_DECLS
+
+#endif /* __SHELL_WINDOW_PREVIEW_H__ */
diff --git a/src/shell-window-tracker-private.h b/src/shell-window-tracker-private.h
new file mode 100644
index 0000000..4307d15
--- /dev/null
+++ b/src/shell-window-tracker-private.h
@@ -0,0 +1,11 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_WINDOW_TRACKER_PRIVATE_H__
+#define __SHELL_WINDOW_TRACKER_PRIVATE_H__
+
+#include "shell-window-tracker.h"
+
+void _shell_window_tracker_add_child_process_app (ShellWindowTracker *tracker,
+ GPid pid,
+ ShellApp *app);
+
+#endif
diff --git a/src/shell-window-tracker.c b/src/shell-window-tracker.c
new file mode 100644
index 0000000..dda9e2b
--- /dev/null
+++ b/src/shell-window-tracker.c
@@ -0,0 +1,811 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <meta/display.h>
+#include <meta/group.h>
+#include <meta/util.h>
+#include <meta/window.h>
+#include <meta/meta-workspace-manager.h>
+#include <meta/meta-startup-notification.h>
+
+#include "shell-window-tracker-private.h"
+#include "shell-app-private.h"
+#include "shell-global.h"
+#include "st.h"
+
+/* This file includes modified code from
+ * desktop-data-engine/engine-dbus/hippo-application-monitor.c
+ * in the functions collecting application usage data.
+ * Written by Owen Taylor, originally licensed under LGPL 2.1.
+ * Copyright Red Hat, Inc. 2006-2008
+ */
+
+/**
+ * SECTION:shell-window-tracker
+ * @short_description: Associate windows with applications
+ *
+ * Maintains a mapping from windows to applications (.desktop file ids).
+ * It currently implements this with some heuristics on the WM_CLASS X11
+ * property (and some static override regexps); in the future, we want to
+ * have it also track through startup-notification.
+ */
+
+struct _ShellWindowTracker
+{
+ GObject parent;
+
+ ShellApp *focus_app;
+
+ /* <MetaWindow * window, ShellApp *app> */
+ GHashTable *window_to_app;
+};
+
+G_DEFINE_TYPE (ShellWindowTracker, shell_window_tracker, G_TYPE_OBJECT);
+
+enum {
+ PROP_0,
+
+ PROP_FOCUS_APP,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum {
+ STARTUP_SEQUENCE_CHANGED,
+ TRACKED_WINDOWS_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void shell_window_tracker_finalize (GObject *object);
+static void set_focus_app (ShellWindowTracker *tracker,
+ ShellApp *new_focus_app);
+static void on_focus_window_changed (MetaDisplay *display, GParamSpec *spec, ShellWindowTracker *tracker);
+
+static void track_window (ShellWindowTracker *tracker, MetaWindow *window);
+static void disassociate_window (ShellWindowTracker *tracker, MetaWindow *window);
+
+static ShellApp * shell_startup_sequence_get_app (MetaStartupSequence *sequence);
+
+static void
+shell_window_tracker_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellWindowTracker *tracker = SHELL_WINDOW_TRACKER (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_FOCUS_APP:
+ g_value_set_object (value, tracker->focus_app);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+shell_window_tracker_class_init (ShellWindowTrackerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = shell_window_tracker_get_property;
+ gobject_class->finalize = shell_window_tracker_finalize;
+
+ props[PROP_FOCUS_APP] =
+ g_param_spec_object ("focus-app",
+ "Focus App",
+ "Focused application",
+ SHELL_TYPE_APP,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+
+ signals[STARTUP_SEQUENCE_CHANGED] = g_signal_new ("startup-sequence-changed",
+ SHELL_TYPE_WINDOW_TRACKER,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, META_TYPE_STARTUP_SEQUENCE);
+ signals[TRACKED_WINDOWS_CHANGED] = g_signal_new ("tracked-windows-changed",
+ SHELL_TYPE_WINDOW_TRACKER,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static gboolean
+check_app_id_prefix (ShellApp *app,
+ const char *prefix)
+{
+ if (prefix == NULL)
+ return TRUE;
+
+ return g_str_has_prefix (shell_app_get_id (app), prefix);
+}
+
+/*
+ * get_app_from_window_wmclass:
+ *
+ * Looks only at the given window, and attempts to determine
+ * an application based on WM_CLASS. If one can't be determined,
+ * return %NULL.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
+ */
+static ShellApp *
+get_app_from_window_wmclass (MetaWindow *window)
+{
+ ShellApp *app;
+ ShellAppSystem *appsys;
+ const char *wm_class;
+ const char *wm_instance;
+ const char *sandbox_id;
+ g_autofree char *app_prefix = NULL;
+
+ appsys = shell_app_system_get_default ();
+
+ sandbox_id = meta_window_get_sandboxed_app_id (window);
+ if (sandbox_id)
+ app_prefix = g_strdup_printf ("%s.", sandbox_id);
+
+ /* Notes on the heuristics used here:
+ much of the complexity here comes from the desire to support
+ Chrome apps.
+
+ From https://bugzilla.gnome.org/show_bug.cgi?id=673657#c13
+
+ Currently chrome sets WM_CLASS as follows (the first string is the 'instance',
+ the second one is the 'class':
+
+ For the normal browser:
+ WM_CLASS(STRING) = "chromium", "Chromium"
+
+ For a bookmarked page (through 'Tools -> Create application shortcuts')
+ WM_CLASS(STRING) = "wiki.gnome.org__GnomeShell_ApplicationBased", "Chromium"
+
+ For an application from the chrome store (with a .desktop file created through
+ right click, "Create shortcuts" from Chrome's apps overview)
+ WM_CLASS(STRING) = "crx_blpcfgokakmgnkcojhhkbfbldkacnbeo", "Chromium"
+
+ The .desktop file has a matching StartupWMClass, but the name differs, e.g. for
+ the store app (youtube) there is
+
+ .local/share/applications/chrome-blpcfgokakmgnkcojhhkbfbldkacnbeo-Default.desktop
+
+ with
+
+ StartupWMClass=crx_blpcfgokakmgnkcojhhkbfbldkacnbeo
+
+ Note that chromium (but not google-chrome!) includes a StartupWMClass=chromium
+ in their .desktop file, so we must match the instance first.
+
+ Also note that in the good case (regular gtk+ app without hacks), instance and
+ class are the same except for case and there is no StartupWMClass at all.
+ */
+
+ /* first try a match from WM_CLASS (instance part) to StartupWMClass */
+ wm_instance = meta_window_get_wm_class_instance (window);
+ app = shell_app_system_lookup_startup_wmclass (appsys, wm_instance);
+ if (app != NULL && check_app_id_prefix (app, app_prefix))
+ return g_object_ref (app);
+
+ /* then try a match from WM_CLASS to StartupWMClass */
+ wm_class = meta_window_get_wm_class (window);
+ app = shell_app_system_lookup_startup_wmclass (appsys, wm_class);
+ if (app != NULL && check_app_id_prefix (app, app_prefix))
+ return g_object_ref (app);
+
+ /* then try a match from WM_CLASS (instance part) to .desktop */
+ app = shell_app_system_lookup_desktop_wmclass (appsys, wm_instance);
+ if (app != NULL && check_app_id_prefix (app, app_prefix))
+ return g_object_ref (app);
+
+ /* finally, try a match from WM_CLASS to .desktop */
+ app = shell_app_system_lookup_desktop_wmclass (appsys, wm_class);
+ if (app != NULL && check_app_id_prefix (app, app_prefix))
+ return g_object_ref (app);
+
+ return NULL;
+}
+
+/*
+ * get_app_from_id:
+ * @window: a #MetaWindow
+ *
+ * Looks only at the given window, and attempts to determine
+ * an application based on %id. If one can't be determined,
+ * return %NULL.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
+ */
+static ShellApp *
+get_app_from_id (MetaWindow *window,
+ const char *id)
+{
+ ShellApp *app;
+ ShellAppSystem *appsys;
+ g_autofree char *desktop_file = NULL;
+
+ g_return_val_if_fail (id != NULL, NULL);
+
+ appsys = shell_app_system_get_default ();
+
+ desktop_file = g_strconcat (id, ".desktop", NULL);
+ app = shell_app_system_lookup_app (appsys, desktop_file);
+ if (app)
+ return g_object_ref (app);
+
+ return NULL;
+}
+
+/*
+ * get_app_from_gapplication_id:
+ * @window: a #MetaWindow
+ *
+ * Looks only at the given window, and attempts to determine
+ * an application based on _GTK_APPLICATION_ID. If one can't be determined,
+ * return %NULL.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
+ */
+static ShellApp *
+get_app_from_gapplication_id (MetaWindow *window)
+{
+ const char *id;
+
+ id = meta_window_get_gtk_application_id (window);
+ if (!id)
+ return NULL;
+
+ return get_app_from_id (window, id);
+}
+
+/*
+ * get_app_from_sandboxed_app_id:
+ * @window: a #MetaWindow
+ *
+ * Looks only at the given window, and attempts to determine
+ * an application based on its Flatpak or Snap ID. If one can't be determined,
+ * return %NULL.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
+ */
+static ShellApp *
+get_app_from_sandboxed_app_id (MetaWindow *window)
+{
+ const char *id;
+
+ id = meta_window_get_sandboxed_app_id (window);
+ if (!id)
+ return NULL;
+
+ return get_app_from_id (window, id);
+}
+
+/*
+ * get_app_from_window_group:
+ * @monitor: a #ShellWindowTracker
+ * @window: a #MetaWindow
+ *
+ * Check other windows in the group for @window to see if we have
+ * an application for one of them.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
+ */
+static ShellApp*
+get_app_from_window_group (ShellWindowTracker *tracker,
+ MetaWindow *window)
+{
+ ShellApp *result;
+ GSList *group_windows;
+ MetaGroup *group;
+ GSList *iter;
+
+ group = meta_window_get_group (window);
+ if (group == NULL)
+ return NULL;
+
+ group_windows = meta_group_list_windows (group);
+
+ result = NULL;
+ /* Try finding a window in the group of type NORMAL; if we
+ * succeed, use that as our source. */
+ for (iter = group_windows; iter; iter = iter->next)
+ {
+ MetaWindow *group_window = iter->data;
+
+ if (meta_window_get_window_type (group_window) != META_WINDOW_NORMAL)
+ continue;
+
+ result = g_hash_table_lookup (tracker->window_to_app, group_window);
+ if (result)
+ break;
+ }
+
+ g_slist_free (group_windows);
+
+ if (result)
+ g_object_ref (result);
+
+ return result;
+}
+
+/*
+ * get_app_from_window_pid:
+ * @tracker: a #ShellWindowTracker
+ * @window: a #MetaWindow
+ *
+ * Check if the pid associated with @window corresponds to an
+ * application.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
+ */
+static ShellApp *
+get_app_from_window_pid (ShellWindowTracker *tracker,
+ MetaWindow *window)
+{
+ ShellApp *result;
+ pid_t pid;
+
+ if (meta_window_is_remote (window))
+ return NULL;
+
+ pid = meta_window_get_pid (window);
+
+ if (pid < 1)
+ return NULL;
+
+ result = shell_window_tracker_get_app_from_pid (tracker, pid);
+ if (result != NULL)
+ g_object_ref (result);
+
+ return result;
+}
+
+/**
+ * get_app_for_window:
+ *
+ * Determines the application associated with a window, using
+ * all available information such as the window's MetaGroup,
+ * and what we know about other windows.
+ *
+ * Returns: (transfer full): a #ShellApp, or NULL if none is found
+ */
+static ShellApp *
+get_app_for_window (ShellWindowTracker *tracker,
+ MetaWindow *window)
+{
+ ShellApp *result = NULL;
+ MetaWindow *transient_for;
+ const char *startup_id;
+
+ transient_for = meta_window_get_transient_for (window);
+ if (transient_for != NULL)
+ return get_app_for_window (tracker, transient_for);
+
+ /* First, we check whether we already know about this window,
+ * if so, just return that.
+ */
+ if (meta_window_get_window_type (window) == META_WINDOW_NORMAL
+ || meta_window_is_remote (window))
+ {
+ result = g_hash_table_lookup (tracker->window_to_app, window);
+ if (result != NULL)
+ {
+ g_object_ref (result);
+ return result;
+ }
+ }
+
+ if (meta_window_is_remote (window))
+ return _shell_app_new_for_window (window);
+
+ /* Check if the app's WM_CLASS specifies an app; this is
+ * canonical if it does.
+ */
+ result = get_app_from_window_wmclass (window);
+ if (result != NULL)
+ return result;
+
+ /* Check if the window was opened from within a sandbox; if this
+ * is the case, a corresponding .desktop file is guaranteed to match;
+ */
+ result = get_app_from_sandboxed_app_id (window);
+ if (result != NULL)
+ return result;
+
+ /* Check if the window has a GApplication ID attached; this is
+ * canonical if it does
+ */
+ result = get_app_from_gapplication_id (window);
+ if (result != NULL)
+ return result;
+
+ result = get_app_from_window_pid (tracker, window);
+ if (result != NULL)
+ return result;
+
+ /* Now we check whether we have a match through startup-notification */
+ startup_id = meta_window_get_startup_id (window);
+ if (startup_id)
+ {
+ GSList *iter, *sequences;
+
+ sequences = shell_window_tracker_get_startup_sequences (tracker);
+ for (iter = sequences; iter; iter = iter->next)
+ {
+ MetaStartupSequence *sequence = iter->data;
+ const char *id = meta_startup_sequence_get_id (sequence);
+ if (strcmp (id, startup_id) != 0)
+ continue;
+
+ result = shell_startup_sequence_get_app (sequence);
+ if (result)
+ {
+ result = g_object_ref (result);
+ break;
+ }
+ }
+ }
+
+ /* If we didn't get a startup-notification match, see if we matched
+ * any other windows in the group.
+ */
+ if (result == NULL)
+ result = get_app_from_window_group (tracker, window);
+
+ /* Our last resort - we create a fake app from the window */
+ if (result == NULL)
+ result = _shell_app_new_for_window (window);
+
+ return result;
+}
+
+static void
+update_focus_app (ShellWindowTracker *self)
+{
+ MetaWindow *new_focus_win;
+ ShellApp *new_focus_app;
+
+ new_focus_win = meta_display_get_focus_window (shell_global_get_display (shell_global_get ()));
+
+ /* we only consider an app focused if the focus window can be clearly
+ * associated with a running app; this is the case if the focus window
+ * or one of its parents is visible in the taskbar, e.g.
+ * - 'nautilus' should appear focused when its about dialog has focus
+ * - 'nautilus' should not appear focused when the DESKTOP has focus
+ */
+ while (new_focus_win && meta_window_is_skip_taskbar (new_focus_win))
+ new_focus_win = meta_window_get_transient_for (new_focus_win);
+
+ new_focus_app = new_focus_win ? shell_window_tracker_get_window_app (self, new_focus_win) : NULL;
+
+ if (new_focus_app)
+ {
+ shell_app_update_window_actions (new_focus_app, new_focus_win);
+ shell_app_update_app_actions (new_focus_app, new_focus_win);
+ }
+
+ set_focus_app (self, new_focus_app);
+
+ g_clear_object (&new_focus_app);
+}
+
+static void
+tracked_window_changed (ShellWindowTracker *self,
+ MetaWindow *window)
+{
+ /* It's simplest to just treat this as a remove + add. */
+ disassociate_window (self, window);
+ track_window (self, window);
+ /* also just recalculate the focused app, in case it was the focused
+ window that changed */
+ update_focus_app (self);
+}
+
+static void
+on_wm_class_changed (MetaWindow *window,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
+ tracked_window_changed (self, window);
+}
+
+static void
+on_title_changed (MetaWindow *window,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
+ g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
+}
+
+static void
+on_gtk_application_id_changed (MetaWindow *window,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
+ tracked_window_changed (self, window);
+}
+
+static void
+on_window_unmanaged (MetaWindow *window,
+ gpointer user_data)
+{
+ disassociate_window (SHELL_WINDOW_TRACKER (user_data), window);
+}
+
+static void
+track_window (ShellWindowTracker *self,
+ MetaWindow *window)
+{
+ ShellApp *app;
+
+ app = get_app_for_window (self, window);
+ if (!app)
+ return;
+
+ /* At this point we've stored the association from window -> application */
+ g_hash_table_insert (self->window_to_app, window, app);
+
+ g_signal_connect (window, "notify::wm-class", G_CALLBACK (on_wm_class_changed), self);
+ g_signal_connect (window, "notify::title", G_CALLBACK (on_title_changed), self);
+ g_signal_connect (window, "notify::gtk-application-id", G_CALLBACK (on_gtk_application_id_changed), self);
+ g_signal_connect (window, "unmanaged", G_CALLBACK (on_window_unmanaged), self);
+
+ _shell_app_add_window (app, window);
+
+ g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
+}
+
+static void
+on_window_created (MetaDisplay *display,
+ MetaWindow *window,
+ gpointer user_data)
+{
+ track_window (SHELL_WINDOW_TRACKER (user_data), window);
+}
+
+static void
+disassociate_window (ShellWindowTracker *self,
+ MetaWindow *window)
+{
+ ShellApp *app;
+
+ app = g_hash_table_lookup (self->window_to_app, window);
+ if (!app)
+ return;
+
+ g_object_ref (app);
+
+ g_hash_table_remove (self->window_to_app, window);
+
+ _shell_app_remove_window (app, window);
+ g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_wm_class_changed), self);
+ g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_title_changed), self);
+ g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_gtk_application_id_changed), self);
+ g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_window_unmanaged), self);
+
+ g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
+
+ g_object_unref (app);
+}
+
+static void
+load_initial_windows (ShellWindowTracker *tracker)
+{
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+ g_autoptr (GList) windows = NULL;
+ GList *l;
+
+ windows = meta_display_list_all_windows (display);
+ for (l = windows; l; l = l->next)
+ track_window (tracker, l->data);
+}
+
+static void
+init_window_tracking (ShellWindowTracker *self)
+{
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+
+ g_signal_connect (display, "notify::focus-window",
+ G_CALLBACK (on_focus_window_changed), self);
+ g_signal_connect(display, "window-created",
+ G_CALLBACK (on_window_created), self);
+}
+
+static void
+on_startup_sequence_changed (MetaStartupNotification *sn,
+ MetaStartupSequence *sequence,
+ ShellWindowTracker *self)
+{
+ ShellApp *app;
+
+ app = shell_startup_sequence_get_app (sequence);
+ if (app)
+ _shell_app_handle_startup_sequence (app, sequence);
+
+ g_signal_emit (G_OBJECT (self), signals[STARTUP_SEQUENCE_CHANGED], 0, sequence);
+}
+
+static void
+shell_window_tracker_init (ShellWindowTracker *self)
+{
+ MetaDisplay *display = shell_global_get_display (shell_global_get ());
+ MetaStartupNotification *sn = meta_display_get_startup_notification (display);
+
+ self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_object_unref);
+
+
+ g_signal_connect (sn, "changed",
+ G_CALLBACK (on_startup_sequence_changed), self);
+
+ load_initial_windows (self);
+ init_window_tracking (self);
+}
+
+static void
+shell_window_tracker_finalize (GObject *object)
+{
+ ShellWindowTracker *self = SHELL_WINDOW_TRACKER (object);
+
+ g_hash_table_destroy (self->window_to_app);
+
+ G_OBJECT_CLASS (shell_window_tracker_parent_class)->finalize(object);
+}
+
+/**
+ * shell_window_tracker_get_window_app:
+ * @tracker: An app monitor instance
+ * @metawin: A #MetaWindow
+ *
+ * Returns: (transfer full): Application associated with window
+ */
+ShellApp *
+shell_window_tracker_get_window_app (ShellWindowTracker *tracker,
+ MetaWindow *metawin)
+{
+ ShellApp *app;
+
+ app = g_hash_table_lookup (tracker->window_to_app, metawin);
+ if (app)
+ g_object_ref (app);
+
+ return app;
+}
+
+
+/**
+ * shell_window_tracker_get_app_from_pid:
+ * @tracker: A #ShellAppSystem
+ * @pid: A Unix process identifier
+ *
+ * Look up the application corresponding to a process.
+ *
+ * Returns: (transfer none): A #ShellApp, or %NULL if none
+ */
+ShellApp *
+shell_window_tracker_get_app_from_pid (ShellWindowTracker *tracker,
+ int pid)
+{
+ GSList *running = shell_app_system_get_running (shell_app_system_get_default());
+ GSList *iter;
+ ShellApp *result = NULL;
+
+ for (iter = running; iter; iter = iter->next)
+ {
+ ShellApp *app = iter->data;
+ GSList *pids = shell_app_get_pids (app);
+ GSList *pids_iter;
+
+ for (pids_iter = pids; pids_iter; pids_iter = pids_iter->next)
+ {
+ int app_pid = GPOINTER_TO_INT (pids_iter->data);
+ if (app_pid == pid)
+ {
+ result = app;
+ break;
+ }
+ }
+ g_slist_free (pids);
+
+ if (result != NULL)
+ break;
+ }
+
+ g_slist_free (running);
+
+ return result;
+}
+
+static void
+set_focus_app (ShellWindowTracker *tracker,
+ ShellApp *new_focus_app)
+{
+ if (new_focus_app == tracker->focus_app)
+ return;
+
+ if (tracker->focus_app != NULL)
+ g_object_unref (tracker->focus_app);
+
+ tracker->focus_app = new_focus_app;
+
+ if (tracker->focus_app != NULL)
+ g_object_ref (tracker->focus_app);
+
+ g_object_notify_by_pspec (G_OBJECT (tracker), props[PROP_FOCUS_APP]);
+}
+
+static void
+on_focus_window_changed (MetaDisplay *display,
+ GParamSpec *spec,
+ ShellWindowTracker *tracker)
+{
+ update_focus_app (tracker);
+}
+
+/**
+ * shell_window_tracker_get_startup_sequences:
+ * @tracker:
+ *
+ * Returns: (transfer none) (element-type MetaStartupSequence): Currently active startup sequences
+ */
+GSList *
+shell_window_tracker_get_startup_sequences (ShellWindowTracker *self)
+{
+ ShellGlobal *global = shell_global_get ();
+ MetaDisplay *display = shell_global_get_display (global);
+ MetaStartupNotification *sn = meta_display_get_startup_notification (display);
+
+ return meta_startup_notification_get_sequences (sn);
+}
+
+static ShellApp *
+shell_startup_sequence_get_app (MetaStartupSequence *sequence)
+{
+ const char *appid;
+ char *basename;
+ ShellAppSystem *appsys;
+ ShellApp *app;
+
+ appid = meta_startup_sequence_get_application_id (sequence);
+ if (!appid)
+ return NULL;
+
+ basename = g_path_get_basename (appid);
+ appsys = shell_app_system_get_default ();
+ app = shell_app_system_lookup_app (appsys, basename);
+ g_free (basename);
+ return app;
+}
+
+/**
+ * shell_window_tracker_get_default:
+ *
+ * Return Value: (transfer none): The global #ShellWindowTracker instance
+ */
+ShellWindowTracker *
+shell_window_tracker_get_default (void)
+{
+ static ShellWindowTracker *instance;
+
+ if (instance == NULL)
+ instance = g_object_new (SHELL_TYPE_WINDOW_TRACKER, NULL);
+
+ return instance;
+}
diff --git a/src/shell-window-tracker.h b/src/shell-window-tracker.h
new file mode 100644
index 0000000..87c38f8
--- /dev/null
+++ b/src/shell-window-tracker.h
@@ -0,0 +1,29 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_WINDOW_TRACKER_H__
+#define __SHELL_WINDOW_TRACKER_H__
+
+#include <glib-object.h>
+#include <glib.h>
+#include <meta/window.h>
+#include <meta/meta-startup-notification.h>
+
+#include "shell-app.h"
+#include "shell-app-system.h"
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_WINDOW_TRACKER (shell_window_tracker_get_type ())
+G_DECLARE_FINAL_TYPE (ShellWindowTracker, shell_window_tracker,
+ SHELL, WINDOW_TRACKER, GObject)
+
+ShellWindowTracker* shell_window_tracker_get_default(void);
+
+ShellApp *shell_window_tracker_get_window_app (ShellWindowTracker *tracker, MetaWindow *metawin);
+
+ShellApp *shell_window_tracker_get_app_from_pid (ShellWindowTracker *tracker, int pid);
+
+GSList *shell_window_tracker_get_startup_sequences (ShellWindowTracker *tracker);
+
+G_END_DECLS
+
+#endif /* __SHELL_WINDOW_TRACKER_H__ */
diff --git a/src/shell-wm-private.h b/src/shell-wm-private.h
new file mode 100644
index 0000000..1363087
--- /dev/null
+++ b/src/shell-wm-private.h
@@ -0,0 +1,63 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_WM_PRIVATE_H__
+#define __SHELL_WM_PRIVATE_H__
+
+#include "shell-wm.h"
+
+G_BEGIN_DECLS
+
+/* These forward along the different effects from GnomeShellPlugin */
+
+void _shell_wm_minimize (ShellWM *wm,
+ MetaWindowActor *actor);
+void _shell_wm_unminimize (ShellWM *wm,
+ MetaWindowActor *actor);
+void _shell_wm_size_changed(ShellWM *wm,
+ MetaWindowActor *actor);
+void _shell_wm_size_change(ShellWM *wm,
+ MetaWindowActor *actor,
+ MetaSizeChange which_change,
+ MetaRectangle *old_frame_rect,
+ MetaRectangle *old_buffer_rect);
+void _shell_wm_map (ShellWM *wm,
+ MetaWindowActor *actor);
+void _shell_wm_destroy (ShellWM *wm,
+ MetaWindowActor *actor);
+
+void _shell_wm_switch_workspace (ShellWM *wm,
+ gint from,
+ gint to,
+ MetaMotionDirection direction);
+void _shell_wm_kill_window_effects (ShellWM *wm,
+ MetaWindowActor *actor);
+void _shell_wm_kill_switch_workspace (ShellWM *wm);
+
+void _shell_wm_show_tile_preview (ShellWM *wm,
+ MetaWindow *window,
+ MetaRectangle *tile_rect,
+ int tile_monitor);
+void _shell_wm_hide_tile_preview (ShellWM *wm);
+void _shell_wm_show_window_menu (ShellWM *wm,
+ MetaWindow *window,
+ MetaWindowMenuType menu,
+ int x,
+ int y);
+void _shell_wm_show_window_menu_for_rect (ShellWM *wm,
+ MetaWindow *window,
+ MetaWindowMenuType menu,
+ MetaRectangle *rect);
+
+gboolean _shell_wm_filter_keybinding (ShellWM *wm,
+ MetaKeyBinding *binding);
+
+void _shell_wm_confirm_display_change (ShellWM *wm);
+
+MetaCloseDialog * _shell_wm_create_close_dialog (ShellWM *wm,
+ MetaWindow *window);
+
+MetaInhibitShortcutsDialog * _shell_wm_create_inhibit_shortcuts_dialog (ShellWM *wm,
+ MetaWindow *window);
+
+G_END_DECLS
+
+#endif /* __SHELL_WM_PRIVATE_H__ */
diff --git a/src/shell-wm.c b/src/shell-wm.c
new file mode 100644
index 0000000..a11e419
--- /dev/null
+++ b/src/shell-wm.c
@@ -0,0 +1,462 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <meta/meta-enum-types.h>
+#include <meta/keybindings.h>
+
+#include "shell-wm-private.h"
+#include "shell-global.h"
+
+struct _ShellWM {
+ GObject parent;
+
+ MetaPlugin *plugin;
+};
+
+/* Signals */
+enum
+{
+ MINIMIZE,
+ UNMINIMIZE,
+ SIZE_CHANGED,
+ SIZE_CHANGE,
+ MAP,
+ DESTROY,
+ SWITCH_WORKSPACE,
+ KILL_SWITCH_WORKSPACE,
+ KILL_WINDOW_EFFECTS,
+ SHOW_TILE_PREVIEW,
+ HIDE_TILE_PREVIEW,
+ SHOW_WINDOW_MENU,
+ FILTER_KEYBINDING,
+ CONFIRM_DISPLAY_CHANGE,
+ CREATE_CLOSE_DIALOG,
+ CREATE_INHIBIT_SHORTCUTS_DIALOG,
+
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE(ShellWM, shell_wm, G_TYPE_OBJECT);
+
+static guint shell_wm_signals [LAST_SIGNAL] = { 0 };
+
+static void
+shell_wm_init (ShellWM *wm)
+{
+}
+
+static void
+shell_wm_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (shell_wm_parent_class)->finalize (object);
+}
+
+static void
+shell_wm_class_init (ShellWMClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = shell_wm_finalize;
+
+ shell_wm_signals[MINIMIZE] =
+ g_signal_new ("minimize",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WINDOW_ACTOR);
+ shell_wm_signals[UNMINIMIZE] =
+ g_signal_new ("unminimize",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WINDOW_ACTOR);
+ shell_wm_signals[SIZE_CHANGED] =
+ g_signal_new ("size-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WINDOW_ACTOR);
+ shell_wm_signals[SIZE_CHANGE] =
+ g_signal_new ("size-change",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 4,
+ META_TYPE_WINDOW_ACTOR, META_TYPE_SIZE_CHANGE, META_TYPE_RECTANGLE, META_TYPE_RECTANGLE);
+ shell_wm_signals[MAP] =
+ g_signal_new ("map",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WINDOW_ACTOR);
+ shell_wm_signals[DESTROY] =
+ g_signal_new ("destroy",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WINDOW_ACTOR);
+ shell_wm_signals[SWITCH_WORKSPACE] =
+ g_signal_new ("switch-workspace",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+ shell_wm_signals[KILL_SWITCH_WORKSPACE] =
+ g_signal_new ("kill-switch-workspace",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ shell_wm_signals[KILL_WINDOW_EFFECTS] =
+ g_signal_new ("kill-window-effects",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WINDOW_ACTOR);
+ shell_wm_signals[SHOW_TILE_PREVIEW] =
+ g_signal_new ("show-tile-preview",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 3,
+ META_TYPE_WINDOW,
+ META_TYPE_RECTANGLE,
+ G_TYPE_INT);
+ shell_wm_signals[HIDE_TILE_PREVIEW] =
+ g_signal_new ("hide-tile-preview",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ shell_wm_signals[SHOW_WINDOW_MENU] =
+ g_signal_new ("show-window-menu",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 3,
+ META_TYPE_WINDOW, G_TYPE_INT, META_TYPE_RECTANGLE);
+ shell_wm_signals[FILTER_KEYBINDING] =
+ g_signal_new ("filter-keybinding",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ g_signal_accumulator_true_handled, NULL, NULL,
+ G_TYPE_BOOLEAN, 1,
+ META_TYPE_KEY_BINDING);
+ shell_wm_signals[CONFIRM_DISPLAY_CHANGE] =
+ g_signal_new ("confirm-display-change",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ /**
+ * ShellWM::create-close-dialog:
+ * @wm: The WM
+ * @window: The window to create the dialog for
+ *
+ * Creates a close dialog for the given window.
+ *
+ * Returns: (transfer full): The close dialog instance.
+ */
+ shell_wm_signals[CREATE_CLOSE_DIALOG] =
+ g_signal_new ("create-close-dialog",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ META_TYPE_CLOSE_DIALOG, 1, META_TYPE_WINDOW);
+ /**
+ * ShellWM::create-inhibit-shortcuts-dialog:
+ * @wm: The WM
+ * @window: The window to create the dialog for
+ *
+ * Creates an inhibit shortcuts dialog for the given window.
+ *
+ * Returns: (transfer full): The inhibit shortcuts dialog instance.
+ */
+ shell_wm_signals[CREATE_INHIBIT_SHORTCUTS_DIALOG] =
+ g_signal_new ("create-inhibit-shortcuts-dialog",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ META_TYPE_INHIBIT_SHORTCUTS_DIALOG, 1, META_TYPE_WINDOW);
+}
+
+void
+_shell_wm_switch_workspace (ShellWM *wm,
+ gint from,
+ gint to,
+ MetaMotionDirection direction)
+{
+ g_signal_emit (wm, shell_wm_signals[SWITCH_WORKSPACE], 0,
+ from, to, direction);
+}
+
+/**
+ * shell_wm_completed_switch_workspace:
+ * @wm: the ShellWM
+ *
+ * The plugin must call this when it has finished switching the
+ * workspace.
+ **/
+void
+shell_wm_completed_switch_workspace (ShellWM *wm)
+{
+ meta_plugin_switch_workspace_completed (wm->plugin);
+}
+
+/**
+ * shell_wm_completed_minimize:
+ * @wm: the ShellWM
+ * @actor: the MetaWindowActor actor
+ *
+ * The plugin must call this when it has completed a window minimize effect.
+ **/
+void
+shell_wm_completed_minimize (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ meta_plugin_minimize_completed (wm->plugin, actor);
+}
+
+/**
+ * shell_wm_completed_unminimize:
+ * @wm: the ShellWM
+ * @actor: the MetaWindowActor actor
+ *
+ * The plugin must call this when it has completed a window unminimize effect.
+ **/
+void
+shell_wm_completed_unminimize (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ meta_plugin_unminimize_completed (wm->plugin, actor);
+}
+
+void
+shell_wm_completed_size_change (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ meta_plugin_size_change_completed (wm->plugin, actor);
+}
+
+/**
+ * shell_wm_completed_map:
+ * @wm: the ShellWM
+ * @actor: the MetaWindowActor actor
+ *
+ * The plugin must call this when it has completed a window map effect.
+ **/
+void
+shell_wm_completed_map (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ meta_plugin_map_completed (wm->plugin, actor);
+}
+
+/**
+ * shell_wm_completed_destroy:
+ * @wm: the ShellWM
+ * @actor: the MetaWindowActor actor
+ *
+ * The plugin must call this when it has completed a window destroy effect.
+ **/
+void
+shell_wm_completed_destroy (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ meta_plugin_destroy_completed (wm->plugin, actor);
+}
+
+/**
+ * shell_wm_complete_display_change:
+ * @wm: the ShellWM
+ * @ok: if the new configuration was OK
+ *
+ * The plugin must call this after the user responded to the confirmation dialog.
+ */
+void
+shell_wm_complete_display_change (ShellWM *wm,
+ gboolean ok)
+{
+ meta_plugin_complete_display_change (wm->plugin, ok);
+}
+
+void
+_shell_wm_kill_switch_workspace (ShellWM *wm)
+{
+ g_signal_emit (wm, shell_wm_signals[KILL_SWITCH_WORKSPACE], 0);
+}
+
+void
+_shell_wm_kill_window_effects (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ g_signal_emit (wm, shell_wm_signals[KILL_WINDOW_EFFECTS], 0, actor);
+}
+
+void
+_shell_wm_show_tile_preview (ShellWM *wm,
+ MetaWindow *window,
+ MetaRectangle *tile_rect,
+ int tile_monitor)
+{
+ g_signal_emit (wm, shell_wm_signals[SHOW_TILE_PREVIEW], 0,
+ window, tile_rect, tile_monitor);
+}
+
+void
+_shell_wm_hide_tile_preview (ShellWM *wm)
+{
+ g_signal_emit (wm, shell_wm_signals[HIDE_TILE_PREVIEW], 0);
+}
+
+void
+_shell_wm_show_window_menu (ShellWM *wm,
+ MetaWindow *window,
+ MetaWindowMenuType menu,
+ int x,
+ int y)
+{
+ MetaRectangle rect;
+
+ rect.x = x;
+ rect.y = y;
+ rect.width = rect.height = 0;
+
+ _shell_wm_show_window_menu_for_rect (wm, window, menu, &rect);
+}
+
+void
+_shell_wm_show_window_menu_for_rect (ShellWM *wm,
+ MetaWindow *window,
+ MetaWindowMenuType menu,
+ MetaRectangle *rect)
+{
+ g_signal_emit (wm, shell_wm_signals[SHOW_WINDOW_MENU], 0, window, menu, rect);
+}
+
+void
+_shell_wm_minimize (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ g_signal_emit (wm, shell_wm_signals[MINIMIZE], 0, actor);
+}
+
+void
+_shell_wm_unminimize (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ g_signal_emit (wm, shell_wm_signals[UNMINIMIZE], 0, actor);
+}
+
+void
+_shell_wm_size_changed (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ g_signal_emit (wm, shell_wm_signals[SIZE_CHANGED], 0, actor);
+}
+
+void
+_shell_wm_size_change (ShellWM *wm,
+ MetaWindowActor *actor,
+ MetaSizeChange which_change,
+ MetaRectangle *old_frame_rect,
+ MetaRectangle *old_buffer_rect)
+{
+ g_signal_emit (wm, shell_wm_signals[SIZE_CHANGE], 0, actor, which_change, old_frame_rect, old_buffer_rect);
+}
+
+void
+_shell_wm_map (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ g_signal_emit (wm, shell_wm_signals[MAP], 0, actor);
+}
+
+void
+_shell_wm_destroy (ShellWM *wm,
+ MetaWindowActor *actor)
+{
+ g_signal_emit (wm, shell_wm_signals[DESTROY], 0, actor);
+}
+
+gboolean
+_shell_wm_filter_keybinding (ShellWM *wm,
+ MetaKeyBinding *binding)
+{
+ gboolean rv;
+
+ g_signal_emit (wm, shell_wm_signals[FILTER_KEYBINDING], 0, binding, &rv);
+
+ return rv;
+}
+
+void
+_shell_wm_confirm_display_change (ShellWM *wm)
+{
+ g_signal_emit (wm, shell_wm_signals[CONFIRM_DISPLAY_CHANGE], 0);
+}
+
+MetaCloseDialog *
+_shell_wm_create_close_dialog (ShellWM *wm,
+ MetaWindow *window)
+{
+ MetaCloseDialog *dialog;
+
+ g_signal_emit (wm, shell_wm_signals[CREATE_CLOSE_DIALOG], 0, window, &dialog);
+
+ return dialog;
+}
+
+MetaInhibitShortcutsDialog *
+_shell_wm_create_inhibit_shortcuts_dialog (ShellWM *wm,
+ MetaWindow *window)
+{
+ MetaInhibitShortcutsDialog *dialog;
+
+ g_signal_emit (wm, shell_wm_signals[CREATE_INHIBIT_SHORTCUTS_DIALOG], 0, window, &dialog);
+
+ return dialog;
+}
+
+/**
+ * shell_wm_new:
+ * @plugin: the #MetaPlugin
+ *
+ * Creates a new window management interface by hooking into @plugin.
+ *
+ * Return value: the new window-management interface
+ **/
+ShellWM *
+shell_wm_new (MetaPlugin *plugin)
+{
+ ShellWM *wm;
+
+ wm = g_object_new (SHELL_TYPE_WM, NULL);
+ wm->plugin = plugin;
+
+ return wm;
+}
diff --git a/src/shell-wm.h b/src/shell-wm.h
new file mode 100644
index 0000000..ddfe095
--- /dev/null
+++ b/src/shell-wm.h
@@ -0,0 +1,32 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_WM_H__
+#define __SHELL_WM_H__
+
+#include <glib-object.h>
+#include <meta/meta-plugin.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_WM (shell_wm_get_type ())
+G_DECLARE_FINAL_TYPE (ShellWM, shell_wm, SHELL, WM, GObject)
+
+ShellWM *shell_wm_new (MetaPlugin *plugin);
+
+void shell_wm_completed_minimize (ShellWM *wm,
+ MetaWindowActor *actor);
+void shell_wm_completed_unminimize (ShellWM *wm,
+ MetaWindowActor *actor);
+void shell_wm_completed_size_change (ShellWM *wm,
+ MetaWindowActor *actor);
+void shell_wm_completed_map (ShellWM *wm,
+ MetaWindowActor *actor);
+void shell_wm_completed_destroy (ShellWM *wm,
+ MetaWindowActor *actor);
+void shell_wm_completed_switch_workspace (ShellWM *wm);
+
+void shell_wm_complete_display_change (ShellWM *wm,
+ gboolean ok);
+
+G_END_DECLS
+
+#endif /* __SHELL_WM_H__ */
diff --git a/src/shell-workspace-background.c b/src/shell-workspace-background.c
new file mode 100644
index 0000000..1c0bbc3
--- /dev/null
+++ b/src/shell-workspace-background.c
@@ -0,0 +1,226 @@
+#include "config.h"
+
+#include "shell-workspace-background.h"
+
+#include "shell-global.h"
+#include <meta/meta-workspace-manager.h>
+
+#define BACKGROUND_MARGIN 12
+
+enum
+{
+ PROP_0,
+
+ PROP_MONITOR_INDEX,
+ PROP_STATE_ADJUSTMENT_VALUE,
+
+ PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST] = { NULL, };
+
+struct _ShellWorkspaceBackground
+{
+ /*< private >*/
+ StWidget parent_instance;
+
+ int monitor_index;
+ double state_adjustment_value;
+
+ MetaRectangle work_area;
+ MetaRectangle monitor_geometry;
+};
+
+G_DEFINE_TYPE (ShellWorkspaceBackground, shell_workspace_background, ST_TYPE_WIDGET);
+
+static void
+on_workareas_changed (ShellWorkspaceBackground *self)
+{
+ ShellGlobal *global = shell_global_get ();
+ MetaDisplay *display = shell_global_get_display (global);
+ MetaWorkspaceManager *workspace_manager = shell_global_get_workspace_manager (global);
+ MetaWorkspace *workspace =
+ meta_workspace_manager_get_workspace_by_index (workspace_manager, 0);
+
+ meta_workspace_get_work_area_for_monitor (workspace,
+ self->monitor_index,
+ &self->work_area);
+
+ meta_display_get_monitor_geometry (display,
+ self->monitor_index,
+ &self->monitor_geometry);
+}
+
+static void
+shell_workspace_background_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ ShellWorkspaceBackground *self = SHELL_WORKSPACE_BACKGROUND (actor);
+ ShellGlobal *global = shell_global_get ();
+ ClutterStage *stage = shell_global_get_stage (global);
+ StThemeContext *context = st_theme_context_get_for_stage (stage);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ ClutterActorBox scaled_box, my_box, content_box;
+ ClutterActor *child;
+ float content_width, content_height;
+ float scaled_width, scaled_height;
+ float x_scale, y_scale;
+ float width, height;
+ int left_offset, right_offset;
+ int top_offset, bottom_offset;
+ int scale_factor;
+
+ scale_factor = st_theme_context_get_scale_factor (context);
+
+ clutter_actor_box_get_size (box, &width, &height);
+ scaled_height = height - BACKGROUND_MARGIN * 2 * scale_factor;
+ scaled_width = (scaled_height / height) * width;
+
+ scaled_box.x1 = box->x1 + (width - scaled_width) / 2;
+ scaled_box.y1 = box->y1 + (height - scaled_height) / 2;
+ clutter_actor_box_set_size (&scaled_box, scaled_width, scaled_height);
+
+ clutter_actor_box_interpolate(box, &scaled_box,
+ self->state_adjustment_value, &my_box);
+
+ clutter_actor_set_allocation (actor, &my_box);
+
+ st_theme_node_get_content_box (theme_node, &my_box, &content_box);
+
+ child = clutter_actor_get_first_child (actor);
+ clutter_actor_allocate (child, &content_box);
+
+ clutter_actor_box_get_size (&content_box, &content_width, &content_height);
+ x_scale = content_width / self->work_area.width;
+ y_scale = content_height / self->work_area.height;
+
+ left_offset = self->work_area.x - self->monitor_geometry.x;
+ top_offset = self->work_area.y - self->monitor_geometry.y;
+ right_offset = self->monitor_geometry.width - self->work_area.width - left_offset;
+ bottom_offset = self->monitor_geometry.height - self->work_area.height - top_offset;
+
+ clutter_actor_box_set_origin (&content_box,
+ -left_offset * x_scale,
+ -top_offset * y_scale);
+ clutter_actor_box_set_size (&content_box,
+ content_width + (left_offset + right_offset) * x_scale,
+ content_height + (top_offset + bottom_offset) * y_scale);
+
+ child = clutter_actor_get_first_child (child);
+ clutter_actor_allocate (child, &content_box);
+}
+
+static void
+shell_workspace_background_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (shell_workspace_background_parent_class)->constructed (object);
+
+ on_workareas_changed (SHELL_WORKSPACE_BACKGROUND (object));
+}
+
+static void
+shell_workspace_background_get_property (GObject *gobject,
+ unsigned int property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShellWorkspaceBackground *self = SHELL_WORKSPACE_BACKGROUND (gobject);
+
+ switch (property_id)
+ {
+ case PROP_MONITOR_INDEX:
+ g_value_set_int (value, self->monitor_index);
+ break;
+
+ case PROP_STATE_ADJUSTMENT_VALUE:
+ g_value_set_double (value, self->state_adjustment_value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+ }
+}
+
+static void
+shell_workspace_background_set_property (GObject *gobject,
+ unsigned int property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShellWorkspaceBackground *self = SHELL_WORKSPACE_BACKGROUND (gobject);
+
+ switch (property_id)
+ {
+ case PROP_MONITOR_INDEX:
+ {
+ int new_value = g_value_get_int (value);
+ if (self->monitor_index != new_value)
+ {
+ self->monitor_index = new_value;
+ g_object_notify_by_pspec (gobject, obj_props[PROP_MONITOR_INDEX]);
+ }
+ }
+ break;
+
+ case PROP_STATE_ADJUSTMENT_VALUE:
+ {
+ double new_value = g_value_get_double (value);
+ if (self->state_adjustment_value != new_value)
+ {
+ self->state_adjustment_value = new_value;
+ g_object_notify_by_pspec (gobject, obj_props[PROP_STATE_ADJUSTMENT_VALUE]);
+ }
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+ }
+}
+
+static void
+shell_workspace_background_class_init (ShellWorkspaceBackgroundClass *klass)
+{
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ actor_class->allocate = shell_workspace_background_allocate;
+
+ gobject_class->constructed = shell_workspace_background_constructed;
+ gobject_class->get_property = shell_workspace_background_get_property;
+ gobject_class->set_property = shell_workspace_background_set_property;
+
+ /**
+ * ShellWorkspaceBackground:monitor-index:
+ */
+ obj_props[PROP_MONITOR_INDEX] =
+ g_param_spec_int ("monitor-index", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * ShellWorkspaceBackground:state-adjustment-value:
+ */
+ obj_props[PROP_STATE_ADJUSTMENT_VALUE] =
+ g_param_spec_double ("state-adjustment-value", "", "",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
+}
+
+static void
+shell_workspace_background_init (ShellWorkspaceBackground *self)
+{
+ ShellGlobal *global = shell_global_get ();
+ MetaDisplay *display = shell_global_get_display (global);
+
+ g_signal_connect_object (display, "workareas-changed",
+ G_CALLBACK (on_workareas_changed),
+ self, G_CONNECT_SWAPPED);
+}
diff --git a/src/shell-workspace-background.h b/src/shell-workspace-background.h
new file mode 100644
index 0000000..48b2bd6
--- /dev/null
+++ b/src/shell-workspace-background.h
@@ -0,0 +1,14 @@
+#ifndef __SHELL_WORKSPACE_BACKGROUND_H__
+#define __SHELL_WORKSPACE_BACKGROUND_H__
+
+#include <st/st.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_WORKSPACE_BACKGROUND (shell_workspace_background_get_type ())
+G_DECLARE_FINAL_TYPE (ShellWorkspaceBackground, shell_workspace_background,
+ SHELL, WORKSPACE_BACKGROUND, StWidget)
+
+G_END_DECLS
+
+#endif /* __SHELL_WORKSPACE_BACKGROUND_H__ */
diff --git a/src/st/croco/cr-additional-sel.c b/src/st/croco/cr-additional-sel.c
new file mode 100644
index 0000000..9bd8d6a
--- /dev/null
+++ b/src/st/croco/cr-additional-sel.c
@@ -0,0 +1,498 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ *
+ */
+
+#include "cr-additional-sel.h"
+#include "string.h"
+
+/**
+ * CRAdditionalSel:
+ *
+ * #CRAdditionalSel abstracts an additional selector.
+ * An additional selector is the selector part
+ * that comes after the combination of type selectors.
+ * It can be either "a class selector (the .class part),
+ * a pseudo class selector, an attribute selector
+ * or an id selector.
+ */
+
+/**
+ * cr_additional_sel_new:
+ *
+ * Default constructor of #CRAdditionalSel.
+ * Returns the newly build instance of #CRAdditionalSel.
+ */
+CRAdditionalSel *
+cr_additional_sel_new (void)
+{
+ CRAdditionalSel *result = NULL;
+
+ result = g_try_malloc (sizeof (CRAdditionalSel));
+
+ if (result == NULL) {
+ cr_utils_trace_debug ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRAdditionalSel));
+
+ return result;
+}
+
+/**
+ * cr_additional_sel_new_with_type:
+ * @a_sel_type: the type of the newly built instance
+ * of #CRAdditionalSel.
+ *
+ * Constructor of #CRAdditionalSel.
+ * Returns the newly built instance of #CRAdditionalSel.
+ */
+CRAdditionalSel *
+cr_additional_sel_new_with_type (enum AddSelectorType a_sel_type)
+{
+ CRAdditionalSel *result = NULL;
+
+ result = cr_additional_sel_new ();
+
+ g_return_val_if_fail (result, NULL);
+
+ result->type = a_sel_type;
+
+ return result;
+}
+
+/**
+ * cr_additional_sel_set_class_name:
+ * @a_this: the "this pointer" of the current instance
+ * of #CRAdditionalSel .
+ * @a_class_name: the new class name to set.
+ *
+ * Sets a new class name to a
+ * CLASS additional selector.
+ */
+void
+cr_additional_sel_set_class_name (CRAdditionalSel * a_this,
+ CRString * a_class_name)
+{
+ g_return_if_fail (a_this && a_this->type == CLASS_ADD_SELECTOR);
+
+ if (a_this->content.class_name) {
+ cr_string_destroy (a_this->content.class_name);
+ }
+
+ a_this->content.class_name = a_class_name;
+}
+
+/**
+ * cr_additional_sel_set_id_name:
+ * @a_this: the "this pointer" of the current instance
+ * of #CRAdditionalSel .
+ * @a_id: the new id to set.
+ *
+ * Sets a new id name to an
+ * ID additional selector.
+ */
+void
+cr_additional_sel_set_id_name (CRAdditionalSel * a_this, CRString * a_id)
+{
+ g_return_if_fail (a_this && a_this->type == ID_ADD_SELECTOR);
+
+ if (a_this->content.id_name) {
+ cr_string_destroy (a_this->content.id_name);
+ }
+
+ a_this->content.id_name = a_id;
+}
+
+/**
+ * cr_additional_sel_set_pseudo:
+ * @a_this: the "this pointer" of the current instance
+ * of #CRAdditionalSel .
+ * @a_pseudo: the new pseudo to set.
+ *
+ * Sets a new pseudo to a
+ * PSEUDO additional selector.
+ */
+void
+cr_additional_sel_set_pseudo (CRAdditionalSel * a_this, CRPseudo * a_pseudo)
+{
+ g_return_if_fail (a_this
+ && a_this->type == PSEUDO_CLASS_ADD_SELECTOR);
+
+ if (a_this->content.pseudo) {
+ cr_pseudo_destroy (a_this->content.pseudo);
+ }
+
+ a_this->content.pseudo = a_pseudo;
+}
+
+/**
+ * cr_additional_sel_set_attr_sel:
+ * @a_this: the "this pointer" of the current instance
+ * of #CRAdditionalSel .
+ * @a_sel: the new instance of #CRAttrSel to set.
+ *
+ * Sets a new instance of #CRAttrSel to
+ * a ATTRIBUTE additional selector.
+ */
+void
+cr_additional_sel_set_attr_sel (CRAdditionalSel * a_this, CRAttrSel * a_sel)
+{
+ g_return_if_fail (a_this && a_this->type == ATTRIBUTE_ADD_SELECTOR);
+
+ if (a_this->content.attr_sel) {
+ cr_attr_sel_destroy (a_this->content.attr_sel);
+ }
+
+ a_this->content.attr_sel = a_sel;
+}
+
+/**
+ * cr_additional_sel_append:
+ * @a_this: the "this pointer" of the current instance
+ * of #CRAdditionalSel .
+ * @a_sel: the new instance to #CRAdditional to append.
+ *
+ * Appends a new instance of #CRAdditional to the
+ * current list of #CRAdditional.
+ *
+ * Returns the new list of CRAdditionalSel or NULL if an error arises.
+ */
+CRAdditionalSel *
+cr_additional_sel_append (CRAdditionalSel * a_this, CRAdditionalSel * a_sel)
+{
+ CRAdditionalSel *cur_sel = NULL;
+
+ g_return_val_if_fail (a_sel, NULL);
+
+ if (a_this == NULL) {
+ return a_sel;
+ }
+
+ if (a_sel == NULL)
+ return NULL;
+
+ for (cur_sel = a_this;
+ cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
+
+ g_return_val_if_fail (cur_sel != NULL, NULL);
+
+ cur_sel->next = a_sel;
+ a_sel->prev = cur_sel;
+
+ return a_this;
+}
+
+/**
+ * cr_additional_sel_prepend:
+ * @a_this: the "this pointer" of the current instance
+ * of #CRAdditionalSel .
+ * @a_sel: the new instance to #CRAdditional to preappend.
+ *
+ * Preppends a new instance of #CRAdditional to the
+ * current list of #CRAdditional.
+ *
+ * Returns the new list of CRAdditionalSel or NULL if an error arises.
+ */
+CRAdditionalSel *
+cr_additional_sel_prepend (CRAdditionalSel * a_this, CRAdditionalSel * a_sel)
+{
+ g_return_val_if_fail (a_sel, NULL);
+
+ if (a_this == NULL) {
+ return a_sel;
+ }
+
+ a_sel->next = a_this;
+ a_this->prev = a_sel;
+
+ return a_sel;
+}
+
+guchar *
+cr_additional_sel_to_string (CRAdditionalSel const * a_this)
+{
+ guchar *result = NULL;
+ GString *str_buf = NULL;
+ CRAdditionalSel const *cur = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ str_buf = g_string_new (NULL);
+
+ for (cur = a_this; cur; cur = cur->next) {
+ switch (cur->type) {
+ case CLASS_ADD_SELECTOR:
+ {
+ guchar *name = NULL;
+
+ if (cur->content.class_name) {
+ name = (guchar *) g_strndup
+ (cur->content.class_name->stryng->str,
+ cur->content.class_name->stryng->len);
+
+ if (name) {
+ g_string_append_printf
+ (str_buf, ".%s",
+ name);
+ g_free (name);
+ name = NULL;
+ }
+ }
+ }
+ break;
+
+ case ID_ADD_SELECTOR:
+ {
+ guchar *name = NULL;
+
+ if (cur->content.id_name) {
+ name = (guchar *) g_strndup
+ (cur->content.id_name->stryng->str,
+ cur->content.id_name->stryng->len);
+
+ if (name) {
+ g_string_append_printf
+ (str_buf, "#%s",
+ name);
+ g_free (name);
+ name = NULL;
+ }
+ }
+ }
+
+ break;
+
+ case PSEUDO_CLASS_ADD_SELECTOR:
+ {
+ if (cur->content.pseudo) {
+ guchar *tmp_str = NULL;
+
+ tmp_str = cr_pseudo_to_string
+ (cur->content.pseudo);
+ if (tmp_str) {
+ g_string_append_printf
+ (str_buf, ":%s",
+ tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ }
+ break;
+
+ case ATTRIBUTE_ADD_SELECTOR:
+ if (cur->content.attr_sel) {
+ guchar *tmp_str = NULL;
+
+ g_string_append_c (str_buf, '[');
+ tmp_str = cr_attr_sel_to_string
+ (cur->content.attr_sel);
+ if (tmp_str) {
+ g_string_append_printf
+ (str_buf, "%s]", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+}
+
+guchar *
+cr_additional_sel_one_to_string (CRAdditionalSel const *a_this)
+{
+ guchar *result = NULL;
+ GString *str_buf = NULL;
+
+ g_return_val_if_fail (a_this, NULL) ;
+
+ str_buf = g_string_new (NULL) ;
+
+ switch (a_this->type) {
+ case CLASS_ADD_SELECTOR:
+ {
+ guchar *name = NULL;
+
+ if (a_this->content.class_name) {
+ name = (guchar *) g_strndup
+ (a_this->content.class_name->stryng->str,
+ a_this->content.class_name->stryng->len);
+
+ if (name) {
+ g_string_append_printf
+ (str_buf, ".%s",
+ name);
+ g_free (name);
+ name = NULL;
+ }
+ }
+ }
+ break;
+
+ case ID_ADD_SELECTOR:
+ {
+ guchar *name = NULL;
+
+ if (a_this->content.id_name) {
+ name = (guchar *) g_strndup
+ (a_this->content.id_name->stryng->str,
+ a_this->content.id_name->stryng->len);
+
+ if (name) {
+ g_string_append_printf
+ (str_buf, "#%s",
+ name);
+ g_free (name);
+ name = NULL;
+ }
+ }
+ }
+
+ break;
+
+ case PSEUDO_CLASS_ADD_SELECTOR:
+ {
+ if (a_this->content.pseudo) {
+ guchar *tmp_str = NULL;
+
+ tmp_str = cr_pseudo_to_string
+ (a_this->content.pseudo);
+ if (tmp_str) {
+ g_string_append_printf
+ (str_buf, ":%s",
+ tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ }
+ break;
+
+ case ATTRIBUTE_ADD_SELECTOR:
+ if (a_this->content.attr_sel) {
+ guchar *tmp_str = NULL;
+
+ g_string_append_printf (str_buf, "[");
+ tmp_str = cr_attr_sel_to_string
+ (a_this->content.attr_sel);
+ if (tmp_str) {
+ g_string_append_printf
+ (str_buf, "%s]", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_additional_sel_dump:
+ * @a_this: the "this pointer" of the current instance of
+ * #CRAdditionalSel.
+ * @a_fp: the destination file.
+ *
+ * Dumps the current instance of #CRAdditionalSel to a file
+ */
+void
+cr_additional_sel_dump (CRAdditionalSel const * a_this, FILE * a_fp)
+{
+ guchar *tmp_str = NULL;
+
+ g_return_if_fail (a_fp);
+
+ if (a_this) {
+ tmp_str = cr_additional_sel_to_string (a_this);
+ if (tmp_str) {
+ fprintf (a_fp, "%s", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+}
+
+/**
+ * cr_additional_sel_destroy:
+ * @a_this: the "this pointer" of the current instance
+ * of #CRAdditionalSel .
+ *
+ * Destroys an instance of #CRAdditional.
+ */
+void
+cr_additional_sel_destroy (CRAdditionalSel * a_this)
+{
+ g_return_if_fail (a_this);
+
+ switch (a_this->type) {
+ case CLASS_ADD_SELECTOR:
+ cr_string_destroy (a_this->content.class_name);
+ a_this->content.class_name = NULL;
+ break;
+
+ case PSEUDO_CLASS_ADD_SELECTOR:
+ cr_pseudo_destroy (a_this->content.pseudo);
+ a_this->content.pseudo = NULL;
+ break;
+
+ case ID_ADD_SELECTOR:
+ cr_string_destroy (a_this->content.id_name);
+ a_this->content.id_name = NULL;
+ break;
+
+ case ATTRIBUTE_ADD_SELECTOR:
+ cr_attr_sel_destroy (a_this->content.attr_sel);
+ a_this->content.attr_sel = NULL;
+ break;
+
+ default:
+ break;
+ }
+
+ if (a_this->next) {
+ cr_additional_sel_destroy (a_this->next);
+ }
+
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-additional-sel.h b/src/st/croco/cr-additional-sel.h
new file mode 100644
index 0000000..7ca3e07
--- /dev/null
+++ b/src/st/croco/cr-additional-sel.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See the COPYRIGHTS file for copyright information.
+ */
+
+
+#ifndef __CR_ADD_SEL_H__
+#define __CR_ADD_SEL_H__
+
+#include <stdio.h>
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-attr-sel.h"
+#include "cr-pseudo.h"
+#include "cr-additional-sel.h"
+
+G_BEGIN_DECLS
+
+enum AddSelectorType
+{
+ NO_ADD_SELECTOR = 0 ,
+ CLASS_ADD_SELECTOR = 1 ,
+ PSEUDO_CLASS_ADD_SELECTOR = 1 << 1,
+ ID_ADD_SELECTOR = 1 << 3,
+ ATTRIBUTE_ADD_SELECTOR = 1 << 4
+} ;
+
+union CRAdditionalSelectorContent
+{
+ CRString *class_name ;
+ CRString *id_name ;
+ CRPseudo *pseudo ;
+ CRAttrSel *attr_sel ;
+} ;
+
+typedef struct _CRAdditionalSel CRAdditionalSel ;
+
+struct _CRAdditionalSel
+{
+ enum AddSelectorType type ;
+ union CRAdditionalSelectorContent content ;
+
+ CRAdditionalSel * next ;
+ CRAdditionalSel * prev ;
+ CRParsingLocation location ;
+} ;
+
+CRAdditionalSel * cr_additional_sel_new (void) ;
+
+CRAdditionalSel * cr_additional_sel_new_with_type (enum AddSelectorType a_sel_type) ;
+
+CRAdditionalSel * cr_additional_sel_append (CRAdditionalSel *a_this,
+ CRAdditionalSel *a_sel) ;
+
+void cr_additional_sel_set_class_name (CRAdditionalSel *a_this,
+ CRString *a_class_name) ;
+
+void cr_additional_sel_set_id_name (CRAdditionalSel *a_this,
+ CRString *a_id) ;
+
+void cr_additional_sel_set_pseudo (CRAdditionalSel *a_this,
+ CRPseudo *a_pseudo) ;
+
+void cr_additional_sel_set_attr_sel (CRAdditionalSel *a_this,
+ CRAttrSel *a_sel) ;
+
+CRAdditionalSel * cr_additional_sel_prepend (CRAdditionalSel *a_this,
+ CRAdditionalSel *a_sel) ;
+
+guchar * cr_additional_sel_to_string (CRAdditionalSel const *a_this) ;
+
+guchar * cr_additional_sel_one_to_string (CRAdditionalSel const *a_this) ;
+
+void cr_additional_sel_dump (CRAdditionalSel const *a_this, FILE *a_fp) ;
+
+void cr_additional_sel_destroy (CRAdditionalSel *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_ADD_SEL_H*/
diff --git a/src/st/croco/cr-attr-sel.c b/src/st/croco/cr-attr-sel.c
new file mode 100644
index 0000000..fc8e6ef
--- /dev/null
+++ b/src/st/croco/cr-attr-sel.c
@@ -0,0 +1,234 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See COPYRIGHTS file for copyrights information.
+ */
+
+#include <stdio.h>
+#include "cr-attr-sel.h"
+
+/**
+ * CRAttrSel:
+ *
+ * #CRAdditionalSel abstracts an attribute selector.
+ * Attributes selectors are described in the css2 spec [5.8].
+ * There are more generally used in the css2 selectors described in
+ * css2 spec [5] .
+ */
+
+/**
+ * cr_attr_sel_new:
+ * The constructor of #CRAttrSel.
+ * Returns the newly allocated instance
+ * of #CRAttrSel.
+ */
+CRAttrSel *
+cr_attr_sel_new (void)
+{
+ CRAttrSel *result = NULL;
+
+ result = g_malloc0 (sizeof (CRAttrSel));
+
+ return result;
+}
+
+/**
+ * cr_attr_sel_append_attr_sel:
+ * @a_this: the this pointer of the current instance of #CRAttrSel.
+ * @a_attr_sel: selector to append.
+ *
+ * Appends an attribute selector to the current list of
+ * attribute selectors represented by a_this.
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_attr_sel_append_attr_sel (CRAttrSel * a_this, CRAttrSel * a_attr_sel)
+{
+ CRAttrSel *cur_sel = NULL;
+
+ g_return_val_if_fail (a_this && a_attr_sel,
+ CR_BAD_PARAM_ERROR);
+
+ for (cur_sel = a_this;
+ cur_sel->next;
+ cur_sel = cur_sel->next) ;
+
+ cur_sel->next = a_attr_sel;
+ a_attr_sel->prev = cur_sel;
+
+ return CR_OK;
+}
+
+/**
+ * cr_attr_sel_prepend_attr_sel:
+ *@a_this: the "this pointer" of the current instance *of #CRAttrSel.
+ *@a_attr_sel: the attribute selector to append.
+ *
+ *Prepends an attribute selector to the list of
+ *attributes selector represented by a_this.
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_attr_sel_prepend_attr_sel (CRAttrSel * a_this,
+ CRAttrSel * a_attr_sel)
+{
+ g_return_val_if_fail (a_this && a_attr_sel,
+ CR_BAD_PARAM_ERROR);
+
+ a_attr_sel->next = a_this;
+ a_this->prev = a_attr_sel;
+
+ return CR_OK;
+}
+
+/**
+ * cr_attr_sel_to_string:
+ * @a_this: the current instance of #CRAttrSel.
+ *
+ * Serializes an attribute selector into a string
+ * Returns the serialized attribute selector.
+ */
+guchar *
+cr_attr_sel_to_string (CRAttrSel const * a_this)
+{
+ CRAttrSel const *cur = NULL;
+ guchar *result = NULL;
+ GString *str_buf = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ str_buf = g_string_new (NULL);
+
+ for (cur = a_this; cur; cur = cur->next) {
+ if (cur->prev) {
+ g_string_append_c (str_buf, ' ');
+ }
+
+ if (cur->name) {
+ guchar *name = NULL;
+
+ name = (guchar *) g_strndup (cur->name->stryng->str,
+ cur->name->stryng->len);
+ if (name) {
+ g_string_append (str_buf, (const gchar *) name);
+ g_free (name);
+ name = NULL;
+ }
+ }
+
+ if (cur->value) {
+ guchar *value = NULL;
+
+ value = (guchar *) g_strndup (cur->value->stryng->str,
+ cur->value->stryng->len);
+ if (value) {
+ switch (cur->match_way) {
+ case SET:
+ break;
+
+ case EQUALS:
+ g_string_append_c (str_buf, '=');
+ break;
+
+ case INCLUDES:
+ g_string_append (str_buf, "~=");
+ break;
+
+ case DASHMATCH:
+ g_string_append (str_buf, "|=");
+ break;
+
+ default:
+ break;
+ }
+
+ g_string_append_printf
+ (str_buf, "\"%s\"", value);
+
+ g_free (value);
+ value = NULL;
+ }
+ }
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ }
+
+ return result;
+}
+
+/**
+ * cr_attr_sel_dump:
+ * @a_this: the "this pointer" of the current instance of
+ * #CRAttrSel.
+ * @a_fp: the destination file.
+ *
+ * Dumps the current instance of #CRAttrSel to a file.
+ */
+void
+cr_attr_sel_dump (CRAttrSel const * a_this, FILE * a_fp)
+{
+ guchar *tmp_str = NULL;
+
+ g_return_if_fail (a_this);
+
+ tmp_str = cr_attr_sel_to_string (a_this);
+
+ if (tmp_str) {
+ fprintf (a_fp, "%s", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+}
+
+/**
+ *cr_attr_sel_destroy:
+ *@a_this: the "this pointer" of the current
+ *instance of #CRAttrSel.
+ *
+ *Destroys the current instance of #CRAttrSel.
+ *Frees all the fields if they are non null.
+ */
+void
+cr_attr_sel_destroy (CRAttrSel * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->name) {
+ cr_string_destroy (a_this->name);
+ a_this->name = NULL;
+ }
+
+ if (a_this->value) {
+ cr_string_destroy (a_this->value);
+ a_this->value = NULL;
+ }
+
+ if (a_this->next) {
+ cr_attr_sel_destroy (a_this->next);
+ a_this->next = NULL;
+ }
+
+ if (a_this) {
+ g_free (a_this);
+ a_this = NULL;
+ }
+}
+
diff --git a/src/st/croco/cr-attr-sel.h b/src/st/croco/cr-attr-sel.h
new file mode 100644
index 0000000..82d5a87
--- /dev/null
+++ b/src/st/croco/cr-attr-sel.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_ATTR_SEL_H__
+#define __CR_ATTR_SEL_H__
+
+#include <stdio.h>
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-parsing-location.h"
+#include "cr-string.h"
+
+G_BEGIN_DECLS
+
+
+struct _CRAttrSel ;
+typedef struct _CRAttrSel CRAttrSel ;
+
+enum AttrMatchWay
+{
+ NO_MATCH = 0,
+ SET,
+ EQUALS,
+ INCLUDES,
+ DASHMATCH
+} ;
+
+struct _CRAttrSel
+{
+ CRString *name ;
+ CRString *value ;
+ enum AttrMatchWay match_way ;
+ CRAttrSel *next ;
+ CRAttrSel *prev ;
+ CRParsingLocation location ;
+} ;
+
+CRAttrSel * cr_attr_sel_new (void) ;
+
+enum CRStatus cr_attr_sel_append_attr_sel (CRAttrSel * a_this,
+ CRAttrSel *a_attr_sel) ;
+
+enum CRStatus cr_attr_sel_prepend_attr_sel (CRAttrSel *a_this,
+ CRAttrSel *a_attr_sel) ;
+
+guchar * cr_attr_sel_to_string (CRAttrSel const *a_this) ;
+
+void cr_attr_sel_dump (CRAttrSel const *a_this, FILE *a_fp) ;
+
+void cr_attr_sel_destroy (CRAttrSel *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_ATTR_SEL_H__*/
diff --git a/src/st/croco/cr-cascade.c b/src/st/croco/cr-cascade.c
new file mode 100644
index 0000000..68f59bb
--- /dev/null
+++ b/src/st/croco/cr-cascade.c
@@ -0,0 +1,215 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the
+ * GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the
+ * GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ *$Id$
+ */
+
+#include <string.h>
+#include "cr-cascade.h"
+
+#define PRIVATE(a_this) ((a_this)->priv)
+
+struct _CRCascadePriv {
+ /**
+ *the 3 style sheets of the cascade:
+ *author, user, and useragent sheet.
+ *Intended to be addressed by
+ *sheets[ORIGIN_AUTHOR] or sheets[ORIGIN_USER]
+ *of sheets[ORIGIN_UA] ;
+ */
+ CRStyleSheet *sheets[3];
+ guint ref_count;
+};
+
+/**
+ * cr_cascade_new:
+ *@a_author_sheet: the author origin style sheet. May be NULL.
+ *@a_user_sheet: the user origin style sheet. May be NULL.
+ *@a_ua_sheet: the user agent origin style sheet. May be NULL.
+ *
+ *Constructor of the #CRCascade class.
+ *Note that all three parameters of this
+ *method are ref counted and their refcount is increased.
+ *Their refcount will be decreased at the destruction of
+ *the instance of #CRCascade.
+ *So the caller should not call their destructor. The caller
+ *should call their ref/unref method instead if it wants
+ *
+ *Returns the newly built instance of CRCascade or NULL if
+ *an error arose during construction.
+ */
+CRCascade *
+cr_cascade_new (CRStyleSheet * a_author_sheet,
+ CRStyleSheet * a_user_sheet, CRStyleSheet * a_ua_sheet)
+{
+ CRCascade *result = NULL;
+
+ result = g_try_malloc (sizeof (CRCascade));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRCascade));
+
+ PRIVATE (result) = g_try_malloc (sizeof (CRCascadePriv));
+ if (!PRIVATE (result)) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+ memset (PRIVATE (result), 0, sizeof (CRCascadePriv));
+
+ if (a_author_sheet) {
+ cr_cascade_set_sheet (result, a_author_sheet, ORIGIN_AUTHOR);
+ }
+ if (a_user_sheet) {
+ cr_cascade_set_sheet (result, a_user_sheet, ORIGIN_USER);
+ }
+ if (a_ua_sheet) {
+ cr_cascade_set_sheet (result, a_ua_sheet, ORIGIN_UA);
+ }
+
+ return result;
+}
+
+/**
+ * cr_cascade_get_sheet:
+ *@a_this: the current instance of #CRCascade.
+ *@a_origin: the origin of the style sheet as
+ *defined in the css2 spec in chapter 6.4.
+ *Gets a given origin sheet.
+ *
+ *Gets a sheet, part of the cascade.
+ *Note that the returned stylesheet
+ *is refcounted so if the caller wants
+ *to manage it's lifecycle, it must use
+ *cr_stylesheet_ref()/cr_stylesheet_unref() instead
+ *of the cr_stylesheet_destroy() method.
+ *Returns the style sheet, or NULL if it does not
+ *exist.
+ */
+CRStyleSheet *
+cr_cascade_get_sheet (CRCascade * a_this, enum CRStyleOrigin a_origin)
+{
+ g_return_val_if_fail (a_this
+ && a_origin >= ORIGIN_UA
+ && a_origin < NB_ORIGINS, NULL);
+
+ return PRIVATE (a_this)->sheets[a_origin];
+}
+
+/**
+ * cr_cascade_set_sheet:
+ *@a_this: the current instance of #CRCascade.
+ *@a_sheet: the stylesheet to set.
+ *@a_origin: the origin of the stylesheet.
+ *
+ *Sets a stylesheet in the cascade
+ *
+ *Returns CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_cascade_set_sheet (CRCascade * a_this,
+ CRStyleSheet * a_sheet, enum CRStyleOrigin a_origin)
+{
+ g_return_val_if_fail (a_this
+ && a_sheet
+ && a_origin >= ORIGIN_UA
+ && a_origin < NB_ORIGINS, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->sheets[a_origin])
+ cr_stylesheet_unref (PRIVATE (a_this)->sheets[a_origin]);
+ PRIVATE (a_this)->sheets[a_origin] = a_sheet;
+ cr_stylesheet_ref (a_sheet);
+ a_sheet->origin = a_origin;
+ return CR_OK;
+}
+
+/**
+ *cr_cascade_ref:
+ *@a_this: the current instance of #CRCascade
+ *
+ *Increases the reference counter of the current instance
+ *of #CRCascade.
+ */
+void
+cr_cascade_ref (CRCascade * a_this)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this));
+
+ PRIVATE (a_this)->ref_count++;
+}
+
+/**
+ * cr_cascade_unref:
+ *@a_this: the current instance of
+ *#CRCascade.
+ *
+ *Decrements the reference counter associated
+ *to this instance of #CRCascade. If the reference
+ *counter reaches zero, the instance is destroyed
+ *using cr_cascade_destroy()
+ */
+void
+cr_cascade_unref (CRCascade * a_this)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this));
+
+ if (PRIVATE (a_this)->ref_count)
+ PRIVATE (a_this)->ref_count--;
+ if (!PRIVATE (a_this)->ref_count) {
+ cr_cascade_destroy (a_this);
+ }
+}
+
+/**
+ * cr_cascade_destroy:
+ * @a_this: the current instance of #CRCascade
+ *
+ * Destructor of #CRCascade.
+ */
+void
+cr_cascade_destroy (CRCascade * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (PRIVATE (a_this)) {
+ gulong i = 0;
+
+ for (i = 0; i < NB_ORIGINS; i++) {
+ if (PRIVATE (a_this)->sheets[i]) {
+ if (cr_stylesheet_unref
+ (PRIVATE (a_this)->sheets[i])
+ == TRUE) {
+ PRIVATE (a_this)->sheets[i] = NULL;
+ }
+ }
+ }
+ g_free (PRIVATE (a_this));
+ PRIVATE (a_this) = NULL;
+ }
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-cascade.h b/src/st/croco/cr-cascade.h
new file mode 100644
index 0000000..3119ae8
--- /dev/null
+++ b/src/st/croco/cr-cascade.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the
+ * GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the
+ * GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ */
+
+/*
+ *$Id$
+ */
+
+#ifndef __CR_CASCADE_H__
+#define __CR_CASCADE_H__
+
+#include "cr-stylesheet.h"
+
+/**
+ *@file
+ *the declaration of the #CRCascade class.
+ */
+
+G_BEGIN_DECLS
+
+
+typedef struct _CRCascadePriv CRCascadePriv ;
+
+/**
+ *An abstraction of the "Cascade" defined
+ *in the css2 spec, chapter 6.4.
+ */
+typedef struct _CRCascade CRCascade ;
+
+struct _CRCascade
+{
+ CRCascadePriv *priv ;
+};
+
+
+CRCascade * cr_cascade_new (CRStyleSheet *a_author_sheet,
+ CRStyleSheet *a_user_sheet,
+ CRStyleSheet *a_ua_sheet) ;
+
+CRStyleSheet * cr_cascade_get_sheet (CRCascade *a_this,
+ enum CRStyleOrigin a_origin) ;
+
+enum CRStatus cr_cascade_set_sheet (CRCascade *a_this,
+ CRStyleSheet *a_sheet,
+ enum CRStyleOrigin a_origin) ;
+
+void cr_cascade_ref (CRCascade *a_this) ;
+
+void cr_cascade_unref (CRCascade *a_this) ;
+
+void cr_cascade_destroy (CRCascade *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_CASCADE_H__*/
diff --git a/src/st/croco/cr-declaration.c b/src/st/croco/cr-declaration.c
new file mode 100644
index 0000000..6c70128
--- /dev/null
+++ b/src/st/croco/cr-declaration.c
@@ -0,0 +1,798 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli.
+ * See COPYRIGHTS file for copyright information.
+ */
+
+
+#include <string.h>
+#include "cr-declaration.h"
+#include "cr-statement.h"
+#include "cr-parser.h"
+
+/**
+ *@CRDeclaration:
+ *
+ *The definition of the #CRDeclaration class.
+ */
+
+/**
+ * dump:
+ *@a_this: the current instance of #CRDeclaration.
+ *@a_fp: the destination file pointer.
+ *@a_indent: the number of indentation white char.
+ *
+ *Dumps (serializes) one css declaration to a file.
+ */
+static void
+dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent)
+{
+ guchar *str = NULL;
+
+ g_return_if_fail (a_this);
+
+ str = (guchar *) cr_declaration_to_string (a_this, a_indent);
+ if (str) {
+ fprintf (a_fp, "%s", str);
+ g_free (str);
+ str = NULL;
+ }
+}
+
+/**
+ * cr_declaration_new:
+ * @a_statement: the statement this declaration belongs to. can be NULL.
+ *@a_property: the property string of the declaration
+ *@a_value: the value expression of the declaration.
+ *Constructor of #CRDeclaration.
+ *
+ *Returns the newly built instance of #CRDeclaration, or NULL in
+ *case of error.
+ *
+ *The returned CRDeclaration takes ownership of @a_property and @a_value.
+ *(E.g. cr_declaration_destroy on this CRDeclaration will also free
+ *@a_property and @a_value.)
+ */
+CRDeclaration *
+cr_declaration_new (CRStatement * a_statement,
+ CRString * a_property, CRTerm * a_value)
+{
+ CRDeclaration *result = NULL;
+
+ g_return_val_if_fail (a_property, NULL);
+
+ if (a_statement)
+ g_return_val_if_fail (a_statement
+ && ((a_statement->type == RULESET_STMT)
+ || (a_statement->type
+ == AT_FONT_FACE_RULE_STMT)
+ || (a_statement->type
+ == AT_PAGE_RULE_STMT)), NULL);
+
+ result = g_try_malloc (sizeof (CRDeclaration));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRDeclaration));
+ result->property = a_property;
+ result->value = a_value;
+
+ if (a_value) {
+ cr_term_ref (a_value);
+ }
+ result->parent_statement = a_statement;
+ return result;
+}
+
+/**
+ * cr_declaration_parse_from_buf:
+ *@a_statement: the parent css2 statement of this
+ *this declaration. Must be non NULL and of type
+ *RULESET_STMT (must be a ruleset).
+ *@a_str: the string that contains the statement.
+ *@a_enc: the encoding of a_str.
+ *
+ *Parses a text buffer that contains
+ *a css declaration.
+ *Returns the parsed declaration, or NULL in case of error.
+ */
+CRDeclaration *
+cr_declaration_parse_from_buf (CRStatement * a_statement,
+ const guchar * a_str, enum CREncoding a_enc)
+{
+ enum CRStatus status = CR_OK;
+ CRTerm *value = NULL;
+ CRString *property = NULL;
+ CRDeclaration *result = NULL;
+ CRParser *parser = NULL;
+ gboolean important = FALSE;
+
+ g_return_val_if_fail (a_str, NULL);
+ if (a_statement)
+ g_return_val_if_fail (a_statement->type == RULESET_STMT,
+ NULL);
+
+ parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE);
+ g_return_val_if_fail (parser, NULL);
+
+ status = cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ status = cr_parser_parse_declaration (parser, &property,
+ &value, &important);
+ if (status != CR_OK || !property)
+ goto cleanup;
+
+ result = cr_declaration_new (a_statement, property, value);
+ if (result) {
+ property = NULL;
+ value = NULL;
+ result->important = important;
+ }
+
+ cleanup:
+
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ }
+
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+
+ if (value) {
+ cr_term_destroy (value);
+ value = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_declaration_parse_list_from_buf:
+ *@a_str: the input buffer that contains the list of declaration to
+ *parse.
+ *@a_enc: the encoding of a_str
+ *
+ *Parses a ';' separated list of properties declaration.
+ *Returns the parsed list of declaration, NULL if parsing failed.
+ */
+CRDeclaration *
+cr_declaration_parse_list_from_buf (const guchar * a_str,
+ enum CREncoding a_enc)
+{
+
+ enum CRStatus status = CR_OK;
+ CRTerm *value = NULL;
+ CRString *property = NULL;
+ CRDeclaration *result = NULL,
+ *cur_decl = NULL;
+ CRParser *parser = NULL;
+ CRTknzr *tokenizer = NULL;
+ gboolean important = FALSE;
+
+ g_return_val_if_fail (a_str, NULL);
+
+ parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE);
+ g_return_val_if_fail (parser, NULL);
+ status = cr_parser_get_tknzr (parser, &tokenizer);
+ if (status != CR_OK || !tokenizer) {
+ if (status == CR_OK)
+ status = CR_ERROR;
+ goto cleanup;
+ }
+ status = cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ status = cr_parser_parse_declaration (parser, &property,
+ &value, &important);
+ if (status != CR_OK || !property) {
+ if (status != CR_OK)
+ status = CR_ERROR;
+ goto cleanup;
+ }
+ result = cr_declaration_new (NULL, property, value);
+ if (result) {
+ property = NULL;
+ value = NULL;
+ result->important = important;
+ }
+ /*now, go parse the other declarations */
+ for (;;) {
+ guint32 c = 0;
+
+ cr_parser_try_to_skip_spaces_and_comments (parser);
+ status = cr_tknzr_peek_char (tokenizer, &c);
+ if (status != CR_OK) {
+ if (status == CR_END_OF_INPUT_ERROR)
+ status = CR_OK;
+ goto cleanup;
+ }
+ if (c == ';') {
+ status = cr_tknzr_read_char (tokenizer, &c);
+ } else {
+ break;
+ }
+ important = FALSE;
+ cr_parser_try_to_skip_spaces_and_comments (parser);
+ status = cr_parser_parse_declaration (parser, &property,
+ &value, &important);
+ if (status != CR_OK || !property) {
+ if (status == CR_END_OF_INPUT_ERROR) {
+ status = CR_OK;
+ }
+ break;
+ }
+ cur_decl = cr_declaration_new (NULL, property, value);
+ if (cur_decl) {
+ cur_decl->important = important;
+ result = cr_declaration_append (result, cur_decl);
+ property = NULL;
+ value = NULL;
+ cur_decl = NULL;
+ } else {
+ break;
+ }
+ }
+
+ cleanup:
+
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ }
+
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+
+ if (value) {
+ cr_term_destroy (value);
+ value = NULL;
+ }
+
+ if (status != CR_OK && result) {
+ cr_declaration_destroy (result);
+ result = NULL;
+ }
+ return result;
+}
+
+/**
+ * cr_declaration_append:
+ *@a_this: the current declaration list.
+ *@a_new: the declaration to append.
+ *
+ *Appends a new declaration to the current declarations list.
+ *Returns the declaration list with a_new appended to it, or NULL
+ *in case of error.
+ */
+CRDeclaration *
+cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new)
+{
+ CRDeclaration *cur = NULL;
+
+ g_return_val_if_fail (a_new, NULL);
+
+ if (!a_this)
+ return a_new;
+
+ for (cur = a_this; cur && cur->next; cur = cur->next) ;
+
+ cur->next = a_new;
+ a_new->prev = cur;
+
+ return a_this;
+}
+
+/**
+ * cr_declaration_unlink:
+ *@a_decls: the declaration to unlink.
+ *
+ *Unlinks the declaration from the declaration list.
+ *case of a successful completion, NULL otherwise.
+ *
+ *Returns a pointer to the unlinked declaration in
+ */
+CRDeclaration *
+cr_declaration_unlink (CRDeclaration * a_decl)
+{
+ CRDeclaration *result = a_decl;
+
+ g_return_val_if_fail (result, NULL);
+
+ /*
+ *some sanity checks first
+ */
+ if (a_decl->prev) {
+ g_return_val_if_fail (a_decl->prev->next == a_decl, NULL);
+
+ }
+ if (a_decl->next) {
+ g_return_val_if_fail (a_decl->next->prev == a_decl, NULL);
+ }
+
+ /*
+ *now, the real unlinking job.
+ */
+ if (a_decl->prev) {
+ a_decl->prev->next = a_decl->next;
+ }
+ if (a_decl->next) {
+ a_decl->next->prev = a_decl->prev;
+ }
+ if (a_decl->parent_statement) {
+ CRDeclaration **children_decl_ptr = NULL;
+
+ switch (a_decl->parent_statement->type) {
+ case RULESET_STMT:
+ if (a_decl->parent_statement->kind.ruleset) {
+ children_decl_ptr =
+ &a_decl->parent_statement->
+ kind.ruleset->decl_list;
+ }
+
+ break;
+
+ case AT_FONT_FACE_RULE_STMT:
+ if (a_decl->parent_statement->kind.font_face_rule) {
+ children_decl_ptr =
+ &a_decl->parent_statement->
+ kind.font_face_rule->decl_list;
+ }
+ break;
+ case AT_PAGE_RULE_STMT:
+ if (a_decl->parent_statement->kind.page_rule) {
+ children_decl_ptr =
+ &a_decl->parent_statement->
+ kind.page_rule->decl_list;
+ }
+
+ default:
+ break;
+ }
+ if (children_decl_ptr
+ && *children_decl_ptr && *children_decl_ptr == a_decl)
+ *children_decl_ptr = (*children_decl_ptr)->next;
+ }
+
+ a_decl->next = NULL;
+ a_decl->prev = NULL;
+ a_decl->parent_statement = NULL;
+
+ return result;
+}
+
+/**
+ * cr_declaration_prepend:
+ * @a_this: the current declaration list.
+ * @a_new: the declaration to prepend.
+ *
+ * prepends a declaration to the current declaration list.
+ *
+ * Returns the list with a_new prepended or NULL in case of error.
+ */
+CRDeclaration *
+cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new)
+{
+ CRDeclaration *cur = NULL;
+
+ g_return_val_if_fail (a_new, NULL);
+
+ if (!a_this)
+ return a_new;
+
+ a_this->prev = a_new;
+ a_new->next = a_this;
+
+ for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
+
+ return cur;
+}
+
+/**
+ * cr_declaration_append2:
+ *@a_this: the current declaration list.
+ *@a_prop: the property string of the declaration to append.
+ *@a_value: the value of the declaration to append.
+ *
+ *Appends a declaration to the current declaration list.
+ *Returns the list with the new property appended to it, or NULL in
+ *case of an error.
+ */
+CRDeclaration *
+cr_declaration_append2 (CRDeclaration * a_this,
+ CRString * a_prop, CRTerm * a_value)
+{
+ CRDeclaration *new_elem = NULL;
+
+ if (a_this) {
+ new_elem = cr_declaration_new (a_this->parent_statement,
+ a_prop, a_value);
+ } else {
+ new_elem = cr_declaration_new (NULL, a_prop, a_value);
+ }
+
+ g_return_val_if_fail (new_elem, NULL);
+
+ return cr_declaration_append (a_this, new_elem);
+}
+
+/**
+ * cr_declaration_dump:
+ *@a_this: the current instance of #CRDeclaration.
+ *@a_fp: the destination file.
+ *@a_indent: the number of indentation white char.
+ *@a_one_per_line: whether to put one declaration per line of not .
+ *
+ *
+ *Dumps a declaration list to a file.
+ */
+void
+cr_declaration_dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent,
+ gboolean a_one_per_line)
+{
+ CRDeclaration const *cur = NULL;
+
+ g_return_if_fail (a_this);
+
+ for (cur = a_this; cur; cur = cur->next) {
+ if (cur->prev) {
+ if (a_one_per_line == TRUE)
+ fprintf (a_fp, ";\n");
+ else
+ fprintf (a_fp, "; ");
+ }
+ dump (cur, a_fp, a_indent);
+ }
+}
+
+/**
+ * cr_declaration_dump_one:
+ *@a_this: the current instance of #CRDeclaration.
+ *@a_fp: the destination file.
+ *@a_indent: the number of indentation white char.
+ *
+ *Dumps the first declaration of the declaration list to a file.
+ */
+void
+cr_declaration_dump_one (CRDeclaration const * a_this, FILE * a_fp, glong a_indent)
+{
+ g_return_if_fail (a_this);
+
+ dump (a_this, a_fp, a_indent);
+}
+
+/**
+ * cr_declaration_to_string:
+ *@a_this: the current instance of #CRDeclaration.
+ *@a_indent: the number of indentation white char
+ *to put before the actual serialisation.
+ *
+ *Serializes the declaration into a string
+ *Returns the serialized form the declaration. The caller must
+ *free the string using g_free().
+ */
+gchar *
+cr_declaration_to_string (CRDeclaration const * a_this, gulong a_indent)
+{
+ GString *stringue = NULL;
+
+ gchar *str = NULL,
+ *result = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ stringue = g_string_new (NULL);
+
+ if (a_this->property
+ && a_this->property->stryng
+ && a_this->property->stryng->str) {
+ str = g_strndup (a_this->property->stryng->str,
+ a_this->property->stryng->len);
+ if (str) {
+ cr_utils_dump_n_chars2 (' ', stringue,
+ a_indent);
+ g_string_append (stringue, str);
+ g_free (str);
+ str = NULL;
+ } else
+ goto error;
+
+ if (a_this->value) {
+ guchar *value_str = NULL;
+
+ value_str = cr_term_to_string (a_this->value);
+ if (value_str) {
+ g_string_append_printf (stringue, " : %s",
+ value_str);
+ g_free (value_str);
+ } else
+ goto error;
+ }
+ if (a_this->important == TRUE) {
+ g_string_append_printf (stringue, " %s",
+ "!important");
+ }
+ }
+ if (stringue && stringue->str) {
+ result = g_string_free (stringue, FALSE);
+ }
+ return result;
+
+ error:
+ if (stringue) {
+ g_string_free (stringue, TRUE);
+ stringue = NULL;
+ }
+ if (str) {
+ g_free (str);
+ str = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_declaration_list_to_string:
+ *@a_this: the current instance of #CRDeclaration.
+ *@a_indent: the number of indentation white char
+ *to put before the actual serialisation.
+ *
+ *Serializes the declaration list into a string
+ */
+guchar *
+cr_declaration_list_to_string (CRDeclaration const * a_this, gulong a_indent)
+{
+ CRDeclaration const *cur = NULL;
+ GString *stringue = NULL;
+ guchar *str = NULL,
+ *result = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ stringue = g_string_new (NULL);
+
+ for (cur = a_this; cur; cur = cur->next) {
+ str = (guchar *) cr_declaration_to_string (cur, a_indent);
+ if (str) {
+ g_string_append_printf (stringue, "%s;", str);
+ g_free (str);
+ } else
+ break;
+ }
+ if (stringue && stringue->str) {
+ result = (guchar *) g_string_free (stringue, FALSE);
+ }
+
+ return result;
+}
+
+/**
+ * cr_declaration_list_to_string2:
+ *@a_this: the current instance of #CRDeclaration.
+ *@a_indent: the number of indentation white char
+ *@a_one_decl_per_line: whether to output one doc per line or not.
+ *to put before the actual serialisation.
+ *
+ *Serializes the declaration list into a string
+ *Returns the serialized form the declararation.
+ */
+guchar *
+cr_declaration_list_to_string2 (CRDeclaration const * a_this,
+ gulong a_indent, gboolean a_one_decl_per_line)
+{
+ CRDeclaration const *cur = NULL;
+ GString *stringue = NULL;
+ guchar *str = NULL,
+ *result = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ stringue = g_string_new (NULL);
+
+ for (cur = a_this; cur; cur = cur->next) {
+ str = (guchar *) cr_declaration_to_string (cur, a_indent);
+ if (str) {
+ if (a_one_decl_per_line == TRUE) {
+ if (cur->next)
+ g_string_append_printf (stringue,
+ "%s;\n", str);
+ else
+ g_string_append (stringue,
+ (const gchar *) str);
+ } else {
+ if (cur->next)
+ g_string_append_printf (stringue,
+ "%s;", str);
+ else
+ g_string_append (stringue,
+ (const gchar *) str);
+ }
+ g_free (str);
+ } else
+ break;
+ }
+ if (stringue && stringue->str) {
+ result = (guchar *) g_string_free (stringue, FALSE);
+ }
+
+ return result;
+}
+
+/**
+ * cr_declaration_nr_props:
+ *@a_this: the current instance of #CRDeclaration.
+ *Return the number of properties in the declaration
+ */
+gint
+cr_declaration_nr_props (CRDeclaration const * a_this)
+{
+ CRDeclaration const *cur = NULL;
+ int nr = 0;
+
+ g_return_val_if_fail (a_this, -1);
+
+ for (cur = a_this; cur; cur = cur->next)
+ nr++;
+ return nr;
+}
+
+/**
+ * cr_declaration_get_from_list:
+ *@a_this: the current instance of #CRDeclaration.
+ *@itemnr: the index into the declaration list.
+ *
+ *Use an index to get a CRDeclaration from the declaration list.
+ *
+ *Returns #CRDeclaration at position itemnr,
+ *if itemnr > number of declarations - 1,
+ *it will return NULL.
+ */
+CRDeclaration *
+cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr)
+{
+ CRDeclaration *cur = NULL;
+ int nr = 0;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ for (cur = a_this; cur; cur = cur->next)
+ if (nr++ == itemnr)
+ return cur;
+ return NULL;
+}
+
+/**
+ * cr_declaration_get_by_prop_name:
+ *@a_this: the current instance of #CRDeclaration.
+ *@a_prop: the property name to search for.
+ *
+ *Use property name to get a CRDeclaration from the declaration list.
+ *Returns #CRDeclaration with property name a_prop, or NULL if not found.
+ */
+CRDeclaration *
+cr_declaration_get_by_prop_name (CRDeclaration * a_this,
+ const guchar * a_prop)
+{
+ CRDeclaration *cur = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+ g_return_val_if_fail (a_prop, NULL);
+
+ for (cur = a_this; cur; cur = cur->next) {
+ if (cur->property
+ && cur->property->stryng
+ && cur->property->stryng->str) {
+ if (!strcmp (cur->property->stryng->str,
+ (const char *) a_prop)) {
+ return cur;
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * cr_declaration_ref:
+ *@a_this: the current instance of #CRDeclaration.
+ *
+ *Increases the ref count of the current instance of #CRDeclaration.
+ */
+void
+cr_declaration_ref (CRDeclaration * a_this)
+{
+ g_return_if_fail (a_this);
+
+ a_this->ref_count++;
+}
+
+/**
+ * cr_declaration_unref:
+ *@a_this: the current instance of #CRDeclaration.
+ *
+ *Decrements the ref count of the current instance of #CRDeclaration.
+ *If the ref count reaches zero, the current instance of #CRDeclaration
+ *if destroyed.
+ *Returns TRUE if @a_this was destroyed (ref count reached zero),
+ *FALSE otherwise.
+ */
+gboolean
+cr_declaration_unref (CRDeclaration * a_this)
+{
+ g_return_val_if_fail (a_this, FALSE);
+
+ if (a_this->ref_count) {
+ a_this->ref_count--;
+ }
+
+ if (a_this->ref_count == 0) {
+ cr_declaration_destroy (a_this);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * cr_declaration_destroy:
+ *@a_this: the current instance of #CRDeclaration.
+ *
+ *Destructor of the declaration list.
+ */
+void
+cr_declaration_destroy (CRDeclaration * a_this)
+{
+ CRDeclaration *cur = NULL;
+
+ g_return_if_fail (a_this);
+
+ /*
+ * Go to the last element of the list.
+ */
+ for (cur = a_this; cur->next; cur = cur->next)
+ g_assert (cur->next->prev == cur);
+
+ /*
+ * Walk backward the list and free each "next" element.
+ * Meanwhile, free each property/value pair contained in the list.
+ */
+ for (; cur; cur = cur->prev) {
+ g_free (cur->next);
+ cur->next = NULL;
+
+ if (cur->property) {
+ cr_string_destroy (cur->property);
+ cur->property = NULL;
+ }
+
+ if (cur->value) {
+ cr_term_destroy (cur->value);
+ cur->value = NULL;
+ }
+ }
+
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-declaration.h b/src/st/croco/cr-declaration.h
new file mode 100644
index 0000000..eee8be3
--- /dev/null
+++ b/src/st/croco/cr-declaration.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See the COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_DECLARATION_H__
+#define __CR_DECLARATION_H__
+
+#include <stdio.h>
+#include "cr-utils.h"
+#include "cr-term.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *The declaration of the #CRDeclaration class.
+ */
+
+/*forward declaration of what is defined in cr-statement.h*/
+typedef struct _CRStatement CRStatement ;
+
+/**
+ *The abstraction of a css declaration defined by the
+ *css2 spec in chapter 4.
+ *It is actually a chained list of property/value pairs.
+ */
+typedef struct _CRDeclaration CRDeclaration ;
+struct _CRDeclaration
+{
+ /**The property.*/
+ CRString *property ;
+
+ /**The value of the property.*/
+ CRTerm *value ;
+
+ /*the ruleset that contains this declaration*/
+ CRStatement *parent_statement ;
+
+ /*the next declaration*/
+ CRDeclaration *next ;
+
+ /*the previous one declaration*/
+ CRDeclaration *prev ;
+
+ /*does the declaration have the important keyword ?*/
+ gboolean important ;
+
+ glong ref_count ;
+
+ CRParsingLocation location ;
+ /*reserved for future usage*/
+ gpointer rfu0 ;
+ gpointer rfu1 ;
+ gpointer rfu2 ;
+ gpointer rfu3 ;
+} ;
+
+
+CRDeclaration * cr_declaration_new (CRStatement *a_statement,
+ CRString *a_property,
+ CRTerm *a_value) ;
+
+
+CRDeclaration * cr_declaration_parse_from_buf (CRStatement *a_statement,
+ const guchar *a_str,
+ enum CREncoding a_enc) ;
+
+CRDeclaration * cr_declaration_parse_list_from_buf (const guchar *a_str,
+ enum CREncoding a_enc) ;
+
+CRDeclaration * cr_declaration_append (CRDeclaration *a_this,
+ CRDeclaration *a_new) ;
+
+CRDeclaration * cr_declaration_append2 (CRDeclaration *a_this,
+ CRString *a_prop,
+ CRTerm *a_value) ;
+
+CRDeclaration * cr_declaration_prepend (CRDeclaration *a_this,
+ CRDeclaration *a_new) ;
+
+CRDeclaration * cr_declaration_unlink (CRDeclaration * a_decl) ;
+
+void
+cr_declaration_dump (CRDeclaration const *a_this,
+ FILE *a_fp, glong a_indent,
+ gboolean a_one_per_line) ;
+
+void cr_declaration_dump_one (CRDeclaration const *a_this,
+ FILE *a_fp, glong a_indent) ;
+
+gint cr_declaration_nr_props (CRDeclaration const *a_this) ;
+
+CRDeclaration * cr_declaration_get_from_list (CRDeclaration *a_this,
+ int itemnr) ;
+
+CRDeclaration * cr_declaration_get_by_prop_name (CRDeclaration *a_this,
+ const guchar *a_str) ;
+
+gchar * cr_declaration_to_string (CRDeclaration const *a_this,
+ gulong a_indent) ;
+
+guchar * cr_declaration_list_to_string (CRDeclaration const *a_this,
+ gulong a_indent) ;
+
+guchar * cr_declaration_list_to_string2 (CRDeclaration const *a_this,
+ gulong a_indent,
+ gboolean a_one_decl_per_line) ;
+
+void cr_declaration_ref (CRDeclaration *a_this) ;
+
+gboolean cr_declaration_unref (CRDeclaration *a_this) ;
+
+void cr_declaration_destroy (CRDeclaration *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_DECLARATION_H__*/
diff --git a/src/st/croco/cr-doc-handler.c b/src/st/croco/cr-doc-handler.c
new file mode 100644
index 0000000..b0ef13c
--- /dev/null
+++ b/src/st/croco/cr-doc-handler.c
@@ -0,0 +1,276 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See COPRYRIGHTS file for copyright information.
+ */
+
+#include <string.h>
+#include "cr-doc-handler.h"
+#include "cr-parser.h"
+
+/**
+ *@CRDocHandler:
+ *
+ *The definition of the CRDocHandler class.
+ *Contains methods to instantiate, destroy,
+ *and initialize instances of #CRDocHandler
+ *to custom values.
+ */
+
+#define PRIVATE(obj) (obj)->priv
+
+struct _CRDocHandlerPriv {
+ /**
+ *This pointer is to hold an application parsing context.
+ *For example, it used by the Object Model parser to
+ *store it parsing context. #CRParser does not touch it, but
+ *#CROMParser does. #CROMParser allocates this pointer at
+ *the beginning of the css document, and frees it at the end
+ *of the document.
+ */
+ gpointer context;
+
+ /**
+ *The place where #CROMParser puts the result of its parsing, if
+ *any.
+ */
+ gpointer result;
+ /**
+ *a pointer to the parser used to parse
+ *the current document.
+ */
+ CRParser *parser ;
+};
+
+/**
+ * cr_doc_handler_new:
+ *Constructor of #CRDocHandler.
+ *
+ *Returns the newly built instance of
+ *#CRDocHandler
+ *
+ */
+CRDocHandler *
+cr_doc_handler_new (void)
+{
+ CRDocHandler *result = NULL;
+
+ result = g_try_malloc (sizeof (CRDocHandler));
+
+ g_return_val_if_fail (result, NULL);
+
+ memset (result, 0, sizeof (CRDocHandler));
+ result->ref_count++;
+
+ result->priv = g_try_malloc (sizeof (CRDocHandlerPriv));
+ if (!result->priv) {
+ cr_utils_trace_info ("Out of memory exception");
+ g_free (result);
+ return NULL;
+ }
+
+ cr_doc_handler_set_default_sac_handler (result);
+
+ return result;
+}
+
+/**
+ * cr_doc_handler_get_ctxt:
+ *@a_this: the current instance of #CRDocHandler.
+ *@a_ctxt: out parameter. The new parsing context.
+ *
+ *Gets the private parsing context associated to the document handler
+ *The private parsing context is used by libcroco only.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_doc_handler_get_ctxt (CRDocHandler const * a_this, gpointer * a_ctxt)
+{
+ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR);
+
+ *a_ctxt = a_this->priv->context;
+
+ return CR_OK;
+}
+
+/**
+ * cr_doc_handler_set_ctxt:
+ *@a_this: the current instance of #CRDocHandler
+ *@a_ctxt: a pointer to the parsing context.
+ *
+ *Sets the private parsing context.
+ *This is used by libcroco only.
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_doc_handler_set_ctxt (CRDocHandler * a_this, gpointer a_ctxt)
+{
+ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR);
+ a_this->priv->context = a_ctxt;
+ return CR_OK;
+}
+
+/**
+ * cr_doc_handler_get_result:
+ *@a_this: the current instance of #CRDocHandler
+ *@a_result: out parameter. The returned result.
+ *
+ *Gets the private parsing result.
+ *The private parsing result is used by libcroco only.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_doc_handler_get_result (CRDocHandler const * a_this, gpointer * a_result)
+{
+ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR);
+
+ *a_result = a_this->priv->result;
+
+ return CR_OK;
+}
+
+/**
+ * cr_doc_handler_set_result:
+ *@a_this: the current instance of #CRDocHandler
+ *@a_result: the new result.
+ *
+ *Sets the private parsing context.
+ *This is used by libcroco only.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_doc_handler_set_result (CRDocHandler * a_this, gpointer a_result)
+{
+ g_return_val_if_fail (a_this && a_this->priv, CR_BAD_PARAM_ERROR);
+ a_this->priv->result = a_result;
+ return CR_OK;
+}
+
+/**
+ *cr_doc_handler_set_default_sac_handler:
+ *@a_this: a pointer to the current instance of #CRDocHandler.
+ *
+ *Sets the sac handlers contained in the current
+ *instance of DocHandler to the default handlers.
+ *For the time being the default handlers are
+ *test handlers. This is expected to change in a
+ *near future, when the libcroco gets a bit debugged.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_doc_handler_set_default_sac_handler (CRDocHandler * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ a_this->start_document = NULL;
+ a_this->end_document = NULL;
+ a_this->import_style = NULL;
+ a_this->namespace_declaration = NULL;
+ a_this->comment = NULL;
+ a_this->start_selector = NULL;
+ a_this->end_selector = NULL;
+ a_this->property = NULL;
+ a_this->start_font_face = NULL;
+ a_this->end_font_face = NULL;
+ a_this->start_media = NULL;
+ a_this->end_media = NULL;
+ a_this->start_page = NULL;
+ a_this->end_page = NULL;
+ a_this->ignorable_at_rule = NULL;
+ a_this->error = NULL;
+ a_this->unrecoverable_error = NULL;
+ return CR_OK;
+}
+
+/**
+ * cr_doc_handler_ref:
+ *@a_this: the current instance of #CRDocHandler.
+ */
+void
+cr_doc_handler_ref (CRDocHandler * a_this)
+{
+ g_return_if_fail (a_this);
+
+ a_this->ref_count++;
+}
+
+/**
+ * cr_doc_handler_unref:
+ *@a_this: the current instance of #CRDocHandler.
+ *
+ *Decreases the ref count of the current instance of #CRDocHandler.
+ *If the ref count reaches '0' then, destroys the instance.
+ *
+ *Returns TRUE if the instance as been destroyed, FALSE otherwise.
+ */
+gboolean
+cr_doc_handler_unref (CRDocHandler * a_this)
+{
+ g_return_val_if_fail (a_this, FALSE);
+
+ if (a_this->ref_count > 0) {
+ a_this->ref_count--;
+ }
+
+ if (a_this->ref_count == 0) {
+ cr_doc_handler_destroy (a_this);
+ return TRUE;
+ }
+ return FALSE ;
+}
+
+/**
+ * cr_doc_handler_destroy:
+ *@a_this: the instance of #CRDocHandler to
+ *destroy.
+ *
+ *The destructor of the #CRDocHandler class.
+ */
+void
+cr_doc_handler_destroy (CRDocHandler * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->priv) {
+ g_free (a_this->priv);
+ a_this->priv = NULL;
+ }
+ g_free (a_this);
+}
+
+/**
+ * cr_doc_handler_associate_a_parser:
+ *Associates a parser to the current document handler
+ *
+ *@a_this: the current instance of document handler.
+ *@a_parser: the parser to associate.
+ */
+void
+cr_doc_handler_associate_a_parser (CRDocHandler *a_this,
+ gpointer a_parser)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this)
+ && a_parser) ;
+
+ PRIVATE (a_this)->parser = a_parser ;
+}
diff --git a/src/st/croco/cr-doc-handler.h b/src/st/croco/cr-doc-handler.h
new file mode 100644
index 0000000..d12673f
--- /dev/null
+++ b/src/st/croco/cr-doc-handler.h
@@ -0,0 +1,298 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See the COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_DOC_HANDLER_H__
+#define __CR_DOC_HANDLER_H__
+
+/**
+ *@file
+ *The declaration of the #CRDocumentHandler class.
+ *This class is actually the parsing events handler.
+ */
+
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-input.h"
+#include "cr-stylesheet.h"
+
+G_BEGIN_DECLS
+
+
+typedef struct _CRDocHandler CRDocHandler ;
+
+struct _CRDocHandlerPriv ;
+typedef struct _CRDocHandlerPriv CRDocHandlerPriv ;
+
+
+/**
+ *The SAC document handler.
+ *An instance of this class is to
+ *be passed to a parser. Then, during the parsing
+ *the parser calls the convenient function pointer
+ *whenever a particular event (a css construction) occurs.
+ */
+struct _CRDocHandler
+{
+ CRDocHandlerPriv *priv ;
+
+ /**
+ *This pointer is to be used by the application for
+ *it custom needs. It is there to extend the doc handler.
+ */
+ gpointer app_data ;
+
+ /**
+ *Is called at the beginning of the parsing of the document.
+ *@param a_this a pointer to the current instance of
+ *#CRDocHandler.
+ */
+ void (*start_document) (CRDocHandler *a_this) ;
+
+ /**
+ *Is called to notify the end of the parsing of the document.
+ *@param a_this a pointer to the current instance of
+ *#CRDocHandler.
+ */
+ void (*end_document) (CRDocHandler *a_this) ;
+
+ /**
+ *Is called to notify an at charset rule.
+ *@param a_this the document handler.
+ *@param a_charset the declared charset.
+ */
+ void (*charset) (CRDocHandler *a_this,
+ CRString *a_charset,
+ CRParsingLocation *a_charset_sym_location) ;
+
+ /**
+ *Is called to notify an import statement in
+ *the stylesheet.
+ *@param a_this the current instance of #CRDocHandler.
+ *@param a_media_list a doubly linked list of GString objects.
+ *Each GString object contains a string which is the
+ *destination media for style information.
+ *@param a_uri the uri of the imported style sheet.
+ *@param a_uri_default_ns the default namespace of URI
+ *@param a_location the parsing location of the '\@import'
+ *keyword.
+ *of the imported style sheet.
+ */
+ void (*import_style) (CRDocHandler *a_this,
+ GList *a_media_list,
+ CRString *a_uri,
+ CRString *a_uri_default_ns,
+ CRParsingLocation *a_location) ;
+
+ void (*import_style_result) (CRDocHandler *a_this,
+ GList *a_media_list,
+ CRString *a_uri,
+ CRString *a_uri_default_ns,
+ CRStyleSheet *a_sheet) ;
+
+ /**
+ *Is called to notify a namespace declaration.
+ *Not used yet.
+ *@param a_this the current instance of #CRDocHandler.
+ *@param a_prefix the prefix of the namespace.
+ *@param a_uri the uri of the namespace.
+ *@param a_location the location of the "@namespace" keyword.
+ */
+ void (*namespace_declaration) (CRDocHandler *a_this,
+ CRString *a_prefix,
+ CRString *a_uri,
+ CRParsingLocation *a_location) ;
+
+ /**
+ *Is called to notify a comment.
+ *@param a_this a pointer to the current instance
+ *of #CRDocHandler.
+ *@param a_comment the comment.
+ */
+ void (*comment) (CRDocHandler *a_this,
+ CRString *a_comment) ;
+
+ /**
+ *Is called to notify the beginning of a rule
+ *statement.
+ *@param a_this the current instance of #CRDocHandler.
+ *@param a_selector_list the list of selectors that precedes
+ *the rule declarations.
+ */
+ void (*start_selector) (CRDocHandler * a_this,
+ CRSelector *a_selector_list) ;
+
+ /**
+ *Is called to notify the end of a rule statement.
+ *@param a_this the current instance of #CRDocHandler.
+ *@param a_selector_list the list of selectors that precedes
+ *the rule declarations. This pointer is the same as
+ *the one passed to start_selector() ;
+ */
+ void (*end_selector) (CRDocHandler *a_this,
+ CRSelector *a_selector_list) ;
+
+
+ /**
+ *Is called to notify a declaration.
+ *@param a_this a pointer to the current instance
+ *of #CRDocHandler.
+ *@param a_name the name of the parsed property.
+ *@param a_expression a css expression that represents
+ *the value of the property. A css expression is
+ *actually a linked list of 'terms'. Each term can
+ *be linked to other using operators.
+ *
+ */
+ void (*property) (CRDocHandler *a_this,
+ CRString *a_name,
+ CRTerm *a_expression,
+ gboolean a_is_important) ;
+ /**
+ *Is called to notify the start of a font face statement.
+ *The parser invokes this method at the beginning of every
+ *font face statement in the style sheet. There will
+ *be a corresponding end_font_face () event for every
+ *start_font_face () event.
+ *
+ *@param a_this a pointer to the current instance of
+ *#CRDocHandler.
+ *@param a_location the parsing location of the "\@font-face"
+ *keyword.
+ */
+ void (*start_font_face) (CRDocHandler *a_this,
+ CRParsingLocation *a_location) ;
+
+ /**
+ *Is called to notify the end of a font face statement.
+ *@param a_this a pointer to the current instance of
+ *#CRDocHandler.
+ */
+ void (*end_font_face) (CRDocHandler *a_this) ;
+
+
+ /**
+ *Is called to notify the beginning of a media statement.
+ *The parser will invoke this method at the beginning of
+ *every media statement in the style sheet. There will be
+ *a corresponding end_media() event for every start_media()
+ *event.
+ *@param a_this a pointer to the current instance of
+ *#CRDocHandler.
+ *@param a_media_list a double linked list of
+ #CRString * objects.
+ *Each CRString objects is actually a destination media for
+ *the style information.
+ */
+ void (*start_media) (CRDocHandler *a_this,
+ GList *a_media_list,
+ CRParsingLocation *a_location) ;
+
+ /**
+ *Is called to notify the end of a media statement.
+ *@param a_this a pointer to the current instance
+ *of #CRDocHandler.
+ *@param a_media_list a double linked list of GString * objects.
+ *Each GString objects is actually a destination media for
+ *the style information.
+ */
+ void (*end_media) (CRDocHandler *a_this,
+ GList *a_media_list) ;
+
+ /**
+ *Is called to notify the beginning of a page statement.
+ *The parser invokes this function at the beginning of
+ *every page statement in the style sheet. There will be
+ *a corresponding end_page() event for every single
+ *start_page() event.
+ *@param a_this a pointer to the current instance of
+ *#CRDocHandler.
+ *@param a_name the name of the page (if any, null otherwise).
+ *@param a_pseudo_page the pseudo page (if any, null otherwise).
+ *@param a_location the parsing location of the "\@page" keyword.
+ */
+ void (*start_page) (CRDocHandler *a_this,
+ CRString *a_name,
+ CRString *a_pseudo_page,
+ CRParsingLocation *a_location) ;
+
+ /**
+ *Is called to notify the end of a page statement.
+ *@param a_this a pointer to the current instance of
+ *#CRDocHandler.
+ *@param a_name the name of the page (if any, null otherwise).
+ *@param a_pseudo_page the pseudo page (if any, null otherwise).
+ */
+ void (*end_page) (CRDocHandler *a_this,
+ CRString *a_name,
+ CRString *pseudo_page) ;
+
+ /**
+ *Is Called to notify an unknown at-rule not supported
+ *by this parser.
+ */
+ void (*ignorable_at_rule) (CRDocHandler *a_this,
+ CRString *a_name) ;
+
+ /**
+ *Is called to notify a parsing error. After this error
+ *the application must ignore the rule being parsed, if
+ *any. After completion of this callback,
+ *the parser will then try to resume the parsing,
+ *ignoring the current error.
+ */
+ void (*error) (CRDocHandler *a_this) ;
+
+ /**
+ *Is called to notify an unrecoverable parsing error.
+ *This is the place to put emergency routines that free allocated
+ *resources.
+ */
+ void (*unrecoverable_error) (CRDocHandler *a_this) ;
+
+ gboolean resolve_import ;
+ gulong ref_count ;
+} ;
+
+CRDocHandler * cr_doc_handler_new (void) ;
+
+enum CRStatus cr_doc_handler_set_result (CRDocHandler *a_this, gpointer a_result) ;
+
+enum CRStatus cr_doc_handler_get_result (CRDocHandler const *a_this, gpointer * a_result) ;
+
+enum CRStatus cr_doc_handler_set_ctxt (CRDocHandler *a_this, gpointer a_ctxt) ;
+
+enum CRStatus cr_doc_handler_get_ctxt (CRDocHandler const *a_this, gpointer * a_ctxt) ;
+
+enum CRStatus cr_doc_handler_set_default_sac_handler (CRDocHandler *a_this) ;
+
+void cr_doc_handler_associate_a_parser (CRDocHandler *a_this,
+ gpointer a_parser) ;
+
+void cr_doc_handler_ref (CRDocHandler *a_this) ;
+
+gboolean cr_doc_handler_unref (CRDocHandler *a_this) ;
+
+void cr_doc_handler_destroy (CRDocHandler *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_DOC_HANDLER_H__*/
diff --git a/src/st/croco/cr-enc-handler.c b/src/st/croco/cr-enc-handler.c
new file mode 100644
index 0000000..65adc7a
--- /dev/null
+++ b/src/st/croco/cr-enc-handler.c
@@ -0,0 +1,184 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ *$Id$
+ */
+
+/**
+ *@file
+ *The definition of the #CREncHandler class.
+ */
+
+#include "cr-enc-handler.h"
+#include "cr-utils.h"
+
+#include <string.h>
+
+struct CREncAlias {
+ const gchar *name;
+ enum CREncoding encoding;
+};
+
+static struct CREncAlias gv_default_aliases[] = {
+ {"UTF-8", CR_UTF_8},
+ {"UTF_8", CR_UTF_8},
+ {"UTF8", CR_UTF_8},
+ {"UTF-16", CR_UTF_16},
+ {"UTF_16", CR_UTF_16},
+ {"UTF16", CR_UTF_16},
+ {"UCS1", CR_UCS_1},
+ {"UCS-1", CR_UCS_1},
+ {"UCS_1", CR_UCS_1},
+ {"ISO-8859-1", CR_UCS_1},
+ {"ISO_8859-1", CR_UCS_1},
+ {"UCS-1", CR_UCS_1},
+ {"UCS_1", CR_UCS_1},
+ {"UCS4", CR_UCS_4},
+ {"UCS-4", CR_UCS_4},
+ {"UCS_4", CR_UCS_4},
+ {"ASCII", CR_ASCII},
+ {0, 0}
+};
+
+static CREncHandler gv_default_enc_handlers[] = {
+ {CR_UCS_1, cr_utils_ucs1_to_utf8, cr_utils_utf8_to_ucs1,
+ cr_utils_ucs1_str_len_as_utf8, cr_utils_utf8_str_len_as_ucs1},
+
+ {CR_ISO_8859_1, cr_utils_ucs1_to_utf8, cr_utils_utf8_to_ucs1,
+ cr_utils_ucs1_str_len_as_utf8, cr_utils_utf8_str_len_as_ucs1},
+
+ {CR_ASCII, cr_utils_ucs1_to_utf8, cr_utils_utf8_to_ucs1,
+ cr_utils_ucs1_str_len_as_utf8, cr_utils_utf8_str_len_as_ucs1},
+
+ {0, NULL, NULL, NULL, NULL}
+};
+
+/**
+ * cr_enc_handler_get_instance:
+ *@a_enc: the encoding of the Handler.
+ *
+ *Gets the instance of encoding handler.
+ *This function implements a singleton pattern.
+ *
+ *Returns the instance of #CREncHandler.
+ */
+CREncHandler *
+cr_enc_handler_get_instance (enum CREncoding a_enc)
+{
+ gulong i = 0;
+
+ for (i = 0; gv_default_enc_handlers[i].encoding; i++) {
+ if (gv_default_enc_handlers[i].encoding == a_enc) {
+ return (CREncHandler *) & gv_default_enc_handlers[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * cr_enc_handler_resolve_enc_alias:
+ *@a_alias_name: the encoding name.
+ *@a_enc: output param. The returned encoding type
+ *or 0 if the alias is not supported.
+ *
+ *Given an encoding name (called an alias name)
+ *the function returns the matching encoding type.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_enc_handler_resolve_enc_alias (const guchar * a_alias_name,
+ enum CREncoding *a_enc)
+{
+ gulong i = 0;
+ guchar *alias_name_up = NULL;
+ enum CRStatus status = CR_ENCODING_NOT_FOUND_ERROR;
+
+ g_return_val_if_fail (a_alias_name != NULL, CR_BAD_PARAM_ERROR);
+
+ alias_name_up = (guchar *) g_ascii_strup ((const gchar *) a_alias_name, -1);
+
+ for (i = 0; gv_default_aliases[i].name; i++) {
+ if (!strcmp (gv_default_aliases[i].name, (const gchar *) alias_name_up)) {
+ *a_enc = gv_default_aliases[i].encoding;
+ status = CR_OK;
+ break;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * cr_enc_handler_convert_input:
+ *@a_this: the current instance of #CREncHandler.
+ *@a_in: the input buffer to convert.
+ *@a_in_len: in/out parameter. The len of the input
+ *buffer to convert. After return, contains the number of
+ *bytes actually consumed.
+ *@a_out: output parameter. The converted output buffer.
+ *Must be freed by the buffer.
+ *@a_out_len: output parameter. The length of the output buffer.
+ *
+ *Converts a raw input buffer into an utf8 buffer.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_enc_handler_convert_input (CREncHandler * a_this,
+ const guchar * a_in,
+ gulong * a_in_len,
+ guchar ** a_out, gulong * a_out_len)
+{
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && a_in && a_in_len && a_out,
+ CR_BAD_PARAM_ERROR);
+
+ if (a_this->decode_input == NULL)
+ return CR_OK;
+
+ if (a_this->enc_str_len_as_utf8) {
+ status = a_this->enc_str_len_as_utf8 (a_in,
+ &a_in[*a_in_len - 1],
+ a_out_len);
+
+ g_return_val_if_fail (status == CR_OK, status);
+ } else {
+ *a_out_len = *a_in_len;
+ }
+
+ *a_out = g_malloc0 (*a_out_len);
+
+ status = a_this->decode_input (a_in, a_in_len, *a_out, a_out_len);
+
+ if (status != CR_OK) {
+ g_free (*a_out);
+ *a_out = NULL;
+ }
+
+ g_return_val_if_fail (status == CR_OK, status);
+
+ return CR_OK;
+}
diff --git a/src/st/croco/cr-enc-handler.h b/src/st/croco/cr-enc-handler.h
new file mode 100644
index 0000000..0727764
--- /dev/null
+++ b/src/st/croco/cr-enc-handler.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ *$Id$
+ */
+
+/**
+ *@file:
+ *The declaration of the #CREncHandler class.
+ *
+ */
+
+#ifndef __CR_ENC_HANDLER_H__
+#define __CR_ENC_HANDLER_H__
+
+#include "cr-utils.h"
+
+G_BEGIN_DECLS
+
+
+typedef struct _CREncHandler CREncHandler ;
+
+typedef enum CRStatus (*CREncInputFunc) (const guchar * a_in,
+ gulong *a_in_len,
+ guchar *a_out,
+ gulong *a_out_len) ;
+
+typedef enum CRStatus (*CREncOutputFunc) (const guchar * a_in,
+ gulong *a_in_len,
+ guchar *a_out,
+ gulong *a_out_len) ;
+
+typedef enum CRStatus (*CREncInputStrLenAsUtf8Func)
+(const guchar *a_in_start,
+ const guchar *a_in_end,
+ gulong *a_in_size);
+
+typedef enum CRStatus (*CREncUtf8StrLenAsOutputFunc)
+(const guchar *a_in_start,
+ const guchar *a_in_end,
+ gulong *a_in_size) ;
+
+/**
+ *This class is responsible of the
+ *the encoding conversions stuffs in
+ *libcroco.
+ */
+
+struct _CREncHandler
+{
+ enum CREncoding encoding ;
+ CREncInputFunc decode_input ;
+ CREncInputFunc encode_output ;
+ CREncInputStrLenAsUtf8Func enc_str_len_as_utf8 ;
+ CREncUtf8StrLenAsOutputFunc utf8_str_len_as_enc ;
+} ;
+
+CREncHandler *
+cr_enc_handler_get_instance (enum CREncoding a_enc) ;
+
+enum CRStatus
+cr_enc_handler_resolve_enc_alias (const guchar *a_alias_name,
+ enum CREncoding *a_enc) ;
+
+enum CRStatus
+cr_enc_handler_convert_input (CREncHandler *a_this,
+ const guchar *a_in,
+ gulong *a_in_len,
+ guchar **a_out,
+ gulong *a_out_len) ;
+
+G_END_DECLS
+
+#endif /*__CR_ENC_HANDLER_H__*/
diff --git a/src/st/croco/cr-fonts.c b/src/st/croco/cr-fonts.c
new file mode 100644
index 0000000..a64ffc0
--- /dev/null
+++ b/src/st/croco/cr-fonts.c
@@ -0,0 +1,948 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of
+ * the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the
+ * GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ *See COPYRIGHTS file for copyright information
+ */
+
+#include "cr-fonts.h"
+#include <string.h>
+
+static enum CRStatus
+cr_font_family_to_string_real (CRFontFamily const * a_this,
+ gboolean a_walk_list, GString ** a_string)
+{
+ guchar const *name = NULL;
+ enum CRStatus result = CR_OK;
+
+ if (!*a_string) {
+ *a_string = g_string_new (NULL);
+ g_return_val_if_fail (*a_string,
+ CR_INSTANCIATION_FAILED_ERROR);
+ }
+
+ if (!a_this) {
+ g_string_append (*a_string, "NULL");
+ return CR_OK;
+ }
+
+ switch (a_this->type) {
+ case FONT_FAMILY_SANS_SERIF:
+ name = (guchar const *) "sans-serif";
+ break;
+
+ case FONT_FAMILY_SERIF:
+ name = (guchar const *) "sans-serif";
+ break;
+
+ case FONT_FAMILY_CURSIVE:
+ name = (guchar const *) "cursive";
+ break;
+
+ case FONT_FAMILY_FANTASY:
+ name = (guchar const *) "fantasy";
+ break;
+
+ case FONT_FAMILY_MONOSPACE:
+ name = (guchar const *) "monospace";
+ break;
+
+ case FONT_FAMILY_NON_GENERIC:
+ name = (guchar const *) a_this->name;
+ break;
+
+ default:
+ name = NULL;
+ break;
+ }
+
+ if (name) {
+ if (a_this->prev) {
+ g_string_append_printf (*a_string, ", %s", name);
+ } else {
+ g_string_append (*a_string, (const gchar *) name);
+ }
+ }
+ if (a_walk_list == TRUE && a_this->next) {
+ result = cr_font_family_to_string_real (a_this->next,
+ TRUE, a_string);
+ }
+ return result;
+}
+
+static const gchar *
+cr_predefined_absolute_font_size_to_string (enum CRPredefinedAbsoluteFontSize
+ a_code)
+{
+ gchar const *str = NULL;
+
+ switch (a_code) {
+ case FONT_SIZE_XX_SMALL:
+ str = "xx-small";
+ break;
+ case FONT_SIZE_X_SMALL:
+ str = "x-small";
+ break;
+ case FONT_SIZE_SMALL:
+ str = "small";
+ break;
+ case FONT_SIZE_MEDIUM:
+ str = "medium";
+ break;
+ case FONT_SIZE_LARGE:
+ str = "large";
+ break;
+ case FONT_SIZE_X_LARGE:
+ str = "x-large";
+ break;
+ case FONT_SIZE_XX_LARGE:
+ str = "xx-large";
+ break;
+ default:
+ str = "unknown absolute font size value";
+ }
+ return str;
+}
+
+static const gchar *
+cr_relative_font_size_to_string (enum CRRelativeFontSize a_code)
+{
+ gchar const *str = NULL;
+
+ switch (a_code) {
+ case FONT_SIZE_LARGER:
+ str = "larger";
+ break;
+ case FONT_SIZE_SMALLER:
+ str = "smaller";
+ break;
+ default:
+ str = "unknown relative font size value";
+ break;
+ }
+ return str;
+}
+
+/**
+ * cr_font_family_new:
+ * @a_type: the type of font family to create.
+ * @a_name: the name of the font family.
+ *
+ * create a font family.
+ *
+ * Returns the newly built font family.
+ */
+CRFontFamily *
+cr_font_family_new (enum CRFontFamilyType a_type, guchar * a_name)
+{
+ CRFontFamily *result = NULL;
+
+ result = g_try_malloc (sizeof (CRFontFamily));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRFontFamily));
+ result->type = a_type;
+
+ cr_font_family_set_name (result, a_name);
+
+ return result;
+}
+
+/**
+ * cr_font_family_to_string:
+ * @a_this: the current instance of #CRFontFamily.
+ * @a_walk_font_family_list: whether the serialize the entire list.
+ *
+ * Returns the seriliazed font family. The caller has to free it using
+ * g_free().
+ */
+guchar *
+cr_font_family_to_string (CRFontFamily const * a_this,
+ gboolean a_walk_font_family_list)
+{
+ enum CRStatus status = CR_OK;
+ guchar *result = NULL;
+ GString *stringue = NULL;
+
+ if (!a_this) {
+ result = (guchar *) g_strdup ("NULL");
+ g_return_val_if_fail (result, NULL);
+ return result;
+ }
+ status = cr_font_family_to_string_real (a_this,
+ a_walk_font_family_list,
+ &stringue);
+
+ if (status == CR_OK && stringue) {
+ result = (guchar *) g_string_free (stringue, FALSE);
+ stringue = NULL;
+
+ } else {
+ if (stringue) {
+ g_string_free (stringue, TRUE);
+ stringue = NULL;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * cr_font_family_set_name:
+ * @a_this: the current instance of #CRFontFamily.
+ * @a_name: the new name
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_family_set_name (CRFontFamily * a_this, guchar * a_name)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ /*
+ *only non generic font families can have a name
+ */
+
+ if (a_this->type != FONT_FAMILY_NON_GENERIC) {
+ return CR_BAD_PARAM_ERROR;
+ }
+
+ if (a_this->name) {
+ g_free (a_this->name);
+ a_this->name = NULL;
+ }
+
+ a_this->name = a_name;
+ return CR_OK;
+}
+
+/**
+ * cr_font_family_append:
+ * @a_this: the current instance of #CRFontFamily.
+ * @a_family_to_append: the font family to append to the list
+ *
+ * Returns the new font family list.
+ */
+CRFontFamily *
+cr_font_family_append (CRFontFamily * a_this,
+ CRFontFamily * a_family_to_append)
+{
+ CRFontFamily *cur_ff = NULL;
+
+ g_return_val_if_fail (a_family_to_append, NULL);
+
+ if (!a_this)
+ return a_family_to_append;
+
+ for (cur_ff = a_this; cur_ff && cur_ff->next; cur_ff = cur_ff->next) ;
+
+ cur_ff->next = a_family_to_append;
+ a_family_to_append->prev = cur_ff;
+
+ return a_this;
+
+}
+
+/**
+ * cr_font_family_prepend:
+ * @a_this: the current instance #CRFontFamily.
+ * @a_family_to_prepend: the font family to prepend to the list.
+ *
+ * Returns the font family list.
+ */
+CRFontFamily *
+cr_font_family_prepend (CRFontFamily * a_this,
+ CRFontFamily * a_family_to_prepend)
+{
+ g_return_val_if_fail (a_this && a_family_to_prepend, NULL);
+
+ if (!a_this)
+ return a_family_to_prepend;
+
+ a_family_to_prepend->next = a_this;
+ a_this->prev = a_family_to_prepend;
+
+ return a_family_to_prepend;
+}
+
+/**
+ * cr_font_family_destroy:
+ * @a_this: the current instance of #CRFontFamily.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_family_destroy (CRFontFamily * a_this)
+{
+ CRFontFamily *cur_ff = NULL;
+
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ for (cur_ff = a_this; cur_ff && cur_ff->next; cur_ff = cur_ff->next) ;
+
+ for (; cur_ff; cur_ff = cur_ff->prev) {
+ if (a_this->name) {
+ g_free (a_this->name);
+ a_this->name = NULL;
+ }
+
+ if (cur_ff->next) {
+ g_free (cur_ff->next);
+
+ }
+
+ if (cur_ff->prev == NULL) {
+ g_free (a_this);
+ }
+ }
+
+ return CR_OK;
+}
+
+/***************************************************
+ *'font-size' manipulation functions definitions
+ ***************************************************/
+
+/**
+ * cr_font_size_new:
+ *
+ * Returns the newly created font size.
+ */
+CRFontSize *
+cr_font_size_new (void)
+{
+ CRFontSize *result = NULL;
+
+ result = g_try_malloc (sizeof (CRFontSize));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRFontSize));
+
+ return result;
+}
+
+/**
+ * cr_font_size_clear:
+ * @a_this: the current instance of #CRFontSize
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_size_clear (CRFontSize * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ switch (a_this->type) {
+ case PREDEFINED_ABSOLUTE_FONT_SIZE:
+ case RELATIVE_FONT_SIZE:
+ case INHERITED_FONT_SIZE:
+ memset (a_this, 0, sizeof (CRFontSize));
+ break;
+
+ case ABSOLUTE_FONT_SIZE:
+ memset (a_this, 0, sizeof (CRFontSize));
+ break;
+
+ default:
+ return CR_UNKNOWN_TYPE_ERROR;
+ }
+
+ return CR_OK;
+}
+
+/**
+ * cr_font_size_copy:
+ * @a_dst: the destination #CRFontSize (where to copy to).
+ * @a_src: the source #CRFontSize (where to copy from).
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_size_copy (CRFontSize * a_dst, CRFontSize const * a_src)
+{
+ g_return_val_if_fail (a_dst && a_src, CR_BAD_PARAM_ERROR);
+
+ switch (a_src->type) {
+ case PREDEFINED_ABSOLUTE_FONT_SIZE:
+ case RELATIVE_FONT_SIZE:
+ case INHERITED_FONT_SIZE:
+ cr_font_size_clear (a_dst);
+ memcpy (a_dst, a_src, sizeof (CRFontSize));
+ break;
+
+ case ABSOLUTE_FONT_SIZE:
+ cr_font_size_clear (a_dst);
+ cr_num_copy (&a_dst->value.absolute,
+ &a_src->value.absolute);
+ a_dst->type = a_src->type;
+ break;
+
+ default:
+ return CR_UNKNOWN_TYPE_ERROR;
+ }
+ return CR_OK;
+}
+
+/**
+ * cr_font_size_set_predefined_absolute_font_size:
+ * @a_this: the current instance of #CRFontSize.
+ * @a_predefined: what to set.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_size_set_predefined_absolute_font_size (CRFontSize *a_this,
+ enum CRPredefinedAbsoluteFontSize a_predefined)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ;
+ g_return_val_if_fail (a_predefined >= FONT_SIZE_XX_SMALL
+ && a_predefined < NB_PREDEFINED_ABSOLUTE_FONT_SIZES,
+ CR_BAD_PARAM_ERROR) ;
+
+ a_this->type = PREDEFINED_ABSOLUTE_FONT_SIZE ;
+ a_this->value.predefined = a_predefined ;
+
+ return CR_OK ;
+}
+
+/**
+ * cr_font_size_set_relative_font_size:
+ * @a_this: the current instance of #CRFontSize
+ * @a_relative: the new relative font size
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_size_set_relative_font_size (CRFontSize *a_this,
+ enum CRRelativeFontSize a_relative)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ;
+ g_return_val_if_fail (a_relative >= FONT_SIZE_LARGER
+ && a_relative < NB_RELATIVE_FONT_SIZE,
+ CR_BAD_PARAM_ERROR) ;
+
+ a_this->type = RELATIVE_FONT_SIZE ;
+ a_this->value.relative = a_relative ;
+ return CR_OK ;
+}
+
+/**
+ * cr_font_size_set_absolute_font_size:
+ * @a_this: the current instance of #CRFontSize
+ * @a_num_type: the type of number to set.
+ * @a_value: the actual value to set.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_size_set_absolute_font_size (CRFontSize *a_this,
+ enum CRNumType a_num_type,
+ gdouble a_value)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ;
+ g_return_val_if_fail (a_num_type >= NUM_AUTO
+ && a_num_type < NB_NUM_TYPE,
+ CR_BAD_PARAM_ERROR) ;
+
+ a_this->type = ABSOLUTE_FONT_SIZE ;
+ cr_num_set (&a_this->value.absolute,
+ a_value, a_num_type) ;
+ return CR_OK ;
+}
+
+/**
+ * cr_font_size_set_to_inherit:
+ * @a_this: the current instance of #CRFontSize
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_font_size_set_to_inherit (CRFontSize *a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ;
+
+ cr_font_size_clear (a_this) ;
+ a_this->type = INHERITED_FONT_SIZE ;
+
+ return CR_OK ;
+}
+
+/**
+ * cr_font_size_is_set_to_inherit:
+ * @a_this: the current instance of #CRFontSize.
+ *
+ * Returns TRUE if the current instance is set to 'inherit'.
+ */
+gboolean
+cr_font_size_is_set_to_inherit (CRFontSize const *a_this)
+{
+ g_return_val_if_fail (a_this, FALSE) ;
+
+ return a_this->type == INHERITED_FONT_SIZE ;
+}
+
+/**
+ * cr_font_size_to_string:
+ * @a_this: the current instance of #CRFontSize
+ *
+ * Returns the serialized form of #CRFontSize. The returned string
+ * has to bee freed using g_free().
+ */
+gchar *
+cr_font_size_to_string (CRFontSize const * a_this)
+{
+ gchar *str = NULL;
+
+ if (!a_this) {
+ str = g_strdup ("NULL");
+ g_return_val_if_fail (str, NULL);
+ return str;
+ }
+ switch (a_this->type) {
+ case PREDEFINED_ABSOLUTE_FONT_SIZE:
+ str = g_strdup (cr_predefined_absolute_font_size_to_string
+ (a_this->value.predefined));
+ break;
+ case ABSOLUTE_FONT_SIZE:
+ str = (gchar *) cr_num_to_string (&a_this->value.absolute);
+ break;
+ case RELATIVE_FONT_SIZE:
+ str = g_strdup (cr_relative_font_size_to_string
+ (a_this->value.relative));
+ break;
+ case INHERITED_FONT_SIZE:
+ str = g_strdup ("inherit");
+ break;
+ default:
+ break;
+ }
+ return str;
+}
+
+/**
+ * cr_font_size_get_smaller_predefined:
+ * @a_font_size: the font size to consider.
+ * @a_smaller_size: out parameter. The a smaller value than @a_font_size.
+ */
+void
+cr_font_size_get_smaller_predefined_font_size
+ (enum CRPredefinedAbsoluteFontSize a_font_size,
+ enum CRPredefinedAbsoluteFontSize *a_smaller_size)
+{
+ enum CRPredefinedAbsoluteFontSize result = FONT_SIZE_MEDIUM ;
+
+ g_return_if_fail (a_smaller_size) ;
+ g_return_if_fail (a_font_size < NB_PREDEFINED_ABSOLUTE_FONT_SIZES
+ && a_font_size >= FONT_SIZE_XX_SMALL) ;
+
+ switch (a_font_size) {
+ case FONT_SIZE_XX_SMALL:
+ result = FONT_SIZE_XX_SMALL ;
+ break ;
+ case FONT_SIZE_X_SMALL:
+ result = FONT_SIZE_XX_SMALL ;
+ break ;
+ case FONT_SIZE_SMALL:
+ result = FONT_SIZE_X_SMALL;
+ break ;
+ case FONT_SIZE_MEDIUM:
+ result = FONT_SIZE_SMALL;
+ break ;
+ case FONT_SIZE_LARGE:
+ result = FONT_SIZE_MEDIUM;
+ break ;
+ case FONT_SIZE_X_LARGE:
+ result = FONT_SIZE_LARGE;
+ break ;
+ case FONT_SIZE_XX_LARGE:
+ result = FONT_SIZE_XX_LARGE;
+ break ;
+ case FONT_SIZE_INHERIT:
+ cr_utils_trace_info ("can't return a smaller size for FONT_SIZE_INHERIT") ;
+ result = FONT_SIZE_MEDIUM ;
+ break ;
+ default:
+ cr_utils_trace_info ("Unknown FONT_SIZE") ;
+ result = FONT_SIZE_MEDIUM ;
+ break ;
+ }
+ *a_smaller_size = result ;
+}
+
+
+/**
+ * cr_font_size_get_larger_predefined_font_size:
+ * @a_font_size: the font size to consider.
+ * @a_larger_size: out parameter. the font size considered larger than
+ * @a_font_size.
+ *
+ */
+void
+cr_font_size_get_larger_predefined_font_size
+ (enum CRPredefinedAbsoluteFontSize a_font_size,
+ enum CRPredefinedAbsoluteFontSize *a_larger_size)
+{
+ enum CRPredefinedAbsoluteFontSize result = FONT_SIZE_MEDIUM ;
+
+ g_return_if_fail (a_larger_size) ;
+ g_return_if_fail (a_font_size >= FONT_SIZE_XX_SMALL
+ && a_font_size < NB_PREDEFINED_ABSOLUTE_FONT_SIZES) ;
+
+ switch (a_font_size) {
+ case FONT_SIZE_XX_SMALL:
+ result = FONT_SIZE_X_SMALL ;
+ break ;
+ case FONT_SIZE_X_SMALL:
+ result = FONT_SIZE_SMALL ;
+ break ;
+ case FONT_SIZE_SMALL:
+ result = FONT_SIZE_MEDIUM;
+ break ;
+ case FONT_SIZE_MEDIUM:
+ result = FONT_SIZE_LARGE;
+ break ;
+ case FONT_SIZE_LARGE:
+ result = FONT_SIZE_X_LARGE;
+ break ;
+ case FONT_SIZE_X_LARGE:
+ result = FONT_SIZE_XX_LARGE ;
+ break ;
+ case FONT_SIZE_XX_LARGE:
+ result = FONT_SIZE_XX_LARGE;
+ break ;
+ case FONT_SIZE_INHERIT:
+ cr_utils_trace_info ("can't return a bigger size for FONT_SIZE_INHERIT") ;
+ result = FONT_SIZE_MEDIUM ;
+ break ;
+ default:
+ cr_utils_trace_info ("Unknown FONT_SIZE") ;
+ result = FONT_SIZE_MEDIUM ;
+ break ;
+ }
+ *a_larger_size = result ;
+}
+
+/**
+ * cr_font_size_is_predefined_absolute_font_size:
+ * @a_font_size: the font size to consider.
+ *
+ * Returns TRUE if the instance is an predefined absolute font size, FALSE
+ * otherwise.
+ */
+gboolean
+cr_font_size_is_predefined_absolute_font_size
+ (enum CRPredefinedAbsoluteFontSize a_font_size)
+{
+ if (a_font_size >= FONT_SIZE_XX_SMALL
+ && a_font_size < NB_PREDEFINED_ABSOLUTE_FONT_SIZES) {
+ return TRUE ;
+ } else {
+ return FALSE ;
+ }
+}
+
+/**
+ * cr_font_size_adjust_to_string:
+ * @a_this: the instance of #CRFontSizeAdjust.
+ *
+ * Returns the serialized form of #CRFontSizeAdjust
+ */
+gchar *
+cr_font_size_adjust_to_string (CRFontSizeAdjust const * a_this)
+{
+ gchar *str = NULL;
+
+ if (!a_this) {
+ str = g_strdup ("NULL");
+ g_return_val_if_fail (str, NULL);
+ return str;
+ }
+
+ switch (a_this->type) {
+ case FONT_SIZE_ADJUST_NONE:
+ str = g_strdup ("none");
+ break;
+ case FONT_SIZE_ADJUST_NUMBER:
+ if (a_this->num)
+ str = (gchar *) cr_num_to_string (a_this->num);
+ else
+ str = g_strdup ("unknown font-size-adjust property value"); /* Should raise an error no?*/
+ break;
+ case FONT_SIZE_ADJUST_INHERIT:
+ str = g_strdup ("inherit");
+ }
+ return str;
+}
+
+/**
+ * cr_font_style_to_string:
+ * @a_code: the current instance of #CRFontStyle .
+ *
+ * Returns the serialized #CRFontStyle. The caller must free the returned
+ * string using g_free().
+ */
+const gchar *
+cr_font_style_to_string (enum CRFontStyle a_code)
+{
+ gchar *str = NULL;
+
+ switch (a_code) {
+ case FONT_STYLE_NORMAL:
+ str = (gchar *) "normal";
+ break;
+ case FONT_STYLE_ITALIC:
+ str = (gchar *) "italic";
+ break;
+ case FONT_STYLE_OBLIQUE:
+ str = (gchar *) "oblique";
+ break;
+ case FONT_STYLE_INHERIT:
+ str = (gchar *) "inherit";
+ break;
+ default:
+ str = (gchar *) "unknown font style value";
+ break;
+ }
+ return str;
+}
+
+/**
+ * cr_font_variant_to_string:
+ * @a_code: the current instance of #CRFontVariant.
+ *
+ * Returns the serialized form of #CRFontVariant. The caller has
+ * to free the returned string using g_free().
+ */
+const gchar *
+cr_font_variant_to_string (enum CRFontVariant a_code)
+{
+ gchar *str = NULL;
+
+ switch (a_code) {
+ case FONT_VARIANT_NORMAL:
+ str = (gchar *) "normal";
+ break;
+ case FONT_VARIANT_SMALL_CAPS:
+ str = (gchar *) "small-caps";
+ break;
+ case FONT_VARIANT_INHERIT:
+ str = (gchar *) "inherit";
+ break;
+ }
+ return str;
+}
+
+/**
+ * cr_font_weight_get_bolder:
+ * @a_weight: the #CRFontWeight to consider.
+ *
+ * Returns a font weight bolder than @a_weight
+ */
+enum CRFontWeight
+cr_font_weight_get_bolder (enum CRFontWeight a_weight)
+{
+ if (a_weight == FONT_WEIGHT_INHERIT) {
+ cr_utils_trace_info ("can't return a bolder weight for FONT_WEIGHT_INHERIT") ;
+ return a_weight;
+ } else if (a_weight >= FONT_WEIGHT_900) {
+ return FONT_WEIGHT_900 ;
+ } else if (a_weight < FONT_WEIGHT_NORMAL) {
+ return FONT_WEIGHT_NORMAL ;
+ } else if (a_weight == FONT_WEIGHT_BOLDER
+ || a_weight == FONT_WEIGHT_LIGHTER) {
+ cr_utils_trace_info ("FONT_WEIGHT_BOLDER or FONT_WEIGHT_LIGHTER should not appear here") ;
+ return FONT_WEIGHT_NORMAL ;
+ } else {
+ return a_weight << 1 ;
+ }
+}
+
+/**
+ * cr_font_weight_to_string:
+ * @a_code: the font weight to consider.
+ *
+ * Returns the serialized form of #CRFontWeight.
+ */
+const gchar *
+cr_font_weight_to_string (enum CRFontWeight a_code)
+{
+ gchar *str = NULL;
+
+ switch (a_code) {
+ case FONT_WEIGHT_NORMAL:
+ str = (gchar *) "normal";
+ break;
+ case FONT_WEIGHT_BOLD:
+ str = (gchar *) "bold";
+ break;
+ case FONT_WEIGHT_BOLDER:
+ str = (gchar *) "bolder";
+ break;
+ case FONT_WEIGHT_LIGHTER:
+ str = (gchar *) "lighter";
+ break;
+ case FONT_WEIGHT_100:
+ str = (gchar *) "100";
+ break;
+ case FONT_WEIGHT_200:
+ str = (gchar *) "200";
+ break;
+ case FONT_WEIGHT_300:
+ str = (gchar *) "300";
+ break;
+ case FONT_WEIGHT_400:
+ str = (gchar *) "400";
+ break;
+ case FONT_WEIGHT_500:
+ str = (gchar *) "500";
+ break;
+ case FONT_WEIGHT_600:
+ str = (gchar *) "600";
+ break;
+ case FONT_WEIGHT_700:
+ str = (gchar *) "700";
+ break;
+ case FONT_WEIGHT_800:
+ str = (gchar *) "800";
+ break;
+ case FONT_WEIGHT_900:
+ str = (gchar *) "900";
+ break;
+ case FONT_WEIGHT_INHERIT:
+ str = (gchar *) "inherit";
+ break;
+ default:
+ str = (gchar *) "unknown font-weight property value";
+ break;
+ }
+ return str;
+}
+
+/**
+ * cr_font_stretch_to_string:
+ * @a_code: the instance of #CRFontStretch to consider.
+ *
+ * Returns the serialized form of #CRFontStretch.
+ */
+const gchar *
+cr_font_stretch_to_string (enum CRFontStretch a_code)
+{
+ gchar *str = NULL;
+
+ switch (a_code) {
+ case FONT_STRETCH_NORMAL:
+ str = (gchar *) "normal";
+ break;
+ case FONT_STRETCH_WIDER:
+ str = (gchar *) "wider";
+ break;
+ case FONT_STRETCH_NARROWER:
+ str = (gchar *) "narrower";
+ break;
+ case FONT_STRETCH_ULTRA_CONDENSED:
+ str = (gchar *) "ultra-condensed";
+ break;
+ case FONT_STRETCH_EXTRA_CONDENSED:
+ str = (gchar *) "extra-condensed";
+ break;
+ case FONT_STRETCH_CONDENSED:
+ str = (gchar *) "condensed";
+ break;
+ case FONT_STRETCH_SEMI_CONDENSED:
+ str = (gchar *) "semi-condensed";
+ break;
+ case FONT_STRETCH_SEMI_EXPANDED:
+ str = (gchar *) "semi-expanded";
+ break;
+ case FONT_STRETCH_EXPANDED:
+ str = (gchar *) "expanded";
+ break;
+ case FONT_STRETCH_EXTRA_EXPANDED:
+ str = (gchar *) "extra-expaned";
+ break;
+ case FONT_STRETCH_ULTRA_EXPANDED:
+ str = (gchar *) "ultra-expanded";
+ break;
+ case FONT_STRETCH_INHERIT:
+ str = (gchar *) "inherit";
+ break;
+ }
+ return str;
+}
+
+/**
+ * cr_font_size_destroy:
+ * @a_font_size: the font size to destroy
+ *
+ */
+void
+cr_font_size_destroy (CRFontSize * a_font_size)
+{
+ g_return_if_fail (a_font_size);
+
+ g_free (a_font_size) ;
+}
+
+/*******************************************************
+ *'font-size-adjust' manipulation function definition
+ *******************************************************/
+
+/**
+ * cr_font_size_adjust_new:
+ *
+ * Returns a newly built instance of #CRFontSizeAdjust
+ */
+CRFontSizeAdjust *
+cr_font_size_adjust_new (void)
+{
+ CRFontSizeAdjust *result = NULL;
+
+ result = g_try_malloc (sizeof (CRFontSizeAdjust));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRFontSizeAdjust));
+
+ return result;
+}
+
+/**
+ * cr_font_size_adjust_destroy:
+ * @a_this: the current instance of #CRFontSizeAdjust.
+ *
+ */
+void
+cr_font_size_adjust_destroy (CRFontSizeAdjust * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->type == FONT_SIZE_ADJUST_NUMBER && a_this->num) {
+ cr_num_destroy (a_this->num);
+ a_this->num = NULL;
+ }
+}
diff --git a/src/st/croco/cr-fonts.h b/src/st/croco/cr-fonts.h
new file mode 100644
index 0000000..9eaeeeb
--- /dev/null
+++ b/src/st/croco/cr-fonts.h
@@ -0,0 +1,315 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of
+ * the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the
+ * GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_FONTS_H__
+#define __CR_FONTS_H__
+
+#include "cr-utils.h"
+#include "cr-num.h"
+
+/**
+ *@file
+ *Various type declarations about font selection related
+ *properties.
+ */
+G_BEGIN_DECLS
+
+
+enum CRFontFamilyType
+{
+ FONT_FAMILY_SANS_SERIF,
+ FONT_FAMILY_SERIF,
+ FONT_FAMILY_CURSIVE,
+ FONT_FAMILY_FANTASY,
+ FONT_FAMILY_MONOSPACE,
+ FONT_FAMILY_NON_GENERIC,
+ FONT_FAMILY_INHERIT,
+ /**/
+ NB_FONT_FAMILIE_TYPES
+} ;
+
+typedef struct _CRFontFamily CRFontFamily ;
+
+struct _CRFontFamily
+{
+ enum CRFontFamilyType type ;
+
+ /*
+ *The name of the font family, in case
+ *it is non generic.
+ *Is set only if the type is FONT_FAMILY_NON_GENERIC.
+ */
+ guchar *name ;
+
+ CRFontFamily *next ;
+ CRFontFamily *prev ;
+} ;
+
+
+/**
+ *The different types
+ *of absolute font size.
+ *This is used by the 'font-size'
+ *property defined in css2 spec
+ *in chapter 15.2.4 .
+ *These values a indexes of
+ *table of size so please, do not
+ *change their definition order unless
+ *you know what you are doing.
+ */
+enum CRPredefinedAbsoluteFontSize
+{
+ FONT_SIZE_XX_SMALL=0,
+ FONT_SIZE_X_SMALL,
+ FONT_SIZE_SMALL,
+ FONT_SIZE_MEDIUM,
+ FONT_SIZE_LARGE,
+ FONT_SIZE_X_LARGE,
+ FONT_SIZE_XX_LARGE,
+ FONT_SIZE_INHERIT,
+ NB_PREDEFINED_ABSOLUTE_FONT_SIZES
+} ;
+
+/**
+ *The different types
+ *of relative font size.
+ *This is used by the 'font-size'
+ *property defined in css2 spec
+ *in chapter 15.2.4 .
+ *These values a indexes of
+ *table of size so please, do not
+ *change their definition order unless
+ *you know what you are doing.
+ */
+enum CRRelativeFontSize
+{
+ FONT_SIZE_LARGER,
+ FONT_SIZE_SMALLER,
+ NB_RELATIVE_FONT_SIZE
+} ;
+
+/**
+ *The type of font-size property.
+ *Used to define the type of #CRFontSize .
+ *See css2 spec chapter 15.2.4 to understand.
+ */
+enum CRFontSizeType {
+ /**
+ *If the type of #CRFontSize is
+ *PREDEFINED_ABSOLUTE_FONT_SIZE,
+ *the CRFontSize::value.predefined_absolute
+ *field will be defined.
+ */
+ PREDEFINED_ABSOLUTE_FONT_SIZE,
+
+ /**
+ *If the type of #CRFontSize is
+ *ABSOLUTE_FONT_SIZE,
+ *the CRFontSize::value.absolute
+ *field will be defined.
+ */
+ ABSOLUTE_FONT_SIZE,
+
+ /**
+ *If the type of #CRFontSize is
+ *RELATIVE_FONT_SIZE,
+ *the CRFontSize::value.relative
+ *field will be defined.
+ */
+ RELATIVE_FONT_SIZE,
+
+ /**
+ *If the type of #CRFontSize is
+ *INHERITED_FONT_SIZE,
+ *the None of the field of the CRFontSize::value enum
+ *will be defined.
+ */
+ INHERITED_FONT_SIZE,
+
+ NB_FONT_SIZE_TYPE
+} ;
+
+typedef struct _CRFontSize CRFontSize ;
+struct _CRFontSize {
+ enum CRFontSizeType type ;
+ union {
+ enum CRPredefinedAbsoluteFontSize predefined ;
+ enum CRRelativeFontSize relative ;
+ CRNum absolute ;
+ } value;
+} ;
+
+enum CRFontSizeAdjustType
+{
+ FONT_SIZE_ADJUST_NONE = 0,
+ FONT_SIZE_ADJUST_NUMBER,
+ FONT_SIZE_ADJUST_INHERIT
+} ;
+typedef struct _CRFontSizeAdjust CRFontSizeAdjust ;
+struct _CRFontSizeAdjust
+{
+ enum CRFontSizeAdjustType type ;
+ CRNum *num ;
+} ;
+
+enum CRFontStyle
+{
+ FONT_STYLE_NORMAL=0,
+ FONT_STYLE_ITALIC,
+ FONT_STYLE_OBLIQUE,
+ FONT_STYLE_INHERIT
+} ;
+
+enum CRFontVariant
+{
+ FONT_VARIANT_NORMAL=0,
+ FONT_VARIANT_SMALL_CAPS,
+ FONT_VARIANT_INHERIT
+} ;
+
+enum CRFontWeight
+{
+ FONT_WEIGHT_NORMAL = 1,
+ FONT_WEIGHT_BOLD = 1<<1,
+ FONT_WEIGHT_BOLDER = 1<<2,
+ FONT_WEIGHT_LIGHTER = 1<<3,
+ FONT_WEIGHT_100 = 1<<4,
+ FONT_WEIGHT_200 = 1<<5,
+ FONT_WEIGHT_300 = 1<<6,
+ FONT_WEIGHT_400 = 1<<7,
+ FONT_WEIGHT_500 = 1<<8,
+ FONT_WEIGHT_600 = 1<<9,
+ FONT_WEIGHT_700 = 1<<10,
+ FONT_WEIGHT_800 = 1<<11,
+ FONT_WEIGHT_900 = 1<<12,
+ FONT_WEIGHT_INHERIT = 1<<13,
+ NB_FONT_WEIGHTS
+} ;
+
+enum CRFontStretch
+{
+ FONT_STRETCH_NORMAL=0,
+ FONT_STRETCH_WIDER,
+ FONT_STRETCH_NARROWER,
+ FONT_STRETCH_ULTRA_CONDENSED,
+ FONT_STRETCH_EXTRA_CONDENSED,
+ FONT_STRETCH_CONDENSED,
+ FONT_STRETCH_SEMI_CONDENSED,
+ FONT_STRETCH_SEMI_EXPANDED,
+ FONT_STRETCH_EXPANDED,
+ FONT_STRETCH_EXTRA_EXPANDED,
+ FONT_STRETCH_ULTRA_EXPANDED,
+ FONT_STRETCH_INHERIT
+} ;
+
+/**************************************
+ *'font-family' manipulation functions
+ ***************************************/
+CRFontFamily *
+cr_font_family_new (enum CRFontFamilyType a_type, guchar *a_name) ;
+
+CRFontFamily *
+cr_font_family_append (CRFontFamily *a_this,
+ CRFontFamily *a_family_to_append) ;
+
+guchar *
+cr_font_family_to_string (CRFontFamily const *a_this,
+ gboolean a_walk_font_family_list) ;
+
+CRFontFamily *
+cr_font_family_prepend (CRFontFamily *a_this,
+ CRFontFamily *a_family_to_prepend);
+
+enum CRStatus
+cr_font_family_destroy (CRFontFamily *a_this) ;
+
+enum CRStatus
+cr_font_family_set_name (CRFontFamily *a_this, guchar *a_name) ;
+
+
+/************************************
+ *'font-size' manipulation functions
+ ***********************************/
+
+CRFontSize * cr_font_size_new (void) ;
+
+enum CRStatus cr_font_size_clear (CRFontSize *a_this) ;
+
+enum CRStatus cr_font_size_copy (CRFontSize *a_dst,
+ CRFontSize const *a_src) ;
+enum CRStatus cr_font_size_set_predefined_absolute_font_size (CRFontSize *a_this,
+ enum CRPredefinedAbsoluteFontSize a_predefined) ;
+enum CRStatus cr_font_size_set_relative_font_size (CRFontSize *a_this,
+ enum CRRelativeFontSize a_relative) ;
+
+enum CRStatus cr_font_size_set_absolute_font_size (CRFontSize *a_this,
+ enum CRNumType a_num_type,
+ gdouble a_value) ;
+
+enum CRStatus cr_font_size_set_to_inherit (CRFontSize *a_this) ;
+
+gboolean cr_font_size_is_set_to_inherit (CRFontSize const *a_this) ;
+
+gchar* cr_font_size_to_string (CRFontSize const *a_this) ;
+
+void cr_font_size_destroy (CRFontSize *a_font_size) ;
+
+/*******************************************************
+ *'font-size-adjust' manipulation function declarations
+ *******************************************************/
+
+CRFontSizeAdjust * cr_font_size_adjust_new (void) ;
+
+gchar * cr_font_size_adjust_to_string (CRFontSizeAdjust const *a_this) ;
+
+void cr_font_size_adjust_destroy (CRFontSizeAdjust *a_this) ;
+
+void
+cr_font_size_get_smaller_predefined_font_size (enum CRPredefinedAbsoluteFontSize a_font_size,
+ enum CRPredefinedAbsoluteFontSize *a_smaller_size) ;
+void
+cr_font_size_get_larger_predefined_font_size (enum CRPredefinedAbsoluteFontSize a_font_size,
+ enum CRPredefinedAbsoluteFontSize *a_larger_size) ;
+
+gboolean
+cr_font_size_is_predefined_absolute_font_size (enum CRPredefinedAbsoluteFontSize a_font_size) ;
+
+/***********************************
+ *various other font related functions
+ ***********************************/
+const gchar * cr_font_style_to_string (enum CRFontStyle a_code) ;
+
+const gchar * cr_font_weight_to_string (enum CRFontWeight a_code) ;
+
+enum CRFontWeight
+cr_font_weight_get_bolder (enum CRFontWeight a_weight) ;
+
+const gchar * cr_font_variant_to_string (enum CRFontVariant a_code) ;
+
+const gchar * cr_font_stretch_to_string (enum CRFontStretch a_code) ;
+
+G_END_DECLS
+
+#endif
diff --git a/src/st/croco/cr-input.c b/src/st/croco/cr-input.c
new file mode 100644
index 0000000..430e75e
--- /dev/null
+++ b/src/st/croco/cr-input.c
@@ -0,0 +1,1191 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include "stdio.h"
+#include <string.h>
+#include "cr-input.h"
+#include "cr-enc-handler.h"
+
+/**
+ *@CRInput:
+ *
+ *The definition of the #CRInput class.
+ */
+
+/*******************
+ *Private type defs
+ *******************/
+
+/**
+ *The private attributes of
+ *the #CRInputPriv class.
+ */
+struct _CRInputPriv {
+ /*
+ *The input buffer
+ */
+ guchar *in_buf;
+ gulong in_buf_size;
+
+ gulong nb_bytes;
+
+ /*
+ *The index of the next byte
+ *to be read.
+ */
+ gulong next_byte_index;
+
+ /*
+ *The current line number
+ */
+ gulong line;
+
+ /*
+ *The current col number
+ */
+ gulong col;
+
+ gboolean end_of_line;
+ gboolean end_of_input;
+
+ /*
+ *the reference count of this
+ *instance.
+ */
+ guint ref_count;
+ gboolean free_in_buf;
+};
+
+#define PRIVATE(object) (object)->priv
+
+/***************************
+ *private constants
+ **************************/
+#define CR_INPUT_MEM_CHUNK_SIZE 1024 * 4
+
+static CRInput *cr_input_new_real (void);
+
+static CRInput *
+cr_input_new_real (void)
+{
+ CRInput *result = NULL;
+
+ result = g_try_malloc (sizeof (CRInput));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRInput));
+
+ PRIVATE (result) = g_try_malloc (sizeof (CRInputPriv));
+ if (!PRIVATE (result)) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+ memset (PRIVATE (result), 0, sizeof (CRInputPriv));
+ PRIVATE (result)->free_in_buf = TRUE;
+ return result;
+}
+
+/****************
+ *Public methods
+ ***************/
+
+/**
+ * cr_input_new_from_buf:
+ *@a_buf: the memory buffer to create the input stream from.
+ *The #CRInput keeps this pointer so user should not free it !.
+ *@a_len: the size of the input buffer.
+ *@a_enc: the buffer's encoding.
+ *@a_free_buf: if set to TRUE, this a_buf will be freed
+ *at the destruction of this instance. If set to false, it is up
+ *to the caller to free it.
+ *
+ *Creates a new input stream from a memory buffer.
+ *Returns the newly built instance of #CRInput.
+ */
+CRInput *
+cr_input_new_from_buf (guchar * a_buf,
+ gulong a_len,
+ enum CREncoding a_enc,
+ gboolean a_free_buf)
+{
+ CRInput *result = NULL;
+ enum CRStatus status = CR_OK;
+ CREncHandler *enc_handler = NULL;
+ gulong len = a_len;
+
+ g_return_val_if_fail (a_buf, NULL);
+
+ result = cr_input_new_real ();
+ g_return_val_if_fail (result, NULL);
+
+ /*transform the encoding in utf8 */
+ if (a_enc != CR_UTF_8) {
+ enc_handler = cr_enc_handler_get_instance (a_enc);
+ if (!enc_handler) {
+ goto error;
+ }
+
+ status = cr_enc_handler_convert_input
+ (enc_handler, a_buf, &len,
+ &PRIVATE (result)->in_buf,
+ &PRIVATE (result)->in_buf_size);
+ if (status != CR_OK)
+ goto error;
+ PRIVATE (result)->free_in_buf = TRUE;
+ if (a_free_buf == TRUE && a_buf) {
+ g_free (a_buf) ;
+ a_buf = NULL ;
+ }
+ PRIVATE (result)->nb_bytes = PRIVATE (result)->in_buf_size;
+ } else {
+ PRIVATE (result)->in_buf = (guchar *) a_buf;
+ PRIVATE (result)->in_buf_size = a_len;
+ PRIVATE (result)->nb_bytes = a_len;
+ PRIVATE (result)->free_in_buf = a_free_buf;
+ }
+ PRIVATE (result)->line = 1;
+ PRIVATE (result)->col = 0;
+ return result;
+
+ error:
+ if (result) {
+ cr_input_destroy (result);
+ result = NULL;
+ }
+
+ return NULL;
+}
+
+/**
+ * cr_input_new_from_uri:
+ *@a_file_uri: the file to create *the input stream from.
+ *@a_enc: the encoding of the file *to create the input from.
+ *
+ *Creates a new input stream from
+ *a file.
+ *
+ *Returns the newly created input stream if
+ *this method could read the file and create it,
+ *NULL otherwise.
+ */
+
+CRInput *
+cr_input_new_from_uri (const gchar * a_file_uri, enum CREncoding a_enc)
+{
+ CRInput *result = NULL;
+ enum CRStatus status = CR_OK;
+ FILE *file_ptr = NULL;
+ guchar tmp_buf[CR_INPUT_MEM_CHUNK_SIZE] = { 0 };
+ gulong nb_read = 0,
+ len = 0,
+ buf_size = 0;
+ gboolean loop = TRUE;
+ guchar *buf = NULL;
+
+ g_return_val_if_fail (a_file_uri, NULL);
+
+ file_ptr = fopen (a_file_uri, "r");
+
+ if (file_ptr == NULL) {
+
+#ifdef CR_DEBUG
+ cr_utils_trace_debug ("could not open file");
+#endif
+ g_warning ("Could not open file %s\n", a_file_uri);
+
+ return NULL;
+ }
+
+ /*load the file */
+ while (loop) {
+ nb_read = fread (tmp_buf, 1 /*read bytes */ ,
+ CR_INPUT_MEM_CHUNK_SIZE /*nb of bytes */ ,
+ file_ptr);
+
+ if (nb_read != CR_INPUT_MEM_CHUNK_SIZE) {
+ /*we read less chars than we wanted */
+ if (feof (file_ptr)) {
+ /*we reached eof */
+ loop = FALSE;
+ } else {
+ /*a pb occurred !! */
+ cr_utils_trace_debug ("an io error occurred");
+ status = CR_ERROR;
+ goto cleanup;
+ }
+ }
+
+ if (status == CR_OK) {
+ /*read went well */
+ buf = g_realloc (buf, len + CR_INPUT_MEM_CHUNK_SIZE);
+ memcpy (buf + len, tmp_buf, nb_read);
+ len += nb_read;
+ buf_size += CR_INPUT_MEM_CHUNK_SIZE;
+ }
+ }
+
+ if (status == CR_OK) {
+ result = cr_input_new_from_buf (buf, len, a_enc, TRUE);
+ if (!result) {
+ goto cleanup;
+ }
+ /*
+ *we should free buf here because it's own by CRInput.
+ *(see the last parameter of cr_input_new_from_buf().
+ */
+ buf = NULL;
+ }
+
+ cleanup:
+ if (file_ptr) {
+ fclose (file_ptr);
+ file_ptr = NULL;
+ }
+
+ if (buf) {
+ g_free (buf);
+ buf = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_input_destroy:
+ *@a_this: the current instance of #CRInput.
+ *
+ *The destructor of the #CRInput class.
+ */
+void
+cr_input_destroy (CRInput * a_this)
+{
+ if (a_this == NULL)
+ return;
+
+ if (PRIVATE (a_this)) {
+ if (PRIVATE (a_this)->in_buf && PRIVATE (a_this)->free_in_buf) {
+ g_free (PRIVATE (a_this)->in_buf);
+ PRIVATE (a_this)->in_buf = NULL;
+ }
+
+ g_free (PRIVATE (a_this));
+ PRIVATE (a_this) = NULL;
+ }
+
+ g_free (a_this);
+}
+
+/**
+ * cr_input_ref:
+ *@a_this: the current instance of #CRInput.
+ *
+ *Increments the reference count of the current
+ *instance of #CRInput.
+ */
+void
+cr_input_ref (CRInput * a_this)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this));
+
+ PRIVATE (a_this)->ref_count++;
+}
+
+/**
+ * cr_input_unref:
+ *@a_this: the current instance of #CRInput.
+ *
+ *Decrements the reference count of this instance
+ *of #CRInput. If the reference count goes down to
+ *zero, this instance is destroyed.
+ *
+ * Returns TRUE if the instance of #CRInput got destroyed, false otherwise.
+ */
+gboolean
+cr_input_unref (CRInput * a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE);
+
+ if (PRIVATE (a_this)->ref_count) {
+ PRIVATE (a_this)->ref_count--;
+ }
+
+ if (PRIVATE (a_this)->ref_count == 0) {
+ cr_input_destroy (a_this);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * cr_input_end_of_input:
+ *@a_this: the current instance of #CRInput.
+ *@a_end_of_input: out parameter. Is set to TRUE if
+ *the current instance has reached the end of its input buffer,
+ *FALSE otherwise.
+ *
+ *Tests whether the current instance of
+ *#CRInput has reached its input buffer.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ * Note that all the out parameters of this method are valid if
+ * and only if this method returns CR_OK.
+ */
+enum CRStatus
+cr_input_end_of_input (CRInput const * a_this, gboolean * a_end_of_input)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_end_of_input, CR_BAD_PARAM_ERROR);
+
+ *a_end_of_input = (PRIVATE (a_this)->next_byte_index
+ >= PRIVATE (a_this)->in_buf_size) ? TRUE : FALSE;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_get_nb_bytes_left:
+ *@a_this: the current instance of #CRInput.
+ *
+ *Returns the number of bytes left in the input stream
+ *before the end, -1 in case of error.
+ */
+glong
+cr_input_get_nb_bytes_left (CRInput const * a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), -1);
+ g_return_val_if_fail (PRIVATE (a_this)->nb_bytes
+ <= PRIVATE (a_this)->in_buf_size, -1);
+ g_return_val_if_fail (PRIVATE (a_this)->next_byte_index
+ <= PRIVATE (a_this)->nb_bytes, -1);
+
+ if (PRIVATE (a_this)->end_of_input)
+ return 0;
+
+ return PRIVATE (a_this)->nb_bytes - PRIVATE (a_this)->next_byte_index;
+}
+
+/**
+ * cr_input_read_byte:
+ *@a_this: the current instance of #CRInput.
+ *@a_byte: out parameter the returned byte.
+ *
+ *Gets the next byte of the input.
+ *Updates the state of the input so that
+ *the next invocation of this method returns
+ *the next coming byte.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise. All the out parameters of this method are valid if
+ *and only if this method returns CR_OK.
+ */
+enum CRStatus
+cr_input_read_byte (CRInput * a_this, guchar * a_byte)
+{
+ gulong nb_bytes_left = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_byte, CR_BAD_PARAM_ERROR);
+
+ g_return_val_if_fail (PRIVATE (a_this)->next_byte_index <=
+ PRIVATE (a_this)->nb_bytes, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->end_of_input == TRUE)
+ return CR_END_OF_INPUT_ERROR;
+
+ nb_bytes_left = cr_input_get_nb_bytes_left (a_this);
+
+ if (nb_bytes_left < 1) {
+ return CR_END_OF_INPUT_ERROR;
+ }
+
+ *a_byte = PRIVATE (a_this)->in_buf[PRIVATE (a_this)->next_byte_index];
+
+ if (PRIVATE (a_this)->nb_bytes -
+ PRIVATE (a_this)->next_byte_index < 2) {
+ PRIVATE (a_this)->end_of_input = TRUE;
+ } else {
+ PRIVATE (a_this)->next_byte_index++;
+ }
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_read_char:
+ *@a_this: the current instance of CRInput.
+ *@a_char: out parameter. The read character.
+ *
+ *Reads an unicode character from the current instance of
+ *#CRInput.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_input_read_char (CRInput * a_this, guint32 * a_char)
+{
+ enum CRStatus status = CR_OK;
+ gulong consumed = 0,
+ nb_bytes_left = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char,
+ CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->end_of_input == TRUE)
+ return CR_END_OF_INPUT_ERROR;
+
+ nb_bytes_left = cr_input_get_nb_bytes_left (a_this);
+
+ if (nb_bytes_left < 1) {
+ return CR_END_OF_INPUT_ERROR;
+ }
+
+ status = cr_utils_read_char_from_utf8_buf
+ (PRIVATE (a_this)->in_buf
+ +
+ PRIVATE (a_this)->next_byte_index,
+ nb_bytes_left, a_char, &consumed);
+
+ if (status == CR_OK) {
+ /*update next byte index */
+ PRIVATE (a_this)->next_byte_index += consumed;
+
+ /*update line and column number */
+ if (PRIVATE (a_this)->end_of_line == TRUE) {
+ PRIVATE (a_this)->col = 1;
+ PRIVATE (a_this)->line++;
+ PRIVATE (a_this)->end_of_line = FALSE;
+ } else if (*a_char != '\n') {
+ PRIVATE (a_this)->col++;
+ }
+
+ if (*a_char == '\n') {
+ PRIVATE (a_this)->end_of_line = TRUE;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * cr_input_set_line_num:
+ *@a_this: the "this pointer" of the current instance of #CRInput.
+ *@a_line_num: the new line number.
+ *
+ *Setter of the current line number.
+ *
+ *Return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_set_line_num (CRInput * a_this, glong a_line_num)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->line = a_line_num;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_get_line_num:
+ *@a_this: the "this pointer" of the current instance of #CRInput.
+ *@a_line_num: the returned line number.
+ *
+ *Getter of the current line number.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_get_line_num (CRInput const * a_this, glong * a_line_num)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_line_num, CR_BAD_PARAM_ERROR);
+
+ *a_line_num = PRIVATE (a_this)->line;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_set_column_num:
+ *@a_this: the "this pointer" of the current instance of #CRInput.
+ *@a_col: the new column number.
+ *
+ *Setter of the current column number.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_set_column_num (CRInput * a_this, glong a_col)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->col = a_col;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_get_column_num:
+ *@a_this: the "this pointer" of the current instance of #CRInput.
+ *@a_col: out parameter
+ *
+ *Getter of the current column number.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_get_column_num (CRInput const * a_this, glong * a_col)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_col,
+ CR_BAD_PARAM_ERROR);
+
+ *a_col = PRIVATE (a_this)->col;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_increment_line_num:
+ *@a_this: the "this pointer" of the current instance of #CRInput.
+ *@a_increment: the increment to add to the line number.
+ *
+ *Increments the current line number.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_increment_line_num (CRInput * a_this, glong a_increment)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->line += a_increment;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_increment_col_num:
+ *@a_this: the "this pointer" of the current instance of #CRInput.
+ *@a_increment: the increment to add to the column number.
+ *
+ *Increments the current column number.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_increment_col_num (CRInput * a_this, glong a_increment)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->col += a_increment;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_consume_char:
+ *@a_this: the this pointer.
+ *@a_char: the character to consume. If set to zero,
+ *consumes any character.
+ *
+ *Consumes the next character of the input stream if
+ *and only if that character equals a_char.
+ *
+ *Returns CR_OK upon successful completion, CR_PARSING_ERROR if
+ *next char is different from a_char, an other error code otherwise
+ */
+enum CRStatus
+cr_input_consume_char (CRInput * a_this, guint32 a_char)
+{
+ guint32 c;
+ enum CRStatus status;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ if ((status = cr_input_peek_char (a_this, &c)) != CR_OK) {
+ return status;
+ }
+
+ if (c == a_char || a_char == 0) {
+ status = cr_input_read_char (a_this, &c);
+ } else {
+ return CR_PARSING_ERROR;
+ }
+
+ return status;
+}
+
+/**
+ * cr_input_consume_chars:
+ *@a_this: the this pointer of the current instance of #CRInput.
+ *@a_char: the character to consume.
+ *@a_nb_char: in/out parameter. The number of characters to consume.
+ *If set to a negative value, the function will consume all the occurrences
+ *of a_char found.
+ *After return, if the return value equals CR_OK, this variable contains
+ *the number of characters actually consumed.
+ *
+ *Consumes up to a_nb_char occurrences of the next contiguous characters
+ *which equal a_char. Note that the next character of the input stream
+ **MUST* equal a_char to trigger the consumption, or else, the error
+ *code CR_PARSING_ERROR is returned.
+ *If the number of contiguous characters that equals a_char is less than
+ *a_nb_char, then this function consumes all the characters it can consume.
+ *
+ *Returns CR_OK if at least one character has been consumed, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_input_consume_chars (CRInput * a_this, guint32 a_char, gulong * a_nb_char)
+{
+ enum CRStatus status = CR_OK;
+ gulong nb_consumed = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_nb_char,
+ CR_BAD_PARAM_ERROR);
+
+ g_return_val_if_fail (a_char != 0 || a_nb_char != NULL,
+ CR_BAD_PARAM_ERROR);
+
+ for (nb_consumed = 0; ((status == CR_OK)
+ && (*a_nb_char > 0
+ && nb_consumed < *a_nb_char));
+ nb_consumed++) {
+ status = cr_input_consume_char (a_this, a_char);
+ }
+
+ *a_nb_char = nb_consumed;
+
+ if ((nb_consumed > 0)
+ && ((status == CR_PARSING_ERROR)
+ || (status == CR_END_OF_INPUT_ERROR))) {
+ status = CR_OK;
+ }
+
+ return status;
+}
+
+/**
+ * cr_input_consume_white_spaces:
+ *@a_this: the "this pointer" of the current instance of #CRInput.
+ *@a_nb_chars: in/out parameter. The number of white spaces to
+ *consume. After return, holds the number of white spaces actually consumed.
+ *
+ *Same as cr_input_consume_chars() but this one consumes white
+ *spaces.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_consume_white_spaces (CRInput * a_this, gulong * a_nb_chars)
+{
+ enum CRStatus status = CR_OK;
+ guint32 cur_char = 0,
+ nb_consumed = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_nb_chars,
+ CR_BAD_PARAM_ERROR);
+
+ for (nb_consumed = 0;
+ ((*a_nb_chars > 0) && (nb_consumed < *a_nb_chars));
+ nb_consumed++) {
+ status = cr_input_peek_char (a_this, &cur_char);
+ if (status != CR_OK)
+ break;
+
+ /*if the next char is a white space, consume it ! */
+ if (cr_utils_is_white_space (cur_char) == TRUE) {
+ status = cr_input_read_char (a_this, &cur_char);
+ if (status != CR_OK)
+ break;
+ continue;
+ }
+
+ break;
+
+ }
+
+ *a_nb_chars = (gulong) nb_consumed;
+
+ if (nb_consumed && status == CR_END_OF_INPUT_ERROR) {
+ status = CR_OK;
+ }
+
+ return status;
+}
+
+/**
+ * cr_input_peek_char:
+ *@a_this: the current instance of #CRInput.
+ *@a_char: out parameter. The returned character.
+ *
+ *Same as cr_input_read_char() but does not update the
+ *internal state of the input stream. The next call
+ *to cr_input_peek_char() or cr_input_read_char() will thus
+ *return the same character as the current one.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_input_peek_char (CRInput const * a_this, guint32 * a_char)
+{
+ enum CRStatus status = CR_OK;
+ gulong consumed = 0,
+ nb_bytes_left = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_char, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->next_byte_index >=
+ PRIVATE (a_this)->in_buf_size) {
+ return CR_END_OF_INPUT_ERROR;
+ }
+
+ nb_bytes_left = cr_input_get_nb_bytes_left (a_this);
+
+ if (nb_bytes_left < 1) {
+ return CR_END_OF_INPUT_ERROR;
+ }
+
+ status = cr_utils_read_char_from_utf8_buf
+ (PRIVATE (a_this)->in_buf +
+ PRIVATE (a_this)->next_byte_index,
+ nb_bytes_left, a_char, &consumed);
+
+ return status;
+}
+
+/**
+ * cr_input_peek_byte:
+ *@a_this: the current instance of #CRInput.
+ *@a_origin: the origin to consider in the calculation
+ *of the position of the byte to peek.
+ *@a_offset: the offset of the byte to peek, starting from
+ *the origin specified by a_origin.
+ *@a_byte: out parameter the peeked byte.
+ *
+ *Gets a byte from the input stream,
+ *starting from the current position in the input stream.
+ *Unlike cr_input_peek_next_byte() this method
+ *does not update the state of the current input stream.
+ *Subsequent calls to cr_input_peek_byte with the same arguments
+ *will return the same byte.
+ *
+ *Returns CR_OK upon successful completion or,
+ *CR_BAD_PARAM_ERROR if at least one of the parameters is invalid;
+ *CR_OUT_OF_BOUNDS_ERROR if the indexed byte is out of bounds.
+ */
+enum CRStatus
+cr_input_peek_byte (CRInput const * a_this, enum CRSeekPos a_origin,
+ gulong a_offset, guchar * a_byte)
+{
+ gulong abs_offset = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_byte, CR_BAD_PARAM_ERROR);
+
+ switch (a_origin) {
+
+ case CR_SEEK_CUR:
+ abs_offset = PRIVATE (a_this)->next_byte_index - 1 + a_offset;
+ break;
+
+ case CR_SEEK_BEGIN:
+ abs_offset = a_offset;
+ break;
+
+ case CR_SEEK_END:
+ abs_offset = PRIVATE (a_this)->in_buf_size - 1 - a_offset;
+ break;
+
+ default:
+ return CR_BAD_PARAM_ERROR;
+ }
+
+ if (abs_offset < PRIVATE (a_this)->in_buf_size) {
+
+ *a_byte = PRIVATE (a_this)->in_buf[abs_offset];
+
+ return CR_OK;
+
+ } else {
+ return CR_END_OF_INPUT_ERROR;
+ }
+}
+
+/**
+ * cr_input_peek_byte2:
+ *@a_this: the current byte input stream.
+ *@a_offset: the offset of the byte to peek, starting
+ *from the current input position pointer.
+ *@a_eof: out parameter. Is set to true is we reach end of
+ *stream. If set to NULL by the caller, this parameter is not taken
+ *in account.
+ *
+ *Same as cr_input_peek_byte() but with a simplified
+ *interface.
+ *
+ *Returns the read byte or 0 if something bad happened.
+ */
+guchar
+cr_input_peek_byte2 (CRInput const * a_this, gulong a_offset, gboolean * a_eof)
+{
+ guchar result = 0;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), 0);
+
+ if (a_eof)
+ *a_eof = FALSE;
+
+ status = cr_input_peek_byte (a_this, CR_SEEK_CUR, a_offset, &result);
+
+ if ((status == CR_END_OF_INPUT_ERROR)
+ && a_eof)
+ *a_eof = TRUE;
+
+ return result;
+}
+
+/**
+ * cr_input_get_byte_addr:
+ *@a_this: the current instance of #CRInput.
+ *@a_offset: the offset of the byte in the input stream starting
+ *from the beginning of the stream.
+ *
+ *Gets the memory address of the byte located at a given offset
+ *in the input stream.
+ *
+ *Returns the address, otherwise NULL if an error occurred.
+ */
+guchar *
+cr_input_get_byte_addr (CRInput * a_this, gulong a_offset)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), NULL);
+
+ if (a_offset >= PRIVATE (a_this)->nb_bytes) {
+ return NULL;
+ }
+
+ return &PRIVATE (a_this)->in_buf[a_offset];
+}
+
+/**
+ * cr_input_get_cur_byte_addr:
+ *@a_this: the current input stream
+ *@a_offset: out parameter. The returned address.
+ *
+ *Gets the address of the current character pointer.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_get_cur_byte_addr (CRInput * a_this, guchar ** a_offset)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_offset,
+ CR_BAD_PARAM_ERROR);
+
+ if (!PRIVATE (a_this)->next_byte_index) {
+ return CR_START_OF_INPUT_ERROR;
+ }
+
+ *a_offset = cr_input_get_byte_addr
+ (a_this, PRIVATE (a_this)->next_byte_index - 1);
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_seek_index:
+ *@a_this: the current instance of #CRInput.
+ *@a_origin: the origin to consider during the calculation
+ *of the absolute position of the new "current byte index".
+ *@a_pos: the relative offset of the new "current byte index."
+ *This offset is relative to the origin a_origin.
+ *
+ *Sets the "current byte index" of the current instance
+ *of #CRInput. Next call to cr_input_get_byte() will return
+ *the byte next after the new "current byte index".
+ *
+ *Returns CR_OK upon successful completion otherwise returns
+ *CR_BAD_PARAM_ERROR if at least one of the parameters is not valid
+ *or CR_OUT_BOUNDS_ERROR in case of error.
+ */
+enum CRStatus
+cr_input_seek_index (CRInput * a_this, enum CRSeekPos a_origin, gint a_pos)
+{
+
+ glong abs_offset = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ switch (a_origin) {
+
+ case CR_SEEK_CUR:
+ abs_offset = PRIVATE (a_this)->next_byte_index - 1 + a_pos;
+ break;
+
+ case CR_SEEK_BEGIN:
+ abs_offset = a_pos;
+ break;
+
+ case CR_SEEK_END:
+ abs_offset = PRIVATE (a_this)->in_buf_size - 1 - a_pos;
+ break;
+
+ default:
+ return CR_BAD_PARAM_ERROR;
+ }
+
+ if ((abs_offset > 0)
+ && (gulong) abs_offset < PRIVATE (a_this)->nb_bytes) {
+
+ /*update the input stream's internal state */
+ PRIVATE (a_this)->next_byte_index = abs_offset + 1;
+
+ return CR_OK;
+ }
+
+ return CR_OUT_OF_BOUNDS_ERROR;
+}
+
+/**
+ * cr_input_get_cur_pos:
+ *@a_this: the current instance of #CRInput.
+ *@a_pos: out parameter. The returned position.
+ *
+ *Gets the position of the "current byte index" which
+ *is basically the position of the last returned byte in the
+ *input stream.
+ *
+ *Returns CR_OK upon successful completion. Otherwise,
+ *CR_BAD_PARAMETER_ERROR if at least one of the arguments is invalid.
+ *CR_START_OF_INPUT if no call to either cr_input_read_byte()
+ *or cr_input_seek_index() have been issued before calling
+ *cr_input_get_cur_pos()
+ *Note that the out parameters of this function are valid if and only if this
+ *function returns CR_OK.
+ */
+enum CRStatus
+cr_input_get_cur_pos (CRInput const * a_this, CRInputPos * a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_pos,
+ CR_BAD_PARAM_ERROR);
+
+ a_pos->next_byte_index = PRIVATE (a_this)->next_byte_index;
+ a_pos->line = PRIVATE (a_this)->line;
+ a_pos->col = PRIVATE (a_this)->col;
+ a_pos->end_of_line = PRIVATE (a_this)->end_of_line;
+ a_pos->end_of_file = PRIVATE (a_this)->end_of_input;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_get_parsing_location:
+ *@a_this: the current instance of #CRInput
+ *@a_loc: the set parsing location.
+ *
+ *Gets the current parsing location.
+ *The Parsing location is a public datastructure that
+ *represents the current line/column/byte offset/ in the input
+ *stream.
+ *
+ *Returns CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_input_get_parsing_location (CRInput const *a_this,
+ CRParsingLocation *a_loc)
+{
+ g_return_val_if_fail (a_this
+ && PRIVATE (a_this)
+ && a_loc,
+ CR_BAD_PARAM_ERROR) ;
+
+ a_loc->line = PRIVATE (a_this)->line ;
+ a_loc->column = PRIVATE (a_this)->col ;
+ if (PRIVATE (a_this)->next_byte_index) {
+ a_loc->byte_offset = PRIVATE (a_this)->next_byte_index - 1 ;
+ } else {
+ a_loc->byte_offset = PRIVATE (a_this)->next_byte_index ;
+ }
+ return CR_OK ;
+}
+
+/**
+ * cr_input_get_cur_index:
+ *@a_this: the "this pointer" of the current instance of
+ *#CRInput
+ *@a_index: out parameter. The returned index.
+ *
+ *Getter of the next byte index.
+ *It actually returns the index of the
+ *next byte to be read.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_input_get_cur_index (CRInput const * a_this, glong * a_index)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_index, CR_BAD_PARAM_ERROR);
+
+ *a_index = PRIVATE (a_this)->next_byte_index;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_set_cur_index:
+ *@a_this: the "this pointer" of the current instance
+ *of #CRInput .
+ *@a_index: the new index to set.
+ *
+ *Setter of the next byte index.
+ *It sets the index of the next byte to be read.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_set_cur_index (CRInput * a_this, glong a_index)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->next_byte_index = a_index;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_set_end_of_file:
+ *@a_this: the current instance of #CRInput.
+ *@a_eof: the new end of file flag.
+ *
+ *Sets the end of file flag.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_set_end_of_file (CRInput * a_this, gboolean a_eof)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->end_of_input = a_eof;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_get_end_of_file:
+ *@a_this: the current instance of #CRInput.
+ *@a_eof: out parameter the place to put the end of
+ *file flag.
+ *
+ *Gets the end of file flag.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_get_end_of_file (CRInput const * a_this, gboolean * a_eof)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_eof, CR_BAD_PARAM_ERROR);
+
+ *a_eof = PRIVATE (a_this)->end_of_input;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_set_end_of_line:
+ *@a_this: the current instance of #CRInput.
+ *@a_eol: the new end of line flag.
+ *
+ *Sets the end of line flag.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_input_set_end_of_line (CRInput * a_this, gboolean a_eol)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->end_of_line = a_eol;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_get_end_of_line:
+ *@a_this: the current instance of #CRInput
+ *@a_eol: out parameter. The place to put
+ *the returned flag
+ *
+ *Gets the end of line flag of the current input.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_input_get_end_of_line (CRInput const * a_this, gboolean * a_eol)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_eol, CR_BAD_PARAM_ERROR);
+
+ *a_eol = PRIVATE (a_this)->end_of_line;
+
+ return CR_OK;
+}
+
+/**
+ * cr_input_set_cur_pos:
+ *@a_this: the "this pointer" of the current instance of
+ *#CRInput.
+ *@a_pos: the new position.
+ *
+ *Sets the current position in the input stream.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_input_set_cur_pos (CRInput * a_this, CRInputPos const * a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_pos,
+ CR_BAD_PARAM_ERROR);
+
+ cr_input_set_column_num (a_this, a_pos->col);
+ cr_input_set_line_num (a_this, a_pos->line);
+ cr_input_set_cur_index (a_this, a_pos->next_byte_index);
+ cr_input_set_end_of_line (a_this, a_pos->end_of_line);
+ cr_input_set_end_of_file (a_this, a_pos->end_of_file);
+
+ return CR_OK;
+}
diff --git a/src/st/croco/cr-input.h b/src/st/croco/cr-input.h
new file mode 100644
index 0000000..9eb402a
--- /dev/null
+++ b/src/st/croco/cr-input.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset:8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See the COPYRIGHTS file for copyrights information.
+ */
+
+#ifndef __CR_INPUT_SRC_H__
+#define __CR_INPUT_SRC_H__
+
+
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *The libcroco basic input stream class
+ *declaration file.
+ */
+
+typedef struct _CRInput CRInput ;
+typedef struct _CRInputPriv CRInputPriv ;
+
+/**
+ *The #CRInput class provides the abstraction of
+ *an utf8-encoded character stream.
+ */
+struct _CRInput
+{
+ CRInputPriv *priv ;
+} ;
+
+typedef struct _CRInputPos CRInputPos ;
+
+struct _CRInputPos
+{
+ glong line ;
+ glong col ;
+ gboolean end_of_file ;
+ gboolean end_of_line ;
+ glong next_byte_index ;
+} ;
+
+CRInput *
+cr_input_new_from_buf (guchar *a_buf, gulong a_len,
+ enum CREncoding a_enc, gboolean a_free_buf) ;
+CRInput *
+cr_input_new_from_uri (const gchar *a_file_uri,
+ enum CREncoding a_enc) ;
+
+void
+cr_input_destroy (CRInput *a_this) ;
+
+void
+cr_input_ref (CRInput *a_this) ;
+
+gboolean
+cr_input_unref (CRInput *a_this) ;
+
+enum CRStatus
+cr_input_read_byte (CRInput *a_this, guchar *a_byte) ;
+
+enum CRStatus
+cr_input_read_char (CRInput *a_this, guint32 *a_char) ;
+
+enum CRStatus
+cr_input_consume_chars (CRInput *a_this, guint32 a_char,
+ gulong *a_nb_char) ;
+
+enum CRStatus
+cr_input_consume_char (CRInput *a_this, guint32 a_char) ;
+
+enum CRStatus
+cr_input_consume_white_spaces (CRInput *a_this, gulong *a_nb_chars) ;
+
+enum CRStatus
+cr_input_peek_byte (CRInput const *a_this, enum CRSeekPos a_origin,
+ gulong a_offset, guchar *a_byte) ;
+
+guchar
+cr_input_peek_byte2 (CRInput const *a_this, gulong a_offset,
+ gboolean *a_eof) ;
+
+enum CRStatus
+cr_input_peek_char (CRInput const *a_this, guint32 *a_char) ;
+
+guchar *
+cr_input_get_byte_addr (CRInput *a_this,
+ gulong a_offset) ;
+
+enum CRStatus
+cr_input_get_cur_byte_addr (CRInput *a_this, guchar ** a_offset) ;
+
+enum CRStatus
+cr_input_seek_index (CRInput *a_this,
+ enum CRSeekPos a_origin, gint a_pos) ;
+
+enum CRStatus
+cr_input_get_cur_index (CRInput const *a_this, glong *a_index) ;
+
+enum CRStatus
+cr_input_set_cur_index (CRInput *a_this, glong a_index) ;
+
+enum CRStatus
+cr_input_get_cur_pos (CRInput const *a_this, CRInputPos * a_pos) ;
+
+enum CRStatus
+cr_input_set_cur_pos (CRInput *a_this, CRInputPos const *a_pos) ;
+
+enum CRStatus
+cr_input_get_parsing_location (CRInput const *a_this,
+ CRParsingLocation *a_loc) ;
+
+enum CRStatus
+cr_input_get_end_of_line (CRInput const *a_this, gboolean *a_eol) ;
+
+enum CRStatus
+cr_input_set_end_of_line (CRInput *a_this, gboolean a_eol) ;
+
+enum CRStatus
+cr_input_get_end_of_file (CRInput const *a_this, gboolean *a_eof) ;
+
+enum CRStatus
+cr_input_set_end_of_file (CRInput *a_this, gboolean a_eof) ;
+
+enum CRStatus
+cr_input_set_line_num (CRInput *a_this, glong a_line_num) ;
+
+enum CRStatus
+cr_input_get_line_num (CRInput const *a_this, glong *a_line_num) ;
+
+enum CRStatus
+cr_input_set_column_num (CRInput *a_this, glong a_col) ;
+
+enum CRStatus
+cr_input_get_column_num (CRInput const *a_this, glong *a_col) ;
+
+enum CRStatus
+cr_input_increment_line_num (CRInput *a_this,
+ glong a_increment) ;
+
+enum CRStatus
+cr_input_increment_col_num (CRInput *a_this,
+ glong a_increment) ;
+
+glong
+cr_input_get_nb_bytes_left (CRInput const *a_this) ;
+
+enum CRStatus
+cr_input_end_of_input (CRInput const *a_this, gboolean *a_end_of_input) ;
+
+G_END_DECLS
+
+#endif /*__CR_INPUT_SRC_H__*/
+
diff --git a/src/st/croco/cr-num.c b/src/st/croco/cr-num.c
new file mode 100644
index 0000000..ba17285
--- /dev/null
+++ b/src/st/croco/cr-num.c
@@ -0,0 +1,313 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyrights information.
+ */
+
+/**
+ *@CRNum:
+ *
+ *The definition
+ *of the #CRNum class.
+ */
+
+#include "cr-num.h"
+#include "string.h"
+
+/**
+ * cr_num_new:
+ *
+ *#CRNum.
+ *
+ *Returns the newly built instance of
+ *#CRNum.
+ */
+CRNum *
+cr_num_new (void)
+{
+ CRNum *result = NULL;
+
+ result = g_try_malloc (sizeof (CRNum));
+
+ if (result == NULL) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRNum));
+
+ return result;
+}
+
+/**
+ * cr_num_new_with_val:
+ * @a_val: the numerical value of the number.
+ * @a_type: the type of number.
+ *
+ * A constructor of #CRNum.
+ *
+ * Returns the newly built instance of #CRNum or
+ * NULL if an error arises.
+ */
+CRNum *
+cr_num_new_with_val (gdouble a_val, enum CRNumType a_type)
+{
+ CRNum *result = NULL;
+
+ result = cr_num_new ();
+
+ g_return_val_if_fail (result, NULL);
+
+ result->val = a_val;
+ result->type = a_type;
+
+ return result;
+}
+
+/**
+ * cr_num_to_string:
+ *@a_this: the current instance of #CRNum.
+ *
+ *Returns the newly built string representation
+ *of the current instance of #CRNum. The returned
+ *string is NULL terminated. The caller *must*
+ *free the returned string.
+ */
+guchar *
+cr_num_to_string (CRNum const * a_this)
+{
+ gdouble test_val = 0.0;
+
+ guchar *tmp_char1 = NULL,
+ *tmp_char2 = NULL,
+ *result = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ test_val = a_this->val - (glong) a_this->val;
+
+ if (!test_val) {
+ tmp_char1 = (guchar *) g_strdup_printf ("%ld", (glong) a_this->val);
+ } else {
+ tmp_char1 = (guchar *) g_new0 (char, G_ASCII_DTOSTR_BUF_SIZE + 1);
+ if (tmp_char1 != NULL)
+ g_ascii_dtostr ((gchar *) tmp_char1, G_ASCII_DTOSTR_BUF_SIZE, a_this->val);
+ }
+
+ g_return_val_if_fail (tmp_char1, NULL);
+
+ switch (a_this->type) {
+ case NUM_LENGTH_EM:
+ tmp_char2 = (guchar *) "em";
+ break;
+
+ case NUM_LENGTH_EX:
+ tmp_char2 = (guchar *) "ex";
+ break;
+
+ case NUM_LENGTH_PX:
+ tmp_char2 = (guchar *) "px";
+ break;
+
+ case NUM_LENGTH_IN:
+ tmp_char2 = (guchar *) "in";
+ break;
+
+ case NUM_LENGTH_CM:
+ tmp_char2 = (guchar *) "cm";
+ break;
+
+ case NUM_LENGTH_MM:
+ tmp_char2 = (guchar *) "mm";
+ break;
+
+ case NUM_LENGTH_PT:
+ tmp_char2 = (guchar *) "pt";
+ break;
+
+ case NUM_LENGTH_PC:
+ tmp_char2 = (guchar *) "pc";
+ break;
+
+ case NUM_ANGLE_DEG:
+ tmp_char2 = (guchar *) "deg";
+ break;
+
+ case NUM_ANGLE_RAD:
+ tmp_char2 = (guchar *) "rad";
+ break;
+
+ case NUM_ANGLE_GRAD:
+ tmp_char2 = (guchar *) "grad";
+ break;
+
+ case NUM_TIME_MS:
+ tmp_char2 = (guchar *) "ms";
+ break;
+
+ case NUM_TIME_S:
+ tmp_char2 = (guchar *) "s";
+ break;
+
+ case NUM_FREQ_HZ:
+ tmp_char2 = (guchar *) "Hz";
+ break;
+
+ case NUM_FREQ_KHZ:
+ tmp_char2 = (guchar *) "KHz";
+ break;
+
+ case NUM_PERCENTAGE:
+ tmp_char2 = (guchar *) "%";
+ break;
+ case NUM_INHERIT:
+ tmp_char2 = (guchar *) "inherit";
+ break ;
+ case NUM_AUTO:
+ tmp_char2 = (guchar *) "auto";
+ break ;
+ case NUM_GENERIC:
+ tmp_char2 = NULL ;
+ break ;
+ default:
+ tmp_char2 = (guchar *) "unknown";
+ break;
+ }
+
+ if (tmp_char2) {
+ result = (guchar *) g_strconcat ((gchar *) tmp_char1, tmp_char2, NULL);
+ g_free (tmp_char1);
+ } else {
+ result = tmp_char1;
+ }
+
+ return result;
+}
+
+/**
+ * cr_num_copy:
+ *@a_src: the instance of #CRNum to copy.
+ *Must be non NULL.
+ *@a_dest: the destination of the copy.
+ *Must be non NULL
+ *
+ *Copies an instance of #CRNum.
+ *
+ *Returns CR_OK upon successful completion, an
+ *error code otherwise.
+ */
+enum CRStatus
+cr_num_copy (CRNum * a_dest, CRNum const * a_src)
+{
+ g_return_val_if_fail (a_dest && a_src, CR_BAD_PARAM_ERROR);
+
+ memcpy (a_dest, a_src, sizeof (CRNum));
+
+ return CR_OK;
+}
+
+/**
+ * cr_num_dup:
+ *@a_this: the instance of #CRNum to duplicate.
+ *
+ *Duplicates an instance of #CRNum
+ *
+ *Returns the newly created (duplicated) instance of #CRNum.
+ *Must be freed by cr_num_destroy().
+ */
+CRNum *
+cr_num_dup (CRNum const * a_this)
+{
+ CRNum *result = NULL;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ result = cr_num_new ();
+ g_return_val_if_fail (result, NULL);
+
+ status = cr_num_copy (result, a_this);
+ g_return_val_if_fail (status == CR_OK, NULL);
+
+ return result;
+}
+
+/**
+ * cr_num_set:
+ *Sets an instance of #CRNum.
+ *@a_this: the current instance of #CRNum to be set.
+ *@a_val: the new numerical value to be hold by the current
+ *instance of #CRNum
+ *@a_type: the new type of #CRNum.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_num_set (CRNum * a_this, gdouble a_val, enum CRNumType a_type)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ a_this->val = a_val;
+ a_this->type = a_type;
+
+ return CR_OK;
+}
+
+/**
+ * cr_num_is_fixed_length:
+ * @a_this: the current instance of #CRNum .
+ *
+ *Tests if the current instance of #CRNum is a fixed
+ *length value or not. Typically a fixed length value
+ *is anything from NUM_LENGTH_EM to NUM_LENGTH_PC.
+ *See the definition of #CRNumType to see what we mean.
+ *
+ *Returns TRUE if the instance of #CRNum is a fixed length number,
+ *FALSE otherwise.
+ */
+gboolean
+cr_num_is_fixed_length (CRNum const * a_this)
+{
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (a_this, FALSE);
+
+ if (a_this->type >= NUM_LENGTH_EM
+ && a_this->type <= NUM_LENGTH_PC) {
+ result = TRUE ;
+ }
+ return result ;
+}
+
+/**
+ * cr_num_destroy:
+ *@a_this: the this pointer of
+ *the current instance of #CRNum.
+ *
+ *The destructor of #CRNum.
+ */
+void
+cr_num_destroy (CRNum * a_this)
+{
+ g_return_if_fail (a_this);
+
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-num.h b/src/st/croco/cr-num.h
new file mode 100644
index 0000000..2b73aaf
--- /dev/null
+++ b/src/st/croco/cr-num.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information
+ */
+
+
+/**
+ *@file
+ *The declaration
+ *of the #CRNum class.
+ */
+
+#ifndef __CR_NUM_H__
+#define __CR_NUM_H__
+
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *The declaration of the #CRNum class.
+ *
+ */
+
+/**
+ *The different types
+ *of numbers.
+ *Please, do not modify
+ *the declaration order of the enum
+ *members, unless you know
+ *what you are doing.
+ */
+enum CRNumType
+{
+ NUM_AUTO = 0,
+ NUM_GENERIC,
+ NUM_LENGTH_EM,
+ NUM_LENGTH_EX,
+ NUM_LENGTH_PX,
+ NUM_LENGTH_IN,
+ NUM_LENGTH_CM,
+ NUM_LENGTH_MM,
+ NUM_LENGTH_PT,
+ NUM_LENGTH_PC,
+ NUM_ANGLE_DEG,
+ NUM_ANGLE_RAD,
+ NUM_ANGLE_GRAD,
+ NUM_TIME_MS,
+ NUM_TIME_S,
+ NUM_FREQ_HZ,
+ NUM_FREQ_KHZ,
+ NUM_PERCENTAGE,
+ NUM_INHERIT,
+ NUM_UNKNOWN_TYPE,
+ NB_NUM_TYPE
+} ;
+
+
+/**
+ *An abstraction of a number (num)
+ *as defined in the css2 spec.
+ */
+typedef struct _CRNum CRNum ;
+
+/**
+ *An abstraction of a number (num)
+ *as defined in the css2 spec.
+ */
+struct _CRNum
+{
+ enum CRNumType type ;
+ gdouble val ;
+ CRParsingLocation location ;
+} ;
+
+CRNum *
+cr_num_new (void) ;
+
+CRNum *
+cr_num_new_with_val (gdouble a_val,
+ enum CRNumType a_type) ;
+
+CRNum *
+cr_num_dup (CRNum const *a_this) ;
+
+guchar *
+cr_num_to_string (CRNum const *a_this) ;
+
+enum CRStatus
+cr_num_copy (CRNum *a_dest, CRNum const *a_src) ;
+
+enum CRStatus
+cr_num_set (CRNum *a_this, gdouble a_val,
+ enum CRNumType a_type) ;
+
+gboolean
+cr_num_is_fixed_length (CRNum const *a_this) ;
+
+void
+cr_num_destroy (CRNum *a_this) ;
+
+
+G_END_DECLS
+
+
+#endif /*__CR_NUM_H__*/
diff --git a/src/st/croco/cr-om-parser.c b/src/st/croco/cr-om-parser.c
new file mode 100644
index 0000000..90f7106
--- /dev/null
+++ b/src/st/croco/cr-om-parser.c
@@ -0,0 +1,1142 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include <string.h>
+#include "cr-utils.h"
+#include "cr-om-parser.h"
+
+/**
+ *@CROMParser:
+ *
+ *The definition of the CSS Object Model Parser.
+ *This parser uses (and sits) the SAC api of libcroco defined
+ *in cr-parser.h and cr-doc-handler.h
+ */
+
+struct _CROMParserPriv {
+ CRParser *parser;
+};
+
+#define PRIVATE(a_this) ((a_this)->priv)
+
+/*
+ *Forward declaration of a type defined later
+ *in this file.
+ */
+struct _ParsingContext;
+typedef struct _ParsingContext ParsingContext;
+
+static ParsingContext *new_parsing_context (void);
+
+static void destroy_context (ParsingContext * a_ctxt);
+
+static void unrecoverable_error (CRDocHandler * a_this);
+
+static void error (CRDocHandler * a_this);
+
+static void property (CRDocHandler * a_this,
+ CRString * a_name,
+ CRTerm * a_expression,
+ gboolean a_important);
+
+static void end_selector (CRDocHandler * a_this,
+ CRSelector * a_selector_list);
+
+static void start_selector (CRDocHandler * a_this,
+ CRSelector * a_selector_list);
+
+static void start_font_face (CRDocHandler * a_this,
+ CRParsingLocation *a_location);
+
+static void end_font_face (CRDocHandler * a_this);
+
+static void end_document (CRDocHandler * a_this);
+
+static void start_document (CRDocHandler * a_this);
+
+static void charset (CRDocHandler * a_this,
+ CRString * a_charset,
+ CRParsingLocation *a_location);
+
+static void start_page (CRDocHandler * a_this, CRString * a_page,
+ CRString * a_pseudo_page,
+ CRParsingLocation *a_location);
+
+static void end_page (CRDocHandler * a_this, CRString * a_page,
+ CRString * a_pseudo_page);
+
+static void start_media (CRDocHandler * a_this,
+ GList * a_media_list,
+ CRParsingLocation *a_location);
+
+static void end_media (CRDocHandler * a_this,
+ GList * a_media_list);
+
+static void import_style (CRDocHandler * a_this,
+ GList * a_media_list,
+ CRString * a_uri,
+ CRString * a_uri_default_ns,
+ CRParsingLocation *a_location);
+
+struct _ParsingContext {
+ CRStyleSheet *stylesheet;
+ CRStatement *cur_stmt;
+ CRStatement *cur_media_stmt;
+};
+
+/********************************************
+ *Private methods
+ ********************************************/
+
+static ParsingContext *
+new_parsing_context (void)
+{
+ ParsingContext *result = NULL;
+
+ result = g_try_malloc (sizeof (ParsingContext));
+ if (!result) {
+ cr_utils_trace_info ("Out of Memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (ParsingContext));
+ return result;
+}
+
+static void
+destroy_context (ParsingContext * a_ctxt)
+{
+ g_return_if_fail (a_ctxt);
+
+ if (a_ctxt->stylesheet) {
+ cr_stylesheet_destroy (a_ctxt->stylesheet);
+ a_ctxt->stylesheet = NULL;
+ }
+ if (a_ctxt->cur_stmt) {
+ cr_statement_destroy (a_ctxt->cur_stmt);
+ a_ctxt->cur_stmt = NULL;
+ }
+ g_free (a_ctxt);
+}
+
+static enum CRStatus
+cr_om_parser_init_default_sac_handler (CROMParser * a_this)
+{
+ CRDocHandler *sac_handler = NULL;
+ gboolean created_handler = FALSE;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->parser,
+ CR_BAD_PARAM_ERROR);
+
+ status = cr_parser_get_sac_handler (PRIVATE (a_this)->parser,
+ &sac_handler);
+ g_return_val_if_fail (status == CR_OK, status);
+
+ if (!sac_handler) {
+ sac_handler = cr_doc_handler_new ();
+ created_handler = TRUE;
+ }
+
+ /*
+ *initialize here the sac handler.
+ */
+ sac_handler->start_document = start_document;
+ sac_handler->end_document = end_document;
+ sac_handler->start_selector = start_selector;
+ sac_handler->end_selector = end_selector;
+ sac_handler->property = property;
+ sac_handler->start_font_face = start_font_face;
+ sac_handler->end_font_face = end_font_face;
+ sac_handler->error = error;
+ sac_handler->unrecoverable_error = unrecoverable_error;
+ sac_handler->charset = charset;
+ sac_handler->start_page = start_page;
+ sac_handler->end_page = end_page;
+ sac_handler->start_media = start_media;
+ sac_handler->end_media = end_media;
+ sac_handler->import_style = import_style;
+
+ if (created_handler) {
+ status = cr_parser_set_sac_handler (PRIVATE (a_this)->parser,
+ sac_handler);
+ cr_doc_handler_unref (sac_handler);
+ }
+
+ return status;
+
+}
+
+static void
+start_document (CRDocHandler * a_this)
+{
+ ParsingContext *ctxt = NULL;
+ CRStyleSheet *stylesheet = NULL;
+
+ g_return_if_fail (a_this);
+
+ ctxt = new_parsing_context ();
+ g_return_if_fail (ctxt);
+
+ stylesheet = cr_stylesheet_new (NULL);
+ ctxt->stylesheet = stylesheet;
+ cr_doc_handler_set_ctxt (a_this, ctxt);
+}
+
+static void
+start_font_face (CRDocHandler * a_this,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ g_return_if_fail (a_this);
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+ g_return_if_fail (ctxt->cur_stmt == NULL);
+
+ ctxt->cur_stmt =
+ cr_statement_new_at_font_face_rule (ctxt->stylesheet, NULL);
+
+ g_return_if_fail (ctxt->cur_stmt);
+}
+
+static void
+end_font_face (CRDocHandler * a_this)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+ CRStatement *stmts = NULL;
+
+ g_return_if_fail (a_this);
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+ g_return_if_fail
+ (ctxt->cur_stmt
+ && ctxt->cur_stmt->type == AT_FONT_FACE_RULE_STMT
+ && ctxt->stylesheet);
+
+ stmts = cr_statement_append (ctxt->stylesheet->statements,
+ ctxt->cur_stmt);
+ if (!stmts)
+ goto error;
+
+ ctxt->stylesheet->statements = stmts;
+ stmts = NULL;
+ ctxt->cur_stmt = NULL;
+
+ return;
+
+ error:
+
+ if (ctxt->cur_stmt) {
+ cr_statement_destroy (ctxt->cur_stmt);
+ ctxt->cur_stmt = NULL;
+ }
+
+ if (!stmts) {
+ cr_statement_destroy (stmts);
+ stmts = NULL;
+ }
+}
+
+static void
+end_document (CRDocHandler * a_this)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ if (!ctxt->stylesheet || ctxt->cur_stmt)
+ goto error;
+
+ status = cr_doc_handler_set_result (a_this, ctxt->stylesheet);
+ g_return_if_fail (status == CR_OK);
+
+ ctxt->stylesheet = NULL;
+ destroy_context (ctxt);
+ cr_doc_handler_set_ctxt (a_this, NULL);
+
+ return;
+
+ error:
+ if (ctxt) {
+ destroy_context (ctxt);
+ }
+}
+
+static void
+charset (CRDocHandler * a_this, CRString * a_charset,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *stmt = NULL,
+ *stmt2 = NULL;
+ CRString *charset = NULL;
+
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+ g_return_if_fail (ctxt->stylesheet);
+
+ charset = cr_string_dup (a_charset) ;
+ stmt = cr_statement_new_at_charset_rule (ctxt->stylesheet, charset);
+ g_return_if_fail (stmt);
+ stmt2 = cr_statement_append (ctxt->stylesheet->statements, stmt);
+ if (!stmt2) {
+ if (stmt) {
+ cr_statement_destroy (stmt);
+ stmt = NULL;
+ }
+ if (charset) {
+ cr_string_destroy (charset);
+ }
+ return;
+ }
+ ctxt->stylesheet->statements = stmt2;
+ stmt2 = NULL;
+}
+
+static void
+start_page (CRDocHandler * a_this,
+ CRString * a_page,
+ CRString * a_pseudo,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+ g_return_if_fail (ctxt->cur_stmt == NULL);
+
+ ctxt->cur_stmt = cr_statement_new_at_page_rule
+ (ctxt->stylesheet, NULL, NULL, NULL);
+ if (a_page) {
+ ctxt->cur_stmt->kind.page_rule->name =
+ cr_string_dup (a_page) ;
+
+ if (!ctxt->cur_stmt->kind.page_rule->name) {
+ goto error;
+ }
+ }
+ if (a_pseudo) {
+ ctxt->cur_stmt->kind.page_rule->pseudo =
+ cr_string_dup (a_pseudo) ;
+ if (!ctxt->cur_stmt->kind.page_rule->pseudo) {
+ goto error;
+ }
+ }
+ return;
+
+ error:
+ if (ctxt->cur_stmt) {
+ cr_statement_destroy (ctxt->cur_stmt);
+ ctxt->cur_stmt = NULL;
+ }
+}
+
+static void
+end_page (CRDocHandler * a_this,
+ CRString * a_page,
+ CRString * a_pseudo_page)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+ CRStatement *stmt = NULL;
+
+ (void) a_page;
+ (void) a_pseudo_page;
+
+ g_return_if_fail (a_this);
+
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ g_return_if_fail (ctxt->cur_stmt
+ && ctxt->cur_stmt->type == AT_PAGE_RULE_STMT
+ && ctxt->stylesheet);
+
+ stmt = cr_statement_append (ctxt->stylesheet->statements,
+ ctxt->cur_stmt);
+
+ if (stmt) {
+ ctxt->stylesheet->statements = stmt;
+ stmt = NULL;
+ ctxt->cur_stmt = NULL;
+ }
+
+ if (ctxt->cur_stmt) {
+ cr_statement_destroy (ctxt->cur_stmt);
+ ctxt->cur_stmt = NULL;
+ }
+ a_page = NULL; /*keep compiler happy */
+ a_pseudo_page = NULL; /*keep compiler happy */
+}
+
+static void
+start_media (CRDocHandler * a_this,
+ GList * a_media_list,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+ GList *media_list = NULL;
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ g_return_if_fail (ctxt
+ && ctxt->cur_stmt == NULL
+ && ctxt->cur_media_stmt == NULL
+ && ctxt->stylesheet);
+ if (a_media_list) {
+ /*duplicate the media_list */
+ media_list = cr_utils_dup_glist_of_cr_string
+ (a_media_list);
+ }
+ ctxt->cur_media_stmt =
+ cr_statement_new_at_media_rule
+ (ctxt->stylesheet, NULL, media_list);
+
+}
+
+static void
+end_media (CRDocHandler * a_this, GList * a_media_list)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+ CRStatement *stmts = NULL;
+
+ (void) a_media_list;
+
+ g_return_if_fail (a_this);
+
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ g_return_if_fail (ctxt
+ && ctxt->cur_media_stmt
+ && ctxt->cur_media_stmt->type == AT_MEDIA_RULE_STMT
+ && ctxt->stylesheet);
+
+ stmts = cr_statement_append (ctxt->stylesheet->statements,
+ ctxt->cur_media_stmt);
+
+ if (!stmts) {
+ cr_statement_destroy (ctxt->cur_media_stmt);
+ ctxt->cur_media_stmt = NULL;
+ }
+
+ ctxt->stylesheet->statements = stmts;
+ stmts = NULL;
+
+ ctxt->cur_stmt = NULL ;
+ ctxt->cur_media_stmt = NULL ;
+ a_media_list = NULL;
+}
+
+static void
+import_style (CRDocHandler * a_this,
+ GList * a_media_list,
+ CRString * a_uri,
+ CRString * a_uri_default_ns,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ CRString *uri = NULL;
+ CRStatement *stmt = NULL,
+ *stmt2 = NULL;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+ GList *media_list = NULL ;
+
+ (void) a_uri_default_ns;
+
+ g_return_if_fail (a_this);
+
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ g_return_if_fail (ctxt->stylesheet);
+
+ uri = cr_string_dup (a_uri) ;
+
+ if (a_media_list)
+ media_list = cr_utils_dup_glist_of_cr_string (a_media_list) ;
+
+ stmt = cr_statement_new_at_import_rule
+ (ctxt->stylesheet, uri, media_list, NULL);
+
+ if (!stmt)
+ goto error;
+
+ if (ctxt->cur_stmt) {
+ stmt2 = cr_statement_append (ctxt->cur_stmt, stmt);
+ if (!stmt2)
+ goto error;
+ ctxt->cur_stmt = stmt2;
+ stmt2 = NULL;
+ stmt = NULL;
+ } else {
+ stmt2 = cr_statement_append (ctxt->stylesheet->statements,
+ stmt);
+ if (!stmt2)
+ goto error;
+ ctxt->stylesheet->statements = stmt2;
+ stmt2 = NULL;
+ stmt = NULL;
+ }
+
+ return;
+
+ error:
+ if (uri) {
+ cr_string_destroy (uri);
+ }
+
+ if (stmt) {
+ cr_statement_destroy (stmt);
+ stmt = NULL;
+ }
+ a_uri_default_ns = NULL; /*keep compiler happy */
+}
+
+static void
+start_selector (CRDocHandler * a_this, CRSelector * a_selector_list)
+{
+ enum CRStatus status = CR_OK ;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+ if (ctxt->cur_stmt) {
+ /*hmm, this should be NULL so free it */
+ cr_statement_destroy (ctxt->cur_stmt);
+ ctxt->cur_stmt = NULL;
+ }
+
+ ctxt->cur_stmt = cr_statement_new_ruleset
+ (ctxt->stylesheet, a_selector_list, NULL, NULL);
+}
+
+static void
+end_selector (CRDocHandler * a_this, CRSelector * a_selector_list)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ (void) a_selector_list;
+
+ g_return_if_fail (a_this);
+
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ g_return_if_fail (ctxt->cur_stmt && ctxt->stylesheet);
+
+ if (ctxt->cur_stmt) {
+ CRStatement *stmts = NULL;
+
+ if (ctxt->cur_media_stmt) {
+ CRAtMediaRule *media_rule = NULL;
+
+ media_rule = ctxt->cur_media_stmt->kind.media_rule;
+
+ stmts = cr_statement_append
+ (media_rule->rulesets, ctxt->cur_stmt);
+
+ if (!stmts) {
+ cr_utils_trace_info
+ ("Could not append a new statement");
+ cr_statement_destroy (media_rule->rulesets);
+ ctxt->cur_media_stmt->
+ kind.media_rule->rulesets = NULL;
+ return;
+ }
+ media_rule->rulesets = stmts;
+ ctxt->cur_stmt = NULL;
+ } else {
+ stmts = cr_statement_append
+ (ctxt->stylesheet->statements,
+ ctxt->cur_stmt);
+ if (!stmts) {
+ cr_utils_trace_info
+ ("Could not append a new statement");
+ cr_statement_destroy (ctxt->cur_stmt);
+ ctxt->cur_stmt = NULL;
+ return;
+ }
+ ctxt->stylesheet->statements = stmts;
+ ctxt->cur_stmt = NULL;
+ }
+
+ }
+
+ a_selector_list = NULL; /*keep compiler happy */
+}
+
+static void
+property (CRDocHandler * a_this,
+ CRString * a_name,
+ CRTerm * a_expression,
+ gboolean a_important)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+ CRDeclaration *decl = NULL,
+ *decl2 = NULL;
+ CRString *str = NULL;
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ /*
+ *make sure a current ruleset statement has been allocated
+ *already.
+ */
+ g_return_if_fail
+ (ctxt->cur_stmt
+ &&
+ (ctxt->cur_stmt->type == RULESET_STMT
+ || ctxt->cur_stmt->type == AT_FONT_FACE_RULE_STMT
+ || ctxt->cur_stmt->type == AT_PAGE_RULE_STMT));
+
+ if (a_name) {
+ str = cr_string_dup (a_name);
+ g_return_if_fail (str);
+ }
+
+ /*instantiates a new declaration */
+ decl = cr_declaration_new (ctxt->cur_stmt, str, a_expression);
+ g_return_if_fail (decl);
+ str = NULL;
+ decl->important = a_important;
+ /*
+ *add the new declaration to the current statement
+ *being build.
+ */
+ switch (ctxt->cur_stmt->type) {
+ case RULESET_STMT:
+ decl2 = cr_declaration_append
+ (ctxt->cur_stmt->kind.ruleset->decl_list, decl);
+ if (!decl2) {
+ cr_declaration_destroy (decl);
+ cr_utils_trace_info
+ ("Could not append decl to ruleset");
+ goto error;
+ }
+ ctxt->cur_stmt->kind.ruleset->decl_list = decl2;
+ decl = NULL;
+ decl2 = NULL;
+ break;
+
+ case AT_FONT_FACE_RULE_STMT:
+ decl2 = cr_declaration_append
+ (ctxt->cur_stmt->kind.font_face_rule->decl_list,
+ decl);
+ if (!decl2) {
+ cr_declaration_destroy (decl);
+ cr_utils_trace_info
+ ("Could not append decl to ruleset");
+ goto error;
+ }
+ ctxt->cur_stmt->kind.font_face_rule->decl_list = decl2;
+ decl = NULL;
+ decl2 = NULL;
+ break;
+ case AT_PAGE_RULE_STMT:
+ decl2 = cr_declaration_append
+ (ctxt->cur_stmt->kind.page_rule->decl_list, decl);
+ if (!decl2) {
+ cr_declaration_destroy (decl);
+ cr_utils_trace_info
+ ("Could not append decl to ruleset");
+ goto error;
+ }
+ ctxt->cur_stmt->kind.page_rule->decl_list = decl2;
+ decl = NULL;
+ decl2 = NULL;
+ break;
+
+ default:
+ goto error;
+ break;
+ }
+
+ return;
+
+ error:
+ if (str) {
+ g_free (str);
+ str = NULL;
+ }
+
+ if (decl) {
+ cr_declaration_destroy (decl);
+ decl = NULL;
+ }
+}
+
+static void
+error (CRDocHandler * a_this)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ g_return_if_fail (a_this);
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK && ctxt);
+
+ if (ctxt->cur_stmt) {
+ cr_statement_destroy (ctxt->cur_stmt);
+ ctxt->cur_stmt = NULL;
+ }
+}
+
+static void
+unrecoverable_error (CRDocHandler * a_this)
+{
+ enum CRStatus status = CR_OK;
+ ParsingContext *ctxt = NULL;
+ ParsingContext **ctxtptr = NULL;
+
+ ctxtptr = &ctxt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
+ g_return_if_fail (status == CR_OK);
+
+ if (ctxt) {
+ if (ctxt->stylesheet) {
+ status = cr_doc_handler_set_result
+ (a_this, ctxt->stylesheet);
+ g_return_if_fail (status == CR_OK);
+ }
+ g_free (ctxt);
+ cr_doc_handler_set_ctxt (a_this, NULL);
+ }
+}
+
+/********************************************
+ *Public methods
+ ********************************************/
+
+/**
+ * cr_om_parser_new:
+ *@a_input: the input stream.
+ *
+ *Constructor of the CROMParser.
+ *Returns the newly built instance of #CROMParser.
+ */
+CROMParser *
+cr_om_parser_new (CRInput * a_input)
+{
+ CROMParser *result = NULL;
+ enum CRStatus status = CR_OK;
+
+ result = g_try_malloc (sizeof (CROMParser));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CROMParser));
+ PRIVATE (result) = g_try_malloc (sizeof (CROMParserPriv));
+
+ if (!PRIVATE (result)) {
+ cr_utils_trace_info ("Out of memory");
+ goto error;
+ }
+
+ memset (PRIVATE (result), 0, sizeof (CROMParserPriv));
+
+ PRIVATE (result)->parser = cr_parser_new_from_input (a_input);
+
+ if (!PRIVATE (result)->parser) {
+ cr_utils_trace_info ("parsing instantiation failed");
+ goto error;
+ }
+
+ status = cr_om_parser_init_default_sac_handler (result);
+
+ if (status != CR_OK) {
+ goto error;
+ }
+
+ return result;
+
+ error:
+
+ if (result) {
+ cr_om_parser_destroy (result);
+ }
+
+ return NULL;
+}
+
+/**
+ * cr_om_parser_parse_buf:
+ *@a_this: the current instance of #CROMParser.
+ *@a_buf: the in memory buffer to parse.
+ *@a_len: the length of the in memory buffer in number of bytes.
+ *@a_enc: the encoding of the in memory buffer.
+ *@a_result: out parameter the resulting style sheet
+ *
+ *Parses the content of an in memory buffer.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_om_parser_parse_buf (CROMParser * a_this,
+ const guchar * a_buf,
+ gulong a_len,
+ enum CREncoding a_enc, CRStyleSheet ** a_result)
+{
+
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && a_result, CR_BAD_PARAM_ERROR);
+
+ if (!PRIVATE (a_this)->parser) {
+ PRIVATE (a_this)->parser = cr_parser_new (NULL);
+ }
+
+ status = cr_parser_parse_buf (PRIVATE (a_this)->parser,
+ a_buf, a_len, a_enc);
+
+ if (status == CR_OK) {
+ CRStyleSheet *result = NULL;
+ CRStyleSheet **resultptr = NULL;
+ CRDocHandler *sac_handler = NULL;
+
+ cr_parser_get_sac_handler (PRIVATE (a_this)->parser,
+ &sac_handler);
+ g_return_val_if_fail (sac_handler, CR_ERROR);
+ resultptr = &result;
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer *) resultptr);
+ g_return_val_if_fail (status == CR_OK, status);
+
+ if (result)
+ *a_result = result;
+ }
+
+ return status;
+}
+
+/**
+ * cr_om_parser_simply_parse_buf:
+ *@a_buf: the css2 in memory buffer.
+ *@a_len: the length of the in memory buffer.
+ *@a_enc: the encoding of the in memory buffer.
+ *@a_result: out parameter. The resulting css2 style sheet.
+ *
+ *The simpler way to parse an in memory css2 buffer.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_om_parser_simply_parse_buf (const guchar * a_buf,
+ gulong a_len,
+ enum CREncoding a_enc,
+ CRStyleSheet ** a_result)
+{
+ CROMParser *parser = NULL;
+ enum CRStatus status = CR_OK;
+
+ parser = cr_om_parser_new (NULL);
+ if (!parser) {
+ cr_utils_trace_info ("Could not create om parser");
+ cr_utils_trace_info ("System possibly out of memory");
+ return CR_ERROR;
+ }
+
+ status = cr_om_parser_parse_buf (parser, a_buf, a_len,
+ a_enc, a_result);
+
+ if (parser) {
+ cr_om_parser_destroy (parser);
+ parser = NULL;
+ }
+
+ return status;
+}
+
+/**
+ * cr_om_parser_parse_file:
+ *@a_this: the current instance of the cssom parser.
+ *@a_file_uri: the uri of the file.
+ *(only local file paths are supported so far)
+ *@a_enc: the encoding of the file.
+ *@a_result: out parameter. A pointer
+ *the build css object model.
+ *
+ *Parses a css2 stylesheet contained
+ *in a file.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_om_parser_parse_file (CROMParser * a_this,
+ const guchar * a_file_uri,
+ enum CREncoding a_enc, CRStyleSheet ** a_result)
+{
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && a_file_uri && a_result,
+ CR_BAD_PARAM_ERROR);
+
+ if (!PRIVATE (a_this)->parser) {
+ PRIVATE (a_this)->parser = cr_parser_new_from_file
+ (a_file_uri, a_enc);
+ }
+
+ status = cr_parser_parse_file (PRIVATE (a_this)->parser,
+ a_file_uri, a_enc);
+
+ if (status == CR_OK) {
+ CRStyleSheet *result = NULL;
+ CRStyleSheet **resultptr = NULL;
+ CRDocHandler *sac_handler = NULL;
+
+ cr_parser_get_sac_handler (PRIVATE (a_this)->parser,
+ &sac_handler);
+ g_return_val_if_fail (sac_handler, CR_ERROR);
+ resultptr = &result;
+ status = cr_doc_handler_get_result
+ (sac_handler, (gpointer *) resultptr);
+ g_return_val_if_fail (status == CR_OK, status);
+ if (result)
+ *a_result = result;
+ }
+
+ return status;
+}
+
+/**
+ * cr_om_parser_simply_parse_file:
+ *@a_file_path: the css2 local file path.
+ *@a_enc: the file encoding.
+ *@a_result: out parameter. The returned css stylesheet.
+ *Must be freed by the caller using cr_stylesheet_destroy.
+ *
+ *The simpler method to parse a css2 file.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ *Note that this method uses cr_om_parser_parse_file() so both methods
+ *have the same return values.
+ */
+enum CRStatus
+cr_om_parser_simply_parse_file (const guchar * a_file_path,
+ enum CREncoding a_enc,
+ CRStyleSheet ** a_result)
+{
+ CROMParser *parser = NULL;
+ enum CRStatus status = CR_OK;
+
+ parser = cr_om_parser_new (NULL);
+ if (!parser) {
+ cr_utils_trace_info ("Could not allocate om parser");
+ cr_utils_trace_info ("System may be out of memory");
+ return CR_ERROR;
+ }
+
+ status = cr_om_parser_parse_file (parser, a_file_path,
+ a_enc, a_result);
+ if (parser) {
+ cr_om_parser_destroy (parser);
+ parser = NULL;
+ }
+
+ return status;
+}
+
+/**
+ * cr_om_parser_parse_paths_to_cascade:
+ *@a_this: the current instance of #CROMParser
+ *@a_author_path: the path to the author stylesheet
+ *@a_user_path: the path to the user stylesheet
+ *@a_ua_path: the path to the User Agent stylesheet
+ *@a_encoding: the encoding of the sheets.
+ *@a_result: out parameter. The resulting cascade if the parsing
+ *was okay
+ *
+ *Parses three sheets located by their paths and build a cascade
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise
+ */
+enum CRStatus
+cr_om_parser_parse_paths_to_cascade (CROMParser * a_this,
+ const guchar * a_author_path,
+ const guchar * a_user_path,
+ const guchar * a_ua_path,
+ enum CREncoding a_encoding,
+ CRCascade ** a_result)
+{
+ enum CRStatus status = CR_OK;
+
+ /*0->author sheet, 1->user sheet, 2->UA sheet */
+ CRStyleSheet *sheets[3];
+ guchar *paths[3];
+ CRCascade *result = NULL;
+ gint i = 0;
+
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ memset (sheets, 0, sizeof (CRStyleSheet*) * 3);
+ paths[0] = (guchar *) a_author_path;
+ paths[1] = (guchar *) a_user_path;
+ paths[2] = (guchar *) a_ua_path;
+
+ for (i = 0; i < 3; i++) {
+ status = cr_om_parser_parse_file (a_this, paths[i],
+ a_encoding, &sheets[i]);
+ if (status != CR_OK) {
+ if (sheets[i]) {
+ cr_stylesheet_unref (sheets[i]);
+ sheets[i] = NULL;
+ }
+ continue;
+ }
+ }
+ result = cr_cascade_new (sheets[0], sheets[1], sheets[2]);
+ if (!result) {
+ for (i = 0; i < 3; i++) {
+ cr_stylesheet_unref (sheets[i]);
+ sheets[i] = 0;
+ }
+ return CR_ERROR;
+ }
+ *a_result = result;
+ return CR_OK;
+}
+
+/**
+ * cr_om_parser_simply_parse_paths_to_cascade:
+ *@a_author_path: the path to the author stylesheet
+ *@a_user_path: the path to the user stylesheet
+ *@a_ua_path: the path to the User Agent stylesheet
+ *@a_encoding: the encoding of the sheets.
+ *@a_result: out parameter. The resulting cascade if the parsing
+ *was okay
+ *
+ *Parses three sheets located by their paths and build a cascade
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise
+ */
+enum CRStatus
+cr_om_parser_simply_parse_paths_to_cascade (const guchar * a_author_path,
+ const guchar * a_user_path,
+ const guchar * a_ua_path,
+ enum CREncoding a_encoding,
+ CRCascade ** a_result)
+{
+ enum CRStatus status = CR_OK;
+ CROMParser *parser = NULL;
+
+ parser = cr_om_parser_new (NULL);
+ if (!parser) {
+ cr_utils_trace_info ("could not allocated om parser");
+ cr_utils_trace_info ("System may be out of memory");
+ return CR_ERROR;
+ }
+ status = cr_om_parser_parse_paths_to_cascade (parser,
+ a_author_path,
+ a_user_path,
+ a_ua_path,
+ a_encoding, a_result);
+ if (parser) {
+ cr_om_parser_destroy (parser);
+ parser = NULL;
+ }
+ return status;
+}
+
+/**
+ * cr_om_parser_destroy:
+ *@a_this: the current instance of #CROMParser.
+ *
+ *Destructor of the #CROMParser.
+ */
+void
+cr_om_parser_destroy (CROMParser * a_this)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this));
+
+ if (PRIVATE (a_this)->parser) {
+ cr_parser_destroy (PRIVATE (a_this)->parser);
+ PRIVATE (a_this)->parser = NULL;
+ }
+
+ if (PRIVATE (a_this)) {
+ g_free (PRIVATE (a_this));
+ PRIVATE (a_this) = NULL;
+ }
+
+ if (a_this) {
+ g_free (a_this);
+ a_this = NULL;
+ }
+}
diff --git a/src/st/croco/cr-om-parser.h b/src/st/croco/cr-om-parser.h
new file mode 100644
index 0000000..13d35b1
--- /dev/null
+++ b/src/st/croco/cr-om-parser.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the
+ * GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ *$Id$
+ */
+
+#ifndef __CR_OM_PARSER_H__
+#define __CR_OM_PARSER_H__
+
+#include "cr-parser.h"
+#include "cr-cascade.h"
+
+
+/**
+ *@file
+ *The definition of the CSS Object Model Parser.
+ *This parser uses (and sits) the SAC api of libcroco defined
+ *in cr-parser.h and cr-doc-handler.h
+ */
+
+G_BEGIN_DECLS
+
+typedef struct _CROMParser CROMParser ;
+typedef struct _CROMParserPriv CROMParserPriv ;
+
+/**
+ *The Object model parser.
+ *Can parse a css file and build a css object model.
+ *This parser uses an instance of #CRParser and defines
+ *a set of SAC callbacks to build the Object Model.
+ */
+struct _CROMParser
+{
+ CROMParserPriv *priv ;
+} ;
+
+CROMParser * cr_om_parser_new (CRInput *a_input) ;
+
+
+enum CRStatus cr_om_parser_simply_parse_file (const guchar *a_file_path,
+ enum CREncoding a_enc,
+ CRStyleSheet **a_result) ;
+
+enum CRStatus cr_om_parser_parse_file (CROMParser *a_this,
+ const guchar *a_file_uri,
+ enum CREncoding a_enc,
+ CRStyleSheet **a_result) ;
+
+enum CRStatus cr_om_parser_simply_parse_buf (const guchar *a_buf,
+ gulong a_len,
+ enum CREncoding a_enc,
+ CRStyleSheet **a_result) ;
+
+enum CRStatus cr_om_parser_parse_buf (CROMParser *a_this,
+ const guchar *a_buf,
+ gulong a_len,
+ enum CREncoding a_enc,
+ CRStyleSheet **a_result) ;
+
+enum CRStatus cr_om_parser_parse_paths_to_cascade (CROMParser *a_this,
+ const guchar *a_author_path,
+ const guchar *a_user_path,
+ const guchar *a_ua_path,
+ enum CREncoding a_encoding,
+ CRCascade ** a_result) ;
+
+enum CRStatus cr_om_parser_simply_parse_paths_to_cascade (const guchar *a_author_path,
+ const guchar *a_user_path,
+ const guchar *a_ua_path,
+ enum CREncoding a_encoding,
+ CRCascade ** a_result) ;
+
+void cr_om_parser_destroy (CROMParser *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_OM_PARSER_H__*/
diff --git a/src/st/croco/cr-parser.c b/src/st/croco/cr-parser.c
new file mode 100644
index 0000000..d4f40cf
--- /dev/null
+++ b/src/st/croco/cr-parser.c
@@ -0,0 +1,4539 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the
+ * GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyrights information.
+ */
+
+/**
+ *@CRParser:
+ *
+ *The definition of the #CRParser class.
+ */
+
+#include "string.h"
+#include "cr-parser.h"
+#include "cr-num.h"
+#include "cr-term.h"
+#include "cr-simple-sel.h"
+#include "cr-attr-sel.h"
+
+/*
+ *Random notes:
+ *CSS core syntax vs CSS level 2 syntax
+ *=====================================
+ *
+ *One must keep in mind
+ *that css UA must comply with two syntaxes.
+ *
+ *1/the specific syntax that defines the css language
+ *for a given level of specification (e.g css2 syntax
+ *defined in appendix D.1 of the css2 spec)
+ *
+ *2/the core (general) syntax that is there to allow
+ *UAs to parse style sheets written in levels of CSS that
+ *didn't exist at the time the UAs were created.
+ *
+ *the name of parsing functions (or methods) contained in this file
+ *follows the following scheme: cr_parser_parse_<production_name> (...) ;
+ *where <production_name> is the name
+ *of a production of the css2 language.
+ *When a given production is
+ *defined by the css2 level grammar *and* by the
+ *css core syntax, there will be two functions to parse that production:
+ *one will parse the production defined by the css2 level grammar and the
+ *other will parse the production defined by the css core grammar.
+ *The css2 level grammar related parsing function will be called:
+ *cr_parser_parse_<production_name> (...) ;
+ *Then css core grammar related parsing function will be called:
+ *cr_parser_parse_<production_name>_core (...) ;
+ *
+ *If a production is defined only by the css core grammar, then
+ *it will be named:
+ *cr_parser_parse_<production_name>_core (...) ;
+ */
+
+typedef struct _CRParserError CRParserError;
+
+/**
+ *An abstraction of an error reported by by the
+ *parsing routines.
+ */
+struct _CRParserError {
+ guchar *msg;
+ enum CRStatus status;
+ glong line;
+ glong column;
+ glong byte_num;
+};
+
+enum CRParserState {
+ READY_STATE = 0,
+ TRY_PARSE_CHARSET_STATE,
+ CHARSET_PARSED_STATE,
+ TRY_PARSE_IMPORT_STATE,
+ IMPORT_PARSED_STATE,
+ TRY_PARSE_RULESET_STATE,
+ RULESET_PARSED_STATE,
+ TRY_PARSE_MEDIA_STATE,
+ MEDIA_PARSED_STATE,
+ TRY_PARSE_PAGE_STATE,
+ PAGE_PARSED_STATE,
+ TRY_PARSE_FONT_FACE_STATE,
+ FONT_FACE_PARSED_STATE
+} ;
+
+/**
+ *The private attributes of
+ *#CRParser.
+ */
+struct _CRParserPriv {
+ /**
+ *The tokenizer
+ */
+ CRTknzr *tknzr;
+
+ /**
+ *The sac handlers to call
+ *to notify the parsing of
+ *the css2 constructions.
+ */
+ CRDocHandler *sac_handler;
+
+ /**
+ *A stack of errors reported
+ *by the parsing routines.
+ *Contains instance of #CRParserError.
+ *This pointer is the top of the stack.
+ */
+ GList *err_stack;
+
+ enum CRParserState state;
+ gboolean resolve_import;
+ gboolean is_case_sensitive;
+ gboolean use_core_grammar;
+};
+
+#define PRIVATE(obj) ((obj)->priv)
+
+#define CHARS_TAB_SIZE 12
+
+#define RECURSIVE_CALLERS_LIMIT 100
+
+/**
+ * IS_NUM:
+ *@a_char: the char to test.
+ *return TRUE if the character is a number ([0-9]), FALSE otherwise
+ */
+#define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
+
+/**
+ *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
+ *
+ *@param status the status (of type enum CRStatus) to test.
+ *@param is_exception if set to FALSE, the final status returned
+ *by the current function will be CR_PARSING_ERROR. If set to TRUE, the
+ *current status will be the current value of the 'status' variable.
+ *
+ */
+#define CHECK_PARSING_STATUS(status, is_exception) \
+if ((status) != CR_OK) \
+{ \
+ if (is_exception == FALSE) \
+ { \
+ status = CR_PARSING_ERROR ; \
+ } \
+ goto error ; \
+}
+
+/**
+ * CHECK_PARSING_STATUS_ERR:
+ *@a_this: the current instance of #CRParser .
+ *@a_status: the status to check. Is of type enum #CRStatus.
+ *@a_is_exception: in case of error, if is TRUE, the status
+ *is set to CR_PARSING_ERROR before goto error. If is false, the
+ *real low level status is kept and will be returned by the
+ *upper level function that called this macro. Usually,this must
+ *be set to FALSE.
+ *
+ *same as CHECK_PARSING_STATUS() but this one pushes an error
+ *on the parser error stack when an error arises.
+ *
+ */
+#define CHECK_PARSING_STATUS_ERR(a_this, a_status, a_is_exception,\
+ a_err_msg, a_err_status) \
+if ((a_status) != CR_OK) \
+{ \
+ if (a_is_exception == FALSE) a_status = CR_PARSING_ERROR ; \
+ cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
+ goto error ; \
+}
+
+/**
+ *Peeks the next char from the input stream of the current parser
+ *by invoking cr_tknzr_input_peek_char().
+ *invokes CHECK_PARSING_STATUS on the status returned by
+ *cr_tknzr_peek_char().
+ *
+ *@param a_this the current instance of #CRParser.
+ *@param a_to_char a pointer to the char where to store the
+ *char peeked.
+ */
+#define PEEK_NEXT_CHAR(a_this, a_to_char) \
+{\
+status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
+CHECK_PARSING_STATUS (status, TRUE) \
+}
+
+/**
+ *Reads the next char from the input stream of the current parser.
+ *In case of error, jumps to the "error:" label located in the
+ *function where this macro is called.
+ *@param a_this the current instance of #CRParser
+ *@param to_char a pointer to the guint32 char where to store
+ *the character read.
+ */
+#define READ_NEXT_CHAR(a_this, a_to_char) \
+status = cr_tknzr_read_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
+CHECK_PARSING_STATUS (status, TRUE)
+
+/**
+ *Gets information about the current position in
+ *the input of the parser.
+ *In case of failure, this macro returns from the
+ *calling function and
+ *returns a status code of type enum #CRStatus.
+ *@param a_this the current instance of #CRParser.
+ *@param a_pos out parameter. A pointer to the position
+ *inside the current parser input. Must
+ */
+#define RECORD_INITIAL_POS(a_this, a_pos) \
+status = cr_tknzr_get_cur_pos (PRIVATE \
+(a_this)->tknzr, a_pos) ; \
+g_return_val_if_fail (status == CR_OK, status)
+
+/**
+ *Gets the address of the current byte inside the
+ *parser input.
+ *@param parser the current instance of #CRParser.
+ *@param addr out parameter a pointer (guchar*)
+ *to where the address must be put.
+ */
+#define RECORD_CUR_BYTE_ADDR(a_this, a_addr) \
+status = cr_tknzr_get_cur_byte_addr \
+ (PRIVATE (a_this)->tknzr, a_addr) ; \
+CHECK_PARSING_STATUS (status, TRUE)
+
+/**
+ *Peeks a byte from the topmost parser input at
+ *a given offset from the current position.
+ *If it fails, goto the "error:" label.
+ *
+ *@param a_parser the current instance of #CRParser.
+ *@param a_offset the offset of the byte to peek, the
+ *current byte having the offset '0'.
+ *@param a_byte_ptr out parameter a pointer (guchar*) to
+ *where the peeked char is to be stored.
+ */
+#define PEEK_BYTE(a_parser, a_offset, a_byte_ptr) \
+status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, \
+ a_offset, \
+ a_byte_ptr) ; \
+CHECK_PARSING_STATUS (status, TRUE) ;
+
+#define BYTE(a_parser, a_offset, a_eof) \
+cr_tknzr_peek_byte2 (PRIVATE (a_this)->tknzr, a_offset, a_eof)
+
+/**
+ *Reads a byte from the topmost parser input
+ *steam.
+ *If it fails, goto the "error" label.
+ *@param a_this the current instance of #CRParser.
+ *@param a_byte_ptr the guchar * where to put the read char.
+ */
+#define READ_NEXT_BYTE(a_this, a_byte_ptr) \
+status = cr_tknzr_read_byte (PRIVATE (a_this)->tknzr, a_byte_ptr) ; \
+CHECK_PARSING_STATUS (status, TRUE) ;
+
+/**
+ *Skips a given number of byte in the topmost
+ *parser input. Don't update line and column number.
+ *In case of error, jumps to the "error:" label
+ *of the surrounding function.
+ *@param a_parser the current instance of #CRParser.
+ *@param a_nb_bytes the number of bytes to skip.
+ */
+#define SKIP_BYTES(a_this, a_nb_bytes) \
+status = cr_tknzr_seek_index (PRIVATE (a_this)->tknzr, \
+ CR_SEEK_CUR, a_nb_bytes) ; \
+CHECK_PARSING_STATUS (status, TRUE) ;
+
+/**
+ *Skip utf8 encoded characters.
+ *Updates line and column numbers.
+ *@param a_parser the current instance of #CRParser.
+ *@param a_nb_chars the number of chars to skip. Must be of
+ *type glong.
+ */
+#define SKIP_CHARS(a_parser, a_nb_chars) \
+{ \
+glong nb_chars = a_nb_chars ; \
+status = cr_tknzr_consume_chars \
+ (PRIVATE (a_parser)->tknzr,0, &nb_chars) ; \
+CHECK_PARSING_STATUS (status, TRUE) ; \
+}
+
+/**
+ *Tests the condition and if it is false, sets
+ *status to "CR_PARSING_ERROR" and goto the 'error'
+ *label.
+ *@param condition the condition to test.
+ */
+#define ENSURE_PARSING_COND(condition) \
+if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
+
+#define ENSURE_PARSING_COND_ERR(a_this, a_condition, \
+ a_err_msg, a_err_status) \
+if (! (a_condition)) \
+{ \
+ status = CR_PARSING_ERROR; \
+ cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
+ goto error ; \
+}
+
+#define GET_NEXT_TOKEN(a_this, a_token_ptr) \
+status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, \
+ a_token_ptr) ; \
+ENSURE_PARSING_COND (status == CR_OK) ;
+
+#ifdef WITH_UNICODE_ESCAPE_AND_RANGE
+static enum CRStatus cr_parser_parse_unicode_escape (CRParser * a_this,
+ guint32 * a_unicode);
+static enum CRStatus cr_parser_parse_escape (CRParser * a_this,
+ guint32 * a_esc_code);
+
+static enum CRStatus cr_parser_parse_unicode_range (CRParser * a_this,
+ CRString ** a_inf,
+ CRString ** a_sup);
+#endif
+
+static enum CRStatus cr_parser_parse_stylesheet_core (CRParser * a_this);
+
+static enum CRStatus cr_parser_parse_atrule_core (CRParser * a_this);
+
+static enum CRStatus cr_parser_parse_ruleset_core (CRParser * a_this);
+
+static enum CRStatus cr_parser_parse_selector_core (CRParser * a_this);
+
+static enum CRStatus cr_parser_parse_declaration_core (CRParser * a_this);
+
+static enum CRStatus cr_parser_parse_any_core (CRParser * a_this,
+ guint n_calls);
+
+static enum CRStatus cr_parser_parse_block_core (CRParser * a_this,
+ guint n_calls);
+
+static enum CRStatus cr_parser_parse_value_core (CRParser * a_this);
+
+static enum CRStatus cr_parser_parse_string (CRParser * a_this,
+ CRString ** a_str);
+
+static enum CRStatus cr_parser_parse_ident (CRParser * a_this,
+ CRString ** a_str);
+
+static enum CRStatus cr_parser_parse_uri (CRParser * a_this,
+ CRString ** a_str);
+
+static enum CRStatus cr_parser_parse_function (CRParser * a_this,
+ CRString ** a_func_name,
+ CRTerm ** a_expr);
+static enum CRStatus cr_parser_parse_property (CRParser * a_this,
+ CRString ** a_property);
+
+static enum CRStatus cr_parser_parse_attribute_selector (CRParser * a_this,
+ CRAttrSel ** a_sel);
+
+static enum CRStatus cr_parser_parse_simple_selector (CRParser * a_this,
+ CRSimpleSel ** a_sel);
+
+static enum CRStatus cr_parser_parse_simple_sels (CRParser * a_this,
+ CRSimpleSel ** a_sel);
+
+static CRParserError *cr_parser_error_new (const guchar * a_msg,
+ enum CRStatus);
+
+static void cr_parser_error_set_msg (CRParserError * a_this,
+ const guchar * a_msg);
+
+static void cr_parser_error_dump (CRParserError * a_this);
+
+static void cr_parser_error_set_status (CRParserError * a_this,
+ enum CRStatus a_status);
+
+static void cr_parser_error_set_pos (CRParserError * a_this,
+ glong a_line,
+ glong a_column, glong a_byte_num);
+static void
+ cr_parser_error_destroy (CRParserError * a_this);
+
+static enum CRStatus cr_parser_push_error (CRParser * a_this,
+ const guchar * a_msg,
+ enum CRStatus a_status);
+
+static enum CRStatus cr_parser_dump_err_stack (CRParser * a_this,
+ gboolean a_clear_errs);
+static enum CRStatus
+ cr_parser_clear_errors (CRParser * a_this);
+
+/*****************************
+ *error managemet methods
+ *****************************/
+
+/**
+ *Constructor of #CRParserError class.
+ *@param a_msg the brute error message.
+ *@param a_status the error status.
+ *@return the newly built instance of #CRParserError.
+ */
+static CRParserError *
+cr_parser_error_new (const guchar * a_msg, enum CRStatus a_status)
+{
+ CRParserError *result = NULL;
+
+ result = g_try_malloc (sizeof (CRParserError));
+
+ if (result == NULL) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRParserError));
+
+ cr_parser_error_set_msg (result, a_msg);
+ cr_parser_error_set_status (result, a_status);
+
+ return result;
+}
+
+/**
+ *Sets the message associated to this instance of #CRError.
+ *@param a_this the current instance of #CRParserError.
+ *@param a_msg the new message.
+ */
+static void
+cr_parser_error_set_msg (CRParserError * a_this, const guchar * a_msg)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->msg) {
+ g_free (a_this->msg);
+ }
+
+ a_this->msg = (guchar *) g_strdup ((const gchar *) a_msg);
+}
+
+/**
+ *Sets the error status.
+ *@param a_this the current instance of #CRParserError.
+ *@param a_status the new error status.
+ *
+ */
+static void
+cr_parser_error_set_status (CRParserError * a_this, enum CRStatus a_status)
+{
+ g_return_if_fail (a_this);
+
+ a_this->status = a_status;
+}
+
+/**
+ *Sets the position of the parser error.
+ *@param a_this the current instance of #CRParserError.
+ *@param a_line the line number.
+ *@param a_column the column number.
+ *@param a_byte_num the byte number.
+ */
+static void
+cr_parser_error_set_pos (CRParserError * a_this,
+ glong a_line, glong a_column, glong a_byte_num)
+{
+ g_return_if_fail (a_this);
+
+ a_this->line = a_line;
+ a_this->column = a_column;
+ a_this->byte_num = a_byte_num;
+}
+
+static void
+cr_parser_error_dump (CRParserError * a_this)
+{
+ g_return_if_fail (a_this);
+
+ g_printerr ("parsing error: %ld:%ld:", a_this->line, a_this->column);
+
+ g_printerr ("%s\n", a_this->msg);
+}
+
+/**
+ *The destructor of #CRParserError.
+ *@param a_this the current instance of #CRParserError.
+ */
+static void
+cr_parser_error_destroy (CRParserError * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->msg) {
+ g_free (a_this->msg);
+ a_this->msg = NULL;
+ }
+
+ g_free (a_this);
+}
+
+/**
+ *Pushes an error on the parser error stack.
+ *@param a_this the current instance of #CRParser.
+ *@param a_msg the error message.
+ *@param a_status the error status.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_push_error (CRParser * a_this,
+ const guchar * a_msg, enum CRStatus a_status)
+{
+ enum CRStatus status = CR_OK;
+
+ CRParserError *error = NULL;
+ CRInputPos pos;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_msg, CR_BAD_PARAM_ERROR);
+
+ error = cr_parser_error_new (a_msg, a_status);
+
+ g_return_val_if_fail (error, CR_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &pos);
+
+ cr_parser_error_set_pos
+ (error, pos.line, pos.col, pos.next_byte_index - 1);
+
+ PRIVATE (a_this)->err_stack =
+ g_list_prepend (PRIVATE (a_this)->err_stack, error);
+
+ if (PRIVATE (a_this)->err_stack == NULL)
+ goto error;
+
+ return CR_OK;
+
+ error:
+
+ if (error) {
+ cr_parser_error_destroy (error);
+ error = NULL;
+ }
+
+ return status;
+}
+
+/**
+ *Dumps the error stack on stdout.
+ *@param a_this the current instance of #CRParser.
+ *@param a_clear_errs whether to clear the error stack
+ *after the dump or not.
+ *@return CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+static enum CRStatus
+cr_parser_dump_err_stack (CRParser * a_this, gboolean a_clear_errs)
+{
+ GList *cur = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->err_stack == NULL)
+ return CR_OK;
+
+ for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
+ cr_parser_error_dump ((CRParserError *) cur->data);
+ }
+
+ if (a_clear_errs == TRUE) {
+ cr_parser_clear_errors (a_this);
+ }
+
+ return CR_OK;
+}
+
+/**
+ *Clears all the errors contained in the parser error stack.
+ *Frees all the errors, and the stack that contains'em.
+ *@param a_this the current instance of #CRParser.
+ */
+static enum CRStatus
+cr_parser_clear_errors (CRParser * a_this)
+{
+ GList *cur = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
+ if (cur->data) {
+ cr_parser_error_destroy ((CRParserError *)
+ cur->data);
+ }
+ }
+
+ if (PRIVATE (a_this)->err_stack) {
+ g_list_free (PRIVATE (a_this)->err_stack);
+ PRIVATE (a_this)->err_stack = NULL;
+ }
+
+ return CR_OK;
+}
+
+/**
+ * cr_parser_try_to_skip_spaces_and_comments:
+ *@a_this: the current instance of #CRParser.
+ *
+ *Same as cr_parser_try_to_skip_spaces() but this one skips
+ *spaces and comments.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_try_to_skip_spaces_and_comments (CRParser * a_this)
+{
+ enum CRStatus status = CR_ERROR;
+ CRToken *token = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
+ do {
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ if (status != CR_OK)
+ goto error;
+ }
+ while ((token != NULL)
+ && (token->type == COMMENT_TK || token->type == S_TK));
+
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
+
+ return status;
+
+ error:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ return status;
+}
+
+/***************************************
+ *End of Parser input handling routines
+ ***************************************/
+
+
+/*************************************
+ *Non trivial terminal productions
+ *parsing routines
+ *************************************/
+
+/**
+ *Parses a css stylesheet following the core css grammar.
+ *This is mainly done for test purposes.
+ *During the parsing, no callback is called. This is just
+ *to validate that the stylesheet is well formed according to the
+ *css core syntax.
+ *stylesheet : [ CDO | CDC | S | statement ]*;
+ *@param a_this the current instance of #CRParser.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_stylesheet_core (CRParser * a_this)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ continue_parsing:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ if (status == CR_END_OF_INPUT_ERROR) {
+ status = CR_OK;
+ goto done;
+ } else if (status != CR_OK) {
+ goto error;
+ }
+
+ switch (token->type) {
+
+ case CDO_TK:
+ case CDC_TK:
+ goto continue_parsing;
+ break;
+ default:
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token = NULL;
+ status = cr_parser_parse_statement_core (a_this);
+ cr_parser_clear_errors (a_this);
+ if (status == CR_OK) {
+ goto continue_parsing;
+ } else if (status == CR_END_OF_INPUT_ERROR) {
+ goto done;
+ } else {
+ goto error;
+ }
+ }
+
+ done:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+
+ error:
+ cr_parser_push_error
+ (a_this, (const guchar *) "could not recognize next production", CR_ERROR);
+
+ cr_parser_dump_err_stack (a_this, TRUE);
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses an at-rule as defined by the css core grammar
+ *in chapter 4.1 in the css2 spec.
+ *at-rule : ATKEYWORD S* any* [ block | ';' S* ];
+ *@param a_this the current instance of #CRParser.
+ *@return CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_atrule_core (CRParser * a_this)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token
+ &&
+ (token->type == ATKEYWORD_TK
+ || token->type == IMPORT_SYM_TK
+ || token->type == PAGE_SYM_TK
+ || token->type == MEDIA_SYM_TK
+ || token->type == FONT_FACE_SYM_TK
+ || token->type == CHARSET_SYM_TK));
+
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ do {
+ status = cr_parser_parse_any_core (a_this, 0);
+ } while (status == CR_OK);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ if (token->type == CBO_TK) {
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ status = cr_parser_parse_block_core (a_this, 0);
+ CHECK_PARSING_STATUS (status,
+ FALSE);
+ goto done;
+ } else if (token->type == SEMICOLON_TK) {
+ goto done;
+ } else {
+ status = CR_PARSING_ERROR ;
+ goto error;
+ }
+
+ done:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ return CR_OK;
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr,
+ &init_pos);
+ return status;
+}
+
+/**
+ *Parses a ruleset as defined by the css core grammar in chapter
+ *4.1 of the css2 spec.
+ *ruleset ::= selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
+ *@param a_this the current instance of #CRParser.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_ruleset_core (CRParser * a_this)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_selector_core (a_this);
+
+ ENSURE_PARSING_COND (status == CR_OK
+ || status == CR_PARSING_ERROR
+ || status == CR_END_OF_INPUT_ERROR);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token
+ && token->type == CBO_TK);
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_parser_parse_declaration_core (a_this);
+
+ parse_declaration_list:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+ if (token->type == CBC_TK) {
+ goto done;
+ }
+
+ ENSURE_PARSING_COND (status == CR_OK
+ && token && token->type == SEMICOLON_TK);
+
+ cr_token_destroy (token);
+ token = NULL;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_parser_parse_declaration_core (a_this);
+ cr_parser_clear_errors (a_this);
+ ENSURE_PARSING_COND (status == CR_OK || status == CR_PARSING_ERROR);
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+ if (token->type == CBC_TK) {
+ cr_token_destroy (token);
+ token = NULL;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ goto done;
+ } else {
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ goto parse_declaration_list;
+ }
+
+ done:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (status == CR_OK) {
+ return CR_OK;
+ }
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses a "selector" as specified by the css core
+ *grammar.
+ *selector : any+;
+ *@param a_this the current instance of #CRParser.
+ *@return CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_selector_core (CRParser * a_this)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_any_core (a_this, 0);
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ do {
+ status = cr_parser_parse_any_core (a_this, 0);
+
+ } while (status == CR_OK);
+
+ return CR_OK;
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses a "block" as defined in the css core grammar
+ *in chapter 4.1 of the css2 spec.
+ *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*;
+ *@param a_this the current instance of #CRParser.
+ *@param n_calls used to limit recursion depth
+ *FIXME: code this function.
+ */
+static enum CRStatus
+cr_parser_parse_block_core (CRParser * a_this,
+ guint n_calls)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ if (n_calls > RECURSIVE_CALLERS_LIMIT)
+ return CR_ERROR;
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token
+ && token->type == CBO_TK);
+
+ parse_block_content:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ if (token->type == CBC_TK) {
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ goto done;
+ } else if (token->type == SEMICOLON_TK) {
+ goto parse_block_content;
+ } else if (token->type == ATKEYWORD_TK) {
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ goto parse_block_content;
+ } else if (token->type == CBO_TK) {
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ status = cr_parser_parse_block_core (a_this, n_calls + 1);
+ CHECK_PARSING_STATUS (status, FALSE);
+ goto parse_block_content;
+ } else {
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ status = cr_parser_parse_any_core (a_this, n_calls + 1);
+ CHECK_PARSING_STATUS (status, FALSE);
+ goto parse_block_content;
+ }
+
+ done:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (status == CR_OK)
+ return CR_OK;
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+static enum CRStatus
+cr_parser_parse_declaration_core (CRParser * a_this)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+ CRString *prop = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_property (a_this, &prop);
+ CHECK_PARSING_STATUS (status, FALSE);
+ cr_parser_clear_errors (a_this);
+ ENSURE_PARSING_COND (status == CR_OK && prop);
+ cr_string_destroy (prop);
+ prop = NULL;
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token
+ && token->type == DELIM_TK
+ && token->u.unichar == ':');
+ cr_token_destroy (token);
+ token = NULL;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_parser_parse_value_core (a_this);
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ return CR_OK;
+
+ error:
+
+ if (prop) {
+ cr_string_destroy (prop);
+ prop = NULL;
+ }
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses a "value" production as defined by the css core grammar
+ *in chapter 4.1.
+ *value ::= [ any | block | ATKEYWORD S* ]+;
+ *@param a_this the current instance of #CRParser.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_value_core (CRParser * a_this)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+ glong ref = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ continue_parsing:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ switch (token->type) {
+ case CBO_TK:
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ status = cr_parser_parse_block_core (a_this, 0);
+ CHECK_PARSING_STATUS (status, FALSE);
+ ref++;
+ goto continue_parsing;
+
+ case ATKEYWORD_TK:
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ ref++;
+ goto continue_parsing;
+
+ default:
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ status = cr_parser_parse_any_core (a_this, 0);
+ if (status == CR_OK) {
+ ref++;
+ goto continue_parsing;
+ } else if (status == CR_PARSING_ERROR) {
+ status = CR_OK;
+ goto done;
+ } else {
+ goto error;
+ }
+ }
+
+ done:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (status == CR_OK && ref)
+ return CR_OK;
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses an "any" as defined by the css core grammar in the
+ *css2 spec in chapter 4.1.
+ *any ::= [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
+ * | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
+ * | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*;
+ *
+ *@param a_this the current instance of #CRParser.
+ *@param n_calls used to limit recursion depth
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_any_core (CRParser * a_this,
+ guint n_calls)
+{
+ CRToken *token1 = NULL,
+ *token2 = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ if (n_calls > RECURSIVE_CALLERS_LIMIT)
+ return CR_ERROR;
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token1);
+
+ ENSURE_PARSING_COND (status == CR_OK && token1);
+
+ switch (token1->type) {
+ case IDENT_TK:
+ case NUMBER_TK:
+ case RGB_TK:
+ case PERCENTAGE_TK:
+ case DIMEN_TK:
+ case EMS_TK:
+ case EXS_TK:
+ case LENGTH_TK:
+ case ANGLE_TK:
+ case FREQ_TK:
+ case TIME_TK:
+ case STRING_TK:
+ case DELIM_TK:
+ case URI_TK:
+ case HASH_TK:
+ case UNICODERANGE_TK:
+ case INCLUDES_TK:
+ case DASHMATCH_TK:
+ case S_TK:
+ case COMMENT_TK:
+ case IMPORTANT_SYM_TK:
+ status = CR_OK;
+ break;
+ case FUNCTION_TK:
+ /*
+ *this case isn't specified by the spec but it
+ *does happen. So we have to handle it.
+ *We must consider function with parameters.
+ *We consider parameter as being an "any*" production.
+ */
+ do {
+ status = cr_parser_parse_any_core (a_this, n_calls + 1);
+ } while (status == CR_OK);
+
+ ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token2);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token2 && token2->type == PC_TK);
+ break;
+ case PO_TK:
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token2);
+ ENSURE_PARSING_COND (status == CR_OK && token2);
+
+ if (token2->type == PC_TK) {
+ cr_token_destroy (token2);
+ token2 = NULL;
+ goto done;
+ } else {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token2);
+ token2 = NULL;
+ }
+
+ do {
+ status = cr_parser_parse_any_core (a_this, n_calls + 1);
+ } while (status == CR_OK);
+
+ ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token2);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token2 && token2->type == PC_TK);
+ status = CR_OK;
+ break;
+
+ case BO_TK:
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token2);
+ ENSURE_PARSING_COND (status == CR_OK && token2);
+
+ if (token2->type == BC_TK) {
+ cr_token_destroy (token2);
+ token2 = NULL;
+ goto done;
+ } else {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token2);
+ token2 = NULL;
+ }
+
+ do {
+ status = cr_parser_parse_any_core (a_this, n_calls + 1);
+ } while (status == CR_OK);
+
+ ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token2);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token2 && token2->type == BC_TK);
+ status = CR_OK;
+ break;
+ default:
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ done:
+ if (token1) {
+ cr_token_destroy (token1);
+ token1 = NULL;
+ }
+
+ if (token2) {
+ cr_token_destroy (token2);
+ token2 = NULL;
+ }
+
+ return CR_OK;
+
+ error:
+
+ if (token1) {
+ cr_token_destroy (token1);
+ token1 = NULL;
+ }
+
+ if (token2) {
+ cr_token_destroy (token2);
+ token2 = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+ return status;
+}
+
+/**
+ *Parses an attribute selector as defined in the css2 spec in
+ *appendix D.1:
+ *attrib ::= '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
+ * [ IDENT | STRING ] S* ]? ']'
+ *
+ *@param a_this the "this pointer" of the current instance of
+ *#CRParser .
+ *@param a_sel out parameter. The successfully parsed attribute selector.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_attribute_selector (CRParser * a_this,
+ CRAttrSel ** a_sel)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ CRToken *token = NULL;
+ CRAttrSel *result = NULL;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token
+ && token->type == BO_TK);
+ cr_parsing_location_copy
+ (&location, &token->location) ;
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ result = cr_attr_sel_new ();
+ if (!result) {
+ cr_utils_trace_info ("result failed") ;
+ status = CR_OUT_OF_MEMORY_ERROR ;
+ goto error ;
+ }
+ cr_parsing_location_copy (&result->location,
+ &location) ;
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token && token->type == IDENT_TK);
+
+ result->name = token->u.str;
+ token->u.str = NULL;
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ if (token->type == INCLUDES_TK) {
+ result->match_way = INCLUDES;
+ goto parse_right_part;
+ } else if (token->type == DASHMATCH_TK) {
+ result->match_way = DASHMATCH;
+ goto parse_right_part;
+ } else if (token->type == DELIM_TK && token->u.unichar == '=') {
+ result->match_way = EQUALS;
+ goto parse_right_part;
+ } else if (token->type == BC_TK) {
+ result->match_way = SET;
+ goto done;
+ }
+
+ parse_right_part:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ if (token->type == IDENT_TK) {
+ result->value = token->u.str;
+ token->u.str = NULL;
+ } else if (token->type == STRING_TK) {
+ result->value = token->u.str;
+ token->u.str = NULL;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+
+ ENSURE_PARSING_COND (status == CR_OK && token
+ && token->type == BC_TK);
+ done:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (*a_sel) {
+ status = cr_attr_sel_append_attr_sel (*a_sel, result);
+ CHECK_PARSING_STATUS (status, FALSE);
+ } else {
+ *a_sel = result;
+ }
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+
+ error:
+
+ if (result) {
+ cr_attr_sel_destroy (result);
+ result = NULL;
+ }
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses a "property" as specified by the css2 spec at [4.1.1]:
+ *property : IDENT S*;
+ *
+ *@param a_this the "this pointer" of the current instance of #CRParser.
+ *@param GString a_property out parameter. The parsed property without the
+ *trailing spaces. If *a_property is NULL, this function allocates a
+ *new instance of GString and set it content to the parsed property.
+ *If not, the property is just appended to a_property's previous content.
+ *In both cases, it is up to the caller to free a_property.
+ *@return CR_OK upon successful completion, CR_PARSING_ERROR if the
+ *next construction was not a "property", or an error code.
+ */
+static enum CRStatus
+cr_parser_parse_property (CRParser * a_this,
+ CRString ** a_property)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->tknzr
+ && a_property,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_ident (a_this, a_property);
+ CHECK_PARSING_STATUS (status, TRUE);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+
+ error:
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_term:
+ *@a_term: out parameter. The successfully parsed term.
+ *
+ *Parses a "term" as defined in the css2 spec, appendix D.1:
+ *term ::= unary_operator? [NUMBER S* | PERCENTAGE S* | LENGTH S* |
+ *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] |
+ *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
+ *
+ *TODO: handle parsing of 'RGB'
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_term (CRParser * a_this, CRTerm ** a_term)
+{
+ enum CRStatus status = CR_PARSING_ERROR;
+ CRInputPos init_pos;
+ CRTerm *result = NULL;
+ CRTerm *param = NULL;
+ CRToken *token = NULL;
+ CRString *func_name = NULL;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this && a_term, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ result = cr_term_new ();
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ if (status != CR_OK || !token)
+ goto error;
+
+ cr_parsing_location_copy (&location, &token->location) ;
+ if (token->type == DELIM_TK && token->u.unichar == '+') {
+ result->unary_op = PLUS_UOP;
+ cr_token_destroy (token) ;
+ token = NULL ;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ if (status != CR_OK || !token)
+ goto error;
+ } else if (token->type == DELIM_TK && token->u.unichar == '-') {
+ result->unary_op = MINUS_UOP;
+ cr_token_destroy (token) ;
+ token = NULL ;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ if (status != CR_OK || !token)
+ goto error;
+ }
+
+ if (token->type == EMS_TK
+ || token->type == EXS_TK
+ || token->type == LENGTH_TK
+ || token->type == ANGLE_TK
+ || token->type == TIME_TK
+ || token->type == FREQ_TK
+ || token->type == PERCENTAGE_TK
+ || token->type == NUMBER_TK) {
+ status = cr_term_set_number (result, token->u.num);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token->u.num = NULL;
+ status = CR_OK;
+ } else if (token && token->type == FUNCTION_TK) {
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ status = cr_parser_parse_function (a_this, &func_name,
+ &param);
+
+ if (status == CR_OK) {
+ status = cr_term_set_function (result,
+ func_name,
+ param);
+ CHECK_PARSING_STATUS (status, TRUE);
+ }
+ } else if (token && token->type == STRING_TK) {
+ status = cr_term_set_string (result,
+ token->u.str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token->u.str = NULL;
+ } else if (token && token->type == IDENT_TK) {
+ status = cr_term_set_ident (result, token->u.str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token->u.str = NULL;
+ } else if (token && token->type == URI_TK) {
+ status = cr_term_set_uri (result, token->u.str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token->u.str = NULL;
+ } else if (token && token->type == RGB_TK) {
+ status = cr_term_set_rgb (result, token->u.rgb);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token->u.rgb = NULL;
+ } else if (token && token->type == UNICODERANGE_TK) {
+ result->type = TERM_UNICODERANGE;
+ status = CR_PARSING_ERROR;
+ } else if (token && token->type == HASH_TK) {
+ status = cr_term_set_hash (result, token->u.str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token->u.str = NULL;
+ } else {
+ status = CR_PARSING_ERROR;
+ }
+
+ if (status != CR_OK) {
+ goto error;
+ }
+ cr_parsing_location_copy (&result->location,
+ &location) ;
+ *a_term = cr_term_append_term (*a_term, result);
+
+ result = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+
+ error:
+
+ if (result) {
+ cr_term_destroy (result);
+ result = NULL;
+ }
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (param) {
+ cr_term_destroy (param);
+ param = NULL;
+ }
+
+ if (func_name) {
+ cr_string_destroy (func_name);
+ func_name = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_simple_selector:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *@a_sel: out parameter. Is set to the successfully parsed simple
+ *selector.
+ *
+ *Parses a "simple_selector" as defined by the css2 spec in appendix D.1 :
+ *element_name? [ HASH | class | attrib | pseudo ]* S*
+ *and where pseudo is:
+ *pseudo ::= ':' [ IDENT | FUNCTION S* IDENT S* ')' ]
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_simple_selector (CRParser * a_this, CRSimpleSel ** a_sel)
+{
+ enum CRStatus status = CR_ERROR;
+ CRInputPos init_pos;
+ CRToken *token = NULL;
+ CRSimpleSel *sel = NULL;
+ CRAdditionalSel *add_sel_list = NULL;
+ gboolean found_sel = FALSE;
+ guint32 cur_char = 0;
+
+ g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ if (status != CR_OK)
+ goto error;
+
+ sel = cr_simple_sel_new ();
+ ENSURE_PARSING_COND (sel);
+
+ cr_parsing_location_copy
+ (&sel->location,
+ &token->location) ;
+
+ if (token && token->type == DELIM_TK
+ && token->u.unichar == '*') {
+ sel->type_mask |= UNIVERSAL_SELECTOR;
+ sel->name = cr_string_new_from_string ("*");
+ found_sel = TRUE;
+ } else if (token && token->type == IDENT_TK) {
+ sel->name = token->u.str;
+ sel->type_mask |= TYPE_SELECTOR;
+ token->u.str = NULL;
+ found_sel = TRUE;
+ } else {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ }
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ for (;;) {
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr,
+ &token);
+ if (status != CR_OK)
+ goto error;
+
+ if (token && token->type == HASH_TK) {
+ /*we parsed an attribute id */
+ CRAdditionalSel *add_sel = NULL;
+
+ add_sel = cr_additional_sel_new_with_type
+ (ID_ADD_SELECTOR);
+
+ add_sel->content.id_name = token->u.str;
+ token->u.str = NULL;
+
+ cr_parsing_location_copy
+ (&add_sel->location,
+ &token->location) ;
+ add_sel_list =
+ cr_additional_sel_append
+ (add_sel_list, add_sel);
+ found_sel = TRUE;
+ } else if (token && (token->type == DELIM_TK)
+ && (token->u.unichar == '.')) {
+ cr_token_destroy (token);
+ token = NULL;
+
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+ if (status != CR_OK)
+ goto error;
+
+ if (token && token->type == IDENT_TK) {
+ CRAdditionalSel *add_sel = NULL;
+
+ add_sel = cr_additional_sel_new_with_type
+ (CLASS_ADD_SELECTOR);
+
+ add_sel->content.class_name = token->u.str;
+ token->u.str = NULL;
+
+ add_sel_list =
+ cr_additional_sel_append
+ (add_sel_list, add_sel);
+ found_sel = TRUE;
+
+ cr_parsing_location_copy
+ (&add_sel->location,
+ & token->location) ;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ } else if (token && token->type == BO_TK) {
+ CRAttrSel *attr_sel = NULL;
+ CRAdditionalSel *add_sel = NULL;
+
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ if (status != CR_OK)
+ goto error;
+ token = NULL;
+
+ status = cr_parser_parse_attribute_selector
+ (a_this, &attr_sel);
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ add_sel = cr_additional_sel_new_with_type
+ (ATTRIBUTE_ADD_SELECTOR);
+
+ ENSURE_PARSING_COND (add_sel != NULL);
+
+ add_sel->content.attr_sel = attr_sel;
+
+ add_sel_list =
+ cr_additional_sel_append
+ (add_sel_list, add_sel);
+ found_sel = TRUE;
+ cr_parsing_location_copy
+ (&add_sel->location,
+ &attr_sel->location) ;
+ } else if (token && (token->type == DELIM_TK)
+ && (token->u.unichar == ':')) {
+ CRPseudo *pseudo = NULL;
+
+ /*try to parse a pseudo */
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ pseudo = cr_pseudo_new ();
+
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ cr_parsing_location_copy
+ (&pseudo->location,
+ &token->location) ;
+
+ if (token->type == IDENT_TK) {
+ pseudo->type = IDENT_PSEUDO;
+ pseudo->name = token->u.str;
+ token->u.str = NULL;
+ found_sel = TRUE;
+ } else if (token->type == FUNCTION_TK) {
+ pseudo->name = token->u.str;
+ token->u.str = NULL;
+ cr_parser_try_to_skip_spaces_and_comments
+ (a_this);
+ status = cr_parser_parse_ident
+ (a_this, &pseudo->extra);
+
+ ENSURE_PARSING_COND (status == CR_OK);
+ READ_NEXT_CHAR (a_this, &cur_char);
+ ENSURE_PARSING_COND (cur_char == ')');
+ pseudo->type = FUNCTION_PSEUDO;
+ found_sel = TRUE;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ if (status == CR_OK) {
+ CRAdditionalSel *add_sel = NULL;
+
+ add_sel = cr_additional_sel_new_with_type
+ (PSEUDO_CLASS_ADD_SELECTOR);
+
+ add_sel->content.pseudo = pseudo;
+ cr_parsing_location_copy
+ (&add_sel->location,
+ &pseudo->location) ;
+ add_sel_list =
+ cr_additional_sel_append
+ (add_sel_list, add_sel);
+ status = CR_OK;
+ }
+ } else {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ break;
+ }
+ }
+
+ if (status == CR_OK && found_sel == TRUE) {
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ sel->add_sel = add_sel_list;
+ add_sel_list = NULL;
+
+ if (*a_sel == NULL) {
+ *a_sel = sel;
+ } else {
+ cr_simple_sel_append_simple_sel (*a_sel, sel);
+ }
+
+ sel = NULL;
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+ } else {
+ status = CR_PARSING_ERROR;
+ }
+
+ error:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (add_sel_list) {
+ cr_additional_sel_destroy (add_sel_list);
+ add_sel_list = NULL;
+ }
+
+ if (sel) {
+ cr_simple_sel_destroy (sel);
+ sel = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+
+}
+
+/**
+ * cr_parser_parse_simple_sels:
+ *@a_this: the this pointer of the current instance of #CRParser.
+ *@a_start: a pointer to the
+ *first character of the successfully parsed
+ *string.
+ *@a_end: a pointer to the last character of the successfully parsed
+ *string.
+ *
+ *Parses a "selector" as defined by the css2 spec in appendix D.1:
+ *selector ::= simple_selector [ combinator simple_selector ]*
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_simple_sels (CRParser * a_this,
+ CRSimpleSel ** a_sel)
+{
+ enum CRStatus status = CR_ERROR;
+ CRInputPos init_pos;
+ CRSimpleSel *sel = NULL;
+ guint32 cur_char = 0;
+
+ g_return_val_if_fail (a_this
+ && PRIVATE (a_this)
+ && a_sel,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_simple_selector (a_this, &sel);
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ *a_sel = cr_simple_sel_append_simple_sel (*a_sel, sel);
+
+ for (;;) {
+ guint32 next_char = 0;
+ enum Combinator comb = 0;
+
+ sel = NULL;
+
+ PEEK_NEXT_CHAR (a_this, &next_char);
+
+ if (next_char == '+') {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ comb = COMB_PLUS;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ } else if (next_char == '>') {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ comb = COMB_GT;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ } else {
+ comb = COMB_WS;
+ }
+
+ status = cr_parser_parse_simple_selector (a_this, &sel);
+ if (status != CR_OK)
+ break;
+
+ if (comb && sel) {
+ sel->combinator = comb;
+ comb = 0;
+ }
+ if (sel) {
+ *a_sel = cr_simple_sel_append_simple_sel (*a_sel,
+ sel) ;
+ }
+ }
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+
+ error:
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_selector:
+ *@a_this: the current instance of #CRParser.
+ *@a_selector: the parsed list of comma separated
+ *selectors.
+ *
+ *Parses a comma separated list of selectors.
+ *
+ *Returns CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_selector (CRParser * a_this,
+ CRSelector ** a_selector)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ guint32 cur_char = 0,
+ next_char = 0;
+ CRSimpleSel *simple_sels = NULL;
+ CRSelector *selector = NULL;
+
+ g_return_val_if_fail (a_this && a_selector, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_simple_sels (a_this, &simple_sels);
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ if (simple_sels) {
+ selector = cr_selector_append_simple_sel
+ (selector, simple_sels);
+ if (selector) {
+ cr_parsing_location_copy
+ (&selector->location,
+ &simple_sels->location) ;
+ }
+ simple_sels = NULL;
+ } else {
+ status = CR_PARSING_ERROR ;
+ goto error ;
+ }
+
+ status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
+ &next_char);
+ if (status != CR_OK) {
+ if (status == CR_END_OF_INPUT_ERROR) {
+ status = CR_OK;
+ goto okay;
+ } else {
+ goto error;
+ }
+ }
+
+ if (next_char == ',') {
+ for (;;) {
+ simple_sels = NULL;
+
+ status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
+ &next_char);
+ if (status != CR_OK) {
+ if (status == CR_END_OF_INPUT_ERROR) {
+ status = CR_OK;
+ break;
+ } else {
+ goto error;
+ }
+ }
+
+ if (next_char != ',')
+ break;
+
+ /*consume the ',' char */
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_simple_sels
+ (a_this, &simple_sels);
+
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ if (simple_sels) {
+ selector =
+ cr_selector_append_simple_sel
+ (selector, simple_sels);
+
+ simple_sels = NULL;
+ }
+ }
+ }
+
+ okay:
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ if (!*a_selector) {
+ *a_selector = selector;
+ } else {
+ *a_selector = cr_selector_append (*a_selector, selector);
+ }
+
+ selector = NULL;
+ return CR_OK;
+
+ error:
+
+ if (simple_sels) {
+ cr_simple_sel_destroy (simple_sels);
+ simple_sels = NULL;
+ }
+
+ if (selector) {
+ cr_selector_unref (selector);
+ selector = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_function:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *
+ *@a_func_name: out parameter. The parsed function name
+ *@a_expr: out parameter. The successfully parsed term.
+ *
+ *Parses a "function" as defined in css spec at appendix D.1:
+ *function ::= FUNCTION S* expr ')' S*
+ *FUNCTION ::= ident'('
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_function (CRParser * a_this,
+ CRString ** a_func_name,
+ CRTerm ** a_expr)
+{
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+ CRToken *token = NULL;
+ CRTerm *expr = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_func_name,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ if (status != CR_OK)
+ goto error;
+
+ if (token && token->type == FUNCTION_TK) {
+ *a_func_name = token->u.str;
+ token->u.str = NULL;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this) ;
+
+ status = cr_parser_parse_expr (a_this, &expr);
+
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ if (status != CR_OK)
+ goto error;
+
+ ENSURE_PARSING_COND (token && token->type == PC_TK);
+
+ cr_token_destroy (token);
+ token = NULL;
+
+ if (expr) {
+ *a_expr = cr_term_append_term (*a_expr, expr);
+ expr = NULL;
+ }
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+
+ error:
+
+ if (*a_func_name) {
+ cr_string_destroy (*a_func_name);
+ *a_func_name = NULL;
+ }
+
+ if (expr) {
+ cr_term_destroy (expr);
+ expr = NULL;
+ }
+
+ if (token) {
+ cr_token_destroy (token);
+
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_uri:
+ *@a_this: the current instance of #CRParser.
+ *@a_str: the successfully parsed url.
+ *
+ *Parses an uri as defined by the css spec [4.1.1]:
+ * URI ::= url\({w}{string}{w}\)
+ * |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_uri (CRParser * a_this, CRString ** a_str)
+{
+
+ enum CRStatus status = CR_PARSING_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
+
+ status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
+ URI_TK, NO_ET, a_str, NULL);
+ return status;
+}
+
+/**
+ * cr_parser_parse_string:
+ *@a_this: the current instance of #CRParser.
+ *@a_start: out parameter. Upon successful completion,
+ *points to the beginning of the string, points to an undefined value
+ *otherwise.
+ *@a_end: out parameter. Upon successful completion, points to
+ *the beginning of the string, points to an undefined value otherwise.
+ *
+ *Parses a string type as defined in css spec [4.1.1]:
+ *
+ *string ::= {string1}|{string2}
+ *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
+ *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_string (CRParser * a_this, CRString ** a_str)
+{
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->tknzr
+ && a_str, CR_BAD_PARAM_ERROR);
+
+ status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
+ STRING_TK, NO_ET, a_str, NULL);
+ return status;
+}
+
+/**
+ *Parses an "ident" as defined in css spec [4.1.1]:
+ *ident ::= {nmstart}{nmchar}*
+ *
+ *@param a_this the currens instance of #CRParser.
+ *
+ *@param a_str a pointer to parsed ident. If *a_str is NULL,
+ *this function allocates a new instance of #CRString. If not,
+ *the function just appends the parsed string to the one passed.
+ *In both cases it is up to the caller to free *a_str.
+ *
+ *@return CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_ident (CRParser * a_this, CRString ** a_str)
+{
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->tknzr
+ && a_str, CR_BAD_PARAM_ERROR);
+
+ status = cr_tknzr_parse_token (PRIVATE (a_this)->tknzr,
+ IDENT_TK, NO_ET, a_str, NULL);
+ return status;
+}
+
+/**
+ *the next rule is ignored as well. This seems to be a bug
+ *Parses a stylesheet as defined in the css2 spec in appendix D.1:
+ *stylesheet ::= [ CHARSET_SYM S* STRING S* ';' ]?
+ * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
+ * [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
+ *
+ *TODO: Finish the code of this function. Think about splitting it into
+ *smaller functions.
+ *
+ *@param a_this the "this pointer" of the current instance of #CRParser.
+ *@param a_start out parameter. A pointer to the first character of
+ *the successfully parsed string.
+ *@param a_end out parameter. A pointer to the first character of
+ *the successfully parsed string.
+ *
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_parser_parse_stylesheet (CRParser * a_this)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ CRToken *token = NULL;
+ CRString *charset = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ PRIVATE (a_this)->state = READY_STATE;
+
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->start_document) {
+ PRIVATE (a_this)->sac_handler->start_document
+ (PRIVATE (a_this)->sac_handler);
+ }
+
+ parse_charset:
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+
+ if (status == CR_END_OF_INPUT_ERROR)
+ goto done;
+ CHECK_PARSING_STATUS (status, TRUE);
+
+ if (token && token->type == CHARSET_SYM_TK) {
+ CRParsingLocation location = {0} ;
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token = NULL;
+
+ status = cr_parser_parse_charset (a_this,
+ &charset,
+ &location);
+
+ if (status == CR_OK && charset) {
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->charset) {
+ PRIVATE (a_this)->sac_handler->charset
+ (PRIVATE (a_this)->sac_handler,
+ charset, &location);
+ }
+ } else if (status != CR_END_OF_INPUT_ERROR) {
+ status = cr_parser_parse_atrule_core (a_this);
+ CHECK_PARSING_STATUS (status, FALSE);
+ }
+
+ if (charset) {
+ cr_string_destroy (charset);
+ charset = NULL;
+ }
+ } else if (token
+ && (token->type == S_TK
+ || token->type == COMMENT_TK)) {
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ CHECK_PARSING_STATUS (status, TRUE);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ goto parse_charset ;
+ } else if (token) {
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ CHECK_PARSING_STATUS (status, TRUE);
+ }
+
+/* parse_imports:*/
+ do {
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ cr_parser_try_to_skip_spaces_and_comments (a_this) ;
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+
+ if (status == CR_END_OF_INPUT_ERROR)
+ goto done;
+ CHECK_PARSING_STATUS (status, TRUE);
+ } while (token
+ && (token->type == S_TK
+ || token->type == CDO_TK || token->type == CDC_TK));
+
+ if (token) {
+ status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL;
+ }
+
+ for (;;) {
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+ if (status == CR_END_OF_INPUT_ERROR)
+ goto done;
+ CHECK_PARSING_STATUS (status, TRUE);
+
+ if (token && token->type == IMPORT_SYM_TK) {
+ GList *media_list = NULL;
+ CRString *import_string = NULL;
+ CRParsingLocation location = {0} ;
+
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ CHECK_PARSING_STATUS (status, TRUE);
+
+ status = cr_parser_parse_import (a_this,
+ &media_list,
+ &import_string,
+ &location);
+ if (status == CR_OK) {
+ if (import_string
+ && PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->import_style) {
+ PRIVATE (a_this)->sac_handler->import_style
+ (PRIVATE(a_this)->sac_handler,
+ media_list,
+ import_string,
+ NULL, &location) ;
+
+ if ((PRIVATE (a_this)->sac_handler->resolve_import == TRUE)) {
+ /*
+ *TODO: resolve the
+ *import rule.
+ */
+ }
+
+ if ((PRIVATE (a_this)->sac_handler->import_style_result)) {
+ PRIVATE (a_this)->sac_handler->import_style_result
+ (PRIVATE (a_this)->sac_handler,
+ media_list, import_string,
+ NULL, NULL);
+ }
+ }
+ } else if (status != CR_END_OF_INPUT_ERROR) {
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->error) {
+ PRIVATE (a_this)->sac_handler->error
+ (PRIVATE (a_this)->sac_handler);
+ }
+ status = cr_parser_parse_atrule_core (a_this);
+ CHECK_PARSING_STATUS (status, TRUE) ;
+ } else {
+ goto error ;
+ }
+
+ /*
+ *then, after calling the appropriate
+ *SAC handler, free
+ *the media_list and import_string.
+ */
+ if (media_list) {
+ GList *cur = NULL;
+
+ /*free the medium list */
+ for (cur = media_list; cur; cur = cur->next) {
+ if (cur->data) {
+ cr_string_destroy (cur->data);
+ }
+ }
+
+ g_list_free (media_list);
+ media_list = NULL;
+ }
+
+ if (import_string) {
+ cr_string_destroy (import_string);
+ import_string = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ } else if (token
+ && (token->type == S_TK
+ || token->type == CDO_TK
+ || token->type == CDC_TK)) {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+
+ do {
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+
+ if (status == CR_END_OF_INPUT_ERROR)
+ goto done;
+ CHECK_PARSING_STATUS (status, TRUE);
+ } while (token
+ && (token->type == S_TK
+ || token->type == CDO_TK
+ || token->type == CDC_TK));
+ } else {
+ if (token) {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ }
+ goto parse_ruleset_and_others;
+ }
+ }
+
+ parse_ruleset_and_others:
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ for (;;) {
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+ if (status == CR_END_OF_INPUT_ERROR)
+ goto done;
+ CHECK_PARSING_STATUS (status, TRUE);
+
+ if (token
+ && (token->type == S_TK
+ || token->type == CDO_TK || token->type == CDC_TK)) {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+
+ do {
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments
+ (a_this);
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+ } while (token
+ && (token->type == S_TK
+ || token->type == COMMENT_TK
+ || token->type == CDO_TK
+ || token->type == CDC_TK));
+ if (token) {
+ cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ }
+ } else if (token
+ && (token->type == HASH_TK
+ || (token->type == DELIM_TK
+ && token->u.unichar == '.')
+ || (token->type == DELIM_TK
+ && token->u.unichar == ':')
+ || (token->type == DELIM_TK
+ && token->u.unichar == '*')
+ || (token->type == BO_TK)
+ || token->type == IDENT_TK)) {
+ /*
+ *Try to parse a CSS2 ruleset.
+ *if the parsing fails, try to parse
+ *a css core ruleset.
+ */
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token = NULL;
+
+ status = cr_parser_parse_ruleset (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->error) {
+ PRIVATE (a_this)->sac_handler->
+ error
+ (PRIVATE (a_this)->
+ sac_handler);
+ }
+
+ status = cr_parser_parse_ruleset_core
+ (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ } else if (token && token->type == MEDIA_SYM_TK) {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token = NULL;
+
+ status = cr_parser_parse_media (a_this);
+ if (status == CR_OK) {
+ continue;
+ } else {
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->error) {
+ PRIVATE (a_this)->sac_handler->
+ error
+ (PRIVATE (a_this)->
+ sac_handler);
+ }
+
+ status = cr_parser_parse_atrule_core (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ } else if (token && token->type == PAGE_SYM_TK) {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token = NULL;
+ status = cr_parser_parse_page (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->error) {
+ PRIVATE (a_this)->sac_handler->
+ error
+ (PRIVATE (a_this)->
+ sac_handler);
+ }
+
+ status = cr_parser_parse_atrule_core (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ } else if (token && token->type == FONT_FACE_SYM_TK) {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token = NULL;
+ status = cr_parser_parse_font_face (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->error) {
+ PRIVATE (a_this)->sac_handler->
+ error
+ (PRIVATE (a_this)->
+ sac_handler);
+ }
+
+ status = cr_parser_parse_atrule_core (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ } else {
+ status = cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr, token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ token = NULL;
+ status = cr_parser_parse_statement_core (a_this);
+
+ if (status == CR_OK) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+
+ done:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (status == CR_END_OF_INPUT_ERROR || status == CR_OK) {
+
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->end_document) {
+ PRIVATE (a_this)->sac_handler->end_document
+ (PRIVATE (a_this)->sac_handler);
+ }
+
+ return CR_OK;
+ }
+
+ cr_parser_push_error
+ (a_this, (const guchar *) "could not recognize next production", CR_ERROR);
+
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
+ PRIVATE (a_this)->sac_handler->
+ unrecoverable_error (PRIVATE (a_this)->sac_handler);
+ }
+
+ cr_parser_dump_err_stack (a_this, TRUE);
+
+ return status;
+
+ error:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->unrecoverable_error) {
+ PRIVATE (a_this)->sac_handler->
+ unrecoverable_error (PRIVATE (a_this)->sac_handler);
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/****************************************
+ *Public CRParser Methods
+ ****************************************/
+
+/**
+ * cr_parser_new:
+ * @a_tknzr: the tokenizer to use for the parsing.
+ *
+ *Creates a new parser to parse data
+ *coming the input stream given in parameter.
+ *
+ *Returns the newly created instance of #CRParser,
+ *or NULL if an error occurred.
+ */
+CRParser *
+cr_parser_new (CRTknzr * a_tknzr)
+{
+ CRParser *result = NULL;
+ enum CRStatus status = CR_OK;
+
+ result = g_malloc0 (sizeof (CRParser));
+
+ PRIVATE (result) = g_malloc0 (sizeof (CRParserPriv));
+
+ if (a_tknzr) {
+ status = cr_parser_set_tknzr (result, a_tknzr);
+ }
+
+ g_return_val_if_fail (status == CR_OK, NULL);
+
+ return result;
+}
+
+/**
+ * cr_parser_new_from_buf:
+ *@a_buf: the buffer to parse.
+ *@a_len: the length of the data in the buffer.
+ *@a_enc: the encoding of the input buffer a_buf.
+ *@a_free_buf: if set to TRUE, a_buf will be freed
+ *during the destruction of the newly built instance
+ *of #CRParser. If set to FALSE, it is up to the caller to
+ *eventually free it.
+ *
+ *Instantiates a new parser from a memory buffer.
+ *
+ *Returns the newly built parser, or NULL if an error arises.
+ */
+CRParser *
+cr_parser_new_from_buf (guchar * a_buf,
+ gulong a_len,
+ enum CREncoding a_enc,
+ gboolean a_free_buf)
+{
+ CRParser *result = NULL;
+ CRInput *input = NULL;
+
+ g_return_val_if_fail (a_buf && a_len, NULL);
+
+ input = cr_input_new_from_buf (a_buf, a_len, a_enc, a_free_buf);
+ g_return_val_if_fail (input, NULL);
+
+ result = cr_parser_new_from_input (input);
+ if (!result) {
+ cr_input_destroy (input);
+ input = NULL;
+ return NULL;
+ }
+ return result;
+}
+
+/**
+ * cr_parser_new_from_input:
+ * @a_input: the parser input stream to use.
+ *
+ * Returns a newly built parser input.
+ */
+CRParser *
+cr_parser_new_from_input (CRInput * a_input)
+{
+ CRParser *result = NULL;
+ CRTknzr *tokenizer = NULL;
+
+ if (a_input) {
+ tokenizer = cr_tknzr_new (a_input);
+ g_return_val_if_fail (tokenizer, NULL);
+ }
+
+ result = cr_parser_new (tokenizer);
+ g_return_val_if_fail (result, NULL);
+
+ return result;
+}
+
+/**
+ * cr_parser_new_from_file:
+ * @a_file_uri: the uri of the file to parse.
+ * @a_enc: the file encoding to use.
+ *
+ * Returns the newly built parser.
+ */
+CRParser *
+cr_parser_new_from_file (const guchar * a_file_uri, enum CREncoding a_enc)
+{
+ CRParser *result = NULL;
+ CRTknzr *tokenizer = NULL;
+
+ tokenizer = cr_tknzr_new_from_uri (a_file_uri, a_enc);
+ if (!tokenizer) {
+ cr_utils_trace_info ("Could not open input file");
+ return NULL;
+ }
+
+ result = cr_parser_new (tokenizer);
+ g_return_val_if_fail (result, NULL);
+ return result;
+}
+
+/**
+ * cr_parser_set_sac_handler:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *@a_handler: the handler to set.
+ *
+ *Sets a SAC document handler to the parser.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_set_sac_handler (CRParser * a_this, CRDocHandler * a_handler)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->sac_handler) {
+ cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
+ }
+
+ PRIVATE (a_this)->sac_handler = a_handler;
+ cr_doc_handler_ref (a_handler);
+
+ return CR_OK;
+}
+
+/**
+ * cr_parser_get_sac_handler:
+ *@a_this: the "this pointer" of the current instance of
+ *#CRParser.
+ *@a_handler: out parameter. The returned handler.
+ *
+ *Gets the SAC document handler.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_parser_get_sac_handler (CRParser * a_this, CRDocHandler ** a_handler)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ *a_handler = PRIVATE (a_this)->sac_handler;
+
+ return CR_OK;
+}
+
+/**
+ * cr_parser_set_default_sac_handler:
+ *@a_this: a pointer to the current instance of #CRParser.
+ *
+ *Sets the SAC handler associated to the current instance
+ *of #CRParser to the default SAC handler.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_set_default_sac_handler (CRParser * a_this)
+{
+ CRDocHandler *default_sac_handler = NULL;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ default_sac_handler = cr_doc_handler_new ();
+
+ cr_doc_handler_set_default_sac_handler (default_sac_handler);
+
+ status = cr_parser_set_sac_handler (a_this, default_sac_handler);
+
+ if (status != CR_OK) {
+ cr_doc_handler_destroy (default_sac_handler);
+ default_sac_handler = NULL;
+ }
+
+ return status;
+}
+
+/**
+ * cr_parser_set_use_core_grammar:
+ * @a_this: the current instance of #CRParser.
+ * @a_use_core_grammar: where to parse against the css core grammar.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_set_use_core_grammar (CRParser * a_this,
+ gboolean a_use_core_grammar)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->use_core_grammar = a_use_core_grammar;
+
+ return CR_OK;
+}
+
+/**
+ * cr_parser_get_use_core_grammar:
+ * @a_this: the current instance of #CRParser.
+ * @a_use_core_grammar: whether to use the core grammar or not.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_get_use_core_grammar (CRParser const * a_this,
+ gboolean * a_use_core_grammar)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ *a_use_core_grammar = PRIVATE (a_this)->use_core_grammar;
+
+ return CR_OK;
+}
+
+/**
+ * cr_parser_parse_file:
+ *@a_this: a pointer to the current instance of #CRParser.
+ *@a_file_uri: the uri to the file to load. For the time being,
+ *@a_enc: the encoding of the file to parse.
+ *only local files are supported.
+ *
+ *Parses a the given in parameter.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_file (CRParser * a_this,
+ const guchar * a_file_uri, enum CREncoding a_enc)
+{
+ enum CRStatus status = CR_ERROR;
+ CRTknzr *tknzr = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_file_uri, CR_BAD_PARAM_ERROR);
+
+ tknzr = cr_tknzr_new_from_uri (a_file_uri, a_enc);
+
+ g_return_val_if_fail (tknzr != NULL, CR_ERROR);
+
+ status = cr_parser_set_tknzr (a_this, tknzr);
+ g_return_val_if_fail (status == CR_OK, CR_ERROR);
+
+ status = cr_parser_parse (a_this);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_expr:
+ * @a_this: the current instance of #CRParser.
+ * @a_expr: out parameter. the parsed expression.
+ *
+ *Parses an expression as defined by the css2 spec in appendix
+ *D.1:
+ *expr: term [ operator term ]*
+ *
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_expr (CRParser * a_this, CRTerm ** a_expr)
+{
+ enum CRStatus status = CR_ERROR;
+ CRInputPos init_pos;
+ CRTerm *expr = NULL,
+ *expr2 = NULL;
+ guchar next_byte = 0;
+ gulong nb_terms = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_expr, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_term (a_this, &expr);
+
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ for (;;) {
+ guchar operator = 0;
+
+ status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr,
+ 1, &next_byte);
+ if (status != CR_OK) {
+ if (status == CR_END_OF_INPUT_ERROR) {
+ /*
+ if (!nb_terms)
+ {
+ goto error ;
+ }
+ */
+ status = CR_OK;
+ break;
+ } else {
+ goto error;
+ }
+ }
+
+ if (next_byte == '/' || next_byte == ',') {
+ READ_NEXT_BYTE (a_this, &operator);
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_term (a_this, &expr2);
+
+ if (status != CR_OK || expr2 == NULL) {
+ status = CR_OK;
+ break;
+ }
+
+ switch (operator) {
+ case '/':
+ expr2->the_operator = DIVIDE;
+ break;
+ case ',':
+ expr2->the_operator = COMMA;
+
+ default:
+ break;
+ }
+
+ expr = cr_term_append_term (expr, expr2);
+ expr2 = NULL;
+ operator = 0;
+ nb_terms++;
+ }
+
+ if (status == CR_OK) {
+ *a_expr = cr_term_append_term (*a_expr, expr);
+ expr = NULL;
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+ }
+
+ error:
+
+ if (expr) {
+ cr_term_destroy (expr);
+ expr = NULL;
+ }
+
+ if (expr2) {
+ cr_term_destroy (expr2);
+ expr2 = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_prio:
+ *@a_this: the current instance of #CRParser.
+ *@a_prio: a string representing the priority.
+ *Today, only "!important" is returned as only this
+ *priority is defined by css2.
+ *
+ *Parses a declaration priority as defined by
+ *the css2 grammar in appendix C:
+ *prio: IMPORTANT_SYM S*
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_prio (CRParser * a_this, CRString ** a_prio)
+{
+ enum CRStatus status = CR_ERROR;
+ CRInputPos init_pos;
+ CRToken *token = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_prio
+ && *a_prio == NULL, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ if (status == CR_END_OF_INPUT_ERROR) {
+ goto error;
+ }
+ ENSURE_PARSING_COND (status == CR_OK
+ && token && token->type == IMPORTANT_SYM_TK);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ *a_prio = cr_string_new_from_string ("!important");
+ cr_token_destroy (token);
+ token = NULL;
+ return CR_OK;
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_declaration:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *@a_property: the successfully parsed property. The caller
+ * *must* free the returned pointer.
+ *@a_expr: the expression that represents the attribute value.
+ *The caller *must* free the returned pointer.
+ *
+ *TODO: return the parsed priority, so that
+ *upper layers can take benefit from it.
+ *Parses a "declaration" as defined by the css2 spec in appendix D.1:
+ *declaration ::= [property ':' S* expr prio?]?
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_declaration (CRParser * a_this,
+ CRString ** a_property,
+ CRTerm ** a_expr, gboolean * a_important)
+{
+ enum CRStatus status = CR_ERROR;
+ CRInputPos init_pos;
+ guint32 cur_char = 0;
+ CRTerm *expr = NULL;
+ CRString *prio = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_property && a_expr
+ && a_important, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_property (a_this, a_property);
+
+ if (status == CR_END_OF_INPUT_ERROR)
+ goto error;
+
+ CHECK_PARSING_STATUS_ERR
+ (a_this, status, FALSE,
+ (const guchar *) "while parsing declaration: next property is malformed",
+ CR_SYNTAX_ERROR);
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if (cur_char != ':') {
+ status = CR_PARSING_ERROR;
+ cr_parser_push_error
+ (a_this,
+ (const guchar *) "while parsing declaration: this char must be ':'",
+ CR_SYNTAX_ERROR);
+ goto error;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_expr (a_this, &expr);
+
+ CHECK_PARSING_STATUS_ERR
+ (a_this, status, FALSE,
+ (const guchar *) "while parsing declaration: next expression is malformed",
+ CR_SYNTAX_ERROR);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_parser_parse_prio (a_this, &prio);
+ if (prio) {
+ cr_string_destroy (prio);
+ prio = NULL;
+ *a_important = TRUE;
+ } else {
+ *a_important = FALSE;
+ }
+ if (*a_expr) {
+ cr_term_append_term (*a_expr, expr);
+ expr = NULL;
+ } else {
+ *a_expr = expr;
+ expr = NULL;
+ }
+
+ cr_parser_clear_errors (a_this);
+ return CR_OK;
+
+ error:
+
+ if (expr) {
+ cr_term_destroy (expr);
+ expr = NULL;
+ }
+
+ if (*a_property) {
+ cr_string_destroy (*a_property);
+ *a_property = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_statement_core:
+ *@a_this: the current instance of #CRParser.
+ *
+ *Parses a statement as defined by the css core grammar in
+ *chapter 4.1 of the css2 spec.
+ *statement : ruleset | at-rule;
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_statement_core (CRParser * a_this)
+{
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ switch (token->type) {
+ case ATKEYWORD_TK:
+ case IMPORT_SYM_TK:
+ case PAGE_SYM_TK:
+ case MEDIA_SYM_TK:
+ case FONT_FACE_SYM_TK:
+ case CHARSET_SYM_TK:
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ status = cr_parser_parse_atrule_core (a_this);
+ CHECK_PARSING_STATUS (status, TRUE);
+ break;
+
+ default:
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ status = cr_parser_parse_ruleset_core (a_this);
+ cr_parser_clear_errors (a_this);
+ CHECK_PARSING_STATUS (status, TRUE);
+ }
+
+ return CR_OK;
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_ruleset:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *
+ *Parses a "ruleset" as defined in the css2 spec at appendix D.1.
+ *ruleset ::= selector [ ',' S* selector ]*
+ *'{' S* declaration? [ ';' S* declaration? ]* '}' S*;
+ *
+ *This methods calls the the SAC handler on the relevant SAC handler
+ *callbacks whenever it encounters some specific constructions.
+ *See the documentation of #CRDocHandler (the SAC handler) to know
+ *when which SAC handler is called.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_ruleset (CRParser * a_this)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ guint32 cur_char = 0,
+ next_char = 0;
+ CRString *property = NULL;
+ CRTerm *expr = NULL;
+ CRSimpleSel *simple_sels = NULL;
+ CRSelector *selector = NULL;
+ gboolean start_selector = FALSE,
+ is_important = FALSE;
+ CRParsingLocation end_parsing_location;
+
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_parser_parse_selector (a_this, &selector);
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ ENSURE_PARSING_COND_ERR
+ (a_this, cur_char == '{',
+ (const guchar *) "while parsing rulset: current char should be '{'",
+ CR_SYNTAX_ERROR);
+
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->start_selector) {
+ /*
+ *the selector is ref counted so that the parser's user
+ *can choose to keep it.
+ */
+ if (selector) {
+ cr_selector_ref (selector);
+ }
+
+ PRIVATE (a_this)->sac_handler->start_selector
+ (PRIVATE (a_this)->sac_handler, selector);
+ start_selector = TRUE;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ PRIVATE (a_this)->state = TRY_PARSE_RULESET_STATE;
+
+ status = cr_parser_parse_declaration (a_this, &property,
+ &expr,
+ &is_important);
+ if (expr) {
+ cr_term_ref (expr);
+ }
+ if (status == CR_OK
+ && PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->property) {
+ PRIVATE (a_this)->sac_handler->property
+ (PRIVATE (a_this)->sac_handler, property, expr,
+ is_important);
+ }
+ if (status == CR_OK) {
+ /*
+ *free the allocated
+ *'property' and 'term' before parsing
+ *next declarations.
+ */
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (expr) {
+ cr_term_unref (expr);
+ expr = NULL;
+ }
+ } else {/*status != CR_OK*/
+ guint32 c = 0 ;
+ /*
+ *test if we have reached '}', which
+ *would mean that we are parsing an empty ruleset (eg. x{ })
+ *In that case, goto end_of_ruleset.
+ */
+ status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, &c) ;
+ if (status == CR_OK && c == '}') {
+ status = CR_OK ;
+ goto end_of_ruleset ;
+ }
+ }
+ CHECK_PARSING_STATUS_ERR
+ (a_this, status, FALSE,
+ (const guchar *) "while parsing ruleset: next construction should be a declaration",
+ CR_SYNTAX_ERROR);
+
+ for (;;) {
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (next_char != ';')
+ break;
+
+ /*consume the ';' char */
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_declaration (a_this, &property,
+ &expr, &is_important);
+
+ if (expr) {
+ cr_term_ref (expr);
+ }
+ if (status == CR_OK
+ && PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->property) {
+ PRIVATE (a_this)->sac_handler->property
+ (PRIVATE (a_this)->sac_handler,
+ property, expr, is_important);
+ }
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (expr) {
+ cr_term_unref (expr);
+ expr = NULL;
+ }
+ }
+
+ end_of_ruleset:
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ cr_parser_get_parsing_location (a_this, &end_parsing_location);
+ READ_NEXT_CHAR (a_this, &cur_char);
+ ENSURE_PARSING_COND_ERR
+ (a_this, cur_char == '}',
+ (const guchar *) "while parsing rulset: current char must be a '}'",
+ CR_SYNTAX_ERROR);
+
+ selector->location = end_parsing_location;
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->end_selector) {
+ PRIVATE (a_this)->sac_handler->end_selector
+ (PRIVATE (a_this)->sac_handler, selector);
+ start_selector = FALSE;
+ }
+
+ if (expr) {
+ cr_term_unref (expr);
+ expr = NULL;
+ }
+
+ if (simple_sels) {
+ cr_simple_sel_destroy (simple_sels);
+ simple_sels = NULL;
+ }
+
+ if (selector) {
+ cr_selector_unref (selector);
+ selector = NULL;
+ }
+
+ cr_parser_clear_errors (a_this);
+ PRIVATE (a_this)->state = RULESET_PARSED_STATE;
+
+ return CR_OK;
+
+ error:
+ if (start_selector == TRUE
+ && PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->error) {
+ PRIVATE (a_this)->sac_handler->error
+ (PRIVATE (a_this)->sac_handler);
+ }
+ if (expr) {
+ cr_term_unref (expr);
+ expr = NULL;
+ }
+ if (simple_sels) {
+ cr_simple_sel_destroy (simple_sels);
+ simple_sels = NULL;
+ }
+ if (property) {
+ cr_string_destroy (property);
+ }
+ if (selector) {
+ cr_selector_unref (selector);
+ selector = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_import:
+ *@a_this: the "this pointer" of the current instance
+ *of #CRParser.
+ *@a_media_list: out parameter. A linked list of
+ *#CRString
+ *Each CRString is a string that contains
+ *a 'medium' declaration part of the successfully
+ *parsed 'import' declaration.
+ *@a_import_string: out parameter.
+ *A string that contains the 'import
+ *string". The import string can be either an uri (if it starts with
+ *the substring "uri(") or a any other css2 string. Note that
+ * *a_import_string must be initially set to NULL or else, this function
+ *will return CR_BAD_PARAM_ERROR.
+ *@a_location: the location (line, column) where the import has been parsed
+ *
+ *Parses an 'import' declaration as defined in the css2 spec
+ *in appendix D.1:
+ *
+ *import ::=
+ *\@import [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_import (CRParser * a_this,
+ GList ** a_media_list,
+ CRString ** a_import_string,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ guint32 cur_char = 0,
+ next_char = 0;
+ CRString *medium = NULL;
+
+ g_return_val_if_fail (a_this
+ && a_import_string
+ && (*a_import_string == NULL),
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ if (BYTE (a_this, 1, NULL) == '@'
+ && BYTE (a_this, 2, NULL) == 'i'
+ && BYTE (a_this, 3, NULL) == 'm'
+ && BYTE (a_this, 4, NULL) == 'p'
+ && BYTE (a_this, 5, NULL) == 'o'
+ && BYTE (a_this, 6, NULL) == 'r'
+ && BYTE (a_this, 7, NULL) == 't') {
+ SKIP_CHARS (a_this, 1);
+ if (a_location) {
+ cr_parser_get_parsing_location
+ (a_this, a_location) ;
+ }
+ SKIP_CHARS (a_this, 6);
+ status = CR_OK;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ PRIVATE (a_this)->state = TRY_PARSE_IMPORT_STATE;
+
+ PEEK_NEXT_CHAR (a_this, &next_char);
+
+ if (next_char == '"' || next_char == '\'') {
+ status = cr_parser_parse_string (a_this, a_import_string);
+
+ CHECK_PARSING_STATUS (status, FALSE);
+ } else {
+ status = cr_parser_parse_uri (a_this, a_import_string);
+
+ CHECK_PARSING_STATUS (status, FALSE);
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_ident (a_this, &medium);
+
+ if (status == CR_OK && medium) {
+ *a_media_list = g_list_append (*a_media_list, medium);
+ medium = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ for (; status == CR_OK;) {
+ if ((status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr,
+ &next_char)) != CR_OK) {
+ if (status == CR_END_OF_INPUT_ERROR) {
+ status = CR_OK;
+ goto okay;
+ }
+ goto error;
+ }
+
+ if (next_char == ',') {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ } else {
+ break;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_ident (a_this, &medium);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ if ((status == CR_OK) && medium) {
+ *a_media_list = g_list_append (*a_media_list, medium);
+
+ medium = NULL;
+ }
+
+ CHECK_PARSING_STATUS (status, FALSE);
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ }
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ READ_NEXT_CHAR (a_this, &cur_char);
+ ENSURE_PARSING_COND (cur_char == ';');
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ okay:
+ cr_parser_clear_errors (a_this);
+ PRIVATE (a_this)->state = IMPORT_PARSED_STATE;
+
+ return CR_OK;
+
+ error:
+
+ if (*a_media_list) {
+ GList *cur = NULL;
+
+ /*
+ *free each element of *a_media_list.
+ *Note that each element of *a_medium list *must*
+ *be a GString* or else, the code that is coming next
+ *will corrupt the memory and lead to hard to debug
+ *random crashes.
+ *This is where C++ and its compile time
+ *type checking mechanism (through STL containers) would
+ *have prevented us to go through this hassle.
+ */
+ for (cur = *a_media_list; cur; cur = cur->next) {
+ if (cur->data) {
+ cr_string_destroy (cur->data);
+ }
+ }
+
+ g_list_free (*a_media_list);
+ *a_media_list = NULL;
+ }
+
+ if (*a_import_string) {
+ cr_string_destroy (*a_import_string);
+ *a_import_string = NULL;
+ }
+
+ if (medium) {
+ cr_string_destroy (medium);
+ medium = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_media:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *
+ *Parses a 'media' declaration as specified in the css2 spec at
+ *appendix D.1:
+ *
+ *media ::= \@media S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
+ *
+ *Note that this function calls the required sac handlers during the parsing
+ *to notify media productions. See #CRDocHandler to know the callback called
+ *during \@media parsing.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_media (CRParser * a_this)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ CRToken *token = NULL;
+ guint32 next_char = 0,
+ cur_char = 0;
+ CRString *medium = NULL;
+ GList *media_list = NULL;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this
+ && PRIVATE (a_this),
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token
+ && token->type == MEDIA_SYM_TK);
+ cr_parsing_location_copy (&location, &token->location) ;
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token && token->type == IDENT_TK);
+
+ medium = token->u.str;
+ token->u.str = NULL;
+ cr_token_destroy (token);
+ token = NULL;
+
+ if (medium) {
+ media_list = g_list_append (media_list, medium);
+ medium = NULL;
+ }
+
+ for (; status == CR_OK;) {
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ PEEK_NEXT_CHAR (a_this, &next_char);
+
+ if (next_char == ',') {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ } else {
+ break;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_ident (a_this, &medium);
+
+ CHECK_PARSING_STATUS (status, FALSE);
+
+ if (medium) {
+ media_list = g_list_append (media_list, medium);
+ medium = NULL;
+ }
+ }
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ ENSURE_PARSING_COND (cur_char == '{');
+
+ /*
+ *call the SAC handler api here.
+ */
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->start_media) {
+ PRIVATE (a_this)->sac_handler->start_media
+ (PRIVATE (a_this)->sac_handler, media_list,
+ &location);
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ PRIVATE (a_this)->state = TRY_PARSE_MEDIA_STATE;
+
+ for (; status == CR_OK;) {
+ status = cr_parser_parse_ruleset (a_this);
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ }
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ ENSURE_PARSING_COND (cur_char == '}');
+
+ /*
+ *call the right SAC handler api here.
+ */
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->end_media) {
+ PRIVATE (a_this)->sac_handler->end_media
+ (PRIVATE (a_this)->sac_handler, media_list);
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ /*
+ *Then, free the data structures passed to
+ *the last call to the SAC handler.
+ */
+ if (medium) {
+ cr_string_destroy (medium);
+ medium = NULL;
+ }
+
+ if (media_list) {
+ GList *cur = NULL;
+
+ for (cur = media_list; cur; cur = cur->next) {
+ cr_string_destroy (cur->data);
+ }
+
+ g_list_free (media_list);
+ media_list = NULL;
+ }
+
+ cr_parser_clear_errors (a_this);
+ PRIVATE (a_this)->state = MEDIA_PARSED_STATE;
+
+ return CR_OK;
+
+ error:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (medium) {
+ cr_string_destroy (medium);
+ medium = NULL;
+ }
+
+ if (media_list) {
+ GList *cur = NULL;
+
+ for (cur = media_list; cur; cur = cur->next) {
+ cr_string_destroy (cur->data);
+ }
+
+ g_list_free (media_list);
+ media_list = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_page:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *
+ *Parses '\@page' rule as specified in the css2 spec in appendix D.1:
+ *page ::= PAGE_SYM S* IDENT? pseudo_page? S*
+ *'{' S* declaration [ ';' S* declaration ]* '}' S*
+ *
+ *This function also calls the relevant SAC handlers whenever it
+ *encounters a construction that must
+ *be reported to the calling application.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_page (CRParser * a_this)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ CRToken *token = NULL;
+ CRTerm *css_expression = NULL;
+ CRString *page_selector = NULL,
+ *page_pseudo_class = NULL,
+ *property = NULL;
+ gboolean important = TRUE;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token) ;
+ ENSURE_PARSING_COND (status == CR_OK
+ && token
+ && token->type == PAGE_SYM_TK);
+
+ cr_parsing_location_copy (&location, &token->location) ;
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ if (token->type == IDENT_TK) {
+ page_selector = token->u.str;
+ token->u.str = NULL;
+ cr_token_destroy (token);
+ token = NULL;
+ } else {
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ }
+
+ /*
+ *try to parse pseudo_page
+ */
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ if (token->type == DELIM_TK && token->u.unichar == ':') {
+ cr_token_destroy (token);
+ token = NULL;
+ status = cr_parser_parse_ident (a_this, &page_pseudo_class);
+ CHECK_PARSING_STATUS (status, FALSE);
+ } else {
+ cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
+ token = NULL;
+ }
+
+ /*
+ *parse_block
+ *
+ */
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+
+ ENSURE_PARSING_COND (status == CR_OK && token
+ && token->type == CBO_TK);
+
+ cr_token_destroy (token);
+ token = NULL;
+
+ /*
+ *Call the appropriate SAC handler here.
+ */
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->start_page) {
+ PRIVATE (a_this)->sac_handler->start_page
+ (PRIVATE (a_this)->sac_handler,
+ page_selector, page_pseudo_class,
+ &location);
+ }
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ PRIVATE (a_this)->state = TRY_PARSE_PAGE_STATE;
+
+ status = cr_parser_parse_declaration (a_this, &property,
+ &css_expression,
+ &important);
+ ENSURE_PARSING_COND (status == CR_OK);
+
+ /*
+ *call the relevant SAC handler here...
+ */
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->property) {
+ if (css_expression)
+ cr_term_ref (css_expression);
+
+ PRIVATE (a_this)->sac_handler->property
+ (PRIVATE (a_this)->sac_handler,
+ property, css_expression, important);
+ }
+ /*
+ *... and free the data structure passed to that last
+ *SAC handler.
+ */
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (css_expression) {
+ cr_term_unref (css_expression);
+ css_expression = NULL;
+ }
+
+ for (;;) {
+ /*parse the other ';' separated declarations */
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+
+ ENSURE_PARSING_COND (status == CR_OK && token);
+
+ if (token->type != SEMICOLON_TK) {
+ cr_tknzr_unget_token
+ (PRIVATE (a_this)->tknzr,
+ token);
+ token = NULL ;
+ break;
+ }
+
+ cr_token_destroy (token);
+ token = NULL;
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_parser_parse_declaration (a_this, &property,
+ &css_expression,
+ &important);
+ if (status != CR_OK)
+ break ;
+
+ /*
+ *call the relevant SAC handler here...
+ */
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->property) {
+ cr_term_ref (css_expression);
+ PRIVATE (a_this)->sac_handler->property
+ (PRIVATE (a_this)->sac_handler,
+ property, css_expression, important);
+ }
+ /*
+ *... and free the data structure passed to that last
+ *SAC handler.
+ */
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (css_expression) {
+ cr_term_unref (css_expression);
+ css_expression = NULL;
+ }
+ }
+ cr_parser_try_to_skip_spaces_and_comments
+ (a_this) ;
+ if (token) {
+ cr_token_destroy (token) ;
+ token = NULL ;
+ }
+
+ status = cr_tknzr_get_next_token
+ (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token
+ && token->type == CBC_TK) ;
+ cr_token_destroy (token) ;
+ token = NULL ;
+ /*
+ *call the relevant SAC handler here.
+ */
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->end_page) {
+ PRIVATE (a_this)->sac_handler->end_page
+ (PRIVATE (a_this)->sac_handler,
+ page_selector, page_pseudo_class);
+ }
+
+ if (page_selector) {
+ cr_string_destroy (page_selector);
+ page_selector = NULL;
+ }
+
+ if (page_pseudo_class) {
+ cr_string_destroy (page_pseudo_class);
+ page_pseudo_class = NULL;
+ }
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ /*here goes the former implem of this function ... */
+
+ cr_parser_clear_errors (a_this);
+ PRIVATE (a_this)->state = PAGE_PARSED_STATE;
+
+ return CR_OK;
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ if (page_selector) {
+ cr_string_destroy (page_selector);
+ page_selector = NULL;
+ }
+ if (page_pseudo_class) {
+ cr_string_destroy (page_pseudo_class);
+ page_pseudo_class = NULL;
+ }
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (css_expression) {
+ cr_term_destroy (css_expression);
+ css_expression = NULL;
+ }
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+ return status;
+}
+
+/**
+ * cr_parser_parse_charset:
+ *@a_this: the "this pointer" of the current instance of #CRParser.
+ *@a_value: out parameter. The actual parsed value of the charset
+ *declararation. Note that for safety check reasons, *a_value must be
+ *set to NULL.
+ *@a_charset_sym_location: the parsing location of the charset rule
+ *
+ *Parses a charset declaration as defined implicitly by the css2 spec in
+ *appendix D.1:
+ *charset ::= CHARSET_SYM S* STRING S* ';'
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_charset (CRParser * a_this, CRString ** a_value,
+ CRParsingLocation *a_charset_sym_location)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ CRToken *token = NULL;
+ CRString *charset_str = NULL;
+
+ g_return_val_if_fail (a_this && a_value
+ && (*a_value == NULL),
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+
+ ENSURE_PARSING_COND (status == CR_OK
+ && token && token->type == CHARSET_SYM_TK);
+ if (a_charset_sym_location) {
+ cr_parsing_location_copy (a_charset_sym_location,
+ &token->location) ;
+ }
+ cr_token_destroy (token);
+ token = NULL;
+
+ PRIVATE (a_this)->state = TRY_PARSE_CHARSET_STATE;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token && token->type == STRING_TK);
+ charset_str = token->u.str;
+ token->u.str = NULL;
+ cr_token_destroy (token);
+ token = NULL;
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+
+ ENSURE_PARSING_COND (status == CR_OK
+ && token && token->type == SEMICOLON_TK);
+ cr_token_destroy (token);
+ token = NULL;
+
+ if (charset_str) {
+ *a_value = charset_str;
+ charset_str = NULL;
+ }
+
+ PRIVATE (a_this)->state = CHARSET_PARSED_STATE;
+ return CR_OK;
+
+ error:
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (*a_value) {
+ cr_string_destroy (*a_value);
+ *a_value = NULL;
+ }
+
+ if (charset_str) {
+ cr_string_destroy (charset_str);
+ charset_str = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+
+ return status;
+}
+
+/**
+ * cr_parser_parse_font_face:
+ *@a_this: the current instance of #CRParser.
+ *
+ *Parses the "\@font-face" rule specified in the css1 spec in
+ *appendix D.1:
+ *
+ *font_face ::= FONT_FACE_SYM S*
+ *'{' S* declaration [ ';' S* declaration ]* '}' S*
+ *
+ *This function will call SAC handlers whenever it is necessary.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_font_face (CRParser * a_this)
+{
+ enum CRStatus status = CR_ERROR;
+ CRInputPos init_pos;
+ CRString *property = NULL;
+ CRTerm *css_expression = NULL;
+ CRToken *token = NULL;
+ gboolean important = FALSE;
+ guint32 next_char = 0,
+ cur_char = 0;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
+ ENSURE_PARSING_COND (status == CR_OK
+ && token
+ && token->type == FONT_FACE_SYM_TK);
+
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ if (token) {
+ cr_parsing_location_copy (&location,
+ &token->location) ;
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
+ &token);
+ ENSURE_PARSING_COND (status == CR_OK && token
+ && token->type == CBO_TK);
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ /*
+ *here, call the relevant SAC handler.
+ */
+ if (PRIVATE (a_this)->sac_handler
+ && PRIVATE (a_this)->sac_handler->start_font_face) {
+ PRIVATE (a_this)->sac_handler->start_font_face
+ (PRIVATE (a_this)->sac_handler, &location);
+ }
+ PRIVATE (a_this)->state = TRY_PARSE_FONT_FACE_STATE;
+ /*
+ *and resume the parsing.
+ */
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_parser_parse_declaration (a_this, &property,
+ &css_expression, &important);
+ if (status == CR_OK) {
+ /*
+ *here, call the relevant SAC handler.
+ */
+ cr_term_ref (css_expression);
+ if (PRIVATE (a_this)->sac_handler &&
+ PRIVATE (a_this)->sac_handler->property) {
+ PRIVATE (a_this)->sac_handler->property
+ (PRIVATE (a_this)->sac_handler,
+ property, css_expression, important);
+ }
+ ENSURE_PARSING_COND (css_expression && property);
+ }
+ /*free the data structures allocated during last parsing. */
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (css_expression) {
+ cr_term_unref (css_expression);
+ css_expression = NULL;
+ }
+ for (;;) {
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (next_char == ';') {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ } else {
+ break;
+ }
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ status = cr_parser_parse_declaration (a_this,
+ &property,
+ &css_expression,
+ &important);
+ if (status != CR_OK)
+ break;
+ /*
+ *here, call the relevant SAC handler.
+ */
+ cr_term_ref (css_expression);
+ if (PRIVATE (a_this)->sac_handler->property) {
+ PRIVATE (a_this)->sac_handler->property
+ (PRIVATE (a_this)->sac_handler,
+ property, css_expression, important);
+ }
+ /*
+ *Then, free the data structures allocated during
+ *last parsing.
+ */
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (css_expression) {
+ cr_term_unref (css_expression);
+ css_expression = NULL;
+ }
+ }
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+ READ_NEXT_CHAR (a_this, &cur_char);
+ ENSURE_PARSING_COND (cur_char == '}');
+ /*
+ *here, call the relevant SAC handler.
+ */
+ if (PRIVATE (a_this)->sac_handler->end_font_face) {
+ PRIVATE (a_this)->sac_handler->end_font_face
+ (PRIVATE (a_this)->sac_handler);
+ }
+ cr_parser_try_to_skip_spaces_and_comments (a_this);
+
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ cr_parser_clear_errors (a_this);
+ PRIVATE (a_this)->state = FONT_FACE_PARSED_STATE;
+ return CR_OK;
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+ if (property) {
+ cr_string_destroy (property);
+ property = NULL;
+ }
+ if (css_expression) {
+ cr_term_destroy (css_expression);
+ css_expression = NULL;
+ }
+ cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
+ return status;
+}
+
+/**
+ * cr_parser_parse:
+ *@a_this: the current instance of #CRParser.
+ *
+ *Parses the data that comes from the
+ *input previously associated to the current instance of
+ *#CRParser.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse (CRParser * a_this)
+{
+ enum CRStatus status = CR_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->use_core_grammar == FALSE) {
+ status = cr_parser_parse_stylesheet (a_this);
+ } else {
+ status = cr_parser_parse_stylesheet_core (a_this);
+ }
+
+ return status;
+}
+
+/**
+ * cr_parser_set_tknzr:
+ * @a_this: the current instance of #CRParser;
+ * @a_tknzr: the new tokenizer.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_set_tknzr (CRParser * a_this, CRTknzr * a_tknzr)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->tknzr) {
+ cr_tknzr_unref (PRIVATE (a_this)->tknzr);
+ }
+
+ PRIVATE (a_this)->tknzr = a_tknzr;
+
+ if (a_tknzr)
+ cr_tknzr_ref (a_tknzr);
+
+ return CR_OK;
+}
+
+/**
+ * cr_parser_get_tknzr:
+ *@a_this: the current instance of #CRParser
+ *@a_tknzr: out parameter. The returned tokenizer
+ *
+ *Getter of the parser's underlying tokenizer
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise
+ */
+enum CRStatus
+cr_parser_get_tknzr (CRParser * a_this, CRTknzr ** a_tknzr)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_tknzr, CR_BAD_PARAM_ERROR);
+
+ *a_tknzr = PRIVATE (a_this)->tknzr;
+ return CR_OK;
+}
+
+/**
+ * cr_parser_get_parsing_location:
+ *@a_this: the current instance of #CRParser
+ *@a_loc: the parsing location to get.
+ *
+ *Gets the current parsing location.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_parser_get_parsing_location (CRParser const *a_this,
+ CRParsingLocation *a_loc)
+{
+ g_return_val_if_fail (a_this
+ && PRIVATE (a_this)
+ && a_loc, CR_BAD_PARAM_ERROR) ;
+
+ return cr_tknzr_get_parsing_location
+ (PRIVATE (a_this)->tknzr, a_loc) ;
+}
+
+/**
+ * cr_parser_parse_buf:
+ *@a_this: the current instance of #CRparser
+ *@a_buf: the input buffer
+ *@a_len: the length of the input buffer
+ *@a_enc: the encoding of the buffer
+ *
+ *Parses a stylesheet from a buffer
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parser_parse_buf (CRParser * a_this,
+ const guchar * a_buf,
+ gulong a_len, enum CREncoding a_enc)
+{
+ enum CRStatus status = CR_ERROR;
+ CRTknzr *tknzr = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_buf, CR_BAD_PARAM_ERROR);
+
+ tknzr = cr_tknzr_new_from_buf ((guchar*)a_buf, a_len, a_enc, FALSE);
+
+ g_return_val_if_fail (tknzr != NULL, CR_ERROR);
+
+ status = cr_parser_set_tknzr (a_this, tknzr);
+ g_return_val_if_fail (status == CR_OK, CR_ERROR);
+
+ status = cr_parser_parse (a_this);
+
+ return status;
+}
+
+/**
+ * cr_parser_destroy:
+ *@a_this: the current instance of #CRParser to
+ *destroy.
+ *
+ *Destroys the current instance
+ *of #CRParser.
+ */
+void
+cr_parser_destroy (CRParser * a_this)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this));
+
+ if (PRIVATE (a_this)->tknzr) {
+ if (cr_tknzr_unref (PRIVATE (a_this)->tknzr) == TRUE)
+ PRIVATE (a_this)->tknzr = NULL;
+ }
+
+ if (PRIVATE (a_this)->sac_handler) {
+ cr_doc_handler_unref (PRIVATE (a_this)->sac_handler);
+ PRIVATE (a_this)->sac_handler = NULL;
+ }
+
+ if (PRIVATE (a_this)->err_stack) {
+ cr_parser_clear_errors (a_this);
+ PRIVATE (a_this)->err_stack = NULL;
+ }
+
+ if (PRIVATE (a_this)) {
+ g_free (PRIVATE (a_this));
+ PRIVATE (a_this) = NULL;
+ }
+
+ if (a_this) {
+ g_free (a_this);
+ a_this = NULL; /*useless. Just for the sake of coherence */
+ }
+}
diff --git a/src/st/croco/cr-parser.h b/src/st/croco/cr-parser.h
new file mode 100644
index 0000000..6dce943
--- /dev/null
+++ b/src/st/croco/cr-parser.h
@@ -0,0 +1,128 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyrights information.
+ */
+
+#ifndef __CR_PARSER_H__
+#define __CR_PARSER_H__
+
+#include <glib.h>
+#include "cr-input.h"
+#include "cr-tknzr.h"
+#include "cr-utils.h"
+#include "cr-doc-handler.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *The declaration file
+ *of the #CRParser class.
+ */
+typedef struct _CRParser CRParser ;
+typedef struct _CRParserPriv CRParserPriv ;
+
+
+/**
+ *The implementation of
+ *the SAC parser.
+ *The Class is opaque
+ *and must be manipulated through
+ *the provided methods.
+ */
+struct _CRParser {
+ CRParserPriv *priv ;
+} ;
+
+
+CRParser * cr_parser_new (CRTknzr *a_tknzr) ;
+
+CRParser * cr_parser_new_from_buf (guchar *a_buf, gulong a_len,
+ enum CREncoding a_enc,
+ gboolean a_free_buf) ;
+
+CRParser * cr_parser_new_from_file (const guchar *a_file_uri,
+ enum CREncoding a_enc) ;
+
+CRParser * cr_parser_new_from_input (CRInput *a_input) ;
+
+enum CRStatus cr_parser_set_tknzr (CRParser *a_this, CRTknzr *a_tknzr) ;
+
+enum CRStatus cr_parser_get_tknzr (CRParser *a_this, CRTknzr **a_tknzr) ;
+
+enum CRStatus cr_parser_get_parsing_location (CRParser const *a_this, CRParsingLocation *a_loc) ;
+
+enum CRStatus cr_parser_try_to_skip_spaces_and_comments (CRParser *a_this) ;
+
+
+enum CRStatus cr_parser_set_sac_handler (CRParser *a_this,
+ CRDocHandler *a_handler) ;
+
+enum CRStatus cr_parser_get_sac_handler (CRParser *a_this,
+ CRDocHandler **a_handler) ;
+
+enum CRStatus cr_parser_set_use_core_grammar (CRParser *a_this,
+ gboolean a_use_core_grammar) ;
+enum CRStatus cr_parser_get_use_core_grammar (CRParser const *a_this,
+ gboolean *a_use_core_grammar) ;
+
+enum CRStatus cr_parser_parse (CRParser *a_this) ;
+
+enum CRStatus cr_parser_parse_file (CRParser *a_this,
+ const guchar *a_file_uri,
+ enum CREncoding a_enc) ;
+
+enum CRStatus cr_parser_parse_buf (CRParser *a_this, const guchar *a_buf,
+ gulong a_len, enum CREncoding a_enc) ;
+
+enum CRStatus cr_parser_set_default_sac_handler (CRParser *a_this) ;
+
+enum CRStatus cr_parser_parse_term (CRParser *a_this, CRTerm **a_term) ;
+
+enum CRStatus cr_parser_parse_expr (CRParser *a_this, CRTerm **a_expr) ;
+
+enum CRStatus cr_parser_parse_prio (CRParser *a_this, CRString **a_prio) ;
+
+enum CRStatus cr_parser_parse_declaration (CRParser *a_this, CRString **a_property,
+ CRTerm **a_expr, gboolean *a_important) ;
+
+enum CRStatus cr_parser_parse_statement_core (CRParser *a_this) ;
+
+enum CRStatus cr_parser_parse_ruleset (CRParser *a_this) ;
+
+enum CRStatus cr_parser_parse_import (CRParser *a_this, GList ** a_media_list,
+ CRString **a_import_string,
+ CRParsingLocation *a_location) ;
+
+enum CRStatus cr_parser_parse_media (CRParser *a_this) ;
+
+enum CRStatus cr_parser_parse_page (CRParser *a_this) ;
+
+enum CRStatus cr_parser_parse_charset (CRParser *a_this, CRString **a_value,
+ CRParsingLocation *a_charset_sym_location) ;
+
+enum CRStatus cr_parser_parse_font_face (CRParser *a_this) ;
+
+void cr_parser_destroy (CRParser *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_PARSER_H__*/
diff --git a/src/st/croco/cr-parsing-location.c b/src/st/croco/cr-parsing-location.c
new file mode 100644
index 0000000..2b40974
--- /dev/null
+++ b/src/st/croco/cr-parsing-location.c
@@ -0,0 +1,171 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli.
+ * See the COPYRIGHTS file for copyright information.
+ */
+
+#include <string.h>
+#include "cr-parsing-location.h"
+
+/**
+ *@CRParsingLocation:
+ *
+ *Definition of the #CRparsingLocation class.
+ */
+
+
+/**
+ * cr_parsing_location_new:
+ *Instantiates a new parsing location.
+ *
+ *Returns the newly instantiated #CRParsingLocation.
+ *Must be freed by cr_parsing_location_destroy()
+ */
+CRParsingLocation *
+cr_parsing_location_new (void)
+{
+ CRParsingLocation * result = NULL ;
+
+ result = g_try_malloc (sizeof (CRParsingLocation)) ;
+ if (!result) {
+ cr_utils_trace_info ("Out of memory error") ;
+ return NULL ;
+ }
+ cr_parsing_location_init (result) ;
+ return result ;
+}
+
+/**
+ * cr_parsing_location_init:
+ *@a_this: the current instance of #CRParsingLocation.
+ *
+ *Initializes the an instance of #CRparsingLocation.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_parsing_location_init (CRParsingLocation *a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ;
+
+ memset (a_this, 0, sizeof (CRParsingLocation)) ;
+ return CR_OK ;
+}
+
+/**
+ * cr_parsing_location_copy:
+ *@a_to: the destination of the copy.
+ *Must be allocated by the caller.
+ *@a_from: the source of the copy.
+ *
+ *Copies an instance of CRParsingLocation into another one.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_parsing_location_copy (CRParsingLocation *a_to,
+ CRParsingLocation const *a_from)
+{
+ g_return_val_if_fail (a_to && a_from, CR_BAD_PARAM_ERROR) ;
+
+ memcpy (a_to, a_from, sizeof (CRParsingLocation)) ;
+ return CR_OK ;
+}
+
+/**
+ * cr_parsing_location_to_string:
+ *@a_this: the current instance of #CRParsingLocation.
+ *@a_mask: a bitmap that defines which parts of the
+ *parsing location are to be serialized (line, column or byte offset)
+ *
+ *Returns the serialized string or NULL in case of an error.
+ */
+gchar *
+cr_parsing_location_to_string (CRParsingLocation const *a_this,
+ enum CRParsingLocationSerialisationMask a_mask)
+{
+ GString *result = NULL ;
+ gchar *str = NULL ;
+
+ g_return_val_if_fail (a_this, NULL) ;
+
+ if (!a_mask) {
+ a_mask = DUMP_LINE | DUMP_COLUMN | DUMP_BYTE_OFFSET ;
+ }
+ result =g_string_new (NULL) ;
+ if (!result)
+ return NULL ;
+ if (a_mask & DUMP_LINE) {
+ g_string_append_printf (result, "line:%d ",
+ a_this->line) ;
+ }
+ if (a_mask & DUMP_COLUMN) {
+ g_string_append_printf (result, "column:%d ",
+ a_this->column) ;
+ }
+ if (a_mask & DUMP_BYTE_OFFSET) {
+ g_string_append_printf (result, "byte offset:%d ",
+ a_this->byte_offset) ;
+ }
+ if (result->len) {
+ str = g_string_free (result, FALSE) ;
+ } else {
+ g_string_free (result, TRUE) ;
+ }
+ return str ;
+}
+
+/**
+ * cr_parsing_location_dump:
+ * @a_this: current instance of #CRParsingLocation
+ * @a_mask: the serialization mask.
+ * @a_fp: the file pointer to dump the parsing location to.
+ */
+void
+cr_parsing_location_dump (CRParsingLocation const *a_this,
+ enum CRParsingLocationSerialisationMask a_mask,
+ FILE *a_fp)
+{
+ gchar *str = NULL ;
+
+ g_return_if_fail (a_this && a_fp) ;
+ str = cr_parsing_location_to_string (a_this, a_mask) ;
+ if (str) {
+ fprintf (a_fp, "%s", str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+/**
+ * cr_parsing_location_destroy:
+ *@a_this: the current instance of #CRParsingLocation. Must
+ *have been allocated with cr_parsing_location_new().
+ *
+ *Destroys the current instance of #CRParsingLocation
+ */
+void
+cr_parsing_location_destroy (CRParsingLocation *a_this)
+{
+ g_return_if_fail (a_this) ;
+ g_free (a_this) ;
+}
+
diff --git a/src/st/croco/cr-parsing-location.h b/src/st/croco/cr-parsing-location.h
new file mode 100644
index 0000000..b8064a5
--- /dev/null
+++ b/src/st/croco/cr-parsing-location.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli.
+ * See the COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_PARSING_LOCATION_H__
+#define __CR_PARSING_LOCATION_H__
+
+#include "cr-utils.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *The declaration of the CRParsingLocation
+ *object. This object keeps track of line/column/byte offset/
+ *at which the parsing of a given CSS construction appears.
+ */
+
+typedef struct _CRParsingLocation CRParsingLocation;
+struct _CRParsingLocation {
+ guint line ;
+ guint column ;
+ guint byte_offset ;
+} ;
+
+
+enum CRParsingLocationSerialisationMask {
+ DUMP_LINE = 1,
+ DUMP_COLUMN = 1 << 1,
+ DUMP_BYTE_OFFSET = 1 << 2
+} ;
+
+CRParsingLocation * cr_parsing_location_new (void) ;
+
+enum CRStatus cr_parsing_location_init (CRParsingLocation *a_this) ;
+
+enum CRStatus cr_parsing_location_copy (CRParsingLocation *a_to,
+ CRParsingLocation const *a_from) ;
+
+gchar * cr_parsing_location_to_string (CRParsingLocation const *a_this,
+ enum CRParsingLocationSerialisationMask a_mask) ;
+void cr_parsing_location_dump (CRParsingLocation const *a_this,
+ enum CRParsingLocationSerialisationMask a_mask,
+ FILE *a_fp) ;
+
+void cr_parsing_location_destroy (CRParsingLocation *a_this) ;
+
+
+
+G_END_DECLS
+#endif
diff --git a/src/st/croco/cr-prop-list.c b/src/st/croco/cr-prop-list.c
new file mode 100644
index 0000000..03c4478
--- /dev/null
+++ b/src/st/croco/cr-prop-list.c
@@ -0,0 +1,404 @@
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyrights information.
+ */
+
+#include <string.h>
+#include "cr-prop-list.h"
+
+#define PRIVATE(a_obj) (a_obj)->priv
+
+struct _CRPropListPriv {
+ CRString *prop;
+ CRDeclaration *decl;
+ CRPropList *next;
+ CRPropList *prev;
+};
+
+static CRPropList *cr_prop_list_allocate (void);
+
+/**
+ *Default allocator of CRPropList
+ *@return the newly allocated CRPropList or NULL
+ *if an error arises.
+ */
+static CRPropList *
+cr_prop_list_allocate (void)
+{
+ CRPropList *result = NULL;
+
+ result = g_try_malloc (sizeof (CRPropList));
+ if (!result) {
+ cr_utils_trace_info ("could not allocate CRPropList");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRPropList));
+ PRIVATE (result) = g_try_malloc (sizeof (CRPropListPriv));
+ if (!result) {
+ cr_utils_trace_info ("could not allocate CRPropListPriv");
+ g_free (result);
+ return NULL;
+ }
+ memset (PRIVATE (result), 0, sizeof (CRPropListPriv));
+ return result;
+}
+
+/****************
+ *public methods
+ ***************/
+
+/**
+ * cr_prop_list_append:
+ *@a_this: the current instance of #CRPropList
+ *@a_to_append: the property list to append
+ *
+ *Appends a property list to the current one.
+ *
+ *Returns the resulting prop list, or NULL if an error
+ *occurred
+ */
+CRPropList *
+cr_prop_list_append (CRPropList * a_this, CRPropList * a_to_append)
+{
+ CRPropList *cur = NULL;
+
+ g_return_val_if_fail (a_to_append, NULL);
+
+ if (!a_this)
+ return a_to_append;
+
+ /*go fetch the last element of the list */
+ for (cur = a_this;
+ cur && PRIVATE (cur) && PRIVATE (cur)->next;
+ cur = PRIVATE (cur)->next) ;
+ g_return_val_if_fail (cur, NULL);
+ PRIVATE (cur)->next = a_to_append;
+ PRIVATE (a_to_append)->prev = cur;
+ return a_this;
+}
+
+/**
+ * cr_prop_list_append2:
+ *Appends a pair of prop/declaration to
+ *the current prop list.
+ *@a_this: the current instance of #CRPropList
+ *@a_prop: the property to consider
+ *@a_decl: the declaration to consider
+ *
+ *Returns the resulting property list, or NULL in case
+ *of an error.
+ */
+CRPropList *
+cr_prop_list_append2 (CRPropList * a_this,
+ CRString * a_prop,
+ CRDeclaration * a_decl)
+{
+ CRPropList *list = NULL,
+ *result = NULL;
+
+ g_return_val_if_fail (a_prop && a_decl, NULL);
+
+ list = cr_prop_list_allocate ();
+ g_return_val_if_fail (list && PRIVATE (list), NULL);
+
+ PRIVATE (list)->prop = a_prop;
+ PRIVATE (list)->decl = a_decl;
+
+ result = cr_prop_list_append (a_this, list);
+ return result;
+}
+
+/**
+ * cr_prop_list_prepend:
+ *@a_this: the current instance of #CRPropList
+ *@a_to_prepend: the new list to prepend.
+ *
+ *Prepends a list to the current list
+ *Returns the new properties list.
+ */
+CRPropList *
+cr_prop_list_prepend (CRPropList * a_this, CRPropList * a_to_prepend)
+{
+ CRPropList *cur = NULL;
+
+ g_return_val_if_fail (a_to_prepend, NULL);
+
+ if (!a_this)
+ return a_to_prepend;
+
+ for (cur = a_to_prepend; cur && PRIVATE (cur)->next;
+ cur = PRIVATE (cur)->next) ;
+ g_return_val_if_fail (cur, NULL);
+ PRIVATE (cur)->next = a_this;
+ PRIVATE (a_this)->prev = cur;
+ return a_to_prepend;
+}
+
+/**
+ * cr_prop_list_prepend2:
+ *@a_this: the current instance of #CRPropList
+ *@a_prop_name: property name to append
+ *@a_decl: the property value to append.
+ *
+ *Prepends a property to a list of properties
+ *
+ *Returns the new property list.
+ */
+CRPropList *
+cr_prop_list_prepend2 (CRPropList * a_this,
+ CRString * a_prop_name, CRDeclaration * a_decl)
+{
+ CRPropList *list = NULL,
+ *result = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_prop_name && a_decl, NULL);
+
+ list = cr_prop_list_allocate ();
+ g_return_val_if_fail (list, NULL);
+ PRIVATE (list)->prop = a_prop_name;
+ PRIVATE (list)->decl = a_decl;
+ result = cr_prop_list_prepend (a_this, list);
+ return result;
+}
+
+/**
+ * cr_prop_list_set_prop:
+ *@a_this: the current instance of #CRPropList
+ *@a_prop: the property to set
+ *
+ *Sets the property of a CRPropList
+ */
+enum CRStatus
+cr_prop_list_set_prop (CRPropList * a_this, CRString * a_prop)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_prop, CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->prop = a_prop;
+ return CR_OK;
+}
+
+/**
+ * cr_prop_list_get_prop:
+ *@a_this: the current instance of #CRPropList
+ *@a_prop: out parameter. The returned property
+ *
+ *Getter of the property associated to the current instance
+ *of #CRPropList
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_prop_list_get_prop (CRPropList const * a_this, CRString ** a_prop)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_prop, CR_BAD_PARAM_ERROR);
+
+ *a_prop = PRIVATE (a_this)->prop;
+ return CR_OK;
+}
+
+/**
+ * cr_prop_list_set_decl:
+ * @a_this: the current instance of #CRPropList
+ * @a_decl: the new property value.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_prop_list_set_decl (CRPropList * a_this, CRDeclaration * a_decl)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_decl, CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->decl = a_decl;
+ return CR_OK;
+}
+
+/**
+ * cr_prop_list_get_decl:
+ * @a_this: the current instance of #CRPropList
+ * @a_decl: out parameter. The property value
+ *
+ * Returns CR_OK upon successful completion.
+ */
+enum CRStatus
+cr_prop_list_get_decl (CRPropList const * a_this, CRDeclaration ** a_decl)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_decl, CR_BAD_PARAM_ERROR);
+
+ *a_decl = PRIVATE (a_this)->decl;
+ return CR_OK;
+}
+
+/**
+ * cr_prop_list_lookup_prop:
+ *@a_this: the current instance of #CRPropList
+ *@a_prop: the property to lookup
+ *@a_prop_list: out parameter. The property/declaration
+ *pair found (if and only if the function returned code if CR_OK)
+ *
+ *Lookup a given property/declaration pair
+ *
+ *Returns CR_OK if a prop/decl pair has been found,
+ *CR_VALUE_NOT_FOUND_ERROR if not, or an error code if something
+ *bad happens.
+ */
+enum CRStatus
+cr_prop_list_lookup_prop (CRPropList * a_this,
+ CRString * a_prop, CRPropList ** a_pair)
+{
+ CRPropList *cur = NULL;
+
+ g_return_val_if_fail (a_prop && a_pair, CR_BAD_PARAM_ERROR);
+
+ if (!a_this)
+ return CR_VALUE_NOT_FOUND_ERROR;
+
+ g_return_val_if_fail (PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ for (cur = a_this; cur; cur = PRIVATE (cur)->next) {
+ if (PRIVATE (cur)->prop
+ && PRIVATE (cur)->prop->stryng
+ && PRIVATE (cur)->prop->stryng->str
+ && a_prop->stryng
+ && a_prop->stryng->str
+ && !strcmp (PRIVATE (cur)->prop->stryng->str,
+ a_prop->stryng->str))
+ break;
+ }
+
+ if (cur) {
+ *a_pair = cur;
+ return CR_OK;
+ }
+
+ return CR_VALUE_NOT_FOUND_ERROR;
+}
+
+/**
+ * cr_prop_list_get_next:
+ *@a_this: the current instance of CRPropList
+ *
+ *Gets the next prop/decl pair in the list
+ *
+ *Returns the next prop/declaration pair of the list,
+ *or NULL if we reached end of list (or if an error occurs)
+ */
+CRPropList *
+cr_prop_list_get_next (CRPropList * a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), NULL);
+
+ return PRIVATE (a_this)->next;
+}
+
+/**
+ * cr_prop_list_get_prev:
+ *@a_this: the current instance of CRPropList
+ *
+ *Gets the previous prop/decl pair in the list
+ *
+ *Returns the previous prop/declaration pair of the list,
+ *or NULL if we reached end of list (or if an error occurs)
+ */
+CRPropList *
+cr_prop_list_get_prev (CRPropList * a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), NULL);
+
+ return PRIVATE (a_this)->prev;
+}
+
+/**
+ * cr_prop_list_unlink:
+ *@a_this: the current list of prop/decl pairs
+ *@a_pair: the prop/decl pair to unlink.
+ *
+ *Unlinks a prop/decl pair from the list
+ *
+ *Returns the new list or NULL in case of an error.
+ */
+CRPropList *
+cr_prop_list_unlink (CRPropList * a_this, CRPropList * a_pair)
+{
+ CRPropList *prev = NULL,
+ *next = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_pair, NULL);
+
+ /*some sanity checks */
+ if (PRIVATE (a_pair)->next) {
+ next = PRIVATE (a_pair)->next;
+ g_return_val_if_fail (PRIVATE (next), NULL);
+ g_return_val_if_fail (PRIVATE (next)->prev == a_pair, NULL);
+ }
+ if (PRIVATE (a_pair)->prev) {
+ prev = PRIVATE (a_pair)->prev;
+ g_return_val_if_fail (PRIVATE (prev), NULL);
+ g_return_val_if_fail (PRIVATE (prev)->next == a_pair, NULL);
+ }
+ if (prev) {
+ PRIVATE (prev)->next = next;
+ }
+ if (next) {
+ PRIVATE (next)->prev = prev;
+ }
+ PRIVATE (a_pair)->prev = PRIVATE (a_pair)->next = NULL;
+ if (a_this == a_pair) {
+ if (next)
+ return next;
+ return NULL;
+ }
+ return a_this;
+}
+
+/**
+ * cr_prop_list_destroy:
+ * @a_this: the current instance of #CRPropList
+ */
+void
+cr_prop_list_destroy (CRPropList * a_this)
+{
+ CRPropList *tail = NULL,
+ *cur = NULL;
+
+ g_return_if_fail (a_this && PRIVATE (a_this));
+
+ for (tail = a_this;
+ tail && PRIVATE (tail) && PRIVATE (tail)->next;
+ tail = cr_prop_list_get_next (tail)) ;
+ g_return_if_fail (tail);
+
+ cur = tail;
+
+ while (cur) {
+ tail = PRIVATE (cur)->prev;
+ if (tail && PRIVATE (tail))
+ PRIVATE (tail)->next = NULL;
+ PRIVATE (cur)->prev = NULL;
+ g_free (PRIVATE (cur));
+ PRIVATE (cur) = NULL;
+ g_free (cur);
+ cur = tail;
+ }
+}
diff --git a/src/st/croco/cr-prop-list.h b/src/st/croco/cr-prop-list.h
new file mode 100644
index 0000000..797ba43
--- /dev/null
+++ b/src/st/croco/cr-prop-list.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyrights information.
+ */
+
+#ifndef __CR_PROP_LIST_H__
+#define __CR_PROP_LIST_H__
+
+#include "cr-utils.h"
+#include "cr-declaration.h"
+#include "cr-string.h"
+
+G_BEGIN_DECLS
+
+typedef struct _CRPropList CRPropList ;
+typedef struct _CRPropListPriv CRPropListPriv ;
+
+struct _CRPropList
+{
+ CRPropListPriv * priv;
+} ;
+
+CRPropList * cr_prop_list_append (CRPropList *a_this,
+ CRPropList *a_to_append) ;
+
+CRPropList * cr_prop_list_append2 (CRPropList *a_this,
+ CRString *a_prop,
+ CRDeclaration *a_decl) ;
+
+CRPropList * cr_prop_list_prepend (CRPropList *a_this,
+ CRPropList *a_to_append) ;
+
+CRPropList * cr_prop_list_prepend2 (CRPropList *a_this,
+ CRString *a_prop,
+ CRDeclaration *a_decl) ;
+
+enum CRStatus cr_prop_list_set_prop (CRPropList *a_this,
+ CRString *a_prop) ;
+
+enum CRStatus cr_prop_list_get_prop (CRPropList const *a_this,
+ CRString **a_prop) ;
+
+enum CRStatus cr_prop_list_lookup_prop (CRPropList *a_this,
+ CRString *a_prop,
+ CRPropList**a_pair) ;
+
+CRPropList * cr_prop_list_get_next (CRPropList *a_this) ;
+
+CRPropList * cr_prop_list_get_prev (CRPropList *a_this) ;
+
+enum CRStatus cr_prop_list_set_decl (CRPropList *a_this,
+ CRDeclaration *a_decl);
+
+enum CRStatus cr_prop_list_get_decl (CRPropList const *a_this,
+ CRDeclaration **a_decl) ;
+
+CRPropList * cr_prop_list_unlink (CRPropList *a_this,
+ CRPropList *a_pair) ;
+
+void cr_prop_list_destroy (CRPropList *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_PROP_LIST_H__*/
diff --git a/src/st/croco/cr-pseudo.c b/src/st/croco/cr-pseudo.c
new file mode 100644
index 0000000..f81f9a6
--- /dev/null
+++ b/src/st/croco/cr-pseudo.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include "cr-pseudo.h"
+
+/**
+ *@CRPseudo:
+ *The definition of the #CRPseudo class.
+ */
+
+/**
+ * cr_pseudo_new:
+ *Constructor of the #CRPseudo class.
+ *
+ *Returns the newly build instance.
+ */
+CRPseudo *
+cr_pseudo_new (void)
+{
+ CRPseudo *result = NULL;
+
+ result = g_malloc0 (sizeof (CRPseudo));
+
+ return result;
+}
+
+/**
+ * cr_pseudo_to_string:
+ * @a_this: the current instance of #CRPseud.
+ *
+ * Returns the serialized pseudo. Caller must free the returned
+ * string using g_free().
+ */
+guchar *
+cr_pseudo_to_string (CRPseudo const * a_this)
+{
+ guchar *result = NULL;
+ GString *str_buf = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ str_buf = g_string_new (NULL);
+
+ if (a_this->type == IDENT_PSEUDO) {
+ guchar *name = NULL;
+
+ if (a_this->name == NULL) {
+ goto error;
+ }
+
+ name = (guchar *) g_strndup (a_this->name->stryng->str,
+ a_this->name->stryng->len);
+
+ if (name) {
+ g_string_append (str_buf, (const gchar *) name);
+ g_free (name);
+ name = NULL;
+ }
+ } else if (a_this->type == FUNCTION_PSEUDO) {
+ guchar *name = NULL,
+ *arg = NULL;
+
+ if (a_this->name == NULL)
+ goto error;
+
+ name = (guchar *) g_strndup (a_this->name->stryng->str,
+ a_this->name->stryng->len);
+
+ if (a_this->extra) {
+ arg = (guchar *) g_strndup (a_this->extra->stryng->str,
+ a_this->extra->stryng->len);
+ }
+
+ if (name) {
+ g_string_append_printf (str_buf, "%s(", name);
+ g_free (name);
+ name = NULL;
+
+ if (arg) {
+ g_string_append (str_buf, (const gchar *) arg);
+ g_free (arg);
+ arg = NULL;
+ }
+
+ g_string_append_c (str_buf, ')');
+ }
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+
+ error:
+ g_string_free (str_buf, TRUE);
+ return NULL;
+}
+
+/**
+ * cr_pseudo_dump:
+ *@a_this: the current instance of pseudo
+ *@a_fp: the destination file pointer.
+ *
+ *Dumps the pseudo to a file.
+ *
+ */
+void
+cr_pseudo_dump (CRPseudo const * a_this, FILE * a_fp)
+{
+ guchar *tmp_str = NULL;
+
+ if (a_this) {
+ tmp_str = cr_pseudo_to_string (a_this);
+ if (tmp_str) {
+ fprintf (a_fp, "%s", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+}
+
+/**
+ * cr_pseudo_destroy:
+ *@a_this: the current instance to destroy.
+ *
+ *destructor of the #CRPseudo class.
+ */
+void
+cr_pseudo_destroy (CRPseudo * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->name) {
+ cr_string_destroy (a_this->name);
+ a_this->name = NULL;
+ }
+
+ if (a_this->extra) {
+ cr_string_destroy (a_this->extra);
+ a_this->extra = NULL;
+ }
+
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-pseudo.h b/src/st/croco/cr-pseudo.h
new file mode 100644
index 0000000..8917da4
--- /dev/null
+++ b/src/st/croco/cr-pseudo.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See COPYRIGHTS file for copyright information
+ */
+
+#ifndef __CR_PSEUDO_H__
+#define __CR_PSEUDO_H__
+
+#include <stdio.h>
+#include <glib.h>
+#include "cr-attr-sel.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+enum CRPseudoType
+{
+ IDENT_PSEUDO = 0,
+ FUNCTION_PSEUDO
+} ;
+
+typedef struct _CRPseudo CRPseudo ;
+
+/**
+ *The CRPseudo Class.
+ *Abstract a "pseudo" as defined by the css2 spec
+ *in appendix D.1 .
+ */
+struct _CRPseudo
+{
+ enum CRPseudoType type ;
+ CRString *name ;
+ CRString *extra ;
+ CRParsingLocation location ;
+} ;
+
+CRPseudo * cr_pseudo_new (void) ;
+
+guchar * cr_pseudo_to_string (CRPseudo const *a_this) ;
+
+void cr_pseudo_dump (CRPseudo const *a_this, FILE *a_fp) ;
+
+void cr_pseudo_destroy (CRPseudo *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_PSEUDO_H__*/
diff --git a/src/st/croco/cr-rgb.c b/src/st/croco/cr-rgb.c
new file mode 100644
index 0000000..a2b478f
--- /dev/null
+++ b/src/st/croco/cr-rgb.c
@@ -0,0 +1,604 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyrights information.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "cr-rgb.h"
+#include "cr-term.h"
+#include "cr-parser.h"
+
+static const CRRgb gv_standard_colors[] = {
+ {(const guchar*)"aliceblue", 240, 248, 255, FALSE, {0,0,0}},
+ {(const guchar*)"antiquewhite", 250, 235, 215, FALSE, {0,0,0}},
+ {(const guchar*)"aqua", 0, 255, 255, FALSE, {0,0,0}},
+ {(const guchar*)"aquamarine", 127, 255, 212, FALSE, {0,0,0}},
+ {(const guchar*)"azure", 240, 255, 255, FALSE, {0,0,0}},
+ {(const guchar*)"beige", 245, 245, 220, FALSE, {0,0,0}},
+ {(const guchar*)"bisque", 255, 228, 196, FALSE, {0,0,0}},
+ {(const guchar*)"black", 0, 0, 0, FALSE, {0,0,0}},
+ {(const guchar*)"blanchedalmond", 255, 235, 205, FALSE, {0,0,0}},
+ {(const guchar*)"blue", 0, 0, 255, FALSE, {0,0,0}},
+ {(const guchar*)"blueviolet", 138, 43, 226, FALSE, {0,0,0}},
+ {(const guchar*)"brown", 165, 42, 42, FALSE, {0,0,0}},
+ {(const guchar*)"burlywood", 222, 184, 135, FALSE, {0,0,0}},
+ {(const guchar*)"cadetblue", 95, 158, 160, FALSE, {0,0,0}},
+ {(const guchar*)"chartreuse", 127, 255, 0, FALSE, {0,0,0}},
+ {(const guchar*)"chocolate", 210, 105, 30, FALSE, {0,0,0}},
+ {(const guchar*)"coral", 255, 127, 80, FALSE, {0,0,0}},
+ {(const guchar*)"cornflowerblue", 100, 149, 237, FALSE, {0,0,0}},
+ {(const guchar*)"cornsilk", 255, 248, 220, FALSE, {0,0,0}},
+ {(const guchar*)"crimson", 220, 20, 60, FALSE, {0,0,0}},
+ {(const guchar*)"cyan", 0, 255, 255, FALSE, {0,0,0}},
+ {(const guchar*)"darkblue", 0, 0, 139, FALSE, {0,0,0}},
+ {(const guchar*)"darkcyan", 0, 139, 139, FALSE, {0,0,0}},
+ {(const guchar*)"darkgoldenrod", 184, 134, 11, FALSE, {0,0,0}},
+ {(const guchar*)"darkgray", 169, 169, 169, FALSE, {0,0,0}},
+ {(const guchar*)"darkgreen", 0, 100, 0, FALSE, {0,0,0}},
+ {(const guchar*)"darkgrey", 169, 169, 169, FALSE, {0,0,0}},
+ {(const guchar*)"darkkhaki", 189, 183, 107, FALSE, {0,0,0}},
+ {(const guchar*)"darkmagenta", 139, 0, 139, FALSE, {0,0,0}},
+ {(const guchar*)"darkolivegreen", 85, 107, 47, FALSE, {0,0,0}},
+ {(const guchar*)"darkorange", 255, 140, 0, FALSE, {0,0,0}},
+ {(const guchar*)"darkorchid", 153, 50, 204, FALSE, {0,0,0}},
+ {(const guchar*)"darkred", 139, 0, 0, FALSE, {0,0,0}},
+ {(const guchar*)"darksalmon", 233, 150, 122, FALSE, {0,0,0}},
+ {(const guchar*)"darkseagreen", 143, 188, 143, FALSE, {0,0,0}},
+ {(const guchar*)"darkslateblue", 72, 61, 139, FALSE, {0,0,0}},
+ {(const guchar*)"darkslategray", 47, 79, 79, FALSE, {0,0,0}},
+ {(const guchar*)"darkslategrey", 47, 79, 79, FALSE, {0,0,0}},
+ {(const guchar*)"darkturquoise", 0, 206, 209, FALSE, {0,0,0}},
+ {(const guchar*)"darkviolet", 148, 0, 211, FALSE, {0,0,0}},
+ {(const guchar*)"deeppink", 255, 20, 147, FALSE, {0,0,0}},
+ {(const guchar*)"deepskyblue", 0, 191, 255, FALSE, {0,0,0}},
+ {(const guchar*)"dimgray", 105, 105, 105, FALSE, {0,0,0}},
+ {(const guchar*)"dimgrey", 105, 105, 105, FALSE, {0,0,0}},
+ {(const guchar*)"dodgerblue", 30, 144, 255, FALSE, {0,0,0}},
+ {(const guchar*)"firebrick", 178, 34, 34, FALSE, {0,0,0}},
+ {(const guchar*)"floralwhite", 255, 250, 240, FALSE, {0,0,0}},
+ {(const guchar*)"forestgreen", 34, 139, 34, FALSE, {0,0,0}},
+ {(const guchar*)"fuchsia", 255, 0, 255, FALSE, {0,0,0}},
+ {(const guchar*)"gainsboro", 220, 220, 220, FALSE, {0,0,0}},
+ {(const guchar*)"ghostwhite", 248, 248, 255, FALSE, {0,0,0}},
+ {(const guchar*)"gold", 255, 215, 0, FALSE, {0,0,0}},
+ {(const guchar*)"goldenrod", 218, 165, 32, FALSE, {0,0,0}},
+ {(const guchar*)"gray", 128, 128, 128, FALSE, {0,0,0}},
+ {(const guchar*)"green", 0, 128, 0, FALSE, {0,0,0}},
+ {(const guchar*)"greenyellow", 173, 255, 47, FALSE, {0,0,0}},
+ {(const guchar*)"grey", 128, 128, 128, FALSE, {0,0,0}},
+ {(const guchar*)"honeydew", 240, 255, 240, FALSE, {0,0,0}},
+ {(const guchar*)"hotpink", 255, 105, 180, FALSE, {0,0,0}},
+ {(const guchar*)"indianred", 205, 92, 92, FALSE, {0,0,0}},
+ {(const guchar*)"indigo", 75, 0, 130, FALSE, {0,0,0}},
+ {(const guchar*)"ivory", 255, 255, 240, FALSE, {0,0,0}},
+ {(const guchar*)"khaki", 240, 230, 140, FALSE, {0,0,0}},
+ {(const guchar*)"lavender", 230, 230, 250, FALSE, {0,0,0}},
+ {(const guchar*)"lavenderblush", 255, 240, 245, FALSE, {0,0,0}},
+ {(const guchar*)"lawngreen", 124, 252, 0, FALSE, {0,0,0}},
+ {(const guchar*)"lemonchiffon", 255, 250, 205, FALSE, {0,0,0}},
+ {(const guchar*)"lightblue", 173, 216, 230, FALSE, {0,0,0}},
+ {(const guchar*)"lightcoral", 240, 128, 128, FALSE, {0,0,0}},
+ {(const guchar*)"lightcyan", 224, 255, 255, FALSE, {0,0,0}},
+ {(const guchar*)"lightgoldenrodyellow", 250, 250, 210, FALSE, {0,0,0}},
+ {(const guchar*)"lightgray", 211, 211, 211, FALSE, {0,0,0}},
+ {(const guchar*)"lightgreen", 144, 238, 144, FALSE, {0,0,0}},
+ {(const guchar*)"lightgrey", 211, 211, 211, FALSE, {0,0,0}},
+ {(const guchar*)"lightpink", 255, 182, 193, FALSE, {0,0,0}},
+ {(const guchar*)"lightsalmon", 255, 160, 122, FALSE, {0,0,0}},
+ {(const guchar*)"lightseagreen", 32, 178, 170, FALSE, {0,0,0}},
+ {(const guchar*)"lightskyblue", 135, 206, 250, FALSE, {0,0,0}},
+ {(const guchar*)"lightslategray", 119, 136, 153, FALSE, {0,0,0}},
+ {(const guchar*)"lightslategrey", 119, 136, 153, FALSE, {0,0,0}},
+ {(const guchar*)"lightsteelblue", 176, 196, 222, FALSE, {0,0,0}},
+ {(const guchar*)"lightyellow", 255, 255, 224, FALSE, {0,0,0}},
+ {(const guchar*)"lime", 0, 255, 0, FALSE, {0,0,0}},
+ {(const guchar*)"limegreen", 50, 205, 50, FALSE, {0,0,0}},
+ {(const guchar*)"linen", 250, 240, 230, FALSE, {0,0,0}},
+ {(const guchar*)"magenta", 255, 0, 255, FALSE, {0,0,0}},
+ {(const guchar*)"maroon", 128, 0, 0, FALSE, {0,0,0}},
+ {(const guchar*)"mediumaquamarine", 102, 205, 170, FALSE, {0,0,0}},
+ {(const guchar*)"mediumblue", 0, 0, 205, FALSE, {0,0,0}},
+ {(const guchar*)"mediumorchid", 186, 85, 211, FALSE, {0,0,0}},
+ {(const guchar*)"mediumpurple", 147, 112, 219, FALSE, {0,0,0}},
+ {(const guchar*)"mediumseagreen", 60, 179, 113, FALSE, {0,0,0}},
+ {(const guchar*)"mediumslateblue", 123, 104, 238, FALSE, {0,0,0}},
+ {(const guchar*)"mediumspringgreen", 0, 250, 154, FALSE, {0,0,0}},
+ {(const guchar*)"mediumturquoise", 72, 209, 204, FALSE, {0,0,0}},
+ {(const guchar*)"mediumvioletred", 199, 21, 133, FALSE, {0,0,0}},
+ {(const guchar*)"midnightblue", 25, 25, 112, FALSE, {0,0,0}},
+ {(const guchar*)"mintcream", 245, 255, 250, FALSE, {0,0,0}},
+ {(const guchar*)"mistyrose", 255, 228, 225, FALSE, {0,0,0}},
+ {(const guchar*)"moccasin", 255, 228, 181, FALSE, {0,0,0}},
+ {(const guchar*)"navajowhite", 255, 222, 173, FALSE, {0,0,0}},
+ {(const guchar*)"navy", 0, 0, 128, FALSE, {0,0,0}},
+ {(const guchar*)"oldlace", 253, 245, 230, FALSE, {0,0,0}},
+ {(const guchar*)"olive", 128, 128, 0, FALSE, {0,0,0}},
+ {(const guchar*)"olivedrab", 107, 142, 35, FALSE, {0,0,0}},
+ {(const guchar*)"orange", 255, 165, 0, FALSE, {0,0,0}},
+ {(const guchar*)"orangered", 255, 69, 0, FALSE, {0,0,0}},
+ {(const guchar*)"orchid", 218, 112, 214, FALSE, {0,0,0}},
+ {(const guchar*)"palegoldenrod", 238, 232, 170, FALSE, {0,0,0}},
+ {(const guchar*)"palegreen", 152, 251, 152, FALSE, {0,0,0}},
+ {(const guchar*)"paleturquoise", 175, 238, 238, FALSE, {0,0,0}},
+ {(const guchar*)"palevioletred", 219, 112, 147, FALSE, {0,0,0}},
+ {(const guchar*)"papayawhip", 255, 239, 213, FALSE, {0,0,0}},
+ {(const guchar*)"peachpuff", 255, 218, 185, FALSE, {0,0,0}},
+ {(const guchar*)"peru", 205, 133, 63, FALSE, {0,0,0}},
+ {(const guchar*)"pink", 255, 192, 203, FALSE, {0,0,0}},
+ {(const guchar*)"plum", 221, 160, 221, FALSE, {0,0,0}},
+ {(const guchar*)"powderblue", 176, 224, 230, FALSE, {0,0,0}},
+ {(const guchar*)"purple", 128, 0, 128, FALSE, {0,0,0}},
+ {(const guchar*)"red", 255, 0, 0, FALSE, {0,0,0}},
+ {(const guchar*)"rosybrown", 188, 143, 143, FALSE, {0,0,0}},
+ {(const guchar*)"royalblue", 65, 105, 225, FALSE, {0,0,0}},
+ {(const guchar*)"saddlebrown", 139, 69, 19, FALSE, {0,0,0}},
+ {(const guchar*)"salmon", 250, 128, 114, FALSE, {0,0,0}},
+ {(const guchar*)"sandybrown", 244, 164, 96, FALSE, {0,0,0}},
+ {(const guchar*)"seagreen", 46, 139, 87, FALSE, {0,0,0}},
+ {(const guchar*)"seashell", 255, 245, 238, FALSE, {0,0,0}},
+ {(const guchar*)"sienna", 160, 82, 45, FALSE, {0,0,0}},
+ {(const guchar*)"silver", 192, 192, 192, FALSE, {0,0,0}},
+ {(const guchar*)"skyblue", 135, 206, 235, FALSE, {0,0,0}},
+ {(const guchar*)"slateblue", 106, 90, 205, FALSE, {0,0,0}},
+ {(const guchar*)"slategray", 112, 128, 144, FALSE, {0,0,0}},
+ {(const guchar*)"slategrey", 112, 128, 144, FALSE, {0,0,0}},
+ {(const guchar*)"snow", 255, 250, 250, FALSE, {0,0,0}},
+ {(const guchar*)"springgreen", 0, 255, 127, FALSE, {0,0,0}},
+ {(const guchar*)"steelblue", 70, 130, 180, FALSE, {0,0,0}},
+ {(const guchar*)"tan", 210, 180, 140, FALSE, {0,0,0}},
+ {(const guchar*)"teal", 0, 128, 128, FALSE, {0,0,0}},
+ {(const guchar*)"thistle", 216, 191, 216, FALSE, {0,0,0}},
+ {(const guchar*)"tomato", 255, 99, 71, FALSE, {0,0,0}},
+ {(const guchar*)"turquoise", 64, 224, 208, FALSE, {0,0,0}},
+ {(const guchar*)"violet", 238, 130, 238, FALSE, {0,0,0}},
+ {(const guchar*)"wheat", 245, 222, 179, FALSE, {0,0,0}},
+ {(const guchar*)"white", 255, 255, 255, FALSE, {0,0,0}},
+ {(const guchar*)"whitesmoke", 245, 245, 245, FALSE, {0,0,0}},
+ {(const guchar*)"yellow", 255, 255, 0, FALSE, {0,0,0}},
+ {(const guchar*)"yellowgreen", 154, 205, 50, FALSE, {0,0,0}}
+};
+
+/**
+ * cr_rgb_new:
+ *
+ *The default constructor of #CRRgb.
+ *
+ *Returns the newly built instance of #CRRgb
+ */
+CRRgb *
+cr_rgb_new (void)
+{
+ CRRgb *result = NULL;
+
+ result = g_try_malloc (sizeof (CRRgb));
+
+ if (result == NULL) {
+ cr_utils_trace_info ("No more memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRRgb));
+
+ return result;
+}
+
+/**
+ * cr_rgb_new_with_vals:
+ *@a_red: the red component of the color.
+ *@a_green: the green component of the color.
+ *@a_blue: the blue component of the color.
+ *@a_unit: the unit of the rgb values.
+ *(either percentage or integer values)
+ *
+ *A constructor of #CRRgb.
+ *
+ *Returns the newly built instance of #CRRgb.
+ */
+CRRgb *
+cr_rgb_new_with_vals (gulong a_red, gulong a_green,
+ gulong a_blue, gboolean a_is_percentage)
+{
+ CRRgb *result = NULL;
+
+ result = cr_rgb_new ();
+
+ g_return_val_if_fail (result, NULL);
+
+ result->red = a_red;
+ result->green = a_green;
+ result->blue = a_blue;
+ result->is_percentage = a_is_percentage;
+
+ return result;
+}
+
+/**
+ * cr_rgb_to_string:
+ *@a_this: the instance of #CRRgb to serialize.
+ *
+ *Serializes the rgb into a zero terminated string.
+ *
+ *Returns the zero terminated string containing the serialized
+ *rgb. MUST BE FREED by the caller using g_free().
+ */
+guchar *
+cr_rgb_to_string (CRRgb const * a_this)
+{
+ guchar *result = NULL;
+ GString *str_buf = NULL;
+
+ str_buf = g_string_new (NULL);
+ g_return_val_if_fail (str_buf, NULL);
+
+ if (a_this->is_percentage == 1) {
+ g_string_append_printf (str_buf, "%ld", a_this->red);
+
+ g_string_append (str_buf, "%, ");
+
+ g_string_append_printf (str_buf, "%ld", a_this->green);
+ g_string_append (str_buf, "%, ");
+
+ g_string_append_printf (str_buf, "%ld", a_this->blue);
+ g_string_append_c (str_buf, '%');
+ } else {
+ g_string_append_printf (str_buf, "%ld", a_this->red);
+ g_string_append (str_buf, ", ");
+
+ g_string_append_printf (str_buf, "%ld", a_this->green);
+ g_string_append (str_buf, ", ");
+
+ g_string_append_printf (str_buf, "%ld", a_this->blue);
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ }
+
+ return result;
+}
+
+/**
+ * cr_rgb_dump:
+ *@a_this: the "this pointer" of
+ *the current instance of #CRRgb.
+ *@a_fp: the destination file pointer.
+ *
+ *Dumps the current instance of #CRRgb
+ *to a file.
+ */
+void
+cr_rgb_dump (CRRgb const * a_this, FILE * a_fp)
+{
+ guchar *str = NULL;
+
+ g_return_if_fail (a_this);
+
+ str = cr_rgb_to_string (a_this);
+
+ if (str) {
+ fprintf (a_fp, "%s", str);
+ g_free (str);
+ str = NULL;
+ }
+}
+
+/**
+ * cr_rgb_compute_from_percentage:
+ *@a_this: the current instance of #CRRgb
+ *
+ *If the rgb values are expressed in percentage,
+ *compute their real value.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_rgb_compute_from_percentage (CRRgb * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ if (a_this->is_percentage == FALSE)
+ return CR_OK;
+ a_this->red = a_this->red * 255 / 100;
+ a_this->green = a_this->green * 255 / 100;
+ a_this->blue = a_this->blue * 255 / 100;
+ a_this->is_percentage = FALSE;
+ return CR_OK;
+}
+
+/**
+ * cr_rgb_set:
+ *@a_this: the current instance of #CRRgb.
+ *@a_red: the red value.
+ *@a_green: the green value.
+ *@a_blue: the blue value.
+ *
+ *Sets rgb values to the RGB.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_rgb_set (CRRgb * a_this, gulong a_red,
+ gulong a_green, gulong a_blue, gboolean a_is_percentage)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+ if (a_is_percentage != FALSE) {
+ g_return_val_if_fail (a_red <= 100
+ && a_green <= 100
+ && a_blue <= 100, CR_BAD_PARAM_ERROR);
+ }
+
+ a_this->is_percentage = a_is_percentage;
+
+ a_this->red = a_red;
+ a_this->green = a_green;
+ a_this->blue = a_blue;
+ return CR_OK;
+}
+
+/**
+ * cr_rgb_set_from_rgb:
+ *@a_this: the current instance of #CRRgb.
+ *@a_rgb: the rgb to "copy"
+ *
+ *Sets the rgb from an other one.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_rgb_set_from_rgb (CRRgb * a_this, CRRgb const * a_rgb)
+{
+ g_return_val_if_fail (a_this && a_rgb, CR_BAD_PARAM_ERROR);
+
+ cr_rgb_copy (a_this, a_rgb) ;
+
+ return CR_OK;
+}
+
+static int
+cr_rgb_color_name_compare (const void *a,
+ const void *b)
+{
+ const char *a_color_name = a;
+ const CRRgb *rgb = b;
+
+ return g_ascii_strcasecmp (a_color_name, (const char *) rgb->name);
+}
+
+/**
+ * cr_rgb_set_from_name:
+ * @a_this: the current instance of #CRRgb
+ * @a_color_name: the color name
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_rgb_set_from_name (CRRgb * a_this, const guchar * a_color_name)
+{
+ enum CRStatus status = CR_OK;
+ CRRgb *result;
+
+ g_return_val_if_fail (a_this && a_color_name, CR_BAD_PARAM_ERROR);
+
+ result = bsearch (a_color_name,
+ gv_standard_colors,
+ G_N_ELEMENTS (gv_standard_colors),
+ sizeof (gv_standard_colors[0]),
+ cr_rgb_color_name_compare);
+ if (result != NULL)
+ cr_rgb_set_from_rgb (a_this, result);
+ else
+ status = CR_UNKNOWN_TYPE_ERROR;
+
+ return status;
+}
+
+/**
+ * cr_rgb_set_from_hex_str:
+ * @a_this: the current instance of #CRRgb
+ * @a_hex: the hexadecimal value to set.
+ *
+ * Returns CR_OK upon successful completion.
+ */
+enum CRStatus
+cr_rgb_set_from_hex_str (CRRgb * a_this, const guchar * a_hex)
+{
+ enum CRStatus status = CR_OK;
+ gulong i = 0;
+ guchar colors[3] = { 0 };
+
+ g_return_val_if_fail (a_this && a_hex, CR_BAD_PARAM_ERROR);
+
+ if (strlen ((const char *) a_hex) == 3) {
+ for (i = 0; i < 3; i++) {
+ if (a_hex[i] >= '0' && a_hex[i] <= '9') {
+ colors[i] = a_hex[i] - '0';
+ colors[i] = (colors[i] << 4) | colors[i];
+ } else if (a_hex[i] >= 'a' && a_hex[i] <= 'z') {
+ colors[i] = 10 + a_hex[i] - 'a';
+ colors[i] = (colors[i] << 4) | colors[i];
+ } else if (a_hex[i] >= 'A' && a_hex[i] <= 'Z') {
+ colors[i] = 10 + a_hex[i] - 'A';
+ colors[i] = (colors[i] << 4) | colors[i];
+ } else {
+ status = CR_UNKNOWN_TYPE_ERROR;
+ }
+ }
+ } else if (strlen ((const char *) a_hex) == 6) {
+ for (i = 0; i < 6; i++) {
+ if (a_hex[i] >= '0' && a_hex[i] <= '9') {
+ colors[i / 2] <<= 4;
+ colors[i / 2] |= a_hex[i] - '0';
+ status = CR_OK;
+ } else if (a_hex[i] >= 'a' && a_hex[i] <= 'z') {
+ colors[i / 2] <<= 4;
+ colors[i / 2] |= 10 + a_hex[i] - 'a';
+ status = CR_OK;
+ } else if (a_hex[i] >= 'A' && a_hex[i] <= 'Z') {
+ colors[i / 2] <<= 4;
+ colors[i / 2] |= 10 + a_hex[i] - 'A';
+ status = CR_OK;
+ } else {
+ status = CR_UNKNOWN_TYPE_ERROR;
+ }
+ }
+ } else {
+ status = CR_UNKNOWN_TYPE_ERROR;
+ }
+
+ if (status == CR_OK) {
+ status = cr_rgb_set (a_this, colors[0],
+ colors[1], colors[2], FALSE);
+ }
+ return status;
+}
+
+/**
+ * cr_rgb_set_from_term:
+ *@a_this: the instance of #CRRgb to set
+ *@a_value: the terminal from which to set
+ *
+ *Set the rgb from a terminal symbol
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_rgb_set_from_term (CRRgb *a_this, const struct _CRTerm *a_value)
+{
+ enum CRStatus status = CR_OK ;
+ g_return_val_if_fail (a_this && a_value,
+ CR_BAD_PARAM_ERROR) ;
+
+ switch(a_value->type) {
+ case TERM_RGB:
+ if (a_value->content.rgb) {
+ cr_rgb_set_from_rgb
+ (a_this, a_value->content.rgb) ;
+ }
+ break ;
+ case TERM_IDENT:
+ if (a_value->content.str
+ && a_value->content.str->stryng
+ && a_value->content.str->stryng->str) {
+ status = cr_rgb_set_from_name
+ (a_this,
+ (const guchar *) a_value->content.str->stryng->str) ;
+ } else {
+ cr_utils_trace_info
+ ("a_value has NULL string value") ;
+ }
+ break ;
+ case TERM_HASH:
+ if (a_value->content.str
+ && a_value->content.str->stryng
+ && a_value->content.str->stryng->str) {
+ status = cr_rgb_set_from_hex_str
+ (a_this,
+ (const guchar *) a_value->content.str->stryng->str) ;
+ } else {
+ cr_utils_trace_info
+ ("a_value has NULL string value") ;
+ }
+ break ;
+ default:
+ status = CR_UNKNOWN_TYPE_ERROR ;
+ }
+ return status ;
+}
+
+enum CRStatus
+cr_rgb_copy (CRRgb *a_dest, CRRgb const *a_src)
+{
+ g_return_val_if_fail (a_dest && a_src,
+ CR_BAD_PARAM_ERROR) ;
+
+ memcpy (a_dest, a_src, sizeof (CRRgb)) ;
+ return CR_OK ;
+}
+
+/**
+ * cr_rgb_destroy:
+ *@a_this: the "this pointer" of the
+ *current instance of #CRRgb.
+ *
+ *Destructor of #CRRgb.
+ */
+void
+cr_rgb_destroy (CRRgb * a_this)
+{
+ g_return_if_fail (a_this);
+ g_free (a_this);
+}
+
+/**
+ * cr_rgb_parse_from_buf:
+ *@a_str: a string that contains a color description
+ *@a_enc: the encoding of a_str
+ *
+ *Parses a text buffer that contains a rgb color
+ *
+ *Returns the parsed color, or NULL in case of error
+ */
+CRRgb *
+cr_rgb_parse_from_buf (const guchar *a_str,
+ enum CREncoding a_enc)
+{
+ enum CRStatus status = CR_OK ;
+ CRTerm *value = NULL ;
+ CRParser * parser = NULL;
+ CRRgb *result = NULL;
+
+ g_return_val_if_fail (a_str, NULL);
+
+ parser = cr_parser_new_from_buf ((guchar *) a_str, strlen ((const char *) a_str), a_enc, FALSE);
+
+ g_return_val_if_fail (parser, NULL);
+
+ status = cr_parser_try_to_skip_spaces_and_comments (parser) ;
+ if (status != CR_OK)
+ goto cleanup;
+
+ status = cr_parser_parse_term (parser, &value);
+ if (status != CR_OK)
+ goto cleanup;
+
+ result = cr_rgb_new ();
+ if (!result)
+ goto cleanup;
+
+ status = cr_rgb_set_from_term (result, value);
+
+cleanup:
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ }
+ if (value) {
+ cr_term_destroy(value);
+ value = NULL;
+ }
+ return result ;
+}
+
diff --git a/src/st/croco/cr-rgb.h b/src/st/croco/cr-rgb.h
new file mode 100644
index 0000000..a77e309
--- /dev/null
+++ b/src/st/croco/cr-rgb.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * see COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_RGB_H__
+#define __CR_RGB_H__
+
+#include <stdio.h>
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+
+typedef struct _CRRgb CRRgb ;
+struct _CRRgb
+{
+ /*
+ *the unit of the rgb.
+ *Either NO_UNIT (integer) or
+ *UNIT_PERCENTAGE (percentage).
+ */
+ const guchar *name ;
+ glong red ;
+ glong green ;
+ glong blue ;
+ gboolean is_percentage ;
+ CRParsingLocation location ;
+} ;
+
+CRRgb * cr_rgb_new (void) ;
+
+CRRgb * cr_rgb_new_with_vals (gulong a_red, gulong a_green,
+ gulong a_blue, gboolean a_is_percentage) ;
+
+CRRgb *cr_rgb_parse_from_buf(const guchar *a_str,
+ enum CREncoding a_enc);
+
+enum CRStatus cr_rgb_compute_from_percentage (CRRgb *a_this) ;
+
+enum CRStatus cr_rgb_set (CRRgb *a_this, gulong a_red,
+ gulong a_green, gulong a_blue,
+ gboolean a_is_percentage) ;
+
+enum CRStatus cr_rgb_copy (CRRgb *a_dest, CRRgb const *a_src) ;
+
+enum CRStatus cr_rgb_set_from_rgb (CRRgb *a_this, CRRgb const *a_rgb) ;
+
+enum CRStatus cr_rgb_set_from_name (CRRgb *a_this, const guchar *a_color_name) ;
+
+enum CRStatus cr_rgb_set_from_hex_str (CRRgb *a_this, const guchar * a_hex_value) ;
+
+struct _CRTerm;
+
+enum CRStatus cr_rgb_set_from_term (CRRgb *a_this, const struct _CRTerm *a_value);
+
+guchar * cr_rgb_to_string (CRRgb const *a_this) ;
+
+void cr_rgb_dump (CRRgb const *a_this, FILE *a_fp) ;
+
+void cr_rgb_destroy (CRRgb *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_RGB_H__*/
diff --git a/src/st/croco/cr-selector.c b/src/st/croco/cr-selector.c
new file mode 100644
index 0000000..c9aad43
--- /dev/null
+++ b/src/st/croco/cr-selector.c
@@ -0,0 +1,305 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include <string.h>
+#include "cr-selector.h"
+#include "cr-parser.h"
+
+/**
+ * cr_selector_new:
+ *
+ *@a_simple_sel: the initial simple selector list
+ *of the current instance of #CRSelector.
+ *
+ *Creates a new instance of #CRSelector.
+ *
+ *Returns the newly built instance of #CRSelector, or
+ *NULL in case of failure.
+ */
+CRSelector *
+cr_selector_new (CRSimpleSel * a_simple_sel)
+{
+ CRSelector *result = NULL;
+
+ result = g_try_malloc (sizeof (CRSelector));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRSelector));
+ result->simple_sel = a_simple_sel;
+ return result;
+}
+
+CRSelector *
+cr_selector_parse_from_buf (const guchar * a_char_buf, enum CREncoding a_enc)
+{
+ CRParser *parser = NULL;
+
+ g_return_val_if_fail (a_char_buf, NULL);
+
+ parser = cr_parser_new_from_buf ((guchar*)a_char_buf, strlen ((const char *) a_char_buf),
+ a_enc, FALSE);
+ g_return_val_if_fail (parser, NULL);
+
+ return NULL;
+}
+
+/**
+ * cr_selector_append:
+ *
+ *@a_this: the current instance of #CRSelector.
+ *@a_new: the instance of #CRSelector to be appended.
+ *
+ *Appends a new instance of #CRSelector to the current selector list.
+ *
+ *Returns the new list.
+ */
+CRSelector *
+cr_selector_append (CRSelector * a_this, CRSelector * a_new)
+{
+ CRSelector *cur = NULL;
+
+ if (!a_this) {
+ return a_new;
+ }
+
+ /*walk forward the list headed by a_this to get the list tail */
+ for (cur = a_this; cur && cur->next; cur = cur->next) ;
+
+ cur->next = a_new;
+ a_new->prev = cur;
+
+ return a_this;
+}
+
+/**
+ * cr_selector_prepend:
+ *
+ *@a_this: the current instance of #CRSelector list.
+ *@a_new: the instance of #CRSelector.
+ *
+ *Prepends an element to the #CRSelector list.
+ *
+ *Returns the new list.
+ */
+CRSelector *
+cr_selector_prepend (CRSelector * a_this, CRSelector * a_new)
+{
+ CRSelector *cur = NULL;
+
+ a_new->next = a_this;
+ a_this->prev = a_new;
+
+ for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
+
+ return cur;
+}
+
+/**
+ * cr_selector_append_simple_sel:
+ *
+ *@a_this: the current instance of #CRSelector.
+ *@a_simple_sel: the simple selector to append.
+ *
+ *append a simple selector to the current #CRSelector list.
+ *
+ *Returns the new list or NULL in case of failure.
+ */
+CRSelector *
+cr_selector_append_simple_sel (CRSelector * a_this,
+ CRSimpleSel * a_simple_sel)
+{
+ CRSelector *selector = NULL;
+
+ selector = cr_selector_new (a_simple_sel);
+ g_return_val_if_fail (selector, NULL);
+
+ return cr_selector_append (a_this, selector);
+}
+
+guchar *
+cr_selector_to_string (CRSelector const * a_this)
+{
+ guchar *result = NULL;
+ GString *str_buf = NULL;
+
+ str_buf = g_string_new (NULL);
+ g_return_val_if_fail (str_buf, NULL);
+
+ if (a_this) {
+ CRSelector const *cur = NULL;
+
+ for (cur = a_this; cur; cur = cur->next) {
+ if (cur->simple_sel) {
+ guchar *tmp_str = NULL;
+
+ tmp_str = cr_simple_sel_to_string
+ (cur->simple_sel);
+
+ if (tmp_str) {
+ if (cur->prev)
+ g_string_append (str_buf,
+ ", ");
+
+ g_string_append (str_buf, (const gchar *) tmp_str);
+
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ }
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_selector_dump:
+ *
+ *@a_this: the current instance of #CRSelector.
+ *@a_fp: the destination file.
+ *
+ *Serializes the current instance of #CRSelector to a file.
+ */
+void
+cr_selector_dump (CRSelector const * a_this, FILE * a_fp)
+{
+ guchar *tmp_buf = NULL;
+
+ if (a_this) {
+ tmp_buf = cr_selector_to_string (a_this);
+ if (tmp_buf) {
+ fprintf (a_fp, "%s", tmp_buf);
+ g_free (tmp_buf);
+ tmp_buf = NULL;
+ }
+ }
+}
+
+/**
+ * cr_selector_ref:
+ *
+ *@a_this: the current instance of #CRSelector.
+ *
+ *Increments the ref count of the current instance
+ *of #CRSelector.
+ */
+void
+cr_selector_ref (CRSelector * a_this)
+{
+ g_return_if_fail (a_this);
+
+ a_this->ref_count++;
+}
+
+/**
+ * cr_selector_unref:
+ *
+ *@a_this: the current instance of #CRSelector.
+ *
+ *Decrements the ref count of the current instance of
+ *#CRSelector.
+ *If the ref count reaches zero, the current instance of
+ *#CRSelector is destroyed.
+ *
+ *Returns TRUE if this function destroyed the current instance
+ *of #CRSelector, FALSE otherwise.
+ */
+gboolean
+cr_selector_unref (CRSelector * a_this)
+{
+ g_return_val_if_fail (a_this, FALSE);
+
+ if (a_this->ref_count) {
+ a_this->ref_count--;
+ }
+
+ if (a_this->ref_count == 0) {
+ cr_selector_destroy (a_this);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * cr_selector_destroy:
+ *
+ *@a_this: the current instance of #CRSelector.
+ *
+ *Destroys the selector list.
+ */
+void
+cr_selector_destroy (CRSelector * a_this)
+{
+ CRSelector *cur = NULL;
+
+ g_return_if_fail (a_this);
+
+ /*
+ *go and get the list tail. In the same time, free
+ *all the simple selectors contained in the list.
+ */
+ for (cur = a_this; cur && cur->next; cur = cur->next) {
+ if (cur->simple_sel) {
+ cr_simple_sel_destroy (cur->simple_sel);
+ cur->simple_sel = NULL;
+ }
+ }
+
+ if (cur) {
+ if (cur->simple_sel) {
+ cr_simple_sel_destroy (cur->simple_sel);
+ cur->simple_sel = NULL;
+ }
+ }
+
+ /*in case the list has only one element */
+ if (cur && !cur->prev) {
+ g_free (cur);
+ return;
+ }
+
+ /*walk backward the list and free each "next element" */
+ for (cur = cur->prev; cur && cur->prev; cur = cur->prev) {
+ if (cur->next) {
+ g_free (cur->next);
+ cur->next = NULL;
+ }
+ }
+
+ if (!cur)
+ return;
+
+ if (cur->next) {
+ g_free (cur->next);
+ cur->next = NULL;
+ }
+
+ g_free (cur);
+}
diff --git a/src/st/croco/cr-selector.h b/src/st/croco/cr-selector.h
new file mode 100644
index 0000000..dd6a7f7
--- /dev/null
+++ b/src/st/croco/cr-selector.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_SELECTOR_H__
+#define __CR_SELECTOR_H__
+
+#include <stdio.h>
+#include "cr-utils.h"
+#include "cr-simple-sel.h"
+#include "cr-parsing-location.h"
+
+/**
+ *@file
+ *The declaration file of the #CRSelector file.
+ */
+
+G_BEGIN_DECLS
+
+typedef struct _CRSelector CRSelector ;
+
+/**
+ *Abstracts a CSS2 selector as defined in the right part
+ *of the 'ruleset" production in the appendix D.1 of the
+ *css2 spec.
+ *It is actually the abstraction of a comma separated list
+ *of simple selectors list.
+ *In a css2 file, a selector is a list of simple selectors
+ *separated by a comma.
+ *e.g: sel0, sel1, sel2 ...
+ *Each seln is a simple selector
+ */
+struct _CRSelector
+{
+ /**
+ *A Selection expression.
+ *It is a list of basic selectors.
+ *Each basic selector can be either an element
+ *selector, an id selector, a class selector, an
+ *attribute selector, an universal selector etc ...
+ */
+ CRSimpleSel *simple_sel ;
+
+ /**The next selector list element*/
+ CRSelector *next ;
+ CRSelector *prev ;
+ CRParsingLocation location ;
+ glong ref_count ;
+};
+
+CRSelector* cr_selector_new (CRSimpleSel *a_sel_expr) ;
+
+CRSelector * cr_selector_parse_from_buf (const guchar * a_char_buf,
+ enum CREncoding a_enc) ;
+
+CRSelector* cr_selector_append (CRSelector *a_this, CRSelector *a_new) ;
+
+CRSelector* cr_selector_append_simple_sel (CRSelector *a_this,
+ CRSimpleSel *a_simple_sel) ;
+
+CRSelector* cr_selector_prepend (CRSelector *a_this, CRSelector *a_new) ;
+
+guchar * cr_selector_to_string (CRSelector const *a_this) ;
+
+void cr_selector_dump (CRSelector const *a_this, FILE *a_fp) ;
+
+void cr_selector_ref (CRSelector *a_this) ;
+
+gboolean cr_selector_unref (CRSelector *a_this) ;
+
+void cr_selector_destroy (CRSelector *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_SELECTOR_H__*/
diff --git a/src/st/croco/cr-simple-sel.c b/src/st/croco/cr-simple-sel.c
new file mode 100644
index 0000000..bac8621
--- /dev/null
+++ b/src/st/croco/cr-simple-sel.c
@@ -0,0 +1,323 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include "cr-simple-sel.h"
+
+/**
+ * cr_simple_sel_new:
+ *
+ *The constructor of #CRSimpleSel.
+ *
+ *Returns the new instance of #CRSimpleSel.
+ */
+CRSimpleSel *
+cr_simple_sel_new (void)
+{
+ CRSimpleSel *result = NULL;
+
+ result = g_try_malloc (sizeof (CRSimpleSel));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRSimpleSel));
+
+ return result;
+}
+
+/**
+ * cr_simple_sel_append_simple_sel:
+ *
+ *Appends a simpe selector to the current list of simple selector.
+ *
+ *@a_this: the this pointer of the current instance of #CRSimpleSel.
+ *@a_sel: the simple selector to append.
+ *
+ *Returns: the new list upon successful completion, an error code otherwise.
+ */
+CRSimpleSel *
+cr_simple_sel_append_simple_sel (CRSimpleSel * a_this, CRSimpleSel * a_sel)
+{
+ CRSimpleSel *cur = NULL;
+
+ g_return_val_if_fail (a_sel, NULL);
+
+ if (a_this == NULL)
+ return a_sel;
+
+ for (cur = a_this; cur->next; cur = cur->next) ;
+
+ cur->next = a_sel;
+ a_sel->prev = cur;
+
+ return a_this;
+}
+
+/**
+ * cr_simple_sel_prepend_simple_sel:
+ *
+ *@a_this: the this pointer of the current instance of #CRSimpleSel.
+ *@a_sel: the simple selector to prepend.
+ *
+ *Prepends a simple selector to the current list of simple selectors.
+ *
+ *Returns the new list upon successful completion, an error code otherwise.
+ */
+CRSimpleSel *
+cr_simple_sel_prepend_simple_sel (CRSimpleSel * a_this, CRSimpleSel * a_sel)
+{
+ g_return_val_if_fail (a_sel, NULL);
+
+ if (a_this == NULL)
+ return a_sel;
+
+ a_sel->next = a_this;
+ a_this->prev = a_sel;
+
+ return a_sel;
+}
+
+guchar *
+cr_simple_sel_to_string (CRSimpleSel const * a_this)
+{
+ GString *str_buf = NULL;
+ guchar *result = NULL;
+
+ CRSimpleSel const *cur = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ str_buf = g_string_new (NULL);
+ for (cur = a_this; cur; cur = cur->next) {
+ if (cur->name) {
+ guchar *str = (guchar *) g_strndup (cur->name->stryng->str,
+ cur->name->stryng->len);
+
+ if (str) {
+ switch (cur->combinator) {
+ case COMB_WS:
+ g_string_append (str_buf, " ");
+ break;
+
+ case COMB_PLUS:
+ g_string_append (str_buf, "+");
+ break;
+
+ case COMB_GT:
+ g_string_append (str_buf, ">");
+ break;
+
+ default:
+ break;
+ }
+
+ g_string_append (str_buf, (const gchar *) str);
+ g_free (str);
+ str = NULL;
+ }
+ }
+
+ if (cur->add_sel) {
+ guchar *tmp_str = NULL;
+
+ tmp_str = cr_additional_sel_to_string (cur->add_sel);
+ if (tmp_str) {
+ g_string_append (str_buf, (const gchar *) tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+}
+
+
+guchar *
+cr_simple_sel_one_to_string (CRSimpleSel const * a_this)
+{
+ GString *str_buf = NULL;
+ guchar *result = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ str_buf = g_string_new (NULL);
+ if (a_this->name) {
+ guchar *str = (guchar *) g_strndup (a_this->name->stryng->str,
+ a_this->name->stryng->len);
+
+ if (str) {
+ g_string_append_printf (str_buf, "%s", str);
+ g_free (str);
+ str = NULL;
+ }
+ }
+
+ if (a_this->add_sel) {
+ guchar *tmp_str = NULL;
+
+ tmp_str = cr_additional_sel_to_string (a_this->add_sel);
+ if (tmp_str) {
+ g_string_append_printf
+ (str_buf, "%s", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_simple_sel_dump:
+ *@a_this: the current instance of #CRSimpleSel.
+ *@a_fp: the destination file pointer.
+ *
+ *Dumps the selector to a file.
+ *TODO: add the support of unicode in the dump.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_simple_sel_dump (CRSimpleSel const * a_this, FILE * a_fp)
+{
+ guchar *tmp_str = NULL;
+
+ g_return_val_if_fail (a_fp, CR_BAD_PARAM_ERROR);
+
+ if (a_this) {
+ tmp_str = cr_simple_sel_to_string (a_this);
+ if (tmp_str) {
+ fprintf (a_fp, "%s", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+
+ return CR_OK;
+}
+
+/**
+ * cr_simple_sel_compute_specificity:
+ *
+ *@a_this: the current instance of #CRSimpleSel
+ *
+ *Computes the selector (combinator separated list of simple selectors)
+ *as defined in the css2 spec in chapter 6.4.3
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_simple_sel_compute_specificity (CRSimpleSel * a_this)
+{
+ CRAdditionalSel const *cur_add_sel = NULL;
+ CRSimpleSel const *cur_sel = NULL;
+ gulong a = 0,
+ b = 0,
+ c = 0;
+
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ for (cur_sel = a_this; cur_sel; cur_sel = cur_sel->next) {
+ if (cur_sel->type_mask & TYPE_SELECTOR) {
+ c++; /*hmmh, is this a new language ? */
+ } else if (!cur_sel->name
+ || !cur_sel->name->stryng
+ || !cur_sel->name->stryng->str) {
+ if (cur_sel->add_sel->type ==
+ PSEUDO_CLASS_ADD_SELECTOR) {
+ /*
+ *this is a pseudo element, and
+ *the spec says, "ignore pseudo elements".
+ */
+ continue;
+ }
+ }
+
+ for (cur_add_sel = cur_sel->add_sel;
+ cur_add_sel; cur_add_sel = cur_add_sel->next) {
+ switch (cur_add_sel->type) {
+ case ID_ADD_SELECTOR:
+ a++;
+ break;
+
+ case NO_ADD_SELECTOR:
+ continue;
+
+ default:
+ b++;
+ break;
+ }
+ }
+ }
+
+ /*we suppose a, b and c have 1 to 3 digits */
+ a_this->specificity = a * 1000000 + b * 1000 + c;
+
+ return CR_OK;
+}
+
+/**
+ * cr_simple_sel_destroy:
+ *
+ *@a_this: the this pointer of the current instance of #CRSimpleSel.
+ *
+ *The destructor of the current instance of
+ *#CRSimpleSel.
+ */
+void
+cr_simple_sel_destroy (CRSimpleSel * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->name) {
+ cr_string_destroy (a_this->name);
+ a_this->name = NULL;
+ }
+
+ if (a_this->add_sel) {
+ cr_additional_sel_destroy (a_this->add_sel);
+ a_this->add_sel = NULL;
+ }
+
+ if (a_this->next) {
+ cr_simple_sel_destroy (a_this->next);
+ }
+
+ if (a_this) {
+ g_free (a_this);
+ }
+}
diff --git a/src/st/croco/cr-simple-sel.h b/src/st/croco/cr-simple-sel.h
new file mode 100644
index 0000000..72b15fd
--- /dev/null
+++ b/src/st/croco/cr-simple-sel.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+
+#ifndef __CR_SEL_H__
+#define __CR_SEL_H__
+
+#include <stdio.h>
+#include <glib.h>
+#include "cr-additional-sel.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *the declaration of the #CRSimpleSel class.
+ *
+ */
+enum Combinator
+{
+ NO_COMBINATOR,
+ COMB_WS,/*whitespace: descendent*/
+ COMB_PLUS,/*'+': preceded by*/
+ COMB_GT/*greater than ('>'): child*/
+} ;
+
+enum SimpleSelectorType
+{
+ NO_SELECTOR_TYPE = 0,
+ UNIVERSAL_SELECTOR = 1,
+ TYPE_SELECTOR = 1 << 1
+} ;
+
+typedef struct _CRSimpleSel CRSimpleSel ;
+
+/**
+ *The abstraction of a css2 simple selection list
+ *as defined by the right part of the "selector" production in the
+ *appendix D.1 of the css2 spec.
+ *It is basically a list of simple selector, each
+ *simple selector being separated by a combinator.
+ *
+ *In the libcroco's implementation, each simple selector
+ *is made of at most two parts:
+ *
+ *1/An element name or 'type selector' (which can hold a '*' and
+ *then been called 'universal selector')
+ *
+ *2/An additional selector that "specializes" the preceding type or
+ *universal selector. The additional selector can be either
+ *an id selector, or a class selector, or an attribute selector.
+ */
+struct _CRSimpleSel
+{
+ enum SimpleSelectorType type_mask ;
+ gboolean is_case_sentive ;
+ CRString * name ;
+ /**
+ *The combinator that separates
+ *this simple selector from the previous
+ *one.
+ */
+ enum Combinator combinator ;
+
+ /**
+ *The additional selector list of the
+ *current simple selector.
+ *An additional selector may
+ *be a class selector, an id selector,
+ *or an attribute selector.
+ *Note that this field is a linked list.
+ */
+ CRAdditionalSel *add_sel ;
+
+ /*
+ *the specificity as specified by
+ *chapter 6.4.3 of the spec.
+ */
+ gulong specificity ;
+
+ CRSimpleSel *next ;
+ CRSimpleSel *prev ;
+ CRParsingLocation location ;
+} ;
+
+CRSimpleSel * cr_simple_sel_new (void) ;
+
+CRSimpleSel * cr_simple_sel_append_simple_sel (CRSimpleSel *a_this,
+ CRSimpleSel *a_sel) ;
+
+CRSimpleSel * cr_simple_sel_prepend_simple_sel (CRSimpleSel *a_this,
+ CRSimpleSel *a_sel) ;
+
+guchar * cr_simple_sel_to_string (CRSimpleSel const *a_this) ;
+
+guchar * cr_simple_sel_one_to_string (CRSimpleSel const * a_this) ;
+
+enum CRStatus cr_simple_sel_dump (CRSimpleSel const *a_this, FILE *a_fp) ;
+
+enum CRStatus cr_simple_sel_dump_attr_sel_list (CRSimpleSel const *a_this) ;
+
+enum CRStatus cr_simple_sel_compute_specificity (CRSimpleSel *a_this) ;
+
+void cr_simple_sel_destroy (CRSimpleSel *a_this) ;
+
+G_END_DECLS
+
+
+#endif /*__CR_SIMPLE_SEL_H__*/
diff --git a/src/st/croco/cr-statement.c b/src/st/croco/cr-statement.c
new file mode 100644
index 0000000..eaeb49f
--- /dev/null
+++ b/src/st/croco/cr-statement.c
@@ -0,0 +1,2784 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli.
+ * See COPYRIGHTS files for copyrights information.
+ */
+
+#include <string.h>
+#include "cr-statement.h"
+#include "cr-parser.h"
+
+/**
+ *@file
+ *Definition of the #CRStatement class.
+ */
+
+#define DECLARATION_INDENT_NB 2
+
+static void cr_statement_clear (CRStatement * a_this);
+
+static void
+parse_font_face_start_font_face_cb (CRDocHandler * a_this,
+ CRParsingLocation *a_location)
+{
+ CRStatement *stmt = NULL;
+ enum CRStatus status = CR_OK;
+
+ stmt = cr_statement_new_at_font_face_rule (NULL, NULL);
+ g_return_if_fail (stmt);
+
+ status = cr_doc_handler_set_ctxt (a_this, stmt);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_font_face_unrecoverable_error_cb (CRDocHandler * a_this)
+{
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+ enum CRStatus status = CR_OK;
+
+ g_return_if_fail (a_this);
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr);
+ if (status != CR_OK) {
+ cr_utils_trace_info ("Couldn't get parsing context. "
+ "This may lead to some memory leaks.");
+ return;
+ }
+ if (stmt) {
+ cr_statement_destroy (stmt);
+ cr_doc_handler_set_ctxt (a_this, NULL);
+ return;
+ }
+}
+
+static void
+parse_font_face_property_cb (CRDocHandler * a_this,
+ CRString * a_name,
+ CRTerm * a_value, gboolean a_important)
+{
+ enum CRStatus status = CR_OK;
+ CRString *name = NULL;
+ CRDeclaration *decl = NULL;
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+
+ g_return_if_fail (a_this && a_name);
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr);
+ g_return_if_fail (status == CR_OK && stmt);
+ g_return_if_fail (stmt->type == AT_FONT_FACE_RULE_STMT);
+
+ name = cr_string_dup (a_name) ;
+ g_return_if_fail (name);
+ decl = cr_declaration_new (stmt, name, a_value);
+ if (!decl) {
+ cr_utils_trace_info ("cr_declaration_new () failed.");
+ goto error;
+ }
+ name = NULL;
+
+ stmt->kind.font_face_rule->decl_list =
+ cr_declaration_append (stmt->kind.font_face_rule->decl_list,
+ decl);
+ if (!stmt->kind.font_face_rule->decl_list)
+ goto error;
+ decl = NULL;
+
+ error:
+ if (decl) {
+ cr_declaration_unref (decl);
+ decl = NULL;
+ }
+ if (name) {
+ cr_string_destroy (name);
+ name = NULL;
+ }
+}
+
+static void
+parse_font_face_end_font_face_cb (CRDocHandler * a_this)
+{
+ CRStatement *result = NULL;
+ CRStatement **resultptr = NULL;
+ enum CRStatus status = CR_OK;
+
+ g_return_if_fail (a_this);
+
+ resultptr = &result;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) resultptr);
+ g_return_if_fail (status == CR_OK && result);
+ g_return_if_fail (result->type == AT_FONT_FACE_RULE_STMT);
+
+ status = cr_doc_handler_set_result (a_this, result);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_page_start_page_cb (CRDocHandler * a_this,
+ CRString * a_name,
+ CRString * a_pseudo_page,
+ CRParsingLocation *a_location)
+{
+ CRStatement *stmt = NULL;
+ enum CRStatus status = CR_OK;
+ CRString *page_name = NULL, *pseudo_name = NULL ;
+
+ if (a_name)
+ page_name = cr_string_dup (a_name) ;
+ if (a_pseudo_page)
+ pseudo_name = cr_string_dup (a_pseudo_page) ;
+
+ stmt = cr_statement_new_at_page_rule (NULL, NULL,
+ page_name,
+ pseudo_name);
+ page_name = NULL ;
+ pseudo_name = NULL ;
+ g_return_if_fail (stmt);
+ status = cr_doc_handler_set_ctxt (a_this, stmt);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_page_unrecoverable_error_cb (CRDocHandler * a_this)
+{
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+ enum CRStatus status = CR_OK;
+
+ g_return_if_fail (a_this);
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr);
+ if (status != CR_OK) {
+ cr_utils_trace_info ("Couldn't get parsing context. "
+ "This may lead to some memory leaks.");
+ return;
+ }
+ if (stmt) {
+ cr_statement_destroy (stmt);
+ stmt = NULL;
+ cr_doc_handler_set_ctxt (a_this, NULL);
+ }
+}
+
+static void
+parse_page_property_cb (CRDocHandler * a_this,
+ CRString * a_name,
+ CRTerm * a_expression, gboolean a_important)
+{
+ CRString *name = NULL;
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+ CRDeclaration *decl = NULL;
+ enum CRStatus status = CR_OK;
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr);
+ g_return_if_fail (status == CR_OK && stmt->type == AT_PAGE_RULE_STMT);
+
+ name = cr_string_dup (a_name);
+ g_return_if_fail (name);
+
+ decl = cr_declaration_new (stmt, name, a_expression);
+ g_return_if_fail (decl);
+ decl->important = a_important;
+ stmt->kind.page_rule->decl_list =
+ cr_declaration_append (stmt->kind.page_rule->decl_list, decl);
+ g_return_if_fail (stmt->kind.page_rule->decl_list);
+}
+
+static void
+parse_page_end_page_cb (CRDocHandler * a_this,
+ CRString * a_name,
+ CRString * a_pseudo_page)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr);
+ g_return_if_fail (status == CR_OK && stmt);
+ g_return_if_fail (stmt->type == AT_PAGE_RULE_STMT);
+
+ status = cr_doc_handler_set_result (a_this, stmt);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_at_media_start_media_cb (CRDocHandler * a_this,
+ GList * a_media_list,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *at_media = NULL;
+ GList *media_list = NULL;
+
+ g_return_if_fail (a_this && a_this->priv);
+
+ if (a_media_list) {
+ /*duplicate media list */
+ media_list = cr_utils_dup_glist_of_cr_string
+ (a_media_list);
+ }
+
+ g_return_if_fail (media_list);
+
+ /*make sure cr_statement_new_at_media_rule works in this case. */
+ at_media = cr_statement_new_at_media_rule (NULL, NULL, media_list);
+
+ status = cr_doc_handler_set_ctxt (a_this, at_media);
+ g_return_if_fail (status == CR_OK);
+ status = cr_doc_handler_set_result (a_this, at_media);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_at_media_unrecoverable_error_cb (CRDocHandler * a_this)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+
+ g_return_if_fail (a_this);
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_result (a_this, (gpointer *) stmtptr);
+ if (status != CR_OK) {
+ cr_utils_trace_info ("Couldn't get parsing context. "
+ "This may lead to some memory leaks.");
+ return;
+ }
+ if (stmt) {
+ cr_statement_destroy (stmt);
+ stmt = NULL;
+ cr_doc_handler_set_ctxt (a_this, NULL);
+ cr_doc_handler_set_result (a_this, NULL);
+ }
+}
+
+static void
+parse_at_media_start_selector_cb (CRDocHandler * a_this,
+ CRSelector * a_sellist)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *at_media = NULL;
+ CRStatement **at_media_ptr = NULL;
+ CRStatement *ruleset = NULL;
+
+ g_return_if_fail (a_this && a_this->priv && a_sellist);
+
+ at_media_ptr = &at_media;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) at_media_ptr);
+ g_return_if_fail (status == CR_OK && at_media);
+ g_return_if_fail (at_media->type == AT_MEDIA_RULE_STMT);
+ ruleset = cr_statement_new_ruleset (NULL, a_sellist, NULL, at_media);
+ g_return_if_fail (ruleset);
+ status = cr_doc_handler_set_ctxt (a_this, ruleset);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_at_media_property_cb (CRDocHandler * a_this,
+ CRString * a_name, CRTerm * a_value,
+ gboolean a_important)
+{
+ enum CRStatus status = CR_OK;
+
+ /*
+ *the current ruleset stmt, child of the
+ *current at-media being parsed.
+ */
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+ CRDeclaration *decl = NULL;
+ CRString *name = NULL;
+
+ g_return_if_fail (a_this && a_name);
+
+ name = cr_string_dup (a_name) ;
+ g_return_if_fail (name);
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_ctxt (a_this,
+ (gpointer *) stmtptr);
+ g_return_if_fail (status == CR_OK && stmt);
+ g_return_if_fail (stmt->type == RULESET_STMT);
+
+ decl = cr_declaration_new (stmt, name, a_value);
+ g_return_if_fail (decl);
+ decl->important = a_important;
+ status = cr_statement_ruleset_append_decl (stmt, decl);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_at_media_end_selector_cb (CRDocHandler * a_this,
+ CRSelector * a_sellist)
+{
+ enum CRStatus status = CR_OK;
+
+ /*
+ *the current ruleset stmt, child of the
+ *current at-media being parsed.
+ */
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+
+ g_return_if_fail (a_this && a_sellist);
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_ctxt (a_this, (gpointer *) stmtptr);
+ g_return_if_fail (status == CR_OK && stmt
+ && stmt->type == RULESET_STMT);
+ g_return_if_fail (stmt->kind.ruleset->parent_media_rule);
+
+ status = cr_doc_handler_set_ctxt
+ (a_this, stmt->kind.ruleset->parent_media_rule);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_at_media_end_media_cb (CRDocHandler * a_this,
+ GList * a_media_list)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *at_media = NULL;
+ CRStatement **at_media_ptr = NULL;
+
+ g_return_if_fail (a_this && a_this->priv);
+
+ at_media_ptr = &at_media;
+ status = cr_doc_handler_get_ctxt (a_this,
+ (gpointer *) at_media_ptr);
+ g_return_if_fail (status == CR_OK && at_media);
+ status = cr_doc_handler_set_result (a_this, at_media);
+}
+
+static void
+parse_ruleset_start_selector_cb (CRDocHandler * a_this,
+ CRSelector * a_sellist)
+{
+ CRStatement *ruleset = NULL;
+
+ g_return_if_fail (a_this && a_this->priv && a_sellist);
+
+ ruleset = cr_statement_new_ruleset (NULL, a_sellist, NULL, NULL);
+ g_return_if_fail (ruleset);
+
+ cr_doc_handler_set_result (a_this, ruleset);
+}
+
+static void
+parse_ruleset_unrecoverable_error_cb (CRDocHandler * a_this)
+{
+ CRStatement *stmt = NULL;
+ CRStatement **stmtptr = NULL;
+ enum CRStatus status = CR_OK;
+
+ stmtptr = &stmt;
+ status = cr_doc_handler_get_result (a_this, (gpointer *) stmtptr);
+ if (status != CR_OK) {
+ cr_utils_trace_info ("Couldn't get parsing context. "
+ "This may lead to some memory leaks.");
+ return;
+ }
+ if (stmt) {
+ cr_statement_destroy (stmt);
+ stmt = NULL;
+ cr_doc_handler_set_result (a_this, NULL);
+ }
+}
+
+static void
+parse_ruleset_property_cb (CRDocHandler * a_this,
+ CRString * a_name,
+ CRTerm * a_value, gboolean a_important)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *ruleset = NULL;
+ CRStatement **rulesetptr = NULL;
+ CRDeclaration *decl = NULL;
+ CRString *stringue = NULL;
+
+ g_return_if_fail (a_this && a_this->priv && a_name);
+
+ stringue = cr_string_dup (a_name);
+ g_return_if_fail (stringue);
+
+ rulesetptr = &ruleset;
+ status = cr_doc_handler_get_result (a_this, (gpointer *) rulesetptr);
+ g_return_if_fail (status == CR_OK
+ && ruleset
+ && ruleset->type == RULESET_STMT);
+
+ decl = cr_declaration_new (ruleset, stringue, a_value);
+ g_return_if_fail (decl);
+ decl->important = a_important;
+ status = cr_statement_ruleset_append_decl (ruleset, decl);
+ g_return_if_fail (status == CR_OK);
+}
+
+static void
+parse_ruleset_end_selector_cb (CRDocHandler * a_this,
+ CRSelector * a_sellist)
+{
+ CRStatement *result = NULL;
+ CRStatement **resultptr = NULL;
+ enum CRStatus status = CR_OK;
+
+ g_return_if_fail (a_this && a_sellist);
+
+ resultptr = &result;
+ status = cr_doc_handler_get_result (a_this, (gpointer *) resultptr);
+
+ g_return_if_fail (status == CR_OK
+ && result
+ && result->type == RULESET_STMT);
+}
+
+static void
+cr_statement_clear (CRStatement * a_this)
+{
+ g_return_if_fail (a_this);
+
+ switch (a_this->type) {
+ case AT_RULE_STMT:
+ break;
+ case RULESET_STMT:
+ if (!a_this->kind.ruleset)
+ return;
+ if (a_this->kind.ruleset->sel_list) {
+ cr_selector_unref (a_this->kind.ruleset->sel_list);
+ a_this->kind.ruleset->sel_list = NULL;
+ }
+ if (a_this->kind.ruleset->decl_list) {
+ cr_declaration_destroy
+ (a_this->kind.ruleset->decl_list);
+ a_this->kind.ruleset->decl_list = NULL;
+ }
+ g_free (a_this->kind.ruleset);
+ a_this->kind.ruleset = NULL;
+ break;
+
+ case AT_IMPORT_RULE_STMT:
+ if (!a_this->kind.import_rule)
+ return;
+ if (a_this->kind.import_rule->url) {
+ cr_string_destroy
+ (a_this->kind.import_rule->url) ;
+ a_this->kind.import_rule->url = NULL;
+ }
+ g_free (a_this->kind.import_rule);
+ a_this->kind.import_rule = NULL;
+ break;
+
+ case AT_MEDIA_RULE_STMT:
+ if (!a_this->kind.media_rule)
+ return;
+ if (a_this->kind.media_rule->rulesets) {
+ cr_statement_destroy
+ (a_this->kind.media_rule->rulesets);
+ a_this->kind.media_rule->rulesets = NULL;
+ }
+ if (a_this->kind.media_rule->media_list) {
+ GList *cur = NULL;
+
+ for (cur = a_this->kind.media_rule->media_list;
+ cur; cur = cur->next) {
+ if (cur->data) {
+ cr_string_destroy ((CRString *) cur->data);
+ cur->data = NULL;
+ }
+
+ }
+ g_list_free (a_this->kind.media_rule->media_list);
+ a_this->kind.media_rule->media_list = NULL;
+ }
+ g_free (a_this->kind.media_rule);
+ a_this->kind.media_rule = NULL;
+ break;
+
+ case AT_PAGE_RULE_STMT:
+ if (!a_this->kind.page_rule)
+ return;
+
+ if (a_this->kind.page_rule->decl_list) {
+ cr_declaration_destroy
+ (a_this->kind.page_rule->decl_list);
+ a_this->kind.page_rule->decl_list = NULL;
+ }
+ if (a_this->kind.page_rule->name) {
+ cr_string_destroy
+ (a_this->kind.page_rule->name);
+ a_this->kind.page_rule->name = NULL;
+ }
+ if (a_this->kind.page_rule->pseudo) {
+ cr_string_destroy
+ (a_this->kind.page_rule->pseudo);
+ a_this->kind.page_rule->pseudo = NULL;
+ }
+ g_free (a_this->kind.page_rule);
+ a_this->kind.page_rule = NULL;
+ break;
+
+ case AT_CHARSET_RULE_STMT:
+ if (!a_this->kind.charset_rule)
+ return;
+
+ if (a_this->kind.charset_rule->charset) {
+ cr_string_destroy
+ (a_this->kind.charset_rule->charset);
+ a_this->kind.charset_rule->charset = NULL;
+ }
+ g_free (a_this->kind.charset_rule);
+ a_this->kind.charset_rule = NULL;
+ break;
+
+ case AT_FONT_FACE_RULE_STMT:
+ if (!a_this->kind.font_face_rule)
+ return;
+
+ if (a_this->kind.font_face_rule->decl_list) {
+ cr_declaration_unref
+ (a_this->kind.font_face_rule->decl_list);
+ a_this->kind.font_face_rule->decl_list = NULL;
+ }
+ g_free (a_this->kind.font_face_rule);
+ a_this->kind.font_face_rule = NULL;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * cr_statement_ruleset_to_string:
+ *
+ *@a_this: the current instance of #CRStatement
+ *@a_indent: the number of whitespace to use for indentation
+ *
+ *Serializes the ruleset statement into a string
+ *
+ *Returns the newly allocated serialised string. Must be freed
+ *by the caller, using g_free().
+ */
+static gchar *
+cr_statement_ruleset_to_string (CRStatement const * a_this, glong a_indent)
+{
+ GString *stringue = NULL;
+ gchar *tmp_str = NULL,
+ *result = NULL;
+
+ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT, NULL);
+
+ stringue = g_string_new (NULL);
+
+ if (a_this->kind.ruleset->sel_list) {
+ if (a_indent)
+ cr_utils_dump_n_chars2 (' ', stringue, a_indent);
+
+ tmp_str =
+ (gchar *) cr_selector_to_string (a_this->kind.ruleset->
+ sel_list);
+ if (tmp_str) {
+ g_string_append (stringue, tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ g_string_append (stringue, " {\n");
+ if (a_this->kind.ruleset->decl_list) {
+ tmp_str = (gchar *) cr_declaration_list_to_string2
+ (a_this->kind.ruleset->decl_list,
+ a_indent + DECLARATION_INDENT_NB, TRUE);
+ if (tmp_str) {
+ g_string_append (stringue, tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ g_string_append (stringue, "\n");
+ cr_utils_dump_n_chars2 (' ', stringue, a_indent);
+ }
+ g_string_append (stringue, "}");
+ result = g_string_free (stringue, FALSE);
+
+ if (tmp_str) {
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ return result;
+}
+
+
+/**
+ * cr_statement_font_face_rule_to_string:
+ *
+ *@a_this: the current instance of #CRStatement to consider
+ *It must be a font face rule statement.
+ *@a_indent: the number of white spaces of indentation.
+ *
+ *Serializes a font face rule statement into a string.
+ *
+ *Returns the serialized string. Must be deallocated by the caller
+ *using g_free().
+ */
+static gchar *
+cr_statement_font_face_rule_to_string (CRStatement const * a_this,
+ glong a_indent)
+{
+ gchar *result = NULL, *tmp_str = NULL ;
+ GString *stringue = NULL ;
+
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_FONT_FACE_RULE_STMT,
+ NULL);
+
+ if (a_this->kind.font_face_rule->decl_list) {
+ stringue = g_string_new (NULL) ;
+ g_return_val_if_fail (stringue, NULL) ;
+ if (a_indent)
+ cr_utils_dump_n_chars2 (' ', stringue,
+ a_indent);
+ g_string_append (stringue, "@font-face {\n");
+ tmp_str = (gchar *) cr_declaration_list_to_string2
+ (a_this->kind.font_face_rule->decl_list,
+ a_indent + DECLARATION_INDENT_NB, TRUE) ;
+ if (tmp_str) {
+ g_string_append (stringue,
+ tmp_str) ;
+ g_free (tmp_str) ;
+ tmp_str = NULL ;
+ }
+ g_string_append (stringue, "\n}");
+ }
+ if (stringue) {
+ result = g_string_free (stringue, FALSE);
+ stringue = NULL ;
+ }
+ return result ;
+}
+
+
+/**
+ * cr_statement_charset_to_string:
+ *
+ *Serialises an \@charset statement into a string.
+ *@a_this: the statement to serialize.
+ *@a_indent: the number of indentation spaces
+ *
+ *Returns the serialized charset statement. Must be
+ *freed by the caller using g_free().
+ */
+static gchar *
+cr_statement_charset_to_string (CRStatement const *a_this,
+ gulong a_indent)
+{
+ gchar *str = NULL ;
+ GString *stringue = NULL ;
+
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_CHARSET_RULE_STMT,
+ NULL) ;
+
+ if (a_this->kind.charset_rule
+ && a_this->kind.charset_rule->charset
+ && a_this->kind.charset_rule->charset->stryng
+ && a_this->kind.charset_rule->charset->stryng->str) {
+ str = g_strndup (a_this->kind.charset_rule->charset->stryng->str,
+ a_this->kind.charset_rule->charset->stryng->len);
+ g_return_val_if_fail (str, NULL);
+ stringue = g_string_new (NULL) ;
+ g_return_val_if_fail (stringue, NULL) ;
+ cr_utils_dump_n_chars2 (' ', stringue, a_indent);
+ g_string_append_printf (stringue,
+ "@charset \"%s\" ;", str);
+ if (str) {
+ g_free (str);
+ str = NULL;
+ }
+ }
+ if (stringue) {
+ str = g_string_free (stringue, FALSE);
+ }
+ return str ;
+}
+
+
+/**
+ * cr_statement_at_page_rule_to_string:
+ *
+ *Serialises the at page rule statement into a string
+ *@a_this: the current instance of #CRStatement. Must
+ *be an "\@page" rule statement.
+ *
+ *Returns the serialized string. Must be freed by the caller
+ */
+static gchar *
+cr_statement_at_page_rule_to_string (CRStatement const *a_this,
+ gulong a_indent)
+{
+ GString *stringue = NULL;
+ gchar *result = NULL ;
+
+ stringue = g_string_new (NULL) ;
+
+ cr_utils_dump_n_chars2 (' ', stringue, a_indent) ;
+ g_string_append (stringue, "@page");
+ if (a_this->kind.page_rule->name
+ && a_this->kind.page_rule->name->stryng) {
+ g_string_append_printf
+ (stringue, " %s",
+ a_this->kind.page_rule->name->stryng->str) ;
+ } else {
+ g_string_append (stringue, " ");
+ }
+ if (a_this->kind.page_rule->pseudo
+ && a_this->kind.page_rule->pseudo->stryng) {
+ g_string_append_printf
+ (stringue, " :%s",
+ a_this->kind.page_rule->pseudo->stryng->str) ;
+ }
+ if (a_this->kind.page_rule->decl_list) {
+ gchar *str = NULL ;
+ g_string_append (stringue, " {\n");
+ str = (gchar *) cr_declaration_list_to_string2
+ (a_this->kind.page_rule->decl_list,
+ a_indent + DECLARATION_INDENT_NB, TRUE) ;
+ if (str) {
+ g_string_append (stringue, str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+ g_string_append (stringue, "\n}\n");
+ }
+ result = g_string_free (stringue, FALSE) ;
+ stringue = NULL ;
+ return result ;
+}
+
+
+/**
+ *Serializes an \@media statement.
+ *@param a_this the current instance of #CRStatement
+ *@param a_indent the number of spaces of indentation.
+ *@return the serialized \@media statement. Must be freed
+ *by the caller using g_free().
+ */
+static gchar *
+cr_statement_media_rule_to_string (CRStatement const *a_this,
+ gulong a_indent)
+{
+ gchar *str = NULL ;
+ GString *stringue = NULL ;
+ GList const *cur = NULL;
+
+ g_return_val_if_fail (a_this->type == AT_MEDIA_RULE_STMT,
+ NULL);
+
+ if (a_this->kind.media_rule) {
+ stringue = g_string_new (NULL) ;
+ cr_utils_dump_n_chars2 (' ', stringue, a_indent);
+ g_string_append (stringue, "@media");
+
+ for (cur = a_this->kind.media_rule->media_list; cur;
+ cur = cur->next) {
+ if (cur->data) {
+ gchar *str2 = cr_string_dup2
+ ((CRString const *) cur->data);
+
+ if (str2) {
+ if (cur->prev) {
+ g_string_append
+ (stringue,
+ ",");
+ }
+ g_string_append_printf
+ (stringue,
+ " %s", str2);
+ g_free (str2);
+ str2 = NULL;
+ }
+ }
+ }
+ g_string_append (stringue, " {\n");
+ str = cr_statement_list_to_string
+ (a_this->kind.media_rule->rulesets,
+ a_indent + DECLARATION_INDENT_NB) ;
+ if (str) {
+ g_string_append (stringue, str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+ g_string_append (stringue, "\n}");
+ }
+ if (stringue) {
+ str = g_string_free (stringue, FALSE) ;
+ }
+ return str ;
+}
+
+
+static gchar *
+cr_statement_import_rule_to_string (CRStatement const *a_this,
+ gulong a_indent)
+{
+ GString *stringue = NULL ;
+ gchar *str = NULL;
+
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_IMPORT_RULE_STMT
+ && a_this->kind.import_rule,
+ NULL) ;
+
+ if (a_this->kind.import_rule->url
+ && a_this->kind.import_rule->url->stryng) {
+ stringue = g_string_new (NULL) ;
+ g_return_val_if_fail (stringue, NULL) ;
+ str = g_strndup (a_this->kind.import_rule->url->stryng->str,
+ a_this->kind.import_rule->url->stryng->len);
+ cr_utils_dump_n_chars2 (' ', stringue, a_indent);
+ if (str) {
+ g_string_append_printf (stringue,
+ "@import url(\"%s\")",
+ str);
+ g_free (str);
+ str = NULL ;
+ } else /*there is no url, so no import rule, get out! */
+ return NULL;
+
+ if (a_this->kind.import_rule->media_list) {
+ GList const *cur = NULL;
+
+ for (cur = a_this->kind.import_rule->media_list;
+ cur; cur = cur->next) {
+ if (cur->data) {
+ CRString const *crstr = cur->data;
+
+ if (cur->prev) {
+ g_string_append
+ (stringue, ", ");
+ }
+ if (crstr
+ && crstr->stryng
+ && crstr->stryng->str) {
+ g_string_append_len
+ (stringue,
+ crstr->stryng->str,
+ crstr->stryng->len) ;
+ }
+ }
+ }
+ }
+ g_string_append (stringue, " ;");
+ }
+ if (stringue) {
+ str = g_string_free (stringue, FALSE) ;
+ stringue = NULL ;
+ }
+ return str ;
+}
+
+
+/*******************
+ *public functions
+ ******************/
+
+/**
+ * cr_statement_does_buf_parses_against_core:
+ *
+ *@a_buf: the buffer to parse.
+ *@a_encoding: the character encoding of a_buf.
+ *
+ *Tries to parse a buffer and says whether if the content of the buffer
+ *is a css statement as defined by the "Core CSS Grammar" (chapter 4 of the
+ *css spec) or not.
+ *
+ *Returns TRUE if the buffer parses against the core grammar, false otherwise.
+ */
+gboolean
+cr_statement_does_buf_parses_against_core (const guchar * a_buf,
+ enum CREncoding a_encoding)
+{
+ CRParser *parser = NULL;
+ enum CRStatus status = CR_OK;
+ gboolean result = FALSE;
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_encoding, FALSE);
+ g_return_val_if_fail (parser, FALSE);
+
+ status = cr_parser_set_use_core_grammar (parser, TRUE);
+ if (status != CR_OK) {
+ goto cleanup;
+ }
+
+ status = cr_parser_parse_statement_core (parser);
+ if (status == CR_OK) {
+ result = TRUE;
+ }
+
+ cleanup:
+ if (parser) {
+ cr_parser_destroy (parser);
+ }
+
+ return result;
+}
+
+/**
+ * cr_statement_parse_from_buf:
+ *
+ *@a_buf: the buffer to parse.
+ *@a_encoding: the character encoding of a_buf.
+ *
+ *Parses a buffer that contains a css statement and returns
+ *an instance of #CRStatement in case of successful parsing.
+ *TODO: at support of "\@import" rules.
+ *
+ *Returns the newly built instance of #CRStatement in case
+ *of successful parsing, NULL otherwise.
+ */
+CRStatement *
+cr_statement_parse_from_buf (const guchar * a_buf, enum CREncoding a_encoding)
+{
+ CRStatement *result = NULL;
+
+ /*
+ *The strategy of this function is "brute force".
+ *It tries to parse all the types of CRStatement it knows about.
+ *I could do this a smarter way but I don't have the time now.
+ *I think I will revisit this when time of performances and
+ *pull based incremental parsing comes.
+ */
+
+ result = cr_statement_ruleset_parse_from_buf (a_buf, a_encoding);
+ if (!result) {
+ result = cr_statement_at_charset_rule_parse_from_buf
+ (a_buf, a_encoding);
+ } else {
+ goto out;
+ }
+
+ if (!result) {
+ result = cr_statement_at_media_rule_parse_from_buf
+ (a_buf, a_encoding);
+ } else {
+ goto out;
+ }
+
+ if (!result) {
+ result = cr_statement_at_charset_rule_parse_from_buf
+ (a_buf, a_encoding);
+ } else {
+ goto out;
+ }
+
+ if (!result) {
+ result = cr_statement_font_face_rule_parse_from_buf
+ (a_buf, a_encoding);
+
+ } else {
+ goto out;
+ }
+
+ if (!result) {
+ result = cr_statement_at_page_rule_parse_from_buf
+ (a_buf, a_encoding);
+ } else {
+ goto out;
+ }
+
+ if (!result) {
+ result = cr_statement_at_import_rule_parse_from_buf
+ (a_buf, a_encoding);
+ } else {
+ goto out;
+ }
+
+ out:
+ return result;
+}
+
+/**
+ * cr_statement_ruleset_parse_from_buf:
+ *
+ *@a_buf: the buffer to parse.
+ *@a_enc: the character encoding of a_buf.
+ *
+ *Parses a buffer that contains a ruleset statement an instantiates
+ *a #CRStatement of type RULESET_STMT.
+ *
+ *Returns the newly built instance of #CRStatement in case of successful parsing,
+ *NULL otherwise.
+ */
+CRStatement *
+cr_statement_ruleset_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_enc)
+{
+ enum CRStatus status = CR_OK;
+ CRStatement *result = NULL;
+ CRStatement **resultptr = NULL;
+ CRParser *parser = NULL;
+ CRDocHandler *sac_handler = NULL;
+
+ g_return_val_if_fail (a_buf, NULL);
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_enc, FALSE);
+
+ g_return_val_if_fail (parser, NULL);
+
+ sac_handler = cr_doc_handler_new ();
+ g_return_val_if_fail (parser, NULL);
+
+ sac_handler->start_selector = parse_ruleset_start_selector_cb;
+ sac_handler->end_selector = parse_ruleset_end_selector_cb;
+ sac_handler->property = parse_ruleset_property_cb;
+ sac_handler->unrecoverable_error =
+ parse_ruleset_unrecoverable_error_cb;
+
+ cr_parser_set_sac_handler (parser, sac_handler);
+ cr_parser_try_to_skip_spaces_and_comments (parser);
+ status = cr_parser_parse_ruleset (parser);
+ if (status != CR_OK) {
+ goto cleanup;
+ }
+
+ resultptr = &result;
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer *) resultptr);
+ if (!((status == CR_OK) && result)) {
+ if (result) {
+ cr_statement_destroy (result);
+ result = NULL;
+ }
+ }
+
+ cleanup:
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ sac_handler = NULL ;
+ }
+ if (sac_handler) {
+ cr_doc_handler_unref (sac_handler);
+ sac_handler = NULL;
+ }
+ return result;
+}
+
+/**
+ * cr_statement_new_ruleset:
+ *
+ *@a_sel_list: the list of #CRSimpleSel (selectors)
+ *the rule applies to.
+ *@a_decl_list: the list of instances of #CRDeclaration
+ *that composes the ruleset.
+ *@a_media_types: a list of instances of GString that
+ *describe the media list this ruleset applies to.
+ *
+ *Creates a new instance of #CRStatement of type
+ *#CRRulSet.
+ *
+ *Returns the new instance of #CRStatement or NULL if something
+ *went wrong.
+ */
+CRStatement *
+cr_statement_new_ruleset (CRStyleSheet * a_sheet,
+ CRSelector * a_sel_list,
+ CRDeclaration * a_decl_list,
+ CRStatement * a_parent_media_rule)
+{
+ CRStatement *result = NULL;
+
+ g_return_val_if_fail (a_sel_list, NULL);
+
+ if (a_parent_media_rule) {
+ g_return_val_if_fail
+ (a_parent_media_rule->type == AT_MEDIA_RULE_STMT,
+ NULL);
+ g_return_val_if_fail (a_parent_media_rule->kind.media_rule,
+ NULL);
+ }
+
+ result = g_try_malloc (sizeof (CRStatement));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRStatement));
+ result->type = RULESET_STMT;
+ result->kind.ruleset = g_try_malloc (sizeof (CRRuleSet));
+
+ if (!result->kind.ruleset) {
+ cr_utils_trace_info ("Out of memory");
+ if (result)
+ g_free (result);
+ return NULL;
+ }
+
+ memset (result->kind.ruleset, 0, sizeof (CRRuleSet));
+ result->kind.ruleset->sel_list = a_sel_list;
+ if (a_sel_list)
+ cr_selector_ref (a_sel_list);
+ result->kind.ruleset->decl_list = a_decl_list;
+
+ if (a_parent_media_rule) {
+ result->kind.ruleset->parent_media_rule = a_parent_media_rule;
+ a_parent_media_rule->kind.media_rule->rulesets =
+ cr_statement_append
+ (a_parent_media_rule->kind.media_rule->rulesets,
+ result);
+ }
+
+ cr_statement_set_parent_sheet (result, a_sheet);
+
+ return result;
+}
+
+/**
+ * cr_statement_at_media_rule_parse_from_buf:
+ *
+ *@a_buf: the input to parse.
+ *@a_enc: the encoding of the buffer.
+ *
+ *Parses a buffer that contains an "\@media" declaration
+ *and builds an \@media css statement.
+ *
+ *Returns the \@media statement, or NULL if the buffer could not
+ *be successfully parsed.
+ */
+CRStatement *
+cr_statement_at_media_rule_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_enc)
+{
+ CRParser *parser = NULL;
+ CRStatement *result = NULL;
+ CRStatement **resultptr = NULL;
+ CRDocHandler *sac_handler = NULL;
+ enum CRStatus status = CR_OK;
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_enc, FALSE);
+ if (!parser) {
+ cr_utils_trace_info ("Instantiation of the parser failed");
+ goto cleanup;
+ }
+
+ sac_handler = cr_doc_handler_new ();
+ if (!sac_handler) {
+ cr_utils_trace_info
+ ("Instantiation of the sac handler failed");
+ goto cleanup;
+ }
+
+ sac_handler->start_media = parse_at_media_start_media_cb;
+ sac_handler->start_selector = parse_at_media_start_selector_cb;
+ sac_handler->property = parse_at_media_property_cb;
+ sac_handler->end_selector = parse_at_media_end_selector_cb;
+ sac_handler->end_media = parse_at_media_end_media_cb;
+ sac_handler->unrecoverable_error =
+ parse_at_media_unrecoverable_error_cb;
+
+ status = cr_parser_set_sac_handler (parser, sac_handler);
+ if (status != CR_OK)
+ goto cleanup;
+
+ status = cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ status = cr_parser_parse_media (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ resultptr = &result;
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer *) resultptr);
+ if (status != CR_OK)
+ goto cleanup;
+
+ cleanup:
+
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ sac_handler = NULL ;
+ }
+ if (sac_handler) {
+ cr_doc_handler_unref (sac_handler);
+ sac_handler = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_statement_new_at_media_rule:
+ *
+ *@a_ruleset: the ruleset statements contained
+ *in the \@media rule.
+ *@a_media: the media string list. A list of GString pointers.
+ *
+ *Instantiates an instance of #CRStatement of type
+ *AT_MEDIA_RULE_STMT (\@media ruleset).
+ *
+ */
+CRStatement *
+cr_statement_new_at_media_rule (CRStyleSheet * a_sheet,
+ CRStatement * a_rulesets, GList * a_media)
+{
+ CRStatement *result = NULL,
+ *cur = NULL;
+
+ if (a_rulesets)
+ g_return_val_if_fail (a_rulesets->type == RULESET_STMT, NULL);
+
+ result = g_try_malloc (sizeof (CRStatement));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRStatement));
+ result->type = AT_MEDIA_RULE_STMT;
+
+ result->kind.media_rule = g_try_malloc (sizeof (CRAtMediaRule));
+ if (!result->kind.media_rule) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+ memset (result->kind.media_rule, 0, sizeof (CRAtMediaRule));
+ result->kind.media_rule->rulesets = a_rulesets;
+ for (cur = a_rulesets; cur; cur = cur->next) {
+ if (cur->type != RULESET_STMT || !cur->kind.ruleset) {
+ cr_utils_trace_info ("Bad parameter a_rulesets. "
+ "It should be a list of "
+ "correct ruleset statement only !");
+ goto error;
+ }
+ cur->kind.ruleset->parent_media_rule = result;
+ }
+
+ result->kind.media_rule->media_list = a_media;
+ if (a_sheet) {
+ cr_statement_set_parent_sheet (result, a_sheet);
+ }
+
+ return result;
+
+ error:
+ return NULL;
+}
+
+/**
+ * cr_statement_new_at_import_rule:
+ *
+ *@a_url: the url to connect to the get the file
+ *to be imported.
+ *@a_sheet: the imported parsed stylesheet.
+ *
+ *Creates a new instance of #CRStatment of type
+ *#CRAtImportRule.
+ *
+ *Returns the newly built instance of #CRStatement.
+ */
+CRStatement *
+cr_statement_new_at_import_rule (CRStyleSheet * a_container_sheet,
+ CRString * a_url,
+ GList * a_media_list,
+ CRStyleSheet * a_imported_sheet)
+{
+ CRStatement *result = NULL;
+
+ result = g_try_malloc (sizeof (CRStatement));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRStatement));
+ result->type = AT_IMPORT_RULE_STMT;
+
+ result->kind.import_rule = g_try_malloc (sizeof (CRAtImportRule));
+
+ if (!result->kind.import_rule) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+
+ memset (result->kind.import_rule, 0, sizeof (CRAtImportRule));
+ result->kind.import_rule->url = a_url;
+ result->kind.import_rule->media_list = a_media_list;
+ result->kind.import_rule->sheet = a_imported_sheet;
+ if (a_container_sheet)
+ cr_statement_set_parent_sheet (result, a_container_sheet);
+
+ return result;
+}
+
+/**
+ * cr_statement_at_import_rule_parse_from_buf:
+ *
+ *@a_buf: the buffer to parse.
+ *@a_encoding: the encoding of a_buf.
+ *
+ *Parses a buffer that contains an "\@import" rule and
+ *instantiate a #CRStatement of type AT_IMPORT_RULE_STMT
+ *
+ *Returns the newly built instance of #CRStatement in case of
+ *a successful parsing, NULL otherwise.
+ */
+CRStatement *
+cr_statement_at_import_rule_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_encoding)
+{
+ enum CRStatus status = CR_OK;
+ CRParser *parser = NULL;
+ CRStatement *result = NULL;
+ GList *media_list = NULL;
+ CRString *import_string = NULL;
+ CRParsingLocation location = {0} ;
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_encoding, FALSE);
+ if (!parser) {
+ cr_utils_trace_info ("Instantiation of parser failed.");
+ goto cleanup;
+ }
+
+ status = cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ status = cr_parser_parse_import (parser,
+ &media_list,
+ &import_string,
+ &location);
+ if (status != CR_OK || !import_string)
+ goto cleanup;
+
+ result = cr_statement_new_at_import_rule (NULL, import_string,
+ media_list, NULL);
+ if (result) {
+ cr_parsing_location_copy (&result->location,
+ &location) ;
+ import_string = NULL;
+ media_list = NULL;
+ }
+
+ cleanup:
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ }
+ if (media_list) {
+ for (; media_list;
+ media_list = g_list_next (media_list)) {
+ if (media_list->data) {
+ cr_string_destroy ((CRString*)media_list->data);
+ media_list->data = NULL;
+ }
+ }
+ g_list_free (media_list);
+ media_list = NULL;
+ }
+ if (import_string) {
+ cr_string_destroy (import_string);
+ import_string = NULL;
+ }
+
+ return result;
+}
+
+/**
+ * cr_statement_new_at_page_rule:
+ *
+ *@a_decl_list: a list of instances of #CRDeclarations
+ *which is actually the list of declarations that applies to
+ *this page rule.
+ *@a_selector: the page rule selector.
+ *
+ *Creates a new instance of #CRStatement of type
+ *#CRAtPageRule.
+ *
+ *Returns the newly built instance of #CRStatement or NULL
+ *in case of error.
+ */
+CRStatement *
+cr_statement_new_at_page_rule (CRStyleSheet * a_sheet,
+ CRDeclaration * a_decl_list,
+ CRString * a_name, CRString * a_pseudo)
+{
+ CRStatement *result = NULL;
+
+ result = g_try_malloc (sizeof (CRStatement));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRStatement));
+ result->type = AT_PAGE_RULE_STMT;
+
+ result->kind.page_rule = g_try_malloc (sizeof (CRAtPageRule));
+
+ if (!result->kind.page_rule) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+
+ memset (result->kind.page_rule, 0, sizeof (CRAtPageRule));
+ if (a_decl_list) {
+ result->kind.page_rule->decl_list = a_decl_list;
+ cr_declaration_ref (a_decl_list);
+ }
+ result->kind.page_rule->name = a_name;
+ result->kind.page_rule->pseudo = a_pseudo;
+ if (a_sheet)
+ cr_statement_set_parent_sheet (result, a_sheet);
+
+ return result;
+}
+
+/**
+ * cr_statement_at_page_rule_parse_from_buf:
+ *
+ *@a_buf: the character buffer to parse.
+ *@a_encoding: the character encoding of a_buf.
+ *
+ *Parses a buffer that contains an "\@page" production and,
+ *if the parsing succeeds, builds the page statement.
+ *
+ *Returns the newly built at page statement in case of successful parsing,
+ *NULL otherwise.
+ */
+CRStatement *
+cr_statement_at_page_rule_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_encoding)
+{
+ enum CRStatus status = CR_OK;
+ CRParser *parser = NULL;
+ CRDocHandler *sac_handler = NULL;
+ CRStatement *result = NULL;
+ CRStatement **resultptr = NULL;
+
+ g_return_val_if_fail (a_buf, NULL);
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_encoding, FALSE);
+ if (!parser) {
+ cr_utils_trace_info ("Instantiation of the parser failed.");
+ goto cleanup;
+ }
+
+ sac_handler = cr_doc_handler_new ();
+ if (!sac_handler) {
+ cr_utils_trace_info
+ ("Instantiation of the sac handler failed.");
+ goto cleanup;
+ }
+
+ sac_handler->start_page = parse_page_start_page_cb;
+ sac_handler->property = parse_page_property_cb;
+ sac_handler->end_page = parse_page_end_page_cb;
+ sac_handler->unrecoverable_error = parse_page_unrecoverable_error_cb;
+
+ status = cr_parser_set_sac_handler (parser, sac_handler);
+ if (status != CR_OK)
+ goto cleanup;
+
+ /*Now, invoke the parser to parse the "@page production" */
+ cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK)
+ goto cleanup;
+ status = cr_parser_parse_page (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ resultptr = &result;
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer *) resultptr);
+
+ cleanup:
+
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ sac_handler = NULL ;
+ }
+ if (sac_handler) {
+ cr_doc_handler_unref (sac_handler);
+ sac_handler = NULL;
+ }
+ return result;
+}
+
+/**
+ * cr_statement_new_at_charset_rule:
+ *
+ *@a_charset: the string representing the charset.
+ *Note that the newly built instance of #CRStatement becomes
+ *the owner of a_charset. The caller must not free a_charset !!!.
+ *
+ *Creates a new instance of #CRStatement of type
+ *#CRAtCharsetRule.
+ *
+ *Returns the newly built instance of #CRStatement or NULL
+ *if an error arises.
+ */
+CRStatement *
+cr_statement_new_at_charset_rule (CRStyleSheet * a_sheet,
+ CRString * a_charset)
+{
+ CRStatement *result = NULL;
+
+ g_return_val_if_fail (a_charset, NULL);
+
+ result = g_try_malloc (sizeof (CRStatement));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRStatement));
+ result->type = AT_CHARSET_RULE_STMT;
+
+ result->kind.charset_rule = g_try_malloc (sizeof (CRAtCharsetRule));
+
+ if (!result->kind.charset_rule) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+ memset (result->kind.charset_rule, 0, sizeof (CRAtCharsetRule));
+ result->kind.charset_rule->charset = a_charset;
+ cr_statement_set_parent_sheet (result, a_sheet);
+
+ return result;
+}
+
+/**
+ * cr_statement_at_charset_rule_parse_from_buf:
+ *
+ *@a_buf: the buffer to parse.
+ *@a_encoding: the character encoding of the buffer.
+ *
+ *Parses a buffer that contains an '\@charset' rule and
+ *creates an instance of #CRStatement of type AT_CHARSET_RULE_STMT.
+ *
+ *Returns the newly built instance of #CRStatement.
+ */
+CRStatement *
+cr_statement_at_charset_rule_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_encoding)
+{
+ enum CRStatus status = CR_OK;
+ CRParser *parser = NULL;
+ CRStatement *result = NULL;
+ CRString *charset = NULL;
+
+ g_return_val_if_fail (a_buf, NULL);
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_encoding, FALSE);
+ if (!parser) {
+ cr_utils_trace_info ("Instantiation of the parser failed.");
+ goto cleanup;
+ }
+
+ /*Now, invoke the parser to parse the "@charset production" */
+ cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK)
+ goto cleanup;
+ status = cr_parser_parse_charset (parser, &charset, NULL);
+ if (status != CR_OK || !charset)
+ goto cleanup;
+
+ result = cr_statement_new_at_charset_rule (NULL, charset);
+ if (result)
+ charset = NULL;
+
+ cleanup:
+
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ }
+ if (charset) {
+ cr_string_destroy (charset);
+ }
+
+ return result;
+}
+
+/**
+ * cr_statement_new_at_font_face_rule:
+ *
+ *@a_font_decls: a list of instances of #CRDeclaration. Each declaration
+ *is actually a font declaration.
+ *
+ *Creates an instance of #CRStatement of type #CRAtFontFaceRule.
+ *
+ *Returns the newly built instance of #CRStatement.
+ */
+CRStatement *
+cr_statement_new_at_font_face_rule (CRStyleSheet * a_sheet,
+ CRDeclaration * a_font_decls)
+{
+ CRStatement *result = NULL;
+
+ result = g_try_malloc (sizeof (CRStatement));
+
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRStatement));
+ result->type = AT_FONT_FACE_RULE_STMT;
+
+ result->kind.font_face_rule = g_try_malloc
+ (sizeof (CRAtFontFaceRule));
+
+ if (!result->kind.font_face_rule) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+ memset (result->kind.font_face_rule, 0, sizeof (CRAtFontFaceRule));
+
+ result->kind.font_face_rule->decl_list = a_font_decls;
+ if (a_sheet)
+ cr_statement_set_parent_sheet (result, a_sheet);
+
+ return result;
+}
+
+/**
+ * cr_statement_font_face_rule_parse_from_buf:
+ *
+ *
+ *@a_buf: the buffer to parse.
+ *@a_encoding: the character encoding of a_buf.
+ *
+ *Parses a buffer that contains an "\@font-face" rule and builds
+ *an instance of #CRStatement of type AT_FONT_FACE_RULE_STMT out of it.
+ *
+ *Returns the newly built instance of #CRStatement in case of successufull
+ *parsing, NULL otherwise.
+ */
+CRStatement *
+cr_statement_font_face_rule_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_encoding)
+{
+ CRStatement *result = NULL;
+ CRStatement **resultptr = NULL;
+ CRParser *parser = NULL;
+ CRDocHandler *sac_handler = NULL;
+ enum CRStatus status = CR_OK;
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_encoding, FALSE);
+ if (!parser)
+ goto cleanup;
+
+ sac_handler = cr_doc_handler_new ();
+ if (!sac_handler)
+ goto cleanup;
+
+ /*
+ *set sac callbacks here
+ */
+ sac_handler->start_font_face = parse_font_face_start_font_face_cb;
+ sac_handler->property = parse_font_face_property_cb;
+ sac_handler->end_font_face = parse_font_face_end_font_face_cb;
+ sac_handler->unrecoverable_error =
+ parse_font_face_unrecoverable_error_cb;
+
+ status = cr_parser_set_sac_handler (parser, sac_handler);
+ if (status != CR_OK)
+ goto cleanup;
+
+ /*
+ *cleanup spaces of comment that may be there before the real
+ *"@font-face" thing.
+ */
+ status = cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ status = cr_parser_parse_font_face (parser);
+ if (status != CR_OK)
+ goto cleanup;
+
+ resultptr = &result;
+ status = cr_doc_handler_get_result (sac_handler,
+ (gpointer *) resultptr);
+ if (status != CR_OK || !result)
+ goto cleanup;
+
+ cleanup:
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ sac_handler = NULL ;
+ }
+ if (sac_handler) {
+ cr_doc_handler_unref (sac_handler);
+ sac_handler = NULL;
+ }
+ return result;
+}
+
+/**
+ * cr_statement_set_parent_sheet:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *@a_sheet: the sheet that contains the current statement.
+ *
+ *Sets the container stylesheet.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_set_parent_sheet (CRStatement * a_this, CRStyleSheet * a_sheet)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+ a_this->parent_sheet = a_sheet;
+ return CR_OK;
+}
+
+/**
+ * cr_statement_get_parent_sheet:
+ *
+ *@a_this: the current #CRStatement.
+ *@a_sheet: out parameter. A pointer to the sheets that
+ *
+ *Gets the sheets that contains the current statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_get_parent_sheet (CRStatement * a_this, CRStyleSheet ** a_sheet)
+{
+ g_return_val_if_fail (a_this && a_sheet, CR_BAD_PARAM_ERROR);
+ *a_sheet = a_this->parent_sheet;
+ return CR_OK;
+}
+
+/**
+ * cr_statement_append:
+ *
+ *@a_this: the current instance of the statement list.
+ *@a_new: a_new the new instance of #CRStatement to append.
+ *
+ *Appends a new statement to the statement list.
+ *
+ *Returns the new list statement list, or NULL in cas of failure.
+ */
+CRStatement *
+cr_statement_append (CRStatement * a_this, CRStatement * a_new)
+{
+ CRStatement *cur = NULL;
+
+ g_return_val_if_fail (a_new, NULL);
+
+ if (!a_this) {
+ return a_new;
+ }
+
+ /*walk forward in the current list to find the tail list element */
+ for (cur = a_this; cur && cur->next; cur = cur->next) ;
+
+ cur->next = a_new;
+ a_new->prev = cur;
+
+ return a_this;
+}
+
+/**
+ * cr_statement_prepend:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *@a_new: the new statement to prepend.
+ *
+ *Prepends the an instance of #CRStatement to
+ *the current statement list.
+ *
+ *Returns the new list with the new statement prepended,
+ *or NULL in case of an error.
+ */
+CRStatement *
+cr_statement_prepend (CRStatement * a_this, CRStatement * a_new)
+{
+ CRStatement *cur = NULL;
+
+ g_return_val_if_fail (a_new, NULL);
+
+ if (!a_this)
+ return a_new;
+
+ a_new->next = a_this;
+ a_this->prev = a_new;
+
+ /*walk backward in the prepended list to find the head list element */
+ for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
+
+ return cur;
+}
+
+/**
+ * cr_statement_unlink:
+ *
+ *@a_this: the current statements list.
+ *@a_to_unlink: the statement to unlink from the list.
+ *
+ *Unlinks a statement from the statements list.
+ *
+ *Returns the new list where a_to_unlink has been unlinked
+ *from, or NULL in case of error.
+ */
+CRStatement *
+cr_statement_unlink (CRStatement * a_stmt)
+{
+ CRStatement *result = a_stmt;
+
+ g_return_val_if_fail (result, NULL);
+
+ /**
+ *Some sanity checks first
+ */
+ if (a_stmt->next) {
+ g_return_val_if_fail (a_stmt->next->prev == a_stmt, NULL);
+ }
+ if (a_stmt->prev) {
+ g_return_val_if_fail (a_stmt->prev->next == a_stmt, NULL);
+ }
+
+ /**
+ *Now, the real unlinking job.
+ */
+ if (a_stmt->next) {
+ a_stmt->next->prev = a_stmt->prev;
+ }
+ if (a_stmt->prev) {
+ a_stmt->prev->next = a_stmt->next;
+ }
+
+ if (a_stmt->parent_sheet
+ && a_stmt->parent_sheet->statements == a_stmt) {
+ a_stmt->parent_sheet->statements =
+ a_stmt->parent_sheet->statements->next;
+ }
+
+ a_stmt->next = NULL;
+ a_stmt->prev = NULL;
+ a_stmt->parent_sheet = NULL;
+
+ return result;
+}
+
+/**
+ * cr_statement_nr_rules:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *
+ *Gets the number of rules in the statement list;
+ *
+ *Returns number of rules in the statement list.
+ */
+gint
+cr_statement_nr_rules (CRStatement const * a_this)
+{
+ CRStatement const *cur = NULL;
+ int nr = 0;
+
+ g_return_val_if_fail (a_this, -1);
+
+ for (cur = a_this; cur; cur = cur->next)
+ nr++;
+ return nr;
+}
+
+/**
+ * cr_statement_get_from_list:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *@itemnr: the index into the statement list.
+ *
+ *Use an index to get a CRStatement from the statement list.
+ *
+ *Returns CRStatement at position itemnr, if itemnr > number of statements - 1,
+ *it will return NULL.
+ */
+CRStatement *
+cr_statement_get_from_list (CRStatement * a_this, int itemnr)
+{
+ CRStatement *cur = NULL;
+ int nr = 0;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ for (cur = a_this; cur; cur = cur->next)
+ if (nr++ == itemnr)
+ return cur;
+ return NULL;
+}
+
+/**
+ * cr_statement_ruleset_set_sel_list:
+ *
+ *@a_this: the current ruleset statement.
+ *@a_sel_list: the selector list to set. Note
+ *that this function increments the ref count of a_sel_list.
+ *The sel list will be destroyed at the destruction of the
+ *current instance of #CRStatement.
+ *
+ *Sets a selector list to a ruleset statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_ruleset_set_sel_list (CRStatement * a_this,
+ CRSelector * a_sel_list)
+{
+ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT,
+ CR_BAD_PARAM_ERROR);
+
+ if (a_this->kind.ruleset->sel_list)
+ cr_selector_unref (a_this->kind.ruleset->sel_list);
+
+ a_this->kind.ruleset->sel_list = a_sel_list;
+
+ if (a_sel_list)
+ cr_selector_ref (a_sel_list);
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_ruleset_get_declarations:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *@a_decl_list: out parameter. A pointer to the the returned
+ *list of declaration. Must not be NULL.
+ *
+ *Gets a pointer to the list of declaration contained
+ *in the ruleset statement.
+ *
+ *Returns CR_OK upon successful completion, an error code if something
+ *bad happened.
+ */
+enum CRStatus
+cr_statement_ruleset_get_declarations (CRStatement * a_this,
+ CRDeclaration ** a_decl_list)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == RULESET_STMT
+ && a_this->kind.ruleset
+ && a_decl_list, CR_BAD_PARAM_ERROR);
+
+ *a_decl_list = a_this->kind.ruleset->decl_list;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_ruleset_get_sel_list:
+ *
+ *@a_this: the current ruleset statement.
+ *@a_list: out parameter. The returned selector list,
+ *if and only if the function returned CR_OK.
+ *
+ *Gets a pointer to the selector list contained in
+ *the current ruleset statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_ruleset_get_sel_list (CRStatement const * a_this, CRSelector ** a_list)
+{
+ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT
+ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR);
+
+ *a_list = a_this->kind.ruleset->sel_list;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_ruleset_set_decl_list:
+ *
+ *@a_this: the current ruleset statement.
+ *@a_list: the declaration list to be added to the current
+ *ruleset statement.
+ *
+ *Sets a declaration list to the current ruleset statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_ruleset_set_decl_list (CRStatement * a_this,
+ CRDeclaration * a_list)
+{
+ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT
+ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR);
+
+ if (a_this->kind.ruleset->decl_list == a_list)
+ return CR_OK;
+
+ if (a_this->kind.ruleset->sel_list) {
+ cr_declaration_destroy (a_this->kind.ruleset->decl_list);
+ }
+
+ a_this->kind.ruleset->sel_list = NULL;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_ruleset_append_decl2:
+ *
+ *@a_this: the current statement.
+ *@a_prop: the property of the declaration.
+ *@a_value: the value of the declaration.
+ *
+ *Appends a declaration to the current ruleset statement.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_statement_ruleset_append_decl2 (CRStatement * a_this,
+ CRString * a_prop,
+ CRTerm * a_value)
+{
+ CRDeclaration *new_decls = NULL;
+
+ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT
+ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR);
+
+ new_decls = cr_declaration_append2
+ (a_this->kind.ruleset->decl_list,
+ a_prop, a_value);
+ g_return_val_if_fail (new_decls, CR_ERROR);
+ a_this->kind.ruleset->decl_list = new_decls;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_ruleset_append_decl:
+ *
+ *Appends a declaration to the current statement.
+ *
+ *@a_this: the current statement.
+ *@a_declaration: the declaration to append.
+ *
+ *Returns CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_statement_ruleset_append_decl (CRStatement * a_this,
+ CRDeclaration * a_decl)
+{
+ CRDeclaration *new_decls = NULL;
+
+ g_return_val_if_fail (a_this && a_this->type == RULESET_STMT
+ && a_this->kind.ruleset, CR_BAD_PARAM_ERROR);
+
+ new_decls = cr_declaration_append
+ (a_this->kind.ruleset->decl_list, a_decl);
+ g_return_val_if_fail (new_decls, CR_ERROR);
+ a_this->kind.ruleset->decl_list = new_decls;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_import_rule_set_imported_sheet:
+ *
+ *Sets a stylesheet to the current \@import rule.
+ *@a_this: the current \@import rule.
+ *@a_sheet: the stylesheet. The stylesheet is owned
+ *by the current instance of #CRStatement, that is, the
+ *stylesheet will be destroyed when the current instance
+ *of #CRStatement is destroyed.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_import_rule_set_imported_sheet (CRStatement * a_this,
+ CRStyleSheet * a_sheet)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_IMPORT_RULE_STMT
+ && a_this->kind.import_rule,
+ CR_BAD_PARAM_ERROR);
+
+ a_this->kind.import_rule->sheet = a_sheet;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_import_rule_get_imported_sheet:
+ *
+ *@a_this: the current \@import rule statement.
+ *@a_sheet: out parameter. The returned stylesheet if and
+ *only if the function returns CR_OK.
+ *
+ *Gets the stylesheet contained by the \@import rule statement.
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_import_rule_get_imported_sheet (CRStatement * a_this,
+ CRStyleSheet ** a_sheet)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_IMPORT_RULE_STMT
+ && a_this->kind.import_rule,
+ CR_BAD_PARAM_ERROR);
+ *a_sheet = a_this->kind.import_rule->sheet;
+ return CR_OK;
+
+}
+
+/**
+ * cr_statement_at_import_rule_set_url:
+ *
+ *@a_this: the current \@import rule statement.
+ *@a_url: the url to set.
+ *
+ *Sets an url to the current \@import rule statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_import_rule_set_url (CRStatement * a_this,
+ CRString * a_url)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_IMPORT_RULE_STMT
+ && a_this->kind.import_rule,
+ CR_BAD_PARAM_ERROR);
+
+ if (a_this->kind.import_rule->url) {
+ cr_string_destroy (a_this->kind.import_rule->url);
+ }
+
+ a_this->kind.import_rule->url = a_url;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_import_rule_get_url:
+ *
+ *@a_this: the current \@import rule statement.
+ *@a_url: out parameter. The returned url if
+ *and only if the function returned CR_OK.
+ *
+ *Gets the url of the \@import rule statement.
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_import_rule_get_url (CRStatement const * a_this,
+ CRString ** a_url)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_IMPORT_RULE_STMT
+ && a_this->kind.import_rule,
+ CR_BAD_PARAM_ERROR);
+
+ *a_url = a_this->kind.import_rule->url;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_media_nr_rules:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *
+ *Returns the number of rules in the media rule;
+ */
+int
+cr_statement_at_media_nr_rules (CRStatement const * a_this)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_MEDIA_RULE_STMT
+ && a_this->kind.media_rule, CR_BAD_PARAM_ERROR);
+
+ return cr_statement_nr_rules (a_this->kind.media_rule->rulesets);
+}
+
+/**
+ * cr_statement_at_media_get_from_list:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *@itemnr: the index into the media rule list of rules.
+ *
+ *Use an index to get a CRStatement from the media rule list of rules.
+ *
+ *Returns CRStatement at position itemnr, if itemnr > number of rules - 1,
+ *it will return NULL.
+ */
+CRStatement *
+cr_statement_at_media_get_from_list (CRStatement * a_this, int itemnr)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_MEDIA_RULE_STMT
+ && a_this->kind.media_rule, NULL);
+
+ return cr_statement_get_from_list (a_this->kind.media_rule->rulesets,
+ itemnr);
+}
+
+/**
+ * cr_statement_at_page_rule_set_declarations:
+ *
+ *@a_this: the current \@page rule statement.
+ *@a_decl_list: the declaration list to add. Will be freed
+ *by the current instance of #CRStatement when it is destroyed.
+ *
+ *Sets a declaration list to the current \@page rule statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_page_rule_set_declarations (CRStatement * a_this,
+ CRDeclaration * a_decl_list)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_PAGE_RULE_STMT
+ && a_this->kind.page_rule, CR_BAD_PARAM_ERROR);
+
+ if (a_this->kind.page_rule->decl_list) {
+ cr_declaration_unref (a_this->kind.page_rule->decl_list);
+ }
+
+ a_this->kind.page_rule->decl_list = a_decl_list;
+
+ if (a_decl_list) {
+ cr_declaration_ref (a_decl_list);
+ }
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_page_rule_get_declarations:
+ *
+ *@a_this: the current \@page rule statement.
+ *@a_decl_list: out parameter. The returned declaration list.
+ *
+ *Gets the declaration list associated to the current \@page rule
+ *statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_page_rule_get_declarations (CRStatement * a_this,
+ CRDeclaration ** a_decl_list)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_PAGE_RULE_STMT
+ && a_this->kind.page_rule, CR_BAD_PARAM_ERROR);
+
+ *a_decl_list = a_this->kind.page_rule->decl_list;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_charset_rule_set_charset:
+ *
+ *
+ *@a_this: the current \@charset rule statement.
+ *@a_charset: the charset to set.
+ *
+ *Sets the charset of the current \@charset rule statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_charset_rule_set_charset (CRStatement * a_this,
+ CRString * a_charset)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_CHARSET_RULE_STMT
+ && a_this->kind.charset_rule,
+ CR_BAD_PARAM_ERROR);
+
+ if (a_this->kind.charset_rule->charset) {
+ cr_string_destroy (a_this->kind.charset_rule->charset);
+ }
+ a_this->kind.charset_rule->charset = a_charset;
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_charset_rule_get_charset:
+ *@a_this: the current \@charset rule statement.
+ *@a_charset: out parameter. The returned charset string if
+ *and only if the function returned CR_OK.
+ *
+ *Gets the charset string associated to the current
+ *\@charset rule statement.
+ *
+ * Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_charset_rule_get_charset (CRStatement const * a_this,
+ CRString ** a_charset)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_CHARSET_RULE_STMT
+ && a_this->kind.charset_rule,
+ CR_BAD_PARAM_ERROR);
+
+ *a_charset = a_this->kind.charset_rule->charset;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_font_face_rule_set_decls:
+ *
+ *@a_this: the current \@font-face rule statement.
+ *@a_decls: the declarations list to set.
+ *
+ *Sets a declaration list to the current \@font-face rule statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_font_face_rule_set_decls (CRStatement * a_this,
+ CRDeclaration * a_decls)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_FONT_FACE_RULE_STMT
+ && a_this->kind.font_face_rule,
+ CR_BAD_PARAM_ERROR);
+
+ if (a_this->kind.font_face_rule->decl_list) {
+ cr_declaration_unref (a_this->kind.font_face_rule->decl_list);
+ }
+
+ a_this->kind.font_face_rule->decl_list = a_decls;
+ cr_declaration_ref (a_decls);
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_font_face_rule_get_decls:
+ *
+ *@a_this: the current \@font-face rule statement.
+ *@a_decls: out parameter. The returned declaration list if
+ *and only if this function returns CR_OK.
+ *
+ *Gets the declaration list associated to the current instance
+ *of \@font-face rule statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_font_face_rule_get_decls (CRStatement * a_this,
+ CRDeclaration ** a_decls)
+{
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_FONT_FACE_RULE_STMT
+ && a_this->kind.font_face_rule,
+ CR_BAD_PARAM_ERROR);
+
+ *a_decls = a_this->kind.font_face_rule->decl_list;
+
+ return CR_OK;
+}
+
+/**
+ * cr_statement_at_font_face_rule_add_decl:
+ *
+ *@a_this: the current \@font-face rule statement.
+ *@a_prop: the property of the declaration.
+ *@a_value: the value of the declaration.
+ *
+ *Adds a declaration to the current \@font-face rule
+ *statement.
+ *
+ *Returns CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_statement_at_font_face_rule_add_decl (CRStatement * a_this,
+ CRString * a_prop, CRTerm * a_value)
+{
+ CRDeclaration *decls = NULL;
+
+ g_return_val_if_fail (a_this
+ && a_this->type == AT_FONT_FACE_RULE_STMT
+ && a_this->kind.font_face_rule,
+ CR_BAD_PARAM_ERROR);
+
+ decls = cr_declaration_append2
+ (a_this->kind.font_face_rule->decl_list,
+ a_prop, a_value);
+
+ g_return_val_if_fail (decls, CR_ERROR);
+
+ if (a_this->kind.font_face_rule->decl_list == NULL)
+ cr_declaration_ref (decls);
+
+ a_this->kind.font_face_rule->decl_list = decls;
+
+ return CR_OK;
+}
+
+
+/**
+ * cr_statement_to_string:
+ *
+ *@a_this: the current statement to serialize
+ *@a_indent: the number of white space of indentation.
+ *
+ *Serializes a css statement into a string
+ *
+ *Returns the serialized statement. Must be freed by the caller
+ *using g_free().
+ */
+gchar *
+cr_statement_to_string (CRStatement const * a_this, gulong a_indent)
+{
+ gchar *str = NULL ;
+
+ if (!a_this)
+ return NULL;
+
+ switch (a_this->type) {
+ case RULESET_STMT:
+ str = cr_statement_ruleset_to_string
+ (a_this, a_indent);
+ break;
+
+ case AT_FONT_FACE_RULE_STMT:
+ str = cr_statement_font_face_rule_to_string
+ (a_this, a_indent) ;
+ break;
+
+ case AT_CHARSET_RULE_STMT:
+ str = cr_statement_charset_to_string
+ (a_this, a_indent);
+ break;
+
+ case AT_PAGE_RULE_STMT:
+ str = cr_statement_at_page_rule_to_string
+ (a_this, a_indent);
+ break;
+
+ case AT_MEDIA_RULE_STMT:
+ str = cr_statement_media_rule_to_string
+ (a_this, a_indent);
+ break;
+
+ case AT_IMPORT_RULE_STMT:
+ str = cr_statement_import_rule_to_string
+ (a_this, a_indent);
+ break;
+
+ default:
+ cr_utils_trace_info ("Statement unrecognized");
+ break;
+ }
+ return str ;
+}
+
+gchar*
+cr_statement_list_to_string (CRStatement const *a_this, gulong a_indent)
+{
+ CRStatement const *cur_stmt = NULL ;
+ GString *stringue = NULL ;
+ gchar *str = NULL ;
+
+ g_return_val_if_fail (a_this, NULL) ;
+
+ stringue = g_string_new (NULL) ;
+ if (!stringue) {
+ cr_utils_trace_info ("Out of memory") ;
+ return NULL ;
+ }
+ for (cur_stmt = a_this ; cur_stmt;
+ cur_stmt = cur_stmt->next) {
+ str = cr_statement_to_string (cur_stmt, a_indent) ;
+ if (str) {
+ if (!cur_stmt->prev) {
+ g_string_append (stringue, str) ;
+ } else {
+ g_string_append_printf
+ (stringue, "\n%s", str) ;
+ }
+ g_free (str) ;
+ str = NULL ;
+ }
+ }
+ str = g_string_free (stringue, FALSE) ;
+ return str ;
+}
+
+/**
+ * cr_statement_dump:
+ *
+ *@a_this: the current css2 statement.
+ *@a_fp: the destination file pointer.
+ *@a_indent: the number of white space indentation characters.
+ *
+ *Dumps the css2 statement to a file.
+ */
+void
+cr_statement_dump (CRStatement const * a_this, FILE * a_fp, gulong a_indent)
+{
+ gchar *str = NULL ;
+
+ if (!a_this)
+ return;
+
+ str = cr_statement_to_string (a_this, a_indent) ;
+ if (str) {
+ fprintf (a_fp, "%s",str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+/**
+ * cr_statement_dump_ruleset:
+ *
+ *@a_this: the current instance of #CRStatement.
+ *@a_fp: the destination file pointer.
+ *@a_indent: the number of indentation white spaces to add.
+ *
+ *Dumps a ruleset statement to a file.
+ */
+void
+cr_statement_dump_ruleset (CRStatement const * a_this, FILE * a_fp, glong a_indent)
+{
+ gchar *str = NULL;
+
+ g_return_if_fail (a_fp && a_this);
+ str = cr_statement_ruleset_to_string (a_this, a_indent);
+ if (str) {
+ fprintf (a_fp, "%s", str);
+ g_free (str);
+ str = NULL;
+ }
+}
+
+/**
+ * cr_statement_dump_font_face_rule:
+ *
+ *@a_this: the current instance of font face rule statement.
+ *@a_fp: the destination file pointer.
+ *@a_indent: the number of white space indentation.
+ *
+ *Dumps a font face rule statement to a file.
+ */
+void
+cr_statement_dump_font_face_rule (CRStatement const * a_this, FILE * a_fp,
+ glong a_indent)
+{
+ gchar *str = NULL ;
+ g_return_if_fail (a_this
+ && a_this->type == AT_FONT_FACE_RULE_STMT);
+
+ str = cr_statement_font_face_rule_to_string (a_this,
+ a_indent) ;
+ if (str) {
+ fprintf (a_fp, "%s", str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+/**
+ * cr_statement_dump_charset:
+ *
+ *@a_this: the current instance of the \@charset rule statement.
+ *@a_fp: the destination file pointer.
+ *@a_indent: the number of indentation white spaces.
+ *
+ *Dumps an \@charset rule statement to a file.
+ */
+void
+cr_statement_dump_charset (CRStatement const * a_this, FILE * a_fp, gulong a_indent)
+{
+ gchar *str = NULL;
+
+ g_return_if_fail (a_this && a_this->type == AT_CHARSET_RULE_STMT);
+
+ str = cr_statement_charset_to_string (a_this,
+ a_indent) ;
+ if (str) {
+ fprintf (a_fp, "%s", str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+
+/**
+ * cr_statement_dump_page:
+ *
+ *@a_this: the statement to dump on stdout.
+ *@a_fp: the destination file pointer.
+ *@a_indent: the number of indentation white spaces.
+ *
+ *Dumps an \@page rule statement on stdout.
+ */
+void
+cr_statement_dump_page (CRStatement const * a_this, FILE * a_fp, gulong a_indent)
+{
+ gchar *str = NULL;
+
+ g_return_if_fail (a_this
+ && a_this->type == AT_PAGE_RULE_STMT
+ && a_this->kind.page_rule);
+
+ str = cr_statement_at_page_rule_to_string (a_this, a_indent) ;
+ if (str) {
+ fprintf (a_fp, "%s", str);
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+
+/**
+ * cr_statement_dump_media_rule:
+ *
+ *@a_this: the statement to dump.
+ *@a_fp: the destination file pointer
+ *@a_indent: the number of white spaces indentation.
+ *
+ *Dumps an \@media rule statement to a file.
+ */
+void
+cr_statement_dump_media_rule (CRStatement const * a_this,
+ FILE * a_fp,
+ gulong a_indent)
+{
+ gchar *str = NULL ;
+ g_return_if_fail (a_this->type == AT_MEDIA_RULE_STMT);
+
+ str = cr_statement_media_rule_to_string (a_this, a_indent) ;
+ if (str) {
+ fprintf (a_fp, "%s", str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+/**
+ * cr_statement_dump_import_rule:
+ *
+ *@a_fp: the destination file pointer.
+ *@a_indent: the number of white space indentations.
+ *
+ *Dumps an \@import rule statement to a file.
+ */
+void
+cr_statement_dump_import_rule (CRStatement const * a_this, FILE * a_fp,
+ gulong a_indent)
+{
+ gchar *str = NULL ;
+ g_return_if_fail (a_this
+ && a_this->type == AT_IMPORT_RULE_STMT
+ && a_fp
+ && a_this->kind.import_rule);
+
+ str = cr_statement_import_rule_to_string (a_this, a_indent) ;
+ if (str) {
+ fprintf (a_fp, "%s", str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+/**
+ * cr_statement_destroy:
+ *
+ * @a_this: the current instance of #CRStatement.
+ *
+ *Destructor of #CRStatement.
+ */
+void
+cr_statement_destroy (CRStatement * a_this)
+{
+ CRStatement *cur = NULL;
+
+ g_return_if_fail (a_this);
+
+ /*go get the tail of the list */
+ for (cur = a_this; cur && cur->next; cur = cur->next) {
+ cr_statement_clear (cur);
+ }
+
+ if (cur)
+ cr_statement_clear (cur);
+
+ if (cur->prev == NULL) {
+ g_free (a_this);
+ return;
+ }
+
+ /*walk backward and free next element */
+ for (cur = cur->prev; cur && cur->prev; cur = cur->prev) {
+ if (cur->next) {
+ g_free (cur->next);
+ cur->next = NULL;
+ }
+ }
+
+ if (!cur)
+ return;
+
+ /*free the one remaining list */
+ if (cur->next) {
+ g_free (cur->next);
+ cur->next = NULL;
+ }
+
+ g_free (cur);
+ cur = NULL;
+}
diff --git a/src/st/croco/cr-statement.h b/src/st/croco/cr-statement.h
new file mode 100644
index 0000000..c5bec97
--- /dev/null
+++ b/src/st/croco/cr-statement.h
@@ -0,0 +1,440 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include <stdio.h>
+#include "cr-utils.h"
+#include "cr-term.h"
+#include "cr-selector.h"
+#include "cr-declaration.h"
+
+#ifndef __CR_STATEMENT_H__
+#define __CR_STATEMENT_H__
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *Declaration of the #CRStatement class.
+ */
+
+/*
+ *forward declaration of CRStyleSheet which is defined in
+ *cr-stylesheet.h
+ */
+
+struct _CRStatement ;
+
+/*
+ *typedef struct _CRStatement CRStatement ;
+ *this is forward declared in
+ *cr-declaration.h already.
+ */
+
+struct _CRAtMediaRule ;
+typedef struct _CRAtMediaRule CRAtMediaRule ;
+
+typedef struct _CRRuleSet CRRuleSet ;
+
+/**
+ *The abstraction of a css ruleset.
+ *A ruleset is made of a list of selectors,
+ *followed by a list of declarations.
+ */
+struct _CRRuleSet
+{
+ /**A list of instances of #CRSimpeSel*/
+ CRSelector *sel_list ;
+
+ /**A list of instances of #CRDeclaration*/
+ CRDeclaration *decl_list ;
+
+ /**
+ *The parent media rule, or NULL if
+ *no parent media rule exists.
+ */
+ CRStatement *parent_media_rule ;
+} ;
+
+/*
+ *a forward declaration of CRStylesheet.
+ *CRStylesheet is actually declared in
+ *cr-stylesheet.h
+ */
+struct _CRStyleSheet ;
+typedef struct _CRStyleSheet CRStyleSheet;
+
+
+/**The \@import rule abstraction.*/
+typedef struct _CRAtImportRule CRAtImportRule ;
+struct _CRAtImportRule
+{
+ /**the url of the import rule*/
+ CRString *url ;
+
+ GList *media_list ;
+
+ /**
+ *the stylesheet fetched from the url, if any.
+ *this is not "owned" by #CRAtImportRule which means
+ *it is not destroyed by the destructor of #CRAtImportRule.
+ */
+ CRStyleSheet * sheet;
+};
+
+
+/**abstraction of an \@media rule*/
+struct _CRAtMediaRule
+{
+ GList *media_list ;
+ CRStatement *rulesets ;
+} ;
+
+
+typedef struct _CRAtPageRule CRAtPageRule ;
+/**The \@page rule abstraction*/
+struct _CRAtPageRule
+{
+ /**a list of instances of #CRDeclaration*/
+ CRDeclaration *decl_list ;
+
+ /**page selector. Is a pseudo selector*/
+ CRString *name ;
+ CRString *pseudo ;
+} ;
+
+/**The \@charset rule abstraction*/
+typedef struct _CRAtCharsetRule CRAtCharsetRule ;
+struct _CRAtCharsetRule
+{
+ CRString * charset ;
+};
+
+/**The abstraction of the \@font-face rule.*/
+typedef struct _CRAtFontFaceRule CRAtFontFaceRule ;
+struct _CRAtFontFaceRule
+{
+ /*a list of instanaces of #CRDeclaration*/
+ CRDeclaration *decl_list ;
+} ;
+
+
+/**
+ *The possible types of css2 statements.
+ */
+enum CRStatementType
+{
+ /**
+ *A generic css at-rule
+ *each unknown at-rule will
+ *be of this type.
+ */
+
+ /**A css at-rule*/
+ AT_RULE_STMT = 0,
+
+ /*A css ruleset*/
+ RULESET_STMT,
+
+ /**A css2 import rule*/
+ AT_IMPORT_RULE_STMT,
+
+ /**A css2 media rule*/
+ AT_MEDIA_RULE_STMT,
+
+ /**A css2 page rule*/
+ AT_PAGE_RULE_STMT,
+
+ /**A css2 charset rule*/
+ AT_CHARSET_RULE_STMT,
+
+ /**A css2 font face rule*/
+ AT_FONT_FACE_RULE_STMT
+} ;
+
+
+/**
+ *The abstraction of css statement as defined
+ *in the chapter 4 and appendix D.1 of the css2 spec.
+ *A statement is actually a double chained list of
+ *statements.A statement can be a ruleset, an \@import
+ *rule, an \@page rule etc ...
+ */
+struct _CRStatement
+{
+ /**
+ *The type of the statement.
+ */
+ enum CRStatementType type ;
+
+ union
+ {
+ CRRuleSet *ruleset ;
+ CRAtImportRule *import_rule ;
+ CRAtMediaRule *media_rule ;
+ CRAtPageRule *page_rule ;
+ CRAtCharsetRule *charset_rule ;
+ CRAtFontFaceRule *font_face_rule ;
+ } kind ;
+
+ /*
+ *the specificity of the selector
+ *that matched this statement.
+ *This is only used by the cascading
+ *order determination algorithm.
+ */
+ gulong specificity ;
+
+ /*
+ *the style sheet that contains
+ *this css statement.
+ */
+ CRStyleSheet *parent_sheet ;
+ CRStatement *next ;
+ CRStatement *prev ;
+
+ CRParsingLocation location ;
+
+ /**
+ *a custom pointer useable by
+ *applications that use libcroco.
+ *libcroco itself will never modify
+ *this pointer.
+ */
+ gpointer app_data ;
+
+ /**
+ *a custom pointer used
+ *by the upper layers of libcroco.
+ *application should never use this
+ *pointer.
+ */
+ gpointer croco_data ;
+
+} ;
+
+
+gboolean
+cr_statement_does_buf_parses_against_core (const guchar *a_buf,
+ enum CREncoding a_encoding) ;
+CRStatement *
+cr_statement_parse_from_buf (const guchar *a_buf,
+ enum CREncoding a_encoding) ;
+CRStatement*
+cr_statement_new_ruleset (CRStyleSheet *a_sheet,
+ CRSelector *a_sel_list,
+ CRDeclaration *a_decl_list,
+ CRStatement *a_media_rule) ;
+CRStatement *
+cr_statement_ruleset_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_enc) ;
+
+CRStatement*
+cr_statement_new_at_import_rule (CRStyleSheet *a_container_sheet,
+ CRString *a_url,
+ GList *a_media_list,
+ CRStyleSheet *a_imported_sheet) ;
+
+CRStatement *
+cr_statement_at_import_rule_parse_from_buf (const guchar * a_buf,
+ enum CREncoding a_encoding) ;
+
+CRStatement *
+cr_statement_new_at_media_rule (CRStyleSheet *a_sheet,
+ CRStatement *a_ruleset,
+ GList *a_media) ;
+CRStatement *
+cr_statement_at_media_rule_parse_from_buf (const guchar *a_buf,
+ enum CREncoding a_enc) ;
+
+CRStatement *
+cr_statement_new_at_charset_rule (CRStyleSheet *a_sheet,
+ CRString *a_charset) ;
+CRStatement *
+cr_statement_at_charset_rule_parse_from_buf (const guchar *a_buf,
+ enum CREncoding a_encoding);
+
+
+CRStatement *
+cr_statement_new_at_font_face_rule (CRStyleSheet *a_sheet,
+ CRDeclaration *a_font_decls) ;
+CRStatement *
+cr_statement_font_face_rule_parse_from_buf (const guchar *a_buf,
+ enum CREncoding a_encoding) ;
+
+CRStatement *
+cr_statement_new_at_page_rule (CRStyleSheet *a_sheet,
+ CRDeclaration *a_decl_list,
+ CRString *a_name,
+ CRString *a_pseudo) ;
+CRStatement *
+cr_statement_at_page_rule_parse_from_buf (const guchar *a_buf,
+ enum CREncoding a_encoding) ;
+
+enum CRStatus
+cr_statement_set_parent_sheet (CRStatement *a_this,
+ CRStyleSheet *a_sheet) ;
+
+enum CRStatus
+cr_statement_get_parent_sheet (CRStatement *a_this,
+ CRStyleSheet **a_sheet) ;
+
+CRStatement *
+cr_statement_append (CRStatement *a_this,
+ CRStatement *a_new) ;
+
+CRStatement*
+cr_statement_prepend (CRStatement *a_this,
+ CRStatement *a_new) ;
+
+CRStatement *
+cr_statement_unlink (CRStatement *a_stmt) ;
+
+enum CRStatus
+cr_statement_ruleset_set_sel_list (CRStatement *a_this,
+ CRSelector *a_sel_list) ;
+
+enum CRStatus
+cr_statement_ruleset_get_sel_list (CRStatement const *a_this,
+ CRSelector **a_list) ;
+
+enum CRStatus
+cr_statement_ruleset_set_decl_list (CRStatement *a_this,
+ CRDeclaration *a_list) ;
+
+enum CRStatus
+cr_statement_ruleset_get_declarations (CRStatement *a_this,
+ CRDeclaration **a_decl_list) ;
+
+enum CRStatus
+cr_statement_ruleset_append_decl2 (CRStatement *a_this,
+ CRString *a_prop, CRTerm *a_value) ;
+
+enum CRStatus
+cr_statement_ruleset_append_decl (CRStatement *a_this,
+ CRDeclaration *a_decl) ;
+
+enum CRStatus
+cr_statement_at_import_rule_set_imported_sheet (CRStatement *a_this,
+ CRStyleSheet *a_sheet) ;
+
+enum CRStatus
+cr_statement_at_import_rule_get_imported_sheet (CRStatement *a_this,
+ CRStyleSheet **a_sheet) ;
+
+enum CRStatus
+cr_statement_at_import_rule_set_url (CRStatement *a_this,
+ CRString *a_url) ;
+
+enum CRStatus
+cr_statement_at_import_rule_get_url (CRStatement const *a_this,
+ CRString **a_url) ;
+
+gint
+cr_statement_at_media_nr_rules (CRStatement const *a_this) ;
+
+CRStatement *
+cr_statement_at_media_get_from_list (CRStatement *a_this, int itemnr) ;
+
+enum CRStatus
+cr_statement_at_page_rule_set_sel (CRStatement *a_this,
+ CRSelector *a_sel) ;
+
+enum CRStatus
+cr_statement_at_page_rule_get_sel (CRStatement const *a_this,
+ CRSelector **a_sel) ;
+
+enum CRStatus
+cr_statement_at_page_rule_set_declarations (CRStatement *a_this,
+ CRDeclaration *a_decl_list) ;
+
+enum CRStatus
+cr_statement_at_page_rule_get_declarations (CRStatement *a_this,
+ CRDeclaration **a_decl_list) ;
+
+enum CRStatus
+cr_statement_at_charset_rule_set_charset (CRStatement *a_this,
+ CRString *a_charset) ;
+
+enum CRStatus
+cr_statement_at_charset_rule_get_charset (CRStatement const *a_this,
+ CRString **a_charset) ;
+
+enum CRStatus
+cr_statement_at_font_face_rule_set_decls (CRStatement *a_this,
+ CRDeclaration *a_decls) ;
+
+enum CRStatus
+cr_statement_at_font_face_rule_get_decls (CRStatement *a_this,
+ CRDeclaration **a_decls) ;
+
+enum CRStatus
+cr_statement_at_font_face_rule_add_decl (CRStatement *a_this,
+ CRString *a_prop,
+ CRTerm *a_value) ;
+
+gchar *
+cr_statement_to_string (CRStatement const * a_this, gulong a_indent) ;
+
+gchar*
+cr_statement_list_to_string (CRStatement const *a_this, gulong a_indent) ;
+
+void
+cr_statement_dump (CRStatement const *a_this, FILE *a_fp, gulong a_indent) ;
+
+void
+cr_statement_dump_ruleset (CRStatement const * a_this, FILE * a_fp,
+ glong a_indent) ;
+
+void
+cr_statement_dump_font_face_rule (CRStatement const * a_this,
+ FILE * a_fp,
+ glong a_indent) ;
+
+void
+cr_statement_dump_page (CRStatement const * a_this, FILE * a_fp,
+ gulong a_indent) ;
+
+
+void
+cr_statement_dump_media_rule (CRStatement const * a_this,
+ FILE * a_fp,
+ gulong a_indent) ;
+
+void
+cr_statement_dump_import_rule (CRStatement const * a_this, FILE * a_fp,
+ gulong a_indent) ;
+void
+cr_statement_dump_charset (CRStatement const * a_this, FILE * a_fp,
+ gulong a_indent) ;
+gint
+cr_statement_nr_rules (CRStatement const *a_this) ;
+
+CRStatement *
+cr_statement_get_from_list (CRStatement *a_this, int itemnr) ;
+
+void
+cr_statement_destroy (CRStatement *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_STATEMENT_H__*/
diff --git a/src/st/croco/cr-string.c b/src/st/croco/cr-string.c
new file mode 100644
index 0000000..6a16676
--- /dev/null
+++ b/src/st/croco/cr-string.c
@@ -0,0 +1,168 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli.
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include <string.h>
+#include "cr-string.h"
+
+/**
+ *Instantiates a #CRString
+ *@return the newly instantiated #CRString
+ *Must be freed with cr_string_destroy().
+ */
+CRString *
+cr_string_new (void)
+{
+ CRString *result = NULL ;
+
+ result = g_try_malloc (sizeof (CRString)) ;
+ if (!result) {
+ cr_utils_trace_info ("Out of memory") ;
+ return NULL ;
+ }
+ memset (result, 0, sizeof (CRString)) ;
+ result->stryng = g_string_new (NULL) ;
+ return result ;
+}
+
+/**
+ *Instantiate a string and initialise it to
+ *a_string.
+ *@param a_string the initial string
+ *@return the newly instantiated string.
+ */
+CRString *
+cr_string_new_from_string (const gchar * a_string)
+{
+ CRString *result = NULL ;
+
+ result = cr_string_new () ;
+ if (!result) {
+ cr_utils_trace_info ("Out of memory") ;
+ return NULL ;
+ }
+ if (a_string)
+ g_string_append (result->stryng, a_string) ;
+ return result ;
+}
+
+/**
+ *Instantiates a #CRString from an instance of GString.
+ *@param a_string the input string that will be copied into
+ *the newly instantiated #CRString
+ *@return the newly instantiated #CRString.
+ */
+CRString *
+cr_string_new_from_gstring (GString const *a_string)
+{
+ CRString *result = NULL ;
+
+ result = cr_string_new () ;
+ if (!result) {
+ cr_utils_trace_info ("Out of memory") ;
+ return NULL ;
+ }
+ if (a_string) {
+ g_string_append_len (result->stryng,
+ a_string->str,
+ a_string->len);
+
+ }
+ return result ;
+}
+
+CRString *
+cr_string_dup (CRString const *a_this)
+{
+ CRString *result = NULL ;
+ g_return_val_if_fail (a_this, NULL) ;
+
+ result = cr_string_new_from_gstring (a_this->stryng) ;
+ if (!result) {
+ cr_utils_trace_info ("Out of memory") ;
+ return NULL ;
+ }
+ cr_parsing_location_copy (&result->location,
+ &a_this->location) ;
+ return result ;
+}
+
+gchar *
+cr_string_dup2 (CRString const *a_this)
+{
+ gchar *result = NULL ;
+
+ g_return_val_if_fail (a_this, NULL) ;
+
+ if (a_this
+ && a_this->stryng
+ && a_this->stryng->str) {
+ result = g_strndup (a_this->stryng->str,
+ a_this->stryng->len) ;
+ }
+ return result ;
+}
+
+/**
+ *Returns a pointer to the internal raw NULL terminated string
+ *of the current instance of #CRString.
+ *@param a_this the current instance of #CRString
+ */
+const gchar *
+cr_string_peek_raw_str (CRString const *a_this)
+{
+ g_return_val_if_fail (a_this, NULL) ;
+
+ if (a_this->stryng && a_this->stryng->str)
+ return a_this->stryng->str ;
+ return NULL ;
+}
+
+/**
+ *Returns the length of the internal raw NULL terminated
+ *string of the current instance of #CRString.
+ *@param a_this the current instance of #CRString.
+ *@return the len of the internal raw NULL termninated string,
+ *of -1 if no length can be returned.
+ */
+gint
+cr_string_peek_raw_str_len (CRString const *a_this)
+{
+ g_return_val_if_fail (a_this && a_this->stryng,
+ -1) ;
+ return a_this->stryng->len ;
+}
+
+/**
+ *@param a_this the #CRString to destroy.
+ */
+void
+cr_string_destroy (CRString *a_this)
+{
+ g_return_if_fail (a_this) ;
+
+ if (a_this->stryng) {
+ g_string_free (a_this->stryng, TRUE) ;
+ a_this->stryng = NULL ;
+ }
+ g_free (a_this) ;
+}
diff --git a/src/st/croco/cr-string.h b/src/st/croco/cr-string.h
new file mode 100644
index 0000000..2700f0e
--- /dev/null
+++ b/src/st/croco/cr-string.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See COPYRIGHTS file for copyright information.
+ */
+
+/**
+ *@file
+ *Declaration file of the #CRString class.
+ */
+
+#ifndef __CR_STRING_H__
+#define __CR_STRING_H__
+
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+typedef struct _CRString CRString ;
+
+/**
+ *This is a ship implementation of string based on GString.
+ *Actually, the aim of CRString is to store the parsing location
+ *(line,column,byte offset) at which a given string has been parsed
+ *in the input CSS.
+ *So this class has a gstring field of type GString that users can
+ *freely manipulate, and also a CRParginLocation type where the
+ *parsing location is store. If you don't want to deal with parsing
+ *location stuffs, then use GString instead. If we were in C++ for example,
+ *CRString would just inherit GString and just add accessors to
+ *the CRParsingLocation data ... but we are not and we still have
+ *to provide the parsing location information.
+ */
+struct _CRString {
+ /**
+ *The GString where all the string
+ *operation happen.
+ */
+ GString *stryng ;
+ /**
+ *The parsing location storage area.
+ */
+ CRParsingLocation location ;
+} ;
+
+CRString * cr_string_new (void) ;
+
+CRString *cr_string_new_from_string (const gchar * a_string) ;
+CRString * cr_string_new_from_gstring (GString const *a_string) ;
+CRString *cr_string_dup (CRString const *a_this) ;
+gchar *cr_string_dup2 (CRString const *a_this) ;
+const gchar *cr_string_peek_raw_str (CRString const *a_this) ;
+gint cr_string_peek_raw_str_len (CRString const *a_this) ;
+void cr_string_destroy (CRString *a_this) ;
+
+G_END_DECLS
+
+#endif
diff --git a/src/st/croco/cr-stylesheet.c b/src/st/croco/cr-stylesheet.c
new file mode 100644
index 0000000..63e763f
--- /dev/null
+++ b/src/st/croco/cr-stylesheet.c
@@ -0,0 +1,177 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * Copyright (C) 2002-2004 Dodji Seketeli
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include "string.h"
+#include "cr-stylesheet.h"
+
+/**
+ *@file
+ *The definition of the #CRStyleSheet class
+ */
+
+/**
+ *Constructor of the #CRStyleSheet class.
+ *@param the initial list of css statements.
+ *@return the newly built css2 stylesheet, or NULL in case of error.
+ */
+CRStyleSheet *
+cr_stylesheet_new (CRStatement * a_stmts)
+{
+ CRStyleSheet *result;
+
+ result = g_try_malloc (sizeof (CRStyleSheet));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRStyleSheet));
+
+ if (a_stmts)
+ result->statements = a_stmts;
+
+ return result;
+}
+
+/**
+ *@param a_this the current instance of #CRStyleSheet
+ *@return the serialized stylesheet.
+ */
+gchar *
+cr_stylesheet_to_string (CRStyleSheet const *a_this)
+{
+ gchar *str = NULL;
+ GString *stringue = NULL;
+ CRStatement const *cur_stmt = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ if (a_this->statements) {
+ stringue = g_string_new (NULL) ;
+ g_return_val_if_fail (stringue, NULL) ;
+ }
+ for (cur_stmt = a_this->statements;
+ cur_stmt; cur_stmt = cur_stmt->next) {
+ if (cur_stmt->prev) {
+ g_string_append (stringue, "\n\n") ;
+ }
+ str = cr_statement_to_string (cur_stmt, 0) ;
+ if (str) {
+ g_string_append (stringue, str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+ }
+ if (stringue) {
+ str = g_string_free (stringue, FALSE) ;
+ stringue = NULL ;
+ }
+ return str ;
+}
+
+/**
+ *Dumps the current css2 stylesheet to a file.
+ *@param a_this the current instance of #CRStyleSheet.
+ *@param a_fp the destination file
+ */
+void
+cr_stylesheet_dump (CRStyleSheet const * a_this, FILE * a_fp)
+{
+ gchar *str = NULL ;
+
+ g_return_if_fail (a_this);
+
+ str = cr_stylesheet_to_string (a_this) ;
+ if (str) {
+ fprintf (a_fp, "%s", str) ;
+ g_free (str) ;
+ str = NULL ;
+ }
+}
+
+/**
+ *Return the number of rules in the stylesheet.
+ *@param a_this the current instance of #CRStyleSheet.
+ *@return number of rules in the stylesheet.
+ */
+gint
+cr_stylesheet_nr_rules (CRStyleSheet const * a_this)
+{
+ g_return_val_if_fail (a_this, -1);
+
+ return cr_statement_nr_rules (a_this->statements);
+}
+
+/**
+ *Use an index to get a CRStatement from the rules in a given stylesheet.
+ *@param a_this the current instance of #CRStatement.
+ *@param itemnr the index into the rules.
+ *@return CRStatement at position itemnr, if itemnr > number of rules - 1,
+ *it will return NULL.
+ */
+CRStatement *
+cr_stylesheet_statement_get_from_list (CRStyleSheet * a_this, int itemnr)
+{
+ g_return_val_if_fail (a_this, NULL);
+
+ return cr_statement_get_from_list (a_this->statements, itemnr);
+}
+
+void
+cr_stylesheet_ref (CRStyleSheet * a_this)
+{
+ g_return_if_fail (a_this);
+
+ a_this->ref_count++;
+}
+
+gboolean
+cr_stylesheet_unref (CRStyleSheet * a_this)
+{
+ g_return_val_if_fail (a_this, FALSE);
+
+ if (a_this->ref_count)
+ a_this->ref_count--;
+
+ if (!a_this->ref_count) {
+ cr_stylesheet_destroy (a_this);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ *Destructor of the #CRStyleSheet class.
+ *@param a_this the current instance of the #CRStyleSheet class.
+ */
+void
+cr_stylesheet_destroy (CRStyleSheet * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (a_this->statements) {
+ cr_statement_destroy (a_this->statements);
+ a_this->statements = NULL;
+ }
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-stylesheet.h b/src/st/croco/cr-stylesheet.h
new file mode 100644
index 0000000..2d6b4fa
--- /dev/null
+++ b/src/st/croco/cr-stylesheet.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * see COPYRIGHTS file for copyright information.
+ */
+
+
+#ifndef __CR_STYLESHEET_H__
+#define __CR_STYLESHEET_H__
+
+#include "cr-utils.h"
+#include "cr-statement.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *The declaration of the #CRStyleSheet class.
+ */
+
+
+enum CRStyleOrigin
+{
+ /*Please don't change the order of
+ *the values enumerated here ...
+ *New values should be added at the end,
+ *just before ORIGIN_END.
+ */
+ ORIGIN_UA = 0,
+ ORIGIN_USER,
+ ORIGIN_AUTHOR,
+
+ /*must always be the last one*/
+ NB_ORIGINS
+} ;
+
+/**
+ *An abstraction of a css stylesheet as defined
+ *by the css2 spec in chapter 4.
+ */
+struct _CRStyleSheet
+{
+ /**The css statements list*/
+ CRStatement *statements ;
+
+ enum CRStyleOrigin origin ;
+
+ /*the parent import rule, if any.*/
+ CRStatement *parent_import_rule ;
+
+ /**custom data used by libcroco*/
+ gpointer croco_data ;
+
+ /**
+ *custom application data pointer
+ *Can be used by applications.
+ */
+ gpointer app_data ;
+
+ /**
+ *the reference count of this instance
+ *Please, don't never ever modify it
+ *directly. Use cr_stylesheet_ref()
+ *and cr_stylesheet_unref() instead.
+ */
+ gulong ref_count ;
+} ;
+
+CRStyleSheet * cr_stylesheet_new (CRStatement *a_stmts) ;
+
+gchar * cr_stylesheet_to_string (CRStyleSheet const *a_this) ;
+void cr_stylesheet_dump (CRStyleSheet const *a_this, FILE *a_fp) ;
+
+gint cr_stylesheet_nr_rules (CRStyleSheet const *a_this) ;
+
+CRStatement * cr_stylesheet_statement_get_from_list (CRStyleSheet *a_this, int itemnr) ;
+
+void cr_stylesheet_ref (CRStyleSheet *a_this) ;
+
+gboolean cr_stylesheet_unref (CRStyleSheet *a_this) ;
+
+void cr_stylesheet_destroy (CRStyleSheet *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_STYLESHEET_H__*/
diff --git a/src/st/croco/cr-term.c b/src/st/croco/cr-term.c
new file mode 100644
index 0000000..b527d95
--- /dev/null
+++ b/src/st/croco/cr-term.c
@@ -0,0 +1,786 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "cr-term.h"
+#include "cr-num.h"
+#include "cr-parser.h"
+
+/**
+ *@file
+ *Definition of the #CRTem class.
+ */
+
+static void
+cr_term_clear (CRTerm * a_this)
+{
+ g_return_if_fail (a_this);
+
+ switch (a_this->type) {
+ case TERM_NUMBER:
+ if (a_this->content.num) {
+ cr_num_destroy (a_this->content.num);
+ a_this->content.num = NULL;
+ }
+ break;
+
+ case TERM_FUNCTION:
+ if (a_this->ext_content.func_param) {
+ cr_term_destroy (a_this->ext_content.func_param);
+ a_this->ext_content.func_param = NULL;
+ }
+ case TERM_STRING:
+ case TERM_IDENT:
+ case TERM_URI:
+ case TERM_HASH:
+ if (a_this->content.str) {
+ cr_string_destroy (a_this->content.str);
+ a_this->content.str = NULL;
+ }
+ break;
+
+ case TERM_RGB:
+ if (a_this->content.rgb) {
+ cr_rgb_destroy (a_this->content.rgb);
+ a_this->content.rgb = NULL;
+ }
+ break;
+
+ case TERM_UNICODERANGE:
+ case TERM_NO_TYPE:
+ default:
+ break;
+ }
+
+ a_this->type = TERM_NO_TYPE;
+}
+
+/**
+ *Instantiate a #CRTerm.
+ *@return the newly build instance
+ *of #CRTerm.
+ */
+CRTerm *
+cr_term_new (void)
+{
+ CRTerm *result = NULL;
+
+ result = g_try_malloc (sizeof (CRTerm));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRTerm));
+ return result;
+}
+
+/**
+ *Parses an expression as defined by the css2 spec
+ *and builds the expression as a list of terms.
+ *@param a_buf the buffer to parse.
+ *@return a pointer to the first term of the expression or
+ *NULL if parsing failed.
+ */
+CRTerm *
+cr_term_parse_expression_from_buf (const guchar * a_buf,
+ enum CREncoding a_encoding)
+{
+ CRParser *parser = NULL;
+ CRTerm *result = NULL;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_buf, NULL);
+
+ parser = cr_parser_new_from_buf ((guchar*)a_buf, strlen ((const char *) a_buf),
+ a_encoding, FALSE);
+ g_return_val_if_fail (parser, NULL);
+
+ status = cr_parser_try_to_skip_spaces_and_comments (parser);
+ if (status != CR_OK) {
+ goto cleanup;
+ }
+ status = cr_parser_parse_expr (parser, &result);
+ if (status != CR_OK) {
+ if (result) {
+ cr_term_destroy (result);
+ result = NULL;
+ }
+ }
+
+ cleanup:
+ if (parser) {
+ cr_parser_destroy (parser);
+ parser = NULL;
+ }
+
+ return result;
+}
+
+enum CRStatus
+cr_term_set_number (CRTerm * a_this, CRNum * a_num)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_term_clear (a_this);
+
+ a_this->type = TERM_NUMBER;
+ a_this->content.num = a_num;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_term_set_function (CRTerm * a_this, CRString * a_func_name,
+ CRTerm * a_func_param)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_term_clear (a_this);
+
+ a_this->type = TERM_FUNCTION;
+ a_this->content.str = a_func_name;
+ a_this->ext_content.func_param = a_func_param;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_term_set_string (CRTerm * a_this, CRString * a_str)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_term_clear (a_this);
+
+ a_this->type = TERM_STRING;
+ a_this->content.str = a_str;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_term_set_ident (CRTerm * a_this, CRString * a_str)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_term_clear (a_this);
+
+ a_this->type = TERM_IDENT;
+ a_this->content.str = a_str;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_term_set_uri (CRTerm * a_this, CRString * a_str)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_term_clear (a_this);
+
+ a_this->type = TERM_URI;
+ a_this->content.str = a_str;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_term_set_rgb (CRTerm * a_this, CRRgb * a_rgb)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_term_clear (a_this);
+
+ a_this->type = TERM_RGB;
+ a_this->content.rgb = a_rgb;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_term_set_hash (CRTerm * a_this, CRString * a_str)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_term_clear (a_this);
+
+ a_this->type = TERM_HASH;
+ a_this->content.str = a_str;
+ return CR_OK;
+}
+
+/**
+ *Appends a new term to the current list of #CRTerm.
+ *
+ *@param a_this the "this pointer" of the current instance
+ *of #CRTerm .
+ *@param a_new_term the term to append.
+ *@return the list of terms with the a_new_term appended to it.
+ */
+CRTerm *
+cr_term_append_term (CRTerm * a_this, CRTerm * a_new_term)
+{
+ CRTerm *cur = NULL;
+
+ g_return_val_if_fail (a_new_term, NULL);
+
+ if (a_this == NULL)
+ return a_new_term;
+
+ for (cur = a_this; cur->next; cur = cur->next) ;
+
+ cur->next = a_new_term;
+ a_new_term->prev = cur;
+
+ return a_this;
+}
+
+/**
+ *Prepends a term to the list of terms represented by a_this.
+ *
+ *@param a_this the "this pointer" of the current instance of
+ *#CRTerm .
+ *@param a_new_term the term to prepend.
+ *@return the head of the new list.
+ */
+CRTerm *
+cr_term_prepend_term (CRTerm * a_this, CRTerm * a_new_term)
+{
+ g_return_val_if_fail (a_this && a_new_term, NULL);
+
+ a_new_term->next = a_this;
+ a_this->prev = a_new_term;
+
+ return a_new_term;
+}
+
+/**
+ *Serializes the expression represented by
+ *the chained instances of #CRterm.
+ *@param a_this the current instance of #CRTerm
+ *@return the zero terminated string containing the serialized
+ *form of #CRTerm. MUST BE FREED BY THE CALLER using g_free().
+ */
+guchar *
+cr_term_to_string (CRTerm const * a_this)
+{
+ GString *str_buf = NULL;
+ CRTerm const *cur = NULL;
+ guchar *result = NULL,
+ *content = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ str_buf = g_string_new (NULL);
+ g_return_val_if_fail (str_buf, NULL);
+
+ for (cur = a_this; cur; cur = cur->next) {
+ if ((cur->content.str == NULL)
+ && (cur->content.num == NULL)
+ && (cur->content.rgb == NULL))
+ continue;
+
+ switch (cur->the_operator) {
+ case DIVIDE:
+ g_string_append (str_buf, " / ");
+ break;
+
+ case COMMA:
+ g_string_append (str_buf, ", ");
+ break;
+
+ case NO_OP:
+ if (cur->prev) {
+ g_string_append (str_buf, " ");
+ }
+ break;
+ default:
+
+ break;
+ }
+
+ switch (cur->unary_op) {
+ case PLUS_UOP:
+ g_string_append (str_buf, "+");
+ break;
+
+ case MINUS_UOP:
+ g_string_append (str_buf, "-");
+ break;
+
+ default:
+ break;
+ }
+
+ switch (cur->type) {
+ case TERM_NUMBER:
+ if (cur->content.num) {
+ content = cr_num_to_string (cur->content.num);
+ }
+
+ if (content) {
+ g_string_append (str_buf, (const gchar *) content);
+ g_free (content);
+ content = NULL;
+ }
+
+ break;
+
+ case TERM_FUNCTION:
+ if (cur->content.str) {
+ content = (guchar *) g_strndup
+ (cur->content.str->stryng->str,
+ cur->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf (str_buf, "%s(",
+ content);
+
+ if (cur->ext_content.func_param) {
+ guchar *tmp_str = NULL;
+
+ tmp_str = cr_term_to_string
+ (cur->
+ ext_content.func_param);
+
+ if (tmp_str) {
+ g_string_append (str_buf,
+ (const gchar *) tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ }
+ g_string_append (str_buf, ")");
+ g_free (content);
+ content = NULL;
+ }
+
+ break;
+
+ case TERM_STRING:
+ if (cur->content.str) {
+ content = (guchar *) g_strndup
+ (cur->content.str->stryng->str,
+ cur->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf (str_buf,
+ "\"%s\"", content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ case TERM_IDENT:
+ if (cur->content.str) {
+ content = (guchar *) g_strndup
+ (cur->content.str->stryng->str,
+ cur->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append (str_buf, (const gchar *) content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ case TERM_URI:
+ if (cur->content.str) {
+ content = (guchar *) g_strndup
+ (cur->content.str->stryng->str,
+ cur->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf
+ (str_buf, "url(%s)", content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ case TERM_RGB:
+ if (cur->content.rgb) {
+ guchar *tmp_str = NULL;
+
+ g_string_append (str_buf, "rgb(");
+ tmp_str = cr_rgb_to_string (cur->content.rgb);
+
+ if (tmp_str) {
+ g_string_append (str_buf, (const gchar *) tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ g_string_append (str_buf, ")");
+ }
+
+ break;
+
+ case TERM_UNICODERANGE:
+ g_string_append
+ (str_buf,
+ "?found unicoderange: dump not supported yet?");
+ break;
+
+ case TERM_HASH:
+ if (cur->content.str) {
+ content = (guchar *) g_strndup
+ (cur->content.str->stryng->str,
+ cur->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf (str_buf,
+ "#%s", content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ default:
+ g_string_append (str_buf,
+ "Unrecognized Term type");
+ break;
+ }
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+}
+
+guchar *
+cr_term_one_to_string (CRTerm const * a_this)
+{
+ GString *str_buf = NULL;
+ guchar *result = NULL,
+ *content = NULL;
+
+ g_return_val_if_fail (a_this, NULL);
+
+ str_buf = g_string_new (NULL);
+ g_return_val_if_fail (str_buf, NULL);
+
+ if ((a_this->content.str == NULL)
+ && (a_this->content.num == NULL)
+ && (a_this->content.rgb == NULL))
+ return NULL ;
+
+ switch (a_this->the_operator) {
+ case DIVIDE:
+ g_string_append_printf (str_buf, " / ");
+ break;
+
+ case COMMA:
+ g_string_append_printf (str_buf, ", ");
+ break;
+
+ case NO_OP:
+ if (a_this->prev) {
+ g_string_append_printf (str_buf, " ");
+ }
+ break;
+ default:
+
+ break;
+ }
+
+ switch (a_this->unary_op) {
+ case PLUS_UOP:
+ g_string_append_printf (str_buf, "+");
+ break;
+
+ case MINUS_UOP:
+ g_string_append_printf (str_buf, "-");
+ break;
+
+ default:
+ break;
+ }
+
+ switch (a_this->type) {
+ case TERM_NUMBER:
+ if (a_this->content.num) {
+ content = cr_num_to_string (a_this->content.num);
+ }
+
+ if (content) {
+ g_string_append (str_buf, (const gchar *) content);
+ g_free (content);
+ content = NULL;
+ }
+
+ break;
+
+ case TERM_FUNCTION:
+ if (a_this->content.str) {
+ content = (guchar *) g_strndup
+ (a_this->content.str->stryng->str,
+ a_this->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf (str_buf, "%s(",
+ content);
+
+ if (a_this->ext_content.func_param) {
+ guchar *tmp_str = NULL;
+
+ tmp_str = cr_term_to_string
+ (a_this->
+ ext_content.func_param);
+
+ if (tmp_str) {
+ g_string_append_printf
+ (str_buf,
+ "%s", tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+
+ g_string_append_printf (str_buf, ")");
+ g_free (content);
+ content = NULL;
+ }
+ }
+
+ break;
+
+ case TERM_STRING:
+ if (a_this->content.str) {
+ content = (guchar *) g_strndup
+ (a_this->content.str->stryng->str,
+ a_this->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf (str_buf,
+ "\"%s\"", content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ case TERM_IDENT:
+ if (a_this->content.str) {
+ content = (guchar *) g_strndup
+ (a_this->content.str->stryng->str,
+ a_this->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append (str_buf, (const gchar *) content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ case TERM_URI:
+ if (a_this->content.str) {
+ content = (guchar *) g_strndup
+ (a_this->content.str->stryng->str,
+ a_this->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf
+ (str_buf, "url(%s)", content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ case TERM_RGB:
+ if (a_this->content.rgb) {
+ guchar *tmp_str = NULL;
+
+ g_string_append_printf (str_buf, "rgb(");
+ tmp_str = cr_rgb_to_string (a_this->content.rgb);
+
+ if (tmp_str) {
+ g_string_append (str_buf, (const gchar *) tmp_str);
+ g_free (tmp_str);
+ tmp_str = NULL;
+ }
+ g_string_append_printf (str_buf, ")");
+ }
+
+ break;
+
+ case TERM_UNICODERANGE:
+ g_string_append_printf
+ (str_buf,
+ "?found unicoderange: dump not supported yet?");
+ break;
+
+ case TERM_HASH:
+ if (a_this->content.str) {
+ content = (guchar *) g_strndup
+ (a_this->content.str->stryng->str,
+ a_this->content.str->stryng->len);
+ }
+
+ if (content) {
+ g_string_append_printf (str_buf,
+ "#%s", content);
+ g_free (content);
+ content = NULL;
+ }
+ break;
+
+ default:
+ g_string_append_printf (str_buf,
+ "%s",
+ "Unrecognized Term type");
+ break;
+ }
+
+ if (str_buf) {
+ result = (guchar *) g_string_free (str_buf, FALSE);
+ str_buf = NULL;
+ }
+
+ return result;
+}
+
+/**
+ *Dumps the expression (a list of terms connected by operators)
+ *to a file.
+ *TODO: finish the dump. The dump of some type of terms have not yet been
+ *implemented.
+ *@param a_this the current instance of #CRTerm.
+ *@param a_fp the destination file pointer.
+ */
+void
+cr_term_dump (CRTerm const * a_this, FILE * a_fp)
+{
+ guchar *content = NULL;
+
+ g_return_if_fail (a_this);
+
+ content = cr_term_to_string (a_this);
+
+ if (content) {
+ fprintf (a_fp, "%s", content);
+ g_free (content);
+ }
+}
+
+/**
+ *Return the number of terms in the expression.
+ *@param a_this the current instance of #CRTerm.
+ *@return number of terms in the expression.
+ */
+int
+cr_term_nr_values (CRTerm const *a_this)
+{
+ CRTerm const *cur = NULL ;
+ int nr = 0;
+
+ g_return_val_if_fail (a_this, -1) ;
+
+ for (cur = a_this ; cur ; cur = cur->next)
+ nr ++;
+ return nr;
+}
+
+/**
+ *Use an index to get a CRTerm from the expression.
+ *@param a_this the current instance of #CRTerm.
+ *@param itemnr the index into the expression.
+ *@return CRTerm at position itemnr, if itemnr > number of terms - 1,
+ *it will return NULL.
+ */
+CRTerm *
+cr_term_get_from_list (CRTerm *a_this, int itemnr)
+{
+ CRTerm *cur = NULL ;
+ int nr = 0;
+
+ g_return_val_if_fail (a_this, NULL) ;
+
+ for (cur = a_this ; cur ; cur = cur->next)
+ if (nr++ == itemnr)
+ return cur;
+ return NULL;
+}
+
+/**
+ *Increments the reference counter of the current instance
+ *of #CRTerm.*
+ *@param a_this the current instance of #CRTerm.
+ */
+void
+cr_term_ref (CRTerm * a_this)
+{
+ g_return_if_fail (a_this);
+
+ a_this->ref_count++;
+}
+
+/**
+ *Decrements the ref count of the current instance of
+ *#CRTerm. If the ref count reaches zero, the instance is
+ *destroyed.
+ *@param a_this the current instance of #CRTerm.
+ *@return TRUE if the current instance has been destroyed, FALSE otherwise.
+ */
+gboolean
+cr_term_unref (CRTerm * a_this)
+{
+ g_return_val_if_fail (a_this, FALSE);
+
+ if (a_this->ref_count) {
+ a_this->ref_count--;
+ }
+
+ if (a_this->ref_count == 0) {
+ cr_term_destroy (a_this);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ *The destructor of the the #CRTerm class.
+ *@param a_this the "this pointer" of the current instance
+ *of #CRTerm.
+ */
+void
+cr_term_destroy (CRTerm * a_this)
+{
+ g_return_if_fail (a_this);
+
+ cr_term_clear (a_this);
+
+ if (a_this->next) {
+ cr_term_destroy (a_this->next);
+ a_this->next = NULL;
+ }
+
+ if (a_this) {
+ g_free (a_this);
+ }
+
+}
diff --git a/src/st/croco/cr-term.h b/src/st/croco/cr-term.h
new file mode 100644
index 0000000..0f22dda
--- /dev/null
+++ b/src/st/croco/cr-term.h
@@ -0,0 +1,190 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include <stdio.h>
+#include <glib.h>
+#include "cr-utils.h"
+#include "cr-rgb.h"
+#include "cr-num.h"
+#include "cr-string.h"
+
+#ifndef __CR_TERM_H__
+#define __CR_TERM_H__
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *Declaration of the #CRTem class.
+ */
+
+enum CRTermType
+{
+ TERM_NO_TYPE = 0,
+ TERM_NUMBER,
+ TERM_FUNCTION,
+ TERM_STRING,
+ TERM_IDENT,
+ TERM_URI,
+ TERM_RGB,
+ TERM_UNICODERANGE,
+ TERM_HASH
+} ;
+
+
+enum UnaryOperator
+{
+ NO_UNARY_UOP = 0,
+ PLUS_UOP,
+ MINUS_UOP,
+ EMPTY_UNARY_UOP
+} ;
+
+enum Operator
+{
+ NO_OP = 0,
+ DIVIDE,
+ COMMA
+} ;
+
+struct _CRTerm ;
+typedef struct _CRTerm CRTerm ;
+
+/**
+ *An abstraction of a css2 term as
+ *defined in the CSS2 spec in appendix D.1:
+ *term ::=
+ *[ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S*
+ *| ANGLE S* | TIME S* | FREQ S* | function ]
+ * | STRING S* | IDENT S* | URI S* | RGB S*
+ *| UNICODERANGE S* | hexcolor
+ */
+struct _CRTerm
+{
+ /**
+ *The type of the term.
+ */
+ enum CRTermType type ;
+
+ /**
+ *The unary operator associated to
+ *the current term.
+ */
+ enum UnaryOperator unary_op ;
+
+ /**
+ *The operator associated to the current term.
+ */
+ enum Operator the_operator ;
+
+
+ /**
+ *The content of the term.
+ *Depending of the type of the term,
+ *this holds either a number, a percentage ...
+ */
+ union
+ {
+ CRNum *num ;
+ CRString * str ;
+ CRRgb * rgb ;
+ } content ;
+
+ /**
+ *If the term is of type UNICODERANGE,
+ *this field holds the upper bound of the range.
+ *if the term is of type FUNCTION, this holds
+ *an instance of CRTerm that represents
+ * the expression which is the argument of the function.
+ */
+ union
+ {
+ CRTerm *func_param ;
+ } ext_content ;
+
+ /**
+ *A spare pointer, just in case.
+ *Can be used by the application.
+ */
+ gpointer app_data ;
+
+ glong ref_count ;
+
+ /**
+ *A pointer to the next term,
+ *just in case this term is part of
+ *an expression.
+ */
+ CRTerm *next ;
+
+ /**
+ *A pointer to the previous
+ *term.
+ */
+ CRTerm *prev ;
+ CRParsingLocation location ;
+} ;
+
+CRTerm * cr_term_parse_expression_from_buf (const guchar *a_buf,
+ enum CREncoding a_encoding) ;
+CRTerm * cr_term_new (void) ;
+
+enum CRStatus cr_term_set_number (CRTerm *a_this, CRNum *a_num) ;
+
+enum CRStatus cr_term_set_function (CRTerm *a_this,
+ CRString *a_func_name,
+ CRTerm *a_func_param) ;
+
+enum CRStatus cr_term_set_string (CRTerm *a_this, CRString *a_str) ;
+
+enum CRStatus cr_term_set_ident (CRTerm *a_this, CRString *a_str) ;
+
+enum CRStatus cr_term_set_uri (CRTerm *a_this, CRString *a_str) ;
+
+enum CRStatus cr_term_set_rgb (CRTerm *a_this, CRRgb *a_rgb) ;
+
+enum CRStatus cr_term_set_hash (CRTerm *a_this, CRString *a_str) ;
+
+CRTerm * cr_term_append_term (CRTerm *a_this, CRTerm *a_new_term) ;
+
+CRTerm * cr_term_prepend_term (CRTerm *a_this, CRTerm *a_new_term) ;
+
+guchar * cr_term_to_string (CRTerm const *a_this) ;
+
+guchar * cr_term_one_to_string (CRTerm const * a_this) ;
+
+void cr_term_dump (CRTerm const *a_this, FILE *a_fp) ;
+
+int cr_term_nr_values (CRTerm const *a_this) ;
+
+CRTerm * cr_term_get_from_list (CRTerm *a_this, int itemnr) ;
+
+void cr_term_ref (CRTerm *a_this) ;
+
+gboolean cr_term_unref (CRTerm *a_this) ;
+
+void cr_term_destroy (CRTerm * a_term) ;
+
+G_END_DECLS
+
+#endif /*__CR_TERM_H__*/
diff --git a/src/st/croco/cr-tknzr.c b/src/st/croco/cr-tknzr.c
new file mode 100644
index 0000000..54f18f2
--- /dev/null
+++ b/src/st/croco/cr-tknzr.c
@@ -0,0 +1,2762 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See the COPYRIGHTS file for copyrights information.
+ */
+
+/**
+ *@file
+ *The definition of the #CRTknzr (tokenizer)
+ *class.
+ */
+
+#include "string.h"
+#include "cr-tknzr.h"
+#include "cr-doc-handler.h"
+
+struct _CRTknzrPriv {
+ /**The parser input stream of bytes*/
+ CRInput *input;
+
+ /**
+ *A cache where tknzr_unget_token()
+ *puts back the token. tknzr_get_next_token()
+ *first look in this cache, and if and
+ *only if it's empty, fetches the next token
+ *from the input stream.
+ */
+ CRToken *token_cache;
+
+ /**
+ *The position of the end of the previous token
+ *or char fetched.
+ */
+ CRInputPos prev_pos;
+
+ CRDocHandler *sac_handler;
+
+ /**
+ *The reference count of the current instance
+ *of #CRTknzr. Is manipulated by cr_tknzr_ref()
+ *and cr_tknzr_unref().
+ */
+ glong ref_count;
+};
+
+#define PRIVATE(obj) ((obj)->priv)
+
+/**
+ *return TRUE if the character is a number ([0-9]), FALSE otherwise
+ *@param a_char the char to test.
+ */
+#define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
+
+/**
+ *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
+ *
+ *@param status the status (of type enum CRStatus) to test.
+ *@param is_exception if set to FALSE, the final status returned the
+ *current function will be CR_PARSING_ERROR. If set to TRUE, the
+ *current status will be the current value of the 'status' variable.
+ *
+ */
+#define CHECK_PARSING_STATUS(status, is_exception) \
+if ((status) != CR_OK) \
+{ \
+ if (is_exception == FALSE) \
+ { \
+ status = CR_PARSING_ERROR ; \
+ } \
+ goto error ; \
+}
+
+/**
+ *Peeks the next char from the input stream of the current tokenizer.
+ *invokes CHECK_PARSING_STATUS on the status returned by
+ *cr_tknzr_input_peek_char().
+ *
+ *@param the current instance of #CRTkzr.
+ *@param to_char a pointer to the char where to store the
+ *char peeked.
+ */
+#define PEEK_NEXT_CHAR(a_tknzr, a_to_char) \
+{\
+status = cr_tknzr_peek_char (a_tknzr, a_to_char) ; \
+CHECK_PARSING_STATUS (status, TRUE) \
+}
+
+/**
+ *Reads the next char from the input stream of the current parser.
+ *In case of error, jumps to the "error:" label located in the
+ *function where this macro is called.
+ *@param parser the current instance of #CRTknzr
+ *@param to_char a pointer to the guint32 char where to store
+ *the character read.
+ */
+#define READ_NEXT_CHAR(a_tknzr, to_char) \
+status = cr_tknzr_read_char (a_tknzr, to_char) ;\
+CHECK_PARSING_STATUS (status, TRUE)
+
+/**
+ *Gets information about the current position in
+ *the input of the parser.
+ *In case of failure, this macro returns from the
+ *calling function and
+ *returns a status code of type enum #CRStatus.
+ *@param parser the current instance of #CRTknzr.
+ *@param pos out parameter. A pointer to the position
+ *inside the current parser input. Must
+ */
+#define RECORD_INITIAL_POS(a_tknzr, a_pos) \
+status = cr_input_get_cur_pos (PRIVATE \
+(a_tknzr)->input, a_pos) ; \
+g_return_val_if_fail (status == CR_OK, status)
+
+/**
+ *Gets the address of the current byte inside the
+ *parser input.
+ *@param parser the current instance of #CRTknzr.
+ *@param addr out parameter a pointer (guchar*)
+ *to where the address must be put.
+ */
+#define RECORD_CUR_BYTE_ADDR(a_tknzr, a_addr) \
+status = cr_input_get_cur_byte_addr \
+ (PRIVATE (a_tknzr)->input, a_addr) ; \
+CHECK_PARSING_STATUS (status, TRUE)
+
+/**
+ *Peeks a byte from the topmost parser input at
+ *a given offset from the current position.
+ *If it fails, goto the "error:" label.
+ *
+ *@param a_parser the current instance of #CRTknzr.
+ *@param a_offset the offset of the byte to peek, the
+ *current byte having the offset '0'.
+ *@param a_byte_ptr out parameter a pointer (guchar*) to
+ *where the peeked char is to be stored.
+ */
+#define PEEK_BYTE(a_tknzr, a_offset, a_byte_ptr) \
+status = cr_tknzr_peek_byte (a_tknzr, \
+ a_offset, \
+ a_byte_ptr) ; \
+CHECK_PARSING_STATUS (status, TRUE) ;
+
+#define BYTE(a_input, a_n, a_eof) \
+cr_input_peek_byte2 (a_input, a_n, a_eof)
+
+/**
+ *Reads a byte from the topmost parser input
+ *steam.
+ *If it fails, goto the "error" label.
+ *@param a_parser the current instance of #CRTknzr.
+ *@param a_byte_ptr the guchar * where to put the read char.
+ */
+#define READ_NEXT_BYTE(a_tknzr, a_byte_ptr) \
+status = \
+cr_input_read_byte (PRIVATE (a_tknzr)->input, a_byte_ptr) ;\
+CHECK_PARSING_STATUS (status, TRUE) ;
+
+/**
+ *Skips a given number of byte in the topmost
+ *parser input. Don't update line and column number.
+ *In case of error, jumps to the "error:" label
+ *of the surrounding function.
+ *@param a_parser the current instance of #CRTknzr.
+ *@param a_nb_bytes the number of bytes to skip.
+ */
+#define SKIP_BYTES(a_tknzr, a_nb_bytes) \
+status = cr_input_seek_index (PRIVATE (a_tknzr)->input, \
+ CR_SEEK_CUR, a_nb_bytes) ; \
+CHECK_PARSING_STATUS (status, TRUE) ;
+
+/**
+ *Skip utf8 encoded characters.
+ *Updates line and column numbers.
+ *@param a_parser the current instance of #CRTknzr.
+ *@param a_nb_chars the number of chars to skip. Must be of
+ *type glong.
+ */
+#define SKIP_CHARS(a_tknzr, a_nb_chars) \
+{ \
+gulong nb_chars = a_nb_chars ; \
+status = cr_input_consume_chars \
+ (PRIVATE (a_tknzr)->input,0, &nb_chars) ; \
+CHECK_PARSING_STATUS (status, TRUE) ; \
+}
+
+/**
+ *Tests the condition and if it is false, sets
+ *status to "CR_PARSING_ERROR" and goto the 'error'
+ *label.
+ *@param condition the condition to test.
+ */
+#define ENSURE_PARSING_COND(condition) \
+if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
+
+static enum CRStatus cr_tknzr_parse_nl (CRTknzr * a_this,
+ guchar ** a_start,
+ guchar ** a_end,
+ CRParsingLocation *a_location);
+
+static enum CRStatus cr_tknzr_parse_w (CRTknzr * a_this,
+ guchar ** a_start,
+ guchar ** a_end,
+ CRParsingLocation *a_location) ;
+
+static enum CRStatus cr_tknzr_parse_unicode_escape (CRTknzr * a_this,
+ guint32 * a_unicode,
+ CRParsingLocation *a_location) ;
+
+static enum CRStatus cr_tknzr_parse_escape (CRTknzr * a_this,
+ guint32 * a_esc_code,
+ CRParsingLocation *a_location);
+
+static enum CRStatus cr_tknzr_parse_string (CRTknzr * a_this,
+ CRString ** a_str);
+
+static enum CRStatus cr_tknzr_parse_comment (CRTknzr * a_this,
+ CRString ** a_comment);
+
+static enum CRStatus cr_tknzr_parse_nmstart (CRTknzr * a_this,
+ guint32 * a_char,
+ CRParsingLocation *a_location);
+
+static enum CRStatus cr_tknzr_parse_num (CRTknzr * a_this,
+ CRNum ** a_num);
+
+/**********************************
+ *PRIVATE methods
+ **********************************/
+
+/**
+ *Parses a "w" as defined by the css spec at [4.1.1]:
+ * w ::= [ \t\r\n\f]*
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out param. Upon successful completion, points
+ *to the beginning of the parsed white space, points to NULL otherwise.
+ *Can also point to NULL is there is no white space actually.
+ *@param a_end out param. Upon successful completion, points
+ *to the end of the parsed white space, points to NULL otherwise.
+ *Can also point to NULL is there is no white space actually.
+ */
+static enum CRStatus
+cr_tknzr_parse_w (CRTknzr * a_this,
+ guchar ** a_start,
+ guchar ** a_end,
+ CRParsingLocation *a_location)
+{
+ guint32 cur_char = 0;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_start && a_end,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ *a_start = NULL;
+ *a_end = NULL;
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if (cr_utils_is_white_space (cur_char) == FALSE) {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ if (a_location) {
+ cr_tknzr_get_parsing_location (a_this,
+ a_location) ;
+ }
+ RECORD_CUR_BYTE_ADDR (a_this, a_start);
+ *a_end = *a_start;
+
+ for (;;) {
+ gboolean is_eof = FALSE;
+
+ cr_input_get_end_of_file (PRIVATE (a_this)->input, &is_eof);
+ if (is_eof)
+ break;
+
+ status = cr_tknzr_peek_char (a_this, &cur_char);
+ if (status == CR_END_OF_INPUT_ERROR) {
+ break;
+ } else if (status != CR_OK) {
+ goto error;
+ }
+
+ if (cr_utils_is_white_space (cur_char) == TRUE) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ RECORD_CUR_BYTE_ADDR (a_this, a_end);
+ } else {
+ break;
+ }
+ }
+
+ return CR_OK;
+
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses a newline as defined in the css2 spec:
+ * nl ::= \n|\r\n|\r|\f
+ *
+ *@param a_this the "this pointer" of the current instance of #CRTknzr.
+ *@param a_start a pointer to the first character of the successfully
+ *parsed string.
+ *@param a_end a pointer to the last character of the successfully parsed
+ *string.
+ *@result CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_nl (CRTknzr * a_this,
+ guchar ** a_start,
+ guchar ** a_end,
+ CRParsingLocation *a_location)
+{
+ CRInputPos init_pos;
+ guchar next_chars[2] = { 0 };
+ enum CRStatus status = CR_PARSING_ERROR;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_start && a_end, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ PEEK_BYTE (a_this, 1, &next_chars[0]);
+ PEEK_BYTE (a_this, 2, &next_chars[1]);
+
+ if ((next_chars[0] == '\r' && next_chars[1] == '\n')) {
+ SKIP_BYTES (a_this, 1);
+ if (a_location) {
+ cr_tknzr_get_parsing_location
+ (a_this, a_location) ;
+ }
+ SKIP_CHARS (a_this, 1);
+
+ RECORD_CUR_BYTE_ADDR (a_this, a_end);
+
+ status = CR_OK;
+ } else if (next_chars[0] == '\n'
+ || next_chars[0] == '\r' || next_chars[0] == '\f') {
+ SKIP_CHARS (a_this, 1);
+ if (a_location) {
+ cr_tknzr_get_parsing_location
+ (a_this, a_location) ;
+ }
+ RECORD_CUR_BYTE_ADDR (a_this, a_start);
+ *a_end = *a_start;
+ status = CR_OK;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ return CR_OK ;
+
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+ return status;
+}
+
+/**
+ *Go ahead in the parser input, skipping all the spaces.
+ *If the next char if not a white space, this function does nothing.
+ *In any cases, it stops when it encounters a non white space character.
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_try_to_skip_spaces (CRTknzr * a_this)
+{
+ enum CRStatus status = CR_ERROR;
+ guint32 cur_char = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
+
+ status = cr_input_peek_char (PRIVATE (a_this)->input, &cur_char);
+
+ if (status != CR_OK) {
+ if (status == CR_END_OF_INPUT_ERROR)
+ return CR_OK;
+ return status;
+ }
+
+ if (cr_utils_is_white_space (cur_char) == TRUE) {
+ gulong nb_chars = -1; /*consume all spaces */
+
+ status = cr_input_consume_white_spaces
+ (PRIVATE (a_this)->input, &nb_chars);
+ }
+
+ return status;
+}
+
+/**
+ *Parses a "comment" as defined in the css spec at [4.1.1]:
+ *COMMENT ::= \/\*[^*]*\*+([^/][^*]*\*+)*\/ .
+ *This complex regexp is just to say that comments start
+ *with the two chars '/''*' and ends with the two chars '*''/'.
+ *It also means that comments cannot be nested.
+ *So based on that, I've just tried to implement the parsing function
+ *simply and in a straight forward manner.
+ */
+static enum CRStatus
+cr_tknzr_parse_comment (CRTknzr * a_this,
+ CRString ** a_comment)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ guint32 cur_char = 0, next_char= 0;
+ CRString *comment = NULL;
+ CRParsingLocation loc = {0} ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+ READ_NEXT_CHAR (a_this, &cur_char) ;
+ ENSURE_PARSING_COND (cur_char == '/');
+ cr_tknzr_get_parsing_location (a_this, &loc) ;
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+ ENSURE_PARSING_COND (cur_char == '*');
+ comment = cr_string_new ();
+ for (;;) { /* [^*]* */
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (next_char == '*')
+ break;
+ READ_NEXT_CHAR (a_this, &cur_char);
+ g_string_append_unichar (comment->stryng, cur_char);
+ }
+ /* Stop condition: next_char == '*' */
+ for (;;) { /* \*+ */
+ READ_NEXT_CHAR(a_this, &cur_char);
+ ENSURE_PARSING_COND (cur_char == '*');
+ g_string_append_unichar (comment->stryng, cur_char);
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (next_char != '*')
+ break;
+ }
+ /* Stop condition: next_char != '*' */
+ for (;;) { /* ([^/][^*]*\*+)* */
+ if (next_char == '/')
+ break;
+ READ_NEXT_CHAR(a_this, &cur_char);
+ g_string_append_unichar (comment->stryng, cur_char);
+ for (;;) { /* [^*]* */
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (next_char == '*')
+ break;
+ READ_NEXT_CHAR (a_this, &cur_char);
+ g_string_append_unichar (comment->stryng, cur_char);
+ }
+ /* Stop condition: next_char = '*', no need to verify, because peek and read exit to error anyway */
+ for (;;) { /* \*+ */
+ READ_NEXT_CHAR(a_this, &cur_char);
+ ENSURE_PARSING_COND (cur_char == '*');
+ g_string_append_unichar (comment->stryng, cur_char);
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (next_char != '*')
+ break;
+ }
+ /* Continue condition: next_char != '*' */
+ }
+ /* Stop condition: next_char == '\/' */
+ READ_NEXT_CHAR(a_this, &cur_char);
+ g_string_append_unichar (comment->stryng, cur_char);
+
+ if (status == CR_OK) {
+ cr_parsing_location_copy (&comment->location,
+ &loc) ;
+ *a_comment = comment;
+ return CR_OK;
+ }
+ error:
+
+ if (comment) {
+ cr_string_destroy (comment);
+ comment = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses an 'unicode' escape sequence defined
+ *in css spec at chap 4.1.1:
+ *unicode ::= \\[0-9a-f]{1,6}[ \n\r\t\f]?
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out parameter. A pointer to the start
+ *of the unicode escape sequence. Must *NOT* be deleted by
+ *the caller.
+ *@param a_end out parameter. A pointer to the last character
+ *of the unicode escape sequence. Must *NOT* be deleted by the caller.
+ *@return CR_OK if parsing succeeded, an error code otherwise.
+ *Error code can be either CR_PARSING_ERROR if the string
+ *parsed just doesn't
+ *respect the production or another error if a
+ *lower level error occurred.
+ */
+static enum CRStatus
+cr_tknzr_parse_unicode_escape (CRTknzr * a_this,
+ guint32 * a_unicode,
+ CRParsingLocation *a_location)
+{
+ guint32 cur_char;
+ CRInputPos init_pos;
+ glong occur = 0;
+ guint32 unicode = 0;
+ guchar *tmp_char_ptr1 = NULL,
+ *tmp_char_ptr2 = NULL;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_unicode, CR_BAD_PARAM_ERROR);
+
+ /*first, let's backup the current position pointer */
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if (cur_char != '\\') {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ if (a_location) {
+ cr_tknzr_get_parsing_location
+ (a_this, a_location) ;
+ }
+ PEEK_NEXT_CHAR (a_this, &cur_char);
+
+ for (occur = 0, unicode = 0; ((cur_char >= '0' && cur_char <= '9')
+ || (cur_char >= 'a' && cur_char <= 'f')
+ || (cur_char >= 'A' && cur_char <= 'F'))
+ && occur < 6; occur++) {
+ gint cur_char_val = 0;
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if ((cur_char >= '0' && cur_char <= '9')) {
+ cur_char_val = (cur_char - '0');
+ } else if ((cur_char >= 'a' && cur_char <= 'f')) {
+ cur_char_val = 10 + (cur_char - 'a');
+ } else if ((cur_char >= 'A' && cur_char <= 'F')) {
+ cur_char_val = 10 + (cur_char - 'A');
+ }
+
+ unicode = unicode * 16 + cur_char_val;
+
+ PEEK_NEXT_CHAR (a_this, &cur_char);
+ }
+
+ /* Eat a whitespace if possible. */
+ cr_tknzr_parse_w (a_this, &tmp_char_ptr1,
+ &tmp_char_ptr2, NULL);
+ *a_unicode = unicode;
+ return CR_OK;
+
+ error:
+ /*
+ *restore the initial position pointer backuped at
+ *the beginning of this function.
+ */
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+
+ return status;
+}
+
+/**
+ *parses an escape sequence as defined by the css spec:
+ *escape ::= {unicode}|\\[ -~\200-\4177777]
+ *@param a_this the current instance of #CRTknzr .
+ */
+static enum CRStatus
+cr_tknzr_parse_escape (CRTknzr * a_this, guint32 * a_esc_code,
+ CRParsingLocation *a_location)
+{
+ enum CRStatus status = CR_OK;
+ guint32 cur_char = 0;
+ CRInputPos init_pos;
+ guchar next_chars[2];
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_esc_code, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ PEEK_BYTE (a_this, 1, &next_chars[0]);
+ PEEK_BYTE (a_this, 2, &next_chars[1]);
+
+ if (next_chars[0] != '\\') {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ if ((next_chars[1] >= '0' && next_chars[1] <= '9')
+ || (next_chars[1] >= 'a' && next_chars[1] <= 'f')
+ || (next_chars[1] >= 'A' && next_chars[1] <= 'F')) {
+ status = cr_tknzr_parse_unicode_escape (a_this, a_esc_code,
+ a_location);
+ } else {
+ /*consume the '\' char */
+ READ_NEXT_CHAR (a_this, &cur_char);
+ if (a_location) {
+ cr_tknzr_get_parsing_location (a_this,
+ a_location) ;
+ }
+ /*then read the char after the '\' */
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if (cur_char != ' ' && (cur_char < 200 || cur_char > 4177777)) {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ *a_esc_code = cur_char;
+
+ }
+ if (status == CR_OK) {
+ return CR_OK;
+ }
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return status;
+}
+
+/**
+ *Parses a string type as defined in css spec [4.1.1]:
+ *
+ *string ::= {string1}|{string2}
+ *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
+ *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out parameter. Upon successful completion,
+ *points to the beginning of the string, points to an undefined value
+ *otherwise.
+ *@param a_end out parameter. Upon successful completion, points to
+ *the beginning of the string, points to an undefined value otherwise.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_string (CRTknzr * a_this, CRString ** a_str)
+{
+ guint32 cur_char = 0,
+ delim = 0;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+ CRString *str = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if (cur_char == '"')
+ delim = '"';
+ else if (cur_char == '\'')
+ delim = '\'';
+ else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ str = cr_string_new ();
+ if (str) {
+ cr_tknzr_get_parsing_location
+ (a_this, &str->location) ;
+ }
+ for (;;) {
+ guchar next_chars[2] = { 0 };
+
+ PEEK_BYTE (a_this, 1, &next_chars[0]);
+ PEEK_BYTE (a_this, 2, &next_chars[1]);
+
+ if (next_chars[0] == '\\') {
+ guchar *tmp_char_ptr1 = NULL,
+ *tmp_char_ptr2 = NULL;
+ guint32 esc_code = 0;
+
+ if (next_chars[1] == '\'' || next_chars[1] == '"') {
+ g_string_append_unichar (str->stryng,
+ next_chars[1]);
+ SKIP_BYTES (a_this, 2);
+ status = CR_OK;
+ } else {
+ status = cr_tknzr_parse_escape
+ (a_this, &esc_code, NULL);
+
+ if (status == CR_OK) {
+ g_string_append_unichar
+ (str->stryng,
+ esc_code);
+ }
+ }
+
+ if (status != CR_OK) {
+ /*
+ *consume the '\' char, and try to parse
+ *a newline.
+ */
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ status = cr_tknzr_parse_nl
+ (a_this, &tmp_char_ptr1,
+ &tmp_char_ptr2, NULL);
+ }
+
+ CHECK_PARSING_STATUS (status, FALSE);
+ } else if (strchr ("\t !#$%&", next_chars[0])
+ || (next_chars[0] >= '(' && next_chars[0] <= '~')) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ g_string_append_unichar (str->stryng,
+ cur_char);
+ status = CR_OK;
+ }
+
+ else if (cr_utils_is_nonascii (next_chars[0])) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ g_string_append_unichar (str->stryng, cur_char);
+ } else if (next_chars[0] == delim) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ break;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ }
+
+ if (status == CR_OK) {
+ if (*a_str == NULL) {
+ *a_str = str;
+ str = NULL;
+ } else {
+ (*a_str)->stryng = g_string_append_len
+ ((*a_str)->stryng,
+ str->stryng->str,
+ str->stryng->len);
+ cr_string_destroy (str);
+ }
+ return CR_OK;
+ }
+
+ error:
+
+ if (str) {
+ cr_string_destroy (str) ;
+ str = NULL;
+ }
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return status;
+}
+
+/**
+ *Parses the an nmstart as defined by the css2 spec [4.1.1]:
+ * nmstart [a-zA-Z]|{nonascii}|{escape}
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out param. A pointer to the starting point of
+ *the token.
+ *@param a_end out param. A pointer to the ending point of the
+ *token.
+ *@param a_char out param. The actual parsed nmchar.
+ *@return CR_OK upon successful completion,
+ *an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_nmstart (CRTknzr * a_this,
+ guint32 * a_char,
+ CRParsingLocation *a_location)
+{
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+ guint32 cur_char = 0,
+ next_char = 0;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_char, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ PEEK_NEXT_CHAR (a_this, &next_char);
+
+ if (next_char == '\\') {
+ status = cr_tknzr_parse_escape (a_this, a_char,
+ a_location);
+
+ if (status != CR_OK)
+ goto error;
+
+ } else if (cr_utils_is_nonascii (next_char) == TRUE
+ || ((next_char >= 'a') && (next_char <= 'z'))
+ || ((next_char >= 'A') && (next_char <= 'Z'))
+ ) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ if (a_location) {
+ cr_tknzr_get_parsing_location (a_this,
+ a_location) ;
+ }
+ *a_char = cur_char;
+ status = CR_OK;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ return CR_OK;
+
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+
+ return status;
+
+}
+
+/**
+ *Parses an nmchar as described in the css spec at
+ *chap 4.1.1:
+ *nmchar ::= [a-z0-9-]|{nonascii}|{escape}
+ *
+ *Humm, I have added the possibility for nmchar to
+ *contain upper case letters.
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_start out param. A pointer to the starting point of
+ *the token.
+ *@param a_end out param. A pointer to the ending point of the
+ *token.
+ *@param a_char out param. The actual parsed nmchar.
+ *@return CR_OK upon successful completion,
+ *an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_nmchar (CRTknzr * a_this, guint32 * a_char,
+ CRParsingLocation *a_location)
+{
+ guint32 cur_char = 0,
+ next_char = 0;
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_input_peek_char (PRIVATE (a_this)->input,
+ &next_char) ;
+ if (status != CR_OK)
+ goto error;
+
+ if (next_char == '\\') {
+ status = cr_tknzr_parse_escape (a_this, a_char,
+ a_location);
+
+ if (status != CR_OK)
+ goto error;
+
+ } else if (cr_utils_is_nonascii (next_char) == TRUE
+ || ((next_char >= 'a') && (next_char <= 'z'))
+ || ((next_char >= 'A') && (next_char <= 'Z'))
+ || ((next_char >= '0') && (next_char <= '9'))
+ || (next_char == '-')
+ || (next_char == '_') /*'_' not allowed by the spec. */
+ ) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ *a_char = cur_char;
+ status = CR_OK;
+ if (a_location) {
+ cr_tknzr_get_parsing_location
+ (a_this, a_location) ;
+ }
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ return CR_OK;
+
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return status;
+}
+
+/**
+ *Parses an "ident" as defined in css spec [4.1.1]:
+ *ident ::= {nmstart}{nmchar}*
+ *
+ *Actually parses it using the css3 grammar:
+ *ident ::= -?{nmstart}{nmchar}*
+ *@param a_this the currens instance of #CRTknzr.
+ *
+ *@param a_str a pointer to parsed ident. If *a_str is NULL,
+ *this function allocates a new instance of CRString. If not,
+ *the function just appends the parsed string to the one passed.
+ *In both cases it is up to the caller to free *a_str.
+ *
+ *@return CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_ident (CRTknzr * a_this, CRString ** a_str)
+{
+ guint32 tmp_char = 0;
+ CRString *stringue = NULL ;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+ gboolean location_is_set = FALSE ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+ PEEK_NEXT_CHAR (a_this, &tmp_char) ;
+ stringue = cr_string_new () ;
+ g_return_val_if_fail (stringue,
+ CR_OUT_OF_MEMORY_ERROR) ;
+
+ if (tmp_char == '-') {
+ READ_NEXT_CHAR (a_this, &tmp_char) ;
+ cr_tknzr_get_parsing_location
+ (a_this, &stringue->location) ;
+ location_is_set = TRUE ;
+ g_string_append_unichar (stringue->stryng,
+ tmp_char) ;
+ }
+ status = cr_tknzr_parse_nmstart (a_this, &tmp_char, NULL);
+ if (status != CR_OK) {
+ status = CR_PARSING_ERROR;
+ goto end ;
+ }
+ if (location_is_set == FALSE) {
+ cr_tknzr_get_parsing_location
+ (a_this, &stringue->location) ;
+ location_is_set = TRUE ;
+ }
+ g_string_append_unichar (stringue->stryng, tmp_char);
+ for (;;) {
+ status = cr_tknzr_parse_nmchar (a_this,
+ &tmp_char,
+ NULL);
+ if (status != CR_OK) {
+ status = CR_OK ;
+ break;
+ }
+ g_string_append_unichar (stringue->stryng, tmp_char);
+ }
+ if (status == CR_OK) {
+ if (!*a_str) {
+ *a_str = stringue ;
+
+ } else {
+ g_string_append_len ((*a_str)->stryng,
+ stringue->stryng->str,
+ stringue->stryng->len) ;
+ cr_string_destroy (stringue) ;
+ }
+ stringue = NULL ;
+ }
+
+ error:
+ end:
+ if (stringue) {
+ cr_string_destroy (stringue) ;
+ stringue = NULL ;
+ }
+ if (status != CR_OK ) {
+ cr_tknzr_set_cur_pos (a_this, &init_pos) ;
+ }
+ return status ;
+}
+
+
+/**
+ *Parses a "name" as defined by css spec [4.1.1]:
+ *name ::= {nmchar}+
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *
+ *@param a_str out parameter. A pointer to the successfully parsed
+ *name. If *a_str is set to NULL, this function allocates a new instance
+ *of CRString. If not, it just appends the parsed name to the passed *a_str.
+ *In both cases, it is up to the caller to free *a_str.
+ *
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_name (CRTknzr * a_this,
+ CRString ** a_str)
+{
+ guint32 tmp_char = 0;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+ gboolean str_needs_free = FALSE,
+ is_first_nmchar=TRUE ;
+ glong i = 0;
+ CRParsingLocation loc = {0} ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str,
+ CR_BAD_PARAM_ERROR) ;
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ if (*a_str == NULL) {
+ *a_str = cr_string_new ();
+ str_needs_free = TRUE;
+ }
+ for (i = 0;; i++) {
+ if (is_first_nmchar == TRUE) {
+ status = cr_tknzr_parse_nmchar
+ (a_this, &tmp_char,
+ &loc) ;
+ is_first_nmchar = FALSE ;
+ } else {
+ status = cr_tknzr_parse_nmchar
+ (a_this, &tmp_char, NULL) ;
+ }
+ if (status != CR_OK)
+ break;
+ g_string_append_unichar ((*a_str)->stryng,
+ tmp_char);
+ }
+ if (i > 0) {
+ cr_parsing_location_copy
+ (&(*a_str)->location, &loc) ;
+ return CR_OK;
+ }
+ if (str_needs_free == TRUE && *a_str) {
+ cr_string_destroy (*a_str);
+ *a_str = NULL;
+ }
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return CR_PARSING_ERROR;
+}
+
+/**
+ *Parses a "hash" as defined by the css spec in [4.1.1]:
+ *HASH ::= #{name}
+ */
+static enum CRStatus
+cr_tknzr_parse_hash (CRTknzr * a_this, CRString ** a_str)
+{
+ guint32 cur_char = 0;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+ gboolean str_needs_free = FALSE;
+ CRParsingLocation loc = {0} ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+ READ_NEXT_CHAR (a_this, &cur_char);
+ if (cur_char != '#') {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ if (*a_str == NULL) {
+ *a_str = cr_string_new ();
+ str_needs_free = TRUE;
+ }
+ cr_tknzr_get_parsing_location (a_this,
+ &loc) ;
+ status = cr_tknzr_parse_name (a_this, a_str);
+ cr_parsing_location_copy (&(*a_str)->location, &loc) ;
+ if (status != CR_OK) {
+ goto error;
+ }
+ return CR_OK;
+
+ error:
+ if (str_needs_free == TRUE && *a_str) {
+ cr_string_destroy (*a_str);
+ *a_str = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return status;
+}
+
+/**
+ *Parses an uri as defined by the css spec [4.1.1]:
+ * URI ::= url\({w}{string}{w}\)
+ * |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
+ *
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_str the successfully parsed url.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_uri (CRTknzr * a_this,
+ CRString ** a_str)
+{
+ guint32 cur_char = 0;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_PARSING_ERROR;
+ guchar tab[4] = { 0 }, *tmp_ptr1 = NULL, *tmp_ptr2 = NULL;
+ CRString *str = NULL;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this
+ && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ PEEK_BYTE (a_this, 1, &tab[0]);
+ PEEK_BYTE (a_this, 2, &tab[1]);
+ PEEK_BYTE (a_this, 3, &tab[2]);
+ PEEK_BYTE (a_this, 4, &tab[3]);
+
+ if (tab[0] != 'u' || tab[1] != 'r' || tab[2] != 'l' || tab[3] != '(') {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ /*
+ *Here, we want to skip 4 bytes ('u''r''l''(').
+ *But we also need to keep track of the parsing location
+ *of the 'u'. So, we skip 1 byte, we record the parsing
+ *location, then we skip the 3 remaining bytes.
+ */
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this, &location) ;
+ SKIP_CHARS (a_this, 3);
+ cr_tknzr_try_to_skip_spaces (a_this);
+ status = cr_tknzr_parse_string (a_this, a_str);
+
+ if (status == CR_OK) {
+ guint32 next_char = 0;
+ status = cr_tknzr_parse_w (a_this, &tmp_ptr1,
+ &tmp_ptr2, NULL);
+ cr_tknzr_try_to_skip_spaces (a_this);
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (next_char == ')') {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ status = CR_OK;
+ } else {
+ status = CR_PARSING_ERROR;
+ }
+ }
+ if (status != CR_OK) {
+ str = cr_string_new ();
+ for (;;) {
+ guint32 next_char = 0;
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ if (strchr ("!#$%&", next_char)
+ || (next_char >= '*' && next_char <= '~')
+ || (cr_utils_is_nonascii (next_char) == TRUE)) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ g_string_append_unichar
+ (str->stryng, cur_char);
+ status = CR_OK;
+ } else {
+ guint32 esc_code = 0;
+ status = cr_tknzr_parse_escape
+ (a_this, &esc_code, NULL);
+ if (status == CR_OK) {
+ g_string_append_unichar
+ (str->stryng,
+ esc_code);
+ } else {
+ status = CR_OK;
+ break;
+ }
+ }
+ }
+ cr_tknzr_try_to_skip_spaces (a_this);
+ READ_NEXT_CHAR (a_this, &cur_char);
+ if (cur_char == ')') {
+ status = CR_OK;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ if (str) {
+ if (*a_str == NULL) {
+ *a_str = str;
+ str = NULL;
+ } else {
+ g_string_append_len
+ ((*a_str)->stryng,
+ str->stryng->str,
+ str->stryng->len);
+ cr_string_destroy (str);
+ }
+ }
+ }
+
+ cr_parsing_location_copy
+ (&(*a_str)->location,
+ &location) ;
+ return CR_OK ;
+ error:
+ if (str) {
+ cr_string_destroy (str);
+ str = NULL;
+ }
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return status;
+}
+
+/**
+ *parses an RGB as defined in the css2 spec.
+ *rgb: rgb '('S*{num}%?S* ',' {num}#?S*,S*{num}#?S*')'
+ *
+ *@param a_this the "this pointer" of the current instance of
+ *@param a_rgb out parameter the parsed rgb.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_rgb (CRTknzr * a_this, CRRgb ** a_rgb)
+{
+ enum CRStatus status = CR_OK;
+ CRInputPos init_pos;
+ CRNum *num = NULL;
+ guchar next_bytes[3] = { 0 }, cur_byte = 0;
+ glong red = 0,
+ green = 0,
+ blue = 0,
+ i = 0;
+ gboolean is_percentage = FALSE;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ PEEK_BYTE (a_this, 1, &next_bytes[0]);
+ PEEK_BYTE (a_this, 2, &next_bytes[1]);
+ PEEK_BYTE (a_this, 3, &next_bytes[2]);
+
+ if (((next_bytes[0] == 'r') || (next_bytes[0] == 'R'))
+ && ((next_bytes[1] == 'g') || (next_bytes[1] == 'G'))
+ && ((next_bytes[2] == 'b') || (next_bytes[2] == 'B'))) {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this, &location) ;
+ SKIP_CHARS (a_this, 2);
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ READ_NEXT_BYTE (a_this, &cur_byte);
+ ENSURE_PARSING_COND (cur_byte == '(');
+
+ cr_tknzr_try_to_skip_spaces (a_this);
+ status = cr_tknzr_parse_num (a_this, &num);
+ ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
+
+ if (num->val > G_MAXLONG) {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ red = num->val;
+ cr_num_destroy (num);
+ num = NULL;
+
+ PEEK_BYTE (a_this, 1, &next_bytes[0]);
+ if (next_bytes[0] == '%') {
+ SKIP_CHARS (a_this, 1);
+ is_percentage = TRUE;
+ }
+ cr_tknzr_try_to_skip_spaces (a_this);
+
+ for (i = 0; i < 2; i++) {
+ READ_NEXT_BYTE (a_this, &cur_byte);
+ ENSURE_PARSING_COND (cur_byte == ',');
+
+ cr_tknzr_try_to_skip_spaces (a_this);
+ status = cr_tknzr_parse_num (a_this, &num);
+ ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
+
+ if (num->val > G_MAXLONG) {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ PEEK_BYTE (a_this, 1, &next_bytes[0]);
+ if (next_bytes[0] == '%') {
+ SKIP_CHARS (a_this, 1);
+ is_percentage = 1;
+ }
+
+ if (i == 0) {
+ green = num->val;
+ } else if (i == 1) {
+ blue = num->val;
+ }
+
+ if (num) {
+ cr_num_destroy (num);
+ num = NULL;
+ }
+ cr_tknzr_try_to_skip_spaces (a_this);
+ }
+
+ READ_NEXT_BYTE (a_this, &cur_byte);
+ if (*a_rgb == NULL) {
+ *a_rgb = cr_rgb_new_with_vals (red, green, blue,
+ is_percentage);
+
+ if (*a_rgb == NULL) {
+ status = CR_ERROR;
+ goto error;
+ }
+ status = CR_OK;
+ } else {
+ (*a_rgb)->red = red;
+ (*a_rgb)->green = green;
+ (*a_rgb)->blue = blue;
+ (*a_rgb)->is_percentage = is_percentage;
+
+ status = CR_OK;
+ }
+
+ if (status == CR_OK) {
+ if (a_rgb && *a_rgb) {
+ cr_parsing_location_copy
+ (&(*a_rgb)->location,
+ &location) ;
+ }
+ return CR_OK;
+ }
+
+ error:
+ if (num) {
+ cr_num_destroy (num);
+ num = NULL;
+ }
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return CR_OK;
+}
+
+/**
+ *Parses a atkeyword as defined by the css spec in [4.1.1]:
+ *ATKEYWORD ::= @{ident}
+ *
+ *@param a_this the "this pointer" of the current instance of
+ *#CRTknzr.
+ *
+ *@param a_str out parameter. The parsed atkeyword. If *a_str is
+ *set to NULL this function allocates a new instance of CRString and
+ *sets it to the parsed atkeyword. If not, this function just appends
+ *the parsed atkeyword to the end of *a_str. In both cases it is up to
+ *the caller to free *a_str.
+ *
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+static enum CRStatus
+cr_tknzr_parse_atkeyword (CRTknzr * a_this,
+ CRString ** a_str)
+{
+ guint32 cur_char = 0;
+ CRInputPos init_pos;
+ gboolean str_needs_free = FALSE;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_str, CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if (cur_char != '@') {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ if (*a_str == NULL) {
+ *a_str = cr_string_new ();
+ str_needs_free = TRUE;
+ }
+ status = cr_tknzr_parse_ident (a_this, a_str);
+ if (status != CR_OK) {
+ goto error;
+ }
+ return CR_OK;
+ error:
+
+ if (str_needs_free == TRUE && *a_str) {
+ cr_string_destroy (*a_str);
+ *a_str = NULL;
+ }
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return status;
+}
+
+static enum CRStatus
+cr_tknzr_parse_important (CRTknzr * a_this,
+ CRParsingLocation *a_location)
+{
+ guint32 cur_char = 0;
+ CRInputPos init_pos;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+ READ_NEXT_CHAR (a_this, &cur_char);
+ ENSURE_PARSING_COND (cur_char == '!');
+ if (a_location) {
+ cr_tknzr_get_parsing_location (a_this,
+ a_location) ;
+ }
+ cr_tknzr_try_to_skip_spaces (a_this);
+
+ if (BYTE (PRIVATE (a_this)->input, 1, NULL) == 'i'
+ && BYTE (PRIVATE (a_this)->input, 2, NULL) == 'm'
+ && BYTE (PRIVATE (a_this)->input, 3, NULL) == 'p'
+ && BYTE (PRIVATE (a_this)->input, 4, NULL) == 'o'
+ && BYTE (PRIVATE (a_this)->input, 5, NULL) == 'r'
+ && BYTE (PRIVATE (a_this)->input, 6, NULL) == 't'
+ && BYTE (PRIVATE (a_this)->input, 7, NULL) == 'a'
+ && BYTE (PRIVATE (a_this)->input, 8, NULL) == 'n'
+ && BYTE (PRIVATE (a_this)->input, 9, NULL) == 't') {
+ SKIP_BYTES (a_this, 9);
+ if (a_location) {
+ cr_tknzr_get_parsing_location (a_this,
+ a_location) ;
+ }
+ return CR_OK;
+ } else {
+ status = CR_PARSING_ERROR;
+ }
+
+ error:
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+
+ return status;
+}
+
+/**
+ *Parses a num as defined in the css spec [4.1.1]:
+ *[0-9]+|[0-9]*\.[0-9]+
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_num out parameter. The parsed number.
+ *@return CR_OK upon successful completion,
+ *an error code otherwise.
+ *
+ *The CSS specification says that numbers may be
+ *preceded by '+' or '-' to indicate the sign.
+ *Technically, the "num" construction as defined
+ *by the tokenizer doesn't allow this, but we parse
+ *it here for simplicity.
+ */
+static enum CRStatus
+cr_tknzr_parse_num (CRTknzr * a_this,
+ CRNum ** a_num)
+{
+ enum CRStatus status = CR_PARSING_ERROR;
+ enum CRNumType val_type = NUM_GENERIC;
+ gboolean parsing_dec, /* true iff seen decimal point. */
+ parsed; /* true iff the substring seen so far is a valid CSS
+ number, i.e. `[0-9]+|[0-9]*\.[0-9]+'. */
+ guint32 cur_char = 0,
+ next_char = 0;
+ gdouble numerator, denominator = 1;
+ CRInputPos init_pos;
+ CRParsingLocation location = {0} ;
+ int sign = 1;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR);
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+ READ_NEXT_CHAR (a_this, &cur_char);
+
+ if (cur_char == '+' || cur_char == '-') {
+ if (cur_char == '-') {
+ sign = -1;
+ }
+ READ_NEXT_CHAR (a_this, &cur_char);
+ }
+
+ if (IS_NUM (cur_char)) {
+ numerator = (cur_char - '0');
+ parsing_dec = FALSE;
+ parsed = TRUE;
+ } else if (cur_char == '.') {
+ numerator = 0;
+ parsing_dec = TRUE;
+ parsed = FALSE;
+ } else {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+ cr_tknzr_get_parsing_location (a_this, &location) ;
+
+ for (;;) {
+ status = cr_tknzr_peek_char (a_this, &next_char);
+ if (status != CR_OK) {
+ if (status == CR_END_OF_INPUT_ERROR)
+ status = CR_OK;
+ break;
+ }
+ if (next_char == '.') {
+ if (parsing_dec) {
+ status = CR_PARSING_ERROR;
+ goto error;
+ }
+
+ READ_NEXT_CHAR (a_this, &cur_char);
+ parsing_dec = TRUE;
+ parsed = FALSE; /* In CSS, there must be at least
+ one digit after `.'. */
+ } else if (IS_NUM (next_char)) {
+ READ_NEXT_CHAR (a_this, &cur_char);
+ parsed = TRUE;
+
+ numerator = numerator * 10 + (cur_char - '0');
+ if (parsing_dec) {
+ denominator *= 10;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (!parsed) {
+ status = CR_PARSING_ERROR;
+ }
+
+ /*
+ *Now, set the output param values.
+ */
+ if (status == CR_OK) {
+ gdouble val = (numerator / denominator) * sign;
+ if (*a_num == NULL) {
+ *a_num = cr_num_new_with_val (val, val_type);
+
+ if (*a_num == NULL) {
+ status = CR_ERROR;
+ goto error;
+ }
+ } else {
+ (*a_num)->val = val;
+ (*a_num)->type = val_type;
+ }
+ cr_parsing_location_copy (&(*a_num)->location,
+ &location) ;
+ return CR_OK;
+ }
+
+ error:
+
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+
+ return status;
+}
+
+/*********************************************
+ *PUBLIC methods
+ ********************************************/
+
+CRTknzr *
+cr_tknzr_new (CRInput * a_input)
+{
+ CRTknzr *result = NULL;
+
+ result = g_try_malloc (sizeof (CRTknzr));
+
+ if (result == NULL) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRTknzr));
+
+ result->priv = g_try_malloc (sizeof (CRTknzrPriv));
+
+ if (result->priv == NULL) {
+ cr_utils_trace_info ("Out of memory");
+
+ if (result) {
+ g_free (result);
+ result = NULL;
+ }
+
+ return NULL;
+ }
+ memset (result->priv, 0, sizeof (CRTknzrPriv));
+ if (a_input)
+ cr_tknzr_set_input (result, a_input);
+ return result;
+}
+
+CRTknzr *
+cr_tknzr_new_from_buf (guchar * a_buf, gulong a_len,
+ enum CREncoding a_enc,
+ gboolean a_free_at_destroy)
+{
+ CRTknzr *result = NULL;
+ CRInput *input = NULL;
+
+ input = cr_input_new_from_buf (a_buf, a_len, a_enc,
+ a_free_at_destroy);
+
+ g_return_val_if_fail (input != NULL, NULL);
+
+ result = cr_tknzr_new (input);
+
+ return result;
+}
+
+CRTknzr *
+cr_tknzr_new_from_uri (const guchar * a_file_uri,
+ enum CREncoding a_enc)
+{
+ CRTknzr *result = NULL;
+ CRInput *input = NULL;
+
+ input = cr_input_new_from_uri ((const gchar *) a_file_uri, a_enc);
+ g_return_val_if_fail (input != NULL, NULL);
+
+ result = cr_tknzr_new (input);
+
+ return result;
+}
+
+void
+cr_tknzr_ref (CRTknzr * a_this)
+{
+ g_return_if_fail (a_this && PRIVATE (a_this));
+
+ PRIVATE (a_this)->ref_count++;
+}
+
+gboolean
+cr_tknzr_unref (CRTknzr * a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE);
+
+ if (PRIVATE (a_this)->ref_count > 0) {
+ PRIVATE (a_this)->ref_count--;
+ }
+
+ if (PRIVATE (a_this)->ref_count == 0) {
+ cr_tknzr_destroy (a_this);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+enum CRStatus
+cr_tknzr_set_input (CRTknzr * a_this, CRInput * a_input)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->input) {
+ cr_input_unref (PRIVATE (a_this)->input);
+ }
+
+ PRIVATE (a_this)->input = a_input;
+
+ cr_input_ref (PRIVATE (a_this)->input);
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_tknzr_get_input (CRTknzr * a_this, CRInput ** a_input)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ *a_input = PRIVATE (a_this)->input;
+
+ return CR_OK;
+}
+
+/*********************************
+ *Tokenizer input handling routines
+ *********************************/
+
+/**
+ *Reads the next byte from the parser input stream.
+ *@param a_this the "this pointer" of the current instance of
+ *#CRParser.
+ *@param a_byte out parameter the place where to store the byte
+ *read.
+ *@return CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_tknzr_read_byte (CRTknzr * a_this, guchar * a_byte)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ return cr_input_read_byte (PRIVATE (a_this)->input, a_byte);
+
+}
+
+/**
+ *Reads the next char from the parser input stream.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_char out parameter. The read char.
+ *@return CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_tknzr_read_char (CRTknzr * a_this, guint32 * a_char)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_char, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_read_char (PRIVATE (a_this)->input, a_char);
+}
+
+/**
+ *Peeks a char from the parser input stream.
+ *To "peek a char" means reads the next char without consuming it.
+ *Subsequent calls to this function return the same char.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_char out parameter. The peeked char upon successful completion.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_tknzr_peek_char (CRTknzr * a_this, guint32 * a_char)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_char, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_peek_char (PRIVATE (a_this)->input, a_char);
+}
+
+/**
+ *Peeks a byte ahead at a given position in the parser input stream.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_offset the offset of the peeked byte starting from the current
+ *byte in the parser input stream.
+ *@param a_byte out parameter. The peeked byte upon
+ *successful completion.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_tknzr_peek_byte (CRTknzr * a_this, gulong a_offset, guchar * a_byte)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input && a_byte,
+ CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_peek_byte (PRIVATE (a_this)->input,
+ CR_SEEK_CUR, a_offset, a_byte);
+}
+
+/**
+ *Same as cr_tknzr_peek_byte() but this api returns the byte peeked.
+ *@param a_this the current instance of #CRTknzr.
+ *@param a_offset the offset of the peeked byte starting from the current
+ *byte in the parser input stream.
+ *@param a_eof out parameter. If not NULL, is set to TRUE if we reached end of
+ *file, FALE otherwise. If the caller sets it to NULL, this parameter
+ *is just ignored.
+ *@return the peeked byte.
+ */
+guchar
+cr_tknzr_peek_byte2 (CRTknzr * a_this, gulong a_offset, gboolean * a_eof)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input, 0);
+
+ return cr_input_peek_byte2 (PRIVATE (a_this)->input, a_offset, a_eof);
+}
+
+/**
+ *Gets the number of bytes left in the topmost input stream
+ *associated to this parser.
+ *@param a_this the current instance of #CRTknzr
+ *@return the number of bytes left or -1 in case of error.
+ */
+glong
+cr_tknzr_get_nb_bytes_left (CRTknzr * a_this)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_get_nb_bytes_left (PRIVATE (a_this)->input);
+}
+
+enum CRStatus
+cr_tknzr_get_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_pos, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_get_cur_pos (PRIVATE (a_this)->input, a_pos);
+}
+
+enum CRStatus
+cr_tknzr_get_parsing_location (CRTknzr *a_this,
+ CRParsingLocation *a_loc)
+{
+ g_return_val_if_fail (a_this
+ && PRIVATE (a_this)
+ && a_loc,
+ CR_BAD_PARAM_ERROR) ;
+
+ return cr_input_get_parsing_location
+ (PRIVATE (a_this)->input, a_loc) ;
+}
+
+enum CRStatus
+cr_tknzr_get_cur_byte_addr (CRTknzr * a_this, guchar ** a_addr)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_get_cur_byte_addr (PRIVATE (a_this)->input, a_addr);
+}
+
+enum CRStatus
+cr_tknzr_seek_index (CRTknzr * a_this, enum CRSeekPos a_origin, gint a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_seek_index (PRIVATE (a_this)->input, a_origin, a_pos);
+}
+
+enum CRStatus
+cr_tknzr_consume_chars (CRTknzr * a_this, guint32 a_char, glong * a_nb_char)
+{
+ gulong consumed = *(gulong *) a_nb_char;
+ enum CRStatus status;
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_input_set_cur_pos (PRIVATE (a_this)->input,
+ &PRIVATE (a_this)->prev_pos);
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ status = cr_input_consume_chars (PRIVATE (a_this)->input,
+ a_char, &consumed);
+ *a_nb_char = (glong) consumed;
+ return status;
+}
+
+enum CRStatus
+cr_tknzr_set_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ return cr_input_set_cur_pos (PRIVATE (a_this)->input, a_pos);
+}
+
+enum CRStatus
+cr_tknzr_unget_token (CRTknzr * a_this, CRToken * a_token)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->token_cache == NULL,
+ CR_BAD_PARAM_ERROR);
+
+ PRIVATE (a_this)->token_cache = a_token;
+
+ return CR_OK;
+}
+
+/**
+ *Returns the next token of the input stream.
+ *This method is really central. Each parsing
+ *method calls it.
+ *@param a_this the current tokenizer.
+ *@param a_tk out parameter. The returned token.
+ *for the sake of mem leak avoidance, *a_tk must
+ *be NULL.
+ *@param CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_tknzr_get_next_token (CRTknzr * a_this, CRToken ** a_tk)
+{
+ enum CRStatus status = CR_OK;
+ CRToken *token = NULL;
+ CRInputPos init_pos;
+ guint32 next_char = 0;
+ guchar next_bytes[4] = { 0 };
+ gboolean reached_eof = FALSE;
+ CRInput *input = NULL;
+ CRString *str = NULL;
+ CRRgb *rgb = NULL;
+ CRParsingLocation location = {0} ;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_tk && *a_tk == NULL
+ && PRIVATE (a_this)->input,
+ CR_BAD_PARAM_ERROR);
+
+ if (PRIVATE (a_this)->token_cache) {
+ *a_tk = PRIVATE (a_this)->token_cache;
+ PRIVATE (a_this)->token_cache = NULL;
+ return CR_OK;
+ }
+
+ RECORD_INITIAL_POS (a_this, &init_pos);
+
+ status = cr_input_get_end_of_file
+ (PRIVATE (a_this)->input, &reached_eof);
+ ENSURE_PARSING_COND (status == CR_OK);
+
+ if (reached_eof == TRUE) {
+ status = CR_END_OF_INPUT_ERROR;
+ goto error;
+ }
+
+ input = PRIVATE (a_this)->input;
+
+ PEEK_NEXT_CHAR (a_this, &next_char);
+ token = cr_token_new ();
+ ENSURE_PARSING_COND (token);
+
+ switch (next_char) {
+ case '@':
+ {
+ if (BYTE (input, 2, NULL) == 'f'
+ && BYTE (input, 3, NULL) == 'o'
+ && BYTE (input, 4, NULL) == 'n'
+ && BYTE (input, 5, NULL) == 't'
+ && BYTE (input, 6, NULL) == '-'
+ && BYTE (input, 7, NULL) == 'f'
+ && BYTE (input, 8, NULL) == 'a'
+ && BYTE (input, 9, NULL) == 'c'
+ && BYTE (input, 10, NULL) == 'e') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location
+ (a_this, &location) ;
+ SKIP_CHARS (a_this, 9);
+ status = cr_token_set_font_face_sym (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+
+ if (BYTE (input, 2, NULL) == 'c'
+ && BYTE (input, 3, NULL) == 'h'
+ && BYTE (input, 4, NULL) == 'a'
+ && BYTE (input, 5, NULL) == 'r'
+ && BYTE (input, 6, NULL) == 's'
+ && BYTE (input, 7, NULL) == 'e'
+ && BYTE (input, 8, NULL) == 't') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location
+ (a_this, &location) ;
+ SKIP_CHARS (a_this, 7);
+ status = cr_token_set_charset_sym (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+
+ if (BYTE (input, 2, NULL) == 'i'
+ && BYTE (input, 3, NULL) == 'm'
+ && BYTE (input, 4, NULL) == 'p'
+ && BYTE (input, 5, NULL) == 'o'
+ && BYTE (input, 6, NULL) == 'r'
+ && BYTE (input, 7, NULL) == 't') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location
+ (a_this, &location) ;
+ SKIP_CHARS (a_this, 6);
+ status = cr_token_set_import_sym (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+
+ if (BYTE (input, 2, NULL) == 'm'
+ && BYTE (input, 3, NULL) == 'e'
+ && BYTE (input, 4, NULL) == 'd'
+ && BYTE (input, 5, NULL) == 'i'
+ && BYTE (input, 6, NULL) == 'a') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ SKIP_CHARS (a_this, 5);
+ status = cr_token_set_media_sym (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+
+ if (BYTE (input, 2, NULL) == 'p'
+ && BYTE (input, 3, NULL) == 'a'
+ && BYTE (input, 4, NULL) == 'g'
+ && BYTE (input, 5, NULL) == 'e') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ SKIP_CHARS (a_this, 4);
+ status = cr_token_set_page_sym (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+ status = cr_tknzr_parse_atkeyword (a_this, &str);
+ if (status == CR_OK) {
+ status = cr_token_set_atkeyword (token, str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ if (str) {
+ cr_parsing_location_copy (&token->location,
+ &str->location) ;
+ }
+ goto done;
+ }
+ }
+ break;
+
+ case 'u':
+
+ if (BYTE (input, 2, NULL) == 'r'
+ && BYTE (input, 3, NULL) == 'l'
+ && BYTE (input, 4, NULL) == '(') {
+ CRString *str2 = NULL;
+
+ status = cr_tknzr_parse_uri (a_this, &str2);
+ if (status == CR_OK) {
+ status = cr_token_set_uri (token, str2);
+ CHECK_PARSING_STATUS (status, TRUE);
+ if (str2) {
+ cr_parsing_location_copy (&token->location,
+ &str2->location) ;
+ }
+ goto done;
+ }
+ }
+ goto fallback;
+ break;
+
+ case 'r':
+ if (BYTE (input, 2, NULL) == 'g'
+ && BYTE (input, 3, NULL) == 'b'
+ && BYTE (input, 4, NULL) == '(') {
+ status = cr_tknzr_parse_rgb (a_this, &rgb);
+ if (status == CR_OK && rgb) {
+ status = cr_token_set_rgb (token, rgb);
+ CHECK_PARSING_STATUS (status, TRUE);
+ if (rgb) {
+ cr_parsing_location_copy (&token->location,
+ &rgb->location) ;
+ }
+ rgb = NULL;
+ goto done;
+ }
+
+ }
+ goto fallback;
+ break;
+
+ case '<':
+ if (BYTE (input, 2, NULL) == '!'
+ && BYTE (input, 3, NULL) == '-'
+ && BYTE (input, 4, NULL) == '-') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ SKIP_CHARS (a_this, 3);
+ status = cr_token_set_cdo (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+ break;
+
+ case '-':
+ if (BYTE (input, 2, NULL) == '-'
+ && BYTE (input, 3, NULL) == '>') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ SKIP_CHARS (a_this, 2);
+ status = cr_token_set_cdc (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ } else {
+ status = cr_tknzr_parse_ident
+ (a_this, &str);
+ if (status == CR_OK) {
+ cr_token_set_ident
+ (token, str);
+ if (str) {
+ cr_parsing_location_copy (&token->location,
+ &str->location) ;
+ }
+ goto done;
+ } else {
+ goto parse_number;
+ }
+ }
+ break;
+
+ case '~':
+ if (BYTE (input, 2, NULL) == '=') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ SKIP_CHARS (a_this, 1);
+ status = cr_token_set_includes (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+ break;
+
+ case '|':
+ if (BYTE (input, 2, NULL) == '=') {
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ SKIP_CHARS (a_this, 1);
+ status = cr_token_set_dashmatch (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+ break;
+
+ case '/':
+ if (BYTE (input, 2, NULL) == '*') {
+ status = cr_tknzr_parse_comment (a_this, &str);
+
+ if (status == CR_OK) {
+ status = cr_token_set_comment (token, str);
+ str = NULL;
+ CHECK_PARSING_STATUS (status, TRUE);
+ if (str) {
+ cr_parsing_location_copy (&token->location,
+ &str->location) ;
+ }
+ goto done;
+ }
+ }
+ break ;
+
+ case ';':
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_semicolon (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+
+ case '{':
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_cbo (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ goto done;
+
+ case '}':
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_cbc (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+
+ case '(':
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_po (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+
+ case ')':
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_pc (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+
+ case '[':
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_bo (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+
+ case ']':
+ SKIP_CHARS (a_this, 1);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_bc (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ {
+ guchar *start = NULL,
+ *end = NULL;
+
+ status = cr_tknzr_parse_w (a_this, &start,
+ &end, &location);
+ if (status == CR_OK) {
+ status = cr_token_set_s (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ goto done;
+ }
+ }
+ break;
+
+ case '#':
+ {
+ status = cr_tknzr_parse_hash (a_this, &str);
+ if (status == CR_OK && str) {
+ status = cr_token_set_hash (token, str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ if (str) {
+ cr_parsing_location_copy (&token->location,
+ &str->location) ;
+ }
+ str = NULL;
+ goto done;
+ }
+ }
+ break;
+
+ case '\'':
+ case '"':
+ status = cr_tknzr_parse_string (a_this, &str);
+ if (status == CR_OK && str) {
+ status = cr_token_set_string (token, str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ if (str) {
+ cr_parsing_location_copy (&token->location,
+ &str->location) ;
+ }
+ str = NULL;
+ goto done;
+ }
+ break;
+
+ case '!':
+ status = cr_tknzr_parse_important (a_this, &location);
+ if (status == CR_OK) {
+ status = cr_token_set_important_sym (token);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ goto done;
+ }
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '.':
+ case '+':
+ /* '-' case is handled separately above for --> comments */
+ parse_number:
+ {
+ CRNum *num = NULL;
+
+ status = cr_tknzr_parse_num (a_this, &num);
+ if (status == CR_OK && num) {
+ next_bytes[0] = BYTE (input, 1, NULL);
+ next_bytes[1] = BYTE (input, 2, NULL);
+ next_bytes[2] = BYTE (input, 3, NULL);
+ next_bytes[3] = BYTE (input, 4, NULL);
+
+ if (next_bytes[0] == 'e'
+ && next_bytes[1] == 'm') {
+ num->type = NUM_LENGTH_EM;
+ status = cr_token_set_ems (token,
+ num);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'e'
+ && next_bytes[1] == 'x') {
+ num->type = NUM_LENGTH_EX;
+ status = cr_token_set_exs (token,
+ num);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'p'
+ && next_bytes[1] == 'x') {
+ num->type = NUM_LENGTH_PX;
+ status = cr_token_set_length
+ (token, num, LENGTH_PX_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'c'
+ && next_bytes[1] == 'm') {
+ num->type = NUM_LENGTH_CM;
+ status = cr_token_set_length
+ (token, num, LENGTH_CM_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'm'
+ && next_bytes[1] == 'm') {
+ num->type = NUM_LENGTH_MM;
+ status = cr_token_set_length
+ (token, num, LENGTH_MM_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'i'
+ && next_bytes[1] == 'n') {
+ num->type = NUM_LENGTH_IN;
+ status = cr_token_set_length
+ (token, num, LENGTH_IN_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'p'
+ && next_bytes[1] == 't') {
+ num->type = NUM_LENGTH_PT;
+ status = cr_token_set_length
+ (token, num, LENGTH_PT_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'p'
+ && next_bytes[1] == 'c') {
+ num->type = NUM_LENGTH_PC;
+ status = cr_token_set_length
+ (token, num, LENGTH_PC_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'd'
+ && next_bytes[1] == 'e'
+ && next_bytes[2] == 'g') {
+ num->type = NUM_ANGLE_DEG;
+ status = cr_token_set_angle
+ (token, num, ANGLE_DEG_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 3);
+ } else if (next_bytes[0] == 'r'
+ && next_bytes[1] == 'a'
+ && next_bytes[2] == 'd') {
+ num->type = NUM_ANGLE_RAD;
+ status = cr_token_set_angle
+ (token, num, ANGLE_RAD_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 3);
+ } else if (next_bytes[0] == 'g'
+ && next_bytes[1] == 'r'
+ && next_bytes[2] == 'a'
+ && next_bytes[3] == 'd') {
+ num->type = NUM_ANGLE_GRAD;
+ status = cr_token_set_angle
+ (token, num, ANGLE_GRAD_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 4);
+ } else if (next_bytes[0] == 'm'
+ && next_bytes[1] == 's') {
+ num->type = NUM_TIME_MS;
+ status = cr_token_set_time
+ (token, num, TIME_MS_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 's') {
+ num->type = NUM_TIME_S;
+ status = cr_token_set_time
+ (token, num, TIME_S_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 1);
+ } else if (next_bytes[0] == 'H'
+ && next_bytes[1] == 'z') {
+ num->type = NUM_FREQ_HZ;
+ status = cr_token_set_freq
+ (token, num, FREQ_HZ_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 2);
+ } else if (next_bytes[0] == 'k'
+ && next_bytes[1] == 'H'
+ && next_bytes[2] == 'z') {
+ num->type = NUM_FREQ_KHZ;
+ status = cr_token_set_freq
+ (token, num, FREQ_KHZ_ET);
+ num = NULL;
+ SKIP_CHARS (a_this, 3);
+ } else if (next_bytes[0] == '%') {
+ num->type = NUM_PERCENTAGE;
+ status = cr_token_set_percentage
+ (token, num);
+ num = NULL;
+ SKIP_CHARS (a_this, 1);
+ } else {
+ status = cr_tknzr_parse_ident (a_this,
+ &str);
+ if (status == CR_OK && str) {
+ num->type = NUM_UNKNOWN_TYPE;
+ status = cr_token_set_dimen
+ (token, num, str);
+ num = NULL;
+ CHECK_PARSING_STATUS (status,
+ TRUE);
+ str = NULL;
+ } else {
+ status = cr_token_set_number
+ (token, num);
+ num = NULL;
+ CHECK_PARSING_STATUS (status, CR_OK);
+ str = NULL;
+ }
+ }
+ if (token && token->u.num) {
+ cr_parsing_location_copy (&token->location,
+ &token->u.num->location) ;
+ } else {
+ status = CR_ERROR ;
+ }
+ goto done ;
+ }
+ }
+ break;
+
+ default:
+ fallback:
+ /*process the fallback cases here */
+
+ if (next_char == '\\'
+ || (cr_utils_is_nonascii (next_bytes[0]) == TRUE)
+ || ((next_char >= 'a') && (next_char <= 'z'))
+ || ((next_char >= 'A') && (next_char <= 'Z'))) {
+ status = cr_tknzr_parse_ident (a_this, &str);
+ if (status == CR_OK && str) {
+ guint32 next_c = 0;
+
+ status = cr_input_peek_char
+ (PRIVATE (a_this)->input, &next_c);
+
+ if (status == CR_OK && next_c == '(') {
+
+ SKIP_CHARS (a_this, 1);
+ status = cr_token_set_function
+ (token, str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ /*ownership is transferred
+ *to token by cr_token_set_function.
+ */
+ if (str) {
+ cr_parsing_location_copy (&token->location,
+ &str->location) ;
+ }
+ str = NULL;
+ } else {
+ status = cr_token_set_ident (token,
+ str);
+ CHECK_PARSING_STATUS (status, TRUE);
+ if (str) {
+ cr_parsing_location_copy (&token->location,
+ &str->location) ;
+ }
+ str = NULL;
+ }
+ goto done;
+ } else {
+ if (str) {
+ cr_string_destroy (str);
+ str = NULL;
+ }
+ }
+ }
+ break;
+ }
+
+ READ_NEXT_CHAR (a_this, &next_char);
+ cr_tknzr_get_parsing_location (a_this,
+ &location) ;
+ status = cr_token_set_delim (token, next_char);
+ CHECK_PARSING_STATUS (status, TRUE);
+ cr_parsing_location_copy (&token->location,
+ &location) ;
+ done:
+
+ if (status == CR_OK && token) {
+ *a_tk = token;
+ /*
+ *store the previous position input stream pos.
+ */
+ memmove (&PRIVATE (a_this)->prev_pos,
+ &init_pos, sizeof (CRInputPos));
+ return CR_OK;
+ }
+
+ error:
+ if (token) {
+ cr_token_destroy (token);
+ token = NULL;
+ }
+
+ if (str) {
+ cr_string_destroy (str);
+ str = NULL;
+ }
+ cr_tknzr_set_cur_pos (a_this, &init_pos);
+ return status;
+
+}
+
+enum CRStatus
+cr_tknzr_parse_token (CRTknzr * a_this, enum CRTokenType a_type,
+ enum CRTokenExtraType a_et, gpointer a_res,
+ gpointer a_extra_res)
+{
+ enum CRStatus status = CR_OK;
+ CRToken *token = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && PRIVATE (a_this)->input
+ && a_res, CR_BAD_PARAM_ERROR);
+
+ status = cr_tknzr_get_next_token (a_this, &token);
+ if (status != CR_OK)
+ return status;
+ if (token == NULL)
+ return CR_PARSING_ERROR;
+
+ if (token->type == a_type) {
+ switch (a_type) {
+ case NO_TK:
+ case S_TK:
+ case CDO_TK:
+ case CDC_TK:
+ case INCLUDES_TK:
+ case DASHMATCH_TK:
+ case IMPORT_SYM_TK:
+ case PAGE_SYM_TK:
+ case MEDIA_SYM_TK:
+ case FONT_FACE_SYM_TK:
+ case CHARSET_SYM_TK:
+ case IMPORTANT_SYM_TK:
+ status = CR_OK;
+ break;
+
+ case STRING_TK:
+ case IDENT_TK:
+ case HASH_TK:
+ case ATKEYWORD_TK:
+ case FUNCTION_TK:
+ case COMMENT_TK:
+ case URI_TK:
+ *((CRString **) a_res) = token->u.str;
+ token->u.str = NULL;
+ status = CR_OK;
+ break;
+
+ case EMS_TK:
+ case EXS_TK:
+ case PERCENTAGE_TK:
+ case NUMBER_TK:
+ *((CRNum **) a_res) = token->u.num;
+ token->u.num = NULL;
+ status = CR_OK;
+ break;
+
+ case LENGTH_TK:
+ case ANGLE_TK:
+ case TIME_TK:
+ case FREQ_TK:
+ if (token->extra_type == a_et) {
+ *((CRNum **) a_res) = token->u.num;
+ token->u.num = NULL;
+ status = CR_OK;
+ }
+ break;
+
+ case DIMEN_TK:
+ *((CRNum **) a_res) = token->u.num;
+ if (a_extra_res == NULL) {
+ status = CR_BAD_PARAM_ERROR;
+ goto error;
+ }
+
+ *((CRString **) a_extra_res) = token->dimen;
+ token->u.num = NULL;
+ token->dimen = NULL;
+ status = CR_OK;
+ break;
+
+ case DELIM_TK:
+ *((guint32 *) a_res) = token->u.unichar;
+ status = CR_OK;
+ break;
+
+ case UNICODERANGE_TK:
+ default:
+ status = CR_PARSING_ERROR;
+ break;
+ }
+
+ cr_token_destroy (token);
+ token = NULL;
+ } else {
+ cr_tknzr_unget_token (a_this, token);
+ token = NULL;
+ status = CR_PARSING_ERROR;
+ }
+
+ return status;
+
+ error:
+
+ if (token) {
+ cr_tknzr_unget_token (a_this, token);
+ token = NULL;
+ }
+
+ return status;
+}
+
+void
+cr_tknzr_destroy (CRTknzr * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (PRIVATE (a_this) && PRIVATE (a_this)->input) {
+ if (cr_input_unref (PRIVATE (a_this)->input)
+ == TRUE) {
+ PRIVATE (a_this)->input = NULL;
+ }
+ }
+
+ if (PRIVATE (a_this)->token_cache) {
+ cr_token_destroy (PRIVATE (a_this)->token_cache);
+ PRIVATE (a_this)->token_cache = NULL;
+ }
+
+ if (PRIVATE (a_this)) {
+ g_free (PRIVATE (a_this));
+ PRIVATE (a_this) = NULL;
+ }
+
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-tknzr.h b/src/st/croco/cr-tknzr.h
new file mode 100644
index 0000000..13985b3
--- /dev/null
+++ b/src/st/croco/cr-tknzr.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for coypyright information.
+ */
+
+/**
+ *@file
+ *The declaration of the #CRTknzr (tokenizer)
+ *class.
+ */
+
+#ifndef __CR_TKNZR_H__
+#define __CR_TKNZR_H__
+
+#include "cr-utils.h"
+#include "cr-input.h"
+#include "cr-token.h"
+
+G_BEGIN_DECLS
+
+
+typedef struct _CRTknzr CRTknzr ;
+typedef struct _CRTknzrPriv CRTknzrPriv ;
+
+/**
+ *The tokenizer is the class that knows
+ *about all the css token. Its main job is
+ *to return the next token found in the character
+ *input stream.
+ */
+struct _CRTknzr
+{
+ /*the private data of the tokenizer.*/
+ CRTknzrPriv *priv ;
+} ;
+
+CRTknzr * cr_tknzr_new (CRInput *a_input) ;
+
+CRTknzr * cr_tknzr_new_from_uri (const guchar *a_file_uri,
+ enum CREncoding a_enc) ;
+
+CRTknzr * cr_tknzr_new_from_buf (guchar *a_buf, gulong a_len,
+ enum CREncoding a_enc,
+ gboolean a_free_at_destroy) ;
+
+gboolean cr_tknzr_unref (CRTknzr *a_this) ;
+
+void cr_tknzr_ref (CRTknzr *a_this) ;
+
+enum CRStatus cr_tknzr_read_byte (CRTknzr *a_this, guchar *a_byte) ;
+
+enum CRStatus cr_tknzr_read_char (CRTknzr *a_this, guint32 *a_char);
+
+enum CRStatus cr_tknzr_peek_char (CRTknzr *a_this, guint32 *a_char) ;
+
+enum CRStatus cr_tknzr_peek_byte (CRTknzr *a_this, gulong a_offset,
+ guchar *a_byte) ;
+
+guchar cr_tknzr_peek_byte2 (CRTknzr *a_this, gulong a_offset,
+ gboolean *a_eof) ;
+
+enum CRStatus cr_tknzr_set_cur_pos (CRTknzr *a_this, CRInputPos *a_pos) ;
+
+glong cr_tknzr_get_nb_bytes_left (CRTknzr *a_this) ;
+
+enum CRStatus cr_tknzr_get_cur_pos (CRTknzr *a_this, CRInputPos *a_pos) ;
+
+enum CRStatus cr_tknzr_get_parsing_location (CRTknzr *a_this,
+ CRParsingLocation *a_loc) ;
+
+enum CRStatus cr_tknzr_seek_index (CRTknzr *a_this,
+ enum CRSeekPos a_origin,
+ gint a_pos) ;
+
+enum CRStatus cr_tknzr_get_cur_byte_addr (CRTknzr *a_this, guchar **a_addr) ;
+
+
+enum CRStatus cr_tknzr_consume_chars (CRTknzr *a_this, guint32 a_char,
+ glong *a_nb_char) ;
+
+enum CRStatus cr_tknzr_get_next_token (CRTknzr *a_this, CRToken ** a_tk) ;
+
+enum CRStatus cr_tknzr_unget_token (CRTknzr *a_this, CRToken *a_token) ;
+
+
+enum CRStatus cr_tknzr_parse_token (CRTknzr *a_this, enum CRTokenType a_type,
+ enum CRTokenExtraType a_et, gpointer a_res,
+ gpointer a_extra_res) ;
+enum CRStatus cr_tknzr_set_input (CRTknzr *a_this, CRInput *a_input) ;
+
+enum CRStatus cr_tknzr_get_input (CRTknzr *a_this, CRInput **a_input) ;
+
+void cr_tknzr_destroy (CRTknzr *a_this) ;
+
+G_END_DECLS
+
+#endif /*__CR_TKZNR_H__*/
diff --git a/src/st/croco/cr-token.c b/src/st/croco/cr-token.c
new file mode 100644
index 0000000..91dd632
--- /dev/null
+++ b/src/st/croco/cr-token.c
@@ -0,0 +1,636 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * see COPYRIGHTS file for copyright information.
+ */
+
+/**
+ *@file
+ *The definition of the #CRToken class.
+ *Abstracts a css2 token.
+ */
+#include <string.h>
+#include "cr-token.h"
+
+/*
+ *TODO: write a CRToken::to_string() method.
+ */
+
+/**
+ *Frees the attributes of the current instance
+ *of #CRtoken.
+ *@param a_this the current instance of #CRToken.
+ */
+static void
+cr_token_clear (CRToken * a_this)
+{
+ g_return_if_fail (a_this);
+
+ switch (a_this->type) {
+ case S_TK:
+ case CDO_TK:
+ case CDC_TK:
+ case INCLUDES_TK:
+ case DASHMATCH_TK:
+ case PAGE_SYM_TK:
+ case MEDIA_SYM_TK:
+ case FONT_FACE_SYM_TK:
+ case CHARSET_SYM_TK:
+ case IMPORT_SYM_TK:
+ case IMPORTANT_SYM_TK:
+ case SEMICOLON_TK:
+ case NO_TK:
+ case DELIM_TK:
+ case CBO_TK:
+ case CBC_TK:
+ case BO_TK:
+ case BC_TK:
+ break;
+
+ case STRING_TK:
+ case IDENT_TK:
+ case HASH_TK:
+ case URI_TK:
+ case FUNCTION_TK:
+ case COMMENT_TK:
+ case ATKEYWORD_TK:
+ if (a_this->u.str) {
+ cr_string_destroy (a_this->u.str);
+ a_this->u.str = NULL;
+ }
+ break;
+
+ case EMS_TK:
+ case EXS_TK:
+ case LENGTH_TK:
+ case ANGLE_TK:
+ case TIME_TK:
+ case FREQ_TK:
+ case PERCENTAGE_TK:
+ case NUMBER_TK:
+ case PO_TK:
+ case PC_TK:
+ if (a_this->u.num) {
+ cr_num_destroy (a_this->u.num);
+ a_this->u.num = NULL;
+ }
+ break;
+
+ case DIMEN_TK:
+ if (a_this->u.num) {
+ cr_num_destroy (a_this->u.num);
+ a_this->u.num = NULL;
+ }
+
+ if (a_this->dimen) {
+ cr_string_destroy (a_this->dimen);
+ a_this->dimen = NULL;
+ }
+
+ break;
+
+ case RGB_TK:
+ if (a_this->u.rgb) {
+ cr_rgb_destroy (a_this->u.rgb) ;
+ a_this->u.rgb = NULL ;
+ }
+ break ;
+
+ case UNICODERANGE_TK:
+ /*not supported yet. */
+ break;
+
+ default:
+ cr_utils_trace_info ("I don't know how to clear this token\n") ;
+ break;
+ }
+
+ a_this->type = NO_TK;
+}
+
+/**
+ *Default constructor of
+ *the #CRToken class.
+ *@return the newly built instance of #CRToken.
+ */
+CRToken *
+cr_token_new (void)
+{
+ CRToken *result = NULL;
+
+ result = g_try_malloc (sizeof (CRToken));
+
+ if (result == NULL) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+
+ memset (result, 0, sizeof (CRToken));
+
+ return result;
+}
+
+/**
+ *Sets the type of curren instance of
+ *#CRToken to 'S_TK' (S in the css2 spec)
+ *@param a_this the current instance of #CRToken.
+ *@return CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_token_set_s (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = S_TK;
+
+ return CR_OK;
+}
+
+/**
+ *Sets the type of the current instance of
+ *#CRToken to 'CDO_TK' (CDO as said by the css2 spec)
+ *@param a_this the current instance of #CRToken.
+ *@return CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_token_set_cdo (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = CDO_TK;
+
+ return CR_OK;
+}
+
+/**
+ *Sets the type of the current token to
+ *CDC_TK (CDC as said by the css2 spec).
+ *@param a_this the current instance of #CRToken.
+ *@return CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_token_set_cdc (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = CDC_TK;
+
+ return CR_OK;
+}
+
+/**
+ *Sets the type of the current instance of
+ *#CRToken to INCLUDES_TK (INCLUDES as said by the css2 spec).
+ *@param a_this the current instance of #CRToken.
+ *@return CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_token_set_includes (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = INCLUDES_TK;
+
+ return CR_OK;
+}
+
+/**
+ *Sets the type of the current instance of
+ *#CRToken to DASHMATCH_TK (DASHMATCH as said by the css2 spec).
+ *@param a_this the current instance of #CRToken.
+ *@return CR_OK upon successful completion, an error
+ *code otherwise.
+ */
+enum CRStatus
+cr_token_set_dashmatch (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = DASHMATCH_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_comment (CRToken * a_this, CRString * a_str)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = COMMENT_TK;
+ a_this->u.str = a_str ;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_string (CRToken * a_this, CRString * a_str)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = STRING_TK;
+
+ a_this->u.str = a_str ;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_ident (CRToken * a_this, CRString * a_ident)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = IDENT_TK;
+ a_this->u.str = a_ident;
+ return CR_OK;
+}
+
+
+enum CRStatus
+cr_token_set_function (CRToken * a_this, CRString * a_fun_name)
+{
+ g_return_val_if_fail (a_this,
+ CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = FUNCTION_TK;
+ a_this->u.str = a_fun_name;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_hash (CRToken * a_this, CRString * a_hash)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = HASH_TK;
+ a_this->u.str = a_hash;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_rgb (CRToken * a_this, CRRgb * a_rgb)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = RGB_TK;
+ a_this->u.rgb = a_rgb;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_import_sym (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = IMPORT_SYM_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_page_sym (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = PAGE_SYM_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_media_sym (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = MEDIA_SYM_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_font_face_sym (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = FONT_FACE_SYM_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_charset_sym (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = CHARSET_SYM_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_atkeyword (CRToken * a_this, CRString * a_atname)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+ a_this->type = ATKEYWORD_TK;
+ a_this->u.str = a_atname;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_important_sym (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+ cr_token_clear (a_this);
+ a_this->type = IMPORTANT_SYM_TK;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_ems (CRToken * a_this, CRNum * a_num)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+ cr_token_clear (a_this);
+ a_this->type = EMS_TK;
+ a_this->u.num = a_num;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_exs (CRToken * a_this, CRNum * a_num)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+ cr_token_clear (a_this);
+ a_this->type = EXS_TK;
+ a_this->u.num = a_num;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_length (CRToken * a_this, CRNum * a_num,
+ enum CRTokenExtraType a_et)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = LENGTH_TK;
+ a_this->extra_type = a_et;
+ a_this->u.num = a_num;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_angle (CRToken * a_this, CRNum * a_num,
+ enum CRTokenExtraType a_et)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = ANGLE_TK;
+ a_this->extra_type = a_et;
+ a_this->u.num = a_num;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_time (CRToken * a_this, CRNum * a_num,
+ enum CRTokenExtraType a_et)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = TIME_TK;
+ a_this->extra_type = a_et;
+ a_this->u.num = a_num;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_freq (CRToken * a_this, CRNum * a_num,
+ enum CRTokenExtraType a_et)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = FREQ_TK;
+ a_this->extra_type = a_et;
+ a_this->u.num = a_num;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_dimen (CRToken * a_this, CRNum * a_num,
+ CRString * a_dim)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+ cr_token_clear (a_this);
+ a_this->type = DIMEN_TK;
+ a_this->u.num = a_num;
+ a_this->dimen = a_dim;
+ return CR_OK;
+
+}
+
+enum CRStatus
+cr_token_set_percentage (CRToken * a_this, CRNum * a_num)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = PERCENTAGE_TK;
+ a_this->u.num = a_num;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_number (CRToken * a_this, CRNum * a_num)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = NUMBER_TK;
+ a_this->u.num = a_num;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_uri (CRToken * a_this, CRString * a_uri)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = URI_TK;
+ a_this->u.str = a_uri;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_delim (CRToken * a_this, guint32 a_char)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = DELIM_TK;
+ a_this->u.unichar = a_char;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_semicolon (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = SEMICOLON_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_cbo (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = CBO_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_cbc (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = CBC_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_po (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = PO_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_pc (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = PC_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_bo (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = BO_TK;
+
+ return CR_OK;
+}
+
+enum CRStatus
+cr_token_set_bc (CRToken * a_this)
+{
+ g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
+
+ cr_token_clear (a_this);
+
+ a_this->type = BC_TK;
+
+ return CR_OK;
+}
+
+/**
+ *The destructor of the #CRToken class.
+ *@param a_this the current instance of #CRToken.
+ */
+void
+cr_token_destroy (CRToken * a_this)
+{
+ g_return_if_fail (a_this);
+
+ cr_token_clear (a_this);
+
+ g_free (a_this);
+}
diff --git a/src/st/croco/cr-token.h b/src/st/croco/cr-token.h
new file mode 100644
index 0000000..f1257b7
--- /dev/null
+++ b/src/st/croco/cr-token.h
@@ -0,0 +1,212 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#ifndef __CR_TOKEN_H__
+#define __CR_TOKEN_H__
+
+#include "cr-utils.h"
+#include "cr-input.h"
+#include "cr-num.h"
+#include "cr-rgb.h"
+#include "cr-string.h"
+#include "cr-parsing-location.h"
+
+G_BEGIN_DECLS
+
+enum CRTokenType
+{
+ NO_TK,
+ S_TK,
+ CDO_TK,
+ CDC_TK,
+ INCLUDES_TK,
+ DASHMATCH_TK,
+ COMMENT_TK,
+ STRING_TK,
+ IDENT_TK,
+ HASH_TK,
+ IMPORT_SYM_TK,
+ PAGE_SYM_TK,
+ MEDIA_SYM_TK,
+ FONT_FACE_SYM_TK,
+ CHARSET_SYM_TK,
+ ATKEYWORD_TK,
+ IMPORTANT_SYM_TK,
+ EMS_TK,
+ EXS_TK,
+ LENGTH_TK,
+ ANGLE_TK,
+ TIME_TK,
+ FREQ_TK,
+ DIMEN_TK,
+ PERCENTAGE_TK,
+ NUMBER_TK,
+ RGB_TK,
+ URI_TK,
+ FUNCTION_TK,
+ UNICODERANGE_TK,
+ SEMICOLON_TK,
+ CBO_TK, /*opening curly bracket*/
+ CBC_TK, /*closing curly bracket*/
+ PO_TK, /*opening parenthesis*/
+ PC_TK, /*closing parenthesis*/
+ BO_TK, /*opening bracket*/
+ BC_TK, /*closing bracket*/
+ DELIM_TK
+} ;
+
+enum CRTokenExtraType
+{
+ NO_ET = 0,
+ LENGTH_PX_ET,
+ LENGTH_CM_ET,
+ LENGTH_MM_ET,
+ LENGTH_IN_ET,
+ LENGTH_PT_ET,
+ LENGTH_PC_ET,
+ ANGLE_DEG_ET,
+ ANGLE_RAD_ET,
+ ANGLE_GRAD_ET,
+ TIME_MS_ET,
+ TIME_S_ET,
+ FREQ_HZ_ET,
+ FREQ_KHZ_ET
+} ;
+
+typedef struct _CRToken CRToken ;
+
+/**
+ *This class abstracts a css2 token.
+ */
+struct _CRToken
+{
+ enum CRTokenType type ;
+ enum CRTokenExtraType extra_type ;
+ CRInputPos pos ;
+
+ union
+ {
+ CRString *str ;
+ CRRgb *rgb ;
+ CRNum *num ;
+ guint32 unichar ;
+ } u ;
+
+ CRString * dimen ;
+ CRParsingLocation location ;
+} ;
+
+CRToken* cr_token_new (void) ;
+
+enum CRStatus cr_token_set_s (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_cdo (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_cdc (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_includes (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_dashmatch (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_comment (CRToken *a_this, CRString *a_str) ;
+
+enum CRStatus cr_token_set_string (CRToken *a_this, CRString *a_str) ;
+
+enum CRStatus cr_token_set_ident (CRToken *a_this, CRString * a_ident) ;
+
+enum CRStatus cr_token_set_hash (CRToken *a_this, CRString *a_hash) ;
+
+enum CRStatus cr_token_set_rgb (CRToken *a_this, CRRgb *a_rgb) ;
+
+enum CRStatus cr_token_set_import_sym (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_page_sym (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_media_sym (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_font_face_sym (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_charset_sym (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_atkeyword (CRToken *a_this, CRString *a_atname) ;
+
+enum CRStatus cr_token_set_important_sym (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_ems (CRToken *a_this, CRNum *a_num) ;
+
+enum CRStatus cr_token_set_exs (CRToken *a_this, CRNum *a_num) ;
+
+enum CRStatus cr_token_set_length (CRToken *a_this, CRNum *a_num,
+ enum CRTokenExtraType a_et) ;
+
+enum CRStatus cr_token_set_angle (CRToken *a_this, CRNum *a_num,
+ enum CRTokenExtraType a_et) ;
+
+enum CRStatus cr_token_set_time (CRToken *a_this, CRNum *a_num,
+ enum CRTokenExtraType a_et) ;
+
+enum CRStatus cr_token_set_freq (CRToken *a_this, CRNum *a_num,
+ enum CRTokenExtraType a_et) ;
+
+enum CRStatus cr_token_set_dimen (CRToken *a_this, CRNum *a_num,
+ CRString *a_dim) ;
+
+enum CRStatus cr_token_set_percentage (CRToken *a_this, CRNum *a_num) ;
+
+enum CRStatus cr_token_set_number (CRToken *a_this, CRNum *a_num) ;
+
+enum CRStatus cr_token_set_uri (CRToken *a_this, CRString *a_uri) ;
+
+enum CRStatus cr_token_set_function (CRToken *a_this,
+ CRString *a_fun_name) ;
+
+enum CRStatus cr_token_set_bc (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_bo (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_po (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_pc (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_cbc (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_cbo (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_semicolon (CRToken *a_this) ;
+
+enum CRStatus cr_token_set_delim (CRToken *a_this, guint32 a_char) ;
+
+
+/*
+ enum CRStatus
+ cr_token_set_unicoderange (CRToken *a_this,
+ CRUnicodeRange *a_range) ;
+*/
+
+void
+cr_token_destroy (CRToken *a_this) ;
+
+
+G_END_DECLS
+
+#endif /*__CR_TOKEN_H__*/
diff --git a/src/st/croco/cr-utils.c b/src/st/croco/cr-utils.c
new file mode 100644
index 0000000..5fafade
--- /dev/null
+++ b/src/st/croco/cr-utils.c
@@ -0,0 +1,1330 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * See COPYRIGHTS file for copyright information.
+ */
+
+#include "cr-utils.h"
+#include "cr-string.h"
+
+/**
+ *@file:
+ *Some misc utility functions used
+ *in the libcroco.
+ *Note that troughout this file I will
+ *refer to the CSS SPECIFICATIONS DOCUMENTATION
+ *written by the w3c guys. You can find that document
+ *at http://www.w3.org/TR/REC-CSS2/ .
+ */
+
+/****************************
+ *Encoding transformations and
+ *encoding helpers
+ ****************************/
+
+/*
+ *Here is the correspondence between the ucs-4 charactere codes
+ *and there matching utf-8 encoding pattern as described by RFC 2279:
+ *
+ *UCS-4 range (hex.) UTF-8 octet sequence (binary)
+ *------------------ -----------------------------
+ *0000 0000-0000 007F 0xxxxxxx
+ *0000 0080-0000 07FF 110xxxxx 10xxxxxx
+ *0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
+ *0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ *0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ *0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx
+ */
+
+/**
+ *Given an utf8 string buffer, calculates
+ *the length of this string if it was encoded
+ *in ucs4.
+ *@param a_in_start a pointer to the beginning of
+ *the input utf8 string.
+ *@param a_in_end a pointre to the end of the input
+ *utf8 string (points to the last byte of the buffer)
+ *@param a_len out parameter the calculated length.
+ *@return CR_OK upon successful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_utils_utf8_str_len_as_ucs4 (const guchar * a_in_start,
+ const guchar * a_in_end, gulong * a_len)
+{
+ guchar *byte_ptr = NULL;
+ gint len = 0;
+
+ /*
+ *to store the final decoded
+ *unicode char
+ */
+ guint c = 0;
+
+ g_return_val_if_fail (a_in_start && a_in_end && a_len,
+ CR_BAD_PARAM_ERROR);
+ *a_len = 0;
+
+ for (byte_ptr = (guchar *) a_in_start;
+ byte_ptr <= a_in_end; byte_ptr++) {
+ gint nb_bytes_2_decode = 0;
+
+ if (*byte_ptr <= 0x7F) {
+ /*
+ *7 bits long char
+ *encoded over 1 byte:
+ * 0xxx xxxx
+ */
+ c = *byte_ptr;
+ nb_bytes_2_decode = 1;
+
+ } else if ((*byte_ptr & 0xE0) == 0xC0) {
+ /*
+ *up to 11 bits long char.
+ *encoded over 2 bytes:
+ *110x xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 0x1F;
+ nb_bytes_2_decode = 2;
+
+ } else if ((*byte_ptr & 0xF0) == 0xE0) {
+ /*
+ *up to 16 bit long char
+ *encoded over 3 bytes:
+ *1110 xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 0x0F;
+ nb_bytes_2_decode = 3;
+
+ } else if ((*byte_ptr & 0xF8) == 0xF0) {
+ /*
+ *up to 21 bits long char
+ *encoded over 4 bytes:
+ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 0x7;
+ nb_bytes_2_decode = 4;
+
+ } else if ((*byte_ptr & 0xFC) == 0xF8) {
+ /*
+ *up to 26 bits long char
+ *encoded over 5 bytes.
+ *1111 10xx 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 3;
+ nb_bytes_2_decode = 5;
+
+ } else if ((*byte_ptr & 0xFE) == 0xFC) {
+ /*
+ *up to 31 bits long char
+ *encoded over 6 bytes:
+ *1111 110x 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 1;
+ nb_bytes_2_decode = 6;
+
+ } else {
+ /*
+ *BAD ENCODING
+ */
+ return CR_ENCODING_ERROR;
+ }
+
+ /*
+ *Go and decode the remaining byte(s)
+ *(if any) to get the current character.
+ */
+ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) {
+ /*decode the next byte */
+ byte_ptr++;
+
+ /*byte pattern must be: 10xx xxxx */
+ if ((*byte_ptr & 0xC0) != 0x80) {
+ return CR_ENCODING_ERROR;
+ }
+
+ c = (c << 6) | (*byte_ptr & 0x3F);
+ }
+
+ len++;
+ }
+
+ *a_len = len;
+
+ return CR_OK;
+}
+
+/**
+ *Given an ucs4 string, this function
+ *returns the size (in bytes) this string
+ *would have occupied if it was encoded in utf-8.
+ *@param a_in_start a pointer to the beginning of the input
+ *buffer.
+ *@param a_in_end a pointer to the end of the input buffer.
+ *@param a_len out parameter. The computed length.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_utils_ucs4_str_len_as_utf8 (const guint32 * a_in_start,
+ const guint32 * a_in_end, gulong * a_len)
+{
+ gint len = 0;
+ guint32 *char_ptr = NULL;
+
+ g_return_val_if_fail (a_in_start && a_in_end && a_len,
+ CR_BAD_PARAM_ERROR);
+
+ for (char_ptr = (guint32 *) a_in_start;
+ char_ptr <= a_in_end; char_ptr++) {
+ if (*char_ptr <= 0x7F) {
+ /*the utf-8 char would take 1 byte */
+ len += 1;
+ } else if (*char_ptr <= 0x7FF) {
+ /*the utf-8 char would take 2 bytes */
+ len += 2;
+ } else if (*char_ptr <= 0xFFFF) {
+ len += 3;
+ } else if (*char_ptr <= 0x1FFFFF) {
+ len += 4;
+ } else if (*char_ptr <= 0x3FFFFFF) {
+ len += 5;
+ } else if (*char_ptr <= 0x7FFFFFFF) {
+ len += 6;
+ }
+ }
+
+ *a_len = len;
+ return CR_OK;
+}
+
+/**
+ *Given an ucsA string, this function
+ *returns the size (in bytes) this string
+ *would have occupied if it was encoded in utf-8.
+ *@param a_in_start a pointer to the beginning of the input
+ *buffer.
+ *@param a_in_end a pointer to the end of the input buffer.
+ *@param a_len out parameter. The computed length.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_utils_ucs1_str_len_as_utf8 (const guchar * a_in_start,
+ const guchar * a_in_end, gulong * a_len)
+{
+ gint len = 0;
+ guchar *char_ptr = NULL;
+
+ g_return_val_if_fail (a_in_start && a_in_end && a_len,
+ CR_BAD_PARAM_ERROR);
+
+ for (char_ptr = (guchar *) a_in_start;
+ char_ptr <= a_in_end; char_ptr++) {
+ if (*char_ptr <= 0x7F) {
+ /*the utf-8 char would take 1 byte */
+ len += 1;
+ } else {
+ /*the utf-8 char would take 2 bytes */
+ len += 2;
+ }
+ }
+
+ *a_len = len;
+ return CR_OK;
+}
+
+/**
+ *Converts an utf8 buffer into an ucs4 buffer.
+ *
+ *@param a_in the input utf8 buffer to convert.
+ *@param a_in_len in/out parameter. The size of the
+ *input buffer to convert. After return, this parameter contains
+ *the actual number of bytes consumed.
+ *@param a_out the output converted ucs4 buffer. Must be allocated by
+ *the caller.
+ *@param a_out_len in/out parameter. The size of the output buffer.
+ *If this size is actually smaller than the real needed size, the function
+ *just converts what it can and returns a success status. After return,
+ *this param points to the actual number of characters decoded.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_utils_utf8_to_ucs4 (const guchar * a_in,
+ gulong * a_in_len, guint32 * a_out, gulong * a_out_len)
+{
+ gulong in_len = 0,
+ out_len = 0,
+ in_index = 0,
+ out_index = 0;
+ enum CRStatus status = CR_OK;
+
+ /*
+ *to store the final decoded
+ *unicode char
+ */
+ guint c = 0;
+
+ g_return_val_if_fail (a_in && a_in_len
+ && a_out && a_out_len, CR_BAD_PARAM_ERROR);
+
+ if (*a_in_len < 1) {
+ status = CR_OK;
+ goto end;
+ }
+
+ in_len = *a_in_len;
+ out_len = *a_out_len;
+
+ for (in_index = 0, out_index = 0;
+ (in_index < in_len) && (out_index < out_len);
+ in_index++, out_index++) {
+ gint nb_bytes_2_decode = 0;
+
+ if (a_in[in_index] <= 0x7F) {
+ /*
+ *7 bits long char
+ *encoded over 1 byte:
+ * 0xxx xxxx
+ */
+ c = a_in[in_index];
+ nb_bytes_2_decode = 1;
+
+ } else if ((a_in[in_index] & 0xE0) == 0xC0) {
+ /*
+ *up to 11 bits long char.
+ *encoded over 2 bytes:
+ *110x xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 0x1F;
+ nb_bytes_2_decode = 2;
+
+ } else if ((a_in[in_index] & 0xF0) == 0xE0) {
+ /*
+ *up to 16 bit long char
+ *encoded over 3 bytes:
+ *1110 xxxx 10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 0x0F;
+ nb_bytes_2_decode = 3;
+
+ } else if ((a_in[in_index] & 0xF8) == 0xF0) {
+ /*
+ *up to 21 bits long char
+ *encoded over 4 bytes:
+ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 0x7;
+ nb_bytes_2_decode = 4;
+
+ } else if ((a_in[in_index] & 0xFC) == 0xF8) {
+ /*
+ *up to 26 bits long char
+ *encoded over 5 bytes.
+ *1111 10xx 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 3;
+ nb_bytes_2_decode = 5;
+
+ } else if ((a_in[in_index] & 0xFE) == 0xFC) {
+ /*
+ *up to 31 bits long char
+ *encoded over 6 bytes:
+ *1111 110x 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 1;
+ nb_bytes_2_decode = 6;
+
+ } else {
+ /*BAD ENCODING */
+ goto end;
+ }
+
+ /*
+ *Go and decode the remaining byte(s)
+ *(if any) to get the current character.
+ */
+ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) {
+ /*decode the next byte */
+ in_index++;
+
+ /*byte pattern must be: 10xx xxxx */
+ if ((a_in[in_index] & 0xC0) != 0x80) {
+ goto end;
+ }
+
+ c = (c << 6) | (a_in[in_index] & 0x3F);
+ }
+
+ /*
+ *The decoded ucs4 char is now
+ *in c.
+ */
+
+ /************************
+ *Some security tests
+ ***********************/
+
+ /*be sure c is a char */
+ if (c == 0xFFFF || c == 0xFFFE)
+ goto end;
+
+ /*be sure c is inferior to the max ucs4 char value */
+ if (c > 0x10FFFF)
+ goto end;
+
+ /*
+ *c must be less than UTF16 "lower surrogate begin"
+ *or higher than UTF16 "High surrogate end"
+ */
+ if (c >= 0xD800 && c <= 0xDFFF)
+ goto end;
+
+ /*Avoid characters that equals zero */
+ if (c == 0)
+ goto end;
+
+ a_out[out_index] = c;
+ }
+
+ end:
+ *a_out_len = out_index + 1;
+ *a_in_len = in_index + 1;
+
+ return status;
+}
+
+/**
+ *Reads a character from an utf8 buffer.
+ *Actually decode the next character code (unicode character code)
+ *and returns it.
+ *@param a_in the starting address of the utf8 buffer.
+ *@param a_in_len the length of the utf8 buffer.
+ *@param a_out output parameter. The resulting read char.
+ *@param a_consumed the number of the bytes consumed to
+ *decode the returned character code.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_utils_read_char_from_utf8_buf (const guchar * a_in,
+ gulong a_in_len,
+ guint32 * a_out, gulong * a_consumed)
+{
+ gulong in_index = 0,
+ nb_bytes_2_decode = 0;
+ enum CRStatus status = CR_OK;
+
+ /*
+ *to store the final decoded
+ *unicode char
+ */
+ guint32 c = 0;
+
+ g_return_val_if_fail (a_in && a_out && a_out
+ && a_consumed, CR_BAD_PARAM_ERROR);
+
+ if (a_in_len < 1) {
+ status = CR_OK;
+ goto end;
+ }
+
+ if (*a_in <= 0x7F) {
+ /*
+ *7 bits long char
+ *encoded over 1 byte:
+ * 0xxx xxxx
+ */
+ c = *a_in;
+ nb_bytes_2_decode = 1;
+
+ } else if ((*a_in & 0xE0) == 0xC0) {
+ /*
+ *up to 11 bits long char.
+ *encoded over 2 bytes:
+ *110x xxxx 10xx xxxx
+ */
+ c = *a_in & 0x1F;
+ nb_bytes_2_decode = 2;
+
+ } else if ((*a_in & 0xF0) == 0xE0) {
+ /*
+ *up to 16 bit long char
+ *encoded over 3 bytes:
+ *1110 xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *a_in & 0x0F;
+ nb_bytes_2_decode = 3;
+
+ } else if ((*a_in & 0xF8) == 0xF0) {
+ /*
+ *up to 21 bits long char
+ *encoded over 4 bytes:
+ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *a_in & 0x7;
+ nb_bytes_2_decode = 4;
+
+ } else if ((*a_in & 0xFC) == 0xF8) {
+ /*
+ *up to 26 bits long char
+ *encoded over 5 bytes.
+ *1111 10xx 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx
+ */
+ c = *a_in & 3;
+ nb_bytes_2_decode = 5;
+
+ } else if ((*a_in & 0xFE) == 0xFC) {
+ /*
+ *up to 31 bits long char
+ *encoded over 6 bytes:
+ *1111 110x 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *a_in & 1;
+ nb_bytes_2_decode = 6;
+
+ } else {
+ /*BAD ENCODING */
+ goto end;
+ }
+
+ if (nb_bytes_2_decode > a_in_len) {
+ status = CR_END_OF_INPUT_ERROR;
+ goto end;
+ }
+
+ /*
+ *Go and decode the remaining byte(s)
+ *(if any) to get the current character.
+ */
+ for (in_index = 1; in_index < nb_bytes_2_decode; in_index++) {
+ /*byte pattern must be: 10xx xxxx */
+ if ((a_in[in_index] & 0xC0) != 0x80) {
+ goto end;
+ }
+
+ c = (c << 6) | (a_in[in_index] & 0x3F);
+ }
+
+ /*
+ *The decoded ucs4 char is now
+ *in c.
+ */
+
+ /************************
+ *Some security tests
+ ***********************/
+
+ /*be sure c is a char */
+ if (c == 0xFFFF || c == 0xFFFE)
+ goto end;
+
+ /*be sure c is inferior to the max ucs4 char value */
+ if (c > 0x10FFFF)
+ goto end;
+
+ /*
+ *c must be less than UTF16 "lower surrogate begin"
+ *or higher than UTF16 "High surrogate end"
+ */
+ if (c >= 0xD800 && c <= 0xDFFF)
+ goto end;
+
+ /*Avoid characters that equals zero */
+ if (c == 0)
+ goto end;
+
+ *a_out = c;
+
+ end:
+ *a_consumed = nb_bytes_2_decode;
+
+ return status;
+}
+
+/**
+ *
+ */
+enum CRStatus
+cr_utils_utf8_str_len_as_ucs1 (const guchar * a_in_start,
+ const guchar * a_in_end, gulong * a_len)
+{
+ /*
+ *Note: this function can be made shorter
+ *but it considers all the cases of the utf8 encoding
+ *to ease further extensions ...
+ */
+
+ guchar *byte_ptr = NULL;
+ gint len = 0;
+
+ /*
+ *to store the final decoded
+ *unicode char
+ */
+ guint c = 0;
+
+ g_return_val_if_fail (a_in_start && a_in_end && a_len,
+ CR_BAD_PARAM_ERROR);
+ *a_len = 0;
+
+ for (byte_ptr = (guchar *) a_in_start;
+ byte_ptr <= a_in_end; byte_ptr++) {
+ gint nb_bytes_2_decode = 0;
+
+ if (*byte_ptr <= 0x7F) {
+ /*
+ *7 bits long char
+ *encoded over 1 byte:
+ * 0xxx xxxx
+ */
+ c = *byte_ptr;
+ nb_bytes_2_decode = 1;
+
+ } else if ((*byte_ptr & 0xE0) == 0xC0) {
+ /*
+ *up to 11 bits long char.
+ *encoded over 2 bytes:
+ *110x xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 0x1F;
+ nb_bytes_2_decode = 2;
+
+ } else if ((*byte_ptr & 0xF0) == 0xE0) {
+ /*
+ *up to 16 bit long char
+ *encoded over 3 bytes:
+ *1110 xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 0x0F;
+ nb_bytes_2_decode = 3;
+
+ } else if ((*byte_ptr & 0xF8) == 0xF0) {
+ /*
+ *up to 21 bits long char
+ *encoded over 4 bytes:
+ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 0x7;
+ nb_bytes_2_decode = 4;
+
+ } else if ((*byte_ptr & 0xFC) == 0xF8) {
+ /*
+ *up to 26 bits long char
+ *encoded over 5 bytes.
+ *1111 10xx 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 3;
+ nb_bytes_2_decode = 5;
+
+ } else if ((*byte_ptr & 0xFE) == 0xFC) {
+ /*
+ *up to 31 bits long char
+ *encoded over 6 bytes:
+ *1111 110x 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = *byte_ptr & 1;
+ nb_bytes_2_decode = 6;
+
+ } else {
+ /*
+ *BAD ENCODING
+ */
+ return CR_ENCODING_ERROR;
+ }
+
+ /*
+ *Go and decode the remaining byte(s)
+ *(if any) to get the current character.
+ */
+ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) {
+ /*decode the next byte */
+ byte_ptr++;
+
+ /*byte pattern must be: 10xx xxxx */
+ if ((*byte_ptr & 0xC0) != 0x80) {
+ return CR_ENCODING_ERROR;
+ }
+
+ c = (c << 6) | (*byte_ptr & 0x3F);
+ }
+
+ /*
+ *The decoded ucs4 char is now
+ *in c.
+ */
+
+ if (c <= 0xFF) { /*Add other conditions to support
+ *other char sets (ucs2, ucs3, ucs4).
+ */
+ len++;
+ } else {
+ /*the char is too long to fit
+ *into the supposed charset len.
+ */
+ return CR_ENCODING_ERROR;
+ }
+ }
+
+ *a_len = len;
+
+ return CR_OK;
+}
+
+/**
+ *Converts an utf8 string into an ucs4 string.
+ *@param a_in the input string to convert.
+ *@param a_in_len in/out parameter. The length of the input
+ *string. After return, points to the actual number of bytes
+ *consumed. This can be useful to debug the input stream in case
+ *of encoding error.
+ *@param a_out out parameter. Points to the output string. It is allocated
+ *by this function and must be freed by the caller.
+ *@param a_out_len out parameter. The length of the output string.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ *
+ */
+enum CRStatus
+cr_utils_utf8_str_to_ucs4 (const guchar * a_in,
+ gulong * a_in_len,
+ guint32 ** a_out, gulong * a_out_len)
+{
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_in && a_in_len
+ && a_out && a_out_len, CR_BAD_PARAM_ERROR);
+
+ status = cr_utils_utf8_str_len_as_ucs4 (a_in,
+ &a_in[*a_in_len - 1],
+ a_out_len);
+
+ g_return_val_if_fail (status == CR_OK, status);
+
+ *a_out = g_malloc0 (*a_out_len * sizeof (guint32));
+
+ status = cr_utils_utf8_to_ucs4 (a_in, a_in_len, *a_out, a_out_len);
+
+ return status;
+}
+
+/**
+ *Converts an ucs4 buffer into an utf8 buffer.
+ *
+ *@param a_in the input ucs4 buffer to convert.
+ *@param a_in_len in/out parameter. The size of the
+ *input buffer to convert. After return, this parameter contains
+ *the actual number of characters consumed.
+ *@param a_out the output converted utf8 buffer. Must be allocated by
+ *the caller.
+ *@param a_out_len in/out parameter. The size of the output buffer.
+ *If this size is actually smaller than the real needed size, the function
+ *just converts what it can and returns a success status. After return,
+ *this param points to the actual number of bytes in the buffer.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_utils_ucs4_to_utf8 (const guint32 * a_in,
+ gulong * a_in_len, guchar * a_out, gulong * a_out_len)
+{
+ gulong in_len = 0,
+ in_index = 0,
+ out_index = 0;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_in && a_in_len && a_out && a_out_len,
+ CR_BAD_PARAM_ERROR);
+
+ if (*a_in_len < 1) {
+ status = CR_OK;
+ goto end;
+ }
+
+ in_len = *a_in_len;
+
+ for (in_index = 0; in_index < in_len; in_index++) {
+ /*
+ *FIXME: return whenever we encounter forbidden char values.
+ */
+
+ if (a_in[in_index] <= 0x7F) {
+ a_out[out_index] = a_in[in_index];
+ out_index++;
+ } else if (a_in[in_index] <= 0x7FF) {
+ a_out[out_index] = (0xC0 | (a_in[in_index] >> 6));
+ a_out[out_index + 1] =
+ (0x80 | (a_in[in_index] & 0x3F));
+ out_index += 2;
+ } else if (a_in[in_index] <= 0xFFFF) {
+ a_out[out_index] = (0xE0 | (a_in[in_index] >> 12));
+ a_out[out_index + 1] =
+ (0x80 | ((a_in[in_index] >> 6) & 0x3F));
+ a_out[out_index + 2] =
+ (0x80 | (a_in[in_index] & 0x3F));
+ out_index += 3;
+ } else if (a_in[in_index] <= 0x1FFFFF) {
+ a_out[out_index] = (0xF0 | (a_in[in_index] >> 18));
+ a_out[out_index + 1]
+ = (0x80 | ((a_in[in_index] >> 12) & 0x3F));
+ a_out[out_index + 2]
+ = (0x80 | ((a_in[in_index] >> 6) & 0x3F));
+ a_out[out_index + 3]
+ = (0x80 | (a_in[in_index] & 0x3F));
+ out_index += 4;
+ } else if (a_in[in_index] <= 0x3FFFFFF) {
+ a_out[out_index] = (0xF8 | (a_in[in_index] >> 24));
+ a_out[out_index + 1] =
+ (0x80 | (a_in[in_index] >> 18));
+ a_out[out_index + 2]
+ = (0x80 | ((a_in[in_index] >> 12) & 0x3F));
+ a_out[out_index + 3]
+ = (0x80 | ((a_in[in_index] >> 6) & 0x3F));
+ a_out[out_index + 4]
+ = (0x80 | (a_in[in_index] & 0x3F));
+ out_index += 5;
+ } else if (a_in[in_index] <= 0x7FFFFFFF) {
+ a_out[out_index] = (0xFC | (a_in[in_index] >> 30));
+ a_out[out_index + 1] =
+ (0x80 | (a_in[in_index] >> 24));
+ a_out[out_index + 2]
+ = (0x80 | ((a_in[in_index] >> 18) & 0x3F));
+ a_out[out_index + 3]
+ = (0x80 | ((a_in[in_index] >> 12) & 0x3F));
+ a_out[out_index + 4]
+ = (0x80 | ((a_in[in_index] >> 6) & 0x3F));
+ a_out[out_index + 4]
+ = (0x80 | (a_in[in_index] & 0x3F));
+ out_index += 6;
+ } else {
+ status = CR_ENCODING_ERROR;
+ goto end;
+ }
+ } /*end for */
+
+ end:
+ *a_in_len = in_index + 1;
+ *a_out_len = out_index + 1;
+
+ return status;
+}
+
+/**
+ *Converts an ucs4 string into an utf8 string.
+ *@param a_in the input string to convert.
+ *@param a_in_len in/out parameter. The length of the input
+ *string. After return, points to the actual number of characters
+ *consumed. This can be useful to debug the input string in case
+ *of encoding error.
+ *@param a_out out parameter. Points to the output string. It is allocated
+ *by this function and must be freed by the caller.
+ *@param a_out_len out parameter. The length (in bytes) of the output string.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_utils_ucs4_str_to_utf8 (const guint32 * a_in,
+ gulong * a_in_len,
+ guchar ** a_out, gulong * a_out_len)
+{
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_in && a_in_len && a_out
+ && a_out_len, CR_BAD_PARAM_ERROR);
+
+ status = cr_utils_ucs4_str_len_as_utf8 (a_in,
+ &a_in[*a_out_len - 1],
+ a_out_len);
+
+ g_return_val_if_fail (status == CR_OK, status);
+
+ status = cr_utils_ucs4_to_utf8 (a_in, a_in_len, *a_out, a_out_len);
+
+ return status;
+}
+
+/**
+ *Converts an ucs1 buffer into an utf8 buffer.
+ *The caller must know the size of the resulting buffer and
+ *allocate it prior to calling this function.
+ *
+ *@param a_in the input ucs1 buffer.
+ *
+ *@param a_in_len in/out parameter. The length of the input buffer.
+ *After return, points to the number of bytes actually consumed even
+ *in case of encoding error.
+ *
+ *@param a_out out parameter. The output utf8 converted buffer.
+ *
+ *@param a_out_len in/out parameter. The size of the output buffer.
+ *If the output buffer size is shorter than the actual needed size,
+ *this function just convert what it can.
+ *
+ *@return CR_OK upon successful completion, an error code otherwise.
+ *
+ */
+enum CRStatus
+cr_utils_ucs1_to_utf8 (const guchar * a_in,
+ gulong * a_in_len, guchar * a_out, gulong * a_out_len)
+{
+ gulong out_index = 0,
+ in_index = 0,
+ in_len = 0,
+ out_len = 0;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_in && a_in_len
+ && a_out_len,
+ CR_BAD_PARAM_ERROR);
+
+ if (*a_in_len == 0) {
+ *a_out_len = 0 ;
+ return status;
+ }
+ g_return_val_if_fail (a_out, CR_BAD_PARAM_ERROR) ;
+
+ in_len = *a_in_len;
+ out_len = *a_out_len;
+
+ for (in_index = 0, out_index = 0;
+ (in_index < in_len) && (out_index < out_len); in_index++) {
+ /*
+ *FIXME: return whenever we encounter forbidden char values.
+ */
+
+ if (a_in[in_index] <= 0x7F) {
+ a_out[out_index] = a_in[in_index];
+ out_index++;
+ } else {
+ a_out[out_index] = (0xC0 | (a_in[in_index] >> 6));
+ a_out[out_index + 1] =
+ (0x80 | (a_in[in_index] & 0x3F));
+ out_index += 2;
+ }
+ } /*end for */
+
+ *a_in_len = in_index;
+ *a_out_len = out_index;
+
+ return status;
+}
+
+/**
+ *Converts an ucs1 string into an utf8 string.
+ *@param a_in_start the beginning of the input string to convert.
+ *@param a_in_end the end of the input string to convert.
+ *@param a_out out parameter. The converted string.
+ *@param a_out out parameter. The length of the converted string.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ *
+ */
+enum CRStatus
+cr_utils_ucs1_str_to_utf8 (const guchar * a_in,
+ gulong * a_in_len,
+ guchar ** a_out, gulong * a_out_len)
+{
+ gulong out_len = 0;
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_in && a_in_len && a_out
+ && a_out_len, CR_BAD_PARAM_ERROR);
+
+ if (*a_in_len < 1) {
+ *a_out_len = 0;
+ *a_out = NULL;
+ return CR_OK;
+ }
+
+ status = cr_utils_ucs1_str_len_as_utf8 (a_in, &a_in[*a_in_len - 1],
+ &out_len);
+
+ g_return_val_if_fail (status == CR_OK, status);
+
+ *a_out = g_malloc0 (out_len);
+
+ status = cr_utils_ucs1_to_utf8 (a_in, a_in_len, *a_out, &out_len);
+
+ *a_out_len = out_len;
+
+ return status;
+}
+
+/**
+ *Converts an utf8 buffer into an ucs1 buffer.
+ *The caller must know the size of the resulting
+ *converted buffer, and allocated it prior to calling this
+ *function.
+ *
+ *@param a_in the input utf8 buffer to convert.
+ *
+ *@param a_in_len in/out parameter. The size of the input utf8 buffer.
+ *After return, points to the number of bytes consumed
+ *by the function even in case of encoding error.
+ *
+ *@param a_out out parameter. Points to the resulting buffer.
+ *Must be allocated by the caller. If the size of a_out is shorter
+ *than its required size, this function converts what it can and return
+ *a successful status.
+ *
+ *@param a_out_len in/out parameter. The size of the output buffer.
+ *After return, points to the number of bytes consumed even in case of
+ *encoding error.
+ *
+ *@return CR_OK upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_utils_utf8_to_ucs1 (const guchar * a_in,
+ gulong * a_in_len, guchar * a_out, gulong * a_out_len)
+{
+ gulong in_index = 0,
+ out_index = 0,
+ in_len = 0,
+ out_len = 0;
+ enum CRStatus status = CR_OK;
+
+ /*
+ *to store the final decoded
+ *unicode char
+ */
+ guint32 c = 0;
+
+ g_return_val_if_fail (a_in && a_in_len
+ && a_out && a_out_len, CR_BAD_PARAM_ERROR);
+
+ if (*a_in_len < 1) {
+ goto end;
+ }
+
+ in_len = *a_in_len;
+ out_len = *a_out_len;
+
+ for (in_index = 0, out_index = 0;
+ (in_index < in_len) && (out_index < out_len);
+ in_index++, out_index++) {
+ gint nb_bytes_2_decode = 0;
+
+ if (a_in[in_index] <= 0x7F) {
+ /*
+ *7 bits long char
+ *encoded over 1 byte:
+ * 0xxx xxxx
+ */
+ c = a_in[in_index];
+ nb_bytes_2_decode = 1;
+
+ } else if ((a_in[in_index] & 0xE0) == 0xC0) {
+ /*
+ *up to 11 bits long char.
+ *encoded over 2 bytes:
+ *110x xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 0x1F;
+ nb_bytes_2_decode = 2;
+
+ } else if ((a_in[in_index] & 0xF0) == 0xE0) {
+ /*
+ *up to 16 bit long char
+ *encoded over 3 bytes:
+ *1110 xxxx 10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 0x0F;
+ nb_bytes_2_decode = 3;
+
+ } else if ((a_in[in_index] & 0xF8) == 0xF0) {
+ /*
+ *up to 21 bits long char
+ *encoded over 4 bytes:
+ *1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 0x7;
+ nb_bytes_2_decode = 4;
+
+ } else if ((a_in[in_index] & 0xFC) == 0xF8) {
+ /*
+ *up to 26 bits long char
+ *encoded over 5 bytes.
+ *1111 10xx 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 3;
+ nb_bytes_2_decode = 5;
+
+ } else if ((a_in[in_index] & 0xFE) == 0xFC) {
+ /*
+ *up to 31 bits long char
+ *encoded over 6 bytes:
+ *1111 110x 10xx xxxx 10xx xxxx
+ *10xx xxxx 10xx xxxx 10xx xxxx
+ */
+ c = a_in[in_index] & 1;
+ nb_bytes_2_decode = 6;
+
+ } else {
+ /*BAD ENCODING */
+ status = CR_ENCODING_ERROR;
+ goto end;
+ }
+
+ /*
+ *Go and decode the remaining byte(s)
+ *(if any) to get the current character.
+ */
+ if (in_index + nb_bytes_2_decode - 1 >= in_len) {
+ goto end;
+ }
+
+ for (; nb_bytes_2_decode > 1; nb_bytes_2_decode--) {
+ /*decode the next byte */
+ in_index++;
+
+ /*byte pattern must be: 10xx xxxx */
+ if ((a_in[in_index] & 0xC0) != 0x80) {
+ status = CR_ENCODING_ERROR;
+ goto end;
+ }
+
+ c = (c << 6) | (a_in[in_index] & 0x3F);
+ }
+
+ /*
+ *The decoded ucs4 char is now
+ *in c.
+ */
+
+ if (c > 0xFF) {
+ status = CR_ENCODING_ERROR;
+ goto end;
+ }
+
+ a_out[out_index] = c;
+ }
+
+ end:
+ *a_out_len = out_index;
+ *a_in_len = in_index;
+
+ return status;
+}
+
+/**
+ *Converts an utf8 buffer into an
+ *ucs1 buffer.
+ *@param a_in_start the start of the input buffer.
+ *@param a_in_end the end of the input buffer.
+ *@param a_out out parameter. The resulting converted ucs4 buffer.
+ *Must be freed by the caller.
+ *@param a_out_len out parameter. The length of the converted buffer.
+ *@return CR_OK upon successful completion, an error code otherwise.
+ *Note that out parameters are valid if and only if this function
+ *returns CR_OK.
+ */
+enum CRStatus
+cr_utils_utf8_str_to_ucs1 (const guchar * a_in,
+ gulong * a_in_len,
+ guchar ** a_out, gulong * a_out_len)
+{
+ enum CRStatus status = CR_OK;
+
+ g_return_val_if_fail (a_in && a_in_len
+ && a_out && a_out_len, CR_BAD_PARAM_ERROR);
+
+ if (*a_in_len < 1) {
+ *a_out_len = 0;
+ *a_out = NULL;
+ return CR_OK;
+ }
+
+ status = cr_utils_utf8_str_len_as_ucs4 (a_in, &a_in[*a_in_len - 1],
+ a_out_len);
+
+ g_return_val_if_fail (status == CR_OK, status);
+
+ *a_out = g_malloc0 (*a_out_len * sizeof (guint32));
+
+ status = cr_utils_utf8_to_ucs1 (a_in, a_in_len, *a_out, a_out_len);
+ return status;
+}
+
+/*****************************************
+ *CSS basic types identification utilities
+ *****************************************/
+
+/**
+ *Returns TRUE if a_char is a white space as
+ *defined in the css spec in chap 4.1.1.
+ *
+ *white-space ::= ' '| \t|\r|\n|\f
+ *
+ *@param a_char the character to test.
+ *return TRUE if is a white space, false otherwise.
+ */
+gboolean
+cr_utils_is_white_space (guint32 a_char)
+{
+ switch (a_char) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ case '\f':
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ *Returns true if the character is a newline
+ *as defined in the css spec in the chap 4.1.1.
+ *
+ *nl ::= \n|\r\n|\r|\f
+ *
+ *@param a_char the character to test.
+ *@return TRUE if the character is a newline, FALSE otherwise.
+ */
+gboolean
+cr_utils_is_newline (guint32 a_char)
+{
+ switch (a_char) {
+ case '\n':
+ case '\r':
+ case '\f':
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ *returns TRUE if the char is part of an hexa num char:
+ *i.e hexa_char ::= [0-9A-F]
+ */
+gboolean
+cr_utils_is_hexa_char (guint32 a_char)
+{
+ if ((a_char >= '0' && a_char <= '9')
+ || (a_char >= 'A' && a_char <= 'F')) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ *Returns true if the character is a nonascii
+ *character (as defined in the css spec chap 4.1.1):
+ *
+ *nonascii ::= [^\0-\177]
+ *
+ *@param a_char the character to test.
+ *@return TRUE if the character is a nonascii char,
+ *FALSE otherwise.
+ */
+gboolean
+cr_utils_is_nonascii (guint32 a_char)
+{
+ if (a_char <= 177) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ *Dumps a character a_nb times on a file.
+ *@param a_char the char to dump
+ *@param a_fp the destination file pointer
+ *@param a_nb the number of times a_char is to be dumped.
+ */
+void
+cr_utils_dump_n_chars (guchar a_char, FILE * a_fp, glong a_nb)
+{
+ glong i = 0;
+
+ for (i = 0; i < a_nb; i++) {
+ fprintf (a_fp, "%c", a_char);
+ }
+}
+
+void
+cr_utils_dump_n_chars2 (guchar a_char, GString * a_string, glong a_nb)
+{
+ glong i = 0;
+
+ g_return_if_fail (a_string);
+
+ for (i = 0; i < a_nb; i++) {
+ g_string_append_printf (a_string, "%c", a_char);
+ }
+}
+
+/**
+ *Duplicates a list of GString instances.
+ *@return the duplicated list of GString instances or NULL if
+ *something bad happened.
+ *@param a_list_of_strings the list of strings to be duplicated.
+ */
+GList *
+cr_utils_dup_glist_of_string (GList const * a_list_of_strings)
+{
+ GList const *cur = NULL;
+ GList *result = NULL;
+
+ g_return_val_if_fail (a_list_of_strings, NULL);
+
+ for (cur = a_list_of_strings; cur; cur = cur->next) {
+ GString *str = NULL;
+
+ str = g_string_new_len (((GString *) cur->data)->str,
+ ((GString *) cur->data)->len);
+ if (str)
+ result = g_list_append (result, str);
+ }
+
+ return result;
+}
+
+/**
+ *Duplicate a GList where the GList::data is a CRString.
+ *@param a_list_of_strings the list to duplicate
+ *@return the duplicated list, or NULL if something bad
+ *happened.
+ */
+GList *
+cr_utils_dup_glist_of_cr_string (GList const * a_list_of_strings)
+{
+ GList const *cur = NULL;
+ GList *result = NULL;
+
+ g_return_val_if_fail (a_list_of_strings, NULL);
+
+ for (cur = a_list_of_strings; cur; cur = cur->next) {
+ CRString *str = NULL;
+
+ str = cr_string_dup ((CRString const *) cur->data) ;
+ if (str)
+ result = g_list_append (result, str);
+ }
+
+ return result;
+}
diff --git a/src/st/croco/cr-utils.h b/src/st/croco/cr-utils.h
new file mode 100644
index 0000000..54aa249
--- /dev/null
+++ b/src/st/croco/cr-utils.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Author: Dodji Seketeli
+ * Look at file COPYRIGHTS for copyright information
+ */
+
+#ifndef __CR_DEFS_H__
+#define __CR_DEFS_H__
+
+#include <stdio.h>
+#include <glib.h>
+#include "libcroco-config.h"
+
+G_BEGIN_DECLS
+
+/**
+ *@file
+ *The Croco library basic types definitions
+ *And global definitions.
+ */
+
+/**
+ *The status type returned
+ *by the methods of the croco library.
+ */
+enum CRStatus {
+ CR_OK,
+ CR_BAD_PARAM_ERROR,
+ CR_INSTANCIATION_FAILED_ERROR,
+ CR_UNKNOWN_TYPE_ERROR,
+ CR_UNKNOWN_PROP_ERROR,
+ CR_UNKNOWN_PROP_VAL_ERROR,
+ CR_UNEXPECTED_POSITION_SCHEME,
+ CR_START_OF_INPUT_ERROR,
+ CR_END_OF_INPUT_ERROR,
+ CR_OUTPUT_TOO_SHORT_ERROR,
+ CR_INPUT_TOO_SHORT_ERROR,
+ CR_OUT_OF_BOUNDS_ERROR,
+ CR_EMPTY_PARSER_INPUT_ERROR,
+ CR_ENCODING_ERROR,
+ CR_ENCODING_NOT_FOUND_ERROR,
+ CR_PARSING_ERROR,
+ CR_SYNTAX_ERROR,
+ CR_NO_ROOT_NODE_ERROR,
+ CR_NO_TOKEN,
+ CR_OUT_OF_MEMORY_ERROR,
+ CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR,
+ CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR,
+ CR_ERROR,
+ CR_FILE_NOT_FOUND_ERROR,
+ CR_VALUE_NOT_FOUND_ERROR
+} ;
+
+/**
+ *Values used by
+ *cr_input_seek_position() ;
+ */
+enum CRSeekPos {
+ CR_SEEK_CUR,
+ CR_SEEK_BEGIN,
+ CR_SEEK_END
+} ;
+
+/**
+ *Encoding values.
+ */
+enum CREncoding
+{
+ CR_UCS_4 = 1/*Must be not NULL*/,
+ CR_UCS_1,
+ CR_ISO_8859_1,
+ CR_ASCII,
+ CR_UTF_8,
+ CR_UTF_16,
+ CR_AUTO/*should be the last one*/
+} ;
+
+
+
+
+#define CROCO_LOG_DOMAIN "LIBCROCO"
+
+#ifdef __GNUC__
+#define cr_utils_trace(a_log_level, a_msg) \
+g_log (CROCO_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d (%s): %s\n", \
+ __FILE__, \
+ __LINE__, \
+ __PRETTY_FUNCTION__, \
+ a_msg)
+#else /*__GNUC__*/
+
+#define cr_utils_trace(a_log_level, a_msg) \
+g_log (CROCO_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d: %s\n", \
+ __FILE__, \
+ __LINE__, \
+ a_msg)
+#endif
+
+/**
+ *Traces an info message.
+ *The file, line and enclosing function
+ *of the message will be automatically
+ *added to the message.
+ *@param a_msg the msg to trace.
+ */
+#define cr_utils_trace_info(a_msg) \
+cr_utils_trace (G_LOG_LEVEL_INFO, a_msg)
+
+/**
+ *Trace a debug message.
+ *The file, line and enclosing function
+ *of the message will be automatically
+ *added to the message.
+ *@param a_msg the msg to trace.
+ */
+#define cr_utils_trace_debug(a_msg) \
+cr_utils_trace (G_LOG_LEVEL_DEBUG, a_msg) ;
+
+
+/****************************
+ *Encoding transformations and
+ *encoding helpers
+ ****************************/
+
+enum CRStatus
+cr_utils_read_char_from_utf8_buf (const guchar * a_in, gulong a_in_len,
+ guint32 *a_out, gulong *a_consumed) ;
+
+enum CRStatus
+cr_utils_ucs1_to_utf8 (const guchar *a_in, gulong *a_in_len,
+ guchar *a_out, gulong *a_out_len) ;
+
+enum CRStatus
+cr_utils_utf8_to_ucs1 (const guchar * a_in, gulong * a_in_len,
+ guchar *a_out, gulong *a_out_len) ;
+
+enum CRStatus
+cr_utils_ucs4_to_utf8 (const guint32 *a_in, gulong *a_in_len,
+ guchar *a_out, gulong *a_out_len) ;
+
+enum CRStatus
+cr_utils_utf8_str_len_as_ucs4 (const guchar *a_in_start,
+ const guchar *a_in_end,
+ gulong *a_len) ;
+enum CRStatus
+cr_utils_ucs1_str_len_as_utf8 (const guchar *a_in_start,
+ const guchar *a_in_end,
+ gulong *a_len) ;
+enum CRStatus
+cr_utils_utf8_str_len_as_ucs1 (const guchar *a_in_start,
+ const guchar *a_in_end,
+ gulong *a_len) ;
+enum CRStatus
+cr_utils_ucs4_str_len_as_utf8 (const guint32 *a_in_start,
+ const guint32 *a_in_end,
+ gulong *a_len) ;
+
+enum CRStatus
+cr_utils_ucs1_str_to_utf8 (const guchar *a_in_start,
+ gulong *a_in_len,
+ guchar **a_out,
+ gulong *a_len) ;
+
+enum CRStatus
+cr_utils_utf8_str_to_ucs1 (const guchar * a_in_start,
+ gulong * a_in_len,
+ guchar **a_out,
+ gulong *a_out_len) ;
+
+enum CRStatus
+cr_utils_utf8_to_ucs4 (const guchar * a_in,
+ gulong * a_in_len,
+ guint32 *a_out, gulong *a_out_len) ;
+
+enum CRStatus
+cr_utils_ucs4_str_to_utf8 (const guint32 *a_in,
+ gulong *a_in_len,
+ guchar **a_out, gulong *a_out_len) ;
+
+enum CRStatus
+cr_utils_utf8_str_to_ucs4 (const guchar * a_in,
+ gulong *a_in_len,
+ guint32 **a_out,
+ gulong *a_out_len) ;
+
+
+/*****************************************
+ *CSS basic types identification utilities
+ *****************************************/
+
+gboolean
+cr_utils_is_newline (guint32 a_char) ;
+
+gboolean
+cr_utils_is_white_space (guint32 a_char) ;
+
+gboolean
+cr_utils_is_nonascii (guint32 a_char) ;
+
+gboolean
+cr_utils_is_hexa_char (guint32 a_char) ;
+
+
+/**********************************
+ *Miscellaneous utility functions
+ ***********************************/
+
+void
+cr_utils_dump_n_chars (guchar a_char,
+ FILE *a_fp,
+ glong a_nb) ;
+
+void
+cr_utils_dump_n_chars2 (guchar a_char,
+ GString *a_string,
+ glong a_nb) ;
+GList *
+cr_utils_dup_glist_of_string (GList const *a_list) ;
+
+GList *
+cr_utils_dup_glist_of_cr_string (GList const * a_list_of_strings) ;
+
+G_END_DECLS
+
+#endif /*__CR_DEFS_H__*/
diff --git a/src/st/croco/libcroco-config.h b/src/st/croco/libcroco-config.h
new file mode 100644
index 0000000..1ffb758
--- /dev/null
+++ b/src/st/croco/libcroco-config.h
@@ -0,0 +1,13 @@
+#ifndef LIBCROCO_VERSION_NUMBER
+#define LIBCROCO_VERSION_NUMBER 612
+#endif
+
+#ifndef LIBCROCO_VERSION
+#define LIBCROCO_VERSION "0.6.12"
+#endif
+
+#ifndef G_DISABLE_CHECKS
+#if 0
+#define G_DISABLE_CHECKS 0
+#endif
+#endif
diff --git a/src/st/croco/libcroco.h b/src/st/croco/libcroco.h
new file mode 100644
index 0000000..6187a7c
--- /dev/null
+++ b/src/st/croco/libcroco.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of The Croco Library
+ *
+ * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __LIBCROCO_H__
+#define __LIBCROCO_H__
+
+#include "libcroco-config.h"
+
+#include "cr-utils.h"
+#include "cr-pseudo.h"
+#include "cr-term.h"
+#include "cr-attr-sel.h"
+#include "cr-simple-sel.h"
+#include "cr-selector.h"
+#include "cr-enc-handler.h"
+#include "cr-doc-handler.h"
+#include "cr-input.h"
+#include "cr-parser.h"
+#include "cr-statement.h"
+#include "cr-stylesheet.h"
+#include "cr-om-parser.h"
+#include "cr-prop-list.h"
+#include "cr-string.h"
+
+#endif /*__LIBCROCO_H__*/
diff --git a/src/st/meson.build b/src/st/meson.build
new file mode 100644
index 0000000..717aa05
--- /dev/null
+++ b/src/st/meson.build
@@ -0,0 +1,220 @@
+# please, keep this sorted alphabetically
+st_headers = [
+ 'st-adjustment.h',
+ 'st-bin.h',
+ 'st-border-image.h',
+ 'st-box-layout.h',
+ 'st-button.h',
+ 'st-clipboard.h',
+ 'st-drawing-area.h',
+ 'st-entry.h',
+ 'st-focus-manager.h',
+ 'st-generic-accessible.h',
+ 'st-icon.h',
+ 'st-icon-colors.h',
+ 'st-image-content.h',
+ 'st-label.h',
+ 'st-password-entry.h',
+ 'st-scrollable.h',
+ 'st-scroll-bar.h',
+ 'st-scroll-view.h',
+ 'st-scroll-view-fade.h',
+ 'st-settings.h',
+ 'st-shadow.h',
+ 'st-texture-cache.h',
+ 'st-theme.h',
+ 'st-theme-context.h',
+ 'st-theme-node.h',
+ 'st-types.h',
+ 'st-viewport.h',
+ 'st-widget.h',
+ 'st-widget-accessible.h'
+]
+
+st_includes = []
+foreach include : st_headers
+ st_includes += '#include <@0@>'.format(include)
+endforeach
+
+st_h_data = configuration_data()
+st_h_data.set('includes', '\n'.join(st_includes))
+
+st_h = configure_file(
+ input: 'st.h.in',
+ output: 'st.h',
+ configuration: st_h_data
+)
+
+st_inc = include_directories('.', '..')
+
+# please, keep this sorted alphabetically
+st_private_headers = [
+ 'croco/cr-additional-sel.h',
+ 'croco/cr-attr-sel.h',
+ 'croco/cr-cascade.h',
+ 'croco/cr-declaration.h',
+ 'croco/cr-doc-handler.h',
+ 'croco/cr-enc-handler.h',
+ 'croco/cr-fonts.h',
+ 'croco/cr-input.h',
+ 'croco/cr-num.h',
+ 'croco/cr-om-parser.h',
+ 'croco/cr-parser.h',
+ 'croco/cr-parsing-location.h',
+ 'croco/cr-prop-list.h',
+ 'croco/cr-pseudo.h',
+ 'croco/cr-rgb.h',
+ 'croco/cr-selector.h',
+ 'croco/cr-simple-sel.h',
+ 'croco/cr-statement.h',
+ 'croco/cr-string.h',
+ 'croco/cr-stylesheet.h',
+ 'croco/cr-term.h',
+ 'croco/cr-tknzr.h',
+ 'croco/cr-token.h',
+ 'croco/cr-utils.h',
+ 'croco/libcroco-config.h',
+ 'croco/libcroco.h',
+ 'st-private.h',
+ 'st-theme-private.h',
+ 'st-theme-node-private.h',
+ 'st-theme-node-transition.h'
+]
+
+# please, keep this sorted alphabetically
+croco_sources = [
+ 'croco/cr-additional-sel.c',
+ 'croco/cr-attr-sel.c',
+ 'croco/cr-cascade.c',
+ 'croco/cr-declaration.c',
+ 'croco/cr-doc-handler.c',
+ 'croco/cr-enc-handler.c',
+ 'croco/cr-fonts.c',
+ 'croco/cr-input.c',
+ 'croco/cr-num.c',
+ 'croco/cr-om-parser.c',
+ 'croco/cr-parser.c',
+ 'croco/cr-parsing-location.c',
+ 'croco/cr-prop-list.c',
+ 'croco/cr-pseudo.c',
+ 'croco/cr-rgb.c',
+ 'croco/cr-selector.c',
+ 'croco/cr-simple-sel.c',
+ 'croco/cr-statement.c',
+ 'croco/cr-string.c',
+ 'croco/cr-stylesheet.c',
+ 'croco/cr-term.c',
+ 'croco/cr-tknzr.c',
+ 'croco/cr-token.c',
+ 'croco/cr-utils.c',
+]
+
+# please, keep this sorted alphabetically
+st_sources = [
+ 'st-adjustment.c',
+ 'st-bin.c',
+ 'st-border-image.c',
+ 'st-box-layout.c',
+ 'st-button.c',
+ 'st-clipboard.c',
+ 'st-drawing-area.c',
+ 'st-entry.c',
+ 'st-focus-manager.c',
+ 'st-generic-accessible.c',
+ 'st-icon.c',
+ 'st-icon-colors.c',
+ 'st-image-content.c',
+ 'st-label.c',
+ 'st-password-entry.c',
+ 'st-private.c',
+ 'st-scrollable.c',
+ 'st-scroll-bar.c',
+ 'st-scroll-view.c',
+ 'st-scroll-view-fade.c',
+ 'st-settings.c',
+ 'st-shadow.c',
+ 'st-texture-cache.c',
+ 'st-theme.c',
+ 'st-theme-context.c',
+ 'st-theme-node.c',
+ 'st-theme-node-drawing.c',
+ 'st-theme-node-transition.c',
+ 'st-viewport.c',
+ 'st-widget.c'
+]
+
+st_enums = gnome.mkenums_simple('st-enum-types',
+ sources: st_headers,
+ header_prefix: '''
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif'''
+)
+
+st_gir_sources = st_sources + st_headers + st_enums
+
+data_to_c = find_program(meson.project_source_root() + '/src/data-to-c.pl')
+
+glsl_sources = custom_target('scroll-view-fade-glsl',
+ input: ['st-scroll-view-fade.glsl'],
+ output: ['st-scroll-view-fade-generated.h'],
+ capture: true,
+ command: [data_to_c, '@INPUT@', 'st_scroll_view_fade_glsl']
+)
+
+st_nogir_sources = [glsl_sources]
+
+st_cflags = [
+ '-I@0@/src'.format(meson.project_source_root()),
+ '-I@0@'.format(meson.project_build_root()),
+ '-DPREFIX="@0@"'.format(prefix),
+ '-DLIBDIR="@0@"'.format(libdir),
+ '-DG_LOG_DOMAIN="St"',
+ '-DST_COMPILATION',
+ '-DCLUTTER_ENABLE_EXPERIMENTAL_API',
+ '-DCOGL_ENABLE_EXPERIMENTAL_API',
+ '-DPACKAGE_DATA_DIR="@0@"'.format(pkgdatadir)
+]
+
+# Currently meson requires a shared library for building girs
+libst = shared_library('st-1.0',
+ sources: st_gir_sources + st_nogir_sources + croco_sources,
+ c_args: st_cflags,
+ dependencies: [clutter_dep, gtk_dep, mutter_dep, libxml_dep, m_dep],
+ build_rpath: mutter_typelibdir,
+ install_rpath: mutter_typelibdir,
+ install_dir: pkglibdir,
+ install: true
+)
+
+libst_dep = declare_dependency(link_with: libst,
+ sources: st_enums[1]
+)
+
+if get_option('tests')
+ mutter_test_dep = dependency(libmutter_test_pc, version: mutter_req)
+ test_theme = executable('test-theme',
+ sources: 'test-theme.c',
+ c_args: st_cflags,
+ dependencies: [mutter_test_dep, gtk_dep, libxml_dep],
+ build_rpath: mutter_typelibdir,
+ link_with: libst
+ )
+
+ test('CSS styling support', test_theme,
+ workdir: meson.current_source_dir()
+ )
+endif
+
+libst_gir = gnome.generate_gir(libst,
+ sources: st_gir_sources,
+ nsversion: '1.0',
+ namespace: 'St',
+ includes: ['Clutter-' + mutter_api_version, 'Cally-' + mutter_api_version, 'Meta-' + mutter_api_version, 'Gtk-3.0'],
+ dependencies: [mutter_dep],
+ include_directories: include_directories('..'),
+ extra_args: ['-DST_COMPILATION', '--quiet'],
+ install_dir_gir: pkgdatadir,
+ install_dir_typelib: pkglibdir,
+ install: true
+)
diff --git a/src/st/st-adjustment.c b/src/st/st-adjustment.c
new file mode 100644
index 0000000..d2baa66
--- /dev/null
+++ b/src/st/st-adjustment.c
@@ -0,0 +1,1013 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-adjustment.c: Adjustment object
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-adjustment
+ * @short_description: A GObject representing an adjustable bounded value
+ *
+ * The #StAdjustment object represents a range of values bounded between a
+ * minimum and maximum, together with step and page increments and a page size.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+#include <math.h>
+
+#include "st-adjustment.h"
+#include "st-private.h"
+
+typedef struct _StAdjustmentPrivate StAdjustmentPrivate;
+
+struct _StAdjustmentPrivate
+{
+ ClutterActor *actor;
+
+ /* Do not sanity-check values while constructing,
+ * not all properties may be set yet. */
+ guint is_constructing : 1;
+
+ GHashTable *transitions;
+
+ gdouble lower;
+ gdouble upper;
+ gdouble value;
+ gdouble step_increment;
+ gdouble page_increment;
+ gdouble page_size;
+};
+
+static void animatable_iface_init (ClutterAnimatableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (StAdjustment, st_adjustment, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (StAdjustment)
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
+ animatable_iface_init));
+
+enum
+{
+ PROP_0,
+
+ PROP_ACTOR,
+ PROP_LOWER,
+ PROP_UPPER,
+ PROP_VALUE,
+ PROP_STEP_INC,
+ PROP_PAGE_INC,
+ PROP_PAGE_SIZE,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum
+{
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+typedef struct _TransitionClosure
+{
+ StAdjustment *adjustment;
+ ClutterTransition *transition;
+ char *name;
+ gulong completed_id;
+} TransitionClosure;
+
+static gboolean st_adjustment_set_lower (StAdjustment *adjustment,
+ gdouble lower);
+static gboolean st_adjustment_set_upper (StAdjustment *adjustment,
+ gdouble upper);
+static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment,
+ gdouble step);
+static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment,
+ gdouble page);
+static gboolean st_adjustment_set_page_size (StAdjustment *adjustment,
+ gdouble size);
+
+static ClutterActor *
+st_adjustment_get_actor (ClutterAnimatable *animatable)
+{
+ StAdjustment *adjustment = ST_ADJUSTMENT (animatable);
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
+
+ g_warn_if_fail (priv->actor);
+
+ return priv->actor;
+}
+
+static void
+animatable_iface_init (ClutterAnimatableInterface *iface)
+{
+ iface->get_actor = st_adjustment_get_actor;
+}
+
+static void
+st_adjustment_constructed (GObject *object)
+{
+ GObjectClass *g_class;
+ StAdjustment *self = ST_ADJUSTMENT (object);
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (self);
+
+ g_class = G_OBJECT_CLASS (st_adjustment_parent_class);
+ /* The docs say we're suppose to chain up, but would crash without
+ * some extra care. */
+ if (g_class && g_class->constructed &&
+ g_class->constructed != st_adjustment_constructed)
+ {
+ g_class->constructed (object);
+ }
+
+ priv->is_constructing = FALSE;
+ st_adjustment_clamp_page (self, priv->lower, priv->upper);
+}
+
+static void
+st_adjustment_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (ST_ADJUSTMENT (gobject));
+
+ switch (prop_id)
+ {
+ case PROP_ACTOR:
+ g_value_set_object (value, priv->actor);
+ break;
+
+ case PROP_LOWER:
+ g_value_set_double (value, priv->lower);
+ break;
+
+ case PROP_UPPER:
+ g_value_set_double (value, priv->upper);
+ break;
+
+ case PROP_VALUE:
+ g_value_set_double (value, priv->value);
+ break;
+
+ case PROP_STEP_INC:
+ g_value_set_double (value, priv->step_increment);
+ break;
+
+ case PROP_PAGE_INC:
+ g_value_set_double (value, priv->page_increment);
+ break;
+
+ case PROP_PAGE_SIZE:
+ g_value_set_double (value, priv->page_size);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+actor_destroyed (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ StAdjustment *adj = ST_ADJUSTMENT (user_data);
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adj);
+
+ priv->actor = NULL;
+
+ g_object_notify_by_pspec (G_OBJECT (adj), props[PROP_ACTOR]);
+}
+
+static void
+st_adjustment_set_actor (StAdjustment *adj,
+ ClutterActor *actor)
+{
+ StAdjustmentPrivate *priv;
+
+ priv = st_adjustment_get_instance_private (adj);
+
+ if (priv->actor == actor)
+ return;
+
+ if (priv->actor)
+ g_object_weak_unref (G_OBJECT (priv->actor), actor_destroyed, adj);
+ priv->actor = actor;
+ if (priv->actor)
+ g_object_weak_ref (G_OBJECT (priv->actor), actor_destroyed, adj);
+
+ g_object_notify_by_pspec (G_OBJECT (adj), props[PROP_ACTOR]);
+}
+
+static void
+st_adjustment_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StAdjustment *adj = ST_ADJUSTMENT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_ACTOR:
+ st_adjustment_set_actor (adj, g_value_get_object (value));
+ break;
+
+ case PROP_LOWER:
+ st_adjustment_set_lower (adj, g_value_get_double (value));
+ break;
+
+ case PROP_UPPER:
+ st_adjustment_set_upper (adj, g_value_get_double (value));
+ break;
+
+ case PROP_VALUE:
+ st_adjustment_set_value (adj, g_value_get_double (value));
+ break;
+
+ case PROP_STEP_INC:
+ st_adjustment_set_step_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_INC:
+ st_adjustment_set_page_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_SIZE:
+ st_adjustment_set_page_size (adj, g_value_get_double (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_adjustment_dispose (GObject *object)
+{
+ StAdjustmentPrivate *priv;
+
+ priv = st_adjustment_get_instance_private (ST_ADJUSTMENT (object));
+ if (priv->actor)
+ {
+ g_object_weak_unref (G_OBJECT (priv->actor), actor_destroyed, object);
+ priv->actor = NULL;
+ }
+ g_clear_pointer (&priv->transitions, g_hash_table_unref);
+
+ G_OBJECT_CLASS (st_adjustment_parent_class)->dispose (object);
+}
+
+static void
+st_adjustment_class_init (StAdjustmentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = st_adjustment_constructed;
+ object_class->get_property = st_adjustment_get_property;
+ object_class->set_property = st_adjustment_set_property;
+ object_class->dispose = st_adjustment_dispose;
+
+ /**
+ * StAdjustment:actor:
+ *
+ * If the adjustment is used as #ClutterAnimatable for a
+ * #ClutterPropertyTransition, this property is used to determine which
+ * monitor should drive the animation.
+ */
+ props[PROP_ACTOR] =
+ g_param_spec_object ("actor", "Actor", "Actor",
+ CLUTTER_TYPE_ACTOR,
+ ST_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StAdjustment:lower:
+ *
+ * The minimum value of the adjustment.
+ */
+ props[PROP_LOWER] =
+ g_param_spec_double ("lower", "Lower", "Lower bound",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StAdjustment:upper:
+ *
+ * The maximum value of the adjustment.
+ *
+ * Note that values will be restricted by `upper - page-size` if
+ * #StAdjustment:page-size is non-zero.
+ */
+ props[PROP_UPPER] =
+ g_param_spec_double ("upper", "Upper", "Upper bound",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StAdjustment:value:
+ *
+ * The value of the adjustment.
+ */
+ props[PROP_VALUE] =
+ g_param_spec_double ("value", "Value", "Current value",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StAdjustment:step-increment:
+ *
+ * The step increment of the adjustment.
+ */
+ props[PROP_STEP_INC] =
+ g_param_spec_double ("step-increment", "Step Increment", "Step increment",
+ 0.0, G_MAXDOUBLE, 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StAdjustment:page-increment:
+ *
+ * The page increment of the adjustment.
+ */
+ props[PROP_PAGE_INC] =
+ g_param_spec_double ("page-increment", "Page Increment", "Page increment",
+ 0.0, G_MAXDOUBLE, 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StAdjustment:page-size:
+ *
+ * The page size of the adjustment.
+ *
+ * Note that the page-size is irrelevant and should be set to zero if the
+ * adjustment is used for a simple scalar value.
+ */
+ props[PROP_PAGE_SIZE] =
+ g_param_spec_double ("page-size", "Page Size", "Page size",
+ 0.0, G_MAXDOUBLE, 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+
+ /**
+ * StAdjustment::changed:
+ * @self: the #StAdjustment
+ *
+ * Emitted when any of the adjustment properties have changed, except for
+ * #StAdjustment:value.
+ */
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StAdjustmentClass, changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_adjustment_init (StAdjustment *self)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (self);
+ priv->is_constructing = TRUE;
+}
+
+/**
+ * st_adjustment_new:
+ * @actor: (nullable): a #ClutterActor
+ * @value: the initial value
+ * @lower: the minimum value
+ * @upper: the maximum value
+ * @step_increment: the step increment
+ * @page_increment: the page increment
+ * @page_size: the page size
+ *
+ * Creates a new #StAdjustment
+ *
+ * Returns: a new #StAdjustment
+ */
+StAdjustment *
+st_adjustment_new (ClutterActor *actor,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ return g_object_new (ST_TYPE_ADJUSTMENT,
+ "actor", actor,
+ "value", value,
+ "lower", lower,
+ "upper", upper,
+ "step-increment", step_increment,
+ "page-increment", page_increment,
+ "page-size", page_size,
+ NULL);
+}
+
+/**
+ * st_adjustment_get_value:
+ * @adjustment: a #StAdjustment
+ *
+ * Gets the current value of the adjustment. See st_adjustment_set_value().
+ *
+ * Returns: The current value of the adjustment
+ */
+gdouble
+st_adjustment_get_value (StAdjustment *adjustment)
+{
+ g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), 0);
+
+ return ((StAdjustmentPrivate *)st_adjustment_get_instance_private (adjustment))->value;
+}
+
+/**
+ * st_adjustment_set_value:
+ * @adjustment: a #StAdjustment
+ * @value: the new value
+ *
+ * Sets the #StAdjustment value. The value is clamped to lie between
+ * #StAdjustment:lower and #StAdjustment:upper - #StAdjustment:page-size.
+ */
+void
+st_adjustment_set_value (StAdjustment *adjustment,
+ gdouble value)
+{
+ StAdjustmentPrivate *priv;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing)
+ {
+ value = CLAMP (value,
+ priv->lower,
+ MAX (priv->lower, priv->upper - priv->page_size));
+ }
+
+ if (priv->value != value)
+ {
+ priv->value = value;
+
+ g_object_notify_by_pspec (G_OBJECT (adjustment), props[PROP_VALUE]);
+ }
+}
+
+/**
+ * st_adjustment_clamp_page:
+ * @adjustment: a #StAdjustment
+ * @lower: the lower value
+ * @upper: the upper value
+ *
+ * Set #StAdjustment:value to a value clamped between @lower and @upper. The
+ * clamping described by st_adjustment_set_value() still applies.
+ */
+void
+st_adjustment_clamp_page (StAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper)
+{
+ StAdjustmentPrivate *priv;
+ gboolean changed;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
+ upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
+
+ changed = FALSE;
+
+ if (priv->value + priv->page_size > upper)
+ {
+ priv->value = upper - priv->page_size;
+ changed = TRUE;
+ }
+
+ if (priv->value < lower)
+ {
+ priv->value = lower;
+ changed = TRUE;
+ }
+
+ if (changed)
+ g_object_notify_by_pspec (G_OBJECT (adjustment), props[PROP_VALUE]);
+}
+
+/**
+ * st_adjustment_set_lower:
+ * @adjustment: a #StAdjustment
+ * @lower: the new minimum value
+ *
+ * Sets the minimum value of the adjustment.
+ *
+ * When setting multiple adjustment properties via their individual
+ * setters, multiple #GObject::notify and #StAdjustment::changed
+ * signals will be emitted. However, it’s possible to compress the
+ * #GObject::notify signals into one by calling
+ * g_object_freeze_notify() and g_object_thaw_notify() around the
+ * calls to the individual setters.
+ *
+ * Alternatively, using st_adjustment_set_values() will compress both
+ * #GObject::notify and #StAdjustment::changed emissions.
+ */
+static gboolean
+st_adjustment_set_lower (StAdjustment *adjustment,
+ gdouble lower)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->lower != lower)
+ {
+ priv->lower = lower;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify_by_pspec (G_OBJECT (adjustment), props[PROP_LOWER]);
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing)
+ st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * st_adjustment_set_upper:
+ * @adjustment: a #StAdjustment
+ * @upper: the new maximum value
+ *
+ * Sets the maximum value of the adjustment.
+ *
+ * Note that values will be restricted by `upper - page-size`
+ * if the page-size property is nonzero.
+ *
+ * See st_adjustment_set_lower() about how to compress multiple
+ * signal emissions when setting multiple adjustment properties.
+ *
+ * Returns: %TRUE if the value was changed
+ */
+static gboolean
+st_adjustment_set_upper (StAdjustment *adjustment,
+ gdouble upper)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->upper != upper)
+ {
+ priv->upper = upper;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify_by_pspec (G_OBJECT (adjustment), props[PROP_UPPER]);
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing)
+ st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * st_adjustment_set_step_increment:
+ * @adjustment: a #StAdjustment
+ * @step: the new step increment
+ *
+ * Sets the step increment of the adjustment.
+ *
+ * See st_adjustment_set_lower() about how to compress multiple
+ * signal emissions when setting multiple adjustment properties.
+ *
+ * Returns: %TRUE if the value was changed
+ */
+static gboolean
+st_adjustment_set_step_increment (StAdjustment *adjustment,
+ gdouble step)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->step_increment != step)
+ {
+ priv->step_increment = step;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify_by_pspec (G_OBJECT (adjustment), props[PROP_STEP_INC]);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * st_adjustment_set_page_increment:
+ * @adjustment: a #StAdjustment
+ * @page: the new page increment
+ *
+ * Sets the page increment of the adjustment.
+ *
+ * See st_adjustment_set_lower() about how to compress multiple
+ * signal emissions when setting multiple adjustment properties.
+ *
+ * Returns: %TRUE if the value was changed
+ */
+static gboolean
+st_adjustment_set_page_increment (StAdjustment *adjustment,
+ gdouble page)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->page_increment != page)
+ {
+ priv->page_increment = page;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify_by_pspec (G_OBJECT (adjustment), props[PROP_PAGE_INC]);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * st_adjustment_set_page_size:
+ * @adjustment: a #StAdjustment
+ * @size: the new page size
+ *
+ * Sets the page size of the adjustment.
+ *
+ * See st_adjustment_set_lower() about how to compress multiple
+ * signal emissions when setting multiple adjustment properties.
+ *
+ * Returns: %TRUE if the value was changed
+ */
+static gboolean
+st_adjustment_set_page_size (StAdjustment *adjustment,
+ gdouble size)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->page_size != size)
+ {
+ priv->page_size = size;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify_by_pspec (G_OBJECT (adjustment), props[PROP_PAGE_SIZE]);
+
+ /* We'll explicitly clamp after construction. */
+ if (!priv->is_constructing)
+ st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * st_adjustment_set_values:
+ * @adjustment: a #StAdjustment
+ * @value: the new value
+ * @lower: the new minimum value
+ * @upper: the new maximum value
+ * @step_increment: the new step increment
+ * @page_increment: the new page increment
+ * @page_size: the new page size
+ *
+ * Sets all properties of the adjustment at once.
+ *
+ * Use this function to avoid multiple emissions of the #GObject::notify and
+ * #StAdjustment::changed signals. See st_adjustment_set_lower() for an
+ * alternative way of compressing multiple emissions of #GObject::notify into
+ * one.
+ */
+void
+st_adjustment_set_values (StAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ StAdjustmentPrivate *priv;
+ gboolean emit_changed = FALSE;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+ g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE);
+ g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE);
+ g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE);
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ emit_changed = FALSE;
+
+ g_object_freeze_notify (G_OBJECT (adjustment));
+
+ emit_changed |= st_adjustment_set_lower (adjustment, lower);
+ emit_changed |= st_adjustment_set_upper (adjustment, upper);
+ emit_changed |= st_adjustment_set_step_increment (adjustment, step_increment);
+ emit_changed |= st_adjustment_set_page_increment (adjustment, page_increment);
+ emit_changed |= st_adjustment_set_page_size (adjustment, page_size);
+
+ if (value != priv->value)
+ {
+ st_adjustment_set_value (adjustment, value);
+ emit_changed = TRUE;
+ }
+
+ if (emit_changed)
+ g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
+
+ g_object_thaw_notify (G_OBJECT (adjustment));
+}
+
+/**
+ * st_adjustment_get_values:
+ * @adjustment: an #StAdjustment
+ * @value: (out) (optional): the current value
+ * @lower: (out) (optional): the lower bound
+ * @upper: (out) (optional): the upper bound
+ * @step_increment: (out) (optional): the step increment
+ * @page_increment: (out) (optional): the page increment
+ * @page_size: (out) (optional): the page size
+ *
+ * Gets all of @adjustment's values at once.
+ */
+void
+st_adjustment_get_values (StAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size)
+{
+ StAdjustmentPrivate *priv;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ if (lower)
+ *lower = priv->lower;
+
+ if (upper)
+ *upper = priv->upper;
+
+ if (value)
+ *value = st_adjustment_get_value (adjustment);
+
+ if (step_increment)
+ *step_increment = priv->step_increment;
+
+ if (page_increment)
+ *page_increment = priv->page_increment;
+
+ if (page_size)
+ *page_size = priv->page_size;
+}
+
+/**
+ * st_adjustment_adjust_for_scroll_event:
+ * @adjustment: An #StAdjustment
+ * @delta: A delta, retrieved directly from clutter_event_get_scroll_delta()
+ * or similar.
+ *
+ * Adjusts the adjustment using delta values from a scroll event.
+ * You should use this instead of using st_adjustment_set_value()
+ * as this method will tweak the values directly using the same
+ * math as GTK+, to ensure that scrolling is consistent across
+ * the environment.
+ */
+void
+st_adjustment_adjust_for_scroll_event (StAdjustment *adjustment,
+ gdouble delta)
+{
+ StAdjustmentPrivate *priv;
+ gdouble new_value, scroll_unit;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ scroll_unit = pow (priv->page_size, 2.0 / 3.0);
+
+ new_value = priv->value + delta * scroll_unit;
+ st_adjustment_set_value (adjustment, new_value);
+}
+
+static void
+transition_closure_free (gpointer data)
+{
+ TransitionClosure *clos;
+ ClutterTimeline *timeline;
+
+ if (G_UNLIKELY (data == NULL))
+ return;
+
+ clos = data;
+ timeline = CLUTTER_TIMELINE (clos->transition);
+
+ g_clear_signal_handler (&clos->completed_id, clos->transition);
+
+ if (clutter_timeline_is_playing (timeline))
+ clutter_timeline_stop (timeline);
+
+ g_object_unref (clos->transition);
+ g_free (clos->name);
+ g_free (clos);
+}
+
+static void
+remove_transition (StAdjustment *adjustment,
+ const char *name)
+{
+ StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
+
+ g_hash_table_remove (priv->transitions, name);
+
+ if (g_hash_table_size (priv->transitions) == 0)
+ g_clear_pointer (&priv->transitions, g_hash_table_unref);
+}
+
+static void
+on_transition_stopped (ClutterTransition *transition,
+ gboolean is_finished,
+ TransitionClosure *clos)
+{
+ StAdjustment *adjustment = clos->adjustment;
+
+ if (!clutter_transition_get_remove_on_complete (transition))
+ return;
+
+ /* Take a reference, because removing the closure will
+ * release the reference on the transition, and we want
+ * it to survive the signal emission; ClutterTransition's
+ * own ::stopped signal closure will release it after all
+ * other handlers have run.
+ */
+ g_object_ref (transition);
+
+ remove_transition (adjustment, clos->name);
+}
+
+/**
+ * st_adjustment_get_transition:
+ * @adjustment: a #StAdjustment
+ * @name: a transition name
+ *
+ * Get the #ClutterTransition for @name previously added with
+ * st_adjustment_add_transition() or %NULL if not found.
+ *
+ * Returns: (transfer none) (nullable): a #ClutterTransition
+ */
+ClutterTransition *
+st_adjustment_get_transition (StAdjustment *adjustment,
+ const char *name)
+{
+ StAdjustmentPrivate *priv;
+ TransitionClosure *clos;
+
+ g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), NULL);
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->transitions == NULL)
+ return NULL;
+
+ clos = g_hash_table_lookup (priv->transitions, name);
+ if (clos == NULL)
+ return NULL;
+
+ return clos->transition;
+}
+
+/**
+ * st_adjustment_add_transition:
+ * @adjustment: a #StAdjustment
+ * @name: a unique name for the transition
+ * @transition: a #ClutterTransition
+ *
+ * Add a #ClutterTransition for the adjustment. If the transition stops, it will
+ * be automatically removed if #ClutterTransition:remove-on-complete is %TRUE.
+ */
+void
+st_adjustment_add_transition (StAdjustment *adjustment,
+ const char *name,
+ ClutterTransition *transition)
+{
+ StAdjustmentPrivate *priv;
+ TransitionClosure *clos;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->transitions == NULL)
+ priv->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ transition_closure_free);
+
+ if (g_hash_table_lookup (priv->transitions, name) != NULL)
+ {
+ g_warning ("A transition with name '%s' already exists for "
+ "adjustment '%p'", name, adjustment);
+ return;
+ }
+
+ clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (adjustment));
+
+ clos = g_new (TransitionClosure, 1);
+ clos->adjustment = adjustment;
+ clos->transition = g_object_ref (transition);
+ clos->name = g_strdup (name);
+ clos->completed_id = g_signal_connect (transition, "stopped",
+ G_CALLBACK (on_transition_stopped),
+ clos);
+
+ g_hash_table_insert (priv->transitions, clos->name, clos);
+ clutter_timeline_start (CLUTTER_TIMELINE (transition));
+}
+
+/**
+ * st_adjusmtent_remove_transition:
+ * @adjustment: a #StAdjustment
+ * @name: the name of the transition to remove
+ *
+ * Remove a #ClutterTransition previously added by st_adjustment_add_transtion()
+ * with @name.
+ */
+void
+st_adjustment_remove_transition (StAdjustment *adjustment,
+ const char *name)
+{
+ StAdjustmentPrivate *priv;
+ TransitionClosure *clos;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+ g_return_if_fail (name != NULL);
+
+ priv = st_adjustment_get_instance_private (adjustment);
+
+ if (priv->transitions == NULL)
+ return;
+
+ clos = g_hash_table_lookup (priv->transitions, name);
+ if (clos == NULL)
+ return;
+
+ remove_transition (adjustment, name);
+}
diff --git a/src/st/st-adjustment.h b/src/st/st-adjustment.h
new file mode 100644
index 0000000..08a0fc3
--- /dev/null
+++ b/src/st/st-adjustment.h
@@ -0,0 +1,92 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-adjustment.h: Adjustment object
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_ADJUSTMENT_H__
+#define __ST_ADJUSTMENT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_ADJUSTMENT (st_adjustment_get_type())
+G_DECLARE_DERIVABLE_TYPE (StAdjustment, st_adjustment, ST, ADJUSTMENT, GObject)
+
+/**
+ * StAdjustmentClass:
+ * @changed: Class handler for the ::changed signal.
+ *
+ * Base class for #StAdjustment.
+ */
+struct _StAdjustmentClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ void (* changed) (StAdjustment *adjustment);
+};
+
+StAdjustment *st_adjustment_new (ClutterActor *actor,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+gdouble st_adjustment_get_value (StAdjustment *adjustment);
+void st_adjustment_set_value (StAdjustment *adjustment,
+ gdouble value);
+void st_adjustment_clamp_page (StAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper);
+void st_adjustment_set_values (StAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+void st_adjustment_get_values (StAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size);
+
+void st_adjustment_adjust_for_scroll_event (StAdjustment *adjustment,
+ gdouble delta);
+
+ClutterTransition * st_adjustment_get_transition (StAdjustment *adjustment,
+ const char *name);
+void st_adjustment_add_transition (StAdjustment *adjustment,
+ const char *name,
+ ClutterTransition *transition);
+void st_adjustment_remove_transition (StAdjustment *adjustment,
+ const char *name);
+
+G_END_DECLS
+
+#endif /* __ST_ADJUSTMENT_H__ */
diff --git a/src/st/st-bin.c b/src/st/st-bin.c
new file mode 100644
index 0000000..9d86ea2
--- /dev/null
+++ b/src/st/st-bin.c
@@ -0,0 +1,404 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-bin.c: Basic container actor
+ *
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-bin
+ * @short_description: a simple container with one actor
+ *
+ * #StBin is a simple container capable of having only one
+ * #ClutterActor as a child.
+ *
+ * #StBin inherits from #StWidget, so it is fully themable.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "st-bin.h"
+#include "st-enum-types.h"
+#include "st-private.h"
+
+typedef struct _StBinPrivate StBinPrivate;
+struct _StBinPrivate
+{
+ ClutterActor *child;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_CHILD,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (StBin, st_bin, ST_TYPE_WIDGET,
+ G_ADD_PRIVATE (StBin)
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init));
+
+static void
+st_bin_add (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ st_bin_set_child (ST_BIN (container), actor);
+}
+
+static void
+st_bin_remove (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ StBin *bin = ST_BIN (container);
+ StBinPrivate *priv = st_bin_get_instance_private (bin);
+
+ if (priv->child == actor)
+ st_bin_set_child (bin, NULL);
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ iface->add = st_bin_add;
+ iface->remove = st_bin_remove;
+}
+
+static double
+get_align_factor (ClutterActorAlign align)
+{
+ switch (align)
+ {
+ case CLUTTER_ACTOR_ALIGN_CENTER:
+ return 0.5;
+
+ case CLUTTER_ACTOR_ALIGN_START:
+ return 0.0;
+
+ case CLUTTER_ACTOR_ALIGN_END:
+ return 1.0;
+
+ case CLUTTER_ACTOR_ALIGN_FILL:
+ break;
+ }
+
+ return 0.0;
+}
+
+static void
+st_bin_allocate (ClutterActor *self,
+ const ClutterActorBox *box)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (self));
+
+ clutter_actor_set_allocation (self, box);
+
+ if (priv->child && clutter_actor_is_visible (priv->child))
+ {
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ ClutterActorAlign x_align = clutter_actor_get_x_align (priv->child);
+ ClutterActorAlign y_align = clutter_actor_get_y_align (priv->child);
+ ClutterActorBox childbox;
+
+ st_theme_node_get_content_box (theme_node, box, &childbox);
+ clutter_actor_allocate_align_fill (priv->child, &childbox,
+ get_align_factor (x_align),
+ get_align_factor (y_align),
+ x_align == CLUTTER_ACTOR_ALIGN_FILL,
+ y_align == CLUTTER_ACTOR_ALIGN_FILL);
+ }
+}
+
+static void
+st_bin_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (self));
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+
+ st_theme_node_adjust_for_height (theme_node, &for_height);
+
+ if (priv->child == NULL || !clutter_actor_is_visible (priv->child))
+ {
+ if (min_width_p)
+ *min_width_p = 0;
+
+ if (natural_width_p)
+ *natural_width_p = 0;
+ }
+ else
+ {
+ ClutterActorAlign y_align = clutter_actor_get_y_align (priv->child);
+
+ _st_actor_get_preferred_width (priv->child, for_height,
+ y_align == CLUTTER_ACTOR_ALIGN_FILL,
+ min_width_p,
+ natural_width_p);
+ }
+
+ st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
+}
+
+static void
+st_bin_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (self));
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ if (priv->child == NULL || !clutter_actor_is_visible (priv->child))
+ {
+ if (min_height_p)
+ *min_height_p = 0;
+
+ if (natural_height_p)
+ *natural_height_p = 0;
+ }
+ else
+ {
+ ClutterActorAlign x_align = clutter_actor_get_y_align (priv->child);
+
+ _st_actor_get_preferred_height (priv->child, for_width,
+ x_align == CLUTTER_ACTOR_ALIGN_FILL,
+ min_height_p,
+ natural_height_p);
+ }
+
+ st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
+}
+
+static void
+st_bin_destroy (ClutterActor *actor)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (actor));
+
+ if (priv->child)
+ clutter_actor_destroy (priv->child);
+ g_assert (priv->child == NULL);
+
+ CLUTTER_ACTOR_CLASS (st_bin_parent_class)->destroy (actor);
+}
+
+static void
+st_bin_popup_menu (StWidget *widget)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (widget));
+
+ if (priv->child && ST_IS_WIDGET (priv->child))
+ st_widget_popup_menu (ST_WIDGET (priv->child));
+}
+
+static gboolean
+st_bin_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (widget));
+ ClutterActor *bin_actor = CLUTTER_ACTOR (widget);
+
+ if (st_widget_get_can_focus (widget))
+ {
+ if (from && clutter_actor_contains (bin_actor, from))
+ return FALSE;
+
+ if (clutter_actor_is_mapped (bin_actor))
+ {
+ clutter_actor_grab_key_focus (bin_actor);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ else if (priv->child && ST_IS_WIDGET (priv->child))
+ return st_widget_navigate_focus (ST_WIDGET (priv->child), from, direction, FALSE);
+ else
+ return FALSE;
+}
+
+static void
+st_bin_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StBin *bin = ST_BIN (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD:
+ st_bin_set_child (bin, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+st_bin_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (gobject));
+
+ switch (prop_id)
+ {
+ case PROP_CHILD:
+ g_value_set_object (value, priv->child);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+st_bin_class_init (StBinClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ gobject_class->set_property = st_bin_set_property;
+ gobject_class->get_property = st_bin_get_property;
+
+ actor_class->get_preferred_width = st_bin_get_preferred_width;
+ actor_class->get_preferred_height = st_bin_get_preferred_height;
+ actor_class->allocate = st_bin_allocate;
+ actor_class->destroy = st_bin_destroy;
+
+ widget_class->popup_menu = st_bin_popup_menu;
+ widget_class->navigate_focus = st_bin_navigate_focus;
+
+ /**
+ * StBin:child:
+ *
+ * The child #ClutterActor of the #StBin container.
+ */
+ props[PROP_CHILD] =
+ g_param_spec_object ("child",
+ "Child",
+ "The child of the Bin",
+ CLUTTER_TYPE_ACTOR,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+}
+
+static void
+st_bin_init (StBin *bin)
+{
+}
+
+/**
+ * st_bin_new:
+ *
+ * Creates a new #StBin, a simple container for one child.
+ *
+ * Returns: the newly created #StBin actor
+ */
+StWidget *
+st_bin_new (void)
+{
+ return g_object_new (ST_TYPE_BIN, NULL);
+}
+
+/**
+ * st_bin_set_child:
+ * @bin: a #StBin
+ * @child: (nullable): a #ClutterActor, or %NULL
+ *
+ * Sets @child as the child of @bin.
+ *
+ * If @bin already has a child, the previous child is removed.
+ */
+void
+st_bin_set_child (StBin *bin,
+ ClutterActor *child)
+{
+ StBinPrivate *priv;
+
+ g_return_if_fail (ST_IS_BIN (bin));
+ g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
+
+ priv = st_bin_get_instance_private (bin);
+
+ if (priv->child == child)
+ return;
+
+ if (child)
+ {
+ ClutterActor *parent = clutter_actor_get_parent (child);
+
+ if (parent)
+ {
+ g_warning ("%s: The provided 'child' actor %p already has a "
+ "(different) parent %p and can't be made a child of %p.",
+ G_STRFUNC, child, parent, bin);
+ return;
+ }
+ }
+
+ if (priv->child)
+ clutter_actor_remove_child (CLUTTER_ACTOR (bin), priv->child);
+
+ priv->child = NULL;
+
+ if (child)
+ {
+ priv->child = child;
+ clutter_actor_add_child (CLUTTER_ACTOR (bin), child);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+ g_object_notify_by_pspec (G_OBJECT (bin), props[PROP_CHILD]);
+}
+
+/**
+ * st_bin_get_child:
+ * @bin: a #StBin
+ *
+ * Gets the #ClutterActor child for @bin.
+ *
+ * Returns: (transfer none) (nullable): a #ClutterActor, or %NULL
+ */
+ClutterActor *
+st_bin_get_child (StBin *bin)
+{
+ g_return_val_if_fail (ST_IS_BIN (bin), NULL);
+
+ return ((StBinPrivate *)st_bin_get_instance_private (bin))->child;
+}
diff --git a/src/st/st-bin.h b/src/st/st-bin.h
new file mode 100644
index 0000000..7784f45
--- /dev/null
+++ b/src/st/st-bin.h
@@ -0,0 +1,53 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-bin.h: Basic container actor
+ *
+ * Copyright 2009, 2008 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_BIN_H__
+#define __ST_BIN_H__
+
+#include <st/st-types.h>
+#include <st/st-widget.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_BIN (st_bin_get_type ())
+G_DECLARE_DERIVABLE_TYPE (StBin, st_bin, ST, BIN, StWidget)
+
+/**
+ * StBinClass:
+ *
+ * The #StBinClass struct contains only private data
+ */
+struct _StBinClass
+{
+ /*< private >*/
+ StWidgetClass parent_class;
+};
+
+StWidget * st_bin_new (void);
+void st_bin_set_child (StBin *bin,
+ ClutterActor *child);
+ClutterActor *st_bin_get_child (StBin *bin);
+
+G_END_DECLS
+
+#endif /* __ST_BIN_H__ */
diff --git a/src/st/st-border-image.c b/src/st/st-border-image.c
new file mode 100644
index 0000000..ee09d02
--- /dev/null
+++ b/src/st/st-border-image.c
@@ -0,0 +1,171 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-border-image.c: store information about an image with borders
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "st-border-image.h"
+
+struct _StBorderImage {
+ GObject parent;
+
+ GFile *file;
+ int border_top;
+ int border_right;
+ int border_bottom;
+ int border_left;
+
+ int scale_factor;
+};
+
+struct _StBorderImageClass {
+ GObjectClass parent_class;
+
+};
+
+G_DEFINE_TYPE (StBorderImage, st_border_image, G_TYPE_OBJECT)
+
+static void
+st_border_image_finalize (GObject *object)
+{
+ StBorderImage *image = ST_BORDER_IMAGE (object);
+
+ g_object_unref (image->file);
+
+ G_OBJECT_CLASS (st_border_image_parent_class)->finalize (object);
+}
+
+static void
+st_border_image_class_init (StBorderImageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = st_border_image_finalize;
+}
+
+static void
+st_border_image_init (StBorderImage *image)
+{
+}
+
+/**
+ * st_border_image_new:
+ * @file: a #GFile
+ * @border_top: the top border
+ * @border_right: the right border
+ * @border_bottom: the bottom border
+ * @border_left: the left border
+ * @scale_factor: the scale factor
+ *
+ * Creates a new #StBorderImage.
+ *
+ * Returns: a new #StBorderImage.
+ */
+StBorderImage *
+st_border_image_new (GFile *file,
+ int border_top,
+ int border_right,
+ int border_bottom,
+ int border_left,
+ int scale_factor)
+{
+ StBorderImage *image;
+
+ image = g_object_new (ST_TYPE_BORDER_IMAGE, NULL);
+
+ image->file = g_object_ref (file);
+ image->border_top = border_top;
+ image->border_right = border_right;
+ image->border_bottom = border_bottom;
+ image->border_left = border_left;
+ image->scale_factor = scale_factor;
+
+ return image;
+}
+
+/**
+ * st_border_image_get_file:
+ * @image: a #StBorderImage
+ *
+ * Get the #GFile for @image.
+ *
+ * Returns: (transfer none): a #GFile
+ */
+GFile *
+st_border_image_get_file (StBorderImage *image)
+{
+ g_return_val_if_fail (ST_IS_BORDER_IMAGE (image), NULL);
+
+ return image->file;
+}
+
+/**
+ * st_border_image_get_border:
+ * @image: a #StBorderImage
+ * @border_top: (out) (optional): the top border
+ * @border_right: (out) (optional): the right border
+ * @border_bottom: (out) (optional): the bottom border
+ * @border_left: (out) (optional): the left border
+ *
+ * Get the border widths for @image, taking into account the scale factor
+ * provided at construction.
+ */
+void
+st_border_image_get_borders (StBorderImage *image,
+ int *border_top,
+ int *border_right,
+ int *border_bottom,
+ int *border_left)
+{
+ g_return_if_fail (ST_IS_BORDER_IMAGE (image));
+
+ if (border_top)
+ *border_top = image->border_top * image->scale_factor;
+ if (border_right)
+ *border_right = image->border_right * image->scale_factor;
+ if (border_bottom)
+ *border_bottom = image->border_bottom * image->scale_factor;
+ if (border_left)
+ *border_left = image->border_left * image->scale_factor;
+}
+
+/**
+ * st_border_image_equal:
+ * @image: a #StBorderImage
+ * @other: a different #StBorderImage
+ *
+ * Check if two #StBorderImage objects are identical.
+ *
+ * Returns: %TRUE if the two border image objects are identical
+ */
+gboolean
+st_border_image_equal (StBorderImage *image,
+ StBorderImage *other)
+{
+ g_return_val_if_fail (ST_IS_BORDER_IMAGE (image), FALSE);
+ g_return_val_if_fail (ST_IS_BORDER_IMAGE (other), FALSE);
+
+ return (image->border_top == other->border_top &&
+ image->border_right == other->border_right &&
+ image->border_bottom == other->border_bottom &&
+ image->border_left == other->border_left &&
+ g_file_equal (image->file, other->file));
+}
diff --git a/src/st/st-border-image.h b/src/st/st-border-image.h
new file mode 100644
index 0000000..9c58152
--- /dev/null
+++ b/src/st/st-border-image.h
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-border-image.h: store information about an image with borders
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_BORDER_IMAGE_H__
+#define __ST_BORDER_IMAGE_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+/* A StBorderImage encapsulates an image with specified unscaled borders on each edge.
+ */
+
+#define ST_TYPE_BORDER_IMAGE (st_border_image_get_type ())
+G_DECLARE_FINAL_TYPE (StBorderImage, st_border_image, ST, BORDER_IMAGE, GObject)
+
+StBorderImage *st_border_image_new (GFile *file,
+ int border_top,
+ int border_right,
+ int border_bottom,
+ int border_left,
+ int scale_factor);
+
+GFile *st_border_image_get_file (StBorderImage *image);
+void st_border_image_get_borders (StBorderImage *image,
+ int *border_top,
+ int *border_right,
+ int *border_bottom,
+ int *border_left);
+
+gboolean st_border_image_equal (StBorderImage *image,
+ StBorderImage *other);
+
+G_END_DECLS
+
+#endif /* __ST_BORDER_IMAGE_H__ */
diff --git a/src/st/st-box-layout.c b/src/st/st-box-layout.c
new file mode 100644
index 0000000..990fb9e
--- /dev/null
+++ b/src/st/st-box-layout.c
@@ -0,0 +1,307 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-box-layout.h: box layout actor
+ *
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2009 Abderrahim Kitouni
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010 Florian Muellner
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Portions copied from Clutter:
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2006 OpenedHand
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ */
+
+/**
+ * SECTION:st-box-layout
+ * @short_description: a layout container arranging children in a single line
+ *
+ * The #StBoxLayout arranges its children along a single line, where each
+ * child can be allocated either its preferred size or larger if the expand
+ * option is set. If the fill option is set, the actor will be allocated more
+ * than its requested size. If the fill option is not set, but the expand option
+ * is enabled, then the position of the actor within the available space can
+ * be determined by the alignment child property.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "st-box-layout.h"
+
+#include "st-private.h"
+#include "st-scrollable.h"
+
+
+enum {
+ PROP_0,
+
+ PROP_VERTICAL,
+ PROP_PACK_START,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+struct _StBoxLayoutPrivate
+{
+ StAdjustment *hadjustment;
+ StAdjustment *vadjustment;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (StBoxLayout, st_box_layout, ST_TYPE_VIEWPORT);
+
+
+static void
+st_box_layout_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterLayoutManager *layout;
+ ClutterOrientation orientation;
+
+ switch (property_id)
+ {
+ case PROP_VERTICAL:
+ layout = clutter_actor_get_layout_manager (CLUTTER_ACTOR (object));
+ orientation = clutter_box_layout_get_orientation (CLUTTER_BOX_LAYOUT (layout));
+ g_value_set_boolean (value, orientation == CLUTTER_ORIENTATION_VERTICAL);
+ break;
+
+ case PROP_PACK_START:
+ g_value_set_boolean (value, FALSE);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+st_box_layout_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StBoxLayout *box = ST_BOX_LAYOUT (object);
+
+ switch (property_id)
+ {
+ case PROP_VERTICAL:
+ st_box_layout_set_vertical (box, g_value_get_boolean (value));
+ break;
+
+ case PROP_PACK_START:
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+st_box_layout_style_changed (StWidget *self)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (self);
+ ClutterBoxLayout *layout;
+ double spacing;
+
+ layout = CLUTTER_BOX_LAYOUT (clutter_actor_get_layout_manager (CLUTTER_ACTOR (self)));
+
+ spacing = st_theme_node_get_length (theme_node, "spacing");
+ clutter_box_layout_set_spacing (layout, (int)(spacing + 0.5));
+
+ ST_WIDGET_CLASS (st_box_layout_parent_class)->style_changed (self);
+}
+
+static void
+layout_notify (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GObject *self = user_data;
+ const char *prop_name = g_param_spec_get_name (pspec);
+
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (self), prop_name))
+ g_object_notify (self, prop_name);
+}
+
+static void
+on_layout_manager_notify (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ ClutterActor *actor = CLUTTER_ACTOR (object);
+ ClutterLayoutManager *layout = clutter_actor_get_layout_manager (actor);
+
+ g_warn_if_fail (CLUTTER_IS_BOX_LAYOUT (layout));
+
+ if (layout == NULL)
+ return;
+
+ g_signal_connect_swapped (layout, "layout-changed",
+ G_CALLBACK (clutter_actor_queue_relayout), actor);
+ g_signal_connect (layout, "notify", G_CALLBACK (layout_notify), object);
+}
+
+static void
+st_box_layout_class_init (StBoxLayoutClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ object_class->get_property = st_box_layout_get_property;
+ object_class->set_property = st_box_layout_set_property;
+
+ widget_class->style_changed = st_box_layout_style_changed;
+
+ /**
+ * StBoxLayout:vertical:
+ *
+ * A convenience property for the #ClutterBoxLayout:vertical property of the
+ * internal layout for #StBoxLayout.
+ */
+ props[PROP_VERTICAL] =
+ g_param_spec_boolean ("vertical",
+ "Vertical",
+ "Whether the layout should be vertical, rather"
+ "than horizontal",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StBoxLayout:pack-start:
+ *
+ * A convenience property for the #ClutterBoxLayout:pack-start property of the
+ * internal layout for #StBoxLayout.
+ */
+ props[PROP_PACK_START] =
+ g_param_spec_boolean ("pack-start",
+ "Pack Start",
+ "Whether to pack items at the start of the box",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_DEPRECATED);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+st_box_layout_init (StBoxLayout *self)
+{
+ self->priv = st_box_layout_get_instance_private (self);
+
+ g_signal_connect (self, "notify::layout-manager",
+ G_CALLBACK (on_layout_manager_notify), NULL);
+ clutter_actor_set_layout_manager (CLUTTER_ACTOR (self), clutter_box_layout_new ());
+}
+
+/**
+ * st_box_layout_new:
+ *
+ * Create a new #StBoxLayout.
+ *
+ * Returns: a newly allocated #StBoxLayout
+ */
+StWidget *
+st_box_layout_new (void)
+{
+ return g_object_new (ST_TYPE_BOX_LAYOUT, NULL);
+}
+
+/**
+ * st_box_layout_set_vertical:
+ * @box: A #StBoxLayout
+ * @vertical: %TRUE if the layout should be vertical
+ *
+ * Set the value of the #StBoxLayout:vertical property
+ */
+void
+st_box_layout_set_vertical (StBoxLayout *box,
+ gboolean vertical)
+{
+ ClutterLayoutManager *layout;
+ ClutterOrientation orientation;
+
+ g_return_if_fail (ST_IS_BOX_LAYOUT (box));
+
+ layout = clutter_actor_get_layout_manager (CLUTTER_ACTOR (box));
+ orientation = vertical ? CLUTTER_ORIENTATION_VERTICAL
+ : CLUTTER_ORIENTATION_HORIZONTAL;
+
+ if (clutter_box_layout_get_orientation (CLUTTER_BOX_LAYOUT (layout)) != orientation)
+ {
+ clutter_box_layout_set_orientation (CLUTTER_BOX_LAYOUT (layout), orientation);
+ g_object_notify_by_pspec (G_OBJECT (box), props[PROP_VERTICAL]);
+ }
+}
+
+/**
+ * st_box_layout_get_vertical:
+ * @box: A #StBoxLayout
+ *
+ * Get the value of the #StBoxLayout:vertical property.
+ *
+ * Returns: %TRUE if the layout is vertical
+ */
+gboolean
+st_box_layout_get_vertical (StBoxLayout *box)
+{
+ ClutterLayoutManager *layout;
+ ClutterOrientation orientation;
+
+ g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE);
+
+ layout = clutter_actor_get_layout_manager (CLUTTER_ACTOR (box));
+ orientation = clutter_box_layout_get_orientation (CLUTTER_BOX_LAYOUT (layout));
+ return orientation == CLUTTER_ORIENTATION_VERTICAL;
+}
+
+/**
+ * st_box_layout_set_pack_start:
+ * @box: A #StBoxLayout
+ * @pack_start: %TRUE if the layout should use pack-start
+ *
+ * Deprecated: No longer has any effect
+ */
+void
+st_box_layout_set_pack_start (StBoxLayout *box,
+ gboolean pack_start)
+{
+}
+
+/**
+ * st_box_layout_get_pack_start:
+ * @box: A #StBoxLayout
+ *
+ * Returns: the value of the #StBoxLayout:pack-start property,
+ * always %FALSE
+ */
+gboolean
+st_box_layout_get_pack_start (StBoxLayout *box)
+{
+ return FALSE;
+}
diff --git a/src/st/st-box-layout.h b/src/st/st-box-layout.h
new file mode 100644
index 0000000..82f5a70
--- /dev/null
+++ b/src/st/st-box-layout.h
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-box-layout.h: box layout actor
+ *
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef _ST_BOX_LAYOUT_H
+#define _ST_BOX_LAYOUT_H
+
+#include <st/st-widget.h>
+#include <st/st-viewport.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_BOX_LAYOUT st_box_layout_get_type()
+G_DECLARE_FINAL_TYPE (StBoxLayout, st_box_layout, ST, BOX_LAYOUT, StViewport)
+
+typedef struct _StBoxLayout StBoxLayout;
+typedef struct _StBoxLayoutPrivate StBoxLayoutPrivate;
+
+/**
+ * StBoxLayout:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _StBoxLayout
+{
+ /*< private >*/
+ StViewport parent;
+
+ StBoxLayoutPrivate *priv;
+};
+
+StWidget *st_box_layout_new (void);
+
+void st_box_layout_set_vertical (StBoxLayout *box,
+ gboolean vertical);
+gboolean st_box_layout_get_vertical (StBoxLayout *box);
+
+void st_box_layout_set_pack_start (StBoxLayout *box,
+ gboolean pack_start);
+gboolean st_box_layout_get_pack_start (StBoxLayout *box);
+
+G_END_DECLS
+
+#endif /* _ST_BOX_LAYOUT_H */
diff --git a/src/st/st-button.c b/src/st/st-button.c
new file mode 100644
index 0000000..148197c
--- /dev/null
+++ b/src/st/st-button.c
@@ -0,0 +1,1043 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-button.c: Plain button actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-button
+ * @short_description: Button widget
+ *
+ * A button widget with support for either a text label or icon, toggle mode
+ * and transitions effects between states.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "st-button.h"
+
+#include "st-icon.h"
+#include "st-enum-types.h"
+#include "st-texture-cache.h"
+#include "st-private.h"
+
+#include <st/st-widget-accessible.h>
+
+enum
+{
+ PROP_0,
+
+ PROP_LABEL,
+ PROP_ICON_NAME,
+ PROP_BUTTON_MASK,
+ PROP_TOGGLE_MODE,
+ PROP_CHECKED,
+ PROP_PRESSED,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum
+{
+ CLICKED,
+
+ LAST_SIGNAL
+};
+
+typedef struct _StButtonPrivate StButtonPrivate;
+
+struct _StButtonPrivate
+{
+ gchar *text;
+
+ ClutterInputDevice *device;
+ ClutterEventSequence *press_sequence;
+ ClutterGrab *grab;
+
+ guint button_mask : 3;
+ guint is_toggle : 1;
+
+ guint pressed : 3;
+ guint grabbed : 3;
+ guint is_checked : 1;
+};
+
+static guint button_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (StButton, st_button, ST_TYPE_BIN);
+
+static GType st_button_accessible_get_type (void) G_GNUC_CONST;
+
+static void
+st_button_update_label_style (StButton *button)
+{
+ ClutterActor *label;
+
+ label = st_bin_get_child (ST_BIN (button));
+
+ /* check the child is really a label */
+ if (!CLUTTER_IS_TEXT (label))
+ return;
+
+ _st_set_text_from_style (CLUTTER_TEXT (label), st_widget_get_theme_node (ST_WIDGET (button)));
+}
+
+static void
+st_button_style_changed (StWidget *widget)
+{
+ StButton *button = ST_BUTTON (widget);
+ StButtonClass *button_class = ST_BUTTON_GET_CLASS (button);
+
+ ST_WIDGET_CLASS (st_button_parent_class)->style_changed (widget);
+
+ /* update the label styling */
+ st_button_update_label_style (button);
+
+ /* run a transition if applicable */
+ if (button_class->transition)
+ {
+ button_class->transition (button);
+ }
+}
+
+static void
+st_button_press (StButton *button,
+ ClutterInputDevice *device,
+ StButtonMask mask,
+ ClutterEventSequence *sequence)
+{
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+ gboolean active_changed = priv->pressed == 0 || sequence;
+
+ if (active_changed)
+ st_widget_add_style_pseudo_class (ST_WIDGET (button), "active");
+
+ priv->pressed |= mask;
+ priv->press_sequence = sequence;
+ priv->device = device;
+
+ if (active_changed)
+ g_object_notify_by_pspec (G_OBJECT (button), props[PROP_PRESSED]);
+}
+
+static void
+st_button_release (StButton *button,
+ ClutterInputDevice *device,
+ StButtonMask mask,
+ int clicked_button,
+ ClutterEventSequence *sequence)
+{
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+
+ if ((device && priv->device != device) ||
+ (sequence && priv->press_sequence != sequence))
+ return;
+ else if (!sequence)
+ {
+ priv->pressed &= ~mask;
+
+ if (priv->pressed != 0)
+ return;
+ }
+
+ priv->press_sequence = NULL;
+ priv->device = NULL;
+ st_widget_remove_style_pseudo_class (ST_WIDGET (button), "active");
+ g_object_notify_by_pspec (G_OBJECT (button), props[PROP_PRESSED]);
+
+ if (clicked_button || sequence)
+ {
+ if (priv->is_toggle)
+ st_button_set_checked (button, !priv->is_checked);
+
+ g_signal_emit (button, button_signals[CLICKED], 0, clicked_button);
+ }
+}
+
+static gboolean
+st_button_button_press (ClutterActor *actor,
+ ClutterButtonEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+ StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button);
+ ClutterInputDevice *device = clutter_event_get_device ((ClutterEvent*) event);
+
+ if (priv->press_sequence)
+ return CLUTTER_EVENT_PROPAGATE;
+
+ if (priv->button_mask & mask)
+ {
+ ClutterActor *stage;
+
+ stage = clutter_actor_get_stage (actor);
+
+ if (priv->grabbed == 0)
+ priv->grab = clutter_stage_grab (CLUTTER_STAGE (stage), actor);
+
+ priv->grabbed |= mask;
+ st_button_press (button, device, mask, NULL);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_button_button_release (ClutterActor *actor,
+ ClutterButtonEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+ StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button);
+ ClutterInputDevice *device = clutter_event_get_device ((ClutterEvent*) event);
+
+ if (priv->button_mask & mask)
+ {
+ ClutterStage *stage;
+ ClutterActor *target;
+ gboolean is_click;
+
+ stage = clutter_event_get_stage ((ClutterEvent *) event);
+ target = clutter_stage_get_event_actor (stage, (ClutterEvent *) event);
+
+ is_click = priv->grabbed && clutter_actor_contains (actor, target);
+ st_button_release (button, device, mask, is_click ? event->button : 0, NULL);
+
+ priv->grabbed &= ~mask;
+ if (priv->grab && priv->grabbed == 0)
+ {
+ clutter_grab_dismiss (priv->grab);
+ g_clear_pointer (&priv->grab, clutter_grab_unref);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_button_touch_event (ClutterActor *actor,
+ ClutterTouchEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+ StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (1);
+ ClutterEventSequence *sequence;
+ ClutterInputDevice *device;
+
+ if (priv->pressed != 0)
+ return CLUTTER_EVENT_PROPAGATE;
+ if ((priv->button_mask & mask) == 0)
+ return CLUTTER_EVENT_PROPAGATE;
+
+ device = clutter_event_get_device ((ClutterEvent*) event);
+ sequence = clutter_event_get_event_sequence ((ClutterEvent*) event);
+
+ if (event->type == CLUTTER_TOUCH_BEGIN && !priv->grab && !priv->press_sequence)
+ {
+ st_button_press (button, device, 0, sequence);
+ return CLUTTER_EVENT_STOP;
+ }
+ else if (event->type == CLUTTER_TOUCH_END &&
+ priv->device == device &&
+ priv->press_sequence == sequence)
+ {
+ st_button_release (button, device, mask, 0, sequence);
+ return CLUTTER_EVENT_STOP;
+ }
+ else if (event->type == CLUTTER_TOUCH_CANCEL)
+ {
+ st_button_fake_release (button);
+ }
+
+ return CLUTTER_EVENT_PROPAGATE;
+}
+
+static gboolean
+st_button_key_press (ClutterActor *actor,
+ ClutterKeyEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+
+ if (priv->button_mask & ST_BUTTON_ONE)
+ {
+ if (event->keyval == CLUTTER_KEY_space ||
+ event->keyval == CLUTTER_KEY_Return ||
+ event->keyval == CLUTTER_KEY_KP_Enter ||
+ event->keyval == CLUTTER_KEY_ISO_Enter)
+ {
+ st_button_press (button, NULL, ST_BUTTON_ONE, NULL);
+ return TRUE;
+ }
+ }
+
+ return CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_press_event (actor, event);
+}
+
+static gboolean
+st_button_key_release (ClutterActor *actor,
+ ClutterKeyEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+
+ if (priv->button_mask & ST_BUTTON_ONE)
+ {
+ if (event->keyval == CLUTTER_KEY_space ||
+ event->keyval == CLUTTER_KEY_Return ||
+ event->keyval == CLUTTER_KEY_KP_Enter ||
+ event->keyval == CLUTTER_KEY_ISO_Enter)
+ {
+ gboolean is_click;
+
+ is_click = (priv->pressed & ST_BUTTON_ONE);
+ st_button_release (button, NULL, ST_BUTTON_ONE, is_click ? 1 : 0, NULL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+st_button_key_focus_out (ClutterActor *actor)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+
+ /* If we lose focus between a key press and release, undo the press */
+ if ((priv->pressed & ST_BUTTON_ONE) &&
+ !(priv->grabbed & ST_BUTTON_ONE))
+ st_button_release (button, NULL, ST_BUTTON_ONE, 0, NULL);
+
+ CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_focus_out (actor);
+}
+
+static gboolean
+st_button_enter (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+ gboolean ret;
+
+ ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event);
+
+ if (priv->grabbed)
+ {
+ if (st_widget_get_hover (ST_WIDGET (button)))
+ st_button_press (button, priv->device,
+ priv->grabbed, NULL);
+ else
+ st_button_release (button, priv->device,
+ priv->grabbed, 0, NULL);
+ }
+
+ return ret;
+}
+
+static gboolean
+st_button_leave (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+ gboolean ret;
+
+ ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event);
+
+ if (priv->grabbed)
+ {
+ if (st_widget_get_hover (ST_WIDGET (button)))
+ st_button_press (button, priv->device,
+ priv->grabbed, NULL);
+ else
+ st_button_release (button, priv->device,
+ priv->grabbed, 0, NULL);
+ }
+
+ return ret;
+}
+
+static void
+st_button_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StButton *button = ST_BUTTON (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ st_button_set_label (button, g_value_get_string (value));
+ break;
+ case PROP_ICON_NAME:
+ st_button_set_icon_name (button, g_value_get_string (value));
+ break;
+ case PROP_BUTTON_MASK:
+ st_button_set_button_mask (button, g_value_get_flags (value));
+ break;
+ case PROP_TOGGLE_MODE:
+ st_button_set_toggle_mode (button, g_value_get_boolean (value));
+ break;
+ case PROP_CHECKED:
+ st_button_set_checked (button, g_value_get_boolean (value));
+ break;
+
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_button_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StButtonPrivate *priv = st_button_get_instance_private (ST_BUTTON (gobject));
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ g_value_set_string (value, priv->text);
+ break;
+ case PROP_ICON_NAME:
+ g_value_set_string (value, st_button_get_icon_name (ST_BUTTON (gobject)));
+ break;
+ case PROP_BUTTON_MASK:
+ g_value_set_flags (value, priv->button_mask);
+ break;
+ case PROP_TOGGLE_MODE:
+ g_value_set_boolean (value, priv->is_toggle);
+ break;
+ case PROP_CHECKED:
+ g_value_set_boolean (value, priv->is_checked);
+ break;
+ case PROP_PRESSED:
+ g_value_set_boolean (value, priv->pressed != 0 || priv->press_sequence != NULL);
+ break;
+
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_button_finalize (GObject *gobject)
+{
+ StButtonPrivate *priv = st_button_get_instance_private (ST_BUTTON (gobject));
+
+ g_free (priv->text);
+
+ G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject);
+}
+
+static void
+st_button_class_init (StButtonClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ gobject_class->set_property = st_button_set_property;
+ gobject_class->get_property = st_button_get_property;
+ gobject_class->finalize = st_button_finalize;
+
+ actor_class->button_press_event = st_button_button_press;
+ actor_class->button_release_event = st_button_button_release;
+ actor_class->key_press_event = st_button_key_press;
+ actor_class->key_release_event = st_button_key_release;
+ actor_class->key_focus_out = st_button_key_focus_out;
+ actor_class->enter_event = st_button_enter;
+ actor_class->leave_event = st_button_leave;
+ actor_class->touch_event = st_button_touch_event;
+
+ widget_class->style_changed = st_button_style_changed;
+ widget_class->get_accessible_type = st_button_accessible_get_type;
+
+ /**
+ * StButton:label:
+ *
+ * The label of the #StButton.
+ */
+ props[PROP_LABEL] =
+ g_param_spec_string ("label",
+ "Label",
+ "Label of the button",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StButton:icon-name:
+ *
+ * The icon name of the #StButton.
+ */
+ props[PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name",
+ "Icon name",
+ "Icon name of the button",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StButton:button-mask:
+ *
+ * Which buttons will trigger the #StButton::clicked signal.
+ */
+ props[PROP_BUTTON_MASK] =
+ g_param_spec_flags ("button-mask",
+ "Button mask",
+ "Which buttons trigger the 'clicked' signal",
+ ST_TYPE_BUTTON_MASK, ST_BUTTON_ONE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StButton:toggle-mode:
+ *
+ * Whether the #StButton is operating in toggle mode (on/off).
+ */
+ props[PROP_TOGGLE_MODE] =
+ g_param_spec_boolean ("toggle-mode",
+ "Toggle Mode",
+ "Enable or disable toggling",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StButton:checked:
+ *
+ * If #StButton:toggle-mode is %TRUE, indicates if the #StButton is toggled
+ * "on" or "off".
+ *
+ * When the value is %TRUE, the #StButton will have the `checked` CSS
+ * pseudo-class set.
+ */
+ props[PROP_CHECKED] =
+ g_param_spec_boolean ("checked",
+ "Checked",
+ "Indicates if a toggle button is \"on\" or \"off\"",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StButton:pressed:
+ *
+ * In contrast to #StButton:checked, this property indicates whether the
+ * #StButton is being actively pressed, rather than just in the "on" state.
+ */
+ props[PROP_PRESSED] =
+ g_param_spec_boolean ("pressed",
+ "Pressed",
+ "Indicates if the button is pressed in",
+ FALSE,
+ ST_PARAM_READABLE);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+
+
+ /**
+ * StButton::clicked:
+ * @button: the object that received the signal
+ * @clicked_button: the mouse button that was used
+ *
+ * Emitted when the user activates the button, either with a mouse press and
+ * release or with the keyboard.
+ */
+ button_signals[CLICKED] =
+ g_signal_new ("clicked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StButtonClass, clicked),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+}
+
+static void
+st_button_init (StButton *button)
+{
+ StButtonPrivate *priv = st_button_get_instance_private (button);
+
+ priv->button_mask = ST_BUTTON_ONE;
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (button), TRUE);
+ st_widget_set_track_hover (ST_WIDGET (button), TRUE);
+}
+
+/**
+ * st_button_new:
+ *
+ * Create a new button
+ *
+ * Returns: a new #StButton
+ */
+StWidget *
+st_button_new (void)
+{
+ return g_object_new (ST_TYPE_BUTTON, NULL);
+}
+
+/**
+ * st_button_new_with_label:
+ * @text: text to set the label to
+ *
+ * Create a new #StButton with the specified label
+ *
+ * Returns: a new #StButton
+ */
+StWidget *
+st_button_new_with_label (const gchar *text)
+{
+ return g_object_new (ST_TYPE_BUTTON, "label", text, NULL);
+}
+
+/**
+ * st_button_get_label:
+ * @button: a #StButton
+ *
+ * Get the text displayed on the button. If the label is empty, an empty string
+ * will be returned instead of %NULL.
+ *
+ * Returns: (transfer none): the text for the button
+ */
+const gchar *
+st_button_get_label (StButton *button)
+{
+ g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
+
+ return ((StButtonPrivate *)st_button_get_instance_private (button))->text;
+}
+
+/**
+ * st_button_set_label:
+ * @button: a #Stbutton
+ * @text: (nullable): text to set the label to
+ *
+ * Sets the text displayed on the button.
+ */
+void
+st_button_set_label (StButton *button,
+ const gchar *text)
+{
+ StButtonPrivate *priv;
+ ClutterActor *label;
+
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ priv = st_button_get_instance_private (button);
+
+ if (g_strcmp0 (priv->text, text) == 0)
+ return;
+
+ g_free (priv->text);
+
+ if (text)
+ priv->text = g_strdup (text);
+ else
+ priv->text = g_strdup ("");
+
+ label = st_bin_get_child (ST_BIN (button));
+
+ if (label && CLUTTER_IS_TEXT (label))
+ {
+ clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
+ }
+ else
+ {
+ label = g_object_new (CLUTTER_TYPE_TEXT,
+ "text", priv->text,
+ "line-alignment", PANGO_ALIGN_CENTER,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "use-markup", TRUE,
+ "x-align", CLUTTER_ACTOR_ALIGN_CENTER,
+ "y-align", CLUTTER_ACTOR_ALIGN_CENTER,
+ NULL);
+ st_bin_set_child (ST_BIN (button), label);
+ }
+
+ /* Fake a style change so that we reset the style properties on the label */
+ st_widget_style_changed (ST_WIDGET (button));
+
+ g_object_notify_by_pspec (G_OBJECT (button), props[PROP_LABEL]);
+}
+
+/**
+ * st_button_get_icon_name:
+ * @button: a #StButton
+ *
+ * Get the icon name of the button. If the button isn't showing an icon,
+ * the return value will be %NULL.
+ *
+ * Returns: (transfer none) (nullable): the icon name of the button
+ */
+const char *
+st_button_get_icon_name (StButton *button)
+{
+ ClutterActor *icon;
+
+ g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
+
+ icon = st_bin_get_child (ST_BIN (button));
+ if (ST_IS_ICON (icon))
+ return st_icon_get_icon_name (ST_ICON (icon));
+ return NULL;
+}
+
+/**
+ * st_button_set_icon_name:
+ * @button: a #Stbutton
+ * @icon_name: an icon name
+ *
+ * Adds an `StIcon` with the given icon name as a child.
+ *
+ * If @button already contains a child actor, that child will
+ * be removed and replaced with the icon.
+ */
+void
+st_button_set_icon_name (StButton *button,
+ const char *icon_name)
+{
+ ClutterActor *icon;
+
+ g_return_if_fail (ST_IS_BUTTON (button));
+ g_return_if_fail (icon_name != NULL);
+
+ icon = st_bin_get_child (ST_BIN (button));
+
+ if (ST_IS_ICON (icon))
+ {
+ if (g_strcmp0 (st_icon_get_icon_name (ST_ICON (icon)), icon_name) == 0)
+ return;
+
+ st_icon_set_icon_name (ST_ICON (icon), icon_name);
+ }
+ else
+ {
+ icon = g_object_new (ST_TYPE_ICON,
+ "icon-name", icon_name,
+ "x-align", CLUTTER_ACTOR_ALIGN_CENTER,
+ "y-align", CLUTTER_ACTOR_ALIGN_CENTER,
+ NULL);
+ st_bin_set_child (ST_BIN (button), icon);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (button), props[PROP_ICON_NAME]);
+}
+
+/**
+ * st_button_get_button_mask:
+ * @button: a #StButton
+ *
+ * Gets the mask of mouse buttons that @button emits the
+ * #StButton::clicked signal for.
+ *
+ * Returns: the mask of mouse buttons that @button emits the
+ * #StButton::clicked signal for.
+ */
+StButtonMask
+st_button_get_button_mask (StButton *button)
+{
+ g_return_val_if_fail (ST_IS_BUTTON (button), 0);
+
+ return ((StButtonPrivate *)st_button_get_instance_private (button))->button_mask;
+}
+
+/**
+ * st_button_set_button_mask:
+ * @button: a #Stbutton
+ * @mask: the mask of mouse buttons that @button responds to
+ *
+ * Sets which mouse buttons @button emits #StButton::clicked for.
+ */
+void
+st_button_set_button_mask (StButton *button,
+ StButtonMask mask)
+{
+ StButtonPrivate *priv;
+
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ priv = st_button_get_instance_private (button);
+
+ if (priv->button_mask == mask)
+ return;
+
+ priv->button_mask = mask;
+
+ g_object_notify_by_pspec (G_OBJECT (button), props[PROP_BUTTON_MASK]);
+}
+
+/**
+ * st_button_get_toggle_mode:
+ * @button: a #StButton
+ *
+ * Get the toggle mode status of the button.
+ *
+ * Returns: %TRUE if toggle mode is set, otherwise %FALSE
+ */
+gboolean
+st_button_get_toggle_mode (StButton *button)
+{
+ g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
+
+ return ((StButtonPrivate *)st_button_get_instance_private (button))->is_toggle;
+}
+
+/**
+ * st_button_set_toggle_mode:
+ * @button: a #Stbutton
+ * @toggle: %TRUE or %FALSE
+ *
+ * Enables or disables toggle mode for the button. In toggle mode, the active
+ * state will be "toggled" when the user clicks the button.
+ */
+void
+st_button_set_toggle_mode (StButton *button,
+ gboolean toggle)
+{
+ StButtonPrivate *priv;
+
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ priv = st_button_get_instance_private (button);
+
+ if (priv->is_toggle == toggle)
+ return;
+
+ priv->is_toggle = toggle;
+
+ g_object_notify_by_pspec (G_OBJECT (button), props[PROP_TOGGLE_MODE]);
+}
+
+/**
+ * st_button_get_checked:
+ * @button: a #StButton
+ *
+ * Get the #StButton:checked property of a #StButton that is in toggle mode.
+ *
+ * Returns: %TRUE if the button is checked, or %FALSE if not
+ */
+gboolean
+st_button_get_checked (StButton *button)
+{
+ g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
+
+ return ((StButtonPrivate *)st_button_get_instance_private (button))->is_checked;
+}
+
+/**
+ * st_button_set_checked:
+ * @button: a #Stbutton
+ * @checked: %TRUE or %FALSE
+ *
+ * Set the #StButton:checked property of the button. This is only really useful
+ * if the button has #StButton:toggle-mode property set to %TRUE.
+ */
+void
+st_button_set_checked (StButton *button,
+ gboolean checked)
+{
+ StButtonPrivate *priv;
+
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ priv = st_button_get_instance_private (button);
+ if (priv->is_checked == checked)
+ return;
+
+ priv->is_checked = checked;
+
+ if (checked)
+ st_widget_add_style_pseudo_class (ST_WIDGET (button), "checked");
+ else
+ st_widget_remove_style_pseudo_class (ST_WIDGET (button), "checked");
+
+ g_object_notify_by_pspec (G_OBJECT (button), props[PROP_CHECKED]);
+}
+
+/**
+ * st_button_fake_release:
+ * @button: an #StButton
+ *
+ * If this widget is holding a pointer grab, this function will
+ * will ungrab it, and reset the #StButton:pressed state. The effect is
+ * similar to if the user had released the mouse button, but without
+ * emitting the #StButton::clicked signal.
+ *
+ * This function is useful if for example you want to do something
+ * after the user is holding the mouse button for a given period of
+ * time, breaking the grab.
+ */
+void
+st_button_fake_release (StButton *button)
+{
+ StButtonPrivate *priv;
+
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ priv = st_button_get_instance_private (button);
+
+ if (priv->grab)
+ {
+ clutter_grab_dismiss (priv->grab);
+ g_clear_pointer (&priv->grab, clutter_grab_unref);
+ }
+
+ priv->grabbed = 0;
+
+ if (priv->pressed || priv->press_sequence)
+ st_button_release (button, priv->device,
+ priv->pressed, 0, NULL);
+}
+
+/******************************************************************************/
+/*************************** ACCESSIBILITY SUPPORT ****************************/
+/******************************************************************************/
+
+#define ST_TYPE_BUTTON_ACCESSIBLE st_button_accessible_get_type ()
+
+#define ST_BUTTON_ACCESSIBLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessible))
+
+#define ST_IS_BUTTON_ACCESSIBLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ ST_TYPE_BUTTON_ACCESSIBLE))
+
+#define ST_BUTTON_ACCESSIBLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
+
+#define ST_IS_BUTTON_ACCESSIBLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ ST_TYPE_BUTTON_ACCESSIBLE))
+
+#define ST_BUTTON_ACCESSIBLE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
+
+typedef struct _StButtonAccessible StButtonAccessible;
+typedef struct _StButtonAccessibleClass StButtonAccessibleClass;
+
+struct _StButtonAccessible
+{
+ StWidgetAccessible parent;
+};
+
+struct _StButtonAccessibleClass
+{
+ StWidgetAccessibleClass parent_class;
+};
+
+/* AtkObject */
+static void st_button_accessible_initialize (AtkObject *obj,
+ gpointer data);
+
+G_DEFINE_TYPE (StButtonAccessible, st_button_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
+
+static const gchar *
+st_button_accessible_get_name (AtkObject *obj)
+{
+ StButton *button = NULL;
+ const gchar *name = NULL;
+
+ button = ST_BUTTON (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+ if (button == NULL)
+ return NULL;
+
+ name = ATK_OBJECT_CLASS (st_button_accessible_parent_class)->get_name (obj);
+ if (name != NULL)
+ return name;
+
+ return st_button_get_label (button);
+}
+
+static void
+st_button_accessible_class_init (StButtonAccessibleClass *klass)
+{
+ AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+ atk_class->initialize = st_button_accessible_initialize;
+ atk_class->get_name = st_button_accessible_get_name;
+}
+
+static void
+st_button_accessible_init (StButtonAccessible *self)
+{
+ /* initialization done on AtkObject->initialize */
+}
+
+static void
+st_button_accessible_notify_label_cb (StButton *button,
+ GParamSpec *psec,
+ AtkObject *accessible)
+{
+ g_object_notify (G_OBJECT (accessible), "accessible-name");
+}
+
+static void
+st_button_accessible_compute_role (AtkObject *accessible,
+ StButton *button)
+{
+ atk_object_set_role (accessible, st_button_get_toggle_mode (button)
+ ? ATK_ROLE_TOGGLE_BUTTON : ATK_ROLE_PUSH_BUTTON);
+}
+
+static void
+st_button_accessible_notify_toggle_mode_cb (StButton *button,
+ GParamSpec *psec,
+ AtkObject *accessible)
+{
+ st_button_accessible_compute_role (accessible, button);
+}
+
+static void
+st_button_accessible_initialize (AtkObject *obj,
+ gpointer data)
+{
+ ATK_OBJECT_CLASS (st_button_accessible_parent_class)->initialize (obj, data);
+
+ st_button_accessible_compute_role (obj, ST_BUTTON (data));
+
+ g_signal_connect (data, "notify::label",
+ G_CALLBACK (st_button_accessible_notify_label_cb), obj);
+ g_signal_connect (data, "notify::toggle-mode",
+ G_CALLBACK (st_button_accessible_notify_toggle_mode_cb), obj);
+}
diff --git a/src/st/st-button.h b/src/st/st-button.h
new file mode 100644
index 0000000..3f7dbb0
--- /dev/null
+++ b/src/st/st-button.h
@@ -0,0 +1,85 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-button.h: Plain button actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_BUTTON_H__
+#define __ST_BUTTON_H__
+
+G_BEGIN_DECLS
+
+#include <st/st-bin.h>
+
+#define ST_TYPE_BUTTON (st_button_get_type ())
+G_DECLARE_DERIVABLE_TYPE (StButton, st_button, ST, BUTTON, StBin)
+
+struct _StButtonClass
+{
+ StBinClass parent_class;
+
+ /* vfuncs, not signals */
+ void (* transition) (StButton *button);
+
+ /* signals */
+ void (* clicked) (StButton *button, int clicked_button);
+};
+
+StWidget *st_button_new (void);
+StWidget *st_button_new_with_label (const gchar *text);
+const gchar *st_button_get_label (StButton *button);
+void st_button_set_label (StButton *button,
+ const gchar *text);
+const char *st_button_get_icon_name (StButton *button);
+void st_button_set_icon_name (StButton *button,
+ const char *icon_name);
+void st_button_set_toggle_mode (StButton *button,
+ gboolean toggle);
+gboolean st_button_get_toggle_mode (StButton *button);
+void st_button_set_checked (StButton *button,
+ gboolean checked);
+gboolean st_button_get_checked (StButton *button);
+
+void st_button_fake_release (StButton *button);
+
+/**
+ * StButtonMask:
+ * @ST_BUTTON_ONE: button 1 (left)
+ * @ST_BUTTON_TWO: button 2 (middle)
+ * @ST_BUTTON_THREE: button 3 (right)
+ *
+ * A mask representing which mouse buttons an #StButton responds to.
+ */
+typedef enum {
+ ST_BUTTON_ONE = (1 << 0),
+ ST_BUTTON_TWO = (1 << 1),
+ ST_BUTTON_THREE = (1 << 2),
+} StButtonMask;
+
+#define ST_BUTTON_MASK_FROM_BUTTON(button) (1 << ((button) - 1))
+
+void st_button_set_button_mask (StButton *button,
+ StButtonMask mask);
+StButtonMask st_button_get_button_mask (StButton *button);
+
+G_END_DECLS
+
+#endif /* __ST_BUTTON_H__ */
diff --git a/src/st/st-clipboard.c b/src/st/st-clipboard.c
new file mode 100644
index 0000000..4b730c9
--- /dev/null
+++ b/src/st/st-clipboard.c
@@ -0,0 +1,348 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-clipboard.c: clipboard object
+ *
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-clipboard
+ * @short_description: a simple representation of the clipboard
+ *
+ * #StCliboard is a very simple object representation of the clipboard
+ * available to applications. Text is always assumed to be UTF-8 and non-text
+ * items are not handled.
+ */
+
+#include "config.h"
+
+#include "st-clipboard.h"
+
+#include <meta/display.h>
+#include <meta/meta-selection-source-memory.h>
+#include <meta/meta-selection.h>
+
+G_DEFINE_TYPE (StClipboard, st_clipboard, G_TYPE_OBJECT)
+
+typedef struct _TransferData TransferData;
+struct _TransferData
+{
+ StClipboard *clipboard;
+ GCallback callback;
+ gpointer user_data;
+ GOutputStream *stream;
+};
+
+const char *supported_mimetypes[] = {
+ "text/plain;charset=utf-8",
+ "UTF8_STRING",
+ "text/plain",
+ "STRING",
+};
+
+static MetaSelection *meta_selection = NULL;
+
+static void
+st_clipboard_class_init (StClipboardClass *klass)
+{
+}
+
+static void
+st_clipboard_init (StClipboard *self)
+{
+}
+
+/**
+ * st_clipboard_get_default:
+ *
+ * Get the global #StClipboard object that represents the clipboard.
+ *
+ * Returns: (transfer none): a #StClipboard owned by St and must not be
+ * unrefferenced or freed.
+ */
+StClipboard*
+st_clipboard_get_default (void)
+{
+ static StClipboard *default_clipboard = NULL;
+
+ if (!default_clipboard)
+ {
+ default_clipboard = g_object_new (ST_TYPE_CLIPBOARD, NULL);
+ }
+
+ return default_clipboard;
+}
+
+static gboolean
+convert_type (StClipboardType type,
+ MetaSelectionType *type_out)
+{
+ if (type == ST_CLIPBOARD_TYPE_PRIMARY)
+ *type_out = META_SELECTION_PRIMARY;
+ else if (type == ST_CLIPBOARD_TYPE_CLIPBOARD)
+ *type_out = META_SELECTION_CLIPBOARD;
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+static const char *
+pick_mimetype (MetaSelection *meta_selection,
+ MetaSelectionType selection_type)
+{
+ const char *selected_mimetype = NULL;
+ GList *mimetypes;
+ int i;
+
+ mimetypes = meta_selection_get_mimetypes (meta_selection, selection_type);
+
+ for (i = 0; i < G_N_ELEMENTS (supported_mimetypes); i++)
+ {
+ if (g_list_find_custom (mimetypes, supported_mimetypes[i],
+ (GCompareFunc) g_strcmp0))
+ {
+ selected_mimetype = supported_mimetypes[i];
+ break;
+ }
+ }
+
+ g_list_free_full (mimetypes, g_free);
+ return selected_mimetype;
+}
+
+static void
+transfer_cb (MetaSelection *selection,
+ GAsyncResult *res,
+ TransferData *data)
+{
+ gchar *text = NULL;
+
+ if (meta_selection_transfer_finish (selection, res, NULL))
+ {
+ gsize data_size;
+
+ data_size =
+ g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (data->stream));
+ text = g_new0 (char, data_size + 1);
+ memcpy (text, g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (data->stream)), data_size);
+ }
+
+ ((StClipboardCallbackFunc) data->callback) (data->clipboard, text,
+ data->user_data);
+ g_object_unref (data->stream);
+ g_free (data);
+ g_free (text);
+}
+
+static void
+transfer_bytes_cb (MetaSelection *selection,
+ GAsyncResult *res,
+ TransferData *data)
+{
+ GBytes *bytes = NULL;
+
+ if (meta_selection_transfer_finish (selection, res, NULL))
+ bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (data->stream));
+
+ ((StClipboardContentCallbackFunc) data->callback) (data->clipboard, bytes,
+ data->user_data);
+ g_object_unref (data->stream);
+ g_clear_pointer (&bytes, g_bytes_unref);
+}
+
+/**
+ * st_clipboard_get_mimetypes:
+ * @clipboard: a #StClipboard
+ *
+ * Gets a list of the mimetypes supported by the default #StClipboard.
+ *
+ * Returns: (element-type utf8) (transfer full): the supported mimetypes
+ */
+GList *
+st_clipboard_get_mimetypes (StClipboard *clipboard,
+ StClipboardType type)
+{
+ MetaSelectionType selection_type;
+
+ g_return_val_if_fail (ST_IS_CLIPBOARD (clipboard), NULL);
+ g_return_val_if_fail (meta_selection != NULL, NULL);
+
+ if (!convert_type (type, &selection_type))
+ return NULL;
+
+ return meta_selection_get_mimetypes (meta_selection, selection_type);
+}
+
+/**
+ * st_clipboard_get_text:
+ * @clipboard: A #StCliboard
+ * @type: The type of clipboard data you want
+ * @callback: (scope async): function to be called when the text is retrieved
+ * @user_data: data to be passed to the callback
+ *
+ * Request the data from the clipboard in text form. @callback is executed
+ * when the data is retrieved.
+ */
+void
+st_clipboard_get_text (StClipboard *clipboard,
+ StClipboardType type,
+ StClipboardCallbackFunc callback,
+ gpointer user_data)
+{
+ MetaSelectionType selection_type;
+ TransferData *data;
+ const char *mimetype = NULL;
+
+ g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
+ g_return_if_fail (meta_selection != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (convert_type (type, &selection_type))
+ mimetype = pick_mimetype (meta_selection, selection_type);
+
+ if (!mimetype)
+ {
+ callback (clipboard, NULL, user_data);
+ return;
+ }
+
+ data = g_new0 (TransferData, 1);
+ data->clipboard = clipboard;
+ data->callback = G_CALLBACK (callback);
+ data->user_data = user_data;
+ data->stream = g_memory_output_stream_new_resizable ();
+
+ meta_selection_transfer_async (meta_selection,
+ selection_type,
+ mimetype, -1,
+ data->stream, NULL,
+ (GAsyncReadyCallback) transfer_cb,
+ data);
+}
+
+/**
+ * st_clipboard_get_content:
+ * @clipboard: A #StCliboard
+ * @type: The type of clipboard data you want
+ * @mimetype: The mimetype to get content for
+ * @callback: (scope async): function to be called when the type is retrieved
+ * @user_data: data to be passed to the callback
+ *
+ * Request the data from the clipboard in #GBytes form. @callback is executed
+ * when the data is retrieved.
+ */
+void
+st_clipboard_get_content (StClipboard *clipboard,
+ StClipboardType type,
+ const gchar *mimetype,
+ StClipboardContentCallbackFunc callback,
+ gpointer user_data)
+{
+ MetaSelectionType selection_type;
+ TransferData *data;
+
+ g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
+ g_return_if_fail (meta_selection != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (!mimetype || !convert_type (type, &selection_type))
+ {
+ callback (clipboard, NULL, user_data);
+ return;
+ }
+
+ data = g_new0 (TransferData, 1);
+ data->clipboard = clipboard;
+ data->callback = G_CALLBACK (callback);
+ data->user_data = user_data;
+ data->stream = g_memory_output_stream_new_resizable ();
+
+ meta_selection_transfer_async (meta_selection,
+ selection_type,
+ mimetype, -1,
+ data->stream, NULL,
+ (GAsyncReadyCallback) transfer_bytes_cb,
+ data);
+}
+
+/**
+ * st_clipboard_set_content:
+ * @clipboard: A #StClipboard
+ * @type: The type of clipboard that you want to set
+ * @mimetype: content mimetype
+ * @bytes: content data
+ *
+ * Sets the clipboard content to @bytes.
+ *
+ * @mimetype is a semi-colon separated list of mime-type strings.
+ **/
+void
+st_clipboard_set_content (StClipboard *clipboard,
+ StClipboardType type,
+ const gchar *mimetype,
+ GBytes *bytes)
+{
+ MetaSelectionType selection_type;
+ MetaSelectionSource *source;
+
+ g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
+ g_return_if_fail (meta_selection != NULL);
+ g_return_if_fail (bytes != NULL);
+
+ if (!convert_type (type, &selection_type))
+ return;
+
+ source = meta_selection_source_memory_new (mimetype, bytes);
+ meta_selection_set_owner (meta_selection, selection_type, source);
+ g_object_unref (source);
+}
+
+/**
+ * st_clipboard_set_text:
+ * @clipboard: A #StClipboard
+ * @type: The type of clipboard that you want to set
+ * @text: text to copy to the clipboard
+ *
+ * Sets text as the current contents of the clipboard.
+ */
+void
+st_clipboard_set_text (StClipboard *clipboard,
+ StClipboardType type,
+ const gchar *text)
+{
+ GBytes *bytes;
+
+ g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
+ g_return_if_fail (meta_selection != NULL);
+ g_return_if_fail (text != NULL);
+
+ bytes = g_bytes_new_take (g_strdup (text), strlen (text));
+ st_clipboard_set_content (clipboard, type, "text/plain;charset=utf-8", bytes);
+ g_bytes_unref (bytes);
+}
+
+/**
+ * st_clipboard_set_selection: (skip)
+ *
+ * Sets the #MetaSelection of the default #StClipboard.
+ *
+ * This function is called during the initialization of GNOME Shell.
+ */
+void
+st_clipboard_set_selection (MetaSelection *selection)
+{
+ meta_selection = selection;
+}
diff --git a/src/st/st-clipboard.h b/src/st/st-clipboard.h
new file mode 100644
index 0000000..022b832
--- /dev/null
+++ b/src/st/st-clipboard.h
@@ -0,0 +1,105 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-clipboard.h: clipboard object
+ *
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef _ST_CLIPBOARD_H
+#define _ST_CLIPBOARD_H
+
+#include <glib-object.h>
+#include <meta/meta-selection.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_CLIPBOARD st_clipboard_get_type()
+G_DECLARE_FINAL_TYPE (StClipboard, st_clipboard, ST, CLIPBOARD, GObject)
+
+typedef struct _StClipboard StClipboard;
+
+/**
+ * StClipboard:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+struct _StClipboard
+{
+ /*< private >*/
+ GObject parent;
+};
+
+typedef enum {
+ ST_CLIPBOARD_TYPE_PRIMARY,
+ ST_CLIPBOARD_TYPE_CLIPBOARD
+} StClipboardType;
+
+/**
+ * StClipboardCallbackFunc:
+ * @clipboard: A #StClipboard
+ * @text: text from the clipboard
+ * @user_data: user data
+ *
+ * Callback function called when text is retrieved from the clipboard.
+ */
+typedef void (*StClipboardCallbackFunc) (StClipboard *clipboard,
+ const gchar *text,
+ gpointer user_data);
+
+/**
+ * StClipboardContentCallbackFunc:
+ * @clipboard: A #StClipboard
+ * @bytes: content from the clipboard
+ * @user_data: user data
+ *
+ * Callback function called when content is retrieved from the clipboard.
+ */
+typedef void (*StClipboardContentCallbackFunc) (StClipboard *clipboard,
+ GBytes *bytes,
+ gpointer user_data);
+
+StClipboard* st_clipboard_get_default (void);
+
+GList * st_clipboard_get_mimetypes (StClipboard *clipboard,
+ StClipboardType type);
+
+void st_clipboard_get_text (StClipboard *clipboard,
+ StClipboardType type,
+ StClipboardCallbackFunc callback,
+ gpointer user_data);
+void st_clipboard_set_text (StClipboard *clipboard,
+ StClipboardType type,
+ const gchar *text);
+
+void st_clipboard_set_content (StClipboard *clipboard,
+ StClipboardType type,
+ const gchar *mimetype,
+ GBytes *bytes);
+void st_clipboard_get_content (StClipboard *clipboard,
+ StClipboardType type,
+ const gchar *mimetype,
+ StClipboardContentCallbackFunc callback,
+ gpointer user_data);
+
+void st_clipboard_set_selection (MetaSelection *selection);
+
+G_END_DECLS
+
+#endif /* _ST_CLIPBOARD_H */
diff --git a/src/st/st-drawing-area.c b/src/st/st-drawing-area.c
new file mode 100644
index 0000000..38737fd
--- /dev/null
+++ b/src/st/st-drawing-area.c
@@ -0,0 +1,242 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-drawing-area.c: A dynamically-sized Cairo drawing area
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-drawing-area
+ * @short_description: A dynamically-sized Cairo drawing area
+ *
+ * #StDrawingArea allows drawing via Cairo; the primary difference is that
+ * it is dynamically sized. To use, connect to the #StDrawingArea::repaint
+ * signal, and inside the signal handler, call
+ * st_drawing_area_get_context() to get the Cairo context to draw to. The
+ * #StDrawingArea::repaint signal will be emitted by default when the area is
+ * resized or the CSS style changes; you can use the
+ * st_drawing_area_queue_repaint() as well.
+ */
+
+#include "st-drawing-area.h"
+
+#include <cairo.h>
+#include <math.h>
+
+typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate;
+struct _StDrawingAreaPrivate {
+ cairo_t *context;
+ guint in_repaint : 1;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (StDrawingArea, st_drawing_area, ST_TYPE_WIDGET);
+
+/* Signals */
+enum
+{
+ REPAINT,
+ LAST_SIGNAL
+};
+
+static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 };
+
+static gboolean
+draw_content (ClutterCanvas *canvas,
+ cairo_t *cr,
+ int width,
+ int height,
+ gpointer user_data)
+{
+ StDrawingArea *area = ST_DRAWING_AREA (user_data);
+ StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area);
+
+ priv->context = cr;
+ priv->in_repaint = TRUE;
+
+ clutter_cairo_clear (cr);
+ g_signal_emit (area, st_drawing_area_signals[REPAINT], 0);
+
+ priv->context = NULL;
+ priv->in_repaint = FALSE;
+
+ return TRUE;
+}
+
+static void
+st_drawing_area_allocate (ClutterActor *self,
+ const ClutterActorBox *box)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ ClutterContent *content = clutter_actor_get_content (self);
+ ClutterActorBox content_box;
+ int width, height;
+ float resource_scale;
+
+ resource_scale = clutter_actor_get_resource_scale (self);
+
+ clutter_actor_set_allocation (self, box);
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ width = (int)(0.5 + content_box.x2 - content_box.x1);
+ height = (int)(0.5 + content_box.y2 - content_box.y1);
+
+ clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale);
+ clutter_canvas_set_size (CLUTTER_CANVAS (content), width, height);
+}
+
+static void
+st_drawing_area_style_changed (StWidget *self)
+{
+ (ST_WIDGET_CLASS (st_drawing_area_parent_class))->style_changed (self);
+
+ st_drawing_area_queue_repaint (ST_DRAWING_AREA (self));
+}
+
+static void
+st_drawing_area_resource_scale_changed (ClutterActor *self)
+{
+ float resource_scale;
+ ClutterContent *content = clutter_actor_get_content (self);
+
+ resource_scale = clutter_actor_get_resource_scale (self);
+ clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale);
+
+ if (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed)
+ CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed (self);
+}
+
+static void
+st_drawing_area_class_init (StDrawingAreaClass *klass)
+{
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ actor_class->allocate = st_drawing_area_allocate;
+ widget_class->style_changed = st_drawing_area_style_changed;
+ actor_class->resource_scale_changed = st_drawing_area_resource_scale_changed;
+
+ st_drawing_area_signals[REPAINT] =
+ g_signal_new ("repaint",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StDrawingAreaClass, repaint),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_drawing_area_init (StDrawingArea *area)
+{
+ ClutterContent *content = clutter_canvas_new ();
+ g_signal_connect (content, "draw", G_CALLBACK (draw_content), area);
+ clutter_actor_set_content (CLUTTER_ACTOR (area), content);
+ g_object_unref (content);
+}
+
+/**
+ * st_drawing_area_queue_repaint:
+ * @area: the #StDrawingArea
+ *
+ * Will cause the actor to emit a #StDrawingArea::repaint signal before it is
+ * next drawn to the scene. Useful if some parameters for the area being
+ * drawn other than the size or style have changed. Note that
+ * clutter_actor_queue_redraw() will simply result in the same
+ * contents being drawn to the scene again.
+ */
+void
+st_drawing_area_queue_repaint (StDrawingArea *area)
+{
+ g_return_if_fail (ST_IS_DRAWING_AREA (area));
+
+ clutter_content_invalidate (clutter_actor_get_content (CLUTTER_ACTOR (area)));
+}
+
+/**
+ * st_drawing_area_get_context:
+ * @area: the #StDrawingArea
+ *
+ * Gets the Cairo context to paint to. This function must only be called
+ * from a signal handler or virtual function for the #StDrawingArea::repaint
+ * signal.
+ *
+ * JavaScript code must call the special dispose function before returning from
+ * the signal handler or virtual function to avoid leaking memory:
+ *
+ * |[<!-- language="JavaScript" -->
+ * function onRepaint(area) {
+ * let cr = area.get_context();
+ *
+ * // Draw to the context
+ *
+ * cr.$dispose();
+ * }
+ *
+ * let area = new St.DrawingArea();
+ * area.connect('repaint', onRepaint);
+ * ]|
+ *
+ * Returns: (transfer none): the Cairo context for the paint operation
+ */
+cairo_t *
+st_drawing_area_get_context (StDrawingArea *area)
+{
+ StDrawingAreaPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_DRAWING_AREA (area), NULL);
+
+ priv = st_drawing_area_get_instance_private (area);
+ g_return_val_if_fail (priv->in_repaint, NULL);
+
+ return priv->context;
+}
+
+/**
+ * st_drawing_area_get_surface_size:
+ * @area: the #StDrawingArea
+ * @width: (out) (optional): location to store the width of the painted area
+ * @height: (out) (optional): location to store the height of the painted area
+ *
+ * Gets the size of the cairo surface being painted to, which is equal
+ * to the size of the content area of the widget. This function must
+ * only be called from a signal handler for the #StDrawingArea::repaint signal.
+ */
+void
+st_drawing_area_get_surface_size (StDrawingArea *area,
+ guint *width,
+ guint *height)
+{
+ StDrawingAreaPrivate *priv;
+ ClutterContent *content;
+ float w, h, resource_scale;
+
+ g_return_if_fail (ST_IS_DRAWING_AREA (area));
+
+ priv = st_drawing_area_get_instance_private (area);
+ g_return_if_fail (priv->in_repaint);
+
+ content = clutter_actor_get_content (CLUTTER_ACTOR (area));
+ clutter_content_get_preferred_size (content, &w, &h);
+
+ resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (area));
+
+ w /= resource_scale;
+ h /= resource_scale;
+
+ if (width)
+ *width = ceilf (w);
+ if (height)
+ *height = ceilf (h);
+}
diff --git a/src/st/st-drawing-area.h b/src/st/st-drawing-area.h
new file mode 100644
index 0000000..e09f9c5
--- /dev/null
+++ b/src/st/st-drawing-area.h
@@ -0,0 +1,44 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-drawing-area.h: A dynamically-sized Cairo drawing area
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_DRAWING_AREA_H__
+#define __ST_DRAWING_AREA_H__
+
+#include "st-widget.h"
+#include <cairo.h>
+
+#define ST_TYPE_DRAWING_AREA (st_drawing_area_get_type ())
+G_DECLARE_DERIVABLE_TYPE (StDrawingArea, st_drawing_area,
+ ST, DRAWING_AREA, StWidget)
+
+struct _StDrawingAreaClass
+{
+ StWidgetClass parent_class;
+
+ void (*repaint) (StDrawingArea *area);
+};
+
+void st_drawing_area_queue_repaint (StDrawingArea *area);
+cairo_t *st_drawing_area_get_context (StDrawingArea *area);
+void st_drawing_area_get_surface_size (StDrawingArea *area,
+ guint *width,
+ guint *height);
+
+#endif /* __ST_DRAWING_AREA_H__ */
diff --git a/src/st/st-entry.c b/src/st/st-entry.c
new file mode 100644
index 0000000..64f85fd
--- /dev/null
+++ b/src/st/st-entry.c
@@ -0,0 +1,1626 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-entry.c: Plain entry actor
+ *
+ * Copyright 2008, 2009 Intel Corporation
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-entry
+ * @short_description: Widget for displaying text
+ *
+ * #StEntry is a simple widget for displaying text. It derives from
+ * #StWidget to add extra style and placement functionality over
+ * #ClutterText. The internal #ClutterText is publicly accessibly to allow
+ * applications to set further properties.
+ *
+ * #StEntry supports the following pseudo style states:
+ *
+ * - `focus`: the widget has focus
+ * - `indeterminate`: the widget is showing the hint text or actor
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "st-entry.h"
+
+#include "st-icon.h"
+#include "st-label.h"
+#include "st-settings.h"
+#include "st-widget.h"
+#include "st-texture-cache.h"
+#include "st-clipboard.h"
+#include "st-private.h"
+
+#include "st-widget-accessible.h"
+
+
+/* properties */
+enum
+{
+ PROP_0,
+
+ PROP_CLUTTER_TEXT,
+ PROP_PRIMARY_ICON,
+ PROP_SECONDARY_ICON,
+ PROP_HINT_TEXT,
+ PROP_HINT_ACTOR,
+ PROP_TEXT,
+ PROP_INPUT_PURPOSE,
+ PROP_INPUT_HINTS,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+/* signals */
+enum
+{
+ PRIMARY_ICON_CLICKED,
+ SECONDARY_ICON_CLICKED,
+
+ LAST_SIGNAL
+};
+
+#define ST_ENTRY_PRIV(x) st_entry_get_instance_private ((StEntry *) x)
+
+
+typedef struct _StEntryPrivate StEntryPrivate;
+struct _StEntryPrivate
+{
+ ClutterActor *entry;
+
+ ClutterActor *primary_icon;
+ ClutterActor *secondary_icon;
+
+ ClutterActor *hint_actor;
+
+ gfloat spacing;
+
+ gboolean has_ibeam;
+
+ StShadow *shadow_spec;
+
+ CoglPipeline *text_shadow_material;
+ gfloat shadow_width;
+ gfloat shadow_height;
+};
+
+static guint entry_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (StEntry, st_entry, ST_TYPE_WIDGET);
+
+static GType st_entry_accessible_get_type (void) G_GNUC_CONST;
+
+static void
+st_entry_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StEntry *entry = ST_ENTRY (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_PRIMARY_ICON:
+ st_entry_set_primary_icon (entry, g_value_get_object (value));
+ break;
+
+ case PROP_SECONDARY_ICON:
+ st_entry_set_secondary_icon (entry, g_value_get_object (value));
+ break;
+
+ case PROP_HINT_TEXT:
+ st_entry_set_hint_text (entry, g_value_get_string (value));
+ break;
+
+ case PROP_HINT_ACTOR:
+ st_entry_set_hint_actor (entry, g_value_get_object (value));
+ break;
+
+ case PROP_TEXT:
+ st_entry_set_text (entry, g_value_get_string (value));
+ break;
+
+ case PROP_INPUT_PURPOSE:
+ st_entry_set_input_purpose (entry, g_value_get_enum (value));
+ break;
+
+ case PROP_INPUT_HINTS:
+ st_entry_set_input_hints (entry, g_value_get_flags (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_entry_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CLUTTER_TEXT:
+ g_value_set_object (value, priv->entry);
+ break;
+
+ case PROP_PRIMARY_ICON:
+ g_value_set_object (value, priv->primary_icon);
+ break;
+
+ case PROP_SECONDARY_ICON:
+ g_value_set_object (value, priv->secondary_icon);
+ break;
+
+ case PROP_HINT_TEXT:
+ g_value_set_string (value, st_entry_get_hint_text (ST_ENTRY (gobject)));
+ break;
+
+ case PROP_HINT_ACTOR:
+ g_value_set_object (value, priv->hint_actor);
+ break;
+
+ case PROP_TEXT:
+ g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->entry)));
+ break;
+
+ case PROP_INPUT_PURPOSE:
+ g_value_set_enum (value, clutter_text_get_input_purpose (CLUTTER_TEXT (priv->entry)));
+ break;
+
+ case PROP_INPUT_HINTS:
+ g_value_set_flags (value, clutter_text_get_input_hints (CLUTTER_TEXT (priv->entry)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_entry_dispose (GObject *object)
+{
+ StEntry *entry = ST_ENTRY (object);
+ StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
+
+ cogl_clear_object (&priv->text_shadow_material);
+
+ G_OBJECT_CLASS (st_entry_parent_class)->dispose (object);
+}
+
+static void
+st_entry_update_hint_visibility (StEntry *self)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (self);
+ gboolean hint_visible =
+ priv->hint_actor != NULL &&
+ !clutter_text_has_preedit (CLUTTER_TEXT (priv->entry)) &&
+ strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), "") == 0;
+
+ if (priv->hint_actor)
+ g_object_set (priv->hint_actor, "visible", hint_visible, NULL);
+
+ if (hint_visible)
+ st_widget_add_style_pseudo_class (ST_WIDGET (self), "indeterminate");
+ else
+ st_widget_remove_style_pseudo_class (ST_WIDGET (self), "indeterminate");
+}
+
+static void
+st_entry_style_changed (StWidget *self)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (self);
+ StThemeNode *theme_node;
+ StShadow *shadow_spec;
+ ClutterColor color;
+ gdouble size;
+
+ theme_node = st_widget_get_theme_node (self);
+
+ shadow_spec = st_theme_node_get_text_shadow (theme_node);
+ if (!priv->shadow_spec || !shadow_spec ||
+ !st_shadow_equal (shadow_spec, priv->shadow_spec))
+ {
+ g_clear_pointer (&priv->text_shadow_material, cogl_object_unref);
+
+ g_clear_pointer (&priv->shadow_spec, st_shadow_unref);
+ if (shadow_spec)
+ priv->shadow_spec = st_shadow_ref (shadow_spec);
+ }
+
+ _st_set_text_from_style (CLUTTER_TEXT (priv->entry), theme_node);
+
+ if (st_theme_node_lookup_length (theme_node, "caret-size", TRUE, &size))
+ clutter_text_set_cursor_size (CLUTTER_TEXT (priv->entry), (int)(.5 + size));
+
+ if (st_theme_node_lookup_color (theme_node, "caret-color", TRUE, &color))
+ clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), &color);
+
+ if (st_theme_node_lookup_color (theme_node, "selection-background-color", TRUE, &color))
+ clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), &color);
+
+ if (st_theme_node_lookup_color (theme_node, "selected-color", TRUE, &color))
+ clutter_text_set_selected_text_color (CLUTTER_TEXT (priv->entry), &color);
+
+ ST_WIDGET_CLASS (st_entry_parent_class)->style_changed (self);
+}
+
+static gboolean
+st_entry_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (widget);
+
+ /* This is basically the same as st_widget_real_navigate_focus(),
+ * except that widget is behaving as a proxy for priv->entry (which
+ * isn't an StWidget and so has no can-focus flag of its own).
+ */
+
+ if (from == priv->entry)
+ return FALSE;
+ else if (st_widget_get_can_focus (widget) &&
+ clutter_actor_is_mapped (priv->entry))
+ {
+ clutter_actor_grab_key_focus (priv->entry);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static void
+st_entry_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ gfloat hint_w, hint_min_w, icon_w;
+
+ st_theme_node_adjust_for_height (theme_node, &for_height);
+
+ clutter_actor_get_preferred_width (priv->entry, for_height,
+ min_width_p,
+ natural_width_p);
+
+ if (priv->hint_actor)
+ {
+ clutter_actor_get_preferred_width (priv->hint_actor, -1,
+ &hint_min_w, &hint_w);
+
+ if (min_width_p && hint_min_w > *min_width_p)
+ *min_width_p = hint_min_w;
+
+ if (natural_width_p && hint_w > *natural_width_p)
+ *natural_width_p = hint_w;
+ }
+
+ if (priv->primary_icon)
+ {
+ clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w);
+
+ if (min_width_p)
+ *min_width_p += icon_w + priv->spacing;
+
+ if (natural_width_p)
+ *natural_width_p += icon_w + priv->spacing;
+ }
+
+ if (priv->secondary_icon)
+ {
+ clutter_actor_get_preferred_width (priv->secondary_icon,
+ -1, NULL, &icon_w);
+
+ if (min_width_p)
+ *min_width_p += icon_w + priv->spacing;
+
+ if (natural_width_p)
+ *natural_width_p += icon_w + priv->spacing;
+ }
+
+ st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
+}
+
+static void
+st_entry_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ gfloat hint_h, icon_h;
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ clutter_actor_get_preferred_height (priv->entry, for_width,
+ min_height_p,
+ natural_height_p);
+
+ if (priv->hint_actor)
+ {
+ clutter_actor_get_preferred_height (priv->hint_actor, -1, NULL, &hint_h);
+
+ if (min_height_p && hint_h > *min_height_p)
+ *min_height_p = hint_h;
+
+ if (natural_height_p && hint_h > *natural_height_p)
+ *natural_height_p = hint_h;
+ }
+
+ if (priv->primary_icon)
+ {
+ clutter_actor_get_preferred_height (priv->primary_icon,
+ -1, NULL, &icon_h);
+
+ if (min_height_p && icon_h > *min_height_p)
+ *min_height_p = icon_h;
+
+ if (natural_height_p && icon_h > *natural_height_p)
+ *natural_height_p = icon_h;
+ }
+
+ if (priv->secondary_icon)
+ {
+ clutter_actor_get_preferred_height (priv->secondary_icon,
+ -1, NULL, &icon_h);
+
+ if (min_height_p && icon_h > *min_height_p)
+ *min_height_p = icon_h;
+
+ if (natural_height_p && icon_h > *natural_height_p)
+ *natural_height_p = icon_h;
+ }
+
+ st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
+}
+
+static void
+st_entry_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ ClutterActorBox content_box, child_box, icon_box, hint_box;
+ gfloat icon_w, icon_h;
+ gfloat hint_w, hint_min_w, hint_h;
+ gfloat entry_h, min_h, pref_h, avail_h;
+ ClutterActor *left_icon, *right_icon;
+ gboolean is_rtl;
+
+ is_rtl = clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL;
+
+ if (is_rtl)
+ {
+ right_icon = priv->primary_icon;
+ left_icon = priv->secondary_icon;
+ }
+ else
+ {
+ left_icon = priv->primary_icon;
+ right_icon = priv->secondary_icon;
+ }
+
+ clutter_actor_set_allocation (actor, box);
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ avail_h = content_box.y2 - content_box.y1;
+
+ child_box.x1 = content_box.x1;
+ child_box.x2 = content_box.x2;
+
+ if (left_icon)
+ {
+ clutter_actor_get_preferred_width (left_icon, -1, NULL, &icon_w);
+ clutter_actor_get_preferred_height (left_icon, -1, NULL, &icon_h);
+
+ icon_box.x1 = content_box.x1;
+ icon_box.x2 = icon_box.x1 + icon_w;
+
+ icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
+ icon_box.y2 = icon_box.y1 + icon_h;
+
+ clutter_actor_allocate (left_icon, &icon_box);
+
+ /* reduce the size for the entry */
+ child_box.x1 = MIN (child_box.x2, child_box.x1 + icon_w + priv->spacing);
+ }
+
+ if (right_icon)
+ {
+ clutter_actor_get_preferred_width (right_icon, -1, NULL, &icon_w);
+ clutter_actor_get_preferred_height (right_icon, -1, NULL, &icon_h);
+
+ icon_box.x2 = content_box.x2;
+ icon_box.x1 = icon_box.x2 - icon_w;
+
+ icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
+ icon_box.y2 = icon_box.y1 + icon_h;
+
+ clutter_actor_allocate (right_icon, &icon_box);
+
+ /* reduce the size for the entry */
+ child_box.x2 = MAX (child_box.x1, child_box.x2 - icon_w - priv->spacing);
+ }
+
+ if (priv->hint_actor)
+ {
+ /* now allocate the hint actor */
+ hint_box = child_box;
+
+ clutter_actor_get_preferred_width (priv->hint_actor, -1, &hint_min_w, &hint_w);
+ clutter_actor_get_preferred_height (priv->hint_actor, -1, NULL, &hint_h);
+
+ hint_w = CLAMP (hint_w, hint_min_w, child_box.x2 - child_box.x1);
+
+ if (is_rtl)
+ hint_box.x1 = hint_box.x2 - hint_w;
+ else
+ hint_box.x2 = hint_box.x1 + hint_w;
+
+ hint_box.y1 = ceil (content_box.y1 + avail_h / 2 - hint_h / 2);
+ hint_box.y2 = hint_box.y1 + hint_h;
+
+ clutter_actor_allocate (priv->hint_actor, &hint_box);
+ }
+
+ clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1,
+ &min_h, &pref_h);
+
+ entry_h = CLAMP (pref_h, min_h, avail_h);
+
+ child_box.y1 = (int) (content_box.y1 + avail_h / 2 - entry_h / 2);
+ child_box.y2 = child_box.y1 + entry_h;
+
+ clutter_actor_allocate (priv->entry, &child_box);
+}
+
+static void
+clutter_text_reactive_changed_cb (ClutterActor *text,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ ClutterActor *stage;
+
+ if (clutter_actor_get_reactive (text))
+ return;
+
+ if (!clutter_actor_has_key_focus (text))
+ return;
+
+ stage = clutter_actor_get_stage (text);
+ if (stage == NULL)
+ return;
+
+ clutter_stage_set_key_focus (CLUTTER_STAGE (stage), NULL);
+}
+
+static void
+clutter_text_focus_in_cb (ClutterText *text,
+ ClutterActor *actor)
+{
+ st_widget_add_style_pseudo_class (ST_WIDGET (actor), "focus");
+ clutter_text_set_cursor_visible (text, TRUE);
+}
+
+static void
+clutter_text_focus_out_cb (ClutterText *text,
+ ClutterActor *actor)
+{
+ st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "focus");
+ clutter_text_set_cursor_visible (text, FALSE);
+}
+
+static void
+clutter_text_cursor_changed (ClutterText *text,
+ StEntry *entry)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
+
+ st_entry_update_hint_visibility (entry);
+
+ g_clear_pointer (&priv->text_shadow_material, cogl_object_unref);
+}
+
+static void
+clutter_text_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ StEntry *entry = ST_ENTRY (user_data);
+ StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
+
+ st_entry_update_hint_visibility (entry);
+
+ /* Since the text changed, force a regen of the shadow texture */
+ cogl_clear_object (&priv->text_shadow_material);
+
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_TEXT]);
+}
+
+static void
+invalidate_shadow_pipeline (GObject *object,
+ GParamSpec *pspec,
+ StEntry *entry)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
+
+ g_clear_pointer (&priv->text_shadow_material, cogl_object_unref);
+}
+
+static void
+st_entry_clipboard_callback (StClipboard *clipboard,
+ const gchar *text,
+ gpointer data)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (data);
+ ClutterText *ctext = (ClutterText*)priv->entry;
+ gint cursor_pos;
+
+ if (!text)
+ return;
+
+ /* delete the current selection before pasting */
+ clutter_text_delete_selection (ctext);
+
+ /* "paste" the clipboard text into the entry */
+ cursor_pos = clutter_text_get_cursor_position (ctext);
+ clutter_text_insert_text (ctext, text, cursor_pos);
+}
+
+static gboolean
+clutter_text_button_press_event (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ gpointer user_data)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (user_data);
+
+ if (event->button == 2 &&
+ clutter_text_get_editable (CLUTTER_TEXT (priv->entry)))
+ {
+ StSettings *settings;
+ gboolean primary_paste_enabled;
+
+ settings = st_settings_get ();
+ g_object_get (settings, "primary-paste", &primary_paste_enabled, NULL);
+
+ if (primary_paste_enabled)
+ {
+ StClipboard *clipboard;
+
+ clipboard = st_clipboard_get_default ();
+
+ /* By the time the clipboard callback is called,
+ * the rest of the signal handlers will have
+ * run, making the text cursor to be in the correct
+ * place.
+ */
+ st_clipboard_get_text (clipboard,
+ ST_CLIPBOARD_TYPE_PRIMARY,
+ st_entry_clipboard_callback,
+ user_data);
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_entry_key_press_event (ClutterActor *actor,
+ ClutterKeyEvent *event)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+
+ /* This is expected to handle events that were emitted for the inner
+ ClutterText. They only reach this function if the ClutterText
+ didn't handle them */
+
+ /* paste */
+ if (((event->modifier_state & CLUTTER_CONTROL_MASK)
+ && event->keyval == CLUTTER_KEY_v) ||
+ ((event->modifier_state & CLUTTER_CONTROL_MASK)
+ && event->keyval == CLUTTER_KEY_V) ||
+ ((event->modifier_state & CLUTTER_SHIFT_MASK)
+ && event->keyval == CLUTTER_KEY_Insert))
+ {
+ StClipboard *clipboard;
+
+ clipboard = st_clipboard_get_default ();
+
+ st_clipboard_get_text (clipboard,
+ ST_CLIPBOARD_TYPE_CLIPBOARD,
+ st_entry_clipboard_callback,
+ actor);
+
+ return TRUE;
+ }
+
+ /* copy */
+ if ((event->modifier_state & CLUTTER_CONTROL_MASK)
+ && (event->keyval == CLUTTER_KEY_c || event->keyval == CLUTTER_KEY_C) &&
+ clutter_text_get_password_char ((ClutterText*) priv->entry) == 0)
+ {
+ StClipboard *clipboard;
+ gchar *text;
+
+ clipboard = st_clipboard_get_default ();
+
+ text = clutter_text_get_selection ((ClutterText*) priv->entry);
+
+ if (text && strlen (text))
+ st_clipboard_set_text (clipboard,
+ ST_CLIPBOARD_TYPE_CLIPBOARD,
+ text);
+
+ g_free (text);
+
+ return TRUE;
+ }
+
+
+ /* cut */
+ if ((event->modifier_state & CLUTTER_CONTROL_MASK)
+ && (event->keyval == CLUTTER_KEY_x || event->keyval == CLUTTER_KEY_X) &&
+ clutter_text_get_password_char ((ClutterText*) priv->entry) == 0)
+ {
+ StClipboard *clipboard;
+ gchar *text;
+
+ clipboard = st_clipboard_get_default ();
+
+ text = clutter_text_get_selection ((ClutterText*) priv->entry);
+
+ if (text && strlen (text))
+ {
+ st_clipboard_set_text (clipboard,
+ ST_CLIPBOARD_TYPE_CLIPBOARD,
+ text);
+
+ /* now delete the text */
+ clutter_text_delete_selection ((ClutterText *) priv->entry);
+ }
+
+ g_free (text);
+
+ return TRUE;
+ }
+
+
+ /* delete to beginning of line */
+ if ((event->modifier_state & CLUTTER_CONTROL_MASK) &&
+ (event->keyval == CLUTTER_KEY_u || event->keyval == CLUTTER_KEY_U))
+ {
+ int pos = clutter_text_get_cursor_position ((ClutterText *)priv->entry);
+ clutter_text_delete_text ((ClutterText *)priv->entry, 0, pos);
+
+ return TRUE;
+ }
+
+
+ /* delete to end of line */
+ if ((event->modifier_state & CLUTTER_CONTROL_MASK) &&
+ (event->keyval == CLUTTER_KEY_k || event->keyval == CLUTTER_KEY_K))
+ {
+ ClutterTextBuffer *buffer = clutter_text_get_buffer ((ClutterText *)priv->entry);
+ int pos = clutter_text_get_cursor_position ((ClutterText *)priv->entry);
+ clutter_text_buffer_delete_text (buffer, pos, -1);
+
+ return TRUE;
+ }
+
+ return CLUTTER_ACTOR_CLASS (st_entry_parent_class)->key_press_event (actor, event);
+}
+
+static void
+st_entry_key_focus_in (ClutterActor *actor)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+
+ /* We never want key focus. The ClutterText should be given first
+ pass for all key events */
+ clutter_actor_grab_key_focus (priv->entry);
+}
+
+static StEntryCursorFunc cursor_func = NULL;
+static gpointer cursor_func_data = NULL;
+
+/**
+ * st_entry_set_cursor_func: (skip)
+ *
+ * This function is for private use by libgnome-shell.
+ * Do not ever use.
+ */
+void
+st_entry_set_cursor_func (StEntryCursorFunc func,
+ gpointer data)
+{
+ cursor_func = func;
+ cursor_func_data = data;
+}
+
+static void
+st_entry_set_cursor (StEntry *entry,
+ gboolean use_ibeam)
+{
+ if (cursor_func)
+ cursor_func (entry, use_ibeam, cursor_func_data);
+
+ ((StEntryPrivate *)ST_ENTRY_PRIV (entry))->has_ibeam = use_ibeam;
+}
+
+static gboolean
+st_entry_enter_event (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+ ClutterStage *stage;
+ ClutterActor *target;
+
+ stage = clutter_event_get_stage ((ClutterEvent *) event);
+ target = clutter_stage_get_event_actor (stage, (ClutterEvent *) event);
+
+ if (target == priv->entry && event->related != NULL)
+ st_entry_set_cursor (ST_ENTRY (actor), TRUE);
+
+ return CLUTTER_ACTOR_CLASS (st_entry_parent_class)->enter_event (actor, event);
+}
+
+static gboolean
+st_entry_leave_event (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ st_entry_set_cursor (ST_ENTRY (actor), FALSE);
+
+ return CLUTTER_ACTOR_CLASS (st_entry_parent_class)->leave_event (actor, event);
+}
+
+static void
+st_entry_paint (ClutterActor *actor,
+ ClutterPaintContext *paint_context)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+ ClutterActorClass *parent_class;
+
+ st_widget_paint_background (ST_WIDGET (actor), paint_context);
+
+ if (priv->shadow_spec)
+ {
+ ClutterActorBox allocation;
+ float width, height;
+
+ clutter_actor_get_allocation_box (priv->entry, &allocation);
+ clutter_actor_box_get_size (&allocation, &width, &height);
+
+ if (priv->text_shadow_material == NULL ||
+ width != priv->shadow_width ||
+ height != priv->shadow_height)
+ {
+ CoglPipeline *material;
+
+ cogl_clear_object (&priv->text_shadow_material);
+
+ material = _st_create_shadow_pipeline_from_actor (priv->shadow_spec,
+ priv->entry);
+
+ priv->shadow_width = width;
+ priv->shadow_height = height;
+ priv->text_shadow_material = material;
+ }
+
+ if (priv->text_shadow_material != NULL)
+ {
+ CoglFramebuffer *framebuffer =
+ clutter_paint_context_get_framebuffer (paint_context);
+
+ _st_paint_shadow_with_opacity (priv->shadow_spec,
+ framebuffer,
+ priv->text_shadow_material,
+ &allocation,
+ clutter_actor_get_paint_opacity (priv->entry));
+ }
+ }
+
+ /* Since we paint the background ourselves, chain to the parent class
+ * of StWidget, to avoid painting it twice.
+ * This is needed as we still want to paint children.
+ */
+ parent_class = g_type_class_peek_parent (st_entry_parent_class);
+ parent_class->paint (actor, paint_context);
+}
+
+static void
+st_entry_unmap (ClutterActor *actor)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
+ if (priv->has_ibeam)
+ st_entry_set_cursor (ST_ENTRY (actor), FALSE);
+
+ CLUTTER_ACTOR_CLASS (st_entry_parent_class)->unmap (actor);
+}
+
+static gboolean
+st_entry_get_paint_volume (ClutterActor *actor,
+ ClutterPaintVolume *volume)
+{
+ return clutter_paint_volume_set_from_allocation (volume, actor);
+}
+
+static void
+st_entry_class_init (StEntryClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ gobject_class->set_property = st_entry_set_property;
+ gobject_class->get_property = st_entry_get_property;
+ gobject_class->dispose = st_entry_dispose;
+
+ actor_class->get_preferred_width = st_entry_get_preferred_width;
+ actor_class->get_preferred_height = st_entry_get_preferred_height;
+ actor_class->allocate = st_entry_allocate;
+ actor_class->paint = st_entry_paint;
+ actor_class->unmap = st_entry_unmap;
+ actor_class->get_paint_volume = st_entry_get_paint_volume;
+
+ actor_class->key_press_event = st_entry_key_press_event;
+ actor_class->key_focus_in = st_entry_key_focus_in;
+
+ actor_class->enter_event = st_entry_enter_event;
+ actor_class->leave_event = st_entry_leave_event;
+
+ widget_class->style_changed = st_entry_style_changed;
+ widget_class->navigate_focus = st_entry_navigate_focus;
+ widget_class->get_accessible_type = st_entry_accessible_get_type;
+
+ /**
+ * StEntry:clutter-text:
+ *
+ * The internal #ClutterText actor supporting the #StEntry.
+ */
+ props[PROP_CLUTTER_TEXT] =
+ g_param_spec_object ("clutter-text",
+ "Clutter Text",
+ "Internal ClutterText actor",
+ CLUTTER_TYPE_TEXT,
+ ST_PARAM_READABLE);
+
+ /**
+ * StEntry:primary-icon:
+ *
+ * The #ClutterActor acting as the primary icon at the start of the #StEntry.
+ */
+ props[PROP_PRIMARY_ICON] =
+ g_param_spec_object ("primary-icon",
+ "Primary Icon",
+ "Primary Icon actor",
+ CLUTTER_TYPE_ACTOR,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StEntry:secondary-icon:
+ *
+ * The #ClutterActor acting as the secondary icon at the end of the #StEntry.
+ */
+ props[PROP_SECONDARY_ICON] =
+ g_param_spec_object ("secondary-icon",
+ "Secondary Icon",
+ "Secondary Icon actor",
+ CLUTTER_TYPE_ACTOR,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StEntry:hint-text:
+ *
+ * The text to display when the entry is empty and unfocused. Setting this
+ * will replace the actor of #StEntry::hint-actor.
+ */
+ props[PROP_HINT_TEXT] =
+ g_param_spec_string ("hint-text",
+ "Hint Text",
+ "Text to display when the entry is not focused "
+ "and the text property is empty",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StEntry:hint-actor:
+ *
+ * A #ClutterActor to display when the entry is empty and unfocused. Setting
+ * this will replace the actor displaying #StEntry:hint-text.
+ */
+ props[PROP_HINT_ACTOR] =
+ g_param_spec_object ("hint-actor",
+ "Hint Actor",
+ "An actor to display when the entry is not focused "
+ "and the text property is empty",
+ CLUTTER_TYPE_ACTOR,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StEntry:text:
+ *
+ * The current text value of the #StEntry.
+ */
+ props[PROP_TEXT] =
+ g_param_spec_string ("text",
+ "Text",
+ "Text of the entry",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StEntry:input-purpose:
+ *
+ * The #ClutterInputContentPurpose that helps on-screen keyboards and similar
+ * input methods to decide which keys should be presented to the user.
+ */
+ props[PROP_INPUT_PURPOSE] =
+ g_param_spec_enum ("input-purpose",
+ "Purpose",
+ "Purpose of the text field",
+ CLUTTER_TYPE_INPUT_CONTENT_PURPOSE,
+ CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StEntry:input-hints:
+ *
+ * The #ClutterInputContentHintFlags providing additional hints (beyond
+ * #StEntry:input-purpose) that allow input methods to fine-tune their
+ * behaviour.
+ */
+ props[PROP_INPUT_HINTS] =
+ g_param_spec_flags ("input-hints",
+ "hints",
+ "Hints for the text field behaviour",
+ CLUTTER_TYPE_INPUT_CONTENT_HINT_FLAGS,
+ 0,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+
+ /* signals */
+ /**
+ * StEntry::primary-icon-clicked:
+ * @self: the #StEntry
+ *
+ * Emitted when the primary icon is clicked.
+ */
+ entry_signals[PRIMARY_ICON_CLICKED] =
+ g_signal_new ("primary-icon-clicked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StEntryClass, primary_icon_clicked),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * StEntry::secondary-icon-clicked:
+ * @self: the #StEntry
+ *
+ * Emitted when the secondary icon is clicked.
+ */
+ entry_signals[SECONDARY_ICON_CLICKED] =
+ g_signal_new ("secondary-icon-clicked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StEntryClass, secondary_icon_clicked),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_entry_init (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ priv = st_entry_get_instance_private (entry);
+
+ priv->entry = g_object_new (CLUTTER_TYPE_TEXT,
+ "line-alignment", PANGO_ALIGN_LEFT,
+ "editable", TRUE,
+ "reactive", TRUE,
+ "single-line-mode", TRUE,
+ NULL);
+
+ g_object_bind_property (G_OBJECT (entry), "reactive",
+ priv->entry, "reactive",
+ G_BINDING_DEFAULT);
+
+ g_signal_connect(priv->entry, "notify::reactive",
+ G_CALLBACK (clutter_text_reactive_changed_cb), entry);
+
+ g_signal_connect (priv->entry, "key-focus-in",
+ G_CALLBACK (clutter_text_focus_in_cb), entry);
+
+ g_signal_connect (priv->entry, "key-focus-out",
+ G_CALLBACK (clutter_text_focus_out_cb), entry);
+
+ g_signal_connect (priv->entry, "button-press-event",
+ G_CALLBACK (clutter_text_button_press_event), entry);
+
+ g_signal_connect (priv->entry, "cursor-changed",
+ G_CALLBACK (clutter_text_cursor_changed), entry);
+
+ g_signal_connect (priv->entry, "notify::text",
+ G_CALLBACK (clutter_text_changed_cb), entry);
+
+ /* These properties might get set from CSS using _st_set_text_from_style */
+ g_signal_connect (priv->entry, "notify::font-description",
+ G_CALLBACK (invalidate_shadow_pipeline), entry);
+
+ g_signal_connect (priv->entry, "notify::attributes",
+ G_CALLBACK (invalidate_shadow_pipeline), entry);
+
+ g_signal_connect (priv->entry, "notify::justify",
+ G_CALLBACK (invalidate_shadow_pipeline), entry);
+
+ g_signal_connect (priv->entry, "notify::line-alignment",
+ G_CALLBACK (invalidate_shadow_pipeline), entry);
+
+
+ priv->spacing = 6.0f;
+
+ priv->text_shadow_material = NULL;
+ priv->shadow_width = -1.;
+ priv->shadow_height = -1.;
+
+ clutter_actor_add_child (CLUTTER_ACTOR (entry), priv->entry);
+ clutter_actor_set_reactive ((ClutterActor *) entry, TRUE);
+
+ /* set cursor hidden until we receive focus */
+ clutter_text_set_cursor_visible ((ClutterText *) priv->entry, FALSE);
+}
+
+/**
+ * st_entry_new:
+ * @text: (nullable): text to set the entry to
+ *
+ * Create a new #StEntry with the specified text.
+ *
+ * Returns: a new #StEntry
+ */
+StWidget *
+st_entry_new (const gchar *text)
+{
+ StWidget *entry;
+
+ /* add the entry to the stage, but don't allow it to be visible */
+ entry = g_object_new (ST_TYPE_ENTRY,
+ "text", text,
+ NULL);
+
+ return entry;
+}
+
+/**
+ * st_entry_get_text:
+ * @entry: a #StEntry
+ *
+ * Get the text displayed on the entry. If @entry is empty, an empty string will
+ * be returned instead of %NULL.
+ *
+ * Returns: (transfer none): the text for the entry
+ */
+const gchar *
+st_entry_get_text (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
+
+ priv = st_entry_get_instance_private (entry);
+
+ return clutter_text_get_text (CLUTTER_TEXT (priv->entry));
+}
+
+/**
+ * st_entry_set_text:
+ * @entry: a #StEntry
+ * @text: (nullable): text to set the entry to
+ *
+ * Sets the text displayed on the entry. If @text is %NULL, the #ClutterText
+ * will instead be set to an empty string.
+ */
+void
+st_entry_set_text (StEntry *entry,
+ const gchar *text)
+{
+ StEntryPrivate *priv;
+
+ g_return_if_fail (ST_IS_ENTRY (entry));
+
+ priv = st_entry_get_instance_private (entry);
+
+ clutter_text_set_text (CLUTTER_TEXT (priv->entry), text);
+
+ /* Note: PROP_TEXT will get notfied from our notify::text handler connected
+ * to priv->entry. */
+}
+
+/**
+ * st_entry_get_clutter_text:
+ * @entry: a #StEntry
+ *
+ * Retrieve the internal #ClutterText so that extra parameters can be set.
+ *
+ * Returns: (transfer none): the #ClutterText used by @entry
+ */
+ClutterActor*
+st_entry_get_clutter_text (StEntry *entry)
+{
+ g_return_val_if_fail (ST_ENTRY (entry), NULL);
+
+ return ((StEntryPrivate *)ST_ENTRY_PRIV (entry))->entry;
+}
+
+/**
+ * st_entry_set_hint_text:
+ * @entry: a #StEntry
+ * @text: (nullable): text to set as the entry hint
+ *
+ * Sets the text to display when the entry is empty and unfocused. When the
+ * entry is displaying the hint, it has a pseudo class of `indeterminate`.
+ * A value of %NULL unsets the hint.
+ */
+void
+st_entry_set_hint_text (StEntry *entry,
+ const gchar *text)
+{
+ StWidget *label;
+
+ g_return_if_fail (ST_IS_ENTRY (entry));
+
+ label = st_label_new (text);
+ st_widget_add_style_class_name (label, "hint-text");
+
+ st_entry_set_hint_actor (ST_ENTRY (entry), CLUTTER_ACTOR (label));
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_HINT_TEXT]);
+}
+
+/**
+ * st_entry_get_hint_text:
+ * @entry: a #StEntry
+ *
+ * Gets the text that is displayed when the entry is empty and unfocused or
+ * %NULL if the #StEntry:hint-actor was set to an actor that is not a #StLabel.
+ *
+ * Unlike st_entry_get_text() this function may return %NULL if
+ * #StEntry:hint-actor is not a #StLabel.
+ *
+ * Returns: (nullable) (transfer none): the current value of the hint property
+ */
+const gchar *
+st_entry_get_hint_text (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
+
+ priv = ST_ENTRY_PRIV (entry);
+
+ if (priv->hint_actor != NULL && ST_IS_LABEL (priv->hint_actor))
+ return st_label_get_text (ST_LABEL (priv->hint_actor));
+
+ return NULL;
+}
+
+/**
+ * st_entry_set_input_purpose:
+ * @entry: a #StEntry
+ * @purpose: the purpose
+ *
+ * Sets the #StEntry:input-purpose property which
+ * can be used by on-screen keyboards and other input
+ * methods to adjust their behaviour.
+ */
+void
+st_entry_set_input_purpose (StEntry *entry,
+ ClutterInputContentPurpose purpose)
+{
+ StEntryPrivate *priv;
+ ClutterText *editable;
+
+ g_return_if_fail (ST_IS_ENTRY (entry));
+
+ priv = st_entry_get_instance_private (entry);
+ editable = CLUTTER_TEXT (priv->entry);
+
+ if (clutter_text_get_input_purpose (editable) != purpose)
+ {
+ clutter_text_set_input_purpose (editable, purpose);
+
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_INPUT_PURPOSE]);
+ }
+}
+
+/**
+ * st_entry_get_input_purpose:
+ * @entry: a #StEntry
+ *
+ * Gets the value of the #StEntry:input-purpose property.
+ *
+ * Returns: the input purpose of the entry
+ */
+ClutterInputContentPurpose
+st_entry_get_input_purpose (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY (entry), CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL);
+
+ priv = st_entry_get_instance_private (entry);
+ return clutter_text_get_input_purpose (CLUTTER_TEXT (priv->entry));
+}
+
+/**
+ * st_entry_set_input_hints:
+ * @entry: a #StEntry
+ * @hints: the hints
+ *
+ * Sets the #StEntry:input-hints property, which
+ * allows input methods to fine-tune their behaviour.
+ */
+void
+st_entry_set_input_hints (StEntry *entry,
+ ClutterInputContentHintFlags hints)
+{
+ StEntryPrivate *priv;
+ ClutterText *editable;
+
+ g_return_if_fail (ST_IS_ENTRY (entry));
+
+ priv = st_entry_get_instance_private (entry);
+ editable = CLUTTER_TEXT (priv->entry);
+
+ if (clutter_text_get_input_hints (editable) != hints)
+ {
+ clutter_text_set_input_hints (editable, hints);
+
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_INPUT_HINTS]);
+ }
+}
+
+/**
+ * st_entry_get_input_hints:
+ * @entry: a #StEntry
+ *
+ * Gets the value of the #StEntry:input-hints property.
+ *
+ * Returns: the input hints for the entry
+ */
+ClutterInputContentHintFlags
+st_entry_get_input_hints (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY (entry), 0);
+
+ priv = st_entry_get_instance_private (entry);
+ return clutter_text_get_input_hints (CLUTTER_TEXT (priv->entry));
+}
+
+static void
+_st_entry_icon_clicked_cb (ClutterClickAction *action,
+ ClutterActor *actor,
+ StEntry *entry)
+{
+ StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
+
+ if (!clutter_actor_get_reactive (CLUTTER_ACTOR (entry)))
+ return;
+
+ if (actor == priv->primary_icon)
+ g_signal_emit (entry, entry_signals[PRIMARY_ICON_CLICKED], 0);
+ else
+ g_signal_emit (entry, entry_signals[SECONDARY_ICON_CLICKED], 0);
+}
+
+static void
+_st_entry_set_icon (StEntry *entry,
+ ClutterActor **icon,
+ ClutterActor *new_icon)
+{
+ if (*icon)
+ {
+ clutter_actor_remove_action_by_name (*icon, "entry-icon-action");
+ clutter_actor_remove_child (CLUTTER_ACTOR (entry), *icon);
+ *icon = NULL;
+ }
+
+ if (new_icon)
+ {
+ ClutterAction *action;
+
+ *icon = g_object_ref (new_icon);
+
+ clutter_actor_set_reactive (*icon, TRUE);
+ clutter_actor_add_child (CLUTTER_ACTOR (entry), *icon);
+
+ action = clutter_click_action_new ();
+ clutter_actor_add_action_with_name (*icon, "entry-icon-action", action);
+ g_signal_connect (action, "clicked",
+ G_CALLBACK (_st_entry_icon_clicked_cb), entry);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (entry));
+}
+
+/**
+ * st_entry_set_primary_icon:
+ * @entry: a #StEntry
+ * @icon: (nullable): a #ClutterActor
+ *
+ * Set the primary icon of the entry to @icon.
+ */
+void
+st_entry_set_primary_icon (StEntry *entry,
+ ClutterActor *icon)
+{
+ StEntryPrivate *priv;
+
+ g_return_if_fail (ST_IS_ENTRY (entry));
+
+ priv = st_entry_get_instance_private (entry);
+
+ if (priv->primary_icon == icon)
+ return;
+
+ _st_entry_set_icon (entry, &priv->primary_icon, icon);
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_PRIMARY_ICON]);
+}
+
+/**
+ * st_entry_get_primary_icon:
+ * @entry: a #StEntry
+ *
+ * Get the value of the #StEntry:primary-icon property.
+ *
+ * Returns: (nullable) (transfer none): a #ClutterActor
+ */
+ClutterActor *
+st_entry_get_primary_icon (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
+
+ priv = ST_ENTRY_PRIV (entry);
+ return priv->primary_icon;
+}
+
+/**
+ * st_entry_set_secondary_icon:
+ * @entry: a #StEntry
+ * @icon: (nullable): an #ClutterActor
+ *
+ * Set the secondary icon of the entry to @icon.
+ */
+void
+st_entry_set_secondary_icon (StEntry *entry,
+ ClutterActor *icon)
+{
+ StEntryPrivate *priv;
+
+ g_return_if_fail (ST_IS_ENTRY (entry));
+
+ priv = st_entry_get_instance_private (entry);
+
+ if (priv->secondary_icon == icon)
+ return;
+
+ _st_entry_set_icon (entry, &priv->secondary_icon, icon);
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_SECONDARY_ICON]);
+}
+
+/**
+ * st_entry_get_secondary_icon:
+ * @entry: a #StEntry
+ *
+ * Get the value of the #StEntry:secondary-icon property.
+ *
+ * Returns: (nullable) (transfer none): a #ClutterActor
+ */
+ClutterActor *
+st_entry_get_secondary_icon (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
+
+ priv = ST_ENTRY_PRIV (entry);
+ return priv->secondary_icon;
+}
+
+/**
+ * st_entry_set_hint_actor:
+ * @entry: a #StEntry
+ * @hint_actor: (nullable): a #ClutterActor
+ *
+ * Set the hint actor of the entry to @hint_actor.
+ */
+void
+st_entry_set_hint_actor (StEntry *entry,
+ ClutterActor *hint_actor)
+{
+ StEntryPrivate *priv;
+
+ g_return_if_fail (ST_IS_ENTRY (entry));
+
+ priv = ST_ENTRY_PRIV (entry);
+
+ if (priv->hint_actor == hint_actor)
+ return;
+
+ if (priv->hint_actor != NULL)
+ {
+ clutter_actor_remove_child (CLUTTER_ACTOR (entry), priv->hint_actor);
+ priv->hint_actor = NULL;
+ }
+
+ if (hint_actor != NULL)
+ {
+ priv->hint_actor = hint_actor;
+ clutter_actor_add_child (CLUTTER_ACTOR (entry), priv->hint_actor);
+ }
+
+ st_entry_update_hint_visibility (entry);
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_HINT_ACTOR]);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (entry));
+}
+
+/**
+ * st_entry_get_hint_actor:
+ * @entry: a #StEntry
+ *
+ * Get the value of the #StEntry:hint-actor property.
+ *
+ * Returns: (nullable) (transfer none): a #ClutterActor
+ */
+ClutterActor *
+st_entry_get_hint_actor (StEntry *entry)
+{
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
+
+ priv = ST_ENTRY_PRIV (entry);
+ return priv->hint_actor;
+}
+
+/******************************************************************************/
+/*************************** ACCESSIBILITY SUPPORT ****************************/
+/******************************************************************************/
+
+#define ST_TYPE_ENTRY_ACCESSIBLE (st_entry_accessible_get_type ())
+#define ST_ENTRY_ACCESSIBLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessible))
+#define ST_IS_ENTRY_ACCESSIBLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ST_TYPE_ENTRY_ACCESSIBLE))
+#define ST_ENTRY_ACCESSIBLE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessibleClass))
+#define ST_IS_ENTRY_ACCESSIBLE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), ST_TYPE_ENTRY_ACCESSIBLE))
+#define ST_ENTRY_ACCESSIBLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessibleClass))
+
+typedef struct _StEntryAccessible StEntryAccessible;
+typedef struct _StEntryAccessibleClass StEntryAccessibleClass;
+
+struct _StEntryAccessible
+{
+ StWidgetAccessible parent;
+};
+
+struct _StEntryAccessibleClass
+{
+ StWidgetAccessibleClass parent_class;
+};
+
+G_DEFINE_TYPE (StEntryAccessible, st_entry_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
+
+static void
+st_entry_accessible_init (StEntryAccessible *self)
+{
+ /* initialization done on AtkObject->initialize */
+}
+
+static void
+st_entry_accessible_initialize (AtkObject *obj,
+ gpointer data)
+{
+ ATK_OBJECT_CLASS (st_entry_accessible_parent_class)->initialize (obj, data);
+
+ /* StEntry is behaving as a ClutterText container */
+ atk_object_set_role (obj, ATK_ROLE_PANEL);
+}
+
+static gint
+st_entry_accessible_get_n_children (AtkObject *obj)
+{
+ StEntry *entry = NULL;
+ StEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ENTRY_ACCESSIBLE (obj), 0);
+
+ entry = ST_ENTRY (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+ if (entry == NULL)
+ return 0;
+
+ priv = st_entry_get_instance_private (entry);
+ if (priv->entry == NULL)
+ return 0;
+ else
+ return 1;
+}
+
+static AtkObject*
+st_entry_accessible_ref_child (AtkObject *obj,
+ gint i)
+{
+ StEntry *entry = NULL;
+ StEntryPrivate *priv;
+ AtkObject *result = NULL;
+
+ g_return_val_if_fail (ST_IS_ENTRY_ACCESSIBLE (obj), NULL);
+ g_return_val_if_fail (i == 0, NULL);
+
+ entry = ST_ENTRY (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+ if (entry == NULL)
+ return NULL;
+
+ priv = st_entry_get_instance_private (entry);
+ if (priv->entry == NULL)
+ return NULL;
+
+ result = clutter_actor_get_accessible (priv->entry);
+ g_object_ref (result);
+
+ return result;
+}
+
+
+static void
+st_entry_accessible_class_init (StEntryAccessibleClass *klass)
+{
+ AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+ atk_class->initialize = st_entry_accessible_initialize;
+ atk_class->get_n_children = st_entry_accessible_get_n_children;
+ atk_class->ref_child= st_entry_accessible_ref_child;
+}
diff --git a/src/st/st-entry.h b/src/st/st-entry.h
new file mode 100644
index 0000000..2a05759
--- /dev/null
+++ b/src/st/st-entry.h
@@ -0,0 +1,79 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-entry.h: Plain entry actor
+ *
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_ENTRY_H__
+#define __ST_ENTRY_H__
+
+G_BEGIN_DECLS
+
+#include <st/st-widget.h>
+
+#define ST_TYPE_ENTRY (st_entry_get_type ())
+G_DECLARE_DERIVABLE_TYPE (StEntry, st_entry, ST, ENTRY, StWidget)
+
+struct _StEntryClass
+{
+ StWidgetClass parent_class;
+
+ /* signals */
+ void (*primary_icon_clicked) (StEntry *entry);
+ void (*secondary_icon_clicked) (StEntry *entry);
+};
+
+StWidget *st_entry_new (const gchar *text);
+const gchar *st_entry_get_text (StEntry *entry);
+void st_entry_set_text (StEntry *entry,
+ const gchar *text);
+ClutterActor *st_entry_get_clutter_text (StEntry *entry);
+
+void st_entry_set_hint_text (StEntry *entry,
+ const gchar *text);
+const gchar *st_entry_get_hint_text (StEntry *entry);
+
+void st_entry_set_input_purpose (StEntry *entry,
+ ClutterInputContentPurpose purpose);
+void st_entry_set_input_hints (StEntry *entry,
+ ClutterInputContentHintFlags hints);
+
+ClutterInputContentPurpose st_entry_get_input_purpose (StEntry *entry);
+ClutterInputContentHintFlags st_entry_get_input_hints (StEntry *entry);
+
+void st_entry_set_primary_icon (StEntry *entry,
+ ClutterActor *icon);
+ClutterActor * st_entry_get_primary_icon (StEntry *entry);
+
+void st_entry_set_secondary_icon (StEntry *entry,
+ ClutterActor *icon);
+ClutterActor * st_entry_get_secondary_icon (StEntry *entry);
+
+void st_entry_set_hint_actor (StEntry *entry,
+ ClutterActor *hint_actor);
+ClutterActor * st_entry_get_hint_actor (StEntry *entry);
+
+typedef void (*StEntryCursorFunc) (StEntry *entry, gboolean use_ibeam, gpointer data);
+void st_entry_set_cursor_func (StEntryCursorFunc func,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __ST_ENTRY_H__ */
diff --git a/src/st/st-focus-manager.c b/src/st/st-focus-manager.c
new file mode 100644
index 0000000..1ac6d28
--- /dev/null
+++ b/src/st/st-focus-manager.c
@@ -0,0 +1,256 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-focus-manager.c: Keyboard focus manager
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-focus-manager
+ * @short_description: Keyboard focus management
+ *
+ * #StFocusManager handles keyboard focus for all actors on the stage.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "st-focus-manager.h"
+
+struct _StFocusManagerPrivate
+{
+ GHashTable *groups;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (StFocusManager, st_focus_manager, G_TYPE_OBJECT)
+
+static void
+st_focus_manager_dispose (GObject *object)
+{
+ StFocusManager *manager = ST_FOCUS_MANAGER (object);
+
+ if (manager->priv->groups)
+ {
+ g_hash_table_destroy (manager->priv->groups);
+ manager->priv->groups = NULL;
+ }
+
+ G_OBJECT_CLASS (st_focus_manager_parent_class)->dispose (object);
+}
+
+static void
+st_focus_manager_class_init (StFocusManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = st_focus_manager_dispose;
+}
+
+static void
+st_focus_manager_init (StFocusManager *manager)
+{
+ manager->priv = st_focus_manager_get_instance_private (manager);
+ manager->priv->groups = g_hash_table_new (NULL, NULL);
+}
+
+static gboolean
+st_focus_manager_stage_event (ClutterActor *stage,
+ ClutterEvent *event,
+ gpointer user_data)
+{
+ StFocusManager *manager = user_data;
+ StDirectionType direction;
+ gboolean wrap_around = FALSE;
+ ClutterActor *focused, *group;
+
+ if (event->type != CLUTTER_KEY_PRESS)
+ return FALSE;
+
+ switch (event->key.keyval)
+ {
+ case CLUTTER_KEY_Up:
+ direction = ST_DIR_UP;
+ break;
+ case CLUTTER_KEY_Down:
+ direction = ST_DIR_DOWN;
+ break;
+ case CLUTTER_KEY_Left:
+ direction = ST_DIR_LEFT;
+ break;
+ case CLUTTER_KEY_Right:
+ direction = ST_DIR_RIGHT;
+ break;
+ case CLUTTER_KEY_Tab:
+ if (event->key.modifier_state & CLUTTER_SHIFT_MASK)
+ direction = ST_DIR_TAB_BACKWARD;
+ else
+ direction = ST_DIR_TAB_FORWARD;
+ wrap_around = TRUE;
+ break;
+ case CLUTTER_KEY_ISO_Left_Tab:
+ direction = ST_DIR_TAB_BACKWARD;
+ wrap_around = TRUE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ focused = clutter_stage_get_key_focus (CLUTTER_STAGE (stage));
+ if (!focused)
+ return FALSE;
+
+ for (group = focused; group != stage; group = clutter_actor_get_parent (group))
+ {
+ if (g_hash_table_lookup (manager->priv->groups, group))
+ {
+ return st_widget_navigate_focus (ST_WIDGET (group), focused,
+ direction, wrap_around);
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * st_focus_manager_get_for_stage:
+ * @stage: a #ClutterStage
+ *
+ * Gets the #StFocusManager for @stage, creating it if necessary.
+ *
+ * Returns: (transfer none): the focus manager for @stage
+ */
+StFocusManager *
+st_focus_manager_get_for_stage (ClutterStage *stage)
+{
+ StFocusManager *manager;
+
+ manager = g_object_get_data (G_OBJECT (stage), "st-focus-manager");
+ if (!manager)
+ {
+ manager = g_object_new (ST_TYPE_FOCUS_MANAGER, NULL);
+ g_object_set_data_full (G_OBJECT (stage), "st-focus-manager",
+ manager, g_object_unref);
+
+ g_signal_connect (stage, "event",
+ G_CALLBACK (st_focus_manager_stage_event), manager);
+ }
+
+ return manager;
+}
+
+static void
+remove_destroyed_group (ClutterActor *actor,
+ gpointer user_data)
+{
+ StFocusManager *manager = user_data;
+
+ st_focus_manager_remove_group (manager, ST_WIDGET (actor));
+}
+
+/**
+ * st_focus_manager_add_group:
+ * @manager: the #StFocusManager
+ * @root: the root container of the group
+ *
+ * Adds a new focus group to @manager. When the focus is in an actor
+ * that is a descendant of @root, @manager will handle moving focus
+ * from one actor to another within @root based on keyboard events.
+ */
+void
+st_focus_manager_add_group (StFocusManager *manager,
+ StWidget *root)
+{
+ gpointer count_p = g_hash_table_lookup (manager->priv->groups, root);
+ int count = count_p ? GPOINTER_TO_INT (count_p) : 0;
+
+ g_signal_connect (root, "destroy",
+ G_CALLBACK (remove_destroyed_group),
+ manager);
+ g_hash_table_insert (manager->priv->groups, root, GINT_TO_POINTER (++count));
+}
+
+/**
+ * st_focus_manager_remove_group:
+ * @manager: the #StFocusManager
+ * @root: the root container of the group
+ *
+ * Removes the group rooted at @root from @manager
+ */
+void
+st_focus_manager_remove_group (StFocusManager *manager,
+ StWidget *root)
+{
+ gpointer count_p = g_hash_table_lookup (manager->priv->groups, root);
+ int count = count_p ? GPOINTER_TO_INT (count_p) : 0;
+
+ if (count == 0)
+ return;
+ if (count == 1)
+ g_hash_table_remove (manager->priv->groups, root);
+ else
+ g_hash_table_insert (manager->priv->groups, root, GINT_TO_POINTER(--count));
+}
+
+/**
+ * st_focus_manager_get_group:
+ * @manager: the #StFocusManager
+ * @widget: an #StWidget
+ *
+ * Checks if @widget is inside a focus group, and if so, returns
+ * the root of that group.
+ *
+ * Returns: (transfer none): the focus group root, or %NULL if
+ * @widget is not in a focus group
+ */
+StWidget *
+st_focus_manager_get_group (StFocusManager *manager,
+ StWidget *widget)
+{
+ ClutterActor *actor = CLUTTER_ACTOR (widget);
+
+ while (actor && !g_hash_table_lookup (manager->priv->groups, actor))
+ actor = clutter_actor_get_parent (actor);
+
+ return ST_WIDGET (actor);
+}
+
+/**
+ * st_focus_manager_navigate_from_event:
+ * @manager: the #StFocusManager
+ * @event: a #ClutterEvent
+ *
+ * Try to navigate from @event as if it bubbled all the way up to
+ * the stage. This is useful in complex event handling situations
+ * where you want key navigation, but a parent might be stopping
+ * the key navigation event from bubbling all the way up to the stage.
+ *
+ * Returns: Whether a new actor was navigated to
+ */
+gboolean
+st_focus_manager_navigate_from_event (StFocusManager *manager,
+ ClutterEvent *event)
+{
+ ClutterActor *stage;
+
+ if (event->type != CLUTTER_KEY_PRESS)
+ return FALSE;
+
+ stage = CLUTTER_ACTOR (event->key.stage);
+ return st_focus_manager_stage_event (stage, event, manager);
+}
diff --git a/src/st/st-focus-manager.h b/src/st/st-focus-manager.h
new file mode 100644
index 0000000..ba8442b
--- /dev/null
+++ b/src/st/st-focus-manager.h
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-focus-manager.h: Keyboard focus manager
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_FOCUS_MANAGER_H__
+#define __ST_FOCUS_MANAGER_H__
+
+#include <st/st-types.h>
+#include <st/st-widget.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_FOCUS_MANAGER (st_focus_manager_get_type ())
+G_DECLARE_FINAL_TYPE (StFocusManager, st_focus_manager, ST, FOCUS_MANAGER, GObject)
+
+typedef struct _StFocusManager StFocusManager;
+typedef struct _StFocusManagerPrivate StFocusManagerPrivate;
+
+/**
+ * StFocusManager:
+ *
+ * The #StFocusManager struct contains only private data
+ */
+struct _StFocusManager
+{
+ /*< private >*/
+ GObject parent_instance;
+
+ StFocusManagerPrivate *priv;
+};
+
+StFocusManager *st_focus_manager_get_for_stage (ClutterStage *stage);
+
+void st_focus_manager_add_group (StFocusManager *manager,
+ StWidget *root);
+void st_focus_manager_remove_group (StFocusManager *manager,
+ StWidget *root);
+StWidget *st_focus_manager_get_group (StFocusManager *manager,
+ StWidget *widget);
+gboolean st_focus_manager_navigate_from_event (StFocusManager *manager,
+ ClutterEvent *event);
+
+G_END_DECLS
+
+#endif /* __ST_FOCUS_MANAGER_H__ */
diff --git a/src/st/st-generic-accessible.c b/src/st/st-generic-accessible.c
new file mode 100644
index 0000000..e6cb393
--- /dev/null
+++ b/src/st/st-generic-accessible.c
@@ -0,0 +1,246 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-generic-accessible.c: generic accessible
+ *
+ * Copyright 2013 Igalia, S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-generic-accessible
+ * @short_description: An accessible class with signals for
+ * implementing specific Atk interfaces
+ *
+ * #StGenericAccessible is mainly a workaround for the current lack of
+ * of a proper support for GValue at javascript. See bug#703412 for
+ * more information. We implement the accessible interfaces, but proxy
+ * the virtual functions into signals, which gjs can catch.
+ *
+ * #StGenericAccessible is an #StWidgetAccessible
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "st-generic-accessible.h"
+
+static void atk_value_iface_init (AtkValueIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE(StGenericAccessible,
+ st_generic_accessible,
+ ST_TYPE_WIDGET_ACCESSIBLE,
+ G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE,
+ atk_value_iface_init));
+/* Signals */
+enum
+{
+ GET_CURRENT_VALUE,
+ GET_MAXIMUM_VALUE,
+ GET_MINIMUM_VALUE,
+ SET_CURRENT_VALUE,
+ GET_MINIMUM_INCREMENT,
+ LAST_SIGNAL
+};
+
+static guint st_generic_accessible_signals [LAST_SIGNAL] = { 0 };
+
+static void
+st_generic_accessible_init (StGenericAccessible *accessible)
+{
+}
+
+static void
+st_generic_accessible_class_init (StGenericAccessibleClass *klass)
+{
+ /**
+ * StGenericAccessible::get-current-value:
+ * @self: the #StGenericAccessible
+ *
+ * Emitted when atk_value_get_current_value() is called on
+ * @self. Right now we only care about doubles, so the value is
+ * directly returned by the signal.
+ *
+ * Returns: value of the current element.
+ */
+ st_generic_accessible_signals[GET_CURRENT_VALUE] =
+ g_signal_new ("get-current-value",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_DOUBLE, 0);
+
+ /**
+ * StGenericAccessible::get-maximum-value:
+ * @self: the #StGenericAccessible
+ *
+ * Emitted when atk_value_get_maximum_value() is called on
+ * @self. Right now we only care about doubles, so the value is
+ * directly returned by the signal.
+ *
+ * Returns: maximum value of the accessible.
+ */
+ st_generic_accessible_signals[GET_MAXIMUM_VALUE] =
+ g_signal_new ("get-maximum-value",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_DOUBLE, 0);
+
+ /**
+ * StGenericAccessible::get-minimum-value:
+ * @self: the #StGenericAccessible
+ *
+ * Emitted when atk_value_get_current_value() is called on
+ * @self. Right now we only care about doubles, so the value is
+ * directly returned by the signal.
+ *
+ * Returns: minimum value of the accessible.
+ */
+ st_generic_accessible_signals[GET_MINIMUM_VALUE] =
+ g_signal_new ("get-minimum-value",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_DOUBLE, 0);
+
+ /**
+ * StGenericAccessible::get-minimum-increment:
+ * @self: the #StGenericAccessible
+ *
+ * Emitted when atk_value_get_minimum_increment() is called on
+ * @self. Right now we only care about doubles, so the value is
+ * directly returned by the signal.
+ *
+ * Returns: value of the current element.
+ */
+ st_generic_accessible_signals[GET_MINIMUM_INCREMENT] =
+ g_signal_new ("get-minimum-increment",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_DOUBLE, 0);
+
+ /**
+ * StGenericAccessible::set-current-value:
+ * @self: the #StGenericAccessible
+ * @new_value: the new value for the accessible
+ *
+ * Emitted when atk_value_set_current_value() is called on
+ * @self. Right now we only care about doubles, so the value is
+ * directly returned by the signal.
+ */
+ st_generic_accessible_signals[SET_CURRENT_VALUE] =
+ g_signal_new ("set-current-value",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_DOUBLE);
+
+}
+
+static void
+st_generic_accessible_get_current_value (AtkValue *obj,
+ GValue *value)
+{
+ gdouble current_value = 0;
+
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_signal_emit (G_OBJECT (obj), st_generic_accessible_signals[GET_CURRENT_VALUE], 0, &current_value);
+ g_value_set_double (value, current_value);
+}
+
+static void
+st_generic_accessible_get_maximum_value (AtkValue *obj,
+ GValue *value)
+{
+ gdouble current_value = 0;
+
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_signal_emit (G_OBJECT (obj), st_generic_accessible_signals[GET_MAXIMUM_VALUE], 0, &current_value);
+ g_value_set_double (value, current_value);
+}
+
+static void
+st_generic_accessible_get_minimum_value (AtkValue *obj,
+ GValue *value)
+{
+ gdouble current_value = 0;
+
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_signal_emit (G_OBJECT (obj), st_generic_accessible_signals[GET_MINIMUM_VALUE], 0, &current_value);
+ g_value_set_double (value, current_value);
+}
+
+static void
+st_generic_accessible_get_minimum_increment (AtkValue *obj,
+ GValue *value)
+{
+ gdouble current_value = 0;
+
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_signal_emit (G_OBJECT (obj), st_generic_accessible_signals[GET_MINIMUM_INCREMENT], 0, &current_value);
+ g_value_set_double (value, current_value);
+}
+
+static gboolean
+st_generic_accessible_set_current_value (AtkValue *obj,
+ const GValue *value)
+{
+ gdouble current_value = 0;
+
+ current_value = g_value_get_double (value);
+ g_signal_emit (G_OBJECT (obj), st_generic_accessible_signals[SET_CURRENT_VALUE], 0, current_value);
+
+ return TRUE; // we assume that the value was properly set
+}
+
+static void
+atk_value_iface_init (AtkValueIface *iface)
+{
+ iface->get_current_value = st_generic_accessible_get_current_value;
+ iface->get_maximum_value = st_generic_accessible_get_maximum_value;
+ iface->get_minimum_value = st_generic_accessible_get_minimum_value;
+ iface->get_minimum_increment = st_generic_accessible_get_minimum_increment;
+ iface->set_current_value = st_generic_accessible_set_current_value;
+}
+
+/**
+ * st_generic_accessible_new_for_actor:
+ * @actor: a #Clutter Actor
+ *
+ * Create a new #StGenericAccessible for @actor.
+ *
+ * This is useful only for custom widgets that need a proxy for #AtkObject.
+ *
+ * Returns: (transfer full): a new #AtkObject
+ */
+AtkObject*
+st_generic_accessible_new_for_actor (ClutterActor *actor)
+{
+ AtkObject *accessible = NULL;
+
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
+
+ accessible = ATK_OBJECT (g_object_new (ST_TYPE_GENERIC_ACCESSIBLE,
+ NULL));
+ atk_object_initialize (accessible, actor);
+
+ return accessible;
+}
diff --git a/src/st/st-generic-accessible.h b/src/st/st-generic-accessible.h
new file mode 100644
index 0000000..99a6a71
--- /dev/null
+++ b/src/st/st-generic-accessible.h
@@ -0,0 +1,62 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-generic-accessible.h: generic accessible
+ *
+ * Copyright 2013 Igalia, S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_GENERIC_ACCESSIBLE_H__
+#define __ST_GENERIC_ACCESSIBLE_H__
+
+#include <clutter/clutter.h>
+#include <st/st-widget-accessible.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_GENERIC_ACCESSIBLE (st_generic_accessible_get_type ())
+#define ST_GENERIC_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_GENERIC_ACCESSIBLE, StGenericAccessible))
+#define ST_GENERIC_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_GENERIC_ACCESSIBLE, StGenericAccessibleClass))
+#define ST_IS_GENERIC_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_GENERIC_ACCESSIBLE))
+#define ST_IS_GENERIC_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_GENERIC_ACCESSIBLE))
+#define ST_GENERIC_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_GENERIC_ACCESSIBLE, StGenericAccessibleClass))
+
+typedef struct _StGenericAccessible StGenericAccessible;
+typedef struct _StGenericAccessibleClass StGenericAccessibleClass;
+
+typedef struct _StGenericAccessiblePrivate StGenericAccessiblePrivate;
+
+struct _StGenericAccessible
+{
+ StWidgetAccessible parent;
+
+ StGenericAccessiblePrivate *priv;
+};
+
+struct _StGenericAccessibleClass
+{
+ StWidgetAccessibleClass parent_class;
+};
+
+GType st_generic_accessible_get_type (void) G_GNUC_CONST;
+
+AtkObject* st_generic_accessible_new_for_actor (ClutterActor *actor);
+
+G_END_DECLS
+
+#endif /* __ST_GENERIC_ACCESSIBLE_H__ */
diff --git a/src/st/st-icon-colors.c b/src/st/st-icon-colors.c
new file mode 100644
index 0000000..c6a082a
--- /dev/null
+++ b/src/st/st-icon-colors.c
@@ -0,0 +1,133 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-icon-colors.c: Colors for colorizing a symbolic icon
+ *
+ * Copyright 2010 Red Hat, Inc.
+ * Copyright 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "st-icon-colors.h"
+
+/**
+ * st_icon_colors_new:
+ *
+ * Creates a new #StIconColors. All colors are initialized to transparent black.
+ *
+ * Returns: a newly created #StIconColors. Free with st_icon_colors_unref()
+ */
+StIconColors *
+st_icon_colors_new (void)
+{
+ StIconColors *colors;
+
+ colors = g_new0 (StIconColors, 1);
+ colors->ref_count = 1;
+
+ return colors;
+}
+
+/**
+ * st_icon_colors_ref:
+ * @colors: a #StIconColors
+ *
+ * Atomically increments the reference count of @colors by one.
+ *
+ * Returns: the passed in #StIconColors.
+ */
+StIconColors *
+st_icon_colors_ref (StIconColors *colors)
+{
+ g_return_val_if_fail (colors != NULL, NULL);
+ g_return_val_if_fail (colors->ref_count > 0, colors);
+
+ g_atomic_int_inc ((volatile int *)&colors->ref_count);
+ return colors;
+}
+
+/**
+ * st_icon_colors_unref:
+ * @colors: a #StIconColors
+ *
+ * Atomically decrements the reference count of @colors by one.
+ * If the reference count drops to 0, all memory allocated by the
+ * #StIconColors is released.
+ */
+void
+st_icon_colors_unref (StIconColors *colors)
+{
+ g_return_if_fail (colors != NULL);
+ g_return_if_fail (colors->ref_count > 0);
+
+ if (g_atomic_int_dec_and_test ((volatile int *)&colors->ref_count))
+ g_free (colors);
+}
+
+/**
+ * st_icon_colors_copy:
+ * @colors: a #StIconColors
+ *
+ * Creates a new StIconColors structure that is a copy of the passed
+ * in @colors. You would use this function instead of st_icon_colors_ref()
+ * if you were planning to change colors in the result.
+ *
+ * Returns: a newly created #StIconColors.
+ */
+StIconColors *
+st_icon_colors_copy (StIconColors *colors)
+{
+ StIconColors *copy;
+
+ g_return_val_if_fail (colors != NULL, NULL);
+
+ copy = st_icon_colors_new ();
+
+ copy->foreground = colors->foreground;
+ copy->warning = colors->warning;
+ copy->error = colors->error;
+ copy->success = colors->success;
+
+ return copy;
+}
+
+/**
+ * st_icon_colors_equal:
+ * @colors: a #StIconColors
+ * @other: another #StIconColors
+ *
+ * Check if two #StIconColors objects are identical.
+ *
+ * Returns: %TRUE if the #StIconColors are equal
+ */
+gboolean
+st_icon_colors_equal (StIconColors *colors,
+ StIconColors *other)
+{
+ if (colors == other)
+ return TRUE;
+
+ if (colors == NULL || other == NULL)
+ return FALSE;
+
+ return clutter_color_equal (&colors->foreground, &other->foreground) &&
+ clutter_color_equal (&colors->warning, &other->warning) &&
+ clutter_color_equal (&colors->error, &other->error) &&
+ clutter_color_equal (&colors->success, &other->success);
+}
+
+G_DEFINE_BOXED_TYPE (StIconColors,
+ st_icon_colors,
+ st_icon_colors_ref,
+ st_icon_colors_unref)
diff --git a/src/st/st-icon-colors.h b/src/st/st-icon-colors.h
new file mode 100644
index 0000000..e994a75
--- /dev/null
+++ b/src/st/st-icon-colors.h
@@ -0,0 +1,43 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_ICON_COLORS__
+#define __ST_ICON_COLORS__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_ICON_COLORS (st_icon_colors_get_type ())
+
+typedef struct _StIconColors StIconColors;
+
+/**
+ * StIconColors:
+ * @foreground: foreground color
+ * @warning: color indicating a warning state
+ * @error: color indicating an error state
+ * @success: color indicating a successful operation
+ *
+ * The #StIconColors structure encapsulates colors for colorizing a symbolic
+ * icon.
+ */
+struct _StIconColors {
+ volatile guint ref_count;
+
+ ClutterColor foreground;
+ ClutterColor warning;
+ ClutterColor error;
+ ClutterColor success;
+};
+
+GType st_icon_colors_get_type (void) G_GNUC_CONST;
+
+StIconColors *st_icon_colors_new (void);
+StIconColors *st_icon_colors_ref (StIconColors *colors);
+void st_icon_colors_unref (StIconColors *colors);
+StIconColors *st_icon_colors_copy (StIconColors *colors);
+gboolean st_icon_colors_equal (StIconColors *colors,
+ StIconColors *other);
+
+G_END_DECLS
+
+#endif /* __ST_ICON_COLORS__ */
diff --git a/src/st/st-icon.c b/src/st/st-icon.c
new file mode 100644
index 0000000..6009afe
--- /dev/null
+++ b/src/st/st-icon.c
@@ -0,0 +1,833 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-icon.c: icon widget
+ *
+ * Copyright 2009, 2010 Intel Corporation.
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-icon
+ * @short_description: a simple styled icon actor
+ *
+ * #StIcon is a simple styled texture actor that displays an image from
+ * a stylesheet.
+ */
+
+#include "st-enum-types.h"
+#include "st-icon.h"
+#include "st-texture-cache.h"
+#include "st-theme-context.h"
+#include "st-private.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_GICON,
+ PROP_FALLBACK_GICON,
+
+ PROP_ICON_NAME,
+ PROP_ICON_SIZE,
+ PROP_FALLBACK_ICON_NAME,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+struct _StIconPrivate
+{
+ ClutterActor *icon_texture;
+ ClutterActor *pending_texture;
+ gulong opacity_handler_id;
+
+ GIcon *gicon;
+ gint prop_icon_size; /* icon size set as property */
+ gint theme_icon_size; /* icon size from theme node */
+ gint icon_size; /* icon size we are using */
+ GIcon *fallback_gicon;
+ gboolean needs_update;
+
+ StIconColors *colors;
+
+ CoglPipeline *shadow_pipeline;
+ StShadow *shadow_spec;
+ graphene_size_t shadow_size;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (StIcon, st_icon, ST_TYPE_WIDGET)
+
+static void st_icon_update (StIcon *icon);
+static gboolean st_icon_update_icon_size (StIcon *icon);
+static void st_icon_update_shadow_pipeline (StIcon *icon);
+static void st_icon_clear_shadow_pipeline (StIcon *icon);
+
+static GIcon *default_gicon = NULL;
+
+#define IMAGE_MISSING_ICON_NAME "image-missing"
+#define DEFAULT_ICON_SIZE 48
+
+static void
+st_icon_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StIcon *icon = ST_ICON (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_GICON:
+ st_icon_set_gicon (icon, g_value_get_object (value));
+ break;
+
+ case PROP_FALLBACK_GICON:
+ st_icon_set_fallback_gicon (icon, g_value_get_object (value));
+ break;
+
+ case PROP_ICON_NAME:
+ st_icon_set_icon_name (icon, g_value_get_string (value));
+ break;
+
+ case PROP_ICON_SIZE:
+ st_icon_set_icon_size (icon, g_value_get_int (value));
+ break;
+
+ case PROP_FALLBACK_ICON_NAME:
+ st_icon_set_fallback_icon_name (icon, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_icon_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StIcon *icon = ST_ICON (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_GICON:
+ g_value_set_object (value, st_icon_get_gicon (icon));
+ break;
+
+ case PROP_FALLBACK_GICON:
+ g_value_set_object (value, st_icon_get_fallback_gicon (icon));
+ break;
+
+ case PROP_ICON_NAME:
+ g_value_set_string (value, st_icon_get_icon_name (icon));
+ break;
+
+ case PROP_ICON_SIZE:
+ g_value_set_int (value, st_icon_get_icon_size (icon));
+ break;
+
+ case PROP_FALLBACK_ICON_NAME:
+ g_value_set_string (value, st_icon_get_fallback_icon_name (icon));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_icon_dispose (GObject *gobject)
+{
+ StIconPrivate *priv = ST_ICON (gobject)->priv;
+
+ if (priv->icon_texture)
+ {
+ clutter_actor_destroy (priv->icon_texture);
+ priv->icon_texture = NULL;
+ }
+
+ if (priv->pending_texture)
+ {
+ clutter_actor_destroy (priv->pending_texture);
+ g_object_unref (priv->pending_texture);
+ priv->pending_texture = NULL;
+ }
+
+ g_clear_object (&priv->gicon);
+ g_clear_object (&priv->fallback_gicon);
+ g_clear_pointer (&priv->colors, st_icon_colors_unref);
+ g_clear_pointer (&priv->shadow_pipeline, cogl_object_unref);
+ g_clear_pointer (&priv->shadow_spec, st_shadow_unref);
+
+ G_OBJECT_CLASS (st_icon_parent_class)->dispose (gobject);
+}
+
+static void
+st_icon_paint (ClutterActor *actor,
+ ClutterPaintContext *paint_context)
+{
+ StIcon *icon = ST_ICON (actor);
+ StIconPrivate *priv = icon->priv;
+
+ st_widget_paint_background (ST_WIDGET (actor), paint_context);
+
+ if (priv->icon_texture)
+ {
+ st_icon_update_shadow_pipeline (icon);
+
+ if (priv->shadow_pipeline)
+ {
+ ClutterActorBox allocation;
+ CoglFramebuffer *framebuffer;
+
+ clutter_actor_get_allocation_box (priv->icon_texture, &allocation);
+ framebuffer = clutter_paint_context_get_framebuffer (paint_context);
+ _st_paint_shadow_with_opacity (priv->shadow_spec,
+ framebuffer,
+ priv->shadow_pipeline,
+ &allocation,
+ clutter_actor_get_paint_opacity (priv->icon_texture));
+ }
+
+ clutter_actor_paint (priv->icon_texture, paint_context);
+ }
+}
+
+static void
+st_icon_style_changed (StWidget *widget)
+{
+ StIcon *self = ST_ICON (widget);
+ StThemeNode *theme_node = st_widget_get_theme_node (widget);
+ StIconPrivate *priv = self->priv;
+ gboolean should_update = FALSE;
+ g_autoptr(StShadow) shadow_spec = NULL;
+ StIconColors *colors;
+
+ shadow_spec = st_theme_node_get_shadow (theme_node, "icon-shadow");
+
+ if (shadow_spec && shadow_spec->inset)
+ {
+ g_warning ("The icon-shadow property does not support inset shadows");
+ g_clear_pointer (&shadow_spec, st_shadow_unref);
+ }
+
+ if ((shadow_spec && priv->shadow_spec && !st_shadow_equal (shadow_spec, priv->shadow_spec)) ||
+ (shadow_spec && !priv->shadow_spec) || (!shadow_spec && priv->shadow_spec))
+ {
+ st_icon_clear_shadow_pipeline (self);
+
+ g_clear_pointer (&priv->shadow_spec, st_shadow_unref);
+ priv->shadow_spec = g_steal_pointer (&shadow_spec);
+
+ should_update = TRUE;
+ }
+
+ colors = st_theme_node_get_icon_colors (theme_node);
+
+ if ((colors && priv->colors && !st_icon_colors_equal (colors, priv->colors)) ||
+ (colors && !priv->colors) || (!colors && priv->colors))
+ {
+ g_clear_pointer (&priv->colors, st_icon_colors_unref);
+ priv->colors = st_icon_colors_ref (colors);
+
+ should_update = TRUE;
+ }
+
+ priv->theme_icon_size = (int)(0.5 + st_theme_node_get_length (theme_node, "icon-size"));
+
+ should_update |= st_icon_update_icon_size (self);
+
+ if (priv->needs_update || should_update)
+ st_icon_update (self);
+
+ ST_WIDGET_CLASS (st_icon_parent_class)->style_changed (widget);
+}
+
+static void
+st_icon_resource_scale_changed (ClutterActor *actor)
+{
+ st_icon_update (ST_ICON (actor));
+
+ if (CLUTTER_ACTOR_CLASS (st_icon_parent_class)->resource_scale_changed)
+ CLUTTER_ACTOR_CLASS (st_icon_parent_class)->resource_scale_changed (actor);
+}
+
+static void
+st_icon_class_init (StIconClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ object_class->get_property = st_icon_get_property;
+ object_class->set_property = st_icon_set_property;
+ object_class->dispose = st_icon_dispose;
+
+ actor_class->paint = st_icon_paint;
+
+ widget_class->style_changed = st_icon_style_changed;
+ actor_class->resource_scale_changed = st_icon_resource_scale_changed;
+
+ /**
+ * StIcon:gicon:
+ *
+ * The #GIcon being displayed by this #StIcon.
+ */
+ props[PROP_GICON] =
+ g_param_spec_object ("gicon",
+ "GIcon",
+ "The GIcon shown by this icon actor",
+ G_TYPE_ICON,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StIcon:fallback-gicon:
+ *
+ * The fallback #GIcon to display if #StIcon:gicon fails to load.
+ */
+ props[PROP_FALLBACK_GICON] =
+ g_param_spec_object ("fallback-gicon",
+ "Fallback GIcon",
+ "The fallback GIcon shown if the normal icon fails to load",
+ G_TYPE_ICON,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StIcon:icon-name:
+ *
+ * The name of the icon if the icon being displayed is a #GThemedIcon.
+ */
+ props[PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name",
+ "Icon name",
+ "An icon name",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StIcon:icon-size:
+ *
+ * The size of the icon, if greater than `0`. Other the icon size is derived
+ * from the current style.
+ */
+ props[PROP_ICON_SIZE] =
+ g_param_spec_int ("icon-size",
+ "Icon size",
+ "The size if the icon, if positive. Otherwise the size will be derived from the current style",
+ -1, G_MAXINT, -1,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StIcon:fallback-icon-name:
+ *
+ * The fallback icon name of the #StIcon. See st_icon_set_fallback_icon_name()
+ * for details.
+ */
+ props[PROP_FALLBACK_ICON_NAME] =
+ g_param_spec_string ("fallback-icon-name",
+ "Fallback icon name",
+ "A fallback icon name",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+st_icon_init (StIcon *self)
+{
+ ClutterLayoutManager *layout_manager;
+
+ if (G_UNLIKELY (default_gicon == NULL))
+ default_gicon = g_themed_icon_new (IMAGE_MISSING_ICON_NAME);
+
+ self->priv = st_icon_get_instance_private (self);
+
+ layout_manager = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_FILL,
+ CLUTTER_BIN_ALIGNMENT_FILL);
+ clutter_actor_set_layout_manager (CLUTTER_ACTOR (self), layout_manager);
+
+ /* Set the icon size to -1 here to make sure we apply the scale to the
+ * default size on the first "style-changed" signal. */
+ self->priv->icon_size = -1;
+ self->priv->prop_icon_size = -1;
+
+ self->priv->shadow_pipeline = NULL;
+}
+
+static void
+st_icon_clear_shadow_pipeline (StIcon *icon)
+{
+ StIconPrivate *priv = icon->priv;
+
+ g_clear_pointer (&priv->shadow_pipeline, cogl_object_unref);
+ graphene_size_init (&priv->shadow_size, 0, 0);
+}
+
+static void
+st_icon_update_shadow_pipeline (StIcon *icon)
+{
+ StIconPrivate *priv = icon->priv;
+
+ if (priv->icon_texture && priv->shadow_spec)
+ {
+ ClutterActorBox box;
+ float width, height;
+
+ clutter_actor_get_allocation_box (CLUTTER_ACTOR (priv->icon_texture),
+ &box);
+ clutter_actor_box_get_size (&box, &width, &height);
+
+ if (priv->shadow_pipeline == NULL ||
+ priv->shadow_size.width != width ||
+ priv->shadow_size.height != height)
+ {
+ st_icon_clear_shadow_pipeline (icon);
+
+ priv->shadow_pipeline =
+ _st_create_shadow_pipeline_from_actor (priv->shadow_spec,
+ priv->icon_texture);
+
+ if (priv->shadow_pipeline)
+ graphene_size_init (&priv->shadow_size, width, height);
+ }
+ }
+}
+
+static void
+on_content_changed (ClutterActor *actor,
+ GParamSpec *pspec,
+ StIcon *icon)
+{
+ st_icon_clear_shadow_pipeline (icon);
+}
+
+static void
+st_icon_finish_update (StIcon *icon)
+{
+ StIconPrivate *priv = icon->priv;
+
+ if (priv->icon_texture)
+ {
+ clutter_actor_destroy (priv->icon_texture);
+ priv->icon_texture = NULL;
+ }
+
+ if (priv->pending_texture)
+ {
+ priv->icon_texture = priv->pending_texture;
+ priv->pending_texture = NULL;
+ clutter_actor_set_x_align (priv->icon_texture, CLUTTER_ACTOR_ALIGN_CENTER);
+ clutter_actor_set_y_align (priv->icon_texture, CLUTTER_ACTOR_ALIGN_CENTER);
+ clutter_actor_add_child (CLUTTER_ACTOR (icon), priv->icon_texture);
+
+ /* Remove the temporary ref we added */
+ g_object_unref (priv->icon_texture);
+
+ st_icon_clear_shadow_pipeline (icon);
+
+ g_signal_connect_object (priv->icon_texture, "notify::content",
+ G_CALLBACK (on_content_changed), icon, 0);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (icon));
+}
+
+static void
+opacity_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ StIcon *icon = user_data;
+ StIconPrivate *priv = icon->priv;
+
+ g_clear_signal_handler (&priv->opacity_handler_id, priv->pending_texture);
+
+ st_icon_finish_update (icon);
+}
+
+static void
+st_icon_update (StIcon *icon)
+{
+ StIconPrivate *priv = icon->priv;
+ StThemeNode *theme_node;
+ StTextureCache *cache;
+ gint paint_scale;
+ ClutterActor *stage;
+ StThemeContext *context;
+ float resource_scale;
+
+ if (priv->pending_texture)
+ {
+ clutter_actor_destroy (priv->pending_texture);
+ g_object_unref (priv->pending_texture);
+ priv->pending_texture = NULL;
+ priv->opacity_handler_id = 0;
+ }
+
+ if (priv->gicon == NULL && priv->fallback_gicon == NULL)
+ {
+ g_clear_pointer (&priv->icon_texture, clutter_actor_destroy);
+ return;
+ }
+
+ priv->needs_update = TRUE;
+
+ theme_node = st_widget_peek_theme_node (ST_WIDGET (icon));
+ if (theme_node == NULL)
+ return;
+
+ if (priv->icon_size <= 0)
+ return;
+
+ resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (icon));
+
+ stage = clutter_actor_get_stage (CLUTTER_ACTOR (icon));
+ context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage));
+ g_object_get (context, "scale-factor", &paint_scale, NULL);
+
+ cache = st_texture_cache_get_default ();
+
+ if (priv->gicon != NULL)
+ priv->pending_texture = st_texture_cache_load_gicon (cache,
+ theme_node,
+ priv->gicon,
+ priv->icon_size / paint_scale,
+ paint_scale,
+ resource_scale);
+
+ if (priv->pending_texture == NULL && priv->fallback_gicon != NULL)
+ priv->pending_texture = st_texture_cache_load_gicon (cache,
+ theme_node,
+ priv->fallback_gicon,
+ priv->icon_size / paint_scale,
+ paint_scale,
+ resource_scale);
+
+ if (priv->pending_texture == NULL)
+ priv->pending_texture = st_texture_cache_load_gicon (cache,
+ theme_node,
+ default_gicon,
+ priv->icon_size / paint_scale,
+ paint_scale,
+ resource_scale);
+ priv->needs_update = FALSE;
+
+ if (priv->pending_texture)
+ {
+ g_object_ref_sink (priv->pending_texture);
+
+ if (clutter_actor_get_opacity (priv->pending_texture) != 0 || priv->icon_texture == NULL)
+ {
+ /* This icon is ready for showing, or nothing else is already showing */
+ st_icon_finish_update (icon);
+ }
+ else
+ {
+ /* Will be shown when fully loaded */
+ priv->opacity_handler_id = g_signal_connect_object (priv->pending_texture, "notify::opacity", G_CALLBACK (opacity_changed_cb), icon, 0);
+ }
+ }
+ else if (priv->icon_texture)
+ {
+ clutter_actor_destroy (priv->icon_texture);
+ priv->icon_texture = NULL;
+ }
+}
+
+static gboolean
+st_icon_update_icon_size (StIcon *icon)
+{
+ StIconPrivate *priv = icon->priv;
+ int new_size;
+ gint scale = 1;
+ ClutterActor *stage;
+ StThemeContext *context;
+
+ stage = clutter_actor_get_stage (CLUTTER_ACTOR (icon));
+ if (stage != NULL)
+ {
+ context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage));
+ g_object_get (context, "scale-factor", &scale, NULL);
+ }
+
+ if (priv->prop_icon_size > 0)
+ new_size = priv->prop_icon_size * scale;
+ else if (priv->theme_icon_size > 0)
+ new_size = priv->theme_icon_size;
+ else
+ new_size = DEFAULT_ICON_SIZE * scale;
+
+ if (new_size != priv->icon_size)
+ {
+ priv->icon_size = new_size;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * st_icon_new:
+ *
+ * Create a newly allocated #StIcon.
+ *
+ * Returns: A newly allocated #StIcon
+ */
+ClutterActor *
+st_icon_new (void)
+{
+ return g_object_new (ST_TYPE_ICON, NULL);
+}
+
+/**
+ * st_icon_get_icon_name:
+ * @icon: an #StIcon
+ *
+ * This is a convenience method to get the icon name of the current icon, if it
+ * is currenyly a #GThemedIcon, or %NULL otherwise.
+ *
+ * Returns: (transfer none) (nullable): The name of the icon or %NULL
+ */
+const gchar *
+st_icon_get_icon_name (StIcon *icon)
+{
+ StIconPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ICON (icon), NULL);
+
+ priv = icon->priv;
+
+ if (priv->gicon && G_IS_THEMED_ICON (priv->gicon))
+ return g_themed_icon_get_names (G_THEMED_ICON (priv->gicon)) [0];
+ else
+ return NULL;
+}
+
+/**
+ * st_icon_set_icon_name:
+ * @icon: an #StIcon
+ * @icon_name: (nullable): the name of the icon
+ *
+ * This is a convenience method to set the #GIcon to a #GThemedIcon created
+ * using the given icon name. If @icon_name is an empty string, %NULL or
+ * fails to load, the fallback icon will be shown.
+ */
+void
+st_icon_set_icon_name (StIcon *icon,
+ const gchar *icon_name)
+{
+ g_autoptr(GIcon) gicon = NULL;
+
+ g_return_if_fail (ST_IS_ICON (icon));
+
+ if (g_strcmp0 (icon_name, st_icon_get_icon_name (icon)) == 0)
+ return;
+
+ if (icon_name && *icon_name)
+ gicon = g_themed_icon_new_with_default_fallbacks (icon_name);
+
+ g_object_freeze_notify (G_OBJECT (icon));
+
+ st_icon_set_gicon (icon, gicon);
+ g_object_notify_by_pspec (G_OBJECT (icon), props[PROP_ICON_NAME]);
+
+ g_object_thaw_notify (G_OBJECT (icon));
+}
+
+/**
+ * st_icon_get_gicon:
+ * @icon: an #StIcon
+ *
+ * Gets the current #GIcon in use.
+ *
+ * Returns: (nullable) (transfer none): The current #GIcon, if set, otherwise %NULL
+ */
+GIcon *
+st_icon_get_gicon (StIcon *icon)
+{
+ g_return_val_if_fail (ST_IS_ICON (icon), NULL);
+
+ return icon->priv->gicon;
+}
+
+/**
+ * st_icon_set_gicon:
+ * @icon: an #StIcon
+ * @gicon: (nullable): a #GIcon
+ *
+ * Sets a #GIcon to show for the icon. If @gicon is %NULL or fails to load,
+ * the fallback icon set using st_icon_set_fallback_icon() will be shown.
+ */
+void
+st_icon_set_gicon (StIcon *icon, GIcon *gicon)
+{
+ g_return_if_fail (ST_IS_ICON (icon));
+ g_return_if_fail (gicon == NULL || G_IS_ICON (gicon));
+
+ if (g_icon_equal (icon->priv->gicon, gicon)) /* do nothing */
+ return;
+
+ g_set_object (&icon->priv->gicon, gicon);
+ g_object_notify_by_pspec (G_OBJECT (icon), props[PROP_GICON]);
+
+ st_icon_update (icon);
+}
+
+/**
+ * st_icon_get_fallback_gicon:
+ * @icon: a #StIcon
+ *
+ * Gets the currently set fallback #GIcon.
+ *
+ * Returns: (transfer none): The fallback #GIcon, if set, otherwise %NULL
+ */
+GIcon *
+st_icon_get_fallback_gicon (StIcon *icon)
+{
+ g_return_val_if_fail (ST_IS_ICON (icon), NULL);
+
+ return icon->priv->fallback_gicon;
+}
+
+/**
+ * st_icon_set_fallback_gicon:
+ * @icon: a #StIcon
+ * @fallback_gicon: (nullable): the fallback #GIcon
+ *
+ * Sets a fallback #GIcon to show if the normal icon fails to load.
+ * If @fallback_gicon is %NULL or fails to load, the icon is unset and no
+ * texture will be visible for the fallback icon.
+ */
+void
+st_icon_set_fallback_gicon (StIcon *icon,
+ GIcon *fallback_gicon)
+{
+ g_return_if_fail (ST_IS_ICON (icon));
+ g_return_if_fail (fallback_gicon == NULL || G_IS_ICON (fallback_gicon));
+
+ if (g_icon_equal (icon->priv->fallback_gicon, fallback_gicon))
+ return;
+
+ g_set_object (&icon->priv->fallback_gicon, fallback_gicon);
+ g_object_notify_by_pspec (G_OBJECT (icon), props[PROP_FALLBACK_GICON]);
+
+ st_icon_update (icon);
+}
+
+/**
+ * st_icon_get_icon_size:
+ * @icon: an #StIcon
+ *
+ * Gets the explicit size set using st_icon_set_icon_size() for the icon.
+ * This is not necessarily the size that the icon will be displayed at.
+ *
+ * Returns: The explicitly set size, or -1 if no size has been set
+ */
+gint
+st_icon_get_icon_size (StIcon *icon)
+{
+ g_return_val_if_fail (ST_IS_ICON (icon), -1);
+
+ return icon->priv->prop_icon_size;
+}
+
+/**
+ * st_icon_set_icon_size:
+ * @icon: an #StIcon
+ * @size: if positive, the new size, otherwise the size will be
+ * derived from the current style
+ *
+ * Sets an explicit size for the icon. Setting @size to -1 will use the size
+ * defined by the current style or the default icon size.
+ */
+void
+st_icon_set_icon_size (StIcon *icon,
+ gint size)
+{
+ StIconPrivate *priv;
+
+ g_return_if_fail (ST_IS_ICON (icon));
+
+ priv = icon->priv;
+ if (priv->prop_icon_size != size)
+ {
+ priv->prop_icon_size = size;
+ if (st_icon_update_icon_size (icon))
+ st_icon_update (icon);
+ g_object_notify_by_pspec (G_OBJECT (icon), props[PROP_ICON_SIZE]);
+ }
+}
+
+/**
+ * st_icon_get_fallback_icon_name:
+ * @icon: an #StIcon
+ *
+ * This is a convenience method to get the icon name of the fallback
+ * #GThemedIcon that is currently set.
+ *
+ * Returns: (transfer none): The name of the icon or %NULL if no icon is set
+ */
+const gchar *
+st_icon_get_fallback_icon_name (StIcon *icon)
+{
+ StIconPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ICON (icon), NULL);
+
+ priv = icon->priv;
+
+ if (priv->fallback_gicon && G_IS_THEMED_ICON (priv->fallback_gicon))
+ return g_themed_icon_get_names (G_THEMED_ICON (priv->fallback_gicon)) [0];
+ else
+ return NULL;
+}
+
+/**
+ * st_icon_set_fallback_icon_name:
+ * @icon: an #StIcon
+ * @fallback_icon_name: (nullable): the name of the fallback icon
+ *
+ * This is a convenience method to set the fallback #GIcon to a #GThemedIcon
+ * created using the given icon name. If @fallback_icon_name is an empty
+ * string, %NULL or fails to load, the icon is unset and no texture will
+ * be visible for the fallback icon.
+ */
+void
+st_icon_set_fallback_icon_name (StIcon *icon,
+ const gchar *fallback_icon_name)
+{
+ g_autoptr(GIcon) gicon = NULL;
+
+ g_return_if_fail (ST_IS_ICON (icon));
+
+ if (g_strcmp0 (fallback_icon_name, st_icon_get_fallback_icon_name (icon)) == 0)
+ return;
+
+ if (fallback_icon_name && *fallback_icon_name)
+ gicon = g_themed_icon_new_with_default_fallbacks (fallback_icon_name);
+
+ g_object_freeze_notify (G_OBJECT (icon));
+
+ st_icon_set_fallback_gicon (icon, gicon);
+ g_object_notify_by_pspec (G_OBJECT (icon), props[PROP_FALLBACK_ICON_NAME]);
+
+ g_object_thaw_notify (G_OBJECT (icon));
+}
diff --git a/src/st/st-icon.h b/src/st/st-icon.h
new file mode 100644
index 0000000..8714ef9
--- /dev/null
+++ b/src/st/st-icon.h
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-icon.h: icon widget
+ *
+ * Copyright 2009, 2010 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef _ST_ICON
+#define _ST_ICON
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <st/st-widget.h>
+
+#include <st/st-types.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_ICON st_icon_get_type()
+G_DECLARE_FINAL_TYPE (StIcon, st_icon, ST, ICON, StWidget)
+
+typedef struct _StIconPrivate StIconPrivate;
+
+/**
+ * StIcon:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _StIcon {
+ /*< private >*/
+ StWidget parent;
+
+ StIconPrivate *priv;
+};
+
+ClutterActor* st_icon_new (void);
+
+GIcon *st_icon_get_gicon (StIcon *icon);
+void st_icon_set_gicon (StIcon *icon,
+ GIcon *gicon);
+
+GIcon *st_icon_get_fallback_gicon (StIcon *icon);
+void st_icon_set_fallback_gicon (StIcon *icon,
+ GIcon *fallback_gicon);
+
+const gchar *st_icon_get_icon_name (StIcon *icon);
+void st_icon_set_icon_name (StIcon *icon,
+ const gchar *icon_name);
+
+const gchar *st_icon_get_fallback_icon_name (StIcon *icon);
+void st_icon_set_fallback_icon_name (StIcon *icon,
+ const gchar *fallback_icon_name);
+
+gint st_icon_get_icon_size (StIcon *icon);
+void st_icon_set_icon_size (StIcon *icon,
+ gint size);
+
+G_END_DECLS
+
+#endif /* _ST_ICON */
+
diff --git a/src/st/st-image-content.c b/src/st/st-image-content.c
new file mode 100644
index 0000000..92f1c14
--- /dev/null
+++ b/src/st/st-image-content.c
@@ -0,0 +1,346 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-image-content.h: A content image with scaling support
+ *
+ * Copyright 2019 Canonical, Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "st-image-content.h"
+#include "st-private.h"
+
+struct _StImageContent
+{
+ /*< private >*/
+ ClutterImage parent_instance;
+};
+
+typedef struct _StImageContentPrivate StImageContentPrivate;
+struct _StImageContentPrivate
+{
+ int width;
+ int height;
+};
+
+enum
+{
+ PROP_0,
+ PROP_PREFERRED_WIDTH,
+ PROP_PREFERRED_HEIGHT,
+};
+
+static void clutter_content_interface_init (ClutterContentInterface *iface);
+static void g_icon_interface_init (GIconIface *iface);
+static void g_loadable_icon_interface_init (GLoadableIconIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (StImageContent, st_image_content, CLUTTER_TYPE_IMAGE,
+ G_ADD_PRIVATE (StImageContent)
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
+ clutter_content_interface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
+ g_icon_interface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON,
+ g_loadable_icon_interface_init))
+
+static void
+st_image_content_init (StImageContent *self)
+{
+}
+
+static void
+st_image_content_constructed (GObject *object)
+{
+ StImageContent *self = ST_IMAGE_CONTENT (object);
+ StImageContentPrivate *priv = st_image_content_get_instance_private (self);
+
+ if (priv->width < 0 || priv->height < 0)
+ g_warning ("StImageContent initialized with invalid preferred size: %dx%d\n",
+ priv->width, priv->height);
+}
+
+static void
+st_image_content_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StImageContent *self = ST_IMAGE_CONTENT (object);
+ StImageContentPrivate *priv = st_image_content_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_PREFERRED_WIDTH:
+ g_value_set_int (value, priv->width);
+ break;
+
+ case PROP_PREFERRED_HEIGHT:
+ g_value_set_int (value, priv->height);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_image_content_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StImageContent *self = ST_IMAGE_CONTENT (object);
+ StImageContentPrivate *priv = st_image_content_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_PREFERRED_WIDTH:
+ priv->width = g_value_get_int (value);
+ break;
+
+ case PROP_PREFERRED_HEIGHT:
+ priv->height = g_value_get_int (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_image_content_class_init (StImageContentClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = st_image_content_constructed;
+ object_class->get_property = st_image_content_get_property;
+ object_class->set_property = st_image_content_set_property;
+
+ pspec = g_param_spec_int ("preferred-width",
+ "Preferred Width",
+ "Preferred Width of the Content when painted",
+ -1, G_MAXINT, -1,
+ G_PARAM_CONSTRUCT_ONLY | ST_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_PREFERRED_WIDTH, pspec);
+
+ pspec = g_param_spec_int ("preferred-height",
+ "Preferred Height",
+ "Preferred Height of the Content when painted",
+ -1, G_MAXINT, -1,
+ G_PARAM_CONSTRUCT_ONLY | ST_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_PREFERRED_HEIGHT, pspec);
+}
+
+static gboolean
+st_image_content_get_preferred_size (ClutterContent *content,
+ float *width,
+ float *height)
+{
+ StImageContent *self = ST_IMAGE_CONTENT (content);
+ StImageContentPrivate *priv = st_image_content_get_instance_private (self);
+ CoglTexture *texture;
+
+ texture = clutter_image_get_texture (CLUTTER_IMAGE (content));
+
+ if (texture == NULL)
+ return FALSE;
+
+ g_assert_cmpint (priv->width, >, -1);
+ g_assert_cmpint (priv->height, >, -1);
+
+ if (width != NULL)
+ *width = (float) priv->width;
+
+ if (height != NULL)
+ *height = (float) priv->height;
+
+ return TRUE;
+}
+
+static GdkPixbuf*
+pixbuf_from_image (StImageContent *image)
+{
+ CoglTexture *texture;
+ int width, height, rowstride;
+ uint8_t *data;
+
+ texture = clutter_image_get_texture (CLUTTER_IMAGE (image));
+ if (!texture || !cogl_texture_is_get_data_supported (texture))
+ return NULL;
+
+ width = cogl_texture_get_width (texture);
+ height = cogl_texture_get_width (texture);
+ rowstride = 4 * width;
+ data = g_new (uint8_t, rowstride * height);
+
+ cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_RGBA_8888, rowstride, data);
+
+ return gdk_pixbuf_new_from_data ((const guchar *)data,
+ GDK_COLORSPACE_RGB,
+ TRUE, 8, width, height, rowstride,
+ (GdkPixbufDestroyNotify)g_free, NULL);
+}
+
+static void
+clutter_content_interface_init (ClutterContentInterface *iface)
+{
+ iface->get_preferred_size = st_image_content_get_preferred_size;
+}
+
+static guint
+st_image_content_hash (GIcon *icon)
+{
+ return g_direct_hash (icon);
+}
+
+static gboolean
+st_image_content_equal (GIcon *icon1,
+ GIcon *icon2)
+{
+ return g_direct_equal (icon1, icon2);
+}
+
+static GVariant *
+st_image_content_serialize (GIcon *icon)
+{
+ g_autoptr (GdkPixbuf) pixbuf = NULL;
+
+ pixbuf = pixbuf_from_image (ST_IMAGE_CONTENT (icon));
+ if (!pixbuf)
+ return NULL;
+
+ return g_icon_serialize (G_ICON (pixbuf));
+}
+
+static void
+g_icon_interface_init (GIconIface *iface)
+{
+ iface->hash = st_image_content_hash;
+ iface->equal = st_image_content_equal;
+ iface->serialize = st_image_content_serialize;
+}
+
+static GInputStream *
+st_image_load (GLoadableIcon *icon,
+ int size,
+ char **type,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr (GdkPixbuf) pixbuf = NULL;
+
+ pixbuf = pixbuf_from_image (ST_IMAGE_CONTENT (icon));
+ if (!pixbuf)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to read texture");
+ return NULL;
+ }
+
+ return g_loadable_icon_load (G_LOADABLE_ICON (pixbuf),
+ size, type, cancellable, error);
+}
+
+static void
+load_image_thread (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GInputStream *stream;
+ GError *error = NULL;
+ char *type;
+
+ stream = st_image_load (G_LOADABLE_ICON (object),
+ GPOINTER_TO_INT (task_data),
+ &type,
+ cancellable,
+ &error);
+
+ if (error)
+ {
+ g_task_return_error (task, error);
+ }
+ else
+ {
+ g_task_set_task_data (task, type, g_free);
+ g_task_return_pointer (task, stream, g_object_unref);
+ }
+}
+
+static void
+st_image_load_async (GLoadableIcon *icon,
+ int size,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = NULL;
+
+ task = g_task_new (icon, cancellable, callback, user_data);
+ g_task_set_task_data (task, GINT_TO_POINTER (size), NULL);
+ g_task_run_in_thread (task, load_image_thread);
+}
+
+static GInputStream *
+st_image_load_finish (GLoadableIcon *icon,
+ GAsyncResult *res,
+ char **type,
+ GError **error)
+{
+ GInputStream *stream;
+
+ stream = g_task_propagate_pointer (G_TASK (res), error);
+ if (!stream)
+ return NULL;
+
+ if (type)
+ *type = g_strdup (g_task_get_task_data (G_TASK (res)));
+
+ return stream;
+}
+
+static void
+g_loadable_icon_interface_init (GLoadableIconIface *iface)
+{
+ iface->load = st_image_load;
+ iface->load_async = st_image_load_async;
+ iface->load_finish = st_image_load_finish;
+}
+
+/**
+ * st_image_content_new_with_preferred_size:
+ * @width: The preferred width to be used when drawing the content
+ * @height: The preferred width to be used when drawing the content
+ *
+ * Creates a new #StImageContent, a simple content for sized images.
+ *
+ * See #ClutterImage for setting the actual image to display or #StIcon for
+ * displaying icons.
+ *
+ * Returns: (transfer full): the newly created #StImageContent content
+ * Use g_object_unref() when done.
+ */
+ClutterContent *
+st_image_content_new_with_preferred_size (int width,
+ int height)
+{
+ return g_object_new (ST_TYPE_IMAGE_CONTENT,
+ "preferred-width", width,
+ "preferred-height", height,
+ NULL);
+}
diff --git a/src/st/st-image-content.h b/src/st/st-image-content.h
new file mode 100644
index 0000000..0ebb0b7
--- /dev/null
+++ b/src/st/st-image-content.h
@@ -0,0 +1,33 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-image-content.h: A content image with scaling support
+ *
+ * Copyright 2019 Canonical, Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_IMAGE_CONTENT_H__
+#define __ST_IMAGE_CONTENT_H__
+
+#include <clutter/clutter.h>
+
+#define ST_TYPE_IMAGE_CONTENT (st_image_content_get_type ())
+G_DECLARE_FINAL_TYPE (StImageContent, st_image_content,
+ ST, IMAGE_CONTENT, ClutterImage)
+
+ClutterContent *st_image_content_new_with_preferred_size (int width,
+ int height);
+
+#endif /* __ST_IMAGE_CONTENT_H__ */
diff --git a/src/st/st-label.c b/src/st/st-label.c
new file mode 100644
index 0000000..fe77743
--- /dev/null
+++ b/src/st/st-label.c
@@ -0,0 +1,549 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-label.c: Plain label actor
+ *
+ * Copyright 2008,2009 Intel Corporation
+ * Copyright 2009 Red Hat, Inc.
+ * Copyright 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-label
+ * @short_description: Widget for displaying text
+ *
+ * #StLabel is a simple widget for displaying text. It derives from
+ * #StWidget to add extra style and placement functionality over
+ * #ClutterText. The internal #ClutterText is publicly accessibly to allow
+ * applications to set further properties.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "st-label.h"
+#include "st-private.h"
+#include "st-widget.h"
+
+#include <st/st-widget-accessible.h>
+
+enum
+{
+ PROP_0,
+
+ PROP_CLUTTER_TEXT,
+ PROP_TEXT,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+struct _StLabelPrivate
+{
+ ClutterActor *label;
+
+ StShadow *shadow_spec;
+
+ CoglPipeline *text_shadow_pipeline;
+ float shadow_width;
+ float shadow_height;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (StLabel, st_label, ST_TYPE_WIDGET);
+
+static GType st_label_accessible_get_type (void) G_GNUC_CONST;
+
+static void
+st_label_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StLabel *label = ST_LABEL (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_TEXT:
+ st_label_set_text (label, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_label_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StLabelPrivate *priv = ST_LABEL (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_CLUTTER_TEXT:
+ g_value_set_object (value, priv->label);
+ break;
+
+ case PROP_TEXT:
+ g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_label_style_changed (StWidget *self)
+{
+ StLabelPrivate *priv = ST_LABEL(self)->priv;
+ StThemeNode *theme_node;
+ StShadow *shadow_spec;
+
+ theme_node = st_widget_get_theme_node (self);
+
+ shadow_spec = st_theme_node_get_text_shadow (theme_node);
+ if (!priv->shadow_spec || !shadow_spec ||
+ !st_shadow_equal (shadow_spec, priv->shadow_spec))
+ {
+ g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref);
+
+ g_clear_pointer (&priv->shadow_spec, st_shadow_unref);
+ if (shadow_spec)
+ priv->shadow_spec = st_shadow_ref (shadow_spec);
+ }
+
+ _st_set_text_from_style ((ClutterText *)priv->label, st_widget_get_theme_node (self));
+
+ ST_WIDGET_CLASS (st_label_parent_class)->style_changed (self);
+}
+
+static void
+st_label_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StLabelPrivate *priv = ST_LABEL (actor)->priv;
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+
+ st_theme_node_adjust_for_height (theme_node, &for_height);
+
+ clutter_actor_get_preferred_width (priv->label, for_height,
+ min_width_p,
+ natural_width_p);
+
+ st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
+}
+
+static void
+st_label_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StLabelPrivate *priv = ST_LABEL (actor)->priv;
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ clutter_actor_get_preferred_height (priv->label, for_width,
+ min_height_p,
+ natural_height_p);
+
+ st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
+}
+
+static void
+st_label_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ StLabelPrivate *priv = ST_LABEL (actor)->priv;
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ ClutterActorBox content_box;
+
+ clutter_actor_set_allocation (actor, box);
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ clutter_actor_allocate (priv->label, &content_box);
+}
+
+static void
+st_label_dispose (GObject *object)
+{
+ StLabelPrivate *priv = ST_LABEL (object)->priv;
+
+ priv->label = NULL;
+ g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref);
+
+ G_OBJECT_CLASS (st_label_parent_class)->dispose (object);
+}
+
+static void
+st_label_paint (ClutterActor *actor,
+ ClutterPaintContext *paint_context)
+{
+ StLabelPrivate *priv = ST_LABEL (actor)->priv;
+
+ st_widget_paint_background (ST_WIDGET (actor), paint_context);
+
+ if (priv->shadow_spec)
+ {
+ ClutterActorBox allocation;
+ float width, height;
+ float resource_scale;
+
+ clutter_actor_get_allocation_box (priv->label, &allocation);
+ clutter_actor_box_get_size (&allocation, &width, &height);
+
+ resource_scale = clutter_actor_get_resource_scale (priv->label);
+
+ width *= resource_scale;
+ height *= resource_scale;
+
+ if (priv->text_shadow_pipeline == NULL ||
+ width != priv->shadow_width ||
+ height != priv->shadow_height)
+ {
+ g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref);
+
+ priv->shadow_width = width;
+ priv->shadow_height = height;
+ priv->text_shadow_pipeline =
+ _st_create_shadow_pipeline_from_actor (priv->shadow_spec,
+ priv->label);
+ }
+
+ if (priv->text_shadow_pipeline != NULL)
+ {
+ CoglFramebuffer *framebuffer;
+
+ framebuffer =
+ clutter_paint_context_get_framebuffer (paint_context);
+ _st_paint_shadow_with_opacity (priv->shadow_spec,
+ framebuffer,
+ priv->text_shadow_pipeline,
+ &allocation,
+ clutter_actor_get_paint_opacity (priv->label));
+ }
+ }
+
+ clutter_actor_paint (priv->label, paint_context);
+}
+
+static void
+st_label_resource_scale_changed (ClutterActor *actor)
+{
+ StLabelPrivate *priv = ST_LABEL (actor)->priv;
+
+ g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref);
+
+ if (CLUTTER_ACTOR_CLASS (st_label_parent_class)->resource_scale_changed)
+ CLUTTER_ACTOR_CLASS (st_label_parent_class)->resource_scale_changed (actor);
+}
+
+static void
+st_label_class_init (StLabelClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ gobject_class->set_property = st_label_set_property;
+ gobject_class->get_property = st_label_get_property;
+ gobject_class->dispose = st_label_dispose;
+
+ actor_class->paint = st_label_paint;
+ actor_class->allocate = st_label_allocate;
+ actor_class->get_preferred_width = st_label_get_preferred_width;
+ actor_class->get_preferred_height = st_label_get_preferred_height;
+ actor_class->resource_scale_changed = st_label_resource_scale_changed;
+
+ widget_class->style_changed = st_label_style_changed;
+ widget_class->get_accessible_type = st_label_accessible_get_type;
+
+ /**
+ * StLabel:clutter-text:
+ *
+ * The internal #ClutterText actor supporting the label
+ */
+ props[PROP_CLUTTER_TEXT] =
+ g_param_spec_object ("clutter-text",
+ "Clutter Text",
+ "Internal ClutterText actor",
+ CLUTTER_TYPE_TEXT,
+ ST_PARAM_READABLE);
+
+ /**
+ * StLabel:text:
+ *
+ * The current text being display in the #StLabel.
+ */
+ props[PROP_TEXT] =
+ g_param_spec_string ("text",
+ "Text",
+ "Text of the label",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+}
+
+static void
+invalidate_shadow_pipeline (GObject *object,
+ GParamSpec *pspec,
+ StLabel *label)
+{
+ StLabelPrivate *priv = st_label_get_instance_private (label);
+
+ g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref);
+}
+
+static void
+st_label_init (StLabel *label)
+{
+ ClutterActor *actor = CLUTTER_ACTOR (label);
+ StLabelPrivate *priv;
+
+ label->priv = priv = st_label_get_instance_private (label);
+
+ label->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ label->priv->text_shadow_pipeline = NULL;
+ label->priv->shadow_width = -1.;
+ label->priv->shadow_height = -1.;
+
+ /* These properties might get set from CSS using _st_set_text_from_style */
+ g_signal_connect (priv->label, "notify::font-description",
+ G_CALLBACK (invalidate_shadow_pipeline), label);
+
+ g_signal_connect (priv->label, "notify::attributes",
+ G_CALLBACK (invalidate_shadow_pipeline), label);
+
+ g_signal_connect (priv->label, "notify::justify",
+ G_CALLBACK (invalidate_shadow_pipeline), label);
+
+ g_signal_connect (priv->label, "notify::line-alignment",
+ G_CALLBACK (invalidate_shadow_pipeline), label);
+
+ clutter_actor_add_child (actor, priv->label);
+
+ clutter_actor_set_offscreen_redirect (actor,
+ CLUTTER_OFFSCREEN_REDIRECT_ALWAYS);
+}
+
+/**
+ * st_label_new:
+ * @text: (nullable): text to set the label to
+ *
+ * Create a new #StLabel with the label specified by @text.
+ *
+ * Returns: a new #StLabel
+ */
+StWidget *
+st_label_new (const gchar *text)
+{
+ if (text == NULL || *text == '\0')
+ return g_object_new (ST_TYPE_LABEL, NULL);
+ else
+ return g_object_new (ST_TYPE_LABEL,
+ "text", text,
+ NULL);
+}
+
+/**
+ * st_label_get_text:
+ * @label: a #StLabel
+ *
+ * Get the text displayed on the label.
+ *
+ * Returns: (transfer none): the text for the label. This must not be freed by
+ * the application
+ */
+const gchar *
+st_label_get_text (StLabel *label)
+{
+ g_return_val_if_fail (ST_IS_LABEL (label), NULL);
+
+ return clutter_text_get_text (CLUTTER_TEXT (label->priv->label));
+}
+
+/**
+ * st_label_set_text:
+ * @label: a #StLabel
+ * @text: (nullable): text to set the label to
+ *
+ * Sets the text displayed by the label.
+ */
+void
+st_label_set_text (StLabel *label,
+ const gchar *text)
+{
+ StLabelPrivate *priv;
+ ClutterText *ctext;
+
+ g_return_if_fail (ST_IS_LABEL (label));
+
+ priv = label->priv;
+ ctext = CLUTTER_TEXT (priv->label);
+
+ if (clutter_text_get_editable (ctext) ||
+ g_strcmp0 (clutter_text_get_text (ctext), text) != 0)
+ {
+ g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref);
+
+ clutter_text_set_text (ctext, text);
+
+ g_object_notify_by_pspec (G_OBJECT (label), props[PROP_TEXT]);
+ }
+}
+
+/**
+ * st_label_get_clutter_text:
+ * @label: a #StLabel
+ *
+ * Retrieve the internal #ClutterText used by @label so that extra parameters
+ * can be set.
+ *
+ * Returns: (transfer none): the #ClutterText used by #StLabel. The actor
+ * is owned by the #StLabel and should not be destroyed by the application.
+ */
+ClutterActor*
+st_label_get_clutter_text (StLabel *label)
+{
+ g_return_val_if_fail (ST_LABEL (label), NULL);
+
+ return label->priv->label;
+}
+
+
+/******************************************************************************/
+/*************************** ACCESSIBILITY SUPPORT ****************************/
+/******************************************************************************/
+
+#define ST_TYPE_LABEL_ACCESSIBLE st_label_accessible_get_type ()
+
+#define ST_LABEL_ACCESSIBLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessible))
+
+#define ST_IS_LABEL_ACCESSIBLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ ST_TYPE_LABEL_ACCESSIBLE))
+
+#define ST_LABEL_ACCESSIBLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass))
+
+#define ST_IS_LABEL_ACCESSIBLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ ST_TYPE_LABEL_ACCESSIBLE))
+
+#define ST_LABEL_ACCESSIBLE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass))
+
+typedef struct _StLabelAccessible StLabelAccessible;
+typedef struct _StLabelAccessibleClass StLabelAccessibleClass;
+
+struct _StLabelAccessible
+{
+ StWidgetAccessible parent;
+};
+
+struct _StLabelAccessibleClass
+{
+ StWidgetAccessibleClass parent_class;
+};
+
+/* AtkObject */
+static void st_label_accessible_initialize (AtkObject *obj,
+ gpointer data);
+static const gchar * st_label_accessible_get_name (AtkObject *obj);
+
+G_DEFINE_TYPE (StLabelAccessible, st_label_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
+
+static void
+st_label_accessible_class_init (StLabelAccessibleClass *klass)
+{
+ AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+ atk_class->initialize = st_label_accessible_initialize;
+ atk_class->get_name = st_label_accessible_get_name;
+}
+
+static void
+st_label_accessible_init (StLabelAccessible *self)
+{
+ /* initialization done on AtkObject->initialize */
+}
+
+static void
+label_text_notify_cb (StLabel *label,
+ GParamSpec *pspec,
+ AtkObject *accessible)
+{
+ g_object_notify (G_OBJECT (accessible), "accessible-name");
+}
+
+static void
+st_label_accessible_initialize (AtkObject *obj,
+ gpointer data)
+{
+ ATK_OBJECT_CLASS (st_label_accessible_parent_class)->initialize (obj, data);
+
+ g_signal_connect (data, "notify::text",
+ G_CALLBACK (label_text_notify_cb),
+ obj);
+
+ obj->role = ATK_ROLE_LABEL;
+}
+
+static const gchar *
+st_label_accessible_get_name (AtkObject *obj)
+{
+ const gchar *name = NULL;
+
+ g_return_val_if_fail (ST_IS_LABEL_ACCESSIBLE (obj), NULL);
+
+ name = ATK_OBJECT_CLASS (st_label_accessible_parent_class)->get_name (obj);
+ if (name == NULL)
+ {
+ ClutterActor *actor = NULL;
+
+ actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+ if (actor == NULL || st_widget_has_style_class_name (ST_WIDGET (actor), "hidden"))
+ name = NULL;
+ else
+ name = st_label_get_text (ST_LABEL (actor));
+ }
+
+ return name;
+}
diff --git a/src/st/st-label.h b/src/st/st-label.h
new file mode 100644
index 0000000..456ad31
--- /dev/null
+++ b/src/st/st-label.h
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-label.h: Plain label actor
+ *
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_LABEL_H__
+#define __ST_LABEL_H__
+
+G_BEGIN_DECLS
+
+#include <st/st-widget.h>
+
+#define ST_TYPE_LABEL (st_label_get_type ())
+G_DECLARE_FINAL_TYPE (StLabel, st_label, ST, LABEL, StWidget)
+
+typedef struct _StLabelPrivate StLabelPrivate;
+
+/**
+ * StLabel:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+struct _StLabel
+{
+ /*< private >*/
+ StWidget parent_instance;
+
+ StLabelPrivate *priv;
+};
+
+StWidget * st_label_new (const gchar *text);
+const gchar * st_label_get_text (StLabel *label);
+void st_label_set_text (StLabel *label,
+ const gchar *text);
+ClutterActor * st_label_get_clutter_text (StLabel *label);
+
+G_END_DECLS
+
+#endif /* __ST_LABEL_H__ */
diff --git a/src/st/st-password-entry.c b/src/st/st-password-entry.c
new file mode 100644
index 0000000..43f9b52
--- /dev/null
+++ b/src/st/st-password-entry.c
@@ -0,0 +1,363 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-password-entry.c: Password entry actor based on st-entry
+ *
+ * Copyright 2019 Endless Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "st-private.h"
+#include "st-password-entry.h"
+#include "st-icon.h"
+#include "st-settings.h"
+
+#define BLACK_CIRCLE 9679
+
+#define ST_PASSWORD_ENTRY_PRIV(x) st_password_entry_get_instance_private ((StPasswordEntry *) x)
+
+typedef struct _StPasswordEntryPrivate StPasswordEntryPrivate;
+
+struct _StPasswordEntry
+{
+ /*< private >*/
+ StEntry parent_instance;
+};
+
+struct _StPasswordEntryPrivate
+{
+ ClutterActor *peek_password_icon;
+
+ gboolean password_visible;
+ gboolean show_peek_icon;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_PASSWORD_VISIBLE,
+ PROP_SHOW_PEEK_ICON,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (StPasswordEntry, st_password_entry, ST_TYPE_ENTRY);
+
+static gboolean
+show_password_locked_down (StPasswordEntry *entry)
+{
+ gboolean disable_show_password = FALSE;
+
+ g_object_get (st_settings_get (), "disable-show-password", &disable_show_password, NULL);
+
+ return disable_show_password;
+}
+
+static void
+st_password_entry_secondary_icon_clicked (StEntry *entry)
+{
+ StPasswordEntry *password_entry = ST_PASSWORD_ENTRY (entry);
+ StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (password_entry);
+
+ st_password_entry_set_password_visible (password_entry, !priv->password_visible);
+}
+
+static void
+st_password_entry_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StPasswordEntry *entry = ST_PASSWORD_ENTRY (gobject);
+ StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_PASSWORD_VISIBLE:
+ g_value_set_boolean (value, priv->password_visible);
+ break;
+
+ case PROP_SHOW_PEEK_ICON:
+ g_value_set_boolean (value, st_password_entry_get_show_peek_icon (entry));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_password_entry_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StPasswordEntry *entry = ST_PASSWORD_ENTRY (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_PASSWORD_VISIBLE:
+ st_password_entry_set_password_visible (entry, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_PEEK_ICON:
+ st_password_entry_set_show_peek_icon (entry, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_password_entry_dispose (GObject *gobject)
+{
+ StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (gobject);
+
+ g_clear_object (&priv->peek_password_icon);
+
+ G_OBJECT_CLASS(st_password_entry_parent_class)->dispose (gobject);
+}
+
+static void
+st_password_entry_class_init (StPasswordEntryClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ StEntryClass *st_entry_class = ST_ENTRY_CLASS (klass);
+
+ gobject_class->get_property = st_password_entry_get_property;
+ gobject_class->set_property = st_password_entry_set_property;
+ gobject_class->dispose = st_password_entry_dispose;
+
+ st_entry_class->secondary_icon_clicked = st_password_entry_secondary_icon_clicked;
+
+ /**
+ * StPasswordEntry:password-visible:
+ *
+ * Whether the text in the entry is masked for privacy.
+ */
+ props[PROP_PASSWORD_VISIBLE] = g_param_spec_boolean ("password-visible",
+ "Password visible",
+ "Whether the text in the entry is masked or not",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StPasswordEntry:show-peek-icon:
+ *
+ * Whether to display an icon button to toggle the masking enabled by the
+ * #StPasswordEntry:password-visible property.
+ */
+ props[PROP_SHOW_PEEK_ICON] = g_param_spec_boolean ("show-peek-icon",
+ "Show peek icon",
+ "Whether to show the password peek icon",
+ TRUE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+}
+
+static void
+update_peek_icon (StPasswordEntry *entry)
+{
+ StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (entry);
+ gboolean show_peek_icon;
+
+ show_peek_icon = st_password_entry_get_show_peek_icon (entry);
+
+ if (show_peek_icon)
+ st_entry_set_secondary_icon (ST_ENTRY (entry), priv->peek_password_icon);
+ else
+ st_entry_set_secondary_icon (ST_ENTRY (entry), NULL);
+}
+
+static void
+on_disable_show_password_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ StPasswordEntry *entry = ST_PASSWORD_ENTRY (user_data);
+
+ if (show_password_locked_down (entry))
+ st_password_entry_set_password_visible (entry, FALSE);
+
+ update_peek_icon (entry);
+
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_SHOW_PEEK_ICON]);
+}
+
+static void
+clutter_text_password_char_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ StPasswordEntry *entry = ST_PASSWORD_ENTRY (user_data);
+ ClutterActor *clutter_text;
+
+ clutter_text = st_entry_get_clutter_text (ST_ENTRY (entry));
+ if (clutter_text_get_password_char (CLUTTER_TEXT (clutter_text)) == 0)
+ st_password_entry_set_password_visible (entry, TRUE);
+ else
+ st_password_entry_set_password_visible (entry, FALSE);
+}
+
+static void
+st_password_entry_init (StPasswordEntry *entry)
+{
+ StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (entry);
+ ClutterActor *clutter_text;
+
+ priv->peek_password_icon = g_object_new (ST_TYPE_ICON,
+ "style-class", "peek-password",
+ "icon-name", "view-reveal-symbolic",
+ NULL);
+ st_entry_set_secondary_icon (ST_ENTRY (entry), priv->peek_password_icon);
+
+ st_password_entry_set_show_peek_icon (entry, TRUE);
+
+ g_signal_connect_object (st_settings_get (),
+ "notify::disable-show-password",
+ G_CALLBACK (on_disable_show_password_changed),
+ entry,
+ 0);
+
+ clutter_text = st_entry_get_clutter_text (ST_ENTRY (entry));
+ clutter_text_set_password_char (CLUTTER_TEXT (clutter_text), BLACK_CIRCLE);
+
+ st_entry_set_input_purpose (ST_ENTRY (entry), CLUTTER_INPUT_CONTENT_PURPOSE_PASSWORD);
+
+ g_signal_connect (clutter_text, "notify::password-char",
+ G_CALLBACK (clutter_text_password_char_cb), entry);
+}
+
+/**
+ * st_password_entry_new:
+ *
+ * Create a new #StPasswordEntry.
+ *
+ * Returns: a new #StEntry
+ */
+StEntry*
+st_password_entry_new (void)
+{
+ return ST_ENTRY (g_object_new (ST_TYPE_PASSWORD_ENTRY, NULL));
+}
+
+/**
+ * st_password_entry_set_show_peek_icon:
+ * @entry: a #StPasswordEntry
+ * @value: %TRUE to show the peek-icon in the entry
+ *
+ * Sets whether to show or hide the peek-icon in the password entry. If %TRUE,
+ * a icon button for temporarily unmasking the password will be shown at the
+ * end of the entry.
+ */
+void
+st_password_entry_set_show_peek_icon (StPasswordEntry *entry,
+ gboolean value)
+{
+ StPasswordEntryPrivate *priv;
+
+ g_return_if_fail (ST_IS_PASSWORD_ENTRY (entry));
+
+ priv = ST_PASSWORD_ENTRY_PRIV (entry);
+ if (priv->show_peek_icon == value)
+ return;
+
+ priv->show_peek_icon = value;
+
+ update_peek_icon (entry);
+
+ if (st_password_entry_get_show_peek_icon (entry) != value)
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_SHOW_PEEK_ICON]);
+}
+
+/**
+ * st_password_entry_get_show_peek_icon:
+ * @entry: a #StPasswordEntry
+ *
+ * Gets whether peek-icon is shown or hidden in the password entry.
+ *
+ * Returns: %TRUE if visible
+ */
+gboolean
+st_password_entry_get_show_peek_icon (StPasswordEntry *entry)
+{
+ StPasswordEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_PASSWORD_ENTRY (entry), TRUE);
+
+ priv = ST_PASSWORD_ENTRY_PRIV (entry);
+ return priv->show_peek_icon && !show_password_locked_down (entry);
+}
+
+/**
+ * st_password_entry_set_password_visible:
+ * @entry: a #StPasswordEntry
+ * @value: %TRUE to show the password in the entry, #FALSE otherwise
+ *
+ * Sets whether to show or hide text in the password entry.
+ */
+void
+st_password_entry_set_password_visible (StPasswordEntry *entry,
+ gboolean value)
+{
+ StPasswordEntryPrivate *priv;
+ ClutterActor *clutter_text;
+
+ g_return_if_fail (ST_IS_PASSWORD_ENTRY (entry));
+
+ priv = ST_PASSWORD_ENTRY_PRIV (entry);
+ if (priv->password_visible == value)
+ return;
+
+ priv->password_visible = value;
+
+ clutter_text = st_entry_get_clutter_text (ST_ENTRY (entry));
+ if (priv->password_visible)
+ {
+ clutter_text_set_password_char (CLUTTER_TEXT (clutter_text), 0);
+ st_icon_set_icon_name (ST_ICON (priv->peek_password_icon), "view-conceal-symbolic");
+ }
+ else
+ {
+ clutter_text_set_password_char (CLUTTER_TEXT (clutter_text), BLACK_CIRCLE);
+ st_icon_set_icon_name (ST_ICON (priv->peek_password_icon), "view-reveal-symbolic");
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_PASSWORD_VISIBLE]);
+}
+
+/**
+ * st_password_entry_get_password_visible:
+ * @entry: a #StPasswordEntry
+ *
+ * Gets whether the text is masked in the password entry.
+ *
+ * Returns: %TRUE if visible
+ */
+gboolean
+st_password_entry_get_password_visible (StPasswordEntry *entry)
+{
+ StPasswordEntryPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_PASSWORD_ENTRY (entry), FALSE);
+
+ priv = ST_PASSWORD_ENTRY_PRIV (entry);
+ return priv->password_visible;
+}
diff --git a/src/st/st-password-entry.h b/src/st/st-password-entry.h
new file mode 100644
index 0000000..3998068
--- /dev/null
+++ b/src/st/st-password-entry.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-password-entry.h: Password entry actor based on st-entry
+ *
+ * Copyright 2019 Endless Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_PASSWORD_ENTRY_H__
+#define __ST_PASSWORD_ENTRY_H__
+
+G_BEGIN_DECLS
+
+#include <st/st-entry.h>
+
+#define ST_TYPE_PASSWORD_ENTRY (st_password_entry_get_type ())
+
+G_DECLARE_FINAL_TYPE (StPasswordEntry, st_password_entry, ST, PASSWORD_ENTRY, StEntry)
+
+StEntry *st_password_entry_new (void);
+gboolean st_password_entry_get_password_visible (StPasswordEntry *entry);
+void st_password_entry_set_password_visible (StPasswordEntry *entry,
+ gboolean value);
+gboolean st_password_entry_get_show_peek_icon (StPasswordEntry *entry);
+void st_password_entry_set_show_peek_icon (StPasswordEntry *entry,
+ gboolean value);
+
+G_END_DECLS
+
+#endif /* __ST_PASSWORD_ENTRY_H__ */
+
diff --git a/src/st/st-private.c b/src/st/st-private.c
new file mode 100644
index 0000000..bb98151
--- /dev/null
+++ b/src/st/st-private.c
@@ -0,0 +1,804 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-private.h: Private declarations and functions
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010 Florian Müllner
+ * Copyright 2010 Intel Corporation
+ * Copyright 2010 Giovanni Campagna
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <math.h>
+#include <string.h>
+
+#include "st-private.h"
+
+/**
+ * _st_actor_get_preferred_width:
+ * @actor: a #ClutterActor
+ * @for_height: as with clutter_actor_get_preferred_width()
+ * @y_fill: %TRUE if @actor will fill its allocation vertically
+ * @min_width_p: as with clutter_actor_get_preferred_width()
+ * @natural_width_p: as with clutter_actor_get_preferred_width()
+ *
+ * Like clutter_actor_get_preferred_width(), but if @y_fill is %FALSE,
+ * then it will compute a width request based on the assumption that
+ * @actor will be given an allocation no taller than its natural
+ * height.
+ */
+void
+_st_actor_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gboolean y_fill,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ if (!y_fill && for_height != -1)
+ {
+ ClutterRequestMode mode;
+ gfloat natural_height;
+
+ mode = clutter_actor_get_request_mode (actor);
+ if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
+ {
+ clutter_actor_get_preferred_height (actor, -1, NULL, &natural_height);
+ if (for_height > natural_height)
+ for_height = natural_height;
+ }
+ }
+
+ clutter_actor_get_preferred_width (actor, for_height, min_width_p, natural_width_p);
+}
+
+/**
+ * _st_actor_get_preferred_height:
+ * @actor: a #ClutterActor
+ * @for_width: as with clutter_actor_get_preferred_height()
+ * @x_fill: %TRUE if @actor will fill its allocation horizontally
+ * @min_height_p: as with clutter_actor_get_preferred_height()
+ * @natural_height_p: as with clutter_actor_get_preferred_height()
+ *
+ * Like clutter_actor_get_preferred_height(), but if @x_fill is
+ * %FALSE, then it will compute a height request based on the
+ * assumption that @actor will be given an allocation no wider than
+ * its natural width.
+ */
+void
+_st_actor_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gboolean x_fill,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ if (!x_fill && for_width != -1)
+ {
+ ClutterRequestMode mode;
+ gfloat natural_width;
+
+ mode = clutter_actor_get_request_mode (actor);
+ if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ clutter_actor_get_preferred_width (actor, -1, NULL, &natural_width);
+ if (for_width > natural_width)
+ for_width = natural_width;
+ }
+ }
+
+ clutter_actor_get_preferred_height (actor, for_width, min_height_p, natural_height_p);
+}
+
+/**
+ * _st_set_text_from_style:
+ * @text: Target #ClutterText
+ * @theme_node: Source #StThemeNode
+ *
+ * Set various GObject properties of the @text object using
+ * CSS information from @theme_node.
+ */
+void
+_st_set_text_from_style (ClutterText *text,
+ StThemeNode *theme_node)
+{
+
+ ClutterColor color;
+ StTextDecoration decoration;
+ PangoAttrList *attribs = NULL;
+ const PangoFontDescription *font;
+ PangoAttribute *foreground;
+ StTextAlign align;
+ gdouble spacing;
+ gchar *font_features;
+
+ font = st_theme_node_get_font (theme_node);
+ clutter_text_set_font_description (text, (PangoFontDescription *) font);
+
+ attribs = pango_attr_list_new ();
+
+ st_theme_node_get_foreground_color (theme_node, &color);
+ clutter_text_set_cursor_color (text, &color);
+ foreground = pango_attr_foreground_new (color.red * 255,
+ color.green * 255,
+ color.blue * 255);
+ pango_attr_list_insert (attribs, foreground);
+
+ if (color.alpha != 255)
+ {
+ PangoAttribute *alpha;
+
+ /* An alpha value of 0 means "system inherited", so the
+ * minimum regular value is 1.
+ */
+ if (color.alpha == 0)
+ alpha = pango_attr_foreground_alpha_new (1);
+ else
+ alpha = pango_attr_foreground_alpha_new (color.alpha * 255);
+
+ pango_attr_list_insert (attribs, alpha);
+ }
+
+ decoration = st_theme_node_get_text_decoration (theme_node);
+ if (decoration)
+ {
+ if (decoration & ST_TEXT_DECORATION_UNDERLINE)
+ {
+ PangoAttribute *underline = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+ pango_attr_list_insert (attribs, underline);
+ }
+ if (decoration & ST_TEXT_DECORATION_LINE_THROUGH)
+ {
+ PangoAttribute *strikethrough = pango_attr_strikethrough_new (TRUE);
+ pango_attr_list_insert (attribs, strikethrough);
+ }
+ /* Pango doesn't have an equivalent attribute for _OVERLINE, and we deliberately
+ * skip BLINK (for now...)
+ */
+ }
+
+ spacing = st_theme_node_get_letter_spacing (theme_node);
+ if (spacing)
+ {
+ PangoAttribute *letter_spacing = pango_attr_letter_spacing_new ((int)(.5 + spacing) * PANGO_SCALE);
+ pango_attr_list_insert (attribs, letter_spacing);
+ }
+
+ font_features = st_theme_node_get_font_features (theme_node);
+ if (font_features)
+ {
+ pango_attr_list_insert (attribs, pango_attr_font_features_new (font_features));
+ g_free (font_features);
+ }
+
+ clutter_text_set_attributes (text, attribs);
+
+ if (attribs)
+ pango_attr_list_unref (attribs);
+
+ align = st_theme_node_get_text_align (theme_node);
+ if (align == ST_TEXT_ALIGN_JUSTIFY)
+ {
+ clutter_text_set_justify (text, TRUE);
+ clutter_text_set_line_alignment (text, PANGO_ALIGN_LEFT);
+ }
+ else
+ {
+ clutter_text_set_justify (text, FALSE);
+ clutter_text_set_line_alignment (text, (PangoAlignment) align);
+ }
+}
+
+/**
+ * _st_create_texture_pipeline:
+ * @src_texture: The CoglTexture for the pipeline
+ *
+ * Creates a simple pipeline which contains the given texture as a
+ * single layer.
+ */
+CoglPipeline *
+_st_create_texture_pipeline (CoglTexture *src_texture)
+{
+ static CoglPipeline *texture_pipeline_template = NULL;
+ CoglPipeline *pipeline;
+
+ g_return_val_if_fail (src_texture != NULL, NULL);
+
+ /* The only state used in the pipeline that would affect the shader
+ generation is the texture type on the layer. Therefore we create
+ a template pipeline which sets this state and all texture
+ pipelines are created as a copy of this. That way Cogl can find
+ the shader state for the pipeline more quickly by looking at the
+ pipeline ancestry instead of resorting to the shader cache. */
+ if (G_UNLIKELY (texture_pipeline_template == NULL))
+ {
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ texture_pipeline_template = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_layer_null_texture (texture_pipeline_template, 0);
+ }
+
+ pipeline = cogl_pipeline_copy (texture_pipeline_template);
+
+ if (src_texture != NULL)
+ cogl_pipeline_set_layer_texture (pipeline, 0, src_texture);
+
+ return pipeline;
+}
+
+/*****
+ * Shadows
+ *****/
+
+static gdouble *
+calculate_gaussian_kernel (gdouble sigma,
+ guint n_values)
+{
+ gdouble *ret, sum;
+ gdouble exp_divisor;
+ int half, i;
+
+ g_return_val_if_fail (sigma > 0, NULL);
+
+ half = n_values / 2;
+
+ ret = g_malloc (n_values * sizeof (gdouble));
+ sum = 0.0;
+
+ exp_divisor = 2 * sigma * sigma;
+
+ /* n_values of 1D Gauss function */
+ for (i = 0; i < (int)n_values; i++)
+ {
+ ret[i] = exp (-(i - half) * (i - half) / exp_divisor);
+ sum += ret[i];
+ }
+
+ /* normalize */
+ for (i = 0; i < (int)n_values; i++)
+ ret[i] /= sum;
+
+ return ret;
+}
+
+static guchar *
+blur_pixels (guchar *pixels_in,
+ gint width_in,
+ gint height_in,
+ gint rowstride_in,
+ gdouble blur,
+ gint *width_out,
+ gint *height_out,
+ size_t *rowstride_out)
+{
+ guchar *pixels_out;
+ gdouble sigma;
+
+ /* The CSS specification defines (or will define) the blur radius as twice
+ * the Gaussian standard deviation. See:
+ *
+ * http://lists.w3.org/Archives/Public/www-style/2010Sep/0002.html
+ */
+ sigma = blur / 2.;
+
+ if ((guint) blur == 0)
+ {
+ *width_out = width_in;
+ *height_out = height_in;
+ *rowstride_out = rowstride_in;
+ pixels_out = g_memdup2 (pixels_in, *rowstride_out * *height_out);
+ }
+ else
+ {
+ gdouble *kernel;
+ guchar *line;
+ gint n_values, half;
+ gint x_in, y_in, x_out, y_out, i;
+
+ n_values = (gint) 5 * sigma;
+ half = n_values / 2;
+
+ *width_out = width_in + 2 * half;
+ *height_out = height_in + 2 * half;
+ *rowstride_out = (*width_out + 3) & ~3;
+
+ pixels_out = g_malloc0 (*rowstride_out * *height_out);
+ line = g_malloc0 (*rowstride_out);
+
+ kernel = calculate_gaussian_kernel (sigma, n_values);
+
+ /* vertical blur */
+ for (x_in = 0; x_in < width_in; x_in++)
+ for (y_out = 0; y_out < *height_out; y_out++)
+ {
+ guchar *pixel_in, *pixel_out;
+ gint i0, i1;
+
+ y_in = y_out - half;
+
+ /* We read from the source at 'y = y_in + i - half'; clamp the
+ * full i range [0, n_values) so that y is in [0, height_in).
+ */
+ i0 = MAX (half - y_in, 0);
+ i1 = MIN (height_in + half - y_in, n_values);
+
+ pixel_in = pixels_in + (y_in + i0 - half) * rowstride_in + x_in;
+ pixel_out = pixels_out + y_out * *rowstride_out + (x_in + half);
+
+ for (i = i0; i < i1; i++)
+ {
+ *pixel_out += *pixel_in * kernel[i];
+ pixel_in += rowstride_in;
+ }
+ }
+
+ /* horizontal blur */
+ for (y_out = 0; y_out < *height_out; y_out++)
+ {
+ memcpy (line, pixels_out + y_out * *rowstride_out, *rowstride_out);
+
+ for (x_out = 0; x_out < *width_out; x_out++)
+ {
+ gint i0, i1;
+ guchar *pixel_out, *pixel_in;
+
+ /* We read from the source at 'x = x_out + i - half'; clamp the
+ * full i range [0, n_values) so that x is in [0, width_out).
+ */
+ i0 = MAX (half - x_out, 0);
+ i1 = MIN (*width_out + half - x_out, n_values);
+
+ pixel_in = line + x_out + i0 - half;
+ pixel_out = pixels_out + *rowstride_out * y_out + x_out;
+
+ *pixel_out = 0;
+ for (i = i0; i < i1; i++)
+ {
+ *pixel_out += *pixel_in * kernel[i];
+ pixel_in++;
+ }
+ }
+ }
+ g_free (kernel);
+ g_free (line);
+ }
+
+ return pixels_out;
+}
+
+CoglPipeline *
+_st_create_shadow_pipeline (StShadow *shadow_spec,
+ CoglTexture *src_texture,
+ float resource_scale)
+{
+ ClutterBackend *backend = clutter_get_default_backend ();
+ CoglContext *ctx = clutter_backend_get_cogl_context (backend);
+ g_autoptr (ClutterPaintNode) texture_node = NULL;
+ g_autoptr (ClutterPaintNode) blur_node = NULL;
+ g_autoptr (CoglOffscreen) offscreen = NULL;
+ g_autoptr (GError) error = NULL;
+ ClutterPaintContext *paint_context;
+ CoglFramebuffer *fb;
+ CoglPipeline *pipeline;
+ CoglTexture *texture;
+ float sampling_radius;
+ float sigma;
+ int src_height, dst_height;
+ int src_width, dst_width;
+ CoglPipeline *texture_pipeline;
+
+ static CoglPipelineKey texture_pipeline_key =
+ "st-create-shadow-pipeline-saturate-alpha";
+ static CoglPipeline *shadow_pipeline_template = NULL;
+
+ g_return_val_if_fail (shadow_spec != NULL, NULL);
+ g_return_val_if_fail (src_texture != NULL, NULL);
+
+ sampling_radius = resource_scale * shadow_spec->blur;
+ sigma = sampling_radius / 2.f;
+ sampling_radius = ceilf (sampling_radius);
+
+ src_width = cogl_texture_get_width (src_texture);
+ src_height = cogl_texture_get_height (src_texture);
+ dst_width = src_width + 2 * sampling_radius;
+ dst_height = src_height + 2 * sampling_radius;
+
+ texture = cogl_texture_2d_new_with_size (ctx, dst_width, dst_height);
+ if (!texture)
+ return NULL;
+
+ offscreen = cogl_offscreen_new_with_texture (texture);
+ fb = COGL_FRAMEBUFFER (offscreen);
+ if (!cogl_framebuffer_allocate (fb, &error))
+ {
+ cogl_clear_object (&texture);
+ return NULL;
+ }
+
+ cogl_framebuffer_clear4f (fb, COGL_BUFFER_BIT_COLOR, 0.f, 0.f, 0.f, 0.f);
+ cogl_framebuffer_orthographic (fb, 0, 0, dst_width, dst_height, 0, 1.0);
+
+ /* Blur */
+ blur_node = clutter_blur_node_new (dst_width, dst_height, sigma);
+ clutter_paint_node_add_rectangle (blur_node,
+ &(ClutterActorBox) {
+ 0.f, 0.f,
+ dst_width, dst_height,
+ });
+
+ /* Texture */
+ texture_pipeline = cogl_context_get_named_pipeline (ctx,
+ &texture_pipeline_key);
+
+ if (G_UNLIKELY (texture_pipeline == NULL))
+ {
+ CoglSnippet *snippet;
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ "",
+ "if (cogl_color_out.a > 0.0)\n"
+ " cogl_color_out.a = 1.0;");
+
+ texture_pipeline = cogl_pipeline_new (ctx);
+ cogl_pipeline_add_snippet (texture_pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_context_set_named_pipeline (ctx,
+ &texture_pipeline_key,
+ texture_pipeline);
+ }
+
+ /* No need to unref texture_pipeline since the named pipeline hash
+ * doesn't change its ref count from 1. Also no need to copy texture_pipeline
+ * since we'll be completely finished with it after clutter_paint_node_paint.
+ */
+
+ cogl_pipeline_set_layer_texture (texture_pipeline, 0, src_texture);
+ texture_node = clutter_pipeline_node_new (texture_pipeline);
+ clutter_paint_node_add_child (blur_node, texture_node);
+ clutter_paint_node_add_rectangle (texture_node,
+ &(ClutterActorBox) {
+ .x1 = sampling_radius,
+ .y1 = sampling_radius,
+ .x2 = src_width + sampling_radius,
+ .y2 = src_height + sampling_radius,
+ });
+
+ paint_context =
+ clutter_paint_context_new_for_framebuffer (fb, NULL, CLUTTER_PAINT_FLAG_NONE);
+ clutter_paint_node_paint (blur_node, paint_context);
+ clutter_paint_context_destroy (paint_context);
+
+ if (G_UNLIKELY (shadow_pipeline_template == NULL))
+ {
+ shadow_pipeline_template = cogl_pipeline_new (ctx);
+
+ /* We set up the pipeline to blend the shadow texture with the combine
+ * constant, but defer setting the latter until painting, so that we can
+ * take the actor's overall opacity into account. */
+ cogl_pipeline_set_layer_combine (shadow_pipeline_template, 0,
+ "RGBA = MODULATE (CONSTANT, TEXTURE[A])",
+ NULL);
+ }
+
+ pipeline = cogl_pipeline_copy (shadow_pipeline_template);
+ cogl_pipeline_set_layer_texture (pipeline, 0, texture);
+
+ cogl_clear_object (&texture);
+
+ return pipeline;
+}
+
+CoglPipeline *
+_st_create_shadow_pipeline_from_actor (StShadow *shadow_spec,
+ ClutterActor *actor)
+{
+ ClutterContent *image = NULL;
+ CoglPipeline *shadow_pipeline = NULL;
+ float resource_scale;
+ float width, height;
+ ClutterPaintContext *paint_context;
+
+ g_return_val_if_fail (clutter_actor_has_allocation (actor), NULL);
+
+ clutter_actor_get_size (actor, &width, &height);
+
+ if (width == 0 || height == 0)
+ return NULL;
+
+ resource_scale = clutter_actor_get_resource_scale (actor);
+
+ width = ceilf (width * resource_scale);
+ height = ceilf (height * resource_scale);
+
+ image = clutter_actor_get_content (actor);
+ if (image && CLUTTER_IS_IMAGE (image))
+ {
+ CoglTexture *texture;
+
+ texture = clutter_image_get_texture (CLUTTER_IMAGE (image));
+ if (texture &&
+ cogl_texture_get_width (texture) == width &&
+ cogl_texture_get_height (texture) == height)
+ shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, texture,
+ resource_scale);
+ }
+
+ if (shadow_pipeline == NULL)
+ {
+ CoglTexture *buffer;
+ CoglOffscreen *offscreen;
+ CoglFramebuffer *fb;
+ CoglContext *ctx;
+ CoglColor clear_color;
+ GError *catch_error = NULL;
+ float x, y;
+
+ ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ buffer = cogl_texture_2d_new_with_size (ctx, width, height);
+
+ if (buffer == NULL)
+ return NULL;
+
+ offscreen = cogl_offscreen_new_with_texture (buffer);
+ fb = COGL_FRAMEBUFFER (offscreen);
+
+ if (!cogl_framebuffer_allocate (fb, &catch_error))
+ {
+ g_error_free (catch_error);
+ g_object_unref (offscreen);
+ cogl_object_unref (buffer);
+ return NULL;
+ }
+
+ cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
+ clutter_actor_get_position (actor, &x, &y);
+ x *= resource_scale;
+ y *= resource_scale;
+
+ cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
+ cogl_framebuffer_translate (fb, -x, -y, 0);
+ cogl_framebuffer_orthographic (fb, 0, 0, width, height, 0, 1.0);
+ cogl_framebuffer_scale (fb, resource_scale, resource_scale, 1);
+
+ clutter_actor_set_opacity_override (actor, 255);
+
+ paint_context =
+ clutter_paint_context_new_for_framebuffer (fb, NULL,
+ CLUTTER_PAINT_FLAG_NONE);
+ clutter_actor_paint (actor, paint_context);
+ clutter_paint_context_destroy (paint_context);
+
+ clutter_actor_set_opacity_override (actor, -1);
+
+ g_object_unref (fb);
+
+ shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, buffer,
+ resource_scale);
+
+ cogl_object_unref (buffer);
+ }
+
+ return shadow_pipeline;
+}
+
+/**
+ * _st_create_shadow_cairo_pattern:
+ * @shadow_spec: the definition of the shadow
+ * @src_pattern: surface pattern for which we create the shadow
+ * (must be a surface pattern)
+ *
+ * This is a utility function for creating shadows used by
+ * st-theme-node.c; it's in this file to share the gaussian
+ * blur implementation. The usage of this function is quite different
+ * depending on whether shadow_spec->inset is %TRUE or not. If
+ * shadow_spec->inset is %TRUE, the caller should pass in a @src_pattern
+ * which is the <i>inverse</i> of what they want shadowed, and must take
+ * care of the spread and offset from the shadow spec themselves. If
+ * shadow_spec->inset is %FALSE then the caller should pass in what they
+ * want shadowed directly, and this function takes care of the spread and
+ * the offset.
+ */
+cairo_pattern_t *
+_st_create_shadow_cairo_pattern (StShadow *shadow_spec_in,
+ cairo_pattern_t *src_pattern)
+{
+ g_autoptr(StShadow) shadow_spec = NULL;
+ static cairo_user_data_key_t shadow_pattern_user_data;
+ cairo_t *cr;
+ cairo_surface_t *src_surface;
+ cairo_surface_t *surface_in;
+ cairo_surface_t *surface_out;
+ cairo_pattern_t *dst_pattern;
+ guchar *pixels_in, *pixels_out;
+ gint width_in, height_in, rowstride_in;
+ gint width_out, height_out;
+ size_t rowstride_out;
+ cairo_matrix_t shadow_matrix;
+ double xscale_in, yscale_in;
+ int i, j;
+
+ g_return_val_if_fail (shadow_spec_in != NULL, NULL);
+ g_return_val_if_fail (src_pattern != NULL, NULL);
+
+ if (cairo_pattern_get_surface (src_pattern, &src_surface) != CAIRO_STATUS_SUCCESS)
+ /* The most likely reason we can't get the pattern is that sizing went hairwire
+ * and the caller tried to create a surface too big for memory, leaving us with
+ * a pattern in an error state; we return a transparent pattern for the shadow.
+ */
+ return cairo_pattern_create_rgba(1.0, 1.0, 1.0, 0.0);
+
+ width_in = cairo_image_surface_get_width (src_surface);
+ height_in = cairo_image_surface_get_height (src_surface);
+
+ cairo_surface_get_device_scale (src_surface, &xscale_in, &yscale_in);
+
+ if (xscale_in != 1.0 || yscale_in != 1.0)
+ {
+ /* Scale the shadow specifications in a temporary copy so that
+ * we can work everywhere in absolute surface coordinates */
+ double scale = (xscale_in + yscale_in) / 2.0;
+ shadow_spec = st_shadow_new (&shadow_spec_in->color,
+ shadow_spec_in->xoffset * xscale_in,
+ shadow_spec_in->yoffset * yscale_in,
+ shadow_spec_in->blur * scale,
+ shadow_spec_in->spread * scale,
+ shadow_spec_in->inset);
+ }
+ else
+ {
+ shadow_spec = st_shadow_ref (shadow_spec_in);
+ }
+
+ /* We want the output to be a color agnostic alpha mask,
+ * so we need to strip the color channels from the input
+ */
+ if (cairo_image_surface_get_format (src_surface) != CAIRO_FORMAT_A8)
+ {
+ surface_in = cairo_image_surface_create (CAIRO_FORMAT_A8,
+ width_in, height_in);
+
+ cr = cairo_create (surface_in);
+ cairo_set_source_surface (cr, src_surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ }
+ else
+ {
+ surface_in = cairo_surface_reference (src_surface);
+ }
+
+ pixels_in = cairo_image_surface_get_data (surface_in);
+ rowstride_in = cairo_image_surface_get_stride (surface_in);
+
+ pixels_out = blur_pixels (pixels_in, width_in, height_in, rowstride_in,
+ shadow_spec->blur,
+ &width_out, &height_out, &rowstride_out);
+ cairo_surface_destroy (surface_in);
+
+ /* Invert pixels for inset shadows */
+ if (shadow_spec->inset)
+ {
+ for (j = 0; j < height_out; j++)
+ {
+ guchar *p = pixels_out + rowstride_out * j;
+ for (i = 0; i < width_out; i++, p++)
+ *p = ~*p;
+ }
+ }
+
+ surface_out = cairo_image_surface_create_for_data (pixels_out,
+ CAIRO_FORMAT_A8,
+ width_out,
+ height_out,
+ rowstride_out);
+ cairo_surface_set_device_scale (surface_out, xscale_in, yscale_in);
+ cairo_surface_set_user_data (surface_out, &shadow_pattern_user_data,
+ pixels_out, (cairo_destroy_func_t) g_free);
+
+ dst_pattern = cairo_pattern_create_for_surface (surface_out);
+ cairo_surface_destroy (surface_out);
+
+ cairo_pattern_get_matrix (src_pattern, &shadow_matrix);
+
+ if (shadow_spec->inset)
+ {
+ /* Scale the matrix in surface absolute coordinates */
+ cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in);
+
+ /* For inset shadows, offsets and spread radius have already been
+ * applied to the original pattern, so all left to do is shift the
+ * blurred image left, so that it aligns centered under the
+ * unblurred one
+ */
+ cairo_matrix_translate (&shadow_matrix,
+ (width_out - width_in) / 2.0,
+ (height_out - height_in) / 2.0);
+
+ /* Scale back the matrix in original coordinates */
+ cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in);
+
+ cairo_pattern_set_matrix (dst_pattern, &shadow_matrix);
+ return dst_pattern;
+ }
+
+ /* Read all the code from the cairo_pattern_set_matrix call
+ * at the end of this function to here from bottom to top,
+ * because each new affine transformation is applied in
+ * front of all the previous ones */
+
+ /* 6. Invert the matrix back */
+ cairo_matrix_invert (&shadow_matrix);
+
+ /* Scale the matrix in surface absolute coordinates */
+ cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in);
+
+ /* 5. Adjust based on specified offsets */
+ cairo_matrix_translate (&shadow_matrix,
+ shadow_spec->xoffset,
+ shadow_spec->yoffset);
+
+ /* 4. Recenter the newly scaled image */
+ cairo_matrix_translate (&shadow_matrix,
+ - shadow_spec->spread,
+ - shadow_spec->spread);
+
+ /* 3. Scale up the blurred image to fill the spread */
+ cairo_matrix_scale (&shadow_matrix,
+ (width_in + 2.0 * shadow_spec->spread) / width_in,
+ (height_in + 2.0 * shadow_spec->spread) / height_in);
+
+ /* 2. Shift the blurred image left, so that it aligns centered
+ * under the unblurred one */
+ cairo_matrix_translate (&shadow_matrix,
+ - (width_out - width_in) / 2.0,
+ - (height_out - height_in) / 2.0);
+
+ /* Scale back the matrix in scaled coordinates */
+ cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in);
+
+ /* 1. Invert the matrix so we can work with it in pattern space
+ */
+ cairo_matrix_invert (&shadow_matrix);
+
+ cairo_pattern_set_matrix (dst_pattern, &shadow_matrix);
+
+ return dst_pattern;
+}
+
+void
+_st_paint_shadow_with_opacity (StShadow *shadow_spec,
+ CoglFramebuffer *framebuffer,
+ CoglPipeline *shadow_pipeline,
+ ClutterActorBox *box,
+ guint8 paint_opacity)
+{
+ ClutterActorBox shadow_box;
+ CoglColor color;
+
+ g_return_if_fail (shadow_spec != NULL);
+ g_return_if_fail (shadow_pipeline != NULL);
+
+ st_shadow_get_box (shadow_spec, box, &shadow_box);
+
+ cogl_color_init_from_4ub (&color,
+ shadow_spec->color.red * paint_opacity / 255,
+ shadow_spec->color.green * paint_opacity / 255,
+ shadow_spec->color.blue * paint_opacity / 255,
+ shadow_spec->color.alpha * paint_opacity / 255);
+ cogl_color_premultiply (&color);
+ cogl_pipeline_set_layer_combine_constant (shadow_pipeline, 0, &color);
+ cogl_framebuffer_draw_rectangle (framebuffer,
+ shadow_pipeline,
+ shadow_box.x1, shadow_box.y1,
+ shadow_box.x2, shadow_box.y2);
+}
diff --git a/src/st/st-private.h b/src/st/st-private.h
new file mode 100644
index 0000000..3f1fd12
--- /dev/null
+++ b/src/st/st-private.h
@@ -0,0 +1,75 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-private.h: Private declarations and functions
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2010 Red Hat, Inc.
+ * Copyright 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_PRIVATE_H__
+#define __ST_PRIVATE_H__
+
+#include <glib.h>
+#include <cairo.h>
+#include "st-widget.h"
+#include "st-bin.h"
+#include "st-shadow.h"
+
+G_BEGIN_DECLS
+
+#define I_(str) (g_intern_static_string ((str)))
+
+#define ST_PARAM_READABLE (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)
+#define ST_PARAM_WRITABLE (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)
+#define ST_PARAM_READWRITE (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+
+G_END_DECLS
+
+ClutterActor *_st_widget_get_dnd_clone (StWidget *widget);
+
+void _st_actor_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gboolean y_fill,
+ gfloat *min_width_p,
+ gfloat *natural_width_p);
+void _st_actor_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gboolean x_fill,
+ gfloat *min_height_p,
+ gfloat *natural_height_p);
+
+void _st_set_text_from_style (ClutterText *text,
+ StThemeNode *theme_node);
+
+CoglPipeline * _st_create_texture_pipeline (CoglTexture *src_texture);
+
+/* Helper for widgets which need to draw additional shadows */
+CoglPipeline * _st_create_shadow_pipeline (StShadow *shadow_spec,
+ CoglTexture *src_texture,
+ float resource_scale);
+CoglPipeline * _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec,
+ ClutterActor *actor);
+cairo_pattern_t *_st_create_shadow_cairo_pattern (StShadow *shadow_spec,
+ cairo_pattern_t *src_pattern);
+
+void _st_paint_shadow_with_opacity (StShadow *shadow_spec,
+ CoglFramebuffer *framebuffer,
+ CoglPipeline *shadow_pipeline,
+ ClutterActorBox *box,
+ guint8 paint_opacity);
+
+#endif /* __ST_PRIVATE_H__ */
diff --git a/src/st/st-scroll-bar.c b/src/st/st-scroll-bar.c
new file mode 100644
index 0000000..72bcd55
--- /dev/null
+++ b/src/st/st-scroll-bar.c
@@ -0,0 +1,1014 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scroll-bar.c: Scroll bar actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010 Maxim Ermilov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-scroll-bar
+ * @short_description: a user interface element to control scrollable areas.
+ *
+ * The #StScrollBar allows users to scroll scrollable actors, either by
+ * the step or page amount, or by manually dragging the handle.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include <clutter/clutter.h>
+
+#include "st-scroll-bar.h"
+#include "st-bin.h"
+#include "st-enum-types.h"
+#include "st-private.h"
+#include "st-button.h"
+#include "st-settings.h"
+
+#define PAGING_INITIAL_REPEAT_TIMEOUT 500
+#define PAGING_SUBSEQUENT_REPEAT_TIMEOUT 200
+
+typedef struct _StScrollBarPrivate StScrollBarPrivate;
+struct _StScrollBarPrivate
+{
+ StAdjustment *adjustment;
+
+ gfloat x_origin;
+ gfloat y_origin;
+
+ ClutterInputDevice *grab_device;
+ ClutterGrab *grab;
+
+ ClutterActor *trough;
+ ClutterActor *handle;
+
+ gfloat move_x;
+ gfloat move_y;
+
+ /* Trough-click handling. */
+ enum { NONE, UP, DOWN } paging_direction;
+ guint paging_source_id;
+ guint paging_event_no;
+
+ guint vertical : 1;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (StScrollBar, st_scroll_bar, ST_TYPE_WIDGET)
+
+#define ST_SCROLL_BAR_PRIVATE(sb) st_scroll_bar_get_instance_private (ST_SCROLL_BAR (sb))
+
+enum
+{
+ PROP_0,
+
+ PROP_ADJUSTMENT,
+ PROP_VERTICAL,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum
+{
+ SCROLL_START,
+ SCROLL_STOP,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static gboolean
+handle_button_press_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *bar);
+
+static void stop_scrolling (StScrollBar *bar);
+
+static void
+st_scroll_bar_set_vertical (StScrollBar *bar,
+ gboolean vertical)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR_PRIVATE (bar);
+
+ if (priv->vertical == vertical)
+ return;
+
+ priv->vertical = vertical;
+
+ if (priv->vertical)
+ clutter_actor_set_name (CLUTTER_ACTOR (priv->handle),
+ "vhandle");
+ else
+ clutter_actor_set_name (CLUTTER_ACTOR (priv->handle),
+ "hhandle");
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bar));
+ g_object_notify_by_pspec (G_OBJECT (bar), props[PROP_VERTICAL]);
+}
+
+static void
+st_scroll_bar_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR_PRIVATE (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_ADJUSTMENT:
+ g_value_set_object (value, priv->adjustment);
+ break;
+
+ case PROP_VERTICAL:
+ g_value_set_boolean (value, priv->vertical);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_scroll_bar_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_ADJUSTMENT:
+ st_scroll_bar_set_adjustment (bar, g_value_get_object (value));
+ break;
+
+ case PROP_VERTICAL:
+ st_scroll_bar_set_vertical (bar, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_scroll_bar_dispose (GObject *gobject)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (gobject);
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+
+ if (priv->adjustment)
+ st_scroll_bar_set_adjustment (bar, NULL);
+
+ if (priv->handle)
+ {
+ clutter_actor_destroy (priv->handle);
+ priv->handle = NULL;
+ }
+
+ if (priv->trough)
+ {
+ clutter_actor_destroy (priv->trough);
+ priv->trough = NULL;
+ }
+
+ G_OBJECT_CLASS (st_scroll_bar_parent_class)->dispose (gobject);
+}
+
+static void
+st_scroll_bar_unmap (ClutterActor *actor)
+{
+ CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->unmap (actor);
+
+ stop_scrolling (ST_SCROLL_BAR (actor));
+}
+
+static void
+scroll_bar_allocate_children (StScrollBar *bar,
+ const ClutterActorBox *box)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (bar));
+ ClutterActorBox content_box, trough_box;
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ trough_box.x1 = content_box.x1;
+ trough_box.y1 = content_box.y1;
+ trough_box.x2 = content_box.x2;
+ trough_box.y2 = content_box.y2;
+ clutter_actor_allocate (priv->trough, &trough_box);
+
+ if (priv->adjustment)
+ {
+ float handle_size, position, avail_size;
+ gdouble value, lower, upper, page_size, increment, min_size, max_size;
+ ClutterActorBox handle_box = { 0, };
+
+ st_adjustment_get_values (priv->adjustment,
+ &value,
+ &lower,
+ &upper,
+ NULL,
+ NULL,
+ &page_size);
+
+ if ((upper == lower)
+ || (page_size >= (upper - lower)))
+ increment = 1.0;
+ else
+ increment = page_size / (upper - lower);
+
+ min_size = 32.;
+ st_theme_node_lookup_length (theme_node, "min-size", FALSE, &min_size);
+ max_size = G_MAXINT16;
+ st_theme_node_lookup_length (theme_node, "max-size", FALSE, &max_size);
+
+ if (upper - lower - page_size <= 0)
+ position = 0;
+ else
+ position = (value - lower) / (upper - lower - page_size);
+
+ if (priv->vertical)
+ {
+ avail_size = content_box.y2 - content_box.y1;
+ handle_size = increment * avail_size;
+ handle_size = CLAMP (handle_size, min_size, max_size);
+
+ handle_box.x1 = content_box.x1;
+ handle_box.y1 = content_box.y1 + position * (avail_size - handle_size);
+
+ handle_box.x2 = content_box.x2;
+ handle_box.y2 = handle_box.y1 + handle_size;
+ }
+ else
+ {
+ ClutterTextDirection direction;
+
+ avail_size = content_box.x2 - content_box.x1;
+ handle_size = increment * avail_size;
+ handle_size = CLAMP (handle_size, min_size, max_size);
+
+ direction = clutter_actor_get_text_direction (CLUTTER_ACTOR (bar));
+ if (direction == CLUTTER_TEXT_DIRECTION_RTL)
+ {
+ handle_box.x2 = content_box.x2 - position * (avail_size - handle_size);
+ handle_box.x1 = handle_box.x2 - handle_size;
+ }
+ else
+ {
+ handle_box.x1 = content_box.x1 + position * (avail_size - handle_size);
+ handle_box.x2 = handle_box.x1 + handle_size;
+ }
+
+ handle_box.y1 = content_box.y1;
+ handle_box.y2 = content_box.y2;
+ }
+
+ clutter_actor_allocate (priv->handle, &handle_box);
+ }
+}
+
+static void
+st_scroll_bar_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (self);
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ gfloat trough_min_width, trough_natural_width;
+ gfloat handle_min_width, handle_natural_width;
+
+ st_theme_node_adjust_for_height (theme_node, &for_height);
+
+ _st_actor_get_preferred_width (priv->trough, for_height, TRUE,
+ &trough_min_width, &trough_natural_width);
+
+ _st_actor_get_preferred_width (priv->handle, for_height, TRUE,
+ &handle_min_width, &handle_natural_width);
+
+ if (priv->vertical)
+ {
+ if (min_width_p)
+ *min_width_p = MAX (trough_min_width, handle_min_width);
+
+ if (natural_width_p)
+ *natural_width_p = MAX (trough_natural_width, handle_natural_width);
+ }
+ else
+ {
+ if (min_width_p)
+ *min_width_p = trough_min_width + handle_min_width;
+
+ if (natural_width_p)
+ *natural_width_p = trough_natural_width + handle_natural_width;
+ }
+
+ st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
+}
+
+static void
+st_scroll_bar_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (self);
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ gfloat trough_min_height, trough_natural_height;
+ gfloat handle_min_height, handle_natural_height;
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ _st_actor_get_preferred_height (priv->trough, for_width, TRUE,
+ &trough_min_height, &trough_natural_height);
+
+ _st_actor_get_preferred_height (priv->handle, for_width, TRUE,
+ &handle_min_height, &handle_natural_height);
+
+ if (priv->vertical)
+ {
+ if (min_height_p)
+ *min_height_p = trough_min_height + handle_min_height;
+
+ if (natural_height_p)
+ *natural_height_p = trough_natural_height + handle_natural_height;
+ }
+ else
+ {
+ if (min_height_p)
+ *min_height_p = MAX (trough_min_height, handle_min_height);
+
+ if (natural_height_p)
+ *natural_height_p = MAX (trough_natural_height, handle_natural_height);
+ }
+
+ st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
+}
+
+static void
+st_scroll_bar_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (actor);
+
+ clutter_actor_set_allocation (actor, box);
+
+ scroll_bar_allocate_children (bar, box);
+}
+
+static void
+scroll_bar_update_positions (StScrollBar *bar)
+{
+ ClutterActorBox box;
+
+ /* Due to a change in the adjustments, we need to reposition our
+ * children; since adjustments changes can come from allocation
+ * changes in the scrolled area, we can't just queue a new relayout -
+ * we may already be in a relayout cycle. On the other hand, if
+ * a relayout is already queued, we can't just go ahead and allocate
+ * our children, since we don't have a valid allocation, and calling
+ * clutter_actor_get_allocation_box() will trigger an immediate
+ * stage relayout. So what we do is go ahead and immediately
+ * allocate our children if we already have a valid allocation, and
+ * otherwise just wait for the queued relayout.
+ */
+ if (!clutter_actor_has_allocation (CLUTTER_ACTOR (bar)))
+ return;
+
+ clutter_actor_get_allocation_box (CLUTTER_ACTOR (bar), &box);
+ scroll_bar_allocate_children (bar, &box);
+}
+
+static void
+bar_reactive_notify_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (gobject);
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+
+ clutter_actor_set_reactive (priv->handle,
+ clutter_actor_get_reactive (CLUTTER_ACTOR (bar)));
+}
+
+static GObject*
+st_scroll_bar_constructor (GType type,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObjectClass *gobject_class;
+ GObject *obj;
+ StScrollBar *bar;
+
+ gobject_class = G_OBJECT_CLASS (st_scroll_bar_parent_class);
+ obj = gobject_class->constructor (type, n_properties, properties);
+
+ bar = ST_SCROLL_BAR (obj);
+
+ g_signal_connect (bar, "notify::reactive",
+ G_CALLBACK (bar_reactive_notify_cb), NULL);
+
+ return obj;
+}
+
+static void
+adjust_with_direction (StAdjustment *adj,
+ ClutterScrollDirection direction)
+{
+ gdouble delta;
+
+ switch (direction)
+ {
+ case CLUTTER_SCROLL_UP:
+ case CLUTTER_SCROLL_LEFT:
+ delta = -1.0;
+ break;
+ case CLUTTER_SCROLL_RIGHT:
+ case CLUTTER_SCROLL_DOWN:
+ delta = 1.0;
+ break;
+ case CLUTTER_SCROLL_SMOOTH:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ st_adjustment_adjust_for_scroll_event (adj, delta);
+}
+
+static gboolean
+st_scroll_bar_scroll_event (ClutterActor *actor,
+ ClutterScrollEvent *event)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR_PRIVATE (actor);
+ ClutterTextDirection direction;
+ ClutterScrollDirection scroll_dir;
+
+ if (clutter_event_is_pointer_emulated ((ClutterEvent *) event))
+ return TRUE;
+
+ direction = clutter_actor_get_text_direction (actor);
+ scroll_dir = event->direction;
+
+ switch (scroll_dir)
+ {
+ case CLUTTER_SCROLL_SMOOTH:
+ {
+ gdouble delta_x, delta_y;
+ clutter_event_get_scroll_delta ((ClutterEvent *)event, &delta_x, &delta_y);
+
+ if (direction == CLUTTER_TEXT_DIRECTION_RTL)
+ delta_x *= -1;
+
+ if (priv->vertical)
+ st_adjustment_adjust_for_scroll_event (priv->adjustment, delta_y);
+ else
+ st_adjustment_adjust_for_scroll_event (priv->adjustment, delta_x);
+ }
+ break;
+ case CLUTTER_SCROLL_LEFT:
+ case CLUTTER_SCROLL_RIGHT:
+ if (direction == CLUTTER_TEXT_DIRECTION_RTL)
+ scroll_dir = scroll_dir == CLUTTER_SCROLL_LEFT ? CLUTTER_SCROLL_RIGHT
+ : CLUTTER_SCROLL_LEFT;
+ /* Fall through */
+ case CLUTTER_SCROLL_UP:
+ case CLUTTER_SCROLL_DOWN:
+ adjust_with_direction (priv->adjustment, scroll_dir);
+ break;
+ default:
+ g_return_val_if_reached (FALSE);
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+st_scroll_bar_class_init (StScrollBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ object_class->get_property = st_scroll_bar_get_property;
+ object_class->set_property = st_scroll_bar_set_property;
+ object_class->dispose = st_scroll_bar_dispose;
+ object_class->constructor = st_scroll_bar_constructor;
+
+ actor_class->get_preferred_width = st_scroll_bar_get_preferred_width;
+ actor_class->get_preferred_height = st_scroll_bar_get_preferred_height;
+ actor_class->allocate = st_scroll_bar_allocate;
+ actor_class->scroll_event = st_scroll_bar_scroll_event;
+ actor_class->unmap = st_scroll_bar_unmap;
+
+ /**
+ * StScrollBar:adjustment:
+ *
+ * The #StAdjustment controlling the #StScrollBar.
+ */
+ props[PROP_ADJUSTMENT] =
+ g_param_spec_object ("adjustment", "Adjustment", "The adjustment",
+ ST_TYPE_ADJUSTMENT,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StScrollBar:vertical:
+ *
+ * Whether the #StScrollBar is vertical. If %FALSE it is horizontal.
+ */
+ props[PROP_VERTICAL] =
+ g_param_spec_boolean ("vertical",
+ "Vertical Orientation",
+ "Vertical Orientation",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+
+
+ /**
+ * StScrollBar::scroll-start:
+ * @bar: a #StScrollBar
+ *
+ * Emitted when the #StScrollBar begins scrolling.
+ */
+ signals[SCROLL_START] =
+ g_signal_new ("scroll-start",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StScrollBarClass, scroll_start),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * StScrollBar::scroll-stop:
+ * @bar: a #StScrollBar
+ *
+ * Emitted when the #StScrollBar finishes scrolling.
+ */
+ signals[SCROLL_STOP] =
+ g_signal_new ("scroll-stop",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StScrollBarClass, scroll_stop),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+move_slider (StScrollBar *bar,
+ gfloat x,
+ gfloat y)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+ ClutterTextDirection direction;
+ gdouble position, lower, upper, page_size;
+ gfloat ux, uy, pos, size;
+
+ if (!priv->adjustment)
+ return;
+
+ if (!clutter_actor_transform_stage_point (priv->trough, x, y, &ux, &uy))
+ return;
+
+ if (priv->vertical)
+ size = clutter_actor_get_height (priv->trough)
+ - clutter_actor_get_height (priv->handle);
+ else
+ size = clutter_actor_get_width (priv->trough)
+ - clutter_actor_get_width (priv->handle);
+
+ if (size == 0)
+ return;
+
+ if (priv->vertical)
+ pos = uy - priv->y_origin;
+ else
+ pos = ux - priv->x_origin;
+ pos = CLAMP (pos, 0, size);
+
+ st_adjustment_get_values (priv->adjustment,
+ NULL,
+ &lower,
+ &upper,
+ NULL,
+ NULL,
+ &page_size);
+
+ direction = clutter_actor_get_text_direction (CLUTTER_ACTOR (bar));
+ if (!priv->vertical && direction == CLUTTER_TEXT_DIRECTION_RTL)
+ pos = size - pos;
+
+ position = ((pos / size)
+ * (upper - lower - page_size))
+ + lower;
+
+ st_adjustment_set_value (priv->adjustment, position);
+}
+
+static void
+stop_scrolling (StScrollBar *bar)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+ if (!priv->grab_device)
+ return;
+
+ st_widget_remove_style_pseudo_class (ST_WIDGET (priv->handle), "active");
+
+ if (priv->grab)
+ {
+ clutter_grab_dismiss (priv->grab);
+ g_clear_pointer (&priv->grab, clutter_grab_unref);
+ }
+
+ priv->grab_device = NULL;
+ g_signal_emit (bar, signals[SCROLL_STOP], 0);
+}
+
+static gboolean
+handle_motion_event_cb (ClutterActor *trough,
+ ClutterMotionEvent *event,
+ StScrollBar *bar)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+ if (!priv->grab_device)
+ return FALSE;
+
+ move_slider (bar, event->x, event->y);
+ return TRUE;
+}
+
+static gboolean
+handle_button_release_event_cb (ClutterActor *trough,
+ ClutterButtonEvent *event,
+ StScrollBar *bar)
+{
+ if (event->button != 1)
+ return FALSE;
+
+ stop_scrolling (bar);
+ return TRUE;
+}
+
+static gboolean
+handle_button_press_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *bar)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
+ ClutterInputDevice *device = clutter_event_get_device ((ClutterEvent*) event);
+ ClutterActor *stage;
+
+ if (event->button != 1)
+ return FALSE;
+
+ if (!clutter_actor_transform_stage_point (priv->handle,
+ event->x,
+ event->y,
+ &priv->x_origin,
+ &priv->y_origin))
+ return FALSE;
+
+ st_widget_add_style_pseudo_class (ST_WIDGET (priv->handle), "active");
+
+ /* Account for the scrollbar-trough-handle nesting. */
+ priv->x_origin += clutter_actor_get_x (priv->trough);
+ priv->y_origin += clutter_actor_get_y (priv->trough);
+
+ g_assert (!priv->grab_device);
+
+ stage = clutter_actor_get_stage (actor);
+ priv->grab = clutter_stage_grab (CLUTTER_STAGE (stage), priv->handle);
+ priv->grab_device = device;
+ g_signal_emit (bar, signals[SCROLL_START], 0);
+
+ return TRUE;
+}
+
+static gboolean
+trough_paging_cb (StScrollBar *self)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+ ClutterTextDirection direction;
+ g_autoptr (ClutterTransition) transition = NULL;
+ StSettings *settings;
+ gfloat handle_pos, event_pos, tx, ty;
+ gdouble value, new_value;
+ gdouble page_increment;
+ gdouble slow_down_factor;
+ gboolean ret;
+
+ gulong mode;
+
+ if (priv->paging_event_no == 0)
+ {
+ /* Scroll on after initial timeout. */
+ mode = CLUTTER_EASE_OUT_CUBIC;
+ ret = FALSE;
+ priv->paging_event_no = 1;
+ priv->paging_source_id = g_timeout_add (
+ PAGING_INITIAL_REPEAT_TIMEOUT,
+ (GSourceFunc) trough_paging_cb,
+ self);
+ g_source_set_name_by_id (priv->paging_source_id, "[gnome-shell] trough_paging_cb");
+ }
+ else if (priv->paging_event_no == 1)
+ {
+ /* Scroll on after subsequent timeout. */
+ ret = FALSE;
+ mode = CLUTTER_EASE_IN_CUBIC;
+ priv->paging_event_no = 2;
+ priv->paging_source_id = g_timeout_add (
+ PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+ (GSourceFunc) trough_paging_cb,
+ self);
+ g_source_set_name_by_id (priv->paging_source_id, "[gnome-shell] trough_paging_cb");
+ }
+ else
+ {
+ /* Keep scrolling. */
+ ret = TRUE;
+ mode = CLUTTER_LINEAR;
+ priv->paging_event_no++;
+ }
+
+ /* Do the scrolling */
+ st_adjustment_get_values (priv->adjustment,
+ &value, NULL, NULL,
+ NULL, &page_increment, NULL);
+
+ if (priv->vertical)
+ handle_pos = clutter_actor_get_y (priv->handle);
+ else
+ handle_pos = clutter_actor_get_x (priv->handle);
+
+ clutter_actor_transform_stage_point (CLUTTER_ACTOR (priv->trough),
+ priv->move_x,
+ priv->move_y,
+ &tx, &ty);
+
+ direction = clutter_actor_get_text_direction (CLUTTER_ACTOR (self));
+ if (!priv->vertical && direction == CLUTTER_TEXT_DIRECTION_RTL)
+ page_increment *= -1;
+
+ if (priv->vertical)
+ event_pos = ty;
+ else
+ event_pos = tx;
+
+ if (event_pos > handle_pos)
+ {
+ if (priv->paging_direction == NONE)
+ {
+ /* Remember direction. */
+ priv->paging_direction = DOWN;
+ }
+ if (priv->paging_direction == UP)
+ {
+ /* Scrolled far enough. */
+ return FALSE;
+ }
+ new_value = value + page_increment;
+ }
+ else
+ {
+ if (priv->paging_direction == NONE)
+ {
+ /* Remember direction. */
+ priv->paging_direction = UP;
+ }
+ if (priv->paging_direction == DOWN)
+ {
+ /* Scrolled far enough. */
+ return FALSE;
+ }
+ new_value = value - page_increment;
+ }
+
+ /* Stop existing transition, if one exists */
+ st_adjustment_remove_transition (priv->adjustment, "value");
+
+ settings = st_settings_get ();
+ g_object_get (settings, "slow-down-factor", &slow_down_factor, NULL);
+
+ /* FIXME: Creating a new transition for each scroll is probably not the best
+ * idea, but it's a lot less involved than extending the current animation */
+ transition = g_object_new (CLUTTER_TYPE_PROPERTY_TRANSITION,
+ "property-name", "value",
+ "interval", clutter_interval_new (G_TYPE_DOUBLE, value, new_value),
+ "duration", (guint)(PAGING_SUBSEQUENT_REPEAT_TIMEOUT * slow_down_factor),
+ "progress-mode", mode,
+ "remove-on-complete", TRUE,
+ NULL);
+ st_adjustment_add_transition (priv->adjustment, "value", transition);
+
+ return ret;
+}
+
+static gboolean
+trough_button_press_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *self)
+{
+ StScrollBarPrivate *priv;
+
+ g_return_val_if_fail (self, FALSE);
+
+ if (event->button != 1)
+ return FALSE;
+
+ priv = st_scroll_bar_get_instance_private (self);
+ if (priv->adjustment == NULL)
+ return FALSE;
+
+ priv->move_x = event->x;
+ priv->move_y = event->y;
+ priv->paging_direction = NONE;
+ priv->paging_event_no = 0;
+ trough_paging_cb (self);
+
+ return TRUE;
+}
+
+static gboolean
+trough_button_release_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *self)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+
+ if (event->button != 1)
+ return FALSE;
+
+ g_clear_handle_id (&priv->paging_source_id, g_source_remove);
+
+ return TRUE;
+}
+
+static gboolean
+trough_leave_event_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ StScrollBar *self)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+
+ if (priv->paging_source_id)
+ {
+ g_clear_handle_id (&priv->paging_source_id, g_source_remove);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+st_scroll_bar_notify_reactive (StScrollBar *self)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+
+ gboolean reactive = clutter_actor_get_reactive (CLUTTER_ACTOR (self));
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (priv->trough), reactive);
+ clutter_actor_set_reactive (CLUTTER_ACTOR (priv->handle), reactive);
+}
+
+static void
+st_scroll_bar_init (StScrollBar *self)
+{
+ StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+
+ priv->trough = (ClutterActor *) st_bin_new ();
+ clutter_actor_set_reactive ((ClutterActor *) priv->trough, TRUE);
+ clutter_actor_set_name (CLUTTER_ACTOR (priv->trough), "trough");
+ clutter_actor_add_child (CLUTTER_ACTOR (self),
+ CLUTTER_ACTOR (priv->trough));
+ g_signal_connect (priv->trough, "button-press-event",
+ G_CALLBACK (trough_button_press_event_cb), self);
+ g_signal_connect (priv->trough, "button-release-event",
+ G_CALLBACK (trough_button_release_event_cb), self);
+ g_signal_connect (priv->trough, "leave-event",
+ G_CALLBACK (trough_leave_event_cb), self);
+
+ priv->handle = (ClutterActor *) st_button_new ();
+ clutter_actor_set_name (CLUTTER_ACTOR (priv->handle), "hhandle");
+ clutter_actor_add_child (CLUTTER_ACTOR (self),
+ CLUTTER_ACTOR (priv->handle));
+ g_signal_connect (priv->handle, "button-press-event",
+ G_CALLBACK (handle_button_press_event_cb), self);
+ g_signal_connect (priv->handle, "button-release-event",
+ G_CALLBACK (handle_button_release_event_cb), self);
+ g_signal_connect (priv->handle, "motion-event",
+ G_CALLBACK (handle_motion_event_cb), self);
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
+
+ g_signal_connect (self, "notify::reactive",
+ G_CALLBACK (st_scroll_bar_notify_reactive), NULL);
+}
+
+StWidget *
+st_scroll_bar_new (StAdjustment *adjustment)
+{
+ return g_object_new (ST_TYPE_SCROLL_BAR,
+ "adjustment", adjustment,
+ NULL);
+}
+
+static void
+on_notify_value (GObject *object,
+ GParamSpec *pspec,
+ StScrollBar *bar)
+{
+ scroll_bar_update_positions (bar);
+}
+
+static void
+on_changed (StAdjustment *adjustment,
+ StScrollBar *bar)
+{
+ scroll_bar_update_positions (bar);
+}
+
+void
+st_scroll_bar_set_adjustment (StScrollBar *bar,
+ StAdjustment *adjustment)
+{
+ StScrollBarPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_BAR (bar));
+
+ priv = st_scroll_bar_get_instance_private (bar);
+
+ if (adjustment == priv->adjustment)
+ return;
+
+ if (priv->adjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->adjustment,
+ on_notify_value,
+ bar);
+ g_signal_handlers_disconnect_by_func (priv->adjustment,
+ on_changed,
+ bar);
+ g_object_unref (priv->adjustment);
+ priv->adjustment = NULL;
+ }
+
+ if (adjustment)
+ {
+ priv->adjustment = g_object_ref (adjustment);
+
+ g_signal_connect (priv->adjustment, "notify::value",
+ G_CALLBACK (on_notify_value),
+ bar);
+ g_signal_connect (priv->adjustment, "changed",
+ G_CALLBACK (on_changed),
+ bar);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bar));
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (bar), props[PROP_ADJUSTMENT]);
+}
+
+/**
+ * st_scroll_bar_get_adjustment:
+ * @bar: a #StScrollbar
+ *
+ * Gets the #StAdjustment that controls the current position of @bar.
+ *
+ * Returns: (transfer none): an #StAdjustment
+ */
+StAdjustment *
+st_scroll_bar_get_adjustment (StScrollBar *bar)
+{
+ g_return_val_if_fail (ST_IS_SCROLL_BAR (bar), NULL);
+
+ return ((StScrollBarPrivate *)ST_SCROLL_BAR_PRIVATE (bar))->adjustment;
+}
+
diff --git a/src/st/st-scroll-bar.h b/src/st/st-scroll-bar.h
new file mode 100644
index 0000000..2c69fdd
--- /dev/null
+++ b/src/st/st-scroll-bar.h
@@ -0,0 +1,53 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scroll-bar.h: Scroll bar actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_SCROLL_BAR_H__
+#define __ST_SCROLL_BAR_H__
+
+#include <st/st-adjustment.h>
+#include <st/st-widget.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SCROLL_BAR (st_scroll_bar_get_type())
+G_DECLARE_DERIVABLE_TYPE (StScrollBar, st_scroll_bar, ST, SCROLL_BAR, StWidget)
+
+struct _StScrollBarClass
+{
+ StWidgetClass parent_class;
+
+ /* signals */
+ void (*scroll_start) (StScrollBar *bar);
+ void (*scroll_stop) (StScrollBar *bar);
+};
+
+StWidget *st_scroll_bar_new (StAdjustment *adjustment);
+
+void st_scroll_bar_set_adjustment (StScrollBar *bar,
+ StAdjustment *adjustment);
+StAdjustment *st_scroll_bar_get_adjustment (StScrollBar *bar);
+
+G_END_DECLS
+
+#endif /* __ST_SCROLL_BAR_H__ */
diff --git a/src/st/st-scroll-view-fade.c b/src/st/st-scroll-view-fade.c
new file mode 100644
index 0000000..77b1d0b
--- /dev/null
+++ b/src/st/st-scroll-view-fade.c
@@ -0,0 +1,461 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scroll-view-fade.h: Edge fade effect for StScrollView
+ *
+ * Copyright 2010 Intel Corporation.
+ * Copyright 2011 Adel Gadllah
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "st-private.h"
+#include "st-scroll-view-fade.h"
+#include "st-scroll-view.h"
+#include "st-widget.h"
+#include "st-theme-node.h"
+#include "st-scroll-bar.h"
+#include "st-scrollable.h"
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#define DEFAULT_FADE_OFFSET 68.0f
+
+#include "st-scroll-view-fade-generated.h"
+
+struct _StScrollViewFade
+{
+ ClutterShaderEffect parent_instance;
+
+ /* a back pointer to our actor, so that we can query it */
+ ClutterActor *actor;
+
+ StAdjustment *vadjustment;
+ StAdjustment *hadjustment;
+
+ guint fade_edges : 1;
+ guint extend_fade_area: 1;
+
+ ClutterMargin fade_margins;
+};
+
+G_DEFINE_TYPE (StScrollViewFade,
+ st_scroll_view_fade,
+ CLUTTER_TYPE_SHADER_EFFECT);
+
+enum {
+ PROP_0,
+
+ PROP_FADE_MARGINS,
+ PROP_FADE_EDGES,
+ PROP_EXTEND_FADE_AREA,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+static CoglTexture *
+st_scroll_view_fade_create_texture (ClutterOffscreenEffect *effect,
+ gfloat min_width,
+ gfloat min_height)
+{
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ return COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, min_width, min_height));
+}
+
+static char *
+st_scroll_view_fade_get_static_shader_source (ClutterShaderEffect *effect)
+{
+ return g_strdup (st_scroll_view_fade_glsl);
+}
+
+
+static void
+st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect,
+ ClutterPaintNode *node,
+ ClutterPaintContext *paint_context)
+{
+ StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect);
+ ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
+ ClutterOffscreenEffectClass *parent;
+
+ gdouble value, lower, upper, page_size;
+ ClutterActor *vscroll = st_scroll_view_get_vscroll_bar (ST_SCROLL_VIEW (self->actor));
+ ClutterActor *hscroll = st_scroll_view_get_hscroll_bar (ST_SCROLL_VIEW (self->actor));
+ gboolean h_scroll_visible, v_scroll_visible, rtl;
+
+ ClutterActorBox allocation, content_box, paint_box;
+
+ float fade_area_topleft[2];
+ float fade_area_bottomright[2];
+ graphene_point3d_t verts[4];
+
+ clutter_actor_get_paint_box (self->actor, &paint_box);
+ clutter_actor_get_abs_allocation_vertices (self->actor, verts);
+
+ clutter_actor_get_allocation_box (self->actor, &allocation);
+ st_theme_node_get_content_box (st_widget_get_theme_node (ST_WIDGET (self->actor)),
+ (const ClutterActorBox *)&allocation, &content_box);
+
+ /*
+ * The FBO is based on the paint_volume's size which can be larger then the actual
+ * allocation, so we have to account for that when passing the positions
+ */
+ fade_area_topleft[0] = content_box.x1 + (verts[0].x - paint_box.x1);
+ fade_area_topleft[1] = content_box.y1 + (verts[0].y - paint_box.y1);
+ fade_area_bottomright[0] = content_box.x2 + (verts[3].x - paint_box.x2) + 1;
+ fade_area_bottomright[1] = content_box.y2 + (verts[3].y - paint_box.y2) + 1;
+
+ g_object_get (ST_SCROLL_VIEW (self->actor),
+ "hscrollbar-visible", &h_scroll_visible,
+ "vscrollbar-visible", &v_scroll_visible,
+ NULL);
+
+ if (v_scroll_visible)
+ {
+ if (clutter_actor_get_text_direction (self->actor) == CLUTTER_TEXT_DIRECTION_RTL)
+ fade_area_topleft[0] += clutter_actor_get_width (vscroll);
+
+ fade_area_bottomright[0] -= clutter_actor_get_width (vscroll);
+ }
+
+ if (h_scroll_visible)
+ fade_area_bottomright[1] -= clutter_actor_get_height (hscroll);
+
+ if (self->fade_margins.left < 0)
+ fade_area_topleft[0] -= ABS (self->fade_margins.left);
+ if (self->fade_margins.right < 0)
+ fade_area_bottomright[0] += ABS (self->fade_margins.right);
+ if (self->fade_margins.top < 0)
+ fade_area_topleft[1] -= ABS (self->fade_margins.top);
+ if (self->fade_margins.bottom < 0)
+ fade_area_bottomright[1] += ABS (self->fade_margins.bottom);
+
+ st_adjustment_get_values (self->vadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
+ value = (value - lower) / (upper - page_size - lower);
+ clutter_shader_effect_set_uniform (shader, "fade_edges_top", G_TYPE_INT, 1, self->fade_edges ? value >= 0.0 : value > 0.0);
+ clutter_shader_effect_set_uniform (shader, "fade_edges_bottom", G_TYPE_INT, 1, self->fade_edges ? value <= 1.0 : value < 1.0);
+
+ st_adjustment_get_values (self->hadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
+ value = (value - lower) / (upper - page_size - lower);
+ rtl = clutter_actor_get_text_direction (self->actor) == CLUTTER_TEXT_DIRECTION_RTL;
+ clutter_shader_effect_set_uniform (shader, "fade_edges_left", G_TYPE_INT, 1,
+ self->fade_edges ?
+ value >= 0.0 :
+ (rtl ? value < 1.0 : value > 0.0));
+ clutter_shader_effect_set_uniform (shader, "fade_edges_right", G_TYPE_INT, 1,
+ self->fade_edges ?
+ value <= 1.0 :
+ (rtl ? value > 0.0 : value < 1.0));
+
+ clutter_shader_effect_set_uniform (shader, "extend_fade_area", G_TYPE_INT, 1, self->extend_fade_area);
+ clutter_shader_effect_set_uniform (shader, "fade_offset_top", G_TYPE_FLOAT, 1, ABS (self->fade_margins.top));
+ clutter_shader_effect_set_uniform (shader, "fade_offset_bottom", G_TYPE_FLOAT, 1, ABS (self->fade_margins.bottom));
+ clutter_shader_effect_set_uniform (shader, "fade_offset_left", G_TYPE_FLOAT, 1, ABS (self->fade_margins.left));
+ clutter_shader_effect_set_uniform (shader, "fade_offset_right", G_TYPE_FLOAT, 1, ABS (self->fade_margins.right));
+ clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0);
+ clutter_shader_effect_set_uniform (shader, "height", G_TYPE_FLOAT, 1, clutter_actor_get_height (self->actor));
+ clutter_shader_effect_set_uniform (shader, "width", G_TYPE_FLOAT, 1, clutter_actor_get_width (self->actor));
+ clutter_shader_effect_set_uniform (shader, "fade_area_topleft", CLUTTER_TYPE_SHADER_FLOAT, 2, fade_area_topleft);
+ clutter_shader_effect_set_uniform (shader, "fade_area_bottomright", CLUTTER_TYPE_SHADER_FLOAT, 2, fade_area_bottomright);
+
+ parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (st_scroll_view_fade_parent_class);
+ parent->paint_target (effect, node, paint_context);
+}
+
+static void
+on_adjustment_changed (StAdjustment *adjustment,
+ ClutterEffect *effect)
+{
+ gdouble value, lower, upper, page_size;
+ gboolean needs_fade;
+ StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect);
+
+ st_adjustment_get_values (self->vadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
+ needs_fade = (value > lower + 0.1) || (value < upper - page_size - 0.1);
+
+ if (!needs_fade)
+ {
+ st_adjustment_get_values (self->hadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
+ needs_fade = (value > lower + 0.1) || (value < upper - page_size - 0.1);
+ }
+
+ clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), needs_fade);
+}
+
+static void
+st_scroll_view_fade_set_actor (ClutterActorMeta *meta,
+ ClutterActor *actor)
+{
+ StScrollViewFade *self = ST_SCROLL_VIEW_FADE (meta);
+ ClutterActorMetaClass *parent;
+
+ g_return_if_fail (actor == NULL || ST_IS_SCROLL_VIEW (actor));
+
+ if (self->vadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (self->vadjustment,
+ (gpointer)on_adjustment_changed,
+ self);
+ self->vadjustment = NULL;
+ }
+
+ if (self->hadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (self->hadjustment,
+ (gpointer)on_adjustment_changed,
+ self);
+ self->hadjustment = NULL;
+ }
+
+
+ if (actor)
+ {
+ StScrollView *scroll_view = ST_SCROLL_VIEW (actor);
+ StScrollBar *vscroll = ST_SCROLL_BAR (st_scroll_view_get_vscroll_bar (scroll_view));
+ StScrollBar *hscroll = ST_SCROLL_BAR (st_scroll_view_get_hscroll_bar (scroll_view));
+ self->vadjustment = ST_ADJUSTMENT (st_scroll_bar_get_adjustment (vscroll));
+ self->hadjustment = ST_ADJUSTMENT (st_scroll_bar_get_adjustment (hscroll));
+
+ g_signal_connect (self->vadjustment, "changed",
+ G_CALLBACK (on_adjustment_changed),
+ self);
+
+ g_signal_connect (self->hadjustment, "changed",
+ G_CALLBACK (on_adjustment_changed),
+ self);
+
+ on_adjustment_changed (NULL, CLUTTER_EFFECT (self));
+ }
+
+ parent = CLUTTER_ACTOR_META_CLASS (st_scroll_view_fade_parent_class);
+ parent->set_actor (meta, actor);
+
+ /* we keep a back pointer here, to avoid going through the ActorMeta */
+ self->actor = clutter_actor_meta_get_actor (meta);
+}
+
+static void
+st_scroll_view_fade_dispose (GObject *gobject)
+{
+ StScrollViewFade *self = ST_SCROLL_VIEW_FADE (gobject);
+
+ if (self->vadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (self->vadjustment,
+ (gpointer)on_adjustment_changed,
+ self);
+ self->vadjustment = NULL;
+ }
+
+ if (self->hadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (self->hadjustment,
+ (gpointer)on_adjustment_changed,
+ self);
+ self->hadjustment = NULL;
+ }
+
+ self->actor = NULL;
+
+ G_OBJECT_CLASS (st_scroll_view_fade_parent_class)->dispose (gobject);
+}
+
+static void
+st_scroll_view_set_fade_margins (StScrollViewFade *self,
+ ClutterMargin *fade_margins)
+{
+ if (self->fade_margins.left == fade_margins->left &&
+ self->fade_margins.right == fade_margins->right &&
+ self->fade_margins.top == fade_margins->top &&
+ self->fade_margins.bottom == fade_margins->bottom)
+ return;
+
+ self->fade_margins = *fade_margins;
+
+ if (self->actor != NULL)
+ clutter_actor_queue_redraw (self->actor);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_FADE_MARGINS]);
+}
+
+static void
+st_scroll_view_fade_set_fade_edges (StScrollViewFade *self,
+ gboolean fade_edges)
+{
+ if (self->fade_edges == fade_edges)
+ return;
+
+ g_object_freeze_notify (G_OBJECT (self));
+
+ self->fade_edges = fade_edges;
+
+ if (self->actor != NULL)
+ clutter_actor_queue_redraw (self->actor);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_FADE_EDGES]);
+ g_object_thaw_notify (G_OBJECT (self));
+}
+
+static void
+st_scroll_view_fade_set_extend_fade_area (StScrollViewFade *self,
+ gboolean extend_fade_area)
+{
+ if (self->extend_fade_area == extend_fade_area)
+ return;
+
+ self->extend_fade_area = extend_fade_area;
+
+ if (self->actor != NULL)
+ clutter_actor_queue_redraw (self->actor);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_EXTEND_FADE_AREA]);
+}
+
+static void
+st_scroll_view_fade_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollViewFade *self = ST_SCROLL_VIEW_FADE (object);
+
+ switch (prop_id)
+ {
+ case PROP_FADE_MARGINS:
+ st_scroll_view_set_fade_margins (self, g_value_get_boxed (value));
+ break;
+ case PROP_FADE_EDGES:
+ st_scroll_view_fade_set_fade_edges (self, g_value_get_boolean (value));
+ break;
+ case PROP_EXTEND_FADE_AREA:
+ st_scroll_view_fade_set_extend_fade_area (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_scroll_view_fade_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollViewFade *self = ST_SCROLL_VIEW_FADE (object);
+
+ switch (prop_id)
+ {
+ case PROP_FADE_MARGINS:
+ g_value_set_boxed (value, &self->fade_margins);
+ break;
+ case PROP_FADE_EDGES:
+ g_value_set_boolean (value, self->fade_edges);
+ break;
+ case PROP_EXTEND_FADE_AREA:
+ g_value_set_boolean (value, self->extend_fade_area);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_scroll_view_fade_class_init (StScrollViewFadeClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterShaderEffectClass *shader_class;
+ ClutterOffscreenEffectClass *offscreen_class;
+ ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
+
+ gobject_class->dispose = st_scroll_view_fade_dispose;
+ gobject_class->get_property = st_scroll_view_fade_get_property;
+ gobject_class->set_property = st_scroll_view_fade_set_property;
+
+ meta_class->set_actor = st_scroll_view_fade_set_actor;
+
+ shader_class = CLUTTER_SHADER_EFFECT_CLASS (klass);
+ shader_class->get_static_shader_source = st_scroll_view_fade_get_static_shader_source;
+
+ offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
+ offscreen_class->create_texture = st_scroll_view_fade_create_texture;
+ offscreen_class->paint_target = st_scroll_view_fade_paint_target;
+
+ /**
+ * StScrollViewFade:fade-margins:
+ *
+ * The margins widths that are faded.
+ */
+ props[PROP_FADE_MARGINS] =
+ g_param_spec_boxed ("fade-margins",
+ "Fade margins",
+ "The margin widths that are faded",
+ CLUTTER_TYPE_MARGIN,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StScrollViewFade:fade-edges:
+ *
+ * Whether the faded area should extend to the edges of the #StScrollViewFade.
+ */
+ props[PROP_FADE_EDGES] =
+ g_param_spec_boolean ("fade-edges",
+ "Fade Edges",
+ "Whether the faded area should extend to the edges",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StScrollViewFade:extend-fade-area:
+ *
+ * Whether faded edges should extend beyond the faded area of the #StScrollViewFade.
+ */
+ props[PROP_EXTEND_FADE_AREA] =
+ g_param_spec_boolean ("extend-fade-area",
+ "Extend Fade Area",
+ "Whether faded edges should extend beyond the faded area",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+}
+
+static void
+st_scroll_view_fade_init (StScrollViewFade *self)
+{
+ self->fade_margins = (ClutterMargin) {
+ DEFAULT_FADE_OFFSET,
+ DEFAULT_FADE_OFFSET,
+ DEFAULT_FADE_OFFSET,
+ DEFAULT_FADE_OFFSET,
+ };
+}
+
+/**
+ * st_scroll_view_fade_new:
+ *
+ * Create a new #StScrollViewFade.
+ *
+ * Returns: (transfer full): a new #StScrollViewFade
+ */
+ClutterEffect *
+st_scroll_view_fade_new (void)
+{
+ return g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
+}
diff --git a/src/st/st-scroll-view-fade.glsl b/src/st/st-scroll-view-fade.glsl
new file mode 100644
index 0000000..ba6582f
--- /dev/null
+++ b/src/st/st-scroll-view-fade.glsl
@@ -0,0 +1,77 @@
+/*
+ * st-scroll-view-fade.glsl: Edge fade effect for StScrollView
+ *
+ * Copyright 2010 Intel Corporation.
+ * Copyright 2011 Adel Gadllah
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+uniform sampler2D tex;
+uniform float height;
+uniform float width;
+uniform float fade_offset_top;
+uniform float fade_offset_bottom;
+uniform float fade_offset_left;
+uniform float fade_offset_right;
+uniform bool fade_edges_top;
+uniform bool fade_edges_right;
+uniform bool fade_edges_bottom;
+uniform bool fade_edges_left;
+uniform bool extend_fade_area;
+
+uniform vec2 fade_area_topleft;
+uniform vec2 fade_area_bottomright;
+
+void main ()
+{
+ cogl_color_out = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));
+
+ float y = height * cogl_tex_coord_in[0].y;
+ float x = width * cogl_tex_coord_in[0].x;
+ float ratio = 1.0;
+
+ if (x > fade_area_topleft[0] && x < fade_area_bottomright[0] &&
+ y > fade_area_topleft[1] && y < fade_area_bottomright[1])
+ {
+ float after_left = x - fade_area_topleft[0];
+ float before_right = fade_area_bottomright[0] - x;
+ float after_top = y - fade_area_topleft[1];
+ float before_bottom = fade_area_bottomright[1] - y;
+
+ if (after_top < fade_offset_top && fade_edges_top) {
+ ratio *= after_top / fade_offset_top;
+ }
+
+ if (before_bottom < fade_offset_bottom && fade_edges_bottom) {
+ ratio *= before_bottom / fade_offset_bottom;
+ }
+
+ if (after_left < fade_offset_left && fade_edges_left) {
+ ratio *= after_left / fade_offset_left;
+ }
+
+ if (before_right < fade_offset_right && fade_edges_right) {
+ ratio *= before_right / fade_offset_right;
+ }
+ } else if (extend_fade_area) {
+ if (x <= fade_area_topleft[0] && fade_edges_left ||
+ x >= fade_area_bottomright[0] && fade_edges_right ||
+ y <= fade_area_topleft[1] && fade_edges_top ||
+ y >= fade_area_bottomright[1] && fade_edges_bottom) {
+ ratio = 0.0;
+ }
+ }
+
+ cogl_color_out *= ratio;
+}
diff --git a/src/st/st-scroll-view-fade.h b/src/st/st-scroll-view-fade.h
new file mode 100644
index 0000000..2c65a77
--- /dev/null
+++ b/src/st/st-scroll-view-fade.h
@@ -0,0 +1,36 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scroll-view-fade.h: Edge fade effect for StScrollView
+ *
+ * Copyright 2010 Intel Corporation.
+ * Copyright 2011 Adel Gadllah
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_SCROLL_VIEW_FADE_H__
+#define __ST_SCROLL_VIEW_FADE_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SCROLL_VIEW_FADE (st_scroll_view_fade_get_type ())
+G_DECLARE_FINAL_TYPE (StScrollViewFade, st_scroll_view_fade,
+ ST, SCROLL_VIEW_FADE, ClutterShaderEffect)
+
+ClutterEffect *st_scroll_view_fade_new (void);
+
+G_END_DECLS
+
+#endif /* __ST_SCROLL_VIEW_FADE_H__ */
diff --git a/src/st/st-scroll-view.c b/src/st/st-scroll-view.c
new file mode 100644
index 0000000..50de481
--- /dev/null
+++ b/src/st/st-scroll-view.c
@@ -0,0 +1,1327 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010 Maxim Ermilov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:st-scroll-view
+ * @short_description: a container for scrollable children
+ *
+ * #StScrollView is a single child container for actors that implement
+ * #StScrollable. It provides scrollbars around the edge of the child to
+ * allow the user to move around the scrollable area.
+ */
+
+/* TODO: The code here currently only deals with height-for-width
+ * allocation; width-for-height allocation would need a second set of
+ * code paths through get_preferred_height()/get_preferred_width()/allocate()
+ * that reverse the roles of the horizontal and vertical scrollbars.
+ *
+ * TODO: The multiple layout passes with and without scrollbars when
+ * using the automatic policy causes considerable inefficiency because
+ * it breaks request caching; we should saved the last size passed
+ * into allocate() and if it's the same as previous size not repeat
+ * the determination of scrollbar visibility. This requires overriding
+ * queue_relayout() so we know when to discard the saved value.
+ *
+ * The size negotiation between the #StScrollView and the child is
+ * described in the documentation for #StScrollable; the significant
+ * part to note there is that reported minimum sizes for a scrolled
+ * child are the minimum sizes when no scrollbar is needed. This allows
+ * us to determine what scrollbars are visible without a need to look
+ * inside the #StAdjustment.
+ *
+ * The second simplification that we make that allows us to implement
+ * a straightforward height-for-width negotiation without multiple
+ * allocate passes is that when the scrollbar policy is
+ * AUTO, we always reserve space for the scrollbar in the
+ * reported minimum and natural size.
+ *
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=611740 for a more
+ * detailed description of the considerations involved.
+ */
+
+#include "st-enum-types.h"
+#include "st-private.h"
+#include "st-scroll-view.h"
+#include "st-scroll-bar.h"
+#include "st-scrollable.h"
+#include "st-scroll-view-fade.h"
+#include <clutter/clutter.h>
+#include <math.h>
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+static ClutterContainerIface *st_scroll_view_parent_iface = NULL;
+
+struct _StScrollViewPrivate
+{
+ /* a pointer to the child; this is actually stored
+ * inside StBin:child, but we keep it to avoid
+ * calling st_bin_get_child() every time we need it
+ */
+ ClutterActor *child;
+
+ StAdjustment *hadjustment;
+ ClutterActor *hscroll;
+ StAdjustment *vadjustment;
+ ClutterActor *vscroll;
+
+ StPolicyType hscrollbar_policy;
+ StPolicyType vscrollbar_policy;
+
+ gfloat row_size;
+ gfloat column_size;
+
+ StScrollViewFade *fade_effect;
+
+ guint row_size_set : 1;
+ guint column_size_set : 1;
+ guint mouse_scroll : 1;
+ guint overlay_scrollbars : 1;
+ guint hscrollbar_visible : 1;
+ guint vscrollbar_visible : 1;
+};
+
+G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN,
+ G_ADD_PRIVATE (StScrollView)
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init))
+
+enum {
+ PROP_0,
+
+ PROP_HSCROLL,
+ PROP_VSCROLL,
+ PROP_HSCROLLBAR_POLICY,
+ PROP_VSCROLLBAR_POLICY,
+ PROP_HSCROLLBAR_VISIBLE,
+ PROP_VSCROLLBAR_VISIBLE,
+ PROP_MOUSE_SCROLL,
+ PROP_OVERLAY_SCROLLBARS,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+static void
+st_scroll_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollViewPrivate *priv = ((StScrollView *) object)->priv;
+
+ switch (property_id)
+ {
+ case PROP_HSCROLL:
+ g_value_set_object (value, priv->hscroll);
+ break;
+ case PROP_VSCROLL:
+ g_value_set_object (value, priv->vscroll);
+ break;
+ case PROP_HSCROLLBAR_POLICY:
+ g_value_set_enum (value, priv->hscrollbar_policy);
+ break;
+ case PROP_VSCROLLBAR_POLICY:
+ g_value_set_enum (value, priv->vscrollbar_policy);
+ break;
+ case PROP_HSCROLLBAR_VISIBLE:
+ g_value_set_boolean (value, priv->hscrollbar_visible);
+ break;
+ case PROP_VSCROLLBAR_VISIBLE:
+ g_value_set_boolean (value, priv->vscrollbar_visible);
+ break;
+ case PROP_MOUSE_SCROLL:
+ g_value_set_boolean (value, priv->mouse_scroll);
+ break;
+ case PROP_OVERLAY_SCROLLBARS:
+ g_value_set_boolean (value, priv->overlay_scrollbars);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+/**
+ * st_scroll_view_update_fade_effect:
+ * @scroll: a #StScrollView
+ * @fade_margins: a #ClutterMargin defining the vertical fade effects, in pixels.
+ *
+ * Sets the fade effects in all four edges of the view. A value of 0
+ * disables the effect.
+ */
+void
+st_scroll_view_update_fade_effect (StScrollView *scroll,
+ ClutterMargin *fade_margins)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ /* A fade amount of other than 0 enables the effect. */
+ if (fade_margins->left != 0. || fade_margins->right != 0. ||
+ fade_margins->top != 0. || fade_margins->bottom != 0.)
+ {
+ if (priv->fade_effect == NULL)
+ {
+ priv->fade_effect = g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
+
+ clutter_actor_add_effect_with_name (CLUTTER_ACTOR (scroll), "fade",
+ CLUTTER_EFFECT (priv->fade_effect));
+ }
+
+ g_object_set (priv->fade_effect,
+ "fade-margins", fade_margins,
+ NULL);
+ }
+ else
+ {
+ if (priv->fade_effect != NULL)
+ {
+ clutter_actor_remove_effect (CLUTTER_ACTOR (scroll),
+ CLUTTER_EFFECT (priv->fade_effect));
+ priv->fade_effect = NULL;
+ }
+ }
+}
+
+static void
+st_scroll_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollView *self = ST_SCROLL_VIEW (object);
+ StScrollViewPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_MOUSE_SCROLL:
+ st_scroll_view_set_mouse_scrolling (self,
+ g_value_get_boolean (value));
+ break;
+ case PROP_OVERLAY_SCROLLBARS:
+ st_scroll_view_set_overlay_scrollbars (self,
+ g_value_get_boolean (value));
+ break;
+ case PROP_HSCROLLBAR_POLICY:
+ st_scroll_view_set_policy (self,
+ g_value_get_enum (value),
+ priv->vscrollbar_policy);
+ break;
+ case PROP_VSCROLLBAR_POLICY:
+ st_scroll_view_set_policy (self,
+ priv->hscrollbar_policy,
+ g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+st_scroll_view_dispose (GObject *object)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv;
+
+ if (priv->fade_effect)
+ {
+ clutter_actor_remove_effect (CLUTTER_ACTOR (object), CLUTTER_EFFECT (priv->fade_effect));
+ priv->fade_effect = NULL;
+ }
+
+ g_clear_pointer (&priv->vscroll, clutter_actor_destroy);
+ g_clear_pointer (&priv->hscroll, clutter_actor_destroy);
+
+ /* For most reliable freeing of memory, an object with signals
+ * like StAdjustment should be explicitly disposed. Since we own
+ * the adjustments, we take care of that. This also disconnects
+ * the signal handlers that we established on creation.
+ */
+ if (priv->hadjustment)
+ {
+ g_object_run_dispose (G_OBJECT (priv->hadjustment));
+ g_object_unref (priv->hadjustment);
+ priv->hadjustment = NULL;
+ }
+
+ if (priv->vadjustment)
+ {
+ g_object_run_dispose (G_OBJECT (priv->vadjustment));
+ g_object_unref (priv->vadjustment);
+ priv->vadjustment = NULL;
+ }
+
+ G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
+}
+
+static void
+st_scroll_view_paint (ClutterActor *actor,
+ ClutterPaintContext *paint_context)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+
+ st_widget_paint_background (ST_WIDGET (actor), paint_context);
+
+ if (priv->child)
+ clutter_actor_paint (priv->child, paint_context);
+ if (priv->hscrollbar_visible)
+ clutter_actor_paint (priv->hscroll, paint_context);
+ if (priv->vscrollbar_visible)
+ clutter_actor_paint (priv->vscroll, paint_context);
+}
+
+static void
+st_scroll_view_pick (ClutterActor *actor,
+ ClutterPickContext *pick_context)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+
+ /* Chain up so we get a bounding box pained (if we are reactive) */
+ CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->pick (actor, pick_context);
+
+ if (priv->child)
+ clutter_actor_pick (priv->child, pick_context);
+ if (priv->hscrollbar_visible)
+ clutter_actor_pick (priv->hscroll, pick_context);
+ if (priv->vscrollbar_visible)
+ clutter_actor_pick (priv->vscroll, pick_context);
+}
+
+static gboolean
+st_scroll_view_get_paint_volume (ClutterActor *actor,
+ ClutterPaintVolume *volume)
+{
+ return clutter_paint_volume_set_from_allocation (volume, actor);
+}
+
+static double
+get_scrollbar_width (StScrollView *scroll,
+ gfloat for_height)
+{
+ StScrollViewPrivate *priv = scroll->priv;
+
+ if (clutter_actor_is_visible (priv->vscroll))
+ {
+ gfloat min_size;
+
+ clutter_actor_get_preferred_width (CLUTTER_ACTOR (priv->vscroll), for_height,
+ &min_size, NULL);
+ return min_size;
+ }
+ else
+ return 0;
+}
+
+static double
+get_scrollbar_height (StScrollView *scroll,
+ gfloat for_width)
+{
+ StScrollViewPrivate *priv = scroll->priv;
+
+ if (clutter_actor_is_visible (priv->hscroll))
+ {
+ gfloat min_size;
+
+ clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->hscroll), for_width,
+ &min_size, NULL);
+
+ return min_size;
+ }
+ else
+ return 0;
+}
+
+static void
+st_scroll_view_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ gboolean account_for_vscrollbar = FALSE;
+ gfloat min_width = 0, natural_width;
+ gfloat child_min_width, child_natural_width;
+
+ if (!priv->child)
+ return;
+
+ st_theme_node_adjust_for_height (theme_node, &for_height);
+
+ clutter_actor_get_preferred_width (priv->child, -1,
+ &child_min_width, &child_natural_width);
+
+ natural_width = child_natural_width;
+
+ switch (priv->hscrollbar_policy)
+ {
+ case ST_POLICY_NEVER:
+ min_width = child_min_width;
+ break;
+ case ST_POLICY_ALWAYS:
+ case ST_POLICY_AUTOMATIC:
+ case ST_POLICY_EXTERNAL:
+ /* Should theoretically use the min width of the hscrollbar,
+ * but that's not cleanly defined at the moment */
+ min_width = 0;
+ break;
+ default:
+ g_warn_if_reached();
+ break;
+ }
+
+ switch (priv->vscrollbar_policy)
+ {
+ case ST_POLICY_NEVER:
+ case ST_POLICY_EXTERNAL:
+ account_for_vscrollbar = FALSE;
+ break;
+ case ST_POLICY_ALWAYS:
+ account_for_vscrollbar = !priv->overlay_scrollbars;
+ break;
+ case ST_POLICY_AUTOMATIC:
+ /* For automatic scrollbars, we always request space for the vertical
+ * scrollbar; we won't know whether we actually need one until our
+ * height is assigned in allocate().
+ */
+ account_for_vscrollbar = !priv->overlay_scrollbars;
+ break;
+ default:
+ g_warn_if_reached();
+ break;
+ }
+
+ if (account_for_vscrollbar)
+ {
+ float sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), for_height);
+
+ min_width += sb_width;
+ natural_width += sb_width;
+ }
+
+ if (min_width_p)
+ *min_width_p = min_width;
+
+ if (natural_width_p)
+ *natural_width_p = natural_width;
+
+ st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
+}
+
+static void
+st_scroll_view_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ gboolean account_for_hscrollbar = FALSE;
+ gfloat min_height = 0, natural_height;
+ gfloat child_min_height, child_natural_height;
+ gfloat child_min_width;
+ gfloat sb_width;
+
+ if (!priv->child)
+ return;
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ clutter_actor_get_preferred_width (priv->child, -1,
+ &child_min_width, NULL);
+
+ if (min_height_p)
+ *min_height_p = 0;
+
+ sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1);
+
+ switch (priv->vscrollbar_policy)
+ {
+ case ST_POLICY_NEVER:
+ case ST_POLICY_EXTERNAL:
+ break;
+ case ST_POLICY_ALWAYS:
+ case ST_POLICY_AUTOMATIC:
+ /* We've requested space for the scrollbar, subtract it back out */
+ for_width -= sb_width;
+ break;
+ default:
+ g_warn_if_reached();
+ break;
+ }
+
+ switch (priv->hscrollbar_policy)
+ {
+ case ST_POLICY_NEVER:
+ case ST_POLICY_EXTERNAL:
+ account_for_hscrollbar = FALSE;
+ break;
+ case ST_POLICY_ALWAYS:
+ account_for_hscrollbar = !priv->overlay_scrollbars;
+ break;
+ case ST_POLICY_AUTOMATIC:
+ /* For automatic scrollbars, we always request space for the horizontal
+ * scrollbar; we won't know whether we actually need one until our
+ * width is assigned in allocate().
+ */
+ account_for_hscrollbar = !priv->overlay_scrollbars;
+ break;
+ default:
+ g_warn_if_reached();
+ break;
+ }
+
+ clutter_actor_get_preferred_height (priv->child, for_width,
+ &child_min_height, &child_natural_height);
+
+ natural_height = child_natural_height;
+
+ switch (priv->vscrollbar_policy)
+ {
+ case ST_POLICY_NEVER:
+ min_height = child_min_height;
+ break;
+ case ST_POLICY_ALWAYS:
+ case ST_POLICY_AUTOMATIC:
+ case ST_POLICY_EXTERNAL:
+ /* Should theoretically use the min height of the vscrollbar,
+ * but that's not cleanly defined at the moment */
+ min_height = 0;
+ break;
+ default:
+ g_warn_if_reached();
+ break;
+ }
+
+ if (account_for_hscrollbar)
+ {
+ float sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), for_width);
+
+ min_height += sb_height;
+ natural_height += sb_height;
+ }
+
+ if (min_height_p)
+ *min_height_p = min_height;
+
+ if (natural_height_p)
+ *natural_height_p = natural_height;
+
+ st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
+}
+
+static void
+st_scroll_view_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ ClutterActorBox content_box, child_box;
+ gfloat avail_width, avail_height, sb_width, sb_height;
+ gboolean hscrollbar_visible, vscrollbar_visible;
+
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+
+ clutter_actor_set_allocation (actor, box);
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ avail_width = content_box.x2 - content_box.x1;
+ avail_height = content_box.y2 - content_box.y1;
+
+ if (clutter_actor_get_request_mode (actor) == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1);
+ sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), sb_width);
+ }
+ else
+ {
+ sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), -1);
+ sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), sb_height);
+ }
+
+ /* Determine what scrollbars are visible. The basic idea of the
+ * handling of an automatic scrollbars is that we start off with the
+ * assumption that we don't need any scrollbars, see if that works,
+ * and if not add horizontal and vertical scrollbars until we are no
+ * longer overflowing.
+ */
+ if (priv->child)
+ {
+ gfloat child_min_width;
+ gfloat child_min_height;
+
+ clutter_actor_get_preferred_width (priv->child, -1,
+ &child_min_width, NULL);
+
+ if (priv->vscrollbar_policy == ST_POLICY_AUTOMATIC)
+ {
+ if (priv->hscrollbar_policy == ST_POLICY_AUTOMATIC)
+ {
+ /* Pass one, try without a vertical scrollbar */
+ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL);
+ vscrollbar_visible = child_min_height > avail_height;
+ hscrollbar_visible = child_min_width > avail_width - (vscrollbar_visible ? sb_width : 0);
+ vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0);
+
+ /* Pass two - if we needed a vertical scrollbar, get a new preferred height */
+ if (vscrollbar_visible)
+ {
+ clutter_actor_get_preferred_height (priv->child, MAX (avail_width - sb_width, 0),
+ &child_min_height, NULL);
+ hscrollbar_visible = child_min_width > avail_width - sb_width;
+ }
+ }
+ else
+ {
+ hscrollbar_visible = priv->hscrollbar_policy == ST_POLICY_ALWAYS;
+
+ /* try without a vertical scrollbar */
+ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL);
+ vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0);
+ }
+ }
+ else
+ {
+ vscrollbar_visible = priv->vscrollbar_policy == ST_POLICY_ALWAYS;
+
+ if (priv->hscrollbar_policy == ST_POLICY_AUTOMATIC)
+ hscrollbar_visible = child_min_width > avail_height - (vscrollbar_visible ? 0 : sb_width);
+ else
+ hscrollbar_visible = priv->hscrollbar_policy == ST_POLICY_ALWAYS;
+ }
+ }
+ else
+ {
+ hscrollbar_visible = priv->hscrollbar_policy != ST_POLICY_NEVER &&
+ priv->hscrollbar_policy != ST_POLICY_EXTERNAL;
+ vscrollbar_visible = priv->vscrollbar_policy != ST_POLICY_NEVER &&
+ priv->vscrollbar_policy != ST_POLICY_EXTERNAL;
+ }
+
+ /* Whether or not we show the scrollbars, if the scrollbars are visible
+ * actors, we need to give them some allocation, so we unconditionally
+ * give them the "right" allocation; that might overlap the child when
+ * the scrollbars are not visible, but it doesn't matter because we
+ * don't include them in pick or paint.
+ */
+
+ /* Vertical scrollbar */
+ if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
+ {
+ child_box.x1 = content_box.x1;
+ child_box.x2 = content_box.x1 + sb_width;
+ }
+ else
+ {
+ child_box.x1 = content_box.x2 - sb_width;
+ child_box.x2 = content_box.x2;
+ }
+ child_box.y1 = content_box.y1;
+ child_box.y2 = content_box.y2 - (hscrollbar_visible ? sb_height : 0);
+
+ clutter_actor_allocate (priv->vscroll, &child_box);
+
+ /* Horizontal scrollbar */
+ if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
+ {
+ child_box.x1 = content_box.x1 + (vscrollbar_visible ? sb_width : 0);
+ child_box.x2 = content_box.x2;
+ }
+ else
+ {
+ child_box.x1 = content_box.x1;
+ child_box.x2 = content_box.x2 - (vscrollbar_visible ? sb_width : 0);
+ }
+ child_box.y1 = content_box.y2 - sb_height;
+ child_box.y2 = content_box.y2;
+
+ clutter_actor_allocate (priv->hscroll, &child_box);
+
+ /* In case the scrollbar policy is NEVER or EXTERNAL or scrollbars
+ * should be overlaid, we don't trim the content box allocation by
+ * the scrollbar size.
+ * Fold this into the scrollbar sizes to simplify the rest of the
+ * computations.
+ */
+ if (priv->hscrollbar_policy == ST_POLICY_NEVER ||
+ priv->hscrollbar_policy == ST_POLICY_EXTERNAL ||
+ priv->overlay_scrollbars)
+ sb_height = 0;
+ if (priv->vscrollbar_policy == ST_POLICY_NEVER ||
+ priv->vscrollbar_policy == ST_POLICY_EXTERNAL ||
+ priv->overlay_scrollbars)
+ sb_width = 0;
+
+ /* Child */
+ if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
+ {
+ child_box.x1 = content_box.x1 + sb_width;
+ child_box.x2 = content_box.x2;
+ }
+ else
+ {
+ child_box.x1 = content_box.x1;
+ child_box.x2 = content_box.x2 - sb_width;
+ }
+ child_box.y1 = content_box.y1;
+ child_box.y2 = content_box.y2 - sb_height;
+
+ if (priv->child)
+ clutter_actor_allocate (priv->child, &child_box);
+
+ if (priv->hscrollbar_visible != hscrollbar_visible)
+ {
+ g_object_freeze_notify (G_OBJECT (actor));
+ priv->hscrollbar_visible = hscrollbar_visible;
+ g_object_notify_by_pspec (G_OBJECT (actor),
+ props[PROP_HSCROLLBAR_VISIBLE]);
+ g_object_thaw_notify (G_OBJECT (actor));
+ }
+
+ if (priv->vscrollbar_visible != vscrollbar_visible)
+ {
+ g_object_freeze_notify (G_OBJECT (actor));
+ priv->vscrollbar_visible = vscrollbar_visible;
+ g_object_notify_by_pspec (G_OBJECT (actor),
+ props[PROP_VSCROLLBAR_VISIBLE]);
+ g_object_thaw_notify (G_OBJECT (actor));
+ }
+
+}
+
+static void
+adjust_with_direction (StAdjustment *adj,
+ ClutterScrollDirection direction)
+{
+ gdouble delta;
+
+ switch (direction)
+ {
+ case CLUTTER_SCROLL_UP:
+ case CLUTTER_SCROLL_LEFT:
+ delta = -1.0;
+ break;
+ case CLUTTER_SCROLL_RIGHT:
+ case CLUTTER_SCROLL_DOWN:
+ delta = 1.0;
+ break;
+ case CLUTTER_SCROLL_SMOOTH:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ st_adjustment_adjust_for_scroll_event (adj, delta);
+}
+
+static void
+st_scroll_view_style_changed (StWidget *widget)
+{
+ StScrollView *self = ST_SCROLL_VIEW (widget);
+ gboolean has_vfade, has_hfade;
+ double vfade_offset = 0.0;
+ double hfade_offset = 0.0;
+
+ StThemeNode *theme_node = st_widget_get_theme_node (widget);
+
+ has_vfade = st_theme_node_lookup_length (theme_node, "-st-vfade-offset", FALSE, &vfade_offset);
+ has_hfade = st_theme_node_lookup_length (theme_node, "-st-hfade-offset", FALSE, &hfade_offset);
+ if (has_vfade || has_hfade)
+ {
+ st_scroll_view_update_fade_effect (self,
+ &(ClutterMargin) {
+ .top = vfade_offset,
+ .bottom = vfade_offset,
+ .left = hfade_offset,
+ .right = hfade_offset,
+ });
+ }
+
+ ST_WIDGET_CLASS (st_scroll_view_parent_class)->style_changed (widget);
+}
+
+static gboolean
+st_scroll_view_scroll_event (ClutterActor *self,
+ ClutterScrollEvent *event)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
+ ClutterTextDirection direction;
+
+ /* don't handle scroll events if requested not to */
+ if (!priv->mouse_scroll)
+ return FALSE;
+
+ if (clutter_event_is_pointer_emulated ((ClutterEvent *) event))
+ return TRUE;
+
+ direction = clutter_actor_get_text_direction (self);
+
+ switch (event->direction)
+ {
+ case CLUTTER_SCROLL_SMOOTH:
+ {
+ gdouble delta_x, delta_y;
+ clutter_event_get_scroll_delta ((ClutterEvent *)event, &delta_x, &delta_y);
+
+ if (direction == CLUTTER_TEXT_DIRECTION_RTL)
+ delta_x *= -1;
+
+ st_adjustment_adjust_for_scroll_event (priv->hadjustment, delta_x);
+ st_adjustment_adjust_for_scroll_event (priv->vadjustment, delta_y);
+ }
+ break;
+ case CLUTTER_SCROLL_UP:
+ case CLUTTER_SCROLL_DOWN:
+ adjust_with_direction (priv->vadjustment, event->direction);
+ break;
+ case CLUTTER_SCROLL_LEFT:
+ case CLUTTER_SCROLL_RIGHT:
+ if (direction == CLUTTER_TEXT_DIRECTION_RTL)
+ {
+ ClutterScrollDirection dir;
+
+ dir = event->direction == CLUTTER_SCROLL_LEFT ? CLUTTER_SCROLL_RIGHT
+ : CLUTTER_SCROLL_LEFT;
+ adjust_with_direction (priv->hadjustment, dir);
+ }
+ else
+ {
+ adjust_with_direction (priv->hadjustment, event->direction);
+ }
+ break;
+ default:
+ g_warn_if_reached();
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+st_scroll_view_class_init (StScrollViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ object_class->get_property = st_scroll_view_get_property;
+ object_class->set_property = st_scroll_view_set_property;
+ object_class->dispose = st_scroll_view_dispose;
+
+ actor_class->paint = st_scroll_view_paint;
+ actor_class->pick = st_scroll_view_pick;
+ actor_class->get_paint_volume = st_scroll_view_get_paint_volume;
+ actor_class->get_preferred_width = st_scroll_view_get_preferred_width;
+ actor_class->get_preferred_height = st_scroll_view_get_preferred_height;
+ actor_class->allocate = st_scroll_view_allocate;
+ actor_class->scroll_event = st_scroll_view_scroll_event;
+
+ widget_class->style_changed = st_scroll_view_style_changed;
+
+ /**
+ * StScrollView:hscroll:
+ *
+ * The horizontal #StScrollBar for the #StScrollView.
+ */
+ props[PROP_HSCROLL] =
+ g_param_spec_object ("hscroll",
+ "StScrollBar",
+ "Horizontal scroll indicator",
+ ST_TYPE_SCROLL_BAR,
+ ST_PARAM_READABLE);
+
+ /**
+ * StScrollView:vscroll:
+ *
+ * The vertical #StScrollBar for the #StScrollView.
+ */
+ props[PROP_VSCROLL] =
+ g_param_spec_object ("vscroll",
+ "StScrollBar",
+ "Vertical scroll indicator",
+ ST_TYPE_SCROLL_BAR,
+ ST_PARAM_READABLE);
+
+ /**
+ * StScrollView:vscrollbar-policy:
+ *
+ * The #StPolicyType for when to show the vertical #StScrollBar.
+ */
+ props[PROP_VSCROLLBAR_POLICY] =
+ g_param_spec_enum ("vscrollbar-policy",
+ "Vertical Scrollbar Policy",
+ "When the vertical scrollbar is displayed",
+ ST_TYPE_POLICY_TYPE,
+ ST_POLICY_AUTOMATIC,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StScrollView:hscrollbar-policy:
+ *
+ * The #StPolicyType for when to show the horizontal #StScrollBar.
+ */
+ props[PROP_HSCROLLBAR_POLICY] =
+ g_param_spec_enum ("hscrollbar-policy",
+ "Horizontal Scrollbar Policy",
+ "When the horizontal scrollbar is displayed",
+ ST_TYPE_POLICY_TYPE,
+ ST_POLICY_AUTOMATIC,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StScrollView:hscrollbar-visible:
+ *
+ * Whether the horizontal #StScrollBar is visible.
+ */
+ props[PROP_HSCROLLBAR_VISIBLE] =
+ g_param_spec_boolean ("hscrollbar-visible",
+ "Horizontal Scrollbar Visibility",
+ "Whether the horizontal scrollbar is visible",
+ TRUE,
+ ST_PARAM_READABLE);
+
+ /**
+ * StScrollView:vscrollbar-visible:
+ *
+ * Whether the vertical #StScrollBar is visible.
+ */
+ props[PROP_VSCROLLBAR_VISIBLE] =
+ g_param_spec_boolean ("vscrollbar-visible",
+ "Vertical Scrollbar Visibility",
+ "Whether the vertical scrollbar is visible",
+ TRUE,
+ ST_PARAM_READABLE);
+
+ /**
+ * StScrollView:enable-mouse-scrolling:
+ *
+ * Whether to enable automatic mouse wheel scrolling.
+ */
+ props[PROP_MOUSE_SCROLL] =
+ g_param_spec_boolean ("enable-mouse-scrolling",
+ "Enable Mouse Scrolling",
+ "Enable automatic mouse wheel scrolling",
+ TRUE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StScrollView:overlay-scrollbars:
+ *
+ * Whether scrollbars are painted on top of the content.
+ */
+ props[PROP_OVERLAY_SCROLLBARS] =
+ g_param_spec_boolean ("overlay-scrollbars",
+ "Use Overlay Scrollbars",
+ "Overlay scrollbars over the content",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+st_scroll_view_init (StScrollView *self)
+{
+ StScrollViewPrivate *priv = self->priv = st_scroll_view_get_instance_private (self);
+
+ priv->hscrollbar_policy = ST_POLICY_AUTOMATIC;
+ priv->vscrollbar_policy = ST_POLICY_AUTOMATIC;
+
+ priv->hadjustment = g_object_new (ST_TYPE_ADJUSTMENT,
+ "actor", self,
+ NULL);
+ priv->hscroll = g_object_new (ST_TYPE_SCROLL_BAR,
+ "adjustment", priv->hadjustment,
+ "vertical", FALSE,
+ NULL);
+
+ priv->vadjustment = g_object_new (ST_TYPE_ADJUSTMENT,
+ "actor", self,
+ NULL);
+ priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR,
+ "adjustment", priv->vadjustment,
+ "vertical", TRUE,
+ NULL);
+
+ clutter_actor_add_child (CLUTTER_ACTOR (self), priv->hscroll);
+ clutter_actor_add_child (CLUTTER_ACTOR (self), priv->vscroll);
+
+ /* mouse scroll is enabled by default, so we also need to be reactive */
+ priv->mouse_scroll = TRUE;
+ g_object_set (G_OBJECT (self), "reactive", TRUE, NULL);
+}
+
+static void
+st_scroll_view_add (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ StScrollView *self = ST_SCROLL_VIEW (container);
+ StScrollViewPrivate *priv = self->priv;
+
+ if (ST_IS_SCROLLABLE (actor))
+ {
+ priv->child = actor;
+
+ /* chain up to StBin::add() */
+ st_scroll_view_parent_iface->add (container, actor);
+
+ st_scrollable_set_adjustments (ST_SCROLLABLE (actor),
+ priv->hadjustment, priv->vadjustment);
+ }
+ else
+ {
+ g_warning ("Attempting to add an actor of type %s to "
+ "a StScrollView, but the actor does "
+ "not implement StScrollable.",
+ g_type_name (G_OBJECT_TYPE (actor)));
+ }
+}
+
+static void
+st_scroll_view_remove (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ StScrollView *self = ST_SCROLL_VIEW (container);
+ StScrollViewPrivate *priv = self->priv;
+
+ if (actor == priv->child)
+ {
+ g_object_ref (priv->child);
+
+ /* chain up to StBin::remove() */
+ st_scroll_view_parent_iface->remove (container, actor);
+
+ st_scrollable_set_adjustments (ST_SCROLLABLE (priv->child),
+ NULL, NULL);
+
+ g_object_unref (priv->child);
+ priv->child = NULL;
+ }
+ else
+ {
+ if (actor == priv->vscroll)
+ priv->vscroll = NULL;
+ else if (actor == priv->hscroll)
+ priv->hscroll = NULL;
+ else
+ g_assert ("Unknown child removed from StScrollView");
+
+ clutter_actor_remove_child (CLUTTER_ACTOR (container), actor);
+ }
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ /* store a pointer to the StBin implementation of
+ * ClutterContainer so that we can chain up when
+ * overriding the methods
+ */
+ st_scroll_view_parent_iface = g_type_interface_peek_parent (iface);
+
+ iface->add = st_scroll_view_add;
+ iface->remove = st_scroll_view_remove;
+}
+
+/**
+ * st_scroll_view_new:
+ *
+ * Create a new #StScrollView.
+ *
+ * Returns: (transfer full): a new #StScrollView
+ */
+StWidget *
+st_scroll_view_new (void)
+{
+ return g_object_new (ST_TYPE_SCROLL_VIEW, NULL);
+}
+
+/**
+ * st_scroll_view_get_hscroll_bar:
+ * @scroll: a #StScrollView
+ *
+ * Gets the horizontal #StScrollBar of the #StScrollView.
+ *
+ * Returns: (transfer none): the horizontal scrollbar
+ */
+ClutterActor *
+st_scroll_view_get_hscroll_bar (StScrollView *scroll)
+{
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
+
+ return scroll->priv->hscroll;
+}
+
+/**
+ * st_scroll_view_get_vscroll_bar:
+ * @scroll: a #StScrollView
+ *
+ * Gets the vertical scrollbar of the #StScrollView.
+ *
+ * Returns: (transfer none): the vertical #StScrollBar
+ */
+ClutterActor *
+st_scroll_view_get_vscroll_bar (StScrollView *scroll)
+{
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
+
+ return scroll->priv->vscroll;
+}
+
+/**
+ * st_scroll_view_get_column_size:
+ * @scroll: a #StScrollView
+ *
+ * Get the step increment of the horizontal plane.
+ *
+ * Returns: the horizontal step increment
+ */
+gfloat
+st_scroll_view_get_column_size (StScrollView *scroll)
+{
+ gdouble column_size;
+
+ g_return_val_if_fail (scroll, 0);
+
+ g_object_get (scroll->priv->hadjustment,
+ "step-increment", &column_size,
+ NULL);
+
+ return column_size;
+}
+
+/**
+ * st_scroll_view_set_column_size:
+ * @scroll: a #StScrollView
+ * @column_size: horizontal step increment
+ *
+ * Set the step increment of the horizontal plane to @column_size.
+ */
+void
+st_scroll_view_set_column_size (StScrollView *scroll,
+ gfloat column_size)
+{
+ g_return_if_fail (scroll);
+
+ if (column_size < 0)
+ {
+ scroll->priv->column_size_set = FALSE;
+ scroll->priv->column_size = -1;
+ }
+ else
+ {
+ scroll->priv->column_size_set = TRUE;
+ scroll->priv->column_size = column_size;
+
+ g_object_set (scroll->priv->hadjustment,
+ "step-increment", (gdouble) scroll->priv->column_size,
+ NULL);
+ }
+}
+
+/**
+ * st_scroll_view_get_row_size:
+ * @scroll: a #StScrollView
+ *
+ * Get the step increment of the vertical plane.
+ *
+ * Returns: the vertical step increment
+ */
+gfloat
+st_scroll_view_get_row_size (StScrollView *scroll)
+{
+ gdouble row_size;
+
+ g_return_val_if_fail (scroll, 0);
+
+ g_object_get (scroll->priv->vadjustment,
+ "step-increment", &row_size,
+ NULL);
+
+ return row_size;
+}
+
+/**
+ * st_scroll_view_set_row_size:
+ * @scroll: a #StScrollView
+ * @row_size: vertical step increment
+ *
+ * Set the step increment of the vertical plane to @row_size.
+ */
+void
+st_scroll_view_set_row_size (StScrollView *scroll,
+ gfloat row_size)
+{
+ g_return_if_fail (scroll);
+
+ if (row_size < 0)
+ {
+ scroll->priv->row_size_set = FALSE;
+ scroll->priv->row_size = -1;
+ }
+ else
+ {
+ scroll->priv->row_size_set = TRUE;
+ scroll->priv->row_size = row_size;
+
+ g_object_set (scroll->priv->vadjustment,
+ "step-increment", (gdouble) scroll->priv->row_size,
+ NULL);
+ }
+}
+
+/**
+ * st_scroll_view_set_mouse_scrolling:
+ * @scroll: a #StScrollView
+ * @enabled: %TRUE or %FALSE
+ *
+ * Sets automatic mouse wheel scrolling to enabled or disabled.
+ */
+void
+st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
+ gboolean enabled)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ if (priv->mouse_scroll != enabled)
+ {
+ priv->mouse_scroll = enabled;
+
+ /* make sure we can receive mouse wheel events */
+ if (enabled)
+ clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE);
+
+ g_object_notify_by_pspec (G_OBJECT (scroll), props[PROP_MOUSE_SCROLL]);
+ }
+}
+
+/**
+ * st_scroll_view_get_mouse_scrolling:
+ * @scroll: a #StScrollView
+ *
+ * Get whether automatic mouse wheel scrolling is enabled or disabled.
+ *
+ * Returns: %TRUE if enabled, %FALSE otherwise
+ */
+gboolean
+st_scroll_view_get_mouse_scrolling (StScrollView *scroll)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ return priv->mouse_scroll;
+}
+
+/**
+ * st_scroll_view_set_overlay_scrollbars:
+ * @scroll: A #StScrollView
+ * @enabled: Whether to enable overlay scrollbars
+ *
+ * Sets whether scrollbars are painted on top of the content.
+ */
+void
+st_scroll_view_set_overlay_scrollbars (StScrollView *scroll,
+ gboolean enabled)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ if (priv->overlay_scrollbars != enabled)
+ {
+ priv->overlay_scrollbars = enabled;
+ g_object_notify_by_pspec (G_OBJECT (scroll),
+ props[PROP_OVERLAY_SCROLLBARS]);
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll));
+ }
+}
+
+/**
+ * st_scroll_view_get_overlay_scrollbars:
+ * @scroll: A #StScrollView
+ *
+ * Gets whether scrollbars are painted on top of the content.
+ *
+ * Returns: %TRUE if enabled, %FALSE otherwise
+ */
+gboolean
+st_scroll_view_get_overlay_scrollbars (StScrollView *scroll)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ return priv->overlay_scrollbars;
+}
+
+/**
+ * st_scroll_view_set_policy:
+ * @scroll: A #StScrollView
+ * @hscroll: Whether to enable horizontal scrolling
+ * @vscroll: Whether to enable vertical scrolling
+ *
+ * Set the scroll policy.
+ */
+void
+st_scroll_view_set_policy (StScrollView *scroll,
+ StPolicyType hscroll,
+ StPolicyType vscroll)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ if (priv->hscrollbar_policy == hscroll && priv->vscrollbar_policy == vscroll)
+ return;
+
+ g_object_freeze_notify ((GObject *) scroll);
+
+ if (priv->hscrollbar_policy != hscroll)
+ {
+ priv->hscrollbar_policy = hscroll;
+ g_object_notify_by_pspec ((GObject *) scroll,
+ props[PROP_HSCROLLBAR_POLICY]);
+ }
+
+ if (priv->vscrollbar_policy != vscroll)
+ {
+ priv->vscrollbar_policy = vscroll;
+ g_object_notify_by_pspec ((GObject *) scroll,
+ props[PROP_VSCROLLBAR_POLICY]);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll));
+
+ g_object_thaw_notify ((GObject *) scroll);
+}
diff --git a/src/st/st-scroll-view.h b/src/st/st-scroll-view.h
new file mode 100644
index 0000000..e2acaca
--- /dev/null
+++ b/src/st/st-scroll-view.h
@@ -0,0 +1,90 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2010 Red Hat, Inc.
+ * Copyright 2010 Maxim Ermilov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_SCROLL_VIEW_H__
+#define __ST_SCROLL_VIEW_H__
+
+#include <st/st-bin.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SCROLL_VIEW (st_scroll_view_get_type())
+G_DECLARE_FINAL_TYPE (StScrollView, st_scroll_view, ST, SCROLL_VIEW, StBin)
+
+typedef enum
+{
+ ST_POLICY_ALWAYS,
+ ST_POLICY_AUTOMATIC,
+ ST_POLICY_NEVER,
+ ST_POLICY_EXTERNAL,
+} StPolicyType;
+
+typedef struct _StScrollViewPrivate StScrollViewPrivate;
+
+/**
+ * StScrollView:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _StScrollView
+{
+ /*< private >*/
+ StBin parent_instance;
+
+ StScrollViewPrivate *priv;
+};
+
+StWidget *st_scroll_view_new (void);
+
+ClutterActor *st_scroll_view_get_hscroll_bar (StScrollView *scroll);
+ClutterActor *st_scroll_view_get_vscroll_bar (StScrollView *scroll);
+
+gfloat st_scroll_view_get_column_size (StScrollView *scroll);
+void st_scroll_view_set_column_size (StScrollView *scroll,
+ gfloat column_size);
+
+gfloat st_scroll_view_get_row_size (StScrollView *scroll);
+void st_scroll_view_set_row_size (StScrollView *scroll,
+ gfloat row_size);
+
+void st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
+ gboolean enabled);
+gboolean st_scroll_view_get_mouse_scrolling (StScrollView *scroll);
+
+void st_scroll_view_set_overlay_scrollbars (StScrollView *scroll,
+ gboolean enabled);
+gboolean st_scroll_view_get_overlay_scrollbars (StScrollView *scroll);
+
+void st_scroll_view_set_policy (StScrollView *scroll,
+ StPolicyType hscroll,
+ StPolicyType vscroll);
+void st_scroll_view_update_fade_effect (StScrollView *scroll,
+ ClutterMargin *fade_margins);
+
+G_END_DECLS
+
+#endif /* __ST_SCROLL_VIEW_H__ */
diff --git a/src/st/st-scrollable.c b/src/st/st-scrollable.c
new file mode 100644
index 0000000..3a77052
--- /dev/null
+++ b/src/st/st-scrollable.c
@@ -0,0 +1,196 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scrollable.c: Scrollable interface
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "st-private.h"
+#include "st-scrollable.h"
+
+G_DEFINE_INTERFACE (StScrollable, st_scrollable, G_TYPE_OBJECT)
+
+/**
+ * SECTION:st-scrollable
+ * @short_description: A #ClutterActor that can be scrolled
+ *
+ * The #StScrollable interface is exposed by actors that support scrolling.
+ *
+ * The interface contains methods for getting and setting the adjustments
+ * for scrolling; these adjustments will be used to hook the scrolled
+ * position up to scrollbars or other external controls. When a #StScrollable
+ * is added to a parent container, the parent container is responsible
+ * for setting the adjustments. The parent container then sets the adjustments
+ * back to %NULL when the scrollable is removed.
+ *
+ * For #StScrollable supporting height-for-width size negotiation, size
+ * negotiation works as follows:
+ *
+ * In response to get_preferred_width(), the scrollable should report
+ * the minimum width at which horizontal scrolling is needed for the
+ * preferred width, and natural width of the actor when not
+ * horizontally scrolled as the natural width.
+ *
+ * The for_width passed into get_preferred_height() is the width at which
+ * the scrollable will be allocated; this will be smaller than the minimum
+ * width when scrolling horizontally, so the scrollable may want to adjust
+ * it up to the minimum width before computing a preferred height. (Other
+ * scrollables may want to fit as much content into the allocated area
+ * as possible and only scroll what absolutely needs to scroll - consider,
+ * for example, the line-wrapping behavior of a text editor where there
+ * is a long line without any spaces.) As for width, get_preferred_height()
+ * should return the minimum size at which no scrolling is needed for the
+ * minimum height, and the natural size of the actor when not vertically scrolled
+ * as the natural height.
+ *
+ * In allocate() the allocation box passed in will be actual allocated
+ * size of the actor so will be smaller than the reported minimum
+ * width and/or height when scrolling is present. Any scrollable actor
+ * must support being allocated at any size down to 0x0 without
+ * crashing, however if the actor has content around the scrolled area
+ * and has an absolute minimum size that's bigger than 0x0 its
+ * acceptable for it to misdraw between 0x0 and the absolute minimum
+ * size. It's up to the application author to avoid letting the user
+ * resize the scroll view small enough so that the scrolled area
+ * vanishes.
+ *
+ * In response to allocate, in addition to normal handling, the
+ * scrollable should also set the limits of the the horizontal and
+ * vertical adjustments that were set on it earlier. The standard
+ * settings are:
+ *
+ * lower: 0
+ * page_size: allocated size (width or height)
+ * upper: MAX (total size of the scrolled area,allocated_size)
+ * step_increment: natural row/column height or a fixed fraction of the page size
+ * page_increment: page_size - step_increment
+ */
+static void
+st_scrollable_default_init (StScrollableInterface *g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ /**
+ * StScrollable:hadjustment:
+ *
+ * The horizontal #StAdjustment used by the #StScrollable.
+ *
+ * Implementations should override this property to provide read-write
+ * access to the #StAdjustment.
+ *
+ * JavaScript code may override this as demonstrated below:
+ *
+ * |[<!-- language="JavaScript" -->
+ * var MyScrollable = GObject.registerClass({
+ * Properties: {
+ * 'hadjustment': GObject.ParamSpec.override(
+ * 'hadjustment',
+ * St.Scrollable
+ * )
+ * }
+ * }, class MyScrollable extends St.Scrollable {
+ *
+ * get hadjustment() {
+ * return this._hadjustment || null;
+ * }
+ *
+ * set hadjustment(adjustment) {
+ * if (this.hadjustment === adjustment)
+ * return;
+ *
+ * this._hadjustment = adjustment;
+ * this.notify('hadjustment');
+ * }
+ * });
+ * ]|
+ */
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("hadjustment",
+ "StAdjustment",
+ "Horizontal adjustment",
+ ST_TYPE_ADJUSTMENT,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY));
+
+ /**
+ * StScrollable:vadjustment:
+ *
+ * The vertical #StAdjustment used by the #StScrollable.
+ *
+ * Implementations should override this property to provide read-write
+ * access to the #StAdjustment.
+ *
+ * See #StScrollable:hadjustment for an example of how to override this
+ * property in JavaScript code.
+ */
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("vadjustment",
+ "StAdjustment",
+ "Vertical adjustment",
+ ST_TYPE_ADJUSTMENT,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY));
+
+ initialized = TRUE;
+ }
+}
+
+/**
+ * st_scrollable_set_adjustments:
+ * @scrollable: a #StScrollable
+ * @hadjustment: the horizontal #StAdjustment
+ * @vadjustment: the vertical #StAdjustment
+ *
+ * This method should be implemented by classes implementing the #StScrollable
+ * interface.
+ *
+ * JavaScript code should do this by overriding the `vfunc_set_adjustments()`
+ * method.
+ */
+void
+st_scrollable_set_adjustments (StScrollable *scrollable,
+ StAdjustment *hadjustment,
+ StAdjustment *vadjustment)
+{
+ ST_SCROLLABLE_GET_IFACE (scrollable)->set_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
+
+/**
+ * st_scroll_bar_get_adjustments:
+ * @hadjustment: (transfer none) (out) (optional): location to store the horizontal adjustment, or %NULL
+ * @vadjustment: (transfer none) (out) (optional): location to store the vertical adjustment, or %NULL
+ *
+ * Gets the adjustment objects that store the offsets of the scrollable widget
+ * into its possible scrolling area.
+ *
+ * This method should be implemented by classes implementing the #StScrollable
+ * interface.
+ *
+ * JavaScript code should do this by overriding the `vfunc_get_adjustments()`
+ * method.
+ */
+void
+st_scrollable_get_adjustments (StScrollable *scrollable,
+ StAdjustment **hadjustment,
+ StAdjustment **vadjustment)
+{
+ ST_SCROLLABLE_GET_IFACE (scrollable)->get_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
diff --git a/src/st/st-scrollable.h b/src/st/st-scrollable.h
new file mode 100644
index 0000000..797ec7d
--- /dev/null
+++ b/src/st/st-scrollable.h
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scrollable.h: Scrollable interface
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_SCROLLABLE_H__
+#define __ST_SCROLLABLE_H__
+
+#include <glib-object.h>
+#include <st/st-adjustment.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SCROLLABLE (st_scrollable_get_type ())
+G_DECLARE_INTERFACE (StScrollable, st_scrollable, ST, SCROLLABLE, GObject)
+
+typedef struct _StScrollableInterface StScrollableInterface;
+
+struct _StScrollableInterface
+{
+ GTypeInterface parent;
+
+ void (* set_adjustments) (StScrollable *scrollable,
+ StAdjustment *hadjustment,
+ StAdjustment *vadjustment);
+ void (* get_adjustments) (StScrollable *scrollable,
+ StAdjustment **hadjustment,
+ StAdjustment **vadjustment);
+};
+
+void st_scrollable_set_adjustments (StScrollable *scrollable,
+ StAdjustment *hadjustment,
+ StAdjustment *vadjustment);
+void st_scrollable_get_adjustments (StScrollable *scrollable,
+ StAdjustment **hadjustment,
+ StAdjustment **vadjustment);
+
+G_END_DECLS
+
+#endif /* __ST_SCROLLABLE_H__ */
diff --git a/src/st/st-settings.c b/src/st/st-settings.c
new file mode 100644
index 0000000..04bf68f
--- /dev/null
+++ b/src/st/st-settings.c
@@ -0,0 +1,451 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-settings.c: Global settings
+ *
+ * Copyright 2019 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include <gio/gio.h>
+
+#include "st-private.h"
+#include "st-settings.h"
+
+#define KEY_ENABLE_ANIMATIONS "enable-animations"
+#define KEY_PRIMARY_PASTE "gtk-enable-primary-paste"
+#define KEY_DRAG_THRESHOLD "drag-threshold"
+#define KEY_FONT_NAME "font-name"
+#define KEY_HIGH_CONTRAST "high-contrast"
+#define KEY_GTK_ICON_THEME "icon-theme"
+#define KEY_MAGNIFIER_ACTIVE "screen-magnifier-enabled"
+#define KEY_DISABLE_SHOW_PASSWORD "disable-show-password"
+
+enum {
+ PROP_0,
+ PROP_ENABLE_ANIMATIONS,
+ PROP_PRIMARY_PASTE,
+ PROP_DRAG_THRESHOLD,
+ PROP_FONT_NAME,
+ PROP_HIGH_CONTRAST,
+ PROP_GTK_ICON_THEME,
+ PROP_MAGNIFIER_ACTIVE,
+ PROP_SLOW_DOWN_FACTOR,
+ PROP_DISABLE_SHOW_PASSWORD,
+ N_PROPS
+};
+
+GParamSpec *props[N_PROPS] = { 0 };
+
+struct _StSettings
+{
+ GObject parent_object;
+ GSettings *interface_settings;
+ GSettings *mouse_settings;
+ GSettings *a11y_applications_settings;
+ GSettings *a11y_interface_settings;
+ GSettings *lockdown_settings;
+
+ gchar *font_name;
+ gboolean high_contrast;
+ gchar *gtk_icon_theme;
+ int inhibit_animations_count;
+ gboolean enable_animations;
+ gboolean primary_paste;
+ gboolean magnifier_active;
+ gboolean disable_show_password;
+ gint drag_threshold;
+ double slow_down_factor;
+};
+
+G_DEFINE_TYPE (StSettings, st_settings, G_TYPE_OBJECT)
+
+#define EPSILON (1e-10)
+
+static void
+st_settings_set_slow_down_factor (StSettings *settings,
+ double factor)
+{
+ if (fabs (settings->slow_down_factor - factor) < EPSILON)
+ return;
+
+ settings->slow_down_factor = factor;
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_SLOW_DOWN_FACTOR]);
+}
+
+static gboolean
+get_enable_animations (StSettings *settings)
+{
+ if (settings->inhibit_animations_count > 0)
+ return FALSE;
+ else
+ return settings->enable_animations;
+}
+
+void
+st_settings_inhibit_animations (StSettings *settings)
+{
+ gboolean enable_animations;
+
+ enable_animations = get_enable_animations (settings);
+ settings->inhibit_animations_count++;
+
+ if (enable_animations != get_enable_animations (settings))
+ g_object_notify_by_pspec (G_OBJECT (settings),
+ props[PROP_ENABLE_ANIMATIONS]);
+}
+
+void
+st_settings_uninhibit_animations (StSettings *settings)
+{
+ gboolean enable_animations;
+
+ enable_animations = get_enable_animations (settings);
+ settings->inhibit_animations_count--;
+
+ if (enable_animations != get_enable_animations (settings))
+ g_object_notify_by_pspec (G_OBJECT (settings),
+ props[PROP_ENABLE_ANIMATIONS]);
+}
+
+static void
+st_settings_finalize (GObject *object)
+{
+ StSettings *settings = ST_SETTINGS (object);
+
+ g_object_unref (settings->interface_settings);
+ g_object_unref (settings->mouse_settings);
+ g_object_unref (settings->a11y_applications_settings);
+ g_object_unref (settings->a11y_interface_settings);
+ g_object_unref (settings->lockdown_settings);
+ g_free (settings->font_name);
+ g_free (settings->gtk_icon_theme);
+
+ G_OBJECT_CLASS (st_settings_parent_class)->finalize (object);
+}
+
+static void
+st_settings_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StSettings *settings = ST_SETTINGS (object);
+
+ switch (prop_id)
+ {
+ case PROP_SLOW_DOWN_FACTOR:
+ st_settings_set_slow_down_factor (settings, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+st_settings_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StSettings *settings = ST_SETTINGS (object);
+
+ switch (prop_id)
+ {
+ case PROP_ENABLE_ANIMATIONS:
+ g_value_set_boolean (value, get_enable_animations (settings));
+ break;
+ case PROP_PRIMARY_PASTE:
+ g_value_set_boolean (value, settings->primary_paste);
+ break;
+ case PROP_DRAG_THRESHOLD:
+ g_value_set_int (value, settings->drag_threshold);
+ break;
+ case PROP_FONT_NAME:
+ g_value_set_string (value, settings->font_name);
+ break;
+ case PROP_HIGH_CONTRAST:
+ g_value_set_boolean (value, settings->high_contrast);
+ break;
+ case PROP_GTK_ICON_THEME:
+ g_value_set_string (value, settings->gtk_icon_theme);
+ break;
+ case PROP_MAGNIFIER_ACTIVE:
+ g_value_set_boolean (value, settings->magnifier_active);
+ break;
+ case PROP_SLOW_DOWN_FACTOR:
+ g_value_set_double (value, settings->slow_down_factor);
+ break;
+ case PROP_DISABLE_SHOW_PASSWORD:
+ g_value_set_boolean (value, settings->disable_show_password);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+st_settings_class_init (StSettingsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = st_settings_finalize;
+ object_class->set_property = st_settings_set_property;
+ object_class->get_property = st_settings_get_property;
+
+ /**
+ * StSettings:enable-animations:
+ *
+ * Whether animations are enabled.
+ */
+ props[PROP_ENABLE_ANIMATIONS] = g_param_spec_boolean ("enable-animations",
+ "Enable animations",
+ "Enable animations",
+ TRUE,
+ ST_PARAM_READABLE);
+
+ /**
+ * StSettings:primary-paste:
+ *
+ * Whether pasting from the `PRIMARY` selection is supported (eg. middle-click
+ * paste).
+ */
+ props[PROP_PRIMARY_PASTE] = g_param_spec_boolean ("primary-paste",
+ "Primary paste",
+ "Primary paste",
+ TRUE,
+ ST_PARAM_READABLE);
+
+ /**
+ * StSettings:drag-threshold:
+ *
+ * The threshold before a drag operation begins.
+ */
+ props[PROP_DRAG_THRESHOLD] = g_param_spec_int ("drag-threshold",
+ "Drag threshold",
+ "Drag threshold",
+ 0, G_MAXINT, 8,
+ ST_PARAM_READABLE);
+
+ /**
+ * StSettings:font-name:
+ *
+ * The current font name.
+ */
+ props[PROP_FONT_NAME] = g_param_spec_string ("font-name",
+ "font name",
+ "font name",
+ "",
+ ST_PARAM_READABLE);
+
+ /**
+ * StSettings:high-contrast:
+ *
+ * Whether the accessibility high contrast mode is enabled.
+ */
+ props[PROP_HIGH_CONTRAST] = g_param_spec_boolean ("high-contrast",
+ "High contrast",
+ "High contrast",
+ FALSE,
+ ST_PARAM_READABLE);
+
+ /**
+ * StSettings:gtk-icon-theme:
+ *
+ * The current GTK icon theme
+ */
+ props[PROP_GTK_ICON_THEME] = g_param_spec_string ("gtk-icon-theme",
+ "GTK Icon Theme",
+ "GTK Icon Theme",
+ "",
+ ST_PARAM_READABLE);
+
+ /**
+ * StSettings:magnifier-active:
+ *
+ * Whether the accessibility magnifier is active.
+ */
+ props[PROP_MAGNIFIER_ACTIVE] = g_param_spec_boolean("magnifier-active",
+ "Magnifier is active",
+ "Whether the a11y magnifier is active",
+ FALSE,
+ ST_PARAM_READABLE);
+
+ /**
+ * StSettings:slow-down-factor:
+ *
+ * The slow-down factor applied to all animation durations.
+ */
+ props[PROP_SLOW_DOWN_FACTOR] = g_param_spec_double("slow-down-factor",
+ "Slow down factor",
+ "Factor applied to all animation durations",
+ EPSILON, G_MAXDOUBLE, 1.0,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StSettings:disable-show-password:
+ *
+ * Whether password showing can be locked down
+ */
+ props[PROP_DISABLE_SHOW_PASSWORD] = g_param_spec_boolean("disable-show-password",
+ "'Show Password' is disabled",
+ "Whether user can request to see their password",
+ FALSE,
+ ST_PARAM_READABLE);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+on_interface_settings_changed (GSettings *g_settings,
+ const gchar *key,
+ StSettings *settings)
+{
+ if (g_str_equal (key, KEY_ENABLE_ANIMATIONS))
+ {
+ settings->enable_animations = g_settings_get_boolean (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_ENABLE_ANIMATIONS]);
+ }
+ else if (g_str_equal (key, KEY_PRIMARY_PASTE))
+ {
+ settings->primary_paste = g_settings_get_boolean (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_PRIMARY_PASTE]);
+ }
+ else if (g_str_equal (key, KEY_FONT_NAME))
+ {
+ g_free (settings->font_name);
+ settings->font_name = g_settings_get_string (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_FONT_NAME]);
+ }
+ else if (g_str_equal (key, KEY_GTK_ICON_THEME))
+ {
+ g_free (settings->gtk_icon_theme);
+ settings->gtk_icon_theme = g_settings_get_string (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings),
+ props[PROP_GTK_ICON_THEME]);
+ }
+}
+
+static void
+on_mouse_settings_changed (GSettings *g_settings,
+ const gchar *key,
+ StSettings *settings)
+{
+ if (g_str_equal (key, KEY_DRAG_THRESHOLD))
+ {
+ settings->drag_threshold = g_settings_get_int (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_DRAG_THRESHOLD]);
+ }
+}
+
+static void
+on_a11y_applications_settings_changed (GSettings *g_settings,
+ const gchar *key,
+ StSettings *settings)
+{
+ if (g_str_equal (key, KEY_MAGNIFIER_ACTIVE))
+ {
+ settings->magnifier_active = g_settings_get_boolean (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_MAGNIFIER_ACTIVE]);
+ }
+}
+
+static void
+on_a11y_interface_settings_changed (GSettings *g_settings,
+ const gchar *key,
+ StSettings *settings)
+{
+ if (g_str_equal (key, KEY_HIGH_CONTRAST))
+ {
+ settings->high_contrast = g_settings_get_boolean (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_HIGH_CONTRAST]);
+
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_GTK_ICON_THEME]);
+ }
+}
+
+static void
+on_lockdown_settings_changed (GSettings *g_settings,
+ const gchar *key,
+ StSettings *settings)
+{
+ if (g_str_equal (key, KEY_DISABLE_SHOW_PASSWORD))
+ {
+ settings->disable_show_password = g_settings_get_boolean (g_settings, key);
+ g_object_notify_by_pspec (G_OBJECT (settings), props[PROP_DISABLE_SHOW_PASSWORD]);
+ }
+}
+
+static void
+st_settings_init (StSettings *settings)
+{
+ settings->interface_settings = g_settings_new ("org.gnome.desktop.interface");
+ g_signal_connect (settings->interface_settings, "changed",
+ G_CALLBACK (on_interface_settings_changed), settings);
+
+ settings->mouse_settings = g_settings_new ("org.gnome.desktop.peripherals.mouse");
+ g_signal_connect (settings->mouse_settings, "changed",
+ G_CALLBACK (on_mouse_settings_changed), settings);
+
+ settings->a11y_applications_settings = g_settings_new ("org.gnome.desktop.a11y.applications");
+ g_signal_connect (settings->a11y_applications_settings, "changed",
+ G_CALLBACK (on_a11y_applications_settings_changed), settings);
+
+ settings->a11y_interface_settings = g_settings_new ("org.gnome.desktop.a11y.interface");
+ g_signal_connect (settings->a11y_interface_settings, "changed",
+ G_CALLBACK (on_a11y_interface_settings_changed), settings);
+
+ settings->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown");
+ g_signal_connect (settings->lockdown_settings, "changed",
+ G_CALLBACK (on_lockdown_settings_changed), settings);
+
+ settings->enable_animations = g_settings_get_boolean (settings->interface_settings,
+ KEY_ENABLE_ANIMATIONS);
+ settings->primary_paste = g_settings_get_boolean (settings->interface_settings,
+ KEY_PRIMARY_PASTE);
+ settings->font_name = g_settings_get_string (settings->interface_settings,
+ KEY_FONT_NAME);
+ settings->gtk_icon_theme = g_settings_get_string (settings->interface_settings,
+ KEY_GTK_ICON_THEME);
+ settings->drag_threshold = g_settings_get_int (settings->mouse_settings,
+ KEY_DRAG_THRESHOLD);
+ settings->magnifier_active = g_settings_get_boolean (settings->a11y_applications_settings,
+ KEY_MAGNIFIER_ACTIVE);
+ settings->high_contrast = g_settings_get_boolean (settings->a11y_interface_settings,
+ KEY_HIGH_CONTRAST);
+ settings->slow_down_factor = 1.;
+ settings->disable_show_password = g_settings_get_boolean (settings->lockdown_settings, KEY_DISABLE_SHOW_PASSWORD);
+}
+
+/**
+ * st_settings_get:
+ *
+ * Gets the global #StSettings object.
+ *
+ * Returns: (transfer none): the global #StSettings object
+ **/
+StSettings *
+st_settings_get (void)
+{
+ static StSettings *settings = NULL;
+
+ if (!settings)
+ settings = g_object_new (ST_TYPE_SETTINGS, NULL);
+
+ return settings;
+}
diff --git a/src/st/st-settings.h b/src/st/st-settings.h
new file mode 100644
index 0000000..8b25494
--- /dev/null
+++ b/src/st/st-settings.h
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-settings.h: Global settings
+ *
+ * Copyright 2019 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_SETTINGS_H__
+#define __ST_SETTINGS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SETTINGS (st_settings_get_type ())
+G_DECLARE_FINAL_TYPE (StSettings, st_settings, ST, SETTINGS, GObject)
+
+StSettings * st_settings_get (void);
+
+void st_settings_inhibit_animations (StSettings *settings);
+
+void st_settings_uninhibit_animations (StSettings *settings);
+
+G_END_DECLS
+
+#endif /* __ST_SETTINGS_H__ */
diff --git a/src/st/st-shadow.c b/src/st/st-shadow.c
new file mode 100644
index 0000000..0a8e319
--- /dev/null
+++ b/src/st/st-shadow.c
@@ -0,0 +1,307 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-shadow.c: Boxed type holding for -st-shadow attributes
+ *
+ * Copyright 2009, 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "st-shadow.h"
+#include "st-private.h"
+
+G_DEFINE_BOXED_TYPE (StShadow, st_shadow, st_shadow_ref, st_shadow_unref)
+G_DEFINE_BOXED_TYPE (StShadowHelper, st_shadow_helper, st_shadow_helper_copy, st_shadow_helper_free)
+
+/**
+ * SECTION: st-shadow
+ * @short_description: Boxed type for -st-shadow attributes
+ *
+ * #StShadow is a boxed type for storing attributes of the -st-shadow
+ * property, modelled liberally after the CSS3 box-shadow property.
+ * See http://www.css3.info/preview/box-shadow/
+ *
+ */
+
+/**
+ * st_shadow_new:
+ * @color: shadow's color
+ * @xoffset: horizontal offset
+ * @yoffset: vertical offset
+ * @blur: blur radius
+ * @spread: spread radius
+ * @inset: whether the shadow should be inset
+ *
+ * Creates a new #StShadow
+ *
+ * Returns: the newly allocated shadow. Use st_shadow_free() when done
+ */
+StShadow *
+st_shadow_new (ClutterColor *color,
+ gdouble xoffset,
+ gdouble yoffset,
+ gdouble blur,
+ gdouble spread,
+ gboolean inset)
+{
+ StShadow *shadow;
+
+ shadow = g_new (StShadow, 1);
+
+ shadow->color = *color;
+ shadow->xoffset = xoffset;
+ shadow->yoffset = yoffset;
+ shadow->blur = blur;
+ shadow->spread = spread;
+ shadow->inset = inset;
+ shadow->ref_count = 1;
+
+ return shadow;
+}
+
+/**
+ * st_shadow_ref:
+ * @shadow: a #StShadow
+ *
+ * Atomically increments the reference count of @shadow by one.
+ *
+ * Returns: the passed in #StShadow.
+ */
+StShadow *
+st_shadow_ref (StShadow *shadow)
+{
+ g_return_val_if_fail (shadow != NULL, NULL);
+ g_return_val_if_fail (shadow->ref_count > 0, shadow);
+
+ g_atomic_int_inc (&shadow->ref_count);
+ return shadow;
+}
+
+/**
+ * st_shadow_unref:
+ * @shadow: a #StShadow
+ *
+ * Atomically decrements the reference count of @shadow by one.
+ * If the reference count drops to 0, all memory allocated by the
+ * #StShadow is released.
+ */
+void
+st_shadow_unref (StShadow *shadow)
+{
+ g_return_if_fail (shadow != NULL);
+ g_return_if_fail (shadow->ref_count > 0);
+
+ if (g_atomic_int_dec_and_test (&shadow->ref_count))
+ g_free (shadow);
+}
+
+/**
+ * st_shadow_equal:
+ * @shadow: a #StShadow
+ * @other: a different #StShadow
+ *
+ * Check if two shadow objects are identical. Note that two shadows may
+ * compare non-identically if they differ only by floating point rounding
+ * errors.
+ *
+ * Returns: %TRUE if the two shadows are identical
+ */
+gboolean
+st_shadow_equal (StShadow *shadow,
+ StShadow *other)
+{
+ g_return_val_if_fail (shadow != NULL, FALSE);
+ g_return_val_if_fail (other != NULL, FALSE);
+
+ if (shadow == other)
+ return TRUE;
+
+ /* We use strict equality to compare double quantities; this means
+ * that, for example, a shadow offset of 0.25in does not necessarily
+ * compare equal to a shadow offset of 18pt in this test. Assume
+ * that a few false negatives are mostly harmless.
+ */
+
+ return (clutter_color_equal (&shadow->color, &other->color) &&
+ shadow->xoffset == other->xoffset &&
+ shadow->yoffset == other->yoffset &&
+ shadow->blur == other->blur &&
+ shadow->spread == other->spread &&
+ shadow->inset == other->inset);
+}
+
+/**
+ * st_shadow_get_box:
+ * @shadow: a #StShadow
+ * @actor_box: the box allocated to a #ClutterAlctor
+ * @shadow_box: computed box occupied by @shadow
+ *
+ * Gets the box used to paint @shadow, which will be partly
+ * outside of @actor_box
+ */
+void
+st_shadow_get_box (StShadow *shadow,
+ const ClutterActorBox *actor_box,
+ ClutterActorBox *shadow_box)
+{
+ g_return_if_fail (shadow != NULL);
+ g_return_if_fail (actor_box != NULL);
+ g_return_if_fail (shadow_box != NULL);
+
+ /* Inset shadows are drawn below the border, so returning
+ * the original box is not actually correct; still, it's
+ * good enough for the purpose of determining additional space
+ * required outside the actor box.
+ */
+ if (shadow->inset)
+ {
+ *shadow_box = *actor_box;
+ return;
+ }
+
+ shadow_box->x1 = actor_box->x1 + shadow->xoffset
+ - shadow->blur - shadow->spread;
+ shadow_box->x2 = actor_box->x2 + shadow->xoffset
+ + shadow->blur + shadow->spread;
+ shadow_box->y1 = actor_box->y1 + shadow->yoffset
+ - shadow->blur - shadow->spread;
+ shadow_box->y2 = actor_box->y2 + shadow->yoffset
+ + shadow->blur + shadow->spread;
+}
+
+/**
+ * SECTION: st-shadow-helper
+ *
+ * An helper for implementing a drop shadow on a actor.
+ * The actor is expected to recreate the helper whenever its contents
+ * or size change. Then, it would call st_shadow_helper_paint() inside
+ * its paint() virtual function.
+ */
+
+struct _StShadowHelper {
+ StShadow *shadow;
+ CoglPipeline *pipeline;
+
+ gfloat width;
+ gfloat height;
+};
+
+/**
+ * st_shadow_helper_new:
+ * @shadow: a #StShadow representing the shadow properties
+ *
+ * Builds a #StShadowHelper that will build a drop shadow
+ * using @source as the mask.
+ *
+ * Returns: (transfer full): a new #StShadowHelper
+ */
+StShadowHelper *
+st_shadow_helper_new (StShadow *shadow)
+{
+ StShadowHelper *helper;
+
+ helper = g_new0 (StShadowHelper, 1);
+ helper->shadow = st_shadow_ref (shadow);
+
+ return helper;
+}
+
+/**
+ * st_shadow_helper_update:
+ * @helper: a #StShadowHelper
+ * @source: a #ClutterActor
+ *
+ * Update @helper from @source.
+ */
+void
+st_shadow_helper_update (StShadowHelper *helper,
+ ClutterActor *source)
+{
+ gfloat width, height;
+
+ clutter_actor_get_size (source, &width, &height);
+
+ if (helper->pipeline == NULL ||
+ helper->width != width ||
+ helper->height != height)
+ {
+ if (helper->pipeline)
+ cogl_object_unref (helper->pipeline);
+
+ helper->pipeline = _st_create_shadow_pipeline_from_actor (helper->shadow, source);
+ helper->width = width;
+ helper->height = height;
+ }
+}
+
+/**
+ * st_shadow_helper_copy:
+ * @helper: the #StShadowHelper to copy
+ *
+ * Returns: (transfer full): a copy of @helper
+ */
+StShadowHelper *
+st_shadow_helper_copy (StShadowHelper *helper)
+{
+ StShadowHelper *copy;
+
+ copy = g_new (StShadowHelper, 1);
+ *copy = *helper;
+ if (copy->pipeline)
+ cogl_object_ref (copy->pipeline);
+ st_shadow_ref (copy->shadow);
+
+ return copy;
+}
+
+/**
+ * st_shadow_helper_free:
+ * @helper: a #StShadowHelper
+ *
+ * Free resources associated with @helper.
+ */
+void
+st_shadow_helper_free (StShadowHelper *helper)
+{
+ if (helper->pipeline)
+ cogl_object_unref (helper->pipeline);
+ st_shadow_unref (helper->shadow);
+
+ g_free (helper);
+}
+
+/**
+ * st_shadow_helper_paint:
+ * @helper: a #StShadowHelper
+ * @framebuffer: a #CoglFramebuffer
+ * @actor_box: the bounding box of the shadow
+ * @paint_opacity: the opacity at which the shadow is painted
+ *
+ * Paints the shadow associated with @helper This must only
+ * be called from the implementation of ClutterActor::paint().
+ */
+void
+st_shadow_helper_paint (StShadowHelper *helper,
+ CoglFramebuffer *framebuffer,
+ ClutterActorBox *actor_box,
+ guint8 paint_opacity)
+{
+ _st_paint_shadow_with_opacity (helper->shadow,
+ framebuffer,
+ helper->pipeline,
+ actor_box,
+ paint_opacity);
+}
diff --git a/src/st/st-shadow.h b/src/st/st-shadow.h
new file mode 100644
index 0000000..267d48f
--- /dev/null
+++ b/src/st/st-shadow.h
@@ -0,0 +1,95 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-shadow.h: Boxed type holding for -st-shadow attributes
+ *
+ * Copyright 2009, 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_SHADOW__
+#define __ST_SHADOW__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SHADOW (st_shadow_get_type ())
+#define ST_TYPE_SHADOW_HELPER (st_shadow_get_type ())
+
+typedef struct _StShadow StShadow;
+typedef struct _StShadowHelper StShadowHelper;
+
+/**
+ * StShadow:
+ * @color: shadow's color
+ * @xoffset: horizontal offset - positive values mean placement to the right,
+ * negative values placement to the left of the element.
+ * @yoffset: vertical offset - positive values mean placement below, negative
+ * values placement above the element.
+ * @blur: shadow's blur radius - a value of 0.0 will result in a hard shadow.
+ * @spread: shadow's spread radius - grow the shadow without enlarging the
+ * blur.
+ *
+ * Attributes of the -st-shadow property.
+ */
+struct _StShadow {
+ ClutterColor color;
+ gdouble xoffset;
+ gdouble yoffset;
+ gdouble blur;
+ gdouble spread;
+ gboolean inset;
+ volatile int ref_count;
+};
+
+GType st_shadow_get_type (void) G_GNUC_CONST;
+
+StShadow *st_shadow_new (ClutterColor *color,
+ gdouble xoffset,
+ gdouble yoffset,
+ gdouble blur,
+ gdouble spread,
+ gboolean inset);
+StShadow *st_shadow_ref (StShadow *shadow);
+void st_shadow_unref (StShadow *shadow);
+
+gboolean st_shadow_equal (StShadow *shadow,
+ StShadow *other);
+
+void st_shadow_get_box (StShadow *shadow,
+ const ClutterActorBox *actor_box,
+ ClutterActorBox *shadow_box);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (StShadow, st_shadow_unref)
+
+
+GType st_shadow_helper_get_type (void) G_GNUC_CONST;
+
+StShadowHelper *st_shadow_helper_new (StShadow *shadow);
+
+StShadowHelper *st_shadow_helper_copy (StShadowHelper *helper);
+void st_shadow_helper_free (StShadowHelper *helper);
+
+void st_shadow_helper_update (StShadowHelper *helper,
+ ClutterActor *source);
+
+void st_shadow_helper_paint (StShadowHelper *helper,
+ CoglFramebuffer *framebuffer,
+ ClutterActorBox *actor_box,
+ guint8 paint_opacity);
+
+G_END_DECLS
+
+#endif /* __ST_SHADOW__ */
diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c
new file mode 100644
index 0000000..7062221
--- /dev/null
+++ b/src/st/st-texture-cache.c
@@ -0,0 +1,1688 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-texture-cache.h: Object for loading and caching images as textures
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010, Maxim Ermilov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "st-image-content.h"
+#include "st-texture-cache.h"
+#include "st-private.h"
+#include "st-settings.h"
+#include <gtk/gtk.h>
+#include <math.h>
+#include <string.h>
+#include <glib.h>
+
+#define CACHE_PREFIX_ICON "icon:"
+#define CACHE_PREFIX_FILE "file:"
+#define CACHE_PREFIX_FILE_FOR_CAIRO "file-for-cairo:"
+
+struct _StTextureCachePrivate
+{
+ GtkIconTheme *icon_theme;
+ GSettings *settings;
+
+ /* Things that were loaded with a cache policy != NONE */
+ GHashTable *keyed_cache; /* char * -> ClutterImage* */
+ GHashTable *keyed_surface_cache; /* char * -> cairo_surface_t* */
+
+ GHashTable *used_scales; /* Set: double */
+
+ /* Presently this is used to de-duplicate requests for GIcons and async URIs. */
+ GHashTable *outstanding_requests; /* char * -> AsyncTextureLoadData * */
+
+ /* File monitors to evict cache data on changes */
+ GHashTable *file_monitors; /* char * -> GFileMonitor * */
+
+ GCancellable *cancellable;
+};
+
+static void st_texture_cache_dispose (GObject *object);
+static void st_texture_cache_finalize (GObject *object);
+
+enum
+{
+ ICON_THEME_CHANGED,
+ TEXTURE_FILE_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+G_DEFINE_TYPE(StTextureCache, st_texture_cache, G_TYPE_OBJECT);
+
+/* We want to preserve the aspect ratio by default, also the default
+ * pipeline for an empty texture is full opacity white, which we
+ * definitely don't want. Skip that by setting 0 opacity.
+ */
+static ClutterActor *
+create_invisible_actor (void)
+{
+ return g_object_new (CLUTTER_TYPE_ACTOR,
+ "opacity", 0,
+ "request-mode", CLUTTER_REQUEST_CONTENT_SIZE,
+ NULL);
+}
+
+/* Reverse the opacity we added while loading */
+static void
+set_content_from_image (ClutterActor *actor,
+ ClutterContent *image)
+{
+ g_assert (image && CLUTTER_IS_IMAGE (image));
+
+ clutter_actor_set_content (actor, image);
+ clutter_actor_set_opacity (actor, 255);
+}
+
+static void
+st_texture_cache_class_init (StTextureCacheClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *)klass;
+
+ gobject_class->dispose = st_texture_cache_dispose;
+ gobject_class->finalize = st_texture_cache_finalize;
+
+ /**
+ * StTextureCache::icon-theme-changed:
+ * @self: a #StTextureCache
+ *
+ * Emitted when the icon theme is changed.
+ */
+ signals[ICON_THEME_CHANGED] =
+ g_signal_new ("icon-theme-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, /* no default handler slot */
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * StTextureCache::texture-file-changed:
+ * @self: a #StTextureCache
+ * @file: a #GFile
+ *
+ * Emitted when the source file of a texture is changed.
+ */
+ signals[TEXTURE_FILE_CHANGED] =
+ g_signal_new ("texture-file-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, /* no default handler slot */
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_FILE);
+}
+
+/* Evicts all cached textures for named icons */
+static void
+st_texture_cache_evict_icons (StTextureCache *cache)
+{
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, cache->priv->keyed_cache);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const char *cache_key = key;
+
+ /* This is too conservative - it takes out all cached textures
+ * for GIcons even when they aren't named icons, but it's not
+ * worth the complexity of parsing the key and calling
+ * g_icon_new_for_string(); icon theme changes aren't normal */
+ if (g_str_has_prefix (cache_key, CACHE_PREFIX_ICON))
+ g_hash_table_iter_remove (&iter);
+ }
+}
+
+static void
+on_icon_theme_changed (StSettings *settings,
+ GParamSpec *pspec,
+ StTextureCache *cache)
+{
+ g_autofree gchar *theme = NULL;
+
+ g_cancellable_cancel (cache->priv->cancellable);
+ g_cancellable_reset (cache->priv->cancellable);
+
+ st_texture_cache_evict_icons (cache);
+
+ g_object_get (settings, "gtk-icon-theme", &theme, NULL);
+ gtk_icon_theme_set_custom_theme (cache->priv->icon_theme, theme);
+
+ g_signal_emit (cache, signals[ICON_THEME_CHANGED], 0);
+}
+
+static void
+on_gtk_icon_theme_changed (GtkIconTheme *icon_theme,
+ StTextureCache *self)
+{
+ st_texture_cache_evict_icons (self);
+ g_signal_emit (self, signals[ICON_THEME_CHANGED], 0);
+}
+
+static void
+st_texture_cache_init (StTextureCache *self)
+{
+ StSettings *settings;
+
+ self->priv = g_new0 (StTextureCachePrivate, 1);
+
+ self->priv->icon_theme = gtk_icon_theme_new ();
+ gtk_icon_theme_add_resource_path (self->priv->icon_theme,
+ "/org/gnome/shell/icons");
+ g_signal_connect (self->priv->icon_theme, "changed",
+ G_CALLBACK (on_gtk_icon_theme_changed), self);
+
+ settings = st_settings_get ();
+ g_signal_connect (settings, "notify::gtk-icon-theme",
+ G_CALLBACK (on_icon_theme_changed), self);
+
+ self->priv->keyed_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+ self->priv->keyed_surface_cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) cairo_surface_destroy);
+ self->priv->used_scales = g_hash_table_new_full (g_double_hash, g_double_equal,
+ g_free, NULL);
+ self->priv->outstanding_requests = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+ self->priv->file_monitors = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
+ g_object_unref, g_object_unref);
+
+ self->priv->cancellable = g_cancellable_new ();
+
+ on_icon_theme_changed (settings, NULL, self);
+}
+
+static void
+st_texture_cache_dispose (GObject *object)
+{
+ StTextureCache *self = (StTextureCache*)object;
+
+ g_cancellable_cancel (self->priv->cancellable);
+
+ g_clear_object (&self->priv->settings);
+ g_clear_object (&self->priv->icon_theme);
+ g_clear_object (&self->priv->cancellable);
+
+ g_clear_pointer (&self->priv->keyed_cache, g_hash_table_destroy);
+ g_clear_pointer (&self->priv->keyed_surface_cache, g_hash_table_destroy);
+ g_clear_pointer (&self->priv->used_scales, g_hash_table_destroy);
+ g_clear_pointer (&self->priv->outstanding_requests, g_hash_table_destroy);
+ g_clear_pointer (&self->priv->file_monitors, g_hash_table_destroy);
+
+ G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
+}
+
+static void
+st_texture_cache_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
+}
+
+static void
+compute_pixbuf_scale (gint width,
+ gint height,
+ gint available_width,
+ gint available_height,
+ gint *new_width,
+ gint *new_height)
+{
+ int scaled_width, scaled_height;
+
+ if (width == 0 || height == 0)
+ {
+ *new_width = *new_height = 0;
+ return;
+ }
+
+ if (available_width >= 0 && available_height >= 0)
+ {
+ /* This should keep the aspect ratio of the image intact, because if
+ * available_width < (available_height * width) / height
+ * then
+ * (available_width * height) / width < available_height
+ * So we are guaranteed to either scale the image to have an available_width
+ * for width and height scaled accordingly OR have the available_height
+ * for height and width scaled accordingly, whichever scaling results
+ * in the image that can fit both available dimensions.
+ */
+ scaled_width = MIN (available_width, (available_height * width) / height);
+ scaled_height = MIN (available_height, (available_width * height) / width);
+ }
+ else if (available_width >= 0)
+ {
+ scaled_width = available_width;
+ scaled_height = (available_width * height) / width;
+ }
+ else if (available_height >= 0)
+ {
+ scaled_width = (available_height * width) / height;
+ scaled_height = available_height;
+ }
+ else
+ {
+ scaled_width = scaled_height = 0;
+ }
+
+ /* Scale the image only if that will not increase its original dimensions. */
+ if (scaled_width > 0 && scaled_height > 0 && scaled_width < width && scaled_height < height)
+ {
+ *new_width = scaled_width;
+ *new_height = scaled_height;
+ }
+ else
+ {
+ *new_width = width;
+ *new_height = height;
+ }
+}
+
+static void
+rgba_from_clutter (GdkRGBA *rgba,
+ ClutterColor *color)
+{
+ rgba->red = color->red / 255.;
+ rgba->green = color->green / 255.;
+ rgba->blue = color->blue / 255.;
+ rgba->alpha = color->alpha / 255.;
+}
+
+/* A private structure for keeping width, height and scale. */
+typedef struct {
+ int width;
+ int height;
+ int scale;
+} Dimensions;
+
+/* This struct corresponds to a request for an texture.
+ * It's creasted when something needs a new texture,
+ * and destroyed when the texture data is loaded. */
+typedef struct {
+ StTextureCache *cache;
+ StTextureCachePolicy policy;
+ char *key;
+
+ guint width;
+ guint height;
+ guint paint_scale;
+ gfloat resource_scale;
+ GSList *actors;
+
+ GtkIconInfo *icon_info;
+ StIconColors *colors;
+ GFile *file;
+} AsyncTextureLoadData;
+
+static void
+texture_load_data_free (gpointer p)
+{
+ AsyncTextureLoadData *data = p;
+
+ if (data->icon_info)
+ {
+ g_object_unref (data->icon_info);
+ if (data->colors)
+ st_icon_colors_unref (data->colors);
+ }
+ else if (data->file)
+ g_object_unref (data->file);
+
+ if (data->key)
+ g_free (data->key);
+
+ if (data->actors)
+ g_slist_free_full (data->actors, (GDestroyNotify) g_object_unref);
+
+ g_free (data);
+}
+
+/**
+ * on_image_size_prepared:
+ * @pixbuf_loader: #GdkPixbufLoader loading the image
+ * @width: the original width of the image
+ * @height: the original height of the image
+ * @data: pointer to the #Dimensions structure containing available width and height for the image,
+ * available width or height can be -1 if the dimension is not limited
+ *
+ * Private function.
+ *
+ * Sets the size of the image being loaded to fit the available width and height dimensions,
+ * but never scales up the image beyond its actual size.
+ * Intended to be used as a callback for #GdkPixbufLoader "size-prepared" signal.
+ */
+static void
+on_image_size_prepared (GdkPixbufLoader *pixbuf_loader,
+ gint width,
+ gint height,
+ gpointer data)
+{
+ Dimensions *available_dimensions = data;
+ int available_width = available_dimensions->width;
+ int available_height = available_dimensions->height;
+ int scale_factor = available_dimensions->scale;
+ int scaled_width;
+ int scaled_height;
+
+ compute_pixbuf_scale (width, height, available_width, available_height,
+ &scaled_width, &scaled_height);
+
+ gdk_pixbuf_loader_set_size (pixbuf_loader,
+ scaled_width * scale_factor,
+ scaled_height * scale_factor);
+}
+
+static GdkPixbuf *
+impl_load_pixbuf_data (const guchar *data,
+ gsize size,
+ int available_width,
+ int available_height,
+ int scale,
+ GError **error)
+{
+ GdkPixbufLoader *pixbuf_loader = NULL;
+ GdkPixbuf *rotated_pixbuf = NULL;
+ GdkPixbuf *pixbuf;
+ gboolean success;
+ Dimensions available_dimensions;
+ int width_before_rotation, width_after_rotation;
+
+ pixbuf_loader = gdk_pixbuf_loader_new ();
+
+ available_dimensions.width = available_width;
+ available_dimensions.height = available_height;
+ available_dimensions.scale = scale;
+ g_signal_connect (pixbuf_loader, "size-prepared",
+ G_CALLBACK (on_image_size_prepared), &available_dimensions);
+
+ success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
+ if (!success)
+ goto out;
+ success = gdk_pixbuf_loader_close (pixbuf_loader, error);
+ if (!success)
+ goto out;
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
+
+ width_before_rotation = gdk_pixbuf_get_width (pixbuf);
+
+ rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+ width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf);
+
+ /* There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
+ * so we only check that once it is loaded, and reload it again if it needs to be rotated in order
+ * to use the available width and height correctly.
+ * See http://bugzilla.gnome.org/show_bug.cgi?id=579003
+ */
+ if (width_before_rotation != width_after_rotation)
+ {
+ g_object_unref (pixbuf_loader);
+ g_object_unref (rotated_pixbuf);
+ rotated_pixbuf = NULL;
+
+ pixbuf_loader = gdk_pixbuf_loader_new ();
+
+ /* We know that the image will later be rotated, so we reverse the available dimensions. */
+ available_dimensions.width = available_height;
+ available_dimensions.height = available_width;
+ available_dimensions.scale = scale;
+ g_signal_connect (pixbuf_loader, "size-prepared",
+ G_CALLBACK (on_image_size_prepared), &available_dimensions);
+
+ success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
+ if (!success)
+ goto out;
+
+ success = gdk_pixbuf_loader_close (pixbuf_loader, error);
+ if (!success)
+ goto out;
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
+
+ rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+ }
+
+out:
+ if (pixbuf_loader)
+ g_object_unref (pixbuf_loader);
+ return rotated_pixbuf;
+}
+
+static GdkPixbuf *
+impl_load_pixbuf_file (GFile *file,
+ int available_width,
+ int available_height,
+ int paint_scale,
+ float resource_scale,
+ GError **error)
+{
+ GdkPixbuf *pixbuf = NULL;
+ char *contents = NULL;
+ gsize size;
+
+ if (g_file_load_contents (file, NULL, &contents, &size, NULL, error))
+ {
+ int scale = ceilf (paint_scale * resource_scale);
+ pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size,
+ available_width, available_height,
+ scale,
+ error);
+ }
+
+ g_free (contents);
+
+ return pixbuf;
+}
+
+static void
+load_pixbuf_thread (GTask *result,
+ gpointer source,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GdkPixbuf *pixbuf;
+ AsyncTextureLoadData *data = task_data;
+ GError *error = NULL;
+
+ g_assert (data != NULL);
+ g_assert (data->file != NULL);
+
+ pixbuf = impl_load_pixbuf_file (data->file, data->width, data->height,
+ data->paint_scale, data->resource_scale,
+ &error);
+
+ if (error != NULL)
+ g_task_return_error (result, error);
+ else if (pixbuf)
+ g_task_return_pointer (result, g_object_ref (pixbuf), g_object_unref);
+
+ g_clear_object (&pixbuf);
+}
+
+static GdkPixbuf *
+load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static ClutterContent *
+pixbuf_to_st_content_image (GdkPixbuf *pixbuf,
+ int width,
+ int height,
+ int paint_scale,
+ float resource_scale)
+{
+ ClutterContent *image;
+ g_autoptr(GError) error = NULL;
+
+ float native_width, native_height;
+
+ native_width = ceilf (gdk_pixbuf_get_width (pixbuf) / resource_scale);
+ native_height = ceilf (gdk_pixbuf_get_height (pixbuf) / resource_scale);
+
+ if (width < 0 && height < 0)
+ {
+ width = native_width;
+ height = native_height;
+ }
+ else if (width < 0)
+ {
+ height *= paint_scale;
+ width = native_width * (height / native_height);
+ }
+ else if (height < 0)
+ {
+ width *= paint_scale;
+ height = native_height * (width / native_width);
+ }
+ else
+ {
+ width *= paint_scale;
+ height *= paint_scale;
+ }
+
+ image = st_image_content_new_with_preferred_size (width, height);
+ clutter_image_set_data (CLUTTER_IMAGE (image),
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_has_alpha (pixbuf) ?
+ COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf),
+ &error);
+
+ if (error)
+ {
+ g_warning ("Failed to allocate texture: %s", error->message);
+ g_clear_object (&image);
+ }
+
+ return image;
+}
+
+static cairo_surface_t *
+pixbuf_to_cairo_surface (GdkPixbuf *pixbuf)
+{
+ cairo_surface_t *dummy_surface;
+ cairo_pattern_t *pattern;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ dummy_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+
+ cr = cairo_create (dummy_surface);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ pattern = cairo_get_source (cr);
+ cairo_pattern_get_surface (pattern, &surface);
+ cairo_surface_reference (surface);
+ cairo_destroy (cr);
+ cairo_surface_destroy (dummy_surface);
+
+ return surface;
+}
+
+static void
+finish_texture_load (AsyncTextureLoadData *data,
+ GdkPixbuf *pixbuf)
+{
+ g_autoptr(ClutterContent) image = NULL;
+ GSList *iter;
+ StTextureCache *cache;
+
+ cache = data->cache;
+
+ g_hash_table_remove (cache->priv->outstanding_requests, data->key);
+
+ if (pixbuf == NULL)
+ goto out;
+
+ if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE)
+ {
+ gpointer orig_key = NULL, value = NULL;
+
+ if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key,
+ &orig_key, &value))
+ {
+ image = pixbuf_to_st_content_image (pixbuf,
+ data->width, data->height,
+ data->paint_scale,
+ data->resource_scale);
+ if (!image)
+ goto out;
+
+ g_hash_table_insert (cache->priv->keyed_cache, g_strdup (data->key),
+ g_object_ref (image));
+ }
+ else
+ {
+ image = g_object_ref (value);
+ }
+ }
+ else
+ {
+ image = pixbuf_to_st_content_image (pixbuf,
+ data->width, data->height,
+ data->paint_scale,
+ data->resource_scale);
+ if (!image)
+ goto out;
+ }
+
+ for (iter = data->actors; iter; iter = iter->next)
+ {
+ ClutterActor *actor = iter->data;
+ set_content_from_image (actor, image);
+ }
+
+out:
+ texture_load_data_free (data);
+}
+
+static void
+on_symbolic_icon_loaded (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GdkPixbuf *pixbuf;
+ pixbuf = gtk_icon_info_load_symbolic_finish (GTK_ICON_INFO (source), result, NULL, NULL);
+ finish_texture_load (user_data, pixbuf);
+ g_clear_object (&pixbuf);
+}
+
+static void
+on_icon_loaded (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GdkPixbuf *pixbuf;
+ pixbuf = gtk_icon_info_load_icon_finish (GTK_ICON_INFO (source), result, NULL);
+ finish_texture_load (user_data, pixbuf);
+ g_clear_object (&pixbuf);
+}
+
+static void
+on_pixbuf_loaded (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GdkPixbuf *pixbuf;
+ pixbuf = load_pixbuf_async_finish (ST_TEXTURE_CACHE (source), result, NULL);
+ finish_texture_load (user_data, pixbuf);
+ g_clear_object (&pixbuf);
+}
+
+static void
+load_texture_async (StTextureCache *cache,
+ AsyncTextureLoadData *data)
+{
+ if (data->file)
+ {
+ GTask *task = g_task_new (cache, NULL, on_pixbuf_loaded, data);
+ g_task_set_task_data (task, data, NULL);
+ g_task_run_in_thread (task, load_pixbuf_thread);
+ g_object_unref (task);
+ }
+ else if (data->icon_info)
+ {
+ StIconColors *colors = data->colors;
+ if (colors)
+ {
+ GdkRGBA foreground_color;
+ GdkRGBA success_color;
+ GdkRGBA warning_color;
+ GdkRGBA error_color;
+
+ rgba_from_clutter (&foreground_color, &colors->foreground);
+ rgba_from_clutter (&success_color, &colors->success);
+ rgba_from_clutter (&warning_color, &colors->warning);
+ rgba_from_clutter (&error_color, &colors->error);
+
+ gtk_icon_info_load_symbolic_async (data->icon_info,
+ &foreground_color, &success_color,
+ &warning_color, &error_color,
+ cache->priv->cancellable,
+ on_symbolic_icon_loaded, data);
+ }
+ else
+ {
+ gtk_icon_info_load_icon_async (data->icon_info,
+ cache->priv->cancellable,
+ on_icon_loaded, data);
+ }
+ }
+ else
+ g_assert_not_reached ();
+}
+
+typedef struct {
+ StTextureCache *cache;
+ ClutterContent *image;
+ GObject *source;
+ gulong notify_signal_id;
+ gboolean weakref_active;
+} StTextureCachePropertyBind;
+
+static void
+st_texture_cache_load_surface (ClutterContent **image,
+ cairo_surface_t *surface)
+{
+ g_return_if_fail (image != NULL);
+
+ if (surface != NULL &&
+ cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE &&
+ (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ||
+ cairo_image_surface_get_format (surface) == CAIRO_FORMAT_RGB24))
+ {
+ g_autoptr(GError) error = NULL;
+ int width, height, size;
+
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_width (surface);
+ size = MAX(width, height);
+
+ if (*image == NULL)
+ *image = st_image_content_new_with_preferred_size (size, size);
+
+ clutter_image_set_data (CLUTTER_IMAGE (*image),
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ?
+ COGL_PIXEL_FORMAT_BGRA_8888 : COGL_PIXEL_FORMAT_BGR_888,
+ width,
+ height,
+ cairo_image_surface_get_stride (surface),
+ &error);
+
+ if (error)
+ g_warning ("Failed to allocate texture: %s", error->message);
+ }
+ else if (*image == NULL)
+ {
+ *image = st_image_content_new_with_preferred_size (0, 0);
+ }
+}
+
+static void
+st_texture_cache_reset_texture (StTextureCachePropertyBind *bind,
+ const char *propname)
+{
+ cairo_surface_t *surface;
+
+ g_object_get (bind->source, propname, &surface, NULL);
+
+ st_texture_cache_load_surface (&bind->image, surface);
+}
+
+static void
+st_texture_cache_on_pixbuf_notify (GObject *object,
+ GParamSpec *paramspec,
+ gpointer data)
+{
+ StTextureCachePropertyBind *bind = data;
+ st_texture_cache_reset_texture (bind, paramspec->name);
+}
+
+static void
+st_texture_cache_bind_weak_notify (gpointer data,
+ GObject *source_location)
+{
+ StTextureCachePropertyBind *bind = data;
+ bind->weakref_active = FALSE;
+ g_signal_handler_disconnect (bind->source, bind->notify_signal_id);
+}
+
+static void
+st_texture_cache_free_bind (gpointer data)
+{
+ StTextureCachePropertyBind *bind = data;
+ if (bind->weakref_active)
+ g_object_weak_unref (G_OBJECT (bind->image), st_texture_cache_bind_weak_notify, bind);
+ g_free (bind);
+}
+
+/**
+ * st_texture_cache_bind_cairo_surface_property:
+ * @cache: A #StTextureCache
+ * @object: A #GObject with a property @property_name of type #cairo_surface_t
+ * @property_name: Name of a property
+ *
+ * Create a #GIcon which tracks the #cairo_surface_t value of a GObject property
+ * named by @property_name. Unlike other methods in StTextureCache, the underlying
+ * #CoglTexture is not shared by default with other invocations to this method.
+ *
+ * If the source object is destroyed, the texture will continue to show the last
+ * value of the property.
+ *
+ * Returns: (transfer full): A new #GIcon
+ */
+GIcon *
+st_texture_cache_bind_cairo_surface_property (StTextureCache *cache,
+ GObject *object,
+ const char *property_name)
+{
+ gchar *notify_key;
+ StTextureCachePropertyBind *bind;
+
+ bind = g_new0 (StTextureCachePropertyBind, 1);
+ bind->cache = cache;
+ bind->source = object;
+
+ st_texture_cache_reset_texture (bind, property_name);
+
+ g_object_weak_ref (G_OBJECT (bind->image), st_texture_cache_bind_weak_notify, bind);
+ bind->weakref_active = TRUE;
+
+ notify_key = g_strdup_printf ("notify::%s", property_name);
+ bind->notify_signal_id = g_signal_connect_data (object, notify_key, G_CALLBACK(st_texture_cache_on_pixbuf_notify),
+ bind, (GClosureNotify)st_texture_cache_free_bind, 0);
+ g_free (notify_key);
+
+ return G_ICON (bind->image);
+}
+
+/**
+ * st_texture_cache_load_cairo_surface_to_gicon:
+ * @cache: A #StTextureCache
+ * @surface: A #cairo_surface_t
+ *
+ * Create a #GIcon from @surface.
+ *
+ * Returns: (transfer full): A new #GIcon
+ */
+GIcon *
+st_texture_cache_load_cairo_surface_to_gicon (StTextureCache *cache,
+ cairo_surface_t *surface)
+{
+ ClutterContent *image = NULL;
+ st_texture_cache_load_surface (&image, surface);
+
+ return G_ICON (image);
+}
+
+/**
+ * st_texture_cache_load: (skip)
+ * @cache: A #StTextureCache
+ * @key: Arbitrary string used to refer to item
+ * @policy: Caching policy
+ * @load: Function to create the texture, if not already cached
+ * @data: User data passed to @load
+ * @error: A #GError
+ *
+ * Load an arbitrary texture, caching it. The string chosen for @key
+ * should be of the form "type-prefix:type-uuid". For example,
+ * "url:file:///usr/share/icons/hicolor/48x48/apps/firefox.png", or
+ * "stock-icon:gtk-ok".
+ *
+ * Returns: (transfer full): A newly-referenced handle to the texture
+ */
+CoglTexture *
+st_texture_cache_load (StTextureCache *cache,
+ const char *key,
+ StTextureCachePolicy policy,
+ StTextureCacheLoader load,
+ void *data,
+ GError **error)
+{
+ CoglTexture *texture;
+
+ texture = g_hash_table_lookup (cache->priv->keyed_cache, key);
+ if (!texture)
+ {
+ texture = load (cache, key, data, error);
+ if (texture && policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
+ g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texture);
+ }
+
+ if (texture && policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
+ cogl_object_ref (texture);
+
+ return texture;
+}
+
+/**
+ * ensure_request:
+ * @cache: A #StTextureCache
+ * @key: A cache key
+ * @policy: Cache policy
+ * @request: (out): If no request is outstanding, one will be created and returned here
+ * @texture: A texture to be added to the request
+ *
+ * Check for any outstanding load for the data represented by @key. If there
+ * is already a request pending, append it to that request to avoid loading
+ * the data multiple times.
+ *
+ * Returns: %TRUE if there is already a request pending
+ */
+static gboolean
+ensure_request (StTextureCache *cache,
+ const char *key,
+ StTextureCachePolicy policy,
+ AsyncTextureLoadData **request,
+ ClutterActor *actor)
+{
+ ClutterContent *image;
+ AsyncTextureLoadData *pending;
+ gboolean had_pending;
+
+ image = g_hash_table_lookup (cache->priv->keyed_cache, key);
+
+ if (image != NULL)
+ {
+ /* We had this cached already, just set the texture and we're done. */
+ set_content_from_image (actor, image);
+ return TRUE;
+ }
+
+ pending = g_hash_table_lookup (cache->priv->outstanding_requests, key);
+ had_pending = pending != NULL;
+
+ if (pending == NULL)
+ {
+ /* Not cached and no pending request, create it */
+ *request = g_new0 (AsyncTextureLoadData, 1);
+ if (policy != ST_TEXTURE_CACHE_POLICY_NONE)
+ g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request);
+ }
+ else
+ *request = pending;
+
+ /* Regardless of whether there was a pending request, prepend our texture here. */
+ (*request)->actors = g_slist_prepend ((*request)->actors, g_object_ref (actor));
+
+ return had_pending;
+}
+
+/**
+ * st_texture_cache_load_gicon:
+ * @cache: A #StTextureCache
+ * @theme_node: (nullable): The #StThemeNode to use for colors, or %NULL
+ * if the icon must not be recolored
+ * @icon: the #GIcon to load
+ * @size: Size of themed
+ * @paint_scale: Scale factor of display
+ * @resource_scale: Resource scale factor
+ *
+ * This method returns a new #ClutterActor for a given #GIcon. If the
+ * icon isn't loaded already, the texture will be filled
+ * asynchronously.
+ *
+ * Returns: (transfer none) (nullable): A new #ClutterActor for the icon, or %NULL if not found
+ */
+ClutterActor *
+st_texture_cache_load_gicon (StTextureCache *cache,
+ StThemeNode *theme_node,
+ GIcon *icon,
+ gint size,
+ gint paint_scale,
+ gfloat resource_scale)
+{
+ AsyncTextureLoadData *request;
+ ClutterActor *actor;
+ gint scale;
+ char *gicon_string;
+ g_autofree char *key = NULL;
+ float actor_size;
+ GtkIconTheme *theme;
+ StTextureCachePolicy policy;
+ StIconColors *colors = NULL;
+ StIconStyle icon_style = ST_ICON_STYLE_REQUESTED;
+ GtkIconLookupFlags lookup_flags;
+
+ actor_size = size * paint_scale;
+
+ if (ST_IS_IMAGE_CONTENT (icon))
+ {
+ int width, height;
+
+ g_object_get (G_OBJECT (icon),
+ "preferred-width", &width,
+ "preferred-height", &height,
+ NULL);
+ if (width == 0 && height == 0)
+ return NULL;
+
+ return g_object_new (CLUTTER_TYPE_ACTOR,
+ "content-gravity", CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT,
+ "width", actor_size,
+ "height", actor_size,
+ "content", CLUTTER_CONTENT (icon),
+ NULL);
+ }
+
+ if (theme_node)
+ {
+ colors = st_theme_node_get_icon_colors (theme_node);
+ icon_style = st_theme_node_get_icon_style (theme_node);
+ }
+
+ /* Do theme lookups in the main thread to avoid thread-unsafety */
+ theme = cache->priv->icon_theme;
+
+ lookup_flags = GTK_ICON_LOOKUP_USE_BUILTIN;
+
+ if (icon_style == ST_ICON_STYLE_REGULAR)
+ lookup_flags |= GTK_ICON_LOOKUP_FORCE_REGULAR;
+ else if (icon_style == ST_ICON_STYLE_SYMBOLIC)
+ lookup_flags |= GTK_ICON_LOOKUP_FORCE_SYMBOLIC;
+
+ if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
+ lookup_flags |= GTK_ICON_LOOKUP_DIR_RTL;
+ else
+ lookup_flags |= GTK_ICON_LOOKUP_DIR_LTR;
+
+ scale = ceilf (paint_scale * resource_scale);
+
+ gicon_string = g_icon_to_string (icon);
+ /* A return value of NULL indicates that the icon can not be serialized,
+ * so don't have a unique identifier for it as a cache key, and thus can't
+ * be cached. If it is cacheable, we hardcode a policy of FOREVER here for
+ * now; we should actually blow this away on icon theme changes probably */
+ policy = gicon_string != NULL ? ST_TEXTURE_CACHE_POLICY_FOREVER
+ : ST_TEXTURE_CACHE_POLICY_NONE;
+ if (colors)
+ {
+ /* This raises some doubts about the practice of using string keys */
+ key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d,style=%d,colors=%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x",
+ gicon_string, size, scale, icon_style,
+ colors->foreground.red, colors->foreground.blue, colors->foreground.green, colors->foreground.alpha,
+ colors->warning.red, colors->warning.blue, colors->warning.green, colors->warning.alpha,
+ colors->error.red, colors->error.blue, colors->error.green, colors->error.alpha,
+ colors->success.red, colors->success.blue, colors->success.green, colors->success.alpha);
+ }
+ else
+ {
+ key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d,style=%d",
+ gicon_string, size, scale, icon_style);
+ }
+ g_free (gicon_string);
+
+ actor = create_invisible_actor ();
+ clutter_actor_set_content_gravity (actor, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT);
+ clutter_actor_set_size (actor, actor_size, actor_size);
+ if (!ensure_request (cache, key, policy, &request, actor))
+ {
+ /* Else, make a new request */
+ GtkIconInfo *info;
+
+ info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon,
+ size, scale,
+ lookup_flags);
+ if (info == NULL)
+ {
+ g_hash_table_remove (cache->priv->outstanding_requests, key);
+ texture_load_data_free (request);
+ g_object_unref (actor);
+ return NULL;
+ }
+
+ request->cache = cache;
+ /* Transfer ownership of key */
+ request->key = g_steal_pointer (&key);
+ request->policy = policy;
+ request->colors = colors ? st_icon_colors_ref (colors) : NULL;
+ request->icon_info = info;
+ request->width = request->height = size;
+ request->paint_scale = paint_scale;
+ request->resource_scale = resource_scale;
+
+ load_texture_async (cache, request);
+ }
+
+ return actor;
+}
+
+static ClutterActor *
+load_from_pixbuf (GdkPixbuf *pixbuf,
+ int paint_scale,
+ float resource_scale)
+{
+ g_autoptr(ClutterContent) image = NULL;
+ ClutterActor *actor;
+
+ image = pixbuf_to_st_content_image (pixbuf, -1, -1, paint_scale, resource_scale);
+
+ actor = g_object_new (CLUTTER_TYPE_ACTOR,
+ "request-mode", CLUTTER_REQUEST_CONTENT_SIZE,
+ NULL);
+ clutter_actor_set_content (actor, image);
+
+ return actor;
+}
+
+static void
+hash_table_remove_with_scales (GHashTable *hash,
+ GList *scales,
+ const char *base_key)
+{
+ GList *l;
+
+ for (l = scales; l; l = l->next)
+ {
+ double scale = *((double *)l->data);
+ g_autofree char *key = NULL;
+ key = g_strdup_printf ("%s%f", base_key, scale);
+ g_hash_table_remove (hash, key);
+ }
+}
+
+static void
+hash_table_insert_scale (GHashTable *hash,
+ double scale)
+{
+ double *saved_scale;
+
+ if (g_hash_table_contains (hash, &scale))
+ return;
+
+ saved_scale = g_new (double, 1);
+ *saved_scale = scale;
+
+ g_hash_table_add (hash, saved_scale);
+}
+
+static void
+file_changed_cb (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ StTextureCache *cache = user_data;
+ char *key;
+ guint file_hash;
+ g_autoptr (GList) scales = NULL;
+
+ if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+ return;
+
+ file_hash = g_file_hash (file);
+ scales = g_hash_table_get_keys (cache->priv->used_scales);
+
+ key = g_strdup_printf (CACHE_PREFIX_FILE "%u", file_hash);
+ g_hash_table_remove (cache->priv->keyed_cache, key);
+ hash_table_remove_with_scales (cache->priv->keyed_cache, scales, key);
+ g_free (key);
+
+ key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u", file_hash);
+ g_hash_table_remove (cache->priv->keyed_surface_cache, key);
+ hash_table_remove_with_scales (cache->priv->keyed_surface_cache, scales, key);
+ g_free (key);
+
+ g_signal_emit (cache, signals[TEXTURE_FILE_CHANGED], 0, file);
+}
+
+static void
+ensure_monitor_for_file (StTextureCache *cache,
+ GFile *file)
+{
+ StTextureCachePrivate *priv = cache->priv;
+
+ /* No point in trying to monitor files that are part of a
+ * GResource, since it does not support file monitoring.
+ */
+ if (g_file_has_uri_scheme (file, "resource"))
+ return;
+
+ if (g_hash_table_lookup (priv->file_monitors, file) == NULL)
+ {
+ GFileMonitor *monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
+ NULL, NULL);
+ g_signal_connect (monitor, "changed",
+ G_CALLBACK (file_changed_cb), cache);
+ g_hash_table_insert (priv->file_monitors, g_object_ref (file), monitor);
+ }
+}
+
+typedef struct {
+ GFile *gfile;
+ gint grid_width, grid_height;
+ gint paint_scale;
+ gfloat resource_scale;
+ ClutterActor *actor;
+ GCancellable *cancellable;
+ GFunc load_callback;
+ gpointer load_callback_data;
+} AsyncImageData;
+
+static void
+on_data_destroy (gpointer data)
+{
+ AsyncImageData *d = (AsyncImageData *)data;
+ g_object_unref (d->gfile);
+ g_object_unref (d->actor);
+ g_object_unref (d->cancellable);
+ g_free (d);
+}
+
+static void
+on_sliced_image_actor_destroyed (ClutterActor *actor,
+ gpointer data)
+{
+ GTask *task = data;
+ GCancellable *cancellable = g_task_get_cancellable (task);
+
+ g_cancellable_cancel (cancellable);
+}
+
+static void
+on_sliced_image_loaded (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GObject *cache = source_object;
+ AsyncImageData *data = (AsyncImageData *)user_data;
+ GTask *task = G_TASK (res);
+ GList *list, *pixbufs;
+
+ if (g_task_had_error (task) || g_cancellable_is_cancelled (data->cancellable))
+ return;
+
+ pixbufs = g_task_propagate_pointer (task, NULL);
+
+ for (list = pixbufs; list; list = list->next)
+ {
+ ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data),
+ data->paint_scale,
+ data->resource_scale);
+ clutter_actor_hide (actor);
+ clutter_actor_add_child (data->actor, actor);
+ }
+
+ g_list_free_full (pixbufs, g_object_unref);
+
+ g_signal_handlers_disconnect_by_func (data->actor,
+ on_sliced_image_actor_destroyed,
+ task);
+
+ if (data->load_callback != NULL)
+ data->load_callback (cache, data->load_callback_data);
+}
+
+static void
+free_glist_unref_gobjects (gpointer p)
+{
+ g_list_free_full (p, g_object_unref);
+}
+
+static void
+on_loader_size_prepared (GdkPixbufLoader *loader,
+ gint width,
+ gint height,
+ gpointer user_data)
+{
+ AsyncImageData *data = user_data;
+ int scale = ceilf (data->paint_scale * data->resource_scale);
+
+ gdk_pixbuf_loader_set_size (loader, width * scale, height * scale);
+}
+
+static void
+load_sliced_image (GTask *result,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ AsyncImageData *data;
+ GList *res = NULL;
+ GdkPixbuf *pix;
+ gint width, height, y, x;
+ gint scale_factor;
+ GdkPixbufLoader *loader;
+ GError *error = NULL;
+ gchar *buffer = NULL;
+ gsize length;
+
+ g_assert (cancellable);
+
+ data = task_data;
+ g_assert (data);
+
+ loader = gdk_pixbuf_loader_new ();
+ g_signal_connect (loader, "size-prepared", G_CALLBACK (on_loader_size_prepared), data);
+
+ if (!g_file_load_contents (data->gfile, cancellable, &buffer, &length, NULL, &error))
+ {
+ g_warning ("Failed to open sliced image: %s", error->message);
+ goto out;
+ }
+
+ if (!gdk_pixbuf_loader_write (loader, (const guchar *) buffer, length, &error))
+ {
+ g_warning ("Failed to load image: %s", error->message);
+ goto out;
+ }
+
+ if (!gdk_pixbuf_loader_close (loader, NULL))
+ goto out;
+
+ pix = gdk_pixbuf_loader_get_pixbuf (loader);
+ width = gdk_pixbuf_get_width (pix);
+ height = gdk_pixbuf_get_height (pix);
+ scale_factor = ceilf (data->paint_scale * data->resource_scale);
+ for (y = 0; y < height; y += data->grid_height * scale_factor)
+ {
+ for (x = 0; x < width; x += data->grid_width * scale_factor)
+ {
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y,
+ data->grid_width * scale_factor,
+ data->grid_height * scale_factor);
+ g_assert (pixbuf != NULL);
+ res = g_list_append (res, pixbuf);
+ }
+ }
+
+ out:
+ /* We don't need the original pixbuf anymore, which is owned by the loader,
+ * though the subpixbufs will hold a reference. */
+ g_object_unref (loader);
+ g_free (buffer);
+ g_clear_pointer (&error, g_error_free);
+ g_task_return_pointer (result, res, free_glist_unref_gobjects);
+}
+
+/**
+ * st_texture_cache_load_sliced_image:
+ * @cache: A #StTextureCache
+ * @file: A #GFile
+ * @grid_width: Width in pixels
+ * @grid_height: Height in pixels
+ * @paint_scale: Scale factor of the display
+ * @load_callback: (scope async) (nullable): Function called when the image is loaded, or %NULL
+ * @user_data: Data to pass to the load callback
+ *
+ * This function reads a single image file which contains multiple images internally.
+ * The image file will be divided using @grid_width and @grid_height;
+ * note that the dimensions of the image loaded from @path
+ * should be a multiple of the specified grid dimensions.
+ *
+ * Returns: (transfer none): A new #ClutterActor
+ */
+ClutterActor *
+st_texture_cache_load_sliced_image (StTextureCache *cache,
+ GFile *file,
+ gint grid_width,
+ gint grid_height,
+ gint paint_scale,
+ gfloat resource_scale,
+ GFunc load_callback,
+ gpointer user_data)
+{
+ AsyncImageData *data;
+ GTask *result;
+ ClutterActor *actor = clutter_actor_new ();
+ GCancellable *cancellable = g_cancellable_new ();
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_assert (paint_scale > 0);
+ g_assert (resource_scale > 0);
+
+ data = g_new0 (AsyncImageData, 1);
+ data->grid_width = grid_width;
+ data->grid_height = grid_height;
+ data->paint_scale = paint_scale;
+ data->resource_scale = resource_scale;
+ data->gfile = g_object_ref (file);
+ data->actor = actor;
+ data->cancellable = cancellable;
+ data->load_callback = load_callback;
+ data->load_callback_data = user_data;
+ g_object_ref (G_OBJECT (actor));
+
+ result = g_task_new (cache, cancellable, on_sliced_image_loaded, data);
+
+ g_signal_connect (actor, "destroy",
+ G_CALLBACK (on_sliced_image_actor_destroyed), result);
+
+ g_task_set_task_data (result, data, on_data_destroy);
+ g_task_run_in_thread (result, load_sliced_image);
+
+ g_object_unref (result);
+
+ return actor;
+}
+
+/**
+ * st_texture_cache_load_file_async:
+ * @cache: A #StTextureCache
+ * @file: a #GFile of the image file from which to create a pixbuf
+ * @available_width: available width for the image, can be -1 if not limited
+ * @available_height: available height for the image, can be -1 if not limited
+ * @paint_scale: scale factor of the display
+ * @resource_scale: Resource scale factor
+ *
+ * Asynchronously load an image. Initially, the returned texture will have a natural
+ * size of zero. At some later point, either the image will be loaded successfully
+ * and at that point size will be negotiated, or upon an error, no image will be set.
+ *
+ * Returns: (transfer none): A new #ClutterActor with no image loaded initially.
+ */
+ClutterActor *
+st_texture_cache_load_file_async (StTextureCache *cache,
+ GFile *file,
+ int available_width,
+ int available_height,
+ int paint_scale,
+ gfloat resource_scale)
+{
+ ClutterActor *actor;
+ AsyncTextureLoadData *request;
+ StTextureCachePolicy policy;
+ gchar *key;
+ int scale;
+
+ scale = ceilf (paint_scale * resource_scale);
+ key = g_strdup_printf (CACHE_PREFIX_FILE "%u%d", g_file_hash (file), scale);
+
+ policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */
+
+ actor = create_invisible_actor ();
+
+ if (ensure_request (cache, key, policy, &request, actor))
+ {
+ /* If there's an outstanding request, we've just added ourselves to it */
+ g_free (key);
+ }
+ else
+ {
+ /* Else, make a new request */
+
+ request->cache = cache;
+ /* Transfer ownership of key */
+ request->key = key;
+ request->file = g_object_ref (file);
+ request->policy = policy;
+ request->width = available_width;
+ request->height = available_height;
+ request->paint_scale = paint_scale;
+ request->resource_scale = resource_scale;
+
+ load_texture_async (cache, request);
+ }
+
+ ensure_monitor_for_file (cache, file);
+
+ return actor;
+}
+
+static CoglTexture *
+st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache,
+ StTextureCachePolicy policy,
+ GFile *file,
+ int available_width,
+ int available_height,
+ int paint_scale,
+ gfloat resource_scale,
+ GError **error)
+{
+ ClutterContent *image;
+ CoglTexture *texdata;
+ GdkPixbuf *pixbuf;
+ char *key;
+
+ key = g_strdup_printf (CACHE_PREFIX_FILE "%u%f", g_file_hash (file), resource_scale);
+
+ texdata = NULL;
+ image = g_hash_table_lookup (cache->priv->keyed_cache, key);
+
+ if (image == NULL)
+ {
+ pixbuf = impl_load_pixbuf_file (file, available_width, available_height,
+ paint_scale, resource_scale, error);
+ if (!pixbuf)
+ goto out;
+
+ image = pixbuf_to_st_content_image (pixbuf,
+ available_height, available_width,
+ paint_scale, resource_scale);
+ g_object_unref (pixbuf);
+
+ if (!image)
+ goto out;
+
+ if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
+ {
+ g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), image);
+ hash_table_insert_scale (cache->priv->used_scales, (double)resource_scale);
+ }
+ }
+
+ /* Because the texture is loaded synchronously, we won't call
+ * clutter_image_set_data(), so it's safe to use the texture
+ * of ClutterImage here. */
+ texdata = clutter_image_get_texture (CLUTTER_IMAGE (image));
+ cogl_object_ref (texdata);
+
+ ensure_monitor_for_file (cache, file);
+
+out:
+ g_free (key);
+ return texdata;
+}
+
+static cairo_surface_t *
+st_texture_cache_load_file_sync_to_cairo_surface (StTextureCache *cache,
+ StTextureCachePolicy policy,
+ GFile *file,
+ int available_width,
+ int available_height,
+ int paint_scale,
+ gfloat resource_scale,
+ GError **error)
+{
+ cairo_surface_t *surface;
+ GdkPixbuf *pixbuf;
+ char *key;
+
+ key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u%f", g_file_hash (file), resource_scale);
+
+ surface = g_hash_table_lookup (cache->priv->keyed_surface_cache, key);
+
+ if (surface == NULL)
+ {
+ pixbuf = impl_load_pixbuf_file (file, available_width, available_height,
+ paint_scale, resource_scale, error);
+ if (!pixbuf)
+ goto out;
+
+ surface = pixbuf_to_cairo_surface (pixbuf);
+ g_object_unref (pixbuf);
+
+ if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
+ {
+ cairo_surface_reference (surface);
+ g_hash_table_insert (cache->priv->keyed_surface_cache,
+ g_strdup (key), surface);
+ hash_table_insert_scale (cache->priv->used_scales, (double)resource_scale);
+ }
+ }
+ else
+ cairo_surface_reference (surface);
+
+ ensure_monitor_for_file (cache, file);
+
+out:
+ g_free (key);
+ return surface;
+}
+
+/**
+ * st_texture_cache_load_file_to_cogl_texture: (skip)
+ * @cache: A #StTextureCache
+ * @file: A #GFile in supported image format
+ * @paint_scale: Scale factor of the display
+ * @resource_scale: Resource scale factor
+ *
+ * This function synchronously loads the given file path
+ * into a COGL texture. On error, a warning is emitted
+ * and %NULL is returned.
+ *
+ * Returns: (transfer full): a new #CoglTexture
+ */
+CoglTexture *
+st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache,
+ GFile *file,
+ gint paint_scale,
+ gfloat resource_scale)
+{
+ CoglTexture *texture;
+ GError *error = NULL;
+
+ texture = st_texture_cache_load_file_sync_to_cogl_texture (cache, ST_TEXTURE_CACHE_POLICY_FOREVER,
+ file, -1, -1, paint_scale, resource_scale,
+ &error);
+
+ if (texture == NULL)
+ {
+ char *uri = g_file_get_uri (file);
+ g_warning ("Failed to load %s: %s", uri, error->message);
+ g_clear_error (&error);
+ g_free (uri);
+ }
+
+ return texture;
+}
+
+/**
+ * st_texture_cache_load_file_to_cairo_surface:
+ * @cache: A #StTextureCache
+ * @file: A #GFile in supported image format
+ * @paint_scale: Scale factor of the display
+ * @resource_scale: Resource scale factor
+ *
+ * This function synchronously loads the given file path
+ * into a cairo surface. On error, a warning is emitted
+ * and %NULL is returned.
+ *
+ * Returns: (transfer full): a new #cairo_surface_t
+ */
+cairo_surface_t *
+st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
+ GFile *file,
+ gint paint_scale,
+ gfloat resource_scale)
+{
+ cairo_surface_t *surface;
+ GError *error = NULL;
+
+ surface = st_texture_cache_load_file_sync_to_cairo_surface (cache, ST_TEXTURE_CACHE_POLICY_FOREVER,
+ file, -1, -1, paint_scale, resource_scale,
+ &error);
+
+ if (surface == NULL)
+ {
+ char *uri = g_file_get_uri (file);
+ g_warning ("Failed to load %s: %s", uri, error->message);
+ g_clear_error (&error);
+ g_free (uri);
+ }
+
+ return surface;
+}
+
+static StTextureCache *instance = NULL;
+
+/**
+ * st_texture_cache_get_default:
+ *
+ * Returns: (transfer none): The global texture cache
+ */
+StTextureCache*
+st_texture_cache_get_default (void)
+{
+ if (instance == NULL)
+ instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
+ return instance;
+}
+
+/**
+ * st_texture_cache_rescan_icon_theme:
+ *
+ * Rescan the current icon theme, if necessary.
+ *
+ * Returns: %TRUE if the icon theme has changed and needed to be reloaded.
+ */
+gboolean
+st_texture_cache_rescan_icon_theme (StTextureCache *cache)
+{
+ StTextureCachePrivate *priv = cache->priv;
+
+ return gtk_icon_theme_rescan_if_needed (priv->icon_theme);
+}
diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h
new file mode 100644
index 0000000..55d8495
--- /dev/null
+++ b/src/st/st-texture-cache.h
@@ -0,0 +1,120 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-texture-cache.h: Object for loading and caching images as textures
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010, Maxim Ermilov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_TEXTURE_CACHE_H__
+#define __ST_TEXTURE_CACHE_H__
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <clutter/clutter.h>
+
+#include <st/st-types.h>
+#include <st/st-theme-node.h>
+#include <st/st-widget.h>
+
+#define ST_TYPE_TEXTURE_CACHE (st_texture_cache_get_type ())
+G_DECLARE_FINAL_TYPE (StTextureCache, st_texture_cache,
+ ST, TEXTURE_CACHE, GObject)
+
+typedef struct _StTextureCachePrivate StTextureCachePrivate;
+
+struct _StTextureCache
+{
+ GObject parent;
+
+ StTextureCachePrivate *priv;
+};
+
+typedef enum {
+ ST_TEXTURE_CACHE_POLICY_NONE,
+ ST_TEXTURE_CACHE_POLICY_FOREVER
+} StTextureCachePolicy;
+
+StTextureCache* st_texture_cache_get_default (void);
+
+ClutterActor *
+st_texture_cache_load_sliced_image (StTextureCache *cache,
+ GFile *file,
+ gint grid_width,
+ gint grid_height,
+ gint paint_scale,
+ gfloat resource_scale,
+ GFunc load_callback,
+ gpointer user_data);
+
+GIcon *st_texture_cache_bind_cairo_surface_property (StTextureCache *cache,
+ GObject *object,
+ const char *property_name);
+GIcon *
+st_texture_cache_load_cairo_surface_to_gicon (StTextureCache *cache,
+ cairo_surface_t *surface);
+
+ClutterActor *st_texture_cache_load_gicon (StTextureCache *cache,
+ StThemeNode *theme_node,
+ GIcon *icon,
+ gint size,
+ gint paint_scale,
+ gfloat resource_scale);
+
+ClutterActor *st_texture_cache_load_file_async (StTextureCache *cache,
+ GFile *file,
+ int available_width,
+ int available_height,
+ int paint_scale,
+ gfloat resource_scale);
+
+CoglTexture *st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache,
+ GFile *file,
+ gint paint_scale,
+ gfloat resource_scale);
+
+cairo_surface_t *st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
+ GFile *file,
+ gint paint_scale,
+ gfloat resource_scale);
+
+/**
+ * StTextureCacheLoader: (skip)
+ * @cache: a #StTextureCache
+ * @key: Unique identifier for this texture
+ * @data: Callback user data
+ * @error: A #GError
+ *
+ * See st_texture_cache_load(). Implementations should return a
+ * texture handle for the given key, or set @error.
+ *
+ */
+typedef CoglTexture * (*StTextureCacheLoader) (StTextureCache *cache, const char *key, void *data, GError **error);
+
+CoglTexture * st_texture_cache_load (StTextureCache *cache,
+ const char *key,
+ StTextureCachePolicy policy,
+ StTextureCacheLoader load,
+ void *data,
+ GError **error);
+
+gboolean st_texture_cache_rescan_icon_theme (StTextureCache *cache);
+
+#endif /* __ST_TEXTURE_CACHE_H__ */
diff --git a/src/st/st-theme-context.c b/src/st/st-theme-context.c
new file mode 100644
index 0000000..4055786
--- /dev/null
+++ b/src/st/st-theme-context.c
@@ -0,0 +1,492 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-context.c: holds global information about a tree of styled objects
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2009 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "st-private.h"
+#include "st-settings.h"
+#include "st-texture-cache.h"
+#include "st-theme.h"
+#include "st-theme-context.h"
+#include "st-theme-node-private.h"
+
+struct _StThemeContext {
+ GObject parent;
+
+ PangoFontDescription *font;
+ StThemeNode *root_node;
+ StTheme *theme;
+
+ /* set of StThemeNode */
+ GHashTable *nodes;
+
+ gulong stylesheets_changed_id;
+
+ int scale_factor;
+};
+
+enum
+{
+ PROP_0,
+ PROP_SCALE_FACTOR,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum
+{
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT)
+
+static PangoFontDescription *get_interface_font_description (void);
+static void on_font_name_changed (StSettings *settings,
+ GParamSpec *pspec,
+ StThemeContext *context);
+static void on_icon_theme_changed (StTextureCache *cache,
+ StThemeContext *context);
+static void st_theme_context_changed (StThemeContext *context);
+
+static void st_theme_context_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void st_theme_context_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void
+st_theme_context_set_scale_factor (StThemeContext *context,
+ int scale_factor)
+{
+ if (scale_factor == context->scale_factor)
+ return;
+
+ context->scale_factor = scale_factor;
+ g_object_notify_by_pspec (G_OBJECT (context), props[PROP_SCALE_FACTOR]);
+ st_theme_context_changed (context);
+}
+
+
+static void
+st_theme_context_finalize (GObject *object)
+{
+ StThemeContext *context = ST_THEME_CONTEXT (object);
+
+ g_signal_handlers_disconnect_by_func (st_settings_get (),
+ (gpointer) on_font_name_changed,
+ context);
+ g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
+ (gpointer) on_icon_theme_changed,
+ context);
+ g_signal_handlers_disconnect_by_func (clutter_get_default_backend (),
+ (gpointer) st_theme_context_changed,
+ context);
+
+ g_clear_signal_handler (&context->stylesheets_changed_id, context->theme);
+
+ if (context->nodes)
+ g_hash_table_unref (context->nodes);
+ if (context->root_node)
+ g_object_unref (context->root_node);
+ if (context->theme)
+ g_object_unref (context->theme);
+
+ pango_font_description_free (context->font);
+
+ G_OBJECT_CLASS (st_theme_context_parent_class)->finalize (object);
+}
+
+static void
+st_theme_context_class_init (StThemeContextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = st_theme_context_set_property;
+ object_class->get_property = st_theme_context_get_property;
+ object_class->finalize = st_theme_context_finalize;
+
+ /**
+ * StThemeContext:scale-factor:
+ *
+ * The scaling factor used for HiDPI scaling.
+ */
+ props[PROP_SCALE_FACTOR] =
+ g_param_spec_int ("scale-factor",
+ "Scale factor",
+ "Integer scale factor used for HiDPI scaling",
+ 0, G_MAXINT, 1,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+
+ /**
+ * StThemeContext::changed:
+ * @self: a #StThemeContext
+ *
+ * Emitted when the icon theme, font, resolution, scale factor or the current
+ * theme's custom stylesheets change.
+ */
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, /* no default handler slot */
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_theme_context_init (StThemeContext *context)
+{
+ context->font = get_interface_font_description ();
+
+ g_signal_connect (st_settings_get (),
+ "notify::font-name",
+ G_CALLBACK (on_font_name_changed),
+ context);
+ g_signal_connect (st_texture_cache_get_default (),
+ "icon-theme-changed",
+ G_CALLBACK (on_icon_theme_changed),
+ context);
+ g_signal_connect_swapped (clutter_get_default_backend (),
+ "resolution-changed",
+ G_CALLBACK (st_theme_context_changed),
+ context);
+
+ context->nodes = g_hash_table_new_full ((GHashFunc) st_theme_node_hash,
+ (GEqualFunc) st_theme_node_equal,
+ g_object_unref, NULL);
+ context->scale_factor = 1;
+}
+
+static void
+st_theme_context_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StThemeContext *context = ST_THEME_CONTEXT (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCALE_FACTOR:
+ st_theme_context_set_scale_factor (context, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_theme_context_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StThemeContext *context = ST_THEME_CONTEXT (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCALE_FACTOR:
+ g_value_set_int (value, context->scale_factor);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * st_theme_context_new:
+ *
+ * Create a new theme context not associated with any #ClutterStage.
+ * This can be useful in testing scenarios, or if using StThemeContext
+ * with something other than #ClutterActor objects, but you generally
+ * should use st_theme_context_get_for_stage() instead.
+ *
+ * Returns: (transfer full): a new #StThemeContext
+ */
+StThemeContext *
+st_theme_context_new (void)
+{
+ StThemeContext *context;
+
+ context = g_object_new (ST_TYPE_THEME_CONTEXT, NULL);
+
+ return context;
+}
+
+static PangoFontDescription *
+get_interface_font_description (void)
+{
+ StSettings *settings = st_settings_get ();
+ g_autofree char *font_name = NULL;
+
+ g_object_get (settings, "font-name", &font_name, NULL);
+ return pango_font_description_from_string (font_name);
+}
+
+static void
+on_stage_destroy (ClutterStage *stage)
+{
+ StThemeContext *context = st_theme_context_get_for_stage (stage);
+
+ g_object_set_data (G_OBJECT (stage), "st-theme-context", NULL);
+ g_object_unref (context);
+}
+
+static void
+st_theme_context_changed (StThemeContext *context)
+{
+ StThemeNode *old_root = context->root_node;
+ context->root_node = NULL;
+ g_hash_table_remove_all (context->nodes);
+
+ g_signal_emit (context, signals[CHANGED], 0);
+
+ if (old_root)
+ g_object_unref (old_root);
+}
+
+static void
+on_font_name_changed (StSettings *settings,
+ GParamSpec *pspect,
+ StThemeContext *context)
+{
+ PangoFontDescription *font_desc = get_interface_font_description ();
+ st_theme_context_set_font (context, font_desc);
+
+ pango_font_description_free (font_desc);
+}
+
+static gboolean
+changed_idle (gpointer userdata)
+{
+ st_theme_context_changed (userdata);
+ return FALSE;
+}
+
+static void
+on_icon_theme_changed (StTextureCache *cache,
+ StThemeContext *context)
+{
+ guint id;
+
+ /* Note that an icon theme change isn't really a change of the StThemeContext;
+ * the style information has changed. But since the style factors into the
+ * icon_name => icon lookup, faking a theme context change is a good way
+ * to force users such as StIcon to look up icons again.
+ */
+ id = g_idle_add ((GSourceFunc) changed_idle, context);
+ g_source_set_name_by_id (id, "[gnome-shell] changed_idle");
+}
+
+/**
+ * st_theme_context_get_for_stage:
+ * @stage: a #ClutterStage
+ *
+ * Gets a singleton theme context associated with the stage.
+ *
+ * Returns: (transfer none): the singleton theme context for the stage
+ */
+StThemeContext *
+st_theme_context_get_for_stage (ClutterStage *stage)
+{
+ StThemeContext *context;
+
+ g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
+
+ context = g_object_get_data (G_OBJECT (stage), "st-theme-context");
+ if (context)
+ return context;
+
+ context = st_theme_context_new ();
+ g_object_set_data (G_OBJECT (stage), "st-theme-context", context);
+ g_signal_connect (stage, "destroy",
+ G_CALLBACK (on_stage_destroy), NULL);
+
+ return context;
+}
+
+/**
+ * st_theme_context_set_theme:
+ * @context: a #StThemeContext
+ * @theme: a #StTheme
+ *
+ * Sets the default set of theme stylesheets for the context. This theme will
+ * be used for the root node and for nodes descending from it, unless some other
+ * style is explicitly specified.
+ */
+void
+st_theme_context_set_theme (StThemeContext *context,
+ StTheme *theme)
+{
+ g_return_if_fail (ST_IS_THEME_CONTEXT (context));
+ g_return_if_fail (theme == NULL || ST_IS_THEME (theme));
+
+ if (context->theme != theme)
+ {
+ if (context->theme)
+ g_clear_signal_handler (&context->stylesheets_changed_id, context->theme);
+
+ g_set_object (&context->theme, theme);
+
+ if (context->theme)
+ {
+ context->stylesheets_changed_id =
+ g_signal_connect_swapped (context->theme,
+ "custom-stylesheets-changed",
+ G_CALLBACK (st_theme_context_changed),
+ context);
+ }
+
+ st_theme_context_changed (context);
+ }
+}
+
+/**
+ * st_theme_context_get_theme:
+ * @context: a #StThemeContext
+ *
+ * Gets the default theme for the context. See st_theme_context_set_theme()
+ *
+ * Returns: (transfer none): the default theme for the context
+ */
+StTheme *
+st_theme_context_get_theme (StThemeContext *context)
+{
+ g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
+
+ return context->theme;
+}
+
+/**
+ * st_theme_context_set_font:
+ * @context: a #StThemeContext
+ * @font: the default font for theme context
+ *
+ * Sets the default font for the theme context. This is the font that
+ * is inherited by the root node of the tree of theme nodes. If the
+ * font is not overridden, then this font will be used. If the font is
+ * partially modified (for example, with 'font-size: 110%'), then that
+ * modification is based on this font.
+ */
+void
+st_theme_context_set_font (StThemeContext *context,
+ const PangoFontDescription *font)
+{
+ g_return_if_fail (ST_IS_THEME_CONTEXT (context));
+ g_return_if_fail (font != NULL);
+
+ if (context->font == font ||
+ pango_font_description_equal (context->font, font))
+ return;
+
+ pango_font_description_free (context->font);
+ context->font = pango_font_description_copy (font);
+ st_theme_context_changed (context);
+}
+
+/**
+ * st_theme_context_get_font:
+ * @context: a #StThemeContext
+ *
+ * Gets the default font for the theme context. See st_theme_context_set_font().
+ *
+ * Returns: the default font for the theme context.
+ */
+const PangoFontDescription *
+st_theme_context_get_font (StThemeContext *context)
+{
+ g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
+
+ return context->font;
+}
+
+/**
+ * st_theme_context_get_root_node:
+ * @context: a #StThemeContext
+ *
+ * Gets the root node of the tree of theme style nodes that associated with this
+ * context. For the node tree associated with a stage, this node represents
+ * styles applied to the stage itself.
+ *
+ * Returns: (transfer none): the root node of the context's style tree
+ */
+StThemeNode *
+st_theme_context_get_root_node (StThemeContext *context)
+{
+ if (context->root_node == NULL)
+ context->root_node = st_theme_node_new (context, NULL, context->theme,
+ G_TYPE_NONE, NULL, NULL, NULL, NULL);
+
+ return context->root_node;
+}
+
+/**
+ * st_theme_context_intern_node:
+ * @context: a #StThemeContext
+ * @node: a #StThemeNode
+ *
+ * Return an existing node matching @node, or if that isn't possible,
+ * @node itself.
+ *
+ * Returns: (transfer none): a node with the same properties as @node
+ */
+StThemeNode *
+st_theme_context_intern_node (StThemeContext *context,
+ StThemeNode *node)
+{
+ StThemeNode *mine = g_hash_table_lookup (context->nodes, node);
+
+ /* this might be node or not - it doesn't actually matter */
+ if (mine != NULL)
+ return mine;
+
+ g_hash_table_add (context->nodes, g_object_ref (node));
+ return node;
+}
+
+/**
+ * st_theme_context_get_scale_factor:
+ * @context: a #StThemeContext
+ *
+ * Return the current scale factor of @context.
+ *
+ * Returns: an integer scale factor
+ */
+int
+st_theme_context_get_scale_factor (StThemeContext *context)
+{
+ g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), -1);
+
+ return context->scale_factor;
+}
diff --git a/src/st/st-theme-context.h b/src/st/st-theme-context.h
new file mode 100644
index 0000000..165ce25
--- /dev/null
+++ b/src/st/st-theme-context.h
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-context.c: holds global information about a tree of styled objects
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2009 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_THEME_CONTEXT_H__
+#define __ST_THEME_CONTEXT_H__
+
+#include <clutter/clutter.h>
+#include <pango/pango.h>
+#include "st-theme-node.h"
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:st-theme-context
+ * @short_description: holds global information about a tree of styled objects
+ *
+ * #StThemeContext is responsible for managing information global to a tree of styled objects,
+ * such as the set of stylesheets or the default font. In normal usage, a #StThemeContext
+ * is bound to a #ClutterStage; a singleton #StThemeContext can be obtained for a #ClutterStage
+ * by using st_theme_context_get_for_stage().
+ */
+
+#define ST_TYPE_THEME_CONTEXT (st_theme_context_get_type ())
+G_DECLARE_FINAL_TYPE (StThemeContext, st_theme_context,
+ ST, THEME_CONTEXT, GObject)
+
+StThemeContext *st_theme_context_new (void);
+StThemeContext *st_theme_context_get_for_stage (ClutterStage *stage);
+
+void st_theme_context_set_theme (StThemeContext *context,
+ StTheme *theme);
+StTheme * st_theme_context_get_theme (StThemeContext *context);
+
+void st_theme_context_set_font (StThemeContext *context,
+ const PangoFontDescription *font);
+const PangoFontDescription *st_theme_context_get_font (StThemeContext *context);
+
+StThemeNode * st_theme_context_get_root_node (StThemeContext *context);
+
+StThemeNode * st_theme_context_intern_node (StThemeContext *context,
+ StThemeNode *node);
+
+int st_theme_context_get_scale_factor (StThemeContext *context);
+
+G_END_DECLS
+
+#endif /* __ST_THEME_CONTEXT_H__ */
diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c
new file mode 100644
index 0000000..72745ed
--- /dev/null
+++ b/src/st/st-theme-node-drawing.c
@@ -0,0 +1,2864 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-node-drawing.c: Code to draw themed elements
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2009, 2010 Florian Müllner
+ * Copyright 2010 Intel Corporation.
+ * Copyright 2011 Quentin "Sardem FF7" Glidic
+ *
+ * Contains code derived from:
+ * rectangle.c: Rounded rectangle.
+ * Copyright 2008 litl, LLC.
+ * st-texture-frame.h: Expandible texture actor
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "st-shadow.h"
+#include "st-private.h"
+#include "st-theme-private.h"
+#include "st-theme-context.h"
+#include "st-texture-cache.h"
+#include "st-theme-node-private.h"
+
+/****
+ * Rounded corners
+ ****/
+
+typedef struct {
+ ClutterColor color;
+ ClutterColor border_color_1;
+ ClutterColor border_color_2;
+ guint radius;
+ guint border_width_1;
+ guint border_width_2;
+ float resource_scale;
+} StCornerSpec;
+
+typedef enum {
+ ST_PAINT_BORDERS_MODE_COLOR,
+ ST_PAINT_BORDERS_MODE_SILHOUETTE
+} StPaintBordersMode;
+
+static void
+elliptical_arc (cairo_t *cr,
+ double x_center,
+ double y_center,
+ double x_radius,
+ double y_radius,
+ double angle1,
+ double angle2)
+{
+ cairo_save (cr);
+ cairo_translate (cr, x_center, y_center);
+ cairo_scale (cr, x_radius, y_radius);
+ cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
+ cairo_restore (cr);
+}
+
+static CoglTexture *
+create_corner_material (StCornerSpec *corner)
+{
+ ClutterBackend *backend = clutter_get_default_backend ();
+ CoglContext *ctx = clutter_backend_get_cogl_context (backend);
+ GError *error = NULL;
+ CoglTexture *texture;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ guint rowstride;
+ guint8 *data;
+ guint size;
+ guint logical_size;
+ guint max_border_width;
+ double device_scaling;
+
+ max_border_width = MAX(corner->border_width_2, corner->border_width_1);
+ logical_size = 2 * MAX(max_border_width, corner->radius);
+ size = ceilf (logical_size * corner->resource_scale);
+ rowstride = size * 4;
+ data = g_new0 (guint8, size * rowstride);
+
+ surface = cairo_image_surface_create_for_data (data,
+ CAIRO_FORMAT_ARGB32,
+ size, size,
+ rowstride);
+ device_scaling = (double) size / logical_size;
+ cairo_surface_set_device_scale (surface, device_scaling, device_scaling);
+ cr = cairo_create (surface);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_scale (cr, logical_size, logical_size);
+
+ if (max_border_width <= corner->radius)
+ {
+ double x_radius, y_radius;
+
+ if (max_border_width != 0)
+ {
+ cairo_set_source_rgba (cr,
+ corner->border_color_1.red / 255.,
+ corner->border_color_1.green / 255.,
+ corner->border_color_1.blue / 255.,
+ corner->border_color_1.alpha / 255.);
+
+ cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI);
+ cairo_fill (cr);
+ }
+
+ cairo_set_source_rgba (cr,
+ corner->color.red / 255.,
+ corner->color.green / 255.,
+ corner->color.blue / 255.,
+ corner->color.alpha / 255.);
+
+ x_radius = 0.5 * (1.0 - (double) corner->border_width_2 / corner->radius);
+ y_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
+
+ /* TOPRIGHT */
+ elliptical_arc (cr,
+ 0.5, 0.5,
+ x_radius, y_radius,
+ 3 * M_PI / 2, 2 * M_PI);
+
+ /* BOTTOMRIGHT */
+ elliptical_arc (cr,
+ 0.5, 0.5,
+ x_radius, y_radius,
+ 0, M_PI / 2);
+
+ /* TOPLEFT */
+ elliptical_arc (cr,
+ 0.5, 0.5,
+ x_radius, y_radius,
+ M_PI, 3 * M_PI / 2);
+
+ /* BOTTOMLEFT */
+ elliptical_arc (cr,
+ 0.5, 0.5,
+ x_radius, y_radius,
+ M_PI / 2, M_PI);
+
+ cairo_fill (cr);
+ }
+ else
+ {
+ double radius;
+
+ radius = (gdouble)corner->radius / max_border_width;
+
+ cairo_set_source_rgba (cr,
+ corner->border_color_1.red / 255.,
+ corner->border_color_1.green / 255.,
+ corner->border_color_1.blue / 255.,
+ corner->border_color_1.alpha / 255.);
+
+ cairo_arc (cr, radius, radius, radius, M_PI, 3 * M_PI / 2);
+ cairo_line_to (cr, 1.0 - radius, 0.0);
+ cairo_arc (cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2 * M_PI);
+ cairo_line_to (cr, 1.0, 1.0 - radius);
+ cairo_arc (cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2);
+ cairo_line_to (cr, radius, 1.0);
+ cairo_arc (cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI);
+ cairo_fill (cr);
+ }
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (surface);
+
+ texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, size, size,
+ CLUTTER_CAIRO_FORMAT_ARGB32,
+ rowstride,
+ data,
+ &error));
+
+ if (error)
+ {
+ g_warning ("Failed to allocate texture: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_free (data);
+
+ return texture;
+}
+
+static char *
+corner_to_string (StCornerSpec *corner)
+{
+ return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u,%.4f",
+ corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha,
+ corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha,
+ corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha,
+ corner->radius,
+ corner->border_width_1,
+ corner->border_width_2,
+ corner->resource_scale);
+}
+
+static CoglTexture *
+load_corner (StTextureCache *cache,
+ const char *key,
+ void *datap,
+ GError **error)
+{
+ return create_corner_material ((StCornerSpec *) datap);
+}
+
+/* To match the CSS specification, we want the border to look like it was
+ * drawn over the background. But actually drawing the border over the
+ * background will produce slightly bad antialiasing at the edges, so
+ * compute the effective border color instead.
+ */
+#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
+#define MULT(c,a) NORM(c*a)
+
+static void
+premultiply (ClutterColor *color)
+{
+ guint t;
+ color->red = MULT (color->red, color->alpha);
+ color->green = MULT (color->green, color->alpha);
+ color->blue = MULT (color->blue, color->alpha);
+}
+
+static void
+unpremultiply (ClutterColor *color)
+{
+ if (color->alpha != 0)
+ {
+ color->red = MIN((color->red * 255 + 127) / color->alpha, 255);
+ color->green = MIN((color->green * 255 + 127) / color->alpha, 255);
+ color->blue = MIN((color->blue * 255 + 127) / color->alpha, 255);
+ }
+}
+
+static void
+over (const ClutterColor *source,
+ const ClutterColor *destination,
+ ClutterColor *result)
+{
+ guint t;
+ ClutterColor src = *source;
+ ClutterColor dst = *destination;
+
+ premultiply (&src);
+ premultiply (&dst);
+
+ result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
+ result->red = src.red + NORM ((255 - src.alpha) * dst.red);
+ result->green = src.green + NORM ((255 - src.alpha) * dst.green);
+ result->blue = src.blue + NORM ((255 - src.alpha) * dst.blue);
+
+ unpremultiply (result);
+}
+
+/*
+ * st_theme_node_reduce_border_radius:
+ * @node: a #StThemeNode
+ * @width: The width of the box
+ * @height: The height of the box
+ * @corners: (array length=4) (out): reduced corners
+ *
+ * Implements the corner overlap algorithm mentioned at
+ * http://www.w3.org/TR/css3-background/#corner-overlap
+ */
+static void
+st_theme_node_reduce_border_radius (StThemeNode *node,
+ float width,
+ float height,
+ guint *corners)
+{
+ gfloat scale;
+ guint sum;
+
+ scale = 1.0;
+
+ /* top */
+ sum = node->border_radius[ST_CORNER_TOPLEFT]
+ + node->border_radius[ST_CORNER_TOPRIGHT];
+
+ if (sum > 0)
+ scale = MIN (width / sum, scale);
+
+ /* right */
+ sum = node->border_radius[ST_CORNER_TOPRIGHT]
+ + node->border_radius[ST_CORNER_BOTTOMRIGHT];
+
+ if (sum > 0)
+ scale = MIN (height / sum, scale);
+
+ /* bottom */
+ sum = node->border_radius[ST_CORNER_BOTTOMLEFT]
+ + node->border_radius[ST_CORNER_BOTTOMRIGHT];
+
+ if (sum > 0)
+ scale = MIN (width / sum, scale);
+
+ /* left */
+ sum = node->border_radius[ST_CORNER_BOTTOMLEFT]
+ + node->border_radius[ST_CORNER_TOPLEFT];
+
+ if (sum > 0)
+ scale = MIN (height / sum, scale);
+
+ corners[ST_CORNER_TOPLEFT] = node->border_radius[ST_CORNER_TOPLEFT] * scale;
+ corners[ST_CORNER_TOPRIGHT] = node->border_radius[ST_CORNER_TOPRIGHT] * scale;
+ corners[ST_CORNER_BOTTOMLEFT] = node->border_radius[ST_CORNER_BOTTOMLEFT] * scale;
+ corners[ST_CORNER_BOTTOMRIGHT] = node->border_radius[ST_CORNER_BOTTOMRIGHT] * scale;
+}
+
+static void
+st_theme_node_get_corner_border_widths (StThemeNode *node,
+ StCorner corner_id,
+ guint *border_width_1,
+ guint *border_width_2)
+{
+ switch (corner_id)
+ {
+ case ST_CORNER_TOPLEFT:
+ if (border_width_1)
+ *border_width_1 = node->border_width[ST_SIDE_TOP];
+ if (border_width_2)
+ *border_width_2 = node->border_width[ST_SIDE_LEFT];
+ break;
+ case ST_CORNER_TOPRIGHT:
+ if (border_width_1)
+ *border_width_1 = node->border_width[ST_SIDE_TOP];
+ if (border_width_2)
+ *border_width_2 = node->border_width[ST_SIDE_RIGHT];
+ break;
+ case ST_CORNER_BOTTOMRIGHT:
+ if (border_width_1)
+ *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+ if (border_width_2)
+ *border_width_2 = node->border_width[ST_SIDE_RIGHT];
+ break;
+ case ST_CORNER_BOTTOMLEFT:
+ if (border_width_1)
+ *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+ if (border_width_2)
+ *border_width_2 = node->border_width[ST_SIDE_LEFT];
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+static CoglPipeline *
+st_theme_node_lookup_corner (StThemeNode *node,
+ float width,
+ float height,
+ float resource_scale,
+ StCorner corner_id)
+{
+ CoglTexture *texture = NULL;
+ CoglPipeline *material = NULL;
+ char *key;
+ StTextureCache *cache;
+ StCornerSpec corner;
+ guint radius[4];
+
+ cache = st_texture_cache_get_default ();
+
+ st_theme_node_reduce_border_radius (node, width, height, radius);
+
+ if (radius[corner_id] == 0)
+ return NULL;
+
+ corner.radius = radius[corner_id];
+ corner.color = node->background_color;
+ corner.resource_scale = resource_scale;
+ st_theme_node_get_corner_border_widths (node, corner_id,
+ &corner.border_width_1,
+ &corner.border_width_2);
+
+ switch (corner_id)
+ {
+ case ST_CORNER_TOPLEFT:
+ over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
+ over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
+ break;
+ case ST_CORNER_TOPRIGHT:
+ over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
+ over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
+ break;
+ case ST_CORNER_BOTTOMRIGHT:
+ over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
+ over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
+ break;
+ case ST_CORNER_BOTTOMLEFT:
+ over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
+ over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ if (corner.color.alpha == 0 &&
+ corner.border_color_1.alpha == 0 &&
+ corner.border_color_2.alpha == 0)
+ {
+ if (node->box_shadow == NULL)
+ return NULL;
+ else /* We still need a corner texture to render the box-shadow */
+ corner.color = (ClutterColor) {0, 0, 0, 255};
+ }
+
+ key = corner_to_string (&corner);
+ texture = st_texture_cache_load (cache, key, ST_TEXTURE_CACHE_POLICY_FOREVER, load_corner, &corner, NULL);
+
+ if (texture)
+ {
+ material = _st_create_texture_pipeline (texture);
+ cogl_object_unref (texture);
+ }
+
+ g_free (key);
+
+ return material;
+}
+
+static void
+get_background_scale (StThemeNode *node,
+ gdouble painting_area_width,
+ gdouble painting_area_height,
+ gdouble background_image_width,
+ gdouble background_image_height,
+ gdouble *scale_w,
+ gdouble *scale_h)
+{
+ *scale_w = -1.0;
+ *scale_h = -1.0;
+
+ switch (node->background_size)
+ {
+ case ST_BACKGROUND_SIZE_AUTO:
+ *scale_w = 1.0f;
+ break;
+ case ST_BACKGROUND_SIZE_CONTAIN:
+ *scale_w = MIN (painting_area_width / background_image_width,
+ painting_area_height / background_image_height);
+ break;
+ case ST_BACKGROUND_SIZE_COVER:
+ *scale_w = MAX (painting_area_width / background_image_width,
+ painting_area_height / background_image_height);
+ break;
+ case ST_BACKGROUND_SIZE_FIXED:
+ if (node->background_size_w > -1)
+ {
+ *scale_w = node->background_size_w / background_image_width;
+ if (node->background_size_h > -1)
+ *scale_h = node->background_size_h / background_image_height;
+ }
+ else if (node->background_size_h > -1)
+ *scale_w = node->background_size_h / background_image_height;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ if (*scale_h < 0.0)
+ *scale_h = *scale_w;
+}
+
+static void
+get_background_coordinates (StThemeNode *node,
+ gdouble painting_area_width,
+ gdouble painting_area_height,
+ gdouble background_image_width,
+ gdouble background_image_height,
+ gdouble *x,
+ gdouble *y)
+{
+ /* honor the specified position if any */
+ if (node->background_position_set)
+ {
+ *x = node->background_position_x;
+ *y = node->background_position_y;
+ }
+ else
+ {
+ /* center the background on the widget */
+ *x = (painting_area_width / 2.0) - (background_image_width / 2.0);
+ *y = (painting_area_height / 2.0) - (background_image_height / 2.0);
+ }
+}
+
+static void
+get_background_position (StThemeNode *self,
+ const ClutterActorBox *allocation,
+ float resource_scale,
+ ClutterActorBox *result,
+ ClutterActorBox *texture_coords)
+{
+ gdouble painting_area_width, painting_area_height;
+ gdouble background_image_width, background_image_height;
+ gdouble x1, y1;
+ gdouble scale_w, scale_h;
+
+ /* get the background image size */
+ background_image_width = cogl_texture_get_width (self->background_texture);
+ background_image_height = cogl_texture_get_height (self->background_texture);
+
+ background_image_width /= resource_scale;
+ background_image_height /= resource_scale;
+
+ /* get the painting area size */
+ painting_area_width = allocation->x2 - allocation->x1;
+ painting_area_height = allocation->y2 - allocation->y1;
+
+ /* scale if requested */
+ get_background_scale (self,
+ painting_area_width, painting_area_height,
+ background_image_width, background_image_height,
+ &scale_w, &scale_h);
+
+ background_image_width *= scale_w;
+ background_image_height *= scale_h;
+
+ /* get coordinates */
+ get_background_coordinates (self,
+ painting_area_width, painting_area_height,
+ background_image_width, background_image_height,
+ &x1, &y1);
+
+ if (self->background_repeat)
+ {
+ gdouble width = allocation->x2 - allocation->x1 + x1;
+ gdouble height = allocation->y2 - allocation->y1 + y1;
+
+ *result = *allocation;
+
+ /* reference image is at x1, y1 */
+ texture_coords->x1 = x1 / background_image_width;
+ texture_coords->y1 = y1 / background_image_height;
+ texture_coords->x2 = width / background_image_width;
+ texture_coords->y2 = height / background_image_height;
+ }
+ else
+ {
+ result->x1 = x1;
+ result->y1 = y1;
+ result->x2 = x1 + background_image_width;
+ result->y2 = y1 + background_image_height;
+
+ texture_coords->x1 = texture_coords->y1 = 0;
+ texture_coords->x2 = texture_coords->y2 = 1;
+ }
+}
+
+/* Use of this function marks code which doesn't support
+ * non-uniform colors.
+ */
+static void
+get_arbitrary_border_color (StThemeNode *node,
+ ClutterColor *color)
+{
+ if (color)
+ st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
+}
+
+static gboolean
+st_theme_node_has_visible_outline (StThemeNode *node)
+{
+ if (node->background_color.alpha > 0)
+ return TRUE;
+
+ if (node->background_gradient_end.alpha > 0)
+ return TRUE;
+
+ if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
+ node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
+ node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
+ node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
+ return TRUE;
+
+ if (node->border_width[ST_SIDE_TOP] > 0 ||
+ node->border_width[ST_SIDE_LEFT] > 0 ||
+ node->border_width[ST_SIDE_RIGHT] > 0 ||
+ node->border_width[ST_SIDE_BOTTOM] > 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static cairo_pattern_t *
+create_cairo_pattern_of_background_gradient (StThemeNode *node,
+ float width,
+ float height)
+{
+ cairo_pattern_t *pattern;
+
+ g_return_val_if_fail (node->background_gradient_type != ST_GRADIENT_NONE,
+ NULL);
+
+ if (node->background_gradient_type == ST_GRADIENT_VERTICAL)
+ pattern = cairo_pattern_create_linear (0, 0, 0, height);
+ else if (node->background_gradient_type == ST_GRADIENT_HORIZONTAL)
+ pattern = cairo_pattern_create_linear (0, 0, width, 0);
+ else
+ {
+ gdouble cx, cy;
+
+ cx = width / 2.;
+ cy = height / 2.;
+ pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy));
+ }
+
+ cairo_pattern_add_color_stop_rgba (pattern, 0,
+ node->background_color.red / 255.,
+ node->background_color.green / 255.,
+ node->background_color.blue / 255.,
+ node->background_color.alpha / 255.);
+ cairo_pattern_add_color_stop_rgba (pattern, 1,
+ node->background_gradient_end.red / 255.,
+ node->background_gradient_end.green / 255.,
+ node->background_gradient_end.blue / 255.,
+ node->background_gradient_end.alpha / 255.);
+ return pattern;
+}
+
+static cairo_pattern_t *
+create_cairo_pattern_of_background_image (StThemeNode *node,
+ float width,
+ float height,
+ float resource_scale,
+ gboolean *needs_background_fill)
+{
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+ cairo_content_t content;
+ cairo_matrix_t matrix;
+ GFile *file;
+
+ StTextureCache *texture_cache;
+
+ gdouble background_image_width, background_image_height;
+ gdouble x, y;
+ gdouble scale_w, scale_h;
+
+ file = st_theme_node_get_background_image (node);
+
+ texture_cache = st_texture_cache_get_default ();
+
+ surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file,
+ node->cached_scale_factor,
+ resource_scale);
+
+ if (surface == NULL)
+ return NULL;
+
+ g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+ content = cairo_surface_get_content (surface);
+ pattern = cairo_pattern_create_for_surface (surface);
+
+ background_image_width = cairo_image_surface_get_width (surface);
+ background_image_height = cairo_image_surface_get_height (surface);
+
+ *needs_background_fill = TRUE;
+
+ cairo_matrix_init_identity (&matrix);
+
+ if (resource_scale != 1.0)
+ {
+ background_image_width /= resource_scale;
+ background_image_height /= resource_scale;
+
+ cairo_matrix_scale (&matrix, resource_scale, resource_scale);
+ }
+
+ get_background_scale (node,
+ width, height,
+ background_image_width, background_image_height,
+ &scale_w, &scale_h);
+
+ if ((scale_w != 1) || (scale_h != 1))
+ cairo_matrix_scale (&matrix, 1.0/scale_w, 1.0/scale_h);
+
+ background_image_width *= scale_w;
+ background_image_height *= scale_h;
+
+ get_background_coordinates (node,
+ width, height,
+ background_image_width, background_image_height,
+ &x, &y);
+ cairo_matrix_translate (&matrix, -x, -y);
+
+ if (node->background_repeat)
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+
+ /* If it's opaque, fills up the entire allocated
+ * area, then don't bother doing a background fill first
+ */
+ if (content != CAIRO_CONTENT_COLOR_ALPHA)
+ {
+ if (node->background_repeat ||
+ (x >= 0 &&
+ y >= 0 &&
+ background_image_width - x >= width &&
+ background_image_height -y >= height))
+ *needs_background_fill = FALSE;
+ }
+
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ return pattern;
+}
+
+/* fill_exterior = TRUE means that pattern is a surface pattern and
+ * we should extend the pattern with a solid fill from its edges.
+ * This is a bit of a hack; the alternative would be to make the
+ * surface of the surface pattern 1 pixel bigger and use CAIRO_EXTEND_PAD.
+ */
+static void
+paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec,
+ cairo_pattern_t *pattern,
+ gboolean fill_exterior,
+ cairo_t *cr,
+ cairo_path_t *interior_path,
+ cairo_path_t *outline_path)
+{
+ /* If there are borders, clip the shadow to the interior
+ * of the borders; if there is a visible outline, clip the shadow to
+ * that outline
+ */
+ cairo_path_t *path = (interior_path != NULL) ? interior_path : outline_path;
+ double x1, x2, y1, y2;
+
+ /* fill_exterior only makes sense if we're clipping the shadow - filling
+ * to the edges of the surface would be silly */
+ g_assert (!(fill_exterior && path == NULL));
+
+ cairo_save (cr);
+ if (path != NULL)
+ {
+ cairo_append_path (cr, path);
+
+ /* There's no way to invert a path in cairo, so we need bounds for
+ * the area we are drawing in order to create the "exterior" region.
+ * Pixel align to hit fast paths.
+ */
+ if (fill_exterior)
+ {
+ cairo_path_extents (cr, &x1, &y1, &x2, &y2);
+ x1 = floor (x1);
+ y1 = floor (y1);
+ x2 = ceil (x2);
+ y2 = ceil (y2);
+ }
+
+ cairo_clip (cr);
+ }
+
+ cairo_set_source_rgba (cr,
+ shadow_spec->color.red / 255.0,
+ shadow_spec->color.green / 255.0,
+ shadow_spec->color.blue / 255.0,
+ shadow_spec->color.alpha / 255.0);
+ if (fill_exterior)
+ {
+ cairo_surface_t *surface;
+ int width, height;
+ double xscale, yscale;
+ cairo_matrix_t matrix;
+
+ cairo_save (cr);
+
+ /* Start with a rectangle enclosing the bounds of the clipped
+ * region */
+ cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
+
+ /* Then subtract out the bounds of the surface in the surface
+ * pattern; we transform the context by the inverse of the
+ * pattern matrix to get to surface coordinates */
+
+ if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
+ /* Something went wrong previously */
+ goto no_surface;
+
+ cairo_surface_get_device_scale (surface, &xscale, &yscale);
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+
+ cairo_pattern_get_matrix (pattern, &matrix);
+ cairo_matrix_invert (&matrix);
+ cairo_matrix_scale (&matrix, 1.0 / xscale, 1.0 / yscale);
+ cairo_transform (cr, &matrix);
+
+ cairo_rectangle (cr, 0, height, width, - height);
+ cairo_fill (cr);
+
+ no_surface:
+ cairo_restore (cr);
+ }
+
+ cairo_mask (cr, pattern);
+ cairo_restore (cr);
+}
+
+static void
+paint_background_image_shadow_to_cairo_context (StThemeNode *node,
+ StShadow *shadow_spec,
+ cairo_pattern_t *pattern,
+ cairo_t *cr,
+ cairo_path_t *interior_path,
+ cairo_path_t *outline_path,
+ int x,
+ int y,
+ int width,
+ int height,
+ float resource_scale)
+{
+ cairo_pattern_t *shadow_pattern;
+
+ g_assert (shadow_spec != NULL);
+ g_assert (pattern != NULL);
+
+ if (outline_path != NULL)
+ {
+ cairo_surface_t *clipped_surface;
+ cairo_pattern_t *clipped_pattern;
+ cairo_t *temp_cr;
+
+ /* Prerender the pattern to a temporary surface,
+ * so it's properly clipped before we create a shadow from it
+ */
+ width = ceilf (width * resource_scale);
+ height = ceilf (height * resource_scale);
+ clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cairo_surface_set_device_scale (clipped_surface, resource_scale, resource_scale);
+ temp_cr = cairo_create (clipped_surface);
+
+ cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (temp_cr);
+ cairo_set_operator (temp_cr, CAIRO_OPERATOR_SOURCE);
+
+ if (interior_path != NULL)
+ {
+ cairo_append_path (temp_cr, interior_path);
+ cairo_clip (temp_cr);
+ }
+
+ cairo_append_path (temp_cr, outline_path);
+ cairo_translate (temp_cr, x, y);
+ cairo_set_source (temp_cr, pattern);
+ cairo_clip (temp_cr);
+ cairo_paint (temp_cr);
+ cairo_destroy (temp_cr);
+
+ clipped_pattern = cairo_pattern_create_for_surface (clipped_surface);
+ cairo_surface_destroy (clipped_surface);
+
+ shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
+ clipped_pattern);
+ cairo_pattern_destroy (clipped_pattern);
+ }
+ else
+ {
+ shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
+ pattern);
+ }
+
+ paint_shadow_pattern_to_cairo_context (shadow_spec,
+ shadow_pattern, FALSE,
+ cr,
+ interior_path,
+ outline_path);
+ cairo_pattern_destroy (shadow_pattern);
+}
+
+/* gets the extents of a cairo_path_t; slightly inefficient, but much simpler than
+ * computing from the raw path data */
+static void
+path_extents (cairo_path_t *path,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2)
+
+{
+ cairo_surface_t *dummy = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1);
+ cairo_t *cr = cairo_create (dummy);
+
+ cairo_append_path (cr, path);
+ cairo_path_extents (cr, x1, y1, x2, y2);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (dummy);
+}
+
+static void
+paint_inset_box_shadow_to_cairo_context (StThemeNode *node,
+ StShadow *shadow_spec,
+ float resource_scale,
+ cairo_t *cr,
+ cairo_path_t *shadow_outline)
+{
+ cairo_surface_t *shadow_surface;
+ cairo_pattern_t *shadow_pattern;
+ double extents_x1, extents_y1, extents_x2, extents_y2;
+ double shrunk_extents_x1, shrunk_extents_y1, shrunk_extents_x2, shrunk_extents_y2;
+ gboolean fill_exterior;
+
+ g_assert (shadow_spec != NULL);
+ g_assert (shadow_outline != NULL);
+
+ /* Create the pattern used to create the inset shadow; as the shadow
+ * should be drawn as if everything outside the outline was opaque,
+ * we use a temporary surface to draw the background as a solid shape,
+ * which is inverted when creating the shadow pattern.
+ */
+
+ /* First we need to find the size of the temporary surface
+ */
+ path_extents (shadow_outline,
+ &extents_x1, &extents_y1, &extents_x2, &extents_y2);
+
+ /* Shrink the extents by the spread, and offset */
+ shrunk_extents_x1 = extents_x1 + shadow_spec->xoffset + shadow_spec->spread;
+ shrunk_extents_y1 = extents_y1 + shadow_spec->yoffset + shadow_spec->spread;
+ shrunk_extents_x2 = extents_x2 + shadow_spec->xoffset - shadow_spec->spread;
+ shrunk_extents_y2 = extents_y2 + shadow_spec->yoffset - shadow_spec->spread;
+
+ if (shrunk_extents_x1 >= shrunk_extents_x2 || shrunk_extents_y1 >= shrunk_extents_y2)
+ {
+ /* Shadow occupies entire area within border */
+ shadow_pattern = cairo_pattern_create_rgb (0., 0., 0.);
+ fill_exterior = FALSE;
+ }
+ else
+ {
+ /* Bounds of temporary surface */
+ int surface_x = floor (shrunk_extents_x1);
+ int surface_y = floor (shrunk_extents_y1);
+ int surface_width = ceil ((shrunk_extents_x2 - surface_x) * resource_scale);
+ int surface_height = ceil ((shrunk_extents_y2 - surface_y) * resource_scale);
+
+ /* Center of the original path */
+ double x_center = (extents_x1 + extents_x2) / 2;
+ double y_center = (extents_y1 + extents_y2) / 2;
+
+ cairo_pattern_t *pattern;
+ cairo_t *temp_cr;
+ cairo_matrix_t matrix;
+
+ shadow_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, surface_width, surface_height);
+ cairo_surface_set_device_scale (shadow_surface, resource_scale, resource_scale);
+ temp_cr = cairo_create (shadow_surface);
+
+ /* Match the coordinates in the temporary context to the parent context */
+ cairo_translate (temp_cr, - surface_x, - surface_y);
+
+ /* Shadow offset */
+ cairo_translate (temp_cr, shadow_spec->xoffset, shadow_spec->yoffset);
+
+ /* Scale the path around the center to match the shrunk bounds */
+ cairo_translate (temp_cr, x_center, y_center);
+ cairo_scale (temp_cr,
+ (shrunk_extents_x2 - shrunk_extents_x1) / (extents_x2 - extents_x1),
+ (shrunk_extents_y2 - shrunk_extents_y1) / (extents_y2 - extents_y1));
+ cairo_translate (temp_cr, - x_center, - y_center);
+
+ cairo_append_path (temp_cr, shadow_outline);
+ cairo_fill (temp_cr);
+ cairo_destroy (temp_cr);
+
+ pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_surface_destroy (shadow_surface);
+
+ /* The pattern needs to be offset back to coordinates in the parent context */
+ cairo_matrix_init_translate (&matrix, - surface_x, - surface_y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec, pattern);
+ fill_exterior = TRUE;
+
+ cairo_pattern_destroy (pattern);
+ }
+
+ paint_shadow_pattern_to_cairo_context (shadow_spec,
+ shadow_pattern, fill_exterior,
+ cr,
+ shadow_outline,
+ NULL);
+
+ cairo_pattern_destroy (shadow_pattern);
+}
+
+/* In order for borders to be smoothly blended with non-solid backgrounds,
+ * we need to use cairo. This function is a slow fallback path for those
+ * cases (gradients, background images, etc).
+ */
+static CoglTexture *
+st_theme_node_prerender_background (StThemeNode *node,
+ float actor_width,
+ float actor_height,
+ float resource_scale)
+{
+ ClutterBackend *backend = clutter_get_default_backend ();
+ CoglContext *ctx = clutter_backend_get_cogl_context (backend);
+ GError *error = NULL;
+ StBorderImage *border_image;
+ CoglTexture *texture;
+ guint radius[4];
+ int i;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ StShadow *shadow_spec;
+ StShadow *box_shadow_spec;
+ cairo_pattern_t *pattern = NULL;
+ cairo_path_t *outline_path = NULL;
+ gboolean draw_solid_background = TRUE;
+ gboolean background_is_translucent;
+ gboolean interior_dirty;
+ gboolean draw_background_image_shadow = FALSE;
+ gboolean has_visible_outline;
+ ClutterColor border_color;
+ guint border_width[4];
+ guint rowstride;
+ guchar *data;
+ ClutterActorBox actor_box;
+ ClutterActorBox paint_box;
+ cairo_path_t *interior_path = NULL;
+ float width, height;
+ int texture_width;
+ int texture_height;
+
+ border_image = st_theme_node_get_border_image (node);
+
+ shadow_spec = st_theme_node_get_background_image_shadow (node);
+ box_shadow_spec = st_theme_node_get_box_shadow (node);
+
+ actor_box.x1 = 0;
+ actor_box.x2 = actor_width;
+ actor_box.y1 = 0;
+ actor_box.y2 = actor_height;
+
+ /* If there's a background image shadow, we
+ * may need to create an image bigger than the nodes
+ * allocation
+ */
+ st_theme_node_get_background_paint_box (node, &actor_box, &paint_box);
+
+ /* translate the boxes so the paint box is at 0,0
+ */
+ actor_box.x1 += - paint_box.x1;
+ actor_box.x2 += - paint_box.x1;
+ actor_box.y1 += - paint_box.y1;
+ actor_box.y2 += - paint_box.y1;
+
+ width = paint_box.x2 - paint_box.x1;
+ height = paint_box.y2 - paint_box.y1;
+
+ texture_width = ceilf (width * resource_scale);
+ texture_height = ceilf (height * resource_scale);
+
+ rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, texture_width);
+ data = g_new0 (guchar, texture_height * rowstride);
+
+ /* We zero initialize the destination memory, so it's fully transparent
+ * by default.
+ */
+ interior_dirty = FALSE;
+
+ surface = cairo_image_surface_create_for_data (data,
+ CAIRO_FORMAT_ARGB32,
+ texture_width, texture_height,
+ rowstride);
+ cairo_surface_set_device_scale (surface, resource_scale, resource_scale);
+ cr = cairo_create (surface);
+
+ /* TODO - support non-uniform border colors */
+ get_arbitrary_border_color (node, &border_color);
+
+ st_theme_node_reduce_border_radius (node, width, height, radius);
+
+ for (i = 0; i < 4; i++)
+ border_width[i] = st_theme_node_get_border_width (node, i);
+
+ /* Note we don't support translucent background images on top
+ * of gradients. It's strictly either/or.
+ */
+ if (node->background_gradient_type != ST_GRADIENT_NONE)
+ {
+ pattern = create_cairo_pattern_of_background_gradient (node, width, height);
+ draw_solid_background = FALSE;
+
+ /* If the gradient has any translucent areas, we need to
+ * erase the interior region before drawing, so that we show
+ * what's actually under the gradient and not whatever is
+ * left over from filling the border, etc.
+ */
+ if (node->background_color.alpha < 255 ||
+ node->background_gradient_end.alpha < 255)
+ background_is_translucent = TRUE;
+ else
+ background_is_translucent = FALSE;
+ }
+ else
+ {
+ GFile *background_image;
+
+ background_image = st_theme_node_get_background_image (node);
+
+ if (background_image != NULL)
+ {
+ pattern = create_cairo_pattern_of_background_image (node,
+ width, height,
+ resource_scale,
+ &draw_solid_background);
+ if (shadow_spec && pattern != NULL)
+ draw_background_image_shadow = TRUE;
+ }
+
+ /* We never need to clear the interior region before drawing the
+ * background image, because it either always fills the entire area
+ * opaquely, or we draw the solid background behind it.
+ */
+ background_is_translucent = FALSE;
+ }
+
+ if (pattern == NULL)
+ draw_solid_background = TRUE;
+
+ /* drawing the solid background implicitly clears the interior
+ * region, so if we're going to draw a solid background before drawing
+ * the background pattern, then we don't need to bother also clearing the
+ * background region.
+ */
+ if (draw_solid_background)
+ background_is_translucent = FALSE;
+
+ has_visible_outline = st_theme_node_has_visible_outline (node);
+
+ /* Create a path for the background's outline first */
+ if (radius[ST_CORNER_TOPLEFT] > 0)
+ cairo_arc (cr,
+ actor_box.x1 + radius[ST_CORNER_TOPLEFT],
+ actor_box.y1 + radius[ST_CORNER_TOPLEFT],
+ radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
+ else
+ cairo_move_to (cr, actor_box.x1, actor_box.y1);
+ cairo_line_to (cr, actor_box.x2 - radius[ST_CORNER_TOPRIGHT], actor_box.x1);
+ if (radius[ST_CORNER_TOPRIGHT] > 0)
+ cairo_arc (cr,
+ actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
+ actor_box.x1 + radius[ST_CORNER_TOPRIGHT],
+ radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
+ cairo_line_to (cr, actor_box.x2, actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT]);
+ if (radius[ST_CORNER_BOTTOMRIGHT] > 0)
+ cairo_arc (cr,
+ actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
+ actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
+ radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
+ cairo_line_to (cr, actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT], actor_box.y2);
+ if (radius[ST_CORNER_BOTTOMLEFT] > 0)
+ cairo_arc (cr,
+ actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
+ actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
+ radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
+ cairo_close_path (cr);
+
+ outline_path = cairo_copy_path (cr);
+
+ /* If we have a solid border, we fill the outline shape with the border
+ * color and create the inline shape for the background;
+ * otherwise the outline shape is filled with the background
+ * directly
+ */
+ if (border_image == NULL &&
+ (border_width[ST_SIDE_TOP] > 0 ||
+ border_width[ST_SIDE_RIGHT] > 0 ||
+ border_width[ST_SIDE_BOTTOM] > 0 ||
+ border_width[ST_SIDE_LEFT] > 0))
+ {
+ cairo_set_source_rgba (cr,
+ border_color.red / 255.,
+ border_color.green / 255.,
+ border_color.blue / 255.,
+ border_color.alpha / 255.);
+ cairo_fill (cr);
+
+ /* We were sloppy when filling in the border, and now the interior
+ * is filled with the border color, too.
+ */
+ interior_dirty = TRUE;
+
+ if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP],
+ border_width[ST_SIDE_LEFT]))
+ elliptical_arc (cr,
+ actor_box.x1 + radius[ST_CORNER_TOPLEFT],
+ actor_box.y1 + radius[ST_CORNER_TOPLEFT],
+ radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT],
+ radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP],
+ M_PI, 3 * M_PI / 2);
+ else
+ cairo_move_to (cr,
+ actor_box.x1 + border_width[ST_SIDE_LEFT],
+ actor_box.y1 + border_width[ST_SIDE_TOP]);
+
+ cairo_line_to (cr,
+ actor_box.x2 - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]),
+ actor_box.y1 + border_width[ST_SIDE_TOP]);
+
+ if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP],
+ border_width[ST_SIDE_RIGHT]))
+ elliptical_arc (cr,
+ actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
+ actor_box.y1 + radius[ST_CORNER_TOPRIGHT],
+ radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT],
+ radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP],
+ 3 * M_PI / 2, 2 * M_PI);
+ else
+ cairo_line_to (cr,
+ actor_box.x2 - border_width[ST_SIDE_RIGHT],
+ actor_box.y1 + border_width[ST_SIDE_TOP]);
+
+ cairo_line_to (cr,
+ actor_box.x2 - border_width[ST_SIDE_RIGHT],
+ actor_box.y2 - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM]));
+
+ if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM],
+ border_width[ST_SIDE_RIGHT]))
+ elliptical_arc (cr,
+ actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
+ actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
+ radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT],
+ radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM],
+ 0, M_PI / 2);
+ else
+ cairo_line_to (cr,
+ actor_box.x2 - border_width[ST_SIDE_RIGHT],
+ actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
+
+ cairo_line_to (cr,
+ MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]),
+ actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
+
+ if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM],
+ border_width[ST_SIDE_LEFT]))
+ elliptical_arc (cr,
+ actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
+ actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
+ radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT],
+ radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM],
+ M_PI / 2, M_PI);
+ else
+ cairo_line_to (cr,
+ actor_box.x1 + border_width[ST_SIDE_LEFT],
+ actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
+
+ cairo_close_path (cr);
+
+ interior_path = cairo_copy_path (cr);
+
+ /* clip drawing to the region inside of the borders
+ */
+ cairo_clip (cr);
+
+ /* But fill the pattern as if it started at the edge of outline,
+ * behind the borders. This is similar to
+ * background-clip: border-box; semantics.
+ */
+ cairo_append_path (cr, outline_path);
+ }
+
+ if (interior_dirty && background_is_translucent)
+ {
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_fill_preserve (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ }
+
+ if (draw_solid_background)
+ {
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_set_source_rgba (cr,
+ node->background_color.red / 255.,
+ node->background_color.green / 255.,
+ node->background_color.blue / 255.,
+ node->background_color.alpha / 255.);
+ cairo_fill_preserve (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ }
+
+ if (draw_background_image_shadow)
+ {
+ paint_background_image_shadow_to_cairo_context (node,
+ shadow_spec,
+ pattern,
+ cr,
+ interior_path,
+ has_visible_outline? outline_path : NULL,
+ actor_box.x1,
+ actor_box.y1,
+ width, height,
+ resource_scale);
+ cairo_append_path (cr, outline_path);
+ }
+
+ cairo_translate (cr, actor_box.x1, actor_box.y1);
+
+ if (pattern != NULL)
+ {
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+ cairo_pattern_destroy (pattern);
+ }
+
+ if (box_shadow_spec && box_shadow_spec->inset)
+ {
+ paint_inset_box_shadow_to_cairo_context (node,
+ box_shadow_spec,
+ resource_scale,
+ cr,
+ interior_path ? interior_path
+ : outline_path);
+ }
+
+ if (outline_path != NULL)
+ cairo_path_destroy (outline_path);
+
+ if (interior_path != NULL)
+ cairo_path_destroy (interior_path);
+
+ texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx,
+ texture_width,
+ texture_height,
+ CLUTTER_CAIRO_FORMAT_ARGB32,
+ rowstride,
+ data,
+ &error));
+
+ if (error)
+ {
+ g_warning ("Failed to allocate texture: %s", error->message);
+ g_error_free (error);
+ }
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ g_free (data);
+
+ return texture;
+}
+
+static void st_theme_node_paint_borders (StThemeNodePaintState *state,
+ CoglFramebuffer *framebuffer,
+ const ClutterActorBox *box,
+ StPaintBordersMode mode,
+ guint8 paint_opacity);
+
+void
+st_theme_node_invalidate_border_image (StThemeNode *node)
+{
+ cogl_clear_object (&node->border_slices_texture);
+ cogl_clear_object (&node->border_slices_pipeline);
+}
+
+static gboolean
+st_theme_node_load_border_image (StThemeNode *node,
+ gfloat resource_scale)
+{
+ if (node->border_slices_texture == NULL)
+ {
+ StBorderImage *border_image;
+ GFile *file;
+
+ border_image = st_theme_node_get_border_image (node);
+ if (border_image == NULL)
+ goto out;
+
+ file = st_border_image_get_file (border_image);
+
+ node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (),
+ file,
+ node->cached_scale_factor,
+ resource_scale);
+ if (node->border_slices_texture == NULL)
+ goto out;
+
+ node->border_slices_pipeline = _st_create_texture_pipeline (node->border_slices_texture);
+ }
+
+ out:
+ return node->border_slices_texture != NULL;
+}
+
+void
+st_theme_node_invalidate_background_image (StThemeNode *node)
+{
+ cogl_clear_object (&node->background_texture);
+ cogl_clear_object (&node->background_pipeline);
+ cogl_clear_object (&node->background_shadow_pipeline);
+}
+
+static gboolean
+st_theme_node_load_background_image (StThemeNode *node,
+ gfloat resource_scale)
+{
+ if (node->background_texture == NULL)
+ {
+ GFile *background_image;
+ StShadow *background_image_shadow_spec;
+
+ background_image = st_theme_node_get_background_image (node);
+ if (background_image == NULL)
+ goto out;
+
+ background_image_shadow_spec = st_theme_node_get_background_image_shadow (node);
+ node->background_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (),
+ background_image,
+ node->cached_scale_factor,
+ resource_scale);
+ if (node->background_texture == NULL)
+ goto out;
+
+ node->background_pipeline = _st_create_texture_pipeline (node->background_texture);
+
+ if (node->background_repeat)
+ cogl_pipeline_set_layer_wrap_mode (node->background_pipeline, 0,
+ COGL_PIPELINE_WRAP_MODE_REPEAT);
+
+ if (background_image_shadow_spec)
+ {
+ node->background_shadow_pipeline = _st_create_shadow_pipeline (background_image_shadow_spec,
+ node->background_texture,
+ resource_scale);
+ }
+ }
+
+ out:
+ return node->background_texture != NULL;
+}
+
+static gboolean
+st_theme_node_invalidate_resources_for_file (StThemeNode *node,
+ GFile *file)
+{
+ StBorderImage *border_image;
+ gboolean changed = FALSE;
+ GFile *theme_file;
+
+ theme_file = st_theme_node_get_background_image (node);
+ if ((theme_file != NULL) && g_file_equal (theme_file, file))
+ {
+ st_theme_node_invalidate_background_image (node);
+ changed = TRUE;
+ }
+
+ border_image = st_theme_node_get_border_image (node);
+ theme_file = border_image ? st_border_image_get_file (border_image) : NULL;
+ if ((theme_file != NULL) && g_file_equal (theme_file, file))
+ {
+ st_theme_node_invalidate_border_image (node);
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
+static void st_theme_node_compute_maximum_borders (StThemeNodePaintState *state);
+static void st_theme_node_prerender_shadow (StThemeNodePaintState *state);
+
+static void
+st_theme_node_render_resources (StThemeNodePaintState *state,
+ StThemeNode *node,
+ float width,
+ float height,
+ float resource_scale)
+{
+ gboolean has_border;
+ gboolean has_border_radius;
+ gboolean has_inset_box_shadow;
+ gboolean has_large_corners;
+ StShadow *box_shadow_spec;
+
+ g_return_if_fail (width > 0 && height > 0);
+
+ /* FIXME - need to separate this into things that need to be recomputed on
+ * geometry change versus things that can be cached regardless, such as
+ * a background image.
+ */
+ st_theme_node_paint_state_free (state);
+
+ st_theme_node_paint_state_set_node (state, node);
+ state->alloc_width = width;
+ state->alloc_height = height;
+ state->resource_scale = resource_scale;
+
+ _st_theme_node_ensure_background (node);
+ _st_theme_node_ensure_geometry (node);
+
+ box_shadow_spec = st_theme_node_get_box_shadow (node);
+ has_inset_box_shadow = box_shadow_spec && box_shadow_spec->inset;
+
+ if (node->border_width[ST_SIDE_TOP] > 0 ||
+ node->border_width[ST_SIDE_LEFT] > 0 ||
+ node->border_width[ST_SIDE_RIGHT] > 0 ||
+ node->border_width[ST_SIDE_BOTTOM] > 0)
+ has_border = TRUE;
+ else
+ has_border = FALSE;
+
+ if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
+ node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
+ node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
+ node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
+ has_border_radius = TRUE;
+ else
+ has_border_radius = FALSE;
+
+ /* The cogl code pads each corner to the maximum border radius,
+ * which results in overlapping corner areas if the radius
+ * exceeds the actor's halfsize, causing rendering errors.
+ * Fall back to cairo in these cases. */
+ has_large_corners = FALSE;
+
+ if (has_border_radius) {
+ guint border_radius[4];
+ int corner;
+
+ st_theme_node_reduce_border_radius (node, width, height, border_radius);
+
+ for (corner = 0; corner < 4; corner ++) {
+ if (border_radius[corner] * 2 > height ||
+ border_radius[corner] * 2 > width) {
+ has_large_corners = TRUE;
+ break;
+ }
+ }
+ }
+
+ state->corner_material[ST_CORNER_TOPLEFT] =
+ st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPLEFT);
+ state->corner_material[ST_CORNER_TOPRIGHT] =
+ st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPRIGHT);
+ state->corner_material[ST_CORNER_BOTTOMRIGHT] =
+ st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMRIGHT);
+ state->corner_material[ST_CORNER_BOTTOMLEFT] =
+ st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMLEFT);
+
+ /* Use cairo to prerender the node if there is a gradient, or
+ * background image with borders and/or rounded corners,
+ * or large corners, since we can't do those things
+ * easily with cogl.
+ *
+ * FIXME: if we could figure out ahead of time that a
+ * background image won't overlap with the node borders,
+ * then we could use cogl for that case.
+ */
+ if ((node->background_gradient_type != ST_GRADIENT_NONE)
+ || (has_inset_box_shadow && (has_border || node->background_color.alpha > 0))
+ || (st_theme_node_get_background_image (node) && (has_border || has_border_radius))
+ || has_large_corners)
+ state->prerendered_texture = st_theme_node_prerender_background (node, width, height,
+ resource_scale);
+
+ if (state->prerendered_texture)
+ state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture);
+ else
+ state->prerendered_pipeline = NULL;
+
+ if (box_shadow_spec && !has_inset_box_shadow)
+ {
+ st_theme_node_compute_maximum_borders (state);
+
+ if (st_theme_node_load_border_image (node, resource_scale))
+ state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec,
+ node->border_slices_texture,
+ state->resource_scale);
+ else if (state->prerendered_texture != NULL)
+ state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec,
+ state->prerendered_texture,
+ state->resource_scale);
+ else
+ st_theme_node_prerender_shadow (state);
+ }
+
+ /* If we don't have cached textures yet, check whether we can cache
+ them. */
+ if (!node->cached_textures)
+ {
+ if (state->prerendered_pipeline == NULL &&
+ width >= node->box_shadow_min_width &&
+ height >= node->box_shadow_min_height)
+ {
+ st_theme_node_paint_state_copy (&node->cached_state, state);
+ node->cached_textures = TRUE;
+ }
+ }
+}
+
+static void
+st_theme_node_update_resources (StThemeNodePaintState *state,
+ StThemeNode *node,
+ float width,
+ float height,
+ float resource_scale)
+{
+ gboolean had_prerendered_texture = FALSE;
+ gboolean had_box_shadow = FALSE;
+ StShadow *box_shadow_spec;
+
+ g_return_if_fail (width > 0 && height > 0);
+
+ /* Free handles we can't reuse */
+ had_prerendered_texture = (state->prerendered_texture != NULL);
+ cogl_clear_object (&state->prerendered_texture);
+
+ if (state->prerendered_pipeline != NULL)
+ {
+ cogl_clear_object (&state->prerendered_pipeline);
+
+ if (node->border_slices_texture == NULL &&
+ state->box_shadow_pipeline != NULL)
+ {
+ cogl_clear_object (&state->box_shadow_pipeline);
+ had_box_shadow = TRUE;
+ }
+ }
+
+ st_theme_node_paint_state_set_node (state, node);
+ state->alloc_width = width;
+ state->alloc_height = height;
+ state->resource_scale = resource_scale;
+
+ box_shadow_spec = st_theme_node_get_box_shadow (node);
+
+ if (had_prerendered_texture)
+ {
+ state->prerendered_texture = st_theme_node_prerender_background (node, width, height, resource_scale);
+ state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture);
+ }
+ else
+ {
+ int corner_id;
+
+ for (corner_id = 0; corner_id < 4; corner_id++)
+ if (state->corner_material[corner_id] == NULL)
+ state->corner_material[corner_id] =
+ st_theme_node_lookup_corner (node, width, height, resource_scale, corner_id);
+ }
+
+ if (had_box_shadow)
+ state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec,
+ state->prerendered_texture,
+ state->resource_scale);
+}
+
+static void
+paint_material_with_opacity (CoglPipeline *material,
+ CoglFramebuffer *framebuffer,
+ ClutterActorBox *box,
+ ClutterActorBox *coords,
+ guint8 paint_opacity)
+{
+ cogl_pipeline_set_color4ub (material,
+ paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+ if (coords)
+ cogl_framebuffer_draw_textured_rectangle (framebuffer, material,
+ box->x1, box->y1, box->x2, box->y2,
+ coords->x1, coords->y1, coords->x2, coords->y2);
+ else
+ cogl_framebuffer_draw_rectangle (framebuffer, material,
+ box->x1, box->y1, box->x2, box->y2);
+}
+
+static void
+st_theme_node_ensure_color_pipeline (StThemeNode *node)
+{
+ static CoglPipeline *color_pipeline_template = NULL;
+
+ if (node->color_pipeline != NULL)
+ return;
+
+ if (G_UNLIKELY (color_pipeline_template == NULL))
+ {
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ color_pipeline_template = cogl_pipeline_new (ctx);
+ }
+
+ node->color_pipeline = cogl_pipeline_copy (color_pipeline_template);
+}
+
+static void
+st_theme_node_paint_borders (StThemeNodePaintState *state,
+ CoglFramebuffer *framebuffer,
+ const ClutterActorBox *box,
+ StPaintBordersMode mode,
+ guint8 paint_opacity)
+{
+ StThemeNode *node = state->node;
+ float width, height;
+ guint border_width[4];
+ guint border_radius[4];
+ guint max_border_radius = 0;
+ guint max_width_radius[4];
+ int corner_id, side_id;
+ ClutterColor border_color;
+ guint8 alpha;
+ gboolean corners_are_transparent;
+
+ width = box->x2 - box->x1;
+ height = box->y2 - box->y1;
+
+ /* TODO - support non-uniform border colors */
+ get_arbitrary_border_color (node, &border_color);
+
+ for (side_id = 0; side_id < 4; side_id++)
+ border_width[side_id] = st_theme_node_get_border_width(node, side_id);
+
+ st_theme_node_reduce_border_radius (node, width, height, border_radius);
+
+ for (corner_id = 0; corner_id < 4; corner_id++)
+ {
+ guint border_width_1, border_width_2;
+
+ st_theme_node_get_corner_border_widths (node, corner_id,
+ &border_width_1, &border_width_2);
+
+ if (border_radius[corner_id] > max_border_radius)
+ max_border_radius = border_radius[corner_id];
+ max_width_radius[corner_id] = MAX(MAX(border_width_1, border_width_2),
+ border_radius[corner_id]);
+ }
+
+ /* borders */
+ if (border_width[ST_SIDE_TOP] > 0 ||
+ border_width[ST_SIDE_RIGHT] > 0 ||
+ border_width[ST_SIDE_BOTTOM] > 0 ||
+ border_width[ST_SIDE_LEFT] > 0)
+ {
+ ClutterColor effective_border;
+ gboolean skip_corner_1, skip_corner_2;
+ float rects[16];
+
+ over (&border_color, &node->background_color, &effective_border);
+ alpha = paint_opacity * effective_border.alpha / 255;
+
+ if (alpha > 0)
+ {
+ st_theme_node_ensure_color_pipeline (node);
+ cogl_pipeline_set_color4ub (node->color_pipeline,
+ effective_border.red * alpha / 255,
+ effective_border.green * alpha / 255,
+ effective_border.blue * alpha / 255,
+ alpha);
+
+ /* NORTH */
+ skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0;
+ skip_corner_2 = border_radius[ST_CORNER_TOPRIGHT] > 0;
+
+ rects[0] = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : 0;
+ rects[1] = 0;
+ rects[2] = skip_corner_2 ? width - max_width_radius[ST_CORNER_TOPRIGHT] : width;
+ rects[3] = border_width[ST_SIDE_TOP];
+
+ /* EAST */
+ skip_corner_1 = border_radius[ST_CORNER_TOPRIGHT] > 0;
+ skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
+
+ rects[4] = width - border_width[ST_SIDE_RIGHT];
+ rects[5] = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT]
+ : border_width[ST_SIDE_TOP];
+ rects[6] = width;
+ rects[7] = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMRIGHT]
+ : height - border_width[ST_SIDE_BOTTOM];
+
+ /* SOUTH */
+ skip_corner_1 = border_radius[ST_CORNER_BOTTOMLEFT] > 0;
+ skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
+
+ rects[8] = skip_corner_1 ? max_width_radius[ST_CORNER_BOTTOMLEFT] : 0;
+ rects[9] = height - border_width[ST_SIDE_BOTTOM];
+ rects[10] = skip_corner_2 ? width - max_width_radius[ST_CORNER_BOTTOMRIGHT]
+ : width;
+ rects[11] = height;
+
+ /* WEST */
+ skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0;
+ skip_corner_2 = border_radius[ST_CORNER_BOTTOMLEFT] > 0;
+
+ rects[12] = 0;
+ rects[13] = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT]
+ : border_width[ST_SIDE_TOP];
+ rects[14] = border_width[ST_SIDE_LEFT];
+ rects[15] = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMLEFT]
+ : height - border_width[ST_SIDE_BOTTOM];
+
+ cogl_framebuffer_draw_rectangles (framebuffer,
+ node->color_pipeline,
+ rects, 4);
+ }
+ }
+
+ corners_are_transparent = mode == ST_PAINT_BORDERS_MODE_COLOR &&
+ node->background_color.alpha == 0 &&
+ node->border_color[0].alpha == 0;
+
+ /* corners */
+ if (max_border_radius > 0 && paint_opacity > 0 && !corners_are_transparent)
+ {
+ for (corner_id = 0; corner_id < 4; corner_id++)
+ {
+ if (state->corner_material[corner_id] == NULL)
+ continue;
+
+ cogl_pipeline_set_color4ub (state->corner_material[corner_id],
+ paint_opacity, paint_opacity,
+ paint_opacity, paint_opacity);
+
+ switch (corner_id)
+ {
+ case ST_CORNER_TOPLEFT:
+ cogl_framebuffer_draw_textured_rectangle (framebuffer,
+ state->corner_material[corner_id], 0, 0,
+ max_width_radius[ST_CORNER_TOPLEFT], max_width_radius[ST_CORNER_TOPLEFT],
+ 0, 0, 0.5, 0.5);
+ break;
+ case ST_CORNER_TOPRIGHT:
+ cogl_framebuffer_draw_textured_rectangle (framebuffer,
+ state->corner_material[corner_id],
+ width - max_width_radius[ST_CORNER_TOPRIGHT], 0,
+ width, max_width_radius[ST_CORNER_TOPRIGHT],
+ 0.5, 0, 1, 0.5);
+ break;
+ case ST_CORNER_BOTTOMRIGHT:
+ cogl_framebuffer_draw_textured_rectangle (framebuffer,
+ state->corner_material[corner_id],
+ width - max_width_radius[ST_CORNER_BOTTOMRIGHT],
+ height - max_width_radius[ST_CORNER_BOTTOMRIGHT],
+ width, height,
+ 0.5, 0.5, 1, 1);
+ break;
+ case ST_CORNER_BOTTOMLEFT:
+ cogl_framebuffer_draw_textured_rectangle (framebuffer,
+ state->corner_material[corner_id],
+ 0, height - max_width_radius[ST_CORNER_BOTTOMLEFT],
+ max_width_radius[ST_CORNER_BOTTOMLEFT], height,
+ 0, 0.5, 0.5, 1);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+ }
+
+ /* background color */
+ alpha = mode == ST_PAINT_BORDERS_MODE_SILHOUETTE ?
+ 255 :
+ paint_opacity * node->background_color.alpha / 255;
+ if (alpha > 0)
+ {
+ st_theme_node_ensure_color_pipeline (node);
+ cogl_pipeline_set_color4ub (node->color_pipeline,
+ node->background_color.red * alpha / 255,
+ node->background_color.green * alpha / 255,
+ node->background_color.blue * alpha / 255,
+ alpha);
+
+ /* We add padding to each corner, so that all corners end up as if they
+ * had a border-radius of max_border_radius, which allows us to treat
+ * corners as uniform further on.
+ */
+ for (corner_id = 0; corner_id < 4; corner_id++)
+ {
+ float verts[8];
+ int n_rects;
+
+ /* corner texture does not need padding */
+ if (max_border_radius == border_radius[corner_id])
+ continue;
+
+ n_rects = border_radius[corner_id] == 0 ? 1 : 2;
+
+ switch (corner_id)
+ {
+ case ST_CORNER_TOPLEFT:
+ verts[0] = border_width[ST_SIDE_LEFT];
+ verts[1] = MAX(border_radius[corner_id],
+ border_width[ST_SIDE_TOP]);
+ verts[2] = max_border_radius;
+ verts[3] = max_border_radius;
+ if (n_rects == 2)
+ {
+ verts[4] = MAX(border_radius[corner_id],
+ border_width[ST_SIDE_LEFT]);
+ verts[5] = border_width[ST_SIDE_TOP];
+ verts[6] = max_border_radius;
+ verts[7] = MAX(border_radius[corner_id],
+ border_width[ST_SIDE_TOP]);
+ }
+ break;
+ case ST_CORNER_TOPRIGHT:
+ verts[0] = width - max_border_radius;
+ verts[1] = MAX(border_radius[corner_id],
+ border_width[ST_SIDE_TOP]);
+ verts[2] = width - border_width[ST_SIDE_RIGHT];
+ verts[3] = max_border_radius;
+ if (n_rects == 2)
+ {
+ verts[4] = width - max_border_radius;
+ verts[5] = border_width[ST_SIDE_TOP];
+ verts[6] = width - MAX(border_radius[corner_id],
+ border_width[ST_SIDE_RIGHT]);
+ verts[7] = MAX(border_radius[corner_id],
+ border_width[ST_SIDE_TOP]);
+ }
+ break;
+ case ST_CORNER_BOTTOMRIGHT:
+ verts[0] = width - max_border_radius;
+ verts[1] = height - max_border_radius;
+ verts[2] = width - border_width[ST_SIDE_RIGHT];
+ verts[3] = height - MAX(border_radius[corner_id],
+ border_width[ST_SIDE_BOTTOM]);
+ if (n_rects == 2)
+ {
+ verts[4] = width - max_border_radius;
+ verts[5] = height - MAX(border_radius[corner_id],
+ border_width[ST_SIDE_BOTTOM]);
+ verts[6] = width - MAX(border_radius[corner_id],
+ border_width[ST_SIDE_RIGHT]);
+ verts[7] = height - border_width[ST_SIDE_BOTTOM];
+ }
+ break;
+ case ST_CORNER_BOTTOMLEFT:
+ verts[0] = border_width[ST_SIDE_LEFT];
+ verts[1] = height - max_border_radius;
+ verts[2] = max_border_radius;
+ verts[3] = height - MAX(border_radius[corner_id],
+ border_width[ST_SIDE_BOTTOM]);
+ if (n_rects == 2)
+ {
+ verts[4] = MAX(border_radius[corner_id],
+ border_width[ST_SIDE_LEFT]);
+ verts[5] = height - MAX(border_radius[corner_id],
+ border_width[ST_SIDE_BOTTOM]);
+ verts[6] = max_border_radius;
+ verts[7] = height - border_width[ST_SIDE_BOTTOM];
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ cogl_framebuffer_draw_rectangles (framebuffer,
+ node->color_pipeline,
+ verts, n_rects);
+ }
+
+ /* Once we've drawn the borders and corners, if the corners are bigger
+ * then the border width, the remaining area is shaped like
+ *
+ * ########
+ * ##########
+ * ##########
+ * ########
+ *
+ * We draw it in at most 3 pieces - first the top and bottom if
+ * necessary, then the main rectangle
+ */
+ if (max_border_radius > border_width[ST_SIDE_TOP])
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
+ border_width[ST_SIDE_TOP],
+ width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
+ max_border_radius);
+ if (max_border_radius > border_width[ST_SIDE_BOTTOM])
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
+ height - max_border_radius,
+ width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
+ height - border_width[ST_SIDE_BOTTOM]);
+
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ border_width[ST_SIDE_LEFT],
+ MAX(border_width[ST_SIDE_TOP], max_border_radius),
+ width - border_width[ST_SIDE_RIGHT],
+ height - MAX(border_width[ST_SIDE_BOTTOM], max_border_radius));
+ }
+}
+
+static void
+st_theme_node_paint_sliced_shadow (StThemeNodePaintState *state,
+ CoglFramebuffer *framebuffer,
+ const ClutterActorBox *box,
+ guint8 paint_opacity)
+{
+ StThemeNode *node = state->node;
+ guint border_radius[4];
+ CoglColor color;
+ StShadow *box_shadow_spec;
+ gfloat xoffset, yoffset;
+ gfloat width, height;
+ gfloat shadow_width, shadow_height;
+ gfloat xend, yend, top, bottom, left, right;
+ gfloat s_top, s_bottom, s_left, s_right;
+ gfloat shadow_blur_radius, x_spread_factor, y_spread_factor;
+ float rectangles[8 * 9];
+ gint idx;
+ ClutterColor background_color;
+ static const ClutterColor invisible_occluded = {3, 2, 1, 0};
+
+ if (paint_opacity == 0)
+ return;
+
+ st_theme_node_reduce_border_radius (node, box->x2 - box->x1, box->y2 - box->y1, border_radius);
+
+ box_shadow_spec = st_theme_node_get_box_shadow (node);
+
+ /* Compute input & output areas :
+ *
+ * yoffset ----------------------------
+ * | | | |
+ * | | | |
+ * | | | |
+ * top ----------------------------
+ * | | | |
+ * | | | |
+ * | | | |
+ * bottom ----------------------------
+ * | | | |
+ * | | | |
+ * | | | |
+ * yend ----------------------------
+ * xoffset left right xend
+ *
+ * s_top = top in offscreen's coordinates (0.0 - 1.0)
+ * s_bottom = bottom in offscreen's coordinates (0.0 - 1.0)
+ * s_left = left in offscreen's coordinates (0.0 - 1.0)
+ * s_right = right in offscreen's coordinates (0.0 - 1.0)
+ */
+ if (box_shadow_spec->blur == 0)
+ shadow_blur_radius = 0;
+ else
+ shadow_blur_radius = ceilf (1.5 * box_shadow_spec->blur / 2.0) * 2.0;
+
+ shadow_width = state->box_shadow_width + 2 * shadow_blur_radius;
+ shadow_height = state->box_shadow_height + 2 * shadow_blur_radius;
+
+ /* Compute input regions parameters */
+ s_top = shadow_blur_radius + box_shadow_spec->blur +
+ MAX (node->border_radius[ST_CORNER_TOPLEFT],
+ node->border_radius[ST_CORNER_TOPRIGHT]);
+ s_bottom = shadow_blur_radius + box_shadow_spec->blur +
+ MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
+ node->border_radius[ST_CORNER_BOTTOMRIGHT]);
+ s_left = shadow_blur_radius + box_shadow_spec->blur +
+ MAX (node->border_radius[ST_CORNER_TOPLEFT],
+ node->border_radius[ST_CORNER_BOTTOMLEFT]);
+ s_right = shadow_blur_radius + box_shadow_spec->blur +
+ MAX (node->border_radius[ST_CORNER_TOPRIGHT],
+ node->border_radius[ST_CORNER_BOTTOMRIGHT]);
+
+ /* Compute output regions parameters */
+ xoffset = box->x1 + box_shadow_spec->xoffset - shadow_blur_radius - box_shadow_spec->spread;
+ yoffset = box->y1 + box_shadow_spec->yoffset - shadow_blur_radius - box_shadow_spec->spread;
+ width = box->x2 - box->x1 + 2 * shadow_blur_radius;
+ height = box->y2 - box->y1 + 2 * shadow_blur_radius;
+
+ x_spread_factor = (width + 2 * box_shadow_spec->spread) / width;
+ y_spread_factor = (height + 2 * box_shadow_spec->spread) / height;
+
+ width += 2 * box_shadow_spec->spread;
+ height += 2 * box_shadow_spec->spread;
+
+ xend = xoffset + width;
+ yend = yoffset + height;
+
+ top = s_top * y_spread_factor;
+ bottom = s_bottom * y_spread_factor;
+ left = s_left * x_spread_factor;
+ right = s_right * x_spread_factor;
+
+ bottom = height - bottom;
+ right = width - right;
+
+ /* Final adjustments */
+ s_top /= shadow_height;
+ s_bottom /= shadow_height;
+ s_left /= shadow_width;
+ s_right /= shadow_width;
+
+ s_bottom = 1.0 - s_bottom;
+ s_right = 1.0 - s_right;
+
+ top += yoffset;
+ bottom += yoffset;
+ left += xoffset;
+ right += xoffset;
+
+ /* Setup pipeline */
+ cogl_color_init_from_4ub (&color,
+ box_shadow_spec->color.red * paint_opacity / 255,
+ box_shadow_spec->color.green * paint_opacity / 255,
+ box_shadow_spec->color.blue * paint_opacity / 255,
+ box_shadow_spec->color.alpha * paint_opacity / 255);
+ cogl_color_premultiply (&color);
+
+ cogl_pipeline_set_layer_combine_constant (state->box_shadow_pipeline, 0, &color);
+
+ idx = 0;
+
+ if (yoffset < top)
+ {
+ if (xoffset < left)
+ {
+ /* Top left corner */
+ rectangles[idx++] = xoffset;
+ rectangles[idx++] = yoffset;
+ rectangles[idx++] = left;
+ rectangles[idx++] = top;
+
+ rectangles[idx++] = 0;
+ rectangles[idx++] = 0;
+ rectangles[idx++] = s_left;
+ rectangles[idx++] = s_top;
+ }
+
+ /* Top middle */
+ rectangles[idx++] = left;
+ rectangles[idx++] = yoffset;
+ rectangles[idx++] = right;
+ rectangles[idx++] = top;
+
+ rectangles[idx++] = s_left;
+ rectangles[idx++] = 0;
+ rectangles[idx++] = s_right;
+ rectangles[idx++] = s_top;
+
+ if (xend > right)
+ {
+ /* Top right corner */
+ rectangles[idx++] = right;
+ rectangles[idx++] = yoffset;
+ rectangles[idx++] = xend;
+ rectangles[idx++] = top;
+
+ rectangles[idx++] = s_right;
+ rectangles[idx++] = 0;
+ rectangles[idx++] = 1;
+ rectangles[idx++] = s_top;
+ }
+ }
+
+ if (xoffset < left)
+ {
+ /* Left middle */
+ rectangles[idx++] = xoffset;
+ rectangles[idx++] = top;
+ rectangles[idx++] = left;
+ rectangles[idx++] = bottom;
+
+ rectangles[idx++] = 0;
+ rectangles[idx++] = s_top;
+ rectangles[idx++] = s_left;
+ rectangles[idx++] = s_bottom;
+ }
+
+ /* Center middle is not definitely occluded? */
+ st_theme_node_get_background_color (node, &background_color);
+ if (!clutter_color_equal (&background_color, &invisible_occluded) ||
+ paint_opacity < 255 ||
+ xoffset > shadow_blur_radius || left < 0 ||
+ yoffset > shadow_blur_radius || top < 0)
+ {
+ rectangles[idx++] = left;
+ rectangles[idx++] = top;
+ rectangles[idx++] = right;
+ rectangles[idx++] = bottom;
+
+ rectangles[idx++] = s_left;
+ rectangles[idx++] = s_top;
+ rectangles[idx++] = s_right;
+ rectangles[idx++] = s_bottom;
+ }
+
+ if (xend > right)
+ {
+ /* Right middle */
+ rectangles[idx++] = right;
+ rectangles[idx++] = top;
+ rectangles[idx++] = xend;
+ rectangles[idx++] = bottom;
+
+ rectangles[idx++] = s_right;
+ rectangles[idx++] = s_top;
+ rectangles[idx++] = 1;
+ rectangles[idx++] = s_bottom;
+ }
+
+ if (yend > bottom)
+ {
+ if (xoffset < left)
+ {
+ /* Bottom left corner */
+ rectangles[idx++] = xoffset;
+ rectangles[idx++] = bottom;
+ rectangles[idx++] = left;
+ rectangles[idx++] = yend;
+
+ rectangles[idx++] = 0;
+ rectangles[idx++] = s_bottom;
+ rectangles[idx++] = s_left;
+ rectangles[idx++] = 1;
+ }
+
+ /* Bottom middle */
+ rectangles[idx++] = left;
+ rectangles[idx++] = bottom;
+ rectangles[idx++] = right;
+ rectangles[idx++] = yend;
+
+ rectangles[idx++] = s_left;
+ rectangles[idx++] = s_bottom;
+ rectangles[idx++] = s_right;
+ rectangles[idx++] = 1;
+
+ if (xend > right)
+ {
+ /* Bottom right corner */
+ rectangles[idx++] = right;
+ rectangles[idx++] = bottom;
+ rectangles[idx++] = xend;
+ rectangles[idx++] = yend;
+
+ rectangles[idx++] = s_right;
+ rectangles[idx++] = s_bottom;
+ rectangles[idx++] = 1;
+ rectangles[idx++] = 1;
+ }
+ }
+
+ cogl_framebuffer_draw_textured_rectangles (framebuffer, state->box_shadow_pipeline,
+ rectangles, idx / 8);
+
+#if 0
+ /* Visual feedback on shadow's 9-slice and original offscreen buffer,
+ for debug purposes */
+ cogl_framebuffer_draw_rectangle (framebuffer, state->box_shadow_pipeline,
+ xend, yoffset, xend + shadow_width, yoffset + shadow_height);
+
+ st_theme_node_ensure_color_pipeline (node);
+ cogl_pipeline_set_color4ub (node->color_pipeline, 0xff, 0x0, 0x0, 0xff);
+
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xoffset, top, xend, top + 1);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xoffset, bottom, xend, bottom + 1);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ left, yoffset, left + 1, yend);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ right, yoffset, right + 1, yend);
+
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend, yoffset, xend + shadow_width, yoffset + 1);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend, yoffset + shadow_height, xend + shadow_width, yoffset + shadow_height + 1);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend, yoffset, xend + 1, yoffset + shadow_height);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend + shadow_width, yoffset, xend + shadow_width + 1, yoffset + shadow_height);
+
+ s_top *= shadow_height;
+ s_bottom *= shadow_height;
+ s_left *= shadow_width;
+ s_right *= shadow_width;
+
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend, yoffset + s_top, xend + shadow_width, yoffset + s_top + 1);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend, yoffset + s_bottom, xend + shadow_width, yoffset + s_bottom + 1);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend + s_left, yoffset, xend + s_left + 1, yoffset + shadow_height);
+ cogl_framebuffer_draw_rectangle (framebuffer, node->color_pipeline,
+ xend + s_right, yoffset, xend + s_right + 1, yoffset + shadow_height);
+
+#endif
+}
+
+static void
+st_theme_node_prerender_shadow (StThemeNodePaintState *state)
+{
+ StThemeNode *node = state->node;
+ CoglContext *ctx;
+ int fb_width, fb_height;
+ CoglTexture *buffer;
+ CoglOffscreen *offscreen = NULL;
+ CoglFramebuffer *framebuffer;
+ GError *error = NULL;
+
+ ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+ /* Render offscreen */
+ fb_width = ceilf (state->box_shadow_width * state->resource_scale);
+ fb_height = ceilf (state->box_shadow_height * state->resource_scale);
+ buffer = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, fb_width, fb_height));
+ if (buffer == NULL)
+ return;
+
+ offscreen = cogl_offscreen_new_with_texture (buffer);
+ framebuffer = COGL_FRAMEBUFFER (offscreen);
+
+ if (cogl_framebuffer_allocate (framebuffer, &error))
+ {
+ ClutterActorBox box = { 0, 0, state->box_shadow_width, state->box_shadow_height};
+
+ cogl_framebuffer_orthographic (framebuffer, 0, 0,
+ fb_width, fb_height, 0, 1.0);
+ cogl_framebuffer_scale (framebuffer,
+ state->resource_scale,
+ state->resource_scale, 1);
+ cogl_framebuffer_clear4f (framebuffer, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0);
+
+ st_theme_node_paint_borders (state, framebuffer, &box, ST_PAINT_BORDERS_MODE_SILHOUETTE, 0xFF);
+
+ state->box_shadow_pipeline = _st_create_shadow_pipeline (st_theme_node_get_box_shadow (node),
+ buffer, state->resource_scale);
+ }
+
+ g_clear_error (&error);
+ g_clear_object (&offscreen);
+ cogl_clear_object (&buffer);
+}
+
+static void
+st_theme_node_compute_maximum_borders (StThemeNodePaintState *state)
+{
+ int max_borders[4], center_radius;
+ StThemeNode * node = state->node;
+
+ /* Compute maximum borders sizes */
+ max_borders[ST_SIDE_TOP] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
+ node->border_radius[ST_CORNER_TOPRIGHT]);
+ max_borders[ST_SIDE_BOTTOM] = MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
+ node->border_radius[ST_CORNER_BOTTOMRIGHT]);
+ max_borders[ST_SIDE_LEFT] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
+ node->border_radius[ST_CORNER_BOTTOMLEFT]);
+ max_borders[ST_SIDE_RIGHT] = MAX (node->border_radius[ST_CORNER_TOPRIGHT],
+ node->border_radius[ST_CORNER_BOTTOMRIGHT]);
+
+ center_radius = (node->box_shadow->blur > 0) ? (2 * node->box_shadow->blur + 1) : 1;
+
+ node->box_shadow_min_width = max_borders[ST_SIDE_LEFT] + max_borders[ST_SIDE_RIGHT] + center_radius;
+ node->box_shadow_min_height = max_borders[ST_SIDE_TOP] + max_borders[ST_SIDE_BOTTOM] + center_radius;
+ if (state->alloc_width < node->box_shadow_min_width ||
+ state->alloc_height < node->box_shadow_min_height)
+ {
+ state->box_shadow_width = state->alloc_width;
+ state->box_shadow_height = state->alloc_height;
+ }
+ else
+ {
+ state->box_shadow_width = node->box_shadow_min_width;
+ state->box_shadow_height = node->box_shadow_min_height;
+ }
+}
+
+static void
+st_theme_node_paint_sliced_border_image (StThemeNode *node,
+ CoglFramebuffer *framebuffer,
+ float width,
+ float height,
+ guint8 paint_opacity)
+{
+ gfloat ex, ey;
+ gfloat tx1, ty1, tx2, ty2;
+ gint border_left, border_right, border_top, border_bottom;
+ float img_width, img_height;
+ StBorderImage *border_image;
+ CoglPipeline *pipeline;
+
+ border_image = st_theme_node_get_border_image (node);
+ g_assert (border_image != NULL);
+
+ st_border_image_get_borders (border_image,
+ &border_left, &border_right, &border_top, &border_bottom);
+
+ img_width = cogl_texture_get_width (node->border_slices_texture);
+ img_height = cogl_texture_get_height (node->border_slices_texture);
+
+ tx1 = border_left / img_width;
+ tx2 = (img_width - border_right) / img_width;
+ ty1 = border_top / img_height;
+ ty2 = (img_height - border_bottom) / img_height;
+
+ ex = width - border_right;
+ if (ex < 0)
+ ex = border_right; /* FIXME ? */
+
+ ey = height - border_bottom;
+ if (ey < 0)
+ ey = border_bottom; /* FIXME ? */
+
+ pipeline = node->border_slices_pipeline;
+ cogl_pipeline_set_color4ub (pipeline,
+ paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+ {
+ float rectangles[] =
+ {
+ /* top left corner */
+ 0, 0, border_left, border_top,
+ 0.0, 0.0,
+ tx1, ty1,
+
+ /* top middle */
+ border_left, 0, ex, border_top,
+ tx1, 0.0,
+ tx2, ty1,
+
+ /* top right */
+ ex, 0, width, border_top,
+ tx2, 0.0,
+ 1.0, ty1,
+
+ /* mid left */
+ 0, border_top, border_left, ey,
+ 0.0, ty1,
+ tx1, ty2,
+
+ /* center */
+ border_left, border_top, ex, ey,
+ tx1, ty1,
+ tx2, ty2,
+
+ /* mid right */
+ ex, border_top, width, ey,
+ tx2, ty1,
+ 1.0, ty2,
+
+ /* bottom left */
+ 0, ey, border_left, height,
+ 0.0, ty2,
+ tx1, 1.0,
+
+ /* bottom center */
+ border_left, ey, ex, height,
+ tx1, ty2,
+ tx2, 1.0,
+
+ /* bottom right */
+ ex, ey, width, height,
+ tx2, ty2,
+ 1.0, 1.0
+ };
+
+ cogl_framebuffer_draw_textured_rectangles (framebuffer, pipeline, rectangles, 9);
+ }
+}
+
+static void
+st_theme_node_paint_outline (StThemeNode *node,
+ CoglFramebuffer *framebuffer,
+ const ClutterActorBox *box,
+ guint8 paint_opacity)
+
+{
+ float width, height;
+ int outline_width;
+ float rects[16];
+ ClutterColor outline_color, effective_outline;
+ guint8 alpha;
+
+ width = box->x2 - box->x1;
+ height = box->y2 - box->y1;
+
+ outline_width = st_theme_node_get_outline_width (node);
+ if (outline_width == 0)
+ return;
+
+ st_theme_node_get_outline_color (node, &outline_color);
+ over (&outline_color, &node->background_color, &effective_outline);
+
+ alpha = paint_opacity * outline_color.alpha / 255;
+
+ st_theme_node_ensure_color_pipeline (node);
+ cogl_pipeline_set_color4ub (node->color_pipeline,
+ effective_outline.red * alpha / 255,
+ effective_outline.green * alpha / 255,
+ effective_outline.blue * alpha / 255,
+ alpha);
+
+ /* The outline is drawn just outside the border, which means just
+ * outside the allocation box. This means that in some situations
+ * involving clip_to_allocation or the screen edges, you won't be
+ * able to see the outline. In practice, it works well enough.
+ */
+
+ /* NORTH */
+ rects[0] = -outline_width;
+ rects[1] = -outline_width;
+ rects[2] = width + outline_width;
+ rects[3] = 0;
+
+ /* EAST */
+ rects[4] = width;
+ rects[5] = 0;
+ rects[6] = width + outline_width;
+ rects[7] = height;
+
+ /* SOUTH */
+ rects[8] = -outline_width;
+ rects[9] = height;
+ rects[10] = width + outline_width;
+ rects[11] = height + outline_width;
+
+ /* WEST */
+ rects[12] = -outline_width;
+ rects[13] = 0;
+ rects[14] = 0;
+ rects[15] = height;
+
+ cogl_framebuffer_draw_rectangles (framebuffer, node->color_pipeline, rects, 4);
+}
+
+static gboolean
+st_theme_node_needs_new_box_shadow_for_size (StThemeNodePaintState *state,
+ StThemeNode *node,
+ float width,
+ float height,
+ float resource_scale)
+{
+ if (!node->rendered_once)
+ return TRUE;
+
+ /* The resource scale changed, so need to recompute a new box-shadow */
+ if (fabsf (state->resource_scale - resource_scale) > FLT_EPSILON)
+ return TRUE;
+
+ /* The allocation hasn't changed, no need to recompute a new
+ box-shadow. */
+ if (state->alloc_width == width &&
+ state->alloc_height == height)
+ return FALSE;
+
+ /* If there is no shadow, no need to recompute a new box-shadow. */
+ if (node->box_shadow_min_width == 0 ||
+ node->box_shadow_min_height == 0)
+ return FALSE;
+
+ /* If the new size is inferior to the box-shadow minimum size (we
+ already know the size has changed), we need to recompute the
+ box-shadow. */
+ if (width < node->box_shadow_min_width ||
+ height < node->box_shadow_min_height)
+ return TRUE;
+
+ /* Now checking whether the size of the node has crossed the minimum
+ box-shadow size boundary, from below to above the minimum size .
+ If that's the case, we need to recompute the box-shadow */
+ if (state->alloc_width < node->box_shadow_min_width ||
+ state->alloc_height < node->box_shadow_min_height)
+ return TRUE;
+
+ return FALSE;
+}
+
+void
+st_theme_node_paint (StThemeNode *node,
+ StThemeNodePaintState *state,
+ CoglFramebuffer *framebuffer,
+ const ClutterActorBox *box,
+ guint8 paint_opacity,
+ float resource_scale)
+{
+ float width, height;
+ ClutterActorBox allocation;
+
+ /* Some things take an ActorBox, some things just width/height */
+ width = box->x2 - box->x1;
+ height = box->y2 - box->y1;
+ allocation.x1 = allocation.y1 = 0;
+ allocation.x2 = width;
+ allocation.y2 = height;
+
+ if (width <= 0 || height <= 0 || resource_scale <= 0.0f)
+ return;
+
+ /* Check whether we need to recreate the textures of the paint
+ * state, either because :
+ * 1) the theme node associated to the paint state has changed
+ * 2) the allocation size change requires recreating textures
+ */
+ if (state->node != node ||
+ st_theme_node_needs_new_box_shadow_for_size (state, node, width, height,
+ resource_scale))
+ {
+ /* If we had the ability to cache textures on the node, then we
+ can just copy them over to the paint state and avoid all
+ rendering. We end up sharing textures a cross different
+ widgets. */
+ if (node->rendered_once && node->cached_textures &&
+ width >= node->box_shadow_min_width && height >= node->box_shadow_min_height &&
+ fabsf (resource_scale - state->resource_scale) < FLT_EPSILON)
+ st_theme_node_paint_state_copy (state, &node->cached_state);
+ else
+ st_theme_node_render_resources (state, node, width, height, resource_scale);
+
+ node->rendered_once = TRUE;
+ }
+ else if (state->alloc_width != width || state->alloc_height != height ||
+ fabsf (state->resource_scale - resource_scale) > FLT_EPSILON)
+ st_theme_node_update_resources (state, node, width, height, resource_scale);
+
+ /* Rough notes about the relationship of borders and backgrounds in CSS3;
+ * see http://www.w3.org/TR/css3-background/ for more accurate details.
+ *
+ * - Things are drawn in 4 layers, from the bottom:
+ * Background color
+ * Background image
+ * Border color or border image
+ * Content
+ * - The background color, gradient and image extend to and are clipped by
+ * the edge of the border area, so will be rounded if the border is
+ * rounded. (CSS3 background-clip property modifies this)
+ * - The border image replaces what would normally be drawn by the border
+ * - The border image is not clipped by a rounded border-radius
+ * - The border radius rounds the background even if the border is
+ * zero width or a border image is being used.
+ *
+ * Deviations from the above as implemented here:
+ * - The combination of border image and a non-zero border radius is
+ * not supported; the background color will be drawn with square
+ * corners.
+ * - The background image is drawn above the border color, not below it.
+ * - We clip the background image to the inside edges of the border
+ * instead of the outside edges of the border (but position the image
+ * such that it's aligned to the outside edges)
+ */
+
+ if (state->box_shadow_pipeline)
+ {
+ if (state->alloc_width < node->box_shadow_min_width ||
+ state->alloc_height < node->box_shadow_min_height)
+ _st_paint_shadow_with_opacity (node->box_shadow,
+ framebuffer,
+ state->box_shadow_pipeline,
+ &allocation,
+ paint_opacity);
+ else
+ st_theme_node_paint_sliced_shadow (state,
+ framebuffer,
+ &allocation,
+ paint_opacity);
+ }
+
+ if (state->prerendered_pipeline != NULL ||
+ st_theme_node_load_border_image (node, resource_scale))
+ {
+ if (state->prerendered_pipeline != NULL)
+ {
+ ClutterActorBox paint_box;
+
+ st_theme_node_get_background_paint_box (node,
+ &allocation,
+ &paint_box);
+
+ paint_material_with_opacity (state->prerendered_pipeline,
+ framebuffer,
+ &paint_box,
+ NULL,
+ paint_opacity);
+ }
+
+ if (node->border_slices_pipeline != NULL)
+ st_theme_node_paint_sliced_border_image (node, framebuffer, width, height, paint_opacity);
+ }
+ else
+ {
+ st_theme_node_paint_borders (state, framebuffer, box, ST_PAINT_BORDERS_MODE_COLOR, paint_opacity);
+ }
+
+ st_theme_node_paint_outline (node, framebuffer, box, paint_opacity);
+
+ if (state->prerendered_pipeline == NULL &&
+ st_theme_node_load_background_image (node, resource_scale))
+ {
+ ClutterActorBox background_box;
+ ClutterActorBox texture_coords;
+ gboolean has_visible_outline;
+
+ /* If the node doesn't have an opaque or repeating background or
+ * a border then we let its background image shadows leak out,
+ * but otherwise we clip it.
+ */
+ has_visible_outline = st_theme_node_has_visible_outline (node);
+
+ get_background_position (node, &allocation, resource_scale,
+ &background_box, &texture_coords);
+
+ if (has_visible_outline || node->background_repeat)
+ cogl_framebuffer_push_rectangle_clip (framebuffer,
+ allocation.x1, allocation.y1,
+ allocation.x2, allocation.y2);
+
+ /* CSS based drop shadows
+ *
+ * Drop shadows in ST are modelled after the CSS3 box-shadow property;
+ * see http://www.css3.info/preview/box-shadow/ for a detailed description.
+ *
+ * While the syntax of the property is mostly identical - we do not support
+ * multiple shadows and allow for a more liberal placement of the color
+ * parameter - its interpretation defers significantly in that the shadow's
+ * shape is not determined by the bounding box, but by the CSS background
+ * image. The drop shadows are allowed to escape the nodes allocation if
+ * there is nothing (like a border, or the edge of the background color)
+ * to logically confine it.
+ */
+ if (node->background_shadow_pipeline != NULL)
+ _st_paint_shadow_with_opacity (node->background_image_shadow,
+ framebuffer,
+ node->background_shadow_pipeline,
+ &background_box,
+ paint_opacity);
+
+ paint_material_with_opacity (node->background_pipeline,
+ framebuffer,
+ &background_box,
+ &texture_coords,
+ paint_opacity);
+
+ if (has_visible_outline || node->background_repeat)
+ cogl_framebuffer_pop_clip (framebuffer);
+ }
+}
+
+static void
+st_theme_node_paint_state_node_free_internal (StThemeNodePaintState *state,
+ gboolean unref_node)
+{
+ int corner_id;
+
+ cogl_clear_object (&state->prerendered_texture);
+ cogl_clear_object (&state->prerendered_pipeline);
+ cogl_clear_object (&state->box_shadow_pipeline);
+
+ for (corner_id = 0; corner_id < 4; corner_id++)
+ cogl_clear_object (&state->corner_material[corner_id]);
+
+ if (unref_node)
+ st_theme_node_paint_state_set_node (state, NULL);
+
+ st_theme_node_paint_state_init (state);
+}
+
+static void
+st_theme_node_paint_state_node_freed (StThemeNodePaintState *state)
+{
+ st_theme_node_paint_state_node_free_internal (state, FALSE);
+}
+
+void
+st_theme_node_paint_state_set_node (StThemeNodePaintState *state, StThemeNode *node)
+{
+ if (state->node)
+ g_object_weak_unref (G_OBJECT (state->node),
+ (GWeakNotify) st_theme_node_paint_state_node_freed,
+ state);
+
+ state->node = node;
+ if (state->node)
+ g_object_weak_ref (G_OBJECT (state->node),
+ (GWeakNotify) st_theme_node_paint_state_node_freed,
+ state);
+}
+
+void
+st_theme_node_paint_state_free (StThemeNodePaintState *state)
+{
+ st_theme_node_paint_state_node_free_internal (state, TRUE);
+}
+
+void
+st_theme_node_paint_state_init (StThemeNodePaintState *state)
+{
+ int corner_id;
+
+ state->alloc_width = 0;
+ state->alloc_height = 0;
+ state->resource_scale = -1;
+ state->node = NULL;
+ state->box_shadow_pipeline = NULL;
+ state->prerendered_texture = NULL;
+ state->prerendered_pipeline = NULL;
+
+ for (corner_id = 0; corner_id < 4; corner_id++)
+ state->corner_material[corner_id] = NULL;
+}
+
+void
+st_theme_node_paint_state_copy (StThemeNodePaintState *state,
+ StThemeNodePaintState *other)
+{
+ int corner_id;
+
+ if (state == other)
+ return;
+
+ st_theme_node_paint_state_free (state);
+
+ st_theme_node_paint_state_set_node (state, other->node);
+
+ state->alloc_width = other->alloc_width;
+ state->alloc_height = other->alloc_height;
+ state->resource_scale = other->resource_scale;
+ state->box_shadow_width = other->box_shadow_width;
+ state->box_shadow_height = other->box_shadow_height;
+
+ if (other->box_shadow_pipeline)
+ state->box_shadow_pipeline = cogl_object_ref (other->box_shadow_pipeline);
+ if (other->prerendered_texture)
+ state->prerendered_texture = cogl_object_ref (other->prerendered_texture);
+ if (other->prerendered_pipeline)
+ state->prerendered_pipeline = cogl_object_ref (other->prerendered_pipeline);
+ for (corner_id = 0; corner_id < 4; corner_id++)
+ if (other->corner_material[corner_id])
+ state->corner_material[corner_id] = cogl_object_ref (other->corner_material[corner_id]);
+}
+
+void
+st_theme_node_paint_state_invalidate (StThemeNodePaintState *state)
+{
+ state->alloc_width = 0;
+ state->alloc_height = 0;
+ state->resource_scale = -1.0f;
+}
+
+gboolean
+st_theme_node_paint_state_invalidate_for_file (StThemeNodePaintState *state,
+ GFile *file)
+{
+ if (state->node != NULL &&
+ st_theme_node_invalidate_resources_for_file (state->node, file))
+ {
+ st_theme_node_paint_state_invalidate (state);
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h
new file mode 100644
index 0000000..7533482
--- /dev/null
+++ b/src/st/st-theme-node-private.h
@@ -0,0 +1,131 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-node-private.h: private structures and functions for StThemeNode
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2011 Quentin "Sardem FF7" Glidic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_THEME_NODE_PRIVATE_H__
+#define __ST_THEME_NODE_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+#include "st-theme-node.h"
+#include "croco/libcroco.h"
+#include "st-types.h"
+
+G_BEGIN_DECLS
+
+struct _StThemeNode {
+ GObject parent;
+
+ StThemeContext *context;
+ StThemeNode *parent_node;
+ StTheme *theme;
+
+ PangoFontDescription *font_desc;
+
+ ClutterColor background_color;
+ /* If gradient is set, then background_color is the gradient start */
+ StGradientType background_gradient_type;
+ ClutterColor background_gradient_end;
+
+ int background_position_x;
+ int background_position_y;
+
+ StBackgroundSize background_size;
+ gint background_size_w;
+ gint background_size_h;
+
+ ClutterColor foreground_color;
+ ClutterColor border_color[4];
+ ClutterColor outline_color;
+
+ int border_width[4];
+ int border_radius[4];
+ int outline_width;
+ guint padding[4];
+ guint margin[4];
+
+ int width;
+ int height;
+ int min_width;
+ int min_height;
+ int max_width;
+ int max_height;
+
+ int transition_duration;
+
+ GFile *background_image;
+ StBorderImage *border_image;
+ StShadow *box_shadow;
+ StShadow *background_image_shadow;
+ StShadow *text_shadow;
+ StIconColors *icon_colors;
+
+ GType element_type;
+ char *element_id;
+ GStrv element_classes;
+ GStrv pseudo_classes;
+ char *inline_style;
+
+ CRDeclaration **properties;
+ int n_properties;
+
+ /* We hold onto these separately so we can destroy them on finalize */
+ CRDeclaration *inline_properties;
+
+ guint background_position_set : 1;
+ guint background_repeat : 1;
+
+ guint properties_computed : 1;
+ guint geometry_computed : 1;
+ guint background_computed : 1;
+ guint foreground_computed : 1;
+ guint border_image_computed : 1;
+ guint box_shadow_computed : 1;
+ guint background_image_shadow_computed : 1;
+ guint text_shadow_computed : 1;
+ guint link_type : 2;
+ guint rendered_once : 1;
+ guint cached_textures : 1;
+
+ int box_shadow_min_width;
+ int box_shadow_min_height;
+
+ guint stylesheets_changed_id;
+
+ CoglPipeline *border_slices_texture;
+ CoglPipeline *border_slices_pipeline;
+ CoglPipeline *background_texture;
+ CoglPipeline *background_pipeline;
+ CoglPipeline *background_shadow_pipeline;
+ CoglPipeline *color_pipeline;
+
+ StThemeNodePaintState cached_state;
+
+ int cached_scale_factor;
+};
+
+void _st_theme_node_ensure_background (StThemeNode *node);
+void _st_theme_node_ensure_geometry (StThemeNode *node);
+void _st_theme_node_apply_margins (StThemeNode *node,
+ ClutterActor *actor);
+
+G_END_DECLS
+
+#endif /* __ST_THEME_NODE_PRIVATE_H__ */
diff --git a/src/st/st-theme-node-transition.c b/src/st/st-theme-node-transition.c
new file mode 100644
index 0000000..20b1476
--- /dev/null
+++ b/src/st/st-theme-node-transition.c
@@ -0,0 +1,470 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-node-transition.c: Theme node transitions for StWidget.
+ *
+ * Copyright 2010 Florian Müllner
+ * Copyright 2010 Adel Gadllah
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <math.h>
+
+#include "st-theme-node-transition.h"
+
+enum {
+ COMPLETED,
+ NEW_FRAME,
+ LAST_SIGNAL
+};
+
+typedef struct _StThemeNodeTransitionPrivate StThemeNodeTransitionPrivate;
+
+struct _StThemeNodeTransition {
+ GObject parent;
+
+ StThemeNodeTransitionPrivate *priv;
+};
+
+struct _StThemeNodeTransitionPrivate {
+ StThemeNode *old_theme_node;
+ StThemeNode *new_theme_node;
+
+ StThemeNodePaintState old_paint_state;
+ StThemeNodePaintState new_paint_state;
+
+ CoglTexture *old_texture;
+ CoglTexture *new_texture;
+
+ CoglFramebuffer *old_offscreen;
+ CoglFramebuffer *new_offscreen;
+
+ CoglPipeline *material;
+
+ ClutterTimeline *timeline;
+
+ gulong timeline_completed_id;
+ gulong timeline_new_frame_id;
+
+ ClutterActorBox last_allocation;
+ ClutterActorBox offscreen_box;
+
+ gboolean needs_setup;
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_PRIVATE (StThemeNodeTransition, st_theme_node_transition, G_TYPE_OBJECT);
+
+
+static void
+on_timeline_completed (ClutterTimeline *timeline,
+ StThemeNodeTransition *transition)
+{
+ g_signal_emit (transition, signals[COMPLETED], 0);
+}
+
+static void
+on_timeline_new_frame (ClutterTimeline *timeline,
+ gint frame_num,
+ StThemeNodeTransition *transition)
+{
+ g_signal_emit (transition, signals[NEW_FRAME], 0);
+}
+
+StThemeNodeTransition *
+st_theme_node_transition_new (ClutterActor *actor,
+ StThemeNode *from_node,
+ StThemeNode *to_node,
+ StThemeNodePaintState *old_paint_state,
+ unsigned int duration)
+{
+ StThemeNodeTransition *transition;
+ g_return_val_if_fail (ST_IS_THEME_NODE (from_node), NULL);
+ g_return_val_if_fail (ST_IS_THEME_NODE (to_node), NULL);
+
+ duration = st_theme_node_get_transition_duration (to_node);
+
+ transition = g_object_new (ST_TYPE_THEME_NODE_TRANSITION, NULL);
+
+ transition->priv->old_theme_node = g_object_ref (from_node);
+ transition->priv->new_theme_node = g_object_ref (to_node);
+
+ st_theme_node_paint_state_copy (&transition->priv->old_paint_state,
+ old_paint_state);
+
+ transition->priv->timeline = clutter_timeline_new_for_actor (actor, duration);
+
+ transition->priv->timeline_completed_id =
+ g_signal_connect (transition->priv->timeline, "completed",
+ G_CALLBACK (on_timeline_completed), transition);
+ transition->priv->timeline_new_frame_id =
+ g_signal_connect (transition->priv->timeline, "new-frame",
+ G_CALLBACK (on_timeline_new_frame), transition);
+
+ clutter_timeline_set_progress_mode (transition->priv->timeline, CLUTTER_EASE_IN_OUT_QUAD);
+
+ clutter_timeline_start (transition->priv->timeline);
+
+ return transition;
+}
+
+/**
+ * st_theme_node_transition_get_new_paint_state: (skip)
+ *
+ */
+StThemeNodePaintState *
+st_theme_node_transition_get_new_paint_state (StThemeNodeTransition *transition)
+{
+ return &transition->priv->new_paint_state;
+}
+
+void
+st_theme_node_transition_update (StThemeNodeTransition *transition,
+ StThemeNode *new_node)
+{
+ StThemeNodeTransitionPrivate *priv;
+ StThemeNode *old_node;
+ ClutterTimelineDirection direction;
+
+ g_return_if_fail (ST_IS_THEME_NODE_TRANSITION (transition));
+ g_return_if_fail (ST_IS_THEME_NODE (new_node));
+
+ priv = transition->priv;
+ direction = clutter_timeline_get_direction (priv->timeline);
+ old_node = (direction == CLUTTER_TIMELINE_FORWARD) ? priv->old_theme_node
+ : priv->new_theme_node;
+
+ /* If the update is the reversal of the current transition,
+ * we reverse the timeline.
+ * Otherwise, we should initiate a new transition from the
+ * current state to the new one; this is hard to do if the
+ * transition is in an intermediate state, so we just cancel
+ * the ongoing transition in that case.
+ * Note that reversing a timeline before any time elapsed
+ * results in the timeline's time position being set to the
+ * full duration - this is not what we want, so we cancel the
+ * transition as well in that case.
+ */
+ if (st_theme_node_equal (new_node, old_node))
+ {
+ {
+ StThemeNodePaintState tmp;
+
+ st_theme_node_paint_state_init (&tmp);
+ st_theme_node_paint_state_copy (&tmp, &priv->old_paint_state);
+ st_theme_node_paint_state_copy (&priv->old_paint_state, &priv->new_paint_state);
+ st_theme_node_paint_state_copy (&priv->new_paint_state, &tmp);
+ st_theme_node_paint_state_free (&tmp);
+ }
+
+ if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)
+ {
+ if (direction == CLUTTER_TIMELINE_FORWARD)
+ clutter_timeline_set_direction (priv->timeline,
+ CLUTTER_TIMELINE_BACKWARD);
+ else
+ clutter_timeline_set_direction (priv->timeline,
+ CLUTTER_TIMELINE_FORWARD);
+ }
+ else
+ {
+ clutter_timeline_stop (priv->timeline);
+ g_signal_emit (transition, signals[COMPLETED], 0);
+ }
+ }
+ else
+ {
+ if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)
+ {
+ clutter_timeline_stop (priv->timeline);
+ g_signal_emit (transition, signals[COMPLETED], 0);
+ }
+ else
+ {
+ guint new_duration = st_theme_node_get_transition_duration (new_node);
+
+ clutter_timeline_set_duration (priv->timeline, new_duration);
+
+ g_object_unref (priv->new_theme_node);
+ priv->new_theme_node = g_object_ref (new_node);
+
+ st_theme_node_paint_state_invalidate (&priv->new_paint_state);
+ }
+ }
+}
+
+static void
+calculate_offscreen_box (StThemeNodeTransition *transition,
+ const ClutterActorBox *allocation)
+{
+ ClutterActorBox paint_box;
+
+ st_theme_node_transition_get_paint_box (transition,
+ allocation,
+ &paint_box);
+ transition->priv->offscreen_box.x1 = paint_box.x1 - allocation->x1;
+ transition->priv->offscreen_box.y1 = paint_box.y1 - allocation->y1;
+ transition->priv->offscreen_box.x2 = paint_box.x2 - allocation->x1;
+ transition->priv->offscreen_box.y2 = paint_box.y2 - allocation->y1;
+}
+
+void
+st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition,
+ const ClutterActorBox *allocation,
+ ClutterActorBox *paint_box)
+{
+ StThemeNodeTransitionPrivate *priv = transition->priv;
+ ClutterActorBox old_node_box, new_node_box;
+
+ st_theme_node_get_paint_box (priv->old_theme_node,
+ allocation,
+ &old_node_box);
+
+ st_theme_node_get_paint_box (priv->new_theme_node,
+ allocation,
+ &new_node_box);
+
+ paint_box->x1 = MIN (old_node_box.x1, new_node_box.x1);
+ paint_box->y1 = MIN (old_node_box.y1, new_node_box.y1);
+ paint_box->x2 = MAX (old_node_box.x2, new_node_box.x2);
+ paint_box->y2 = MAX (old_node_box.y2, new_node_box.y2);
+}
+
+static gboolean
+setup_framebuffers (StThemeNodeTransition *transition,
+ const ClutterActorBox *allocation,
+ float resource_scale)
+{
+ StThemeNodeTransitionPrivate *priv = transition->priv;
+ CoglContext *ctx;
+ guint width, height;
+ GError *catch_error = NULL;
+
+ /* template material to avoid unnecessary shader compilation */
+ static CoglPipeline *material_template = NULL;
+
+ ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ width = ceilf ((priv->offscreen_box.x2 - priv->offscreen_box.x1) * resource_scale);
+ height = ceilf ((priv->offscreen_box.y2 - priv->offscreen_box.y1) * resource_scale);
+
+ g_return_val_if_fail (width > 0, FALSE);
+ g_return_val_if_fail (height > 0, FALSE);
+
+ cogl_clear_object (&priv->old_texture);
+ priv->old_texture = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height));
+
+ cogl_clear_object (&priv->new_texture);
+ priv->new_texture = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height));
+
+ if (priv->old_texture == NULL)
+ return FALSE;
+
+ if (priv->new_texture == NULL)
+ return FALSE;
+
+ g_clear_object (&priv->old_offscreen);
+ priv->old_offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_with_texture (priv->old_texture));
+ if (!cogl_framebuffer_allocate (priv->old_offscreen, &catch_error))
+ {
+ g_error_free (catch_error);
+ g_clear_object (&priv->old_offscreen);
+ return FALSE;
+ }
+
+ g_clear_object (&priv->new_offscreen);
+ priv->new_offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_with_texture (priv->new_texture));
+ if (!cogl_framebuffer_allocate (priv->new_offscreen, &catch_error))
+ {
+ g_error_free (catch_error);
+ g_clear_object (&priv->new_offscreen);
+ return FALSE;
+ }
+
+ if (priv->material == NULL)
+ {
+ if (G_UNLIKELY (material_template == NULL))
+ {
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ material_template = cogl_pipeline_new (ctx);
+
+ cogl_pipeline_set_layer_combine (material_template, 0,
+ "RGBA = REPLACE (TEXTURE)",
+ NULL);
+ cogl_pipeline_set_layer_combine (material_template, 1,
+ "RGBA = INTERPOLATE (PREVIOUS, "
+ "TEXTURE, "
+ "CONSTANT[A])",
+ NULL);
+ cogl_pipeline_set_layer_combine (material_template, 2,
+ "RGBA = MODULATE (PREVIOUS, "
+ "PRIMARY)",
+ NULL);
+ }
+ priv->material = cogl_pipeline_copy (material_template);
+ }
+
+ cogl_pipeline_set_layer_texture (priv->material, 0, priv->new_texture);
+ cogl_pipeline_set_layer_texture (priv->material, 1, priv->old_texture);
+
+ cogl_framebuffer_clear4f (priv->old_offscreen, COGL_BUFFER_BIT_COLOR,
+ 0, 0, 0, 0);
+ cogl_framebuffer_orthographic (priv->old_offscreen,
+ priv->offscreen_box.x1,
+ priv->offscreen_box.y1,
+ priv->offscreen_box.x2,
+ priv->offscreen_box.y2, 0.0, 1.0);
+
+ st_theme_node_paint (priv->old_theme_node, &priv->old_paint_state,
+ priv->old_offscreen, allocation, 255, resource_scale);
+
+ cogl_framebuffer_clear4f (priv->new_offscreen, COGL_BUFFER_BIT_COLOR,
+ 0, 0, 0, 0);
+ cogl_framebuffer_orthographic (priv->new_offscreen,
+ priv->offscreen_box.x1,
+ priv->offscreen_box.y1,
+ priv->offscreen_box.x2,
+ priv->offscreen_box.y2, 0.0, 1.0);
+ st_theme_node_paint (priv->new_theme_node, &priv->new_paint_state,
+ priv->new_offscreen, allocation, 255, resource_scale);
+
+ return TRUE;
+}
+
+void
+st_theme_node_transition_paint (StThemeNodeTransition *transition,
+ CoglFramebuffer *framebuffer,
+ ClutterActorBox *allocation,
+ guint8 paint_opacity,
+ float resource_scale)
+{
+ StThemeNodeTransitionPrivate *priv = transition->priv;
+
+ CoglColor constant;
+ float tex_coords[] = {
+ 0.0, 0.0, 1.0, 1.0,
+ 0.0, 0.0, 1.0, 1.0,
+ };
+
+ g_return_if_fail (ST_IS_THEME_NODE (priv->old_theme_node));
+ g_return_if_fail (ST_IS_THEME_NODE (priv->new_theme_node));
+
+ if (!clutter_actor_box_equal (allocation, &priv->last_allocation))
+ priv->needs_setup = TRUE;
+
+ if (priv->needs_setup)
+ {
+ priv->last_allocation = *allocation;
+
+ calculate_offscreen_box (transition, allocation);
+ priv->needs_setup = clutter_actor_box_get_area (&priv->offscreen_box) == 0 ||
+ !setup_framebuffers (transition, allocation,
+ resource_scale);
+
+ if (priv->needs_setup) /* setting up framebuffers failed */
+ return;
+ }
+
+ cogl_color_init_from_4f (&constant, 0., 0., 0.,
+ clutter_timeline_get_progress (priv->timeline));
+ cogl_pipeline_set_layer_combine_constant (priv->material, 1, &constant);
+
+ cogl_pipeline_set_color4ub (priv->material,
+ paint_opacity, paint_opacity,
+ paint_opacity, paint_opacity);
+
+ cogl_framebuffer_draw_multitextured_rectangle (framebuffer,
+ priv->material,
+ priv->offscreen_box.x1,
+ priv->offscreen_box.y1,
+ priv->offscreen_box.x2,
+ priv->offscreen_box.y2,
+ tex_coords, 8);
+}
+
+static void
+st_theme_node_transition_dispose (GObject *object)
+{
+ StThemeNodeTransitionPrivate *priv = ST_THEME_NODE_TRANSITION (object)->priv;
+
+ g_clear_object (&priv->old_theme_node);
+ g_clear_object (&priv->new_theme_node);
+
+ cogl_clear_object (&priv->old_texture);
+ cogl_clear_object (&priv->new_texture);
+
+ g_clear_object (&priv->old_offscreen);
+ g_clear_object (&priv->new_offscreen);
+
+ cogl_clear_object (&priv->material);
+
+ if (priv->timeline)
+ {
+ g_clear_signal_handler (&priv->timeline_completed_id, priv->timeline);
+ g_clear_signal_handler (&priv->timeline_new_frame_id, priv->timeline);
+
+ g_clear_object (&priv->timeline);
+ }
+
+ priv->timeline_completed_id = 0;
+ priv->timeline_new_frame_id = 0;
+
+ st_theme_node_paint_state_free (&priv->old_paint_state);
+ st_theme_node_paint_state_free (&priv->new_paint_state);
+
+ G_OBJECT_CLASS (st_theme_node_transition_parent_class)->dispose (object);
+}
+
+static void
+st_theme_node_transition_init (StThemeNodeTransition *transition)
+{
+ transition->priv = st_theme_node_transition_get_instance_private (transition);
+
+ transition->priv->old_theme_node = NULL;
+ transition->priv->new_theme_node = NULL;
+
+ transition->priv->old_texture = NULL;
+ transition->priv->new_texture = NULL;
+
+ transition->priv->old_offscreen = NULL;
+ transition->priv->new_offscreen = NULL;
+
+ st_theme_node_paint_state_init (&transition->priv->old_paint_state);
+ st_theme_node_paint_state_init (&transition->priv->new_paint_state);
+
+ transition->priv->needs_setup = TRUE;
+}
+
+static void
+st_theme_node_transition_class_init (StThemeNodeTransitionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = st_theme_node_transition_dispose;
+
+ signals[COMPLETED] =
+ g_signal_new ("completed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals[NEW_FRAME] =
+ g_signal_new ("new-frame",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
diff --git a/src/st/st-theme-node-transition.h b/src/st/st-theme-node-transition.h
new file mode 100644
index 0000000..e7420e6
--- /dev/null
+++ b/src/st/st-theme-node-transition.h
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-node-transition.h: Theme node transitions for StWidget.
+ *
+ * Copyright 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_THEME_NODE_TRANSITION_H__
+#define __ST_THEME_NODE_TRANSITION_H__
+
+#include <clutter/clutter.h>
+
+#include "st-widget.h"
+#include "st-theme-node.h"
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_THEME_NODE_TRANSITION (st_theme_node_transition_get_type ())
+G_DECLARE_FINAL_TYPE (StThemeNodeTransition, st_theme_node_transition,
+ ST, THEME_NODE_TRANSITION, GObject)
+
+StThemeNodeTransition *st_theme_node_transition_new (ClutterActor *actor,
+ StThemeNode *from_node,
+ StThemeNode *to_node,
+ StThemeNodePaintState *old_paint_state,
+ guint duration);
+
+void st_theme_node_transition_update (StThemeNodeTransition *transition,
+ StThemeNode *new_node);
+
+void st_theme_node_transition_paint (StThemeNodeTransition *transition,
+ CoglFramebuffer *framebuffer,
+ ClutterActorBox *allocation,
+ guint8 paint_opacity,
+ float resource_scale);
+
+void st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition,
+ const ClutterActorBox *allocation,
+ ClutterActorBox *paint_box);
+
+StThemeNodePaintState * st_theme_node_transition_get_new_paint_state (StThemeNodeTransition *transition);
+
+G_END_DECLS
+
+#endif
diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c
new file mode 100644
index 0000000..6e09c39
--- /dev/null
+++ b/src/st/st-theme-node.c
@@ -0,0 +1,4325 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-node.c: style information for one node in a tree of themed objects
+ *
+ * Copyright 2008-2010 Red Hat, Inc.
+ * Copyright 2009 Steve Frécinaux
+ * Copyright 2009, 2010 Florian Müllner
+ * Copyright 2010 Adel Gadllah
+ * Copyright 2010 Giovanni Campagna
+ * Copyright 2011 Quentin "Sardem FF7" Glidic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "st-settings.h"
+#include "st-theme-private.h"
+#include "st-theme-context.h"
+#include "st-theme-node-private.h"
+
+static void st_theme_node_dispose (GObject *object);
+static void st_theme_node_finalize (GObject *object);
+
+static const ClutterColor BLACK_COLOR = { 0, 0, 0, 0xff };
+static const ClutterColor TRANSPARENT_COLOR = { 0, 0, 0, 0 };
+static const ClutterColor DEFAULT_SUCCESS_COLOR = { 0x4e, 0x9a, 0x06, 0xff };
+static const ClutterColor DEFAULT_WARNING_COLOR = { 0xf5, 0x79, 0x3e, 0xff };
+static const ClutterColor DEFAULT_ERROR_COLOR = { 0xcc, 0x00, 0x00, 0xff };
+
+G_DEFINE_TYPE (StThemeNode, st_theme_node, G_TYPE_OBJECT)
+
+static void
+st_theme_node_init (StThemeNode *node)
+{
+ node->transition_duration = -1;
+
+ st_theme_node_paint_state_init (&node->cached_state);
+}
+
+static void
+st_theme_node_class_init (StThemeNodeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = st_theme_node_dispose;
+ object_class->finalize = st_theme_node_finalize;
+}
+
+static void
+maybe_free_properties (StThemeNode *node)
+{
+ if (node->properties)
+ {
+ g_free (node->properties);
+ node->properties = NULL;
+ node->n_properties = 0;
+ }
+
+ if (node->inline_properties)
+ {
+ /* This destroys the list, not just the head of the list */
+ cr_declaration_destroy (node->inline_properties);
+ node->inline_properties = NULL;
+ }
+}
+
+static void
+st_theme_node_dispose (GObject *gobject)
+{
+ StThemeNode *node = ST_THEME_NODE (gobject);
+
+ if (node->parent_node)
+ {
+ g_object_unref (node->parent_node);
+ node->parent_node = NULL;
+ }
+
+ if (node->border_image)
+ {
+ g_object_unref (node->border_image);
+ node->border_image = NULL;
+ }
+
+ if (node->icon_colors)
+ {
+ st_icon_colors_unref (node->icon_colors);
+ node->icon_colors = NULL;
+ }
+
+ st_theme_node_paint_state_free (&node->cached_state);
+
+ g_clear_object (&node->theme);
+
+ G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject);
+}
+
+static void
+st_theme_node_finalize (GObject *object)
+{
+ StThemeNode *node = ST_THEME_NODE (object);
+
+ g_free (node->element_id);
+ g_strfreev (node->element_classes);
+ g_strfreev (node->pseudo_classes);
+ g_free (node->inline_style);
+
+ maybe_free_properties (node);
+
+ g_clear_pointer (&node->font_desc, pango_font_description_free);
+
+ g_clear_pointer (&node->box_shadow, st_shadow_unref);
+ g_clear_pointer (&node->background_image_shadow, st_shadow_unref);
+ g_clear_pointer (&node->text_shadow, st_shadow_unref);
+
+ g_clear_object (&node->background_image);
+
+ cogl_clear_object (&node->background_texture);
+ cogl_clear_object (&node->background_pipeline);
+ cogl_clear_object (&node->background_shadow_pipeline);
+ cogl_clear_object (&node->border_slices_texture);
+ cogl_clear_object (&node->border_slices_pipeline);
+ cogl_clear_object (&node->color_pipeline);
+
+ G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object);
+}
+
+static GStrv
+split_on_whitespace (const gchar *s)
+{
+ gchar *cur;
+ gchar *l;
+ gchar *temp;
+ GPtrArray *arr;
+
+ if (s == NULL)
+ return NULL;
+
+ arr = g_ptr_array_new ();
+ l = g_strdup (s);
+
+ cur = strtok_r (l, " \t\f\r\n", &temp);
+
+ while (cur != NULL)
+ {
+ g_ptr_array_add (arr, g_strdup (cur));
+ cur = strtok_r (NULL, " \t\f\r\n", &temp);
+ }
+
+ g_free (l);
+ g_ptr_array_add (arr, NULL);
+ return (GStrv) g_ptr_array_free (arr, FALSE);
+}
+
+/**
+ * st_theme_node_new:
+ * @context: the context representing global state for this themed tree
+ * @parent_node: (nullable): the parent node of this node
+ * @theme: (nullable): a theme (stylesheet set) that overrides the
+ * theme inherited from the parent node
+ * @element_type: the type of the GObject represented by this node
+ * in the tree (corresponding to an element if we were theming an XML
+ * document. %G_TYPE_NONE means this style was created for the stage
+ * actor and matches a selector element name of 'stage'.
+ * @element_id: (nullable): the ID to match CSS rules against
+ * @element_class: (nullable): a whitespace-separated list of classes
+ * to match CSS rules against
+ * @pseudo_class: (nullable): a whitespace-separated list of pseudo-classes
+ * (like 'hover' or 'visited') to match CSS rules against
+ *
+ * Creates a new #StThemeNode. Once created, a node is immutable. If any
+ * of the attributes of the node (like the @element_class) change the node
+ * and its child nodes must be destroyed and recreated.
+ *
+ * Returns: (transfer full): a new #StThemeNode
+ */
+StThemeNode *
+st_theme_node_new (StThemeContext *context,
+ StThemeNode *parent_node,
+ StTheme *theme,
+ GType element_type,
+ const char *element_id,
+ const char *element_class,
+ const char *pseudo_class,
+ const char *inline_style)
+{
+ StThemeNode *node;
+
+ g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
+ g_return_val_if_fail (parent_node == NULL || ST_IS_THEME_NODE (parent_node), NULL);
+
+ node = g_object_new (ST_TYPE_THEME_NODE, NULL);
+
+ node->context = context;
+ if (parent_node != NULL)
+ node->parent_node = g_object_ref (parent_node);
+ else
+ node->parent_node = NULL;
+
+ if (theme == NULL && parent_node != NULL)
+ theme = parent_node->theme;
+
+ g_set_object (&node->theme, theme);
+ node->element_type = element_type;
+ node->element_id = g_strdup (element_id);
+ node->element_classes = split_on_whitespace (element_class);
+ node->pseudo_classes = split_on_whitespace (pseudo_class);
+ node->inline_style = g_strdup (inline_style);
+ node->cached_scale_factor = st_theme_context_get_scale_factor (context);
+
+ return node;
+}
+
+/**
+ * st_theme_node_get_parent:
+ * @node: a #StThemeNode
+ *
+ * Gets the parent themed element node.
+ *
+ * Returns: (nullable) (transfer none): the parent #StThemeNode, or %NULL if
+ * this is the root node of the tree of theme elements.
+ */
+StThemeNode *
+st_theme_node_get_parent (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ return node->parent_node;
+}
+
+/**
+ * st_theme_node_get_theme:
+ * @node: a #StThemeNode
+ *
+ * Gets the theme stylesheet set that styles this node
+ *
+ * Returns: (transfer none): the theme stylesheet set
+ */
+StTheme *
+st_theme_node_get_theme (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ return node->theme;
+}
+
+/**
+ * st_theme_node_get_element_type:
+ * @node: a #StThemeNode
+ *
+ * Get the element #GType for @node.
+ *
+ * Returns: the element type
+ */
+GType
+st_theme_node_get_element_type (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), G_TYPE_NONE);
+
+ return node->element_type;
+}
+
+/**
+ * st_theme_node_get_element_id:
+ * @node: a #StThemeNode
+ *
+ * Get the unique element ID for @node.
+ *
+ * Returns: (transfer none): the element's ID
+ */
+const char *
+st_theme_node_get_element_id (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ return node->element_id;
+}
+
+/**
+ * st_theme_node_get_element_classes:
+ * @node: a #StThemeNode
+ *
+ * Get the list of element classes for @node.
+ *
+ * Returns: (transfer none): the element's classes
+ */
+GStrv
+st_theme_node_get_element_classes (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ return node->element_classes;
+}
+
+/**
+ * st_theme_node_get_pseudo_classes:
+ * @node: a #StThemeNode
+ *
+ * Get the list of pseudo-classes for @node (eg. `:focused`).
+ *
+ * Returns: (transfer none): the element's pseudo-classes
+ */
+GStrv
+st_theme_node_get_pseudo_classes (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ return node->pseudo_classes;
+}
+
+/**
+ * st_theme_node_equal:
+ * @node_a: first #StThemeNode
+ * @node_b: second #StThemeNode
+ *
+ * Compare two #StThemeNodes. Two nodes which compare equal will match
+ * the same CSS rules and have the same style properties. However, two
+ * nodes that have ended up with identical style properties do not
+ * necessarily compare equal.
+ *
+ * In detail, @node_a and @node_b are considered equal if and only if:
+ *
+ * - they share the same #StTheme and #StThemeContext
+ * - they have the same parent
+ * - they have the same element type
+ * - their id, class, pseudo-class and inline-style match
+ *
+ * Returns: %TRUE if @node_a equals @node_b
+ */
+gboolean
+st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node_a), FALSE);
+
+ if (node_a == node_b)
+ return TRUE;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node_b), FALSE);
+
+ if (node_a->parent_node != node_b->parent_node ||
+ node_a->context != node_b->context ||
+ node_a->theme != node_b->theme ||
+ node_a->element_type != node_b->element_type ||
+ node_a->cached_scale_factor != node_b->cached_scale_factor ||
+ g_strcmp0 (node_a->element_id, node_b->element_id) ||
+ g_strcmp0 (node_a->inline_style, node_b->inline_style))
+ return FALSE;
+
+ if ((node_a->element_classes == NULL) != (node_b->element_classes == NULL))
+ return FALSE;
+
+ if ((node_a->pseudo_classes == NULL) != (node_b->pseudo_classes == NULL))
+ return FALSE;
+
+ if (node_a->element_classes != NULL)
+ {
+ int i;
+
+ for (i = 0; ; i++)
+ {
+ if (g_strcmp0 (node_a->element_classes[i],
+ node_b->element_classes[i]))
+ return FALSE;
+
+ if (node_a->element_classes[i] == NULL)
+ break;
+ }
+ }
+
+ if (node_a->pseudo_classes != NULL)
+ {
+ int i;
+
+ for (i = 0; ; i++)
+ {
+ if (g_strcmp0 (node_a->pseudo_classes[i],
+ node_b->pseudo_classes[i]))
+ return FALSE;
+
+ if (node_a->pseudo_classes[i] == NULL)
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * st_theme_node_hash:
+ * @node: a #StThemeNode
+ *
+ * Converts @node to a hash value.
+ *
+ * Returns: a hash value corresponding to @node
+ */
+guint
+st_theme_node_hash (StThemeNode *node)
+{
+ guint hash;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), 0);
+
+ hash = GPOINTER_TO_UINT (node->parent_node);
+
+ hash = hash * 33 + GPOINTER_TO_UINT (node->context);
+ hash = hash * 33 + GPOINTER_TO_UINT (node->theme);
+ hash = hash * 33 + ((guint) node->element_type);
+ hash = hash * 33 + ((guint) node->cached_scale_factor);
+
+ if (node->element_id != NULL)
+ hash = hash * 33 + g_str_hash (node->element_id);
+
+ if (node->inline_style != NULL)
+ hash = hash * 33 + g_str_hash (node->inline_style);
+
+ if (node->element_classes != NULL)
+ {
+ gchar **it;
+
+ for (it = node->element_classes; *it != NULL; it++)
+ hash = hash * 33 + g_str_hash (*it) + 1;
+ }
+
+ if (node->pseudo_classes != NULL)
+ {
+ gchar **it;
+
+ for (it = node->pseudo_classes; *it != NULL; it++)
+ hash = hash * 33 + g_str_hash (*it) + 1;
+ }
+
+ return hash;
+}
+
+static void
+ensure_properties (StThemeNode *node)
+{
+ if (!node->properties_computed)
+ {
+ GPtrArray *properties = NULL;
+
+ node->properties_computed = TRUE;
+
+ if (node->theme)
+ properties = _st_theme_get_matched_properties (node->theme, node);
+
+ if (node->inline_style && *node->inline_style != '\0')
+ {
+ CRDeclaration *cur_decl;
+
+ if (!properties)
+ properties = g_ptr_array_new ();
+
+ node->inline_properties = _st_theme_parse_declaration_list (node->inline_style);
+ for (cur_decl = node->inline_properties; cur_decl; cur_decl = cur_decl->next)
+ g_ptr_array_add (properties, cur_decl);
+ }
+
+ if (properties)
+ {
+ node->n_properties = properties->len;
+ node->properties = (CRDeclaration **)g_ptr_array_free (properties, FALSE);
+ }
+ }
+}
+
+typedef enum {
+ VALUE_FOUND,
+ VALUE_NOT_FOUND,
+ VALUE_INHERIT
+} GetFromTermResult;
+
+static gboolean
+term_is_inherit (CRTerm *term)
+{
+ return (term->type == TERM_IDENT &&
+ strcmp (term->content.str->stryng->str, "inherit") == 0);
+}
+
+static gboolean
+term_is_none (CRTerm *term)
+{
+ return (term->type == TERM_IDENT &&
+ strcmp (term->content.str->stryng->str, "none") == 0);
+}
+
+static gboolean
+term_is_transparent (CRTerm *term)
+{
+ return (term->type == TERM_IDENT &&
+ strcmp (term->content.str->stryng->str, "transparent") == 0);
+}
+
+static int
+color_component_from_double (double component)
+{
+ /* We want to spread the range 0-1 equally over 0..255, but
+ * 1.0 should map to 255 not 256, so we need to special-case it.
+ * See http://people.redhat.com/otaylor/pixel-converting.html
+ * for (very) detailed discussion of related issues. */
+ if (component >= 1.0)
+ return 255;
+ else
+ return (int)(component * 256);
+}
+
+static GetFromTermResult
+get_color_from_rgba_term (CRTerm *term,
+ ClutterColor *color)
+{
+ CRTerm *arg = term->ext_content.func_param;
+ CRNum *num;
+ double r = 0, g = 0, b = 0, a = 0;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ double value;
+
+ if (arg == NULL)
+ return VALUE_NOT_FOUND;
+
+ if ((i == 0 && arg->the_operator != NO_OP) ||
+ (i > 0 && arg->the_operator != COMMA))
+ return VALUE_NOT_FOUND;
+
+ if (arg->type != TERM_NUMBER)
+ return VALUE_NOT_FOUND;
+
+ num = arg->content.num;
+
+ /* For simplicity, we convert a,r,g,b to [0,1.0] floats and then
+ * convert them back below. Then when we set them on a cairo content
+ * we convert them back to floats, and then cairo converts them
+ * back to integers to pass them to X, and so forth...
+ */
+ if (i < 3)
+ {
+ if (num->type == NUM_PERCENTAGE)
+ value = num->val / 100;
+ else if (num->type == NUM_GENERIC)
+ value = num->val / 255;
+ else
+ return VALUE_NOT_FOUND;
+ }
+ else
+ {
+ if (num->type != NUM_GENERIC)
+ return VALUE_NOT_FOUND;
+
+ value = num->val;
+ }
+
+ value = CLAMP (value, 0, 1);
+
+ switch (i)
+ {
+ case 0:
+ r = value;
+ break;
+ case 1:
+ g = value;
+ break;
+ case 2:
+ b = value;
+ break;
+ case 3:
+ a = value;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ arg = arg->next;
+ }
+
+ color->red = color_component_from_double (r);
+ color->green = color_component_from_double (g);
+ color->blue = color_component_from_double (b);
+ color->alpha = color_component_from_double (a);
+
+ return VALUE_FOUND;
+}
+
+static GetFromTermResult
+get_color_from_term (StThemeNode *node,
+ CRTerm *term,
+ ClutterColor *color)
+{
+ CRRgb rgb;
+ enum CRStatus status;
+
+ if (term_is_inherit (term))
+ {
+ return VALUE_INHERIT;
+ }
+ /* Since libcroco doesn't know about rgba colors, it can't handle
+ * the transparent keyword
+ */
+ else if (term_is_transparent (term))
+ {
+ *color = TRANSPARENT_COLOR;
+ return VALUE_FOUND;
+ }
+ /* rgba () colors - a CSS3 addition, are not supported by libcroco,
+ * but they are parsed as a "function", so we can emulate the
+ * functionality.
+ */
+ else if (term->type == TERM_FUNCTION &&
+ term->content.str &&
+ term->content.str->stryng &&
+ term->content.str->stryng->str &&
+ strcmp (term->content.str->stryng->str, "rgba") == 0)
+ {
+ return get_color_from_rgba_term (term, color);
+ }
+
+ status = cr_rgb_set_from_term (&rgb, term);
+ if (status != CR_OK)
+ return VALUE_NOT_FOUND;
+
+ if (rgb.is_percentage)
+ cr_rgb_compute_from_percentage (&rgb);
+
+ color->red = rgb.red;
+ color->green = rgb.green;
+ color->blue = rgb.blue;
+ color->alpha = 0xff;
+
+ return VALUE_FOUND;
+}
+
+/**
+ * st_theme_node_lookup_color:
+ * @node: a #StThemeNode
+ * @property_name: The name of the color property
+ * @inherit: if %TRUE, if a value is not found for the property on the
+ * node, then it will be looked up on the parent node, and then on the
+ * parent's parent, and so forth. Note that if the property has a
+ * value of 'inherit' it will be inherited even if %FALSE is passed
+ * in for @inherit; this only affects the default behavior for inheritance.
+ * @color: (out caller-allocates): location to store the color that was
+ * determined. If the property is not found, the value in this location
+ * will not be changed.
+ *
+ * Generically looks up a property containing a single color value. When
+ * specific getters (like st_theme_node_get_background_color()) exist, they
+ * should be used instead. They are cached, so more efficient, and have
+ * handling for shortcut properties and other details of CSS.
+ *
+ * See also st_theme_node_get_color(), which provides a simpler API.
+ *
+ * Returns: %TRUE if the property was found in the properties for this
+ * theme node (or in the properties of parent nodes when inheriting.)
+ */
+gboolean
+st_theme_node_lookup_color (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ ClutterColor *color)
+{
+
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), FALSE);
+ g_return_val_if_fail (property_name != NULL, FALSE);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, property_name) == 0)
+ {
+ GetFromTermResult result = get_color_from_term (node, decl->value, color);
+ if (result == VALUE_FOUND)
+ {
+ return TRUE;
+ }
+ else if (result == VALUE_INHERIT)
+ {
+ if (node->parent_node)
+ return st_theme_node_lookup_color (node->parent_node, property_name, inherit, color);
+ else
+ break;
+ }
+ }
+ }
+
+ if (inherit && node->parent_node)
+ return st_theme_node_lookup_color (node->parent_node, property_name, inherit, color);
+
+ return FALSE;
+}
+
+/**
+ * st_theme_node_get_color:
+ * @node: a #StThemeNode
+ * @property_name: The name of the color property
+ * @color: (out caller-allocates): location to store the color that
+ * was determined.
+ *
+ * Generically looks up a property containing a single color value. When
+ * specific getters (like st_theme_node_get_background_color()) exist, they
+ * should be used instead. They are cached, so more efficient, and have
+ * handling for shortcut properties and other details of CSS.
+ *
+ * If @property_name is not found, a warning will be logged and a
+ * default color returned.
+ *
+ * See also st_theme_node_lookup_color(), which provides more options,
+ * and lets you handle the case where the theme does not specify the
+ * indicated color.
+ */
+void
+st_theme_node_get_color (StThemeNode *node,
+ const char *property_name,
+ ClutterColor *color)
+{
+ if (!st_theme_node_lookup_color (node, property_name, FALSE, color))
+ {
+ g_warning ("Did not find color property '%s'", property_name);
+ memset (color, 0, sizeof (ClutterColor));
+ }
+}
+
+/**
+ * st_theme_node_lookup_double:
+ * @node: a #StThemeNode
+ * @property_name: The name of the numeric property
+ * @inherit: if %TRUE, if a value is not found for the property on the
+ * node, then it will be looked up on the parent node, and then on the
+ * parent's parent, and so forth. Note that if the property has a
+ * value of 'inherit' it will be inherited even if %FALSE is passed
+ * in for @inherit; this only affects the default behavior for inheritance.
+ * @value: (out): location to store the value that was determined.
+ * If the property is not found, the value in this location
+ * will not be changed.
+ *
+ * Generically looks up a property containing a single numeric value
+ * without units.
+ *
+ * See also st_theme_node_get_double(), which provides a simpler API.
+ *
+ * Returns: %TRUE if the property was found in the properties for this
+ * theme node (or in the properties of parent nodes when inheriting.)
+ */
+gboolean
+st_theme_node_lookup_double (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ double *value)
+{
+ gboolean result = FALSE;
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), FALSE);
+ g_return_val_if_fail (property_name != NULL, FALSE);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, property_name) == 0)
+ {
+ CRTerm *term = decl->value;
+
+ if (term->type != TERM_NUMBER || term->content.num->type != NUM_GENERIC)
+ continue;
+
+ *value = term->content.num->val;
+ result = TRUE;
+ break;
+ }
+ }
+
+ if (!result && inherit && node->parent_node)
+ result = st_theme_node_lookup_double (node->parent_node, property_name, inherit, value);
+
+ return result;
+}
+
+/**
+ * st_theme_node_lookup_time:
+ * @node: a #StThemeNode
+ * @property_name: The name of the time property
+ * @inherit: if %TRUE, if a value is not found for the property on the
+ * node, then it will be looked up on the parent node, and then on the
+ * parent's parent, and so forth. Note that if the property has a
+ * value of 'inherit' it will be inherited even if %FALSE is passed
+ * in for @inherit; this only affects the default behavior for inheritance.
+ * @value: (out): location to store the value that was determined.
+ * If the property is not found, the value in this location
+ * will not be changed.
+ *
+ * Generically looks up a property containing a single time value,
+ * which is converted to milliseconds.
+ *
+ * Returns: %TRUE if the property was found in the properties for this
+ * theme node (or in the properties of parent nodes when inheriting.)
+ */
+gboolean
+st_theme_node_lookup_time (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ double *value)
+{
+ gboolean result = FALSE;
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), FALSE);
+ g_return_val_if_fail (property_name != NULL, FALSE);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, property_name) == 0)
+ {
+ CRTerm *term = decl->value;
+ int factor = 1;
+
+ if (term->type != TERM_NUMBER)
+ continue;
+
+ if (term->content.num->type != NUM_TIME_S &&
+ term->content.num->type != NUM_TIME_MS)
+ continue;
+
+ if (term->content.num->type == NUM_TIME_S)
+ factor = 1000;
+
+ *value = factor * term->content.num->val;
+ result = TRUE;
+ break;
+ }
+ }
+
+ if (!result && inherit && node->parent_node)
+ result = st_theme_node_lookup_time (node->parent_node, property_name, inherit, value);
+
+ return result;
+}
+
+/**
+ * st_theme_node_get_double:
+ * @node: a #StThemeNode
+ * @property_name: The name of the numeric property
+ *
+ * Generically looks up a property containing a single numeric value
+ * without units.
+ *
+ * See also st_theme_node_lookup_double(), which provides more options,
+ * and lets you handle the case where the theme does not specify the
+ * indicated value.
+ *
+ * Returns: the value found. If @property_name is not
+ * found, a warning will be logged and 0 will be returned.
+ */
+gdouble
+st_theme_node_get_double (StThemeNode *node,
+ const char *property_name)
+{
+ gdouble value;
+
+ if (st_theme_node_lookup_double (node, property_name, FALSE, &value))
+ return value;
+ else
+ {
+ g_warning ("Did not find double property '%s'", property_name);
+ return 0.0;
+ }
+}
+
+/**
+ * st_theme_node_lookup_url:
+ * @node: a #StThemeNode
+ * @property_name: The name of the string property
+ * @inherit: if %TRUE, if a value is not found for the property on the
+ * node, then it will be looked up on the parent node, and then on the
+ * parent's parent, and so forth. Note that if the property has a
+ * value of 'inherit' it will be inherited even if %FALSE is passed
+ * in for @inherit; this only affects the default behavior for inheritance.
+ * @file: (out) (transfer full): location to store the newly allocated value that was
+ * determined. If the property is not found, the value in this location
+ * will not be changed.
+ *
+ * Looks up a property containing a single URL value.
+ *
+ * See also st_theme_node_get_url(), which provides a simpler API.
+ *
+ * Returns: %TRUE if the property was found in the properties for this
+ * theme node (or in the properties of parent nodes when inheriting.)
+ */
+gboolean
+st_theme_node_lookup_url (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ GFile **file)
+{
+ gboolean result = FALSE;
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), FALSE);
+ g_return_val_if_fail (property_name != NULL, FALSE);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, property_name) == 0)
+ {
+ CRTerm *term = decl->value;
+ CRStyleSheet *base_stylesheet;
+
+ if (term->type != TERM_URI && term->type != TERM_STRING)
+ continue;
+
+ if (decl->parent_statement != NULL)
+ base_stylesheet = decl->parent_statement->parent_sheet;
+ else
+ base_stylesheet = NULL;
+
+ *file = _st_theme_resolve_url (node->theme,
+ base_stylesheet,
+ decl->value->content.str->stryng->str);
+ result = TRUE;
+ break;
+ }
+ }
+
+ if (!result && inherit && node->parent_node)
+ result = st_theme_node_lookup_url (node->parent_node, property_name, inherit, file);
+
+ return result;
+}
+
+/**
+ * st_theme_node_get_url:
+ * @node: a #StThemeNode
+ * @property_name: The name of the string property
+ *
+ * Looks up a property containing a single URL value.
+ *
+ * See also st_theme_node_lookup_url(), which provides more options,
+ * and lets you handle the case where the theme does not specify the
+ * indicated value.
+ *
+ * Returns: (nullable) (transfer full): the newly allocated value if found.
+ * If @property_name is not found, a warning will be logged and %NULL
+ * will be returned.
+ */
+GFile *
+st_theme_node_get_url (StThemeNode *node,
+ const char *property_name)
+{
+ GFile *file;
+
+ if (st_theme_node_lookup_url (node, property_name, FALSE, &file))
+ return file;
+ else
+ {
+ g_warning ("Did not find string property '%s'", property_name);
+ return NULL;
+ }
+}
+
+static const PangoFontDescription *
+get_parent_font (StThemeNode *node)
+{
+ if (node->parent_node)
+ return st_theme_node_get_font (node->parent_node);
+ else
+ return st_theme_context_get_font (node->context);
+}
+
+static GetFromTermResult
+get_length_from_term (StThemeNode *node,
+ CRTerm *term,
+ gboolean use_parent_font,
+ gdouble *length)
+{
+ CRNum *num;
+
+ enum {
+ ABSOLUTE,
+ POINTS,
+ FONT_RELATIVE,
+ } type = ABSOLUTE;
+
+ double multiplier = 1.0;
+
+
+ if (term->type != TERM_NUMBER)
+ {
+ g_warning ("Ignoring length property that isn't a number at line %d, col %d",
+ term->location.line, term->location.column);
+ return VALUE_NOT_FOUND;
+ }
+
+ num = term->content.num;
+
+ switch (num->type)
+ {
+ case NUM_LENGTH_PX:
+ type = ABSOLUTE;
+ multiplier = 1 * node->cached_scale_factor;
+ break;
+ case NUM_LENGTH_PT:
+ type = POINTS;
+ multiplier = 1;
+ break;
+ case NUM_LENGTH_IN:
+ type = POINTS;
+ multiplier = 72;
+ break;
+ case NUM_LENGTH_CM:
+ type = POINTS;
+ multiplier = 72. / 2.54;
+ break;
+ case NUM_LENGTH_MM:
+ type = POINTS;
+ multiplier = 72. / 25.4;
+ break;
+ case NUM_LENGTH_PC:
+ type = POINTS;
+ multiplier = 12. / 25.4;
+ break;
+ case NUM_LENGTH_EM:
+ {
+ type = FONT_RELATIVE;
+ multiplier = 1;
+ break;
+ }
+ case NUM_LENGTH_EX:
+ {
+ /* Doing better would require actually resolving the font description
+ * to a specific font, and Pango doesn't have an ex metric anyways,
+ * so we'd have to try and synthesize it by complicated means.
+ *
+ * The 0.5em is the CSS spec suggested thing to use when nothing
+ * better is available.
+ */
+ type = FONT_RELATIVE;
+ multiplier = 0.5;
+ break;
+ }
+
+ case NUM_INHERIT:
+ return VALUE_INHERIT;
+
+ case NUM_AUTO:
+ g_warning ("'auto' not supported for lengths");
+ return VALUE_NOT_FOUND;
+
+ case NUM_GENERIC:
+ {
+ if (num->val != 0)
+ {
+ g_warning ("length values must specify a unit");
+ return VALUE_NOT_FOUND;
+ }
+ else
+ {
+ type = ABSOLUTE;
+ multiplier = 0;
+ }
+ break;
+ }
+
+ case NUM_PERCENTAGE:
+ g_warning ("percentage lengths not currently supported");
+ return VALUE_NOT_FOUND;
+
+ case NUM_ANGLE_DEG:
+ case NUM_ANGLE_RAD:
+ case NUM_ANGLE_GRAD:
+ case NUM_TIME_MS:
+ case NUM_TIME_S:
+ case NUM_FREQ_HZ:
+ case NUM_FREQ_KHZ:
+ case NUM_UNKNOWN_TYPE:
+ case NB_NUM_TYPE:
+ default:
+ g_warning ("Ignoring invalid type of number of length property");
+ return VALUE_NOT_FOUND;
+ }
+
+ switch (type)
+ {
+ case ABSOLUTE:
+ *length = num->val * multiplier;
+ break;
+ case POINTS:
+ {
+ double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
+ *length = num->val * multiplier * (resolution / 72.);
+ }
+ break;
+ case FONT_RELATIVE:
+ {
+ const PangoFontDescription *desc;
+ double font_size;
+
+ if (use_parent_font)
+ desc = get_parent_font (node);
+ else
+ desc = st_theme_node_get_font (node);
+
+ font_size = (double)pango_font_description_get_size (desc) / PANGO_SCALE;
+
+ if (pango_font_description_get_size_is_absolute (desc))
+ {
+ *length = num->val * multiplier * font_size;
+ }
+ else
+ {
+ double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
+ *length = num->val * multiplier * (resolution / 72.) * font_size;
+ }
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return VALUE_FOUND;
+}
+
+static GetFromTermResult
+get_length_from_term_int (StThemeNode *node,
+ CRTerm *term,
+ gboolean use_parent_font,
+ gint *length)
+{
+ double value;
+ GetFromTermResult result;
+
+ result = get_length_from_term (node, term, use_parent_font, &value);
+ if (result == VALUE_FOUND)
+ *length = (int) ((value / node->cached_scale_factor) + 0.5) * node->cached_scale_factor;
+ return result;
+}
+
+static GetFromTermResult
+get_length_internal (StThemeNode *node,
+ const char *property_name,
+ gdouble *length)
+{
+ int i;
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, property_name) == 0)
+ {
+ GetFromTermResult result = get_length_from_term (node, decl->value, FALSE, length);
+ if (result != VALUE_NOT_FOUND)
+ return result;
+ }
+ }
+
+ return VALUE_NOT_FOUND;
+}
+
+/**
+ * st_theme_node_lookup_length:
+ * @node: a #StThemeNode
+ * @property_name: The name of the length property
+ * @inherit: if %TRUE, if a value is not found for the property on the
+ * node, then it will be looked up on the parent node, and then on the
+ * parent's parent, and so forth. Note that if the property has a
+ * value of 'inherit' it will be inherited even if %FALSE is passed
+ * in for @inherit; this only affects the default behavior for inheritance.
+ * @length: (out): location to store the length that was determined.
+ * If the property is not found, the value in this location
+ * will not be changed. The returned length is resolved
+ * to pixels.
+ *
+ * Generically looks up a property containing a single length value. When
+ * specific getters (like st_theme_node_get_border_width()) exist, they
+ * should be used instead. They are cached, so more efficient, and have
+ * handling for shortcut properties and other details of CSS.
+ *
+ * See also st_theme_node_get_length(), which provides a simpler API.
+ *
+ * Returns: %TRUE if the property was found in the properties for this
+ * theme node (or in the properties of parent nodes when inheriting.)
+ */
+gboolean
+st_theme_node_lookup_length (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ gdouble *length)
+{
+ GetFromTermResult result;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), FALSE);
+ g_return_val_if_fail (property_name != NULL, FALSE);
+
+ result = get_length_internal (node, property_name, length);
+
+ if (result == VALUE_FOUND)
+ return TRUE;
+ else if (result == VALUE_INHERIT)
+ inherit = TRUE;
+
+ if (inherit && node->parent_node)
+ return st_theme_node_lookup_length (node->parent_node, property_name, inherit, length);
+
+ return FALSE;
+}
+
+/**
+ * st_theme_node_get_length:
+ * @node: a #StThemeNode
+ * @property_name: The name of the length property
+ *
+ * Generically looks up a property containing a single length value. When
+ * specific getters (like st_theme_node_get_border_width()) exist, they
+ * should be used instead. They are cached, so more efficient, and have
+ * handling for shortcut properties and other details of CSS.
+ *
+ * Unlike st_theme_node_get_color() and st_theme_node_get_double(),
+ * this does not print a warning if the property is not found; it just
+ * returns 0.
+ *
+ * See also st_theme_node_lookup_length(), which provides more options. The
+ * returned value is in physical pixels, as opposed to logical pixels.
+ *
+ * Returns: the length, in pixels, or 0 if the property was not found.
+ */
+gdouble
+st_theme_node_get_length (StThemeNode *node,
+ const char *property_name)
+{
+ gdouble length;
+
+ if (st_theme_node_lookup_length (node, property_name, FALSE, &length))
+ return length;
+ else
+ return 0.0;
+}
+
+static void
+do_border_radius_term (StThemeNode *node,
+ CRTerm *term,
+ gboolean topleft,
+ gboolean topright,
+ gboolean bottomright,
+ gboolean bottomleft)
+{
+ int value;
+
+ if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND)
+ return;
+
+ if (topleft)
+ node->border_radius[ST_CORNER_TOPLEFT] = value;
+ if (topright)
+ node->border_radius[ST_CORNER_TOPRIGHT] = value;
+ if (bottomright)
+ node->border_radius[ST_CORNER_BOTTOMRIGHT] = value;
+ if (bottomleft)
+ node->border_radius[ST_CORNER_BOTTOMLEFT] = value;
+}
+
+static void
+do_border_radius (StThemeNode *node,
+ CRDeclaration *decl)
+{
+ const char *property_name = decl->property->stryng->str + 13; /* Skip 'border-radius' */
+
+ if (strcmp (property_name, "") == 0)
+ {
+ /* Slight deviation ... if we don't understand some of the terms and understand others,
+ * then we set the ones we understand and ignore the others instead of ignoring the
+ * whole thing
+ */
+ if (decl->value == NULL) /* 0 values */
+ return;
+ else if (decl->value->next == NULL) /* 1 value */
+ {
+ do_border_radius_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* all corners */
+ return;
+ }
+ else if (decl->value->next->next == NULL) /* 2 values */
+ {
+ do_border_radius_term (node, decl->value, TRUE, FALSE, TRUE, FALSE); /* topleft/bottomright */
+ do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, TRUE); /* topright/bottomleft */
+ }
+ else if (decl->value->next->next->next == NULL) /* 3 values */
+ {
+ do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); /* topleft */
+ do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, TRUE); /* topright/bottomleft */
+ do_border_radius_term (node, decl->value->next->next, FALSE, FALSE, TRUE, FALSE); /* bottomright */
+ }
+ else if (decl->value->next->next->next->next == NULL) /* 4 values */
+ {
+ do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); /* topleft */
+ do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, FALSE); /* topright */
+ do_border_radius_term (node, decl->value->next->next, FALSE, FALSE, TRUE, FALSE); /* bottomright */
+ do_border_radius_term (node, decl->value->next->next->next, FALSE, FALSE, FALSE, TRUE); /* bottomleft */
+ }
+ else
+ {
+ g_warning ("Too many values for border-radius property");
+ return;
+ }
+ }
+ else
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ return;
+
+ if (strcmp (property_name, "-topleft") == 0)
+ do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
+ else if (strcmp (property_name, "-topright") == 0)
+ do_border_radius_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
+ else if (strcmp (property_name, "-bottomright") == 0)
+ do_border_radius_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
+ else if (strcmp (property_name, "-bottomleft") == 0)
+ do_border_radius_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
+ }
+}
+
+static void
+do_border_property (StThemeNode *node,
+ CRDeclaration *decl)
+{
+ const char *property_name = decl->property->stryng->str + 6; /* Skip 'border' */
+ StSide side = (StSide)-1;
+ ClutterColor color;
+ gboolean color_set = FALSE;
+ int width = 0; /* suppress warning */
+ gboolean width_set = FALSE;
+ int j;
+
+ if (g_str_has_prefix (property_name, "-radius"))
+ {
+ do_border_radius (node, decl);
+ return;
+ }
+
+ if (g_str_has_prefix (property_name, "-left"))
+ {
+ side = ST_SIDE_LEFT;
+ property_name += 5;
+ }
+ else if (g_str_has_prefix (property_name, "-right"))
+ {
+ side = ST_SIDE_RIGHT;
+ property_name += 6;
+ }
+ else if (g_str_has_prefix (property_name, "-top"))
+ {
+ side = ST_SIDE_TOP;
+ property_name += 4;
+ }
+ else if (g_str_has_prefix (property_name, "-bottom"))
+ {
+ side = ST_SIDE_BOTTOM;
+ property_name += 7;
+ }
+
+ if (strcmp (property_name, "") == 0)
+ {
+ /* Set value for width/color/style in any order */
+ CRTerm *term;
+
+ for (term = decl->value; term; term = term->next)
+ {
+ GetFromTermResult result;
+
+ if (term->type == TERM_IDENT)
+ {
+ const char *ident = term->content.str->stryng->str;
+ if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0)
+ {
+ width = 0;
+ width_set = TRUE;
+ continue;
+ }
+ else if (strcmp (ident, "solid") == 0)
+ {
+ /* The only thing we support */
+ continue;
+ }
+ else if (strcmp (ident, "dotted") == 0 ||
+ strcmp (ident, "dashed") == 0 ||
+ strcmp (ident, "double") == 0 ||
+ strcmp (ident, "groove") == 0 ||
+ strcmp (ident, "ridge") == 0 ||
+ strcmp (ident, "inset") == 0 ||
+ strcmp (ident, "outset") == 0)
+ {
+ /* Treat the same as solid */
+ continue;
+ }
+
+ /* Presumably a color, fall through */
+ }
+
+ if (term->type == TERM_NUMBER)
+ {
+ result = get_length_from_term_int (node, term, FALSE, &width);
+ if (result != VALUE_NOT_FOUND)
+ {
+ width_set = result == VALUE_FOUND;
+ continue;
+ }
+ }
+
+ result = get_color_from_term (node, term, &color);
+ if (result != VALUE_NOT_FOUND)
+ {
+ color_set = result == VALUE_FOUND;
+ continue;
+ }
+ }
+
+ }
+ else if (strcmp (property_name, "-color") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ return;
+
+ if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND)
+ /* Ignore inherit */
+ color_set = TRUE;
+ }
+ else if (strcmp (property_name, "-width") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ return;
+
+ if (get_length_from_term_int (node, decl->value, FALSE, &width) == VALUE_FOUND)
+ /* Ignore inherit */
+ width_set = TRUE;
+ }
+
+ if (side == (StSide)-1)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ if (color_set)
+ node->border_color[j] = color;
+ if (width_set)
+ node->border_width[j] = width;
+ }
+ }
+ else
+ {
+ if (color_set)
+ node->border_color[side] = color;
+ if (width_set)
+ node->border_width[side] = width;
+ }
+}
+
+static void
+do_outline_property (StThemeNode *node,
+ CRDeclaration *decl)
+{
+ const char *property_name = decl->property->stryng->str + 7; /* Skip 'outline' */
+ ClutterColor color;
+ gboolean color_set = FALSE;
+ int width = 0; /* suppress warning */
+ gboolean width_set = FALSE;
+
+ if (strcmp (property_name, "") == 0)
+ {
+ /* Set value for width/color/style in any order */
+ CRTerm *term;
+
+ for (term = decl->value; term; term = term->next)
+ {
+ GetFromTermResult result;
+
+ if (term->type == TERM_IDENT)
+ {
+ const char *ident = term->content.str->stryng->str;
+ if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0)
+ {
+ width = 0;
+ width_set = TRUE;
+ continue;
+ }
+ else if (strcmp (ident, "solid") == 0)
+ {
+ /* The only thing we support */
+ continue;
+ }
+ else if (strcmp (ident, "dotted") == 0 ||
+ strcmp (ident, "dashed") == 0 ||
+ strcmp (ident, "double") == 0 ||
+ strcmp (ident, "groove") == 0 ||
+ strcmp (ident, "ridge") == 0 ||
+ strcmp (ident, "inset") == 0 ||
+ strcmp (ident, "outset") == 0)
+ {
+ /* Treat the same as solid */
+ continue;
+ }
+
+ /* Presumably a color, fall through */
+ }
+
+ if (term->type == TERM_NUMBER)
+ {
+ result = get_length_from_term_int (node, term, FALSE, &width);
+ if (result != VALUE_NOT_FOUND)
+ {
+ width_set = result == VALUE_FOUND;
+ continue;
+ }
+ }
+
+ result = get_color_from_term (node, term, &color);
+ if (result != VALUE_NOT_FOUND)
+ {
+ color_set = result == VALUE_FOUND;
+ continue;
+ }
+ }
+
+ }
+ else if (strcmp (property_name, "-color") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ return;
+
+ if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND)
+ /* Ignore inherit */
+ color_set = TRUE;
+ }
+ else if (strcmp (property_name, "-width") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ return;
+
+ if (get_length_from_term_int (node, decl->value, FALSE, &width) == VALUE_FOUND)
+ /* Ignore inherit */
+ width_set = TRUE;
+ }
+
+ if (color_set)
+ node->outline_color = color;
+ if (width_set)
+ node->outline_width = width;
+}
+
+static void
+do_padding_property_term (StThemeNode *node,
+ CRTerm *term,
+ gboolean left,
+ gboolean right,
+ gboolean top,
+ gboolean bottom)
+{
+ int value;
+
+ if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND)
+ return;
+
+ if (left)
+ node->padding[ST_SIDE_LEFT] = value;
+ if (right)
+ node->padding[ST_SIDE_RIGHT] = value;
+ if (top)
+ node->padding[ST_SIDE_TOP] = value;
+ if (bottom)
+ node->padding[ST_SIDE_BOTTOM] = value;
+}
+
+static void
+do_padding_property (StThemeNode *node,
+ CRDeclaration *decl)
+{
+ const char *property_name = decl->property->stryng->str + 7; /* Skip 'padding' */
+
+ if (strcmp (property_name, "") == 0)
+ {
+ /* Slight deviation ... if we don't understand some of the terms and understand others,
+ * then we set the ones we understand and ignore the others instead of ignoring the
+ * whole thing
+ */
+ if (decl->value == NULL) /* 0 values */
+ return;
+ else if (decl->value->next == NULL) /* 1 value */
+ {
+ do_padding_property_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* left/right/top/bottom */
+ return;
+ }
+ else if (decl->value->next->next == NULL) /* 2 values */
+ {
+ do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, TRUE); /* top/bottom */
+ do_padding_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */
+ }
+ else if (decl->value->next->next->next == NULL) /* 3 values */
+ {
+ do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */
+ do_padding_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */
+ do_padding_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */
+ }
+ else if (decl->value->next->next->next->next == NULL) /* 4 values */
+ {
+ do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */
+ do_padding_property_term (node, decl->value->next, FALSE, TRUE, FALSE, FALSE); /* right */
+ do_padding_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */
+ do_padding_property_term (node, decl->value->next->next->next, TRUE, FALSE, FALSE, FALSE); /* left */
+ }
+ else
+ {
+ g_warning ("Too many values for padding property");
+ return;
+ }
+ }
+ else
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ return;
+
+ if (strcmp (property_name, "-left") == 0)
+ do_padding_property_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
+ else if (strcmp (property_name, "-right") == 0)
+ do_padding_property_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
+ else if (strcmp (property_name, "-top") == 0)
+ do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
+ else if (strcmp (property_name, "-bottom") == 0)
+ do_padding_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
+ }
+}
+
+static void
+do_margin_property_term (StThemeNode *node,
+ CRTerm *term,
+ gboolean left,
+ gboolean right,
+ gboolean top,
+ gboolean bottom)
+{
+ int value;
+
+ if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND)
+ return;
+
+ if (left)
+ node->margin[ST_SIDE_LEFT] = value;
+ if (right)
+ node->margin[ST_SIDE_RIGHT] = value;
+ if (top)
+ node->margin[ST_SIDE_TOP] = value;
+ if (bottom)
+ node->margin[ST_SIDE_BOTTOM] = value;
+}
+
+static void
+do_margin_property (StThemeNode *node,
+ CRDeclaration *decl)
+{
+ const char *property_name = decl->property->stryng->str + 6; /* Skip 'margin' */
+
+ if (strcmp (property_name, "") == 0)
+ {
+ /* Slight deviation ... if we don't understand some of the terms and understand others,
+ * then we set the ones we understand and ignore the others instead of ignoring the
+ * whole thing
+ */
+ if (decl->value == NULL) /* 0 values */
+ return;
+ else if (decl->value->next == NULL) /* 1 value */
+ {
+ do_margin_property_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* left/right/top/bottom */
+ return;
+ }
+ else if (decl->value->next->next == NULL) /* 2 values */
+ {
+ do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, TRUE); /* top/bottom */
+ do_margin_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */
+ }
+ else if (decl->value->next->next->next == NULL) /* 3 values */
+ {
+ do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */
+ do_margin_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */
+ do_margin_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */
+ }
+ else if (decl->value->next->next->next->next == NULL) /* 4 values */
+ {
+ do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */
+ do_margin_property_term (node, decl->value->next, FALSE, TRUE, FALSE, FALSE); /* right */
+ do_margin_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */
+ do_margin_property_term (node, decl->value->next->next->next, TRUE, FALSE, FALSE, FALSE); /* left */
+ }
+ else
+ {
+ g_warning ("Too many values for margin property");
+ return;
+ }
+ }
+ else
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ return;
+
+ if (strcmp (property_name, "-left") == 0)
+ do_margin_property_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
+ else if (strcmp (property_name, "-right") == 0)
+ do_margin_property_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
+ else if (strcmp (property_name, "-top") == 0)
+ do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
+ else if (strcmp (property_name, "-bottom") == 0)
+ do_margin_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
+ }
+}
+
+static void
+do_size_property (StThemeNode *node,
+ CRDeclaration *decl,
+ int *node_value)
+{
+ CRTerm *term = decl->value;
+
+ if (term->type == TERM_IDENT &&
+ strcmp (term->content.str->stryng->str, "auto") == 0)
+ *node_value = -1;
+ else
+ get_length_from_term_int (node, term, FALSE, node_value);
+}
+
+void
+_st_theme_node_ensure_geometry (StThemeNode *node)
+{
+ int i, j;
+ int width, height;
+
+ if (node->geometry_computed)
+ return;
+
+ node->geometry_computed = TRUE;
+
+ ensure_properties (node);
+
+ for (j = 0; j < 4; j++)
+ {
+ node->border_width[j] = 0;
+ node->border_color[j] = TRANSPARENT_COLOR;
+ }
+
+ node->outline_width = 0;
+ node->outline_color = TRANSPARENT_COLOR;
+
+ width = -1;
+ height = -1;
+ node->width = -1;
+ node->height = -1;
+ node->min_width = -1;
+ node->min_height = -1;
+ node->max_width = -1;
+ node->max_height = -1;
+
+ for (i = 0; i < node->n_properties; i++)
+ {
+ CRDeclaration *decl = node->properties[i];
+ const char *property_name = decl->property->stryng->str;
+
+ if (g_str_has_prefix (property_name, "border"))
+ do_border_property (node, decl);
+ else if (g_str_has_prefix (property_name, "outline"))
+ do_outline_property (node, decl);
+ else if (g_str_has_prefix (property_name, "padding"))
+ do_padding_property (node, decl);
+ else if (g_str_has_prefix (property_name, "margin"))
+ do_margin_property (node, decl);
+ else if (strcmp (property_name, "width") == 0)
+ do_size_property (node, decl, &width);
+ else if (strcmp (property_name, "height") == 0)
+ do_size_property (node, decl, &height);
+ else if (strcmp (property_name, "-st-natural-width") == 0)
+ do_size_property (node, decl, &node->width);
+ else if (strcmp (property_name, "-st-natural-height") == 0)
+ do_size_property (node, decl, &node->height);
+ else if (strcmp (property_name, "min-width") == 0)
+ do_size_property (node, decl, &node->min_width);
+ else if (strcmp (property_name, "min-height") == 0)
+ do_size_property (node, decl, &node->min_height);
+ else if (strcmp (property_name, "max-width") == 0)
+ do_size_property (node, decl, &node->max_width);
+ else if (strcmp (property_name, "max-height") == 0)
+ do_size_property (node, decl, &node->max_height);
+ }
+
+ /*
+ * Setting width sets max-width, min-width and -st-natural-width,
+ * unless one of them is set individually.
+ * Setting min-width sets natural width too, so that the minimum
+ * width reported by get_preferred_width() is always not greater
+ * than the natural width.
+ * The natural width in node->width is actually a lower bound, the
+ * actor is allowed to request something greater than that, but
+ * not greater than max-width.
+ * We don't need to clamp node->width to be less than max_width,
+ * that's done by adjust_preferred_width.
+ */
+ if (width != -1)
+ {
+ if (node->width == -1)
+ node->width = width;
+ if (node->min_width == -1)
+ node->min_width = width;
+ if (node->max_width == -1)
+ node->max_width = width;
+ }
+
+ if (node->width < node->min_width)
+ node->width = node->min_width;
+
+ if (height != -1)
+ {
+ if (node->height == -1)
+ node->height = height;
+ if (node->min_height == -1)
+ node->min_height = height;
+ if (node->max_height == -1)
+ node->max_height = height;
+ }
+
+ if (node->height < node->min_height)
+ node->height = node->min_height;
+}
+
+/**
+ * st_theme_node_get_border_width:
+ * @node: a #StThemeNode
+ * @side: a #StCorner
+ *
+ * Get the border width for @node on @side, in physical pixels.
+ *
+ * Returns: the border width in physical pixels
+ */
+int
+st_theme_node_get_border_width (StThemeNode *node,
+ StSide side)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
+ g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
+
+ _st_theme_node_ensure_geometry (node);
+
+ return node->border_width[side];
+}
+
+/**
+ * st_theme_node_get_border_radius:
+ * @node: a #StThemeNode
+ * @corner: a #StCorner
+ *
+ * Get the border radius for @node at @corner, in physical pixels.
+ *
+ * Returns: the border radius in physical pixels
+ */
+int
+st_theme_node_get_border_radius (StThemeNode *node,
+ StCorner corner)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
+ g_return_val_if_fail (corner >= ST_CORNER_TOPLEFT && corner <= ST_CORNER_BOTTOMLEFT, 0.);
+
+ _st_theme_node_ensure_geometry (node);
+
+ return node->border_radius[corner];
+}
+
+/**
+ * st_theme_node_get_outline_width:
+ * @node: a #StThemeNode
+ *
+ * Get the width of the outline for @node, in physical pixels.
+ *
+ * Returns: the width in physical pixels
+ */
+int
+st_theme_node_get_outline_width (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), 0);
+
+ _st_theme_node_ensure_geometry (node);
+
+ return node->outline_width;
+}
+
+/**
+ * st_theme_node_get_outline_color:
+ * @node: a #StThemeNode
+ * @color: (out caller-allocates): location to store the color
+ *
+ * Gets the color of @node's outline.
+ */
+void
+st_theme_node_get_outline_color (StThemeNode *node,
+ ClutterColor *color)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ _st_theme_node_ensure_geometry (node);
+
+ *color = node->outline_color;
+}
+
+/**
+ * st_theme_node_get_width:
+ * @node: a #StThemeNode
+ *
+ * Get the width for @node, in physical pixels.
+ *
+ * Returns: the width in physical pixels
+ */
+int
+st_theme_node_get_width (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
+
+ _st_theme_node_ensure_geometry (node);
+ return node->width;
+}
+
+/**
+ * st_theme_node_get_height:
+ * @node: a #StThemeNode
+ *
+ * Get the height for @node, in physical pixels.
+ *
+ * Returns: the height in physical pixels
+ */
+int
+st_theme_node_get_height (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
+
+ _st_theme_node_ensure_geometry (node);
+ return node->height;
+}
+
+/**
+ * st_theme_node_get_min_width:
+ * @node: a #StThemeNode
+ *
+ * Get the minimum width for @node, in physical pixels.
+ *
+ * Returns: the minimum width in physical pixels
+ */
+int
+st_theme_node_get_min_width (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
+
+ _st_theme_node_ensure_geometry (node);
+ return node->min_width;
+}
+
+/**
+ * st_theme_node_get_min_height:
+ * @node: a #StThemeNode
+ *
+ * Get the minimum height for @node, in physical pixels.
+ *
+ * Returns: the minimum height in physical pixels
+ */
+int
+st_theme_node_get_min_height (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
+
+ _st_theme_node_ensure_geometry (node);
+ return node->min_height;
+}
+
+/**
+ * st_theme_node_get_max_width:
+ * @node: a #StThemeNode
+ *
+ * Get the maximum width for @node, in physical pixels.
+ *
+ * Returns: the maximum width in physical pixels
+ */
+int
+st_theme_node_get_max_width (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
+
+ _st_theme_node_ensure_geometry (node);
+ return node->max_width;
+}
+
+/**
+ * st_theme_node_get_max_height:
+ * @node: a #StThemeNode
+ *
+ * Get the maximum height for @node, in physical pixels.
+ *
+ * Returns: the maximum height in physical pixels
+ */
+int
+st_theme_node_get_max_height (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
+
+ _st_theme_node_ensure_geometry (node);
+ return node->max_height;
+}
+
+void
+_st_theme_node_ensure_background (StThemeNode *node)
+{
+ int i;
+
+ if (node->background_computed)
+ return;
+
+ node->background_repeat = FALSE;
+ node->background_computed = TRUE;
+ node->background_color = TRANSPARENT_COLOR;
+ node->background_gradient_type = ST_GRADIENT_NONE;
+ node->background_position_set = FALSE;
+ node->background_size = ST_BACKGROUND_SIZE_AUTO;
+
+ ensure_properties (node);
+
+ for (i = 0; i < node->n_properties; i++)
+ {
+ CRDeclaration *decl = node->properties[i];
+ const char *property_name = decl->property->stryng->str;
+
+ if (g_str_has_prefix (property_name, "background"))
+ property_name += 10;
+ else
+ continue;
+
+ if (strcmp (property_name, "") == 0)
+ {
+ /* We're very liberal here ... if we recognize any term in the expression we take it, and
+ * we ignore the rest. The actual specification is:
+ *
+ * background: [<'background-color'> || <'background-image'> || <'background-repeat'> || <'background-attachment'> || <'background-position'>] | inherit
+ */
+
+ CRTerm *term;
+ /* background: property sets all terms to specified or default values */
+ node->background_color = TRANSPARENT_COLOR;
+ g_clear_object (&node->background_image);
+ node->background_position_set = FALSE;
+ node->background_size = ST_BACKGROUND_SIZE_AUTO;
+
+ for (term = decl->value; term; term = term->next)
+ {
+ GetFromTermResult result = get_color_from_term (node, term, &node->background_color);
+ if (result == VALUE_FOUND)
+ {
+ /* color stored in node->background_color */
+ }
+ else if (result == VALUE_INHERIT)
+ {
+ if (node->parent_node)
+ {
+ st_theme_node_get_background_color (node->parent_node, &node->background_color);
+ node->background_image = g_object_ref (st_theme_node_get_background_image (node->parent_node));
+ }
+ }
+ else if (term_is_none (term))
+ {
+ /* leave node->background_color as transparent */
+ }
+ else if (term->type == TERM_URI)
+ {
+ CRStyleSheet *base_stylesheet;
+ GFile *file;
+
+ if (decl->parent_statement != NULL)
+ base_stylesheet = decl->parent_statement->parent_sheet;
+ else
+ base_stylesheet = NULL;
+
+ file = _st_theme_resolve_url (node->theme,
+ base_stylesheet,
+ term->content.str->stryng->str);
+
+ node->background_image = file;
+ }
+ }
+ }
+ else if (strcmp (property_name, "-position") == 0)
+ {
+ GetFromTermResult result = get_length_from_term_int (node, decl->value, FALSE, &node->background_position_x);
+ if (result == VALUE_NOT_FOUND)
+ {
+ node->background_position_set = FALSE;
+ continue;
+ }
+ else
+ node->background_position_set = TRUE;
+
+ result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_position_y);
+
+ if (result == VALUE_NOT_FOUND)
+ {
+ node->background_position_set = FALSE;
+ continue;
+ }
+ else
+ node->background_position_set = TRUE;
+ }
+ else if (strcmp (property_name, "-repeat") == 0)
+ {
+ if (decl->value->type == TERM_IDENT)
+ {
+ if (strcmp (decl->value->content.str->stryng->str, "repeat") == 0)
+ node->background_repeat = TRUE;
+ }
+ }
+ else if (strcmp (property_name, "-size") == 0)
+ {
+ if (decl->value->type == TERM_IDENT)
+ {
+ if (strcmp (decl->value->content.str->stryng->str, "contain") == 0)
+ node->background_size = ST_BACKGROUND_SIZE_CONTAIN;
+ else if (strcmp (decl->value->content.str->stryng->str, "cover") == 0)
+ node->background_size = ST_BACKGROUND_SIZE_COVER;
+ else if ((strcmp (decl->value->content.str->stryng->str, "auto") == 0) && (decl->value->next) && (decl->value->next->type == TERM_NUMBER))
+ {
+ GetFromTermResult result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_size_h);
+
+ node->background_size_w = -1;
+ node->background_size = (result == VALUE_FOUND) ? ST_BACKGROUND_SIZE_FIXED : ST_BACKGROUND_SIZE_AUTO;
+ }
+ else
+ node->background_size = ST_BACKGROUND_SIZE_AUTO;
+ }
+ else if (decl->value->type == TERM_NUMBER)
+ {
+ GetFromTermResult result = get_length_from_term_int (node, decl->value, FALSE, &node->background_size_w);
+ if (result == VALUE_NOT_FOUND)
+ continue;
+
+ node->background_size = ST_BACKGROUND_SIZE_FIXED;
+
+ if ((decl->value->next) && (decl->value->next->type == TERM_NUMBER))
+ {
+ result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_size_h);
+
+ if (result == VALUE_FOUND)
+ continue;
+ }
+ node->background_size_h = -1;
+ }
+ else
+ node->background_size = ST_BACKGROUND_SIZE_AUTO;
+ }
+ else if (strcmp (property_name, "-color") == 0)
+ {
+ GetFromTermResult result;
+
+ if (decl->value == NULL || decl->value->next != NULL)
+ continue;
+
+ result = get_color_from_term (node, decl->value, &node->background_color);
+ if (result == VALUE_FOUND)
+ {
+ /* color stored in node->background_color */
+ }
+ else if (result == VALUE_INHERIT)
+ {
+ if (node->parent_node)
+ st_theme_node_get_background_color (node->parent_node, &node->background_color);
+ }
+ }
+ else if (strcmp (property_name, "-image") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ continue;
+
+ if (decl->value->type == TERM_URI)
+ {
+ CRStyleSheet *base_stylesheet;
+
+ if (decl->parent_statement != NULL)
+ base_stylesheet = decl->parent_statement->parent_sheet;
+ else
+ base_stylesheet = NULL;
+
+ g_clear_object (&node->background_image);
+ node->background_image = _st_theme_resolve_url (node->theme,
+ base_stylesheet,
+ decl->value->content.str->stryng->str);
+ }
+ else if (term_is_inherit (decl->value))
+ {
+ g_clear_object (&node->background_image);
+ node->background_image = g_object_ref (st_theme_node_get_background_image (node->parent_node));
+ }
+ else if (term_is_none (decl->value))
+ {
+ g_clear_object (&node->background_image);
+ }
+ }
+ else if (strcmp (property_name, "-gradient-direction") == 0)
+ {
+ CRTerm *term = decl->value;
+ if (strcmp (term->content.str->stryng->str, "vertical") == 0)
+ {
+ node->background_gradient_type = ST_GRADIENT_VERTICAL;
+ }
+ else if (strcmp (term->content.str->stryng->str, "horizontal") == 0)
+ {
+ node->background_gradient_type = ST_GRADIENT_HORIZONTAL;
+ }
+ else if (strcmp (term->content.str->stryng->str, "radial") == 0)
+ {
+ node->background_gradient_type = ST_GRADIENT_RADIAL;
+ }
+ else if (strcmp (term->content.str->stryng->str, "none") == 0)
+ {
+ node->background_gradient_type = ST_GRADIENT_NONE;
+ }
+ else
+ {
+ g_warning ("Unrecognized background-gradient-direction \"%s\"",
+ term->content.str->stryng->str);
+ }
+ }
+ else if (strcmp (property_name, "-gradient-start") == 0)
+ {
+ get_color_from_term (node, decl->value, &node->background_color);
+ }
+ else if (strcmp (property_name, "-gradient-end") == 0)
+ {
+ get_color_from_term (node, decl->value, &node->background_gradient_end);
+ }
+ }
+}
+
+/**
+ * st_theme_node_get_background_color:
+ * @node: a #StThemeNode
+ * @color: (out caller-allocates): location to store the color
+ *
+ * Gets @node's background color.
+ */
+void
+st_theme_node_get_background_color (StThemeNode *node,
+ ClutterColor *color)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ _st_theme_node_ensure_background (node);
+
+ *color = node->background_color;
+}
+
+/**
+ * st_theme_node_get_background_image:
+ * @node: a #StThemeNode
+ *
+ * Returns: (transfer none): @node's background image.
+ */
+GFile *
+st_theme_node_get_background_image (StThemeNode *node)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ _st_theme_node_ensure_background (node);
+
+ return node->background_image;
+}
+
+/**
+ * st_theme_node_get_foreground_color:
+ * @node: a #StThemeNode
+ * @color: (out caller-allocates): location to store the color
+ *
+ * Gets @node's foreground color.
+ */
+void
+st_theme_node_get_foreground_color (StThemeNode *node,
+ ClutterColor *color)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ if (!node->foreground_computed)
+ {
+ int i;
+
+ node->foreground_computed = TRUE;
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, "color") == 0)
+ {
+ GetFromTermResult result = get_color_from_term (node, decl->value, &node->foreground_color);
+ if (result == VALUE_FOUND)
+ goto out;
+ else if (result == VALUE_INHERIT)
+ break;
+ }
+ }
+
+ if (node->parent_node)
+ st_theme_node_get_foreground_color (node->parent_node, &node->foreground_color);
+ else
+ node->foreground_color = BLACK_COLOR; /* default to black */
+ }
+
+ out:
+ *color = node->foreground_color;
+}
+
+
+/**
+ * st_theme_node_get_background_gradient:
+ * @node: A #StThemeNode
+ * @type: (out): Type of gradient
+ * @start: (out caller-allocates): Color at start of gradient
+ * @end: (out caller-allocates): Color at end of gradient
+ *
+ * The @start and @end arguments will only be set if @type is not #ST_GRADIENT_NONE.
+ */
+void
+st_theme_node_get_background_gradient (StThemeNode *node,
+ StGradientType *type,
+ ClutterColor *start,
+ ClutterColor *end)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ _st_theme_node_ensure_background (node);
+
+ *type = node->background_gradient_type;
+ if (*type != ST_GRADIENT_NONE)
+ {
+ *start = node->background_color;
+ *end = node->background_gradient_end;
+ }
+}
+
+/**
+ * st_theme_node_get_border_color:
+ * @node: a #StThemeNode
+ * @side: a #StSide
+ * @color: (out caller-allocates): location to store the color
+ *
+ * Gets the color of @node's border on @side
+ */
+void
+st_theme_node_get_border_color (StThemeNode *node,
+ StSide side,
+ ClutterColor *color)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+ g_return_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT);
+
+ _st_theme_node_ensure_geometry (node);
+
+ *color = node->border_color[side];
+}
+
+/**
+ * st_theme_node_get_padding:
+ * @node: a #StThemeNode
+ * @side: a #StSide
+ *
+ * Get the padding for @node on @side, in physical pixels. This corresponds to
+ * the CSS properties such as `padding-top`.
+ *
+ * Returns: the padding size in physical pixels
+ */
+double
+st_theme_node_get_padding (StThemeNode *node,
+ StSide side)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
+ g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
+
+ _st_theme_node_ensure_geometry (node);
+
+ return node->padding[side];
+}
+
+/**
+ * st_theme_node_get_margin:
+ * @node: a #StThemeNode
+ * @side: a #StSide
+ *
+ * Get the margin for @node on @side, in physical pixels. This corresponds to
+ * the CSS properties such as `margin-top`.
+ *
+ * Returns: the margin size in physical pixels
+ */
+double
+st_theme_node_get_margin (StThemeNode *node,
+ StSide side)
+{
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
+ g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
+
+ _st_theme_node_ensure_geometry (node);
+
+ return node->margin[side];
+}
+
+/**
+ * st_theme_node_get_transition_duration:
+ * @node: an #StThemeNode
+ *
+ * Get the value of the transition-duration property, which
+ * specifies the transition time between the previous #StThemeNode
+ * and @node.
+ *
+ * Returns: the node's transition duration in milliseconds
+ */
+int
+st_theme_node_get_transition_duration (StThemeNode *node)
+{
+ StSettings *settings;
+ gdouble value = 0.0;
+ gdouble factor;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), 0);
+
+ settings = st_settings_get ();
+ g_object_get (settings, "slow-down-factor", &factor, NULL);
+
+ if (node->transition_duration > -1)
+ return factor * node->transition_duration;
+
+ st_theme_node_lookup_time (node, "transition-duration", FALSE, &value);
+
+ node->transition_duration = (int)value;
+
+ return factor * node->transition_duration;
+}
+
+/**
+ * st_theme_node_get_icon_style:
+ * @node: a #StThemeNode
+ *
+ * Get the icon style for @node (eg. symbolic, regular). This corresponds to the
+ * special `-st-icon-style` CSS property.
+ *
+ * Returns: the icon style for @node
+ */
+StIconStyle
+st_theme_node_get_icon_style (StThemeNode *node)
+{
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), ST_ICON_STYLE_REQUESTED);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, "-st-icon-style") == 0)
+ {
+ CRTerm *term;
+
+ for (term = decl->value; term; term = term->next)
+ {
+ if (term->type != TERM_IDENT)
+ goto next_decl;
+
+ if (strcmp (term->content.str->stryng->str, "requested") == 0)
+ return ST_ICON_STYLE_REQUESTED;
+ else if (strcmp (term->content.str->stryng->str, "regular") == 0)
+ return ST_ICON_STYLE_REGULAR;
+ else if (strcmp (term->content.str->stryng->str, "symbolic") == 0)
+ return ST_ICON_STYLE_SYMBOLIC;
+ else
+ g_warning ("Unknown -st-icon-style \"%s\"",
+ term->content.str->stryng->str);
+ }
+ }
+
+ next_decl:
+ ;
+ }
+
+ if (node->parent_node)
+ return st_theme_node_get_icon_style (node->parent_node);
+
+ return ST_ICON_STYLE_REQUESTED;
+}
+
+/**
+ * st_theme_node_get_text_decoration
+ * @node: a #StThemeNode
+ *
+ * Get the text decoration for @node (eg. underline, line-through, etc).
+ *
+ * Returns: the text decoration for @node
+ */
+StTextDecoration
+st_theme_node_get_text_decoration (StThemeNode *node)
+{
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), 0);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, "text-decoration") == 0)
+ {
+ CRTerm *term = decl->value;
+ StTextDecoration decoration = 0;
+
+ /* Specification is none | [ underline || overline || line-through || blink ] | inherit
+ *
+ * We're a bit more liberal, and for example treat 'underline none' as the same as
+ * none.
+ */
+ for (; term; term = term->next)
+ {
+ if (term->type != TERM_IDENT)
+ goto next_decl;
+
+ if (strcmp (term->content.str->stryng->str, "none") == 0)
+ {
+ return 0;
+ }
+ else if (strcmp (term->content.str->stryng->str, "inherit") == 0)
+ {
+ if (node->parent_node)
+ return st_theme_node_get_text_decoration (node->parent_node);
+ }
+ else if (strcmp (term->content.str->stryng->str, "underline") == 0)
+ {
+ decoration |= ST_TEXT_DECORATION_UNDERLINE;
+ }
+ else if (strcmp (term->content.str->stryng->str, "overline") == 0)
+ {
+ decoration |= ST_TEXT_DECORATION_OVERLINE;
+ }
+ else if (strcmp (term->content.str->stryng->str, "line-through") == 0)
+ {
+ decoration |= ST_TEXT_DECORATION_LINE_THROUGH;
+ }
+ else if (strcmp (term->content.str->stryng->str, "blink") == 0)
+ {
+ decoration |= ST_TEXT_DECORATION_BLINK;
+ }
+ else
+ {
+ goto next_decl;
+ }
+ }
+
+ return decoration;
+ }
+
+ next_decl:
+ ;
+ }
+
+ return 0;
+}
+
+/**
+ * st_theme_node_get_text_align:
+ * @node: a #StThemeNode
+ *
+ * Get the text alignment of @node.
+ *
+ * Returns: the alignment of text for @node
+ */
+StTextAlign
+st_theme_node_get_text_align(StThemeNode *node)
+{
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE(node), ST_TEXT_ALIGN_LEFT);
+
+ ensure_properties(node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp(decl->property->stryng->str, "text-align") == 0)
+ {
+ CRTerm *term = decl->value;
+
+ if (term->type != TERM_IDENT || term->next)
+ continue;
+
+ if (strcmp(term->content.str->stryng->str, "inherit") == 0)
+ {
+ if (node->parent_node)
+ return st_theme_node_get_text_align(node->parent_node);
+ return ST_TEXT_ALIGN_LEFT;
+ }
+ else if (strcmp(term->content.str->stryng->str, "left") == 0)
+ {
+ return ST_TEXT_ALIGN_LEFT;
+ }
+ else if (strcmp(term->content.str->stryng->str, "right") == 0)
+ {
+ return ST_TEXT_ALIGN_RIGHT;
+ }
+ else if (strcmp(term->content.str->stryng->str, "center") == 0)
+ {
+ return ST_TEXT_ALIGN_CENTER;
+ }
+ else if (strcmp(term->content.str->stryng->str, "justify") == 0)
+ {
+ return ST_TEXT_ALIGN_JUSTIFY;
+ }
+ }
+ }
+ if(node->parent_node)
+ return st_theme_node_get_text_align(node->parent_node);
+
+ if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
+ return ST_TEXT_ALIGN_RIGHT;
+ return ST_TEXT_ALIGN_LEFT;
+}
+
+/**
+ * st_theme_node_get_letter_spacing:
+ * @node: a #StThemeNode
+ *
+ * Gets the value for the letter-spacing style property, in physical pixels.
+ *
+ * Returns: the value of the letter-spacing property, if
+ * found, or zero if such property has not been found.
+ */
+gdouble
+st_theme_node_get_letter_spacing (StThemeNode *node)
+{
+ gdouble spacing = 0.;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), spacing);
+
+ ensure_properties (node);
+
+ st_theme_node_lookup_length (node, "letter-spacing", FALSE, &spacing);
+ return spacing;
+}
+
+static gboolean
+font_family_from_terms (CRTerm *term,
+ char **family)
+{
+ GString *family_string;
+ gboolean result = FALSE;
+ gboolean last_was_quoted = FALSE;
+
+ if (!term)
+ return FALSE;
+
+ family_string = g_string_new (NULL);
+
+ while (term)
+ {
+ if (term->type != TERM_STRING && term->type != TERM_IDENT)
+ {
+ goto out;
+ }
+
+ if (family_string->len > 0)
+ {
+ if (term->the_operator != COMMA && term->the_operator != NO_OP)
+ goto out;
+ /* Can concatenate two bare words, but not two quoted strings */
+ if ((term->the_operator == NO_OP && last_was_quoted) || term->type == TERM_STRING)
+ goto out;
+
+ if (term->the_operator == NO_OP)
+ g_string_append (family_string, " ");
+ else
+ g_string_append (family_string, ",");
+ }
+ else
+ {
+ if (term->the_operator != NO_OP)
+ goto out;
+ }
+
+ g_string_append (family_string, term->content.str->stryng->str);
+
+ term = term->next;
+ }
+
+ result = TRUE;
+
+ out:
+ if (result)
+ {
+ *family = g_string_free (family_string, FALSE);
+ return TRUE;
+ }
+ else
+ {
+ *family = g_string_free (family_string, TRUE);
+ return FALSE;
+ }
+}
+
+/* In points */
+static int font_sizes[] = {
+ 6 * 1024, /* xx-small */
+ 8 * 1024, /* x-small */
+ 10 * 1024, /* small */
+ 12 * 1024, /* medium */
+ 16 * 1024, /* large */
+ 20 * 1024, /* x-large */
+ 24 * 1024, /* xx-large */
+};
+
+static gboolean
+font_size_from_term (StThemeNode *node,
+ CRTerm *term,
+ double *size)
+{
+ if (term->type == TERM_IDENT)
+ {
+ double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
+ /* We work in integers to avoid double comparisons when converting back
+ * from a size in pixels to a logical size.
+ */
+ int size_points = (int)(0.5 + *size * (72. / resolution));
+
+ if (strcmp (term->content.str->stryng->str, "xx-small") == 0)
+ size_points = font_sizes[0];
+ else if (strcmp (term->content.str->stryng->str, "x-small") == 0)
+ size_points = font_sizes[1];
+ else if (strcmp (term->content.str->stryng->str, "small") == 0)
+ size_points = font_sizes[2];
+ else if (strcmp (term->content.str->stryng->str, "medium") == 0)
+ size_points = font_sizes[3];
+ else if (strcmp (term->content.str->stryng->str, "large") == 0)
+ size_points = font_sizes[4];
+ else if (strcmp (term->content.str->stryng->str, "x-large") == 0)
+ size_points = font_sizes[5];
+ else if (strcmp (term->content.str->stryng->str, "xx-large") == 0)
+ size_points = font_sizes[6];
+ else if (strcmp (term->content.str->stryng->str, "smaller") == 0)
+ {
+ /* Find the standard size equal to or smaller than the current size */
+ int i = 0;
+
+ while (i <= 6 && font_sizes[i] < size_points)
+ i++;
+
+ if (i > 6)
+ {
+ /* original size greater than any standard size */
+ size_points = (int)(0.5 + size_points / 1.2);
+ }
+ else
+ {
+ /* Go one smaller than that, if possible */
+ if (i > 0)
+ i--;
+
+ size_points = font_sizes[i];
+ }
+ }
+ else if (strcmp (term->content.str->stryng->str, "larger") == 0)
+ {
+ /* Find the standard size equal to or larger than the current size */
+ int i = 6;
+
+ while (i >= 0 && font_sizes[i] > size_points)
+ i--;
+
+ if (i < 0) /* original size smaller than any standard size */
+ i = 0;
+
+ /* Go one larger than that, if possible */
+ if (i < 6)
+ i++;
+
+ size_points = font_sizes[i];
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ *size = size_points * (resolution / 72.);
+ return TRUE;
+
+ }
+ else if (term->type == TERM_NUMBER && term->content.num->type == NUM_PERCENTAGE)
+ {
+ *size *= term->content.num->val / 100.;
+ return TRUE;
+ }
+ else if (get_length_from_term (node, term, TRUE, size) == VALUE_FOUND)
+ {
+ /* Convert from pixels to Pango units */
+ *size *= 1024;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+font_weight_from_term (CRTerm *term,
+ PangoWeight *weight,
+ gboolean *weight_absolute)
+{
+ if (term->type == TERM_NUMBER)
+ {
+ int weight_int;
+
+ /* The spec only allows numeric weights from 100-900, though Pango
+ * will handle any number. We just let anything through.
+ */
+ if (term->content.num->type != NUM_GENERIC)
+ return FALSE;
+
+ weight_int = (int)(0.5 + term->content.num->val);
+
+ *weight = weight_int;
+ *weight_absolute = TRUE;
+
+ }
+ else if (term->type == TERM_IDENT)
+ {
+ /* FIXME: handle INHERIT */
+
+ if (strcmp (term->content.str->stryng->str, "bold") == 0)
+ {
+ *weight = PANGO_WEIGHT_BOLD;
+ *weight_absolute = TRUE;
+ }
+ else if (strcmp (term->content.str->stryng->str, "normal") == 0)
+ {
+ *weight = PANGO_WEIGHT_NORMAL;
+ *weight_absolute = TRUE;
+ }
+ else if (strcmp (term->content.str->stryng->str, "bolder") == 0)
+ {
+ *weight = PANGO_WEIGHT_BOLD;
+ *weight_absolute = FALSE;
+ }
+ else if (strcmp (term->content.str->stryng->str, "lighter") == 0)
+ {
+ *weight = PANGO_WEIGHT_LIGHT;
+ *weight_absolute = FALSE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+font_style_from_term (CRTerm *term,
+ PangoStyle *style)
+{
+ if (term->type != TERM_IDENT)
+ return FALSE;
+
+ /* FIXME: handle INHERIT */
+
+ if (strcmp (term->content.str->stryng->str, "normal") == 0)
+ *style = PANGO_STYLE_NORMAL;
+ else if (strcmp (term->content.str->stryng->str, "oblique") == 0)
+ *style = PANGO_STYLE_OBLIQUE;
+ else if (strcmp (term->content.str->stryng->str, "italic") == 0)
+ *style = PANGO_STYLE_ITALIC;
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+font_variant_from_term (CRTerm *term,
+ PangoVariant *variant)
+{
+ if (term->type != TERM_IDENT)
+ return FALSE;
+
+ /* FIXME: handle INHERIT */
+
+ if (strcmp (term->content.str->stryng->str, "normal") == 0)
+ *variant = PANGO_VARIANT_NORMAL;
+ else if (strcmp (term->content.str->stryng->str, "small-caps") == 0)
+ *variant = PANGO_VARIANT_SMALL_CAPS;
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * st_theme_node_get_font:
+ * @node: a #StThemeNode
+ *
+ * Get the current font of @node as a #PangoFontDescription
+ *
+ * Returns: (transfer none): the current font
+ */
+const PangoFontDescription *
+st_theme_node_get_font (StThemeNode *node)
+{
+ /* Initialized despite _set flags to suppress compiler warnings */
+ PangoStyle font_style = PANGO_STYLE_NORMAL;
+ gboolean font_style_set = FALSE;
+ PangoVariant variant = PANGO_VARIANT_NORMAL;
+ gboolean variant_set = FALSE;
+ PangoWeight weight = PANGO_WEIGHT_NORMAL;
+ gboolean weight_absolute = TRUE;
+ gboolean weight_set = FALSE;
+ double size = 0.;
+ gboolean size_set = FALSE;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ char *family = NULL;
+ double parent_size;
+ int i;
+
+ if (node->font_desc)
+ return node->font_desc;
+
+ node->font_desc = pango_font_description_copy (get_parent_font (node));
+ parent_size = pango_font_description_get_size (node->font_desc);
+ if (!pango_font_description_get_size_is_absolute (node->font_desc))
+ {
+ double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
+ parent_size *= (resolution / 72.);
+ }
+
+ ensure_properties (node);
+
+ for (i = 0; i < node->n_properties; i++)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, "font") == 0)
+ {
+ PangoStyle tmp_style = PANGO_STYLE_NORMAL;
+ PangoVariant tmp_variant = PANGO_VARIANT_NORMAL;
+ PangoWeight tmp_weight = PANGO_WEIGHT_NORMAL;
+ gboolean tmp_weight_absolute = TRUE;
+ double tmp_size;
+ CRTerm *term = decl->value;
+
+ /* A font specification starts with node/variant/weight
+ * in any order. Each is allowed to be specified only once,
+ * but we don't enforce that.
+ */
+ for (; term; term = term->next)
+ {
+ if (font_style_from_term (term, &tmp_style))
+ continue;
+ if (font_variant_from_term (term, &tmp_variant))
+ continue;
+ if (font_weight_from_term (term, &tmp_weight, &tmp_weight_absolute))
+ continue;
+
+ break;
+ }
+
+ /* The size is mandatory */
+
+ if (term == NULL || term->type != TERM_NUMBER)
+ {
+ g_warning ("Size missing from font property");
+ continue;
+ }
+
+ tmp_size = parent_size;
+ if (!font_size_from_term (node, term, &tmp_size))
+ {
+ g_warning ("Couldn't parse size in font property");
+ continue;
+ }
+
+ term = term->next;
+
+ if (term != NULL && term->type && TERM_NUMBER && term->the_operator == DIVIDE)
+ {
+ /* Ignore line-height specification */
+ term = term->next;
+ }
+
+ /* the font family is mandatory - it is a comma-separated list of
+ * names.
+ */
+ if (!font_family_from_terms (term, &family))
+ {
+ g_warning ("Couldn't parse family in font property");
+ continue;
+ }
+
+ font_style = tmp_style;
+ font_style_set = TRUE;
+ weight = tmp_weight;
+ weight_absolute = tmp_weight_absolute;
+ weight_set = TRUE;
+ variant = tmp_variant;
+ variant_set = TRUE;
+
+ size = tmp_size;
+ size_set = TRUE;
+
+ }
+ else if (strcmp (decl->property->stryng->str, "font-family") == 0)
+ {
+ if (!font_family_from_terms (decl->value, &family))
+ {
+ g_warning ("Couldn't parse family in font property");
+ continue;
+ }
+ }
+ else if (strcmp (decl->property->stryng->str, "font-weight") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ continue;
+
+ if (font_weight_from_term (decl->value, &weight, &weight_absolute))
+ weight_set = TRUE;
+ }
+ else if (strcmp (decl->property->stryng->str, "font-style") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ continue;
+
+ if (font_style_from_term (decl->value, &font_style))
+ font_style_set = TRUE;
+ }
+ else if (strcmp (decl->property->stryng->str, "font-variant") == 0)
+ {
+ if (decl->value == NULL || decl->value->next != NULL)
+ continue;
+
+ if (font_variant_from_term (decl->value, &variant))
+ variant_set = TRUE;
+ }
+ else if (strcmp (decl->property->stryng->str, "font-size") == 0)
+ {
+ gdouble tmp_size;
+ if (decl->value == NULL || decl->value->next != NULL)
+ continue;
+
+ tmp_size = parent_size;
+ if (font_size_from_term (node, decl->value, &tmp_size))
+ {
+ size = tmp_size;
+ size_set = TRUE;
+ }
+ }
+ }
+
+ if (family)
+ {
+ pango_font_description_set_family (node->font_desc, family);
+ g_free (family);
+ }
+
+ if (size_set)
+ pango_font_description_set_absolute_size (node->font_desc, size);
+
+ if (weight_set)
+ {
+ if (!weight_absolute)
+ {
+ /* bolder/lighter are supposed to switch between available styles, but with
+ * font substitution, that gets to be a pretty fuzzy concept. So we use
+ * a fixed step of 200. (The spec says 100, but that might not take us from
+ * normal to bold.
+ */
+
+ PangoWeight old_weight = pango_font_description_get_weight (node->font_desc);
+ if (weight == PANGO_WEIGHT_BOLD)
+ weight = old_weight + 200;
+ else
+ weight = old_weight - 200;
+
+ if (weight < 100)
+ weight = 100;
+ if (weight > 900)
+ weight = 900;
+ }
+
+ pango_font_description_set_weight (node->font_desc, weight);
+ }
+
+ if (font_style_set)
+ pango_font_description_set_style (node->font_desc, font_style);
+ if (variant_set)
+ pango_font_description_set_variant (node->font_desc, variant);
+
+ return node->font_desc;
+}
+
+/**
+ * st_theme_node_get_font_features:
+ * @node: a #StThemeNode
+ *
+ * Get the CSS font-features for @node.
+ *
+ * Returns: (transfer full): font-features as a string
+ */
+gchar *
+st_theme_node_get_font_features (StThemeNode *node)
+{
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, "font-feature-settings") == 0)
+ {
+ CRTerm *term = decl->value;
+
+ if (!term->next && term->type == TERM_IDENT)
+ {
+ gchar *ident = term->content.str->stryng->str;
+
+ if (strcmp (ident, "inherit") == 0)
+ break;
+
+ if (strcmp (ident, "normal") == 0)
+ return NULL;
+ }
+
+ return (gchar *)cr_term_to_string (term);
+ }
+ }
+
+ return node->parent_node ? st_theme_node_get_font_features (node->parent_node) : NULL;
+}
+
+/**
+ * st_theme_node_get_border_image:
+ * @node: a #StThemeNode
+ *
+ * Gets the value for the border-image style property
+ *
+ * Returns: (transfer none): the border image, or %NULL
+ * if there is no border image.
+ */
+StBorderImage *
+st_theme_node_get_border_image (StThemeNode *node)
+{
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ if (node->border_image_computed)
+ return node->border_image;
+
+ node->border_image = NULL;
+ node->border_image_computed = TRUE;
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, "border-image") == 0)
+ {
+ CRTerm *term = decl->value;
+ CRStyleSheet *base_stylesheet;
+ int borders[4];
+ int n_borders = 0;
+ int j;
+
+ const char *url;
+ int border_top;
+ int border_right;
+ int border_bottom;
+ int border_left;
+
+ GFile *file;
+
+ /* Support border-image: none; to suppress a previously specified border image */
+ if (term_is_none (term))
+ {
+ if (term->next == NULL)
+ return NULL;
+ else
+ goto next_property;
+ }
+
+ /* First term must be the URL to the image */
+ if (term->type != TERM_URI)
+ goto next_property;
+
+ url = term->content.str->stryng->str;
+
+ term = term->next;
+
+ /* Followed by 0 to 4 numbers or percentages. *Not lengths*. The interpretation
+ * of a number is supposed to be pixels if the image is pixel based, otherwise CSS pixels.
+ */
+ for (j = 0; j < 4; j++)
+ {
+ if (term == NULL)
+ break;
+
+ if (term->type != TERM_NUMBER)
+ goto next_property;
+
+ if (term->content.num->type == NUM_GENERIC)
+ {
+ borders[n_borders] = (int)(0.5 + term->content.num->val);
+ n_borders++;
+ }
+ else if (term->content.num->type == NUM_PERCENTAGE)
+ {
+ /* This would be easiest to support if we moved image handling into StBorderImage */
+ g_warning ("Percentages not supported for border-image");
+ goto next_property;
+ }
+ else
+ goto next_property;
+
+ term = term->next;
+ }
+
+ switch (n_borders)
+ {
+ case 0:
+ border_top = border_right = border_bottom = border_left = 0;
+ break;
+ case 1:
+ border_top = border_right = border_bottom = border_left = borders[0];
+ break;
+ case 2:
+ border_top = border_bottom = borders[0];
+ border_left = border_right = borders[1];
+ break;
+ case 3:
+ border_top = borders[0];
+ border_left = border_right = borders[1];
+ border_bottom = borders[2];
+ break;
+ case 4:
+ default:
+ border_top = borders[0];
+ border_right = borders[1];
+ border_bottom = borders[2];
+ border_left = borders[3];
+ break;
+ }
+
+ if (decl->parent_statement != NULL)
+ base_stylesheet = decl->parent_statement->parent_sheet;
+ else
+ base_stylesheet = NULL;
+
+ file = _st_theme_resolve_url (node->theme, base_stylesheet, url);
+
+ if (file == NULL)
+ goto next_property;
+
+ node->border_image = st_border_image_new (file,
+ border_top, border_right, border_bottom, border_left,
+ node->cached_scale_factor);
+
+ g_object_unref (file);
+
+ return node->border_image;
+ }
+
+ next_property:
+ ;
+ }
+
+ return NULL;
+}
+
+/**
+ * st_theme_node_get_horizontal_padding:
+ * @node: a #StThemeNode
+ *
+ * Gets the total horizontal padding (left + right padding), in physical pixels.
+ *
+ * Returns: the total horizontal padding in physical pixels
+ */
+double
+st_theme_node_get_horizontal_padding (StThemeNode *node)
+{
+ double padding = 0.0;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), padding);
+
+ padding += st_theme_node_get_padding (node, ST_SIDE_LEFT);
+ padding += st_theme_node_get_padding (node, ST_SIDE_RIGHT);
+
+ return padding;
+}
+
+/**
+ * st_theme_node_get_vertical_padding:
+ * @node: a #StThemeNode
+ *
+ * Gets the total vertical padding (top + bottom padding), in physical pixels.
+ *
+ * Returns: the total vertical padding in physical pixels
+ */
+double
+st_theme_node_get_vertical_padding (StThemeNode *node)
+{
+ double padding = 0.0;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), padding);
+
+ padding += st_theme_node_get_padding (node, ST_SIDE_TOP);
+ padding += st_theme_node_get_padding (node, ST_SIDE_BOTTOM);
+
+ return padding;
+}
+
+void
+_st_theme_node_apply_margins (StThemeNode *node,
+ ClutterActor *actor)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ _st_theme_node_ensure_geometry (node);
+
+ clutter_actor_set_margin_left (actor, st_theme_node_get_margin(node, ST_SIDE_LEFT));
+ clutter_actor_set_margin_right (actor, st_theme_node_get_margin(node, ST_SIDE_RIGHT));
+ clutter_actor_set_margin_top (actor, st_theme_node_get_margin(node, ST_SIDE_TOP));
+ clutter_actor_set_margin_bottom (actor, st_theme_node_get_margin(node, ST_SIDE_BOTTOM));
+}
+
+static GetFromTermResult
+parse_shadow_property (StThemeNode *node,
+ CRDeclaration *decl,
+ ClutterColor *color,
+ gdouble *xoffset,
+ gdouble *yoffset,
+ gdouble *blur,
+ gdouble *spread,
+ gboolean *inset,
+ gboolean *is_none)
+{
+ GetFromTermResult result;
+ CRTerm *term;
+ int n_offsets = 0;
+ *is_none = FALSE;
+
+ /* default values */
+ color->red = 0x0; color->green = 0x0; color->blue = 0x0; color->alpha = 0xff;
+ *xoffset = 0.;
+ *yoffset = 0.;
+ *blur = 0.;
+ *spread = 0.;
+ *inset = FALSE;
+
+ /* The CSS3 draft of the box-shadow property[0] is a lot stricter
+ * regarding the order of terms:
+ * If the 'inset' keyword is specified, it has to be first or last,
+ * and the color may not be mixed with the lengths; while we parse
+ * length values in the correct order, we allow for arbitrary
+ * placement of the color and 'inset' keyword.
+ *
+ * [0] http://www.w3.org/TR/css3-background/#box-shadow
+ */
+ for (term = decl->value; term; term = term->next)
+ {
+ /* if we found "none", we're all set with the default values */
+ if (term_is_none (term)) {
+ *is_none = TRUE;
+ return VALUE_FOUND;
+ }
+
+ if (term->type == TERM_NUMBER)
+ {
+ gdouble value;
+ gdouble multiplier;
+
+ multiplier = (term->unary_op == MINUS_UOP) ? -1. : 1.;
+ result = get_length_from_term (node, term, FALSE, &value);
+
+ if (result == VALUE_INHERIT)
+ {
+ /* we only allow inherit on the line by itself */
+ if (n_offsets > 0)
+ return VALUE_NOT_FOUND;
+ else
+ return VALUE_INHERIT;
+ }
+ else if (result == VALUE_FOUND)
+ {
+ switch (n_offsets++)
+ {
+ case 0:
+ *xoffset = multiplier * value;
+ break;
+ case 1:
+ *yoffset = multiplier * value;
+ break;
+ case 2:
+ if (multiplier < 0)
+ g_warning ("Negative blur values are "
+ "not allowed");
+ *blur = value;
+ break;
+ case 3:
+ if (multiplier < 0)
+ g_warning ("Negative spread values are "
+ "not allowed");
+ *spread = value;
+ break;
+ default:
+ g_warning ("Ignoring excess values in shadow definition");
+ break;
+ }
+ continue;
+ }
+ }
+ else if (term->type == TERM_IDENT &&
+ strcmp (term->content.str->stryng->str, "inset") == 0)
+ {
+ *inset = TRUE;
+ continue;
+ }
+
+ result = get_color_from_term (node, term, color);
+
+ if (result == VALUE_INHERIT)
+ {
+ if (n_offsets > 0)
+ return VALUE_NOT_FOUND;
+ else
+ return VALUE_INHERIT;
+ }
+ else if (result == VALUE_FOUND)
+ {
+ continue;
+ }
+ }
+
+ /* The only required terms are the x and y offsets
+ */
+ if (n_offsets >= 2)
+ return VALUE_FOUND;
+ else
+ return VALUE_NOT_FOUND;
+}
+
+/**
+ * st_theme_node_lookup_shadow:
+ * @node: a #StThemeNode
+ * @property_name: The name of the shadow property
+ * @inherit: if %TRUE, if a value is not found for the property on the
+ * node, then it will be looked up on the parent node, and then on the
+ * parent's parent, and so forth. Note that if the property has a
+ * value of 'inherit' it will be inherited even if %FALSE is passed
+ * in for @inherit; this only affects the default behavior for inheritance.
+ * @shadow: (out): location to store the shadow
+ *
+ * If the property is not found, the value in the shadow variable will not
+ * be changed.
+ *
+ * Generically looks up a property containing a set of shadow values. When
+ * specific getters (like st_theme_node_get_box_shadow ()) exist, they
+ * should be used instead. They are cached, so more efficient, and have
+ * handling for shortcut properties and other details of CSS.
+ *
+ * See also st_theme_node_get_shadow(), which provides a simpler API.
+ *
+ * Returns: %TRUE if the property was found in the properties for this
+ * theme node (or in the properties of parent nodes when inheriting.), %FALSE
+ * if the property was not found, or was explicitly set to 'none'.
+ */
+gboolean
+st_theme_node_lookup_shadow (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ StShadow **shadow)
+{
+ ClutterColor color = { 0., };
+ gdouble xoffset = 0.;
+ gdouble yoffset = 0.;
+ gdouble blur = 0.;
+ gdouble spread = 0.;
+ gboolean inset = FALSE;
+ gboolean is_none = FALSE;
+
+ int i;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), FALSE);
+ g_return_val_if_fail (property_name != NULL, FALSE);
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+
+ if (strcmp (decl->property->stryng->str, property_name) == 0)
+ {
+ GetFromTermResult result = parse_shadow_property (node,
+ decl,
+ &color,
+ &xoffset,
+ &yoffset,
+ &blur,
+ &spread,
+ &inset,
+ &is_none);
+ if (result == VALUE_FOUND)
+ {
+ if (is_none)
+ return FALSE;
+
+ *shadow = st_shadow_new (&color,
+ xoffset, yoffset,
+ blur, spread,
+ inset);
+ return TRUE;
+ }
+ else if (result == VALUE_INHERIT)
+ {
+ if (node->parent_node)
+ return st_theme_node_lookup_shadow (node->parent_node,
+ property_name,
+ inherit,
+ shadow);
+ else
+ break;
+ }
+ }
+ }
+
+ if (inherit && node->parent_node)
+ return st_theme_node_lookup_shadow (node->parent_node,
+ property_name,
+ inherit,
+ shadow);
+
+ return FALSE;
+}
+
+/**
+ * st_theme_node_get_shadow:
+ * @node: a #StThemeNode
+ * @property_name: The name of the shadow property
+ *
+ * Generically looks up a property containing a set of shadow values. When
+ * specific getters (like st_theme_node_get_box_shadow()) exist, they
+ * should be used instead. They are cached, so more efficient, and have
+ * handling for shortcut properties and other details of CSS.
+ *
+ * Like st_theme_get_length(), this does not print a warning if the property is
+ * not found; it just returns %NULL
+ *
+ * See also st_theme_node_lookup_shadow (), which provides more options.
+ *
+ * Returns: (nullable) (transfer full): the shadow, or %NULL if the property was
+ * not found.
+ */
+StShadow *
+st_theme_node_get_shadow (StThemeNode *node,
+ const char *property_name)
+{
+ StShadow *shadow;
+
+ if (st_theme_node_lookup_shadow (node, property_name, FALSE, &shadow))
+ return shadow;
+ else
+ return NULL;
+}
+
+/**
+ * st_theme_node_get_box_shadow:
+ * @node: a #StThemeNode
+ *
+ * Gets the value for the box-shadow style property
+ *
+ * Returns: (nullable) (transfer none): the node's shadow, or %NULL
+ * if node has no shadow
+ */
+StShadow *
+st_theme_node_get_box_shadow (StThemeNode *node)
+{
+ StShadow *shadow;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ if (node->box_shadow_computed)
+ return node->box_shadow;
+
+ node->box_shadow = NULL;
+ node->box_shadow_computed = TRUE;
+
+ if (st_theme_node_lookup_shadow (node,
+ "box-shadow",
+ FALSE,
+ &shadow))
+ {
+ node->box_shadow = shadow;
+
+ return node->box_shadow;
+ }
+
+ return NULL;
+}
+
+/**
+ * st_theme_node_get_background_image_shadow:
+ * @node: a #StThemeNode
+ *
+ * Gets the value for the -st-background-image-shadow style property
+ *
+ * Returns: (nullable) (transfer none): the node's background image shadow, or
+ * %NULL if node has no such shadow
+ */
+StShadow *
+st_theme_node_get_background_image_shadow (StThemeNode *node)
+{
+ StShadow *shadow;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ if (node->background_image_shadow_computed)
+ return node->background_image_shadow;
+
+ node->background_image_shadow = NULL;
+ node->background_image_shadow_computed = TRUE;
+
+ if (st_theme_node_lookup_shadow (node,
+ "-st-background-image-shadow",
+ FALSE,
+ &shadow))
+ {
+ if (shadow->inset)
+ {
+ g_warning ("The -st-background-image-shadow property does not "
+ "support inset shadows");
+ st_shadow_unref (shadow);
+ shadow = NULL;
+ }
+
+ node->background_image_shadow = shadow;
+
+ return node->background_image_shadow;
+ }
+
+ return NULL;
+}
+
+/**
+ * st_theme_node_get_text_shadow:
+ * @node: a #StThemeNode
+ *
+ * Gets the value for the text-shadow style property
+ *
+ * Returns: (nullable) (transfer none): the node's text-shadow, or %NULL
+ * if node has no text-shadow
+ */
+StShadow *
+st_theme_node_get_text_shadow (StThemeNode *node)
+{
+ StShadow *result = NULL;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ if (node->text_shadow_computed)
+ return node->text_shadow;
+
+ ensure_properties (node);
+
+ if (!st_theme_node_lookup_shadow (node,
+ "text-shadow",
+ FALSE,
+ &result))
+ {
+ if (node->parent_node)
+ {
+ result = st_theme_node_get_text_shadow (node->parent_node);
+ if (result)
+ st_shadow_ref (result);
+ }
+ }
+
+ if (result && result->inset)
+ {
+ g_warning ("The text-shadow property does not support inset shadows");
+ st_shadow_unref (result);
+ result = NULL;
+ }
+
+ node->text_shadow = result;
+ node->text_shadow_computed = TRUE;
+
+ return result;
+}
+
+/**
+ * st_theme_node_get_icon_colors:
+ * @node: a #StThemeNode
+ *
+ * Gets the colors that should be used for colorizing symbolic icons according
+ * the style of this node.
+ *
+ * Returns: (transfer none): the icon colors to use for this theme node
+ */
+StIconColors *
+st_theme_node_get_icon_colors (StThemeNode *node)
+{
+ /* Foreground here will always be the same as st_theme_node_get_foreground_color(),
+ * but there's a loss of symmetry and little efficiency win if we try to exploit
+ * that. */
+
+ enum {
+ FOREGROUND = 1 << 0,
+ WARNING = 1 << 1,
+ ERROR = 1 << 2,
+ SUCCESS = 1 << 3
+ };
+
+ gboolean shared_with_parent;
+ int i;
+ ClutterColor color = { 0, };
+
+ guint still_need = FOREGROUND | WARNING | ERROR | SUCCESS;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ if (node->icon_colors)
+ return node->icon_colors;
+
+ if (node->parent_node)
+ {
+ node->icon_colors = st_theme_node_get_icon_colors (node->parent_node);
+ shared_with_parent = TRUE;
+ }
+ else
+ {
+ node->icon_colors = st_icon_colors_new ();
+ node->icon_colors->foreground = BLACK_COLOR;
+ node->icon_colors->warning = DEFAULT_WARNING_COLOR;
+ node->icon_colors->error = DEFAULT_ERROR_COLOR;
+ node->icon_colors->success = DEFAULT_SUCCESS_COLOR;
+ shared_with_parent = FALSE;
+ }
+
+ ensure_properties (node);
+
+ for (i = node->n_properties - 1; i >= 0 && still_need != 0; i--)
+ {
+ CRDeclaration *decl = node->properties[i];
+ GetFromTermResult result = VALUE_NOT_FOUND;
+ guint found = 0;
+
+ if ((still_need & FOREGROUND) != 0 &&
+ strcmp (decl->property->stryng->str, "color") == 0)
+ {
+ found = FOREGROUND;
+ result = get_color_from_term (node, decl->value, &color);
+ }
+ else if ((still_need & WARNING) != 0 &&
+ strcmp (decl->property->stryng->str, "warning-color") == 0)
+ {
+ found = WARNING;
+ result = get_color_from_term (node, decl->value, &color);
+ }
+ else if ((still_need & ERROR) != 0 &&
+ strcmp (decl->property->stryng->str, "error-color") == 0)
+ {
+ found = ERROR;
+ result = get_color_from_term (node, decl->value, &color);
+ }
+ else if ((still_need & SUCCESS) != 0 &&
+ strcmp (decl->property->stryng->str, "success-color") == 0)
+ {
+ found = SUCCESS;
+ result = get_color_from_term (node, decl->value, &color);
+ }
+
+ if (result == VALUE_INHERIT)
+ {
+ still_need &= ~found;
+ }
+ else if (result == VALUE_FOUND)
+ {
+ still_need &= ~found;
+ if (shared_with_parent)
+ {
+ node->icon_colors = st_icon_colors_copy (node->icon_colors);
+ shared_with_parent = FALSE;
+ }
+
+ switch (found)
+ {
+ case FOREGROUND:
+ node->icon_colors->foreground = color;
+ break;
+ case WARNING:
+ node->icon_colors->warning = color;
+ break;
+ case ERROR:
+ node->icon_colors->error = color;
+ break;
+ case SUCCESS:
+ node->icon_colors->success = color;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+ }
+
+ if (shared_with_parent)
+ st_icon_colors_ref (node->icon_colors);
+
+ return node->icon_colors;
+}
+
+static float
+get_width_inc (StThemeNode *node)
+{
+ return ((int)(0.5 + node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT] +
+ (int)(0.5 + node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]);
+}
+
+static float
+get_height_inc (StThemeNode *node)
+{
+ return ((int)(0.5 + node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP] +
+ (int)(0.5 + node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]);
+}
+
+/**
+ * st_theme_node_adjust_for_height:
+ * @node: a #StThemeNode
+ * @for_height: (inout): the "for height" to adjust
+ *
+ * Adjusts a "for height" passed to clutter_actor_get_preferred_width() to
+ * account for borders and padding. This is a convenience function meant
+ * to be called from a get_preferred_width() method of a #ClutterActor
+ * subclass. The value after adjustment is the height available for the actor's
+ * content.
+ */
+void
+st_theme_node_adjust_for_height (StThemeNode *node,
+ float *for_height)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+ g_return_if_fail (for_height != NULL);
+
+ if (*for_height >= 0)
+ {
+ float height_inc = get_height_inc (node);
+ *for_height = MAX (0, *for_height - height_inc);
+ }
+}
+
+/**
+ * st_theme_node_adjust_preferred_width:
+ * @node: a #StThemeNode
+ * @min_width_p: (inout) (nullable): the minimum width to adjust
+ * @natural_width_p: (inout): the natural width to adjust
+ *
+ * Adjusts the minimum and natural width computed for an actor by
+ * adding on the necessary space for borders and padding and taking
+ * into account any minimum or maximum width. This is a convenience
+ * function meant to be called from the get_preferred_width() method
+ * of a #ClutterActor subclass
+ */
+void
+st_theme_node_adjust_preferred_width (StThemeNode *node,
+ float *min_width_p,
+ float *natural_width_p)
+{
+ float width_inc;
+
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ _st_theme_node_ensure_geometry (node);
+
+ width_inc = get_width_inc (node);
+
+ if (min_width_p)
+ {
+ if (node->min_width != -1)
+ *min_width_p = node->min_width;
+ *min_width_p += width_inc;
+ }
+
+ if (natural_width_p)
+ {
+ if (node->width != -1)
+ *natural_width_p = MAX (*natural_width_p, node->width);
+ if (node->max_width != -1)
+ *natural_width_p = MIN (*natural_width_p, node->max_width);
+ *natural_width_p += width_inc;
+ }
+}
+
+/**
+ * st_theme_node_adjust_for_width:
+ * @node: a #StThemeNode
+ * @for_width: (inout): the "for width" to adjust
+ *
+ * Adjusts a "for width" passed to clutter_actor_get_preferred_height() to
+ * account for borders and padding. This is a convenience function meant
+ * to be called from a get_preferred_height() method of a #ClutterActor
+ * subclass. The value after adjustment is the width available for the actor's
+ * content.
+ */
+void
+st_theme_node_adjust_for_width (StThemeNode *node,
+ float *for_width)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+ g_return_if_fail (for_width != NULL);
+
+ if (*for_width >= 0)
+ {
+ float width_inc = get_width_inc (node);
+ *for_width = MAX (0, *for_width - width_inc);
+ }
+}
+
+/**
+ * st_theme_node_adjust_preferred_height:
+ * @node: a #StThemeNode
+ * @min_height_p: (inout) (nullable): the minimum height to adjust
+ * @natural_height_p: (inout): the natural height to adjust
+ *
+ * Adjusts the minimum and natural height computed for an actor by
+ * adding on the necessary space for borders and padding and taking
+ * into account any minimum or maximum height. This is a convenience
+ * function meant to be called from the get_preferred_height() method
+ * of a #ClutterActor subclass
+ */
+void
+st_theme_node_adjust_preferred_height (StThemeNode *node,
+ float *min_height_p,
+ float *natural_height_p)
+{
+ float height_inc;
+
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ _st_theme_node_ensure_geometry (node);
+
+ height_inc = get_height_inc (node);
+
+ if (min_height_p)
+ {
+ if (node->min_height != -1)
+ *min_height_p = node->min_height;
+ *min_height_p += height_inc;
+ }
+ if (natural_height_p)
+ {
+ if (node->height != -1)
+ *natural_height_p = MAX (*natural_height_p, node->height);
+ if (node->max_height != -1)
+ *natural_height_p = MIN (*natural_height_p, node->max_height);
+ *natural_height_p += height_inc;
+ }
+}
+
+/**
+ * st_theme_node_get_content_box:
+ * @node: a #StThemeNode
+ * @allocation: the box allocated to a #ClutterAlctor
+ * @content_box: (out caller-allocates): computed box occupied by the actor's content
+ *
+ * Gets the box within an actor's allocation that contents the content
+ * of an actor (excluding borders and padding). This is a convenience function
+ * meant to be used from the allocate() or paint() methods of a #ClutterActor
+ * subclass.
+ */
+void
+st_theme_node_get_content_box (StThemeNode *node,
+ const ClutterActorBox *allocation,
+ ClutterActorBox *content_box)
+{
+ double noncontent_left, noncontent_top, noncontent_right, noncontent_bottom;
+ double avail_width, avail_height, content_width, content_height;
+
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ _st_theme_node_ensure_geometry (node);
+
+ avail_width = allocation->x2 - allocation->x1;
+ avail_height = allocation->y2 - allocation->y1;
+
+ noncontent_left = node->border_width[ST_SIDE_LEFT] + node->padding[ST_SIDE_LEFT];
+ noncontent_top = node->border_width[ST_SIDE_TOP] + node->padding[ST_SIDE_TOP];
+ noncontent_right = node->border_width[ST_SIDE_RIGHT] + node->padding[ST_SIDE_RIGHT];
+ noncontent_bottom = node->border_width[ST_SIDE_BOTTOM] + node->padding[ST_SIDE_BOTTOM];
+
+ content_box->x1 = (int)(0.5 + noncontent_left);
+ content_box->y1 = (int)(0.5 + noncontent_top);
+
+ content_width = avail_width - noncontent_left - noncontent_right;
+ if (content_width < 0)
+ content_width = 0;
+ content_height = avail_height - noncontent_top - noncontent_bottom;
+ if (content_height < 0)
+ content_height = 0;
+
+ content_box->x2 = (int)(0.5 + content_box->x1 + content_width);
+ content_box->y2 = (int)(0.5 + content_box->y1 + content_height);
+}
+
+/**
+ * st_theme_node_get_background_paint_box:
+ * @node: a #StThemeNode
+ * @allocation: the box allocated to a #ClutterActor
+ * @paint_box: (out caller-allocates): computed box occupied when painting the actor's background
+ *
+ * Gets the box used to paint the actor's background, including the area
+ * occupied by properties which paint outside the actor's assigned allocation.
+ */
+void
+st_theme_node_get_background_paint_box (StThemeNode *node,
+ const ClutterActorBox *actor_box,
+ ClutterActorBox *paint_box)
+{
+ StShadow *background_image_shadow;
+ ClutterActorBox shadow_box;
+
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+ g_return_if_fail (actor_box != NULL);
+ g_return_if_fail (paint_box != NULL);
+
+ background_image_shadow = st_theme_node_get_background_image_shadow (node);
+
+ *paint_box = *actor_box;
+
+ if (!background_image_shadow)
+ return;
+
+ st_shadow_get_box (background_image_shadow, actor_box, &shadow_box);
+
+ paint_box->x1 = MIN (paint_box->x1, shadow_box.x1);
+ paint_box->x2 = MAX (paint_box->x2, shadow_box.x2);
+ paint_box->y1 = MIN (paint_box->y1, shadow_box.y1);
+ paint_box->y2 = MAX (paint_box->y2, shadow_box.y2);
+}
+
+/**
+ * st_theme_node_get_paint_box:
+ * @node: a #StThemeNode
+ * @allocation: the box allocated to a #ClutterActor
+ * @paint_box: (out caller-allocates): computed box occupied when painting the actor
+ *
+ * Gets the box used to paint the actor, including the area occupied
+ * by properties which paint outside the actor's assigned allocation.
+ * When painting @node to an offscreen buffer, this function can be
+ * used to determine the necessary size of the buffer.
+ */
+void
+st_theme_node_get_paint_box (StThemeNode *node,
+ const ClutterActorBox *actor_box,
+ ClutterActorBox *paint_box)
+{
+ StShadow *box_shadow;
+ ClutterActorBox shadow_box;
+ int outline_width;
+
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+ g_return_if_fail (actor_box != NULL);
+ g_return_if_fail (paint_box != NULL);
+
+ box_shadow = st_theme_node_get_box_shadow (node);
+ outline_width = st_theme_node_get_outline_width (node);
+
+ st_theme_node_get_background_paint_box (node, actor_box, paint_box);
+
+ if (!box_shadow && !outline_width)
+ return;
+
+ paint_box->x1 -= outline_width;
+ paint_box->x2 += outline_width;
+ paint_box->y1 -= outline_width;
+ paint_box->y2 += outline_width;
+
+ if (box_shadow)
+ {
+ st_shadow_get_box (box_shadow, actor_box, &shadow_box);
+
+ paint_box->x1 = MIN (paint_box->x1, shadow_box.x1);
+ paint_box->x2 = MAX (paint_box->x2, shadow_box.x2);
+ paint_box->y1 = MIN (paint_box->y1, shadow_box.y1);
+ paint_box->y2 = MAX (paint_box->y2, shadow_box.y2);
+ }
+}
+
+/**
+ * st_theme_node_geometry_equal:
+ * @node: a #StThemeNode
+ * @other: a different #StThemeNode
+ *
+ * Tests if two theme nodes have the same borders and padding; this can be
+ * used to optimize having to relayout when the style applied to a Clutter
+ * actor changes colors without changing the geometry.
+ *
+ * Returns: %TRUE if equal, %FALSE otherwise
+ */
+gboolean
+st_theme_node_geometry_equal (StThemeNode *node,
+ StThemeNode *other)
+{
+ StSide side;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), FALSE);
+
+ if (node == other)
+ return TRUE;
+
+ g_return_val_if_fail (ST_IS_THEME_NODE (other), FALSE);
+
+ if (node->cached_scale_factor != other->cached_scale_factor)
+ return FALSE;
+
+ _st_theme_node_ensure_geometry (node);
+ _st_theme_node_ensure_geometry (other);
+
+ for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++)
+ {
+ if (node->border_width[side] != other->border_width[side])
+ return FALSE;
+ if (node->padding[side] != other->padding[side])
+ return FALSE;
+ }
+
+ if (node->width != other->width || node->height != other->height)
+ return FALSE;
+ if (node->min_width != other->min_width || node->min_height != other->min_height)
+ return FALSE;
+ if (node->max_width != other->max_width || node->max_height != other->max_height)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * st_theme_node_paint_equal:
+ * @node: (nullable): a #StThemeNode
+ * @other: (nullable): a different #StThemeNode
+ *
+ * Check if st_theme_node_paint() will paint identically for @node as it does
+ * for @other. Note that in some cases this function may return %TRUE even
+ * if there is no visible difference in the painting.
+ *
+ * Returns: %TRUE if the two theme nodes paint identically. %FALSE if the
+ * two nodes potentially paint differently.
+ */
+gboolean
+st_theme_node_paint_equal (StThemeNode *node,
+ StThemeNode *other)
+{
+ StBorderImage *border_image, *other_border_image;
+ StShadow *shadow, *other_shadow;
+ int i;
+
+ /* Make sure NULL != NULL */
+ if (node == NULL || other == NULL)
+ return FALSE;
+
+ if (node == other)
+ return TRUE;
+
+ _st_theme_node_ensure_background (node);
+ _st_theme_node_ensure_background (other);
+
+ if (!clutter_color_equal (&node->background_color, &other->background_color))
+ return FALSE;
+
+ if (node->background_gradient_type != other->background_gradient_type)
+ return FALSE;
+
+ if (node->background_gradient_type != ST_GRADIENT_NONE &&
+ !clutter_color_equal (&node->background_gradient_end, &other->background_gradient_end))
+ return FALSE;
+
+ if ((node->background_image != NULL) &&
+ (other->background_image != NULL) &&
+ !g_file_equal (node->background_image, other->background_image))
+ return FALSE;
+
+ _st_theme_node_ensure_geometry (node);
+ _st_theme_node_ensure_geometry (other);
+
+ for (i = 0; i < 4; i++)
+ {
+ if (node->border_width[i] != other->border_width[i])
+ return FALSE;
+
+ if (node->border_width[i] > 0 &&
+ !clutter_color_equal (&node->border_color[i], &other->border_color[i]))
+ return FALSE;
+
+ if (node->border_radius[i] != other->border_radius[i])
+ return FALSE;
+ }
+
+ if (node->outline_width != other->outline_width)
+ return FALSE;
+
+ if (node->outline_width > 0 &&
+ !clutter_color_equal (&node->outline_color, &other->outline_color))
+ return FALSE;
+
+ border_image = st_theme_node_get_border_image (node);
+ other_border_image = st_theme_node_get_border_image (other);
+
+ if ((border_image == NULL) != (other_border_image == NULL))
+ return FALSE;
+
+ if (border_image != NULL && !st_border_image_equal (border_image, other_border_image))
+ return FALSE;
+
+ shadow = st_theme_node_get_box_shadow (node);
+ other_shadow = st_theme_node_get_box_shadow (other);
+
+ if ((shadow == NULL) != (other_shadow == NULL))
+ return FALSE;
+
+ if (shadow != NULL && !st_shadow_equal (shadow, other_shadow))
+ return FALSE;
+
+ shadow = st_theme_node_get_background_image_shadow (node);
+ other_shadow = st_theme_node_get_background_image_shadow (other);
+
+ if ((shadow == NULL) != (other_shadow == NULL))
+ return FALSE;
+
+ if (shadow != NULL && !st_shadow_equal (shadow, other_shadow))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * st_theme_node_to_string:
+ * @node: a #StThemeNode
+ *
+ * Serialize @node to a string of its #GType name, CSS ID, classes and
+ * pseudo-classes.
+ *
+ * Returns: the serialized theme node
+ */
+gchar *
+st_theme_node_to_string (StThemeNode *node)
+{
+ GString *desc;
+ gchar **it;
+
+ if (!node)
+ return g_strdup ("[null]");
+
+ desc = g_string_new (NULL);
+ g_string_append_printf (desc,
+ "[%p %s#%s",
+ node,
+ g_type_name (node->element_type),
+ node->element_id);
+
+ for (it = node->element_classes; it && *it; it++)
+ g_string_append_printf (desc, ".%s", *it);
+
+ for (it = node->pseudo_classes; it && *it; it++)
+ g_string_append_printf (desc, ":%s", *it);
+
+ g_string_append_c (desc, ']');
+
+ return g_string_free (desc, FALSE);
+}
diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h
new file mode 100644
index 0000000..520e29f
--- /dev/null
+++ b/src/st/st-theme-node.h
@@ -0,0 +1,368 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-node.h: style information for one node in a tree of themed objects
+ *
+ * Copyright 2008-2010 Red Hat, Inc.
+ * Copyright 2009, 2010 Florian Müllner
+ * Copyright 2010 Giovanni Campagna
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_THEME_NODE_H__
+#define __ST_THEME_NODE_H__
+
+#include <clutter/clutter.h>
+#include "st-border-image.h"
+#include "st-icon-colors.h"
+#include "st-shadow.h"
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:st-theme-node
+ * @short_description: style information for one node in a tree of themed objects
+ *
+ * A #StThemeNode represents the CSS style information (the set of CSS properties) for one
+ * node in a tree of themed objects. In typical usage, it represents the style information
+ * for a single #ClutterActor. A #StThemeNode is immutable: attributes such as the
+ * CSS classes for the node are passed in at construction. If the attributes of the node
+ * or any parent node change, the node should be discarded and a new node created.
+ * #StThemeNode has generic accessors to look up properties by name and specific
+ * accessors for standard CSS properties that add caching and handling of various
+ * details of the CSS specification. #StThemeNode also has convenience functions to help
+ * in implementing a #ClutterActor with borders and padding.
+ *
+ * Note that pixel measurements take the #StThemeContext:scale-factor into
+ * account so all values are in physical pixels, as opposed to logical pixels.
+ * Physical pixels correspond to actor sizes, not necessarily to pixels on
+ * display devices (eg. when `scale-monitor-framebuffer` is enabled).
+ */
+
+typedef struct _StTheme StTheme;
+typedef struct _StThemeContext StThemeContext;
+
+#define ST_TYPE_THEME_NODE (st_theme_node_get_type ())
+G_DECLARE_FINAL_TYPE (StThemeNode, st_theme_node, ST, THEME_NODE, GObject)
+
+/**
+ * StSide:
+ * @ST_SIDE_TOP: The top side.
+ * @ST_SIDE_RIGHT: The right side.
+ * @ST_SIDE_BOTTOM: The bottom side.
+ * @ST_SIDE_LEFT: The left side.
+ *
+ * Used to target a particular side of a #StThemeNode element.
+ */
+typedef enum {
+ ST_SIDE_TOP,
+ ST_SIDE_RIGHT,
+ ST_SIDE_BOTTOM,
+ ST_SIDE_LEFT
+} StSide;
+
+/**
+ * StCorner:
+ * @ST_CORNER_TOPLEFT: The top-right corner.
+ * @ST_CORNER_TOPRIGHT: The top-right corner.
+ * @ST_CORNER_BOTTOMRIGHT: The bottom-right corner.
+ * @ST_CORNER_BOTTOMLEFT: The bottom-left corner.
+ *
+ * Used to target a particular corner of a #StThemeNode element.
+ */
+typedef enum {
+ ST_CORNER_TOPLEFT,
+ ST_CORNER_TOPRIGHT,
+ ST_CORNER_BOTTOMRIGHT,
+ ST_CORNER_BOTTOMLEFT
+} StCorner;
+
+/* These are the CSS values; that doesn't mean we have to implement blink... */
+/**
+ * StTextDecoration:
+ * @ST_TEXT_DECORATION_: Text is underlined
+ * @ST_TEXT_DECORATION_OVERLINE: Text is overlined
+ * @ST_TEXT_DECORATION_LINE_THROUGH: Text is striked out
+ * @ST_TEXT_DECORATION_BLINK: Text blinks
+ *
+ * Flags used to determine the decoration of text.
+ *
+ * Not that neither %ST_TEXT_DECORATION_OVERLINE or %ST_TEXT_DECORATION_BLINK
+ * are implemented, currently.
+ */
+typedef enum {
+ ST_TEXT_DECORATION_UNDERLINE = 1 << 0,
+ ST_TEXT_DECORATION_OVERLINE = 1 << 1,
+ ST_TEXT_DECORATION_LINE_THROUGH = 1 << 2,
+ ST_TEXT_DECORATION_BLINK = 1 << 3
+} StTextDecoration;
+
+/**
+ * StTextAlign:
+ * @ST_TEXT_ALIGN_LEFT: Text is aligned at the beginning of the label.
+ * @ST_TEXT_ALIGN_CENTER: Text is aligned in the middle of the label.
+ * @ST_TEXT_ALIGN_RIGHT: Text is aligned at the end of the label.
+ * @ST_GRADIENT_JUSTIFY: Text is justified in the label.
+ *
+ * Used to align text in a label.
+ */
+typedef enum {
+ ST_TEXT_ALIGN_LEFT = PANGO_ALIGN_LEFT,
+ ST_TEXT_ALIGN_CENTER = PANGO_ALIGN_CENTER,
+ ST_TEXT_ALIGN_RIGHT = PANGO_ALIGN_RIGHT,
+ ST_TEXT_ALIGN_JUSTIFY
+} StTextAlign;
+
+/**
+ * StGradientType:
+ * @ST_GRADIENT_NONE: No gradient.
+ * @ST_GRADIENT_VERTICAL: A vertical gradient.
+ * @ST_GRADIENT_HORIZONTAL: A horizontal gradient.
+ * @ST_GRADIENT_RADIAL: Lookup the style requested in the icon name.
+ *
+ * Used to specify options when rendering gradients.
+ */
+typedef enum {
+ ST_GRADIENT_NONE,
+ ST_GRADIENT_VERTICAL,
+ ST_GRADIENT_HORIZONTAL,
+ ST_GRADIENT_RADIAL
+} StGradientType;
+
+/**
+ * StIconStyle:
+ * @ST_ICON_STYLE_REQUESTED: Lookup the style requested in the icon name.
+ * @ST_ICON_STYLE_REGULAR: Try to always load regular icons, even when symbolic
+ * icon names are given.
+ * @ST_ICON_STYLE_SYMBOLIC: Try to always load symbolic icons, even when regular
+ * icon names are given.
+ *
+ * Used to specify options when looking up icons.
+ */
+typedef enum {
+ ST_ICON_STYLE_REQUESTED,
+ ST_ICON_STYLE_REGULAR,
+ ST_ICON_STYLE_SYMBOLIC
+} StIconStyle;
+
+typedef struct _StThemeNodePaintState StThemeNodePaintState;
+
+struct _StThemeNodePaintState {
+ StThemeNode *node;
+
+ float alloc_width;
+ float alloc_height;
+
+ float box_shadow_width;
+ float box_shadow_height;
+
+ float resource_scale;
+
+ CoglPipeline *box_shadow_pipeline;
+ CoglPipeline *prerendered_texture;
+ CoglPipeline *prerendered_pipeline;
+ CoglPipeline *corner_material[4];
+};
+
+StThemeNode *st_theme_node_new (StThemeContext *context,
+ StThemeNode *parent_node, /* can be null */
+ StTheme *theme, /* can be null */
+ GType element_type,
+ const char *element_id,
+ const char *element_class,
+ const char *pseudo_class,
+ const char *inline_style);
+
+StThemeNode *st_theme_node_get_parent (StThemeNode *node);
+
+StTheme *st_theme_node_get_theme (StThemeNode *node);
+
+gboolean st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b);
+guint st_theme_node_hash (StThemeNode *node);
+
+GType st_theme_node_get_element_type (StThemeNode *node);
+const char *st_theme_node_get_element_id (StThemeNode *node);
+GStrv st_theme_node_get_element_classes (StThemeNode *node);
+GStrv st_theme_node_get_pseudo_classes (StThemeNode *node);
+
+/* Generic getters ... these are not cached so are less efficient. The other
+ * reason for adding the more specific version is that we can handle the
+ * details of the actual CSS rules, which can be complicated, especially
+ * for fonts
+ */
+gboolean st_theme_node_lookup_color (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ ClutterColor *color);
+gboolean st_theme_node_lookup_double (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ double *value);
+gboolean st_theme_node_lookup_length (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ gdouble *length);
+gboolean st_theme_node_lookup_time (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ gdouble *value);
+gboolean st_theme_node_lookup_shadow (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ StShadow **shadow);
+gboolean st_theme_node_lookup_url (StThemeNode *node,
+ const char *property_name,
+ gboolean inherit,
+ GFile **file);
+
+/* Easier-to-use variants of the above, for application-level use */
+void st_theme_node_get_color (StThemeNode *node,
+ const char *property_name,
+ ClutterColor *color);
+gdouble st_theme_node_get_double (StThemeNode *node,
+ const char *property_name);
+gdouble st_theme_node_get_length (StThemeNode *node,
+ const char *property_name);
+StShadow *st_theme_node_get_shadow (StThemeNode *node,
+ const char *property_name);
+GFile *st_theme_node_get_url (StThemeNode *node,
+ const char *property_name);
+
+/* Specific getters for particular properties: cached
+ */
+void st_theme_node_get_background_color (StThemeNode *node,
+ ClutterColor *color);
+void st_theme_node_get_foreground_color (StThemeNode *node,
+ ClutterColor *color);
+void st_theme_node_get_background_gradient (StThemeNode *node,
+ StGradientType *type,
+ ClutterColor *start,
+ ClutterColor *end);
+
+GFile *st_theme_node_get_background_image (StThemeNode *node);
+
+int st_theme_node_get_border_width (StThemeNode *node,
+ StSide side);
+int st_theme_node_get_border_radius (StThemeNode *node,
+ StCorner corner);
+void st_theme_node_get_border_color (StThemeNode *node,
+ StSide side,
+ ClutterColor *color);
+
+int st_theme_node_get_outline_width (StThemeNode *node);
+void st_theme_node_get_outline_color (StThemeNode *node,
+ ClutterColor *color);
+
+double st_theme_node_get_padding (StThemeNode *node,
+ StSide side);
+
+double st_theme_node_get_horizontal_padding (StThemeNode *node);
+double st_theme_node_get_vertical_padding (StThemeNode *node);
+
+double st_theme_node_get_margin (StThemeNode *node,
+ StSide side);
+
+int st_theme_node_get_width (StThemeNode *node);
+int st_theme_node_get_height (StThemeNode *node);
+int st_theme_node_get_min_width (StThemeNode *node);
+int st_theme_node_get_min_height (StThemeNode *node);
+int st_theme_node_get_max_width (StThemeNode *node);
+int st_theme_node_get_max_height (StThemeNode *node);
+
+int st_theme_node_get_transition_duration (StThemeNode *node);
+
+StIconStyle st_theme_node_get_icon_style (StThemeNode *node);
+
+StTextDecoration st_theme_node_get_text_decoration (StThemeNode *node);
+
+StTextAlign st_theme_node_get_text_align (StThemeNode *node);
+
+double st_theme_node_get_letter_spacing (StThemeNode *node);
+
+/* Font rule processing is pretty complicated, so we just hardcode it
+ * under the standard font/font-family/font-size/etc names. This means
+ * you can't have multiple separate styled fonts for a single item,
+ * but that should be OK.
+ */
+const PangoFontDescription *st_theme_node_get_font (StThemeNode *node);
+
+gchar *st_theme_node_get_font_features (StThemeNode *node);
+
+StBorderImage *st_theme_node_get_border_image (StThemeNode *node);
+StShadow *st_theme_node_get_box_shadow (StThemeNode *node);
+StShadow *st_theme_node_get_text_shadow (StThemeNode *node);
+
+StShadow *st_theme_node_get_background_image_shadow (StThemeNode *node);
+
+StIconColors *st_theme_node_get_icon_colors (StThemeNode *node);
+
+/* Helpers for get_preferred_width()/get_preferred_height() ClutterActor vfuncs */
+void st_theme_node_adjust_for_height (StThemeNode *node,
+ float *for_height);
+void st_theme_node_adjust_preferred_width (StThemeNode *node,
+ float *min_width_p,
+ float *natural_width_p);
+void st_theme_node_adjust_for_width (StThemeNode *node,
+ float *for_width);
+void st_theme_node_adjust_preferred_height (StThemeNode *node,
+ float *min_height_p,
+ float *natural_height_p);
+
+/* Helper for allocate() ClutterActor vfunc */
+void st_theme_node_get_content_box (StThemeNode *node,
+ const ClutterActorBox *allocation,
+ ClutterActorBox *content_box);
+/* Helper for StThemeNodeTransition */
+void st_theme_node_get_paint_box (StThemeNode *node,
+ const ClutterActorBox *allocation,
+ ClutterActorBox *paint_box);
+/* Helper for background prerendering */
+void st_theme_node_get_background_paint_box (StThemeNode *node,
+ const ClutterActorBox *allocation,
+ ClutterActorBox *paint_box);
+
+gboolean st_theme_node_geometry_equal (StThemeNode *node,
+ StThemeNode *other);
+gboolean st_theme_node_paint_equal (StThemeNode *node,
+ StThemeNode *other);
+
+/**
+ * st_theme_node_paint: (skip)
+ */
+void st_theme_node_paint (StThemeNode *node,
+ StThemeNodePaintState *state,
+ CoglFramebuffer *framebuffer,
+ const ClutterActorBox *box,
+ guint8 paint_opacity,
+ float resource_scale);
+
+void st_theme_node_invalidate_background_image (StThemeNode *node);
+void st_theme_node_invalidate_border_image (StThemeNode *node);
+
+gchar * st_theme_node_to_string (StThemeNode *node);
+
+void st_theme_node_paint_state_init (StThemeNodePaintState *state);
+void st_theme_node_paint_state_free (StThemeNodePaintState *state);
+void st_theme_node_paint_state_copy (StThemeNodePaintState *state,
+ StThemeNodePaintState *other);
+void st_theme_node_paint_state_invalidate (StThemeNodePaintState *state);
+gboolean st_theme_node_paint_state_invalidate_for_file (StThemeNodePaintState *state,
+ GFile *file);
+
+void st_theme_node_paint_state_set_node (StThemeNodePaintState *state,
+ StThemeNode *node);
+
+G_END_DECLS
+
+#endif /* __ST_THEME_NODE_H__ */
diff --git a/src/st/st-theme-private.h b/src/st/st-theme-private.h
new file mode 100644
index 0000000..2083a7c
--- /dev/null
+++ b/src/st/st-theme-private.h
@@ -0,0 +1,41 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-private.h: Private StThemeMethods
+ *
+ * Copyright 2008, 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST_THEME_PRIVATE_H__
+#define __ST_THEME_PRIVATE_H__
+
+#include "croco/libcroco.h"
+#include "st-theme.h"
+
+G_BEGIN_DECLS
+
+GPtrArray *_st_theme_get_matched_properties (StTheme *theme,
+ StThemeNode *node);
+
+/* Resolve an URL from the stylesheet to a file */
+GFile *_st_theme_resolve_url (StTheme *theme,
+ CRStyleSheet *base_stylesheet,
+ const char *url);
+
+CRDeclaration *_st_theme_parse_declaration_list (const char *str);
+
+G_END_DECLS
+
+#endif /* __ST_THEME_PRIVATE_H__ */
diff --git a/src/st/st-theme.c b/src/st/st-theme.c
new file mode 100644
index 0000000..20d42fd
--- /dev/null
+++ b/src/st/st-theme.c
@@ -0,0 +1,1085 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme.c: A set of CSS stylesheets used for rule matching
+ *
+ * Copyright 2003-2004 Dodji Seketeli
+ * Copyright 2008, 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file started as a cut-and-paste of cr-sel-eng.c from libcroco.
+ *
+ * In moving it to hippo-canvas:
+ * - Reformatted and otherwise edited to match our coding style
+ * - Switched from handling xmlNode to handling HippoStyle
+ * - Simplified by removing things that we don't need or that don't
+ * make sense in our context.
+ * - The code to get a list of matching properties works quite differently;
+ * we order things in priority order, but we don't actually try to
+ * coalesce properties with the same name.
+ *
+ * In moving it to GNOME Shell:
+ * - Renamed again to StTheme
+ * - Reformatted to match the gnome-shell coding style
+ * - Removed notion of "theme engine" from hippo-canvas
+ * - pseudo-class matching changed from link enum to strings
+ * - Some code simplification
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "st-private.h"
+#include "st-theme-node.h"
+#include "st-theme-private.h"
+
+static void st_theme_constructed (GObject *object);
+static void st_theme_finalize (GObject *object);
+static void st_theme_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void st_theme_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+struct _StTheme
+{
+ GObject parent;
+
+ GFile *application_stylesheet;
+ GFile *default_stylesheet;
+ GFile *theme_stylesheet;
+ GSList *custom_stylesheets;
+
+ GHashTable *stylesheets_by_file;
+ GHashTable *files_by_stylesheet;
+
+ CRCascade *cascade;
+};
+
+enum
+{
+ PROP_0,
+ PROP_APPLICATION_STYLESHEET,
+ PROP_THEME_STYLESHEET,
+ PROP_DEFAULT_STYLESHEET
+};
+
+enum
+{
+ STYLESHEETS_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (StTheme, st_theme, G_TYPE_OBJECT)
+
+/* Quick strcmp. Test only for == 0 or != 0, not < 0 or > 0. */
+#define strqcmp(str,lit,lit_len) \
+ (strlen (str) != (lit_len) || memcmp (str, lit, lit_len))
+
+static gboolean
+file_equal0 (GFile *file1,
+ GFile *file2)
+{
+ if (file1 == file2)
+ return TRUE;
+
+ if ((file1 == NULL) || (file2 == NULL))
+ return FALSE;
+
+ return g_file_equal (file1, file2);
+}
+
+static void
+st_theme_init (StTheme *theme)
+{
+ theme->stylesheets_by_file = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
+ (GDestroyNotify)g_object_unref, (GDestroyNotify)cr_stylesheet_unref);
+ theme->files_by_stylesheet = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void
+st_theme_class_init (StThemeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = st_theme_constructed;
+ object_class->finalize = st_theme_finalize;
+ object_class->set_property = st_theme_set_property;
+ object_class->get_property = st_theme_get_property;
+
+ /**
+ * StTheme:application-stylesheet:
+ *
+ * The highest priority stylesheet, representing application-specific
+ * styling; this is associated with the CSS "author" stylesheet.
+ */
+ g_object_class_install_property (object_class,
+ PROP_APPLICATION_STYLESHEET,
+ g_param_spec_object ("application-stylesheet",
+ "Application Stylesheet",
+ "Stylesheet with application-specific styling",
+ G_TYPE_FILE,
+ ST_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * StTheme:theme-stylesheet:
+ *
+ * The second priority stylesheet, representing theme-specific styling;
+ * this is associated with the CSS "user" stylesheet.
+ */
+ g_object_class_install_property (object_class,
+ PROP_THEME_STYLESHEET,
+ g_param_spec_object ("theme-stylesheet",
+ "Theme Stylesheet",
+ "Stylesheet with theme-specific styling",
+ G_TYPE_FILE,
+ ST_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * StTheme:default-stylesheet:
+ *
+ * The lowest priority stylesheet, representing global default
+ * styling; this is associated with the CSS "user agent" stylesheet.
+ */
+ g_object_class_install_property (object_class,
+ PROP_DEFAULT_STYLESHEET,
+ g_param_spec_object ("default-stylesheet",
+ "Default Stylesheet",
+ "Stylesheet with global default styling",
+ G_TYPE_FILE,
+ ST_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ signals[STYLESHEETS_CHANGED] =
+ g_signal_new ("custom-stylesheets-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, /* no default handler slot */
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static CRStyleSheet *
+parse_stylesheet (GFile *file,
+ GError **error)
+{
+ enum CRStatus status;
+ CRStyleSheet *stylesheet;
+ char *contents;
+ gsize length;
+
+ if (file == NULL)
+ return NULL;
+
+ if (!g_file_load_contents (file, NULL, &contents, &length, NULL, error))
+ return NULL;
+
+ status = cr_om_parser_simply_parse_buf ((const guchar *) contents,
+ length,
+ CR_UTF_8,
+ &stylesheet);
+ g_free (contents);
+
+ if (status != CR_OK)
+ {
+ char *uri = g_file_get_uri (file);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Error parsing stylesheet '%s'; errcode:%d", uri, status);
+ g_free (uri);
+ return NULL;
+ }
+
+ /* Extension stylesheet */
+ stylesheet->app_data = GUINT_TO_POINTER (FALSE);
+
+ return stylesheet;
+}
+
+CRDeclaration *
+_st_theme_parse_declaration_list (const char *str)
+{
+ return cr_declaration_parse_list_from_buf ((const guchar *)str,
+ CR_UTF_8);
+}
+
+/* Just g_warning for now until we have something nicer to do */
+static CRStyleSheet *
+parse_stylesheet_nofail (GFile *file)
+{
+ GError *error = NULL;
+ CRStyleSheet *result;
+
+ result = parse_stylesheet (file, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+ return result;
+}
+
+static void
+insert_stylesheet (StTheme *theme,
+ GFile *file,
+ CRStyleSheet *stylesheet)
+{
+ if (stylesheet == NULL)
+ return;
+
+ g_object_ref (file);
+ cr_stylesheet_ref (stylesheet);
+
+ g_hash_table_insert (theme->stylesheets_by_file, file, stylesheet);
+ g_hash_table_insert (theme->files_by_stylesheet, stylesheet, file);
+}
+
+/**
+ * st_theme_load_stylesheet:
+ * @theme: a #StTheme
+ * @file: a #GFile
+ * @error: a #GError
+ *
+ * Load the stylesheet associated with @file.
+ *
+ * Returns: %TRUE if successful
+ */
+gboolean
+st_theme_load_stylesheet (StTheme *theme,
+ GFile *file,
+ GError **error)
+{
+ CRStyleSheet *stylesheet;
+
+ stylesheet = parse_stylesheet (file, error);
+ if (!stylesheet)
+ return FALSE;
+
+ stylesheet->app_data = GUINT_TO_POINTER (TRUE);
+
+ insert_stylesheet (theme, file, stylesheet);
+ cr_stylesheet_ref (stylesheet);
+ theme->custom_stylesheets = g_slist_prepend (theme->custom_stylesheets, stylesheet);
+ g_signal_emit (theme, signals[STYLESHEETS_CHANGED], 0);
+
+ return TRUE;
+}
+
+/**
+ * st_theme_unload_stylesheet:
+ * @theme: a #StTheme
+ * @file: a #GFile
+ *
+ * Unload the stylesheet associated with @file. If @file was not loaded this
+ * function does nothing.
+ */
+void
+st_theme_unload_stylesheet (StTheme *theme,
+ GFile *file)
+{
+ CRStyleSheet *stylesheet;
+
+ stylesheet = g_hash_table_lookup (theme->stylesheets_by_file, file);
+ if (!stylesheet)
+ return;
+
+ if (!g_slist_find (theme->custom_stylesheets, stylesheet))
+ return;
+
+ theme->custom_stylesheets = g_slist_remove (theme->custom_stylesheets, stylesheet);
+
+ g_signal_emit (theme, signals[STYLESHEETS_CHANGED], 0);
+
+ /* We need to remove the entry from the hashtable after emitting the signal
+ * since we might still access the files_by_stylesheet hashtable in
+ * _st_theme_resolve_url() during the signal emission.
+ */
+ g_hash_table_remove (theme->stylesheets_by_file, file);
+ g_hash_table_remove (theme->files_by_stylesheet, stylesheet);
+ cr_stylesheet_unref (stylesheet);
+}
+
+/**
+ * st_theme_get_custom_stylesheets:
+ * @theme: an #StTheme
+ *
+ * Get a list of the stylesheet files loaded with st_theme_load_stylesheet().
+ *
+ * Returns: (transfer full) (element-type GFile): the list of stylesheet files
+ * that were loaded with st_theme_load_stylesheet()
+ */
+GSList*
+st_theme_get_custom_stylesheets (StTheme *theme)
+{
+ GSList *result = NULL;
+ GSList *iter;
+
+ for (iter = theme->custom_stylesheets; iter; iter = iter->next)
+ {
+ CRStyleSheet *stylesheet = iter->data;
+ GFile *file = g_hash_table_lookup (theme->files_by_stylesheet, stylesheet);
+
+ result = g_slist_prepend (result, g_object_ref (file));
+ }
+
+ return result;
+}
+
+static void
+st_theme_constructed (GObject *object)
+{
+ StTheme *theme = ST_THEME (object);
+ CRStyleSheet *application_stylesheet;
+ CRStyleSheet *theme_stylesheet;
+ CRStyleSheet *default_stylesheet;
+
+ G_OBJECT_CLASS (st_theme_parent_class)->constructed (object);
+
+ application_stylesheet = parse_stylesheet_nofail (theme->application_stylesheet);
+ theme_stylesheet = parse_stylesheet_nofail (theme->theme_stylesheet);
+ default_stylesheet = parse_stylesheet_nofail (theme->default_stylesheet);
+
+ theme->cascade = cr_cascade_new (application_stylesheet,
+ theme_stylesheet,
+ default_stylesheet);
+
+ if (theme->cascade == NULL)
+ g_error ("Out of memory when creating cascade object");
+
+ insert_stylesheet (theme, theme->application_stylesheet, application_stylesheet);
+ insert_stylesheet (theme, theme->theme_stylesheet, theme_stylesheet);
+ insert_stylesheet (theme, theme->default_stylesheet, default_stylesheet);
+}
+
+static void
+st_theme_finalize (GObject * object)
+{
+ StTheme *theme = ST_THEME (object);
+
+ g_slist_foreach (theme->custom_stylesheets, (GFunc) cr_stylesheet_unref, NULL);
+ g_slist_free (theme->custom_stylesheets);
+ theme->custom_stylesheets = NULL;
+
+ g_hash_table_destroy (theme->stylesheets_by_file);
+ g_hash_table_destroy (theme->files_by_stylesheet);
+
+ g_clear_object (&theme->application_stylesheet);
+ g_clear_object (&theme->theme_stylesheet);
+ g_clear_object (&theme->default_stylesheet);
+
+ if (theme->cascade)
+ {
+ cr_cascade_unref (theme->cascade);
+ theme->cascade = NULL;
+ }
+
+ G_OBJECT_CLASS (st_theme_parent_class)->finalize (object);
+}
+
+static void
+st_theme_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StTheme *theme = ST_THEME (object);
+
+ switch (prop_id)
+ {
+ case PROP_APPLICATION_STYLESHEET:
+ {
+ GFile *file = g_value_get_object (value);
+
+ if (!file_equal0 (file, theme->application_stylesheet))
+ {
+ g_clear_object (&theme->application_stylesheet);
+ if (file != NULL)
+ theme->application_stylesheet = g_object_ref (file);
+ }
+
+ break;
+ }
+ case PROP_THEME_STYLESHEET:
+ {
+ GFile *file = g_value_get_object (value);
+
+ if (!file_equal0 (file, theme->theme_stylesheet))
+ {
+ g_clear_object (&theme->theme_stylesheet);
+ if (file != NULL)
+ theme->theme_stylesheet = g_object_ref (file);
+ }
+
+ break;
+ }
+ case PROP_DEFAULT_STYLESHEET:
+ {
+ GFile *file = g_value_get_object (value);
+
+ if (!file_equal0 (file, theme->default_stylesheet))
+ {
+ g_clear_object (&theme->default_stylesheet);
+ if (file != NULL)
+ theme->default_stylesheet = g_object_ref (file);
+ }
+
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_theme_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StTheme *theme = ST_THEME (object);
+
+ switch (prop_id)
+ {
+ case PROP_APPLICATION_STYLESHEET:
+ g_value_set_object (value, theme->application_stylesheet);
+ break;
+ case PROP_THEME_STYLESHEET:
+ g_value_set_object (value, theme->theme_stylesheet);
+ break;
+ case PROP_DEFAULT_STYLESHEET:
+ g_value_set_object (value, theme->default_stylesheet);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * st_theme_new:
+ * @application_stylesheet: The highest priority stylesheet, representing application-specific
+ * styling; this is associated with the CSS "author" stylesheet, may be %NULL
+ * @theme_stylesheet: The second priority stylesheet, representing theme-specific styling ;
+ * this is associated with the CSS "user" stylesheet, may be %NULL
+ * @default_stylesheet: The lowest priority stylesheet, representing global default styling;
+ * this is associated with the CSS "user agent" stylesheet, may be %NULL
+ *
+ * Returns: the newly created theme object
+ **/
+StTheme *
+st_theme_new (GFile *application_stylesheet,
+ GFile *theme_stylesheet,
+ GFile *default_stylesheet)
+{
+ StTheme *theme = g_object_new (ST_TYPE_THEME,
+ "application-stylesheet", application_stylesheet,
+ "theme-stylesheet", theme_stylesheet,
+ "default-stylesheet", default_stylesheet,
+ NULL);
+
+ return theme;
+}
+
+static gboolean
+string_in_list (GString *stryng,
+ GStrv list)
+{
+ gchar **it;
+
+ if (list == NULL)
+ return FALSE;
+
+ for (it = list; *it != NULL; it++)
+ {
+ if (!strqcmp (*it, stryng->str, stryng->len))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+pseudo_class_add_sel_matches_style (StTheme *a_this,
+ CRAdditionalSel *a_add_sel,
+ StThemeNode *a_node)
+{
+ GStrv node_pseudo_classes;
+
+ g_return_val_if_fail (a_this
+ && a_add_sel
+ && a_add_sel->content.pseudo
+ && a_add_sel->content.pseudo->name
+ && a_add_sel->content.pseudo->name->stryng
+ && a_add_sel->content.pseudo->name->stryng->str
+ && a_node, FALSE);
+
+ node_pseudo_classes = st_theme_node_get_pseudo_classes (a_node);
+
+ return string_in_list (a_add_sel->content.pseudo->name->stryng,
+ node_pseudo_classes);
+}
+
+/**
+ * class_add_sel_matches_style:
+ * @a_add_sel: The class additional selector to consider.
+ * @a_node: The style node to consider.
+ *
+ * Returns: %TRUE if the class additional selector matches
+ * the style node given in argument, %FALSE otherwise.
+ */
+static gboolean
+class_add_sel_matches_style (CRAdditionalSel *a_add_sel,
+ StThemeNode *a_node)
+{
+ GStrv element_classes;
+
+ g_return_val_if_fail (a_add_sel
+ && a_add_sel->type == CLASS_ADD_SELECTOR
+ && a_add_sel->content.class_name
+ && a_add_sel->content.class_name->stryng
+ && a_add_sel->content.class_name->stryng->str
+ && a_node, FALSE);
+
+ element_classes = st_theme_node_get_element_classes (a_node);
+
+ return string_in_list (a_add_sel->content.class_name->stryng,
+ element_classes);
+}
+
+/*
+ *@return TRUE if the additional attribute selector matches
+ *the current style node given in argument, FALSE otherwise.
+ *@param a_add_sel the additional attribute selector to consider.
+ *@param a_node the style node to consider.
+ */
+static gboolean
+id_add_sel_matches_style (CRAdditionalSel *a_add_sel,
+ StThemeNode *a_node)
+{
+ gboolean result = FALSE;
+ const char *id;
+
+ g_return_val_if_fail (a_add_sel
+ && a_add_sel->type == ID_ADD_SELECTOR
+ && a_add_sel->content.id_name
+ && a_add_sel->content.id_name->stryng
+ && a_add_sel->content.id_name->stryng->str
+ && a_node, FALSE);
+ g_return_val_if_fail (a_add_sel
+ && a_add_sel->type == ID_ADD_SELECTOR
+ && a_node, FALSE);
+
+ id = st_theme_node_get_element_id (a_node);
+
+ if (id != NULL)
+ {
+ if (!strqcmp (id, a_add_sel->content.id_name->stryng->str,
+ a_add_sel->content.id_name->stryng->len))
+ {
+ result = TRUE;
+ }
+ }
+
+ return result;
+}
+
+/**
+ *additional_selector_matches_style:
+ *Evaluates if a given additional selector matches an style node.
+ *@param a_add_sel the additional selector to consider.
+ *@param a_node the style node to consider.
+ *@return TRUE is a_add_sel matches a_node, FALSE otherwise.
+ */
+static gboolean
+additional_selector_matches_style (StTheme *a_this,
+ CRAdditionalSel *a_add_sel,
+ StThemeNode *a_node)
+{
+ CRAdditionalSel *cur_add_sel = NULL;
+
+ g_return_val_if_fail (a_add_sel, FALSE);
+
+ for (cur_add_sel = a_add_sel; cur_add_sel; cur_add_sel = cur_add_sel->next)
+ {
+ switch (cur_add_sel->type)
+ {
+ case NO_ADD_SELECTOR:
+ return FALSE;
+ case CLASS_ADD_SELECTOR:
+ if (!class_add_sel_matches_style (cur_add_sel, a_node))
+ return FALSE;
+ break;
+ case ID_ADD_SELECTOR:
+ if (!id_add_sel_matches_style (cur_add_sel, a_node))
+ return FALSE;
+ break;
+ case ATTRIBUTE_ADD_SELECTOR:
+ g_warning ("Attribute selectors not supported");
+ return FALSE;
+ case PSEUDO_CLASS_ADD_SELECTOR:
+ if (!pseudo_class_add_sel_matches_style (a_this, cur_add_sel, a_node))
+ return FALSE;
+ break;
+ default:
+ g_warning ("Unhandled selector type %d", cur_add_sel->type);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+element_name_matches_type (const char *element_name,
+ GType element_type)
+{
+ if (element_type == G_TYPE_NONE)
+ {
+ return strcmp (element_name, "stage") == 0;
+ }
+ else
+ {
+ GType match_type = g_type_from_name (element_name);
+ if (match_type == G_TYPE_INVALID)
+ return FALSE;
+
+ return g_type_is_a (element_type, match_type);
+ }
+}
+
+/*
+ *Evaluate a selector (a simple selectors list) and says
+ *if it matches the style node given in parameter.
+ *The algorithm used here is the following:
+ *Walk the combinator separated list of simple selectors backward, starting
+ *from the end of the list. For each simple selector, looks if
+ *if matches the current style.
+ *
+ *@param a_this the selection engine.
+ *@param a_sel the simple selection list.
+ *@param a_node the style node.
+ *@param a_result out parameter. Set to true if the
+ *selector matches the style node, FALSE otherwise.
+ *@param a_recurse if set to TRUE, the function will walk to
+ *the next simple selector (after the evaluation of the current one)
+ *and recursively evaluate it. Must be usually set to TRUE unless you
+ *know what you are doing.
+ */
+static enum CRStatus
+sel_matches_style_real (StTheme *a_this,
+ CRSimpleSel *a_sel,
+ StThemeNode *a_node,
+ gboolean *a_result,
+ gboolean a_eval_sel_list_from_end,
+ gboolean a_recurse)
+{
+ CRSimpleSel *cur_sel = NULL;
+ StThemeNode *cur_node = NULL;
+ GType cur_type;
+
+ *a_result = FALSE;
+
+ if (a_eval_sel_list_from_end)
+ {
+ /*go and get the last simple selector of the list */
+ for (cur_sel = a_sel; cur_sel && cur_sel->next; cur_sel = cur_sel->next)
+ ;
+ }
+ else
+ {
+ cur_sel = a_sel;
+ }
+
+ cur_node = a_node;
+ cur_type = st_theme_node_get_element_type (cur_node);
+
+ while (cur_sel)
+ {
+ if (((cur_sel->type_mask & TYPE_SELECTOR)
+ && (cur_sel->name
+ && cur_sel->name->stryng
+ && cur_sel->name->stryng->str)
+ &&
+ (element_name_matches_type (cur_sel->name->stryng->str, cur_type)))
+ || (cur_sel->type_mask & UNIVERSAL_SELECTOR))
+ {
+ /*
+ *this simple selector
+ *matches the current style node
+ *Let's see if the preceding
+ *simple selectors also match
+ *their style node counterpart.
+ */
+ if (cur_sel->add_sel)
+ {
+ if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
+ goto walk_a_step_in_expr;
+ else
+ goto done;
+ }
+ else
+ goto walk_a_step_in_expr;
+ }
+ if (!(cur_sel->type_mask & TYPE_SELECTOR)
+ && !(cur_sel->type_mask & UNIVERSAL_SELECTOR))
+ {
+ if (!cur_sel->add_sel)
+ goto done;
+ if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
+ goto walk_a_step_in_expr;
+ else
+ goto done;
+ }
+ else
+ {
+ goto done;
+ }
+
+ walk_a_step_in_expr:
+ if (a_recurse == FALSE)
+ {
+ *a_result = TRUE;
+ goto done;
+ }
+
+ /*
+ *here, depending on the combinator of cur_sel
+ *choose the axis of the element tree traversal
+ *and walk one step in the element tree.
+ */
+ if (!cur_sel->prev)
+ break;
+
+ switch (cur_sel->combinator)
+ {
+ case NO_COMBINATOR:
+ break;
+
+ case COMB_WS: /*descendant selector */
+ {
+ StThemeNode *n = NULL;
+
+ /*
+ *walk the element tree upward looking for a parent
+ *style that matches the preceding selector.
+ */
+ for (n = st_theme_node_get_parent (a_node); n; n = st_theme_node_get_parent (n))
+ {
+ enum CRStatus status;
+ gboolean matches = FALSE;
+
+ status = sel_matches_style_real (a_this, cur_sel->prev, n, &matches, FALSE, TRUE);
+
+ if (status != CR_OK)
+ goto done;
+
+ if (matches)
+ {
+ cur_node = n;
+ cur_type = st_theme_node_get_element_type (cur_node);
+ break;
+ }
+ }
+
+ if (!n)
+ {
+ /*
+ *didn't find any ancestor that matches
+ *the previous simple selector.
+ */
+ goto done;
+ }
+ /*
+ *in this case, the preceding simple sel
+ *will have been interpreted twice, which
+ *is a cpu and mem waste ... I need to find
+ *another way to do this. Anyway, this is
+ *my first attempt to write this function and
+ *I am a bit clueless.
+ */
+ break;
+ }
+
+ case COMB_PLUS:
+ g_warning ("+ combinators are not supported");
+ goto done;
+
+ case COMB_GT:
+ cur_node = st_theme_node_get_parent (cur_node);
+ if (!cur_node)
+ goto done;
+ cur_type = st_theme_node_get_element_type (cur_node);
+ break;
+
+ default:
+ goto done;
+ }
+
+ cur_sel = cur_sel->prev;
+ }
+
+ /*
+ *if we reached this point, it means the selector matches
+ *the style node.
+ */
+ *a_result = TRUE;
+
+done:
+ return CR_OK;
+}
+
+static void
+add_matched_properties (StTheme *a_this,
+ CRStyleSheet *a_nodesheet,
+ StThemeNode *a_node,
+ GPtrArray *props)
+{
+ CRStatement *cur_stmt = NULL;
+ CRSelector *sel_list = NULL;
+ CRSelector *cur_sel = NULL;
+ gboolean matches = FALSE;
+ enum CRStatus status = CR_OK;
+
+ /*
+ *walk through the list of statements and,
+ *get the selectors list inside the statements that
+ *contain some, and try to match our style node in these
+ *selectors lists.
+ */
+ for (cur_stmt = a_nodesheet->statements; cur_stmt; cur_stmt = cur_stmt->next)
+ {
+ /*
+ *initialize the selector list in which we will
+ *really perform the search.
+ */
+ sel_list = NULL;
+
+ /*
+ *get the the damn selector list in
+ *which we have to look
+ */
+ switch (cur_stmt->type)
+ {
+ case RULESET_STMT:
+ if (cur_stmt->kind.ruleset && cur_stmt->kind.ruleset->sel_list)
+ {
+ sel_list = cur_stmt->kind.ruleset->sel_list;
+ }
+ break;
+
+ case AT_IMPORT_RULE_STMT:
+ {
+ CRAtImportRule *import_rule = cur_stmt->kind.import_rule;
+
+ if (import_rule->sheet == NULL)
+ {
+ GFile *file = NULL;
+
+ if (import_rule->url->stryng && import_rule->url->stryng->str)
+ {
+ file = _st_theme_resolve_url (a_this,
+ a_nodesheet,
+ import_rule->url->stryng->str);
+ import_rule->sheet = parse_stylesheet (file, NULL);
+ }
+
+ if (import_rule->sheet)
+ {
+ insert_stylesheet (a_this, file, import_rule->sheet);
+ /* refcount of stylesheets starts off at zero, so we don't need to unref! */
+ }
+ else
+ {
+ /* Set a marker to avoid repeatedly trying to parse a non-existent or
+ * broken stylesheet
+ */
+ import_rule->sheet = (CRStyleSheet *) - 1;
+ }
+
+ if (file)
+ g_object_unref (file);
+ }
+
+ if (import_rule->sheet != (CRStyleSheet *) - 1)
+ {
+ add_matched_properties (a_this, import_rule->sheet,
+ a_node, props);
+ }
+ }
+ break;
+ case AT_MEDIA_RULE_STMT:
+ case AT_RULE_STMT:
+ case AT_PAGE_RULE_STMT:
+ case AT_CHARSET_RULE_STMT:
+ case AT_FONT_FACE_RULE_STMT:
+ default:
+ break;
+ }
+
+ if (!sel_list)
+ continue;
+
+ /*
+ *now, we have a comma separated selector list to look in.
+ *let's walk it and try to match the style node
+ *on each item of the list.
+ */
+ for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next)
+ {
+ if (!cur_sel->simple_sel)
+ continue;
+
+ status = sel_matches_style_real (a_this, cur_sel->simple_sel, a_node, &matches, TRUE, TRUE);
+
+ if (status == CR_OK && matches)
+ {
+ CRDeclaration *cur_decl = NULL;
+
+ /* In order to sort the matching properties, we need to compute the
+ * specificity of the selector that actually matched this
+ * element. In a non-thread-safe fashion, we store it in the
+ * ruleset. (Fixing this would mean cut-and-pasting
+ * cr_simple_sel_compute_specificity(), and have no need for
+ * thread-safety anyways.)
+ *
+ * Once we've sorted the properties, the specificity no longer
+ * matters and it can be safely overridden.
+ */
+ cr_simple_sel_compute_specificity (cur_sel->simple_sel);
+
+ cur_stmt->specificity = cur_sel->simple_sel->specificity;
+
+ for (cur_decl = cur_stmt->kind.ruleset->decl_list; cur_decl; cur_decl = cur_decl->next)
+ g_ptr_array_add (props, cur_decl);
+ }
+ }
+ }
+}
+
+#define ORIGIN_OFFSET_IMPORTANT (NB_ORIGINS)
+#define ORIGIN_OFFSET_EXTENSION (NB_ORIGINS * 2)
+
+static inline int
+get_origin (const CRDeclaration * decl)
+{
+ enum CRStyleOrigin origin = decl->parent_statement->parent_sheet->origin;
+ gboolean is_extension_sheet = GPOINTER_TO_UINT (decl->parent_statement->parent_sheet->app_data);
+
+ if (decl->important)
+ origin += ORIGIN_OFFSET_IMPORTANT;
+
+ if (is_extension_sheet)
+ origin += ORIGIN_OFFSET_EXTENSION;
+
+ return origin;
+}
+
+/* Order of comparison is so that higher priority statements compare after
+ * lower priority statements */
+static int
+compare_declarations (gconstpointer a,
+ gconstpointer b)
+{
+ /* g_ptr_array_sort() is broooken */
+ CRDeclaration *decl_a = *(CRDeclaration **) a;
+ CRDeclaration *decl_b = *(CRDeclaration **) b;
+
+ int origin_a = get_origin (decl_a);
+ int origin_b = get_origin (decl_b);
+
+ if (origin_a != origin_b)
+ return origin_a - origin_b;
+
+ if (decl_a->parent_statement->specificity != decl_b->parent_statement->specificity)
+ return decl_a->parent_statement->specificity - decl_b->parent_statement->specificity;
+
+ return 0;
+}
+
+GPtrArray *
+_st_theme_get_matched_properties (StTheme *theme,
+ StThemeNode *node)
+{
+ enum CRStyleOrigin origin = 0;
+ CRStyleSheet *sheet = NULL;
+ GPtrArray *props = g_ptr_array_new ();
+ GSList *iter;
+
+ g_return_val_if_fail (ST_IS_THEME (theme), NULL);
+ g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
+
+ for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++)
+ {
+ sheet = cr_cascade_get_sheet (theme->cascade, origin);
+ if (!sheet)
+ continue;
+
+ add_matched_properties (theme, sheet, node, props);
+ }
+
+ for (iter = theme->custom_stylesheets; iter; iter = iter->next)
+ add_matched_properties (theme, iter->data, node, props);
+
+ /* We count on a stable sort here so that later declarations come
+ * after earlier declarations */
+ g_ptr_array_sort (props, compare_declarations);
+
+ return props;
+}
+
+/* Resolve an url from an url() reference in a stylesheet into a GFile,
+ * if possible. The resolution here is distinctly lame and
+ * will fail on many examples.
+ */
+GFile *
+_st_theme_resolve_url (StTheme *theme,
+ CRStyleSheet *base_stylesheet,
+ const char *url)
+{
+ char *scheme;
+ GFile *resource;
+
+ if ((scheme = g_uri_parse_scheme (url)))
+ {
+ g_free (scheme);
+ resource = g_file_new_for_uri (url);
+ }
+ else if (base_stylesheet != NULL)
+ {
+ GFile *base_file = NULL, *parent;
+
+ base_file = g_hash_table_lookup (theme->files_by_stylesheet, base_stylesheet);
+
+ /* This is an internal function, if we get here with
+ a bad @base_stylesheet we have a problem. */
+ g_assert (base_file);
+
+ parent = g_file_get_parent (base_file);
+ resource = g_file_resolve_relative_path (parent, url);
+
+ g_object_unref (parent);
+ }
+ else
+ {
+ resource = g_file_new_for_path (url);
+ }
+
+ return resource;
+}
diff --git a/src/st/st-theme.h b/src/st/st-theme.h
new file mode 100644
index 0000000..d3f242c
--- /dev/null
+++ b/src/st/st-theme.h
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme.h: A set of CSS stylesheets used for rule matching
+ *
+ * Copyright 2008, 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ST_THEME_H__
+#define __ST_THEME_H__
+
+#include <glib-object.h>
+
+#include "st-theme-node.h"
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:st-theme
+ * @short_description: a set of stylesheets
+ *
+ * #StTheme holds a set of stylesheets. (The "cascade" of the name
+ * Cascading Stylesheets.) A #StTheme can be set to apply to all the actors
+ * in a stage using st_theme_context_set_theme().
+ */
+
+#define ST_TYPE_THEME (st_theme_get_type ())
+G_DECLARE_FINAL_TYPE (StTheme, st_theme, ST, THEME, GObject)
+
+StTheme *st_theme_new (GFile *application_stylesheet,
+ GFile *theme_stylesheet,
+ GFile *default_stylesheet);
+
+gboolean st_theme_load_stylesheet (StTheme *theme, GFile *file, GError **error);
+void st_theme_unload_stylesheet (StTheme *theme, GFile *file);
+GSList *st_theme_get_custom_stylesheets (StTheme *theme);
+
+G_END_DECLS
+
+#endif /* __ST_THEME_H__ */
diff --git a/src/st/st-types.h b/src/st/st-types.h
new file mode 100644
index 0000000..d204041
--- /dev/null
+++ b/src/st/st-types.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ST_TYPES_H__
+#define __ST_TYPES_H__
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:st-types
+ * @short_description: type definitions used throughout St
+ *
+ * Common types for StWidgets.
+ */
+
+typedef enum {
+ ST_ALIGN_START,
+ ST_ALIGN_MIDDLE,
+ ST_ALIGN_END
+} StAlign;
+
+typedef enum {
+ ST_BACKGROUND_SIZE_AUTO,
+ ST_BACKGROUND_SIZE_CONTAIN,
+ ST_BACKGROUND_SIZE_COVER,
+ ST_BACKGROUND_SIZE_FIXED
+} StBackgroundSize;
+
+G_END_DECLS
+
+#endif /* __ST_TYPES_H__ */
diff --git a/src/st/st-viewport.c b/src/st/st-viewport.c
new file mode 100644
index 0000000..e6b9127
--- /dev/null
+++ b/src/st/st-viewport.c
@@ -0,0 +1,600 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-scrollable-wiget.c: a scrollable actor
+ *
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2009 Abderrahim Kitouni
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2010 Florian Muellner
+ * Copyright 2019 Endless, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Portions copied from Clutter:
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2006 OpenedHand
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ */
+
+/**
+ * SECTION:st-viewport
+ * @short_description: a scrollable container
+ *
+ * The #StViewport is a generic #StScrollable implementation.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "st-viewport.h"
+
+#include "st-private.h"
+#include "st-scrollable.h"
+
+
+static void st_viewport_scrollable_interface_init (StScrollableInterface *iface);
+
+enum {
+ PROP_0,
+
+ PROP_CLIP_TO_VIEW,
+
+ N_PROPS,
+
+ /* StScrollable */
+ PROP_HADJUST,
+ PROP_VADJUST
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+typedef struct
+{
+ StAdjustment *hadjustment;
+ StAdjustment *vadjustment;
+ gboolean clip_to_view;
+} StViewportPrivate;
+
+G_DEFINE_TYPE_WITH_CODE (StViewport, st_viewport, ST_TYPE_WIDGET,
+ G_ADD_PRIVATE (StViewport)
+ G_IMPLEMENT_INTERFACE (ST_TYPE_SCROLLABLE,
+ st_viewport_scrollable_interface_init));
+
+/*
+ * StScrollable Interface Implementation
+ */
+static void
+adjustment_value_notify_cb (StAdjustment *adjustment,
+ GParamSpec *pspec,
+ StViewport *viewport)
+{
+ clutter_actor_invalidate_transform (CLUTTER_ACTOR (viewport));
+ clutter_actor_invalidate_paint_volume (CLUTTER_ACTOR (viewport));
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (viewport));
+}
+
+static void
+scrollable_set_adjustments (StScrollable *scrollable,
+ StAdjustment *hadjustment,
+ StAdjustment *vadjustment)
+{
+ StViewport *viewport = ST_VIEWPORT (scrollable);
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (viewport);
+
+ g_object_freeze_notify (G_OBJECT (scrollable));
+
+ if (hadjustment != priv->hadjustment)
+ {
+ if (priv->hadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->hadjustment,
+ adjustment_value_notify_cb,
+ scrollable);
+ g_object_unref (priv->hadjustment);
+ }
+
+ if (hadjustment)
+ {
+ g_object_ref (hadjustment);
+ g_signal_connect (hadjustment, "notify::value",
+ G_CALLBACK (adjustment_value_notify_cb),
+ scrollable);
+ }
+
+ priv->hadjustment = hadjustment;
+ g_object_notify (G_OBJECT (scrollable), "hadjustment");
+ }
+
+ if (vadjustment != priv->vadjustment)
+ {
+ if (priv->vadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->vadjustment,
+ adjustment_value_notify_cb,
+ scrollable);
+ g_object_unref (priv->vadjustment);
+ }
+
+ if (vadjustment)
+ {
+ g_object_ref (vadjustment);
+ g_signal_connect (vadjustment, "notify::value",
+ G_CALLBACK (adjustment_value_notify_cb),
+ scrollable);
+ }
+
+ priv->vadjustment = vadjustment;
+ g_object_notify (G_OBJECT (scrollable), "vadjustment");
+ }
+
+ g_object_thaw_notify (G_OBJECT (scrollable));
+}
+
+static void
+scrollable_get_adjustments (StScrollable *scrollable,
+ StAdjustment **hadjustment,
+ StAdjustment **vadjustment)
+{
+ StViewport *viewport = ST_VIEWPORT (scrollable);
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (viewport);
+
+ if (hadjustment)
+ *hadjustment = priv->hadjustment;
+
+ if (vadjustment)
+ *vadjustment = priv->vadjustment;
+}
+
+static void
+st_viewport_scrollable_interface_init (StScrollableInterface *iface)
+{
+ iface->set_adjustments = scrollable_set_adjustments;
+ iface->get_adjustments = scrollable_get_adjustments;
+}
+
+static void
+st_viewport_set_clip_to_view (StViewport *viewport,
+ gboolean clip_to_view)
+{
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (viewport);
+
+ if (!!priv->clip_to_view != !!clip_to_view)
+ {
+ priv->clip_to_view = clip_to_view;
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (viewport));
+ g_object_notify_by_pspec (G_OBJECT (viewport), props[PROP_CLIP_TO_VIEW]);
+ }
+}
+
+static void
+st_viewport_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (ST_VIEWPORT (object));
+ StAdjustment *adjustment;
+
+ switch (property_id)
+ {
+ case PROP_HADJUST:
+ scrollable_get_adjustments (ST_SCROLLABLE (object), &adjustment, NULL);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_VADJUST:
+ scrollable_get_adjustments (ST_SCROLLABLE (object), NULL, &adjustment);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_CLIP_TO_VIEW:
+ g_value_set_boolean (value, priv->clip_to_view);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+st_viewport_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StViewport *viewport = ST_VIEWPORT (object);
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (viewport);
+
+ switch (property_id)
+ {
+ case PROP_HADJUST:
+ scrollable_set_adjustments (ST_SCROLLABLE (object),
+ g_value_get_object (value),
+ priv->vadjustment);
+ break;
+
+ case PROP_VADJUST:
+ scrollable_set_adjustments (ST_SCROLLABLE (object),
+ priv->hadjustment,
+ g_value_get_object (value));
+ break;
+
+ case PROP_CLIP_TO_VIEW:
+ st_viewport_set_clip_to_view (viewport, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+st_viewport_dispose (GObject *object)
+{
+ StViewport *viewport = ST_VIEWPORT (object);
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (viewport);
+
+ g_clear_object (&priv->hadjustment);
+ g_clear_object (&priv->vadjustment);
+
+ G_OBJECT_CLASS (st_viewport_parent_class)->dispose (object);
+}
+
+static void
+st_viewport_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ StViewport *viewport = ST_VIEWPORT (actor);
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (viewport);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ ClutterLayoutManager *layout = clutter_actor_get_layout_manager (actor);
+ ClutterActorBox viewport_box;
+ ClutterActorBox content_box;
+ float avail_width, avail_height;
+ float min_width, natural_width;
+ float min_height, natural_height;
+
+ st_theme_node_get_content_box (theme_node, box, &viewport_box);
+ clutter_actor_box_get_size (&viewport_box, &avail_width, &avail_height);
+
+ clutter_layout_manager_get_preferred_width (layout, CLUTTER_CONTAINER (actor),
+ avail_height,
+ &min_width, &natural_width);
+ clutter_layout_manager_get_preferred_height (layout, CLUTTER_CONTAINER (actor),
+ MAX (avail_width, min_width),
+ &min_height, &natural_height);
+
+ /* Because StViewport implements StScrollable, the allocation box passed here
+ * may not match the minimum sizes reported by the layout manager. When that
+ * happens, the content box needs to be adjusted to match the reported minimum
+ * sizes before being passed to clutter_layout_manager_allocate() */
+ clutter_actor_set_allocation (actor, box);
+
+ content_box = viewport_box;
+ if (priv->hadjustment)
+ content_box.x2 += MAX (0, min_width - avail_width);
+ if (priv->vadjustment)
+ content_box.y2 += MAX (0, min_height - avail_height);
+
+ clutter_layout_manager_allocate (layout, CLUTTER_CONTAINER (actor),
+ &content_box);
+
+ /* update adjustments for scrolling */
+ if (priv->vadjustment)
+ {
+ double prev_value;
+
+ g_object_set (G_OBJECT (priv->vadjustment),
+ "lower", 0.0,
+ "upper", MAX (min_height, avail_height),
+ "page-size", avail_height,
+ "step-increment", avail_height / 6,
+ "page-increment", avail_height - avail_height / 6,
+ NULL);
+
+ prev_value = st_adjustment_get_value (priv->vadjustment);
+ st_adjustment_set_value (priv->vadjustment, prev_value);
+ }
+
+ if (priv->hadjustment)
+ {
+ double prev_value;
+
+ g_object_set (G_OBJECT (priv->hadjustment),
+ "lower", 0.0,
+ "upper", MAX (min_width, avail_width),
+ "page-size", avail_width,
+ "step-increment", avail_width / 6,
+ "page-increment", avail_width - avail_width / 6,
+ NULL);
+
+ prev_value = st_adjustment_get_value (priv->hadjustment);
+ st_adjustment_set_value (priv->hadjustment, prev_value);
+ }
+}
+
+static double
+get_hadjustment_value (StViewport *viewport)
+{
+ StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
+ ClutterTextDirection direction;
+ double x, upper, page_size;
+
+ if (!priv->hadjustment)
+ return 0;
+
+ st_adjustment_get_values (priv->hadjustment,
+ &x, NULL, &upper, NULL, NULL, &page_size);
+
+ direction = clutter_actor_get_text_direction (CLUTTER_ACTOR (viewport));
+ if (direction == CLUTTER_TEXT_DIRECTION_RTL)
+ return upper - page_size - x;
+
+ return x;
+}
+
+static void
+st_viewport_apply_transform (ClutterActor *actor,
+ graphene_matrix_t *matrix)
+{
+ StViewport *viewport = ST_VIEWPORT (actor);
+ StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
+ ClutterActorClass *parent_class =
+ CLUTTER_ACTOR_CLASS (st_viewport_parent_class);
+ graphene_point3d_t p = GRAPHENE_POINT3D_INIT_ZERO;
+
+ if (priv->hadjustment)
+ p.x = -(int)get_hadjustment_value (viewport);
+
+ if (priv->vadjustment)
+ p.y = -(int)st_adjustment_get_value (priv->vadjustment);
+
+ graphene_matrix_translate (matrix, &p);
+
+ parent_class->apply_transform (actor, matrix);
+}
+
+/* If we are translated, then we need to translate back before chaining
+ * up or the background and borders will be drawn in the wrong place */
+static void
+get_border_paint_offsets (StViewport *viewport,
+ int *x,
+ int *y)
+{
+ StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
+
+ if (priv->hadjustment)
+ *x = get_hadjustment_value (viewport);
+ else
+ *x = 0;
+
+ if (priv->vadjustment)
+ *y = st_adjustment_get_value (priv->vadjustment);
+ else
+ *y = 0;
+}
+
+
+static void
+st_viewport_paint (ClutterActor *actor,
+ ClutterPaintContext *paint_context)
+{
+ StViewport *viewport = ST_VIEWPORT (actor);
+ StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ int x, y;
+ ClutterActorBox allocation_box;
+ ClutterActorBox content_box;
+ ClutterActor *child;
+ CoglFramebuffer *fb = clutter_paint_context_get_framebuffer (paint_context);
+
+ get_border_paint_offsets (viewport, &x, &y);
+ if (x != 0 || y != 0)
+ {
+ cogl_framebuffer_push_matrix (fb);
+ cogl_framebuffer_translate (fb, x, y, 0);
+ }
+
+ st_widget_paint_background (ST_WIDGET (actor), paint_context);
+
+ if (x != 0 || y != 0)
+ cogl_framebuffer_pop_matrix (fb);
+
+ if (clutter_actor_get_n_children (actor) == 0)
+ return;
+
+ clutter_actor_get_allocation_box (actor, &allocation_box);
+ st_theme_node_get_content_box (theme_node, &allocation_box, &content_box);
+
+ content_box.x1 += x;
+ content_box.y1 += y;
+ content_box.x2 += x;
+ content_box.y2 += y;
+
+ /* The content area forms the viewport into the scrolled contents, while
+ * the borders and background stay in place; after drawing the borders and
+ * background, we clip to the content area */
+ if (priv->clip_to_view && (priv->hadjustment || priv->vadjustment))
+ {
+ cogl_framebuffer_push_rectangle_clip (fb,
+ (int)content_box.x1,
+ (int)content_box.y1,
+ (int)content_box.x2,
+ (int)content_box.y2);
+ }
+
+ for (child = clutter_actor_get_first_child (actor);
+ child != NULL;
+ child = clutter_actor_get_next_sibling (child))
+ clutter_actor_paint (child, paint_context);
+
+ if (priv->clip_to_view && (priv->hadjustment || priv->vadjustment))
+ cogl_framebuffer_pop_clip (fb);
+}
+
+static void
+st_viewport_pick (ClutterActor *actor,
+ ClutterPickContext *pick_context)
+{
+ StViewport *viewport = ST_VIEWPORT (actor);
+ StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ int x, y;
+ g_autoptr (ClutterActorBox) allocation_box = NULL;
+ ClutterActorBox content_box;
+ ClutterActor *child;
+
+ CLUTTER_ACTOR_CLASS (st_viewport_parent_class)->pick (actor, pick_context);
+
+ if (clutter_actor_get_n_children (actor) == 0)
+ return;
+
+ g_object_get (actor, "allocation", &allocation_box, NULL);
+ st_theme_node_get_content_box (theme_node, allocation_box, &content_box);
+
+ get_border_paint_offsets (viewport, &x, &y);
+
+ content_box.x1 += x;
+ content_box.y1 += y;
+ content_box.x2 += x;
+ content_box.y2 += y;
+
+ if (priv->hadjustment || priv->vadjustment)
+ clutter_pick_context_push_clip (pick_context, &content_box);
+
+ for (child = clutter_actor_get_first_child (actor);
+ child != NULL;
+ child = clutter_actor_get_next_sibling (child))
+ clutter_actor_pick (child, pick_context);
+
+ if (priv->hadjustment || priv->vadjustment)
+ clutter_pick_context_pop_clip (pick_context);
+}
+
+static gboolean
+st_viewport_get_paint_volume (ClutterActor *actor,
+ ClutterPaintVolume *volume)
+{
+ StViewport *viewport = ST_VIEWPORT (actor);
+ StViewportPrivate *priv = st_viewport_get_instance_private (viewport);
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ ClutterActorBox allocation_box;
+ ClutterActorBox content_box;
+ int x, y;
+
+ /* Setting the paint volume does not make sense when we don't have any allocation */
+ if (!clutter_actor_has_allocation (actor))
+ return FALSE;
+
+ if (!priv->clip_to_view)
+ return CLUTTER_ACTOR_CLASS (st_viewport_parent_class)->get_paint_volume (actor, volume);
+
+ /* When have an adjustment we are clipped to the content box, so base
+ * our paint volume on that. */
+ if (priv->hadjustment || priv->vadjustment)
+ {
+ double width, height;
+
+ clutter_actor_get_allocation_box (actor, &allocation_box);
+ st_theme_node_get_content_box (theme_node, &allocation_box, &content_box);
+
+ width = content_box.x2 - content_box.x1;
+ height = content_box.y2 - content_box.y1;
+
+ clutter_paint_volume_set_width (volume, width);
+ clutter_paint_volume_set_height (volume, height);
+ }
+ else if (!CLUTTER_ACTOR_CLASS (st_viewport_parent_class)->get_paint_volume (actor, volume))
+ {
+ return FALSE;
+ }
+
+ /* When scrolled, st_viewport_apply_transform() includes the scroll offset
+ * and affects paint volumes. This is right for our children, but our paint volume
+ * is determined by our allocation and borders and doesn't scroll, so we need
+ * to reverse-compensate here, the same as we do when painting.
+ */
+ get_border_paint_offsets (viewport, &x, &y);
+ if (x != 0 || y != 0)
+ {
+ graphene_point3d_t origin;
+
+ clutter_paint_volume_get_origin (volume, &origin);
+ origin.x += x;
+ origin.y += y;
+ clutter_paint_volume_set_origin (volume, &origin);
+ }
+
+ return TRUE;
+}
+
+static void
+st_viewport_class_init (StViewportClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ object_class->get_property = st_viewport_get_property;
+ object_class->set_property = st_viewport_set_property;
+ object_class->dispose = st_viewport_dispose;
+
+ actor_class->allocate = st_viewport_allocate;
+ actor_class->apply_transform = st_viewport_apply_transform;
+
+ actor_class->paint = st_viewport_paint;
+ actor_class->get_paint_volume = st_viewport_get_paint_volume;
+ actor_class->pick = st_viewport_pick;
+
+ props[PROP_CLIP_TO_VIEW] =
+ g_param_spec_boolean ("clip-to-view",
+ "Clip to view",
+ "Clip to view",
+ TRUE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /* StScrollable properties */
+ g_object_class_override_property (object_class,
+ PROP_HADJUST,
+ "hadjustment");
+
+ g_object_class_override_property (object_class,
+ PROP_VADJUST,
+ "vadjustment");
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+st_viewport_init (StViewport *self)
+{
+ StViewportPrivate *priv =
+ st_viewport_get_instance_private (self);
+
+ priv->clip_to_view = TRUE;
+}
diff --git a/src/st/st-viewport.h b/src/st/st-viewport.h
new file mode 100644
index 0000000..8f0b16b
--- /dev/null
+++ b/src/st/st-viewport.h
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-viewport.h: viewport actor
+ *
+ * Copyright 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2019 Endless, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#pragma once
+
+#include <st/st-widget.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_VIEWPORT (st_viewport_get_type())
+G_DECLARE_DERIVABLE_TYPE (StViewport, st_viewport, ST, VIEWPORT, StWidget)
+
+struct _StViewportClass
+{
+ StWidgetClass parent_class;
+};
+
+G_END_DECLS
diff --git a/src/st/st-widget-accessible.h b/src/st/st-widget-accessible.h
new file mode 100644
index 0000000..c60f778
--- /dev/null
+++ b/src/st/st-widget-accessible.h
@@ -0,0 +1,76 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-widget-accessible.h: Accessible object for StWidget
+ *
+ * Copyright 2010 Igalia, S.L.
+ * Author: Alejandro Piñeiro Iglesias <apinheiro@igalia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_WIDGET_ACCESSIBLE_H__
+#define __ST_WIDGET_ACCESSIBLE_H__
+
+G_BEGIN_DECLS
+
+#include <st/st-widget.h>
+#include <cally/cally.h>
+
+#define ST_TYPE_WIDGET_ACCESSIBLE st_widget_accessible_get_type ()
+
+#define ST_WIDGET_ACCESSIBLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessible))
+
+#define ST_IS_WIDGET_ACCESSIBLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ ST_TYPE_WIDGET_ACCESSIBLE))
+
+#define ST_WIDGET_ACCESSIBLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessibleClass))
+
+#define ST_IS_WIDGET_ACCESSIBLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ ST_TYPE_WIDGET_ACCESSIBLE))
+
+#define ST_WIDGET_ACCESSIBLE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessibleClass))
+
+typedef struct _StWidgetAccessible StWidgetAccessible;
+typedef struct _StWidgetAccessibleClass StWidgetAccessibleClass;
+typedef struct _StWidgetAccessiblePrivate StWidgetAccessiblePrivate;
+
+struct _StWidgetAccessible
+{
+ CallyActor parent;
+
+ /*< private >*/
+ StWidgetAccessiblePrivate *priv;
+};
+
+struct _StWidgetAccessibleClass
+{
+ CallyActorClass parent_class;
+};
+
+GType st_widget_accessible_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __ST_WIDGET_ACCESSIBLE_H__ */
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
new file mode 100644
index 0000000..31c400b
--- /dev/null
+++ b/src/st/st-widget.c
@@ -0,0 +1,3049 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-widget.c: Base class for St actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2009 Abderrahim Kitouni
+ * Copyright 2009, 2010 Florian Müllner
+ * Copyright 2010 Adel Gadllah
+ * Copyright 2012 Igalia, S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <clutter/clutter.h>
+
+#include "st-widget.h"
+
+#include "st-label.h"
+#include "st-private.h"
+#include "st-settings.h"
+#include "st-texture-cache.h"
+#include "st-theme-context.h"
+#include "st-theme-node-transition.h"
+#include "st-theme-node-private.h"
+#include "st-drawing-area.h"
+
+#include "st-widget-accessible.h"
+
+#include <atk/atk-enum-types.h>
+
+/* This is set in stone and also hard-coded in GDK. */
+#define VIRTUAL_CORE_POINTER_ID 2
+
+/*
+ * Forward declaration for sake of StWidgetChild
+ */
+typedef struct _StWidgetPrivate StWidgetPrivate;
+struct _StWidgetPrivate
+{
+ StThemeNode *theme_node;
+ gchar *pseudo_class;
+ gchar *style_class;
+ gchar *inline_style;
+
+ StThemeNodeTransition *transition_animation;
+
+ guint is_style_dirty : 1;
+ guint first_child_dirty : 1;
+ guint last_child_dirty : 1;
+ guint draw_bg_color : 1;
+ guint draw_border_internal : 1;
+ guint track_hover : 1;
+ guint hover : 1;
+ guint can_focus : 1;
+
+ gulong texture_file_changed_id;
+ guint update_child_styles_id;
+
+ AtkObject *accessible;
+ AtkRole accessible_role;
+ AtkStateSet *local_state_set;
+
+ ClutterActor *label_actor;
+ gchar *accessible_name;
+
+ StWidget *last_visible_child;
+ StWidget *first_visible_child;
+
+ StThemeNodePaintState paint_states[2];
+ int current_paint_state : 2;
+};
+
+/**
+ * SECTION:st-widget
+ * @short_description: Base class for stylable actors
+ *
+ * #StWidget is a simple abstract class on top of #ClutterActor. It
+ * provides basic themeing properties.
+ *
+ * Actors in the St library should subclass #StWidget if they plan
+ * to obey to a certain #StStyle.
+ */
+
+enum
+{
+ PROP_0,
+
+ PROP_PSEUDO_CLASS,
+ PROP_STYLE_CLASS,
+ PROP_STYLE,
+ PROP_TRACK_HOVER,
+ PROP_HOVER,
+ PROP_CAN_FOCUS,
+ PROP_LABEL_ACTOR,
+ PROP_ACCESSIBLE_ROLE,
+ PROP_ACCESSIBLE_NAME,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum
+{
+ STYLE_CHANGED,
+ POPUP_MENU,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (StWidget, st_widget, CLUTTER_TYPE_ACTOR);
+#define ST_WIDGET_PRIVATE(w) ((StWidgetPrivate *)st_widget_get_instance_private (w))
+
+static void st_widget_recompute_style (StWidget *widget,
+ StThemeNode *old_theme_node);
+static gboolean st_widget_real_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction);
+
+static AtkObject * st_widget_get_accessible (ClutterActor *actor);
+static gboolean st_widget_has_accessible (ClutterActor *actor);
+
+static void
+st_widget_update_insensitive (StWidget *widget)
+{
+ if (clutter_actor_get_reactive (CLUTTER_ACTOR (widget)))
+ st_widget_remove_style_pseudo_class (widget, "insensitive");
+ else
+ st_widget_add_style_pseudo_class (widget, "insensitive");
+}
+
+static void
+st_widget_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StWidget *actor = ST_WIDGET (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_PSEUDO_CLASS:
+ st_widget_set_style_pseudo_class (actor, g_value_get_string (value));
+ break;
+
+ case PROP_STYLE_CLASS:
+ st_widget_set_style_class_name (actor, g_value_get_string (value));
+ break;
+
+ case PROP_STYLE:
+ st_widget_set_style (actor, g_value_get_string (value));
+ break;
+
+ case PROP_TRACK_HOVER:
+ st_widget_set_track_hover (actor, g_value_get_boolean (value));
+ break;
+
+ case PROP_HOVER:
+ st_widget_set_hover (actor, g_value_get_boolean (value));
+ break;
+
+ case PROP_CAN_FOCUS:
+ st_widget_set_can_focus (actor, g_value_get_boolean (value));
+ break;
+
+ case PROP_LABEL_ACTOR:
+ st_widget_set_label_actor (actor, g_value_get_object (value));
+ break;
+
+ case PROP_ACCESSIBLE_ROLE:
+ st_widget_set_accessible_role (actor, g_value_get_enum (value));
+ break;
+
+ case PROP_ACCESSIBLE_NAME:
+ st_widget_set_accessible_name (actor, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_widget_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StWidget *actor = ST_WIDGET (gobject);
+ StWidgetPrivate *priv = st_widget_get_instance_private (ST_WIDGET (gobject));
+
+ switch (prop_id)
+ {
+ case PROP_PSEUDO_CLASS:
+ g_value_set_string (value, priv->pseudo_class);
+ break;
+
+ case PROP_STYLE_CLASS:
+ g_value_set_string (value, priv->style_class);
+ break;
+
+ case PROP_STYLE:
+ g_value_set_string (value, priv->inline_style);
+ break;
+
+ case PROP_TRACK_HOVER:
+ g_value_set_boolean (value, priv->track_hover);
+ break;
+
+ case PROP_HOVER:
+ g_value_set_boolean (value, priv->hover);
+ break;
+
+ case PROP_CAN_FOCUS:
+ g_value_set_boolean (value, priv->can_focus);
+ break;
+
+ case PROP_LABEL_ACTOR:
+ g_value_set_object (value, priv->label_actor);
+ break;
+
+ case PROP_ACCESSIBLE_ROLE:
+ g_value_set_enum (value, st_widget_get_accessible_role (actor));
+ break;
+
+ case PROP_ACCESSIBLE_NAME:
+ g_value_set_string (value, priv->accessible_name);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_widget_constructed (GObject *gobject)
+{
+ G_OBJECT_CLASS (st_widget_parent_class)->constructed (gobject);
+
+ st_widget_update_insensitive (ST_WIDGET (gobject));
+}
+
+static void
+st_widget_remove_transition (StWidget *widget)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ if (priv->transition_animation)
+ {
+ g_object_run_dispose (G_OBJECT (priv->transition_animation));
+ g_object_unref (priv->transition_animation);
+ priv->transition_animation = NULL;
+ }
+}
+
+static void
+next_paint_state (StWidget *widget)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ priv->current_paint_state = (priv->current_paint_state + 1) % G_N_ELEMENTS (priv->paint_states);
+}
+
+static StThemeNodePaintState *
+current_paint_state (StWidget *widget)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ return &priv->paint_states[priv->current_paint_state];
+}
+
+static void
+st_widget_texture_cache_changed (StTextureCache *cache,
+ GFile *file,
+ gpointer user_data)
+{
+ StWidget *actor = ST_WIDGET (user_data);
+ StWidgetPrivate *priv = st_widget_get_instance_private (actor);
+ gboolean changed = FALSE;
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
+ {
+ StThemeNodePaintState *paint_state = &priv->paint_states[i];
+ changed |= st_theme_node_paint_state_invalidate_for_file (paint_state, file);
+ }
+
+ if (changed && clutter_actor_is_mapped (CLUTTER_ACTOR (actor)))
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
+}
+
+static void
+st_widget_dispose (GObject *gobject)
+{
+ StWidget *actor = ST_WIDGET (gobject);
+ StWidgetPrivate *priv = st_widget_get_instance_private (actor);
+
+ g_clear_pointer (&priv->theme_node, g_object_unref);
+
+ st_widget_remove_transition (actor);
+
+ g_clear_pointer (&priv->label_actor, g_object_unref);
+
+ g_clear_signal_handler (&priv->texture_file_changed_id,
+ st_texture_cache_get_default ());
+
+ g_clear_object (&priv->first_visible_child);
+ g_clear_object (&priv->last_visible_child);
+
+ G_OBJECT_CLASS (st_widget_parent_class)->dispose (gobject);
+
+ g_clear_handle_id (&priv->update_child_styles_id, g_source_remove);
+}
+
+static void
+st_widget_finalize (GObject *gobject)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (ST_WIDGET (gobject));
+ guint i;
+
+ g_free (priv->style_class);
+ g_free (priv->pseudo_class);
+ g_object_unref (priv->local_state_set);
+ g_free (priv->accessible_name);
+ g_free (priv->inline_style);
+
+ for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
+ st_theme_node_paint_state_free (&priv->paint_states[i]);
+
+ G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
+}
+
+
+static void
+st_widget_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+
+ st_theme_node_adjust_for_width (theme_node, &for_height);
+
+ CLUTTER_ACTOR_CLASS (st_widget_parent_class)->get_preferred_width (self, for_height, min_width_p, natural_width_p);
+
+ st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
+}
+
+static void
+st_widget_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+
+ st_theme_node_adjust_for_width (theme_node, &for_width);
+
+ CLUTTER_ACTOR_CLASS (st_widget_parent_class)->get_preferred_height (self, for_width, min_height_p, natural_height_p);
+
+ st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
+}
+
+static void
+st_widget_allocate (ClutterActor *actor,
+ const ClutterActorBox *box)
+{
+ StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+ ClutterActorBox content_box;
+
+ /* Note that we can't just chain up to clutter_actor_real_allocate --
+ * Clutter does some dirty tricks for backwards compatibility.
+ * Clutter also passes the actor's allocation directly to the layout
+ * manager, meaning that we can't modify it for children only.
+ */
+
+ clutter_actor_set_allocation (actor, box);
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ /* If we've chained up to here, we want to allocate the children using the
+ * currently installed layout manager */
+ clutter_layout_manager_allocate (clutter_actor_get_layout_manager (actor),
+ CLUTTER_CONTAINER (actor),
+ &content_box);
+}
+
+/**
+ * st_widget_paint_background:
+ * @widget: The #StWidget
+ *
+ * Paint the background of the widget. This is meant to be called by
+ * subclasses of StWidget that need to paint the background without
+ * painting children.
+ */
+void
+st_widget_paint_background (StWidget *widget,
+ ClutterPaintContext *paint_context)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+ CoglFramebuffer *framebuffer;
+ StThemeNode *theme_node;
+ ClutterActorBox allocation;
+ float resource_scale;
+ guint8 opacity;
+
+ resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (widget));
+
+ framebuffer = clutter_paint_context_get_framebuffer (paint_context);
+ theme_node = st_widget_get_theme_node (widget);
+
+ clutter_actor_get_allocation_box (CLUTTER_ACTOR (widget), &allocation);
+
+ opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (widget));
+
+ if (priv->transition_animation)
+ st_theme_node_transition_paint (priv->transition_animation,
+ framebuffer,
+ &allocation,
+ opacity,
+ resource_scale);
+ else
+ st_theme_node_paint (theme_node,
+ current_paint_state (widget),
+ framebuffer,
+ &allocation,
+ opacity,
+ resource_scale);
+}
+
+static void
+st_widget_paint (ClutterActor *actor,
+ ClutterPaintContext *paint_context)
+{
+ st_widget_paint_background (ST_WIDGET (actor), paint_context);
+
+ /* Chain up so we paint children. */
+ CLUTTER_ACTOR_CLASS (st_widget_parent_class)->paint (actor, paint_context);
+}
+
+static void
+st_widget_parent_set (ClutterActor *widget,
+ ClutterActor *old_parent)
+{
+ StWidget *self = ST_WIDGET (widget);
+ ClutterActorClass *parent_class;
+
+ parent_class = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
+ if (parent_class->parent_set)
+ parent_class->parent_set (widget, old_parent);
+
+ st_widget_style_changed (self);
+}
+
+static void
+st_widget_map (ClutterActor *actor)
+{
+ StWidget *self = ST_WIDGET (actor);
+
+ CLUTTER_ACTOR_CLASS (st_widget_parent_class)->map (actor);
+
+ st_widget_ensure_style (self);
+}
+
+static void
+st_widget_unmap (ClutterActor *actor)
+{
+ StWidget *self = ST_WIDGET (actor);
+ StWidgetPrivate *priv = st_widget_get_instance_private (self);
+
+ CLUTTER_ACTOR_CLASS (st_widget_parent_class)->unmap (actor);
+
+ st_widget_remove_transition (self);
+
+ if (priv->track_hover && priv->hover)
+ st_widget_set_hover (self, FALSE);
+}
+
+static void
+notify_children_of_style_change (ClutterActor *self)
+{
+ ClutterActorIter iter;
+ ClutterActor *actor;
+
+ clutter_actor_iter_init (&iter, self);
+ while (clutter_actor_iter_next (&iter, &actor))
+ {
+ if (ST_IS_WIDGET (actor))
+ st_widget_style_changed (ST_WIDGET (actor));
+ else
+ notify_children_of_style_change (actor);
+ }
+}
+
+static void
+st_widget_real_style_changed (StWidget *self)
+{
+ clutter_actor_queue_redraw ((ClutterActor *) self);
+}
+
+void
+st_widget_style_changed (StWidget *widget)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+ StThemeNode *old_theme_node = NULL;
+
+ priv->is_style_dirty = TRUE;
+ if (priv->theme_node)
+ {
+ old_theme_node = priv->theme_node;
+ priv->theme_node = NULL;
+ }
+
+ /* update the style only if we are mapped */
+ if (clutter_actor_is_mapped (CLUTTER_ACTOR (widget)))
+ st_widget_recompute_style (widget, old_theme_node);
+
+ /* Descend through all children. If the actor is not mapped,
+ * children will clear their theme node without recomputing style.
+ */
+ notify_children_of_style_change (CLUTTER_ACTOR (widget));
+
+ if (old_theme_node)
+ g_object_unref (old_theme_node);
+}
+
+static void
+on_theme_context_changed (StThemeContext *context,
+ ClutterStage *stage)
+{
+ notify_children_of_style_change (CLUTTER_ACTOR (stage));
+}
+
+static StThemeNode *
+get_root_theme_node (ClutterStage *stage)
+{
+ StThemeContext *context = st_theme_context_get_for_stage (stage);
+
+ if (!g_object_get_data (G_OBJECT (context), "st-theme-initialized"))
+ {
+ g_object_set_data (G_OBJECT (context), "st-theme-initialized", GUINT_TO_POINTER (1));
+ g_signal_connect (G_OBJECT (context), "changed",
+ G_CALLBACK (on_theme_context_changed), stage);
+ }
+
+ return st_theme_context_get_root_node (context);
+}
+
+/**
+ * st_widget_get_theme_node:
+ * @widget: a #StWidget
+ *
+ * Gets the theme node holding style information for the widget.
+ * The theme node is used to access standard and custom CSS
+ * properties of the widget.
+ *
+ * Note: it is a fatal error to call this on a widget that is
+ * not been added to a stage.
+ *
+ * Returns: (transfer none): the theme node for the widget.
+ * This is owned by the widget. When attributes of the widget
+ * or the environment that affect the styling change (for example
+ * the style_class property of the widget), it will be recreated,
+ * and the ::style-changed signal will be emitted on the widget.
+ */
+StThemeNode *
+st_widget_get_theme_node (StWidget *widget)
+{
+ StWidgetPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_WIDGET (widget), NULL);
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->theme_node == NULL)
+ {
+ StThemeContext *context;
+ StThemeNode *tmp_node;
+ StThemeNode *parent_node = NULL;
+ ClutterStage *stage = NULL;
+ ClutterActor *parent;
+ char *pseudo_class, *direction_pseudo_class;
+
+ parent = clutter_actor_get_parent (CLUTTER_ACTOR (widget));
+ while (parent != NULL)
+ {
+ if (parent_node == NULL && ST_IS_WIDGET (parent))
+ parent_node = st_widget_get_theme_node (ST_WIDGET (parent));
+ else if (CLUTTER_IS_STAGE (parent))
+ stage = CLUTTER_STAGE (parent);
+
+ parent = clutter_actor_get_parent (parent);
+ }
+
+ if (stage == NULL)
+ {
+ g_autofree char *desc = st_describe_actor (CLUTTER_ACTOR (widget));
+
+ g_critical ("st_widget_get_theme_node called on the widget %s which is not in the stage.",
+ desc);
+
+ return g_object_new (ST_TYPE_THEME_NODE, NULL);
+ }
+
+ if (parent_node == NULL)
+ parent_node = get_root_theme_node (CLUTTER_STAGE (stage));
+
+ /* Always append a "magic" pseudo class indicating the text
+ * direction, to allow to adapt the CSS when necessary without
+ * requiring separate style sheets.
+ */
+ if (clutter_actor_get_text_direction (CLUTTER_ACTOR (widget)) == CLUTTER_TEXT_DIRECTION_RTL)
+ direction_pseudo_class = (char *)"rtl";
+ else
+ direction_pseudo_class = (char *)"ltr";
+
+ if (priv->pseudo_class)
+ pseudo_class = g_strconcat(priv->pseudo_class, " ",
+ direction_pseudo_class, NULL);
+ else
+ pseudo_class = direction_pseudo_class;
+
+ context = st_theme_context_get_for_stage (stage);
+ tmp_node = st_theme_node_new (context, parent_node, NULL,
+ G_OBJECT_TYPE (widget),
+ clutter_actor_get_name (CLUTTER_ACTOR (widget)),
+ priv->style_class,
+ pseudo_class,
+ priv->inline_style);
+
+ if (pseudo_class != direction_pseudo_class)
+ g_free (pseudo_class);
+
+ priv->theme_node = g_object_ref (st_theme_context_intern_node (context,
+ tmp_node));
+ g_object_unref (tmp_node);
+ }
+
+ return priv->theme_node;
+}
+
+/**
+ * st_widget_peek_theme_node:
+ * @widget: a #StWidget
+ *
+ * Returns the theme node for the widget if it has already been
+ * computed, %NULL if the widget hasn't been added to a stage or the theme
+ * node hasn't been computed. If %NULL is returned, then ::style-changed
+ * will be reliably emitted before the widget is allocated or painted.
+ *
+ * Returns: (transfer none): the theme node for the widget.
+ * This is owned by the widget. When attributes of the widget
+ * or the environment that affect the styling change (for example
+ * the style_class property of the widget), it will be recreated,
+ * and the ::style-changed signal will be emitted on the widget.
+ */
+StThemeNode *
+st_widget_peek_theme_node (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), NULL);
+
+ return ST_WIDGET_PRIVATE (widget)->theme_node;
+}
+
+static gboolean
+st_widget_enter (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (ST_WIDGET (actor));
+
+ if (priv->track_hover)
+ {
+ ClutterStage *stage;
+ ClutterActor *target;
+
+ stage = clutter_event_get_stage ((ClutterEvent *) event);
+ target = clutter_stage_get_event_actor (stage, (ClutterEvent *) event);
+
+ if (clutter_actor_contains (actor, target))
+ st_widget_set_hover (ST_WIDGET (actor), TRUE);
+ else
+ {
+ /* The widget has a grab and is being told about an
+ * enter-event outside its hierarchy. Hopefully we already
+ * got a leave-event, but if not, handle it now.
+ */
+ st_widget_set_hover (ST_WIDGET (actor), FALSE);
+ }
+ }
+
+ if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event)
+ return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event);
+ else
+ return FALSE;
+}
+
+static gboolean
+st_widget_leave (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (ST_WIDGET (actor));
+
+ if (priv->track_hover)
+ {
+ if (!event->related || !clutter_actor_contains (actor, event->related))
+ st_widget_set_hover (ST_WIDGET (actor), FALSE);
+ }
+
+ if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event)
+ return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event);
+ else
+ return FALSE;
+}
+
+static void
+st_widget_key_focus_in (ClutterActor *actor)
+{
+ StWidget *widget = ST_WIDGET (actor);
+
+ st_widget_add_style_pseudo_class (widget, "focus");
+}
+
+static void
+st_widget_key_focus_out (ClutterActor *actor)
+{
+ StWidget *widget = ST_WIDGET (actor);
+
+ st_widget_remove_style_pseudo_class (widget, "focus");
+}
+
+static gboolean
+st_widget_key_press_event (ClutterActor *actor,
+ ClutterKeyEvent *event)
+{
+ if (event->keyval == CLUTTER_KEY_Menu ||
+ (event->keyval == CLUTTER_KEY_F10 &&
+ (event->modifier_state & CLUTTER_SHIFT_MASK)))
+ {
+ st_widget_popup_menu (ST_WIDGET (actor));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_widget_get_paint_volume (ClutterActor *self,
+ ClutterPaintVolume *volume)
+{
+ ClutterActorBox paint_box, alloc_box;
+ StThemeNode *theme_node;
+ StWidgetPrivate *priv;
+ graphene_point3d_t origin;
+
+ /* Setting the paint volume does not make sense when we don't have any allocation */
+ if (!clutter_actor_has_allocation (self))
+ return FALSE;
+
+ priv = st_widget_get_instance_private (ST_WIDGET (self));
+
+ theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+ clutter_actor_get_allocation_box (self, &alloc_box);
+
+ if (priv->transition_animation)
+ st_theme_node_transition_get_paint_box (priv->transition_animation,
+ &alloc_box, &paint_box);
+ else
+ st_theme_node_get_paint_box (theme_node, &alloc_box, &paint_box);
+
+ origin.x = paint_box.x1 - alloc_box.x1;
+ origin.y = paint_box.y1 - alloc_box.y1;
+ origin.z = 0.0f;
+
+ clutter_paint_volume_set_origin (volume, &origin);
+ clutter_paint_volume_set_width (volume, paint_box.x2 - paint_box.x1);
+ clutter_paint_volume_set_height (volume, paint_box.y2 - paint_box.y1);
+
+ if (!clutter_actor_get_clip_to_allocation (self))
+ {
+ ClutterActor *child;
+ StShadow *shadow_spec = st_theme_node_get_text_shadow (theme_node);
+
+ if (shadow_spec)
+ {
+ ClutterActorBox shadow_box;
+
+ st_shadow_get_box (shadow_spec, &alloc_box, &shadow_box);
+ clutter_paint_volume_union_box (volume, &shadow_box);
+ }
+
+ /* Based on ClutterGroup/ClutterBox; include the children's
+ * paint volumes, since they may paint outside our allocation.
+ */
+ for (child = clutter_actor_get_first_child (self);
+ child != NULL;
+ child = clutter_actor_get_next_sibling (child))
+ {
+ const ClutterPaintVolume *child_volume;
+
+ if (!clutter_actor_is_visible (child))
+ continue;
+
+ child_volume = clutter_actor_get_transformed_paint_volume (child, self);
+ if (!child_volume)
+ return FALSE;
+
+ clutter_paint_volume_union (volume, child_volume);
+ }
+ }
+
+ return TRUE;
+}
+
+static GList *
+st_widget_real_get_focus_chain (StWidget *widget)
+{
+ GList *children, *l, *visible = NULL;
+
+ children = clutter_actor_get_children (CLUTTER_ACTOR (widget));
+
+ for (l = children; l; l = l->next)
+ {
+ if (clutter_actor_is_visible (CLUTTER_ACTOR (l->data)))
+ visible = g_list_prepend (visible, l->data);
+ }
+
+ g_list_free (children);
+
+ return g_list_reverse (visible);
+}
+
+static void
+st_widget_resource_scale_changed (ClutterActor *actor)
+{
+ StWidget *widget = ST_WIDGET (actor);
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
+ st_theme_node_paint_state_invalidate (&priv->paint_states[i]);
+
+ if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->resource_scale_changed)
+ CLUTTER_ACTOR_CLASS (st_widget_parent_class)->resource_scale_changed (actor);
+}
+
+static void
+st_widget_class_init (StWidgetClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ gobject_class->set_property = st_widget_set_property;
+ gobject_class->get_property = st_widget_get_property;
+ gobject_class->constructed = st_widget_constructed;
+ gobject_class->dispose = st_widget_dispose;
+ gobject_class->finalize = st_widget_finalize;
+
+ actor_class->get_preferred_width = st_widget_get_preferred_width;
+ actor_class->get_preferred_height = st_widget_get_preferred_height;
+ actor_class->allocate = st_widget_allocate;
+ actor_class->paint = st_widget_paint;
+ actor_class->get_paint_volume = st_widget_get_paint_volume;
+ actor_class->parent_set = st_widget_parent_set;
+ actor_class->map = st_widget_map;
+ actor_class->unmap = st_widget_unmap;
+
+ actor_class->enter_event = st_widget_enter;
+ actor_class->leave_event = st_widget_leave;
+ actor_class->key_focus_in = st_widget_key_focus_in;
+ actor_class->key_focus_out = st_widget_key_focus_out;
+ actor_class->key_press_event = st_widget_key_press_event;
+
+ actor_class->get_accessible = st_widget_get_accessible;
+ actor_class->has_accessible = st_widget_has_accessible;
+
+ actor_class->resource_scale_changed = st_widget_resource_scale_changed;
+
+ klass->style_changed = st_widget_real_style_changed;
+ klass->navigate_focus = st_widget_real_navigate_focus;
+ klass->get_accessible_type = st_widget_accessible_get_type;
+ klass->get_focus_chain = st_widget_real_get_focus_chain;
+
+ /**
+ * StWidget:pseudo-class:
+ *
+ * The pseudo-class of the actor. Typical values include "hover", "active",
+ * "focus".
+ */
+ props[PROP_PSEUDO_CLASS] =
+ g_param_spec_string ("pseudo-class",
+ "Pseudo Class",
+ "Pseudo class for styling",
+ "",
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:style-class:
+ *
+ * The style-class of the actor for use in styling.
+ */
+ props[PROP_STYLE_CLASS] =
+ g_param_spec_string ("style-class",
+ "Style Class",
+ "Style class for styling",
+ "",
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:style:
+ *
+ * Inline style information for the actor as a ';'-separated list of
+ * CSS properties.
+ */
+ props[PROP_STYLE] =
+ g_param_spec_string ("style",
+ "Style",
+ "Inline style string",
+ "",
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:track-hover:
+ *
+ * Determines whether the widget tracks pointer hover state. If
+ * %TRUE (and the widget is visible and reactive), the
+ * #StWidget:hover property and "hover" style pseudo class will be
+ * adjusted automatically as the pointer moves in and out of the
+ * widget.
+ */
+ props[PROP_TRACK_HOVER] =
+ g_param_spec_boolean ("track-hover",
+ "Track hover",
+ "Determines whether the widget tracks hover state",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:hover:
+ *
+ * Whether or not the pointer is currently hovering over the widget. This is
+ * only tracked automatically if #StWidget:track-hover is %TRUE, but you can
+ * adjust it manually in any case.
+ */
+ props[PROP_HOVER] =
+ g_param_spec_boolean ("hover",
+ "Hover",
+ "Whether the pointer is hovering over the widget",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:can-focus:
+ *
+ * Whether or not the widget can be focused via keyboard navigation.
+ */
+ props[PROP_CAN_FOCUS] =
+ g_param_spec_boolean ("can-focus",
+ "Can focus",
+ "Whether the widget can be focused via keyboard navigation",
+ FALSE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:label-actor:
+ *
+ * An actor that labels this widget.
+ */
+ props[PROP_LABEL_ACTOR] =
+ g_param_spec_object ("label-actor",
+ "Label",
+ "Label that identifies this widget",
+ CLUTTER_TYPE_ACTOR,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:accessible-role:
+ *
+ * The accessible role of this object
+ */
+ props[PROP_ACCESSIBLE_ROLE] =
+ g_param_spec_enum ("accessible-role",
+ "Accessible Role",
+ "The accessible role of this object",
+ ATK_TYPE_ROLE,
+ ATK_ROLE_INVALID,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * StWidget:accessible-name:
+ *
+ * Object instance's name for assistive technology access.
+ */
+ props[PROP_ACCESSIBLE_NAME] =
+ g_param_spec_string ("accessible-name",
+ "Accessible name",
+ "Object instance's name for assistive technology access.",
+ NULL,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, props);
+
+ /**
+ * StWidget::style-changed:
+ * @widget: the #StWidget
+ *
+ * Emitted when the style information that the widget derives from the
+ * theme changes
+ */
+ signals[STYLE_CHANGED] =
+ g_signal_new ("style-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StWidgetClass, style_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * StWidget::popup-menu:
+ * @widget: the #StWidget
+ *
+ * Emitted when the user has requested a context menu (eg, via a keybinding)
+ */
+ signals[POPUP_MENU] =
+ g_signal_new ("popup-menu",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StWidgetClass, popup_menu),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static const gchar *
+find_class_name (const gchar *class_list,
+ const gchar *class_name)
+{
+ gint len = strlen (class_name);
+ const gchar *match;
+
+ if (!class_list)
+ return NULL;
+
+ for (match = strstr (class_list, class_name); match; match = strstr (match + 1, class_name))
+ {
+ if ((match == class_list || g_ascii_isspace (match[-1])) &&
+ (match[len] == '\0' || g_ascii_isspace (match[len])))
+ return match;
+ }
+
+ return NULL;
+}
+
+static gboolean
+set_class_list (gchar **class_list,
+ const gchar *new_class_list)
+{
+ if (g_strcmp0 (*class_list, new_class_list) != 0)
+ {
+ g_free (*class_list);
+ *class_list = g_strdup (new_class_list);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gboolean
+add_class_name (gchar **class_list,
+ const gchar *class_name)
+{
+ gchar *new_class_list;
+
+ if (*class_list)
+ {
+ if (find_class_name (*class_list, class_name))
+ return FALSE;
+
+ new_class_list = g_strdup_printf ("%s %s", *class_list, class_name);
+ g_free (*class_list);
+ *class_list = new_class_list;
+ }
+ else
+ *class_list = g_strdup (class_name);
+
+ return TRUE;
+}
+
+static gboolean
+remove_class_name (gchar **class_list,
+ const gchar *class_name)
+{
+ const gchar *match, *end;
+ gchar *new_class_list;
+
+ if (!*class_list)
+ return FALSE;
+
+ if (strcmp (*class_list, class_name) == 0)
+ {
+ g_free (*class_list);
+ *class_list = NULL;
+ return TRUE;
+ }
+
+ match = find_class_name (*class_list, class_name);
+ if (!match)
+ return FALSE;
+ end = match + strlen (class_name);
+
+ /* Adjust either match or end to include a space as well.
+ * (One or the other must be possible at this point.)
+ */
+ if (match != *class_list)
+ match--;
+ else
+ end++;
+
+ new_class_list = g_strdup_printf ("%.*s%s", (int)(match - *class_list),
+ *class_list, end);
+ g_free (*class_list);
+ *class_list = new_class_list;
+
+ return TRUE;
+}
+
+/**
+ * st_widget_set_style_class_name:
+ * @actor: a #StWidget
+ * @style_class_list: (nullable): a new style class list string
+ *
+ * Set the style class name list. @style_class_list can either be
+ * %NULL, for no classes, or a space-separated list of style class
+ * names. See also st_widget_add_style_class_name() and
+ * st_widget_remove_style_class_name().
+ */
+void
+st_widget_set_style_class_name (StWidget *actor,
+ const gchar *style_class_list)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (actor));
+
+ priv = st_widget_get_instance_private (actor);
+
+ if (set_class_list (&priv->style_class, style_class_list))
+ {
+ st_widget_style_changed (actor);
+ g_object_notify_by_pspec (G_OBJECT (actor), props[PROP_STYLE_CLASS]);
+ }
+}
+
+/**
+ * st_widget_add_style_class_name:
+ * @actor: a #StWidget
+ * @style_class: a style class name string
+ *
+ * Adds @style_class to @actor's style class name list, if it is not
+ * already present.
+ */
+void
+st_widget_add_style_class_name (StWidget *actor,
+ const gchar *style_class)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (actor));
+ g_return_if_fail (style_class != NULL);
+
+ priv = st_widget_get_instance_private (actor);
+
+ if (add_class_name (&priv->style_class, style_class))
+ {
+ st_widget_style_changed (actor);
+ g_object_notify_by_pspec (G_OBJECT (actor), props[PROP_STYLE_CLASS]);
+ }
+}
+
+/**
+ * st_widget_remove_style_class_name:
+ * @actor: a #StWidget
+ * @style_class: a style class name string
+ *
+ * Removes @style_class from @actor's style class name, if it is
+ * present.
+ */
+void
+st_widget_remove_style_class_name (StWidget *actor,
+ const gchar *style_class)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (actor));
+ g_return_if_fail (style_class != NULL);
+
+ priv = st_widget_get_instance_private (actor);
+
+ if (remove_class_name (&priv->style_class, style_class))
+ {
+ st_widget_style_changed (actor);
+ g_object_notify_by_pspec (G_OBJECT (actor), props[PROP_STYLE_CLASS]);
+ }
+}
+
+/**
+ * st_widget_get_style_class_name:
+ * @actor: a #StWidget
+ *
+ * Get the current style class name
+ *
+ * Returns: the class name string. The string is owned by the #StWidget and
+ * should not be modified or freed.
+ */
+const gchar*
+st_widget_get_style_class_name (StWidget *actor)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
+
+ return ST_WIDGET_PRIVATE (actor)->style_class;
+}
+
+/**
+ * st_widget_has_style_class_name:
+ * @actor: a #StWidget
+ * @style_class: a style class string
+ *
+ * Tests if @actor's style class list includes @style_class.
+ *
+ * Returns: whether or not @actor's style class list includes
+ * @style_class.
+ */
+gboolean
+st_widget_has_style_class_name (StWidget *actor,
+ const gchar *style_class)
+{
+ StWidgetPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE);
+
+ priv = st_widget_get_instance_private (actor);
+
+ return find_class_name (priv->style_class, style_class) != NULL;
+}
+
+/**
+ * st_widget_get_style_pseudo_class:
+ * @actor: a #StWidget
+ *
+ * Get the current style pseudo class list.
+ *
+ * Note that an actor can have multiple pseudo classes; if you just
+ * want to test for the presence of a specific pseudo class, use
+ * st_widget_has_style_pseudo_class().
+ *
+ * Returns: the pseudo class list string. The string is owned by the
+ * #StWidget and should not be modified or freed.
+ */
+const gchar*
+st_widget_get_style_pseudo_class (StWidget *actor)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
+
+ return ST_WIDGET_PRIVATE (actor)->pseudo_class;
+}
+
+/**
+ * st_widget_has_style_pseudo_class:
+ * @actor: a #StWidget
+ * @pseudo_class: a pseudo class string
+ *
+ * Tests if @actor's pseudo class list includes @pseudo_class.
+ *
+ * Returns: whether or not @actor's pseudo class list includes
+ * @pseudo_class.
+ */
+gboolean
+st_widget_has_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class)
+{
+ StWidgetPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE);
+
+ priv = st_widget_get_instance_private (actor);
+
+ return find_class_name (priv->pseudo_class, pseudo_class) != NULL;
+}
+
+/**
+ * st_widget_set_style_pseudo_class:
+ * @actor: a #StWidget
+ * @pseudo_class_list: (nullable): a new pseudo class list string
+ *
+ * Set the style pseudo class list. @pseudo_class_list can either be
+ * %NULL, for no classes, or a space-separated list of pseudo class
+ * names. See also st_widget_add_style_pseudo_class() and
+ * st_widget_remove_style_pseudo_class().
+ */
+void
+st_widget_set_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class_list)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (actor));
+
+ priv = st_widget_get_instance_private (actor);
+
+ if (set_class_list (&priv->pseudo_class, pseudo_class_list))
+ {
+ st_widget_style_changed (actor);
+ g_object_notify_by_pspec (G_OBJECT (actor), props[PROP_PSEUDO_CLASS]);
+ }
+}
+
+/**
+ * st_widget_add_style_pseudo_class:
+ * @actor: a #StWidget
+ * @pseudo_class: a pseudo class string
+ *
+ * Adds @pseudo_class to @actor's pseudo class list, if it is not
+ * already present.
+ */
+void
+st_widget_add_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (actor));
+ g_return_if_fail (pseudo_class != NULL);
+
+ priv = st_widget_get_instance_private (actor);
+
+ if (add_class_name (&priv->pseudo_class, pseudo_class))
+ {
+ st_widget_style_changed (actor);
+ g_object_notify_by_pspec (G_OBJECT (actor), props[PROP_PSEUDO_CLASS]);
+ }
+}
+
+/**
+ * st_widget_remove_style_pseudo_class:
+ * @actor: a #StWidget
+ * @pseudo_class: a pseudo class string
+ *
+ * Removes @pseudo_class from @actor's pseudo class, if it is present.
+ */
+void
+st_widget_remove_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (actor));
+ g_return_if_fail (pseudo_class != NULL);
+
+ priv = st_widget_get_instance_private (actor);
+
+ if (remove_class_name (&priv->pseudo_class, pseudo_class))
+ {
+ st_widget_style_changed (actor);
+ g_object_notify_by_pspec (G_OBJECT (actor), props[PROP_PSEUDO_CLASS]);
+ }
+}
+
+/**
+ * st_widget_set_style:
+ * @actor: a #StWidget
+ * @style: (nullable): a inline style string, or %NULL
+ *
+ * Set the inline style string for this widget. The inline style string is an
+ * optional ';'-separated list of CSS properties that override the style as
+ * determined from the stylesheets of the current theme.
+ */
+void
+st_widget_set_style (StWidget *actor,
+ const gchar *style)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (actor));
+
+ priv = st_widget_get_instance_private (actor);
+
+ if (g_strcmp0 (style, priv->inline_style))
+ {
+ g_free (priv->inline_style);
+ priv->inline_style = g_strdup (style);
+
+ st_widget_style_changed (actor);
+
+ g_object_notify_by_pspec (G_OBJECT (actor), props[PROP_STYLE]);
+ }
+}
+
+/**
+ * st_widget_get_style:
+ * @actor: a #StWidget
+ *
+ * Get the current inline style string. See st_widget_set_style().
+ *
+ * Returns: (transfer none) (nullable): The inline style string, or %NULL. The
+ * string is owned by the #StWidget and should not be modified or freed.
+ */
+const gchar*
+st_widget_get_style (StWidget *actor)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
+
+ return ST_WIDGET_PRIVATE (actor)->inline_style;
+}
+
+static void
+st_widget_set_first_visible_child (StWidget *widget,
+ ClutterActor *actor)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ if (priv->first_visible_child == NULL && actor == NULL)
+ return;
+
+ if (priv->first_visible_child != NULL &&
+ CLUTTER_ACTOR (priv->first_visible_child) == actor)
+ return;
+
+ if (priv->first_visible_child != NULL)
+ {
+ st_widget_remove_style_pseudo_class (priv->first_visible_child, "first-child");
+ g_clear_object (&priv->first_visible_child);
+ }
+
+ if (actor == NULL)
+ return;
+
+ if (ST_IS_WIDGET (actor))
+ {
+ st_widget_add_style_pseudo_class (ST_WIDGET (actor), "first-child");
+ priv->first_visible_child = g_object_ref (ST_WIDGET (actor));
+ }
+}
+
+static void
+st_widget_set_last_visible_child (StWidget *widget,
+ ClutterActor *actor)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ if (priv->last_visible_child == NULL && actor == NULL)
+ return;
+
+ if (priv->last_visible_child != NULL &&
+ CLUTTER_ACTOR (priv->last_visible_child) == actor)
+ return;
+
+ if (priv->last_visible_child != NULL)
+ {
+ st_widget_remove_style_pseudo_class (priv->last_visible_child, "last-child");
+ g_clear_object (&priv->last_visible_child);
+ }
+
+ if (actor == NULL)
+ return;
+
+ if (ST_IS_WIDGET (actor))
+ {
+ st_widget_add_style_pseudo_class (ST_WIDGET (actor), "last-child");
+ priv->last_visible_child = g_object_ref (ST_WIDGET (actor));
+ }
+}
+
+static void
+st_widget_name_notify (StWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ st_widget_style_changed (widget);
+}
+
+static void
+st_widget_reactive_notify (StWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ st_widget_update_insensitive (widget);
+
+ if (priv->track_hover)
+ st_widget_sync_hover(widget);
+}
+
+static ClutterActor *
+find_nearest_visible_backwards (ClutterActor *actor)
+{
+ ClutterActor *prev = actor;
+
+ while (prev != NULL && !clutter_actor_is_visible (prev))
+ prev = clutter_actor_get_previous_sibling (prev);
+ return prev;
+}
+
+static ClutterActor *
+find_nearest_visible_forward (ClutterActor *actor)
+{
+ ClutterActor *next = actor;
+
+ while (next != NULL && !clutter_actor_is_visible (next))
+ next = clutter_actor_get_next_sibling (next);
+ return next;
+}
+
+static gboolean
+st_widget_update_child_styles (StWidget *widget)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ if (priv->first_child_dirty)
+ {
+ ClutterActor *first_child;
+
+ priv->first_child_dirty = FALSE;
+
+ first_child = clutter_actor_get_first_child (CLUTTER_ACTOR (widget));
+ st_widget_set_first_visible_child (widget,
+ find_nearest_visible_forward (first_child));
+ }
+
+ if (priv->last_child_dirty)
+ {
+ ClutterActor *last_child;
+
+ priv->last_child_dirty = FALSE;
+
+ last_child = clutter_actor_get_last_child (CLUTTER_ACTOR (widget));
+ st_widget_set_last_visible_child (widget,
+ find_nearest_visible_backwards (last_child));
+ }
+
+ priv->update_child_styles_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static void
+st_widget_queue_child_styles_update (StWidget *widget)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ if (priv->update_child_styles_id != 0)
+ return;
+
+ priv->update_child_styles_id = g_idle_add ((GSourceFunc) st_widget_update_child_styles, widget);
+}
+
+static void
+st_widget_visible_notify (StWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ StWidgetPrivate *parent_priv;
+ ClutterActor *actor = CLUTTER_ACTOR (widget);
+ ClutterActor *parent = clutter_actor_get_parent (actor);
+
+ if (parent == NULL || !ST_IS_WIDGET (parent))
+ return;
+
+ parent_priv = st_widget_get_instance_private (ST_WIDGET (parent));
+
+ if (clutter_actor_is_visible (actor))
+ {
+ ClutterActor *before, *after;
+
+ before = clutter_actor_get_previous_sibling (actor);
+ if (find_nearest_visible_backwards (before) == NULL)
+ parent_priv->first_child_dirty = TRUE;
+
+ after = clutter_actor_get_next_sibling (actor);
+ if (find_nearest_visible_forward (after) == NULL)
+ parent_priv->last_child_dirty = TRUE;
+ }
+ else
+ {
+ if (st_widget_has_style_pseudo_class (widget, "first-child"))
+ parent_priv->first_child_dirty = TRUE;
+
+ if (st_widget_has_style_pseudo_class (widget, "last-child"))
+ parent_priv->last_child_dirty = TRUE;
+ }
+
+ if (parent_priv->first_child_dirty || parent_priv->last_child_dirty)
+ st_widget_queue_child_styles_update (ST_WIDGET (parent));
+}
+
+static void
+st_widget_first_child_notify (StWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ priv->first_child_dirty = TRUE;
+ st_widget_queue_child_styles_update (widget);
+}
+
+static void
+st_widget_last_child_notify (StWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ priv->last_child_dirty = TRUE;
+ st_widget_queue_child_styles_update (widget);
+}
+
+static void
+st_widget_init (StWidget *actor)
+{
+ StWidgetPrivate *priv;
+ guint i;
+
+ priv = st_widget_get_instance_private (actor);
+ priv->transition_animation = NULL;
+ priv->local_state_set = atk_state_set_new ();
+
+ /* connect style changed */
+ g_signal_connect (actor, "notify::name", G_CALLBACK (st_widget_name_notify), NULL);
+ g_signal_connect (actor, "notify::reactive", G_CALLBACK (st_widget_reactive_notify), NULL);
+
+ g_signal_connect (actor, "notify::visible", G_CALLBACK (st_widget_visible_notify), NULL);
+ g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL);
+ g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL);
+ priv->texture_file_changed_id = g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
+ G_CALLBACK (st_widget_texture_cache_changed), actor);
+
+ for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
+ st_theme_node_paint_state_init (&priv->paint_states[i]);
+}
+
+static void
+on_transition_completed (StThemeNodeTransition *transition,
+ StWidget *widget)
+{
+ next_paint_state (widget);
+
+ st_theme_node_paint_state_copy (current_paint_state (widget),
+ st_theme_node_transition_get_new_paint_state (transition));
+
+ st_widget_remove_transition (widget);
+}
+
+static void
+st_widget_recompute_style (StWidget *widget,
+ StThemeNode *old_theme_node)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+ StThemeNode *new_theme_node = st_widget_get_theme_node (widget);
+ int transition_duration;
+ StSettings *settings;
+ gboolean paint_equal, geometry_equal = FALSE;
+ gboolean animations_enabled;
+
+ if (new_theme_node == old_theme_node)
+ {
+ priv->is_style_dirty = FALSE;
+ return;
+ }
+
+ _st_theme_node_apply_margins (new_theme_node, CLUTTER_ACTOR (widget));
+
+ if (old_theme_node)
+ geometry_equal = st_theme_node_geometry_equal (old_theme_node, new_theme_node);
+ if (!geometry_equal)
+ clutter_actor_queue_relayout ((ClutterActor *) widget);
+
+ transition_duration = st_theme_node_get_transition_duration (new_theme_node);
+
+ paint_equal = st_theme_node_paint_equal (old_theme_node, new_theme_node);
+
+ settings = st_settings_get ();
+ g_object_get (settings, "enable-animations", &animations_enabled, NULL);
+
+ if (animations_enabled && transition_duration > 0)
+ {
+ if (priv->transition_animation != NULL)
+ {
+ st_theme_node_transition_update (priv->transition_animation,
+ new_theme_node);
+ }
+ else if (old_theme_node && !paint_equal)
+ {
+ /* Since our transitions are only of the painting done by StThemeNode, we
+ * only want to start a transition when what is painted changes; if
+ * other visual aspects like the foreground color of a label change,
+ * we can't animate that anyways.
+ */
+
+ priv->transition_animation =
+ st_theme_node_transition_new (CLUTTER_ACTOR (widget),
+ old_theme_node,
+ new_theme_node,
+ current_paint_state (widget),
+ transition_duration);
+
+ g_signal_connect (priv->transition_animation, "completed",
+ G_CALLBACK (on_transition_completed), widget);
+ g_signal_connect_swapped (priv->transition_animation,
+ "new-frame",
+ G_CALLBACK (clutter_actor_queue_redraw),
+ widget);
+ }
+ }
+ else if (priv->transition_animation)
+ {
+ st_widget_remove_transition (widget);
+ }
+
+ if (!paint_equal)
+ {
+ clutter_actor_invalidate_paint_volume (CLUTTER_ACTOR (widget));
+
+ next_paint_state (widget);
+
+ if (!st_theme_node_paint_equal (new_theme_node, current_paint_state (widget)->node))
+ st_theme_node_paint_state_invalidate (current_paint_state (widget));
+ }
+
+ g_signal_emit (widget, signals[STYLE_CHANGED], 0);
+
+ priv->is_style_dirty = FALSE;
+}
+
+/**
+ * st_widget_ensure_style:
+ * @widget: A #StWidget
+ *
+ * Ensures that @widget has read its style information and propagated any
+ * changes to its children.
+ */
+void
+st_widget_ensure_style (StWidget *widget)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->is_style_dirty)
+ {
+ st_widget_recompute_style (widget, NULL);
+ notify_children_of_style_change (CLUTTER_ACTOR (widget));
+ }
+}
+
+/**
+ * st_widget_set_track_hover:
+ * @widget: A #StWidget
+ * @track_hover: %TRUE if the widget should track the pointer hover state
+ *
+ * Enables hover tracking on the #StWidget.
+ *
+ * If hover tracking is enabled, and the widget is visible and
+ * reactive, then @widget's #StWidget:hover property will be updated
+ * automatically to reflect whether the pointer is in @widget (or one
+ * of its children), and @widget's #StWidget:pseudo-class will have
+ * the "hover" class added and removed from it accordingly.
+ *
+ * Note that currently it is not possible to correctly track the hover
+ * state when another actor has a pointer grab. You can use
+ * st_widget_sync_hover() to update the property manually in this
+ * case.
+ */
+void
+st_widget_set_track_hover (StWidget *widget,
+ gboolean track_hover)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->track_hover != track_hover)
+ {
+ priv->track_hover = track_hover;
+ g_object_notify_by_pspec (G_OBJECT (widget), props[PROP_TRACK_HOVER]);
+
+ if (priv->track_hover)
+ st_widget_sync_hover (widget);
+ else
+ st_widget_set_hover (widget, FALSE);
+ }
+}
+
+/**
+ * st_widget_get_track_hover:
+ * @widget: A #StWidget
+ *
+ * Returns the current value of the #StWidget:track-hover property. See
+ * st_widget_set_track_hover() for more information.
+ *
+ * Returns: current value of track-hover on @widget
+ */
+gboolean
+st_widget_get_track_hover (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+ return ST_WIDGET_PRIVATE (widget)->track_hover;
+}
+
+/**
+ * st_widget_set_hover:
+ * @widget: A #StWidget
+ * @hover: whether the pointer is hovering over the widget
+ *
+ * Sets @widget's hover property and adds or removes "hover" from its
+ * pseudo class accordingly.
+ *
+ * If you have set #StWidget:track-hover, you should not need to call
+ * this directly. You can call st_widget_sync_hover() if the hover
+ * state might be out of sync due to another actor's pointer grab.
+ */
+void
+st_widget_set_hover (StWidget *widget,
+ gboolean hover)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->hover != hover)
+ {
+ priv->hover = hover;
+ if (priv->hover)
+ st_widget_add_style_pseudo_class (widget, "hover");
+ else
+ st_widget_remove_style_pseudo_class (widget, "hover");
+ g_object_notify_by_pspec (G_OBJECT (widget), props[PROP_HOVER]);
+ }
+}
+
+/**
+ * st_widget_sync_hover:
+ * @widget: A #StWidget
+ *
+ * Sets @widget's hover state according to the current pointer
+ * position. This can be used to ensure that it is correct after
+ * (or during) a pointer grab.
+ */
+void
+st_widget_sync_hover (StWidget *widget)
+{
+ ClutterInputDevice *pointer;
+ ClutterActor *stage;
+ ClutterActor *pointer_actor;
+ ClutterSeat *seat;
+
+ seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
+ pointer = clutter_seat_get_pointer (seat);
+ stage = clutter_actor_get_stage (CLUTTER_ACTOR (widget));
+ if (!stage)
+ return;
+
+ pointer_actor = clutter_stage_get_device_actor (CLUTTER_STAGE (stage), pointer, NULL);
+ if (pointer_actor && clutter_actor_get_reactive (CLUTTER_ACTOR (widget)))
+ st_widget_set_hover (widget, clutter_actor_contains (CLUTTER_ACTOR (widget), pointer_actor));
+ else
+ st_widget_set_hover (widget, FALSE);
+}
+
+/**
+ * st_widget_get_hover:
+ * @widget: A #StWidget
+ *
+ * If #StWidget:track-hover is set, this returns whether the pointer
+ * is currently over the widget.
+ *
+ * Returns: current value of hover on @widget
+ */
+gboolean
+st_widget_get_hover (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+ return ST_WIDGET_PRIVATE (widget)->hover;
+}
+
+/**
+ * st_widget_set_can_focus:
+ * @widget: A #StWidget
+ * @can_focus: %TRUE if the widget can receive keyboard focus
+ * via keyboard navigation
+ *
+ * Marks @widget as being able to receive keyboard focus via
+ * keyboard navigation.
+ */
+void
+st_widget_set_can_focus (StWidget *widget,
+ gboolean can_focus)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->can_focus != can_focus)
+ {
+ priv->can_focus = can_focus;
+ g_object_notify_by_pspec (G_OBJECT (widget), props[PROP_CAN_FOCUS]);
+ }
+}
+
+/**
+ * st_widget_get_can_focus:
+ * @widget: A #StWidget
+ *
+ * Returns the current value of the can-focus property. See
+ * st_widget_set_can_focus() for more information.
+ *
+ * Returns: current value of can-focus on @widget
+ */
+gboolean
+st_widget_get_can_focus (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+ return ST_WIDGET_PRIVATE (widget)->can_focus;
+}
+
+/**
+ * st_widget_popup_menu:
+ * @self: A #StWidget
+ *
+ * Asks the widget to pop-up a context menu by emitting #StWidget::popup-menu.
+ */
+void
+st_widget_popup_menu (StWidget *self)
+{
+ g_signal_emit (self, signals[POPUP_MENU], 0);
+}
+
+/* filter @children to contain only only actors that overlap @rbox
+ * when moving in @direction. (Assuming no transformations.)
+ */
+static GList *
+filter_by_position (GList *children,
+ ClutterActorBox *rbox,
+ StDirectionType direction)
+{
+ ClutterActorBox cbox;
+ graphene_point3d_t abs_vertices[4];
+ GList *l, *ret;
+ ClutterActor *child;
+
+ for (l = children, ret = NULL; l; l = l->next)
+ {
+ child = l->data;
+ clutter_actor_get_abs_allocation_vertices (child, abs_vertices);
+ clutter_actor_box_from_vertices (&cbox, abs_vertices);
+
+ /* Filter out children if they are in the wrong direction from
+ * @rbox, or if they don't overlap it. To account for floating-
+ * point imprecision, an actor is "down" (etc.) from an another
+ * actor even if it overlaps it by up to 0.1 pixels.
+ */
+ switch (direction)
+ {
+ case ST_DIR_UP:
+ if (cbox.y2 > rbox->y1 + 0.1)
+ continue;
+ break;
+
+ case ST_DIR_DOWN:
+ if (cbox.y1 < rbox->y2 - 0.1)
+ continue;
+ break;
+
+ case ST_DIR_LEFT:
+ if (cbox.x2 > rbox->x1 + 0.1)
+ continue;
+ break;
+
+ case ST_DIR_RIGHT:
+ if (cbox.x1 < rbox->x2 - 0.1)
+ continue;
+ break;
+
+ case ST_DIR_TAB_BACKWARD:
+ case ST_DIR_TAB_FORWARD:
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ ret = g_list_prepend (ret, child);
+ }
+
+ g_list_free (children);
+ return ret;
+}
+
+
+static void
+get_midpoint (ClutterActorBox *box,
+ int *x,
+ int *y)
+{
+ *x = (box->x1 + box->x2) / 2;
+ *y = (box->y1 + box->y2) / 2;
+}
+
+static double
+get_distance (ClutterActor *actor,
+ ClutterActorBox *bbox)
+{
+ int ax, ay, bx, by, dx, dy;
+ ClutterActorBox abox;
+ graphene_point3d_t abs_vertices[4];
+
+ clutter_actor_get_abs_allocation_vertices (actor, abs_vertices);
+ clutter_actor_box_from_vertices (&abox, abs_vertices);
+
+ get_midpoint (&abox, &ax, &ay);
+ get_midpoint (bbox, &bx, &by);
+ dx = ax - bx;
+ dy = ay - by;
+
+ /* Not the exact distance, but good enough to sort by. */
+ return dx*dx + dy*dy;
+}
+
+static int
+sort_by_distance (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ ClutterActor *actor_a = (ClutterActor *)a;
+ ClutterActor *actor_b = (ClutterActor *)b;
+ ClutterActorBox *box = user_data;
+
+ return get_distance (actor_a, box) - get_distance (actor_b, box);
+}
+
+static gboolean
+st_widget_real_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+ ClutterActor *widget_actor, *focus_child;
+ GList *children, *l;
+
+ widget_actor = CLUTTER_ACTOR (widget);
+ if (from == widget_actor)
+ return FALSE;
+
+ /* Figure out if @from is a descendant of @widget, and if so,
+ * set @focus_child to the immediate child of @widget that
+ * contains (or *is*) @from.
+ */
+ focus_child = from;
+ while (focus_child && clutter_actor_get_parent (focus_child) != widget_actor)
+ focus_child = clutter_actor_get_parent (focus_child);
+
+ if (priv->can_focus)
+ {
+ if (!focus_child)
+ {
+ if (clutter_actor_is_mapped (widget_actor))
+ {
+ /* Accept focus from outside */
+ clutter_actor_grab_key_focus (widget_actor);
+ return TRUE;
+ }
+ else
+ {
+ /* Refuse to set focus on hidden actors */
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* Yield focus from within: since @widget itself is
+ * focusable we don't allow the focus to be navigated
+ * within @widget.
+ */
+ return FALSE;
+ }
+ }
+
+ /* See if we can navigate within @focus_child */
+ if (focus_child && ST_IS_WIDGET (focus_child))
+ {
+ if (st_widget_navigate_focus (ST_WIDGET (focus_child), from, direction, FALSE))
+ return TRUE;
+ }
+
+ children = st_widget_get_focus_chain (widget);
+ if (direction == ST_DIR_TAB_FORWARD ||
+ direction == ST_DIR_TAB_BACKWARD)
+ {
+ /* At this point we know that we want to navigate focus to one of
+ * @widget's immediate children; the next one after @focus_child, or the
+ * first one if @focus_child is %NULL. (With "next" and "first" being
+ * determined by @direction.)
+ */
+ if (direction == ST_DIR_TAB_BACKWARD)
+ children = g_list_reverse (children);
+
+ if (focus_child)
+ {
+ /* Remove focus_child and any earlier children */
+ while (children && children->data != focus_child)
+ children = g_list_delete_link (children, children);
+ if (children)
+ children = g_list_delete_link (children, children);
+ }
+ }
+ else /* direction is an arrow key, not tab */
+ {
+ ClutterActorBox sort_box;
+ graphene_point3d_t abs_vertices[4];
+
+ /* Compute the allocation box of the previous focused actor. If there
+ * was no previous focus, use the coordinates of the appropriate edge of
+ * @widget.
+ *
+ * Note that all of this code assumes the actors are not
+ * transformed (or at most, they are all scaled by the same
+ * amount). If @widget or any of its children is rotated, or
+ * any child is inconsistently scaled, then the focus chain will
+ * probably be unpredictable.
+ */
+ if (from)
+ {
+ clutter_actor_get_abs_allocation_vertices (from, abs_vertices);
+ clutter_actor_box_from_vertices (&sort_box, abs_vertices);
+ }
+ else
+ {
+ clutter_actor_get_abs_allocation_vertices (widget_actor, abs_vertices);
+ clutter_actor_box_from_vertices (&sort_box, abs_vertices);
+ switch (direction)
+ {
+ case ST_DIR_UP:
+ sort_box.y1 = sort_box.y2;
+ break;
+ case ST_DIR_DOWN:
+ sort_box.y2 = sort_box.y1;
+ break;
+ case ST_DIR_LEFT:
+ sort_box.x1 = sort_box.x2;
+ break;
+ case ST_DIR_RIGHT:
+ sort_box.x2 = sort_box.x1;
+ break;
+ case ST_DIR_TAB_FORWARD:
+ case ST_DIR_TAB_BACKWARD:
+ default:
+ g_warn_if_reached ();
+ }
+ }
+
+ if (from)
+ children = filter_by_position (children, &sort_box, direction);
+ if (children)
+ children = g_list_sort_with_data (children, sort_by_distance, &sort_box);
+ }
+
+ /* Now try each child in turn */
+ for (l = children; l; l = l->next)
+ {
+ if (ST_IS_WIDGET (l->data))
+ {
+ if (st_widget_navigate_focus (l->data, from, direction, FALSE))
+ {
+ g_list_free (children);
+ return TRUE;
+ }
+ }
+ }
+
+ g_list_free (children);
+ return FALSE;
+}
+
+
+/**
+ * st_widget_navigate_focus:
+ * @widget: the "top level" container
+ * @from: (nullable): the actor that the focus is coming from
+ * @direction: the direction focus is moving in
+ * @wrap_around: whether focus should wrap around
+ *
+ * Tries to update the keyboard focus within @widget in response to a
+ * keyboard event.
+ *
+ * If @from is a descendant of @widget, this attempts to move the
+ * keyboard focus to the next descendant of @widget (in the order
+ * implied by @direction) that has the #StWidget:can-focus property
+ * set. If @from is %NULL, this attempts to focus either @widget
+ * itself, or its first descendant in the order implied by
+ * @direction. If @from is outside of @widget, it behaves as if it was
+ * a descendant if @direction is one of the directional arrows and as
+ * if it was %NULL otherwise.
+ *
+ * If a container type is marked #StWidget:can-focus, the expected
+ * behavior is that it will only take up a single slot on the focus
+ * chain as a whole, rather than allowing navigation between its child
+ * actors (or having a distinction between itself being focused and
+ * one of its children being focused).
+ *
+ * Some widget classes might have slightly different behavior from the
+ * above, where that would make more sense.
+ *
+ * If @wrap_around is %TRUE and @from is a child of @widget, but the
+ * widget has no further children that can accept the focus in the
+ * given direction, then st_widget_navigate_focus() will try a second
+ * time, using a %NULL @from, which should cause it to reset the focus
+ * to the first available widget in the given direction.
+ *
+ * Returns: %TRUE if clutter_actor_grab_key_focus() has been
+ * called on an actor. %FALSE if not.
+ */
+gboolean
+st_widget_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction,
+ gboolean wrap_around)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+ if (ST_WIDGET_GET_CLASS (widget)->navigate_focus (widget, from, direction))
+ return TRUE;
+ if (wrap_around && from && clutter_actor_contains (CLUTTER_ACTOR (widget), from))
+ return ST_WIDGET_GET_CLASS (widget)->navigate_focus (widget, NULL, direction);
+ return FALSE;
+}
+
+static gboolean
+append_actor_text (GString *desc,
+ ClutterActor *actor)
+{
+ if (CLUTTER_IS_TEXT (actor))
+ {
+ g_string_append_printf (desc, " (\"%s\")",
+ clutter_text_get_text (CLUTTER_TEXT (actor)));
+ return TRUE;
+ }
+ else if (ST_IS_LABEL (actor))
+ {
+ g_string_append_printf (desc, " (\"%s\")",
+ st_label_get_text (ST_LABEL (actor)));
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * st_describe_actor:
+ * @actor: a #ClutterActor
+ *
+ * Creates a string describing @actor, for use in debugging. This
+ * includes the class name and actor name (if any), plus if @actor
+ * is an #StWidget, its style class and pseudo class names.
+ *
+ * Returns: the debug name.
+ */
+char *
+st_describe_actor (ClutterActor *actor)
+{
+ GString *desc;
+ const char *name;
+ int i;
+
+ if (!actor)
+ return g_strdup ("[null]");
+
+ desc = g_string_new (NULL);
+ g_string_append_printf (desc, "[%p %s", actor,
+ G_OBJECT_TYPE_NAME (actor));
+
+ if (ST_IS_WIDGET (actor))
+ {
+ const char *style_class = st_widget_get_style_class_name (ST_WIDGET (actor));
+ const char *pseudo_class = st_widget_get_style_pseudo_class (ST_WIDGET (actor));
+ char **classes;
+
+ if (style_class)
+ {
+ classes = g_strsplit (style_class, ",", -1);
+ for (i = 0; classes[i]; i++)
+ {
+ g_strchug (classes[i]);
+ g_string_append_printf (desc, ".%s", classes[i]);
+ }
+ g_strfreev (classes);
+ }
+
+ if (pseudo_class)
+ {
+ classes = g_strsplit (pseudo_class, ",", -1);
+ for (i = 0; classes[i]; i++)
+ {
+ g_strchug (classes[i]);
+ g_string_append_printf (desc, ":%s", classes[i]);
+ }
+ g_strfreev (classes);
+ }
+ }
+
+ name = clutter_actor_get_name (actor);
+ if (name)
+ g_string_append_printf (desc, " \"%s\"", name);
+
+ if (!append_actor_text (desc, actor))
+ {
+ GList *children, *l;
+
+ /* Do a limited search of @actor's children looking for a label */
+ children = clutter_actor_get_children (actor);
+ for (l = children, i = 0; l && i < 20; l = l->next, i++)
+ {
+ if (append_actor_text (desc, l->data))
+ break;
+ children = g_list_concat (children, clutter_actor_get_children (l->data));
+ }
+ g_list_free (children);
+ }
+
+ g_string_append_c (desc, ']');
+
+ return g_string_free (desc, FALSE);
+}
+
+/**
+ * st_widget_get_label_actor:
+ * @widget: a #StWidget
+ *
+ * Gets the label that identifies @widget if it is defined
+ *
+ * Returns: (transfer none): the label that identifies the widget
+ */
+ClutterActor *
+st_widget_get_label_actor (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), NULL);
+
+ return ST_WIDGET_PRIVATE (widget)->label_actor;
+}
+
+/**
+ * st_widget_set_label_actor:
+ * @widget: a #StWidget
+ * @label: a #ClutterActor
+ *
+ * Sets @label as the #ClutterActor that identifies (labels)
+ * @widget. @label can be %NULL to indicate that @widget is not
+ * labelled any more
+ */
+
+void
+st_widget_set_label_actor (StWidget *widget,
+ ClutterActor *label)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->label_actor != label)
+ {
+ if (priv->label_actor)
+ g_object_unref (priv->label_actor);
+
+ if (label != NULL)
+ priv->label_actor = g_object_ref (label);
+ else
+ priv->label_actor = NULL;
+
+ g_object_notify_by_pspec (G_OBJECT (widget), props[PROP_LABEL_ACTOR]);
+ }
+}
+
+/**
+ * st_widget_set_accessible_name:
+ * @widget: widget to set the accessible name for
+ * @name: (nullable): a character string to be set as the accessible name
+ *
+ * This method sets @name as the accessible name for @widget.
+ *
+ * Usually you will have no need to set the accessible name for an
+ * object, as usually there is a label for most of the interface
+ * elements. So in general it is better to just use
+ * @st_widget_set_label_actor. This method is only required when you
+ * need to set an accessible name and there is no available label
+ * object.
+ *
+ */
+void
+st_widget_set_accessible_name (StWidget *widget,
+ const gchar *name)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (g_strcmp0 (name, priv->accessible_name) == 0)
+ return;
+
+ if (priv->accessible_name != NULL)
+ g_free (priv->accessible_name);
+
+ priv->accessible_name = g_strdup (name);
+ g_object_notify_by_pspec (G_OBJECT (widget), props[PROP_ACCESSIBLE_NAME]);
+}
+
+/**
+ * st_widget_get_accessible_name:
+ * @widget: widget to get the accessible name for
+ *
+ * Gets the accessible name for this widget. See
+ * st_widget_set_accessible_name() for more information.
+ *
+ * Returns: a character string representing the accessible name
+ * of the widget.
+ */
+const gchar *
+st_widget_get_accessible_name (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), NULL);
+
+ return ST_WIDGET_PRIVATE (widget)->accessible_name;
+}
+
+/**
+ * st_widget_set_accessible_role:
+ * @widget: widget to set the accessible role for
+ * @role: The role to use
+ *
+ * This method sets @role as the accessible role for @widget. This
+ * role describes what kind of user interface element @widget is and
+ * is provided so that assistive technologies know how to present
+ * @widget to the user.
+ *
+ * Usually you will have no need to set the accessible role for an
+ * object, as this information is extracted from the context of the
+ * object (ie: a #StButton has by default a push button role). This
+ * method is only required when you need to redefine the role
+ * currently associated with the widget, for instance if it is being
+ * used in an unusual way (ie: a #StButton used as a togglebutton), or
+ * if a generic object is used directly (ie: a container as a menu
+ * item).
+ *
+ * If @role is #ATK_ROLE_INVALID, the role will not be changed
+ * and the accessible's default role will be used instead.
+ */
+void
+st_widget_set_accessible_role (StWidget *widget,
+ AtkRole role)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->accessible_role == role)
+ return;
+
+ priv->accessible_role = role;
+
+ g_object_notify_by_pspec (G_OBJECT (widget), props[PROP_ACCESSIBLE_ROLE]);
+}
+
+
+/**
+ * st_widget_get_accessible_role:
+ * @widget: widget to get the accessible role for
+ *
+ * Gets the #AtkRole for this widget. See
+ * st_widget_set_accessible_role() for more information.
+ *
+ * Returns: accessible #AtkRole for this widget
+ */
+AtkRole
+st_widget_get_accessible_role (StWidget *widget)
+{
+ StWidgetPrivate *priv;
+ AtkRole role = ATK_ROLE_INVALID;
+
+ g_return_val_if_fail (ST_IS_WIDGET (widget), ATK_ROLE_INVALID);
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->accessible_role != ATK_ROLE_INVALID)
+ role = priv->accessible_role;
+ else if (priv->accessible != NULL)
+ role = atk_object_get_role (priv->accessible);
+
+ return role;
+}
+
+static void
+notify_accessible_state_change (StWidget *widget,
+ AtkStateType state,
+ gboolean value)
+{
+ StWidgetPrivate *priv = st_widget_get_instance_private (widget);
+
+ if (priv->accessible != NULL)
+ atk_object_notify_state_change (priv->accessible, state, value);
+}
+
+/**
+ * st_widget_add_accessible_state:
+ * @widget: A #StWidget
+ * @state: #AtkStateType state to add
+ *
+ * This method adds @state as one of the accessible states for
+ * @widget. The list of states of a widget describes the current state
+ * of user interface element @widget and is provided so that assistive
+ * technologies know how to present @widget to the user.
+ *
+ * Usually you will have no need to add accessible states for an
+ * object, as the accessible object can extract most of the states
+ * from the object itself (ie: a #StButton knows when it is pressed).
+ * This method is only required when one cannot extract the
+ * information automatically from the object itself (i.e.: a generic
+ * container used as a toggle menu item will not automatically include
+ * the toggled state).
+ *
+ */
+void
+st_widget_add_accessible_state (StWidget *widget,
+ AtkStateType state)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (atk_state_set_add_state (priv->local_state_set, state))
+ notify_accessible_state_change (widget, state, TRUE);
+}
+
+/**
+ * st_widget_remove_accessible_state:
+ * @widget: A #StWidget
+ * @state: #AtkState state to remove
+ *
+ * This method removes @state as on of the accessible states for
+ * @widget. See st_widget_add_accessible_state() for more information.
+ *
+ */
+void
+st_widget_remove_accessible_state (StWidget *widget,
+ AtkStateType state)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (atk_state_set_remove_state (priv->local_state_set, state))
+ notify_accessible_state_change (widget, state, FALSE);
+}
+
+/******************************************************************************/
+/*************************** ACCESSIBILITY SUPPORT ****************************/
+/******************************************************************************/
+
+/* GObject */
+
+static void st_widget_accessible_dispose (GObject *gobject);
+
+/* AtkObject */
+static AtkStateSet *st_widget_accessible_ref_state_set (AtkObject *obj);
+static void st_widget_accessible_initialize (AtkObject *obj,
+ gpointer data);
+static AtkRole st_widget_accessible_get_role (AtkObject *obj);
+
+/* Private methods */
+static void on_pseudo_class_notify (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data);
+static void on_can_focus_notify (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data);
+static void on_label_notify (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data);
+static void check_pseudo_class (StWidgetAccessible *self,
+ StWidget *widget);
+static void check_labels (StWidgetAccessible *self,
+ StWidget *widget);
+
+struct _StWidgetAccessiblePrivate
+{
+ /* Cached values (used to avoid extra notifications) */
+ gboolean selected;
+ gboolean checked;
+
+ /* The current_label. Right now there are the proper atk
+ * relationships between this object and the label
+ */
+ AtkObject *current_label;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (StWidgetAccessible, st_widget_accessible, CALLY_TYPE_ACTOR)
+
+static gboolean
+st_widget_has_accessible (ClutterActor *actor)
+{
+ StWidget *widget;
+ StWidgetPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE);
+
+ widget = ST_WIDGET (actor);
+ priv = st_widget_get_instance_private (widget);
+
+ return priv->accessible != NULL;
+}
+
+static AtkObject *
+st_widget_get_accessible (ClutterActor *actor)
+{
+ StWidget *widget = NULL;
+ StWidgetPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
+
+ widget = ST_WIDGET (actor);
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->accessible == NULL)
+ {
+ priv->accessible =
+ g_object_new (ST_WIDGET_GET_CLASS (widget)->get_accessible_type (),
+ NULL);
+
+ atk_object_initialize (priv->accessible, actor);
+
+ /* AtkGObjectAccessible, which StWidgetAccessible derives from, clears
+ * the back reference to the object in a weak notify for the object;
+ * weak-ref notification, which occurs during g_object_real_dispose(),
+ * is then the optimal time to clear the forward reference. We
+ * can't clear the reference in dispose() before chaining up, since
+ * clutter_actor_dispose() causes notifications to be sent out, which
+ * will result in a new accessible object being created.
+ */
+ g_object_add_weak_pointer (G_OBJECT (actor),
+ (gpointer *)&priv->accessible);
+ }
+
+ return priv->accessible;
+}
+
+/**
+ * st_widget_set_accessible:
+ * @widget: A #StWidget
+ * @accessible: an accessible (#AtkObject)
+ *
+ * This method allows to set a customly created accessible object to
+ * this widget. For example if you define a new subclass of
+ * #StWidgetAccessible at the javascript code.
+ *
+ * NULL is a valid value for @accessible. That contemplates the
+ * hypothetical case of not needing anymore a custom accessible object
+ * for the widget. Next call of st_widget_get_accessible() would
+ * create and return a default accessible.
+ *
+ * It assumes that the call to atk_object_initialize that bound the
+ * gobject with the custom accessible object was already called, so
+ * not a responsibility of this method.
+ *
+ */
+void
+st_widget_set_accessible (StWidget *widget,
+ AtkObject *accessible)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+ g_return_if_fail (accessible == NULL || ATK_IS_GOBJECT_ACCESSIBLE (accessible));
+
+ priv = st_widget_get_instance_private (widget);
+
+ if (priv->accessible != accessible)
+ {
+ if (priv->accessible)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (widget),
+ (gpointer *)&priv->accessible);
+ g_object_unref (priv->accessible);
+ priv->accessible = NULL;
+ }
+
+ if (accessible)
+ {
+ priv->accessible = g_object_ref (accessible);
+ /* See note in st_widget_get_accessible() */
+ g_object_add_weak_pointer (G_OBJECT (widget),
+ (gpointer *)&priv->accessible);
+ }
+ else
+ priv->accessible = NULL;
+ }
+}
+
+static const gchar *
+st_widget_accessible_get_name (AtkObject *obj)
+{
+ const gchar* name = NULL;
+
+ g_return_val_if_fail (ST_IS_WIDGET_ACCESSIBLE (obj), NULL);
+
+ name = ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->get_name (obj);
+ if (name == NULL)
+ {
+ StWidget *widget = NULL;
+
+ widget = ST_WIDGET (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+ if (widget == NULL)
+ name = NULL;
+ else
+ name = st_widget_get_accessible_name (widget);
+ }
+
+ return name;
+}
+
+static void
+st_widget_accessible_class_init (StWidgetAccessibleClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = st_widget_accessible_dispose;
+
+ atk_class->ref_state_set = st_widget_accessible_ref_state_set;
+ atk_class->initialize = st_widget_accessible_initialize;
+ atk_class->get_role = st_widget_accessible_get_role;
+ atk_class->get_name = st_widget_accessible_get_name;
+}
+
+static void
+st_widget_accessible_init (StWidgetAccessible *self)
+{
+ StWidgetAccessiblePrivate *priv = st_widget_accessible_get_instance_private (self);
+
+ self->priv = priv;
+}
+
+static void
+st_widget_accessible_dispose (GObject *gobject)
+{
+ StWidgetAccessible *self = ST_WIDGET_ACCESSIBLE (gobject);
+
+ if (self->priv->current_label)
+ {
+ g_object_unref (self->priv->current_label);
+ self->priv->current_label = NULL;
+ }
+
+ G_OBJECT_CLASS (st_widget_accessible_parent_class)->dispose (gobject);
+}
+
+static void
+on_accessible_name_notify (GObject *gobject,
+ GParamSpec *pspec,
+ AtkObject *accessible)
+{
+ g_object_notify (G_OBJECT (accessible), "accessible-name");
+}
+
+static void
+st_widget_accessible_initialize (AtkObject *obj,
+ gpointer data)
+{
+ ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->initialize (obj, data);
+
+ g_signal_connect (data, "notify::pseudo-class",
+ G_CALLBACK (on_pseudo_class_notify),
+ obj);
+
+ g_signal_connect (data, "notify::can-focus",
+ G_CALLBACK (on_can_focus_notify),
+ obj);
+
+ g_signal_connect (data, "notify::label-actor",
+ G_CALLBACK (on_label_notify),
+ obj);
+
+ g_signal_connect (data, "notify::accessible-name",
+ G_CALLBACK (on_accessible_name_notify),
+ obj);
+
+ /* Check the cached selected state and notify the first selection.
+ * Ie: it is required to ensure a first notification when Alt+Tab
+ * popup appears
+ */
+ check_pseudo_class (ST_WIDGET_ACCESSIBLE (obj), ST_WIDGET (data));
+ check_labels (ST_WIDGET_ACCESSIBLE (obj), ST_WIDGET (data));
+}
+
+static AtkStateSet *
+st_widget_accessible_ref_state_set (AtkObject *obj)
+{
+ AtkStateSet *result = NULL;
+ AtkStateSet *aux_set = NULL;
+ ClutterActor *actor = NULL;
+ StWidget *widget = NULL;
+ StWidgetPrivate *widget_priv;
+ StWidgetAccessible *self = NULL;
+
+ result = ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->ref_state_set (obj);
+
+ actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+ if (actor == NULL) /* State is defunct */
+ return result;
+
+ widget = ST_WIDGET (actor);
+ self = ST_WIDGET_ACCESSIBLE (obj);
+ widget_priv = st_widget_get_instance_private (widget);
+
+ /* priv->selected should be properly updated on the
+ * ATK_STATE_SELECTED notification callbacks
+ */
+ if (self->priv->selected)
+ atk_state_set_add_state (result, ATK_STATE_SELECTED);
+
+ if (self->priv->checked)
+ atk_state_set_add_state (result, ATK_STATE_CHECKED);
+
+ /* On clutter there isn't any tip to know if a actor is focusable or
+ * not, anyone can receive the key_focus. For this reason
+ * cally_actor sets any actor as FOCUSABLE. This is not the case on
+ * St, where we have can_focus. But this means that we need to
+ * remove the state FOCUSABLE if it is not focusable
+ */
+ if (st_widget_get_can_focus (widget))
+ atk_state_set_add_state (result, ATK_STATE_FOCUSABLE);
+ else
+ atk_state_set_remove_state (result, ATK_STATE_FOCUSABLE);
+
+ /* We add the states added externally if required */
+ if (!atk_state_set_is_empty (widget_priv->local_state_set))
+ {
+ aux_set = atk_state_set_or_sets (result, widget_priv->local_state_set);
+
+ g_object_unref (result); /* previous result will not be used */
+ result = aux_set;
+ }
+
+ return result;
+}
+
+static AtkRole
+st_widget_accessible_get_role (AtkObject *obj)
+{
+ StWidget *widget = NULL;
+ StWidgetPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_WIDGET_ACCESSIBLE (obj), ATK_ROLE_INVALID);
+
+ widget = ST_WIDGET (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+ if (widget == NULL)
+ return ATK_ROLE_INVALID;
+
+ priv = st_widget_get_instance_private (widget);
+ if (priv->accessible_role != ATK_ROLE_INVALID)
+ return priv->accessible_role;
+
+ return ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->get_role (obj);
+}
+
+static void
+on_pseudo_class_notify (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ check_pseudo_class (ST_WIDGET_ACCESSIBLE (data),
+ ST_WIDGET (gobject));
+}
+
+/*
+ * In some cases the only way to check some states are checking the
+ * pseudo-class. Like if the object is selected (see bug 637830) or if
+ * the object is toggled. This method also notifies a state change if
+ * the value is different to the one cached.
+ *
+ * We also assume that if the object uses that pseudo-class, it makes
+ * sense to notify that state change. It would be possible to refine
+ * that behaviour checking the role (ie: notify CHECKED changes only
+ * for CHECK_BUTTON roles).
+ *
+ * In a ideal world we would have a more standard way to get the
+ * state, like the widget-context (as in the case of
+ * gtktreeview-cells), or something like the property "can-focus". But
+ * for the moment this is enough, and we can update that in the future
+ * if required.
+ */
+static void
+check_pseudo_class (StWidgetAccessible *self,
+ StWidget *widget)
+{
+ gboolean found = FALSE;
+
+ found = st_widget_has_style_pseudo_class (widget,
+ "selected");
+
+ if (found != self->priv->selected)
+ {
+ self->priv->selected = found;
+ atk_object_notify_state_change (ATK_OBJECT (self),
+ ATK_STATE_SELECTED,
+ found);
+ }
+
+ found = st_widget_has_style_pseudo_class (widget,
+ "checked");
+ if (found != self->priv->checked)
+ {
+ self->priv->checked = found;
+ atk_object_notify_state_change (ATK_OBJECT (self),
+ ATK_STATE_CHECKED,
+ found);
+ }
+}
+
+static void
+on_can_focus_notify (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ gboolean can_focus = st_widget_get_can_focus (ST_WIDGET (gobject));
+
+ atk_object_notify_state_change (ATK_OBJECT (data),
+ ATK_STATE_FOCUSABLE, can_focus);
+}
+
+static void
+on_label_notify (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ check_labels (ST_WIDGET_ACCESSIBLE (data), ST_WIDGET (gobject));
+}
+
+static void
+check_labels (StWidgetAccessible *widget_accessible,
+ StWidget *widget)
+{
+ ClutterActor *label = NULL;
+ AtkObject *label_accessible = NULL;
+
+ /* We only call this method at startup, and when the label changes,
+ * so it is fine to remove the previous relationships if we have the
+ * current_label by default
+ */
+ if (widget_accessible->priv->current_label != NULL)
+ {
+ AtkObject *previous_label = widget_accessible->priv->current_label;
+
+ atk_object_remove_relationship (ATK_OBJECT (widget_accessible),
+ ATK_RELATION_LABELLED_BY,
+ previous_label);
+
+ atk_object_remove_relationship (previous_label,
+ ATK_RELATION_LABEL_FOR,
+ ATK_OBJECT (widget_accessible));
+
+ g_object_unref (previous_label);
+ }
+
+ label = st_widget_get_label_actor (widget);
+ if (label == NULL)
+ {
+ widget_accessible->priv->current_label = NULL;
+ }
+ else
+ {
+ label_accessible = clutter_actor_get_accessible (label);
+ widget_accessible->priv->current_label = g_object_ref (label_accessible);
+
+ atk_object_add_relationship (ATK_OBJECT (widget_accessible),
+ ATK_RELATION_LABELLED_BY,
+ label_accessible);
+
+ atk_object_add_relationship (label_accessible,
+ ATK_RELATION_LABEL_FOR,
+ ATK_OBJECT (widget_accessible));
+ }
+}
+
+/**
+ * st_widget_get_focus_chain:
+ * @widget: An #StWidget
+ *
+ * Gets a list of the focusable children of @widget, in "Tab"
+ * order. By default, this returns all visible
+ * (as in clutter_actor_is_visible()) children of @widget.
+ *
+ * Returns: (element-type Clutter.Actor) (transfer container):
+ * @widget's focusable children
+ */
+GList *
+st_widget_get_focus_chain (StWidget *widget)
+{
+ return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget);
+}
diff --git a/src/st/st-widget.h b/src/st/st-widget.h
new file mode 100644
index 0000000..f00c987
--- /dev/null
+++ b/src/st/st-widget.h
@@ -0,0 +1,167 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-widget.h: Base class for St actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2009 Abderrahim Kitouni
+ * Copyright 2010 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_WIDGET_H__
+#define __ST_WIDGET_H__
+
+#include <clutter/clutter.h>
+#include <st/st-types.h>
+#include <st/st-theme.h>
+#include <st/st-theme-node.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_WIDGET (st_widget_get_type ())
+G_DECLARE_DERIVABLE_TYPE (StWidget, st_widget, ST, WIDGET, ClutterActor)
+
+/**
+ * StDirectionType:
+ * @ST_DIR_TAB_FORWARD: Move forward.
+ * @ST_DIR_TAB_BACKWARD: Move backward.
+ * @ST_DIR_UP: Move up.
+ * @ST_DIR_DOWN: Move down.
+ * @ST_DIR_LEFT: Move left.
+ * @ST_DIR_RIGHT: Move right.
+ *
+ * Enumeration for focus direction.
+ */
+typedef enum
+{
+ ST_DIR_TAB_FORWARD,
+ ST_DIR_TAB_BACKWARD,
+ ST_DIR_UP,
+ ST_DIR_DOWN,
+ ST_DIR_LEFT,
+ ST_DIR_RIGHT,
+} StDirectionType;
+
+typedef struct _StWidgetClass StWidgetClass;
+
+/**
+ * StWidgetClass:
+ *
+ * Base class for stylable actors.
+ */
+struct _StWidgetClass
+{
+ /*< private >*/
+ ClutterActorClass parent_class;
+
+ /* signals */
+ void (* style_changed) (StWidget *self);
+ void (* popup_menu) (StWidget *self);
+
+ /* vfuncs */
+
+ /**
+ * StWidgetClass::navigate_focus:
+ * @self: the "top level" container
+ * @from: (nullable): the actor that the focus is coming from
+ * @direction: the direction focus is moving in
+ */
+ gboolean (* navigate_focus) (StWidget *self,
+ ClutterActor *from,
+ StDirectionType direction);
+ GType (* get_accessible_type) (void);
+
+ GList * (* get_focus_chain) (StWidget *widget);
+};
+
+void st_widget_set_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class_list);
+void st_widget_add_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class);
+void st_widget_remove_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class);
+const gchar * st_widget_get_style_pseudo_class (StWidget *actor);
+gboolean st_widget_has_style_pseudo_class (StWidget *actor,
+ const gchar *pseudo_class);
+
+void st_widget_set_style_class_name (StWidget *actor,
+ const gchar *style_class_list);
+void st_widget_add_style_class_name (StWidget *actor,
+ const gchar *style_class);
+void st_widget_remove_style_class_name (StWidget *actor,
+ const gchar *style_class);
+const gchar * st_widget_get_style_class_name (StWidget *actor);
+gboolean st_widget_has_style_class_name (StWidget *actor,
+ const gchar *style_class);
+
+void st_widget_set_style (StWidget *actor,
+ const gchar *style);
+const gchar * st_widget_get_style (StWidget *actor);
+void st_widget_set_track_hover (StWidget *widget,
+ gboolean track_hover);
+gboolean st_widget_get_track_hover (StWidget *widget);
+void st_widget_set_hover (StWidget *widget,
+ gboolean hover);
+void st_widget_sync_hover (StWidget *widget);
+gboolean st_widget_get_hover (StWidget *widget);
+void st_widget_popup_menu (StWidget *self);
+
+void st_widget_ensure_style (StWidget *widget);
+
+void st_widget_set_can_focus (StWidget *widget,
+ gboolean can_focus);
+gboolean st_widget_get_can_focus (StWidget *widget);
+gboolean st_widget_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction,
+ gboolean wrap_around);
+
+ClutterActor * st_widget_get_label_actor (StWidget *widget);
+void st_widget_set_label_actor (StWidget *widget,
+ ClutterActor *label);
+
+/* Only to be used by sub-classes of StWidget */
+void st_widget_style_changed (StWidget *widget);
+StThemeNode * st_widget_get_theme_node (StWidget *widget);
+StThemeNode * st_widget_peek_theme_node (StWidget *widget);
+
+GList * st_widget_get_focus_chain (StWidget *widget);
+void st_widget_paint_background (StWidget *widget,
+ ClutterPaintContext *paint_context);
+
+/* debug methods */
+char *st_describe_actor (ClutterActor *actor);
+
+/* accessibility methods */
+void st_widget_set_accessible_role (StWidget *widget,
+ AtkRole role);
+AtkRole st_widget_get_accessible_role (StWidget *widget);
+void st_widget_add_accessible_state (StWidget *widget,
+ AtkStateType state);
+void st_widget_remove_accessible_state (StWidget *widget,
+ AtkStateType state);
+void st_widget_set_accessible_name (StWidget *widget,
+ const gchar *name);
+const gchar * st_widget_get_accessible_name (StWidget *widget);
+void st_widget_set_accessible (StWidget *widget,
+ AtkObject *accessible);
+G_END_DECLS
+
+#endif /* __ST_WIDGET_H__ */
diff --git a/src/st/st.h.in b/src/st/st.h.in
new file mode 100644
index 0000000..825c820
--- /dev/null
+++ b/src/st/st.h.in
@@ -0,0 +1,3 @@
+#define ST_H_INSIDE 1
+@includes@
+#undef ST_H_INSIDE
diff --git a/src/st/test-theme.c b/src/st/test-theme.c
new file mode 100644
index 0000000..3fcbd99
--- /dev/null
+++ b/src/st/test-theme.c
@@ -0,0 +1,637 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * test-theme.c: test program for CSS styling code
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <clutter/clutter.h>
+#include "st-theme.h"
+#include "st-theme-context.h"
+#include "st-label.h"
+#include "st-button.h"
+#include <math.h>
+#include <string.h>
+#include <meta-test/meta-context-test.h>
+#include <meta/meta-backend.h>
+
+static ClutterActor *stage;
+static StThemeNode *root;
+static StThemeNode *group1;
+static StThemeNode *text1;
+static StThemeNode *text2;
+static StThemeNode *group2;
+static StThemeNode *text3;
+static StThemeNode *text4;
+static StThemeNode *group3;
+static StThemeNode *group4;
+static StThemeNode *group5;
+static StThemeNode *group6;
+static StThemeNode *button;
+static gboolean fail;
+
+static const char *test;
+
+static void
+assert_font (StThemeNode *node,
+ const char *node_description,
+ const char *expected)
+{
+ char *value = pango_font_description_to_string (st_theme_node_get_font (node));
+
+ if (strcmp (expected, value) != 0)
+ {
+ g_print ("%s: %s.font: expected: %s, got: %s\n",
+ test, node_description, expected, value);
+ fail = TRUE;
+ }
+
+ g_free (value);
+}
+
+static void
+assert_font_features (StThemeNode *node,
+ const char *node_description,
+ const char *expected)
+{
+ char *value = st_theme_node_get_font_features (node);
+
+ if (g_strcmp0 (expected, value) != 0)
+ {
+ g_print ("%s: %s.font-feature-settings: expected: %s, got: %s\n",
+ test, node_description, expected, value);
+ fail = TRUE;
+ }
+
+ if (value)
+ g_free (value);
+}
+
+static char *
+text_decoration_to_string (StTextDecoration decoration)
+{
+ GString *result = g_string_new (NULL);
+
+ if (decoration & ST_TEXT_DECORATION_UNDERLINE)
+ g_string_append(result, " underline");
+ if (decoration & ST_TEXT_DECORATION_OVERLINE)
+ g_string_append(result, " overline");
+ if (decoration & ST_TEXT_DECORATION_LINE_THROUGH)
+ g_string_append(result, " line_through");
+ if (decoration & ST_TEXT_DECORATION_BLINK)
+ g_string_append(result, " blink");
+
+ if (result->len > 0)
+ g_string_erase (result, 0, 1);
+ else
+ g_string_append(result, "none");
+
+ return g_string_free (result, FALSE);
+}
+
+static void
+assert_text_decoration (StThemeNode *node,
+ const char *node_description,
+ StTextDecoration expected)
+{
+ StTextDecoration value = st_theme_node_get_text_decoration (node);
+ if (expected != value)
+ {
+ char *es = text_decoration_to_string (expected);
+ char *vs = text_decoration_to_string (value);
+
+ g_print ("%s: %s.text-decoration: expected: %s, got: %s\n",
+ test, node_description, es, vs);
+ fail = TRUE;
+
+ g_free (es);
+ g_free (vs);
+ }
+}
+
+static void
+assert_foreground_color (StThemeNode *node,
+ const char *node_description,
+ guint32 expected)
+{
+ ClutterColor color;
+ guint32 value;
+
+ st_theme_node_get_foreground_color (node, &color);
+ value = clutter_color_to_pixel (&color);
+
+ if (expected != value)
+ {
+ g_print ("%s: %s.color: expected: #%08x, got: #%08x\n",
+ test, node_description, expected, value);
+ fail = TRUE;
+ }
+}
+
+static void
+assert_background_color (StThemeNode *node,
+ const char *node_description,
+ guint32 expected)
+{
+ ClutterColor color;
+ guint32 value;
+
+ st_theme_node_get_background_color (node, &color);
+ value = clutter_color_to_pixel (&color);
+
+ if (expected != value)
+ {
+ g_print ("%s: %s.background-color: expected: #%08x, got: #%08x\n",
+ test, node_description, expected, value);
+ fail = TRUE;
+ }
+}
+
+static const char *
+side_to_string (StSide side)
+{
+ switch (side)
+ {
+ case ST_SIDE_TOP:
+ return "top";
+ case ST_SIDE_RIGHT:
+ return "right";
+ case ST_SIDE_BOTTOM:
+ return "bottom";
+ case ST_SIDE_LEFT:
+ return "left";
+ default:
+ return "<unknown>";
+ }
+}
+
+static void
+assert_border_color (StThemeNode *node,
+ const char *node_description,
+ StSide side,
+ guint32 expected)
+{
+ ClutterColor color;
+ guint32 value;
+
+ st_theme_node_get_border_color (node, side, &color);
+ value = clutter_color_to_pixel (&color);
+
+ if (expected != value)
+ {
+ g_print ("%s: %s.border-%s-color: expected: #%08x, got: #%08x\n",
+ test, node_description, side_to_string (side), expected, value);
+ fail = TRUE;
+ }
+}
+
+static void
+assert_background_image (StThemeNode *node,
+ const char *node_description,
+ const char *expected)
+{
+ GFile *value = st_theme_node_get_background_image (node);
+ GFile *expected_file;
+
+ if (expected != NULL && value != NULL)
+ {
+ expected_file = g_file_new_for_path (expected);
+
+ if (!g_file_equal (expected_file, value))
+ {
+ char *uri = g_file_get_uri (expected_file);
+ g_print ("%s: %s.background-image: expected: %s, got: %s\n",
+ test, node_description, expected, uri);
+ fail = TRUE;
+ g_free (uri);
+ }
+ }
+}
+
+#define LENGTH_EPSILON 0.001
+
+static void
+assert_length (const char *node_description,
+ const char *property_description,
+ double expected,
+ double value)
+{
+ if (fabs (expected - value) > LENGTH_EPSILON)
+ {
+ g_print ("%s %s.%s: expected: %3f, got: %3f\n",
+ test, node_description, property_description, expected, value);
+ fail = TRUE;
+ }
+}
+
+static void
+test_defaults (void)
+{
+ test = "defaults";
+ /* font comes from context */
+ assert_font (root, "stage", "sans-serif 12");
+ /* black is the default foreground color */
+ assert_foreground_color (root, "stage", 0x00000ff);
+}
+
+static void
+test_lengths (void)
+{
+ test = "lengths";
+ /* 12pt == 16px at 96dpi */
+ assert_length ("group1", "padding-top", 16.,
+ st_theme_node_get_padding (group1, ST_SIDE_TOP));
+ /* 12px == 12px */
+ assert_length ("group1", "padding-right", 12.,
+ st_theme_node_get_padding (group1, ST_SIDE_RIGHT));
+ /* 2em == 32px (with a 12pt font) */
+ assert_length ("group1", "padding-bottom", 32.,
+ st_theme_node_get_padding (group1, ST_SIDE_BOTTOM));
+ /* 1in == 72pt == 96px, at 96dpi */
+ assert_length ("group1", "padding-left", 96.,
+ st_theme_node_get_padding (group1, ST_SIDE_LEFT));
+
+ /* 12pt == 16px at 96dpi */
+ assert_length ("group1", "margin-top", 16.,
+ st_theme_node_get_margin (group1, ST_SIDE_TOP));
+ /* 12px == 12px */
+ assert_length ("group1", "margin-right", 12.,
+ st_theme_node_get_margin (group1, ST_SIDE_RIGHT));
+ /* 2em == 32px (with a 12pt font) */
+ assert_length ("group1", "margin-bottom", 32.,
+ st_theme_node_get_margin (group1, ST_SIDE_BOTTOM));
+ /* 1in == 72pt == 96px, at 96dpi */
+ assert_length ("group1", "margin-left", 96.,
+ st_theme_node_get_margin (group1, ST_SIDE_LEFT));
+}
+
+static void
+test_classes (void)
+{
+ test = "classes";
+ /* .special-text class overrides size and style;
+ * the StBin.special-text selector doesn't match */
+ assert_font (text1, "text1", "sans-serif Italic 32px");
+}
+
+static void
+test_type_inheritance (void)
+{
+ test = "type_inheritance";
+ /* From StBin element selector */
+ assert_length ("button", "padding-top", 10.,
+ st_theme_node_get_padding (button, ST_SIDE_TOP));
+ /* From StButton element selector */
+ assert_length ("button", "padding-right", 20.,
+ st_theme_node_get_padding (button, ST_SIDE_RIGHT));
+}
+
+static void
+test_adjacent_selector (void)
+{
+ test = "adjacent_selector";
+ /* #group1 > #text1 matches text1 */
+ assert_foreground_color (text1, "text1", 0x00ff00ff);
+ /* stage > #text2 doesn't match text2 */
+ assert_foreground_color (text2, "text2", 0x000000ff);
+}
+
+static void
+test_padding (void)
+{
+ test = "padding";
+ /* Test that a 4-sided padding property assigns the right paddings to
+ * all sides */
+ assert_length ("group2", "padding-top", 1.,
+ st_theme_node_get_padding (group2, ST_SIDE_TOP));
+ assert_length ("group2", "padding-right", 2.,
+ st_theme_node_get_padding (group2, ST_SIDE_RIGHT));
+ assert_length ("group2", "padding-bottom", 3.,
+ st_theme_node_get_padding (group2, ST_SIDE_BOTTOM));
+ assert_length ("group2", "padding-left", 4.,
+ st_theme_node_get_padding (group2, ST_SIDE_LEFT));
+}
+
+static void
+test_margin (void)
+{
+ test = "margin";
+ /* Test that a 4-sided margin property assigns the right margin to
+ * all sides */
+ assert_length ("group2", "margin-top", 1.,
+ st_theme_node_get_margin (group2, ST_SIDE_TOP));
+ assert_length ("group2", "margin-right", 2.,
+ st_theme_node_get_margin (group2, ST_SIDE_RIGHT));
+ assert_length ("group2", "margin-bottom", 3.,
+ st_theme_node_get_margin (group2, ST_SIDE_BOTTOM));
+ assert_length ("group2", "margin-left", 4.,
+ st_theme_node_get_margin (group2, ST_SIDE_LEFT));
+
+ /* Test that a 3-sided margin property assigns the right margin to
+ * all sides */
+ assert_length ("group4", "margin-top", 1.,
+ st_theme_node_get_margin (group4, ST_SIDE_TOP));
+ assert_length ("group4", "margin-right", 2.,
+ st_theme_node_get_margin (group4, ST_SIDE_RIGHT));
+ assert_length ("group4", "margin-bottom", 3.,
+ st_theme_node_get_margin (group4, ST_SIDE_BOTTOM));
+ assert_length ("group4", "margin-left", 2.,
+ st_theme_node_get_margin (group4, ST_SIDE_LEFT));
+
+ /* Test that a 2-sided margin property assigns the right margin to
+ * all sides */
+ assert_length ("group5", "margin-top", 1.,
+ st_theme_node_get_margin (group5, ST_SIDE_TOP));
+ assert_length ("group5", "margin-right", 2.,
+ st_theme_node_get_margin (group5, ST_SIDE_RIGHT));
+ assert_length ("group5", "margin-bottom", 1.,
+ st_theme_node_get_margin (group5, ST_SIDE_BOTTOM));
+ assert_length ("group5", "margin-left", 2.,
+ st_theme_node_get_margin (group5, ST_SIDE_LEFT));
+
+ /* Test that all sides have a margin of 0 when not specified */
+ assert_length ("group6", "margin-top", 0.,
+ st_theme_node_get_margin (group6, ST_SIDE_TOP));
+ assert_length ("group6", "margin-right", 0.,
+ st_theme_node_get_margin (group6, ST_SIDE_RIGHT));
+ assert_length ("group6", "margin-bottom", 0.,
+ st_theme_node_get_margin (group6, ST_SIDE_BOTTOM));
+ assert_length ("group6", "margin-left", 0.,
+ st_theme_node_get_margin (group6, ST_SIDE_LEFT));
+}
+
+static void
+test_border (void)
+{
+ test = "border";
+
+ /* group2 is defined as having a thin black border along the top three
+ * sides with rounded joins, then a square-joined green border at the
+ * bottom
+ */
+
+ assert_length ("group2", "border-top-width", 2.,
+ st_theme_node_get_border_width (group2, ST_SIDE_TOP));
+ assert_length ("group2", "border-right-width", 2.,
+ st_theme_node_get_border_width (group2, ST_SIDE_RIGHT));
+ assert_length ("group2", "border-bottom-width", 5.,
+ st_theme_node_get_border_width (group2, ST_SIDE_BOTTOM));
+ assert_length ("group2", "border-left-width", 2.,
+ st_theme_node_get_border_width (group2, ST_SIDE_LEFT));
+
+ assert_border_color (group2, "group2", ST_SIDE_TOP, 0x000000ff);
+ assert_border_color (group2, "group2", ST_SIDE_RIGHT, 0x000000ff);
+ assert_border_color (group2, "group2", ST_SIDE_BOTTOM, 0x0000ffff);
+ assert_border_color (group2, "group2", ST_SIDE_LEFT, 0x000000ff);
+
+ assert_length ("group2", "border-radius-topleft", 10.,
+ st_theme_node_get_border_radius (group2, ST_CORNER_TOPLEFT));
+ assert_length ("group2", "border-radius-topright", 10.,
+ st_theme_node_get_border_radius (group2, ST_CORNER_TOPRIGHT));
+ assert_length ("group2", "border-radius-bottomright", 0.,
+ st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMRIGHT));
+ assert_length ("group2", "border-radius-bottomleft", 0.,
+ st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMLEFT));
+}
+
+static void
+test_background (void)
+{
+ test = "background";
+ /* group1 has a background: shortcut property setting color and image */
+ assert_background_color (group1, "group1", 0xff0000ff);
+ assert_background_image (group1, "group1", "some-background.png");
+ /* text1 inherits the background image but not the color */
+ assert_background_color (text1, "text1", 0x00000000);
+ assert_background_image (text1, "text1", "some-background.png");
+ /* text2 inherits both, but then background: none overrides both */
+ assert_background_color (text2, "text2", 0x00000000);
+ assert_background_image (text2, "text2", NULL);
+ /* background-image property */
+ assert_background_image (group2, "group2", "other-background.png");
+}
+
+static void
+test_font (void)
+{
+ test = "font";
+ /* font specified with font: */
+ assert_font (group2, "group2", "serif Italic 12px");
+ /* text3 inherits and overrides individually properties */
+ assert_font (text3, "text3", "serif Bold Oblique Small-Caps 24px");
+}
+
+static void
+test_font_features (void)
+{
+ test = "font_features";
+ /* group1 has font-feature-settings: "tnum" */
+ assert_font_features (group1, "group1", "\"tnum\"");
+ /* text2 should inherit from group1 */
+ assert_font_features (text2, "text2", "\"tnum\"");
+ /* group2 has font-feature-settings: "tnum", "zero" */
+ assert_font_features (group2, "group2", "\"tnum\", \"zero\"");
+ /* text3 should inherit from group2 using the inherit keyword */
+ assert_font_features (text3, "text3", "\"tnum\", \"zero\"");
+ /* text4 has font-feature-settings: normal */
+ assert_font_features (text4, "text4", NULL);
+}
+
+static void
+test_pseudo_class (void)
+{
+ StWidget *label;
+ StThemeNode *labelNode;
+
+ test = "pseudo_class";
+ /* text4 has :visited and :hover pseudo-classes, so should pick up both of these */
+ assert_foreground_color (text4, "text4", 0x888888ff);
+ assert_text_decoration (text4, "text4", ST_TEXT_DECORATION_UNDERLINE);
+ /* :hover pseudo-class matches, but class doesn't match */
+ assert_text_decoration (group3, "group3", 0);
+
+ /* Test the StWidget add/remove pseudo_class interfaces */
+ label = st_label_new ("foo");
+ clutter_actor_add_child (stage, CLUTTER_ACTOR (label));
+
+ labelNode = st_widget_get_theme_node (label);
+ assert_foreground_color (labelNode, "label", 0x000000ff);
+ assert_text_decoration (labelNode, "label", 0);
+ assert_length ("label", "border-width", 0.,
+ st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
+
+ st_widget_add_style_pseudo_class (label, "visited");
+ g_assert (st_widget_has_style_pseudo_class (label, "visited"));
+ labelNode = st_widget_get_theme_node (label);
+ assert_foreground_color (labelNode, "label", 0x888888ff);
+ assert_text_decoration (labelNode, "label", 0);
+ assert_length ("label", "border-width", 0.,
+ st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
+
+ st_widget_add_style_pseudo_class (label, "hover");
+ g_assert (st_widget_has_style_pseudo_class (label, "hover"));
+ labelNode = st_widget_get_theme_node (label);
+ assert_foreground_color (labelNode, "label", 0x888888ff);
+ assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
+ assert_length ("label", "border-width", 0.,
+ st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
+
+ st_widget_remove_style_pseudo_class (label, "visited");
+ g_assert (!st_widget_has_style_pseudo_class (label, "visited"));
+ g_assert (st_widget_has_style_pseudo_class (label, "hover"));
+ labelNode = st_widget_get_theme_node (label);
+ assert_foreground_color (labelNode, "label", 0x000000ff);
+ assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
+ assert_length ("label", "border-width", 0.,
+ st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
+
+ st_widget_add_style_pseudo_class (label, "boxed");
+ labelNode = st_widget_get_theme_node (label);
+ assert_foreground_color (labelNode, "label", 0x000000ff);
+ assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
+ assert_length ("label", "border-width", 1.,
+ st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
+
+ st_widget_remove_style_pseudo_class (label, "hover");
+ labelNode = st_widget_get_theme_node (label);
+ assert_foreground_color (labelNode, "label", 0x000000ff);
+ assert_text_decoration (labelNode, "label", 0);
+ assert_length ("label", "border-width", 1.,
+ st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
+
+ st_widget_remove_style_pseudo_class (label, "boxed");
+ g_assert (!st_widget_has_style_pseudo_class (label, "boxed"));
+ g_assert (st_widget_has_style_pseudo_class (label, "insensitive"));
+ labelNode = st_widget_get_theme_node (label);
+ assert_foreground_color (labelNode, "label", 0x000000ff);
+ assert_text_decoration (labelNode, "label", 0);
+ assert_length ("label", "border-width", 0.,
+ st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (label), TRUE);
+ g_assert (st_widget_get_style_pseudo_class (label) == NULL);
+}
+
+static void
+test_inline_style (void)
+{
+ test = "inline_style";
+ /* These properties come from the inline-style specified when creating the node */
+ assert_foreground_color (text3, "text3", 0x00000ffff);
+ assert_length ("text3", "padding-bottom", 12.,
+ st_theme_node_get_padding (text3, ST_SIDE_BOTTOM));
+}
+
+int
+main (int argc, char **argv)
+{
+ MetaContext *context;
+ g_autoptr (GError) error = NULL;
+ MetaBackend *backend;
+ StTheme *theme;
+ StThemeContext *theme_context;
+ PangoFontDescription *font_desc;
+ GFile *file;
+ g_autofree char *cwd = NULL;
+
+ gtk_init (&argc, &argv);
+
+ /* meta_init() cds to $HOME */
+ cwd = g_get_current_dir ();
+
+ context = meta_create_test_context (META_CONTEXT_TEST_TYPE_NESTED,
+ META_CONTEXT_TEST_FLAG_NONE);
+ if (!meta_context_configure (context, &argc, &argv, &error))
+ g_error ("Failed to configure: %s", error->message);
+
+ if (!meta_context_setup (context, &error))
+ g_error ("Failed to setup: %s", error->message);
+
+ if (chdir (cwd) < 0)
+ g_error ("chdir('%s') failed: %s", cwd, g_strerror (errno));
+
+ /* Make sure our assumptions about resolution are correct */
+ g_object_set (clutter_settings_get_default (), "font-dpi", -1, NULL);
+
+ file = g_file_new_for_path ("test-theme.css");
+ theme = st_theme_new (file, NULL, NULL);
+ g_object_unref (file);
+
+ backend = meta_get_backend ();
+ stage = meta_backend_get_stage (backend);
+ theme_context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage));
+ st_theme_context_set_theme (theme_context, theme);
+
+ font_desc = pango_font_description_from_string ("sans-serif 12");
+ st_theme_context_set_font (theme_context, font_desc);
+ pango_font_description_free (font_desc);
+
+ root = st_theme_context_get_root_node (theme_context);
+ group1 = st_theme_node_new (theme_context, root, NULL,
+ CLUTTER_TYPE_ACTOR, "group1", NULL, NULL, NULL);
+ text1 = st_theme_node_new (theme_context, group1, NULL,
+ CLUTTER_TYPE_TEXT, "text1", "special-text", NULL, NULL);
+ text2 = st_theme_node_new (theme_context, group1, NULL,
+ CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
+ group2 = st_theme_node_new (theme_context, root, NULL,
+ CLUTTER_TYPE_ACTOR, "group2", NULL, NULL, NULL);
+ group4 = st_theme_node_new (theme_context, root, NULL,
+ CLUTTER_TYPE_ACTOR, "group4", NULL, NULL, NULL);
+ group5 = st_theme_node_new (theme_context, root, NULL,
+ CLUTTER_TYPE_ACTOR, "group5", NULL, NULL, NULL);
+ group6 = st_theme_node_new (theme_context, root, NULL,
+ CLUTTER_TYPE_ACTOR, "group6", NULL, NULL, NULL);
+ text3 = st_theme_node_new (theme_context, group2, NULL,
+ CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
+ "color: #0000ff; padding-bottom: 12px;");
+ text4 = st_theme_node_new (theme_context, group2, NULL,
+ CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover", NULL);
+ group3 = st_theme_node_new (theme_context, group2, NULL,
+ CLUTTER_TYPE_ACTOR, "group3", NULL, "hover", NULL);
+ button = st_theme_node_new (theme_context, root, NULL,
+ ST_TYPE_BUTTON, "button", NULL, NULL, NULL);
+
+ test_defaults ();
+ test_lengths ();
+ test_classes ();
+ test_type_inheritance ();
+ test_adjacent_selector ();
+ test_padding ();
+ test_margin ();
+ test_border ();
+ test_background ();
+ test_font ();
+ test_font_features ();
+ test_pseudo_class ();
+ test_inline_style ();
+
+ g_object_unref (button);
+ g_object_unref (group1);
+ g_object_unref (group2);
+ g_object_unref (group3);
+ g_object_unref (group4);
+ g_object_unref (group5);
+ g_object_unref (group6);
+ g_object_unref (text1);
+ g_object_unref (text2);
+ g_object_unref (text3);
+ g_object_unref (text4);
+ g_object_unref (theme);
+
+ g_object_unref (context);
+
+ return fail ? 1 : 0;
+}
diff --git a/src/st/test-theme.css b/src/st/test-theme.css
new file mode 100644
index 0000000..d180255
--- /dev/null
+++ b/src/st/test-theme.css
@@ -0,0 +1,107 @@
+stage {
+}
+
+#group1 {
+ padding: 12pt;
+ padding-right: 12px;
+ padding-bottom: 2em;
+ padding-left: 1in;
+
+ margin: 12pt;
+ margin-right: 12px;
+ margin-bottom: 2em;
+ margin-left: 1in;
+
+ background: #ff0000 url('some-background.png');
+
+ font-feature-settings: "tnum";
+}
+
+#text1 {
+ background-image: inherit;
+}
+
+.special-text {
+ font-size: 24pt;
+ font-style: italic;
+}
+
+StBin.special-text {
+ font-weight: bold;
+}
+
+#text2 {
+ background: inherit;
+ background: none; /* also overrides the color */
+}
+
+#group2 {
+ font: italic 12px serif;
+ font-feature-settings: "tnum", "zero";
+}
+
+#text3 {
+ font-variant: small-caps;
+ font-weight: bold;
+ font-style: oblique;
+ font-size: 200%;
+ font-feature-settings: "pnum";
+}
+
+#text4 {
+ font-feature-settings: normal;
+}
+
+StBin {
+ padding: 10px;
+}
+
+StButton {
+ padding-right: 20px;
+}
+
+#group1 > #text1 {
+ color: #00ff00;
+}
+
+stage > #text2 {
+ color: #ff0000;
+}
+
+#group2 > #text3 {
+ font-feature-settings: inherit;
+}
+
+#group2 {
+ background-image: url('other-background.png');
+ padding: 1px 2px 3px 4px;
+ margin: 1px 2px 3px 4px;
+
+ border: 2px solid #000000;
+ border-bottom: 5px solid #0000ff;
+ border-radius: 10px 10px 0px 0px;
+}
+
+ClutterText:hover, StLabel:hover {
+ text-decoration: underline;
+}
+
+ClutterText:visited, StLabel:visited {
+ color: #888888;
+}
+
+StLabel:boxed {
+ border: 1px;
+}
+
+#group4 {
+ margin: 1px 2px 3px;
+}
+
+#group5 {
+ margin: 1px 2px;
+}
+
+#group6 {
+ padding: 5px;
+}
diff --git a/src/tray/meson.build b/src/tray/meson.build
new file mode 100644
index 0000000..139e3b2
--- /dev/null
+++ b/src/tray/meson.build
@@ -0,0 +1,12 @@
+tray_sources = [
+ 'na-tray-child.c',
+ 'na-tray-child.h',
+ 'na-tray-manager.c',
+ 'na-tray-manager.h'
+]
+
+libtray = static_library('tray', tray_sources,
+ c_args: ['-DG_LOG_DOMAIN="notification_area"'],
+ dependencies: [clutter_dep, gtk_dep],
+ include_directories: conf_inc
+)
diff --git a/src/tray/na-tray-child.c b/src/tray/na-tray-child.c
new file mode 100644
index 0000000..175ac8a
--- /dev/null
+++ b/src/tray/na-tray-child.c
@@ -0,0 +1,504 @@
+/* na-tray-child.c
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ * Copyright (C) 2003-2006 Vincent Untz
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "na-tray-child.h"
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+G_DEFINE_TYPE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET)
+
+static void
+na_tray_child_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object);
+}
+
+static void
+na_tray_child_realize (GtkWidget *widget)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (widget);
+ GdkVisual *visual = gtk_widget_get_visual (widget);
+ GdkWindow *window;
+
+ GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget);
+
+ window = gtk_widget_get_window (widget);
+
+ if (child->has_alpha)
+ {
+ /* We have real transparency with an ARGB visual and the Composite
+ * extension. */
+
+ /* Set a transparent background */
+ cairo_pattern_t *transparent = cairo_pattern_create_rgba (0, 0, 0, 0);
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gdk_window_set_background_pattern (window, transparent);
+G_GNUC_END_IGNORE_DEPRECATIONS
+ cairo_pattern_destroy (transparent);
+
+ child->parent_relative_bg = FALSE;
+ }
+ else if (visual == gdk_window_get_visual (gdk_window_get_parent (window)))
+ {
+ /* Otherwise, if the visual matches the visual of the parent window, we
+ * can use a parent-relative background and fake transparency. */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gdk_window_set_background_pattern (window, NULL);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+ child->parent_relative_bg = TRUE;
+ }
+ else
+ {
+ /* Nothing to do; the icon will sit on top of an ugly gray box */
+ child->parent_relative_bg = FALSE;
+ }
+
+ gtk_widget_set_app_paintable (GTK_WIDGET (child),
+ child->parent_relative_bg || child->has_alpha);
+
+ /* Double-buffering will interfere with the parent-relative-background fake
+ * transparency, since the double-buffer code doesn't know how to fill in the
+ * background of the double-buffer correctly.
+ * The function is deprecated because it is only meaningful on X11 - the
+ * same is true for XEmbed of course, so just ignore the warning.
+ */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gtk_widget_set_double_buffered (GTK_WIDGET (child),
+ child->parent_relative_bg);
+G_GNUC_END_IGNORE_DEPRECATIONS
+}
+
+static void
+na_tray_child_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ /* The default handler resets the background according to the new style.
+ * We either use a transparent background or a parent-relative background
+ * and ignore the style background. So, just don't chain up.
+ */
+}
+
+#if 0
+/* This is adapted from code that was commented out in na-tray-manager.c; the
+ * code in na-tray-manager.c wouldn't have worked reliably, this will. So maybe
+ * it can be re-enabled. On other hand, things seem to be working fine without
+ * it.
+ *
+ * If reenabling, you need to hook it up in na_tray_child_class_init().
+ */
+static void
+na_tray_child_size_request (GtkWidget *widget,
+ GtkRequisition *request)
+{
+ GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_request (widget, request);
+
+ /*
+ * Make sure the icons have a meaningful size ..
+ */
+ if ((request->width < 16) || (request->height < 16))
+ {
+ gint nw = MAX (24, request->width);
+ gint nh = MAX (24, request->height);
+ g_warning ("Tray icon has requested a size of (%ix%i), resizing to (%ix%i)",
+ req.width, req.height, nw, nh);
+ request->width = nw;
+ request->height = nh;
+ }
+}
+#endif
+
+static void
+na_tray_child_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (widget);
+ GtkAllocation widget_allocation;
+ gboolean moved, resized;
+
+ gtk_widget_get_allocation (widget, &widget_allocation);
+
+ moved = (allocation->x != widget_allocation.x ||
+ allocation->y != widget_allocation.y);
+ resized = (allocation->width != widget_allocation.width ||
+ allocation->height != widget_allocation.height);
+
+ /* When we are allocating the widget while mapped we need special handling
+ * for both real and fake transparency.
+ *
+ * Real transparency: we need to invalidate and trigger a redraw of the old
+ * and new areas. (GDK really should handle this for us, but doesn't as of
+ * GTK+-2.14)
+ *
+ * Fake transparency: if the widget moved, we need to force the contents to
+ * be redrawn with the new offset for the parent-relative background.
+ */
+ if ((moved || resized) && gtk_widget_get_mapped (widget))
+ {
+ if (na_tray_child_has_alpha (child))
+ gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
+ &widget_allocation, FALSE);
+ }
+
+ GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget,
+ allocation);
+
+ if ((moved || resized) && gtk_widget_get_mapped (widget))
+ {
+ if (na_tray_child_has_alpha (NA_TRAY_CHILD (widget)))
+ gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
+ &widget_allocation, FALSE);
+ else if (moved && child->parent_relative_bg)
+ na_tray_child_force_redraw (child);
+ }
+}
+
+/* The plug window should completely occupy the area of the child, so we won't
+ * get a draw event. But in case we do (the plug unmaps itself, say), this
+ * draw handler draws with real or fake transparency.
+ */
+static gboolean
+na_tray_child_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (widget);
+
+ if (na_tray_child_has_alpha (child))
+ {
+ /* Clear to transparent */
+ cairo_set_source_rgba (cr, 0, 0, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ }
+ else if (child->parent_relative_bg)
+ {
+ GdkWindow *window;
+ cairo_surface_t *target;
+ GdkRectangle clip_rect;
+
+ window = gtk_widget_get_window (widget);
+ target = cairo_get_group_target (cr);
+
+ gdk_cairo_get_clip_rectangle (cr, &clip_rect);
+
+ /* Clear to parent-relative pixmap
+ * We need to use direct X access here because GDK doesn't know about
+ * the parent relative pixmap. */
+ cairo_surface_flush (target);
+
+ XClearArea (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ clip_rect.x, clip_rect.y,
+ clip_rect.width, clip_rect.height,
+ False);
+ cairo_surface_mark_dirty_rectangle (target,
+ clip_rect.x, clip_rect.y,
+ clip_rect.width, clip_rect.height);
+ }
+
+ return FALSE;
+}
+
+static void
+na_tray_child_init (NaTrayChild *child)
+{
+}
+
+static void
+na_tray_child_class_init (NaTrayChildClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = (GObjectClass *)klass;
+ widget_class = (GtkWidgetClass *)klass;
+
+ gobject_class->finalize = na_tray_child_finalize;
+ widget_class->style_set = na_tray_child_style_set;
+ widget_class->realize = na_tray_child_realize;
+ widget_class->size_allocate = na_tray_child_size_allocate;
+ widget_class->draw = na_tray_child_draw;
+}
+
+GtkWidget *
+na_tray_child_new (GdkScreen *screen,
+ Window icon_window)
+{
+ XWindowAttributes window_attributes;
+ GdkDisplay *display;
+ Display *xdisplay;
+ NaTrayChild *child;
+ GdkVisual *visual;
+ gboolean visual_has_alpha;
+ int red_prec, green_prec, blue_prec, depth;
+ int result;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ g_return_val_if_fail (icon_window != None, NULL);
+
+ xdisplay = GDK_SCREEN_XDISPLAY (screen);
+ display = gdk_x11_lookup_xdisplay (xdisplay);
+
+ /* We need to determine the visual of the window we are embedding and create
+ * the socket in the same visual.
+ */
+
+ gdk_x11_display_error_trap_push (display);
+ result = XGetWindowAttributes (xdisplay, icon_window,
+ &window_attributes);
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ if (!result) /* Window already gone */
+ return NULL;
+
+ visual = gdk_x11_screen_lookup_visual (screen,
+ window_attributes.visual->visualid);
+ if (!visual) /* Icon window is on another screen? */
+ return NULL;
+
+ child = g_object_new (NA_TYPE_TRAY_CHILD, NULL);
+ child->icon_window = icon_window;
+
+ gtk_widget_set_visual (GTK_WIDGET (child), visual);
+
+ /* We have alpha if the visual has something other than red, green,
+ * and blue */
+ gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec);
+ gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec);
+ gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec);
+ depth = gdk_visual_get_depth (visual);
+
+ visual_has_alpha = red_prec + blue_prec + green_prec < depth;
+ child->has_alpha = visual_has_alpha;
+
+ return GTK_WIDGET (child);
+}
+
+char *
+na_tray_child_get_title (NaTrayChild *child)
+{
+ char *retval = NULL;
+ GdkDisplay *display;
+ Atom utf8_string, atom, type;
+ int result;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ gchar *val;
+
+ g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL);
+
+ display = gtk_widget_get_display (GTK_WIDGET (child));
+
+ utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
+
+ gdk_x11_display_error_trap_push (display);
+
+ result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ child->icon_window,
+ atom,
+ 0, G_MAXLONG,
+ False, utf8_string,
+ &type, &format, &nitems,
+ &bytes_after, (guchar **)&val);
+
+ if (gdk_x11_display_error_trap_pop (display) || result != Success)
+ return NULL;
+
+ if (type != utf8_string ||
+ format != 8 ||
+ nitems == 0)
+ {
+ if (val)
+ XFree (val);
+ return NULL;
+ }
+
+ if (!g_utf8_validate (val, nitems, NULL))
+ {
+ XFree (val);
+ return NULL;
+ }
+
+ retval = g_strndup (val, nitems);
+
+ XFree (val);
+
+ return retval;
+}
+
+/**
+ * na_tray_child_has_alpha;
+ * @child: a #NaTrayChild
+ *
+ * Checks if the child has an ARGB visual and real alpha transparence.
+ * (as opposed to faked alpha transparency with an parent-relative
+ * background)
+ *
+ * Return value: %TRUE if the child has an alpha transparency
+ */
+gboolean
+na_tray_child_has_alpha (NaTrayChild *child)
+{
+ g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE);
+
+ return child->has_alpha;
+}
+
+/* If we are faking transparency with a window-relative background, force a
+ * redraw of the icon. This should be called if the background changes or if
+ * the child is shifted with respect to the background.
+ */
+void
+na_tray_child_force_redraw (NaTrayChild *child)
+{
+ GtkWidget *widget = GTK_WIDGET (child);
+
+ if (gtk_widget_get_mapped (widget) && child->parent_relative_bg)
+ {
+#if 1
+ /* Sending an ExposeEvent might cause redraw problems if the
+ * icon is expecting the server to clear-to-background before
+ * the redraw. It should be ok for GtkStatusIcon or EggTrayIcon.
+ */
+ GdkDisplay *display = gtk_widget_get_display (widget);
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ XEvent xev;
+ GdkWindow *plug_window;
+ GtkAllocation allocation;
+
+ plug_window = gtk_socket_get_plug_window (GTK_SOCKET (child));
+ gtk_widget_get_allocation (widget, &allocation);
+
+ xev.xexpose.type = Expose;
+ xev.xexpose.window = GDK_WINDOW_XID (plug_window);
+ xev.xexpose.x = 0;
+ xev.xexpose.y = 0;
+ xev.xexpose.width = allocation.width;
+ xev.xexpose.height = allocation.height;
+ xev.xexpose.count = 0;
+
+ gdk_x11_display_error_trap_push (display);
+ XSendEvent (xdisplay,
+ xev.xexpose.window,
+ False, ExposureMask,
+ &xev);
+ gdk_x11_display_error_trap_pop_ignored (display);
+#else
+ /* Hiding and showing is the safe way to do it, but can result in more
+ * flickering.
+ */
+ gdk_window_hide (widget->window);
+ gdk_window_show (widget->window);
+#endif
+ }
+}
+
+/* from libwnck/xutils.c, comes as LGPLv2+ */
+static char *
+latin1_to_utf8 (const char *latin1)
+{
+ GString *str;
+ const char *p;
+
+ str = g_string_new (NULL);
+
+ p = latin1;
+ while (*p)
+ {
+ g_string_append_unichar (str, (gunichar) *p);
+ ++p;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+/* derived from libwnck/xutils.c, comes as LGPLv2+ */
+static void
+_get_wmclass (Display *xdisplay,
+ Window xwindow,
+ char **res_class,
+ char **res_name)
+{
+ GdkDisplay *display;
+ XClassHint ch;
+
+ ch.res_name = NULL;
+ ch.res_class = NULL;
+
+ display = gdk_x11_lookup_xdisplay (xdisplay);
+ gdk_x11_display_error_trap_push (display);
+ XGetClassHint (xdisplay, xwindow, &ch);
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ if (res_class)
+ *res_class = NULL;
+
+ if (res_name)
+ *res_name = NULL;
+
+ if (ch.res_name)
+ {
+ if (res_name)
+ *res_name = latin1_to_utf8 (ch.res_name);
+
+ XFree (ch.res_name);
+ }
+
+ if (ch.res_class)
+ {
+ if (res_class)
+ *res_class = latin1_to_utf8 (ch.res_class);
+
+ XFree (ch.res_class);
+ }
+}
+
+/**
+ * na_tray_child_get_wm_class;
+ * @child: a #NaTrayChild
+ * @res_name: return location for a string containing the application name of
+ * @child, or %NULL
+ * @res_class: return location for a string containing the application class of
+ * @child, or %NULL
+ *
+ * Fetches the resource associated with @child.
+ */
+void
+na_tray_child_get_wm_class (NaTrayChild *child,
+ char **res_name,
+ char **res_class)
+{
+ GdkDisplay *display;
+
+ g_return_if_fail (NA_IS_TRAY_CHILD (child));
+
+ display = gtk_widget_get_display (GTK_WIDGET (child));
+
+ _get_wmclass (GDK_DISPLAY_XDISPLAY (display),
+ child->icon_window,
+ res_class,
+ res_name);
+}
diff --git a/src/tray/na-tray-child.h b/src/tray/na-tray-child.h
new file mode 100644
index 0000000..d143676
--- /dev/null
+++ b/src/tray/na-tray-child.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* na-tray-child.h
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ * Copyright (C) 2003-2006 Vincent Untz
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NA_TRAY_CHILD_H__
+#define __NA_TRAY_CHILD_H__
+
+#include <gtk/gtk.h>
+#include <gtk/gtkx.h>
+
+G_BEGIN_DECLS
+
+#define NA_TYPE_TRAY_CHILD (na_tray_child_get_type ())
+#define NA_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_CHILD, NaTrayChild))
+#define NA_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_CHILD, NaTrayChildClass))
+#define NA_IS_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_CHILD))
+#define NA_IS_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_CHILD))
+#define NA_TRAY_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_CHILD, NaTrayChildClass))
+
+typedef struct _NaTrayChild NaTrayChild;
+typedef struct _NaTrayChildClass NaTrayChildClass;
+typedef struct _NaTrayChildChild NaTrayChildChild;
+
+struct _NaTrayChild
+{
+ GtkSocket parent_instance;
+ Window icon_window;
+ guint has_alpha : 1;
+ guint parent_relative_bg : 1;
+};
+
+struct _NaTrayChildClass
+{
+ GtkSocketClass parent_class;
+};
+
+GType na_tray_child_get_type (void);
+
+GtkWidget *na_tray_child_new (GdkScreen *screen,
+ Window icon_window);
+char *na_tray_child_get_title (NaTrayChild *child);
+gboolean na_tray_child_has_alpha (NaTrayChild *child);
+void na_tray_child_force_redraw (NaTrayChild *child);
+void na_tray_child_get_wm_class (NaTrayChild *child,
+ char **res_name,
+ char **res_class);
+
+G_END_DECLS
+
+#endif /* __NA_TRAY_CHILD_H__ */
diff --git a/src/tray/na-tray-manager.c b/src/tray/na-tray-manager.c
new file mode 100644
index 0000000..15e1d80
--- /dev/null
+++ b/src/tray/na-tray-manager.c
@@ -0,0 +1,888 @@
+/* na-tray-manager.c
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ * Copyright (C) 2003-2006 Vincent Untz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Used to be: eggtraymanager.c
+ */
+
+#include <config.h>
+#include <string.h>
+#include <libintl.h>
+
+#include "na-tray-manager.h"
+
+#if defined (GDK_WINDOWING_X11)
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+#elif defined (GDK_WINDOWING_WIN32)
+#include <gdk/gdkwin32.h>
+#endif
+#include <gtk/gtk.h>
+
+/* Signals */
+enum
+{
+ TRAY_ICON_ADDED,
+ TRAY_ICON_REMOVED,
+ MESSAGE_SENT,
+ MESSAGE_CANCELLED,
+ LOST_SELECTION,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_ORIENTATION
+};
+
+typedef struct
+{
+ long id, len;
+ long remaining_len;
+
+ long timeout;
+ char *str;
+#ifdef GDK_WINDOWING_X11
+ Window window;
+#endif
+} PendingMessage;
+
+static guint manager_signals[LAST_SIGNAL];
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+#define SYSTEM_TRAY_ORIENTATION_HORZ 0
+#define SYSTEM_TRAY_ORIENTATION_VERT 1
+
+#ifdef GDK_WINDOWING_X11
+static gboolean na_tray_manager_check_running_screen_x11 (void);
+#endif
+
+static void na_tray_manager_finalize (GObject *object);
+static void na_tray_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void na_tray_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void na_tray_manager_unmanage (NaTrayManager *manager);
+
+G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT)
+
+static void
+na_tray_manager_init (NaTrayManager *manager)
+{
+ manager->invisible = NULL;
+ manager->socket_table = g_hash_table_new (NULL, NULL);
+
+ manager->fg.red = 0;
+ manager->fg.green = 0;
+ manager->fg.blue = 0;
+
+ manager->error.red = 0xff;
+ manager->error.green = 0;
+ manager->error.blue = 0;
+
+ manager->warning.red = 0xff;
+ manager->warning.green = 0xff;
+ manager->warning.blue = 0;
+
+ manager->success.red = 0;
+ manager->success.green = 0xff;
+ manager->success.blue = 0;
+}
+
+static void
+na_tray_manager_class_init (NaTrayManagerClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *)klass;
+
+ gobject_class->finalize = na_tray_manager_finalize;
+ gobject_class->set_property = na_tray_manager_set_property;
+ gobject_class->get_property = na_tray_manager_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_ORIENTATION,
+ g_param_spec_enum ("orientation",
+ "orientation",
+ "orientation",
+ GTK_TYPE_ORIENTATION,
+ GTK_ORIENTATION_HORIZONTAL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT));
+
+ manager_signals[TRAY_ICON_ADDED] =
+ g_signal_new ("tray_icon_added",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_SOCKET);
+
+ manager_signals[TRAY_ICON_REMOVED] =
+ g_signal_new ("tray_icon_removed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_SOCKET);
+ manager_signals[MESSAGE_SENT] =
+ g_signal_new ("message_sent",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 4,
+ GTK_TYPE_SOCKET,
+ G_TYPE_STRING,
+ G_TYPE_LONG,
+ G_TYPE_LONG);
+ manager_signals[MESSAGE_CANCELLED] =
+ g_signal_new ("message_cancelled",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_SOCKET,
+ G_TYPE_LONG);
+ manager_signals[LOST_SELECTION] =
+ g_signal_new ("lost_selection",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+#if defined (GDK_WINDOWING_X11)
+ /* Nothing */
+#elif defined (GDK_WINDOWING_WIN32)
+ g_warning ("Port NaTrayManager to Win32");
+#else
+ g_warning ("Port NaTrayManager to this GTK+ backend");
+#endif
+}
+
+static void
+na_tray_manager_finalize (GObject *object)
+{
+ NaTrayManager *manager;
+
+ manager = NA_TRAY_MANAGER (object);
+
+ na_tray_manager_unmanage (manager);
+
+ g_list_free (manager->messages);
+ g_hash_table_destroy (manager->socket_table);
+
+ G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object);
+}
+
+static void
+na_tray_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NaTrayManager *manager = NA_TRAY_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ na_tray_manager_set_orientation (manager, g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+na_tray_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NaTrayManager *manager = NA_TRAY_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, manager->orientation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+NaTrayManager *
+na_tray_manager_new (void)
+{
+ NaTrayManager *manager;
+
+ manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL);
+
+ return manager;
+}
+
+#ifdef GDK_WINDOWING_X11
+
+static gboolean
+na_tray_manager_plug_removed (GtkSocket *socket,
+ NaTrayManager *manager)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (socket);
+
+ g_hash_table_remove (manager->socket_table,
+ GINT_TO_POINTER (child->icon_window));
+ g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
+
+ /* This destroys the socket. */
+ return FALSE;
+}
+
+static void
+na_tray_manager_handle_dock_request (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ Window icon_window = xevent->data.l[2];
+ GtkWidget *child;
+
+ if (g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (icon_window)))
+ {
+ /* We already got this notification earlier, ignore this one */
+ return;
+ }
+
+ child = na_tray_child_new (manager->screen, icon_window);
+ if (child == NULL) /* already gone or other error */
+ return;
+
+ g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
+ child);
+
+ /* If the child wasn't attached, then destroy it */
+
+ if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child))))
+ {
+ gtk_widget_destroy (child);
+ return;
+ }
+
+ g_signal_connect (child, "plug_removed",
+ G_CALLBACK (na_tray_manager_plug_removed), manager);
+
+ gtk_socket_add_id (GTK_SOCKET (child), icon_window);
+
+ if (!gtk_socket_get_plug_window (GTK_SOCKET (child)))
+ {
+ /* Embedding failed, we won't get a plug-removed signal */
+ /* This signal destroys the socket */
+ g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
+ return;
+ }
+
+ g_hash_table_insert (manager->socket_table,
+ GINT_TO_POINTER (icon_window), child);
+ gtk_widget_show (child);
+}
+
+static void
+pending_message_free (PendingMessage *message)
+{
+ g_free (message->str);
+ g_free (message);
+}
+
+static void
+na_tray_manager_handle_message_data (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ GList *p;
+ int len;
+
+ /* Try to see if we can find the pending message in the list */
+ for (p = manager->messages; p; p = p->next)
+ {
+ PendingMessage *msg = p->data;
+
+ if (xevent->window == msg->window)
+ {
+ /* Append the message */
+ len = MIN (msg->remaining_len, 20);
+
+ memcpy ((msg->str + msg->len - msg->remaining_len),
+ &xevent->data, len);
+ msg->remaining_len -= len;
+
+ if (msg->remaining_len == 0)
+ {
+ GtkSocket *socket;
+
+ socket = g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (msg->window));
+
+ if (socket)
+ g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
+ socket, msg->str, msg->id, msg->timeout);
+
+ pending_message_free (msg);
+ manager->messages = g_list_remove_link (manager->messages, p);
+ g_list_free_1 (p);
+ }
+
+ break;
+ }
+ }
+}
+
+static void
+na_tray_manager_handle_begin_message (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ GtkSocket *socket;
+ GList *p;
+ PendingMessage *msg;
+ long timeout;
+ long len;
+ long id;
+
+ socket = g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (xevent->window));
+ /* we don't know about this tray icon, so ignore the message */
+ if (!socket)
+ return;
+
+ timeout = xevent->data.l[2];
+ len = xevent->data.l[3];
+ id = xevent->data.l[4];
+
+ /* Check if the same message is already in the queue and remove it if so */
+ for (p = manager->messages; p; p = p->next)
+ {
+ PendingMessage *pmsg = p->data;
+
+ if (xevent->window == pmsg->window &&
+ id == pmsg->id)
+ {
+ /* Hmm, we found it, now remove it */
+ pending_message_free (pmsg);
+ manager->messages = g_list_remove_link (manager->messages, p);
+ g_list_free_1 (p);
+ break;
+ }
+ }
+
+ if (len == 0)
+ {
+ g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
+ socket, "", id, timeout);
+ }
+ else
+ {
+ /* Now add the new message to the queue */
+ msg = g_new0 (PendingMessage, 1);
+ msg->window = xevent->window;
+ msg->timeout = timeout;
+ msg->len = len;
+ msg->id = id;
+ msg->remaining_len = msg->len;
+ msg->str = g_malloc (msg->len + 1);
+ msg->str[msg->len] = '\0';
+ manager->messages = g_list_prepend (manager->messages, msg);
+ }
+}
+
+static void
+na_tray_manager_handle_cancel_message (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ GList *p;
+ GtkSocket *socket;
+ long id;
+
+ id = xevent->data.l[2];
+
+ /* Check if the message is in the queue and remove it if so */
+ for (p = manager->messages; p; p = p->next)
+ {
+ PendingMessage *msg = p->data;
+
+ if (xevent->window == msg->window &&
+ id == msg->id)
+ {
+ pending_message_free (msg);
+ manager->messages = g_list_remove_link (manager->messages, p);
+ g_list_free_1 (p);
+ break;
+ }
+ }
+
+ socket = g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (xevent->window));
+
+ if (socket)
+ {
+ g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
+ socket, xevent->data.l[2]);
+ }
+}
+
+static GdkFilterReturn
+na_tray_manager_window_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (GdkXEvent *)xev;
+ NaTrayManager *manager = data;
+
+ if (xevent->type == ClientMessage)
+ {
+ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_REQUEST_DOCK */
+ if (xevent->xclient.message_type == manager->opcode_atom &&
+ xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK)
+ {
+ na_tray_manager_handle_dock_request (manager,
+ (XClientMessageEvent *) xevent);
+ return GDK_FILTER_REMOVE;
+ }
+ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_BEGIN_MESSAGE */
+ else if (xevent->xclient.message_type == manager->opcode_atom &&
+ xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE)
+ {
+ na_tray_manager_handle_begin_message (manager,
+ (XClientMessageEvent *) event);
+ return GDK_FILTER_REMOVE;
+ }
+ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_CANCEL_MESSAGE */
+ else if (xevent->xclient.message_type == manager->opcode_atom &&
+ xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE)
+ {
+ na_tray_manager_handle_cancel_message (manager,
+ (XClientMessageEvent *) event);
+ return GDK_FILTER_REMOVE;
+ }
+ /* _NET_SYSTEM_TRAY_MESSAGE_DATA */
+ else if (xevent->xclient.message_type == manager->message_data_atom)
+ {
+ na_tray_manager_handle_message_data (manager,
+ (XClientMessageEvent *) event);
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ else if (xevent->type == SelectionClear)
+ {
+ g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
+ na_tray_manager_unmanage (manager);
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+#if 0
+//FIXME investigate why this doesn't work
+static gboolean
+na_tray_manager_selection_clear_event (GtkWidget *widget,
+ GdkEventSelection *event,
+ NaTrayManager *manager)
+{
+ g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
+ na_tray_manager_unmanage (manager);
+
+ return FALSE;
+}
+#endif
+#endif
+
+static void
+na_tray_manager_unmanage (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkDisplay *display;
+ guint32 timestamp;
+ GtkWidget *invisible;
+ GdkWindow *window;
+
+ if (manager->invisible == NULL)
+ return;
+
+ invisible = manager->invisible;
+ window = gtk_widget_get_window (invisible);
+
+ g_assert (GTK_IS_INVISIBLE (invisible));
+ g_assert (gtk_widget_get_realized (invisible));
+ g_assert (GDK_IS_WINDOW (window));
+
+ display = gtk_widget_get_display (invisible);
+
+ if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
+ window)
+ {
+ timestamp = gdk_x11_get_server_time (window);
+ gdk_selection_owner_set_for_display (display,
+ NULL,
+ manager->selection_atom,
+ timestamp,
+ TRUE);
+ }
+
+ gdk_window_remove_filter (window,
+ na_tray_manager_window_filter, manager);
+
+ manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */
+ gtk_widget_destroy (invisible);
+ g_object_unref (G_OBJECT (invisible));
+#endif
+}
+
+static void
+na_tray_manager_set_orientation_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Atom orientation_atom;
+ gulong data[1];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ display = gtk_widget_get_display (manager->invisible);
+ orientation_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_ORIENTATION");
+
+ data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ?
+ SYSTEM_TRAY_ORIENTATION_HORZ :
+ SYSTEM_TRAY_ORIENTATION_VERT;
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ orientation_atom,
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *) &data, 1);
+#endif
+}
+
+static void
+na_tray_manager_set_visual_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Visual *xvisual;
+ Atom visual_atom;
+ gulong data[1];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ /* The visual property is a hint to the tray icons as to what visual they
+ * should use for their windows. If the X server has RGBA colormaps, then
+ * we tell the tray icons to use a RGBA colormap and we'll composite the
+ * icon onto its parents with real transparency. Otherwise, we just tell
+ * the icon to use our colormap, and we'll do some hacks with parent
+ * relative backgrounds to simulate transparency.
+ */
+
+ display = gtk_widget_get_display (manager->invisible);
+ visual_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_VISUAL");
+
+ if (gdk_screen_get_rgba_visual (manager->screen) != NULL)
+ xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen));
+ else
+ {
+ /* We actually want the visual of the tray where the icons will
+ * be embedded. In almost all cases, this will be the same as the visual
+ * of the screen.
+ */
+ xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (manager->screen));
+ }
+
+ data[0] = XVisualIDFromVisual (xvisual);
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ visual_atom,
+ XA_VISUALID, 32,
+ PropModeReplace,
+ (guchar *) &data, 1);
+#endif
+}
+
+static void
+na_tray_manager_set_colors_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Atom atom;
+ gulong data[12];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ display = gtk_widget_get_display (manager->invisible);
+ atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_COLORS");
+
+ data[0] = manager->fg.red * 0x101;
+ data[1] = manager->fg.green * 0x101;
+ data[2] = manager->fg.blue * 0x101;
+ data[3] = manager->error.red * 0x101;
+ data[4] = manager->error.green * 0x101;
+ data[5] = manager->error.blue * 0x101;
+ data[6] = manager->warning.red * 0x101;
+ data[7] = manager->warning.green * 0x101;
+ data[8] = manager->warning.blue * 0x101;
+ data[9] = manager->success.red * 0x101;
+ data[10] = manager->success.green * 0x101;
+ data[11] = manager->success.blue * 0x101;
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ atom,
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *) &data, 12);
+#endif
+}
+
+#ifdef GDK_WINDOWING_X11
+
+static gboolean
+na_tray_manager_manage_screen_x11 (NaTrayManager *manager)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ Screen *xscreen;
+ GtkWidget *invisible;
+ GdkWindow *window;
+ char *selection_atom_name;
+ guint32 timestamp;
+
+ g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE);
+ g_return_val_if_fail (manager->screen == NULL, FALSE);
+
+ /* If there's already a manager running on the screen
+ * we can't create another one.
+ */
+#if 0
+ if (na_tray_manager_check_running_screen_x11 ())
+ return FALSE;
+#endif
+
+ screen = gdk_screen_get_default ();
+ manager->screen = screen;
+
+ display = gdk_screen_get_display (screen);
+ xscreen = GDK_SCREEN_XSCREEN (screen);
+
+ invisible = gtk_invisible_new_for_screen (screen);
+ gtk_widget_realize (invisible);
+
+ gtk_widget_add_events (invisible,
+ GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
+
+ selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
+ gdk_x11_get_default_screen ());
+ manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE);
+ g_free (selection_atom_name);
+
+ manager->invisible = invisible;
+ g_object_ref (G_OBJECT (manager->invisible));
+
+ na_tray_manager_set_orientation_property (manager);
+ na_tray_manager_set_visual_property (manager);
+ na_tray_manager_set_colors_property (manager);
+
+ window = gtk_widget_get_window (invisible);
+
+ timestamp = gdk_x11_get_server_time (window);
+
+ /* Check if we could set the selection owner successfully */
+ if (gdk_selection_owner_set_for_display (display,
+ window,
+ manager->selection_atom,
+ timestamp,
+ TRUE))
+ {
+ XClientMessageEvent xev;
+ GdkAtom opcode_atom;
+ GdkAtom message_data_atom;
+
+ xev.type = ClientMessage;
+ xev.window = RootWindowOfScreen (xscreen);
+ xev.message_type = gdk_x11_get_xatom_by_name_for_display (display,
+ "MANAGER");
+
+ xev.format = 32;
+ xev.data.l[0] = timestamp;
+ xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
+ manager->selection_atom);
+ xev.data.l[2] = GDK_WINDOW_XID (window);
+ xev.data.l[3] = 0; /* manager specific data */
+ xev.data.l[4] = 0; /* manager specific data */
+
+ XSendEvent (GDK_DISPLAY_XDISPLAY (display),
+ RootWindowOfScreen (xscreen),
+ False, StructureNotifyMask, (XEvent *)&xev);
+
+ opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
+ manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display,
+ opcode_atom);
+
+ message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA",
+ FALSE);
+ manager->message_data_atom = gdk_x11_atom_to_xatom_for_display (display,
+ message_data_atom);
+
+ /* Add a window filter */
+#if 0
+ /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */
+ g_signal_connect (invisible, "selection-clear-event",
+ G_CALLBACK (na_tray_manager_selection_clear_event),
+ manager);
+#endif
+ gdk_window_add_filter (window,
+ na_tray_manager_window_filter, manager);
+ return TRUE;
+ }
+ else
+ {
+ gtk_widget_destroy (invisible);
+ g_object_unref (invisible);
+ manager->invisible = NULL;
+
+ manager->screen = NULL;
+
+ return FALSE;
+ }
+}
+
+#endif
+
+gboolean
+na_tray_manager_manage_screen (NaTrayManager *manager)
+{
+ g_return_val_if_fail (manager->screen == NULL, FALSE);
+
+#ifdef GDK_WINDOWING_X11
+ return na_tray_manager_manage_screen_x11 (manager);
+#else
+ return FALSE;
+#endif
+}
+
+#ifdef GDK_WINDOWING_X11
+
+static gboolean
+na_tray_manager_check_running_screen_x11 (void)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ Atom selection_atom;
+ char *selection_atom_name;
+
+ screen = gdk_screen_get_default ();
+ display = gdk_screen_get_display (screen);
+ selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
+ gdk_x11_get_default_screen ());
+ selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ selection_atom_name);
+ g_free (selection_atom_name);
+
+ if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
+ selection_atom) != None)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+#endif
+
+gboolean
+na_tray_manager_check_running (void)
+{
+#ifdef GDK_WINDOWING_X11
+ return na_tray_manager_check_running_screen_x11 ();
+#else
+ return FALSE;
+#endif
+}
+
+void
+na_tray_manager_set_orientation (NaTrayManager *manager,
+ GtkOrientation orientation)
+{
+ g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
+
+ if (manager->orientation != orientation)
+ {
+ manager->orientation = orientation;
+
+ na_tray_manager_set_orientation_property (manager);
+
+ g_object_notify (G_OBJECT (manager), "orientation");
+ }
+}
+
+void
+na_tray_manager_set_colors (NaTrayManager *manager,
+ ClutterColor *fg,
+ ClutterColor *error,
+ ClutterColor *warning,
+ ClutterColor *success)
+{
+ g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
+
+ if (!clutter_color_equal (&manager->fg, fg) ||
+ !clutter_color_equal (&manager->error, error) ||
+ !clutter_color_equal (&manager->warning, warning) ||
+ !clutter_color_equal (&manager->success, success))
+ {
+ manager->fg = *fg;
+ manager->error = *error;
+ manager->warning = *warning;
+ manager->success = *success;
+
+ na_tray_manager_set_colors_property (manager);
+ }
+}
+
+GtkOrientation
+na_tray_manager_get_orientation (NaTrayManager *manager)
+{
+ g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
+
+ return manager->orientation;
+}
diff --git a/src/tray/na-tray-manager.h b/src/tray/na-tray-manager.h
new file mode 100644
index 0000000..727b02d
--- /dev/null
+++ b/src/tray/na-tray-manager.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* na-tray-manager.h
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ * Copyright (C) 2003-2006 Vincent Untz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Used to be: eggtraymanager.h
+ */
+
+#ifndef __NA_TRAY_MANAGER_H__
+#define __NA_TRAY_MANAGER_H__
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#include <gtk/gtk.h>
+#include <clutter/clutter.h>
+
+#include "na-tray-child.h"
+
+G_BEGIN_DECLS
+
+#define NA_TYPE_TRAY_MANAGER (na_tray_manager_get_type ())
+#define NA_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManager))
+#define NA_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass))
+#define NA_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_MANAGER))
+#define NA_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_MANAGER))
+#define NA_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass))
+
+typedef struct _NaTrayManager NaTrayManager;
+typedef struct _NaTrayManagerClass NaTrayManagerClass;
+
+struct _NaTrayManager
+{
+ GObject parent_instance;
+
+#ifdef GDK_WINDOWING_X11
+ GdkAtom selection_atom;
+ Atom opcode_atom;
+ Atom message_data_atom;
+#endif
+
+ GtkWidget *invisible;
+ GdkScreen *screen;
+ GtkOrientation orientation;
+ ClutterColor fg;
+ ClutterColor error;
+ ClutterColor warning;
+ ClutterColor success;
+
+ GList *messages;
+ GHashTable *socket_table;
+};
+
+struct _NaTrayManagerClass
+{
+ GObjectClass parent_class;
+
+ void (* tray_icon_added) (NaTrayManager *manager,
+ NaTrayChild *child);
+ void (* tray_icon_removed) (NaTrayManager *manager,
+ NaTrayChild *child);
+
+ void (* message_sent) (NaTrayManager *manager,
+ NaTrayChild *child,
+ const gchar *message,
+ glong id,
+ glong timeout);
+
+ void (* message_cancelled) (NaTrayManager *manager,
+ NaTrayChild *child,
+ glong id);
+
+ void (* lost_selection) (NaTrayManager *manager);
+};
+
+GType na_tray_manager_get_type (void);
+
+gboolean na_tray_manager_check_running (void);
+NaTrayManager *na_tray_manager_new (void);
+gboolean na_tray_manager_manage_screen (NaTrayManager *manager);
+void na_tray_manager_set_orientation (NaTrayManager *manager,
+ GtkOrientation orientation);
+GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager);
+void na_tray_manager_set_colors (NaTrayManager *manager,
+ ClutterColor *fg,
+ ClutterColor *error,
+ ClutterColor *warning,
+ ClutterColor *success);
+
+
+G_END_DECLS
+
+#endif /* __NA_TRAY_MANAGER_H__ */
diff --git a/subprojects/extensions-app/COPYING b/subprojects/extensions-app/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/subprojects/extensions-app/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/subprojects/extensions-app/README.md b/subprojects/extensions-app/README.md
new file mode 100644
index 0000000..6c0feb6
--- /dev/null
+++ b/subprojects/extensions-app/README.md
@@ -0,0 +1,32 @@
+# ![logo] GNOME Extensions
+GNOME Extensions is a small application for managing GNOME Shell
+extensions. It is usually built as part of gnome-shell, but can be
+used as a stand-alone project as well.
+
+Bugs should be reported to the GNOME [bug tracking system][bug-tracker].
+
+## Installation
+If Extensions is not already installed on your GNOME system, we
+recommend getting it from [flathub].
+
+<a href='https://flathub.org/apps/details/org.gnome.Extensions'>
+ <img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/>
+</a>
+
+## Building
+Before the project can be built stand-alone, the po directory has
+to be populated with translations (from gnome-shell).
+
+To do that, simply run the included script:
+```sh
+$ ./generate-translations.sh
+```
+
+## License
+gnome-extensions-app is distributed under the terms of the GNU General Public
+License, version 2 or later. See the [COPYING][license] file for details.
+
+[logo]: logo.png
+[bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell/issues
+[flathub]: https://flathub.org
+[license]: COPYING
diff --git a/subprojects/extensions-app/build-aux/flatpak/org.gnome.Extensions.json b/subprojects/extensions-app/build-aux/flatpak/org.gnome.Extensions.json
new file mode 100644
index 0000000..3ac00f6
--- /dev/null
+++ b/subprojects/extensions-app/build-aux/flatpak/org.gnome.Extensions.json
@@ -0,0 +1,39 @@
+{
+ "app-id": "org.gnome.Extensions.Devel",
+ "runtime": "org.gnome.Platform",
+ "runtime-version": "master",
+ "sdk": "org.gnome.Sdk",
+ "command": "gnome-extensions-app",
+ "tags": ["nightly"],
+ "finish-args": [
+ "--share=ipc", "--socket=fallback-x11",
+ "--socket=wayland",
+ "--device=dri",
+ "--talk-name=org.gnome.SessionManager",
+ "--talk-name=org.gnome.Shell.Extensions"
+ ],
+ "build-options": {
+ "cflags": "-O2 -g"
+ },
+ "modules": [
+ {
+ "name": "gnome-extensions-app",
+ "buildsystem": "meson",
+ "builddir": true,
+ "subdir": "subprojects/extensions-app",
+ "config-opts": ["-Dprofile=development"],
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://gitlab.gnome.org/GNOME/gnome-shell.git"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "subprojects/extensions-app/generate-translations.sh"
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/extensions-app/build-aux/meson/check-version.py b/subprojects/extensions-app/build-aux/meson/check-version.py
new file mode 120000
index 0000000..fbe6c74
--- /dev/null
+++ b/subprojects/extensions-app/build-aux/meson/check-version.py
@@ -0,0 +1 @@
+../../../../meson/check-version.py \ No newline at end of file
diff --git a/subprojects/extensions-app/data/css/style.css b/subprojects/extensions-app/data/css/style.css
new file mode 100644
index 0000000..dac7633
--- /dev/null
+++ b/subprojects/extensions-app/data/css/style.css
@@ -0,0 +1,21 @@
+row.extension>box {
+ margin: 12px;
+}
+
+row.extension>box,
+row.extension box.header {
+ border-spacing: 12px;
+}
+
+row.extension box.actions,
+row.extension box.actions>box {
+ border-spacing: 6px;
+}
+
+row.extension box.information,
+row.extension box.status {
+ border-spacing: 3px;
+}
+
+image.error { color: @error_color; }
+image.warning { color: @warning_color; }
diff --git a/subprojects/extensions-app/data/dbus-interfaces/org.gnome.Shell.Extensions.xml b/subprojects/extensions-app/data/dbus-interfaces/org.gnome.Shell.Extensions.xml
new file mode 120000
index 0000000..defde79
--- /dev/null
+++ b/subprojects/extensions-app/data/dbus-interfaces/org.gnome.Shell.Extensions.xml
@@ -0,0 +1 @@
+../../../../data/dbus-interfaces/org.gnome.Shell.Extensions.xml \ No newline at end of file
diff --git a/subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.Devel.svg b/subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.Devel.svg
new file mode 100644
index 0000000..60c1018
--- /dev/null
+++ b/subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.Devel.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><clipPath id="a"><path d="M0 0h128v128H0z"/></clipPath><clipPath id="c"><path d="M0 0h128v128H0z"/></clipPath><clipPath id="i"><path d="M0 0h128v128H0z"/></clipPath><clipPath id="g"><path d="M0 0h128v128H0z"/></clipPath><g id="m" clip-path="url(#i)"><use xlink:href="#j" mask="url(#k)"/></g><g id="e" clip-path="url(#a)"><path d="M21.965 18.363v4l2.039 17.75c.418 3.637 1.055 7.282.844 10.938-.106 1.828-.465 3.668-1.309 5.293-.426.812-.969 1.57-1.644 2.191-.676.621-1.485 1.106-2.364 1.356-1.047.297-2.156.261-3.238.164-1.094-.098-2.227-.282-3.137-.903-.394-.273-.73-.617-1.082-.945-.351-.328-.726-.645-1.168-.832-.691-.297-1.492-.254-2.207-.023-.715.23-1.355.64-1.972 1.066-1.188.824-2.325 1.758-3.184 2.922-1.734 2.344-2.203 5.46-1.711 8.336.367 2.152 1.258 4.238 2.688 5.89.722.836 1.582 1.563 2.566 2.063.98.504 2.09.773 3.195.71 1.446-.085 2.79-.73 4.114-1.327 1.32-.594 2.718-1.16 4.168-1.067a5.113 5.113 0 012.332.758 5.958 5.958 0 011.773 1.707c.848 1.242 1.234 2.742 1.531 4.219 1.832 9.133.778 18.82-2.98 27.344v4c6.812 2.347 13.8 4.199 20.886 5.535 2.356.441 4.801.824 7.13.246 1.163-.29 2.28-.832 3.148-1.66a5.39 5.39 0 001.441-2.34c.266-.887.278-1.828.254-2.754L54 110c-.426.09-.875.059-1.285-.082a2.751 2.751 0 01-1.16-.8 3.768 3.768 0 01-.7-1.231c-.316-.895-.351-1.864-.355-2.813-.008-1.57.063-3.187.684-4.629.629-1.465 1.793-2.652 3.117-3.535 2.64-1.758 5.898-2.375 9.07-2.414 2.91-.035 5.918.414 8.387 1.953 1.238.77 2.316 1.809 3.043 3.07.722 1.262 1.078 2.739.953 4.188-.098 1.117-.477 2.2-1.02 3.18A9.862 9.862 0 0172 110c.012.996.02 1.992.02 2.988.003.555.003 1.114.07 1.664.07.551.21 1.098.476 1.586.383.7 1.012 1.239 1.692 1.657.68.414 1.422.722 2.176.984 4.207 1.46 8.793 1.562 13.203.922a40.756 40.756 0 0015.86-5.88l-.005-4.003a60.146 60.146 0 01-3.433-25.465c.125-1.512.312-3.039.894-4.441.29-.7.68-1.364 1.18-1.93.504-.566 1.12-1.039 1.82-1.332.82-.344 1.727-.434 2.613-.367.887.07 1.75.297 2.598.57 1.688.547 3.344 1.297 5.117 1.418 1.492.098 2.996-.266 4.332-.937a9.894 9.894 0 004.016-3.715 9.91 9.91 0 001.418-5.285l.176-2.258a16.406 16.406 0 00-3.075-6.477c-.785-.996-1.707-1.914-2.835-2.496a5.247 5.247 0 00-1.805-.558 4.18 4.18 0 00-1.875.214c-.988.364-1.774 1.125-2.703 1.614-1.032.543-2.203.738-3.364.824-1.148.086-2.324.07-3.425-.274-1.48-.464-2.743-1.52-3.602-2.812-.855-1.293-1.332-2.805-1.59-4.328-.574-3.399-.117-6.871.266-10.297a227.56 227.56 0 001.34-19.973 497.333 497.333 0 01-21.118-5.117c-1.355-.36-2.718-.726-4.113-.894-1.394-.165-2.836-.118-4.168.332a7.108 7.108 0 00-2.93 1.894v4c0 .414.016.828.11 1.235.094.402.258.789.445 1.156.371.738.82 1.433 1.313 2.097.98 1.329 2.136 2.563 2.765 4.094.688 1.676.668 3.606.07 5.317-.597 1.707-1.75 3.203-3.179 4.312-1.43 1.113-3.133 1.852-4.895 2.262-1.765.414-3.593.508-5.402.406-2.926-.164-5.906-.875-8.25-2.633-1.172-.883-2.164-2.015-2.793-3.34-.625-1.324-.879-2.828-.668-4.277.293-1.973 1.399-3.71 2.45-5.406.421-.684.831-1.38 1.078-2.14.242-.763.296-1.571.308-2.372L54.633 18a1.89 1.89 0 01-1.695.777c-.516-.047-.989-.3-1.41-.601-.419-.301-.801-.653-1.231-.938-.871-.578-1.914-.855-2.957-.933-1.043-.075-2.09.039-3.125.191-2.403.356-4.77.91-7.176 1.266-2.402.351-4.832.46-7.262.496zm0 0" fill="url(#b)"/><path d="M21.965 18.363l2.039 17.746c.426 3.715 1.16 7.414 1.094 11.149-.035 1.867-.34 3.77-1.239 5.41-.449.816-1.047 1.562-1.785 2.133-.738.574-1.617.972-2.543 1.09-1.504.195-2.996-.344-4.379-.961-1.379-.621-2.75-1.344-4.246-1.555-1.465-.21-2.984.094-4.293.777-1.308.688-2.406 1.746-3.207 2.989-1.601 2.484-1.972 5.605-1.574 8.535.3 2.219 1.05 4.433 2.512 6.133.73.847 1.633 1.558 2.656 2.011 1.023.457 2.168.657 3.281.52 1.54-.192 2.926-1 4.324-1.676.704-.336 1.422-.644 2.176-.828.754-.184 1.551-.238 2.309-.066.82.183 1.566.628 2.164 1.218.601.586 1.05 1.313 1.375 2.086.64 1.551.762 3.258.84 4.934a123.2 123.2 0 01-2.25 29.965 133.42 133.42 0 0020.886 5.53c2.352.427 4.793.782 7.11.196 1.16-.293 2.27-.828 3.152-1.633.883-.804 1.528-1.89 1.711-3.074.133-.879.012-1.789-.25-2.64-.262-.852-.668-1.657-1.101-2.438-.868-1.559-1.887-3.086-2.227-4.84-.3-1.558-.023-3.207.676-4.633.703-1.425 1.816-2.629 3.12-3.535 2.614-1.804 5.9-2.383 9.075-2.41 2.91-.023 5.918.414 8.39 1.953 1.235.77 2.313 1.809 3.04 3.07.73 1.258 1.097 2.743.953 4.188-.117 1.113-.524 2.18-1.024 3.18-.5 1.004-1.093 1.957-1.609 2.949-.512.996-.95 2.047-1.11 3.152-.156 1.11-.019 2.29.555 3.25.41.684 1.02 1.23 1.696 1.649.675.422 1.418.722 2.168.992a30.23 30.23 0 0015.183 1.332 30.128 30.128 0 0013.88-6.293l-2.587-19.371c-.453-3.406-1.14-6.813-1.047-10.246.043-1.719.34-3.461 1.141-4.98a6.796 6.796 0 011.578-2.02 5.338 5.338 0 012.262-1.18c.8-.183 1.637-.164 2.441-.02.809.145 1.59.41 2.352.708 1.527.597 3.023 1.34 4.648 1.558 1.5.203 3.055-.062 4.426-.699 1.375-.64 2.566-1.648 3.469-2.863 1.808-2.434 2.418-5.625 2.047-8.63-.305-2.448-1.258-4.862-2.953-6.655-.848-.895-1.875-1.63-3.016-2.098-1.14-.473-2.395-.684-3.621-.563-1.637.157-3.153.875-4.688 1.461-.765.297-1.55.559-2.355.707-.809.149-1.649.176-2.45-.004-1.214-.27-2.296-1.007-3.081-1.972-.786-.965-1.286-2.14-1.547-3.36-.524-2.43-.125-4.953.23-7.414a199.18 199.18 0 002-24.914 119.325 119.325 0 00-22.3-4.867c-2.329-.281-4.75-.48-6.97.274-1.113.378-2.152 1-2.933 1.875-.781.87-1.289 2.011-1.312 3.183-.02.824.199 1.645.543 2.395.347.75.812 1.441 1.312 2.097 1 1.317 2.148 2.559 2.773 4.09.688 1.676.676 3.606.082 5.32-.593 1.711-1.746 3.207-3.175 4.32-1.434 1.114-3.137 1.852-4.903 2.263-1.765.41-3.593.5-5.406.394-2.918-.172-5.894-.89-8.23-2.652-1.168-.88-2.157-2.016-2.79-3.332-.632-1.317-.906-2.82-.69-4.266.296-1.965 1.437-3.68 2.398-5.418.48-.871.925-1.77 1.199-2.723.277-.953.379-1.976.168-2.945-.219-1.016-.774-1.941-1.512-2.672-.738-.726-1.652-1.261-2.629-1.617-1.949-.71-4.078-.707-6.148-.586a58.229 58.229 0 00-22.254 5.867zm0 0" fill="#33d17a"/></g><g id="f" clip-path="url(#c)" filter="url(#d)"><use xlink:href="#e"/></g><g id="j" clip-path="url(#g)"><path d="M128 80.64V128H0V80.64zm0 0" fill="url(#h)"/><path d="M13.309 80.64L60.664 128H81.88l-47.36-47.36zm42.421 0L103.094 128h21.215L76.945 80.64zm42.43 0L128 110.48V89.27l-8.629-8.63zM0 88.548v21.215L18.238 128h21.215zm0 0"/></g><linearGradient id="l" gradientUnits="userSpaceOnUse" x1="10.23" y1="87.43" x2="133.236" y2="88.679" gradientTransform="translate(-8 -16)"><stop offset="0" stop-color="#208757"/><stop offset=".077" stop-color="#2ec27e"/><stop offset=".147" stop-color="#208a5a"/><stop offset=".198" stop-color="#26a269"/><stop offset=".364" stop-color="#1c774d"/><stop offset=".407" stop-color="#26a269"/><stop offset=".493" stop-color="#26a269"/><stop offset=".576" stop-color="#26a269"/><stop offset=".606" stop-color="#2ec27e"/><stop offset=".681" stop-color="#26a269"/><stop offset=".784" stop-color="#1d7a4e"/><stop offset=".945" stop-color="#28ab6f"/><stop offset="1" stop-color="#48d493"/></linearGradient><linearGradient id="h" gradientUnits="userSpaceOnUse" x1="300" y1="235" x2="428" y2="235" gradientTransform="matrix(0 .37 -.98462 0 295.385 -30.36)"><stop offset="0" stop-color="#f9f06b"/><stop offset="1" stop-color="#f5c211"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="10.23" y1="87.43" x2="133.236" y2="88.679" gradientTransform="translate(-8 -16)"><stop offset="0" stop-color="#208757"/><stop offset=".077" stop-color="#2ec27e"/><stop offset=".147" stop-color="#208a5a"/><stop offset=".198" stop-color="#26a269"/><stop offset=".364" stop-color="#1c774d"/><stop offset=".407" stop-color="#26a269"/><stop offset=".493" stop-color="#26a269"/><stop offset=".576" stop-color="#26a269"/><stop offset=".606" stop-color="#2ec27e"/><stop offset=".681" stop-color="#26a269"/><stop offset=".784" stop-color="#1d7a4e"/><stop offset=".945" stop-color="#28ab6f"/><stop offset="1" stop-color="#48d493"/></linearGradient><mask id="k"><g filter="url(#d)"><path fill-opacity=".8" d="M0 0h128v128H0z"/></g></mask><mask id="n"><use xlink:href="#f"/></mask><filter id="d" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%"><feColorMatrix in="SourceGraphic" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter></defs><path d="M21.965 18.363v4l2.039 17.75c.418 3.637 1.055 7.282.844 10.938-.106 1.828-.465 3.668-1.309 5.293-.426.812-.969 1.57-1.644 2.191-.676.621-1.485 1.106-2.364 1.356-1.047.297-2.156.261-3.238.164-1.094-.098-2.227-.282-3.137-.903-.394-.273-.73-.617-1.082-.945-.351-.328-.726-.645-1.168-.832-.691-.297-1.492-.254-2.207-.023-.715.23-1.355.64-1.972 1.066-1.188.824-2.325 1.758-3.184 2.922-1.734 2.344-2.203 5.46-1.711 8.336.367 2.152 1.258 4.238 2.688 5.89.722.836 1.582 1.563 2.566 2.063.98.504 2.09.773 3.195.71 1.446-.085 2.79-.73 4.114-1.327 1.32-.594 2.718-1.16 4.168-1.067a5.113 5.113 0 012.332.758 5.958 5.958 0 011.773 1.707c.848 1.242 1.234 2.742 1.531 4.219 1.832 9.133.778 18.82-2.98 27.344v4c6.812 2.347 13.8 4.199 20.886 5.535 2.356.441 4.801.824 7.13.246 1.163-.29 2.28-.832 3.148-1.66a5.39 5.39 0 001.441-2.34c.266-.887.278-1.828.254-2.754L54 110c-.426.09-.875.059-1.285-.082a2.751 2.751 0 01-1.16-.8 3.768 3.768 0 01-.7-1.231c-.316-.895-.351-1.864-.355-2.813-.008-1.57.063-3.187.684-4.629.629-1.465 1.793-2.652 3.117-3.535 2.64-1.758 5.898-2.375 9.07-2.414 2.91-.035 5.918.414 8.387 1.953 1.238.77 2.316 1.809 3.043 3.07.722 1.262 1.078 2.739.953 4.188-.098 1.117-.477 2.2-1.02 3.18A9.862 9.862 0 0172 110c.012.996.02 1.992.02 2.988.003.555.003 1.114.07 1.664.07.551.21 1.098.476 1.586.383.7 1.012 1.239 1.692 1.657.68.414 1.422.722 2.176.984 4.207 1.46 8.793 1.562 13.203.922a40.756 40.756 0 0015.86-5.88l-.005-4.003a60.146 60.146 0 01-3.433-25.465c.125-1.512.312-3.039.894-4.441.29-.7.68-1.364 1.18-1.93.504-.566 1.12-1.039 1.82-1.332.82-.344 1.727-.434 2.613-.367.887.07 1.75.297 2.598.57 1.688.547 3.344 1.297 5.117 1.418 1.492.098 2.996-.266 4.332-.937a9.894 9.894 0 004.016-3.715 9.91 9.91 0 001.418-5.285l.176-2.258a16.406 16.406 0 00-3.075-6.477c-.785-.996-1.707-1.914-2.835-2.496a5.247 5.247 0 00-1.805-.558 4.18 4.18 0 00-1.875.214c-.988.364-1.774 1.125-2.703 1.614-1.032.543-2.203.738-3.364.824-1.148.086-2.324.07-3.425-.274-1.48-.464-2.743-1.52-3.602-2.812-.855-1.293-1.332-2.805-1.59-4.328-.574-3.399-.117-6.871.266-10.297a227.56 227.56 0 001.34-19.973 497.333 497.333 0 01-21.118-5.117c-1.355-.36-2.718-.726-4.113-.894-1.394-.165-2.836-.118-4.168.332a7.108 7.108 0 00-2.93 1.894v4c0 .414.016.828.11 1.235.094.402.258.789.445 1.156.371.738.82 1.433 1.313 2.097.98 1.329 2.136 2.563 2.765 4.094.688 1.676.668 3.606.07 5.317-.597 1.707-1.75 3.203-3.179 4.312-1.43 1.113-3.133 1.852-4.895 2.262-1.765.414-3.593.508-5.402.406-2.926-.164-5.906-.875-8.25-2.633-1.172-.883-2.164-2.015-2.793-3.34-.625-1.324-.879-2.828-.668-4.277.293-1.973 1.399-3.71 2.45-5.406.421-.684.831-1.38 1.078-2.14.242-.763.296-1.571.308-2.372L54.633 18a1.89 1.89 0 01-1.695.777c-.516-.047-.989-.3-1.41-.601-.419-.301-.801-.653-1.231-.938-.871-.578-1.914-.855-2.957-.933-1.043-.075-2.09.039-3.125.191-2.403.356-4.77.91-7.176 1.266-2.402.351-4.832.46-7.262.496zm0 0" fill="url(#l)"/><path d="M21.965 18.363l2.039 17.746c.426 3.715 1.16 7.414 1.094 11.149-.035 1.867-.34 3.77-1.239 5.41-.449.816-1.047 1.562-1.785 2.133-.738.574-1.617.972-2.543 1.09-1.504.195-2.996-.344-4.379-.961-1.379-.621-2.75-1.344-4.246-1.555-1.465-.21-2.984.094-4.293.777-1.308.688-2.406 1.746-3.207 2.989-1.601 2.484-1.972 5.605-1.574 8.535.3 2.219 1.05 4.433 2.512 6.133.73.847 1.633 1.558 2.656 2.011 1.023.457 2.168.657 3.281.52 1.54-.192 2.926-1 4.324-1.676.704-.336 1.422-.644 2.176-.828.754-.184 1.551-.238 2.309-.066.82.183 1.566.628 2.164 1.218.601.586 1.05 1.313 1.375 2.086.64 1.551.762 3.258.84 4.934a123.2 123.2 0 01-2.25 29.965 133.42 133.42 0 0020.886 5.53c2.352.427 4.793.782 7.11.196 1.16-.293 2.27-.828 3.152-1.633.883-.804 1.528-1.89 1.711-3.074.133-.879.012-1.789-.25-2.64-.262-.852-.668-1.657-1.101-2.438-.868-1.559-1.887-3.086-2.227-4.84-.3-1.558-.023-3.207.676-4.633.703-1.425 1.816-2.629 3.12-3.535 2.614-1.804 5.9-2.383 9.075-2.41 2.91-.023 5.918.414 8.39 1.953 1.235.77 2.313 1.809 3.04 3.07.73 1.258 1.097 2.743.953 4.188-.117 1.113-.524 2.18-1.024 3.18-.5 1.004-1.093 1.957-1.609 2.949-.512.996-.95 2.047-1.11 3.152-.156 1.11-.019 2.29.555 3.25.41.684 1.02 1.23 1.696 1.649.675.422 1.418.722 2.168.992a30.23 30.23 0 0015.183 1.332 30.128 30.128 0 0013.88-6.293l-2.587-19.371c-.453-3.406-1.14-6.813-1.047-10.246.043-1.719.34-3.461 1.141-4.98a6.796 6.796 0 011.578-2.02 5.338 5.338 0 012.262-1.18c.8-.183 1.637-.164 2.441-.02.809.145 1.59.41 2.352.708 1.527.597 3.023 1.34 4.648 1.558 1.5.203 3.055-.062 4.426-.699 1.375-.64 2.566-1.648 3.469-2.863 1.808-2.434 2.418-5.625 2.047-8.63-.305-2.448-1.258-4.862-2.953-6.655-.848-.895-1.875-1.63-3.016-2.098-1.14-.473-2.395-.684-3.621-.563-1.637.157-3.153.875-4.688 1.461-.765.297-1.55.559-2.355.707-.809.149-1.649.176-2.45-.004-1.214-.27-2.296-1.007-3.081-1.972-.786-.965-1.286-2.14-1.547-3.36-.524-2.43-.125-4.953.23-7.414a199.18 199.18 0 002-24.914 119.325 119.325 0 00-22.3-4.867c-2.329-.281-4.75-.48-6.97.274-1.113.378-2.152 1-2.933 1.875-.781.87-1.289 2.011-1.312 3.183-.02.824.199 1.645.543 2.395.347.75.812 1.441 1.312 2.097 1 1.317 2.148 2.559 2.773 4.09.688 1.676.676 3.606.082 5.32-.593 1.711-1.746 3.207-3.175 4.32-1.434 1.114-3.137 1.852-4.903 2.263-1.765.41-3.593.5-5.406.394-2.918-.172-5.894-.89-8.23-2.652-1.168-.88-2.157-2.016-2.79-3.332-.632-1.317-.906-2.82-.69-4.266.296-1.965 1.437-3.68 2.398-5.418.48-.871.925-1.77 1.199-2.723.277-.953.379-1.976.168-2.945-.219-1.016-.774-1.941-1.512-2.672-.738-.726-1.652-1.261-2.629-1.617-1.949-.71-4.078-.707-6.148-.586a58.229 58.229 0 00-22.254 5.867zm0 0" fill="#33d17a"/><use xlink:href="#m" mask="url(#n)"/></svg> \ No newline at end of file
diff --git a/subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.svg b/subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.svg
new file mode 100644
index 0000000..496be6b
--- /dev/null
+++ b/subprojects/extensions-app/data/icons/hicolor/scalable/apps/org.gnome.Extensions.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="10.23" y1="87.43" x2="133.236" y2="88.679" gradientTransform="translate(-8 -16)"><stop offset="0" stop-color="#208757"/><stop offset=".077" stop-color="#2ec27e"/><stop offset=".147" stop-color="#208a5a"/><stop offset=".198" stop-color="#26a269"/><stop offset=".364" stop-color="#1c774d"/><stop offset=".407" stop-color="#26a269"/><stop offset=".493" stop-color="#26a269"/><stop offset=".576" stop-color="#26a269"/><stop offset=".606" stop-color="#2ec27e"/><stop offset=".681" stop-color="#26a269"/><stop offset=".784" stop-color="#1d7a4e"/><stop offset=".945" stop-color="#28ab6f"/><stop offset="1" stop-color="#48d493"/></linearGradient></defs><path d="M21.965 18.363v4l2.039 17.75c.418 3.637 1.055 7.282.844 10.938-.106 1.828-.465 3.668-1.309 5.293-.426.812-.969 1.57-1.644 2.191-.676.621-1.485 1.106-2.364 1.356-1.047.297-2.156.261-3.238.164-1.094-.098-2.227-.282-3.137-.903-.394-.273-.73-.617-1.082-.945-.351-.328-.726-.645-1.168-.832-.691-.297-1.492-.254-2.207-.023-.715.23-1.355.64-1.972 1.066-1.188.824-2.325 1.758-3.184 2.922-1.734 2.344-2.203 5.46-1.711 8.336.367 2.152 1.258 4.238 2.688 5.89.722.836 1.582 1.563 2.566 2.063.98.504 2.09.773 3.195.71 1.446-.085 2.79-.73 4.114-1.327 1.32-.594 2.718-1.16 4.168-1.067a5.113 5.113 0 012.332.758 5.958 5.958 0 011.773 1.707c.848 1.242 1.234 2.742 1.531 4.219 1.832 9.133.778 18.82-2.98 27.344v4c6.812 2.347 13.8 4.199 20.886 5.535 2.356.441 4.801.824 7.13.246 1.163-.29 2.28-.832 3.148-1.66a5.39 5.39 0 001.441-2.34c.266-.887.278-1.828.254-2.754L54 110c-.426.09-.875.059-1.285-.082a2.751 2.751 0 01-1.16-.8 3.768 3.768 0 01-.7-1.231c-.316-.895-.351-1.864-.355-2.813-.008-1.57.063-3.187.684-4.629.629-1.465 1.793-2.652 3.117-3.535 2.64-1.758 5.898-2.375 9.07-2.414 2.91-.035 5.918.414 8.387 1.953 1.238.77 2.316 1.809 3.043 3.07.722 1.262 1.078 2.739.953 4.188-.098 1.117-.477 2.2-1.02 3.18A9.862 9.862 0 0172 110c.012.996.02 1.992.02 2.988.003.555.003 1.114.07 1.664.07.551.21 1.098.476 1.586.383.7 1.012 1.239 1.692 1.657.68.414 1.422.722 2.176.984 4.207 1.46 8.793 1.562 13.203.922a40.756 40.756 0 0015.86-5.88l-.005-4.003a60.146 60.146 0 01-3.433-25.465c.125-1.512.312-3.039.894-4.441.29-.7.68-1.364 1.18-1.93.504-.566 1.12-1.039 1.82-1.332.82-.344 1.727-.434 2.613-.367.887.07 1.75.297 2.598.57 1.688.547 3.344 1.297 5.117 1.418 1.492.098 2.996-.266 4.332-.937a9.894 9.894 0 004.016-3.715 9.91 9.91 0 001.418-5.285l.176-2.258a16.406 16.406 0 00-3.075-6.477c-.785-.996-1.707-1.914-2.835-2.496a5.247 5.247 0 00-1.805-.558 4.18 4.18 0 00-1.875.214c-.988.364-1.774 1.125-2.703 1.614-1.032.543-2.203.738-3.364.824-1.148.086-2.324.07-3.425-.274-1.48-.464-2.743-1.52-3.602-2.812-.855-1.293-1.332-2.805-1.59-4.328-.574-3.399-.117-6.871.266-10.297a227.56 227.56 0 001.34-19.973 497.333 497.333 0 01-21.118-5.117c-1.355-.36-2.718-.726-4.113-.894-1.394-.165-2.836-.118-4.168.332a7.108 7.108 0 00-2.93 1.894v4c0 .414.016.828.11 1.235.094.402.258.789.445 1.156.371.738.82 1.433 1.313 2.097.98 1.329 2.136 2.563 2.765 4.094.688 1.676.668 3.606.07 5.317-.597 1.707-1.75 3.203-3.179 4.312-1.43 1.113-3.133 1.852-4.895 2.262-1.765.414-3.593.508-5.402.406-2.926-.164-5.906-.875-8.25-2.633-1.172-.883-2.164-2.015-2.793-3.34-.625-1.324-.879-2.828-.668-4.277.293-1.973 1.399-3.71 2.45-5.406.421-.684.831-1.38 1.078-2.14.242-.763.296-1.571.308-2.372L54.633 18a1.89 1.89 0 01-1.695.777c-.516-.047-.989-.3-1.41-.601-.419-.301-.801-.653-1.231-.938-.871-.578-1.914-.855-2.957-.933-1.043-.075-2.09.039-3.125.191-2.403.356-4.77.91-7.176 1.266-2.402.351-4.832.46-7.262.496zm0 0" fill="url(#a)"/><path d="M21.965 18.363l2.039 17.746c.426 3.715 1.16 7.414 1.094 11.149-.035 1.867-.34 3.77-1.239 5.41-.449.816-1.047 1.562-1.785 2.133-.738.574-1.617.972-2.543 1.09-1.504.195-2.996-.344-4.379-.961-1.379-.621-2.75-1.344-4.246-1.555-1.465-.21-2.984.094-4.293.777-1.308.688-2.406 1.746-3.207 2.989-1.601 2.484-1.972 5.605-1.574 8.535.3 2.219 1.05 4.433 2.512 6.133.73.847 1.633 1.558 2.656 2.011 1.023.457 2.168.657 3.281.52 1.54-.192 2.926-1 4.324-1.676.704-.336 1.422-.644 2.176-.828.754-.184 1.551-.238 2.309-.066.82.183 1.566.628 2.164 1.218.601.586 1.05 1.313 1.375 2.086.64 1.551.762 3.258.84 4.934a123.2 123.2 0 01-2.25 29.965 133.42 133.42 0 0020.886 5.53c2.352.427 4.793.782 7.11.196 1.16-.293 2.27-.828 3.152-1.633.883-.804 1.528-1.89 1.711-3.074.133-.879.012-1.789-.25-2.64-.262-.852-.668-1.657-1.101-2.438-.868-1.559-1.887-3.086-2.227-4.84-.3-1.558-.023-3.207.676-4.633.703-1.425 1.816-2.629 3.12-3.535 2.614-1.804 5.9-2.383 9.075-2.41 2.91-.023 5.918.414 8.39 1.953 1.235.77 2.313 1.809 3.04 3.07.73 1.258 1.097 2.743.953 4.188-.117 1.113-.524 2.18-1.024 3.18-.5 1.004-1.093 1.957-1.609 2.949-.512.996-.95 2.047-1.11 3.152-.156 1.11-.019 2.29.555 3.25.41.684 1.02 1.23 1.696 1.649.675.422 1.418.722 2.168.992a30.23 30.23 0 0015.183 1.332 30.128 30.128 0 0013.88-6.293l-2.587-19.371c-.453-3.406-1.14-6.813-1.047-10.246.043-1.719.34-3.461 1.141-4.98a6.796 6.796 0 011.578-2.02 5.338 5.338 0 012.262-1.18c.8-.183 1.637-.164 2.441-.02.809.145 1.59.41 2.352.708 1.527.597 3.023 1.34 4.648 1.558 1.5.203 3.055-.062 4.426-.699 1.375-.64 2.566-1.648 3.469-2.863 1.808-2.434 2.418-5.625 2.047-8.63-.305-2.448-1.258-4.862-2.953-6.655-.848-.895-1.875-1.63-3.016-2.098-1.14-.473-2.395-.684-3.621-.563-1.637.157-3.153.875-4.688 1.461-.765.297-1.55.559-2.355.707-.809.149-1.649.176-2.45-.004-1.214-.27-2.296-1.007-3.081-1.972-.786-.965-1.286-2.14-1.547-3.36-.524-2.43-.125-4.953.23-7.414a199.18 199.18 0 002-24.914 119.325 119.325 0 00-22.3-4.867c-2.329-.281-4.75-.48-6.97.274-1.113.378-2.152 1-2.933 1.875-.781.87-1.289 2.011-1.312 3.183-.02.824.199 1.645.543 2.395.347.75.812 1.441 1.312 2.097 1 1.317 2.148 2.559 2.773 4.09.688 1.676.676 3.606.082 5.32-.593 1.711-1.746 3.207-3.175 4.32-1.434 1.114-3.137 1.852-4.903 2.263-1.765.41-3.593.5-5.406.394-2.918-.172-5.894-.89-8.23-2.652-1.168-.88-2.157-2.016-2.79-3.332-.632-1.317-.906-2.82-.69-4.266.296-1.965 1.437-3.68 2.398-5.418.48-.871.925-1.77 1.199-2.723.277-.953.379-1.976.168-2.945-.219-1.016-.774-1.941-1.512-2.672-.738-.726-1.652-1.261-2.629-1.617-1.949-.71-4.078-.707-6.148-.586a58.229 58.229 0 00-22.254 5.867zm0 0" fill="#33d17a"/></svg> \ No newline at end of file
diff --git a/subprojects/extensions-app/data/icons/hicolor/symbolic/apps/org.gnome.Extensions-symbolic.svg b/subprojects/extensions-app/data/icons/hicolor/symbolic/apps/org.gnome.Extensions-symbolic.svg
new file mode 100644
index 0000000..4208a4d
--- /dev/null
+++ b/subprojects/extensions-app/data/icons/hicolor/symbolic/apps/org.gnome.Extensions-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M6.5 1.031c-.371 0-.742-.035-1.11.016-.367.05-.73.203-.972.476-.125.141-.215.309-.266.485-.047.18-.054.367-.02.55.032.184.102.356.192.516.09.164.203.309.317.457L5 4H2a1.8 1.8 0 00-.41.035.791.791 0 00-.36.195.791.791 0 00-.195.36C1 4.723 1 4.863 1 5v2.75l.77-.344c.265-.117.542-.23.832-.242.289-.016.586.074.812.254.227.18.383.441.465.723.082.277.101.57.121.859.02.316.04.637-.016.95-.058.312-.199.616-.43.831a1.264 1.264 0 01-.874.32c-.317-.007-.618-.128-.91-.257L1 10.5V14c0 .137.004.277.035.41a.791.791 0 00.195.36c.098.097.227.16.36.195.133.035.273.035.41.035h3l-.328-.68c-.14-.293-.274-.597-.29-.922-.015-.32.095-.652.31-.894.214-.242.523-.39.84-.453.316-.067.644-.059.968-.059.324 0 .652-.008.969.059.316.062.625.21.84.453.214.242.324.574.308.894-.015.325-.148.63-.289.922L8 15h3a1.8 1.8 0 00.41-.035.791.791 0 00.36-.195.791.791 0 00.195-.36C12 14.277 12 14.137 12 14v-3.563l.703.297c.29.125.59.239.902.246.313.004.63-.101.864-.308.238-.203.386-.496.46-.8C15 9.565 15 9.25 15 8.937c0-.313 0-.63-.07-.934-.075-.305-.223-.598-.461-.8a1.288 1.288 0 00-.864-.31c-.312.008-.613.122-.902.247L12 7.437V5a1.8 1.8 0 00-.035-.41.791.791 0 00-.195-.36.791.791 0 00-.36-.195C11.277 4 11.137 4 11 4H8l.36-.469c.113-.148.226-.293.316-.457.09-.16.16-.332.191-.515a1.248 1.248 0 00-.02-.551 1.256 1.256 0 00-.265-.485c-.242-.273-.605-.425-.973-.476-.367-.05-.738-.016-1.109-.016zm0 0" fill="#474747"/></svg> \ No newline at end of file
diff --git a/subprojects/extensions-app/data/icons/meson.build b/subprojects/extensions-app/data/icons/meson.build
new file mode 100644
index 0000000..eff6e4b
--- /dev/null
+++ b/subprojects/extensions-app/data/icons/meson.build
@@ -0,0 +1 @@
+install_subdir('hicolor', install_dir: icondir)
diff --git a/subprojects/extensions-app/data/meson.build b/subprojects/extensions-app/data/meson.build
new file mode 100644
index 0000000..4f24267
--- /dev/null
+++ b/subprojects/extensions-app/data/meson.build
@@ -0,0 +1,50 @@
+gnome.compile_resources(
+ app_id + '.data',
+ configure_file(
+ input: base_id + '.data.gresource.xml.in',
+ output: app_id + '.data.gresource.xml',
+ configuration: {'profile': '/'.join(profile.split('.')) },
+ ),
+ gresource_bundle: true,
+ install: true,
+ install_dir: pkgdatadir
+)
+
+desktop_file = app_id + '.desktop'
+desktopconf = configuration_data()
+# We substitute in bindir so it works as an autostart
+# file when built in a non-system prefix
+desktopconf.set('bindir', bindir)
+desktopconf.set('app_id', app_id)
+desktopconf.set('prgname', prgname)
+
+i18n.merge_file(
+ input: configure_file(
+ input: base_id + '.desktop.in.in',
+ output: desktop_file + '.in',
+ configuration: desktopconf
+ ),
+ output: desktop_file,
+ po_dir: po_dir,
+ install: true,
+ install_dir: desktopdir,
+ type: 'desktop'
+)
+
+if (desktop_file_validate.found())
+ test('Validating ' + desktop_file,
+ desktop_file_validate,
+ args: [desktop_file],
+ workdir: meson.current_build_dir()
+ )
+endif
+
+configure_file(
+ input: base_id + '.service.in',
+ output: app_id + '.service',
+ configuration: desktopconf,
+ install_dir: servicedir,
+)
+
+subdir('icons')
+subdir('metainfo')
diff --git a/subprojects/extensions-app/data/metainfo/extensions-main.png b/subprojects/extensions-app/data/metainfo/extensions-main.png
new file mode 100644
index 0000000..7d3de2b
--- /dev/null
+++ b/subprojects/extensions-app/data/metainfo/extensions-main.png
Binary files differ
diff --git a/subprojects/extensions-app/data/metainfo/extensions-remove.png b/subprojects/extensions-app/data/metainfo/extensions-remove.png
new file mode 100644
index 0000000..d54e201
--- /dev/null
+++ b/subprojects/extensions-app/data/metainfo/extensions-remove.png
Binary files differ
diff --git a/subprojects/extensions-app/data/metainfo/extensions-update.png b/subprojects/extensions-app/data/metainfo/extensions-update.png
new file mode 100644
index 0000000..4b91cd8
--- /dev/null
+++ b/subprojects/extensions-app/data/metainfo/extensions-update.png
Binary files differ
diff --git a/subprojects/extensions-app/data/metainfo/meson.build b/subprojects/extensions-app/data/metainfo/meson.build
new file mode 100644
index 0000000..a19bfa8
--- /dev/null
+++ b/subprojects/extensions-app/data/metainfo/meson.build
@@ -0,0 +1,16 @@
+metainfo = app_id + '.metainfo.xml'
+i18n.merge_file(
+ input: base_id + '.metainfo.xml.in',
+ output: metainfo,
+ po_dir: po_dir,
+ install: true,
+ install_dir: metainfodir
+)
+
+if (appstream_util.found())
+ test('Validating ' + metainfo,
+ appstream_util,
+ args: ['validate', '--nonet', metainfo],
+ workdir: meson.current_build_dir()
+ )
+endif
diff --git a/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in b/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in
new file mode 100644
index 0000000..8ce6332
--- /dev/null
+++ b/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component type="desktop-application">
+ <id>org.gnome.Extensions</id>
+
+ <name>Extensions</name>
+ <summary>Manage your GNOME Extensions</summary>
+
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-2.0-or-later</project_license>
+
+ <url type="homepage">https://gitlab.gnome.org/GNOME/gnome-shell/-/tree/HEAD/subprojects/extensions-app</url>
+ <url type="bugtracker">https://gitlab.gnome.org/GNOME/gnome-shell/issues/new</url>
+ <url type="donation">http://www.gnome.org/friends/</url>
+ <url type="translate">https://wiki.gnome.org/TranslationProject</url>
+
+ <project_group>GNOME</project_group>
+ <developer_name>The GNOME Project</developer_name>
+
+ <launchable type="desktop-id">org.gnome.Extensions.desktop</launchable>
+
+ <kudos>
+ <kudo>HiDpiIcon</kudo>
+ <kudo>HighContrast</kudo>
+ <kudo>ModernToolkit</kudo>
+ </kudos>
+
+ <recommends>
+ <control>pointing</control>
+ <control>keyboard</control>
+ <control>touch</control>
+ </recommends>
+
+ <content_rating type="oars-1.0"/>
+
+ <description>
+ <p>
+ GNOME Extensions handles updating extensions, configuring extension preferences and removing or disabling unwanted extensions.
+ </p>
+ </description>
+
+ <releases>
+ <release version="43.9" date="2023-09-16"/>
+ <release version="43.8" date="2023-08-22"/>
+ <release version="43.7" date="2023-07-04"/>
+ <release version="43.6" date="2023-06-03"/>
+ <release version="43.5" date="2023-04-24"/>
+ <release version="43.4" date="2023-03-19"/>
+ <release version="43.3" date="2023-02-13"/>
+ <release version="43.2" date="2022-12-06"/>
+ <release version="43.1" date="2022-10-22"/>
+ <release version="43.0" date="2022-09-17">
+ <p>Modernize About window</p>
+ </release>
+ </releases>
+
+ <screenshots>
+ <screenshot type="default">
+ <image>https://gitlab.gnome.org/GNOME/gnome-shell/raw/HEAD/subprojects/extensions-app/data/metainfo/extensions-main.png</image>
+ </screenshot>
+ <screenshot>
+ <image>https://gitlab.gnome.org/GNOME/gnome-shell/raw/HEAD/subprojects/extensions-app/data/metainfo/extensions-update.png</image>
+ </screenshot>
+ <screenshot>
+ <image>https://gitlab.gnome.org/GNOME/gnome-shell/raw/HEAD/subprojects/extensions-app/data/metainfo/extensions-remove.png</image>
+ </screenshot>
+ </screenshots>
+</component>
diff --git a/subprojects/extensions-app/data/org.gnome.Extensions.data.gresource.xml.in b/subprojects/extensions-app/data/org.gnome.Extensions.data.gresource.xml.in
new file mode 100644
index 0000000..ca04c08
--- /dev/null
+++ b/subprojects/extensions-app/data/org.gnome.Extensions.data.gresource.xml.in
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/Extensions@profile@">
+ <file alias="style.css">css/style.css</file>
+ </gresource>
+
+ <gresource prefix="/org/gnome/Extensions">
+ <file>dbus-interfaces/org.gnome.Shell.Extensions.xml</file>
+
+ <file>ui/extension-row.ui</file>
+ <file>ui/extensions-window.ui</file>
+ </gresource>
+</gresources>
diff --git a/subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in b/subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in
new file mode 100644
index 0000000..b68f5ff
--- /dev/null
+++ b/subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=Extensions
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=@app_id@
+Comment=Configure GNOME Shell Extensions
+Exec=@bindir@/@prgname@ --gapplication-service
+DBusActivatable=true
+Categories=GNOME;GTK;Utility;
+OnlyShowIn=GNOME;
diff --git a/subprojects/extensions-app/data/org.gnome.Extensions.service.in b/subprojects/extensions-app/data/org.gnome.Extensions.service.in
new file mode 100644
index 0000000..2150999
--- /dev/null
+++ b/subprojects/extensions-app/data/org.gnome.Extensions.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=@app_id@
+Exec=@bindir@/@prgname@
diff --git a/subprojects/extensions-app/data/ui/extension-row.ui b/subprojects/extensions-app/data/ui/extension-row.ui
new file mode 100644
index 0000000..37acb68
--- /dev/null
+++ b/subprojects/extensions-app/data/ui/extension-row.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="ExtensionRow" parent="GtkListBoxRow">
+ <style>
+ <class name="extension"/>
+ </style>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="hexpand">true</property>
+ <style>
+ <class name="information"/>
+ </style>
+ <child>
+ <object class="GtkBox">
+ <style>
+ <class name="header"/>
+ </style>
+ <child>
+ <object class="GtkLabel" id="nameLabel">
+ <property name="xalign">0</property>
+ <style>
+ <class name="title"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="versionLabel">
+ <property name="visible">false</property>
+ <property name="xalign">0</property>
+ <property name="yalign">1</property>
+ <style>
+ <class name="caption"/>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <style>
+ <class name="status"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="errorIcon">
+ <property name="visible">false</property>
+ <property name="icon-name">dialog-error-symbolic</property>
+ <property name="tooltip-text" translatable="yes">The extension had an error</property>
+ <style>
+ <class name="error"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="updatesIcon">
+ <property name="visible">false</property>
+ <property name="icon-name">software-update-available-symbolic</property>
+ <property name="tooltip-text" translatable="yes">The extension can be updated</property>
+ <style>
+ <class name="warning"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="descriptionLabel">
+ <property name="xalign">0</property>
+ <property name="ellipsize">end</property>
+ <style>
+ <class name="subtitle"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="errorLabel">
+ <property name="visible">false</property>
+ <property name="selectable">true</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="caption"/>
+ <class name="error"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="switch">
+ <property name="valign">center</property>
+ <property name="action-name">row.enabled</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCenterBox" id="actionsBox">
+ <style>
+ <class name="actions"/>
+ </style>
+ <child type="start">
+ <object class="GtkBox">
+ <child>
+ <object class="GtkButton" id="websiteButton">
+ <property name="label" translatable="yes">Website</property>
+ <property name="action-name">row.show-url</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="prefsButton">
+ <property name="visible"
+ bind-source="prefsButton"
+ bind-property="sensitive"
+ bind-flags="sync-create"/>
+ <property name="label" translatable="yes">Settings</property>
+ <property name="action-name">row.show-prefs</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton" id="removeButton">
+ <property name="visible"
+ bind-source="removeButton"
+ bind-property="sensitive"
+ bind-flags="sync-create"/>
+ <property name="label" translatable="yes">Remove…</property>
+ <property name="action-name">row.uninstall</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/subprojects/extensions-app/data/ui/extensions-window.ui b/subprojects/extensions-app/data/ui/extensions-window.ui
new file mode 100644
index 0000000..88e0f11
--- /dev/null
+++ b/subprojects/extensions-app/data/ui/extensions-window.ui
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <menu id="primary-menu">
+ <section>
+ <item>
+ <attribute name="label" translatable="yes">Help</attribute>
+ <attribute name="action">win.show-help</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">About Extensions</attribute>
+ <attribute name="action">win.show-about</attribute>
+ </item>
+ </section>
+ </menu>
+ <template class="ExtensionsWindow" parent="GtkApplicationWindow">
+ <property name="default-width">800</property>
+ <property name="default-height">500</property>
+ <property name="title" translatable="yes">Extensions</property>
+ <child type="titlebar">
+ <object class="GtkHeaderBar">
+ <child type="end">
+ <object class="GtkMenuButton" id="menuButton">
+ <property name="receives-default">True</property>
+ <property name="menu-model">primary-menu</property>
+ <property name="icon-name">open-menu-symbolic</property>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkToggleButton" id="searchButton">
+ <property name="receives-default">True</property>
+ <property name="icon-name">edit-find-symbolic</property>
+ <child>
+ <object class="GtkShortcutController">
+ <property name='scope'>global</property>
+ <child>
+ <object class='GtkShortcut'>
+ <property name='trigger'>&lt;Control&gt;f</property>
+ <property name='action'>activate</property>
+ </object>
+ </child>
+ <child>
+ <object class='GtkShortcut'>
+ <property name='trigger'>&lt;Control&gt;s</property>
+ <property name='action'>activate</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkSearchBar" id="searchBar">
+ <property name="key-capture-widget">ExtensionsWindow</property>
+ <property name="search-mode-enabled"
+ bind-source="searchButton"
+ bind-property="active"
+ bind-flags="bidirectional"/>
+ <child>
+ <object class="GtkSearchEntry" id="searchEntry">
+ <property name="max-width-chars">35</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="mainStack">
+ <property name="transition-type">crossfade</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkStackPage">
+ <property name="name">main</property>
+ <property name="child">
+ <object class="AdwPreferencesPage">
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Extensions</property>
+ <property name="subtitle" translatable="yes">Extensions can cause performance and stability issues. Disable extensions if you encounter problems with your system.</property>
+ <property name="activatable-widget">enabledSwitch</property>
+ <child>
+ <object class="GtkSwitch" id="enabledSwitch">
+ <property name="action-name">win.user-extensions-enabled</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup" id="userGroup">
+ <property name="title" translatable="yes">Manually Installed</property>
+ <property name="description" translatable="yes">To find and add extensions, visit &lt;a href="https://extensions.gnome.org"&gt;extensions.gnome.org&lt;/a&gt;.</property>
+ <child>
+ <object class="GtkListBox" id="userList">
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="boxed-list"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup" id="systemGroup">
+ <property name="title" translatable="yes">Built-In</property>
+ <child>
+ <object class="GtkListBox" id="systemList">
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="boxed-list"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStackPage">
+ <property name="name">placeholder</property>
+ <property name="child">
+ <object class="AdwStatusPage">
+ <property name="icon-name">org.gnome.Extensions-symbolic</property>
+ <property name="title" translatable="yes">No Installed Extensions</property>
+ <property name="description" translatable="yes">To find and add extensions, visit &lt;a href="https://extensions.gnome.org"&gt;extensions.gnome.org&lt;/a&gt;.</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStackPage">
+ <property name="name">noshell</property>
+ <property name="child">
+ <object class="AdwStatusPage">
+ <property name="title" translatable="yes">Something’s gone wrong</property>
+ <property name="description" translatable="yes">We’re very sorry, but it was not possible to get the list of installed extensions. Make sure you are logged into GNOME and try again.</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkActionBar" id="updatesBar">
+ <property name="revealed">False</property>
+ <child>
+ <object class="GtkImage">
+ <property name="pixel-size">24</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="icon-name">software-update-available-symbolic</property>
+ <style>
+ <class name="warning"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Extension Updates Ready</property>
+ <style>
+ <class name="heading"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="updatesLabel">
+ <property name="halign">start</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton">
+ <property name="label" translatable="yes">Log Out…</property>
+ <property name="valign">center</property>
+ <property name="action-name">win.logout</property>
+ <property name="receives-default">True</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/subprojects/extensions-app/generate-translations.sh b/subprojects/extensions-app/generate-translations.sh
new file mode 100755
index 0000000..591eb76
--- /dev/null
+++ b/subprojects/extensions-app/generate-translations.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/bash
+
+cd $(dirname $0)
+
+sed -e '/subprojects\/extensions-app/!d' \
+ -e 's:subprojects/extensions-app/::' ../../po/POTFILES.in > po/POTFILES.in
+
+for l in $(<po/LINGUAS)
+do
+ cp ../../po/$l.po po/$l.po
+done
+
+builddir=$(mktemp -d -p.)
+
+meson setup $builddir
+meson compile -C $builddir gnome-extensions-app-pot
+meson compile -C $builddir gnome-extensions-app-update-po
+
+rm -rf $builddir
diff --git a/subprojects/extensions-app/js/gnome-extensions-app.in b/subprojects/extensions-app/js/gnome-extensions-app.in
new file mode 100644
index 0000000..dc6d627
--- /dev/null
+++ b/subprojects/extensions-app/js/gnome-extensions-app.in
@@ -0,0 +1,2 @@
+#!/bin/sh
+@gjs@ @pkgdatadir@/@app_id@ "$@"
diff --git a/subprojects/extensions-app/js/main.js b/subprojects/extensions-app/js/main.js
new file mode 100644
index 0000000..792cc9e
--- /dev/null
+++ b/subprojects/extensions-app/js/main.js
@@ -0,0 +1,548 @@
+/* exported main */
+imports.gi.versions.Adw = '1';
+imports.gi.versions.Gtk = '4.0';
+
+const Gettext = imports.gettext;
+const Package = imports.package;
+const { Adw, GLib, Gio, GObject, Gtk, Shew } = imports.gi;
+
+Package.initFormat();
+
+const ExtensionUtils = imports.misc.extensionUtils;
+
+const { ExtensionState, ExtensionType } = ExtensionUtils;
+
+const GnomeShellIface = loadInterfaceXML('org.gnome.Shell.Extensions');
+const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
+
+Gio._promisify(Gio.DBusConnection.prototype, 'call');
+Gio._promisify(Shew.WindowExporter.prototype, 'export');
+
+function loadInterfaceXML(iface) {
+ const uri = `resource:///org/gnome/Extensions/dbus-interfaces/${iface}.xml`;
+ const f = Gio.File.new_for_uri(uri);
+
+ try {
+ let [ok_, bytes] = f.load_contents(null);
+ return new TextDecoder().decode(bytes);
+ } catch (e) {
+ log(`Failed to load D-Bus interface ${iface}`);
+ }
+
+ return null;
+}
+
+function toggleState(action) {
+ let state = action.get_state();
+ action.change_state(new GLib.Variant('b', !state.get_boolean()));
+}
+
+var Application = GObject.registerClass(
+class Application extends Adw.Application {
+ _init() {
+ GLib.set_prgname('gnome-extensions-app');
+ super._init({ application_id: Package.name });
+
+ this.connect('window-removed', (a, window) => window.run_dispose());
+ }
+
+ get shellProxy() {
+ return this._shellProxy;
+ }
+
+ vfunc_activate() {
+ this._shellProxy.CheckForUpdatesAsync().catch(logError);
+ this._window.present();
+ }
+
+ vfunc_startup() {
+ super.vfunc_startup();
+
+ this.add_action_entries(
+ [{
+ name: 'quit',
+ activate: () => this._window.close(),
+ }]);
+
+ this.set_accels_for_action('app.quit', ['<Primary>q']);
+
+ this._shellProxy = new GnomeShellProxy(Gio.DBus.session,
+ 'org.gnome.Shell.Extensions', '/org/gnome/Shell/Extensions');
+
+ this._window = new ExtensionsWindow({ application: this });
+ }
+});
+
+var ExtensionsWindow = GObject.registerClass({
+ GTypeName: 'ExtensionsWindow',
+ Template: 'resource:///org/gnome/Extensions/ui/extensions-window.ui',
+ InternalChildren: [
+ 'userGroup',
+ 'userList',
+ 'systemGroup',
+ 'systemList',
+ 'mainStack',
+ 'searchBar',
+ 'searchButton',
+ 'searchEntry',
+ 'updatesBar',
+ 'updatesLabel',
+ ],
+}, class ExtensionsWindow extends Gtk.ApplicationWindow {
+ _init(params) {
+ super._init(params);
+
+ this._updatesCheckId = 0;
+
+ this._exporter = new Shew.WindowExporter({ window: this });
+ this._exportedHandle = '';
+
+ this.add_action_entries(
+ [{
+ name: 'show-about',
+ activate: () => this._showAbout(),
+ }, {
+ name: 'logout',
+ activate: () => this._logout(),
+ }, {
+ name: 'user-extensions-enabled',
+ state: 'false',
+ change_state: (a, state) => {
+ this._shellProxy.UserExtensionsEnabled = state.get_boolean();
+ },
+ }]);
+
+ this._searchTerms = [];
+ this._searchEntry.connect('search-changed', () => {
+ const { text } = this._searchEntry;
+ if (text === '')
+ this._searchTerms = [];
+ else
+ [this._searchTerms] = GLib.str_tokenize_and_fold(text, null);
+
+ this._userList.invalidate_filter();
+ this._systemList.invalidate_filter();
+ });
+
+ this._userList.set_sort_func(this._sortList.bind(this));
+ this._userList.set_filter_func(this._filterList.bind(this));
+ this._userList.set_placeholder(new Gtk.Label({
+ label: _('No Matches'),
+ margin_start: 12,
+ margin_end: 12,
+ margin_top: 12,
+ margin_bottom: 12,
+ }));
+ this._userList.connect('row-activated', (_list, row) => row.activate());
+
+ this._systemList.set_sort_func(this._sortList.bind(this));
+ this._systemList.set_filter_func(this._filterList.bind(this));
+ this._systemList.set_placeholder(new Gtk.Label({
+ label: _('No Matches'),
+ margin_start: 12,
+ margin_end: 12,
+ margin_top: 12,
+ margin_bottom: 12,
+ }));
+ this._systemList.connect('row-activated', (_list, row) => row.activate());
+
+ this._shellProxy.connectSignal('ExtensionStateChanged',
+ this._onExtensionStateChanged.bind(this));
+
+ this._shellProxy.connect('g-properties-changed',
+ this._onUserExtensionsEnabledChanged.bind(this));
+ this._onUserExtensionsEnabledChanged();
+
+ this._scanExtensions();
+ }
+
+ get _shellProxy() {
+ return this.application.shellProxy;
+ }
+
+ uninstall(uuid) {
+ let row = this._findExtensionRow(uuid);
+
+ let dialog = new Gtk.MessageDialog({
+ transient_for: this,
+ modal: true,
+ text: _('Remove “%s”?').format(row.name),
+ secondary_text: _('If you remove the extension, you need to return to download it if you want to enable it again'),
+ });
+
+ dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
+ dialog.add_button(_('Remove'), Gtk.ResponseType.ACCEPT)
+ .get_style_context().add_class('destructive-action');
+
+ dialog.connect('response', (dlg, response) => {
+ if (response === Gtk.ResponseType.ACCEPT)
+ this._shellProxy.UninstallExtensionAsync(uuid).catch(logError);
+ dialog.destroy();
+ });
+ dialog.present();
+ }
+
+ async openPrefs(uuid) {
+ if (!this._exportedHandle) {
+ try {
+ this._exportedHandle = await this._exporter.export();
+ } catch (e) {
+ log(`Failed to export window: ${e.message}`);
+ }
+ }
+
+ this._shellProxy.OpenExtensionPrefsAsync(uuid,
+ this._exportedHandle,
+ {modal: new GLib.Variant('b', true)}).catch(logError);
+ }
+
+ _showAbout() {
+ let aboutWindow = new Adw.AboutWindow({
+ developers: [
+ 'Florian Müllner <fmuellner@gnome.org>',
+ 'Jasper St. Pierre <jstpierre@mecheye.net>',
+ 'Didier Roche <didrocks@ubuntu.com>',
+ 'Romain Vigier <contact@romainvigier.fr>',
+ ],
+ designers: [
+ 'Allan Day <allanpday@gmail.com>',
+ 'Tobias Bernard <tbernard@gnome.org>',
+ ],
+ translator_credits: _('translator-credits'),
+ application_name: _('Extensions'),
+ license_type: Gtk.License.GPL_2_0,
+ application_icon: Package.name,
+ version: Package.version,
+ developer_name: _('The GNOME Project'),
+ website: 'https://apps.gnome.org/app/org.gnome.Extensions/',
+ issue_url: 'https://gitlab.gnome.org/GNOME/gnome-shell/issues/new',
+
+ transient_for: this,
+ });
+ aboutWindow.present();
+ }
+
+ _logout() {
+ this.application.get_dbus_connection().call(
+ 'org.gnome.SessionManager',
+ '/org/gnome/SessionManager',
+ 'org.gnome.SessionManager',
+ 'Logout',
+ new GLib.Variant('(u)', [0]),
+ null,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ null);
+ }
+
+ _sortList(row1, row2) {
+ return row1.name.localeCompare(row2.name);
+ }
+
+ _filterList(row) {
+ return this._searchTerms.every(
+ t => row.keywords.some(k => k.startsWith(t)));
+ }
+
+ _findExtensionRow(uuid) {
+ return [
+ ...this._userList,
+ ...this._systemList,
+ ].find(c => c.uuid === uuid);
+ }
+
+ _onUserExtensionsEnabledChanged() {
+ let action = this.lookup_action('user-extensions-enabled');
+ action.set_state(
+ new GLib.Variant('b', this._shellProxy.UserExtensionsEnabled));
+ }
+
+ _onExtensionStateChanged(proxy, senderName, [uuid, newState]) {
+ let extension = ExtensionUtils.deserializeExtension(newState);
+ let row = this._findExtensionRow(uuid);
+
+ this._queueUpdatesCheck();
+
+ // the extension's type changed; remove the corresponding row
+ // and reset the variable to null so that we create a new row
+ // below and add it to the appropriate list
+ if (row && row.type !== extension.type) {
+ row.get_parent().remove(row);
+ row = null;
+ }
+
+ if (row) {
+ if (extension.state === ExtensionState.UNINSTALLED)
+ row.get_parent().remove(row);
+ } else {
+ this._addExtensionRow(extension);
+ }
+
+ this._syncListVisibility();
+ }
+
+ async _scanExtensions() {
+ try {
+ const [extensionsMap] = await this._shellProxy.ListExtensionsAsync();
+
+ for (let uuid in extensionsMap) {
+ let extension = ExtensionUtils.deserializeExtension(extensionsMap[uuid]);
+ this._addExtensionRow(extension);
+ }
+ this._extensionsLoaded();
+ } catch (e) {
+ if (e instanceof Gio.DBusError) {
+ log(`Failed to connect to shell proxy: ${e}`);
+ this._mainStack.visible_child_name = 'noshell';
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ _addExtensionRow(extension) {
+ let row = new ExtensionRow(extension);
+
+ if (row.type === ExtensionType.PER_USER)
+ this._userList.append(row);
+ else
+ this._systemList.append(row);
+ }
+
+ _queueUpdatesCheck() {
+ if (this._updatesCheckId)
+ return;
+
+ this._updatesCheckId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT, 1, () => {
+ this._checkUpdates();
+
+ this._updatesCheckId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _syncListVisibility() {
+ this._userGroup.visible = [...this._userList].length > 1;
+ this._systemGroup.visible = [...this._systemList].length > 1;
+
+ if (this._userGroup.visible || this._systemGroup.visible)
+ this._mainStack.visible_child_name = 'main';
+ else
+ this._mainStack.visible_child_name = 'placeholder';
+ }
+
+ _checkUpdates() {
+ let nUpdates = [...this._userList].filter(c => c.hasUpdate).length;
+
+ this._updatesLabel.label = Gettext.ngettext(
+ '%d extension will be updated on next login.',
+ '%d extensions will be updated on next login.',
+ nUpdates).format(nUpdates);
+ this._updatesBar.revealed = nUpdates > 0;
+ }
+
+ _extensionsLoaded() {
+ this._syncListVisibility();
+ this._checkUpdates();
+ }
+});
+
+var ExtensionRow = GObject.registerClass({
+ GTypeName: 'ExtensionRow',
+ Template: 'resource:///org/gnome/Extensions/ui/extension-row.ui',
+ InternalChildren: [
+ 'nameLabel',
+ 'descriptionLabel',
+ 'versionLabel',
+ 'errorLabel',
+ 'errorIcon',
+ 'updatesIcon',
+ 'switch',
+ 'actionsBox',
+ ],
+}, class ExtensionRow extends Gtk.ListBoxRow {
+ _init(extension) {
+ super._init();
+
+ this._app = Gio.Application.get_default();
+ this._extension = extension;
+ this._prefsModule = null;
+
+ [this._keywords] = GLib.str_tokenize_and_fold(this.name, null);
+
+ this._actionGroup = new Gio.SimpleActionGroup();
+ this.insert_action_group('row', this._actionGroup);
+
+ let action;
+ action = new Gio.SimpleAction({
+ name: 'show-prefs',
+ enabled: this.hasPrefs,
+ });
+ action.connect('activate', () => this.get_root().openPrefs(this.uuid));
+ this._actionGroup.add_action(action);
+
+ action = new Gio.SimpleAction({
+ name: 'show-url',
+ enabled: this.url !== '',
+ });
+ action.connect('activate', () => {
+ Gio.AppInfo.launch_default_for_uri(
+ this.url, this.get_display().get_app_launch_context());
+ });
+ this._actionGroup.add_action(action);
+
+ action = new Gio.SimpleAction({
+ name: 'uninstall',
+ enabled: this.type === ExtensionType.PER_USER,
+ });
+ action.connect('activate', () => this.get_root().uninstall(this.uuid));
+ this._actionGroup.add_action(action);
+
+ action = new Gio.SimpleAction({
+ name: 'enabled',
+ state: new GLib.Variant('b', false),
+ });
+ action.connect('activate', toggleState);
+ action.connect('change-state', (a, state) => {
+ if (state.get_boolean())
+ this._app.shellProxy.EnableExtensionAsync(this.uuid).catch(logError);
+ else
+ this._app.shellProxy.DisableExtensionAsync(this.uuid).catch(logError);
+ });
+ this._actionGroup.add_action(action);
+
+ this._nameLabel.label = this.name;
+
+ const desc = this._extension.metadata.description.split('\n')[0];
+ this._descriptionLabel.label = desc;
+ this._descriptionLabel.tooltip_text = desc;
+
+ this.connect('destroy', this._onDestroy.bind(this));
+
+ this._extensionStateChangedId = this._app.shellProxy.connectSignal(
+ 'ExtensionStateChanged', (p, sender, [uuid, newState]) => {
+ if (this.uuid !== uuid)
+ return;
+
+ this._extension = ExtensionUtils.deserializeExtension(newState);
+ this._updateState();
+ });
+ this._updateState();
+ }
+
+ vfunc_activate() {
+ this._switch.mnemonic_activate(false);
+ }
+
+ get uuid() {
+ return this._extension.uuid;
+ }
+
+ get name() {
+ return this._extension.metadata.name;
+ }
+
+ get hasPrefs() {
+ return this._extension.hasPrefs;
+ }
+
+ get hasUpdate() {
+ return this._extension.hasUpdate || false;
+ }
+
+ get hasError() {
+ const { state } = this._extension;
+ return state === ExtensionState.OUT_OF_DATE ||
+ state === ExtensionState.ERROR;
+ }
+
+ get type() {
+ return this._extension.type;
+ }
+
+ get creator() {
+ return this._extension.metadata.creator || '';
+ }
+
+ get url() {
+ return this._extension.metadata.url || '';
+ }
+
+ get version() {
+ return this._extension.metadata.version || '';
+ }
+
+ get error() {
+ if (!this.hasError)
+ return '';
+
+ if (this._extension.state === ExtensionState.OUT_OF_DATE)
+ return _('The extension is incompatible with the current GNOME version');
+
+ return this._extension.error
+ ? this._extension.error : _('The extension had an error');
+ }
+
+ get keywords() {
+ return this._keywords;
+ }
+
+ _updateState() {
+ let state = this._extension.state === ExtensionState.ENABLED;
+
+ let action = this._actionGroup.lookup('enabled');
+ action.set_state(new GLib.Variant('b', state));
+ action.enabled = this._canToggle();
+
+ if (!action.enabled)
+ this._switch.active = state;
+
+ this._updatesIcon.visible = this.hasUpdate;
+ this._errorIcon.visible = this.hasError;
+
+ this._descriptionLabel.visible = !this.hasError;
+
+ this._errorLabel.label = this.error;
+ this._errorLabel.visible = this.error !== '';
+
+ this._versionLabel.label = this.version.toString();
+ this._versionLabel.visible = this.version !== '';
+ }
+
+ _onDestroy() {
+ if (!this._app.shellProxy)
+ return;
+
+ if (this._extensionStateChangedId)
+ this._app.shellProxy.disconnectSignal(this._extensionStateChangedId);
+ this._extensionStateChangedId = 0;
+ }
+
+ _canToggle() {
+ return this._extension.canChange;
+ }
+});
+
+function initEnvironment() {
+ // Monkey-patch in a "global" object that fakes some Shell utilities
+ // that ExtensionUtils depends on.
+ globalThis.global = {
+ log(...args) {
+ print(args.join(', '));
+ },
+
+ logError(s) {
+ log(`ERROR: ${s}`);
+ },
+
+ userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']),
+ };
+}
+
+function main(argv) {
+ initEnvironment();
+ Package.initGettext();
+
+ new Application().run(argv);
+}
diff --git a/subprojects/extensions-app/js/meson.build b/subprojects/extensions-app/js/meson.build
new file mode 100644
index 0000000..ce2a776
--- /dev/null
+++ b/subprojects/extensions-app/js/meson.build
@@ -0,0 +1,40 @@
+launcherconf = configuration_data()
+launcherconf.set('app_id', app_id)
+launcherconf.set('PACKAGE_NAME', package_name)
+if vcs_tag != ''
+ launcherconf.set('PACKAGE_VERSION', '@0@ (@1@)'.format(package_version, vcs_tag))
+else
+ launcherconf.set('PACKAGE_VERSION', package_version)
+endif
+launcherconf.set('prefix', prefix)
+launcherconf.set('libdir', libdir)
+launcherconf.set('pkgdatadir', pkgdatadir)
+launcherconf.set('gjs', gjs.full_path())
+
+configure_file(
+ input: prgname + '.in',
+ output: prgname,
+ configuration: launcherconf,
+ install_dir: bindir,
+ install_mode: 'rwxr-xr-x',
+)
+
+configure_file(
+ input: base_id + '.in',
+ output: app_id,
+ configuration: launcherconf,
+ install_dir: pkgdatadir,
+)
+
+gnome.compile_resources(
+ app_id + '.src',
+ configure_file(
+ input: base_id + '.src.gresource.xml.in',
+ output: app_id + '.src.gresource.xml',
+ configuration: {'profile': '/'.join(profile.split('.')) },
+ ),
+ source_dir: ['.', '../../../js'],
+ gresource_bundle: true,
+ install: true,
+ install_dir: pkgdatadir
+)
diff --git a/subprojects/extensions-app/js/misc/config.js b/subprojects/extensions-app/js/misc/config.js
new file mode 100644
index 0000000..d213b78
--- /dev/null
+++ b/subprojects/extensions-app/js/misc/config.js
@@ -0,0 +1 @@
+/* Fake module to satify import in ExtensionUtils */
diff --git a/subprojects/extensions-app/js/org.gnome.Extensions.in b/subprojects/extensions-app/js/org.gnome.Extensions.in
new file mode 100644
index 0000000..da7ab25
--- /dev/null
+++ b/subprojects/extensions-app/js/org.gnome.Extensions.in
@@ -0,0 +1,6 @@
+imports.package.start({
+ name: '@PACKAGE_NAME@',
+ version: '@PACKAGE_VERSION@',
+ prefix: '@prefix@',
+ libdir: '@libdir@',
+});
diff --git a/subprojects/extensions-app/js/org.gnome.Extensions.src.gresource.xml.in b/subprojects/extensions-app/js/org.gnome.Extensions.src.gresource.xml.in
new file mode 100644
index 0000000..330ede1
--- /dev/null
+++ b/subprojects/extensions-app/js/org.gnome.Extensions.src.gresource.xml.in
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/Extensions@profile@/js">
+ <file>main.js</file>
+
+ <file>misc/config.js</file>
+ <file>misc/extensionUtils.js</file>
+ </gresource>
+</gresources>
diff --git a/subprojects/extensions-app/logo.png b/subprojects/extensions-app/logo.png
new file mode 100644
index 0000000..8872925
--- /dev/null
+++ b/subprojects/extensions-app/logo.png
Binary files differ
diff --git a/subprojects/extensions-app/meson.build b/subprojects/extensions-app/meson.build
new file mode 100644
index 0000000..dfb28dc
--- /dev/null
+++ b/subprojects/extensions-app/meson.build
@@ -0,0 +1,90 @@
+project('gnome-extensions-app',
+ version: '43.9',
+ meson_version: '>= 0.58.0',
+ license: 'GPLv2+'
+)
+
+if get_option('profile') == 'development'
+ profile = '.Devel'
+ vcs_tag = run_command('git', 'rev-parse', '--short', '@',
+ check: false,
+ ).stdout().strip()
+else
+ profile = ''
+ vcs_tag = ''
+endif
+
+base_id = 'org.gnome.Extensions'
+app_id = base_id + profile
+prgname = 'gnome-extensions-app'
+
+gnome = import('gnome')
+i18n = import('i18n')
+
+if meson.is_subproject()
+ package_name = get_option('package_name')
+ assert(package_name != '',
+ 'package_name must be specified for subproject builds')
+
+ po_dir = join_paths(meson.current_source_dir(), '../../po')
+else
+ package_name = meson.project_name()
+ po_dir = join_paths(meson.current_source_dir(), 'po')
+endif
+
+package_version = meson.project_version()
+prefix = get_option('prefix')
+
+bindir = join_paths(prefix, get_option('bindir'))
+libdir = join_paths(prefix, get_option('libdir'))
+datadir = join_paths(prefix, get_option('datadir'))
+pkgdatadir = join_paths(datadir, package_name)
+
+desktopdir = join_paths(datadir, 'applications')
+icondir = join_paths(datadir, 'icons')
+localedir = join_paths(datadir, 'locale')
+metainfodir = join_paths(datadir, 'metainfo')
+servicedir = join_paths(datadir, 'dbus-1', 'services')
+
+gjs = find_program('gjs')
+appstream_util = find_program('appstream-util', required: false)
+desktop_file_validate = find_program('desktop-file-validate', required: false)
+
+subdir('data')
+subdir('js')
+
+if not meson.is_subproject()
+ subproject('shew',
+ default_options: [
+ 'package_name=@0@'.format(meson.project_name()),
+ ]
+ )
+
+ subdir('po')
+
+ gnome.post_install(
+ gtk_update_icon_cache: true
+ )
+
+ if appstream_util.found()
+ meson.add_dist_script('build-aux/meson/check-version.py',
+ meson.project_version(),
+ '--type=metainfo',
+ 'data/metainfo/org.gnome.Extensions.metainfo.xml.in')
+ endif
+
+ summary_dirs = {
+ 'prefix': get_option('prefix'),
+ 'bindir': get_option('bindir'),
+ 'libdir': get_option('bindir'),
+ 'datadir': get_option('datadir'),
+ }
+
+ summary_build = {
+ 'buildtype': get_option('buildtype'),
+ 'debug': get_option('debug'),
+ }
+
+ summary(summary_dirs, section: 'Directories')
+ summary(summary_build, section: 'Build Configuration')
+endif
diff --git a/subprojects/extensions-app/meson_options.txt b/subprojects/extensions-app/meson_options.txt
new file mode 100644
index 0000000..ca2eb41
--- /dev/null
+++ b/subprojects/extensions-app/meson_options.txt
@@ -0,0 +1,12 @@
+option('package_name',
+ type: 'string',
+ description: 'The gettext domain name',
+)
+option('profile',
+ type: 'combo',
+ choices: [
+ 'default',
+ 'development'
+ ],
+ value: 'default'
+)
diff --git a/subprojects/extensions-app/po/.gitignore b/subprojects/extensions-app/po/.gitignore
new file mode 100644
index 0000000..3b2228d
--- /dev/null
+++ b/subprojects/extensions-app/po/.gitignore
@@ -0,0 +1,3 @@
+*.po
+*.pot
+POTFILES.in
diff --git a/subprojects/extensions-app/po/LINGUAS b/subprojects/extensions-app/po/LINGUAS
new file mode 120000
index 0000000..4fb83a5
--- /dev/null
+++ b/subprojects/extensions-app/po/LINGUAS
@@ -0,0 +1 @@
+../../../po/LINGUAS \ No newline at end of file
diff --git a/subprojects/extensions-app/po/meson.build b/subprojects/extensions-app/po/meson.build
new file mode 100644
index 0000000..5a1b0e2
--- /dev/null
+++ b/subprojects/extensions-app/po/meson.build
@@ -0,0 +1 @@
+i18n.gettext(package_name, preset: 'glib')
diff --git a/subprojects/extensions-app/subprojects/shew b/subprojects/extensions-app/subprojects/shew
new file mode 120000
index 0000000..50988a9
--- /dev/null
+++ b/subprojects/extensions-app/subprojects/shew
@@ -0,0 +1 @@
+../../shew/ \ No newline at end of file
diff --git a/subprojects/extensions-tool/COPYING b/subprojects/extensions-tool/COPYING
new file mode 100644
index 0000000..10926e8
--- /dev/null
+++ b/subprojects/extensions-tool/COPYING
@@ -0,0 +1,675 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/subprojects/extensions-tool/README.md b/subprojects/extensions-tool/README.md
new file mode 100644
index 0000000..dc2e2d7
--- /dev/null
+++ b/subprojects/extensions-tool/README.md
@@ -0,0 +1,23 @@
+# gnome-extensions-tool
+gnome-extensions-tool is a command line utility for managing
+GNOME Shell extensions. It is usually built as part of gnome-shell,
+but can be used as a stand-alone project as well (for example to
+create an extension bundle as part of continuous integration).
+
+Bugs should be reported to the GNOME [bug tracking system][bug-tracker].
+
+## Building
+Before the project can be built stand-alone, the po directory has
+to be populated with translations (from gnome-shell).
+
+To do that, simply run the included script:
+```sh
+$ ./generate-translations.sh
+```
+
+## License
+gnome-extensions-tool is distributed under the terms of the GNU General Public
+License, version 3 or later. See the [COPYING][license] file for details.
+
+[bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell/issues
+[license]: COPYING
diff --git a/subprojects/extensions-tool/completion/bash/gnome-extensions b/subprojects/extensions-tool/completion/bash/gnome-extensions
new file mode 100644
index 0000000..05cd039
--- /dev/null
+++ b/subprojects/extensions-tool/completion/bash/gnome-extensions
@@ -0,0 +1,91 @@
+
+# Check for bash
+[ -z "$BASH_VERSION" ] && return
+
+################################################################################
+
+__gnome_extensions() {
+ local commands="version enable disable reset info install show list create pack prefs uninstall"
+ local COMMAND=${COMP_WORDS[1]}
+
+ _init_completion -s || return
+
+ case "${COMP_CWORD}" in
+ 1)
+ COMPREPLY=($(compgen -W "help $commands" -- "$2"))
+ return 0
+ ;;
+
+ 2)
+ case "$COMMAND" in
+ help)
+ COMPREPLY=($(compgen -W "$commands" -- "$2"))
+ return 0
+ ;;
+
+ disable)
+ local list_opt=--enabled
+ ;;&
+ enable)
+ local list_opt=--disabled
+ ;;&
+ prefs)
+ local list_opt=--prefs
+ ;;&
+ uninstall)
+ local list_opt=--user
+ ;;&
+ enable|disable|info|show|prefs|reset|uninstall)
+ COMPREPLY=($(compgen -W "`gnome-extensions list $list_opt`" -- "$2"))
+ return 0
+ ;;
+ esac
+ ;;
+ esac
+
+ case "$COMMAND" in
+ create)
+ case "$prev" in
+ --template)
+ COMPREPLY=($(compgen -W "`gnome-extensions create --list-templates`" -- "$2"))
+ return 0
+ ;;
+ esac
+ ;;
+ pack)
+ case "$prev" in
+ --podir|--out-dir|-o)
+ _filedir -d
+ return 0
+ ;;
+ --schema)
+ _filedir gschema.xml
+ return 0
+ ;;
+ --extra-source)
+ _filedir
+ return 0
+ ;;
+ esac
+ ;;
+ install)
+ if [[ $cur != -* ]]
+ then
+ _filedir zip
+ return 0
+ fi
+ ;;
+ esac
+
+ # Stop if we are currently waiting for an option value
+ $split && return
+
+ # Otherwise, get the supported options for ${COMMAND} (if any)
+ COMPREPLY=($(compgen -W "$(_parse_help $1 "help $COMMAND")" -- "$2"))
+ [[ $COMPREPLY == *= ]] && compopt -o nospace
+ return 0
+}
+
+################################################################################
+
+complete -F __gnome_extensions gnome-extensions
diff --git a/subprojects/extensions-tool/generate-translations.sh b/subprojects/extensions-tool/generate-translations.sh
new file mode 100755
index 0000000..3521a44
--- /dev/null
+++ b/subprojects/extensions-tool/generate-translations.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/bash
+
+cd $(dirname $0)
+
+sed -e '/subprojects\/extensions-tool/!d' \
+ -e 's:subprojects/extensions-tool/::' ../../po/POTFILES.in > po/POTFILES.in
+
+for l in $(<po/LINGUAS)
+do
+ cp ../../po/$l.po po/$l.po
+done
+
+builddir=$(mktemp -d -p.)
+
+meson setup -Dman=False $builddir
+meson compile -C $builddir gnome-extensions-tool-pot
+meson compile -C $builddir gnome-extensions-tool-update-po
+
+rm -rf $builddir
diff --git a/subprojects/extensions-tool/man/gnome-extensions.1 b/subprojects/extensions-tool/man/gnome-extensions.1
new file mode 100644
index 0000000..d106682
--- /dev/null
+++ b/subprojects/extensions-tool/man/gnome-extensions.1
@@ -0,0 +1,297 @@
+'\" t
+.\" Title: gnome-extensions
+.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author]
+.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
+.\" Date: August 2018
+.\" Manual: User Commands
+.\" Source: GNOME-EXTENSIONS-TOOL
+.\" Language: English
+.\"
+.TH "GNOME\-EXTENSIONS" "1" "August 2018" "GNOME\-EXTENSIONS\-TOOL" "User Commands"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+gnome-extensions \- Command line tool for managing GNOME extensions
+.SH "SYNOPSIS"
+.sp
+\fBgnome\-extensions\fR help [\fICOMMAND\fR]
+.sp
+\fBgnome\-extensions\fR version
+.sp
+\fBgnome\-extensions\fR enable \fIUUID\fR
+.sp
+\fBgnome\-extensions\fR disable \fIUUID\fR
+.sp
+\fBgnome\-extensions\fR reset \fIUUID\fR
+.sp
+\fBgnome\-extensions\fR info \fIUUID\fR
+.sp
+\fBgnome\-extensions\fR show \fIUUID\fR
+.sp
+\fBgnome\-extensions\fR list [\fIOPTION\fR\&...]
+.sp
+\fBgnome\-extensions\fR prefs \fIUUID\fR
+.sp
+\fBgnome\-extensions\fR create [\fIOPTION\fR\&...]
+.sp
+\fBgnome\-extensions\fR pack [\fIOPTION\fR\&...]
+.sp
+\fBgnome\-extensions\fR install [\fIOPTION\fR\&...] \fIPACK\fR
+.sp
+\fBgnome\-extensions\fR uninstall \fIUUID\fR
+.SH "DESCRIPTION"
+.sp
+\fBgnome\-extensions\fR is a utility that makes some common GNOME extensions operations available on the command line\&.
+.SH "COMMON OPTIONS"
+.sp
+All commands except for \fBhelp\fR and \fBversion\fR handle the following options:
+.PP
+\fB\-\-quiet\fR, \fB\-q\fR
+.RS 4
+Do not print error messages
+.RE
+.SH "COMMANDS"
+.PP
+\fBhelp\fR [\fICOMMAND\fR]
+.RS 4
+Displays a short synopsis of the available commands or provides detailed help on a specific command\&.
+.RE
+.PP
+\fBversion\fR
+.RS 4
+Prints the program version\&.
+.RE
+.PP
+\fBenable\fR \fIUUID\fR
+.RS 4
+Enables the extension identified by
+\fIUUID\fR\&.
+.sp
+The command will not detect any errors from the extension itself, use the
+\fBinfo\fR
+command to confirm that the extension state is
+\fBENABLED\fR\&.
+.sp
+If the extension is already enabled, the command will do nothing\&.
+.RE
+.PP
+\fBdisable\fR \fIUUID\fR
+.RS 4
+Disables the extension identified by
+\fIUUID\fR\&.
+.sp
+If the extension is not enabled, the command will do nothing\&.
+.RE
+.PP
+\fBreset\fR \fIUUID\fR
+.RS 4
+Reset the extension identified by
+\fIUUID\fR\&.
+.sp
+The extension will be disabled in GNOME, but may be enabled by other sessions like GNOME Classic\&.
+.RE
+.PP
+\fBinfo\fR \fIUUID\fR
+.RS 4
+Show details of the extension identified by
+\fIUUID\fR, including name, description and state\&.
+.RE
+.PP
+\fBshow\fR \fIUUID\fR
+.RS 4
+Synonym of info\&.
+.RE
+.PP
+\fBlist\fR [\fIOPTION\fR\&...]
+.RS 4
+Displays a list of installed extensions\&.
+.PP
+\fBOptions\fR
+.RS 4
+.\".PP
+\fB\-\-user\fR
+.RS 4
+Include extensions installed in the user\(cqs
+\fB$HOME\fR
+.RE
+.PP
+\fB\-\-system\fR
+.RS 4
+Include extensions installed in the system
+.RE
+.PP
+\fB\-\-enabled\fR
+.RS 4
+Include enabled extensions
+.RE
+.PP
+\fB\-\-disabled\fR
+.RS 4
+Include disabled extensions
+.RE
+.PP
+\fB\-\-prefs\fR
+.RS 4
+Only include extensions with preferences
+.RE
+.PP
+\fB\-\-updates\fR
+.RS 4
+Only include extensions with pending updates
+.RE
+.PP
+\fB\-d\fR, \fB\-\-details\fR
+.RS 4
+Show some extra information for each extension
+.RE
+.RE
+.RE
+.PP
+\fBprefs\fR \fIUUID\fR
+.RS 4
+Open the preference dialog of the extension identified by
+\fIUUID\fR\&.
+.RE
+.PP
+\fBcreate\fR [\fIOPTION\fR\&...]
+.RS 4
+Creates a new extension from a template\&.
+.PP
+\fBOptions\fR
+.RS 4
+.\".PP
+\fB\-\-name\fR=\fINAME\fR
+.RS 4
+Set the user\-visible name in the extension\(cqs metadata to
+\fINAME\fR
+.RE
+.PP
+\fB\-\-description\fR=\fIDESC\fR
+.RS 4
+Set the description in the extension\(cqs metadata to
+\fIDESC\fR
+.RE
+.PP
+\fB\-\-uuid\fR=\fIUUID\fR
+.RS 4
+Set the unique extension ID in the metadata to
+\fIUUID\fR
+.RE
+.PP
+\fB\-\-template\fR=\fITEMPLATE\fR
+.RS 4
+Use
+\fITEMPLATE\fR
+as base for the new extension
+.RE
+.PP
+\fB\-i\fR, \fB\-\-interactive\fR
+.RS 4
+Prompt for any extension metadata that hasn\(cqt been provided on the command line
+.RE
+.RE
+.RE
+.PP
+\fBpack\fR [\fIOPTION\fR\&...] [\fISOURCE\-DIRECTORY\fR]
+.RS 4
+Creates an extension bundle that is suitable for publishing\&.
+.sp
+The bundle will always include the required files extension\&.js and metadata\&.json, as well as the optional stylesheet\&.css and prefs\&.js if found\&. Each additional source that should be included must be specified with
+\fB\-\-extra\-source\fR\&.
+.sp
+If the extension includes one or more GSettings schemas, they can either be placed in a schemas/ folder to be picked up automatically, or be specified with
+\fB\-\-schema\fR\&.
+.sp
+Similarily, translations are included automatically when they are located in a po/ folder, otherwise the
+\fB\-\-podir\fR
+option can be used to point to the correct directory\&. If no gettext domain is provided on the command line, the value of the
+\fBgettext\-domain\fR
+metadata field is used if it exists, and the extension UUID if not\&.
+.sp
+All files are searched in
+\fISOURCE\-DIRECTORY\fR
+if specified, or the current directory otherwise\&.
+.PP
+\fBOptions\fR
+.RS 4
+.\".PP
+\fB\-\-extra\-source\fR=\fIFILE\fR
+.RS 4
+Additional source to include in the bundle
+.RE
+.PP
+\fB\-\-schema\fR=\fISCHEMA\fR
+.RS 4
+A GSettings schema that should be compiled and included
+.RE
+.PP
+\fB\-\-podir\fR=\fIPODIR\fR
+.RS 4
+A directory with translations that should be compiled and included
+.RE
+.PP
+\fB\-\-gettext\-domain\fR=\fIDOMAIN\fR
+.RS 4
+The gettext domain to use for translations
+.RE
+.PP
+\fB\-f\fR, \fB\-\-force\fR
+.RS 4
+Overwrite an existing pack
+.RE
+.PP
+\fB\-o\fR, \fB\-\-out\-dir\fR=\fIDIRECTORY\fR
+.RS 4
+The directory where the pack should be created
+.RE
+.RE
+.RE
+.PP
+\fBinstall\fR [\fIOPTION\fR\&...] \fIPACK\fR
+.RS 4
+Installs an extension from the bundle
+\fIPACK\fR\&.
+.sp
+The command unpacks the extension files and moves them to the expected location in the user\(cqs
+\fB$HOME\fR, so that it will be loaded in the next session\&.
+.sp
+It is mainly intended for testing, not as a replacement for the extension website\&. As extensions have privileged access to the user\(cqs session, it is advised to never load extensions from untrusted sources without carefully reviewing their content\&.
+.PP
+\fBOptions\fR
+.RS 4
+.\".PP
+\fB\-\-force\fR
+.RS 4
+Override an existing extension
+.RE
+.RE
+.RE
+.PP
+\fBuninstall\fR \fIUUID\fR
+.RS 4
+Uninstalls the extension identified by
+\fIUUID\fR\&.
+.RE
+.SH "EXIT STATUS"
+.sp
+On success 0 is returned, a non\-zero failure code otherwise\&.
+.SH "BUGS"
+.sp
+The tool is part of the gnome\-shell project, and bugs should be reported in its issue tracker at \m[blue]\fBhttps://gitlab\&.gnome\&.org/GNOME/gnome\-shell/issues\fR\m[]\&.
diff --git a/subprojects/extensions-tool/man/gnome-extensions.txt b/subprojects/extensions-tool/man/gnome-extensions.txt
new file mode 100644
index 0000000..85d657b
--- /dev/null
+++ b/subprojects/extensions-tool/man/gnome-extensions.txt
@@ -0,0 +1,211 @@
+GNOME-EXTENSIONS(1)
+===================
+:man manual: User Commands
+:man source: GNOME-EXTENSIONS-TOOL
+:doctype: manpage
+:date: August 2018
+
+NAME
+----
+gnome-extensions - Command line tool for managing GNOME extensions
+
+SYNOPSIS
+--------
+*gnome-extensions* help ['COMMAND']
+
+*gnome-extensions* version
+
+*gnome-extensions* enable 'UUID'
+
+*gnome-extensions* disable 'UUID'
+
+*gnome-extensions* reset 'UUID'
+
+*gnome-extensions* info 'UUID'
+
+*gnome-extensions* show 'UUID'
+
+*gnome-extensions* list ['OPTION'...]
+
+*gnome-extensions* prefs 'UUID'
+
+*gnome-extensions* create ['OPTION'...]
+
+*gnome-extensions* pack ['OPTION'...]
+
+*gnome-extensions* install ['OPTION'...] 'PACK'
+
+*gnome-extensions* uninstall 'UUID'
+
+DESCRIPTION
+-----------
+*gnome-extensions* is a utility that makes some common GNOME extensions
+operations available on the command line.
+
+COMMON OPTIONS
+--------------
+All commands except for *help* and *version* handle the following options:
+
+*--quiet*, *-q*::
+Do not print error messages
+
+COMMANDS
+--------
+*help* ['COMMAND']::
+Displays a short synopsis of the available commands or provides
+detailed help on a specific command.
+
+*version*::
+Prints the program version.
+
+*enable* 'UUID'::
+Enables the extension identified by 'UUID'.
++
+The command will not detect any errors from the extension itself, use the
+*info* command to confirm that the extension state is *ENABLED*.
++
+If the extension is already enabled, the command will do nothing.
+
+*disable* 'UUID'::
+Disables the extension identified by 'UUID'.
++
+If the extension is not enabled, the command will do nothing.
+
+*reset* 'UUID'::
+Reset the extension identified by 'UUID'.
++
+The extension will be disabled in GNOME, but may be enabled by other sessions
+like GNOME Classic.
+
+*info* 'UUID'::
+Show details of the extension identified by 'UUID', including name,
+description and state.
+
+*show* 'UUID'::
+Synonym of info.
+
+*list* ['OPTION'...]::
+Displays a list of installed extensions.
++
+.Options
+ *--user*;;
+ Include extensions installed in the user's *$HOME*
+
+ *--system*;;
+ Include extensions installed in the system
+
+ *--enabled*;;
+ Include enabled extensions
+
+ *--disabled*;;
+ Include disabled extensions
+
+ *--prefs*;;
+ Only include extensions with preferences
+
+ *--updates*;;
+ Only include extensions with pending updates
+
+ *-d*;;
+ *--details*;;
+ Show some extra information for each extension
+
+*prefs* 'UUID'::
+Open the preference dialog of the extension identified by 'UUID'.
+
+
+*create* ['OPTION'...]::
+Creates a new extension from a template.
++
+.Options
+ *--name*='NAME':::
+ Set the user-visible name in the extension's metadata
+ to 'NAME'
+
+ *--description*='DESC':::
+ Set the description in the extension's metadata to 'DESC'
+
+ *--uuid*='UUID':::
+ Set the unique extension ID in the metadata to 'UUID'
+
+ *--template*='TEMPLATE':::
+ Use 'TEMPLATE' as base for the new extension
+
+ *-i*:::
+ *--interactive*:::
+ Prompt for any extension metadata that hasn't been provided
+ on the command line
+
+*pack* ['OPTION'...] ['SOURCE-DIRECTORY']::
+Creates an extension bundle that is suitable for publishing.
++
+The bundle will always include the required files extension.js
+and metadata.json, as well as the optional stylesheet.css and
+prefs.js if found. Each additional source that should be included
+must be specified with *--extra-source*.
++
+If the extension includes one or more GSettings schemas, they can
+either be placed in a schemas/ folder to be picked up automatically,
+or be specified with *--schema*.
++
+Similarily, translations are included automatically when they are
+located in a po/ folder, otherwise the *--podir* option can be
+used to point to the correct directory. If no gettext domain is
+provided on the command line, the value of the *gettext-domain*
+metadata field is used if it exists, and the extension UUID
+if not.
++
+All files are searched in 'SOURCE-DIRECTORY' if specified, or
+the current directory otherwise.
++
+.Options
+ *--extra-source*='FILE':::
+ Additional source to include in the bundle
+
+ *--schema*='SCHEMA':::
+ A GSettings schema that should be compiled and
+ included
+
+ *--podir*='PODIR':::
+ A directory with translations that should be
+ compiled and included
+
+ *--gettext-domain*='DOMAIN':::
+ The gettext domain to use for translations
+
+ *-f*:::
+ *--force*:::
+ Overwrite an existing pack
+
+ *-o*:::
+ *--out-dir*='DIRECTORY':::
+ The directory where the pack should be created
+
+*install* ['OPTION'...] 'PACK'::
+Installs an extension from the bundle 'PACK'.
++
+The command unpacks the extension files and moves them to
+the expected location in the user's *$HOME*, so that it
+will be loaded in the next session.
++
+It is mainly intended for testing, not as a replacement for
+the extension website. As extensions have privileged access
+to the user's session, it is advised to never load extensions
+from untrusted sources without carefully reviewing their content.
++
+.Options
+ *--force*:::
+ Override an existing extension
+
+*uninstall* 'UUID'::
+Uninstalls the extension identified by 'UUID'.
+
+
+EXIT STATUS
+-----------
+On success 0 is returned, a non-zero failure code otherwise.
+
+BUGS
+----
+The tool is part of the gnome-shell project, and bugs should be reported
+in its issue tracker at https://gitlab.gnome.org/GNOME/gnome-shell/issues.
diff --git a/subprojects/extensions-tool/man/meson.build b/subprojects/extensions-tool/man/meson.build
new file mode 100644
index 0000000..643509c
--- /dev/null
+++ b/subprojects/extensions-tool/man/meson.build
@@ -0,0 +1,7 @@
+custom_target('gnome-extensions.1',
+ input: ['gnome-extensions.txt', 'stylesheet.xsl'],
+ output: 'gnome-extensions.1',
+ command: [a2x, '-D', '@OUTDIR@', '--xsl-file', '@INPUT1@', '-f', 'manpage', '@INPUT0@'],
+ install_dir: mandir + '/man1',
+ install: true
+)
diff --git a/subprojects/extensions-tool/man/stylesheet.xsl b/subprojects/extensions-tool/man/stylesheet.xsl
new file mode 100644
index 0000000..047bd1b
--- /dev/null
+++ b/subprojects/extensions-tool/man/stylesheet.xsl
@@ -0,0 +1,27 @@
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'>
+<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"/>
+
+<xsl:template match="variablelist/title">
+ <xsl:text>.PP&#10;</xsl:text>
+ <xsl:call-template name="bold">
+ <xsl:with-param name="node" select="."/>
+ <xsl:with-param name="context" select=".."/>
+ </xsl:call-template>
+ <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+<xsl:template match="varlistentry[preceding-sibling::title]">
+ <xsl:if test="not(preceding-sibling::varlistentry)">
+ <xsl:text>.RS 4&#10;</xsl:text>
+ <!-- comment out the leading .PP added by the original template -->
+ <xsl:text>.\"</xsl:text>
+ </xsl:if>
+ <xsl:apply-imports/>
+ <xsl:if test="position() = last()">
+ <xsl:text>.RE&#10;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/subprojects/extensions-tool/meson.build b/subprojects/extensions-tool/meson.build
new file mode 100644
index 0000000..11e48d9
--- /dev/null
+++ b/subprojects/extensions-tool/meson.build
@@ -0,0 +1,84 @@
+project('gnome-extensions-tool', 'c',
+ version: '43.9',
+ meson_version: '>= 0.58.0',
+ license: 'GPLv2+'
+)
+
+gio_req = '>= 2.56.0'
+
+fs = import('fs')
+gnome = import('gnome')
+i18n = import('i18n')
+
+if meson.is_subproject()
+ package_name = get_option('package_name')
+ assert(package_name != '',
+ 'package_name must be specified for subproject builds')
+else
+ package_name = meson.project_name()
+endif
+
+package_version = meson.project_version()
+prefix = get_option('prefix')
+
+bindir = join_paths(prefix, get_option('bindir'))
+datadir = join_paths(prefix, get_option('datadir'))
+mandir = join_paths(prefix, get_option('mandir'))
+
+localedir = join_paths(datadir, 'locale')
+
+gio_dep = dependency('gio-2.0', version: gio_req)
+gio_unix_dep = dependency('gio-unix-2.0', version: gio_req)
+autoar_dep = dependency('gnome-autoar-0')
+json_dep = dependency('json-glib-1.0')
+
+cc = meson.get_compiler('c')
+
+bash_completion = dependency('bash-completion', required: get_option('bash_completion'))
+
+po_dir = meson.global_source_root() + '/po'
+
+subdir('src')
+
+if bash_completion.found()
+ install_data('completion/bash/gnome-extensions',
+ install_dir: bash_completion.get_variable('completionsdir', pkgconfig_define: ['datadir', datadir])
+ )
+endif
+
+if get_option('man')
+ if fs.exists('man/gnome-extensions.1')
+ install_man('man/gnome-extensions.1')
+ else
+ a2x = find_program('a2x')
+ subdir('man')
+ endif
+endif
+
+if not meson.is_subproject()
+ subdir('po')
+
+ summary_dirs = {
+ 'prefix': get_option('prefix'),
+ 'bindir': get_option('bindir'),
+ 'datadir': get_option('datadir'),
+ }
+
+ if get_option('man')
+ summary_dirs += { 'mandir': get_option('mandir') }
+ endif
+
+ summary_build = {
+ 'buildtype': get_option('buildtype'),
+ 'debug': get_option('debug'),
+ }
+
+ summary_options = {
+ 'man': get_option('man'),
+ 'bash_completion': bash_completion.found(),
+ }
+
+ summary(summary_dirs, section: 'Directories')
+ summary(summary_build, section: 'Build Configuration')
+ summary(summary_options, section: 'Build Options')
+endif
diff --git a/subprojects/extensions-tool/meson_options.txt b/subprojects/extensions-tool/meson_options.txt
new file mode 100644
index 0000000..fb6e370
--- /dev/null
+++ b/subprojects/extensions-tool/meson_options.txt
@@ -0,0 +1,17 @@
+option('man',
+ type: 'boolean',
+ value: true,
+ description: 'Generate man pages',
+ yield: true,
+)
+
+option('bash_completion',
+ type: 'feature',
+ value: 'auto',
+ description: 'Install bash completion support',
+)
+
+option('package_name',
+ type: 'string',
+ description: 'The gettext domain name when used as a subproject'
+)
diff --git a/subprojects/extensions-tool/po/.gitignore b/subprojects/extensions-tool/po/.gitignore
new file mode 100644
index 0000000..3b2228d
--- /dev/null
+++ b/subprojects/extensions-tool/po/.gitignore
@@ -0,0 +1,3 @@
+*.po
+*.pot
+POTFILES.in
diff --git a/subprojects/extensions-tool/po/LINGUAS b/subprojects/extensions-tool/po/LINGUAS
new file mode 120000
index 0000000..4fb83a5
--- /dev/null
+++ b/subprojects/extensions-tool/po/LINGUAS
@@ -0,0 +1 @@
+../../../po/LINGUAS \ No newline at end of file
diff --git a/subprojects/extensions-tool/po/meson.build b/subprojects/extensions-tool/po/meson.build
new file mode 100644
index 0000000..5a1b0e2
--- /dev/null
+++ b/subprojects/extensions-tool/po/meson.build
@@ -0,0 +1 @@
+i18n.gettext(package_name, preset: 'glib')
diff --git a/subprojects/extensions-tool/src/command-create.c b/subprojects/extensions-tool/src/command-create.c
new file mode 100644
index 0000000..420fb27
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-create.c
@@ -0,0 +1,506 @@
+/* command-create.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define _GNU_SOURCE /* for strcasestr */
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <gio/gunixinputstream.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+#define TEMPLATES_PATH "/org/gnome/extensions-tool/templates"
+#define TEMPLATE_KEY "Path"
+#define SORT_DATA "desktop-id"
+
+static char *
+get_shell_version (GError **error)
+{
+ g_autoptr (GDBusProxy) proxy = NULL;
+ g_autoptr (GVariant) variant = NULL;
+ g_auto (GStrv) split_version = NULL;
+
+ proxy = get_shell_proxy (error);
+ if (proxy == NULL)
+ return NULL;
+
+ variant = g_dbus_proxy_get_cached_property (proxy, "ShellVersion");
+ if (variant == NULL)
+ return NULL;
+
+ split_version = g_strsplit (g_variant_get_string (variant, NULL), ".", 2);
+ return g_steal_pointer(&split_version[0]);
+}
+
+static GDesktopAppInfo *
+load_app_info_from_resource (const char *uri)
+{
+ g_autoptr (GFile) file = NULL;
+ g_autofree char *contents = NULL;
+ g_autoptr (GKeyFile) keyfile = NULL;
+
+ file = g_file_new_for_uri (uri);
+ if (!g_file_load_contents (file, NULL, &contents, NULL, NULL, NULL))
+ return NULL;
+
+ keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_data (keyfile, contents, -1, G_KEY_FILE_NONE, NULL))
+ return NULL;
+
+ return g_desktop_app_info_new_from_keyfile (keyfile);
+}
+
+static int
+sort_func (gconstpointer a, gconstpointer b)
+{
+ GObject *info1 = *((GObject **) a);
+ GObject *info2 = *((GObject **) b);
+ const char *desktop1 = g_object_get_data (info1, SORT_DATA);
+ const char *desktop2 = g_object_get_data (info2, SORT_DATA);
+
+ return g_strcmp0 (desktop1, desktop2);
+}
+
+static GPtrArray *
+get_templates (void)
+{
+ g_auto (GStrv) children = NULL;
+ GPtrArray *templates = g_ptr_array_new_with_free_func (g_object_unref);
+ char **s;
+
+ children = g_resources_enumerate_children (TEMPLATES_PATH, 0, NULL);
+
+ for (s = children; *s; s++)
+ {
+ g_autofree char *uri = NULL;
+ GDesktopAppInfo *info;
+
+ if (!g_str_has_suffix (*s, ".desktop"))
+ continue;
+
+ uri = g_strdup_printf ("resource://" TEMPLATES_PATH "/%s", *s);
+ info = load_app_info_from_resource (uri);
+ if (!info)
+ continue;
+
+ g_object_set_data_full (G_OBJECT (info), SORT_DATA, g_strdup (*s), g_free);
+ g_ptr_array_add (templates, info);
+ }
+
+ g_ptr_array_sort (templates, sort_func);
+
+ return templates;
+}
+
+static char *
+escape_json_string (const char *string)
+{
+ GString *escaped = g_string_new (string);
+
+ for (gsize i = 0; i < escaped->len; ++i)
+ {
+ if (escaped->str[i] == '"' || escaped->str[i] == '\\')
+ {
+ g_string_insert_c (escaped, i, '\\');
+ ++i;
+ }
+ }
+
+ return g_string_free (escaped, FALSE);
+}
+
+static gboolean
+create_metadata (GFile *target_dir,
+ const char *uuid,
+ const char *name,
+ const char *description,
+ GError **error)
+{
+ g_autofree char *uuid_escaped = NULL;
+ g_autofree char *name_escaped = NULL;
+ g_autofree char *desc_escaped = NULL;
+ g_autoptr (GFile) target = NULL;
+ g_autoptr (GString) json = NULL;
+ g_autofree char *version = NULL;
+
+ version = get_shell_version (error);
+ if (version == NULL)
+ return FALSE;
+
+ uuid_escaped = escape_json_string (uuid);
+ name_escaped = escape_json_string (name);
+ desc_escaped = escape_json_string (description);
+
+ json = g_string_new ("{\n");
+
+ g_string_append_printf (json, " \"name\": \"%s\",\n", name_escaped);
+ g_string_append_printf (json, " \"description\": \"%s\",\n", desc_escaped);
+ g_string_append_printf (json, " \"uuid\": \"%s\",\n", uuid_escaped);
+ g_string_append_printf (json, " \"shell-version\": [\n");
+ g_string_append_printf (json, " \"%s\"\n", version);
+ g_string_append_printf (json, " ]\n}\n");
+
+ target = g_file_get_child (target_dir, "metadata.json");
+ return g_file_replace_contents (target,
+ json->str,
+ json->len,
+ NULL,
+ FALSE,
+ 0,
+ NULL,
+ NULL,
+ error);
+}
+
+
+static gboolean
+copy_extension_template (const char *template, GFile *target_dir, GError **error)
+{
+ g_auto (GStrv) templates = NULL;
+ g_autofree char *path = NULL;
+ char **s;
+
+ path = g_strdup_printf (TEMPLATES_PATH "/%s", template);
+ templates = g_resources_enumerate_children (path, 0, NULL);
+
+ if (templates == NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "No template %s", template);
+ return FALSE;
+ }
+
+ for (s = templates; *s; s++)
+ {
+ g_autoptr (GFile) target = NULL;
+ g_autoptr (GFile) source = NULL;
+ g_autofree char *uri = NULL;
+
+ uri = g_strdup_printf ("resource://%s/%s", path, *s);
+ source = g_file_new_for_uri (uri);
+ target = g_file_get_child (target_dir, *s);
+
+ if (!g_file_copy (source, target, G_FILE_COPY_TARGET_DEFAULT_PERMS, NULL, NULL, NULL, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+launch_extension_source (GFile *dir, GError **error)
+{
+ g_autoptr (GFile) main_source = NULL;
+ g_autoptr (GAppInfo) handler = NULL;
+ GList l;
+
+ main_source = g_file_get_child (dir, "extension.js");
+ handler = g_file_query_default_handler (main_source, NULL, NULL);
+
+ /* Translators: a file path to an extension directory */
+ g_print (_("The new extension was successfully created in %s.\n"),
+ g_file_peek_path (dir));
+
+ if (handler == NULL)
+ return TRUE;
+
+ l.data = main_source;
+ l.next = l.prev = NULL;
+
+ return g_app_info_launch (handler, &l, NULL, error);
+}
+
+static gboolean
+create_extension (const char *uuid, const char *name, const char *description, const char *template)
+{
+ g_autoptr (GFile) dir = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (template == NULL)
+ template = "plain";
+
+ dir = g_file_new_build_filename (g_get_user_data_dir (),
+ "gnome-shell",
+ "extensions",
+ uuid,
+ NULL);
+
+ if (!g_file_make_directory_with_parents (dir, NULL, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return FALSE;
+ }
+
+ if (!create_metadata (dir, uuid, name, description, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return FALSE;
+ }
+
+ if (!copy_extension_template (template, dir, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return FALSE;
+ }
+
+ if (!launch_extension_source (dir, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+prompt_metadata (char **uuid, char **name, char **description, char **template)
+{
+ g_autoptr (GInputStream) stdin = NULL;
+ g_autoptr (GDataInputStream) istream = NULL;
+
+ if ((uuid == NULL || *uuid != NULL) &&
+ (name == NULL || *name != NULL) &&
+ (description == NULL || *description != NULL) &&
+ (template == NULL || *template != NULL))
+ return;
+
+ stdin = g_unix_input_stream_new (0, FALSE);
+ istream = g_data_input_stream_new (stdin);
+
+ if (name != NULL && *name == NULL)
+ {
+ char *line = NULL;
+
+ g_print (
+ _("Name should be a very short (ideally descriptive) string.\n"
+ "Examples are: %s"),
+ "“Click To Focus”, “Adblock”, “Shell Window Shrinker”\n");
+
+ while (line == NULL)
+ {
+ g_print ("%s: ", _("Name"));
+
+ line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL);
+ }
+ *name = g_strdelimit (line, "\n", '\0');
+
+ g_print ("\n");
+ }
+
+ if (description != NULL && *description == NULL)
+ {
+ char *line = NULL;
+
+ g_print (
+ _("Description is a single-sentence explanation of what your extension does.\n"
+ "Examples are: %s"),
+ "“Make windows visible on click”, “Block advertisement popups”, “Animate windows shrinking on minimize”\n");
+
+ while (line == NULL)
+ {
+ g_print ("%s: ", _("Description"));
+
+ line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL);
+ }
+ *description = g_strdelimit (line, "\n", '\0');
+
+ g_print ("\n");
+ }
+
+ if (uuid != NULL && *uuid == NULL)
+ {
+ char *line = NULL;
+
+ g_print (
+ _("UUID is a globally-unique identifier for your extension.\n"
+ "This should be in the format of an email address (clicktofocus@janedoe.example.com)\n"));
+
+ while (line == NULL)
+ {
+ g_print ("UUID: ");
+
+ line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL);
+ }
+ *uuid = g_strdelimit (line, "\n", '\0');
+
+ g_print ("\n");
+ }
+
+ if (template != NULL && *template == NULL)
+ {
+ g_autoptr (GPtrArray) templates = get_templates ();
+
+ if (templates->len == 1)
+ {
+ GDesktopAppInfo *info = g_ptr_array_index (templates, 0);
+ *template = g_desktop_app_info_get_string (info, TEMPLATE_KEY);
+ }
+ else
+ {
+ int i;
+
+ g_print (_("Choose one of the available templates:\n"));
+ for (i = 0; i < templates->len; i++)
+ {
+ GAppInfo *info = g_ptr_array_index (templates, i);
+ g_print ("%d) %-10s – %s\n",
+ i + 1,
+ g_app_info_get_name (info),
+ g_app_info_get_description (info));
+ }
+
+ while (*template == NULL)
+ {
+ g_autofree char *line = NULL;
+
+ g_print ("%s [1-%d]: ", _("Template"), templates->len);
+
+ line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL);
+
+ if (line == NULL)
+ continue;
+
+ if (g_ascii_isdigit (*line))
+ {
+ long i = strtol (line, NULL, 10);
+
+ if (i > 0 && i <= templates->len)
+ {
+ GDesktopAppInfo *info;
+
+ info = g_ptr_array_index (templates, i - 1);
+ *template =
+ g_desktop_app_info_get_string (info, TEMPLATE_KEY);
+ }
+ }
+ else
+ {
+ for (i = 0; i < templates->len; i++)
+ {
+ GDesktopAppInfo *info = g_ptr_array_index (templates, i);
+ g_autofree char *cur_template = NULL;
+
+ cur_template =
+ g_desktop_app_info_get_string (info, TEMPLATE_KEY);
+
+ if (strcasestr (cur_template, line) != NULL)
+ *template = g_steal_pointer (&cur_template);
+ }
+ }
+ }
+ g_print ("\n");
+ }
+ }
+}
+
+int
+handle_create (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_autofree char *name = NULL;
+ g_autofree char *description = NULL;
+ g_autofree char *uuid = NULL;
+ g_autofree char *template = NULL;
+ gboolean interactive = FALSE;
+ gboolean list_templates = FALSE;
+ GOptionEntry entries[] = {
+ { .long_name = "uuid",
+ .arg = G_OPTION_ARG_STRING, .arg_data = &uuid,
+ .arg_description = "UUID",
+ .description = _("The unique identifier of the new extension") },
+ { .long_name = "name",
+ .arg = G_OPTION_ARG_STRING, .arg_data = &name,
+ .arg_description = _("NAME"),
+ .description = _("The user-visible name of the new extension") },
+ { .long_name = "description",
+ .arg_description = _("DESCRIPTION"),
+ .arg = G_OPTION_ARG_STRING, .arg_data = &description,
+ .description = _("A short description of what the extension does") },
+ { .long_name = "template",
+ .arg = G_OPTION_ARG_STRING, .arg_data = &template,
+ .arg_description = _("TEMPLATE"),
+ .description = _("The template to use for the new extension") },
+ { .long_name = "list-templates",
+ .arg = G_OPTION_ARG_NONE, .arg_data = &list_templates,
+ .flags = G_OPTION_FLAG_HIDDEN },
+ { .long_name = "interactive", .short_name = 'i',
+ .arg = G_OPTION_ARG_NONE, .arg_data = &interactive,
+ .description = _("Enter extension information interactively") },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions create");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Create a new extension"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group ());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (argc > 1)
+ {
+ show_help (context, _("Unknown arguments"));
+ return 1;
+ }
+
+ if (list_templates)
+ {
+ g_autoptr (GPtrArray) templates = get_templates ();
+ int i;
+
+ for (i = 0; i < templates->len; i++)
+ {
+ GDesktopAppInfo *info = g_ptr_array_index (templates, i);
+ g_autofree char *template = NULL;
+
+ template = g_desktop_app_info_get_string (info, TEMPLATE_KEY);
+ g_print ("%s\n", template);
+ }
+ return 0;
+ }
+
+ if (interactive)
+ prompt_metadata (&uuid, &name, &description, &template);
+
+ if (uuid == NULL || name == NULL || description == NULL)
+ {
+ show_help (context, _("UUID, name and description are required"));
+ return 1;
+ }
+
+ return create_extension (uuid, name, description, template) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/command-disable.c b/subprojects/extensions-tool/src/command-disable.c
new file mode 100644
index 0000000..bae11b2
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-disable.c
@@ -0,0 +1,126 @@
+/* command-disable.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static gboolean
+disable_extension_gsettings (const char *uuid)
+{
+ g_autoptr(GSettings) settings = get_shell_settings ();
+
+ if (settings == NULL)
+ return FALSE;
+
+ return settings_list_remove (settings, "enabled-extensions", uuid) &&
+ settings_list_add (settings, "disabled-extensions", uuid);
+}
+
+static gboolean
+disable_extension_dbus (GDBusProxy *proxy,
+ const char *uuid)
+{
+ g_autoptr (GVariant) response = NULL;
+ g_autoptr (GError) error = NULL;
+ gboolean success = FALSE;
+
+ response = g_dbus_proxy_call_sync (proxy,
+ "DisableExtension",
+ g_variant_new ("(s)", uuid),
+ 0,
+ -1,
+ NULL,
+ &error);
+
+ if (response == NULL)
+ return disable_extension_gsettings (uuid);
+
+ g_variant_get (response, "(b)", &success);
+
+ if (!success)
+ g_printerr (_("Extension “%s” does not exist\n"), uuid);
+
+ return success;
+}
+
+static gboolean
+disable_extension (const char *uuid)
+{
+ g_autoptr (GDBusProxy) proxy = NULL;
+ g_autoptr (GError) error = NULL;
+
+ proxy = get_shell_proxy (&error);
+
+ if (proxy != NULL)
+ return disable_extension_dbus (proxy, uuid);
+ else
+ return disable_extension_gsettings (uuid);
+}
+
+int
+handle_disable (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto(GStrv) uuids = NULL;
+ GOptionEntry entries[] = {
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description = "UUID",
+ .arg = G_OPTION_ARG_STRING_ARRAY, .arg_data = &uuids },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions disable");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Disable an extension"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (uuids == NULL)
+ {
+ show_help (context, _("No UUID given"));
+ return 1;
+ }
+ else if (g_strv_length (uuids) > 1)
+ {
+ show_help (context, _("More than one UUID given"));
+ return 1;
+ }
+
+ return disable_extension (*uuids) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/command-enable.c b/subprojects/extensions-tool/src/command-enable.c
new file mode 100644
index 0000000..712de4a
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-enable.c
@@ -0,0 +1,126 @@
+/* command-enable.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static gboolean
+enable_extension_gsettings (const char *uuid)
+{
+ g_autoptr(GSettings) settings = get_shell_settings ();
+
+ if (settings == NULL)
+ return FALSE;
+
+ return settings_list_add (settings, "enabled-extensions", uuid) &&
+ settings_list_remove (settings, "disabled-extensions", uuid);
+}
+
+static gboolean
+enable_extension_dbus (GDBusProxy *proxy,
+ const char *uuid)
+{
+ g_autoptr (GVariant) response = NULL;
+ g_autoptr (GError) error = NULL;
+ gboolean success = FALSE;
+
+ response = g_dbus_proxy_call_sync (proxy,
+ "EnableExtension",
+ g_variant_new ("(s)", uuid),
+ 0,
+ -1,
+ NULL,
+ &error);
+
+ if (response == NULL)
+ return enable_extension_gsettings (uuid);
+
+ g_variant_get (response, "(b)", &success);
+
+ if (!success)
+ g_printerr (_("Extension “%s” does not exist\n"), uuid);
+
+ return success;
+}
+
+static gboolean
+enable_extension (const char *uuid)
+{
+ g_autoptr (GDBusProxy) proxy = NULL;
+ g_autoptr (GError) error = NULL;
+
+ proxy = get_shell_proxy (&error);
+
+ if (proxy != NULL)
+ return enable_extension_dbus (proxy, uuid);
+ else
+ return enable_extension_gsettings (uuid);
+}
+
+int
+handle_enable (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto(GStrv) uuids = NULL;
+ GOptionEntry entries[] = {
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description = "UUID",
+ .arg = G_OPTION_ARG_STRING_ARRAY, .arg_data = &uuids },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions enable");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Enable an extension"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (uuids == NULL)
+ {
+ show_help (context, _("No UUID given"));
+ return 1;
+ }
+ else if (g_strv_length (uuids) > 1)
+ {
+ show_help (context, _("More than one UUID given"));
+ return 1;
+ }
+
+ return enable_extension (*uuids) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/command-info.c b/subprojects/extensions-tool/src/command-info.c
new file mode 100644
index 0000000..61492a5
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-info.c
@@ -0,0 +1,113 @@
+/* commands-info.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static gboolean
+show_extension_info (const char *uuid)
+{
+ g_autoptr (GDBusProxy) proxy = NULL;
+ g_autoptr (GVariant) response = NULL;
+ g_autoptr (GVariant) asv = NULL;
+ g_autoptr (GVariantDict) info = NULL;
+ g_autoptr (GError) error = NULL;
+
+ proxy = get_shell_proxy (&error);
+ if (proxy == NULL)
+ return FALSE;
+
+ response = g_dbus_proxy_call_sync (proxy,
+ "GetExtensionInfo",
+ g_variant_new ("(s)", uuid),
+ 0,
+ -1,
+ NULL,
+ &error);
+ if (response == NULL)
+ {
+ g_printerr (_("Failed to connect to GNOME Shell\n"));
+ return FALSE;
+ }
+
+ asv = g_variant_get_child_value (response, 0);
+ info = g_variant_dict_new (asv);
+
+ if (!g_variant_dict_contains (info, "uuid"))
+ {
+ g_printerr (_("Extension “%s” doesn't exist\n"), uuid);
+ return FALSE;
+ }
+
+ print_extension_info (info, DISPLAY_DETAILED);
+
+ return TRUE;
+}
+
+int
+handle_info (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto(GStrv) uuids = NULL;
+ GOptionEntry entries[] = {
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description = "UUID",
+ .arg = G_OPTION_ARG_STRING_ARRAY, .arg_data = &uuids },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions info");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Show extensions info"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (uuids == NULL)
+ {
+ show_help (context, _("No UUID given"));
+ return 1;
+ }
+ else if (g_strv_length (uuids) > 1)
+ {
+ show_help (context, _("More than one UUID given"));
+ return 1;
+ }
+
+ return show_extension_info (*uuids) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/command-install.c b/subprojects/extensions-tool/src/command-install.c
new file mode 100644
index 0000000..2eefaba
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-install.c
@@ -0,0 +1,213 @@
+/* command-install.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <gnome-autoar/gnome-autoar.h>
+#include <json-glib/json-glib.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static JsonObject *
+load_metadata (GFile *dir,
+ GError **error)
+{
+ g_autoptr (JsonParser) parser = NULL;
+ g_autoptr (GInputStream) stream = NULL;
+ g_autoptr (GFile) file = NULL;
+
+ file = g_file_get_child (dir, "metadata.json");
+ stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
+ if (stream == NULL)
+ return NULL;
+
+ parser = json_parser_new_immutable ();
+ if (!json_parser_load_from_stream (parser, stream, NULL, error))
+ return NULL;
+
+ return json_node_dup_object (json_parser_get_root (parser));
+}
+
+static void
+on_error (AutoarExtractor *extractor,
+ GError *error,
+ gpointer data)
+{
+ *((GError **)data) = g_error_copy (error);
+}
+
+static GFile *
+on_decide_destination (AutoarExtractor *extractor,
+ GFile *dest,
+ GList *files,
+ gpointer data)
+{
+ g_autofree char *dest_path = NULL;
+ GFile *new_dest;
+ int copy = 1;
+
+ dest_path = g_file_get_path (dest);
+ new_dest = g_object_ref (dest);
+
+ while (g_file_query_exists (new_dest, NULL))
+ {
+ g_autofree char *new_path = g_strdup_printf ("%s (%d)", dest_path, copy);
+
+ g_object_unref (new_dest);
+ new_dest = g_file_new_for_path (new_path);
+
+ copy++;
+ }
+
+ *((GFile **)data) = g_object_ref (new_dest);
+
+ return new_dest;
+}
+
+static int
+install_extension (const char *bundle,
+ gboolean force)
+{
+ g_autoptr (AutoarExtractor) extractor = NULL;
+ g_autoptr (JsonObject) metadata = NULL;
+ g_autoptr (GFile) cachedir = NULL;
+ g_autoptr (GFile) tmpdir = NULL;
+ g_autoptr (GFile) src = NULL;
+ g_autoptr (GFile) dst = NULL;
+ g_autoptr (GFile) dstdir = NULL;
+ g_autoptr (GError) error = NULL;
+ g_autofree char *cwd = NULL;
+ const char *uuid;
+
+ cwd = g_get_current_dir ();
+ src = g_file_new_for_commandline_arg_and_cwd (bundle, cwd);
+ cachedir = g_file_new_for_path (g_get_user_cache_dir ());
+
+ extractor = autoar_extractor_new (src, cachedir);
+
+ g_signal_connect (extractor, "error", G_CALLBACK (on_error), &error);
+ g_signal_connect (extractor, "decide-destination", G_CALLBACK (on_decide_destination), &tmpdir);
+
+ autoar_extractor_start (extractor, NULL);
+
+ if (error != NULL)
+ goto err;
+
+ metadata = load_metadata (tmpdir, &error);
+ if (metadata == NULL)
+ goto err;
+
+ dstdir = g_file_new_build_filename (g_get_user_data_dir (),
+ "gnome-shell", "extensions", NULL);
+
+ if (!g_file_make_directory_with_parents (dstdir, NULL, &error))
+ {
+ if (error->code == G_IO_ERROR_EXISTS)
+ g_clear_error (&error);
+ else
+ goto err;
+ }
+
+ uuid = json_object_get_string_member (metadata, "uuid");
+ dst = g_file_get_child (dstdir, uuid);
+
+ if (g_file_query_exists (dst, NULL))
+ {
+ if (!force)
+ {
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+ "%s exists and --force was not specified", uuid);
+ goto err;
+ }
+ else if (!file_delete_recursively (dst, &error))
+ {
+ goto err;
+ }
+ }
+
+ if (!g_file_move (tmpdir, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, &error))
+ goto err;
+
+ return 0;
+
+err:
+ if (error != NULL)
+ g_printerr ("%s\n", error->message);
+
+ if (tmpdir != NULL)
+ file_delete_recursively (tmpdir, NULL);
+
+ return 2;
+}
+
+int
+handle_install (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto (GStrv) filenames = NULL;
+ gboolean force = FALSE;
+ GOptionEntry entries[] = {
+ { .long_name = "force", .short_name = 'f',
+ .arg = G_OPTION_ARG_NONE, .arg_data = &force,
+ .description = _("Overwrite an existing extension") },
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description =_("EXTENSION_BUNDLE"),
+ .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = &filenames },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions install");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Install an extension bundle"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (filenames == NULL)
+ {
+ show_help (context, _("No extension bundle specified"));
+ return 1;
+ }
+
+ if (g_strv_length (filenames) > 1)
+ {
+ show_help (context, _("More than one extension bundle specified"));
+ return 1;
+ }
+
+ return install_extension (*filenames, force);
+}
diff --git a/subprojects/extensions-tool/src/command-list.c b/subprojects/extensions-tool/src/command-list.c
new file mode 100644
index 0000000..62db4d9
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-list.c
@@ -0,0 +1,196 @@
+/* command-list.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+
+typedef enum {
+ LIST_FLAGS_NONE = 0,
+ LIST_FLAGS_USER = 1 << 0,
+ LIST_FLAGS_SYSTEM = 1 << 1,
+ LIST_FLAGS_ENABLED = 1 << 2,
+ LIST_FLAGS_DISABLED = 1 << 3,
+ LIST_FLAGS_NO_PREFS = 1 << 4,
+ LIST_FLAGS_NO_UPDATES = 1 << 5,
+} ListFilterFlags;
+
+static gboolean
+list_extensions (ListFilterFlags filter, DisplayFormat format)
+{
+ g_autoptr (GDBusProxy) proxy = NULL;
+ g_autoptr (GVariant) response = NULL;
+ g_autoptr (GVariant) extensions = NULL;
+ g_autoptr (GError) error = NULL;
+ gboolean needs_newline = FALSE;
+ GVariantIter iter;
+ GVariant *value;
+ char *uuid;
+
+ proxy = get_shell_proxy (&error);
+ if (proxy == NULL)
+ return FALSE;
+
+ response = g_dbus_proxy_call_sync (proxy,
+ "ListExtensions",
+ NULL,
+ 0,
+ -1,
+ NULL,
+ &error);
+ if (response == NULL)
+ {
+ g_printerr (_("Failed to connect to GNOME Shell\n"));
+ return FALSE;
+ }
+
+ extensions = g_variant_get_child_value (response, 0);
+
+ g_variant_iter_init (&iter, extensions);
+ while (g_variant_iter_loop (&iter, "{s@a{sv}}", &uuid, &value))
+ {
+ g_autoptr (GVariantDict) info = NULL;
+ double type, state;
+ gboolean has_prefs;
+ gboolean has_update;
+
+ info = g_variant_dict_new (value);
+ g_variant_dict_lookup (info, "type", "d", &type);
+ g_variant_dict_lookup (info, "state", "d", &state);
+ g_variant_dict_lookup (info, "hasPrefs", "b", &has_prefs);
+ g_variant_dict_lookup (info, "hasUpdate", "b", &has_update);
+
+ if (type == TYPE_USER && (filter & LIST_FLAGS_USER) == 0)
+ continue;
+
+ if (type == TYPE_SYSTEM && (filter & LIST_FLAGS_SYSTEM) == 0)
+ continue;
+
+ if (state == STATE_ENABLED && (filter & LIST_FLAGS_ENABLED) == 0)
+ continue;
+
+ if (state != STATE_ENABLED && (filter & LIST_FLAGS_DISABLED) == 0)
+ continue;
+
+ if (!has_prefs && (filter & LIST_FLAGS_NO_PREFS) == 0)
+ continue;
+
+ if (!has_update && (filter & LIST_FLAGS_NO_UPDATES) == 0)
+ continue;
+
+ if (needs_newline)
+ g_print ("\n");
+
+ print_extension_info (info, format);
+ needs_newline = (format != DISPLAY_ONELINE);
+ }
+
+ return TRUE;
+}
+
+int
+handle_list (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ int flags = LIST_FLAGS_NONE;
+ gboolean details = FALSE;
+ gboolean user = FALSE;
+ gboolean system = FALSE;
+ gboolean enabled = FALSE;
+ gboolean disabled = FALSE;
+ gboolean has_prefs = FALSE;
+ gboolean has_updates = FALSE;
+ GOptionEntry entries[] = {
+ { .long_name = "user",
+ .arg = G_OPTION_ARG_NONE, .arg_data = &user,
+ .description = _("Show user-installed extensions") },
+ { .long_name = "system",
+ .arg = G_OPTION_ARG_NONE, .arg_data = &system,
+ .description = _("Show system-installed extensions") },
+ { .long_name = "enabled",
+ .arg = G_OPTION_ARG_NONE, .arg_data = &enabled,
+ .description = _("Show enabled extensions") },
+ { .long_name = "disabled",
+ .arg = G_OPTION_ARG_NONE, .arg_data = &disabled,
+ .description = _("Show disabled extensions") },
+ { .long_name = "prefs",
+ .arg = G_OPTION_ARG_NONE, .arg_data = &has_prefs,
+ .description = _("Show extensions with preferences") },
+ { .long_name = "updates",
+ .arg = G_OPTION_ARG_NONE, .arg_data = &has_updates,
+ .description = _("Show extensions with updates") },
+ { .long_name = "details", .short_name = 'd',
+ .arg = G_OPTION_ARG_NONE, .arg_data = &details,
+ .description = _("Print extension details") },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions list");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("List installed extensions"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (argc > 1)
+ {
+ show_help (context, _("Unknown arguments"));
+ return 1;
+ }
+
+ if (user || !system)
+ flags |= LIST_FLAGS_USER;
+
+ if (system || !user)
+ flags |= LIST_FLAGS_SYSTEM;
+
+ if (enabled || !disabled)
+ flags |= LIST_FLAGS_ENABLED;
+
+ if (disabled || !enabled)
+ flags |= LIST_FLAGS_DISABLED;
+
+ if (!has_prefs)
+ flags |= LIST_FLAGS_NO_PREFS;
+
+ if (!has_updates)
+ flags |= LIST_FLAGS_NO_UPDATES;
+
+ return list_extensions (flags, details ? DISPLAY_DETAILED
+ : DISPLAY_ONELINE) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/command-pack.c b/subprojects/extensions-tool/src/command-pack.c
new file mode 100644
index 0000000..c8d9950
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-pack.c
@@ -0,0 +1,516 @@
+/* command-pack.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <gnome-autoar/gnome-autoar.h>
+#include <json-glib/json-glib.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+typedef struct _ExtensionPack {
+ GHashTable *files;
+ JsonObject *metadata;
+ GFile *tmpdir;
+ char *srcdir;
+} ExtensionPack;
+
+static void extension_pack_free (ExtensionPack *);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (ExtensionPack, extension_pack_free);
+
+static ExtensionPack *
+extension_pack_new (const char *srcdir)
+{
+ ExtensionPack *pack = g_new0 (ExtensionPack, 1);
+ pack->srcdir = g_strdup (srcdir);
+ pack->files = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+ return pack;
+}
+
+static void
+extension_pack_free (ExtensionPack *pack)
+{
+ if (pack->tmpdir)
+ file_delete_recursively (pack->tmpdir, NULL);
+
+ g_clear_pointer (&pack->files, g_hash_table_destroy);
+ g_clear_pointer (&pack->metadata, json_object_unref);
+ g_clear_pointer (&pack->srcdir, g_free);
+ g_clear_object (&pack->tmpdir);
+ g_free (pack);
+}
+
+static void
+extension_pack_add_source (ExtensionPack *pack,
+ const char *filename)
+{
+ g_autoptr (GFile) file = NULL;
+ file = g_file_new_for_commandline_arg_and_cwd (filename, pack->srcdir);
+ if (g_file_query_exists (file, NULL))
+ g_hash_table_insert (pack->files,
+ g_path_get_basename (filename), g_steal_pointer (&file));
+}
+
+static gboolean
+extension_pack_check_required_file (ExtensionPack *pack,
+ const char *filename,
+ GError **error)
+{
+ if (!g_hash_table_contains (pack->files, filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "Missing %s in extension pack", filename);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+ensure_tmpdir (ExtensionPack *pack,
+ GError **error)
+{
+ g_autofree char *path = NULL;
+
+ if (pack->tmpdir != NULL)
+ return TRUE;
+
+ path = g_dir_make_tmp ("gnome-extensions.XXXXXX", error);
+ if (path != NULL)
+ pack->tmpdir = g_file_new_for_path (path);
+
+ return pack->tmpdir != NULL;
+}
+
+static gboolean
+ensure_metadata (ExtensionPack *pack,
+ GError **error)
+{
+ g_autoptr (JsonParser) parser = NULL;
+ g_autoptr (GInputStream) stream = NULL;
+ GFile *file = NULL;
+
+ if (pack->metadata != NULL)
+ return TRUE;
+
+ if (!extension_pack_check_required_file (pack, "metadata.json", error))
+ return FALSE;
+
+ file = g_hash_table_lookup (pack->files, "metadata.json");
+ stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+ if (stream == NULL)
+ return FALSE;
+
+ parser = json_parser_new_immutable ();
+
+ if (!json_parser_load_from_stream (parser, stream, NULL, error))
+ return FALSE;
+
+ pack->metadata = json_node_dup_object (json_parser_get_root (parser));
+ return TRUE;
+}
+
+static gboolean
+extension_pack_add_schemas (ExtensionPack *pack,
+ char **schemas,
+ GError **error)
+{
+ g_autoptr (GSubprocess) proc = NULL;
+ g_autoptr (GFile) dstdir = NULL;
+ g_autofree char *dstpath = NULL;
+ char **s;
+
+ if (!ensure_tmpdir (pack, error))
+ return FALSE;
+
+ dstdir = g_file_get_child (pack->tmpdir, "schemas");
+ if (!g_file_make_directory (dstdir, NULL, error))
+ return FALSE;
+
+ for (s = schemas; s && *s; s++)
+ {
+ g_autoptr (GFile) src = NULL;
+ g_autoptr (GFile) dst = NULL;
+ g_autofree char *basename = NULL;
+
+ src = g_file_new_for_commandline_arg_and_cwd (*s, pack->srcdir);
+
+ basename = g_file_get_basename (src);
+ dst = g_file_get_child (dstdir, basename);
+
+ if (!g_file_copy (src, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, error))
+ return FALSE;
+ }
+
+ dstpath = g_file_get_path (dstdir);
+ proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDERR_SILENCE, error,
+ "glib-compile-schemas", "--strict", dstpath, NULL);
+
+ if (!g_subprocess_wait_check (proc, NULL, error))
+ return FALSE;
+
+ g_hash_table_insert (pack->files,
+ g_strdup ("schemas"), g_steal_pointer (&dstdir));
+ return TRUE;
+}
+
+static gboolean
+extension_pack_add_locales (ExtensionPack *pack,
+ const char *podir,
+ const char *gettext_domain,
+ GError **error)
+{
+ g_autoptr (GFile) dstdir = NULL;
+ g_autoptr (GFile) srcdir = NULL;
+ g_autoptr (GFileEnumerator) file_enum = NULL;
+ g_autofree char *dstpath = NULL;
+ g_autofree char *moname = NULL;
+ GFile *child;
+ GFileInfo *info;
+
+ if (!ensure_tmpdir (pack, error))
+ return FALSE;
+
+ dstdir = g_file_get_child (pack->tmpdir, "locale");
+ if (!g_file_make_directory (dstdir, NULL, error))
+ return FALSE;
+
+ srcdir = g_file_new_for_commandline_arg_and_cwd (podir, pack->srcdir);
+ file_enum = g_file_enumerate_children (srcdir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ error);
+ if (file_enum == NULL)
+ return FALSE;
+
+ if (gettext_domain == NULL)
+ {
+ if (!ensure_metadata (pack, error))
+ return FALSE;
+
+ if (json_object_has_member (pack->metadata, "gettext-domain"))
+ gettext_domain = json_object_get_string_member (pack->metadata,
+ "gettext-domain");
+ else
+ gettext_domain = json_object_get_string_member (pack->metadata,
+ "uuid");
+ }
+
+ dstpath = g_file_get_path (dstdir);
+ moname = g_strdup_printf ("%s.mo", gettext_domain);
+
+ while (TRUE)
+ {
+ g_autoptr (GSubprocess) proc = NULL;
+ g_autoptr (GFile) modir = NULL;
+ g_autofree char *popath = NULL;
+ g_autofree char *mopath = NULL;
+ g_autofree char *lang = NULL;
+ const char *name;
+
+ if (!g_file_enumerator_iterate (file_enum, &info, &child, NULL, error))
+ return FALSE;
+
+ if (info == NULL)
+ break;
+
+ name = g_file_info_get_name (info);
+ if (!g_str_has_suffix (name, ".po"))
+ continue;
+
+ lang = g_strndup (name, strlen (name) - 3 /* strlen (".po") */);
+ modir = g_file_new_build_filename (dstpath, lang, "LC_MESSAGES", NULL);
+ if (!g_file_make_directory_with_parents (modir, NULL, error))
+ return FALSE;
+
+ mopath = g_build_filename (dstpath, lang, "LC_MESSAGES", moname, NULL);
+ popath = g_file_get_path (child);
+
+ proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDERR_SILENCE, error,
+ "msgfmt", "-o", mopath, popath, NULL);
+
+ if (!g_subprocess_wait_check (proc, NULL, error))
+ return FALSE;
+ }
+
+ g_hash_table_insert (pack->files,
+ g_strdup ("locale"), g_steal_pointer (&dstdir));
+ return TRUE;
+}
+
+static void
+on_error (AutoarCompressor *compressor,
+ GError *error,
+ gpointer data)
+{
+ *((GError **)data) = g_error_copy (error);
+}
+
+static gboolean
+extension_pack_compress (ExtensionPack *pack,
+ const char *outdir,
+ gboolean overwrite,
+ GError **error)
+{
+ g_autoptr (AutoarCompressor) compressor = NULL;
+ g_autoptr (GError) err = NULL;
+ g_autoptr (GFile) outfile = NULL;
+ g_autofree char *name = NULL;
+ const char *uuid;
+
+ if (!ensure_metadata (pack, error))
+ return FALSE;
+
+ uuid = json_object_get_string_member (pack->metadata, "uuid");
+ name = g_strdup_printf ("%s.shell-extension.zip", uuid);
+ outfile = g_file_new_for_commandline_arg_and_cwd (name, outdir);
+
+ if (g_file_query_exists (outfile, NULL))
+ {
+ if (!overwrite)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+ "%s exists and --force was not specified", name);
+ return FALSE;
+ }
+ else if (!g_file_delete (outfile, NULL, error))
+ {
+ return FALSE;
+ }
+ }
+
+ compressor = autoar_compressor_new (g_hash_table_get_values (pack->files),
+ outfile,
+ AUTOAR_FORMAT_ZIP,
+ AUTOAR_FILTER_NONE,
+ FALSE);
+ autoar_compressor_set_output_is_dest (compressor, TRUE);
+
+ g_signal_connect (compressor, "error", G_CALLBACK (on_error), err);
+
+ autoar_compressor_start (compressor, NULL);
+
+ if (err != NULL)
+ {
+ g_propagate_error (error, err);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static char **
+find_schemas (const char *basepath,
+ GError **error)
+{
+ g_autoptr (GFile) basedir = NULL;
+ g_autoptr (GFile) schemadir = NULL;
+ g_autoptr (GFileEnumerator) file_enum = NULL;
+ g_autoptr (GPtrArray) schemas = NULL;
+ GFile *child;
+ GFileInfo *info;
+
+ basedir = g_file_new_for_path (basepath);
+ schemadir = g_file_get_child (basedir, "schemas");
+ file_enum = g_file_enumerate_children (schemadir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, error);
+
+ if (error && *error)
+ {
+ if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
+ g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
+ g_clear_error (error);
+ return NULL;
+ }
+
+ schemas = g_ptr_array_new_with_free_func (g_free);
+
+ while (TRUE)
+ {
+ if (!g_file_enumerator_iterate (file_enum, &info, &child, NULL, error))
+ return NULL;
+
+ if (child == NULL)
+ break;
+
+ if (!g_str_has_suffix (g_file_info_get_name (info), ".gschema.xml"))
+ continue;
+
+ g_ptr_array_add (schemas, g_file_get_relative_path (basedir, child));
+ }
+ g_ptr_array_add (schemas, NULL);
+
+ return (char **)g_ptr_array_free (g_ptr_array_ref (schemas), FALSE);
+}
+
+static int
+pack_extension (char *srcdir,
+ char *dstdir,
+ gboolean force,
+ char **extra_sources,
+ char **schemas,
+ char *podir,
+ char *gettext_domain)
+{
+ g_autoptr (ExtensionPack) pack = NULL;
+ g_autoptr (GError) error = NULL;
+ char **s;
+
+ pack = extension_pack_new (srcdir);
+ extension_pack_add_source (pack, "extension.js");
+ extension_pack_add_source (pack, "metadata.json");
+ extension_pack_add_source (pack, "stylesheet.css");
+ extension_pack_add_source (pack, "prefs.js");
+
+ for (s = extra_sources; s && *s; s++)
+ extension_pack_add_source (pack, *s);
+
+ if (!extension_pack_check_required_file (pack, "extension.js", &error))
+ goto err;
+
+ if (!extension_pack_check_required_file (pack, "metadata.json", &error))
+ goto err;
+
+ if (schemas == NULL)
+ schemas = find_schemas (srcdir, &error);
+
+ if (schemas != NULL)
+ extension_pack_add_schemas (pack, schemas, &error);
+
+ if (error)
+ goto err;
+
+ if (podir == NULL)
+ {
+ g_autoptr (GFile) dir = NULL;
+
+ dir = g_file_new_for_commandline_arg_and_cwd ("po", srcdir);
+ if (g_file_query_exists (dir, NULL))
+ podir = (char *)"po";
+ }
+
+ if (podir != NULL)
+ extension_pack_add_locales (pack, podir, gettext_domain, &error);
+
+ if (error)
+ goto err;
+
+ extension_pack_compress (pack, dstdir, force, &error);
+
+err:
+ if (error)
+ {
+ g_printerr ("%s\n", error->message);
+ return 2;
+ }
+
+ return 0;
+}
+
+int
+handle_pack (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto(GStrv) extra_sources = NULL;
+ g_auto(GStrv) schemas = NULL;
+ g_auto(GStrv) srcdirs = NULL;
+ g_autofree char *podir = NULL;
+ g_autofree char *srcdir = NULL;
+ g_autofree char *dstdir = NULL;
+ g_autofree char *gettext_domain = NULL;
+ gboolean force = FALSE;
+ GOptionEntry entries[] = {
+ { .long_name = "extra-source",
+ .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = &extra_sources,
+ .arg_description = _("FILE"),
+ .description = _("Additional source to include in the bundle") },
+ { .long_name = "schema",
+ .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = &schemas,
+ .arg_description = _("SCHEMA"),
+ .description = _("A GSettings schema that should be included") },
+ { .long_name = "podir",
+ .arg_description = _("DIRECTORY"),
+ .arg = G_OPTION_ARG_FILENAME, .arg_data = &podir,
+ .description = _("The directory where translations are found") },
+ { .long_name = "gettext-domain",
+ .arg_description = _("DOMAIN"),
+ .arg = G_OPTION_ARG_STRING, .arg_data = &gettext_domain,
+ .description = _("The gettext domain to use for translations") },
+ { .long_name = "force", .short_name = 'f',
+ .arg = G_OPTION_ARG_NONE, .arg_data = &force,
+ .description = _("Overwrite an existing pack") },
+ { .long_name = "out-dir", .short_name = 'o',
+ .arg_description = _("DIRECTORY"),
+ .arg = G_OPTION_ARG_FILENAME, .arg_data = &dstdir,
+ .description = _("The directory where the pack should be created") },
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description =_("SOURCE_DIRECTORY"),
+ .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = &srcdirs },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions pack");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Create an extension bundle"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (srcdirs)
+ {
+ if (g_strv_length (srcdirs) > 1)
+ {
+ show_help (context, _("More than one source directory specified"));
+ return 1;
+ }
+ srcdir = g_strdup (*srcdirs);
+ }
+ else
+ {
+ srcdir = g_get_current_dir ();
+ }
+
+ if (dstdir == NULL)
+ dstdir = g_get_current_dir ();
+
+ return pack_extension (srcdir, dstdir, force,
+ extra_sources, schemas, podir, gettext_domain);
+}
diff --git a/subprojects/extensions-tool/src/command-prefs.c b/subprojects/extensions-tool/src/command-prefs.c
new file mode 100644
index 0000000..01c385e
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-prefs.c
@@ -0,0 +1,115 @@
+/* commands-prefs.c
+ *
+ * Copyright 2019 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static gboolean
+launch_extension_prefs (const char *uuid)
+{
+ g_autoptr (GDBusProxy) proxy = NULL;
+ g_autoptr (GVariant) info = NULL;
+ g_autoptr (GError) error = NULL;
+ gboolean has_prefs;
+
+ proxy = get_shell_proxy (&error);
+ if (proxy == NULL)
+ return FALSE;
+
+ info = get_extension_property (proxy, uuid, "hasPrefs");
+ if (info == NULL)
+ return FALSE;
+
+ has_prefs = g_variant_get_boolean (info);
+ if (!has_prefs)
+ {
+ g_printerr (_("Extension “%s” doesn't have preferences\n"), uuid);
+ return FALSE;
+ }
+
+ g_dbus_proxy_call_sync (proxy,
+ "OpenExtensionPrefs",
+ g_variant_new ("(ssa{sv})", uuid, "", NULL),
+ 0,
+ -1,
+ NULL,
+ &error);
+
+ if (error)
+ {
+ g_dbus_error_strip_remote_error (error);
+ g_printerr (_("Failed to open prefs for extension “%s”: %s\n"),
+ uuid, error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+handle_prefs (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto(GStrv) uuids = NULL;
+ GOptionEntry entries[] = {
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description = "UUID",
+ .arg = G_OPTION_ARG_STRING_ARRAY, .arg_data = &uuids },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions prefs");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Opens extension preferences"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (uuids == NULL)
+ {
+ show_help (context, _("No UUID given"));
+ return 1;
+ }
+ else if (g_strv_length (uuids) > 1)
+ {
+ show_help (context, _("More than one UUID given"));
+ return 1;
+ }
+
+ return launch_extension_prefs (*uuids) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/command-reset.c b/subprojects/extensions-tool/src/command-reset.c
new file mode 100644
index 0000000..2615f15
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-reset.c
@@ -0,0 +1,86 @@
+/* command-reset.c
+ g_option_context_add_group (context, get_option_group());
+ *
+ * Copyright 2019 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static gboolean
+reset_extension (const char *uuid)
+{
+ g_autoptr(GSettings) settings = get_shell_settings();
+
+ if (settings == NULL)
+ return FALSE;
+
+ return settings_list_remove (settings, "enabled-extensions", uuid) &&
+ settings_list_remove (settings, "disabled-extensions", uuid);
+}
+
+int
+handle_reset (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto(GStrv) uuids = NULL;
+ GOptionEntry entries[] = {
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description = "UUID",
+ .arg = G_OPTION_ARG_STRING_ARRAY, .arg_data = &uuids },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions reset");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Reset an extension"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (uuids == NULL)
+ {
+ show_help (context, _("No UUID given"));
+ return 1;
+ }
+ else if (g_strv_length (uuids) > 1)
+ {
+ show_help (context, _("More than one UUID given"));
+ return 1;
+ }
+
+ return reset_extension (*uuids) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/command-uninstall.c b/subprojects/extensions-tool/src/command-uninstall.c
new file mode 100644
index 0000000..344b720
--- /dev/null
+++ b/subprojects/extensions-tool/src/command-uninstall.c
@@ -0,0 +1,114 @@
+/* commands-uninstall.c
+ *
+ * Copyright 2019 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static gboolean
+uninstall_extension (const char *uuid)
+{
+ g_autoptr (GDBusProxy) proxy = NULL;
+ g_autoptr (GVariant) info = NULL;
+ g_autoptr (GVariant) response = NULL;
+ g_autoptr (GError) error = NULL;
+ gboolean success = FALSE;
+ double type;
+
+ proxy = get_shell_proxy (&error);
+ if (proxy == NULL)
+ return FALSE;
+
+ info = get_extension_property (proxy, uuid, "type");
+ if (info == NULL)
+ return FALSE;
+
+ type = g_variant_get_double (info);
+ if (type == TYPE_SYSTEM)
+ {
+ g_printerr (_("Cannot uninstall system extensions\n"));
+ return FALSE;
+ }
+
+ response = g_dbus_proxy_call_sync (proxy,
+ "UninstallExtension",
+ g_variant_new ("(s)", uuid),
+ 0,
+ -1,
+ NULL,
+ &error);
+
+ g_variant_get (response, "(b)", &success);
+
+ if (!success)
+ g_printerr (_("Failed to uninstall “%s”\n"), uuid);
+
+ return success;
+}
+
+int
+handle_uninstall (int argc, char *argv[], gboolean do_help)
+{
+ g_autoptr (GOptionContext) context = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto(GStrv) uuids = NULL;
+ GOptionEntry entries[] = {
+ { .long_name = G_OPTION_REMAINING,
+ .arg_description = "UUID",
+ .arg = G_OPTION_ARG_STRING_ARRAY, .arg_data = &uuids },
+ { NULL }
+ };
+
+ g_set_prgname ("gnome-extensions uninstall");
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context, _("Uninstall an extension"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, get_option_group());
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ return 1;
+ }
+
+ if (uuids == NULL)
+ {
+ show_help (context, _("No UUID given"));
+ return 1;
+ }
+ else if (g_strv_length (uuids) > 1)
+ {
+ show_help (context, _("More than one UUID given"));
+ return 1;
+ }
+
+ return uninstall_extension (*uuids) ? 0 : 2;
+}
diff --git a/subprojects/extensions-tool/src/commands.h b/subprojects/extensions-tool/src/commands.h
new file mode 100644
index 0000000..618e841
--- /dev/null
+++ b/subprojects/extensions-tool/src/commands.h
@@ -0,0 +1,38 @@
+/* commands.h
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+int handle_enable (int argc, char *argv[], gboolean do_help);
+int handle_disable (int argc, char *argv[], gboolean do_help);
+int handle_reset (int argc, char *argv[], gboolean do_help);
+int handle_list (int argc, char *argv[], gboolean do_help);
+int handle_info (int argc, char *argv[], gboolean do_help);
+int handle_prefs (int argc, char *argv[], gboolean do_help);
+int handle_create (int argc, char *argv[], gboolean do_help);
+int handle_pack (int argc, char *argv[], gboolean do_help);
+int handle_install (int argc, char *argv[], gboolean do_help);
+int handle_uninstall (int argc, char *argv[], gboolean do_help);
+
+G_END_DECLS
diff --git a/subprojects/extensions-tool/src/common.h b/subprojects/extensions-tool/src/common.h
new file mode 100644
index 0000000..2b04484
--- /dev/null
+++ b/subprojects/extensions-tool/src/common.h
@@ -0,0 +1,73 @@
+/* common.h
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ TYPE_SYSTEM = 1,
+ TYPE_USER
+} ExtensionType;
+
+typedef enum {
+ STATE_ENABLED = 1,
+ STATE_DISABLED,
+ STATE_ERROR,
+ STATE_OUT_OF_DATE,
+ STATE_DOWNLOADING,
+ STATE_INITIALIZED,
+
+ STATE_UNINSTALLED = 99
+} ExtensionState;
+
+typedef enum {
+ DISPLAY_ONELINE,
+ DISPLAY_DETAILED
+} DisplayFormat;
+
+GOptionGroup *get_option_group (void);
+
+void show_help (GOptionContext *context,
+ const char *message);
+
+void print_extension_info (GVariantDict *info,
+ DisplayFormat format);
+
+GDBusProxy *get_shell_proxy (GError **error);
+GVariant *get_extension_property (GDBusProxy *proxy,
+ const char *uuid,
+ const char *property);
+
+GSettings *get_shell_settings (void);
+
+gboolean settings_list_add (GSettings *settings,
+ const char *key,
+ const char *value);
+gboolean settings_list_remove (GSettings *settings,
+ const char *key,
+ const char *value);
+
+gboolean file_delete_recursively (GFile *file,
+ GError **error);
+
+G_END_DECLS
diff --git a/subprojects/extensions-tool/src/gnome-extensions-tool.gresource.xml b/subprojects/extensions-tool/src/gnome-extensions-tool.gresource.xml
new file mode 100644
index 0000000..0db87c3
--- /dev/null
+++ b/subprojects/extensions-tool/src/gnome-extensions-tool.gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/extensions-tool">
+ <file>templates/00-plain.desktop</file>
+ <file>templates/indicator.desktop</file>
+ <file>templates/indicator/extension.js</file>
+ <file>templates/indicator/stylesheet.css</file>
+ <file>templates/plain/extension.js</file>
+ <file>templates/plain/stylesheet.css</file>
+ </gresource>
+</gresources>
diff --git a/subprojects/extensions-tool/src/main.c b/subprojects/extensions-tool/src/main.c
new file mode 100644
index 0000000..66a3476
--- /dev/null
+++ b/subprojects/extensions-tool/src/main.c
@@ -0,0 +1,412 @@
+/* main.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner@gnome.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+#include <locale.h>
+
+#include "config.h"
+#include "commands.h"
+#include "common.h"
+
+static const char *
+extension_state_to_string (ExtensionState state)
+{
+ switch (state)
+ {
+ case STATE_ENABLED:
+ return "ENABLED";
+ case STATE_DISABLED:
+ return "DISABLED";
+ case STATE_ERROR:
+ return "ERROR";
+ case STATE_OUT_OF_DATE:
+ return "OUT OF DATE";
+ case STATE_DOWNLOADING:
+ return "DOWNLOADING";
+ case STATE_INITIALIZED:
+ return "INITIALIZED";
+ case STATE_UNINSTALLED:
+ return "UNINSTALLED";
+ }
+ return "UNKNOWN";
+}
+
+static void
+print_nothing (const char *message)
+{
+}
+
+static gboolean
+quiet_cb (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ g_set_printerr_handler (print_nothing);
+ return TRUE;
+}
+
+GOptionGroup *
+get_option_group ()
+{
+ GOptionEntry entries[] = {
+ { .long_name = "quiet", .short_name = 'q',
+ .description = _("Do not print error messages"),
+ .arg = G_OPTION_ARG_CALLBACK, .arg_data = &quiet_cb,
+ .flags = G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_IN_MAIN },
+ { NULL }
+ };
+ GOptionGroup *group;
+
+ group = g_option_group_new ("Common", "common options", "common options", NULL, NULL);
+ g_option_group_add_entries (group, entries);
+
+ return group;
+}
+
+void
+show_help (GOptionContext *context, const char *message)
+{
+ g_autofree char *help = NULL;
+
+ if (message)
+ g_printerr ("gnome-extensions: %s\n\n", message);
+
+ help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s", help);
+}
+
+GDBusProxy *
+get_shell_proxy (GError **error)
+{
+ return g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.gnome.Shell.Extensions",
+ "/org/gnome/Shell/Extensions",
+ "org.gnome.Shell.Extensions",
+ NULL,
+ error);
+}
+
+GSettings *
+get_shell_settings (void)
+{
+ g_autoptr (GSettingsSchema) schema = NULL;
+ GSettingsSchemaSource *schema_source;
+
+ schema_source = g_settings_schema_source_get_default ();
+ schema = g_settings_schema_source_lookup (schema_source,
+ "org.gnome.shell",
+ TRUE);
+
+ if (schema == NULL)
+ return NULL;
+
+ return g_settings_new_full (schema, NULL, NULL);
+}
+
+GVariant *
+get_extension_property (GDBusProxy *proxy,
+ const char *uuid,
+ const char *property)
+{
+ g_autoptr (GVariant) response = NULL;
+ g_autoptr (GVariant) asv = NULL;
+ g_autoptr (GVariantDict) info = NULL;
+ g_autoptr (GError) error = NULL;
+
+ response = g_dbus_proxy_call_sync (proxy,
+ "GetExtensionInfo",
+ g_variant_new ("(s)", uuid),
+ 0,
+ -1,
+ NULL,
+ &error);
+ if (response == NULL)
+ {
+ g_printerr (_("Failed to connect to GNOME Shell\n"));
+ return NULL;
+ }
+
+ asv = g_variant_get_child_value (response, 0);
+ info = g_variant_dict_new (asv);
+
+ if (!g_variant_dict_contains (info, "uuid"))
+ {
+ g_printerr (_("Extension “%s” doesn't exist\n"), uuid);
+ return NULL;
+ }
+
+ return g_variant_dict_lookup_value (info, property, NULL);
+}
+
+gboolean
+settings_list_add (GSettings *settings,
+ const char *key,
+ const char *value)
+{
+ g_auto(GStrv) list = NULL;
+ g_auto(GStrv) new_value = NULL;
+ guint n_values;
+ int i;
+
+ if (!g_settings_is_writable (settings, key))
+ return FALSE;
+
+ list = g_settings_get_strv (settings, key);
+
+ if (g_strv_contains ((const char **)list, value))
+ return TRUE;
+
+ n_values = g_strv_length (list);
+ new_value = g_new0 (char *, n_values + 2);
+ for (i = 0; i < n_values; i++)
+ new_value[i] = g_strdup (list[i]);
+ new_value[i] = g_strdup (value);
+
+ g_settings_set_strv (settings, key, (const char **)new_value);
+ g_settings_sync ();
+
+ return TRUE;
+}
+
+gboolean
+settings_list_remove (GSettings *settings,
+ const char *key,
+ const char *value)
+{
+ g_auto(GStrv) list = NULL;
+ g_auto(GStrv) new_value = NULL;
+ const char **s;
+ guint n_values;
+ int i;
+
+ if (!g_settings_is_writable (settings, key))
+ return FALSE;
+
+ list = g_settings_get_strv (settings, key);
+
+ if (!g_strv_contains ((const char **)list, value))
+ return TRUE;
+
+ n_values = g_strv_length (list);
+ new_value = g_new0 (char *, n_values);
+ i = 0;
+ for (s = (const char **)list; *s != NULL; s++)
+ if (!g_str_equal (*s, value))
+ new_value[i++] = g_strdup (*s);
+
+ g_settings_set_strv (settings, key, (const char **)new_value);
+ g_settings_sync ();
+
+ return TRUE;
+}
+
+void
+print_extension_info (GVariantDict *info,
+ DisplayFormat format)
+{
+ const char *uuid, *name, *desc, *path, *url, *author;
+ double state, version;
+
+ g_variant_dict_lookup (info, "uuid", "&s", &uuid);
+ g_print ("%s\n", uuid);
+
+ if (format == DISPLAY_ONELINE)
+ return;
+
+ g_variant_dict_lookup (info, "name", "&s", &name);
+ g_print (" %s: %s\n", _("Name"), name);
+
+ g_variant_dict_lookup (info, "description", "&s", &desc);
+ g_print (" %s: %s\n", _("Description"), desc);
+
+ g_variant_dict_lookup (info, "path", "&s", &path);
+ g_print (" %s: %s\n", _("Path"), path);
+
+ if (g_variant_dict_lookup (info, "url", "&s", &url))
+ g_print (" %s: %s\n", _("URL"), url);
+
+ if (g_variant_dict_lookup (info, "original-author", "&s", &author))
+ g_print (" %s: %s\n", _("Original author"), author);
+
+ if (g_variant_dict_lookup (info, "version", "d", &version))
+ g_print (" %s: %.0f\n", _("Version"), version);
+
+ g_variant_dict_lookup (info, "state", "d", &state);
+ g_print (" %s: %s\n", _("State"), extension_state_to_string (state));
+}
+
+gboolean
+file_delete_recursively (GFile *file,
+ GError **error)
+{
+ g_autoptr (GFileEnumerator) file_enum = NULL;
+ GFile *child;
+
+ file_enum = g_file_enumerate_children (file,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (file_enum)
+ while (TRUE)
+ {
+ if (!g_file_enumerator_iterate (file_enum, NULL, &child, NULL, error))
+ return FALSE;
+
+ if (child == NULL)
+ break;
+
+ if (!file_delete_recursively (child, error))
+ return FALSE;
+ }
+
+ return g_file_delete (file, NULL, error);
+}
+
+
+static int
+handle_version (int argc, char *argv[], gboolean do_help)
+{
+ if (do_help || argc > 1)
+ {
+ if (!do_help)
+ g_printerr ("gnome-extensions: %s\n\n", _("“version” takes no arguments"));
+
+ g_printerr ("%s\n", _("Usage:"));
+ g_printerr (" gnome-extensions version\n");
+ g_printerr ("\n");
+ g_printerr ("%s\n", _("Print version information and exit."));
+
+ return do_help ? 0 : 2;
+ }
+
+ g_print ("%s\n", VERSION);
+
+ return 0;
+}
+
+static void
+usage (void)
+{
+ g_autofree char *help_command = NULL;
+
+ help_command = g_strdup_printf ("gnome-extensions help %s", _("COMMAND"));
+
+ g_printerr ("%s\n", _("Usage:"));
+ g_printerr (" gnome-extensions %s %s\n", _("COMMAND"), _("[ARGS…]"));
+ g_printerr ("\n");
+ g_printerr ("%s\n", _("Commands:"));
+ g_printerr (" help %s\n", _("Print help"));
+ g_printerr (" version %s\n", _("Print version"));
+ g_printerr (" enable %s\n", _("Enable extension"));
+ g_printerr (" disable %s\n", _("Disable extension"));
+ g_printerr (" reset %s\n", _("Reset extension"));
+ g_printerr (" uninstall %s\n", _("Uninstall extension"));
+ g_printerr (" list %s\n", _("List extensions"));
+ g_printerr (" info %s\n", _("Show extension info"));
+ g_printerr (" show %s\n", _("Show extension info"));
+ g_printerr (" prefs %s\n", _("Open extension preferences"));
+ g_printerr (" create %s\n", _("Create extension"));
+ g_printerr (" pack %s\n", _("Package extension"));
+ g_printerr (" install %s\n", _("Install extension bundle"));
+ g_printerr ("\n");
+ g_printerr (_("Use “%s” to get detailed help.\n"), help_command);
+}
+
+int
+main (int argc, char *argv[])
+{
+ const char *command;
+ gboolean do_help = FALSE;
+
+ setlocale (LC_ALL, "");
+ textdomain (GETTEXT_PACKAGE);
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+ if (argc < 2)
+ {
+ usage ();
+ return 1;
+ }
+
+ command = argv[1];
+ argc--;
+ argv++;
+
+ if (g_str_equal (command, "help"))
+ {
+ if (argc == 1)
+ {
+ usage ();
+ return 0;
+ }
+ else
+ {
+ command = argv[1];
+ do_help = TRUE;
+ }
+ }
+ else if (g_str_equal (command, "--help"))
+ {
+ usage ();
+ return 0;
+ }
+ else if (g_str_equal (command, "--version"))
+ {
+ command = "version";
+ }
+
+ if (g_str_equal (command, "version"))
+ return handle_version (argc, argv, do_help);
+ else if (g_str_equal (command, "enable"))
+ return handle_enable (argc, argv, do_help);
+ else if (g_str_equal (command, "disable"))
+ return handle_disable (argc, argv, do_help);
+ else if (g_str_equal (command, "reset"))
+ return handle_reset (argc, argv, do_help);
+ else if (g_str_equal (command, "list"))
+ return handle_list (argc, argv, do_help);
+ else if (g_str_equal (command, "info"))
+ return handle_info (argc, argv, do_help);
+ else if (g_str_equal (command, "show"))
+ return handle_info (argc, argv, do_help);
+ else if (g_str_equal (command, "prefs"))
+ return handle_prefs (argc, argv, do_help);
+ else if (g_str_equal (command, "create"))
+ return handle_create (argc, argv, do_help);
+ else if (g_str_equal (command, "pack"))
+ return handle_pack (argc, argv, do_help);
+ else if (g_str_equal (command, "install"))
+ return handle_install (argc, argv, do_help);
+ else if (g_str_equal (command, "uninstall"))
+ return handle_uninstall (argc, argv, do_help);
+ else
+ usage ();
+
+ return 1;
+}
diff --git a/subprojects/extensions-tool/src/meson.build b/subprojects/extensions-tool/src/meson.build
new file mode 100644
index 0000000..a855fef
--- /dev/null
+++ b/subprojects/extensions-tool/src/meson.build
@@ -0,0 +1,37 @@
+config_h = configuration_data()
+config_h.set_quoted('GETTEXT_PACKAGE', package_name)
+config_h.set_quoted('VERSION', meson.project_version())
+config_h.set_quoted('LOCALEDIR', localedir)
+config_h.set('HAVE_BIND_TEXTDOMAIN_CODESET', cc.has_function('bind_textdomain_codeset'))
+configure_file(
+ output: 'config.h',
+ configuration: config_h,
+)
+
+sources = [
+ 'command-create.c',
+ 'command-disable.c',
+ 'command-enable.c',
+ 'command-info.c',
+ 'command-install.c',
+ 'command-list.c',
+ 'command-pack.c',
+ 'command-prefs.c',
+ 'command-reset.c',
+ 'command-uninstall.c',
+ 'main.c'
+]
+
+subdir('templates')
+
+resources = gnome.compile_resources('resources',
+ 'gnome-extensions-tool.gresource.xml',
+ source_dir: ['.', meson.current_build_dir()],
+ dependencies: template_deps,
+)
+
+executable('gnome-extensions',
+ sources, resources,
+ dependencies: [gio_dep, gio_unix_dep, autoar_dep, json_dep],
+ install: true
+)
diff --git a/subprojects/extensions-tool/src/templates/00-plain.desktop.in b/subprojects/extensions-tool/src/templates/00-plain.desktop.in
new file mode 100644
index 0000000..36ddf80
--- /dev/null
+++ b/subprojects/extensions-tool/src/templates/00-plain.desktop.in
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Plain
+Comment=An empty extension
+Path=plain
diff --git a/subprojects/extensions-tool/src/templates/indicator.desktop.in b/subprojects/extensions-tool/src/templates/indicator.desktop.in
new file mode 100644
index 0000000..1718e94
--- /dev/null
+++ b/subprojects/extensions-tool/src/templates/indicator.desktop.in
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Indicator
+Comment=Add an icon to the top bar
+Path=indicator
diff --git a/subprojects/extensions-tool/src/templates/indicator/extension.js b/subprojects/extensions-tool/src/templates/indicator/extension.js
new file mode 100644
index 0000000..9ed2c38
--- /dev/null
+++ b/subprojects/extensions-tool/src/templates/indicator/extension.js
@@ -0,0 +1,70 @@
+/* extension.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* exported init */
+
+const GETTEXT_DOMAIN = 'my-indicator-extension';
+
+const { GObject, St } = imports.gi;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Main = imports.ui.main;
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+
+const _ = ExtensionUtils.gettext;
+
+const Indicator = GObject.registerClass(
+class Indicator extends PanelMenu.Button {
+ _init() {
+ super._init(0.0, _('My Shiny Indicator'));
+
+ this.add_child(new St.Icon({
+ icon_name: 'face-smile-symbolic',
+ style_class: 'system-status-icon',
+ }));
+
+ let item = new PopupMenu.PopupMenuItem(_('Show Notification'));
+ item.connect('activate', () => {
+ Main.notify(_('Whatʼs up, folks?'));
+ });
+ this.menu.addMenuItem(item);
+ }
+});
+
+class Extension {
+ constructor(uuid) {
+ this._uuid = uuid;
+
+ ExtensionUtils.initTranslations(GETTEXT_DOMAIN);
+ }
+
+ enable() {
+ this._indicator = new Indicator();
+ Main.panel.addToStatusArea(this._uuid, this._indicator);
+ }
+
+ disable() {
+ this._indicator.destroy();
+ this._indicator = null;
+ }
+}
+
+function init(meta) {
+ return new Extension(meta.uuid);
+}
diff --git a/subprojects/extensions-tool/src/templates/indicator/stylesheet.css b/subprojects/extensions-tool/src/templates/indicator/stylesheet.css
new file mode 100644
index 0000000..37b93f2
--- /dev/null
+++ b/subprojects/extensions-tool/src/templates/indicator/stylesheet.css
@@ -0,0 +1 @@
+/* Add your custom extension styling here */
diff --git a/subprojects/extensions-tool/src/templates/meson.build b/subprojects/extensions-tool/src/templates/meson.build
new file mode 100644
index 0000000..d693bfa
--- /dev/null
+++ b/subprojects/extensions-tool/src/templates/meson.build
@@ -0,0 +1,13 @@
+template_metas = [
+ '00-plain.desktop',
+ 'indicator.desktop',
+]
+template_deps = []
+foreach template : template_metas
+ template_deps += i18n.merge_file(
+ input: template + '.in',
+ output: template,
+ po_dir: po_dir,
+ type: 'desktop',
+ )
+endforeach
diff --git a/subprojects/extensions-tool/src/templates/plain/extension.js b/subprojects/extensions-tool/src/templates/plain/extension.js
new file mode 100644
index 0000000..64857af
--- /dev/null
+++ b/subprojects/extensions-tool/src/templates/plain/extension.js
@@ -0,0 +1,34 @@
+/* extension.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* exported init */
+
+class Extension {
+ constructor() {
+ }
+
+ enable() {
+ }
+
+ disable() {
+ }
+}
+
+function init() {
+ return new Extension();
+}
diff --git a/subprojects/extensions-tool/src/templates/plain/stylesheet.css b/subprojects/extensions-tool/src/templates/plain/stylesheet.css
new file mode 100644
index 0000000..37b93f2
--- /dev/null
+++ b/subprojects/extensions-tool/src/templates/plain/stylesheet.css
@@ -0,0 +1 @@
+/* Add your custom extension styling here */
diff --git a/subprojects/gvc/.gitignore b/subprojects/gvc/.gitignore
new file mode 100644
index 0000000..7d2ebe7
--- /dev/null
+++ b/subprojects/gvc/.gitignore
@@ -0,0 +1,11 @@
+.deps/
+.libs/
+.dirstamp
+Makefile.in
+Makefile
+*.la
+*.lo
+*.o
+*.gir
+*.typelib
+test-audio-device-selection
diff --git a/subprojects/gvc/.gitlab-ci.yml b/subprojects/gvc/.gitlab-ci.yml
new file mode 100644
index 0000000..447a655
--- /dev/null
+++ b/subprojects/gvc/.gitlab-ci.yml
@@ -0,0 +1,16 @@
+stages:
+- test
+
+build-fedora:
+ image: fedora:latest
+ stage: test
+ before_script:
+ - dnf install -y redhat-rpm-config gcc clang meson pulseaudio-libs-devel alsa-lib-devel gtk3-devel
+ script:
+ - cd .gitlab-ci
+ - meson _build
+ - ninja -C _build
+ - rm -rf _build
+ - CC=clang meson _build
+ - ninja -C _build
+
diff --git a/subprojects/gvc/.gitlab-ci/meson.build b/subprojects/gvc/.gitlab-ci/meson.build
new file mode 100644
index 0000000..d54e1dd
--- /dev/null
+++ b/subprojects/gvc/.gitlab-ci/meson.build
@@ -0,0 +1,23 @@
+project('gnome-volume-control-ci', 'c',
+ version: '1.0.0',
+ meson_version: '>= 0.47.0',
+ license: 'GPLv2+'
+)
+
+prefix = get_option('prefix')
+
+datadir = join_paths(prefix, get_option('datadir'))
+libdir = join_paths(prefix, get_option('libdir'))
+
+pkgdatadir = join_paths(datadir, meson.project_name())
+pkglibdir = join_paths(libdir, meson.project_name())
+
+libgvc = subproject('gvc',
+ default_options: [
+ 'package_name=' + meson.project_name(),
+ 'package_version=' + meson.project_version(),
+ 'pkgdatadir=' + pkgdatadir,
+ 'pkglibdir=' + pkglibdir,
+ 'alsa=true'
+ ]
+)
diff --git a/subprojects/gvc/.gitlab-ci/subprojects/gvc b/subprojects/gvc/.gitlab-ci/subprojects/gvc
new file mode 120000
index 0000000..6581736
--- /dev/null
+++ b/subprojects/gvc/.gitlab-ci/subprojects/gvc
@@ -0,0 +1 @@
+../../ \ No newline at end of file
diff --git a/subprojects/gvc/README.md b/subprojects/gvc/README.md
new file mode 100644
index 0000000..2fabe49
--- /dev/null
+++ b/subprojects/gvc/README.md
@@ -0,0 +1,12 @@
+# libgnome-volume-control
+
+libgnome-volume-control is a copy library that's supposed to be used as
+a git sub-module. If your project uses some of libgnome-volume-control's
+strings in a user-facing manner, don't forget to add those files to your
+POTFILES.in for translation.
+
+## Projects using libgnome-volume-control
+
+- [gnome-shell](https://gitlab.gnome.org/GNOME/gnome-shell)
+- [gnome-settings-daemon](https://gitlab.gnome.org/GNOME/gnome-settings-daemon)
+- [gnome-control-center](https://gitlab.gnome.org/GNOME/gnome-control-center)
diff --git a/subprojects/gvc/gvc-channel-map-private.h b/subprojects/gvc/gvc-channel-map-private.h
new file mode 100644
index 0000000..3949de3
--- /dev/null
+++ b/subprojects/gvc/gvc-channel-map-private.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_CHANNEL_MAP_PRIVATE_H
+#define __GVC_CHANNEL_MAP_PRIVATE_H
+
+#include <glib-object.h>
+#include <pulse/pulseaudio.h>
+
+G_BEGIN_DECLS
+
+GvcChannelMap * gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *map);
+const pa_channel_map * gvc_channel_map_get_pa_channel_map (const GvcChannelMap *map);
+
+void gvc_channel_map_volume_changed (GvcChannelMap *map,
+ const pa_cvolume *cv,
+ gboolean set);
+const pa_cvolume * gvc_channel_map_get_cvolume (const GvcChannelMap *map);
+
+G_END_DECLS
+
+#endif /* __GVC_CHANNEL_MAP_PRIVATE_H */
diff --git a/subprojects/gvc/gvc-channel-map.c b/subprojects/gvc/gvc-channel-map.c
new file mode 100644
index 0000000..688a451
--- /dev/null
+++ b/subprojects/gvc/gvc-channel-map.c
@@ -0,0 +1,246 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-channel-map.h"
+#include "gvc-channel-map-private.h"
+
+struct GvcChannelMapPrivate
+{
+ pa_channel_map pa_map;
+ gboolean pa_volume_is_set;
+ pa_cvolume pa_volume;
+ gdouble extern_volume[NUM_TYPES]; /* volume, balance, fade, lfe */
+ gboolean can_balance;
+ gboolean can_fade;
+};
+
+enum {
+ VOLUME_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gvc_channel_map_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcChannelMap, gvc_channel_map, G_TYPE_OBJECT)
+
+guint
+gvc_channel_map_get_num_channels (const GvcChannelMap *map)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), 0);
+
+ if (!pa_channel_map_valid(&map->priv->pa_map))
+ return 0;
+
+ return map->priv->pa_map.channels;
+}
+
+const gdouble *
+gvc_channel_map_get_volume (GvcChannelMap *map)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
+
+ if (!pa_channel_map_valid(&map->priv->pa_map))
+ return NULL;
+
+ map->priv->extern_volume[VOLUME] = (gdouble) pa_cvolume_max (&map->priv->pa_volume);
+ if (gvc_channel_map_can_balance (map))
+ map->priv->extern_volume[BALANCE] = (gdouble) pa_cvolume_get_balance (&map->priv->pa_volume, &map->priv->pa_map);
+ else
+ map->priv->extern_volume[BALANCE] = 0;
+ if (gvc_channel_map_can_fade (map))
+ map->priv->extern_volume[FADE] = (gdouble) pa_cvolume_get_fade (&map->priv->pa_volume, &map->priv->pa_map);
+ else
+ map->priv->extern_volume[FADE] = 0;
+ if (gvc_channel_map_has_lfe (map))
+ map->priv->extern_volume[LFE] = (gdouble) pa_cvolume_get_position (&map->priv->pa_volume, &map->priv->pa_map, PA_CHANNEL_POSITION_LFE);
+ else
+ map->priv->extern_volume[LFE] = 0;
+
+ return map->priv->extern_volume;
+}
+
+gboolean
+gvc_channel_map_can_balance (const GvcChannelMap *map)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+ return map->priv->can_balance;
+}
+
+gboolean
+gvc_channel_map_can_fade (const GvcChannelMap *map)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+ return map->priv->can_fade;
+}
+
+const char *
+gvc_channel_map_get_mapping (const GvcChannelMap *map)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
+
+ if (!pa_channel_map_valid(&map->priv->pa_map))
+ return NULL;
+
+ return pa_channel_map_to_pretty_name (&map->priv->pa_map);
+}
+
+/**
+ * gvc_channel_map_has_position: (skip)
+ * @map:
+ * @position:
+ *
+ * Returns:
+ */
+gboolean
+gvc_channel_map_has_position (const GvcChannelMap *map,
+ pa_channel_position_t position)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+ return pa_channel_map_has_position (&(map->priv->pa_map), position);
+}
+
+const pa_channel_map *
+gvc_channel_map_get_pa_channel_map (const GvcChannelMap *map)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
+
+ if (!pa_channel_map_valid(&map->priv->pa_map))
+ return NULL;
+
+ return &map->priv->pa_map;
+}
+
+const pa_cvolume *
+gvc_channel_map_get_cvolume (const GvcChannelMap *map)
+{
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
+
+ if (!pa_channel_map_valid(&map->priv->pa_map))
+ return NULL;
+
+ return &map->priv->pa_volume;
+}
+
+static void
+gvc_channel_map_class_init (GvcChannelMapClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = gvc_channel_map_finalize;
+
+ signals [VOLUME_CHANGED] =
+ g_signal_new ("volume-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+void
+gvc_channel_map_volume_changed (GvcChannelMap *map,
+ const pa_cvolume *cv,
+ gboolean set)
+{
+ g_return_if_fail (GVC_IS_CHANNEL_MAP (map));
+ g_return_if_fail (cv != NULL);
+ g_return_if_fail (pa_cvolume_compatible_with_channel_map(cv, &map->priv->pa_map));
+
+ if (pa_cvolume_equal(cv, &map->priv->pa_volume))
+ return;
+
+ map->priv->pa_volume = *cv;
+
+ if (map->priv->pa_volume_is_set == FALSE) {
+ map->priv->pa_volume_is_set = TRUE;
+ return;
+ }
+ g_signal_emit (map, signals[VOLUME_CHANGED], 0, set);
+}
+
+static void
+gvc_channel_map_init (GvcChannelMap *map)
+{
+ map->priv = gvc_channel_map_get_instance_private (map);
+ map->priv->pa_volume_is_set = FALSE;
+}
+
+static void
+gvc_channel_map_finalize (GObject *object)
+{
+ GvcChannelMap *channel_map;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_CHANNEL_MAP (object));
+
+ channel_map = GVC_CHANNEL_MAP (object);
+
+ g_return_if_fail (channel_map->priv != NULL);
+
+ G_OBJECT_CLASS (gvc_channel_map_parent_class)->finalize (object);
+}
+
+GvcChannelMap *
+gvc_channel_map_new (void)
+{
+ GObject *map;
+ map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL);
+ return GVC_CHANNEL_MAP (map);
+}
+
+static void
+set_from_pa_map (GvcChannelMap *map,
+ const pa_channel_map *pa_map)
+{
+ g_assert (pa_channel_map_valid(pa_map));
+
+ map->priv->can_balance = pa_channel_map_can_balance (pa_map);
+ map->priv->can_fade = pa_channel_map_can_fade (pa_map);
+
+ map->priv->pa_map = *pa_map;
+ pa_cvolume_set(&map->priv->pa_volume, pa_map->channels, PA_VOLUME_NORM);
+}
+
+GvcChannelMap *
+gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *pa_map)
+{
+ GObject *map;
+ map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL);
+
+ set_from_pa_map (GVC_CHANNEL_MAP (map), pa_map);
+
+ return GVC_CHANNEL_MAP (map);
+}
diff --git a/subprojects/gvc/gvc-channel-map.h b/subprojects/gvc/gvc-channel-map.h
new file mode 100644
index 0000000..85c5772
--- /dev/null
+++ b/subprojects/gvc/gvc-channel-map.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_CHANNEL_MAP_H
+#define __GVC_CHANNEL_MAP_H
+
+#include <glib-object.h>
+#include <gvc-pulseaudio-fake.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_CHANNEL_MAP (gvc_channel_map_get_type ())
+#define GVC_CHANNEL_MAP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMap))
+#define GVC_CHANNEL_MAP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass))
+#define GVC_IS_CHANNEL_MAP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_CHANNEL_MAP))
+#define GVC_IS_CHANNEL_MAP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_MAP))
+#define GVC_CHANNEL_MAP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass))
+
+typedef struct GvcChannelMapPrivate GvcChannelMapPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GvcChannelMapPrivate *priv;
+} GvcChannelMap;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (*volume_changed) (GvcChannelMap *channel_map, gboolean set);
+} GvcChannelMapClass;
+
+enum {
+ VOLUME,
+ BALANCE,
+ FADE,
+ LFE,
+ NUM_TYPES
+};
+
+GType gvc_channel_map_get_type (void);
+
+GvcChannelMap * gvc_channel_map_new (void);
+guint gvc_channel_map_get_num_channels (const GvcChannelMap *map);
+const gdouble * gvc_channel_map_get_volume (GvcChannelMap *map);
+gboolean gvc_channel_map_can_balance (const GvcChannelMap *map);
+gboolean gvc_channel_map_can_fade (const GvcChannelMap *map);
+gboolean gvc_channel_map_has_position (const GvcChannelMap *map,
+ pa_channel_position_t position);
+#define gvc_channel_map_has_lfe(x) gvc_channel_map_has_position (x, PA_CHANNEL_POSITION_LFE)
+
+const char * gvc_channel_map_get_mapping (const GvcChannelMap *map);
+
+G_END_DECLS
+
+#endif /* __GVC_CHANNEL_MAP_H */
diff --git a/subprojects/gvc/gvc-mixer-card-private.h b/subprojects/gvc/gvc-mixer-card-private.h
new file mode 100644
index 0000000..e190f7f
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-card-private.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_CARD_PRIVATE_H
+#define __GVC_MIXER_CARD_PRIVATE_H
+
+#include <pulse/pulseaudio.h>
+#include "gvc-mixer-card.h"
+
+G_BEGIN_DECLS
+
+GvcMixerCard * gvc_mixer_card_new (pa_context *context,
+ guint index);
+pa_context * gvc_mixer_card_get_pa_context (GvcMixerCard *card);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_CARD_PRIVATE_H */
diff --git a/subprojects/gvc/gvc-mixer-card.c b/subprojects/gvc/gvc-mixer-card.c
new file mode 100644
index 0000000..39f59ca
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-card.c
@@ -0,0 +1,574 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ * Copyright (C) 2009 Bastien Nocera
+ * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-card.h"
+#include "gvc-mixer-card-private.h"
+
+static guint32 card_serial = 1;
+
+struct GvcMixerCardPrivate
+{
+ pa_context *pa_context;
+ guint id;
+ guint index;
+ char *name;
+ char *icon_name;
+ char *profile;
+ char *target_profile;
+ char *human_profile;
+ GList *profiles;
+ pa_operation *profile_op;
+ GList *ports;
+};
+
+enum
+{
+ PROP_0,
+ PROP_ID,
+ PROP_PA_CONTEXT,
+ PROP_INDEX,
+ PROP_NAME,
+ PROP_ICON_NAME,
+ PROP_PROFILE,
+ PROP_HUMAN_PROFILE,
+ N_PROPS
+};
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
+
+static void gvc_mixer_card_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerCard, gvc_mixer_card, G_TYPE_OBJECT)
+
+static guint32
+get_next_card_serial (void)
+{
+ guint32 serial;
+
+ serial = card_serial++;
+
+ if ((gint32)card_serial < 0) {
+ card_serial = 1;
+ }
+
+ return serial;
+}
+
+pa_context *
+gvc_mixer_card_get_pa_context (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0);
+ return card->priv->pa_context;
+}
+
+guint
+gvc_mixer_card_get_index (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0);
+ return card->priv->index;
+}
+
+guint
+gvc_mixer_card_get_id (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0);
+ return card->priv->id;
+}
+
+const char *
+gvc_mixer_card_get_name (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
+ return card->priv->name;
+}
+
+gboolean
+gvc_mixer_card_set_name (GvcMixerCard *card,
+ const char *name)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+
+ g_free (card->priv->name);
+ card->priv->name = g_strdup (name);
+ g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_NAME]);
+
+ return TRUE;
+}
+
+const char *
+gvc_mixer_card_get_icon_name (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
+ return card->priv->icon_name;
+}
+
+gboolean
+gvc_mixer_card_set_icon_name (GvcMixerCard *card,
+ const char *icon_name)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+
+ g_free (card->priv->icon_name);
+ card->priv->icon_name = g_strdup (icon_name);
+ g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_ICON_NAME]);
+
+ return TRUE;
+}
+
+/**
+ * gvc_mixer_card_get_profile: (skip)
+ * @card:
+ *
+ * Returns:
+ */
+GvcMixerCardProfile *
+gvc_mixer_card_get_profile (GvcMixerCard *card)
+{
+ GList *l;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
+ g_return_val_if_fail (card->priv->profiles != NULL, NULL);
+
+ for (l = card->priv->profiles; l != NULL; l = l->next) {
+ GvcMixerCardProfile *p = l->data;
+ if (g_str_equal (card->priv->profile, p->profile)) {
+ return p;
+ }
+ }
+
+ g_assert_not_reached ();
+
+ return NULL;
+}
+
+gboolean
+gvc_mixer_card_set_profile (GvcMixerCard *card,
+ const char *profile)
+{
+ GList *l;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+ g_return_val_if_fail (card->priv->profiles != NULL, FALSE);
+
+ g_free (card->priv->profile);
+ card->priv->profile = g_strdup (profile);
+
+ g_free (card->priv->human_profile);
+ card->priv->human_profile = NULL;
+
+ for (l = card->priv->profiles; l != NULL; l = l->next) {
+ GvcMixerCardProfile *p = l->data;
+ if (g_str_equal (card->priv->profile, p->profile)) {
+ card->priv->human_profile = g_strdup (p->human_profile);
+ break;
+ }
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_PROFILE]);
+
+ return TRUE;
+}
+
+static void
+_pa_context_set_card_profile_by_index_cb (pa_context *context,
+ int success,
+ void *userdata)
+{
+ GvcMixerCard *card = GVC_MIXER_CARD (userdata);
+
+ g_assert (card->priv->target_profile);
+
+ if (success > 0) {
+ gvc_mixer_card_set_profile (card, card->priv->target_profile);
+ } else {
+ g_debug ("Failed to switch profile on '%s' from '%s' to '%s'",
+ card->priv->name,
+ card->priv->profile,
+ card->priv->target_profile);
+ }
+ g_free (card->priv->target_profile);
+ card->priv->target_profile = NULL;
+
+ pa_operation_unref (card->priv->profile_op);
+ card->priv->profile_op = NULL;
+}
+
+/**
+ * gvc_mixer_card_change_profile:
+ * @card: a #GvcMixerCard
+ * @profile: (allow-none): the profile to change to or %NULL.
+ *
+ * Change the profile in use on this card.
+ *
+ * Returns: %TRUE if profile successfully changed or already using this profile.
+ */
+gboolean
+gvc_mixer_card_change_profile (GvcMixerCard *card,
+ const char *profile)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+ g_return_val_if_fail (card->priv->profiles != NULL, FALSE);
+
+ /* Same profile, or already requested? */
+ if (g_strcmp0 (card->priv->profile, profile) == 0)
+ return TRUE;
+ if (g_strcmp0 (profile, card->priv->target_profile) == 0)
+ return TRUE;
+ if (card->priv->profile_op != NULL) {
+ pa_operation_cancel (card->priv->profile_op);
+ pa_operation_unref (card->priv->profile_op);
+ card->priv->profile_op = NULL;
+ }
+
+ if (card->priv->profile != NULL) {
+ g_free (card->priv->target_profile);
+ card->priv->target_profile = g_strdup (profile);
+
+ card->priv->profile_op = pa_context_set_card_profile_by_index (card->priv->pa_context,
+ card->priv->index,
+ card->priv->target_profile,
+ _pa_context_set_card_profile_by_index_cb,
+ card);
+
+ if (card->priv->profile_op == NULL) {
+ g_warning ("pa_context_set_card_profile_by_index() failed");
+ return FALSE;
+ }
+ } else {
+ g_assert (card->priv->human_profile == NULL);
+ card->priv->profile = g_strdup (profile);
+ }
+
+ return TRUE;
+}
+
+/**
+ * gvc_mixer_card_get_profiles:
+ *
+ * Return value: (transfer none) (element-type GvcMixerCardProfile):
+ */
+const GList *
+gvc_mixer_card_get_profiles (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
+ return card->priv->profiles;
+}
+
+/**
+ * gvc_mixer_card_get_ports:
+ *
+ * Return value: (transfer none) (element-type GvcMixerCardPort):
+ */
+const GList *
+gvc_mixer_card_get_ports (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
+ return card->priv->ports;
+}
+
+/**
+ * gvc_mixer_card_profile_compare:
+ *
+ * Return value: 1 if @a has a higher priority, -1 if @b has a higher
+ * priority, 0 if @a and @b have the same priority.
+ */
+int
+gvc_mixer_card_profile_compare (GvcMixerCardProfile *a,
+ GvcMixerCardProfile *b)
+{
+ if (a->priority == b->priority)
+ return 0;
+ if (a->priority > b->priority)
+ return 1;
+ return -1;
+}
+
+/**
+ * gvc_mixer_card_set_profiles:
+ * @profiles: (transfer full) (element-type GvcMixerCardProfile):
+ */
+gboolean
+gvc_mixer_card_set_profiles (GvcMixerCard *card,
+ GList *profiles)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+ g_return_val_if_fail (card->priv->profiles == NULL, FALSE);
+
+ card->priv->profiles = g_list_sort (profiles, (GCompareFunc) gvc_mixer_card_profile_compare);
+
+ return TRUE;
+}
+
+/**
+ * gvc_mixer_card_get_gicon:
+ * @card:
+ *
+ * Return value: (transfer full):
+ */
+GIcon *
+gvc_mixer_card_get_gicon (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
+
+ if (card->priv->icon_name == NULL)
+ return NULL;
+
+ return g_themed_icon_new_with_default_fallbacks (card->priv->icon_name);
+}
+
+static void
+free_port (GvcMixerCardPort *port)
+{
+ g_free (port->port);
+ g_free (port->human_port);
+ g_free (port->icon_name);
+ g_list_free (port->profiles);
+
+ g_free (port);
+}
+
+/**
+ * gvc_mixer_card_set_ports:
+ * @ports: (transfer full) (element-type GvcMixerCardPort):
+ */
+gboolean
+gvc_mixer_card_set_ports (GvcMixerCard *card,
+ GList *ports)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+ g_return_val_if_fail (card->priv->ports == NULL, FALSE);
+
+ g_list_free_full (card->priv->ports, (GDestroyNotify) free_port);
+ card->priv->ports = ports;
+
+ return TRUE;
+}
+
+static void
+gvc_mixer_card_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerCard *self = GVC_MIXER_CARD (object);
+
+ switch (prop_id) {
+ case PROP_PA_CONTEXT:
+ self->priv->pa_context = g_value_get_pointer (value);
+ break;
+ case PROP_INDEX:
+ self->priv->index = g_value_get_ulong (value);
+ break;
+ case PROP_ID:
+ self->priv->id = g_value_get_ulong (value);
+ break;
+ case PROP_NAME:
+ gvc_mixer_card_set_name (self, g_value_get_string (value));
+ break;
+ case PROP_ICON_NAME:
+ gvc_mixer_card_set_icon_name (self, g_value_get_string (value));
+ break;
+ case PROP_PROFILE:
+ gvc_mixer_card_set_profile (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gvc_mixer_card_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerCard *self = GVC_MIXER_CARD (object);
+
+ switch (prop_id) {
+ case PROP_PA_CONTEXT:
+ g_value_set_pointer (value, self->priv->pa_context);
+ break;
+ case PROP_INDEX:
+ g_value_set_ulong (value, self->priv->index);
+ break;
+ case PROP_ID:
+ g_value_set_ulong (value, self->priv->id);
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, self->priv->name);
+ break;
+ case PROP_ICON_NAME:
+ g_value_set_string (value, self->priv->icon_name);
+ break;
+ case PROP_PROFILE:
+ g_value_set_string (value, self->priv->profile);
+ break;
+ case PROP_HUMAN_PROFILE:
+ g_value_set_string (value, self->priv->human_profile);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gvc_mixer_card_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ GvcMixerCard *self;
+
+ object = G_OBJECT_CLASS (gvc_mixer_card_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+ self = GVC_MIXER_CARD (object);
+
+ self->priv->id = get_next_card_serial ();
+
+ return object;
+}
+
+static void
+gvc_mixer_card_class_init (GvcMixerCardClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = gvc_mixer_card_constructor;
+ gobject_class->finalize = gvc_mixer_card_finalize;
+
+ gobject_class->set_property = gvc_mixer_card_set_property;
+ gobject_class->get_property = gvc_mixer_card_get_property;
+
+ obj_props[PROP_INDEX] = g_param_spec_ulong ("index",
+ "Index",
+ "The index for this card",
+ 0, G_MAXULONG, 0,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_ID] = g_param_spec_ulong ("id",
+ "id",
+ "The id for this card",
+ 0, G_MAXULONG, 0,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_PA_CONTEXT] = g_param_spec_pointer ("pa-context",
+ "PulseAudio context",
+ "The PulseAudio context for this card",
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_NAME] = g_param_spec_string ("name",
+ "Name",
+ "Name to display for this card",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_ICON_NAME] = g_param_spec_string ("icon-name",
+ "Icon Name",
+ "Name of icon to display for this card",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_PROFILE] = g_param_spec_string ("profile",
+ "Profile",
+ "Name of current profile for this card",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_HUMAN_PROFILE] = g_param_spec_string ("human-profile",
+ "Profile (Human readable)",
+ "Name of current profile for this card in human readable form",
+ NULL,
+ G_PARAM_READABLE|G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
+}
+
+static void
+gvc_mixer_card_init (GvcMixerCard *card)
+{
+ card->priv = gvc_mixer_card_get_instance_private (card);
+}
+
+GvcMixerCard *
+gvc_mixer_card_new (pa_context *context,
+ guint index)
+{
+ GObject *object;
+
+ object = g_object_new (GVC_TYPE_MIXER_CARD,
+ "index", index,
+ "pa-context", context,
+ NULL);
+ return GVC_MIXER_CARD (object);
+}
+
+static void
+free_profile (GvcMixerCardProfile *p)
+{
+ g_free (p->profile);
+ g_free (p->human_profile);
+ g_free (p->status);
+ g_free (p);
+}
+
+static void
+gvc_mixer_card_finalize (GObject *object)
+{
+ GvcMixerCard *mixer_card;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_CARD (object));
+
+ mixer_card = GVC_MIXER_CARD (object);
+
+ g_return_if_fail (mixer_card->priv != NULL);
+
+ g_free (mixer_card->priv->name);
+ mixer_card->priv->name = NULL;
+
+ g_free (mixer_card->priv->icon_name);
+ mixer_card->priv->icon_name = NULL;
+
+ g_free (mixer_card->priv->target_profile);
+ mixer_card->priv->target_profile = NULL;
+
+ g_free (mixer_card->priv->profile);
+ mixer_card->priv->profile = NULL;
+
+ g_free (mixer_card->priv->human_profile);
+ mixer_card->priv->human_profile = NULL;
+
+ g_list_free_full (mixer_card->priv->profiles, (GDestroyNotify) free_profile);
+ mixer_card->priv->profiles = NULL;
+
+ g_list_free_full (mixer_card->priv->ports, (GDestroyNotify) free_port);
+ mixer_card->priv->ports = NULL;
+
+ G_OBJECT_CLASS (gvc_mixer_card_parent_class)->finalize (object);
+}
+
diff --git a/subprojects/gvc/gvc-mixer-card.h b/subprojects/gvc/gvc-mixer-card.h
new file mode 100644
index 0000000..814f8d4
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-card.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_CARD_H
+#define __GVC_MIXER_CARD_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_CARD (gvc_mixer_card_get_type ())
+#define GVC_MIXER_CARD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CARD, GvcMixerCard))
+#define GVC_MIXER_CARD_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CARD, GvcMixerCardClass))
+#define GVC_IS_MIXER_CARD(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CARD))
+#define GVC_IS_MIXER_CARD_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CARD))
+#define GVC_MIXER_CARD_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CARD, GvcMixerCardClass))
+
+typedef struct GvcMixerCardPrivate GvcMixerCardPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GvcMixerCardPrivate *priv;
+} GvcMixerCard;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ /* vtable */
+} GvcMixerCardClass;
+
+typedef struct
+{
+ char *profile;
+ char *human_profile;
+ char *status;
+ guint priority;
+ guint n_sinks, n_sources;
+} GvcMixerCardProfile;
+
+typedef struct
+{
+ char *port;
+ char *human_port;
+ char *icon_name;
+ guint priority;
+ gint available;
+ gint direction;
+ GList *profiles;
+} GvcMixerCardPort;
+
+GType gvc_mixer_card_get_type (void);
+
+guint gvc_mixer_card_get_id (GvcMixerCard *card);
+guint gvc_mixer_card_get_index (GvcMixerCard *card);
+const char * gvc_mixer_card_get_name (GvcMixerCard *card);
+const char * gvc_mixer_card_get_icon_name (GvcMixerCard *card);
+GvcMixerCardProfile * gvc_mixer_card_get_profile (GvcMixerCard *card);
+const GList * gvc_mixer_card_get_profiles (GvcMixerCard *card);
+const GList * gvc_mixer_card_get_ports (GvcMixerCard *card);
+gboolean gvc_mixer_card_change_profile (GvcMixerCard *card,
+ const char *profile);
+GIcon * gvc_mixer_card_get_gicon (GvcMixerCard *card);
+
+int gvc_mixer_card_profile_compare (GvcMixerCardProfile *a,
+ GvcMixerCardProfile *b);
+
+/* private */
+gboolean gvc_mixer_card_set_name (GvcMixerCard *card,
+ const char *name);
+gboolean gvc_mixer_card_set_icon_name (GvcMixerCard *card,
+ const char *name);
+gboolean gvc_mixer_card_set_profile (GvcMixerCard *card,
+ const char *profile);
+gboolean gvc_mixer_card_set_profiles (GvcMixerCard *card,
+ GList *profiles);
+gboolean gvc_mixer_card_set_ports (GvcMixerCard *stream,
+ GList *ports);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_CARD_H */
diff --git a/subprojects/gvc/gvc-mixer-control-private.h b/subprojects/gvc/gvc-mixer-control-private.h
new file mode 100644
index 0000000..ac79975
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-control-private.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_CONTROL_PRIVATE_H
+#define __GVC_MIXER_CONTROL_PRIVATE_H
+
+#include <glib-object.h>
+#include <pulse/pulseaudio.h>
+#include "gvc-mixer-stream.h"
+#include "gvc-mixer-card.h"
+
+G_BEGIN_DECLS
+
+pa_context * gvc_mixer_control_get_pa_context (GvcMixerControl *control);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_CONTROL_PRIVATE_H */
diff --git a/subprojects/gvc/gvc-mixer-control.c b/subprojects/gvc/gvc-mixer-control.c
new file mode 100644
index 0000000..b603b77
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-control.c
@@ -0,0 +1,3881 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006-2008 Lennart Poettering
+ * Copyright (C) 2008 Sjoerd Simons <sjoerd@luon.net>
+ * Copyright (C) 2008 William Jon McCann
+ * Copyright (C) 2012 Conor Curran
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/glib-mainloop.h>
+#include <pulse/ext-stream-restore.h>
+
+#ifdef HAVE_ALSA
+#include <alsa/asoundlib.h>
+#endif /* HAVE_ALSA */
+
+#include "gvc-mixer-control.h"
+#include "gvc-mixer-sink.h"
+#include "gvc-mixer-source.h"
+#include "gvc-mixer-sink-input.h"
+#include "gvc-mixer-source-output.h"
+#include "gvc-mixer-event-role.h"
+#include "gvc-mixer-card.h"
+#include "gvc-mixer-card-private.h"
+#include "gvc-channel-map-private.h"
+#include "gvc-mixer-control-private.h"
+#include "gvc-mixer-ui-device.h"
+
+#define RECONNECT_DELAY 5
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ N_PROPS
+};
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
+
+struct GvcMixerControlPrivate
+{
+ pa_glib_mainloop *pa_mainloop;
+ pa_mainloop_api *pa_api;
+ pa_context *pa_context;
+ guint server_protocol_version;
+ int n_outstanding;
+ guint reconnect_id;
+ char *name;
+
+ gboolean default_sink_is_set;
+ guint default_sink_id;
+ char *default_sink_name;
+ gboolean default_source_is_set;
+ guint default_source_id;
+ char *default_source_name;
+
+ gboolean event_sink_input_is_set;
+ guint event_sink_input_id;
+
+ GHashTable *all_streams;
+ GHashTable *sinks; /* fixed outputs */
+ GHashTable *sources; /* fixed inputs */
+ GHashTable *sink_inputs; /* routable output streams */
+ GHashTable *source_outputs; /* routable input streams */
+ GHashTable *clients;
+ GHashTable *cards;
+
+ GvcMixerStream *new_default_sink_stream; /* new default sink stream, used in gvc_mixer_control_set_default_sink () */
+ GvcMixerStream *new_default_source_stream; /* new default source stream, used in gvc_mixer_control_set_default_source () */
+
+ GHashTable *ui_outputs; /* UI visible outputs */
+ GHashTable *ui_inputs; /* UI visible inputs */
+
+ /* When we change profile on a device that is not the server default sink,
+ * it will jump back to the default sink set by the server to prevent the
+ * audio setup from being 'outputless'.
+ *
+ * All well and good but then when we get the new stream created for the
+ * new profile how do we know that this is the intended default or selected
+ * device the user wishes to use. */
+ guint profile_swapping_device_id;
+
+#ifdef HAVE_ALSA
+ int headset_card;
+ gboolean has_headsetmic;
+ gboolean has_headphonemic;
+ gboolean headset_plugged_in;
+ char *headphones_name;
+ char *headsetmic_name;
+ char *headphonemic_name;
+ char *internalspk_name;
+ char *internalmic_name;
+#endif /* HAVE_ALSA */
+
+ GvcMixerControlState state;
+};
+
+enum {
+ STATE_CHANGED,
+ STREAM_ADDED,
+ STREAM_REMOVED,
+ STREAM_CHANGED,
+ CARD_ADDED,
+ CARD_REMOVED,
+ DEFAULT_SINK_CHANGED,
+ DEFAULT_SOURCE_CHANGED,
+ ACTIVE_OUTPUT_UPDATE,
+ ACTIVE_INPUT_UPDATE,
+ OUTPUT_ADDED,
+ INPUT_ADDED,
+ OUTPUT_REMOVED,
+ INPUT_REMOVED,
+ AUDIO_DEVICE_SELECTION_NEEDED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gvc_mixer_control_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT)
+
+pa_context *
+gvc_mixer_control_get_pa_context (GvcMixerControl *control)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+ return control->priv->pa_context;
+}
+
+/**
+ * gvc_mixer_control_get_event_sink_input:
+ * @control:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerStream *
+gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
+{
+ GvcMixerStream *stream;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ stream = g_hash_table_lookup (control->priv->all_streams,
+ GUINT_TO_POINTER (control->priv->event_sink_input_id));
+
+ return stream;
+}
+
+static void
+gvc_mixer_control_stream_restore_cb (pa_context *c,
+ GvcMixerStream *new_stream,
+ const pa_ext_stream_restore_info *info,
+ GvcMixerControl *control)
+{
+ pa_operation *o;
+ pa_ext_stream_restore_info new_info;
+
+ if (new_stream == NULL)
+ return;
+
+ new_info.name = info->name;
+ new_info.channel_map = info->channel_map;
+ new_info.volume = info->volume;
+ new_info.mute = info->mute;
+
+ new_info.device = gvc_mixer_stream_get_name (new_stream);
+
+ o = pa_ext_stream_restore_write (control->priv->pa_context,
+ PA_UPDATE_REPLACE,
+ &new_info, 1,
+ TRUE, NULL, NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_ext_stream_restore_write() failed: %s",
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
+ return;
+ }
+
+ g_debug ("Changed default device for %s to %s", info->name, new_info.device);
+
+ pa_operation_unref (o);
+}
+
+static void
+gvc_mixer_control_stream_restore_sink_cb (pa_context *c,
+ const pa_ext_stream_restore_info *info,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = (GvcMixerControl *) userdata;
+ if (eol || info == NULL || !g_str_has_prefix(info->name, "sink-input-by"))
+ return;
+ gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_sink_stream, info, control);
+}
+
+static void
+gvc_mixer_control_stream_restore_source_cb (pa_context *c,
+ const pa_ext_stream_restore_info *info,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = (GvcMixerControl *) userdata;
+ if (eol || info == NULL || !g_str_has_prefix(info->name, "source-output-by"))
+ return;
+ gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_source_stream, info, control);
+}
+
+/**
+ * gvc_mixer_control_lookup_device_from_stream:
+ * @control:
+ * @stream:
+ *
+ * Returns: (transfer none): a #GvcUIDevice or %NULL
+ */
+GvcMixerUIDevice *
+gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control,
+ GvcMixerStream *stream)
+{
+ GList *devices, *d;
+ gboolean is_network_stream;
+ const GList *ports;
+ GvcMixerUIDevice *ret;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+
+ if (GVC_IS_MIXER_SOURCE (stream))
+ devices = g_hash_table_get_values (control->priv->ui_inputs);
+ else
+ devices = g_hash_table_get_values (control->priv->ui_outputs);
+
+ ret = NULL;
+ ports = gvc_mixer_stream_get_ports (stream);
+ is_network_stream = (ports == NULL);
+
+ for (d = devices; d != NULL; d = d->next) {
+ GvcMixerUIDevice *device = d->data;
+ guint stream_id = G_MAXUINT;
+
+ g_object_get (G_OBJECT (device),
+ "stream-id", &stream_id,
+ NULL);
+
+ if (is_network_stream &&
+ stream_id == gvc_mixer_stream_get_id (stream)) {
+ g_debug ("lookup device from stream - %s - it is a network_stream ",
+ gvc_mixer_ui_device_get_description (device));
+ ret = device;
+ break;
+ } else if (!is_network_stream) {
+ const GvcMixerStreamPort *port;
+ port = gvc_mixer_stream_get_port (stream);
+
+ if (stream_id == gvc_mixer_stream_get_id (stream) &&
+ g_strcmp0 (gvc_mixer_ui_device_get_port (device),
+ port->port) == 0) {
+ g_debug ("lookup-device-from-stream found device: device description '%s', device port = '%s', device stream id %i AND stream port = '%s' stream id '%u' and stream description '%s'",
+ gvc_mixer_ui_device_get_description (device),
+ gvc_mixer_ui_device_get_port (device),
+ stream_id,
+ port->port,
+ gvc_mixer_stream_get_id (stream),
+ gvc_mixer_stream_get_description (stream));
+ ret = device;
+ break;
+ }
+ }
+ }
+
+ g_debug ("gvc_mixer_control_lookup_device_from_stream - Could not find a device for stream '%s'",gvc_mixer_stream_get_description (stream));
+
+ g_list_free (devices);
+
+ return ret;
+}
+
+gboolean
+gvc_mixer_control_set_default_sink (GvcMixerControl *control,
+ GvcMixerStream *stream)
+{
+ pa_operation *o;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ g_debug ("about to set default sink on server");
+ o = pa_context_set_default_sink (control->priv->pa_context,
+ gvc_mixer_stream_get_name (stream),
+ NULL,
+ NULL);
+ if (o == NULL) {
+ g_warning ("pa_context_set_default_sink() failed: %s",
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
+ return FALSE;
+ }
+
+ pa_operation_unref (o);
+
+ control->priv->new_default_sink_stream = stream;
+ g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_sink_stream);
+
+ o = pa_ext_stream_restore_read (control->priv->pa_context,
+ gvc_mixer_control_stream_restore_sink_cb,
+ control);
+
+ if (o == NULL) {
+ g_warning ("pa_ext_stream_restore_read() failed: %s",
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
+ return FALSE;
+ }
+
+ pa_operation_unref (o);
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_control_set_default_source (GvcMixerControl *control,
+ GvcMixerStream *stream)
+{
+ GvcMixerUIDevice* input;
+ pa_operation *o;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ o = pa_context_set_default_source (control->priv->pa_context,
+ gvc_mixer_stream_get_name (stream),
+ NULL,
+ NULL);
+ if (o == NULL) {
+ g_warning ("pa_context_set_default_source() failed");
+ return FALSE;
+ }
+
+ pa_operation_unref (o);
+
+ control->priv->new_default_source_stream = stream;
+ g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_source_stream);
+
+ o = pa_ext_stream_restore_read (control->priv->pa_context,
+ gvc_mixer_control_stream_restore_source_cb,
+ control);
+
+ if (o == NULL) {
+ g_warning ("pa_ext_stream_restore_read() failed: %s",
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
+ return FALSE;
+ }
+
+ pa_operation_unref (o);
+
+ /* source change successful, update the UI. */
+ input = gvc_mixer_control_lookup_device_from_stream (control, stream);
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_INPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (input));
+
+ return TRUE;
+}
+
+/**
+ * gvc_mixer_control_get_default_sink:
+ * @control:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerStream *
+gvc_mixer_control_get_default_sink (GvcMixerControl *control)
+{
+ GvcMixerStream *stream;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ if (control->priv->default_sink_is_set) {
+ stream = g_hash_table_lookup (control->priv->all_streams,
+ GUINT_TO_POINTER (control->priv->default_sink_id));
+ } else {
+ stream = NULL;
+ }
+
+ return stream;
+}
+
+/**
+ * gvc_mixer_control_get_default_source:
+ * @control:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerStream *
+gvc_mixer_control_get_default_source (GvcMixerControl *control)
+{
+ GvcMixerStream *stream;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ if (control->priv->default_source_is_set) {
+ stream = g_hash_table_lookup (control->priv->all_streams,
+ GUINT_TO_POINTER (control->priv->default_source_id));
+ } else {
+ stream = NULL;
+ }
+
+ return stream;
+}
+
+static gpointer
+gvc_mixer_control_lookup_id (GHashTable *hash_table,
+ guint id)
+{
+ return g_hash_table_lookup (hash_table,
+ GUINT_TO_POINTER (id));
+}
+
+/**
+ * gvc_mixer_control_lookup_stream_id:
+ * @control:
+ * @id:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerStream *
+gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
+ guint id)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ return gvc_mixer_control_lookup_id (control->priv->all_streams, id);
+}
+
+/**
+ * gvc_mixer_control_lookup_card_id:
+ * @control:
+ * @id:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerCard *
+gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
+ guint id)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ return gvc_mixer_control_lookup_id (control->priv->cards, id);
+}
+
+/**
+ * gvc_mixer_control_lookup_output_id:
+ * @control:
+ * @id:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerUIDevice *
+gvc_mixer_control_lookup_output_id (GvcMixerControl *control,
+ guint id)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ return gvc_mixer_control_lookup_id (control->priv->ui_outputs, id);
+}
+
+/**
+ * gvc_mixer_control_lookup_input_id:
+ * @control:
+ * @id:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerUIDevice *
+gvc_mixer_control_lookup_input_id (GvcMixerControl *control,
+ guint id)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ return gvc_mixer_control_lookup_id (control->priv->ui_inputs, id);
+}
+
+/**
+ * gvc_mixer_control_get_stream_from_device:
+ * @control:
+ * @device:
+ *
+ * Returns: (transfer none):
+ */
+GvcMixerStream *
+gvc_mixer_control_get_stream_from_device (GvcMixerControl *control,
+ GvcMixerUIDevice *device)
+{
+ gint stream_id;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ stream_id = gvc_mixer_ui_device_get_stream_id (device);
+
+ if (stream_id == GVC_MIXER_UI_DEVICE_INVALID) {
+ g_debug ("gvc_mixer_control_get_stream_from_device - device has a null stream");
+ return NULL;
+ }
+ return gvc_mixer_control_lookup_stream_id (control, stream_id);
+}
+
+/**
+ * gvc_mixer_control_change_profile_on_selected_device:
+ * @control:
+ * @device:
+ * @profile: (allow-none): Can be %NULL if any profile present on this port is okay
+ *
+ * Returns: This method will attempt to swap the profile on the card of
+ * the device with given profile name. If successfull it will set the
+ * preferred profile on that device so as we know the next time the user
+ * moves to that device it should have this profile active.
+ */
+gboolean
+gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl *control,
+ GvcMixerUIDevice *device,
+ const gchar *profile)
+{
+ const gchar *best_profile;
+ GvcMixerCardProfile *current_profile;
+ GvcMixerCard *card;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);
+
+ g_object_get (G_OBJECT (device), "card", &card, NULL);
+ current_profile = gvc_mixer_card_get_profile (card);
+
+ if (current_profile)
+ best_profile = gvc_mixer_ui_device_get_best_profile (device, profile, current_profile->profile);
+ else
+ best_profile = profile;
+
+ g_assert (best_profile);
+
+ g_debug ("Selected '%s', moving to profile '%s' on card '%s' on stream id %i",
+ profile ? profile : "(any)", best_profile,
+ gvc_mixer_card_get_name (card),
+ gvc_mixer_ui_device_get_stream_id (device));
+
+ g_debug ("default sink name = %s and default sink id %u",
+ control->priv->default_sink_name,
+ control->priv->default_sink_id);
+
+ control->priv->profile_swapping_device_id = gvc_mixer_ui_device_get_id (device);
+
+ if (gvc_mixer_card_change_profile (card, best_profile)) {
+ gvc_mixer_ui_device_set_user_preferred_profile (device, best_profile);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * gvc_mixer_control_change_output:
+ * @control:
+ * @output:
+ * This method is called from the UI when the user selects a previously unselected device.
+ * - Firstly it queries the stream from the device.
+ * - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources)
+ * In the scenario of a NULL stream on the device
+ * - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device.
+ * - It then caches this device in control->priv->cached_desired_output_id so that when the update_sink triggered
+ * from when we attempt to change profile we will know exactly what device to highlight on that stream.
+ * - It attempts to swap the profile on the card from that device and returns.
+ * - Next, it handles network or bluetooth streams that only require their stream to be made the default.
+ * - Next it deals with port changes so if the stream's active port is not the same as the port on the device
+ * it will attempt to change the port on that stream to be same as the device. If this fails it will return.
+ * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active output device.
+ */
+void
+gvc_mixer_control_change_output (GvcMixerControl *control,
+ GvcMixerUIDevice* output)
+{
+ GvcMixerStream *stream;
+ GvcMixerStream *default_stream;
+ const GvcMixerStreamPort *active_port;
+ const gchar *output_port;
+
+ g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
+ g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (output));
+
+ g_debug ("control change output");
+
+ stream = gvc_mixer_control_get_stream_from_device (control, output);
+ if (stream == NULL) {
+ gvc_mixer_control_change_profile_on_selected_device (control,
+ output, NULL);
+ return;
+ }
+
+ /* Handle a network sink as a portless or cardless device */
+ if (!gvc_mixer_ui_device_has_ports (output)) {
+ g_debug ("Did we try to move to a software/bluetooth sink ?");
+ if (gvc_mixer_control_set_default_sink (control, stream)) {
+ /* sink change was successful, update the UI.*/
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_OUTPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (output));
+ }
+ else {
+ g_warning ("Failed to set default sink with stream from output %s",
+ gvc_mixer_ui_device_get_description (output));
+ }
+ return;
+ }
+
+ active_port = gvc_mixer_stream_get_port (stream);
+ output_port = gvc_mixer_ui_device_get_port (output);
+ /* First ensure the correct port is active on the sink */
+ if (g_strcmp0 (active_port->port, output_port) != 0) {
+ g_debug ("Port change, switch to = %s", output_port);
+ if (gvc_mixer_stream_change_port (stream, output_port) == FALSE) {
+ g_warning ("Could not change port !");
+ return;
+ }
+ }
+
+ default_stream = gvc_mixer_control_get_default_sink (control);
+
+ /* Finally if we are not on the correct stream, swap over. */
+ if (stream != default_stream) {
+ GvcMixerUIDevice* device;
+
+ g_debug ("Attempting to swap over to stream %s ",
+ gvc_mixer_stream_get_description (stream));
+ if (gvc_mixer_control_set_default_sink (control, stream)) {
+ device = gvc_mixer_control_lookup_device_from_stream (control, stream);
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_OUTPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (device));
+ } else {
+ /* If the move failed for some reason reset the UI. */
+ device = gvc_mixer_control_lookup_device_from_stream (control, default_stream);
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_OUTPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (device));
+ }
+ }
+}
+
+
+/**
+ * gvc_mixer_control_change_input:
+ * @control:
+ * @input:
+ * This method is called from the UI when the user selects a previously unselected device.
+ * - Firstly it queries the stream from the device.
+ * - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources)
+ * In the scenario of a NULL stream on the device
+ * - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device.
+ * - It then caches this device in control->priv->cached_desired_input_id so that when the update_source triggered
+ * from when we attempt to change profile we will know exactly what device to highlight on that stream.
+ * - It attempts to swap the profile on the card from that device and returns.
+ * - Next, it handles network or bluetooth streams that only require their stream to be made the default.
+ * - Next it deals with port changes so if the stream's active port is not the same as the port on the device
+ * it will attempt to change the port on that stream to be same as the device. If this fails it will return.
+ * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active input device.
+ */
+void
+gvc_mixer_control_change_input (GvcMixerControl *control,
+ GvcMixerUIDevice* input)
+{
+ GvcMixerStream *stream;
+ GvcMixerStream *default_stream;
+ const GvcMixerStreamPort *active_port;
+ const gchar *input_port;
+
+ g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
+ g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (input));
+
+ stream = gvc_mixer_control_get_stream_from_device (control, input);
+ if (stream == NULL) {
+ gvc_mixer_control_change_profile_on_selected_device (control,
+ input, NULL);
+ return;
+ }
+
+ /* Handle a network sink as a portless/cardless device */
+ if (!gvc_mixer_ui_device_has_ports (input)) {
+ g_debug ("Did we try to move to a software/bluetooth source ?");
+ if (! gvc_mixer_control_set_default_source (control, stream)) {
+ g_warning ("Failed to set default source with stream from input %s",
+ gvc_mixer_ui_device_get_description (input));
+ }
+ return;
+ }
+
+ active_port = gvc_mixer_stream_get_port (stream);
+ input_port = gvc_mixer_ui_device_get_port (input);
+ /* First ensure the correct port is active on the sink */
+ if (g_strcmp0 (active_port->port, input_port) != 0) {
+ g_debug ("Port change, switch to = %s", input_port);
+ if (gvc_mixer_stream_change_port (stream, input_port) == FALSE) {
+ g_warning ("Could not change port!");
+ return;
+ }
+ }
+
+ default_stream = gvc_mixer_control_get_default_source (control);
+
+ /* Finally if we are not on the correct stream, swap over. */
+ if (stream != default_stream) {
+ g_debug ("change-input - attempting to swap over to stream %s",
+ gvc_mixer_stream_get_description (stream));
+ gvc_mixer_control_set_default_source (control, stream);
+ }
+}
+
+
+static void
+listify_hash_values_hfunc (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSList **list = user_data;
+
+ *list = g_slist_prepend (*list, value);
+}
+
+static int
+gvc_name_collate (const char *namea,
+ const char *nameb)
+{
+ if (nameb == NULL && namea == NULL)
+ return 0;
+ if (nameb == NULL)
+ return 1;
+ if (namea == NULL)
+ return -1;
+
+ return g_utf8_collate (namea, nameb);
+}
+
+static int
+gvc_card_collate (GvcMixerCard *a,
+ GvcMixerCard *b)
+{
+ const char *namea;
+ const char *nameb;
+
+ g_return_val_if_fail (a == NULL || GVC_IS_MIXER_CARD (a), 0);
+ g_return_val_if_fail (b == NULL || GVC_IS_MIXER_CARD (b), 0);
+
+ namea = gvc_mixer_card_get_name (a);
+ nameb = gvc_mixer_card_get_name (b);
+
+ return gvc_name_collate (namea, nameb);
+}
+
+/**
+ * gvc_mixer_control_get_cards:
+ * @control:
+ *
+ * Returns: (transfer container) (element-type Gvc.MixerCard):
+ */
+GSList *
+gvc_mixer_control_get_cards (GvcMixerControl *control)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (control->priv->cards,
+ listify_hash_values_hfunc,
+ &retval);
+ return g_slist_sort (retval, (GCompareFunc) gvc_card_collate);
+}
+
+static int
+gvc_stream_collate (GvcMixerStream *a,
+ GvcMixerStream *b)
+{
+ const char *namea;
+ const char *nameb;
+
+ g_return_val_if_fail (a == NULL || GVC_IS_MIXER_STREAM (a), 0);
+ g_return_val_if_fail (b == NULL || GVC_IS_MIXER_STREAM (b), 0);
+
+ namea = gvc_mixer_stream_get_name (a);
+ nameb = gvc_mixer_stream_get_name (b);
+
+ return gvc_name_collate (namea, nameb);
+}
+
+/**
+ * gvc_mixer_control_get_streams:
+ * @control:
+ *
+ * Returns: (transfer container) (element-type Gvc.MixerStream):
+ */
+GSList *
+gvc_mixer_control_get_streams (GvcMixerControl *control)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (control->priv->all_streams,
+ listify_hash_values_hfunc,
+ &retval);
+ return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
+}
+
+/**
+ * gvc_mixer_control_get_sinks:
+ * @control:
+ *
+ * Returns: (transfer container) (element-type Gvc.MixerSink):
+ */
+GSList *
+gvc_mixer_control_get_sinks (GvcMixerControl *control)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (control->priv->sinks,
+ listify_hash_values_hfunc,
+ &retval);
+ return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
+}
+
+/**
+ * gvc_mixer_control_get_sources:
+ * @control:
+ *
+ * Returns: (transfer container) (element-type Gvc.MixerSource):
+ */
+GSList *
+gvc_mixer_control_get_sources (GvcMixerControl *control)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (control->priv->sources,
+ listify_hash_values_hfunc,
+ &retval);
+ return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
+}
+
+/**
+ * gvc_mixer_control_get_sink_inputs:
+ * @control:
+ *
+ * Returns: (transfer container) (element-type Gvc.MixerSinkInput):
+ */
+GSList *
+gvc_mixer_control_get_sink_inputs (GvcMixerControl *control)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (control->priv->sink_inputs,
+ listify_hash_values_hfunc,
+ &retval);
+ return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
+}
+
+/**
+ * gvc_mixer_control_get_source_outputs:
+ * @control:
+ *
+ * Returns: (transfer container) (element-type Gvc.MixerSourceOutput):
+ */
+GSList *
+gvc_mixer_control_get_source_outputs (GvcMixerControl *control)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (control->priv->source_outputs,
+ listify_hash_values_hfunc,
+ &retval);
+ return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
+}
+
+static void
+dec_outstanding (GvcMixerControl *control)
+{
+ if (control->priv->n_outstanding <= 0) {
+ return;
+ }
+
+ if (--control->priv->n_outstanding <= 0) {
+ control->priv->state = GVC_STATE_READY;
+ g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_READY);
+ }
+}
+
+GvcMixerControlState
+gvc_mixer_control_get_state (GvcMixerControl *control)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), GVC_STATE_CLOSED);
+
+ return control->priv->state;
+}
+
+static void
+on_default_source_port_notify (GObject *object,
+ GParamSpec *pspec,
+ GvcMixerControl *control)
+{
+ char *port;
+ GvcMixerUIDevice *input;
+
+ g_object_get (object, "port", &port, NULL);
+ input = gvc_mixer_control_lookup_device_from_stream (control,
+ GVC_MIXER_STREAM (object));
+
+ g_debug ("on_default_source_port_notify - moved to port '%s' which SHOULD ?? correspond to output '%s'",
+ port,
+ gvc_mixer_ui_device_get_description (input));
+
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_INPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (input));
+
+ g_free (port);
+}
+
+
+static void
+_set_default_source (GvcMixerControl *control,
+ GvcMixerStream *stream)
+{
+ guint new_id;
+
+ if (stream == NULL) {
+ control->priv->default_source_id = 0;
+ control->priv->default_source_is_set = FALSE;
+ g_signal_emit (control,
+ signals[DEFAULT_SOURCE_CHANGED],
+ 0,
+ PA_INVALID_INDEX);
+ return;
+ }
+
+ new_id = gvc_mixer_stream_get_id (stream);
+
+ if (control->priv->default_source_id != new_id) {
+ GvcMixerUIDevice *input;
+ control->priv->default_source_id = new_id;
+ control->priv->default_source_is_set = TRUE;
+ g_signal_emit (control,
+ signals[DEFAULT_SOURCE_CHANGED],
+ 0,
+ new_id);
+
+ if (control->priv->default_source_is_set) {
+ g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_source (control),
+ on_default_source_port_notify,
+ control);
+ }
+
+ g_signal_connect (stream,
+ "notify::port",
+ G_CALLBACK (on_default_source_port_notify),
+ control);
+
+ input = gvc_mixer_control_lookup_device_from_stream (control, stream);
+
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_INPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (input));
+ }
+}
+
+static void
+on_default_sink_port_notify (GObject *object,
+ GParamSpec *pspec,
+ GvcMixerControl *control)
+{
+ char *port;
+ GvcMixerUIDevice *output;
+
+ g_object_get (object, "port", &port, NULL);
+
+ output = gvc_mixer_control_lookup_device_from_stream (control,
+ GVC_MIXER_STREAM (object));
+ if (output != NULL) {
+ g_debug ("on_default_sink_port_notify - moved to port %s - which SHOULD correspond to output %s",
+ port,
+ gvc_mixer_ui_device_get_description (output));
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_OUTPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (output));
+ }
+ g_free (port);
+}
+
+static void
+_set_default_sink (GvcMixerControl *control,
+ GvcMixerStream *stream)
+{
+ guint new_id;
+
+ if (stream == NULL) {
+ /* Don't tell front-ends about an unset default
+ * sink if it's already unset */
+ if (control->priv->default_sink_is_set == FALSE)
+ return;
+ control->priv->default_sink_id = 0;
+ control->priv->default_sink_is_set = FALSE;
+ g_signal_emit (control,
+ signals[DEFAULT_SINK_CHANGED],
+ 0,
+ PA_INVALID_INDEX);
+ return;
+ }
+
+ new_id = gvc_mixer_stream_get_id (stream);
+
+ if (control->priv->default_sink_id != new_id) {
+ GvcMixerUIDevice *output;
+ if (control->priv->default_sink_is_set) {
+ g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_sink (control),
+ on_default_sink_port_notify,
+ control);
+ }
+
+ control->priv->default_sink_id = new_id;
+
+ control->priv->default_sink_is_set = TRUE;
+ g_signal_emit (control,
+ signals[DEFAULT_SINK_CHANGED],
+ 0,
+ new_id);
+
+ g_signal_connect (stream,
+ "notify::port",
+ G_CALLBACK (on_default_sink_port_notify),
+ control);
+
+ output = gvc_mixer_control_lookup_device_from_stream (control, stream);
+
+ g_debug ("active_sink change");
+
+ g_signal_emit (G_OBJECT (control),
+ signals[ACTIVE_OUTPUT_UPDATE],
+ 0,
+ gvc_mixer_ui_device_get_id (output));
+ }
+}
+
+static gboolean
+_stream_has_name (gpointer key,
+ GvcMixerStream *stream,
+ const char *name)
+{
+ const char *t_name;
+
+ t_name = gvc_mixer_stream_get_name (stream);
+
+ if (t_name != NULL
+ && name != NULL
+ && strcmp (t_name, name) == 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GvcMixerStream *
+find_stream_for_name (GvcMixerControl *control,
+ const char *name)
+{
+ GvcMixerStream *stream;
+
+ stream = g_hash_table_find (control->priv->all_streams,
+ (GHRFunc)_stream_has_name,
+ (char *)name);
+ return stream;
+}
+
+static void
+update_default_source_from_name (GvcMixerControl *control,
+ const char *name)
+{
+ gboolean changed = FALSE;
+
+ if ((control->priv->default_source_name == NULL
+ && name != NULL)
+ || (control->priv->default_source_name != NULL
+ && name == NULL)
+ || (name != NULL && strcmp (control->priv->default_source_name, name) != 0)) {
+ changed = TRUE;
+ }
+
+ if (changed) {
+ GvcMixerStream *stream;
+
+ g_free (control->priv->default_source_name);
+ control->priv->default_source_name = g_strdup (name);
+
+ stream = find_stream_for_name (control, name);
+ _set_default_source (control, stream);
+ }
+}
+
+static void
+update_default_sink_from_name (GvcMixerControl *control,
+ const char *name)
+{
+ gboolean changed = FALSE;
+
+ if ((control->priv->default_sink_name == NULL
+ && name != NULL)
+ || (control->priv->default_sink_name != NULL
+ && name == NULL)
+ || (name != NULL && strcmp (control->priv->default_sink_name, name) != 0)) {
+ changed = TRUE;
+ }
+
+ if (changed) {
+ GvcMixerStream *stream;
+ g_free (control->priv->default_sink_name);
+ control->priv->default_sink_name = g_strdup (name);
+
+ stream = find_stream_for_name (control, name);
+ _set_default_sink (control, stream);
+ }
+}
+
+static void
+update_server (GvcMixerControl *control,
+ const pa_server_info *info)
+{
+ if (info->default_source_name != NULL) {
+ update_default_source_from_name (control, info->default_source_name);
+ }
+ if (info->default_sink_name != NULL) {
+ g_debug ("update server");
+ update_default_sink_from_name (control, info->default_sink_name);
+ }
+}
+
+static void
+remove_stream (GvcMixerControl *control,
+ GvcMixerStream *stream)
+{
+ guint id;
+
+ g_object_ref (stream);
+
+ id = gvc_mixer_stream_get_id (stream);
+
+ if (id == control->priv->default_sink_id) {
+ _set_default_sink (control, NULL);
+ } else if (id == control->priv->default_source_id) {
+ _set_default_source (control, NULL);
+ }
+
+ g_hash_table_remove (control->priv->all_streams,
+ GUINT_TO_POINTER (id));
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_REMOVED],
+ 0,
+ gvc_mixer_stream_get_id (stream));
+ g_object_unref (stream);
+}
+
+static void
+add_stream (GvcMixerControl *control,
+ GvcMixerStream *stream)
+{
+ g_hash_table_insert (control->priv->all_streams,
+ GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
+ stream);
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_ADDED],
+ 0,
+ gvc_mixer_stream_get_id (stream));
+}
+
+/* This method will match individual stream ports against its corresponding device
+ * It does this by:
+ * - iterates through our devices and finds the one where the card-id on the device is the same as the card-id on the stream
+ * and the port-name on the device is the same as the streamport-name.
+ * This should always find a match and is used exclusively by sync_devices().
+ */
+static gboolean
+match_stream_with_devices (GvcMixerControl *control,
+ GvcMixerStreamPort *stream_port,
+ GvcMixerStream *stream)
+{
+ GList *devices, *d;
+ guint stream_card_id;
+ guint stream_id;
+ gboolean in_possession = FALSE;
+
+ stream_id = gvc_mixer_stream_get_id (stream);
+ stream_card_id = gvc_mixer_stream_get_card_index (stream);
+
+ devices = g_hash_table_get_values (GVC_IS_MIXER_SOURCE (stream) ? control->priv->ui_inputs : control->priv->ui_outputs);
+
+ for (d = devices; d != NULL; d = d->next) {
+ GvcMixerUIDevice *device;
+ guint device_stream_id;
+ gchar *device_port_name;
+ gchar *origin;
+ gchar *description;
+ GvcMixerCard *card;
+ guint card_id;
+
+ device = d->data;
+ g_object_get (G_OBJECT (device),
+ "stream-id", &device_stream_id,
+ "card", &card,
+ "origin", &origin,
+ "description", &description,
+ "port-name", &device_port_name,
+ NULL);
+
+ if (card == NULL) {
+ if (device_stream_id == stream_id) {
+ g_debug ("Matched stream %u with card-less device '%s', with stream already setup",
+ stream_id, description);
+ in_possession = TRUE;
+ }
+ } else {
+ card_id = gvc_mixer_card_get_index (card);
+
+ g_debug ("Attempt to match_stream update_with_existing_outputs - Try description : '%s', origin : '%s', device port name : '%s', card : %p, AGAINST stream port: '%s', sink card id %i",
+ description,
+ origin,
+ device_port_name,
+ card,
+ stream_port->port,
+ stream_card_id);
+
+ if (stream_card_id == card_id &&
+ g_strcmp0 (device_port_name, stream_port->port) == 0) {
+ g_debug ("Match device with stream: We have a match with description: '%s', origin: '%s', cached already with device id %u, so set stream id to %i",
+ description,
+ origin,
+ gvc_mixer_ui_device_get_id (device),
+ stream_id);
+
+ g_object_set (G_OBJECT (device),
+ "stream-id", stream_id,
+ NULL);
+ in_possession = TRUE;
+ }
+ }
+
+ g_free (device_port_name);
+ g_free (origin);
+ g_free (description);
+
+ if (in_possession == TRUE)
+ break;
+ }
+
+ g_list_free (devices);
+ return in_possession;
+}
+
+/*
+ * This method attempts to match a sink or source with its relevant UI device.
+ * GvcMixerStream can represent both a sink or source.
+ * Using static card port introspection implies that we know beforehand what
+ * outputs and inputs are available to the user.
+ * But that does not mean that all of these inputs and outputs are available to be used.
+ * For instance we might be able to see that there is a HDMI port available but if
+ * we are on the default analog stereo output profile there is no valid sink for
+ * that HDMI device. We first need to change profile and when update_sink() is called
+ * only then can we match the new hdmi sink with its corresponding device.
+ *
+ * Firstly it checks to see if the incoming stream has no ports.
+ * - If a stream has no ports but has a valid card ID (bluetooth), it will attempt
+ * to match the device with the stream using the card id.
+ * - If a stream has no ports and no valid card id, it goes ahead and makes a new
+ * device (software/network devices are only detectable at the sink/source level)
+ * If the stream has ports it will match each port against the stream using match_stream_with_devices().
+ *
+ * This method should always find a match.
+ */
+static void
+sync_devices (GvcMixerControl *control,
+ GvcMixerStream* stream)
+{
+ /* Go through ports to see what outputs can be created. */
+ const GList *stream_ports;
+ const GList *n = NULL;
+ gboolean is_output = !GVC_IS_MIXER_SOURCE (stream);
+
+ stream_ports = gvc_mixer_stream_get_ports (stream);
+
+ if (stream_ports == NULL) {
+ GvcMixerUIDevice *device;
+ /* Bluetooth, no ports but a valid card */
+ if (gvc_mixer_stream_get_card_index (stream) != PA_INVALID_INDEX) {
+ GList *devices, *d;
+ gboolean in_possession = FALSE;
+
+ devices = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs);
+
+ for (d = devices; d != NULL; d = d->next) {
+ GvcMixerCard *card;
+ guint card_id;
+
+ device = d->data;
+
+ g_object_get (G_OBJECT (device),
+ "card", &card,
+ NULL);
+ card_id = gvc_mixer_card_get_index (card);
+ g_debug ("sync devices, device description - '%s', device card id - %i, stream description - %s, stream card id - %i",
+ gvc_mixer_ui_device_get_description (device),
+ card_id,
+ gvc_mixer_stream_get_description (stream),
+ gvc_mixer_stream_get_card_index (stream));
+ if (card_id == gvc_mixer_stream_get_card_index (stream)) {
+ in_possession = TRUE;
+ break;
+ }
+ }
+ g_list_free (devices);
+
+ if (!in_possession) {
+ g_warning ("Couldn't match the portless stream (with card) - '%s' is it an input ? -> %i, streams card id -> %i",
+ gvc_mixer_stream_get_description (stream),
+ GVC_IS_MIXER_SOURCE (stream),
+ gvc_mixer_stream_get_card_index (stream));
+ return;
+ }
+
+ g_object_set (G_OBJECT (device),
+ "stream-id", gvc_mixer_stream_get_id (stream),
+ "description", gvc_mixer_stream_get_description (stream),
+ "origin", "", /*Leave it empty for these special cases*/
+ "port-name", NULL,
+ "port-available", TRUE,
+ NULL);
+ } else { /* Network sink/source has no ports and no card. */
+ GObject *object;
+
+ object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
+ "stream-id", gvc_mixer_stream_get_id (stream),
+ "description", gvc_mixer_stream_get_description (stream),
+ "origin", "", /* Leave it empty for these special cases */
+ "port-name", NULL,
+ "port-available", TRUE,
+ NULL);
+ device = GVC_MIXER_UI_DEVICE (object);
+
+ g_hash_table_insert (is_output ? control->priv->ui_outputs : control->priv->ui_inputs,
+ GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)),
+ g_object_ref (device));
+
+ }
+ g_signal_emit (G_OBJECT (control),
+ signals[is_output ? OUTPUT_ADDED : INPUT_ADDED],
+ 0,
+ gvc_mixer_ui_device_get_id (device));
+
+ return;
+ }
+
+ /* Go ahead and make sure to match each port against a previously created device */
+ for (n = stream_ports; n != NULL; n = n->next) {
+
+ GvcMixerStreamPort *stream_port;
+ stream_port = n->data;
+
+ if (match_stream_with_devices (control, stream_port, stream))
+ continue;
+
+ g_warning ("Sync_devices: Failed to match stream id: %u, description: '%s', origin: '%s'",
+ gvc_mixer_stream_get_id (stream),
+ stream_port->human_port,
+ gvc_mixer_stream_get_description (stream));
+ }
+}
+
+static void
+set_icon_name_from_proplist (GvcMixerStream *stream,
+ pa_proplist *l,
+ const char *default_icon_name)
+{
+ const char *t;
+
+ if ((t = pa_proplist_gets (l, PA_PROP_DEVICE_ICON_NAME))) {
+ goto finish;
+ }
+
+ if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) {
+ goto finish;
+ }
+
+ if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) {
+ goto finish;
+ }
+
+ if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) {
+ goto finish;
+ }
+
+ if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
+
+ if (strcmp (t, "video") == 0 ||
+ strcmp (t, "phone") == 0) {
+ goto finish;
+ }
+
+ if (strcmp (t, "music") == 0) {
+ t = "audio";
+ goto finish;
+ }
+
+ if (strcmp (t, "game") == 0) {
+ t = "applications-games";
+ goto finish;
+ }
+
+ if (strcmp (t, "event") == 0) {
+ t = "dialog-information";
+ goto finish;
+ }
+ }
+
+ t = default_icon_name;
+
+ finish:
+ gvc_mixer_stream_set_icon_name (stream, t);
+}
+
+static GvcMixerStreamState
+translate_pa_state (pa_sink_state_t state) {
+ switch (state) {
+ case PA_SINK_RUNNING:
+ return GVC_STREAM_STATE_RUNNING;
+ case PA_SINK_IDLE:
+ return GVC_STREAM_STATE_IDLE;
+ case PA_SINK_SUSPENDED:
+ return GVC_STREAM_STATE_SUSPENDED;
+ case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
+ case PA_SINK_UNLINKED:
+ default:
+ return GVC_STREAM_STATE_INVALID;
+ }
+}
+
+/*
+ * Called when anything changes with a sink.
+ */
+static void
+update_sink (GvcMixerControl *control,
+ const pa_sink_info *info)
+{
+ GvcMixerStream *stream;
+ gboolean is_new;
+ pa_volume_t max_volume;
+ GvcChannelMap *map;
+ char map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
+#if 1
+ g_debug ("Updating sink: index=%u name='%s' description='%s' map='%s'",
+ info->index,
+ info->name,
+ info->description,
+ map_buff);
+#endif
+
+ map = NULL;
+ is_new = FALSE;
+ stream = g_hash_table_lookup (control->priv->sinks,
+ GUINT_TO_POINTER (info->index));
+
+ if (stream == NULL) {
+ GList *list = NULL;
+ guint i;
+
+ map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
+ stream = gvc_mixer_sink_new (control->priv->pa_context,
+ info->index,
+ map);
+
+ for (i = 0; i < info->n_ports; i++) {
+ GvcMixerStreamPort *port;
+
+ port = g_slice_new0 (GvcMixerStreamPort);
+ port->port = g_strdup (info->ports[i]->name);
+ port->human_port = g_strdup (info->ports[i]->description);
+ port->priority = info->ports[i]->priority;
+ port->available = info->ports[i]->available != PA_PORT_AVAILABLE_NO;
+
+ list = g_list_prepend (list, port);
+ }
+ gvc_mixer_stream_set_ports (stream, list);
+
+ g_object_unref (map);
+ is_new = TRUE;
+
+ } else if (gvc_mixer_stream_is_running (stream)) {
+ /* Ignore events if volume changes are outstanding */
+ g_debug ("Ignoring event, volume changes are outstanding");
+ return;
+ }
+
+ max_volume = pa_cvolume_max (&info->volume);
+ gvc_mixer_stream_set_name (stream, info->name);
+ gvc_mixer_stream_set_card_index (stream, info->card);
+ gvc_mixer_stream_set_description (stream, info->description);
+ set_icon_name_from_proplist (stream, info->proplist, "audio-card");
+ gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR));
+ gvc_mixer_stream_set_sysfs_path (stream, pa_proplist_gets (info->proplist, "sysfs.path"));
+ gvc_mixer_stream_set_volume (stream, (guint)max_volume);
+ gvc_mixer_stream_set_is_muted (stream, info->mute);
+ gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME));
+ gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
+ gvc_mixer_stream_set_state (stream, translate_pa_state (info->state));
+
+ /* Messy I know but to set the port everytime regardless of whether it has changed will cost us a
+ * port change notify signal which causes the frontend to resync.
+ * Only update the UI when something has changed. */
+ if (info->active_port != NULL) {
+ if (is_new)
+ gvc_mixer_stream_set_port (stream, info->active_port->name);
+ else {
+ const GvcMixerStreamPort *active_port;
+ active_port = gvc_mixer_stream_get_port (stream);
+ if (active_port == NULL ||
+ g_strcmp0 (active_port->port, info->active_port->name) != 0) {
+ g_debug ("update sink - apparently a port update");
+ gvc_mixer_stream_set_port (stream, info->active_port->name);
+ }
+ }
+ }
+
+ if (is_new) {
+ g_debug ("update sink - is new");
+
+ g_hash_table_insert (control->priv->sinks,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (stream));
+ add_stream (control, stream);
+ /* Always sink on a new stream to able to assign the right stream id
+ * to the appropriate outputs (multiple potential outputs per stream). */
+ sync_devices (control, stream);
+ } else {
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_CHANGED],
+ 0,
+ gvc_mixer_stream_get_id (stream));
+ }
+
+ /*
+ * When we change profile on a device that is not the server default sink,
+ * it will jump back to the default sink set by the server to prevent the audio setup from being 'outputless'.
+ * All well and good but then when we get the new stream created for the new profile how do we know
+ * that this is the intended default or selected device the user wishes to use.
+ * This is messy but it's the only reliable way that it can be done without ripping the whole thing apart.
+ */
+ if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) {
+ GvcMixerUIDevice *dev = NULL;
+ dev = gvc_mixer_control_lookup_output_id (control, control->priv->profile_swapping_device_id);
+ if (dev != NULL) {
+ /* now check to make sure this new stream is the same stream just matched and set on the device object */
+ if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) {
+ g_debug ("Looks like we profile swapped on a non server default sink");
+ gvc_mixer_control_set_default_sink (control, stream);
+ control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
+ }
+ }
+ }
+
+ if (control->priv->default_sink_name != NULL
+ && info->name != NULL
+ && strcmp (control->priv->default_sink_name, info->name) == 0) {
+ _set_default_sink (control, stream);
+ }
+
+ if (map == NULL)
+ map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream);
+
+ gvc_channel_map_volume_changed (map, &info->volume, FALSE);
+}
+
+static void
+update_source (GvcMixerControl *control,
+ const pa_source_info *info)
+{
+ GvcMixerStream *stream;
+ gboolean is_new;
+ pa_volume_t max_volume;
+
+#if 1
+ g_debug ("Updating source: index=%u name='%s' description='%s'",
+ info->index,
+ info->name,
+ info->description);
+#endif
+
+ /* completely ignore monitors, they're not real sources */
+ if (info->monitor_of_sink != PA_INVALID_INDEX) {
+ return;
+ }
+
+ is_new = FALSE;
+
+ stream = g_hash_table_lookup (control->priv->sources,
+ GUINT_TO_POINTER (info->index));
+ if (stream == NULL) {
+ GList *list = NULL;
+ guint i;
+ GvcChannelMap *map;
+
+ map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
+ stream = gvc_mixer_source_new (control->priv->pa_context,
+ info->index,
+ map);
+
+ for (i = 0; i < info->n_ports; i++) {
+ GvcMixerStreamPort *port;
+
+ port = g_slice_new0 (GvcMixerStreamPort);
+ port->port = g_strdup (info->ports[i]->name);
+ port->human_port = g_strdup (info->ports[i]->description);
+ port->priority = info->ports[i]->priority;
+ list = g_list_prepend (list, port);
+ }
+ gvc_mixer_stream_set_ports (stream, list);
+
+ g_object_unref (map);
+ is_new = TRUE;
+ } else if (gvc_mixer_stream_is_running (stream)) {
+ /* Ignore events if volume changes are outstanding */
+ g_debug ("Ignoring event, volume changes are outstanding");
+ return;
+ }
+
+ max_volume = pa_cvolume_max (&info->volume);
+
+ gvc_mixer_stream_set_name (stream, info->name);
+ gvc_mixer_stream_set_card_index (stream, info->card);
+ gvc_mixer_stream_set_description (stream, info->description);
+ set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
+ gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR));
+ gvc_mixer_stream_set_volume (stream, (guint)max_volume);
+ gvc_mixer_stream_set_is_muted (stream, info->mute);
+ gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME));
+ gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
+ g_debug ("update source");
+
+ if (info->active_port != NULL) {
+ if (is_new)
+ gvc_mixer_stream_set_port (stream, info->active_port->name);
+ else {
+ const GvcMixerStreamPort *active_port;
+ active_port = gvc_mixer_stream_get_port (stream);
+ if (active_port == NULL ||
+ g_strcmp0 (active_port->port, info->active_port->name) != 0) {
+ g_debug ("update source - apparently a port update");
+ gvc_mixer_stream_set_port (stream, info->active_port->name);
+ }
+ }
+ }
+
+ if (is_new) {
+ g_hash_table_insert (control->priv->sources,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (stream));
+ add_stream (control, stream);
+ sync_devices (control, stream);
+ } else {
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_CHANGED],
+ 0,
+ gvc_mixer_stream_get_id (stream));
+ }
+
+ if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) {
+ GvcMixerUIDevice *dev = NULL;
+
+ dev = gvc_mixer_control_lookup_input_id (control, control->priv->profile_swapping_device_id);
+
+ if (dev != NULL) {
+ /* now check to make sure this new stream is the same stream just matched and set on the device object */
+ if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) {
+ g_debug ("Looks like we profile swapped on a non server default source");
+ gvc_mixer_control_set_default_source (control, stream);
+ control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
+ }
+ }
+ }
+ if (control->priv->default_source_name != NULL
+ && info->name != NULL
+ && strcmp (control->priv->default_source_name, info->name) == 0) {
+ _set_default_source (control, stream);
+ }
+}
+
+static void
+set_is_event_stream_from_proplist (GvcMixerStream *stream,
+ pa_proplist *l)
+{
+ const char *t;
+ gboolean is_event_stream;
+
+ is_event_stream = FALSE;
+
+ if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
+ if (g_str_equal (t, "event"))
+ is_event_stream = TRUE;
+ }
+
+ gvc_mixer_stream_set_is_event_stream (stream, is_event_stream);
+}
+
+static void
+set_application_id_from_proplist (GvcMixerStream *stream,
+ pa_proplist *l)
+{
+ const char *t;
+
+ if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) {
+ gvc_mixer_stream_set_application_id (stream, t);
+ }
+}
+
+static void
+update_sink_input (GvcMixerControl *control,
+ const pa_sink_input_info *info)
+{
+ GvcMixerStream *stream;
+ gboolean is_new;
+ pa_volume_t max_volume;
+ const char *name;
+
+#if 0
+ g_debug ("Updating sink input: index=%u name='%s' client=%u sink=%u",
+ info->index,
+ info->name,
+ info->client,
+ info->sink);
+#endif
+
+ is_new = FALSE;
+
+ stream = g_hash_table_lookup (control->priv->sink_inputs,
+ GUINT_TO_POINTER (info->index));
+ if (stream == NULL) {
+ GvcChannelMap *map;
+ map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
+ stream = gvc_mixer_sink_input_new (control->priv->pa_context,
+ info->index,
+ map);
+ g_object_unref (map);
+ is_new = TRUE;
+ } else if (gvc_mixer_stream_is_running (stream)) {
+ /* Ignore events if volume changes are outstanding */
+ g_debug ("Ignoring event, volume changes are outstanding");
+ return;
+ }
+
+ max_volume = pa_cvolume_max (&info->volume);
+
+ name = (const char *)g_hash_table_lookup (control->priv->clients,
+ GUINT_TO_POINTER (info->client));
+ gvc_mixer_stream_set_name (stream, name);
+ gvc_mixer_stream_set_description (stream, info->name);
+
+ set_application_id_from_proplist (stream, info->proplist);
+ set_is_event_stream_from_proplist (stream, info->proplist);
+ set_icon_name_from_proplist (stream, info->proplist, "application-x-executable");
+ gvc_mixer_stream_set_volume (stream, (guint)max_volume);
+ gvc_mixer_stream_set_is_muted (stream, info->mute);
+ gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX);
+
+ if (is_new) {
+ g_hash_table_insert (control->priv->sink_inputs,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (stream));
+ add_stream (control, stream);
+ } else {
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_CHANGED],
+ 0,
+ gvc_mixer_stream_get_id (stream));
+ }
+}
+
+static void
+update_source_output (GvcMixerControl *control,
+ const pa_source_output_info *info)
+{
+ GvcMixerStream *stream;
+ gboolean is_new;
+ pa_volume_t max_volume;
+ const char *name;
+
+#if 1
+ g_debug ("Updating source output: index=%u name='%s' client=%u source=%u",
+ info->index,
+ info->name,
+ info->client,
+ info->source);
+#endif
+
+ is_new = FALSE;
+ stream = g_hash_table_lookup (control->priv->source_outputs,
+ GUINT_TO_POINTER (info->index));
+ if (stream == NULL) {
+ GvcChannelMap *map;
+ map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
+ stream = gvc_mixer_source_output_new (control->priv->pa_context,
+ info->index,
+ map);
+ g_object_unref (map);
+ is_new = TRUE;
+ }
+
+ name = (const char *)g_hash_table_lookup (control->priv->clients,
+ GUINT_TO_POINTER (info->client));
+
+ max_volume = pa_cvolume_max (&info->volume);
+
+ gvc_mixer_stream_set_name (stream, name);
+ gvc_mixer_stream_set_description (stream, info->name);
+ set_application_id_from_proplist (stream, info->proplist);
+ set_is_event_stream_from_proplist (stream, info->proplist);
+ gvc_mixer_stream_set_volume (stream, (guint)max_volume);
+ gvc_mixer_stream_set_is_muted (stream, info->mute);
+ set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
+
+ if (is_new) {
+ g_hash_table_insert (control->priv->source_outputs,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (stream));
+ add_stream (control, stream);
+ } else {
+ g_signal_emit (G_OBJECT (control),
+ signals[STREAM_CHANGED],
+ 0,
+ gvc_mixer_stream_get_id (stream));
+ }
+}
+
+static void
+update_client (GvcMixerControl *control,
+ const pa_client_info *info)
+{
+#if 1
+ g_debug ("Updating client: index=%u name='%s'",
+ info->index,
+ info->name);
+#endif
+ g_hash_table_insert (control->priv->clients,
+ GUINT_TO_POINTER (info->index),
+ g_strdup (info->name));
+}
+
+static char *
+card_num_streams_to_status (guint sinks,
+ guint sources)
+{
+ char *sinks_str;
+ char *sources_str;
+ char *ret;
+
+ if (sinks == 0 && sources == 0) {
+ /* translators:
+ * The device has been disabled */
+ return g_strdup (_("Disabled"));
+ }
+ if (sinks == 0) {
+ sinks_str = NULL;
+ } else {
+ /* translators:
+ * The number of sound outputs on a particular device */
+ sinks_str = g_strdup_printf (ngettext ("%u Output",
+ "%u Outputs",
+ sinks),
+ sinks);
+ }
+ if (sources == 0) {
+ sources_str = NULL;
+ } else {
+ /* translators:
+ * The number of sound inputs on a particular device */
+ sources_str = g_strdup_printf (ngettext ("%u Input",
+ "%u Inputs",
+ sources),
+ sources);
+ }
+ if (sources_str == NULL)
+ return sinks_str;
+ if (sinks_str == NULL)
+ return sources_str;
+ ret = g_strdup_printf ("%s / %s", sinks_str, sources_str);
+ g_free (sinks_str);
+ g_free (sources_str);
+ return ret;
+}
+
+/*
+ * A utility method to gather which card profiles are relevant to the port .
+ */
+static GList *
+determine_profiles_for_port (pa_card_port_info *port,
+ GList* card_profiles)
+{
+ guint i;
+ GList *supported_profiles = NULL;
+ GList *p;
+ for (i = 0; i < port->n_profiles; i++) {
+ for (p = card_profiles; p != NULL; p = p->next) {
+ GvcMixerCardProfile *prof;
+ prof = p->data;
+ if (g_strcmp0 (port->profiles[i]->name, prof->profile) == 0)
+ supported_profiles = g_list_append (supported_profiles, prof);
+ }
+ }
+ g_debug ("%i profiles supported on port %s",
+ g_list_length (supported_profiles),
+ port->description);
+ return g_list_sort (supported_profiles, (GCompareFunc) gvc_mixer_card_profile_compare);
+}
+
+static gboolean
+is_card_port_an_output (GvcMixerCardPort* port)
+{
+ return port->direction == PA_DIRECTION_OUTPUT ? TRUE : FALSE;
+}
+
+/*
+ * This method will create a ui device for the given port.
+ */
+static void
+create_ui_device_from_port (GvcMixerControl* control,
+ GvcMixerCardPort* port,
+ GvcMixerCard* card)
+{
+ GvcMixerUIDeviceDirection direction;
+ GObject *object;
+ GvcMixerUIDevice *uidevice;
+ gboolean available = port->available != PA_PORT_AVAILABLE_NO;
+
+ direction = (is_card_port_an_output (port) == TRUE) ? UIDeviceOutput : UIDeviceInput;
+
+ object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
+ "type", (guint)direction,
+ "card", card,
+ "port-name", port->port,
+ "description", port->human_port,
+ "origin", gvc_mixer_card_get_name (card),
+ "port-available", available,
+ "icon-name", port->icon_name,
+ NULL);
+
+ uidevice = GVC_MIXER_UI_DEVICE (object);
+ gvc_mixer_ui_device_set_profiles (uidevice, port->profiles);
+
+ g_hash_table_insert (is_card_port_an_output (port) ? control->priv->ui_outputs : control->priv->ui_inputs,
+ GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (uidevice)),
+ uidevice);
+
+
+ if (available) {
+ g_signal_emit (G_OBJECT (control),
+ signals[is_card_port_an_output (port) ? OUTPUT_ADDED : INPUT_ADDED],
+ 0,
+ gvc_mixer_ui_device_get_id (uidevice));
+ }
+
+ g_debug ("create_ui_device_from_port, direction %u, description '%s', origin '%s', port available %i",
+ direction,
+ port->human_port,
+ gvc_mixer_card_get_name (card),
+ available);
+}
+
+/*
+ * This method will match up GvcMixerCardPorts with existing devices.
+ * A match is achieved if the device's card-id and the port's card-id are the same
+ * && the device's port-name and the card-port's port member are the same.
+ * A signal is then sent adding or removing that device from the UI depending on the availability of the port.
+ */
+static void
+match_card_port_with_existing_device (GvcMixerControl *control,
+ GvcMixerCardPort *card_port,
+ GvcMixerCard *card,
+ gboolean available)
+{
+ GList *d;
+ GList *devices;
+ GvcMixerUIDevice *device;
+ gboolean is_output = is_card_port_an_output (card_port);
+
+ devices = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs);
+
+ for (d = devices; d != NULL; d = d->next) {
+ GvcMixerCard *device_card;
+ gchar *device_port_name;
+
+ device = d->data;
+ g_object_get (G_OBJECT (device),
+ "card", &device_card,
+ "port-name", &device_port_name,
+ NULL);
+
+ if (g_strcmp0 (card_port->port, device_port_name) == 0 &&
+ device_card == card) {
+ g_debug ("Found the relevant device %s, update its port availability flag to %i, is_output %i",
+ device_port_name,
+ available,
+ is_output);
+ g_object_set (G_OBJECT (device),
+ "port-available", available, NULL);
+ g_signal_emit (G_OBJECT (control),
+ is_output ? signals[available ? OUTPUT_ADDED : OUTPUT_REMOVED] : signals[available ? INPUT_ADDED : INPUT_REMOVED],
+ 0,
+ gvc_mixer_ui_device_get_id (device));
+ }
+ g_free (device_port_name);
+ }
+
+ g_list_free (devices);
+}
+
+static void
+create_ui_device_from_card (GvcMixerControl *control,
+ GvcMixerCard *card)
+{
+ GObject *object;
+ GvcMixerUIDevice *in;
+ GvcMixerUIDevice *out;
+ const GList *profiles;
+
+ /* For now just create two devices and presume this device is multi directional
+ * Ensure to remove both on card removal (available to false by default) */
+ profiles = gvc_mixer_card_get_profiles (card);
+
+ g_debug ("Portless card just registered - %i", gvc_mixer_card_get_index (card));
+
+ object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
+ "type", UIDeviceInput,
+ "description", gvc_mixer_card_get_name (card),
+ "origin", "", /* Leave it empty for these special cases */
+ "port-name", NULL,
+ "port-available", FALSE,
+ "card", card,
+ NULL);
+ in = GVC_MIXER_UI_DEVICE (object);
+ gvc_mixer_ui_device_set_profiles (in, profiles);
+
+ g_hash_table_insert (control->priv->ui_inputs,
+ GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (in)),
+ g_object_ref (in));
+ object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
+ "type", UIDeviceOutput,
+ "description", gvc_mixer_card_get_name (card),
+ "origin", "", /* Leave it empty for these special cases */
+ "port-name", NULL,
+ "port-available", FALSE,
+ "card", card,
+ NULL);
+ out = GVC_MIXER_UI_DEVICE (object);
+ gvc_mixer_ui_device_set_profiles (out, profiles);
+
+ g_hash_table_insert (control->priv->ui_outputs,
+ GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (out)),
+ g_object_ref (out));
+}
+
+#ifdef HAVE_ALSA
+typedef struct {
+ char *port_name_to_set;
+ guint32 headset_card;
+} PortStatusData;
+
+static void
+port_status_data_free (PortStatusData *data)
+{
+ if (data == NULL)
+ return;
+ g_free (data->port_name_to_set);
+ g_free (data);
+}
+
+/*
+ We need to re-enumerate sources and sinks every time the user makes a choice,
+ because they can change due to use interaction in other software (or policy
+ changes inside PulseAudio). Enumeration means PulseAudio will do a series of
+ callbacks, one for every source/sink.
+ Set the port when we find the correct source/sink.
+ */
+
+static void
+sink_info_cb (pa_context *c,
+ const pa_sink_info *i,
+ int eol,
+ void *userdata)
+{
+ PortStatusData *data = userdata;
+ pa_operation *o;
+ guint j;
+ const char *s;
+
+ if (eol != 0) {
+ port_status_data_free (data);
+ return;
+ }
+
+ if (i->card != data->headset_card)
+ return;
+
+ s = data->port_name_to_set;
+
+ if (i->active_port &&
+ strcmp (i->active_port->name, s) == 0)
+ return;
+
+ for (j = 0; j < i->n_ports; j++)
+ if (strcmp (i->ports[j]->name, s) == 0)
+ break;
+
+ if (j >= i->n_ports)
+ return;
+
+ o = pa_context_set_sink_port_by_index (c, i->index, s, NULL, NULL);
+ g_clear_pointer (&o, pa_operation_unref);
+}
+
+static void
+source_info_cb (pa_context *c,
+ const pa_source_info *i,
+ int eol,
+ void *userdata)
+{
+ PortStatusData *data = userdata;
+ pa_operation *o;
+ guint j;
+ const char *s;
+
+ if (eol != 0) {
+ port_status_data_free (data);
+ return;
+ }
+
+ if (i->card != data->headset_card)
+ return;
+
+ s = data->port_name_to_set;
+
+ for (j = 0; j < i->n_ports; j++) {
+ if (g_str_equal (i->ports[j]->name, s)) {
+ o = pa_context_set_default_source (c,
+ i->name,
+ NULL,
+ NULL);
+ if (o == NULL) {
+ g_warning ("pa_context_set_default_source() failed");
+ return;
+ }
+ }
+ }
+
+ if (i->active_port && strcmp (i->active_port->name, s) == 0)
+ return;
+
+ for (j = 0; j < i->n_ports; j++)
+ if (strcmp (i->ports[j]->name, s) == 0)
+ break;
+
+ if (j >= i->n_ports)
+ return;
+
+ o = pa_context_set_source_port_by_index(c, i->index, s, NULL, NULL);
+ g_clear_pointer (&o, pa_operation_unref);
+}
+
+static void
+gvc_mixer_control_set_port_status_for_headset (GvcMixerControl *control,
+ guint id,
+ const char *port_name,
+ gboolean is_output)
+{
+ pa_operation *o;
+ PortStatusData *data;
+
+ if (port_name == NULL)
+ return;
+
+ data = g_new0 (PortStatusData, 1);
+ data->port_name_to_set = g_strdup (port_name);
+ data->headset_card = id;
+
+ if (is_output)
+ o = pa_context_get_sink_info_list (control->priv->pa_context, sink_info_cb, data);
+ else
+ o = pa_context_get_source_info_list (control->priv->pa_context, source_info_cb, data);
+
+ g_clear_pointer (&o, pa_operation_unref);
+}
+#endif /* HAVE_ALSA */
+
+static void
+free_priv_port_names (GvcMixerControl *control)
+{
+#ifdef HAVE_ALSA
+ g_clear_pointer (&control->priv->headphones_name, g_free);
+ g_clear_pointer (&control->priv->headsetmic_name, g_free);
+ g_clear_pointer (&control->priv->headphonemic_name, g_free);
+ g_clear_pointer (&control->priv->internalspk_name, g_free);
+ g_clear_pointer (&control->priv->internalmic_name, g_free);
+#endif
+}
+
+void
+gvc_mixer_control_set_headset_port (GvcMixerControl *control,
+ guint id,
+ GvcHeadsetPortChoice choice)
+{
+ g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
+
+#ifdef HAVE_ALSA
+ switch (choice) {
+ case GVC_HEADSET_PORT_CHOICE_HEADPHONES:
+ gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphones_name, TRUE);
+ gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->internalmic_name, FALSE);
+ break;
+ case GVC_HEADSET_PORT_CHOICE_HEADSET:
+ gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphones_name, TRUE);
+ gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headsetmic_name, FALSE);
+ break;
+ case GVC_HEADSET_PORT_CHOICE_MIC:
+ gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->internalspk_name, TRUE);
+ gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphonemic_name, FALSE);
+ break;
+ case GVC_HEADSET_PORT_CHOICE_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+#else
+ g_warning ("BUG: libgnome-volume-control compiled without ALSA support");
+#endif /* HAVE_ALSA */
+}
+
+#ifdef HAVE_ALSA
+typedef struct {
+ const pa_card_port_info *headphones;
+ const pa_card_port_info *headsetmic;
+ const pa_card_port_info *headphonemic;
+ const pa_card_port_info *internalmic;
+ const pa_card_port_info *internalspk;
+} headset_ports;
+
+/*
+ In PulseAudio without ucm, ports will show up with the following names:
+ Headphones - analog-output-headphones
+ Headset mic - analog-input-headset-mic (was: analog-input-microphone-headset)
+ Jack in mic-in mode - analog-input-headphone-mic (was: analog-input-microphone)
+
+ However, since regular mics also show up as analog-input-microphone,
+ we need to check for certain controls on alsa mixer level too, to know
+ if we deal with a separate mic jack, or a multi-function jack with a
+ mic-in mode (also called "headphone mic").
+ We check for the following names:
+
+ Headphone Mic Jack - indicates headphone and mic-in mode share the same jack,
+ i e, not two separate jacks. Hardware cannot distinguish between a
+ headphone and a mic.
+ Headset Mic Phantom Jack - indicates headset jack where hardware can not
+ distinguish between headphones and headsets
+ Headset Mic Jack - indicates headset jack where hardware can distinguish
+ between headphones and headsets. There is no use popping up a dialog in
+ this case, unless we already need to do this for the mic-in mode.
+
+ From the PA_PROCOTOL_VERSION=34, The device_port structure adds 2 members
+ availability_group and type, with the help of these 2 members, we could
+ consolidate the port checking and port setting for non-ucm and with-ucm
+ cases.
+*/
+
+#define HEADSET_PORT_SET(dst, src) \
+ do { \
+ if (!(dst) || (dst)->priority < (src)->priority) \
+ dst = src; \
+ } while (0)
+
+#define GET_PORT_NAME(x) (x ? g_strdup (x->name) : NULL)
+
+static headset_ports *
+get_headset_ports (GvcMixerControl *control,
+ const pa_card_info *c)
+{
+ headset_ports *h;
+ guint i;
+
+ h = g_new0 (headset_ports, 1);
+
+ for (i = 0; i < c->n_ports; i++) {
+ pa_card_port_info *p = c->ports[i];
+ if (control->priv->server_protocol_version < 34) {
+ if (g_str_equal (p->name, "analog-output-headphones"))
+ h->headphones = p;
+ else if (g_str_equal (p->name, "analog-input-headset-mic"))
+ h->headsetmic = p;
+ else if (g_str_equal (p->name, "analog-input-headphone-mic"))
+ h->headphonemic = p;
+ else if (g_str_equal (p->name, "analog-input-internal-mic"))
+ h->internalmic = p;
+ else if (g_str_equal (p->name, "analog-output-speaker"))
+ h->internalspk = p;
+ } else {
+#if (PA_PROTOCOL_VERSION >= 34)
+ /* in the first loop, set only headphones */
+ /* the microphone ports are assigned in the second loop */
+ if (p->type == PA_DEVICE_PORT_TYPE_HEADPHONES) {
+ if (p->availability_group)
+ HEADSET_PORT_SET (h->headphones, p);
+ } else if (p->type == PA_DEVICE_PORT_TYPE_SPEAKER) {
+ HEADSET_PORT_SET (h->internalspk, p);
+ } else if (p->type == PA_DEVICE_PORT_TYPE_MIC) {
+ if (!p->availability_group)
+ HEADSET_PORT_SET (h->internalmic, p);
+ }
+#else
+ g_warning_once ("libgnome-volume-control running against PulseAudio %u, "
+ "but compiled against older %d, report a bug to your distribution",
+ control->priv->server_protocol_version,
+ PA_PROTOCOL_VERSION);
+#endif
+ }
+ }
+
+#if (PA_PROTOCOL_VERSION >= 34)
+ if (h->headphones && (control->priv->server_protocol_version >= 34)) {
+ for (i = 0; i < c->n_ports; i++) {
+ pa_card_port_info *p = c->ports[i];
+ if (g_strcmp0(h->headphones->availability_group, p->availability_group))
+ continue;
+ if (p->direction != PA_DIRECTION_INPUT)
+ continue;
+ if (p->type == PA_DEVICE_PORT_TYPE_HEADSET)
+ HEADSET_PORT_SET (h->headsetmic, p);
+ else if (p->type == PA_DEVICE_PORT_TYPE_MIC)
+ HEADSET_PORT_SET (h->headphonemic, p);
+ }
+ }
+#endif
+
+ return h;
+}
+
+static gboolean
+verify_alsa_card (int cardindex,
+ gboolean *headsetmic,
+ gboolean *headphonemic)
+{
+ char *ctlstr;
+ snd_hctl_t *hctl;
+ snd_ctl_elem_id_t *id;
+ int err;
+
+ *headsetmic = FALSE;
+ *headphonemic = FALSE;
+
+ ctlstr = g_strdup_printf ("hw:%i", cardindex);
+ if ((err = snd_hctl_open (&hctl, ctlstr, 0)) < 0) {
+ g_warning ("snd_hctl_open failed: %s", snd_strerror(err));
+ g_free (ctlstr);
+ return FALSE;
+ }
+ g_free (ctlstr);
+
+ if ((err = snd_hctl_load (hctl)) < 0) {
+ g_warning ("snd_hctl_load failed: %s", snd_strerror(err));
+ snd_hctl_close (hctl);
+ return FALSE;
+ }
+
+ snd_ctl_elem_id_alloca (&id);
+
+ snd_ctl_elem_id_clear (id);
+ snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_CARD);
+ snd_ctl_elem_id_set_name (id, "Headphone Mic Jack");
+ if (snd_hctl_find_elem (hctl, id))
+ *headphonemic = TRUE;
+
+ snd_ctl_elem_id_clear (id);
+ snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_CARD);
+ snd_ctl_elem_id_set_name (id, "Headset Mic Phantom Jack");
+ if (snd_hctl_find_elem (hctl, id))
+ *headsetmic = TRUE;
+
+ if (*headphonemic) {
+ snd_ctl_elem_id_clear (id);
+ snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_CARD);
+ snd_ctl_elem_id_set_name (id, "Headset Mic Jack");
+ if (snd_hctl_find_elem (hctl, id))
+ *headsetmic = TRUE;
+ }
+
+ snd_hctl_close (hctl);
+ return *headsetmic || *headphonemic;
+}
+
+static void
+check_audio_device_selection_needed (GvcMixerControl *control,
+ const pa_card_info *info)
+{
+ headset_ports *h;
+ gboolean start_dialog, stop_dialog;
+
+ start_dialog = FALSE;
+ stop_dialog = FALSE;
+ h = get_headset_ports (control, info);
+
+ if (!h->headphones ||
+ (!h->headsetmic && !h->headphonemic)) {
+ /* Not a headset jack */
+ goto out;
+ }
+
+ if (control->priv->headset_card != (int) info->index) {
+ int cardindex;
+ gboolean hsmic = TRUE;
+ gboolean hpmic = TRUE;
+ const char *s;
+
+ s = pa_proplist_gets (info->proplist, "alsa.card");
+ if (!s)
+ goto out;
+
+ cardindex = strtol (s, NULL, 10);
+ if (cardindex == 0 && strcmp(s, "0") != 0)
+ goto out;
+
+ if (control->priv->server_protocol_version < 34) {
+ if (!verify_alsa_card(cardindex, &hsmic, &hpmic))
+ goto out;
+ }
+
+ control->priv->headset_card = info->index;
+ control->priv->has_headsetmic = hsmic && h->headsetmic;
+ control->priv->has_headphonemic = hpmic && h->headphonemic;
+ } else {
+ start_dialog = (h->headphones->available != PA_PORT_AVAILABLE_NO) && !control->priv->headset_plugged_in;
+ stop_dialog = (h->headphones->available == PA_PORT_AVAILABLE_NO) && control->priv->headset_plugged_in;
+ }
+
+ control->priv->headset_plugged_in = h->headphones->available != PA_PORT_AVAILABLE_NO;
+ free_priv_port_names (control);
+ control->priv->headphones_name = GET_PORT_NAME(h->headphones);
+ control->priv->headsetmic_name = GET_PORT_NAME(h->headsetmic);
+ control->priv->headphonemic_name = GET_PORT_NAME(h->headphonemic);
+ control->priv->internalspk_name = GET_PORT_NAME(h->internalspk);
+ control->priv->internalmic_name = GET_PORT_NAME(h->internalmic);
+
+ if (!start_dialog &&
+ !stop_dialog)
+ goto out;
+
+ if (stop_dialog) {
+ g_signal_emit (G_OBJECT (control),
+ signals[AUDIO_DEVICE_SELECTION_NEEDED],
+ 0,
+ info->index,
+ FALSE,
+ GVC_HEADSET_PORT_CHOICE_NONE);
+ } else {
+ GvcHeadsetPortChoice choices;
+
+ choices = GVC_HEADSET_PORT_CHOICE_HEADPHONES;
+ if (control->priv->has_headsetmic)
+ choices |= GVC_HEADSET_PORT_CHOICE_HEADSET;
+ if (control->priv->has_headphonemic)
+ choices |= GVC_HEADSET_PORT_CHOICE_MIC;
+
+ g_signal_emit (G_OBJECT (control),
+ signals[AUDIO_DEVICE_SELECTION_NEEDED],
+ 0,
+ info->index,
+ TRUE,
+ choices);
+ }
+
+out:
+ g_free (h);
+}
+#endif /* HAVE_ALSA */
+
+/*
+ * At this point we can determine all devices available to us (besides network 'ports')
+ * This is done by the following:
+ *
+ * - gvc_mixer_card and gvc_mixer_card_ports are created and relevant setters are called.
+ * - First it checks to see if it's a portless card. Bluetooth devices are portless AFAIHS.
+ * If so it creates two devices, an input and an output.
+ * - If it's a 'normal' card with ports it will create a new ui-device or
+ * synchronise port availability with the existing device cached for that port on this card. */
+
+static void
+update_card (GvcMixerControl *control,
+ const pa_card_info *info)
+{
+ const GList *card_ports = NULL;
+ const GList *m = NULL;
+ GvcMixerCard *card;
+ gboolean is_new = FALSE;
+#if 1
+ guint i;
+ const char *key;
+ void *state;
+
+ g_debug ("Updating card %s (index: %u driver: %s):",
+ info->name, info->index, info->driver);
+
+ for (i = 0; i < info->n_profiles; i++) {
+ struct pa_card_profile_info pi = info->profiles[i];
+ gboolean is_default;
+
+ is_default = (g_strcmp0 (pi.name, info->active_profile->name) == 0);
+ g_debug ("\tProfile '%s': %d sources %d sinks%s",
+ pi.name, pi.n_sources, pi.n_sinks,
+ is_default ? " (Current)" : "");
+ }
+ state = NULL;
+ key = pa_proplist_iterate (info->proplist, &state);
+ while (key != NULL) {
+ g_debug ("\tProperty: '%s' = '%s'",
+ key, pa_proplist_gets (info->proplist, key));
+ key = pa_proplist_iterate (info->proplist, &state);
+ }
+#endif
+ card = g_hash_table_lookup (control->priv->cards,
+ GUINT_TO_POINTER (info->index));
+ if (card == NULL) {
+ GList *profile_list = NULL;
+ GList *port_list = NULL;
+
+ for (i = 0; i < info->n_profiles; i++) {
+ GvcMixerCardProfile *profile;
+ struct pa_card_profile_info pi = info->profiles[i];
+
+ profile = g_new0 (GvcMixerCardProfile, 1);
+ profile->profile = g_strdup (pi.name);
+ profile->human_profile = g_strdup (pi.description);
+ profile->status = card_num_streams_to_status (pi.n_sinks, pi.n_sources);
+ profile->n_sinks = pi.n_sinks;
+ profile->n_sources = pi.n_sources;
+ profile->priority = pi.priority;
+ profile_list = g_list_prepend (profile_list, profile);
+ }
+ card = gvc_mixer_card_new (control->priv->pa_context,
+ info->index);
+
+ for (i = 0; i < info->n_ports; i++) {
+ GvcMixerCardPort *port;
+ port = g_new0 (GvcMixerCardPort, 1);
+ port->port = g_strdup (info->ports[i]->name);
+ port->human_port = g_strdup (info->ports[i]->description);
+ port->priority = info->ports[i]->priority;
+ port->available = info->ports[i]->available;
+ port->direction = info->ports[i]->direction;
+ port->icon_name = g_strdup (pa_proplist_gets (info->ports[i]->proplist, "device.icon_name"));
+ port->profiles = determine_profiles_for_port (info->ports[i], profile_list);
+ port_list = g_list_prepend (port_list, port);
+ }
+
+ gvc_mixer_card_set_profiles (card, profile_list);
+ gvc_mixer_card_set_ports (card, port_list);
+ is_new = TRUE;
+ }
+
+ gvc_mixer_card_set_name (card, pa_proplist_gets (info->proplist, "device.description"));
+ gvc_mixer_card_set_icon_name (card, pa_proplist_gets (info->proplist, "device.icon_name"));
+ gvc_mixer_card_set_profile (card, info->active_profile->name);
+
+ if (is_new) {
+ g_hash_table_insert (control->priv->cards,
+ GUINT_TO_POINTER (info->index),
+ card);
+ }
+
+ card_ports = gvc_mixer_card_get_ports (card);
+
+ if (card_ports == NULL && is_new) {
+ g_debug ("Portless card just registered - %s", gvc_mixer_card_get_name (card));
+ create_ui_device_from_card (control, card);
+ }
+
+ for (m = card_ports; m != NULL; m = m->next) {
+ GvcMixerCardPort *card_port;
+ card_port = m->data;
+ if (is_new)
+ create_ui_device_from_port (control, card_port, card);
+ else {
+ for (i = 0; i < info->n_ports; i++) {
+ if (g_strcmp0 (card_port->port, info->ports[i]->name) == 0) {
+ if ((card_port->available == PA_PORT_AVAILABLE_NO) != (info->ports[i]->available == PA_PORT_AVAILABLE_NO)) {
+ card_port->available = info->ports[i]->available;
+ g_debug ("sync port availability on card %i, card port name '%s', new available value %i",
+ gvc_mixer_card_get_index (card),
+ card_port->port,
+ card_port->available);
+ match_card_port_with_existing_device (control,
+ card_port,
+ card,
+ card_port->available != PA_PORT_AVAILABLE_NO);
+ }
+ }
+ }
+ }
+ }
+
+#ifdef HAVE_ALSA
+ check_audio_device_selection_needed (control, info);
+#endif /* HAVE_ALSA */
+
+ g_signal_emit (G_OBJECT (control),
+ signals[CARD_ADDED],
+ 0,
+ info->index);
+}
+
+static void
+_pa_context_get_sink_info_cb (pa_context *context,
+ const pa_sink_info *i,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (eol < 0) {
+ if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+ return;
+ }
+
+ g_warning ("Sink callback failure");
+ return;
+ }
+
+ if (eol > 0) {
+ dec_outstanding (control);
+ return;
+ }
+
+ update_sink (control, i);
+}
+
+static void
+_pa_context_get_source_info_cb (pa_context *context,
+ const pa_source_info *i,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (eol < 0) {
+ if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+ return;
+ }
+
+ g_warning ("Source callback failure");
+ return;
+ }
+
+ if (eol > 0) {
+ dec_outstanding (control);
+ return;
+ }
+
+ update_source (control, i);
+}
+
+static void
+_pa_context_get_sink_input_info_cb (pa_context *context,
+ const pa_sink_input_info *i,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (eol < 0) {
+ if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+ return;
+ }
+
+ g_warning ("Sink input callback failure");
+ return;
+ }
+
+ if (eol > 0) {
+ dec_outstanding (control);
+ return;
+ }
+
+ update_sink_input (control, i);
+}
+
+static void
+_pa_context_get_source_output_info_cb (pa_context *context,
+ const pa_source_output_info *i,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (eol < 0) {
+ if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+ return;
+ }
+
+ g_warning ("Source output callback failure");
+ return;
+ }
+
+ if (eol > 0) {
+ dec_outstanding (control);
+ return;
+ }
+
+ update_source_output (control, i);
+}
+
+static void
+_pa_context_get_client_info_cb (pa_context *context,
+ const pa_client_info *i,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (eol < 0) {
+ if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+ return;
+ }
+
+ g_warning ("Client callback failure");
+ return;
+ }
+
+ if (eol > 0) {
+ dec_outstanding (control);
+ return;
+ }
+
+ update_client (control, i);
+}
+
+static void
+_pa_context_get_card_info_by_index_cb (pa_context *context,
+ const pa_card_info *i,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (eol < 0) {
+ if (pa_context_errno (context) == PA_ERR_NOENTITY)
+ return;
+
+ g_warning ("Card callback failure");
+ return;
+ }
+
+ if (eol > 0) {
+ dec_outstanding (control);
+ return;
+ }
+
+ update_card (control, i);
+}
+
+static void
+_pa_context_get_server_info_cb (pa_context *context,
+ const pa_server_info *i,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (i == NULL) {
+ g_warning ("Server info callback failure");
+ return;
+ }
+ g_debug ("get server info");
+ update_server (control, i);
+ dec_outstanding (control);
+}
+
+static void
+remove_event_role_stream (GvcMixerControl *control)
+{
+ g_debug ("Removing event role");
+}
+
+static void
+update_event_role_stream (GvcMixerControl *control,
+ const pa_ext_stream_restore_info *info)
+{
+ GvcMixerStream *stream;
+ gboolean is_new;
+ pa_volume_t max_volume;
+
+ if (strcmp (info->name, "sink-input-by-media-role:event") != 0) {
+ return;
+ }
+
+#if 0
+ g_debug ("Updating event role: name='%s' device='%s'",
+ info->name,
+ info->device);
+#endif
+
+ is_new = FALSE;
+
+ if (!control->priv->event_sink_input_is_set) {
+ pa_channel_map pa_map;
+ GvcChannelMap *map;
+
+ pa_map.channels = 1;
+ pa_map.map[0] = PA_CHANNEL_POSITION_MONO;
+ map = gvc_channel_map_new_from_pa_channel_map (&pa_map);
+
+ stream = gvc_mixer_event_role_new (control->priv->pa_context,
+ info->device,
+ map);
+ control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
+ control->priv->event_sink_input_is_set = TRUE;
+
+ is_new = TRUE;
+ } else {
+ stream = g_hash_table_lookup (control->priv->all_streams,
+ GUINT_TO_POINTER (control->priv->event_sink_input_id));
+ }
+
+ max_volume = pa_cvolume_max (&info->volume);
+
+ gvc_mixer_stream_set_name (stream, _("System Sounds"));
+ gvc_mixer_stream_set_icon_name (stream, "audio-x-generic");
+ gvc_mixer_stream_set_volume (stream, (guint)max_volume);
+ gvc_mixer_stream_set_is_muted (stream, info->mute);
+
+ if (is_new) {
+ add_stream (control, stream);
+ }
+}
+
+static void
+_pa_ext_stream_restore_read_cb (pa_context *context,
+ const pa_ext_stream_restore_info *i,
+ int eol,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ if (eol < 0) {
+ g_debug ("Failed to initialized stream_restore extension: %s",
+ pa_strerror (pa_context_errno (context)));
+ remove_event_role_stream (control);
+ return;
+ }
+
+ if (eol > 0) {
+ dec_outstanding (control);
+ /* If we don't have an event stream to restore, then
+ * set one up with a default 100% volume */
+ if (!control->priv->event_sink_input_is_set) {
+ pa_ext_stream_restore_info info;
+
+ memset (&info, 0, sizeof(info));
+ info.name = "sink-input-by-media-role:event";
+ info.volume.channels = 1;
+ info.volume.values[0] = PA_VOLUME_NORM;
+ update_event_role_stream (control, &info);
+ }
+ return;
+ }
+
+ update_event_role_stream (control, i);
+}
+
+static void
+_pa_ext_stream_restore_subscribe_cb (pa_context *context,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+ pa_operation *o;
+
+ o = pa_ext_stream_restore_read (context,
+ _pa_ext_stream_restore_read_cb,
+ control);
+ if (o == NULL) {
+ g_warning ("pa_ext_stream_restore_read() failed");
+ return;
+ }
+
+ pa_operation_unref (o);
+}
+
+static void
+req_update_server_info (GvcMixerControl *control,
+ int index)
+{
+ pa_operation *o;
+
+ o = pa_context_get_server_info (control->priv->pa_context,
+ _pa_context_get_server_info_cb,
+ control);
+ if (o == NULL) {
+ g_warning ("pa_context_get_server_info() failed");
+ return;
+ }
+ pa_operation_unref (o);
+}
+
+static void
+req_update_client_info (GvcMixerControl *control,
+ int index)
+{
+ pa_operation *o;
+
+ if (index < 0) {
+ o = pa_context_get_client_info_list (control->priv->pa_context,
+ _pa_context_get_client_info_cb,
+ control);
+ } else {
+ o = pa_context_get_client_info (control->priv->pa_context,
+ index,
+ _pa_context_get_client_info_cb,
+ control);
+ }
+
+ if (o == NULL) {
+ g_warning ("pa_context_client_info_list() failed");
+ return;
+ }
+ pa_operation_unref (o);
+}
+
+static void
+req_update_card (GvcMixerControl *control,
+ int index)
+{
+ pa_operation *o;
+
+ if (index < 0) {
+ o = pa_context_get_card_info_list (control->priv->pa_context,
+ _pa_context_get_card_info_by_index_cb,
+ control);
+ } else {
+ o = pa_context_get_card_info_by_index (control->priv->pa_context,
+ index,
+ _pa_context_get_card_info_by_index_cb,
+ control);
+ }
+
+ if (o == NULL) {
+ g_warning ("pa_context_get_card_info_by_index() failed");
+ return;
+ }
+ pa_operation_unref (o);
+}
+
+static void
+req_update_sink_info (GvcMixerControl *control,
+ int index)
+{
+ pa_operation *o;
+
+ if (index < 0) {
+ o = pa_context_get_sink_info_list (control->priv->pa_context,
+ _pa_context_get_sink_info_cb,
+ control);
+ } else {
+ o = pa_context_get_sink_info_by_index (control->priv->pa_context,
+ index,
+ _pa_context_get_sink_info_cb,
+ control);
+ }
+
+ if (o == NULL) {
+ g_warning ("pa_context_get_sink_info_list() failed");
+ return;
+ }
+ pa_operation_unref (o);
+}
+
+static void
+req_update_source_info (GvcMixerControl *control,
+ int index)
+{
+ pa_operation *o;
+
+ if (index < 0) {
+ o = pa_context_get_source_info_list (control->priv->pa_context,
+ _pa_context_get_source_info_cb,
+ control);
+ } else {
+ o = pa_context_get_source_info_by_index(control->priv->pa_context,
+ index,
+ _pa_context_get_source_info_cb,
+ control);
+ }
+
+ if (o == NULL) {
+ g_warning ("pa_context_get_source_info_list() failed");
+ return;
+ }
+ pa_operation_unref (o);
+}
+
+static void
+req_update_sink_input_info (GvcMixerControl *control,
+ int index)
+{
+ pa_operation *o;
+
+ if (index < 0) {
+ o = pa_context_get_sink_input_info_list (control->priv->pa_context,
+ _pa_context_get_sink_input_info_cb,
+ control);
+ } else {
+ o = pa_context_get_sink_input_info (control->priv->pa_context,
+ index,
+ _pa_context_get_sink_input_info_cb,
+ control);
+ }
+
+ if (o == NULL) {
+ g_warning ("pa_context_get_sink_input_info_list() failed");
+ return;
+ }
+ pa_operation_unref (o);
+}
+
+static void
+req_update_source_output_info (GvcMixerControl *control,
+ int index)
+{
+ pa_operation *o;
+
+ if (index < 0) {
+ o = pa_context_get_source_output_info_list (control->priv->pa_context,
+ _pa_context_get_source_output_info_cb,
+ control);
+ } else {
+ o = pa_context_get_source_output_info (control->priv->pa_context,
+ index,
+ _pa_context_get_source_output_info_cb,
+ control);
+ }
+
+ if (o == NULL) {
+ g_warning ("pa_context_get_source_output_info_list() failed");
+ return;
+ }
+ pa_operation_unref (o);
+}
+
+static void
+remove_client (GvcMixerControl *control,
+ guint index)
+{
+ g_hash_table_remove (control->priv->clients,
+ GUINT_TO_POINTER (index));
+}
+
+static void
+remove_card (GvcMixerControl *control,
+ guint index)
+{
+
+ GList *devices, *d;
+
+ devices = g_list_concat (g_hash_table_get_values (control->priv->ui_inputs),
+ g_hash_table_get_values (control->priv->ui_outputs));
+
+ for (d = devices; d != NULL; d = d->next) {
+ GvcMixerCard *card;
+ GvcMixerUIDevice *device = d->data;
+
+ g_object_get (G_OBJECT (device), "card", &card, NULL);
+
+ if (card == NULL)
+ continue;
+
+ if (gvc_mixer_card_get_index (card) == index) {
+ g_signal_emit (G_OBJECT (control),
+ signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED],
+ 0,
+ gvc_mixer_ui_device_get_id (device));
+ g_debug ("Card removal remove device %s",
+ gvc_mixer_ui_device_get_description (device));
+ g_hash_table_remove (gvc_mixer_ui_device_is_output (device) ? control->priv->ui_outputs : control->priv->ui_inputs,
+ GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)));
+ }
+ }
+
+ g_list_free (devices);
+
+ g_hash_table_remove (control->priv->cards,
+ GUINT_TO_POINTER (index));
+
+ g_signal_emit (G_OBJECT (control),
+ signals[CARD_REMOVED],
+ 0,
+ index);
+}
+
+static void
+remove_sink (GvcMixerControl *control,
+ guint index)
+{
+ GvcMixerStream *stream;
+ GvcMixerUIDevice *device;
+
+ g_debug ("Removing sink: index=%u", index);
+
+ stream = g_hash_table_lookup (control->priv->sinks,
+ GUINT_TO_POINTER (index));
+ if (stream == NULL)
+ return;
+
+ device = gvc_mixer_control_lookup_device_from_stream (control, stream);
+
+ if (device != NULL) {
+ gvc_mixer_ui_device_invalidate_stream (device);
+ if (!gvc_mixer_ui_device_has_ports (device)) {
+ g_signal_emit (G_OBJECT (control),
+ signals[OUTPUT_REMOVED],
+ 0,
+ gvc_mixer_ui_device_get_id (device));
+ } else {
+ GList *devices, *d;
+
+ devices = g_hash_table_get_values (control->priv->ui_outputs);
+
+ for (d = devices; d != NULL; d = d->next) {
+ guint stream_id = GVC_MIXER_UI_DEVICE_INVALID;
+ device = d->data;
+ g_object_get (G_OBJECT (device),
+ "stream-id", &stream_id,
+ NULL);
+ if (stream_id == gvc_mixer_stream_get_id (stream))
+ gvc_mixer_ui_device_invalidate_stream (device);
+ }
+
+ g_list_free (devices);
+ }
+ }
+
+ g_hash_table_remove (control->priv->sinks,
+ GUINT_TO_POINTER (index));
+
+ remove_stream (control, stream);
+}
+
+static void
+remove_source (GvcMixerControl *control,
+ guint index)
+{
+ GvcMixerStream *stream;
+ GvcMixerUIDevice *device;
+
+ g_debug ("Removing source: index=%u", index);
+
+ stream = g_hash_table_lookup (control->priv->sources,
+ GUINT_TO_POINTER (index));
+ if (stream == NULL)
+ return;
+
+ device = gvc_mixer_control_lookup_device_from_stream (control, stream);
+
+ if (device != NULL) {
+ gvc_mixer_ui_device_invalidate_stream (device);
+ if (!gvc_mixer_ui_device_has_ports (device)) {
+ g_signal_emit (G_OBJECT (control),
+ signals[INPUT_REMOVED],
+ 0,
+ gvc_mixer_ui_device_get_id (device));
+ } else {
+ GList *devices, *d;
+
+ devices = g_hash_table_get_values (control->priv->ui_inputs);
+
+ for (d = devices; d != NULL; d = d->next) {
+ guint stream_id = GVC_MIXER_UI_DEVICE_INVALID;
+ device = d->data;
+ g_object_get (G_OBJECT (device),
+ "stream-id", &stream_id,
+ NULL);
+ if (stream_id == gvc_mixer_stream_get_id (stream))
+ gvc_mixer_ui_device_invalidate_stream (device);
+ }
+
+ g_list_free (devices);
+ }
+ }
+
+ g_hash_table_remove (control->priv->sources,
+ GUINT_TO_POINTER (index));
+
+ remove_stream (control, stream);
+}
+
+static void
+remove_sink_input (GvcMixerControl *control,
+ guint index)
+{
+ GvcMixerStream *stream;
+
+ g_debug ("Removing sink input: index=%u", index);
+
+ stream = g_hash_table_lookup (control->priv->sink_inputs,
+ GUINT_TO_POINTER (index));
+ if (stream == NULL) {
+ return;
+ }
+ g_hash_table_remove (control->priv->sink_inputs,
+ GUINT_TO_POINTER (index));
+
+ remove_stream (control, stream);
+}
+
+static void
+remove_source_output (GvcMixerControl *control,
+ guint index)
+{
+ GvcMixerStream *stream;
+
+ g_debug ("Removing source output: index=%u", index);
+
+ stream = g_hash_table_lookup (control->priv->source_outputs,
+ GUINT_TO_POINTER (index));
+ if (stream == NULL) {
+ return;
+ }
+ g_hash_table_remove (control->priv->source_outputs,
+ GUINT_TO_POINTER (index));
+
+ remove_stream (control, stream);
+}
+
+static void
+_pa_context_subscribe_cb (pa_context *context,
+ pa_subscription_event_type_t t,
+ uint32_t index,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ remove_sink (control, index);
+ } else {
+ req_update_sink_info (control, index);
+ }
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ remove_source (control, index);
+ } else {
+ req_update_source_info (control, index);
+ }
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ remove_sink_input (control, index);
+ } else {
+ req_update_sink_input_info (control, index);
+ }
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ remove_source_output (control, index);
+ } else {
+ req_update_source_output_info (control, index);
+ }
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_CLIENT:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ remove_client (control, index);
+ } else {
+ req_update_client_info (control, index);
+ }
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_SERVER:
+ req_update_server_info (control, index);
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_CARD:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ remove_card (control, index);
+ } else {
+ req_update_card (control, index);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+gvc_mixer_control_ready (GvcMixerControl *control)
+{
+ pa_operation *o;
+
+ pa_context_set_subscribe_callback (control->priv->pa_context,
+ _pa_context_subscribe_cb,
+ control);
+ o = pa_context_subscribe (control->priv->pa_context,
+ (pa_subscription_mask_t)
+ (PA_SUBSCRIPTION_MASK_SINK|
+ PA_SUBSCRIPTION_MASK_SOURCE|
+ PA_SUBSCRIPTION_MASK_SINK_INPUT|
+ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
+ PA_SUBSCRIPTION_MASK_CLIENT|
+ PA_SUBSCRIPTION_MASK_SERVER|
+ PA_SUBSCRIPTION_MASK_CARD),
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_subscribe() failed");
+ return;
+ }
+ pa_operation_unref (o);
+
+ req_update_server_info (control, -1);
+ req_update_card (control, -1);
+ req_update_client_info (control, -1);
+ req_update_sink_info (control, -1);
+ req_update_source_info (control, -1);
+ req_update_sink_input_info (control, -1);
+ req_update_source_output_info (control, -1);
+
+ control->priv->server_protocol_version = pa_context_get_server_protocol_version (control->priv->pa_context);
+
+ control->priv->n_outstanding = 6;
+
+ /* This call is not always supported */
+ o = pa_ext_stream_restore_read (control->priv->pa_context,
+ _pa_ext_stream_restore_read_cb,
+ control);
+ if (o != NULL) {
+ pa_operation_unref (o);
+ control->priv->n_outstanding++;
+
+ pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context,
+ _pa_ext_stream_restore_subscribe_cb,
+ control);
+
+ o = pa_ext_stream_restore_subscribe (control->priv->pa_context,
+ 1,
+ NULL,
+ NULL);
+ if (o != NULL) {
+ pa_operation_unref (o);
+ }
+
+ } else {
+ g_debug ("Failed to initialized stream_restore extension: %s",
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
+ }
+}
+
+static void
+gvc_mixer_new_pa_context (GvcMixerControl *self)
+{
+ pa_proplist *proplist;
+
+ g_return_if_fail (self);
+ g_return_if_fail (!self->priv->pa_context);
+
+ proplist = pa_proplist_new ();
+ pa_proplist_sets (proplist,
+ PA_PROP_APPLICATION_NAME,
+ self->priv->name);
+ pa_proplist_sets (proplist,
+ PA_PROP_APPLICATION_ID,
+ "org.gnome.VolumeControl");
+ pa_proplist_sets (proplist,
+ PA_PROP_APPLICATION_ICON_NAME,
+ "multimedia-volume-control");
+ pa_proplist_sets (proplist,
+ PA_PROP_APPLICATION_VERSION,
+ PACKAGE_VERSION);
+
+ self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
+
+ pa_proplist_free (proplist);
+ g_assert (self->priv->pa_context);
+}
+
+static void
+remove_all_items (GvcMixerControl *control,
+ GHashTable *hash_table,
+ void (*remove_item)(GvcMixerControl *control, guint index))
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (remove_item) {
+ remove_item (control, GPOINTER_TO_UINT (key));
+ g_hash_table_remove (hash_table, key);
+ g_hash_table_iter_init (&iter, hash_table);
+ } else {
+ g_hash_table_iter_remove (&iter);
+ }
+ }
+}
+
+static gboolean
+idle_reconnect (gpointer data)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (data);
+
+ g_return_val_if_fail (control, FALSE);
+
+ g_debug ("Reconnect: clean up all objects");
+
+ remove_all_items (control, control->priv->sinks, remove_sink);
+ remove_all_items (control, control->priv->sources, remove_source);
+ remove_all_items (control, control->priv->sink_inputs, remove_sink_input);
+ remove_all_items (control, control->priv->source_outputs, remove_source_output);
+ remove_all_items (control, control->priv->cards, remove_card);
+ remove_all_items (control, control->priv->ui_inputs, NULL);
+ remove_all_items (control, control->priv->ui_outputs, NULL);
+ remove_all_items (control, control->priv->clients, remove_client);
+
+ g_debug ("Reconnect: make new connection");
+
+ if (control->priv->pa_context) {
+ pa_context_unref (control->priv->pa_context);
+ control->priv->pa_context = NULL;
+ control->priv->server_protocol_version = 0;
+ gvc_mixer_new_pa_context (control);
+ }
+
+ gvc_mixer_control_open (control); /* cannot fail */
+
+ control->priv->reconnect_id = 0;
+ return FALSE;
+}
+
+static void
+_pa_context_state_cb (pa_context *context,
+ void *userdata)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+ switch (pa_context_get_state (context)) {
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+
+ case PA_CONTEXT_READY:
+ gvc_mixer_control_ready (control);
+ break;
+
+ case PA_CONTEXT_FAILED:
+ control->priv->state = GVC_STATE_FAILED;
+ g_signal_emit (control, signals[STATE_CHANGED], 0, GVC_STATE_FAILED);
+ if (control->priv->reconnect_id == 0)
+ control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control);
+ break;
+
+ case PA_CONTEXT_TERMINATED:
+ default:
+ /* FIXME: */
+ break;
+ }
+}
+
+gboolean
+gvc_mixer_control_open (GvcMixerControl *control)
+{
+ int res;
+
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+ g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
+ g_return_val_if_fail (pa_context_get_state (control->priv->pa_context) == PA_CONTEXT_UNCONNECTED, FALSE);
+
+ pa_context_set_state_callback (control->priv->pa_context,
+ _pa_context_state_cb,
+ control);
+
+ control->priv->state = GVC_STATE_CONNECTING;
+ g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CONNECTING);
+ res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL);
+ if (res < 0) {
+ g_warning ("Failed to connect context: %s",
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
+ }
+
+ return res;
+}
+
+gboolean
+gvc_mixer_control_close (GvcMixerControl *control)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+ g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
+
+ pa_context_disconnect (control->priv->pa_context);
+
+ control->priv->state = GVC_STATE_CLOSED;
+ g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CLOSED);
+ return TRUE;
+}
+
+static void
+gvc_mixer_control_dispose (GObject *object)
+{
+ GvcMixerControl *control = GVC_MIXER_CONTROL (object);
+
+ if (control->priv->reconnect_id != 0) {
+ g_source_remove (control->priv->reconnect_id);
+ control->priv->reconnect_id = 0;
+ }
+
+ if (control->priv->pa_context != NULL) {
+ pa_context_unref (control->priv->pa_context);
+ control->priv->pa_context = NULL;
+ }
+
+ if (control->priv->default_source_name != NULL) {
+ g_free (control->priv->default_source_name);
+ control->priv->default_source_name = NULL;
+ }
+ if (control->priv->default_sink_name != NULL) {
+ g_free (control->priv->default_sink_name);
+ control->priv->default_sink_name = NULL;
+ }
+
+ if (control->priv->pa_mainloop != NULL) {
+ pa_glib_mainloop_free (control->priv->pa_mainloop);
+ control->priv->pa_mainloop = NULL;
+ }
+
+ if (control->priv->all_streams != NULL) {
+ g_hash_table_destroy (control->priv->all_streams);
+ control->priv->all_streams = NULL;
+ }
+
+ if (control->priv->sinks != NULL) {
+ g_hash_table_destroy (control->priv->sinks);
+ control->priv->sinks = NULL;
+ }
+ if (control->priv->sources != NULL) {
+ g_hash_table_destroy (control->priv->sources);
+ control->priv->sources = NULL;
+ }
+ if (control->priv->sink_inputs != NULL) {
+ g_hash_table_destroy (control->priv->sink_inputs);
+ control->priv->sink_inputs = NULL;
+ }
+ if (control->priv->source_outputs != NULL) {
+ g_hash_table_destroy (control->priv->source_outputs);
+ control->priv->source_outputs = NULL;
+ }
+ if (control->priv->clients != NULL) {
+ g_hash_table_destroy (control->priv->clients);
+ control->priv->clients = NULL;
+ }
+ if (control->priv->cards != NULL) {
+ g_hash_table_destroy (control->priv->cards);
+ control->priv->cards = NULL;
+ }
+ if (control->priv->ui_outputs != NULL) {
+ g_hash_table_destroy (control->priv->ui_outputs);
+ control->priv->ui_outputs = NULL;
+ }
+ if (control->priv->ui_inputs != NULL) {
+ g_hash_table_destroy (control->priv->ui_inputs);
+ control->priv->ui_inputs = NULL;
+ }
+
+ free_priv_port_names (control);
+ G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
+}
+
+static void
+gvc_mixer_control_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerControl *self = GVC_MIXER_CONTROL (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_free (self->priv->name);
+ self->priv->name = g_value_dup_string (value);
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gvc_mixer_control_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerControl *self = GVC_MIXER_CONTROL (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, self->priv->name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static GObject *
+gvc_mixer_control_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ GvcMixerControl *self;
+
+ object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+ self = GVC_MIXER_CONTROL (object);
+
+ gvc_mixer_new_pa_context (self);
+ self->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
+
+ return object;
+}
+
+static void
+gvc_mixer_control_class_init (GvcMixerControlClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = gvc_mixer_control_constructor;
+ object_class->dispose = gvc_mixer_control_dispose;
+ object_class->finalize = gvc_mixer_control_finalize;
+ object_class->set_property = gvc_mixer_control_set_property;
+ object_class->get_property = gvc_mixer_control_get_property;
+
+ obj_props[PROP_NAME] = g_param_spec_string ("name",
+ "Name",
+ "Name to display for this mixer control",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+ g_object_class_install_properties (object_class, N_PROPS, obj_props);
+
+ signals [STATE_CHANGED] =
+ g_signal_new ("state-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [STREAM_ADDED] =
+ g_signal_new ("stream-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [STREAM_REMOVED] =
+ g_signal_new ("stream-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [STREAM_CHANGED] =
+ g_signal_new ("stream-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, stream_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [AUDIO_DEVICE_SELECTION_NEEDED] =
+ g_signal_new ("audio-device-selection-needed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_UINT);
+ signals [CARD_ADDED] =
+ g_signal_new ("card-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [CARD_REMOVED] =
+ g_signal_new ("card-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [DEFAULT_SINK_CHANGED] =
+ g_signal_new ("default-sink-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [DEFAULT_SOURCE_CHANGED] =
+ g_signal_new ("default-source-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [ACTIVE_OUTPUT_UPDATE] =
+ g_signal_new ("active-output-update",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, active_output_update),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [ACTIVE_INPUT_UPDATE] =
+ g_signal_new ("active-input-update",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, active_input_update),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [OUTPUT_ADDED] =
+ g_signal_new ("output-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, output_added),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [INPUT_ADDED] =
+ g_signal_new ("input-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, input_added),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [OUTPUT_REMOVED] =
+ g_signal_new ("output-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, output_removed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [INPUT_REMOVED] =
+ g_signal_new ("input-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GvcMixerControlClass, input_removed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+}
+
+
+static void
+gvc_mixer_control_init (GvcMixerControl *control)
+{
+ control->priv = gvc_mixer_control_get_instance_private (control);
+
+ control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ());
+ g_assert (control->priv->pa_mainloop);
+
+ control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop);
+ g_assert (control->priv->pa_api);
+
+ control->priv->all_streams = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+ control->priv->sinks = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+ control->priv->sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+ control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+ control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+ control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+ control->priv->ui_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+ control->priv->ui_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+
+ control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
+
+#ifdef HAVE_ALSA
+ control->priv->headset_card = -1;
+#endif /* HAVE_ALSA */
+
+ control->priv->state = GVC_STATE_CLOSED;
+}
+
+static void
+gvc_mixer_control_finalize (GObject *object)
+{
+ GvcMixerControl *mixer_control;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_CONTROL (object));
+
+ mixer_control = GVC_MIXER_CONTROL (object);
+ g_free (mixer_control->priv->name);
+ mixer_control->priv->name = NULL;
+
+ g_return_if_fail (mixer_control->priv != NULL);
+ G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object);
+}
+
+GvcMixerControl *
+gvc_mixer_control_new (const char *name)
+{
+ GObject *control;
+ control = g_object_new (GVC_TYPE_MIXER_CONTROL,
+ "name", name,
+ NULL);
+ return GVC_MIXER_CONTROL (control);
+}
+
+gdouble
+gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), 0);
+ return (gdouble) PA_VOLUME_NORM;
+}
+
+gdouble
+gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), 0);
+ return (gdouble) PA_VOLUME_UI_MAX;
+}
diff --git a/subprojects/gvc/gvc-mixer-control.h b/subprojects/gvc/gvc-mixer-control.h
new file mode 100644
index 0000000..8137849
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-control.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_CONTROL_H
+#define __GVC_MIXER_CONTROL_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+#include "gvc-mixer-card.h"
+#include "gvc-mixer-ui-device.h"
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GVC_STATE_CLOSED,
+ GVC_STATE_READY,
+ GVC_STATE_CONNECTING,
+ GVC_STATE_FAILED
+} GvcMixerControlState;
+
+typedef enum
+{
+ GVC_HEADSET_PORT_CHOICE_NONE = 0,
+ GVC_HEADSET_PORT_CHOICE_HEADPHONES = 1 << 0,
+ GVC_HEADSET_PORT_CHOICE_HEADSET = 1 << 1,
+ GVC_HEADSET_PORT_CHOICE_MIC = 1 << 2
+} GvcHeadsetPortChoice;
+
+#define GVC_TYPE_MIXER_CONTROL (gvc_mixer_control_get_type ())
+#define GVC_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControl))
+#define GVC_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))
+#define GVC_IS_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CONTROL))
+#define GVC_IS_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CONTROL))
+#define GVC_MIXER_CONTROL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))
+
+typedef struct GvcMixerControlPrivate GvcMixerControlPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GvcMixerControlPrivate *priv;
+} GvcMixerControl;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (*state_changed) (GvcMixerControl *control,
+ GvcMixerControlState new_state);
+ void (*stream_added) (GvcMixerControl *control,
+ guint id);
+ void (*stream_changed) (GvcMixerControl *control,
+ guint id);
+ void (*stream_removed) (GvcMixerControl *control,
+ guint id);
+ void (*card_added) (GvcMixerControl *control,
+ guint id);
+ void (*card_removed) (GvcMixerControl *control,
+ guint id);
+ void (*default_sink_changed) (GvcMixerControl *control,
+ guint id);
+ void (*default_source_changed) (GvcMixerControl *control,
+ guint id);
+ void (*active_output_update) (GvcMixerControl *control,
+ guint id);
+ void (*active_input_update) (GvcMixerControl *control,
+ guint id);
+ void (*output_added) (GvcMixerControl *control,
+ guint id);
+ void (*input_added) (GvcMixerControl *control,
+ guint id);
+ void (*output_removed) (GvcMixerControl *control,
+ guint id);
+ void (*input_removed) (GvcMixerControl *control,
+ guint id);
+ void (*audio_device_selection_needed)
+ (GvcMixerControl *control,
+ guint id,
+ gboolean show_dialog,
+ GvcHeadsetPortChoice choices);
+} GvcMixerControlClass;
+
+GType gvc_mixer_control_get_type (void);
+
+GvcMixerControl * gvc_mixer_control_new (const char *name);
+
+gboolean gvc_mixer_control_open (GvcMixerControl *control);
+gboolean gvc_mixer_control_close (GvcMixerControl *control);
+
+GSList * gvc_mixer_control_get_cards (GvcMixerControl *control);
+GSList * gvc_mixer_control_get_streams (GvcMixerControl *control);
+GSList * gvc_mixer_control_get_sinks (GvcMixerControl *control);
+GSList * gvc_mixer_control_get_sources (GvcMixerControl *control);
+GSList * gvc_mixer_control_get_sink_inputs (GvcMixerControl *control);
+GSList * gvc_mixer_control_get_source_outputs (GvcMixerControl *control);
+
+GvcMixerStream * gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
+ guint id);
+GvcMixerCard * gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
+ guint id);
+GvcMixerUIDevice * gvc_mixer_control_lookup_output_id (GvcMixerControl *control,
+ guint id);
+GvcMixerUIDevice * gvc_mixer_control_lookup_input_id (GvcMixerControl *control,
+ guint id);
+GvcMixerUIDevice * gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control,
+ GvcMixerStream *stream);
+
+GvcMixerStream * gvc_mixer_control_get_default_sink (GvcMixerControl *control);
+GvcMixerStream * gvc_mixer_control_get_default_source (GvcMixerControl *control);
+GvcMixerStream * gvc_mixer_control_get_event_sink_input (GvcMixerControl *control);
+
+gboolean gvc_mixer_control_set_default_sink (GvcMixerControl *control,
+ GvcMixerStream *stream);
+gboolean gvc_mixer_control_set_default_source (GvcMixerControl *control,
+ GvcMixerStream *stream);
+
+gdouble gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control);
+gdouble gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control);
+void gvc_mixer_control_change_output (GvcMixerControl *control,
+ GvcMixerUIDevice* output);
+void gvc_mixer_control_change_input (GvcMixerControl *control,
+ GvcMixerUIDevice* input);
+GvcMixerStream* gvc_mixer_control_get_stream_from_device (GvcMixerControl *control,
+ GvcMixerUIDevice *device);
+gboolean gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl *control,
+ GvcMixerUIDevice *device,
+ const gchar* profile);
+
+void gvc_mixer_control_set_headset_port (GvcMixerControl *control,
+ guint id,
+ GvcHeadsetPortChoice choices);
+
+GvcMixerControlState gvc_mixer_control_get_state (GvcMixerControl *control);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_CONTROL_H */
diff --git a/subprojects/gvc/gvc-mixer-event-role.c b/subprojects/gvc/gvc-mixer-event-role.c
new file mode 100644
index 0000000..272edb0
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-event-role.c
@@ -0,0 +1,229 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/ext-stream-restore.h>
+
+#include "gvc-mixer-event-role.h"
+#include "gvc-mixer-stream-private.h"
+#include "gvc-channel-map-private.h"
+
+struct GvcMixerEventRolePrivate
+{
+ char *device;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE,
+ N_PROPS
+};
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
+
+static void gvc_mixer_event_role_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerEventRole, gvc_mixer_event_role, GVC_TYPE_MIXER_STREAM)
+
+static gboolean
+update_settings (GvcMixerEventRole *role,
+ gboolean is_muted,
+ gpointer *op)
+{
+ pa_operation *o;
+ const GvcChannelMap *map;
+ pa_context *context;
+ pa_ext_stream_restore_info info;
+
+ map = gvc_mixer_stream_get_channel_map (GVC_MIXER_STREAM(role));
+
+ info.volume = *gvc_channel_map_get_cvolume(map);
+ info.name = "sink-input-by-media-role:event";
+ info.channel_map = *gvc_channel_map_get_pa_channel_map(map);
+ info.device = role->priv->device;
+ info.mute = is_muted;
+
+ context = gvc_mixer_stream_get_pa_context (GVC_MIXER_STREAM (role));
+
+ o = pa_ext_stream_restore_write (context,
+ PA_UPDATE_REPLACE,
+ &info,
+ 1,
+ TRUE,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_ext_stream_restore_write() failed");
+ return FALSE;
+ }
+
+ if (op != NULL)
+ *op = o;
+
+ return TRUE;
+}
+
+static gboolean
+gvc_mixer_event_role_push_volume (GvcMixerStream *stream, gpointer *op)
+{
+ return update_settings (GVC_MIXER_EVENT_ROLE (stream),
+ gvc_mixer_stream_get_is_muted (stream), op);
+}
+
+static gboolean
+gvc_mixer_event_role_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ /* Apply change straight away so that we don't get a race with
+ * gvc_mixer_event_role_push_volume().
+ * See https://bugs.freedesktop.org/show_bug.cgi?id=51413 */
+ gvc_mixer_stream_set_is_muted (stream, is_muted);
+ return update_settings (GVC_MIXER_EVENT_ROLE (stream),
+ is_muted, NULL);
+}
+
+static gboolean
+gvc_mixer_event_role_set_device (GvcMixerEventRole *role,
+ const char *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_EVENT_ROLE (role), FALSE);
+
+ g_free (role->priv->device);
+ role->priv->device = g_strdup (device);
+ g_object_notify_by_pspec (G_OBJECT (role), obj_props[PROP_DEVICE]);
+
+ return TRUE;
+}
+
+static void
+gvc_mixer_event_role_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ gvc_mixer_event_role_set_device (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gvc_mixer_event_role_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_string (value, self->priv->device);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
+
+ object_class->finalize = gvc_mixer_event_role_finalize;
+ object_class->set_property = gvc_mixer_event_role_set_property;
+ object_class->get_property = gvc_mixer_event_role_get_property;
+
+ stream_class->push_volume = gvc_mixer_event_role_push_volume;
+ stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted;
+
+ obj_props[PROP_DEVICE] = g_param_spec_string ("device",
+ "Device",
+ "Device",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ g_object_class_install_properties (object_class, N_PROPS, obj_props);
+}
+
+static void
+gvc_mixer_event_role_init (GvcMixerEventRole *event_role)
+{
+ event_role->priv = gvc_mixer_event_role_get_instance_private (event_role);
+
+}
+
+static void
+gvc_mixer_event_role_finalize (GObject *object)
+{
+ GvcMixerEventRole *mixer_event_role;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_EVENT_ROLE (object));
+
+ mixer_event_role = GVC_MIXER_EVENT_ROLE (object);
+
+ g_return_if_fail (mixer_event_role->priv != NULL);
+
+ g_free (mixer_event_role->priv->device);
+
+ G_OBJECT_CLASS (gvc_mixer_event_role_parent_class)->finalize (object);
+}
+
+/**
+ * gvc_mixer_event_role_new: (skip)
+ * @context:
+ * @device:
+ * @channel_map:
+ *
+ * Returns:
+ */
+GvcMixerStream *
+gvc_mixer_event_role_new (pa_context *context,
+ const char *device,
+ GvcChannelMap *channel_map)
+{
+ GObject *object;
+
+ object = g_object_new (GVC_TYPE_MIXER_EVENT_ROLE,
+ "pa-context", context,
+ "index", 0,
+ "device", device,
+ "channel-map", channel_map,
+ NULL);
+
+ return GVC_MIXER_STREAM (object);
+}
diff --git a/subprojects/gvc/gvc-mixer-event-role.h b/subprojects/gvc/gvc-mixer-event-role.h
new file mode 100644
index 0000000..ab4c509
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-event-role.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_EVENT_ROLE_H
+#define __GVC_MIXER_EVENT_ROLE_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_EVENT_ROLE (gvc_mixer_event_role_get_type ())
+#define GVC_MIXER_EVENT_ROLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRole))
+#define GVC_MIXER_EVENT_ROLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass))
+#define GVC_IS_MIXER_EVENT_ROLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_EVENT_ROLE))
+#define GVC_IS_MIXER_EVENT_ROLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_EVENT_ROLE))
+#define GVC_MIXER_EVENT_ROLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass))
+
+typedef struct GvcMixerEventRolePrivate GvcMixerEventRolePrivate;
+
+typedef struct
+{
+ GvcMixerStream parent;
+ GvcMixerEventRolePrivate *priv;
+} GvcMixerEventRole;
+
+typedef struct
+{
+ GvcMixerStreamClass parent_class;
+} GvcMixerEventRoleClass;
+
+GType gvc_mixer_event_role_get_type (void);
+
+GvcMixerStream * gvc_mixer_event_role_new (pa_context *context,
+ const char *device,
+ GvcChannelMap *channel_map);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_EVENT_ROLE_H */
diff --git a/subprojects/gvc/gvc-mixer-sink-input.c b/subprojects/gvc/gvc-mixer-sink-input.c
new file mode 100644
index 0000000..a359daf
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-sink-input.c
@@ -0,0 +1,159 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-sink-input.h"
+#include "gvc-mixer-stream-private.h"
+#include "gvc-channel-map-private.h"
+
+struct GvcMixerSinkInputPrivate
+{
+ gpointer dummy;
+};
+
+static void gvc_mixer_sink_input_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM)
+
+static gboolean
+gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op)
+{
+ pa_operation *o;
+ guint index;
+ const GvcChannelMap *map;
+ pa_context *context;
+ const pa_cvolume *cv;
+
+ index = gvc_mixer_stream_get_index (stream);
+
+ map = gvc_mixer_stream_get_channel_map (stream);
+
+ cv = gvc_channel_map_get_cvolume(map);
+
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_sink_input_volume (context,
+ index,
+ cv,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_sink_input_volume() failed");
+ return FALSE;
+ }
+
+ *op = o;
+
+ return TRUE;
+}
+
+static gboolean
+gvc_mixer_sink_input_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ pa_operation *o;
+ guint index;
+ pa_context *context;
+
+ index = gvc_mixer_stream_get_index (stream);
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_sink_input_mute (context,
+ index,
+ is_muted,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_sink_input_mute_by_index() failed");
+ return FALSE;
+ }
+
+ pa_operation_unref(o);
+
+ return TRUE;
+}
+
+static void
+gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
+
+ object_class->finalize = gvc_mixer_sink_input_finalize;
+
+ stream_class->push_volume = gvc_mixer_sink_input_push_volume;
+ stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted;
+}
+
+static void
+gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input)
+{
+ sink_input->priv = gvc_mixer_sink_input_get_instance_private (sink_input);
+}
+
+static void
+gvc_mixer_sink_input_finalize (GObject *object)
+{
+ GvcMixerSinkInput *mixer_sink_input;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object));
+
+ mixer_sink_input = GVC_MIXER_SINK_INPUT (object);
+
+ g_return_if_fail (mixer_sink_input->priv != NULL);
+ G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->finalize (object);
+}
+
+/**
+ * gvc_mixer_sink_input_new: (skip)
+ * @context:
+ * @index:
+ * @channel_map:
+ *
+ * Returns:
+ */
+GvcMixerStream *
+gvc_mixer_sink_input_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map)
+{
+ GObject *object;
+
+ object = g_object_new (GVC_TYPE_MIXER_SINK_INPUT,
+ "pa-context", context,
+ "index", index,
+ "channel-map", channel_map,
+ NULL);
+
+ return GVC_MIXER_STREAM (object);
+}
diff --git a/subprojects/gvc/gvc-mixer-sink-input.h b/subprojects/gvc/gvc-mixer-sink-input.h
new file mode 100644
index 0000000..17bf127
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-sink-input.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_SINK_INPUT_H
+#define __GVC_MIXER_SINK_INPUT_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_SINK_INPUT (gvc_mixer_sink_input_get_type ())
+#define GVC_MIXER_SINK_INPUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInput))
+#define GVC_MIXER_SINK_INPUT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass))
+#define GVC_IS_MIXER_SINK_INPUT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK_INPUT))
+#define GVC_IS_MIXER_SINK_INPUT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK_INPUT))
+#define GVC_MIXER_SINK_INPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass))
+
+typedef struct GvcMixerSinkInputPrivate GvcMixerSinkInputPrivate;
+
+typedef struct
+{
+ GvcMixerStream parent;
+ GvcMixerSinkInputPrivate *priv;
+} GvcMixerSinkInput;
+
+typedef struct
+{
+ GvcMixerStreamClass parent_class;
+} GvcMixerSinkInputClass;
+
+GType gvc_mixer_sink_input_get_type (void);
+
+GvcMixerStream * gvc_mixer_sink_input_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_SINK_INPUT_H */
diff --git a/subprojects/gvc/gvc-mixer-sink.c b/subprojects/gvc/gvc-mixer-sink.c
new file mode 100644
index 0000000..a6115c6
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-sink.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-sink.h"
+#include "gvc-mixer-stream-private.h"
+#include "gvc-channel-map-private.h"
+
+struct GvcMixerSinkPrivate
+{
+ gpointer dummy;
+};
+
+static void gvc_mixer_sink_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM)
+
+static gboolean
+gvc_mixer_sink_push_volume (GvcMixerStream *stream, gpointer *op)
+{
+ pa_operation *o;
+ guint index;
+ const GvcChannelMap *map;
+ pa_context *context;
+ const pa_cvolume *cv;
+
+ index = gvc_mixer_stream_get_index (stream);
+
+ map = gvc_mixer_stream_get_channel_map (stream);
+
+ /* set the volume */
+ cv = gvc_channel_map_get_cvolume(map);
+
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_sink_volume_by_index (context,
+ index,
+ cv,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
+ return FALSE;
+ }
+
+ *op = o;
+
+ return TRUE;
+}
+
+static gboolean
+gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ pa_operation *o;
+ guint index;
+ pa_context *context;
+
+ index = gvc_mixer_stream_get_index (stream);
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_sink_mute_by_index (context,
+ index,
+ is_muted,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
+ return FALSE;
+ }
+
+ pa_operation_unref(o);
+
+ return TRUE;
+}
+
+static gboolean
+gvc_mixer_sink_change_port (GvcMixerStream *stream,
+ const char *port)
+{
+ pa_operation *o;
+ guint index;
+ pa_context *context;
+
+ index = gvc_mixer_stream_get_index (stream);
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_sink_port_by_index (context,
+ index,
+ port,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_sink_port_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
+ return FALSE;
+ }
+
+ pa_operation_unref(o);
+
+ return TRUE;
+}
+
+static void
+gvc_mixer_sink_class_init (GvcMixerSinkClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
+
+ object_class->finalize = gvc_mixer_sink_finalize;
+
+ stream_class->push_volume = gvc_mixer_sink_push_volume;
+ stream_class->change_port = gvc_mixer_sink_change_port;
+ stream_class->change_is_muted = gvc_mixer_sink_change_is_muted;
+}
+
+static void
+gvc_mixer_sink_init (GvcMixerSink *sink)
+{
+ sink->priv = gvc_mixer_sink_get_instance_private (sink);
+}
+
+static void
+gvc_mixer_sink_finalize (GObject *object)
+{
+ GvcMixerSink *mixer_sink;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_SINK (object));
+
+ mixer_sink = GVC_MIXER_SINK (object);
+
+ g_return_if_fail (mixer_sink->priv != NULL);
+ G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->finalize (object);
+}
+
+/**
+ * gvc_mixer_sink_new: (skip)
+ * @context:
+ * @index:
+ * @channel_map:
+ *
+ * Returns:
+ */
+GvcMixerStream *
+gvc_mixer_sink_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map)
+
+{
+ GObject *object;
+
+ object = g_object_new (GVC_TYPE_MIXER_SINK,
+ "pa-context", context,
+ "index", index,
+ "channel-map", channel_map,
+ NULL);
+
+ return GVC_MIXER_STREAM (object);
+}
diff --git a/subprojects/gvc/gvc-mixer-sink.h b/subprojects/gvc/gvc-mixer-sink.h
new file mode 100644
index 0000000..3fbe291
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-sink.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_SINK_H
+#define __GVC_MIXER_SINK_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_SINK (gvc_mixer_sink_get_type ())
+#define GVC_MIXER_SINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK, GvcMixerSink))
+#define GVC_MIXER_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass))
+#define GVC_IS_MIXER_SINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK))
+#define GVC_IS_MIXER_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK))
+#define GVC_MIXER_SINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass))
+
+typedef struct GvcMixerSinkPrivate GvcMixerSinkPrivate;
+
+typedef struct
+{
+ GvcMixerStream parent;
+ GvcMixerSinkPrivate *priv;
+} GvcMixerSink;
+
+typedef struct
+{
+ GvcMixerStreamClass parent_class;
+} GvcMixerSinkClass;
+
+GType gvc_mixer_sink_get_type (void);
+
+GvcMixerStream * gvc_mixer_sink_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_SINK_H */
diff --git a/subprojects/gvc/gvc-mixer-source-output.c b/subprojects/gvc/gvc-mixer-source-output.c
new file mode 100644
index 0000000..c4a275a
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-source-output.c
@@ -0,0 +1,160 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-source-output.h"
+#include "gvc-mixer-stream-private.h"
+#include "gvc-channel-map-private.h"
+
+struct GvcMixerSourceOutputPrivate
+{
+ gpointer dummy;
+};
+
+static void gvc_mixer_source_output_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSourceOutput, gvc_mixer_source_output, GVC_TYPE_MIXER_STREAM)
+
+static gboolean
+gvc_mixer_source_output_push_volume (GvcMixerStream *stream, gpointer *op)
+{
+ pa_operation *o;
+ guint index;
+ const GvcChannelMap *map;
+ pa_context *context;
+ const pa_cvolume *cv;
+
+ index = gvc_mixer_stream_get_index (stream);
+
+ map = gvc_mixer_stream_get_channel_map (stream);
+
+ cv = gvc_channel_map_get_cvolume(map);
+
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_source_output_volume (context,
+ index,
+ cv,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_source_output_volume() failed");
+ return FALSE;
+ }
+
+ *op = o;
+
+ return TRUE;
+}
+
+static gboolean
+gvc_mixer_source_output_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ pa_operation *o;
+ guint index;
+ pa_context *context;
+
+ index = gvc_mixer_stream_get_index (stream);
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_source_output_mute (context,
+ index,
+ is_muted,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_source_output_mute_by_index() failed");
+ return FALSE;
+ }
+
+ pa_operation_unref(o);
+
+ return TRUE;
+}
+
+static void
+gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
+
+ object_class->finalize = gvc_mixer_source_output_finalize;
+
+ stream_class->push_volume = gvc_mixer_source_output_push_volume;
+ stream_class->change_is_muted = gvc_mixer_source_output_change_is_muted;
+}
+
+static void
+gvc_mixer_source_output_init (GvcMixerSourceOutput *source_output)
+{
+ source_output->priv = gvc_mixer_source_output_get_instance_private (source_output);
+
+}
+
+static void
+gvc_mixer_source_output_finalize (GObject *object)
+{
+ GvcMixerSourceOutput *mixer_source_output;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_SOURCE_OUTPUT (object));
+
+ mixer_source_output = GVC_MIXER_SOURCE_OUTPUT (object);
+
+ g_return_if_fail (mixer_source_output->priv != NULL);
+ G_OBJECT_CLASS (gvc_mixer_source_output_parent_class)->finalize (object);
+}
+
+/**
+ * gvc_mixer_source_output_new: (skip)
+ * @context:
+ * @index:
+ * @channel_map:
+ *
+ * Returns:
+ */
+GvcMixerStream *
+gvc_mixer_source_output_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map)
+{
+ GObject *object;
+
+ object = g_object_new (GVC_TYPE_MIXER_SOURCE_OUTPUT,
+ "pa-context", context,
+ "index", index,
+ "channel-map", channel_map,
+ NULL);
+
+ return GVC_MIXER_STREAM (object);
+}
diff --git a/subprojects/gvc/gvc-mixer-source-output.h b/subprojects/gvc/gvc-mixer-source-output.h
new file mode 100644
index 0000000..4d9a6d6
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-source-output.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_SOURCE_OUTPUT_H
+#define __GVC_MIXER_SOURCE_OUTPUT_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_SOURCE_OUTPUT (gvc_mixer_source_output_get_type ())
+#define GVC_MIXER_SOURCE_OUTPUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutput))
+#define GVC_MIXER_SOURCE_OUTPUT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass))
+#define GVC_IS_MIXER_SOURCE_OUTPUT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT))
+#define GVC_IS_MIXER_SOURCE_OUTPUT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE_OUTPUT))
+#define GVC_MIXER_SOURCE_OUTPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass))
+
+typedef struct GvcMixerSourceOutputPrivate GvcMixerSourceOutputPrivate;
+
+typedef struct
+{
+ GvcMixerStream parent;
+ GvcMixerSourceOutputPrivate *priv;
+} GvcMixerSourceOutput;
+
+typedef struct
+{
+ GvcMixerStreamClass parent_class;
+} GvcMixerSourceOutputClass;
+
+GType gvc_mixer_source_output_get_type (void);
+
+GvcMixerStream * gvc_mixer_source_output_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_SOURCE_OUTPUT_H */
diff --git a/subprojects/gvc/gvc-mixer-source.c b/subprojects/gvc/gvc-mixer-source.c
new file mode 100644
index 0000000..434eec3
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-source.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-source.h"
+#include "gvc-mixer-stream-private.h"
+#include "gvc-channel-map-private.h"
+
+struct GvcMixerSourcePrivate
+{
+ gpointer dummy;
+};
+
+static void gvc_mixer_source_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSource, gvc_mixer_source, GVC_TYPE_MIXER_STREAM)
+
+static gboolean
+gvc_mixer_source_push_volume (GvcMixerStream *stream, gpointer *op)
+{
+ pa_operation *o;
+ guint index;
+ const GvcChannelMap *map;
+ pa_context *context;
+ const pa_cvolume *cv;
+
+ index = gvc_mixer_stream_get_index (stream);
+
+ map = gvc_mixer_stream_get_channel_map (stream);
+
+ /* set the volume */
+ cv = gvc_channel_map_get_cvolume (map);
+
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_source_volume_by_index (context,
+ index,
+ cv,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
+ return FALSE;
+ }
+
+ *op = o;
+
+ return TRUE;
+}
+
+static gboolean
+gvc_mixer_source_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ pa_operation *o;
+ guint index;
+ pa_context *context;
+
+ index = gvc_mixer_stream_get_index (stream);
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_source_mute_by_index (context,
+ index,
+ is_muted,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
+ return FALSE;
+ }
+
+ pa_operation_unref(o);
+
+ return TRUE;
+}
+
+static gboolean
+gvc_mixer_source_change_port (GvcMixerStream *stream,
+ const char *port)
+{
+ pa_operation *o;
+ guint index;
+ pa_context *context;
+
+ index = gvc_mixer_stream_get_index (stream);
+ context = gvc_mixer_stream_get_pa_context (stream);
+
+ o = pa_context_set_source_port_by_index (context,
+ index,
+ port,
+ NULL,
+ NULL);
+
+ if (o == NULL) {
+ g_warning ("pa_context_set_source_port_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
+ return FALSE;
+ }
+
+ pa_operation_unref(o);
+
+ return TRUE;
+}
+
+static void
+gvc_mixer_source_class_init (GvcMixerSourceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
+
+ object_class->finalize = gvc_mixer_source_finalize;
+
+ stream_class->push_volume = gvc_mixer_source_push_volume;
+ stream_class->change_is_muted = gvc_mixer_source_change_is_muted;
+ stream_class->change_port = gvc_mixer_source_change_port;
+}
+
+static void
+gvc_mixer_source_init (GvcMixerSource *source)
+{
+ source->priv = gvc_mixer_source_get_instance_private (source);
+}
+
+static void
+gvc_mixer_source_finalize (GObject *object)
+{
+ GvcMixerSource *mixer_source;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_SOURCE (object));
+
+ mixer_source = GVC_MIXER_SOURCE (object);
+
+ g_return_if_fail (mixer_source->priv != NULL);
+ G_OBJECT_CLASS (gvc_mixer_source_parent_class)->finalize (object);
+}
+
+/**
+ * gvc_mixer_source_new: (skip)
+ * @context:
+ * @index:
+ * @channel_map:
+ *
+ * Returns:
+ */
+GvcMixerStream *
+gvc_mixer_source_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map)
+
+{
+ GObject *object;
+
+ object = g_object_new (GVC_TYPE_MIXER_SOURCE,
+ "pa-context", context,
+ "index", index,
+ "channel-map", channel_map,
+ NULL);
+
+ return GVC_MIXER_STREAM (object);
+}
diff --git a/subprojects/gvc/gvc-mixer-source.h b/subprojects/gvc/gvc-mixer-source.h
new file mode 100644
index 0000000..bdffe8c
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-source.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_SOURCE_H
+#define __GVC_MIXER_SOURCE_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_SOURCE (gvc_mixer_source_get_type ())
+#define GVC_MIXER_SOURCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSource))
+#define GVC_MIXER_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass))
+#define GVC_IS_MIXER_SOURCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE))
+#define GVC_IS_MIXER_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE))
+#define GVC_MIXER_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass))
+
+typedef struct GvcMixerSourcePrivate GvcMixerSourcePrivate;
+
+typedef struct
+{
+ GvcMixerStream parent;
+ GvcMixerSourcePrivate *priv;
+} GvcMixerSource;
+
+typedef struct
+{
+ GvcMixerStreamClass parent_class;
+} GvcMixerSourceClass;
+
+GType gvc_mixer_source_get_type (void);
+
+GvcMixerStream * gvc_mixer_source_new (pa_context *context,
+ guint index,
+ GvcChannelMap *channel_map);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_SOURCE_H */
diff --git a/subprojects/gvc/gvc-mixer-stream-private.h b/subprojects/gvc/gvc-mixer-stream-private.h
new file mode 100644
index 0000000..b97ecf5
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-stream-private.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_STREAM_PRIVATE_H
+#define __GVC_MIXER_STREAM_PRIVATE_H
+
+#include <glib-object.h>
+
+#include "gvc-channel-map.h"
+
+G_BEGIN_DECLS
+
+pa_context * gvc_mixer_stream_get_pa_context (GvcMixerStream *stream);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_STREAM_PRIVATE_H */
diff --git a/subprojects/gvc/gvc-mixer-stream.c b/subprojects/gvc/gvc-mixer-stream.c
new file mode 100644
index 0000000..f9bcc40
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-stream.c
@@ -0,0 +1,1055 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-stream.h"
+#include "gvc-mixer-stream-private.h"
+#include "gvc-channel-map-private.h"
+#include "gvc-enum-types.h"
+
+static guint32 stream_serial = 1;
+
+struct GvcMixerStreamPrivate
+{
+ pa_context *pa_context;
+ guint id;
+ guint index;
+ guint card_index;
+ GvcChannelMap *channel_map;
+ char *name;
+ char *description;
+ char *application_id;
+ char *icon_name;
+ char *form_factor;
+ char *sysfs_path;
+ gboolean is_muted;
+ gboolean can_decibel;
+ gboolean is_event_stream;
+ gboolean is_virtual;
+ pa_volume_t base_volume;
+ pa_operation *change_volume_op;
+ char *port;
+ char *human_port;
+ GList *ports;
+ GvcMixerStreamState state;
+};
+
+enum
+{
+ PROP_0,
+ PROP_ID,
+ PROP_PA_CONTEXT,
+ PROP_CHANNEL_MAP,
+ PROP_INDEX,
+ PROP_NAME,
+ PROP_DESCRIPTION,
+ PROP_APPLICATION_ID,
+ PROP_ICON_NAME,
+ PROP_FORM_FACTOR,
+ PROP_SYSFS_PATH,
+ PROP_VOLUME,
+ PROP_DECIBEL,
+ PROP_IS_MUTED,
+ PROP_CAN_DECIBEL,
+ PROP_IS_EVENT_STREAM,
+ PROP_IS_VIRTUAL,
+ PROP_CARD_INDEX,
+ PROP_PORT,
+ PROP_STATE,
+ N_PROPS
+};
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
+
+static void gvc_mixer_stream_finalize (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GvcMixerStream, gvc_mixer_stream, G_TYPE_OBJECT)
+
+static void
+free_port (GvcMixerStreamPort *p)
+{
+ g_free (p->port);
+ g_free (p->human_port);
+ g_slice_free (GvcMixerStreamPort, p);
+}
+
+static GvcMixerStreamPort *
+dup_port (GvcMixerStreamPort *p)
+{
+ GvcMixerStreamPort *m;
+
+ m = g_slice_new (GvcMixerStreamPort);
+
+ *m = *p;
+ m->port = g_strdup (p->port);
+ m->human_port = g_strdup (p->human_port);
+
+ return m;
+}
+
+G_DEFINE_BOXED_TYPE (GvcMixerStreamPort, gvc_mixer_stream_port, dup_port, free_port)
+
+static guint32
+get_next_stream_serial (void)
+{
+ guint32 serial;
+
+ serial = stream_serial++;
+
+ if ((gint32)stream_serial < 0) {
+ stream_serial = 1;
+ }
+
+ return serial;
+}
+
+pa_context *
+gvc_mixer_stream_get_pa_context (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+ return stream->priv->pa_context;
+}
+
+guint
+gvc_mixer_stream_get_index (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+ return stream->priv->index;
+}
+
+guint
+gvc_mixer_stream_get_id (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+ return stream->priv->id;
+}
+
+const GvcChannelMap *
+gvc_mixer_stream_get_channel_map (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->channel_map;
+}
+
+/**
+ * gvc_mixer_stream_get_volume:
+ * @stream:
+ *
+ * Returns: (type guint32):
+ */
+pa_volume_t
+gvc_mixer_stream_get_volume (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+
+ return (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME];
+}
+
+gdouble
+gvc_mixer_stream_get_decibel (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+
+ return pa_sw_volume_to_dB(
+ (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]);
+}
+
+/**
+ * gvc_mixer_stream_set_volume:
+ * @stream:
+ * @volume: (type guint32):
+ *
+ * Returns:
+ */
+gboolean
+gvc_mixer_stream_set_volume (GvcMixerStream *stream,
+ pa_volume_t volume)
+{
+ pa_cvolume cv;
+
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
+ pa_cvolume_scale(&cv, volume);
+
+ if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
+ gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
+ gdouble db)
+{
+ pa_cvolume cv;
+
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
+ pa_cvolume_scale(&cv, pa_sw_volume_from_dB(db));
+
+ if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
+ gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_get_is_muted (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+ return stream->priv->is_muted;
+}
+
+gboolean
+gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+ return stream->priv->can_decibel;
+}
+
+gboolean
+gvc_mixer_stream_set_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ if (is_muted != stream->priv->is_muted) {
+ stream->priv->is_muted = is_muted;
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_MUTED]);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_set_can_decibel (GvcMixerStream *stream,
+ gboolean can_decibel)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ if (can_decibel != stream->priv->can_decibel) {
+ stream->priv->can_decibel = can_decibel;
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CAN_DECIBEL]);
+ }
+
+ return TRUE;
+}
+
+const char *
+gvc_mixer_stream_get_name (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->name;
+}
+
+const char *
+gvc_mixer_stream_get_description (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->description;
+}
+
+gboolean
+gvc_mixer_stream_set_name (GvcMixerStream *stream,
+ const char *name)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ g_free (stream->priv->name);
+ stream->priv->name = g_strdup (name);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_NAME]);
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_set_description (GvcMixerStream *stream,
+ const char *description)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ g_free (stream->priv->description);
+ stream->priv->description = g_strdup (description);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_DESCRIPTION]);
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_is_event_stream (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ return stream->priv->is_event_stream;
+}
+
+gboolean
+gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
+ gboolean is_event_stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ stream->priv->is_event_stream = is_event_stream;
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_EVENT_STREAM]);
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_is_virtual (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ return stream->priv->is_virtual;
+}
+
+gboolean
+gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream,
+ gboolean is_virtual)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ stream->priv->is_virtual = is_virtual;
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_VIRTUAL]);
+
+ return TRUE;
+}
+
+const char *
+gvc_mixer_stream_get_application_id (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->application_id;
+}
+
+gboolean
+gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
+ const char *application_id)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ g_free (stream->priv->application_id);
+ stream->priv->application_id = g_strdup (application_id);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_APPLICATION_ID]);
+
+ return TRUE;
+}
+
+static void
+on_channel_map_volume_changed (GvcChannelMap *channel_map,
+ gboolean set,
+ GvcMixerStream *stream)
+{
+ if (set == TRUE)
+ gvc_mixer_stream_push_volume (stream);
+
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
+}
+
+static gboolean
+gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
+ GvcChannelMap *channel_map)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ if (channel_map != NULL) {
+ g_object_ref (channel_map);
+ }
+
+ if (stream->priv->channel_map != NULL) {
+ g_signal_handlers_disconnect_by_func (stream->priv->channel_map,
+ on_channel_map_volume_changed,
+ stream);
+ g_object_unref (stream->priv->channel_map);
+ }
+
+ stream->priv->channel_map = channel_map;
+
+ if (stream->priv->channel_map != NULL) {
+ g_signal_connect (stream->priv->channel_map,
+ "volume-changed",
+ G_CALLBACK (on_channel_map_volume_changed),
+ stream);
+
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CHANNEL_MAP]);
+ }
+
+ return TRUE;
+}
+
+const char *
+gvc_mixer_stream_get_icon_name (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->icon_name;
+}
+
+const char *
+gvc_mixer_stream_get_form_factor (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->form_factor;
+}
+
+const char *
+gvc_mixer_stream_get_sysfs_path (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->sysfs_path;
+}
+
+/**
+ * gvc_mixer_stream_get_gicon:
+ * @stream: a #GvcMixerStream
+ *
+ * Returns: (transfer full): a new #GIcon
+ */
+GIcon *
+gvc_mixer_stream_get_gicon (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ if (stream->priv->icon_name == NULL)
+ return NULL;
+ return g_themed_icon_new_with_default_fallbacks (stream->priv->icon_name);
+}
+
+gboolean
+gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
+ const char *icon_name)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ g_free (stream->priv->icon_name);
+ stream->priv->icon_name = g_strdup (icon_name);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_ICON_NAME]);
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_set_form_factor (GvcMixerStream *stream,
+ const char *form_factor)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ g_free (stream->priv->form_factor);
+ stream->priv->form_factor = g_strdup (form_factor);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_FORM_FACTOR]);
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream,
+ const char *sysfs_path)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ g_free (stream->priv->sysfs_path);
+ stream->priv->sysfs_path = g_strdup (sysfs_path);
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_SYSFS_PATH]);
+
+ return TRUE;
+}
+
+/**
+ * gvc_mixer_stream_get_base_volume:
+ * @stream:
+ *
+ * Returns: (type guint32):
+ */
+pa_volume_t
+gvc_mixer_stream_get_base_volume (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+
+ return stream->priv->base_volume;
+}
+
+/**
+ * gvc_mixer_stream_set_base_volume:
+ * @stream:
+ * @base_volume: (type guint32):
+ *
+ * Returns:
+ */
+gboolean
+gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
+ pa_volume_t base_volume)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ stream->priv->base_volume = base_volume;
+
+ return TRUE;
+}
+
+const GvcMixerStreamPort *
+gvc_mixer_stream_get_port (GvcMixerStream *stream)
+{
+ GList *l;
+
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ g_return_val_if_fail (stream->priv->ports != NULL, NULL);
+
+ for (l = stream->priv->ports; l != NULL; l = l->next) {
+ GvcMixerStreamPort *p = l->data;
+ if (g_strcmp0 (stream->priv->port, p->port) == 0) {
+ return p;
+ }
+ }
+
+ g_assert_not_reached ();
+
+ return NULL;
+}
+
+gboolean
+gvc_mixer_stream_set_port (GvcMixerStream *stream,
+ const char *port)
+{
+ GList *l;
+
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+ g_return_val_if_fail (stream->priv->ports != NULL, FALSE);
+
+ g_free (stream->priv->port);
+ stream->priv->port = g_strdup (port);
+
+ g_free (stream->priv->human_port);
+ stream->priv->human_port = NULL;
+
+ for (l = stream->priv->ports; l != NULL; l = l->next) {
+ GvcMixerStreamPort *p = l->data;
+ if (g_str_equal (stream->priv->port, p->port)) {
+ stream->priv->human_port = g_strdup (p->human_port);
+ break;
+ }
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_PORT]);
+
+ return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_change_port (GvcMixerStream *stream,
+ const char *port)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+ return GVC_MIXER_STREAM_GET_CLASS (stream)->change_port (stream, port);
+}
+
+/**
+ * gvc_mixer_stream_get_ports:
+ *
+ * Return value: (transfer none) (element-type GvcMixerStreamPort):
+ */
+const GList *
+gvc_mixer_stream_get_ports (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+ return stream->priv->ports;
+}
+
+gboolean
+gvc_mixer_stream_set_state (GvcMixerStream *stream,
+ GvcMixerStreamState state)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ if (stream->priv->state != state) {
+ stream->priv->state = state;
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_STATE]);
+ }
+
+ return TRUE;
+}
+
+GvcMixerStreamState
+gvc_mixer_stream_get_state (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), GVC_STREAM_STATE_INVALID);
+ return stream->priv->state;
+}
+
+static int
+sort_ports (GvcMixerStreamPort *a,
+ GvcMixerStreamPort *b)
+{
+ if (a->priority == b->priority)
+ return 0;
+ if (a->priority > b->priority)
+ return 1;
+ return -1;
+}
+
+/**
+ * gvc_mixer_stream_set_ports:
+ * @ports: (transfer full) (element-type GvcMixerStreamPort):
+ */
+gboolean
+gvc_mixer_stream_set_ports (GvcMixerStream *stream,
+ GList *ports)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+ g_return_val_if_fail (stream->priv->ports == NULL, FALSE);
+
+ stream->priv->ports = g_list_sort (ports, (GCompareFunc) sort_ports);
+
+ return TRUE;
+}
+
+guint
+gvc_mixer_stream_get_card_index (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), PA_INVALID_INDEX);
+ return stream->priv->card_index;
+}
+
+gboolean
+gvc_mixer_stream_set_card_index (GvcMixerStream *stream,
+ guint card_index)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ stream->priv->card_index = card_index;
+ g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CARD_INDEX]);
+
+ return TRUE;
+}
+
+static void
+gvc_mixer_stream_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerStream *self = GVC_MIXER_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_PA_CONTEXT:
+ self->priv->pa_context = g_value_get_pointer (value);
+ break;
+ case PROP_INDEX:
+ self->priv->index = g_value_get_ulong (value);
+ break;
+ case PROP_ID:
+ self->priv->id = g_value_get_ulong (value);
+ break;
+ case PROP_CHANNEL_MAP:
+ gvc_mixer_stream_set_channel_map (self, g_value_get_object (value));
+ break;
+ case PROP_NAME:
+ gvc_mixer_stream_set_name (self, g_value_get_string (value));
+ break;
+ case PROP_DESCRIPTION:
+ gvc_mixer_stream_set_description (self, g_value_get_string (value));
+ break;
+ case PROP_APPLICATION_ID:
+ gvc_mixer_stream_set_application_id (self, g_value_get_string (value));
+ break;
+ case PROP_ICON_NAME:
+ gvc_mixer_stream_set_icon_name (self, g_value_get_string (value));
+ break;
+ case PROP_FORM_FACTOR:
+ gvc_mixer_stream_set_form_factor (self, g_value_get_string (value));
+ break;
+ case PROP_SYSFS_PATH:
+ gvc_mixer_stream_set_sysfs_path (self, g_value_get_string (value));
+ break;
+ case PROP_VOLUME:
+ gvc_mixer_stream_set_volume (self, g_value_get_ulong (value));
+ break;
+ case PROP_DECIBEL:
+ gvc_mixer_stream_set_decibel (self, g_value_get_double (value));
+ break;
+ case PROP_IS_MUTED:
+ gvc_mixer_stream_set_is_muted (self, g_value_get_boolean (value));
+ break;
+ case PROP_IS_EVENT_STREAM:
+ gvc_mixer_stream_set_is_event_stream (self, g_value_get_boolean (value));
+ break;
+ case PROP_IS_VIRTUAL:
+ gvc_mixer_stream_set_is_virtual (self, g_value_get_boolean (value));
+ break;
+ case PROP_CAN_DECIBEL:
+ gvc_mixer_stream_set_can_decibel (self, g_value_get_boolean (value));
+ break;
+ case PROP_PORT:
+ gvc_mixer_stream_set_port (self, g_value_get_string (value));
+ break;
+ case PROP_STATE:
+ gvc_mixer_stream_set_state (self, g_value_get_enum (value));
+ break;
+ case PROP_CARD_INDEX:
+ self->priv->card_index = g_value_get_long (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gvc_mixer_stream_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerStream *self = GVC_MIXER_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_PA_CONTEXT:
+ g_value_set_pointer (value, self->priv->pa_context);
+ break;
+ case PROP_INDEX:
+ g_value_set_ulong (value, self->priv->index);
+ break;
+ case PROP_ID:
+ g_value_set_ulong (value, self->priv->id);
+ break;
+ case PROP_CHANNEL_MAP:
+ g_value_set_object (value, self->priv->channel_map);
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, self->priv->name);
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, self->priv->description);
+ break;
+ case PROP_APPLICATION_ID:
+ g_value_set_string (value, self->priv->application_id);
+ break;
+ case PROP_ICON_NAME:
+ g_value_set_string (value, self->priv->icon_name);
+ break;
+ case PROP_FORM_FACTOR:
+ g_value_set_string (value, self->priv->form_factor);
+ break;
+ case PROP_SYSFS_PATH:
+ g_value_set_string (value, self->priv->sysfs_path);
+ break;
+ case PROP_VOLUME:
+ g_value_set_ulong (value,
+ pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map)));
+ break;
+ case PROP_DECIBEL:
+ g_value_set_double (value,
+ pa_sw_volume_to_dB(pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map))));
+ break;
+ case PROP_IS_MUTED:
+ g_value_set_boolean (value, self->priv->is_muted);
+ break;
+ case PROP_IS_EVENT_STREAM:
+ g_value_set_boolean (value, self->priv->is_event_stream);
+ break;
+ case PROP_IS_VIRTUAL:
+ g_value_set_boolean (value, self->priv->is_virtual);
+ break;
+ case PROP_CAN_DECIBEL:
+ g_value_set_boolean (value, self->priv->can_decibel);
+ break;
+ case PROP_PORT:
+ g_value_set_string (value, self->priv->port);
+ break;
+ case PROP_STATE:
+ g_value_set_enum (value, self->priv->state);
+ break;
+ case PROP_CARD_INDEX:
+ g_value_set_long (value, self->priv->card_index);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gvc_mixer_stream_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ GvcMixerStream *self;
+
+ object = G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+ self = GVC_MIXER_STREAM (object);
+
+ self->priv->id = get_next_stream_serial ();
+
+ return object;
+}
+
+static gboolean
+gvc_mixer_stream_real_change_port (GvcMixerStream *stream,
+ const char *port)
+{
+ return FALSE;
+}
+
+static gboolean
+gvc_mixer_stream_real_push_volume (GvcMixerStream *stream, gpointer *op)
+{
+ return FALSE;
+}
+
+static gboolean
+gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ return FALSE;
+}
+
+gboolean
+gvc_mixer_stream_push_volume (GvcMixerStream *stream)
+{
+ pa_operation *op;
+ gboolean ret;
+
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ if (stream->priv->is_event_stream != FALSE)
+ return TRUE;
+
+ g_debug ("Pushing new volume to stream '%s' (%s)",
+ stream->priv->description, stream->priv->name);
+
+ ret = GVC_MIXER_STREAM_GET_CLASS (stream)->push_volume (stream, (gpointer *) &op);
+ if (ret) {
+ if (stream->priv->change_volume_op != NULL)
+ pa_operation_unref (stream->priv->change_volume_op);
+ stream->priv->change_volume_op = op;
+ }
+ return ret;
+}
+
+gboolean
+gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted)
+{
+ gboolean ret;
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+ ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_is_muted (stream, is_muted);
+ return ret;
+}
+
+gboolean
+gvc_mixer_stream_is_running (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+ if (stream->priv->change_volume_op == NULL)
+ return FALSE;
+
+ if ((pa_operation_get_state(stream->priv->change_volume_op) == PA_OPERATION_RUNNING))
+ return TRUE;
+
+ pa_operation_unref(stream->priv->change_volume_op);
+ stream->priv->change_volume_op = NULL;
+
+ return FALSE;
+}
+
+static void
+gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = gvc_mixer_stream_constructor;
+ gobject_class->finalize = gvc_mixer_stream_finalize;
+ gobject_class->set_property = gvc_mixer_stream_set_property;
+ gobject_class->get_property = gvc_mixer_stream_get_property;
+
+ klass->push_volume = gvc_mixer_stream_real_push_volume;
+ klass->change_port = gvc_mixer_stream_real_change_port;
+ klass->change_is_muted = gvc_mixer_stream_real_change_is_muted;
+
+ obj_props[PROP_INDEX] = g_param_spec_ulong ("index",
+ "Index",
+ "The index for this stream",
+ 0, G_MAXULONG, 0,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_ID] = g_param_spec_ulong ("id",
+ "id",
+ "The id for this stream",
+ 0, G_MAXULONG, 0,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_CHANNEL_MAP] = g_param_spec_object ("channel-map",
+ "channel map",
+ "The channel map for this stream",
+ GVC_TYPE_CHANNEL_MAP,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_PA_CONTEXT] = g_param_spec_pointer ("pa-context",
+ "PulseAudio context",
+ "The PulseAudio context for this stream",
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_VOLUME] = g_param_spec_ulong ("volume",
+ "Volume",
+ "The volume for this stream",
+ 0, G_MAXULONG, 0,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_DECIBEL] = g_param_spec_double ("decibel",
+ "Decibel",
+ "The decibel level for this stream",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_NAME] = g_param_spec_string ("name",
+ "Name",
+ "Name to display for this stream",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_DESCRIPTION] = g_param_spec_string ("description",
+ "Description",
+ "Description to display for this stream",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_APPLICATION_ID] = g_param_spec_string ("application-id",
+ "Application identifier",
+ "Application identifier for this stream",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_ICON_NAME] = g_param_spec_string ("icon-name",
+ "Icon Name",
+ "Name of icon to display for this stream",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_FORM_FACTOR] = g_param_spec_string ("form-factor",
+ "Form Factor",
+ "Device form factor for this stream, as reported by PulseAudio",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_SYSFS_PATH] = g_param_spec_string ("sysfs-path",
+ "Sysfs path",
+ "Sysfs path for the device associated with this stream",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_IS_MUTED] = g_param_spec_boolean ("is-muted",
+ "is muted",
+ "Whether stream is muted",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_CAN_DECIBEL] = g_param_spec_boolean ("can-decibel",
+ "can decibel",
+ "Whether stream volume can be converted to decibel units",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_IS_EVENT_STREAM] = g_param_spec_boolean ("is-event-stream",
+ "is event stream",
+ "Whether stream's role is to play an event",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_IS_VIRTUAL] = g_param_spec_boolean ("is-virtual",
+ "is virtual stream",
+ "Whether the stream is virtual",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_PORT] = g_param_spec_string ("port",
+ "Port",
+ "The name of the current port for this stream",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_STATE] = g_param_spec_enum ("state",
+ "State",
+ "The current state of this stream",
+ GVC_TYPE_MIXER_STREAM_STATE,
+ GVC_STREAM_STATE_INVALID,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+ obj_props[PROP_CARD_INDEX] = g_param_spec_long ("card-index",
+ "Card index",
+ "The index of the card for this stream",
+ PA_INVALID_INDEX, G_MAXLONG, PA_INVALID_INDEX,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
+}
+
+static void
+gvc_mixer_stream_init (GvcMixerStream *stream)
+{
+ stream->priv = gvc_mixer_stream_get_instance_private (stream);
+}
+
+static void
+gvc_mixer_stream_finalize (GObject *object)
+{
+ GvcMixerStream *mixer_stream;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_IS_MIXER_STREAM (object));
+
+ mixer_stream = GVC_MIXER_STREAM (object);
+
+ g_return_if_fail (mixer_stream->priv != NULL);
+
+ g_object_unref (mixer_stream->priv->channel_map);
+ mixer_stream->priv->channel_map = NULL;
+
+ g_free (mixer_stream->priv->name);
+ mixer_stream->priv->name = NULL;
+
+ g_free (mixer_stream->priv->description);
+ mixer_stream->priv->description = NULL;
+
+ g_free (mixer_stream->priv->application_id);
+ mixer_stream->priv->application_id = NULL;
+
+ g_free (mixer_stream->priv->icon_name);
+ mixer_stream->priv->icon_name = NULL;
+
+ g_free (mixer_stream->priv->form_factor);
+ mixer_stream->priv->form_factor = NULL;
+
+ g_free (mixer_stream->priv->sysfs_path);
+ mixer_stream->priv->sysfs_path = NULL;
+
+ g_free (mixer_stream->priv->port);
+ mixer_stream->priv->port = NULL;
+
+ g_free (mixer_stream->priv->human_port);
+ mixer_stream->priv->human_port = NULL;
+
+ g_list_free_full (mixer_stream->priv->ports, (GDestroyNotify) free_port);
+ mixer_stream->priv->ports = NULL;
+
+ if (mixer_stream->priv->change_volume_op) {
+ pa_operation_unref(mixer_stream->priv->change_volume_op);
+ mixer_stream->priv->change_volume_op = NULL;
+ }
+
+ G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->finalize (object);
+}
diff --git a/subprojects/gvc/gvc-mixer-stream.h b/subprojects/gvc/gvc-mixer-stream.h
new file mode 100644
index 0000000..586ec75
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-stream.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_STREAM_H
+#define __GVC_MIXER_STREAM_H
+
+#include <glib-object.h>
+#include "gvc-pulseaudio-fake.h"
+#include "gvc-channel-map.h"
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_STREAM (gvc_mixer_stream_get_type ())
+#define GVC_MIXER_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStream))
+#define GVC_MIXER_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass))
+#define GVC_IS_MIXER_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_STREAM))
+#define GVC_IS_MIXER_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_STREAM))
+#define GVC_MIXER_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass))
+
+typedef struct GvcMixerStreamPrivate GvcMixerStreamPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GvcMixerStreamPrivate *priv;
+} GvcMixerStream;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ /* vtable */
+ gboolean (*push_volume) (GvcMixerStream *stream,
+ gpointer *operation);
+ gboolean (*change_is_muted) (GvcMixerStream *stream,
+ gboolean is_muted);
+ gboolean (*change_port) (GvcMixerStream *stream,
+ const char *port);
+} GvcMixerStreamClass;
+
+typedef struct
+{
+ char *port;
+ char *human_port;
+ guint priority;
+ gboolean available;
+} GvcMixerStreamPort;
+
+typedef enum
+{
+ GVC_STREAM_STATE_INVALID,
+ GVC_STREAM_STATE_RUNNING,
+ GVC_STREAM_STATE_IDLE,
+ GVC_STREAM_STATE_SUSPENDED
+} GvcMixerStreamState;
+
+GType gvc_mixer_stream_port_get_type (void) G_GNUC_CONST;
+GType gvc_mixer_stream_get_type (void) G_GNUC_CONST;
+
+guint gvc_mixer_stream_get_index (GvcMixerStream *stream);
+guint gvc_mixer_stream_get_id (GvcMixerStream *stream);
+const GvcChannelMap *gvc_mixer_stream_get_channel_map(GvcMixerStream *stream);
+const GvcMixerStreamPort *gvc_mixer_stream_get_port (GvcMixerStream *stream);
+const GList * gvc_mixer_stream_get_ports (GvcMixerStream *stream);
+gboolean gvc_mixer_stream_change_port (GvcMixerStream *stream,
+ const char *port);
+
+pa_volume_t gvc_mixer_stream_get_volume (GvcMixerStream *stream);
+gdouble gvc_mixer_stream_get_decibel (GvcMixerStream *stream);
+gboolean gvc_mixer_stream_push_volume (GvcMixerStream *stream);
+pa_volume_t gvc_mixer_stream_get_base_volume (GvcMixerStream *stream);
+
+gboolean gvc_mixer_stream_get_is_muted (GvcMixerStream *stream);
+gboolean gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream);
+gboolean gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
+ gboolean is_muted);
+gboolean gvc_mixer_stream_is_running (GvcMixerStream *stream);
+const char * gvc_mixer_stream_get_name (GvcMixerStream *stream);
+const char * gvc_mixer_stream_get_icon_name (GvcMixerStream *stream);
+const char * gvc_mixer_stream_get_form_factor (GvcMixerStream *stream);
+const char * gvc_mixer_stream_get_sysfs_path (GvcMixerStream *stream);
+GIcon * gvc_mixer_stream_get_gicon (GvcMixerStream *stream);
+const char * gvc_mixer_stream_get_description (GvcMixerStream *stream);
+const char * gvc_mixer_stream_get_application_id (GvcMixerStream *stream);
+gboolean gvc_mixer_stream_is_event_stream (GvcMixerStream *stream);
+gboolean gvc_mixer_stream_is_virtual (GvcMixerStream *stream);
+guint gvc_mixer_stream_get_card_index (GvcMixerStream *stream);
+GvcMixerStreamState gvc_mixer_stream_get_state (GvcMixerStream *stream);
+
+/* private */
+gboolean gvc_mixer_stream_set_volume (GvcMixerStream *stream,
+ pa_volume_t volume);
+gboolean gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
+ gdouble db);
+gboolean gvc_mixer_stream_set_is_muted (GvcMixerStream *stream,
+ gboolean is_muted);
+gboolean gvc_mixer_stream_set_can_decibel (GvcMixerStream *stream,
+ gboolean can_decibel);
+gboolean gvc_mixer_stream_set_name (GvcMixerStream *stream,
+ const char *name);
+gboolean gvc_mixer_stream_set_description (GvcMixerStream *stream,
+ const char *description);
+gboolean gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
+ const char *name);
+gboolean gvc_mixer_stream_set_form_factor (GvcMixerStream *stream,
+ const char *form_factor);
+gboolean gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream,
+ const char *sysfs_path);
+gboolean gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
+ gboolean is_event_stream);
+gboolean gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream,
+ gboolean is_event_stream);
+gboolean gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
+ const char *application_id);
+gboolean gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
+ pa_volume_t base_volume);
+gboolean gvc_mixer_stream_set_port (GvcMixerStream *stream,
+ const char *port);
+gboolean gvc_mixer_stream_set_ports (GvcMixerStream *stream,
+ GList *ports);
+gboolean gvc_mixer_stream_set_card_index (GvcMixerStream *stream,
+ guint card_index);
+gboolean gvc_mixer_stream_set_state (GvcMixerStream *stream,
+ GvcMixerStreamState state);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_STREAM_H */
diff --git a/subprojects/gvc/gvc-mixer-ui-device.c b/subprojects/gvc/gvc-mixer-ui-device.c
new file mode 100644
index 0000000..db1a694
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-ui-device.c
@@ -0,0 +1,744 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * gvc-mixer-ui-device.c
+ * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
+ * Copyright (C) 2012 David Henningsson, Canonical Ltd. <david.henningsson@canonical.com>
+ *
+ * gvc-mixer-ui-device.c is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gvc-mixer-ui-device.c is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "gvc-mixer-ui-device.h"
+#include "gvc-mixer-card.h"
+
+struct GvcMixerUIDevicePrivate
+{
+ gchar *first_line_desc;
+ gchar *second_line_desc;
+
+ GvcMixerCard *card;
+ gchar *port_name;
+ char *icon_name;
+ guint stream_id;
+ guint id;
+ gboolean port_available;
+
+ /* These two lists contain pointers to GvcMixerCardProfile objects. Those objects are owned by GvcMixerCard. *
+ * TODO: Do we want to add a weak reference to the GvcMixerCard for this reason? */
+ GList *supported_profiles; /* all profiles supported by this port.*/
+ GList *profiles; /* profiles to be added to combobox, subset of supported_profiles. */
+ GvcMixerUIDeviceDirection type;
+ gboolean disable_profile_swapping;
+ gchar *user_preferred_profile;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DESC_LINE_1,
+ PROP_DESC_LINE_2,
+ PROP_CARD,
+ PROP_PORT_NAME,
+ PROP_STREAM_ID,
+ PROP_UI_DEVICE_TYPE,
+ PROP_PORT_AVAILABLE,
+ PROP_ICON_NAME,
+ N_PROPS
+};
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
+
+static void gvc_mixer_ui_device_finalize (GObject *object);
+
+static void gvc_mixer_ui_device_set_icon_name (GvcMixerUIDevice *device,
+ const char *icon_name);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerUIDevice, gvc_mixer_ui_device, G_TYPE_OBJECT)
+
+static guint32
+get_next_output_serial (void)
+{
+ static guint32 output_serial = 1;
+ guint32 serial;
+
+ serial = output_serial++;
+
+ if ((gint32)output_serial < 0)
+ output_serial = 1;
+
+ return serial;
+}
+
+static void
+gvc_mixer_ui_device_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object);
+
+ switch (property_id) {
+ case PROP_DESC_LINE_1:
+ g_value_set_string (value, self->priv->first_line_desc);
+ break;
+ case PROP_DESC_LINE_2:
+ g_value_set_string (value, self->priv->second_line_desc);
+ break;
+ case PROP_CARD:
+ g_value_set_pointer (value, self->priv->card);
+ break;
+ case PROP_PORT_NAME:
+ g_value_set_string (value, self->priv->port_name);
+ break;
+ case PROP_STREAM_ID:
+ g_value_set_uint (value, self->priv->stream_id);
+ break;
+ case PROP_UI_DEVICE_TYPE:
+ g_value_set_uint (value, (guint)self->priv->type);
+ break;
+ case PROP_PORT_AVAILABLE:
+ g_value_set_boolean (value, self->priv->port_available);
+ break;
+ case PROP_ICON_NAME:
+ g_value_set_string (value, gvc_mixer_ui_device_get_icon_name (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gvc_mixer_ui_device_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object);
+
+ switch (property_id) {
+ case PROP_DESC_LINE_1:
+ g_free (self->priv->first_line_desc);
+ self->priv->first_line_desc = g_value_dup_string (value);
+ g_debug ("gvc-mixer-output-set-property - 1st line: %s",
+ self->priv->first_line_desc);
+ break;
+ case PROP_DESC_LINE_2:
+ g_free (self->priv->second_line_desc);
+ self->priv->second_line_desc = g_value_dup_string (value);
+ g_debug ("gvc-mixer-output-set-property - 2nd line: %s",
+ self->priv->second_line_desc);
+ break;
+ case PROP_CARD:
+ self->priv->card = g_value_get_pointer (value);
+ g_debug ("gvc-mixer-output-set-property - card: %p",
+ self->priv->card);
+ break;
+ case PROP_PORT_NAME:
+ g_free (self->priv->port_name);
+ self->priv->port_name = g_value_dup_string (value);
+ g_debug ("gvc-mixer-output-set-property - card port name: %s",
+ self->priv->port_name);
+ break;
+ case PROP_STREAM_ID:
+ self->priv->stream_id = g_value_get_uint (value);
+ g_debug ("gvc-mixer-output-set-property - sink/source id: %i",
+ self->priv->stream_id);
+ break;
+ case PROP_UI_DEVICE_TYPE:
+ self->priv->type = (GvcMixerUIDeviceDirection) g_value_get_uint (value);
+ break;
+ case PROP_PORT_AVAILABLE:
+ self->priv->port_available = g_value_get_boolean (value);
+ g_debug ("gvc-mixer-output-set-property - port available %i, value passed in %i",
+ self->priv->port_available, g_value_get_boolean (value));
+ break;
+ case PROP_ICON_NAME:
+ gvc_mixer_ui_device_set_icon_name (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gvc_mixer_ui_device_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ GvcMixerUIDevice *self;
+
+ object = G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+ self = GVC_MIXER_UI_DEVICE (object);
+ self->priv->id = get_next_output_serial ();
+ self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID;
+ return object;
+}
+
+static void
+gvc_mixer_ui_device_init (GvcMixerUIDevice *device)
+{
+ device->priv = gvc_mixer_ui_device_get_instance_private (device);
+}
+
+static void
+gvc_mixer_ui_device_dispose (GObject *object)
+{
+ GvcMixerUIDevice *device;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GVC_MIXER_UI_DEVICE (object));
+
+ device = GVC_MIXER_UI_DEVICE (object);
+
+ g_clear_pointer (&device->priv->port_name, g_free);
+ g_clear_pointer (&device->priv->icon_name, g_free);
+ g_clear_pointer (&device->priv->first_line_desc, g_free);
+ g_clear_pointer (&device->priv->second_line_desc, g_free);
+ g_clear_pointer (&device->priv->profiles, g_list_free);
+ g_clear_pointer (&device->priv->supported_profiles, g_list_free);
+ g_clear_pointer (&device->priv->user_preferred_profile, g_free);
+
+ G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->dispose (object);
+}
+
+static void
+gvc_mixer_ui_device_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->finalize (object);
+}
+
+static void
+gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass)
+{
+ GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = gvc_mixer_ui_device_constructor;
+ object_class->dispose = gvc_mixer_ui_device_dispose;
+ object_class->finalize = gvc_mixer_ui_device_finalize;
+ object_class->set_property = gvc_mixer_ui_device_set_property;
+ object_class->get_property = gvc_mixer_ui_device_get_property;
+
+ obj_props[PROP_DESC_LINE_1] =
+ g_param_spec_string ("description",
+ "Description construct prop",
+ "Set first line description",
+ "no-name-set",
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ obj_props[PROP_DESC_LINE_2] =
+ g_param_spec_string ("origin",
+ "origin construct prop",
+ "Set second line description name",
+ "no-name-set",
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ obj_props[PROP_CARD] =
+ g_param_spec_pointer ("card",
+ "Card from pulse",
+ "Set/Get card",
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ obj_props[PROP_PORT_NAME] =
+ g_param_spec_string ("port-name",
+ "port-name construct prop",
+ "Set port-name",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ obj_props[PROP_STREAM_ID] =
+ g_param_spec_uint ("stream-id",
+ "stream id assigned by gvc-stream",
+ "Set/Get stream id",
+ 0,
+ G_MAXUINT,
+ GVC_MIXER_UI_DEVICE_INVALID,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ obj_props[PROP_UI_DEVICE_TYPE] =
+ g_param_spec_uint ("type",
+ "ui-device type",
+ "determine whether its an input and output",
+ 0, 1, 0,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ obj_props[PROP_PORT_AVAILABLE] =
+ g_param_spec_boolean ("port-available",
+ "available",
+ "determine whether this port is available",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ obj_props[PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name",
+ "Icon Name",
+ "Name of icon to display for this card",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, obj_props);
+}
+
+/* Removes the part of the string that starts with skip_prefix
+ * ie. corresponding to the other direction.
+ * Normally either "input:" or "output:"
+ *
+ * Example: if given the input string "output:hdmi-stereo+input:analog-stereo" and
+ * skip_prefix "input:", the resulting string is "output:hdmi-stereo".
+ *
+ * The returned string must be freed with g_free().
+ */
+static gchar *
+get_profile_canonical_name (const gchar *profile_name, const gchar *skip_prefix)
+{
+ gchar *result = NULL;
+ gchar **s;
+ guint i;
+
+ /* optimisation for the simple case. */
+ if (strstr (profile_name, skip_prefix) == NULL)
+ return g_strdup (profile_name);
+
+ s = g_strsplit (profile_name, "+", 0);
+ for (i = 0; i < g_strv_length (s); i++) {
+ if (g_str_has_prefix (s[i], skip_prefix))
+ continue;
+ if (result == NULL)
+ result = g_strdup (s[i]);
+ else {
+ gchar *c = g_strdup_printf("%s+%s", result, s[i]);
+ g_free(result);
+ result = c;
+ }
+ }
+
+ g_strfreev(s);
+
+ if (!result)
+ return g_strdup("off");
+
+ return result;
+}
+
+const gchar *
+gvc_mixer_ui_device_get_matching_profile (GvcMixerUIDevice *device, const gchar *profile)
+{
+ const gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:";
+ gchar *target_cname = get_profile_canonical_name (profile, skip_prefix);
+ GList *l;
+ gchar *result = NULL;
+
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+ g_return_val_if_fail (profile != NULL, NULL);
+
+ for (l = device->priv->profiles; l != NULL; l = l->next) {
+ gchar *canonical_name;
+ GvcMixerCardProfile* p = l->data;
+ canonical_name = get_profile_canonical_name (p->profile, skip_prefix);
+ if (strcmp (canonical_name, target_cname) == 0)
+ result = p->profile;
+ g_free (canonical_name);
+ }
+
+ g_free (target_cname);
+ g_debug ("Matching profile for '%s' is '%s'", profile, result ? result : "(null)");
+ return result;
+}
+
+
+static void
+add_canonical_names_of_profiles (GvcMixerUIDevice *device,
+ const GList *in_profiles,
+ GHashTable *added_profiles,
+ const gchar *skip_prefix,
+ gboolean only_canonical)
+{
+ const GList *l;
+
+ for (l = in_profiles; l != NULL; l = l->next) {
+ gchar *canonical_name;
+ GvcMixerCardProfile* p = l->data;
+
+ canonical_name = get_profile_canonical_name (p->profile, skip_prefix);
+ g_debug ("The canonical name for '%s' is '%s'", p->profile, canonical_name);
+
+ /* Have we already added the canonical version of this profile? */
+ if (g_hash_table_contains (added_profiles, canonical_name)) {
+ g_free (canonical_name);
+ continue;
+ }
+
+ if (only_canonical && strcmp (p->profile, canonical_name) != 0) {
+ g_free (canonical_name);
+ continue;
+ }
+
+ g_free (canonical_name);
+
+ /* https://bugzilla.gnome.org/show_bug.cgi?id=693654
+ * Don't add a profile that will make the UI device completely disappear */
+ if (p->n_sinks == 0 && p->n_sources == 0)
+ continue;
+
+ g_debug ("Adding profile to combobox: '%s' - '%s'", p->profile, p->human_profile);
+ g_hash_table_insert (added_profiles, g_strdup (p->profile), p);
+ device->priv->profiles = g_list_append (device->priv->profiles, p);
+ }
+}
+
+/**
+ * gvc_mixer_ui_device_set_profiles:
+ * @in_profiles: (element-type Gvc.MixerCardProfile): a list of GvcMixerCardProfile
+ *
+ * Assigns value to
+ * - device->priv->profiles (profiles to be added to combobox)
+ * - device->priv->supported_profiles (all profiles of this port)
+ * - device->priv->disable_profile_swapping (whether to show the combobox)
+ *
+ * This method attempts to reduce the list of profiles visible to the user by figuring out
+ * from the context of that device (whether it's an input or an output) what profiles
+ * actually provide an alternative.
+ *
+ * It does this by the following.
+ * - It ignores off profiles.
+ * - It takes the canonical name of the profile. That name is what you get when you
+ * ignore the other direction.
+ * - In the first iteration, it only adds the names of canonical profiles - i e
+ * when the other side is turned off.
+ * - Normally the first iteration covers all cases, but sometimes (e g bluetooth)
+ * it doesn't, so add other profiles whose canonical name isn't already added
+ * in a second iteration.
+ */
+void
+gvc_mixer_ui_device_set_profiles (GvcMixerUIDevice *device,
+ const GList *in_profiles)
+{
+ GHashTable *added_profiles;
+ const gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:";
+
+ g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device));
+
+ g_debug ("Set profiles for '%s'", gvc_mixer_ui_device_get_description(device));
+
+ if (in_profiles == NULL)
+ return;
+
+ device->priv->supported_profiles = g_list_copy ((GList*) in_profiles);
+
+ added_profiles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ /* Run two iterations: First, add profiles which are canonical themselves,
+ * Second, add profiles for which the canonical name is not added already. */
+
+ add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, TRUE);
+ add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, FALSE);
+
+ /* TODO: Consider adding the "Off" profile here */
+
+ device->priv->disable_profile_swapping = g_hash_table_size (added_profiles) <= 1;
+ g_hash_table_destroy (added_profiles);
+}
+
+/**
+ * gvc_mixer_ui_device_get_best_profile:
+ * @selected: (allow-none): The selected profile or its canonical name or %NULL for any profile
+ * @current: The currently selected profile
+ *
+ * Returns: (transfer none): a profile name, valid as long as the UI device profiles are.
+ */
+const gchar *
+gvc_mixer_ui_device_get_best_profile (GvcMixerUIDevice *device,
+ const gchar *selected,
+ const gchar *current)
+{
+ GList *candidates, *l;
+ const gchar *result;
+ const gchar *skip_prefix;
+ gchar *canonical_name_selected;
+
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+ g_return_val_if_fail (current != NULL, NULL);
+
+ if (device->priv->type == UIDeviceInput)
+ skip_prefix = "output:";
+ else
+ skip_prefix = "input:";
+
+ /* First make a list of profiles acceptable to switch to */
+ canonical_name_selected = NULL;
+ if (selected)
+ canonical_name_selected = get_profile_canonical_name (selected, skip_prefix);
+
+ candidates = NULL;
+ for (l = device->priv->supported_profiles; l != NULL; l = l->next) {
+ gchar *canonical_name;
+ GvcMixerCardProfile* p = l->data;
+ canonical_name = get_profile_canonical_name (p->profile, skip_prefix);
+ if (!canonical_name_selected || strcmp (canonical_name, canonical_name_selected) == 0) {
+ candidates = g_list_append (candidates, p);
+ g_debug ("Candidate for profile switching: '%s'", p->profile);
+ }
+ g_free (canonical_name);
+ }
+
+ if (!candidates) {
+ g_warning ("No suitable profile candidates for '%s'", selected ? selected : "(null)");
+ g_free (canonical_name_selected);
+ return current;
+ }
+
+ /* 1) Maybe we can skip profile switching altogether? */
+ result = NULL;
+ for (l = candidates; (result == NULL) && (l != NULL); l = l->next) {
+ GvcMixerCardProfile* p = l->data;
+ if (strcmp (current, p->profile) == 0)
+ result = p->profile;
+ }
+
+ /* 2) Try to keep the other side unchanged if possible */
+ if (result == NULL) {
+ guint prio = 0;
+ const gchar *skip_prefix_reverse = device->priv->type == UIDeviceInput ? "input:" : "output:";
+ gchar *current_reverse = get_profile_canonical_name (current, skip_prefix_reverse);
+ for (l = candidates; l != NULL; l = l->next) {
+ gchar *p_reverse;
+ GvcMixerCardProfile* p = l->data;
+ p_reverse = get_profile_canonical_name (p->profile, skip_prefix_reverse);
+ g_debug ("Comparing '%s' (from '%s') with '%s', prio %d", p_reverse, p->profile, current_reverse, p->priority);
+ if (strcmp (p_reverse, current_reverse) == 0 && (!result || p->priority > prio)) {
+ result = p->profile;
+ prio = p->priority;
+ }
+ g_free (p_reverse);
+ }
+ g_free (current_reverse);
+ }
+
+ /* 3) All right, let's just pick the profile with highest priority.
+ * TODO: We could consider asking a GUI question if this stops streams
+ * in the other direction */
+ if (result == NULL) {
+ guint prio = 0;
+ for (l = candidates; l != NULL; l = l->next) {
+ GvcMixerCardProfile* p = l->data;
+ if ((p->priority > prio) || !result) {
+ result = p->profile;
+ prio = p->priority;
+ }
+ }
+ }
+
+ g_list_free (candidates);
+ g_free (canonical_name_selected);
+ return result;
+}
+
+const gchar *
+gvc_mixer_ui_device_get_active_profile (GvcMixerUIDevice* device)
+{
+ GvcMixerCardProfile *profile;
+
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ if (device->priv->card == NULL) {
+ g_warning ("Device did not have an appropriate card");
+ return NULL;
+ }
+
+ profile = gvc_mixer_card_get_profile (device->priv->card);
+ return gvc_mixer_ui_device_get_matching_profile (device, profile->profile);
+}
+
+gboolean
+gvc_mixer_ui_device_should_profiles_be_hidden (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);
+
+ return device->priv->disable_profile_swapping;
+}
+
+/**
+ * gvc_mixer_ui_device_get_profiles:
+ * @device:
+ *
+ * Returns: (transfer none) (element-type Gvc.MixerCardProfile):
+ */
+GList*
+gvc_mixer_ui_device_get_profiles (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ return device->priv->profiles;
+}
+
+/**
+ * gvc_mixer_ui_device_get_supported_profiles:
+ * @device:
+ *
+ * Returns: (transfer none) (element-type Gvc.MixerCardProfile):
+ */
+GList*
+gvc_mixer_ui_device_get_supported_profiles (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ return device->priv->supported_profiles;
+}
+
+guint
+gvc_mixer_ui_device_get_id (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0);
+
+ return device->priv->id;
+}
+
+guint
+gvc_mixer_ui_device_get_stream_id (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0);
+
+ return device->priv->stream_id;
+}
+
+void
+gvc_mixer_ui_device_invalidate_stream (GvcMixerUIDevice *self)
+{
+ g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (self));
+
+ self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID;
+}
+
+const gchar *
+gvc_mixer_ui_device_get_description (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ return device->priv->first_line_desc;
+}
+
+const char *
+gvc_mixer_ui_device_get_icon_name (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ if (device->priv->icon_name)
+ return device->priv->icon_name;
+
+ if (device->priv->card)
+ return gvc_mixer_card_get_icon_name (device->priv->card);
+
+ return NULL;
+}
+
+static void
+gvc_mixer_ui_device_set_icon_name (GvcMixerUIDevice *device,
+ const char *icon_name)
+{
+ g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device));
+
+ g_free (device->priv->icon_name);
+ device->priv->icon_name = g_strdup (icon_name);
+ g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_ICON_NAME]);
+}
+
+
+/**
+ * gvc_mixer_ui_device_get_gicon:
+ * @device:
+ *
+ * Returns: (transfer full):
+ */
+GIcon *
+gvc_mixer_ui_device_get_gicon (GvcMixerUIDevice *device)
+{
+ const char *icon_name;
+
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ icon_name = gvc_mixer_ui_device_get_icon_name (device);
+
+ if (icon_name != NULL)
+ return g_themed_icon_new_with_default_fallbacks (icon_name);
+ else
+ return NULL;
+}
+
+const gchar *
+gvc_mixer_ui_device_get_origin (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ return device->priv->second_line_desc;
+}
+
+const gchar*
+gvc_mixer_ui_device_get_user_preferred_profile (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ return device->priv->user_preferred_profile;
+}
+
+const gchar *
+gvc_mixer_ui_device_get_top_priority_profile (GvcMixerUIDevice *device)
+{
+ GList *last;
+ GvcMixerCardProfile *profile;
+
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ last = g_list_last (device->priv->supported_profiles);
+ profile = last->data;
+
+ return profile->profile;
+}
+
+void
+gvc_mixer_ui_device_set_user_preferred_profile (GvcMixerUIDevice *device,
+ const gchar *profile)
+{
+ g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device));
+ g_return_if_fail (profile != NULL);
+
+ g_free (device->priv->user_preferred_profile);
+ device->priv->user_preferred_profile = g_strdup (profile);
+}
+
+const gchar *
+gvc_mixer_ui_device_get_port (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
+
+ return device->priv->port_name;
+}
+
+gboolean
+gvc_mixer_ui_device_has_ports (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);
+
+ return (device->priv->port_name != NULL);
+}
+
+gboolean
+gvc_mixer_ui_device_is_output (GvcMixerUIDevice *device)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);
+
+ return (device->priv->type == UIDeviceOutput);
+}
diff --git a/subprojects/gvc/gvc-mixer-ui-device.h b/subprojects/gvc/gvc-mixer-ui-device.h
new file mode 100644
index 0000000..69095cb
--- /dev/null
+++ b/subprojects/gvc/gvc-mixer-ui-device.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gvc-mixer-ui-device.h is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GVC_MIXER_UI_DEVICE_H_
+#define _GVC_MIXER_UI_DEVICE_H_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_UI_DEVICE (gvc_mixer_ui_device_get_type ())
+#define GVC_MIXER_UI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDevice))
+#define GVC_MIXER_UI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass))
+#define GVC_IS_MIXER_UI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GVC_TYPE_MIXER_UI_DEVICE))
+#define GVC_IS_MIXER_UI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GVC_TYPE_MIXER_UI_DEVICE))
+#define GVC_MIXER_UI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass))
+
+#define GVC_MIXER_UI_DEVICE_INVALID 0
+
+typedef struct GvcMixerUIDevicePrivate GvcMixerUIDevicePrivate;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GvcMixerUIDeviceClass;
+
+typedef struct
+{
+ GObject parent_instance;
+ GvcMixerUIDevicePrivate *priv;
+} GvcMixerUIDevice;
+
+typedef enum
+{
+ UIDeviceInput,
+ UIDeviceOutput,
+} GvcMixerUIDeviceDirection;
+
+GType gvc_mixer_ui_device_get_type (void) G_GNUC_CONST;
+
+guint gvc_mixer_ui_device_get_id (GvcMixerUIDevice *device);
+guint gvc_mixer_ui_device_get_stream_id (GvcMixerUIDevice *device);
+const gchar * gvc_mixer_ui_device_get_description (GvcMixerUIDevice *device);
+const gchar * gvc_mixer_ui_device_get_icon_name (GvcMixerUIDevice *device);
+GIcon * gvc_mixer_ui_device_get_gicon (GvcMixerUIDevice *device);
+const gchar * gvc_mixer_ui_device_get_origin (GvcMixerUIDevice *device);
+const gchar * gvc_mixer_ui_device_get_port (GvcMixerUIDevice *device);
+const gchar * gvc_mixer_ui_device_get_best_profile (GvcMixerUIDevice *device,
+ const gchar *selected,
+ const gchar *current);
+const gchar * gvc_mixer_ui_device_get_active_profile (GvcMixerUIDevice* device);
+const gchar * gvc_mixer_ui_device_get_matching_profile (GvcMixerUIDevice *device,
+ const gchar *profile);
+const gchar * gvc_mixer_ui_device_get_user_preferred_profile (GvcMixerUIDevice *device);
+const gchar * gvc_mixer_ui_device_get_top_priority_profile (GvcMixerUIDevice *device);
+GList * gvc_mixer_ui_device_get_profiles (GvcMixerUIDevice *device);
+GList * gvc_mixer_ui_device_get_supported_profiles (GvcMixerUIDevice *device);
+gboolean gvc_mixer_ui_device_should_profiles_be_hidden (GvcMixerUIDevice *device);
+void gvc_mixer_ui_device_set_profiles (GvcMixerUIDevice *device,
+ const GList *in_profiles);
+void gvc_mixer_ui_device_set_user_preferred_profile (GvcMixerUIDevice *device,
+ const gchar *profile);
+void gvc_mixer_ui_device_invalidate_stream (GvcMixerUIDevice *device);
+gboolean gvc_mixer_ui_device_has_ports (GvcMixerUIDevice *device);
+gboolean gvc_mixer_ui_device_is_output (GvcMixerUIDevice *device);
+
+G_END_DECLS
+
+#endif /* _GVC_MIXER_UI_DEVICE_H_ */
diff --git a/subprojects/gvc/gvc-pulseaudio-fake.h b/subprojects/gvc/gvc-pulseaudio-fake.h
new file mode 100644
index 0000000..92a41b6
--- /dev/null
+++ b/subprojects/gvc/gvc-pulseaudio-fake.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_PULSEAUDIO_FAKE_H
+#define __GVC_PULSEAUDIO_FAKE_H
+
+#ifndef PA_API_VERSION
+#define pa_channel_position_t int
+#define pa_volume_t guint32
+#define pa_context gpointer
+#endif /* PA_API_VERSION */
+
+#endif /* __GVC_PULSEAUDIO_FAKE_H */
diff --git a/subprojects/gvc/libgnome-volume-control.doap b/subprojects/gvc/libgnome-volume-control.doap
new file mode 100644
index 0000000..2fcc8e1
--- /dev/null
+++ b/subprojects/gvc/libgnome-volume-control.doap
@@ -0,0 +1,32 @@
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:gnome="http://api.gnome.org/doap-extensions#"
+ xmlns="http://usefulinc.com/ns/doap#">
+
+ <name xml:lang="en">libgnome-volume-control</name>
+ <shortdesc xml:lang="en">GObject layer for PulseAudio</shortdesc>
+ <description>
+ This library contains code to access PulseAudio using a GObject
+ based library, shared between gnome-control-center, gnome-settings-daemon
+ and gnome-shell. It is not API stable, and it is meant to be used
+ as a submodule.
+ </description>
+
+ <!-- <category rdf:resource="http://api.gnome.org/doap-extensions#desktop" /> -->
+
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Giovanni Campagna</foaf:name>
+ <foaf:mbox rdf:resource="mailto:scampa.giovanni@gmail.com" />
+ <gnome:userid>gcampagna</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Bastien Nocera</foaf:name>
+ <foaf:mbox rdf:resource="mailto:hadess@hadess.net" />
+ <gnome:userid>hadess</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+</Project>
diff --git a/subprojects/gvc/meson.build b/subprojects/gvc/meson.build
new file mode 100644
index 0000000..a1a2af5
--- /dev/null
+++ b/subprojects/gvc/meson.build
@@ -0,0 +1,137 @@
+project('gvc', 'c',
+ meson_version: '>= 0.42.0',
+ default_options: ['static=true']
+)
+
+assert(meson.is_subproject(), 'This project is only intended to be used as a subproject!')
+
+gnome = import('gnome')
+
+pkglibdir = get_option('pkglibdir')
+pkgdatadir = get_option('pkgdatadir')
+
+cdata = configuration_data()
+cdata.set_quoted('GETTEXT_PACKAGE', get_option('package_name'))
+cdata.set_quoted('PACKAGE_VERSION', get_option('package_version'))
+
+libgvc_gir_headers = [
+ 'gvc-channel-map.h',
+ 'gvc-mixer-card.h',
+ 'gvc-mixer-control.h',
+ 'gvc-mixer-event-role.h',
+ 'gvc-mixer-sink.h',
+ 'gvc-mixer-sink-input.h',
+ 'gvc-mixer-source.h',
+ 'gvc-mixer-source-output.h',
+ 'gvc-mixer-stream.h',
+ 'gvc-mixer-ui-device.h'
+]
+
+libgvc_enums = gnome.mkenums_simple('gvc-enum-types',
+ sources: libgvc_gir_headers
+)
+
+libgvc_gir_sources = [
+ 'gvc-channel-map.c',
+ 'gvc-mixer-card.c',
+ 'gvc-mixer-control.c',
+ 'gvc-mixer-event-role.c',
+ 'gvc-mixer-sink.c',
+ 'gvc-mixer-sink-input.c',
+ 'gvc-mixer-source.c',
+ 'gvc-mixer-source-output.c',
+ 'gvc-mixer-stream.c',
+ 'gvc-mixer-ui-device.c'
+]
+
+libgvc_no_gir_sources = [
+ 'gvc-mixer-card-private.h',
+ 'gvc-mixer-stream-private.h',
+ 'gvc-channel-map-private.h',
+ 'gvc-mixer-control-private.h',
+ 'gvc-pulseaudio-fake.h'
+]
+
+libgvc_deps = [
+ dependency('gio-2.0'),
+ dependency('gobject-2.0'),
+ dependency('libpulse', version: '>= 12.99.3'),
+ dependency('libpulse-mainloop-glib')
+]
+
+enable_alsa = get_option('alsa')
+if enable_alsa
+ libgvc_deps += dependency('alsa')
+endif
+cdata.set('HAVE_ALSA', enable_alsa)
+
+enable_static = get_option('static')
+enable_introspection = get_option('introspection')
+
+assert(not enable_static or not enable_introspection, 'Currently meson requires a shared library for building girs.')
+assert(enable_static or pkglibdir != '', 'Installing shared library, but pkglibdir is unset!')
+
+c_args = ['-DG_LOG_DOMAIN="Gvc"']
+
+if enable_introspection
+ c_args += '-DWITH_INTROSPECTION'
+endif
+
+if enable_static
+ libgvc_static = static_library('gvc',
+ sources: libgvc_gir_sources + libgvc_no_gir_sources + libgvc_enums,
+ dependencies: libgvc_deps,
+ c_args: c_args
+ )
+
+ libgvc = libgvc_static
+else
+ if pkglibdir == ''
+ error('Installing shared library, but pkglibdir is unset!')
+ endif
+
+ libgvc_shared = shared_library('gvc',
+ sources: libgvc_gir_sources + libgvc_no_gir_sources + libgvc_enums,
+ dependencies: libgvc_deps,
+ c_args: c_args,
+ install_dir: pkglibdir,
+ install: true
+ )
+
+ libgvc = libgvc_shared
+endif
+
+if enable_introspection
+ assert(pkgdatadir != '', 'Installing introspection, but pkgdatadir is unset!')
+
+ libgvc_gir = gnome.generate_gir(libgvc,
+ sources: libgvc_gir_sources + libgvc_gir_headers + libgvc_enums,
+ nsversion: '1.0',
+ namespace: 'Gvc',
+ includes: ['Gio-2.0', 'GObject-2.0'],
+ extra_args: ['-DWITH_INTROSPECTION', '--quiet'],
+ install_dir_gir: pkgdatadir,
+ install_dir_typelib: pkglibdir,
+ install: true
+ )
+endif
+
+if enable_alsa
+ executable('test-audio-device-selection',
+ sources: 'test-audio-device-selection.c',
+ link_with: libgvc,
+ dependencies: libgvc_deps,
+ c_args: c_args
+ )
+endif
+
+libgvc_dep = declare_dependency(
+ link_with: libgvc,
+ include_directories: include_directories('.'),
+ dependencies: libgvc_deps
+)
+
+configure_file(
+ output: 'config.h',
+ configuration: cdata
+)
diff --git a/subprojects/gvc/meson_options.txt b/subprojects/gvc/meson_options.txt
new file mode 100644
index 0000000..38513e3
--- /dev/null
+++ b/subprojects/gvc/meson_options.txt
@@ -0,0 +1,41 @@
+option('package_name',
+ type: 'string',
+ value: '',
+ description: 'The value for the GETTEXT_PACKAGE define.'
+)
+
+option('package_version',
+ type: 'string',
+ value: '',
+ description: 'The value for the PACKAGE_VERSION define.'
+)
+
+option('pkglibdir',
+ type: 'string',
+ value: '',
+ description: 'The private directory the shared library/typelib will be installed into.'
+)
+
+option('pkgdatadir',
+ type: 'string',
+ value: '',
+ description: 'The private directory the gir file will be installed into.'
+)
+
+option('alsa',
+ type: 'boolean',
+ value: true,
+ description: 'Build ALSA support.'
+)
+
+option('static',
+ type: 'boolean',
+ value: false,
+ description: 'Build as a static library.'
+)
+
+option('introspection',
+ type: 'boolean',
+ value: false,
+ description: 'Build gobject-introspection support'
+)
diff --git a/subprojects/gvc/test-audio-device-selection.c b/subprojects/gvc/test-audio-device-selection.c
new file mode 100644
index 0000000..8195f9d
--- /dev/null
+++ b/subprojects/gvc/test-audio-device-selection.c
@@ -0,0 +1,84 @@
+
+#include <stdio.h>
+#include <locale.h>
+#include <pulse/pulseaudio.h>
+#include "gvc-mixer-control.h"
+
+#define MAX_ATTEMPTS 3
+
+typedef struct {
+ GvcHeadsetPortChoice choice;
+ const char *name;
+} AudioSelectionChoice;
+
+static AudioSelectionChoice audio_selection_choices[] = {
+ { GVC_HEADSET_PORT_CHOICE_HEADPHONES, "headphones" },
+ { GVC_HEADSET_PORT_CHOICE_HEADSET, "headset" },
+ { GVC_HEADSET_PORT_CHOICE_MIC, "microphone" },
+};
+
+static void
+audio_selection_needed (GvcMixerControl *volume,
+ guint id,
+ gboolean show_dialog,
+ GvcHeadsetPortChoice choices,
+ gpointer user_data)
+{
+ const char *args[G_N_ELEMENTS (audio_selection_choices) + 1];
+ guint i, n;
+ int response = -1;
+
+ if (!show_dialog) {
+ g_print ("--- Audio selection not needed anymore for id %d\n", id);
+ return;
+ }
+
+ n = 0;
+ for (i = 0; i < G_N_ELEMENTS (audio_selection_choices); ++i) {
+ if (choices & audio_selection_choices[i].choice)
+ args[n++] = audio_selection_choices[i].name;
+ }
+ args[n] = NULL;
+
+ g_print ("+++ Audio selection needed for id %d\n", id);
+ g_print (" Choices are:\n");
+ for (i = 0; args[i] != NULL; i++)
+ g_print (" %d. %s\n", i + 1, args[i]);
+
+ for (i = 0; response < 0 && i < MAX_ATTEMPTS; i++) {
+ int res;
+
+ g_print ("What is your choice?\n");
+ if (scanf ("%d", &res) == 1 &&
+ res > 0 &&
+ res < (int) g_strv_length ((char **) args)) {
+ response = res;
+ break;
+ }
+ }
+
+ gvc_mixer_control_set_headset_port (volume,
+ id,
+ audio_selection_choices[response - 1].choice);
+}
+
+int main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ GvcMixerControl *volume;
+
+ setlocale (LC_ALL, "");
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ volume = gvc_mixer_control_new ("GNOME Volume Control test");
+ g_signal_connect (volume,
+ "audio-device-selection-needed",
+ G_CALLBACK (audio_selection_needed),
+ NULL);
+ gvc_mixer_control_open (volume);
+
+ g_main_loop_run (loop);
+
+ return 0;
+}
diff --git a/subprojects/shew/COPYING b/subprojects/shew/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/subprojects/shew/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/subprojects/shew/README.md b/subprojects/shew/README.md
new file mode 100644
index 0000000..de15991
--- /dev/null
+++ b/subprojects/shew/README.md
@@ -0,0 +1,24 @@
+# Shell External Windows
+
+Shew is a small support library for dealing with external windows.
+
+The code for creating external windows from a handle that are suitable for
+setting a transient parent was copied from [xdg-desktop-portal-gtk].
+
+The code for exporting a handle for a GtkWindow is losely based on code
+from GTK/[libportal].
+
+## Stability
+This is an unstable library with no API or ABI guarantees, and no
+soname versioning.
+
+It is not recommended to use it outside the gnome-shell project, and
+may only be used as a subproject.
+
+## License
+shew is distributed under the terms of the GNU Lesser General Public License
+version 2.1 or later. See the [COPYING][license] file for details.
+
+[xdg-desktop-portal-gtk]: https://github.com/flatpak/xdg-desktop-portal-gtk
+[libportal]: https://github.com/flatpak/libportal
+[license]: COPYING
diff --git a/subprojects/shew/meson.build b/subprojects/shew/meson.build
new file mode 100644
index 0000000..58a2499
--- /dev/null
+++ b/subprojects/shew/meson.build
@@ -0,0 +1,28 @@
+project('shew', 'c',
+ version: '43.9',
+ meson_version: '>= 0.58.0',
+ license: 'LGPLv2+',
+)
+
+assert(meson.is_subproject(), 'This project is only intended to be used as a subproject!')
+
+gnome = import('gnome')
+pkg = import('pkgconfig')
+
+api_version = '0'
+full_name = '@0@-@1@'.format(meson.project_name(), api_version)
+
+package_version = meson.project_version()
+package_name = get_option('package_name')
+assert(package_name != '', 'package_name must be specified')
+
+pkgdatadir = join_paths(get_option('datadir'), package_name)
+pkglibdir = join_paths(get_option('libdir'), package_name)
+
+girdir = join_paths(pkgdatadir, 'gir-1.0')
+typelibdir = join_paths(pkglibdir, 'girepository-1.0')
+
+gtk_dep = dependency('gtk4')
+x11_dep = dependency('x11', required: false)
+
+subdir('src')
diff --git a/subprojects/shew/meson_options.txt b/subprojects/shew/meson_options.txt
new file mode 100644
index 0000000..b5c0f5c
--- /dev/null
+++ b/subprojects/shew/meson_options.txt
@@ -0,0 +1,4 @@
+option('package_name',
+ type: 'string',
+ description: 'Parent package the library is built into'
+)
diff --git a/subprojects/shew/src/meson.build b/subprojects/shew/src/meson.build
new file mode 100644
index 0000000..e590a46
--- /dev/null
+++ b/subprojects/shew/src/meson.build
@@ -0,0 +1,29 @@
+shew_public_headers = files(
+ 'shew-external-window.h',
+ 'shew-window-exporter.h',
+)
+
+shew_sources = [
+ 'shew-external-window-wayland.c',
+ 'shew-external-window-x11.c',
+ 'shew-external-window.c',
+ 'shew-window-exporter.c',
+]
+
+libshew = library(full_name,
+ sources: shew_sources,
+ dependencies: [gtk_dep, x11_dep],
+ install_dir: pkglibdir,
+ install: true,
+)
+
+libshew_gir = gnome.generate_gir(libshew,
+ sources: shew_sources + shew_public_headers,
+ nsversion: api_version,
+ namespace: 'Shew',
+ includes: ['Gdk-4.0', 'Gtk-4.0'],
+ extra_args: ['--quiet'],
+ install_dir_gir: girdir,
+ install_dir_typelib: typelibdir,
+ install: true,
+)
diff --git a/subprojects/shew/src/shew-external-window-wayland.c b/subprojects/shew/src/shew-external-window-wayland.c
new file mode 100644
index 0000000..3d51aa8
--- /dev/null
+++ b/subprojects/shew/src/shew-external-window-wayland.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jonas Ådahl <jadahl@redhat.com>
+ */
+
+#include <gdk/gdk.h>
+
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/wayland/gdkwayland.h>
+#endif
+
+#include "shew-external-window-wayland.h"
+
+static GdkDisplay *wayland_display;
+
+struct _ShewExternalWindowWayland
+{
+ ShewExternalWindow parent;
+
+ char *handle_str;
+};
+
+G_DEFINE_TYPE (ShewExternalWindowWayland, shew_external_window_wayland,
+ SHEW_TYPE_EXTERNAL_WINDOW)
+
+static GdkDisplay *
+get_wayland_display (void)
+{
+ if (wayland_display)
+ return wayland_display;
+
+ gdk_set_allowed_backends ("wayland");
+ wayland_display = gdk_display_open (NULL);
+ gdk_set_allowed_backends (NULL);
+
+ if (!wayland_display)
+ g_warning ("Failed to open Wayland display");
+
+ return wayland_display;
+}
+
+ShewExternalWindowWayland *
+shew_external_window_wayland_new (const char *handle_str)
+{
+ ShewExternalWindowWayland *external_window_wayland;
+ GdkDisplay *display;
+
+ display = get_wayland_display ();
+ if (!display)
+ {
+ g_warning ("No Wayland display connection, ignoring Wayland parent");
+ return NULL;
+ }
+
+ external_window_wayland = g_object_new (SHEW_TYPE_EXTERNAL_WINDOW_WAYLAND,
+ "display", display,
+ NULL);
+ external_window_wayland->handle_str = g_strdup (handle_str);
+
+ return external_window_wayland;
+}
+
+static void
+shew_external_window_wayland_set_parent_of (ShewExternalWindow *external_window,
+ GdkSurface *child_surface)
+{
+ ShewExternalWindowWayland *external_window_wayland =
+ SHEW_EXTERNAL_WINDOW_WAYLAND (external_window);
+ char *handle_str = external_window_wayland->handle_str;
+
+#ifdef GDK_WINDOWING_WAYLAND
+ if (!gdk_wayland_toplevel_set_transient_for_exported (GDK_WAYLAND_TOPLEVEL (child_surface), handle_str))
+ g_warning ("Failed to set portal window transient for external parent");
+#endif
+}
+
+static void
+shew_external_window_wayland_dispose (GObject *object)
+{
+ ShewExternalWindowWayland *external_window_wayland =
+ SHEW_EXTERNAL_WINDOW_WAYLAND (object);
+
+ g_free (external_window_wayland->handle_str);
+
+ G_OBJECT_CLASS (shew_external_window_wayland_parent_class)->dispose (object);
+}
+
+static void
+shew_external_window_wayland_init (ShewExternalWindowWayland *external_window_wayland)
+{
+}
+
+static void
+shew_external_window_wayland_class_init (ShewExternalWindowWaylandClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ShewExternalWindowClass *external_window_class = SHEW_EXTERNAL_WINDOW_CLASS (klass);
+
+ object_class->dispose = shew_external_window_wayland_dispose;
+
+ external_window_class->set_parent_of = shew_external_window_wayland_set_parent_of;
+}
diff --git a/subprojects/shew/src/shew-external-window-wayland.h b/subprojects/shew/src/shew-external-window-wayland.h
new file mode 100644
index 0000000..c9e9fd7
--- /dev/null
+++ b/subprojects/shew/src/shew-external-window-wayland.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jonas Ådahl <jadahl@redhat.com>
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#include "shew-external-window.h"
+
+#define SHEW_TYPE_EXTERNAL_WINDOW_WAYLAND (shew_external_window_wayland_get_type ())
+G_DECLARE_FINAL_TYPE (ShewExternalWindowWayland, shew_external_window_wayland, SHEW, EXTERNAL_WINDOW_WAYLAND, ShewExternalWindow)
+
+ShewExternalWindowWayland *shew_external_window_wayland_new (const char *handle_str);
diff --git a/subprojects/shew/src/shew-external-window-x11.c b/subprojects/shew/src/shew-external-window-x11.c
new file mode 100644
index 0000000..7f08665
--- /dev/null
+++ b/subprojects/shew/src/shew-external-window-x11.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jonas Ådahl <jadahl@redhat.com>
+ */
+
+#include <errno.h>
+#include <gdk/gdk.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/x11/gdkx.h>
+#include <X11/Xlib.h>
+#endif
+#include <stdlib.h>
+
+#include "shew-external-window-x11.h"
+
+static GdkDisplay *x11_display;
+
+struct _ShewExternalWindowX11
+{
+ ShewExternalWindow parent;
+
+ int foreign_xid;
+};
+
+G_DEFINE_TYPE (ShewExternalWindowX11, shew_external_window_x11,
+ SHEW_TYPE_EXTERNAL_WINDOW)
+
+static GdkDisplay *
+get_x11_display (void)
+{
+ if (x11_display)
+ return x11_display;
+
+ gdk_set_allowed_backends ("x11");
+ x11_display = gdk_display_open (NULL);
+ gdk_set_allowed_backends (NULL);
+ if (!x11_display)
+ g_warning ("Failed to open X11 display");
+
+ return x11_display;
+}
+
+static gboolean
+check_foreign_xid (GdkDisplay *display,
+ int xid)
+{
+ gboolean result = FALSE;
+#ifdef GDK_WINDOWING_X11
+ XWindowAttributes attrs;
+
+ gdk_x11_display_error_trap_push (display);
+ result = XGetWindowAttributes (GDK_DISPLAY_XDISPLAY (display), xid, &attrs);
+ if (gdk_x11_display_error_trap_pop (display))
+ return FALSE;
+
+#endif
+ return result;
+}
+
+ShewExternalWindowX11 *
+shew_external_window_x11_new (const char *handle_str)
+{
+ ShewExternalWindowX11 *external_window_x11;
+ GdkDisplay *display;
+ int xid;
+
+ display = get_x11_display ();
+ if (!display)
+ {
+ g_warning ("No X display connection, ignoring X11 parent");
+ return NULL;
+ }
+
+ errno = 0;
+ xid = strtol (handle_str, NULL, 16);
+ if (errno != 0)
+ {
+ g_warning ("Failed to reference external X11 window, invalid XID %s", handle_str);
+ return NULL;
+ }
+
+ if (!check_foreign_xid (display, xid))
+ {
+ g_warning ("Failed to find foreign window for XID %d", xid);
+ return NULL;
+ }
+
+ external_window_x11 = g_object_new (SHEW_TYPE_EXTERNAL_WINDOW_X11,
+ "display", display,
+ NULL);
+ external_window_x11->foreign_xid = xid;
+
+ return external_window_x11;
+}
+
+static void
+shew_external_window_x11_set_parent_of (ShewExternalWindow *external_window,
+ GdkSurface *child_surface)
+{
+ ShewExternalWindowX11 *external_window_x11 =
+ SHEW_EXTERNAL_WINDOW_X11 (external_window);
+
+#ifdef GDK_WINDOWING_X11
+ XSetTransientForHint (GDK_SURFACE_XDISPLAY (child_surface),
+ GDK_SURFACE_XID (child_surface),
+ external_window_x11->foreign_xid);
+#endif
+}
+
+static void
+shew_external_window_x11_init (ShewExternalWindowX11 *external_window_x11)
+{
+}
+
+static void
+shew_external_window_x11_class_init (ShewExternalWindowX11Class *klass)
+{
+ ShewExternalWindowClass *external_window_class = SHEW_EXTERNAL_WINDOW_CLASS (klass);
+
+ external_window_class->set_parent_of = shew_external_window_x11_set_parent_of;
+}
diff --git a/subprojects/shew/src/shew-external-window-x11.h b/subprojects/shew/src/shew-external-window-x11.h
new file mode 100644
index 0000000..ed0bab4
--- /dev/null
+++ b/subprojects/shew/src/shew-external-window-x11.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jonas Ådahl <jadahl@redhat.com>
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#include "shew-external-window.h"
+
+#define SHEW_TYPE_EXTERNAL_WINDOW_X11 (shew_external_window_x11_get_type ())
+G_DECLARE_FINAL_TYPE (ShewExternalWindowX11, shew_external_window_x11, SHEW, EXTERNAL_WINDOW_X11, ShewExternalWindow)
+
+ShewExternalWindowX11 *shew_external_window_x11_new (const char *handle_str);
diff --git a/subprojects/shew/src/shew-external-window.c b/subprojects/shew/src/shew-external-window.c
new file mode 100644
index 0000000..118d93f
--- /dev/null
+++ b/subprojects/shew/src/shew-external-window.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jonas Ådahl <jadahl@redhat.com>
+ */
+
+#include <string.h>
+
+#include "shew-external-window.h"
+#include "shew-external-window-x11.h"
+#include "shew-external-window-wayland.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_DISPLAY,
+};
+
+typedef struct _ShewExternalWindowPrivate
+{
+ GdkDisplay *display;
+} ShewExternalWindowPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShewExternalWindow, shew_external_window, G_TYPE_OBJECT)
+
+ShewExternalWindow *
+shew_external_window_new_from_handle (const char *handle_str)
+{
+#ifdef GDK_WINDOWING_X11
+ {
+ const char x11_prefix[] = "x11:";
+ if (g_str_has_prefix (handle_str, x11_prefix))
+ {
+ ShewExternalWindowX11 *external_window_x11;
+ const char *x11_handle_str = handle_str + strlen (x11_prefix);
+
+ external_window_x11 = shew_external_window_x11_new (x11_handle_str);
+ return SHEW_EXTERNAL_WINDOW (external_window_x11);
+ }
+ }
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+ {
+ const char wayland_prefix[] = "wayland:";
+ if (g_str_has_prefix (handle_str, wayland_prefix))
+ {
+ ShewExternalWindowWayland *external_window_wayland;
+ const char *wayland_handle_str = handle_str + strlen (wayland_prefix);
+
+ external_window_wayland =
+ shew_external_window_wayland_new (wayland_handle_str);
+ return SHEW_EXTERNAL_WINDOW (external_window_wayland);
+ }
+ }
+#endif
+
+ g_warning ("Unhandled parent window type %s\n", handle_str);
+ return NULL;
+}
+
+void
+shew_external_window_set_parent_of (ShewExternalWindow *external_window,
+ GdkSurface *child_surface)
+{
+ SHEW_EXTERNAL_WINDOW_GET_CLASS (external_window)->set_parent_of (external_window,
+ child_surface);
+}
+
+/**
+ * shew_external_window_get_display:
+ * Returns: (transfer none)
+ */
+GdkDisplay *
+shew_external_window_get_display (ShewExternalWindow *external_window)
+{
+ ShewExternalWindowPrivate *priv =
+ shew_external_window_get_instance_private (external_window);
+
+ return priv->display;
+}
+
+static void
+shew_external_window_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShewExternalWindow *external_window = SHEW_EXTERNAL_WINDOW (object);
+ ShewExternalWindowPrivate *priv =
+ shew_external_window_get_instance_private (external_window);
+
+ switch (prop_id)
+ {
+ case PROP_DISPLAY:
+ g_set_object (&priv->display, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+shew_external_window_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShewExternalWindow *external_window = SHEW_EXTERNAL_WINDOW (object);
+ ShewExternalWindowPrivate *priv =
+ shew_external_window_get_instance_private (external_window);
+
+ switch (prop_id)
+ {
+ case PROP_DISPLAY:
+ g_value_set_object (value, priv->display);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+shew_external_window_init (ShewExternalWindow *external_window)
+{
+}
+
+static void
+shew_external_window_class_init (ShewExternalWindowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = shew_external_window_get_property;
+ object_class->set_property = shew_external_window_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY,
+ g_param_spec_object ("display",
+ "GdkDisplay",
+ "The GdkDisplay instance",
+ GDK_TYPE_DISPLAY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/subprojects/shew/src/shew-external-window.h b/subprojects/shew/src/shew-external-window.h
new file mode 100644
index 0000000..ca6671b
--- /dev/null
+++ b/subprojects/shew/src/shew-external-window.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jonas Ådahl <jadahl@redhat.com>
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+
+#define SHEW_TYPE_EXTERNAL_WINDOW (shew_external_window_get_type ())
+G_DECLARE_DERIVABLE_TYPE (ShewExternalWindow, shew_external_window, SHEW, EXTERNAL_WINDOW, GObject)
+
+struct _ShewExternalWindowClass
+{
+ GObjectClass parent_class;
+
+ void (*set_parent_of) (ShewExternalWindow *external_window,
+ GdkSurface *child_surface);
+};
+
+ShewExternalWindow *shew_external_window_new_from_handle (const char *handle_str);
+
+void shew_external_window_set_parent_of (ShewExternalWindow *external_window,
+ GdkSurface *child_surface);
+
+GdkDisplay *shew_external_window_get_display (ShewExternalWindow *external_window);
diff --git a/subprojects/shew/src/shew-window-exporter.c b/subprojects/shew/src/shew-window-exporter.c
new file mode 100644
index 0000000..ab84bf8
--- /dev/null
+++ b/subprojects/shew/src/shew-window-exporter.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright © 2020 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Florian Müllner <fmuellner@gnome.org>
+ */
+
+#include "shew-window-exporter.h"
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/x11/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/wayland/gdkwayland.h>
+#endif
+
+struct _ShewWindowExporter
+{
+ GObject parent;
+
+ GtkWindow *window;
+};
+
+G_DEFINE_TYPE (ShewWindowExporter, shew_window_exporter, G_TYPE_OBJECT)
+
+enum
+{
+ PROP_0,
+
+ PROP_WINDOW,
+};
+
+ShewWindowExporter *
+shew_window_exporter_new (GtkWindow *window)
+{
+ return g_object_new (SHEW_TYPE_WINDOW_EXPORTER,
+ "window", window,
+ NULL);
+}
+
+#ifdef GDK_WINDOWING_WAYLAND
+static void
+wayland_window_exported (GdkToplevel *toplevel,
+ const char *handle,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = user_data;
+
+ g_task_return_pointer (task, g_strdup_printf ("wayland:%s", handle), g_free);
+}
+#endif
+
+void
+shew_window_exporter_export (ShewWindowExporter *exporter,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = NULL;
+ GtkWidget *widget;
+
+ g_return_if_fail (SHEW_IS_WINDOW_EXPORTER (exporter));
+
+ if (exporter->window == NULL)
+ {
+ g_task_report_new_error (exporter, callback, user_data,
+ shew_window_exporter_export,
+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "No window to export");
+ return;
+ }
+
+ task = g_task_new (exporter, NULL, callback, user_data);
+ g_task_set_source_tag (task, shew_window_exporter_export);
+
+ widget = GTK_WIDGET (exporter->window);
+
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (widget)))
+ {
+ GdkSurface *s = gtk_native_get_surface (GTK_NATIVE (widget));
+ guint32 xid = (guint32) gdk_x11_surface_get_xid (s);
+
+ g_task_return_pointer (task, g_strdup_printf ("x11:%x", xid), g_free);
+ }
+#endif
+
+#ifdef GDK_WINDOWING_WAYLAND
+ if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget)))
+ {
+ GdkSurface *s = gtk_native_get_surface (GTK_NATIVE (widget));
+ gdk_wayland_toplevel_export_handle (GDK_WAYLAND_TOPLEVEL (s),
+ wayland_window_exported,
+ g_steal_pointer (&task), NULL);
+ }
+#endif
+
+ if (task != NULL && !g_task_get_completed (task))
+ {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Unsupported windowing system");
+ }
+}
+
+char *
+shew_window_exporter_export_finish (ShewWindowExporter *exporter,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SHEW_IS_WINDOW_EXPORTER (exporter), NULL);
+ g_return_val_if_fail (g_async_result_is_tagged (result, shew_window_exporter_export), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+void
+shew_window_exporter_unexport (ShewWindowExporter *exporter)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (SHEW_IS_WINDOW_EXPORTER (exporter));
+
+ widget = GTK_WIDGET (exporter->window);
+
+#ifdef GDK_WINDOWING_WAYLAND
+ if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget)))
+ {
+ GdkSurface *s = gtk_native_get_surface (GTK_NATIVE (widget));
+ gdk_wayland_toplevel_unexport_handle (GDK_WAYLAND_TOPLEVEL (s));
+ }
+#endif
+}
+
+static void
+shew_window_exporter_dispose (GObject *object)
+{
+ ShewWindowExporter *exporter = SHEW_WINDOW_EXPORTER (object);
+
+ g_clear_object (&exporter->window);
+
+ G_OBJECT_CLASS (shew_window_exporter_parent_class)->dispose (object);
+}
+
+static void
+shew_window_exporter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ShewWindowExporter *exporter = SHEW_WINDOW_EXPORTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ g_set_object (&exporter->window, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+shew_window_exporter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ShewWindowExporter *exporter = SHEW_WINDOW_EXPORTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ g_value_set_object (value, exporter->window);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+shew_window_exporter_init (ShewWindowExporter *exporter)
+{
+}
+
+static void
+shew_window_exporter_class_init (ShewWindowExporterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = shew_window_exporter_get_property;
+ object_class->set_property = shew_window_exporter_set_property;
+ object_class->dispose = shew_window_exporter_dispose;
+
+ g_object_class_install_property (object_class,
+ PROP_WINDOW,
+ g_param_spec_object ("window",
+ "GtkWindow",
+ "The GtkWindow to export",
+ GTK_TYPE_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/subprojects/shew/src/shew-window-exporter.h b/subprojects/shew/src/shew-window-exporter.h
new file mode 100644
index 0000000..224fff5
--- /dev/null
+++ b/subprojects/shew/src/shew-window-exporter.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2020 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Florian Müllner <fmuellner@gnome.org>
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define SHEW_TYPE_WINDOW_EXPORTER (shew_window_exporter_get_type ())
+G_DECLARE_FINAL_TYPE (ShewWindowExporter, shew_window_exporter, SHEW, WINDOW_EXPORTER, GObject)
+
+ShewWindowExporter *shew_window_exporter_new (GtkWindow *window);
+
+void shew_window_exporter_export (ShewWindowExporter *exporter,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+char *shew_window_exporter_export_finish (ShewWindowExporter *exporter,
+ GAsyncResult *result,
+ GError **error);
+
+void shew_window_exporter_unexport (ShewWindowExporter *exporter);
diff --git a/tests/interactive/background-repeat.js b/tests/interactive/background-repeat.js
new file mode 100644
index 0000000..1377f74
--- /dev/null
+++ b/tests/interactive/background-repeat.js
@@ -0,0 +1,28 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage({ width: 640, height: 480 });
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ width: stage.width,
+ height: stage.height,
+ style: 'background: #ffee88;' });
+ stage.add_actor(vbox);
+
+ let scroll = new St.ScrollView();
+ vbox.add(scroll, { expand: true });
+
+ let box = new St.BoxLayout({ vertical: true });
+ scroll.add_actor(box);
+
+ let contents = new St.Widget({ width: 1000, height: 1000,
+ style_class: 'background-image background-repeat' });
+ box.add_actor(contents);
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/background-size.js b/tests/interactive/background-size.js
new file mode 100644
index 0000000..8f8738d
--- /dev/null
+++ b/tests/interactive/background-size.js
@@ -0,0 +1,82 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Cogl, Clutter, Meta, St } = imports.gi;
+
+
+function test() {
+ Meta.init();
+
+ let stage = Meta.get_backend().get_stage();
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ style: 'background: #ffee88;' });
+ vbox.add_constraint(new Clutter.BindConstraint({ source: stage,
+ coordinate: Clutter.BindCoordinate.SIZE }));
+ stage.add_actor(vbox);
+
+ let scroll = new St.ScrollView();
+ vbox.add(scroll, { expand: true });
+
+ vbox = new St.BoxLayout({ vertical: true,
+ style: 'padding: 10px;'
+ + 'spacing: 20px;' });
+ scroll.add_actor(vbox);
+
+ let tbox = null;
+
+ function addTestCase(image, size, backgroundSize, useCairo) {
+ let obin = new St.Bin({ style: 'border: 3px solid green;' });
+ tbox.add(obin);
+
+ let [width, height] = size;
+ let bin = new St.Bin({ style_class: 'background-image-' + image,
+ width: width,
+ height: height,
+ style: `${useCairo
+ ? 'border: 1px solid transparent;' : ''
+ } background-size: ${backgroundSize};`,
+ x_fill: true,
+ y_fill: true
+ });
+ obin.set_child(bin);
+
+ bin.set_child(new St.Label({ text: backgroundSize + (useCairo ? ' (cairo)' : ' (cogl)'),
+ style: 'font-size: 15px;'
+ + 'text-align: center;'
+ }));
+ }
+
+ function addTestLine(image, size) {
+ const backgroundSizes = ["auto", "contain", "cover", "200px 200px", "100px 100px", "100px 200px"];
+
+ let [width, height] = size;
+ vbox.add(new St.Label({ text: image + '.svg / ' + width + '×' + height,
+ style: 'font-size: 15px;'
+ + 'text-align: center;'
+ }));
+
+ tbox = new St.BoxLayout({ style: 'spacing: 20px;' });
+ vbox.add(tbox);
+
+ for (let s of backgroundSizes)
+ addTestCase(image, size, s, false);
+ for (let s of backgroundSizes)
+ addTestCase(image, size, s, true);
+ }
+
+ function addTestImage(image) {
+ const containerSizes = [[100, 100], [200, 200], [250, 250], [100, 250], [250, 100]];
+
+ for (let size of containerSizes)
+ addTestLine(image, size);
+ }
+
+ addTestImage ('200-200');
+ addTestImage ('200-100');
+ addTestImage ('100-200');
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/border-radius.js b/tests/interactive/border-radius.js
new file mode 100644
index 0000000..4d26518
--- /dev/null
+++ b/tests/interactive/border-radius.js
@@ -0,0 +1,61 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage({ width: 640, height: 480 });
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ width: stage.width,
+ height: stage.height,
+ style: 'background: #ffee88;' });
+ stage.add_actor(vbox);
+
+ let scroll = new St.ScrollView();
+ vbox.add(scroll, { expand: true });
+
+ let box = new St.BoxLayout({ vertical: true,
+ style: 'padding: 10px;'
+ + 'spacing: 20px;' });
+ scroll.add_actor(box);
+
+ function addTestCase(radii, useGradient) {
+ let background;
+ if (useGradient)
+ background = 'background-gradient-direction: vertical;'
+ + 'background-gradient-start: white;'
+ + 'background-gradient-end: gray;';
+ else
+ background = 'background: white;';
+
+ box.add(new St.Label({ text: "border-radius: " + radii + ";",
+ style: 'border: 1px solid black; '
+ + 'border-radius: ' + radii + ';'
+ + 'padding: 5px;' + background }),
+ { x_fill: false });
+ }
+
+ // uniform backgrounds
+ addTestCase(" 0px 5px 10px 15px", false);
+ addTestCase(" 5px 10px 15px 0px", false);
+ addTestCase("10px 15px 0px 5px", false);
+ addTestCase("15px 0px 5px 10px", false);
+
+ // gradient backgrounds
+ addTestCase(" 0px 5px 10px 15px", true);
+ addTestCase(" 5px 10px 15px 0px", true);
+ addTestCase("10px 15px 0px 5px", true);
+ addTestCase("15px 0px 5px 10px", true);
+
+ // border-radius reduction
+ // these should all take the cairo fallback,
+ // so don't bother testing w/ or w/out gradients.
+ addTestCase("200px 200px 200px 200px", false);
+ addTestCase("200px 200px 0px 200px", false);
+ addTestCase("999px 0px 999px 0px", false);
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/border-width.js b/tests/interactive/border-width.js
new file mode 100644
index 0000000..30c7575
--- /dev/null
+++ b/tests/interactive/border-width.js
@@ -0,0 +1,58 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage({ width: 640, height: 480 });
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ width: stage.width,
+ height: stage.height,
+ style: 'padding: 10px; background: #ffee88;'
+ });
+ stage.add_actor(vbox);
+
+ let scroll = new St.ScrollView();
+ vbox.add(scroll, { expand: true });
+
+ let box = new St.BoxLayout({ vertical: true,
+ style: 'spacing: 20px;' });
+ scroll.add_actor(box);
+
+ function addTestCase(borders, useGradient) {
+ let background;
+ if (useGradient)
+ background = 'background-gradient-direction: vertical;'
+ + 'background-gradient-start: white;'
+ + 'background-gradient-end: gray;';
+ else
+ background = 'background: white;';
+
+ let border_style = "border-top: " + borders[St.Side.TOP] + " solid black;\n" +
+ "border-right: " + borders[St.Side.RIGHT] + " solid black;\n" +
+ "border-bottom: " + borders[St.Side.BOTTOM] + " solid black;\n" +
+ "border-left: " + borders[St.Side.LEFT] + " solid black;";
+ box.add(new St.Label({ text: border_style,
+ style: border_style
+ + 'border-radius: 0px 5px 15px 25px;'
+ + 'padding: 5px;' + background }),
+ { x_fill: false });
+ }
+
+ // uniform backgrounds
+ addTestCase([" 0px", " 5px", "10px", "15px"], false);
+ addTestCase([" 5px", "10px", "15px", " 0px"], false);
+ addTestCase(["10px", "15px", " 0px", " 5px"], false);
+ addTestCase(["15px", " 0px", " 5px", "10px"], false);
+
+ // gradient backgrounds
+ addTestCase([" 0px", " 5px", "10px", "15px"], true);
+ addTestCase([" 5px", "10px", "15px", " 0px"], true);
+ addTestCase(["10px", "15px", " 0px", " 5px"], true);
+ addTestCase(["15px", " 0px", " 5px", "10px"], true);
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/borders.js b/tests/interactive/borders.js
new file mode 100644
index 0000000..4812acb
--- /dev/null
+++ b/tests/interactive/borders.js
@@ -0,0 +1,133 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage({ width: 640, height: 480 });
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ width: stage.width,
+ height: stage.height,
+ style: 'background: #ffee88;' });
+ stage.add_actor(vbox);
+
+ let scroll = new St.ScrollView();
+ vbox.add(scroll, { expand: true });
+
+ let box = new St.BoxLayout({ vertical: true,
+ style: 'padding: 10px;'
+ + 'spacing: 20px;' });
+ scroll.add_actor(box);
+
+ box.add(new St.Label({ text: "Hello World",
+ style: 'border: 1px solid black; '
+ + 'padding: 5px;' }));
+
+ box.add(new St.Label({ text: "Hello Round World",
+ style: 'border: 3px solid green; '
+ + 'border-radius: 8px; '
+ + 'padding: 5px;' }));
+
+ box.add(new St.Label({ text: "Hello Background",
+ style: 'border: 3px solid green; '
+ + 'border-radius: 8px; '
+ + 'background: white; '
+ + 'padding: 5px;' }));
+
+ box.add(new St.Label({ text: "Hello Translucent Black Border",
+ style: 'border: 3px solid rgba(0, 0, 0, 0.4); '
+ + 'background: white; ' }));
+
+ box.add(new St.Label({ text: "Hello Translucent Background",
+ style: 'background: rgba(255, 255, 255, 0.3);' }));
+
+ box.add(new St.Label({ text: "Border, Padding, Content: 20px" }));
+
+ let b1 = new St.BoxLayout({ vertical: true,
+ style: 'border: 20px solid black; '
+ + 'background: white; '
+ + 'padding: 20px;' });
+ box.add(b1);
+
+ b1.add(new St.BoxLayout({ width: 20, height: 20,
+ style: 'background: black' }));
+
+ box.add(new St.Label({ text: "Translucent big blue border, with rounding",
+ style: 'border: 20px solid rgba(0, 0, 255, 0.2); '
+ + 'border-radius: 10px; '
+ + 'background: white; '
+ + 'padding: 10px;' }));
+
+ box.add(new St.Label({ text: "Transparent border",
+ style: 'border: 20px solid transparent; '
+ + 'background: white; '
+ + 'padding: 10px;' }));
+
+ box.add(new St.Label({ text: "Border Image",
+ style_class: "border-image",
+ style: "padding: 10px;" }));
+
+ box.add(new St.Label({ text: "Border Image with Gradient",
+ style_class: 'border-image-with-background-gradient',
+ style: "padding: 10px;"
+ + 'background-gradient-direction: vertical;' }));
+
+ box.add(new St.Label({ text: "Rounded, framed, shadowed gradients" }));
+
+ let framedGradients = new St.BoxLayout({ vertical: false,
+ style: 'padding: 10px; spacing: 12px;' });
+ box.add(framedGradients);
+
+ function addGradientCase(direction, borderWidth, borderRadius, extra) {
+ let gradientBox = new St.BoxLayout({ style_class: 'background-gradient',
+ style: 'border: ' + borderWidth + 'px solid #8b0000;'
+ + 'border-radius: ' + borderRadius + 'px;'
+ + 'background-gradient-direction: ' + direction + ';'
+ + 'width: 32px;'
+ + 'height: 32px;'
+ + extra });
+ framedGradients.add(gradientBox, { x_fill: false, y_fill: false } );
+ }
+
+ addGradientCase ('horizontal', 0, 5, 'box-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
+ addGradientCase ('horizontal', 2, 5, 'box-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
+ addGradientCase ('horizontal', 5, 2, 'box-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
+ addGradientCase ('horizontal', 5, 20, 'box-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
+ addGradientCase ('vertical', 0, 5, 'box-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
+ addGradientCase ('vertical', 2, 5, 'box-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
+ addGradientCase ('vertical', 5, 2, 'box-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
+ addGradientCase ('vertical', 5, 20, 'box-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
+
+ box.add(new St.Label({ text: "Rounded, framed, shadowed images" }));
+
+ let framedImages = new St.BoxLayout({ vertical: false,
+ style: 'padding: 10px; spacing: 6px;' });
+ box.add(framedImages);
+
+ function addBackgroundImageCase(borderWidth, borderRadius, width, height, extra) {
+ let imageBox = new St.BoxLayout({ style_class: 'background-image',
+ style: 'border: ' + borderWidth + 'px solid #8b8b8b;'
+ + 'border-radius: ' + borderRadius + 'px;'
+ + 'width: ' + width + 'px;'
+ + 'height: ' + height + 'px;'
+ + extra });
+ framedImages.add(imageBox, { x_fill: false, y_fill: false } );
+ }
+
+ addBackgroundImageCase (0, 0, 32, 32, 'background-position: 2px 5px');
+ addBackgroundImageCase (0, 0, 16, 16, '-st-background-image-shadow: 1px 1px 4px 0px rgba(0,0,0,0.5); background-color: rgba(0,0,0,0)');
+ addBackgroundImageCase (0, 5, 32, 32, '-st-background-image-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
+ addBackgroundImageCase (2, 5, 32, 32, '-st-background-image-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
+ addBackgroundImageCase (5, 2, 32, 32, '-st-background-image-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
+ addBackgroundImageCase (5, 20, 32, 32, '-st-background-image-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
+ addBackgroundImageCase (0, 5, 48, 48, '-st-background-image-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
+ addBackgroundImageCase (5, 5, 48, 48, '-st-background-image-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
+ addBackgroundImageCase (0, 5, 64, 64, '-st-background-image-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
+ addBackgroundImageCase (5, 5, 64, 64, '-st-background-image-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
+ addBackgroundImageCase (0, 5, 32, 32, 'background-position: 2px 5px');
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/box-layout.js b/tests/interactive/box-layout.js
new file mode 100644
index 0000000..bb9a5bb
--- /dev/null
+++ b/tests/interactive/box-layout.js
@@ -0,0 +1,85 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage();
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ vertical: true,
+ width: stage.width,
+ height: stage.height,
+ style: 'padding: 10px;'
+ + 'spacing: 10px;' });
+ stage.add_actor(vbox);
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ let colored_boxes = new St.BoxLayout({ vertical: true,
+ width: 200,
+ height: 200,
+ style: 'border: 2px solid black;' });
+ vbox.add(colored_boxes, { x_fill: false,
+ x_align: St.Align.MIDDLE });
+
+ let b2 = new St.BoxLayout({ style: 'border: 2px solid #666666' });
+ colored_boxes.add(b2, { expand: true });
+
+ b2.add(new St.Label({ text: "Expand",
+ style: 'border: 1px solid #aaaaaa; '
+ + 'background: #ffeecc' }),
+ { expand: true });
+ b2.add(new St.Label({ text: "Expand\nNo Fill",
+ style: 'border: 1px solid #aaaaaa; '
+ + 'background: #ccffaa' }),
+ { expand: true,
+ x_fill: false,
+ x_align: St.Align.MIDDLE,
+ y_fill: false,
+ y_align: St.Align.MIDDLE });
+
+ colored_boxes.add(new St.Label({ text: "Default",
+ style: 'border: 1px solid #aaaaaa; '
+ + 'background: #cceeff' }));
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ function createCollapsableBox(width) {
+ let b = new St.BoxLayout({ width: width,
+ style: 'border: 1px solid black;'
+ + 'font: 13px Sans;' });
+ b.add(new St.Label({ text: "Very Very Very Long",
+ style: 'background: #ffaacc;'
+ + 'padding: 5px; '
+ + 'border: 1px solid #666666;' }),
+ { expand: true });
+ b.add(new St.Label({ text: "Very Very Long",
+ style: 'background: #ffeecc; '
+ + 'padding: 5px; '
+ + 'border: 1px solid #666666;' }),
+ { expand: true });
+ b.add(new St.Label({ text: "Very Long",
+ style: 'background: #ccffaa; '
+ + 'padding: 5px; '
+ + 'border: 1px solid #666666;' }),
+ { expand: true });
+ b.add(new St.Label({ text: "Short",
+ style: 'background: #cceeff; '
+ + 'padding: 5px; '
+ + 'border: 1px solid #666666;' }),
+ { expand: true });
+
+ return b;
+ }
+
+ for (let width = 200; width <= 500; width += 60 ) {
+ vbox.add(createCollapsableBox (width),
+ { x_fill: false,
+ x_align: St.Align.MIDDLE });
+ }
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/box-shadow-animated.js b/tests/interactive/box-shadow-animated.js
new file mode 100644
index 0000000..cf117a7
--- /dev/null
+++ b/tests/interactive/box-shadow-animated.js
@@ -0,0 +1,80 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, GLib, St } = imports.gi;
+
+const DELAY = 2000;
+
+function resize_animated(label) {
+ if (label.width == 100) {
+ label.save_easing_state();
+ label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
+ label.set_easing_duration(DELAY - 50);
+ label.set_size(500, 500);
+ label.restore_easing_state();
+ } else {
+ label.save_easing_state();
+ label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
+ label.set_easing_duration(DELAY - 50);
+ label.set_size(100, 100);
+ label.restore_easing_state();
+ }
+}
+
+function get_css_style(shadow_style)
+{
+ return 'border: 20px solid black;' +
+ 'border-radius: 20px;' +
+ 'background-color: white; ' +
+ 'padding: 5px;' + shadow_style;
+}
+
+function test() {
+ let stage = new Clutter.Stage({ width: 1000, height: 600 });
+ UI.init(stage);
+
+ let iter = 0;
+ let shadowStyles = [ 'box-shadow: 3px 50px 0px 4px rgba(0,0,0,0.5);',
+ 'box-shadow: 3px 4px 10px 4px rgba(0,0,0,0.5);',
+ 'box-shadow: 0px 50px 0px 0px rgba(0,0,0,0.5);',
+ 'box-shadow: 100px 100px 20px 4px rgba(0,0,0,0.5);'];
+ let label1 = new St.Label({ style: get_css_style(shadowStyles[iter]),
+ text: shadowStyles[iter],
+ x: 20,
+ y: 20,
+ width: 100,
+ height: 100
+ });
+ stage.add_actor(label1);
+ let label2 = new St.Label({ style: get_css_style(shadowStyles[iter]),
+ text: shadowStyles[iter],
+ x: 500,
+ y: 20,
+ width: 100,
+ height: 100
+ });
+ stage.add_actor(label2);
+
+ resize_animated(label1);
+ resize_animated(label2);
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, DELAY, () => {
+ log(label1 + label1.get_size());
+ resize_animated(label1);
+ resize_animated(label2);
+ return true;
+ });
+
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, 2 * DELAY, () => {
+ iter += 1;
+ iter %= shadowStyles.length;
+ label1.set_style(get_css_style(shadowStyles[iter]));
+ label1.set_text(shadowStyles[iter]);
+ label2.set_style(get_css_style(shadowStyles[iter]));
+ label2.set_text(shadowStyles[iter]);
+ return true;
+ });
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/box-shadows.js b/tests/interactive/box-shadows.js
new file mode 100644
index 0000000..c9c677c
--- /dev/null
+++ b/tests/interactive/box-shadows.js
@@ -0,0 +1,56 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage({ width: 640, height: 480 });
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ width: stage.width,
+ height: stage.height,
+ style: 'background: #ffee88;' });
+ stage.add_actor(vbox);
+
+ let scroll = new St.ScrollView();
+ vbox.add(scroll, { expand: true });
+
+ let box = new St.BoxLayout({ vertical: true,
+ style: 'padding: 10px;'
+ + 'spacing: 20px;' });
+ scroll.add_actor(box);
+
+
+ function addTestCase(inset, offsetX, offsetY, blur, spread) {
+ let shadowStyle = 'box-shadow: ' + (inset ? 'inset ' : '') +
+ offsetX + 'px ' + offsetY + 'px ' + blur + 'px ' +
+ (spread > 0 ? (' ' + spread + 'px ') : '') +
+ 'rgba(0,0,0,0.5);';
+ let label = new St.Label({ style: 'border: 4px solid black;' +
+ 'border-radius: 5px;' +
+ 'background-color: white; ' +
+ 'padding: 5px;' +
+ shadowStyle,
+ text: shadowStyle });
+ box.add(label, { x_fill: false, y_fill: false } );
+ }
+
+ addTestCase (false, 3, 4, 0, 0);
+ addTestCase (false, 3, 4, 0, 4);
+ addTestCase (false, 3, 4, 4, 0);
+ addTestCase (false, 3, 4, 4, 4);
+ addTestCase (false, -3, -4, 4, 0);
+ addTestCase (false, 0, 0, 0, 4);
+ addTestCase (false, 0, 0, 4, 0);
+ addTestCase (true, 3, 4, 0, 0);
+ addTestCase (true, 3, 4, 0, 4);
+ addTestCase (true, 3, 4, 4, 0);
+ addTestCase (true, 3, 4, 4, 4);
+ addTestCase (true, -3, -4, 4, 0);
+ addTestCase (true, 0, 0, 0, 4);
+ addTestCase (true, 0, 0, 4, 0);
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/calendar.js b/tests/interactive/calendar.js
new file mode 100644
index 0000000..d1d435a
--- /dev/null
+++ b/tests/interactive/calendar.js
@@ -0,0 +1,28 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage({ width: 400, height: 400 });
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ vertical: true,
+ width: stage.width,
+ height: stage.height,
+ style: 'padding: 10px; spacing: 10px; font: 15px sans-serif;' });
+ stage.add_actor(vbox);
+
+ // Calendar can only be imported after Environment.init()
+ const Calendar = imports.ui.calendar;
+ let calendar = new Calendar.Calendar();
+ vbox.add(calendar,
+ { expand: true,
+ x_fill: false, x_align: St.Align.MIDDLE,
+ y_fill: false, y_align: St.Align.START });
+ calendar.setEventSource(new Calendar.EmptyEventSource());
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/css-fonts.js b/tests/interactive/css-fonts.js
new file mode 100644
index 0000000..a257693
--- /dev/null
+++ b/tests/interactive/css-fonts.js
@@ -0,0 +1,40 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage();
+ UI.init(stage);
+
+ let b = new St.BoxLayout({ vertical: true,
+ width: stage.width,
+ height: stage.height });
+ stage.add_actor(b);
+
+ let t;
+
+ t = new St.Label({ "text": "Bold", style_class: "bold" });
+ b.add(t);
+ t = new St.Label({ "text": "Monospace", style_class: "monospace" });
+ b.add(t);
+ t = new St.Label({ "text": "Italic", style_class: "italic" });
+ b.add(t);
+ t = new St.Label({ "text": "Bold Italic", style_class: "bold italic" });
+ b.add(t);
+ t = new St.Label({ "text": "Big Italic", style_class: "big italic" });
+ b.add(t);
+ t = new St.Label({ "text": "Big Bold", style_class: "big bold" });
+ b.add(t);
+
+ let b2 = new St.BoxLayout({ vertical: true, style_class: "monospace" });
+ b.add(b2);
+ t = new St.Label({ "text": "Big Monospace", style_class: "big" });
+ b2.add(t);
+ t = new St.Label({ "text": "Italic Monospace", style_class: "italic" });
+ b2.add(t);
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/entry.js b/tests/interactive/entry.js
new file mode 100644
index 0000000..9ae0106
--- /dev/null
+++ b/tests/interactive/entry.js
@@ -0,0 +1,57 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, GLib, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage({ width: 400, height: 400 });
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ vertical: true,
+ width: stage.width,
+ height: stage.height,
+ style: 'padding: 10px; spacing: 10px; font: 32px sans-serif;' });
+ stage.add_actor(vbox);
+
+ let entry = new St.Entry({ style: 'border: 1px solid black; text-shadow: 0 2px red;',
+ text: 'Example text' });
+ vbox.add(entry,
+ { expand: true,
+ y_fill: false, y_align: St.Align.MIDDLE });
+ entry.grab_key_focus();
+
+ let entryTextHint = new St.Entry({ style: 'border: 1px solid black; text-shadow: 0 2px red;',
+ hint_text: 'Hint text' });
+ vbox.add(entryTextHint,
+ { expand: true,
+ y_fill: false, y_align: St.Align.MIDDLE });
+
+ let hintActor = new St.Label({ text: 'Hint actor' });
+ let entryHintActor = new St.Entry({ style: 'border: 1px solid black; text-shadow: 0 2px red;',
+ hint_actor: hintActor });
+ vbox.add(entryHintActor,
+ { expand: true,
+ y_fill: false, y_align: St.Align.MIDDLE });
+
+ let hintActor2 = new St.Label({ text: 'Hint both (actor)' });
+ let entryHintBoth = new St.Entry({ style: 'border: 1px solid black; text-shadow: 0 2px red;',
+ hint_actor: hintActor2 });
+ let idx = 0;
+ GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, function() {
+ idx++;
+
+ if (idx % 2 == 0)
+ entryHintBoth.hint_actor = hintActor2;
+ else
+ entryHintBoth.hint_text = 'Hint both (text)';
+
+ return true;
+ });
+ vbox.add(entryHintBoth,
+ { expand: true,
+ y_fill: false, y_align: St.Align.MIDDLE });
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/gapplication.js b/tests/interactive/gapplication.js
new file mode 100755
index 0000000..9dd2a40
--- /dev/null
+++ b/tests/interactive/gapplication.js
@@ -0,0 +1,104 @@
+#!/usr/bin/env gjs
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+imports.gi.versions = { Gdk: '3.0', Gtk: '3.0' };
+const { Gdk, Gio, GLib, Gtk } = imports.gi;
+
+function do_action(action, parameter) {
+ print ("Action '" + action.name + "' invoked");
+}
+
+function do_action_param(action, parameter) {
+ print ("Action '" + action.name + "' invoked with parameter " + parameter.print(true));
+}
+
+function do_action_toggle(action) {
+ action.set_state(GLib.Variant.new('b', !action.state.deepUnpack()));
+ print ("Toggled");
+}
+
+function do_action_state_change(action) {
+ print ("Action '" + action.name + "' has now state " + action.state.print(true));
+}
+
+function main() {
+ Gtk.init(null);
+ Gdk.set_program_class('test-gjsgapp');
+
+ let app = new Gtk.Application({ application_id: 'org.gnome.Shell.GtkApplicationTest' });
+ app.connect('activate', () => {
+ print ("Activated");
+ });
+
+ let action = new Gio.SimpleAction({ name: 'one' });
+ action.connect('activate', do_action);
+ app.add_action(action);
+
+ action = new Gio.SimpleAction({ name: 'two' });
+ action.connect('activate', do_action);
+ app.add_action(action);
+
+ action = new Gio.SimpleAction({ name: 'toggle', state: GLib.Variant.new('b', false) });
+ action.connect('activate', do_action_toggle);
+ action.connect('notify::state', do_action_state_change);
+ app.add_action(action);
+
+ action = new Gio.SimpleAction({ name: 'disable', enabled: false });
+ action.set_enabled(false);
+ action.connect('activate', do_action);
+ app.add_action(action);
+
+ action = new Gio.SimpleAction({ name: 'parameter-int', parameter_type: GLib.VariantType.new('u') });
+ action.connect('activate', do_action_param);
+ app.add_action(action);
+
+ action = new Gio.SimpleAction({ name: 'parameter-string', parameter_type: GLib.VariantType.new('s') });
+ action.connect('activate', do_action_param);
+ app.add_action(action);
+
+ let menu = new Gio.Menu();
+ menu.append('An action', 'app.one');
+
+ let section = new Gio.Menu();
+ section.append('Another action', 'app.two');
+ section.append('Same as above', 'app.two');
+ menu.append_section(null, section);
+
+ // another section, to check separators
+ section = new Gio.Menu();
+ section.append('Checkbox', 'app.toggle');
+ section.append('Disabled', 'app.disable');
+ section.append('Missing Action', 'app.no-action');
+ menu.append_section('Subsection', section);
+
+ // empty sections or submenus should be invisible
+ menu.append_section('Empty section', new Gio.Menu());
+ menu.append_submenu('Empty submenu', new Gio.Menu());
+
+ let submenu = new Gio.Menu();
+ submenu.append('Open c:\\', 'app.parameter-string::c:\\');
+ submenu.append('Open /home', 'app.parameter-string::/home');
+ menu.append_submenu('Recent files', submenu);
+
+ let item = Gio.MenuItem.new('Say 42', null);
+ item.set_action_and_target_value('app.parameter-int', GLib.Variant.new('u', 42));
+ menu.append_item(item);
+
+ item = Gio.MenuItem.new('Say 43', null);
+ item.set_action_and_target_value('app.parameter-int', GLib.Variant.new('u', 43));
+ menu.append_item(item);
+
+ let window = null;
+
+ app.connect_after('startup', app => {
+ app.set_app_menu(menu);
+ window = new Gtk.ApplicationWindow({ title: "Test Application", application: app });
+ });
+ app.connect('activate', app => {
+ window.present();
+ });
+
+ app.run(null);
+}
+
+main();
diff --git a/tests/interactive/icons.js b/tests/interactive/icons.js
new file mode 100644
index 0000000..65b7f65
--- /dev/null
+++ b/tests/interactive/icons.js
@@ -0,0 +1,79 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage();
+ UI.init(stage);
+
+ let b = new St.BoxLayout({ vertical: true,
+ width: stage.width,
+ height: stage.height });
+ stage.add_actor(b);
+
+ function addTest(label, icon_props) {
+ if (b.get_children().length > 0)
+ b.add (new St.BoxLayout({ style: 'background: #cccccc; border: 10px transparent white; height: 1px; ' }));
+
+ let hb = new St.BoxLayout({ vertical: false,
+ style: 'spacing: 10px;' });
+
+ hb.add(new St.Label({ text: label }), { y_fill: false });
+ hb.add(new St.Icon(icon_props));
+
+ b.add(hb);
+ }
+
+ addTest("Symbolic",
+ { icon_name: 'battery-full-symbolic',
+ icon_size: 48 });
+ addTest("Full color",
+ { icon_name: 'battery-full',
+ icon_size: 48 });
+ addTest("Default size",
+ { icon_name: 'battery-full-symbolic' });
+ addTest("Size set by property",
+ { icon_name: 'battery-full-symbolic',
+ icon_size: 32 });
+ addTest("Size set by style",
+ { icon_name: 'battery-full-symbolic',
+ style: 'icon-size: 1em;' });
+ addTest("16px icon in 48px icon widget",
+ { icon_name: 'battery-full-symbolic',
+ style: 'icon-size: 16px; width: 48px; height: 48px; border: 1px solid black;' });
+
+ function iconRow(icons, box_style) {
+ let hb = new St.BoxLayout({ vertical: false, style: box_style });
+
+ for (let iconName of icons) {
+ hb.add(new St.Icon({ icon_name: iconName,
+ icon_size: 48 }));
+ }
+
+ b.add(hb);
+ }
+
+ let normalCss = 'background: white; color: black; padding: 10px 10px;';
+ let reversedCss = 'background: black; color: white; warning-color: #ffcc00; error-color: #ff0000; padding: 10px 10px;';
+
+ let batteryIcons = ['battery-full-charging-symbolic',
+ 'battery-full-symbolic',
+ 'battery-good-symbolic',
+ 'battery-low-symbolic',
+ 'battery-caution-symbolic' ];
+
+ let volumeIcons = ['audio-volume-high-symbolic',
+ 'audio-volume-medium-symbolic',
+ 'audio-volume-low-symbolic',
+ 'audio-volume-muted-symbolic' ];
+
+ iconRow(batteryIcons, normalCss);
+ iconRow(batteryIcons, reversedCss);
+ iconRow(volumeIcons, normalCss);
+ iconRow(volumeIcons, reversedCss);
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/inline-style.js b/tests/interactive/inline-style.js
new file mode 100644
index 0000000..3952c3a
--- /dev/null
+++ b/tests/interactive/inline-style.js
@@ -0,0 +1,46 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage();
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ vertical: true,
+ width: stage.width,
+ height: stage.height });
+ stage.add_actor(vbox);
+
+ let hbox = new St.BoxLayout({ style: 'spacing: 12px;' });
+ vbox.add(hbox);
+
+ let text = new St.Label({ text: "Styled Text" });
+ vbox.add (text);
+
+ let size = 24;
+ function update_size() {
+ text.style = 'font-size: ' + size + 'pt';
+ }
+ update_size();
+
+ let button;
+
+ button = new St.Button ({ label: 'Smaller', style_class: 'push-button' });
+ hbox.add (button);
+ button.connect('clicked', () => {
+ size /= 1.2;
+ update_size ();
+ });
+
+ button = new St.Button ({ label: 'Bigger', style_class: 'push-button' });
+ hbox.add (button);
+ button.connect('clicked', () => {
+ size *= 1.2;
+ update_size ();
+ });
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/scroll-view-sizing.js b/tests/interactive/scroll-view-sizing.js
new file mode 100644
index 0000000..a6c682e
--- /dev/null
+++ b/tests/interactive/scroll-view-sizing.js
@@ -0,0 +1,395 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, GObject, Gtk, Shell, St } = imports.gi;
+
+// This is an interactive test of the sizing behavior of StScrollView. It
+// may be interesting in the future to split out the two classes at the
+// top into utility classes for testing the sizing behavior of other
+// containers and actors.
+
+/****************************************************************************/
+
+// FlowedBoxes: This is a simple actor that demonstrates an interesting
+// height-for-width behavior. A set of boxes of different sizes are line-wrapped
+// horizontally with the minimum horizontal size being determined by the
+// largest box. It would be easy to extend this to allow doing vertical
+// wrapping instead, if you wanted to see just how badly our width-for-height
+// implementation is or work on fixing it.
+
+const BOX_HEIGHT = 20;
+const BOX_WIDTHS = [
+ 10, 40, 100, 20, 60, 30, 70, 10, 20, 200, 50, 70, 90, 20, 40,
+ 10, 40, 100, 20, 60, 30, 70, 10, 20, 200, 50, 70, 90, 20, 40,
+ 10, 40, 100, 20, 60, 30, 70, 10, 20, 200, 50, 70, 90, 20, 40,
+ 10, 40, 100, 20, 60, 30, 70, 10, 20, 200, 50, 70, 90, 20, 40,
+];
+
+const SPACING = 10;
+
+var FlowedBoxes = GObject.registerClass(
+class FlowedBoxes extends St.Widget {
+ _init() {
+ super._init();
+
+ for (let i = 0; i < BOX_WIDTHS.length; i++) {
+ let child = new St.Bin({ width: BOX_WIDTHS[i], height: BOX_HEIGHT,
+ style: 'border: 1px solid #444444; background: #00aa44' });
+ this.add_actor(child);
+ }
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ let children = this.get_children();
+
+ let maxMinWidth = 0;
+ let totalNaturalWidth = 0;
+
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];
+ let [minWidth, naturalWidth] = child.get_preferred_width(-1);
+ maxMinWidth = Math.max(maxMinWidth, minWidth);
+ if (i != 0)
+ totalNaturalWidth += SPACING;
+ totalNaturalWidth += naturalWidth;
+ }
+
+ return [maxMinWidth, totalNaturalWidth];
+ }
+
+ _layoutChildren(forWidth, callback) {
+ let children = this.get_children();
+
+ let x = 0;
+ let y = 0;
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];
+ let [minWidth, naturalWidth] = child.get_preferred_width(-1);
+ let [minHeight, naturalHeight] = child.get_preferred_height(naturalWidth);
+
+ let x1 = x;
+ if (x != 0)
+ x1 += SPACING;
+ let x2 = x1 + naturalWidth;
+
+ if (x2 > forWidth) {
+ if (x > 0) {
+ x1 = 0;
+ y += BOX_HEIGHT + SPACING;
+ }
+
+ x2 = naturalWidth;
+ }
+
+ callback(child, x1, y, x2, y + naturalHeight);
+ x = x2;
+ }
+
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ let height = 0;
+ this._layoutChildren(forWidth,
+ function(child, x1, y1, x2, y2) {
+ height = Math.max(height, y2);
+ });
+
+ return [height, height];
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ this._layoutChildren(box.x2 - box.x1,
+ function(child, x1, y1, x2, y2) {
+ child.allocate(new Clutter.ActorBox({ x1: x1, y1: y1, x2: x2, y2: y2 }));
+ });
+ }
+});
+
+/****************************************************************************/
+
+// SizingIllustrator: this is a container that allows interactively exploring
+// the sizing behavior of the child. Lines are drawn to indicate the minimum
+// and natural size of the child, and a drag handle allows the user to resize
+// the child interactively and see how that affects it.
+//
+// This is currently only written for the case where the child is height-for-width
+
+var SizingIllustrator = GObject.registerClass(
+class SizingIllustrator extends St.Widget {
+ _init() {
+ super._init();
+
+ this.minWidthLine = new St.Bin({ style: 'background: red' });
+ this.add_actor(this.minWidthLine);
+ this.minHeightLine = new St.Bin({ style: 'background: red' });
+ this.add_actor(this.minHeightLine);
+
+ this.naturalWidthLine = new St.Bin({ style: 'background: #4444ff' });
+ this.add_actor(this.naturalWidthLine);
+ this.naturalHeightLine = new St.Bin({ style: 'background: #4444ff' });
+ this.add_actor(this.naturalHeightLine);
+
+ this.currentWidthLine = new St.Bin({ style: 'background: #aaaaaa' });
+ this.add_actor(this.currentWidthLine);
+ this.currentHeightLine = new St.Bin({ style: 'background: #aaaaaa' });
+ this.add_actor(this.currentHeightLine);
+
+ this.handle = new St.Bin({ style: 'background: yellow; border: 1px solid black;',
+ reactive: true });
+ this.handle.connect('button-press-event', this._handlePressed.bind(this));
+ this.handle.connect('button-release-event', this._handleReleased.bind(this));
+ this.handle.connect('motion-event', this._handleMotion.bind(this));
+ this.add_actor(this.handle);
+
+ this._inDrag = false;
+
+ this.width = 300;
+ this.height = 300;
+ }
+
+ add(child) {
+ this.child = child;
+ this.add_child(child);
+ this.set_child_below_sibling(child, null);
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ let children = this.get_children();
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];
+ let [minWidth, naturalWidth] = child.get_preferred_width(-1);
+ if (child == this.child) {
+ this.minWidth = minWidth;
+ this.naturalWidth = naturalWidth;
+ }
+ }
+
+ return [0, 400];
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ let children = this.get_children();
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];
+ if (child == this.child) {
+ [this.minHeight, this.naturalHeight] = child.get_preferred_height(this.width);
+ } else {
+ let [minWidth, naturalWidth] = child.get_preferred_height(naturalWidth);
+ }
+ }
+
+ return [0, 400];
+ }
+
+ vfunc_allocate(box) {
+ this.set_allocation(box);
+
+ box = this.get_theme_node().get_content_box(box);
+
+ let allocWidth = box.x2 - box.x1;
+ let allocHeight = box.y2 - box.y1;
+
+ function alloc(child, x1, y1, x2, y2) {
+ child.allocate(new Clutter.ActorBox({ x1: x1, y1: y1, x2: x2, y2: y2 }));
+ }
+
+ alloc(this.child, 0, 0, this.width, this.height);
+ alloc(this.minWidthLine, this.minWidth, 0, this.minWidth + 1, allocHeight);
+ alloc(this.naturalWidthLine, this.naturalWidth, 0, this.naturalWidth + 1, allocHeight);
+ alloc(this.currentWidthLine, this.width, 0, this.width + 1, allocHeight);
+ alloc(this.minHeightLine, 0, this.minHeight, allocWidth, this.minHeight + 1);
+ alloc(this.naturalHeightLine, 0, this.naturalHeight, allocWidth, this.naturalHeight + 1);
+ alloc(this.currentHeightLine, 0, this.height, allocWidth, this.height + 1);
+ alloc(this.handle, this.width, this.height, this.width + 10, this.height + 10);
+ }
+
+ _handlePressed(handle, event) {
+ if (event.get_button() == 1) {
+ this._inDrag = true;
+ let [handleX, handleY] = handle.get_transformed_position();
+ let [x, y] = event.get_coords();
+ this._dragX = x - handleX;
+ this._dragY = y - handleY;
+ }
+ }
+
+ _handleReleased(handle, event) {
+ if (event.get_button() == 1) {
+ this._inDrag = false;
+ }
+ }
+
+ _handleMotion(handle, event) {
+ if (this._inDrag) {
+ let [x, y] = event.get_coords();
+ let [actorX, actorY] = this.get_transformed_position();
+ this.width = x - this._dragX - actorX;
+ this.height = y - this._dragY - actorY;
+ this.queue_relayout();
+ }
+ }
+});
+
+/****************************************************************************/
+
+function test() {
+ let stage = new Clutter.Stage({ width: 600, height: 600 });
+ UI.init(stage);
+
+ let mainBox = new St.BoxLayout({ width: stage.width,
+ height: stage.height,
+ vertical: true,
+ style: 'padding: 10px;'
+ + 'spacing: 5px;'
+ + 'font: 16px sans-serif;'
+ + 'background: black;'
+ + 'color: white;' });
+ stage.add_actor(mainBox);
+
+ const DOCS = 'Red lines represent minimum size, blue lines natural size. Drag yellow handle to resize ScrollView. Click on options to change.';
+
+ let docsLabel = new St.Label({ text: DOCS });
+ docsLabel.clutter_text.line_wrap = true;
+ mainBox.add(docsLabel);
+
+ let bin = new St.Bin({ x_fill: true, y_fill: true, style: 'border: 2px solid #666666;' });
+ mainBox.add(bin, { x_fill: true, y_fill: true, expand: true });
+
+ let illustrator = new SizingIllustrator();
+ bin.add_actor(illustrator);
+
+ let scrollView = new St.ScrollView();
+ illustrator.add(scrollView);
+
+ let box = new St.BoxLayout({ vertical: true });
+ scrollView.add_actor(box);
+
+ let flowedBoxes = new FlowedBoxes();
+ box.add(flowedBoxes, { expand: false, x_fill: true, y_fill: true });
+
+ let policyBox = new St.BoxLayout({ vertical: false });
+ mainBox.add(policyBox);
+
+ policyBox.add(new St.Label({ text: 'Horizontal Policy: ' }));
+ let hpolicy = new St.Button({ label: 'AUTOMATIC', style: 'text-decoration: underline; color: #4444ff;' });
+ policyBox.add(hpolicy);
+
+ let spacer = new St.Bin();
+ policyBox.add(spacer, { expand: true });
+
+ policyBox.add(new St.Label({ text: 'Vertical Policy: '}));
+ let vpolicy = new St.Button({ label: 'AUTOMATIC', style: 'text-decoration: underline; color: #4444ff;' });
+ policyBox.add(vpolicy);
+
+ function togglePolicy(button) {
+ switch(button.label) {
+ case 'AUTOMATIC':
+ button.label = 'ALWAYS';
+ break;
+ case 'ALWAYS':
+ button.label = 'NEVER';
+ break;
+ case 'NEVER':
+ button.label = 'EXTERNAL';
+ break;
+ case 'EXTERNAL':
+ button.label = 'AUTOMATIC';
+ break;
+ }
+ scrollView.set_policy(Gtk.PolicyType[hpolicy.label], Gtk.PolicyType[vpolicy.label]);
+ }
+
+ hpolicy.connect('clicked', () => { togglePolicy(hpolicy); });
+ vpolicy.connect('clicked', () => { togglePolicy(vpolicy); });
+
+ let fadeBox = new St.BoxLayout({ vertical: false });
+ mainBox.add(fadeBox);
+
+ spacer = new St.Bin();
+ fadeBox.add(spacer, { expand: true });
+
+ fadeBox.add(new St.Label({ text: 'Padding: '}));
+ let paddingButton = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;padding-right:3px;' });
+ fadeBox.add(paddingButton);
+
+ fadeBox.add(new St.Label({ text: 'Borders: '}));
+ let borderButton = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;padding-right:3px;' });
+ fadeBox.add(borderButton);
+
+ fadeBox.add(new St.Label({ text: 'Vertical Fade: '}));
+ let vfade = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;' });
+ fadeBox.add(vfade);
+
+ fadeBox.add(new St.Label({ text: 'Overlay scrollbars: '}));
+ let overlay = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;' });
+ fadeBox.add(overlay);
+
+ function togglePadding(button) {
+ switch(button.label) {
+ case 'No':
+ button.label = 'Yes';
+ break;
+ case 'Yes':
+ button.label = 'No';
+ break;
+ }
+ if (scrollView.style == null)
+ scrollView.style = (button.label == 'Yes' ? 'padding: 10px;' : 'padding: 0;');
+ else
+ scrollView.style += (button.label == 'Yes' ? 'padding: 10px;' : 'padding: 0;');
+ }
+
+ paddingButton.connect('clicked', () => { togglePadding(paddingButton); });
+
+ function toggleBorders(button) {
+ switch(button.label) {
+ case 'No':
+ button.label = 'Yes';
+ break;
+ case 'Yes':
+ button.label = 'No';
+ break;
+ }
+ if (scrollView.style == null)
+ scrollView.style = (button.label == 'Yes' ? 'border: 2px solid red;' : 'border: 0;');
+ else
+ scrollView.style += (button.label == 'Yes' ? 'border: 2px solid red;' : 'border: 0;');
+ }
+
+ borderButton.connect('clicked', () => { toggleBorders(borderButton); });
+
+ function toggleFade(button) {
+ switch(button.label) {
+ case 'No':
+ button.label = 'Yes';
+ break;
+ case 'Yes':
+ button.label = 'No';
+ break;
+ }
+ scrollView.set_style_class_name(button.label == 'Yes' ? 'vfade' : '');
+ }
+
+ vfade.connect('clicked', () => { toggleFade(vfade); });
+ toggleFade(vfade);
+
+ function toggleOverlay(button) {
+ switch(button.label) {
+ case 'No':
+ button.label = 'Yes';
+ break;
+ case 'Yes':
+ button.label = 'No';
+ break;
+ }
+ scrollView.overlay_scrollbars = (button.label == 'Yes');
+ }
+
+ overlay.connect('clicked', () => { toggleOverlay(overlay); });
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/scrolling.js b/tests/interactive/scrolling.js
new file mode 100644
index 0000000..91951ce
--- /dev/null
+++ b/tests/interactive/scrolling.js
@@ -0,0 +1,51 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, Gtk, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage();
+ UI.init(stage);
+
+ let vbox = new St.BoxLayout({ vertical: true,
+ width: stage.width,
+ height: stage.height,
+ style: "padding: 10px;" });
+ stage.add_actor(vbox);
+
+ let toggle = new St.Button({ label: 'Horizontal Scrolling',
+ toggle_mode: true });
+ vbox.add(toggle);
+
+ let v = new St.ScrollView();
+ vbox.add(v, { expand: true });
+
+ toggle.connect('notify::checked', () => {
+ v.set_policy(toggle.checked ? Gtk.PolicyType.AUTOMATIC
+ : Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC);
+ });
+
+ let b = new St.BoxLayout({ vertical: true,
+ style: "border: 2px solid #880000; border-radius: 10px; padding: 0px 5px;" });
+ v.add_actor(b);
+
+ let cc_a = "a".charCodeAt(0);
+ let s = "";
+ for (let i = 0; i < 26 * 3; i++) {
+ s += String.fromCharCode(cc_a + i % 26);
+
+ let t = new St.Label({ text: s,
+ reactive: true });
+ let line = i + 1;
+ t.connect('button-press-event',
+ function() {
+ log("Click on line " + line);
+ });
+ b.add(t);
+ }
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/interactive/test-title.js b/tests/interactive/test-title.js
new file mode 100755
index 0000000..0a468dd
--- /dev/null
+++ b/tests/interactive/test-title.js
@@ -0,0 +1,37 @@
+#!/usr/bin/env gjs
+
+imports.gi.versions.Gtk = '3.0';
+
+const { GLib, Gtk } = imports.gi;
+
+function nextTitle() {
+ let length = Math.random() * 20;
+ let str = '';
+
+ for (let i = 0; i < length; i++) {
+ // 97 == 'a'
+ str += String.fromCharCode(97 + Math.random() * 26);
+ }
+
+ return str;
+}
+
+function main() {
+ Gtk.init(null);
+
+ let win = new Gtk.Window({ title: nextTitle() });
+ win.connect('destroy', () => {
+ Gtk.main_quit();
+ });
+ win.present();
+
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, 5000, function() {
+ win.title = nextTitle();
+ return true;
+ });
+
+ Gtk.main();
+}
+
+main();
+
diff --git a/tests/interactive/transitions.js b/tests/interactive/transitions.js
new file mode 100644
index 0000000..7b2eac1
--- /dev/null
+++ b/tests/interactive/transitions.js
@@ -0,0 +1,35 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const UI = imports.testcommon.ui;
+
+const { Clutter, St } = imports.gi;
+
+function test() {
+ let stage = new Clutter.Stage();
+ UI.init(stage);
+
+ let hbox = new St.BoxLayout({ name: 'transition-container',
+ reactive: true,
+ track_hover: true,
+ width: stage.width,
+ height: stage.height,
+ style: 'padding: 10px;'
+ + 'spacing: 10px;' });
+ stage.add_actor(hbox);
+
+ for (let i = 0; i < 5; i ++) {
+ let label = new St.Label({ text: (i+1).toString(),
+ name: "label" + i,
+ style_class: 'transition-label',
+ reactive: true,
+ track_hover: true });
+
+ hbox.add(label, { x_fill: false,
+ y_fill: false });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ UI.main(stage);
+}
+test();
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 0000000..9d3925d
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,29 @@
+testconf = configuration_data()
+testconf.set('MUTTER_TYPELIB_DIR', mutter_typelibdir)
+testconf.set('srcdir', meson.current_source_dir())
+run_test = configure_file(
+ input: 'run-test.sh.in',
+ output: 'run-test.sh',
+ configuration: testconf
+)
+
+testenv = environment()
+testenv.set('GSETTINGS_SCHEMA_DIR', join_paths(meson.project_build_root(), 'data'))
+
+tests = [
+ 'highlighter',
+ 'insertSorted',
+ 'jsParse',
+ 'markup',
+ 'params',
+ 'signalTracker',
+ 'url',
+ 'versionCompare',
+]
+
+foreach test : tests
+ test(test, run_test,
+ args: 'unit/@0@.js'.format(test),
+ env: testenv,
+ workdir: meson.current_source_dir())
+endforeach
diff --git a/tests/run-test.sh.in b/tests/run-test.sh.in
new file mode 100755
index 0000000..ea6d157
--- /dev/null
+++ b/tests/run-test.sh.in
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+usage() {
+ echo >&2 "Usage run-test.sh [-v|--verbose] <test_js>..."
+ exit 1
+}
+
+tests=
+verbose=false
+debug=
+for arg in $@ ; do
+ case $arg in
+ -g|--debug)
+ debug="libtool --mode=execute gdb --args"
+ ;;
+ -v|--verbose)
+ verbose=true
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ tests="$tests $arg"
+ ;;
+ esac
+done
+
+builddir=`dirname $0`
+builddir=`cd $builddir && pwd`
+srcdir=@srcdir@
+srcdir=`cd $srcdir && pwd`
+
+GI_TYPELIB_PATH="$GI_TYPELIB_PATH${GI_TYPELIB_PATH:+:}@MUTTER_TYPELIB_DIR@:$builddir/../src:$builddir/../src/st:$builddir/../subprojects/gvc"
+GJS_PATH="$srcdir:$srcdir/../js:$builddir/../js"
+GJS_DEBUG_OUTPUT=stderr
+$verbose || GJS_DEBUG_TOPICS="JS ERROR;JS LOG"
+GNOME_SHELL_TESTSDIR="$srcdir/"
+GNOME_SHELL_JS="$srcdir/../js"
+GNOME_SHELL_DATADIR="$builddir/../data"
+
+export GI_TYPELIB_PATH GJS_PATH GJS_DEBUG_OUTPUT GJS_DEBUG_TOPICS GNOME_SHELL_TESTSDIR GNOME_SHELL_JS GNOME_SHELL_DATADIR LD_PRELOAD
+
+for test in $tests ; do
+ $debug $builddir/../src/run-js-test $test || exit $?
+done
diff --git a/tests/testcommon/100-200.svg b/tests/testcommon/100-200.svg
new file mode 100644
index 0000000..59a5307
--- /dev/null
+++ b/tests/testcommon/100-200.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 100 200" width="100" height="200">
+ <path
+ d="
+ M 2,2 h 96 v 196 h -96 v -196
+ M 8,8 h 84 v 184 h -84 v -184
+ "
+ fill="white"
+ stroke="blue"
+ stroke-width="2"
+ stroke-linecap="square"
+ />
+ <path
+ d="
+ M 10,10 h 20 v 20 h -20 v -20
+ "
+ fill="green"
+ />
+</svg>
diff --git a/tests/testcommon/200-100.svg b/tests/testcommon/200-100.svg
new file mode 100644
index 0000000..e149b5f
--- /dev/null
+++ b/tests/testcommon/200-100.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 200 100" width="200" height="100">
+ <path
+ d="
+ M 2,2 h 196 v 96 h -196 v -96
+ M 8,8 h 184 v 84 h -184 v -84
+ "
+ fill="white"
+ stroke="blue"
+ stroke-width="2"
+ stroke-linecap="square"
+ />
+ <path
+ d="
+ M 10,10 h 20 v 20 h -20 v -20
+ "
+ fill="green"
+ />
+</svg>
diff --git a/tests/testcommon/200-200.svg b/tests/testcommon/200-200.svg
new file mode 100644
index 0000000..9965a2a
--- /dev/null
+++ b/tests/testcommon/200-200.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 200 200" width="200" height="200">
+ <path
+ d="
+ M 2,2 h 196 v 196 h -196 v -196
+ M 8,8 h 184 v 184 h -184 v -184
+ "
+ fill="white"
+ stroke="blue"
+ stroke-width="2"
+ stroke-linecap="square"
+ />
+ <path
+ d="
+ M 10,10 h 20 v 20 h -20 v -20
+ "
+ fill="green"
+ />
+</svg>
diff --git a/tests/testcommon/border-image.png b/tests/testcommon/border-image.png
new file mode 100644
index 0000000..e680020
--- /dev/null
+++ b/tests/testcommon/border-image.png
Binary files differ
diff --git a/tests/testcommon/face-plain.png b/tests/testcommon/face-plain.png
new file mode 100644
index 0000000..962d70f
--- /dev/null
+++ b/tests/testcommon/face-plain.png
Binary files differ
diff --git a/tests/testcommon/test.css b/tests/testcommon/test.css
new file mode 100644
index 0000000..b82d230
--- /dev/null
+++ b/tests/testcommon/test.css
@@ -0,0 +1,112 @@
+@import url("resource:///org/gnome/shell/theme/gnome-shell.css");
+
+stage {
+ font: 16pt serif;
+ color: black;
+}
+
+.red {
+ background-color: red;
+}
+
+.green {
+ background-color: green;
+}
+
+.blue {
+ background-color: blue;
+}
+
+.bold {
+ font-weight: bold;
+}
+
+.italic {
+ font-style: italic;
+}
+
+.big {
+ font-size: 150%;
+}
+
+.monospace {
+ font-family: monospace;
+}
+
+.border-image {
+ border: 15px;
+ border-image: url('border-image.png') 16;
+}
+
+.background-image-200-200 {
+ background-image: url('200-200.svg');
+}
+
+.background-image-100-200 {
+ background-image: url('100-200.svg');
+}
+
+.background-image-200-100 {
+ background-image: url('200-100.svg');
+}
+
+.background-gradient {
+ background-gradient-start: rgba(127, 255, 127, .6);
+ background-gradient-end: rgba(127, 127, 255, .6);
+}
+
+.border-image-with-background-gradient {
+ border: 15px black solid;
+ border-image: url('border-image.png') 16;
+ background-gradient-start: #88ff88;
+ background-gradient-end: #8888ff;
+}
+
+.background-image {
+ background-image: url('face-plain.png');
+ background-color: white;
+}
+
+.background-repeat {
+ background-repeat: repeat;
+}
+
+.push-button {
+ background: #eeddbb;
+ border: 1px solid black;
+ border-radius: 8px;
+ padding: 5px;
+}
+
+.push-button:hover {
+ background: #ffeecc;
+}
+
+.push-button:active {
+ background: #ccbb99;
+}
+
+.vfade {
+ -st-fade-offset: 68px;
+}
+
+#transition-container .transition-label {
+ color: white;
+ width: 1em;
+ height: 1em;
+ padding: 1em;
+ background-color: #333;
+ border: 2px solid black;
+ border-radius: 8px;
+ transition-duration: 1s;
+}
+
+#transition-container:hover .transition-label {
+ background-color: blue;
+ border: 2px solid red;
+}
+
+#transition-container .transition-label:hover {
+ background-color: green;
+ border: 2px solid blue;
+}
diff --git a/tests/testcommon/ui.js b/tests/testcommon/ui.js
new file mode 100644
index 0000000..abacea5
--- /dev/null
+++ b/tests/testcommon/ui.js
@@ -0,0 +1,28 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Config = imports.misc.config;
+
+imports.gi.versions = { Clutter: Config.LIBMUTTER_API_VERSION, Gtk: '3.0' };
+
+const { Clutter, Gio, GLib, St } = imports.gi;
+
+const Environment = imports.ui.environment;
+
+function init(stage) {
+ Environment.init();
+ let themeResource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource');
+ themeResource._register();
+
+ let context = St.ThemeContext.get_for_stage(stage);
+ let stylesheetPath = GLib.getenv("GNOME_SHELL_TESTSDIR") + "/testcommon/test.css";
+ let theme = new St.Theme({ application_stylesheet: Gio.File.new_for_path(stylesheetPath) });
+ context.set_theme(theme);
+}
+
+function main(stage) {
+ stage.show();
+ stage.connect('destroy', () => {
+ Clutter.main_quit();
+ });
+ Clutter.main();
+}
diff --git a/tests/unit/highlighter.js b/tests/unit/highlighter.js
new file mode 100644
index 0000000..d582d38
--- /dev/null
+++ b/tests/unit/highlighter.js
@@ -0,0 +1,106 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+// Test cases for SearchResult description match highlighter
+
+const JsUnit = imports.jsUnit;
+const Pango = imports.gi.Pango;
+
+const Environment = imports.ui.environment;
+Environment.init();
+
+const Util = imports.misc.util;
+
+const tests = [
+ { input: 'abc cba',
+ terms: null,
+ output: 'abc cba' },
+ { input: 'abc cba',
+ terms: [],
+ output: 'abc cba' },
+ { input: 'abc cba',
+ terms: [''],
+ output: 'abc cba' },
+ { input: 'abc cba',
+ terms: ['a'],
+ output: '<b>a</b>bc cb<b>a</b>' },
+ { input: 'abc cba',
+ terms: ['a', 'a'],
+ output: '<b>a</b>bc cb<b>a</b>' },
+ { input: 'CaSe InSenSiTiVe',
+ terms: ['cas', 'sens'],
+ output: '<b>CaS</b>e In<b>SenS</b>iTiVe' },
+ { input: 'This contains the < character',
+ terms: null,
+ output: 'This contains the &lt; character' },
+ { input: 'Don\'t',
+ terms: ['t'],
+ output: 'Don&apos;<b>t</b>' },
+ { input: 'Don\'t',
+ terms: ['n\'t'],
+ output: 'Do<b>n&apos;t</b>' },
+ { input: 'Don\'t',
+ terms: ['o', 't'],
+ output: 'D<b>o</b>n&apos;<b>t</b>' },
+ { input: 'salt&pepper',
+ terms: ['salt'],
+ output: '<b>salt</b>&amp;pepper' },
+ { input: 'salt&pepper',
+ terms: ['salt', 'alt'],
+ output: '<b>salt</b>&amp;pepper' },
+ { input: 'salt&pepper',
+ terms: ['pepper'],
+ output: 'salt&amp;<b>pepper</b>' },
+ { input: 'salt&pepper',
+ terms: ['salt', 'pepper'],
+ output: '<b>salt</b>&amp;<b>pepper</b>' },
+ { input: 'salt&pepper',
+ terms: ['t', 'p'],
+ output: 'sal<b>t</b>&amp;<b>p</b>e<b>p</b><b>p</b>er' },
+ { input: 'salt&pepper',
+ terms: ['t', '&', 'p'],
+ output: 'sal<b>t</b><b>&amp;</b><b>p</b>e<b>p</b><b>p</b>er' },
+ { input: 'salt&pepper',
+ terms: ['e'],
+ output: 'salt&amp;p<b>e</b>pp<b>e</b>r' },
+ { input: 'salt&pepper',
+ terms: ['&a', '&am', '&amp', '&amp;'],
+ output: 'salt&amp;pepper' },
+ { input: '&&&&&',
+ terms: ['a'],
+ output: '&amp;&amp;&amp;&amp;&amp;' },
+ { input: '&;&;&;&;&;',
+ terms: ['a'],
+ output: '&amp;;&amp;;&amp;;&amp;;&amp;;' },
+ { input: '&;&;&;&;&;',
+ terms: [';'],
+ output: '&amp;<b>;</b>&amp;<b>;</b>&amp;<b>;</b>&amp;<b>;</b>&amp;<b>;</b>' },
+ { input: '&amp;',
+ terms: ['a'],
+ output: '&amp;<b>a</b>mp;' }
+];
+
+try {
+ for (let i = 0; i < tests.length; i++) {
+ let highlighter = new Util.Highlighter(tests[i].terms);
+ let output = highlighter.highlight(tests[i].input);
+
+ JsUnit.assertEquals(`Test ${i + 1} highlight ` +
+ `"${tests[i].terms}" in "${tests[i].input}"`,
+ output, tests[i].output);
+
+ let parsed = false;
+ try {
+ Pango.parse_markup(output, -1, '');
+ parsed = true;
+ } catch (e) {}
+ JsUnit.assertEquals(`Test ${i + 1} is valid markup`, true, parsed);
+ }
+} catch (e) {
+ if (typeof(e.isJsUnitException) != 'undefined'
+ && e.isJsUnitException)
+ {
+ if (e.comment)
+ log(`Error in: ${e.comment}`);
+ }
+ throw e;
+}
diff --git a/tests/unit/insertSorted.js b/tests/unit/insertSorted.js
new file mode 100644
index 0000000..610aeed
--- /dev/null
+++ b/tests/unit/insertSorted.js
@@ -0,0 +1,76 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+// Test cases for Util.insertSorted
+
+const JsUnit = imports.jsUnit;
+
+// Needed so that Util can bring some UI stuff
+// we don't actually use
+const Environment = imports.ui.environment;
+Environment.init();
+const Util = imports.misc.util;
+
+function assertArrayEquals(errorMessage, array1, array2) {
+ JsUnit.assertEquals(errorMessage + ' length',
+ array1.length, array2.length);
+ for (let j = 0; j < array1.length; j++) {
+ JsUnit.assertEquals(errorMessage + ' item ' + j,
+ array1[j], array2[j]);
+ }
+}
+
+function cmp(one, two) {
+ return one-two;
+}
+
+let arrayInt = [1, 2, 3, 5, 6];
+Util.insertSorted(arrayInt, 4, cmp);
+
+assertArrayEquals('first test', [1,2,3,4,5,6], arrayInt);
+
+// no comparator, integer sorting is implied
+Util.insertSorted(arrayInt, 3);
+
+assertArrayEquals('second test', [1,2,3,3,4,5,6], arrayInt);
+
+let obj1 = { a: 1 };
+let obj2 = { a: 2, b: 0 };
+let obj3 = { a: 2, b: 1 };
+let obj4 = { a: 3 };
+
+function objCmp(one, two) {
+ return one.a - two.a;
+}
+
+let arrayObj = [obj1, obj3, obj4];
+
+// obj2 compares equivalent to obj3, should be
+// inserted before
+Util.insertSorted(arrayObj, obj2, objCmp);
+
+assertArrayEquals('object test', [obj1, obj2, obj3, obj4], arrayObj);
+
+function checkedCmp(one, two) {
+ if (typeof one != 'number' ||
+ typeof two != 'number')
+ throw new TypeError('Invalid type passed to checkedCmp');
+
+ return one-two;
+}
+
+let arrayEmpty = [];
+
+// check that no comparisons are made when
+// inserting in a empty array
+Util.insertSorted(arrayEmpty, 3, checkedCmp);
+
+// Insert at the end and check that we don't
+// access past it
+Util.insertSorted(arrayEmpty, 4, checkedCmp);
+Util.insertSorted(arrayEmpty, 5, checkedCmp);
+
+// Some more insertions
+Util.insertSorted(arrayEmpty, 2, checkedCmp);
+Util.insertSorted(arrayEmpty, 1, checkedCmp);
+
+assertArrayEquals('checkedCmp test', [1, 2, 3, 4, 5], arrayEmpty);
diff --git a/tests/unit/jsParse.js b/tests/unit/jsParse.js
new file mode 100644
index 0000000..468138b
--- /dev/null
+++ b/tests/unit/jsParse.js
@@ -0,0 +1,194 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+// Test cases for MessageTray URLification
+
+const JsUnit = imports.jsUnit;
+
+const Environment = imports.ui.environment;
+Environment.init();
+
+const JsParse = imports.misc.jsParse;
+
+const HARNESS_COMMAND_HEADER = "let imports = obj;" +
+ "let global = obj;" +
+ "let Main = obj;" +
+ "let foo = obj;" +
+ "let r = obj;";
+
+const testsFindMatchingQuote = [
+ { input: '"double quotes"',
+ output: 0 },
+ { input: '\'single quotes\'',
+ output: 0 },
+ { input: 'some unquoted "some quoted"',
+ output: 14 },
+ { input: '"mixed \' quotes\'"',
+ output: 0 },
+ { input: '"escaped \\" quote"',
+ output: 0 }
+];
+const testsFindMatchingSlash = [
+ { input: '/slash/',
+ output: 0 },
+ { input: '/slash " with $ funny ^\' stuff/',
+ output: 0 },
+ { input: 'some unslashed /some slashed/',
+ output: 15 },
+ { input: '/escaped \\/ slash/',
+ output: 0 }
+];
+const testsFindMatchingBrace = [
+ { input: '[square brace]',
+ output: 0 },
+ { input: '(round brace)',
+ output: 0 },
+ { input: '([()][nesting!])',
+ output: 0 },
+ { input: '[we have "quoted [" braces]',
+ output: 0 },
+ { input: '[we have /regex [/ braces]',
+ output: 0 },
+ { input: '([[])[] mismatched braces ]',
+ output: 1 }
+];
+const testsGetExpressionOffset = [
+ { input: 'abc.123',
+ output: 0 },
+ { input: 'foo().bar',
+ output: 0 },
+ { input: 'foo(bar',
+ output: 4 },
+ { input: 'foo[abc.match(/"/)]',
+ output: 0 }
+];
+const testsGetDeclaredConstants = [
+ { input: 'const foo = X; const bar = Y;',
+ output: ['foo', 'bar'] },
+ { input: 'const foo=X; const bar=Y',
+ output: ['foo', 'bar'] }
+];
+const testsIsUnsafeExpression = [
+ { input: 'foo.bar',
+ output: false },
+ { input: 'foo[\'bar\']',
+ output: false },
+ { input: 'foo["a=b=c".match(/=/)',
+ output: false },
+ { input: 'foo[1==2]',
+ output: false },
+ { input: '(x=4)',
+ output: true },
+ { input: '(x = 4)',
+ output: true },
+ { input: '(x;y)',
+ output: true }
+];
+const testsModifyScope = [
+ "foo['a",
+ "foo()['b'",
+ "obj.foo()('a', 1, 2, 'b')().",
+ "foo.[.",
+ "foo]]]()))].",
+ "123'ab\"",
+ "Main.foo.bar = 3; bar.",
+ "(Main.foo = 3).",
+ "Main[Main.foo+=-1]."
+];
+
+
+
+// Utility function for comparing arrays
+function assertArrayEquals(errorMessage, array1, array2) {
+ JsUnit.assertEquals(errorMessage + ' length',
+ array1.length, array2.length);
+ for (let j = 0; j < array1.length; j++) {
+ JsUnit.assertEquals(errorMessage + ' item ' + j,
+ array1[j], array2[j]);
+ }
+}
+
+//
+// Test javascript parsing
+//
+
+for (let i = 0; i < testsFindMatchingQuote.length; i++) {
+ let text = testsFindMatchingQuote[i].input;
+ let match = JsParse.findMatchingQuote(text, text.length - 1);
+
+ JsUnit.assertEquals('Test testsFindMatchingQuote ' + i,
+ match, testsFindMatchingQuote[i].output);
+}
+
+for (let i = 0; i < testsFindMatchingSlash.length; i++) {
+ let text = testsFindMatchingSlash[i].input;
+ let match = JsParse.findMatchingSlash(text, text.length - 1);
+
+ JsUnit.assertEquals('Test testsFindMatchingSlash ' + i,
+ match, testsFindMatchingSlash[i].output);
+}
+
+for (let i = 0; i < testsFindMatchingBrace.length; i++) {
+ let text = testsFindMatchingBrace[i].input;
+ let match = JsParse.findMatchingBrace(text, text.length - 1);
+
+ JsUnit.assertEquals('Test testsFindMatchingBrace ' + i,
+ match, testsFindMatchingBrace[i].output);
+}
+
+for (let i = 0; i < testsGetExpressionOffset.length; i++) {
+ let text = testsGetExpressionOffset[i].input;
+ let match = JsParse.getExpressionOffset(text, text.length - 1);
+
+ JsUnit.assertEquals('Test testsGetExpressionOffset ' + i,
+ match, testsGetExpressionOffset[i].output);
+}
+
+for (let i = 0; i < testsGetDeclaredConstants.length; i++) {
+ let text = testsGetDeclaredConstants[i].input;
+ let match = JsParse.getDeclaredConstants(text);
+
+ assertArrayEquals('Test testsGetDeclaredConstants ' + i,
+ match, testsGetDeclaredConstants[i].output);
+}
+
+for (let i = 0; i < testsIsUnsafeExpression.length; i++) {
+ let text = testsIsUnsafeExpression[i].input;
+ let unsafe = JsParse.isUnsafeExpression(text);
+
+ JsUnit.assertEquals('Test testsIsUnsafeExpression ' + i,
+ unsafe, testsIsUnsafeExpression[i].output);
+}
+
+//
+// Test safety of eval to get completions
+//
+
+for (let i = 0; i < testsModifyScope.length; i++) {
+ let text = testsModifyScope[i];
+ // We need to use var here for the with statement
+ var obj = {};
+
+ // Just as in JsParse.getCompletions, we will find the offset
+ // of the expression, test whether it is unsafe, and then eval it.
+ let offset = JsParse.getExpressionOffset(text, text.length - 1);
+ if (offset >= 0) {
+ text = text.slice(offset);
+
+ let matches = text.match(/(.*)\.(.*)/);
+ if (matches) {
+ let [expr, base, attrHead] = matches;
+
+ if (!JsParse.isUnsafeExpression(base)) {
+ with (obj) {
+ try {
+ eval(HARNESS_COMMAND_HEADER + base);
+ } catch (e) {
+ JsUnit.assertNotEquals("Code '" + base + "' is valid code", e.constructor, SyntaxError);
+ }
+ }
+ }
+ }
+ }
+ let propertyNames = Object.getOwnPropertyNames(obj);
+ JsUnit.assertEquals("The context '" + JSON.stringify(obj) + "' was not modified", propertyNames.length, 0);
+}
diff --git a/tests/unit/markup.js b/tests/unit/markup.js
new file mode 100644
index 0000000..603ca81
--- /dev/null
+++ b/tests/unit/markup.js
@@ -0,0 +1,143 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+// Test cases for MessageList markup parsing
+
+const JsUnit = imports.jsUnit;
+const Pango = imports.gi.Pango;
+
+const Environment = imports.ui.environment;
+Environment.init();
+
+const Main = imports.ui.main; // unused, but needed to break dependency loop
+const MessageList = imports.ui.messageList;
+
+// Assert that @input, assumed to be markup, gets "fixed" to @output,
+// which is valid markup. If @output is null, @input is expected to
+// convert to itself
+function assertConverts(input, output) {
+ if (!output)
+ output = input;
+ let fixed = MessageList._fixMarkup(input, true);
+ JsUnit.assertEquals(output, fixed);
+
+ let parsed = false;
+ try {
+ Pango.parse_markup(fixed, -1, '');
+ parsed = true;
+ } catch (e) {}
+ JsUnit.assertEquals(true, parsed);
+}
+
+// Assert that @input, assumed to be plain text, gets escaped to @output,
+// which is valid markup.
+function assertEscapes(input, output) {
+ let fixed = MessageList._fixMarkup(input, false);
+ JsUnit.assertEquals(output, fixed);
+
+ let parsed = false;
+ try {
+ Pango.parse_markup(fixed, -1, '');
+ parsed = true;
+ } catch (e) {}
+ JsUnit.assertEquals(true, parsed);
+}
+
+
+
+// CORRECT MARKUP
+
+assertConverts('foo');
+assertEscapes('foo', 'foo');
+
+assertConverts('<b>foo</b>');
+assertEscapes('<b>foo</b>', '&lt;b&gt;foo&lt;/b&gt;');
+
+assertConverts('something <i>foo</i>');
+assertEscapes('something <i>foo</i>', 'something &lt;i&gt;foo&lt;/i&gt;');
+
+assertConverts('<u>foo</u> something');
+assertEscapes('<u>foo</u> something', '&lt;u&gt;foo&lt;/u&gt; something');
+
+assertConverts('<b>bold</b> <i>italic <u>and underlined</u></i>');
+assertEscapes('<b>bold</b> <i>italic <u>and underlined</u></i>', '&lt;b&gt;bold&lt;/b&gt; &lt;i&gt;italic &lt;u&gt;and underlined&lt;/u&gt;&lt;/i&gt;');
+
+assertConverts('this &amp; that');
+assertEscapes('this &amp; that', 'this &amp;amp; that');
+
+assertConverts('this &lt; that');
+assertEscapes('this &lt; that', 'this &amp;lt; that');
+
+assertConverts('this &lt; that &gt; the other');
+assertEscapes('this &lt; that &gt; the other', 'this &amp;lt; that &amp;gt; the other');
+
+assertConverts('this &lt;<i>that</i>&gt;');
+assertEscapes('this &lt;<i>that</i>&gt;', 'this &amp;lt;&lt;i&gt;that&lt;/i&gt;&amp;gt;');
+
+assertConverts('<b>this</b> > <i>that</i>');
+assertEscapes('<b>this</b> > <i>that</i>', '&lt;b&gt;this&lt;/b&gt; &gt; &lt;i&gt;that&lt;/i&gt;');
+
+
+
+// PARTIALLY CORRECT MARKUP
+// correct bits are kept, incorrect bits are escaped
+
+// unrecognized entity
+assertConverts('<b>smile</b> &#9786;!', '<b>smile</b> &amp;#9786;!');
+assertEscapes('<b>smile</b> &#9786;!', '&lt;b&gt;smile&lt;/b&gt; &amp;#9786;!');
+
+// stray '&'; this is really a bug, but it's easier to do it this way
+assertConverts('<b>this</b> & <i>that</i>', '<b>this</b> &amp; <i>that</i>');
+assertEscapes('<b>this</b> & <i>that</i>', '&lt;b&gt;this&lt;/b&gt; &amp; &lt;i&gt;that&lt;/i&gt;');
+
+// likewise with stray '<'
+assertConverts('this < that', 'this &lt; that');
+assertEscapes('this < that', 'this &lt; that');
+
+assertConverts('<b>this</b> < <i>that</i>', '<b>this</b> &lt; <i>that</i>');
+assertEscapes('<b>this</b> < <i>that</i>', '&lt;b&gt;this&lt;/b&gt; &lt; &lt;i&gt;that&lt;/i&gt;');
+
+assertConverts('this < that > the other', 'this &lt; that > the other');
+assertEscapes('this < that > the other', 'this &lt; that &gt; the other');
+
+assertConverts('this <<i>that</i>>', 'this &lt;<i>that</i>>');
+assertEscapes('this <<i>that</i>>', 'this &lt;&lt;i&gt;that&lt;/i&gt;&gt;');
+
+// unknown tags
+assertConverts('<unknown>tag</unknown>', '&lt;unknown>tag&lt;/unknown>');
+assertEscapes('<unknown>tag</unknown>', '&lt;unknown&gt;tag&lt;/unknown&gt;');
+
+// make sure we check beyond the first letter
+assertConverts('<bunknown>tag</bunknown>', '&lt;bunknown>tag&lt;/bunknown>');
+assertEscapes('<bunknown>tag</bunknown>', '&lt;bunknown&gt;tag&lt;/bunknown&gt;');
+
+// with mix of good and bad, we keep the good and escape the bad
+assertConverts('<i>known</i> and <unknown>tag</unknown>', '<i>known</i> and &lt;unknown>tag&lt;/unknown>');
+assertEscapes('<i>known</i> and <unknown>tag</unknown>', '&lt;i&gt;known&lt;/i&gt; and &lt;unknown&gt;tag&lt;/unknown&gt;');
+
+
+
+// FULLY INCORRECT MARKUP
+// (fall back to escaping the whole thing)
+
+// tags not matched up
+assertConverts('<b>in<i>com</i>plete', '&lt;b&gt;in&lt;i&gt;com&lt;/i&gt;plete');
+assertEscapes('<b>in<i>com</i>plete', '&lt;b&gt;in&lt;i&gt;com&lt;/i&gt;plete');
+
+assertConverts('in<i>com</i>plete</b>', 'in&lt;i&gt;com&lt;/i&gt;plete&lt;/b&gt;');
+assertEscapes('in<i>com</i>plete</b>', 'in&lt;i&gt;com&lt;/i&gt;plete&lt;/b&gt;');
+
+// we don't support attributes, and it's too complicated to try
+// to escape both start and end tags, so we just treat it as bad
+assertConverts('<b>good</b> and <b style=\'bad\'>bad</b>', '&lt;b&gt;good&lt;/b&gt; and &lt;b style=&apos;bad&apos;&gt;bad&lt;/b&gt;');
+assertEscapes('<b>good</b> and <b style=\'bad\'>bad</b>', '&lt;b&gt;good&lt;/b&gt; and &lt;b style=&apos;bad&apos;&gt;bad&lt;/b&gt;');
+
+// this is just syntactically invalid
+assertConverts('<b>unrecognized</b stuff>', '&lt;b&gt;unrecognized&lt;/b stuff&gt;');
+assertEscapes('<b>unrecognized</b stuff>', '&lt;b&gt;unrecognized&lt;/b stuff&gt;');
+
+// mismatched tags
+assertConverts('<b>mismatched</i>', '&lt;b&gt;mismatched&lt;/i&gt;');
+assertEscapes('<b>mismatched</i>', '&lt;b&gt;mismatched&lt;/i&gt;');
+
+assertConverts('<b>mismatched/unknown</bunknown>', '&lt;b&gt;mismatched/unknown&lt;/bunknown&gt;');
+assertEscapes('<b>mismatched/unknown</bunknown>', '&lt;b&gt;mismatched/unknown&lt;/bunknown&gt;');
diff --git a/tests/unit/params.js b/tests/unit/params.js
new file mode 100644
index 0000000..6ac4cc1
--- /dev/null
+++ b/tests/unit/params.js
@@ -0,0 +1,32 @@
+const JsUnit = imports.jsUnit;
+const Params = imports.misc.params;
+
+function assertParamsEqual(params, expected) {
+ for (let p in params) {
+ JsUnit.assertTrue(p in expected);
+ JsUnit.assertEquals(params[p], expected[p]);
+ }
+}
+
+let defaults = {
+ foo: 'This is a test',
+ bar: null,
+ baz: 42
+};
+
+assertParamsEqual(
+ Params.parse(null, defaults),
+ defaults);
+
+assertParamsEqual(
+ Params.parse({ bar: 23 }, defaults),
+ { foo: 'This is a test', bar: 23, baz: 42 });
+
+JsUnit.assertRaises(
+ () => {
+ Params.parse({ extraArg: 'quz' }, defaults);
+ });
+
+assertParamsEqual(
+ Params.parse({ extraArg: 'quz' }, defaults, true),
+ { foo: 'This is a test', bar: null, baz: 42, extraArg: 'quz' });
diff --git a/tests/unit/signalTracker.js b/tests/unit/signalTracker.js
new file mode 100644
index 0000000..7943d0a
--- /dev/null
+++ b/tests/unit/signalTracker.js
@@ -0,0 +1,115 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+// Test cases for version comparison
+
+const { GObject } = imports.gi;
+
+const JsUnit = imports.jsUnit;
+const Signals = imports.misc.signals;
+
+const Environment = imports.ui.environment;
+const { TransientSignalHolder, registerDestroyableType } = imports.misc.signalTracker;
+
+Environment.init();
+
+const Destroyable = GObject.registerClass({
+ Signals: { 'destroy': {} },
+}, class Destroyable extends GObject.Object {});
+registerDestroyableType(Destroyable);
+
+const GObjectEmitter = GObject.registerClass({
+ Signals: { 'signal': {} },
+}, class GObjectEmitter extends Destroyable {});
+
+const emitter1 = new Signals.EventEmitter();
+const emitter2 = new GObjectEmitter();
+
+const tracked1 = new Destroyable();
+const tracked2 = {};
+
+let count = 0;
+const handler = () => count++;
+
+emitter1.connectObject('signal', handler, tracked1);
+emitter2.connectObject('signal', handler, tracked1);
+
+emitter1.connectObject('signal', handler, tracked2);
+emitter2.connectObject('signal', handler, tracked2);
+
+JsUnit.assertEquals(count, 0);
+
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 4);
+
+tracked1.emit('destroy');
+
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 6);
+
+emitter1.disconnectObject(tracked2);
+emitter2.emit('destroy');
+
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 6);
+
+emitter1.connectObject(
+ 'signal', handler,
+ 'signal', handler, GObject.ConnectFlags.AFTER,
+ tracked1);
+emitter2.connectObject(
+ 'signal', handler,
+ 'signal', handler, GObject.ConnectFlags.AFTER,
+ tracked1);
+
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 10);
+
+tracked1.emit('destroy');
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 10);
+
+emitter1.connectObject('signal', handler, tracked1);
+emitter2.connectObject('signal', handler, tracked1);
+
+transientHolder = new TransientSignalHolder(tracked1);
+
+emitter1.connectObject('signal', handler, transientHolder);
+emitter2.connectObject('signal', handler, transientHolder);
+
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 14);
+
+transientHolder.destroy();
+
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 16);
+
+transientHolder = new TransientSignalHolder(tracked1);
+
+emitter1.connectObject('signal', handler, transientHolder);
+emitter2.connectObject('signal', handler, transientHolder);
+
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 20);
+
+tracked1.emit('destroy');
+emitter1.emit('signal');
+emitter2.emit('signal');
+
+JsUnit.assertEquals(count, 20);
diff --git a/tests/unit/url.js b/tests/unit/url.js
new file mode 100644
index 0000000..84aecc9
--- /dev/null
+++ b/tests/unit/url.js
@@ -0,0 +1,77 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+// Test cases for MessageTray URLification
+
+const JsUnit = imports.jsUnit;
+
+const Environment = imports.ui.environment;
+Environment.init();
+
+const Util = imports.misc.util;
+
+const tests = [
+ { input: 'This is a test',
+ output: [] },
+ { input: 'This is http://www.gnome.org a test',
+ output: [ { url: 'http://www.gnome.org', pos: 8 } ] },
+ { input: 'This is http://www.gnome.org',
+ output: [ { url: 'http://www.gnome.org', pos: 8 } ] },
+ { input: 'http://www.gnome.org a test',
+ output: [ { url: 'http://www.gnome.org', pos: 0 } ] },
+ { input: 'http://www.gnome.org',
+ output: [ { url: 'http://www.gnome.org', pos: 0 } ] },
+ { input: 'Go to http://www.gnome.org.',
+ output: [ { url: 'http://www.gnome.org', pos: 6 } ] },
+ { input: 'Go to http://www.gnome.org/.',
+ output: [ { url: 'http://www.gnome.org/', pos: 6 } ] },
+ { input: '(Go to http://www.gnome.org!)',
+ output: [ { url: 'http://www.gnome.org', pos: 7 } ] },
+ { input: 'Use GNOME (http://www.gnome.org).',
+ output: [ { url: 'http://www.gnome.org', pos: 11 } ] },
+ { input: 'This is a http://www.gnome.org/path test.',
+ output: [ { url: 'http://www.gnome.org/path', pos: 10 } ] },
+ { input: 'This is a www.gnome.org scheme-less test.',
+ output: [ { url: 'www.gnome.org', pos: 10 } ] },
+ { input: 'This is a www.gnome.org/scheme-less test.',
+ output: [ { url: 'www.gnome.org/scheme-less', pos: 10 } ] },
+ { input: 'This is a http://www.gnome.org:99/port test.',
+ output: [ { url: 'http://www.gnome.org:99/port', pos: 10 } ] },
+ { input: 'This is an ftp://www.gnome.org/ test.',
+ output: [ { url: 'ftp://www.gnome.org/', pos: 11 } ] },
+ { input: 'https://www.gnome.org/(some_url,_with_very_unusual_characters)',
+ output: [ { url: 'https://www.gnome.org/(some_url,_with_very_unusual_characters)', pos: 0 } ] },
+ { input: 'https://www.gnome.org/(some_url_with_unbalanced_parenthesis',
+ output: [ { url: 'https://www.gnome.org/', pos: 0 } ] },
+ { input: 'https://www.gnome.org/‎ plus trailing junk',
+ output: [ { url: 'https://www.gnome.org/', pos: 0 } ] },
+
+ { input: 'Visit http://www.gnome.org/ and http://developer.gnome.org',
+ output: [ { url: 'http://www.gnome.org/', pos: 6 },
+ { url: 'http://developer.gnome.org', pos: 32 } ] },
+
+ { input: 'This is not.a.domain test.',
+ output: [ ] },
+ { input: 'This is not:a.url test.',
+ output: [ ] },
+ { input: 'This is not:/a.url/ test.',
+ output: [ ] },
+ { input: 'This is not:/a.url/ test.',
+ output: [ ] },
+ { input: 'This is not@a.url/ test.',
+ output: [ ] },
+ { input: 'This is surely@not.a/url test.',
+ output: [ ] }
+];
+
+for (let i = 0; i < tests.length; i++) {
+ let match = Util.findUrls(tests[i].input);
+
+ JsUnit.assertEquals('Test ' + i + ' match length',
+ match.length, tests[i].output.length);
+ for (let j = 0; j < match.length; j++) {
+ JsUnit.assertEquals('Test ' + i + ', match ' + j + ' url',
+ match[j].url, tests[i].output[j].url);
+ JsUnit.assertEquals('Test ' + i + ', match ' + j + ' position',
+ match[j].pos, tests[i].output[j].pos);
+ }
+}
diff --git a/tests/unit/versionCompare.js b/tests/unit/versionCompare.js
new file mode 100644
index 0000000..1997a6c
--- /dev/null
+++ b/tests/unit/versionCompare.js
@@ -0,0 +1,52 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+// Test cases for version comparison
+
+const JsUnit = imports.jsUnit;
+
+const Environment = imports.ui.environment;
+Environment.init();
+
+const Util = imports.misc.util;
+
+const tests = [
+ { v1: '40',
+ v2: '40',
+ res: 0 },
+ { v1: '40',
+ v2: '42',
+ res: -1 },
+ { v1: '42',
+ v2: '40',
+ res: 1 },
+ { v1: '3.38.0',
+ v2: '40',
+ res: -1 },
+ { v1: '40',
+ v2: '3.38.0',
+ res: 1 },
+ { v1: '40',
+ v2: '3.38.0',
+ res: 1 },
+ { v1: '40.alpha.1.1',
+ v2: '40',
+ res: -1 },
+ { v1: '40',
+ v2: '40.alpha.1.1',
+ res: 1 },
+ { v1: '40.beta',
+ v2: '40',
+ res: -1 },
+ { v1: '40.1',
+ v2: '40',
+ res: 1 },
+ { v1: '',
+ v2: '40.alpha',
+ res: -1 },
+];
+
+for (let i = 0; i < tests.length; i++) {
+ name = 'Test #' + i + ' v1: ' + tests[i].v1 + ', v2: ' + tests[i].v2;
+ print(name);
+ JsUnit.assertEquals(name, Util.GNOMEversionCompare (tests[i].v1, tests[i].v2), tests[i].res);
+}
diff --git a/tools/build/gnome-shell-build-setup.sh b/tools/build/gnome-shell-build-setup.sh
new file mode 100755
index 0000000..59637e4
--- /dev/null
+++ b/tools/build/gnome-shell-build-setup.sh
@@ -0,0 +1,348 @@
+#!/bin/sh
+#
+# Script that sets up jhbuild to build gnome-shell. Run this to
+# checkout jhbuild and the required configuration.
+#
+# Copyright (C) 2008, Red Hat, Inc.
+#
+# Some ideas and code taken from gtk-osx-build
+#
+# Copyright (C) 2006, 2007, 2008 Imendio AB
+#
+
+release_file=
+
+if which lsb_release > /dev/null 2>&1; then
+ system=`lsb_release -is`
+ version=`lsb_release -rs`
+elif [ -f /etc/fedora-release ] ; then
+ system=Fedora
+ release_file=/etc/fedora-release
+elif [ -f /etc/SuSE-release ] ; then
+ system=SUSE
+ release_file=/etc/SuSE-release
+elif [ -f /etc/mandriva-release ]; then
+ system=MandrivaLinux
+ release_file=/etc/mandriva-release
+fi
+
+if [ x$release_file != x ] ; then
+ version=`sed 's/[^0-9\.]*\([0-9\.]\+\).*/\1/' < $release_file`
+fi
+
+# This is the configuration of packages that we'll need to successfully jhbuild.
+# Each line is of the form:
+#
+# name_of_depenency: <distro_chars>:package [<distro_chars>:package...]
+#
+# The dependency name is purely informative and isn't otherwise used. distro_chars are:
+#
+# f: Fedora
+# d: Debian/Ubuntu
+# s: SuSE
+# m: Mandriva
+#
+# Rather than have some complicated system here, when we have packages that depend
+# on distribution version, we just tweak the package list in the code below.
+# Where known, the module that requires a library is commented.
+
+all_packages() {
+cat <<EOF
+# For this script:
+curl: fdsm:curl
+git: f:git d:git-core
+
+# Build tools
+build-essential: d:build-essential
+automake: fd:automake
+asn1Parser: f:libtasn1-tools d:libtasn1-3-bin s:libtasn1 # gcr
+binutils: f:binutils
+bison: fds:bison
+cmake: fd:cmake # libproxy
+docbook-style-xsl: f:docbook-style-xsl d:docbook-xsl # gtk-doc
+flex: fds:flex
+gettext: fd:gettext
+gcc: f:gcc
+g++: f:gcc-c++
+gperf: f:gperf d:gperf # evolution-data-server gudev
+intltool: f:intltool
+libtool: f:libtool
+make: f:make
+perl-XML-Simple: f:perl-XML-Simple d:libxml-simple-perl # icon-naming-utils
+pkgconfig: f:pkgconfig
+python: f:python
+ruby: fds:ruby # WebKit
+texinfo: fd:texinfo # libgtop
+xsltproc: f:libxslt d:xsltproc # gtk-doc
+
+# Image handling libraries
+freetype: f:freetype-devel d:libfreetype6-dev # fontconfig
+jasper: f:jasper-devel d:libjasper-dev # gdk-pixbuf
+libjpeg: f:libjpeg-devel d:libjpeg-dev # gdk-pixbuf
+libpng: f:libpng-devel d:libpng-dev # gdk-pixbuf
+libtiff: fs:libtiff-devel d:libtiff-dev # gdk-pixbuf
+
+# X libraries
+GL: f:mesa-libGL-devel d:mesa-common-dev d:libgl1-mesa-dev m:GL-devel # cogl
+libX11: s:xorg-x11-proto-devel s:xorg-x11-devel # gtk+
+libXcomposite: f:libXcomposite-devel d:libxcomposite-dev # cogl mutter
+libXcursor: f:libXcursor-devel libxcursor-dev # mousetweaks
+libXdamage: f:libXdamage-devel m:libxdamage-devel d:libxdamage-dev # cogl mutter
+libXi: f:libXi-devel d:libxi-dev # gtk+
+libXrandr: f:libXrandr-devel d:libxrandr-dev # gnome-desktop
+libXrender: f:libXrender-devel d: libxrender-dev # cairo WebKit
+libXt: f:libXt-devel d:libxt-dev # WebKit
+libXtst: f:libXtst-devel d:libxtst-dev # caribou
+xcb: f:xcb-util-devel d:libx11-xcb-dev # startup-notification
+
+# Other libraries
+cracklib: fs:cracklib-devel d:libcrack2-dev # libpwquality
+cups: fs:cups-devel d:libcups2-dev # gnome-control-center
+libdb: d:libdb-dev # evolution-data-server - see below for Fedora
+icu: f:libicu-devel d:libicu-dev # WebKit
+libacl: f:libacl-devel d:libacl1-dev # gudev
+libcurl: f:libcurl-devel # liboauth. See below for Debian
+libffi: fs:libffi-devel d:libffi-dev # gobject-introspection
+libsystemd-login: fs:systemd-devel # gnome-session gnome-settings-daemon polkit PackageKit
+libusb: f:libusb1-devel d:libusb-1.0-0-dev # upower
+openssl: f:openssl-devel d:libssl-dev # liboauth
+pam: f:pam-devel d:libpam-dev # polkit
+ppp: f:ppp-devel d:ppp-dev # NetworkManager
+python-devel: f:python-devel d:python-dev # pygobject py2cairo
+readline: fsm:readline-devel d:libreadline-dev
+sane: f:sane-backends-devel d:libsane-dev # colord
+sqlite: d:libsqlite3-dev f:sqlite-devel # libsoup
+udev: f:libudev-devel d:libudev-dev # gudev
+uuid: f:libuuid-devel d:uuid-dev # Networkmanager
+wireless-tools: f:wireless-tools-devel d:libiw-dev s:libiw-devel # NetworkManager
+
+# python libraries used by gnome-shell wrapper script
+# These are commented out because the gnome-shell wrapper script
+# isn't built by default, and needs updating for running on
+# a pure-GNOME 3 system, rather than recovering to GNOME 2.
+# dbus-python: f:dbus-python d:python-dbus
+# python-gobject: f:pygobject2 d:python-gobject
+# python-gconf: f:gnome-python2-gconf d:python-gconf
+EOF
+}
+
+packages_for_distribution() {
+ distribution_char=$1
+ all_packages |
+ sed -n 's/#.*//; /[^ ]/p' | # Remove comments and blank lines
+ while read dependency_name words ; do
+ for word in $words ; do
+ # Word is <distribution-chars>:package
+ IFS=:
+ set $word
+ IFS=' '
+ case $1 in
+ *$distribution_char*) echo $2
+ esac
+ done
+ done
+}
+
+# We try to make it clear what we're doing via sudo so if a user gets prompted
+# for their password, they have some idea why.
+run_via_sudo() {
+ echo "Running: sudo $@"
+ if sudo "$@" ; then : ; else
+ echo 1>&2 "Command failed."
+ echo 1>&2 "Exiting gnome-shell-build-setup.sh. You can run it again safely."
+ exit 1
+ fi
+}
+
+if test "x$system" = xUbuntu -o "x$system" = xDebian -o "x$system" = xLinuxMint ; then
+ reqd=`packages_for_distribution d`
+
+ if apt-cache show libxcb-util0-dev > /dev/null 2> /dev/null; then
+ reqd="$reqd libxcb-util0-dev"
+ else
+ reqd="$reqd libxcb-event1-dev libxcb-aux0-dev"
+ fi
+
+ if apt-cache show autopoint > /dev/null 2> /dev/null; then
+ reqd="$reqd autopoint"
+ fi
+
+ if [ ! -x /usr/bin/dpkg-checkbuilddeps -o ! -x /usr/bin/apt-file ]; then
+ echo "Installing base dependencies"
+ run_via_sudo apt-get install dpkg-dev apt-file
+ fi
+
+ echo "Updating apt-file cache"
+ run_via_sudo apt-file update
+
+ # libcurl comes in both gnutls and openssl flavors. If the openssl
+ # flavor of the runtime is installed, install the matching -dev
+ # package, but default to the gnutls version. (the libcurl3 vs. libcurl4
+ # mismatch is intentional and is how things are packaged.)
+
+ if ! dpkg-checkbuilddeps -d libcurl-dev /dev/null 2> /dev/null; then
+ if dpkg -s libcurl3 /dev/null 2> /dev/null; then
+ missing="libcurl4-openssl-dev $missing"
+ elif dpkg -s libcurl3-nss /dev/null 2> /dev/null; then
+ missing="libcurl4-nss-dev $missing"
+ else
+ missing="libcurl4-gnutls-dev $missing"
+ fi
+ fi
+
+ for pkg in $reqd ; do
+ if ! dpkg-checkbuilddeps -d $pkg /dev/null 2> /dev/null; then
+ missing="$pkg $missing"
+ fi
+ done
+ if test ! "x$missing" = x; then
+ echo "Installing packages"
+ run_via_sudo apt-get install $missing
+ fi
+fi
+
+if test "x$system" = xFedora ; then
+ reqd=`packages_for_distribution f`
+
+ if expr $version = 14 > /dev/null ; then
+ reqd="$reqd gettext-autopoint"
+ elif expr $version \>= 15 > /dev/null ; then
+ reqd="$reqd gettext-devel"
+ fi
+
+ # For evolution-data-server:
+ # /usr/include/db.h moved packages in Fedora 18
+ if expr $version \>= 18 > /dev/null ; then
+ reqd="$reqd libdb-devel"
+ else
+ reqd="$reqd db4-devel"
+ fi
+
+ echo -n "Computing packages to install ... "
+ for pkg in $reqd ; do
+ if ! rpm -q --whatprovides $pkg > /dev/null 2>&1; then
+ missing="$pkg $missing"
+ fi
+ done
+ echo "done"
+
+ if test ! "x$missing" = x; then
+ echo -n "Installing packages ... "
+ missing_str=
+ for pkg in $missing ; do
+ missing_str="$missing_str${missing_str:+,}\"$pkg\""
+ done
+ gdbus call -e -d org.freedesktop.PackageKit -o /org/freedesktop/PackageKit -m org.freedesktop.PackageKit.Modify.InstallPackageNames 0 "[$missing_str]" "hide-finished,show-warnings"
+ echo "done"
+ fi
+fi
+
+if test "x$system" = xSUSE -o "x$system" = "xSUSE LINUX" ; then
+ reqd=`packages_for_distribution s`
+ if test ! "x$reqd" = x; then
+ echo "Please run 'su --command=\"zypper install $reqd\"' and try again."
+ echo
+ exit 1
+ fi
+fi
+
+if test "x$system" = xMandrivaLinux ; then
+ reqd=`packages_for_distribution m`
+ if test ! "x$reqd" = x; then
+ gurpmi --auto $reqd
+ fi
+fi
+
+SOURCE=$HOME/Source
+BASEURL=http://git.gnome.org/browse/gnome-shell/plain/tools/build
+
+if [ -d $SOURCE ] ; then : ; else
+ mkdir $SOURCE
+ echo "Created $SOURCE"
+fi
+
+checkout_git() {
+ module=$1
+ source=$2
+
+ if [ -d $SOURCE/$1 ] ; then
+ if [ -d $SOURCE/$1/.git ] ; then
+ echo -n "Updating $1 ... "
+ ( cd $SOURCE/$1 && git pull --rebase > /dev/null ) || exit 1
+ echo "done"
+ else
+ echo "$SOURCE/$1 is not a git repository"
+ echo "You should remove it and rerun this script"
+ exit 1
+ fi
+ else
+ echo -n "Checking out $1 into $SOURCE/$1 ... "
+ cd $SOURCE
+ git clone $2 > /dev/null || exit 1
+ echo "done"
+ fi
+}
+
+checkout_git jhbuild git://git.gnome.org/jhbuild
+
+echo -n "Installing jhbuild ... "
+(cd $SOURCE/jhbuild &&
+ ./autogen.sh --simple-install &&
+ make -f Makefile.plain DISABLE_GETTEXT=1 bindir=$HOME/bin install) >/dev/null
+echo "done"
+
+if [ -e $HOME/.jhbuildrc ] ; then
+ if grep JHBUILDRC_GNOME_SHELL $HOME/.jhbuildrc > /dev/null ; then : ; else
+ mv $HOME/.jhbuildrc $HOME/.jhbuildrc.bak
+ echo "Saved ~/.jhbuildrc as ~/.jhbuildrc.bak"
+ fi
+fi
+
+echo -n "Writing ~/.jhbuildrc ... "
+curl -L -s -o $HOME/.jhbuildrc $BASEURL/jhbuildrc-gnome-shell
+echo "done"
+
+if [ ! -f $HOME/.jhbuildrc-custom ]; then
+ echo -n "Writing example ~/.jhbuildrc-custom ... "
+ curl -L -s -o $HOME/.jhbuildrc-custom $BASEURL/jhbuildrc-custom-example
+ echo "done"
+fi
+
+if [ -d $HOME/gnome-shell -a \! -d $HOME/gnome ] ; then
+ cat <<EOF
+WARNING:
+ The old source and install directory '$HOME/gnome-shell' exists, but
+ '$HOME/gnome' doesn't. An empty $HOME/gnome will be created.
+
+ To avoid starting again from scratch you should remove the empty directory,
+ move your old '$HOME/gnome-shell' to '$HOME/gnome', and delete the old
+ install directory:
+
+ rm -rf $HOME/gnome
+ mv $HOME/gnome-shell $HOME/gnome
+ rm -rf $HOME/gnome/install
+EOF
+fi
+
+echo "Installing modules as system packages when possible"
+$HOME/bin/jhbuild sysdeps --install
+
+if test "x`echo $PATH | grep $HOME/bin`" = x; then
+ echo "PATH does not contain $HOME/bin, it is recommended that you add that."
+ echo
+fi
+
+checkout_git git-bz git://git.fishsoup.net/git-bz
+
+
+echo -n "Installing git-bz ... "
+old="`readlink $HOME/bin/git-bz`"
+( cd $HOME/bin && ln -sf ../Source/git-bz/git-bz . )
+new="`readlink $HOME/bin/git-bz`"
+echo "done"
+
+if test "$old" != "$new" -a "$old" != "" ; then
+ echo "WARNING: $HOME/bin/git-bz was changed from '$old' to '$new'"
+fi
+
+echo "Done."
diff --git a/tools/build/jhbuildrc-custom-example b/tools/build/jhbuildrc-custom-example
new file mode 100644
index 0000000..837d24e
--- /dev/null
+++ b/tools/build/jhbuildrc-custom-example
@@ -0,0 +1,18 @@
+# -*- mode: python -*-
+
+# The username for repositories can be overridden.
+#
+#repos["git.gnome.org"] = "ssh://myusername@git.gnome.org/git/"
+
+# Directory where to check sources out
+#
+#checkoutroot = '/opt/gnome-shell/source'
+
+# Directory where to install
+#
+#prefix = '/opt/gnome-shell/install'
+
+# Work around http://bugzilla.gnome.org/show_bug.cgi?id=571240 if
+# your 'install' program is somewhere other than /usr/bin/install
+# (affects: Arch Linux)
+#installprog = 'bin/install'
diff --git a/tools/build/jhbuildrc-gnome-shell b/tools/build/jhbuildrc-gnome-shell
new file mode 100644
index 0000000..aef75db
--- /dev/null
+++ b/tools/build/jhbuildrc-gnome-shell
@@ -0,0 +1,54 @@
+# -*- mode: python -*-
+#
+# jhbuildrc for building gnome-shell. Customizations should be done in
+# ~/.jhbuildrc-custom
+#
+# Copyright (C) 2008 Red Hat, Inc.
+#
+# Some ideas and a bit of code taken from gtk-osx-build
+#
+# Copyright (C) 2006, 2007, 2008 Imendio AB
+#
+# Use .jhbuildrc-custom to override the moduleset, modules to build,
+# the source checkout location, installation prefix, or svn usernames
+# etc.
+#
+# JHBUILDRC_GNOME_SHELL - Do not edit this line (or anything else)
+
+# Only rebuild modules that have changed
+build_policy = 'updated'
+
+moduleset = 'gnome-apps-3.14'
+
+modules = [ 'meta-gnome-core-shell' ]
+
+# what directory should the source be checked out to?
+checkoutroot = os.path.expanduser('~/gnome/source')
+
+# the prefix to configure/install modules to (must have write access)
+prefix = os.path.expanduser('~/gnome/install')
+
+# reduce what we build as much as possible
+ignore_suggests = True
+
+# Use system libraries for the builds
+if use_lib64:
+ _libdir = 'lib64'
+else:
+ _libdir = 'lib'
+addpath('PKG_CONFIG_PATH', os.path.join(os.sep, 'usr', _libdir, 'pkgconfig'))
+addpath('PKG_CONFIG_PATH', os.path.join(os.sep, 'usr', 'share', 'pkgconfig'))
+
+# Look in /usr/share for icons, D-BUS service files, etc
+addpath('XDG_DATA_DIRS', '/usr/share')
+# Look in /etc/xdg for system-global autostart files
+addpath('XDG_CONFIG_DIRS', '/etc/xdg')
+
+# Import optional user RC for further customization. You can override
+# the prefix or default build setup for example, or CFLAGS or
+# module_autogenargs, etc.
+#
+_userrc = os.path.join(os.environ['HOME'], '.jhbuildrc-custom')
+if os.path.exists(_userrc):
+ execfile(_userrc)
+
diff --git a/tools/gnome-shell-overrides-migration.sh b/tools/gnome-shell-overrides-migration.sh
new file mode 100755
index 0000000..a1b4cb6
--- /dev/null
+++ b/tools/gnome-shell-overrides-migration.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+PKG_DATA_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/gnome-shell
+
+MIGRATION_GUARD=$PKG_DATA_DIR/gnome-overrides-migrated
+OVERRIDE_SCHEMA=
+
+if [ -f $MIGRATION_GUARD ]; then
+ exit # already migrated
+fi
+
+# Find the right session
+if echo $XDG_CURRENT_DESKTOP | grep -q -v GNOME; then
+ exit # not a GNOME session
+fi
+
+if echo $XDG_CURRENT_DESKTOP | grep -q Classic; then
+ OVERRIDE_SCHEMA=org.gnome.shell.extensions.classic-overrides
+else
+ OVERRIDE_SCHEMA=org.gnome.shell.overrides
+fi
+
+mkdir -p $PKG_DATA_DIR
+
+for k in `gsettings list-keys $OVERRIDE_SCHEMA`
+do
+ if [ $k = button-layout ]; then
+ orig_schema=org.gnome.desktop.wm.preferences
+ else
+ orig_schema=org.gnome.mutter
+ fi
+
+ oldValue=`gsettings get $OVERRIDE_SCHEMA $k`
+ curValue=`gsettings get $orig_schema $k`
+ if [ $oldValue != $curValue ]; then
+ gsettings set $orig_schema $k $oldValue
+ fi
+done && touch $MIGRATION_GUARD
diff --git a/tools/meson.build b/tools/meson.build
new file mode 100644
index 0000000..d8e217c
--- /dev/null
+++ b/tools/meson.build
@@ -0,0 +1,4 @@
+install_data('gnome-shell-overrides-migration.sh',
+ install_dir: libexecdir,
+ install_mode: 'rwxr-xr-x'
+)